@velarscript/web 0.25.0 → 0.26.1
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/dist/analyzer.d.ts.map +1 -1
- package/dist/analyzer.js +4 -8
- package/dist/analyzer.js.map +1 -1
- package/dist/compiler.d.ts.map +1 -1
- package/dist/compiler.js +39 -25
- package/dist/compiler.js.map +1 -1
- package/dist/editor.d.ts +1 -0
- package/dist/editor.d.ts.map +1 -1
- package/dist/editor.js +215 -1
- package/dist/editor.js.map +1 -1
- package/dist/look.d.ts +25 -0
- package/dist/look.d.ts.map +1 -1
- package/dist/look.js +98 -21
- package/dist/look.js.map +1 -1
- package/dist/semantic.d.ts.map +1 -1
- package/dist/semantic.js +8 -3
- package/dist/semantic.js.map +1 -1
- package/dist/worker-runtime.d.ts.map +1 -1
- package/dist/worker-runtime.js +16 -8
- package/dist/worker-runtime.js.map +1 -1
- package/package.json +2 -2
package/dist/worker-runtime.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export const VELAR_WEB_WORKER_RUNTIME = String.raw `
|
|
2
2
|
import { workerEntries as __velarWorkerEntries } from "velar/worker-manifest";
|
|
3
3
|
import { Cancellation, CancellationError, TaskTimeoutError } from "velar/task";
|
|
4
|
+
import { Bytes as __velarWorkerBinaryRuntime } from "velar/binary";
|
|
4
5
|
|
|
5
6
|
const __velarNativeWorker = globalThis.Worker;
|
|
6
7
|
const __velarWorkerStates = new WeakMap();
|
|
@@ -11,12 +12,12 @@ const __velarWorkerCancelGrace = 1000;
|
|
|
11
12
|
function __velarWorkerInteger(value, minimum, maximum, name) { if (!Number.isSafeInteger(value) || value < minimum || value > maximum) throw new RangeError(name + " must be an integer from " + minimum + " through " + maximum); return value; }
|
|
12
13
|
function __velarWorkerRuntimeType(value, name) { if ((typeof value !== "object" && typeof value !== "function") || value === null || typeof value.parse !== "function" || typeof value.is !== "function") throw new TypeError(name + " must be a runtime Type"); return value; }
|
|
13
14
|
function __velarWorkerDuration(value) { if (value === null) return 0; if (typeof value !== "string") throw new TypeError("Worker timeout must be Duration or null"); const match = /^([+-]?(?:\d+(?:\.\d+)?|\.\d+))(ms|s)$/.exec(value); if (!match) throw new TypeError("Worker timeout must be Duration such as 200ms or 2s"); const milliseconds = Number(match[1]) * (match[2] === "s" ? 1000 : 1); if (!Number.isFinite(milliseconds) || milliseconds < 0 || milliseconds > 2147483647) throw new RangeError("Worker timeout is outside the supported range"); return milliseconds; }
|
|
14
|
-
function __velarWorkerTransfer(value) {
|
|
15
|
+
function __velarWorkerTransfer(value, adopt = false) {
|
|
15
16
|
const transfers = []; const buffers = new Set(); const objects = new Set(); const pending = [{ value, depth: 0 }]; let visited = 0; let transferBytes = 0;
|
|
16
17
|
while (pending.length > 0 && visited < 10000 && transfers.length < 1024) {
|
|
17
18
|
const current = pending.pop(); const item = current.value; visited += 1;
|
|
18
19
|
if (item === null || (typeof item !== "object" && typeof item !== "function") || objects.has(item)) continue; objects.add(item);
|
|
19
|
-
if (ArrayBuffer.isView(item)) { const supported = item instanceof Uint8Array || item instanceof Uint16Array || item instanceof Uint32Array || item instanceof Float32Array; const buffer = item.buffer; if (supported && buffer instanceof ArrayBuffer && item.byteOffset === 0 && item.byteLength === buffer.byteLength &&
|
|
20
|
+
if (ArrayBuffer.isView(item)) { const supported = item instanceof Uint8Array || item instanceof Uint16Array || item instanceof Uint32Array || item instanceof Float32Array; const buffer = item.buffer; if (supported && buffer instanceof ArrayBuffer && item.byteOffset === 0 && item.byteLength === buffer.byteLength && transferBytes + buffer.byteLength <= 64 * 1024 * 1024) { if (adopt) __velarWorkerBinaryRuntime.__velarAdoptTransferredBuffer(item); if (buffer.byteLength > 0 && !buffers.has(buffer)) { buffers.add(buffer); transfers.push(buffer); transferBytes += buffer.byteLength; } } continue; }
|
|
20
21
|
if (current.depth >= 32) continue; const depth = current.depth + 1;
|
|
21
22
|
if (item instanceof Map) { for (const [key, nested] of item) pending.push({ value: key, depth }, { value: nested, depth }); continue; }
|
|
22
23
|
if (item instanceof Set) { for (const nested of item) pending.push({ value: nested, depth }); continue; }
|
|
@@ -24,13 +25,20 @@ function __velarWorkerTransfer(value) {
|
|
|
24
25
|
}
|
|
25
26
|
return transfers;
|
|
26
27
|
}
|
|
27
|
-
function __velarWorkerOutbound(value) {
|
|
28
|
-
const
|
|
29
|
-
if (
|
|
28
|
+
function __velarWorkerOutbound(value, source) {
|
|
29
|
+
const transfers = __velarWorkerTransfer(value);
|
|
30
|
+
if (transfers.length === 0) return { value, transfers };
|
|
31
|
+
const sourceTransfers = new Set(__velarWorkerTransfer(source));
|
|
32
|
+
if (!transfers.some(buffer => sourceTransfers.has(buffer))) return { value, transfers };
|
|
30
33
|
if (typeof __velarWorkerStructuredClone !== "function") throw new Error("The structured clone API is unavailable");
|
|
31
34
|
const snapshot = __velarWorkerStructuredClone(value);
|
|
32
35
|
return { value: snapshot, transfers: __velarWorkerTransfer(snapshot) };
|
|
33
36
|
}
|
|
37
|
+
function __velarWorkerInbound(Type, value) {
|
|
38
|
+
if (!Type.is(value)) return Type.parse(value);
|
|
39
|
+
__velarWorkerTransfer(value, true);
|
|
40
|
+
return value;
|
|
41
|
+
}
|
|
34
42
|
function __velarWorkerFailure(error) { return { name: typeof error?.name === "string" ? error.name : "Error", message: typeof error?.message === "string" ? error.message : String(error), stack: typeof error?.stack === "string" ? error.stack : "" }; }
|
|
35
43
|
export class WorkerBackpressureError extends Error { constructor(message = "Worker queue is full") { super(message); this.name = "WorkerBackpressureError"; } }
|
|
36
44
|
export class WorkerCallError extends Error { constructor(message = "Worker call failed") { super(message); this.name = "WorkerCallError"; } }
|
|
@@ -44,7 +52,7 @@ const __velarWorkerPrototype = Object.freeze({
|
|
|
44
52
|
if (state.pending.size >= state.capacity) return Promise.reject(new WorkerBackpressureError("Worker queue capacity " + state.capacity + " is full"));
|
|
45
53
|
if (cancellation !== null && !Cancellation.is(cancellation)) return Promise.reject(new TypeError("Worker cancellation must be Cancellation or null"));
|
|
46
54
|
let validated; try { validated = state.RequestType.parse(request); } catch (error) { return Promise.reject(error); }
|
|
47
|
-
let outbound; try { outbound = __velarWorkerOutbound(validated); } catch (error) { return Promise.reject(error); }
|
|
55
|
+
let outbound; try { outbound = __velarWorkerOutbound(validated, request); } catch (error) { return Promise.reject(error); }
|
|
48
56
|
const milliseconds = __velarWorkerDuration(timeout); const id = state.nextId++;
|
|
49
57
|
return new Promise((resolve, reject) => { const pending = { resolve, reject, cancel: null, reason: "Worker call cancelled", timeout, timer: null, unsubscribe: () => null }; state.pending.set(id, pending); state.instance.postMessage({ kind: "call", id, value: outbound.value }, outbound.transfers); if (cancellation !== null) pending.unsubscribe = Cancellation.__velarOn(cancellation, reason => { if (!state.pending.has(id) || pending.cancel) return; pending.cancel = "cancel"; pending.reason = reason ?? pending.reason; state.instance.postMessage({ kind: "cancel", id, reason: pending.reason }); }); if (milliseconds > 0) pending.timer = setTimeout(() => { if (!state.pending.has(id) || pending.cancel) return; state.pending.delete(id); pending.unsubscribe(); state.instance.postMessage({ kind: "cancel", id, reason: "Worker call timed out" }); state.abandoned.set(id, setTimeout(() => __velarWorkerCrash(state, new WorkerCrashedError("Worker did not acknowledge a cancelled call within " + __velarWorkerCancelGrace + "ms")), __velarWorkerCancelGrace)); reject(new TaskTimeoutError("Worker call timed out after " + timeout)); }, milliseconds); });
|
|
50
58
|
},
|
|
@@ -53,7 +61,7 @@ const __velarWorkerPrototype = Object.freeze({
|
|
|
53
61
|
function __velarWorkerClient(name, RequestType, ResponseType, capacity) {
|
|
54
62
|
if (typeof __velarNativeWorker !== "function") throw new Error("The browser Worker API is unavailable"); if (typeof name !== "string" || !Object.hasOwn(__velarWorkerEntries, name)) throw new RangeError("Unknown worker entry '" + String(name) + "'"); RequestType = __velarWorkerRuntimeType(RequestType, "Worker request Type"); ResponseType = __velarWorkerRuntimeType(ResponseType, "Worker response Type"); capacity = __velarWorkerInteger(capacity, 1, 10000, "Worker capacity");
|
|
55
63
|
const instance = new __velarNativeWorker(new URL("../" + __velarWorkerEntries[name], import.meta.url), { type: "module", name: "velar:" + name }); const value = Object.create(__velarWorkerPrototype); const state = { instance, RequestType, ResponseType, capacity, nextId: 1, pending: new Map(), abandoned: new Map(), closed: false }; __velarWorkerStates.set(value, state);
|
|
56
|
-
instance.addEventListener("message", event => { const message = event.data; if (!message || !Number.isSafeInteger(message.id)) { __velarWorkerCrash(state, new WorkerCrashedError("Worker returned an invalid message")); return; } const grace = state.abandoned.get(message.id); if (grace !== undefined) { clearTimeout(grace); state.abandoned.delete(message.id); } if (message.kind === "cancel-ack") return; const pending = state.pending.get(message.id); if (!pending) return; state.pending.delete(message.id); if (pending.timer) clearTimeout(pending.timer); pending.unsubscribe(); if (pending.cancel === "cancel") { pending.reject(new CancellationError(pending.reason)); return; } if (!message.ok) { const remote = message.error ?? {}; const failure = new WorkerCallError((remote.name ? remote.name + ": " : "") + (remote.message ?? "Worker call failed")); if (typeof remote.stack === "string" && remote.stack) failure.stack += "\nRemote worker:\n" + remote.stack; pending.reject(failure); return; } try { pending.resolve(state.ResponseType
|
|
64
|
+
instance.addEventListener("message", event => { const message = event.data; if (!message || !Number.isSafeInteger(message.id)) { __velarWorkerCrash(state, new WorkerCrashedError("Worker returned an invalid message")); return; } const grace = state.abandoned.get(message.id); if (grace !== undefined) { clearTimeout(grace); state.abandoned.delete(message.id); } if (message.kind === "cancel-ack") return; const pending = state.pending.get(message.id); if (!pending) return; state.pending.delete(message.id); if (pending.timer) clearTimeout(pending.timer); pending.unsubscribe(); if (pending.cancel === "cancel") { pending.reject(new CancellationError(pending.reason)); return; } if (!message.ok) { const remote = message.error ?? {}; const failure = new WorkerCallError((remote.name ? remote.name + ": " : "") + (remote.message ?? "Worker call failed")); if (typeof remote.stack === "string" && remote.stack) failure.stack += "\nRemote worker:\n" + remote.stack; pending.reject(failure); return; } try { pending.resolve(__velarWorkerInbound(state.ResponseType, message.value)); } catch (error) { pending.reject(error); } });
|
|
57
65
|
instance.addEventListener("error", event => __velarWorkerCrash(state, new WorkerCrashedError(event.message || "Worker crashed"))); instance.addEventListener("messageerror", () => __velarWorkerCrash(state, new WorkerCrashedError("Worker returned an unreadable message"))); return Object.freeze(value);
|
|
58
66
|
}
|
|
59
67
|
const __velarWorkerPoolPrototype = Object.freeze({
|
|
@@ -67,7 +75,7 @@ export function worker(name, RequestType, ResponseType, capacity = 64) { return
|
|
|
67
75
|
export function workerPool(name, RequestType, ResponseType, size, capacity = 256) { size = __velarWorkerInteger(size, 1, 32, "Worker pool size"); capacity = __velarWorkerInteger(capacity, size, 10000, "Worker pool capacity"); const workers = Array.from({ length: size }, () => __velarWorkerClient(name, RequestType, ResponseType, Math.ceil(capacity / size))); const value = Object.create(__velarWorkerPoolPrototype); __velarWorkerPoolStates.set(value, { workers, capacity, closed: false }); return Object.freeze(value); }
|
|
68
76
|
export function serveWorker(RequestType, ResponseType, handler, capacity = 64) {
|
|
69
77
|
if (__velarWorkerServerInstalled) throw new Error("A worker entry may call serveWorker() only once"); RequestType = __velarWorkerRuntimeType(RequestType, "Worker request Type"); ResponseType = __velarWorkerRuntimeType(ResponseType, "Worker response Type"); if (typeof handler !== "function") throw new TypeError("serveWorker handler must be an async function"); capacity = __velarWorkerInteger(capacity, 1, 10000, "Worker capacity"); __velarWorkerServerInstalled = true;
|
|
70
|
-
const queue = []; const active = new Map(); let running = false; const drain = async () => { if (running) return; running = true; while (queue.length) { const message = queue.shift(); const cancellation = Cancellation.__velarCreate(); active.set(message.id, cancellation); try { const request = RequestType
|
|
78
|
+
const queue = []; const active = new Map(); let running = false; const drain = async () => { if (running) return; running = true; while (queue.length) { const message = queue.shift(); const cancellation = Cancellation.__velarCreate(); active.set(message.id, cancellation); try { const request = __velarWorkerInbound(RequestType, message.value); const handled = await handler(request, cancellation); const result = ResponseType.parse(handled); const outbound = __velarWorkerOutbound(result, handled); globalThis.postMessage({ id: message.id, ok: true, value: outbound.value }, { transfer: outbound.transfers }); } catch (error) { globalThis.postMessage({ id: message.id, ok: false, error: __velarWorkerFailure(error) }); } finally { active.delete(message.id); } } running = false; };
|
|
71
79
|
globalThis.addEventListener("message", event => { const message = event.data; if (!message || !Number.isSafeInteger(message.id)) return; if (message.kind === "cancel") { globalThis.postMessage({ id: message.id, kind: "cancel-ack" }); const cancellation = active.get(message.id); if (cancellation) Cancellation.__velarCancel(cancellation, typeof message.reason === "string" ? message.reason : "Worker call cancelled"); else { const index = queue.findIndex(item => item.id === message.id); if (index >= 0) { queue.splice(index, 1); globalThis.postMessage({ id: message.id, ok: false, error: __velarWorkerFailure(new CancellationError(typeof message.reason === "string" ? message.reason : "Worker call cancelled")) }); } } return; } if (message.kind !== "call") return; if (queue.length + active.size >= capacity) { globalThis.postMessage({ id: message.id, ok: false, error: __velarWorkerFailure(new WorkerBackpressureError()) }); return; } queue.push(message); drain(); }); return null;
|
|
72
80
|
}
|
|
73
81
|
`.trimStart();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker-runtime.js","sourceRoot":"","sources":["../src/worker-runtime.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,wBAAwB,GAAG,MAAM,CAAC,GAAG,CAAA
|
|
1
|
+
{"version":3,"file":"worker-runtime.js","sourceRoot":"","sources":["../src/worker-runtime.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,wBAAwB,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgFjD,CAAC,SAAS,EAAE,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@velarscript/web",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.26.1",
|
|
4
4
|
"description": "The official VelarScript Web framework contract and browser runtime.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
}
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@velarscript/compiler": "0.
|
|
36
|
+
"@velarscript/compiler": "0.26.1"
|
|
37
37
|
},
|
|
38
38
|
"exports": {
|
|
39
39
|
".": {
|