space-data-module-sdk 0.5.14 → 0.5.18
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 +91 -1
- package/package.json +8 -4
- package/schemas/HostStorageAbi.fbs +53 -0
- package/src/bundle/codec.js +1 -1
- package/src/compliance/pluginCompliance.js +0 -130
- package/src/generated/orbpro/invoke/plugin-invoke-request.js +1 -1
- package/src/generated/orbpro/invoke/plugin-invoke-response.js +1 -1
- package/src/generated/orbpro/manifest/accepted-type-set.js +1 -1
- package/src/generated/orbpro/manifest/build-artifact.js +1 -1
- package/src/generated/orbpro/manifest/host-capability.js +1 -1
- package/src/generated/orbpro/manifest/method-manifest.js +1 -1
- package/src/generated/orbpro/manifest/plugin-manifest.js +1 -1
- package/src/generated/orbpro/manifest/port-manifest.js +1 -1
- package/src/generated/orbpro/manifest/protocol-spec.js +1 -1
- package/src/generated/orbpro/manifest/timer-spec.js +1 -1
- package/src/generated/orbpro/module/canonicalization-rule.js +1 -1
- package/src/generated/orbpro/module/module-bundle-entry.js +1 -1
- package/src/generated/orbpro/module/module-bundle.js +1 -1
- package/src/generated/orbpro/stream/flat-buffer-type-ref.js +1 -1
- package/src/generated/orbpro/stream/typed-arena-buffer.js +1 -1
- package/src/index.d.ts +157 -1
- package/src/index.js +1 -0
- package/src/invoke/codec.js +10 -8
- package/src/manifest/codec.js +1 -1
- package/src/runtime-host/flatsqlRuntimeStore.js +217 -0
- package/src/runtime-host/index.js +21 -0
- package/src/runtime-host/moduleRegistry.js +92 -0
- package/src/runtime-host/runtimeRegionStore.js +245 -0
- package/src/testing/buildWasmEdgeRunner.js +253 -0
- package/src/testing/index.d.ts +311 -0
- package/src/testing/index.js +21 -0
- package/src/testing/moduleHarness.js +163 -0
- package/src/testing/native/wasmedge_emscripten_pthread_runner.c +3111 -0
- package/src/testing/processInvoke.js +467 -0
- package/src/testing/publicationProtectionDemo.js +271 -0
- package/src/testing/streamInvokeCodec.js +175 -0
- package/src/transport/records.js +56 -55
package/src/index.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ export type ProtocolRoleName = "handle" | "dial" | "both";
|
|
|
11
11
|
|
|
12
12
|
export interface PayloadTypeRef {
|
|
13
13
|
schemaName?: string;
|
|
14
|
-
fileIdentifier?: string
|
|
14
|
+
fileIdentifier?: string;
|
|
15
15
|
schemaHash?: string | number[] | Uint8Array;
|
|
16
16
|
acceptsAnyFlatbuffer?: boolean;
|
|
17
17
|
wireFormat?: PayloadWireFormat;
|
|
@@ -258,6 +258,144 @@ export function loadComplianceConfig(
|
|
|
258
258
|
rootDirectory: string,
|
|
259
259
|
): Promise<{ path: string; config: Record<string, unknown> } | null>;
|
|
260
260
|
export function getWasmExportNames(wasmBytes: Uint8Array): string[];
|
|
261
|
+
|
|
262
|
+
// --- Runtime Host ---
|
|
263
|
+
|
|
264
|
+
export interface RowHandle {
|
|
265
|
+
schemaFileId: string;
|
|
266
|
+
rowId: number;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export interface RuntimeRowView {
|
|
270
|
+
handle: RowHandle;
|
|
271
|
+
payload: unknown;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
export interface RuntimeRowQueryResult {
|
|
275
|
+
columns: string[];
|
|
276
|
+
rows: unknown[][];
|
|
277
|
+
rowCount: number;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
export interface FlatSqlRuntimeStore {
|
|
281
|
+
appendRow(options: { schemaFileId: string; payload?: unknown }): RowHandle;
|
|
282
|
+
listRows(schemaFileId?: string | null): RuntimeRowView[];
|
|
283
|
+
query(sql: string): RuntimeRowQueryResult;
|
|
284
|
+
resolveRow(handle: RowHandle): RuntimeRowView | null;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export interface RuntimeRegionDescriptor {
|
|
288
|
+
regionId: number;
|
|
289
|
+
layoutId: string;
|
|
290
|
+
recordByteLength: number;
|
|
291
|
+
alignment: number;
|
|
292
|
+
recordCount: number;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
export interface RuntimeRegionRecord {
|
|
296
|
+
regionId: number;
|
|
297
|
+
recordIndex: number;
|
|
298
|
+
layoutId: string;
|
|
299
|
+
recordByteLength: number;
|
|
300
|
+
alignment: number;
|
|
301
|
+
byteLength: number;
|
|
302
|
+
bytes: Uint8Array;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
export interface RuntimeRegionExternalRecordView {
|
|
306
|
+
regionId: number;
|
|
307
|
+
recordIndex: number;
|
|
308
|
+
layoutId: string;
|
|
309
|
+
recordByteLength: number;
|
|
310
|
+
alignment: number;
|
|
311
|
+
byteOffset?: number;
|
|
312
|
+
buffer?: ArrayBufferLike;
|
|
313
|
+
elementType?: string;
|
|
314
|
+
elementCount?: number;
|
|
315
|
+
strideElements?: number;
|
|
316
|
+
[key: string]: unknown;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
export interface RuntimeRegionRecordViewRequest {
|
|
320
|
+
regionId: number;
|
|
321
|
+
recordIndex: number;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
export interface RuntimeRegionStore {
|
|
325
|
+
allocateRegion(options: {
|
|
326
|
+
layoutId: string;
|
|
327
|
+
recordByteLength: number;
|
|
328
|
+
alignment?: number;
|
|
329
|
+
initialRecords?: Array<Uint8Array | ArrayBuffer | ArrayBufferView | null | undefined>;
|
|
330
|
+
}): RuntimeRegionDescriptor;
|
|
331
|
+
registerExternalRegion(options: {
|
|
332
|
+
layoutId: string;
|
|
333
|
+
recordByteLength: number;
|
|
334
|
+
alignment?: number;
|
|
335
|
+
recordCount?: number;
|
|
336
|
+
getRecordCount?: (regionId: number) => number;
|
|
337
|
+
resolveRecordView?: (query: {
|
|
338
|
+
regionId: number;
|
|
339
|
+
recordIndex: number;
|
|
340
|
+
layoutId: string;
|
|
341
|
+
recordByteLength: number;
|
|
342
|
+
alignment: number;
|
|
343
|
+
}) =>
|
|
344
|
+
| Omit<
|
|
345
|
+
RuntimeRegionExternalRecordView,
|
|
346
|
+
"regionId" | "recordIndex" | "layoutId" | "recordByteLength" | "alignment"
|
|
347
|
+
>
|
|
348
|
+
| null
|
|
349
|
+
| undefined;
|
|
350
|
+
}): RuntimeRegionDescriptor;
|
|
351
|
+
setRegionRecordCount(regionId: number, recordCount: number): RuntimeRegionDescriptor | null;
|
|
352
|
+
describeRegion(regionId: number): RuntimeRegionDescriptor | null;
|
|
353
|
+
resolveRecord(options: RuntimeRegionRecordViewRequest): RuntimeRegionRecord | null;
|
|
354
|
+
resolveRecordView(
|
|
355
|
+
options: RuntimeRegionRecordViewRequest,
|
|
356
|
+
): RuntimeRegionRecord | RuntimeRegionExternalRecordView | null;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
export interface InstalledRuntimeModule {
|
|
360
|
+
moduleId: string;
|
|
361
|
+
metadata: unknown;
|
|
362
|
+
methodIds: string[];
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
export interface RuntimeModuleRegistry {
|
|
366
|
+
installModule(definition: {
|
|
367
|
+
moduleId: string;
|
|
368
|
+
methods?: Record<string, (...args: unknown[]) => unknown>;
|
|
369
|
+
metadata?: unknown;
|
|
370
|
+
}): InstalledRuntimeModule;
|
|
371
|
+
invokeModule(
|
|
372
|
+
moduleId: string,
|
|
373
|
+
methodId: string,
|
|
374
|
+
...args: unknown[]
|
|
375
|
+
): Promise<unknown>;
|
|
376
|
+
listModules(): InstalledRuntimeModule[];
|
|
377
|
+
loadModule(moduleId: string): {
|
|
378
|
+
moduleId: string;
|
|
379
|
+
methods: Record<string, (...args: unknown[]) => unknown>;
|
|
380
|
+
metadata: unknown;
|
|
381
|
+
} | null;
|
|
382
|
+
unloadModule(moduleId: string): boolean;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
export interface RuntimeHost {
|
|
386
|
+
rows: FlatSqlRuntimeStore;
|
|
387
|
+
regions: RuntimeRegionStore;
|
|
388
|
+
moduleRegistry: RuntimeModuleRegistry;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
export function createFlatSqlRuntimeStore(): FlatSqlRuntimeStore;
|
|
392
|
+
export function createRuntimeRegionStore(): RuntimeRegionStore;
|
|
393
|
+
export function createModuleRegistry(): RuntimeModuleRegistry;
|
|
394
|
+
export function createRuntimeHost(options?: {
|
|
395
|
+
rows?: FlatSqlRuntimeStore;
|
|
396
|
+
regions?: RuntimeRegionStore;
|
|
397
|
+
moduleRegistry?: RuntimeModuleRegistry;
|
|
398
|
+
}): RuntimeHost;
|
|
261
399
|
export function getWasmExportNamesFromFile(wasmPath: string): Promise<string[]>;
|
|
262
400
|
|
|
263
401
|
// --- Auth ---
|
|
@@ -662,12 +800,30 @@ export type {
|
|
|
662
800
|
HarnessInvokeScenario,
|
|
663
801
|
HarnessRawScenario,
|
|
664
802
|
ManifestHarnessPlan,
|
|
803
|
+
ModuleHarness,
|
|
804
|
+
ModuleHarnessRuntimeDescriptor,
|
|
805
|
+
PublicationProtectionDemoAlignedType,
|
|
806
|
+
PublicationProtectionDemoSummary,
|
|
807
|
+
PluginInvokeProcessClient,
|
|
808
|
+
PluginInvokeProcessLaunchPlan,
|
|
809
|
+
WasmEdgeRunnerBuildPlan,
|
|
665
810
|
} from "./testing/index.js";
|
|
666
811
|
|
|
667
812
|
export {
|
|
813
|
+
buildWasmEdgeEmscriptenPthreadRunner,
|
|
814
|
+
buildWasmEdgeSpawnEnv,
|
|
815
|
+
createPublicationProtectionDemoManifest,
|
|
816
|
+
createPublicationProtectionDemoSummary,
|
|
817
|
+
createModuleHarness,
|
|
818
|
+
createPluginInvokeProcessClient,
|
|
819
|
+
createWasmEdgeStreamProcessClient,
|
|
668
820
|
describeCapabilityRuntimeSurface,
|
|
669
821
|
generateManifestHarnessPlan,
|
|
670
822
|
materializeHarnessScenario,
|
|
823
|
+
resolveModuleHarnessLaunchPlan,
|
|
824
|
+
resolveWasmEdgeRunnerBuildPlan,
|
|
825
|
+
resolveWasmEdgeRunnerSourcePath,
|
|
826
|
+
resolveWasmEdgePluginLaunchPlan,
|
|
671
827
|
serializeHarnessPlan,
|
|
672
828
|
} from "./testing/index.js";
|
|
673
829
|
|
package/src/index.js
CHANGED
|
@@ -7,6 +7,7 @@ export * from "./bundle/index.js";
|
|
|
7
7
|
export * from "./capabilities.js";
|
|
8
8
|
export * from "./standards/index.js";
|
|
9
9
|
export * from "./host/index.js";
|
|
10
|
+
export * from "./runtime-host/index.js";
|
|
10
11
|
export * from "./invoke/index.js";
|
|
11
12
|
export * from "./testing/index.js";
|
|
12
13
|
export * from "./deployment/index.js";
|
package/src/invoke/codec.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as flatbuffers from "flatbuffers";
|
|
1
|
+
import * as flatbuffers from "flatbuffers/mjs/flatbuffers.js";
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
4
|
PluginInvokeRequest,
|
|
@@ -12,7 +12,7 @@ import { InvokeSurface } from "../generated/orbpro/manifest/invoke-surface.js";
|
|
|
12
12
|
import { BufferMutability } from "../generated/orbpro/stream/buffer-mutability.js";
|
|
13
13
|
import { BufferOwnership } from "../generated/orbpro/stream/buffer-ownership.js";
|
|
14
14
|
import { FlatBufferTypeRefT } from "../generated/orbpro/stream/flat-buffer-type-ref.js";
|
|
15
|
-
import {
|
|
15
|
+
import { TypedArenaBufferT } from "../generated/orbpro/stream/typed-arena-buffer.js";
|
|
16
16
|
import { toUint8Array } from "../runtime/bufferLike.js";
|
|
17
17
|
|
|
18
18
|
function toByteBuffer(data) {
|
|
@@ -149,20 +149,22 @@ function normalizeArenaFrame(frame = {}, offset) {
|
|
|
149
149
|
|
|
150
150
|
function packArenaFrames(frames = []) {
|
|
151
151
|
const packedFrames = [];
|
|
152
|
-
const
|
|
152
|
+
const normalizedFrames = [];
|
|
153
153
|
let offset = 0;
|
|
154
154
|
for (const frame of frames) {
|
|
155
155
|
const normalized = normalizeArenaFrame(frame, offset);
|
|
156
|
-
for (let index = 0; index < normalized.padding; index += 1) {
|
|
157
|
-
arena.push(0);
|
|
158
|
-
}
|
|
159
|
-
arena.push(...normalized.payload);
|
|
160
156
|
offset = normalized.buffer.offset + normalized.buffer.size;
|
|
161
157
|
packedFrames.push(normalized.buffer);
|
|
158
|
+
normalizedFrames.push(normalized);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const arena = new Uint8Array(offset);
|
|
162
|
+
for (const normalized of normalizedFrames) {
|
|
163
|
+
arena.set(normalized.payload, normalized.buffer.offset);
|
|
162
164
|
}
|
|
163
165
|
return {
|
|
164
166
|
frames: packedFrames,
|
|
165
|
-
arena
|
|
167
|
+
arena,
|
|
166
168
|
};
|
|
167
169
|
}
|
|
168
170
|
|
package/src/manifest/codec.js
CHANGED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { DirectAccessor, FlatSQLDatabase } from "flatsql";
|
|
2
|
+
|
|
3
|
+
const RUNTIME_ROW_TABLE = "RuntimeHostRow";
|
|
4
|
+
const RUNTIME_ROW_SCHEMA = `
|
|
5
|
+
table RuntimeHostRow {
|
|
6
|
+
schemaFileId: string;
|
|
7
|
+
rowId: ulong;
|
|
8
|
+
payloadJson: string;
|
|
9
|
+
}
|
|
10
|
+
`;
|
|
11
|
+
|
|
12
|
+
const encoder = new TextEncoder();
|
|
13
|
+
const decoder = new TextDecoder();
|
|
14
|
+
|
|
15
|
+
function clonePayload(payload) {
|
|
16
|
+
if (payload === null || payload === undefined) {
|
|
17
|
+
return payload ?? null;
|
|
18
|
+
}
|
|
19
|
+
if (typeof structuredClone === "function") {
|
|
20
|
+
return structuredClone(payload);
|
|
21
|
+
}
|
|
22
|
+
if (ArrayBuffer.isView(payload)) {
|
|
23
|
+
return payload.slice(0);
|
|
24
|
+
}
|
|
25
|
+
if (payload instanceof ArrayBuffer) {
|
|
26
|
+
return payload.slice(0);
|
|
27
|
+
}
|
|
28
|
+
if (typeof payload === "object") {
|
|
29
|
+
return JSON.parse(JSON.stringify(payload));
|
|
30
|
+
}
|
|
31
|
+
return payload;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function normalizeSchemaFileId(value) {
|
|
35
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
36
|
+
throw new TypeError("schemaFileId must be a non-empty string");
|
|
37
|
+
}
|
|
38
|
+
return value.trim();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function normalizeRowHandle(handle) {
|
|
42
|
+
if (!handle || typeof handle !== "object") {
|
|
43
|
+
throw new TypeError("row handle is required");
|
|
44
|
+
}
|
|
45
|
+
const schemaFileId = normalizeSchemaFileId(handle.schemaFileId);
|
|
46
|
+
const rowId = Number(handle.rowId);
|
|
47
|
+
if (!Number.isInteger(rowId) || rowId <= 0) {
|
|
48
|
+
throw new TypeError("rowId must be a positive integer");
|
|
49
|
+
}
|
|
50
|
+
return { schemaFileId, rowId };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function escapeSqlStringLiteral(value) {
|
|
54
|
+
return String(value).replaceAll("'", "''");
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function serializePayload(payload) {
|
|
58
|
+
if (payload === null || payload === undefined) {
|
|
59
|
+
return {
|
|
60
|
+
kind: "json",
|
|
61
|
+
value: null,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
if (ArrayBuffer.isView(payload)) {
|
|
65
|
+
return {
|
|
66
|
+
kind: "bytes",
|
|
67
|
+
bytes: Array.from(
|
|
68
|
+
new Uint8Array(payload.buffer, payload.byteOffset, payload.byteLength),
|
|
69
|
+
),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
if (payload instanceof ArrayBuffer) {
|
|
73
|
+
return {
|
|
74
|
+
kind: "bytes",
|
|
75
|
+
bytes: Array.from(new Uint8Array(payload)),
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
kind: "json",
|
|
80
|
+
value: clonePayload(payload),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function deserializePayload(payloadJson) {
|
|
85
|
+
const payload = JSON.parse(String(payloadJson ?? "null"));
|
|
86
|
+
if (payload?.kind === "bytes") {
|
|
87
|
+
return Uint8Array.from(payload.bytes ?? []);
|
|
88
|
+
}
|
|
89
|
+
if (payload?.kind === "json") {
|
|
90
|
+
return clonePayload(payload.value);
|
|
91
|
+
}
|
|
92
|
+
return clonePayload(payload);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function createRuntimeRowAccessor() {
|
|
96
|
+
const accessor = new DirectAccessor();
|
|
97
|
+
accessor.registerAccessor(RUNTIME_ROW_TABLE, (data, path) => {
|
|
98
|
+
const row = JSON.parse(decoder.decode(data));
|
|
99
|
+
let current = row;
|
|
100
|
+
for (const segment of Array.isArray(path) ? path : []) {
|
|
101
|
+
if (current === null || current === undefined) {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
current = current[segment];
|
|
105
|
+
}
|
|
106
|
+
return current ?? null;
|
|
107
|
+
});
|
|
108
|
+
accessor.registerBuilder(RUNTIME_ROW_TABLE, (fields) =>
|
|
109
|
+
encoder.encode(
|
|
110
|
+
JSON.stringify({
|
|
111
|
+
schemaFileId: normalizeSchemaFileId(fields.schemaFileId),
|
|
112
|
+
rowId: Number(fields.rowId),
|
|
113
|
+
payloadJson: String(fields.payloadJson ?? "null"),
|
|
114
|
+
}),
|
|
115
|
+
),
|
|
116
|
+
);
|
|
117
|
+
return accessor;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function cloneQueryResult(result) {
|
|
121
|
+
return {
|
|
122
|
+
columns: Array.from(result?.columns ?? []),
|
|
123
|
+
rows: (result?.rows ?? []).map((row) =>
|
|
124
|
+
Array.isArray(row) ? row.map((value) => clonePayload(value)) : row,
|
|
125
|
+
),
|
|
126
|
+
rowCount: Number(result?.rowCount ?? 0),
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function rowViewFromQueryRow(row) {
|
|
131
|
+
return {
|
|
132
|
+
handle: {
|
|
133
|
+
schemaFileId: String(row[0]),
|
|
134
|
+
rowId: Number(row[1]),
|
|
135
|
+
},
|
|
136
|
+
payload: deserializePayload(row[2]),
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export function createFlatSqlRuntimeStore(options = {}) {
|
|
141
|
+
const accessor = options.accessor ?? createRuntimeRowAccessor();
|
|
142
|
+
const database =
|
|
143
|
+
options.database ??
|
|
144
|
+
FlatSQLDatabase.fromSchema(
|
|
145
|
+
RUNTIME_ROW_SCHEMA,
|
|
146
|
+
accessor,
|
|
147
|
+
options.databaseName ?? "runtime-host",
|
|
148
|
+
);
|
|
149
|
+
const nextRowIdBySchema = new Map();
|
|
150
|
+
const existingRows = database.query(
|
|
151
|
+
`SELECT schemaFileId, rowId FROM ${RUNTIME_ROW_TABLE} ORDER BY schemaFileId, rowId`,
|
|
152
|
+
);
|
|
153
|
+
for (const row of existingRows.rows ?? []) {
|
|
154
|
+
const schemaFileId = normalizeSchemaFileId(row[0]);
|
|
155
|
+
const rowId = Number(row[1]);
|
|
156
|
+
if (Number.isInteger(rowId) && rowId > 0) {
|
|
157
|
+
nextRowIdBySchema.set(
|
|
158
|
+
schemaFileId,
|
|
159
|
+
Math.max(nextRowIdBySchema.get(schemaFileId) ?? 0, rowId),
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function appendRow({ schemaFileId, payload = null }) {
|
|
165
|
+
const normalizedSchemaFileId = normalizeSchemaFileId(schemaFileId);
|
|
166
|
+
const nextRowId = (nextRowIdBySchema.get(normalizedSchemaFileId) ?? 0) + 1;
|
|
167
|
+
nextRowIdBySchema.set(normalizedSchemaFileId, nextRowId);
|
|
168
|
+
database.insert(RUNTIME_ROW_TABLE, {
|
|
169
|
+
schemaFileId: normalizedSchemaFileId,
|
|
170
|
+
rowId: nextRowId,
|
|
171
|
+
payloadJson: JSON.stringify(serializePayload(payload)),
|
|
172
|
+
});
|
|
173
|
+
return {
|
|
174
|
+
schemaFileId: normalizedSchemaFileId,
|
|
175
|
+
rowId: nextRowId,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function resolveRow(handle) {
|
|
180
|
+
const normalizedHandle = normalizeRowHandle(handle);
|
|
181
|
+
return (
|
|
182
|
+
listRows(normalizedHandle.schemaFileId).find(
|
|
183
|
+
(row) => row.handle.rowId === normalizedHandle.rowId,
|
|
184
|
+
) ?? null
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function listRows(schemaFileId = null) {
|
|
189
|
+
const normalizedSchemaFileId =
|
|
190
|
+
schemaFileId === null || schemaFileId === undefined
|
|
191
|
+
? null
|
|
192
|
+
: normalizeSchemaFileId(schemaFileId);
|
|
193
|
+
const result =
|
|
194
|
+
normalizedSchemaFileId === null
|
|
195
|
+
? database.query(
|
|
196
|
+
`SELECT schemaFileId, rowId, payloadJson FROM ${RUNTIME_ROW_TABLE} ORDER BY schemaFileId, rowId`,
|
|
197
|
+
)
|
|
198
|
+
: database.query(
|
|
199
|
+
`SELECT schemaFileId, rowId, payloadJson FROM ${RUNTIME_ROW_TABLE} WHERE schemaFileId = '${escapeSqlStringLiteral(normalizedSchemaFileId)}' ORDER BY rowId`,
|
|
200
|
+
);
|
|
201
|
+
return (result.rows ?? []).map(rowViewFromQueryRow);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function query(sql) {
|
|
205
|
+
if (typeof sql !== "string" || sql.trim().length === 0) {
|
|
206
|
+
throw new TypeError("sql must be a non-empty string");
|
|
207
|
+
}
|
|
208
|
+
return cloneQueryResult(database.query(sql));
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return {
|
|
212
|
+
appendRow,
|
|
213
|
+
listRows,
|
|
214
|
+
query,
|
|
215
|
+
resolveRow,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { createFlatSqlRuntimeStore } from "./flatsqlRuntimeStore.js";
|
|
2
|
+
import { createRuntimeRegionStore } from "./runtimeRegionStore.js";
|
|
3
|
+
import { createModuleRegistry } from "./moduleRegistry.js";
|
|
4
|
+
|
|
5
|
+
export function createRuntimeHost(options = {}) {
|
|
6
|
+
const rows = options.rows ?? createFlatSqlRuntimeStore();
|
|
7
|
+
const regions = options.regions ?? createRuntimeRegionStore();
|
|
8
|
+
const moduleRegistry = options.moduleRegistry ?? createModuleRegistry();
|
|
9
|
+
|
|
10
|
+
return {
|
|
11
|
+
rows,
|
|
12
|
+
regions,
|
|
13
|
+
moduleRegistry,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export {
|
|
18
|
+
createFlatSqlRuntimeStore,
|
|
19
|
+
createModuleRegistry,
|
|
20
|
+
createRuntimeRegionStore,
|
|
21
|
+
};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
function normalizeModuleId(value) {
|
|
2
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
3
|
+
throw new TypeError("moduleId must be a non-empty string");
|
|
4
|
+
}
|
|
5
|
+
return value.trim();
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function cloneMetadata(value) {
|
|
9
|
+
if (value === null || value === undefined) {
|
|
10
|
+
return value ?? null;
|
|
11
|
+
}
|
|
12
|
+
if (typeof structuredClone === "function") {
|
|
13
|
+
return structuredClone(value);
|
|
14
|
+
}
|
|
15
|
+
return JSON.parse(JSON.stringify(value));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function toPublicModuleRecord(moduleRecord) {
|
|
19
|
+
return {
|
|
20
|
+
moduleId: moduleRecord.moduleId,
|
|
21
|
+
metadata: cloneMetadata(moduleRecord.metadata),
|
|
22
|
+
methodIds: Object.keys(moduleRecord.methods),
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function createModuleRegistry() {
|
|
27
|
+
const modules = new Map();
|
|
28
|
+
|
|
29
|
+
function installModule(definition) {
|
|
30
|
+
if (!definition || typeof definition !== "object") {
|
|
31
|
+
throw new TypeError("module definition is required");
|
|
32
|
+
}
|
|
33
|
+
const moduleId = normalizeModuleId(definition.moduleId);
|
|
34
|
+
const moduleRecord = {
|
|
35
|
+
moduleId,
|
|
36
|
+
methods: {
|
|
37
|
+
...(definition.methods ?? {}),
|
|
38
|
+
},
|
|
39
|
+
metadata: cloneMetadata(definition.metadata),
|
|
40
|
+
};
|
|
41
|
+
modules.set(moduleId, moduleRecord);
|
|
42
|
+
return toPublicModuleRecord(moduleRecord);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function loadModule(moduleId) {
|
|
46
|
+
const moduleRecord = modules.get(normalizeModuleId(moduleId));
|
|
47
|
+
if (!moduleRecord) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
moduleId: moduleRecord.moduleId,
|
|
52
|
+
methods: {
|
|
53
|
+
...moduleRecord.methods,
|
|
54
|
+
},
|
|
55
|
+
metadata: cloneMetadata(moduleRecord.metadata),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function unloadModule(moduleId) {
|
|
60
|
+
return modules.delete(normalizeModuleId(moduleId));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function listModules() {
|
|
64
|
+
return Array.from(modules.values(), toPublicModuleRecord);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async function invokeModule(moduleId, methodId, ...args) {
|
|
68
|
+
const moduleRecord = modules.get(normalizeModuleId(moduleId));
|
|
69
|
+
if (!moduleRecord) {
|
|
70
|
+
throw new Error(`Unknown module: ${moduleId}`);
|
|
71
|
+
}
|
|
72
|
+
const method = moduleRecord.methods?.[methodId];
|
|
73
|
+
if (typeof method !== "function") {
|
|
74
|
+
throw new Error(`Unknown module method: ${moduleId}.${methodId}`);
|
|
75
|
+
}
|
|
76
|
+
return method.call(
|
|
77
|
+
{
|
|
78
|
+
moduleId: moduleRecord.moduleId,
|
|
79
|
+
metadata: cloneMetadata(moduleRecord.metadata),
|
|
80
|
+
},
|
|
81
|
+
...args,
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
installModule,
|
|
87
|
+
invokeModule,
|
|
88
|
+
listModules,
|
|
89
|
+
loadModule,
|
|
90
|
+
unloadModule,
|
|
91
|
+
};
|
|
92
|
+
}
|