@deepexi/datasense-connector 3.0.1 → 3.1.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 (185) hide show
  1. package/README.md +158 -139
  2. package/package.json +19 -28
  3. package/scripts/build.ts +188 -0
  4. package/src/define.ts +267 -0
  5. package/src/descriptor.ts +116 -0
  6. package/src/errors.ts +105 -0
  7. package/src/index.ts +108 -0
  8. package/src/internal/location.ts +23 -0
  9. package/src/runtime/guest/bridge.ts +238 -0
  10. package/src/runtime/guest/cache.ts +43 -0
  11. package/src/runtime/guest/check.ts +17 -0
  12. package/src/runtime/guest/crypto.ts +166 -0
  13. package/src/runtime/guest/ctx.ts +157 -0
  14. package/src/runtime/guest/db.ts +59 -0
  15. package/src/runtime/guest/failure.ts +69 -0
  16. package/src/runtime/guest/http.ts +194 -0
  17. package/src/runtime/guest/index.ts +102 -0
  18. package/{runtime/guest/limits.d.ts → src/runtime/guest/limits.ts} +12 -12
  19. package/src/runtime/guest/loader.ts +210 -0
  20. package/src/runtime/guest/log.ts +47 -0
  21. package/src/runtime/guest/map.ts +45 -0
  22. package/src/runtime/guest/operation.ts +193 -0
  23. package/src/runtime/guest/paginate.ts +169 -0
  24. package/src/runtime/guest/pull.ts +177 -0
  25. package/src/runtime/guest/push.ts +92 -0
  26. package/src/runtime/guest/signal.ts +14 -0
  27. package/src/runtime/guest/suggest.ts +51 -0
  28. package/src/runtime/guest/time.ts +137 -0
  29. package/src/runtime/guest-runtime.ts +75 -0
  30. package/src/runtime/index.ts +53 -0
  31. package/src/runtime/protocol.ts +557 -0
  32. package/src/runtime/session.ts +216 -0
  33. package/src/testing/child.ts +21 -0
  34. package/src/testing/compliance.ts +343 -0
  35. package/src/testing/engine.ts +445 -0
  36. package/src/testing/fixtures.ts +591 -0
  37. package/src/testing/gateway.ts +482 -0
  38. package/src/testing/import-guard.ts +221 -0
  39. package/src/testing/index.ts +43 -0
  40. package/src/testing/local-db.ts +226 -0
  41. package/src/testing/mock-external-system.ts +249 -0
  42. package/src/testing/pinned-http.ts +144 -0
  43. package/src/testing/recorder.ts +140 -0
  44. package/src/testing/redaction.ts +94 -0
  45. package/src/testing/replay.ts +437 -0
  46. package/src/testing/run-tests.ts +115 -0
  47. package/src/testing/schema.ts +70 -0
  48. package/src/testing/source-loader.ts +374 -0
  49. package/src/testing/types.ts +78 -0
  50. package/src/types.ts +329 -0
  51. package/src/validate.ts +267 -0
  52. package/test/build.test.ts +159 -0
  53. package/test/define.test-d.ts +182 -0
  54. package/test/fixtures/acceptance.ts +156 -0
  55. package/test/fixtures/connectors/http-pull/.ontos/descriptor.json +72 -0
  56. package/test/fixtures/connectors/http-pull/.ontos/types.ts +61 -0
  57. package/test/fixtures/connectors/http-pull/package.json +5 -0
  58. package/test/fixtures/connectors/http-pull/streams/items/index.ts +37 -0
  59. package/test/fixtures/connectors/http-pull/streams/items/map.ts +12 -0
  60. package/test/fixtures/connectors/http-pull/streams/items/tests/basic/case.json +15 -0
  61. package/test/fixtures/connectors/http-pull/streams/items/tests/basic/exchanges.ndjson +2 -0
  62. package/test/fixtures/connectors/pg-pull/.ontos/descriptor.json +151 -0
  63. package/test/fixtures/connectors/pg-pull/.ontos/types.ts +62 -0
  64. package/test/fixtures/connectors/pg-pull/lib/sql.ts +14 -0
  65. package/test/fixtures/connectors/pg-pull/package.json +5 -0
  66. package/test/fixtures/connectors/pg-pull/streams/customers/index.ts +30 -0
  67. package/test/fixtures/connectors/pg-pull/streams/customers/tests/customers-full/case.json +15 -0
  68. package/test/fixtures/connectors/pg-pull/streams/customers/tests/customers-full/exchanges.ndjson +3 -0
  69. package/test/fixtures/connectors/pg-pull/streams/orders/index.ts +32 -0
  70. package/test/fixtures/descriptor.ts +54 -0
  71. package/test/fixtures/kingdee-example.ts +82 -0
  72. package/test/fixtures/record-acceptance.ts +32 -0
  73. package/test/runtime/ctx.test.ts +434 -0
  74. package/test/runtime/helpers.ts +157 -0
  75. package/test/runtime/jobs.test.ts +444 -0
  76. package/test/runtime/operation.test.ts +175 -0
  77. package/test/runtime/session.test.ts +229 -0
  78. package/test/testing/acceptance-fixtures.test.ts +69 -0
  79. package/test/testing/compliance.test.ts +247 -0
  80. package/test/testing/engine.test.ts +283 -0
  81. package/test/testing/fixtures.test.ts +221 -0
  82. package/test/testing/gateway.test.ts +337 -0
  83. package/test/testing/import-guard.test.ts +45 -0
  84. package/test/testing/mock-external-system.test.ts +144 -0
  85. package/test/testing/operation-fixtures.test.ts +160 -0
  86. package/test/testing/recorder.test.ts +163 -0
  87. package/test/testing/redaction.test.ts +54 -0
  88. package/test/testing/replay.test.ts +207 -0
  89. package/test/testing/run-tests.test.ts +137 -0
  90. package/test/testing/source-loader.test.ts +220 -0
  91. package/test/validate-config.test.ts +104 -0
  92. package/tsconfig.build.json +23 -0
  93. package/tsconfig.json +19 -0
  94. package/cjs/index.cjs +0 -1015
  95. package/cjs/runtime.cjs +0 -752
  96. package/cjs/testing.cjs +0 -3591
  97. package/errors.d.ts +0 -17
  98. package/errors.js +0 -53
  99. package/guest.cjs +0 -2659
  100. package/index.d.ts +0 -13
  101. package/index.js +0 -7
  102. package/internal/location.d.ts +0 -6
  103. package/internal/location.js +0 -8
  104. package/presentation.d.ts +0 -38
  105. package/presentation.js +0 -268
  106. package/runtime/guest/bridge.d.ts +0 -53
  107. package/runtime/guest/bridge.js +0 -187
  108. package/runtime/guest/cache.d.ts +0 -3
  109. package/runtime/guest/cache.js +0 -42
  110. package/runtime/guest/crypto.d.ts +0 -13
  111. package/runtime/guest/crypto.js +0 -177
  112. package/runtime/guest/ctx.d.ts +0 -30
  113. package/runtime/guest/ctx.js +0 -124
  114. package/runtime/guest/db.d.ts +0 -6
  115. package/runtime/guest/db.js +0 -59
  116. package/runtime/guest/discover.d.ts +0 -29
  117. package/runtime/guest/discover.js +0 -51
  118. package/runtime/guest/failure.d.ts +0 -24
  119. package/runtime/guest/failure.js +0 -63
  120. package/runtime/guest/http.d.ts +0 -4
  121. package/runtime/guest/http.js +0 -204
  122. package/runtime/guest/index.d.ts +0 -12
  123. package/runtime/guest/index.js +0 -89
  124. package/runtime/guest/limits.js +0 -12
  125. package/runtime/guest/loader.d.ts +0 -15
  126. package/runtime/guest/loader.js +0 -42
  127. package/runtime/guest/log.d.ts +0 -10
  128. package/runtime/guest/log.js +0 -36
  129. package/runtime/guest/map.d.ts +0 -4
  130. package/runtime/guest/map.js +0 -45
  131. package/runtime/guest/paginate.d.ts +0 -6
  132. package/runtime/guest/paginate.js +0 -180
  133. package/runtime/guest/pull.d.ts +0 -12
  134. package/runtime/guest/pull.js +0 -150
  135. package/runtime/guest/push.d.ts +0 -18
  136. package/runtime/guest/push.js +0 -80
  137. package/runtime/guest/signal.d.ts +0 -3
  138. package/runtime/guest/signal.js +0 -12
  139. package/runtime/guest/time.d.ts +0 -5
  140. package/runtime/guest/time.js +0 -127
  141. package/runtime/guest-runtime.d.ts +0 -7
  142. package/runtime/guest-runtime.js +0 -51
  143. package/runtime/index.d.ts +0 -18
  144. package/runtime/index.js +0 -3
  145. package/runtime/protocol.d.ts +0 -276
  146. package/runtime/protocol.js +0 -409
  147. package/runtime/session.d.ts +0 -12
  148. package/runtime/session.js +0 -191
  149. package/t.d.ts +0 -18
  150. package/t.js +0 -26
  151. package/testing/child.cjs +0 -350
  152. package/testing/child.d.ts +0 -1
  153. package/testing/child.js +0 -12
  154. package/testing/compliance.d.ts +0 -41
  155. package/testing/compliance.js +0 -291
  156. package/testing/engine.d.ts +0 -66
  157. package/testing/engine.js +0 -339
  158. package/testing/fixtures.d.ts +0 -145
  159. package/testing/fixtures.js +0 -407
  160. package/testing/gateway.d.ts +0 -65
  161. package/testing/gateway.js +0 -402
  162. package/testing/import-guard.d.ts +0 -57
  163. package/testing/import-guard.js +0 -187
  164. package/testing/index.d.ts +0 -33
  165. package/testing/index.js +0 -10
  166. package/testing/local-db.d.ts +0 -27
  167. package/testing/local-db.js +0 -181
  168. package/testing/pinned-http.d.ts +0 -14
  169. package/testing/pinned-http.js +0 -125
  170. package/testing/recorder.d.ts +0 -15
  171. package/testing/recorder.js +0 -116
  172. package/testing/redaction.d.ts +0 -28
  173. package/testing/redaction.js +0 -67
  174. package/testing/replay.d.ts +0 -50
  175. package/testing/replay.js +0 -313
  176. package/testing/schema.d.ts +0 -13
  177. package/testing/schema.js +0 -65
  178. package/testing/source-loader.d.ts +0 -19
  179. package/testing/source-loader.js +0 -238
  180. package/testing/types.d.ts +0 -79
  181. package/testing/types.js +0 -18
  182. package/types.d.ts +0 -302
  183. package/types.js +0 -13
  184. package/validate.d.ts +0 -78
  185. package/validate.js +0 -597
package/cjs/runtime.cjs DELETED
@@ -1,752 +0,0 @@
1
- var __create = Object.create;
2
- var __getProtoOf = Object.getPrototypeOf;
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
- var __hasOwnProp = Object.prototype.hasOwnProperty;
7
- function __accessProp(key) {
8
- return this[key];
9
- }
10
- var __toESMCache_node;
11
- var __toESMCache_esm;
12
- var __toESM = (mod, isNodeMode, target) => {
13
- var canCache = mod != null && typeof mod === "object";
14
- if (canCache) {
15
- var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
16
- var cached = cache.get(mod);
17
- if (cached)
18
- return cached;
19
- }
20
- target = mod != null ? __create(__getProtoOf(mod)) : {};
21
- const to = isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
22
- if (mod && typeof mod === "object" || typeof mod === "function") {
23
- for (let key of __getOwnPropNames(mod))
24
- if (!__hasOwnProp.call(to, key))
25
- __defProp(to, key, {
26
- get: __accessProp.bind(mod, key),
27
- enumerable: true
28
- });
29
- }
30
- if (canCache)
31
- cache.set(mod, to);
32
- return to;
33
- };
34
- var __toCommonJS = (from) => {
35
- var entry = (__moduleCache ??= new WeakMap).get(from), desc;
36
- if (entry)
37
- return entry;
38
- entry = __defProp({}, "__esModule", { value: true });
39
- if (from && typeof from === "object" || typeof from === "function") {
40
- for (var key of __getOwnPropNames(from))
41
- if (!__hasOwnProp.call(entry, key))
42
- __defProp(entry, key, {
43
- get: __accessProp.bind(from, key),
44
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
45
- });
46
- }
47
- __moduleCache.set(from, entry);
48
- return entry;
49
- };
50
- var __moduleCache;
51
- var __returnValue = (v) => v;
52
- function __exportSetter(name, newValue) {
53
- this[name] = __returnValue.bind(null, newValue);
54
- }
55
- var __export = (target, all) => {
56
- for (var name in all)
57
- __defProp(target, name, {
58
- get: all[name],
59
- enumerable: true,
60
- configurable: true,
61
- set: __exportSetter.bind(all, name)
62
- });
63
- };
64
-
65
- // src/runtime/index.ts
66
- var exports_runtime = {};
67
- __export(exports_runtime, {
68
- GUEST_ENTRY_PATH: () => GUEST_ENTRY_PATH,
69
- GUEST_FAILURE_CODES: () => GUEST_FAILURE_CODES,
70
- HOST_METHODS: () => HOST_METHODS,
71
- createGuestSession: () => createGuestSession,
72
- loadGuestRuntime: () => loadGuestRuntime,
73
- parseChildMessage: () => parseChildMessage,
74
- parseParentMessage: () => parseParentMessage,
75
- undeclaredEmitTable: () => undeclaredEmitTable
76
- });
77
- module.exports = __toCommonJS(exports_runtime);
78
-
79
- // src/runtime/guest-runtime.ts
80
- var import_promises = require("node:fs/promises");
81
- var import_node_path2 = __toESM(require("node:path"), 1);
82
-
83
- // src/internal/location.ts
84
- var import_node_path = __toESM(require("node:path"), 1);
85
- var IS_SOURCE = false;
86
- var PACKAGE_ROOT = import_node_path.default.resolve(__dirname, "..");
87
- function moduleFile(relative) {
88
- return import_node_path.default.join(PACKAGE_ROOT, relative + ".js");
89
- }
90
-
91
- // src/runtime/guest-runtime.ts
92
- var GUEST_ENTRY_PATH = moduleFile("runtime/guest/index");
93
- var GUEST_BUNDLE_FILE = "guest.cjs";
94
- var CODE_FILE = /\.[cm]?[jt]sx?$/;
95
- function readSourcePlugin(bun) {
96
- return {
97
- name: "datasense-connector-read-source",
98
- setup(build) {
99
- build.onLoad({ filter: CODE_FILE }, async (args) => {
100
- const extension = args.path.slice(args.path.lastIndexOf(".") + 1);
101
- const loader = extension === "tsx" ? "tsx" : extension === "jsx" ? "jsx" : /^[cm]?js$/.test(extension) ? "js" : "ts";
102
- return { contents: await bun.file(args.path).text(), loader };
103
- });
104
- }
105
- };
106
- }
107
- var MACROS_OFF = { macros: false };
108
- async function bundleGuestWithBun(entry = GUEST_ENTRY_PATH) {
109
- const bun = globalThis.Bun;
110
- if (!bun)
111
- throw new Error("the guest runtime can only be bundled from source under Bun; use the built package (dist) under Node.js");
112
- const result = await bun.build({
113
- entrypoints: [entry],
114
- format: "cjs",
115
- target: "browser",
116
- minify: false,
117
- external: [],
118
- plugins: [readSourcePlugin(bun)],
119
- throw: false,
120
- ...MACROS_OFF
121
- });
122
- const output = result.outputs.find((item) => item.kind === "entry-point");
123
- if (!result.success || !output) {
124
- throw new Error(`guest runtime bundle failed: ${result.logs.map((log) => log.message).join("; ") || "no output"}`);
125
- }
126
- return output.text();
127
- }
128
- var cached = null;
129
- function loadGuestRuntime() {
130
- if (cached)
131
- return cached;
132
- const pending = IS_SOURCE ? bundleGuestWithBun() : import_promises.readFile(import_node_path2.default.join(PACKAGE_ROOT, GUEST_BUNDLE_FILE), "utf8");
133
- cached = pending;
134
- pending.catch(() => {
135
- if (cached === pending)
136
- cached = null;
137
- });
138
- return pending;
139
- }
140
- // src/runtime/session.ts
141
- var import_node_vm = __toESM(require("node:vm"), 1);
142
-
143
- // src/runtime/protocol.ts
144
- var HOST_METHODS = Object.freeze([
145
- "definition",
146
- "declaration",
147
- "http",
148
- "db.query",
149
- "db.next",
150
- "db.close",
151
- "crypto",
152
- "cache.get",
153
- "cache.set",
154
- "secret.reveal",
155
- "emit"
156
- ]);
157
- var GUEST_FAILURE_CODES = Object.freeze([
158
- "CONNECTOR_LOAD_FAILED",
159
- "CONFIG_INVALID",
160
- "SCHEMA_ERROR",
161
- "FATAL_ERROR",
162
- "AUTH_ERROR",
163
- "TRANSIENT_ERROR",
164
- "PUSH_UNKNOWN",
165
- "CANCELED"
166
- ]);
167
- var HTTP_METHODS = new Set(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"]);
168
- var BODY_ENCODINGS = new Set(["utf8", "base64"]);
169
- var DIGEST_ENCODINGS = new Set(["hex", "base64"]);
170
- var HMAC_ALGORITHMS = new Set(["sha1", "sha256", "sha512"]);
171
- var HASH_ALGORITHMS = new Set(["md5", "sha1", "sha256", "sha512"]);
172
- var LOG_LEVELS = new Set(["info", "warn", "error"]);
173
- var SYNC_MODES = new Set(["full", "incremental"]);
174
- var SNAPSHOTS = new Set(["complete", "partial"]);
175
- var HOST_METHOD_SET = new Set(HOST_METHODS);
176
- var FAILURE_CODE_SET = new Set(GUEST_FAILURE_CODES);
177
- function isRecord(value) {
178
- return typeof value === "object" && value !== null && !Array.isArray(value);
179
- }
180
- function isStringArray(value) {
181
- return Array.isArray(value) && value.every((item) => typeof item === "string");
182
- }
183
- function isStringRecord(value) {
184
- return isRecord(value) && Object.values(value).every((item) => typeof item === "string");
185
- }
186
- function isId(value) {
187
- return typeof value === "number" && Number.isSafeInteger(value) && value > 0;
188
- }
189
- function parsePushRequest(value) {
190
- if (!isRecord(value))
191
- return null;
192
- const { requestId, push, receivedAt, headers, body } = value;
193
- if (typeof requestId !== "string" || typeof push !== "string" || typeof receivedAt !== "string" || !isStringRecord(headers) || typeof body !== "string")
194
- return null;
195
- return { requestId, push, receivedAt, headers: { ...headers }, body };
196
- }
197
- function parseJob(value) {
198
- if (!isRecord(value) || !isRecord(value.config) || !isStringArray(value.secretFields))
199
- return null;
200
- const config = value.config;
201
- const secretFields = [...value.secretFields];
202
- if (value.kind === "push") {
203
- if (!Array.isArray(value.requests))
204
- return null;
205
- const requests = [];
206
- for (const item of value.requests) {
207
- const request = parsePushRequest(item);
208
- if (!request)
209
- return null;
210
- requests.push(request);
211
- }
212
- return { kind: "push", config, secretFields, requests };
213
- }
214
- if (value.kind === "discover")
215
- return { kind: "discover", config, secretFields };
216
- if (value.kind === "check")
217
- return { kind: "check", config, secretFields };
218
- if (value.kind === "pull") {
219
- const { stream, mode, streamConfig, tables, discoverFirst } = value;
220
- if (typeof stream !== "string" || typeof mode !== "string" || !SYNC_MODES.has(mode) || !isRecord(streamConfig) || !isStringArray(tables))
221
- return null;
222
- if (discoverFirst !== undefined && typeof discoverFirst !== "boolean")
223
- return null;
224
- return {
225
- kind: "pull",
226
- config,
227
- secretFields,
228
- stream,
229
- mode,
230
- state: value.state,
231
- streamConfig,
232
- tables: [...tables],
233
- ...discoverFirst === true ? { discoverFirst: true } : {}
234
- };
235
- }
236
- return null;
237
- }
238
- function parseHostError(value) {
239
- if (!isRecord(value) || typeof value.code !== "string" || typeof value.message !== "string")
240
- return null;
241
- const error = { code: value.code, message: value.message };
242
- if (value.status !== undefined) {
243
- if (typeof value.status !== "number" || !Number.isInteger(value.status))
244
- return null;
245
- error.status = value.status;
246
- }
247
- if (value.retryAfterMs !== undefined) {
248
- if (typeof value.retryAfterMs !== "number" || !Number.isFinite(value.retryAfterMs) || value.retryAfterMs < 0)
249
- return null;
250
- error.retryAfterMs = value.retryAfterMs;
251
- }
252
- return error;
253
- }
254
- function parseParentMessage(value) {
255
- if (!isRecord(value))
256
- return null;
257
- switch (value.type) {
258
- case "execute": {
259
- const { guest, code, expect } = value;
260
- if (typeof guest !== "string" || typeof code !== "string")
261
- return null;
262
- if (!isRecord(expect) || typeof expect.name !== "string" || typeof expect.version !== "string")
263
- return null;
264
- const job = parseJob(value.job);
265
- return job ? { type: "execute", guest, code, expect: { name: expect.name, version: expect.version }, job } : null;
266
- }
267
- case "reply": {
268
- if (!isId(value.id))
269
- return null;
270
- if (value.ok === true)
271
- return { type: "reply", id: value.id, ok: true, value: value.value };
272
- if (value.ok !== false)
273
- return null;
274
- const error = parseHostError(value.error);
275
- return error ? { type: "reply", id: value.id, ok: false, error } : null;
276
- }
277
- case "cancel":
278
- return { type: "cancel" };
279
- default:
280
- return null;
281
- }
282
- }
283
- function parseLogs(value) {
284
- if (!Array.isArray(value))
285
- return null;
286
- const logs = [];
287
- for (const entry of value) {
288
- if (!isRecord(entry) || typeof entry.at !== "string" || typeof entry.level !== "string" || !LOG_LEVELS.has(entry.level) || typeof entry.message !== "string")
289
- return null;
290
- if (entry.requestId !== undefined && typeof entry.requestId !== "string")
291
- return null;
292
- logs.push({
293
- at: entry.at,
294
- level: entry.level,
295
- message: entry.message,
296
- ...entry.requestId !== undefined ? { requestId: entry.requestId } : {}
297
- });
298
- }
299
- return logs;
300
- }
301
- function parseSummary(value) {
302
- if (!isRecord(value))
303
- return null;
304
- const { name, version, hosts, db, rateLimit, pushes, streams, secretFields, exposeToScript, config } = value;
305
- if (typeof name !== "string" || typeof version !== "string")
306
- return null;
307
- if (!isStringArray(hosts) || !isStringArray(pushes) || !isStringArray(streams) || !isStringArray(secretFields) || !isStringArray(exposeToScript))
308
- return null;
309
- if (db !== null && !(isRecord(db) && db.engine === "postgresql"))
310
- return null;
311
- let parsedRateLimit = null;
312
- if (rateLimit !== null) {
313
- if (!isRecord(rateLimit) || typeof rateLimit.perSecond !== "number" || typeof rateLimit.concurrency !== "number")
314
- return null;
315
- parsedRateLimit = { perSecond: rateLimit.perSecond, concurrency: rateLimit.concurrency };
316
- }
317
- return {
318
- name,
319
- version,
320
- hosts: [...hosts],
321
- db: db === null ? null : { engine: "postgresql" },
322
- rateLimit: parsedRateLimit,
323
- pushes: [...pushes],
324
- streams: [...streams],
325
- secretFields: [...secretFields],
326
- exposeToScript: [...exposeToScript],
327
- ...isRecord(config) ? { config } : {}
328
- };
329
- }
330
- function parseTables(value) {
331
- if (!isRecord(value))
332
- return null;
333
- const tables = {};
334
- for (const [name, table] of Object.entries(value)) {
335
- if (!isRecord(table) || !isRecord(table.columns) || !isStringArray(table.primaryKey))
336
- return null;
337
- tables[name] = table;
338
- }
339
- return tables;
340
- }
341
- function parseDiscover(value) {
342
- if (!isRecord(value) || !isRecord(value.streams) || !isRecord(value.pushes))
343
- return null;
344
- const result = { streams: {}, pushes: {} };
345
- for (const [name, stream] of Object.entries(value.streams)) {
346
- if (!isRecord(stream) || !isStringArray(stream.syncModes) || !stream.syncModes.every((mode) => SYNC_MODES.has(mode)))
347
- return null;
348
- if (typeof stream.snapshot !== "string" || !SNAPSHOTS.has(stream.snapshot))
349
- return null;
350
- if (stream.params !== undefined && !(isRecord(stream.params) && stream.params.kind === "object"))
351
- return null;
352
- const tables = parseTables(stream.tables);
353
- if (!tables)
354
- return null;
355
- result.streams[name] = {
356
- tables,
357
- syncModes: [...stream.syncModes],
358
- snapshot: stream.snapshot,
359
- ...stream.params !== undefined ? { params: stream.params } : {}
360
- };
361
- }
362
- for (const [name, push] of Object.entries(value.pushes)) {
363
- if (!isRecord(push))
364
- return null;
365
- const tables = parseTables(push.tables);
366
- if (!tables)
367
- return null;
368
- result.pushes[name] = { tables };
369
- }
370
- return result;
371
- }
372
- function parsePushResult(value, requestIds) {
373
- const { records, removals, rejected } = value;
374
- if (!Array.isArray(records) || !Array.isArray(removals) || !Array.isArray(rejected))
375
- return null;
376
- const logs = parseLogs(value.logs);
377
- if (!logs)
378
- return null;
379
- const result = { kind: "push", records: [], removals: [], rejected: [], logs };
380
- for (const item of records) {
381
- if (!isRecord(item) || typeof item.requestId !== "string" || !requestIds.has(item.requestId))
382
- return null;
383
- if (typeof item.table !== "string" || !isRecord(item.row))
384
- return null;
385
- result.records.push({ requestId: item.requestId, table: item.table, row: item.row });
386
- }
387
- for (const item of removals) {
388
- if (!isRecord(item) || typeof item.requestId !== "string" || !requestIds.has(item.requestId))
389
- return null;
390
- if (typeof item.table !== "string" || !isRecord(item.key))
391
- return null;
392
- result.removals.push({ requestId: item.requestId, table: item.table, key: item.key });
393
- }
394
- for (const item of rejected) {
395
- if (!isRecord(item) || typeof item.requestId !== "string" || !requestIds.has(item.requestId))
396
- return null;
397
- if (typeof item.code !== "string" || typeof item.message !== "string")
398
- return null;
399
- result.rejected.push({ requestId: item.requestId, code: item.code, message: item.message });
400
- }
401
- return result;
402
- }
403
- function parseResult(value, job) {
404
- if (!isRecord(value) || value.kind !== job.kind)
405
- return null;
406
- if (job.kind === "push")
407
- return parsePushResult(value, new Set(job.requests.map((request) => request.requestId)));
408
- const logs = parseLogs(value.logs);
409
- if (!logs)
410
- return null;
411
- if (job.kind === "discover") {
412
- const summary = parseSummary(value.summary);
413
- const discover = parseDiscover(value.discover);
414
- return summary && discover ? { kind: "discover", summary, discover, logs } : null;
415
- }
416
- if (job.kind === "check")
417
- return value.ok === true ? { kind: "check", ok: true, logs } : null;
418
- const emittedBatches = value.emittedBatches;
419
- if (typeof emittedBatches !== "number" || !Number.isSafeInteger(emittedBatches) || emittedBatches < 0)
420
- return null;
421
- if (value.stopped !== undefined && typeof value.stopped !== "boolean")
422
- return null;
423
- return { kind: "pull", emittedBatches, ...value.stopped === true ? { stopped: true } : {}, logs };
424
- }
425
- function parseDeclaration(value) {
426
- if (!isRecord(value))
427
- return null;
428
- const summary = parseSummary(value.summary);
429
- const discover = parseDiscover(value.discover);
430
- return summary && discover ? { summary, discover } : null;
431
- }
432
- function parseBatch(value, tables) {
433
- if (!isRecord(value) || !Array.isArray(value.records) || !Array.isArray(value.removals))
434
- return null;
435
- const logs = parseLogs(value.logs);
436
- if (!logs)
437
- return null;
438
- const batch = { records: [], removals: [], logs };
439
- for (const item of value.records) {
440
- if (!isRecord(item) || typeof item.table !== "string" || !tables.has(item.table) || !isRecord(item.row))
441
- return null;
442
- batch.records.push({ table: item.table, row: item.row });
443
- }
444
- for (const item of value.removals) {
445
- if (!isRecord(item) || typeof item.table !== "string" || !tables.has(item.table) || !isRecord(item.key))
446
- return null;
447
- batch.removals.push({ table: item.table, key: item.key });
448
- }
449
- if (value.checkpoint !== undefined) {
450
- if (!isRecord(value.checkpoint) || !Object.hasOwn(value.checkpoint, "state"))
451
- return null;
452
- batch.checkpoint = { state: value.checkpoint.state };
453
- }
454
- if (value.dropped !== undefined) {
455
- if (typeof value.dropped !== "number" || !Number.isSafeInteger(value.dropped) || value.dropped < 0)
456
- return null;
457
- batch.dropped = value.dropped;
458
- }
459
- return batch;
460
- }
461
- function parseHttpRequest(value) {
462
- if (!isRecord(value))
463
- return null;
464
- const { method, url, headers, query, body, bodyEncoding, timeoutMs } = value;
465
- if (typeof method !== "string" || !HTTP_METHODS.has(method) || typeof url !== "string" || !isStringRecord(headers))
466
- return null;
467
- if (!isRecord(query) || !Object.values(query).every((item) => typeof item === "string" || isStringArray(item)))
468
- return null;
469
- if (body !== null && typeof body !== "string")
470
- return null;
471
- if (typeof bodyEncoding !== "string" || !BODY_ENCODINGS.has(bodyEncoding))
472
- return null;
473
- if (timeoutMs !== undefined && (typeof timeoutMs !== "number" || !Number.isFinite(timeoutMs) || timeoutMs <= 0))
474
- return null;
475
- const parsedQuery = {};
476
- for (const [key, item] of Object.entries(query))
477
- parsedQuery[key] = Array.isArray(item) ? [...item] : item;
478
- return {
479
- method,
480
- url,
481
- headers: { ...headers },
482
- query: parsedQuery,
483
- body,
484
- bodyEncoding,
485
- ...timeoutMs !== undefined ? { timeoutMs } : {}
486
- };
487
- }
488
- function parseCryptoRequest(value) {
489
- if (!isRecord(value) || typeof value.data !== "string" || typeof value.algorithm !== "string")
490
- return null;
491
- if (typeof value.encoding !== "string" || !DIGEST_ENCODINGS.has(value.encoding))
492
- return null;
493
- const { data, algorithm } = value;
494
- const encoding = value.encoding;
495
- if (value.op === "hash") {
496
- return HASH_ALGORITHMS.has(algorithm) ? { op: "hash", algorithm, data, encoding } : null;
497
- }
498
- if (typeof value.key !== "string")
499
- return null;
500
- if (value.op === "hmac") {
501
- return HMAC_ALGORITHMS.has(algorithm) ? { op: "hmac", algorithm, key: value.key, data, encoding } : null;
502
- }
503
- if (value.op === "sign")
504
- return algorithm === "rsa-sha256" ? { op: "sign", algorithm: "rsa-sha256", key: value.key, data, encoding } : null;
505
- return null;
506
- }
507
- function parsePayload(method, payload, job, options) {
508
- switch (method) {
509
- case "definition":
510
- return job.kind === "push" ? null : parseSummary(payload);
511
- case "declaration":
512
- if (options.declared === true)
513
- return null;
514
- return job.kind === "pull" && job.discoverFirst === true ? parseDeclaration(payload) : null;
515
- case "http":
516
- return parseHttpRequest(payload);
517
- case "db.query":
518
- return isRecord(payload) && typeof payload.sql === "string" && Array.isArray(payload.params) ? { sql: payload.sql, params: [...payload.params] } : null;
519
- case "db.next":
520
- case "db.close":
521
- return isRecord(payload) && typeof payload.cursorId === "string" ? { cursorId: payload.cursorId } : null;
522
- case "crypto":
523
- return parseCryptoRequest(payload);
524
- case "cache.get":
525
- return isRecord(payload) && typeof payload.key === "string" ? { key: payload.key } : null;
526
- case "cache.set": {
527
- if (!isRecord(payload) || typeof payload.key !== "string" || typeof payload.value !== "string")
528
- return null;
529
- const ttl = payload.ttlSeconds;
530
- return typeof ttl === "number" && Number.isSafeInteger(ttl) && ttl > 0 ? { key: payload.key, value: payload.value, ttlSeconds: ttl } : null;
531
- }
532
- case "secret.reveal":
533
- return isRecord(payload) && typeof payload.field === "string" && job.secretFields.includes(payload.field) ? { field: payload.field } : null;
534
- case "emit":
535
- if (job.kind !== "pull")
536
- return null;
537
- return parseBatch(payload, new Set(job.discoverFirst === true ? options.tables ?? [] : job.tables));
538
- }
539
- }
540
- function undeclaredEmitTable(value, job, options = {}) {
541
- if (job.kind !== "pull" || !isRecord(value) || value.type !== "call" || value.method !== "emit" || !isRecord(value.payload))
542
- return null;
543
- const allowed = new Set(job.discoverFirst === true ? options.tables ?? [] : job.tables);
544
- for (const key of ["records", "removals"]) {
545
- const items = value.payload[key];
546
- if (!Array.isArray(items))
547
- continue;
548
- for (const item of items) {
549
- if (isRecord(item) && typeof item.table === "string" && !allowed.has(item.table))
550
- return item.table;
551
- }
552
- }
553
- return null;
554
- }
555
- function parseChildMessage(value, job, options = {}) {
556
- if (!isRecord(value))
557
- return null;
558
- switch (value.type) {
559
- case "ready":
560
- return { type: "ready" };
561
- case "call": {
562
- if (!isId(value.id) || typeof value.method !== "string" || !HOST_METHOD_SET.has(value.method))
563
- return null;
564
- const method = value.method;
565
- const payload = parsePayload(method, value.payload, job, options);
566
- return payload === null ? null : { type: "call", id: value.id, method, payload };
567
- }
568
- case "result": {
569
- const result = parseResult(value.result, job);
570
- return result ? { type: "result", result } : null;
571
- }
572
- case "failure": {
573
- if (typeof value.code !== "string" || !FAILURE_CODE_SET.has(value.code) || typeof value.message !== "string")
574
- return null;
575
- if (value.stack !== undefined && typeof value.stack !== "string")
576
- return null;
577
- const logs = parseLogs(value.logs);
578
- if (!logs)
579
- return null;
580
- return {
581
- type: "failure",
582
- code: value.code,
583
- message: value.message,
584
- ...value.stack !== undefined ? { stack: value.stack } : {},
585
- logs
586
- };
587
- }
588
- default:
589
- return null;
590
- }
591
- }
592
-
593
- // src/runtime/session.ts
594
- var GUEST_FILENAME = "ontos-guest.js";
595
- var CONNECTOR_FILENAME = "connector.js";
596
- function isRecord2(value) {
597
- return typeof value === "object" && value !== null && !Array.isArray(value);
598
- }
599
- function guestErrorMessage(error) {
600
- try {
601
- if (error !== null && typeof error === "object") {
602
- const message = error.message;
603
- if (typeof message === "string")
604
- return message;
605
- }
606
- } catch {}
607
- return "unknown error";
608
- }
609
- function bootGuest(context, guestSource) {
610
- const wrapped = `(() => { const module = { exports: {} }; const exports = module.exports; ${guestSource}
611
- ; return module.exports; })()`;
612
- const exported = new import_node_vm.default.Script(wrapped, { filename: GUEST_FILENAME }).runInContext(context);
613
- const boot = exported.bootGuest;
614
- if (typeof boot !== "function")
615
- throw new Error("guest runtime does not export bootGuest");
616
- const api = boot();
617
- const { start, handle, poll } = api;
618
- if (typeof start !== "function" || typeof handle !== "function" || typeof poll !== "function")
619
- throw new Error("guest runtime returned an incomplete API");
620
- return { start, handle, poll };
621
- }
622
- function bootInVm(guestSource, code) {
623
- const context = import_node_vm.default.createContext(Object.create(null), { codeGeneration: { strings: false, wasm: false } });
624
- let guest;
625
- try {
626
- guest = bootGuest(context, guestSource);
627
- } catch (error) {
628
- return { ok: false, code: "CONNECTOR_LOAD_FAILED", message: `connector runtime failed to start: ${guestErrorMessage(error)}` };
629
- }
630
- try {
631
- const factory = new import_node_vm.default.Script(`(function (module, exports, require) {${code}
632
- })`, { filename: CONNECTOR_FILENAME }).runInContext(context);
633
- return { ok: true, guest, factory };
634
- } catch (error) {
635
- return { ok: false, code: "CONNECTOR_LOAD_FAILED", message: `connector code failed to compile: ${guestErrorMessage(error)}` };
636
- }
637
- }
638
- function createGuestSession(send) {
639
- return createSession(send, bootInVm);
640
- }
641
- function createSession(send, boot) {
642
- let guest = null;
643
- let started = false;
644
- let done = false;
645
- let timer;
646
- let timerAt = Number.POSITIVE_INFINITY;
647
- const stop = () => {
648
- done = true;
649
- if (timer !== undefined)
650
- clearTimeout(timer);
651
- timer = undefined;
652
- };
653
- const fail = (code, message) => {
654
- if (done)
655
- return;
656
- stop();
657
- send(JSON.stringify({ type: "failure", code, message, logs: [] }));
658
- };
659
- const schedule = (delay) => {
660
- if (done)
661
- return;
662
- const at = Date.now() + delay;
663
- if (timer !== undefined && timerAt <= at)
664
- return;
665
- if (timer !== undefined)
666
- clearTimeout(timer);
667
- timerAt = at;
668
- timer = setTimeout(pump, delay);
669
- };
670
- function pump() {
671
- timer = undefined;
672
- timerAt = Number.POSITIVE_INFINITY;
673
- if (done || !guest)
674
- return;
675
- let text;
676
- try {
677
- text = guest.poll();
678
- } catch (error) {
679
- fail("FATAL_ERROR", `connector runtime failed: ${guestErrorMessage(error)}`);
680
- return;
681
- }
682
- let parsed;
683
- try {
684
- parsed = typeof text === "string" ? JSON.parse(text) : null;
685
- } catch {
686
- parsed = null;
687
- }
688
- if (!isRecord2(parsed) || !Array.isArray(parsed.out)) {
689
- fail("FATAL_ERROR", "connector runtime returned a malformed poll result");
690
- return;
691
- }
692
- for (const message of parsed.out) {
693
- if (!isRecord2(message))
694
- continue;
695
- send(JSON.stringify(message));
696
- if (message.type === "result" || message.type === "failure") {
697
- stop();
698
- return;
699
- }
700
- }
701
- if (parsed.out.length > 0 || parsed.fired === true)
702
- schedule(0);
703
- else if (typeof parsed.wake === "number" && parsed.wake >= 0)
704
- schedule(parsed.wake);
705
- }
706
- const execute = (guestSource, code, payload) => {
707
- const booted = boot(guestSource, code);
708
- if (!booted.ok) {
709
- fail(booted.code, booted.message);
710
- return;
711
- }
712
- guest = booted.guest;
713
- try {
714
- guest.start(payload, booted.factory);
715
- } catch (error) {
716
- fail("FATAL_ERROR", `connector runtime failed: ${guestErrorMessage(error)}`);
717
- return;
718
- }
719
- schedule(0);
720
- };
721
- return {
722
- handle(json) {
723
- if (done || typeof json !== "string")
724
- return;
725
- let raw;
726
- try {
727
- raw = JSON.parse(json);
728
- } catch {
729
- return;
730
- }
731
- const message = parseParentMessage(raw);
732
- if (!message)
733
- return;
734
- if (message.type === "execute") {
735
- if (started)
736
- return;
737
- started = true;
738
- execute(message.guest, message.code, JSON.stringify({ expect: message.expect, job: message.job }));
739
- return;
740
- }
741
- if (!guest)
742
- return;
743
- try {
744
- guest.handle(JSON.stringify(message));
745
- } catch (error) {
746
- fail("FATAL_ERROR", `connector runtime failed: ${guestErrorMessage(error)}`);
747
- return;
748
- }
749
- schedule(0);
750
- }
751
- };
752
- }