primitive-functions 0.2.0-alpha.0 → 0.2.0-alpha.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/index.d.ts +1697 -35
  2. package/index.js +92 -4
  3. package/package.json +1 -1
package/index.d.ts CHANGED
@@ -10,16 +10,91 @@
10
10
  * time, and reaching the stub means it did not.
11
11
  */
12
12
 
13
- /** The signed-in user on whose behalf the invocation runs. */
13
+ /**
14
+ * The signed-in user who invoked the function over HTTP.
15
+ *
16
+ * Attribution, never authority (#3279): function code acts as the system
17
+ * whoever called it, so this says WHO asked, and the rows the function writes
18
+ * are attributed to them. A trigger fire has no caller and `ctx.user` is
19
+ * null.
20
+ */
14
21
  export interface FunctionUser {
15
22
  userId: string;
16
23
  email: string | null;
17
24
  }
18
25
 
19
- /** What started this invocation. Only `http` exists today. */
20
- export interface FunctionTrigger {
21
- kind: "http";
22
- }
26
+ /**
27
+ * What started this invocation. Narrow on `kind`.
28
+ *
29
+ * The same union the runtime builds (`src/server-functions/trigger-descriptor.ts`),
30
+ * held identical by `tests/api/spec/function-trigger-declarations-3281.test.ts`:
31
+ * a field the fire path sets is declared here, and nothing is declared here
32
+ * that the fire path does not set.
33
+ */
34
+ export type FunctionTrigger =
35
+ /**
36
+ * An HTTP invocation: the caller is `ctx.user`.
37
+ *
38
+ * `runKey` is the key a `start` was coalesced by (#3562) — what you
39
+ * passed as `runKey` to make the run a singleton — and null when the
40
+ * start named none, or when the call was an `invoke`, which coalesces
41
+ * nothing.
42
+ */
43
+ | { kind: "http"; runKey: string | null }
44
+ /** A verified webhook delivery to the function's own key. */
45
+ | {
46
+ kind: "webhook";
47
+ /** The webhook key, which is the function key. */
48
+ webhookKey: string;
49
+ /** The platform's webhook row the delivery log lives under. */
50
+ webhookId: string;
51
+ /** The provider's event id (`X-Webhook-Event-Id`), when it sent one. */
52
+ externalEventId: string | null;
53
+ }
54
+ /** A cron entry firing, scheduled or from the `cron-triggers test` diagnostic. */
55
+ | {
56
+ kind: "cron";
57
+ /** The `[[function.triggers.cron]]` entry's `name`. */
58
+ name: string;
59
+ /** The platform's cron row. */
60
+ triggerId: string;
61
+ /** ISO 8601. The instant the fire was scheduled for; a diagnostic fire's is "now". */
62
+ scheduledFor: string;
63
+ /** Present, and true, only on the diagnostic fire: nothing was scheduled. */
64
+ manual?: true;
65
+ }
66
+ /**
67
+ * A DSL workflow's `workflow.call` step reached this function (#3288).
68
+ *
69
+ * TRANSITIONAL: the bridge exists so a workflow tree can migrate leaf-first
70
+ * during the transition, and it retires with the workflow engine in phase 7,
71
+ * taking this variant with it. `workflowKey` names the PARENT workflow, and
72
+ * `runId`/`stepId` locate the step inside its run.
73
+ */
74
+ | { kind: "workflow"; workflowKey: string; runId: string; stepId: string }
75
+ /**
76
+ * Another function started this one with `ctx.functions.start` (#3280).
77
+ *
78
+ * `functionKey` names the PARENT, and `runId` its run — null when the
79
+ * parent was a request invocation over HTTP, which writes no run row.
80
+ * `runKey` is the key the parent's start named, or null (#3562).
81
+ */
82
+ | {
83
+ kind: "function";
84
+ functionKey: string;
85
+ runId: string | null;
86
+ runKey: string | null;
87
+ }
88
+ /**
89
+ * An admin ran this function as the SYSTEM, from outside any trigger
90
+ * (#3448) — `primitive functions invoke --as system`.
91
+ *
92
+ * `ctx.user` is null and the code runs with the app's system authority, as
93
+ * it does for a cron or webhook fire. `userId` names the admin who asked:
94
+ * attribution, never the principal the code runs as. `runKey` is the key
95
+ * the admin's start named, or null (#3562).
96
+ */
97
+ | { kind: "manual"; userId: string; runKey: string | null };
23
98
 
24
99
  /** The result shape of an operation that answers in response headers. */
25
100
  export interface HeaderResult {
@@ -39,18 +114,309 @@ export interface PrimitivePlatformError extends Error {
39
114
 
40
115
  /**
41
116
  * The typed HTTP profile, generated from the platform's OpenAPI document. Its
42
- * namespaces mirror the operation ids: databaseTypeConfigs, databases, documents, functions.
117
+ * namespaces mirror the operation ids: analytics, blobBuckets, channels, collections, configVars, connections, databaseTypeConfigs, databases, documents, email, functions, gemini, groups, integrations, llm, locks, notifications, prompts, resourceMetadata, secrets, users.
43
118
  * Methods take one options object carrying the operation's path parameters,
44
119
  * query parameters, request headers (under their wire names) and `body`.
45
120
  *
46
121
  * Generated from the same operations as the runtime profile, so a method that
47
122
  * type-checks is a method the platform publishes — and a missing path
48
123
  * parameter or a missing required header is a compile error rather than a
49
- * `TypeError` at invoke time. Namespaces the GATEWAY does not yet allow are
50
- * still declared: they exist, they answer the gateway's structured denial, and
51
- * hiding them would make a route's availability look like a typo.
124
+ * `TypeError` at invoke time. Every call runs with the app's own SYSTEM
125
+ * authority (#3279): the invocation gate was the authorization. The two
126
+ * namespaces the gateway does not admit are still declared: they exist, they
127
+ * answer the gateway's structured denial, and hiding them would make a route's
128
+ * availability look like a typo. Those are `databaseTypeConfigs` whole, and
129
+ * `functions` except for `functions.start` — the nested-start door #3280
130
+ * admits by name, which `ctx.functions.start` calls. `functions.invoke`
131
+ * stays refused: same-run composition is an import.
52
132
  */
53
133
  export interface FunctionApi {
134
+ "analytics": {
135
+ /** `analytics.cohortRetention` — GET /analytics/cohort-retention. Cohort retention metrics */
136
+ "cohortRetention"(args?: {
137
+ "current_timestamp"?: string | number | boolean;
138
+ "_bust"?: string | number | boolean;
139
+ }): Promise<any>;
140
+ /** `analytics.dailyActive` — GET /analytics/daily-active. Daily active user time series */
141
+ "dailyActive"(args?: {
142
+ "windowDays"?: string | number | boolean;
143
+ "current_timestamp"?: string | number | boolean;
144
+ "_bust"?: string | number | boolean;
145
+ }): Promise<any>;
146
+ /** `analytics.errorGroups` — GET /analytics/errors/groups. Group failure events by fingerprint with per-day count buckets */
147
+ "errorGroups"(args?: {
148
+ "windowDays"?: string | number | boolean;
149
+ "limit"?: string | number | boolean;
150
+ "current_timestamp"?: string | number | boolean;
151
+ "_bust"?: string | number | boolean;
152
+ "filter"?: Record<string, Record<string, string | number | boolean | Array<string | number | boolean>>>;
153
+ }): Promise<any>;
154
+ /** `analytics.integrationUsage` — GET /analytics/integrations. Integration usage metrics */
155
+ "integrationUsage"(args?: {
156
+ "windowDays"?: string | number | boolean;
157
+ "current_timestamp"?: string | number | boolean;
158
+ "_bust"?: string | number | boolean;
159
+ }): Promise<any>;
160
+ /** `analytics.listEvents` — GET /analytics/events. List analytics events */
161
+ "listEvents"(args?: {
162
+ "windowDays"?: string | number | boolean;
163
+ "page"?: string | number | boolean;
164
+ "current_timestamp"?: string | number | boolean;
165
+ "_bust"?: string | number | boolean;
166
+ "filter"?: Record<string, Record<string, string | number | boolean | Array<string | number | boolean>>>;
167
+ }): Promise<any>;
168
+ /** `analytics.listEventsGrouped` — GET /analytics/events/grouped. List analytics events grouped by dimension */
169
+ "listEventsGrouped"(args?: {
170
+ "windowDays"?: string | number | boolean;
171
+ "groupBy"?: string | number | boolean;
172
+ "current_timestamp"?: string | number | boolean;
173
+ "_bust"?: string | number | boolean;
174
+ "filter"?: Record<string, Record<string, string | number | boolean | Array<string | number | boolean>>>;
175
+ }): Promise<any>;
176
+ /** `analytics.overviewDau` — GET /analytics/overview/dau. Daily active users overview */
177
+ "overviewDau"(args?: {
178
+ "current_timestamp"?: string | number | boolean;
179
+ "_bust"?: string | number | boolean;
180
+ }): Promise<any>;
181
+ /** `analytics.overviewGrowth` — GET /analytics/overview/growth. User growth overview */
182
+ "overviewGrowth"(args?: {
183
+ "windowDays"?: string | number | boolean;
184
+ "current_timestamp"?: string | number | boolean;
185
+ "_bust"?: string | number | boolean;
186
+ }): Promise<any>;
187
+ /** `analytics.overviewMau` — GET /analytics/overview/mau. Monthly active users overview */
188
+ "overviewMau"(args?: {
189
+ "current_timestamp"?: string | number | boolean;
190
+ "_bust"?: string | number | boolean;
191
+ }): Promise<any>;
192
+ /** `analytics.overviewWau` — GET /analytics/overview/wau. Weekly active users overview */
193
+ "overviewWau"(args?: {
194
+ "current_timestamp"?: string | number | boolean;
195
+ "_bust"?: string | number | boolean;
196
+ }): Promise<any>;
197
+ /** `analytics.rollingActive` — GET /analytics/rolling-active. Rolling active user time series */
198
+ "rollingActive"(args?: {
199
+ "windowDays"?: string | number | boolean;
200
+ "current_timestamp"?: string | number | boolean;
201
+ "_bust"?: string | number | boolean;
202
+ }): Promise<any>;
203
+ /** `analytics.searchUsers` — GET /analytics/users/search. Search app users for analytics */
204
+ "searchUsers"(args?: {
205
+ "q"?: string | number | boolean;
206
+ "limit"?: string | number | boolean;
207
+ "signupDay"?: string | number | boolean;
208
+ "signupStartDay"?: string | number | boolean;
209
+ "signupEndDay"?: string | number | boolean;
210
+ "offset"?: string | number | boolean;
211
+ "current_timestamp"?: string | number | boolean;
212
+ "_bust"?: string | number | boolean;
213
+ }): Promise<any>;
214
+ /** `analytics.topPrompts` — GET /analytics/prompts/top. List the most-executed prompts */
215
+ "topPrompts"(args?: {
216
+ "windowDays"?: string | number | boolean;
217
+ "limit"?: string | number | boolean;
218
+ "current_timestamp"?: string | number | boolean;
219
+ "_bust"?: string | number | boolean;
220
+ }): Promise<any>;
221
+ /** `analytics.topUsers` — GET /analytics/users/top. List the most active users */
222
+ "topUsers"(args?: {
223
+ "windowDays"?: string | number | boolean;
224
+ "limit"?: string | number | boolean;
225
+ "current_timestamp"?: string | number | boolean;
226
+ "_bust"?: string | number | boolean;
227
+ }): Promise<any>;
228
+ /** `analytics.topWorkflows` — GET /analytics/workflows/top. List the most-run workflows */
229
+ "topWorkflows"(args?: {
230
+ "windowDays"?: string | number | boolean;
231
+ "limit"?: string | number | boolean;
232
+ "current_timestamp"?: string | number | boolean;
233
+ "_bust"?: string | number | boolean;
234
+ }): Promise<any>;
235
+ /** `analytics.userDetail` — GET /analytics/users/{userUlid}/detail. Detailed analytics for a single user */
236
+ "userDetail"(args: {
237
+ "userUlid": string | number;
238
+ "current_timestamp"?: string | number | boolean;
239
+ "_bust"?: string | number | boolean;
240
+ }): Promise<any>;
241
+ /** `analytics.userSnapshot` — GET /analytics/users/{userUlid}/snapshot. Activity snapshot for a single user */
242
+ "userSnapshot"(args: {
243
+ "userUlid": string | number;
244
+ "current_timestamp"?: string | number | boolean;
245
+ "_bust"?: string | number | boolean;
246
+ }): Promise<any>;
247
+ /** `analytics.writeForUser` — POST /analytics/write-for-user. Write an analytics event for a subject user under a grant */
248
+ "writeForUser"(args: {
249
+ body: unknown;
250
+ }): Promise<any>;
251
+ };
252
+ "blobBuckets": {
253
+ /** `blobBuckets.createBucket` — POST /blob-buckets. Create a blob bucket */
254
+ "createBucket"(args: {
255
+ body: unknown;
256
+ }): Promise<any>;
257
+ /** `blobBuckets.delete` — DELETE /blob-buckets/{bucketId}/blobs/{blobId}. Delete a blob */
258
+ "delete"(args: {
259
+ "bucketId": string | number;
260
+ "blobId": string | number;
261
+ }): Promise<any>;
262
+ /** `blobBuckets.deleteBatch` — POST /blob-buckets/{bucketId}/blobs/delete. Batch delete blobs from a bucket */
263
+ "deleteBatch"(args: {
264
+ "bucketId": string | number;
265
+ body: unknown;
266
+ }): Promise<any>;
267
+ /** `blobBuckets.deleteBucket` — DELETE /blob-buckets/{bucketId}. Delete a blob bucket */
268
+ "deleteBucket"(args: {
269
+ "bucketId": string | number;
270
+ }): Promise<any>;
271
+ /** `blobBuckets.download` — GET /blob-buckets/{bucketId}/blobs/{blobId}. Download a blob (anonymous allowed for public buckets) */
272
+ "download"(args: {
273
+ "bucketId": string | number;
274
+ "blobId": string | number;
275
+ "disposition"?: string | number | boolean;
276
+ }): Promise<Uint8Array>;
277
+ /** `blobBuckets.downloadSigned` — GET /blob-buckets/{bucketId}/blobs/{blobId}/signed. Download a blob via a signed URL token */
278
+ "downloadSigned"(args: {
279
+ "bucketId": string | number;
280
+ "blobId": string | number;
281
+ "token"?: string | number | boolean;
282
+ }): Promise<Uint8Array>;
283
+ /** `blobBuckets.getBucket` — GET /blob-buckets/{bucketId}. Get a blob bucket by id */
284
+ "getBucket"(args: {
285
+ "bucketId": string | number;
286
+ }): Promise<any>;
287
+ /** `blobBuckets.getMetadata` — GET /blob-buckets/{bucketId}/blobs/{blobId}/metadata. Get blob metadata (anonymous allowed for public buckets) */
288
+ "getMetadata"(args: {
289
+ "bucketId": string | number;
290
+ "blobId": string | number;
291
+ }): Promise<any>;
292
+ /** `blobBuckets.getSignedUrl` — POST /blob-buckets/{bucketId}/blobs/{blobId}/signed-url. Create a signed download URL for a blob */
293
+ "getSignedUrl"(args: {
294
+ "bucketId": string | number;
295
+ "blobId": string | number;
296
+ body?: unknown;
297
+ }): Promise<any>;
298
+ /** `blobBuckets.list` — GET /blob-buckets/{bucketId}/blobs. List blobs in a bucket (anonymous allowed for public buckets) */
299
+ "list"(args: {
300
+ "bucketId": string | number;
301
+ "limit"?: string | number | boolean;
302
+ "cursor"?: string | number | boolean;
303
+ }): Promise<any>;
304
+ /** `blobBuckets.listBuckets` — GET /blob-buckets. List blob buckets */
305
+ "listBuckets"(args?: Record<string, never>): Promise<any>;
306
+ /** `blobBuckets.updateBucket` — PATCH /blob-buckets/{bucketId}. Update a blob bucket */
307
+ "updateBucket"(args: {
308
+ "bucketId": string | number;
309
+ body: unknown;
310
+ }): Promise<any>;
311
+ /** `blobBuckets.upload` — POST /blob-buckets/{bucketId}/blobs. Upload a blob to a bucket */
312
+ "upload"(args: {
313
+ "bucketId": string | number;
314
+ "X-Blob-Filename": string | number;
315
+ "X-Blob-Tags"?: string | number;
316
+ "Content-Type"?: string | number;
317
+ body: ArrayBuffer | Uint8Array | string;
318
+ }): Promise<any>;
319
+ };
320
+ "channels": {
321
+ /** `channels.authorize` — POST /channels/{channel}/grants. Issue a channel grant under a server function's grant */
322
+ "authorize"(args: {
323
+ "channel": string | number;
324
+ body: unknown;
325
+ }): Promise<any>;
326
+ /** `channels.publish` — POST /channels/{channel}/publish. Publish a message to a channel under a server function's grant */
327
+ "publish"(args: {
328
+ "channel": string | number;
329
+ body: unknown;
330
+ }): Promise<any>;
331
+ };
332
+ "collections": {
333
+ /** `collections.addDocument` — POST /collections/{collectionId}/documents. Add a document to a collection */
334
+ "addDocument"(args: {
335
+ "collectionId": string | number;
336
+ body: unknown;
337
+ }): Promise<any>;
338
+ /** `collections.addMember` — POST /collections/{collectionId}/members. Add a member to a collection */
339
+ "addMember"(args: {
340
+ "collectionId": string | number;
341
+ body: unknown;
342
+ }): Promise<any>;
343
+ /** `collections.create` — POST /collections. Create a collection */
344
+ "create"(args: {
345
+ body: unknown;
346
+ }): Promise<any>;
347
+ /** `collections.delete` — DELETE /collections/{collectionId}. Delete a collection */
348
+ "delete"(args: {
349
+ "collectionId": string | number;
350
+ }): Promise<any>;
351
+ /** `collections.get` — GET /collections/{collectionId}. Get a collection by id */
352
+ "get"(args: {
353
+ "collectionId": string | number;
354
+ }): Promise<any>;
355
+ /** `collections.getAccess` — GET /collections/{collectionId}/access. Get a collection's access summary */
356
+ "getAccess"(args: {
357
+ "collectionId": string | number;
358
+ }): Promise<any>;
359
+ /** `collections.grantGroupPermission` — POST /collections/{collectionId}/group-permissions. Grant a group permission on a collection */
360
+ "grantGroupPermission"(args: {
361
+ "collectionId": string | number;
362
+ body: unknown;
363
+ }): Promise<any>;
364
+ /** `collections.list` — GET /collections. List collections */
365
+ "list"(args?: {
366
+ "limit"?: string | number | boolean;
367
+ "cursor"?: string | number | boolean;
368
+ }): Promise<any>;
369
+ /** `collections.listCollectionsForDocument` — GET /documents/{documentId}/collections. List collections a document belongs to (reverse lookup) */
370
+ "listCollectionsForDocument"(args: {
371
+ "documentId": string | number;
372
+ "limit"?: string | number | boolean;
373
+ "cursor"?: string | number | boolean;
374
+ }): Promise<any>;
375
+ /** `collections.listDocuments` — GET /collections/{collectionId}/documents. List documents in a collection */
376
+ "listDocuments"(args: {
377
+ "collectionId": string | number;
378
+ "limit"?: string | number | boolean;
379
+ "cursor"?: string | number | boolean;
380
+ }): Promise<any>;
381
+ /** `collections.listPendingInvitations` — GET /collections/{collectionId}/pending-invitations. List a collection's pending invitations */
382
+ "listPendingInvitations"(args: {
383
+ "collectionId": string | number;
384
+ }): Promise<any>;
385
+ /** `collections.removeDocument` — DELETE /collections/{collectionId}/documents/{documentId}. Remove a document from a collection */
386
+ "removeDocument"(args: {
387
+ "collectionId": string | number;
388
+ "documentId": string | number;
389
+ }): Promise<any>;
390
+ /** `collections.removeMember` — DELETE /collections/{collectionId}/members/{userId}. Remove a member from a collection */
391
+ "removeMember"(args: {
392
+ "collectionId": string | number;
393
+ "userId": string | number;
394
+ }): Promise<any>;
395
+ /** `collections.revokeGroupPermission` — DELETE /collections/{collectionId}/group-permissions/{groupType}/{groupId}. Revoke a group permission on a collection */
396
+ "revokeGroupPermission"(args: {
397
+ "collectionId": string | number;
398
+ "groupType": string | number;
399
+ "groupId": string | number;
400
+ }): Promise<any>;
401
+ /** `collections.update` — PATCH /collections/{collectionId}. Update a collection */
402
+ "update"(args: {
403
+ "collectionId": string | number;
404
+ body: unknown;
405
+ }): Promise<any>;
406
+ };
407
+ "configVars": {
408
+ /** `configVars.get` — GET /config-vars/{name}. Read an app config var under a server function's grants */
409
+ "get"(args: {
410
+ "name": string | number;
411
+ }): Promise<any>;
412
+ };
413
+ "connections": {
414
+ /** `connections.send` — POST /connections/{connectionId}/send. Send a realtime message to one connection of this app */
415
+ "send"(args: {
416
+ "connectionId": string | number;
417
+ body: unknown;
418
+ }): Promise<any>;
419
+ };
54
420
  "databaseTypeConfigs": {
55
421
  /** `databaseTypeConfigs.create` — POST /databases/types. Create a database type */
56
422
  "create"(args: {
@@ -299,7 +665,7 @@ export interface FunctionApi {
299
665
  "query"(args: {
300
666
  "databaseId": string | number;
301
667
  body: unknown;
302
- }): Promise<any>;
668
+ }): Promise<{ items: any[]; hasMore: boolean; nextCursor?: string; prevCursor?: string }>;
303
669
  /** `databases.records.registerIndex` — POST /databases/{databaseId}/records/index/register. Database records: index/register (requires manager-or-higher on the database, or an app-admin/owner role) */
304
670
  "registerIndex"(args: {
305
671
  "databaseId": string | number;
@@ -341,6 +707,12 @@ export interface FunctionApi {
341
707
  "databaseId": string | number;
342
708
  "userId": string | number;
343
709
  }): Promise<any>;
710
+ /** `databases.runOperation` — POST /databases/{databaseId}/operations/{name}/run. Run a database operation under a server function's grants */
711
+ "runOperation"(args: {
712
+ "databaseId": string | number;
713
+ "name": string | number;
714
+ body?: unknown;
715
+ }): Promise<any>;
344
716
  /** `databases.transferOwnership` — POST /databases/{databaseId}/permissions/transfer. Transfer database ownership */
345
717
  "transferOwnership"(args: {
346
718
  "databaseId": string | number;
@@ -358,6 +730,12 @@ export interface FunctionApi {
358
730
  }): Promise<any>;
359
731
  };
360
732
  "documents": {
733
+ /** `documents.abortIngest` — POST /documents/{documentId}/ingest/{sessionId}/abort. Abort a bulk-load session before its swap */
734
+ "abortIngest"(args: {
735
+ "documentId": string | number;
736
+ "sessionId": string | number;
737
+ body: unknown;
738
+ }): Promise<any>;
361
739
  /** `documents.addTag` — POST /documents/{documentId}/tags. Add a tag to a document */
362
740
  "addTag"(args: {
363
741
  "documentId": string | number;
@@ -435,12 +813,18 @@ export interface FunctionApi {
435
813
  "clearLinkAccess"(args: {
436
814
  "documentId": string | number;
437
815
  }): Promise<any>;
816
+ /** `documents.commitIngest` — POST /documents/{documentId}/ingest/{sessionId}/commit. Commit a bulk-load session: freeze its manifest and apply it */
817
+ "commitIngest"(args: {
818
+ "documentId": string | number;
819
+ "sessionId": string | number;
820
+ body: unknown;
821
+ }): Promise<any>;
438
822
  /** `documents.create` — POST /documents. Create a new document */
439
823
  "create"(args: {
440
824
  body: unknown;
441
825
  }): Promise<any>;
442
- /** `documents.createInvitation` — POST /documents/{documentId}/invitations. Send an invitation to collaborate on a document */
443
- "createInvitation"(args: {
826
+ /** `documents.createIngest` — POST /documents/{documentId}/ingest. Open a bulk-load session on a large document */
827
+ "createIngest"(args: {
444
828
  "documentId": string | number;
445
829
  body: unknown;
446
830
  }): Promise<any>;
@@ -475,20 +859,10 @@ export interface FunctionApi {
475
859
  "debugStats"(args: {
476
860
  "documentId": string | number;
477
861
  }): Promise<any>;
478
- /** `documents.declineInvitation` — POST /documents/{documentId}/invitations/{invitationId}/decline. Decline a document invitation (invitee only) */
479
- "declineInvitation"(args: {
480
- "documentId": string | number;
481
- "invitationId": string | number;
482
- }): Promise<any>;
483
862
  /** `documents.delete` — DELETE /documents/{documentId}. Delete a document */
484
863
  "delete"(args: {
485
864
  "documentId": string | number;
486
865
  }): Promise<any>;
487
- /** `documents.deleteInvitation` — DELETE /documents/{documentId}/invitations/{invitationId}. Cancel a pending document invitation */
488
- "deleteInvitation"(args: {
489
- "documentId": string | number;
490
- "invitationId": string | number;
491
- }): Promise<any>;
492
866
  /** `documents.denyAccessRequest` — POST /documents/{documentId}/access-requests/{requestId}/deny. Deny a document access request */
493
867
  "denyAccessRequest"(args: {
494
868
  "documentId": string | number;
@@ -511,6 +885,11 @@ export interface FunctionApi {
511
885
  "getImportChainStatus"(args: {
512
886
  "documentId": string | number;
513
887
  }): Promise<any>;
888
+ /** `documents.getIngest` — GET /documents/{documentId}/ingest/{sessionId}. One bulk-load session's status */
889
+ "getIngest"(args: {
890
+ "documentId": string | number;
891
+ "sessionId": string | number;
892
+ }): Promise<any>;
514
893
  /** `documents.getLinkAccess` — GET /documents/{documentId}/link-access. Get the document's "anyone with the link" access level */
515
894
  "getLinkAccess"(args: {
516
895
  "documentId": string | number;
@@ -527,6 +906,11 @@ export interface FunctionApi {
527
906
  "getSchema"(args: {
528
907
  "documentId": string | number;
529
908
  }): Promise<any>;
909
+ /** `documents.getSnapshot` — GET /documents/{documentId}/snapshots/{buildId}. One snapshot build with its verification result */
910
+ "getSnapshot"(args: {
911
+ "documentId": string | number;
912
+ "buildId": string | number;
913
+ }): Promise<any>;
530
914
  /** `documents.getStats` — GET /documents/{documentId}/stats. Document statistics (record/model/blob counts, approximate size) */
531
915
  "getStats"(args: {
532
916
  "documentId": string | number;
@@ -556,8 +940,6 @@ export interface FunctionApi {
556
940
  "documentId": string | number;
557
941
  body: unknown;
558
942
  }): Promise<any>;
559
- /** `documents.list` — GET /documents. List documents the caller can access */
560
- "list"(args?: Record<string, never>): Promise<any>;
561
943
  /** `documents.listAccessRequests` — GET /documents/{documentId}/access-requests. List access requests for a document */
562
944
  "listAccessRequests"(args: {
563
945
  "documentId": string | number;
@@ -566,14 +948,22 @@ export interface FunctionApi {
566
948
  "listGroupPermissions"(args: {
567
949
  "documentId": string | number;
568
950
  }): Promise<any>;
569
- /** `documents.listInvitations` — GET /documents/{documentId}/invitations. List invitations issued for a document */
570
- "listInvitations"(args: {
951
+ /** `documents.listIngests` — GET /documents/{documentId}/ingest. List a large document's bulk-load sessions, newest first */
952
+ "listIngests"(args: {
571
953
  "documentId": string | number;
954
+ "limit"?: string | number | boolean;
955
+ "cursor"?: string | number | boolean;
572
956
  }): Promise<any>;
573
957
  /** `documents.listPendingInvitations` — GET /documents/{documentId}/pending-invitations. List a document's pending (deferred) invitations */
574
958
  "listPendingInvitations"(args: {
575
959
  "documentId": string | number;
576
960
  }): Promise<any>;
961
+ /** `documents.listSnapshots` — GET /documents/{documentId}/snapshots. List a large document's snapshot builds, newest first */
962
+ "listSnapshots"(args: {
963
+ "documentId": string | number;
964
+ "limit"?: string | number | boolean;
965
+ "cursor"?: string | number | boolean;
966
+ }): Promise<any>;
577
967
  /** `documents.open` — POST /documents/open. Open a document for collaboration (resolves access + alias) */
578
968
  "open"(args: {
579
969
  body: unknown;
@@ -594,6 +984,7 @@ export interface FunctionApi {
594
984
  "count"(args: {
595
985
  "documentId": string | number;
596
986
  "model": string | number;
987
+ "filter"?: Record<string, unknown> | string;
597
988
  }): Promise<any>;
598
989
  /** `documents.records.delete` — DELETE /documents/{documentId}/records/{model}/{recordId}. Delete a record from a document model */
599
990
  "delete"(args: {
@@ -612,6 +1003,9 @@ export interface FunctionApi {
612
1003
  "query"(args: {
613
1004
  "documentId": string | number;
614
1005
  "model": string | number;
1006
+ "limit"?: string | number | boolean;
1007
+ "cursor"?: string | number | boolean;
1008
+ "filter"?: Record<string, unknown> | string;
615
1009
  }): Promise<any>;
616
1010
  /** `documents.records.save` — POST /documents/{documentId}/records/{model}. Create or replace a record in a document model */
617
1011
  "save"(args: {
@@ -640,6 +1034,11 @@ export interface FunctionApi {
640
1034
  "documentId": string | number;
641
1035
  body: unknown;
642
1036
  }): Promise<any>;
1037
+ /** `documents.requestSnapshot` — POST /documents/{documentId}/snapshots. Snapshot a large document now: seal the open epoch and build a base */
1038
+ "requestSnapshot"(args: {
1039
+ "documentId": string | number;
1040
+ body: unknown;
1041
+ }): Promise<any>;
643
1042
  /** `documents.revokeGroupPermission` — DELETE /documents/{documentId}/group-permissions/{groupType}/{groupId}. Revoke a group permission on a document */
644
1043
  "revokeGroupPermission"(args: {
645
1044
  "documentId": string | number;
@@ -666,41 +1065,1304 @@ export interface FunctionApi {
666
1065
  "documentId": string | number;
667
1066
  body: unknown;
668
1067
  }): Promise<any>;
669
- /** `documents.validateAccess` — POST /documents/{documentId}/validate-access. Validate access to a document (accepts/upgrades pending invitations) */
1068
+ /** `documents.uploadIngestChunk` — POST /documents/{documentId}/ingest/{sessionId}/chunks. Upload one chunk of a bulk-load session's artifact */
1069
+ "uploadIngestChunk"(args: {
1070
+ "documentId": string | number;
1071
+ "sessionId": string | number;
1072
+ body: FormData | ArrayBuffer | Uint8Array | string;
1073
+ }): Promise<any>;
1074
+ /** `documents.validateAccess` — POST /documents/{documentId}/validate-access. Resolve the caller's effective access level for a document */
670
1075
  "validateAccess"(args: {
671
1076
  "documentId": string | number;
672
1077
  }): Promise<any>;
673
1078
  };
1079
+ "email": {
1080
+ /** `email.send` — POST /emails/send. Send an application email under a server function's grant */
1081
+ "send"(args: {
1082
+ body: unknown;
1083
+ }): Promise<any>;
1084
+ };
674
1085
  "functions": {
675
- /** `functions.invoke` — POST /functions/{functionKey}. Invoke a server function synchronously and return its result */
1086
+ /** `functions.invoke` — POST /functions/{functionKey}. Invoke a server function: a request function returns its result, a task function starts a run */
676
1087
  "invoke"(args: {
677
1088
  "functionKey": string | number;
678
1089
  body?: unknown;
679
1090
  }): Promise<any>;
1091
+ /** `functions.invokeAsSystem` — POST /functions/{functionKey}/invoke-as-system. Invoke a server function as the system, with no caller */
1092
+ "invokeAsSystem"(args: {
1093
+ "functionKey": string | number;
1094
+ body?: unknown;
1095
+ }): Promise<any>;
1096
+ /** `functions.logs` — GET /functions/{functionKey}/logs. List a server function's invocation logs, newest first */
1097
+ "logs"(args: {
1098
+ "functionKey": string | number;
1099
+ "limit"?: string | number | boolean;
1100
+ "cursor"?: string | number | boolean;
1101
+ "runId"?: string | number | boolean;
1102
+ }): Promise<any>;
1103
+ /** `functions.start` — POST /functions/{functionKey}/start. Start a server function as a task run */
1104
+ "start"(args: {
1105
+ "functionKey": string | number;
1106
+ body?: unknown;
1107
+ }): Promise<any>;
1108
+ };
1109
+ "gemini": {
1110
+ /** `gemini.countTokens` — POST /gemini/count-tokens. Count tokens for a Gemini request */
1111
+ "countTokens"(args: {
1112
+ body: unknown;
1113
+ }): Promise<any>;
1114
+ /** `gemini.generate` — POST /gemini/generate. Generate content with Gemini */
1115
+ "generate"(args: {
1116
+ body: unknown;
1117
+ }): Promise<any>;
1118
+ /** `gemini.generateRaw` — POST /gemini/generate-raw. Generate content with Gemini (raw passthrough) */
1119
+ "generateRaw"(args: {
1120
+ "model"?: string | number | boolean;
1121
+ body: unknown;
1122
+ }): Promise<any>;
1123
+ /** `gemini.models` — GET /gemini/models. List available Gemini models */
1124
+ "models"(args?: Record<string, never>): Promise<any>;
1125
+ };
1126
+ "groups": {
1127
+ /** `groups.addMember` — POST /groups/{groupType}/{groupId}/members. Add a member to a group */
1128
+ "addMember"(args: {
1129
+ "groupType": string | number;
1130
+ "groupId": string | number;
1131
+ body: unknown;
1132
+ }): Promise<any>;
1133
+ /** `groups.create` — POST /groups. Create a group */
1134
+ "create"(args: {
1135
+ body: unknown;
1136
+ }): Promise<any>;
1137
+ /** `groups.delete` — DELETE /groups/{groupType}/{groupId}. Delete a group */
1138
+ "delete"(args: {
1139
+ "groupType": string | number;
1140
+ "groupId": string | number;
1141
+ }): Promise<any>;
1142
+ /** `groups.get` — GET /groups/{groupType}/{groupId}. Get a group by type and id */
1143
+ "get"(args: {
1144
+ "groupType": string | number;
1145
+ "groupId": string | number;
1146
+ }): Promise<any>;
1147
+ /** `groups.list` — GET /groups. List groups */
1148
+ "list"(args?: {
1149
+ "type"?: string | number | boolean;
1150
+ "includeSystem"?: string | number | boolean;
1151
+ "limit"?: string | number | boolean;
1152
+ "cursor"?: string | number | boolean;
1153
+ }): Promise<any>;
1154
+ /** `groups.listDatabases` — GET /groups/{groupType}/{groupId}/databases. List databases shared with a group */
1155
+ "listDatabases"(args: {
1156
+ "groupType": string | number;
1157
+ "groupId": string | number;
1158
+ }): Promise<any>;
1159
+ /** `groups.listDocuments` — GET /groups/{groupType}/{groupId}/documents. List documents shared with a group */
1160
+ "listDocuments"(args: {
1161
+ "groupType": string | number;
1162
+ "groupId": string | number;
1163
+ }): Promise<any>;
1164
+ /** `groups.listMembers` — GET /groups/{groupType}/{groupId}/members. List a group's members */
1165
+ "listMembers"(args: {
1166
+ "groupType": string | number;
1167
+ "groupId": string | number;
1168
+ "limit"?: string | number | boolean;
1169
+ "cursor"?: string | number | boolean;
1170
+ "include"?: string | number | boolean;
1171
+ }): Promise<any>;
1172
+ /** `groups.listPendingInvitations` — GET /groups/{groupType}/{groupId}/pending-invitations. List a group's pending invitations */
1173
+ "listPendingInvitations"(args: {
1174
+ "groupType": string | number;
1175
+ "groupId": string | number;
1176
+ }): Promise<any>;
1177
+ /** `groups.listUserMemberships` — GET /users/{userId}/memberships. List a user's group memberships */
1178
+ "listUserMemberships"(args: {
1179
+ "userId": string | number;
1180
+ "includeSystem"?: string | number | boolean;
1181
+ "type"?: string | number | boolean;
1182
+ }): Promise<any>;
1183
+ /** `groups.removeMember` — DELETE /groups/{groupType}/{groupId}/members/{userId}. Remove a specific member from a group */
1184
+ "removeMember"(args: {
1185
+ "groupType": string | number;
1186
+ "groupId": string | number;
1187
+ "userId": string | number;
1188
+ }): Promise<any>;
1189
+ /** `groups.removeMemberByEmail` — DELETE /groups/{groupType}/{groupId}/members. Remove a group member or pending invitation by email */
1190
+ "removeMemberByEmail"(args: {
1191
+ "groupType": string | number;
1192
+ "groupId": string | number;
1193
+ "email"?: string | number | boolean;
1194
+ }): Promise<any>;
1195
+ /** `groups.update` — PATCH /groups/{groupType}/{groupId}. Update a group */
1196
+ "update"(args: {
1197
+ "groupType": string | number;
1198
+ "groupId": string | number;
1199
+ body: unknown;
1200
+ }): Promise<any>;
1201
+ };
1202
+ "integrations": {
1203
+ /** `integrations.call` — POST /integrations/{integrationKey}/call. Call an integration under a server function's grants */
1204
+ "call"(args: {
1205
+ "integrationKey": string | number;
1206
+ body?: unknown;
1207
+ }): Promise<any>;
1208
+ /** `integrations.list` — GET /integrations. List integrations configured for the app */
1209
+ "list"(args?: Record<string, never>): Promise<any>;
1210
+ /** `integrations.proxy` — POST /integrations/{integrationKey}/proxy. Proxy a request through a configured integration */
1211
+ "proxy"(args: {
1212
+ "integrationKey": string | number;
1213
+ }): Promise<any>;
680
1214
  };
1215
+ "llm": {
1216
+ /** `llm.chat` — POST /llm/chat. Run an LLM chat completion */
1217
+ "chat"(args: {
1218
+ body: unknown;
1219
+ }): Promise<any>;
1220
+ /** `llm.models` — GET /llm/models. List available LLM models */
1221
+ "models"(args?: Record<string, never>): Promise<any>;
1222
+ };
1223
+ "locks": {
1224
+ /** `locks.list` — GET /locks. List all currently-held named locks in the app */
1225
+ "list"(args?: Record<string, never>): Promise<any>;
1226
+ /** `locks.release` — POST /locks/release. Release a held named lock (requires the acquire handle) */
1227
+ "release"(args: {
1228
+ body: unknown;
1229
+ }): Promise<any>;
1230
+ /** `locks.renew` — POST /locks/renew. Extend the lease on a held named lock */
1231
+ "renew"(args: {
1232
+ body: unknown;
1233
+ }): Promise<any>;
1234
+ /** `locks.status` — POST /locks/status. Report the current holder of a named lock */
1235
+ "status"(args: {
1236
+ body: unknown;
1237
+ }): Promise<any>;
1238
+ /** `locks.tryAcquire` — POST /locks/acquire. Attempt to acquire a named lock (single non-blocking attempt) */
1239
+ "tryAcquire"(args: {
1240
+ body: unknown;
1241
+ }): Promise<any>;
1242
+ };
1243
+ "notifications": {
1244
+ /** `notifications.list` — GET /notifications. List the caller's in-app notification inbox (newest first) */
1245
+ "list"(args?: {
1246
+ "limit"?: string | number | boolean;
1247
+ "cursor"?: string | number | boolean;
1248
+ }): Promise<any>;
1249
+ /** `notifications.listDevices` — GET /me/push-tokens. List the caller's registered push devices */
1250
+ "listDevices"(args?: Record<string, never>): Promise<any>;
1251
+ /** `notifications.markAllRead` — POST /notifications/read-all. Mark all of the caller's notifications as read */
1252
+ "markAllRead"(args?: Record<string, never>): Promise<any>;
1253
+ /** `notifications.markRead` — PATCH /notifications/{notificationId}/read. Mark one of the caller's notifications as read */
1254
+ "markRead"(args: {
1255
+ "notificationId": string | number;
1256
+ }): Promise<any>;
1257
+ /** `notifications.registerDevice` — POST /me/push-tokens. Register (upsert) the caller's device push token */
1258
+ "registerDevice"(args: {
1259
+ body: unknown;
1260
+ }): Promise<any>;
1261
+ /** `notifications.send` — POST /notifications/send. Send a notification to an app user (durable in-app inbox row + live WS frame) */
1262
+ "send"(args: {
1263
+ body: unknown;
1264
+ }): Promise<any>;
1265
+ /** `notifications.unreadCount` — GET /notifications/unread-count. Count the caller's unread notifications */
1266
+ "unreadCount"(args?: Record<string, never>): Promise<any>;
1267
+ /** `notifications.unregisterDevice` — DELETE /me/push-tokens/{token}. Unregister one of the caller's device push tokens */
1268
+ "unregisterDevice"(args: {
1269
+ "token": string | number;
1270
+ }): Promise<any>;
1271
+ };
1272
+ "prompts": {
1273
+ /** `prompts.execute` — POST /prompts/{promptKey}/execute. Execute a saved prompt */
1274
+ "execute"(args: {
1275
+ "promptKey": string | number;
1276
+ }): Promise<any>;
1277
+ /** `prompts.run` — POST /prompts/{promptKey}/run. Run a prompt under a server function's grants */
1278
+ "run"(args: {
1279
+ "promptKey": string | number;
1280
+ body?: unknown;
1281
+ }): Promise<any>;
1282
+ };
1283
+ "resourceMetadata": {
1284
+ /** `resourceMetadata.delete` — DELETE /resources/{resourceType}/{resourceId}/metadata/{category}. Delete a resource's metadata for a category (idempotent) */
1285
+ "delete"(args: {
1286
+ "resourceType": string | number;
1287
+ "resourceId": string | number;
1288
+ "category": string | number;
1289
+ }): Promise<any>;
1290
+ /** `resourceMetadata.get` — GET /resources/{resourceType}/{resourceId}/metadata/{category}. Read a resource's metadata for a category */
1291
+ "get"(args: {
1292
+ "resourceType": string | number;
1293
+ "resourceId": string | number;
1294
+ "category": string | number;
1295
+ }): Promise<any>;
1296
+ /** `resourceMetadata.getBatch` — POST /resources/metadata/batch. Batch-read resource metadata (partial success) */
1297
+ "getBatch"(args: {
1298
+ "debug"?: string | number | boolean;
1299
+ body: unknown;
1300
+ }): Promise<any>;
1301
+ /** `resourceMetadata.list` — GET /resources/{resourceType}/{resourceId}/metadata. List all metadata categories on a resource */
1302
+ "list"(args: {
1303
+ "resourceType": string | number;
1304
+ "resourceId": string | number;
1305
+ }): Promise<any>;
1306
+ /** `resourceMetadata.resolve` — POST /metadata/resolve. Resolve a resource by a category's unique metadata value */
1307
+ "resolve"(args: {
1308
+ body: unknown;
1309
+ }): Promise<any>;
1310
+ /** `resourceMetadata.set` — PUT /resources/{resourceType}/{resourceId}/metadata/{category}. Write a resource's metadata for a category */
1311
+ "set"(args: {
1312
+ "resourceType": string | number;
1313
+ "resourceId": string | number;
1314
+ "category": string | number;
1315
+ body: unknown;
1316
+ }): Promise<any>;
1317
+ };
1318
+ "secrets": {
1319
+ /** `secrets.get` — GET /secrets/{name}. Read an app secret under a server function's grants */
1320
+ "get"(args: {
1321
+ "name": string | number;
1322
+ }): Promise<any>;
1323
+ };
1324
+ "users": {
1325
+ /** `users.getBasic` — GET /users/{userId}/basic. Get a user's basic public profile */
1326
+ "getBasic"(args: {
1327
+ "userId": string | number;
1328
+ }): Promise<any>;
1329
+ /** `users.getProfiles` — POST /users/profiles. Look up user profiles in bulk */
1330
+ "getProfiles"(args: {
1331
+ body: unknown;
1332
+ }): Promise<any>;
1333
+ /** `users.list` — GET /users. List app users */
1334
+ "list"(args?: {
1335
+ "email"?: string | number | boolean;
1336
+ "userId"?: string | number | boolean;
1337
+ "name"?: string | number | boolean;
1338
+ "limit"?: string | number | boolean;
1339
+ "cursor"?: string | number | boolean;
1340
+ }): Promise<any>;
1341
+ /** `users.lookup` — GET /users/lookup. Look up a user by identifier */
1342
+ "lookup"(args?: {
1343
+ "email"?: string | number | boolean;
1344
+ }): Promise<any>;
1345
+ /** `users.remove` — DELETE /users/{userId}. Remove a user from the app */
1346
+ "remove"(args: {
1347
+ "userId": string | number;
1348
+ }): Promise<any>;
1349
+ /** `users.send` — POST /users/{userId}/send. Send a realtime message to a member of this app */
1350
+ "send"(args: {
1351
+ "userId": string | number;
1352
+ body: unknown;
1353
+ }): Promise<any>;
1354
+ /** `users.setRole` — PUT /users/{userId}/role. Set a user's app role */
1355
+ "setRole"(args: {
1356
+ "userId": string | number;
1357
+ body: unknown;
1358
+ }): Promise<any>;
1359
+ };
1360
+ }
1361
+
1362
+ /**
1363
+ * The app's database types, model by model — EMPTY here and filled by the
1364
+ * declaration `primitive config push` writes into the config tree's
1365
+ * `functions/` directory (#3182). A tree that has pushed once therefore types
1366
+ * `ctx.db` against its own schemas; one that has not gets the open form and
1367
+ * still compiles.
1368
+ */
1369
+ export interface DatabaseSchemas {}
1370
+
1371
+ /**
1372
+ * The type keys `ctx.db` accepts.
1373
+ *
1374
+ * Every key the augmentation declares, and NOTHING else, once a tree has
1375
+ * pushed: naming a type the app does not have is a compile error. Before that
1376
+ * — an unaugmented tree, a function written before the first push —
1377
+ * `DatabaseSchemas` is empty and `keyof` it is `never`, which would make the
1378
+ * argument unwritable rather than open. So the empty case widens to `string`
1379
+ * and the handle is untyped instead of unusable.
1380
+ */
1381
+ export type DatabaseTypeKeys = [keyof DatabaseSchemas] extends [never]
1382
+ ? string
1383
+ : keyof DatabaseSchemas & string;
1384
+
1385
+ /** A model's row shape, or an open record for a type with no schema. */
1386
+ export type DatabaseModels<K extends string> = K extends keyof DatabaseSchemas
1387
+ ? DatabaseSchemas[K]
1388
+ : Record<string, Record<string, unknown>>;
1389
+
1390
+ /**
1391
+ * The app's functions, key by key — EMPTY here and filled by the declaration
1392
+ * `primitive config push` writes into the config tree's `functions/`
1393
+ * directory (`primitive-function-types.d.ts`): each key's declared input and
1394
+ * output types, from its `inputSchema` and `outputSchema`. It is what types
1395
+ * `defineFunction("<key>", handler)`.
1396
+ */
1397
+ export interface FunctionSchemas {}
1398
+
1399
+ /**
1400
+ * The keys `defineFunction(key, handler)` accepts: every key the augmentation
1401
+ * declares once a tree has pushed, so a key the tree does not declare is a
1402
+ * compile error — and `string` before that, the `DatabaseTypeKeys` rule, so an
1403
+ * unaugmented tree keeps compiling.
1404
+ */
1405
+ export type FunctionKeys = [keyof FunctionSchemas] extends [never]
1406
+ ? string
1407
+ : keyof FunctionSchemas & string;
1408
+
1409
+ /** A declared function's input type, or `any` when nothing is declared. */
1410
+ export type FunctionInput<K extends string> = K extends keyof FunctionSchemas
1411
+ ? FunctionSchemas[K] extends { input: infer I }
1412
+ ? I
1413
+ : any
1414
+ : any;
1415
+
1416
+ /** A declared function's output type, or `any` when nothing is declared. */
1417
+ export type FunctionOutput<K extends string> = K extends keyof FunctionSchemas
1418
+ ? FunctionSchemas[K] extends { output: infer O }
1419
+ ? O
1420
+ : any
1421
+ : any;
1422
+
1423
+ /**
1424
+ * The model names one database type addresses — its own, or any name at all
1425
+ * for a type that declares no schema (the DO is schemaless, so a grant on such
1426
+ * a type is legal and there is simply nothing to narrow to).
1427
+ */
1428
+ export type DatabaseModelNames<TModels> = [keyof TModels] extends [never]
1429
+ ? string
1430
+ : keyof TModels & string;
1431
+
1432
+ /**
1433
+ * A row as the platform HANDS IT BACK: whatever the schema declared, plus the
1434
+ * record identity the platform owns (#3450).
1435
+ *
1436
+ * Every stored record has an `id`. A save that arrives without one is given a
1437
+ * ULID, a projection always keeps it, and `delete` and `patch` take nothing
1438
+ * else — so a function that queries a model and then moves one of its rows
1439
+ * needs the id of the row it just read. A schema is free to say nothing about
1440
+ * that field, and most do; the identity is the platform's, not the schema's,
1441
+ * so it is added here rather than rendered into each declaration.
1442
+ *
1443
+ * A declared `id` is REPLACED, never intersected: one declared optional would
1444
+ * stay optional on a read row it is always present on, and one declared with
1445
+ * some other type would intersect to `never` and be unusable. The platform
1446
+ * stamps a string whatever the schema says.
1447
+ *
1448
+ * An `any` row stays `any` — `DatabaseHandle<any>` is `defineQuery`'s default
1449
+ * handle, and turning its rows into an object with one known member would
1450
+ * narrow code that was deliberately left open.
1451
+ *
1452
+ * Writes are untouched: every one of them takes `Partial<TRow>`, so a row a
1453
+ * function BUILDS still needs no id.
1454
+ */
1455
+ export type PlatformRow<TRow> = 0 extends 1 & TRow
1456
+ ? TRow
1457
+ : Omit<TRow, "id"> & { id: string };
1458
+
1459
+ /** One model's row shape, open when the type declares no schema. */
1460
+ export type DatabaseModelRow<TModels, M extends string> =
1461
+ M extends keyof TModels
1462
+ ? PlatformRow<TModels[M]>
1463
+ : Record<string, unknown> & { id: string };
1464
+
1465
+ /**
1466
+ * One model of one database, scoped: the `modelName` every records call needs
1467
+ * is bound once, so a call names an operation and its own arguments.
1468
+ *
1469
+ * Filters and options are ordinary objects. Inline filter typing arrives with
1470
+ * unified schemas — the intent's decision, not an omission — so what is typed
1471
+ * today is the model name and the row shape a read hands back.
1472
+ */
1473
+ export type ModelScopedBody<TBody = Record<string, unknown>> = TBody & {
1474
+ /**
1475
+ * Bound by the handle. Reserved so a body cannot look like a second way to
1476
+ * choose a model: the runtime assigns the handle's own model last, and a row
1477
+ * type that said otherwise would be a lie about what came back.
1478
+ */
1479
+ modelName?: never;
1480
+ };
1481
+
1482
+ /**
1483
+ * One write inside a model-scoped batch. Every batch verb is a write, so a
1484
+ * batch on a model needs that model's write grant whatever the ops are.
1485
+ */
1486
+ export type ModelBatchOperation<TRow> = {
1487
+ op: "save" | "patch" | "delete" | "increment" | "addToSet" | "removeFromSet";
1488
+ id?: string;
1489
+ data?: Partial<TRow>;
1490
+ /** Bound by the handle, for the same reason every other call's is. */
1491
+ modelName?: never;
1492
+ [key: string]: unknown;
1493
+ };
1494
+
1495
+ export interface DatabaseModelHandle<TRow> {
1496
+ /**
1497
+ * One page of this model's rows, in the decided envelope — `{ items,
1498
+ * hasMore, nextCursor? }` (#3287), the same shape `ctx.doc(...)`,
1499
+ * `ctx.users.list` and the client library answer. The route's own wire
1500
+ * shape is unchanged; the SDK is where the one name lives, so shared paging
1501
+ * code can be written once.
1502
+ */
1503
+ query(body?: ModelScopedBody): Promise<{ items: TRow[]; hasMore: boolean; nextCursor?: string }>;
1504
+ count(body?: ModelScopedBody): Promise<any>;
1505
+ aggregate(body: ModelScopedBody): Promise<any>;
1506
+ save(body: ModelScopedBody<{ id?: string; data: Partial<TRow>; [key: string]: unknown }>): Promise<any>;
1507
+ /**
1508
+ * Change some fields of ONE row — `databases.records.patch`, with this
1509
+ * handle's model bound (#3450).
1510
+ *
1511
+ * The record id is positional and the body may not carry another, for the
1512
+ * same reason it may not name another model: the runtime assigns both LAST,
1513
+ * so a body that appeared to choose either would be a lie about which row
1514
+ * moved. `condition`, `stringSets` and the rest of the operation's own keys
1515
+ * pass through untouched.
1516
+ */
1517
+ patch(
1518
+ recordId: string,
1519
+ body: ModelScopedBody<{ data: Partial<TRow>; id?: never; [key: string]: unknown }>
1520
+ ): Promise<any>;
1521
+ /**
1522
+ * Remove ONE row by the id a read handed back — `databases.records.delete`,
1523
+ * with this handle's model bound (#3450). An optional `condition` makes it
1524
+ * conditional, and travels untouched.
1525
+ */
1526
+ delete(
1527
+ recordId: string,
1528
+ body?: ModelScopedBody<{ condition?: Record<string, unknown>; id?: never }>
1529
+ ): Promise<any>;
1530
+ /** Several writes to THIS model in one request; the binding is per item. */
1531
+ batch(body: {
1532
+ operations: Array<ModelBatchOperation<TRow>>;
1533
+ atomic?: boolean;
1534
+ }): Promise<any>;
1535
+ }
1536
+
1537
+ /** One database, typed by the database TYPE key its grant names. */
1538
+ export interface DatabaseHandle<TModels> {
1539
+ /** The database this handle addresses — part of every query cache key. */
1540
+ databaseId: string;
1541
+ /** The type key this handle was opened with. */
1542
+ databaseType: string;
1543
+ model<M extends DatabaseModelNames<TModels>>(
1544
+ modelName: M
1545
+ ): DatabaseModelHandle<DatabaseModelRow<TModels, M>>;
1546
+ /**
1547
+ * A batch may also SPAN models, and then it names each one itself — the
1548
+ * model-scoped form above is the typed way to write the common case.
1549
+ */
1550
+ batch(body: {
1551
+ operations: Array<Record<string, unknown>>;
1552
+ atomic?: boolean;
1553
+ }): Promise<any>;
1554
+ /** Run one of the database type's prepared operations under this function's grants. */
1555
+ runOperation(name: string, params?: Record<string, unknown>): Promise<any>;
1556
+ }
1557
+
1558
+ /**
1559
+ * The app's DOCUMENT models — EMPTY here and filled by the declaration
1560
+ * `primitive config push` writes into the config tree's `functions/` directory
1561
+ * (`primitive-document-types.d.ts`, from the project's `models/models.toml`).
1562
+ * A project with no document schema gets the open form and still compiles.
1563
+ */
1564
+ export interface DocumentSchemas {}
1565
+
1566
+ /** The model names `ctx.doc(id).model()` accepts — every declared one, or `string` before a push. */
1567
+ export type DocumentModelNames = [keyof DocumentSchemas] extends [never]
1568
+ ? string
1569
+ : keyof DocumentSchemas & string;
1570
+
1571
+ /**
1572
+ * A document model's row shape, or an open record for an undeclared schema —
1573
+ * carrying the platform's record identity either way (#3450), by the same rule
1574
+ * and for the same reason as a database row: the records DO is one
1575
+ * implementation, and `patch` and `delete` here take an id too.
1576
+ */
1577
+ export type DocumentModelRow<M extends string> = M extends keyof DocumentSchemas
1578
+ ? PlatformRow<DocumentSchemas[M]>
1579
+ : Record<string, unknown> & { id: string };
1580
+
1581
+ /** The options a document model read takes: a filter object (or its JSON), a page size, a cursor. */
1582
+ export interface DocumentQueryOptions {
1583
+ filter?: Record<string, unknown> | string;
1584
+ limit?: number;
1585
+ cursor?: string;
1586
+ }
1587
+
1588
+ /**
1589
+ * One document model, bound to its document and its model name — a thin
1590
+ * binding over `ctx.api.documents.records.*` with the route's own shapes.
1591
+ */
1592
+ /**
1593
+ * One op of a document `batch` — the `documents.records.bulk` blob's shape,
1594
+ * typed to this handle's model (#3455).
1595
+ *
1596
+ * `model` is NOT a member: the handle already names it and assigns it last, so
1597
+ * a multi-model blob stays on the raw `ctx.api.documents.records.bulk`
1598
+ * operation rather than becoming a second way to say the same thing.
1599
+ */
1600
+ export type DocumentBatchOp<TRow> =
1601
+ | { action: "create" | "patch"; id: string; data: Partial<TRow>; precondition?: Record<string, unknown> }
1602
+ | { action: "delete"; id: string; precondition?: Record<string, unknown> };
1603
+
1604
+ /** What `documents.records.bulk` answers: the blob's aggregate effect. */
1605
+ export interface DocumentBulkResult {
1606
+ applied: number;
1607
+ added: Array<{ model: string; id: string }>;
1608
+ updated: Array<{ model: string; id: string }>;
1609
+ deleted: number;
1610
+ }
1611
+
1612
+ export interface DocumentModelHandle<TRow> {
1613
+ query(options?: DocumentQueryOptions): Promise<{ items: TRow[]; hasMore: boolean; nextCursor?: string }>;
1614
+ count(options?: { filter?: Record<string, unknown> | string }): Promise<{ count: number }>;
1615
+ aggregate(body: Record<string, unknown>): Promise<any>;
1616
+ save(body: { id: string; data: Partial<TRow>; options?: Record<string, unknown> }): Promise<{ record: TRow }>;
1617
+ patch(recordId: string, body: { data: Partial<TRow>; options?: Record<string, unknown> }): Promise<{ record: TRow }>;
1618
+ delete(recordId: string): Promise<{ deleted: boolean }>;
1619
+ /**
1620
+ * Apply an ordered blob of this model's ops in ONE transaction — all of it
1621
+ * commits or none of it does, and connected clients see one update.
1622
+ *
1623
+ * One model's ops, by construction: every op's `model` is this handle's.
1624
+ * A blob that spans models is the raw `ctx.api.documents.records.bulk`
1625
+ * operation's job.
1626
+ */
1627
+ batch(ops: Array<DocumentBatchOp<TRow>>): Promise<DocumentBulkResult>;
1628
+ }
1629
+
1630
+ /** A document, addressed once; its models are typed from `DocumentSchemas`. */
1631
+ export interface DocumentHandle {
1632
+ documentId: string;
1633
+ model<M extends DocumentModelNames>(modelName: M): DocumentModelHandle<DocumentModelRow<M>>;
1634
+ }
1635
+
1636
+ /**
1637
+ * One call through a granted integration — #3183.
1638
+ *
1639
+ * `path` is RELATIVE to the integration's `baseUrl`, and the host is the
1640
+ * integration's: the platform resolves and normalizes the path before checking
1641
+ * it against `allowedPaths`, so a protocol-relative path or a `..` segment is
1642
+ * refused rather than silently sending the request somewhere else.
1643
+ */
1644
+ export interface IntegrationCallRequest {
1645
+ /** Must be in the integration's `allowedMethods`. Defaults to its own default. */
1646
+ method?: string;
1647
+ /** Relative to the integration's `baseUrl`. */
1648
+ path?: string;
1649
+ query?: Record<string, unknown>;
1650
+ headers?: Record<string, string>;
1651
+ body?: unknown;
1652
+ /** form-urlencoded body; not combinable with `body`. */
1653
+ form?: Record<string, unknown>;
1654
+ bodyMode?: "json" | "raw" | "multipart";
1655
+ }
1656
+
1657
+ /** What the upstream answered, as the platform saw it. */
1658
+ export interface IntegrationCallResult {
1659
+ status: number;
1660
+ headers: Record<string, string>;
1661
+ body: unknown;
1662
+ durationMs: number;
1663
+ traceId: string;
1664
+ /** Set when the platform or the upstream refused; absent on success. */
1665
+ errorCode?: string;
1666
+ }
1667
+
1668
+ /** What a prompt run is given — the member execute route's own body. */
1669
+ export interface PromptRunArgs {
1670
+ variables?: Record<string, unknown>;
1671
+ /** Run against a different model for this call only. */
1672
+ modelOverride?: string;
1673
+ /** Pin a specific config OF THIS PROMPT; defaults to its active one. */
1674
+ configId?: string;
1675
+ }
1676
+
1677
+ /**
1678
+ * The app's PROMPTS — EMPTY here and filled by the declaration
1679
+ * `primitive config push` writes into the config tree's `functions/` directory
1680
+ * (`primitive-prompt-types.d.ts`, from the tree's `prompts/*.toml`). A prompt
1681
+ * that declares an `outputSchema` carries `{ output: <Key>PromptOutput }`; one
1682
+ * that declares none carries `{}`, so reading `parsed.field` on it is a
1683
+ * compile error rather than a runtime `undefined`.
1684
+ */
1685
+ export interface PromptSchemas {}
1686
+
1687
+ /** The keys `ctx.prompts.run` accepts — every declared one, or `string` before a push. */
1688
+ export type PromptKeys = [keyof PromptSchemas] extends [never]
1689
+ ? string
1690
+ : keyof PromptSchemas & string;
1691
+
1692
+ /** One prompt's declared output type, or `unknown` where none is declared. */
1693
+ export type PromptOutput<K extends string> = K extends keyof PromptSchemas
1694
+ ? PromptSchemas[K] extends { output: infer O }
1695
+ ? O
1696
+ : unknown
1697
+ : unknown;
1698
+
1699
+ /**
1700
+ * Why a prompt's own answer was refused — #3330. Never set on a PROVIDER
1701
+ * failure, which is what makes "the model returned the wrong shape"
1702
+ * distinguishable from "the model failed".
1703
+ */
1704
+ export type PromptOutputErrorCode =
1705
+ | "PROMPT_OUTPUT_NOT_JSON"
1706
+ | "PROMPT_OUTPUT_SCHEMA_VIOLATION";
1707
+
1708
+ /** A prompt that declares an output shape, when the answer matched it. */
1709
+ export interface PromptRunSuccess<TParsed> {
1710
+ success: true;
1711
+ output: string;
1712
+ /** The parsed, validated answer — no caller-side `JSON.parse` (#3330). */
1713
+ parsed: TParsed;
1714
+ error?: null;
1715
+ errorCode?: null;
1716
+ metrics?: Record<string, unknown>;
1717
+ configId: string;
1718
+ }
1719
+
1720
+ /**
1721
+ * The failure arm, whichever failed. `errorCode` is set for a SHAPE failure
1722
+ * and absent for a provider one; `output` carries the text either way, which
1723
+ * is where the model's own value lives — a validation `error` names paths and
1724
+ * schema constraints only.
1725
+ */
1726
+ export interface PromptRunFailure {
1727
+ success: false;
1728
+ output: string;
1729
+ error: string | null;
1730
+ errorCode?: PromptOutputErrorCode | null;
1731
+ parsed?: undefined;
1732
+ metrics?: Record<string, unknown>;
1733
+ configId: string;
1734
+ }
1735
+
1736
+ /** The envelope for a key whose declaration says nothing about its output. */
1737
+ export interface PromptRunOpenResult {
1738
+ success: boolean;
1739
+ output: string;
1740
+ /**
1741
+ * Present when the config that ran declares `outputFormat = "json"` and the
1742
+ * text parsed. Untyped: nothing declared its shape.
1743
+ */
1744
+ parsed?: unknown;
1745
+ error?: string | null;
1746
+ errorCode?: PromptOutputErrorCode | null;
1747
+ metrics?: Record<string, unknown>;
1748
+ configId: string;
1749
+ }
1750
+
1751
+ /**
1752
+ * The member execute envelope, plus what the prompt's own declaration adds.
1753
+ *
1754
+ * For a key whose `prompts/<key>.toml` declares an `outputSchema`, this is a
1755
+ * discriminated union: narrow on `success` and `parsed` is the declared type,
1756
+ * required. For every other key — and in a tree that has not pushed — it is
1757
+ * the open shape, with `parsed` optional and `unknown`.
1758
+ */
1759
+ export type PromptRunResult<K extends string = string> = K extends keyof PromptSchemas
1760
+ ? PromptSchemas[K] extends { output: infer O }
1761
+ ? PromptRunSuccess<O> | PromptRunFailure
1762
+ : PromptRunOpenResult
1763
+ : PromptRunOpenResult;
1764
+
1765
+ /** What a send delivered to. */
1766
+ export interface SendResult {
1767
+ /** Unique live connections the frame was sent to. Zero is a success. */
1768
+ connections: number;
1769
+ /** True when the per-user connection ceiling stopped the fanout. */
1770
+ truncated: boolean;
1771
+ }
1772
+
1773
+ /** Who a channel grant is for, and for how long — #3184. */
1774
+ export interface ChannelAuthorizeOptions {
1775
+ /**
1776
+ * The member the grant names. Caller mode defaults to the invoking caller;
1777
+ * system mode has no caller and must say.
1778
+ */
1779
+ userId?: string;
1780
+ /** Default 300 s, clamped to the platform ceiling of 900 s. */
1781
+ ttlSeconds?: number;
1782
+ }
1783
+
1784
+ /** The credential a client presents on the WebSocket it already has. */
1785
+ export interface ChannelGrantResult {
1786
+ channel: string;
1787
+ /** The signed grant. Hand it to the client; it is not readable here. */
1788
+ grant: string;
1789
+ /** Epoch milliseconds. Expiry is the only revocation a channel has. */
1790
+ expiresAt: number;
1791
+ }
1792
+
1793
+ /** Where to start and how big a page to take — #3280. */
1794
+ export interface UserPageOptions {
1795
+ /** Page size. Capped by the route at 100. */
1796
+ limit?: number;
1797
+ /** The `nextCursor` of the page before this one. */
1798
+ cursor?: string;
1799
+ }
1800
+
1801
+ /** One user on the roster, as the platform's own users route reports them. */
1802
+ export interface UserPageItem {
1803
+ userId: string;
1804
+ email: string | null;
1805
+ name: string | null;
1806
+ avatarUrl: string | null;
1807
+ role: string;
1808
+ addedAt: string | null;
1809
+ status: string;
1810
+ disabledAt: string | null;
1811
+ [key: string]: unknown;
1812
+ }
1813
+
1814
+ /** The decided paged envelope, the same shape every paged read answers. */
1815
+ export interface UserPage {
1816
+ items: UserPageItem[];
1817
+ hasMore: boolean;
1818
+ /** Absent on the last page. */
1819
+ nextCursor?: string;
1820
+ }
1821
+
1822
+ /** How a nested start is keyed and labelled — #3280. */
1823
+ export interface FunctionStartOptions {
1824
+ /**
1825
+ * Idempotency key, scoped to the tree's root initiator and the resolved
1826
+ * context document: a repeat replays the run that already exists rather than
1827
+ * starting a second. At most 100 characters.
1828
+ */
1829
+ runKey?: string;
1830
+ /**
1831
+ * The document the child's run is keyed under. Defaults through the
1832
+ * inheritance rule: the parent run's context, else the root caller's root
1833
+ * document, else `fn:<the root function's id>`. At most 100 characters.
1834
+ */
1835
+ contextDocId?: string;
1836
+ /** Caller metadata, at most 1 KB encoded. */
1837
+ meta?: Record<string, unknown>;
1838
+ }
1839
+
1840
+ /** The start envelope, the same one a workflow start answers with — #3280. */
1841
+ export interface FunctionStartResult {
1842
+ /** Poll this on the platform's run-status route. */
1843
+ runId: string;
1844
+ runKey: string;
1845
+ instanceId: string;
1846
+ status: string;
1847
+ /**
1848
+ * True when `runKey` already named a run in this tree, which this envelope
1849
+ * describes. That is what makes a run key a singleton.
1850
+ */
1851
+ existing?: boolean;
681
1852
  }
682
1853
 
683
1854
  export interface FunctionContext {
684
- user: FunctionUser;
1855
+ /** The HTTP caller, or null for a webhook or cron fire. */
1856
+ user: FunctionUser | null;
685
1857
  trigger: FunctionTrigger;
1858
+ /**
1859
+ * Which RUNTIME is hosting this code — #3482.
1860
+ *
1861
+ * Beside `trigger`, which says which DOOR fired, and they are different
1862
+ * questions: an HTTP call is whichever runtime the caller chose
1863
+ * (`invoke` or `start`), a cron fire is always a task and a webhook
1864
+ * delivery is always a request.
1865
+ *
1866
+ * To REFUSE a runtime rather than branch on it, call
1867
+ * `assertRuntime("task")` — at module scope to refuse it outright, or in
1868
+ * the handler to refuse one call.
1869
+ */
1870
+ runtime: "request" | "task";
1871
+ /**
1872
+ * The RUN this invocation belongs to, or null — #3562.
1873
+ *
1874
+ * Set under the task runtime (every slice of the run, the first and every
1875
+ * resume) and for a webhook-fired request run, which writes a row. Null for
1876
+ * an HTTP `invoke`, which writes no run row at all.
1877
+ *
1878
+ * It is the deterministic OWNER a task run presents to re-take its own
1879
+ * lease: `ctx.api.locks.tryAcquire({ body: { key, ttlMs, owner: ctx.runId } })`
1880
+ * succeeds for the run that already holds the key, with a fresh handle.
1881
+ */
1882
+ runId: string | null;
1883
+ /**
1884
+ * The SLICE of the run this code is running in, or null — #3562.
1885
+ *
1886
+ * The same identifier `primitive functions runs` and the run-status
1887
+ * route's `slice.sliceId` report. A task run is a sequence of slices: it
1888
+ * changes when the run hibernates and wakes, which is how a handler can tell
1889
+ * a resume from a first pass.
1890
+ *
1891
+ * Null under the request runtime: a request invocation is not a slice.
1892
+ */
1893
+ sliceId: string | null;
686
1894
  api: FunctionApi;
1895
+ /**
1896
+ * A typed handle over one database. The second argument is the database TYPE
1897
+ * key — the same key a `database:<type>/<model>:read|write` grant names —
1898
+ * and it is what selects the models this handle knows (#3182).
1899
+ */
1900
+ db<K extends DatabaseTypeKeys>(
1901
+ databaseId: string,
1902
+ databaseType: K
1903
+ ): DatabaseHandle<DatabaseModels<K>>;
1904
+ /**
1905
+ * A typed handle over one document's record models, typed from the
1906
+ * project's `models/models.toml` — `ctx.db`'s shape for documents. It
1907
+ * confers nothing: every call is a `documents.records.*` operation on the
1908
+ * same authority.
1909
+ */
1910
+ doc(documentId: string): DocumentHandle;
1911
+ /**
1912
+ * Outbound HTTP, through a granted integration and nowhere else (#3183).
1913
+ *
1914
+ * A function has no raw network access: every `fetch` to a host that is not
1915
+ * the platform answers `FUNCTION_EGRESS_DENIED`, grant or no grant. What an
1916
+ * `integration:<key>` grant buys is this call, which the MAIN WORKER makes on
1917
+ * the function's behalf — which is why the integration's `{{secrets.*}}`
1918
+ * credentials can be injected into the request without ever being visible to
1919
+ * the code that asked for it.
1920
+ */
1921
+ integrations: {
1922
+ call(
1923
+ integrationKey: string,
1924
+ request?: IntegrationCallRequest
1925
+ ): Promise<IntegrationCallResult>;
1926
+ };
1927
+ /**
1928
+ * Start a TASK function from inside this one — #3280.
1929
+ *
1930
+ * The one nested door, and it is a START. A request function is code and
1931
+ * code composes by being IMPORTED, so there is no synchronous nested
1932
+ * invocation to reach for; a request-mode callee answers
1933
+ * `FUNCTION_MODE_MISMATCH`.
1934
+ *
1935
+ * The callee's `access` gate is not consulted — this function's own gate was
1936
+ * the authorization — and every run in the tree is keyed by whoever started
1937
+ * it, so the member who invoked the root polls and terminates them all.
1938
+ * Nesting is bounded at four levels.
1939
+ */
1940
+ functions: {
1941
+ start(
1942
+ functionKey: string,
1943
+ input?: unknown,
1944
+ options?: FunctionStartOptions
1945
+ ): Promise<FunctionStartResult>;
1946
+ };
1947
+ /**
1948
+ * Run one of the app's prompts, with nothing to declare (#3183, #3279).
1949
+ *
1950
+ * The prompt's `accessRule` is not consulted — it states which MEMBERS may
1951
+ * execute the prompt from a client, and the invocation gate is what
1952
+ * authorized this call. The provider request is bounded by the invocation's
1953
+ * own deadline.
1954
+ *
1955
+ * TYPED BY KEY (#3330). Where `prompts/<key>.toml` declares an
1956
+ * `outputSchema`, the platform parses the answer, validates it against that
1957
+ * schema and hands it back as `parsed`, typed — so the shape is written once
1958
+ * in TOML instead of restated as a `JSON.parse` at every call site. Where
1959
+ * only the config that ran declares `outputFormat = "json"`, `parsed` is
1960
+ * present but `unknown`. Where neither is declared the envelope is exactly
1961
+ * what it has always been.
1962
+ *
1963
+ * A shape the declaration refuses is `success: false` with an `errorCode`
1964
+ * (`PROMPT_OUTPUT_NOT_JSON` or `PROMPT_OUTPUT_SCHEMA_VIOLATION`) and the
1965
+ * text still in `output`; a PROVIDER failure has `error` and no
1966
+ * `errorCode`, so the two are told apart without guessing.
1967
+ *
1968
+ * Once a tree has pushed, a key it does not declare is a compile error.
1969
+ */
1970
+ prompts: {
1971
+ run<K extends PromptKeys>(
1972
+ promptKey: K,
1973
+ args?: PromptRunArgs
1974
+ ): Promise<PromptRunResult<K>>;
1975
+ };
1976
+ /**
1977
+ * The value of one app secret, under a `secret:<NAME>` grant (#3183).
1978
+ *
1979
+ * Prefer `ctx.integrations.call` when the secret is a credential FOR an
1980
+ * integration: there the value is injected by the platform and never enters
1981
+ * the sandbox at all. This is for the cases an integration cannot express.
1982
+ *
1983
+ * Rejects with `FUNCTION_SECRET_NOT_FOUND` when the grant names a value
1984
+ * this environment has not been given — secrets are provisioned per
1985
+ * environment, out of band from the push.
1986
+ */
1987
+ secret(name: string): Promise<string>;
1988
+ /** The value of one app config var, under a `var:<NAME>` grant (#3183). */
1989
+ configVar(name: string): Promise<string>;
1990
+ /**
1991
+ * Realtime messages to this app's clients, under a `users:send` grant
1992
+ * (#3183).
1993
+ *
1994
+ * The recipient's client receives a `direct.message` frame carrying this
1995
+ * payload and the sending function's key. Presence is not guaranteed: a
1996
+ * member with no live socket answers `{ connections: 0 }` rather than an
1997
+ * error.
1998
+ */
1999
+ users: {
2000
+ /**
2001
+ * One page of the app's users — #3280.
2002
+ *
2003
+ * On the APP's system authority, whoever invoked the function: a member
2004
+ * who passes this function's gate reads whatever roster it chooses to
2005
+ * expose, so say who may in the function's own `access` expression.
2006
+ * `limit` is capped by the route at 100.
2007
+ */
2008
+ list(options?: UserPageOptions): Promise<UserPage>;
2009
+ /**
2010
+ * Every page of the app's users, in order — #3280.
2011
+ *
2012
+ * FOR A REQUEST FUNCTION AND A SINGLE-SLICE LOOP. A durable loop must take
2013
+ * its pages inside `step.do` with an EXPLICIT cursor: a memoized step does
2014
+ * not advance an iterator on resume, so an iteration across a sleep would
2015
+ * replay pages it had already taken.
2016
+ */
2017
+ iterate(options?: UserPageOptions): AsyncIterable<UserPage>;
2018
+ send(userId: string, payload: unknown): Promise<SendResult>;
2019
+ };
2020
+ /** The same frame to ONE connection, under a `connections:send` grant. */
2021
+ connections: {
2022
+ send(connectionId: string, payload: unknown): Promise<SendResult>;
2023
+ };
2024
+ /**
2025
+ * Realtime channels, under a `channel:<namespace>` grant that COVERS the
2026
+ * channel — #3184.
2027
+ *
2028
+ * `authorize` hands back a short-lived grant for ONE member to present on
2029
+ * the socket it already has; `publish` delivers a `channel.message` frame to
2030
+ * every connection that has joined and whose grant has not expired. Expiry is
2031
+ * the only revocation, so `expiresAt` comes back for the client to renew
2032
+ * ahead of — a renewal is another authorize and another subscribe.
2033
+ */
2034
+ channels: {
2035
+ authorize(
2036
+ channel: string,
2037
+ options?: ChannelAuthorizeOptions
2038
+ ): Promise<ChannelGrantResult>;
2039
+ publish(channel: string, payload: unknown): Promise<SendResult>;
2040
+ };
2041
+ }
2042
+
2043
+ /**
2044
+ * The step handle. `step` is the third argument under BOTH runners, so the
2045
+ * same body runs either way — which is what `mode = "any"` is for (#3454).
2046
+ *
2047
+ * Under the TASK runner it is Cloudflare Workflows' `WorkflowStep`, forwarded
2048
+ * untouched: `step.do` results are memoized and replayed, and `step.sleep`
2049
+ * hibernates.
2050
+ *
2051
+ * Under the REQUEST runner it is the platform's own, bounded by the request's
2052
+ * clock: `do` executes the body inline (there is no engine to memoize into,
2053
+ * and nothing is replayed); `sleep` and `sleepUntil` REALLY wait when the
2054
+ * remaining request budget covers the duration, and otherwise fail the
2055
+ * invocation with `errorCode: "FUNCTION_RUNTIME_REFUSED"` saying to start the
2056
+ * function as a task; `waitForEvent` fails the same way, because nothing can
2057
+ * deliver an event to an invocation that has to return now. The thrown error's
2058
+ * `name` is `STEP_NOT_AVAILABLE` in every case, so a body that catches it
2059
+ * keeps catching it.
2060
+ */
2061
+ export interface FunctionStep {
2062
+ do<T>(name: string, body: () => T | Promise<T>): Promise<T>;
2063
+ do<T>(
2064
+ name: string,
2065
+ config: Record<string, unknown>,
2066
+ body: () => T | Promise<T>
2067
+ ): Promise<T>;
2068
+ sleep(name: string, duration: number | string): Promise<void>;
2069
+ sleepUntil(name: string, timestamp: Date | number): Promise<void>;
2070
+ waitForEvent<T = unknown>(
2071
+ name: string,
2072
+ options: Record<string, unknown>
2073
+ ): Promise<T>;
687
2074
  }
688
2075
 
689
2076
  export declare const SDK_STUB_VERSION: string;
690
2077
 
691
2078
  /**
692
- * Declare a server function's handler.
2079
+ * Thrown by `assertRuntime` when this function is running under the other
2080
+ * runtime — #3482.
2081
+ *
2082
+ * The platform settles it as `status: "failed"` with
2083
+ * `errorCode: "FUNCTION_RUNTIME_REFUSED"`, on a request invocation's
2084
+ * envelope and on a task run's row and record alike.
2085
+ */
2086
+ export declare class FunctionRuntimeError extends Error {
2087
+ name: "FUNCTION_RUNTIME_REFUSED";
2088
+ /** The runtime this invocation is actually running under. */
2089
+ runtime: "request" | "task";
2090
+ /** The runtime the function asked for. */
2091
+ required: "request" | "task";
2092
+ }
2093
+
2094
+ /**
2095
+ * Refuse the runtime this function was not written for — #3482.
2096
+ *
2097
+ * A function is a function and the caller picks the runtime at each call, so
2098
+ * nothing in config says which one a function may run under. This is the lock,
2099
+ * and it lives in the one place that can always be right about the code:
2100
+ *
2101
+ * ```ts
2102
+ * import { assertRuntime } from "primitive-functions";
2103
+ *
2104
+ * // At module scope: this function is never runnable inside a request.
2105
+ * assertRuntime("task");
2106
+ *
2107
+ * export default async function (input, ctx, step) {
2108
+ * await step.sleep("cool-off", "1 hour");
2109
+ * return { ok: true };
2110
+ * }
2111
+ * ```
2112
+ *
2113
+ * Returns when the runtimes match; otherwise throws
2114
+ * {@link FunctionRuntimeError}, which the platform settles as
2115
+ * `status: "failed"` with `errorCode: "FUNCTION_RUNTIME_REFUSED"` and a
2116
+ * message naming both runtimes and the verb that takes the one required.
2117
+ */
2118
+ export declare function assertRuntime(required: "request" | "task"): void;
2119
+
2120
+ /**
2121
+ * The handler a function exports, typed for one declared key.
2122
+ *
2123
+ * `step` is the third argument under BOTH runners (#3186, #3454). A task run
2124
+ * gets the engine's live `WorkflowStep`; a request invocation gets the
2125
+ * platform's own, where `do` runs the body inline and a wait the request
2126
+ * budget cannot cover fails the invocation naming the task runner — so a
2127
+ * task-authored body can be exercised as a request, and the one place the two
2128
+ * genuinely differ says so rather than pretending.
2129
+ */
2130
+ export type FunctionHandler<TInput, TOutput> = (
2131
+ input: TInput,
2132
+ ctx: FunctionContext,
2133
+ step: FunctionStep
2134
+ ) => Promise<TOutput>;
2135
+
2136
+ /**
2137
+ * Declare a server function's handler, typed from its declaration.
2138
+ *
2139
+ * The KEYED form, `defineFunction("<key>", handler)`, is typed from the
2140
+ * `FunctionSchemas` augmentation `primitive config push` writes beside the
2141
+ * sources: `input` is the key's declared input, the return is checked against
2142
+ * its declared output, and a key the tree does not declare is a compile error.
2143
+ * `config push` also refuses an entry whose key names another file's. In a
2144
+ * tree that has not pushed yet the key is any string and both types are `any`.
693
2145
  *
694
- * Both authoring styles reach the platform bootstrap identically — a plain
695
- * default export taking `(input, ctx)` runs exactly as a wrapped one does —
696
- * so this is a typing and intent marker, not a different calling convention.
2146
+ * The UNKEYED form is what it always was: author-declared generics. Both
2147
+ * styles reach the platform bootstrap identically — a plain default export
2148
+ * taking `(input, ctx, step)` runs exactly as a wrapped one does — so this is
2149
+ * a typing and intent marker, not a different calling convention.
697
2150
  */
2151
+ export declare function defineFunction<K extends FunctionKeys>(
2152
+ key: K,
2153
+ handler: (
2154
+ input: FunctionInput<K>,
2155
+ ctx: FunctionContext,
2156
+ step: FunctionStep
2157
+ ) => FunctionOutput<K> | Promise<FunctionOutput<K>>
2158
+ ): FunctionHandler<FunctionInput<K>, FunctionOutput<K>> & { functionKey: K };
698
2159
  export declare function defineFunction<TInput = unknown, TOutput = unknown>(
699
- handler: (input: TInput, ctx: FunctionContext) => TOutput | Promise<TOutput>
700
- ): (input: TInput, ctx: FunctionContext) => Promise<TOutput>;
2160
+ handler: (
2161
+ input: TInput,
2162
+ ctx: FunctionContext,
2163
+ step: FunctionStep
2164
+ ) => TOutput | Promise<TOutput>
2165
+ ): FunctionHandler<TInput, TOutput>;
2166
+
2167
+ /**
2168
+ * One declared parameter of a registered query.
2169
+ *
2170
+ * `caller: true` (or `type: "$caller"`) is the intent's IMMUTABLE binding:
2171
+ * the platform injects the invocation's user and refuses a call that supplies
2172
+ * the parameter itself.
2173
+ */
2174
+ /** The scalar parameter types, and an array parameter's element types. */
2175
+ export type QueryScalarParamType = "string" | "number" | "boolean" | "any";
2176
+
2177
+ export interface QueryParamSpec {
2178
+ type?: QueryScalarParamType | "$caller" | "array";
2179
+ /** For `type: "array"` — the element type; absent means any element. */
2180
+ items?: { type: QueryScalarParamType };
2181
+ caller?: boolean;
2182
+ optional?: boolean;
2183
+ default?: unknown;
2184
+ }
2185
+
2186
+ /** A scalar parameter type's TypeScript type. */
2187
+ export type QueryScalarValue<T> = T extends "string"
2188
+ ? string
2189
+ : T extends "number"
2190
+ ? number
2191
+ : T extends "boolean"
2192
+ ? boolean
2193
+ : T extends "$caller"
2194
+ ? string
2195
+ : any;
2196
+
2197
+ /** The value `run` receives for one declared parameter. */
2198
+ export type QueryParamValue<S> = S extends { caller: true }
2199
+ ? string
2200
+ : S extends { type: "array"; items: { type: infer I } }
2201
+ ? QueryScalarValue<I>[]
2202
+ : S extends { type: "array" }
2203
+ ? unknown[]
2204
+ : S extends { type: infer T }
2205
+ ? QueryScalarValue<T>
2206
+ : any;
2207
+
2208
+ type QueryOptionalKeys<P> = {
2209
+ [K in keyof P]: P[K] extends { optional: true } ? K : never;
2210
+ }[keyof P];
2211
+
2212
+ /**
2213
+ * `run`'s `params`, typed from the declaration: scalars as their scalar,
2214
+ * `$caller` as a string, an array as `T[]`, an `optional: true` parameter
2215
+ * as an optional key (a parameter with a default is always present). An
2216
+ * undeclared name is a compile error.
2217
+ */
2218
+ export type QueryParams<P> = {
2219
+ [K in Exclude<keyof P, QueryOptionalKeys<P>>]: QueryParamValue<P[K]>;
2220
+ } & {
2221
+ [K in QueryOptionalKeys<P>]?: QueryParamValue<P[K]>;
2222
+ };
2223
+
2224
+ /** A registration's definition with its parameters DECLARED, so `run` is typed from them. */
2225
+ export interface TypedQueryDefinition<TDb, TSpec extends Record<string, QueryParamSpec>, TResult> {
2226
+ models: string[];
2227
+ params?: TSpec;
2228
+ cache?: { ttlMs: number };
2229
+ run: (db: TDb, params: QueryParams<TSpec>) => TResult | Promise<TResult>;
2230
+ }
2231
+
2232
+ export interface QueryDefinition<TDb, TParams, TResult> {
2233
+ /** The models this query touches. Caching is off unless the run matches. */
2234
+ models: string[];
2235
+ params?: Record<string, QueryParamSpec>;
2236
+ /**
2237
+ * Cache the answer for `ttlMs` — capped by the platform, and applied ONLY
2238
+ * when the run is verified: every model the body touched was declared, and
2239
+ * no prepared operation was used. A write through the handle drops the
2240
+ * entries that read the written model.
2241
+ */
2242
+ cache?: { ttlMs: number };
2243
+ run: (db: TDb, params: TParams) => TResult | Promise<TResult>;
2244
+ }
2245
+
2246
+ /**
2247
+ * Register a named query over a database handle (#3187).
2248
+ *
2249
+ * A registration helper, not a language: `run` is ordinary code over the same
2250
+ * typed handle, and what registration adds is a name, declared parameters
2251
+ * (validated, coerced and defaulted), the `$caller` binding, and a cache the
2252
+ * platform may use when it can verify what the run touched.
2253
+ *
2254
+ * Register at MODULE scope: a registration made inside the handler still works
2255
+ * for direct calls but is invisible to `config push`'s manifest.
2256
+ *
2257
+ * `run`'s `params` are TYPED from the declaration (the first overload,
2258
+ * inferred with a `const` type parameter): a scalar as its scalar, `$caller`
2259
+ * as a string, `{ type: "array", items: { type: "string" } }` as `string[]`,
2260
+ * an `optional: true` parameter as an optional key; an undeclared name inside
2261
+ * `run` is a compile error. A declared default is coerced to the declared
2262
+ * type at registration, so `run` receives what the declaration promises. The
2263
+ * second overload keeps an explicit `TParams`.
2264
+ */
2265
+ export declare function defineQuery<
2266
+ const TSpec extends Record<string, QueryParamSpec>,
2267
+ TDb = DatabaseHandle<any>,
2268
+ TResult = any
2269
+ >(
2270
+ name: string,
2271
+ definition: TypedQueryDefinition<TDb, TSpec, TResult>
2272
+ ): (db: TDb, params?: Record<string, unknown>) => Promise<TResult>;
2273
+ export declare function defineQuery<
2274
+ TDb = DatabaseHandle<any>,
2275
+ TParams = Record<string, any>,
2276
+ TResult = any
2277
+ >(
2278
+ name: string,
2279
+ definition: QueryDefinition<TDb, TParams, TResult>
2280
+ ): (db: TDb, params?: Record<string, unknown>) => Promise<TResult>;
2281
+
2282
+ /** The same, for a registration that writes. A mutation is never cached. */
2283
+ export declare function defineMutation<
2284
+ const TSpec extends Record<string, QueryParamSpec>,
2285
+ TDb = DatabaseHandle<any>,
2286
+ TResult = any
2287
+ >(
2288
+ name: string,
2289
+ definition: TypedQueryDefinition<TDb, TSpec, TResult>
2290
+ ): (db: TDb, params?: Record<string, unknown>) => Promise<TResult>;
2291
+ export declare function defineMutation<
2292
+ TDb = DatabaseHandle<any>,
2293
+ TParams = Record<string, any>,
2294
+ TResult = any
2295
+ >(
2296
+ name: string,
2297
+ definition: QueryDefinition<TDb, TParams, TResult>
2298
+ ): (db: TDb, params?: Record<string, unknown>) => Promise<TResult>;
2299
+
2300
+ /** The platform's cap on a registered query's cache TTL, in milliseconds. */
2301
+ export declare const MAX_QUERY_CACHE_TTL_MS: number;
2302
+
2303
+ /**
2304
+ * Map over items with BOUNDED concurrency (#3187). Results come back in the
2305
+ * input's order; the first rejection wins and nothing new is started after it.
2306
+ *
2307
+ * The bound matters inside a sandbox: an unbounded fan-out over a page of rows
2308
+ * spends the whole subrequest ceiling in one line.
2309
+ */
2310
+ export declare function pMap<T, R>(
2311
+ items: Iterable<T>,
2312
+ mapper: (item: T, index: number) => R | Promise<R>,
2313
+ options?: { concurrency?: number }
2314
+ ): Promise<R[]>;
2315
+
2316
+ /** The default concurrency `pMap` applies when none is given. */
2317
+ export declare const PMAP_DEFAULT_CONCURRENCY: number;
2318
+
2319
+ /**
2320
+ * A time-ordered ULID.
2321
+ *
2322
+ * GENERATE IT INSIDE `step.do`. A durable run re-executes its handler on
2323
+ * every resume and memoizes only what a step returned, so an id minted at the
2324
+ * top of a handler is a different id after a sleep — and whatever it was
2325
+ * written into is orphaned.
2326
+ */
2327
+ export declare function ulid(): string;
2328
+
2329
+ /** One family's durable `step.do` configuration. */
2330
+ export interface StepPolicy {
2331
+ retries: { limit: number; delay: string; backoff: string };
2332
+ timeout: string;
2333
+ }
2334
+
2335
+ /**
2336
+ * The per-family step policies the DSL runners use, generated from their own
2337
+ * timeout module (#3187):
2338
+ *
2339
+ * await step.do("send-receipt", stepPolicy.email, () =>
2340
+ * ctx.api.email.send({ body: { to, subject, htmlBody } }));
2341
+ *
2342
+ * `retries: 0` on email and the AI families is deliberate — a retried timeout
2343
+ * would send the same email twice or bill the same generation twice.
2344
+ */
2345
+ export declare const stepPolicy: {
2346
+ email: StepPolicy;
2347
+ llm: StepPolicy;
2348
+ gemini: StepPolicy;
2349
+ database: StepPolicy;
2350
+ };
701
2351
 
2352
+ // Every member the three runtime copies put on their own default export — the
2353
+ // platform's `sdk-core.ts`, the published stub and the CLI's collect stub —
2354
+ // and nothing else. A helper the module hands a default import but does not
2355
+ // declare here is one an author cannot call without the compiler refusing it
2356
+ // (#3482 CR3482-002); `tests/api/spec/function-profile.test.ts` holds the four
2357
+ // lists equal and compiles a default import against them.
702
2358
  declare const _default: {
703
2359
  SDK_STUB_VERSION: string;
704
2360
  defineFunction: typeof defineFunction;
2361
+ defineQuery: typeof defineQuery;
2362
+ defineMutation: typeof defineMutation;
2363
+ pMap: typeof pMap;
2364
+ ulid: typeof ulid;
2365
+ assertRuntime: typeof assertRuntime;
2366
+ FunctionRuntimeError: typeof FunctionRuntimeError;
705
2367
  };
706
2368
  export default _default;