@hew-lang/sandbox-vm 0.6.1 → 0.6.3
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 +48 -4
- package/cli/test-runner.mjs +4 -1
- package/dist/interpreter/interpreter.d.ts +1 -1
- package/dist/interpreter/interpreter.js +1 -1
- package/dist/interpreter/parity-runner.js +1 -1
- package/dist/interpreter/run-program.d.ts +3 -2
- package/dist/interpreter/run-program.js +5 -3
- package/dist/interpreter/schema-validator.d.ts +9 -4
- package/dist/interpreter/schema-validator.js +15 -49
- package/dist/interpreter/strings.d.ts +21 -0
- package/dist/interpreter/strings.js +191 -0
- package/dist/interpreter/types.d.ts +6 -0
- package/dist/interpreter/unicode-data.d.ts +4 -0
- package/dist/interpreter/unicode-data.js +1673 -0
- package/dist/interpreter/unicode.d.ts +3 -0
- package/dist/interpreter/unicode.js +35 -0
- package/dist/interpreter/v1/actor-identity.d.ts +3 -0
- package/dist/interpreter/v1/actor-identity.js +7 -0
- package/dist/interpreter/v1/actor-waits.d.ts +6 -0
- package/dist/interpreter/v1/actor-waits.js +33 -0
- package/dist/interpreter/v1/browser-directory.d.ts +107 -0
- package/dist/interpreter/v1/browser-directory.js +288 -0
- package/dist/interpreter/v1/calendar.d.ts +4 -0
- package/dist/interpreter/v1/calendar.js +64 -0
- package/dist/interpreter/v1/codecs.js +8 -11
- package/dist/interpreter/v1/exec.d.ts +2 -2
- package/dist/interpreter/v1/exec.js +1761 -489
- package/dist/interpreter/v1/instructions.generated.d.ts +1135 -0
- package/dist/interpreter/v1/instructions.generated.js +106 -0
- package/dist/interpreter/v1/package-graph.d.ts +2 -0
- package/dist/interpreter/v1/package-graph.js +729 -0
- package/dist/interpreter/v1/package-validator.generated.d.ts +3 -0
- package/dist/interpreter/v1/package-validator.generated.js +75902 -0
- package/dist/interpreter/v1/package.d.ts +5 -645
- package/dist/interpreter/v1/package.js +0 -12
- package/dist/interpreter/v1/pipes.d.ts +1 -0
- package/dist/interpreter/v1/pipes.js +9 -6
- package/dist/interpreter/v1/scheduler.d.ts +8 -1
- package/dist/interpreter/v1/scheduler.js +50 -11
- package/dist/interpreter/v1/shims.d.ts +260 -7
- package/dist/interpreter/v1/shims.js +647 -175
- package/dist/interpreter/v1/structural.d.ts +2 -28
- package/dist/interpreter/v1/timestamp.d.ts +7 -0
- package/dist/interpreter/v1/timestamp.js +54 -0
- package/dist/interpreter/v1/validate.d.ts +8 -2
- package/dist/interpreter/v1/validate.js +284 -200
- package/dist/interpreter/v1/value-operations.d.ts +11 -3
- package/dist/interpreter/v1/value-operations.js +70 -18
- package/dist/interpreter/values.d.ts +30 -8
- package/dist/interpreter/values.js +31 -5
- package/dist/interpreter/worker.js +4 -3
- package/package.json +4 -4
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { RuntimeContinuation } from "./shims.js";
|
|
2
|
+
import { type VmValue, type RcOwner } from "../values.js";
|
|
2
3
|
import { type PackageV1 } from "./package.js";
|
|
3
4
|
export type ValueRequest = {
|
|
5
|
+
kind: "fault";
|
|
6
|
+
message: string;
|
|
7
|
+
} | {
|
|
4
8
|
kind: "call";
|
|
5
9
|
callee: number;
|
|
6
10
|
args: VmValue[];
|
|
@@ -13,8 +17,12 @@ export type ValueProgram = Generator<ValueRequest, VmValue, VmValue>;
|
|
|
13
17
|
/** Execute the exact checked plan, including every derived component selection.
|
|
14
18
|
* A user method yields to an ordinary VM frame and can suspend or fail. */
|
|
15
19
|
export declare function selectedValue(pkg: PackageV1, id: number, args: VmValue[]): ValueProgram;
|
|
16
|
-
export declare function sharedOperation(operation:
|
|
20
|
+
export declare function sharedOperation(operation: Extract<RuntimeContinuation, {
|
|
21
|
+
handler: "shared";
|
|
22
|
+
}>["operation"], args: VmValue[], owner: RcOwner | null, typeName: string, option: (value: VmValue | null) => VmValue): ValueProgram;
|
|
17
23
|
/** Open-addressed collection operations preserve native callback ordering:
|
|
18
24
|
* growth hashes occupied slots before probing the incoming key. Staged slots
|
|
19
25
|
* borrow their owners until every fallible callback has completed. */
|
|
20
|
-
export declare function collectionOperation(pkg: PackageV1,
|
|
26
|
+
export declare function collectionOperation(pkg: PackageV1, request: Extract<RuntimeContinuation, {
|
|
27
|
+
handler: "collection";
|
|
28
|
+
}>, args: VmValue[], callbacks: number[], shape: number | null | undefined): ValueProgram;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { UNIT, cloneValue, compareUtf8 } from "../values.js";
|
|
1
|
+
import { UNIT, cloneValue, compareUtf8, } from "../values.js";
|
|
2
2
|
import { runtimeTag, } from "./package.js";
|
|
3
3
|
import { ShimFault } from "./shims.js";
|
|
4
|
+
import { actorIdentical } from "./actor-identity.js";
|
|
4
5
|
const OFFSET = 0xcbf29ce484222325n;
|
|
5
6
|
const PRIME = 0x100000001b3n;
|
|
6
7
|
const integer = (value) => ({
|
|
@@ -81,7 +82,7 @@ export function* selectedValue(pkg, id, args) {
|
|
|
81
82
|
const plan = pkg.value_capabilities[id];
|
|
82
83
|
if (!plan)
|
|
83
84
|
throw new Error(`selected value plan ${id} is missing`);
|
|
84
|
-
if (plan.callable
|
|
85
|
+
if (plan.callable != null)
|
|
85
86
|
return yield { kind: "call", callee: plan.callable, args };
|
|
86
87
|
const [left, right] = args;
|
|
87
88
|
if (!left)
|
|
@@ -117,10 +118,16 @@ export function* selectedValue(pkg, id, args) {
|
|
|
117
118
|
bytes.every((value, i) => BigInt(value) === bits(right.items[i], "u8")));
|
|
118
119
|
throw new Error("bytes ordering is outside the checked capability domain");
|
|
119
120
|
}
|
|
121
|
+
if (left.kind === "actor" || left.kind === "supervisor") {
|
|
122
|
+
if (equality)
|
|
123
|
+
return boolean(actorIdentical(left, right));
|
|
124
|
+
throw new Error("actor capability is outside the checked equality domain");
|
|
125
|
+
}
|
|
120
126
|
const components = plan.components ?? [];
|
|
121
127
|
let fields;
|
|
122
128
|
let others = [];
|
|
123
129
|
let selections;
|
|
130
|
+
let vectorHashLength;
|
|
124
131
|
if (left.kind === "record") {
|
|
125
132
|
fields = left.fields;
|
|
126
133
|
selections = components;
|
|
@@ -144,13 +151,20 @@ export function* selectedValue(pkg, id, args) {
|
|
|
144
151
|
selections = plan.variants?.[left.tag] ?? [];
|
|
145
152
|
}
|
|
146
153
|
else if (left.kind === "vector") {
|
|
147
|
-
if (hash)
|
|
148
|
-
throw new Error("derived vector Hash is outside the checked capability domain");
|
|
149
|
-
if (right?.kind !== "vector" || left.items.length !== right.items.length)
|
|
150
|
-
return boolean(false);
|
|
151
154
|
fields = left.items;
|
|
152
|
-
|
|
153
|
-
|
|
155
|
+
if (hash) {
|
|
156
|
+
const elementSelection = components[0];
|
|
157
|
+
if (!elementSelection)
|
|
158
|
+
throw new Error("derived vector Hash lacks its checked element selection");
|
|
159
|
+
selections = fields.map(() => elementSelection);
|
|
160
|
+
vectorHashLength = fields.length;
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
if (right?.kind !== "vector" || left.items.length !== right.items.length)
|
|
164
|
+
return boolean(false);
|
|
165
|
+
others = right.items;
|
|
166
|
+
selections = fields.map(() => components[0]);
|
|
167
|
+
}
|
|
154
168
|
}
|
|
155
169
|
else {
|
|
156
170
|
if (hash)
|
|
@@ -159,7 +173,9 @@ export function* selectedValue(pkg, id, args) {
|
|
|
159
173
|
return boolean(!!right && bits(left, plan.ty) === bits(right, plan.ty));
|
|
160
174
|
return scalarOrdering(left, right, plan.ty, ordering);
|
|
161
175
|
}
|
|
162
|
-
let state =
|
|
176
|
+
let state = vectorHashLength === undefined
|
|
177
|
+
? OFFSET
|
|
178
|
+
: mix(OFFSET, BigInt(vectorHashLength));
|
|
163
179
|
for (const [at, field] of fields.entries()) {
|
|
164
180
|
const selection = selections[at];
|
|
165
181
|
if (!selection)
|
|
@@ -247,15 +263,44 @@ function option(pkg, shape, value) {
|
|
|
247
263
|
payload: value === undefined ? [] : [value],
|
|
248
264
|
};
|
|
249
265
|
}
|
|
250
|
-
export function* sharedOperation(operation, args) {
|
|
251
|
-
if (operation === "RcNew")
|
|
252
|
-
|
|
266
|
+
export function* sharedOperation(operation, args, owner, typeName, option) {
|
|
267
|
+
if (operation === "RcNew") {
|
|
268
|
+
if (owner?.closed)
|
|
269
|
+
throw new Error("Rc allocation after actor cleanup quiesced");
|
|
270
|
+
const cell = {
|
|
271
|
+
value: args[0],
|
|
272
|
+
refs: 1,
|
|
273
|
+
weak: 0,
|
|
274
|
+
dying: false,
|
|
275
|
+
owner,
|
|
276
|
+
typeName,
|
|
277
|
+
};
|
|
278
|
+
owner?.blocks.add(cell);
|
|
279
|
+
return { kind: "rc", cell, closed: false };
|
|
280
|
+
}
|
|
253
281
|
const receiver = args[0];
|
|
282
|
+
if (operation === "WeakCloneRc" || operation === "WeakUpgradeRc") {
|
|
283
|
+
if (receiver?.kind !== "weak" || receiver.closed)
|
|
284
|
+
throw new Error("shared operation has no live Weak");
|
|
285
|
+
if (operation === "WeakCloneRc")
|
|
286
|
+
return cloneValue(receiver);
|
|
287
|
+
if (receiver.cell.dying || receiver.cell.refs === 0)
|
|
288
|
+
return option(null);
|
|
289
|
+
receiver.cell.refs += 1;
|
|
290
|
+
return option({ kind: "rc", cell: receiver.cell, closed: false });
|
|
291
|
+
}
|
|
254
292
|
if (receiver?.kind !== "rc" || receiver.closed)
|
|
255
293
|
throw new Error("shared operation has no live Rc");
|
|
294
|
+
if (!receiver.cell || receiver.cell.dying) {
|
|
295
|
+
yield { kind: "fault", message: "Rc payload is being released" };
|
|
296
|
+
return UNIT;
|
|
297
|
+
}
|
|
256
298
|
switch (operation) {
|
|
257
299
|
case "RcClone":
|
|
258
300
|
return cloneValue(receiver);
|
|
301
|
+
case "RcDowngrade":
|
|
302
|
+
receiver.cell.weak += 1;
|
|
303
|
+
return { kind: "weak", cell: receiver.cell, closed: false };
|
|
259
304
|
case "RcGet":
|
|
260
305
|
return cloneValue(receiver.cell.value);
|
|
261
306
|
case "RcIsUnique":
|
|
@@ -263,21 +308,24 @@ export function* sharedOperation(operation, args) {
|
|
|
263
308
|
case "RcStrongCount":
|
|
264
309
|
return integer(BigInt(receiver.cell.refs));
|
|
265
310
|
case "RcWeakCount":
|
|
266
|
-
return integer(
|
|
311
|
+
return integer(BigInt(receiver.cell.weak));
|
|
267
312
|
case "RcSet": {
|
|
268
313
|
const old = receiver.cell.value;
|
|
269
314
|
receiver.cell.value = args[1];
|
|
270
315
|
yield { kind: "release", value: old };
|
|
271
316
|
return UNIT;
|
|
272
317
|
}
|
|
273
|
-
default:
|
|
274
|
-
|
|
318
|
+
default: {
|
|
319
|
+
const exhaustive = operation;
|
|
320
|
+
throw new Error(`shared operation ${String(exhaustive)} has no executor`);
|
|
321
|
+
}
|
|
275
322
|
}
|
|
276
323
|
}
|
|
277
324
|
/** Open-addressed collection operations preserve native callback ordering:
|
|
278
325
|
* growth hashes occupied slots before probing the incoming key. Staged slots
|
|
279
326
|
* borrow their owners until every fallible callback has completed. */
|
|
280
|
-
export function* collectionOperation(pkg,
|
|
327
|
+
export function* collectionOperation(pkg, request, args, callbacks, shape) {
|
|
328
|
+
const { family, operation } = request;
|
|
281
329
|
if (family === "Vector") {
|
|
282
330
|
const receiver = args[0];
|
|
283
331
|
if (receiver?.kind !== "vector")
|
|
@@ -337,7 +385,10 @@ export function* collectionOperation(pkg, family, operation, args, callbacks, sh
|
|
|
337
385
|
};
|
|
338
386
|
return receiver;
|
|
339
387
|
}
|
|
340
|
-
if (
|
|
388
|
+
if (operation === "Keys" ||
|
|
389
|
+
operation === "Values" ||
|
|
390
|
+
operation === "Elements" ||
|
|
391
|
+
operation === "Entries") {
|
|
341
392
|
return {
|
|
342
393
|
kind: "vector",
|
|
343
394
|
elementType: "",
|
|
@@ -417,5 +468,6 @@ export function* collectionOperation(pkg, family, operation, args, callbacks, sh
|
|
|
417
468
|
? boolean(result.found)
|
|
418
469
|
: option(pkg, shape, entry?.value));
|
|
419
470
|
}
|
|
420
|
-
|
|
471
|
+
const exhaustive = operation;
|
|
472
|
+
throw new Error(`collection operation ${String(exhaustive)} has no executor`);
|
|
421
473
|
}
|
|
@@ -1,10 +1,26 @@
|
|
|
1
1
|
import type { JsonValue } from "./types.js";
|
|
2
|
+
import { IndexedString } from "./strings.js";
|
|
3
|
+
export { stringValue } from "./strings.js";
|
|
4
|
+
/** One creator-owned membership list; payload destruction uses the ordinary VM release path. */
|
|
5
|
+
export interface RcOwner {
|
|
6
|
+
blocks: Set<RcCell>;
|
|
7
|
+
closed: boolean;
|
|
8
|
+
}
|
|
9
|
+
export interface RcCell {
|
|
10
|
+
value: VmValue;
|
|
11
|
+
refs: number;
|
|
12
|
+
weak: number;
|
|
13
|
+
dying: boolean;
|
|
14
|
+
owner: RcOwner | null;
|
|
15
|
+
typeName: string;
|
|
16
|
+
}
|
|
2
17
|
export type VmValue = {
|
|
3
18
|
kind: "rc";
|
|
4
|
-
cell:
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
19
|
+
cell: RcCell | null;
|
|
20
|
+
closed: boolean;
|
|
21
|
+
} | {
|
|
22
|
+
kind: "weak";
|
|
23
|
+
cell: RcCell;
|
|
8
24
|
closed: boolean;
|
|
9
25
|
} | {
|
|
10
26
|
kind: "unit";
|
|
@@ -17,10 +33,7 @@ export type VmValue = {
|
|
|
17
33
|
} | {
|
|
18
34
|
kind: "f64";
|
|
19
35
|
value: number;
|
|
20
|
-
} | {
|
|
21
|
-
kind: "string";
|
|
22
|
-
value: string;
|
|
23
|
-
} | {
|
|
36
|
+
} | IndexedString | {
|
|
24
37
|
kind: "actor";
|
|
25
38
|
id: string;
|
|
26
39
|
} | {
|
|
@@ -38,6 +51,15 @@ export type VmValue = {
|
|
|
38
51
|
} | {
|
|
39
52
|
kind: "task";
|
|
40
53
|
id: string;
|
|
54
|
+
} | {
|
|
55
|
+
kind: "host_resource";
|
|
56
|
+
resource: number;
|
|
57
|
+
owner: import("./v1/browser-directory.js").BrowserFsOwner;
|
|
58
|
+
} | {
|
|
59
|
+
kind: "timer";
|
|
60
|
+
resource: number;
|
|
61
|
+
cancel(): void;
|
|
62
|
+
closed: boolean;
|
|
41
63
|
} | {
|
|
42
64
|
kind: "generator";
|
|
43
65
|
id: string;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { stringValue } from "./strings.js";
|
|
2
|
+
export { stringValue } from "./strings.js";
|
|
1
3
|
export const UNIT = { kind: "unit" };
|
|
2
4
|
const UTF8_ENCODER = new TextEncoder();
|
|
3
5
|
export function compareUtf8(a, b) {
|
|
@@ -18,14 +20,24 @@ export function cloneValue(value) {
|
|
|
18
20
|
case "rc":
|
|
19
21
|
if (value.closed)
|
|
20
22
|
throw new Error("cannot copy a released Rc");
|
|
23
|
+
if (!value.cell || value.cell.dying)
|
|
24
|
+
return { kind: "rc", cell: null, closed: false };
|
|
21
25
|
value.cell.refs += 1;
|
|
22
26
|
return { kind: "rc", cell: value.cell, closed: false };
|
|
27
|
+
case "weak":
|
|
28
|
+
if (value.closed)
|
|
29
|
+
throw new Error("cannot copy a released Weak");
|
|
30
|
+
value.cell.weak += 1;
|
|
31
|
+
return { kind: "weak", cell: value.cell, closed: false };
|
|
32
|
+
case "host_resource":
|
|
33
|
+
throw new Error("a filesystem owner cannot be copied");
|
|
34
|
+
case "timer":
|
|
35
|
+
throw new Error("a timer cancellation handle cannot be copied");
|
|
23
36
|
case "unit":
|
|
24
37
|
return UNIT;
|
|
25
38
|
case "bool":
|
|
26
39
|
case "i64":
|
|
27
40
|
case "f64":
|
|
28
|
-
case "string":
|
|
29
41
|
case "actor":
|
|
30
42
|
case "supervisor":
|
|
31
43
|
case "monitor":
|
|
@@ -38,6 +50,8 @@ export function cloneValue(value) {
|
|
|
38
50
|
case "duplex":
|
|
39
51
|
case "function": // function values are immutable — identity clone
|
|
40
52
|
return { ...value };
|
|
53
|
+
case "string":
|
|
54
|
+
return value.clone();
|
|
41
55
|
case "regex":
|
|
42
56
|
return {
|
|
43
57
|
kind: "regex",
|
|
@@ -106,9 +120,9 @@ export function valueFromLiteral(value) {
|
|
|
106
120
|
? { kind: "i64", value: BigInt(value) }
|
|
107
121
|
: { kind: "f64", value };
|
|
108
122
|
case "string":
|
|
109
|
-
return
|
|
123
|
+
return stringValue(value);
|
|
110
124
|
default:
|
|
111
|
-
return
|
|
125
|
+
return stringValue(canonicalJson(value));
|
|
112
126
|
}
|
|
113
127
|
}
|
|
114
128
|
function taggedI64Literal(value) {
|
|
@@ -192,6 +206,10 @@ export function renderF64(value) {
|
|
|
192
206
|
}
|
|
193
207
|
export function renderStdout(value) {
|
|
194
208
|
switch (value.kind) {
|
|
209
|
+
case "host_resource":
|
|
210
|
+
return "FilesystemOwner";
|
|
211
|
+
case "timer":
|
|
212
|
+
return "Timer";
|
|
195
213
|
case "unit":
|
|
196
214
|
return "()";
|
|
197
215
|
case "bool":
|
|
@@ -232,7 +250,9 @@ export function renderStdout(value) {
|
|
|
232
250
|
case "vector":
|
|
233
251
|
return canonicalJson(value.items.map((item) => toJsonValue(item)));
|
|
234
252
|
case "rc":
|
|
235
|
-
return
|
|
253
|
+
return "Rc";
|
|
254
|
+
case "weak":
|
|
255
|
+
return "Weak";
|
|
236
256
|
case "map":
|
|
237
257
|
return canonicalJson(mapEntriesJson(value));
|
|
238
258
|
case "dyn":
|
|
@@ -245,6 +265,10 @@ export function renderStdout(value) {
|
|
|
245
265
|
}
|
|
246
266
|
export function toJsonValue(value) {
|
|
247
267
|
switch (value.kind) {
|
|
268
|
+
case "host_resource":
|
|
269
|
+
return "FilesystemOwner";
|
|
270
|
+
case "timer":
|
|
271
|
+
return "Timer";
|
|
248
272
|
case "unit":
|
|
249
273
|
return null;
|
|
250
274
|
case "bool":
|
|
@@ -280,7 +304,9 @@ export function toJsonValue(value) {
|
|
|
280
304
|
case "vector":
|
|
281
305
|
return value.items.map(toJsonValue);
|
|
282
306
|
case "rc":
|
|
283
|
-
return
|
|
307
|
+
return "Rc";
|
|
308
|
+
case "weak":
|
|
309
|
+
return "Weak";
|
|
284
310
|
case "map":
|
|
285
311
|
return mapEntriesJson(value);
|
|
286
312
|
case "dyn":
|
|
@@ -3,18 +3,19 @@ import { buildPlaygroundState } from "./playground.js";
|
|
|
3
3
|
import { runProgram } from "./run-program.js";
|
|
4
4
|
import { BytecodeValidationError } from "./schema-validator.js";
|
|
5
5
|
const worker = globalThis;
|
|
6
|
-
worker.addEventListener("message", (event) => {
|
|
6
|
+
worker.addEventListener("message", async (event) => {
|
|
7
7
|
const { id } = event.data;
|
|
8
8
|
try {
|
|
9
9
|
if (event.data.kind === "run") {
|
|
10
|
-
const result = runProgram(event.data.source, event.data.stdin);
|
|
10
|
+
const result = await runProgram(event.data.source, event.data.stdin, undefined, event.data.options);
|
|
11
11
|
worker.postMessage({ id, ok: true, result });
|
|
12
12
|
return;
|
|
13
13
|
}
|
|
14
14
|
const { bytecode, options } = event.data;
|
|
15
|
-
const trace = runBytecode(bytecode, {
|
|
15
|
+
const trace = await runBytecode(bytecode, {
|
|
16
16
|
...options,
|
|
17
17
|
codecBridge: globalThis.__hewSandboxExecuteCodec,
|
|
18
|
+
calendarBridge: globalThis.__hewSandboxExecuteCalendar,
|
|
18
19
|
});
|
|
19
20
|
worker.postMessage({
|
|
20
21
|
id,
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hew-lang/sandbox-vm",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.3",
|
|
4
4
|
"hewSource": {
|
|
5
|
-
"commit": "
|
|
6
|
-
"tag": "v0.6.
|
|
5
|
+
"commit": "97c1d865aa46afca1c75cf8ef2c1ea0d7f232471",
|
|
6
|
+
"tag": "v0.6.3",
|
|
7
7
|
"dirty": false
|
|
8
8
|
},
|
|
9
9
|
"description": "Deterministic TypeScript interpreter for the Hew educational sandbox",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"cli/"
|
|
22
22
|
],
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@hew-lang/wasm": "0.6.
|
|
24
|
+
"@hew-lang/wasm": "0.6.3"
|
|
25
25
|
},
|
|
26
26
|
"keywords": [
|
|
27
27
|
"hew",
|