@forgeax/engine-pack 0.1.21 → 0.1.23
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 +159 -46
- package/dist/__tests__/inventory-schema.test.d.ts +2 -0
- package/dist/__tests__/inventory-schema.test.d.ts.map +1 -0
- package/dist/__tests__/parameterized-pack-authoring-gateway.unit.test.d.ts +2 -0
- package/dist/__tests__/parameterized-pack-authoring-gateway.unit.test.d.ts.map +1 -0
- package/dist/__tests__/parameterized-pack-scanner.unit.test.d.ts +2 -0
- package/dist/__tests__/parameterized-pack-scanner.unit.test.d.ts.map +1 -0
- package/dist/__tests__/parameterized-pack.unit.test.d.ts +2 -0
- package/dist/__tests__/parameterized-pack.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 +3314 -803
- 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 +1368 -850
- 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/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 +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +1252 -560
- 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/name.mjs +4 -7
- package/dist/name.mjs.map +1 -1
- package/dist/package-finalizer.d.ts +2 -20
- package/dist/package-finalizer.d.ts.map +1 -1
- package/dist/parameterized-pack-node.d.ts +26 -0
- package/dist/parameterized-pack-node.d.ts.map +1 -0
- package/dist/parameterized-pack-node.mjs +11093 -0
- package/dist/parameterized-pack-node.mjs.map +1 -0
- package/dist/parameterized-pack.d.ts +233 -0
- package/dist/parameterized-pack.d.ts.map +1 -0
- 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 +19 -8
- package/dist/scanner.d.ts.map +1 -1
- package/dist/scanner.mjs +1169 -690
- 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 +8 -32
- package/dist/scriptable-pack-node.d.ts.map +1 -1
- package/dist/scriptable-pack-node.mjs +576 -1021
- 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 +19 -532
- package/dist/scriptable-pack.d.ts.map +1 -1
- package/dist/scriptable-pack.mjs +1042 -527
- package/dist/scriptable-pack.mjs.map +1 -1
- package/package.json +7 -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__/pack.unit.test.ts +1 -238
- package/src/__tests__/package-finalizer.contract.test.ts +55 -0
- package/src/__tests__/parameterized-pack-authoring-gateway.unit.test.ts +249 -0
- package/src/__tests__/parameterized-pack-scanner.unit.test.ts +123 -0
- package/src/__tests__/parameterized-pack.unit.test.ts +266 -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 +14 -15
- package/src/__tests__/scanner-inventory.test.ts +24 -12
- 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 +61 -34
- package/src/cli-asset.ts +70 -85
- package/src/deriveAssetName.ts +14 -13
- package/src/evidence/source-inventory.ts +34 -21
- package/src/guid.ts +65 -4
- package/src/index.ts +12 -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/package-finalizer.ts +2 -42
- package/src/parameterized-pack-node.ts +1968 -0
- package/src/parameterized-pack.ts +1493 -0
- package/src/resolve-asset-source.ts +4 -76
- package/src/scanner.ts +241 -59
- package/src/schema-compiled.ts +2 -0
- package/src/scriptable-pack-node.ts +111 -722
- package/src/scriptable-pack-worker.ts +48 -16
- package/src/scriptable-pack.ts +27 -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
package/dist/scriptable-pack.mjs
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { sha1 } from '@noble/hashes/legacy.js';
|
|
2
2
|
import { uuidv7obj } from 'uuidv7';
|
|
3
|
+
import { ok, err } from '@forgeax/engine-types';
|
|
3
4
|
|
|
4
|
-
// src/
|
|
5
|
+
// src/guid.ts
|
|
5
6
|
|
|
6
7
|
// src/errors.ts
|
|
7
8
|
var PackError = class extends Error {
|
|
@@ -29,6 +30,10 @@ function bytesToDashForm(bytes) {
|
|
|
29
30
|
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]]}`;
|
|
30
31
|
}
|
|
31
32
|
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
33
|
+
var PACK_SOURCE_KEY_RE = /^[a-z0-9][a-z0-9._-]*(\/[a-z0-9][a-z0-9._-]*)*$/;
|
|
34
|
+
function isValidPackSourceKey(value) {
|
|
35
|
+
return typeof value === "string" && PACK_SOURCE_KEY_RE.test(value);
|
|
36
|
+
}
|
|
32
37
|
function isValidAssetGuidString(value) {
|
|
33
38
|
return typeof value === "string" && UUID_RE.test(value);
|
|
34
39
|
}
|
|
@@ -40,6 +45,48 @@ function dashFormToBytes(dashForm) {
|
|
|
40
45
|
}
|
|
41
46
|
return bytes;
|
|
42
47
|
}
|
|
48
|
+
function randomUuidBytes() {
|
|
49
|
+
const uuid = uuidv7obj();
|
|
50
|
+
const bytes = new Uint8Array(16);
|
|
51
|
+
bytes.set(uuid.bytes);
|
|
52
|
+
return bytes;
|
|
53
|
+
}
|
|
54
|
+
function packageId(value) {
|
|
55
|
+
return value;
|
|
56
|
+
}
|
|
57
|
+
var PackageId = {
|
|
58
|
+
parse(dashForm) {
|
|
59
|
+
const parsed = AssetGuid.parse(dashForm);
|
|
60
|
+
return parsed.ok ? { ok: true, value: packageId(parsed.value) } : parsed;
|
|
61
|
+
},
|
|
62
|
+
format(value) {
|
|
63
|
+
return bytesToDashForm(value);
|
|
64
|
+
},
|
|
65
|
+
random() {
|
|
66
|
+
return packageId(randomUuidBytes());
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
function derivedGuid(namespace, sourceKey) {
|
|
70
|
+
if (!(namespace instanceof Uint8Array) || namespace.byteLength !== 16) {
|
|
71
|
+
throw new TypeError("AssetGuid.derive requires a 16-byte PackageId");
|
|
72
|
+
}
|
|
73
|
+
if (!isValidPackSourceKey(sourceKey)) {
|
|
74
|
+
const error = new TypeError(
|
|
75
|
+
`AssetGuid.derive received invalid sourceKey ${JSON.stringify(sourceKey)}`
|
|
76
|
+
);
|
|
77
|
+
Object.defineProperty(error, "code", { value: "pack-source-key-invalid" });
|
|
78
|
+
throw error;
|
|
79
|
+
}
|
|
80
|
+
const name = new TextEncoder().encode(sourceKey);
|
|
81
|
+
const input = new Uint8Array(namespace.byteLength + name.byteLength);
|
|
82
|
+
input.set(namespace, 0);
|
|
83
|
+
input.set(name, namespace.byteLength);
|
|
84
|
+
const digest = sha1(input);
|
|
85
|
+
const result = digest.slice(0, 16);
|
|
86
|
+
result[6] = (result[6] ?? 0) & 15 | 80;
|
|
87
|
+
result[8] = (result[8] ?? 0) & 63 | 128;
|
|
88
|
+
return brand(result);
|
|
89
|
+
}
|
|
43
90
|
var AssetGuid = {
|
|
44
91
|
/**
|
|
45
92
|
* Parse a 36-char RFC 4122 dash-form UUID string into an AssetGuid.
|
|
@@ -83,394 +130,453 @@ var AssetGuid = {
|
|
|
83
130
|
* Works in both Node.js and browser environments.
|
|
84
131
|
*/
|
|
85
132
|
random() {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
133
|
+
return brand(randomUuidBytes());
|
|
134
|
+
},
|
|
135
|
+
/** Derive the stable UUIDv5 projection for one Pack subject and sourceKey. */
|
|
136
|
+
derive(namespace, sourceKey) {
|
|
137
|
+
return derivedGuid(namespace, sourceKey);
|
|
90
138
|
}
|
|
91
139
|
};
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
"
|
|
96
|
-
"
|
|
97
|
-
"
|
|
98
|
-
"
|
|
99
|
-
"
|
|
100
|
-
"
|
|
101
|
-
"
|
|
102
|
-
"
|
|
103
|
-
"
|
|
104
|
-
"
|
|
105
|
-
"skeleton",
|
|
106
|
-
"skin",
|
|
107
|
-
"animation-clip",
|
|
108
|
-
"animation-graph",
|
|
109
|
-
"audio",
|
|
110
|
-
"particle-effect"
|
|
140
|
+
var PACK_PARAMETER_TYPES = [
|
|
141
|
+
"bool",
|
|
142
|
+
"u32",
|
|
143
|
+
"i32",
|
|
144
|
+
"f32",
|
|
145
|
+
"f64",
|
|
146
|
+
"string",
|
|
147
|
+
"enum",
|
|
148
|
+
"vec2",
|
|
149
|
+
"vec3",
|
|
150
|
+
"vec4",
|
|
151
|
+
"color",
|
|
152
|
+
"asset-guid"
|
|
111
153
|
];
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
return SCRIPTABLE_PACK_ASSET_KINDS.includes(value);
|
|
154
|
+
function authoringError(code, expected, hint, detail = {}, actual) {
|
|
155
|
+
return {
|
|
156
|
+
code,
|
|
157
|
+
expected,
|
|
158
|
+
hint,
|
|
159
|
+
...actual === void 0 ? {} : { actual },
|
|
160
|
+
detail
|
|
161
|
+
};
|
|
121
162
|
}
|
|
122
|
-
function
|
|
123
|
-
|
|
124
|
-
if (isRecord(value) && typeof value.type === "string") return value.type;
|
|
125
|
-
return void 0;
|
|
163
|
+
function failure(error) {
|
|
164
|
+
return err(error);
|
|
126
165
|
}
|
|
127
|
-
function
|
|
128
|
-
return
|
|
129
|
-
name: component.name,
|
|
130
|
-
fields: Object.fromEntries(
|
|
131
|
-
Object.entries(component.fields).map(([fieldName, field]) => [
|
|
132
|
-
fieldName,
|
|
133
|
-
sceneComponentFieldType(field)
|
|
134
|
-
])
|
|
135
|
-
)
|
|
136
|
-
}));
|
|
166
|
+
function isRecord(value) {
|
|
167
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
137
168
|
}
|
|
138
|
-
function
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
169
|
+
function validateSceneComponents(value) {
|
|
170
|
+
if (value === void 0) return ok(void 0);
|
|
171
|
+
if (!Array.isArray(value)) {
|
|
172
|
+
return failure(
|
|
173
|
+
parameterFailure(
|
|
174
|
+
"$.sceneComponents",
|
|
175
|
+
"an array of component schema records",
|
|
176
|
+
value,
|
|
177
|
+
"sceneComponents is not an array"
|
|
178
|
+
)
|
|
179
|
+
);
|
|
149
180
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
const preflight = await port.preflight(operation.sourcePath);
|
|
163
|
-
if (!preflight.ok) return preflight;
|
|
164
|
-
if (operation.expectedRevision !== void 0 && operation.expectedRevision !== preflight.value.revision) {
|
|
165
|
-
return err({
|
|
166
|
-
code: "pack-source-revision-conflict",
|
|
167
|
-
expected: operation.expectedRevision,
|
|
168
|
-
actual: preflight.value.revision,
|
|
169
|
-
hint: "inspect the current source revision, then retry the build with a new requestId",
|
|
170
|
-
retryable: true,
|
|
171
|
-
recoveryActions: ["asset.preflight", "mint-request-id"],
|
|
172
|
-
detail: { requestId: operation.requestId, sourcePath: operation.sourcePath }
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
const rebuilt = await port.rebuild(operation.sourcePath, operation.kind);
|
|
176
|
-
return rebuilt.ok ? ok({ sourcePath: operation.sourcePath, ...rebuilt.value }) : rebuilt;
|
|
177
|
-
}
|
|
178
|
-
case "create-scriptable-pack":
|
|
179
|
-
return mutationResult(
|
|
180
|
-
await port.mutate({
|
|
181
|
-
...operation,
|
|
182
|
-
packageId: allocateGuid(),
|
|
183
|
-
initialOutput: { ...operation.initialOutput, guid: allocateGuid() }
|
|
184
|
-
})
|
|
185
|
-
);
|
|
186
|
-
case "add-output":
|
|
187
|
-
return mutationResult(await port.mutate({ ...operation, guid: allocateGuid() }));
|
|
188
|
-
case "clone-scriptable-pack": {
|
|
189
|
-
const inspected = await port.inspect(operation.sourcePath);
|
|
190
|
-
if (!inspected.ok) return inspected;
|
|
191
|
-
const outputGuids = Object.fromEntries(
|
|
192
|
-
inspected.value.meta.subAssets.map((asset) => [asset.sourceKey, allocateGuid()])
|
|
193
|
-
);
|
|
194
|
-
return mutationResult(
|
|
195
|
-
await port.mutate({
|
|
196
|
-
...operation,
|
|
197
|
-
packageId: allocateGuid(),
|
|
198
|
-
outputGuids
|
|
199
|
-
})
|
|
200
|
-
);
|
|
201
|
-
}
|
|
202
|
-
case "add-external-asset":
|
|
203
|
-
case "rename-display":
|
|
204
|
-
case "remove-output":
|
|
205
|
-
return mutationResult(await port.mutate(operation));
|
|
181
|
+
const components = [];
|
|
182
|
+
const names = /* @__PURE__ */ new Set();
|
|
183
|
+
for (const [componentIndex, candidate] of value.entries()) {
|
|
184
|
+
if (!isRecord(candidate) || typeof candidate.name !== "string" || !isRecord(candidate.fields)) {
|
|
185
|
+
return failure(
|
|
186
|
+
parameterFailure(
|
|
187
|
+
`$.sceneComponents[${componentIndex}]`,
|
|
188
|
+
"a component record with name and fields",
|
|
189
|
+
candidate,
|
|
190
|
+
"scene component schema is malformed"
|
|
191
|
+
)
|
|
192
|
+
);
|
|
206
193
|
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
err({
|
|
229
|
-
code: "pack-source-operation-committed",
|
|
230
|
-
expected: "one immutable operation per requestId",
|
|
231
|
-
actual: operation.kind,
|
|
232
|
-
hint: "read the original operation result or mint a new requestId",
|
|
233
|
-
retryable: false,
|
|
234
|
-
recoveryActions: ["read-operation-run", "mint-request-id"],
|
|
235
|
-
detail: { requestId: operation.requestId, sourcePath: operation.sourcePath }
|
|
236
|
-
})
|
|
194
|
+
if (candidate.name.length === 0 || names.has(candidate.name)) {
|
|
195
|
+
return failure(
|
|
196
|
+
parameterFailure(
|
|
197
|
+
`$.sceneComponents[${componentIndex}].name`,
|
|
198
|
+
"a non-empty unique component name",
|
|
199
|
+
candidate.name,
|
|
200
|
+
"scene component names must be unique"
|
|
201
|
+
)
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
names.add(candidate.name);
|
|
205
|
+
const fields = {};
|
|
206
|
+
for (const [fieldName, field] of Object.entries(candidate.fields)) {
|
|
207
|
+
if (typeof field === "string" && field.length === 0 || typeof field !== "string" && (!isRecord(field) || typeof field.type !== "string" || field.type.length === 0)) {
|
|
208
|
+
return failure(
|
|
209
|
+
parameterFailure(
|
|
210
|
+
`$.sceneComponents[${componentIndex}].fields.${fieldName}`,
|
|
211
|
+
"a field type string or { type: string }",
|
|
212
|
+
field,
|
|
213
|
+
"scene component field schema is malformed"
|
|
214
|
+
)
|
|
237
215
|
);
|
|
238
216
|
}
|
|
239
|
-
|
|
240
|
-
requests.set(operation.requestId, { fingerprint, result });
|
|
241
|
-
return result;
|
|
217
|
+
fields[fieldName] = field;
|
|
242
218
|
}
|
|
243
|
-
|
|
219
|
+
components.push({ name: candidate.name, fields });
|
|
220
|
+
}
|
|
221
|
+
return ok(components);
|
|
244
222
|
}
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
minLength: 1,
|
|
248
|
-
maxLength: 128,
|
|
249
|
-
pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$",
|
|
250
|
-
description: "Caller-minted identity for one idempotent ScriptablePack operation."
|
|
251
|
-
};
|
|
252
|
-
var SCRIPTABLE_PACK_SOURCE_PATH_SCHEMA = {
|
|
253
|
-
type: "string",
|
|
254
|
-
minLength: 8,
|
|
255
|
-
pattern: "^[^/].*\\.pack\\.ts$",
|
|
256
|
-
description: "Game-root-relative ScriptablePack source path."
|
|
257
|
-
};
|
|
258
|
-
var SCRIPTABLE_PACK_OWNER_GUID_SCHEMA = {
|
|
259
|
-
type: "string",
|
|
260
|
-
format: "uuid",
|
|
261
|
-
description: "Catalog GUID used to resolve the producer-owned source subject."
|
|
262
|
-
};
|
|
263
|
-
var SCRIPTABLE_PACK_REVISION_SCHEMA = {
|
|
264
|
-
type: "string",
|
|
265
|
-
minLength: 64,
|
|
266
|
-
maxLength: 64,
|
|
267
|
-
pattern: "^[a-f0-9]{64}$",
|
|
268
|
-
description: "SHA-256 source revision returned by preflight."
|
|
269
|
-
};
|
|
270
|
-
var SCRIPTABLE_PACK_SOURCE_KEY_SCHEMA = { type: "string", minLength: 1 };
|
|
271
|
-
var SCRIPTABLE_PACK_ASSET_KIND_SCHEMA = {
|
|
272
|
-
enum: SCRIPTABLE_PACK_ASSET_KINDS,
|
|
273
|
-
description: "Ordinary Asset kind supported by the ScriptablePack producer/loader matrix."
|
|
274
|
-
};
|
|
275
|
-
var SCRIPTABLE_PACK_NAME_SCHEMA = { type: "string", minLength: 1 };
|
|
276
|
-
function scriptablePackArgsSchema(properties, required, options = {}) {
|
|
277
|
-
const subjectRequired = ["requestId", ...required];
|
|
278
|
-
return {
|
|
279
|
-
type: "object",
|
|
280
|
-
properties: {
|
|
281
|
-
sourcePath: SCRIPTABLE_PACK_SOURCE_PATH_SCHEMA,
|
|
282
|
-
ownerGuid: SCRIPTABLE_PACK_OWNER_GUID_SCHEMA,
|
|
283
|
-
requestId: SCRIPTABLE_PACK_REQUEST_ID_SCHEMA,
|
|
284
|
-
...properties
|
|
285
|
-
},
|
|
286
|
-
required: subjectRequired,
|
|
287
|
-
...options.subject === false ? {} : {
|
|
288
|
-
anyOf: [
|
|
289
|
-
{ required: [...subjectRequired, "sourcePath"] },
|
|
290
|
-
{ required: [...subjectRequired, "ownerGuid"] },
|
|
291
|
-
...options.guidSubject === false ? [] : [{ required: [...subjectRequired, "guid"] }]
|
|
292
|
-
]
|
|
293
|
-
},
|
|
294
|
-
additionalProperties: false
|
|
295
|
-
};
|
|
223
|
+
function isPackageId(value) {
|
|
224
|
+
return value instanceof Uint8Array && value.byteLength === 16;
|
|
296
225
|
}
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
additionalProperties: false
|
|
315
|
-
}
|
|
316
|
-
},
|
|
317
|
-
["sourcePath", "initialOutput"],
|
|
318
|
-
{ subject: false }
|
|
319
|
-
)
|
|
320
|
-
},
|
|
321
|
-
{
|
|
322
|
-
id: "asset-source.add-output",
|
|
323
|
-
kind: "add-output",
|
|
324
|
-
domain: "session",
|
|
325
|
-
title: "Add Asset Source Output",
|
|
326
|
-
argsSchema: scriptablePackArgsSchema(
|
|
327
|
-
{
|
|
328
|
-
sourceKey: SCRIPTABLE_PACK_SOURCE_KEY_SCHEMA,
|
|
329
|
-
assetKind: SCRIPTABLE_PACK_ASSET_KIND_SCHEMA,
|
|
330
|
-
name: SCRIPTABLE_PACK_NAME_SCHEMA,
|
|
331
|
-
expectedRevision: SCRIPTABLE_PACK_REVISION_SCHEMA
|
|
332
|
-
},
|
|
333
|
-
["sourceKey", "assetKind", "expectedRevision"]
|
|
334
|
-
)
|
|
335
|
-
},
|
|
336
|
-
{
|
|
337
|
-
id: "asset-source.add-external-asset",
|
|
338
|
-
kind: "add-external-asset",
|
|
339
|
-
domain: "session",
|
|
340
|
-
title: "Add Asset Source External Asset",
|
|
341
|
-
argsSchema: scriptablePackArgsSchema(
|
|
342
|
-
{
|
|
343
|
-
alias: SCRIPTABLE_PACK_SOURCE_KEY_SCHEMA,
|
|
344
|
-
guid: { type: "string", format: "uuid" },
|
|
345
|
-
expectedRevision: SCRIPTABLE_PACK_REVISION_SCHEMA
|
|
346
|
-
},
|
|
347
|
-
["alias", "guid", "expectedRevision"],
|
|
348
|
-
{ guidSubject: false }
|
|
349
|
-
)
|
|
350
|
-
},
|
|
351
|
-
{
|
|
352
|
-
id: "asset-source.rename",
|
|
353
|
-
kind: "rename",
|
|
354
|
-
domain: "session",
|
|
355
|
-
title: "Rename Asset Source Display Name",
|
|
356
|
-
argsSchema: scriptablePackArgsSchema(
|
|
357
|
-
{
|
|
358
|
-
target: {
|
|
359
|
-
oneOf: [
|
|
360
|
-
{
|
|
361
|
-
type: "object",
|
|
362
|
-
properties: { kind: { const: "package" } },
|
|
363
|
-
required: ["kind"],
|
|
364
|
-
additionalProperties: false
|
|
365
|
-
},
|
|
366
|
-
{
|
|
367
|
-
type: "object",
|
|
368
|
-
properties: {
|
|
369
|
-
kind: { const: "output" },
|
|
370
|
-
sourceKey: SCRIPTABLE_PACK_SOURCE_KEY_SCHEMA
|
|
371
|
-
},
|
|
372
|
-
required: ["kind", "sourceKey"],
|
|
373
|
-
additionalProperties: false
|
|
374
|
-
}
|
|
375
|
-
]
|
|
376
|
-
},
|
|
377
|
-
name: SCRIPTABLE_PACK_NAME_SCHEMA,
|
|
378
|
-
expectedRevision: SCRIPTABLE_PACK_REVISION_SCHEMA
|
|
379
|
-
},
|
|
380
|
-
["target", "name", "expectedRevision"]
|
|
381
|
-
)
|
|
382
|
-
},
|
|
383
|
-
{
|
|
384
|
-
id: "asset-source.remove-output",
|
|
385
|
-
kind: "remove-output",
|
|
386
|
-
domain: "session",
|
|
387
|
-
title: "Remove Asset Source Output",
|
|
388
|
-
destructive: true,
|
|
389
|
-
argsSchema: scriptablePackArgsSchema(
|
|
390
|
-
{
|
|
391
|
-
sourceKey: SCRIPTABLE_PACK_SOURCE_KEY_SCHEMA,
|
|
392
|
-
confirmIncomingRefs: { type: "array", items: { type: "string" } },
|
|
393
|
-
expectedRevision: SCRIPTABLE_PACK_REVISION_SCHEMA
|
|
394
|
-
},
|
|
395
|
-
["sourceKey", "expectedRevision"]
|
|
396
|
-
)
|
|
397
|
-
},
|
|
398
|
-
{
|
|
399
|
-
id: "asset-source.clone",
|
|
400
|
-
kind: "clone",
|
|
401
|
-
domain: "session",
|
|
402
|
-
title: "Clone Asset Source",
|
|
403
|
-
argsSchema: scriptablePackArgsSchema(
|
|
404
|
-
{
|
|
405
|
-
targetPath: SCRIPTABLE_PACK_SOURCE_PATH_SCHEMA,
|
|
406
|
-
expectedRevision: SCRIPTABLE_PACK_REVISION_SCHEMA
|
|
407
|
-
},
|
|
408
|
-
["targetPath", "expectedRevision"]
|
|
409
|
-
)
|
|
410
|
-
},
|
|
411
|
-
{
|
|
412
|
-
id: "asset-source.rebuild",
|
|
413
|
-
kind: "rebuild",
|
|
414
|
-
domain: "session",
|
|
415
|
-
title: "Rebuild Asset Source",
|
|
416
|
-
argsSchema: scriptablePackArgsSchema({ expectedRevision: SCRIPTABLE_PACK_REVISION_SCHEMA }, [
|
|
417
|
-
"expectedRevision"
|
|
418
|
-
])
|
|
419
|
-
},
|
|
420
|
-
{
|
|
421
|
-
id: "asset-source.cold-cook",
|
|
422
|
-
kind: "cold-cook",
|
|
423
|
-
domain: "session",
|
|
424
|
-
title: "Cold Cook Asset Source",
|
|
425
|
-
argsSchema: scriptablePackArgsSchema({ expectedRevision: SCRIPTABLE_PACK_REVISION_SCHEMA }, [
|
|
426
|
-
"expectedRevision"
|
|
427
|
-
])
|
|
428
|
-
}
|
|
429
|
-
];
|
|
430
|
-
function actual(value) {
|
|
431
|
-
if (value === null) return "null";
|
|
432
|
-
if (Array.isArray(value)) return "array";
|
|
433
|
-
if (ArrayBuffer.isView(value)) return value.constructor.name;
|
|
434
|
-
return typeof value;
|
|
435
|
-
}
|
|
436
|
-
function invalid(propertyPath, expected, value, sourcePath) {
|
|
437
|
-
return err({
|
|
438
|
-
code: "pack-source-definition-invalid",
|
|
226
|
+
function cloneValue(value) {
|
|
227
|
+
if (value instanceof Uint8Array) return value.slice();
|
|
228
|
+
if (Array.isArray(value)) return value.map((item) => cloneValue(item));
|
|
229
|
+
return value;
|
|
230
|
+
}
|
|
231
|
+
function sourceKeyFailure(sourceKey, propertyPath = "$.sourceKey") {
|
|
232
|
+
return authoringError(
|
|
233
|
+
"pack-source-key-invalid",
|
|
234
|
+
"a sourceKey matching ^[a-z0-9][a-z0-9._-]*(/[a-z0-9][a-z0-9._-]*)*$",
|
|
235
|
+
"use a stable lower-case semantic key; do not derive it from file paths or output order",
|
|
236
|
+
{ propertyPath, sourceKey },
|
|
237
|
+
typeof sourceKey === "string" ? sourceKey : void 0
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
function parameterFailure(propertyPath, expected, actual, reason) {
|
|
241
|
+
return authoringError(
|
|
242
|
+
"pack-parameter-invalid",
|
|
439
243
|
expected,
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
244
|
+
"repair the parameter declaration or the instance values, then inspect and rebuild",
|
|
245
|
+
{ propertyPath, reason, actual: typeof actual === "string" ? actual : JSON.stringify(actual) }
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
function numericType(type) {
|
|
249
|
+
return type === "u32" || type === "i32" || type === "f32" || type === "f64";
|
|
250
|
+
}
|
|
251
|
+
function vectorLength(type) {
|
|
252
|
+
switch (type) {
|
|
253
|
+
case "vec2":
|
|
254
|
+
return 2;
|
|
255
|
+
case "vec3":
|
|
256
|
+
return 3;
|
|
257
|
+
case "vec4":
|
|
258
|
+
case "color":
|
|
259
|
+
return 4;
|
|
260
|
+
default:
|
|
261
|
+
return void 0;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
function parameterValueError(parameter, value, propertyPath) {
|
|
265
|
+
const { type } = parameter;
|
|
266
|
+
if (type === "bool" && typeof value !== "boolean") {
|
|
267
|
+
return parameterFailure(propertyPath, "a boolean", value, "bool value has the wrong type");
|
|
268
|
+
}
|
|
269
|
+
if (numericType(type)) {
|
|
270
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
271
|
+
return parameterFailure(
|
|
272
|
+
propertyPath,
|
|
273
|
+
"a finite number",
|
|
274
|
+
value,
|
|
275
|
+
"numeric value is not finite"
|
|
276
|
+
);
|
|
445
277
|
}
|
|
446
|
-
|
|
278
|
+
if ((type === "u32" || type === "i32") && !Number.isInteger(value)) {
|
|
279
|
+
return parameterFailure(
|
|
280
|
+
propertyPath,
|
|
281
|
+
"an integer",
|
|
282
|
+
value,
|
|
283
|
+
"integer parameter received a fraction"
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
if (type === "u32" && (value < 0 || value > 4294967295)) {
|
|
287
|
+
return parameterFailure(
|
|
288
|
+
propertyPath,
|
|
289
|
+
"an unsigned 32-bit integer",
|
|
290
|
+
value,
|
|
291
|
+
"u32 value is outside [0, 2^32 - 1]"
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
if (type === "i32" && (value < -2147483648 || value > 2147483647)) {
|
|
295
|
+
return parameterFailure(
|
|
296
|
+
propertyPath,
|
|
297
|
+
"a signed 32-bit integer",
|
|
298
|
+
value,
|
|
299
|
+
"i32 value is outside [-2^31, 2^31 - 1]"
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
if (type === "f32" && !Number.isFinite(Math.fround(value))) {
|
|
303
|
+
return parameterFailure(
|
|
304
|
+
propertyPath,
|
|
305
|
+
"a finite IEEE-754 32-bit float",
|
|
306
|
+
value,
|
|
307
|
+
"f32 value overflows binary32"
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
if (parameter.minimum !== void 0 && value < parameter.minimum) {
|
|
311
|
+
return parameterFailure(
|
|
312
|
+
propertyPath,
|
|
313
|
+
`a number >= ${parameter.minimum}`,
|
|
314
|
+
value,
|
|
315
|
+
"value is below minimum"
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
if (parameter.maximum !== void 0 && value > parameter.maximum) {
|
|
319
|
+
return parameterFailure(
|
|
320
|
+
propertyPath,
|
|
321
|
+
`a number <= ${parameter.maximum}`,
|
|
322
|
+
value,
|
|
323
|
+
"value is above maximum"
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
return void 0;
|
|
327
|
+
}
|
|
328
|
+
if (type === "string" && typeof value !== "string") {
|
|
329
|
+
return parameterFailure(propertyPath, "a string", value, "string parameter has the wrong type");
|
|
330
|
+
}
|
|
331
|
+
if (type === "enum") {
|
|
332
|
+
if (typeof value !== "string") {
|
|
333
|
+
return parameterFailure(
|
|
334
|
+
propertyPath,
|
|
335
|
+
"one of the declared enum strings",
|
|
336
|
+
value,
|
|
337
|
+
"enum value has the wrong type"
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
if (parameter.values === void 0 || !parameter.values.includes(value)) {
|
|
341
|
+
return parameterFailure(
|
|
342
|
+
propertyPath,
|
|
343
|
+
"one of the declared enum values",
|
|
344
|
+
value,
|
|
345
|
+
"enum value is not declared"
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
return void 0;
|
|
349
|
+
}
|
|
350
|
+
const length = vectorLength(type);
|
|
351
|
+
if (length !== void 0) {
|
|
352
|
+
if (!Array.isArray(value) || value.length !== length || value.some((component) => typeof component !== "number" || !Number.isFinite(component))) {
|
|
353
|
+
return parameterFailure(
|
|
354
|
+
propertyPath,
|
|
355
|
+
`a finite numeric vector with ${length} components`,
|
|
356
|
+
value,
|
|
357
|
+
"vector value has the wrong shape"
|
|
358
|
+
);
|
|
359
|
+
}
|
|
360
|
+
if (type === "color" && value.some((component) => component < 0 || component > 1)) {
|
|
361
|
+
return parameterFailure(
|
|
362
|
+
propertyPath,
|
|
363
|
+
"an RGBA color with components in [0, 1]",
|
|
364
|
+
value,
|
|
365
|
+
"color component is outside [0, 1]"
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
return void 0;
|
|
369
|
+
}
|
|
370
|
+
if (type === "asset-guid") {
|
|
371
|
+
const validString = typeof value === "string" && isValidAssetGuidString(value);
|
|
372
|
+
if (!(value instanceof Uint8Array && value.byteLength === 16) && !validString) {
|
|
373
|
+
return parameterFailure(
|
|
374
|
+
propertyPath,
|
|
375
|
+
"a 16-byte AssetGuid or UUID string",
|
|
376
|
+
value,
|
|
377
|
+
"asset GUID value is malformed"
|
|
378
|
+
);
|
|
379
|
+
}
|
|
380
|
+
return void 0;
|
|
381
|
+
}
|
|
382
|
+
return parameterFailure(
|
|
383
|
+
propertyPath,
|
|
384
|
+
"a supported Pack parameter type",
|
|
385
|
+
value,
|
|
386
|
+
"unknown parameter type"
|
|
387
|
+
);
|
|
447
388
|
}
|
|
448
|
-
function
|
|
449
|
-
|
|
389
|
+
function normalizeParameterValue(parameter, value, propertyPath) {
|
|
390
|
+
const error = parameterValueError(parameter, value, propertyPath);
|
|
391
|
+
if (error !== void 0) return failure(error);
|
|
392
|
+
if (parameter.type === "asset-guid" && typeof value === "string") {
|
|
393
|
+
const parsed = AssetGuid.parse(value);
|
|
394
|
+
if (!parsed.ok) {
|
|
395
|
+
return failure(
|
|
396
|
+
parameterFailure(
|
|
397
|
+
propertyPath,
|
|
398
|
+
"a valid AssetGuid UUID string",
|
|
399
|
+
value,
|
|
400
|
+
"asset GUID parser rejected the value"
|
|
401
|
+
)
|
|
402
|
+
);
|
|
403
|
+
}
|
|
404
|
+
return ok(parsed.value);
|
|
405
|
+
}
|
|
406
|
+
return ok(cloneValue(value));
|
|
450
407
|
}
|
|
451
|
-
function
|
|
452
|
-
|
|
408
|
+
function validateParameterDefinitions(parameters, propertyPath = "$.parameters") {
|
|
409
|
+
if (!Array.isArray(parameters) || parameters.length === 0) {
|
|
410
|
+
return failure(
|
|
411
|
+
parameterFailure(
|
|
412
|
+
propertyPath,
|
|
413
|
+
"a non-empty parameter descriptor array",
|
|
414
|
+
parameters,
|
|
415
|
+
"parameters must be explicit and non-empty"
|
|
416
|
+
)
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
const names = /* @__PURE__ */ new Set();
|
|
420
|
+
const normalized = [];
|
|
421
|
+
for (const [index, candidate] of parameters.entries()) {
|
|
422
|
+
const path = `${propertyPath}[${index}]`;
|
|
423
|
+
if (!isRecord(candidate))
|
|
424
|
+
return failure(
|
|
425
|
+
parameterFailure(
|
|
426
|
+
path,
|
|
427
|
+
"a parameter descriptor object",
|
|
428
|
+
candidate,
|
|
429
|
+
"descriptor is not an object"
|
|
430
|
+
)
|
|
431
|
+
);
|
|
432
|
+
const name = candidate.name;
|
|
433
|
+
if (typeof name !== "string" || !/^[A-Za-z][A-Za-z0-9_.-]*$/.test(name)) {
|
|
434
|
+
return failure(
|
|
435
|
+
parameterFailure(
|
|
436
|
+
`${path}.name`,
|
|
437
|
+
"a unique identifier-like parameter name",
|
|
438
|
+
name,
|
|
439
|
+
"parameter name is invalid"
|
|
440
|
+
)
|
|
441
|
+
);
|
|
442
|
+
}
|
|
443
|
+
if (names.has(name))
|
|
444
|
+
return failure(
|
|
445
|
+
parameterFailure(
|
|
446
|
+
`${path}.name`,
|
|
447
|
+
"a unique parameter name",
|
|
448
|
+
name,
|
|
449
|
+
"parameter name is duplicated"
|
|
450
|
+
)
|
|
451
|
+
);
|
|
452
|
+
names.add(name);
|
|
453
|
+
const unknown = Object.keys(candidate).find(
|
|
454
|
+
(key) => !["name", "type", "default", "minimum", "maximum", "values", "kind"].includes(key)
|
|
455
|
+
);
|
|
456
|
+
if (unknown !== void 0) {
|
|
457
|
+
return failure(
|
|
458
|
+
parameterFailure(
|
|
459
|
+
`${path}.${unknown}`,
|
|
460
|
+
"only name, type, default, minimum, maximum, values, and kind fields",
|
|
461
|
+
candidate[unknown],
|
|
462
|
+
"parameter descriptors are a closed authoring schema"
|
|
463
|
+
)
|
|
464
|
+
);
|
|
465
|
+
}
|
|
466
|
+
const type = candidate.type;
|
|
467
|
+
if (typeof type !== "string" || !PACK_PARAMETER_TYPES.includes(type)) {
|
|
468
|
+
return failure(
|
|
469
|
+
parameterFailure(
|
|
470
|
+
`${path}.type`,
|
|
471
|
+
PACK_PARAMETER_TYPES.join(" | "),
|
|
472
|
+
type,
|
|
473
|
+
"parameter type is not supported"
|
|
474
|
+
)
|
|
475
|
+
);
|
|
476
|
+
}
|
|
477
|
+
const typed = {
|
|
478
|
+
...candidate,
|
|
479
|
+
name,
|
|
480
|
+
type
|
|
481
|
+
};
|
|
482
|
+
if (typed.values !== void 0) {
|
|
483
|
+
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) {
|
|
484
|
+
return failure(
|
|
485
|
+
parameterFailure(
|
|
486
|
+
`${path}.values`,
|
|
487
|
+
"unique non-empty strings for enum",
|
|
488
|
+
typed.values,
|
|
489
|
+
"enum values are malformed"
|
|
490
|
+
)
|
|
491
|
+
);
|
|
492
|
+
}
|
|
493
|
+
} else if (typed.type === "enum") {
|
|
494
|
+
return failure(
|
|
495
|
+
parameterFailure(
|
|
496
|
+
`${path}.values`,
|
|
497
|
+
"a non-empty enum choices array",
|
|
498
|
+
typed.values,
|
|
499
|
+
"enum has no choices"
|
|
500
|
+
)
|
|
501
|
+
);
|
|
502
|
+
}
|
|
503
|
+
if (typed.kind !== void 0 && (typed.type !== "asset-guid" || typeof typed.kind !== "string")) {
|
|
504
|
+
return failure(
|
|
505
|
+
parameterFailure(
|
|
506
|
+
`${path}.kind`,
|
|
507
|
+
"a kind constraint only on asset-guid parameters",
|
|
508
|
+
typed.kind,
|
|
509
|
+
"kind is misplaced"
|
|
510
|
+
)
|
|
511
|
+
);
|
|
512
|
+
}
|
|
513
|
+
for (const field of ["minimum", "maximum"]) {
|
|
514
|
+
const bound = typed[field];
|
|
515
|
+
if (bound !== void 0 && (typeof bound !== "number" || !Number.isFinite(bound))) {
|
|
516
|
+
return failure(
|
|
517
|
+
parameterFailure(
|
|
518
|
+
`${path}.${field}`,
|
|
519
|
+
"a finite numeric bound",
|
|
520
|
+
bound,
|
|
521
|
+
"numeric bound is invalid"
|
|
522
|
+
)
|
|
523
|
+
);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
if (!numericType(typed.type) && (typed.minimum !== void 0 || typed.maximum !== void 0)) {
|
|
527
|
+
return failure(
|
|
528
|
+
parameterFailure(
|
|
529
|
+
path,
|
|
530
|
+
"minimum/maximum only on numeric parameters",
|
|
531
|
+
typed,
|
|
532
|
+
"numeric bounds are not meaningful for this parameter type"
|
|
533
|
+
)
|
|
534
|
+
);
|
|
535
|
+
}
|
|
536
|
+
if (typed.minimum !== void 0 && typed.maximum !== void 0 && typed.minimum > typed.maximum) {
|
|
537
|
+
return failure(
|
|
538
|
+
parameterFailure(path, "minimum <= maximum", typed, "numeric bounds are reversed")
|
|
539
|
+
);
|
|
540
|
+
}
|
|
541
|
+
const defaultResult = normalizeParameterValue(typed, typed.default, `${path}.default`);
|
|
542
|
+
if (!defaultResult.ok) return defaultResult;
|
|
543
|
+
normalized.push({
|
|
544
|
+
...typed,
|
|
545
|
+
default: defaultResult.value,
|
|
546
|
+
...typed.values === void 0 ? {} : { values: Object.freeze([...typed.values]) }
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
return ok(Object.freeze(normalized));
|
|
453
550
|
}
|
|
454
|
-
function
|
|
455
|
-
return
|
|
551
|
+
function definePackageId(value) {
|
|
552
|
+
if (isPackageId(value)) return value.slice();
|
|
553
|
+
const parsed = PackageId.parse(value);
|
|
554
|
+
if (!parsed.ok) {
|
|
555
|
+
const error = authoringError(
|
|
556
|
+
"pack-package-id-invalid",
|
|
557
|
+
"a 36-character RFC 4122 UUID packageId",
|
|
558
|
+
"use PackageId.random() or repair the packageId literal before loading the Pack",
|
|
559
|
+
{ value },
|
|
560
|
+
value
|
|
561
|
+
);
|
|
562
|
+
throw Object.assign(new TypeError(error.hint), error);
|
|
563
|
+
}
|
|
564
|
+
return parsed.value;
|
|
456
565
|
}
|
|
457
|
-
function
|
|
458
|
-
const
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
guid.slice()
|
|
468
|
-
])
|
|
469
|
-
);
|
|
566
|
+
function cloneDefinition(definition) {
|
|
567
|
+
const parameters = "parameters" in definition ? Object.freeze(
|
|
568
|
+
definition.parameters.map(
|
|
569
|
+
(parameter) => Object.freeze({
|
|
570
|
+
...parameter,
|
|
571
|
+
default: cloneValue(parameter.default),
|
|
572
|
+
...parameter.values === void 0 ? {} : { values: Object.freeze(parameter.values.map((value) => cloneValue(value))) }
|
|
573
|
+
})
|
|
574
|
+
)
|
|
575
|
+
) : void 0;
|
|
470
576
|
const sceneComponents = definition.sceneComponents === void 0 ? void 0 : Object.freeze(
|
|
471
|
-
|
|
577
|
+
definition.sceneComponents.map(
|
|
472
578
|
(component) => Object.freeze({
|
|
473
|
-
|
|
579
|
+
name: component.name,
|
|
474
580
|
fields: Object.freeze({ ...component.fields })
|
|
475
581
|
})
|
|
476
582
|
)
|
|
@@ -478,195 +584,604 @@ function freezeDefinition(definition) {
|
|
|
478
584
|
return Object.freeze({
|
|
479
585
|
...definition,
|
|
480
586
|
packageId: definition.packageId.slice(),
|
|
481
|
-
|
|
482
|
-
externalAssets: Object.freeze(externalAssets),
|
|
587
|
+
...parameters === void 0 ? {} : { parameters },
|
|
483
588
|
...sceneComponents === void 0 ? {} : { sceneComponents }
|
|
484
589
|
});
|
|
485
590
|
}
|
|
486
|
-
function
|
|
591
|
+
function definePack(definition) {
|
|
592
|
+
const validated = validatePackDefinition(definition);
|
|
593
|
+
if (!validated.ok) throw Object.assign(new TypeError(validated.error.hint), validated.error);
|
|
594
|
+
return cloneDefinition(
|
|
595
|
+
validated.value
|
|
596
|
+
);
|
|
597
|
+
}
|
|
598
|
+
function projectParameterizedPackMeta(definition, sourcePath) {
|
|
599
|
+
return {
|
|
600
|
+
schemaVersion: "2.0.0",
|
|
601
|
+
kind: "parameterized-pack-source",
|
|
602
|
+
packageId: PackageId.format(definition.packageId),
|
|
603
|
+
source: sourcePath,
|
|
604
|
+
..."parameters" in definition ? {
|
|
605
|
+
parameters: definition.parameters.map((parameter) => ({
|
|
606
|
+
name: parameter.name,
|
|
607
|
+
type: parameter.type,
|
|
608
|
+
default: jsonParameterValue(parameter.default),
|
|
609
|
+
...parameter.minimum === void 0 ? {} : { minimum: parameter.minimum },
|
|
610
|
+
...parameter.maximum === void 0 ? {} : { maximum: parameter.maximum },
|
|
611
|
+
...parameter.values === void 0 ? {} : { values: parameter.values },
|
|
612
|
+
...parameter.kind === void 0 ? {} : { kind: parameter.kind }
|
|
613
|
+
}))
|
|
614
|
+
} : {}
|
|
615
|
+
};
|
|
616
|
+
}
|
|
617
|
+
function jsonParameterValue(value) {
|
|
618
|
+
return value instanceof Uint8Array ? AssetGuid.format(value) : value;
|
|
619
|
+
}
|
|
620
|
+
function validatePackDefinition(value, sourcePath) {
|
|
487
621
|
if (!isRecord(value))
|
|
488
|
-
return
|
|
489
|
-
|
|
490
|
-
|
|
622
|
+
return failure(
|
|
623
|
+
parameterFailure("$", "a Pack definition object", value, "definition is not an object")
|
|
624
|
+
);
|
|
625
|
+
const unknown = Object.keys(value).find(
|
|
626
|
+
(key) => !["schemaVersion", "packageId", "name", "parameters", "sceneComponents", "build"].includes(
|
|
627
|
+
key
|
|
628
|
+
)
|
|
629
|
+
);
|
|
630
|
+
if (unknown !== void 0) {
|
|
631
|
+
return failure(
|
|
632
|
+
parameterFailure(
|
|
633
|
+
`$.${unknown}`,
|
|
634
|
+
"only schemaVersion, packageId, name, parameters, sceneComponents, and build fields",
|
|
635
|
+
value[unknown],
|
|
636
|
+
"legacy static output declarations and external asset tables are not part of v2 authoring"
|
|
637
|
+
)
|
|
638
|
+
);
|
|
639
|
+
}
|
|
640
|
+
if (value.schemaVersion !== "2.0.0") {
|
|
641
|
+
return failure(
|
|
642
|
+
parameterFailure(
|
|
643
|
+
"$.schemaVersion",
|
|
644
|
+
"the literal '2.0.0'",
|
|
645
|
+
value.schemaVersion,
|
|
646
|
+
"authoring source schema is unsupported"
|
|
647
|
+
)
|
|
648
|
+
);
|
|
491
649
|
}
|
|
492
|
-
if (!
|
|
493
|
-
return
|
|
650
|
+
if (!isPackageId(value.packageId)) {
|
|
651
|
+
return failure(
|
|
652
|
+
authoringError(
|
|
653
|
+
"pack-package-id-invalid",
|
|
654
|
+
"a 16-byte PackageId",
|
|
655
|
+
"use definePackageId() for the stable source identity",
|
|
656
|
+
{ sourcePath, propertyPath: "$.packageId" },
|
|
657
|
+
typeof value.packageId === "string" ? value.packageId : void 0
|
|
658
|
+
)
|
|
659
|
+
);
|
|
494
660
|
}
|
|
495
661
|
if (value.name !== void 0 && typeof value.name !== "string") {
|
|
496
|
-
return
|
|
497
|
-
|
|
498
|
-
if (!isRecord(value.assets) || Object.keys(value.assets).length === 0) {
|
|
499
|
-
return invalid(
|
|
500
|
-
"$.assets",
|
|
501
|
-
"a non-empty sourceKey to descriptor object",
|
|
502
|
-
value.assets,
|
|
503
|
-
sourcePath
|
|
662
|
+
return failure(
|
|
663
|
+
parameterFailure("$.name", "a string when present", value.name, "display name is malformed")
|
|
504
664
|
);
|
|
505
665
|
}
|
|
506
|
-
if (
|
|
507
|
-
return
|
|
508
|
-
"$.
|
|
509
|
-
"an alias to AssetGuid object",
|
|
510
|
-
value.externalAssets,
|
|
511
|
-
sourcePath
|
|
666
|
+
if (typeof value.build !== "function") {
|
|
667
|
+
return failure(
|
|
668
|
+
parameterFailure("$.build", "a build function", value.build, "build is not callable")
|
|
512
669
|
);
|
|
513
670
|
}
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
671
|
+
const sceneComponents = validateSceneComponents(value.sceneComponents);
|
|
672
|
+
if (!sceneComponents.ok) return sceneComponents;
|
|
673
|
+
const hasParameters = Object.hasOwn(value, "parameters");
|
|
674
|
+
if (!hasParameters) {
|
|
675
|
+
return ok(
|
|
676
|
+
Object.freeze({
|
|
677
|
+
schemaVersion: "2.0.0",
|
|
678
|
+
packageId: value.packageId,
|
|
679
|
+
...value.name === void 0 ? {} : { name: value.name },
|
|
680
|
+
...sceneComponents.value === void 0 ? {} : { sceneComponents: sceneComponents.value },
|
|
681
|
+
build: value.build
|
|
682
|
+
})
|
|
683
|
+
);
|
|
684
|
+
}
|
|
685
|
+
const parameters = validateParameterDefinitions(value.parameters);
|
|
686
|
+
if (!parameters.ok) return parameters;
|
|
687
|
+
return ok(
|
|
688
|
+
Object.freeze({
|
|
689
|
+
schemaVersion: "2.0.0",
|
|
690
|
+
packageId: value.packageId,
|
|
691
|
+
...value.name === void 0 ? {} : { name: value.name },
|
|
692
|
+
...sceneComponents.value === void 0 ? {} : { sceneComponents: sceneComponents.value },
|
|
693
|
+
parameters: parameters.value,
|
|
694
|
+
build: value.build
|
|
695
|
+
})
|
|
696
|
+
);
|
|
697
|
+
}
|
|
698
|
+
function isParameterizedPack(value) {
|
|
699
|
+
return isRecord(value) && Array.isArray(value.parameters) && value.parameters.length > 0;
|
|
700
|
+
}
|
|
701
|
+
function resolvePackParameterValues(definition, overrides = {}, inheritedValues) {
|
|
702
|
+
if (!isRecord(overrides)) {
|
|
703
|
+
return failure(
|
|
704
|
+
parameterFailure(
|
|
705
|
+
"$.values",
|
|
706
|
+
"an object of sparse parameter overrides",
|
|
707
|
+
overrides,
|
|
708
|
+
"values is not an object"
|
|
709
|
+
)
|
|
710
|
+
);
|
|
711
|
+
}
|
|
712
|
+
const descriptors = new Map(
|
|
713
|
+
definition.parameters.map((parameter) => [parameter.name, parameter])
|
|
714
|
+
);
|
|
715
|
+
for (const name of Object.keys(overrides)) {
|
|
716
|
+
if (!descriptors.has(name)) {
|
|
717
|
+
return failure(
|
|
718
|
+
parameterFailure(
|
|
719
|
+
`$.values.${name}`,
|
|
720
|
+
"a declared parameter name",
|
|
721
|
+
name,
|
|
722
|
+
"instance contains an unknown parameter"
|
|
723
|
+
)
|
|
521
724
|
);
|
|
522
725
|
}
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
`$.sceneComponents[${componentIndex}].name`,
|
|
544
|
-
"component names to be unique within one ScriptablePack",
|
|
545
|
-
component.name,
|
|
546
|
-
sourcePath
|
|
547
|
-
);
|
|
548
|
-
}
|
|
549
|
-
componentNames.add(component.name);
|
|
550
|
-
if (!isRecord(component.fields)) {
|
|
551
|
-
return invalid(
|
|
552
|
-
`$.sceneComponents[${componentIndex}].fields`,
|
|
553
|
-
"a field-name to schema-type object",
|
|
554
|
-
component.fields,
|
|
555
|
-
sourcePath
|
|
556
|
-
);
|
|
557
|
-
}
|
|
558
|
-
for (const [fieldName, field] of Object.entries(component.fields)) {
|
|
559
|
-
const type = sceneComponentFieldType(field);
|
|
560
|
-
if (fieldName.trim().length === 0 || type === void 0 || type.trim().length === 0) {
|
|
561
|
-
return invalid(
|
|
562
|
-
`$.sceneComponents[${componentIndex}].fields`,
|
|
563
|
-
"field names with non-empty string schema types",
|
|
564
|
-
field,
|
|
565
|
-
sourcePath
|
|
566
|
-
);
|
|
567
|
-
}
|
|
726
|
+
}
|
|
727
|
+
const values = {};
|
|
728
|
+
for (const parameter of definition.parameters) {
|
|
729
|
+
const raw = Object.hasOwn(overrides, parameter.name) ? overrides[parameter.name] : inheritedValues?.[parameter.name] ?? parameter.default;
|
|
730
|
+
const normalized = normalizeParameterValue(parameter, raw, `$.values.${parameter.name}`);
|
|
731
|
+
if (!normalized.ok) return normalized;
|
|
732
|
+
values[parameter.name] = normalized.value;
|
|
733
|
+
}
|
|
734
|
+
return ok(Object.freeze(values));
|
|
735
|
+
}
|
|
736
|
+
function jsonError(propertyPath, expected, actual, reason) {
|
|
737
|
+
return failure(
|
|
738
|
+
authoringError(
|
|
739
|
+
"pack-parameter-invalid",
|
|
740
|
+
expected,
|
|
741
|
+
"repair the v3 pack.json authoring data, then rerun scan or rebuild",
|
|
742
|
+
{
|
|
743
|
+
propertyPath,
|
|
744
|
+
reason,
|
|
745
|
+
actual: typeof actual === "string" ? actual : JSON.stringify(actual)
|
|
568
746
|
}
|
|
569
|
-
|
|
747
|
+
)
|
|
748
|
+
);
|
|
749
|
+
}
|
|
750
|
+
function parsePackageId(value, propertyPath) {
|
|
751
|
+
if (typeof value !== "string") {
|
|
752
|
+
return failure(
|
|
753
|
+
authoringError(
|
|
754
|
+
"pack-package-id-invalid",
|
|
755
|
+
"a UUID string",
|
|
756
|
+
"repair packageId in the authoring file",
|
|
757
|
+
{ propertyPath, value }
|
|
758
|
+
)
|
|
759
|
+
);
|
|
570
760
|
}
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
761
|
+
const parsed = PackageId.parse(value);
|
|
762
|
+
if (!parsed.ok) {
|
|
763
|
+
return failure(
|
|
764
|
+
authoringError(
|
|
765
|
+
"pack-package-id-invalid",
|
|
766
|
+
"a 36-character RFC 4122 UUID",
|
|
767
|
+
"repair packageId or use the authoring gateway to mint one",
|
|
768
|
+
{ propertyPath, value },
|
|
769
|
+
value
|
|
770
|
+
)
|
|
771
|
+
);
|
|
772
|
+
}
|
|
773
|
+
return ok(parsed.value);
|
|
774
|
+
}
|
|
775
|
+
function parsePackJsonAsset(value, sourceKey) {
|
|
776
|
+
if (!isRecord(value))
|
|
777
|
+
return jsonError(
|
|
778
|
+
`$.assets.${sourceKey}`,
|
|
779
|
+
"an asset entry object",
|
|
780
|
+
value,
|
|
781
|
+
"asset entry is not an object"
|
|
782
|
+
);
|
|
783
|
+
const allowed = /* @__PURE__ */ new Set(["kind", "payload", "refs", "name", "artifacts"]);
|
|
784
|
+
const unknown = Object.keys(value).find((key) => !allowed.has(key));
|
|
785
|
+
if (unknown !== void 0)
|
|
786
|
+
return jsonError(
|
|
787
|
+
`$.assets.${sourceKey}.${unknown}`,
|
|
788
|
+
"no GUID or extra authoring field",
|
|
789
|
+
value[unknown],
|
|
790
|
+
"direct v3 entries derive identity from their object key"
|
|
791
|
+
);
|
|
792
|
+
if (typeof value.kind !== "string" || value.kind.length === 0)
|
|
793
|
+
return jsonError(
|
|
794
|
+
`$.assets.${sourceKey}.kind`,
|
|
795
|
+
"a non-empty asset kind",
|
|
796
|
+
value.kind,
|
|
797
|
+
"asset kind is missing"
|
|
798
|
+
);
|
|
799
|
+
if (!isRecord(value.payload))
|
|
800
|
+
return jsonError(
|
|
801
|
+
`$.assets.${sourceKey}.payload`,
|
|
802
|
+
"a JSON object payload",
|
|
803
|
+
value.payload,
|
|
804
|
+
"payload must be a plain object"
|
|
805
|
+
);
|
|
806
|
+
if (!Array.isArray(value.refs) || value.refs.some((ref) => typeof ref !== "string" || !isValidAssetGuidString(ref))) {
|
|
807
|
+
return jsonError(
|
|
808
|
+
`$.assets.${sourceKey}.refs`,
|
|
809
|
+
"an array of UUID references",
|
|
810
|
+
value.refs,
|
|
811
|
+
"references must already be real AssetGuids"
|
|
812
|
+
);
|
|
813
|
+
}
|
|
814
|
+
if (value.name !== void 0 && (typeof value.name !== "string" || value.name.length === 0))
|
|
815
|
+
return jsonError(
|
|
816
|
+
`$.assets.${sourceKey}.name`,
|
|
817
|
+
"a non-empty display name when present",
|
|
818
|
+
value.name,
|
|
819
|
+
"asset name is malformed"
|
|
820
|
+
);
|
|
821
|
+
if (value.artifacts !== void 0 && !isRecord(value.artifacts))
|
|
822
|
+
return jsonError(
|
|
823
|
+
`$.assets.${sourceKey}.artifacts`,
|
|
824
|
+
"an artifact descriptor object when present",
|
|
825
|
+
value.artifacts,
|
|
826
|
+
"artifacts are not an object"
|
|
827
|
+
);
|
|
828
|
+
const base = {
|
|
829
|
+
kind: value.kind,
|
|
830
|
+
payload: value.payload,
|
|
831
|
+
refs: Object.freeze([...value.refs]),
|
|
832
|
+
...value.name === void 0 ? {} : { name: value.name }
|
|
833
|
+
};
|
|
834
|
+
if (value.artifacts === void 0) return ok(base);
|
|
835
|
+
const withArtifacts = {
|
|
836
|
+
...base,
|
|
837
|
+
artifacts: value.artifacts
|
|
838
|
+
};
|
|
839
|
+
return ok(withArtifacts);
|
|
840
|
+
}
|
|
841
|
+
function parseParameterizedPackJson(value) {
|
|
842
|
+
if (!isRecord(value))
|
|
843
|
+
return jsonError("$", "a v3 pack.json object", value, "document is not an object");
|
|
844
|
+
if (value.schemaVersion !== "3.0.0")
|
|
845
|
+
return jsonError(
|
|
846
|
+
"$.schemaVersion",
|
|
847
|
+
"the literal '3.0.0'",
|
|
848
|
+
value.schemaVersion,
|
|
849
|
+
"document schema is not v3"
|
|
850
|
+
);
|
|
851
|
+
const unknown = Object.keys(value).find(
|
|
852
|
+
(key) => !["schemaVersion", "packageId", "assets", "parent", "values"].includes(key)
|
|
853
|
+
);
|
|
854
|
+
if (unknown !== void 0)
|
|
855
|
+
return jsonError(
|
|
856
|
+
`$.${unknown}`,
|
|
857
|
+
"no extra top-level authoring fields",
|
|
858
|
+
value[unknown],
|
|
859
|
+
"keep the v3 document to packageId+assets or packageId+parent+values"
|
|
860
|
+
);
|
|
861
|
+
const packageResult = parsePackageId(value.packageId, "$.packageId");
|
|
862
|
+
if (!packageResult.ok) return packageResult;
|
|
863
|
+
const hasAssets = Object.hasOwn(value, "assets");
|
|
864
|
+
const hasParent = Object.hasOwn(value, "parent");
|
|
865
|
+
const hasValues = Object.hasOwn(value, "values");
|
|
866
|
+
if (hasAssets && (hasParent || hasValues))
|
|
867
|
+
return jsonError(
|
|
868
|
+
"$",
|
|
869
|
+
"assets or parent+values, never both",
|
|
870
|
+
value,
|
|
871
|
+
"direct and instance branches are mutually exclusive"
|
|
872
|
+
);
|
|
873
|
+
if (hasAssets) {
|
|
874
|
+
if (!isRecord(value.assets))
|
|
875
|
+
return jsonError(
|
|
876
|
+
"$.assets",
|
|
877
|
+
"a sourceKey to asset object",
|
|
878
|
+
value.assets,
|
|
879
|
+
"direct assets are not an object"
|
|
602
880
|
);
|
|
881
|
+
const assets = {};
|
|
882
|
+
for (const [sourceKey, entry] of Object.entries(value.assets)) {
|
|
883
|
+
if (!isValidPackSourceKey(sourceKey))
|
|
884
|
+
return failure(sourceKeyFailure(sourceKey, `$.assets.${sourceKey}`));
|
|
885
|
+
const parsed = parsePackJsonAsset(entry, sourceKey);
|
|
886
|
+
if (!parsed.ok) return parsed;
|
|
887
|
+
assets[sourceKey] = parsed.value;
|
|
603
888
|
}
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
889
|
+
return ok({
|
|
890
|
+
format: "direct",
|
|
891
|
+
schemaVersion: "3.0.0",
|
|
892
|
+
packageId: packageResult.value,
|
|
893
|
+
assets: Object.freeze(assets)
|
|
894
|
+
});
|
|
895
|
+
}
|
|
896
|
+
if (!hasParent || !hasValues || hasAssets)
|
|
897
|
+
return jsonError(
|
|
898
|
+
"$",
|
|
899
|
+
"parent and values for an instance document",
|
|
900
|
+
value,
|
|
901
|
+
"instance branch is incomplete"
|
|
902
|
+
);
|
|
903
|
+
const parentResult = parsePackageId(value.parent, "$.parent");
|
|
904
|
+
if (!parentResult.ok) return parentResult;
|
|
905
|
+
if (!isRecord(value.values))
|
|
906
|
+
return jsonError(
|
|
907
|
+
"$.values",
|
|
908
|
+
"a sparse parameter object",
|
|
909
|
+
value.values,
|
|
910
|
+
"instance values are not an object"
|
|
911
|
+
);
|
|
912
|
+
return ok({
|
|
913
|
+
format: "instance",
|
|
914
|
+
schemaVersion: "3.0.0",
|
|
915
|
+
packageId: packageResult.value,
|
|
916
|
+
parent: parentResult.value,
|
|
917
|
+
values: Object.freeze({ ...value.values })
|
|
918
|
+
});
|
|
919
|
+
}
|
|
920
|
+
var parsePackJson = parseParameterizedPackJson;
|
|
921
|
+
var parsePackAuthoringJson = parseParameterizedPackJson;
|
|
922
|
+
function projectDirectPackJson(value) {
|
|
923
|
+
const parsed = "format" in value ? ok(value) : parseParameterizedPackJson(value);
|
|
924
|
+
if (!parsed.ok) return parsed;
|
|
925
|
+
if (parsed.value.format !== "direct") {
|
|
926
|
+
return failure(
|
|
927
|
+
authoringError(
|
|
928
|
+
"pack-parameter-invalid",
|
|
929
|
+
"a direct v3 pack.json",
|
|
930
|
+
"projectDirectPackJson accepts the direct branch only",
|
|
931
|
+
{ observed: parsed.value.format }
|
|
932
|
+
)
|
|
933
|
+
);
|
|
934
|
+
}
|
|
935
|
+
const assets = [];
|
|
936
|
+
for (const [sourceKey, entry] of Object.entries(parsed.value.assets).sort(
|
|
937
|
+
([left], [right]) => left.localeCompare(right)
|
|
938
|
+
)) {
|
|
939
|
+
assets.push({
|
|
940
|
+
...entry,
|
|
941
|
+
guid: AssetGuid.format(AssetGuid.derive(parsed.value.packageId, sourceKey)),
|
|
942
|
+
sourceKey
|
|
943
|
+
});
|
|
944
|
+
}
|
|
945
|
+
return ok({ packageId: PackageId.format(parsed.value.packageId), assets: Object.freeze(assets) });
|
|
946
|
+
}
|
|
947
|
+
function subjectId(subject) {
|
|
948
|
+
return PackageId.format(subject.packageId).toLowerCase();
|
|
949
|
+
}
|
|
950
|
+
async function resolvePackParameterInheritance(subject, readParent) {
|
|
951
|
+
const visited = /* @__PURE__ */ new Set();
|
|
952
|
+
async function visit(current, path) {
|
|
953
|
+
const currentId = subjectId(current);
|
|
954
|
+
if (visited.has(currentId) || path.includes(currentId)) {
|
|
955
|
+
return failure(
|
|
956
|
+
authoringError(
|
|
957
|
+
"pack-parent-cycle",
|
|
958
|
+
"a finite acyclic Pack parent chain",
|
|
959
|
+
"inspect parentChain and repair the first repeated packageId",
|
|
960
|
+
{ packageId: currentId, parentChain: [...path, currentId] }
|
|
961
|
+
)
|
|
610
962
|
);
|
|
611
963
|
}
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
964
|
+
visited.add(currentId);
|
|
965
|
+
const nextPath = [...path, currentId];
|
|
966
|
+
if (current.format === "direct") {
|
|
967
|
+
return failure(
|
|
968
|
+
authoringError(
|
|
969
|
+
"pack-parent-not-parameterized",
|
|
970
|
+
"a parameterized Pack source as the parent",
|
|
971
|
+
"direct packs cannot be instance parents; point the instance at a parameterized source",
|
|
972
|
+
{ packageId: currentId }
|
|
973
|
+
)
|
|
619
974
|
);
|
|
620
975
|
}
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
976
|
+
if (current.format === "source") {
|
|
977
|
+
const descriptors2 = validateParameterDefinitions(current.parameters, "$.parameters");
|
|
978
|
+
if (!descriptors2.ok) return descriptors2;
|
|
979
|
+
if (descriptors2.value.length === 0) {
|
|
980
|
+
return failure(
|
|
981
|
+
authoringError(
|
|
982
|
+
"pack-parent-not-parameterized",
|
|
983
|
+
"the parent source to declare a non-empty parameter list",
|
|
984
|
+
"use clone for an independent zero-parameter Pack instead of creating an empty instance",
|
|
985
|
+
{ packageId: currentId }
|
|
986
|
+
)
|
|
987
|
+
);
|
|
988
|
+
}
|
|
989
|
+
const definition2 = {
|
|
990
|
+
packageId: current.packageId,
|
|
991
|
+
parameters: descriptors2.value};
|
|
992
|
+
const values2 = resolvePackParameterValues(definition2);
|
|
993
|
+
if (!values2.ok) return values2;
|
|
994
|
+
return ok({
|
|
995
|
+
packageId: current.packageId,
|
|
996
|
+
rootPackageId: current.packageId,
|
|
997
|
+
values: values2.value,
|
|
998
|
+
parentChain: [],
|
|
999
|
+
parameters: descriptors2.value
|
|
1000
|
+
});
|
|
627
1001
|
}
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
1002
|
+
const parent = await readParent(current.parent);
|
|
1003
|
+
if (parent === void 0) {
|
|
1004
|
+
return failure(
|
|
1005
|
+
authoringError(
|
|
1006
|
+
"pack-parent-not-found",
|
|
1007
|
+
"the parent packageId to resolve in the source index",
|
|
1008
|
+
"inspect the parent locator or recreate the instance from a live parameterized Pack",
|
|
1009
|
+
{ packageId: currentId, parent: PackageId.format(current.parent) }
|
|
1010
|
+
)
|
|
634
1011
|
);
|
|
635
1012
|
}
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
1013
|
+
if (PackageId.format(parent.packageId).toLowerCase() !== PackageId.format(current.parent).toLowerCase()) {
|
|
1014
|
+
return failure(
|
|
1015
|
+
authoringError(
|
|
1016
|
+
"pack-parent-not-found",
|
|
1017
|
+
"the parent reader to return the requested packageId",
|
|
1018
|
+
"repair the Source Index locator and retry inheritance resolution",
|
|
1019
|
+
{
|
|
1020
|
+
packageId: currentId,
|
|
1021
|
+
requestedParent: PackageId.format(current.parent),
|
|
1022
|
+
observedParent: PackageId.format(parent.packageId)
|
|
1023
|
+
}
|
|
1024
|
+
)
|
|
643
1025
|
);
|
|
644
1026
|
}
|
|
645
|
-
|
|
1027
|
+
const resolvedParent = await visit(parent, nextPath);
|
|
1028
|
+
if (!resolvedParent.ok) return resolvedParent;
|
|
1029
|
+
const descriptors = resolvedParent.value.parameters;
|
|
1030
|
+
const definition = {
|
|
1031
|
+
packageId: resolvedParent.value.rootPackageId,
|
|
1032
|
+
parameters: descriptors};
|
|
1033
|
+
const values = resolvePackParameterValues(
|
|
1034
|
+
definition,
|
|
1035
|
+
current.values,
|
|
1036
|
+
resolvedParent.value.values
|
|
1037
|
+
);
|
|
1038
|
+
if (!values.ok) return values;
|
|
1039
|
+
return ok({
|
|
1040
|
+
packageId: current.packageId,
|
|
1041
|
+
rootPackageId: resolvedParent.value.rootPackageId,
|
|
1042
|
+
values: values.value,
|
|
1043
|
+
parentChain: [PackageId.format(current.parent), ...resolvedParent.value.parentChain],
|
|
1044
|
+
parameters: descriptors
|
|
1045
|
+
});
|
|
646
1046
|
}
|
|
647
|
-
return
|
|
1047
|
+
return visit(subject, []);
|
|
648
1048
|
}
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
1049
|
+
var resolvePackInheritance = resolvePackParameterInheritance;
|
|
1050
|
+
var PARAMETERIZED_PACK_OPERATION_IDS = [
|
|
1051
|
+
"asset.list",
|
|
1052
|
+
"asset.inspect",
|
|
1053
|
+
"asset.resolve",
|
|
1054
|
+
"asset.verify",
|
|
1055
|
+
"asset-source.create",
|
|
1056
|
+
"asset-source.clone",
|
|
1057
|
+
"asset-source.create-instance",
|
|
1058
|
+
"asset-source.apply-values",
|
|
1059
|
+
"asset-source.rebuild",
|
|
1060
|
+
"asset-source.cold-cook"
|
|
1061
|
+
];
|
|
1062
|
+
function createParameterizedPackAuthoringGateway(port) {
|
|
1063
|
+
const requests = /* @__PURE__ */ new Map();
|
|
658
1064
|
return {
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
1065
|
+
execute(operation) {
|
|
1066
|
+
if (typeof operation.requestId !== "string" || operation.requestId.trim().length === 0) {
|
|
1067
|
+
return Promise.resolve(
|
|
1068
|
+
failure(
|
|
1069
|
+
authoringError(
|
|
1070
|
+
"pack-source-revision-conflict",
|
|
1071
|
+
"a non-empty caller-minted requestId",
|
|
1072
|
+
"mint a new requestId and retry",
|
|
1073
|
+
{ requestId: operation.requestId }
|
|
1074
|
+
)
|
|
1075
|
+
)
|
|
1076
|
+
);
|
|
1077
|
+
}
|
|
1078
|
+
let fingerprint;
|
|
1079
|
+
try {
|
|
1080
|
+
fingerprint = JSON.stringify(operation);
|
|
1081
|
+
} catch (cause) {
|
|
1082
|
+
return Promise.resolve(
|
|
1083
|
+
failure(
|
|
1084
|
+
authoringError(
|
|
1085
|
+
"pack-parameter-invalid",
|
|
1086
|
+
"a JSON-serialisable authoring operation",
|
|
1087
|
+
"remove non-serialisable operation fields and retry with a new requestId",
|
|
1088
|
+
{
|
|
1089
|
+
requestId: operation.requestId,
|
|
1090
|
+
cause: cause instanceof Error ? cause.message : String(cause)
|
|
1091
|
+
}
|
|
1092
|
+
)
|
|
1093
|
+
)
|
|
1094
|
+
);
|
|
1095
|
+
}
|
|
1096
|
+
const previous = requests.get(operation.requestId);
|
|
1097
|
+
if (previous !== void 0) {
|
|
1098
|
+
if (previous.fingerprint === fingerprint) return previous.result;
|
|
1099
|
+
return Promise.resolve(
|
|
1100
|
+
failure(
|
|
1101
|
+
authoringError(
|
|
1102
|
+
"pack-source-revision-conflict",
|
|
1103
|
+
"one immutable operation per requestId",
|
|
1104
|
+
"read the original result or mint a new requestId",
|
|
1105
|
+
{ requestId: operation.requestId }
|
|
1106
|
+
)
|
|
1107
|
+
)
|
|
1108
|
+
);
|
|
1109
|
+
}
|
|
1110
|
+
const result = Promise.resolve().then(() => port.execute(operation)).catch(
|
|
1111
|
+
(cause) => failure(
|
|
1112
|
+
authoringError(
|
|
1113
|
+
"pack-parameter-invalid",
|
|
1114
|
+
"the Pack authoring port to return a structured Result",
|
|
1115
|
+
"repair the gateway port failure, then retry with a new requestId",
|
|
1116
|
+
{
|
|
1117
|
+
requestId: operation.requestId,
|
|
1118
|
+
cause: cause instanceof Error ? cause.message : String(cause)
|
|
1119
|
+
}
|
|
1120
|
+
)
|
|
1121
|
+
)
|
|
1122
|
+
);
|
|
1123
|
+
requests.set(operation.requestId, { fingerprint, result });
|
|
1124
|
+
return result;
|
|
1125
|
+
}
|
|
667
1126
|
};
|
|
668
1127
|
}
|
|
1128
|
+
var PARAMETERIZED_PACK_OPERATION_DESCRIPTORS = PARAMETERIZED_PACK_OPERATION_IDS.map(
|
|
1129
|
+
(id) => ({
|
|
1130
|
+
id,
|
|
1131
|
+
domain: id.startsWith("asset-source.") ? "session" : "asset",
|
|
1132
|
+
readOnly: id.startsWith("asset."),
|
|
1133
|
+
requiresRevision: !id.startsWith("asset.") && id !== "asset-source.create"
|
|
1134
|
+
})
|
|
1135
|
+
);
|
|
1136
|
+
|
|
1137
|
+
// src/scriptable-pack.ts
|
|
1138
|
+
var SCRIPTABLE_PACK_ASSET_KINDS = [
|
|
1139
|
+
"mesh",
|
|
1140
|
+
"material",
|
|
1141
|
+
"scene",
|
|
1142
|
+
"texture",
|
|
1143
|
+
"equirect",
|
|
1144
|
+
"sampler",
|
|
1145
|
+
"font",
|
|
1146
|
+
"render-pipeline",
|
|
1147
|
+
"tileset",
|
|
1148
|
+
"video",
|
|
1149
|
+
"skeleton",
|
|
1150
|
+
"skin",
|
|
1151
|
+
"animation-clip",
|
|
1152
|
+
"animation-graph",
|
|
1153
|
+
"audio",
|
|
1154
|
+
"particle-effect",
|
|
1155
|
+
"ies-profile"
|
|
1156
|
+
];
|
|
1157
|
+
function isScriptablePackAssetKind(value) {
|
|
1158
|
+
return SCRIPTABLE_PACK_ASSET_KINDS.includes(value);
|
|
1159
|
+
}
|
|
1160
|
+
var SCRIPTABLE_PACK_CAPABILITY_MANIFEST = {
|
|
1161
|
+
assetKinds: SCRIPTABLE_PACK_ASSET_KINDS,
|
|
1162
|
+
durablePayload: true,
|
|
1163
|
+
refs: true,
|
|
1164
|
+
artifacts: true,
|
|
1165
|
+
hostCapabilities: ["audio-install", "video-play", "particle-execute"]
|
|
1166
|
+
};
|
|
1167
|
+
function sceneComponentFieldType(value) {
|
|
1168
|
+
if (typeof value === "string") return value;
|
|
1169
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
1170
|
+
const type = value.type;
|
|
1171
|
+
return typeof type === "string" ? type : void 0;
|
|
1172
|
+
}
|
|
1173
|
+
function projectScriptablePackSceneComponents(components) {
|
|
1174
|
+
return (components ?? []).map((component) => ({
|
|
1175
|
+
name: component.name,
|
|
1176
|
+
fields: Object.fromEntries(
|
|
1177
|
+
Object.entries(component.fields).map(([fieldName, field]) => [
|
|
1178
|
+
fieldName,
|
|
1179
|
+
sceneComponentFieldType(field)
|
|
1180
|
+
])
|
|
1181
|
+
)
|
|
1182
|
+
}));
|
|
1183
|
+
}
|
|
669
1184
|
|
|
670
|
-
export { SCRIPTABLE_PACK_ASSET_KINDS, SCRIPTABLE_PACK_CAPABILITY_MANIFEST,
|
|
1185
|
+
export { AssetGuid, PACK_PARAMETER_TYPES, PACK_SOURCE_KEY_RE, PARAMETERIZED_PACK_OPERATION_DESCRIPTORS, PARAMETERIZED_PACK_OPERATION_IDS, PackageId, SCRIPTABLE_PACK_ASSET_KINDS, SCRIPTABLE_PACK_CAPABILITY_MANIFEST, createParameterizedPackAuthoringGateway, definePack, definePackageId, isParameterizedPack, isScriptablePackAssetKind, isValidAssetGuidString, isValidPackSourceKey, parsePackAuthoringJson, parsePackJson, parseParameterizedPackJson, projectDirectPackJson, projectParameterizedPackMeta, projectScriptablePackSceneComponents, resolvePackInheritance, resolvePackParameterInheritance, resolvePackParameterValues, validatePackDefinition };
|
|
671
1186
|
//# sourceMappingURL=scriptable-pack.mjs.map
|
|
672
1187
|
//# sourceMappingURL=scriptable-pack.mjs.map
|