@oxy-hq/sdk 2.12.0 → 2.16.0

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 (47) hide show
  1. package/README.md +67 -0
  2. package/dist/{function-context-D8eyZuw_.d.cts → function-context-BNpL5bFb.d.cts} +223 -20
  3. package/dist/function-context-BNpL5bFb.d.cts.map +1 -0
  4. package/dist/{function-context-D8eyZuw_.d.mts → function-context-BNpL5bFb.d.mts} +223 -20
  5. package/dist/function-context-BNpL5bFb.d.mts.map +1 -0
  6. package/dist/index.cjs +80 -16
  7. package/dist/index.cjs.map +1 -1
  8. package/dist/index.d.cts +164 -3
  9. package/dist/index.d.cts.map +1 -1
  10. package/dist/index.d.mts +164 -3
  11. package/dist/index.d.mts.map +1 -1
  12. package/dist/index.mjs +78 -16
  13. package/dist/index.mjs.map +1 -1
  14. package/dist/ops.d.cts +1 -1
  15. package/dist/ops.d.mts +1 -1
  16. package/dist/{react-DW7Z96sD.d.mts → react-CljeXJuw.d.cts} +142 -8
  17. package/dist/react-CljeXJuw.d.cts.map +1 -0
  18. package/dist/{react-DW7Z96sD.d.cts → react-CljeXJuw.d.mts} +142 -8
  19. package/dist/react-CljeXJuw.d.mts.map +1 -0
  20. package/dist/{react-DcT-mUPj.cjs → react-Dvkv2deI.cjs} +110 -59
  21. package/dist/react-Dvkv2deI.cjs.map +1 -0
  22. package/dist/{react-BXGyzgz0.mjs → react-OW1t_J0M.mjs} +103 -26
  23. package/dist/react-OW1t_J0M.mjs.map +1 -0
  24. package/dist/rolldown-runtime-KC0qvQup.cjs +34 -0
  25. package/dist/shell.cjs +38 -3
  26. package/dist/shell.cjs.map +1 -1
  27. package/dist/shell.d.cts +26 -3
  28. package/dist/shell.d.cts.map +1 -1
  29. package/dist/shell.d.mts +26 -3
  30. package/dist/shell.d.mts.map +1 -1
  31. package/dist/shell.mjs +34 -2
  32. package/dist/shell.mjs.map +1 -1
  33. package/dist/testing.cjs +4431 -0
  34. package/dist/testing.cjs.map +1 -0
  35. package/dist/testing.d.cts +656 -0
  36. package/dist/testing.d.cts.map +1 -0
  37. package/dist/testing.d.mts +656 -0
  38. package/dist/testing.d.mts.map +1 -0
  39. package/dist/testing.mjs +4395 -0
  40. package/dist/testing.mjs.map +1 -0
  41. package/package.json +22 -9
  42. package/dist/function-context-D8eyZuw_.d.cts.map +0 -1
  43. package/dist/function-context-D8eyZuw_.d.mts.map +0 -1
  44. package/dist/react-BXGyzgz0.mjs.map +0 -1
  45. package/dist/react-DW7Z96sD.d.cts.map +0 -1
  46. package/dist/react-DW7Z96sD.d.mts.map +0 -1
  47. package/dist/react-DcT-mUPj.cjs.map +0 -1
@@ -0,0 +1,656 @@
1
+
2
+ import { _ as OxyOrgAssignment, f as OxyFunctionRow, l as OxyFunctionContext, p as OxyFunctionUser, v as OxyOrgPlace } from "./function-context-BNpL5bFb.cjs";
3
+ //#region src/testing/gates.d.ts
4
+ /** The loose shape a JSON import of `oxy-app.json` satisfies. */
5
+ interface ManifestLike {
6
+ slug?: string;
7
+ functions?: Record<string, Record<string, unknown> | undefined>;
8
+ }
9
+ /** `FunctionCapabilities` for one function, plus its write allowlist. */
10
+ interface FunctionGates {
11
+ functionName: string;
12
+ slug: string;
13
+ /** `app_<slug>` with `-` → `_`, or `null` when the slug cannot back a writer. */
14
+ writerSchema: string | null;
15
+ secretsWrite: boolean;
16
+ emailSend: boolean;
17
+ orgRead: boolean;
18
+ storageRead: boolean;
19
+ storageWrite: boolean;
20
+ oltp: boolean;
21
+ airhouse: boolean;
22
+ /** `destinations`: the databases `ctx.warehouse` writes and `ctx.tx` may name. */
23
+ destinations: string[];
24
+ /** `customerWarehouseWrites`: database → the non-blank reason, blank ones dropped. */
25
+ customerWarehouseWrites: Record<string, string>;
26
+ /** `fetch.maxResponseBytes`, when the entry raises the ceiling. */
27
+ fetchMaxBytes: number | null;
28
+ }
29
+ /**
30
+ * `app_writer_name` + `WriterRef::schema_name`: the schema an app's slug
31
+ * derives, or `null` when it derives none.
32
+ *
33
+ * Both halves matter. `app_writer_name` refuses a slug containing `_` outright
34
+ * — `my_app` would otherwise alias the hyphenated sibling `my-app` onto one
35
+ * schema and role — and then runs the derived name through `validate_name`,
36
+ * which is what rejects a leading digit, an over-long name, and any character
37
+ * outside `[a-z0-9_]`. Skipping that second half handed back a schema for slugs
38
+ * the host answers `None` for, which is the difference between a green test and
39
+ * a closed `ctx.oltp` in production.
40
+ */
41
+ declare function writerSchema(slug: string): string | null;
42
+ /** Read the gates for `functionName` from `manifest`, or throw naming what is missing. */
43
+ declare function readGates(manifest: ManifestLike, functionName: string): FunctionGates;
44
+ //#endregion
45
+ //#region src/testing/host-contract.d.ts
46
+ /**
47
+ * The host's words, gates, op names and rules, as `@oxy-hq/sdk/testing`
48
+ * enforces them on an app's unit tests.
49
+ *
50
+ * ONE SOURCE OF TRUTH, AND IT IS NOT THIS FILE. Every string below is quoted
51
+ * from the host's source — `host.rs`, `host/destinations.rs`,
52
+ * `host/airhouse_ops.rs`, `upsert_support.rs`, `tx.rs`, `runtime.rs` under
53
+ * `crates/app/src/server/api/custom_apps_functions/`, and the connector's
54
+ * `transaction.rs` / `postgres_tx/convert.rs` — never paraphrased.
55
+ * `{placeholder}`s stand for what the host formats in (`{database}`,
56
+ * `{dialect}`, …) and use the host's own names.
57
+ *
58
+ * HELD TO THE HOST BY A RUST TEST. `sdk_testing_drift.rs`, beside `host.rs`,
59
+ * `include_str!`s this file and fails when a `refusal:` here is no longer a
60
+ * sentence its `source:` file contains, when `HOST_OPS` differs from the
61
+ * host's closed list, when `GATES` stops naming exactly the fields of
62
+ * `FunctionCapabilities`, when `ABSENT_GLOBALS` differs from `oxyc`'s lint
63
+ * list, or when `FETCH_RULES` differs from `is_safe_outbound`. One direction
64
+ * only: this file is checked against the host, the host is never checked
65
+ * against this file (`internal-docs/sdk-testing-context.md` §7, answer 9).
66
+ *
67
+ * THE RUST TEST READS TEXT, NOT TYPESCRIPT: after stripping comments it takes
68
+ * the block from `export const HOST_OPS` to `] as const;`, every object whose
69
+ * key line ends `: {` and carries `source:` and `refusal:` lines, the
70
+ * `hostField:` / `ops:` lines of `GATES`, the `name:` / `member:` lines of
71
+ * `ABSENT_GLOBALS`, and every quoted string in `FETCH_RULES`. Keep those keys
72
+ * spelled as they are, each `refusal:` a single string literal, and none of
73
+ * them behind a comment that is not a full-line one.
74
+ *
75
+ * NO `contractVersion` (§7, answer 10): a reworded refusal reaches an app on
76
+ * its next SDK bump, which is the right moment to see it; a version field
77
+ * with no consumer is a mechanism nobody reads.
78
+ */
79
+ /**
80
+ * Every name the host pages an op under — `HOST_OPS` in `host_call_attrs.rs`,
81
+ * "pinned by unit tests to `host_op_name` and `runtime::host_call_span`". The
82
+ * `HostOp` type is this list, so `"warehouse.inserts"` does not compile.
83
+ */
84
+ declare const HOST_OPS: readonly ["query", "query_stream", "fetch", "semantic.query", "airway.run", "warehouse.insert", "warehouse.exec", "warehouse.upsert", "warehouse.query", "tx.begin", "tx.begin_oltp", "tx.query", "tx.exec", "tx.commit", "tx.rollback", "oltp.query", "oltp.exec", "airhouse.query", "airhouse.exec", "airhouse.append", "storage.getUploadUrl", "storage.getDownloadUrl", "storage.put", "storage.get", "storage.head", "storage.list", "storage.delete", "storage.copy", "secrets.set", "email.send", "org.people", "org.places", "org.assignments"];
85
+ /** One of the host's closed list of op names. */
86
+ type HostOp = (typeof HOST_OPS)[number];
87
+ /**
88
+ * The surface `reply_json` prefixes a refusal with (`runtime.rs`): a thrown
89
+ * `HostError`'s message is `"<surface>: <host message>"`. Every `tx.*` verb —
90
+ * `begin_oltp` included — answers under `ctx.tx`, because one op carries all
91
+ * six.
92
+ */
93
+ declare const SURFACES: {
94
+ readonly query: "ctx.query";
95
+ readonly queryStream: "ctx.queryStream";
96
+ readonly fetch: "ctx.fetch";
97
+ readonly warehouse: "ctx.warehouse";
98
+ readonly tx: "ctx.tx";
99
+ readonly oltp: "ctx.oltp";
100
+ readonly airhouse: "ctx.airhouse";
101
+ readonly secretsSet: "ctx.secrets.set";
102
+ readonly emailSend: "ctx.email.send";
103
+ readonly storage: "ctx.storage";
104
+ readonly orgPeople: "ctx.org.people";
105
+ readonly orgPlaces: "ctx.org.places";
106
+ readonly orgAssignments: "ctx.org.assignments";
107
+ readonly semanticQuery: "ctx.semantic.query";
108
+ readonly airwayRun: "ctx.airway.run";
109
+ };
110
+ /** One refusal the host writes: the file it is quoted from and its template. */
111
+ interface RefusalTemplate {
112
+ readonly source: string;
113
+ readonly refusal: string;
114
+ }
115
+ /**
116
+ * Every refusal the context throws, quoted from the host. The context renders
117
+ * a template by substituting its `{placeholder}`s and prefixing the surface.
118
+ */
119
+ declare const REFUSALS: {
120
+ readonly secretsWrite: {
121
+ readonly source: "host.rs";
122
+ readonly refusal: "ctx.secrets.set: this function has not declared the `secrets.write` capability (add it to oxy-app.json to permit writes)";
123
+ };
124
+ readonly emailSend: {
125
+ readonly source: "host.rs";
126
+ readonly refusal: 'EmailCapabilityMissing: this function has not declared the `email.send` capability (add "email": { "send": true } to its oxy-app.json entry)';
127
+ };
128
+ readonly orgPeople: {
129
+ readonly source: "host.rs";
130
+ readonly refusal: 'OrgCapabilityMissing: this function has not declared the `org.read` capability (add "org": { "read": true } to its oxy-app.json entry)';
131
+ };
132
+ readonly orgMember: {
133
+ readonly source: "host.rs";
134
+ readonly refusal: 'OrgCapabilityMissing: this function has not declared the `org.read` capability (add "org": { "read": true } to its oxy-app.json entry) — ctx.org.{member}';
135
+ };
136
+ readonly storageWrite: {
137
+ readonly source: "host.rs";
138
+ readonly refusal: 'StorageCapabilityMissing: this function has not declared the `storage.write` capability (add "storage": { "write": true } to its oxy-app.json entry)';
139
+ };
140
+ readonly storageRead: {
141
+ readonly source: "host.rs";
142
+ readonly refusal: 'StorageCapabilityMissing: this function has not declared the `storage.read` capability (add "storage": { "read": true } to its oxy-app.json entry)';
143
+ };
144
+ readonly oltp: {
145
+ readonly source: "host.rs";
146
+ readonly refusal: 'OltpCapabilityMissing: this function has not declared the `oltp` capability (add "oltp": { "enabled": true } to its oxy-app.json entry). The schema it writes is derived from the app\'s own slug — the manifest only enables access.';
147
+ };
148
+ readonly airhouse: {
149
+ readonly source: "host/airhouse_ops.rs";
150
+ readonly refusal: 'AirhouseCapabilityMissing: this function has not declared the `airhouse` capability (add "airhouse": { "enabled": true } to its oxy-app.json entry). The schema it writes is derived from the app\'s own slug — the manifest only enables access.';
151
+ };
152
+ readonly destinationNotDeclared: {
153
+ readonly source: "host.rs";
154
+ readonly refusal: "database '{database}' is not in this function's `destinations` allowlist (declare it in oxy-app.json to permit writes)";
155
+ };
156
+ readonly databaseNotConfigured: {
157
+ readonly source: "host.rs";
158
+ readonly refusal: "database '{database}' is not configured for this project";
159
+ };
160
+ readonly managedOltpWrite: {
161
+ readonly source: "host/destinations.rs";
162
+ readonly refusal: "database '{database}' is the org's OLTP store, which this surface reaches as the read-only analyst — write this app's own records with ctx.oltp";
163
+ };
164
+ readonly customerWarehouseWrite: {
165
+ readonly source: "host/destinations.rs";
166
+ readonly refusal: 'database \'{database}\' is a customer warehouse, and customer warehouses are read-only to apps. Facts this app records belong in Airhouse (ctx.airhouse); records it edits belong in ctx.oltp. If this write has to stay, say why on the function in oxy-app.json: "customerWarehouseWrites": { "{database}": "<reason>" }';
167
+ };
168
+ readonly customerWarehouseTransaction: {
169
+ readonly source: "host/destinations.rs";
170
+ readonly refusal: ". A ctx.tx transaction counts as a write even when it only reads — read with ctx.warehouse.query instead";
171
+ };
172
+ readonly upsertUnsupported: {
173
+ readonly source: "upsert_support.rs";
174
+ readonly refusal: "warehouse.upsert is not supported on {dialect}: it compiles to `INSERT … ON CONFLICT … DO UPDATE`, which only Postgres and DuckDB parse. Use warehouse.insert, or warehouse.exec with this warehouse's own upsert statement.";
175
+ };
176
+ readonly transactionOpen: {
177
+ readonly source: "host.rs";
178
+ readonly refusal: "could not open a transaction on '{database}': {e}";
179
+ };
180
+ readonly transactionUnsupported: {
181
+ readonly source: "transaction.rs";
182
+ readonly refusal: "{backend} does not support multi-statement transactions — ctx.tx() requires a Postgres-backed database (`type: postgres`). Use ctx.warehouse.{insert,exec,upsert} for single-statement writes.";
183
+ };
184
+ readonly transactionsOpen: {
185
+ readonly source: "tx.rs";
186
+ readonly refusal: "this invocation already has {MAX_OPEN} transactions open. Commit or roll one back before opening another.";
187
+ };
188
+ readonly transactionFinished: {
189
+ readonly source: "runtime.rs";
190
+ readonly refusal: "{label}: this transaction is already finished — {what} was called after the callback returned";
191
+ };
192
+ readonly transactionFnNotFunction: {
193
+ readonly source: "runtime.rs";
194
+ readonly refusal: "{signature}: fn must be a function";
195
+ };
196
+ readonly btoaBytes: {
197
+ readonly source: "runtime.rs";
198
+ readonly refusal: "btoa: expected a string. For bytes use bytesToBase64() from @oxy-hq/sdk";
199
+ };
200
+ readonly atobPadding: {
201
+ readonly source: "runtime.rs";
202
+ readonly refusal: "atob: '=' may only appear as trailing padding";
203
+ };
204
+ readonly atobLength: {
205
+ readonly source: "runtime.rs";
206
+ readonly refusal: "atob: invalid base64 length";
207
+ };
208
+ readonly fetchInvalidUrl: {
209
+ readonly source: "host.rs";
210
+ readonly refusal: "invalid url: {e}";
211
+ };
212
+ readonly fetchBlocked: {
213
+ readonly source: "host.rs";
214
+ readonly refusal: "fetch to '{url}' blocked by SSRF allowlist";
215
+ };
216
+ readonly fetchInvalidHeaderName: {
217
+ readonly source: "host.rs";
218
+ readonly refusal: "InvalidFetchHeader: `{k}` is not a header name";
219
+ };
220
+ readonly fetchInvalidHeaderValue: {
221
+ readonly source: "host.rs";
222
+ readonly refusal: "InvalidFetchHeader: the value of `{k}` is not a header value";
223
+ };
224
+ readonly fetchTooLarge: {
225
+ readonly source: "host.rs";
226
+ readonly refusal: "response too large ({len} bytes > {max_bytes} cap)";
227
+ };
228
+ readonly slugCannotBackASchema: {
229
+ readonly source: "host.rs";
230
+ readonly refusal: string;
231
+ };
232
+ readonly oltpParamsNotArray: {
233
+ readonly source: "host.rs";
234
+ readonly refusal: "`params` must be an array of values. Pass positional arguments for $1, $2, … — e.g. [name, partySize].";
235
+ };
236
+ readonly noDefaultDatabase: {
237
+ readonly source: "host.rs";
238
+ readonly refusal: "this project has no databases configured";
239
+ };
240
+ readonly oltpUnsupportedColumn: {
241
+ readonly source: "postgres_tx/convert.rs";
242
+ readonly refusal: "result column `{name}` has Postgres type `{ty}`, which cannot be returned directly. Cast it to text in the SELECT list and parse it in your function — cast the whole expression, not the output name (`avg(qty)::text`, `amount::text`). Note `avg`/`sum` over any numeric type, and a bare decimal literal, are all `numeric`. (Supported as-is: bool, int2/4/8, float4/8, text/varchar/char/name, json/jsonb, uuid, timestamptz/timestamp, date, time.)";
243
+ };
244
+ };
245
+ /** One capability gate: the `FunctionCapabilities` field and the ops it refuses. */
246
+ interface Gate {
247
+ /** The `FunctionCapabilities` field in `host.rs`. */
248
+ readonly hostField: string;
249
+ /** The capability as the manifest and the refusal name it: `storage.write`. */
250
+ readonly manifest: string;
251
+ /** The host ops refused while the manifest lacks it. */
252
+ readonly ops: readonly HostOp[];
253
+ }
254
+ /**
255
+ * Every gate in `FunctionCapabilities`, in the struct's order. `storage_read`
256
+ * and `storage_write` list `check_storage_capability`'s arms (`copy` needs
257
+ * both); `customer_warehouse_writes` is decided per database by
258
+ * `destination_write_policy`, after the `destinations` allowlist.
259
+ * `storage_retention` and `fetch_max_bytes` are fields of the struct and
260
+ * deliberately absent: they shape an allowed call, they never refuse one.
261
+ */
262
+ declare const GATES: readonly [{
263
+ readonly hostField: "secrets_write";
264
+ readonly manifest: "secrets.write";
265
+ readonly ops: readonly ["secrets.set"];
266
+ }, {
267
+ readonly hostField: "email_send";
268
+ readonly manifest: "email.send";
269
+ readonly ops: readonly ["email.send"];
270
+ }, {
271
+ readonly hostField: "org_read";
272
+ readonly manifest: "org.read";
273
+ readonly ops: readonly ["org.people", "org.places", "org.assignments"];
274
+ }, {
275
+ readonly hostField: "storage_read";
276
+ readonly manifest: "storage.read";
277
+ readonly ops: readonly ["storage.getDownloadUrl", "storage.get", "storage.head", "storage.list", "storage.copy"];
278
+ }, {
279
+ readonly hostField: "storage_write";
280
+ readonly manifest: "storage.write";
281
+ readonly ops: readonly ["storage.getUploadUrl", "storage.put", "storage.delete", "storage.copy"];
282
+ }, {
283
+ readonly hostField: "oltp";
284
+ readonly manifest: "oltp.enabled";
285
+ readonly ops: readonly ["oltp.query", "oltp.exec", "tx.begin_oltp"];
286
+ }, {
287
+ readonly hostField: "airhouse";
288
+ readonly manifest: "airhouse.enabled";
289
+ readonly ops: readonly ["airhouse.query", "airhouse.exec", "airhouse.append"];
290
+ }, {
291
+ readonly hostField: "customer_warehouse_writes";
292
+ readonly manifest: "customerWarehouseWrites";
293
+ readonly ops: readonly ["warehouse.insert", "warehouse.exec", "warehouse.upsert", "tx.begin"];
294
+ }];
295
+ /**
296
+ * The globals the isolate does not define, as `oxyc`'s `isolate-global` lint
297
+ * names them (`ABSENT_GLOBALS` in `sdk/cli/src/publish/function-lint.ts`).
298
+ * `member` narrows an entry to `<name>.<member>`; an empty `member` is the
299
+ * whole of `process.*`. The isolate's bootstrap defines `Response`, `btoa`,
300
+ * `atob`, `console` and `__buildCtx` and nothing else (`runtime.rs`), so it has
301
+ * no `crypto` global at all; the lint singles out `subtle` because that is what
302
+ * an author reaches for, and `run()` removes the whole global as production
303
+ * does.
304
+ */
305
+ declare const ABSENT_GLOBALS: readonly [{
306
+ readonly name: "Buffer";
307
+ }, {
308
+ readonly name: "TextEncoder";
309
+ }, {
310
+ readonly name: "TextDecoder";
311
+ }, {
312
+ readonly name: "Blob";
313
+ }, {
314
+ readonly name: "File";
315
+ }, {
316
+ readonly name: "FormData";
317
+ }, {
318
+ readonly name: "crypto";
319
+ readonly member: "subtle";
320
+ }, {
321
+ readonly name: "process";
322
+ readonly member: "";
323
+ }];
324
+ /**
325
+ * `is_safe_outbound` in `host.rs`, the first-layer check every `ctx.fetch`
326
+ * URL meets before anything is sent: the scheme, the host names refused by
327
+ * name, and the suffixes an internal name ends with. The IP rules (loopback,
328
+ * unspecified, private, link-local, broadcast, documentation, v4-mapped and
329
+ * embedded v6) are code, in `fetch-rules.ts`. What no unit test can reproduce
330
+ * is the second layer — a public name that RESOLVES to a private address is
331
+ * caught at connect time by `PublicOnlyDnsResolver` — so the context does not
332
+ * pretend to.
333
+ */
334
+ declare const FETCH_RULES: {
335
+ readonly scheme: "https";
336
+ readonly literalHosts: readonly ["localhost", "ip6-localhost", "ip6-loopback"];
337
+ readonly internalSuffixes: readonly [".internal", ".local", ".localdomain", ".svc", ".cluster.local"];
338
+ };
339
+ /** `FETCH_MAX_BYTES` in `host.rs`: the default `ctx.fetch` response ceiling. */
340
+ declare const FETCH_MAX_BYTES: number;
341
+ /** `MAX_OPEN` in `tx.rs`: transactions one invocation may hold open at once. */
342
+ declare const MAX_OPEN_TRANSACTIONS = 4;
343
+ /**
344
+ * How each dialect names itself in a refusal — `SqlDialect::as_str` in the
345
+ * connector crate, which `upsert_support::check` and `transaction::unsupported`
346
+ * format in. Only Postgres, DuckDB and SQLite parse `ON CONFLICT`
347
+ * (`parses_on_conflict`), and only the Postgres connector opens a transaction.
348
+ */
349
+ declare const DIALECTS: {
350
+ readonly clickhouse: {
351
+ readonly name: "ClickHouse";
352
+ readonly parsesOnConflict: false;
353
+ readonly opensTransactions: false;
354
+ };
355
+ readonly postgres: {
356
+ readonly name: "PostgreSQL";
357
+ readonly parsesOnConflict: true;
358
+ readonly opensTransactions: true;
359
+ };
360
+ readonly duckdb: {
361
+ readonly name: "DuckDB";
362
+ readonly parsesOnConflict: true;
363
+ readonly opensTransactions: false;
364
+ };
365
+ };
366
+ type Dialect = keyof typeof DIALECTS;
367
+ //#endregion
368
+ //#region src/testing/zoo.d.ts
369
+ type ZooEngine = "clickhouse" | "postgres" | "duckdb";
370
+ /** Which plane read the value: `ctx.warehouse` / `ctx.query`, or `ctx.oltp` / `ctx.tx`. */
371
+ type ZooPlane = "warehouse" | "oltp";
372
+ interface ZooCase {
373
+ key: string;
374
+ native_type: string;
375
+ class: string;
376
+ value_sql: string;
377
+ expect: {
378
+ warehouse: unknown;
379
+ oltp?: unknown;
380
+ };
381
+ }
382
+ interface ShapeZoo {
383
+ version: number;
384
+ engines: Record<ZooEngine, {
385
+ cases: ZooCase[];
386
+ }>;
387
+ }
388
+ /** Where a row value came from — the honesty flag on every read. */
389
+ type RowSource = "zoo" | "typed" | "author";
390
+ /** A value a read would REFUSE rather than return: `expect.oltp.$error`. */
391
+ declare class ZooRefusal {
392
+ readonly column: string;
393
+ readonly nativeType: string;
394
+ readonly detail: string;
395
+ constructor(column: string, nativeType: string, detail: string);
396
+ /** The host's full sentence for it, from the contract. */
397
+ message(): string;
398
+ }
399
+ declare const ZOO: ShapeZoo;
400
+ /** The zoo table's name, from the fixture's digest — the canary's `zooTableName`. */
401
+ declare function zooTableName(sha256Hex?: string): string;
402
+ /** `c001`, `c002`, … — the zoo column for case `index`. */
403
+ declare function zooColumn(index: number): string;
404
+ /** What a read of case `c` on `plane` returns — or refuses. */
405
+ declare function expectation(c: ZooCase, plane: ZooPlane, column: string): unknown | ZooRefusal;
406
+ /** Every case of `engine` for `nativeType`, as written in the zoo. */
407
+ declare function casesFor(engine: ZooEngine, nativeType: string): ZooCase[];
408
+ /** The native types the zoo pins for `engine`, for a declaration error to list. */
409
+ declare function knownTypes(engine: ZooEngine): string[];
410
+ /**
411
+ * Render one SQL-side value of a typed column through the zoo.
412
+ *
413
+ * - The value IS a case's `value_sql` for this type → that case's expectation
414
+ * (`source: "zoo"`).
415
+ * - `NULL` → `null`.
416
+ * - Otherwise the `plain` case's SHAPE decides: a number-shaped type parses the
417
+ * literal, except that an integer outside `i64` becomes its exact digits as a
418
+ * string where the zoo pins that (`precision_gt_18` expecting a string) — the
419
+ * host reads an overflowing column as `u64` rendered to text; a string-shaped
420
+ * type unquotes; a boolean-shaped type reads `true`/`false`/`1`/`0`; a JSON
421
+ * shape parses (`source: "typed"`).
422
+ */
423
+ declare function renderValue(engine: ZooEngine, nativeType: string, valueSql: string, plane: ZooPlane, column: string): {
424
+ value: unknown | ZooRefusal;
425
+ source: RowSource;
426
+ };
427
+ //#endregion
428
+ //#region src/testing/stores.d.ts
429
+ /** A read's rows, and the weakest source among them. */
430
+ interface ReadResult {
431
+ rows: OxyFunctionRow[];
432
+ source: RowSource | null;
433
+ }
434
+ /**
435
+ * One SQL-evaluating store: a warehouse database, the OLTP schema or the
436
+ * Airhouse schema. `engine` picks which zoo cases render typed columns;
437
+ * `plane` picks which expectation a case yields.
438
+ */
439
+ declare class TableStore {
440
+ readonly engine: ZooEngine;
441
+ readonly plane: ZooPlane;
442
+ /** For a schema-qualified name to resolve to a bare one: the schema, or `null`. */
443
+ readonly schema: string | null;
444
+ private readonly tables;
445
+ constructor(engine: ZooEngine, plane: ZooPlane,
446
+ /** For a schema-qualified name to resolve to a bare one: the schema, or `null`. */
447
+ schema?: string | null);
448
+ /** The zoo table for this engine: column `cNNN` is case NNN's expectation. */
449
+ zoo(): this;
450
+ /**
451
+ * Declare a typed table: `{ id: "UInt64", amount: "Decimal(18, 4)" }`. A
452
+ * type the zoo has no case for is an error here, not a guess at read time.
453
+ */
454
+ table(name: string, columns: Record<string, string>): TypedTable;
455
+ /** Seed rows as JS values, unrendered — every read of them is `source: "author"`. */
456
+ raw(name: string, rows: OxyFunctionRow[]): this;
457
+ /** Every row of `name` as a function would read it; a `$error` cell is its refusal text. */
458
+ rows(name: string): OxyFunctionRow[];
459
+ /** The names of every table declared, seeded or created. */
460
+ tableNames(): string[];
461
+ /** Whether `name` exists. */
462
+ has(name: string): boolean;
463
+ /** Evaluate `sql` as a read: rows, or a throw. */
464
+ query(sql: string, params?: readonly unknown[]): ReadResult;
465
+ /** Evaluate `sql` as a statement: rows affected, or a throw. */
466
+ exec(sql: string, params?: readonly unknown[]): number;
467
+ /** Append JS rows to `name` (`ctx.warehouse.insert`, `ctx.airhouse.append`): unrendered. */
468
+ append(name: string, rows: OxyFunctionRow[]): number;
469
+ /** Snapshot every table, for `ctx.tx` rollback. */
470
+ snapshot(): () => void;
471
+ private key;
472
+ private lookup;
473
+ private require;
474
+ private create;
475
+ private insert;
476
+ private matching;
477
+ private select;
478
+ private remove;
479
+ private update;
480
+ }
481
+ /** A declared typed table: insert SQL-side values, rendered through the zoo. */
482
+ declare class TypedTable {
483
+ private readonly store;
484
+ readonly name: string;
485
+ constructor(store: TableStore, name: string);
486
+ /** Insert rows of SQL-side values (`{ id: "42", at: "'2024-03-10 12:34:56'" }`). */
487
+ insert(rows: Record<string, string>[]): this;
488
+ }
489
+ /** One object in the app's storage silo, body as base64. */
490
+ interface StoredObject {
491
+ key: string;
492
+ body: string;
493
+ contentType: string | null;
494
+ size: number;
495
+ lastModified: string;
496
+ }
497
+ /** What `ctx.fetch` should answer for a URL: a result, or a function of the call. */
498
+ type FetchAnswer = {
499
+ status?: number;
500
+ body?: string;
501
+ encoding?: "utf8" | "base64";
502
+ } | ((url: string, init: RequestInit & {
503
+ encoding?: "utf8" | "base64";
504
+ }) => {
505
+ status?: number;
506
+ body?: string;
507
+ encoding?: "utf8" | "base64";
508
+ });
509
+ /** The `ctx.fetch` side: answers by URL, and what was received. */
510
+ declare class FetchStore {
511
+ readonly received: {
512
+ url: string;
513
+ init: RequestInit & {
514
+ encoding?: "utf8" | "base64";
515
+ };
516
+ }[];
517
+ private readonly answers;
518
+ /** Answer every URL equal to `match` (or matching the RegExp) with `answer`. */
519
+ on(match: string | RegExp, answer: FetchAnswer): this;
520
+ answerFor(url: string): FetchAnswer | undefined;
521
+ }
522
+ //#endregion
523
+ //#region src/testing/context.d.ts
524
+ /** One database the project configures, as the context needs to know it. */
525
+ interface TestDatabase {
526
+ dialect: Dialect;
527
+ /**
528
+ * `destination_kind`'s mapping (`host/destinations.rs`): `airhouse` /
529
+ * `airhouse_managed` may be written; `postgres_managed` is the org's OLTP
530
+ * store, refused; anything else is a customer warehouse, read-only without a
531
+ * `customerWarehouseWrites` reason. An unknown kind fails closed the same way.
532
+ */
533
+ kind: "airhouse" | "airhouse_managed" | "postgres_managed" | "customer";
534
+ }
535
+ interface TestContextOptions {
536
+ /** The `functions.<name>` entry whose capabilities gate the context. */
537
+ function: string;
538
+ /** The project's databases by name; `ctx.query` reaches `defaultDatabase` or the first. */
539
+ databases?: Record<string, TestDatabase>;
540
+ defaultDatabase?: string;
541
+ /** `ctx.env`. */
542
+ env?: Record<string, string>;
543
+ /** `ctx.user`, over a default org member. */
544
+ user?: Partial<OxyFunctionUser>;
545
+ }
546
+ /** One host op the function made, as the context recorded it. */
547
+ interface HostCall {
548
+ op: HostOp;
549
+ /** The op's payload as `__buildCtx` packs it: `{ database, table, rows }`, `{ sql, params }`, … */
550
+ args: Record<string, unknown>;
551
+ /** `refused`: a `HostError` the context threw; `threw`: any other error, an override's included. */
552
+ outcome: "ok" | "refused" | "threw";
553
+ /** The full error message — `"<surface>: <host message>"` for a refusal. */
554
+ message?: string;
555
+ /** What the host op replied, on `ok`. */
556
+ result?: unknown;
557
+ /** For a read answered from a store: the weakest source among its rows. */
558
+ source?: RowSource;
559
+ }
560
+ /** The host op behind a ctx member: answers with what the host would reply. */
561
+ type HostOpImpl = (args: Record<string, unknown>) => Promise<unknown> | unknown;
562
+ type Person = {
563
+ id: string;
564
+ name: string;
565
+ role: string | null;
566
+ kind: "member" | "frontline";
567
+ };
568
+ /** The stores, for a test to seed and inspect. */
569
+ interface TestState {
570
+ warehouse(database: string): TableStore;
571
+ readonly oltp: TableStore;
572
+ readonly airhouse: TableStore;
573
+ readonly storage: Map<string, StoredObject>;
574
+ readonly secrets: Map<string, string>;
575
+ readonly emails: Record<string, unknown>[];
576
+ readonly fetch: FetchStore;
577
+ readonly org: {
578
+ people: Person[];
579
+ places: OxyOrgPlace[];
580
+ assignments: OxyOrgAssignment[];
581
+ };
582
+ readonly semantic: {
583
+ on(answer: (spec: Record<string, unknown>) => unknown): void;
584
+ };
585
+ readonly airway: {
586
+ runs: {
587
+ pipelineRef: string;
588
+ variables: unknown;
589
+ }[];
590
+ };
591
+ }
592
+ interface TestContext {
593
+ ctx: OxyFunctionContext;
594
+ /** Every host op made, in order. */
595
+ readonly calls: readonly HostCall[];
596
+ /** The de-duplicated first-call order of op names. */
597
+ ops(): HostOp[];
598
+ callsTo(op: HostOp): HostCall[];
599
+ /** Replace one host op for this context; still recorded. */
600
+ override(op: HostOp, impl: HostOpImpl): void;
601
+ /** Evaluate `fn` with the isolate's absent globals absent (`run.ts`). */
602
+ run<T>(fn: () => Promise<T> | T): Promise<T>;
603
+ readonly state: TestState;
604
+ readonly gates: FunctionGates;
605
+ }
606
+ declare function createTestContext(manifest: ManifestLike, options: TestContextOptions): TestContext;
607
+ //#endregion
608
+ //#region src/testing/fetch-rules.d.ts
609
+ /** The host's verdict on a URL: allowed, or refused before sending. */
610
+ declare function isSafeOutbound(url: URL): boolean;
611
+ //#endregion
612
+ //#region src/testing/host-error.d.ts
613
+ /** The `name` the runtime gives every refusal it unwraps. */
614
+ declare const HOST_ERROR_NAME = "HostError";
615
+ /**
616
+ * Render a refusal template: every `{placeholder}` replaced by `vars[name]`.
617
+ * A placeholder the caller did not supply is a bug in the context, not in the
618
+ * app under test, so it throws rather than leaking `{database}` into a message
619
+ * a test might then match on.
620
+ */
621
+ declare function render(template: RefusalTemplate, vars?: Record<string, string | number>): string;
622
+ /**
623
+ * The error a function sees when the host refuses: `name` `"HostError"`,
624
+ * message `"<surface>: <message>"`.
625
+ */
626
+ declare function hostError(surface: string, message: string): Error;
627
+ /** `hostError` over a rendered template. */
628
+ declare function refuse(surface: string, template: RefusalTemplate, vars?: Record<string, string | number>): Error;
629
+ /** Whether `err` is a refusal the context (or the runtime) threw. */
630
+ declare function isHostError(err: unknown): err is Error;
631
+ /**
632
+ * An error in the context's OWN words — never the host's. Thrown where the
633
+ * context cannot model what the host would do (an unrecognised statement, an
634
+ * unregistered `fetch` answer) and says so, rather than answering something
635
+ * production would not. Its `name` is `"TestContextError"` so a test can never
636
+ * mistake it for a refusal.
637
+ */
638
+ declare function contextError(message: string): Error;
639
+ //#endregion
640
+ //#region src/testing/run.d.ts
641
+ /** `globalThis.btoa` as the isolate defines it: strings only, Latin1 only. */
642
+ declare function isolateBtoa(input: unknown): string;
643
+ /** `globalThis.atob` as the isolate defines it: WHATWG forgiving-base64. */
644
+ declare function isolateAtob(input: unknown): string;
645
+ /**
646
+ * Evaluate `fn` with every `ABSENT_GLOBALS` entry removed from `globalThis` and
647
+ * `btoa` / `atob` replaced by the isolate's, restoring all of them afterwards
648
+ * — whether `fn` resolves or throws.
649
+ */
650
+ declare function runWithoutIsolateGlobals<T>(fn: () => Promise<T> | T): Promise<T>;
651
+ //#endregion
652
+ //#region src/testing/shape-zoo-sha256.d.ts
653
+ declare const ZOO_SHA256 = "04f75f9054a46e521d8787d65f5d214440d041860971c77e66edc7bfbaf8d16a";
654
+ //#endregion
655
+ export { ABSENT_GLOBALS, DIALECTS, type Dialect, FETCH_MAX_BYTES, FETCH_RULES, type FetchAnswer, FetchStore, type FunctionGates, GATES, type Gate, HOST_ERROR_NAME, HOST_OPS, type HostCall, type HostOp, type HostOpImpl, MAX_OPEN_TRANSACTIONS, type ManifestLike, REFUSALS, type ReadResult, type RefusalTemplate, type RowSource, SURFACES, type ShapeZoo, type StoredObject, TableStore, type TestContext, type TestContextOptions, type TestDatabase, type TestState, TypedTable, ZOO, ZOO_SHA256, type ZooCase, type ZooEngine, type ZooPlane, ZooRefusal, casesFor, contextError, createTestContext, expectation, hostError, isHostError, isSafeOutbound, isolateAtob, isolateBtoa, knownTypes, readGates, refuse, render, renderValue, runWithoutIsolateGlobals, writerSchema, zooColumn, zooTableName };
656
+ //# sourceMappingURL=testing.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.d.cts","names":[],"sources":["../src/testing/gates.ts","../src/testing/host-contract.ts","../src/testing/zoo.ts","../src/testing/stores.ts","../src/testing/context.ts","../src/testing/fetch-rules.ts","../src/testing/host-error.ts","../src/testing/run.ts","../src/testing/shape-zoo-sha256.ts"],"mappings":"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"}