space-data-module-sdk 0.2.5 → 0.2.6
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 +5 -2
- package/package.json +4 -2
- package/src/compiler/compileModule.js +12 -141
- package/src/compiler/emception.d.ts +60 -0
- package/src/compiler/emception.js +191 -0
- package/src/compiler/emceptionNode.js +51 -34
- package/src/compiler/index.d.ts +24 -0
- package/src/compiler/index.js +5 -0
- package/src/index.d.ts +13 -0
- package/src/runtime/index.d.ts +13 -0
- package/src/runtime/index.js +2 -0
package/README.md
CHANGED
|
@@ -294,8 +294,11 @@ npm run check:compliance
|
|
|
294
294
|
```
|
|
295
295
|
|
|
296
296
|
Node.js `>=20` is required. The compiler uses `sdn-emception` and `flatc-wasm`
|
|
297
|
-
by default
|
|
298
|
-
|
|
297
|
+
by default.
|
|
298
|
+
|
|
299
|
+
If another repo needs the same compiler runtime, the package also exposes a
|
|
300
|
+
shared emception session at `space-data-module-sdk/compiler/emception` with
|
|
301
|
+
helpers for serialized command execution and virtual filesystem access.
|
|
299
302
|
|
|
300
303
|
## License
|
|
301
304
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "space-data-module-sdk",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.6",
|
|
4
4
|
"description": "Module SDK for building, validating, signing, and deploying WebAssembly modules on the Space Data Network.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "./src/index.d.ts",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"url": "git+https://github.com/DigitalArsenal/space-data-module-sdk.git"
|
|
10
10
|
},
|
|
11
11
|
"bin": {
|
|
12
|
-
"space-data-module": "
|
|
12
|
+
"space-data-module": "bin/space-data-module.js"
|
|
13
13
|
},
|
|
14
14
|
"exports": {
|
|
15
15
|
".": "./src/index.js",
|
|
@@ -18,8 +18,10 @@
|
|
|
18
18
|
"./auth": "./src/auth/index.js",
|
|
19
19
|
"./transport": "./src/transport/index.js",
|
|
20
20
|
"./compiler": "./src/compiler/index.js",
|
|
21
|
+
"./compiler/emception": "./src/compiler/emception.js",
|
|
21
22
|
"./bundle": "./src/bundle/index.js",
|
|
22
23
|
"./invoke": "./src/invoke/index.js",
|
|
24
|
+
"./runtime": "./src/runtime/index.js",
|
|
23
25
|
"./standards": "./src/standards/index.js",
|
|
24
26
|
"./schemas/*": "./schemas/*"
|
|
25
27
|
},
|
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
import os from "node:os";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import {
|
|
4
|
-
mkdir,
|
|
5
4
|
mkdtemp,
|
|
6
|
-
readFile,
|
|
7
5
|
rm,
|
|
8
6
|
writeFile,
|
|
9
7
|
} from "node:fs/promises";
|
|
10
|
-
import { execFile as execFileCallback } from "node:child_process";
|
|
11
|
-
import { promisify } from "node:util";
|
|
12
8
|
|
|
13
9
|
import {
|
|
14
10
|
createDeploymentAuthorization,
|
|
@@ -43,7 +39,6 @@ import {
|
|
|
43
39
|
import { sha256Bytes } from "../utils/crypto.js";
|
|
44
40
|
import { getWasmWallet } from "../utils/wasmCrypto.js";
|
|
45
41
|
|
|
46
|
-
const execFile = promisify(execFileCallback);
|
|
47
42
|
const C_IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
48
43
|
|
|
49
44
|
function selectCompiler(language) {
|
|
@@ -89,14 +84,6 @@ async function getInvokeCppSupportFiles() {
|
|
|
89
84
|
return { runtimeHeaders, schemaHeaders };
|
|
90
85
|
}
|
|
91
86
|
|
|
92
|
-
async function writeFilesToDirectory(rootDir, files) {
|
|
93
|
-
for (const [relativePath, content] of Object.entries(files)) {
|
|
94
|
-
const filePath = path.join(rootDir, relativePath);
|
|
95
|
-
await mkdir(path.dirname(filePath), { recursive: true });
|
|
96
|
-
await writeFile(filePath, content, "utf8");
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
87
|
async function writeFilesToEmception(emception, rootDir, files) {
|
|
101
88
|
for (const [relativePath, content] of Object.entries(files)) {
|
|
102
89
|
const filePath = path.posix.join(rootDir, relativePath);
|
|
@@ -235,101 +222,6 @@ async function compileWithEmception(options = {}) {
|
|
|
235
222
|
}
|
|
236
223
|
}
|
|
237
224
|
|
|
238
|
-
// ---------------------------------------------------------------------------
|
|
239
|
-
// System Emscripten — fallback to emcc/em++ on PATH
|
|
240
|
-
// ---------------------------------------------------------------------------
|
|
241
|
-
|
|
242
|
-
async function compileWithSystemToolchain(options = {}) {
|
|
243
|
-
const {
|
|
244
|
-
compilerCommand,
|
|
245
|
-
sourceCompilerCommand,
|
|
246
|
-
sourceExtension,
|
|
247
|
-
sourceCode,
|
|
248
|
-
manifestSource,
|
|
249
|
-
invokeHeaderSource,
|
|
250
|
-
invokeSource,
|
|
251
|
-
exportedSymbols,
|
|
252
|
-
outputPath,
|
|
253
|
-
compileOptions,
|
|
254
|
-
} = options;
|
|
255
|
-
const tempDir = await mkdtemp(
|
|
256
|
-
path.join(os.tmpdir(), "space-data-module-sdk-compile-"),
|
|
257
|
-
);
|
|
258
|
-
const sourcePath = path.join(tempDir, `module.${sourceExtension}`);
|
|
259
|
-
const manifestSourcePath = path.join(tempDir, "plugin-manifest-exports.cpp");
|
|
260
|
-
const invokeHeaderPath = path.join(tempDir, "space_data_module_invoke.h");
|
|
261
|
-
const invokeSourcePath = path.join(tempDir, "plugin-invoke-bridge.cpp");
|
|
262
|
-
const sourceObjectPath = path.join(tempDir, "module.o");
|
|
263
|
-
const manifestObjectPath = path.join(tempDir, "plugin-manifest-exports.o");
|
|
264
|
-
const invokeObjectPath = path.join(tempDir, "plugin-invoke-bridge.o");
|
|
265
|
-
const resolvedOutputPath = path.resolve(
|
|
266
|
-
outputPath ?? path.join(tempDir, "module.wasm"),
|
|
267
|
-
);
|
|
268
|
-
const runtimeIncludeDir = path.join(tempDir, "flatbuffers-runtime");
|
|
269
|
-
|
|
270
|
-
const { runtimeHeaders, schemaHeaders } = await getInvokeCppSupportFiles();
|
|
271
|
-
|
|
272
|
-
await writeFile(sourcePath, sourceCode, "utf8");
|
|
273
|
-
await writeFile(manifestSourcePath, manifestSource, "utf8");
|
|
274
|
-
await writeFile(invokeHeaderPath, invokeHeaderSource, "utf8");
|
|
275
|
-
await writeFile(invokeSourcePath, invokeSource, "utf8");
|
|
276
|
-
await writeFilesToDirectory(tempDir, schemaHeaders);
|
|
277
|
-
await writeFilesToDirectory(runtimeIncludeDir, runtimeHeaders);
|
|
278
|
-
|
|
279
|
-
const args = buildCompilerArgs(exportedSymbols, compileOptions);
|
|
280
|
-
|
|
281
|
-
try {
|
|
282
|
-
await execFile(sourceCompilerCommand, [
|
|
283
|
-
"-c",
|
|
284
|
-
sourcePath,
|
|
285
|
-
`-I${tempDir}`,
|
|
286
|
-
"-o",
|
|
287
|
-
sourceObjectPath,
|
|
288
|
-
], { timeout: 120_000 });
|
|
289
|
-
|
|
290
|
-
await execFile(compilerCommand, [
|
|
291
|
-
"-c",
|
|
292
|
-
manifestSourcePath,
|
|
293
|
-
"-std=c++17",
|
|
294
|
-
`-I${tempDir}`,
|
|
295
|
-
`-I${runtimeIncludeDir}`,
|
|
296
|
-
"-o",
|
|
297
|
-
manifestObjectPath,
|
|
298
|
-
], { timeout: 120_000 });
|
|
299
|
-
|
|
300
|
-
await execFile(compilerCommand, [
|
|
301
|
-
"-c",
|
|
302
|
-
invokeSourcePath,
|
|
303
|
-
"-std=c++17",
|
|
304
|
-
`-I${tempDir}`,
|
|
305
|
-
`-I${runtimeIncludeDir}`,
|
|
306
|
-
"-o",
|
|
307
|
-
invokeObjectPath,
|
|
308
|
-
], { timeout: 120_000 });
|
|
309
|
-
|
|
310
|
-
await execFile(compilerCommand, [
|
|
311
|
-
sourceObjectPath,
|
|
312
|
-
manifestObjectPath,
|
|
313
|
-
invokeObjectPath,
|
|
314
|
-
...args,
|
|
315
|
-
"-o",
|
|
316
|
-
resolvedOutputPath,
|
|
317
|
-
], { timeout: 120_000 });
|
|
318
|
-
} catch (error) {
|
|
319
|
-
error.message =
|
|
320
|
-
`Compilation failed with ${compilerCommand}: ` +
|
|
321
|
-
(error.stderr || error.message);
|
|
322
|
-
throw error;
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
const wasmBytes = await readFile(resolvedOutputPath);
|
|
326
|
-
return { wasmBytes, outputPath: resolvedOutputPath, tempDir };
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
// ---------------------------------------------------------------------------
|
|
330
|
-
// Public API
|
|
331
|
-
// ---------------------------------------------------------------------------
|
|
332
|
-
|
|
333
225
|
export async function compileModuleFromSource(options = {}) {
|
|
334
226
|
const manifest = options.manifest ?? {};
|
|
335
227
|
const sourceCode = String(options.sourceCode ?? "");
|
|
@@ -382,38 +274,17 @@ export async function compileModuleFromSource(options = {}) {
|
|
|
382
274
|
...options,
|
|
383
275
|
noEntry: includeCommandMain !== true,
|
|
384
276
|
};
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
outputPath: options.outputPath,
|
|
397
|
-
compileOptions,
|
|
398
|
-
});
|
|
399
|
-
} catch (error) {
|
|
400
|
-
if (error?.code !== "EMCEPTION_LOAD_FAILED") {
|
|
401
|
-
throw error;
|
|
402
|
-
}
|
|
403
|
-
result = await compileWithSystemToolchain({
|
|
404
|
-
compilerCommand: "em++",
|
|
405
|
-
sourceCompilerCommand: compiler.command,
|
|
406
|
-
sourceExtension: compiler.extension,
|
|
407
|
-
sourceCode,
|
|
408
|
-
manifestSource,
|
|
409
|
-
invokeHeaderSource,
|
|
410
|
-
invokeSource,
|
|
411
|
-
exportedSymbols,
|
|
412
|
-
outputPath: options.outputPath,
|
|
413
|
-
compileOptions,
|
|
414
|
-
});
|
|
415
|
-
compilerBackend = "em++ (system)";
|
|
416
|
-
}
|
|
277
|
+
const result = await compileWithEmception({
|
|
278
|
+
sourceCompilerCommand: compiler.command,
|
|
279
|
+
sourceExtension: compiler.extension,
|
|
280
|
+
sourceCode,
|
|
281
|
+
manifestSource,
|
|
282
|
+
invokeHeaderSource,
|
|
283
|
+
invokeSource,
|
|
284
|
+
exportedSymbols,
|
|
285
|
+
outputPath: options.outputPath,
|
|
286
|
+
compileOptions,
|
|
287
|
+
});
|
|
417
288
|
wasmBytes = result.wasmBytes;
|
|
418
289
|
resolvedOutputPath = result.outputPath;
|
|
419
290
|
tempDir = result.tempDir;
|
|
@@ -425,7 +296,7 @@ export async function compileModuleFromSource(options = {}) {
|
|
|
425
296
|
});
|
|
426
297
|
|
|
427
298
|
return {
|
|
428
|
-
compiler:
|
|
299
|
+
compiler: "em++ (emception)",
|
|
429
300
|
language: compiler.language,
|
|
430
301
|
outputPath: resolvedOutputPath,
|
|
431
302
|
tempDir,
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
export interface EmceptionCommandResult {
|
|
2
|
+
command: string;
|
|
3
|
+
exitCode: number;
|
|
4
|
+
stdout: string;
|
|
5
|
+
stderr: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export type SharedEmceptionFileContent =
|
|
9
|
+
| string
|
|
10
|
+
| Uint8Array
|
|
11
|
+
| ArrayBuffer
|
|
12
|
+
| ArrayBufferView;
|
|
13
|
+
|
|
14
|
+
export interface SharedEmceptionHandle {
|
|
15
|
+
getRaw(): unknown;
|
|
16
|
+
exists(targetPath: string): boolean;
|
|
17
|
+
mkdirTree(directoryPath: string): void;
|
|
18
|
+
writeFile(filePath: string, content: SharedEmceptionFileContent): void;
|
|
19
|
+
writeFiles(
|
|
20
|
+
rootDir: string,
|
|
21
|
+
files: Record<string, SharedEmceptionFileContent>,
|
|
22
|
+
): void;
|
|
23
|
+
readFile(filePath: string): Uint8Array;
|
|
24
|
+
readFile(filePath: string, options: { encoding: "utf8" }): string;
|
|
25
|
+
removeTree(targetPath: string): void;
|
|
26
|
+
run(
|
|
27
|
+
command: string,
|
|
28
|
+
options?: { throwOnNonZero?: boolean },
|
|
29
|
+
): EmceptionCommandResult;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface SharedEmceptionSession {
|
|
33
|
+
load(): Promise<unknown>;
|
|
34
|
+
withLock<T>(
|
|
35
|
+
task: (handle: SharedEmceptionHandle) => T | Promise<T>,
|
|
36
|
+
): Promise<T>;
|
|
37
|
+
exists(targetPath: string): Promise<boolean>;
|
|
38
|
+
mkdirTree(directoryPath: string): Promise<void>;
|
|
39
|
+
writeFile(
|
|
40
|
+
filePath: string,
|
|
41
|
+
content: SharedEmceptionFileContent,
|
|
42
|
+
): Promise<void>;
|
|
43
|
+
writeFiles(
|
|
44
|
+
rootDir: string,
|
|
45
|
+
files: Record<string, SharedEmceptionFileContent>,
|
|
46
|
+
): Promise<void>;
|
|
47
|
+
readFile(filePath: string): Promise<Uint8Array>;
|
|
48
|
+
readFile(filePath: string, options: { encoding: "utf8" }): Promise<string>;
|
|
49
|
+
removeTree(targetPath: string): Promise<void>;
|
|
50
|
+
run(
|
|
51
|
+
command: string,
|
|
52
|
+
options?: { throwOnNonZero?: boolean },
|
|
53
|
+
): Promise<EmceptionCommandResult>;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function createSharedEmceptionSession(): SharedEmceptionSession;
|
|
57
|
+
export function loadSharedEmception(): Promise<unknown>;
|
|
58
|
+
export function withSharedEmception<T>(
|
|
59
|
+
task: (handle: SharedEmceptionHandle) => T | Promise<T>,
|
|
60
|
+
): Promise<T>;
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
getSharedEmceptionController,
|
|
5
|
+
loadEmception,
|
|
6
|
+
runWithEmceptionLock,
|
|
7
|
+
} from "./emceptionNode.js";
|
|
8
|
+
|
|
9
|
+
const TEXT_DECODER = new TextDecoder();
|
|
10
|
+
const TEXT_ENCODER = new TextEncoder();
|
|
11
|
+
|
|
12
|
+
function normalizeFileContent(content) {
|
|
13
|
+
if (typeof content === "string") {
|
|
14
|
+
return content;
|
|
15
|
+
}
|
|
16
|
+
if (content instanceof Uint8Array) {
|
|
17
|
+
return content;
|
|
18
|
+
}
|
|
19
|
+
if (content instanceof ArrayBuffer) {
|
|
20
|
+
return new Uint8Array(content);
|
|
21
|
+
}
|
|
22
|
+
if (ArrayBuffer.isView(content)) {
|
|
23
|
+
return new Uint8Array(
|
|
24
|
+
content.buffer,
|
|
25
|
+
content.byteOffset,
|
|
26
|
+
content.byteLength,
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
throw new TypeError(
|
|
30
|
+
"Emception file content must be a string, Uint8Array, ArrayBuffer, or ArrayBufferView.",
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function cloneReadBytes(value) {
|
|
35
|
+
if (typeof value === "string") {
|
|
36
|
+
return TEXT_ENCODER.encode(value);
|
|
37
|
+
}
|
|
38
|
+
const bytes = normalizeFileContent(value);
|
|
39
|
+
return new Uint8Array(bytes);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function removeTree(emception, targetPath) {
|
|
43
|
+
const analysis = emception.FS.analyzePath(targetPath);
|
|
44
|
+
if (!analysis.exists) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const stat = emception.FS.stat(targetPath);
|
|
48
|
+
if (!emception.FS.isDir(stat.mode)) {
|
|
49
|
+
emception.FS.unlink(targetPath);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const entries = emception.FS.readdir(targetPath).filter(
|
|
53
|
+
(entry) => entry !== "." && entry !== "..",
|
|
54
|
+
);
|
|
55
|
+
for (const entry of entries) {
|
|
56
|
+
removeTree(emception, path.posix.join(targetPath, entry));
|
|
57
|
+
}
|
|
58
|
+
emception.FS.rmdir(targetPath);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function normalizeRunResult(command, result) {
|
|
62
|
+
const normalized = {
|
|
63
|
+
command,
|
|
64
|
+
exitCode: Number(result?.returncode ?? 0) >>> 0,
|
|
65
|
+
stdout: String(result?.stdout ?? ""),
|
|
66
|
+
stderr: String(result?.stderr ?? ""),
|
|
67
|
+
};
|
|
68
|
+
return normalized;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function maybeThrowRunFailure(result, options = {}) {
|
|
72
|
+
if (options.throwOnNonZero === false || result.exitCode === 0) {
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
75
|
+
const detail = result.stderr || result.stdout || "unknown emception failure";
|
|
76
|
+
throw new Error(
|
|
77
|
+
`Emception command failed with exit code ${result.exitCode}: ${result.command}\n${detail}`,
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
class SharedEmceptionHandle {
|
|
82
|
+
constructor(emception) {
|
|
83
|
+
this.emception = emception;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
getRaw() {
|
|
87
|
+
return this.emception;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
exists(targetPath) {
|
|
91
|
+
return this.emception.FS.analyzePath(targetPath).exists;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
mkdirTree(directoryPath) {
|
|
95
|
+
this.emception.FS.mkdirTree(directoryPath);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
writeFile(filePath, content) {
|
|
99
|
+
this.emception.FS.mkdirTree(path.posix.dirname(filePath));
|
|
100
|
+
this.emception.writeFile(filePath, normalizeFileContent(content));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
writeFiles(rootDir, files) {
|
|
104
|
+
for (const [relativePath, content] of Object.entries(files ?? {})) {
|
|
105
|
+
this.writeFile(path.posix.join(rootDir, relativePath), content);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
readFile(filePath, options = {}) {
|
|
110
|
+
const bytes = cloneReadBytes(this.emception.readFile(filePath));
|
|
111
|
+
if (options.encoding === "utf8") {
|
|
112
|
+
return TEXT_DECODER.decode(bytes);
|
|
113
|
+
}
|
|
114
|
+
return bytes;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
removeTree(targetPath) {
|
|
118
|
+
removeTree(this.emception, targetPath);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
run(command, options = {}) {
|
|
122
|
+
const result = normalizeRunResult(command, this.emception.run(command));
|
|
123
|
+
return maybeThrowRunFailure(result, options);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
class SharedEmceptionSession {
|
|
128
|
+
constructor(controller = getSharedEmceptionController()) {
|
|
129
|
+
this.controller = controller;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async load() {
|
|
133
|
+
return this.controller.load();
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async withLock(task) {
|
|
137
|
+
return this.controller.withLock(
|
|
138
|
+
(emception) => task(new SharedEmceptionHandle(emception)),
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async exists(targetPath) {
|
|
143
|
+
return this.withLock((handle) => handle.exists(targetPath));
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
async mkdirTree(directoryPath) {
|
|
147
|
+
await this.withLock((handle) => {
|
|
148
|
+
handle.mkdirTree(directoryPath);
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async writeFile(filePath, content) {
|
|
153
|
+
await this.withLock((handle) => {
|
|
154
|
+
handle.writeFile(filePath, content);
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
async writeFiles(rootDir, files) {
|
|
159
|
+
await this.withLock((handle) => {
|
|
160
|
+
handle.writeFiles(rootDir, files);
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
async readFile(filePath, options = {}) {
|
|
165
|
+
return this.withLock((handle) => handle.readFile(filePath, options));
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
async removeTree(targetPath) {
|
|
169
|
+
await this.withLock((handle) => {
|
|
170
|
+
handle.removeTree(targetPath);
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async run(command, options = {}) {
|
|
175
|
+
return this.withLock((handle) => handle.run(command, options));
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export function createSharedEmceptionSession() {
|
|
180
|
+
return new SharedEmceptionSession();
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export async function loadSharedEmception() {
|
|
184
|
+
return loadEmception();
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export async function withSharedEmception(task) {
|
|
188
|
+
return runWithEmceptionLock(
|
|
189
|
+
(emception) => task(new SharedEmceptionHandle(emception)),
|
|
190
|
+
);
|
|
191
|
+
}
|
|
@@ -16,8 +16,6 @@ const FILE_URL_FETCH_PATCH_FLAG =
|
|
|
16
16
|
"__spaceDataModuleSdkFileUrlFetchPatched";
|
|
17
17
|
|
|
18
18
|
let patchedEmceptionRootPromise = null;
|
|
19
|
-
let emceptionInstancePromise = null;
|
|
20
|
-
let emceptionExecutionQueue = Promise.resolve();
|
|
21
19
|
|
|
22
20
|
function installNodeRuntimeShims() {
|
|
23
21
|
if (typeof globalThis.require !== "function") {
|
|
@@ -175,43 +173,62 @@ async function preparePatchedEmceptionRoot() {
|
|
|
175
173
|
return patchedEmceptionRootPromise;
|
|
176
174
|
}
|
|
177
175
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
176
|
+
class EmceptionController {
|
|
177
|
+
#instancePromise = null;
|
|
178
|
+
#executionQueue = Promise.resolve();
|
|
179
|
+
|
|
180
|
+
async load() {
|
|
181
|
+
if (!this.#instancePromise) {
|
|
182
|
+
this.#instancePromise = (async () => {
|
|
183
|
+
installNodeRuntimeShims();
|
|
184
|
+
const patchedRoot = await preparePatchedEmceptionRoot();
|
|
185
|
+
const moduleUrl = pathToFileURL(path.join(patchedRoot, "emception.mjs")).href;
|
|
186
|
+
const { default: Emception } = await import(moduleUrl);
|
|
187
|
+
const emception = new Emception({
|
|
188
|
+
baseUrl: pathToFileURL(`${patchedRoot}${path.sep}`).href,
|
|
189
|
+
});
|
|
190
|
+
await emception.init();
|
|
191
|
+
return emception;
|
|
192
|
+
})().catch((error) => {
|
|
193
|
+
this.#instancePromise = null;
|
|
194
|
+
throw error;
|
|
187
195
|
});
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return this.#instancePromise;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
async withLock(task) {
|
|
202
|
+
const previous = this.#executionQueue;
|
|
203
|
+
let release = () => {};
|
|
204
|
+
this.#executionQueue = new Promise((resolve) => {
|
|
205
|
+
release = resolve;
|
|
193
206
|
});
|
|
207
|
+
await previous.catch(() => {});
|
|
208
|
+
try {
|
|
209
|
+
const emception = await this.load().catch((error) => {
|
|
210
|
+
if (!error.code) {
|
|
211
|
+
error.code = "EMCEPTION_LOAD_FAILED";
|
|
212
|
+
}
|
|
213
|
+
throw error;
|
|
214
|
+
});
|
|
215
|
+
return await task(emception);
|
|
216
|
+
} finally {
|
|
217
|
+
release();
|
|
218
|
+
}
|
|
194
219
|
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const sharedEmceptionController = new EmceptionController();
|
|
195
223
|
|
|
196
|
-
|
|
224
|
+
export function getSharedEmceptionController() {
|
|
225
|
+
return sharedEmceptionController;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export async function loadEmception() {
|
|
229
|
+
return sharedEmceptionController.load();
|
|
197
230
|
}
|
|
198
231
|
|
|
199
232
|
export async function runWithEmceptionLock(task) {
|
|
200
|
-
|
|
201
|
-
let release = () => {};
|
|
202
|
-
emceptionExecutionQueue = new Promise((resolve) => {
|
|
203
|
-
release = resolve;
|
|
204
|
-
});
|
|
205
|
-
await previous.catch(() => {});
|
|
206
|
-
try {
|
|
207
|
-
const emception = await loadEmception().catch((error) => {
|
|
208
|
-
if (!error.code) {
|
|
209
|
-
error.code = "EMCEPTION_LOAD_FAILED";
|
|
210
|
-
}
|
|
211
|
-
throw error;
|
|
212
|
-
});
|
|
213
|
-
return await task(emception);
|
|
214
|
-
} finally {
|
|
215
|
-
release();
|
|
216
|
-
}
|
|
233
|
+
return sharedEmceptionController.withLock(task);
|
|
217
234
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export type {
|
|
2
|
+
CompilationResult,
|
|
3
|
+
ProtectedArtifact,
|
|
4
|
+
} from "../index.js";
|
|
5
|
+
|
|
6
|
+
export {
|
|
7
|
+
cleanupCompilation,
|
|
8
|
+
compileModuleFromSource,
|
|
9
|
+
createRecipientKeypairHex,
|
|
10
|
+
protectModuleArtifact,
|
|
11
|
+
} from "../index.js";
|
|
12
|
+
|
|
13
|
+
export type {
|
|
14
|
+
EmceptionCommandResult,
|
|
15
|
+
SharedEmceptionFileContent,
|
|
16
|
+
SharedEmceptionHandle,
|
|
17
|
+
SharedEmceptionSession,
|
|
18
|
+
} from "./emception.js";
|
|
19
|
+
|
|
20
|
+
export {
|
|
21
|
+
createSharedEmceptionSession,
|
|
22
|
+
loadSharedEmception,
|
|
23
|
+
withSharedEmception,
|
|
24
|
+
} from "./emception.js";
|
package/src/compiler/index.js
CHANGED
package/src/index.d.ts
CHANGED
|
@@ -408,6 +408,19 @@ export function createRecipientKeypairHex(): Promise<{
|
|
|
408
408
|
privateKeyHex: string;
|
|
409
409
|
}>;
|
|
410
410
|
|
|
411
|
+
export type {
|
|
412
|
+
EmceptionCommandResult,
|
|
413
|
+
SharedEmceptionFileContent,
|
|
414
|
+
SharedEmceptionHandle,
|
|
415
|
+
SharedEmceptionSession,
|
|
416
|
+
} from "./compiler/emception.js";
|
|
417
|
+
|
|
418
|
+
export {
|
|
419
|
+
createSharedEmceptionSession,
|
|
420
|
+
loadSharedEmception,
|
|
421
|
+
withSharedEmception,
|
|
422
|
+
} from "./compiler/emception.js";
|
|
423
|
+
|
|
411
424
|
// --- Standards ---
|
|
412
425
|
|
|
413
426
|
export interface StandardsEntry {
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export {
|
|
2
|
+
DefaultInvokeExports,
|
|
3
|
+
DefaultManifestExports,
|
|
4
|
+
DrainPolicy,
|
|
5
|
+
ExternalInterfaceDirection,
|
|
6
|
+
ExternalInterfaceKind,
|
|
7
|
+
InvokeSurface,
|
|
8
|
+
RuntimeTarget,
|
|
9
|
+
} from "../index.js";
|
|
10
|
+
|
|
11
|
+
export function isArrayBufferLike(value: unknown): boolean;
|
|
12
|
+
export function hasByteAddressableBuffer(value: unknown): boolean;
|
|
13
|
+
export function toUint8Array(value: unknown): Uint8Array | null;
|