@crafter8/sdk 0.1.0-next.1
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/LICENSE +21 -0
- package/README.md +175 -0
- package/build.js +553 -0
- package/index.d.ts +473 -0
- package/index.js +1831 -0
- package/mock.d.ts +128 -0
- package/mock.js +471 -0
- package/package.json +61 -0
- package/react.d.ts +30 -0
- package/react.js +124 -0
package/index.js
ADDED
|
@@ -0,0 +1,1831 @@
|
|
|
1
|
+
const DEFINITION_SYMBOL = Symbol.for("@crafter8/sdk/definition");
|
|
2
|
+
const REGISTERED_DATAPACK_PACKAGE_SOURCES = new Map();
|
|
3
|
+
|
|
4
|
+
export const ARTIFACT_SPEC_V1 = 1;
|
|
5
|
+
export const ARTIFACT_SPEC_V2 = 2;
|
|
6
|
+
export const HOST_API_V1 = 1;
|
|
7
|
+
|
|
8
|
+
function isPlainObject(value) {
|
|
9
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function assertString(value, name) {
|
|
13
|
+
if (typeof value !== "string" || value.trim() === "") {
|
|
14
|
+
throw new TypeError(`${name} must be a non-empty string.`);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function assertOptionalString(value, name) {
|
|
19
|
+
if (value === undefined) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
assertString(value, name);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function assertFunction(value, name) {
|
|
26
|
+
if (typeof value !== "function") {
|
|
27
|
+
throw new TypeError(`${name} must be a function.`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function isPackageLikeId(value) {
|
|
32
|
+
return typeof value === "string" && value.trim().includes("/");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function assertStringArray(value, name) {
|
|
36
|
+
if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string" || entry.trim() === "")) {
|
|
37
|
+
throw new TypeError(`${name} must be an array of non-empty strings.`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function copyStringArray(value) {
|
|
42
|
+
return Array.from(new Set(value.map((entry) => entry.trim())));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function normalizeUses(uses) {
|
|
46
|
+
if (uses === undefined) {
|
|
47
|
+
return { modules: [], datapacks: [] };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (!isPlainObject(uses)) {
|
|
51
|
+
throw new TypeError("uses must be an object.");
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const normalized = {
|
|
55
|
+
modules: [],
|
|
56
|
+
datapacks: [],
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
if (uses.modules !== undefined) {
|
|
60
|
+
if (!Array.isArray(uses.modules)) {
|
|
61
|
+
throw new TypeError("uses.modules must be an array.");
|
|
62
|
+
}
|
|
63
|
+
normalized.modules = uses.modules.map((entry) => readArtifactRef(entry, "module").id);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (uses.datapacks !== undefined) {
|
|
67
|
+
if (!Array.isArray(uses.datapacks)) {
|
|
68
|
+
throw new TypeError("uses.datapacks must be an array.");
|
|
69
|
+
}
|
|
70
|
+
normalized.datapacks = uses.datapacks.map((entry) => readArtifactRef(entry, "datapack").id);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return normalized;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function normalizeModuleProvides(input) {
|
|
77
|
+
if (input === undefined) {
|
|
78
|
+
return { exports: {}, operations: [] };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
let exportsRecord = {};
|
|
82
|
+
let operations = [];
|
|
83
|
+
|
|
84
|
+
if (isPlainObject(input.exports) || isPlainObject(input.provides?.exports)) {
|
|
85
|
+
exportsRecord = isPlainObject(input.exports) ? input.exports : input.provides.exports;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (input.operations !== undefined) {
|
|
89
|
+
operations = input.operations;
|
|
90
|
+
} else if (input.provides?.operations !== undefined) {
|
|
91
|
+
operations = input.provides.operations;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (!isPlainObject(exportsRecord)) {
|
|
95
|
+
throw new TypeError("module exports must be an object.");
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
for (const [key, value] of Object.entries(exportsRecord)) {
|
|
99
|
+
assertString(key, "module export key");
|
|
100
|
+
assertFunction(value, `module export "${key}"`);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (operations.length > 0) {
|
|
104
|
+
assertStringArray(operations, "module operations");
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
exports: { ...exportsRecord },
|
|
109
|
+
operations: copyStringArray(operations),
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function normalizeDatapackProvides(provides) {
|
|
114
|
+
if (provides === undefined) {
|
|
115
|
+
return {};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (!isPlainObject(provides)) {
|
|
119
|
+
throw new TypeError("datapack provides must be an object.");
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (provides.profile !== undefined) {
|
|
123
|
+
assertString(provides.profile, "datapack provides.profile");
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
...(provides.profile ? { profile: provides.profile.trim() } : {}),
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function normalizeContents(contents) {
|
|
132
|
+
if (!isPlainObject(contents)) {
|
|
133
|
+
throw new TypeError("contents must be an object.");
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (contents.root !== undefined) {
|
|
137
|
+
assertString(contents.root, "contents.root");
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (contents.manifest !== undefined) {
|
|
141
|
+
assertString(contents.manifest, "contents.manifest");
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return {
|
|
145
|
+
...(contents.root ? { root: contents.root.trim() } : {}),
|
|
146
|
+
...(contents.manifest ? { manifest: contents.manifest.trim() } : {}),
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function normalizeDefinition(kind, config) {
|
|
151
|
+
if (!isPlainObject(config)) {
|
|
152
|
+
throw new TypeError(`${kind} definition must be an object.`);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
assertString(config.id, `${kind}.id`);
|
|
156
|
+
assertString(config.name, `${kind}.name`);
|
|
157
|
+
assertOptionalString(config.summary, `${kind}.summary`);
|
|
158
|
+
|
|
159
|
+
const normalized = {
|
|
160
|
+
id: config.id.trim(),
|
|
161
|
+
kind,
|
|
162
|
+
name: config.name.trim(),
|
|
163
|
+
summary: config.summary?.trim(),
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
if (kind === "app") {
|
|
167
|
+
const hasMount = config.mount !== undefined;
|
|
168
|
+
const hasComponent = config.component !== undefined;
|
|
169
|
+
if (!hasMount && !hasComponent) {
|
|
170
|
+
throw new TypeError("app definition requires either app.mount or app.component.");
|
|
171
|
+
}
|
|
172
|
+
if (hasMount && hasComponent) {
|
|
173
|
+
throw new TypeError("app definition must provide either app.mount or app.component, not both.");
|
|
174
|
+
}
|
|
175
|
+
if (hasMount) {
|
|
176
|
+
assertFunction(config.mount, "app.mount");
|
|
177
|
+
normalized.mount = config.mount;
|
|
178
|
+
}
|
|
179
|
+
if (hasComponent) {
|
|
180
|
+
assertFunction(config.component, "app.component");
|
|
181
|
+
normalized.component = config.component;
|
|
182
|
+
}
|
|
183
|
+
normalized.capabilities = config.capabilities === undefined ? [] : copyStringArray((assertStringArray(config.capabilities, "app.capabilities"), config.capabilities));
|
|
184
|
+
normalized.uses = normalizeUses(config.uses);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (kind === "module") {
|
|
188
|
+
normalized.capabilities = config.capabilities === undefined ? [] : copyStringArray((assertStringArray(config.capabilities, "module.capabilities"), config.capabilities));
|
|
189
|
+
normalized.uses = normalizeUses(config.uses);
|
|
190
|
+
normalized.provides = normalizeModuleProvides(config);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (kind === "datapack") {
|
|
194
|
+
normalized.contents = normalizeContents(config.contents);
|
|
195
|
+
normalized.provides = normalizeDatapackProvides(config.provides);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const definition = {
|
|
199
|
+
...normalized,
|
|
200
|
+
[DEFINITION_SYMBOL]: true,
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
return Object.freeze(definition);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export function defineApp(config) {
|
|
207
|
+
return normalizeDefinition("app", config);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export function defineModule(config) {
|
|
211
|
+
return normalizeDefinition("module", config);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export function defineDatapack(config) {
|
|
215
|
+
return normalizeDefinition("datapack", config);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function normalizeArtifactRef(kind, value) {
|
|
219
|
+
if (typeof value === "string") {
|
|
220
|
+
assertString(value, `${kind} ref`);
|
|
221
|
+
const id = value.trim();
|
|
222
|
+
return Object.freeze({
|
|
223
|
+
kind,
|
|
224
|
+
id,
|
|
225
|
+
...(isPackageLikeId(id) ? { packageName: id } : { slug: id }),
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (isArtifactDefinition(value)) {
|
|
230
|
+
if (value.kind !== kind) {
|
|
231
|
+
throw new TypeError(`Expected ${kind} definition but received ${value.kind}.`);
|
|
232
|
+
}
|
|
233
|
+
return Object.freeze({
|
|
234
|
+
kind,
|
|
235
|
+
id: value.id,
|
|
236
|
+
...(isPackageLikeId(value.id) ? { packageName: value.id } : { slug: value.id }),
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (isPlainObject(value)) {
|
|
241
|
+
const rawId = value.id ?? value.packageName ?? value.slug;
|
|
242
|
+
assertString(rawId, `${kind} ref.id`);
|
|
243
|
+
if (value.kind !== undefined && value.kind !== kind) {
|
|
244
|
+
throw new TypeError(`Expected ${kind} ref but received ${value.kind}.`);
|
|
245
|
+
}
|
|
246
|
+
const id = rawId.trim();
|
|
247
|
+
const packageName =
|
|
248
|
+
typeof value.packageName === "string" && value.packageName.trim() !== ""
|
|
249
|
+
? value.packageName.trim()
|
|
250
|
+
: isPackageLikeId(id)
|
|
251
|
+
? id
|
|
252
|
+
: undefined;
|
|
253
|
+
const slug =
|
|
254
|
+
typeof value.slug === "string" && value.slug.trim() !== ""
|
|
255
|
+
? value.slug.trim()
|
|
256
|
+
: !packageName
|
|
257
|
+
? id
|
|
258
|
+
: undefined;
|
|
259
|
+
const version =
|
|
260
|
+
typeof value.version === "string" && value.version.trim() !== ""
|
|
261
|
+
? value.version.trim()
|
|
262
|
+
: undefined;
|
|
263
|
+
return Object.freeze({
|
|
264
|
+
kind,
|
|
265
|
+
id,
|
|
266
|
+
...(packageName ? { packageName } : {}),
|
|
267
|
+
...(slug ? { slug } : {}),
|
|
268
|
+
...(version ? { version } : {}),
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
throw new TypeError(`${kind} ref must be a string, ref object, or Crafter8 definition.`);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
export function createAppRef(value) {
|
|
276
|
+
return normalizeArtifactRef("app", value);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
export function createModuleRef(value) {
|
|
280
|
+
return normalizeArtifactRef("module", value);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
export function createDatapackRef(value) {
|
|
284
|
+
return normalizeArtifactRef("datapack", value);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export function registerDatapackPackageSource(value, source) {
|
|
288
|
+
const datapack = normalizeDatapackSelector(value);
|
|
289
|
+
assertObject(source, "datapack package source");
|
|
290
|
+
const key = datapack.packageName ?? datapack.id;
|
|
291
|
+
if (!key) {
|
|
292
|
+
throw new TypeError("datapack package source requires a packageName or id.");
|
|
293
|
+
}
|
|
294
|
+
const local =
|
|
295
|
+
source.local === undefined || source.local === null
|
|
296
|
+
? null
|
|
297
|
+
: typeof source.local === "function"
|
|
298
|
+
? source.local
|
|
299
|
+
: () => source.local;
|
|
300
|
+
const fallback =
|
|
301
|
+
source.fallback === undefined || source.fallback === null
|
|
302
|
+
? local
|
|
303
|
+
: typeof source.fallback === "function"
|
|
304
|
+
? source.fallback
|
|
305
|
+
: () => source.fallback;
|
|
306
|
+
|
|
307
|
+
if (local !== null) {
|
|
308
|
+
assertFunction(local, "datapack package source.local");
|
|
309
|
+
}
|
|
310
|
+
if (fallback !== null) {
|
|
311
|
+
assertFunction(fallback, "datapack package source.fallback");
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const registered = Object.freeze({
|
|
315
|
+
datapack,
|
|
316
|
+
local,
|
|
317
|
+
fallback,
|
|
318
|
+
});
|
|
319
|
+
REGISTERED_DATAPACK_PACKAGE_SOURCES.set(key, registered);
|
|
320
|
+
if (datapack.id && datapack.id !== key) {
|
|
321
|
+
REGISTERED_DATAPACK_PACKAGE_SOURCES.set(datapack.id, registered);
|
|
322
|
+
}
|
|
323
|
+
if (datapack.slug && datapack.slug !== key) {
|
|
324
|
+
REGISTERED_DATAPACK_PACKAGE_SOURCES.set(datapack.slug, registered);
|
|
325
|
+
}
|
|
326
|
+
return registered;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
export function getRegisteredDatapackPackageSource(value) {
|
|
330
|
+
const datapack = normalizeDatapackSelector(value);
|
|
331
|
+
const keys = [datapack.packageName, datapack.id, datapack.slug].filter(
|
|
332
|
+
(entry) => typeof entry === "string" && entry.trim() !== "",
|
|
333
|
+
);
|
|
334
|
+
for (const key of keys) {
|
|
335
|
+
const registered = REGISTERED_DATAPACK_PACKAGE_SOURCES.get(key);
|
|
336
|
+
if (registered) {
|
|
337
|
+
return registered;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
return null;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
export function isArtifactDefinition(value) {
|
|
344
|
+
return Boolean(value?.[DEFINITION_SYMBOL]);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
export function readArtifactRef(value, expectedKind) {
|
|
348
|
+
const ref = normalizeArtifactRef(expectedKind ?? value?.kind, value);
|
|
349
|
+
if (expectedKind && ref.kind !== expectedKind) {
|
|
350
|
+
throw new TypeError(`Expected ${expectedKind} ref but received ${ref.kind}.`);
|
|
351
|
+
}
|
|
352
|
+
return ref;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
function normalizeClientSessionSnapshot(session) {
|
|
356
|
+
const source = session && typeof session === "object" ? session : {};
|
|
357
|
+
const version = parseHostApiLabel(source.hostApi ?? source.hostApiLabel ?? source.hostApiVersion ?? HOST_API_V1);
|
|
358
|
+
const grantedCapabilities = Array.isArray(source.grantedCapabilities)
|
|
359
|
+
? source.grantedCapabilities
|
|
360
|
+
: Array.isArray(source.capabilities)
|
|
361
|
+
? source.capabilities
|
|
362
|
+
: [];
|
|
363
|
+
return Object.freeze({
|
|
364
|
+
authenticated: Boolean(source.authenticated ?? source.userId),
|
|
365
|
+
userId: typeof source.userId === "string" && source.userId.trim() !== "" ? source.userId.trim() : null,
|
|
366
|
+
userDisplayName:
|
|
367
|
+
typeof source.userDisplayName === "string" && source.userDisplayName.trim() !== ""
|
|
368
|
+
? source.userDisplayName.trim()
|
|
369
|
+
: typeof source.displayName === "string" && source.displayName.trim() !== ""
|
|
370
|
+
? source.displayName.trim()
|
|
371
|
+
: null,
|
|
372
|
+
activeWorkspaceId:
|
|
373
|
+
typeof source.activeWorkspaceId === "string" && source.activeWorkspaceId.trim() !== ""
|
|
374
|
+
? source.activeWorkspaceId.trim()
|
|
375
|
+
: null,
|
|
376
|
+
activeWorkspaceName:
|
|
377
|
+
typeof source.activeWorkspaceName === "string" && source.activeWorkspaceName.trim() !== ""
|
|
378
|
+
? source.activeWorkspaceName.trim()
|
|
379
|
+
: null,
|
|
380
|
+
capabilities: Array.isArray(grantedCapabilities)
|
|
381
|
+
? copyStringArray(grantedCapabilities.filter((entry) => typeof entry === "string"))
|
|
382
|
+
: [],
|
|
383
|
+
hostApi: version.hostApi,
|
|
384
|
+
hostApiLabel: version.hostApiLabel,
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
function normalizeDatapackWarnings(value) {
|
|
389
|
+
if (value === undefined || value === null) {
|
|
390
|
+
return [];
|
|
391
|
+
}
|
|
392
|
+
if (Array.isArray(value)) {
|
|
393
|
+
return value
|
|
394
|
+
.filter((entry) => typeof entry === "string" && entry.trim() !== "")
|
|
395
|
+
.map((entry) => entry.trim());
|
|
396
|
+
}
|
|
397
|
+
if (typeof value === "string" && value.trim() !== "") {
|
|
398
|
+
return [value.trim()];
|
|
399
|
+
}
|
|
400
|
+
throw new TypeError("datapack warnings must be a string or string array.");
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
function normalizeDatapackSelector(value) {
|
|
404
|
+
const ref = readArtifactRef(value, "datapack");
|
|
405
|
+
return Object.freeze({
|
|
406
|
+
kind: "datapack",
|
|
407
|
+
id: ref.id,
|
|
408
|
+
...(typeof ref.packageName === "string" && ref.packageName.trim() !== "" ? { packageName: ref.packageName.trim() } : {}),
|
|
409
|
+
...(typeof ref.slug === "string" && ref.slug.trim() !== "" ? { slug: ref.slug.trim() } : {}),
|
|
410
|
+
...(typeof ref.version === "string" && ref.version.trim() !== "" ? { version: ref.version.trim() } : {}),
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
function normalizeDatapackResolverOptions(options) {
|
|
415
|
+
if (options === undefined) {
|
|
416
|
+
return Object.freeze({
|
|
417
|
+
mode: "remote-first",
|
|
418
|
+
localResolver: null,
|
|
419
|
+
fallbackResolver: null,
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
assertObject(options, "datapack client options");
|
|
424
|
+
const mode =
|
|
425
|
+
options.mode === undefined
|
|
426
|
+
? "remote-first"
|
|
427
|
+
: options.mode === "local-first" || options.mode === "remote-first"
|
|
428
|
+
? options.mode
|
|
429
|
+
: null;
|
|
430
|
+
if (!mode) {
|
|
431
|
+
throw new TypeError('datapack client mode must be "local-first" or "remote-first".');
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
const localResolver =
|
|
435
|
+
options.localResolver === undefined || options.localResolver === null ? null : options.localResolver;
|
|
436
|
+
const fallbackResolver =
|
|
437
|
+
options.fallbackResolver === undefined || options.fallbackResolver === null ? null : options.fallbackResolver;
|
|
438
|
+
|
|
439
|
+
if (localResolver !== null) {
|
|
440
|
+
assertFunction(localResolver, "datapack localResolver");
|
|
441
|
+
}
|
|
442
|
+
if (fallbackResolver !== null) {
|
|
443
|
+
assertFunction(fallbackResolver, "datapack fallbackResolver");
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
return Object.freeze({
|
|
447
|
+
mode,
|
|
448
|
+
localResolver,
|
|
449
|
+
fallbackResolver,
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
function buildPublishedDatapackContentPath(slug, key) {
|
|
454
|
+
const params = new URLSearchParams();
|
|
455
|
+
params.set("key", key);
|
|
456
|
+
return `/api/datapacks/${encodeURIComponent(slug)}/content?${params.toString()}`;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
function normalizeResolvedDatapackContent(sourceKind, datapack, key, response, warningList = []) {
|
|
460
|
+
const normalizedDatapack = normalizeDatapackSelector(datapack);
|
|
461
|
+
const normalizedKey = String(key || "").trim();
|
|
462
|
+
const data = response?.data ?? response ?? {};
|
|
463
|
+
const content = data?.content ?? {};
|
|
464
|
+
const resolvedDatapack = Object.freeze({
|
|
465
|
+
...normalizedDatapack,
|
|
466
|
+
...(typeof data?.datapack?.packageName === "string" && data.datapack.packageName.trim() !== ""
|
|
467
|
+
? { packageName: data.datapack.packageName.trim() }
|
|
468
|
+
: {}),
|
|
469
|
+
...(typeof data?.datapack?.slug === "string" && data.datapack.slug.trim() !== ""
|
|
470
|
+
? { slug: data.datapack.slug.trim() }
|
|
471
|
+
: normalizedDatapack.slug
|
|
472
|
+
? { slug: normalizedDatapack.slug }
|
|
473
|
+
: {}),
|
|
474
|
+
...(typeof data?.datapack?.version === "string" && data.datapack.version.trim() !== ""
|
|
475
|
+
? { version: data.datapack.version.trim() }
|
|
476
|
+
: normalizedDatapack.version
|
|
477
|
+
? { version: normalizedDatapack.version }
|
|
478
|
+
: {}),
|
|
479
|
+
});
|
|
480
|
+
const resolvedSlug = resolvedDatapack.slug ?? normalizedDatapack.slug ?? normalizedDatapack.id;
|
|
481
|
+
return Object.freeze({
|
|
482
|
+
sourceKind,
|
|
483
|
+
datapack: resolvedDatapack,
|
|
484
|
+
key: normalizedKey,
|
|
485
|
+
url:
|
|
486
|
+
typeof content?.url === "string" && content.url.trim() !== ""
|
|
487
|
+
? content.url.trim()
|
|
488
|
+
: sourceKind === "published"
|
|
489
|
+
? buildPublishedDatapackContentPath(resolvedSlug, normalizedKey)
|
|
490
|
+
: `${sourceKind}://${encodeURIComponent(resolvedDatapack.packageName || resolvedDatapack.id)}/${encodeURIComponent(normalizedKey)}`,
|
|
491
|
+
contentType:
|
|
492
|
+
typeof content?.contentType === "string" && content.contentType.trim() !== ""
|
|
493
|
+
? content.contentType.trim()
|
|
494
|
+
: "application/json",
|
|
495
|
+
cacheKey:
|
|
496
|
+
typeof content?.cacheKey === "string" && content.cacheKey.trim() !== ""
|
|
497
|
+
? content.cacheKey.trim()
|
|
498
|
+
: `${sourceKind}:${resolvedDatapack.packageName || resolvedDatapack.id}:${normalizedKey}`,
|
|
499
|
+
...(typeof content?.integrity === "string" && content.integrity.trim() !== ""
|
|
500
|
+
? { integrity: content.integrity.trim() }
|
|
501
|
+
: {}),
|
|
502
|
+
...(typeof content?.encoding === "string" && content.encoding.trim() !== ""
|
|
503
|
+
? { encoding: content.encoding.trim() }
|
|
504
|
+
: {}),
|
|
505
|
+
requiresAuth:
|
|
506
|
+
typeof content?.requiresAuth === "boolean" ? content.requiresAuth : sourceKind === "published",
|
|
507
|
+
expiresAt:
|
|
508
|
+
typeof content?.expiresAt === "string" && content.expiresAt.trim() !== "" ? content.expiresAt.trim() : null,
|
|
509
|
+
warnings: copyStringArray(normalizeDatapackWarnings([].concat(warningList, normalizeDatapackWarnings(content?.warnings)))),
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
function normalizeDatapackContentValue(response) {
|
|
514
|
+
const data = response?.data ?? response ?? {};
|
|
515
|
+
const content = data?.content ?? null;
|
|
516
|
+
if (content && Object.prototype.hasOwnProperty.call(content, "parsed")) {
|
|
517
|
+
return content.parsed;
|
|
518
|
+
}
|
|
519
|
+
if (content && typeof content.text === "string") {
|
|
520
|
+
return content.text;
|
|
521
|
+
}
|
|
522
|
+
return content;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
function hasInlineDatapackContentValue(response) {
|
|
526
|
+
const data = response?.data ?? response ?? {};
|
|
527
|
+
const content = data?.content ?? null;
|
|
528
|
+
return Boolean(
|
|
529
|
+
content &&
|
|
530
|
+
(Object.prototype.hasOwnProperty.call(content, "parsed") ||
|
|
531
|
+
typeof content.text === "string" ||
|
|
532
|
+
content.kind === "dir"),
|
|
533
|
+
);
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
function resolveFetchableAssetUrl(url, assetBaseUrl) {
|
|
537
|
+
const normalizedUrl = String(url || "").trim();
|
|
538
|
+
if (!normalizedUrl) {
|
|
539
|
+
throw new Error("Resolved datapack content URL is missing.");
|
|
540
|
+
}
|
|
541
|
+
if (/^[a-z]+:\/\//i.test(normalizedUrl)) {
|
|
542
|
+
return normalizedUrl;
|
|
543
|
+
}
|
|
544
|
+
const normalizedBase =
|
|
545
|
+
typeof assetBaseUrl === "string" && assetBaseUrl.trim() !== ""
|
|
546
|
+
? assetBaseUrl.trim()
|
|
547
|
+
: typeof globalThis.location?.origin === "string" && globalThis.location.origin.trim() !== ""
|
|
548
|
+
? globalThis.location.origin.trim()
|
|
549
|
+
: "";
|
|
550
|
+
if (!normalizedBase) {
|
|
551
|
+
return normalizedUrl;
|
|
552
|
+
}
|
|
553
|
+
return new URL(normalizedUrl, normalizedBase).toString();
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
async function fetchResolvedDatapackContentAsset(response, options = {}) {
|
|
557
|
+
const data = response?.data ?? response ?? {};
|
|
558
|
+
const content = data?.content ?? null;
|
|
559
|
+
const fetchImpl = options.assetFetch ?? globalThis.fetch;
|
|
560
|
+
if (typeof fetchImpl !== "function") {
|
|
561
|
+
throw new TypeError("Reading published datapack assets requires assetFetch or global fetch.");
|
|
562
|
+
}
|
|
563
|
+
if (!content || typeof content !== "object" || typeof content.url !== "string" || content.url.trim() === "") {
|
|
564
|
+
return normalizeDatapackContentValue(response);
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
const url = resolveFetchableAssetUrl(content.url, options.assetBaseUrl);
|
|
568
|
+
const res = await fetchImpl(url, {
|
|
569
|
+
method: "GET",
|
|
570
|
+
headers: {
|
|
571
|
+
Accept: typeof content.contentType === "string" && content.contentType.trim() !== "" ? content.contentType.trim() : "*/*",
|
|
572
|
+
},
|
|
573
|
+
});
|
|
574
|
+
if (!res || typeof res !== "object" || typeof res.ok !== "boolean") {
|
|
575
|
+
throw new Error(`assetFetch returned an invalid response while reading "${content.url}".`);
|
|
576
|
+
}
|
|
577
|
+
if (!res.ok) {
|
|
578
|
+
throw new Error(`Failed to read published datapack asset "${content.url}": HTTP ${res.status}`);
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
const contentTypeHeader = typeof res.headers?.get === "function" ? String(res.headers.get("content-type") || "") : "";
|
|
582
|
+
const normalizedContentType = String(content.contentType || contentTypeHeader || "").toLowerCase();
|
|
583
|
+
if (normalizedContentType.includes("json")) {
|
|
584
|
+
return res.json();
|
|
585
|
+
}
|
|
586
|
+
if (
|
|
587
|
+
normalizedContentType.startsWith("text/") ||
|
|
588
|
+
normalizedContentType.includes("xml") ||
|
|
589
|
+
normalizedContentType.includes("yaml") ||
|
|
590
|
+
normalizedContentType.includes("csv")
|
|
591
|
+
) {
|
|
592
|
+
return res.text();
|
|
593
|
+
}
|
|
594
|
+
return res.arrayBuffer();
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
function normalizeDatapackCandidateResult(sourceKind, datapack, key, candidate, warnings = []) {
|
|
598
|
+
assertObject(candidate, `${sourceKind} datapack source candidate`);
|
|
599
|
+
|
|
600
|
+
const response = {
|
|
601
|
+
data: {
|
|
602
|
+
datapack: {
|
|
603
|
+
packageName:
|
|
604
|
+
typeof candidate.packageName === "string" && candidate.packageName.trim() !== ""
|
|
605
|
+
? candidate.packageName.trim()
|
|
606
|
+
: datapack.packageName ?? undefined,
|
|
607
|
+
slug:
|
|
608
|
+
typeof candidate.slug === "string" && candidate.slug.trim() !== ""
|
|
609
|
+
? candidate.slug.trim()
|
|
610
|
+
: datapack.slug ?? undefined,
|
|
611
|
+
version:
|
|
612
|
+
typeof candidate.version === "string" && candidate.version.trim() !== ""
|
|
613
|
+
? candidate.version.trim()
|
|
614
|
+
: datapack.version ?? undefined,
|
|
615
|
+
},
|
|
616
|
+
content: {
|
|
617
|
+
url:
|
|
618
|
+
typeof candidate.url === "string" && candidate.url.trim() !== ""
|
|
619
|
+
? candidate.url.trim()
|
|
620
|
+
: undefined,
|
|
621
|
+
contentType:
|
|
622
|
+
typeof candidate.contentType === "string" && candidate.contentType.trim() !== ""
|
|
623
|
+
? candidate.contentType.trim()
|
|
624
|
+
: undefined,
|
|
625
|
+
cacheKey:
|
|
626
|
+
typeof candidate.cacheKey === "string" && candidate.cacheKey.trim() !== ""
|
|
627
|
+
? candidate.cacheKey.trim()
|
|
628
|
+
: undefined,
|
|
629
|
+
integrity:
|
|
630
|
+
typeof candidate.integrity === "string" && candidate.integrity.trim() !== ""
|
|
631
|
+
? candidate.integrity.trim()
|
|
632
|
+
: undefined,
|
|
633
|
+
encoding:
|
|
634
|
+
typeof candidate.encoding === "string" && candidate.encoding.trim() !== ""
|
|
635
|
+
? candidate.encoding.trim()
|
|
636
|
+
: undefined,
|
|
637
|
+
requiresAuth: typeof candidate.requiresAuth === "boolean" ? candidate.requiresAuth : undefined,
|
|
638
|
+
expiresAt:
|
|
639
|
+
typeof candidate.expiresAt === "string" && candidate.expiresAt.trim() !== ""
|
|
640
|
+
? candidate.expiresAt.trim()
|
|
641
|
+
: candidate.expiresAt === null
|
|
642
|
+
? null
|
|
643
|
+
: undefined,
|
|
644
|
+
warnings: normalizeDatapackWarnings(candidate.warnings),
|
|
645
|
+
},
|
|
646
|
+
},
|
|
647
|
+
};
|
|
648
|
+
|
|
649
|
+
const descriptor = normalizeResolvedDatapackContent(sourceKind, datapack, key, response, warnings);
|
|
650
|
+
const hasValue = Object.prototype.hasOwnProperty.call(candidate, "value");
|
|
651
|
+
const read =
|
|
652
|
+
typeof candidate.read === "function"
|
|
653
|
+
? async () => candidate.read()
|
|
654
|
+
: hasValue
|
|
655
|
+
? async () => candidate.value
|
|
656
|
+
: async () => {
|
|
657
|
+
throw new Error(
|
|
658
|
+
`${sourceKind} datapack source for "${descriptor.datapack.packageName || descriptor.datapack.id}" and key "${descriptor.key}" does not provide value or read().`,
|
|
659
|
+
);
|
|
660
|
+
};
|
|
661
|
+
|
|
662
|
+
return Object.freeze({
|
|
663
|
+
descriptor,
|
|
664
|
+
read,
|
|
665
|
+
});
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
function flattenOperationCatalogEntries(entries) {
|
|
669
|
+
const list = Array.isArray(entries) ? entries : [];
|
|
670
|
+
return list.flatMap((entry) =>
|
|
671
|
+
Array.isArray(entry?.operations)
|
|
672
|
+
? entry.operations.map((operation) =>
|
|
673
|
+
normalizeOperationDescriptor(
|
|
674
|
+
{
|
|
675
|
+
kind: entry.kind,
|
|
676
|
+
scope: entry.scope,
|
|
677
|
+
selectors: entry.selectors,
|
|
678
|
+
resolvedUser: null,
|
|
679
|
+
artifactRef: null,
|
|
680
|
+
artifact: null,
|
|
681
|
+
},
|
|
682
|
+
operation,
|
|
683
|
+
),
|
|
684
|
+
)
|
|
685
|
+
: [],
|
|
686
|
+
);
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
function hasResolvedArtifactSelector(selector = {}) {
|
|
690
|
+
return Boolean(selector.packageName || selector.slug || selector.id || selector.user || selector.operationId);
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
function buildTransportRequest(method, path, query, body, headers) {
|
|
694
|
+
const normalizedMethod = String(method || "GET").toUpperCase();
|
|
695
|
+
const request = {
|
|
696
|
+
method: normalizedMethod,
|
|
697
|
+
path,
|
|
698
|
+
};
|
|
699
|
+
if (query && typeof query === "object" && Object.keys(query).length > 0) {
|
|
700
|
+
request.query = query;
|
|
701
|
+
}
|
|
702
|
+
if (headers && typeof headers === "object" && Object.keys(headers).length > 0) {
|
|
703
|
+
request.headers = headers;
|
|
704
|
+
}
|
|
705
|
+
if (body !== undefined && normalizedMethod !== "GET" && normalizedMethod !== "HEAD") {
|
|
706
|
+
request.body = body;
|
|
707
|
+
}
|
|
708
|
+
return request;
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
function assertTransport(transport) {
|
|
712
|
+
if (!transport || typeof transport !== "object" || typeof transport.request !== "function") {
|
|
713
|
+
throw new TypeError("transport.request must be a function.");
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
async function resolvePublishedDatapackIdentity(transport, datapack) {
|
|
718
|
+
const normalized = normalizeDatapackSelector(datapack);
|
|
719
|
+
if (normalized.slug) {
|
|
720
|
+
return normalized;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
const packageName = normalized.packageName ?? (isPackageLikeId(normalized.id) ? normalized.id : null);
|
|
724
|
+
if (packageName) {
|
|
725
|
+
try {
|
|
726
|
+
const response = await transport.request(
|
|
727
|
+
buildTransportRequest("GET", "/api/artifacts", {
|
|
728
|
+
kind: "datapack",
|
|
729
|
+
packageName,
|
|
730
|
+
}),
|
|
731
|
+
);
|
|
732
|
+
const records = Array.isArray(response?.data?.artifacts) ? response.data.artifacts : [];
|
|
733
|
+
const match = records.find((entry) => typeof entry?.slug === "string" && entry.slug.trim() !== "");
|
|
734
|
+
if (match) {
|
|
735
|
+
return Object.freeze({
|
|
736
|
+
...normalized,
|
|
737
|
+
packageName,
|
|
738
|
+
slug: match.slug.trim(),
|
|
739
|
+
...(typeof match.version === "string" && match.version.trim() !== ""
|
|
740
|
+
? { version: match.version.trim() }
|
|
741
|
+
: normalized.version
|
|
742
|
+
? { version: normalized.version }
|
|
743
|
+
: {}),
|
|
744
|
+
});
|
|
745
|
+
}
|
|
746
|
+
} catch {
|
|
747
|
+
// Allow fallback to non-published resolution modes.
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
if (!packageName) {
|
|
752
|
+
return Object.freeze({
|
|
753
|
+
...normalized,
|
|
754
|
+
slug: normalized.id,
|
|
755
|
+
});
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
throw new Error(`Unable to resolve a published datapack release for "${packageName}".`);
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
async function tryResolveDatapackCandidate(resolver, sourceKind, datapack, key, warnings = []) {
|
|
762
|
+
if (typeof resolver !== "function") {
|
|
763
|
+
return null;
|
|
764
|
+
}
|
|
765
|
+
const candidate = await resolver({
|
|
766
|
+
datapack,
|
|
767
|
+
key,
|
|
768
|
+
});
|
|
769
|
+
if (!candidate) {
|
|
770
|
+
return null;
|
|
771
|
+
}
|
|
772
|
+
return normalizeDatapackCandidateResult(sourceKind, datapack, key, candidate, warnings);
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
async function tryResolveRegisteredDatapackCandidate(sourceKind, datapack, key, warnings = []) {
|
|
776
|
+
const registered = getRegisteredDatapackPackageSource(datapack);
|
|
777
|
+
if (!registered) {
|
|
778
|
+
return null;
|
|
779
|
+
}
|
|
780
|
+
const resolver = sourceKind === "local" ? registered.local : registered.fallback;
|
|
781
|
+
if (typeof resolver !== "function") {
|
|
782
|
+
return null;
|
|
783
|
+
}
|
|
784
|
+
const candidate = await resolver({
|
|
785
|
+
datapack: registered.datapack,
|
|
786
|
+
key,
|
|
787
|
+
});
|
|
788
|
+
if (!candidate) {
|
|
789
|
+
return null;
|
|
790
|
+
}
|
|
791
|
+
return normalizeDatapackCandidateResult(sourceKind, registered.datapack, key, candidate, warnings);
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
async function tryResolvePublishedDatapackSource(transport, datapack, key, assetOptions = {}) {
|
|
795
|
+
const publishedDatapack = await resolvePublishedDatapackIdentity(transport, datapack);
|
|
796
|
+
const response = await transport.request(
|
|
797
|
+
buildTransportRequest("GET", `/api/datapacks/${encodeURIComponent(publishedDatapack.slug)}/resolve-content`, {
|
|
798
|
+
key,
|
|
799
|
+
}),
|
|
800
|
+
);
|
|
801
|
+
return Object.freeze({
|
|
802
|
+
descriptor: normalizeResolvedDatapackContent("published", publishedDatapack, key, response),
|
|
803
|
+
async read() {
|
|
804
|
+
if (hasInlineDatapackContentValue(response)) {
|
|
805
|
+
return normalizeDatapackContentValue(response);
|
|
806
|
+
}
|
|
807
|
+
return fetchResolvedDatapackContentAsset(response, assetOptions);
|
|
808
|
+
},
|
|
809
|
+
});
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
async function resolveDatapackSource(transport, datapackOptions, selector, key, assetOptions = {}) {
|
|
813
|
+
const datapack = normalizeDatapackSelector(selector);
|
|
814
|
+
assertString(key, "datapack content key");
|
|
815
|
+
const normalizedKey = key.trim();
|
|
816
|
+
|
|
817
|
+
if (datapackOptions.mode === "local-first") {
|
|
818
|
+
const local =
|
|
819
|
+
(await tryResolveDatapackCandidate(datapackOptions.localResolver, "local", datapack, normalizedKey)) ||
|
|
820
|
+
(await tryResolveRegisteredDatapackCandidate("local", datapack, normalizedKey));
|
|
821
|
+
if (local) {
|
|
822
|
+
return local;
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
try {
|
|
826
|
+
return await tryResolvePublishedDatapackSource(transport, datapack, normalizedKey, assetOptions);
|
|
827
|
+
} catch {
|
|
828
|
+
const fallback =
|
|
829
|
+
(await tryResolveDatapackCandidate(datapackOptions.fallbackResolver, "fallback", datapack, normalizedKey)) ||
|
|
830
|
+
(await tryResolveRegisteredDatapackCandidate("fallback", datapack, normalizedKey));
|
|
831
|
+
if (fallback) {
|
|
832
|
+
return fallback;
|
|
833
|
+
}
|
|
834
|
+
throw new Error(`Unable to resolve datapack content "${normalizedKey}" for "${datapack.packageName || datapack.id}".`);
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
try {
|
|
839
|
+
return await tryResolvePublishedDatapackSource(transport, datapack, normalizedKey, assetOptions);
|
|
840
|
+
} catch {
|
|
841
|
+
const fallbackWarnings = [
|
|
842
|
+
`Datapack "${datapack.packageName || datapack.id}" is not published in Crafter8. Using fallback distribution for content "${normalizedKey}".`,
|
|
843
|
+
];
|
|
844
|
+
const fallback =
|
|
845
|
+
(await tryResolveDatapackCandidate(
|
|
846
|
+
datapackOptions.fallbackResolver,
|
|
847
|
+
"fallback",
|
|
848
|
+
datapack,
|
|
849
|
+
normalizedKey,
|
|
850
|
+
fallbackWarnings,
|
|
851
|
+
)) ||
|
|
852
|
+
(await tryResolveRegisteredDatapackCandidate("fallback", datapack, normalizedKey, fallbackWarnings));
|
|
853
|
+
if (fallback) {
|
|
854
|
+
return fallback;
|
|
855
|
+
}
|
|
856
|
+
throw new Error(`Unable to resolve datapack content "${normalizedKey}" for "${datapack.packageName || datapack.id}".`);
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
export function isCrafter8HostServices(value) {
|
|
861
|
+
return Boolean(
|
|
862
|
+
value &&
|
|
863
|
+
typeof value === "object" &&
|
|
864
|
+
value.navigation &&
|
|
865
|
+
typeof value.navigation === "object" &&
|
|
866
|
+
typeof value.navigation.navigate === "function" &&
|
|
867
|
+
typeof value.navigation.openItemInGraph === "function",
|
|
868
|
+
);
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
export function createCrafter8HostServices(host) {
|
|
872
|
+
assertObject(host, "host services");
|
|
873
|
+
|
|
874
|
+
if (isCrafter8HostServices(host)) {
|
|
875
|
+
return host;
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
assertHostMethod(host, "navigateToApp");
|
|
879
|
+
assertHostMethod(host, "openItemInGraph");
|
|
880
|
+
|
|
881
|
+
return Object.freeze({
|
|
882
|
+
navigation: Object.freeze({
|
|
883
|
+
navigate(path) {
|
|
884
|
+
assertString(path, "navigation path");
|
|
885
|
+
return host.navigateToApp(path.trim());
|
|
886
|
+
},
|
|
887
|
+
openItemInGraph(itemId) {
|
|
888
|
+
assertString(itemId, "itemId");
|
|
889
|
+
return host.openItemInGraph(itemId.trim());
|
|
890
|
+
},
|
|
891
|
+
}),
|
|
892
|
+
});
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
export function createPublicHttpTransport(options) {
|
|
896
|
+
assertObject(options, "public transport options");
|
|
897
|
+
assertString(options.baseUrl, "public transport baseUrl");
|
|
898
|
+
const baseUrl = options.baseUrl.trim().replace(/\/+$/, "");
|
|
899
|
+
const fetchImpl = options.fetchImpl ?? globalThis.fetch;
|
|
900
|
+
if (typeof fetchImpl !== "function") {
|
|
901
|
+
throw new TypeError("public transport requires fetchImpl or global fetch.");
|
|
902
|
+
}
|
|
903
|
+
const auth = options.auth;
|
|
904
|
+
|
|
905
|
+
return Object.freeze({
|
|
906
|
+
async request(req) {
|
|
907
|
+
assertObject(req, "transport request");
|
|
908
|
+
const method = String(req.method || "GET").toUpperCase();
|
|
909
|
+
const path = appendQueryToPath(req.path, req.query);
|
|
910
|
+
const url = /^https?:\/\//i.test(path) ? path : `${baseUrl}${path.startsWith("/") ? path : `/${path}`}`;
|
|
911
|
+
const headers = new Headers(req.headers || {});
|
|
912
|
+
if (!headers.has("Accept")) headers.set("Accept", "application/json");
|
|
913
|
+
if (auth && typeof auth.getHeaders === "function") {
|
|
914
|
+
const authHeaders = await auth.getHeaders();
|
|
915
|
+
if (authHeaders && typeof authHeaders === "object") {
|
|
916
|
+
for (const [key, value] of Object.entries(authHeaders)) {
|
|
917
|
+
if (typeof value === "string") {
|
|
918
|
+
headers.set(key, value);
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
const init = { method, headers };
|
|
924
|
+
if (req.body !== undefined && method !== "GET" && method !== "HEAD") {
|
|
925
|
+
if (!headers.has("Content-Type")) {
|
|
926
|
+
headers.set("Content-Type", "application/json");
|
|
927
|
+
}
|
|
928
|
+
init.body = headers.get("Content-Type")?.includes("application/json") ? JSON.stringify(req.body) : req.body;
|
|
929
|
+
}
|
|
930
|
+
const res = await fetchImpl(url, init);
|
|
931
|
+
const text = await res.text();
|
|
932
|
+
const data = text ? JSON.parse(text) : null;
|
|
933
|
+
if (!res.ok) {
|
|
934
|
+
throw new Error(data?.error?.message || `HTTP ${res.status}`);
|
|
935
|
+
}
|
|
936
|
+
return data;
|
|
937
|
+
},
|
|
938
|
+
});
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
export function createEmbeddedHostTransport(host) {
|
|
942
|
+
assertObject(host, "embedded host");
|
|
943
|
+
assertHostMethod(host, "fetchJson");
|
|
944
|
+
|
|
945
|
+
return Object.freeze({
|
|
946
|
+
async request(req) {
|
|
947
|
+
assertObject(req, "transport request");
|
|
948
|
+
const method = String(req.method || "GET").toUpperCase();
|
|
949
|
+
const path = appendQueryToPath(req.path, req.query);
|
|
950
|
+
const headers = { ...(req.headers || {}) };
|
|
951
|
+
const init = { method, headers };
|
|
952
|
+
if (req.body !== undefined && method !== "GET" && method !== "HEAD") {
|
|
953
|
+
if (!headers["Content-Type"] && !headers["content-type"]) {
|
|
954
|
+
headers["Content-Type"] = "application/json";
|
|
955
|
+
}
|
|
956
|
+
init.body =
|
|
957
|
+
String(headers["Content-Type"] || headers["content-type"] || "").includes("application/json")
|
|
958
|
+
? JSON.stringify(req.body)
|
|
959
|
+
: req.body;
|
|
960
|
+
}
|
|
961
|
+
return host.fetchJson(path, init);
|
|
962
|
+
},
|
|
963
|
+
});
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
export function isCrafter8Client(value) {
|
|
967
|
+
return Boolean(
|
|
968
|
+
value &&
|
|
969
|
+
typeof value === "object" &&
|
|
970
|
+
value.session &&
|
|
971
|
+
typeof value.session === "object" &&
|
|
972
|
+
typeof value.session.get === "function" &&
|
|
973
|
+
value.artifacts &&
|
|
974
|
+
typeof value.artifacts === "object" &&
|
|
975
|
+
typeof value.artifacts.list === "function" &&
|
|
976
|
+
typeof value.artifacts.get === "function" &&
|
|
977
|
+
value.datapacks &&
|
|
978
|
+
typeof value.datapacks === "object" &&
|
|
979
|
+
typeof value.datapacks.list === "function" &&
|
|
980
|
+
typeof value.datapacks.getManifest === "function" &&
|
|
981
|
+
typeof value.datapacks.listContents === "function" &&
|
|
982
|
+
typeof value.datapacks.resolveContent === "function" &&
|
|
983
|
+
typeof value.datapacks.readContent === "function" &&
|
|
984
|
+
value.operations &&
|
|
985
|
+
typeof value.operations === "object" &&
|
|
986
|
+
typeof value.operations.list === "function" &&
|
|
987
|
+
typeof value.operations.get === "function" &&
|
|
988
|
+
typeof value.operations.invoke === "function",
|
|
989
|
+
);
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
export function createCrafter8Client(args) {
|
|
993
|
+
assertObject(args, "client args");
|
|
994
|
+
assertTransport(args.transport);
|
|
995
|
+
const { transport } = args;
|
|
996
|
+
const sessionResolver = args.sessionResolver;
|
|
997
|
+
const datapackOptions = normalizeDatapackResolverOptions(args.datapacks);
|
|
998
|
+
const assetOptions = {
|
|
999
|
+
...(typeof args.assetFetch === "function" ? { assetFetch: args.assetFetch } : {}),
|
|
1000
|
+
...(typeof args.assetBaseUrl === "string" && args.assetBaseUrl.trim() !== ""
|
|
1001
|
+
? { assetBaseUrl: args.assetBaseUrl.trim() }
|
|
1002
|
+
: {}),
|
|
1003
|
+
};
|
|
1004
|
+
|
|
1005
|
+
const client = {
|
|
1006
|
+
session: Object.freeze({
|
|
1007
|
+
async get() {
|
|
1008
|
+
if (typeof sessionResolver === "function") {
|
|
1009
|
+
return normalizeClientSessionSnapshot(await sessionResolver());
|
|
1010
|
+
}
|
|
1011
|
+
const response = await transport.request(buildTransportRequest("GET", "/api/session/v1"));
|
|
1012
|
+
return normalizeClientSessionSnapshot(response?.data?.session ?? response?.data ?? response);
|
|
1013
|
+
},
|
|
1014
|
+
}),
|
|
1015
|
+
artifacts: Object.freeze({
|
|
1016
|
+
async list(selector = {}) {
|
|
1017
|
+
const response = await transport.request(
|
|
1018
|
+
buildTransportRequest("GET", "/api/artifacts", selector, undefined, undefined),
|
|
1019
|
+
);
|
|
1020
|
+
return Array.isArray(response?.data?.artifacts) ? response.data.artifacts : [];
|
|
1021
|
+
},
|
|
1022
|
+
async get(selector = {}) {
|
|
1023
|
+
const records = await this.list(selector);
|
|
1024
|
+
return records[0] ?? null;
|
|
1025
|
+
},
|
|
1026
|
+
}),
|
|
1027
|
+
datapacks: Object.freeze({
|
|
1028
|
+
async list() {
|
|
1029
|
+
const response = await transport.request(buildTransportRequest("GET", "/api/datapacks"));
|
|
1030
|
+
return Array.isArray(response?.data?.datapacks) ? response.data.datapacks : [];
|
|
1031
|
+
},
|
|
1032
|
+
async getManifest(slug) {
|
|
1033
|
+
assertString(slug, "datapack slug");
|
|
1034
|
+
const response = await transport.request(
|
|
1035
|
+
buildTransportRequest("GET", `/api/datapacks/${encodeURIComponent(slug.trim())}/manifest`),
|
|
1036
|
+
);
|
|
1037
|
+
return response?.data ?? response;
|
|
1038
|
+
},
|
|
1039
|
+
async listContents(slug) {
|
|
1040
|
+
assertString(slug, "datapack slug");
|
|
1041
|
+
const response = await transport.request(
|
|
1042
|
+
buildTransportRequest("GET", `/api/datapacks/${encodeURIComponent(slug.trim())}/contents`),
|
|
1043
|
+
);
|
|
1044
|
+
return response?.data ?? response;
|
|
1045
|
+
},
|
|
1046
|
+
async resolveContent(slug, key) {
|
|
1047
|
+
const resolved = await resolveDatapackSource(transport, datapackOptions, slug, key, assetOptions);
|
|
1048
|
+
return resolved.descriptor;
|
|
1049
|
+
},
|
|
1050
|
+
async readContent(slug, key) {
|
|
1051
|
+
const resolved = await resolveDatapackSource(transport, datapackOptions, slug, key, assetOptions);
|
|
1052
|
+
return resolved.read();
|
|
1053
|
+
},
|
|
1054
|
+
}),
|
|
1055
|
+
operations: Object.freeze({
|
|
1056
|
+
async list(selector = {}) {
|
|
1057
|
+
const response = await transport.request(
|
|
1058
|
+
buildTransportRequest("GET", "/api/artifacts/operations", selector, undefined, undefined),
|
|
1059
|
+
);
|
|
1060
|
+
const catalog = Array.isArray(response?.data?.catalog) ? response.data.catalog : [];
|
|
1061
|
+
const resolved = response?.data?.resolved ?? null;
|
|
1062
|
+
const operationId =
|
|
1063
|
+
typeof selector.operationId === "string" && selector.operationId.trim() !== "" ? selector.operationId.trim() : "";
|
|
1064
|
+
|
|
1065
|
+
if (resolved && Array.isArray(resolved.operations) && hasResolvedArtifactSelector(selector)) {
|
|
1066
|
+
return resolved.operations
|
|
1067
|
+
.filter((operation) => (!operationId ? true : String(operation?.id || "") === operationId))
|
|
1068
|
+
.map((operation) => normalizeOperationDescriptor(resolved, operation));
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
return flattenOperationCatalogEntries(catalog).filter((entry) =>
|
|
1072
|
+
!operationId ? true : String(entry.operation?.id || "") === operationId,
|
|
1073
|
+
);
|
|
1074
|
+
},
|
|
1075
|
+
async get(selector) {
|
|
1076
|
+
assertObject(selector, "operation selector");
|
|
1077
|
+
const response = await transport.request(
|
|
1078
|
+
buildTransportRequest("GET", "/api/artifacts/operations", selector, undefined, undefined),
|
|
1079
|
+
);
|
|
1080
|
+
const resolved = response?.data?.resolved ?? null;
|
|
1081
|
+
const operationId =
|
|
1082
|
+
typeof selector.operationId === "string" && selector.operationId.trim() !== "" ? selector.operationId.trim() : "";
|
|
1083
|
+
|
|
1084
|
+
if (resolved && Array.isArray(resolved.operations)) {
|
|
1085
|
+
const operation = operationId
|
|
1086
|
+
? resolved.operations.find((entry) => String(entry?.id || "") === operationId)
|
|
1087
|
+
: resolved.operations[0];
|
|
1088
|
+
return operation ? normalizeOperationDescriptor(resolved, operation) : null;
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
const catalogEntries = flattenOperationCatalogEntries(Array.isArray(response?.data?.catalog) ? response.data.catalog : []);
|
|
1092
|
+
return operationId
|
|
1093
|
+
? catalogEntries.find((entry) => String(entry.operation?.id || "") === operationId) ?? null
|
|
1094
|
+
: catalogEntries[0] ?? null;
|
|
1095
|
+
},
|
|
1096
|
+
async invoke(selector, input) {
|
|
1097
|
+
assertObject(selector, "operation selector");
|
|
1098
|
+
const resolved = await this.get(selector);
|
|
1099
|
+
if (!resolved) {
|
|
1100
|
+
throw new Error("Operation not found.");
|
|
1101
|
+
}
|
|
1102
|
+
const request = buildOperationRequest(resolved.operation, selector, input);
|
|
1103
|
+
const response = await transport.request(request);
|
|
1104
|
+
return response?.data ?? response;
|
|
1105
|
+
},
|
|
1106
|
+
}),
|
|
1107
|
+
};
|
|
1108
|
+
|
|
1109
|
+
return Object.freeze(client);
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
export function createEmbeddedCrafter8Client(host, options = {}) {
|
|
1113
|
+
assertObject(host, "embedded host");
|
|
1114
|
+
if (isCrafter8Client(host)) {
|
|
1115
|
+
return host;
|
|
1116
|
+
}
|
|
1117
|
+
const transport = createEmbeddedHostTransport(host);
|
|
1118
|
+
if (options === null || typeof options !== "object") {
|
|
1119
|
+
throw new TypeError("embedded client options must be an object.");
|
|
1120
|
+
}
|
|
1121
|
+
return createCrafter8Client({
|
|
1122
|
+
transport,
|
|
1123
|
+
...(options.datapacks ? { datapacks: options.datapacks } : {}),
|
|
1124
|
+
...(typeof options.assetFetch === "function" ? { assetFetch: options.assetFetch } : {}),
|
|
1125
|
+
...(typeof options.assetBaseUrl === "string" && options.assetBaseUrl.trim() !== ""
|
|
1126
|
+
? { assetBaseUrl: options.assetBaseUrl.trim() }
|
|
1127
|
+
: {}),
|
|
1128
|
+
});
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
export function createEmbeddedCrafter8Environment(host, options = {}) {
|
|
1132
|
+
assertObject(host, "embedded host");
|
|
1133
|
+
if (options === null || typeof options !== "object") {
|
|
1134
|
+
throw new TypeError("embedded environment options must be an object.");
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
const client = createEmbeddedCrafter8Client(host, options);
|
|
1138
|
+
const hostServices = createCrafter8HostServices(host);
|
|
1139
|
+
const environment = {
|
|
1140
|
+
client,
|
|
1141
|
+
host: hostServices,
|
|
1142
|
+
};
|
|
1143
|
+
|
|
1144
|
+
if (options.includeLegacyRuntime === true) {
|
|
1145
|
+
environment.runtime = createHostRuntimeApiV1(host);
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
return Object.freeze(environment);
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
function assertObject(value, name) {
|
|
1152
|
+
if (value === null || typeof value !== "object") {
|
|
1153
|
+
throw new TypeError(`${name} must be an object.`);
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
function assertHostMethod(host, name) {
|
|
1158
|
+
if (typeof host?.[name] !== "function") {
|
|
1159
|
+
throw new TypeError(`host.${name} must be a function.`);
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
function safeSession(host) {
|
|
1164
|
+
if (typeof host?.session !== "function") {
|
|
1165
|
+
return {};
|
|
1166
|
+
}
|
|
1167
|
+
const session = host.session();
|
|
1168
|
+
return session && typeof session === "object" ? session : {};
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
function parseHostApiLabel(value) {
|
|
1172
|
+
if (typeof value === "number" && Number.isInteger(value) && value > 0) {
|
|
1173
|
+
return { hostApi: value, hostApiLabel: `${value}.0.0` };
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
if (typeof value === "string" && value.trim() !== "") {
|
|
1177
|
+
const trimmed = value.trim();
|
|
1178
|
+
const match = trimmed.match(/^(\d+)/);
|
|
1179
|
+
const hostApi = match ? Number(match[1]) : HOST_API_V1;
|
|
1180
|
+
return { hostApi, hostApiLabel: trimmed };
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
return { hostApi: HOST_API_V1, hostApiLabel: `${HOST_API_V1}.0.0` };
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
function normalizeHostRuntimeVersion(host) {
|
|
1187
|
+
if (isHostRuntimeApiV1(host)) {
|
|
1188
|
+
return host.version;
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
const session = safeSession(host);
|
|
1192
|
+
const versionSource = host?.version ?? session.hostApiVersion ?? HOST_API_V1;
|
|
1193
|
+
return parseHostApiLabel(versionSource);
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
function normalizeCapabilityList(host) {
|
|
1197
|
+
const session = safeSession(host);
|
|
1198
|
+
return Array.isArray(session.capabilities)
|
|
1199
|
+
? copyStringArray(session.capabilities.filter((entry) => typeof entry === "string"))
|
|
1200
|
+
: [];
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
function normalizeOperationKinds(selector = {}) {
|
|
1204
|
+
const normalizedKind = typeof selector.kind === "string" && selector.kind.trim() !== "" ? selector.kind.trim() : "";
|
|
1205
|
+
if (normalizedKind) {
|
|
1206
|
+
return [normalizedKind];
|
|
1207
|
+
}
|
|
1208
|
+
return ["module", "app", "datapack", "engine", "user-app", "datapack-workspace"];
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
function buildLegacyOperationSelector(kind, selector = {}) {
|
|
1212
|
+
const result = { kind };
|
|
1213
|
+
for (const key of ["slug", "id", "packageName", "user"]) {
|
|
1214
|
+
const value = selector[key];
|
|
1215
|
+
if (typeof value === "string" && value.trim() !== "") {
|
|
1216
|
+
result[key] = value.trim();
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
return result;
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
function normalizeOperationDescriptor(base, operation) {
|
|
1223
|
+
return Object.freeze({
|
|
1224
|
+
kind: String(base.kind || ""),
|
|
1225
|
+
scope: String(base.scope || ""),
|
|
1226
|
+
selectors: base.selectors ?? {},
|
|
1227
|
+
resolvedUser: typeof base.resolvedUser === "string" ? base.resolvedUser : null,
|
|
1228
|
+
artifactRef: typeof base.artifactRef === "string" ? base.artifactRef : null,
|
|
1229
|
+
artifact: base.artifact ?? null,
|
|
1230
|
+
operation,
|
|
1231
|
+
});
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
async function resolveOperationCollection(host, selector = {}) {
|
|
1235
|
+
if (typeof host?.getArtifactOperations !== "function") {
|
|
1236
|
+
return null;
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
const kinds = normalizeOperationKinds(selector);
|
|
1240
|
+
for (const kind of kinds) {
|
|
1241
|
+
try {
|
|
1242
|
+
const resolved = await host.getArtifactOperations(buildLegacyOperationSelector(kind, selector));
|
|
1243
|
+
if (resolved && Array.isArray(resolved.operations) && resolved.operations.length > 0) {
|
|
1244
|
+
return resolved;
|
|
1245
|
+
}
|
|
1246
|
+
} catch {
|
|
1247
|
+
// Try next candidate kind.
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
return null;
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
function appendQueryToPath(path, query) {
|
|
1254
|
+
if (!query || typeof query !== "object") {
|
|
1255
|
+
return path;
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
const search = new URLSearchParams();
|
|
1259
|
+
for (const [key, rawValue] of Object.entries(query)) {
|
|
1260
|
+
if (rawValue === undefined || rawValue === null) continue;
|
|
1261
|
+
if (Array.isArray(rawValue)) {
|
|
1262
|
+
for (const entry of rawValue) {
|
|
1263
|
+
if (entry === undefined || entry === null) continue;
|
|
1264
|
+
search.append(key, String(entry));
|
|
1265
|
+
}
|
|
1266
|
+
continue;
|
|
1267
|
+
}
|
|
1268
|
+
search.set(key, String(rawValue));
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
const suffix = search.toString();
|
|
1272
|
+
if (!suffix) {
|
|
1273
|
+
return path;
|
|
1274
|
+
}
|
|
1275
|
+
return `${path}${path.includes("?") ? "&" : "?"}${suffix}`;
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
function resolveOperationPath(operation, selector = {}, input) {
|
|
1279
|
+
const directPath = typeof operation?.path === "string" && operation.path.trim() !== "" ? operation.path.trim() : "";
|
|
1280
|
+
if (directPath) {
|
|
1281
|
+
return directPath;
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
const template = typeof operation?.pathTemplate === "string" && operation.pathTemplate.trim() !== "" ? operation.pathTemplate.trim() : "";
|
|
1285
|
+
if (!template) {
|
|
1286
|
+
throw new Error(`Operation "${String(operation?.id || "(unknown)")}" is missing a path or pathTemplate.`);
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
const pathParams =
|
|
1290
|
+
(selector && typeof selector.pathParams === "object" && selector.pathParams) ||
|
|
1291
|
+
(input && typeof input === "object" && input.pathParams && typeof input.pathParams === "object" ? input.pathParams : null) ||
|
|
1292
|
+
(input && typeof input === "object" && input.params && typeof input.params === "object" ? input.params : null) ||
|
|
1293
|
+
{};
|
|
1294
|
+
|
|
1295
|
+
return template
|
|
1296
|
+
.replace(/\{([A-Za-z0-9_]+)\}/g, (_match, name) => {
|
|
1297
|
+
const value = pathParams[name];
|
|
1298
|
+
if (value === undefined || value === null || value === "") {
|
|
1299
|
+
throw new Error(`Operation path parameter "${name}" is required.`);
|
|
1300
|
+
}
|
|
1301
|
+
return encodeURIComponent(String(value));
|
|
1302
|
+
})
|
|
1303
|
+
.replace(/:([A-Za-z0-9_]+)/g, (_match, name) => {
|
|
1304
|
+
const value = pathParams[name];
|
|
1305
|
+
if (value === undefined || value === null || value === "") {
|
|
1306
|
+
throw new Error(`Operation path parameter "${name}" is required.`);
|
|
1307
|
+
}
|
|
1308
|
+
return encodeURIComponent(String(value));
|
|
1309
|
+
});
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
function buildOperationRequest(operation, selector = {}, input) {
|
|
1313
|
+
const method = String(operation?.method || "GET").toUpperCase();
|
|
1314
|
+
const query =
|
|
1315
|
+
(selector && typeof selector.query === "object" && selector.query) ||
|
|
1316
|
+
(input && typeof input === "object" && input.query && typeof input.query === "object" ? input.query : null) ||
|
|
1317
|
+
null;
|
|
1318
|
+
|
|
1319
|
+
let body;
|
|
1320
|
+
if (selector && Object.prototype.hasOwnProperty.call(selector, "body")) {
|
|
1321
|
+
body = selector.body;
|
|
1322
|
+
} else if (input && typeof input === "object" && Object.prototype.hasOwnProperty.call(input, "body")) {
|
|
1323
|
+
body = input.body;
|
|
1324
|
+
} else if (method !== "GET" && method !== "HEAD" && input !== undefined) {
|
|
1325
|
+
body = input;
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
const path = appendQueryToPath(resolveOperationPath(operation, selector, input), query);
|
|
1329
|
+
const headers = body !== undefined && method !== "GET" && method !== "HEAD" ? { "Content-Type": "application/json" } : undefined;
|
|
1330
|
+
return buildTransportRequest(method, path, undefined, body, headers);
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
export function isHostRuntimeApiV1(value) {
|
|
1334
|
+
return Boolean(
|
|
1335
|
+
value &&
|
|
1336
|
+
typeof value === "object" &&
|
|
1337
|
+
value.version &&
|
|
1338
|
+
typeof value.version === "object" &&
|
|
1339
|
+
Number.isInteger(value.version.hostApi) &&
|
|
1340
|
+
typeof value.version.hostApiLabel === "string" &&
|
|
1341
|
+
value.session &&
|
|
1342
|
+
typeof value.session === "object" &&
|
|
1343
|
+
typeof value.session.get === "function" &&
|
|
1344
|
+
value.capabilities &&
|
|
1345
|
+
typeof value.capabilities === "object" &&
|
|
1346
|
+
typeof value.capabilities.list === "function" &&
|
|
1347
|
+
typeof value.capabilities.has === "function" &&
|
|
1348
|
+
typeof value.capabilities.assert === "function" &&
|
|
1349
|
+
value.navigation &&
|
|
1350
|
+
typeof value.navigation === "object" &&
|
|
1351
|
+
typeof value.navigation.navigate === "function" &&
|
|
1352
|
+
typeof value.navigation.openItemInGraph === "function" &&
|
|
1353
|
+
value.datapacks &&
|
|
1354
|
+
typeof value.datapacks === "object" &&
|
|
1355
|
+
typeof value.datapacks.list === "function" &&
|
|
1356
|
+
typeof value.datapacks.getManifest === "function" &&
|
|
1357
|
+
typeof value.datapacks.listContents === "function" &&
|
|
1358
|
+
typeof value.datapacks.readContent === "function" &&
|
|
1359
|
+
value.modules &&
|
|
1360
|
+
typeof value.modules === "object" &&
|
|
1361
|
+
typeof value.modules.list === "function" &&
|
|
1362
|
+
typeof value.modules.load === "function" &&
|
|
1363
|
+
value.operations &&
|
|
1364
|
+
typeof value.operations === "object" &&
|
|
1365
|
+
typeof value.operations.list === "function" &&
|
|
1366
|
+
typeof value.operations.get === "function" &&
|
|
1367
|
+
typeof value.operations.invoke === "function"
|
|
1368
|
+
);
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
export function createHostRuntimeApiV1(host) {
|
|
1372
|
+
assertObject(host, "host");
|
|
1373
|
+
|
|
1374
|
+
if (isHostRuntimeApiV1(host)) {
|
|
1375
|
+
return host;
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
assertHostMethod(host, "navigateToApp");
|
|
1379
|
+
assertHostMethod(host, "openItemInGraph");
|
|
1380
|
+
assertHostMethod(host, "listDatapacks");
|
|
1381
|
+
assertHostMethod(host, "getDatapackManifest");
|
|
1382
|
+
assertHostMethod(host, "listDatapackContents");
|
|
1383
|
+
assertHostMethod(host, "readDatapackContent");
|
|
1384
|
+
assertHostMethod(host, "listModules");
|
|
1385
|
+
assertHostMethod(host, "loadModule");
|
|
1386
|
+
assertHostMethod(host, "session");
|
|
1387
|
+
assertHostMethod(host, "hasCapability");
|
|
1388
|
+
assertHostMethod(host, "assertCapability");
|
|
1389
|
+
|
|
1390
|
+
const version = Object.freeze(normalizeHostRuntimeVersion(host));
|
|
1391
|
+
|
|
1392
|
+
const runtime = {
|
|
1393
|
+
version,
|
|
1394
|
+
session: Object.freeze({
|
|
1395
|
+
get() {
|
|
1396
|
+
const session = safeSession(host);
|
|
1397
|
+
return Object.freeze({
|
|
1398
|
+
apiBaseUrl: typeof session.apiBaseUrl === "string" ? session.apiBaseUrl : "",
|
|
1399
|
+
activeWorkspaceId: typeof session.activeWorkspaceId === "string" ? session.activeWorkspaceId : "",
|
|
1400
|
+
activeWorkspaceName: typeof session.activeWorkspaceName === "string" ? session.activeWorkspaceName : null,
|
|
1401
|
+
scenarioIds: typeof session.scenarioIds === "string" ? session.scenarioIds : "",
|
|
1402
|
+
...(typeof session.userId === "string" && session.userId ? { userId: session.userId } : {}),
|
|
1403
|
+
...(typeof session.userDisplayName === "string" || session.userDisplayName === null
|
|
1404
|
+
? { userDisplayName: session.userDisplayName ?? null }
|
|
1405
|
+
: {}),
|
|
1406
|
+
capabilities: normalizeCapabilityList(host),
|
|
1407
|
+
hostApi: version.hostApi,
|
|
1408
|
+
hostApiLabel: version.hostApiLabel,
|
|
1409
|
+
});
|
|
1410
|
+
},
|
|
1411
|
+
}),
|
|
1412
|
+
capabilities: Object.freeze({
|
|
1413
|
+
list() {
|
|
1414
|
+
return normalizeCapabilityList(host);
|
|
1415
|
+
},
|
|
1416
|
+
has(capability) {
|
|
1417
|
+
assertString(capability, "capability");
|
|
1418
|
+
return Boolean(host.hasCapability(capability.trim()));
|
|
1419
|
+
},
|
|
1420
|
+
assert(capability) {
|
|
1421
|
+
assertString(capability, "capability");
|
|
1422
|
+
return host.assertCapability(capability.trim());
|
|
1423
|
+
},
|
|
1424
|
+
}),
|
|
1425
|
+
navigation: Object.freeze({
|
|
1426
|
+
navigate(path) {
|
|
1427
|
+
assertString(path, "navigation path");
|
|
1428
|
+
return host.navigateToApp(path.trim());
|
|
1429
|
+
},
|
|
1430
|
+
openItemInGraph(itemId) {
|
|
1431
|
+
assertString(itemId, "itemId");
|
|
1432
|
+
return host.openItemInGraph(itemId.trim());
|
|
1433
|
+
},
|
|
1434
|
+
}),
|
|
1435
|
+
datapacks: Object.freeze({
|
|
1436
|
+
list() {
|
|
1437
|
+
return host.listDatapacks();
|
|
1438
|
+
},
|
|
1439
|
+
getManifest(slug) {
|
|
1440
|
+
assertString(slug, "datapack slug");
|
|
1441
|
+
return host.getDatapackManifest(slug.trim());
|
|
1442
|
+
},
|
|
1443
|
+
listContents(slug) {
|
|
1444
|
+
assertString(slug, "datapack slug");
|
|
1445
|
+
return host.listDatapackContents(slug.trim());
|
|
1446
|
+
},
|
|
1447
|
+
readContent(slug, key) {
|
|
1448
|
+
assertString(slug, "datapack slug");
|
|
1449
|
+
assertString(key, "datapack content key");
|
|
1450
|
+
return host.readDatapackContent(slug.trim(), key.trim());
|
|
1451
|
+
},
|
|
1452
|
+
}),
|
|
1453
|
+
modules: Object.freeze({
|
|
1454
|
+
list() {
|
|
1455
|
+
return host.listModules();
|
|
1456
|
+
},
|
|
1457
|
+
load(packageName) {
|
|
1458
|
+
assertString(packageName, "module packageName");
|
|
1459
|
+
return host.loadModule(packageName.trim());
|
|
1460
|
+
},
|
|
1461
|
+
}),
|
|
1462
|
+
operations: Object.freeze({
|
|
1463
|
+
async list(selector = {}) {
|
|
1464
|
+
if (selector && (selector.packageName || selector.slug || selector.id || selector.user || selector.operationId)) {
|
|
1465
|
+
const resolved = await resolveOperationCollection(host, selector);
|
|
1466
|
+
if (!resolved) {
|
|
1467
|
+
return [];
|
|
1468
|
+
}
|
|
1469
|
+
const operationId =
|
|
1470
|
+
typeof selector.operationId === "string" && selector.operationId.trim() !== "" ? selector.operationId.trim() : "";
|
|
1471
|
+
return resolved.operations
|
|
1472
|
+
.filter((operation) => (!operationId ? true : String(operation?.id || "") === operationId))
|
|
1473
|
+
.map((operation) => normalizeOperationDescriptor(resolved, operation));
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1476
|
+
if (typeof host.listArtifactOperationCatalog !== "function") {
|
|
1477
|
+
return [];
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
const catalog = await host.listArtifactOperationCatalog(
|
|
1481
|
+
typeof selector.kind === "string" && selector.kind.trim() !== "" ? selector.kind.trim() : undefined,
|
|
1482
|
+
);
|
|
1483
|
+
return catalog.flatMap((entry) =>
|
|
1484
|
+
Array.isArray(entry?.operations)
|
|
1485
|
+
? entry.operations.map((operation) =>
|
|
1486
|
+
normalizeOperationDescriptor(
|
|
1487
|
+
{
|
|
1488
|
+
kind: entry.kind,
|
|
1489
|
+
scope: entry.scope,
|
|
1490
|
+
selectors: entry.selectors,
|
|
1491
|
+
resolvedUser: null,
|
|
1492
|
+
artifactRef: null,
|
|
1493
|
+
artifact: null,
|
|
1494
|
+
},
|
|
1495
|
+
operation,
|
|
1496
|
+
),
|
|
1497
|
+
)
|
|
1498
|
+
: [],
|
|
1499
|
+
);
|
|
1500
|
+
},
|
|
1501
|
+
async get(selector) {
|
|
1502
|
+
assertObject(selector, "operation selector");
|
|
1503
|
+
const resolved = await resolveOperationCollection(host, selector);
|
|
1504
|
+
if (!resolved) {
|
|
1505
|
+
return null;
|
|
1506
|
+
}
|
|
1507
|
+
const operationId =
|
|
1508
|
+
typeof selector.operationId === "string" && selector.operationId.trim() !== "" ? selector.operationId.trim() : "";
|
|
1509
|
+
const operation = operationId
|
|
1510
|
+
? resolved.operations.find((entry) => String(entry?.id || "") === operationId)
|
|
1511
|
+
: resolved.operations[0];
|
|
1512
|
+
if (!operation) {
|
|
1513
|
+
return null;
|
|
1514
|
+
}
|
|
1515
|
+
return normalizeOperationDescriptor(resolved, operation);
|
|
1516
|
+
},
|
|
1517
|
+
async invoke(selector, input) {
|
|
1518
|
+
assertObject(selector, "operation selector");
|
|
1519
|
+
if (typeof host.invokeOperation === "function") {
|
|
1520
|
+
return host.invokeOperation(selector, input);
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
const resolved = await this.get(selector);
|
|
1524
|
+
if (!resolved) {
|
|
1525
|
+
throw new Error("Operation not found.");
|
|
1526
|
+
}
|
|
1527
|
+
|
|
1528
|
+
if (typeof host.fetchJson !== "function") {
|
|
1529
|
+
throw new Error("Current host does not implement operation invocation.");
|
|
1530
|
+
}
|
|
1531
|
+
|
|
1532
|
+
const request = buildOperationRequest(resolved.operation, selector, input);
|
|
1533
|
+
const init = { method: request.method };
|
|
1534
|
+
if (request.headers) {
|
|
1535
|
+
init.headers = request.headers;
|
|
1536
|
+
}
|
|
1537
|
+
if (request.body !== undefined && request.method !== "GET" && request.method !== "HEAD") {
|
|
1538
|
+
init.body = JSON.stringify(request.body);
|
|
1539
|
+
}
|
|
1540
|
+
return host.fetchJson(request.path, init);
|
|
1541
|
+
},
|
|
1542
|
+
}),
|
|
1543
|
+
};
|
|
1544
|
+
|
|
1545
|
+
return Object.freeze(runtime);
|
|
1546
|
+
}
|
|
1547
|
+
|
|
1548
|
+
function normalizeRuntimeArtifact(kind, artifact) {
|
|
1549
|
+
if (!isPlainObject(artifact)) {
|
|
1550
|
+
throw new TypeError(`artifact must be an object for ${kind} context.`);
|
|
1551
|
+
}
|
|
1552
|
+
|
|
1553
|
+
assertString(artifact.id, `${kind} artifact.id`);
|
|
1554
|
+
assertString(artifact.version, `${kind} artifact.version`);
|
|
1555
|
+
assertOptionalString(artifact.name, `${kind} artifact.name`);
|
|
1556
|
+
assertOptionalString(artifact.summary, `${kind} artifact.summary`);
|
|
1557
|
+
|
|
1558
|
+
const normalized = {
|
|
1559
|
+
kind,
|
|
1560
|
+
id: artifact.id.trim(),
|
|
1561
|
+
version: artifact.version.trim(),
|
|
1562
|
+
...(artifact.name ? { name: artifact.name.trim() } : {}),
|
|
1563
|
+
...(artifact.summary ? { summary: artifact.summary.trim() } : {}),
|
|
1564
|
+
};
|
|
1565
|
+
|
|
1566
|
+
if (Array.isArray(artifact.capabilities) && artifact.capabilities.length > 0) {
|
|
1567
|
+
normalized.capabilities = copyStringArray(artifact.capabilities.filter((entry) => typeof entry === "string"));
|
|
1568
|
+
}
|
|
1569
|
+
|
|
1570
|
+
if (isPlainObject(artifact.dependencies)) {
|
|
1571
|
+
const dependencies = {};
|
|
1572
|
+
if (Array.isArray(artifact.dependencies.modules) && artifact.dependencies.modules.length > 0) {
|
|
1573
|
+
dependencies.modules = copyStringArray(artifact.dependencies.modules.filter((entry) => typeof entry === "string"));
|
|
1574
|
+
}
|
|
1575
|
+
if (Array.isArray(artifact.dependencies.datapacks) && artifact.dependencies.datapacks.length > 0) {
|
|
1576
|
+
dependencies.datapacks = copyStringArray(
|
|
1577
|
+
artifact.dependencies.datapacks.filter((entry) => typeof entry === "string"),
|
|
1578
|
+
);
|
|
1579
|
+
}
|
|
1580
|
+
addOptionalObject(normalized, "dependencies", dependencies);
|
|
1581
|
+
}
|
|
1582
|
+
|
|
1583
|
+
if (isPlainObject(artifact.uses)) {
|
|
1584
|
+
const uses = {};
|
|
1585
|
+
if (Array.isArray(artifact.uses.modules) && artifact.uses.modules.length > 0) {
|
|
1586
|
+
uses.modules = copyStringArray(artifact.uses.modules.filter((entry) => typeof entry === "string"));
|
|
1587
|
+
}
|
|
1588
|
+
if (Array.isArray(artifact.uses.datapacks) && artifact.uses.datapacks.length > 0) {
|
|
1589
|
+
uses.datapacks = copyStringArray(artifact.uses.datapacks.filter((entry) => typeof entry === "string"));
|
|
1590
|
+
}
|
|
1591
|
+
addOptionalObject(normalized, "uses", uses);
|
|
1592
|
+
}
|
|
1593
|
+
|
|
1594
|
+
if (isPlainObject(artifact.provides)) {
|
|
1595
|
+
normalized.provides = { ...artifact.provides };
|
|
1596
|
+
}
|
|
1597
|
+
|
|
1598
|
+
return Object.freeze(normalized);
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1601
|
+
export function createAppContext(args) {
|
|
1602
|
+
assertObject(args, "app context args");
|
|
1603
|
+
const hasHTMLElement = typeof HTMLElement !== "undefined";
|
|
1604
|
+
const isValidContainer = hasHTMLElement
|
|
1605
|
+
? args.container instanceof HTMLElement
|
|
1606
|
+
: args.container !== null && typeof args.container === "object";
|
|
1607
|
+
if (!isValidContainer) {
|
|
1608
|
+
throw new TypeError("app context container must be a DOM element-like object.");
|
|
1609
|
+
}
|
|
1610
|
+
|
|
1611
|
+
const client = (() => {
|
|
1612
|
+
if (isCrafter8Client(args.client)) {
|
|
1613
|
+
return args.client;
|
|
1614
|
+
}
|
|
1615
|
+
if (args.host) {
|
|
1616
|
+
return createEmbeddedCrafter8Client(args.host);
|
|
1617
|
+
}
|
|
1618
|
+
throw new TypeError("app context requires client or host.");
|
|
1619
|
+
})();
|
|
1620
|
+
|
|
1621
|
+
const hostServices = (() => {
|
|
1622
|
+
if (args.hostServices) {
|
|
1623
|
+
return createCrafter8HostServices(args.hostServices);
|
|
1624
|
+
}
|
|
1625
|
+
if (args.host) {
|
|
1626
|
+
return createCrafter8HostServices(args.host);
|
|
1627
|
+
}
|
|
1628
|
+
return undefined;
|
|
1629
|
+
})();
|
|
1630
|
+
|
|
1631
|
+
const runtime = (() => {
|
|
1632
|
+
if (args.runtime) {
|
|
1633
|
+
return createHostRuntimeApiV1(args.runtime);
|
|
1634
|
+
}
|
|
1635
|
+
if (args.host) {
|
|
1636
|
+
return createHostRuntimeApiV1(args.host);
|
|
1637
|
+
}
|
|
1638
|
+
return undefined;
|
|
1639
|
+
})();
|
|
1640
|
+
|
|
1641
|
+
return Object.freeze({
|
|
1642
|
+
client,
|
|
1643
|
+
...(hostServices ? { host: hostServices } : {}),
|
|
1644
|
+
...(runtime ? { runtime } : {}),
|
|
1645
|
+
container: args.container,
|
|
1646
|
+
artifact: normalizeRuntimeArtifact("app", args.artifact),
|
|
1647
|
+
});
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
export function createModuleContext(args) {
|
|
1651
|
+
assertObject(args, "module context args");
|
|
1652
|
+
|
|
1653
|
+
const client = (() => {
|
|
1654
|
+
if (isCrafter8Client(args.client)) {
|
|
1655
|
+
return args.client;
|
|
1656
|
+
}
|
|
1657
|
+
if (args.host) {
|
|
1658
|
+
return createEmbeddedCrafter8Client(args.host);
|
|
1659
|
+
}
|
|
1660
|
+
throw new TypeError("module context requires client or host.");
|
|
1661
|
+
})();
|
|
1662
|
+
|
|
1663
|
+
const runtime = (() => {
|
|
1664
|
+
if (args.runtime) {
|
|
1665
|
+
return createHostRuntimeApiV1(args.runtime);
|
|
1666
|
+
}
|
|
1667
|
+
if (args.host) {
|
|
1668
|
+
return createHostRuntimeApiV1(args.host);
|
|
1669
|
+
}
|
|
1670
|
+
return undefined;
|
|
1671
|
+
})();
|
|
1672
|
+
|
|
1673
|
+
return Object.freeze({
|
|
1674
|
+
client,
|
|
1675
|
+
...(runtime ? { runtime } : {}),
|
|
1676
|
+
artifact: normalizeRuntimeArtifact("module", args.artifact),
|
|
1677
|
+
});
|
|
1678
|
+
}
|
|
1679
|
+
|
|
1680
|
+
function addOptionalString(target, key, value) {
|
|
1681
|
+
if (typeof value === "string" && value.trim() !== "") {
|
|
1682
|
+
target[key] = value.trim();
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
|
|
1686
|
+
function addOptionalArray(target, key, value) {
|
|
1687
|
+
if (Array.isArray(value) && value.length > 0) {
|
|
1688
|
+
target[key] = value;
|
|
1689
|
+
}
|
|
1690
|
+
}
|
|
1691
|
+
|
|
1692
|
+
function addOptionalObject(target, key, value) {
|
|
1693
|
+
if (isPlainObject(value) && Object.keys(value).length > 0) {
|
|
1694
|
+
target[key] = value;
|
|
1695
|
+
}
|
|
1696
|
+
}
|
|
1697
|
+
|
|
1698
|
+
function normalizePlatform(options) {
|
|
1699
|
+
const specVersion = options.specVersion ?? ARTIFACT_SPEC_V2;
|
|
1700
|
+
const hostApi = options.hostApi ?? HOST_API_V1;
|
|
1701
|
+
|
|
1702
|
+
if (!Number.isInteger(specVersion) || specVersion <= 0) {
|
|
1703
|
+
throw new TypeError("platform.specVersion must be a positive integer.");
|
|
1704
|
+
}
|
|
1705
|
+
|
|
1706
|
+
if (!Number.isInteger(hostApi) || hostApi <= 0) {
|
|
1707
|
+
throw new TypeError("platform.hostApi must be a positive integer.");
|
|
1708
|
+
}
|
|
1709
|
+
|
|
1710
|
+
return { specVersion, hostApi };
|
|
1711
|
+
}
|
|
1712
|
+
|
|
1713
|
+
function buildUsesBlock(definition) {
|
|
1714
|
+
const uses = {};
|
|
1715
|
+
addOptionalArray(uses, "modules", definition.uses?.modules);
|
|
1716
|
+
addOptionalArray(uses, "datapacks", definition.uses?.datapacks);
|
|
1717
|
+
return uses;
|
|
1718
|
+
}
|
|
1719
|
+
|
|
1720
|
+
function normalizeGeneratedDependencies(input) {
|
|
1721
|
+
if (input === undefined) {
|
|
1722
|
+
return { modules: [], datapacks: [] };
|
|
1723
|
+
}
|
|
1724
|
+
if (!isPlainObject(input)) {
|
|
1725
|
+
throw new TypeError("artifact dependencies must be an object.");
|
|
1726
|
+
}
|
|
1727
|
+
const normalized = {
|
|
1728
|
+
modules: [],
|
|
1729
|
+
datapacks: [],
|
|
1730
|
+
};
|
|
1731
|
+
if (input.modules !== undefined) {
|
|
1732
|
+
if (!Array.isArray(input.modules)) {
|
|
1733
|
+
throw new TypeError("artifact dependencies.modules must be an array.");
|
|
1734
|
+
}
|
|
1735
|
+
normalized.modules = copyStringArray(input.modules.map((entry) => readArtifactRef(entry, "module").id));
|
|
1736
|
+
}
|
|
1737
|
+
if (input.datapacks !== undefined) {
|
|
1738
|
+
if (!Array.isArray(input.datapacks)) {
|
|
1739
|
+
throw new TypeError("artifact dependencies.datapacks must be an array.");
|
|
1740
|
+
}
|
|
1741
|
+
normalized.datapacks = copyStringArray(input.datapacks.map((entry) => readArtifactRef(entry, "datapack").id));
|
|
1742
|
+
}
|
|
1743
|
+
return normalized;
|
|
1744
|
+
}
|
|
1745
|
+
|
|
1746
|
+
function buildPrimaryDependenciesBlock(definition, options) {
|
|
1747
|
+
const generated = normalizeGeneratedDependencies(options.dependencies);
|
|
1748
|
+
const modules = copyStringArray([...(generated.modules || []), ...(definition.uses?.modules || [])]);
|
|
1749
|
+
const datapacks = copyStringArray([...(generated.datapacks || []), ...(definition.uses?.datapacks || [])]);
|
|
1750
|
+
const block = {};
|
|
1751
|
+
addOptionalArray(block, "modules", modules);
|
|
1752
|
+
addOptionalArray(block, "datapacks", datapacks);
|
|
1753
|
+
return block;
|
|
1754
|
+
}
|
|
1755
|
+
|
|
1756
|
+
function buildProvidesBlock(definition) {
|
|
1757
|
+
if (definition.kind === "module") {
|
|
1758
|
+
const provides = {};
|
|
1759
|
+
const exportNames = Object.keys(definition.provides.exports);
|
|
1760
|
+
addOptionalArray(provides, "exports", exportNames);
|
|
1761
|
+
addOptionalArray(provides, "operations", definition.provides.operations);
|
|
1762
|
+
return provides;
|
|
1763
|
+
}
|
|
1764
|
+
|
|
1765
|
+
if (definition.kind === "datapack") {
|
|
1766
|
+
const provides = {};
|
|
1767
|
+
addOptionalString(provides, "profile", definition.provides.profile);
|
|
1768
|
+
return provides;
|
|
1769
|
+
}
|
|
1770
|
+
|
|
1771
|
+
return {};
|
|
1772
|
+
}
|
|
1773
|
+
|
|
1774
|
+
function validateForSpecVersion(definition, specVersion) {
|
|
1775
|
+
if (specVersion === ARTIFACT_SPEC_V1) {
|
|
1776
|
+
return;
|
|
1777
|
+
}
|
|
1778
|
+
|
|
1779
|
+
if (specVersion !== ARTIFACT_SPEC_V2) {
|
|
1780
|
+
throw new TypeError(`Unsupported artifact spec version ${specVersion}.`);
|
|
1781
|
+
}
|
|
1782
|
+
|
|
1783
|
+
if (definition.kind === "app" && definition.capabilities.length === 0) {
|
|
1784
|
+
throw new TypeError("V2 app manifests require at least one capability.");
|
|
1785
|
+
}
|
|
1786
|
+
|
|
1787
|
+
if (definition.kind === "module" && Object.keys(definition.provides.exports).length === 0) {
|
|
1788
|
+
throw new TypeError("V2 module manifests require at least one provided export.");
|
|
1789
|
+
}
|
|
1790
|
+
|
|
1791
|
+
if (definition.kind === "datapack" && !definition.provides.profile) {
|
|
1792
|
+
throw new TypeError("V2 datapack manifests require provides.profile.");
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1795
|
+
|
|
1796
|
+
export function generateArtifactManifest(definition, options = {}) {
|
|
1797
|
+
if (!isArtifactDefinition(definition)) {
|
|
1798
|
+
throw new TypeError("generateArtifactManifest expects a Crafter8 definition created by the SDK.");
|
|
1799
|
+
}
|
|
1800
|
+
|
|
1801
|
+
const version = options.version;
|
|
1802
|
+
assertString(version, "artifact version");
|
|
1803
|
+
|
|
1804
|
+
const platform = normalizePlatform(options);
|
|
1805
|
+
validateForSpecVersion(definition, platform.specVersion);
|
|
1806
|
+
|
|
1807
|
+
const manifest = {
|
|
1808
|
+
kind: definition.kind,
|
|
1809
|
+
id: definition.id,
|
|
1810
|
+
version: version.trim(),
|
|
1811
|
+
platform,
|
|
1812
|
+
};
|
|
1813
|
+
|
|
1814
|
+
addOptionalString(manifest, "name", definition.name);
|
|
1815
|
+
addOptionalString(manifest, "summary", definition.summary);
|
|
1816
|
+
|
|
1817
|
+
if (platform.specVersion >= ARTIFACT_SPEC_V2) {
|
|
1818
|
+
addOptionalArray(manifest, "capabilities", definition.capabilities);
|
|
1819
|
+
addOptionalObject(manifest, "provides", buildProvidesBlock(definition));
|
|
1820
|
+
addOptionalObject(manifest, "dependencies", buildPrimaryDependenciesBlock(definition, options));
|
|
1821
|
+
if (options.legacyUses === true) {
|
|
1822
|
+
addOptionalObject(manifest, "uses", buildUsesBlock(definition));
|
|
1823
|
+
}
|
|
1824
|
+
}
|
|
1825
|
+
|
|
1826
|
+
if (definition.kind === "datapack") {
|
|
1827
|
+
addOptionalObject(manifest, "contents", definition.contents);
|
|
1828
|
+
}
|
|
1829
|
+
|
|
1830
|
+
return manifest;
|
|
1831
|
+
}
|