@deepexi/datasense-connector 3.0.1 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (185) hide show
  1. package/README.md +158 -139
  2. package/package.json +19 -28
  3. package/scripts/build.ts +188 -0
  4. package/src/define.ts +267 -0
  5. package/src/descriptor.ts +116 -0
  6. package/src/errors.ts +105 -0
  7. package/src/index.ts +108 -0
  8. package/src/internal/location.ts +23 -0
  9. package/src/runtime/guest/bridge.ts +238 -0
  10. package/src/runtime/guest/cache.ts +43 -0
  11. package/src/runtime/guest/check.ts +17 -0
  12. package/src/runtime/guest/crypto.ts +166 -0
  13. package/src/runtime/guest/ctx.ts +157 -0
  14. package/src/runtime/guest/db.ts +59 -0
  15. package/src/runtime/guest/failure.ts +69 -0
  16. package/src/runtime/guest/http.ts +194 -0
  17. package/src/runtime/guest/index.ts +102 -0
  18. package/{runtime/guest/limits.d.ts → src/runtime/guest/limits.ts} +12 -12
  19. package/src/runtime/guest/loader.ts +210 -0
  20. package/src/runtime/guest/log.ts +47 -0
  21. package/src/runtime/guest/map.ts +45 -0
  22. package/src/runtime/guest/operation.ts +193 -0
  23. package/src/runtime/guest/paginate.ts +169 -0
  24. package/src/runtime/guest/pull.ts +177 -0
  25. package/src/runtime/guest/push.ts +92 -0
  26. package/src/runtime/guest/signal.ts +14 -0
  27. package/src/runtime/guest/suggest.ts +51 -0
  28. package/src/runtime/guest/time.ts +137 -0
  29. package/src/runtime/guest-runtime.ts +75 -0
  30. package/src/runtime/index.ts +53 -0
  31. package/src/runtime/protocol.ts +557 -0
  32. package/src/runtime/session.ts +216 -0
  33. package/src/testing/child.ts +21 -0
  34. package/src/testing/compliance.ts +343 -0
  35. package/src/testing/engine.ts +445 -0
  36. package/src/testing/fixtures.ts +591 -0
  37. package/src/testing/gateway.ts +482 -0
  38. package/src/testing/import-guard.ts +221 -0
  39. package/src/testing/index.ts +43 -0
  40. package/src/testing/local-db.ts +226 -0
  41. package/src/testing/mock-external-system.ts +249 -0
  42. package/src/testing/pinned-http.ts +144 -0
  43. package/src/testing/recorder.ts +140 -0
  44. package/src/testing/redaction.ts +94 -0
  45. package/src/testing/replay.ts +437 -0
  46. package/src/testing/run-tests.ts +115 -0
  47. package/src/testing/schema.ts +70 -0
  48. package/src/testing/source-loader.ts +374 -0
  49. package/src/testing/types.ts +78 -0
  50. package/src/types.ts +329 -0
  51. package/src/validate.ts +267 -0
  52. package/test/build.test.ts +159 -0
  53. package/test/define.test-d.ts +182 -0
  54. package/test/fixtures/acceptance.ts +156 -0
  55. package/test/fixtures/connectors/http-pull/.ontos/descriptor.json +72 -0
  56. package/test/fixtures/connectors/http-pull/.ontos/types.ts +61 -0
  57. package/test/fixtures/connectors/http-pull/package.json +5 -0
  58. package/test/fixtures/connectors/http-pull/streams/items/index.ts +37 -0
  59. package/test/fixtures/connectors/http-pull/streams/items/map.ts +12 -0
  60. package/test/fixtures/connectors/http-pull/streams/items/tests/basic/case.json +15 -0
  61. package/test/fixtures/connectors/http-pull/streams/items/tests/basic/exchanges.ndjson +2 -0
  62. package/test/fixtures/connectors/pg-pull/.ontos/descriptor.json +151 -0
  63. package/test/fixtures/connectors/pg-pull/.ontos/types.ts +62 -0
  64. package/test/fixtures/connectors/pg-pull/lib/sql.ts +14 -0
  65. package/test/fixtures/connectors/pg-pull/package.json +5 -0
  66. package/test/fixtures/connectors/pg-pull/streams/customers/index.ts +30 -0
  67. package/test/fixtures/connectors/pg-pull/streams/customers/tests/customers-full/case.json +15 -0
  68. package/test/fixtures/connectors/pg-pull/streams/customers/tests/customers-full/exchanges.ndjson +3 -0
  69. package/test/fixtures/connectors/pg-pull/streams/orders/index.ts +32 -0
  70. package/test/fixtures/descriptor.ts +54 -0
  71. package/test/fixtures/kingdee-example.ts +82 -0
  72. package/test/fixtures/record-acceptance.ts +32 -0
  73. package/test/runtime/ctx.test.ts +434 -0
  74. package/test/runtime/helpers.ts +157 -0
  75. package/test/runtime/jobs.test.ts +444 -0
  76. package/test/runtime/operation.test.ts +175 -0
  77. package/test/runtime/session.test.ts +229 -0
  78. package/test/testing/acceptance-fixtures.test.ts +69 -0
  79. package/test/testing/compliance.test.ts +247 -0
  80. package/test/testing/engine.test.ts +283 -0
  81. package/test/testing/fixtures.test.ts +221 -0
  82. package/test/testing/gateway.test.ts +337 -0
  83. package/test/testing/import-guard.test.ts +45 -0
  84. package/test/testing/mock-external-system.test.ts +144 -0
  85. package/test/testing/operation-fixtures.test.ts +160 -0
  86. package/test/testing/recorder.test.ts +163 -0
  87. package/test/testing/redaction.test.ts +54 -0
  88. package/test/testing/replay.test.ts +207 -0
  89. package/test/testing/run-tests.test.ts +137 -0
  90. package/test/testing/source-loader.test.ts +220 -0
  91. package/test/validate-config.test.ts +104 -0
  92. package/tsconfig.build.json +23 -0
  93. package/tsconfig.json +19 -0
  94. package/cjs/index.cjs +0 -1015
  95. package/cjs/runtime.cjs +0 -752
  96. package/cjs/testing.cjs +0 -3591
  97. package/errors.d.ts +0 -17
  98. package/errors.js +0 -53
  99. package/guest.cjs +0 -2659
  100. package/index.d.ts +0 -13
  101. package/index.js +0 -7
  102. package/internal/location.d.ts +0 -6
  103. package/internal/location.js +0 -8
  104. package/presentation.d.ts +0 -38
  105. package/presentation.js +0 -268
  106. package/runtime/guest/bridge.d.ts +0 -53
  107. package/runtime/guest/bridge.js +0 -187
  108. package/runtime/guest/cache.d.ts +0 -3
  109. package/runtime/guest/cache.js +0 -42
  110. package/runtime/guest/crypto.d.ts +0 -13
  111. package/runtime/guest/crypto.js +0 -177
  112. package/runtime/guest/ctx.d.ts +0 -30
  113. package/runtime/guest/ctx.js +0 -124
  114. package/runtime/guest/db.d.ts +0 -6
  115. package/runtime/guest/db.js +0 -59
  116. package/runtime/guest/discover.d.ts +0 -29
  117. package/runtime/guest/discover.js +0 -51
  118. package/runtime/guest/failure.d.ts +0 -24
  119. package/runtime/guest/failure.js +0 -63
  120. package/runtime/guest/http.d.ts +0 -4
  121. package/runtime/guest/http.js +0 -204
  122. package/runtime/guest/index.d.ts +0 -12
  123. package/runtime/guest/index.js +0 -89
  124. package/runtime/guest/limits.js +0 -12
  125. package/runtime/guest/loader.d.ts +0 -15
  126. package/runtime/guest/loader.js +0 -42
  127. package/runtime/guest/log.d.ts +0 -10
  128. package/runtime/guest/log.js +0 -36
  129. package/runtime/guest/map.d.ts +0 -4
  130. package/runtime/guest/map.js +0 -45
  131. package/runtime/guest/paginate.d.ts +0 -6
  132. package/runtime/guest/paginate.js +0 -180
  133. package/runtime/guest/pull.d.ts +0 -12
  134. package/runtime/guest/pull.js +0 -150
  135. package/runtime/guest/push.d.ts +0 -18
  136. package/runtime/guest/push.js +0 -80
  137. package/runtime/guest/signal.d.ts +0 -3
  138. package/runtime/guest/signal.js +0 -12
  139. package/runtime/guest/time.d.ts +0 -5
  140. package/runtime/guest/time.js +0 -127
  141. package/runtime/guest-runtime.d.ts +0 -7
  142. package/runtime/guest-runtime.js +0 -51
  143. package/runtime/index.d.ts +0 -18
  144. package/runtime/index.js +0 -3
  145. package/runtime/protocol.d.ts +0 -276
  146. package/runtime/protocol.js +0 -409
  147. package/runtime/session.d.ts +0 -12
  148. package/runtime/session.js +0 -191
  149. package/t.d.ts +0 -18
  150. package/t.js +0 -26
  151. package/testing/child.cjs +0 -350
  152. package/testing/child.d.ts +0 -1
  153. package/testing/child.js +0 -12
  154. package/testing/compliance.d.ts +0 -41
  155. package/testing/compliance.js +0 -291
  156. package/testing/engine.d.ts +0 -66
  157. package/testing/engine.js +0 -339
  158. package/testing/fixtures.d.ts +0 -145
  159. package/testing/fixtures.js +0 -407
  160. package/testing/gateway.d.ts +0 -65
  161. package/testing/gateway.js +0 -402
  162. package/testing/import-guard.d.ts +0 -57
  163. package/testing/import-guard.js +0 -187
  164. package/testing/index.d.ts +0 -33
  165. package/testing/index.js +0 -10
  166. package/testing/local-db.d.ts +0 -27
  167. package/testing/local-db.js +0 -181
  168. package/testing/pinned-http.d.ts +0 -14
  169. package/testing/pinned-http.js +0 -125
  170. package/testing/recorder.d.ts +0 -15
  171. package/testing/recorder.js +0 -116
  172. package/testing/redaction.d.ts +0 -28
  173. package/testing/redaction.js +0 -67
  174. package/testing/replay.d.ts +0 -50
  175. package/testing/replay.js +0 -313
  176. package/testing/schema.d.ts +0 -13
  177. package/testing/schema.js +0 -65
  178. package/testing/source-loader.d.ts +0 -19
  179. package/testing/source-loader.js +0 -238
  180. package/testing/types.d.ts +0 -79
  181. package/testing/types.js +0 -18
  182. package/types.d.ts +0 -302
  183. package/types.js +0 -13
  184. package/validate.d.ts +0 -78
  185. package/validate.js +0 -597
@@ -1,12 +0,0 @@
1
- /** 子进程入口里调用:收到 execute 时建 vm 上下文、加载其中的 guest 字符串;之后只吃字符串消息。 */
2
- export declare function createGuestSession(send: (json: string) => void): {
3
- handle(json: string): void;
4
- };
5
- /**
6
- * 进程内会话(`./testing` 的 `createInProcessExecutor`,给 vitest 用):不建 vm,在当前全局里重新求值一份 guest 运行时
7
- * (模块状态每次执行独立),连接器是调用方已导入的定义对象,`execute.code` 被忽略。消息协议与 vm 会话完全相同。
8
- * 这里不是隔离边界:连接器代码与宿主同进程同全局,只用于作者在自己机器上跑测试。
9
- */
10
- export declare function createInProcessGuestSession(send: (json: string) => void, definition: unknown): {
11
- handle(json: string): void;
12
- };
@@ -1,191 +0,0 @@
1
- import vm from 'node:vm';
2
- import { parseParentMessage } from './protocol.js';
3
- const GUEST_FILENAME = 'ontos-guest.js';
4
- const CONNECTOR_FILENAME = 'connector.js';
5
- function isRecord(value) {
6
- return typeof value === 'object' && value !== null && !Array.isArray(value);
7
- }
8
- function guestErrorMessage(error) {
9
- try {
10
- if (error !== null && typeof error === 'object') {
11
- const message = error.message;
12
- if (typeof message === 'string')
13
- return message;
14
- }
15
- }
16
- catch {
17
- }
18
- return 'unknown error';
19
- }
20
- function bootGuest(context, guestSource) {
21
- const wrapped = `(() => { const module = { exports: {} }; const exports = module.exports; ${guestSource}\n; return module.exports; })()`;
22
- const exported = new vm.Script(wrapped, { filename: GUEST_FILENAME }).runInContext(context);
23
- const boot = exported.bootGuest;
24
- if (typeof boot !== 'function')
25
- throw new Error('guest runtime does not export bootGuest');
26
- const api = boot();
27
- const { start, handle, poll } = api;
28
- if (typeof start !== 'function' || typeof handle !== 'function' || typeof poll !== 'function')
29
- throw new Error('guest runtime returned an incomplete API');
30
- return { start: start, handle: handle, poll: poll };
31
- }
32
- function bootInVm(guestSource, code) {
33
- const context = vm.createContext(Object.create(null), { codeGeneration: { strings: false, wasm: false } });
34
- let guest;
35
- try {
36
- guest = bootGuest(context, guestSource);
37
- }
38
- catch (error) {
39
- return { ok: false, code: 'CONNECTOR_LOAD_FAILED', message: `connector runtime failed to start: ${guestErrorMessage(error)}` };
40
- }
41
- try {
42
- const factory = new vm.Script(`(function (module, exports, require) {${code}\n})`, { filename: CONNECTOR_FILENAME }).runInContext(context);
43
- return { ok: true, guest, factory };
44
- }
45
- catch (error) {
46
- return { ok: false, code: 'CONNECTOR_LOAD_FAILED', message: `connector code failed to compile: ${guestErrorMessage(error)}` };
47
- }
48
- }
49
- export function createGuestSession(send) {
50
- return createSession(send, bootInVm);
51
- }
52
- export function createInProcessGuestSession(send, definition) {
53
- return createSession(send, (guestSource) => {
54
- let exported;
55
- try {
56
- const module = { exports: {} };
57
- const noRequire = (specifier) => { throw new Error(`guest runtime cannot require ${specifier}`); };
58
- const load = new Function('module', 'exports', 'require', guestSource);
59
- load(module, module.exports, noRequire);
60
- exported = module.exports;
61
- }
62
- catch (error) {
63
- return { ok: false, code: 'CONNECTOR_LOAD_FAILED', message: `connector runtime failed to start: ${guestErrorMessage(error)}` };
64
- }
65
- if (typeof exported.bootLocalGuest !== 'function') {
66
- return { ok: false, code: 'CONNECTOR_LOAD_FAILED', message: 'connector runtime failed to start: guest runtime does not export bootLocalGuest' };
67
- }
68
- const guest = exported.bootLocalGuest();
69
- const factory = (module) => {
70
- module.exports = definition;
71
- };
72
- return { ok: true, guest, factory };
73
- });
74
- }
75
- function createSession(send, boot) {
76
- let guest = null;
77
- let started = false;
78
- let done = false;
79
- let timer;
80
- let timerAt = Number.POSITIVE_INFINITY;
81
- const stop = () => {
82
- done = true;
83
- if (timer !== undefined)
84
- clearTimeout(timer);
85
- timer = undefined;
86
- };
87
- const fail = (code, message) => {
88
- if (done)
89
- return;
90
- stop();
91
- send(JSON.stringify({ type: 'failure', code, message, logs: [] }));
92
- };
93
- const schedule = (delay) => {
94
- if (done)
95
- return;
96
- const at = Date.now() + delay;
97
- if (timer !== undefined && timerAt <= at)
98
- return;
99
- if (timer !== undefined)
100
- clearTimeout(timer);
101
- timerAt = at;
102
- timer = setTimeout(pump, delay);
103
- };
104
- function pump() {
105
- timer = undefined;
106
- timerAt = Number.POSITIVE_INFINITY;
107
- if (done || !guest)
108
- return;
109
- let text;
110
- try {
111
- text = guest.poll();
112
- }
113
- catch (error) {
114
- fail('FATAL_ERROR', `connector runtime failed: ${guestErrorMessage(error)}`);
115
- return;
116
- }
117
- let parsed;
118
- try {
119
- parsed = typeof text === 'string' ? JSON.parse(text) : null;
120
- }
121
- catch {
122
- parsed = null;
123
- }
124
- if (!isRecord(parsed) || !Array.isArray(parsed.out)) {
125
- fail('FATAL_ERROR', 'connector runtime returned a malformed poll result');
126
- return;
127
- }
128
- for (const message of parsed.out) {
129
- if (!isRecord(message))
130
- continue;
131
- send(JSON.stringify(message));
132
- if (message.type === 'result' || message.type === 'failure') {
133
- stop();
134
- return;
135
- }
136
- }
137
- if (parsed.out.length > 0 || parsed.fired === true)
138
- schedule(0);
139
- else if (typeof parsed.wake === 'number' && parsed.wake >= 0)
140
- schedule(parsed.wake);
141
- }
142
- const execute = (guestSource, code, payload) => {
143
- const booted = boot(guestSource, code);
144
- if (!booted.ok) {
145
- fail(booted.code, booted.message);
146
- return;
147
- }
148
- guest = booted.guest;
149
- try {
150
- guest.start(payload, booted.factory);
151
- }
152
- catch (error) {
153
- fail('FATAL_ERROR', `connector runtime failed: ${guestErrorMessage(error)}`);
154
- return;
155
- }
156
- schedule(0);
157
- };
158
- return {
159
- handle(json) {
160
- if (done || typeof json !== 'string')
161
- return;
162
- let raw;
163
- try {
164
- raw = JSON.parse(json);
165
- }
166
- catch {
167
- return;
168
- }
169
- const message = parseParentMessage(raw);
170
- if (!message)
171
- return;
172
- if (message.type === 'execute') {
173
- if (started)
174
- return;
175
- started = true;
176
- execute(message.guest, message.code, JSON.stringify({ expect: message.expect, job: message.job }));
177
- return;
178
- }
179
- if (!guest)
180
- return;
181
- try {
182
- guest.handle(JSON.stringify(message));
183
- }
184
- catch (error) {
185
- fail('FATAL_ERROR', `connector runtime failed: ${guestErrorMessage(error)}`);
186
- return;
187
- }
188
- schedule(0);
189
- },
190
- };
191
- }
package/t.d.ts DELETED
@@ -1,18 +0,0 @@
1
- import type { ArrayType, FieldMeta, ObjectType, ScalarType, SdkType, SecretType } from './types.js';
2
- export declare const t: {
3
- string(meta?: FieldMeta): ScalarType;
4
- integer(meta?: FieldMeta): ScalarType;
5
- decimal(precision: number, scale: number, meta?: FieldMeta): ScalarType;
6
- boolean(meta?: FieldMeta): ScalarType;
7
- date(meta?: FieldMeta): ScalarType;
8
- instant(meta?: FieldMeta): ScalarType;
9
- json(meta?: FieldMeta): ScalarType;
10
- /** `placeholder` 只对标量与 `t.secret()` 有意义,写在 object 上会被校验报出。 */
11
- object(shape: Record<string, SdkType>, meta?: FieldMeta): ObjectType;
12
- /** `placeholder` 只对标量与 `t.secret()` 有意义,写在 array 上会被校验报出。 */
13
- array(item: SdkType, meta?: FieldMeta): ArrayType;
14
- /** 凭证字段,只能放在连接配置顶层;`exposeToScript` 让脚本可取明文(审阅标记,不是安全边界)。 */
15
- secret(options?: {
16
- exposeToScript?: boolean;
17
- } & FieldMeta): SecretType;
18
- };
package/t.js DELETED
@@ -1,26 +0,0 @@
1
- const META_KEYS = ['title', 'description', 'placeholder'];
2
- function build(base, meta) {
3
- const out = { ...base };
4
- if (meta) {
5
- for (const key of META_KEYS) {
6
- if (meta[key] !== undefined)
7
- out[key] = meta[key];
8
- }
9
- }
10
- return Object.freeze(out);
11
- }
12
- function scalar(kind, meta) {
13
- return build({ kind }, meta);
14
- }
15
- export const t = Object.freeze({
16
- string: (meta) => scalar('string', meta),
17
- integer: (meta) => scalar('integer', meta),
18
- decimal: (precision, scale, meta) => build({ kind: 'decimal', precision, scale }, meta),
19
- boolean: (meta) => scalar('boolean', meta),
20
- date: (meta) => scalar('date', meta),
21
- instant: (meta) => scalar('instant', meta),
22
- json: (meta) => scalar('json', meta),
23
- object: (shape, meta) => build({ kind: 'object', shape: Object.freeze({ ...shape }) }, meta),
24
- array: (item, meta) => build({ kind: 'array', item }, meta),
25
- secret: (options) => build(options?.exposeToScript === true ? { kind: 'secret', exposeToScript: true } : { kind: 'secret' }, options),
26
- });
package/testing/child.cjs DELETED
@@ -1,350 +0,0 @@
1
- var __create = Object.create;
2
- var __getProtoOf = Object.getPrototypeOf;
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- function __accessProp(key) {
7
- return this[key];
8
- }
9
- var __toESMCache_node;
10
- var __toESMCache_esm;
11
- var __toESM = (mod, isNodeMode, target) => {
12
- var canCache = mod != null && typeof mod === "object";
13
- if (canCache) {
14
- var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
15
- var cached = cache.get(mod);
16
- if (cached)
17
- return cached;
18
- }
19
- target = mod != null ? __create(__getProtoOf(mod)) : {};
20
- const to = isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
21
- if (mod && typeof mod === "object" || typeof mod === "function") {
22
- for (let key of __getOwnPropNames(mod))
23
- if (!__hasOwnProp.call(to, key))
24
- __defProp(to, key, {
25
- get: __accessProp.bind(mod, key),
26
- enumerable: true
27
- });
28
- }
29
- if (canCache)
30
- cache.set(mod, to);
31
- return to;
32
- };
33
-
34
- // src/runtime/session.ts
35
- var import_node_vm = __toESM(require("node:vm"), 1);
36
-
37
- // src/runtime/protocol.ts
38
- var HOST_METHODS = Object.freeze([
39
- "definition",
40
- "declaration",
41
- "http",
42
- "db.query",
43
- "db.next",
44
- "db.close",
45
- "crypto",
46
- "cache.get",
47
- "cache.set",
48
- "secret.reveal",
49
- "emit"
50
- ]);
51
- var GUEST_FAILURE_CODES = Object.freeze([
52
- "CONNECTOR_LOAD_FAILED",
53
- "CONFIG_INVALID",
54
- "SCHEMA_ERROR",
55
- "FATAL_ERROR",
56
- "AUTH_ERROR",
57
- "TRANSIENT_ERROR",
58
- "PUSH_UNKNOWN",
59
- "CANCELED"
60
- ]);
61
- var HTTP_METHODS = new Set(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"]);
62
- var BODY_ENCODINGS = new Set(["utf8", "base64"]);
63
- var DIGEST_ENCODINGS = new Set(["hex", "base64"]);
64
- var HMAC_ALGORITHMS = new Set(["sha1", "sha256", "sha512"]);
65
- var HASH_ALGORITHMS = new Set(["md5", "sha1", "sha256", "sha512"]);
66
- var LOG_LEVELS = new Set(["info", "warn", "error"]);
67
- var SYNC_MODES = new Set(["full", "incremental"]);
68
- var SNAPSHOTS = new Set(["complete", "partial"]);
69
- var HOST_METHOD_SET = new Set(HOST_METHODS);
70
- var FAILURE_CODE_SET = new Set(GUEST_FAILURE_CODES);
71
- function isRecord(value) {
72
- return typeof value === "object" && value !== null && !Array.isArray(value);
73
- }
74
- function isStringArray(value) {
75
- return Array.isArray(value) && value.every((item) => typeof item === "string");
76
- }
77
- function isStringRecord(value) {
78
- return isRecord(value) && Object.values(value).every((item) => typeof item === "string");
79
- }
80
- function isId(value) {
81
- return typeof value === "number" && Number.isSafeInteger(value) && value > 0;
82
- }
83
- function parsePushRequest(value) {
84
- if (!isRecord(value))
85
- return null;
86
- const { requestId, push, receivedAt, headers, body } = value;
87
- if (typeof requestId !== "string" || typeof push !== "string" || typeof receivedAt !== "string" || !isStringRecord(headers) || typeof body !== "string")
88
- return null;
89
- return { requestId, push, receivedAt, headers: { ...headers }, body };
90
- }
91
- function parseJob(value) {
92
- if (!isRecord(value) || !isRecord(value.config) || !isStringArray(value.secretFields))
93
- return null;
94
- const config = value.config;
95
- const secretFields = [...value.secretFields];
96
- if (value.kind === "push") {
97
- if (!Array.isArray(value.requests))
98
- return null;
99
- const requests = [];
100
- for (const item of value.requests) {
101
- const request = parsePushRequest(item);
102
- if (!request)
103
- return null;
104
- requests.push(request);
105
- }
106
- return { kind: "push", config, secretFields, requests };
107
- }
108
- if (value.kind === "discover")
109
- return { kind: "discover", config, secretFields };
110
- if (value.kind === "check")
111
- return { kind: "check", config, secretFields };
112
- if (value.kind === "pull") {
113
- const { stream, mode, streamConfig, tables, discoverFirst } = value;
114
- if (typeof stream !== "string" || typeof mode !== "string" || !SYNC_MODES.has(mode) || !isRecord(streamConfig) || !isStringArray(tables))
115
- return null;
116
- if (discoverFirst !== undefined && typeof discoverFirst !== "boolean")
117
- return null;
118
- return {
119
- kind: "pull",
120
- config,
121
- secretFields,
122
- stream,
123
- mode,
124
- state: value.state,
125
- streamConfig,
126
- tables: [...tables],
127
- ...discoverFirst === true ? { discoverFirst: true } : {}
128
- };
129
- }
130
- return null;
131
- }
132
- function parseHostError(value) {
133
- if (!isRecord(value) || typeof value.code !== "string" || typeof value.message !== "string")
134
- return null;
135
- const error = { code: value.code, message: value.message };
136
- if (value.status !== undefined) {
137
- if (typeof value.status !== "number" || !Number.isInteger(value.status))
138
- return null;
139
- error.status = value.status;
140
- }
141
- if (value.retryAfterMs !== undefined) {
142
- if (typeof value.retryAfterMs !== "number" || !Number.isFinite(value.retryAfterMs) || value.retryAfterMs < 0)
143
- return null;
144
- error.retryAfterMs = value.retryAfterMs;
145
- }
146
- return error;
147
- }
148
- function parseParentMessage(value) {
149
- if (!isRecord(value))
150
- return null;
151
- switch (value.type) {
152
- case "execute": {
153
- const { guest, code, expect } = value;
154
- if (typeof guest !== "string" || typeof code !== "string")
155
- return null;
156
- if (!isRecord(expect) || typeof expect.name !== "string" || typeof expect.version !== "string")
157
- return null;
158
- const job = parseJob(value.job);
159
- return job ? { type: "execute", guest, code, expect: { name: expect.name, version: expect.version }, job } : null;
160
- }
161
- case "reply": {
162
- if (!isId(value.id))
163
- return null;
164
- if (value.ok === true)
165
- return { type: "reply", id: value.id, ok: true, value: value.value };
166
- if (value.ok !== false)
167
- return null;
168
- const error = parseHostError(value.error);
169
- return error ? { type: "reply", id: value.id, ok: false, error } : null;
170
- }
171
- case "cancel":
172
- return { type: "cancel" };
173
- default:
174
- return null;
175
- }
176
- }
177
-
178
- // src/runtime/session.ts
179
- var GUEST_FILENAME = "ontos-guest.js";
180
- var CONNECTOR_FILENAME = "connector.js";
181
- function isRecord2(value) {
182
- return typeof value === "object" && value !== null && !Array.isArray(value);
183
- }
184
- function guestErrorMessage(error) {
185
- try {
186
- if (error !== null && typeof error === "object") {
187
- const message = error.message;
188
- if (typeof message === "string")
189
- return message;
190
- }
191
- } catch {}
192
- return "unknown error";
193
- }
194
- function bootGuest(context, guestSource) {
195
- const wrapped = `(() => { const module = { exports: {} }; const exports = module.exports; ${guestSource}
196
- ; return module.exports; })()`;
197
- const exported = new import_node_vm.default.Script(wrapped, { filename: GUEST_FILENAME }).runInContext(context);
198
- const boot = exported.bootGuest;
199
- if (typeof boot !== "function")
200
- throw new Error("guest runtime does not export bootGuest");
201
- const api = boot();
202
- const { start, handle, poll } = api;
203
- if (typeof start !== "function" || typeof handle !== "function" || typeof poll !== "function")
204
- throw new Error("guest runtime returned an incomplete API");
205
- return { start, handle, poll };
206
- }
207
- function bootInVm(guestSource, code) {
208
- const context = import_node_vm.default.createContext(Object.create(null), { codeGeneration: { strings: false, wasm: false } });
209
- let guest;
210
- try {
211
- guest = bootGuest(context, guestSource);
212
- } catch (error) {
213
- return { ok: false, code: "CONNECTOR_LOAD_FAILED", message: `connector runtime failed to start: ${guestErrorMessage(error)}` };
214
- }
215
- try {
216
- const factory = new import_node_vm.default.Script(`(function (module, exports, require) {${code}
217
- })`, { filename: CONNECTOR_FILENAME }).runInContext(context);
218
- return { ok: true, guest, factory };
219
- } catch (error) {
220
- return { ok: false, code: "CONNECTOR_LOAD_FAILED", message: `connector code failed to compile: ${guestErrorMessage(error)}` };
221
- }
222
- }
223
- function createGuestSession(send) {
224
- return createSession(send, bootInVm);
225
- }
226
- function createSession(send, boot) {
227
- let guest = null;
228
- let started = false;
229
- let done = false;
230
- let timer;
231
- let timerAt = Number.POSITIVE_INFINITY;
232
- const stop = () => {
233
- done = true;
234
- if (timer !== undefined)
235
- clearTimeout(timer);
236
- timer = undefined;
237
- };
238
- const fail = (code, message) => {
239
- if (done)
240
- return;
241
- stop();
242
- send(JSON.stringify({ type: "failure", code, message, logs: [] }));
243
- };
244
- const schedule = (delay) => {
245
- if (done)
246
- return;
247
- const at = Date.now() + delay;
248
- if (timer !== undefined && timerAt <= at)
249
- return;
250
- if (timer !== undefined)
251
- clearTimeout(timer);
252
- timerAt = at;
253
- timer = setTimeout(pump, delay);
254
- };
255
- function pump() {
256
- timer = undefined;
257
- timerAt = Number.POSITIVE_INFINITY;
258
- if (done || !guest)
259
- return;
260
- let text;
261
- try {
262
- text = guest.poll();
263
- } catch (error) {
264
- fail("FATAL_ERROR", `connector runtime failed: ${guestErrorMessage(error)}`);
265
- return;
266
- }
267
- let parsed;
268
- try {
269
- parsed = typeof text === "string" ? JSON.parse(text) : null;
270
- } catch {
271
- parsed = null;
272
- }
273
- if (!isRecord2(parsed) || !Array.isArray(parsed.out)) {
274
- fail("FATAL_ERROR", "connector runtime returned a malformed poll result");
275
- return;
276
- }
277
- for (const message of parsed.out) {
278
- if (!isRecord2(message))
279
- continue;
280
- send(JSON.stringify(message));
281
- if (message.type === "result" || message.type === "failure") {
282
- stop();
283
- return;
284
- }
285
- }
286
- if (parsed.out.length > 0 || parsed.fired === true)
287
- schedule(0);
288
- else if (typeof parsed.wake === "number" && parsed.wake >= 0)
289
- schedule(parsed.wake);
290
- }
291
- const execute = (guestSource, code, payload) => {
292
- const booted = boot(guestSource, code);
293
- if (!booted.ok) {
294
- fail(booted.code, booted.message);
295
- return;
296
- }
297
- guest = booted.guest;
298
- try {
299
- guest.start(payload, booted.factory);
300
- } catch (error) {
301
- fail("FATAL_ERROR", `connector runtime failed: ${guestErrorMessage(error)}`);
302
- return;
303
- }
304
- schedule(0);
305
- };
306
- return {
307
- handle(json) {
308
- if (done || typeof json !== "string")
309
- return;
310
- let raw;
311
- try {
312
- raw = JSON.parse(json);
313
- } catch {
314
- return;
315
- }
316
- const message = parseParentMessage(raw);
317
- if (!message)
318
- return;
319
- if (message.type === "execute") {
320
- if (started)
321
- return;
322
- started = true;
323
- execute(message.guest, message.code, JSON.stringify({ expect: message.expect, job: message.job }));
324
- return;
325
- }
326
- if (!guest)
327
- return;
328
- try {
329
- guest.handle(JSON.stringify(message));
330
- } catch (error) {
331
- fail("FATAL_ERROR", `connector runtime failed: ${guestErrorMessage(error)}`);
332
- return;
333
- }
334
- schedule(0);
335
- }
336
- };
337
- }
338
-
339
- // src/testing/child.ts
340
- var session = createGuestSession((json) => {
341
- process.send?.(json);
342
- });
343
- process.on("message", (raw) => {
344
- if (typeof raw === "string")
345
- session.handle(raw);
346
- });
347
- process.on("disconnect", () => {
348
- process.exit(0);
349
- });
350
- process.send?.(JSON.stringify({ type: "ready" }));
@@ -1 +0,0 @@
1
- export {};
package/testing/child.js DELETED
@@ -1,12 +0,0 @@
1
- import { createGuestSession } from '../runtime/session.js';
2
- const session = createGuestSession((json) => {
3
- process.send?.(json);
4
- });
5
- process.on('message', (raw) => {
6
- if (typeof raw === 'string')
7
- session.handle(raw);
8
- });
9
- process.on('disconnect', () => {
10
- process.exit(0);
11
- });
12
- process.send?.(JSON.stringify({ type: 'ready' }));
@@ -1,41 +0,0 @@
1
- import type { LocalRunResult } from './types.js';
2
- /** 合规检查码(与 shared 的 `ComplianceCode` 同值)。 */
3
- export type ComplianceCode = 'PRIMARY_KEY_DUPLICATE' | 'RECORD_SCHEMA_MISMATCH' | 'UNDECLARED_TABLE' | 'CHECKPOINT_NOT_MONOTONIC' | 'FULL_READ_NOT_REPEATABLE' | 'FIXTURE_EXCHANGE_UNUSED' | 'FIXTURE_EXCHANGE_MISMATCH' | 'EXPECTATION_FAILED';
4
- export declare const COMPLIANCE_CODES: readonly ComplianceCode[];
5
- export interface ComplianceFinding {
6
- code: ComplianceCode;
7
- message: string;
8
- table?: string;
9
- detail?: unknown;
10
- }
11
- export interface ComplianceInput {
12
- declaration: {
13
- discover: unknown;
14
- } | null;
15
- stream?: string;
16
- /** 推送用例:只看该推送声明的表(`stream` 优先)。 */
17
- push?: string;
18
- mode?: 'full' | 'incremental';
19
- result: LocalRunResult;
20
- rerun?: LocalRunResult;
21
- }
22
- interface DeclaredColumn {
23
- kind: string;
24
- precision?: number;
25
- scale?: number;
26
- }
27
- interface DeclaredTable {
28
- columns: Record<string, DeclaredColumn>;
29
- primaryKey: string[];
30
- }
31
- /** 用例范围内声明的表;声明缺失时为 null(跳过依赖声明的检查)。 */
32
- export declare function scopedTables(declaration: {
33
- discover: unknown;
34
- } | null, scope: {
35
- stream?: string;
36
- push?: string;
37
- }): Record<string, DeclaredTable> | null;
38
- /** 值不符合列类型时返回原因,符合(含空值)时返回 null。 */
39
- export declare function columnValueProblem(type: DeclaredColumn, value: unknown): string | null;
40
- export declare function checkCompliance(input: ComplianceInput): ComplianceFinding[];
41
- export {};