primitive-functions 0.2.0-alpha.8 → 0.2.0-alpha.9

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 +174 -28
  2. package/index.js +30 -0
  3. package/package.json +1 -1
package/index.d.ts CHANGED
@@ -32,8 +32,15 @@ export interface FunctionUser {
32
32
  * that the fire path does not set.
33
33
  */
34
34
  export type FunctionTrigger =
35
- /** An HTTP invocation: the caller is `ctx.user`. */
36
- | { kind: "http" }
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 }
37
44
  /** A verified webhook delivery to the function's own key. */
38
45
  | {
39
46
  kind: "webhook";
@@ -56,8 +63,6 @@ export type FunctionTrigger =
56
63
  /** Present, and true, only on the diagnostic fire: nothing was scheduled. */
57
64
  manual?: true;
58
65
  }
59
- /** A write to a watched database type. */
60
- | { kind: "database"; databaseId: string; databaseType: string }
61
66
  /**
62
67
  * A DSL workflow's `workflow.call` step reached this function (#3288).
63
68
  *
@@ -71,19 +76,25 @@ export type FunctionTrigger =
71
76
  * Another function started this one with `ctx.functions.start` (#3280).
72
77
  *
73
78
  * `functionKey` names the PARENT, and `runId` its run — null when the
74
- * parent was a request invocation over HTTP, which writes no run row, or a
75
- * database-change fire, which writes none either.
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).
76
81
  */
77
- | { kind: "function"; functionKey: string; runId: string | null }
82
+ | {
83
+ kind: "function";
84
+ functionKey: string;
85
+ runId: string | null;
86
+ runKey: string | null;
87
+ }
78
88
  /**
79
89
  * An admin ran this function as the SYSTEM, from outside any trigger
80
90
  * (#3448) — `primitive functions invoke --as system`.
81
91
  *
82
92
  * `ctx.user` is null and the code runs with the app's system authority, as
83
93
  * it does for a cron or webhook fire. `userId` names the admin who asked:
84
- * attribution, never the principal the code runs as.
94
+ * attribution, never the principal the code runs as. `runKey` is the key
95
+ * the admin's start named, or null (#3562).
85
96
  */
86
- | { kind: "manual"; userId: string };
97
+ | { kind: "manual"; userId: string; runKey: string | null };
87
98
 
88
99
  /** The result shape of an operation that answers in response headers. */
89
100
  export interface HeaderResult {
@@ -717,6 +728,12 @@ export interface FunctionApi {
717
728
  }): Promise<any>;
718
729
  };
719
730
  "documents": {
731
+ /** `documents.abortIngest` — POST /documents/{documentId}/ingest/{sessionId}/abort. Abort a bulk-load session before its swap */
732
+ "abortIngest"(args: {
733
+ "documentId": string | number;
734
+ "sessionId": string | number;
735
+ body: unknown;
736
+ }): Promise<any>;
720
737
  /** `documents.addTag` — POST /documents/{documentId}/tags. Add a tag to a document */
721
738
  "addTag"(args: {
722
739
  "documentId": string | number;
@@ -794,10 +811,21 @@ export interface FunctionApi {
794
811
  "clearLinkAccess"(args: {
795
812
  "documentId": string | number;
796
813
  }): Promise<any>;
814
+ /** `documents.commitIngest` — POST /documents/{documentId}/ingest/{sessionId}/commit. Commit a bulk-load session: freeze its manifest and apply it */
815
+ "commitIngest"(args: {
816
+ "documentId": string | number;
817
+ "sessionId": string | number;
818
+ body: unknown;
819
+ }): Promise<any>;
797
820
  /** `documents.create` — POST /documents. Create a new document */
798
821
  "create"(args: {
799
822
  body: unknown;
800
823
  }): Promise<any>;
824
+ /** `documents.createIngest` — POST /documents/{documentId}/ingest. Open a bulk-load session on a large document */
825
+ "createIngest"(args: {
826
+ "documentId": string | number;
827
+ body: unknown;
828
+ }): Promise<any>;
801
829
  /** `documents.createWithAlias` — POST /documents/create-with-alias. Create a document and bind it to an alias */
802
830
  "createWithAlias"(args: {
803
831
  body: unknown;
@@ -855,6 +883,11 @@ export interface FunctionApi {
855
883
  "getImportChainStatus"(args: {
856
884
  "documentId": string | number;
857
885
  }): Promise<any>;
886
+ /** `documents.getIngest` — GET /documents/{documentId}/ingest/{sessionId}. One bulk-load session's status */
887
+ "getIngest"(args: {
888
+ "documentId": string | number;
889
+ "sessionId": string | number;
890
+ }): Promise<any>;
858
891
  /** `documents.getLinkAccess` — GET /documents/{documentId}/link-access. Get the document's "anyone with the link" access level */
859
892
  "getLinkAccess"(args: {
860
893
  "documentId": string | number;
@@ -913,6 +946,12 @@ export interface FunctionApi {
913
946
  "listGroupPermissions"(args: {
914
947
  "documentId": string | number;
915
948
  }): Promise<any>;
949
+ /** `documents.listIngests` — GET /documents/{documentId}/ingest. List a large document's bulk-load sessions, newest first */
950
+ "listIngests"(args: {
951
+ "documentId": string | number;
952
+ "limit"?: string | number | boolean;
953
+ "cursor"?: string | number | boolean;
954
+ }): Promise<any>;
916
955
  /** `documents.listPendingInvitations` — GET /documents/{documentId}/pending-invitations. List a document's pending (deferred) invitations */
917
956
  "listPendingInvitations"(args: {
918
957
  "documentId": string | number;
@@ -1019,6 +1058,12 @@ export interface FunctionApi {
1019
1058
  "documentId": string | number;
1020
1059
  body: unknown;
1021
1060
  }): Promise<any>;
1061
+ /** `documents.uploadIngestChunk` — POST /documents/{documentId}/ingest/{sessionId}/chunks. Upload one chunk of a bulk-load session's artifact */
1062
+ "uploadIngestChunk"(args: {
1063
+ "documentId": string | number;
1064
+ "sessionId": string | number;
1065
+ body: FormData | ArrayBuffer | Uint8Array | string;
1066
+ }): Promise<any>;
1022
1067
  /** `documents.validateAccess` — POST /documents/{documentId}/validate-access. Resolve the caller's effective access level for a document */
1023
1068
  "validateAccess"(args: {
1024
1069
  "documentId": string | number;
@@ -1048,7 +1093,7 @@ export interface FunctionApi {
1048
1093
  "cursor"?: string | number | boolean;
1049
1094
  "runId"?: string | number | boolean;
1050
1095
  }): Promise<any>;
1051
- /** `functions.start` — POST /functions/{functionKey}/start. Start a task function from inside another server function */
1096
+ /** `functions.start` — POST /functions/{functionKey}/start. Start a server function as a task run */
1052
1097
  "start"(args: {
1053
1098
  "functionKey": string | number;
1054
1099
  body?: unknown;
@@ -1339,8 +1384,8 @@ export type DatabaseModels<K extends string> = K extends keyof DatabaseSchemas
1339
1384
  * The app's functions, key by key — EMPTY here and filled by the declaration
1340
1385
  * `primitive config push` writes into the config tree's `functions/`
1341
1386
  * directory (`primitive-function-types.d.ts`): each key's declared input and
1342
- * output types (from its `inputSchema` and `outputSchema`) and its mode. It
1343
- * is what types `defineFunction("<key>", handler)`.
1387
+ * output types, from its `inputSchema` and `outputSchema`. It is what types
1388
+ * `defineFunction("<key>", handler)`.
1344
1389
  */
1345
1390
  export interface FunctionSchemas {}
1346
1391
 
@@ -1368,20 +1413,6 @@ export type FunctionOutput<K extends string> = K extends keyof FunctionSchemas
1368
1413
  : any
1369
1414
  : any;
1370
1415
 
1371
- /**
1372
- * A declared function's mode, fixed at generation time.
1373
- *
1374
- * `"any"` — the default when the authored file names no mode (#3454) — takes
1375
- * EITHER runner and the caller picks at call time; `"request"` and `"task"`
1376
- * lock it to one. An UNDECLARED key is the whole union, because nothing has
1377
- * said which of the three it is.
1378
- */
1379
- export type FunctionMode<K extends string> = K extends keyof FunctionSchemas
1380
- ? FunctionSchemas[K] extends { mode: infer M }
1381
- ? M
1382
- : "request" | "task" | "any"
1383
- : "request" | "task" | "any";
1384
-
1385
1416
  /**
1386
1417
  * The model names one database type addresses — its own, or any name at all
1387
1418
  * for a type that declares no schema (the DO is schemaless, so a grant on such
@@ -1551,6 +1582,26 @@ export interface DocumentQueryOptions {
1551
1582
  * One document model, bound to its document and its model name — a thin
1552
1583
  * binding over `ctx.api.documents.records.*` with the route's own shapes.
1553
1584
  */
1585
+ /**
1586
+ * One op of a document `batch` — the `documents.records.bulk` blob's shape,
1587
+ * typed to this handle's model (#3455).
1588
+ *
1589
+ * `model` is NOT a member: the handle already names it and assigns it last, so
1590
+ * a multi-model blob stays on the raw `ctx.api.documents.records.bulk`
1591
+ * operation rather than becoming a second way to say the same thing.
1592
+ */
1593
+ export type DocumentBatchOp<TRow> =
1594
+ | { action: "create" | "patch"; id: string; data: Partial<TRow>; precondition?: Record<string, unknown> }
1595
+ | { action: "delete"; id: string; precondition?: Record<string, unknown> };
1596
+
1597
+ /** What `documents.records.bulk` answers: the blob's aggregate effect. */
1598
+ export interface DocumentBulkResult {
1599
+ applied: number;
1600
+ added: Array<{ model: string; id: string }>;
1601
+ updated: Array<{ model: string; id: string }>;
1602
+ deleted: number;
1603
+ }
1604
+
1554
1605
  export interface DocumentModelHandle<TRow> {
1555
1606
  query(options?: DocumentQueryOptions): Promise<{ items: TRow[]; hasMore: boolean; nextCursor?: string }>;
1556
1607
  count(options?: { filter?: Record<string, unknown> | string }): Promise<{ count: number }>;
@@ -1558,6 +1609,15 @@ export interface DocumentModelHandle<TRow> {
1558
1609
  save(body: { id: string; data: Partial<TRow>; options?: Record<string, unknown> }): Promise<{ record: TRow }>;
1559
1610
  patch(recordId: string, body: { data: Partial<TRow>; options?: Record<string, unknown> }): Promise<{ record: TRow }>;
1560
1611
  delete(recordId: string): Promise<{ deleted: boolean }>;
1612
+ /**
1613
+ * Apply an ordered blob of this model's ops in ONE transaction — all of it
1614
+ * commits or none of it does, and connected clients see one update.
1615
+ *
1616
+ * One model's ops, by construction: every op's `model` is this handle's.
1617
+ * A blob that spans models is the raw `ctx.api.documents.records.bulk`
1618
+ * operation's job.
1619
+ */
1620
+ batch(ops: Array<DocumentBatchOp<TRow>>): Promise<DocumentBulkResult>;
1561
1621
  }
1562
1622
 
1563
1623
  /** A document, addressed once; its models are typed from `DocumentSchemas`. */
@@ -1785,9 +1845,45 @@ export interface FunctionStartResult {
1785
1845
  }
1786
1846
 
1787
1847
  export interface FunctionContext {
1788
- /** The HTTP caller, or null for a webhook, cron or database-change fire. */
1848
+ /** The HTTP caller, or null for a webhook or cron fire. */
1789
1849
  user: FunctionUser | null;
1790
1850
  trigger: FunctionTrigger;
1851
+ /**
1852
+ * Which RUNTIME is hosting this code — #3482.
1853
+ *
1854
+ * Beside `trigger`, which says which DOOR fired, and they are different
1855
+ * questions: an HTTP call is whichever runtime the caller chose
1856
+ * (`invoke` or `start`), a cron fire is always a task and a webhook
1857
+ * delivery is always a request.
1858
+ *
1859
+ * To REFUSE a runtime rather than branch on it, call
1860
+ * `assertRuntime("task")` — at module scope to refuse it outright, or in
1861
+ * the handler to refuse one call.
1862
+ */
1863
+ runtime: "request" | "task";
1864
+ /**
1865
+ * The RUN this invocation belongs to, or null — #3562.
1866
+ *
1867
+ * Set under the task runtime (every slice of the run, the first and every
1868
+ * resume) and for a webhook-fired request run, which writes a row. Null for
1869
+ * an HTTP `invoke`, which writes no run row at all.
1870
+ *
1871
+ * It is the deterministic OWNER a task run presents to re-take its own
1872
+ * lease: `ctx.api.locks.tryAcquire({ body: { key, ttlMs, owner: ctx.runId } })`
1873
+ * succeeds for the run that already holds the key, with a fresh handle.
1874
+ */
1875
+ runId: string | null;
1876
+ /**
1877
+ * The SLICE of the run this code is running in, or null — #3562.
1878
+ *
1879
+ * The same identifier `primitive functions runs` and the run-status
1880
+ * route's `slice.sliceId` report. A task run is a sequence of slices: it
1881
+ * changes when the run hibernates and wakes, which is how a handler can tell
1882
+ * a resume from a first pass.
1883
+ *
1884
+ * Null under the request runtime: a request invocation is not a slice.
1885
+ */
1886
+ sliceId: string | null;
1791
1887
  api: FunctionApi;
1792
1888
  /**
1793
1889
  * A typed handle over one database. The second argument is the database TYPE
@@ -1949,7 +2045,7 @@ export interface FunctionContext {
1949
2045
  * clock: `do` executes the body inline (there is no engine to memoize into,
1950
2046
  * and nothing is replayed); `sleep` and `sleepUntil` REALLY wait when the
1951
2047
  * remaining request budget covers the duration, and otherwise fail the
1952
- * invocation with `errorCode: "FUNCTION_STEP_NEEDS_TASK"` saying to start the
2048
+ * invocation with `errorCode: "FUNCTION_RUNTIME_REFUSED"` saying to start the
1953
2049
  * function as a task; `waitForEvent` fails the same way, because nothing can
1954
2050
  * deliver an event to an invocation that has to return now. The thrown error's
1955
2051
  * `name` is `STEP_NOT_AVAILABLE` in every case, so a body that catches it
@@ -1972,6 +2068,48 @@ export interface FunctionStep {
1972
2068
 
1973
2069
  export declare const SDK_STUB_VERSION: string;
1974
2070
 
2071
+ /**
2072
+ * Thrown by `assertRuntime` when this function is running under the other
2073
+ * runtime — #3482.
2074
+ *
2075
+ * The platform settles it as `status: "failed"` with
2076
+ * `errorCode: "FUNCTION_RUNTIME_REFUSED"`, on a request invocation's
2077
+ * envelope and on a task run's row and record alike.
2078
+ */
2079
+ export declare class FunctionRuntimeError extends Error {
2080
+ name: "FUNCTION_RUNTIME_REFUSED";
2081
+ /** The runtime this invocation is actually running under. */
2082
+ runtime: "request" | "task";
2083
+ /** The runtime the function asked for. */
2084
+ required: "request" | "task";
2085
+ }
2086
+
2087
+ /**
2088
+ * Refuse the runtime this function was not written for — #3482.
2089
+ *
2090
+ * A function is a function and the caller picks the runtime at each call, so
2091
+ * nothing in config says which one a function may run under. This is the lock,
2092
+ * and it lives in the one place that can always be right about the code:
2093
+ *
2094
+ * ```ts
2095
+ * import { assertRuntime } from "primitive-functions";
2096
+ *
2097
+ * // At module scope: this function is never runnable inside a request.
2098
+ * assertRuntime("task");
2099
+ *
2100
+ * export default async function (input, ctx, step) {
2101
+ * await step.sleep("cool-off", "1 hour");
2102
+ * return { ok: true };
2103
+ * }
2104
+ * ```
2105
+ *
2106
+ * Returns when the runtimes match; otherwise throws
2107
+ * {@link FunctionRuntimeError}, which the platform settles as
2108
+ * `status: "failed"` with `errorCode: "FUNCTION_RUNTIME_REFUSED"` and a
2109
+ * message naming both runtimes and the verb that takes the one required.
2110
+ */
2111
+ export declare function assertRuntime(required: "request" | "task"): void;
2112
+
1975
2113
  /**
1976
2114
  * The handler a function exports, typed for one declared key.
1977
2115
  *
@@ -2204,6 +2342,12 @@ export declare const stepPolicy: {
2204
2342
  database: StepPolicy;
2205
2343
  };
2206
2344
 
2345
+ // Every member the three runtime copies put on their own default export — the
2346
+ // platform's `sdk-core.ts`, the published stub and the CLI's collect stub —
2347
+ // and nothing else. A helper the module hands a default import but does not
2348
+ // declare here is one an author cannot call without the compiler refusing it
2349
+ // (#3482 CR3482-002); `tests/api/spec/function-profile.test.ts` holds the four
2350
+ // lists equal and compiles a default import against them.
2207
2351
  declare const _default: {
2208
2352
  SDK_STUB_VERSION: string;
2209
2353
  defineFunction: typeof defineFunction;
@@ -2211,5 +2355,7 @@ declare const _default: {
2211
2355
  defineMutation: typeof defineMutation;
2212
2356
  pMap: typeof pMap;
2213
2357
  ulid: typeof ulid;
2358
+ assertRuntime: typeof assertRuntime;
2359
+ FunctionRuntimeError: typeof FunctionRuntimeError;
2214
2360
  };
2215
2361
  export default _default;
package/index.js CHANGED
@@ -82,6 +82,34 @@ export const stepPolicy = {
82
82
  database: null,
83
83
  };
84
84
 
85
+ /**
86
+ * The runtime refusal's error class — #3482.
87
+ *
88
+ * Carried rather than thrown from: a function narrows a `catch` with
89
+ * `instanceof FunctionRuntimeError`, and a class that threw on construction
90
+ * would break a build that only type-checks. The platform copy is the one that
91
+ * is ever really thrown.
92
+ */
93
+ export class FunctionRuntimeError extends Error {
94
+ constructor(message, runtime, required) {
95
+ super(message);
96
+ this.name = "FUNCTION_RUNTIME_REFUSED";
97
+ this.runtime = runtime;
98
+ this.required = required;
99
+ }
100
+ }
101
+
102
+ /**
103
+ * Refuse the runtime this function was not written for — #3482.
104
+ *
105
+ * The platform copy compares the required runtime against the one the isolate
106
+ * declared. This one refuses, loudly, like every other function here: an
107
+ * assertion answered outside a sandbox would be answering about nothing.
108
+ */
109
+ export function assertRuntime() {
110
+ throw new Error(NOT_PROVIDED);
111
+ }
112
+
85
113
  export default {
86
114
  SDK_STUB_VERSION,
87
115
  defineFunction,
@@ -89,4 +117,6 @@ export default {
89
117
  defineMutation,
90
118
  pMap,
91
119
  ulid,
120
+ assertRuntime,
121
+ FunctionRuntimeError,
92
122
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "primitive-functions",
3
- "version": "0.2.0-alpha.8",
3
+ "version": "0.2.0-alpha.9",
4
4
  "description": "The module a Primitive server function imports. Supplied by the platform at invoke time; reserved here so the name is ours before the runtime lands.",
5
5
  "license": "UNLICENSED",
6
6
  "author": "Primitive LLC",