space-data-module-sdk 0.5.18 → 0.5.21
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 +79 -7
- package/package.json +10 -1
- package/src/AGENTS.md +33 -0
- package/src/auth/AGENTS.md +25 -0
- package/src/browser.js +8 -0
- package/src/bundle/AGENTS.md +21 -0
- package/src/compiler/AGENTS.md +35 -0
- package/src/compiler/compileModule.js +3 -0
- package/src/compliance/AGENTS.md +18 -0
- package/src/compliance/pluginCompliance.js +0 -1
- package/src/host/AGENTS.md +39 -0
- package/src/host/browserEdgeShims.js +355 -0
- package/src/host/browserHost.js +399 -0
- package/src/host/index.js +4 -0
- package/src/host/isomorphicLoader.js +234 -0
- package/src/host/wasiShim.js +286 -0
- package/src/index.d.ts +325 -0
- package/src/manifest/AGENTS.md +18 -0
- package/src/runtime-host/AGENTS.md +32 -0
- package/src/runtime-host/flatbufferStreamIngestor.js +181 -0
- package/src/runtime-host/flatsqlRuntimeStore.js +120 -49
- package/src/runtime-host/index.js +2 -0
- package/src/testing/AGENTS.md +33 -0
- package/src/testing/browserModuleHarness.js +275 -0
- package/src/testing/index.d.ts +50 -0
- package/src/testing/index.js +5 -0
- package/src/testing/moduleFlatbufferStreamPump.js +255 -0
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser-compatible WASI preview1 shim.
|
|
3
|
+
*
|
|
4
|
+
* Provides the wasi_snapshot_preview1 import namespace so that standalone
|
|
5
|
+
* WASI modules (the same .wasm that runs in WasmEdge) can be instantiated
|
|
6
|
+
* directly in the browser via WebAssembly.instantiate().
|
|
7
|
+
*
|
|
8
|
+
* Covers the 11 imports observed across all SDN plugin standalone builds:
|
|
9
|
+
* clock_time_get, fd_write, fd_read, fd_close, fd_seek, fd_fdstat_get,
|
|
10
|
+
* environ_sizes_get, environ_get, proc_exit, args_get, args_sizes_get,
|
|
11
|
+
* random_get
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const ERRNO_SUCCESS = 0;
|
|
15
|
+
const ERRNO_BADF = 8;
|
|
16
|
+
const ERRNO_INVAL = 28;
|
|
17
|
+
const ERRNO_NOSYS = 52;
|
|
18
|
+
const ERRNO_SPIPE = 70;
|
|
19
|
+
|
|
20
|
+
const CLOCKID_REALTIME = 0;
|
|
21
|
+
const CLOCKID_MONOTONIC = 1;
|
|
22
|
+
|
|
23
|
+
const FILETYPE_CHARACTER_DEVICE = 2;
|
|
24
|
+
|
|
25
|
+
export class WasiExitError extends Error {
|
|
26
|
+
constructor(code) {
|
|
27
|
+
super(`WASI exit with code ${code}`);
|
|
28
|
+
this.name = "WasiExitError";
|
|
29
|
+
this.code = code;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function createBrowserWasiShim(options = {}) {
|
|
34
|
+
const args = options.args ?? [];
|
|
35
|
+
const env = options.env ?? {};
|
|
36
|
+
const stdinBytes = new Uint8Array(options.stdinBytes ?? []);
|
|
37
|
+
const logOutput = options.logOutput === true;
|
|
38
|
+
const performanceApi = options.performance ?? globalThis.performance ?? {
|
|
39
|
+
now: () => Date.now(),
|
|
40
|
+
timeOrigin: 0,
|
|
41
|
+
};
|
|
42
|
+
const cryptoApi = options.crypto ?? globalThis.crypto ?? null;
|
|
43
|
+
const stdoutChunks = [];
|
|
44
|
+
const stderrChunks = [];
|
|
45
|
+
let stdinOffset = 0;
|
|
46
|
+
|
|
47
|
+
let memory = null;
|
|
48
|
+
|
|
49
|
+
function setMemory(mem) {
|
|
50
|
+
memory = mem;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function getMemory() {
|
|
54
|
+
return memory;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function mem8() {
|
|
58
|
+
return new Uint8Array(memory.buffer);
|
|
59
|
+
}
|
|
60
|
+
function view() {
|
|
61
|
+
return new DataView(memory.buffer);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// --- Environment encoding helpers ---
|
|
65
|
+
|
|
66
|
+
const envEntries = Object.entries(env).map(([k, v]) => `${k}=${v}`);
|
|
67
|
+
const encodedEnvEntries = envEntries.map((e) => new TextEncoder().encode(e));
|
|
68
|
+
const encodedArgs = args.map((a) => new TextEncoder().encode(a));
|
|
69
|
+
|
|
70
|
+
// --- WASI functions ---
|
|
71
|
+
|
|
72
|
+
function clock_time_get(clockId, _precisionBigInt, resultPtr) {
|
|
73
|
+
let nanos;
|
|
74
|
+
if (clockId === CLOCKID_REALTIME) {
|
|
75
|
+
nanos = BigInt(
|
|
76
|
+
Math.round((performanceApi.timeOrigin + performanceApi.now()) * 1e6),
|
|
77
|
+
);
|
|
78
|
+
} else if (clockId === CLOCKID_MONOTONIC) {
|
|
79
|
+
nanos = BigInt(Math.round(performanceApi.now() * 1e6));
|
|
80
|
+
} else {
|
|
81
|
+
return ERRNO_INVAL;
|
|
82
|
+
}
|
|
83
|
+
view().setBigUint64(resultPtr, nanos, true);
|
|
84
|
+
return ERRNO_SUCCESS;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function fd_write(fd, iovsPtr, iovsLen, nwrittenPtr) {
|
|
88
|
+
if (fd !== 1 && fd !== 2) return ERRNO_BADF;
|
|
89
|
+
|
|
90
|
+
const target = fd === 1 ? stdoutChunks : stderrChunks;
|
|
91
|
+
let totalWritten = 0;
|
|
92
|
+
const dv = view();
|
|
93
|
+
const bytes = mem8();
|
|
94
|
+
|
|
95
|
+
for (let i = 0; i < iovsLen; i++) {
|
|
96
|
+
const base = iovsPtr + i * 8;
|
|
97
|
+
const ptr = dv.getUint32(base, true);
|
|
98
|
+
const len = dv.getUint32(base + 4, true);
|
|
99
|
+
target.push(bytes.slice(ptr, ptr + len));
|
|
100
|
+
totalWritten += len;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
dv.setUint32(nwrittenPtr, totalWritten, true);
|
|
104
|
+
return ERRNO_SUCCESS;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function fd_read(fd, iovsPtr, iovsLen, nreadPtr) {
|
|
108
|
+
if (fd !== 0) {
|
|
109
|
+
view().setUint32(nreadPtr, 0, true);
|
|
110
|
+
return ERRNO_BADF;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const dv = view();
|
|
114
|
+
const bytes = mem8();
|
|
115
|
+
let totalRead = 0;
|
|
116
|
+
|
|
117
|
+
for (let i = 0; i < iovsLen; i += 1) {
|
|
118
|
+
if (stdinOffset >= stdinBytes.length) {
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
const base = iovsPtr + i * 8;
|
|
122
|
+
const ptr = dv.getUint32(base, true);
|
|
123
|
+
const len = dv.getUint32(base + 4, true);
|
|
124
|
+
const remaining = stdinBytes.length - stdinOffset;
|
|
125
|
+
const count = Math.min(len, remaining);
|
|
126
|
+
bytes.set(stdinBytes.subarray(stdinOffset, stdinOffset + count), ptr);
|
|
127
|
+
stdinOffset += count;
|
|
128
|
+
totalRead += count;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
dv.setUint32(nreadPtr, totalRead, true);
|
|
132
|
+
return ERRNO_SUCCESS;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function fd_close(fd) {
|
|
136
|
+
if (fd <= 2) return ERRNO_SUCCESS;
|
|
137
|
+
return ERRNO_BADF;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function fd_seek(fd, _offsetLo, _whence, _resultPtr) {
|
|
141
|
+
if (fd <= 2) return ERRNO_SPIPE;
|
|
142
|
+
return ERRNO_BADF;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function fd_fdstat_get(fd, bufPtr) {
|
|
146
|
+
if (fd > 2) return ERRNO_BADF;
|
|
147
|
+
const dv = view();
|
|
148
|
+
// filetype (u8) at offset 0 — CHARACTER_DEVICE
|
|
149
|
+
dv.setUint8(bufPtr, FILETYPE_CHARACTER_DEVICE);
|
|
150
|
+
// fdflags (u16) at offset 2
|
|
151
|
+
dv.setUint16(bufPtr + 2, 0, true);
|
|
152
|
+
// rights_base (u64) at offset 8
|
|
153
|
+
dv.setBigUint64(bufPtr + 8, 0n, true);
|
|
154
|
+
// rights_inheriting (u64) at offset 16
|
|
155
|
+
dv.setBigUint64(bufPtr + 16, 0n, true);
|
|
156
|
+
return ERRNO_SUCCESS;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function environ_sizes_get(countPtr, bufSizePtr) {
|
|
160
|
+
const dv = view();
|
|
161
|
+
dv.setUint32(countPtr, encodedEnvEntries.length, true);
|
|
162
|
+
let totalSize = 0;
|
|
163
|
+
for (const entry of encodedEnvEntries) {
|
|
164
|
+
totalSize += entry.length + 1; // null terminator
|
|
165
|
+
}
|
|
166
|
+
dv.setUint32(bufSizePtr, totalSize, true);
|
|
167
|
+
return ERRNO_SUCCESS;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function environ_get(environPtr, environBufPtr) {
|
|
171
|
+
const dv = view();
|
|
172
|
+
const bytes = mem8();
|
|
173
|
+
let bufOffset = environBufPtr;
|
|
174
|
+
|
|
175
|
+
for (let i = 0; i < encodedEnvEntries.length; i++) {
|
|
176
|
+
dv.setUint32(environPtr + i * 4, bufOffset, true);
|
|
177
|
+
bytes.set(encodedEnvEntries[i], bufOffset);
|
|
178
|
+
bufOffset += encodedEnvEntries[i].length;
|
|
179
|
+
bytes[bufOffset] = 0; // null terminator
|
|
180
|
+
bufOffset += 1;
|
|
181
|
+
}
|
|
182
|
+
return ERRNO_SUCCESS;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function args_sizes_get(argcPtr, argvBufSizePtr) {
|
|
186
|
+
const dv = view();
|
|
187
|
+
dv.setUint32(argcPtr, encodedArgs.length, true);
|
|
188
|
+
let totalSize = 0;
|
|
189
|
+
for (const arg of encodedArgs) {
|
|
190
|
+
totalSize += arg.length + 1;
|
|
191
|
+
}
|
|
192
|
+
dv.setUint32(argvBufSizePtr, totalSize, true);
|
|
193
|
+
return ERRNO_SUCCESS;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function args_get(argvPtr, argvBufPtr) {
|
|
197
|
+
const dv = view();
|
|
198
|
+
const bytes = mem8();
|
|
199
|
+
let bufOffset = argvBufPtr;
|
|
200
|
+
|
|
201
|
+
for (let i = 0; i < encodedArgs.length; i++) {
|
|
202
|
+
dv.setUint32(argvPtr + i * 4, bufOffset, true);
|
|
203
|
+
bytes.set(encodedArgs[i], bufOffset);
|
|
204
|
+
bufOffset += encodedArgs[i].length;
|
|
205
|
+
bytes[bufOffset] = 0;
|
|
206
|
+
bufOffset += 1;
|
|
207
|
+
}
|
|
208
|
+
return ERRNO_SUCCESS;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function random_get(bufPtr, bufLen) {
|
|
212
|
+
if (!cryptoApi?.getRandomValues) {
|
|
213
|
+
return ERRNO_NOSYS;
|
|
214
|
+
}
|
|
215
|
+
cryptoApi.getRandomValues(mem8().subarray(bufPtr, bufPtr + bufLen));
|
|
216
|
+
return ERRNO_SUCCESS;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function proc_exit(code) {
|
|
220
|
+
if (logOutput) {
|
|
221
|
+
flushOutput();
|
|
222
|
+
}
|
|
223
|
+
throw new WasiExitError(code);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// --- Output helpers ---
|
|
227
|
+
|
|
228
|
+
function flushOutput() {
|
|
229
|
+
if (stdoutChunks.length > 0) {
|
|
230
|
+
const combined = concatChunks(stdoutChunks);
|
|
231
|
+
if (logOutput) {
|
|
232
|
+
const text = new TextDecoder().decode(combined);
|
|
233
|
+
if (text) console.log(text);
|
|
234
|
+
}
|
|
235
|
+
stdoutChunks.length = 0;
|
|
236
|
+
}
|
|
237
|
+
if (stderrChunks.length > 0) {
|
|
238
|
+
const combined = concatChunks(stderrChunks);
|
|
239
|
+
if (logOutput) {
|
|
240
|
+
const text = new TextDecoder().decode(combined);
|
|
241
|
+
if (text) console.warn(text);
|
|
242
|
+
}
|
|
243
|
+
stderrChunks.length = 0;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function concatChunks(chunks) {
|
|
248
|
+
let totalLen = 0;
|
|
249
|
+
for (const chunk of chunks) totalLen += chunk.length;
|
|
250
|
+
const result = new Uint8Array(totalLen);
|
|
251
|
+
let offset = 0;
|
|
252
|
+
for (const chunk of chunks) {
|
|
253
|
+
result.set(chunk, offset);
|
|
254
|
+
offset += chunk.length;
|
|
255
|
+
}
|
|
256
|
+
return result;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return {
|
|
260
|
+
imports: {
|
|
261
|
+
wasi_snapshot_preview1: {
|
|
262
|
+
clock_time_get,
|
|
263
|
+
fd_write,
|
|
264
|
+
fd_read,
|
|
265
|
+
fd_close,
|
|
266
|
+
fd_seek,
|
|
267
|
+
fd_fdstat_get,
|
|
268
|
+
environ_sizes_get,
|
|
269
|
+
environ_get,
|
|
270
|
+
args_sizes_get,
|
|
271
|
+
args_get,
|
|
272
|
+
random_get,
|
|
273
|
+
proc_exit,
|
|
274
|
+
},
|
|
275
|
+
},
|
|
276
|
+
setMemory,
|
|
277
|
+
getMemory,
|
|
278
|
+
flushOutput,
|
|
279
|
+
get stdout() {
|
|
280
|
+
return concatChunks(stdoutChunks);
|
|
281
|
+
},
|
|
282
|
+
get stderr() {
|
|
283
|
+
return concatChunks(stderrChunks);
|
|
284
|
+
},
|
|
285
|
+
};
|
|
286
|
+
}
|
package/src/index.d.ts
CHANGED
|
@@ -277,6 +277,22 @@ export interface RuntimeRowQueryResult {
|
|
|
277
277
|
rowCount: number;
|
|
278
278
|
}
|
|
279
279
|
|
|
280
|
+
export interface FlatBufferStreamIngestStats {
|
|
281
|
+
bytesReceived: number;
|
|
282
|
+
chunksReceived: number;
|
|
283
|
+
framesDecoded: number;
|
|
284
|
+
framesAppended: number;
|
|
285
|
+
framesRouted: number;
|
|
286
|
+
parseErrors: number;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
export interface FlatBufferStreamIngestContext {
|
|
290
|
+
rawFileIdentifier: string;
|
|
291
|
+
schemaFileId: string;
|
|
292
|
+
rows: FlatSqlRuntimeStore;
|
|
293
|
+
stats: FlatBufferStreamIngestStats;
|
|
294
|
+
}
|
|
295
|
+
|
|
280
296
|
export interface FlatSqlRuntimeStore {
|
|
281
297
|
appendRow(options: { schemaFileId: string; payload?: unknown }): RowHandle;
|
|
282
298
|
listRows(schemaFileId?: string | null): RuntimeRowView[];
|
|
@@ -284,6 +300,13 @@ export interface FlatSqlRuntimeStore {
|
|
|
284
300
|
resolveRow(handle: RowHandle): RuntimeRowView | null;
|
|
285
301
|
}
|
|
286
302
|
|
|
303
|
+
export interface FlatBufferStreamIngestor {
|
|
304
|
+
rows: FlatSqlRuntimeStore;
|
|
305
|
+
stats: FlatBufferStreamIngestStats;
|
|
306
|
+
pushBytes(data: Uint8Array | ArrayBuffer | ArrayBufferView): number;
|
|
307
|
+
finish(): 0;
|
|
308
|
+
}
|
|
309
|
+
|
|
287
310
|
export interface RuntimeRegionDescriptor {
|
|
288
311
|
regionId: number;
|
|
289
312
|
layoutId: string;
|
|
@@ -388,6 +411,25 @@ export interface RuntimeHost {
|
|
|
388
411
|
moduleRegistry: RuntimeModuleRegistry;
|
|
389
412
|
}
|
|
390
413
|
|
|
414
|
+
export function createFlatBufferStreamIngestor(options?: {
|
|
415
|
+
rows?: FlatSqlRuntimeStore;
|
|
416
|
+
frameRouter?:
|
|
417
|
+
| ((
|
|
418
|
+
payload: Uint8Array,
|
|
419
|
+
context: FlatBufferStreamIngestContext,
|
|
420
|
+
) => boolean | void)
|
|
421
|
+
| Record<
|
|
422
|
+
string,
|
|
423
|
+
(
|
|
424
|
+
payload: Uint8Array,
|
|
425
|
+
context: FlatBufferStreamIngestContext,
|
|
426
|
+
) => boolean | void
|
|
427
|
+
>;
|
|
428
|
+
appendFrame?: (
|
|
429
|
+
payload: Uint8Array,
|
|
430
|
+
context: FlatBufferStreamIngestContext,
|
|
431
|
+
) => void;
|
|
432
|
+
}): FlatBufferStreamIngestor;
|
|
391
433
|
export function createFlatSqlRuntimeStore(): FlatSqlRuntimeStore;
|
|
392
434
|
export function createRuntimeRegionStore(): RuntimeRegionStore;
|
|
393
435
|
export function createModuleRegistry(): RuntimeModuleRegistry;
|
|
@@ -1037,6 +1079,62 @@ export interface HostcallBridge {
|
|
|
1037
1079
|
getLastResponseJson(): unknown;
|
|
1038
1080
|
}
|
|
1039
1081
|
|
|
1082
|
+
export interface BrowserFilesystemShim {
|
|
1083
|
+
filesystemRoot?: string;
|
|
1084
|
+
resolvePath(path?: string): string;
|
|
1085
|
+
readFile(
|
|
1086
|
+
path: string,
|
|
1087
|
+
options?: { encoding?: string | null },
|
|
1088
|
+
): Promise<string | Uint8Array>;
|
|
1089
|
+
writeFile(
|
|
1090
|
+
path: string,
|
|
1091
|
+
value: Uint8Array | ArrayBuffer | ArrayBufferView | string,
|
|
1092
|
+
options?: { encoding?: string | null },
|
|
1093
|
+
): Promise<{ path: string }>;
|
|
1094
|
+
appendFile(
|
|
1095
|
+
path: string,
|
|
1096
|
+
value: Uint8Array | ArrayBuffer | ArrayBufferView | string,
|
|
1097
|
+
options?: { encoding?: string | null },
|
|
1098
|
+
): Promise<{ path: string }>;
|
|
1099
|
+
deleteFile(path: string): Promise<{ path: string }>;
|
|
1100
|
+
mkdir(
|
|
1101
|
+
path: string,
|
|
1102
|
+
options?: { recursive?: boolean },
|
|
1103
|
+
): Promise<{ path: string }>;
|
|
1104
|
+
readdir(path?: string): Promise<NodeHostFilesystemEntry[]>;
|
|
1105
|
+
stat(path: string): Promise<NodeHostFilesystemStat>;
|
|
1106
|
+
rename(
|
|
1107
|
+
fromPath: string,
|
|
1108
|
+
toPath: string,
|
|
1109
|
+
): Promise<{ from: string; to: string }>;
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
export interface BrowserEdgeShims {
|
|
1113
|
+
fetch?: (...args: any[]) => Promise<any>;
|
|
1114
|
+
WebSocket?: any;
|
|
1115
|
+
crypto?: any;
|
|
1116
|
+
performance?: {
|
|
1117
|
+
now(): number;
|
|
1118
|
+
timeOrigin: number;
|
|
1119
|
+
};
|
|
1120
|
+
filesystem?: BrowserFilesystemShim;
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
export interface BrowserHostOptions {
|
|
1124
|
+
capabilities?: string[];
|
|
1125
|
+
edgeShims?: BrowserEdgeShims;
|
|
1126
|
+
contextStore?: Map<string, Map<string, unknown>>;
|
|
1127
|
+
fetch?: (...args: any[]) => Promise<any>;
|
|
1128
|
+
WebSocket?: any;
|
|
1129
|
+
crypto?: any;
|
|
1130
|
+
performance?: {
|
|
1131
|
+
now(): number;
|
|
1132
|
+
timeOrigin: number;
|
|
1133
|
+
};
|
|
1134
|
+
filesystem?: BrowserFilesystemShim;
|
|
1135
|
+
filesystemRoot?: string;
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1040
1138
|
export interface NodeHostOptions {
|
|
1041
1139
|
manifest?: PluginManifest;
|
|
1042
1140
|
grantedCapabilities?: string[];
|
|
@@ -1072,6 +1170,12 @@ export class HostFilesystemScopeError extends Error {
|
|
|
1072
1170
|
filesystemRoot: string | null;
|
|
1073
1171
|
}
|
|
1074
1172
|
|
|
1173
|
+
export class BrowserFilesystemScopeError extends Error {
|
|
1174
|
+
code: string;
|
|
1175
|
+
requestedPath: string | null;
|
|
1176
|
+
filesystemRoot: string | null;
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1075
1179
|
export class NodeHost {
|
|
1076
1180
|
runtimeTarget: string;
|
|
1077
1181
|
filesystemRoot: string;
|
|
@@ -1322,14 +1426,93 @@ export class NodeHost {
|
|
|
1322
1426
|
invoke(operation: string, params?: Record<string, any>): Promise<any>;
|
|
1323
1427
|
}
|
|
1324
1428
|
|
|
1429
|
+
export class BrowserHost {
|
|
1430
|
+
runtimeTarget: string;
|
|
1431
|
+
filesystemRoot: string;
|
|
1432
|
+
clock: {
|
|
1433
|
+
now(): number;
|
|
1434
|
+
monotonicNow(): number;
|
|
1435
|
+
nowIso(): string;
|
|
1436
|
+
};
|
|
1437
|
+
random: {
|
|
1438
|
+
bytes(length: number): Uint8Array;
|
|
1439
|
+
};
|
|
1440
|
+
timers: {
|
|
1441
|
+
delay(ms: number): Promise<void>;
|
|
1442
|
+
};
|
|
1443
|
+
schedule: {
|
|
1444
|
+
parse(expression: string): CronSchedule;
|
|
1445
|
+
matches(expression: string | CronSchedule, date?: Date | number | string): boolean;
|
|
1446
|
+
next(expression: string | CronSchedule, from?: Date | number | string): Date;
|
|
1447
|
+
};
|
|
1448
|
+
http: {
|
|
1449
|
+
request<TBody = unknown>(options: NodeHostHttpRequest): Promise<NodeHostHttpResponse<TBody>>;
|
|
1450
|
+
};
|
|
1451
|
+
websocket: {
|
|
1452
|
+
exchange<TBody = unknown>(options: {
|
|
1453
|
+
url: string;
|
|
1454
|
+
protocols?: string | string[];
|
|
1455
|
+
message?: Uint8Array | ArrayBuffer | ArrayBufferView | string | null;
|
|
1456
|
+
responseType?: "bytes" | "utf8" | "json";
|
|
1457
|
+
timeoutMs?: number;
|
|
1458
|
+
expectResponse?: boolean;
|
|
1459
|
+
}): Promise<NodeHostWebSocketResponse<TBody>>;
|
|
1460
|
+
};
|
|
1461
|
+
context: {
|
|
1462
|
+
get(scope: string, key: string): unknown;
|
|
1463
|
+
set(scope: string, key: string, value: unknown): void;
|
|
1464
|
+
delete(scope: string, key: string): void;
|
|
1465
|
+
listKeys(scope: string): string[];
|
|
1466
|
+
listScopes(): string[];
|
|
1467
|
+
};
|
|
1468
|
+
crypto: {
|
|
1469
|
+
sha256(data: Uint8Array | ArrayBuffer | ArrayBufferView | string): Promise<Uint8Array>;
|
|
1470
|
+
sha512(data: Uint8Array | ArrayBuffer | ArrayBufferView | string): Promise<Uint8Array>;
|
|
1471
|
+
aesGcmEncrypt(options: {
|
|
1472
|
+
key: Uint8Array | ArrayBuffer | ArrayBufferView;
|
|
1473
|
+
plaintext: Uint8Array | ArrayBuffer | ArrayBufferView;
|
|
1474
|
+
iv?: Uint8Array | ArrayBuffer | ArrayBufferView;
|
|
1475
|
+
}): Promise<{ ciphertext: Uint8Array; iv: Uint8Array | ArrayBuffer | ArrayBufferView }>;
|
|
1476
|
+
aesGcmDecrypt(options: {
|
|
1477
|
+
key: Uint8Array | ArrayBuffer | ArrayBufferView;
|
|
1478
|
+
ciphertext: Uint8Array | ArrayBuffer | ArrayBufferView;
|
|
1479
|
+
iv: Uint8Array | ArrayBuffer | ArrayBufferView;
|
|
1480
|
+
}): Promise<Uint8Array>;
|
|
1481
|
+
};
|
|
1482
|
+
filesystem: BrowserFilesystemShim;
|
|
1483
|
+
constructor(options?: BrowserHostOptions);
|
|
1484
|
+
listCapabilities(): string[];
|
|
1485
|
+
listSupportedCapabilities(): string[];
|
|
1486
|
+
listOperations(): string[];
|
|
1487
|
+
hasCapability(capability: string): boolean;
|
|
1488
|
+
assertCapability(capability: string, operation?: string | null): void;
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1325
1491
|
export const NodeHostSupportedCapabilities: readonly string[];
|
|
1326
1492
|
export const NodeHostSupportedOperations: readonly string[];
|
|
1493
|
+
export const BrowserHostSupportedCapabilities: readonly string[];
|
|
1494
|
+
export const BrowserHostSupportedOperations: readonly string[];
|
|
1327
1495
|
export const DEFAULT_HOSTCALL_IMPORT_MODULE: string;
|
|
1328
1496
|
export const HOSTCALL_STATUS_OK: number;
|
|
1329
1497
|
export const HOSTCALL_STATUS_ERROR: number;
|
|
1330
1498
|
export const NodeHostSyncHostcallOperations: readonly string[];
|
|
1331
1499
|
|
|
1332
1500
|
export function createNodeHost(options?: NodeHostOptions): NodeHost;
|
|
1501
|
+
export function createBrowserHost(options?: BrowserHostOptions): BrowserHost;
|
|
1502
|
+
export function createMemoryFilesystemEdgeShim(options?: {
|
|
1503
|
+
filesystemRoot?: string;
|
|
1504
|
+
}): BrowserFilesystemShim;
|
|
1505
|
+
export function createBrowserEdgeShims(options?: {
|
|
1506
|
+
fetch?: (...args: any[]) => Promise<any>;
|
|
1507
|
+
WebSocket?: any;
|
|
1508
|
+
crypto?: any;
|
|
1509
|
+
performance?: {
|
|
1510
|
+
now(): number;
|
|
1511
|
+
timeOrigin: number;
|
|
1512
|
+
};
|
|
1513
|
+
filesystem?: BrowserFilesystemShim;
|
|
1514
|
+
filesystemRoot?: string;
|
|
1515
|
+
}): BrowserEdgeShims;
|
|
1333
1516
|
export function parseCronExpression(expression: string): CronSchedule;
|
|
1334
1517
|
export function matchesCronExpression(
|
|
1335
1518
|
expression: string | CronSchedule,
|
|
@@ -1361,6 +1544,148 @@ export function createNodeHostSyncHostcallBridge(options: {
|
|
|
1361
1544
|
maxRequestBytes?: number;
|
|
1362
1545
|
maxResponseBytes?: number;
|
|
1363
1546
|
}): HostcallBridge;
|
|
1547
|
+
export class WasiExitError extends Error {
|
|
1548
|
+
code: number;
|
|
1549
|
+
}
|
|
1550
|
+
export interface BrowserWasiShim {
|
|
1551
|
+
imports: Record<string, Record<string, (...args: number[]) => number>>;
|
|
1552
|
+
setMemory(mem: { buffer: ArrayBuffer | SharedArrayBuffer }): void;
|
|
1553
|
+
getMemory(): { buffer: ArrayBuffer | SharedArrayBuffer } | null;
|
|
1554
|
+
flushOutput(): void;
|
|
1555
|
+
stdout: Uint8Array;
|
|
1556
|
+
stderr: Uint8Array;
|
|
1557
|
+
}
|
|
1558
|
+
export function createBrowserWasiShim(options?: {
|
|
1559
|
+
args?: string[];
|
|
1560
|
+
env?: Record<string, string>;
|
|
1561
|
+
stdinBytes?: Uint8Array | ArrayBuffer | ArrayBufferView;
|
|
1562
|
+
logOutput?: boolean;
|
|
1563
|
+
performance?: {
|
|
1564
|
+
now(): number;
|
|
1565
|
+
timeOrigin: number;
|
|
1566
|
+
};
|
|
1567
|
+
}): BrowserWasiShim;
|
|
1568
|
+
export interface BrowserModuleHarness {
|
|
1569
|
+
runtime: {
|
|
1570
|
+
kind: "browser";
|
|
1571
|
+
profile: string;
|
|
1572
|
+
surface: string;
|
|
1573
|
+
};
|
|
1574
|
+
instance: WebAssembly.Instance;
|
|
1575
|
+
module: WebAssembly.Module;
|
|
1576
|
+
host: BrowserHost;
|
|
1577
|
+
bridge: HostcallBridge | null;
|
|
1578
|
+
wasi: BrowserWasiShim;
|
|
1579
|
+
invokeRaw(
|
|
1580
|
+
requestBytes: Uint8Array | ArrayBuffer | ArrayBufferView,
|
|
1581
|
+
): Promise<Uint8Array>;
|
|
1582
|
+
invoke(request: {
|
|
1583
|
+
methodId?: string | null;
|
|
1584
|
+
inputs?: HarnessInputFrame[];
|
|
1585
|
+
}): Promise<{
|
|
1586
|
+
statusCode: number;
|
|
1587
|
+
errorCode?: string | null;
|
|
1588
|
+
errorMessage?: string | null;
|
|
1589
|
+
outputs: HarnessInputFrame[];
|
|
1590
|
+
}>;
|
|
1591
|
+
readManifest(): Uint8Array | null;
|
|
1592
|
+
destroy(): void;
|
|
1593
|
+
}
|
|
1594
|
+
export function detectArtifactProfile(wasmModule: WebAssembly.Module): string;
|
|
1595
|
+
export function createBrowserModuleHarness(options?: {
|
|
1596
|
+
wasmSource: Uint8Array | ArrayBuffer | string | WebAssembly.Module | unknown;
|
|
1597
|
+
host?: BrowserHost;
|
|
1598
|
+
hostOptions?: BrowserHostOptions;
|
|
1599
|
+
args?: string[];
|
|
1600
|
+
env?: Record<string, string>;
|
|
1601
|
+
surface?: "direct" | "command";
|
|
1602
|
+
performance?: {
|
|
1603
|
+
now(): number;
|
|
1604
|
+
timeOrigin: number;
|
|
1605
|
+
};
|
|
1606
|
+
logOutput?: boolean;
|
|
1607
|
+
}): Promise<BrowserModuleHarness>;
|
|
1608
|
+
export interface ModuleFlatBufferStreamPumpStats {
|
|
1609
|
+
bytesReceived: number;
|
|
1610
|
+
chunksReceived: number;
|
|
1611
|
+
framesDecoded: number;
|
|
1612
|
+
framesInvoked: number;
|
|
1613
|
+
invokes: number;
|
|
1614
|
+
parseErrors: number;
|
|
1615
|
+
}
|
|
1616
|
+
export interface ModuleFlatBufferStreamPumpContext {
|
|
1617
|
+
rawFileIdentifier: string;
|
|
1618
|
+
schemaFileId: string;
|
|
1619
|
+
methodId: string;
|
|
1620
|
+
portId: string;
|
|
1621
|
+
streamId: number;
|
|
1622
|
+
sequence: number;
|
|
1623
|
+
stats: ModuleFlatBufferStreamPumpStats;
|
|
1624
|
+
}
|
|
1625
|
+
export interface ModuleFlatBufferStreamPump {
|
|
1626
|
+
stats: ModuleFlatBufferStreamPumpStats;
|
|
1627
|
+
lastResponse: PluginInvokeResponseEnvelope | null;
|
|
1628
|
+
pushBytes(data: Uint8Array | ArrayBuffer | ArrayBufferView): Promise<number>;
|
|
1629
|
+
finish(): Promise<PluginInvokeResponseEnvelope | null>;
|
|
1630
|
+
}
|
|
1631
|
+
export function createModuleFlatBufferStreamPump(options: {
|
|
1632
|
+
harness?: {
|
|
1633
|
+
invoke(
|
|
1634
|
+
request: PluginInvokeRequestEnvelope,
|
|
1635
|
+
): Promise<PluginInvokeResponseEnvelope>;
|
|
1636
|
+
};
|
|
1637
|
+
invoke?: (
|
|
1638
|
+
request: PluginInvokeRequestEnvelope,
|
|
1639
|
+
) => Promise<PluginInvokeResponseEnvelope>;
|
|
1640
|
+
methodId: string;
|
|
1641
|
+
portId: string;
|
|
1642
|
+
maxFramesPerInvoke?: number;
|
|
1643
|
+
streamId?: number;
|
|
1644
|
+
sequenceStart?: number;
|
|
1645
|
+
typeResolver?: (
|
|
1646
|
+
payload: Uint8Array,
|
|
1647
|
+
context: ModuleFlatBufferStreamPumpContext,
|
|
1648
|
+
) => PayloadTypeRef | null | undefined;
|
|
1649
|
+
frameTemplate?:
|
|
1650
|
+
| Partial<InvokeFrame>
|
|
1651
|
+
| ((
|
|
1652
|
+
payload: Uint8Array,
|
|
1653
|
+
context: ModuleFlatBufferStreamPumpContext,
|
|
1654
|
+
) => Partial<InvokeFrame> | null | undefined);
|
|
1655
|
+
onResponse?: (
|
|
1656
|
+
response: PluginInvokeResponseEnvelope,
|
|
1657
|
+
context: {
|
|
1658
|
+
methodId: string;
|
|
1659
|
+
portId: string;
|
|
1660
|
+
frames: InvokeFrame[];
|
|
1661
|
+
isFinalBatch: boolean;
|
|
1662
|
+
stats: ModuleFlatBufferStreamPumpStats;
|
|
1663
|
+
},
|
|
1664
|
+
) => void | Promise<void>;
|
|
1665
|
+
}): ModuleFlatBufferStreamPump;
|
|
1666
|
+
export function loadModule(options?: {
|
|
1667
|
+
wasmSource: Uint8Array | ArrayBuffer | string | WebAssembly.Module | unknown;
|
|
1668
|
+
host?: BrowserHost;
|
|
1669
|
+
hostOptions?: BrowserHostOptions;
|
|
1670
|
+
args?: string[];
|
|
1671
|
+
env?: Record<string, string | undefined>;
|
|
1672
|
+
surface?: "direct" | "command";
|
|
1673
|
+
runtimeKind?: "wasmedge" | "process";
|
|
1674
|
+
wasmEdgeBinary?: string;
|
|
1675
|
+
wasmEdgeRunnerBinary?: string;
|
|
1676
|
+
enableThreads?: boolean;
|
|
1677
|
+
hostProfile?: "runtime-host";
|
|
1678
|
+
modules?: RuntimeHostTestModuleDefinition[];
|
|
1679
|
+
defaultModuleId?: string;
|
|
1680
|
+
metadata?: unknown;
|
|
1681
|
+
command?: string;
|
|
1682
|
+
cwd?: string;
|
|
1683
|
+
}): Promise<BrowserModuleHarness | ModuleHarness>;
|
|
1684
|
+
export function inspectModule(source: Uint8Array | ArrayBuffer | WebAssembly.Module): Promise<{
|
|
1685
|
+
profile: string;
|
|
1686
|
+
exports: string[];
|
|
1687
|
+
imports: WebAssembly.ModuleImportDescriptor[];
|
|
1688
|
+
}>;
|
|
1364
1689
|
|
|
1365
1690
|
// --- Runtime constants ---
|
|
1366
1691
|
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# AGENTS
|
|
2
|
+
|
|
3
|
+
You are in `src/manifest`, which owns the manifest schema codecs and
|
|
4
|
+
normalization rules.
|
|
5
|
+
|
|
6
|
+
## Rules
|
|
7
|
+
|
|
8
|
+
- Keep manifest encode/decode round-trips stable.
|
|
9
|
+
- Prefer canonical SDS schema names and file identifiers; do not add repo-local
|
|
10
|
+
aliases when a standards name already exists.
|
|
11
|
+
- If you change a manifest field or normalization rule, update the compiler,
|
|
12
|
+
compliance checks, and any affected examples/tests together.
|
|
13
|
+
|
|
14
|
+
## Check Before You Finish
|
|
15
|
+
|
|
16
|
+
- `npm test`
|
|
17
|
+
- `npm run check:compliance`
|
|
18
|
+
- `node --test test/module-sdk.test.js test/compliance.test.js`
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# AGENTS
|
|
2
|
+
|
|
3
|
+
Apply the root and `src/AGENTS.md` files first.
|
|
4
|
+
|
|
5
|
+
## Area Ownership
|
|
6
|
+
|
|
7
|
+
This directory owns the canonical runtime-host storage model: row handles,
|
|
8
|
+
region handles, FlatSQL-backed storage, and binary FlatBuffer ingest on the host
|
|
9
|
+
side.
|
|
10
|
+
|
|
11
|
+
## Storage And Streaming Rules
|
|
12
|
+
|
|
13
|
+
- Keep durable row identity host-owned as `(schemaFileId, rowId)`.
|
|
14
|
+
- Keep runtime aligned-binary identity host-owned as `(regionId, recordIndex)`.
|
|
15
|
+
- The canonical ingest path is binary FlatBuffer bytes, not JSON.
|
|
16
|
+
- Use size-prefixed FlatBuffer frames for streaming transport.
|
|
17
|
+
- Do not coerce row payloads through JSON serialization.
|
|
18
|
+
- If the host owns persistence, use runtime-host ingest helpers.
|
|
19
|
+
- If a resident module owns state, keep the stream binary and push into the
|
|
20
|
+
module through the harness/pump path rather than inventing JSON wrappers.
|
|
21
|
+
|
|
22
|
+
## Key Files
|
|
23
|
+
|
|
24
|
+
- `flatbufferStreamIngestor.js`
|
|
25
|
+
- `flatsqlRuntimeStore.js`
|
|
26
|
+
- `index.js`
|
|
27
|
+
|
|
28
|
+
## Verification
|
|
29
|
+
|
|
30
|
+
- `npm run test:stream-ingest`
|
|
31
|
+
- `node --test test/flatsql-local-node.test.js`
|
|
32
|
+
- `npm run benchmark:stream-1gib` for large-stream changes
|