@deepexi/datasense-connector 0.0.0-stage → 3.0.1

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 (95) hide show
  1. package/README.md +172 -2
  2. package/cjs/index.cjs +1015 -0
  3. package/cjs/runtime.cjs +752 -0
  4. package/cjs/testing.cjs +3591 -0
  5. package/errors.d.ts +17 -0
  6. package/errors.js +53 -0
  7. package/guest.cjs +2659 -0
  8. package/index.d.ts +13 -0
  9. package/index.js +7 -0
  10. package/internal/location.d.ts +6 -0
  11. package/internal/location.js +8 -0
  12. package/package.json +42 -4
  13. package/presentation.d.ts +38 -0
  14. package/presentation.js +268 -0
  15. package/runtime/guest/bridge.d.ts +53 -0
  16. package/runtime/guest/bridge.js +187 -0
  17. package/runtime/guest/cache.d.ts +3 -0
  18. package/runtime/guest/cache.js +42 -0
  19. package/runtime/guest/crypto.d.ts +13 -0
  20. package/runtime/guest/crypto.js +177 -0
  21. package/runtime/guest/ctx.d.ts +30 -0
  22. package/runtime/guest/ctx.js +124 -0
  23. package/runtime/guest/db.d.ts +6 -0
  24. package/runtime/guest/db.js +59 -0
  25. package/runtime/guest/discover.d.ts +29 -0
  26. package/runtime/guest/discover.js +51 -0
  27. package/runtime/guest/failure.d.ts +24 -0
  28. package/runtime/guest/failure.js +63 -0
  29. package/runtime/guest/http.d.ts +4 -0
  30. package/runtime/guest/http.js +204 -0
  31. package/runtime/guest/index.d.ts +12 -0
  32. package/runtime/guest/index.js +89 -0
  33. package/runtime/guest/limits.d.ts +20 -0
  34. package/runtime/guest/limits.js +12 -0
  35. package/runtime/guest/loader.d.ts +15 -0
  36. package/runtime/guest/loader.js +42 -0
  37. package/runtime/guest/log.d.ts +10 -0
  38. package/runtime/guest/log.js +36 -0
  39. package/runtime/guest/map.d.ts +4 -0
  40. package/runtime/guest/map.js +45 -0
  41. package/runtime/guest/paginate.d.ts +6 -0
  42. package/runtime/guest/paginate.js +180 -0
  43. package/runtime/guest/pull.d.ts +12 -0
  44. package/runtime/guest/pull.js +150 -0
  45. package/runtime/guest/push.d.ts +18 -0
  46. package/runtime/guest/push.js +80 -0
  47. package/runtime/guest/signal.d.ts +3 -0
  48. package/runtime/guest/signal.js +12 -0
  49. package/runtime/guest/time.d.ts +5 -0
  50. package/runtime/guest/time.js +127 -0
  51. package/runtime/guest-runtime.d.ts +7 -0
  52. package/runtime/guest-runtime.js +51 -0
  53. package/runtime/index.d.ts +18 -0
  54. package/runtime/index.js +3 -0
  55. package/runtime/protocol.d.ts +276 -0
  56. package/runtime/protocol.js +409 -0
  57. package/runtime/session.d.ts +12 -0
  58. package/runtime/session.js +191 -0
  59. package/t.d.ts +18 -0
  60. package/t.js +26 -0
  61. package/testing/child.cjs +350 -0
  62. package/testing/child.d.ts +1 -0
  63. package/testing/child.js +12 -0
  64. package/testing/compliance.d.ts +41 -0
  65. package/testing/compliance.js +291 -0
  66. package/testing/engine.d.ts +66 -0
  67. package/testing/engine.js +339 -0
  68. package/testing/fixtures.d.ts +145 -0
  69. package/testing/fixtures.js +407 -0
  70. package/testing/gateway.d.ts +65 -0
  71. package/testing/gateway.js +402 -0
  72. package/testing/import-guard.d.ts +57 -0
  73. package/testing/import-guard.js +187 -0
  74. package/testing/index.d.ts +33 -0
  75. package/testing/index.js +10 -0
  76. package/testing/local-db.d.ts +27 -0
  77. package/testing/local-db.js +181 -0
  78. package/testing/pinned-http.d.ts +14 -0
  79. package/testing/pinned-http.js +125 -0
  80. package/testing/recorder.d.ts +15 -0
  81. package/testing/recorder.js +116 -0
  82. package/testing/redaction.d.ts +28 -0
  83. package/testing/redaction.js +67 -0
  84. package/testing/replay.d.ts +50 -0
  85. package/testing/replay.js +313 -0
  86. package/testing/schema.d.ts +13 -0
  87. package/testing/schema.js +65 -0
  88. package/testing/source-loader.d.ts +19 -0
  89. package/testing/source-loader.js +238 -0
  90. package/testing/types.d.ts +79 -0
  91. package/testing/types.js +18 -0
  92. package/types.d.ts +302 -0
  93. package/types.js +13 -0
  94. package/validate.d.ts +78 -0
  95. package/validate.js +597 -0
package/guest.cjs ADDED
@@ -0,0 +1,2659 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropNames = Object.getOwnPropertyNames;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ function __accessProp(key) {
6
+ return this[key];
7
+ }
8
+ var __toCommonJS = (from) => {
9
+ var entry = (__moduleCache ??= new WeakMap).get(from), desc;
10
+ if (entry)
11
+ return entry;
12
+ entry = __defProp({}, "__esModule", { value: true });
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (var key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(entry, key))
16
+ __defProp(entry, key, {
17
+ get: __accessProp.bind(from, key),
18
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
19
+ });
20
+ }
21
+ __moduleCache.set(from, entry);
22
+ return entry;
23
+ };
24
+ var __moduleCache;
25
+ var __returnValue = (v) => v;
26
+ function __exportSetter(name, newValue) {
27
+ this[name] = __returnValue.bind(null, newValue);
28
+ }
29
+ var __export = (target, all) => {
30
+ for (var name in all)
31
+ __defProp(target, name, {
32
+ get: all[name],
33
+ enumerable: true,
34
+ configurable: true,
35
+ set: __exportSetter.bind(all, name)
36
+ });
37
+ };
38
+
39
+ // src/runtime/guest/index.ts
40
+ var exports_guest = {};
41
+ __export(exports_guest, {
42
+ bootGuest: () => bootGuest,
43
+ bootLocalGuest: () => bootLocalGuest
44
+ });
45
+ module.exports = __toCommonJS(exports_guest);
46
+
47
+ // src/index.ts
48
+ var exports_src = {};
49
+ __export(exports_src, {
50
+ ConnectorError: () => ConnectorError,
51
+ DEFAULT_PUSH_NAME: () => DEFAULT_PUSH_NAME,
52
+ PNG_DATA_URI_PREFIX: () => PNG_DATA_URI_PREFIX,
53
+ PRESENTATION_LIMITS: () => PRESENTATION_LIMITS,
54
+ RESERVED_PUSH_NAMES: () => RESERVED_PUSH_NAMES,
55
+ SDK_NAME_PATTERN: () => SDK_NAME_PATTERN,
56
+ SDK_VERSION: () => SDK_VERSION,
57
+ SUPPORTED_SDK_MAJORS: () => SUPPORTED_SDK_MAJORS,
58
+ decodeConnectorIcon: () => decodeConnectorIcon,
59
+ defineConnector: () => defineConnector,
60
+ errors: () => errors,
61
+ normalizeDiscoverResult: () => normalizeDiscoverResult,
62
+ readConnectorPresentation: () => readConnectorPresentation,
63
+ summarizeDefinition: () => summarizeDefinition,
64
+ t: () => t,
65
+ validateConfig: () => validateConfig,
66
+ validateConnectorDefinition: () => validateConnectorDefinition,
67
+ validateDiscoverResult: () => validateDiscoverResult
68
+ });
69
+
70
+ // src/types.ts
71
+ function isSingleObjectPush(push) {
72
+ if (push === null || typeof push !== "object" || Array.isArray(push))
73
+ return false;
74
+ const value = push;
75
+ return typeof value.map === "function" || typeof value.verify === "function";
76
+ }
77
+ var SINGLE_OBJECT_PUSH_MESSAGE = "push must be declared by name, e.g. push: { default: { map } }; the single-object form push: { map } is not supported";
78
+ function defineConnector(definition) {
79
+ if (definition !== null && typeof definition === "object" && isSingleObjectPush(definition.push)) {
80
+ throw new TypeError(SINGLE_OBJECT_PUSH_MESSAGE);
81
+ }
82
+ return definition;
83
+ }
84
+
85
+ // src/validate.ts
86
+ var SDK_NAME_PATTERN = /^[a-z][a-z0-9_]{0,27}$/;
87
+ var RESERVED_PUSH_NAMES = Object.freeze(["dev"]);
88
+ var DEFAULT_PUSH_NAME = "default";
89
+ var VERSION_PATTERN = /^\d+\.\d+\.\d+$/;
90
+ var SCALAR_KINDS = new Set(["string", "integer", "decimal", "boolean", "date", "instant", "json"]);
91
+ var RETENTION_KINDS = new Set(["instant", "date"]);
92
+ var SYNC_MODES = new Set(["full", "incremental"]);
93
+ var SNAPSHOTS = new Set(["complete", "partial"]);
94
+ var PURGE_KEYS = ["tenant", "user"];
95
+ var FIELD_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
96
+ var HOST_PLACEHOLDER = /\{config\.([^{}]*)\}/g;
97
+ var HOST_LABEL = "[a-z0-9](?:[a-z0-9-]*[a-z0-9])?";
98
+ var HOST_PATTERN = new RegExp(`^(?:\\*\\.)?${HOST_LABEL}(?:\\.${HOST_LABEL})*$`, "i");
99
+ var DATE_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
100
+ var INSTANT_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(?:Z|[+-]\d{2}:\d{2})$/;
101
+ var DECIMAL_TEXT_PATTERN = /^-?\d+(?:\.\d+)?$/;
102
+ var PRESENTATION_LIMITS = Object.freeze({
103
+ title: 40,
104
+ description: 120,
105
+ readmeBytes: 65536,
106
+ iconBytes: 262144,
107
+ iconMinPx: 128,
108
+ fieldTitle: 40,
109
+ fieldDescription: 200,
110
+ placeholder: 100,
111
+ tableTitle: 40,
112
+ tableDescription: 200
113
+ });
114
+ function isRecord(value) {
115
+ return typeof value === "object" && value !== null && !Array.isArray(value);
116
+ }
117
+ function isPositiveInteger(value) {
118
+ return typeof value === "number" && Number.isInteger(value) && value > 0;
119
+ }
120
+ function isStringArray(value) {
121
+ return Array.isArray(value) && value.every((item) => typeof item === "string");
122
+ }
123
+ function validateText(path, owner, key, max, issues) {
124
+ const value = owner[key];
125
+ if (value === undefined)
126
+ return;
127
+ if (typeof value !== "string") {
128
+ issues.push({ path: `${path}.${key}`, message: `${key} must be a string` });
129
+ } else if ([...value].length > max) {
130
+ issues.push({ path: `${path}.${key}`, message: `${key} must be at most ${max} characters` });
131
+ }
132
+ }
133
+ function validateFieldMeta(path, type, issues) {
134
+ validateText(path, type, "title", PRESENTATION_LIMITS.fieldTitle, issues);
135
+ validateText(path, type, "description", PRESENTATION_LIMITS.fieldDescription, issues);
136
+ if (type.placeholder !== undefined && (type.kind === "object" || type.kind === "array")) {
137
+ issues.push({ path: `${path}.placeholder`, message: "placeholder is only allowed on scalar and secret fields" });
138
+ return;
139
+ }
140
+ validateText(path, type, "placeholder", PRESENTATION_LIMITS.placeholder, issues);
141
+ }
142
+ function columnTypeProblem(type) {
143
+ if (!isRecord(type) || typeof type.kind !== "string" || !SCALAR_KINDS.has(type.kind)) {
144
+ return "column type must be a scalar type (string, integer, decimal, boolean, date, instant or json)";
145
+ }
146
+ if (type.kind === "decimal")
147
+ return decimalProblem(type);
148
+ return null;
149
+ }
150
+ function decimalProblem(type) {
151
+ const { precision, scale } = type;
152
+ if (!isPositiveInteger(precision) || typeof scale !== "number" || !Number.isInteger(scale) || scale < 0 || scale > precision) {
153
+ return "decimal needs an integer precision > 0 and an integer scale between 0 and precision";
154
+ }
155
+ return null;
156
+ }
157
+ function validateTable(path, table, issues) {
158
+ if (!isRecord(table)) {
159
+ issues.push({ path, message: "table declaration must be an object" });
160
+ return;
161
+ }
162
+ const columns = isRecord(table.columns) ? table.columns : null;
163
+ if (!columns) {
164
+ issues.push({ path: `${path}.columns`, message: "columns must be an object" });
165
+ return;
166
+ }
167
+ const columnKind = (name) => {
168
+ const type = columns[name];
169
+ return isRecord(type) && typeof type.kind === "string" ? type.kind : undefined;
170
+ };
171
+ const isDeclared = (name) => typeof name === "string" && Object.hasOwn(columns, name);
172
+ for (const [name, type] of Object.entries(columns)) {
173
+ const problem = columnTypeProblem(type);
174
+ if (problem)
175
+ issues.push({ path: `${path}.columns.${name}`, message: problem });
176
+ else
177
+ validateFieldMeta(`${path}.columns.${name}`, type, issues);
178
+ }
179
+ validateText(path, table, "title", PRESENTATION_LIMITS.tableTitle, issues);
180
+ validateText(path, table, "description", PRESENTATION_LIMITS.tableDescription, issues);
181
+ const primaryKey = table.primaryKey;
182
+ if (!isStringArray(primaryKey) || primaryKey.length === 0) {
183
+ issues.push({ path: `${path}.primaryKey`, message: "primaryKey must be a non-empty list of column names" });
184
+ } else {
185
+ const missing = primaryKey.filter((name) => !isDeclared(name));
186
+ if (missing.length > 0) {
187
+ issues.push({ path: `${path}.primaryKey`, message: `primaryKey references undeclared columns: ${missing.join(", ")}` });
188
+ }
189
+ }
190
+ if (table.retention !== undefined) {
191
+ const retention = table.retention;
192
+ if (!isRecord(retention)) {
193
+ issues.push({ path: `${path}.retention`, message: "retention must be an object" });
194
+ } else {
195
+ const column = retention.column;
196
+ if (!isDeclared(column)) {
197
+ issues.push({ path: `${path}.retention.column`, message: "retention column must be a declared column" });
198
+ } else if (!RETENTION_KINDS.has(columnKind(column) ?? "")) {
199
+ issues.push({ path: `${path}.retention.column`, message: "retention column must be of type instant or date" });
200
+ } else if (!isStringArray(primaryKey) || !primaryKey.includes(column)) {
201
+ issues.push({ path: `${path}.retention.column`, message: "retention column must be part of primaryKey" });
202
+ }
203
+ if (!isPositiveInteger(retention.days)) {
204
+ issues.push({ path: `${path}.retention.days`, message: "retention days must be a positive integer" });
205
+ }
206
+ }
207
+ }
208
+ if (table.purgeColumns !== undefined) {
209
+ const purgeColumns = table.purgeColumns;
210
+ if (!isRecord(purgeColumns)) {
211
+ issues.push({ path: `${path}.purgeColumns`, message: "purgeColumns must be an object" });
212
+ } else {
213
+ for (const key of PURGE_KEYS) {
214
+ const column = purgeColumns[key];
215
+ if (column !== undefined && !isDeclared(column)) {
216
+ issues.push({ path: `${path}.purgeColumns.${key}`, message: "purge column must be a declared column" });
217
+ }
218
+ }
219
+ }
220
+ }
221
+ if (table.indexes !== undefined) {
222
+ const indexes = table.indexes;
223
+ if (!Array.isArray(indexes)) {
224
+ issues.push({ path: `${path}.indexes`, message: "indexes must be a list of column lists" });
225
+ } else {
226
+ indexes.forEach((index, i) => {
227
+ if (!isStringArray(index) || index.length === 0 || !index.every(isDeclared)) {
228
+ issues.push({ path: `${path}.indexes[${i}]`, message: "index must be a non-empty list of declared columns" });
229
+ }
230
+ });
231
+ }
232
+ }
233
+ }
234
+ function validateTables(path, tables, owners, owner, issues) {
235
+ if (!isRecord(tables)) {
236
+ issues.push({ path, message: "tables must be an object" });
237
+ return;
238
+ }
239
+ for (const [name, table] of Object.entries(tables)) {
240
+ const tablePath = `${path}.${name}`;
241
+ if (!SDK_NAME_PATTERN.test(name)) {
242
+ issues.push({ path: tablePath, message: `table name must match ${SDK_NAME_PATTERN.source}` });
243
+ }
244
+ const previous = owners.get(name);
245
+ if (previous !== undefined) {
246
+ issues.push({ path: tablePath, message: `table ${name} is already declared by ${previous}` });
247
+ } else {
248
+ owners.set(name, owner);
249
+ }
250
+ validateTable(tablePath, table, issues);
251
+ }
252
+ }
253
+ function validateSdkType(path, type, rules, issues) {
254
+ if (!isRecord(type) || typeof type.kind !== "string") {
255
+ issues.push({ path, message: "type must be declared with t.*()" });
256
+ return;
257
+ }
258
+ const nested = { secretAllowed: false, secretMessage: rules.secretMessage };
259
+ if (type.kind === "secret" || type.kind === "object" || type.kind === "array" || SCALAR_KINDS.has(type.kind)) {
260
+ validateFieldMeta(path, type, issues);
261
+ }
262
+ switch (type.kind) {
263
+ case "secret":
264
+ if (!rules.secretAllowed)
265
+ issues.push({ path, message: rules.secretMessage });
266
+ else if (type.exposeToScript !== undefined && typeof type.exposeToScript !== "boolean") {
267
+ issues.push({ path: `${path}.exposeToScript`, message: "exposeToScript must be a boolean" });
268
+ }
269
+ return;
270
+ case "object":
271
+ validateShape(path, type, nested, issues);
272
+ return;
273
+ case "array":
274
+ validateSdkType(`${path}[]`, type.item, nested, issues);
275
+ return;
276
+ case "decimal": {
277
+ const problem = decimalProblem(type);
278
+ if (problem)
279
+ issues.push({ path, message: problem });
280
+ return;
281
+ }
282
+ default:
283
+ if (!SCALAR_KINDS.has(type.kind))
284
+ issues.push({ path, message: `unknown type kind ${type.kind}` });
285
+ }
286
+ }
287
+ function validateShape(path, type, fieldRules, issues) {
288
+ if (!isRecord(type.shape)) {
289
+ issues.push({ path, message: "object type needs a shape" });
290
+ return;
291
+ }
292
+ for (const [field, fieldType] of Object.entries(type.shape)) {
293
+ const fieldPath = `${path}.${field}`;
294
+ if (!FIELD_NAME_PATTERN.test(field)) {
295
+ issues.push({ path: fieldPath, message: `field name must match ${FIELD_NAME_PATTERN.source}` });
296
+ }
297
+ validateSdkType(fieldPath, fieldType, fieldRules, issues);
298
+ }
299
+ }
300
+ function validateObjectDeclaration(path, type, rules, issues) {
301
+ if (!isRecord(type) || type.kind !== "object") {
302
+ issues.push({ path, message: `${path} must be declared with t.object()` });
303
+ return false;
304
+ }
305
+ validateFieldMeta(path, type, issues);
306
+ validateShape(path, type, rules, issues);
307
+ return true;
308
+ }
309
+ function configFieldType(config, fieldPath) {
310
+ let current = config;
311
+ for (const segment of fieldPath.split(".")) {
312
+ if (!isRecord(current) || current.kind !== "object" || !isRecord(current.shape) || !Object.hasOwn(current.shape, segment))
313
+ return;
314
+ current = current.shape[segment];
315
+ }
316
+ return isRecord(current) ? current : undefined;
317
+ }
318
+ function pushNames(push) {
319
+ return isRecord(push) ? Object.keys(push) : [];
320
+ }
321
+ function validatePushHandler(path, handler, issues) {
322
+ if (!isRecord(handler)) {
323
+ issues.push({ path, message: "push handler must be an object with map()" });
324
+ return;
325
+ }
326
+ if (handler.verify !== undefined && typeof handler.verify !== "function") {
327
+ issues.push({ path: `${path}.verify`, message: `${path}.verify must be a function` });
328
+ }
329
+ if (typeof handler.map !== "function") {
330
+ issues.push({ path: `${path}.map`, message: `${path}.map must be a function` });
331
+ }
332
+ }
333
+ function validatePush(push, issues) {
334
+ if (push === undefined)
335
+ return 0;
336
+ if (!isRecord(push)) {
337
+ issues.push({ path: "push", message: "push must be an object" });
338
+ return 0;
339
+ }
340
+ if (isSingleObjectPush(push)) {
341
+ issues.push({ path: "push", message: SINGLE_OBJECT_PUSH_MESSAGE });
342
+ return 1;
343
+ }
344
+ const names = Object.keys(push);
345
+ if (names.length === 0) {
346
+ issues.push({ path: "push", message: "push must declare at least one handler, e.g. push: { default: { map } }" });
347
+ return 0;
348
+ }
349
+ for (const name of names) {
350
+ const path = `push.${name}`;
351
+ if (!SDK_NAME_PATTERN.test(name)) {
352
+ issues.push({ path, message: `push name must match ${SDK_NAME_PATTERN.source}` });
353
+ } else if (RESERVED_PUSH_NAMES.includes(name)) {
354
+ issues.push({ path, message: `push name ${name} is reserved` });
355
+ }
356
+ validatePushHandler(path, push[name], issues);
357
+ }
358
+ return names.length;
359
+ }
360
+ function validateHosts(hosts, config, issues) {
361
+ if (hosts === undefined)
362
+ return 0;
363
+ if (!Array.isArray(hosts)) {
364
+ issues.push({ path: "hosts", message: "hosts must be a list of host names" });
365
+ return 0;
366
+ }
367
+ hosts.forEach((host, i) => {
368
+ const path = `hosts[${i}]`;
369
+ if (typeof host !== "string" || host.length === 0 || host.length > 253) {
370
+ issues.push({ path, message: "host must be a non-empty string" });
371
+ return;
372
+ }
373
+ for (const match of host.matchAll(HOST_PLACEHOLDER)) {
374
+ const fieldPath = match[1] ?? "";
375
+ const fieldType = configFieldType(config, fieldPath);
376
+ if (!fieldType) {
377
+ issues.push({ path, message: `host template references undeclared config field ${fieldPath}` });
378
+ } else if (fieldType.kind !== "string") {
379
+ issues.push({ path, message: `host template field ${fieldPath} must be declared with t.string()` });
380
+ }
381
+ }
382
+ if (!HOST_PATTERN.test(host.replace(HOST_PLACEHOLDER, "x"))) {
383
+ issues.push({ path, message: "host must be an exact host name or *.suffix, without scheme, port or path" });
384
+ }
385
+ });
386
+ return hosts.length;
387
+ }
388
+ function validateRateLimit(rateLimit, issues) {
389
+ if (rateLimit === undefined)
390
+ return;
391
+ if (!isRecord(rateLimit)) {
392
+ issues.push({ path: "rateLimit", message: "rateLimit must be { perSecond, concurrency }" });
393
+ return;
394
+ }
395
+ const { perSecond, concurrency } = rateLimit;
396
+ if (typeof perSecond !== "number" || !Number.isFinite(perSecond) || perSecond <= 0) {
397
+ issues.push({ path: "rateLimit.perSecond", message: "rateLimit.perSecond must be a positive number" });
398
+ }
399
+ if (!isPositiveInteger(concurrency)) {
400
+ issues.push({ path: "rateLimit.concurrency", message: "rateLimit.concurrency must be a positive integer" });
401
+ }
402
+ }
403
+ function validateStreams(streams, issues) {
404
+ if (streams === undefined)
405
+ return 0;
406
+ if (!isRecord(streams)) {
407
+ issues.push({ path: "streams", message: "streams must be an object of async generator functions" });
408
+ return 0;
409
+ }
410
+ for (const [name, stream] of Object.entries(streams)) {
411
+ const path = `streams.${name}`;
412
+ if (!SDK_NAME_PATTERN.test(name))
413
+ issues.push({ path, message: `stream name must match ${SDK_NAME_PATTERN.source}` });
414
+ if (typeof stream !== "function")
415
+ issues.push({ path, message: `${path} must be an async generator function` });
416
+ }
417
+ return Object.keys(streams).length;
418
+ }
419
+ function validateConnectorDefinition(definition) {
420
+ if (!isRecord(definition)) {
421
+ return [{ path: "", message: "connector definition must be an object" }];
422
+ }
423
+ const issues = [];
424
+ if (typeof definition.name !== "string" || !SDK_NAME_PATTERN.test(definition.name)) {
425
+ issues.push({ path: "name", message: `name must match ${SDK_NAME_PATTERN.source}` });
426
+ }
427
+ if (typeof definition.version !== "string" || !VERSION_PATTERN.test(definition.version)) {
428
+ issues.push({ path: "version", message: "version must be a semver string like 1.0.0" });
429
+ }
430
+ validateObjectDeclaration("config", definition.config, {
431
+ secretAllowed: true,
432
+ secretMessage: "secret fields must be top-level config fields"
433
+ }, issues);
434
+ const hostCount = validateHosts(definition.hosts, definition.config, issues);
435
+ const hasDb = definition.db !== undefined;
436
+ if (hasDb && (!isRecord(definition.db) || definition.db.engine !== "postgresql")) {
437
+ issues.push({ path: "db.engine", message: "db.engine must be 'postgresql'" });
438
+ }
439
+ validateRateLimit(definition.rateLimit, issues);
440
+ if (definition.check !== undefined && typeof definition.check !== "function") {
441
+ issues.push({ path: "check", message: "check must be a function" });
442
+ }
443
+ if (typeof definition.discover !== "function") {
444
+ issues.push({ path: "discover", message: "discover must be a function" });
445
+ }
446
+ if (definition.writes !== undefined) {
447
+ issues.push({ path: "writes", message: "external write operations are not available yet" });
448
+ }
449
+ const streamCount = validateStreams(definition.streams, issues);
450
+ const pushCount = validatePush(definition.push, issues);
451
+ if (streamCount === 0 && pushCount === 0) {
452
+ issues.push({ path: "streams", message: "connector must declare at least one stream or push" });
453
+ }
454
+ if (streamCount > 0 && hostCount === 0 && !hasDb) {
455
+ issues.push({ path: "hosts", message: "connectors with streams must declare hosts (for ctx.http) or db (for ctx.db)" });
456
+ }
457
+ return issues;
458
+ }
459
+ function validateStreamDiscovery(path, stream, owners, issues) {
460
+ if (!isRecord(stream)) {
461
+ issues.push({ path, message: "stream declaration must be an object" });
462
+ return;
463
+ }
464
+ validateTables(`${path}.tables`, stream.tables, owners, path, issues);
465
+ const syncModes = stream.syncModes;
466
+ if (!isStringArray(syncModes) || syncModes.length === 0 || !syncModes.every((mode) => SYNC_MODES.has(mode)) || new Set(syncModes).size !== syncModes.length) {
467
+ issues.push({ path: `${path}.syncModes`, message: "syncModes must be a non-empty list of 'full' and/or 'incremental'" });
468
+ }
469
+ if (stream.snapshot !== undefined && (typeof stream.snapshot !== "string" || !SNAPSHOTS.has(stream.snapshot))) {
470
+ issues.push({ path: `${path}.snapshot`, message: "snapshot must be 'complete' or 'partial'" });
471
+ }
472
+ if (stream.params !== undefined) {
473
+ validateObjectDeclaration(`${path}.params`, stream.params, {
474
+ secretAllowed: false,
475
+ secretMessage: "stream params cannot contain secret fields"
476
+ }, issues);
477
+ }
478
+ }
479
+ function definitionStreamNames(definition) {
480
+ if (!isRecord(definition) || !isRecord(definition.streams))
481
+ return [];
482
+ return Object.keys(definition.streams);
483
+ }
484
+ function definitionPushNames(definition) {
485
+ if (!isRecord(definition))
486
+ return [];
487
+ return pushNames(definition.push);
488
+ }
489
+ function validateDiscoverResult(result, definition) {
490
+ if (!isRecord(result)) {
491
+ return [{ path: "", message: "discover result must be an object" }];
492
+ }
493
+ const issues = [];
494
+ const owners = new Map;
495
+ const declaredStreams = definitionStreamNames(definition);
496
+ const streams = result.streams === undefined ? {} : result.streams;
497
+ if (!isRecord(streams)) {
498
+ issues.push({ path: "streams", message: "streams must be an object" });
499
+ } else {
500
+ for (const [name, stream] of Object.entries(streams)) {
501
+ const path = `streams.${name}`;
502
+ if (!declaredStreams.includes(name)) {
503
+ issues.push({ path, message: `stream ${name} is not defined in the connector's streams` });
504
+ }
505
+ validateStreamDiscovery(path, stream, owners, issues);
506
+ }
507
+ for (const name of declaredStreams) {
508
+ if (!Object.hasOwn(streams, name)) {
509
+ issues.push({ path: `streams.${name}`, message: `stream ${name} is defined but missing from the discover result` });
510
+ }
511
+ }
512
+ }
513
+ const declaredPushes = definitionPushNames(definition);
514
+ const pushes = result.push === undefined ? {} : result.push;
515
+ if (!isRecord(pushes)) {
516
+ issues.push({ path: "push", message: "push must be an object of { tables } by push name" });
517
+ } else {
518
+ for (const [name, push] of Object.entries(pushes)) {
519
+ const path = `push.${name}`;
520
+ if (!declaredPushes.includes(name)) {
521
+ issues.push({ path, message: `push ${name} is not defined in the connector's push handlers` });
522
+ }
523
+ if (!isRecord(push)) {
524
+ issues.push({ path, message: "push declaration must be { tables }" });
525
+ continue;
526
+ }
527
+ validateTables(`${path}.tables`, push.tables, owners, path, issues);
528
+ }
529
+ for (const name of declaredPushes) {
530
+ if (!Object.hasOwn(pushes, name)) {
531
+ issues.push({ path: `push.${name}`, message: `push ${name} is defined but missing from the discover result` });
532
+ }
533
+ }
534
+ }
535
+ return issues;
536
+ }
537
+ function normalizeDiscoverResult(result) {
538
+ const streams = {};
539
+ for (const [name, stream] of Object.entries(result.streams ?? {})) {
540
+ streams[name] = {
541
+ tables: stream.tables,
542
+ syncModes: [...stream.syncModes],
543
+ snapshot: stream.snapshot ?? "partial",
544
+ ...stream.params ? { params: stream.params } : {}
545
+ };
546
+ }
547
+ const pushes = {};
548
+ for (const [name, push] of Object.entries(result.push ?? {}))
549
+ pushes[name] = { tables: push.tables };
550
+ return { streams, pushes };
551
+ }
552
+ function topLevelSecrets(config) {
553
+ if (!isRecord(config) || !isRecord(config.shape))
554
+ return [];
555
+ const secrets = [];
556
+ for (const [field, type] of Object.entries(config.shape)) {
557
+ if (isRecord(type) && type.kind === "secret")
558
+ secrets.push({ field, exposeToScript: type.exposeToScript === true });
559
+ }
560
+ return secrets;
561
+ }
562
+ function summarizeDefinition(definition) {
563
+ const secrets = topLevelSecrets(definition.config);
564
+ const rateLimit = definition.rateLimit;
565
+ return {
566
+ name: definition.name,
567
+ version: definition.version,
568
+ hosts: Array.isArray(definition.hosts) ? definition.hosts.filter((host) => typeof host === "string") : [],
569
+ db: definition.db?.engine === "postgresql" ? { engine: "postgresql" } : null,
570
+ rateLimit: rateLimit ? { perSecond: rateLimit.perSecond, concurrency: rateLimit.concurrency } : null,
571
+ pushes: pushNames(definition.push),
572
+ streams: definitionStreamNames(definition),
573
+ secretFields: secrets.map((secret) => secret.field),
574
+ exposeToScript: secrets.filter((secret) => secret.exposeToScript).map((secret) => secret.field),
575
+ ...isRecord(definition.config) ? { config: JSON.parse(JSON.stringify(definition.config)) } : {}
576
+ };
577
+ }
578
+ function isValidDate(text) {
579
+ if (!DATE_PATTERN.test(text))
580
+ return false;
581
+ const date = new Date(`${text}T00:00:00Z`);
582
+ return !Number.isNaN(date.getTime()) && date.toISOString().slice(0, 10) === text;
583
+ }
584
+ function validateValue(path, type, value, issues) {
585
+ if (!isRecord(type) || typeof type.kind !== "string") {
586
+ issues.push({ path, message: "field type is not a valid t.*() declaration" });
587
+ return;
588
+ }
589
+ const fail = (expected) => issues.push({ path, message: `${path} must be ${expected}` });
590
+ switch (type.kind) {
591
+ case "string":
592
+ if (typeof value !== "string")
593
+ fail("a string");
594
+ return;
595
+ case "integer":
596
+ if (typeof value !== "number" || !Number.isSafeInteger(value))
597
+ fail("an integer");
598
+ return;
599
+ case "decimal":
600
+ if (!(typeof value === "number" && Number.isFinite(value)) && !(typeof value === "string" && DECIMAL_TEXT_PATTERN.test(value)))
601
+ fail("a decimal number");
602
+ return;
603
+ case "boolean":
604
+ if (typeof value !== "boolean")
605
+ fail("a boolean");
606
+ return;
607
+ case "date":
608
+ if (typeof value !== "string" || !isValidDate(value))
609
+ fail("a date like 2026-01-31");
610
+ return;
611
+ case "instant":
612
+ if (typeof value !== "string" || !INSTANT_PATTERN.test(value) || Number.isNaN(Date.parse(value)))
613
+ fail("an ISO 8601 timestamp with time zone");
614
+ return;
615
+ case "json":
616
+ return;
617
+ case "object":
618
+ if (!isRecord(value)) {
619
+ fail("an object");
620
+ return;
621
+ }
622
+ validateFields(path, type, value, issues, false);
623
+ return;
624
+ case "array":
625
+ if (!Array.isArray(value)) {
626
+ fail("an array");
627
+ return;
628
+ }
629
+ value.forEach((item, i) => {
630
+ if (item === undefined || item === null)
631
+ issues.push({ path: `${path}[${i}]`, message: `${path}[${i}] is required` });
632
+ else
633
+ validateValue(`${path}[${i}]`, type.item, item, issues);
634
+ });
635
+ return;
636
+ case "secret":
637
+ issues.push({ path, message: "secret fields must be top-level config fields" });
638
+ return;
639
+ default:
640
+ issues.push({ path, message: `unknown type kind ${type.kind}` });
641
+ }
642
+ }
643
+ function validateFields(path, type, value, issues, topLevel) {
644
+ const shape = isRecord(type.shape) ? type.shape : {};
645
+ for (const [field, fieldType] of Object.entries(shape)) {
646
+ const fieldPath = `${path}.${field}`;
647
+ if (topLevel && isRecord(fieldType) && fieldType.kind === "secret")
648
+ continue;
649
+ const fieldValue = value[field];
650
+ if (fieldValue === undefined || fieldValue === null) {
651
+ if (!(isRecord(fieldType) && fieldType.kind === "json" && fieldValue === null)) {
652
+ issues.push({ path: fieldPath, message: `${fieldPath} is required` });
653
+ }
654
+ continue;
655
+ }
656
+ validateValue(fieldPath, fieldType, fieldValue, issues);
657
+ }
658
+ for (const field of Object.keys(value)) {
659
+ if (!Object.hasOwn(shape, field))
660
+ issues.push({ path: `${path}.${field}`, message: `${path}.${field} is not declared` });
661
+ }
662
+ }
663
+ function validateConfig(schema, config) {
664
+ if (!isRecord(config))
665
+ return [{ path: "config", message: "config must be an object" }];
666
+ const issues = [];
667
+ validateFields("config", schema, config, issues, true);
668
+ return issues;
669
+ }
670
+
671
+ // src/errors.ts
672
+ class ConnectorError extends Error {
673
+ code;
674
+ constructor(code, message) {
675
+ super(message);
676
+ this.name = "ConnectorError";
677
+ this.code = code;
678
+ }
679
+ }
680
+
681
+ class AuthError extends ConnectorError {
682
+ constructor(message) {
683
+ super("AUTH_ERROR", message);
684
+ this.name = "AuthError";
685
+ }
686
+ }
687
+
688
+ class RateLimited extends ConnectorError {
689
+ retryAfterMs;
690
+ constructor(retryAfterMs, message) {
691
+ super("RATE_LIMITED", message ?? `rate limited, retry after ${retryAfterMs} ms`);
692
+ this.name = "RateLimited";
693
+ this.retryAfterMs = retryAfterMs;
694
+ }
695
+ }
696
+
697
+ class TransientError extends ConnectorError {
698
+ constructor(message) {
699
+ super("TRANSIENT_ERROR", message);
700
+ this.name = "TransientError";
701
+ }
702
+ }
703
+
704
+ class SchemaError extends ConnectorError {
705
+ constructor(message) {
706
+ super("SCHEMA_ERROR", message);
707
+ this.name = "SchemaError";
708
+ }
709
+ }
710
+
711
+ class FatalError extends ConnectorError {
712
+ constructor(message) {
713
+ super("FATAL_ERROR", message);
714
+ this.name = "FatalError";
715
+ }
716
+ }
717
+
718
+ class RejectedPayload extends ConnectorError {
719
+ constructor(message) {
720
+ super("REJECTED_PAYLOAD", message);
721
+ this.name = "RejectedPayload";
722
+ }
723
+ }
724
+
725
+ class NotAvailable extends ConnectorError {
726
+ constructor(feature) {
727
+ super("NOT_AVAILABLE", `${feature} is not available yet`);
728
+ this.name = "NotAvailable";
729
+ }
730
+ }
731
+ var errors = Object.freeze({ AuthError, RateLimited, TransientError, SchemaError, FatalError, RejectedPayload, NotAvailable });
732
+
733
+ // src/runtime/guest/bridge.ts
734
+ var jsonParse = JSON.parse;
735
+ var jsonStringify = JSON.stringify;
736
+ var dateNow = Date.now;
737
+ var reflectApply = Reflect.apply;
738
+ var mathMin = Math.min;
739
+ var MapCtor = Map;
740
+ class HostCallFailure extends ConnectorError {
741
+ status;
742
+ retryAfterMs;
743
+ constructor(code, message, status, retryAfterMs) {
744
+ super(code, message);
745
+ this.name = "HostCallFailure";
746
+ this.status = status;
747
+ this.retryAfterMs = retryAfterMs;
748
+ }
749
+ }
750
+
751
+ class CanceledError extends ConnectorError {
752
+ constructor() {
753
+ super("CANCELED", "run was canceled");
754
+ this.name = "CanceledError";
755
+ }
756
+ }
757
+ var nextCallId = 1;
758
+ var pending = new MapCtor;
759
+ var timers = [];
760
+ var outbox = [];
761
+ var finished = false;
762
+ var aborted = false;
763
+ var declared = false;
764
+ var stopped = false;
765
+ var abortListeners = [];
766
+ function isRecord2(value) {
767
+ return typeof value === "object" && value !== null && !Array.isArray(value);
768
+ }
769
+ function now() {
770
+ return reflectApply(dateNow, Date, []);
771
+ }
772
+ function enqueue(message) {
773
+ const text = jsonStringify(message);
774
+ if (typeof text !== "string")
775
+ throw new TypeError("message is not serializable");
776
+ outbox[outbox.length] = text;
777
+ }
778
+ function call(method, payload) {
779
+ if (method === "declaration") {
780
+ if (declared)
781
+ return Promise.reject(new ConnectorError("FATAL_ERROR", "declaration was already reported in this run"));
782
+ declared = true;
783
+ }
784
+ const id = nextCallId;
785
+ nextCallId += 1;
786
+ return new Promise((resolve, reject) => {
787
+ try {
788
+ enqueue({ type: "call", id, method, payload });
789
+ } catch (error) {
790
+ reject(new TypeError(`${method} payload is not serializable: ${error instanceof Error ? error.message : String(error)}`));
791
+ return;
792
+ }
793
+ pending.set(id, { resolve, reject });
794
+ });
795
+ }
796
+ function sleep(ms) {
797
+ if (aborted || !(ms > 0))
798
+ return Promise.resolve();
799
+ return new Promise((resolve) => {
800
+ timers[timers.length] = { at: now() + ms, resolve };
801
+ });
802
+ }
803
+ function isAborted() {
804
+ return aborted;
805
+ }
806
+ function isStopped() {
807
+ return stopped;
808
+ }
809
+ function requestStop() {
810
+ if (aborted)
811
+ return;
812
+ stopped = true;
813
+ abort();
814
+ }
815
+ function throwIfAborted() {
816
+ if (aborted)
817
+ throw new CanceledError;
818
+ }
819
+ function onAbort(listener) {
820
+ if (aborted) {
821
+ Promise.resolve().then(() => runListener(listener));
822
+ return;
823
+ }
824
+ abortListeners[abortListeners.length] = listener;
825
+ }
826
+ function runListener(listener) {
827
+ try {
828
+ listener();
829
+ } catch {}
830
+ }
831
+ function abort() {
832
+ if (aborted)
833
+ return;
834
+ aborted = true;
835
+ const due = timers;
836
+ timers = [];
837
+ for (const timer of due)
838
+ timer.resolve();
839
+ for (const [id, entry] of pending) {
840
+ pending.delete(id);
841
+ entry.reject(new CanceledError);
842
+ }
843
+ const listeners = abortListeners.splice(0, abortListeners.length);
844
+ for (const listener of listeners)
845
+ runListener(listener);
846
+ }
847
+ function finish(message) {
848
+ if (finished)
849
+ return;
850
+ finished = true;
851
+ try {
852
+ enqueue(message);
853
+ } catch (error) {
854
+ outbox[outbox.length] = jsonStringify({
855
+ type: "failure",
856
+ code: "FATAL_ERROR",
857
+ logs: [],
858
+ message: `connector result is not serializable: ${error instanceof Error ? error.message : String(error)}`
859
+ });
860
+ }
861
+ }
862
+ function handle(json) {
863
+ if (typeof json !== "string")
864
+ return;
865
+ let message;
866
+ try {
867
+ message = jsonParse(json);
868
+ } catch {
869
+ return;
870
+ }
871
+ if (!isRecord2(message))
872
+ return;
873
+ if (message.type === "cancel") {
874
+ abort();
875
+ return;
876
+ }
877
+ if (message.type !== "reply" || typeof message.id !== "number")
878
+ return;
879
+ const entry = pending.get(message.id);
880
+ if (!entry)
881
+ return;
882
+ pending.delete(message.id);
883
+ if (message.ok === true) {
884
+ entry.resolve(message.value);
885
+ return;
886
+ }
887
+ const error = isRecord2(message.error) ? message.error : {};
888
+ entry.reject(new HostCallFailure(typeof error.code === "string" ? error.code : "HOST_ERROR", typeof error.message === "string" ? error.message : "host call failed", typeof error.status === "number" ? error.status : undefined, typeof error.retryAfterMs === "number" ? error.retryAfterMs : undefined));
889
+ }
890
+ function poll() {
891
+ const current = now();
892
+ let fired = false;
893
+ if (timers.length > 0) {
894
+ const due = [];
895
+ const rest = [];
896
+ for (const timer of timers) {
897
+ if (timer.at <= current)
898
+ due[due.length] = timer;
899
+ else
900
+ rest[rest.length] = timer;
901
+ }
902
+ timers = rest;
903
+ for (const timer of due)
904
+ timer.resolve();
905
+ fired = due.length > 0;
906
+ }
907
+ let wake = -1;
908
+ for (const timer of timers)
909
+ wake = wake < 0 ? timer.at - current : mathMin(wake, timer.at - current);
910
+ const out = outbox;
911
+ outbox = [];
912
+ return `{"out":[${out.join(",")}],"wake":${wake < 0 ? -1 : wake},"fired":${fired ? "true" : "false"}}`;
913
+ }
914
+
915
+ // src/runtime/guest/cache.ts
916
+ function key(value) {
917
+ if (typeof value !== "string" || value.length === 0)
918
+ throw new TypeError("cache key must be a non-empty string");
919
+ return value;
920
+ }
921
+ function ttl(value) {
922
+ if (typeof value !== "number" || !Number.isInteger(value) || value < 1)
923
+ throw new TypeError("cache ttlSeconds must be a positive integer");
924
+ return value;
925
+ }
926
+ async function get(name) {
927
+ const entry = await call("cache.get", { key: key(name) });
928
+ if (entry === null || typeof entry !== "object" || typeof entry.value !== "string")
929
+ return;
930
+ const expiresAt = typeof entry.expiresAt === "string" ? Date.parse(entry.expiresAt) : Number.NaN;
931
+ if (!Number.isNaN(expiresAt) && expiresAt <= now())
932
+ return;
933
+ return entry.value;
934
+ }
935
+ async function set(name, value, ttlSeconds) {
936
+ if (typeof value !== "string")
937
+ throw new TypeError("cache value must be a string");
938
+ await call("cache.set", { key: key(name), value, ttlSeconds: ttl(ttlSeconds) });
939
+ }
940
+ function createCache() {
941
+ return Object.freeze({
942
+ get: (name) => Promise.resolve().then(() => get(name)),
943
+ set: (name, value, ttlSeconds) => Promise.resolve().then(() => set(name, value, ttlSeconds)),
944
+ async getOrSet(name, ttlSeconds, fn) {
945
+ ttl(ttlSeconds);
946
+ if (typeof fn !== "function")
947
+ throw new TypeError("cache.getOrSet expects a function");
948
+ const cached = await get(name);
949
+ if (cached !== undefined)
950
+ return cached;
951
+ const value = await fn();
952
+ await set(name, value, ttlSeconds);
953
+ return value;
954
+ }
955
+ });
956
+ }
957
+
958
+ // src/runtime/guest/crypto.ts
959
+ var HMAC_ALGORITHMS = ["sha1", "sha256", "sha512"];
960
+ var HASH_ALGORITHMS = ["md5", "sha1", "sha256", "sha512"];
961
+ var ENCODINGS = ["hex", "base64"];
962
+ var ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
963
+ function encoding(value) {
964
+ if (value === undefined)
965
+ return "hex";
966
+ if (typeof value !== "string" || !ENCODINGS.includes(value))
967
+ throw new TypeError("encoding must be 'hex' or 'base64'");
968
+ return value;
969
+ }
970
+ function text(value, what) {
971
+ if (typeof value !== "string")
972
+ throw new TypeError(`${what} must be a string`);
973
+ return value;
974
+ }
975
+ async function digest(request) {
976
+ const response = await call("crypto", request);
977
+ if (!response || typeof response.value !== "string")
978
+ throw new TypeError("crypto host call returned no value");
979
+ return response.value;
980
+ }
981
+ function utf8Encode(input) {
982
+ const bytes = [];
983
+ for (let i = 0;i < input.length; i += 1) {
984
+ let code = input.charCodeAt(i);
985
+ if (code >= 55296 && code <= 56319 && i + 1 < input.length) {
986
+ const low = input.charCodeAt(i + 1);
987
+ if (low >= 56320 && low <= 57343) {
988
+ code = 65536 + (code - 55296 << 10) + (low - 56320);
989
+ i += 1;
990
+ }
991
+ }
992
+ if (code >= 55296 && code <= 57343)
993
+ code = 65533;
994
+ if (code < 128)
995
+ bytes.push(code);
996
+ else if (code < 2048)
997
+ bytes.push(192 | code >> 6, 128 | code & 63);
998
+ else if (code < 65536)
999
+ bytes.push(224 | code >> 12, 128 | code >> 6 & 63, 128 | code & 63);
1000
+ else
1001
+ bytes.push(240 | code >> 18, 128 | code >> 12 & 63, 128 | code >> 6 & 63, 128 | code & 63);
1002
+ }
1003
+ return bytes;
1004
+ }
1005
+ function utf8Length(input) {
1006
+ let length = 0;
1007
+ for (let i = 0;i < input.length; i += 1) {
1008
+ const code = input.charCodeAt(i);
1009
+ if (code < 128)
1010
+ length += 1;
1011
+ else if (code < 2048)
1012
+ length += 2;
1013
+ else if (code >= 55296 && code <= 56319 && i + 1 < input.length) {
1014
+ const low = input.charCodeAt(i + 1);
1015
+ if (low >= 56320 && low <= 57343) {
1016
+ length += 4;
1017
+ i += 1;
1018
+ } else {
1019
+ length += 3;
1020
+ }
1021
+ } else
1022
+ length += 3;
1023
+ }
1024
+ return length;
1025
+ }
1026
+ function utf8Decode(bytes) {
1027
+ let out = "";
1028
+ let i = 0;
1029
+ while (i < bytes.length) {
1030
+ const b0 = bytes[i] ?? 0;
1031
+ let code = 65533;
1032
+ let size = 1;
1033
+ const cont = (offset) => {
1034
+ const b = bytes[i + offset];
1035
+ return b !== undefined && (b & 192) === 128 ? b & 63 : -1;
1036
+ };
1037
+ if (b0 < 128) {
1038
+ code = b0;
1039
+ } else if (b0 >= 194 && b0 < 224) {
1040
+ const b1 = cont(1);
1041
+ if (b1 >= 0) {
1042
+ code = (b0 & 31) << 6 | b1;
1043
+ size = 2;
1044
+ }
1045
+ } else if (b0 >= 224 && b0 < 240) {
1046
+ const b1 = cont(1);
1047
+ const b2 = cont(2);
1048
+ if (b1 >= 0 && b2 >= 0) {
1049
+ const value = (b0 & 15) << 12 | b1 << 6 | b2;
1050
+ if (value >= 2048 && (value < 55296 || value > 57343)) {
1051
+ code = value;
1052
+ size = 3;
1053
+ }
1054
+ }
1055
+ } else if (b0 >= 240 && b0 < 245) {
1056
+ const b1 = cont(1);
1057
+ const b2 = cont(2);
1058
+ const b3 = cont(3);
1059
+ if (b1 >= 0 && b2 >= 0 && b3 >= 0) {
1060
+ const value = (b0 & 7) << 18 | b1 << 12 | b2 << 6 | b3;
1061
+ if (value >= 65536 && value <= 1114111) {
1062
+ code = value;
1063
+ size = 4;
1064
+ }
1065
+ }
1066
+ }
1067
+ out += String.fromCodePoint(code);
1068
+ i += size;
1069
+ }
1070
+ return out;
1071
+ }
1072
+ function bytesToBase64(bytes) {
1073
+ let out = "";
1074
+ for (let i = 0;i < bytes.length; i += 3) {
1075
+ const b0 = bytes[i] ?? 0;
1076
+ const b1 = bytes[i + 1];
1077
+ const b2 = bytes[i + 2];
1078
+ const chunk = b0 << 16 | (b1 ?? 0) << 8 | (b2 ?? 0);
1079
+ out += ALPHABET.charAt(chunk >> 18 & 63) + ALPHABET.charAt(chunk >> 12 & 63);
1080
+ out += b1 === undefined ? "=" : ALPHABET.charAt(chunk >> 6 & 63);
1081
+ out += b2 === undefined ? "=" : ALPHABET.charAt(chunk & 63);
1082
+ }
1083
+ return out;
1084
+ }
1085
+ function base64ToBytes(input) {
1086
+ const clean = input.replace(/[\s=]/g, "").replace(/-/g, "+").replace(/_/g, "/");
1087
+ const bytes = [];
1088
+ let buffer = 0;
1089
+ let bits = 0;
1090
+ for (let i = 0;i < clean.length; i += 1) {
1091
+ const value = ALPHABET.indexOf(clean.charAt(i));
1092
+ if (value < 0)
1093
+ throw new TypeError("invalid base64 text");
1094
+ buffer = buffer << 6 | value;
1095
+ bits += 6;
1096
+ if (bits >= 8) {
1097
+ bits -= 8;
1098
+ bytes.push(buffer >> bits & 255);
1099
+ }
1100
+ }
1101
+ return bytes;
1102
+ }
1103
+ function base64Encode(input) {
1104
+ return bytesToBase64(utf8Encode(text(input, "base64Encode input")));
1105
+ }
1106
+ function base64Decode(input) {
1107
+ return utf8Decode(base64ToBytes(text(input, "base64Decode input")));
1108
+ }
1109
+ function createCrypto() {
1110
+ return Object.freeze({
1111
+ hmac(algorithm, key, data, enc) {
1112
+ if (!HMAC_ALGORITHMS.includes(algorithm))
1113
+ return Promise.reject(new TypeError(`unsupported hmac algorithm ${String(algorithm)}`));
1114
+ return Promise.resolve().then(() => digest({ op: "hmac", algorithm, key: text(key, "hmac key"), data: text(data, "hmac data"), encoding: encoding(enc) }));
1115
+ },
1116
+ sign(algorithm, key, data, enc) {
1117
+ if (algorithm !== "rsa-sha256")
1118
+ return Promise.reject(new TypeError(`unsupported sign algorithm ${String(algorithm)}`));
1119
+ return Promise.resolve().then(() => digest({ op: "sign", algorithm, key: text(key, "sign key"), data: text(data, "sign data"), encoding: encoding(enc) }));
1120
+ },
1121
+ hash(algorithm, data, enc) {
1122
+ if (!HASH_ALGORITHMS.includes(algorithm))
1123
+ return Promise.reject(new TypeError(`unsupported hash algorithm ${String(algorithm)}`));
1124
+ return Promise.resolve().then(() => digest({ op: "hash", algorithm, data: text(data, "hash data"), encoding: encoding(enc) }));
1125
+ },
1126
+ base64Encode,
1127
+ base64Decode
1128
+ });
1129
+ }
1130
+
1131
+ // src/runtime/guest/db.ts
1132
+ function isRecord3(value) {
1133
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1134
+ }
1135
+ function param(value) {
1136
+ if (value === undefined)
1137
+ return null;
1138
+ if (typeof value === "bigint")
1139
+ return value.toString();
1140
+ if (value instanceof Date)
1141
+ return value.toISOString();
1142
+ return value;
1143
+ }
1144
+ function rowsOf(value) {
1145
+ if (!isRecord3(value) || !Array.isArray(value.rows) || typeof value.done !== "boolean")
1146
+ throw new TypeError("db host call returned a malformed response");
1147
+ return value.rows;
1148
+ }
1149
+ async function* run(sql, params) {
1150
+ throwIfAborted();
1151
+ const first = await call("db.query", { sql, params: params.map(param) });
1152
+ let rows = rowsOf(first);
1153
+ let done = first.done;
1154
+ const cursorId = typeof first.cursorId === "string" ? first.cursorId : null;
1155
+ if (!done && cursorId === null)
1156
+ throw new TypeError("db host call returned more rows without a cursor");
1157
+ try {
1158
+ for (;; ) {
1159
+ for (const row of rows)
1160
+ yield row;
1161
+ if (done || cursorId === null)
1162
+ return;
1163
+ throwIfAborted();
1164
+ const next = await call("db.next", { cursorId });
1165
+ rows = rowsOf(next);
1166
+ done = next.done;
1167
+ }
1168
+ } finally {
1169
+ if (!done && cursorId !== null) {
1170
+ const closing = call("db.close", { cursorId });
1171
+ if (isAborted())
1172
+ closing.catch(() => {
1173
+ return;
1174
+ });
1175
+ else
1176
+ await closing;
1177
+ }
1178
+ }
1179
+ }
1180
+ function createDb() {
1181
+ return Object.freeze({
1182
+ query(sql, params = []) {
1183
+ if (typeof sql !== "string" || sql.trim().length === 0)
1184
+ throw new TypeError("ctx.db.query expects a SQL string");
1185
+ if (!Array.isArray(params))
1186
+ throw new TypeError("ctx.db.query params must be an array");
1187
+ return run(sql, [...params]);
1188
+ }
1189
+ });
1190
+ }
1191
+
1192
+ // src/runtime/guest/limits.ts
1193
+ var PULL_BATCH_MAX_RECORDS = 1000;
1194
+ var PULL_BATCH_MAX_BYTES = 4 * 1024 * 1024;
1195
+ var PULL_REJECT_THRESHOLD = 1000;
1196
+ var MAX_LOGS = 1000;
1197
+ var MAX_LOG_MESSAGE = 4096;
1198
+ var DEFAULT_CONCURRENCY = 4;
1199
+ var RATE_LIMIT_RETRIES = 5;
1200
+ var TRANSIENT_RETRIES = 3;
1201
+ var TRANSIENT_BACKOFF_MS = 200;
1202
+ var RATE_LIMIT_BACKOFF_MS = 1000;
1203
+ var MAX_RETRY_WAIT_MS = 600000;
1204
+ var CONNECTOR_FILENAME = "connector.js";
1205
+
1206
+ // src/runtime/guest/http.ts
1207
+ var METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"];
1208
+ var ABSOLUTE_URL = /^https?:\/\//i;
1209
+ var TRANSIENT_STATUS = [502, 503, 504];
1210
+ var IDEMPOTENT_METHODS = ["GET", "HEAD", "PUT", "DELETE"];
1211
+ var JSON_CONTENT_TYPE = /(?:^|[/+])json\s*(?:;|$)/i;
1212
+ function isRecord4(value) {
1213
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1214
+ }
1215
+ function stringify(value) {
1216
+ if (typeof value === "string")
1217
+ return value;
1218
+ if (value instanceof Date)
1219
+ return value.toISOString();
1220
+ if (typeof value === "object" && value !== null)
1221
+ return jsonStringify(value);
1222
+ return String(value);
1223
+ }
1224
+ function resolveUrl(url, baseUrl) {
1225
+ if (ABSOLUTE_URL.test(url))
1226
+ return url;
1227
+ if (!baseUrl)
1228
+ throw new TypeError(`relative URL ${url} needs auth.baseUrl or a fixed host in the connector's hosts`);
1229
+ const base = baseUrl.replace(/\/+$/, "");
1230
+ return `${base}${url.startsWith("/") ? "" : "/"}${url}`;
1231
+ }
1232
+ function buildHeaders(options) {
1233
+ const headers = {};
1234
+ for (const source of [options.auth?.headers, options.headers]) {
1235
+ if (source === undefined)
1236
+ continue;
1237
+ if (!isRecord4(source))
1238
+ throw new TypeError("headers must be an object");
1239
+ for (const [name, value] of Object.entries(source)) {
1240
+ if (value === undefined || value === null)
1241
+ continue;
1242
+ headers[name.toLowerCase()] = stringify(value);
1243
+ }
1244
+ }
1245
+ return headers;
1246
+ }
1247
+ function buildQuery(query) {
1248
+ const out = {};
1249
+ if (query === undefined)
1250
+ return out;
1251
+ if (!isRecord4(query))
1252
+ throw new TypeError("query must be an object");
1253
+ for (const [name, value] of Object.entries(query)) {
1254
+ if (value === undefined || value === null)
1255
+ continue;
1256
+ out[name] = Array.isArray(value) ? value.filter((item) => item !== undefined && item !== null).map(stringify) : stringify(value);
1257
+ }
1258
+ return out;
1259
+ }
1260
+ function buildBody(body, headers) {
1261
+ if (body === undefined || body === null)
1262
+ return { body: null, bodyEncoding: "utf8" };
1263
+ if (typeof body === "string")
1264
+ return { body, bodyEncoding: "utf8" };
1265
+ if (body instanceof Uint8Array)
1266
+ return { body: bytesToBase64(Array.from(body)), bodyEncoding: "base64" };
1267
+ if (body instanceof ArrayBuffer)
1268
+ return { body: bytesToBase64(Array.from(new Uint8Array(body))), bodyEncoding: "base64" };
1269
+ const text = jsonStringify(body);
1270
+ if (typeof text !== "string")
1271
+ throw new TypeError("request body is not serializable");
1272
+ if (headers["content-type"] === undefined)
1273
+ headers["content-type"] = "application/json";
1274
+ return { body: text, bodyEncoding: "utf8" };
1275
+ }
1276
+ function buildRequest(method, url, options, defaultBaseUrl) {
1277
+ const upper = typeof method === "string" ? method.toUpperCase() : "";
1278
+ if (!METHODS.includes(upper))
1279
+ throw new TypeError(`unsupported HTTP method ${String(method)}`);
1280
+ if (typeof url !== "string" || url.length === 0)
1281
+ throw new TypeError("url must be a non-empty string");
1282
+ if (!isRecord4(options))
1283
+ throw new TypeError("options must be an object");
1284
+ const baseUrl = options.auth?.baseUrl ?? defaultBaseUrl;
1285
+ const headers = buildHeaders(options);
1286
+ const request = {
1287
+ method: upper,
1288
+ url: resolveUrl(url, typeof baseUrl === "string" ? baseUrl : null),
1289
+ headers,
1290
+ query: buildQuery(options.query),
1291
+ ...buildBody(options.body, headers)
1292
+ };
1293
+ if (options.retry !== undefined && typeof options.retry !== "boolean")
1294
+ throw new TypeError("retry must be a boolean");
1295
+ if (options.timeoutMs !== undefined) {
1296
+ if (typeof options.timeoutMs !== "number" || !(options.timeoutMs > 0))
1297
+ throw new TypeError("timeoutMs must be a positive number");
1298
+ request.timeoutMs = options.timeoutMs;
1299
+ }
1300
+ return request;
1301
+ }
1302
+ function toResponse(value, parseJson) {
1303
+ if (!isRecord4(value) || typeof value.status !== "number" || typeof value.body !== "string")
1304
+ throw new TypeError("http host call returned a malformed response");
1305
+ const raw = value;
1306
+ const headers = {};
1307
+ if (isRecord4(raw.headers)) {
1308
+ for (const [name, item] of Object.entries(raw.headers))
1309
+ if (typeof item === "string")
1310
+ headers[name.toLowerCase()] = item;
1311
+ }
1312
+ const text = raw.body;
1313
+ const url = typeof raw.url === "string" ? raw.url : "";
1314
+ let data = text;
1315
+ if (parseJson && raw.bodyEncoding !== "base64" && JSON_CONTENT_TYPE.test(headers["content-type"] ?? "") && text.length > 0) {
1316
+ try {
1317
+ data = jsonParse(text);
1318
+ } catch {
1319
+ throw new errors.FatalError(`response from ${url} is not valid JSON`);
1320
+ }
1321
+ }
1322
+ return { status: raw.status, url, headers, data, text };
1323
+ }
1324
+ function retryAfterMs(headers) {
1325
+ const value = headers["retry-after"];
1326
+ if (value === undefined)
1327
+ return;
1328
+ const trimmed = value.trim();
1329
+ if (/^\d+(?:\.\d+)?$/.test(trimmed))
1330
+ return Number(trimmed) * 1000;
1331
+ const at = Date.parse(trimmed);
1332
+ return Number.isNaN(at) ? undefined : Math.max(0, at - now());
1333
+ }
1334
+ function clampWait(ms) {
1335
+ return Math.min(Math.max(0, ms), MAX_RETRY_WAIT_MS);
1336
+ }
1337
+ function createHttp(defaultBaseUrl) {
1338
+ async function request(method, url, options = {}) {
1339
+ const req = buildRequest(method, url, options, defaultBaseUrl);
1340
+ const raw = options.raw === true;
1341
+ const label = `${req.method} ${req.url}`;
1342
+ const retryTransient = options.retry ?? IDEMPOTENT_METHODS.includes(req.method);
1343
+ const transientLimit = retryTransient ? TRANSIENT_RETRIES : 0;
1344
+ let rateRetries = 0;
1345
+ let transientRetries = 0;
1346
+ for (;; ) {
1347
+ throwIfAborted();
1348
+ let value;
1349
+ try {
1350
+ value = await call("http", req);
1351
+ } catch (error) {
1352
+ if (error instanceof HostCallFailure && error.code === "EGRESS_RATE_LIMITED") {
1353
+ const wait = error.retryAfterMs ?? RATE_LIMIT_BACKOFF_MS * 2 ** rateRetries;
1354
+ if (rateRetries >= RATE_LIMIT_RETRIES)
1355
+ throw new errors.RateLimited(wait, `${label} is still rate limited after ${RATE_LIMIT_RETRIES} retries`);
1356
+ rateRetries += 1;
1357
+ await sleep(clampWait(wait));
1358
+ continue;
1359
+ }
1360
+ if (error instanceof HostCallFailure && error.code === "EGRESS_TIMEOUT") {
1361
+ if (transientRetries >= transientLimit)
1362
+ throw new errors.TransientError(`${label} timed out after ${transientRetries} retries`);
1363
+ await sleep(TRANSIENT_BACKOFF_MS * 2 ** transientRetries);
1364
+ transientRetries += 1;
1365
+ continue;
1366
+ }
1367
+ throw error;
1368
+ }
1369
+ const response = toResponse(value, !raw);
1370
+ if (raw)
1371
+ return response;
1372
+ const status = response.status;
1373
+ if (status === 429) {
1374
+ const wait = retryAfterMs(response.headers) ?? RATE_LIMIT_BACKOFF_MS * 2 ** rateRetries;
1375
+ if (rateRetries >= RATE_LIMIT_RETRIES)
1376
+ throw new errors.RateLimited(wait, `${label} returned 429 after ${RATE_LIMIT_RETRIES} retries`);
1377
+ rateRetries += 1;
1378
+ await sleep(clampWait(wait));
1379
+ continue;
1380
+ }
1381
+ if (TRANSIENT_STATUS.includes(status)) {
1382
+ if (transientRetries >= transientLimit)
1383
+ throw new errors.TransientError(`${label} returned ${status} after ${transientRetries} retries`);
1384
+ await sleep(TRANSIENT_BACKOFF_MS * 2 ** transientRetries);
1385
+ transientRetries += 1;
1386
+ continue;
1387
+ }
1388
+ if (status === 401)
1389
+ throw new errors.AuthError(`${label} returned 401`);
1390
+ if (status >= 400)
1391
+ throw new errors.FatalError(`${label} returned ${status}`);
1392
+ return response;
1393
+ }
1394
+ }
1395
+ const verb = (method) => (url, options) => request(method, url, options);
1396
+ return Object.freeze({
1397
+ get: verb("GET"),
1398
+ post: verb("POST"),
1399
+ put: verb("PUT"),
1400
+ patch: verb("PATCH"),
1401
+ delete: verb("DELETE"),
1402
+ request
1403
+ });
1404
+ }
1405
+
1406
+ // src/runtime/guest/map.ts
1407
+ function positiveInteger(value, what) {
1408
+ if (typeof value !== "number" || !Number.isInteger(value) || value < 1)
1409
+ throw new TypeError(`${what} must be a positive integer`);
1410
+ return value;
1411
+ }
1412
+ function createMap(concurrency) {
1413
+ const cap = concurrency ?? DEFAULT_CONCURRENCY;
1414
+ return async (items, fn, options) => {
1415
+ if (!Array.isArray(items))
1416
+ throw new TypeError("ctx.map expects an array");
1417
+ if (typeof fn !== "function")
1418
+ throw new TypeError("ctx.map expects a function");
1419
+ const requested = options?.concurrency === undefined ? cap : positiveInteger(options.concurrency, "ctx.map concurrency");
1420
+ const limit = Math.min(requested, cap);
1421
+ const results = new Array(items.length);
1422
+ let next = 0;
1423
+ let failed = false;
1424
+ let firstError;
1425
+ const worker = async () => {
1426
+ while (!failed && next < items.length) {
1427
+ const index = next;
1428
+ next += 1;
1429
+ try {
1430
+ throwIfAborted();
1431
+ results[index] = await fn(items[index]);
1432
+ } catch (error) {
1433
+ if (!failed) {
1434
+ failed = true;
1435
+ firstError = error;
1436
+ }
1437
+ }
1438
+ }
1439
+ };
1440
+ const workers = [];
1441
+ for (let i = 0;i < Math.min(limit, items.length); i += 1)
1442
+ workers.push(worker());
1443
+ await Promise.all(workers);
1444
+ if (failed)
1445
+ throw firstError;
1446
+ return results;
1447
+ };
1448
+ }
1449
+
1450
+ // src/runtime/guest/paginate.ts
1451
+ function compare(a, b) {
1452
+ if (typeof a === "number" && typeof b === "number")
1453
+ return a - b;
1454
+ if (typeof a === "bigint" && typeof b === "bigint")
1455
+ return a < b ? -1 : a > b ? 1 : 0;
1456
+ if (a instanceof Date && b instanceof Date)
1457
+ return a.getTime() - b.getTime();
1458
+ const left = String(a);
1459
+ const right = String(b);
1460
+ return left < right ? -1 : left > right ? 1 : 0;
1461
+ }
1462
+ function makePage(rows) {
1463
+ return Object.freeze({
1464
+ rows,
1465
+ maxOf(fn) {
1466
+ let max;
1467
+ for (const row of rows) {
1468
+ const value = fn(row);
1469
+ if (value === undefined || value === null)
1470
+ continue;
1471
+ if (max === undefined || compare(value, max) > 0)
1472
+ max = value;
1473
+ }
1474
+ return max;
1475
+ }
1476
+ });
1477
+ }
1478
+ function rowsOf2(value, what) {
1479
+ if (!Array.isArray(value))
1480
+ throw new TypeError(`${what} must return an array of rows`);
1481
+ return value;
1482
+ }
1483
+ function requireFunction(value, what) {
1484
+ if (typeof value !== "function")
1485
+ throw new TypeError(`${what} must be a function`);
1486
+ }
1487
+ async function* pageIterator(options) {
1488
+ requireFunction(options.request, "paginate.page request");
1489
+ requireFunction(options.rows, "paginate.page rows");
1490
+ const first = options.firstPage ?? 1;
1491
+ for (let page = first;; page += 1) {
1492
+ throwIfAborted();
1493
+ const res = await options.request(page);
1494
+ const rows = rowsOf2(options.rows(res), "paginate.page rows");
1495
+ if (rows.length === 0)
1496
+ return;
1497
+ yield makePage(rows);
1498
+ if (options.totalPages) {
1499
+ const total = options.totalPages(res);
1500
+ if (typeof total !== "number" || !Number.isFinite(total) || page - first + 1 >= total)
1501
+ return;
1502
+ }
1503
+ }
1504
+ }
1505
+ async function* offsetIterator(options) {
1506
+ requireFunction(options.request, "paginate.offset request");
1507
+ requireFunction(options.rows, "paginate.offset rows");
1508
+ const limit = options.limit;
1509
+ if (typeof limit !== "number" || !Number.isInteger(limit) || limit < 1)
1510
+ throw new TypeError("paginate.offset limit must be a positive integer");
1511
+ for (let offset = options.startOffset ?? 0;; offset += limit) {
1512
+ throwIfAborted();
1513
+ const res = await options.request(offset, limit);
1514
+ const rows = rowsOf2(options.rows(res), "paginate.offset rows");
1515
+ if (rows.length === 0)
1516
+ return;
1517
+ yield makePage(rows);
1518
+ if (options.total) {
1519
+ const total = options.total(res);
1520
+ if (typeof total !== "number" || !Number.isFinite(total) || offset + limit >= total)
1521
+ return;
1522
+ } else if (rows.length < limit) {
1523
+ return;
1524
+ }
1525
+ }
1526
+ }
1527
+ async function* cursorIterator(options) {
1528
+ requireFunction(options.request, "paginate.cursor request");
1529
+ requireFunction(options.rows, "paginate.cursor rows");
1530
+ requireFunction(options.nextCursor, "paginate.cursor nextCursor");
1531
+ let cursor;
1532
+ const seen = new Set;
1533
+ for (;; ) {
1534
+ throwIfAborted();
1535
+ const res = await options.request(cursor);
1536
+ const rows = rowsOf2(options.rows(res), "paginate.cursor rows");
1537
+ if (rows.length > 0)
1538
+ yield makePage(rows);
1539
+ const next = options.nextCursor(res);
1540
+ if (next === null || next === undefined || next === "")
1541
+ return;
1542
+ const text = String(next);
1543
+ if (seen.has(text))
1544
+ throw new TypeError(`paginate.cursor got the same cursor twice: ${text}`);
1545
+ seen.add(text);
1546
+ cursor = text;
1547
+ }
1548
+ }
1549
+ function nextLink(header) {
1550
+ if (!header)
1551
+ return null;
1552
+ for (const part of header.split(",")) {
1553
+ const match = /^\s*<([^>]*)>(.*)$/.exec(part);
1554
+ if (!match)
1555
+ continue;
1556
+ for (const param of (match[2] ?? "").split(";")) {
1557
+ const rel = /^\s*rel\s*=\s*"?([^"]*)"?\s*$/i.exec(param);
1558
+ if (rel && (rel[1] ?? "").toLowerCase().split(/\s+/).includes("next"))
1559
+ return match[1] ?? null;
1560
+ }
1561
+ }
1562
+ return null;
1563
+ }
1564
+ function resolveAgainst(link, current) {
1565
+ if (/^https?:\/\//i.test(link))
1566
+ return link;
1567
+ const origin = /^(https?:\/\/[^/?#]+)/i.exec(current)?.[1];
1568
+ if (!origin)
1569
+ return link;
1570
+ if (link.startsWith("//"))
1571
+ return `${current.slice(0, current.indexOf(":") + 1)}${link}`;
1572
+ if (link.startsWith("/"))
1573
+ return `${origin}${link}`;
1574
+ const path = current.slice(origin.length).replace(/[?#].*$/, "");
1575
+ const dir = path.slice(0, path.lastIndexOf("/") + 1) || "/";
1576
+ return `${origin}${dir}${link}`;
1577
+ }
1578
+ function linkIterator(http) {
1579
+ return async function* (options) {
1580
+ requireFunction(options.rows, "paginate.link rows");
1581
+ if (typeof options.url !== "string")
1582
+ throw new TypeError("paginate.link url must be a string");
1583
+ let url = options.url;
1584
+ const seen = new Set;
1585
+ while (url !== null) {
1586
+ throwIfAborted();
1587
+ seen.add(url);
1588
+ const res = await http.get(url, options.options);
1589
+ const rows = rowsOf2(options.rows(res), "paginate.link rows");
1590
+ if (rows.length > 0)
1591
+ yield makePage(rows);
1592
+ const next = options.next ? options.next(res) : nextLink(res.headers.link);
1593
+ if (next === null || next === undefined || next === "")
1594
+ return;
1595
+ const resolved = resolveAgainst(String(next), res.url || url);
1596
+ if (seen.has(resolved))
1597
+ throw new TypeError(`paginate.link got the same next link twice: ${resolved}`);
1598
+ url = resolved;
1599
+ }
1600
+ };
1601
+ }
1602
+ async function* keysetIterator(options) {
1603
+ requireFunction(options.query, "paginate.keyset query");
1604
+ requireFunction(options.key, "paginate.keyset key");
1605
+ let last;
1606
+ for (;; ) {
1607
+ throwIfAborted();
1608
+ const rows = [];
1609
+ for await (const row of options.query(last))
1610
+ rows.push(row);
1611
+ if (rows.length === 0)
1612
+ return;
1613
+ yield makePage(rows);
1614
+ const key = options.key(rows[rows.length - 1]);
1615
+ if (key === undefined || key === null)
1616
+ throw new TypeError("paginate.keyset key returned no value for the last row");
1617
+ last = key;
1618
+ }
1619
+ }
1620
+ function createPaginate(http) {
1621
+ return Object.freeze({
1622
+ page: pageIterator,
1623
+ offset: offsetIterator,
1624
+ cursor: cursorIterator,
1625
+ link: linkIterator(http),
1626
+ keyset: keysetIterator
1627
+ });
1628
+ }
1629
+
1630
+ // src/runtime/guest/signal.ts
1631
+ function createSignal() {
1632
+ const signal = {
1633
+ onAbort(fn) {
1634
+ if (typeof fn !== "function")
1635
+ throw new TypeError("ctx.signal.onAbort expects a function");
1636
+ onAbort(fn);
1637
+ }
1638
+ };
1639
+ Object.defineProperty(signal, "aborted", { get: isAborted, enumerable: true });
1640
+ return Object.freeze(signal);
1641
+ }
1642
+
1643
+ // src/runtime/guest/time.ts
1644
+ var DateCtor = Date;
1645
+ var dateUtc = Date.UTC;
1646
+ var dateParse = Date.parse;
1647
+ var reflectApply2 = Reflect.apply;
1648
+ var toISOString = Date.prototype.toISOString;
1649
+ var DateTimeFormat = Intl.DateTimeFormat;
1650
+ function isoNow() {
1651
+ return reflectApply2(toISOString, new DateCtor, []);
1652
+ }
1653
+ function iso(ms) {
1654
+ return reflectApply2(toISOString, new DateCtor(ms), []);
1655
+ }
1656
+ var DEFAULT_PATTERN = /^(\d{4})[-/](\d{1,2})[-/](\d{1,2})(?:[ T](\d{1,2}):(\d{2})(?::(\d{2})(?:\.(\d{1,9}))?)?)?$/;
1657
+ var ZONED_PATTERN = /^\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(?:Z|[+-]\d{2}:?\d{2})$/i;
1658
+ var TOKENS = [
1659
+ { token: "YYYY", field: "year", pattern: "(\\d{4})" },
1660
+ { token: "SSS", field: "millisecond", pattern: "(\\d{1,3})" },
1661
+ { token: "MM", field: "month", pattern: "(\\d{2})" },
1662
+ { token: "DD", field: "day", pattern: "(\\d{2})" },
1663
+ { token: "HH", field: "hour", pattern: "(\\d{2})" },
1664
+ { token: "mm", field: "minute", pattern: "(\\d{2})" },
1665
+ { token: "ss", field: "second", pattern: "(\\d{2})" },
1666
+ { token: "M", field: "month", pattern: "(\\d{1,2})" },
1667
+ { token: "D", field: "day", pattern: "(\\d{1,2})" },
1668
+ { token: "H", field: "hour", pattern: "(\\d{1,2})" }
1669
+ ];
1670
+ function escapeRegExp(text) {
1671
+ return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1672
+ }
1673
+ function parseWithFormat(local, format) {
1674
+ const fields = [];
1675
+ let source = "";
1676
+ let i = 0;
1677
+ while (i < format.length) {
1678
+ const match = TOKENS.find((entry) => format.startsWith(entry.token, i));
1679
+ if (match) {
1680
+ fields.push(match.field);
1681
+ source += match.pattern;
1682
+ i += match.token.length;
1683
+ } else {
1684
+ source += escapeRegExp(format.charAt(i));
1685
+ i += 1;
1686
+ }
1687
+ }
1688
+ const result = new RegExp(`^${source}$`).exec(local);
1689
+ if (!result || !fields.includes("year") || !fields.includes("month") || !fields.includes("day"))
1690
+ return null;
1691
+ const clock = { year: 0, month: 0, day: 0, hour: 0, minute: 0, second: 0, millisecond: 0 };
1692
+ fields.forEach((field, index) => {
1693
+ const text = result[index + 1] ?? "0";
1694
+ clock[field] = field === "millisecond" ? Number(text.padEnd(3, "0")) : Number(text);
1695
+ });
1696
+ return clock;
1697
+ }
1698
+ function parseDefault(local) {
1699
+ const match = DEFAULT_PATTERN.exec(local);
1700
+ if (!match)
1701
+ return null;
1702
+ return {
1703
+ year: Number(match[1]),
1704
+ month: Number(match[2]),
1705
+ day: Number(match[3]),
1706
+ hour: Number(match[4] ?? 0),
1707
+ minute: Number(match[5] ?? 0),
1708
+ second: Number(match[6] ?? 0),
1709
+ millisecond: Number((match[7] ?? "0").slice(0, 3).padEnd(3, "0"))
1710
+ };
1711
+ }
1712
+ function wallMillis(clock) {
1713
+ const ms = dateUtc(clock.year, clock.month - 1, clock.day, clock.hour, clock.minute, clock.second, clock.millisecond);
1714
+ if (Number.isNaN(ms))
1715
+ return null;
1716
+ const check = new DateCtor(ms);
1717
+ if (check.getUTCFullYear() !== clock.year || check.getUTCMonth() !== clock.month - 1 || check.getUTCDate() !== clock.day || check.getUTCHours() !== clock.hour || check.getUTCMinutes() !== clock.minute || check.getUTCSeconds() !== clock.second)
1718
+ return null;
1719
+ return ms;
1720
+ }
1721
+ var formatters = new Map;
1722
+ function formatter(timeZone) {
1723
+ let cached = formatters.get(timeZone);
1724
+ if (!cached) {
1725
+ try {
1726
+ cached = new DateTimeFormat("en-US", {
1727
+ timeZone,
1728
+ hourCycle: "h23",
1729
+ year: "numeric",
1730
+ month: "numeric",
1731
+ day: "numeric",
1732
+ hour: "numeric",
1733
+ minute: "numeric",
1734
+ second: "numeric"
1735
+ });
1736
+ } catch {
1737
+ throw new TypeError(`unknown time zone ${timeZone}`);
1738
+ }
1739
+ formatters.set(timeZone, cached);
1740
+ }
1741
+ return cached;
1742
+ }
1743
+ function zoneOffset(timeZone, utcMs) {
1744
+ const base = utcMs - (utcMs % 1000 + 1000) % 1000;
1745
+ const parts = {};
1746
+ for (const part of formatter(timeZone).formatToParts(new DateCtor(base))) {
1747
+ if (part.type !== "literal")
1748
+ parts[part.type] = Number(part.value);
1749
+ }
1750
+ const asUtc = dateUtc(parts.year ?? 0, (parts.month ?? 1) - 1, parts.day ?? 1, parts.hour ?? 0, parts.minute ?? 0, parts.second ?? 0);
1751
+ return asUtc - base;
1752
+ }
1753
+ function toUtc(local, timeZone, format) {
1754
+ if (typeof local !== "string")
1755
+ throw new TypeError("ctx.time.toUtc expects a date-time string");
1756
+ if (typeof timeZone !== "string" || timeZone.length === 0)
1757
+ throw new TypeError("ctx.time.toUtc expects a time zone like Asia/Shanghai");
1758
+ const text = local.trim();
1759
+ if (format === undefined && ZONED_PATTERN.test(text)) {
1760
+ const ms = dateParse(text.replace(" ", "T"));
1761
+ if (Number.isNaN(ms))
1762
+ throw new TypeError(`invalid date-time ${local}`);
1763
+ return iso(ms);
1764
+ }
1765
+ const clock = format === undefined ? parseDefault(text) : parseWithFormat(text, format);
1766
+ const wall = clock ? wallMillis(clock) : null;
1767
+ if (wall === null)
1768
+ throw new TypeError(`invalid date-time ${local}${format === undefined ? "" : ` for format ${format}`}`);
1769
+ const first = zoneOffset(timeZone, wall);
1770
+ let utc = wall - first;
1771
+ const second = zoneOffset(timeZone, utc);
1772
+ if (second !== first)
1773
+ utc = wall - second;
1774
+ return iso(utc);
1775
+ }
1776
+ function createTime() {
1777
+ return Object.freeze({ now: isoNow, toUtc });
1778
+ }
1779
+
1780
+ // src/runtime/guest/ctx.ts
1781
+ var SECRET_HANDLE_PREFIX = "ontos:secret:";
1782
+ var HOST_PLACEHOLDER2 = /\{config\.([^{}]*)\}/g;
1783
+ function isRecord5(value) {
1784
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1785
+ }
1786
+ function deepFreeze(value) {
1787
+ if (value === null || typeof value !== "object")
1788
+ return value;
1789
+ for (const key of Object.keys(value))
1790
+ deepFreeze(value[key]);
1791
+ return Object.freeze(value);
1792
+ }
1793
+ function configString(config, path) {
1794
+ let current = config;
1795
+ for (const segment of path.split(".")) {
1796
+ if (!isRecord5(current) || !Object.hasOwn(current, segment))
1797
+ return;
1798
+ current = current[segment];
1799
+ }
1800
+ return typeof current === "string" && current.length > 0 ? current : undefined;
1801
+ }
1802
+ function defaultBaseUrl(hosts, config) {
1803
+ for (const host of hosts) {
1804
+ let missing = false;
1805
+ const expanded = host.replace(HOST_PLACEHOLDER2, (_match, path) => {
1806
+ const value = configString(config, path);
1807
+ if (value === undefined)
1808
+ missing = true;
1809
+ return value ?? "";
1810
+ });
1811
+ if (!missing && !expanded.includes("*"))
1812
+ return `https://${expanded}`;
1813
+ }
1814
+ return null;
1815
+ }
1816
+ async function prepareSecrets(summary, secretFields) {
1817
+ const secrets = {};
1818
+ for (const field of secretFields) {
1819
+ if (!summary.secretFields.includes(field))
1820
+ continue;
1821
+ if (summary.exposeToScript.includes(field)) {
1822
+ const response = await call("secret.reveal", { field });
1823
+ if (!isRecord5(response) || typeof response.value !== "string")
1824
+ throw new TypeError(`secret.reveal returned no value for ${field}`);
1825
+ secrets[field] = response.value;
1826
+ } else {
1827
+ secrets[field] = `${SECRET_HANDLE_PREFIX}${field}`;
1828
+ }
1829
+ }
1830
+ return Object.freeze(secrets);
1831
+ }
1832
+ function concurrencyFromDefinitionReply(reply) {
1833
+ if (!isRecord5(reply) || !isRecord5(reply.rateLimit))
1834
+ return null;
1835
+ const concurrency = reply.rateLimit.concurrency;
1836
+ if (typeof concurrency !== "number" || !Number.isFinite(concurrency) || concurrency < 1)
1837
+ return null;
1838
+ return Math.floor(concurrency);
1839
+ }
1840
+ function mapConcurrency(options) {
1841
+ const declared = options.summary.rateLimit?.concurrency ?? DEFAULT_CONCURRENCY;
1842
+ const effective = options.effectiveConcurrency ?? null;
1843
+ return effective === null ? declared : Math.min(declared, effective);
1844
+ }
1845
+ function tableName(table) {
1846
+ if (typeof table !== "string" || table.length === 0)
1847
+ throw new TypeError("table must be a non-empty string");
1848
+ return table;
1849
+ }
1850
+ function plainObject(value, what) {
1851
+ if (!isRecord5(value))
1852
+ throw new TypeError(`${what} must be an object`);
1853
+ return value;
1854
+ }
1855
+ function createCheckContext(options) {
1856
+ const config = deepFreeze(options.config);
1857
+ const http = createHttp(defaultBaseUrl(options.summary.hosts, config));
1858
+ const ctx = {
1859
+ config,
1860
+ secrets: options.secrets,
1861
+ http,
1862
+ paginate: createPaginate(http),
1863
+ db: createDb(),
1864
+ map: createMap(mapConcurrency(options)),
1865
+ cache: createCache(),
1866
+ crypto: createCrypto(),
1867
+ time: createTime(),
1868
+ log: options.logs.logger(),
1869
+ signal: createSignal()
1870
+ };
1871
+ Object.defineProperty(ctx, "files", {
1872
+ get() {
1873
+ throw new errors.NotAvailable("ctx.files");
1874
+ }
1875
+ });
1876
+ return Object.freeze(ctx);
1877
+ }
1878
+ function createPullContext(options) {
1879
+ const base = createCheckContext(options);
1880
+ const ctx = Object.create(null);
1881
+ for (const key of Object.keys(base))
1882
+ ctx[key] = base[key];
1883
+ Object.defineProperty(ctx, "files", {
1884
+ get() {
1885
+ throw new errors.NotAvailable("ctx.files");
1886
+ }
1887
+ });
1888
+ ctx.record = (table, row) => Object.freeze({ __ontos: "record", table: tableName(table), row: plainObject(row, "row") });
1889
+ ctx.remove = (table, key) => Object.freeze({ __ontos: "remove", table: tableName(table), key: plainObject(key, "key") });
1890
+ ctx.checkpoint = (state) => Object.freeze({ __ontos: "checkpoint", state });
1891
+ return Object.freeze(ctx);
1892
+ }
1893
+
1894
+ // src/runtime/guest/failure.ts
1895
+ function errorInfo(error) {
1896
+ let code = null;
1897
+ let message = "connector threw a non-error value";
1898
+ let stack;
1899
+ if (error !== null && (typeof error === "object" || typeof error === "function")) {
1900
+ const value = error;
1901
+ try {
1902
+ if (typeof value.code === "string")
1903
+ code = value.code;
1904
+ } catch {
1905
+ code = null;
1906
+ }
1907
+ try {
1908
+ if (typeof value.message === "string")
1909
+ message = value.message;
1910
+ } catch {}
1911
+ try {
1912
+ if (typeof value.stack === "string")
1913
+ stack = value.stack;
1914
+ } catch {
1915
+ stack = undefined;
1916
+ }
1917
+ } else if (typeof error === "string") {
1918
+ message = error;
1919
+ }
1920
+ return { code, message, stack };
1921
+ }
1922
+ function connectorStack(stack) {
1923
+ if (stack === undefined)
1924
+ return;
1925
+ const lines = stack.split(`
1926
+ `);
1927
+ const frames = lines.slice(1).filter((line) => line.includes(`${CONNECTOR_FILENAME}:`));
1928
+ if (frames.length === 0)
1929
+ return;
1930
+ return [lines[0] ?? "", ...frames].join(`
1931
+ `);
1932
+ }
1933
+ var CODE_MAP = Object.freeze({
1934
+ AUTH_ERROR: "AUTH_ERROR",
1935
+ TRANSIENT_ERROR: "TRANSIENT_ERROR",
1936
+ RATE_LIMITED: "TRANSIENT_ERROR",
1937
+ SCHEMA_ERROR: "SCHEMA_ERROR",
1938
+ FATAL_ERROR: "FATAL_ERROR",
1939
+ CANCELED: "CANCELED"
1940
+ });
1941
+ function failure(code, message, logs, stack) {
1942
+ return { type: "failure", code, message, ...stack !== undefined ? { stack } : {}, logs };
1943
+ }
1944
+ function failureFromError(error, logs) {
1945
+ const info = errorInfo(error);
1946
+ const stack = connectorStack(info.stack);
1947
+ if (isAborted())
1948
+ return failure("CANCELED", "run was canceled", logs, stack);
1949
+ const mapped = info.code !== null && Object.hasOwn(CODE_MAP, info.code) ? CODE_MAP[info.code] : undefined;
1950
+ if (mapped)
1951
+ return failure(mapped, info.message, logs, stack);
1952
+ return failure("FATAL_ERROR", info.code === null ? info.message : `${info.code}: ${info.message}`, logs, stack);
1953
+ }
1954
+
1955
+ // src/runtime/guest/discover.ts
1956
+ function plain(value) {
1957
+ const text = jsonStringify(value);
1958
+ if (typeof text !== "string")
1959
+ throw new TypeError("discover result is not serializable");
1960
+ return jsonParse(text);
1961
+ }
1962
+ async function discoverDeclaration(definition, options) {
1963
+ const ctx = createCheckContext(options);
1964
+ let raw;
1965
+ try {
1966
+ if (typeof definition.check === "function")
1967
+ await Reflect.apply(definition.check, definition, [ctx]);
1968
+ throwIfAborted();
1969
+ raw = plain(await Reflect.apply(definition.discover, definition, [ctx]));
1970
+ throwIfAborted();
1971
+ } catch (error) {
1972
+ return { ok: false, failure: failureFromError(error, options.logs.drain()) };
1973
+ }
1974
+ const issues = validateDiscoverResult(raw, definition);
1975
+ if (issues.length > 0) {
1976
+ const message = `invalid discover result: ${issues.map((issue) => `${issue.path || "(root)"}: ${issue.message}`).join("; ")}`;
1977
+ return { ok: false, failure: failure("SCHEMA_ERROR", message, options.logs.drain()) };
1978
+ }
1979
+ return { ok: true, discover: normalizeDiscoverResult(raw) };
1980
+ }
1981
+ async function runDiscover(definition, options) {
1982
+ const outcome = await discoverDeclaration(definition, options);
1983
+ if (!outcome.ok)
1984
+ return outcome.failure;
1985
+ return {
1986
+ type: "result",
1987
+ result: { kind: "discover", summary: options.summary, discover: outcome.discover, logs: options.logs.drain() }
1988
+ };
1989
+ }
1990
+ async function runCheck(definition, options) {
1991
+ const ctx = createCheckContext(options);
1992
+ try {
1993
+ if (typeof definition.check === "function")
1994
+ await Reflect.apply(definition.check, definition, [ctx]);
1995
+ throwIfAborted();
1996
+ } catch (error) {
1997
+ return failureFromError(error, options.logs.drain());
1998
+ }
1999
+ return { type: "result", result: { kind: "check", ok: true, logs: options.logs.drain() } };
2000
+ }
2001
+ // src/t.ts
2002
+ var META_KEYS = ["title", "description", "placeholder"];
2003
+ function build(base, meta) {
2004
+ const out = { ...base };
2005
+ if (meta) {
2006
+ for (const key of META_KEYS) {
2007
+ if (meta[key] !== undefined)
2008
+ out[key] = meta[key];
2009
+ }
2010
+ }
2011
+ return Object.freeze(out);
2012
+ }
2013
+ function scalar(kind, meta) {
2014
+ return build({ kind }, meta);
2015
+ }
2016
+ var t = Object.freeze({
2017
+ string: (meta) => scalar("string", meta),
2018
+ integer: (meta) => scalar("integer", meta),
2019
+ decimal: (precision, scale, meta) => build({ kind: "decimal", precision, scale }, meta),
2020
+ boolean: (meta) => scalar("boolean", meta),
2021
+ date: (meta) => scalar("date", meta),
2022
+ instant: (meta) => scalar("instant", meta),
2023
+ json: (meta) => scalar("json", meta),
2024
+ object: (shape, meta) => build({ kind: "object", shape: Object.freeze({ ...shape }) }, meta),
2025
+ array: (item, meta) => build({ kind: "array", item }, meta),
2026
+ secret: (options) => build(options?.exposeToScript === true ? { kind: "secret", exposeToScript: true } : { kind: "secret" }, options)
2027
+ });
2028
+ // src/presentation.ts
2029
+ var PACKAGE_JSON = "package.json";
2030
+ var README = "README.md";
2031
+ var ICON_PATHS = ["icon.svg", "icon.png"];
2032
+ var ICON_MIME = { "icon.svg": "image/svg+xml", "icon.png": "image/png" };
2033
+ var PNG_DATA_URI_PREFIX = "data:image/png;base64,";
2034
+ var PNG_SIGNATURE = [137, 80, 78, 71, 13, 10, 26, 10];
2035
+ var BASE64 = /^[A-Za-z0-9+/]*={0,2}$/;
2036
+ var FORBIDDEN_SVG_ELEMENTS = new Set(["script", "foreignobject", "iframe", "object", "embed"]);
2037
+ var ALLOWED_ENTITIES = new Set(["amp", "lt", "gt", "quot", "apos"]);
2038
+ var EMPTY_PRESENTATION = { title: null, description: null, icon: null, readme: false };
2039
+ function isRecord6(value) {
2040
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2041
+ }
2042
+ function charCount(text) {
2043
+ return [...text].length;
2044
+ }
2045
+ function utf8Length2(text) {
2046
+ return new TextEncoder().encode(text).length;
2047
+ }
2048
+ function readText(owner, key, path, max, issues) {
2049
+ const value = owner[key];
2050
+ if (value === undefined)
2051
+ return null;
2052
+ if (typeof value !== "string" || value.trim().length === 0) {
2053
+ issues.push({ path, message: `${path.slice(PACKAGE_JSON.length + 1)} must be a non-empty string` });
2054
+ return null;
2055
+ }
2056
+ if (charCount(value) > max) {
2057
+ issues.push({ path, message: `${path.slice(PACKAGE_JSON.length + 1)} must be at most ${max} characters` });
2058
+ return null;
2059
+ }
2060
+ return value;
2061
+ }
2062
+ function scanSvgTags(text) {
2063
+ const tags = [];
2064
+ let i = 0;
2065
+ const n = text.length;
2066
+ const isSpace = (ch) => ch === " " || ch === "\t" || ch === `
2067
+ ` || ch === "\r";
2068
+ while (i < n) {
2069
+ const lt = text.indexOf("<", i);
2070
+ if (lt === -1)
2071
+ break;
2072
+ if (text.startsWith("<!--", lt)) {
2073
+ const end = text.indexOf("-->", lt + 4);
2074
+ if (end === -1)
2075
+ return null;
2076
+ i = end + 3;
2077
+ continue;
2078
+ }
2079
+ if (text.startsWith("<?", lt)) {
2080
+ const end = text.indexOf("?>", lt + 2);
2081
+ if (end === -1)
2082
+ return null;
2083
+ i = end + 2;
2084
+ continue;
2085
+ }
2086
+ i = lt + 1;
2087
+ if (text[i] === "/") {
2088
+ const end = text.indexOf(">", i);
2089
+ if (end === -1)
2090
+ return null;
2091
+ i = end + 1;
2092
+ continue;
2093
+ }
2094
+ while (isSpace(text[i]))
2095
+ i += 1;
2096
+ let name = "";
2097
+ while (i < n && !isSpace(text[i]) && text[i] !== ">" && text[i] !== "/")
2098
+ name += text[i++];
2099
+ const tag = { name: name.slice(name.lastIndexOf(":") + 1), attributes: [] };
2100
+ for (;; ) {
2101
+ while (isSpace(text[i]))
2102
+ i += 1;
2103
+ if (i >= n)
2104
+ return null;
2105
+ if (text[i] === ">") {
2106
+ i += 1;
2107
+ break;
2108
+ }
2109
+ if (text[i] === "/") {
2110
+ i += 1;
2111
+ continue;
2112
+ }
2113
+ let attr = "";
2114
+ while (i < n && !isSpace(text[i]) && text[i] !== "=" && text[i] !== ">" && text[i] !== "/")
2115
+ attr += text[i++];
2116
+ while (isSpace(text[i]))
2117
+ i += 1;
2118
+ let value = "";
2119
+ if (text[i] === "=") {
2120
+ i += 1;
2121
+ while (isSpace(text[i]))
2122
+ i += 1;
2123
+ const quote = text[i];
2124
+ if (quote === '"' || quote === "'") {
2125
+ const end = text.indexOf(quote, i + 1);
2126
+ if (end === -1)
2127
+ return null;
2128
+ value = text.slice(i + 1, end);
2129
+ i = end + 1;
2130
+ } else {
2131
+ while (i < n && !isSpace(text[i]) && text[i] !== ">")
2132
+ value += text[i++];
2133
+ }
2134
+ }
2135
+ if (attr)
2136
+ tag.attributes.push({ name: attr, value });
2137
+ }
2138
+ tags.push(tag);
2139
+ }
2140
+ return tags;
2141
+ }
2142
+ function cssProblem(css) {
2143
+ const lower = css.toLowerCase();
2144
+ if (lower.includes("url(") || lower.includes("@import"))
2145
+ return "must not reference url() or @import in styles";
2146
+ if (css.includes("\\"))
2147
+ return "must not use escapes in styles";
2148
+ return null;
2149
+ }
2150
+ function svgIconProblem(text) {
2151
+ const lower = text.toLowerCase();
2152
+ if (lower.includes("<!doctype") || lower.includes("<!entity"))
2153
+ return "icon.svg must not contain <!DOCTYPE> or <!ENTITY>";
2154
+ if (lower.includes("<![cdata["))
2155
+ return "icon.svg must not contain CDATA sections";
2156
+ for (const match of lower.matchAll(/&(#?[a-z0-9]*);?/g)) {
2157
+ if (!ALLOWED_ENTITIES.has(match[1] ?? "") || !match[0].endsWith(";"))
2158
+ return "icon.svg must not contain character or entity references other than &amp; &lt; &gt; &quot; &apos;";
2159
+ }
2160
+ const tags = scanSvgTags(lower);
2161
+ if (tags === null)
2162
+ return "icon.svg is not well-formed XML";
2163
+ const root = tags[0];
2164
+ if (!root || root.name !== "svg")
2165
+ return "icon.svg must have an <svg> root element";
2166
+ if (!root.attributes.some((attribute) => attribute.name === "viewbox"))
2167
+ return "icon.svg root element must have a viewBox";
2168
+ for (const tag of tags) {
2169
+ if (FORBIDDEN_SVG_ELEMENTS.has(tag.name))
2170
+ return `icon.svg must not contain <${tag.name}>`;
2171
+ for (const { name, value } of tag.attributes) {
2172
+ const local = name.slice(name.lastIndexOf(":") + 1);
2173
+ if (local.startsWith("on"))
2174
+ return `icon.svg must not contain event handler attributes (${name})`;
2175
+ if (local === "href" && !value.trim().startsWith("#"))
2176
+ return `icon.svg ${name} must be a local reference starting with #`;
2177
+ if (local === "style") {
2178
+ const problem = cssProblem(value);
2179
+ if (problem)
2180
+ return `icon.svg style attribute ${problem}`;
2181
+ }
2182
+ }
2183
+ }
2184
+ for (const match of lower.matchAll(/<(?:[a-z0-9_.-]+:)?style\b[^>]*>([\s\S]*?)(?:<\/|$)/g)) {
2185
+ const problem = cssProblem(match[1] ?? "");
2186
+ if (problem)
2187
+ return `icon.svg <style> ${problem}`;
2188
+ }
2189
+ return null;
2190
+ }
2191
+ function decodeBase64(text) {
2192
+ if (text.length % 4 !== 0 || !BASE64.test(text))
2193
+ return null;
2194
+ try {
2195
+ const binary = atob(text);
2196
+ const bytes = new Uint8Array(binary.length);
2197
+ for (let i = 0;i < binary.length; i += 1)
2198
+ bytes[i] = binary.charCodeAt(i);
2199
+ return bytes;
2200
+ } catch {
2201
+ return null;
2202
+ }
2203
+ }
2204
+ function readUint32(bytes, offset) {
2205
+ return (bytes[offset] ?? 0) * 16777216 + ((bytes[offset + 1] ?? 0) << 16 | (bytes[offset + 2] ?? 0) << 8 | (bytes[offset + 3] ?? 0));
2206
+ }
2207
+ function checkIcon(path, content) {
2208
+ const limit = PRESENTATION_LIMITS.iconBytes;
2209
+ if (path === "icon.svg") {
2210
+ const bytes = new TextEncoder().encode(content);
2211
+ if (bytes.length > limit)
2212
+ return { problem: `icon.svg must be at most ${limit} bytes` };
2213
+ const problem = svgIconProblem(content);
2214
+ return problem ? { problem } : { bytes };
2215
+ }
2216
+ if (!content.startsWith(PNG_DATA_URI_PREFIX))
2217
+ return { problem: `icon.png must be stored as ${PNG_DATA_URI_PREFIX}<data>` };
2218
+ const encoded = content.slice(PNG_DATA_URI_PREFIX.length);
2219
+ if (Math.floor(encoded.length / 4) * 3 - 2 > limit)
2220
+ return { problem: `icon.png must be at most ${limit} bytes` };
2221
+ const bytes = decodeBase64(encoded);
2222
+ if (bytes === null)
2223
+ return { problem: "icon.png is not valid base64" };
2224
+ if (bytes.length > limit)
2225
+ return { problem: `icon.png must be at most ${limit} bytes` };
2226
+ if (bytes.length < 24 || PNG_SIGNATURE.some((byte, i) => bytes[i] !== byte) || String.fromCharCode(...bytes.subarray(12, 16)) !== "IHDR") {
2227
+ return { problem: "icon.png is not a PNG image" };
2228
+ }
2229
+ const width = readUint32(bytes, 16);
2230
+ const height = readUint32(bytes, 20);
2231
+ const min = PRESENTATION_LIMITS.iconMinPx;
2232
+ if (width !== height || width < min)
2233
+ return { problem: `icon.png must be square and at least ${min}×${min} pixels, got ${width}×${height}` };
2234
+ return { bytes };
2235
+ }
2236
+ function readConnectorPresentation(files, sha256) {
2237
+ const issues = [];
2238
+ let pkg;
2239
+ try {
2240
+ pkg = JSON.parse(files[PACKAGE_JSON] ?? "");
2241
+ } catch {
2242
+ pkg = undefined;
2243
+ }
2244
+ if (!isRecord6(pkg)) {
2245
+ return { presentation: { ...EMPTY_PRESENTATION }, issues: [{ path: PACKAGE_JSON, message: "package.json must be a JSON object" }] };
2246
+ }
2247
+ const presentation = { ...EMPTY_PRESENTATION };
2248
+ presentation.description = readText(pkg, "description", `${PACKAGE_JSON}:description`, PRESENTATION_LIMITS.description, issues);
2249
+ const ontos = pkg.ontos;
2250
+ if (ontos !== undefined && !isRecord6(ontos)) {
2251
+ issues.push({ path: `${PACKAGE_JSON}:ontos`, message: "ontos must be an object" });
2252
+ } else if (ontos) {
2253
+ presentation.title = readText(ontos, "title", `${PACKAGE_JSON}:ontos.title`, PRESENTATION_LIMITS.title, issues);
2254
+ const icon = ontos.icon;
2255
+ if (icon !== undefined) {
2256
+ if (typeof icon !== "string" || !ICON_PATHS.includes(icon)) {
2257
+ issues.push({ path: `${PACKAGE_JSON}:ontos.icon`, message: "ontos.icon must be icon.svg or icon.png (a file in the source root)" });
2258
+ } else {
2259
+ const path = icon;
2260
+ const content = files[path];
2261
+ if (content === undefined) {
2262
+ issues.push({ path, message: `${path} is referenced by ontos.icon but missing` });
2263
+ } else {
2264
+ const checked = checkIcon(path, content);
2265
+ if ("problem" in checked)
2266
+ issues.push({ path, message: checked.problem });
2267
+ else
2268
+ presentation.icon = { path, mime: ICON_MIME[path], sha256: sha256(checked.bytes), bytes: checked.bytes.length };
2269
+ }
2270
+ }
2271
+ }
2272
+ }
2273
+ const readme = files[README];
2274
+ if (readme !== undefined) {
2275
+ if (utf8Length2(readme) > PRESENTATION_LIMITS.readmeBytes) {
2276
+ issues.push({ path: README, message: `README.md must be at most ${PRESENTATION_LIMITS.readmeBytes} bytes` });
2277
+ } else {
2278
+ presentation.readme = true;
2279
+ }
2280
+ }
2281
+ return { presentation, issues };
2282
+ }
2283
+ function decodeConnectorIcon(files, path) {
2284
+ const content = files[path];
2285
+ if (content === undefined || !ICON_PATHS.includes(path))
2286
+ return null;
2287
+ const checked = checkIcon(path, content);
2288
+ return "bytes" in checked ? checked.bytes : null;
2289
+ }
2290
+
2291
+ // src/index.ts
2292
+ var SDK_VERSION = "3.0.1";
2293
+ var SUPPORTED_SDK_MAJORS = [3];
2294
+
2295
+ // src/runtime/guest/loader.ts
2296
+ var SDK_SPECIFIER = "@deepexi/datasense-connector";
2297
+ var sdkModule = Object.freeze({ ...exports_src, __esModule: true });
2298
+ function connectorRequire(specifier) {
2299
+ if (specifier === SDK_SPECIFIER)
2300
+ return sdkModule;
2301
+ throw new ConnectorError("CONNECTOR_LOAD_FAILED", `cannot require ${String(specifier)}: connectors can only import ${SDK_SPECIFIER} and relative files`);
2302
+ }
2303
+ function resolveExport(exported) {
2304
+ if (exported !== null && (typeof exported === "object" || typeof exported === "function")) {
2305
+ const fallback = exported.default;
2306
+ if (fallback !== undefined && fallback !== null)
2307
+ return fallback;
2308
+ }
2309
+ return exported;
2310
+ }
2311
+ function loadDefinition(factory, expect) {
2312
+ if (typeof factory !== "function")
2313
+ return { ok: false, message: "connector code did not compile to a module", stack: undefined };
2314
+ let definition;
2315
+ try {
2316
+ const module2 = { exports: {} };
2317
+ Reflect.apply(factory, undefined, [module2, module2.exports, connectorRequire]);
2318
+ definition = resolveExport(module2.exports);
2319
+ } catch (error) {
2320
+ const info = errorInfo(error);
2321
+ return { ok: false, message: `connector code failed to load: ${info.message}`, stack: info.stack };
2322
+ }
2323
+ const issues = validateConnectorDefinition(definition);
2324
+ if (issues.length > 0) {
2325
+ return { ok: false, message: `invalid connector definition: ${issues.map((issue) => `${issue.path || "(root)"}: ${issue.message}`).join("; ")}`, stack: undefined };
2326
+ }
2327
+ const loaded = definition;
2328
+ if (loaded.name !== expect.name || loaded.version !== expect.version) {
2329
+ return { ok: false, message: `connector code defines ${loaded.name}@${loaded.version}, expected ${expect.name}@${expect.version}`, stack: undefined };
2330
+ }
2331
+ return { ok: true, definition: loaded };
2332
+ }
2333
+
2334
+ // src/runtime/guest/log.ts
2335
+ function render(message, data) {
2336
+ let text = typeof message === "string" ? message : "non-string log message";
2337
+ if (data !== undefined) {
2338
+ try {
2339
+ const extra = jsonStringify(data);
2340
+ if (typeof extra === "string")
2341
+ text = `${text} ${extra}`;
2342
+ } catch {}
2343
+ }
2344
+ return text.length > MAX_LOG_MESSAGE ? text.slice(0, MAX_LOG_MESSAGE) : text;
2345
+ }
2346
+ function createLogBuffer() {
2347
+ let entries = [];
2348
+ let total = 0;
2349
+ const write = (level, requestId, isOpen) => (message, data) => {
2350
+ if (total >= MAX_LOGS || isOpen && !isOpen())
2351
+ return;
2352
+ total += 1;
2353
+ entries[entries.length] = { at: isoNow(), level, message: render(message, data), ...requestId !== undefined ? { requestId } : {} };
2354
+ };
2355
+ return {
2356
+ drain() {
2357
+ const out = entries;
2358
+ entries = [];
2359
+ return out;
2360
+ },
2361
+ logger(requestId, isOpen) {
2362
+ return Object.freeze({ info: write("info", requestId, isOpen), warn: write("warn", requestId, isOpen), error: write("error", requestId, isOpen) });
2363
+ }
2364
+ };
2365
+ }
2366
+
2367
+ // src/runtime/guest/pull.ts
2368
+ function isRecord7(value) {
2369
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2370
+ }
2371
+ function copy(value, what) {
2372
+ let text;
2373
+ try {
2374
+ text = jsonStringify(value);
2375
+ } catch (error) {
2376
+ throw new errors.SchemaError(`${what} is not serializable: ${error instanceof Error ? error.message : String(error)}`);
2377
+ }
2378
+ if (typeof text !== "string")
2379
+ throw new errors.SchemaError(`${what} is not serializable`);
2380
+ return { value: jsonParse(text), text };
2381
+ }
2382
+ var BATCH_FRAME_BYTES = 5;
2383
+ var ITEM_FRAME_BYTES = 17 + 1;
2384
+ function itemBytes(table, valueText) {
2385
+ return ITEM_FRAME_BYTES + utf8Length(jsonStringify(table)) + utf8Length(valueText);
2386
+ }
2387
+ function emptyBatch() {
2388
+ return { records: [], removals: [], checkpoint: undefined, bytes: BATCH_FRAME_BYTES, dropped: 0 };
2389
+ }
2390
+ function closeIterator(iterator) {
2391
+ try {
2392
+ const closing = iterator.return?.();
2393
+ if (closing && typeof closing.then === "function")
2394
+ closing.then(undefined, () => {
2395
+ return;
2396
+ });
2397
+ } catch {}
2398
+ }
2399
+ async function runPull(definition, job, options) {
2400
+ const streams = definition.streams ?? {};
2401
+ const stream = streams[job.stream];
2402
+ const ctx = createPullContext(options);
2403
+ const input = Object.freeze({
2404
+ mode: job.mode,
2405
+ state: job.state === null ? undefined : deepFreeze(job.state),
2406
+ streamConfig: deepFreeze(job.streamConfig)
2407
+ });
2408
+ const tables = new Set(job.tables);
2409
+ let batch = emptyBatch();
2410
+ let emitted = 0;
2411
+ const flush = async () => {
2412
+ const payload = {
2413
+ records: batch.records,
2414
+ removals: batch.removals,
2415
+ ...batch.checkpoint ? { checkpoint: batch.checkpoint } : {},
2416
+ logs: options.logs.drain(),
2417
+ ...batch.dropped > 0 ? { dropped: batch.dropped } : {}
2418
+ };
2419
+ batch = emptyBatch();
2420
+ const reply = await call("emit", payload);
2421
+ emitted += 1;
2422
+ if (isRecord7(reply) && reply.stop === true)
2423
+ requestStop();
2424
+ };
2425
+ const result = () => ({
2426
+ type: "result",
2427
+ result: { kind: "pull", emittedBatches: emitted, ...isStopped() ? { stopped: true } : {}, logs: options.logs.drain() }
2428
+ });
2429
+ let oversized = 0;
2430
+ const log = options.logs.logger();
2431
+ const add = async (what, table, size, push) => {
2432
+ if (BATCH_FRAME_BYTES + size > PULL_BATCH_MAX_BYTES) {
2433
+ oversized += 1;
2434
+ batch.dropped += 1;
2435
+ log.warn(`RECORD_TOO_LARGE: ${what} for table "${table}" is ${size} bytes, more than the ${PULL_BATCH_MAX_BYTES}-byte batch limit; it was dropped`);
2436
+ if (oversized > PULL_REJECT_THRESHOLD) {
2437
+ throw new errors.SchemaError(`stream ${job.stream} produced ${oversized} records larger than ${PULL_BATCH_MAX_BYTES} bytes, more than ${PULL_REJECT_THRESHOLD}`);
2438
+ }
2439
+ return;
2440
+ }
2441
+ if (batch.records.length + batch.removals.length > 0 && batch.bytes + size > PULL_BATCH_MAX_BYTES)
2442
+ await flush();
2443
+ push();
2444
+ batch.bytes += size;
2445
+ if (batch.records.length + batch.removals.length >= PULL_BATCH_MAX_RECORDS)
2446
+ await flush();
2447
+ };
2448
+ const checkTable = (table) => {
2449
+ if (typeof table !== "string" || !tables.has(table)) {
2450
+ throw new errors.SchemaError(`stream ${job.stream} produced a record for table ${String(table)}, which is not declared for this stream`);
2451
+ }
2452
+ return table;
2453
+ };
2454
+ let iterator;
2455
+ try {
2456
+ const iterable = Reflect.apply(stream, streams, [ctx, input]);
2457
+ const factory = isRecord7(iterable) || typeof iterable === "function" ? iterable[Symbol.asyncIterator] : undefined;
2458
+ if (typeof factory !== "function")
2459
+ throw new errors.SchemaError(`stream ${job.stream} must be an async generator function`);
2460
+ iterator = Reflect.apply(factory, iterable, []);
2461
+ } catch (error) {
2462
+ return failureFromError(error, options.logs.drain());
2463
+ }
2464
+ try {
2465
+ for (;; ) {
2466
+ if (isAborted()) {
2467
+ closeIterator(iterator);
2468
+ return isStopped() ? result() : failure("CANCELED", "run was canceled", options.logs.drain());
2469
+ }
2470
+ const step = await iterator.next();
2471
+ if (step.done)
2472
+ break;
2473
+ const item = step.value;
2474
+ const kind = isRecord7(item) ? item.__ontos : undefined;
2475
+ if (kind === "record" && isRecord7(item)) {
2476
+ const table = checkTable(item.table);
2477
+ if (!isRecord7(item.row))
2478
+ throw new errors.SchemaError(`stream ${job.stream} produced a record whose row is not an object`);
2479
+ const row = copy(item.row, "row");
2480
+ await add("record", table, itemBytes(table, row.text), () => batch.records.push({ table, row: row.value }));
2481
+ } else if (kind === "remove" && isRecord7(item)) {
2482
+ const table = checkTable(item.table);
2483
+ if (!isRecord7(item.key))
2484
+ throw new errors.SchemaError(`stream ${job.stream} produced a removal whose key is not an object`);
2485
+ const key = copy(item.key, "key");
2486
+ await add("removal", table, itemBytes(table, key.text), () => batch.removals.push({ table, key: key.value }));
2487
+ } else if (kind === "checkpoint" && isRecord7(item)) {
2488
+ batch.checkpoint = { state: copy(item.state, "checkpoint state").value };
2489
+ await flush();
2490
+ } else {
2491
+ throw new errors.SchemaError(`stream ${job.stream} yielded a value that is not ctx.record(), ctx.remove() or ctx.checkpoint()`);
2492
+ }
2493
+ }
2494
+ if (isAborted())
2495
+ return isStopped() ? result() : failure("CANCELED", "run was canceled", options.logs.drain());
2496
+ if (batch.records.length + batch.removals.length > 0 || batch.dropped > 0)
2497
+ await flush();
2498
+ } catch (error) {
2499
+ closeIterator(iterator);
2500
+ if (isStopped())
2501
+ return result();
2502
+ return failureFromError(error, options.logs.drain());
2503
+ }
2504
+ return result();
2505
+ }
2506
+
2507
+ // src/runtime/guest/push.ts
2508
+ var WHOLE_RUN_FAILURES = ["SCHEMA_ERROR", "FATAL_ERROR"];
2509
+ function copyRecord(value, what) {
2510
+ if (value === null || typeof value !== "object" || Array.isArray(value))
2511
+ throw new TypeError(`${what} must be an object`);
2512
+ const text = jsonStringify(value);
2513
+ if (typeof text !== "string")
2514
+ throw new TypeError(`${what} is not serializable`);
2515
+ return jsonParse(text);
2516
+ }
2517
+ async function runPush(definition, job) {
2518
+ const handlers = definition.push !== null && typeof definition.push === "object" ? definition.push : {};
2519
+ if (Object.keys(handlers).length === 0)
2520
+ return failure("CONNECTOR_LOAD_FAILED", "connector does not support push", []);
2521
+ const config = deepFreeze(job.config);
2522
+ const logs = createLogBuffer();
2523
+ const result = { kind: "push", records: [], removals: [], rejected: [], logs: [] };
2524
+ for (const request of job.requests) {
2525
+ const requestId = request.requestId;
2526
+ const handler = Object.hasOwn(handlers, request.push) ? handlers[request.push] : undefined;
2527
+ if (!handler) {
2528
+ result.rejected.push({ requestId, code: "PUSH_UNKNOWN", message: `connector has no push named ${request.push}` });
2529
+ continue;
2530
+ }
2531
+ const pending = { records: [], removals: [], open: true };
2532
+ const assertOpen = () => {
2533
+ if (!pending.open)
2534
+ throw new TypeError("ctx is no longer usable after the request finished");
2535
+ };
2536
+ const ctx = Object.freeze({
2537
+ config,
2538
+ record(table, row) {
2539
+ assertOpen();
2540
+ if (typeof table !== "string")
2541
+ throw new TypeError("table must be a string");
2542
+ pending.records.push({ requestId, table, row: copyRecord(row, "row") });
2543
+ },
2544
+ remove(table, key) {
2545
+ assertOpen();
2546
+ if (typeof table !== "string")
2547
+ throw new TypeError("table must be a string");
2548
+ pending.removals.push({ requestId, table, key: copyRecord(key, "key") });
2549
+ },
2550
+ log: logs.logger(requestId, () => pending.open),
2551
+ time: Object.freeze({ now: isoNow })
2552
+ });
2553
+ const body = request.body;
2554
+ const req = Object.freeze({
2555
+ id: requestId,
2556
+ receivedAt: request.receivedAt,
2557
+ headers: deepFreeze({ ...request.headers }),
2558
+ body,
2559
+ json() {
2560
+ return jsonParse(body);
2561
+ }
2562
+ });
2563
+ try {
2564
+ if (typeof handler.verify === "function")
2565
+ await Reflect.apply(handler.verify, handler, [ctx, req]);
2566
+ await Reflect.apply(handler.map, handler, [ctx, req]);
2567
+ pending.open = false;
2568
+ result.records.push(...pending.records);
2569
+ result.removals.push(...pending.removals);
2570
+ } catch (error) {
2571
+ pending.open = false;
2572
+ const info = errorInfo(error);
2573
+ if (info.code !== null && WHOLE_RUN_FAILURES.includes(info.code)) {
2574
+ return failure(info.code, info.message, logs.drain(), connectorStack(info.stack));
2575
+ }
2576
+ result.rejected.push({ requestId, code: info.code ?? "CONNECTOR_ERROR", message: info.message });
2577
+ }
2578
+ }
2579
+ result.logs = logs.drain();
2580
+ return { type: "result", result };
2581
+ }
2582
+
2583
+ // src/runtime/guest/index.ts
2584
+ var STRIPPED_GLOBALS = ["WebAssembly", "ShadowRealm", "console"];
2585
+ function issuesText(issues) {
2586
+ return issues.map((issue) => `${issue.path || "(root)"}: ${issue.message}`).join("; ");
2587
+ }
2588
+ async function run2(definition, job) {
2589
+ if (job.kind === "push")
2590
+ return runPush(definition, job);
2591
+ if (job.kind === "pull" && !(definition.streams && Object.hasOwn(definition.streams, job.stream))) {
2592
+ return failure("CONNECTOR_LOAD_FAILED", `connector has no stream named ${job.stream}`, []);
2593
+ }
2594
+ const summary = jsonParse(jsonStringify(summarizeDefinition(definition)));
2595
+ const logs = createLogBuffer();
2596
+ let effectiveConcurrency;
2597
+ try {
2598
+ effectiveConcurrency = concurrencyFromDefinitionReply(await call("definition", summary));
2599
+ } catch (error) {
2600
+ return failureFromError(error, logs.drain());
2601
+ }
2602
+ const issues = validateConfig(definition.config, job.config);
2603
+ if (issues.length > 0)
2604
+ return failure("CONFIG_INVALID", `invalid connection config: ${issuesText(issues)}`, logs.drain());
2605
+ let secrets;
2606
+ try {
2607
+ secrets = await prepareSecrets(summary, job.secretFields);
2608
+ } catch (error) {
2609
+ return failureFromError(error, logs.drain());
2610
+ }
2611
+ const options = { config: job.config, secrets, summary, logs, effectiveConcurrency };
2612
+ if (job.kind === "discover")
2613
+ return runDiscover(definition, options);
2614
+ if (job.kind === "check")
2615
+ return runCheck(definition, options);
2616
+ if (job.discoverFirst !== true)
2617
+ return runPull(definition, job, options);
2618
+ const declared = await discoverDeclaration(definition, options);
2619
+ if (!declared.ok)
2620
+ return declared.failure;
2621
+ try {
2622
+ await call("declaration", { summary, discover: declared.discover });
2623
+ } catch (error) {
2624
+ return failureFromError(error, logs.drain());
2625
+ }
2626
+ const stream = Object.hasOwn(declared.discover.streams, job.stream) ? declared.discover.streams[job.stream] : undefined;
2627
+ if (!stream)
2628
+ return failure("SCHEMA_ERROR", `stream ${job.stream} is not in the discover result`, logs.drain());
2629
+ return runPull(definition, { ...job, tables: Object.keys(stream.tables) }, options);
2630
+ }
2631
+ async function execute(json, factory) {
2632
+ const { expect, job } = jsonParse(json);
2633
+ const loaded = loadDefinition(factory, expect);
2634
+ if (!loaded.ok)
2635
+ return failure("CONNECTOR_LOAD_FAILED", loaded.message, [], connectorStack(loaded.stack));
2636
+ return run2(loaded.definition, job);
2637
+ }
2638
+ var started = false;
2639
+ function start(json, factory) {
2640
+ if (started || typeof json !== "string")
2641
+ return;
2642
+ started = true;
2643
+ let pending;
2644
+ try {
2645
+ pending = execute(json, factory);
2646
+ } catch (error) {
2647
+ finish(failure("FATAL_ERROR", errorInfo(error).message, []));
2648
+ return;
2649
+ }
2650
+ pending.then((outcome) => finish(outcome), (error) => finish(failure("FATAL_ERROR", `connector runtime failed: ${errorInfo(error).message}`, [])));
2651
+ }
2652
+ function bootGuest() {
2653
+ for (const name of STRIPPED_GLOBALS)
2654
+ Reflect.deleteProperty(globalThis, name);
2655
+ return Object.freeze({ start, handle, poll });
2656
+ }
2657
+ function bootLocalGuest() {
2658
+ return Object.freeze({ start, handle, poll });
2659
+ }