@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.
- package/README.md +158 -139
- package/package.json +19 -28
- package/scripts/build.ts +188 -0
- package/src/define.ts +267 -0
- package/src/descriptor.ts +116 -0
- package/src/errors.ts +105 -0
- package/src/index.ts +108 -0
- package/src/internal/location.ts +23 -0
- package/src/runtime/guest/bridge.ts +238 -0
- package/src/runtime/guest/cache.ts +43 -0
- package/src/runtime/guest/check.ts +17 -0
- package/src/runtime/guest/crypto.ts +166 -0
- package/src/runtime/guest/ctx.ts +157 -0
- package/src/runtime/guest/db.ts +59 -0
- package/src/runtime/guest/failure.ts +69 -0
- package/src/runtime/guest/http.ts +194 -0
- package/src/runtime/guest/index.ts +102 -0
- package/{runtime/guest/limits.d.ts → src/runtime/guest/limits.ts} +12 -12
- package/src/runtime/guest/loader.ts +210 -0
- package/src/runtime/guest/log.ts +47 -0
- package/src/runtime/guest/map.ts +45 -0
- package/src/runtime/guest/operation.ts +193 -0
- package/src/runtime/guest/paginate.ts +169 -0
- package/src/runtime/guest/pull.ts +177 -0
- package/src/runtime/guest/push.ts +92 -0
- package/src/runtime/guest/signal.ts +14 -0
- package/src/runtime/guest/suggest.ts +51 -0
- package/src/runtime/guest/time.ts +137 -0
- package/src/runtime/guest-runtime.ts +75 -0
- package/src/runtime/index.ts +53 -0
- package/src/runtime/protocol.ts +557 -0
- package/src/runtime/session.ts +216 -0
- package/src/testing/child.ts +21 -0
- package/src/testing/compliance.ts +343 -0
- package/src/testing/engine.ts +445 -0
- package/src/testing/fixtures.ts +591 -0
- package/src/testing/gateway.ts +482 -0
- package/src/testing/import-guard.ts +221 -0
- package/src/testing/index.ts +43 -0
- package/src/testing/local-db.ts +226 -0
- package/src/testing/mock-external-system.ts +249 -0
- package/src/testing/pinned-http.ts +144 -0
- package/src/testing/recorder.ts +140 -0
- package/src/testing/redaction.ts +94 -0
- package/src/testing/replay.ts +437 -0
- package/src/testing/run-tests.ts +115 -0
- package/src/testing/schema.ts +70 -0
- package/src/testing/source-loader.ts +374 -0
- package/src/testing/types.ts +78 -0
- package/src/types.ts +329 -0
- package/src/validate.ts +267 -0
- package/test/build.test.ts +159 -0
- package/test/define.test-d.ts +182 -0
- package/test/fixtures/acceptance.ts +156 -0
- package/test/fixtures/connectors/http-pull/.ontos/descriptor.json +72 -0
- package/test/fixtures/connectors/http-pull/.ontos/types.ts +61 -0
- package/test/fixtures/connectors/http-pull/package.json +5 -0
- package/test/fixtures/connectors/http-pull/streams/items/index.ts +37 -0
- package/test/fixtures/connectors/http-pull/streams/items/map.ts +12 -0
- package/test/fixtures/connectors/http-pull/streams/items/tests/basic/case.json +15 -0
- package/test/fixtures/connectors/http-pull/streams/items/tests/basic/exchanges.ndjson +2 -0
- package/test/fixtures/connectors/pg-pull/.ontos/descriptor.json +151 -0
- package/test/fixtures/connectors/pg-pull/.ontos/types.ts +62 -0
- package/test/fixtures/connectors/pg-pull/lib/sql.ts +14 -0
- package/test/fixtures/connectors/pg-pull/package.json +5 -0
- package/test/fixtures/connectors/pg-pull/streams/customers/index.ts +30 -0
- package/test/fixtures/connectors/pg-pull/streams/customers/tests/customers-full/case.json +15 -0
- package/test/fixtures/connectors/pg-pull/streams/customers/tests/customers-full/exchanges.ndjson +3 -0
- package/test/fixtures/connectors/pg-pull/streams/orders/index.ts +32 -0
- package/test/fixtures/descriptor.ts +54 -0
- package/test/fixtures/kingdee-example.ts +82 -0
- package/test/fixtures/record-acceptance.ts +32 -0
- package/test/runtime/ctx.test.ts +434 -0
- package/test/runtime/helpers.ts +157 -0
- package/test/runtime/jobs.test.ts +444 -0
- package/test/runtime/operation.test.ts +175 -0
- package/test/runtime/session.test.ts +229 -0
- package/test/testing/acceptance-fixtures.test.ts +69 -0
- package/test/testing/compliance.test.ts +247 -0
- package/test/testing/engine.test.ts +283 -0
- package/test/testing/fixtures.test.ts +221 -0
- package/test/testing/gateway.test.ts +337 -0
- package/test/testing/import-guard.test.ts +45 -0
- package/test/testing/mock-external-system.test.ts +144 -0
- package/test/testing/operation-fixtures.test.ts +160 -0
- package/test/testing/recorder.test.ts +163 -0
- package/test/testing/redaction.test.ts +54 -0
- package/test/testing/replay.test.ts +207 -0
- package/test/testing/run-tests.test.ts +137 -0
- package/test/testing/source-loader.test.ts +220 -0
- package/test/validate-config.test.ts +104 -0
- package/tsconfig.build.json +23 -0
- package/tsconfig.json +19 -0
- package/cjs/index.cjs +0 -1015
- package/cjs/runtime.cjs +0 -752
- package/cjs/testing.cjs +0 -3591
- package/errors.d.ts +0 -17
- package/errors.js +0 -53
- package/guest.cjs +0 -2659
- package/index.d.ts +0 -13
- package/index.js +0 -7
- package/internal/location.d.ts +0 -6
- package/internal/location.js +0 -8
- package/presentation.d.ts +0 -38
- package/presentation.js +0 -268
- package/runtime/guest/bridge.d.ts +0 -53
- package/runtime/guest/bridge.js +0 -187
- package/runtime/guest/cache.d.ts +0 -3
- package/runtime/guest/cache.js +0 -42
- package/runtime/guest/crypto.d.ts +0 -13
- package/runtime/guest/crypto.js +0 -177
- package/runtime/guest/ctx.d.ts +0 -30
- package/runtime/guest/ctx.js +0 -124
- package/runtime/guest/db.d.ts +0 -6
- package/runtime/guest/db.js +0 -59
- package/runtime/guest/discover.d.ts +0 -29
- package/runtime/guest/discover.js +0 -51
- package/runtime/guest/failure.d.ts +0 -24
- package/runtime/guest/failure.js +0 -63
- package/runtime/guest/http.d.ts +0 -4
- package/runtime/guest/http.js +0 -204
- package/runtime/guest/index.d.ts +0 -12
- package/runtime/guest/index.js +0 -89
- package/runtime/guest/limits.js +0 -12
- package/runtime/guest/loader.d.ts +0 -15
- package/runtime/guest/loader.js +0 -42
- package/runtime/guest/log.d.ts +0 -10
- package/runtime/guest/log.js +0 -36
- package/runtime/guest/map.d.ts +0 -4
- package/runtime/guest/map.js +0 -45
- package/runtime/guest/paginate.d.ts +0 -6
- package/runtime/guest/paginate.js +0 -180
- package/runtime/guest/pull.d.ts +0 -12
- package/runtime/guest/pull.js +0 -150
- package/runtime/guest/push.d.ts +0 -18
- package/runtime/guest/push.js +0 -80
- package/runtime/guest/signal.d.ts +0 -3
- package/runtime/guest/signal.js +0 -12
- package/runtime/guest/time.d.ts +0 -5
- package/runtime/guest/time.js +0 -127
- package/runtime/guest-runtime.d.ts +0 -7
- package/runtime/guest-runtime.js +0 -51
- package/runtime/index.d.ts +0 -18
- package/runtime/index.js +0 -3
- package/runtime/protocol.d.ts +0 -276
- package/runtime/protocol.js +0 -409
- package/runtime/session.d.ts +0 -12
- package/runtime/session.js +0 -191
- package/t.d.ts +0 -18
- package/t.js +0 -26
- package/testing/child.cjs +0 -350
- package/testing/child.d.ts +0 -1
- package/testing/child.js +0 -12
- package/testing/compliance.d.ts +0 -41
- package/testing/compliance.js +0 -291
- package/testing/engine.d.ts +0 -66
- package/testing/engine.js +0 -339
- package/testing/fixtures.d.ts +0 -145
- package/testing/fixtures.js +0 -407
- package/testing/gateway.d.ts +0 -65
- package/testing/gateway.js +0 -402
- package/testing/import-guard.d.ts +0 -57
- package/testing/import-guard.js +0 -187
- package/testing/index.d.ts +0 -33
- package/testing/index.js +0 -10
- package/testing/local-db.d.ts +0 -27
- package/testing/local-db.js +0 -181
- package/testing/pinned-http.d.ts +0 -14
- package/testing/pinned-http.js +0 -125
- package/testing/recorder.d.ts +0 -15
- package/testing/recorder.js +0 -116
- package/testing/redaction.d.ts +0 -28
- package/testing/redaction.js +0 -67
- package/testing/replay.d.ts +0 -50
- package/testing/replay.js +0 -313
- package/testing/schema.d.ts +0 -13
- package/testing/schema.js +0 -65
- package/testing/source-loader.d.ts +0 -19
- package/testing/source-loader.js +0 -238
- package/testing/types.d.ts +0 -79
- package/testing/types.js +0 -18
- package/types.d.ts +0 -302
- package/types.js +0 -13
- package/validate.d.ts +0 -78
- package/validate.js +0 -597
package/runtime/guest/pull.d.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import type { ConnectorDefinition } from '../../types.js';
|
|
2
|
-
import type { GuestJob, GuestResult } from '../protocol.js';
|
|
3
|
-
import { type ContextOptions } from './ctx.js';
|
|
4
|
-
import { type FailureMessage } from './failure.js';
|
|
5
|
-
type PullJob = Extract<GuestJob, {
|
|
6
|
-
kind: 'pull';
|
|
7
|
-
}>;
|
|
8
|
-
export declare function runPull(definition: ConnectorDefinition, job: PullJob, options: ContextOptions): Promise<{
|
|
9
|
-
type: 'result';
|
|
10
|
-
result: GuestResult;
|
|
11
|
-
} | FailureMessage>;
|
|
12
|
-
export {};
|
package/runtime/guest/pull.js
DELETED
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
import { errors } from '../../errors.js';
|
|
2
|
-
import { call, isAborted, isStopped, jsonParse, jsonStringify, requestStop } from './bridge.js';
|
|
3
|
-
import { utf8Length } from './crypto.js';
|
|
4
|
-
import { createPullContext, deepFreeze } from './ctx.js';
|
|
5
|
-
import { failure, failureFromError } from './failure.js';
|
|
6
|
-
import { PULL_BATCH_MAX_BYTES, PULL_BATCH_MAX_RECORDS, PULL_REJECT_THRESHOLD } from './limits.js';
|
|
7
|
-
function isRecord(value) {
|
|
8
|
-
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
9
|
-
}
|
|
10
|
-
function copy(value, what) {
|
|
11
|
-
let text;
|
|
12
|
-
try {
|
|
13
|
-
text = jsonStringify(value);
|
|
14
|
-
}
|
|
15
|
-
catch (error) {
|
|
16
|
-
throw new errors.SchemaError(`${what} is not serializable: ${error instanceof Error ? error.message : String(error)}`);
|
|
17
|
-
}
|
|
18
|
-
if (typeof text !== 'string')
|
|
19
|
-
throw new errors.SchemaError(`${what} is not serializable`);
|
|
20
|
-
return { value: jsonParse(text), text };
|
|
21
|
-
}
|
|
22
|
-
const BATCH_FRAME_BYTES = 5;
|
|
23
|
-
const ITEM_FRAME_BYTES = 17 + 1;
|
|
24
|
-
function itemBytes(table, valueText) {
|
|
25
|
-
return ITEM_FRAME_BYTES + utf8Length(jsonStringify(table)) + utf8Length(valueText);
|
|
26
|
-
}
|
|
27
|
-
function emptyBatch() {
|
|
28
|
-
return { records: [], removals: [], checkpoint: undefined, bytes: BATCH_FRAME_BYTES, dropped: 0 };
|
|
29
|
-
}
|
|
30
|
-
function closeIterator(iterator) {
|
|
31
|
-
try {
|
|
32
|
-
const closing = iterator.return?.();
|
|
33
|
-
if (closing && typeof closing.then === 'function')
|
|
34
|
-
closing.then(undefined, () => undefined);
|
|
35
|
-
}
|
|
36
|
-
catch {
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
export async function runPull(definition, job, options) {
|
|
40
|
-
const streams = definition.streams ?? {};
|
|
41
|
-
const stream = streams[job.stream];
|
|
42
|
-
const ctx = createPullContext(options);
|
|
43
|
-
const input = Object.freeze({
|
|
44
|
-
mode: job.mode,
|
|
45
|
-
state: job.state === null ? undefined : deepFreeze(job.state),
|
|
46
|
-
streamConfig: deepFreeze(job.streamConfig),
|
|
47
|
-
});
|
|
48
|
-
const tables = new Set(job.tables);
|
|
49
|
-
let batch = emptyBatch();
|
|
50
|
-
let emitted = 0;
|
|
51
|
-
const flush = async () => {
|
|
52
|
-
const payload = {
|
|
53
|
-
records: batch.records,
|
|
54
|
-
removals: batch.removals,
|
|
55
|
-
...(batch.checkpoint ? { checkpoint: batch.checkpoint } : {}),
|
|
56
|
-
logs: options.logs.drain(),
|
|
57
|
-
...(batch.dropped > 0 ? { dropped: batch.dropped } : {}),
|
|
58
|
-
};
|
|
59
|
-
batch = emptyBatch();
|
|
60
|
-
const reply = await call('emit', payload);
|
|
61
|
-
emitted += 1;
|
|
62
|
-
if (isRecord(reply) && reply.stop === true)
|
|
63
|
-
requestStop();
|
|
64
|
-
};
|
|
65
|
-
const result = () => ({
|
|
66
|
-
type: 'result',
|
|
67
|
-
result: { kind: 'pull', emittedBatches: emitted, ...(isStopped() ? { stopped: true } : {}), logs: options.logs.drain() },
|
|
68
|
-
});
|
|
69
|
-
let oversized = 0;
|
|
70
|
-
const log = options.logs.logger();
|
|
71
|
-
const add = async (what, table, size, push) => {
|
|
72
|
-
if (BATCH_FRAME_BYTES + size > PULL_BATCH_MAX_BYTES) {
|
|
73
|
-
oversized += 1;
|
|
74
|
-
batch.dropped += 1;
|
|
75
|
-
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`);
|
|
76
|
-
if (oversized > PULL_REJECT_THRESHOLD) {
|
|
77
|
-
throw new errors.SchemaError(`stream ${job.stream} produced ${oversized} records larger than ${PULL_BATCH_MAX_BYTES} bytes, more than ${PULL_REJECT_THRESHOLD}`);
|
|
78
|
-
}
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
if (batch.records.length + batch.removals.length > 0 && batch.bytes + size > PULL_BATCH_MAX_BYTES)
|
|
82
|
-
await flush();
|
|
83
|
-
push();
|
|
84
|
-
batch.bytes += size;
|
|
85
|
-
if (batch.records.length + batch.removals.length >= PULL_BATCH_MAX_RECORDS)
|
|
86
|
-
await flush();
|
|
87
|
-
};
|
|
88
|
-
const checkTable = (table) => {
|
|
89
|
-
if (typeof table !== 'string' || !tables.has(table)) {
|
|
90
|
-
throw new errors.SchemaError(`stream ${job.stream} produced a record for table ${String(table)}, which is not declared for this stream`);
|
|
91
|
-
}
|
|
92
|
-
return table;
|
|
93
|
-
};
|
|
94
|
-
let iterator;
|
|
95
|
-
try {
|
|
96
|
-
const iterable = Reflect.apply(stream, streams, [ctx, input]);
|
|
97
|
-
const factory = isRecord(iterable) || typeof iterable === 'function' ? iterable[Symbol.asyncIterator] : undefined;
|
|
98
|
-
if (typeof factory !== 'function')
|
|
99
|
-
throw new errors.SchemaError(`stream ${job.stream} must be an async generator function`);
|
|
100
|
-
iterator = Reflect.apply(factory, iterable, []);
|
|
101
|
-
}
|
|
102
|
-
catch (error) {
|
|
103
|
-
return failureFromError(error, options.logs.drain());
|
|
104
|
-
}
|
|
105
|
-
try {
|
|
106
|
-
for (;;) {
|
|
107
|
-
if (isAborted()) {
|
|
108
|
-
closeIterator(iterator);
|
|
109
|
-
return isStopped() ? result() : failure('CANCELED', 'run was canceled', options.logs.drain());
|
|
110
|
-
}
|
|
111
|
-
const step = await iterator.next();
|
|
112
|
-
if (step.done)
|
|
113
|
-
break;
|
|
114
|
-
const item = step.value;
|
|
115
|
-
const kind = isRecord(item) ? item.__ontos : undefined;
|
|
116
|
-
if (kind === 'record' && isRecord(item)) {
|
|
117
|
-
const table = checkTable(item.table);
|
|
118
|
-
if (!isRecord(item.row))
|
|
119
|
-
throw new errors.SchemaError(`stream ${job.stream} produced a record whose row is not an object`);
|
|
120
|
-
const row = copy(item.row, 'row');
|
|
121
|
-
await add('record', table, itemBytes(table, row.text), () => batch.records.push({ table, row: row.value }));
|
|
122
|
-
}
|
|
123
|
-
else if (kind === 'remove' && isRecord(item)) {
|
|
124
|
-
const table = checkTable(item.table);
|
|
125
|
-
if (!isRecord(item.key))
|
|
126
|
-
throw new errors.SchemaError(`stream ${job.stream} produced a removal whose key is not an object`);
|
|
127
|
-
const key = copy(item.key, 'key');
|
|
128
|
-
await add('removal', table, itemBytes(table, key.text), () => batch.removals.push({ table, key: key.value }));
|
|
129
|
-
}
|
|
130
|
-
else if (kind === 'checkpoint' && isRecord(item)) {
|
|
131
|
-
batch.checkpoint = { state: copy(item.state, 'checkpoint state').value };
|
|
132
|
-
await flush();
|
|
133
|
-
}
|
|
134
|
-
else {
|
|
135
|
-
throw new errors.SchemaError(`stream ${job.stream} yielded a value that is not ctx.record(), ctx.remove() or ctx.checkpoint()`);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
if (isAborted())
|
|
139
|
-
return isStopped() ? result() : failure('CANCELED', 'run was canceled', options.logs.drain());
|
|
140
|
-
if (batch.records.length + batch.removals.length > 0 || batch.dropped > 0)
|
|
141
|
-
await flush();
|
|
142
|
-
}
|
|
143
|
-
catch (error) {
|
|
144
|
-
closeIterator(iterator);
|
|
145
|
-
if (isStopped())
|
|
146
|
-
return result();
|
|
147
|
-
return failureFromError(error, options.logs.drain());
|
|
148
|
-
}
|
|
149
|
-
return result();
|
|
150
|
-
}
|
package/runtime/guest/push.d.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 推送作业:对每条请求按推送名找处理器,先 `verify` 再 `map`。行为与第 1 期一致:
|
|
3
|
-
* - `ctx.record` / `ctx.remove` 直接记录,请求成功后才并入结果;请求结束后再用该 `ctx` 抛错;
|
|
4
|
-
* - 单条请求抛错只拒收该条(code 取错误的 `code`,普通异常为 `CONNECTOR_ERROR`),已产生的记录丢弃;
|
|
5
|
-
* - `SCHEMA_ERROR` / `FATAL_ERROR` 让整次执行失败;
|
|
6
|
-
* - 推送名在定义里不存在的请求拒收,code `PUSH_UNKNOWN`(定义只有按名字的写法 `push: { <名>: { verify?, map } }`)。
|
|
7
|
-
*/
|
|
8
|
-
import type { ConnectorDefinition } from '../../types.js';
|
|
9
|
-
import type { GuestJob, GuestPushResult } from '../protocol.js';
|
|
10
|
-
import { type FailureMessage } from './failure.js';
|
|
11
|
-
type PushJob = Extract<GuestJob, {
|
|
12
|
-
kind: 'push';
|
|
13
|
-
}>;
|
|
14
|
-
export declare function runPush(definition: ConnectorDefinition, job: PushJob): Promise<{
|
|
15
|
-
type: 'result';
|
|
16
|
-
result: GuestPushResult;
|
|
17
|
-
} | FailureMessage>;
|
|
18
|
-
export {};
|
package/runtime/guest/push.js
DELETED
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import { jsonParse, jsonStringify } from './bridge.js';
|
|
2
|
-
import { deepFreeze } from './ctx.js';
|
|
3
|
-
import { connectorStack, errorInfo, failure } from './failure.js';
|
|
4
|
-
import { createLogBuffer } from './log.js';
|
|
5
|
-
import { isoNow } from './time.js';
|
|
6
|
-
const WHOLE_RUN_FAILURES = ['SCHEMA_ERROR', 'FATAL_ERROR'];
|
|
7
|
-
function copyRecord(value, what) {
|
|
8
|
-
if (value === null || typeof value !== 'object' || Array.isArray(value))
|
|
9
|
-
throw new TypeError(`${what} must be an object`);
|
|
10
|
-
const text = jsonStringify(value);
|
|
11
|
-
if (typeof text !== 'string')
|
|
12
|
-
throw new TypeError(`${what} is not serializable`);
|
|
13
|
-
return jsonParse(text);
|
|
14
|
-
}
|
|
15
|
-
export async function runPush(definition, job) {
|
|
16
|
-
const handlers = definition.push !== null && typeof definition.push === 'object' ? definition.push : {};
|
|
17
|
-
if (Object.keys(handlers).length === 0)
|
|
18
|
-
return failure('CONNECTOR_LOAD_FAILED', 'connector does not support push', []);
|
|
19
|
-
const config = deepFreeze(job.config);
|
|
20
|
-
const logs = createLogBuffer();
|
|
21
|
-
const result = { kind: 'push', records: [], removals: [], rejected: [], logs: [] };
|
|
22
|
-
for (const request of job.requests) {
|
|
23
|
-
const requestId = request.requestId;
|
|
24
|
-
const handler = Object.hasOwn(handlers, request.push) ? handlers[request.push] : undefined;
|
|
25
|
-
if (!handler) {
|
|
26
|
-
result.rejected.push({ requestId, code: 'PUSH_UNKNOWN', message: `connector has no push named ${request.push}` });
|
|
27
|
-
continue;
|
|
28
|
-
}
|
|
29
|
-
const pending = { records: [], removals: [], open: true };
|
|
30
|
-
const assertOpen = () => {
|
|
31
|
-
if (!pending.open)
|
|
32
|
-
throw new TypeError('ctx is no longer usable after the request finished');
|
|
33
|
-
};
|
|
34
|
-
const ctx = Object.freeze({
|
|
35
|
-
config,
|
|
36
|
-
record(table, row) {
|
|
37
|
-
assertOpen();
|
|
38
|
-
if (typeof table !== 'string')
|
|
39
|
-
throw new TypeError('table must be a string');
|
|
40
|
-
pending.records.push({ requestId, table, row: copyRecord(row, 'row') });
|
|
41
|
-
},
|
|
42
|
-
remove(table, key) {
|
|
43
|
-
assertOpen();
|
|
44
|
-
if (typeof table !== 'string')
|
|
45
|
-
throw new TypeError('table must be a string');
|
|
46
|
-
pending.removals.push({ requestId, table, key: copyRecord(key, 'key') });
|
|
47
|
-
},
|
|
48
|
-
log: logs.logger(requestId, () => pending.open),
|
|
49
|
-
time: Object.freeze({ now: isoNow }),
|
|
50
|
-
});
|
|
51
|
-
const body = request.body;
|
|
52
|
-
const req = Object.freeze({
|
|
53
|
-
id: requestId,
|
|
54
|
-
receivedAt: request.receivedAt,
|
|
55
|
-
headers: deepFreeze({ ...request.headers }),
|
|
56
|
-
body,
|
|
57
|
-
json() {
|
|
58
|
-
return jsonParse(body);
|
|
59
|
-
},
|
|
60
|
-
});
|
|
61
|
-
try {
|
|
62
|
-
if (typeof handler.verify === 'function')
|
|
63
|
-
await Reflect.apply(handler.verify, handler, [ctx, req]);
|
|
64
|
-
await Reflect.apply(handler.map, handler, [ctx, req]);
|
|
65
|
-
pending.open = false;
|
|
66
|
-
result.records.push(...pending.records);
|
|
67
|
-
result.removals.push(...pending.removals);
|
|
68
|
-
}
|
|
69
|
-
catch (error) {
|
|
70
|
-
pending.open = false;
|
|
71
|
-
const info = errorInfo(error);
|
|
72
|
-
if (info.code !== null && WHOLE_RUN_FAILURES.includes(info.code)) {
|
|
73
|
-
return failure(info.code, info.message, logs.drain(), connectorStack(info.stack));
|
|
74
|
-
}
|
|
75
|
-
result.rejected.push({ requestId, code: info.code ?? 'CONNECTOR_ERROR', message: info.message });
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
result.logs = logs.drain();
|
|
79
|
-
return { type: 'result', result };
|
|
80
|
-
}
|
package/runtime/guest/signal.js
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { isAborted, onAbort } from './bridge.js';
|
|
2
|
-
export function createSignal() {
|
|
3
|
-
const signal = {
|
|
4
|
-
onAbort(fn) {
|
|
5
|
-
if (typeof fn !== 'function')
|
|
6
|
-
throw new TypeError('ctx.signal.onAbort expects a function');
|
|
7
|
-
onAbort(fn);
|
|
8
|
-
},
|
|
9
|
-
};
|
|
10
|
-
Object.defineProperty(signal, 'aborted', { get: isAborted, enumerable: true });
|
|
11
|
-
return Object.freeze(signal);
|
|
12
|
-
}
|
package/runtime/guest/time.d.ts
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
/** `ctx.time`:`now()` 与 `toUtc(本地时间文本, 时区, 格式?)`。时区换算用上下文自己的 `Intl.DateTimeFormat`。 */
|
|
2
|
-
import type { ConnectorTime } from '../../types.js';
|
|
3
|
-
export declare function isoNow(): string;
|
|
4
|
-
export declare function toUtc(local: string, timeZone: string, format?: string): string;
|
|
5
|
-
export declare function createTime(): ConnectorTime;
|
package/runtime/guest/time.js
DELETED
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
const DateCtor = Date;
|
|
2
|
-
const dateUtc = Date.UTC;
|
|
3
|
-
const dateParse = Date.parse;
|
|
4
|
-
const reflectApply = Reflect.apply;
|
|
5
|
-
const toISOString = Date.prototype.toISOString;
|
|
6
|
-
const DateTimeFormat = Intl.DateTimeFormat;
|
|
7
|
-
export function isoNow() {
|
|
8
|
-
return reflectApply(toISOString, new DateCtor(), []);
|
|
9
|
-
}
|
|
10
|
-
function iso(ms) {
|
|
11
|
-
return reflectApply(toISOString, new DateCtor(ms), []);
|
|
12
|
-
}
|
|
13
|
-
const DEFAULT_PATTERN = /^(\d{4})[-/](\d{1,2})[-/](\d{1,2})(?:[ T](\d{1,2}):(\d{2})(?::(\d{2})(?:\.(\d{1,9}))?)?)?$/;
|
|
14
|
-
const ZONED_PATTERN = /^\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(?:Z|[+-]\d{2}:?\d{2})$/i;
|
|
15
|
-
const TOKENS = [
|
|
16
|
-
{ token: 'YYYY', field: 'year', pattern: '(\\d{4})' },
|
|
17
|
-
{ token: 'SSS', field: 'millisecond', pattern: '(\\d{1,3})' },
|
|
18
|
-
{ token: 'MM', field: 'month', pattern: '(\\d{2})' },
|
|
19
|
-
{ token: 'DD', field: 'day', pattern: '(\\d{2})' },
|
|
20
|
-
{ token: 'HH', field: 'hour', pattern: '(\\d{2})' },
|
|
21
|
-
{ token: 'mm', field: 'minute', pattern: '(\\d{2})' },
|
|
22
|
-
{ token: 'ss', field: 'second', pattern: '(\\d{2})' },
|
|
23
|
-
{ token: 'M', field: 'month', pattern: '(\\d{1,2})' },
|
|
24
|
-
{ token: 'D', field: 'day', pattern: '(\\d{1,2})' },
|
|
25
|
-
{ token: 'H', field: 'hour', pattern: '(\\d{1,2})' },
|
|
26
|
-
];
|
|
27
|
-
function escapeRegExp(text) {
|
|
28
|
-
return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
29
|
-
}
|
|
30
|
-
function parseWithFormat(local, format) {
|
|
31
|
-
const fields = [];
|
|
32
|
-
let source = '';
|
|
33
|
-
let i = 0;
|
|
34
|
-
while (i < format.length) {
|
|
35
|
-
const match = TOKENS.find((entry) => format.startsWith(entry.token, i));
|
|
36
|
-
if (match) {
|
|
37
|
-
fields.push(match.field);
|
|
38
|
-
source += match.pattern;
|
|
39
|
-
i += match.token.length;
|
|
40
|
-
}
|
|
41
|
-
else {
|
|
42
|
-
source += escapeRegExp(format.charAt(i));
|
|
43
|
-
i += 1;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
const result = new RegExp(`^${source}$`).exec(local);
|
|
47
|
-
if (!result || !fields.includes('year') || !fields.includes('month') || !fields.includes('day'))
|
|
48
|
-
return null;
|
|
49
|
-
const clock = { year: 0, month: 0, day: 0, hour: 0, minute: 0, second: 0, millisecond: 0 };
|
|
50
|
-
fields.forEach((field, index) => {
|
|
51
|
-
const text = result[index + 1] ?? '0';
|
|
52
|
-
clock[field] = field === 'millisecond' ? Number(text.padEnd(3, '0')) : Number(text);
|
|
53
|
-
});
|
|
54
|
-
return clock;
|
|
55
|
-
}
|
|
56
|
-
function parseDefault(local) {
|
|
57
|
-
const match = DEFAULT_PATTERN.exec(local);
|
|
58
|
-
if (!match)
|
|
59
|
-
return null;
|
|
60
|
-
return {
|
|
61
|
-
year: Number(match[1]), month: Number(match[2]), day: Number(match[3]),
|
|
62
|
-
hour: Number(match[4] ?? 0), minute: Number(match[5] ?? 0), second: Number(match[6] ?? 0),
|
|
63
|
-
millisecond: Number((match[7] ?? '0').slice(0, 3).padEnd(3, '0')),
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
function wallMillis(clock) {
|
|
67
|
-
const ms = dateUtc(clock.year, clock.month - 1, clock.day, clock.hour, clock.minute, clock.second, clock.millisecond);
|
|
68
|
-
if (Number.isNaN(ms))
|
|
69
|
-
return null;
|
|
70
|
-
const check = new DateCtor(ms);
|
|
71
|
-
if (check.getUTCFullYear() !== clock.year || check.getUTCMonth() !== clock.month - 1 || check.getUTCDate() !== clock.day
|
|
72
|
-
|| check.getUTCHours() !== clock.hour || check.getUTCMinutes() !== clock.minute || check.getUTCSeconds() !== clock.second)
|
|
73
|
-
return null;
|
|
74
|
-
return ms;
|
|
75
|
-
}
|
|
76
|
-
const formatters = new Map();
|
|
77
|
-
function formatter(timeZone) {
|
|
78
|
-
let cached = formatters.get(timeZone);
|
|
79
|
-
if (!cached) {
|
|
80
|
-
try {
|
|
81
|
-
cached = new DateTimeFormat('en-US', {
|
|
82
|
-
timeZone, hourCycle: 'h23', year: 'numeric', month: 'numeric', day: 'numeric', hour: 'numeric', minute: 'numeric', second: 'numeric',
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
catch {
|
|
86
|
-
throw new TypeError(`unknown time zone ${timeZone}`);
|
|
87
|
-
}
|
|
88
|
-
formatters.set(timeZone, cached);
|
|
89
|
-
}
|
|
90
|
-
return cached;
|
|
91
|
-
}
|
|
92
|
-
function zoneOffset(timeZone, utcMs) {
|
|
93
|
-
const base = utcMs - (((utcMs % 1000) + 1000) % 1000);
|
|
94
|
-
const parts = {};
|
|
95
|
-
for (const part of formatter(timeZone).formatToParts(new DateCtor(base))) {
|
|
96
|
-
if (part.type !== 'literal')
|
|
97
|
-
parts[part.type] = Number(part.value);
|
|
98
|
-
}
|
|
99
|
-
const asUtc = dateUtc(parts.year ?? 0, (parts.month ?? 1) - 1, parts.day ?? 1, parts.hour ?? 0, parts.minute ?? 0, parts.second ?? 0);
|
|
100
|
-
return asUtc - base;
|
|
101
|
-
}
|
|
102
|
-
export function toUtc(local, timeZone, format) {
|
|
103
|
-
if (typeof local !== 'string')
|
|
104
|
-
throw new TypeError('ctx.time.toUtc expects a date-time string');
|
|
105
|
-
if (typeof timeZone !== 'string' || timeZone.length === 0)
|
|
106
|
-
throw new TypeError('ctx.time.toUtc expects a time zone like Asia/Shanghai');
|
|
107
|
-
const text = local.trim();
|
|
108
|
-
if (format === undefined && ZONED_PATTERN.test(text)) {
|
|
109
|
-
const ms = dateParse(text.replace(' ', 'T'));
|
|
110
|
-
if (Number.isNaN(ms))
|
|
111
|
-
throw new TypeError(`invalid date-time ${local}`);
|
|
112
|
-
return iso(ms);
|
|
113
|
-
}
|
|
114
|
-
const clock = format === undefined ? parseDefault(text) : parseWithFormat(text, format);
|
|
115
|
-
const wall = clock ? wallMillis(clock) : null;
|
|
116
|
-
if (wall === null)
|
|
117
|
-
throw new TypeError(`invalid date-time ${local}${format === undefined ? '' : ` for format ${format}`}`);
|
|
118
|
-
const first = zoneOffset(timeZone, wall);
|
|
119
|
-
let utc = wall - first;
|
|
120
|
-
const second = zoneOffset(timeZone, utc);
|
|
121
|
-
if (second !== first)
|
|
122
|
-
utc = wall - second;
|
|
123
|
-
return iso(utc);
|
|
124
|
-
}
|
|
125
|
-
export function createTime() {
|
|
126
|
-
return Object.freeze({ now: isoNow, toUtc });
|
|
127
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
export declare const GUEST_ENTRY_PATH: string;
|
|
2
|
-
/** dist 根目录下预打包的 guest 运行时文件名。 */
|
|
3
|
-
export declare const GUEST_BUNDLE_FILE = "guest.cjs";
|
|
4
|
-
/** 用 Bun 把 guest 入口打成 CJS 字符串(SDK 内联)。构建脚本与源码下的 `loadGuestRuntime` 共用。 */
|
|
5
|
-
export declare function bundleGuestWithBun(entry?: string): Promise<string>;
|
|
6
|
-
/** 可放进 `execute.guest` 的 guest 运行时 CJS 字符串(进程内缓存)。 */
|
|
7
|
-
export declare function loadGuestRuntime(): Promise<string>;
|
package/runtime/guest-runtime.js
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { readFile } from 'node:fs/promises';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { IS_SOURCE, moduleFile, PACKAGE_ROOT } from '../internal/location.js';
|
|
4
|
-
export const GUEST_ENTRY_PATH = moduleFile('runtime/guest/index');
|
|
5
|
-
export const GUEST_BUNDLE_FILE = 'guest.cjs';
|
|
6
|
-
const CODE_FILE = /\.[cm]?[jt]sx?$/;
|
|
7
|
-
function readSourcePlugin(bun) {
|
|
8
|
-
return {
|
|
9
|
-
name: 'datasense-connector-read-source',
|
|
10
|
-
setup(build) {
|
|
11
|
-
build.onLoad({ filter: CODE_FILE }, async (args) => {
|
|
12
|
-
const extension = args.path.slice(args.path.lastIndexOf('.') + 1);
|
|
13
|
-
const loader = extension === 'tsx' ? 'tsx' : extension === 'jsx' ? 'jsx' : /^[cm]?js$/.test(extension) ? 'js' : 'ts';
|
|
14
|
-
return { contents: await bun.file(args.path).text(), loader };
|
|
15
|
-
});
|
|
16
|
-
},
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
const MACROS_OFF = { macros: false };
|
|
20
|
-
export async function bundleGuestWithBun(entry = GUEST_ENTRY_PATH) {
|
|
21
|
-
const bun = globalThis.Bun;
|
|
22
|
-
if (!bun)
|
|
23
|
-
throw new Error('the guest runtime can only be bundled from source under Bun; use the built package (dist) under Node.js');
|
|
24
|
-
const result = await bun.build({
|
|
25
|
-
entrypoints: [entry],
|
|
26
|
-
format: 'cjs',
|
|
27
|
-
target: 'browser',
|
|
28
|
-
minify: false,
|
|
29
|
-
external: [],
|
|
30
|
-
plugins: [readSourcePlugin(bun)],
|
|
31
|
-
throw: false,
|
|
32
|
-
...MACROS_OFF,
|
|
33
|
-
});
|
|
34
|
-
const output = result.outputs.find((item) => item.kind === 'entry-point');
|
|
35
|
-
if (!result.success || !output) {
|
|
36
|
-
throw new Error(`guest runtime bundle failed: ${result.logs.map((log) => log.message).join('; ') || 'no output'}`);
|
|
37
|
-
}
|
|
38
|
-
return output.text();
|
|
39
|
-
}
|
|
40
|
-
let cached = null;
|
|
41
|
-
export function loadGuestRuntime() {
|
|
42
|
-
if (cached)
|
|
43
|
-
return cached;
|
|
44
|
-
const pending = IS_SOURCE ? bundleGuestWithBun() : readFile(path.join(PACKAGE_ROOT, GUEST_BUNDLE_FILE), 'utf8');
|
|
45
|
-
cached = pending;
|
|
46
|
-
pending.catch(() => {
|
|
47
|
-
if (cached === pending)
|
|
48
|
-
cached = null;
|
|
49
|
-
});
|
|
50
|
-
return pending;
|
|
51
|
-
}
|
package/runtime/index.d.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `@deepexi/datasense-connector/runtime`:宿主侧入口(runner 的执行子进程与父进程、`./testing` 用),不在 vm 里加载。
|
|
3
|
-
*
|
|
4
|
-
* - `GUEST_ENTRY_PATH`:guest 运行时入口(源码下是 `src/runtime/guest/index.ts`,dist 下是编译后的 `runtime/guest/index.js`),
|
|
5
|
-
* runner 用 `bundleTrustedEntry` 打成 CJS 字符串,放进 `execute.guest`;
|
|
6
|
-
* - `loadGuestRuntime`:直接拿到可放进 `execute.guest` 的 CJS 字符串:dist 下读预打包的 `guest.cjs`,源码下用 `Bun.build` 打包(进程内缓存);
|
|
7
|
-
* - `createGuestSession`:子进程里建 vm 上下文、加载 guest 与连接器代码,之后只转发字符串消息;
|
|
8
|
-
* - `parseParentMessage` / `parseChildMessage`:两侧解析对方消息,结构不合法返回 null;`undeclaredEmitTable` 区分
|
|
9
|
-
* 「`emit` 写了没声明的表」(宿主按 `SCHEMA_ERROR` 结束)与其他不合法消息。
|
|
10
|
-
*
|
|
11
|
-
* 宿主调用 `definition` 的 payload 是 `summarizeDefinition` 的结果:guest 在加载并核对 `expect` 之后、执行作业之前发一次
|
|
12
|
-
* (push 作业不发),宿主回 ok 后才继续。`emit` 的 payload 是 `GuestPullBatch`(不带 `seq`,由宿主编号),回复 `EmitReply`
|
|
13
|
-
* (`{ stop: true }` 时提前结束)。`declaration` 只在 `discoverFirst` 的拉取作业里发一次(再发按协议错误),payload 是 `DeclarationReport`,回复 null。
|
|
14
|
-
*/
|
|
15
|
-
export { GUEST_ENTRY_PATH, loadGuestRuntime } from './guest-runtime.js';
|
|
16
|
-
export { createGuestSession } from './session.js';
|
|
17
|
-
export { GUEST_FAILURE_CODES, HOST_METHODS, parseChildMessage, parseParentMessage, undeclaredEmitTable } from './protocol.js';
|
|
18
|
-
export type { CacheEntry, CacheGetRequest, CacheSetRequest, ChildMessage, CryptoRequest, CryptoResponse, DbCursorRequest, DbNextResponse, DbQueryRequest, DbQueryResponse, DeclarationReport, DefinitionReply, DefinitionSummary, EgressHttpRequest, EgressHttpResponse, EmitReply, GuestFailureCode, GuestJob, GuestLog, GuestPullBatch, GuestPushRequest, GuestPushResult, GuestResult, HostCallError, HostMethod, NormalizedDiscover, ParentMessage, ParseChildOptions, SecretRevealRequest, SecretRevealResponse, } from './protocol.js';
|
package/runtime/index.js
DELETED