@forgeax/engine-remote 0.1.4 → 0.1.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.
Files changed (42) hide show
  1. package/README.md +3 -3
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/__tests__/cli-defaults.unit.test.d.ts +2 -0
  4. package/dist/__tests__/cli-defaults.unit.test.d.ts.map +1 -0
  5. package/dist/__tests__/simulation-inspect.integration.test.d.ts +2 -0
  6. package/dist/__tests__/simulation-inspect.integration.test.d.ts.map +1 -0
  7. package/dist/cli.d.ts +15 -0
  8. package/dist/cli.d.ts.map +1 -0
  9. package/dist/cli.mjs +336 -0
  10. package/dist/cli.mjs.map +1 -0
  11. package/dist/defineSubcommand.d.ts +40 -0
  12. package/dist/defineSubcommand.d.ts.map +1 -0
  13. package/dist/execute.d.ts +1 -0
  14. package/dist/execute.d.ts.map +1 -1
  15. package/dist/execute.mjs +2 -0
  16. package/dist/execute.mjs.map +1 -1
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/introspect.d.ts +2 -0
  19. package/dist/introspect.d.ts.map +1 -1
  20. package/dist/introspect.mjs +5 -8
  21. package/dist/introspect.mjs.map +1 -1
  22. package/dist/server.d.ts +2 -2
  23. package/dist/server.d.ts.map +1 -1
  24. package/dist/server.mjs +20 -58
  25. package/dist/server.mjs.map +1 -1
  26. package/package.json +6 -3
  27. package/src/__tests__/cli-defaults.unit.test.ts +69 -0
  28. package/src/__tests__/console.unit.test.ts +676 -9
  29. package/src/__tests__/errors.unit.test.ts +3 -17
  30. package/src/__tests__/execute.async.test.ts +17 -13
  31. package/src/__tests__/server.unit.test.ts +19 -262
  32. package/src/__tests__/simulation-inspect.integration.test.ts +67 -0
  33. package/src/__tests__/vm-async-eval-verify.mjs +1 -1
  34. package/src/cli.ts +333 -0
  35. package/src/defineSubcommand.ts +169 -0
  36. package/src/execute.ts +3 -0
  37. package/src/index.ts +5 -4
  38. package/src/introspect.ts +10 -15
  39. package/src/server.ts +14 -60
  40. package/dist/__tests__/asset-runtime-inspection.unit.test.d.ts +0 -2
  41. package/dist/__tests__/asset-runtime-inspection.unit.test.d.ts.map +0 -1
  42. package/src/__tests__/asset-runtime-inspection.unit.test.ts +0 -16
package/src/server.ts CHANGED
@@ -46,8 +46,8 @@ export type StartServerOptions = {
46
46
  readonly profiler?: unknown;
47
47
  /** Structural report provider; remote never imports the App owner. */
48
48
  readonly execution?: unknown;
49
- /** Host-owned dynamic module resolver; omitted for package-neutral eval. */
50
- readonly importModule?: (specifier: string) => Promise<unknown>;
49
+ /** Read-only World-owned simulation inspection root. */
50
+ readonly simulation?: unknown;
51
51
  /**
52
52
  * Host-owned RHI capture capability for eval-scope injection (plan-strategy D-4).
53
53
  * It is exposed as the single `rhiCapture` eval root.
@@ -75,13 +75,14 @@ type JsonRpcResponse = {
75
75
  } & ({ result: unknown } | { error: JsonRpcError });
76
76
 
77
77
  function inspectorErrorToJsonRpc(e: RemoteError): JsonRpcError {
78
+ const detail = (e as unknown as { detail?: unknown }).detail;
78
79
  const data: Record<string, unknown> = {
79
80
  code: e.code,
80
81
  expected: e.expected,
81
82
  hint: e.hint,
82
83
  message: e.message,
83
84
  };
84
- if (e.detail !== undefined) data.detail = e.detail;
85
+ if (detail !== undefined) data.detail = detail;
85
86
  return {
86
87
  code: REMOTE_TO_JSONRPC[e.code],
87
88
  message: REMOTE_ERROR_MESSAGES[e.code],
@@ -106,47 +107,6 @@ function respondOk(id: number | string | null, result: unknown): JsonRpcResponse
106
107
  return { jsonrpc: '2.0', id, result };
107
108
  }
108
109
 
109
- type EvalResultShape = 'bigint' | 'cyclic-object' | 'unsupported';
110
-
111
- const EVAL_RESULT_NOT_SERIALIZABLE_EXPECTED = 'eval result is JSON-serializable';
112
- const EVAL_RESULT_NOT_SERIALIZABLE_HINT =
113
- 'return a JSON-safe value; BigInt and cyclic objects are unsupported over JSON-RPC';
114
-
115
- function classifyEvalResultShape(value: unknown): EvalResultShape {
116
- if (typeof value === 'bigint') return 'bigint';
117
- if (value !== null && typeof value === 'object') return 'cyclic-object';
118
- return 'unsupported';
119
- }
120
-
121
- function serializeResponse(response: JsonRpcResponse): string {
122
- try {
123
- const serialized = JSON.stringify(response);
124
- if (serialized !== undefined) return serialized;
125
- } catch {
126
- // A successful eval result can be a valid JavaScript value that JSON-RPC
127
- // cannot carry. Convert that transport-only failure below.
128
- }
129
-
130
- if (!('result' in response)) {
131
- return JSON.stringify(respondError(response.id, -32603, 'Internal error')) as string;
132
- }
133
-
134
- const error = new RemoteError({
135
- code: 'eval-result-not-serializable',
136
- expected: EVAL_RESULT_NOT_SERIALIZABLE_EXPECTED,
137
- hint: EVAL_RESULT_NOT_SERIALIZABLE_HINT,
138
- detail: {
139
- code: 'eval-result-not-serializable',
140
- shape: classifyEvalResultShape(response.result),
141
- },
142
- });
143
- return JSON.stringify({
144
- jsonrpc: '2.0',
145
- id: response.id,
146
- error: inspectorErrorToJsonRpc(error),
147
- }) as string;
148
- }
149
-
150
110
  function isValidId(v: unknown): v is number | string | null {
151
111
  return typeof v === 'number' || typeof v === 'string' || v === null;
152
112
  }
@@ -161,20 +121,17 @@ async function handleEnvelope(
161
121
  introspection: readonly ComponentIntrospectionDescriptor[];
162
122
  profiler: unknown | undefined;
163
123
  execution: unknown | undefined;
164
- importModule: ((specifier: string) => Promise<unknown>) | undefined;
124
+ simulation: unknown | undefined;
165
125
  host: string;
166
126
  port: number;
167
127
  },
168
128
  ): Promise<JsonRpcResponse | null> {
169
- let parsed: JsonRpcRequest | null;
129
+ let parsed: JsonRpcRequest;
170
130
  try {
171
131
  parsed = JSON.parse(raw) as JsonRpcRequest;
172
132
  } catch {
173
133
  return respondError(null, -32700, 'Parse error');
174
134
  }
175
- if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
176
- return respondError(null, -32600, 'Invalid Request');
177
- }
178
135
  if (typeof parsed.method !== 'string') {
179
136
  const id = isValidId(parsed.id) ? parsed.id : null;
180
137
  return respondError(id, -32600, 'Invalid Request');
@@ -193,6 +150,7 @@ async function handleEnvelope(
193
150
  ...(ctx.rhiCapture !== undefined ? { rhiCapture: ctx.rhiCapture } : {}),
194
151
  ...(ctx.profiler !== undefined ? { profiler: ctx.profiler } : {}),
195
152
  ...(ctx.execution !== undefined ? { execution: ctx.execution } : {}),
153
+ ...(ctx.simulation !== undefined ? { simulation: ctx.simulation } : {}),
196
154
  ...(ctx.introspection.length > 0 ? { introspection: ctx.introspection } : {}),
197
155
  }),
198
156
  );
@@ -209,7 +167,7 @@ async function handleEnvelope(
209
167
  rhiCapture: ctx.rhiCapture,
210
168
  profiler: ctx.profiler,
211
169
  execution: ctx.execution,
212
- ...(ctx.importModule === undefined ? {} : { importModule: ctx.importModule }),
170
+ simulation: ctx.simulation,
213
171
  });
214
172
  if (result.ok) {
215
173
  response = respondOk(id, result.value);
@@ -250,7 +208,7 @@ export function startServer(opts: StartServerOptions): Promise<Result<ConsoleHan
250
208
  const introspection = opts.introspection ?? [];
251
209
  const profiler = isProfilerRoot(opts.profiler) ? opts.profiler : undefined;
252
210
  const execution = isExecutionRoot(opts.execution) ? opts.execution : undefined;
253
- const importModule = opts.importModule;
211
+ const simulation = opts.simulation;
254
212
  let settled = false;
255
213
  let boundPort = opts.port;
256
214
 
@@ -286,13 +244,13 @@ export function startServer(opts: StartServerOptions): Promise<Result<ConsoleHan
286
244
  introspection,
287
245
  profiler,
288
246
  execution,
289
- importModule,
247
+ simulation,
290
248
  host,
291
249
  port: boundPort,
292
250
  })
293
251
  .then((response) => {
294
252
  if (response !== null && ws.readyState === ws.OPEN) {
295
- ws.send(serializeResponse(response));
253
+ ws.send(JSON.stringify(response));
296
254
  }
297
255
  })
298
256
  .catch((e: unknown) => {
@@ -310,20 +268,16 @@ export function startServer(opts: StartServerOptions): Promise<Result<ConsoleHan
310
268
  settled = true;
311
269
  const address = wss.address();
312
270
  const port = typeof address === 'object' && address !== null ? address.port : opts.port;
313
- let closePromise: Promise<void> | undefined;
314
271
  boundPort = port;
315
272
  const handle: ConsoleHandle = {
316
273
  port,
317
- close: () => {
318
- if (closePromise !== undefined) return closePromise;
319
- closePromise = new Promise<void>((closeResolve) => {
274
+ close: () =>
275
+ new Promise<void>((closeResolve) => {
320
276
  for (const client of wss.clients) {
321
277
  client.terminate();
322
278
  }
323
279
  wss.close(() => closeResolve());
324
- });
325
- return closePromise;
326
- },
280
+ }),
327
281
  };
328
282
  resolve(ok(handle));
329
283
  });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=asset-runtime-inspection.unit.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"asset-runtime-inspection.unit.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/asset-runtime-inspection.unit.test.ts"],"names":[],"mappings":""}
@@ -1,16 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { buildIntrospectDoc } from '../introspect';
3
-
4
- describe('remote asset runtime inspection', () => {
5
- it('projects the bounded registry snapshot operations without owning registry state', () => {
6
- const doc = buildIntrospectDoc('127.0.0.1', 5732, {
7
- world: {},
8
- renderer: {},
9
- assets: { snapshot: () => ({ ready: [], pending: 0 }) },
10
- }) as { roots: Record<string, { operations?: Record<string, string> }> };
11
-
12
- expect(doc.roots.assets).toMatchObject({
13
- operations: { load: 'assets.load(guid, expectedKind)', snapshot: 'assets.snapshot()' },
14
- });
15
- });
16
- });