@forgeax/engine-pack 0.1.3 → 0.1.4

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 (95) hide show
  1. package/README.md +51 -3
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/__tests__/owner-chain.integration.test.d.ts +2 -0
  4. package/dist/__tests__/owner-chain.integration.test.d.ts.map +1 -0
  5. package/dist/__tests__/package-finalizer.contract.test.d.ts +2 -0
  6. package/dist/__tests__/package-finalizer.contract.test.d.ts.map +1 -0
  7. package/dist/__tests__/runtime.browser.test.d.ts +2 -0
  8. package/dist/__tests__/runtime.browser.test.d.ts.map +1 -0
  9. package/dist/__tests__/scanner-inventory.contract.test.d.ts +2 -0
  10. package/dist/__tests__/scanner-inventory.contract.test.d.ts.map +1 -0
  11. package/dist/artifact-path.mjs +53 -0
  12. package/dist/artifact-path.mjs.map +1 -0
  13. package/dist/atlas/shelf-pack.d.ts.map +1 -1
  14. package/dist/build.d.ts +17 -0
  15. package/dist/build.d.ts.map +1 -0
  16. package/dist/build.mjs +10168 -0
  17. package/dist/build.mjs.map +1 -0
  18. package/dist/builtin.d.ts +20 -0
  19. package/dist/builtin.d.ts.map +1 -1
  20. package/dist/builtin.mjs +141 -0
  21. package/dist/builtin.mjs.map +1 -0
  22. package/dist/catalog-builder.d.ts +43 -0
  23. package/dist/catalog-builder.d.ts.map +1 -0
  24. package/dist/catalog-delta.d.ts +11 -0
  25. package/dist/catalog-delta.d.ts.map +1 -0
  26. package/dist/catalog-projection.d.ts +91 -0
  27. package/dist/catalog-projection.d.ts.map +1 -0
  28. package/dist/cli-asset.mjs +231 -484
  29. package/dist/cli-asset.mjs.map +1 -1
  30. package/dist/index.mjs +83 -1
  31. package/dist/index.mjs.map +1 -1
  32. package/dist/material-cook.d.ts +2 -0
  33. package/dist/material-cook.d.ts.map +1 -0
  34. package/dist/material-cook.mjs +193 -0
  35. package/dist/material-cook.mjs.map +1 -0
  36. package/dist/mesh-bin-contract.mjs +96 -0
  37. package/dist/mesh-bin-contract.mjs.map +1 -0
  38. package/dist/native-cooker-registry.d.ts +19 -0
  39. package/dist/native-cooker-registry.d.ts.map +1 -1
  40. package/dist/native-cooker.mjs +37 -0
  41. package/dist/native-cooker.mjs.map +1 -1
  42. package/dist/package-finalizer.d.ts +121 -0
  43. package/dist/package-finalizer.d.ts.map +1 -0
  44. package/dist/resolve-asset-source.d.ts.map +1 -1
  45. package/dist/resolve-asset-source.mjs +16 -6
  46. package/dist/resolve-asset-source.mjs.map +1 -1
  47. package/dist/runtime-publication.d.ts +44 -0
  48. package/dist/runtime-publication.d.ts.map +1 -0
  49. package/dist/runtime.d.ts +8 -0
  50. package/dist/runtime.d.ts.map +1 -0
  51. package/dist/runtime.mjs +7261 -0
  52. package/dist/runtime.mjs.map +1 -0
  53. package/dist/scanner.d.ts +87 -54
  54. package/dist/scanner.d.ts.map +1 -1
  55. package/dist/scanner.mjs +258 -454
  56. package/dist/scanner.mjs.map +1 -1
  57. package/dist/scriptable-pack-node.d.ts +3 -8
  58. package/dist/scriptable-pack-node.d.ts.map +1 -1
  59. package/dist/scriptable-pack-node.mjs +93 -9
  60. package/dist/scriptable-pack-node.mjs.map +1 -1
  61. package/dist/scriptable-pack-worker.mjs +1 -0
  62. package/dist/scriptable-pack-worker.mjs.map +1 -1
  63. package/dist/scriptable-pack.d.ts +33 -0
  64. package/dist/scriptable-pack.d.ts.map +1 -1
  65. package/dist/scriptable-pack.mjs +91 -2
  66. package/dist/scriptable-pack.mjs.map +1 -1
  67. package/dist/topology.d.ts +1 -1
  68. package/dist/topology.d.ts.map +1 -1
  69. package/package.json +27 -2
  70. package/src/__tests__/mesh-bin-consumer-surface.unit.test.ts +2 -1
  71. package/src/__tests__/owner-chain.integration.test.ts +78 -0
  72. package/src/__tests__/package-finalizer.contract.test.ts +105 -0
  73. package/src/__tests__/runtime.browser.test.ts +15 -0
  74. package/src/__tests__/scanner-inventory.contract.test.ts +167 -0
  75. package/src/__tests__/scanner-inventory.test.ts +16 -12
  76. package/src/__tests__/scriptable-pack.unit.test.ts +42 -0
  77. package/src/atlas/shelf-pack.ts +20 -24
  78. package/src/build.ts +137 -0
  79. package/src/builtin.ts +38 -0
  80. package/src/catalog-builder.ts +313 -0
  81. package/src/catalog-delta.ts +189 -0
  82. package/src/catalog-projection.ts +344 -0
  83. package/src/cli-asset.ts +1 -5
  84. package/src/index.ts +2 -2
  85. package/src/material-cook.ts +13 -0
  86. package/src/native-cooker-registry.ts +72 -0
  87. package/src/package-finalizer.ts +394 -0
  88. package/src/resolve-asset-source.ts +2 -7
  89. package/src/runtime-publication.ts +190 -0
  90. package/src/runtime.ts +23 -0
  91. package/src/scanner.ts +289 -290
  92. package/src/scriptable-pack-node.ts +27 -23
  93. package/src/scriptable-pack-worker.ts +2 -0
  94. package/src/scriptable-pack.ts +123 -1
  95. package/src/topology.ts +2 -1
package/dist/scanner.mjs CHANGED
@@ -1,9 +1,9 @@
1
+ import { createHash } from 'crypto';
1
2
  import { stat, readFile, readdir, realpath, mkdtemp, rm } from 'fs/promises';
2
3
  import { join, basename, dirname, resolve, posix, extname } from 'path';
3
4
  import { PACK_ERROR_HINTS, ok, err, validateSourceOverrideMap, AssetError, ImportError } from '@forgeax/engine-types';
4
5
  import { readFileSync, existsSync } from 'fs';
5
6
  import { uuidv7obj } from 'uuidv7';
6
- import { createHash } from 'crypto';
7
7
  import { tmpdir } from 'os';
8
8
  import { fileURLToPath } from 'url';
9
9
  import { Worker } from 'worker_threads';
@@ -7134,11 +7134,23 @@ function validateProducerOutputs(outputs) {
7134
7134
  }
7135
7135
  return { ok: true, value: outputs };
7136
7136
  }
7137
+
7138
+ // src/deriveAssetName.ts
7137
7139
  function baseName(p) {
7138
7140
  const trimmed = p.replace(/[/\\]+$/, "");
7139
7141
  const idx = Math.max(trimmed.lastIndexOf("/"), trimmed.lastIndexOf("\\"));
7140
7142
  return idx === -1 ? trimmed : trimmed.slice(idx + 1);
7141
7143
  }
7144
+ function deriveAssetName(packagePath, assetCount, storedName) {
7145
+ if (packagePath === null) {
7146
+ return "";
7147
+ }
7148
+ {
7149
+ return baseName(packagePath);
7150
+ }
7151
+ }
7152
+
7153
+ // src/resolve-asset-source.ts
7142
7154
  function deriveSourceName(metaFileName) {
7143
7155
  return metaFileName.replace(/\.meta\.json$/, "");
7144
7156
  }
@@ -7150,7 +7162,7 @@ function isPathEscape(pathDir, resolved) {
7150
7162
  function resolveAssetSource(metaPath, source, paths) {
7151
7163
  if (source === void 0) {
7152
7164
  const metaDir = dirname(metaPath);
7153
- const metaName = baseName(metaPath);
7165
+ const metaName = deriveAssetName(metaPath);
7154
7166
  const derived = deriveSourceName(metaName);
7155
7167
  return ok(resolve(metaDir, derived));
7156
7168
  }
@@ -7647,6 +7659,22 @@ var SCRIPTABLE_PACK_ASSET_KINDS = [
7647
7659
  function isScriptablePackAssetKind(value) {
7648
7660
  return SCRIPTABLE_PACK_ASSET_KINDS.includes(value);
7649
7661
  }
7662
+ function sceneComponentFieldType(value) {
7663
+ if (typeof value === "string") return value;
7664
+ if (isRecord2(value) && typeof value.type === "string") return value.type;
7665
+ return void 0;
7666
+ }
7667
+ function projectScriptablePackSceneComponents(components) {
7668
+ return (components ?? []).map((component) => ({
7669
+ name: component.name,
7670
+ fields: Object.fromEntries(
7671
+ Object.entries(component.fields).map(([fieldName, field]) => [
7672
+ fieldName,
7673
+ sceneComponentFieldType(field)
7674
+ ])
7675
+ )
7676
+ }));
7677
+ }
7650
7678
  var SCRIPTABLE_PACK_REQUEST_ID_SCHEMA = {
7651
7679
  type: "string",
7652
7680
  minLength: 1,
@@ -7872,11 +7900,20 @@ function freezeDefinition(definition) {
7872
7900
  guid.slice()
7873
7901
  ])
7874
7902
  );
7903
+ const sceneComponents = definition.sceneComponents === void 0 ? void 0 : Object.freeze(
7904
+ projectScriptablePackSceneComponents(definition.sceneComponents).map(
7905
+ (component) => Object.freeze({
7906
+ ...component,
7907
+ fields: Object.freeze({ ...component.fields })
7908
+ })
7909
+ )
7910
+ );
7875
7911
  return Object.freeze({
7876
7912
  ...definition,
7877
7913
  packageId: definition.packageId.slice(),
7878
7914
  assets: Object.freeze(assets),
7879
- externalAssets: Object.freeze(externalAssets)
7915
+ externalAssets: Object.freeze(externalAssets),
7916
+ ...sceneComponents === void 0 ? {} : { sceneComponents }
7880
7917
  });
7881
7918
  }
7882
7919
  function validateScriptablePackDefinition(value, sourcePath) {
@@ -7907,6 +7944,63 @@ function validateScriptablePackDefinition(value, sourcePath) {
7907
7944
  sourcePath
7908
7945
  );
7909
7946
  }
7947
+ if (value.sceneComponents !== void 0) {
7948
+ if (!Array.isArray(value.sceneComponents)) {
7949
+ return invalid(
7950
+ "$.sceneComponents",
7951
+ "an array of component tokens or neutral schema rows when present",
7952
+ value.sceneComponents,
7953
+ sourcePath
7954
+ );
7955
+ }
7956
+ const componentNames = /* @__PURE__ */ new Set();
7957
+ for (const [componentIndex, component] of value.sceneComponents.entries()) {
7958
+ if (!isRecord2(component)) {
7959
+ return invalid(
7960
+ `$.sceneComponents[${componentIndex}]`,
7961
+ "a component token or neutral schema row",
7962
+ component,
7963
+ sourcePath
7964
+ );
7965
+ }
7966
+ if (typeof component.name !== "string" || component.name.trim().length === 0) {
7967
+ return invalid(
7968
+ `$.sceneComponents[${componentIndex}].name`,
7969
+ "a non-empty component name",
7970
+ component.name,
7971
+ sourcePath
7972
+ );
7973
+ }
7974
+ if (componentNames.has(component.name)) {
7975
+ return invalid(
7976
+ `$.sceneComponents[${componentIndex}].name`,
7977
+ "component names to be unique within one ScriptablePack",
7978
+ component.name,
7979
+ sourcePath
7980
+ );
7981
+ }
7982
+ componentNames.add(component.name);
7983
+ if (!isRecord2(component.fields)) {
7984
+ return invalid(
7985
+ `$.sceneComponents[${componentIndex}].fields`,
7986
+ "a field-name to schema-type object",
7987
+ component.fields,
7988
+ sourcePath
7989
+ );
7990
+ }
7991
+ for (const [fieldName, field] of Object.entries(component.fields)) {
7992
+ const type = sceneComponentFieldType(field);
7993
+ if (fieldName.trim().length === 0 || type === void 0 || type.trim().length === 0) {
7994
+ return invalid(
7995
+ `$.sceneComponents[${componentIndex}].fields`,
7996
+ "field names with non-empty string schema types",
7997
+ field,
7998
+ sourcePath
7999
+ );
8000
+ }
8001
+ }
8002
+ }
8003
+ }
7910
8004
  if (typeof value.build !== "function") {
7911
8005
  return invalid("$.build", "a build(assetReader) function", value.build, sourcePath);
7912
8006
  }
@@ -8010,7 +8104,7 @@ function scriptablePackWorkerExecArgv() {
8010
8104
  const inherited = process.execArgv.filter((arg) => !arg.startsWith("--input-type"));
8011
8105
  return inherited.includes(IMPORT_META_RESOLVE_FLAG) ? inherited : [...inherited, IMPORT_META_RESOLVE_FLAG];
8012
8106
  }
8013
- async function resolveRelativeSourceImport(sourcePath, specifier) {
8107
+ async function resolveRelativeImport(sourcePath, specifier) {
8014
8108
  if (!specifier.startsWith(".")) return void 0;
8015
8109
  const raw = resolve(dirname(sourcePath), specifier);
8016
8110
  const candidates = extname(raw).length > 0 ? [raw] : [
@@ -8031,19 +8125,20 @@ async function resolveRelativeSourceImport(sourcePath, specifier) {
8031
8125
  }
8032
8126
  return void 0;
8033
8127
  }
8034
- async function inventoryScriptablePackSource(sourcePath) {
8035
- const pending = [await realpath(sourcePath)];
8128
+ async function inventoryScriptablePackSource(sourcePath, initialSourceText) {
8129
+ const root = await realpath(sourcePath);
8130
+ const pending = [root];
8036
8131
  const seen = /* @__PURE__ */ new Set();
8037
8132
  const entries = [];
8038
8133
  while (pending.length > 0) {
8039
8134
  const path = pending.pop();
8040
8135
  if (path === void 0 || seen.has(path)) continue;
8041
8136
  seen.add(path);
8042
- const bytes = await readFile(path);
8137
+ const bytes = path === root && initialSourceText !== void 0 ? new TextEncoder().encode(initialSourceText) : await readFile(path);
8043
8138
  entries.push({ path, digest: `sha256:${createHash("sha256").update(bytes).digest("hex")}` });
8044
- const imports = ts.preProcessFile(new TextDecoder().decode(bytes), true, true).importedFiles;
8045
- for (const imported of imports) {
8046
- const resolved = await resolveRelativeSourceImport(path, imported.fileName);
8139
+ const source = new TextDecoder().decode(bytes);
8140
+ for (const imported of ts.preProcessFile(source, true, true).importedFiles) {
8141
+ const resolved = await resolveRelativeImport(path, imported.fileName);
8047
8142
  if (resolved !== void 0) pending.push(await realpath(resolved));
8048
8143
  }
8049
8144
  }
@@ -8117,6 +8212,7 @@ var WorkerScriptablePackExecutor = class {
8117
8212
  if (message === null || typeof message !== "object") return;
8118
8213
  const value = message;
8119
8214
  if (value.kind === "loaded") {
8215
+ worker.unref();
8120
8216
  worker.off("message", onMessage);
8121
8217
  resolveLoad({
8122
8218
  default: value.definition === void 0 ? void 0 : {
@@ -8194,247 +8290,6 @@ function scriptablePackWorkerUrl() {
8194
8290
  const bundledWorker = new URL("./scriptable-pack-worker.mjs", import.meta.url);
8195
8291
  return existsSync(fileURLToPath(bundledWorker)) ? bundledWorker : new URL("../dist/scriptable-pack-worker.mjs", import.meta.url);
8196
8292
  }
8197
- var ReusableWorkerScriptablePackExecutor = class {
8198
- constructor(maxTasksPerWorker, release) {
8199
- this.maxTasksPerWorker = maxTasksPerWorker;
8200
- this.release = release;
8201
- this.worker = new Worker(scriptablePackWorkerUrl(), {
8202
- workerData: { mode: "reusable" },
8203
- execArgv: scriptablePackWorkerExecArgv(),
8204
- resourceLimits: { maxOldGenerationSizeMb: 256, maxYoungGenerationSizeMb: 64 }
8205
- });
8206
- this.worker.on("message", (message) => this.onMessage(message));
8207
- this.worker.on("error", (error) => {
8208
- this.failed = true;
8209
- this.rejectActive(error);
8210
- void this.dispose("failure");
8211
- });
8212
- this.worker.on("exit", (code) => {
8213
- if (code === 0 || this.disposal !== void 0) return;
8214
- this.failed = true;
8215
- this.rejectActive(new Error(`ScriptablePack worker exited with code ${code}`));
8216
- void this.dispose("failure");
8217
- });
8218
- this.worker.unref();
8219
- }
8220
- maxTasksPerWorker;
8221
- release;
8222
- worker;
8223
- compileRootReady = mkdtemp(resolve(tmpdir(), "forgeax-scriptable-pack-pool-"));
8224
- compileRoot;
8225
- nextLoadId = 0;
8226
- nextBuildId = 0;
8227
- pendingLoad;
8228
- build;
8229
- disposal;
8230
- forceDispose = false;
8231
- failed = false;
8232
- taskCount = 0;
8233
- released = false;
8234
- async load(sourcePath) {
8235
- const activeDisposal = this.disposal;
8236
- if (activeDisposal !== void 0) {
8237
- await activeDisposal;
8238
- if (this.failed) {
8239
- return Promise.reject(new Error("ScriptablePack executor is not leased"));
8240
- }
8241
- this.disposal = void 0;
8242
- }
8243
- this.released = false;
8244
- if (this.pendingLoad !== void 0 || this.build !== void 0) {
8245
- return Promise.reject(new Error("ScriptablePack executor already has an active task"));
8246
- }
8247
- const compileRoot = this.compileRoot ?? await this.compileRootReady;
8248
- if (this.disposal !== void 0) {
8249
- const disposal = this.disposal;
8250
- await disposal;
8251
- if (this.failed) {
8252
- return Promise.reject(new Error("ScriptablePack executor is not leased"));
8253
- }
8254
- this.disposal = void 0;
8255
- }
8256
- const loadId = this.nextLoadId++;
8257
- this.compileRoot = compileRoot;
8258
- this.taskCount += 1;
8259
- return new Promise((resolveLoad, rejectLoad) => {
8260
- this.pendingLoad = { id: loadId, resolve: resolveLoad, reject: rejectLoad };
8261
- try {
8262
- this.worker.postMessage({
8263
- kind: "load",
8264
- loadId,
8265
- sourcePath: resolve(sourcePath),
8266
- compileRoot
8267
- });
8268
- } catch (error) {
8269
- this.pendingLoad = void 0;
8270
- rejectLoad(error);
8271
- void this.dispose("failure");
8272
- }
8273
- });
8274
- }
8275
- dispose(reason = "complete") {
8276
- if (reason === "failure") this.forceDispose = true;
8277
- const activeDisposal = this.disposal;
8278
- if (activeDisposal !== void 0) {
8279
- return activeDisposal;
8280
- }
8281
- this.rejectActive(new Error(`ScriptablePack task disposed during ${reason}`));
8282
- this.disposal = (async () => {
8283
- const compileRoot = this.compileRoot ?? await this.compileRootReady;
8284
- const reusable = reason === "complete" && !this.failed && !this.forceDispose && this.taskCount < this.maxTasksPerWorker;
8285
- this.forceDispose = false;
8286
- if (!reusable) {
8287
- this.compileRoot = void 0;
8288
- await this.worker.terminate();
8289
- await rm(compileRoot, { recursive: true, force: true });
8290
- }
8291
- this.released = true;
8292
- this.disposal = void 0;
8293
- this.release(this, reusable);
8294
- })();
8295
- return this.disposal;
8296
- }
8297
- rejectActive(error) {
8298
- const loading = this.pendingLoad;
8299
- this.pendingLoad = void 0;
8300
- loading?.reject(error);
8301
- const building = this.build;
8302
- this.build = void 0;
8303
- building?.reject(error);
8304
- }
8305
- runBuild(reader) {
8306
- if (this.released || this.disposal !== void 0) {
8307
- return Promise.reject(new Error("ScriptablePack executor is not leased"));
8308
- }
8309
- if (this.build !== void 0) {
8310
- return Promise.reject(new Error("ScriptablePack worker already has an active build"));
8311
- }
8312
- const id = this.nextBuildId++;
8313
- return new Promise((resolveBuild, rejectBuild) => {
8314
- this.build = { id, reader, resolve: resolveBuild, reject: rejectBuild };
8315
- try {
8316
- this.worker.postMessage({ kind: "build", buildId: id });
8317
- } catch (error) {
8318
- this.build = void 0;
8319
- rejectBuild(error);
8320
- void this.dispose("failure");
8321
- }
8322
- });
8323
- }
8324
- onMessage(message) {
8325
- if (message === null || typeof message !== "object") return;
8326
- const value = message;
8327
- const loading = this.pendingLoad;
8328
- if (loading !== void 0 && value.kind === "loaded" && value.loadId === loading.id) {
8329
- this.pendingLoad = void 0;
8330
- loading.resolve({
8331
- default: value.definition === void 0 ? void 0 : {
8332
- ...value.definition,
8333
- build: (reader) => this.runBuild(reader)
8334
- }
8335
- });
8336
- return;
8337
- }
8338
- if (loading !== void 0 && value.kind === "load-threw" && value.loadId === loading.id) {
8339
- this.pendingLoad = void 0;
8340
- loading.reject(hydrateFailure(value.error));
8341
- return;
8342
- }
8343
- const active = this.build;
8344
- if (active === void 0 || value.buildId !== active.id) return;
8345
- if (value.kind === "asset-read" && typeof value.readId === "number") {
8346
- void active.reader.readByGuid(value.guid).then(
8347
- (result) => this.worker.postMessage({
8348
- kind: "asset-result",
8349
- readId: value.readId,
8350
- result: result.ok === true ? result : { ...result, error: serializeFailure(result.error) }
8351
- })
8352
- ).catch((error) => {
8353
- if (this.build?.id !== active.id) return;
8354
- this.build = void 0;
8355
- active.reject(error);
8356
- void this.dispose("failure");
8357
- });
8358
- return;
8359
- }
8360
- this.build = void 0;
8361
- if (value.kind === "build-result") active.resolve(value.result);
8362
- else if (value.kind === "build-threw") active.reject(hydrateFailure(value.error));
8363
- else this.build = active;
8364
- }
8365
- };
8366
- var DefaultScriptablePackModuleExecutorPool = class {
8367
- maxWorkers;
8368
- maxTasksPerWorker;
8369
- idle = [];
8370
- waiters = [];
8371
- executors = /* @__PURE__ */ new Set();
8372
- workerCount = 0;
8373
- closed = false;
8374
- constructor(options = {}) {
8375
- this.maxWorkers = Math.max(1, Math.trunc(options.maxWorkers ?? 2));
8376
- this.maxTasksPerWorker = Math.max(1, Math.trunc(options.maxTasksPerWorker ?? 32));
8377
- }
8378
- acquire() {
8379
- if (this.closed) return Promise.reject(new Error("ScriptablePack executor pool is closed"));
8380
- const executor = this.idle.pop();
8381
- if (executor !== void 0) return Promise.resolve(executor);
8382
- if (this.workerCount < this.maxWorkers) {
8383
- this.workerCount += 1;
8384
- return Promise.resolve(this.createExecutor());
8385
- }
8386
- return new Promise(
8387
- (resolveAcquire, rejectAcquire) => this.waiters.push({ resolve: resolveAcquire, reject: rejectAcquire })
8388
- );
8389
- }
8390
- async dispose() {
8391
- if (this.closed) return;
8392
- this.closed = true;
8393
- const waiters = this.waiters.splice(0);
8394
- for (const waiter of waiters) {
8395
- waiter.reject(new Error("ScriptablePack executor pool is closed"));
8396
- }
8397
- this.idle.splice(0);
8398
- await Promise.all([...this.executors].map((executor) => executor.dispose("failure")));
8399
- }
8400
- createExecutor() {
8401
- const executor = new ReusableWorkerScriptablePackExecutor(
8402
- this.maxTasksPerWorker,
8403
- (executor2, reusable) => {
8404
- if (this.closed) {
8405
- this.executors.delete(executor2);
8406
- if (!reusable) this.workerCount -= 1;
8407
- return;
8408
- }
8409
- if (!reusable) {
8410
- this.executors.delete(executor2);
8411
- this.workerCount -= 1;
8412
- } else {
8413
- this.idle.push(executor2);
8414
- }
8415
- this.drain();
8416
- }
8417
- );
8418
- this.executors.add(executor);
8419
- return executor;
8420
- }
8421
- drain() {
8422
- if (this.closed) return;
8423
- while (this.waiters.length > 0) {
8424
- const idle = this.idle.pop();
8425
- if (idle !== void 0) {
8426
- this.waiters.shift()?.resolve(idle);
8427
- continue;
8428
- }
8429
- if (this.workerCount >= this.maxWorkers) return;
8430
- this.workerCount += 1;
8431
- this.waiters.shift()?.resolve(this.createExecutor());
8432
- }
8433
- }
8434
- };
8435
- function createScriptablePackModuleExecutorPool(options = {}) {
8436
- return new DefaultScriptablePackModuleExecutorPool(options);
8437
- }
8438
8293
  function loadFailure(sourcePath, reason, phase, diagnostic, timeoutMs) {
8439
8294
  return err({
8440
8295
  code: "pack-source-load-failed",
@@ -8545,6 +8400,9 @@ function ok3(value) {
8545
8400
  function packErr(error) {
8546
8401
  return { ok: false, error };
8547
8402
  }
8403
+ var STANDARD_SCRIPTABLE_PACK_SCAN_OPTIONS = Object.freeze(
8404
+ {}
8405
+ );
8548
8406
  var BLACKLIST = /* @__PURE__ */ new Set([
8549
8407
  "node_modules",
8550
8408
  ".forgeax-harness",
@@ -8556,6 +8414,33 @@ var BLACKLIST = /* @__PURE__ */ new Set([
8556
8414
  "coverage"
8557
8415
  ]);
8558
8416
  var SCANNER_BLACKLIST = BLACKLIST;
8417
+ async function readValidatedJson(path, code, validate) {
8418
+ let raw;
8419
+ let parsed;
8420
+ try {
8421
+ raw = await readFile(path, "utf-8");
8422
+ parsed = JSON.parse(raw);
8423
+ } catch {
8424
+ return packErr(
8425
+ makePackError(code, {
8426
+ path,
8427
+ ajvErrors: [{ instancePath: "", message: "JSON parse failed" }]
8428
+ })
8429
+ );
8430
+ }
8431
+ if (!validate(parsed)) {
8432
+ return packErr(
8433
+ makePackError(code, {
8434
+ path,
8435
+ ajvErrors: (validate.errors ?? []).map((error) => ({
8436
+ instancePath: error.instancePath ?? "",
8437
+ message: error.message ?? "unknown ajv error"
8438
+ }))
8439
+ })
8440
+ );
8441
+ }
8442
+ return ok3({ raw, parsed });
8443
+ }
8559
8444
  function makePackError(code, detail) {
8560
8445
  return new PackError({
8561
8446
  code,
@@ -8578,7 +8463,7 @@ function* extractMountSourceGuids(asset) {
8578
8463
  yield resolved.toLowerCase();
8579
8464
  }
8580
8465
  }
8581
- async function scanInventory(roots, opts = {}) {
8466
+ async function scanValidated(roots, opts = {}, capture) {
8582
8467
  const rawPaths = [];
8583
8468
  const explicitRootSet = new Set(roots);
8584
8469
  async function traverse(dir) {
@@ -8623,34 +8508,10 @@ async function scanInventory(roots, opts = {}) {
8623
8508
  const scriptablePaths = rawPaths.filter((p) => p.endsWith(".pack.ts"));
8624
8509
  const guidToPath = /* @__PURE__ */ new Map();
8625
8510
  const packRefs = /* @__PURE__ */ new Map();
8626
- const scriptablePackMeta = /* @__PURE__ */ new Map();
8627
- const metaByGuid = /* @__PURE__ */ new Map();
8628
- const declarations = [];
8629
8511
  for (const packPath of packPaths) {
8630
- let parsed;
8631
- try {
8632
- const raw = await readFile(packPath, "utf-8");
8633
- parsed = JSON.parse(raw);
8634
- } catch {
8635
- return packErr(
8636
- makePackError("pack-malformed-pack", {
8637
- path: packPath,
8638
- ajvErrors: [{ instancePath: "", message: "JSON parse failed" }]
8639
- })
8640
- );
8641
- }
8642
- const valid = validatePack(parsed);
8643
- if (!valid) {
8644
- return packErr(
8645
- makePackError("pack-malformed-pack", {
8646
- path: packPath,
8647
- ajvErrors: (validatePack.errors ?? []).map((e) => ({
8648
- instancePath: e.instancePath,
8649
- message: e.message ?? "unknown ajv error"
8650
- }))
8651
- })
8652
- );
8653
- }
8512
+ const loaded = await readValidatedJson(packPath, "pack-malformed-pack", validatePack);
8513
+ if (!loaded.ok) return loaded;
8514
+ const { raw, parsed } = loaded.value;
8654
8515
  const packageContract = validateProducerContract(parsed);
8655
8516
  if (!packageContract.ok) {
8656
8517
  return packErr(
@@ -8692,7 +8553,12 @@ async function scanInventory(roots, opts = {}) {
8692
8553
  }
8693
8554
  }
8694
8555
  const topologyContract = validateProducerOutputs(
8695
- producerAssets
8556
+ producerAssets.map((asset, sourceIndex) => ({
8557
+ guid: asset.guid,
8558
+ kind: asset.kind,
8559
+ sourceIndex: asset.sourceIndex ?? sourceIndex,
8560
+ ...asset.sourceKey === void 0 ? {} : { sourceKey: asset.sourceKey }
8561
+ }))
8696
8562
  );
8697
8563
  if (!topologyContract.ok) {
8698
8564
  return packErr(
@@ -8733,66 +8599,24 @@ async function scanInventory(roots, opts = {}) {
8733
8599
  );
8734
8600
  }
8735
8601
  guidToPath.set(normalizedGuid, packPath);
8736
- const existingRefs = packRefs.get(normalizedGuid) ?? [];
8737
- for (const ref of asset.refs) {
8738
- existingRefs.push(ref.toLowerCase());
8739
- }
8740
- for (const guid of extractMountSourceGuids(asset)) {
8741
- existingRefs.push(guid);
8742
- }
8743
- packRefs.set(normalizedGuid, existingRefs);
8744
- }
8745
- const outputs = packObj.assets.map((asset) => ({
8746
- guid: asset.guid,
8747
- ...asset.sourceKey === void 0 ? {} : { sourceKey: asset.sourceKey },
8748
- ...asset.sourceIndex === void 0 ? {} : { sourceIndex: asset.sourceIndex },
8749
- kind: asset.kind,
8750
- ...asset.name === void 0 ? {} : { name: asset.name },
8751
- ...asset.execution === void 0 ? {} : { execution: asset.execution },
8752
- refs: asset.refs
8753
- }));
8754
- declarations.push({
8602
+ packRefs.set(normalizedGuid, [
8603
+ ...asset.refs.map((ref) => ref.toLowerCase()),
8604
+ ...extractMountSourceGuids(asset)
8605
+ ]);
8606
+ }
8607
+ capture?.declarations.set(packPath, {
8608
+ format: "pack.json",
8755
8609
  sourcePath: packPath,
8756
- canonicalSourcePath: packPath.replace(/\\/g, "/"),
8757
- producer: "direct-pack",
8758
- ...packObj.packageId === void 0 ? {} : { packageId: packObj.packageId },
8759
- projectedMeta: {
8760
- schemaVersion: "2.0.0",
8761
- ...packObj.packageId === void 0 ? {} : { packageId: packObj.packageId },
8762
- outputs,
8763
- externalGuids: outputs.flatMap((output) => output.refs)
8764
- },
8765
- outputs,
8766
- externalGuids: outputs.flatMap((output) => output.refs),
8767
- diagnostics: []
8610
+ sourceRevision: `sha256:${createHash("sha256").update(raw).digest("hex")}`,
8611
+ sourceText: raw,
8612
+ value: packObj
8768
8613
  });
8769
8614
  }
8770
8615
  const { paths: assetPaths } = loadAssetConfig(process.cwd());
8771
8616
  for (const metaPath of metaPaths) {
8772
- let parsed;
8773
- try {
8774
- const raw = await readFile(metaPath, "utf-8");
8775
- parsed = JSON.parse(raw);
8776
- } catch {
8777
- return packErr(
8778
- makePackError("pack-malformed-meta", {
8779
- path: metaPath,
8780
- ajvErrors: [{ instancePath: "", message: "JSON parse failed" }]
8781
- })
8782
- );
8783
- }
8784
- const valid = validateMeta(parsed);
8785
- if (!valid) {
8786
- return packErr(
8787
- makePackError("pack-malformed-meta", {
8788
- path: metaPath,
8789
- ajvErrors: (validateMeta.errors ?? []).map((e) => ({
8790
- instancePath: e.instancePath,
8791
- message: e.message ?? "unknown ajv error"
8792
- }))
8793
- })
8794
- );
8795
- }
8617
+ const loaded = await readValidatedJson(metaPath, "pack-malformed-meta", validateMeta);
8618
+ if (!loaded.ok) return loaded;
8619
+ const { raw, parsed } = loaded.value;
8796
8620
  const metaContract = validateProducerContract(parsed);
8797
8621
  if (!metaContract.ok) {
8798
8622
  return packErr(
@@ -8803,6 +8627,12 @@ async function scanInventory(roots, opts = {}) {
8803
8627
  );
8804
8628
  }
8805
8629
  const metaObj = parsed;
8630
+ capture?.declarations.set(metaPath, {
8631
+ format: "meta.json",
8632
+ sourcePath: metaPath,
8633
+ sourceRevision: `sha256:${createHash("sha256").update(raw).digest("hex")}`,
8634
+ value: metaObj
8635
+ });
8806
8636
  for (const sub of metaObj.subAssets) {
8807
8637
  if (!isValidAssetGuidString(sub.guid)) {
8808
8638
  return packErr(
@@ -8823,13 +8653,10 @@ async function scanInventory(roots, opts = {}) {
8823
8653
  );
8824
8654
  }
8825
8655
  guidToPath.set(normalizedGuid, metaPath);
8826
- metaByGuid.set(normalizedGuid, metaPath);
8827
8656
  }
8828
8657
  const producerSubAssets = metaObj.subAssets.length > 1 ? metaObj.subAssets : [];
8829
8658
  if (producerSubAssets.length > 0) {
8830
- const topologyContract = validateProducerOutputs(
8831
- producerSubAssets
8832
- );
8659
+ const topologyContract = validateProducerOutputs(producerSubAssets);
8833
8660
  if (!topologyContract.ok) {
8834
8661
  return packErr(
8835
8662
  makePackError("pack-malformed-meta", {
@@ -8854,113 +8681,80 @@ async function scanInventory(roots, opts = {}) {
8854
8681
  })
8855
8682
  );
8856
8683
  }
8857
- const outputs = metaObj.subAssets.map((sub) => ({
8858
- guid: sub.guid,
8859
- ...sub.sourceKey === void 0 ? {} : { sourceKey: sub.sourceKey },
8860
- sourceIndex: sub.sourceIndex,
8861
- kind: sub.kind,
8862
- ...sub.name === void 0 ? {} : { name: sub.name },
8863
- refs: []
8864
- }));
8865
- const externalGuids = (metaObj.importSettings?.externalAssets ?? []).flatMap(
8866
- (entry) => typeof entry.guid === "string" ? [entry.guid] : []
8867
- );
8868
- declarations.push({
8869
- sourcePath: metaPath,
8870
- canonicalSourcePath: metaPath.replace(/\\/g, "/"),
8871
- producer: "source-meta",
8872
- ...metaObj.packageId === void 0 ? {} : { packageId: metaObj.packageId },
8873
- projectedMeta: {
8874
- ...metaObj.packageId === void 0 ? {} : { packageId: metaObj.packageId },
8875
- ...metaObj.importer === void 0 ? {} : { importer: metaObj.importer },
8876
- ...metaObj.source === void 0 ? {} : { source: metaObj.source },
8877
- ...metaObj.name === void 0 ? {} : { name: metaObj.name },
8878
- outputs,
8879
- externalGuids
8880
- },
8881
- outputs,
8882
- externalGuids,
8883
- diagnostics: []
8884
- });
8885
8684
  }
8886
- const scriptablePackExecutorPool = scriptablePaths.length === 0 || opts.scriptablePackLoad?.executor !== void 0 ? void 0 : createScriptablePackModuleExecutorPool();
8887
- try {
8888
- for (const sourcePath of scriptablePaths) {
8889
- const executor = await scriptablePackExecutorPool?.acquire();
8890
- const loaded = await loadScriptablePack(sourcePath, {
8891
- ...opts.scriptablePackLoad,
8892
- metadataOnly: true,
8893
- ...executor === void 0 ? {} : { executor }
8894
- });
8895
- if (!loaded.ok) {
8896
- const diagnostic = "diagnostic" in loaded.error.detail ? loaded.error.detail.diagnostic : void 0;
8897
- return packErr(
8898
- makePackError("pack-malformed-meta", {
8899
- path: sourcePath,
8900
- ajvErrors: [
8901
- {
8902
- instancePath: "",
8903
- message: diagnostic === void 0 ? loaded.error.code : `${loaded.error.code}: ${diagnostic}`
8904
- }
8905
- ]
8906
- })
8907
- );
8908
- }
8909
- const meta = projectScriptablePackMeta(loaded.value, sourcePath);
8910
- scriptablePackMeta.set(sourcePath, meta);
8911
- const sourceClosure = await inventoryScriptablePackSource(sourcePath);
8912
- const topology = validateProducerOutputs(
8913
- meta.subAssets
8685
+ for (const sourcePath of scriptablePaths) {
8686
+ let source;
8687
+ try {
8688
+ source = await readFile(sourcePath, "utf8");
8689
+ } catch {
8690
+ return packErr(
8691
+ makePackError("pack-malformed-meta", {
8692
+ path: sourcePath,
8693
+ ajvErrors: [{ instancePath: "", message: "ScriptablePack source read failed" }]
8694
+ })
8695
+ );
8696
+ }
8697
+ const loaded = await loadScriptablePack(sourcePath, {
8698
+ ...opts.scriptablePack ?? {},
8699
+ ...capture === void 0 ? { metadataOnly: true } : {}
8700
+ });
8701
+ if (!loaded.ok) {
8702
+ const diagnostic = "diagnostic" in loaded.error.detail ? loaded.error.detail.diagnostic : void 0;
8703
+ return packErr(
8704
+ makePackError("pack-malformed-meta", {
8705
+ path: sourcePath,
8706
+ ajvErrors: [
8707
+ {
8708
+ instancePath: "",
8709
+ message: diagnostic === void 0 ? loaded.error.code : `${loaded.error.code}: ${diagnostic}`
8710
+ }
8711
+ ]
8712
+ })
8713
+ );
8714
+ }
8715
+ let sourceClosure;
8716
+ try {
8717
+ sourceClosure = await inventoryScriptablePackSource(sourcePath, source);
8718
+ } catch {
8719
+ return packErr(
8720
+ makePackError("pack-malformed-meta", {
8721
+ path: sourcePath,
8722
+ ajvErrors: [{ instancePath: "", message: "ScriptablePack source closure read failed" }]
8723
+ })
8914
8724
  );
8915
- if (!topology.ok) {
8725
+ }
8726
+ const meta = projectScriptablePackMeta(loaded.value, sourcePath);
8727
+ const topology = validateProducerOutputs(meta.subAssets);
8728
+ if (!topology.ok) {
8729
+ return packErr(
8730
+ makePackError("pack-malformed-meta", {
8731
+ path: sourcePath,
8732
+ ajvErrors: [{ instancePath: "/subAssets", message: topology.error.code }]
8733
+ })
8734
+ );
8735
+ }
8736
+ const declaration = {
8737
+ sourcePath,
8738
+ sourceRevision: `sha256:${createHash("sha256").update(source).digest("hex")}`,
8739
+ meta,
8740
+ definition: loaded.value,
8741
+ sourceClosure
8742
+ };
8743
+ capture?.declarations.set(sourcePath, {
8744
+ format: "pack.ts",
8745
+ ...declaration,
8746
+ value: meta
8747
+ });
8748
+ for (const sub of meta.subAssets) {
8749
+ const guid = sub.guid.toLowerCase();
8750
+ const existing = guidToPath.get(guid);
8751
+ if (existing !== void 0) {
8916
8752
  return packErr(
8917
- makePackError("pack-malformed-meta", {
8918
- path: sourcePath,
8919
- ajvErrors: [{ instancePath: "/subAssets", message: topology.error.code }]
8920
- })
8753
+ makePackError("pack-guid-collision", { paths: [existing, sourcePath], guid })
8921
8754
  );
8922
8755
  }
8923
- for (const sub of meta.subAssets) {
8924
- const guid = sub.guid.toLowerCase();
8925
- const existing = guidToPath.get(guid);
8926
- if (existing !== void 0) {
8927
- return packErr(
8928
- makePackError("pack-guid-collision", { paths: [existing, sourcePath], guid })
8929
- );
8930
- }
8931
- guidToPath.set(guid, sourcePath);
8932
- }
8933
- const outputs = meta.subAssets.map((sub) => ({
8934
- guid: sub.guid,
8935
- sourceKey: sub.sourceKey,
8936
- sourceIndex: sub.sourceIndex,
8937
- kind: sub.kind,
8938
- ...sub.name === void 0 ? {} : { name: sub.name },
8939
- refs: []
8940
- }));
8941
- const externalGuids = meta.importSettings.externalAssets.map((entry) => entry.guid);
8942
- declarations.push({
8943
- sourcePath,
8944
- canonicalSourcePath: sourcePath.replace(/\\/g, "/"),
8945
- producer: "scriptable-pack",
8946
- packageId: meta.packageId,
8947
- projectedMeta: {
8948
- schemaVersion: meta.schemaVersion,
8949
- packageId: meta.packageId,
8950
- importer: meta.importer,
8951
- source: meta.source,
8952
- ...meta.name === void 0 ? {} : { name: meta.name },
8953
- outputs,
8954
- externalGuids
8955
- },
8956
- outputs,
8957
- externalGuids,
8958
- sourceClosure,
8959
- diagnostics: []
8960
- });
8756
+ guidToPath.set(guid, sourcePath);
8961
8757
  }
8962
- } finally {
8963
- await scriptablePackExecutorPool?.dispose();
8964
8758
  }
8965
8759
  const visited = /* @__PURE__ */ new Set();
8966
8760
  const recStack = /* @__PURE__ */ new Set();
@@ -8993,23 +8787,33 @@ async function scanInventory(roots, opts = {}) {
8993
8787
  }
8994
8788
  }
8995
8789
  }
8996
- const declarationsByPath = new Map(
8997
- declarations.map((declaration) => [declaration.sourcePath, declaration])
8998
- );
8999
- return ok3({
9000
- paths: rawPaths,
9001
- scriptablePackMeta,
9002
- declarations,
9003
- declarationsByPath,
9004
- sourceByGuid: new Map(guidToPath),
9005
- metaByGuid
9006
- });
8790
+ return ok3(rawPaths);
9007
8791
  }
9008
8792
  async function scan(roots, opts = {}) {
9009
- const inventory = await scanInventory(roots, opts);
9010
- return inventory.ok ? ok3([...inventory.value.paths]) : inventory;
8793
+ return scanValidated(roots, opts);
8794
+ }
8795
+ async function scanInventory(roots, opts = {}) {
8796
+ const declarations = /* @__PURE__ */ new Map();
8797
+ const scanned = await scanValidated(roots, opts, { declarations });
8798
+ if (!scanned.ok) return scanned;
8799
+ const inventory = [];
8800
+ for (const sourcePath of scanned.value) {
8801
+ const declaration = declarations.get(sourcePath);
8802
+ if (declaration?.format !== "pack.json") continue;
8803
+ for (const [index, asset] of declaration.value.assets.entries()) {
8804
+ inventory.push({
8805
+ guid: asset.guid,
8806
+ kind: asset.kind,
8807
+ sourcePath,
8808
+ sourceRevision: declaration.sourceRevision,
8809
+ ...asset.sourceKey === void 0 ? {} : { sourceKey: asset.sourceKey },
8810
+ ...asset.sourceIndex === void 0 ? { sourceIndex: index } : { sourceIndex: asset.sourceIndex }
8811
+ });
8812
+ }
8813
+ }
8814
+ return ok3({ paths: scanned.value, inventory, declarations });
9011
8815
  }
9012
8816
 
9013
- export { SCANNER_BLACKLIST, scan, scanInventory };
8817
+ export { SCANNER_BLACKLIST, STANDARD_SCRIPTABLE_PACK_SCAN_OPTIONS, scan, scanInventory };
9014
8818
  //# sourceMappingURL=scanner.mjs.map
9015
8819
  //# sourceMappingURL=scanner.mjs.map