@volter/editor-core 0.5.60 → 0.5.61
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/BUNDLED_NOTICES +3 -3
- package/dist-server/packaged.mjs +18 -6
- package/package.json +4 -4
- package/server/routes/control-plane.ts +23 -6
- package/src/command-listener.ts +2 -2
- package/src/command-registry.ts +11 -0
- package/src/editor-host-door.ts +2 -0
- package/src/project-adapter.ts +13 -0
package/BUNDLED_NOTICES
CHANGED
|
@@ -31,7 +31,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
|
31
31
|
SOFTWARE.
|
|
32
32
|
|
|
33
33
|
|
|
34
|
-
@volter/editor-project@0.5.
|
|
34
|
+
@volter/editor-project@0.5.61
|
|
35
35
|
Declared license: Apache-2.0
|
|
36
36
|
|
|
37
37
|
--- packages/editor-project/LICENSE ---
|
|
@@ -240,7 +240,7 @@ Declared license: Apache-2.0
|
|
|
240
240
|
limitations under the License.
|
|
241
241
|
|
|
242
242
|
|
|
243
|
-
@volter/editor-sdk@0.5.
|
|
243
|
+
@volter/editor-sdk@0.5.61
|
|
244
244
|
Declared license: Apache-2.0
|
|
245
245
|
|
|
246
246
|
--- packages/editor-sdk/LICENSE ---
|
|
@@ -461,7 +461,7 @@ The project output-root policy is Apache-2.0, transferred from the original
|
|
|
461
461
|
SDK; its exact source is recorded in provenance/editor-server.json.
|
|
462
462
|
|
|
463
463
|
|
|
464
|
-
@volter/editor-threejs@0.5.
|
|
464
|
+
@volter/editor-threejs@0.5.61
|
|
465
465
|
Declared license: AGPL-3.0-only AND Apache-2.0
|
|
466
466
|
|
|
467
467
|
--- packages/editor-threejs/LICENSE ---
|
package/dist-server/packaged.mjs
CHANGED
|
@@ -77339,11 +77339,12 @@ function createControlPlane(router, ctx) {
|
|
|
77339
77339
|
if (typeof type === "string") {
|
|
77340
77340
|
const contributed = contributedCommandTimeouts.get(type);
|
|
77341
77341
|
if (contributed !== void 0) return contributed;
|
|
77342
|
+
if (!isRelayCommandType(type)) return RELAY_DELIVERY_MAX_WAIT_MS;
|
|
77342
77343
|
}
|
|
77343
77344
|
return relayCommandTimeoutMs(type);
|
|
77344
77345
|
}
|
|
77345
77346
|
function commandAckDeadlineMs(type) {
|
|
77346
|
-
if (typeof type === "string" &&
|
|
77347
|
+
if (typeof type === "string" && !isRelayCommandType(type))
|
|
77347
77348
|
return commandTimeoutMs(type) > RELAY_DELIVERY_ACK_MS ? RELAY_DELIVERY_ACK_MS : null;
|
|
77348
77349
|
return relayCommandAckDeadlineMs(type);
|
|
77349
77350
|
}
|
|
@@ -77365,6 +77366,10 @@ function createControlPlane(router, ctx) {
|
|
|
77365
77366
|
}
|
|
77366
77367
|
contributedCommandTimeouts.clear();
|
|
77367
77368
|
for (const [type, timeoutMs] of next) contributedCommandTimeouts.set(type, timeoutMs);
|
|
77369
|
+
for (const pending of pendingCommands.values()) {
|
|
77370
|
+
const type = pending.command?.["type"];
|
|
77371
|
+
if (typeof type === "string" && next.has(type)) pending.refreshWorkTimer?.();
|
|
77372
|
+
}
|
|
77368
77373
|
return CONTROL_OK;
|
|
77369
77374
|
}
|
|
77370
77375
|
function armDeliveryReceiptWindow(requestId, type) {
|
|
@@ -78089,7 +78094,8 @@ data: ${data}
|
|
|
78089
78094
|
}
|
|
78090
78095
|
const requestId = randomUUID11();
|
|
78091
78096
|
const commandWithId = { ...body, _requestId: requestId };
|
|
78092
|
-
|
|
78097
|
+
let timeoutMs = commandTimeoutMs(body["type"]);
|
|
78098
|
+
let workStartedAt = Date.now();
|
|
78093
78099
|
const hasSeparateDeliveryBudget = commandAckDeadlineMs(body["type"]) !== null;
|
|
78094
78100
|
const resultPromise = new Promise((resolve37) => {
|
|
78095
78101
|
const expire = () => {
|
|
@@ -78128,18 +78134,24 @@ data: ${data}
|
|
|
78128
78134
|
});
|
|
78129
78135
|
};
|
|
78130
78136
|
const timer = setTimeout(expire, timeoutMs);
|
|
78137
|
+
const refreshWorkTimer = () => {
|
|
78138
|
+
const pending = pendingCommands.get(requestId);
|
|
78139
|
+
if (pending === void 0) return;
|
|
78140
|
+
timeoutMs = commandTimeoutMs(body["type"]);
|
|
78141
|
+
clearTimeout(pending.timer);
|
|
78142
|
+
pending.timer = setTimeout(expire, Math.max(0, workStartedAt + timeoutMs - Date.now()));
|
|
78143
|
+
};
|
|
78131
78144
|
pendingCommands.set(requestId, {
|
|
78132
78145
|
resolve: resolve37,
|
|
78133
78146
|
timer,
|
|
78134
78147
|
tabId: targetTabId,
|
|
78135
78148
|
command: commandWithId,
|
|
78149
|
+
refreshWorkTimer,
|
|
78136
78150
|
epochAtRelay: table?.tab(targetTabId)?.epochCount ?? 0,
|
|
78137
78151
|
...hasSeparateDeliveryBudget ? {
|
|
78138
78152
|
restartWorkTimerAfterReceipt: () => {
|
|
78139
|
-
|
|
78140
|
-
|
|
78141
|
-
clearTimeout(pending.timer);
|
|
78142
|
-
pending.timer = setTimeout(expire, timeoutMs);
|
|
78153
|
+
workStartedAt = Date.now();
|
|
78154
|
+
refreshWorkTimer();
|
|
78143
78155
|
}
|
|
78144
78156
|
} : {}
|
|
78145
78157
|
});
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volter/editor-core",
|
|
3
3
|
"author": "Volter AI, Inc.",
|
|
4
|
-
"version": "0.5.
|
|
4
|
+
"version": "0.5.61",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -82,9 +82,9 @@
|
|
|
82
82
|
"three.quarks": "0.16.0",
|
|
83
83
|
"typescript": "5.9.3",
|
|
84
84
|
"zod": "4.3.6",
|
|
85
|
-
"@volter/editor-project": "0.5.
|
|
86
|
-
"@volter/editor-sdk": "0.5.
|
|
87
|
-
"@volter/editor-threejs": "0.5.
|
|
85
|
+
"@volter/editor-project": "0.5.61",
|
|
86
|
+
"@volter/editor-sdk": "0.5.61",
|
|
87
|
+
"@volter/editor-threejs": "0.5.61",
|
|
88
88
|
"vite": "^6.4.3",
|
|
89
89
|
"express": "^5.2.1",
|
|
90
90
|
"chokidar": "^5.0.0",
|
|
@@ -31,6 +31,7 @@ import {
|
|
|
31
31
|
parseEditorControlLifecycle,
|
|
32
32
|
} from '@volter/editor-sdk/session/editor-control-lifecycle';
|
|
33
33
|
import { COMMAND_RESULT_RECEIPT_EVENT } from '@volter/editor-sdk/session/editor-control-protocol';
|
|
34
|
+
import { isRelayCommandType } from '@volter/editor-sdk/session/command-table';
|
|
34
35
|
import type { Request, Response } from 'express';
|
|
35
36
|
import {
|
|
36
37
|
ABNORMAL_SOCKET_CLOSE,
|
|
@@ -358,6 +359,8 @@ export function createControlPlane(router: EditorServerRouter, ctx: RouteContext
|
|
|
358
359
|
* restarts it so time spent queued behind a blocked main thread cannot
|
|
359
360
|
* consume the work budget before the page begins the work. */
|
|
360
361
|
restartWorkTimerAfterReceipt?: () => void;
|
|
362
|
+
/** Adopt a late contribution's declared budget without resetting elapsed work. */
|
|
363
|
+
refreshWorkTimer?: () => void;
|
|
361
364
|
controller?: EditorEventClient;
|
|
362
365
|
controllerClientId?: string;
|
|
363
366
|
}
|
|
@@ -460,11 +463,14 @@ export function createControlPlane(router: EditorServerRouter, ctx: RouteContext
|
|
|
460
463
|
if (typeof type === 'string') {
|
|
461
464
|
const contributed = contributedCommandTimeouts.get(type);
|
|
462
465
|
if (contributed !== undefined) return contributed;
|
|
466
|
+
// The listener can receive a package command before discovery reports
|
|
467
|
+
// its row. Allow bounded discovery, then adopt its real work budget.
|
|
468
|
+
if (!isRelayCommandType(type)) return RELAY_DELIVERY_MAX_WAIT_MS;
|
|
463
469
|
}
|
|
464
470
|
return relayCommandTimeoutMs(type);
|
|
465
471
|
}
|
|
466
472
|
function commandAckDeadlineMs(type: unknown): number | null {
|
|
467
|
-
if (typeof type === 'string' &&
|
|
473
|
+
if (typeof type === 'string' && !isRelayCommandType(type))
|
|
468
474
|
return commandTimeoutMs(type) > RELAY_DELIVERY_ACK_MS ? RELAY_DELIVERY_ACK_MS : null;
|
|
469
475
|
return relayCommandAckDeadlineMs(type);
|
|
470
476
|
}
|
|
@@ -486,6 +492,10 @@ export function createControlPlane(router: EditorServerRouter, ctx: RouteContext
|
|
|
486
492
|
}
|
|
487
493
|
contributedCommandTimeouts.clear();
|
|
488
494
|
for (const [type, timeoutMs] of next) contributedCommandTimeouts.set(type, timeoutMs);
|
|
495
|
+
for (const pending of pendingCommands.values()) {
|
|
496
|
+
const type = pending.command?.['type'];
|
|
497
|
+
if (typeof type === 'string' && next.has(type)) pending.refreshWorkTimer?.();
|
|
498
|
+
}
|
|
489
499
|
return CONTROL_OK;
|
|
490
500
|
}
|
|
491
501
|
|
|
@@ -1703,7 +1713,8 @@ export function createControlPlane(router: EditorServerRouter, ctx: RouteContext
|
|
|
1703
1713
|
const requestId = randomUUID();
|
|
1704
1714
|
const commandWithId = { ...body, _requestId: requestId };
|
|
1705
1715
|
|
|
1706
|
-
|
|
1716
|
+
let timeoutMs = commandTimeoutMs(body['type']);
|
|
1717
|
+
let workStartedAt = Date.now();
|
|
1707
1718
|
const hasSeparateDeliveryBudget = commandAckDeadlineMs(body['type']) !== null;
|
|
1708
1719
|
const resultPromise = new Promise<SettledCommandResult>((resolve) => {
|
|
1709
1720
|
const expire = (): void => {
|
|
@@ -1752,19 +1763,25 @@ export function createControlPlane(router: EditorServerRouter, ctx: RouteContext
|
|
|
1752
1763
|
});
|
|
1753
1764
|
};
|
|
1754
1765
|
const timer = setTimeout(expire, timeoutMs);
|
|
1766
|
+
const refreshWorkTimer = (): void => {
|
|
1767
|
+
const pending = pendingCommands.get(requestId);
|
|
1768
|
+
if (pending === undefined) return;
|
|
1769
|
+
timeoutMs = commandTimeoutMs(body['type']);
|
|
1770
|
+
clearTimeout(pending.timer);
|
|
1771
|
+
pending.timer = setTimeout(expire, Math.max(0, workStartedAt + timeoutMs - Date.now()));
|
|
1772
|
+
};
|
|
1755
1773
|
pendingCommands.set(requestId, {
|
|
1756
1774
|
resolve,
|
|
1757
1775
|
timer,
|
|
1758
1776
|
tabId: targetTabId,
|
|
1759
1777
|
command: commandWithId,
|
|
1778
|
+
refreshWorkTimer,
|
|
1760
1779
|
epochAtRelay: table?.tab(targetTabId)?.epochCount ?? 0,
|
|
1761
1780
|
...(hasSeparateDeliveryBudget
|
|
1762
1781
|
? {
|
|
1763
1782
|
restartWorkTimerAfterReceipt: () => {
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
clearTimeout(pending.timer);
|
|
1767
|
-
pending.timer = setTimeout(expire, timeoutMs);
|
|
1783
|
+
workStartedAt = Date.now();
|
|
1784
|
+
refreshWorkTimer();
|
|
1768
1785
|
},
|
|
1769
1786
|
}
|
|
1770
1787
|
: {}),
|
package/src/command-listener.ts
CHANGED
|
@@ -51,7 +51,7 @@ import {
|
|
|
51
51
|
} from './authoring/shell-document-ops';
|
|
52
52
|
import { parseCameraChoice, parsePoseChoice } from './capture-camera-pose';
|
|
53
53
|
import { noteCommandDispatched } from './command-dispatch';
|
|
54
|
-
import {
|
|
54
|
+
import { resolveContributedCommand, contributedCommandDerivedRefresh } from './command-registry';
|
|
55
55
|
import {
|
|
56
56
|
isAssetDocumentId,
|
|
57
57
|
openAssetDocument,
|
|
@@ -1237,7 +1237,7 @@ export async function handleCommand(
|
|
|
1237
1237
|
// answered by its own handler, through the same relay, ack and derivation
|
|
1238
1238
|
// as the host's table below. Asked first so a contributed verb never
|
|
1239
1239
|
// reads as "editor page predates this CLI".
|
|
1240
|
-
const contributed =
|
|
1240
|
+
const contributed = await resolveContributedCommand(cmd['type']);
|
|
1241
1241
|
if (contributed) {
|
|
1242
1242
|
try {
|
|
1243
1243
|
const answer = await contributed.handle(cmd);
|
package/src/command-registry.ts
CHANGED
|
@@ -55,6 +55,17 @@ export function contributedCommand(type: unknown): CommandSpec | null {
|
|
|
55
55
|
return typeof type === 'string' ? (registered.get(type)?.spec ?? null) : null;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
/** A listener can attach before the presentation-deferred contribution pass.
|
|
59
|
+
* A requested package command needs that pass now; absence is only meaningful
|
|
60
|
+
* after the current project's contributions have finished installing. */
|
|
61
|
+
export async function resolveContributedCommand(type: unknown): Promise<CommandSpec | null> {
|
|
62
|
+
const found = contributedCommand(type);
|
|
63
|
+
if (found || typeof type !== 'string' || isRelayCommandType(type)) return found;
|
|
64
|
+
await (await import('./initial-project')).projectBootstrapSettled();
|
|
65
|
+
await (await import('./tool-loader')).refreshProjectToolContributions();
|
|
66
|
+
return contributedCommand(type);
|
|
67
|
+
}
|
|
68
|
+
|
|
58
69
|
export function contributedCommandDerivedRefresh(type: unknown): CommandDerivedRefresh | null {
|
|
59
70
|
const spec = contributedCommand(type);
|
|
60
71
|
return spec ? (spec.derivedRefresh ?? 'always') : null;
|
package/src/editor-host-door.ts
CHANGED
|
@@ -32,6 +32,7 @@ import {
|
|
|
32
32
|
} from './document-context-registry';
|
|
33
33
|
import { openRegisteredDocumentAsync } from './document-open-registry';
|
|
34
34
|
import { editorConsole } from './editor-console';
|
|
35
|
+
import { resolvedProjectDocumentTable } from './project-adapter';
|
|
35
36
|
import { notify } from './editor-notifications';
|
|
36
37
|
import { registerEditorStateFacet } from './editor-state-facets';
|
|
37
38
|
import { setFilesProvider } from './files/file-provider';
|
|
@@ -259,6 +260,7 @@ export function installEditorHostDoor(): void {
|
|
|
259
260
|
reportWorkerCallMeter: setBlenderCallMeter,
|
|
260
261
|
},
|
|
261
262
|
project: {
|
|
263
|
+
documentTable: resolvedProjectDocumentTable,
|
|
262
264
|
mounts: projectMounts,
|
|
263
265
|
// The ONE reading of the pinned engine version — `ProjectHeader.tsx`'s
|
|
264
266
|
// `v{engineVersion}` and a package's status item are the same number
|
package/src/project-adapter.ts
CHANGED
|
@@ -255,6 +255,19 @@ export function waitForProjectAdapter(): Promise<void> {
|
|
|
255
255
|
});
|
|
256
256
|
}
|
|
257
257
|
|
|
258
|
+
/** A package opening a document during boot needs the final finder-backed
|
|
259
|
+
* table, not the initial declaration with contributed finders still pending. */
|
|
260
|
+
export async function resolvedProjectDocumentTable(): Promise<ResolvedDocumentTable> {
|
|
261
|
+
await (await import('./initial-project')).projectBootstrapSettled();
|
|
262
|
+
await refreshProjectToolContributions();
|
|
263
|
+
await refreshProjectAdapter();
|
|
264
|
+
while (_inFlight) await _inFlight;
|
|
265
|
+
const facet = projectAdapterFacet();
|
|
266
|
+
if (!facet || facet.documentsPending)
|
|
267
|
+
throw new Error('The project document table could not finish loading; inspect the project diagnostics.');
|
|
268
|
+
return facet.scenes;
|
|
269
|
+
}
|
|
270
|
+
|
|
258
271
|
export function subscribeProjectAdapter(fn: () => void): () => void {
|
|
259
272
|
_listeners.add(fn);
|
|
260
273
|
return () => _listeners.delete(fn);
|