@pixel-point/toolcraft 0.0.16 → 0.0.17
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/package.json +1 -1
- package/src/generate-performance-iteration-execution.test.mjs +2 -2
- package/src/generate-test-demand-only-lifecycle.mjs +14 -3
- package/src/generate-test-dependency-sandbox.mjs +42 -5
- package/src/generate-test-doc-assertions.mjs +68 -18
- package/src/generate-test-doc-assertions.test.mjs +22 -0
- package/src/generate-test-file-assertions.mjs +43 -76
- package/src/generate-test-output-assertions.mjs +95 -0
- package/src/generate-test-package-assertions.mjs +6 -0
- package/src/generate-test-performance-boundary-assertions.mjs +45 -0
- package/src/generate-test-product-fixture.mjs +1 -0
- package/src/generate-test-product-fixture.test.mjs +18 -0
- package/src/generate-test-product-iteration.mjs +1 -0
- package/src/generate-test-product-iteration.test.mjs +1 -0
- package/src/generate-test-standalone-delivery-stages.mjs +14 -6
- package/src/generate.test.mjs +14 -0
- package/templates/runtime/contracts/component-contracts.choices.test.ts +12 -9
- package/templates/runtime/contracts/component-contracts.choices.ts +10 -9
- package/templates/runtime/contracts/component-contracts.runtime-acceptance.test.ts +17 -2
- package/templates/runtime/contracts/component-contracts.runtime-panels.test.ts +1 -1
- package/templates/runtime/contracts/component-contracts.runtime-shell.test.ts +1 -1
- package/templates/runtime/contracts/component-contracts.runtime.ts +3 -2
- package/templates/runtime/contracts/decision-contracts.test.ts +25 -4
- package/templates/runtime/contracts/decision-contracts.ts +3 -3
- package/templates/runtime/contracts/performance-verification-policy.test.ts +59 -6
- package/templates/runtime/contracts/performance-verification-policy.ts +5 -2
- package/templates/runtime/export/artifact-download.test.ts +71 -0
- package/templates/runtime/export/artifact-download.ts +80 -0
- package/templates/runtime/export/artifact-export-request.ts +23 -0
- package/templates/runtime/export/artifact-export-settings.test.ts +75 -0
- package/templates/runtime/export/artifact-export-settings.ts +96 -0
- package/templates/runtime/export/artifact-frame-renderer.test.ts +107 -0
- package/templates/runtime/export/artifact-frame-renderer.ts +96 -0
- package/templates/runtime/export/artifact-frame-state.test.ts +54 -0
- package/templates/runtime/export/artifact-frame-state.ts +44 -0
- package/templates/runtime/export/artifact-scene-frame.test.ts +84 -0
- package/templates/runtime/export/artifact-scene-frame.ts +118 -0
- package/templates/runtime/export/export-background.ts +65 -0
- package/templates/runtime/export/export-error.ts +43 -0
- package/templates/runtime/export/export.test.ts +6 -186
- package/templates/runtime/export/image-artifact-export.test.ts +111 -0
- package/templates/runtime/export/image-artifact-export.ts +145 -0
- package/templates/runtime/export/index.ts +14 -1
- package/templates/runtime/export/product-export-renderer.test.ts +77 -0
- package/templates/runtime/export/product-export-renderer.ts +63 -0
- package/templates/runtime/export/video-artifact-export.test.ts +166 -0
- package/templates/runtime/export/video-artifact-export.ts +164 -0
- package/templates/runtime/export/video-encoding-backend.ts +124 -0
- package/templates/runtime/export/video-encoding-policy.test.ts +68 -0
- package/templates/runtime/export/video-encoding-policy.ts +86 -0
- package/templates/runtime/export/video-frame-schedule.test.ts +58 -0
- package/templates/runtime/export/video-frame-schedule.ts +42 -0
- package/templates/runtime/index.ts +1 -0
- package/templates/runtime/react/app-shell/runtime-public-api-runtime.test.tsx +291 -0
- package/templates/runtime/react/app-shell/runtime-public-api-test-support.ts +21 -0
- package/templates/runtime/react/app-shell/runtime-public-api.test.tsx +4 -303
- package/templates/runtime/react/app-shell/toolcraft-app.scene-export.test.tsx +12 -27
- package/templates/runtime/react/app-shell/toolcraft-app.tsx +22 -4
- package/templates/runtime/react/app-shell/toolcraft-source-asset-context.test.tsx +16 -57
- package/templates/runtime/react/app-shell/toolcraft-source-asset-context.tsx +75 -434
- package/templates/runtime/react/app-shell/toolcraft-source-asset-owner.test.ts +194 -0
- package/templates/runtime/react/app-shell/toolcraft-source-asset-owner.ts +227 -0
- package/templates/runtime/react/app-shell/toolcraft-source-asset-provider-ownership.test.tsx +243 -0
- package/templates/runtime/react/app-shell/toolcraft-source-asset-test-support.ts +51 -0
- package/templates/runtime/react/app-shell/toolcraft-unavailable-resource-proof.test.ts +138 -0
- package/templates/runtime/react/app-shell/toolcraft-unavailable-resource-proof.ts +378 -0
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.footer-actions.test.tsx +7 -12
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.scene-export-actions.test.tsx +17 -83
- package/templates/runtime/react/controls-panel/actions/controls-panel-actions.ts +42 -84
- package/templates/runtime/react/controls-panel/actions/export-action-runner.ts +62 -0
- package/templates/runtime/react/panel-host/panel-host-runtime-placement.test.tsx +30 -0
- package/templates/runtime/react/panel-host/panel-host.tsx +4 -0
- package/templates/runtime/scene/scene-bounds.ts +1 -2
- package/templates/runtime/schema/canvas-render-scale.ts +62 -0
- package/templates/runtime/schema/controls-panel-section-id.test.ts +1 -1
- package/templates/runtime/schema/controls-panel-section-roundtrip.test.ts +3 -3
- package/templates/runtime/schema/define-toolcraft.setup-background.test.ts +1 -1
- package/templates/runtime/schema/define-toolcraft.setup-render-scale-timeline.test.ts +71 -3
- package/templates/runtime/schema/define-toolcraft.ts +1 -1
- package/templates/runtime/schema/schema-resolvers.ts +0 -56
- package/templates/runtime/schema/types.ts +4 -4
- package/templates/runtime/state/canvas-background-state.ts +6 -0
- package/templates/runtime/testing/performance-render-plan-integration.test.ts +64 -0
- package/templates/runtime/testing/performance-renderer-pipeline-validation.ts +18 -1
- package/templates/starter/AGENTS.md +8 -7
- package/templates/starter/NOTICE.md +6 -0
- package/templates/starter/docs/toolcraft/README.md +2 -1
- package/templates/starter/docs/toolcraft/acceptance-testing.md +8 -6
- package/templates/starter/docs/toolcraft/agent-worklog.md +3 -3
- package/templates/starter/docs/toolcraft/assembly-workflow.md +4 -3
- package/templates/starter/docs/toolcraft/core/media-upload.md +2 -2
- package/templates/starter/docs/toolcraft/core/performance.md +14 -3
- package/templates/starter/docs/toolcraft/core/runtime-boundary.md +3 -1
- package/templates/starter/docs/toolcraft/core/setup-export.md +16 -12
- package/templates/starter/docs/toolcraft/core/timeline-animation.md +4 -4
- package/templates/starter/docs/toolcraft/performance.md +20 -6
- package/templates/starter/docs/toolcraft/renderer-technique.md +1 -1
- package/templates/starter/docs/toolcraft/schema-reference.md +2 -6
- package/templates/starter/docs/toolcraft/workflow.md +7 -2
- package/templates/starter/e2e/app-browser-control-coverage.spec.ts +33 -0
- package/templates/starter/e2e/app-browser-infinity-canvas-evidence.spec.ts +43 -1
- package/templates/starter/e2e/app-browser-render-scale-evidence.spec.ts +60 -6
- package/templates/starter/e2e/app-browser-render-scale-requirements.spec.ts +77 -1
- package/templates/starter/e2e/app-browser-runtime-provenance.spec.ts +8 -0
- package/templates/starter/e2e/app-browser-runtime-reporter.spec.ts +35 -0
- package/templates/starter/e2e/app-browser-runtime-requirements.spec.ts +4 -1
- package/templates/starter/e2e/app-browser-semantic-evidence.spec.ts +0 -1
- package/templates/starter/e2e/app-browser-shell.spec.ts +13 -5
- package/templates/starter/e2e/app-performance-path-adapters.ts +8 -1
- package/templates/starter/e2e/app-performance.spec.ts +16 -9
- package/templates/starter/e2e/browser-infinity-canvas-evidence.ts +9 -330
- package/templates/starter/e2e/browser-infinity-canvas-unavailable-image-evidence.ts +370 -0
- package/templates/starter/e2e/browser-media-export-evidence.spec.ts +77 -0
- package/templates/starter/e2e/browser-media-export-evidence.ts +208 -0
- package/templates/starter/e2e/browser-render-scale-evidence.ts +37 -29
- package/templates/starter/e2e/browser-runtime-evidence-reporter.ts +4 -1
- package/templates/starter/e2e/browser-runtime-evidence-requirements.ts +43 -9
- package/templates/starter/e2e/browser-standard-timeline-evidence.ts +106 -0
- package/templates/starter/e2e/decoded-pixel-observation.spec.ts +74 -0
- package/templates/starter/e2e/decoded-pixel-observation.ts +227 -0
- package/templates/starter/e2e/export-artifact-helpers.ts +206 -25
- package/templates/starter/e2e/image-artifact-inspection.spec.ts +41 -0
- package/templates/starter/e2e/image-artifact-inspection.ts +99 -0
- package/templates/starter/e2e/performance-canvas-helpers.ts +22 -21
- package/templates/starter/e2e/performance-canvas-quality-guard.spec.ts +312 -0
- package/templates/starter/e2e/performance-canvas-quality-guard.ts +196 -0
- package/templates/starter/e2e/performance-canvas-quality-observer.ts +351 -0
- package/templates/starter/e2e/performance-control-layout-helpers.ts +2 -2
- package/templates/starter/e2e/performance-path-adapter-contract.ts +4 -4
- package/templates/starter/e2e/performance-path-helpers.spec.ts +203 -70
- package/templates/starter/e2e/performance-path-helpers.ts +210 -91
- package/templates/starter/e2e/performance-pipeline-evidence-continuity.spec.ts +76 -0
- package/templates/starter/e2e/performance-pipeline-evidence-test-fixtures.ts +11 -2
- package/templates/starter/e2e/performance-pipeline-invariants.ts +47 -4
- package/templates/starter/e2e/performance-render-scale-runner.spec.ts +140 -38
- package/templates/starter/e2e/video-artifact-inspection.spec.ts +64 -0
- package/templates/starter/e2e/video-artifact-inspection.ts +281 -0
- package/templates/starter/gitignore +1 -0
- package/templates/starter/package.json +1 -0
- package/templates/starter/scripts/check-toolcraft-docs.mjs +17 -0
- package/templates/starter/scripts/toolcraft-delivery-plan-authority.test.mjs +60 -0
- package/templates/starter/scripts/toolcraft-delivery-plan-errors.test.mjs +7 -2
- package/templates/starter/scripts/toolcraft-delivery-plan.mjs +7 -3
- package/templates/starter/scripts/toolcraft-product-boundary-ast.mjs +24 -2
- package/templates/starter/scripts/toolcraft-product-boundary.mjs +12 -0
- package/templates/starter/scripts/toolcraft-product-boundary.test.mjs +46 -0
- package/templates/starter/scripts/toolcraft-product-evidence-boundary.test.mjs +85 -20
- package/templates/starter/scripts/toolcraft-product-evidence-import-policy.mjs +43 -0
- package/templates/starter/scripts/toolcraft-product-export-boundary.mjs +168 -0
- package/templates/starter/scripts/toolcraft-typescript-analysis.d.mts +73 -0
- package/templates/starter/scripts/toolcraft-unavailable-resource-evidence-facade-test-helpers.mjs +318 -0
- package/templates/starter/scripts/toolcraft-unavailable-resource-evidence-facade.test.mjs +155 -206
- package/templates/starter/scripts/toolcraft-verification-inventory.mjs +1 -0
- package/templates/starter/scripts/toolcraft-verification-receipt.test.mjs +51 -0
- package/templates/starter/scripts/toolcraft-vite-env-activation.test.mjs +128 -59
- package/templates/starter/src/app/acceptance/export-artifact-coverage.ts +113 -0
- package/templates/starter/src/app/acceptance/output-background-rules.ts +1 -1
- package/templates/starter/src/app/acceptance/output-export.ts +13 -2
- package/templates/starter/src/app/acceptance/types.ts +7 -0
- package/templates/starter/src/app/acceptance/validate-coverage.ts +2 -1
- package/templates/starter/src/app/starter-acceptance.export-artifact.test.ts +131 -0
- package/templates/starter/src/app/starter-acceptance.keyframes.test.ts +14 -4
- package/templates/starter/src/app/starter-acceptance.output-export-rules.test.ts +42 -1
- package/templates/starter/src/app/starter-acceptance.ts +2 -0
- package/templates/starter/src/app/starter-performance-test-utils.ts +103 -0
- package/templates/starter/src/app/starter-performance.gates.test.ts +231 -278
- package/templates/starter/src/app/starter-performance.lifecycle.test.ts +261 -0
- package/templates/starter/src/app/test-evidence/browser-runtime-contract.ts +2 -0
- package/templates/starter/src/routes/index.tsx +1 -0
- package/templates/runtime/export/export.ts +0 -166
|
@@ -1,28 +1,14 @@
|
|
|
1
|
-
/// <reference types="vite/client" />
|
|
2
|
-
|
|
3
1
|
"use client";
|
|
4
2
|
|
|
5
3
|
import * as React from "react";
|
|
6
4
|
|
|
7
|
-
import {
|
|
8
|
-
createToolcraftSourceAssetCoordinator,
|
|
9
|
-
type ToolcraftSourceAssetCoordinator,
|
|
10
|
-
} from "../../source-assets/source-asset-coordinator";
|
|
11
|
-
import { createIndexedDbToolcraftBinaryAssetRepository } from "../../source-assets/repository/indexeddb-binary-asset-repository";
|
|
12
|
-
import { createMemoryToolcraftBinaryAssetRepository } from "../../source-assets/repository/memory-binary-asset-repository";
|
|
13
|
-
import { createUnavailableToolcraftBinaryAssetRepository } from "../../source-assets/repository/unavailable-binary-asset-repository";
|
|
14
|
-
import type { ToolcraftBinaryAssetRepository } from "../../source-assets/repository/binary-asset-repository";
|
|
15
|
-
import {
|
|
16
|
-
createToolcraftSourceAssetPresentationResources,
|
|
17
|
-
type ToolcraftSourceAssetPresentationResources,
|
|
18
|
-
} from "../../source-assets/source-asset-presentation-resources";
|
|
19
5
|
import {
|
|
20
6
|
createToolcraftDefaultBinaryMediaHydrationJobs,
|
|
21
7
|
getToolcraftBinaryMediaHydrationKey,
|
|
22
8
|
type ToolcraftBinaryMediaHydrationJob,
|
|
23
9
|
} from "../../source-assets/binary-media-hydration";
|
|
10
|
+
import type { ToolcraftSourceAssetCoordinator } from "../../source-assets/source-asset-coordinator";
|
|
24
11
|
import type { ToolcraftExternalStore } from "../../state/toolcraft-external-store";
|
|
25
|
-
import type { ToolcraftImageAsset } from "../../state/types";
|
|
26
12
|
import { ToolcraftModelRenderProvider } from "../model-rendering/model-render-provider";
|
|
27
13
|
import { useToolcraftModelPresentationMode } from "../model-rendering/model-presentation-mode";
|
|
28
14
|
import {
|
|
@@ -30,6 +16,8 @@ import {
|
|
|
30
16
|
resolveToolcraftOrientationControl,
|
|
31
17
|
} from "../orientation-gizmo/orientation-gizmo-selection";
|
|
32
18
|
import { ToolcraftMediaPresentationProvider } from "./toolcraft-media-presentation";
|
|
19
|
+
import { acquireToolcraftSourceAssetOwner } from "./toolcraft-source-asset-owner";
|
|
20
|
+
import { decorateToolcraftUnavailableResourceProofCoordinator } from "./toolcraft-unavailable-resource-proof";
|
|
33
21
|
|
|
34
22
|
export { useToolcraftMediaPresentationUrls } from "./toolcraft-media-presentation";
|
|
35
23
|
|
|
@@ -40,33 +28,13 @@ type ToolcraftSourceAssetCoordinatorFactory = (
|
|
|
40
28
|
store: ToolcraftExternalStore,
|
|
41
29
|
) => ToolcraftSourceAssetCoordinator;
|
|
42
30
|
|
|
43
|
-
type
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
requestId: string;
|
|
47
|
-
resourceRef: string;
|
|
48
|
-
} | null;
|
|
49
|
-
coordinator: ToolcraftSourceAssetCoordinator;
|
|
50
|
-
lifecycleGeneration: number;
|
|
51
|
-
onDisposeError: (error: unknown) => void;
|
|
52
|
-
presentationResources: ToolcraftSourceAssetPresentationResources | null;
|
|
53
|
-
unavailableResourceProofRefs: Set<string>;
|
|
54
|
-
};
|
|
31
|
+
type ToolcraftSourceAssetOwnerLease = ReturnType<
|
|
32
|
+
typeof acquireToolcraftSourceAssetOwner
|
|
33
|
+
>;
|
|
55
34
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
"data-toolcraft-unavailable-resource-evidence";
|
|
60
|
-
const toolcraftUnavailableResourceProofEnabled =
|
|
61
|
-
import.meta.env?.MODE === "test" ||
|
|
62
|
-
import.meta.env?.VITE_TOOLCRAFT_BROWSER_PROOF_FIXTURES ===
|
|
63
|
-
"unavailable-resource";
|
|
64
|
-
|
|
65
|
-
type ToolcraftUnavailableResourceProofRequest = Readonly<{
|
|
66
|
-
action: "activate" | "cleanup";
|
|
67
|
-
fileName: string;
|
|
68
|
-
requestId: string;
|
|
69
|
-
sourceTarget?: string;
|
|
35
|
+
type ToolcraftSourceAssetAcquisition = Readonly<{
|
|
36
|
+
lease: ToolcraftSourceAssetOwnerLease;
|
|
37
|
+
store: ToolcraftExternalStore;
|
|
70
38
|
}>;
|
|
71
39
|
|
|
72
40
|
function selectModelHydrationFingerprint(
|
|
@@ -94,166 +62,71 @@ function selectBinaryHydrationFingerprint(
|
|
|
94
62
|
.join("\u0001");
|
|
95
63
|
}
|
|
96
64
|
|
|
97
|
-
const sourceAssetOwners = new WeakMap<
|
|
98
|
-
ToolcraftExternalStore,
|
|
99
|
-
ToolcraftSourceAssetOwner
|
|
100
|
-
>();
|
|
101
|
-
|
|
102
|
-
function createRepository(): ToolcraftBinaryAssetRepository {
|
|
103
|
-
if (typeof globalThis.indexedDB === "undefined") {
|
|
104
|
-
return typeof navigator !== "undefined" &&
|
|
105
|
-
navigator.userAgent.toLowerCase().includes("jsdom")
|
|
106
|
-
? createMemoryToolcraftBinaryAssetRepository()
|
|
107
|
-
: createUnavailableToolcraftBinaryAssetRepository();
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
try {
|
|
111
|
-
return createIndexedDbToolcraftBinaryAssetRepository();
|
|
112
|
-
} catch {
|
|
113
|
-
return createUnavailableToolcraftBinaryAssetRepository();
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
function reportSourceAssetDisposeError(error: unknown): void {
|
|
118
|
-
const globalReportError = globalThis.reportError;
|
|
119
|
-
if (typeof globalReportError === "function") {
|
|
120
|
-
try {
|
|
121
|
-
globalReportError(error);
|
|
122
|
-
return;
|
|
123
|
-
} catch (reportingFailure) {
|
|
124
|
-
console.error(
|
|
125
|
-
"Toolcraft source asset disposal reporting failed.",
|
|
126
|
-
new AggregateError(
|
|
127
|
-
[error, reportingFailure],
|
|
128
|
-
"Toolcraft source asset disposal and reporting both failed.",
|
|
129
|
-
),
|
|
130
|
-
);
|
|
131
|
-
return;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
console.error("Toolcraft source asset disposal failed.", error);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
const createDefaultCoordinator: ToolcraftSourceAssetCoordinatorFactory = (
|
|
138
|
-
store,
|
|
139
|
-
) =>
|
|
140
|
-
createToolcraftSourceAssetCoordinator({
|
|
141
|
-
dispatch: store.dispatch,
|
|
142
|
-
getState: store.getCommittedState,
|
|
143
|
-
repository: createRepository(),
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
function getOrCreateSourceAssetOwner(
|
|
147
|
-
store: ToolcraftExternalStore,
|
|
148
|
-
createCoordinator: ToolcraftSourceAssetCoordinatorFactory,
|
|
149
|
-
onDisposeError: (error: unknown) => void,
|
|
150
|
-
): ToolcraftSourceAssetOwner {
|
|
151
|
-
const current = sourceAssetOwners.get(store);
|
|
152
|
-
if (current) return current;
|
|
153
|
-
|
|
154
|
-
const baseCoordinator = createCoordinator(store);
|
|
155
|
-
const unavailableResourceProofRefs = new Set<string>();
|
|
156
|
-
const proofScopedResolveResource = baseCoordinator.resolveResource
|
|
157
|
-
? async (
|
|
158
|
-
ref: string,
|
|
159
|
-
options: Readonly<{ signal: AbortSignal }>,
|
|
160
|
-
): Promise<Uint8Array | null> =>
|
|
161
|
-
unavailableResourceProofRefs.has(ref)
|
|
162
|
-
? null
|
|
163
|
-
: baseCoordinator.resolveResource!(ref, options)
|
|
164
|
-
: undefined;
|
|
165
|
-
const coordinator = proofScopedResolveResource
|
|
166
|
-
? {
|
|
167
|
-
...baseCoordinator,
|
|
168
|
-
resolveResource: proofScopedResolveResource,
|
|
169
|
-
}
|
|
170
|
-
: baseCoordinator;
|
|
171
|
-
const presentationResources =
|
|
172
|
-
coordinator.resolveResource && coordinator.retainResourceRef
|
|
173
|
-
? createToolcraftSourceAssetPresentationResources({
|
|
174
|
-
resolveResource: coordinator.resolveResource,
|
|
175
|
-
retainResourceRef: coordinator.retainResourceRef,
|
|
176
|
-
})
|
|
177
|
-
: null;
|
|
178
|
-
const owner = {
|
|
179
|
-
activeUnavailableResourceProof: null,
|
|
180
|
-
coordinator,
|
|
181
|
-
lifecycleGeneration: 0,
|
|
182
|
-
onDisposeError,
|
|
183
|
-
presentationResources,
|
|
184
|
-
unavailableResourceProofRefs,
|
|
185
|
-
};
|
|
186
|
-
sourceAssetOwners.set(store, owner);
|
|
187
|
-
return owner;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
function readUnavailableResourceProofRequest(
|
|
191
|
-
event: Event,
|
|
192
|
-
): ToolcraftUnavailableResourceProofRequest | null {
|
|
193
|
-
if (!(event instanceof CustomEvent)) return null;
|
|
194
|
-
const detail: unknown = event.detail;
|
|
195
|
-
if (typeof detail !== "object" || detail === null || Array.isArray(detail)) {
|
|
196
|
-
return null;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
const request = detail as Partial<ToolcraftUnavailableResourceProofRequest>;
|
|
200
|
-
if (
|
|
201
|
-
(request.action !== "activate" && request.action !== "cleanup") ||
|
|
202
|
-
typeof request.fileName !== "string" ||
|
|
203
|
-
request.fileName.trim() !== request.fileName ||
|
|
204
|
-
request.fileName.length === 0 ||
|
|
205
|
-
typeof request.requestId !== "string" ||
|
|
206
|
-
request.requestId.trim() !== request.requestId ||
|
|
207
|
-
request.requestId.length === 0 ||
|
|
208
|
-
(
|
|
209
|
-
request.sourceTarget !== undefined &&
|
|
210
|
-
(
|
|
211
|
-
typeof request.sourceTarget !== "string" ||
|
|
212
|
-
request.sourceTarget.trim() !== request.sourceTarget ||
|
|
213
|
-
request.sourceTarget.length === 0
|
|
214
|
-
)
|
|
215
|
-
)
|
|
216
|
-
) {
|
|
217
|
-
return null;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
return request as ToolcraftUnavailableResourceProofRequest;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
function reportOwnerDisposeError(
|
|
224
|
-
owner: ToolcraftSourceAssetOwner,
|
|
225
|
-
error: unknown,
|
|
226
|
-
): void {
|
|
227
|
-
try {
|
|
228
|
-
owner.onDisposeError(error);
|
|
229
|
-
} catch (reportingFailure) {
|
|
230
|
-
reportSourceAssetDisposeError(
|
|
231
|
-
new AggregateError(
|
|
232
|
-
[error, reportingFailure],
|
|
233
|
-
"Toolcraft source asset disposal and its injected reporter both failed.",
|
|
234
|
-
),
|
|
235
|
-
);
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
|
|
239
65
|
export function ToolcraftSourceAssetProvider({
|
|
240
66
|
binaryMediaHydrationJobs = [],
|
|
241
67
|
children,
|
|
242
|
-
createCoordinator
|
|
243
|
-
onDisposeError
|
|
68
|
+
createCoordinator,
|
|
69
|
+
onDisposeError,
|
|
70
|
+
onError,
|
|
244
71
|
store,
|
|
245
72
|
}: {
|
|
246
73
|
binaryMediaHydrationJobs?: readonly ToolcraftBinaryMediaHydrationJob[];
|
|
247
74
|
children: React.ReactNode;
|
|
248
75
|
createCoordinator?: ToolcraftSourceAssetCoordinatorFactory;
|
|
76
|
+
/** @deprecated Use onError. */
|
|
249
77
|
onDisposeError?: (error: unknown) => void;
|
|
78
|
+
onError?: (error: unknown) => void;
|
|
250
79
|
store: ToolcraftExternalStore;
|
|
251
80
|
}): React.JSX.Element {
|
|
252
|
-
const
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
81
|
+
const [acquisition, setAcquisition] =
|
|
82
|
+
React.useState<ToolcraftSourceAssetAcquisition | null>(null);
|
|
83
|
+
const activeLease = acquisition?.store === store
|
|
84
|
+
? acquisition.lease
|
|
85
|
+
: null;
|
|
86
|
+
const constructionOnError = onError ?? onDisposeError;
|
|
87
|
+
|
|
88
|
+
// Construction inputs are captured when the store owner is acquired.
|
|
89
|
+
// Same-store callback identity changes intentionally keep that owner.
|
|
90
|
+
React.useLayoutEffect(() => {
|
|
91
|
+
const lease = acquireToolcraftSourceAssetOwner({
|
|
92
|
+
createCoordinator,
|
|
93
|
+
decorateCoordinator:
|
|
94
|
+
decorateToolcraftUnavailableResourceProofCoordinator,
|
|
95
|
+
onError: constructionOnError,
|
|
96
|
+
store,
|
|
97
|
+
});
|
|
98
|
+
setAcquisition({
|
|
99
|
+
lease,
|
|
100
|
+
store,
|
|
101
|
+
});
|
|
102
|
+
return lease.release;
|
|
103
|
+
}, [store]);
|
|
104
|
+
|
|
105
|
+
return activeLease
|
|
106
|
+
? (
|
|
107
|
+
<ToolcraftSourceAssetProviderContent
|
|
108
|
+
binaryMediaHydrationJobs={binaryMediaHydrationJobs}
|
|
109
|
+
lease={activeLease}
|
|
110
|
+
store={store}
|
|
111
|
+
>
|
|
112
|
+
{children}
|
|
113
|
+
</ToolcraftSourceAssetProviderContent>
|
|
114
|
+
)
|
|
115
|
+
: <></>;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function ToolcraftSourceAssetProviderContent({
|
|
119
|
+
binaryMediaHydrationJobs,
|
|
120
|
+
children,
|
|
121
|
+
lease,
|
|
122
|
+
store,
|
|
123
|
+
}: {
|
|
124
|
+
binaryMediaHydrationJobs: readonly ToolcraftBinaryMediaHydrationJob[];
|
|
125
|
+
children: React.ReactNode;
|
|
126
|
+
lease: ToolcraftSourceAssetOwnerLease;
|
|
127
|
+
store: ToolcraftExternalStore;
|
|
128
|
+
}): React.JSX.Element {
|
|
129
|
+
const coordinator = lease.coordinator;
|
|
257
130
|
const modelPresentation = useToolcraftModelPresentationMode();
|
|
258
131
|
const customSourceTargets = React.useMemo(
|
|
259
132
|
() => modelPresentation.mode === "custom"
|
|
@@ -333,8 +206,6 @@ export function ToolcraftSourceAssetProvider({
|
|
|
333
206
|
);
|
|
334
207
|
|
|
335
208
|
React.useEffect(() => {
|
|
336
|
-
const generation = ++owner.lifecycleGeneration;
|
|
337
|
-
|
|
338
209
|
const hydrateModels = (): void => {
|
|
339
210
|
const currentTargets = new Set<string>();
|
|
340
211
|
for (const asset of store.getCommittedState().mediaAssets) {
|
|
@@ -346,22 +217,18 @@ export function ToolcraftSourceAssetProvider({
|
|
|
346
217
|
...hydratedModelTargetsRef.current,
|
|
347
218
|
...currentTargets,
|
|
348
219
|
])) {
|
|
349
|
-
|
|
220
|
+
coordinator.clearPresentationFeedback(target);
|
|
350
221
|
}
|
|
351
222
|
hydratedModelTargetsRef.current = currentTargets;
|
|
352
|
-
void
|
|
353
|
-
reportOwnerDisposeError(owner, error);
|
|
354
|
-
});
|
|
223
|
+
void coordinator.hydrateModels().catch(lease.reportError);
|
|
355
224
|
};
|
|
356
225
|
const unsubscribeHydration = store.subscribeSelector(
|
|
357
226
|
selectModelHydrationFingerprint,
|
|
358
227
|
hydrateModels,
|
|
359
228
|
);
|
|
360
229
|
const hydrateBinaryMedia = (): void => {
|
|
361
|
-
void
|
|
362
|
-
|
|
363
|
-
reportOwnerDisposeError(owner, error);
|
|
364
|
-
},
|
|
230
|
+
void coordinator.hydrateBinaryMedia(binaryHydrationJobs).catch(
|
|
231
|
+
lease.reportError,
|
|
365
232
|
);
|
|
366
233
|
};
|
|
367
234
|
const unsubscribeBinaryHydration = store.subscribeSelector(
|
|
@@ -374,258 +241,32 @@ export function ToolcraftSourceAssetProvider({
|
|
|
374
241
|
return () => {
|
|
375
242
|
unsubscribeHydration();
|
|
376
243
|
unsubscribeBinaryHydration();
|
|
377
|
-
queueMicrotask(() => {
|
|
378
|
-
if (
|
|
379
|
-
owner.lifecycleGeneration !== generation ||
|
|
380
|
-
sourceAssetOwners.get(store) !== owner
|
|
381
|
-
) {
|
|
382
|
-
return;
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
sourceAssetOwners.delete(store);
|
|
386
|
-
try {
|
|
387
|
-
owner.presentationResources?.dispose();
|
|
388
|
-
void owner.coordinator.dispose().catch((error: unknown) => {
|
|
389
|
-
reportOwnerDisposeError(owner, error);
|
|
390
|
-
});
|
|
391
|
-
} catch (error) {
|
|
392
|
-
reportOwnerDisposeError(owner, error);
|
|
393
|
-
}
|
|
394
|
-
});
|
|
395
|
-
};
|
|
396
|
-
}, [binaryHydrationJobs, owner, store]);
|
|
397
|
-
|
|
398
|
-
React.useEffect(() => {
|
|
399
|
-
if (!toolcraftUnavailableResourceProofEnabled) return undefined;
|
|
400
|
-
|
|
401
|
-
const rootSelector = '[data-slot="toolcraft-runtime-app"]';
|
|
402
|
-
let pendingRequestId: string | null = null;
|
|
403
|
-
|
|
404
|
-
const publish = (
|
|
405
|
-
requestId: string,
|
|
406
|
-
observation: Readonly<Record<string, boolean | string>>,
|
|
407
|
-
): void => {
|
|
408
|
-
const root = document.querySelector<HTMLElement>(rootSelector);
|
|
409
|
-
if (!root) return;
|
|
410
|
-
root.setAttribute(
|
|
411
|
-
TOOLCRAFT_UNAVAILABLE_RESOURCE_PROOF_ATTRIBUTE,
|
|
412
|
-
JSON.stringify({ requestId, ...observation }),
|
|
413
|
-
);
|
|
414
|
-
};
|
|
415
|
-
|
|
416
|
-
const handleRequest = (event: Event): void => {
|
|
417
|
-
const request = readUnavailableResourceProofRequest(event);
|
|
418
|
-
if (!request) return;
|
|
419
|
-
|
|
420
|
-
if (pendingRequestId) {
|
|
421
|
-
publish(request.requestId, {
|
|
422
|
-
message: `Unavailable-resource proof request "${pendingRequestId}" is still pending.`,
|
|
423
|
-
status: "error",
|
|
424
|
-
});
|
|
425
|
-
return;
|
|
426
|
-
}
|
|
427
|
-
pendingRequestId = request.requestId;
|
|
428
|
-
|
|
429
|
-
void (async () => {
|
|
430
|
-
if (request.action === "cleanup") {
|
|
431
|
-
const active = owner.activeUnavailableResourceProof;
|
|
432
|
-
if (
|
|
433
|
-
!active ||
|
|
434
|
-
active.requestId !== request.requestId
|
|
435
|
-
) {
|
|
436
|
-
throw new Error(
|
|
437
|
-
"Unavailable-resource proof cleanup must match the active fixture request.",
|
|
438
|
-
);
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
owner.unavailableResourceProofRefs.delete(active.resourceRef);
|
|
442
|
-
const restoredBytes = await owner.coordinator.resolveResource?.(
|
|
443
|
-
active.resourceRef,
|
|
444
|
-
{ signal: new AbortController().signal },
|
|
445
|
-
);
|
|
446
|
-
if (!restoredBytes?.byteLength) {
|
|
447
|
-
throw new Error(
|
|
448
|
-
"Unavailable-resource proof cleanup could not restore the real resolver.",
|
|
449
|
-
);
|
|
450
|
-
}
|
|
451
|
-
store.dispatch({
|
|
452
|
-
assetId: active.assetId,
|
|
453
|
-
expectedResourceRef: active.resourceRef,
|
|
454
|
-
lifecycle: "ready",
|
|
455
|
-
type: "media.setBinaryResourceState",
|
|
456
|
-
});
|
|
457
|
-
const restored = store
|
|
458
|
-
.getCommittedState()
|
|
459
|
-
.mediaAssets.find((asset) => asset.id === active.assetId);
|
|
460
|
-
if (
|
|
461
|
-
!restored ||
|
|
462
|
-
restored.assetKind !== "image" ||
|
|
463
|
-
restored.lifecycle !== "ready"
|
|
464
|
-
) {
|
|
465
|
-
throw new Error(
|
|
466
|
-
"Unavailable-resource proof cleanup did not restore the typed resource state.",
|
|
467
|
-
);
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
owner.activeUnavailableResourceProof = null;
|
|
471
|
-
publish(request.requestId, {
|
|
472
|
-
assetId: active.assetId,
|
|
473
|
-
fileName: request.fileName,
|
|
474
|
-
lifecycle: "ready",
|
|
475
|
-
resolverAvailable: true,
|
|
476
|
-
status: "cleaned",
|
|
477
|
-
});
|
|
478
|
-
return;
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
if (owner.activeUnavailableResourceProof) {
|
|
482
|
-
throw new Error(
|
|
483
|
-
"Unavailable-resource proof supports exactly one active resource fixture.",
|
|
484
|
-
);
|
|
485
|
-
}
|
|
486
|
-
const candidates = store
|
|
487
|
-
.getCommittedState()
|
|
488
|
-
.mediaAssets.filter(
|
|
489
|
-
(
|
|
490
|
-
asset,
|
|
491
|
-
): asset is ToolcraftImageAsset & { lifecycle: "ready" } =>
|
|
492
|
-
asset.assetKind === "image" &&
|
|
493
|
-
asset.lifecycle === "ready" &&
|
|
494
|
-
asset.fileName === request.fileName &&
|
|
495
|
-
(
|
|
496
|
-
request.sourceTarget === undefined ||
|
|
497
|
-
asset.sourceTarget === request.sourceTarget
|
|
498
|
-
),
|
|
499
|
-
);
|
|
500
|
-
if (candidates.length !== 1) {
|
|
501
|
-
throw new Error(
|
|
502
|
-
`Unavailable-resource proof requires exactly one ready image named "${request.fileName}", received ${candidates.length}.`,
|
|
503
|
-
);
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
const asset = candidates[0]!;
|
|
507
|
-
const initialBytes = await owner.coordinator.resolveResource?.(
|
|
508
|
-
asset.resourceRef,
|
|
509
|
-
{ signal: new AbortController().signal },
|
|
510
|
-
);
|
|
511
|
-
if (!initialBytes?.byteLength) {
|
|
512
|
-
throw new Error(
|
|
513
|
-
"Unavailable-resource proof requires a resource available through the real resolver before fault activation.",
|
|
514
|
-
);
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
owner.unavailableResourceProofRefs.add(asset.resourceRef);
|
|
518
|
-
try {
|
|
519
|
-
const faultedBytes = await owner.coordinator.resolveResource?.(
|
|
520
|
-
asset.resourceRef,
|
|
521
|
-
{ signal: new AbortController().signal },
|
|
522
|
-
);
|
|
523
|
-
if (faultedBytes !== null) {
|
|
524
|
-
throw new Error(
|
|
525
|
-
"Unavailable-resource proof did not isolate the requested resolver resource.",
|
|
526
|
-
);
|
|
527
|
-
}
|
|
528
|
-
store.dispatch({
|
|
529
|
-
assetId: asset.id,
|
|
530
|
-
error: {
|
|
531
|
-
code: "browser-proof-resource-unavailable",
|
|
532
|
-
message:
|
|
533
|
-
"The resource is intentionally unavailable for protected browser proof.",
|
|
534
|
-
},
|
|
535
|
-
expectedResourceRef: asset.resourceRef,
|
|
536
|
-
lifecycle: "unavailable",
|
|
537
|
-
type: "media.setBinaryResourceState",
|
|
538
|
-
});
|
|
539
|
-
const unavailable = store
|
|
540
|
-
.getCommittedState()
|
|
541
|
-
.mediaAssets.find((candidate) => candidate.id === asset.id);
|
|
542
|
-
if (
|
|
543
|
-
!unavailable ||
|
|
544
|
-
unavailable.assetKind !== "image" ||
|
|
545
|
-
unavailable.lifecycle !== "unavailable"
|
|
546
|
-
) {
|
|
547
|
-
throw new Error(
|
|
548
|
-
"Unavailable-resource proof did not reach the typed unavailable state.",
|
|
549
|
-
);
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
owner.activeUnavailableResourceProof = {
|
|
553
|
-
assetId: asset.id,
|
|
554
|
-
requestId: request.requestId,
|
|
555
|
-
resourceRef: asset.resourceRef,
|
|
556
|
-
};
|
|
557
|
-
publish(request.requestId, {
|
|
558
|
-
assetId: asset.id,
|
|
559
|
-
fileName: asset.fileName,
|
|
560
|
-
lifecycle: "unavailable",
|
|
561
|
-
resolverAvailable: false,
|
|
562
|
-
status: "active",
|
|
563
|
-
});
|
|
564
|
-
} catch (error) {
|
|
565
|
-
owner.unavailableResourceProofRefs.delete(asset.resourceRef);
|
|
566
|
-
throw error;
|
|
567
|
-
}
|
|
568
|
-
})()
|
|
569
|
-
.catch((error: unknown) => {
|
|
570
|
-
publish(request.requestId, {
|
|
571
|
-
message:
|
|
572
|
-
error instanceof Error
|
|
573
|
-
? error.message
|
|
574
|
-
: "Unavailable-resource proof fixture failed.",
|
|
575
|
-
status: "error",
|
|
576
|
-
});
|
|
577
|
-
})
|
|
578
|
-
.finally(() => {
|
|
579
|
-
pendingRequestId = null;
|
|
580
|
-
});
|
|
581
|
-
};
|
|
582
|
-
|
|
583
|
-
document.addEventListener(
|
|
584
|
-
TOOLCRAFT_UNAVAILABLE_RESOURCE_PROOF_REQUEST,
|
|
585
|
-
handleRequest,
|
|
586
|
-
);
|
|
587
|
-
|
|
588
|
-
return () => {
|
|
589
|
-
document.removeEventListener(
|
|
590
|
-
TOOLCRAFT_UNAVAILABLE_RESOURCE_PROOF_REQUEST,
|
|
591
|
-
handleRequest,
|
|
592
|
-
);
|
|
593
|
-
const active = owner.activeUnavailableResourceProof;
|
|
594
|
-
if (!active) return;
|
|
595
|
-
owner.unavailableResourceProofRefs.delete(active.resourceRef);
|
|
596
|
-
store.dispatch({
|
|
597
|
-
assetId: active.assetId,
|
|
598
|
-
expectedResourceRef: active.resourceRef,
|
|
599
|
-
lifecycle: "ready",
|
|
600
|
-
type: "media.setBinaryResourceState",
|
|
601
|
-
});
|
|
602
|
-
owner.activeUnavailableResourceProof = null;
|
|
603
244
|
};
|
|
604
|
-
}, [
|
|
245
|
+
}, [binaryHydrationJobs, coordinator, lease.reportError, store]);
|
|
605
246
|
|
|
606
247
|
const context = (
|
|
607
248
|
<ToolcraftMediaPresentationProvider
|
|
608
249
|
bootstrapUrls={binaryBootstrapUrls}
|
|
609
|
-
resources={
|
|
250
|
+
resources={lease.presentationResources}
|
|
610
251
|
>
|
|
611
|
-
<ToolcraftSourceAssetCoordinatorContext.Provider value={
|
|
252
|
+
<ToolcraftSourceAssetCoordinatorContext.Provider value={coordinator}>
|
|
612
253
|
{children}
|
|
613
254
|
</ToolcraftSourceAssetCoordinatorContext.Provider>
|
|
614
255
|
</ToolcraftMediaPresentationProvider>
|
|
615
256
|
);
|
|
616
257
|
|
|
617
|
-
return
|
|
258
|
+
return coordinator.resolveResource ? (
|
|
618
259
|
<ToolcraftModelRenderProvider
|
|
619
260
|
activeCustomTargets={activeCustomTargets}
|
|
620
|
-
clearPresentationFeedback={
|
|
261
|
+
clearPresentationFeedback={coordinator.clearPresentationFeedback}
|
|
621
262
|
customConsumers={
|
|
622
263
|
modelPresentation.mode === "custom"
|
|
623
264
|
? modelPresentation.consumers
|
|
624
265
|
: []
|
|
625
266
|
}
|
|
626
|
-
reportPresentationFeedback={
|
|
627
|
-
resolveResource={
|
|
628
|
-
retainResourceRef={
|
|
267
|
+
reportPresentationFeedback={coordinator.reportPresentationFeedback}
|
|
268
|
+
resolveResource={coordinator.resolveResource}
|
|
269
|
+
retainResourceRef={coordinator.retainResourceRef}
|
|
629
270
|
visibleOrientationTarget={visibleOrientationTarget || undefined}
|
|
630
271
|
>
|
|
631
272
|
{context}
|