@bedrock-rbx/core 0.1.0-beta.1 → 0.1.0-beta.3
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 +22 -14
- package/dist/cli/run.mjs.map +1 -1
- package/dist/config.d.mts +1 -1
- package/dist/{define-config-D-LAhfSJ.d.mts → define-config-87u2jqjM.d.mts} +8 -16
- package/dist/{define-config-D-LAhfSJ.d.mts.map → define-config-87u2jqjM.d.mts.map} +1 -1
- package/dist/index.d.mts +10 -33
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{migrate-mantle-state-DqbJ1TLq.mjs → migrate-mantle-state-_7Tkn0hG.mjs} +256 -344
- package/dist/migrate-mantle-state-_7Tkn0hG.mjs.map +1 -0
- package/package.json +4 -4
- package/dist/migrate-mantle-state-DqbJ1TLq.mjs.map +0 -1
|
@@ -8,10 +8,10 @@ import { PlacesClient } from "@bedrock-rbx/ocale/places";
|
|
|
8
8
|
import { UniversesClient } from "@bedrock-rbx/ocale/universes";
|
|
9
9
|
import { readFile } from "node:fs/promises";
|
|
10
10
|
import { loadConfig } from "c12";
|
|
11
|
-
import { execFile } from "node:child_process";
|
|
12
11
|
import { existsSync, mkdtempSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
13
|
-
import { tmpdir } from "node:os";
|
|
14
12
|
import { basename, dirname, isAbsolute, join, resolve } from "node:path";
|
|
13
|
+
import { execFile } from "node:child_process";
|
|
14
|
+
import { tmpdir } from "node:os";
|
|
15
15
|
import { parseYAML, stringifyYAML } from "confbox";
|
|
16
16
|
//#region src/core/derive-price-fields.ts
|
|
17
17
|
/**
|
|
@@ -1357,7 +1357,6 @@ async function publishPlace(deps, desired) {
|
|
|
1357
1357
|
* httpClient: universeBodyHttpClient,
|
|
1358
1358
|
* sleep: async () => {},
|
|
1359
1359
|
* }),
|
|
1360
|
-
* readFile: async () => new Uint8Array(),
|
|
1361
1360
|
* universes: new UniversesClient({
|
|
1362
1361
|
* apiKey: "rbx-your-key",
|
|
1363
1362
|
* httpClient: universeBodyHttpClient,
|
|
@@ -1391,29 +1390,22 @@ function createUniverseDriver(deps) {
|
|
|
1391
1390
|
return {
|
|
1392
1391
|
async create(desired) {
|
|
1393
1392
|
return reconcileUniverse({
|
|
1394
|
-
current: void 0,
|
|
1395
1393
|
deps,
|
|
1396
1394
|
desired
|
|
1397
1395
|
});
|
|
1398
1396
|
},
|
|
1399
|
-
async update(
|
|
1397
|
+
async update(_current, desired) {
|
|
1400
1398
|
return reconcileUniverse({
|
|
1401
|
-
current,
|
|
1402
1399
|
deps,
|
|
1403
1400
|
desired
|
|
1404
1401
|
});
|
|
1405
1402
|
}
|
|
1406
1403
|
};
|
|
1407
1404
|
}
|
|
1408
|
-
function toCurrentState(
|
|
1409
|
-
const { desired, iconAssetIds, rootPlaceId } = inputs;
|
|
1410
|
-
const baseOutputs = { rootPlaceId: asRobloxAssetId(rootPlaceId) };
|
|
1405
|
+
function toCurrentState(desired, rootPlaceId) {
|
|
1411
1406
|
return {
|
|
1412
1407
|
...desired,
|
|
1413
|
-
outputs:
|
|
1414
|
-
...baseOutputs,
|
|
1415
|
-
iconAssetIds
|
|
1416
|
-
}
|
|
1408
|
+
outputs: { rootPlaceId: asRobloxAssetId(rootPlaceId) }
|
|
1417
1409
|
};
|
|
1418
1410
|
}
|
|
1419
1411
|
function buildParameters(desired) {
|
|
@@ -1457,46 +1449,8 @@ async function resolveUniverse(deps, desired) {
|
|
|
1457
1449
|
success: true
|
|
1458
1450
|
};
|
|
1459
1451
|
}
|
|
1460
|
-
async function captureUploadedIconAssetId(deps, desired) {
|
|
1461
|
-
const listed = await deps.universes.icon.list({ universeId: desired.universeId });
|
|
1462
|
-
if (!listed.success) return listed;
|
|
1463
|
-
const enUs = listed.data.find((entry) => entry.languageCode === "en-us");
|
|
1464
|
-
if (enUs === void 0) return {
|
|
1465
|
-
err: new ApiError(`Malformed experience-icon list for ${desired.universeId}: en-us entry missing after upload`, { statusCode: 200 }),
|
|
1466
|
-
success: false
|
|
1467
|
-
};
|
|
1468
|
-
return {
|
|
1469
|
-
data: { "en-us": asRobloxAssetId(enUs.imageId) },
|
|
1470
|
-
success: true
|
|
1471
|
-
};
|
|
1472
|
-
}
|
|
1473
|
-
async function deleteRemovedIcon(deps, desired) {
|
|
1474
|
-
return deps.universes.icon.delete({
|
|
1475
|
-
languageCode: "en-us",
|
|
1476
|
-
universeId: desired.universeId
|
|
1477
|
-
});
|
|
1478
|
-
}
|
|
1479
|
-
async function reconcileIcon(inputs) {
|
|
1480
|
-
const { current, deps, desired } = inputs;
|
|
1481
|
-
if (desired.icon === void 0) return current?.icon === void 0 ? {
|
|
1482
|
-
data: void 0,
|
|
1483
|
-
success: true
|
|
1484
|
-
} : deleteRemovedIcon(deps, desired);
|
|
1485
|
-
if (!shouldReuploadIcon(current?.iconFileHashes, desired.iconFileHashes)) return {
|
|
1486
|
-
data: current?.outputs.iconAssetIds,
|
|
1487
|
-
success: true
|
|
1488
|
-
};
|
|
1489
|
-
const bytes = await deps.readFile(desired.icon["en-us"]);
|
|
1490
|
-
const uploaded = await deps.universes.icon.upload({
|
|
1491
|
-
image: bytes,
|
|
1492
|
-
languageCode: "en-us",
|
|
1493
|
-
universeId: desired.universeId
|
|
1494
|
-
});
|
|
1495
|
-
if (!uploaded.success) return uploaded;
|
|
1496
|
-
return captureUploadedIconAssetId(deps, desired);
|
|
1497
|
-
}
|
|
1498
1452
|
async function reconcileUniverse(inputs) {
|
|
1499
|
-
const {
|
|
1453
|
+
const { deps, desired } = inputs;
|
|
1500
1454
|
const universeResult = await resolveUniverse(deps, desired);
|
|
1501
1455
|
if (!universeResult.success) return universeResult;
|
|
1502
1456
|
const { rootPlaceId } = universeResult.data;
|
|
@@ -1511,18 +1465,8 @@ async function reconcileUniverse(inputs) {
|
|
|
1511
1465
|
success: false
|
|
1512
1466
|
};
|
|
1513
1467
|
}
|
|
1514
|
-
const iconResult = await reconcileIcon({
|
|
1515
|
-
current,
|
|
1516
|
-
deps,
|
|
1517
|
-
desired
|
|
1518
|
-
});
|
|
1519
|
-
if (!iconResult.success) return iconResult;
|
|
1520
1468
|
return {
|
|
1521
|
-
data: toCurrentState(
|
|
1522
|
-
desired,
|
|
1523
|
-
iconAssetIds: iconResult.data,
|
|
1524
|
-
rootPlaceId
|
|
1525
|
-
}),
|
|
1469
|
+
data: toCurrentState(desired, rootPlaceId),
|
|
1526
1470
|
success: true
|
|
1527
1471
|
};
|
|
1528
1472
|
}
|
|
@@ -1656,7 +1600,6 @@ const universeEntry = type({
|
|
|
1656
1600
|
"displayName?": OPTIONAL_STRING,
|
|
1657
1601
|
"facebookSocialLink?": socialLinkOrUndefined$1,
|
|
1658
1602
|
"guildedSocialLink?": socialLinkOrUndefined$1,
|
|
1659
|
-
"icon?": iconMap,
|
|
1660
1603
|
"mobileEnabled?": OPTIONAL_BOOLEAN$2,
|
|
1661
1604
|
"privateServerPriceRobux?": OPTIONAL_ROBUX_PRICE,
|
|
1662
1605
|
"robloxGroupSocialLink?": socialLinkOrUndefined$1,
|
|
@@ -2004,7 +1947,6 @@ const entrySchema = type({
|
|
|
2004
1947
|
"displayName?": "string | undefined",
|
|
2005
1948
|
"facebookSocialLink?": socialLinkOrUndefined,
|
|
2006
1949
|
"guildedSocialLink?": socialLinkOrUndefined,
|
|
2007
|
-
"icon?": iconMap,
|
|
2008
1950
|
"mobileEnabled?": OPTIONAL_BOOLEAN,
|
|
2009
1951
|
"privateServerPriceRobux?": "number.integer >= 0 | undefined",
|
|
2010
1952
|
"robloxGroupSocialLink?": socialLinkOrUndefined,
|
|
@@ -2032,14 +1974,10 @@ function flatten(config) {
|
|
|
2032
1974
|
vrEnabled: entry.vrEnabled,
|
|
2033
1975
|
...copyDeclaredSocialLinks(entry)
|
|
2034
1976
|
};
|
|
2035
|
-
|
|
1977
|
+
return ["privateServerPriceRobux" in entry ? {
|
|
2036
1978
|
...base,
|
|
2037
1979
|
privateServerPriceRobux: entry.privateServerPriceRobux
|
|
2038
|
-
} : base;
|
|
2039
|
-
return [entry.icon === void 0 ? withPrice : {
|
|
2040
|
-
...withPrice,
|
|
2041
|
-
icon: entry.icon
|
|
2042
|
-
}];
|
|
1980
|
+
} : base];
|
|
2043
1981
|
}
|
|
2044
1982
|
function buildBaseDesired(input) {
|
|
2045
1983
|
const base = {
|
|
@@ -2060,23 +1998,9 @@ function buildBaseDesired(input) {
|
|
|
2060
1998
|
privateServerPriceRobux: input.privateServerPriceRobux
|
|
2061
1999
|
} : base;
|
|
2062
2000
|
}
|
|
2063
|
-
async function normalize(input,
|
|
2064
|
-
const withPrice = buildBaseDesired(input);
|
|
2065
|
-
if (input.icon === void 0) return {
|
|
2066
|
-
data: withPrice,
|
|
2067
|
-
success: true
|
|
2068
|
-
};
|
|
2069
|
-
const hashes = await hashIconLocales({
|
|
2070
|
-
key: input.key,
|
|
2071
|
-
icon: input.icon
|
|
2072
|
-
}, io);
|
|
2073
|
-
if (!hashes.success) return hashes;
|
|
2001
|
+
async function normalize(input, _io) {
|
|
2074
2002
|
return {
|
|
2075
|
-
data:
|
|
2076
|
-
...withPrice,
|
|
2077
|
-
icon: input.icon,
|
|
2078
|
-
iconFileHashes: hashes.data
|
|
2079
|
-
},
|
|
2003
|
+
data: buildBaseDesired(input),
|
|
2080
2004
|
success: true
|
|
2081
2005
|
};
|
|
2082
2006
|
}
|
|
@@ -2100,7 +2024,6 @@ function fieldsEqual(desired, current) {
|
|
|
2100
2024
|
}
|
|
2101
2025
|
if (desired.displayName !== void 0 && desired.displayName !== current.displayName) return false;
|
|
2102
2026
|
if ("privateServerPriceRobux" in desired && desired.privateServerPriceRobux !== current.privateServerPriceRobux) return false;
|
|
2103
|
-
if (!iconHashesEqual(current.iconFileHashes, desired.iconFileHashes)) return false;
|
|
2104
2027
|
return declaredSocialLinksEqual(desired, current);
|
|
2105
2028
|
}
|
|
2106
2029
|
//#endregion
|
|
@@ -2832,7 +2755,7 @@ async function dispatchOp(op, registry) {
|
|
|
2832
2755
|
//#region src/shell/build-default-registry.ts
|
|
2833
2756
|
/**
|
|
2834
2757
|
* Construct the default `DriverRegistry` from `config.universe.universeId`
|
|
2835
|
-
* and `
|
|
2758
|
+
* and `BEDROCK_API_KEY`. Reads the API key via the injected `getEnv` seam
|
|
2836
2759
|
* and surfaces `missingCredential` or `registryConfigMissing` as typed
|
|
2837
2760
|
* Results instead of throwing.
|
|
2838
2761
|
*
|
|
@@ -2859,7 +2782,7 @@ async function dispatchOp(op, registry) {
|
|
|
2859
2782
|
* missing API key or the missing universe declaration.
|
|
2860
2783
|
*/
|
|
2861
2784
|
function buildDefaultRegistry(deps) {
|
|
2862
|
-
const apiKey = deps.getEnv("
|
|
2785
|
+
const apiKey = deps.getEnv("BEDROCK_API_KEY");
|
|
2863
2786
|
if (apiKey === void 0) return missingApiKey();
|
|
2864
2787
|
const rawUniverseId = deps.config.universe?.universeId;
|
|
2865
2788
|
if (rawUniverseId === void 0) return missingUniverseId();
|
|
@@ -2877,7 +2800,7 @@ function missingApiKey() {
|
|
|
2877
2800
|
err: {
|
|
2878
2801
|
kind: "missingCredential",
|
|
2879
2802
|
purpose: "registry",
|
|
2880
|
-
variable: "
|
|
2803
|
+
variable: "BEDROCK_API_KEY"
|
|
2881
2804
|
},
|
|
2882
2805
|
success: false
|
|
2883
2806
|
};
|
|
@@ -2916,7 +2839,6 @@ function assembleRegistry(inputs) {
|
|
|
2916
2839
|
}),
|
|
2917
2840
|
universe: createUniverseDriver({
|
|
2918
2841
|
places,
|
|
2919
|
-
readFile,
|
|
2920
2842
|
universes
|
|
2921
2843
|
})
|
|
2922
2844
|
};
|
|
@@ -3057,8 +2979,169 @@ function bootstrapDirectoryPrefix(pid) {
|
|
|
3057
2979
|
return `${LUAU_BOOTSTRAP_TEMP_PREFIX}${pid}-`;
|
|
3058
2980
|
}
|
|
3059
2981
|
//#endregion
|
|
2982
|
+
//#region src/adapters/lute-luau-evaluator.ts
|
|
2983
|
+
const SENTINEL_BASE = "__BEDROCK_LUAU_";
|
|
2984
|
+
const OK_PREFIX = `${SENTINEL_BASE}OK__`;
|
|
2985
|
+
const ERR_PREFIX = `${SENTINEL_BASE}ERR__`;
|
|
2986
|
+
const LUTE_BOOTSTRAP_LUAU = `--!strict
|
|
2987
|
+
local json = require("@std/json")
|
|
2988
|
+
local process = require("@std/process")
|
|
2989
|
+
local io = require("@std/io")
|
|
2990
|
+
|
|
2991
|
+
local function emit(kind, payload)
|
|
2992
|
+
io.write("${SENTINEL_BASE}" .. kind .. "__")
|
|
2993
|
+
io.write(json.serialize(payload))
|
|
2994
|
+
end
|
|
2995
|
+
|
|
2996
|
+
local userBasename = process.args[2]
|
|
2997
|
+
-- The user file lives in a different directory from this bootstrap, so we
|
|
2998
|
+
-- require it via the @user alias defined in the .luaurc written alongside.
|
|
2999
|
+
local req = "@user/" .. string.gsub(userBasename, "%.luau$", "")
|
|
3000
|
+
|
|
3001
|
+
local loadOk, modOrErr = pcall(require, req)
|
|
3002
|
+
if not loadOk then
|
|
3003
|
+
emit("ERR", { kind = "loadFailed", message = tostring(modOrErr) })
|
|
3004
|
+
return
|
|
3005
|
+
end
|
|
3006
|
+
|
|
3007
|
+
local value = if type(modOrErr) == "function" then modOrErr() else modOrErr
|
|
3008
|
+
|
|
3009
|
+
local encOk, encoded = pcall(json.serialize, value)
|
|
3010
|
+
if not encOk then
|
|
3011
|
+
emit("ERR", { kind = "serializeFailed", message = tostring(encoded) })
|
|
3012
|
+
return
|
|
3013
|
+
end
|
|
3014
|
+
|
|
3015
|
+
io.write("${OK_PREFIX}")
|
|
3016
|
+
io.write(encoded)
|
|
3017
|
+
`;
|
|
3018
|
+
const LUAU_RUNTIME_HINT = "install lute (e.g. `mise install` with `github:luau-lang/lute`) or set BEDROCK_LUTE_PATH to the binary.";
|
|
3019
|
+
function isEnoentError(error) {
|
|
3020
|
+
return error instanceof Error && "code" in error && error.code === "ENOENT";
|
|
3021
|
+
}
|
|
3022
|
+
const LUTE_BOOTSTRAP_TIMEOUT_MS = 5e3;
|
|
3023
|
+
/**
|
|
3024
|
+
* Build the default `LuauEvaluator` adapter that shells out to the `lute`
|
|
3025
|
+
* runtime. Reads `BEDROCK_LUTE_PATH` from `process.env` once per call to pick
|
|
3026
|
+
* the binary, so tests can override it via env var without rebuilding the
|
|
3027
|
+
* adapter.
|
|
3028
|
+
* @returns A `LuauEvaluator` that spawns `lute run` per call.
|
|
3029
|
+
*/
|
|
3030
|
+
function createLuteLuauEvaluator() {
|
|
3031
|
+
return evaluateLuauWithLute;
|
|
3032
|
+
}
|
|
3033
|
+
function setupBootstrapDirectory(userCwd) {
|
|
3034
|
+
const bootstrapDirectory = mkdtempSync(join(tmpdir(), bootstrapDirectoryPrefix(process.pid)));
|
|
3035
|
+
writeFileSync(join(bootstrapDirectory, "bootstrap.luau"), LUTE_BOOTSTRAP_LUAU);
|
|
3036
|
+
writeFileSync(join(bootstrapDirectory, ".luaurc"), JSON.stringify({ aliases: { user: userCwd } }));
|
|
3037
|
+
return bootstrapDirectory;
|
|
3038
|
+
}
|
|
3039
|
+
async function runLuteBootstrap(runOptions) {
|
|
3040
|
+
const { bin, bootstrapPath, userBasename } = runOptions;
|
|
3041
|
+
return new Promise((resolve, reject) => {
|
|
3042
|
+
execFile(bin, [
|
|
3043
|
+
"run",
|
|
3044
|
+
bootstrapPath,
|
|
3045
|
+
userBasename
|
|
3046
|
+
], {
|
|
3047
|
+
encoding: "utf8",
|
|
3048
|
+
timeout: LUTE_BOOTSTRAP_TIMEOUT_MS
|
|
3049
|
+
}, (error, stdout) => {
|
|
3050
|
+
if (error instanceof Error) {
|
|
3051
|
+
reject(error);
|
|
3052
|
+
return;
|
|
3053
|
+
}
|
|
3054
|
+
resolve(stdout);
|
|
3055
|
+
});
|
|
3056
|
+
});
|
|
3057
|
+
}
|
|
3058
|
+
function parseBootstrapOutput(stdout) {
|
|
3059
|
+
if (stdout.startsWith(ERR_PREFIX)) throw new Error(stdout.slice(ERR_PREFIX.length));
|
|
3060
|
+
const parsed = JSON.parse(stdout.slice(OK_PREFIX.length));
|
|
3061
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) throw new TypeError("Luau config must return a table at the root");
|
|
3062
|
+
return parsed;
|
|
3063
|
+
}
|
|
3064
|
+
async function evaluateLuauWithLute(absPath) {
|
|
3065
|
+
const overridePath = process.env["BEDROCK_LUTE_PATH"];
|
|
3066
|
+
const lute = overridePath !== void 0 && overridePath.length > 0 ? overridePath : "lute";
|
|
3067
|
+
const bootstrapDirectory = setupBootstrapDirectory(dirname(absPath));
|
|
3068
|
+
try {
|
|
3069
|
+
return {
|
|
3070
|
+
data: parseBootstrapOutput(await runLuteBootstrap({
|
|
3071
|
+
bin: lute,
|
|
3072
|
+
bootstrapPath: join(bootstrapDirectory, "bootstrap.luau"),
|
|
3073
|
+
userBasename: basename(absPath)
|
|
3074
|
+
})),
|
|
3075
|
+
success: true
|
|
3076
|
+
};
|
|
3077
|
+
} catch (err) {
|
|
3078
|
+
if (isEnoentError(err)) return {
|
|
3079
|
+
err: {
|
|
3080
|
+
hint: LUAU_RUNTIME_HINT,
|
|
3081
|
+
kind: "missingRuntime"
|
|
3082
|
+
},
|
|
3083
|
+
success: false
|
|
3084
|
+
};
|
|
3085
|
+
return {
|
|
3086
|
+
err: {
|
|
3087
|
+
kind: "evaluationFailed",
|
|
3088
|
+
message: err instanceof Error ? err.message : String(err)
|
|
3089
|
+
},
|
|
3090
|
+
success: false
|
|
3091
|
+
};
|
|
3092
|
+
} finally {
|
|
3093
|
+
rmSync(bootstrapDirectory, { recursive: true });
|
|
3094
|
+
}
|
|
3095
|
+
}
|
|
3096
|
+
//#endregion
|
|
3060
3097
|
//#region src/shell/load-config.ts
|
|
3061
3098
|
/**
|
|
3099
|
+
* Internal entrypoint that lets tests inject a fake `LuauEvaluator`. The
|
|
3100
|
+
* public {@link loadConfig} wraps this with the real lute adapter; the rest
|
|
3101
|
+
* of the loader pipeline is identical.
|
|
3102
|
+
*
|
|
3103
|
+
* @param deps - Injected dependencies. Only the evaluator is configurable.
|
|
3104
|
+
* @param options - Same loader options accepted by {@link loadConfig}.
|
|
3105
|
+
* @returns Same `Result<Config, ConfigError>` shape as `loadConfig`.
|
|
3106
|
+
*/
|
|
3107
|
+
async function loadConfigWith(deps, options) {
|
|
3108
|
+
const cwd = options?.cwd ?? process.cwd();
|
|
3109
|
+
const configFile = options?.configFile === void 0 ? void 0 : resolveConfigPath(cwd, options.configFile);
|
|
3110
|
+
if (configFile !== void 0 && !isExistingFile(configFile)) return {
|
|
3111
|
+
err: {
|
|
3112
|
+
kind: "fileNotFound",
|
|
3113
|
+
searchedFrom: cwd
|
|
3114
|
+
},
|
|
3115
|
+
success: false
|
|
3116
|
+
};
|
|
3117
|
+
let resolved;
|
|
3118
|
+
try {
|
|
3119
|
+
resolved = await loadConfig({
|
|
3120
|
+
name: "bedrock",
|
|
3121
|
+
cwd,
|
|
3122
|
+
resolve: makeLuauResolver({
|
|
3123
|
+
callerConfigFile: configFile,
|
|
3124
|
+
defaultCwd: cwd,
|
|
3125
|
+
evaluator: deps.evaluator
|
|
3126
|
+
}),
|
|
3127
|
+
...configFile === void 0 ? {} : { configFile }
|
|
3128
|
+
});
|
|
3129
|
+
} catch (err) {
|
|
3130
|
+
return {
|
|
3131
|
+
err: attributeLoadError(err, cwd),
|
|
3132
|
+
success: false
|
|
3133
|
+
};
|
|
3134
|
+
}
|
|
3135
|
+
if (resolved._configFile === void 0) return {
|
|
3136
|
+
err: {
|
|
3137
|
+
kind: "fileNotFound",
|
|
3138
|
+
searchedFrom: cwd
|
|
3139
|
+
},
|
|
3140
|
+
success: false
|
|
3141
|
+
};
|
|
3142
|
+
return validateConfig(resolved.config, resolved._configFile);
|
|
3143
|
+
}
|
|
3144
|
+
/**
|
|
3062
3145
|
* Discover, parse, and validate the project config.
|
|
3063
3146
|
*
|
|
3064
3147
|
* Looks for `bedrock.config.{ts,js,mjs,cjs,yaml,yml,json}`, `.bedrockrc*`,
|
|
@@ -3098,37 +3181,7 @@ function bootstrapDirectoryPrefix(pid) {
|
|
|
3098
3181
|
* ```
|
|
3099
3182
|
*/
|
|
3100
3183
|
async function loadConfig$1(options) {
|
|
3101
|
-
|
|
3102
|
-
const configFile = options?.configFile === void 0 ? void 0 : resolveConfigPath(cwd, options.configFile);
|
|
3103
|
-
if (configFile !== void 0 && !isExistingFile(configFile)) return {
|
|
3104
|
-
err: {
|
|
3105
|
-
kind: "fileNotFound",
|
|
3106
|
-
searchedFrom: cwd
|
|
3107
|
-
},
|
|
3108
|
-
success: false
|
|
3109
|
-
};
|
|
3110
|
-
let resolved;
|
|
3111
|
-
try {
|
|
3112
|
-
resolved = await loadConfig({
|
|
3113
|
-
name: "bedrock",
|
|
3114
|
-
cwd,
|
|
3115
|
-
resolve: makeLuauResolver(cwd, configFile),
|
|
3116
|
-
...configFile === void 0 ? {} : { configFile }
|
|
3117
|
-
});
|
|
3118
|
-
} catch (err) {
|
|
3119
|
-
return {
|
|
3120
|
-
err: attributeLoadError(err, cwd),
|
|
3121
|
-
success: false
|
|
3122
|
-
};
|
|
3123
|
-
}
|
|
3124
|
-
if (resolved._configFile === void 0) return {
|
|
3125
|
-
err: {
|
|
3126
|
-
kind: "fileNotFound",
|
|
3127
|
-
searchedFrom: cwd
|
|
3128
|
-
},
|
|
3129
|
-
success: false
|
|
3130
|
-
};
|
|
3131
|
-
return validateConfig(resolved.config, resolved._configFile);
|
|
3184
|
+
return loadConfigWith({ evaluator: createLuteLuauEvaluator() }, options);
|
|
3132
3185
|
}
|
|
3133
3186
|
function resolveConfigPath(cwd, configFile) {
|
|
3134
3187
|
return isAbsolute(configFile) ? configFile : join(cwd, configFile);
|
|
@@ -3181,6 +3234,19 @@ const NATIVE_CONFIG_EXTENSIONS = [
|
|
|
3181
3234
|
"yml"
|
|
3182
3235
|
];
|
|
3183
3236
|
/**
|
|
3237
|
+
* Internal-only wrapper used at the c12 boundary: makeLuauResolver maps an
|
|
3238
|
+
* evaluator `Err` into this throwable, which `attributeLoadError` unwraps
|
|
3239
|
+
* directly. This keeps the port on the `Result` contract per ADR-009 while
|
|
3240
|
+
* still satisfying c12's exception-based `resolve` callback.
|
|
3241
|
+
*/
|
|
3242
|
+
var EvaluatorThrow = class extends Error {
|
|
3243
|
+
configError;
|
|
3244
|
+
constructor(configError) {
|
|
3245
|
+
super();
|
|
3246
|
+
this.configError = configError;
|
|
3247
|
+
}
|
|
3248
|
+
};
|
|
3249
|
+
/**
|
|
3184
3250
|
* Decide which Luau file the resolver should evaluate for a given c12 source,
|
|
3185
3251
|
* or `undefined` to defer to c12's built-in loaders.
|
|
3186
3252
|
*
|
|
@@ -3199,120 +3265,37 @@ function pickLuauTarget(source, context) {
|
|
|
3199
3265
|
if (source === "." && callerConfigFile !== void 0) return callerConfigFile.endsWith(".luau") ? callerConfigFile : void 0;
|
|
3200
3266
|
return locateLuauConfig(source, cwd);
|
|
3201
3267
|
}
|
|
3202
|
-
function
|
|
3268
|
+
function evaluationErrorToConfigError(err, sourceFile) {
|
|
3269
|
+
if (err.kind === "missingRuntime") return {
|
|
3270
|
+
hint: err.hint,
|
|
3271
|
+
kind: "luauRuntimeMissing",
|
|
3272
|
+
sourceFile
|
|
3273
|
+
};
|
|
3274
|
+
return {
|
|
3275
|
+
kind: "parseFailed",
|
|
3276
|
+
message: err.message,
|
|
3277
|
+
sourceFile
|
|
3278
|
+
};
|
|
3279
|
+
}
|
|
3280
|
+
function makeLuauResolver(deps) {
|
|
3203
3281
|
return async (source, c12Options) => {
|
|
3204
|
-
const cwd = c12Options.cwd ?? defaultCwd;
|
|
3282
|
+
const cwd = c12Options.cwd ?? deps.defaultCwd;
|
|
3205
3283
|
const luauPath = pickLuauTarget(source, {
|
|
3206
|
-
callerConfigFile,
|
|
3284
|
+
callerConfigFile: deps.callerConfigFile,
|
|
3207
3285
|
cwd
|
|
3208
3286
|
});
|
|
3209
3287
|
if (luauPath === void 0) return;
|
|
3288
|
+
const result = await deps.evaluator(luauPath);
|
|
3289
|
+
if (!result.success) throw new EvaluatorThrow(evaluationErrorToConfigError(result.err, luauPath));
|
|
3210
3290
|
return {
|
|
3211
3291
|
_configFile: luauPath,
|
|
3212
|
-
config:
|
|
3292
|
+
config: result.data,
|
|
3213
3293
|
configFile: luauPath,
|
|
3214
3294
|
cwd
|
|
3215
3295
|
};
|
|
3216
3296
|
};
|
|
3217
3297
|
}
|
|
3218
3298
|
const LUAU_CONFIG_BASENAME = "bedrock.config.luau";
|
|
3219
|
-
const SENTINEL_BASE = "__BEDROCK_LUAU_";
|
|
3220
|
-
const OK_PREFIX = `${SENTINEL_BASE}OK__`;
|
|
3221
|
-
const ERR_PREFIX = `${SENTINEL_BASE}ERR__`;
|
|
3222
|
-
const LUTE_BOOTSTRAP_LUAU = `--!strict
|
|
3223
|
-
local json = require("@std/json")
|
|
3224
|
-
local process = require("@std/process")
|
|
3225
|
-
local io = require("@std/io")
|
|
3226
|
-
|
|
3227
|
-
local function emit(kind, payload)
|
|
3228
|
-
io.write("${SENTINEL_BASE}" .. kind .. "__")
|
|
3229
|
-
io.write(json.serialize(payload))
|
|
3230
|
-
end
|
|
3231
|
-
|
|
3232
|
-
local userBasename = process.args[2]
|
|
3233
|
-
-- The user file lives in a different directory from this bootstrap, so we
|
|
3234
|
-
-- require it via the @user alias defined in the .luaurc written alongside.
|
|
3235
|
-
local req = "@user/" .. string.gsub(userBasename, "%.luau$", "")
|
|
3236
|
-
|
|
3237
|
-
local loadOk, modOrErr = pcall(require, req)
|
|
3238
|
-
if not loadOk then
|
|
3239
|
-
emit("ERR", { kind = "loadFailed", message = tostring(modOrErr) })
|
|
3240
|
-
return
|
|
3241
|
-
end
|
|
3242
|
-
|
|
3243
|
-
local value = if type(modOrErr) == "function" then modOrErr() else modOrErr
|
|
3244
|
-
|
|
3245
|
-
local encOk, encoded = pcall(json.serialize, value)
|
|
3246
|
-
if not encOk then
|
|
3247
|
-
emit("ERR", { kind = "serializeFailed", message = tostring(encoded) })
|
|
3248
|
-
return
|
|
3249
|
-
end
|
|
3250
|
-
|
|
3251
|
-
io.write("${OK_PREFIX}")
|
|
3252
|
-
io.write(encoded)
|
|
3253
|
-
`;
|
|
3254
|
-
var LuauRuntimeMissingError = class extends Error {
|
|
3255
|
-
hint;
|
|
3256
|
-
name = "LuauRuntimeMissingError";
|
|
3257
|
-
sourceFile;
|
|
3258
|
-
constructor(sourceFile, hint) {
|
|
3259
|
-
super();
|
|
3260
|
-
this.hint = hint;
|
|
3261
|
-
this.sourceFile = sourceFile;
|
|
3262
|
-
}
|
|
3263
|
-
};
|
|
3264
|
-
const LUAU_RUNTIME_HINT = "install lute (e.g. `mise install` with `github:luau-lang/lute`) or set BEDROCK_LUTE_PATH to the binary.";
|
|
3265
|
-
function isEnoentError(error) {
|
|
3266
|
-
return error instanceof Error && "code" in error && error.code === "ENOENT";
|
|
3267
|
-
}
|
|
3268
|
-
const LUTE_BOOTSTRAP_TIMEOUT_MS = 1e4;
|
|
3269
|
-
async function runLuteBootstrap(runOptions) {
|
|
3270
|
-
const { bin, bootstrapPath, userBasename } = runOptions;
|
|
3271
|
-
return new Promise((resolve, reject) => {
|
|
3272
|
-
execFile(bin, [
|
|
3273
|
-
"run",
|
|
3274
|
-
bootstrapPath,
|
|
3275
|
-
userBasename
|
|
3276
|
-
], {
|
|
3277
|
-
encoding: "utf8",
|
|
3278
|
-
timeout: LUTE_BOOTSTRAP_TIMEOUT_MS
|
|
3279
|
-
}, (error, stdout) => {
|
|
3280
|
-
if (error instanceof Error) {
|
|
3281
|
-
reject(error);
|
|
3282
|
-
return;
|
|
3283
|
-
}
|
|
3284
|
-
resolve(stdout);
|
|
3285
|
-
});
|
|
3286
|
-
});
|
|
3287
|
-
}
|
|
3288
|
-
function parseBootstrapOutput(stdout) {
|
|
3289
|
-
if (stdout.startsWith(ERR_PREFIX)) throw new Error(stdout.slice(ERR_PREFIX.length));
|
|
3290
|
-
const parsed = JSON.parse(stdout.slice(OK_PREFIX.length));
|
|
3291
|
-
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) throw new TypeError("Luau config must return a table at the root");
|
|
3292
|
-
return parsed;
|
|
3293
|
-
}
|
|
3294
|
-
async function evaluateLuauConfig(absPath) {
|
|
3295
|
-
const overridePath = process.env["BEDROCK_LUTE_PATH"];
|
|
3296
|
-
const lute = overridePath !== void 0 && overridePath.length > 0 ? overridePath : "lute";
|
|
3297
|
-
const cwd = dirname(absPath);
|
|
3298
|
-
const base = basename(absPath);
|
|
3299
|
-
const bootstrapDirectory = mkdtempSync(join(tmpdir(), bootstrapDirectoryPrefix(process.pid)));
|
|
3300
|
-
try {
|
|
3301
|
-
const bootstrapPath = join(bootstrapDirectory, "bootstrap.luau");
|
|
3302
|
-
writeFileSync(bootstrapPath, LUTE_BOOTSTRAP_LUAU);
|
|
3303
|
-
writeFileSync(join(bootstrapDirectory, ".luaurc"), JSON.stringify({ aliases: { user: cwd } }));
|
|
3304
|
-
return parseBootstrapOutput(await runLuteBootstrap({
|
|
3305
|
-
bin: lute,
|
|
3306
|
-
bootstrapPath,
|
|
3307
|
-
userBasename: base
|
|
3308
|
-
}).catch((err) => {
|
|
3309
|
-
if (isEnoentError(err)) throw new LuauRuntimeMissingError(absPath, LUAU_RUNTIME_HINT);
|
|
3310
|
-
throw err;
|
|
3311
|
-
}));
|
|
3312
|
-
} finally {
|
|
3313
|
-
rmSync(bootstrapDirectory, { recursive: true });
|
|
3314
|
-
}
|
|
3315
|
-
}
|
|
3316
3299
|
const CONFIG_FILE_IN_FRAME = /[^\s():"']*bedrock\.config\.(?:ts|js|mjs|cjs|yaml|yml|json)/;
|
|
3317
3300
|
function extractConfigFileFromStack(err) {
|
|
3318
3301
|
if (!(err instanceof Error) || err.stack === void 0) return;
|
|
@@ -3334,11 +3317,7 @@ function discoverConfigFile(cwd) {
|
|
|
3334
3317
|
return match === void 0 ? void 0 : join(cwd, match);
|
|
3335
3318
|
}
|
|
3336
3319
|
function attributeLoadError(err, cwd) {
|
|
3337
|
-
if (err instanceof
|
|
3338
|
-
hint: err.hint,
|
|
3339
|
-
kind: "luauRuntimeMissing",
|
|
3340
|
-
sourceFile: err.sourceFile
|
|
3341
|
-
};
|
|
3320
|
+
if (err instanceof EvaluatorThrow) return err.configError;
|
|
3342
3321
|
const message = err instanceof Error ? err.message : String(err);
|
|
3343
3322
|
const frameFile = extractConfigFileFromStack(err);
|
|
3344
3323
|
if (frameFile !== void 0) return {
|
|
@@ -3357,7 +3336,7 @@ function attributeLoadError(err, cwd) {
|
|
|
3357
3336
|
/**
|
|
3358
3337
|
* Run a full reconcile end-to-end. Default-constructs missing deps from
|
|
3359
3338
|
* the project config and the environment variables `GITHUB_TOKEN` and
|
|
3360
|
-
* `
|
|
3339
|
+
* `BEDROCK_API_KEY`; never reads `process.env` when `statePort`,
|
|
3361
3340
|
* `registry`, and `config` are all supplied explicitly.
|
|
3362
3341
|
*
|
|
3363
3342
|
* @param options - Target environment plus optional overrides.
|
|
@@ -3591,14 +3570,9 @@ async function runReconcile(environment, deps) {
|
|
|
3591
3570
|
* the shell from the icon file's bytes) and fall back to the
|
|
3592
3571
|
* Mantle-recorded hashes when the map omits the key. Product resources
|
|
3593
3572
|
* without an icon partner omit `icon` and `iconFileHashes` entirely. The
|
|
3594
|
-
*
|
|
3595
|
-
*
|
|
3596
|
-
*
|
|
3597
|
-
* be read (the shell emits an `ambiguous` warning), surface a universe
|
|
3598
|
-
* resource without those fields. The `outputs` field carries the
|
|
3599
|
-
* Mantle-recorded identifiers (universe `rootPlaceId` and optional
|
|
3600
|
-
* `iconAssetIds`, place `versionNumber`, pass `assetId` and
|
|
3601
|
-
* `iconAssetIds`, product `productId` and optional `iconImageAssetId`).
|
|
3573
|
+
* `outputs` field carries the Mantle-recorded identifiers (universe
|
|
3574
|
+
* `rootPlaceId`, place `versionNumber`, pass `assetId` and `iconAssetIds`,
|
|
3575
|
+
* product `productId` and optional `iconImageAssetId`).
|
|
3602
3576
|
*
|
|
3603
3577
|
* @param inputs - Folded data plus recomputed hashes for this environment.
|
|
3604
3578
|
* @returns A `BedrockState` populated with one resource per folded kind.
|
|
@@ -3611,8 +3585,8 @@ function buildState(inputs) {
|
|
|
3611
3585
|
};
|
|
3612
3586
|
}
|
|
3613
3587
|
function universeResource(inputs) {
|
|
3614
|
-
const { entry,
|
|
3615
|
-
|
|
3588
|
+
const { entry, outputs } = inputs;
|
|
3589
|
+
return {
|
|
3616
3590
|
key: UNIVERSE_SINGLETON_KEY,
|
|
3617
3591
|
consoleEnabled: entry.consoleEnabled,
|
|
3618
3592
|
desktopEnabled: entry.desktopEnabled,
|
|
@@ -3625,12 +3599,6 @@ function universeResource(inputs) {
|
|
|
3625
3599
|
voiceChatEnabled: entry.voiceChatEnabled,
|
|
3626
3600
|
vrEnabled: entry.vrEnabled
|
|
3627
3601
|
};
|
|
3628
|
-
if (entry.icon === void 0 || iconHashes === void 0) return base;
|
|
3629
|
-
return {
|
|
3630
|
-
...base,
|
|
3631
|
-
icon: entry.icon,
|
|
3632
|
-
iconFileHashes: iconHashes
|
|
3633
|
-
};
|
|
3634
3602
|
}
|
|
3635
3603
|
function placeResource(key, fold) {
|
|
3636
3604
|
return {
|
|
@@ -3676,10 +3644,9 @@ function productResource(fold, productIconHashesByKey) {
|
|
|
3676
3644
|
};
|
|
3677
3645
|
}
|
|
3678
3646
|
function composeResources(inputs) {
|
|
3679
|
-
const { folded, passIconHashesByKey, productIconHashesByKey
|
|
3647
|
+
const { folded, passIconHashesByKey, productIconHashesByKey } = inputs;
|
|
3680
3648
|
const universeResources = folded.universe === void 0 ? [] : [universeResource({
|
|
3681
3649
|
entry: folded.universe.entry,
|
|
3682
|
-
iconHashes: universeIconHashes,
|
|
3683
3650
|
outputs: folded.universe.outputs
|
|
3684
3651
|
})];
|
|
3685
3652
|
const placeResources = [...folded.places.entries()].map(([key, entry]) => placeResource(key, entry));
|
|
@@ -4304,14 +4271,14 @@ function readPassInputs(raw) {
|
|
|
4304
4271
|
price: readPrice$1(raw)
|
|
4305
4272
|
};
|
|
4306
4273
|
}
|
|
4307
|
-
function coerceRobloxId$
|
|
4274
|
+
function coerceRobloxId$3(value) {
|
|
4308
4275
|
if (typeof value === "string") return value;
|
|
4309
4276
|
if (Number.isInteger(value)) return String(value);
|
|
4310
4277
|
}
|
|
4311
4278
|
function readPassOutputs(raw) {
|
|
4312
4279
|
if (!isObjectPayload$2(raw)) return;
|
|
4313
|
-
const assetId = coerceRobloxId$
|
|
4314
|
-
const iconAssetId = coerceRobloxId$
|
|
4280
|
+
const assetId = coerceRobloxId$3(raw["assetId"]);
|
|
4281
|
+
const iconAssetId = coerceRobloxId$3(raw["iconAssetId"]);
|
|
4315
4282
|
if (assetId === void 0 || iconAssetId === void 0) return;
|
|
4316
4283
|
return {
|
|
4317
4284
|
assetId,
|
|
@@ -4358,7 +4325,7 @@ function isObjectPayload$1(value) {
|
|
|
4358
4325
|
* @param value - Raw value pulled from a Mantle resource's outputs.
|
|
4359
4326
|
* @returns The stringified ID, or `undefined` when the value is not a valid wire shape.
|
|
4360
4327
|
*/
|
|
4361
|
-
function coerceRobloxId$
|
|
4328
|
+
function coerceRobloxId$2(value) {
|
|
4362
4329
|
if (typeof value === "string") return value;
|
|
4363
4330
|
if (Number.isInteger(value)) return String(value);
|
|
4364
4331
|
}
|
|
@@ -4632,14 +4599,14 @@ function isStartPlace$1(resource) {
|
|
|
4632
4599
|
if (!isObjectPayload$1(resource.inputs)) return false;
|
|
4633
4600
|
return resource.inputs["isStart"] === true;
|
|
4634
4601
|
}
|
|
4635
|
-
function coerceRobloxId$
|
|
4602
|
+
function coerceRobloxId$1(value) {
|
|
4636
4603
|
if (typeof value === "string") return value;
|
|
4637
4604
|
if (Number.isInteger(value)) return String(value);
|
|
4638
4605
|
}
|
|
4639
4606
|
function readPlaceOutputs(resource) {
|
|
4640
4607
|
const { outputs } = resource;
|
|
4641
4608
|
if (!isObjectPayload$1(outputs)) return;
|
|
4642
|
-
const assetId = coerceRobloxId$
|
|
4609
|
+
const assetId = coerceRobloxId$1(outputs["assetId"]);
|
|
4643
4610
|
if (assetId === void 0) return;
|
|
4644
4611
|
return { assetId };
|
|
4645
4612
|
}
|
|
@@ -4762,7 +4729,7 @@ function readProductInputs(raw) {
|
|
|
4762
4729
|
}
|
|
4763
4730
|
function readProductOutputs(raw) {
|
|
4764
4731
|
if (!isObjectPayload$1(raw)) return;
|
|
4765
|
-
const productId = coerceRobloxId$
|
|
4732
|
+
const productId = coerceRobloxId$2(raw["productId"]);
|
|
4766
4733
|
if (productId === void 0) return;
|
|
4767
4734
|
return { productId };
|
|
4768
4735
|
}
|
|
@@ -4778,7 +4745,7 @@ function readProductIconInputs(raw) {
|
|
|
4778
4745
|
}
|
|
4779
4746
|
function readProductIconOutputs(raw) {
|
|
4780
4747
|
if (!isObjectPayload$1(raw)) return;
|
|
4781
|
-
const assetId = coerceRobloxId$
|
|
4748
|
+
const assetId = coerceRobloxId$2(raw["assetId"]);
|
|
4782
4749
|
if (assetId === void 0) return;
|
|
4783
4750
|
return { assetId };
|
|
4784
4751
|
}
|
|
@@ -4960,51 +4927,36 @@ function foldDisplayName(resources) {
|
|
|
4960
4927
|
//#endregion
|
|
4961
4928
|
//#region src/core/migrate/fold-experience-icon.ts
|
|
4962
4929
|
const EXPERIENCE_ICON_KIND = "experienceIcon";
|
|
4930
|
+
const BLOCKED_REASON = "Open Cloud has no route to set a universe's source-language game icon; configure it via the Roblox creator portal.";
|
|
4963
4931
|
/**
|
|
4964
|
-
*
|
|
4965
|
-
*
|
|
4966
|
-
*
|
|
4967
|
-
*
|
|
4968
|
-
*
|
|
4932
|
+
* Surface every Mantle `experienceIcon_<key>` resource as a `blocked`
|
|
4933
|
+
* migration warning. Bedrock has no `UniverseEntry.icon` field today
|
|
4934
|
+
* because no Open Cloud endpoint accepts a source-language game icon, so
|
|
4935
|
+
* the migrator emits one warning per legacy resource (rather than the
|
|
4936
|
+
* first matching entry only) so the operator can audit each affected
|
|
4937
|
+
* environment before reconfiguring the icon by hand.
|
|
4969
4938
|
*
|
|
4970
4939
|
* Resources whose payload is malformed (non-object inputs/outputs,
|
|
4971
|
-
* non-string `filePath
|
|
4972
|
-
*
|
|
4973
|
-
* multiple `experienceIcon` resources lands in a follow-up slice.
|
|
4940
|
+
* non-string `filePath`) are skipped silently, matching the
|
|
4941
|
+
* malformed-payload behaviour of the other fold rules.
|
|
4974
4942
|
*
|
|
4975
4943
|
* @param resources - Mantle resource list for one environment.
|
|
4976
|
-
* @returns
|
|
4944
|
+
* @returns A fragment whose `warnings` carries one `blocked` entry per
|
|
4945
|
+
* legacy experience-icon resource, or {@link EMPTY_FRAGMENT} when none
|
|
4946
|
+
* are present.
|
|
4977
4947
|
*/
|
|
4978
4948
|
function foldExperienceIcon(resources) {
|
|
4979
|
-
const
|
|
4980
|
-
if (
|
|
4981
|
-
const parts = readParts(first);
|
|
4982
|
-
if (parts === void 0) return EMPTY_FRAGMENT;
|
|
4949
|
+
const warnings = resources.filter((resource) => resource.kind === EXPERIENCE_ICON_KIND && hasReadablePayload(resource)).map((resource) => blockedWarning(`${EXPERIENCE_ICON_KIND}_${resource.key}`, BLOCKED_REASON));
|
|
4950
|
+
if (warnings.length === 0) return EMPTY_FRAGMENT;
|
|
4983
4951
|
return {
|
|
4984
|
-
entryFragment: {
|
|
4985
|
-
|
|
4986
|
-
warnings: [interpretiveWarning({
|
|
4987
|
-
bedrockPath: "universe.icon",
|
|
4988
|
-
mantlePath: `${EXPERIENCE_ICON_KIND}_${first.key}`,
|
|
4989
|
-
rule: "experience-icon-to-en-us-locale"
|
|
4990
|
-
})]
|
|
4952
|
+
entryFragment: {},
|
|
4953
|
+
warnings
|
|
4991
4954
|
};
|
|
4992
4955
|
}
|
|
4993
|
-
function
|
|
4994
|
-
|
|
4995
|
-
return isRobloxAssetId(candidate) ? candidate : void 0;
|
|
4996
|
-
}
|
|
4997
|
-
function readParts(resource) {
|
|
4998
|
-
if (!isObjectPayload$1(resource.inputs)) return;
|
|
4956
|
+
function hasReadablePayload(resource) {
|
|
4957
|
+
if (!isObjectPayload$1(resource.inputs)) return false;
|
|
4999
4958
|
const { filePath } = resource.inputs;
|
|
5000
|
-
|
|
5001
|
-
if (!isObjectPayload$1(resource.outputs)) return;
|
|
5002
|
-
const assetId = coerceRobloxId$1(resource.outputs["assetId"]);
|
|
5003
|
-
if (assetId === void 0) return;
|
|
5004
|
-
return {
|
|
5005
|
-
assetId,
|
|
5006
|
-
filePath
|
|
5007
|
-
};
|
|
4959
|
+
return typeof filePath === "string";
|
|
5008
4960
|
}
|
|
5009
4961
|
//#endregion
|
|
5010
4962
|
//#region src/core/migrate/fold-social-links.ts
|
|
@@ -5480,20 +5432,17 @@ function summarizeWarnings(warnings) {
|
|
|
5480
5432
|
//#endregion
|
|
5481
5433
|
//#region src/shell/recompute-icon-hashes.ts
|
|
5482
5434
|
/**
|
|
5483
|
-
* Walk each environment's folded pass
|
|
5484
|
-
*
|
|
5485
|
-
* the
|
|
5486
|
-
*
|
|
5487
|
-
*
|
|
5488
|
-
*
|
|
5489
|
-
*
|
|
5490
|
-
* and omits the icon entirely on the universe resource. Products without
|
|
5491
|
-
* an icon partner and environments without an experience icon are
|
|
5492
|
-
* silently skipped.
|
|
5435
|
+
* Walk each environment's folded pass and product entries, resolve the
|
|
5436
|
+
* locale-keyed icon paths against `stateFileDirectory`, read the bytes via
|
|
5437
|
+
* the injected `readFile`, and compute the SHA-256 hex digest. Files that
|
|
5438
|
+
* cannot be read surface as `ambiguous` `MigrationWarning`s with the
|
|
5439
|
+
* environment-prefixed `mantlePath` and a hint pointing at the resolved
|
|
5440
|
+
* path; the caller carries the Mantle-recorded hashes forward as a
|
|
5441
|
+
* fallback. Products without an icon partner are silently skipped.
|
|
5493
5442
|
*
|
|
5494
5443
|
* @param inputs - Per-environment fold results plus I/O dependencies.
|
|
5495
|
-
* @returns Per-environment recomputed pass
|
|
5496
|
-
*
|
|
5444
|
+
* @returns Per-environment recomputed pass and product hashes plus
|
|
5445
|
+
* accumulated ambiguous warnings.
|
|
5497
5446
|
*/
|
|
5498
5447
|
async function recomputeIconHashes(inputs) {
|
|
5499
5448
|
return collectRecomputation(await Promise.all([...inputs.folds.entries()].map(async ([environment, folded]) => {
|
|
@@ -5509,9 +5458,6 @@ function collectRecomputation(walked) {
|
|
|
5509
5458
|
return {
|
|
5510
5459
|
passHashesByEnvironment: new Map(walked.map(([environment, walk]) => [environment, walk.passHashes])),
|
|
5511
5460
|
productHashesByEnvironment: new Map(walked.map(([environment, walk]) => [environment, walk.productHashes])),
|
|
5512
|
-
universeHashByEnvironment: new Map(walked.flatMap(([environment, walk]) => {
|
|
5513
|
-
return walk.universeHash === void 0 ? [] : [[environment, walk.universeHash]];
|
|
5514
|
-
})),
|
|
5515
5461
|
warnings: walked.flatMap(([, walk]) => walk.warnings)
|
|
5516
5462
|
};
|
|
5517
5463
|
}
|
|
@@ -5562,27 +5508,6 @@ async function walkIconEntries(inputs) {
|
|
|
5562
5508
|
warnings
|
|
5563
5509
|
};
|
|
5564
5510
|
}
|
|
5565
|
-
async function walkUniverseIcon(inputs) {
|
|
5566
|
-
const iconPath = inputs.folded.universe?.entry.icon?.["en-us"];
|
|
5567
|
-
if (iconPath === void 0) return {
|
|
5568
|
-
hash: void 0,
|
|
5569
|
-
warnings: []
|
|
5570
|
-
};
|
|
5571
|
-
const resolved = join(inputs.stateFileDirectory, iconPath);
|
|
5572
|
-
const recomputed = await tryRecomputeHash(inputs.readFile, resolved);
|
|
5573
|
-
if (recomputed === void 0) return {
|
|
5574
|
-
hash: void 0,
|
|
5575
|
-
warnings: [buildAmbiguousIconWarning({
|
|
5576
|
-
environmentName: inputs.environmentName,
|
|
5577
|
-
mantlePath: "experienceIcon_singleton",
|
|
5578
|
-
resolvedPath: resolved
|
|
5579
|
-
})]
|
|
5580
|
-
};
|
|
5581
|
-
return {
|
|
5582
|
-
hash: { "en-us": recomputed },
|
|
5583
|
-
warnings: []
|
|
5584
|
-
};
|
|
5585
|
-
}
|
|
5586
5511
|
async function walkEnvironment(inputs) {
|
|
5587
5512
|
const passWalk = await walkIconEntries({
|
|
5588
5513
|
entries: inputs.folded.passes.map(passWalkEntry),
|
|
@@ -5596,21 +5521,10 @@ async function walkEnvironment(inputs) {
|
|
|
5596
5521
|
readFile: inputs.readFile,
|
|
5597
5522
|
stateFileDirectory: inputs.stateFileDirectory
|
|
5598
5523
|
});
|
|
5599
|
-
const universeWalk = await walkUniverseIcon({
|
|
5600
|
-
environmentName: inputs.environmentName,
|
|
5601
|
-
folded: inputs.folded,
|
|
5602
|
-
readFile: inputs.readFile,
|
|
5603
|
-
stateFileDirectory: inputs.stateFileDirectory
|
|
5604
|
-
});
|
|
5605
5524
|
return {
|
|
5606
5525
|
passHashes: passWalk.perKey,
|
|
5607
5526
|
productHashes: productWalk.perKey,
|
|
5608
|
-
|
|
5609
|
-
warnings: [
|
|
5610
|
-
...passWalk.warnings,
|
|
5611
|
-
...productWalk.warnings,
|
|
5612
|
-
...universeWalk.warnings
|
|
5613
|
-
]
|
|
5527
|
+
warnings: [...passWalk.warnings, ...productWalk.warnings]
|
|
5614
5528
|
};
|
|
5615
5529
|
}
|
|
5616
5530
|
//#endregion
|
|
@@ -5705,8 +5619,7 @@ function buildStatesByEnvironment(inputs) {
|
|
|
5705
5619
|
environment: name,
|
|
5706
5620
|
folded,
|
|
5707
5621
|
passIconHashesByKey: inputs.passHashesByEnvironment.get(name) ?? EMPTY_HASHES,
|
|
5708
|
-
productIconHashesByKey: inputs.productHashesByEnvironment.get(name) ?? EMPTY_HASHES
|
|
5709
|
-
universeIconHashes: inputs.universeHashByEnvironment.get(name)
|
|
5622
|
+
productIconHashesByKey: inputs.productHashesByEnvironment.get(name) ?? EMPTY_HASHES
|
|
5710
5623
|
})];
|
|
5711
5624
|
}));
|
|
5712
5625
|
}
|
|
@@ -5722,7 +5635,7 @@ function collectFoldWarnings(folds) {
|
|
|
5722
5635
|
});
|
|
5723
5636
|
}
|
|
5724
5637
|
function buildReport(inputs, validated) {
|
|
5725
|
-
const { passHashesByEnvironment, productHashesByEnvironment,
|
|
5638
|
+
const { passHashesByEnvironment, productHashesByEnvironment, warnings: iconWarnings } = inputs.iconRecomputation;
|
|
5726
5639
|
const warnings = [
|
|
5727
5640
|
...collectFoldWarnings(inputs.folds),
|
|
5728
5641
|
...inputs.factorizeWarnings,
|
|
@@ -5737,8 +5650,7 @@ function buildReport(inputs, validated) {
|
|
|
5737
5650
|
statesByEnvironment: buildStatesByEnvironment({
|
|
5738
5651
|
folds: inputs.folds,
|
|
5739
5652
|
passHashesByEnvironment,
|
|
5740
|
-
productHashesByEnvironment
|
|
5741
|
-
universeHashByEnvironment
|
|
5653
|
+
productHashesByEnvironment
|
|
5742
5654
|
}),
|
|
5743
5655
|
summary: summarizeWarnings(warnings),
|
|
5744
5656
|
warnings
|
|
@@ -5786,4 +5698,4 @@ function isFileMissing(err) {
|
|
|
5786
5698
|
//#endregion
|
|
5787
5699
|
export { asResourceKey as A, createGistStateAdapter as C, createGamePassDriver as D, validateEnvironmentName as E, isSha256Hex as F, derivePriceFields as I, asSha256Hex as M, isResourceKey as N, createDeveloperProductDriver as O, isRobloxAssetId as P, UNIVERSE_SINGLETON_KEY as S, serializeStateFile as T, isGistStateConfig as _, buildStatePort as a, createPlaceDriver as b, applyOps as c, resolveStateConfig as d, flattenConfig as f, defaultKindRegistry as g, diff as h, loadConfig$1 as i, asRobloxAssetId as j, shouldReuploadIcon as k, validatePlan as l, renderDisplayNamePrefix as m, serializeConfig as n, buildDesired as o, DEFAULT_PREFIX_FORMAT as p, deploy as r, buildDefaultRegistry as s, migrateMantleState as t, selectEnvironment as u, validateConfig as v, parseStateFile as w, SOCIAL_LINK_FIELDS as x, createUniverseDriver as y };
|
|
5788
5700
|
|
|
5789
|
-
//# sourceMappingURL=migrate-mantle-state-
|
|
5701
|
+
//# sourceMappingURL=migrate-mantle-state-_7Tkn0hG.mjs.map
|