@volter/editor-react 0.5.68 → 0.5.69
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/NOTICE +1 -1
- package/contributions/component-contracts.service.ts +1 -1
- package/dist-node/serving.mjs +23 -23
- package/package.json +4 -4
- package/serving/index.ts +1 -1
- package/serving/ui-oid-plugin.ts +19 -19
- package/src/source/oid-transform.ts +24 -24
- package/src/source/plan-csf-story.ts +1 -1
- package/src/source/plan-extract-component.ts +1 -1
- package/src/source/r3f-diagnostic-index.ts +1 -1
- package/src/source/r3f-environment-binding.ts +1 -1
- package/src/source/r3f-project-contracts.ts +3 -3
package/NOTICE
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* The component-contract analyzer, registered for the browser (a `workspace.service`
|
|
3
3
|
* contribution): what the kit's Content index reads when it indexes a project from storage,
|
|
4
4
|
* with no session server to ask (`@volter/editor-sdk/source-analysis`). The session's server
|
|
5
|
-
* registers the same analyzer through this package's `
|
|
5
|
+
* registers the same analyzer through this package's `volter.serving` module.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { registerComponentContractAnalyzer } from '@volter/editor-sdk/source-analysis';
|
package/dist-node/serving.mjs
CHANGED
|
@@ -2718,31 +2718,31 @@ function transformSource(code, file, store, opts) {
|
|
|
2718
2718
|
const componentInstanceExpressions = (parameters) => {
|
|
2719
2719
|
const first = parameters[0];
|
|
2720
2720
|
if (!first) {
|
|
2721
|
-
sourceEdits.push({ pos: parameters.pos, text: "{
|
|
2722
|
-
return { instanceExpression: "
|
|
2721
|
+
sourceEdits.push({ pos: parameters.pos, text: "{ __volterOid, __volterLabel } = {}" });
|
|
2722
|
+
return { instanceExpression: "__volterOid", labelExpression: "__volterLabel" };
|
|
2723
2723
|
}
|
|
2724
2724
|
if (ts8.isObjectBindingPattern(first.name)) {
|
|
2725
2725
|
sourceEdits.push({
|
|
2726
2726
|
pos: first.name.getStart(sf) + 1,
|
|
2727
|
-
text: "
|
|
2727
|
+
text: "__volterOid, __volterLabel, "
|
|
2728
2728
|
});
|
|
2729
2729
|
if (!first.initializer) {
|
|
2730
2730
|
sourceEdits.push({ pos: first.getEnd(), text: " = {}" });
|
|
2731
2731
|
}
|
|
2732
|
-
return { instanceExpression: "
|
|
2732
|
+
return { instanceExpression: "__volterOid", labelExpression: "__volterLabel" };
|
|
2733
2733
|
}
|
|
2734
2734
|
if (ts8.isIdentifier(first.name) && !first.dotDotDotToken) {
|
|
2735
2735
|
sourceEdits.push({
|
|
2736
2736
|
pos: first.name.getStart(sf),
|
|
2737
2737
|
end: first.questionToken?.end ?? first.name.getEnd(),
|
|
2738
|
-
text: `{
|
|
2738
|
+
text: `{ __volterOid, __volterLabel, ...${first.name.text} }`
|
|
2739
2739
|
});
|
|
2740
2740
|
if (!first.initializer) {
|
|
2741
2741
|
sourceEdits.push({ pos: first.getEnd(), text: " = {}" });
|
|
2742
2742
|
}
|
|
2743
|
-
return { instanceExpression: "
|
|
2743
|
+
return { instanceExpression: "__volterOid", labelExpression: "__volterLabel" };
|
|
2744
2744
|
}
|
|
2745
|
-
return { instanceExpression: "
|
|
2745
|
+
return { instanceExpression: "__volterOid", labelExpression: "__volterLabel" };
|
|
2746
2746
|
};
|
|
2747
2747
|
const pushComponent = (name, parameters, body) => {
|
|
2748
2748
|
if (!name || !/^[A-Z]/.test(name) || !parameters) return false;
|
|
@@ -2821,10 +2821,10 @@ function transformSource(code, file, store, opts) {
|
|
|
2821
2821
|
(property2) => ts8.isJsxAttribute(property2) && property2.name.getText(sf) === "name"
|
|
2822
2822
|
);
|
|
2823
2823
|
const label = authoredName?.initializer && ts8.isStringLiteral(authoredName.initializer) ? authoredName.initializer.text : tag;
|
|
2824
|
-
const labelAttribute = authoredName?.initializer && ts8.isJsxExpression(authoredName.initializer) ? `
|
|
2824
|
+
const labelAttribute = authoredName?.initializer && ts8.isJsxExpression(authoredName.initializer) ? `__volterLabel={${authoredName.initializer.expression?.getText(sf) ?? JSON.stringify(tag)}}` : `__volterLabel=${JSON.stringify(label)}`;
|
|
2825
2825
|
sourceEdits.push({
|
|
2826
2826
|
pos: node.attributes.end,
|
|
2827
|
-
text: `
|
|
2827
|
+
text: ` __volterOid="${oid}" ${labelAttribute}`
|
|
2828
2828
|
});
|
|
2829
2829
|
}
|
|
2830
2830
|
} else {
|
|
@@ -3487,7 +3487,7 @@ function r3fAuthoringDiagnostics(code, file, opts) {
|
|
|
3487
3487
|
message: `${rootComponent} animates its authored outer root through useFrame. Keep the outer root stable and animate an inner child so editor transforms do not fight runtime motion.`
|
|
3488
3488
|
});
|
|
3489
3489
|
}
|
|
3490
|
-
if (/^[A-Z]/.test(tag) && contract?.root === "single" && // A package component
|
|
3490
|
+
if (/^[A-Z]/.test(tag) && contract?.root === "single" && // A package component volter knows only through the built-in table is not
|
|
3491
3491
|
// a hierarchy row of its own — `<RigidBody>` never receives the editor's
|
|
3492
3492
|
// stamp, so its props are attributed to the node it wraps
|
|
3493
3493
|
// (`collapsedWrappersOf`). Naming it would name nothing.
|
|
@@ -3525,8 +3525,8 @@ import { existsSync as existsSync2 } from "node:fs";
|
|
|
3525
3525
|
import { join } from "node:path";
|
|
3526
3526
|
|
|
3527
3527
|
// ../editor-project/src/manifest/filename.ts
|
|
3528
|
-
var MANIFEST_FILENAME = "
|
|
3529
|
-
var REMOVED_MANIFEST_FILENAME = "
|
|
3528
|
+
var MANIFEST_FILENAME = "volter.project.json";
|
|
3529
|
+
var REMOVED_MANIFEST_FILENAME = "volter.game.json";
|
|
3530
3530
|
function removedManifestFilenameMessage(where) {
|
|
3531
3531
|
return `${where}: found \`${REMOVED_MANIFEST_FILENAME}\` and no \`${MANIFEST_FILENAME}\`. \`${REMOVED_MANIFEST_FILENAME}\` was REMOVED (the legacy-removal doctrine, docs/ARCHITECTURE-CORE.md \xA7Vocabulary) \u2014 nothing reads it any more, and it is deliberately NOT read as a fallback, because a second accepted name is the defect.
|
|
3532
3532
|
Fix: rename it \u2014 \`git mv ${REMOVED_MANIFEST_FILENAME} ${MANIFEST_FILENAME}\`. The contents are unchanged; only the filename moved.`;
|
|
@@ -3544,7 +3544,7 @@ function resolveManifestPath(dir) {
|
|
|
3544
3544
|
}
|
|
3545
3545
|
|
|
3546
3546
|
// ../editor-sdk/src/session/project-serving.ts
|
|
3547
|
-
var SOURCE_WRITE_ROUTES_PLUGIN = "
|
|
3547
|
+
var SOURCE_WRITE_ROUTES_PLUGIN = "volter-ui-oid";
|
|
3548
3548
|
|
|
3549
3549
|
// ../editor-sdk/src/css-numeric-style.ts
|
|
3550
3550
|
var UNITLESS_NUMBER_PROPS = /* @__PURE__ */ new Set([
|
|
@@ -3684,7 +3684,7 @@ function planDeleteStory(source, name) {
|
|
|
3684
3684
|
if (outside.includes(`defaultStory: '${name}'`) || outside.includes(`defaultStory: "${name}"`)) {
|
|
3685
3685
|
return {
|
|
3686
3686
|
ok: false,
|
|
3687
|
-
reason: `'${name}' is this file's declared defaultStory \u2014 point
|
|
3687
|
+
reason: `'${name}' is this file's declared defaultStory \u2014 point volter.defaultStory at another story first`
|
|
3688
3688
|
};
|
|
3689
3689
|
}
|
|
3690
3690
|
return { ok: true, nextSource: outside, summary: `story '${name}' deleted` };
|
|
@@ -4246,7 +4246,7 @@ export default meta;
|
|
|
4246
4246
|
type Story = StoryObj<typeof meta>;
|
|
4247
4247
|
|
|
4248
4248
|
export const Default: Story = {
|
|
4249
|
-
parameters: {
|
|
4249
|
+
parameters: { volter: { default: true } },
|
|
4250
4250
|
};
|
|
4251
4251
|
`;
|
|
4252
4252
|
const importSpecifier = relativeImportSpecifier(normalizePath(request.sourceFile), componentFile);
|
|
@@ -5048,7 +5048,7 @@ function fileDiagnosticJoin(fileEntries, fileDiagnostics) {
|
|
|
5048
5048
|
|
|
5049
5049
|
// serving/ui-oid-plugin.ts
|
|
5050
5050
|
var NODE_MODULES_RE = /\/node_modules\//;
|
|
5051
|
-
var TOOLING_SRC_RE = /\/packages\/(engine\/src|create-
|
|
5051
|
+
var TOOLING_SRC_RE = /\/packages\/(engine\/src|create-volter-project|volter-cli|editor-sdk)\//;
|
|
5052
5052
|
var EDITOR_SRC_RE = /\/packages\/editor\/src\//;
|
|
5053
5053
|
var EDITABLE_COMPONENTS_RE = /\/ui-editor\/editable-components\/.*\.tsx$/;
|
|
5054
5054
|
var VENDOR_RE = /\/vendor\//;
|
|
@@ -5105,7 +5105,7 @@ function sourceResourcePath(file, projectRoot) {
|
|
|
5105
5105
|
const path = relative(projectRoot, file).replace(/\\/g, "/");
|
|
5106
5106
|
if (path && path !== ".." && !path.startsWith("../") && !isAbsolute(path)) return path;
|
|
5107
5107
|
const identity = createHash("sha256").update(file).digest("hex").slice(0, 16);
|
|
5108
|
-
return `.
|
|
5108
|
+
return `.volter/external-source/${identity}/${basename(file)}`;
|
|
5109
5109
|
}
|
|
5110
5110
|
var utilityClassSupport = /* @__PURE__ */ new Map();
|
|
5111
5111
|
function owningPackageDir(file, fallback) {
|
|
@@ -5130,7 +5130,7 @@ function utilityClassEvidence(dir) {
|
|
|
5130
5130
|
const absolute = join2(dir, name);
|
|
5131
5131
|
if (existsSync3(absolute)) read(absolute, name);
|
|
5132
5132
|
}
|
|
5133
|
-
const skip = /* @__PURE__ */ new Set(["node_modules", "dist", "build", "coverage", ".git", ".
|
|
5133
|
+
const skip = /* @__PURE__ */ new Set(["node_modules", "dist", "build", "coverage", ".git", ".volter", ".claude"]);
|
|
5134
5134
|
let budget = 80;
|
|
5135
5135
|
const walk = (current, prefix, depth) => {
|
|
5136
5136
|
if (depth > 3 || budget <= 0) return;
|
|
@@ -5254,7 +5254,7 @@ function rejectPendingPropRequests(error) {
|
|
|
5254
5254
|
}
|
|
5255
5255
|
function declaredPropWorker() {
|
|
5256
5256
|
if (propWorker) return propWorker;
|
|
5257
|
-
const worker = new Worker(componentPropWorkerUrl(), { name: "
|
|
5257
|
+
const worker = new Worker(componentPropWorkerUrl(), { name: "volter-component-props" });
|
|
5258
5258
|
worker.on(
|
|
5259
5259
|
"message",
|
|
5260
5260
|
(message) => {
|
|
@@ -5890,8 +5890,8 @@ function uiOidPlugin(services, include = defaultProjectScopeInclude) {
|
|
|
5890
5890
|
try {
|
|
5891
5891
|
const body = await readJson(req);
|
|
5892
5892
|
const claimedParticipantId = body["participantId"];
|
|
5893
|
-
const trustedParticipantId = req.headers["x-
|
|
5894
|
-
const trustedRole = req.headers["x-
|
|
5893
|
+
const trustedParticipantId = req.headers["x-volter-share-participant-id"];
|
|
5894
|
+
const trustedRole = req.headers["x-volter-share-role"];
|
|
5895
5895
|
if (typeof trustedParticipantId === "string" && typeof claimedParticipantId === "string" && trustedParticipantId !== claimedParticipantId) {
|
|
5896
5896
|
throw new Error(
|
|
5897
5897
|
"The authenticated share participant does not match the source mutation author."
|
|
@@ -5956,10 +5956,10 @@ function uiOidPlugin(services, include = defaultProjectScopeInclude) {
|
|
|
5956
5956
|
const index = currentOidIndex(store, activeProjectRoot(), server);
|
|
5957
5957
|
res.setHeader("Server-Timing", `index;dur=${Date.now() - indexStarted}`);
|
|
5958
5958
|
res.setHeader(
|
|
5959
|
-
"X-
|
|
5959
|
+
"X-Volter-Oid-Index-Enriched",
|
|
5960
5960
|
index === lastEnrichedIndex?.index ? "1" : "0"
|
|
5961
5961
|
);
|
|
5962
|
-
if (typeof req.headers["x-
|
|
5962
|
+
if (typeof req.headers["x-volter-share-participant-id"] !== "string") {
|
|
5963
5963
|
return json(res, index);
|
|
5964
5964
|
}
|
|
5965
5965
|
return json(
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@volter/editor-react",
|
|
3
3
|
"author": "Volter AI, Inc.",
|
|
4
4
|
"license": "AGPL-3.0-only",
|
|
5
|
-
"version": "0.5.
|
|
5
|
+
"version": "0.5.69",
|
|
6
6
|
"publishConfig": {
|
|
7
7
|
"access": "public"
|
|
8
8
|
},
|
|
@@ -31,15 +31,15 @@
|
|
|
31
31
|
"prepack": "npm run build",
|
|
32
32
|
"typecheck": "tsc -p tsconfig.json"
|
|
33
33
|
},
|
|
34
|
-
"
|
|
34
|
+
"volter": {
|
|
35
35
|
"contributions": [
|
|
36
36
|
"./contributions/component-contracts.service.ts"
|
|
37
37
|
],
|
|
38
38
|
"serving": "./dist-node/serving.mjs"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@volter/editor-sdk": "0.5.
|
|
42
|
-
"@volter/editor-project": "0.5.
|
|
41
|
+
"@volter/editor-sdk": "0.5.69",
|
|
42
|
+
"@volter/editor-project": "0.5.69",
|
|
43
43
|
"typescript": "5.9.3"
|
|
44
44
|
},
|
|
45
45
|
"peerDependencies": {
|
package/serving/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `@volter/editor-react`'s server half (`package.json#
|
|
2
|
+
* `@volter/editor-react`'s server half (`package.json#volter.serving`, the project-serving door in
|
|
3
3
|
* `@volter/editor-sdk/session/project-serving`): the JSX identity stamp and the `/__ui-source/*`
|
|
4
4
|
* authoring routes over a project's React and React Three Fiber source, and the source answers
|
|
5
5
|
* the kit's region decision, validation and Content index ask of a JSX lane.
|
package/serving/ui-oid-plugin.ts
CHANGED
|
@@ -19,13 +19,13 @@
|
|
|
19
19
|
* dev-server `fs.allow` pipeline `server/dev.ts` already builds for project scripts
|
|
20
20
|
* (T3.3) — so `ReactRootAuthoringAdapter` needs those files instrumented too.
|
|
21
21
|
* `defaultProjectScopeInclude` widens to PROJECT scope, not repo-global: any `.tsx`
|
|
22
|
-
* file outside `node_modules`, EXCEPT the
|
|
23
|
-
* itself is built from (`packages/engine/src`, `packages/create-
|
|
24
|
-
* `packages/
|
|
22
|
+
* file outside `node_modules`, EXCEPT the volter tooling/engine source trees this repo
|
|
23
|
+
* itself is built from (`packages/engine/src`, `packages/create-volter-project`,
|
|
24
|
+
* `packages/volter-cli`, `packages/editor-sdk`, and `packages/editor/src` generally —
|
|
25
25
|
* carving OUT `ui-editor/editable-components` so the existing UI-edit-mode surface
|
|
26
26
|
* keeps working unchanged). This does NOT depend on which project happens to be
|
|
27
27
|
* open — `fs.allow` already bounds what Vite can even reach, and this predicate
|
|
28
|
-
* additionally keeps the
|
|
28
|
+
* additionally keeps the volter app's OWN react source un-instrumented.
|
|
29
29
|
*
|
|
30
30
|
* Vendored trees: a vendored GAME's source is STAMPED; every other `/vendor/`
|
|
31
31
|
* tree is not. `data-oid` stamping is what makes a `.tsx` file addressable —
|
|
@@ -53,11 +53,11 @@
|
|
|
53
53
|
* the write-back ban becomes IDENTITY-scoped, not just location-scoped: the
|
|
54
54
|
* `/vendor/` regex above only protects the repo's OWN vendored trees, but
|
|
55
55
|
* wave 2 lets an `ingest-react` world live in ANY external, user-owned
|
|
56
|
-
* folder (`
|
|
56
|
+
* folder (`volter-game-editor edit <folder>`) — an ingested-as-is foreign game there would
|
|
57
57
|
* otherwise be silently stamped/writable, reopening the never-modify-game-
|
|
58
58
|
* source rule by location rather than by identity. `nearestManifestExcludesIngestReact`
|
|
59
59
|
* walks up from a candidate `.tsx`'s own directory to the NEAREST
|
|
60
|
-
* `
|
|
60
|
+
* `volter.project.json`; if that manifest declares an `{ ingest }` react world,
|
|
61
61
|
* the whole manifest folder's subtree is excluded — exact, not coarse:
|
|
62
62
|
* `ingest-react` is single-world by construction (`binding-resolver.ts`'s
|
|
63
63
|
* `MULTI_WORLD_ALLOWED_IDENTITIES` exclusion), so "the manifest folder" IS
|
|
@@ -146,7 +146,7 @@ import {
|
|
|
146
146
|
export { applyStyleWriteRequest, oidAttributeForSurface };
|
|
147
147
|
|
|
148
148
|
const NODE_MODULES_RE = /\/node_modules\//;
|
|
149
|
-
const TOOLING_SRC_RE = /\/packages\/(engine\/src|create-
|
|
149
|
+
const TOOLING_SRC_RE = /\/packages\/(engine\/src|create-volter-project|volter-cli|editor-sdk)\//;
|
|
150
150
|
const EDITOR_SRC_RE = /\/packages\/editor\/src\//;
|
|
151
151
|
const EDITABLE_COMPONENTS_RE = /\/ui-editor\/editable-components\/.*\.tsx$/;
|
|
152
152
|
/** Track N, D-N4 item 2 — see the module doc comment's "Vendored-tree exclusion". */
|
|
@@ -170,13 +170,13 @@ const VENDOR_RE = /\/vendor\//;
|
|
|
170
170
|
*/
|
|
171
171
|
const VENDORED_GAME_SRC_RE = /\/vendor\/games\/[^/]+\//;
|
|
172
172
|
|
|
173
|
-
/** True if a parsed `
|
|
173
|
+
/** True if a parsed `volter.project.json` body declares at least one
|
|
174
174
|
* React root with an `{ ingest }` adapter — the `{ ingest }` shape alone is
|
|
175
175
|
* the answer, because every ingest-react root is D-N8 no-authoring.
|
|
176
176
|
* Defensive against
|
|
177
177
|
* any shape (never trusts the manifest is even an object) — this is a
|
|
178
178
|
* best-effort identity probe for a WRITE-BACK BAN, not manifest validation
|
|
179
|
-
* (the real Zod schema/loader is `@
|
|
179
|
+
* (the real Zod schema/loader is `@volter/project/manifest/load`, not reachable
|
|
180
180
|
* from this vite-config-time, dependency-light file by design). */
|
|
181
181
|
function manifestDeclaresIngestReactWorld(parsed: unknown): boolean {
|
|
182
182
|
if (!parsed || typeof parsed !== 'object') return false;
|
|
@@ -236,7 +236,7 @@ function nearestManifestExcludesIngestReact(dir: string): boolean {
|
|
|
236
236
|
* Cap 2 (React visual-edit parity): the scope guard for the `/__ui-source/css` endpoint.
|
|
237
237
|
* A CSS file path (from a stylesheet's `data-vite-dev-id`) is editable iff it is a
|
|
238
238
|
* first-party PROJECT `.css` file — same carve-outs as `defaultProjectScopeInclude`
|
|
239
|
-
* (never node_modules, vendored trees, the
|
|
239
|
+
* (never node_modules, vendored trees, the volter tooling/engine source, or the editor's
|
|
240
240
|
* own source). Paths are normalized to forward slashes so a Windows dev-id matches. This
|
|
241
241
|
* is the CSS analogue of the `.tsx` stamping scope: an out-of-scope path must not become
|
|
242
242
|
* writable through a client-supplied `file`.
|
|
@@ -267,7 +267,7 @@ function sourceResourcePath(file: string, projectRoot: string): string {
|
|
|
267
267
|
const path = relative(projectRoot, file).replace(/\\/g, '/');
|
|
268
268
|
if (path && path !== '..' && !path.startsWith('../') && !isAbsolute(path)) return path;
|
|
269
269
|
const identity = createHash('sha256').update(file).digest('hex').slice(0, 16);
|
|
270
|
-
return `.
|
|
270
|
+
return `.volter/external-source/${identity}/${basename(file)}`;
|
|
271
271
|
}
|
|
272
272
|
|
|
273
273
|
/**
|
|
@@ -312,7 +312,7 @@ function utilityClassEvidence(dir: string): Map<string, string> {
|
|
|
312
312
|
}
|
|
313
313
|
// Bounded walk for the css/html entry markers (`@tailwind`, a CDN script):
|
|
314
314
|
// shallow, skipping the directories that never hold a project's own entry.
|
|
315
|
-
const skip = new Set(['node_modules', 'dist', 'build', 'coverage', '.git', '.
|
|
315
|
+
const skip = new Set(['node_modules', 'dist', 'build', 'coverage', '.git', '.volter', '.claude']);
|
|
316
316
|
let budget = 80;
|
|
317
317
|
const walk = (current: string, prefix: string, depth: number): void => {
|
|
318
318
|
if (depth > 3 || budget <= 0) return;
|
|
@@ -530,7 +530,7 @@ function rejectPendingPropRequests(error: Error): void {
|
|
|
530
530
|
|
|
531
531
|
function declaredPropWorker(): Worker {
|
|
532
532
|
if (propWorker) return propWorker;
|
|
533
|
-
const worker = new Worker(componentPropWorkerUrl(), { name: '
|
|
533
|
+
const worker = new Worker(componentPropWorkerUrl(), { name: 'volter-component-props' });
|
|
534
534
|
worker.on(
|
|
535
535
|
'message',
|
|
536
536
|
(message: { id?: unknown; props?: Record<string, ComponentPropSpec[]>; error?: unknown }) => {
|
|
@@ -1549,7 +1549,7 @@ export function uiOidPlugin(
|
|
|
1549
1549
|
transform(code, id) {
|
|
1550
1550
|
// A VIRTUAL MODULE IS NOT A FILE, and everything below this line treats
|
|
1551
1551
|
// `clean` as one — `resolveOidSurface` walks up from it looking for the
|
|
1552
|
-
// owning `
|
|
1552
|
+
// owning `volter.project.json`, and `transformSource` records it as the
|
|
1553
1553
|
// stamped file's path. Vite's convention is that a `\0`-prefixed id was
|
|
1554
1554
|
// synthesized by a plugin and has no path behind it, so a path plugin
|
|
1555
1555
|
// skips it.
|
|
@@ -1566,7 +1566,7 @@ export function uiOidPlugin(
|
|
|
1566
1566
|
// W1 — R3F-dialect files get ` userData-oid` (fiber pierces it into
|
|
1567
1567
|
// `object.userData.oid`); react-dom files keep ` data-oid` unchanged.
|
|
1568
1568
|
// WHICH REGION owns this exact file decides: a declaration the project
|
|
1569
|
-
// made (an `include` glob or a `mounts` entry in `
|
|
1569
|
+
// made (an `include` glob or a `mounts` entry in `volter.adapter.ts`), the
|
|
1570
1570
|
// manifest root whose `entry` it is, or reach from one through the live
|
|
1571
1571
|
// import graph. Nothing is inferred from the file's own bytes; a file
|
|
1572
1572
|
// nothing places gets the documented `data-oid` default, said out loud.
|
|
@@ -1688,8 +1688,8 @@ export function uiOidPlugin(
|
|
|
1688
1688
|
try {
|
|
1689
1689
|
const body = await readJson(req);
|
|
1690
1690
|
const claimedParticipantId = body['participantId'];
|
|
1691
|
-
const trustedParticipantId = req.headers['x-
|
|
1692
|
-
const trustedRole = req.headers['x-
|
|
1691
|
+
const trustedParticipantId = req.headers['x-volter-share-participant-id'];
|
|
1692
|
+
const trustedRole = req.headers['x-volter-share-role'];
|
|
1693
1693
|
if (
|
|
1694
1694
|
typeof trustedParticipantId === 'string' &&
|
|
1695
1695
|
typeof claimedParticipantId === 'string' &&
|
|
@@ -1792,10 +1792,10 @@ export function uiOidPlugin(
|
|
|
1792
1792
|
const index = currentOidIndex(store, activeProjectRoot(), server);
|
|
1793
1793
|
res.setHeader('Server-Timing', `index;dur=${Date.now() - indexStarted}`);
|
|
1794
1794
|
res.setHeader(
|
|
1795
|
-
'X-
|
|
1795
|
+
'X-Volter-Oid-Index-Enriched',
|
|
1796
1796
|
index === lastEnrichedIndex?.index ? '1' : '0',
|
|
1797
1797
|
);
|
|
1798
|
-
if (typeof req.headers['x-
|
|
1798
|
+
if (typeof req.headers['x-volter-share-participant-id'] !== 'string') {
|
|
1799
1799
|
return json(res, index);
|
|
1800
1800
|
}
|
|
1801
1801
|
return json(
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* OID instrumentation (C1/C2) — the visual-edit "stamp every JSX element with a
|
|
3
|
-
* stable source id" trick, ported to
|
|
4
|
-
* (
|
|
3
|
+
* stable source id" trick, ported to volter using the TypeScript compiler API
|
|
4
|
+
* (volter has `typescript`; it does NOT have @babel/*). Hand-authored React UI
|
|
5
5
|
* component source is transformed at build/dev time to carry a `data-oid` on every
|
|
6
6
|
* JSX element, and an OID -> {file,line,col,component,tag} index is built so the
|
|
7
7
|
* editor can map a clicked element back to its exact source location.
|
|
@@ -168,7 +168,7 @@ interface SourceEdit {
|
|
|
168
168
|
const R3F_TRANSFORM_PROPS = ['position', 'rotation', 'scale'] as const;
|
|
169
169
|
|
|
170
170
|
/**
|
|
171
|
-
* Contracts for components
|
|
171
|
+
* Contracts for components volter does NOT own, keyed by package specifier and
|
|
172
172
|
* export name. The project resolver reads a local component's contract out of
|
|
173
173
|
* its own source; an installed package has no source to walk from the browser
|
|
174
174
|
* tier and no reason to expect one, so the few whose native root is part of the
|
|
@@ -1067,7 +1067,7 @@ export function sourceProvesR3f(evidence: SourceDialectEvidence): boolean {
|
|
|
1067
1067
|
* the HMR half of this same decision, so the two cannot drift apart by hand.
|
|
1068
1068
|
*
|
|
1069
1069
|
* 2. **The DEFAULT.** When `surface` is `undefined` — no region placed the
|
|
1070
|
-
* file (an ad-hoc file with no `
|
|
1070
|
+
* file (an ad-hoc file with no `volter.project.json` above it, or a module no
|
|
1071
1071
|
* region's import closure reaches and none declares) — the answer is
|
|
1072
1072
|
* `data-oid`, the documented default, and the CALLER says so out loud
|
|
1073
1073
|
* (`server/project-root-surface.ts`'s `OID001`/`OID002`). There is
|
|
@@ -1075,7 +1075,7 @@ export function sourceProvesR3f(evidence: SourceDialectEvidence): boolean {
|
|
|
1075
1075
|
* for a file it could not place, silently, and being wrong that way is
|
|
1076
1076
|
* indistinguishable from being right. Zero inference (ARCHITECTURE-CORE
|
|
1077
1077
|
* §The editor protocol) makes the fix a declaration — an `include` glob or
|
|
1078
|
-
* a `mounts` entry in `
|
|
1078
|
+
* a `mounts` entry in `volter.adapter.ts` — not a better heuristic.
|
|
1079
1079
|
*
|
|
1080
1080
|
* Nothing about a file's own bytes outranks the region — not even a file that
|
|
1081
1081
|
* renders THREE host elements while its region says `dom`. That disagreement is
|
|
@@ -1209,7 +1209,7 @@ function dreiHtmlLocalNames(sf: ts.SourceFile): Set<string> {
|
|
|
1209
1209
|
* `r3f-project-contracts.ts`'s `resolveRelativeModule` already draws between
|
|
1210
1210
|
* "source this editor walks" and "a dependency it never guesses about".
|
|
1211
1211
|
*
|
|
1212
|
-
* Why the callsite stamp must skip them: `
|
|
1212
|
+
* Why the callsite stamp must skip them: `__volterOid`/`__volterLabel` are a
|
|
1213
1213
|
* CONTRACT BETWEEN TWO HALVES OF THIS TRANSFORM. The callsite half writes
|
|
1214
1214
|
* them; the only thing that ever reads them is the callee half —
|
|
1215
1215
|
* `componentInstanceExpressions` destructuring them out of the component's own
|
|
@@ -1223,8 +1223,8 @@ function dreiHtmlLocalNames(sf: ts.SourceFile): Set<string> {
|
|
|
1223
1223
|
* MIXED file (an R3F `Scene` and a DOM `Hud` in one module, so the file's one
|
|
1224
1224
|
* dialect is `userData-oid`), and its `<Canvas>` — `@react-three/fiber`'s own,
|
|
1225
1225
|
* which spreads its rest props onto the `<div>` it wraps the canvas in — put
|
|
1226
|
-
* `
|
|
1227
|
-
* `React does not recognize the \`
|
|
1226
|
+
* `__volterOid` and `__volterLabel` straight into the DOM, for two permanent
|
|
1227
|
+
* `React does not recognize the \`__volterOid\` prop on a DOM element` errors on
|
|
1228
1228
|
* every mount of a real game. Nothing was lost by not stamping it, because
|
|
1229
1229
|
* nothing was reading it.
|
|
1230
1230
|
*
|
|
@@ -1285,7 +1285,7 @@ function dreiCameraLocalNames(sf: ts.SourceFile): Set<string> {
|
|
|
1285
1285
|
* tag's callee is unknowable at transform time, and the same hazard as
|
|
1286
1286
|
* {@link packageImportedLocalNames} for exactly the same reason.
|
|
1287
1287
|
*
|
|
1288
|
-
* `
|
|
1288
|
+
* `__volterOid`/`__volterLabel` are a contract between two halves of this
|
|
1289
1289
|
* transform: the callsite half writes them, the callee half destructures them
|
|
1290
1290
|
* out of the component's own parameters. A tag bound to a parameter names
|
|
1291
1291
|
* WHATEVER the caller passed, so this module cannot know whether the callee
|
|
@@ -1297,7 +1297,7 @@ function dreiCameraLocalNames(sf: ts.SourceFile): Set<string> {
|
|
|
1297
1297
|
* is a parameter — drei's `Stats`/`OrbitControls`, cannon's `Debug`, or a
|
|
1298
1298
|
* project component, depending on the call) and every R3F `*.stories.tsx`
|
|
1299
1299
|
* decorator's `<Story />`. Two standing
|
|
1300
|
-
* `React does not recognize the \`
|
|
1300
|
+
* `React does not recognize the \`__volterOid\` prop on a DOM element` errors on
|
|
1301
1301
|
* every run of the acceptance portfolio.
|
|
1302
1302
|
*
|
|
1303
1303
|
* Nothing is lost by not stamping, and identity actually IMPROVES: a forwarder
|
|
@@ -1437,42 +1437,42 @@ export function transformSource(
|
|
|
1437
1437
|
// Default the whole binding. Isolation/preview/bake call a stamped
|
|
1438
1438
|
// export as a factory (`MainScene()` / `WOOD()`) with no args; a
|
|
1439
1439
|
// required destructure of `undefined` is `Cannot destructure property
|
|
1440
|
-
// '
|
|
1441
|
-
sourceEdits.push({ pos: parameters.pos, text: '{
|
|
1442
|
-
return { instanceExpression: '
|
|
1440
|
+
// '__volterOid'`. React still passes `{}` at a real callsite.
|
|
1441
|
+
sourceEdits.push({ pos: parameters.pos, text: '{ __volterOid, __volterLabel } = {}' });
|
|
1442
|
+
return { instanceExpression: '__volterOid', labelExpression: '__volterLabel' };
|
|
1443
1443
|
}
|
|
1444
1444
|
if (ts.isObjectBindingPattern(first.name)) {
|
|
1445
1445
|
sourceEdits.push({
|
|
1446
1446
|
pos: first.name.getStart(sf) + 1,
|
|
1447
|
-
text: '
|
|
1447
|
+
text: '__volterOid, __volterLabel, ',
|
|
1448
1448
|
});
|
|
1449
1449
|
// After the WHOLE parameter, including a type annotation:
|
|
1450
|
-
// `({ name, ...props }: T)` must become `({
|
|
1450
|
+
// `({ name, ...props }: T)` must become `({ __volterOid, …, name, ...props }: T = {})`.
|
|
1451
1451
|
// Inserting at the binding's end produced `} = {}: T`, which is not
|
|
1452
1452
|
// TypeScript and broke every typed scene export the look verb imports.
|
|
1453
1453
|
if (!first.initializer) {
|
|
1454
1454
|
sourceEdits.push({ pos: first.getEnd(), text: ' = {}' });
|
|
1455
1455
|
}
|
|
1456
|
-
return { instanceExpression: '
|
|
1456
|
+
return { instanceExpression: '__volterOid', labelExpression: '__volterLabel' };
|
|
1457
1457
|
}
|
|
1458
1458
|
if (ts.isIdentifier(first.name) && !first.dotDotDotToken) {
|
|
1459
1459
|
// Preserve the authored identifier as the REST binding. Code below the
|
|
1460
1460
|
// parameter continues to read `props`, but it now sees only game props:
|
|
1461
1461
|
// `function Card(props: Props)` becomes
|
|
1462
|
-
// `function Card({
|
|
1462
|
+
// `function Card({ __volterOid, __volterLabel, ...props }: Props)`.
|
|
1463
1463
|
// Remove an optional marker while replacing the identifier; a binding
|
|
1464
1464
|
// pattern cannot itself be optional, so its equivalent is a `{}` default.
|
|
1465
1465
|
sourceEdits.push({
|
|
1466
1466
|
pos: first.name.getStart(sf),
|
|
1467
1467
|
end: first.questionToken?.end ?? first.name.getEnd(),
|
|
1468
|
-
text: `{
|
|
1468
|
+
text: `{ __volterOid, __volterLabel, ...${first.name.text} }`,
|
|
1469
1469
|
});
|
|
1470
1470
|
if (!first.initializer) {
|
|
1471
1471
|
sourceEdits.push({ pos: first.getEnd(), text: ' = {}' });
|
|
1472
1472
|
}
|
|
1473
|
-
return { instanceExpression: '
|
|
1473
|
+
return { instanceExpression: '__volterOid', labelExpression: '__volterLabel' };
|
|
1474
1474
|
}
|
|
1475
|
-
return { instanceExpression: '
|
|
1475
|
+
return { instanceExpression: '__volterOid', labelExpression: '__volterLabel' };
|
|
1476
1476
|
};
|
|
1477
1477
|
|
|
1478
1478
|
const pushComponent = (
|
|
@@ -1584,7 +1584,7 @@ export function transformSource(
|
|
|
1584
1584
|
const element = ts.isJsxOpeningElement(node) ? node.parent : node;
|
|
1585
1585
|
if (ts.isJsxElement(element) || ts.isJsxSelfClosingElement(element)) {
|
|
1586
1586
|
// An installed package's module never passes through this transform,
|
|
1587
|
-
// so the callee half of the `
|
|
1587
|
+
// so the callee half of the `__volterOid` contract never runs for it and
|
|
1588
1588
|
// the props can only escape (into the DOM, via any package component
|
|
1589
1589
|
// that spreads its rest props onto a host element). The callsite is
|
|
1590
1590
|
// still INDEXED above — the source entry, its parent and its contract
|
|
@@ -1619,11 +1619,11 @@ export function transformSource(
|
|
|
1619
1619
|
: tag;
|
|
1620
1620
|
const labelAttribute =
|
|
1621
1621
|
authoredName?.initializer && ts.isJsxExpression(authoredName.initializer)
|
|
1622
|
-
? `
|
|
1623
|
-
: `
|
|
1622
|
+
? `__volterLabel={${authoredName.initializer.expression?.getText(sf) ?? JSON.stringify(tag)}}`
|
|
1623
|
+
: `__volterLabel=${JSON.stringify(label)}`;
|
|
1624
1624
|
sourceEdits.push({
|
|
1625
1625
|
pos: node.attributes.end,
|
|
1626
|
-
text: `
|
|
1626
|
+
text: ` __volterOid="${oid}" ${labelAttribute}`,
|
|
1627
1627
|
});
|
|
1628
1628
|
}
|
|
1629
1629
|
} else {
|
|
@@ -188,7 +188,7 @@ export function planDeleteStory(source: string, name: string): CsfStoryPlan {
|
|
|
188
188
|
return {
|
|
189
189
|
ok: false,
|
|
190
190
|
reason:
|
|
191
|
-
`'${name}' is this file's declared defaultStory — point
|
|
191
|
+
`'${name}' is this file's declared defaultStory — point volter.defaultStory at another ` +
|
|
192
192
|
'story first',
|
|
193
193
|
};
|
|
194
194
|
}
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
* PURELY ADDITIVE. The channel these diagnostics have always had is untouched:
|
|
60
60
|
* `server/project-validation.ts`'s `validateSource` still formats every one of
|
|
61
61
|
* them into the dev server's terminal warning block and into the project
|
|
62
|
-
* warnings map that `/__editor/state` (and so `
|
|
62
|
+
* warnings map that `/__editor/state` (and so `volter-game-editor status`) reports. Rows are
|
|
63
63
|
* a second, better-placed audience, not a replacement.
|
|
64
64
|
*/
|
|
65
65
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** Exact source tokens for native R3F scene attachments.
|
|
2
2
|
*
|
|
3
|
-
* These are not a
|
|
3
|
+
* These are not a VOLTER environment format. `<color attach="background">`,
|
|
4
4
|
* `<fog attach="fog">`, and `<fogExp2 attach="fog">` are ordinary Fiber
|
|
5
5
|
* projections of THREE.Scene properties. The live Scene remains runtime truth;
|
|
6
6
|
* this metadata only identifies literal constructor arguments that can be
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
* Project-local R3F component contract discovery.
|
|
3
3
|
*
|
|
4
4
|
* This deliberately follows ordinary TypeScript/React modules instead of
|
|
5
|
-
* asking game source to register components with
|
|
5
|
+
* asking game source to register components with volter. Relative imports,
|
|
6
6
|
* named/default exports, aliases, and re-exports are enough for the editor to
|
|
7
7
|
* understand the shared-component shape coding models normally produce.
|
|
8
|
-
* External packages remain conservative:
|
|
8
|
+
* External packages remain conservative: volter never guesses at their runtime
|
|
9
9
|
* scene ownership.
|
|
10
10
|
*/
|
|
11
11
|
import { existsSync, readFileSync, statSync } from 'node:fs';
|
|
@@ -419,7 +419,7 @@ export function r3fAuthoringDiagnostics(
|
|
|
419
419
|
if (
|
|
420
420
|
/^[A-Z]/.test(tag) &&
|
|
421
421
|
contract?.root === 'single' &&
|
|
422
|
-
// A package component
|
|
422
|
+
// A package component volter knows only through the built-in table is not
|
|
423
423
|
// a hierarchy row of its own — `<RigidBody>` never receives the editor's
|
|
424
424
|
// stamp, so its props are attributed to the node it wraps
|
|
425
425
|
// (`collapsedWrappersOf`). Naming it would name nothing.
|