@volter/project 0.5.211 → 0.5.213
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 +2 -2
- package/README.md +8 -15
- package/package.json +5 -26
- package/schemas/volter-project.schema.json +33 -145
- package/schemas/volter-settings.schema.json +11 -49
- package/src/adapter/adapter-module.ts +208 -465
- package/src/adapter/adapter-surface.ts +4 -32
- package/src/adapter/authoring-seam-contract.ts +7 -24
- package/src/adapter/authoring.ts +126 -265
- package/src/adapter/editor-looks.ts +98 -127
- package/src/adapter/finders/registry.ts +6 -21
- package/src/adapter/host-context.ts +17 -72
- package/src/adapter/index.ts +4 -77
- package/src/adapter/manifest-interpreter.ts +7 -50
- package/src/adapter/physics-adapter-2d.ts +4 -21
- package/src/adapter/root-adapter.ts +37 -111
- package/src/adapter/root-seam-contract.ts +3 -6
- package/src/adapter/root-stacking.ts +4 -46
- package/src/adapter/seam-evidence.ts +0 -16
- package/src/adapter/system-adapter.ts +24 -656
- package/src/adapter/system-seam-contract.ts +1 -91
- package/src/adapter/transform.ts +4 -3
- package/src/adapter/workspace-arrangement.ts +33 -49
- package/src/manifest/editor-port.ts +10 -36
- package/src/manifest/filename.ts +3 -42
- package/src/manifest/load-file.ts +9 -15
- package/src/manifest/load.ts +44 -179
- package/src/manifest/locate.ts +8 -32
- package/src/manifest/schema.ts +155 -427
- package/src/semver.ts +16 -0
- package/src/settings/keys.ts +9 -11
- package/src/settings/schema.ts +55 -44
- package/schemas/engine-api.json +0 -57
- package/schemas/engine-api.md +0 -22
- package/src/adapter/binding.ts +0 -445
- package/src/adapter/debug-registry-ref.ts +0 -19
- package/src/adapter/frame-capture.ts +0 -80
- package/src/adapter/ingest/contract-system-slots.ts +0 -28
- package/src/adapter/ingest/game-contract-seams.ts +0 -86
- package/src/adapter/ingest/game-contract.ts +0 -506
- package/src/adapter/ingest/mount-readiness.ts +0 -140
- package/src/adapter/loop-gate-report.ts +0 -53
- package/src/adapter/native-entry-surface.ts +0 -100
- package/src/adapter/render-audio.ts +0 -20
- package/src/adapter/render-memory.ts +0 -34
- package/src/adapter/renderer-config.ts +0 -84
- package/src/adapter/system-slot.ts +0 -301
- package/src/core/system-phase.ts +0 -52
- package/src/inspection.ts +0 -281
- package/src/manifest/configuration-kinds.ts +0 -241
- package/src/manifest/kind-modules.ts +0 -58
- package/src/manifest/runtime-environment.ts +0 -48
package/src/manifest/load.ts
CHANGED
|
@@ -1,28 +1,10 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
// Pure data-in/data-out: `loadGameManifest(raw)` parses `raw` against
|
|
4
|
-
// `GameManifestSchema`, runs the cross-field checks §3 promises beyond what
|
|
5
|
-
// Zod alone can express, resolves every optional/derivable field, and
|
|
6
|
-
// returns a `ResolvedGameManifest` with nothing left implicit. This is the
|
|
7
|
-
// schema's runtime reader (T4.1 policy) — every described field in
|
|
8
|
-
// `schema.ts` either drives a check here or is carried into the resolved
|
|
9
|
-
// output explicitly (see the per-field comments below and
|
|
10
|
-
// `packages/engine/test/schema-consumption-map.ts`'s `manifest.*` entries).
|
|
11
|
-
//
|
|
12
|
-
// No `fs` import here on purpose — `load-file.ts` is the thin Node-only
|
|
1
|
+
// Parse and resolve project manifests without filesystem access. load-file.ts supplies the
|
|
2
|
+
// Node path wrapper; this reader validates cross-field constraints and resolves root defaults.
|
|
13
3
|
|
|
14
|
-
// wrapper that reads a path and calls this function, so browser bundles that
|
|
15
|
-
// only need `loadGameManifest` never pull in `node:fs`.
|
|
16
|
-
|
|
17
|
-
import {
|
|
18
|
-
configurationIssues,
|
|
19
|
-
configurationKind,
|
|
20
|
-
type DeclaredConfiguration,
|
|
21
|
-
} from './configuration-kinds';
|
|
22
4
|
import {
|
|
23
5
|
type AdapterRoot,
|
|
24
|
-
type
|
|
25
|
-
|
|
6
|
+
type ProjectManifest,
|
|
7
|
+
ProjectManifestSchema,
|
|
26
8
|
type LearnMetadata,
|
|
27
9
|
type RootAdapter,
|
|
28
10
|
} from './schema';
|
|
@@ -31,37 +13,28 @@ import {
|
|
|
31
13
|
// Resolved shapes
|
|
32
14
|
// ---------------------------------------------------------------------------
|
|
33
15
|
|
|
34
|
-
/**
|
|
16
|
+
/** Resolved adapter identities. */
|
|
35
17
|
export type ResolvedAdapter =
|
|
36
18
|
| {
|
|
37
19
|
readonly type: 'builtin';
|
|
38
|
-
readonly identity: '
|
|
39
|
-
readonly surface: '
|
|
20
|
+
readonly identity: 'stage' | 'canvas' | 'dom';
|
|
21
|
+
readonly surface: 'stage' | 'canvas' | 'dom';
|
|
40
22
|
}
|
|
41
23
|
| {
|
|
42
24
|
readonly type: 'module';
|
|
43
25
|
readonly identity: 'module';
|
|
44
26
|
readonly module: string;
|
|
45
|
-
readonly surface: '
|
|
27
|
+
readonly surface: 'stage' | 'canvas' | 'dom';
|
|
46
28
|
}
|
|
47
29
|
| {
|
|
48
30
|
readonly type: 'ingest';
|
|
49
|
-
//
|
|
50
|
-
|
|
51
|
-
readonly
|
|
52
|
-
readonly surface: 'three' | 'canvas' | 'dom';
|
|
31
|
+
// Named for the root's declared surface alone.
|
|
32
|
+
readonly identity: `ingest-${'stage' | 'canvas' | 'dom'}`;
|
|
33
|
+
readonly surface: 'stage' | 'canvas' | 'dom';
|
|
53
34
|
/**
|
|
54
|
-
* Project-relative ES module
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
* nothing to say about it, and requiring the key would only make every
|
|
58
|
-
* construction site say `undefined`.
|
|
59
|
-
*/
|
|
60
|
-
readonly contractShim?: string | undefined;
|
|
61
|
-
/**
|
|
62
|
-
* Project-relative ES module that writes this game'''s own DATA file back
|
|
63
|
-
* (see the field'''s schema description). Same OPTIONAL shape and same
|
|
64
|
-
* reason as {@link contractShim}: a game whose truth is entirely source
|
|
35
|
+
* Project-relative ES module that writes this project's own DATA file back
|
|
36
|
+
* (see the field's schema description). OPTIONAL rather than
|
|
37
|
+
* required-and-possibly-undefined: a project whose truth is entirely source
|
|
65
38
|
* declares nothing here.
|
|
66
39
|
*/
|
|
67
40
|
readonly dataWriter?: string | undefined;
|
|
@@ -77,9 +50,9 @@ export interface ResolvedAdapterRoot {
|
|
|
77
50
|
readonly adapter: ResolvedAdapter;
|
|
78
51
|
readonly entry: string | undefined;
|
|
79
52
|
/**
|
|
80
|
-
* The component this root's WORLD is, in the
|
|
53
|
+
* The component this root's WORLD is, in the project's own source (see the
|
|
81
54
|
* field's schema description). Present ⇒ Edit mounts THAT component alone as
|
|
82
|
-
* its design-time document and Play mounts the
|
|
55
|
+
* its design-time document and Play mounts the project through `entry`;
|
|
83
56
|
* absent ⇒ `entry` is what both modes mount.
|
|
84
57
|
*/
|
|
85
58
|
readonly world?: { readonly entry: string; readonly export: string | undefined } | undefined;
|
|
@@ -88,24 +61,15 @@ export interface ResolvedAdapterRoot {
|
|
|
88
61
|
readonly loop: 'gated' | 'self-driven';
|
|
89
62
|
}
|
|
90
63
|
|
|
91
|
-
export interface
|
|
92
|
-
readonly manifestVersion:
|
|
64
|
+
export interface ResolvedProjectManifest {
|
|
65
|
+
readonly manifestVersion: number;
|
|
93
66
|
readonly name: string;
|
|
94
67
|
readonly appId: string | undefined;
|
|
95
68
|
readonly version: string;
|
|
96
69
|
readonly engine: { readonly version: string };
|
|
97
|
-
/** Sorted by zOrder; ties broken by original array order
|
|
70
|
+
/** Sorted by zOrder; ties broken by original array order. */
|
|
98
71
|
readonly roots: readonly ResolvedAdapterRoot[];
|
|
99
|
-
/** The declared run configurations, validated by kind (`run-kinds.ts`). */
|
|
100
|
-
readonly configurations: readonly DeclaredConfiguration[];
|
|
101
|
-
/** Ids whose kind no registry here knows — only ever non-empty when the
|
|
102
|
-
* caller loaded with `configurationKinds: 'defer'` (a bundled host that
|
|
103
|
-
* cannot evaluate a project's kind contributions); the editor session
|
|
104
|
-
* is the validator for those. */
|
|
105
|
-
readonly deferredConfigurationKinds: readonly string[];
|
|
106
72
|
readonly resolution: { readonly width: number; readonly height: number } | undefined;
|
|
107
|
-
/** Construction-time renderer properties no world can declare for itself — see the schema. */
|
|
108
|
-
readonly rendering: { readonly antialias: boolean } | undefined;
|
|
109
73
|
readonly authoring:
|
|
110
74
|
| {
|
|
111
75
|
readonly hierarchy?:
|
|
@@ -122,32 +86,7 @@ export interface ResolvedGameManifest {
|
|
|
122
86
|
| undefined;
|
|
123
87
|
}
|
|
124
88
|
| undefined;
|
|
125
|
-
/**
|
|
126
|
-
readonly debug: { readonly allowInProduction: boolean } | undefined;
|
|
127
|
-
/** D15 — read by `mount-manifest.ts`'s boot-time seeding reader (gates
|
|
128
|
-
* whether `ctx.random` is seeded from `defaultSeed`/`?volter-seed=`), the
|
|
129
|
-
* gameplay-rng-ban burn-down scan, and the dev-mode Math.random phase
|
|
130
|
-
* trap.. */
|
|
131
|
-
readonly determinism:
|
|
132
|
-
| { readonly seededRandom: boolean; readonly defaultSeed?: number | undefined }
|
|
133
|
-
| undefined;
|
|
134
|
-
/** W3d (F11 perf gates) — read by `volter perf --assert-budget`
|
|
135
|
-
* (packages/volter-cli/src/perf.ts, assertBudget): each set metric is
|
|
136
|
-
* compared against the seeded headless perf run's measured value and any
|
|
137
|
-
* exceedance fails the gate with a per-metric table + nonzero exit. */
|
|
138
|
-
readonly budget:
|
|
139
|
-
| {
|
|
140
|
-
readonly frameCpuMsP95?: number | undefined;
|
|
141
|
-
readonly physicsMsP95?: number | undefined;
|
|
142
|
-
readonly maxEntities?: number | undefined;
|
|
143
|
-
readonly maxDrawCalls?: number | undefined;
|
|
144
|
-
readonly maxTriangles?: number | undefined;
|
|
145
|
-
}
|
|
146
|
-
| undefined;
|
|
147
|
-
/** Learner-facing metadata on shipped example projects; read by the editor
|
|
148
|
-
* server's registry surface (`GET /__editor/examples`,
|
|
149
|
-
* packages/editor-core/server/editor-server.ts) to generate the
|
|
150
|
-
* gallery/wizard/Learn-catalog registries. */
|
|
89
|
+
/** Learner-facing metadata carried from the project manifest. */
|
|
151
90
|
readonly learn: LearnMetadata | undefined;
|
|
152
91
|
}
|
|
153
92
|
|
|
@@ -164,37 +103,31 @@ function isModuleAdapter(
|
|
|
164
103
|
}
|
|
165
104
|
|
|
166
105
|
// ---------------------------------------------------------------------------
|
|
167
|
-
// Cross-field checks
|
|
106
|
+
// Cross-field checks beyond individual schema fields
|
|
168
107
|
// ---------------------------------------------------------------------------
|
|
169
108
|
//
|
|
170
|
-
//
|
|
171
|
-
//
|
|
172
|
-
//
|
|
173
|
-
// any other schema consumer see the same constraint the loader always
|
|
174
|
-
// enforced. `parseManifest` below catches that one recognizable schema-level
|
|
175
|
-
// issue and rethrows it as a clean, single `Error` — the exact message shape
|
|
176
|
-
// `checkUniqueRootIds` used to throw directly — rather than a raw multi-issue
|
|
177
|
-
// `ZodError` dump, so existing callers/tests that pattern-match the message
|
|
178
|
-
// text are unaffected by the enforcement moving one layer down.
|
|
109
|
+
// ProjectManifestSchema's `roots` superRefine checks composition rules, including root-id
|
|
110
|
+
// uniqueness. The maintained JSON schema validates field shapes; runtime loading applies these
|
|
111
|
+
// additional rules. loadProjectManifest surfaces composition issues as a single Error.
|
|
179
112
|
|
|
180
113
|
function checkAdapterEntryRules(root: AdapterRoot): void {
|
|
181
114
|
const { adapter } = root;
|
|
182
115
|
if (typeof adapter === 'string' && !root.entry) {
|
|
183
116
|
throw new Error(
|
|
184
|
-
`
|
|
117
|
+
`Project manifest: root "${root.id}" uses the '${adapter}' adapter but declares no ` +
|
|
185
118
|
'`entry` — a built-in adapter root is authored as an entry module.',
|
|
186
119
|
);
|
|
187
120
|
}
|
|
188
121
|
if (typeof adapter === 'object' && 'ingest' in adapter && !root.entry) {
|
|
189
122
|
throw new Error(
|
|
190
|
-
`
|
|
191
|
-
"
|
|
123
|
+
`Project manifest: root "${root.id}" is an { ingest } root but declares no \`entry\` — the ` +
|
|
124
|
+
"project's own entry module is what the host imports.",
|
|
192
125
|
);
|
|
193
126
|
}
|
|
194
127
|
}
|
|
195
128
|
|
|
196
129
|
// ---------------------------------------------------------------------------
|
|
197
|
-
// Resolution
|
|
130
|
+
// Resolution
|
|
198
131
|
// ---------------------------------------------------------------------------
|
|
199
132
|
|
|
200
133
|
function resolveAdapter(root: AdapterRoot): ResolvedAdapter {
|
|
@@ -213,20 +146,13 @@ function resolveAdapter(root: AdapterRoot): ResolvedAdapter {
|
|
|
213
146
|
};
|
|
214
147
|
}
|
|
215
148
|
|
|
216
|
-
// isIngestAdapter
|
|
217
|
-
|
|
218
|
-
const identity =
|
|
219
|
-
adapter.surface === 'three'
|
|
220
|
-
? 'ingest-three'
|
|
221
|
-
: adapter.surface === 'canvas'
|
|
222
|
-
? 'ingest-pixi'
|
|
223
|
-
: 'ingest-react';
|
|
149
|
+
// isIngestAdapter: the identity names the root's declared surface.
|
|
150
|
+
const identity = `ingest-${adapter.surface}` as const;
|
|
224
151
|
const ingest = adapter.ingest;
|
|
225
152
|
return {
|
|
226
153
|
type: 'ingest',
|
|
227
154
|
identity,
|
|
228
155
|
surface: adapter.surface,
|
|
229
|
-
contractShim: ingest.contractShim,
|
|
230
156
|
dataWriter: ingest.dataWriter,
|
|
231
157
|
assets: ingest.assets,
|
|
232
158
|
domStubs: ingest.domStubs,
|
|
@@ -237,28 +163,17 @@ function resolveAdapter(root: AdapterRoot): ResolvedAdapter {
|
|
|
237
163
|
function checkEngineVersionPin(version: string): void {
|
|
238
164
|
if (!SEMVER_RE.test(version)) {
|
|
239
165
|
throw new Error(
|
|
240
|
-
`
|
|
166
|
+
`Project manifest: engine.version "${version}" is not a valid exact semver string (e.g. ` +
|
|
241
167
|
'"0.1.0") — §1.5 requires an exact identity pin, not a range.',
|
|
242
168
|
);
|
|
243
169
|
}
|
|
244
170
|
}
|
|
245
171
|
|
|
246
172
|
/**
|
|
247
|
-
* Resolve
|
|
248
|
-
*
|
|
249
|
-
*
|
|
250
|
-
*
|
|
251
|
-
* `module`/`ingest.*` sub-fields) drives adapter identity
|
|
252
|
-
* resolution; `entry` drives the entry cross-field rule and is
|
|
253
|
-
* carried through; `world` is carried through for the ONE reader that mounts a
|
|
254
|
-
* design-time document (the editor's design session) and is deliberately not
|
|
255
|
-
* cross-checked against `entry` — the two name different modules on purpose;
|
|
256
|
-
* `zOrder` drives the final sort; `pausable`/`loop` are
|
|
257
|
-
* carried through as-is (their consumers are the runtime/CLI, T3.2/T3.3);
|
|
258
|
-
* `description` (T3.3
|
|
259
|
-
* slice 3) is carried through as-is — its consumer today is
|
|
260
|
-
* `resolveIngestDescriptor` (packages/editor-game/src/ingest/resolve-three.ts), which
|
|
261
|
-
* threads an ingest-three root's description into `IngestGame.description`.
|
|
173
|
+
* Resolve a validated root’s adapter identity and entry rules, carrying its
|
|
174
|
+
* remaining fields into the resolved root. `world` names the design-time module
|
|
175
|
+
* and is independent of the runtime `entry`. The manifest loader checks unique
|
|
176
|
+
* ids and sorts the resolved roots by z-order.
|
|
262
177
|
*/
|
|
263
178
|
function resolveRoot(root: AdapterRoot): ResolvedAdapterRoot {
|
|
264
179
|
checkAdapterEntryRules(root);
|
|
@@ -283,43 +198,25 @@ function resolveRoot(root: AdapterRoot): ResolvedAdapterRoot {
|
|
|
283
198
|
// ---------------------------------------------------------------------------
|
|
284
199
|
|
|
285
200
|
/**
|
|
286
|
-
* Parse + validate + resolve a raw manifest value into a `
|
|
201
|
+
* Parse + validate + resolve a raw manifest value into a `ResolvedProjectManifest`.
|
|
287
202
|
* Pure data-in/data-out (no `fs`) — see `load-file.ts` for the Node path-based
|
|
288
203
|
* wrapper. Throws a descriptive `Error` (naming the offending root/field) on
|
|
289
204
|
* any Zod validation failure or cross-field rule violation.
|
|
290
205
|
*/
|
|
291
|
-
export
|
|
292
|
-
|
|
293
|
-
* `defer`: it is reported in `deferredConfigurationKinds` instead. */
|
|
294
|
-
readonly configurationKinds?: 'require' | 'defer';
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
export function loadGameManifest(
|
|
298
|
-
raw: unknown,
|
|
299
|
-
options: LoadGameManifestOptions = {},
|
|
300
|
-
): ResolvedGameManifest {
|
|
301
|
-
// Removed-format guard FIRST, on the raw value: a root declaring a removed
|
|
302
|
-
// content field would otherwise fail Zod's `roots` discriminated union with
|
|
303
|
-
// a confusing shape error instead of the named migration message.
|
|
304
|
-
const parsed = GameManifestSchema.safeParse(raw);
|
|
206
|
+
export function loadProjectManifest(raw: unknown): ResolvedProjectManifest {
|
|
207
|
+
const parsed = ProjectManifestSchema.safeParse(raw);
|
|
305
208
|
if (!parsed.success) {
|
|
306
|
-
//
|
|
307
|
-
//
|
|
308
|
-
// schema-level violations this loader surfaces as a clean, single `Error`
|
|
309
|
-
// (matching `checkUniqueRootIds`'s pre-D-V6 message shape) rather than a
|
|
310
|
-
// raw multi-issue dump, because each one already reads as a finished
|
|
311
|
-
// sentence naming the offending ids. Every other schema violation keeps
|
|
312
|
-
// propagating as the raw `ZodError` it always has (unchanged behavior,
|
|
313
|
-
// e.g. an unrecognized key or a bad `manifestVersion`).
|
|
209
|
+
// Composition issues already name the offending root ids. Surface that
|
|
210
|
+
// message as a single Error; preserve other failures as ZodError details.
|
|
314
211
|
const compositionIssue = parsed.error.issues.find(
|
|
315
212
|
(issue) =>
|
|
316
|
-
issue.message.startsWith('
|
|
317
|
-
issue.message.startsWith('
|
|
213
|
+
issue.message.startsWith('Project manifest: duplicate root id') ||
|
|
214
|
+
issue.message.startsWith('Project manifest: a project has at most one'),
|
|
318
215
|
);
|
|
319
216
|
if (compositionIssue) throw new Error(compositionIssue.message);
|
|
320
217
|
throw parsed.error;
|
|
321
218
|
}
|
|
322
|
-
const manifest:
|
|
219
|
+
const manifest: ProjectManifest = parsed.data;
|
|
323
220
|
|
|
324
221
|
checkEngineVersionPin(manifest.engine.version);
|
|
325
222
|
|
|
@@ -329,37 +226,11 @@ export function loadGameManifest(
|
|
|
329
226
|
}));
|
|
330
227
|
|
|
331
228
|
resolvedWithIndex.sort(
|
|
332
|
-
(a, b) => a.root.zOrder - b.root.zOrder || a.index - b.index, // ties -> array order
|
|
229
|
+
(a, b) => a.root.zOrder - b.root.zOrder || a.index - b.index, // ties -> array order
|
|
333
230
|
);
|
|
334
231
|
|
|
335
232
|
const roots = resolvedWithIndex.map(({ root }) => root);
|
|
336
233
|
|
|
337
|
-
const configurations = (manifest.configurations ?? []) as DeclaredConfiguration[];
|
|
338
|
-
// Kinds are OPEN (ARCHITECTURE-CORE §The project model): each declaration
|
|
339
|
-
// is checked against its registered kind's own schema HERE, where the
|
|
340
|
-
// host's registry is, not in the Zod schema. A host that cannot evaluate a
|
|
341
|
-
// project's kind contributions defers unknown kinds by name instead of
|
|
342
|
-
// refusing them.
|
|
343
|
-
const deferredConfigurationKinds: string[] = [];
|
|
344
|
-
const kindIssues = configurationIssues(configurations).filter((issue) => {
|
|
345
|
-
if (options.configurationKinds !== 'defer') return true;
|
|
346
|
-
const unknown = /^configurations "([^"]+)": unknown kind/.exec(issue);
|
|
347
|
-
if (!unknown) return true;
|
|
348
|
-
deferredConfigurationKinds.push(unknown[1] as string);
|
|
349
|
-
return false;
|
|
350
|
-
});
|
|
351
|
-
if (kindIssues.length > 0) {
|
|
352
|
-
throw new Error(`Game manifest: ${kindIssues.join('\n ')}`);
|
|
353
|
-
}
|
|
354
|
-
for (const configuration of configurations) {
|
|
355
|
-
if (
|
|
356
|
-
!configurationKind(configuration.kind) &&
|
|
357
|
-
!deferredConfigurationKinds.includes(configuration.id)
|
|
358
|
-
) {
|
|
359
|
-
deferredConfigurationKinds.push(configuration.id);
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
|
|
363
234
|
return {
|
|
364
235
|
manifestVersion: manifest.manifestVersion,
|
|
365
236
|
name: manifest.name,
|
|
@@ -367,14 +238,8 @@ export function loadGameManifest(
|
|
|
367
238
|
version: manifest.version,
|
|
368
239
|
engine: { version: manifest.engine.version },
|
|
369
240
|
roots,
|
|
370
|
-
configurations,
|
|
371
|
-
deferredConfigurationKinds,
|
|
372
241
|
resolution: manifest.resolution,
|
|
373
|
-
rendering: manifest.rendering,
|
|
374
242
|
authoring: manifest.authoring,
|
|
375
|
-
debug: manifest.debug,
|
|
376
|
-
determinism: manifest.determinism,
|
|
377
|
-
budget: manifest.budget,
|
|
378
243
|
learn: manifest.learn,
|
|
379
244
|
};
|
|
380
245
|
}
|
package/src/manifest/locate.ts
CHANGED
|
@@ -1,51 +1,27 @@
|
|
|
1
|
-
// Finding the project manifest ON DISK. There is ONE filename
|
|
2
|
-
// `./filename.ts` for why the old one was removed rather than kept readable.
|
|
1
|
+
// Finding the project manifest ON DISK. There is ONE filename (`./filename.ts`).
|
|
3
2
|
//
|
|
4
3
|
// Deliberately dependency-light — `node:fs` + `node:path` + `./filename` and
|
|
5
|
-
// NOTHING else (no Zod, no `./load`).
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
// manifest without dragging the validator in.
|
|
4
|
+
// NOTHING else (no Zod, no `./load`). Its callers (the editor's server, the
|
|
5
|
+
// live session and a product's CLI) locate a manifest without dragging the
|
|
6
|
+
// validator in.
|
|
9
7
|
|
|
10
8
|
import { existsSync } from 'node:fs';
|
|
11
9
|
import { join } from 'node:path';
|
|
12
|
-
import {
|
|
13
|
-
MANIFEST_FILENAME,
|
|
14
|
-
REMOVED_MANIFEST_FILENAME,
|
|
15
|
-
removedManifestFilenameMessage,
|
|
16
|
-
} from './filename';
|
|
10
|
+
import { MANIFEST_FILENAME } from './filename';
|
|
17
11
|
|
|
18
12
|
/**
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* naming the exact fix, never be silently ignored (which would present as
|
|
22
|
-
* "this folder is not a project" — a diagnosis pointing nowhere).
|
|
23
|
-
*
|
|
24
|
-
* Returns normally when `dir` holds the current manifest, or neither file.
|
|
25
|
-
*/
|
|
26
|
-
export function assertNoRemovedManifestFilename(dir: string): void {
|
|
27
|
-
if (existsSync(join(dir, MANIFEST_FILENAME))) return;
|
|
28
|
-
if (!existsSync(join(dir, REMOVED_MANIFEST_FILENAME))) return;
|
|
29
|
-
throw new Error(removedManifestFilenameMessage(join(dir, REMOVED_MANIFEST_FILENAME)));
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* The manifest path inside `dir`. Always `volter.project.json` — so a "not
|
|
34
|
-
* found" message names the filename a project must be using — except that a
|
|
35
|
-
* directory carrying only the removed `volter.game.json` THROWS the rename
|
|
36
|
-
* instruction instead of reporting a phantom path.
|
|
13
|
+
* The manifest path inside `dir`: always `volter.project.json`, so a "not
|
|
14
|
+
* found" message names the filename a project must be using.
|
|
37
15
|
*
|
|
38
16
|
* This is the drop-in replacement for a hand-written `join(dir, …)` everywhere
|
|
39
17
|
* a manifest is READ. Writers want {@link manifestWritePath}.
|
|
40
18
|
*/
|
|
41
19
|
export function resolveManifestPath(dir: string): string {
|
|
42
|
-
assertNoRemovedManifestFilename(dir);
|
|
43
20
|
return join(dir, MANIFEST_FILENAME);
|
|
44
21
|
}
|
|
45
22
|
|
|
46
|
-
/** True when `dir` holds a manifest.
|
|
23
|
+
/** True when `dir` holds a manifest. */
|
|
47
24
|
export function hasManifest(dir: string): boolean {
|
|
48
|
-
assertNoRemovedManifestFilename(dir);
|
|
49
25
|
return existsSync(join(dir, MANIFEST_FILENAME));
|
|
50
26
|
}
|
|
51
27
|
|