@forgeax/engine-pack 0.1.27 → 0.1.29
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/README.md +30 -17
- package/dist/build.mjs +35 -52
- package/dist/build.mjs.map +1 -1
- package/dist/cli-asset.d.ts +1 -1
- package/dist/cli-asset.d.ts.map +1 -1
- package/dist/cli-asset.mjs +40 -33
- package/dist/cli-asset.mjs.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/inventory/declaration.d.ts +1 -1
- package/dist/inventory/declaration.d.ts.map +1 -1
- package/dist/inventory/sync.d.ts.map +1 -1
- package/dist/native-cooker-registry.d.ts +1 -0
- package/dist/native-cooker-registry.d.ts.map +1 -1
- package/dist/native-cooker.mjs.map +1 -1
- package/dist/pack-authoring-node.mjs +22 -13
- package/dist/pack-authoring-node.mjs.map +1 -1
- package/dist/runtime.mjs +1 -1
- package/dist/runtime.mjs.map +1 -1
- package/dist/scanner.mjs +22 -13
- package/dist/scanner.mjs.map +1 -1
- package/dist/schema-compiled.d.ts.map +1 -1
- package/dist/schema.mjs +41 -63
- package/dist/schema.mjs.map +1 -1
- package/dist/scriptable-pack-node.d.ts.map +1 -1
- package/dist/scriptable-pack-node.mjs +11 -3
- package/dist/scriptable-pack-node.mjs.map +1 -1
- package/dist/scriptable-pack-worker.mjs +5 -1
- package/dist/scriptable-pack-worker.mjs.map +1 -1
- package/package.json +3 -6
- package/schema/meta.schema.json +1 -1
- package/src/__tests__/atlas-cli-region-mismatch.test.ts +1 -1
- package/src/__tests__/cli.unit.test.ts +1 -1
- package/src/__tests__/inventory-schema.test.ts +10 -10
- package/src/__tests__/pack-authoring.unit.test.ts +2 -4
- package/src/__tests__/pack.unit.test.ts +89 -127
- package/src/__tests__/scanner-instance-cycle.test.ts +92 -0
- package/src/__tests__/scanner-inventory.contract.test.ts +2 -2
- package/src/__tests__/scriptable-pack-diagnostic.integration.test.ts +45 -0
- package/src/atlas/run-atlas.ts +3 -3
- package/src/atlas/shelf-pack.ts +1 -1
- package/src/atlas/upng-ambient.d.ts +1 -1
- package/src/cli-asset.ts +22 -23
- package/src/inventory/binding.ts +3 -3
- package/src/inventory/declaration.ts +10 -48
- package/src/inventory/sync.ts +3 -2
- package/src/native-cooker-registry.ts +1 -0
- package/src/scanner.ts +29 -29
- package/src/schema-compiled.ts +40 -79
- package/src/scriptable-pack-node.ts +11 -3
- package/src/scriptable-pack-worker.ts +8 -1
- package/src/__tests__/scanner-mount-cycle.test.ts +0 -232
package/README.md
CHANGED
|
@@ -37,11 +37,22 @@ export default definePack({
|
|
|
37
37
|
build: ({ packageId: subjectId }) =>
|
|
38
38
|
ok({
|
|
39
39
|
'mesh/main': { kind: 'mesh', materialSlots: [] },
|
|
40
|
+
'scene/hero': {
|
|
41
|
+
kind: 'scene',
|
|
42
|
+
entities: { body: { components: {} } },
|
|
43
|
+
},
|
|
40
44
|
'scene/main': {
|
|
41
45
|
kind: 'scene',
|
|
42
|
-
entities:
|
|
43
|
-
|
|
44
|
-
|
|
46
|
+
entities: {
|
|
47
|
+
root: { components: {} },
|
|
48
|
+
hero: {
|
|
49
|
+
components: {},
|
|
50
|
+
instance: {
|
|
51
|
+
source: AssetGuid.format(AssetGuid.derive(subjectId, 'scene/hero')),
|
|
52
|
+
overrides: [{ target: ['body'], components: {} }],
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
},
|
|
45
56
|
},
|
|
46
57
|
}),
|
|
47
58
|
});
|
|
@@ -147,8 +158,8 @@ source.
|
|
|
147
158
|
|
|
148
159
|
| Need | Entry | Safe action |
|
|
149
160
|
|:--|:--|:--|
|
|
150
|
-
| Inspect one asset | `forgeax
|
|
151
|
-
| Verify a result | `forgeax
|
|
161
|
+
| Inspect one asset | `forgeax asset inspect --subject <guid> --root <project> --json` | Read structured evidence and diagnostics |
|
|
162
|
+
| Verify a result | `forgeax asset verify --root <project> --json` | Repair the producer or package, then retry |
|
|
152
163
|
| Rebuild a cooked result | Pack/import runner and the declared producer | Cold cook after discarding invalid DDC |
|
|
153
164
|
| Preview old output | Catalog evidence with explicit last-known-good state | Preview only; never publish it as current |
|
|
154
165
|
|
|
@@ -191,7 +202,7 @@ sampler、强度或非默认坐标等附加元数据才使用结构化纹理对
|
|
|
191
202
|
> package moves and DDC relocation. Paths and array positions are locators,
|
|
192
203
|
> not identities.
|
|
193
204
|
|
|
194
|
-
Disk schema, GUID tools (`AssetGuid` brand + UUIDv7/v5), mesh-binary wire facts, and scanner fail-fast chain (13-member `PackErrorCode`) for the
|
|
205
|
+
Disk schema, GUID tools (`AssetGuid` brand + UUIDv7/v5), mesh-binary wire facts, and scanner fail-fast chain (13-member `PackErrorCode`) for the ForgeaX asset package system. The unified `asset list`, `asset inspect`, and `asset verify` commands call this owner in filesystem mode; they are offline and require no WS connection.
|
|
195
206
|
|
|
196
207
|
> Package name vs directory: this package is published as `@forgeax/engine-pack` but lives at `packages/pack` on disk. The `@forgeax/engine-` prefix is the IDE-autocomplete entrypoint AI users discover the package family by; the directory drops the prefix to keep tree depth flat (mirrors the `packages/runtime` / `@forgeax/engine-runtime` pair). All other packages in the engine family follow the same convention.
|
|
197
208
|
|
|
@@ -201,9 +212,10 @@ Pack owns the offline half of the GUID evidence chain: `source inventory -> cata
|
|
|
201
212
|
|
|
202
213
|
The catalog is a locator, not proof. `lookup/verify --guid --project --catalog --json` joins the source meta or authored pack, the catalog row, the receipt, and the package descriptors. Both commands emit one JSON record on stdout or one structured `{code, expected, hint, detail}` record on stderr; there is no runtime or WebSocket dependency.
|
|
203
214
|
|
|
204
|
-
## VFX Pack v2
|
|
215
|
+
## VFX Program v3 in Pack v2
|
|
205
216
|
|
|
206
|
-
VFX source
|
|
217
|
+
VFX source v3 is cooked in one atomic producer step and stored in the ordinary
|
|
218
|
+
Pack v2 envelope. The executable contract is
|
|
207
219
|
`scripts/asset-cook-contract.mjs`; a package-local scripts path is not valid.
|
|
208
220
|
The cook publishes the Pack payload and
|
|
209
221
|
`particle-effect/program.json` with the same source fingerprint. Runtime loads
|
|
@@ -266,7 +278,7 @@ vectors, colors, and asset-GUID constraints. The isolated Pack worker passes
|
|
|
266
278
|
only serializable authoring data to the build and never exposes filesystem or
|
|
267
279
|
runtime state.
|
|
268
280
|
|
|
269
|
-
`forgeax
|
|
281
|
+
`forgeax asset inspect --subject <source.pack.ts> --root <project> --json` executes module initialization, validates the default export, and projects canonical Meta without calling `build`. `@forgeax/engine-pack/source-node` accepts a host executor with `load` and optional `dispose`; `timeoutMs` bounds module initialization and `buildTimeoutMs` bounds one `build(context)` call. A build timeout returns one structured `pack-source-load-failed` Result with `detail.phase: 'build'`, the configured `timeoutMs`, and deterministic cleanup of the isolated worker and compile root.
|
|
270
282
|
|
|
271
283
|
The default worker executes the complete relative TypeScript module closure on the supported Node floor, including Node 22 hosts that do not load `.ts` files directly. It transpiles that closure into a disposable ESM directory, resolves bare imports through the source project's nearest `node_modules`, and removes the directory when the worker is disposed. Bulk producers use the internal `createScriptablePackModuleExecutorPool()` with two recyclable workers; a pooled lease is released after metadata projection or one build, so a generation never retains one live Worker-backed definition per source.
|
|
272
284
|
|
|
@@ -460,21 +472,22 @@ Exhaustive `switch (err.code)` without `default` -- TS guards completeness.
|
|
|
460
472
|
|
|
461
473
|
Access `err.detail.<field>` directly after narrowing via `switch (err.code)` -- full IDE autocomplete.
|
|
462
474
|
|
|
463
|
-
##
|
|
475
|
+
## Unified asset commands
|
|
464
476
|
|
|
465
|
-
|
|
477
|
+
DevKit loads the Pack producer behind the unified `forgeax asset` command;
|
|
478
|
+
this package does not publish an independent executable.
|
|
466
479
|
|
|
467
480
|
| Subcommand | Description | Exit code |
|
|
468
481
|
|:--|:--|:--|
|
|
469
|
-
| `forgeax
|
|
470
|
-
| `forgeax
|
|
471
|
-
| `forgeax
|
|
482
|
+
| `forgeax asset list --root <project> --json` | Print discovered `PackEntry` objects | 0 on valid roots |
|
|
483
|
+
| `forgeax asset inspect --subject <guid> --root <project> --json` | Print matching asset evidence | 0 found / 1 not found |
|
|
484
|
+
| `forgeax asset verify --root <project> --json` | Run the fail-fast scanner and return the first structured `PackError` | 0 clean / 1 error |
|
|
472
485
|
|
|
473
486
|
```bash
|
|
474
487
|
# Direct invocation (after pnpm -F @forgeax/engine-pack build)
|
|
475
|
-
forgeax
|
|
476
|
-
forgeax
|
|
477
|
-
forgeax
|
|
488
|
+
forgeax asset list --root ./game --json
|
|
489
|
+
forgeax asset inspect --subject 01935f3b-aaaa-7000-8000-000000000001 --root ./game --json
|
|
490
|
+
forgeax asset verify --root ./game --json
|
|
478
491
|
```
|
|
479
492
|
|
|
480
493
|
## Scanner 7-step validation chain
|
package/dist/build.mjs
CHANGED
|
@@ -8450,7 +8450,7 @@ var meta_schema_default = {
|
|
|
8450
8450
|
properties: {
|
|
8451
8451
|
schemaVersion: {
|
|
8452
8452
|
oneOf: [{ type: "string" }, { type: "integer" }],
|
|
8453
|
-
description: "Schema version. Strings (e.g. '1.0.0') for hand-authored .meta.json; integers (1, 2, ...) for importer-emitted .meta.json (forgeax
|
|
8453
|
+
description: "Schema version. Strings (e.g. '1.0.0') for hand-authored .meta.json; integers (1, 2, ...) for importer-emitted .meta.json (forgeax asset import writes schemaVersion: 1 as the wire format SSOT, GltfMetaJson literal type)."
|
|
8454
8454
|
},
|
|
8455
8455
|
kind: {
|
|
8456
8456
|
type: "string",
|
|
@@ -9061,9 +9061,17 @@ var WorkerScriptablePackExecutor = class {
|
|
|
9061
9061
|
}
|
|
9062
9062
|
};
|
|
9063
9063
|
worker.on("message", onMessage);
|
|
9064
|
-
|
|
9064
|
+
const fail = (error) => {
|
|
9065
|
+
rejectLoad(error);
|
|
9066
|
+
const active = this.build;
|
|
9067
|
+
this.build = void 0;
|
|
9068
|
+
active?.reject(error);
|
|
9069
|
+
void this.dispose("failure");
|
|
9070
|
+
};
|
|
9071
|
+
worker.once("error", fail);
|
|
9065
9072
|
worker.once("exit", (code) => {
|
|
9066
|
-
if (
|
|
9073
|
+
if (this.disposal !== void 0) return;
|
|
9074
|
+
fail(new Error(`ScriptablePack worker exited with code ${code}`));
|
|
9067
9075
|
});
|
|
9068
9076
|
});
|
|
9069
9077
|
}
|
|
@@ -9298,16 +9306,17 @@ function makePackError(code, detail) {
|
|
|
9298
9306
|
detail
|
|
9299
9307
|
});
|
|
9300
9308
|
}
|
|
9301
|
-
function*
|
|
9309
|
+
function* extractInstanceSourceGuids(asset) {
|
|
9302
9310
|
if (asset.kind !== "scene") return;
|
|
9303
9311
|
const payload = asset.payload;
|
|
9304
|
-
if (!payload ||
|
|
9305
|
-
|
|
9306
|
-
|
|
9307
|
-
|
|
9308
|
-
|
|
9309
|
-
if (
|
|
9310
|
-
const
|
|
9312
|
+
if (!payload || payload.entities === null || typeof payload.entities !== "object" || Array.isArray(payload.entities))
|
|
9313
|
+
return;
|
|
9314
|
+
for (const rawEntity of Object.values(payload.entities)) {
|
|
9315
|
+
if (rawEntity === null || typeof rawEntity !== "object" || Array.isArray(rawEntity)) continue;
|
|
9316
|
+
const instance = rawEntity.instance;
|
|
9317
|
+
if (instance === null || typeof instance !== "object" || Array.isArray(instance)) continue;
|
|
9318
|
+
const source = instance.source;
|
|
9319
|
+
const resolved = typeof source === "number" && Number.isInteger(source) ? asset.refs[source] : typeof source === "string" ? source : void 0;
|
|
9311
9320
|
if (typeof resolved !== "string") continue;
|
|
9312
9321
|
yield resolved.toLowerCase();
|
|
9313
9322
|
}
|
|
@@ -9425,7 +9434,7 @@ async function scanValidated(roots, opts = {}, capture) {
|
|
|
9425
9434
|
guidToPath.set(normalizedGuid, packPath);
|
|
9426
9435
|
packRefs.set(normalizedGuid, [
|
|
9427
9436
|
...asset.refs.map((ref) => ref.toLowerCase()),
|
|
9428
|
-
...
|
|
9437
|
+
...extractInstanceSourceGuids(asset)
|
|
9429
9438
|
]);
|
|
9430
9439
|
}
|
|
9431
9440
|
}
|
|
@@ -9529,7 +9538,7 @@ async function scanValidated(roots, opts = {}, capture) {
|
|
|
9529
9538
|
guidToPath.set(normalizedGuid, packPath);
|
|
9530
9539
|
packRefs.set(normalizedGuid, [
|
|
9531
9540
|
...asset.refs.map((ref) => ref.toLowerCase()),
|
|
9532
|
-
...
|
|
9541
|
+
...extractInstanceSourceGuids(asset)
|
|
9533
9542
|
]);
|
|
9534
9543
|
}
|
|
9535
9544
|
capture?.declarations.set(packPath, {
|
|
@@ -10714,9 +10723,9 @@ function projectAssetRefs(inventory) {
|
|
|
10714
10723
|
}
|
|
10715
10724
|
function projectSceneEntityRefs(inventory) {
|
|
10716
10725
|
return inventory.declarations.flatMap(
|
|
10717
|
-
(row) => row.
|
|
10726
|
+
(row) => row.sceneEntityKeys === void 0 ? [] : row.sceneEntityKeys.map((address) => ({
|
|
10718
10727
|
sceneSourceKey: row.sourceKey,
|
|
10719
|
-
|
|
10728
|
+
address
|
|
10720
10729
|
}))
|
|
10721
10730
|
);
|
|
10722
10731
|
}
|
|
@@ -10776,49 +10785,23 @@ function validateAuthorInventory(value) {
|
|
|
10776
10785
|
{ guid, sourceKey: sourceKey2 }
|
|
10777
10786
|
);
|
|
10778
10787
|
}
|
|
10779
|
-
if (row.kind === "scene") {
|
|
10780
|
-
const payload = row.payload;
|
|
10781
|
-
const payloadRecord = typeof payload === "object" && payload !== null ? payload : void 0;
|
|
10782
|
-
const entities = payloadRecord !== void 0 && Array.isArray(payloadRecord.entities) ? payloadRecord.entities : [];
|
|
10783
|
-
const bindings = /* @__PURE__ */ new Set();
|
|
10784
|
-
for (const entity of entities) {
|
|
10785
|
-
const bindingKey = typeof entity === "object" && entity !== null && typeof entity.bindingKey === "string" ? entity.bindingKey : void 0;
|
|
10786
|
-
if (bindingKey !== void 0 && bindingKey.length === 0) {
|
|
10787
|
-
return failure2(
|
|
10788
|
-
"inventory-scene-binding-missing",
|
|
10789
|
-
"each declared scene bindingKey is non-empty",
|
|
10790
|
-
"remove the empty bindingKey or declare a stable key in the scene producer",
|
|
10791
|
-
{ sourceKey: sourceKey2 }
|
|
10792
|
-
);
|
|
10793
|
-
}
|
|
10794
|
-
if (bindingKey === void 0) continue;
|
|
10795
|
-
if (bindings.has(bindingKey)) {
|
|
10796
|
-
return failure2(
|
|
10797
|
-
"inventory-scene-binding-duplicate",
|
|
10798
|
-
"bindingKey values are unique within one scene",
|
|
10799
|
-
"rename the duplicate bindingKey in the scene producer",
|
|
10800
|
-
{ sourceKey: sourceKey2 }
|
|
10801
|
-
);
|
|
10802
|
-
}
|
|
10803
|
-
bindings.add(bindingKey);
|
|
10804
|
-
}
|
|
10805
|
-
}
|
|
10806
10788
|
guids.add(normalizedGuid);
|
|
10807
10789
|
sourceKeys.add(sourceKey2);
|
|
10808
|
-
const
|
|
10809
|
-
(
|
|
10810
|
-
|
|
10811
|
-
|
|
10812
|
-
|
|
10813
|
-
|
|
10814
|
-
|
|
10790
|
+
const sceneEntityKeys = (() => {
|
|
10791
|
+
if (row.kind !== "scene" || typeof row.payload !== "object" || row.payload === null)
|
|
10792
|
+
return void 0;
|
|
10793
|
+
const entities = row.payload.entities;
|
|
10794
|
+
if (entities === null || typeof entities !== "object" || Array.isArray(entities))
|
|
10795
|
+
return void 0;
|
|
10796
|
+
return Object.keys(entities);
|
|
10797
|
+
})();
|
|
10815
10798
|
declarations.push({
|
|
10816
10799
|
guid,
|
|
10817
10800
|
sourceKey: sourceKey2,
|
|
10818
10801
|
kind: typeof row.kind === "string" ? row.kind : "unknown",
|
|
10819
10802
|
payload: typeof row.payload === "object" && row.payload !== null ? row.payload : {},
|
|
10820
10803
|
refs: Array.isArray(row.refs) ? row.refs.filter((ref) => typeof ref === "string") : [],
|
|
10821
|
-
...
|
|
10804
|
+
...sceneEntityKeys === void 0 ? {} : { sceneEntityKeys }
|
|
10822
10805
|
});
|
|
10823
10806
|
}
|
|
10824
10807
|
return ok({ declarations });
|
|
@@ -10827,7 +10810,7 @@ function validateAuthorInventory(value) {
|
|
|
10827
10810
|
// src/inventory/sync.ts
|
|
10828
10811
|
function inventoryDigest(inventory) {
|
|
10829
10812
|
return inventory.declarations.map(
|
|
10830
|
-
(row) => `${row.guid}\0${row.sourceKey}\0${row.kind}\0${row.
|
|
10813
|
+
(row) => `${row.guid}\0${row.sourceKey}\0${row.kind}\0${row.sceneEntityKeys?.join("\0") ?? ""}`
|
|
10831
10814
|
).sort().join("\n");
|
|
10832
10815
|
}
|
|
10833
10816
|
function syncAuthorInventory(inventory) {
|
|
@@ -10835,7 +10818,7 @@ function syncAuthorInventory(inventory) {
|
|
|
10835
10818
|
declarations: inventory.declarations.map((row) => ({
|
|
10836
10819
|
...row,
|
|
10837
10820
|
refs: [...row.refs],
|
|
10838
|
-
...row.
|
|
10821
|
+
...row.sceneEntityKeys === void 0 ? {} : { sceneEntityKeys: [...row.sceneEntityKeys] }
|
|
10839
10822
|
}))
|
|
10840
10823
|
};
|
|
10841
10824
|
}
|