@heroiclands/package-build 0.4.0 → 0.6.0
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/CHANGELOG.md +115 -0
- package/README.md +238 -2
- package/bin/package-build.mjs +482 -49
- package/bin/report.mjs +13 -4
- package/config.mjs +380 -1
- package/container.mjs +699 -0
- package/coverage.mjs +626 -0
- package/e2e.mjs +876 -0
- package/index.mjs +11 -2
- package/lang.mjs +2 -2
- package/package.json +34 -17
- package/templates.mjs +224 -0
- package/types/config.d.mts +129 -0
- package/types/container.d.mts +359 -0
- package/types/coverage.d.mts +182 -0
- package/types/e2e.d.mts +351 -0
- package/types/index.d.mts +4 -1
- package/types/templates.d.mts +74 -0
- package/text.mjs +0 -74
- package/types/text.d.mts +0 -51
package/config.mjs
CHANGED
|
@@ -70,6 +70,8 @@ const SECTION_KEYS = [
|
|
|
70
70
|
"deploy",
|
|
71
71
|
"release",
|
|
72
72
|
"bundle",
|
|
73
|
+
"container",
|
|
74
|
+
"e2e",
|
|
73
75
|
];
|
|
74
76
|
|
|
75
77
|
/**
|
|
@@ -96,9 +98,26 @@ export const DERIVED_MANIFEST_KEYS = Object.freeze({
|
|
|
96
98
|
});
|
|
97
99
|
const ASSET_KEYS = ["from", "to"];
|
|
98
100
|
const CLEAN_KEYS = ["extra"];
|
|
99
|
-
const LANG_KEYS = [
|
|
101
|
+
const LANG_KEYS = [
|
|
102
|
+
"sources",
|
|
103
|
+
"help",
|
|
104
|
+
"primary",
|
|
105
|
+
"scripts",
|
|
106
|
+
"templates",
|
|
107
|
+
"keyRoots",
|
|
108
|
+
"references",
|
|
109
|
+
"retained",
|
|
110
|
+
"allow",
|
|
111
|
+
];
|
|
100
112
|
const DEPLOY_KEYS = ["envPrefix"];
|
|
101
113
|
const RELEASE_KEYS = ["artifact"];
|
|
114
|
+
const CONTAINER_KEYS = ["image", "stages"];
|
|
115
|
+
const CONTAINER_STAGE_KEYS = ["port", "world", "version"];
|
|
116
|
+
const E2E_KEYS = ["stage", "suite", "build", "world", "gm", "documents"];
|
|
117
|
+
const E2E_SUITE_KEYS = ["run", "open"];
|
|
118
|
+
const E2E_WORLD_KEYS = ["id", "title", "description"];
|
|
119
|
+
const E2E_GM_KEYS = ["name", "password"];
|
|
120
|
+
const E2E_BUILD_TARGET_KEYS = ["script", "recreate"];
|
|
102
121
|
const BUNDLE_KEYS = ["entry"];
|
|
103
122
|
|
|
104
123
|
/**
|
|
@@ -218,6 +237,194 @@ function normalizeManifest(value) {
|
|
|
218
237
|
return Object.freeze(structuredClone(input));
|
|
219
238
|
}
|
|
220
239
|
|
|
240
|
+
/**
|
|
241
|
+
* Normalize a setting that is one glob or several.
|
|
242
|
+
*
|
|
243
|
+
* A repository with a single conventional directory writes a string and a
|
|
244
|
+
* repository with two writes a list; requiring the list form from both would
|
|
245
|
+
* make the common case read like the exception.
|
|
246
|
+
*
|
|
247
|
+
* @param {unknown} value - What was declared, or `undefined`.
|
|
248
|
+
* @param {string[]} fallback - The conventional layout.
|
|
249
|
+
* @param {string} where - Dotted path, for the error.
|
|
250
|
+
* @returns {readonly string[]} The globs, frozen.
|
|
251
|
+
*/
|
|
252
|
+
function normalizeGlobs(value, fallback, where) {
|
|
253
|
+
if (value === undefined) return Object.freeze(fallback);
|
|
254
|
+
const list = Array.isArray(value) ? value : [value];
|
|
255
|
+
return Object.freeze(
|
|
256
|
+
list.map((glob, index) =>
|
|
257
|
+
requireNonEmptyString(
|
|
258
|
+
glob,
|
|
259
|
+
Array.isArray(value) ? `${where}[${index}]` : where,
|
|
260
|
+
),
|
|
261
|
+
),
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* A container stage a repository runs beyond the conventional four.
|
|
267
|
+
*
|
|
268
|
+
* The four every HeroicLands package deploys to — dev, qa, prod, test — need no
|
|
269
|
+
* entry: their data-root variable is derived and their ports are conventional.
|
|
270
|
+
* This is for a stage that is genuinely one repository's, such as an older
|
|
271
|
+
* Foundry serving a previous generation of the package.
|
|
272
|
+
*
|
|
273
|
+
* @param {unknown} value - The stage entry.
|
|
274
|
+
* @param {string} name - The stage name, for the error path.
|
|
275
|
+
* @returns {Readonly<{port: number|null, world: string|null, version: string|null}>}
|
|
276
|
+
*/
|
|
277
|
+
function normalizeContainerStage(value, name) {
|
|
278
|
+
const where = `packageBuild.container.stages.${name}`;
|
|
279
|
+
if (!isMapping(value)) fail(where, "must be a mapping");
|
|
280
|
+
const stage = /** @type {Record<string, unknown>} */ (value);
|
|
281
|
+
rejectUnknownKeys(stage, CONTAINER_STAGE_KEYS, `${where}.`);
|
|
282
|
+
if (stage.port !== undefined && typeof stage.port !== "number") {
|
|
283
|
+
fail(`${where}.port`, "must be a number");
|
|
284
|
+
}
|
|
285
|
+
if (stage.world !== undefined && typeof stage.world !== "string") {
|
|
286
|
+
// An empty string is meaningful — it declares "never auto-launch" —
|
|
287
|
+
// which is exactly why the type has to be checked rather than coerced.
|
|
288
|
+
fail(`${where}.world`, 'must be a string ("" forces no auto-launch)');
|
|
289
|
+
}
|
|
290
|
+
return Object.freeze({
|
|
291
|
+
port: /** @type {number|null} */ (stage.port ?? null),
|
|
292
|
+
world: /** @type {string|null} */ (stage.world ?? null),
|
|
293
|
+
version:
|
|
294
|
+
stage.version === undefined ?
|
|
295
|
+
null
|
|
296
|
+
: requireNonEmptyString(stage.version, `${where}.version`),
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* One end-to-end build target: the npm script that produces it, and whether
|
|
302
|
+
* producing it means the world has to relaunch.
|
|
303
|
+
*
|
|
304
|
+
* A bare string is the common case and reads better than a mapping with one
|
|
305
|
+
* key, so both are accepted. `recreate` is for a target that writes something
|
|
306
|
+
* Foundry reads **once, at world launch** — the manifest — where deploying it
|
|
307
|
+
* into a running world deploys a file nothing will look at.
|
|
308
|
+
*
|
|
309
|
+
* @param {unknown} value - The target entry.
|
|
310
|
+
* @param {string} name - The target name, for the error path.
|
|
311
|
+
* @returns {Readonly<{script: string, recreate: boolean}>}
|
|
312
|
+
*/
|
|
313
|
+
function normalizeE2EBuildTarget(value, name) {
|
|
314
|
+
const where = `packageBuild.e2e.build.${name}`;
|
|
315
|
+
if (typeof value === "string") {
|
|
316
|
+
return Object.freeze({
|
|
317
|
+
script: requireNonEmptyString(value, where),
|
|
318
|
+
recreate: false,
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
if (!isMapping(value)) fail(where, "must be a script name or a mapping");
|
|
322
|
+
const target = /** @type {Record<string, unknown>} */ (value);
|
|
323
|
+
rejectUnknownKeys(target, E2E_BUILD_TARGET_KEYS, `${where}.`);
|
|
324
|
+
if (target.recreate !== undefined && typeof target.recreate !== "boolean") {
|
|
325
|
+
fail(`${where}.recreate`, "must be a boolean");
|
|
326
|
+
}
|
|
327
|
+
return Object.freeze({
|
|
328
|
+
script: requireNonEmptyString(target.script, `${where}.script`),
|
|
329
|
+
recreate: Boolean(target.recreate),
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* The suite a repository runs against the served world.
|
|
335
|
+
*
|
|
336
|
+
* **This is the one thing the harness does not own.** Standing Foundry up,
|
|
337
|
+
* seeding a world and waiting for it to activate are nobody's local problem;
|
|
338
|
+
* what runs against it — a Cypress suite full of one system's helpers — is
|
|
339
|
+
* entirely the repository's. So it is named here, the way an asset transform or
|
|
340
|
+
* a manifest-flags module is named.
|
|
341
|
+
*
|
|
342
|
+
* @param {unknown} value - The `suite` block, or `undefined`.
|
|
343
|
+
* @returns {Readonly<{run: readonly string[], open: readonly string[]|null}>|null}
|
|
344
|
+
*/
|
|
345
|
+
function normalizeE2ESuite(value) {
|
|
346
|
+
if (value === undefined) return null;
|
|
347
|
+
if (!isMapping(value)) fail("packageBuild.e2e.suite", "must be a mapping");
|
|
348
|
+
const suite = /** @type {Record<string, unknown>} */ (value);
|
|
349
|
+
rejectUnknownKeys(suite, E2E_SUITE_KEYS, "packageBuild.e2e.suite.");
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* @param {unknown} value_ - The declared command.
|
|
353
|
+
* @param {string} where - Its path, for the error.
|
|
354
|
+
* @returns {readonly string[]} The program and its arguments.
|
|
355
|
+
*/
|
|
356
|
+
const commandList = (value_, where) => {
|
|
357
|
+
if (!Array.isArray(value_) || value_.length === 0) {
|
|
358
|
+
fail(where, "must be a non-empty list naming a program to run");
|
|
359
|
+
}
|
|
360
|
+
return Object.freeze(
|
|
361
|
+
value_.map((part, i) =>
|
|
362
|
+
requireNonEmptyString(part, `${where}[${i}]`),
|
|
363
|
+
),
|
|
364
|
+
);
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
return Object.freeze({
|
|
368
|
+
run: commandList(suite.run, "packageBuild.e2e.suite.run"),
|
|
369
|
+
open:
|
|
370
|
+
suite.open === undefined ?
|
|
371
|
+
null
|
|
372
|
+
: commandList(suite.open, "packageBuild.e2e.suite.open"),
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* A mapping whose every value is a non-empty string, frozen.
|
|
378
|
+
*
|
|
379
|
+
* @param {unknown} value - The mapping, or `undefined`.
|
|
380
|
+
* @param {string} where - Its path, for the error.
|
|
381
|
+
* @param {readonly string[]} [allowed] - Keys it may declare.
|
|
382
|
+
* @returns {Readonly<Record<string, string>>}
|
|
383
|
+
*/
|
|
384
|
+
function normalizeStringMap(value, where, allowed) {
|
|
385
|
+
if (value === undefined) return Object.freeze({});
|
|
386
|
+
if (!isMapping(value)) fail(where, "must be a mapping");
|
|
387
|
+
const input = /** @type {Record<string, unknown>} */ (value);
|
|
388
|
+
if (allowed) rejectUnknownKeys(input, allowed, `${where}.`);
|
|
389
|
+
return Object.freeze(
|
|
390
|
+
Object.fromEntries(
|
|
391
|
+
Object.entries(input).map(([key, entry]) => [
|
|
392
|
+
key,
|
|
393
|
+
requireNonEmptyString(entry, `${where}.${key}`),
|
|
394
|
+
]),
|
|
395
|
+
),
|
|
396
|
+
);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Normalize an escape-hatch list: entries of `{ <field>, reason }`.
|
|
401
|
+
*
|
|
402
|
+
* Both escape hatches — a key kept despite looking unreferenced, a literal kept
|
|
403
|
+
* despite looking like prose — are a claim about something no scan can see, so
|
|
404
|
+
* each entry states the claim in prose a reviewer can check. Requiring the
|
|
405
|
+
* reason is what keeps the list from silently becoming the place unexplained
|
|
406
|
+
* exceptions accumulate.
|
|
407
|
+
*
|
|
408
|
+
* @param {unknown} value - The declared list, or `undefined`.
|
|
409
|
+
* @param {string} field - The name of the entry's own field.
|
|
410
|
+
* @param {string} where - Dotted path, for the error.
|
|
411
|
+
* @returns {readonly string[]} The field values, frozen; `[]` when absent.
|
|
412
|
+
*/
|
|
413
|
+
function normalizeExceptions(value, field, where) {
|
|
414
|
+
if (value === undefined) return Object.freeze([]);
|
|
415
|
+
if (!Array.isArray(value)) fail(where, "must be a list");
|
|
416
|
+
return Object.freeze(
|
|
417
|
+
value.map((entry, index) => {
|
|
418
|
+
const at = `${where}[${index}]`;
|
|
419
|
+
if (!isMapping(entry)) fail(at, "must be a mapping");
|
|
420
|
+
const item = /** @type {Record<string, unknown>} */ (entry);
|
|
421
|
+
rejectUnknownKeys(item, [field, "reason"], `${at}.`);
|
|
422
|
+
requireNonEmptyString(item.reason, `${at}.reason`);
|
|
423
|
+
return requireNonEmptyString(item[field], `${at}.${field}`);
|
|
424
|
+
}),
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
|
|
221
428
|
/**
|
|
222
429
|
* The resolved `packageBuild` section, every optional half filled in.
|
|
223
430
|
*
|
|
@@ -241,9 +448,43 @@ function normalizeManifest(value) {
|
|
|
241
448
|
* conventional build artifacts.
|
|
242
449
|
* @property {string} langSources Glob for the localization files to check.
|
|
243
450
|
* @property {string|null} langHelp Extra guidance printed after a failure.
|
|
451
|
+
* @property {string} langPrimary The localization file coverage is measured
|
|
452
|
+
* against — the one the package authors.
|
|
453
|
+
* @property {readonly string[]} langScripts Globs for the sources scanned
|
|
454
|
+
* for key references.
|
|
455
|
+
* @property {readonly string[]} langTemplates Globs for the templates scanned
|
|
456
|
+
* for references and for hardcoded text.
|
|
457
|
+
* @property {readonly string[]|null} langKeyRoots The key roots, when the
|
|
458
|
+
* package references one its file does not
|
|
459
|
+
* yet declare. `null` derives them.
|
|
460
|
+
* @property {string|null} langReferences Module to load a `references`
|
|
461
|
+
* function from, contributing the keys only
|
|
462
|
+
* this repository's conventions can find.
|
|
463
|
+
* @property {readonly string[]} langRetained Key prefixes exempt from the
|
|
464
|
+
* unreferenced advisory.
|
|
465
|
+
* @property {readonly string[]} langAllow Template literals that are
|
|
466
|
+
* deliberately not localization keys.
|
|
244
467
|
* @property {string} envPrefix Prefix of the deploy environment variables.
|
|
245
468
|
* @property {string} bundleEntry The bundle file Foundry loads, as the
|
|
246
469
|
* manifest spells it. Derived from the package id.
|
|
470
|
+
* @property {string|null} compatibilityMinimum The Foundry floor the package
|
|
471
|
+
* claims, read from the shared configuration's top level.
|
|
472
|
+
* @property {string} systemId The system a world runs — the package
|
|
473
|
+
* itself for a system, its target for a module.
|
|
474
|
+
* @property {string|null} systemVersion That system's version, when the shared
|
|
475
|
+
* configuration stamps one.
|
|
476
|
+
* @property {string|null} containerImage Image override for every stage.
|
|
477
|
+
* @property {Readonly<Record<string, Readonly<{port: number|null, world: string|null, version: string|null}>>>} containerStages
|
|
478
|
+
* Container stages beyond the conventional four.
|
|
479
|
+
* @property {string} e2eStage Which stage the suite runs against.
|
|
480
|
+
* @property {Readonly<{run: readonly string[], open: readonly string[]|null}>|null} e2eSuite
|
|
481
|
+
* What to run against the served world; `null` when the repository has none.
|
|
482
|
+
* @property {Readonly<Record<string, Readonly<{script: string, recreate: boolean}>>>} e2eBuild
|
|
483
|
+
* Build targets the fast loop can produce, in declaration order.
|
|
484
|
+
* @property {Readonly<Record<string, string>>} e2eWorld Declared world identity.
|
|
485
|
+
* @property {Readonly<Record<string, string>>} e2eGm Declared GM credentials.
|
|
486
|
+
* @property {Readonly<Record<string, string>>} e2eDocuments Extra world
|
|
487
|
+
* collections, as collection → source directory.
|
|
247
488
|
*/
|
|
248
489
|
|
|
249
490
|
/**
|
|
@@ -321,6 +562,56 @@ export function resolvePackageBuildConfig(shared) {
|
|
|
321
562
|
);
|
|
322
563
|
const bundleInput = /** @type {Record<string, unknown>} */ (bundle);
|
|
323
564
|
|
|
565
|
+
const container = section.container ?? {};
|
|
566
|
+
if (!isMapping(container)) {
|
|
567
|
+
fail("packageBuild.container", "must be a mapping");
|
|
568
|
+
}
|
|
569
|
+
rejectUnknownKeys(
|
|
570
|
+
/** @type {Record<string, unknown>} */ (container),
|
|
571
|
+
CONTAINER_KEYS,
|
|
572
|
+
"packageBuild.container.",
|
|
573
|
+
);
|
|
574
|
+
const containerInput = /** @type {Record<string, unknown>} */ (container);
|
|
575
|
+
const declaredStages = containerInput.stages ?? {};
|
|
576
|
+
if (!isMapping(declaredStages)) {
|
|
577
|
+
fail("packageBuild.container.stages", "must be a mapping");
|
|
578
|
+
}
|
|
579
|
+
const containerStages = Object.freeze(
|
|
580
|
+
Object.fromEntries(
|
|
581
|
+
Object.entries(
|
|
582
|
+
/** @type {Record<string, unknown>} */ (declaredStages),
|
|
583
|
+
).map(([name, entry]) => [
|
|
584
|
+
name,
|
|
585
|
+
normalizeContainerStage(entry, name),
|
|
586
|
+
]),
|
|
587
|
+
),
|
|
588
|
+
);
|
|
589
|
+
|
|
590
|
+
const e2e = section.e2e ?? {};
|
|
591
|
+
if (!isMapping(e2e)) fail("packageBuild.e2e", "must be a mapping");
|
|
592
|
+
rejectUnknownKeys(
|
|
593
|
+
/** @type {Record<string, unknown>} */ (e2e),
|
|
594
|
+
E2E_KEYS,
|
|
595
|
+
"packageBuild.e2e.",
|
|
596
|
+
);
|
|
597
|
+
const e2eInput = /** @type {Record<string, unknown>} */ (e2e);
|
|
598
|
+
const declaredBuild = e2eInput.build ?? {};
|
|
599
|
+
if (!isMapping(declaredBuild)) {
|
|
600
|
+
fail("packageBuild.e2e.build", "must be a mapping");
|
|
601
|
+
}
|
|
602
|
+
// Declaration order is build order — a mapping preserves it, and the
|
|
603
|
+
// bundler has to run before the passes that copy into the stage it empties.
|
|
604
|
+
const e2eBuild = Object.freeze(
|
|
605
|
+
Object.fromEntries(
|
|
606
|
+
Object.entries(
|
|
607
|
+
/** @type {Record<string, unknown>} */ (declaredBuild),
|
|
608
|
+
).map(([name, entry]) => [
|
|
609
|
+
name,
|
|
610
|
+
normalizeE2EBuildTarget(entry, name),
|
|
611
|
+
]),
|
|
612
|
+
),
|
|
613
|
+
);
|
|
614
|
+
|
|
324
615
|
return Object.freeze({
|
|
325
616
|
rootDir: shared.rootDir,
|
|
326
617
|
// Where the package is assembled before it is zipped or deployed. Every
|
|
@@ -381,6 +672,57 @@ export function resolvePackageBuildConfig(shared) {
|
|
|
381
672
|
langInput.help === undefined ?
|
|
382
673
|
null
|
|
383
674
|
: requireNonEmptyString(langInput.help, "packageBuild.lang.help"),
|
|
675
|
+
// The file the package authors, and the one every other translation is
|
|
676
|
+
// measured against. Coverage is a question about *this* file: another
|
|
677
|
+
// language missing a key is a translation in progress, not a defect.
|
|
678
|
+
langPrimary:
|
|
679
|
+
langInput.primary === undefined ?
|
|
680
|
+
"lang/en.json"
|
|
681
|
+
: requireNonEmptyString(
|
|
682
|
+
langInput.primary,
|
|
683
|
+
"packageBuild.lang.primary",
|
|
684
|
+
),
|
|
685
|
+
langScripts: normalizeGlobs(
|
|
686
|
+
langInput.scripts,
|
|
687
|
+
["src/**/*.{ts,mjs}"],
|
|
688
|
+
"packageBuild.lang.scripts",
|
|
689
|
+
),
|
|
690
|
+
langTemplates: normalizeGlobs(
|
|
691
|
+
langInput.templates,
|
|
692
|
+
["templates/**/*.hbs"],
|
|
693
|
+
"packageBuild.lang.templates",
|
|
694
|
+
),
|
|
695
|
+
// Derived from the file's own keys unless stated — a package states
|
|
696
|
+
// them only when it references a root the file does not yet declare at
|
|
697
|
+
// all, which is the one case deriving them cannot cover.
|
|
698
|
+
langKeyRoots:
|
|
699
|
+
langInput.keyRoots === undefined ?
|
|
700
|
+
null
|
|
701
|
+
: normalizeGlobs(
|
|
702
|
+
langInput.keyRoots,
|
|
703
|
+
[],
|
|
704
|
+
"packageBuild.lang.keyRoots",
|
|
705
|
+
),
|
|
706
|
+
langReferences:
|
|
707
|
+
langInput.references === undefined ?
|
|
708
|
+
null
|
|
709
|
+
: path.resolve(
|
|
710
|
+
shared.rootDir,
|
|
711
|
+
requireNonEmptyString(
|
|
712
|
+
langInput.references,
|
|
713
|
+
"packageBuild.lang.references",
|
|
714
|
+
),
|
|
715
|
+
),
|
|
716
|
+
langRetained: normalizeExceptions(
|
|
717
|
+
langInput.retained,
|
|
718
|
+
"prefix",
|
|
719
|
+
"packageBuild.lang.retained",
|
|
720
|
+
),
|
|
721
|
+
langAllow: normalizeExceptions(
|
|
722
|
+
langInput.allow,
|
|
723
|
+
"literal",
|
|
724
|
+
"packageBuild.lang.allow",
|
|
725
|
+
),
|
|
384
726
|
envPrefix:
|
|
385
727
|
deployInput.envPrefix === undefined ?
|
|
386
728
|
"SOHL"
|
|
@@ -404,6 +746,43 @@ export function resolvePackageBuildConfig(shared) {
|
|
|
404
746
|
bundleInput.entry,
|
|
405
747
|
"packageBuild.bundle.entry",
|
|
406
748
|
),
|
|
749
|
+
// Read from the top level, where the package already claims it. The
|
|
750
|
+
// end-to-end pin derives from this, so the claim and the evidence for
|
|
751
|
+
// it are the same number and cannot drift apart.
|
|
752
|
+
compatibilityMinimum: shared.compatibility?.minimum ?? null,
|
|
753
|
+
// The system a seeded world runs. For a system package that is itself;
|
|
754
|
+
// for a module it is the system it targets, which the shared
|
|
755
|
+
// configuration already names.
|
|
756
|
+
systemId: shared.stats?.systemId ?? shared.foundryPackage,
|
|
757
|
+
systemVersion: shared.stats?.systemVersion ?? null,
|
|
758
|
+
containerImage:
|
|
759
|
+
containerInput.image === undefined ?
|
|
760
|
+
null
|
|
761
|
+
: requireNonEmptyString(
|
|
762
|
+
containerInput.image,
|
|
763
|
+
"packageBuild.container.image",
|
|
764
|
+
),
|
|
765
|
+
containerStages,
|
|
766
|
+
e2eStage:
|
|
767
|
+
e2eInput.stage === undefined ?
|
|
768
|
+
"test"
|
|
769
|
+
: requireNonEmptyString(e2eInput.stage, "packageBuild.e2e.stage"),
|
|
770
|
+
e2eSuite: normalizeE2ESuite(e2eInput.suite),
|
|
771
|
+
e2eBuild,
|
|
772
|
+
e2eWorld: normalizeStringMap(
|
|
773
|
+
e2eInput.world,
|
|
774
|
+
"packageBuild.e2e.world",
|
|
775
|
+
E2E_WORLD_KEYS,
|
|
776
|
+
),
|
|
777
|
+
e2eGm: normalizeStringMap(
|
|
778
|
+
e2eInput.gm,
|
|
779
|
+
"packageBuild.e2e.gm",
|
|
780
|
+
E2E_GM_KEYS,
|
|
781
|
+
),
|
|
782
|
+
e2eDocuments: normalizeStringMap(
|
|
783
|
+
e2eInput.documents,
|
|
784
|
+
"packageBuild.e2e.documents",
|
|
785
|
+
),
|
|
407
786
|
});
|
|
408
787
|
}
|
|
409
788
|
|