@vgai/engine 0.5.41 → 0.5.44

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. package/dist/adapter/adapter-module.d.ts +28 -29
  2. package/dist/adapter/adapter-module.d.ts.map +1 -1
  3. package/dist/adapter/adapter-module.js +11 -34
  4. package/dist/adapter/authoring-seam-contract.d.ts +10 -0
  5. package/dist/adapter/authoring-seam-contract.d.ts.map +1 -1
  6. package/dist/adapter/authoring-seam-contract.js +2 -0
  7. package/dist/adapter/authoring.d.ts +31 -0
  8. package/dist/adapter/authoring.d.ts.map +1 -1
  9. package/dist/adapter/finders/finder-result.d.ts +2 -2
  10. package/dist/adapter/finders/finder-result.d.ts.map +1 -1
  11. package/dist/adapter/finders/index.d.ts +15 -2
  12. package/dist/adapter/finders/index.d.ts.map +1 -1
  13. package/dist/adapter/finders/index.js +38 -10
  14. package/dist/adapter/finders/prefabs-from-stories.d.ts.map +1 -1
  15. package/dist/adapter/finders/registry.d.ts +45 -0
  16. package/dist/adapter/finders/registry.d.ts.map +1 -0
  17. package/dist/adapter/finders/registry.js +56 -0
  18. package/dist/adapter/ingest/scene-capture.d.ts.map +1 -1
  19. package/dist/adapter/ingest/scene-capture.js +50 -20
  20. package/dist/adapter/system-adapter.d.ts +3 -1
  21. package/dist/adapter/system-adapter.d.ts.map +1 -1
  22. package/dist/canvas-react/pixi-react-root-factory.d.ts.map +1 -1
  23. package/dist/canvas-react/pixi-react-root-factory.js +21 -11
  24. package/dist/data/vite-plugin-data.d.ts +1 -1
  25. package/dist/data/vite-plugin-data.d.ts.map +1 -1
  26. package/dist/data/vite-plugin-data.js +2 -2
  27. package/dist/ecs/user-data.d.ts +6 -0
  28. package/dist/ecs/user-data.d.ts.map +1 -1
  29. package/dist/ecs/user-data.js +4 -0
  30. package/dist/index.d.ts +1 -1
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +1 -1
  33. package/dist/input/host-pointer.d.ts +18 -0
  34. package/dist/input/host-pointer.d.ts.map +1 -1
  35. package/dist/input/host-pointer.js +54 -5
  36. package/dist/input/input-manager.d.ts +83 -1
  37. package/dist/input/input-manager.d.ts.map +1 -1
  38. package/dist/input/input-manager.js +357 -8
  39. package/dist/manifest/configuration-kinds.d.ts +97 -0
  40. package/dist/manifest/configuration-kinds.d.ts.map +1 -0
  41. package/dist/manifest/configuration-kinds.js +148 -0
  42. package/dist/manifest/index.d.ts +2 -1
  43. package/dist/manifest/index.d.ts.map +1 -1
  44. package/dist/manifest/index.js +4 -3
  45. package/dist/manifest/kind-modules.d.ts +18 -0
  46. package/dist/manifest/kind-modules.d.ts.map +1 -0
  47. package/dist/manifest/kind-modules.js +58 -0
  48. package/dist/manifest/load-file.d.ts +3 -3
  49. package/dist/manifest/load-file.d.ts.map +1 -1
  50. package/dist/manifest/load-file.js +4 -4
  51. package/dist/manifest/load.d.ts +17 -12
  52. package/dist/manifest/load.d.ts.map +1 -1
  53. package/dist/manifest/load.js +29 -19
  54. package/dist/manifest/runtime-environment.d.ts +11 -0
  55. package/dist/manifest/runtime-environment.d.ts.map +1 -0
  56. package/dist/manifest/runtime-environment.js +44 -0
  57. package/dist/manifest/schema.d.ts +10 -9
  58. package/dist/manifest/schema.d.ts.map +1 -1
  59. package/dist/manifest/schema.js +32 -27
  60. package/dist/react/use-data.d.ts +1 -1
  61. package/dist/react/use-data.js +1 -1
  62. package/dist/render/environment-capture.d.ts +10 -1
  63. package/dist/render/environment-capture.d.ts.map +1 -1
  64. package/dist/render/environment-capture.js +25 -0
  65. package/dist/render/ibl-override-material.d.ts +11 -2
  66. package/dist/render/ibl-override-material.d.ts.map +1 -1
  67. package/dist/render/ibl-override-material.js +16 -2
  68. package/dist/render/matcap-texture.d.ts +5 -0
  69. package/dist/render/matcap-texture.d.ts.map +1 -0
  70. package/dist/render/matcap-texture.js +82 -0
  71. package/dist/render/soft-particle-depth.d.ts +14 -8
  72. package/dist/render/soft-particle-depth.d.ts.map +1 -1
  73. package/dist/render/soft-particle-depth.js +37 -14
  74. package/dist/render/viewport-shading.d.ts +11 -1
  75. package/dist/render/viewport-shading.d.ts.map +1 -1
  76. package/dist/render/viewport-shading.js +25 -0
  77. package/dist/runtime/create-runtime.js +6 -5
  78. package/dist/runtime/debug-bridge.d.ts.map +1 -1
  79. package/dist/runtime/debug-bridge.js +17 -3
  80. package/dist/runtime/mount-manifest.d.ts.map +1 -1
  81. package/dist/runtime/mount-manifest.js +11 -2
  82. package/dist/settings/schema.d.ts +99 -0
  83. package/dist/settings/schema.d.ts.map +1 -0
  84. package/dist/settings/schema.js +110 -0
  85. package/dist/world3d-react/r3f-root-factory.d.ts.map +1 -1
  86. package/dist/world3d-react/r3f-root-factory.js +34 -36
  87. package/dist/world3d-react/rapier-physics-bridge.d.ts.map +1 -1
  88. package/dist/world3d-react/rapier-physics-bridge.js +21 -6
  89. package/dist-config/config.js +1 -1
  90. package/dist-config/config.js.map +2 -2
  91. package/package.json +2 -1
  92. package/schemas/engine-capabilities.json +4 -4
  93. package/schemas/vgai-project.schema.json +41 -36
  94. package/schemas/vgai-settings.schema.json +112 -0
  95. package/src/adapter/adapter-module.ts +33 -51
  96. package/src/adapter/authoring-seam-contract.ts +2 -0
  97. package/src/adapter/authoring.ts +31 -0
  98. package/src/adapter/finders/finder-result.ts +2 -2
  99. package/src/adapter/finders/index.ts +68 -12
  100. package/src/adapter/finders/prefabs-from-stories.ts +2 -2
  101. package/src/adapter/finders/registry.ts +95 -0
  102. package/src/adapter/finders/scenes-from-entrypoint-selection.ts +4 -4
  103. package/src/adapter/ingest/scene-capture.ts +48 -19
  104. package/src/adapter/system-adapter.ts +3 -1
  105. package/src/canvas-react/pixi-react-root-factory.tsx +21 -11
  106. package/src/data/vite-plugin-data.ts +4 -2
  107. package/src/ecs/user-data.ts +6 -0
  108. package/src/index.ts +1 -1
  109. package/src/input/host-pointer.ts +84 -5
  110. package/src/input/input-manager.ts +404 -7
  111. package/src/manifest/configuration-kinds.ts +241 -0
  112. package/src/manifest/index.ts +4 -3
  113. package/src/manifest/kind-modules.ts +58 -0
  114. package/src/manifest/load-file.ts +11 -5
  115. package/src/manifest/load.ts +53 -22
  116. package/src/manifest/runtime-environment.ts +48 -0
  117. package/src/manifest/schema.ts +39 -33
  118. package/src/react/use-data.ts +1 -1
  119. package/src/render/environment-capture.ts +30 -1
  120. package/src/render/ibl-override-material.ts +32 -2
  121. package/src/render/matcap-texture.ts +92 -0
  122. package/src/render/soft-particle-depth.ts +43 -14
  123. package/src/render/viewport-shading.ts +33 -1
  124. package/src/runtime/create-runtime.ts +6 -5
  125. package/src/runtime/debug-bridge.ts +21 -3
  126. package/src/runtime/mount-manifest.ts +13 -2
  127. package/src/settings/schema.ts +127 -0
  128. package/src/world3d-react/r3f-root-factory.tsx +45 -39
  129. package/src/world3d-react/rapier-physics-bridge.tsx +22 -8
@@ -0,0 +1,97 @@
1
+ /**
2
+ * THE RUN-CONFIGURATION KIND REGISTRY — the open set of kinds a manifest's
3
+ * `configurations[]` may declare (ARCHITECTURE-CORE §The project model). A run
4
+ * configuration is one of the project's ENTRYPOINTS: something the editor,
5
+ * the CLI or a harness can start by id. The engine registers two kinds
6
+ * here; a capability registers more through the same door; the manifest
7
+ * validates each declaration against its kind's own schema at load, and an
8
+ * unregistered kind refuses loudly naming the registered ones.
9
+ *
10
+ * The implicit configuration every project with roots has is `play` — mount
11
+ * every root in the host — which is never declared and may be named by a
12
+ * `compound`.
13
+ */
14
+ import { type ZodType } from 'zod';
15
+ /** The envelope every declaration carries; the rest is the kind's. */
16
+ export interface ConfigurationEnvelope {
17
+ readonly id: string;
18
+ readonly kind: string;
19
+ }
20
+ export type ConfigurationRole = 'run' | 'build';
21
+ export interface ConfigurationKindRegistration<T extends ConfigurationEnvelope = ConfigurationEnvelope> {
22
+ readonly kind: T['kind'];
23
+ /** Where a configuration of this kind surfaces and which verb starts it:
24
+ * `run` (the transport, `vgai run`) or `build` (Export, `vgai build`). */
25
+ readonly role: ConfigurationRole;
26
+ /** The declaration's full shape, envelope included; `.strict()`. */
27
+ readonly schema: ZodType<T>;
28
+ /** One line for schema output and refusals. */
29
+ readonly describe: string;
30
+ /** A build-role kind backed by a project TOOL (the operation catalog):
31
+ * the host runs `tool` with `input(configuration)` and reports its
32
+ * outcome as the build's. This is how a capability's bake becomes a
33
+ * build without the engine knowing what a bake is. */
34
+ readonly build?: {
35
+ readonly tool: string;
36
+ readonly input: (configuration: T) => unknown;
37
+ };
38
+ }
39
+ /**
40
+ * What a CONTRIBUTION exports to register a kind — a plain object, so a
41
+ * project module (evaluated with the project's own copy of the engine)
42
+ * hands it to the HOST, which registers it in the host's registry. A module
43
+ * `src/contributions/<name>.kind.ts` exports it as `kind`.
44
+ */
45
+ export type ConfigurationKindContribution = ConfigurationKindRegistration;
46
+ /** Register a contribution's kind after checking its shape by name. */
47
+ export declare function registerContributedConfigurationKind(value: unknown, source: string): () => void;
48
+ /** Register a kind. A second registration replaces the first (a module
49
+ * re-evaluated on save); never throws. */
50
+ export declare function registerConfigurationKind<T extends ConfigurationEnvelope>(registration: ConfigurationKindRegistration<T>): () => void;
51
+ export declare function registeredConfigurationKinds(): readonly string[];
52
+ export declare function configurationKind(kind: string): ConfigurationKindRegistration | undefined;
53
+ /** The id of the implicit configuration: mount every root in the host. */
54
+ export declare const PLAY_CONFIGURATION_ID = "play";
55
+ /** A process the project runs beside the host: an entry module started
56
+ * with the project's own toolchain, listening on a port when it has one. */
57
+ export interface ProcessConfiguration extends ConfigurationEnvelope {
58
+ readonly kind: 'process';
59
+ /** Project-relative entry module (`server/main.ts`). */
60
+ readonly entry: string;
61
+ /** The command that starts it, run in a shell at `cwd`; `npx tsx <entry>`
62
+ * when omitted. Declare it when the entry needs its own flags. */
63
+ readonly command?: string;
64
+ /** The port it listens on, when readiness means a port. */
65
+ readonly port?: number;
66
+ /** Project-relative working directory; the project root when omitted. */
67
+ readonly cwd?: string;
68
+ readonly env?: Readonly<Record<string, string>>;
69
+ /** How a runner knows it is up: the port accepting connections (the
70
+ * default when `port` is set), or an HTTP path answering 2xx on it. */
71
+ readonly ready?: {
72
+ readonly http?: string;
73
+ };
74
+ }
75
+ /** Several configurations started together, `play` included by name. */
76
+ export interface CompoundConfiguration extends ConfigurationEnvelope {
77
+ readonly kind: 'compound';
78
+ readonly run: readonly string[];
79
+ /** How many instances of the host mount to run against the rest (the
80
+ * editor's Instances picker is this parameter). */
81
+ readonly instances?: number;
82
+ }
83
+ /** A packaged build of the project's own web bundle: the project's build
84
+ * script, then `dist/` packaged as the web artifact. The first build-role
85
+ * kind; a capability registers more (a bake, a static site). */
86
+ export interface BundleConfiguration extends ConfigurationEnvelope {
87
+ readonly kind: 'bundle';
88
+ /** The package.json script that builds (`build`). */
89
+ readonly script?: string;
90
+ /** Extra arguments after `--` (`['--base', './']` for an itch-style relative bundle). */
91
+ readonly args?: readonly string[];
92
+ }
93
+ export type DeclaredConfiguration = ConfigurationEnvelope & Readonly<Record<string, unknown>>;
94
+ /** Validate `configurations[]` against the registry: every kind registered, every
95
+ * declaration its kind's shape, ids unique, compound members declared. */
96
+ export declare function configurationIssues(configurations: readonly ConfigurationEnvelope[]): string[];
97
+ //# sourceMappingURL=configuration-kinds.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"configuration-kinds.d.ts","sourceRoot":"","sources":["../../src/manifest/configuration-kinds.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,148 @@
1
+ /**
2
+ * THE RUN-CONFIGURATION KIND REGISTRY — the open set of kinds a manifest's
3
+ * `configurations[]` may declare (ARCHITECTURE-CORE §The project model). A run
4
+ * configuration is one of the project's ENTRYPOINTS: something the editor,
5
+ * the CLI or a harness can start by id. The engine registers two kinds
6
+ * here; a capability registers more through the same door; the manifest
7
+ * validates each declaration against its kind's own schema at load, and an
8
+ * unregistered kind refuses loudly naming the registered ones.
9
+ *
10
+ * The implicit configuration every project with roots has is `play` — mount
11
+ * every root in the host — which is never declared and may be named by a
12
+ * `compound`.
13
+ */
14
+ import { z } from 'zod';
15
+ const CONFIGURATION_ROLES = ['run', 'build'];
16
+ /** Register a contribution's kind after checking its shape by name. */
17
+ export function registerContributedConfigurationKind(value, source) {
18
+ const record = (value ?? {});
19
+ const problems = [];
20
+ if (typeof record.kind !== 'string' || record.kind.length === 0)
21
+ problems.push('`kind` must be a non-empty string');
22
+ if (!CONFIGURATION_ROLES.includes(record.role))
23
+ problems.push("`role` must be 'run' or 'build'");
24
+ if (typeof record.describe !== 'string')
25
+ problems.push('`describe` must be a string');
26
+ const schema = record.schema;
27
+ if (!schema || typeof schema.safeParse !== 'function')
28
+ problems.push('`schema` must be a Zod schema');
29
+ const build = record.build;
30
+ if (build !== undefined &&
31
+ (typeof build.tool !== 'string' || typeof build.input !== 'function')) {
32
+ problems.push('`build` must be `{ tool: string; input(configuration) }`');
33
+ }
34
+ if (problems.length > 0) {
35
+ throw new Error(`${source}: not a configuration kind — ${problems.join('; ')}`);
36
+ }
37
+ return registerConfigurationKind(record);
38
+ }
39
+ const registry = new Map();
40
+ /** Register a kind. A second registration replaces the first (a module
41
+ * re-evaluated on save); never throws. */
42
+ export function registerConfigurationKind(registration) {
43
+ const entry = registration;
44
+ registry.set(registration.kind, entry);
45
+ return () => {
46
+ if (registry.get(registration.kind) === entry)
47
+ registry.delete(registration.kind);
48
+ };
49
+ }
50
+ export function registeredConfigurationKinds() {
51
+ return [...registry.keys()];
52
+ }
53
+ export function configurationKind(kind) {
54
+ return registry.get(kind);
55
+ }
56
+ /** The id of the implicit configuration: mount every root in the host. */
57
+ export const PLAY_CONFIGURATION_ID = 'play';
58
+ registerConfigurationKind({
59
+ kind: 'process',
60
+ role: 'run',
61
+ describe: 'A process beside the host: an entry module, a port, readiness',
62
+ schema: z
63
+ .object({
64
+ id: z.string().min(1).describe('Configuration id'),
65
+ kind: z.literal('process'),
66
+ entry: z.string().min(1).describe('Project-relative entry module'),
67
+ command: z
68
+ .string()
69
+ .min(1)
70
+ .optional()
71
+ .describe('Shell command that starts it; `npx tsx <entry>` when omitted'),
72
+ port: z.number().int().min(1).max(65535).optional().describe('Port it listens on'),
73
+ cwd: z.string().min(1).optional().describe('Project-relative working directory'),
74
+ env: z.record(z.string(), z.string()).optional().describe('Environment for the process'),
75
+ ready: z
76
+ .object({ http: z.string().min(1).optional().describe('HTTP path answering 2xx when up') })
77
+ .strict()
78
+ .optional()
79
+ .describe('Readiness beyond the port accepting connections'),
80
+ })
81
+ .strict(),
82
+ });
83
+ registerConfigurationKind({
84
+ kind: 'compound',
85
+ role: 'run',
86
+ describe: 'Several configurations started together; `play` names the host mount',
87
+ schema: z
88
+ .object({
89
+ id: z.string().min(1).describe('Configuration id'),
90
+ kind: z.literal('compound'),
91
+ run: z.array(z.string().min(1)).min(1).describe('Configuration ids, `play` included'),
92
+ instances: z
93
+ .number()
94
+ .int()
95
+ .min(1)
96
+ .optional()
97
+ .describe('Instances of the host mount to run against the rest'),
98
+ })
99
+ .strict(),
100
+ });
101
+ registerConfigurationKind({
102
+ kind: 'bundle',
103
+ role: 'build',
104
+ describe: "The project's web bundle: its build script, then dist/ packaged as the artifact",
105
+ schema: z
106
+ .object({
107
+ id: z.string().min(1).describe('Configuration id'),
108
+ kind: z.literal('bundle'),
109
+ script: z.string().min(1).optional().describe('package.json script that builds (`build`)'),
110
+ args: z.array(z.string()).optional().describe('Arguments passed after `--`'),
111
+ })
112
+ .strict(),
113
+ });
114
+ /** Validate `configurations[]` against the registry: every kind registered, every
115
+ * declaration its kind's shape, ids unique, compound members declared. */
116
+ export function configurationIssues(configurations) {
117
+ const issues = [];
118
+ const ids = new Set();
119
+ for (const declaration of configurations) {
120
+ if (ids.has(declaration.id))
121
+ issues.push(`configurations: duplicate id "${declaration.id}"`);
122
+ ids.add(declaration.id);
123
+ const registration = registry.get(declaration.kind);
124
+ if (!registration) {
125
+ issues.push(`configurations "${declaration.id}": unknown kind "${declaration.kind}" — registered: ${registeredConfigurationKinds()
126
+ .map((k) => `"${k}"`)
127
+ .join(', ')}`);
128
+ continue;
129
+ }
130
+ const parsed = registration.schema.safeParse(declaration);
131
+ if (!parsed.success) {
132
+ for (const issue of parsed.error.issues) {
133
+ issues.push(`configurations "${declaration.id}" (${declaration.kind}): ${issue.path.join('.') || '(declaration)'}: ${issue.message}`);
134
+ }
135
+ }
136
+ }
137
+ for (const declaration of configurations) {
138
+ if (declaration.kind !== 'compound')
139
+ continue;
140
+ const members = declaration.run ?? [];
141
+ for (const member of members) {
142
+ if (member !== PLAY_CONFIGURATION_ID && !ids.has(member)) {
143
+ issues.push(`configurations "${declaration.id}": member "${member}" is not a declared configuration (or "${PLAY_CONFIGURATION_ID}")`);
144
+ }
145
+ }
146
+ }
147
+ return issues;
148
+ }
@@ -1,6 +1,7 @@
1
+ export * from './configuration-kinds';
1
2
  export { isManifestFilename, MANIFEST_FILENAME, REMOVED_MANIFEST_FILENAME, removedManifestFilenameMessage, } from './filename';
2
3
  export type { ResolvedAdapter, ResolvedAdapterRoot, ResolvedGameManifest } from './load';
3
- export { DEFAULT_SERVER_MODULE, loadGameManifest } from './load';
4
+ export { loadGameManifest } from './load';
4
5
  export type { AdapterRoot, GameManifest, RootAdapter } from './schema';
5
6
  export { AdapterRootSchema, GameManifestSchema, RootAdapterSchema } from './schema';
6
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/manifest/index.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,yBAAyB,EACzB,8BAA8B,GAC/B,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,eAAe,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,QAAQ,CAAC;AACzF,OAAO,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,QAAQ,CAAC;AACjE,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvE,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/manifest/index.ts"],"names":[],"mappings":"AAGA,cAAc,uBAAuB,CAAC;AACtC,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,yBAAyB,EACzB,8BAA8B,GAC/B,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,eAAe,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,QAAQ,CAAC;AACzF,OAAO,EAAE,gBAAgB,EAAE,MAAM,QAAQ,CAAC;AAC1C,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvE,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC"}
@@ -1,9 +1,10 @@
1
1
  // Game manifest (vgai.project.json) — schema + loader (T3.1 slice
2
2
  // 1)..
3
+ export * from './configuration-kinds';
3
4
  export { isManifestFilename, MANIFEST_FILENAME, REMOVED_MANIFEST_FILENAME, removedManifestFilenameMessage, } from './filename';
4
- export { DEFAULT_SERVER_MODULE, loadGameManifest } from './load';
5
+ export { loadGameManifest } from './load';
5
6
  export { AdapterRootSchema, GameManifestSchema, RootAdapterSchema } from './schema';
6
- // `loadGameManifestFile` (Node-only, reads `path` via `node:fs`) is
7
+ // File loading and kind-module discovery read `node:fs` and are
7
8
  // deliberately NOT re-exported here — this barrel must stay import-safe for
8
9
  // browser bundles. Import it directly from `@engine/manifest/load-file` in
9
- // Node-only contexts (CLI, tests).
10
+ // Node-only contexts; kind discovery lives in `@engine/manifest/kind-modules`.
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Where a project's CONFIGURATION-KIND CONTRIBUTIONS live and how a host
3
+ * takes them in (ARCHITECTURE-CORE §The project model). A module named
4
+ * `src/contributions/<name>.kind.ts` (or `.tsx`) exports `kind`, a plain
5
+ * `ConfigurationKindContribution`; the HOST that loads the manifest — the
6
+ * editor server through Vite's SSR loader, a project's own validate script
7
+ * through tsx — imports each and registers it in its own registry through
8
+ * {@link registerContributedConfigurationKind}. Node-only: it walks the
9
+ * project folder.
10
+ */
11
+ /** Whether one file name is a kind contribution by the convention. */
12
+ export declare function isConfigurationKindModule(fileName: string): boolean;
13
+ /** Absolute paths of the project's kind contributions, sorted. */
14
+ export declare function contributedKindModulePaths(projectDir: string): string[];
15
+ /** Register the `kind` export of an already-imported module; the source
16
+ * path names the module in a refusal. */
17
+ export declare function registerKindModule(module: unknown, source: string): () => void;
18
+ //# sourceMappingURL=kind-modules.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kind-modules.d.ts","sourceRoot":"","sources":["../../src/manifest/kind-modules.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAQH,sEAAsE;AACtE,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAEnE;AAED,kEAAkE;AAClE,wBAAgB,0BAA0B,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,CAwBvE;AAED;0CAC0C;AAC1C,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,IAAI,CAM9E"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Where a project's CONFIGURATION-KIND CONTRIBUTIONS live and how a host
3
+ * takes them in (ARCHITECTURE-CORE §The project model). A module named
4
+ * `src/contributions/<name>.kind.ts` (or `.tsx`) exports `kind`, a plain
5
+ * `ConfigurationKindContribution`; the HOST that loads the manifest — the
6
+ * editor server through Vite's SSR loader, a project's own validate script
7
+ * through tsx — imports each and registers it in its own registry through
8
+ * {@link registerContributedConfigurationKind}. Node-only: it walks the
9
+ * project folder.
10
+ */
11
+ import { readdirSync, statSync } from 'node:fs';
12
+ import { join } from 'node:path';
13
+ import { registerContributedConfigurationKind } from './configuration-kinds';
14
+ const KIND_SUFFIXES = ['.kind.ts', '.kind.tsx'];
15
+ /** Whether one file name is a kind contribution by the convention. */
16
+ export function isConfigurationKindModule(fileName) {
17
+ return KIND_SUFFIXES.some((suffix) => fileName.endsWith(suffix));
18
+ }
19
+ /** Absolute paths of the project's kind contributions, sorted. */
20
+ export function contributedKindModulePaths(projectDir) {
21
+ const found = [];
22
+ const walk = (directory) => {
23
+ let names;
24
+ try {
25
+ names = readdirSync(directory);
26
+ }
27
+ catch {
28
+ return;
29
+ }
30
+ for (const name of names) {
31
+ if (name.startsWith('.') || name === 'node_modules')
32
+ continue;
33
+ const absolute = join(directory, name);
34
+ let isDirectory = false;
35
+ try {
36
+ isDirectory = statSync(absolute).isDirectory();
37
+ }
38
+ catch {
39
+ continue;
40
+ }
41
+ if (isDirectory)
42
+ walk(absolute);
43
+ else if (isConfigurationKindModule(name))
44
+ found.push(absolute);
45
+ }
46
+ };
47
+ walk(join(projectDir, 'src', 'contributions'));
48
+ return found.sort();
49
+ }
50
+ /** Register the `kind` export of an already-imported module; the source
51
+ * path names the module in a refusal. */
52
+ export function registerKindModule(module, source) {
53
+ const exported = module?.kind;
54
+ if (exported === undefined) {
55
+ throw new Error(`${source}: a kind contribution must export \`kind\``);
56
+ }
57
+ return registerContributedConfigurationKind(exported, source);
58
+ }
@@ -1,7 +1,7 @@
1
- import { type ResolvedGameManifest } from './load';
1
+ import { type LoadGameManifestOptions, type ResolvedGameManifest } from './load';
2
2
  export { hasManifest, manifestWritePath, resolveManifestPath } from './locate';
3
3
  /** Read + JSON.parse `path`, then resolve it via `loadGameManifest`. Node-only. */
4
- export declare function loadGameManifestFile(path: string): ResolvedGameManifest;
4
+ export declare function loadGameManifestFile(path: string, options?: LoadGameManifestOptions): ResolvedGameManifest;
5
5
  /** {@link loadGameManifestFile} against `dir`'s `vgai.project.json`. */
6
- export declare function loadGameManifestDir(dir: string): ResolvedGameManifest;
6
+ export declare function loadGameManifestDir(dir: string, options?: LoadGameManifestOptions): ResolvedGameManifest;
7
7
  //# sourceMappingURL=load-file.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"load-file.d.ts","sourceRoot":"","sources":["../../src/manifest/load-file.ts"],"names":[],"mappings":"AASA,OAAO,EAAoB,KAAK,oBAAoB,EAAE,MAAM,QAAQ,CAAC;AAMrE,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAE/E,mFAAmF;AACnF,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,oBAAoB,CAGvE;AAED,wEAAwE;AACxE,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,oBAAoB,CAErE"}
1
+ {"version":3,"file":"load-file.d.ts","sourceRoot":"","sources":["../../src/manifest/load-file.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,KAAK,uBAAuB,EAAoB,KAAK,oBAAoB,EAAE,MAAM,QAAQ,CAAC;AAMnG,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAE/E,mFAAmF;AACnF,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,uBAA4B,GACpC,oBAAoB,CAGtB;AAED,wEAAwE;AACxE,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,uBAA4B,GACpC,oBAAoB,CAEtB"}
@@ -13,11 +13,11 @@ import { resolveManifestPath } from './locate';
13
13
  // module.
14
14
  export { hasManifest, manifestWritePath, resolveManifestPath } from './locate';
15
15
  /** Read + JSON.parse `path`, then resolve it via `loadGameManifest`. Node-only. */
16
- export function loadGameManifestFile(path) {
16
+ export function loadGameManifestFile(path, options = {}) {
17
17
  const raw = JSON.parse(readFileSync(path, 'utf-8'));
18
- return loadGameManifest(raw);
18
+ return loadGameManifest(raw, options);
19
19
  }
20
20
  /** {@link loadGameManifestFile} against `dir`'s `vgai.project.json`. */
21
- export function loadGameManifestDir(dir) {
22
- return loadGameManifestFile(resolveManifestPath(dir));
21
+ export function loadGameManifestDir(dir, options = {}) {
22
+ return loadGameManifestFile(resolveManifestPath(dir), options);
23
23
  }
@@ -1,4 +1,5 @@
1
- import { type LearnMetadata } from './schema';
1
+ import { type DeclaredConfiguration } from './configuration-kinds';
2
+ import { type GameManifest, type LearnMetadata } from './schema';
2
3
  /** D6's resolved adapter identities. */
3
4
  export type ResolvedAdapter = {
4
5
  readonly type: 'builtin';
@@ -62,14 +63,15 @@ export interface ResolvedGameManifest {
62
63
  };
63
64
  /** Sorted by zOrder; ties broken by original array order (§3). */
64
65
  readonly roots: readonly ResolvedAdapterRoot[];
65
- readonly server: {
66
- readonly room: string;
67
- readonly module: string;
68
- /** The declared endpoint, or `undefined` = the scaffold default (see
69
- * the schema's own description; the editor resolves it against the
70
- * page host, which the loader has no business knowing). */
71
- readonly endpoint: string | undefined;
72
- } | undefined;
66
+ /** The declared run configurations, validated by kind (`run-kinds.ts`). */
67
+ readonly configurations: readonly DeclaredConfiguration[];
68
+ /** Ids whose kind no registry here knows — only ever non-empty when the
69
+ * caller loaded with `configurationKinds: 'defer'` (a bundled host that
70
+ * cannot evaluate a project's kind contributions); the editor session
71
+ * is the validator for those. */
72
+ readonly deferredConfigurationKinds: readonly string[];
73
+ /** Deploy-baked relay credentials, when the served manifest carries them. */
74
+ readonly relay: GameManifest['relay'];
73
75
  readonly resolution: {
74
76
  readonly width: number;
75
77
  readonly height: number;
@@ -117,13 +119,16 @@ export interface ResolvedGameManifest {
117
119
  * gallery/wizard/Learn-catalog registries. */
118
120
  readonly learn: LearnMetadata | undefined;
119
121
  }
120
- /** Default relative path for `server.module` when omitted (§3) — the scaffolded Colyseus bootstrap. */
121
- export declare const DEFAULT_SERVER_MODULE = "server/colyseus-setup.ts";
122
122
  /**
123
123
  * Parse + validate + resolve a raw manifest value into a `ResolvedGameManifest`.
124
124
  * Pure data-in/data-out (no `fs`) — see `load-file.ts` for the Node path-based
125
125
  * wrapper. Throws a descriptive `Error` (naming the offending root/field) on
126
126
  * any Zod validation failure or cross-field rule violation.
127
127
  */
128
- export declare function loadGameManifest(raw: unknown): ResolvedGameManifest;
128
+ export interface LoadGameManifestOptions {
129
+ /** `require` (default): a declaration of an unregistered kind refuses.
130
+ * `defer`: it is reported in `deferredConfigurationKinds` instead. */
131
+ readonly configurationKinds?: 'require' | 'defer';
132
+ }
133
+ export declare function loadGameManifest(raw: unknown, options?: LoadGameManifestOptions): ResolvedGameManifest;
129
134
  //# sourceMappingURL=load.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"load.d.ts","sourceRoot":"","sources":["../../src/manifest/load.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"load.d.ts","sourceRoot":"","sources":["../../src/manifest/load.ts"],"names":[],"mappings":"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"}
@@ -12,9 +12,8 @@
12
12
  // No `fs` import here on purpose — `load-file.ts` is the thin Node-only
13
13
  // wrapper that reads a path and calls this function, so browser bundles that
14
14
  // only need `loadGameManifest` never pull in `node:fs`.
15
+ import { configurationIssues, configurationKind, } from './configuration-kinds';
15
16
  import { GameManifestSchema, } from './schema';
16
- /** Default relative path for `server.module` when omitted (§3) — the scaffolded Colyseus bootstrap. */
17
- export const DEFAULT_SERVER_MODULE = 'server/colyseus-setup.ts';
18
17
  const SEMVER_RE = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z-.]+)?(?:\+[0-9A-Za-z-.]+)?$/;
19
18
  // ---------------------------------------------------------------------------
20
19
  // Adapter shape guards
@@ -119,16 +118,7 @@ function resolveRoot(root) {
119
118
  loop: root.loop,
120
119
  };
121
120
  }
122
- // ---------------------------------------------------------------------------
123
- // Entry point
124
- // ---------------------------------------------------------------------------
125
- /**
126
- * Parse + validate + resolve a raw manifest value into a `ResolvedGameManifest`.
127
- * Pure data-in/data-out (no `fs`) — see `load-file.ts` for the Node path-based
128
- * wrapper. Throws a descriptive `Error` (naming the offending root/field) on
129
- * any Zod validation failure or cross-field rule violation.
130
- */
131
- export function loadGameManifest(raw) {
121
+ export function loadGameManifest(raw, options = {}) {
132
122
  // Removed-format guard FIRST, on the raw value: a root declaring a removed
133
123
  // content field would otherwise fail Zod's `roots` discriminated union with
134
124
  // a confusing shape error instead of the named migration message.
@@ -156,13 +146,31 @@ export function loadGameManifest(raw) {
156
146
  }));
157
147
  resolvedWithIndex.sort((a, b) => a.root.zOrder - b.root.zOrder || a.index - b.index);
158
148
  const roots = resolvedWithIndex.map(({ root }) => root);
159
- const server = manifest.server
160
- ? {
161
- room: manifest.server.room,
162
- module: manifest.server.module ?? DEFAULT_SERVER_MODULE,
163
- endpoint: manifest.server.endpoint,
149
+ const configurations = (manifest.configurations ?? []);
150
+ // Kinds are OPEN (ARCHITECTURE-CORE §The project model): each declaration
151
+ // is checked against its registered kind's own schema HERE, where the
152
+ // host's registry is, not in the Zod schema. A host that cannot evaluate a
153
+ // project's kind contributions defers unknown kinds by name instead of
154
+ // refusing them.
155
+ const deferredConfigurationKinds = [];
156
+ const kindIssues = configurationIssues(configurations).filter((issue) => {
157
+ if (options.configurationKinds !== 'defer')
158
+ return true;
159
+ const unknown = /^configurations "([^"]+)": unknown kind/.exec(issue);
160
+ if (!unknown)
161
+ return true;
162
+ deferredConfigurationKinds.push(unknown[1]);
163
+ return false;
164
+ });
165
+ if (kindIssues.length > 0) {
166
+ throw new Error(`Game manifest: ${kindIssues.join('\n ')}`);
167
+ }
168
+ for (const configuration of configurations) {
169
+ if (!configurationKind(configuration.kind) &&
170
+ !deferredConfigurationKinds.includes(configuration.id)) {
171
+ deferredConfigurationKinds.push(configuration.id);
164
172
  }
165
- : undefined;
173
+ }
166
174
  return {
167
175
  manifestVersion: manifest.manifestVersion,
168
176
  name: manifest.name,
@@ -170,7 +178,9 @@ export function loadGameManifest(raw) {
170
178
  version: manifest.version,
171
179
  engine: { version: manifest.engine.version },
172
180
  roots,
173
- server,
181
+ configurations,
182
+ deferredConfigurationKinds,
183
+ relay: manifest.relay,
174
184
  resolution: manifest.resolution,
175
185
  rendering: manifest.rendering,
176
186
  authoring: manifest.authoring,
@@ -0,0 +1,11 @@
1
+ /** `engines.node` from the project's `package.json`, or `null` when the
2
+ * project declares none (or has no package.json). */
3
+ export declare function declaredNodeRange(projectDir: string): string | null;
4
+ /**
5
+ * Why a process of this project cannot start on the running Node, spelled
6
+ * for the person who will fix it — or `null` when it can. A range that is
7
+ * not valid semver is itself the issue: a declaration nobody can satisfy is
8
+ * named, never skipped.
9
+ */
10
+ export declare function nodeRuntimeIssue(projectDir: string, running?: string): string | null;
11
+ //# sourceMappingURL=runtime-environment.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-environment.d.ts","sourceRoot":"","sources":["../../src/manifest/runtime-environment.ts"],"names":[],"mappings":"AAeA;sDACsD;AACtD,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQnE;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,MAA8B,GACtC,MAAM,GAAG,IAAI,CAWf"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * THE RUNTIME HALF OF A PROJECT'S ENVIRONMENT (ARCHITECTURE-CORE §The
3
+ * project model, "environment"): the Node version a project's processes need
4
+ * is declared ONCE, where the ecosystem already declares it —
5
+ * `package.json` `engines.node` — and read here by whatever starts a
6
+ * `process` configuration (the editor server's configurations route, the
7
+ * CLI's autoboot). The manifest carries no `environment` key: a second
8
+ * declaration of the same fact is the drift a single home prevents.
9
+ *
10
+ * Node-only (reads the project folder), like `locate.ts`.
11
+ */
12
+ import { readFileSync } from 'node:fs';
13
+ import { join } from 'node:path';
14
+ import semver from 'semver';
15
+ /** `engines.node` from the project's `package.json`, or `null` when the
16
+ * project declares none (or has no package.json). */
17
+ export function declaredNodeRange(projectDir) {
18
+ try {
19
+ const raw = JSON.parse(readFileSync(join(projectDir, 'package.json'), 'utf-8'));
20
+ const engines = raw?.engines;
21
+ return typeof engines?.node === 'string' && engines.node.trim() ? engines.node.trim() : null;
22
+ }
23
+ catch {
24
+ return null;
25
+ }
26
+ }
27
+ /**
28
+ * Why a process of this project cannot start on the running Node, spelled
29
+ * for the person who will fix it — or `null` when it can. A range that is
30
+ * not valid semver is itself the issue: a declaration nobody can satisfy is
31
+ * named, never skipped.
32
+ */
33
+ export function nodeRuntimeIssue(projectDir, running = process.versions.node) {
34
+ const range = declaredNodeRange(projectDir);
35
+ if (range === null)
36
+ return null;
37
+ if (semver.validRange(range) === null) {
38
+ return `package.json declares engines.node "${range}", which is not a semver range`;
39
+ }
40
+ if (semver.satisfies(running, range, { includePrerelease: true }))
41
+ return null;
42
+ return (`package.json declares engines.node "${range}" and this host runs Node ${running} — ` +
43
+ `run the editor and \`vgai\` on a Node that satisfies it, or change engines.node`);
44
+ }
@@ -168,15 +168,16 @@ export declare const GameManifestSchema: z.ZodObject<{
168
168
  }, z.core.$strict>>>;
169
169
  }, z.core.$strict>>;
170
170
  }, z.core.$strict>>;
171
- server: z.ZodOptional<z.ZodObject<{
172
- room: z.ZodString;
173
- endpoint: z.ZodOptional<z.ZodString>;
174
- module: z.ZodOptional<z.ZodString>;
175
- relay: z.ZodOptional<z.ZodObject<{
176
- signalingUrl: z.ZodOptional<z.ZodString>;
177
- accessToken: z.ZodString;
178
- expiresAt: z.ZodNumber;
179
- }, z.core.$strict>>;
171
+ configurations: z.ZodOptional<z.ZodArray<z.ZodObject<{
172
+ id: z.ZodString;
173
+ kind: z.ZodString;
174
+ }, z.core.$loose>>>;
175
+ relay: z.ZodOptional<z.ZodObject<{
176
+ signalingUrl: z.ZodOptional<z.ZodString>;
177
+ grant: z.ZodOptional<z.ZodString>;
178
+ sessionUrl: z.ZodOptional<z.ZodString>;
179
+ accessToken: z.ZodOptional<z.ZodString>;
180
+ expiresAt: z.ZodOptional<z.ZodNumber>;
180
181
  }, z.core.$strict>>;
181
182
  resolution: z.ZodOptional<z.ZodObject<{
182
183
  width: z.ZodNumber;