@crowdedkingdoms/crowdyjs 8.17.0 → 8.19.0

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.
Files changed (34) hide show
  1. package/README.md +7 -0
  2. package/dist/domains/marketplace.d.ts +7 -1
  3. package/dist/domains/marketplace.d.ts.map +1 -1
  4. package/dist/domains/marketplace.js +10 -1
  5. package/dist/domains/playerCompute.d.ts +7 -1
  6. package/dist/domains/playerCompute.d.ts.map +1 -1
  7. package/dist/domains/playerCompute.js +10 -1
  8. package/dist/generated/graphql.d.ts +117 -16
  9. package/dist/generated/graphql.d.ts.map +1 -1
  10. package/dist/generated/graphql.js +10 -8
  11. package/dist/index.d.ts +5 -2
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +4 -2
  14. package/dist/kit/npcs.d.ts.map +1 -1
  15. package/dist/kit/social.d.ts.map +1 -1
  16. package/dist/live-coding/ide.d.ts +16 -0
  17. package/dist/live-coding/ide.d.ts.map +1 -0
  18. package/dist/live-coding/ide.js +399 -0
  19. package/dist/live-coding/live-coding-controller.d.ts +2 -0
  20. package/dist/live-coding/live-coding-controller.d.ts.map +1 -1
  21. package/dist/live-coding/live-coding-controller.js +1 -0
  22. package/dist/player-runtime/glue-runtime.d.ts +92 -0
  23. package/dist/player-runtime/glue-runtime.d.ts.map +1 -0
  24. package/dist/player-runtime/glue-runtime.js +222 -0
  25. package/dist/player-runtime/glue-sab.d.ts +55 -0
  26. package/dist/player-runtime/glue-sab.d.ts.map +1 -0
  27. package/dist/player-runtime/glue-sab.js +79 -0
  28. package/dist/player-runtime/player-code-broker.d.ts +16 -0
  29. package/dist/player-runtime/player-code-broker.d.ts.map +1 -1
  30. package/dist/player-runtime/player-code-broker.js +38 -3
  31. package/dist/player-runtime/player-glue-worker.d.ts +26 -54
  32. package/dist/player-runtime/player-glue-worker.d.ts.map +1 -1
  33. package/dist/player-runtime/player-glue-worker.js +130 -111
  34. package/package.json +10 -3
@@ -1,127 +1,146 @@
1
1
  /**
2
- * Platform glue worker for browser-target player WASM (player compute P3).
3
- *
4
- * This is the ONLY platform code that shares an execution context with an
5
- * untrusted player module. It runs inside a dedicated Web Worker and is
6
- * deliberately tiny and auditable:
2
+ * Browser Web Worker entry for browser-target player WASM (player compute
3
+ * P3/P5). This is the ONLY platform code that shares an execution context
4
+ * with an untrusted player module. It is intentionally thin: the auditable
5
+ * runtime + ABI marshalling live in [glue-runtime.ts] and the synchronous
6
+ * host-call transport in [glue-sab.ts]; this file only wires them to the
7
+ * worker message loop.
7
8
  *
8
9
  * - it instantiates the gas-injected player artifact with an import table
9
- * that exposes ONLY the `ck.*` host functions (every one forwards to the
10
- * page-side broker; nothing else is importable),
11
- * - it never has the DOM, `window`, auth tokens, `fetch`, or `importScripts`
12
- * of third-party code,
13
- * - it enforces a per-dispatch wall-clock watchdog and forwards the fuel
14
- * budget so a runaway dispatch traps locally (the fuel counter itself is
15
- * injected into the artifact by the server compile pipeline),
16
- * - it reports traps to the broker, which owns the local circuit breaker.
17
- *
18
- * Host calls are synchronous from the guest's perspective. In a worker that
19
- * is realized with a SharedArrayBuffer control block plus Atomics.wait: the
20
- * guest's import stub writes the request, wakes the page, and blocks until
21
- * the broker writes the reply. The message/really-shared plumbing lives in
22
- * buildImportObject / the init handler below; the pure, testable pieces
23
- * (budget parsing, host-fn table) are exported.
10
+ * that exposes ONLY `ck.*` + inert wasi stubs (nothing else importable),
11
+ * - it never has the DOM, `window`, auth tokens, `fetch`, or third-party
12
+ * `importScripts`,
13
+ * - `ck.host_call` blocks the worker on a SharedArrayBuffer while the
14
+ * page-side broker services the (async) server-authorized call and writes
15
+ * the reply back, so the guest sees a synchronous gateway,
16
+ * - it enforces a per-dispatch wall-clock watchdog and reports traps to the
17
+ * broker, which owns the local circuit breaker.
24
18
  *
25
- * This module is bundled to a standalone worker asset; it must not import
26
- * anything that pulls in the SDK client, DOM types, or Node built-ins.
19
+ * Re-exports the pure helpers + the runtime so tests and bundlers can use
20
+ * them without touching worker globals.
27
21
  */
28
- /** The exact host-call names the glue exposes to the guest (mirrors the broker allowlist). */
29
- export const GLUE_HOST_FUNCTIONS = [
30
- 'container_create',
31
- 'container_get',
32
- 'containers_list',
33
- 'container_delete',
34
- 'property_set',
35
- 'model_invoke',
36
- 'user_state_get',
37
- 'user_state_set',
38
- 'grid_state_get',
39
- 'grid_state_set',
40
- 'chunk_get',
41
- 'voxels_list',
42
- 'actors_list',
43
- 'actors_list_radius',
44
- 'voxel_set',
45
- 'emit_spatial',
46
- 'hud_set',
47
- 'overlay_draw',
48
- 'grid_permission_check',
49
- ];
50
- /** Parse the fuel budget the broker forwards; undefined/invalid => unbounded (server still meters). */
51
- export function parseFuelBudget(raw) {
52
- if (raw == null)
53
- return null;
54
- try {
55
- const v = BigInt(raw);
56
- return v > 0n ? v : null;
57
- }
58
- catch {
59
- return null;
60
- }
61
- }
22
+ import { GlueRuntime, GLUE_HOST_FUNCTIONS, parseFuelBudget, runWithWatchdog, } from './glue-runtime.js';
23
+ import { createGlueSab, armGlueRequest, waitAndReadGlueReply, } from './glue-sab.js';
24
+ export { GlueRuntime, GLUE_HOST_FUNCTIONS, parseFuelBudget, runWithWatchdog, };
62
25
  /**
63
- * Wrap a single guest dispatch with the wall-clock watchdog. The fuel trap is
64
- * enforced inside the gas-injected module; this guards against a hang that
65
- * spins without consuming fuel (e.g. a tight host-call loop the broker rate
66
- * cap already bounds, belt-and-suspenders). Pure and unit-testable.
26
+ * Wire the glue runtime to a worker-like message port. Exported (not just
27
+ * run at import) so the Node integration test can drive the identical wiring
28
+ * over a `worker_threads` port.
67
29
  */
68
- export async function runWithWatchdog(dispatch, watchdogMs, now = () => Date.now()) {
69
- const start = now();
70
- try {
71
- dispatch();
72
- }
73
- catch (err) {
74
- const message = err.message ?? 'trap';
75
- if (/fuel|gas|unreachable/i.test(message)) {
76
- return { ok: false, reason: 'fuel', detail: message };
30
+ export function startGlueWorker(port) {
31
+ let runtime = null;
32
+ let sab = null;
33
+ let hostCallId = 0;
34
+ let watchdogMs = 250;
35
+ let ticking = false;
36
+ const post = (message) => port.postMessage(message);
37
+ // Synchronous gateway: post the request (so the page can see it), then
38
+ // block on the SAB until the broker writes the reply.
39
+ const hostCallSync = (reqBytes) => {
40
+ if (!sab)
41
+ throw new Error('host-call transport not initialized');
42
+ let parsed;
43
+ try {
44
+ parsed = JSON.parse(new TextDecoder().decode(reqBytes));
77
45
  }
78
- return { ok: false, reason: 'trap', detail: message };
79
- }
80
- if (now() - start > watchdogMs) {
81
- return { ok: false, reason: 'watchdog' };
82
- }
83
- return { ok: true };
84
- }
85
- /**
86
- * Build the guest import object. Every entry is a `ck.*` host stub that
87
- * forwards to the broker; the function set is fixed at build time, so a guest
88
- * cannot import anything outside GLUE_HOST_FUNCTIONS. `invokeHostCall` is the
89
- * synchronous bridge (SharedArrayBuffer + Atomics in the browser; injectable
90
- * for tests).
91
- */
92
- export function buildImportObject(invokeHostCall) {
93
- const ck = {};
94
- for (const fn of GLUE_HOST_FUNCTIONS) {
95
- ck[fn] = (argsPtr, argsLen, outPtr) => {
96
- // The real ABI marshals argsPtr/argsLen out of guest memory and writes
97
- // the reply back at outPtr; the memory plumbing is wired in the init
98
- // handler where the instance's memory export is available. This stub
99
- // documents the fixed surface and the synchronous forward.
100
- void argsPtr;
101
- void argsLen;
102
- void outPtr;
103
- const reply = invokeHostCall(fn, '');
104
- return reply.length;
46
+ catch {
47
+ throw new Error('host_call request was not valid JSON');
48
+ }
49
+ const id = ++hostCallId;
50
+ armGlueRequest(sab);
51
+ post({
52
+ type: 'hostcall',
53
+ id,
54
+ fn: parsed.fn,
55
+ args: parsed.args ?? {},
56
+ reply: sab.sab,
57
+ });
58
+ return waitAndReadGlueReply(sab);
59
+ };
60
+ const onInit = async (init) => {
61
+ watchdogMs = init.watchdogMs ?? 250;
62
+ void parseFuelBudget(init.fuelPerDispatch);
63
+ sab = createGlueSab();
64
+ runtime = new GlueRuntime({
65
+ hostCallSync,
66
+ onLog: (level, message) => post({ type: 'log', level, message }),
67
+ });
68
+ try {
69
+ await runtime.instantiate(init.artifact);
70
+ const initResult = await runWithWatchdog(() => runtime.init(), watchdogMs);
71
+ report(initResult);
72
+ if ((init.tickIntervalMs ?? 0) > 0)
73
+ startTicking(init.tickIntervalMs);
74
+ post({ type: 'ready' });
75
+ }
76
+ catch (err) {
77
+ post({ type: 'trap', reason: 'trap', detail: err.message });
78
+ }
79
+ };
80
+ const report = (result) => {
81
+ if (result.ok)
82
+ post({ type: 'dispatch-ok' });
83
+ else
84
+ post({ type: 'trap', reason: result.reason, detail: result.detail });
85
+ };
86
+ const startTicking = (intervalMs) => {
87
+ if (ticking)
88
+ return;
89
+ ticking = true;
90
+ let last = Date.now();
91
+ const loop = () => {
92
+ if (!ticking || !runtime)
93
+ return;
94
+ const nowT = Date.now();
95
+ const dt = nowT - last;
96
+ last = nowT;
97
+ void runWithWatchdog(() => runtime.tick(dt), watchdogMs).then(report);
98
+ setTimeout(loop, intervalMs);
105
99
  };
100
+ setTimeout(loop, intervalMs);
101
+ };
102
+ const handle = (data) => {
103
+ if (!data || typeof data !== 'object')
104
+ return;
105
+ const msg = data;
106
+ if (msg.type === 'init')
107
+ void onInit(msg);
108
+ else if (msg.type === 'tick' && runtime) {
109
+ void runWithWatchdog(() => runtime.tick(typeof msg.dtMs === 'number' ? msg.dtMs : 0), watchdogMs).then(report);
110
+ }
111
+ else if (msg.type === 'invoke' && runtime) {
112
+ const payload = msg.payload instanceof Uint8Array ? msg.payload : new Uint8Array(0);
113
+ try {
114
+ const out = runtime.invoke(payload);
115
+ post({ type: 'invoke-result', id: msg.id, ok: true, payload: out });
116
+ }
117
+ catch (err) {
118
+ post({
119
+ type: 'invoke-result',
120
+ id: msg.id,
121
+ ok: false,
122
+ error: err.message,
123
+ });
124
+ }
125
+ }
126
+ else if (msg.type === 'stop') {
127
+ ticking = false;
128
+ }
129
+ };
130
+ if (port.addEventListener) {
131
+ port.addEventListener('message', (e) => handle(e.data));
132
+ }
133
+ else if (port.on) {
134
+ port.on('message', (d) => handle(d));
106
135
  }
107
- return { ck };
108
136
  }
137
+ // Auto-start when loaded as a real browser Web Worker (has addEventListener,
138
+ // no window). Guarded so importing for the pure helpers never touches globals.
109
139
  if (typeof self !== 'undefined' &&
110
140
  typeof self.addEventListener === 'function' &&
111
141
  typeof self.window === 'undefined') {
112
- self.addEventListener('message', (event) => {
113
- const data = event.data;
114
- if (!data || typeof data !== 'object')
115
- return;
116
- if (data.type === 'init') {
117
- const init = data;
118
- // The synchronous host-call bridge (SharedArrayBuffer + Atomics) is set
119
- // up here from init.fuelPerDispatch/watchdogMs; instantiation uses
120
- // buildImportObject so the guest sees only ck.* host functions.
121
- void parseFuelBudget(init.fuelPerDispatch);
122
- void (init.watchdogMs ?? 250);
123
- self.postMessage?.({ type: 'ready' });
124
- return;
125
- }
142
+ startGlueWorker({
143
+ addEventListener: self.addEventListener.bind(self),
144
+ postMessage: (m) => self.postMessage?.(m),
126
145
  });
127
146
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crowdedkingdoms/crowdyjs",
3
- "version": "8.17.0",
3
+ "version": "8.19.0",
4
4
  "description": "Client SDK for Crowded Kingdoms GraphQL API with UDP proxy support",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -70,11 +70,18 @@
70
70
  "@graphql-tools/merge": "^9.1.10",
71
71
  "@types/node": "^22.10.7",
72
72
  "typescript": "^5.7.3",
73
- "ws": "^8.20.0"
73
+ "ws": "^8.21.1"
74
74
  },
75
75
  "dependencies": {
76
+ "@codingame/monaco-vscode-editor-api": "25.1.2",
76
77
  "@graphql-typed-document-node/core": "^3.2.0",
78
+ "dompurify": "3.4.12",
77
79
  "graphql": "^16.13.2",
78
- "graphql-ws": "^6.0.8"
80
+ "graphql-ws": "^6.0.8",
81
+ "monaco-languageclient": "10.7.0",
82
+ "vscode-ws-jsonrpc": "3.5.0"
83
+ },
84
+ "overrides": {
85
+ "dompurify": "$dompurify"
79
86
  }
80
87
  }