@forgeax/engine-pack 0.1.21 → 0.1.24
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 +167 -48
- package/dist/__tests__/inventory-schema.test.d.ts +2 -0
- package/dist/__tests__/inventory-schema.test.d.ts.map +1 -0
- package/dist/__tests__/material-cook-receipt-layer-plan.unit.test.d.ts +2 -0
- package/dist/__tests__/material-cook-receipt-layer-plan.unit.test.d.ts.map +1 -0
- package/dist/__tests__/pack-authoring-gateway.unit.test.d.ts +2 -0
- package/dist/__tests__/pack-authoring-gateway.unit.test.d.ts.map +1 -0
- package/dist/__tests__/pack-authoring.unit.test.d.ts +2 -0
- package/dist/__tests__/pack-authoring.unit.test.d.ts.map +1 -0
- package/dist/__tests__/pack-scanner.unit.test.d.ts +2 -0
- package/dist/__tests__/pack-scanner.unit.test.d.ts.map +1 -0
- package/dist/__tests__/scanner-blacklist.test.d.ts +2 -0
- package/dist/__tests__/scanner-blacklist.test.d.ts.map +1 -0
- package/dist/build.d.ts +9 -4
- package/dist/build.d.ts.map +1 -1
- package/dist/build.mjs +3449 -897
- package/dist/build.mjs.map +1 -1
- package/dist/builtin.mjs +37 -4
- package/dist/builtin.mjs.map +1 -1
- package/dist/catalog-builder.d.ts.map +1 -1
- package/dist/cli-asset.d.ts.map +1 -1
- package/dist/cli-asset.mjs +1361 -845
- package/dist/cli-asset.mjs.map +1 -1
- package/dist/deriveAssetName.d.ts +11 -7
- package/dist/deriveAssetName.d.ts.map +1 -1
- package/dist/evidence/material-cook.d.ts +11 -0
- package/dist/evidence/material-cook.d.ts.map +1 -1
- package/dist/evidence/source-inventory.d.ts.map +1 -1
- package/dist/guid.d.ts +15 -0
- package/dist/guid.d.ts.map +1 -1
- package/dist/guid.mjs +53 -5
- package/dist/guid.mjs.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +1297 -562
- package/dist/index.mjs.map +1 -1
- package/dist/inventory/binding.d.ts +6 -0
- package/dist/inventory/binding.d.ts.map +1 -0
- package/dist/inventory/declaration.d.ts +24 -0
- package/dist/inventory/declaration.d.ts.map +1 -0
- package/dist/inventory/index.d.ts +3 -0
- package/dist/inventory/index.d.ts.map +1 -0
- package/dist/inventory/sync.d.ts +5 -0
- package/dist/inventory/sync.d.ts.map +1 -0
- package/dist/material-cook.d.ts +1 -1
- package/dist/material-cook.d.ts.map +1 -1
- package/dist/material-cook.mjs +50 -3
- package/dist/material-cook.mjs.map +1 -1
- package/dist/name.mjs +4 -7
- package/dist/name.mjs.map +1 -1
- package/dist/pack-authoring-node.d.ts +26 -0
- package/dist/pack-authoring-node.d.ts.map +1 -0
- package/dist/pack-authoring-node.mjs +11089 -0
- package/dist/pack-authoring-node.mjs.map +1 -0
- package/dist/pack-authoring.d.ts +230 -0
- package/dist/pack-authoring.d.ts.map +1 -0
- package/dist/pack-authoring.mjs +1137 -0
- package/dist/pack-authoring.mjs.map +1 -0
- package/dist/package-finalizer.d.ts +2 -20
- package/dist/package-finalizer.d.ts.map +1 -1
- package/dist/resolve-asset-source.d.ts +1 -3
- package/dist/resolve-asset-source.d.ts.map +1 -1
- package/dist/resolve-asset-source.mjs +6 -81
- package/dist/resolve-asset-source.mjs.map +1 -1
- package/dist/runtime.mjs +77 -7
- package/dist/runtime.mjs.map +1 -1
- package/dist/scanner.d.ts +18 -7
- package/dist/scanner.d.ts.map +1 -1
- package/dist/scanner.mjs +1164 -687
- package/dist/scanner.mjs.map +1 -1
- package/dist/schema-compiled.d.ts.map +1 -1
- package/dist/schema.mjs +79 -7
- package/dist/schema.mjs.map +1 -1
- package/dist/scriptable-pack-node.d.ts +7 -31
- package/dist/scriptable-pack-node.d.ts.map +1 -1
- package/dist/scriptable-pack-node.mjs +572 -1019
- package/dist/scriptable-pack-node.mjs.map +1 -1
- package/dist/scriptable-pack-worker.mjs +29 -11
- package/dist/scriptable-pack-worker.mjs.map +1 -1
- package/dist/scriptable-pack.d.ts +20 -532
- package/dist/scriptable-pack.d.ts.map +1 -1
- package/dist/scriptable-pack.mjs +1038 -527
- package/dist/scriptable-pack.mjs.map +1 -1
- package/package.json +2 -7
- package/schema/meta.schema.json +1 -1
- package/schema/pack.schema.json +76 -6
- package/src/__tests__/guid-collision.unit.test.ts +18 -9
- package/src/__tests__/inventory-schema.test.ts +109 -0
- package/src/__tests__/material-cook-receipt-layer-plan.unit.test.ts +16 -0
- package/src/__tests__/material-cook-schema.unit.test.ts +62 -0
- package/src/__tests__/pack-authoring-gateway.unit.test.ts +249 -0
- package/src/__tests__/pack-authoring.unit.test.ts +266 -0
- package/src/__tests__/pack-scanner.unit.test.ts +123 -0
- package/src/__tests__/pack.unit.test.ts +1 -238
- package/src/__tests__/package-finalizer.contract.test.ts +55 -0
- package/src/__tests__/resolve-asset-source.test.ts +15 -98
- package/src/__tests__/scanner-blacklist.test.ts +55 -0
- package/src/__tests__/scanner-inventory.contract.test.ts +11 -14
- package/src/__tests__/scanner-inventory.test.ts +23 -10
- package/src/__tests__/scriptable-pack-cli.integration.test.ts +4 -9
- package/src/__tests__/topology.unit.test.ts +23 -0
- package/src/build.ts +28 -18
- package/src/catalog-builder.ts +60 -33
- package/src/cli-asset.ts +68 -83
- package/src/deriveAssetName.ts +14 -13
- package/src/evidence/material-cook.ts +79 -3
- package/src/evidence/source-inventory.ts +34 -21
- package/src/guid.ts +65 -4
- package/src/index.ts +15 -11
- package/src/inventory/binding.ts +25 -0
- package/src/inventory/declaration.ts +168 -0
- package/src/inventory/index.ts +18 -0
- package/src/inventory/sync.ts +22 -0
- package/src/material-cook.ts +1 -0
- package/src/pack-authoring-node.ts +1966 -0
- package/src/pack-authoring.ts +1478 -0
- package/src/package-finalizer.ts +2 -42
- package/src/resolve-asset-source.ts +4 -76
- package/src/scanner.ts +236 -56
- package/src/schema/material-cook.schema.json +4 -1
- package/src/schema-compiled.ts +2 -0
- package/src/scriptable-pack-node.ts +108 -719
- package/src/scriptable-pack-worker.ts +48 -16
- package/src/scriptable-pack.ts +28 -939
- package/dist/.tsbuildinfo +0 -1
- package/dist/__tests__/load-asset-config.test.d.ts +0 -2
- package/dist/__tests__/load-asset-config.test.d.ts.map +0 -1
- package/dist/__tests__/scriptable-pack.test-d.d.ts +0 -2
- package/dist/__tests__/scriptable-pack.test-d.d.ts.map +0 -1
- package/dist/__tests__/scriptable-pack.unit.test.d.ts +0 -2
- package/dist/__tests__/scriptable-pack.unit.test.d.ts.map +0 -1
- package/dist/config.d.ts +0 -6
- package/dist/config.d.ts.map +0 -1
- package/dist/config.mjs +0 -29
- package/dist/config.mjs.map +0 -1
- package/src/__tests__/load-asset-config.test.ts +0 -121
- package/src/__tests__/scriptable-pack.test-d.ts +0 -131
- package/src/__tests__/scriptable-pack.unit.test.ts +0 -789
- package/src/config.ts +0 -36
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { createHash } from 'crypto';
|
|
2
2
|
import { existsSync } from 'fs';
|
|
3
|
-
import { realpath, readFile, stat, mkdtemp, rm
|
|
3
|
+
import { realpath, readFile, stat, mkdtemp, rm } from 'fs/promises';
|
|
4
4
|
import { tmpdir } from 'os';
|
|
5
|
-
import { resolve, dirname, extname, relative, sep
|
|
5
|
+
import { resolve, dirname, extname, relative, sep } from 'path';
|
|
6
6
|
import { fileURLToPath } from 'url';
|
|
7
7
|
import { Worker } from 'worker_threads';
|
|
8
|
-
import {
|
|
8
|
+
import { err, ok, AssetError, ImportError } from '@forgeax/engine-types';
|
|
9
9
|
import ts from 'typescript';
|
|
10
|
+
import { sha1 } from '@noble/hashes/legacy.js';
|
|
10
11
|
import { uuidv7obj } from 'uuidv7';
|
|
11
12
|
|
|
12
13
|
// src/scriptable-pack-node.ts
|
|
@@ -26,6 +27,8 @@ var PackError = class extends Error {
|
|
|
26
27
|
this.detail = args.detail;
|
|
27
28
|
}
|
|
28
29
|
};
|
|
30
|
+
|
|
31
|
+
// src/guid.ts
|
|
29
32
|
function brand(bytes) {
|
|
30
33
|
return bytes;
|
|
31
34
|
}
|
|
@@ -35,6 +38,10 @@ function bytesToDashForm(bytes) {
|
|
|
35
38
|
return `${h[bytes[0]]}${h[bytes[1]]}${h[bytes[2]]}${h[bytes[3]]}-${h[bytes[4]]}${h[bytes[5]]}-${h[bytes[6]]}${h[bytes[7]]}-${h[bytes[8]]}${h[bytes[9]]}-${h[bytes[10]]}${h[bytes[11]]}${h[bytes[12]]}${h[bytes[13]]}${h[bytes[14]]}${h[bytes[15]]}`;
|
|
36
39
|
}
|
|
37
40
|
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
41
|
+
var PACK_SOURCE_KEY_RE = /^[a-z0-9][a-z0-9._-]*(\/[a-z0-9][a-z0-9._-]*)*$/;
|
|
42
|
+
function isValidPackSourceKey(value) {
|
|
43
|
+
return typeof value === "string" && PACK_SOURCE_KEY_RE.test(value);
|
|
44
|
+
}
|
|
38
45
|
function isValidAssetGuidString(value) {
|
|
39
46
|
return typeof value === "string" && UUID_RE.test(value);
|
|
40
47
|
}
|
|
@@ -46,6 +53,33 @@ function dashFormToBytes(dashForm) {
|
|
|
46
53
|
}
|
|
47
54
|
return bytes;
|
|
48
55
|
}
|
|
56
|
+
function randomUuidBytes() {
|
|
57
|
+
const uuid = uuidv7obj();
|
|
58
|
+
const bytes = new Uint8Array(16);
|
|
59
|
+
bytes.set(uuid.bytes);
|
|
60
|
+
return bytes;
|
|
61
|
+
}
|
|
62
|
+
function derivedGuid(namespace, sourceKey) {
|
|
63
|
+
if (!(namespace instanceof Uint8Array) || namespace.byteLength !== 16) {
|
|
64
|
+
throw new TypeError("AssetGuid.derive requires a 16-byte PackageId");
|
|
65
|
+
}
|
|
66
|
+
if (!isValidPackSourceKey(sourceKey)) {
|
|
67
|
+
const error = new TypeError(
|
|
68
|
+
`AssetGuid.derive received invalid sourceKey ${JSON.stringify(sourceKey)}`
|
|
69
|
+
);
|
|
70
|
+
Object.defineProperty(error, "code", { value: "pack-source-key-invalid" });
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
73
|
+
const name = new TextEncoder().encode(sourceKey);
|
|
74
|
+
const input = new Uint8Array(namespace.byteLength + name.byteLength);
|
|
75
|
+
input.set(namespace, 0);
|
|
76
|
+
input.set(name, namespace.byteLength);
|
|
77
|
+
const digest = sha1(input);
|
|
78
|
+
const result = digest.slice(0, 16);
|
|
79
|
+
result[6] = (result[6] ?? 0) & 15 | 80;
|
|
80
|
+
result[8] = (result[8] ?? 0) & 63 | 128;
|
|
81
|
+
return brand(result);
|
|
82
|
+
}
|
|
49
83
|
var AssetGuid = {
|
|
50
84
|
/**
|
|
51
85
|
* Parse a 36-char RFC 4122 dash-form UUID string into an AssetGuid.
|
|
@@ -89,473 +123,513 @@ var AssetGuid = {
|
|
|
89
123
|
* Works in both Node.js and browser environments.
|
|
90
124
|
*/
|
|
91
125
|
random() {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
126
|
+
return brand(randomUuidBytes());
|
|
127
|
+
},
|
|
128
|
+
/** Derive the stable UUIDv5 projection for one Pack subject and sourceKey. */
|
|
129
|
+
derive(namespace, sourceKey) {
|
|
130
|
+
return derivedGuid(namespace, sourceKey);
|
|
96
131
|
}
|
|
97
132
|
};
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
"
|
|
102
|
-
"
|
|
103
|
-
"
|
|
104
|
-
"
|
|
105
|
-
"
|
|
106
|
-
"
|
|
107
|
-
"
|
|
108
|
-
"
|
|
109
|
-
"
|
|
110
|
-
"
|
|
111
|
-
"
|
|
112
|
-
"
|
|
113
|
-
"audio",
|
|
114
|
-
"particle-effect"
|
|
133
|
+
|
|
134
|
+
// src/pack-authoring.ts
|
|
135
|
+
var PACK_PARAMETER_TYPES = [
|
|
136
|
+
"bool",
|
|
137
|
+
"u32",
|
|
138
|
+
"i32",
|
|
139
|
+
"f32",
|
|
140
|
+
"f64",
|
|
141
|
+
"string",
|
|
142
|
+
"enum",
|
|
143
|
+
"vec2",
|
|
144
|
+
"vec3",
|
|
145
|
+
"vec4",
|
|
146
|
+
"color",
|
|
147
|
+
"asset-guid"
|
|
115
148
|
];
|
|
116
|
-
function
|
|
117
|
-
return SCRIPTABLE_PACK_ASSET_KINDS.includes(value);
|
|
118
|
-
}
|
|
119
|
-
function sceneComponentFieldType(value) {
|
|
120
|
-
if (typeof value === "string") return value;
|
|
121
|
-
if (isRecord(value) && typeof value.type === "string") return value.type;
|
|
122
|
-
return void 0;
|
|
123
|
-
}
|
|
124
|
-
function projectScriptablePackSceneComponents(components) {
|
|
125
|
-
return (components ?? []).map((component) => ({
|
|
126
|
-
name: component.name,
|
|
127
|
-
fields: Object.fromEntries(
|
|
128
|
-
Object.entries(component.fields).map(([fieldName, field]) => [
|
|
129
|
-
fieldName,
|
|
130
|
-
sceneComponentFieldType(field)
|
|
131
|
-
])
|
|
132
|
-
)
|
|
133
|
-
}));
|
|
134
|
-
}
|
|
135
|
-
var SCRIPTABLE_PACK_REQUEST_ID_SCHEMA = {
|
|
136
|
-
type: "string",
|
|
137
|
-
minLength: 1,
|
|
138
|
-
maxLength: 128,
|
|
139
|
-
pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$",
|
|
140
|
-
description: "Caller-minted identity for one idempotent ScriptablePack operation."
|
|
141
|
-
};
|
|
142
|
-
var SCRIPTABLE_PACK_SOURCE_PATH_SCHEMA = {
|
|
143
|
-
type: "string",
|
|
144
|
-
minLength: 8,
|
|
145
|
-
pattern: "^[^/].*\\.pack\\.ts$",
|
|
146
|
-
description: "Game-root-relative ScriptablePack source path."
|
|
147
|
-
};
|
|
148
|
-
var SCRIPTABLE_PACK_OWNER_GUID_SCHEMA = {
|
|
149
|
-
type: "string",
|
|
150
|
-
format: "uuid",
|
|
151
|
-
description: "Catalog GUID used to resolve the producer-owned source subject."
|
|
152
|
-
};
|
|
153
|
-
var SCRIPTABLE_PACK_REVISION_SCHEMA = {
|
|
154
|
-
type: "string",
|
|
155
|
-
minLength: 64,
|
|
156
|
-
maxLength: 64,
|
|
157
|
-
pattern: "^[a-f0-9]{64}$",
|
|
158
|
-
description: "SHA-256 source revision returned by preflight."
|
|
159
|
-
};
|
|
160
|
-
var SCRIPTABLE_PACK_SOURCE_KEY_SCHEMA = { type: "string", minLength: 1 };
|
|
161
|
-
var SCRIPTABLE_PACK_ASSET_KIND_SCHEMA = {
|
|
162
|
-
enum: SCRIPTABLE_PACK_ASSET_KINDS,
|
|
163
|
-
description: "Ordinary Asset kind supported by the ScriptablePack producer/loader matrix."
|
|
164
|
-
};
|
|
165
|
-
var SCRIPTABLE_PACK_NAME_SCHEMA = { type: "string", minLength: 1 };
|
|
166
|
-
function scriptablePackArgsSchema(properties, required, options = {}) {
|
|
167
|
-
const subjectRequired = ["requestId", ...required];
|
|
149
|
+
function authoringError(code, expected, hint, detail = {}, actual) {
|
|
168
150
|
return {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
...properties
|
|
175
|
-
},
|
|
176
|
-
required: subjectRequired,
|
|
177
|
-
...options.subject === false ? {} : {
|
|
178
|
-
anyOf: [
|
|
179
|
-
{ required: [...subjectRequired, "sourcePath"] },
|
|
180
|
-
{ required: [...subjectRequired, "ownerGuid"] },
|
|
181
|
-
...options.guidSubject === false ? [] : [{ required: [...subjectRequired, "guid"] }]
|
|
182
|
-
]
|
|
183
|
-
},
|
|
184
|
-
additionalProperties: false
|
|
151
|
+
code,
|
|
152
|
+
expected,
|
|
153
|
+
hint,
|
|
154
|
+
...actual === void 0 ? {} : { actual },
|
|
155
|
+
detail
|
|
185
156
|
};
|
|
186
157
|
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
id: "asset-source.create",
|
|
190
|
-
kind: "create",
|
|
191
|
-
domain: "session",
|
|
192
|
-
title: "Create Asset Source",
|
|
193
|
-
argsSchema: scriptablePackArgsSchema(
|
|
194
|
-
{
|
|
195
|
-
name: SCRIPTABLE_PACK_NAME_SCHEMA,
|
|
196
|
-
initialOutput: {
|
|
197
|
-
type: "object",
|
|
198
|
-
properties: {
|
|
199
|
-
sourceKey: SCRIPTABLE_PACK_SOURCE_KEY_SCHEMA,
|
|
200
|
-
kind: SCRIPTABLE_PACK_ASSET_KIND_SCHEMA,
|
|
201
|
-
name: SCRIPTABLE_PACK_NAME_SCHEMA
|
|
202
|
-
},
|
|
203
|
-
required: ["sourceKey", "kind"],
|
|
204
|
-
additionalProperties: false
|
|
205
|
-
}
|
|
206
|
-
},
|
|
207
|
-
["sourcePath", "initialOutput"],
|
|
208
|
-
{ subject: false }
|
|
209
|
-
)
|
|
210
|
-
},
|
|
211
|
-
{
|
|
212
|
-
id: "asset-source.add-output",
|
|
213
|
-
kind: "add-output",
|
|
214
|
-
domain: "session",
|
|
215
|
-
title: "Add Asset Source Output",
|
|
216
|
-
argsSchema: scriptablePackArgsSchema(
|
|
217
|
-
{
|
|
218
|
-
sourceKey: SCRIPTABLE_PACK_SOURCE_KEY_SCHEMA,
|
|
219
|
-
assetKind: SCRIPTABLE_PACK_ASSET_KIND_SCHEMA,
|
|
220
|
-
name: SCRIPTABLE_PACK_NAME_SCHEMA,
|
|
221
|
-
expectedRevision: SCRIPTABLE_PACK_REVISION_SCHEMA
|
|
222
|
-
},
|
|
223
|
-
["sourceKey", "assetKind", "expectedRevision"]
|
|
224
|
-
)
|
|
225
|
-
},
|
|
226
|
-
{
|
|
227
|
-
id: "asset-source.add-external-asset",
|
|
228
|
-
kind: "add-external-asset",
|
|
229
|
-
domain: "session",
|
|
230
|
-
title: "Add Asset Source External Asset",
|
|
231
|
-
argsSchema: scriptablePackArgsSchema(
|
|
232
|
-
{
|
|
233
|
-
alias: SCRIPTABLE_PACK_SOURCE_KEY_SCHEMA,
|
|
234
|
-
guid: { type: "string", format: "uuid" },
|
|
235
|
-
expectedRevision: SCRIPTABLE_PACK_REVISION_SCHEMA
|
|
236
|
-
},
|
|
237
|
-
["alias", "guid", "expectedRevision"],
|
|
238
|
-
{ guidSubject: false }
|
|
239
|
-
)
|
|
240
|
-
},
|
|
241
|
-
{
|
|
242
|
-
id: "asset-source.rename",
|
|
243
|
-
kind: "rename",
|
|
244
|
-
domain: "session",
|
|
245
|
-
title: "Rename Asset Source Display Name",
|
|
246
|
-
argsSchema: scriptablePackArgsSchema(
|
|
247
|
-
{
|
|
248
|
-
target: {
|
|
249
|
-
oneOf: [
|
|
250
|
-
{
|
|
251
|
-
type: "object",
|
|
252
|
-
properties: { kind: { const: "package" } },
|
|
253
|
-
required: ["kind"],
|
|
254
|
-
additionalProperties: false
|
|
255
|
-
},
|
|
256
|
-
{
|
|
257
|
-
type: "object",
|
|
258
|
-
properties: {
|
|
259
|
-
kind: { const: "output" },
|
|
260
|
-
sourceKey: SCRIPTABLE_PACK_SOURCE_KEY_SCHEMA
|
|
261
|
-
},
|
|
262
|
-
required: ["kind", "sourceKey"],
|
|
263
|
-
additionalProperties: false
|
|
264
|
-
}
|
|
265
|
-
]
|
|
266
|
-
},
|
|
267
|
-
name: SCRIPTABLE_PACK_NAME_SCHEMA,
|
|
268
|
-
expectedRevision: SCRIPTABLE_PACK_REVISION_SCHEMA
|
|
269
|
-
},
|
|
270
|
-
["target", "name", "expectedRevision"]
|
|
271
|
-
)
|
|
272
|
-
},
|
|
273
|
-
{
|
|
274
|
-
id: "asset-source.remove-output",
|
|
275
|
-
kind: "remove-output",
|
|
276
|
-
domain: "session",
|
|
277
|
-
title: "Remove Asset Source Output",
|
|
278
|
-
destructive: true,
|
|
279
|
-
argsSchema: scriptablePackArgsSchema(
|
|
280
|
-
{
|
|
281
|
-
sourceKey: SCRIPTABLE_PACK_SOURCE_KEY_SCHEMA,
|
|
282
|
-
confirmIncomingRefs: { type: "array", items: { type: "string" } },
|
|
283
|
-
expectedRevision: SCRIPTABLE_PACK_REVISION_SCHEMA
|
|
284
|
-
},
|
|
285
|
-
["sourceKey", "expectedRevision"]
|
|
286
|
-
)
|
|
287
|
-
},
|
|
288
|
-
{
|
|
289
|
-
id: "asset-source.clone",
|
|
290
|
-
kind: "clone",
|
|
291
|
-
domain: "session",
|
|
292
|
-
title: "Clone Asset Source",
|
|
293
|
-
argsSchema: scriptablePackArgsSchema(
|
|
294
|
-
{
|
|
295
|
-
targetPath: SCRIPTABLE_PACK_SOURCE_PATH_SCHEMA,
|
|
296
|
-
expectedRevision: SCRIPTABLE_PACK_REVISION_SCHEMA
|
|
297
|
-
},
|
|
298
|
-
["targetPath", "expectedRevision"]
|
|
299
|
-
)
|
|
300
|
-
},
|
|
301
|
-
{
|
|
302
|
-
id: "asset-source.rebuild",
|
|
303
|
-
kind: "rebuild",
|
|
304
|
-
domain: "session",
|
|
305
|
-
title: "Rebuild Asset Source",
|
|
306
|
-
argsSchema: scriptablePackArgsSchema({ expectedRevision: SCRIPTABLE_PACK_REVISION_SCHEMA }, [
|
|
307
|
-
"expectedRevision"
|
|
308
|
-
])
|
|
309
|
-
},
|
|
310
|
-
{
|
|
311
|
-
id: "asset-source.cold-cook",
|
|
312
|
-
kind: "cold-cook",
|
|
313
|
-
domain: "session",
|
|
314
|
-
title: "Cold Cook Asset Source",
|
|
315
|
-
argsSchema: scriptablePackArgsSchema({ expectedRevision: SCRIPTABLE_PACK_REVISION_SCHEMA }, [
|
|
316
|
-
"expectedRevision"
|
|
317
|
-
])
|
|
318
|
-
}
|
|
319
|
-
];
|
|
320
|
-
function actual(value) {
|
|
321
|
-
if (value === null) return "null";
|
|
322
|
-
if (Array.isArray(value)) return "array";
|
|
323
|
-
if (ArrayBuffer.isView(value)) return value.constructor.name;
|
|
324
|
-
return typeof value;
|
|
325
|
-
}
|
|
326
|
-
function invalid(propertyPath, expected, value, sourcePath) {
|
|
327
|
-
return err({
|
|
328
|
-
code: "pack-source-definition-invalid",
|
|
329
|
-
expected,
|
|
330
|
-
hint: "repair the default exported ScriptablePack definition, then inspect Meta again",
|
|
331
|
-
detail: {
|
|
332
|
-
...sourcePath === void 0 ? {} : { sourcePath },
|
|
333
|
-
propertyPath,
|
|
334
|
-
actual: actual(value)
|
|
335
|
-
}
|
|
336
|
-
});
|
|
158
|
+
function failure(error) {
|
|
159
|
+
return err(error);
|
|
337
160
|
}
|
|
338
161
|
function isRecord(value) {
|
|
339
162
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
340
163
|
}
|
|
341
|
-
function
|
|
164
|
+
function validateSceneComponents(value) {
|
|
165
|
+
if (value === void 0) return ok(void 0);
|
|
166
|
+
if (!Array.isArray(value)) {
|
|
167
|
+
return failure(
|
|
168
|
+
parameterFailure(
|
|
169
|
+
"$.sceneComponents",
|
|
170
|
+
"an array of component schema records",
|
|
171
|
+
value,
|
|
172
|
+
"sceneComponents is not an array"
|
|
173
|
+
)
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
const components = [];
|
|
177
|
+
const names = /* @__PURE__ */ new Set();
|
|
178
|
+
for (const [componentIndex, candidate] of value.entries()) {
|
|
179
|
+
if (!isRecord(candidate) || typeof candidate.name !== "string" || !isRecord(candidate.fields)) {
|
|
180
|
+
return failure(
|
|
181
|
+
parameterFailure(
|
|
182
|
+
`$.sceneComponents[${componentIndex}]`,
|
|
183
|
+
"a component record with name and fields",
|
|
184
|
+
candidate,
|
|
185
|
+
"scene component schema is malformed"
|
|
186
|
+
)
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
if (candidate.name.length === 0 || names.has(candidate.name)) {
|
|
190
|
+
return failure(
|
|
191
|
+
parameterFailure(
|
|
192
|
+
`$.sceneComponents[${componentIndex}].name`,
|
|
193
|
+
"a non-empty unique component name",
|
|
194
|
+
candidate.name,
|
|
195
|
+
"scene component names must be unique"
|
|
196
|
+
)
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
names.add(candidate.name);
|
|
200
|
+
const fields = {};
|
|
201
|
+
for (const [fieldName, field] of Object.entries(candidate.fields)) {
|
|
202
|
+
if (typeof field === "string" && field.length === 0 || typeof field !== "string" && (!isRecord(field) || typeof field.type !== "string" || field.type.length === 0)) {
|
|
203
|
+
return failure(
|
|
204
|
+
parameterFailure(
|
|
205
|
+
`$.sceneComponents[${componentIndex}].fields.${fieldName}`,
|
|
206
|
+
"a field type string or { type: string }",
|
|
207
|
+
field,
|
|
208
|
+
"scene component field schema is malformed"
|
|
209
|
+
)
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
fields[fieldName] = field;
|
|
213
|
+
}
|
|
214
|
+
components.push({ name: candidate.name, fields });
|
|
215
|
+
}
|
|
216
|
+
return ok(components);
|
|
217
|
+
}
|
|
218
|
+
function isPackageId(value) {
|
|
342
219
|
return value instanceof Uint8Array && value.byteLength === 16;
|
|
343
220
|
}
|
|
344
|
-
function
|
|
345
|
-
return
|
|
221
|
+
function cloneValue(value) {
|
|
222
|
+
if (value instanceof Uint8Array) return value.slice();
|
|
223
|
+
if (Array.isArray(value)) return value.map((item) => cloneValue(item));
|
|
224
|
+
return value;
|
|
346
225
|
}
|
|
347
|
-
function
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
);
|
|
354
|
-
const externalAssets = Object.fromEntries(
|
|
355
|
-
Object.entries(definition.externalAssets).map(([alias, guid]) => [
|
|
356
|
-
alias,
|
|
357
|
-
guid.slice()
|
|
358
|
-
])
|
|
359
|
-
);
|
|
360
|
-
const sceneComponents = definition.sceneComponents === void 0 ? void 0 : Object.freeze(
|
|
361
|
-
projectScriptablePackSceneComponents(definition.sceneComponents).map(
|
|
362
|
-
(component) => Object.freeze({
|
|
363
|
-
...component,
|
|
364
|
-
fields: Object.freeze({ ...component.fields })
|
|
365
|
-
})
|
|
366
|
-
)
|
|
226
|
+
function parameterFailure(propertyPath, expected, actual, reason) {
|
|
227
|
+
return authoringError(
|
|
228
|
+
"pack-parameter-invalid",
|
|
229
|
+
expected,
|
|
230
|
+
"repair the parameter declaration or the instance values, then inspect and rebuild",
|
|
231
|
+
{ propertyPath, reason, actual: typeof actual === "string" ? actual : JSON.stringify(actual) }
|
|
367
232
|
);
|
|
368
|
-
return Object.freeze({
|
|
369
|
-
...definition,
|
|
370
|
-
packageId: definition.packageId.slice(),
|
|
371
|
-
assets: Object.freeze(assets),
|
|
372
|
-
externalAssets: Object.freeze(externalAssets),
|
|
373
|
-
...sceneComponents === void 0 ? {} : { sceneComponents }
|
|
374
|
-
});
|
|
375
233
|
}
|
|
376
|
-
function
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
234
|
+
function numericType(type) {
|
|
235
|
+
return type === "u32" || type === "i32" || type === "f32" || type === "f64";
|
|
236
|
+
}
|
|
237
|
+
function vectorLength(type) {
|
|
238
|
+
switch (type) {
|
|
239
|
+
case "vec2":
|
|
240
|
+
return 2;
|
|
241
|
+
case "vec3":
|
|
242
|
+
return 3;
|
|
243
|
+
case "vec4":
|
|
244
|
+
case "color":
|
|
245
|
+
return 4;
|
|
246
|
+
default:
|
|
247
|
+
return void 0;
|
|
381
248
|
}
|
|
382
|
-
|
|
383
|
-
|
|
249
|
+
}
|
|
250
|
+
function parameterValueError(parameter, value, propertyPath) {
|
|
251
|
+
const { type } = parameter;
|
|
252
|
+
if (type === "bool" && typeof value !== "boolean") {
|
|
253
|
+
return parameterFailure(propertyPath, "a boolean", value, "bool value has the wrong type");
|
|
384
254
|
}
|
|
385
|
-
if (
|
|
386
|
-
|
|
255
|
+
if (numericType(type)) {
|
|
256
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
257
|
+
return parameterFailure(
|
|
258
|
+
propertyPath,
|
|
259
|
+
"a finite number",
|
|
260
|
+
value,
|
|
261
|
+
"numeric value is not finite"
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
if ((type === "u32" || type === "i32") && !Number.isInteger(value)) {
|
|
265
|
+
return parameterFailure(
|
|
266
|
+
propertyPath,
|
|
267
|
+
"an integer",
|
|
268
|
+
value,
|
|
269
|
+
"integer parameter received a fraction"
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
if (type === "u32" && (value < 0 || value > 4294967295)) {
|
|
273
|
+
return parameterFailure(
|
|
274
|
+
propertyPath,
|
|
275
|
+
"an unsigned 32-bit integer",
|
|
276
|
+
value,
|
|
277
|
+
"u32 value is outside [0, 2^32 - 1]"
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
if (type === "i32" && (value < -2147483648 || value > 2147483647)) {
|
|
281
|
+
return parameterFailure(
|
|
282
|
+
propertyPath,
|
|
283
|
+
"a signed 32-bit integer",
|
|
284
|
+
value,
|
|
285
|
+
"i32 value is outside [-2^31, 2^31 - 1]"
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
if (type === "f32" && !Number.isFinite(Math.fround(value))) {
|
|
289
|
+
return parameterFailure(
|
|
290
|
+
propertyPath,
|
|
291
|
+
"a finite IEEE-754 32-bit float",
|
|
292
|
+
value,
|
|
293
|
+
"f32 value overflows binary32"
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
if (parameter.minimum !== void 0 && value < parameter.minimum) {
|
|
297
|
+
return parameterFailure(
|
|
298
|
+
propertyPath,
|
|
299
|
+
`a number >= ${parameter.minimum}`,
|
|
300
|
+
value,
|
|
301
|
+
"value is below minimum"
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
if (parameter.maximum !== void 0 && value > parameter.maximum) {
|
|
305
|
+
return parameterFailure(
|
|
306
|
+
propertyPath,
|
|
307
|
+
`a number <= ${parameter.maximum}`,
|
|
308
|
+
value,
|
|
309
|
+
"value is above maximum"
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
return void 0;
|
|
387
313
|
}
|
|
388
|
-
if (
|
|
389
|
-
return
|
|
390
|
-
"$.assets",
|
|
391
|
-
"a non-empty sourceKey to descriptor object",
|
|
392
|
-
value.assets,
|
|
393
|
-
sourcePath
|
|
394
|
-
);
|
|
314
|
+
if (type === "string" && typeof value !== "string") {
|
|
315
|
+
return parameterFailure(propertyPath, "a string", value, "string parameter has the wrong type");
|
|
395
316
|
}
|
|
396
|
-
if (
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
317
|
+
if (type === "enum") {
|
|
318
|
+
if (typeof value !== "string") {
|
|
319
|
+
return parameterFailure(
|
|
320
|
+
propertyPath,
|
|
321
|
+
"one of the declared enum strings",
|
|
322
|
+
value,
|
|
323
|
+
"enum value has the wrong type"
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
if (parameter.values === void 0 || !parameter.values.includes(value)) {
|
|
327
|
+
return parameterFailure(
|
|
328
|
+
propertyPath,
|
|
329
|
+
"one of the declared enum values",
|
|
330
|
+
value,
|
|
331
|
+
"enum value is not declared"
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
return void 0;
|
|
403
335
|
}
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
336
|
+
const length = vectorLength(type);
|
|
337
|
+
if (length !== void 0) {
|
|
338
|
+
if (!Array.isArray(value) || value.length !== length || value.some((component) => typeof component !== "number" || !Number.isFinite(component))) {
|
|
339
|
+
return parameterFailure(
|
|
340
|
+
propertyPath,
|
|
341
|
+
`a finite numeric vector with ${length} components`,
|
|
342
|
+
value,
|
|
343
|
+
"vector value has the wrong shape"
|
|
411
344
|
);
|
|
412
345
|
}
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
sourcePath
|
|
421
|
-
);
|
|
422
|
-
}
|
|
423
|
-
if (typeof component.name !== "string" || component.name.trim().length === 0) {
|
|
424
|
-
return invalid(
|
|
425
|
-
`$.sceneComponents[${componentIndex}].name`,
|
|
426
|
-
"a non-empty component name",
|
|
427
|
-
component.name,
|
|
428
|
-
sourcePath
|
|
429
|
-
);
|
|
430
|
-
}
|
|
431
|
-
if (componentNames.has(component.name)) {
|
|
432
|
-
return invalid(
|
|
433
|
-
`$.sceneComponents[${componentIndex}].name`,
|
|
434
|
-
"component names to be unique within one ScriptablePack",
|
|
435
|
-
component.name,
|
|
436
|
-
sourcePath
|
|
437
|
-
);
|
|
438
|
-
}
|
|
439
|
-
componentNames.add(component.name);
|
|
440
|
-
if (!isRecord(component.fields)) {
|
|
441
|
-
return invalid(
|
|
442
|
-
`$.sceneComponents[${componentIndex}].fields`,
|
|
443
|
-
"a field-name to schema-type object",
|
|
444
|
-
component.fields,
|
|
445
|
-
sourcePath
|
|
446
|
-
);
|
|
447
|
-
}
|
|
448
|
-
for (const [fieldName, field] of Object.entries(component.fields)) {
|
|
449
|
-
const type = sceneComponentFieldType(field);
|
|
450
|
-
if (fieldName.trim().length === 0 || type === void 0 || type.trim().length === 0) {
|
|
451
|
-
return invalid(
|
|
452
|
-
`$.sceneComponents[${componentIndex}].fields`,
|
|
453
|
-
"field names with non-empty string schema types",
|
|
454
|
-
field,
|
|
455
|
-
sourcePath
|
|
456
|
-
);
|
|
457
|
-
}
|
|
458
|
-
}
|
|
346
|
+
if (type === "color" && value.some((component) => component < 0 || component > 1)) {
|
|
347
|
+
return parameterFailure(
|
|
348
|
+
propertyPath,
|
|
349
|
+
"an RGBA color with components in [0, 1]",
|
|
350
|
+
value,
|
|
351
|
+
"color component is outside [0, 1]"
|
|
352
|
+
);
|
|
459
353
|
}
|
|
354
|
+
return void 0;
|
|
460
355
|
}
|
|
461
|
-
if (
|
|
462
|
-
|
|
356
|
+
if (type === "asset-guid") {
|
|
357
|
+
const validString = typeof value === "string" && isValidAssetGuidString(value);
|
|
358
|
+
if (!(value instanceof Uint8Array && value.byteLength === 16) && !validString) {
|
|
359
|
+
return parameterFailure(
|
|
360
|
+
propertyPath,
|
|
361
|
+
"a 16-byte AssetGuid or UUID string",
|
|
362
|
+
value,
|
|
363
|
+
"asset GUID value is malformed"
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
return void 0;
|
|
463
367
|
}
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
368
|
+
return parameterFailure(
|
|
369
|
+
propertyPath,
|
|
370
|
+
"a supported Pack parameter type",
|
|
371
|
+
value,
|
|
372
|
+
"unknown parameter type"
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
function normalizeParameterValue(parameter, value, propertyPath) {
|
|
376
|
+
const error = parameterValueError(parameter, value, propertyPath);
|
|
377
|
+
if (error !== void 0) return failure(error);
|
|
378
|
+
if (parameter.type === "asset-guid" && typeof value === "string") {
|
|
379
|
+
const parsed = AssetGuid.parse(value);
|
|
380
|
+
if (!parsed.ok) {
|
|
381
|
+
return failure(
|
|
382
|
+
parameterFailure(
|
|
383
|
+
propertyPath,
|
|
384
|
+
"a valid AssetGuid UUID string",
|
|
385
|
+
value,
|
|
386
|
+
"asset GUID parser rejected the value"
|
|
387
|
+
)
|
|
388
|
+
);
|
|
469
389
|
}
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
390
|
+
return ok(parsed.value);
|
|
391
|
+
}
|
|
392
|
+
return ok(cloneValue(value));
|
|
393
|
+
}
|
|
394
|
+
function validateParameterDefinitions(parameters, propertyPath = "$.parameters") {
|
|
395
|
+
if (!Array.isArray(parameters) || parameters.length === 0) {
|
|
396
|
+
return failure(
|
|
397
|
+
parameterFailure(
|
|
398
|
+
propertyPath,
|
|
399
|
+
"a non-empty parameter descriptor array",
|
|
400
|
+
parameters,
|
|
401
|
+
"parameters must be explicit and non-empty"
|
|
402
|
+
)
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
const names = /* @__PURE__ */ new Set();
|
|
406
|
+
const normalized = [];
|
|
407
|
+
for (const [index, candidate] of parameters.entries()) {
|
|
408
|
+
const path = `${propertyPath}[${index}]`;
|
|
409
|
+
if (!isRecord(candidate))
|
|
410
|
+
return failure(
|
|
411
|
+
parameterFailure(
|
|
412
|
+
path,
|
|
413
|
+
"a parameter descriptor object",
|
|
414
|
+
candidate,
|
|
415
|
+
"descriptor is not an object"
|
|
416
|
+
)
|
|
417
|
+
);
|
|
418
|
+
const name = candidate.name;
|
|
419
|
+
if (typeof name !== "string" || !/^[A-Za-z][A-Za-z0-9_.-]*$/.test(name)) {
|
|
420
|
+
return failure(
|
|
421
|
+
parameterFailure(
|
|
422
|
+
`${path}.name`,
|
|
423
|
+
"a unique identifier-like parameter name",
|
|
424
|
+
name,
|
|
425
|
+
"parameter name is invalid"
|
|
426
|
+
)
|
|
476
427
|
);
|
|
477
428
|
}
|
|
478
|
-
if (
|
|
479
|
-
return
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
429
|
+
if (names.has(name))
|
|
430
|
+
return failure(
|
|
431
|
+
parameterFailure(
|
|
432
|
+
`${path}.name`,
|
|
433
|
+
"a unique parameter name",
|
|
434
|
+
name,
|
|
435
|
+
"parameter name is duplicated"
|
|
436
|
+
)
|
|
437
|
+
);
|
|
438
|
+
names.add(name);
|
|
439
|
+
const unknown = Object.keys(candidate).find(
|
|
440
|
+
(key) => !["name", "type", "default", "minimum", "maximum", "values", "kind"].includes(key)
|
|
441
|
+
);
|
|
442
|
+
if (unknown !== void 0) {
|
|
443
|
+
return failure(
|
|
444
|
+
parameterFailure(
|
|
445
|
+
`${path}.${unknown}`,
|
|
446
|
+
"only name, type, default, minimum, maximum, values, and kind fields",
|
|
447
|
+
candidate[unknown],
|
|
448
|
+
"parameter descriptors are a closed authoring schema"
|
|
449
|
+
)
|
|
484
450
|
);
|
|
485
451
|
}
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
452
|
+
const type = candidate.type;
|
|
453
|
+
if (typeof type !== "string" || !PACK_PARAMETER_TYPES.includes(type)) {
|
|
454
|
+
return failure(
|
|
455
|
+
parameterFailure(
|
|
456
|
+
`${path}.type`,
|
|
457
|
+
PACK_PARAMETER_TYPES.join(" | "),
|
|
458
|
+
type,
|
|
459
|
+
"parameter type is not supported"
|
|
460
|
+
)
|
|
492
461
|
);
|
|
493
462
|
}
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
463
|
+
const typed = {
|
|
464
|
+
...candidate,
|
|
465
|
+
name,
|
|
466
|
+
type
|
|
467
|
+
};
|
|
468
|
+
if (typed.values !== void 0) {
|
|
469
|
+
if (typed.type !== "enum" || !Array.isArray(typed.values) || typed.values.length === 0 || typed.values.some((item) => typeof item !== "string") || new Set(typed.values).size !== typed.values.length) {
|
|
470
|
+
return failure(
|
|
471
|
+
parameterFailure(
|
|
472
|
+
`${path}.values`,
|
|
473
|
+
"unique non-empty strings for enum",
|
|
474
|
+
typed.values,
|
|
475
|
+
"enum values are malformed"
|
|
476
|
+
)
|
|
477
|
+
);
|
|
478
|
+
}
|
|
479
|
+
} else if (typed.type === "enum") {
|
|
480
|
+
return failure(
|
|
481
|
+
parameterFailure(
|
|
482
|
+
`${path}.values`,
|
|
483
|
+
"a non-empty enum choices array",
|
|
484
|
+
typed.values,
|
|
485
|
+
"enum has no choices"
|
|
486
|
+
)
|
|
500
487
|
);
|
|
501
488
|
}
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
489
|
+
if (typed.kind !== void 0 && (typed.type !== "asset-guid" || typeof typed.kind !== "string")) {
|
|
490
|
+
return failure(
|
|
491
|
+
parameterFailure(
|
|
492
|
+
`${path}.kind`,
|
|
493
|
+
"a kind constraint only on asset-guid parameters",
|
|
494
|
+
typed.kind,
|
|
495
|
+
"kind is misplaced"
|
|
496
|
+
)
|
|
509
497
|
);
|
|
510
498
|
}
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
499
|
+
for (const field of ["minimum", "maximum"]) {
|
|
500
|
+
const bound = typed[field];
|
|
501
|
+
if (bound !== void 0 && (typeof bound !== "number" || !Number.isFinite(bound))) {
|
|
502
|
+
return failure(
|
|
503
|
+
parameterFailure(
|
|
504
|
+
`${path}.${field}`,
|
|
505
|
+
"a finite numeric bound",
|
|
506
|
+
bound,
|
|
507
|
+
"numeric bound is invalid"
|
|
508
|
+
)
|
|
509
|
+
);
|
|
510
|
+
}
|
|
517
511
|
}
|
|
518
|
-
if (!
|
|
519
|
-
return
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
512
|
+
if (!numericType(typed.type) && (typed.minimum !== void 0 || typed.maximum !== void 0)) {
|
|
513
|
+
return failure(
|
|
514
|
+
parameterFailure(
|
|
515
|
+
path,
|
|
516
|
+
"minimum/maximum only on numeric parameters",
|
|
517
|
+
typed,
|
|
518
|
+
"numeric bounds are not meaningful for this parameter type"
|
|
519
|
+
)
|
|
524
520
|
);
|
|
525
521
|
}
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
`$.externalAssets[${JSON.stringify(alias)}]`,
|
|
530
|
-
"a unique foreign GUID not owned by this package",
|
|
531
|
-
guidValue,
|
|
532
|
-
sourcePath
|
|
522
|
+
if (typed.minimum !== void 0 && typed.maximum !== void 0 && typed.minimum > typed.maximum) {
|
|
523
|
+
return failure(
|
|
524
|
+
parameterFailure(path, "minimum <= maximum", typed, "numeric bounds are reversed")
|
|
533
525
|
);
|
|
534
526
|
}
|
|
535
|
-
|
|
527
|
+
const defaultResult = normalizeParameterValue(typed, typed.default, `${path}.default`);
|
|
528
|
+
if (!defaultResult.ok) return defaultResult;
|
|
529
|
+
normalized.push({
|
|
530
|
+
...typed,
|
|
531
|
+
default: defaultResult.value,
|
|
532
|
+
...typed.values === void 0 ? {} : { values: Object.freeze([...typed.values]) }
|
|
533
|
+
});
|
|
536
534
|
}
|
|
537
|
-
return ok(
|
|
535
|
+
return ok(Object.freeze(normalized));
|
|
538
536
|
}
|
|
539
|
-
function
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
537
|
+
function validatePackDefinition(value, sourcePath) {
|
|
538
|
+
if (!isRecord(value))
|
|
539
|
+
return failure(
|
|
540
|
+
parameterFailure("$", "a Pack definition object", value, "definition is not an object")
|
|
541
|
+
);
|
|
542
|
+
const unknown = Object.keys(value).find(
|
|
543
|
+
(key) => !["schemaVersion", "packageId", "name", "parameters", "sceneComponents", "build"].includes(
|
|
544
|
+
key
|
|
545
|
+
)
|
|
546
|
+
);
|
|
547
|
+
if (unknown !== void 0) {
|
|
548
|
+
return failure(
|
|
549
|
+
parameterFailure(
|
|
550
|
+
`$.${unknown}`,
|
|
551
|
+
"only schemaVersion, packageId, name, parameters, sceneComponents, and build fields",
|
|
552
|
+
value[unknown],
|
|
553
|
+
"legacy static output declarations and external asset tables are not part of v2 authoring"
|
|
554
|
+
)
|
|
555
|
+
);
|
|
556
|
+
}
|
|
557
|
+
if (value.schemaVersion !== "2.0.0") {
|
|
558
|
+
return failure(
|
|
559
|
+
parameterFailure(
|
|
560
|
+
"$.schemaVersion",
|
|
561
|
+
"the literal '2.0.0'",
|
|
562
|
+
value.schemaVersion,
|
|
563
|
+
"authoring source schema is unsupported"
|
|
564
|
+
)
|
|
565
|
+
);
|
|
566
|
+
}
|
|
567
|
+
if (!isPackageId(value.packageId)) {
|
|
568
|
+
return failure(
|
|
569
|
+
authoringError(
|
|
570
|
+
"pack-package-id-invalid",
|
|
571
|
+
"a 16-byte PackageId",
|
|
572
|
+
"use definePackageId() for the stable source identity",
|
|
573
|
+
{ sourcePath, propertyPath: "$.packageId" },
|
|
574
|
+
typeof value.packageId === "string" ? value.packageId : void 0
|
|
575
|
+
)
|
|
576
|
+
);
|
|
577
|
+
}
|
|
578
|
+
if (value.name !== void 0 && typeof value.name !== "string") {
|
|
579
|
+
return failure(
|
|
580
|
+
parameterFailure("$.name", "a string when present", value.name, "display name is malformed")
|
|
581
|
+
);
|
|
582
|
+
}
|
|
583
|
+
if (typeof value.build !== "function") {
|
|
584
|
+
return failure(
|
|
585
|
+
parameterFailure("$.build", "a build function", value.build, "build is not callable")
|
|
586
|
+
);
|
|
587
|
+
}
|
|
588
|
+
const sceneComponents = validateSceneComponents(value.sceneComponents);
|
|
589
|
+
if (!sceneComponents.ok) return sceneComponents;
|
|
590
|
+
const hasParameters = Object.hasOwn(value, "parameters");
|
|
591
|
+
if (!hasParameters) {
|
|
592
|
+
return ok(
|
|
593
|
+
Object.freeze({
|
|
594
|
+
schemaVersion: "2.0.0",
|
|
595
|
+
packageId: value.packageId,
|
|
596
|
+
...value.name === void 0 ? {} : { name: value.name },
|
|
597
|
+
...sceneComponents.value === void 0 ? {} : { sceneComponents: sceneComponents.value },
|
|
598
|
+
build: value.build
|
|
599
|
+
})
|
|
600
|
+
);
|
|
601
|
+
}
|
|
602
|
+
const parameters = validateParameterDefinitions(value.parameters);
|
|
603
|
+
if (!parameters.ok) return parameters;
|
|
604
|
+
return ok(
|
|
605
|
+
Object.freeze({
|
|
606
|
+
schemaVersion: "2.0.0",
|
|
607
|
+
packageId: value.packageId,
|
|
608
|
+
...value.name === void 0 ? {} : { name: value.name },
|
|
609
|
+
...sceneComponents.value === void 0 ? {} : { sceneComponents: sceneComponents.value },
|
|
610
|
+
parameters: parameters.value,
|
|
611
|
+
build: value.build
|
|
612
|
+
})
|
|
613
|
+
);
|
|
558
614
|
}
|
|
615
|
+
var PACK_AUTHORING_OPERATION_IDS = [
|
|
616
|
+
"asset.list",
|
|
617
|
+
"asset.inspect",
|
|
618
|
+
"asset.resolve",
|
|
619
|
+
"asset.verify",
|
|
620
|
+
"asset-source.create",
|
|
621
|
+
"asset-source.clone",
|
|
622
|
+
"asset-source.create-instance",
|
|
623
|
+
"asset-source.apply-values",
|
|
624
|
+
"asset-source.rebuild",
|
|
625
|
+
"asset-source.cold-cook"
|
|
626
|
+
];
|
|
627
|
+
PACK_AUTHORING_OPERATION_IDS.map((id) => ({
|
|
628
|
+
id,
|
|
629
|
+
domain: id.startsWith("asset-source.") ? "session" : "asset",
|
|
630
|
+
readOnly: id.startsWith("asset."),
|
|
631
|
+
requiresRevision: !id.startsWith("asset.") && id !== "asset-source.create"
|
|
632
|
+
}));
|
|
559
633
|
|
|
560
634
|
// src/evidence/source-inventory.ts
|
|
561
635
|
function stableValue(value) {
|
|
@@ -651,50 +725,51 @@ async function inventoryScriptablePackSource(sourcePath, initialSourceText) {
|
|
|
651
725
|
}
|
|
652
726
|
function serializeFailure(value) {
|
|
653
727
|
if (value !== null && typeof value === "object") {
|
|
654
|
-
const
|
|
728
|
+
const failure2 = value;
|
|
655
729
|
return {
|
|
656
|
-
name:
|
|
657
|
-
code:
|
|
658
|
-
expected:
|
|
659
|
-
actual:
|
|
660
|
-
hint:
|
|
661
|
-
detail:
|
|
662
|
-
message:
|
|
730
|
+
name: failure2.name,
|
|
731
|
+
code: failure2.code,
|
|
732
|
+
expected: failure2.expected,
|
|
733
|
+
actual: failure2.actual,
|
|
734
|
+
hint: failure2.hint,
|
|
735
|
+
detail: failure2.detail,
|
|
736
|
+
message: failure2.message
|
|
663
737
|
};
|
|
664
738
|
}
|
|
665
739
|
return { message: String(value) };
|
|
666
740
|
}
|
|
667
741
|
function hydrateFailure(value) {
|
|
668
742
|
if (value === null || typeof value !== "object") return value;
|
|
669
|
-
const
|
|
670
|
-
if (
|
|
743
|
+
const failure2 = value;
|
|
744
|
+
if (failure2.name === "AssetError" && typeof failure2.code === "string" && typeof failure2.expected === "string" && typeof failure2.hint === "string") {
|
|
671
745
|
const args = {
|
|
672
|
-
code:
|
|
673
|
-
expected:
|
|
674
|
-
hint:
|
|
746
|
+
code: failure2.code,
|
|
747
|
+
expected: failure2.expected,
|
|
748
|
+
hint: failure2.hint
|
|
675
749
|
};
|
|
676
|
-
return
|
|
750
|
+
return failure2.detail === void 0 ? new AssetError(args) : new AssetError({
|
|
677
751
|
...args,
|
|
678
|
-
detail:
|
|
752
|
+
detail: failure2.detail
|
|
679
753
|
});
|
|
680
754
|
}
|
|
681
|
-
if (
|
|
755
|
+
if (failure2.name === "ImportError" && typeof failure2.code === "string" && typeof failure2.expected === "string" && typeof failure2.hint === "string" && failure2.detail !== void 0) {
|
|
682
756
|
return new ImportError({
|
|
683
|
-
code:
|
|
684
|
-
expected:
|
|
685
|
-
hint:
|
|
686
|
-
detail:
|
|
687
|
-
...typeof
|
|
757
|
+
code: failure2.code,
|
|
758
|
+
expected: failure2.expected,
|
|
759
|
+
hint: failure2.hint,
|
|
760
|
+
detail: failure2.detail,
|
|
761
|
+
...typeof failure2.actual === "string" ? { actual: failure2.actual } : {}
|
|
688
762
|
});
|
|
689
763
|
}
|
|
690
|
-
if (typeof
|
|
691
|
-
const error = new Error(
|
|
692
|
-
if (typeof
|
|
764
|
+
if (typeof failure2.message === "string") {
|
|
765
|
+
const error = new Error(failure2.message);
|
|
766
|
+
if (typeof failure2.name === "string") error.name = failure2.name;
|
|
693
767
|
return error;
|
|
694
768
|
}
|
|
695
769
|
return value;
|
|
696
770
|
}
|
|
697
771
|
var WorkerScriptablePackExecutor = class {
|
|
772
|
+
supportsPackParameters = true;
|
|
698
773
|
worker;
|
|
699
774
|
compileRoot;
|
|
700
775
|
nextBuildId = 0;
|
|
@@ -722,7 +797,7 @@ var WorkerScriptablePackExecutor = class {
|
|
|
722
797
|
resolveLoad({
|
|
723
798
|
default: value.definition === void 0 ? void 0 : {
|
|
724
799
|
...value.definition,
|
|
725
|
-
build: (
|
|
800
|
+
build: (readByGuid, context) => this.runBuild({ readByGuid }, context)
|
|
726
801
|
}
|
|
727
802
|
});
|
|
728
803
|
} else if (value.kind === "load-threw") {
|
|
@@ -754,7 +829,7 @@ var WorkerScriptablePackExecutor = class {
|
|
|
754
829
|
})();
|
|
755
830
|
return this.disposal;
|
|
756
831
|
}
|
|
757
|
-
runBuild(reader) {
|
|
832
|
+
runBuild(reader, context) {
|
|
758
833
|
const worker = this.worker;
|
|
759
834
|
if (worker === void 0) return Promise.reject(new Error("ScriptablePack worker is closed"));
|
|
760
835
|
if (this.build !== void 0)
|
|
@@ -762,7 +837,7 @@ var WorkerScriptablePackExecutor = class {
|
|
|
762
837
|
const id = this.nextBuildId++;
|
|
763
838
|
return new Promise((resolveBuild, rejectBuild) => {
|
|
764
839
|
this.build = { id, reader, resolve: resolveBuild, reject: rejectBuild };
|
|
765
|
-
worker.postMessage({ kind: "build", buildId: id });
|
|
840
|
+
worker.postMessage({ kind: "build", buildId: id, context });
|
|
766
841
|
});
|
|
767
842
|
}
|
|
768
843
|
onMessage(message) {
|
|
@@ -820,6 +895,7 @@ var ReusableWorkerScriptablePackExecutor = class {
|
|
|
820
895
|
}
|
|
821
896
|
maxTasksPerWorker;
|
|
822
897
|
release;
|
|
898
|
+
supportsPackParameters = true;
|
|
823
899
|
worker;
|
|
824
900
|
compileRootReady = mkdtemp(resolve(tmpdir(), "forgeax-scriptable-pack-pool-"));
|
|
825
901
|
compileRoot;
|
|
@@ -903,7 +979,7 @@ var ReusableWorkerScriptablePackExecutor = class {
|
|
|
903
979
|
this.build = void 0;
|
|
904
980
|
building?.reject(error);
|
|
905
981
|
}
|
|
906
|
-
runBuild(reader) {
|
|
982
|
+
runBuild(reader, context) {
|
|
907
983
|
if (this.released || this.disposal !== void 0) {
|
|
908
984
|
return Promise.reject(new Error("ScriptablePack executor is not leased"));
|
|
909
985
|
}
|
|
@@ -914,7 +990,7 @@ var ReusableWorkerScriptablePackExecutor = class {
|
|
|
914
990
|
return new Promise((resolveBuild, rejectBuild) => {
|
|
915
991
|
this.build = { id, reader, resolve: resolveBuild, reject: rejectBuild };
|
|
916
992
|
try {
|
|
917
|
-
this.worker.postMessage({ kind: "build", buildId: id });
|
|
993
|
+
this.worker.postMessage({ kind: "build", buildId: id, context });
|
|
918
994
|
} catch (error) {
|
|
919
995
|
this.build = void 0;
|
|
920
996
|
rejectBuild(error);
|
|
@@ -931,7 +1007,7 @@ var ReusableWorkerScriptablePackExecutor = class {
|
|
|
931
1007
|
loading.resolve({
|
|
932
1008
|
default: value.definition === void 0 ? void 0 : {
|
|
933
1009
|
...value.definition,
|
|
934
|
-
build: (
|
|
1010
|
+
build: (readByGuid, context) => this.runBuild({ readByGuid }, context)
|
|
935
1011
|
}
|
|
936
1012
|
});
|
|
937
1013
|
return;
|
|
@@ -1036,11 +1112,11 @@ var DefaultScriptablePackModuleExecutorPool = class {
|
|
|
1036
1112
|
function createScriptablePackModuleExecutorPool(options = {}) {
|
|
1037
1113
|
return new DefaultScriptablePackModuleExecutorPool(options);
|
|
1038
1114
|
}
|
|
1039
|
-
function
|
|
1040
|
-
return
|
|
1041
|
-
code: "pack-
|
|
1042
|
-
expected: "a trusted
|
|
1043
|
-
hint: "repair the module
|
|
1115
|
+
function scriptablePackLoadFailure(sourcePath, reason, phase, diagnostic, timeoutMs) {
|
|
1116
|
+
return {
|
|
1117
|
+
code: "pack-parameter-invalid",
|
|
1118
|
+
expected: "a trusted Pack v2 authoring module with a valid default export",
|
|
1119
|
+
hint: "repair the source module or its build timeout, then inspect the Pack again",
|
|
1044
1120
|
detail: {
|
|
1045
1121
|
sourcePath,
|
|
1046
1122
|
reason,
|
|
@@ -1048,20 +1124,6 @@ function loadFailure(sourcePath, reason, phase, diagnostic, timeoutMs) {
|
|
|
1048
1124
|
diagnostic,
|
|
1049
1125
|
...timeoutMs === void 0 ? {} : { timeoutMs }
|
|
1050
1126
|
}
|
|
1051
|
-
});
|
|
1052
|
-
}
|
|
1053
|
-
function buildTimeoutFailure(sourcePath, timeoutMs) {
|
|
1054
|
-
return {
|
|
1055
|
-
code: "pack-source-load-failed",
|
|
1056
|
-
expected: "a ScriptablePack build to settle within the configured build timeout",
|
|
1057
|
-
hint: "repair the authored build, then retry through a fresh ScriptablePack generation",
|
|
1058
|
-
detail: {
|
|
1059
|
-
sourcePath,
|
|
1060
|
-
reason: "timeout",
|
|
1061
|
-
phase: "build",
|
|
1062
|
-
timeoutMs,
|
|
1063
|
-
diagnostic: `ScriptablePack build exceeded ${timeoutMs}ms`
|
|
1064
|
-
}
|
|
1065
1127
|
};
|
|
1066
1128
|
}
|
|
1067
1129
|
async function loadScriptablePack(sourcePath, options = {}) {
|
|
@@ -1080,34 +1142,49 @@ async function loadScriptablePack(sourcePath, options = {}) {
|
|
|
1080
1142
|
]);
|
|
1081
1143
|
if (loaded === timeoutSignal) {
|
|
1082
1144
|
disposeReason = "timeout";
|
|
1083
|
-
return
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1145
|
+
return err(
|
|
1146
|
+
scriptablePackLoadFailure(
|
|
1147
|
+
sourcePath,
|
|
1148
|
+
"timeout",
|
|
1149
|
+
"module-load",
|
|
1150
|
+
`Pack module initialization exceeded ${timeoutMs}ms`,
|
|
1151
|
+
timeoutMs
|
|
1152
|
+
)
|
|
1089
1153
|
);
|
|
1090
1154
|
}
|
|
1091
1155
|
const moduleValue = loaded !== null && typeof loaded === "object" && "default" in loaded ? loaded.default : void 0;
|
|
1092
|
-
const validated =
|
|
1156
|
+
const validated = validatePackDefinition(moduleValue, sourcePath);
|
|
1093
1157
|
if (!validated.ok) {
|
|
1094
1158
|
disposeReason = "failure";
|
|
1095
1159
|
return validated;
|
|
1096
1160
|
}
|
|
1097
1161
|
if (options.metadataOnly === true) {
|
|
1098
1162
|
disposeReason = "complete";
|
|
1099
|
-
return validated;
|
|
1163
|
+
return ok(validated.value);
|
|
1100
1164
|
}
|
|
1101
1165
|
const definition = validated.value;
|
|
1166
|
+
const build = definition.build;
|
|
1102
1167
|
return ok({
|
|
1103
1168
|
...definition,
|
|
1104
|
-
build: async (
|
|
1169
|
+
build: async (context) => {
|
|
1105
1170
|
let buildTimedOut = false;
|
|
1106
1171
|
let buildTimeout;
|
|
1107
|
-
const timeoutResult = err(
|
|
1172
|
+
const timeoutResult = err(
|
|
1173
|
+
scriptablePackLoadFailure(
|
|
1174
|
+
sourcePath,
|
|
1175
|
+
"timeout",
|
|
1176
|
+
"build",
|
|
1177
|
+
`ScriptablePack build exceeded ${buildTimeoutMs}ms`,
|
|
1178
|
+
buildTimeoutMs
|
|
1179
|
+
)
|
|
1180
|
+
);
|
|
1108
1181
|
try {
|
|
1182
|
+
const invocation = "supportsPackParameters" in executor && executor.supportsPackParameters === true ? build(context.readByGuid, {
|
|
1183
|
+
packageId: [...context.packageId],
|
|
1184
|
+
..."values" in context && context.values !== void 0 ? { values: context.values } : {}
|
|
1185
|
+
}) : definition.build(context);
|
|
1109
1186
|
const built = await Promise.race([
|
|
1110
|
-
Promise.resolve(
|
|
1187
|
+
Promise.resolve(invocation),
|
|
1111
1188
|
new Promise((resolve3) => {
|
|
1112
1189
|
buildTimeout = setTimeout(() => {
|
|
1113
1190
|
buildTimedOut = true;
|
|
@@ -1130,545 +1207,21 @@ async function loadScriptablePack(sourcePath, options = {}) {
|
|
|
1130
1207
|
}
|
|
1131
1208
|
});
|
|
1132
1209
|
} catch (error) {
|
|
1133
|
-
const diagnostic = error instanceof Error ? error.message : String(error);
|
|
1134
1210
|
disposeReason = "failure";
|
|
1135
|
-
return
|
|
1211
|
+
return err(
|
|
1212
|
+
scriptablePackLoadFailure(
|
|
1213
|
+
sourcePath,
|
|
1214
|
+
"module-load",
|
|
1215
|
+
"module-load",
|
|
1216
|
+
error instanceof Error ? error.message : String(error)
|
|
1217
|
+
)
|
|
1218
|
+
);
|
|
1136
1219
|
} finally {
|
|
1137
1220
|
if (timeout !== void 0) clearTimeout(timeout);
|
|
1138
1221
|
if (disposeReason !== void 0) await executor.dispose?.(disposeReason);
|
|
1139
1222
|
}
|
|
1140
1223
|
}
|
|
1141
|
-
var CANONICAL_SCAFFOLD_MARKER = "// @forgeax-scriptable-pack canonical-v1";
|
|
1142
|
-
var CANONICAL_MANIFEST_NAME = "canonicalManifest";
|
|
1143
|
-
var CANONICAL_MUTABLE_KINDS = /* @__PURE__ */ new Set(["scene", "mesh", "material"]);
|
|
1144
|
-
function authoringError(code, input) {
|
|
1145
|
-
return {
|
|
1146
|
-
code,
|
|
1147
|
-
expected: input.expected,
|
|
1148
|
-
...input.actual === void 0 ? {} : { actual: input.actual },
|
|
1149
|
-
hint: input.hint,
|
|
1150
|
-
retryable: input.retryable,
|
|
1151
|
-
recoveryActions: input.recoveryActions,
|
|
1152
|
-
detail: {
|
|
1153
|
-
...input.requestId === void 0 ? {} : { requestId: input.requestId },
|
|
1154
|
-
...input.sourcePath === void 0 ? {} : { sourcePath: input.sourcePath },
|
|
1155
|
-
...input.sourceKey === void 0 ? {} : { sourceKey: input.sourceKey },
|
|
1156
|
-
...input.incomingRefs === void 0 ? {} : { incomingRefs: input.incomingRefs }
|
|
1157
|
-
}
|
|
1158
|
-
};
|
|
1159
|
-
}
|
|
1160
|
-
function confinedSourcePath(gameRoot, sourcePath) {
|
|
1161
|
-
const root = resolve(gameRoot);
|
|
1162
|
-
const absolute = resolve(root, sourcePath);
|
|
1163
|
-
const rel = relative(root, absolute);
|
|
1164
|
-
if (rel.length === 0 || rel === ".." || rel.startsWith(`..${sep}`) || isAbsolute(sourcePath) || !rel.endsWith(".pack.ts")) {
|
|
1165
|
-
return err(
|
|
1166
|
-
authoringError("pack-source-path-invalid", {
|
|
1167
|
-
expected: "a game-root-relative *.pack.ts path confined to the selected game",
|
|
1168
|
-
actual: sourcePath,
|
|
1169
|
-
hint: "choose a relative ScriptablePack source path inside the selected game root",
|
|
1170
|
-
retryable: false,
|
|
1171
|
-
recoveryActions: ["choose-game-relative-source-path"],
|
|
1172
|
-
sourcePath
|
|
1173
|
-
})
|
|
1174
|
-
);
|
|
1175
|
-
}
|
|
1176
|
-
return ok({ absolute, relative: rel.split(sep).join("/") });
|
|
1177
|
-
}
|
|
1178
|
-
function revisionOf(source) {
|
|
1179
|
-
return createHash("sha256").update(source).digest("hex");
|
|
1180
|
-
}
|
|
1181
|
-
function unwrapExpression(expression) {
|
|
1182
|
-
if (ts.isAsExpression(expression) || ts.isSatisfiesExpression(expression) || ts.isParenthesizedExpression(expression)) {
|
|
1183
|
-
return unwrapExpression(expression.expression);
|
|
1184
|
-
}
|
|
1185
|
-
return expression;
|
|
1186
|
-
}
|
|
1187
|
-
function literalValue(expression) {
|
|
1188
|
-
const value = unwrapExpression(expression);
|
|
1189
|
-
if (ts.isStringLiteral(value) || ts.isNoSubstitutionTemplateLiteral(value)) return value.text;
|
|
1190
|
-
if (value.kind === ts.SyntaxKind.TrueKeyword) return true;
|
|
1191
|
-
if (value.kind === ts.SyntaxKind.FalseKeyword) return false;
|
|
1192
|
-
if (value.kind === ts.SyntaxKind.NullKeyword) return null;
|
|
1193
|
-
if (ts.isNumericLiteral(value)) return Number(value.text);
|
|
1194
|
-
if (ts.isArrayLiteralExpression(value)) return value.elements.map(literalValue);
|
|
1195
|
-
if (ts.isObjectLiteralExpression(value)) {
|
|
1196
|
-
const result = {};
|
|
1197
|
-
for (const property of value.properties) {
|
|
1198
|
-
if (!ts.isPropertyAssignment(property))
|
|
1199
|
-
throw new TypeError("manifest properties must be data");
|
|
1200
|
-
const name = property.name;
|
|
1201
|
-
const key = ts.isIdentifier(name) || ts.isStringLiteral(name) || ts.isNumericLiteral(name) ? name.text : void 0;
|
|
1202
|
-
if (key === void 0) throw new TypeError("manifest property names must be static");
|
|
1203
|
-
result[key] = literalValue(property.initializer);
|
|
1204
|
-
}
|
|
1205
|
-
return result;
|
|
1206
|
-
}
|
|
1207
|
-
throw new TypeError("manifest values must be literal data");
|
|
1208
|
-
}
|
|
1209
|
-
function parseCanonicalManifest(source, sourcePath) {
|
|
1210
|
-
if (!source.startsWith(`${CANONICAL_SCAFFOLD_MARKER}
|
|
1211
|
-
`)) return void 0;
|
|
1212
|
-
const file = ts.createSourceFile(
|
|
1213
|
-
sourcePath,
|
|
1214
|
-
source,
|
|
1215
|
-
ts.ScriptTarget.Latest,
|
|
1216
|
-
true,
|
|
1217
|
-
ts.ScriptKind.TS
|
|
1218
|
-
);
|
|
1219
|
-
for (const statement of file.statements) {
|
|
1220
|
-
if (!ts.isVariableStatement(statement)) continue;
|
|
1221
|
-
for (const declaration of statement.declarationList.declarations) {
|
|
1222
|
-
if (ts.isIdentifier(declaration.name) && declaration.name.text === CANONICAL_MANIFEST_NAME && declaration.initializer !== void 0) {
|
|
1223
|
-
const value = literalValue(declaration.initializer);
|
|
1224
|
-
if (value === null || typeof value !== "object") return void 0;
|
|
1225
|
-
return value;
|
|
1226
|
-
}
|
|
1227
|
-
}
|
|
1228
|
-
}
|
|
1229
|
-
return void 0;
|
|
1230
|
-
}
|
|
1231
|
-
function guidExpression(value) {
|
|
1232
|
-
return `parseGuid(${JSON.stringify(value)})`;
|
|
1233
|
-
}
|
|
1234
|
-
function renderCanonicalScriptablePack(manifest) {
|
|
1235
|
-
const data = JSON.stringify(manifest, null, 2);
|
|
1236
|
-
return `${CANONICAL_SCAFFOLD_MARKER}
|
|
1237
|
-
import { AssetGuid } from '@forgeax/engine-pack/guid';
|
|
1238
|
-
import { buildMeshAttributeMapForUvSets } from '@forgeax/engine-geometry';
|
|
1239
|
-
import type { ScriptablePackDefinition } from '@forgeax/engine-pack/source';
|
|
1240
|
-
import type { Asset, AssetGuid as AssetGuidType } from '@forgeax/engine-types';
|
|
1241
|
-
import { ok } from '@forgeax/engine-types';
|
|
1242
|
-
|
|
1243
|
-
const ${CANONICAL_MANIFEST_NAME} = ${data} as const;
|
|
1244
|
-
|
|
1245
|
-
function parseGuid(value: string): AssetGuidType {
|
|
1246
|
-
const parsed = AssetGuid.parse(value);
|
|
1247
|
-
if (!parsed.ok) throw parsed.error;
|
|
1248
|
-
return parsed.value;
|
|
1249
|
-
}
|
|
1250
|
-
|
|
1251
|
-
function createOutput(kind: string): Asset {
|
|
1252
|
-
switch (kind) {
|
|
1253
|
-
case 'scene':
|
|
1254
|
-
return { kind: 'scene', entities: [], mounts: [] };
|
|
1255
|
-
case 'material':
|
|
1256
|
-
return { kind: 'material', values: {} };
|
|
1257
|
-
case 'mesh': {
|
|
1258
|
-
const vertices = new Float32Array([
|
|
1259
|
-
0, 0.7, 0, 0, 0, 1, 0.5, 1, 0, 0, 0, 1,
|
|
1260
|
-
-0.7, -0.6, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1,
|
|
1261
|
-
0.7, -0.6, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1,
|
|
1262
|
-
]);
|
|
1263
|
-
return {
|
|
1264
|
-
kind: 'mesh',
|
|
1265
|
-
vertices,
|
|
1266
|
-
indices: new Uint16Array([0, 1, 2]),
|
|
1267
|
-
attributes: { ...buildMeshAttributeMapForUvSets(1), position: vertices },
|
|
1268
|
-
aabb: new Float32Array([-0.7, -0.6, 0, 0.7, 0.7, 0]),
|
|
1269
|
-
submeshes: [{ indexOffset: 0, indexCount: 3, vertexCount: 3, topology: 'triangle-list', materialSlot: 0 }],
|
|
1270
|
-
materialSlots: [{ slotName: 'Default' }],
|
|
1271
|
-
};
|
|
1272
|
-
}
|
|
1273
|
-
default:
|
|
1274
|
-
throw new Error(\`canonical ScriptablePack output kind is not mutable: \${kind}\`);
|
|
1275
|
-
}
|
|
1276
|
-
}
|
|
1277
|
-
|
|
1278
|
-
const assets = Object.fromEntries(
|
|
1279
|
-
Object.entries(${CANONICAL_MANIFEST_NAME}.assets).map(([sourceKey, asset]) => [
|
|
1280
|
-
sourceKey,
|
|
1281
|
-
{ ...asset, guid: parseGuid(asset.guid) },
|
|
1282
|
-
]),
|
|
1283
|
-
);
|
|
1284
|
-
const externalAssets = Object.fromEntries(
|
|
1285
|
-
Object.entries(${CANONICAL_MANIFEST_NAME}.externalAssets).map(([alias, guid]) => [alias, parseGuid(guid)]),
|
|
1286
|
-
);
|
|
1287
|
-
|
|
1288
|
-
export default {
|
|
1289
|
-
schemaVersion: '1.0.0',
|
|
1290
|
-
packageId: ${guidExpression(manifest.packageId)},
|
|
1291
|
-
${manifest.name === void 0 ? "" : `name: ${JSON.stringify(manifest.name)},
|
|
1292
|
-
`}assets,
|
|
1293
|
-
externalAssets,
|
|
1294
|
-
build: () => ok(Object.fromEntries(Object.entries(assets).map(([sourceKey, asset]) => [sourceKey, createOutput(asset.kind)])) as Record<string, Asset>),
|
|
1295
|
-
} satisfies ScriptablePackDefinition;
|
|
1296
|
-
`;
|
|
1297
|
-
}
|
|
1298
|
-
function definitionFromCanonical(manifest) {
|
|
1299
|
-
const packageId = AssetGuid.parse(manifest.packageId);
|
|
1300
|
-
if (!packageId.ok) return invalidCanonicalGuid("$.packageId", manifest.packageId);
|
|
1301
|
-
const assets = {};
|
|
1302
|
-
for (const [sourceKey, output] of Object.entries(manifest.assets)) {
|
|
1303
|
-
const guid = AssetGuid.parse(output.guid);
|
|
1304
|
-
if (!guid.ok)
|
|
1305
|
-
return invalidCanonicalGuid(`$.assets[${JSON.stringify(sourceKey)}].guid`, output.guid);
|
|
1306
|
-
assets[sourceKey] = {
|
|
1307
|
-
guid: guid.value,
|
|
1308
|
-
kind: output.kind,
|
|
1309
|
-
...output.name === void 0 ? {} : { name: output.name }
|
|
1310
|
-
};
|
|
1311
|
-
}
|
|
1312
|
-
const externalAssets = {};
|
|
1313
|
-
for (const [alias, value] of Object.entries(manifest.externalAssets)) {
|
|
1314
|
-
const guid = AssetGuid.parse(value);
|
|
1315
|
-
if (!guid.ok) return invalidCanonicalGuid(`$.externalAssets[${JSON.stringify(alias)}]`, value);
|
|
1316
|
-
externalAssets[alias] = guid.value;
|
|
1317
|
-
}
|
|
1318
|
-
return validateScriptablePackDefinition({
|
|
1319
|
-
schemaVersion: "1.0.0",
|
|
1320
|
-
packageId: packageId.value,
|
|
1321
|
-
...manifest.name === void 0 ? {} : { name: manifest.name },
|
|
1322
|
-
assets,
|
|
1323
|
-
externalAssets,
|
|
1324
|
-
build: () => ok({})
|
|
1325
|
-
});
|
|
1326
|
-
}
|
|
1327
|
-
function invalidCanonicalGuid(propertyPath, actual2) {
|
|
1328
|
-
return err({
|
|
1329
|
-
code: "pack-source-definition-invalid",
|
|
1330
|
-
expected: "a canonical UUID string",
|
|
1331
|
-
hint: "repair the canonical scaffold manifest GUID and inspect Meta again",
|
|
1332
|
-
detail: { propertyPath, actual: actual2 }
|
|
1333
|
-
});
|
|
1334
|
-
}
|
|
1335
|
-
function invalidCanonicalSource(sourcePath, error) {
|
|
1336
|
-
return err({
|
|
1337
|
-
code: "pack-source-definition-invalid",
|
|
1338
|
-
expected: "a literal canonical-v1 manifest with the required package/assets/external fields",
|
|
1339
|
-
hint: "repair the canonical scaffold or open it as custom source without structured mutation",
|
|
1340
|
-
detail: {
|
|
1341
|
-
sourcePath,
|
|
1342
|
-
propertyPath: "$.canonicalManifest",
|
|
1343
|
-
actual: error instanceof Error ? error.message : String(error)
|
|
1344
|
-
}
|
|
1345
|
-
});
|
|
1346
|
-
}
|
|
1347
|
-
async function atomicWrite(path, source) {
|
|
1348
|
-
await mkdir(dirname(path), { recursive: true });
|
|
1349
|
-
const temporary = `${path}.${process.pid}.${Date.now()}.tmp`;
|
|
1350
|
-
try {
|
|
1351
|
-
await writeFile(temporary, source, { encoding: "utf8", flag: "wx" });
|
|
1352
|
-
await rename(temporary, path);
|
|
1353
|
-
} catch (error) {
|
|
1354
|
-
await rm(temporary, { force: true });
|
|
1355
|
-
throw error;
|
|
1356
|
-
}
|
|
1357
|
-
}
|
|
1358
|
-
function sameStrings(left, right) {
|
|
1359
|
-
return [...left].sort().join("\0") === [...right].sort().join("\0");
|
|
1360
|
-
}
|
|
1361
|
-
function createFileSystemScriptablePackAuthoringPort(options) {
|
|
1362
|
-
const resolvePath = (sourcePath) => confinedSourcePath(options.gameRoot, sourcePath);
|
|
1363
|
-
async function readSource(sourcePath) {
|
|
1364
|
-
const path = resolvePath(sourcePath);
|
|
1365
|
-
if (!path.ok) return path;
|
|
1366
|
-
try {
|
|
1367
|
-
const source = await readFile(path.value.absolute, "utf8");
|
|
1368
|
-
return ok({ ...path.value, source, revision: revisionOf(source) });
|
|
1369
|
-
} catch (error) {
|
|
1370
|
-
return err(
|
|
1371
|
-
authoringError("pack-source-write-failed", {
|
|
1372
|
-
expected: "a readable ScriptablePack source file",
|
|
1373
|
-
actual: error instanceof Error ? error.message : String(error),
|
|
1374
|
-
hint: "restore the source file or create it through the ScriptablePack gateway",
|
|
1375
|
-
retryable: true,
|
|
1376
|
-
recoveryActions: ["inspect-source-path", "retry"],
|
|
1377
|
-
sourcePath
|
|
1378
|
-
})
|
|
1379
|
-
);
|
|
1380
|
-
}
|
|
1381
|
-
}
|
|
1382
|
-
async function inspect(sourcePath) {
|
|
1383
|
-
const read = await readSource(sourcePath);
|
|
1384
|
-
if (!read.ok) return read;
|
|
1385
|
-
let canonical;
|
|
1386
|
-
try {
|
|
1387
|
-
canonical = parseCanonicalManifest(read.value.source, read.value.relative);
|
|
1388
|
-
} catch (error) {
|
|
1389
|
-
return invalidCanonicalSource(read.value.relative, error);
|
|
1390
|
-
}
|
|
1391
|
-
const loaded = canonical === void 0 ? await loadScriptablePack(read.value.absolute, { metadataOnly: true }) : definitionFromCanonical(canonical);
|
|
1392
|
-
if (!loaded.ok) return loaded;
|
|
1393
|
-
return ok({
|
|
1394
|
-
revision: read.value.revision,
|
|
1395
|
-
meta: projectScriptablePackMeta(loaded.value, read.value.relative)
|
|
1396
|
-
});
|
|
1397
|
-
}
|
|
1398
|
-
async function preflight(sourcePath) {
|
|
1399
|
-
const read = await readSource(sourcePath);
|
|
1400
|
-
if (!read.ok) return read;
|
|
1401
|
-
const inspected = await inspect(sourcePath);
|
|
1402
|
-
if (!inspected.ok) return inspected;
|
|
1403
|
-
let canonical;
|
|
1404
|
-
try {
|
|
1405
|
-
canonical = parseCanonicalManifest(read.value.source, read.value.relative);
|
|
1406
|
-
} catch (error) {
|
|
1407
|
-
return invalidCanonicalSource(read.value.relative, error);
|
|
1408
|
-
}
|
|
1409
|
-
const incomingRefs = await options.incomingRefs?.(read.value.relative) ?? [];
|
|
1410
|
-
return ok({
|
|
1411
|
-
revision: inspected.value.revision,
|
|
1412
|
-
meta: inspected.value.meta,
|
|
1413
|
-
capabilities: canonical === void 0 ? {
|
|
1414
|
-
inspect: true,
|
|
1415
|
-
rebuild: true,
|
|
1416
|
-
coldCook: true,
|
|
1417
|
-
reason: "structured mutation is available only for canonical-v1 scaffolds"
|
|
1418
|
-
} : {
|
|
1419
|
-
inspect: true,
|
|
1420
|
-
rebuild: true,
|
|
1421
|
-
coldCook: true,
|
|
1422
|
-
addOutput: true,
|
|
1423
|
-
addExternalAsset: true,
|
|
1424
|
-
renameDisplay: true,
|
|
1425
|
-
removeOutput: true,
|
|
1426
|
-
clone: true
|
|
1427
|
-
},
|
|
1428
|
-
incomingRefs
|
|
1429
|
-
});
|
|
1430
|
-
}
|
|
1431
|
-
async function mutate(operation) {
|
|
1432
|
-
const targetPath = operation.kind === "clone-scriptable-pack" ? operation.targetPath : operation.sourcePath;
|
|
1433
|
-
const target = resolvePath(targetPath);
|
|
1434
|
-
if (!target.ok) return target;
|
|
1435
|
-
let manifest;
|
|
1436
|
-
let currentRevision;
|
|
1437
|
-
if (operation.kind === "create-scriptable-pack") {
|
|
1438
|
-
if (!CANONICAL_MUTABLE_KINDS.has(operation.initialOutput.kind)) {
|
|
1439
|
-
return err(
|
|
1440
|
-
authoringError("pack-source-mutation-unsupported", {
|
|
1441
|
-
expected: "a canonical scene, mesh, or material initial output",
|
|
1442
|
-
actual: operation.initialOutput.kind,
|
|
1443
|
-
hint: "create one of the canonical output kinds, then hand-author advanced outputs",
|
|
1444
|
-
retryable: false,
|
|
1445
|
-
recoveryActions: ["choose-supported-output-kind"],
|
|
1446
|
-
requestId: operation.requestId,
|
|
1447
|
-
sourcePath: operation.sourcePath
|
|
1448
|
-
})
|
|
1449
|
-
);
|
|
1450
|
-
}
|
|
1451
|
-
try {
|
|
1452
|
-
await stat(target.value.absolute);
|
|
1453
|
-
return err(
|
|
1454
|
-
authoringError("pack-source-revision-conflict", {
|
|
1455
|
-
expected: "an unused target source path",
|
|
1456
|
-
actual: "source already exists",
|
|
1457
|
-
hint: "inspect the existing source or choose a new path",
|
|
1458
|
-
retryable: false,
|
|
1459
|
-
recoveryActions: ["asset.preflight", "choose-new-source-path"],
|
|
1460
|
-
requestId: operation.requestId,
|
|
1461
|
-
sourcePath: operation.sourcePath
|
|
1462
|
-
})
|
|
1463
|
-
);
|
|
1464
|
-
} catch {
|
|
1465
|
-
}
|
|
1466
|
-
manifest = {
|
|
1467
|
-
schemaVersion: "1.0.0",
|
|
1468
|
-
packageId: AssetGuid.format(operation.packageId),
|
|
1469
|
-
...operation.name === void 0 ? {} : { name: operation.name },
|
|
1470
|
-
assets: {
|
|
1471
|
-
[operation.initialOutput.sourceKey]: {
|
|
1472
|
-
guid: AssetGuid.format(operation.initialOutput.guid),
|
|
1473
|
-
kind: operation.initialOutput.kind,
|
|
1474
|
-
...operation.initialOutput.name === void 0 ? {} : { name: operation.initialOutput.name }
|
|
1475
|
-
}
|
|
1476
|
-
},
|
|
1477
|
-
externalAssets: {}
|
|
1478
|
-
};
|
|
1479
|
-
} else {
|
|
1480
|
-
const read = await readSource(operation.sourcePath);
|
|
1481
|
-
if (!read.ok) return read;
|
|
1482
|
-
currentRevision = read.value.revision;
|
|
1483
|
-
if (operation.expectedRevision !== void 0 && operation.expectedRevision !== currentRevision) {
|
|
1484
|
-
return err(
|
|
1485
|
-
authoringError("pack-source-revision-conflict", {
|
|
1486
|
-
expected: operation.expectedRevision,
|
|
1487
|
-
actual: currentRevision,
|
|
1488
|
-
hint: "inspect the current source revision, reconcile the edit, then retry with a new requestId",
|
|
1489
|
-
retryable: true,
|
|
1490
|
-
recoveryActions: ["asset.preflight", "mint-request-id"],
|
|
1491
|
-
requestId: operation.requestId,
|
|
1492
|
-
sourcePath: operation.sourcePath
|
|
1493
|
-
})
|
|
1494
|
-
);
|
|
1495
|
-
}
|
|
1496
|
-
let parsed;
|
|
1497
|
-
try {
|
|
1498
|
-
parsed = parseCanonicalManifest(read.value.source, read.value.relative);
|
|
1499
|
-
} catch (error) {
|
|
1500
|
-
return invalidCanonicalSource(read.value.relative, error);
|
|
1501
|
-
}
|
|
1502
|
-
if (parsed === void 0) {
|
|
1503
|
-
return err(
|
|
1504
|
-
authoringError("pack-source-mutation-unsupported", {
|
|
1505
|
-
expected: "a canonical-v1 scaffold for structured source mutation",
|
|
1506
|
-
actual: "custom ScriptablePack source",
|
|
1507
|
-
hint: "clone into a canonical scaffold or edit custom source with a code editor",
|
|
1508
|
-
retryable: false,
|
|
1509
|
-
recoveryActions: ["clone-scriptable-pack", "open-code-editor"],
|
|
1510
|
-
requestId: operation.requestId,
|
|
1511
|
-
sourcePath: operation.sourcePath
|
|
1512
|
-
})
|
|
1513
|
-
);
|
|
1514
|
-
}
|
|
1515
|
-
manifest = parsed;
|
|
1516
|
-
}
|
|
1517
|
-
if (operation.kind === "clone-scriptable-pack") {
|
|
1518
|
-
try {
|
|
1519
|
-
await stat(target.value.absolute);
|
|
1520
|
-
return err(
|
|
1521
|
-
authoringError("pack-source-revision-conflict", {
|
|
1522
|
-
expected: "an unused clone target source path",
|
|
1523
|
-
actual: "source already exists",
|
|
1524
|
-
hint: "inspect the existing target or choose a new path",
|
|
1525
|
-
retryable: false,
|
|
1526
|
-
recoveryActions: ["asset.preflight", "choose-new-source-path"],
|
|
1527
|
-
requestId: operation.requestId,
|
|
1528
|
-
sourcePath: target.value.relative
|
|
1529
|
-
})
|
|
1530
|
-
);
|
|
1531
|
-
} catch {
|
|
1532
|
-
}
|
|
1533
|
-
}
|
|
1534
|
-
if (operation.kind === "add-output") {
|
|
1535
|
-
if (!CANONICAL_MUTABLE_KINDS.has(operation.assetKind)) {
|
|
1536
|
-
return err(
|
|
1537
|
-
authoringError("pack-source-mutation-unsupported", {
|
|
1538
|
-
expected: "a canonical scene, mesh, or material output",
|
|
1539
|
-
actual: operation.assetKind,
|
|
1540
|
-
hint: "hand-author advanced output kinds in the source module",
|
|
1541
|
-
retryable: false,
|
|
1542
|
-
recoveryActions: ["open-code-editor"],
|
|
1543
|
-
requestId: operation.requestId,
|
|
1544
|
-
sourcePath: operation.sourcePath,
|
|
1545
|
-
sourceKey: operation.sourceKey
|
|
1546
|
-
})
|
|
1547
|
-
);
|
|
1548
|
-
}
|
|
1549
|
-
manifest = {
|
|
1550
|
-
...manifest,
|
|
1551
|
-
assets: {
|
|
1552
|
-
...manifest.assets,
|
|
1553
|
-
[operation.sourceKey]: {
|
|
1554
|
-
guid: AssetGuid.format(operation.guid),
|
|
1555
|
-
kind: operation.assetKind,
|
|
1556
|
-
...operation.name === void 0 ? {} : { name: operation.name }
|
|
1557
|
-
}
|
|
1558
|
-
}
|
|
1559
|
-
};
|
|
1560
|
-
} else if (operation.kind === "add-external-asset") {
|
|
1561
|
-
manifest = {
|
|
1562
|
-
...manifest,
|
|
1563
|
-
externalAssets: {
|
|
1564
|
-
...manifest.externalAssets,
|
|
1565
|
-
[operation.alias]: AssetGuid.format(operation.guid)
|
|
1566
|
-
}
|
|
1567
|
-
};
|
|
1568
|
-
} else if (operation.kind === "rename-display") {
|
|
1569
|
-
if (operation.target.kind === "package") manifest = { ...manifest, name: operation.name };
|
|
1570
|
-
else {
|
|
1571
|
-
const output = manifest.assets[operation.target.sourceKey];
|
|
1572
|
-
if (output === void 0)
|
|
1573
|
-
return err(
|
|
1574
|
-
authoringError("pack-source-mutation-unsupported", {
|
|
1575
|
-
expected: "an existing sourceKey",
|
|
1576
|
-
actual: operation.target.sourceKey,
|
|
1577
|
-
hint: "inspect Meta and choose a current output",
|
|
1578
|
-
retryable: true,
|
|
1579
|
-
recoveryActions: ["asset.preflight"],
|
|
1580
|
-
requestId: operation.requestId,
|
|
1581
|
-
sourcePath: operation.sourcePath,
|
|
1582
|
-
sourceKey: operation.target.sourceKey
|
|
1583
|
-
})
|
|
1584
|
-
);
|
|
1585
|
-
manifest = {
|
|
1586
|
-
...manifest,
|
|
1587
|
-
assets: {
|
|
1588
|
-
...manifest.assets,
|
|
1589
|
-
[operation.target.sourceKey]: { ...output, name: operation.name }
|
|
1590
|
-
}
|
|
1591
|
-
};
|
|
1592
|
-
}
|
|
1593
|
-
} else if (operation.kind === "remove-output") {
|
|
1594
|
-
const incomingRefs = await options.incomingRefs?.(operation.sourcePath, operation.sourceKey) ?? [];
|
|
1595
|
-
if (incomingRefs.length > 0 && !sameStrings(incomingRefs, operation.confirmIncomingRefs ?? [])) {
|
|
1596
|
-
return err(
|
|
1597
|
-
authoringError("pack-source-reference-conflict", {
|
|
1598
|
-
expected: "explicit acknowledgement of every current incoming reference",
|
|
1599
|
-
actual: `${incomingRefs.length} incoming references`,
|
|
1600
|
-
hint: "inspect dependents, then confirm the exact incoming reference identities or cancel",
|
|
1601
|
-
retryable: true,
|
|
1602
|
-
recoveryActions: ["inspect-incoming-refs", "confirm-remove-output"],
|
|
1603
|
-
requestId: operation.requestId,
|
|
1604
|
-
sourcePath: operation.sourcePath,
|
|
1605
|
-
sourceKey: operation.sourceKey,
|
|
1606
|
-
incomingRefs
|
|
1607
|
-
})
|
|
1608
|
-
);
|
|
1609
|
-
}
|
|
1610
|
-
const { [operation.sourceKey]: removed, ...assets } = manifest.assets;
|
|
1611
|
-
if (removed === void 0 || Object.keys(assets).length === 0) {
|
|
1612
|
-
return err(
|
|
1613
|
-
authoringError("pack-source-mutation-unsupported", {
|
|
1614
|
-
expected: "an existing output while retaining at least one package output",
|
|
1615
|
-
actual: operation.sourceKey,
|
|
1616
|
-
hint: "keep one output or remove the entire source file through an explicit file operation",
|
|
1617
|
-
retryable: false,
|
|
1618
|
-
recoveryActions: ["asset.preflight"],
|
|
1619
|
-
requestId: operation.requestId,
|
|
1620
|
-
sourcePath: operation.sourcePath,
|
|
1621
|
-
sourceKey: operation.sourceKey
|
|
1622
|
-
})
|
|
1623
|
-
);
|
|
1624
|
-
}
|
|
1625
|
-
manifest = { ...manifest, assets };
|
|
1626
|
-
} else if (operation.kind === "clone-scriptable-pack") {
|
|
1627
|
-
manifest = {
|
|
1628
|
-
...manifest,
|
|
1629
|
-
packageId: AssetGuid.format(operation.packageId),
|
|
1630
|
-
assets: Object.fromEntries(
|
|
1631
|
-
Object.entries(manifest.assets).map(([sourceKey, output]) => [
|
|
1632
|
-
sourceKey,
|
|
1633
|
-
{
|
|
1634
|
-
...output,
|
|
1635
|
-
guid: AssetGuid.format(operation.outputGuids[sourceKey])
|
|
1636
|
-
}
|
|
1637
|
-
])
|
|
1638
|
-
)
|
|
1639
|
-
};
|
|
1640
|
-
}
|
|
1641
|
-
const source = renderCanonicalScriptablePack(manifest);
|
|
1642
|
-
try {
|
|
1643
|
-
await atomicWrite(target.value.absolute, source);
|
|
1644
|
-
return ok({ sourcePath: target.value.relative, revision: revisionOf(source) });
|
|
1645
|
-
} catch (error) {
|
|
1646
|
-
return err(
|
|
1647
|
-
authoringError("pack-source-write-failed", {
|
|
1648
|
-
expected: "an atomic source-file replacement",
|
|
1649
|
-
actual: error instanceof Error ? error.message : String(error),
|
|
1650
|
-
hint: "repair filesystem permissions or disk capacity, then retry with a new requestId",
|
|
1651
|
-
retryable: true,
|
|
1652
|
-
recoveryActions: ["inspect-filesystem", "mint-request-id"],
|
|
1653
|
-
requestId: operation.requestId,
|
|
1654
|
-
sourcePath: target.value.relative
|
|
1655
|
-
})
|
|
1656
|
-
);
|
|
1657
|
-
}
|
|
1658
|
-
}
|
|
1659
|
-
return {
|
|
1660
|
-
preflight,
|
|
1661
|
-
mutate,
|
|
1662
|
-
inspect,
|
|
1663
|
-
async rebuild(sourcePath, mode) {
|
|
1664
|
-
const path = resolvePath(sourcePath);
|
|
1665
|
-
if (!path.ok) return path;
|
|
1666
|
-
const rebuilt = await options.rebuild?.(path.value.relative, mode) ?? ok(void 0);
|
|
1667
|
-
return rebuilt.ok ? inspect(path.value.relative) : rebuilt;
|
|
1668
|
-
}
|
|
1669
|
-
};
|
|
1670
|
-
}
|
|
1671
1224
|
|
|
1672
|
-
export {
|
|
1225
|
+
export { createScriptablePackModuleExecutorPool, inventoryScriptablePackSource, loadScriptablePack, readOptionalBuildCache, scriptablePackDdcKey };
|
|
1673
1226
|
//# sourceMappingURL=scriptable-pack-node.mjs.map
|
|
1674
1227
|
//# sourceMappingURL=scriptable-pack-node.mjs.map
|