@@ -225,6 +223,8 @@ These Key-Value Store features are supported out of the box when using Puter.js:
- **[`puter.kv.expireAt()`](/KV/expireAt/)** - Set key expiration timestamp
- **[`puter.kv.list()`](/KV/list/)** - List keys in ascending or descending order
- **[`puter.kv.flush()`](/KV/flush/)** - Clear all data
+- **[`puter.kv.MAX_KEY_SIZE`](/KV/MAX_KEY_SIZE/)** - Maximum key size in bytes
+- **[`puter.kv.MAX_VALUE_SIZE`](/KV/MAX_VALUE_SIZE/)** - Maximum value size in bytes
## Examples
diff --git a/src/docs/src/KV/list.md b/src/docs/src/KV/list.md
index 5b9dcf397..5e1e45ba3 100755
--- a/src/docs/src/KV/list.md
+++ b/src/docs/src/KV/list.md
@@ -58,7 +58,7 @@ When paginating, iterate until the result has no `cursor` — a page may hold fe
Invalid options, such as a non-boolean `reverse` or `offset` combined with `stream`, reject the returned promise with an `invalid_request` error; `list()` never throws synchronously. With `stream: true`, the iterator's first `next()` rejects instead.
-Full (non-paginated) listings keep resolving to a plain array, so existing code is unaffected — under the hood the SDK now fetches them page by page. They still read the entire store, though: every page is metered, so on large stores a bare `list()` gets slow and costly (the SDK logs a one-time console warning when a full listing spans multiple pages). Prefer `stream: true` or explicit `limit`/`cursor` pages, and narrow the scan with a `pattern`.
+Without pagination params, the full listing is returned as a plain array. It reads the entire store and is metered per page, so on large stores a bare `list()` gets slow and costly. The SDK logs a one-time console warning when a full listing spans multiple pages. Use `stream: true` or `limit` and `cursor` pages instead, and narrow the scan with a `pattern`.
With `stream: true`, the method returns an async iterator of [`KVListPage`](/Objects/kvlistpage) objects instead:
@@ -74,7 +74,7 @@ for await (const page of puter.kv.list({ pattern: 'log:*', stream: true })) {
List keys in reverse order
-```html;kv-list-reverse
+```html
diff --git a/src/docs/src/KV/set.md b/src/docs/src/KV/set.md
index d8503ca8a..779fc7c66 100755
--- a/src/docs/src/KV/set.md
+++ b/src/docs/src/KV/set.md
@@ -38,7 +38,7 @@ A Unix timestamp in seconds at which the key should expire. Omit it, or pass `nu
#### `disableSharing` (Boolean) (optional)
-Pass inside the trailing options object — `set(key, value, { disableSharing: true })` — to mark this entry private to your app. A private entry cannot be read, listed, changed, or deleted by any other app, even one the user has granted access to your app's data with [`puter.perms.request('appData', …)`](/Perms/appData/). It is also not watched by other apps, or by another user through a [share handle](/Events/onLocal/#sharing-key-value-events-with-another-user): those subscriptions get no event, key, or value for it. Use it for anything another app should never see, such as a cached access token: a user approving a request cannot see what your store holds.
+Pass inside the trailing options object — `set(key, value, { disableSharing: true })` — to mark this entry private to your app. A private entry cannot be read, listed, changed, or deleted by any other app, even one the user has granted access to your app's data with [`puter.perms.request('appData', …)`](/Perms/appData/). It is also not watched by other apps, or by another user through a [share handle](/Events/subjects/#sharing-key-value-events-with-another-user): those subscriptions get no event, key, or value for it. Use it for anything another app should never see, such as a cached access token: a user approving a request cannot see what your store holds.
The batch form takes it too — `set([...items], { disableSharing: true })` marks every entry in the batch.
diff --git a/src/docs/src/KV/update.md b/src/docs/src/KV/update.md
index 50a8f5cf8..f77cecff3 100644
--- a/src/docs/src/KV/update.md
+++ b/src/docs/src/KV/update.md
@@ -59,7 +59,7 @@ A rejection carries an `Error` with a stable `code`:
Update an element of a root array
-```html;kv-update-root-array
+```html
diff --git a/src/docs/src/Objects.md b/src/docs/src/Objects.md
index 72ffc9dfa..8213f9e36 100644
--- a/src/docs/src/Objects.md
+++ b/src/docs/src/Objects.md
@@ -11,6 +11,7 @@ Various object types and classes that represent different entities in the Puter
- **[AppConnection](/Objects/AppConnection/)** - Represents a connection to an application
- **[ChatResponse](/Objects/chatresponse/)** - Represents an AI chat response
- **[ChatResponseChunk](/Objects/chatresponsechunk/)** - Represents a chunk of streaming chat response data
+- **[CreateAppResult](/Objects/createappresult/)** - Represents the result of creating an app
- **[DetailedAppUsage](/Objects/detailedappusage/)** - Represents detailed resource usage statistics for a specific application
- **[EmailListPage](/Objects/emaillistpage/)** - Represents one page of mailbox listing results
- **[EmailMessage](/Objects/emailmessage/)** - Represents a fully parsed message from the user's mailbox
@@ -18,9 +19,12 @@ Various object types and classes that represent different entities in the Puter
- **[EmailSummary](/Objects/emailsummary/)** - Represents one message in a mailbox listing
- **[FSItem](/Objects/fsitem/)** - Represents a file or directory
- **[KVPair](/Objects/kvpair/)** - Represents a key-value pair
+- **[KVListPage](/Objects/kvlistpage/)** - Represents one page of key-value list results
- **[MonthlyUsage](/Objects/monthlyusage/)** - Represents user's monthly resource usage information
- **[PuterPeerConnection](/Objects/puterpeerconnection/)** - Represents a data-channel connection to a peer
- **[PuterPeerServer](/Objects/puterpeerserver/)** - Represents a peer server and its connected clients
+- **[SignInResult](/Objects/signinresult/)** - Represents the result of a sign-in
+- **[SpaceInfo](/Objects/spaceinfo/)** - Represents the user's storage capacity and usage
- **[Speech2TxtResult](/Objects/speech2txtresult/)** - Represents speech-to-text transcription results
- **[Subdomain](/Objects/subdomain/)** - Represents a subdomain
- **[Team](/Objects/team/)** - Represents a Puter team
@@ -29,5 +33,6 @@ Various object types and classes that represent different entities in the Puter
- **[TTSVoice](/Objects/ttsvoice/)** - Represents an available text-to-speech voice
- **[ToolCall](/Objects/toolcall/)** - Represents a tool invocation request
- **[User](/Objects/user/)** - Represents a Puter user
+- **[UserProfile](/Objects/userprofile/)** - Represents a user's public profile
- **[WorkerDeployment](/Objects/workerdeployment/)** - Represents a worker deployment result
- **[WorkerInfo](/Objects/workerinfo/)** - Represents worker information
diff --git a/src/docs/src/Peer.md b/src/docs/src/Peer.md
index 125b3aa22..a70a968fd 100644
--- a/src/docs/src/Peer.md
+++ b/src/docs/src/Peer.md
@@ -131,6 +131,7 @@ These peer features are supported out of the box when using Puter.js:
- **[`puter.peer.serve()`](/Peer/serve/)** - Create a peer server and generate an invite code
- **[`puter.peer.connect()`](/Peer/connect/)** - Connect to a peer server using an invite code
- **[`puter.peer.ensureTurnRelays()`](/Peer/ensureTurnRelays/)** - Preload TURN relays for faster connections
+- **[`puter.peer.createGuestGrant()`](/Peer/createGuestGrant/)** - Create a grant that lets a guest without a Puter account use Puter's relays
## Examples
diff --git a/src/docs/src/Perms.md b/src/docs/src/Perms.md
index 8edc2e64c..7f72a7ad4 100644
--- a/src/docs/src/Perms.md
+++ b/src/docs/src/Perms.md
@@ -6,9 +6,7 @@ platforms: [websites, apps]
The Permissions API enables your application to request access to user data and resources such as email addresses, special folders (Desktop, Documents, Pictures, Videos), apps, subdomains, and other apps' saved data.
-There are two methods. [`puter.perms.request()`](/Perms/request/) asks the user for access, and [`puter.perms.check()`](/Perms/check/) reports whether they have already granted it without prompting.
-
-Both take the same two arguments: the resource being asked about, and the details that resource needs.
+Each request names a resource and the details that resource needs:
```js
await puter.perms.request('email');
@@ -17,8 +15,6 @@ await puter.perms.request('apps', { access: 'read' });
await puter.perms.request('appData', { app: 'contacts', scopes: 'read' });
```
-When requesting permissions, users will be prompted to grant or deny access. If a permission has already been granted, the user will not be prompted again. This provides a seamless experience while maintaining user privacy and control.
-
## Features
@@ -190,22 +186,6 @@ Pass an array to ask for everything your app needs in one call. Anything already
-## Resources
-
-The resource decides which details the call takes and what a request resolves to:
-
-| Resource | Details | `request()` resolves to |
-| -------- | ------- | ----------------------- |
-| `'email'` | — | The user's email address |
-| `'folder'` | `{ name, access }` | The folder's path |
-| `'apps'` | `{ access }` | `true` if granted |
-| `'subdomains'` | `{ access }` | `true` if granted |
-| `'appData'` | `{ app, scopes }` | `true` if granted |
-| `'appRootDir'` | `{ app, access }` | The app's root directory, or `undefined` if denied |
-| `'permission'` | `{ permission }` or `{ permissions }` | `true` if granted |
-
-Anything denied resolves to a falsy value, so one `if` covers both outcomes. `access` is `'read'` (the default) or `'write'`.
-
## Functions
- **[`puter.perms.request()`](/Perms/request/)** - Request access to a resource, or to several at once
diff --git a/src/docs/src/Perms/request.md b/src/docs/src/Perms/request.md
index 9d8cb353a..d255d7206 100644
--- a/src/docs/src/Perms/request.md
+++ b/src/docs/src/Perms/request.md
@@ -6,9 +6,13 @@ platforms: [websites, apps]
Request access to something belonging to the user. The first argument names the resource; the second carries the details that resource takes.
-The user is prompted to allow or deny. Anything already granted is skipped, so a call whose access is fully in place doesn't prompt at all. [`puter.perms.check()`](/Perms/check/) reads the same state, so the two always agree.
+
-Inside the Puter desktop the prompt is shown as a dialog. On websites, it opens in a popup window on the Puter origin — call this from a user gesture (e.g. a click handler) so the browser doesn't block the popup; without a gesture, a consent dialog is shown first and the popup opens when the user clicks Continue.
+The user is prompted to grant or deny access. If they already granted it, they aren't prompted again, so a call whose access is fully in place doesn't prompt at all. [`puter.perms.check()`](/Perms/check/) reads the same state, so the two always agree.
+
+
+
+Inside the Puter desktop the prompt is shown as a dialog. On websites, it opens in a popup window on the Puter origin. Call this from a user gesture, such as a click handler, so the browser doesn't block the popup. Without a gesture, a consent dialog is shown first and the popup opens when the user clicks Continue.
## Syntax
@@ -66,8 +70,6 @@ The array form resolves to an array of those values, in the order asked.
Some permission strings are not supported and are denied silently.
-An app's root directory has no raw form: `app-root-dir:` reads as nothing during a permission check, so asking for it this way would prompt on every call and [`check()`](/Perms/check/) would report it as not granted even once it is. Use the `'appRootDir'` resource, which asks the server directly.
-
A lone string that names no resource is treated as a permission string, so `puter.perms.request('fs:/user/Documents:read')` keeps working.
## Creating a path on request
diff --git a/src/docs/src/UI/getLanguage.md b/src/docs/src/UI/getLanguage.md
index 9d1a32cf1..c61140aea 100644
--- a/src/docs/src/UI/getLanguage.md
+++ b/src/docs/src/UI/getLanguage.md
@@ -4,7 +4,7 @@ description: Retrieves the current language/locale code from the Puter environme
platforms: [apps]
---
-Retrieves the current language/locale code from the Puter environment. This function communicates with the host environment to get the active language setting.
+Retrieves the current language/locale code from the Puter environment.
## Syntax
```js
diff --git a/src/docs/src/Workers/list.md b/src/docs/src/Workers/list.md
index 7cda1a1d3..4a7893069 100644
--- a/src/docs/src/Workers/list.md
+++ b/src/docs/src/Workers/list.md
@@ -35,7 +35,7 @@ When the request includes any pagination option, the promise instead resolves to
- `cursor` (String) (optional): Present while more pages exist; pass it to the next call.
- `total` (Number) (optional): Present when `includeTotal` was set.
-Requests without pagination params keep returning the full list as a plain array, so existing code is unaffected — under the hood the SDK now fetches it page by page.
+Without pagination params, the full list is returned as a plain array.
With `stream: true`, the method returns an async iterator of page objects instead:
diff --git a/src/docs/src/rate-limits-and-quotas.md b/src/docs/src/rate-limits-and-quotas.md
index a6708cd72..c9e9b97a3 100644
--- a/src/docs/src/rate-limits-and-quotas.md
+++ b/src/docs/src/rate-limits-and-quotas.md
@@ -361,7 +361,7 @@ The mutation and read budgets are per user, per app, not per team: administering
### Events
-See [Events](/Events/) for an overview, [`onLocal()`](/Events/onLocal/#gaps) for gap markers, and [`onPersistent()`](/Events/onPersistent/#suspended-subscriptions) for suspension.
+See [Events](/Events/) for an overview, [`onLocal()`](/Events/subjects/#gaps) for gap markers, and [`onPersistent()`](/Events/onPersistent/#suspended-subscriptions) for suspension.
#### What an account can hold
@@ -389,7 +389,7 @@ Counted per event, per region:
- KV limits follow the plan of the **key's owner**, not the writer's or the subscribers'. If the owner's plan can't be looked up, the free numbers apply. A server with no metering uses the paid numbers.
- They count subscriptions, not people. A user with three tabs open counts three times.
-- Past the limit, the next subscriptions get a [gap marker](/Events/onLocal/#gaps) instead of the event (up to the same number again), and any after that get nothing. With 1,100 subscriptions on a paid owner's key: 512 get the event, 512 get a gap, 76 get nothing.
+- Past the limit, the next subscriptions get a [gap marker](/Events/subjects/#gaps) instead of the event (up to the same number again), and any after that get nothing. With 1,100 subscriptions on a paid owner's key: 512 get the event, 512 get a gap, 76 get nothing.
- The region that handles the write delivers to every persistent subscription plus its own connected clients. Other regions deliver only to their own connected clients. So persistent subscriptions share one budget per event, while session subscriptions spread across regions can reach more in total.
- `includeValue` values are left out of every delivery for a change when more than **128** subscriptions match it (whatever the owner's plan), when the filter-check limit stops the count early, or when the value is over **16 KB**.
diff --git a/src/docs/src/recipes/deploy-workers-programmatically.md b/src/docs/src/recipes/deploy-workers-programmatically.md
index 56796e751..8d64695f1 100644
--- a/src/docs/src/recipes/deploy-workers-programmatically.md
+++ b/src/docs/src/recipes/deploy-workers-programmatically.md
@@ -33,8 +33,8 @@ const deployment = await puter.workers.create('notes-api', 'notes-api.js');
deployment.url; // 'https://notes-api.puter.work'
```
-Give it 5 to 30 seconds before the first request, while the worker spreads to
-every edge server. Then call it with
+Give it 5 to 30 seconds before the first request, while the worker deploys.
+Then call it with
[`puter.workers.exec()`](/Workers/exec/), which sends the signed-in user along
so the route can use `user.puter`:
@@ -46,11 +46,10 @@ await res.json(); // { username: 'grace' }
[Build an API with a worker](/recipes/build-an-api/) covers what to put
in the routes.
-Worker names are global, like subdomains, and are stored in lowercase. A name
-can use letters, numbers, hyphens and underscores. When another account already
-has the name, `create()` rejects, so pick another one or use
-[`puter.randName()`](/Utils/randName/). The account deploying the worker needs a
-verified email address.
+Worker names are global, like subdomains. When another account already has the
+name, [`create()`](/Workers/create/) rejects, so pick another one or use
+[`puter.randName()`](/Utils/randName/). All the naming rules and requirements
+are on the [`puter.workers.create()`](/Workers/create/) page.
## Update Its Code
diff --git a/src/docs/src/recipes/share-data-between-apps.md b/src/docs/src/recipes/share-data-between-apps.md
index 68d555dfe..d74d1b406 100644
--- a/src/docs/src/recipes/share-data-between-apps.md
+++ b/src/docs/src/recipes/share-data-between-apps.md
@@ -32,13 +32,9 @@ const granted = await puter.perms.request('appData', {
This returns `true` if the user allows it and `false` if they don't. Once
allowed, later calls return `true` without asking again.
-There are three levels of access. Only ask for what you need:
-
-| Scope | Lets your app call |
-| --- | --- |
-| `read` | `get`, `list` |
-| `write` | `set`, `add`, `incr`, `decr`, `update` |
-| `delete` | `del`, `remove`, `expire`, `expireAt` |
+There are three levels of access, `read`, `write` and `delete`. Only ask for what
+you need. The methods each one allows are listed in
+[Using another app's data](/Perms/appData/).
`write` doesn't include `delete`, so an app with `write` access can add and
change entries but can't remove them. To ask for more than one, pass an array,
@@ -144,4 +140,4 @@ const setPrivate = (key, value) => puter.kv.set(key, value, { disableSharing: tr
through [Events](/Events/).
- This is for sharing between apps for the same user. To share data between
different users, use a [worker](/recipes/store-server-side-data/) or an
- [Events share handle](/Events/onLocal/#sharing-key-value-events-with-another-user).
+ [Events share handle](/Events/subjects/#sharing-key-value-events-with-another-user).
diff --git a/src/docs/src/recipes/work-with-a-large-directory.md b/src/docs/src/recipes/work-with-a-large-directory.md
index 224fbfa8a..9fc462bbe 100644
--- a/src/docs/src/recipes/work-with-a-large-directory.md
+++ b/src/docs/src/recipes/work-with-a-large-directory.md
@@ -66,9 +66,9 @@ const items = await puter.fs.readdir({
});
```
-The `sortBy` option takes `name` (the default), `modified`, `type` or `size`,
-and `sortOrder` takes `asc` (the default) or `desc`. When you read pages with a
-cursor, every page must use the same sort as the first one.
+All the sort options are listed on [`puter.fs.readdir()`](/FS/readdir/). When
+you read pages with a cursor, every page must use the same sort as the first
+one.
## Include Subdirectories
diff --git a/src/docs/src/sidebar.js b/src/docs/src/sidebar.js
index d115450db..abdb5bce5 100755
--- a/src/docs/src/sidebar.js
+++ b/src/docs/src/sidebar.js
@@ -358,6 +358,14 @@ let sidebar = [
source: '/FS/stat.md',
path: '/FS/stat',
},
+ {
+ title: '
space()',
+ page_title: '
puter.fs.space()',
+ title_tag: 'puter.fs.space()',
+ icon: '/assets/img/function.svg',
+ source: '/FS/space.md',
+ path: '/FS/space',
+ },
{
title: '
delete()',
page_title: '
puter.fs.delete()',
@@ -414,6 +422,14 @@ let sidebar = [
source: '/FS/listShared.md',
path: '/FS/listShared',
},
+ {
+ title: '
listSharedByMe()',
+ page_title: '
puter.fs.listSharedByMe()',
+ title_tag: 'puter.fs.listSharedByMe()',
+ icon: '/assets/img/function.svg',
+ source: '/FS/listSharedByMe.md',
+ path: '/FS/listSharedByMe',
+ },
{
title: '
getShares()',
page_title: '
puter.fs.getShares()',
@@ -502,6 +518,14 @@ let sidebar = [
source: '/Events/workers.md',
path: '/Events/workers',
},
+ {
+ title: 'Subjects and events',
+ page_title: 'Subjects and events',
+ title_tag: 'Events Subjects',
+ icon: '/assets/img/object.svg',
+ source: '/Events/subjects.md',
+ path: '/Events/subjects',
+ },
],
},
{
@@ -633,14 +657,6 @@ let sidebar = [
source: '/Email.md',
path: '/Email',
children: [
- {
- title: '
sendTransactional()',
- page_title: '
puter.email.sendTransactional()',
- title_tag: 'puter.email.sendTransactional()',
- icon: '/assets/img/function.svg',
- source: '/Email/sendTransactional.md',
- path: '/Email/sendTransactional',
- },
{
title: '
list()',
page_title: '
puter.email.list()',
@@ -657,6 +673,14 @@ let sidebar = [
source: '/Email/get.md',
path: '/Email/get',
},
+ {
+ title: '
sendTransactional()',
+ page_title: '
puter.email.sendTransactional()',
+ title_tag: 'puter.email.sendTransactional()',
+ icon: '/assets/img/function.svg',
+ source: '/Email/sendTransactional.md',
+ path: '/Email/sendTransactional',
+ },
],
},
{
@@ -1374,6 +1398,13 @@ let sidebar = [
source: '/Objects/signinresult.md',
path: '/Objects/signinresult',
},
+ {
+ title: '
SpaceInfo',
+ title_tag: 'SpaceInfo',
+ icon: '/assets/img/object.svg',
+ source: '/Objects/spaceinfo.md',
+ path: '/Objects/spaceinfo',
+ },
{
title: '
Speech2TxtResult',
title_tag: 'Speech2TxtResult',