@bedrock-rbx/core 0.1.0-beta.12 → 0.1.0-beta.13
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/dist/cli/run.mjs +39 -8
- package/dist/cli/run.mjs.map +1 -1
- package/dist/config.d.mts +2 -2
- package/dist/{define-config-87u2jqjM.d.mts → define-config-Bd0XIiSX.d.mts} +155 -8
- package/dist/{define-config-87u2jqjM.d.mts.map → define-config-Bd0XIiSX.d.mts.map} +1 -1
- package/dist/index.d.mts +25 -4
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{migrate-mantle-state-Dkk5zGHw.mjs → migrate-mantle-state-CQjWBZwT.mjs} +543 -68
- package/dist/migrate-mantle-state-CQjWBZwT.mjs.map +1 -0
- package/package.json +2 -2
- package/dist/migrate-mantle-state-Dkk5zGHw.mjs.map +0 -1
|
@@ -144,6 +144,7 @@ function deployErrorMessage(err) {
|
|
|
144
144
|
case "applyFailed": return `apply failed for '${err.cause.key}': ${applyCauseDetail(err.cause)}`;
|
|
145
145
|
case "buildDesiredFailed": return `build desired state failed for '${err.cause.key}' ${buildDesiredDetail(err.cause)}`;
|
|
146
146
|
case "configLoadFailed": return `config load failed: ${configErrorDetail(err.cause)}`;
|
|
147
|
+
case "incompletePassEntry": return `pass '${err.key}' is missing '${err.missingField}' under environment '${err.environment}'`;
|
|
147
148
|
case "incompletePlaceEntry": return `place '${err.key}' is missing '${err.missingField}' under environment '${err.environment}'`;
|
|
148
149
|
case "incompleteUniverseEntry": return `universe is missing '${err.missingField}' under environment '${err.environment}'`;
|
|
149
150
|
case "missingCredential": return `missing credential: environment variable ${err.variable} is not set`;
|
|
@@ -454,17 +455,255 @@ async function sha256Hex(bytes) {
|
|
|
454
455
|
return Array.from(new Uint8Array(buffer), (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
455
456
|
}
|
|
456
457
|
//#endregion
|
|
458
|
+
//#region src/core/redacted-icon.ts
|
|
459
|
+
const REDACTED_ICON_BYTES = new Uint8Array([
|
|
460
|
+
137,
|
|
461
|
+
80,
|
|
462
|
+
78,
|
|
463
|
+
71,
|
|
464
|
+
13,
|
|
465
|
+
10,
|
|
466
|
+
26,
|
|
467
|
+
10,
|
|
468
|
+
0,
|
|
469
|
+
0,
|
|
470
|
+
0,
|
|
471
|
+
13,
|
|
472
|
+
73,
|
|
473
|
+
72,
|
|
474
|
+
68,
|
|
475
|
+
82,
|
|
476
|
+
0,
|
|
477
|
+
0,
|
|
478
|
+
0,
|
|
479
|
+
64,
|
|
480
|
+
0,
|
|
481
|
+
0,
|
|
482
|
+
0,
|
|
483
|
+
64,
|
|
484
|
+
8,
|
|
485
|
+
0,
|
|
486
|
+
0,
|
|
487
|
+
0,
|
|
488
|
+
0,
|
|
489
|
+
143,
|
|
490
|
+
2,
|
|
491
|
+
46,
|
|
492
|
+
2,
|
|
493
|
+
0,
|
|
494
|
+
0,
|
|
495
|
+
0,
|
|
496
|
+
137,
|
|
497
|
+
73,
|
|
498
|
+
68,
|
|
499
|
+
65,
|
|
500
|
+
84,
|
|
501
|
+
120,
|
|
502
|
+
156,
|
|
503
|
+
237,
|
|
504
|
+
86,
|
|
505
|
+
209,
|
|
506
|
+
14,
|
|
507
|
+
128,
|
|
508
|
+
32,
|
|
509
|
+
8,
|
|
510
|
+
244,
|
|
511
|
+
83,
|
|
512
|
+
252,
|
|
513
|
+
148,
|
|
514
|
+
254,
|
|
515
|
+
255,
|
|
516
|
+
167,
|
|
517
|
+
174,
|
|
518
|
+
37,
|
|
519
|
+
98,
|
|
520
|
+
130,
|
|
521
|
+
189,
|
|
522
|
+
20,
|
|
523
|
+
110,
|
|
524
|
+
57,
|
|
525
|
+
119,
|
|
526
|
+
108,
|
|
527
|
+
26,
|
|
528
|
+
194,
|
|
529
|
+
188,
|
|
530
|
+
64,
|
|
531
|
+
15,
|
|
532
|
+
42,
|
|
533
|
+
229,
|
|
534
|
+
160,
|
|
535
|
+
164,
|
|
536
|
+
13,
|
|
537
|
+
0,
|
|
538
|
+
142,
|
|
539
|
+
160,
|
|
540
|
+
44,
|
|
541
|
+
144,
|
|
542
|
+
2,
|
|
543
|
+
1,
|
|
544
|
+
200,
|
|
545
|
+
3,
|
|
546
|
+
242,
|
|
547
|
+
96,
|
|
548
|
+
82,
|
|
549
|
+
45,
|
|
550
|
+
176,
|
|
551
|
+
31,
|
|
552
|
+
176,
|
|
553
|
+
161,
|
|
554
|
+
244,
|
|
555
|
+
60,
|
|
556
|
+
0,
|
|
557
|
+
0,
|
|
558
|
+
153,
|
|
559
|
+
81,
|
|
560
|
+
245,
|
|
561
|
+
235,
|
|
562
|
+
161,
|
|
563
|
+
142,
|
|
564
|
+
219,
|
|
565
|
+
222,
|
|
566
|
+
188,
|
|
567
|
+
254,
|
|
568
|
+
187,
|
|
569
|
+
128,
|
|
570
|
+
50,
|
|
571
|
+
224,
|
|
572
|
+
116,
|
|
573
|
+
181,
|
|
574
|
+
168,
|
|
575
|
+
205,
|
|
576
|
+
106,
|
|
577
|
+
134,
|
|
578
|
+
202,
|
|
579
|
+
113,
|
|
580
|
+
0,
|
|
581
|
+
0,
|
|
582
|
+
109,
|
|
583
|
+
150,
|
|
584
|
+
173,
|
|
585
|
+
101,
|
|
586
|
+
97,
|
|
587
|
+
0,
|
|
588
|
+
58,
|
|
589
|
+
239,
|
|
590
|
+
67,
|
|
591
|
+
4,
|
|
592
|
+
254,
|
|
593
|
+
29,
|
|
594
|
+
239,
|
|
595
|
+
83,
|
|
596
|
+
168,
|
|
597
|
+
232,
|
|
598
|
+
177,
|
|
599
|
+
51,
|
|
600
|
+
144,
|
|
601
|
+
176,
|
|
602
|
+
251,
|
|
603
|
+
80,
|
|
604
|
+
101,
|
|
605
|
+
209,
|
|
606
|
+
82,
|
|
607
|
+
80,
|
|
608
|
+
239,
|
|
609
|
+
0,
|
|
610
|
+
240,
|
|
611
|
+
85,
|
|
612
|
+
216,
|
|
613
|
+
15,
|
|
614
|
+
216,
|
|
615
|
+
15,
|
|
616
|
+
200,
|
|
617
|
+
131,
|
|
618
|
+
89,
|
|
619
|
+
197,
|
|
620
|
+
180,
|
|
621
|
+
1,
|
|
622
|
+
0,
|
|
623
|
+
255,
|
|
624
|
+
15,
|
|
625
|
+
86,
|
|
626
|
+
184,
|
|
627
|
+
5,
|
|
628
|
+
2,
|
|
629
|
+
228,
|
|
630
|
+
255,
|
|
631
|
+
207,
|
|
632
|
+
224,
|
|
633
|
+
4,
|
|
634
|
+
233,
|
|
635
|
+
243,
|
|
636
|
+
166,
|
|
637
|
+
219,
|
|
638
|
+
234,
|
|
639
|
+
149,
|
|
640
|
+
21,
|
|
641
|
+
116,
|
|
642
|
+
0,
|
|
643
|
+
0,
|
|
644
|
+
0,
|
|
645
|
+
0,
|
|
646
|
+
73,
|
|
647
|
+
69,
|
|
648
|
+
78,
|
|
649
|
+
68,
|
|
650
|
+
174,
|
|
651
|
+
66,
|
|
652
|
+
96,
|
|
653
|
+
130
|
|
654
|
+
]);
|
|
655
|
+
/**
|
|
656
|
+
* Sentinel path written into a resource's `icon["en-us"]` field when
|
|
657
|
+
* redaction substitutes the bedrock-supplied placeholder image. Callers
|
|
658
|
+
* route this path through {@link withRedactedIcon} or through the
|
|
659
|
+
* `readBytes` short-circuit; neither touches the filesystem.
|
|
660
|
+
*/
|
|
661
|
+
const REDACTED_ICON_PATH = "<bedrock:redacted-icon.png>";
|
|
662
|
+
/**
|
|
663
|
+
* `true` when `path` is the redacted-icon sentinel. `readBytes` and
|
|
664
|
+
* {@link withRedactedIcon} both use this predicate to decide whether to
|
|
665
|
+
* bypass the injected file reader.
|
|
666
|
+
*
|
|
667
|
+
* @param path - Icon path supplied by a flattened or normalized resource entry.
|
|
668
|
+
* @returns `true` for {@link REDACTED_ICON_PATH}; otherwise `false`.
|
|
669
|
+
*/
|
|
670
|
+
function isRedactedIconPath(path) {
|
|
671
|
+
return path === REDACTED_ICON_PATH;
|
|
672
|
+
}
|
|
673
|
+
/**
|
|
674
|
+
* Wrap a `readFile` so the sentinel resolves to {@link REDACTED_ICON_BYTES}
|
|
675
|
+
* without touching the inner reader. Applied once at the shell deploy /
|
|
676
|
+
* preview boundary; the wrapped reader flows to every consumer (normalize,
|
|
677
|
+
* registry drivers) unchanged.
|
|
678
|
+
*
|
|
679
|
+
* @param readFile - Inner reader that handles every non-sentinel path.
|
|
680
|
+
* @returns Sentinel-aware reader with the same callable shape as `readFile`.
|
|
681
|
+
*/
|
|
682
|
+
function withRedactedIcon(readFile) {
|
|
683
|
+
return async (path) => {
|
|
684
|
+
if (isRedactedIconPath(path)) return new Uint8Array(REDACTED_ICON_BYTES);
|
|
685
|
+
return readFile(path);
|
|
686
|
+
};
|
|
687
|
+
}
|
|
688
|
+
//#endregion
|
|
457
689
|
//#region src/core/kinds/read-bytes.ts
|
|
458
690
|
/**
|
|
459
691
|
* Read file bytes via the injected reader, translating rejections into a
|
|
460
692
|
* `fileReadFailed` `BuildDesiredError`. Shared by kind modules whose
|
|
461
|
-
* pre-I/O normalization hashes a file the user declared by path.
|
|
693
|
+
* pre-I/O normalization hashes a file the user declared by path. The
|
|
694
|
+
* redacted-icon sentinel short-circuits to the embedded placeholder
|
|
695
|
+
* bytes without invoking the injected reader, so a redaction-substituted
|
|
696
|
+
* icon path produces a deterministic hash on every deploy.
|
|
462
697
|
*
|
|
463
698
|
* @param target - Path to read plus the resource key blamed on failure.
|
|
464
699
|
* @param io - I/O surface carrying the injected `readFile` function.
|
|
465
700
|
* @returns `Ok` with the bytes, or `Err` with a `fileReadFailed` error.
|
|
466
701
|
*/
|
|
467
702
|
async function readBytes(target, io) {
|
|
703
|
+
if (isRedactedIconPath(target.filePath)) return {
|
|
704
|
+
data: new Uint8Array(REDACTED_ICON_BYTES),
|
|
705
|
+
success: true
|
|
706
|
+
};
|
|
468
707
|
try {
|
|
469
708
|
return {
|
|
470
709
|
data: await io.readFile(target.filePath),
|
|
@@ -696,12 +935,16 @@ function planFollowUpPatch(desired, createResponse) {
|
|
|
696
935
|
* ```
|
|
697
936
|
*/
|
|
698
937
|
function createDeveloperProductDriver(deps) {
|
|
938
|
+
const effective = {
|
|
939
|
+
...deps,
|
|
940
|
+
readFile: withRedactedIcon(deps.readFile)
|
|
941
|
+
};
|
|
699
942
|
return {
|
|
700
943
|
async create(desired) {
|
|
701
|
-
return createOne(
|
|
944
|
+
return createOne(effective, desired);
|
|
702
945
|
},
|
|
703
946
|
async update(current, desired) {
|
|
704
|
-
return updateOne(
|
|
947
|
+
return updateOne(effective, {
|
|
705
948
|
current,
|
|
706
949
|
desired
|
|
707
950
|
});
|
|
@@ -853,12 +1096,16 @@ async function updateOne(deps, { current, desired }) {
|
|
|
853
1096
|
* ```
|
|
854
1097
|
*/
|
|
855
1098
|
function createGamePassDriver(deps) {
|
|
1099
|
+
const effective = {
|
|
1100
|
+
...deps,
|
|
1101
|
+
readFile: withRedactedIcon(deps.readFile)
|
|
1102
|
+
};
|
|
856
1103
|
return {
|
|
857
1104
|
async create(desired) {
|
|
858
|
-
return createGamePass(
|
|
1105
|
+
return createGamePass(effective, desired);
|
|
859
1106
|
},
|
|
860
1107
|
async update(current, desired) {
|
|
861
|
-
return updateGamePass(
|
|
1108
|
+
return updateGamePass(effective, {
|
|
862
1109
|
current,
|
|
863
1110
|
desired
|
|
864
1111
|
});
|
|
@@ -1891,6 +2138,8 @@ function isGistStateConfig(config) {
|
|
|
1891
2138
|
}
|
|
1892
2139
|
const OPTIONAL_BOOLEAN$2 = "boolean | undefined";
|
|
1893
2140
|
const OPTIONAL_STRING = "string | undefined";
|
|
2141
|
+
const REDACTED_KEY = "redacted?";
|
|
2142
|
+
const NON_EMPTY_OVERRIDE_MESSAGE = "a non-empty override object; use `redacted: true` for default placeholders";
|
|
1894
2143
|
/**
|
|
1895
2144
|
* Shared arktype constraint for any optional positive-integer field.
|
|
1896
2145
|
* Reused by per-kind entry schemas so positive-integer fields validate
|
|
@@ -1907,11 +2156,35 @@ const OPTIONAL_POSITIVE_INTEGER = "(number.integer >= 1) | undefined";
|
|
|
1907
2156
|
* identically.
|
|
1908
2157
|
*/
|
|
1909
2158
|
const OPTIONAL_ROBUX_PRICE = "number.integer >= 0 | undefined";
|
|
2159
|
+
const gamePassRedacted = type({
|
|
2160
|
+
"description?": "string",
|
|
2161
|
+
"icon?": iconMap,
|
|
2162
|
+
"name?": "string"
|
|
2163
|
+
}).onUndeclaredKey("reject").narrow((value, ctx) => {
|
|
2164
|
+
if (Object.keys(value).length === 0) return ctx.mustBe(NON_EMPTY_OVERRIDE_MESSAGE);
|
|
2165
|
+
return true;
|
|
2166
|
+
}).or(OPTIONAL_BOOLEAN$2);
|
|
2167
|
+
const placeRedacted = type({
|
|
2168
|
+
"description?": "string",
|
|
2169
|
+
"displayName?": "string"
|
|
2170
|
+
}).onUndeclaredKey("reject").narrow((value, ctx) => {
|
|
2171
|
+
if (Object.keys(value).length === 0) return ctx.mustBe(NON_EMPTY_OVERRIDE_MESSAGE);
|
|
2172
|
+
return true;
|
|
2173
|
+
}).or(OPTIONAL_BOOLEAN$2);
|
|
2174
|
+
const productRedacted = type({
|
|
2175
|
+
"description?": "string",
|
|
2176
|
+
"icon?": iconMap,
|
|
2177
|
+
"name?": "string"
|
|
2178
|
+
}).onUndeclaredKey("reject").narrow((value, ctx) => {
|
|
2179
|
+
if (Object.keys(value).length === 0) return ctx.mustBe(NON_EMPTY_OVERRIDE_MESSAGE);
|
|
2180
|
+
return true;
|
|
2181
|
+
}).or(OPTIONAL_BOOLEAN$2);
|
|
1910
2182
|
const gamePassEntry = type({
|
|
1911
2183
|
"name": "string",
|
|
1912
2184
|
"description": "string",
|
|
1913
2185
|
"icon": iconMap,
|
|
1914
|
-
"price?": OPTIONAL_ROBUX_PRICE
|
|
2186
|
+
"price?": OPTIONAL_ROBUX_PRICE,
|
|
2187
|
+
[REDACTED_KEY]: gamePassRedacted
|
|
1915
2188
|
});
|
|
1916
2189
|
const passesCollection = type({ [`[/${RESOURCE_KEY_PATTERN_SOURCE}/]`]: gamePassEntry }).onUndeclaredKey("reject");
|
|
1917
2190
|
const developerProductEntry = type({
|
|
@@ -1920,6 +2193,7 @@ const developerProductEntry = type({
|
|
|
1920
2193
|
"icon?": iconMap,
|
|
1921
2194
|
"isRegionalPricingEnabled?": OPTIONAL_BOOLEAN$2,
|
|
1922
2195
|
"price?": OPTIONAL_ROBUX_PRICE,
|
|
2196
|
+
[REDACTED_KEY]: productRedacted,
|
|
1923
2197
|
"storePageEnabled?": OPTIONAL_BOOLEAN$2
|
|
1924
2198
|
}).onUndeclaredKey("reject");
|
|
1925
2199
|
const productsCollection = type({ [`[/${RESOURCE_KEY_PATTERN_SOURCE}/]`]: developerProductEntry }).onUndeclaredKey("reject");
|
|
@@ -1928,6 +2202,7 @@ const placeEntry = type({
|
|
|
1928
2202
|
"description?": OPTIONAL_STRING,
|
|
1929
2203
|
"displayName?": OPTIONAL_STRING,
|
|
1930
2204
|
"filePath": "string",
|
|
2205
|
+
[REDACTED_KEY]: placeRedacted,
|
|
1931
2206
|
"serverSize?": OPTIONAL_POSITIVE_INTEGER
|
|
1932
2207
|
}).onUndeclaredKey("reject");
|
|
1933
2208
|
const placesCollection = type({ [`[/${RESOURCE_KEY_PATTERN_SOURCE}/]`]: placeEntry }).onUndeclaredKey("reject");
|
|
@@ -1961,7 +2236,8 @@ const gamePassOverlay = type({
|
|
|
1961
2236
|
"description?": "string",
|
|
1962
2237
|
"icon?": iconMap,
|
|
1963
2238
|
"name?": "string",
|
|
1964
|
-
"price?": OPTIONAL_ROBUX_PRICE
|
|
2239
|
+
"price?": OPTIONAL_ROBUX_PRICE,
|
|
2240
|
+
[REDACTED_KEY]: OPTIONAL_BOOLEAN$2
|
|
1965
2241
|
}).onUndeclaredKey("reject");
|
|
1966
2242
|
const passesOverlayCollection = type({ [`[/${RESOURCE_KEY_PATTERN_SOURCE}/]`]: gamePassOverlay }).onUndeclaredKey("reject");
|
|
1967
2243
|
const developerProductOverlay = type({
|
|
@@ -1970,6 +2246,7 @@ const developerProductOverlay = type({
|
|
|
1970
2246
|
"isRegionalPricingEnabled?": OPTIONAL_BOOLEAN$2,
|
|
1971
2247
|
"name?": "string",
|
|
1972
2248
|
"price?": OPTIONAL_ROBUX_PRICE,
|
|
2249
|
+
[REDACTED_KEY]: OPTIONAL_BOOLEAN$2,
|
|
1973
2250
|
"storePageEnabled?": OPTIONAL_BOOLEAN$2
|
|
1974
2251
|
}).onUndeclaredKey("reject");
|
|
1975
2252
|
const productsOverlayCollection = type({ [`[/${RESOURCE_KEY_PATTERN_SOURCE}/]`]: developerProductOverlay }).onUndeclaredKey("reject");
|
|
@@ -1978,15 +2255,19 @@ const placeOverlay = type({
|
|
|
1978
2255
|
"displayName?": OPTIONAL_STRING,
|
|
1979
2256
|
"filePath?": "string",
|
|
1980
2257
|
"placeId": ROBLOX_ID_DIGITS,
|
|
2258
|
+
[REDACTED_KEY]: OPTIONAL_BOOLEAN$2,
|
|
1981
2259
|
"serverSize?": OPTIONAL_POSITIVE_INTEGER
|
|
1982
2260
|
}).onUndeclaredKey("reject");
|
|
2261
|
+
const placesOverlayCollection = type({ [`[/${RESOURCE_KEY_PATTERN_SOURCE}/]`]: placeOverlay }).onUndeclaredKey("reject");
|
|
2262
|
+
const universeOverlay = universeEntry;
|
|
1983
2263
|
const environmentEntry = type({
|
|
1984
2264
|
"label?": OPTIONAL_STRING,
|
|
1985
2265
|
"passes?": passesOverlayCollection,
|
|
1986
|
-
"places?":
|
|
2266
|
+
"places?": placesOverlayCollection,
|
|
1987
2267
|
"products?": productsOverlayCollection,
|
|
2268
|
+
[REDACTED_KEY]: OPTIONAL_BOOLEAN$2,
|
|
1988
2269
|
"state?": stateConfig,
|
|
1989
|
-
"universe?":
|
|
2270
|
+
"universe?": universeOverlay
|
|
1990
2271
|
}).onUndeclaredKey("reject");
|
|
1991
2272
|
const rootSchema = type({
|
|
1992
2273
|
"displayNamePrefix?": type({
|
|
@@ -2083,6 +2364,7 @@ const entrySchema$3 = type({
|
|
|
2083
2364
|
"icon?": iconMap,
|
|
2084
2365
|
"isRegionalPricingEnabled?": OPTIONAL_BOOLEAN$1,
|
|
2085
2366
|
"price?": OPTIONAL_ROBUX_PRICE,
|
|
2367
|
+
"redacted?": "boolean | undefined",
|
|
2086
2368
|
"storePageEnabled?": OPTIONAL_BOOLEAN$1
|
|
2087
2369
|
});
|
|
2088
2370
|
function flatten$3(config) {
|
|
@@ -2166,7 +2448,8 @@ const entrySchema$2 = type({
|
|
|
2166
2448
|
"name": "string",
|
|
2167
2449
|
"description": "string",
|
|
2168
2450
|
"icon": iconMap,
|
|
2169
|
-
"price?": OPTIONAL_ROBUX_PRICE
|
|
2451
|
+
"price?": OPTIONAL_ROBUX_PRICE,
|
|
2452
|
+
"redacted?": "boolean | undefined"
|
|
2170
2453
|
});
|
|
2171
2454
|
function flatten$2(config) {
|
|
2172
2455
|
return Object.entries(config.passes ?? {}).map(([key, entry]) => {
|
|
@@ -2647,9 +2930,172 @@ function resolveStateConfig(config, environment) {
|
|
|
2647
2930
|
success: false
|
|
2648
2931
|
};
|
|
2649
2932
|
}
|
|
2933
|
+
/**
|
|
2934
|
+
* Pure transform that substitutes bedrock-supplied placeholder content for
|
|
2935
|
+
* every resource whose effective `redacted` flag is truthy. The effective
|
|
2936
|
+
* flag is the per-resource `redacted` value when set, otherwise the
|
|
2937
|
+
* `environmentRedacted` fallback. A `redacted` object form replaces
|
|
2938
|
+
* matching fields with the supplied values and falls back to the bedrock
|
|
2939
|
+
* defaults for the rest. Runs between env-overlay merge and display-name
|
|
2940
|
+
* prefix render so the rest of the pipeline (flatten, normalize, diff,
|
|
2941
|
+
* apply) operates on already-redacted values and needs no special-case
|
|
2942
|
+
* redaction logic.
|
|
2943
|
+
*
|
|
2944
|
+
* @param config - Post-merge `ResolvedConfig` produced by `selectEnvironment`.
|
|
2945
|
+
* @param environmentRedacted - Environment-level redaction toggle. Resources
|
|
2946
|
+
* that omit a per-resource `redacted` flag inherit this value.
|
|
2947
|
+
* @returns A `ResolvedConfig` whose redacted entries carry placeholder
|
|
2948
|
+
* values; non-redacted entries pass through verbatim, and the input is
|
|
2949
|
+
* not mutated.
|
|
2950
|
+
*/
|
|
2951
|
+
function applyRedaction(config, environmentRedacted = false) {
|
|
2952
|
+
const passes = redactPasses(config.passes, environmentRedacted);
|
|
2953
|
+
const places = redactPlaces(config.places, environmentRedacted);
|
|
2954
|
+
const products = redactProducts(config.products, environmentRedacted);
|
|
2955
|
+
if (passes === config.passes && places === config.places && products === config.products) return config;
|
|
2956
|
+
return {
|
|
2957
|
+
...config,
|
|
2958
|
+
...passes === void 0 ? {} : { passes },
|
|
2959
|
+
...places === void 0 ? {} : { places },
|
|
2960
|
+
...products === void 0 ? {} : { products }
|
|
2961
|
+
};
|
|
2962
|
+
}
|
|
2963
|
+
/**
|
|
2964
|
+
* Inspect the pre-redaction merged config and produce one annotation per
|
|
2965
|
+
* resource flagged `redacted: true`. Callers thread the result into plan
|
|
2966
|
+
* output so authors can see which resources are redacted in the active
|
|
2967
|
+
* environment and whether their real-value edits are being suppressed.
|
|
2968
|
+
*
|
|
2969
|
+
* Operates on the pre-redaction view because the post-redaction config no
|
|
2970
|
+
* longer carries the real `name`/`description`/`icon` values needed to
|
|
2971
|
+
* detect divergence from the placeholder defaults.
|
|
2972
|
+
*
|
|
2973
|
+
* @param merged - `ResolvedConfig` produced by environment overlay merge,
|
|
2974
|
+
* before `applyRedaction` has substituted placeholders.
|
|
2975
|
+
* @returns Zero or more annotations, one per redacted resource. Empty when
|
|
2976
|
+
* the config declares no redacted resources.
|
|
2977
|
+
*/
|
|
2978
|
+
function collectRedactionAnnotations(merged) {
|
|
2979
|
+
const passes = Object.entries(merged.passes ?? {}).filter(([, entry]) => entry.redacted === true).map(([key, entry]) => {
|
|
2980
|
+
return {
|
|
2981
|
+
key: asResourceKey(key),
|
|
2982
|
+
hasRealValueEdits: passHasRealValueEdits(entry),
|
|
2983
|
+
kind: "gamePass"
|
|
2984
|
+
};
|
|
2985
|
+
});
|
|
2986
|
+
const products = Object.entries(merged.products ?? {}).filter(([, entry]) => entry.redacted === true).map(([key, entry]) => {
|
|
2987
|
+
return {
|
|
2988
|
+
key: asResourceKey(key),
|
|
2989
|
+
hasRealValueEdits: productHasRealValueEdits(entry),
|
|
2990
|
+
kind: "developerProduct"
|
|
2991
|
+
};
|
|
2992
|
+
});
|
|
2993
|
+
return [...passes, ...products];
|
|
2994
|
+
}
|
|
2995
|
+
function redactPass(entry, override) {
|
|
2996
|
+
return {
|
|
2997
|
+
...entry,
|
|
2998
|
+
name: override.name ?? "Redacted Pass",
|
|
2999
|
+
description: override.description ?? "",
|
|
3000
|
+
icon: override.icon ?? { "en-us": "<bedrock:redacted-icon.png>" }
|
|
3001
|
+
};
|
|
3002
|
+
}
|
|
3003
|
+
function redactPasses(passes, environmentRedacted) {
|
|
3004
|
+
if (passes === void 0) return;
|
|
3005
|
+
if (!Object.values(passes).some((entry) => (entry.redacted ?? environmentRedacted) !== false)) return passes;
|
|
3006
|
+
return Object.fromEntries(Object.entries(passes).map(([key, entry]) => {
|
|
3007
|
+
const effective = entry.redacted ?? environmentRedacted;
|
|
3008
|
+
if (effective === false) return [key, entry];
|
|
3009
|
+
return [key, redactPass(entry, typeof effective === "object" ? effective : {})];
|
|
3010
|
+
}));
|
|
3011
|
+
}
|
|
3012
|
+
function redactPlace(entry, override) {
|
|
3013
|
+
return {
|
|
3014
|
+
...entry,
|
|
3015
|
+
description: override.description ?? "",
|
|
3016
|
+
displayName: override.displayName ?? entry.displayName
|
|
3017
|
+
};
|
|
3018
|
+
}
|
|
3019
|
+
function redactPlaces(places, environmentRedacted) {
|
|
3020
|
+
if (places === void 0) return;
|
|
3021
|
+
if (!Object.values(places).some((entry) => (entry.redacted ?? environmentRedacted) !== false)) return places;
|
|
3022
|
+
return Object.fromEntries(Object.entries(places).map(([key, entry]) => {
|
|
3023
|
+
const effective = entry.redacted ?? environmentRedacted;
|
|
3024
|
+
if (effective === false) return [key, entry];
|
|
3025
|
+
return [key, redactPlace(entry, typeof effective === "object" ? effective : {})];
|
|
3026
|
+
}));
|
|
3027
|
+
}
|
|
3028
|
+
function redactProduct(entry, override) {
|
|
3029
|
+
return {
|
|
3030
|
+
...entry,
|
|
3031
|
+
name: override.name ?? "Redacted Product",
|
|
3032
|
+
description: override.description ?? "",
|
|
3033
|
+
icon: override.icon ?? { "en-us": "<bedrock:redacted-icon.png>" }
|
|
3034
|
+
};
|
|
3035
|
+
}
|
|
3036
|
+
function redactProducts(products, environmentRedacted) {
|
|
3037
|
+
if (products === void 0) return;
|
|
3038
|
+
if (!Object.values(products).some((entry) => (entry.redacted ?? environmentRedacted) !== false)) return products;
|
|
3039
|
+
return Object.fromEntries(Object.entries(products).map(([key, entry]) => {
|
|
3040
|
+
const effective = entry.redacted ?? environmentRedacted;
|
|
3041
|
+
if (effective === false) return [key, entry];
|
|
3042
|
+
return [key, redactProduct(entry, typeof effective === "object" ? effective : {})];
|
|
3043
|
+
}));
|
|
3044
|
+
}
|
|
3045
|
+
function passHasRealValueEdits(entry) {
|
|
3046
|
+
return entry.name !== "Redacted Pass" || entry.description !== "" || entry.icon["en-us"] !== "<bedrock:redacted-icon.png>";
|
|
3047
|
+
}
|
|
3048
|
+
function productHasRealValueEdits(entry) {
|
|
3049
|
+
return entry.name !== "Redacted Product" || entry.description !== "" || entry.icon !== void 0 && entry.icon["en-us"] !== "<bedrock:redacted-icon.png>";
|
|
3050
|
+
}
|
|
2650
3051
|
//#endregion
|
|
2651
3052
|
//#region src/core/select-environment.ts
|
|
2652
3053
|
/**
|
|
3054
|
+
* Project a `Config` onto a single environment up to the pre-redaction
|
|
3055
|
+
* merge boundary. Looks up the env entry, deep-merges its resource overlay
|
|
3056
|
+
* over the root config, and runs the same pass, place, and universe
|
|
3057
|
+
* completeness checks {@link selectEnvironment} runs, so the returned
|
|
3058
|
+
* `merged` config honours the full `ResolvedConfig` contract. Real
|
|
3059
|
+
* `name`, `description`, and `icon` values on redacted resources stay
|
|
3060
|
+
* intact, letting callers inspect divergence from placeholder defaults
|
|
3061
|
+
* before {@link selectEnvironment} substitutes them.
|
|
3062
|
+
*
|
|
3063
|
+
* @param config - Validated project config.
|
|
3064
|
+
* @param environment - Environment name to project onto.
|
|
3065
|
+
* @returns The matched env entry plus the merged config, or any of the
|
|
3066
|
+
* `SelectEnvironmentError` failure modes.
|
|
3067
|
+
*/
|
|
3068
|
+
function selectMergedEnvironment(config, environment) {
|
|
3069
|
+
const entry = config.environments[environment];
|
|
3070
|
+
if (entry === void 0) return {
|
|
3071
|
+
err: unknownEnvironment(config, environment),
|
|
3072
|
+
success: false
|
|
3073
|
+
};
|
|
3074
|
+
const merged = mergeOverlays(config, entry);
|
|
3075
|
+
const incompletePass = findIncompletePass(merged, environment);
|
|
3076
|
+
if (incompletePass !== void 0) return {
|
|
3077
|
+
err: incompletePass,
|
|
3078
|
+
success: false
|
|
3079
|
+
};
|
|
3080
|
+
const incompletePlace = findIncompletePlace(merged, environment);
|
|
3081
|
+
if (incompletePlace !== void 0) return {
|
|
3082
|
+
err: incompletePlace,
|
|
3083
|
+
success: false
|
|
3084
|
+
};
|
|
3085
|
+
const incompleteUniverse = findIncompleteUniverse(merged, environment);
|
|
3086
|
+
if (incompleteUniverse !== void 0) return {
|
|
3087
|
+
err: incompleteUniverse,
|
|
3088
|
+
success: false
|
|
3089
|
+
};
|
|
3090
|
+
return {
|
|
3091
|
+
data: {
|
|
3092
|
+
entry,
|
|
3093
|
+
merged
|
|
3094
|
+
},
|
|
3095
|
+
success: true
|
|
3096
|
+
};
|
|
3097
|
+
}
|
|
3098
|
+
/**
|
|
2653
3099
|
* Project a validated `Config` onto a single environment. Looks up the
|
|
2654
3100
|
* matching `environments[environment]` entry, deep-merges its resource
|
|
2655
3101
|
* overlay (`passes`, `places`, `universe`) over the root config via defu,
|
|
@@ -2727,28 +3173,80 @@ function resolveStateConfig(config, environment) {
|
|
|
2727
3173
|
* projection failed.
|
|
2728
3174
|
*/
|
|
2729
3175
|
function selectEnvironment(config, environment) {
|
|
2730
|
-
const
|
|
2731
|
-
if (
|
|
2732
|
-
|
|
2733
|
-
|
|
3176
|
+
const mergedResult = selectMergedEnvironment(config, environment);
|
|
3177
|
+
if (!mergedResult.success) return mergedResult;
|
|
3178
|
+
const { entry, merged } = mergedResult.data;
|
|
3179
|
+
return {
|
|
3180
|
+
data: redactAndPrefix({
|
|
3181
|
+
config,
|
|
3182
|
+
entry,
|
|
3183
|
+
merged
|
|
3184
|
+
}),
|
|
3185
|
+
success: true
|
|
2734
3186
|
};
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
const
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
3187
|
+
}
|
|
3188
|
+
function findIncompletePass(merged, environment) {
|
|
3189
|
+
const { passes } = merged;
|
|
3190
|
+
if (passes === void 0) return;
|
|
3191
|
+
const candidates = passes;
|
|
3192
|
+
for (const [key, entry] of Object.entries(candidates)) {
|
|
3193
|
+
if (entry.name === void 0) return {
|
|
3194
|
+
key,
|
|
3195
|
+
environment,
|
|
3196
|
+
kind: "incompletePassEntry",
|
|
3197
|
+
missingField: "name"
|
|
3198
|
+
};
|
|
3199
|
+
if (entry.description === void 0) return {
|
|
3200
|
+
key,
|
|
3201
|
+
environment,
|
|
3202
|
+
kind: "incompletePassEntry",
|
|
3203
|
+
missingField: "description"
|
|
3204
|
+
};
|
|
3205
|
+
if (entry.icon === void 0) return {
|
|
3206
|
+
key,
|
|
3207
|
+
environment,
|
|
3208
|
+
kind: "incompletePassEntry",
|
|
3209
|
+
missingField: "icon"
|
|
3210
|
+
};
|
|
3211
|
+
}
|
|
3212
|
+
}
|
|
3213
|
+
function mergeEntry(overlay, base) {
|
|
3214
|
+
return defu(overlay, base ?? {});
|
|
3215
|
+
}
|
|
3216
|
+
function mergeKeyedRecord(overlay, base) {
|
|
3217
|
+
if (overlay === void 0) return base;
|
|
3218
|
+
return {
|
|
3219
|
+
...base ?? {},
|
|
3220
|
+
...Object.fromEntries(Object.entries(overlay).map(([key, partial]) => {
|
|
3221
|
+
return [key, mergeEntry(partial, base?.[key])];
|
|
3222
|
+
}))
|
|
2743
3223
|
};
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
3224
|
+
}
|
|
3225
|
+
function mergeUniverse(overlay, base) {
|
|
3226
|
+
if (overlay === void 0 && base === void 0) return;
|
|
3227
|
+
return defu(overlay ?? {}, base ?? {});
|
|
3228
|
+
}
|
|
3229
|
+
function mergeOverlays(config, entry) {
|
|
3230
|
+
const passes = mergeKeyedRecord(entry.passes, config.passes);
|
|
3231
|
+
const places = mergeKeyedRecord(entry.places, config.places);
|
|
3232
|
+
const products = mergeKeyedRecord(entry.products, config.products);
|
|
3233
|
+
const universe = mergeUniverse(entry.universe, config.universe);
|
|
3234
|
+
const state = entry.state ?? config.state;
|
|
3235
|
+
const { places: _placesRoot, products: _productsRoot, universe: _universeRoot, ...rest } = config;
|
|
3236
|
+
return {
|
|
3237
|
+
...rest,
|
|
3238
|
+
...passes === void 0 ? {} : { passes },
|
|
3239
|
+
...places === void 0 ? {} : { places },
|
|
3240
|
+
...products === void 0 ? {} : { products },
|
|
3241
|
+
...state === void 0 ? {} : { state },
|
|
3242
|
+
...universe === void 0 ? {} : { universe }
|
|
2748
3243
|
};
|
|
3244
|
+
}
|
|
3245
|
+
function unknownEnvironment(config, environment) {
|
|
2749
3246
|
return {
|
|
2750
|
-
|
|
2751
|
-
|
|
3247
|
+
declared: Object.keys(config.environments),
|
|
3248
|
+
environment,
|
|
3249
|
+
kind: "unknownEnvironment"
|
|
2752
3250
|
};
|
|
2753
3251
|
}
|
|
2754
3252
|
function findIncompleteUniverse(projected, environment) {
|
|
@@ -2779,22 +3277,6 @@ function findIncompletePlace(projected, environment) {
|
|
|
2779
3277
|
};
|
|
2780
3278
|
}
|
|
2781
3279
|
}
|
|
2782
|
-
function mergeEntry(overlay, base) {
|
|
2783
|
-
return defu(overlay, base ?? {});
|
|
2784
|
-
}
|
|
2785
|
-
function mergeKeyedRecord(overlay, base) {
|
|
2786
|
-
if (overlay === void 0) return base;
|
|
2787
|
-
return {
|
|
2788
|
-
...base ?? {},
|
|
2789
|
-
...Object.fromEntries(Object.entries(overlay).map(([key, partial]) => {
|
|
2790
|
-
return [key, mergeEntry(partial, base?.[key])];
|
|
2791
|
-
}))
|
|
2792
|
-
};
|
|
2793
|
-
}
|
|
2794
|
-
function mergeUniverse(overlay, base) {
|
|
2795
|
-
if (overlay === void 0 && base === void 0) return;
|
|
2796
|
-
return defu(overlay ?? {}, base ?? {});
|
|
2797
|
-
}
|
|
2798
3280
|
function resolvePrefix(config, entry) {
|
|
2799
3281
|
if (config.displayNamePrefix?.enabled === false) return;
|
|
2800
3282
|
const { label } = entry;
|
|
@@ -2818,33 +3300,18 @@ function applyPlacesPrefix(places, prefix) {
|
|
|
2818
3300
|
}];
|
|
2819
3301
|
}));
|
|
2820
3302
|
}
|
|
2821
|
-
function
|
|
2822
|
-
const { config, entry } = inputs;
|
|
2823
|
-
const
|
|
2824
|
-
const mergedPlaces = mergeKeyedRecord(entry.places, config.places);
|
|
2825
|
-
const products = mergeKeyedRecord(entry.products, config.products);
|
|
2826
|
-
const merged = mergeUniverse(entry.universe, config.universe);
|
|
3303
|
+
function redactAndPrefix(inputs) {
|
|
3304
|
+
const { config, entry, merged } = inputs;
|
|
3305
|
+
const redacted = applyRedaction(merged, entry.redacted);
|
|
2827
3306
|
const prefix = resolvePrefix(config, entry);
|
|
2828
|
-
const
|
|
2829
|
-
const
|
|
2830
|
-
const state = entry.state ?? config.state;
|
|
2831
|
-
const { places: _placesRoot, products: _productsRoot, universe: _universeRoot, ...rest } = config;
|
|
3307
|
+
const places = applyPlacesPrefix(redacted.places, prefix);
|
|
3308
|
+
const universe = applyUniversePrefix(redacted.universe, prefix);
|
|
2832
3309
|
return {
|
|
2833
|
-
...
|
|
2834
|
-
...passes === void 0 ? {} : { passes },
|
|
3310
|
+
...redacted,
|
|
2835
3311
|
...places === void 0 ? {} : { places },
|
|
2836
|
-
...products === void 0 ? {} : { products },
|
|
2837
|
-
...state === void 0 ? {} : { state },
|
|
2838
3312
|
...universe === void 0 ? {} : { universe }
|
|
2839
3313
|
};
|
|
2840
3314
|
}
|
|
2841
|
-
function unknownEnvironment(config, environment) {
|
|
2842
|
-
return {
|
|
2843
|
-
declared: Object.keys(config.environments),
|
|
2844
|
-
environment,
|
|
2845
|
-
kind: "unknownEnvironment"
|
|
2846
|
-
};
|
|
2847
|
-
}
|
|
2848
3315
|
//#endregion
|
|
2849
3316
|
//#region src/core/validate-plan.ts
|
|
2850
3317
|
/**
|
|
@@ -4462,6 +4929,14 @@ function buildRootPasses(primaryFold) {
|
|
|
4462
4929
|
if (primaryFold.passes.length === 0) return;
|
|
4463
4930
|
return Object.fromEntries(primaryFold.passes.map(({ key, entry }) => [key, entry]));
|
|
4464
4931
|
}
|
|
4932
|
+
function buildFullPassOverlay(entry) {
|
|
4933
|
+
return {
|
|
4934
|
+
name: entry.name,
|
|
4935
|
+
description: entry.description,
|
|
4936
|
+
icon: entry.icon,
|
|
4937
|
+
...entry.price !== void 0 && { price: entry.price }
|
|
4938
|
+
};
|
|
4939
|
+
}
|
|
4465
4940
|
function buildPassOverlayEntry(entry, primary) {
|
|
4466
4941
|
const overlay = {};
|
|
4467
4942
|
if (!Object.is(primary.name, entry.name)) overlay.name = entry.name;
|
|
@@ -4475,7 +4950,7 @@ function buildPassesOverlay(fold, primary) {
|
|
|
4475
4950
|
const overlay = {};
|
|
4476
4951
|
for (const { key, entry } of fold.passes) {
|
|
4477
4952
|
const primaryEntry = primaryByKey.get(key);
|
|
4478
|
-
const passOverlay = primaryEntry === void 0 ?
|
|
4953
|
+
const passOverlay = primaryEntry === void 0 ? buildFullPassOverlay(entry) : buildPassOverlayEntry(entry, primaryEntry);
|
|
4479
4954
|
if (passOverlay !== void 0) overlay[key] = passOverlay;
|
|
4480
4955
|
}
|
|
4481
4956
|
return Object.keys(overlay).length === 0 ? void 0 : overlay;
|
|
@@ -6038,6 +6513,6 @@ function isFileMissing(err) {
|
|
|
6038
6513
|
return typeof err === "object" && err !== null && "code" in err && typeof err.code === "string" && FILE_MISSING_CODES.has(err.code);
|
|
6039
6514
|
}
|
|
6040
6515
|
//#endregion
|
|
6041
|
-
export {
|
|
6516
|
+
export { createGamePassDriver as A, createClackProgressAdapter as B, createPlaceDriver as C, parseStateFile as D, createGistStateAdapter as E, asSha256Hex as F, renderMigrationSummary as G, renderDeployError as H, isResourceKey as I, renderParseError as K, isRobloxAssetId as L, shouldReuploadIcon as M, asResourceKey as N, serializeStateFile as O, asRobloxAssetId as P, isSha256Hex as R, createUniverseDriver as S, UNIVERSE_SINGLETON_KEY as T, renderMigrateError as U, renderBuildStatePortError as V, renderMigrateParseError as W, diff as _, buildStatePort as a, validateConfig as b, applyOps as c, selectMergedEnvironment as d, collectRedactionAnnotations as f, renderDisplayNamePrefix as g, DEFAULT_PREFIX_FORMAT as h, loadConfig$1 as i, createDeveloperProductDriver as j, validateEnvironmentName as k, validatePlan as l, flattenConfig as m, serializeConfig as n, buildDesired as o, resolveStateConfig as p, renderStateWriteError as q, deploy as r, buildDefaultRegistry as s, migrateMantleState as t, selectEnvironment as u, defaultKindRegistry as v, SOCIAL_LINK_FIELDS as w, createClackPort as x, isGistStateConfig as y, derivePriceFields as z };
|
|
6042
6517
|
|
|
6043
|
-
//# sourceMappingURL=migrate-mantle-state-
|
|
6518
|
+
//# sourceMappingURL=migrate-mantle-state-CQjWBZwT.mjs.map
|