@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.
- package/README.md +67 -0
- package/dist/{function-context-D8eyZuw_.d.cts → function-context-BNpL5bFb.d.cts} +223 -20
- package/dist/function-context-BNpL5bFb.d.cts.map +1 -0
- package/dist/{function-context-D8eyZuw_.d.mts → function-context-BNpL5bFb.d.mts} +223 -20
- package/dist/function-context-BNpL5bFb.d.mts.map +1 -0
- package/dist/index.cjs +80 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +164 -3
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +164 -3
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +78 -16
- package/dist/index.mjs.map +1 -1
- package/dist/ops.d.cts +1 -1
- package/dist/ops.d.mts +1 -1
- package/dist/{react-DW7Z96sD.d.mts → react-CljeXJuw.d.cts} +142 -8
- package/dist/react-CljeXJuw.d.cts.map +1 -0
- package/dist/{react-DW7Z96sD.d.cts → react-CljeXJuw.d.mts} +142 -8
- package/dist/react-CljeXJuw.d.mts.map +1 -0
- package/dist/{react-DcT-mUPj.cjs → react-Dvkv2deI.cjs} +110 -59
- package/dist/react-Dvkv2deI.cjs.map +1 -0
- package/dist/{react-BXGyzgz0.mjs → react-OW1t_J0M.mjs} +103 -26
- package/dist/react-OW1t_J0M.mjs.map +1 -0
- package/dist/rolldown-runtime-KC0qvQup.cjs +34 -0
- package/dist/shell.cjs +38 -3
- package/dist/shell.cjs.map +1 -1
- package/dist/shell.d.cts +26 -3
- package/dist/shell.d.cts.map +1 -1
- package/dist/shell.d.mts +26 -3
- package/dist/shell.d.mts.map +1 -1
- package/dist/shell.mjs +34 -2
- package/dist/shell.mjs.map +1 -1
- package/dist/testing.cjs +4431 -0
- package/dist/testing.cjs.map +1 -0
- package/dist/testing.d.cts +656 -0
- package/dist/testing.d.cts.map +1 -0
- package/dist/testing.d.mts +656 -0
- package/dist/testing.d.mts.map +1 -0
- package/dist/testing.mjs +4395 -0
- package/dist/testing.mjs.map +1 -0
- package/package.json +22 -9
- package/dist/function-context-D8eyZuw_.d.cts.map +0 -1
- package/dist/function-context-D8eyZuw_.d.mts.map +0 -1
- package/dist/react-BXGyzgz0.mjs.map +0 -1
- package/dist/react-DW7Z96sD.d.cts.map +0 -1
- package/dist/react-DW7Z96sD.d.mts.map +0 -1
- 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.mjs";
|
|
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.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testing.d.mts","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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"}
|