@bedrock-rbx/core 0.1.0-beta.14 → 0.1.0-beta.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +180 -0
- package/dist/cli/run.mjs +135 -37
- package/dist/cli/run.mjs.map +1 -1
- package/dist/config.d.mts +1 -1
- package/dist/{define-config-C2cOtDpP.d.mts → define-config-B4GZRPj-.d.mts} +3 -3
- package/dist/{define-config-C2cOtDpP.d.mts.map → define-config-B4GZRPj-.d.mts.map} +1 -1
- package/dist/index.d.mts +242 -91
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +2 -26
- package/dist/index.mjs.map +1 -1
- package/dist/{migrate-mantle-state-qejWFAR0.mjs → migrate-mantle-state-ClQ40EFD.mjs} +458 -164
- package/dist/migrate-mantle-state-ClQ40EFD.mjs.map +1 -0
- package/package.json +4 -4
- package/dist/migrate-mantle-state-qejWFAR0.mjs.map +0 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { cancel, intro, log, outro } from "@clack/prompts";
|
|
1
2
|
import { ApiError, PermissionError } from "@bedrock-rbx/ocale";
|
|
2
3
|
import { ArkErrors, type } from "arktype";
|
|
3
|
-
import {
|
|
4
|
+
import { execFile, spawn } from "node:child_process";
|
|
4
5
|
import process from "node:process";
|
|
5
6
|
import { defu } from "defu";
|
|
6
7
|
import { createHash } from "node:crypto";
|
|
@@ -12,9 +13,51 @@ import { readFile } from "node:fs/promises";
|
|
|
12
13
|
import { loadConfig } from "c12";
|
|
13
14
|
import { existsSync, mkdtempSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
14
15
|
import { basename, dirname, isAbsolute, join, resolve } from "node:path";
|
|
15
|
-
import { execFile } from "node:child_process";
|
|
16
16
|
import { tmpdir } from "node:os";
|
|
17
17
|
import { parseYAML, stringifyYAML } from "confbox";
|
|
18
|
+
//#region src/cli/clack-port.ts
|
|
19
|
+
/**
|
|
20
|
+
* Construct a `ClackPort` whose methods delegate to `@clack/prompts`. The
|
|
21
|
+
* resulting port writes to `process.stdout` via clack's defaults. Kept in
|
|
22
|
+
* its own module so consumers that never need the clack-backed rendering
|
|
23
|
+
* (programmatic deploys, custom adapters) do not pull `@clack/prompts`
|
|
24
|
+
* into their bundle.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
*
|
|
28
|
+
* ```ts
|
|
29
|
+
* import { createClackPort } from "@bedrock-rbx/core";
|
|
30
|
+
*
|
|
31
|
+
* const port = createClackPort();
|
|
32
|
+
*
|
|
33
|
+
* expect(typeof port.logSuccess).toBe("function");
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* @returns A port whose six methods each invoke the matching clack helper.
|
|
37
|
+
*/
|
|
38
|
+
function createClackPort() {
|
|
39
|
+
return {
|
|
40
|
+
cancel: (message) => {
|
|
41
|
+
cancel(message);
|
|
42
|
+
},
|
|
43
|
+
intro: (message) => {
|
|
44
|
+
intro(message);
|
|
45
|
+
},
|
|
46
|
+
logError: (message) => {
|
|
47
|
+
log.error(message);
|
|
48
|
+
},
|
|
49
|
+
logMessage: (message) => {
|
|
50
|
+
log.message(message);
|
|
51
|
+
},
|
|
52
|
+
logSuccess: (message) => {
|
|
53
|
+
log.success(message);
|
|
54
|
+
},
|
|
55
|
+
outro: (message) => {
|
|
56
|
+
outro(message);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
//#endregion
|
|
18
61
|
//#region src/cli/render.ts
|
|
19
62
|
/**
|
|
20
63
|
* Render a `DeployError` to the supplied `ClackPort`. Most variants emit a
|
|
@@ -46,6 +89,30 @@ function renderParseError(err, port) {
|
|
|
46
89
|
port.logError(parseErrorMessage(err));
|
|
47
90
|
}
|
|
48
91
|
/**
|
|
92
|
+
* Render a `SpawnOverrideError` to the supplied `ClackPort` as a single
|
|
93
|
+
* error line that names the environment alongside the failure mode. On
|
|
94
|
+
* `launchFailed` the child never produced output of its own, so the parent
|
|
95
|
+
* carries the diagnostic; on `nonZeroExit` the parent's line attributes the
|
|
96
|
+
* exit code to a specific environment when several spawns are running.
|
|
97
|
+
* @param input - Environment + spawn-override error to describe.
|
|
98
|
+
* @param port - The output port the diagnostic is written to.
|
|
99
|
+
*/
|
|
100
|
+
function renderOverrideError(input, port) {
|
|
101
|
+
port.logError(overrideErrorMessage(input));
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Render the failure surfaced when override discovery throws a non-absence
|
|
105
|
+
* filesystem error (for example `EACCES` on a `.bedrock/<command>.ts` that
|
|
106
|
+
* exists but cannot be read). Discovery refuses to fall through to the
|
|
107
|
+
* built-in path in that case, so the CLI reports the cause and exits rather
|
|
108
|
+
* than crashing on the unhandled throw.
|
|
109
|
+
* @param error - The value thrown during override discovery.
|
|
110
|
+
* @param port - The output port the diagnostic is written to.
|
|
111
|
+
*/
|
|
112
|
+
function renderOverrideDiscoveryError(error, port) {
|
|
113
|
+
port.logError(`override discovery failed: ${safeStringify(error)}`);
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
49
116
|
* Render a `ParseMigrateError` to the supplied `ClackPort`. Reuses
|
|
50
117
|
* `parseErrorMessage` for the three flag-shape variants and adds a
|
|
51
118
|
* dedicated message for `unknownSource` listing the supported sources.
|
|
@@ -182,6 +249,11 @@ function parseErrorMessage(err) {
|
|
|
182
249
|
case "unknownFlag": return `unknown flag '--${err.flag}'`;
|
|
183
250
|
}
|
|
184
251
|
}
|
|
252
|
+
function overrideErrorMessage(input) {
|
|
253
|
+
const { environment, err } = input;
|
|
254
|
+
if (err.kind === "launchFailed") return `${environment}: failed to launch override - ${err.cause.message}`;
|
|
255
|
+
return `${environment}: override exited with code ${String(err.exitCode)}`;
|
|
256
|
+
}
|
|
185
257
|
function migrateParseErrorMessage(err) {
|
|
186
258
|
if (err.kind === "unknownSource") return `unknown migration source '${err.received}' (supported: ${err.supported.join(", ")})`;
|
|
187
259
|
return parseErrorMessage(err);
|
|
@@ -1235,6 +1307,27 @@ function createClackProgressAdapter(deps) {
|
|
|
1235
1307
|
renderEvent(event, deps);
|
|
1236
1308
|
} };
|
|
1237
1309
|
}
|
|
1310
|
+
/**
|
|
1311
|
+
* Build a {@link ProgressPort} for the default CLI rendering path: wires a
|
|
1312
|
+
* fresh {@link createClackPort} into {@link createClackProgressAdapter}. The
|
|
1313
|
+
* `config` argument (raw `Config` or env-resolved `ResolvedConfig`) is
|
|
1314
|
+
* forwarded so `stateWritten` events can name the persistence backend; pass
|
|
1315
|
+
* `undefined` when the config has not yet loaded.
|
|
1316
|
+
*
|
|
1317
|
+
* Internal: used by `deploy()`'s default-port resolver when callers omit
|
|
1318
|
+
* `progress` and `BEDROCK_CLI` is set.
|
|
1319
|
+
*
|
|
1320
|
+
* @param config - Pre-loaded or env-resolved config used to format the
|
|
1321
|
+
* state-backend label, or `undefined` to render the generic placeholder.
|
|
1322
|
+
* @returns A clack-backed `ProgressPort` that writes to `process.stdout`.
|
|
1323
|
+
*/
|
|
1324
|
+
function createDefaultProgressAdapter(config) {
|
|
1325
|
+
const clack = createClackPort();
|
|
1326
|
+
return config === void 0 ? createClackProgressAdapter({ clack }) : createClackProgressAdapter({
|
|
1327
|
+
clack,
|
|
1328
|
+
config
|
|
1329
|
+
});
|
|
1330
|
+
}
|
|
1238
1331
|
function applySummaryLine(event) {
|
|
1239
1332
|
return `Succeeded in ${(event.durationMs / 1e3).toFixed(1)}s: ${[
|
|
1240
1333
|
`${event.created} create`,
|
|
@@ -1800,7 +1893,9 @@ const GITHUB_API_BASE = "https://api.github.com";
|
|
|
1800
1893
|
const GITHUB_API_VERSION = "2026-03-10";
|
|
1801
1894
|
const USER_AGENT = "bedrock";
|
|
1802
1895
|
const MAX_INLINE_BYTES = 1e7;
|
|
1803
|
-
const MAX_RETRIES =
|
|
1896
|
+
const MAX_RETRIES = 6;
|
|
1897
|
+
const BASE_BACKOFF_MS = 500;
|
|
1898
|
+
const MAX_BACKOFF_MS = 16e3;
|
|
1804
1899
|
const RETRYABLE_STATUSES = new Set([
|
|
1805
1900
|
409,
|
|
1806
1901
|
502,
|
|
@@ -1843,6 +1938,7 @@ function createGistStateAdapter(deps) {
|
|
|
1843
1938
|
const ctx = {
|
|
1844
1939
|
fetchFn: deps.fetch ?? globalThis.fetch.bind(globalThis),
|
|
1845
1940
|
gistId: deps.gistId,
|
|
1941
|
+
random: deps.random ?? Math.random,
|
|
1846
1942
|
sleep: deps.sleep ?? defaultSleep,
|
|
1847
1943
|
token: deps.token
|
|
1848
1944
|
};
|
|
@@ -1938,14 +2034,15 @@ async function sendGet(ctx) {
|
|
|
1938
2034
|
function isRetryableStatus(status) {
|
|
1939
2035
|
return RETRYABLE_STATUSES.has(status);
|
|
1940
2036
|
}
|
|
1941
|
-
function backoffMs(attempt) {
|
|
1942
|
-
|
|
2037
|
+
function backoffMs(attempt, random) {
|
|
2038
|
+
const half = Math.min(MAX_BACKOFF_MS, BASE_BACKOFF_MS * 2 ** attempt) / 2;
|
|
2039
|
+
return half + random() * half;
|
|
1943
2040
|
}
|
|
1944
|
-
async function withRetry(
|
|
2041
|
+
async function withRetry(retry, operation) {
|
|
1945
2042
|
let response = await operation();
|
|
1946
2043
|
for (let attempt = 0; attempt < MAX_RETRIES; attempt += 1) {
|
|
1947
2044
|
if (response.ok || !isRetryableStatus(response.status)) return response;
|
|
1948
|
-
await sleep(backoffMs(attempt));
|
|
2045
|
+
await retry.sleep(backoffMs(attempt, retry.random));
|
|
1949
2046
|
response = await operation();
|
|
1950
2047
|
}
|
|
1951
2048
|
return response;
|
|
@@ -1953,7 +2050,7 @@ async function withRetry(sleep, operation) {
|
|
|
1953
2050
|
async function fetchGistBody(ctx, file) {
|
|
1954
2051
|
let response;
|
|
1955
2052
|
try {
|
|
1956
|
-
response = await withRetry(ctx
|
|
2053
|
+
response = await withRetry(ctx, async () => sendGet(ctx));
|
|
1957
2054
|
} catch (err) {
|
|
1958
2055
|
return {
|
|
1959
2056
|
err: networkError(err, file),
|
|
@@ -1983,14 +2080,14 @@ function stateErr(file, reason) {
|
|
|
1983
2080
|
success: false
|
|
1984
2081
|
};
|
|
1985
2082
|
}
|
|
1986
|
-
async function readGistContent({ entry, fetchFn, file,
|
|
2083
|
+
async function readGistContent({ entry, fetchFn, file, retry }) {
|
|
1987
2084
|
if (entry.size > MAX_INLINE_BYTES) return stateErr(file, `state file too large: ${entry.size} bytes`);
|
|
1988
2085
|
if (entry.isTruncated) {
|
|
1989
2086
|
if (entry.rawUrl === void 0) return stateErr(file, "truncated gist file missing raw_url");
|
|
1990
2087
|
const { rawUrl } = entry;
|
|
1991
2088
|
let rawResponse;
|
|
1992
2089
|
try {
|
|
1993
|
-
rawResponse = await withRetry(
|
|
2090
|
+
rawResponse = await withRetry(retry, async () => fetchFn(rawUrl));
|
|
1994
2091
|
} catch (err) {
|
|
1995
2092
|
return {
|
|
1996
2093
|
err: networkError(err, file),
|
|
@@ -2016,7 +2113,7 @@ async function readPath(ctx, environment) {
|
|
|
2016
2113
|
entry,
|
|
2017
2114
|
fetchFn: ctx.fetchFn,
|
|
2018
2115
|
file,
|
|
2019
|
-
|
|
2116
|
+
retry: ctx
|
|
2020
2117
|
});
|
|
2021
2118
|
}
|
|
2022
2119
|
async function sendPatch(ctx, body) {
|
|
@@ -2065,7 +2162,7 @@ async function writePath(ctx, state) {
|
|
|
2065
2162
|
const body = JSON.stringify({ files: { [fileName(state.environment)]: { content: serializeStateFile(state) } } });
|
|
2066
2163
|
let response;
|
|
2067
2164
|
try {
|
|
2068
|
-
response = await withRetry(ctx
|
|
2165
|
+
response = await withRetry(ctx, async () => sendPatch(ctx, body));
|
|
2069
2166
|
} catch (err) {
|
|
2070
2167
|
return {
|
|
2071
2168
|
err: networkError(err, file),
|
|
@@ -2092,6 +2189,30 @@ async function writePath(ctx, state) {
|
|
|
2092
2189
|
};
|
|
2093
2190
|
}
|
|
2094
2191
|
//#endregion
|
|
2192
|
+
//#region src/adapters/no-op-progress-adapter.ts
|
|
2193
|
+
/**
|
|
2194
|
+
* Build a {@link ProgressPort} that silently drops every event. Useful for
|
|
2195
|
+
* tests and programmatic callers who want to invoke deploy logic without
|
|
2196
|
+
* any rendering.
|
|
2197
|
+
*
|
|
2198
|
+
* @example
|
|
2199
|
+
*
|
|
2200
|
+
* ```ts
|
|
2201
|
+
* import { createNoOpProgressAdapter } from "@bedrock-rbx/core";
|
|
2202
|
+
*
|
|
2203
|
+
* const port = createNoOpProgressAdapter();
|
|
2204
|
+
*
|
|
2205
|
+
* expect(() =>
|
|
2206
|
+
* port.emit({ environment: "production", kind: "deploySuccess", resourceCount: 3 }),
|
|
2207
|
+
* ).not.toThrow();
|
|
2208
|
+
* ```
|
|
2209
|
+
*
|
|
2210
|
+
* @returns A `ProgressPort` whose `emit` method is a no-op.
|
|
2211
|
+
*/
|
|
2212
|
+
function createNoOpProgressAdapter() {
|
|
2213
|
+
return { emit() {} };
|
|
2214
|
+
}
|
|
2215
|
+
//#endregion
|
|
2095
2216
|
//#region src/core/resources.ts
|
|
2096
2217
|
/**
|
|
2097
2218
|
* Ordered list of optional metadata fields the driver routes through
|
|
@@ -2470,46 +2591,192 @@ async function reconcileUniverse(inputs) {
|
|
|
2470
2591
|
};
|
|
2471
2592
|
}
|
|
2472
2593
|
//#endregion
|
|
2473
|
-
//#region src/cli/
|
|
2594
|
+
//#region src/cli/default-spawner.ts
|
|
2474
2595
|
/**
|
|
2475
|
-
*
|
|
2476
|
-
*
|
|
2477
|
-
*
|
|
2478
|
-
*
|
|
2479
|
-
*
|
|
2480
|
-
*
|
|
2596
|
+
* Translate a `child.on("close", code, signal)` payload into the
|
|
2597
|
+
* {@link Spawner.spawn} return shape. Extracted from the adapter so the
|
|
2598
|
+
* signal-terminated branch can be exercised without launching a real
|
|
2599
|
+
* process. The caller normalizes node's `null` to `undefined` at the
|
|
2600
|
+
* boundary so this helper never sees `null`.
|
|
2601
|
+
* @param code - Exit code reported by the child, or `undefined` if the
|
|
2602
|
+
* child was terminated by a signal before exiting.
|
|
2603
|
+
* @param signal - Signal name reported by the child, or `undefined` when
|
|
2604
|
+
* no signal terminated it.
|
|
2605
|
+
* @returns `Ok(code)` for a clean exit (including `0`); otherwise
|
|
2606
|
+
* `Err(launchFailed)` carrying a synthetic Error whose message names
|
|
2607
|
+
* the signal.
|
|
2608
|
+
*/
|
|
2609
|
+
function classifySpawnClose(code, signal) {
|
|
2610
|
+
if (code !== void 0) return {
|
|
2611
|
+
data: code,
|
|
2612
|
+
success: true
|
|
2613
|
+
};
|
|
2614
|
+
return {
|
|
2615
|
+
err: {
|
|
2616
|
+
cause: /* @__PURE__ */ new Error(`spawned process terminated by signal ${signal ?? "unknown"}`),
|
|
2617
|
+
kind: "launchFailed"
|
|
2618
|
+
},
|
|
2619
|
+
success: false
|
|
2620
|
+
};
|
|
2621
|
+
}
|
|
2622
|
+
/**
|
|
2623
|
+
* Construct a {@link Spawner} backed by `node:child_process.spawn` with
|
|
2624
|
+
* `stdio` inherited from the parent process. The child's environment is
|
|
2625
|
+
* `process.env` overlaid with {@link SpawnInvocation.envOverrides} (overrides
|
|
2626
|
+
* win on key collision).
|
|
2627
|
+
*
|
|
2628
|
+
* - Exit codes resolve as `Ok(exitCode)` (including `0`).
|
|
2629
|
+
* - `ENOENT` and other launch-time errors resolve as `Err(launchFailed)`
|
|
2630
|
+
* with the original error in `cause` (its `code` field carries the
|
|
2631
|
+
* errno where present).
|
|
2632
|
+
* - Children terminated by signal before producing an exit code collapse
|
|
2633
|
+
* into `launchFailed` with a synthetic `Error` whose message names the
|
|
2634
|
+
* signal; a distinct variant lands the day a caller needs to act on the
|
|
2635
|
+
* difference.
|
|
2636
|
+
*
|
|
2637
|
+
* @returns A `Spawner` whose `spawn` settles once the child closes.
|
|
2481
2638
|
* @example
|
|
2482
2639
|
*
|
|
2483
2640
|
* ```ts
|
|
2484
|
-
* import {
|
|
2641
|
+
* import { createDefaultSpawner } from "@bedrock-rbx/core";
|
|
2642
|
+
* import process from "node:process";
|
|
2485
2643
|
*
|
|
2486
|
-
* const
|
|
2644
|
+
* const spawner = createDefaultSpawner();
|
|
2487
2645
|
*
|
|
2488
|
-
*
|
|
2646
|
+
* return spawner
|
|
2647
|
+
* .spawn({
|
|
2648
|
+
* args: ["-e", "process.exit(0)"],
|
|
2649
|
+
* command: process.execPath,
|
|
2650
|
+
* envOverrides: {},
|
|
2651
|
+
* })
|
|
2652
|
+
* .then((result) => {
|
|
2653
|
+
* expect(result.success).toBeTrue();
|
|
2654
|
+
* if (result.success) {
|
|
2655
|
+
* expect(result.data).toBe(0);
|
|
2656
|
+
* }
|
|
2657
|
+
* });
|
|
2489
2658
|
* ```
|
|
2659
|
+
*/
|
|
2660
|
+
function createDefaultSpawner() {
|
|
2661
|
+
return { spawn: spawnViaChildProcess };
|
|
2662
|
+
}
|
|
2663
|
+
async function spawnViaChildProcess(invocation) {
|
|
2664
|
+
return new Promise((resolve) => {
|
|
2665
|
+
const child = spawn(invocation.command, [...invocation.args], {
|
|
2666
|
+
env: {
|
|
2667
|
+
...process.env,
|
|
2668
|
+
...invocation.envOverrides
|
|
2669
|
+
},
|
|
2670
|
+
stdio: "inherit"
|
|
2671
|
+
});
|
|
2672
|
+
child.once("error", (error) => {
|
|
2673
|
+
resolve({
|
|
2674
|
+
err: {
|
|
2675
|
+
cause: error,
|
|
2676
|
+
kind: "launchFailed"
|
|
2677
|
+
},
|
|
2678
|
+
success: false
|
|
2679
|
+
});
|
|
2680
|
+
});
|
|
2681
|
+
child.once("close", (code, signal) => {
|
|
2682
|
+
resolve(classifySpawnClose(code ?? void 0, signal ?? void 0));
|
|
2683
|
+
});
|
|
2684
|
+
});
|
|
2685
|
+
}
|
|
2686
|
+
//#endregion
|
|
2687
|
+
//#region src/cli/credential-environment-overrides.ts
|
|
2688
|
+
/**
|
|
2689
|
+
* Map CLI credential flags to their corresponding env-var names, omitting
|
|
2690
|
+
* entries whose flag is `undefined`.
|
|
2691
|
+
* @param flags - CLI credential flag values to translate.
|
|
2692
|
+
* @returns An immutable record of env-var names to their override values.
|
|
2693
|
+
*/
|
|
2694
|
+
function buildCredentialOverrides(flags) {
|
|
2695
|
+
const overrides = {};
|
|
2696
|
+
if (flags.apiKey !== void 0) overrides["BEDROCK_API_KEY"] = flags.apiKey;
|
|
2697
|
+
if (flags.githubToken !== void 0) overrides["BEDROCK_GITHUB_TOKEN"] = flags.githubToken;
|
|
2698
|
+
return overrides;
|
|
2699
|
+
}
|
|
2700
|
+
//#endregion
|
|
2701
|
+
//#region src/cli/dispatch-override.ts
|
|
2702
|
+
/**
|
|
2703
|
+
* Dispatch a single `.bedrock/<command>.ts` override invocation through the
|
|
2704
|
+
* supplied {@link Spawner}. Encapsulates the spawn protocol:
|
|
2705
|
+
*
|
|
2706
|
+
* - argv = `[overridePath, "--env", environment]`, with `"--config", configFile`
|
|
2707
|
+
* appended when supplied.
|
|
2708
|
+
* - `apiKey` becomes the `BEDROCK_API_KEY` env-var override; `githubToken`
|
|
2709
|
+
* becomes `BEDROCK_GITHUB_TOKEN`. Neither value appears in argv.
|
|
2710
|
+
* - `BEDROCK_CLI=1` is always set in the env. The override's `deploy()`
|
|
2711
|
+
* reads this on the `getEnv` seam to default to the clack progress
|
|
2712
|
+
* adapter; absent that downstream wiring, the variable is a forward-
|
|
2713
|
+
* compatible signal a future caller can act on.
|
|
2714
|
+
*
|
|
2715
|
+
* The dispatcher itself reads no ambient state: every input arrives via the
|
|
2716
|
+
* `invocation` argument and the `Spawner` port is the only side-effect seam.
|
|
2717
|
+
*
|
|
2718
|
+
* @param invocation - Path, environment, and parsed deploy-flag inputs.
|
|
2719
|
+
* @param spawner - Port the dispatcher hands the resolved
|
|
2720
|
+
* {@link SpawnInvocation} to.
|
|
2721
|
+
* @returns `Ok(undefined)` when the child exited zero; otherwise an
|
|
2722
|
+
* {@link SpawnOverrideError} discriminating launch vs non-zero exit.
|
|
2490
2723
|
*
|
|
2491
|
-
* @
|
|
2724
|
+
* @example
|
|
2725
|
+
*
|
|
2726
|
+
* ```ts
|
|
2727
|
+
* import { dispatchOverride, type Spawner } from "@bedrock-rbx/core";
|
|
2728
|
+
*
|
|
2729
|
+
* const spawner: Spawner = {
|
|
2730
|
+
* async spawn() {
|
|
2731
|
+
* return { data: 0, success: true };
|
|
2732
|
+
* },
|
|
2733
|
+
* };
|
|
2734
|
+
*
|
|
2735
|
+
* return dispatchOverride(
|
|
2736
|
+
* {
|
|
2737
|
+
* environment: "production",
|
|
2738
|
+
* overridePath: "/abs/.bedrock/deploy.ts",
|
|
2739
|
+
* },
|
|
2740
|
+
* spawner,
|
|
2741
|
+
* ).then((result) => {
|
|
2742
|
+
* expect(result.success).toBeTrue();
|
|
2743
|
+
* });
|
|
2744
|
+
* ```
|
|
2492
2745
|
*/
|
|
2493
|
-
function
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2746
|
+
async function dispatchOverride(invocation, spawner) {
|
|
2747
|
+
const args = [
|
|
2748
|
+
invocation.overridePath,
|
|
2749
|
+
"--env",
|
|
2750
|
+
invocation.environment
|
|
2751
|
+
];
|
|
2752
|
+
if (invocation.configFile !== void 0) args.push("--config", invocation.configFile);
|
|
2753
|
+
const credentialOverrides = buildCredentialOverrides(invocation);
|
|
2754
|
+
const launched = await spawner.spawn({
|
|
2755
|
+
args,
|
|
2756
|
+
command: "bun",
|
|
2757
|
+
envOverrides: {
|
|
2758
|
+
...credentialOverrides,
|
|
2759
|
+
BEDROCK_CLI: "1"
|
|
2760
|
+
}
|
|
2761
|
+
});
|
|
2762
|
+
if (!launched.success) return {
|
|
2763
|
+
err: {
|
|
2764
|
+
cause: launched.err.cause,
|
|
2765
|
+
kind: "launchFailed"
|
|
2506
2766
|
},
|
|
2507
|
-
|
|
2508
|
-
|
|
2767
|
+
success: false
|
|
2768
|
+
};
|
|
2769
|
+
const exitCode = launched.data;
|
|
2770
|
+
if (exitCode !== 0) return {
|
|
2771
|
+
err: {
|
|
2772
|
+
exitCode,
|
|
2773
|
+
kind: "nonZeroExit"
|
|
2509
2774
|
},
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2775
|
+
success: false
|
|
2776
|
+
};
|
|
2777
|
+
return {
|
|
2778
|
+
data: void 0,
|
|
2779
|
+
success: true
|
|
2513
2780
|
};
|
|
2514
2781
|
}
|
|
2515
2782
|
//#endregion
|
|
@@ -2600,7 +2867,7 @@ function assertReconcilable(current, desired) {
|
|
|
2600
2867
|
/**
|
|
2601
2868
|
* Resource-kind module for Roblox developer products. Owns the entry
|
|
2602
2869
|
* schema, flattening, icon-hash normalization, drift-equality, and the
|
|
2603
|
-
*
|
|
2870
|
+
* pre-reconcile icon-removal rejection for the `developerProduct` kind.
|
|
2604
2871
|
*/
|
|
2605
2872
|
const developerProductKind = {
|
|
2606
2873
|
assertReconcilable,
|
|
@@ -2890,7 +3157,7 @@ const defaultKindRegistry = {
|
|
|
2890
3157
|
*
|
|
2891
3158
|
* @param desired - Declared desired state from user config, already normalized
|
|
2892
3159
|
* (file hashes computed, nullable wire values mapped to `undefined`).
|
|
2893
|
-
* @param current - Last-known
|
|
3160
|
+
* @param current - Last-known current state from the state file.
|
|
2894
3161
|
* @returns Operations to reconcile the two snapshots.
|
|
2895
3162
|
*
|
|
2896
3163
|
* @example
|
|
@@ -3100,7 +3367,7 @@ const PLACE_ENV_FIELDS = ["description", "displayName"];
|
|
|
3100
3367
|
* disambiguating suffix on a redacted developer-product's default `name`.
|
|
3101
3368
|
* Stable across config edits (driven only by the bedrock resource key, not
|
|
3102
3369
|
* declaration order) and opaque to a Roblox player browsing the marketplace.
|
|
3103
|
-
* A natural collision is caught
|
|
3370
|
+
* A natural collision is caught before any apply-side driver I/O by `assertAllReconcilable`.
|
|
3104
3371
|
*
|
|
3105
3372
|
* @param key - Bedrock resource key for the developer product being redacted.
|
|
3106
3373
|
* @returns The first six lowercase hex characters of the SHA-256 digest of `key`.
|
|
@@ -3617,105 +3884,6 @@ function redactAndPrefix(inputs) {
|
|
|
3617
3884
|
};
|
|
3618
3885
|
}
|
|
3619
3886
|
//#endregion
|
|
3620
|
-
//#region src/core/validate-plan.ts
|
|
3621
|
-
/**
|
|
3622
|
-
* Plan-time invariant check that runs after `buildDesired` and before
|
|
3623
|
-
* `diff`. Walks paired `(kind, key)` entries and dispatches to each
|
|
3624
|
-
* kind module's optional `assertReconcilable` hook so kind-specific
|
|
3625
|
-
* rejections (e.g. Removing a developer-product icon, which the upstream
|
|
3626
|
-
* API has no documented unset path for) surface as typed errors before
|
|
3627
|
-
* `diff` runs and before any apply-side driver I/O is attempted.
|
|
3628
|
-
*
|
|
3629
|
-
* Pure and synchronous. Current-only entries (no matching desired) are
|
|
3630
|
-
* ignored: their reconciliation is `diff`'s concern, not this seam's.
|
|
3631
|
-
*
|
|
3632
|
-
* @param desired - Desired state from `buildDesired`.
|
|
3633
|
-
* @param current - Prior current state from the state port.
|
|
3634
|
-
* @returns `Ok(undefined)` when every paired entry passes its kind-level
|
|
3635
|
-
* reconcilability check, or the first `Err` returned by a hook.
|
|
3636
|
-
*
|
|
3637
|
-
* @example
|
|
3638
|
-
*
|
|
3639
|
-
* ```ts
|
|
3640
|
-
* import { asResourceKey, asRobloxAssetId, asSha256Hex, validatePlan } from "@bedrock-rbx/core";
|
|
3641
|
-
*
|
|
3642
|
-
* const result = validatePlan(
|
|
3643
|
-
* [
|
|
3644
|
-
* {
|
|
3645
|
-
* description: "Stocks the player up with 1,000 premium gems.",
|
|
3646
|
-
* isRegionalPricingEnabled: undefined,
|
|
3647
|
-
* key: asResourceKey("gem-pack"),
|
|
3648
|
-
* kind: "developerProduct",
|
|
3649
|
-
* name: "Gem Pack",
|
|
3650
|
-
* price: undefined,
|
|
3651
|
-
* storePageEnabled: undefined,
|
|
3652
|
-
* },
|
|
3653
|
-
* ],
|
|
3654
|
-
* [
|
|
3655
|
-
* {
|
|
3656
|
-
* description: "Stocks the player up with 1,000 premium gems.",
|
|
3657
|
-
* icon: { "en-us": "assets/gem-pack.png" },
|
|
3658
|
-
* iconFileHashes: {
|
|
3659
|
-
* "en-us": asSha256Hex(
|
|
3660
|
-
* "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
|
3661
|
-
* ),
|
|
3662
|
-
* },
|
|
3663
|
-
* isRegionalPricingEnabled: undefined,
|
|
3664
|
-
* key: asResourceKey("gem-pack"),
|
|
3665
|
-
* kind: "developerProduct",
|
|
3666
|
-
* name: "Gem Pack",
|
|
3667
|
-
* outputs: { productId: asRobloxAssetId("9876543210") },
|
|
3668
|
-
* price: undefined,
|
|
3669
|
-
* storePageEnabled: undefined,
|
|
3670
|
-
* },
|
|
3671
|
-
* ],
|
|
3672
|
-
* );
|
|
3673
|
-
*
|
|
3674
|
-
* expect(result.success).toBeFalse();
|
|
3675
|
-
* if (!result.success) {
|
|
3676
|
-
* expect(result.err.kind).toBe("iconRemovalRejected");
|
|
3677
|
-
* }
|
|
3678
|
-
* ```
|
|
3679
|
-
*/
|
|
3680
|
-
function validatePlan(desired, current) {
|
|
3681
|
-
const collision = detectProductNameCollision(desired);
|
|
3682
|
-
if (collision !== void 0) return collision;
|
|
3683
|
-
const currentByKey = new Map(current.map((entry) => [compositeKey(entry), entry]));
|
|
3684
|
-
for (const entry of desired) {
|
|
3685
|
-
const matched = currentByKey.get(compositeKey(entry));
|
|
3686
|
-
if (matched === void 0) continue;
|
|
3687
|
-
const check = defaultKindRegistry[entry.kind].assertReconcilable?.(matched, entry);
|
|
3688
|
-
if (check !== void 0 && !check.success) return check;
|
|
3689
|
-
}
|
|
3690
|
-
return {
|
|
3691
|
-
data: void 0,
|
|
3692
|
-
success: true
|
|
3693
|
-
};
|
|
3694
|
-
}
|
|
3695
|
-
function compositeKey(resource) {
|
|
3696
|
-
return `${resource.kind}:${resource.key}`;
|
|
3697
|
-
}
|
|
3698
|
-
function detectProductNameCollision(desired) {
|
|
3699
|
-
const seenByName = /* @__PURE__ */ new Map();
|
|
3700
|
-
for (const entry of desired) {
|
|
3701
|
-
if (entry.kind !== "developerProduct") continue;
|
|
3702
|
-
const prior = seenByName.get(entry.name);
|
|
3703
|
-
if (prior === void 0) {
|
|
3704
|
-
seenByName.set(entry.name, entry.key);
|
|
3705
|
-
continue;
|
|
3706
|
-
}
|
|
3707
|
-
return {
|
|
3708
|
-
err: {
|
|
3709
|
-
keys: [prior, entry.key],
|
|
3710
|
-
kind: "redactedNameCollision",
|
|
3711
|
-
message: `developer products '${prior}' and '${entry.key}' both resolve to the wire name '${entry.name}'. Roblox enforces per-universe uniqueness on developer-product names, so the second update would be rejected as DuplicateProductName. Set 'redacted: { name: "<unique>" }' on one of them to disambiguate.`,
|
|
3712
|
-
resolvedName: entry.name
|
|
3713
|
-
},
|
|
3714
|
-
success: false
|
|
3715
|
-
};
|
|
3716
|
-
}
|
|
3717
|
-
}
|
|
3718
|
-
//#endregion
|
|
3719
3887
|
//#region src/shell/apply-ops.ts
|
|
3720
3888
|
/**
|
|
3721
3889
|
* Dispatch reconciliation operations to their matching drivers in two phases
|
|
@@ -4169,7 +4337,7 @@ const STATE_PORT_HINT = "pass a custom statePort via opts.statePort";
|
|
|
4169
4337
|
* const port = buildStatePort({
|
|
4170
4338
|
* fetch: async () =>
|
|
4171
4339
|
* new Response(JSON.stringify({ files: {} }), { status: 200 }),
|
|
4172
|
-
* getEnv: (name) => (name === "
|
|
4340
|
+
* getEnv: (name) => (name === "BEDROCK_GITHUB_TOKEN" ? "ghp_example" : undefined),
|
|
4173
4341
|
* stateConfig: { backend: "gist", gistId: "abc123" },
|
|
4174
4342
|
* });
|
|
4175
4343
|
*
|
|
@@ -4192,12 +4360,12 @@ function buildStatePort(deps) {
|
|
|
4192
4360
|
};
|
|
4193
4361
|
}
|
|
4194
4362
|
function buildGistStatePort(stateConfig, deps) {
|
|
4195
|
-
const token = deps.getEnv("GITHUB_TOKEN");
|
|
4363
|
+
const token = deps.getEnv("BEDROCK_GITHUB_TOKEN") ?? deps.getEnv("GITHUB_TOKEN");
|
|
4196
4364
|
if (token === void 0) return {
|
|
4197
4365
|
err: {
|
|
4198
4366
|
kind: "missingCredential",
|
|
4199
4367
|
purpose: "stateBackend",
|
|
4200
|
-
variable: "
|
|
4368
|
+
variable: "BEDROCK_GITHUB_TOKEN"
|
|
4201
4369
|
},
|
|
4202
4370
|
success: false
|
|
4203
4371
|
};
|
|
@@ -4211,6 +4379,62 @@ function buildGistStatePort(stateConfig, deps) {
|
|
|
4211
4379
|
};
|
|
4212
4380
|
}
|
|
4213
4381
|
//#endregion
|
|
4382
|
+
//#region src/core/assert-all-reconcilable.ts
|
|
4383
|
+
/**
|
|
4384
|
+
* Batch reconcilability check that runs after `buildDesired` and before
|
|
4385
|
+
* `diff`. Walks paired `(kind, key)` entries and dispatches to each
|
|
4386
|
+
* kind module's optional `assertReconcilable` hook so kind-specific
|
|
4387
|
+
* rejections (e.g. Removing a developer-product icon, which the upstream
|
|
4388
|
+
* API has no documented unset path for) surface as typed errors before
|
|
4389
|
+
* `diff` runs and before any apply-side driver I/O is attempted.
|
|
4390
|
+
*
|
|
4391
|
+
* Pure and synchronous. Current-only entries (no matching desired) are
|
|
4392
|
+
* ignored: their reconciliation is `diff`'s concern, not this seam's.
|
|
4393
|
+
*
|
|
4394
|
+
* @param desired - Desired state from `buildDesired`.
|
|
4395
|
+
* @param current - Prior current state from the state port.
|
|
4396
|
+
* @returns `Ok(undefined)` when every paired entry passes its kind-level
|
|
4397
|
+
* reconcilability check, or the first `Err` returned by a hook.
|
|
4398
|
+
*/
|
|
4399
|
+
function assertAllReconcilable(desired, current) {
|
|
4400
|
+
const collision = detectProductNameCollision(desired);
|
|
4401
|
+
if (collision !== void 0) return collision;
|
|
4402
|
+
const currentByKey = new Map(current.map((entry) => [compositeKey(entry), entry]));
|
|
4403
|
+
for (const entry of desired) {
|
|
4404
|
+
const matched = currentByKey.get(compositeKey(entry));
|
|
4405
|
+
if (matched === void 0) continue;
|
|
4406
|
+
const check = defaultKindRegistry[entry.kind].assertReconcilable?.(matched, entry);
|
|
4407
|
+
if (check !== void 0 && !check.success) return check;
|
|
4408
|
+
}
|
|
4409
|
+
return {
|
|
4410
|
+
data: void 0,
|
|
4411
|
+
success: true
|
|
4412
|
+
};
|
|
4413
|
+
}
|
|
4414
|
+
function compositeKey(resource) {
|
|
4415
|
+
return `${resource.kind}:${resource.key}`;
|
|
4416
|
+
}
|
|
4417
|
+
function detectProductNameCollision(desired) {
|
|
4418
|
+
const seenByName = /* @__PURE__ */ new Map();
|
|
4419
|
+
for (const entry of desired) {
|
|
4420
|
+
if (entry.kind !== "developerProduct") continue;
|
|
4421
|
+
const prior = seenByName.get(entry.name);
|
|
4422
|
+
if (prior === void 0) {
|
|
4423
|
+
seenByName.set(entry.name, entry.key);
|
|
4424
|
+
continue;
|
|
4425
|
+
}
|
|
4426
|
+
return {
|
|
4427
|
+
err: {
|
|
4428
|
+
keys: [prior, entry.key],
|
|
4429
|
+
kind: "redactedNameCollision",
|
|
4430
|
+
message: `developer products '${prior}' and '${entry.key}' both resolve to the wire name '${entry.name}'. Roblox enforces per-universe uniqueness on developer-product names, so the second update would be rejected as DuplicateProductName. Set 'redacted: { name: "<unique>" }' on one of them to disambiguate.`,
|
|
4431
|
+
resolvedName: entry.name
|
|
4432
|
+
},
|
|
4433
|
+
success: false
|
|
4434
|
+
};
|
|
4435
|
+
}
|
|
4436
|
+
}
|
|
4437
|
+
//#endregion
|
|
4214
4438
|
//#region src/shell/load-config-internal.ts
|
|
4215
4439
|
const LUAU_BOOTSTRAP_TEMP_PREFIX = "bedrock-luau-bootstrap-";
|
|
4216
4440
|
/**
|
|
@@ -4584,10 +4808,26 @@ function attributeLoadError(err, cwd) {
|
|
|
4584
4808
|
//#endregion
|
|
4585
4809
|
//#region src/shell/deploy.ts
|
|
4586
4810
|
/**
|
|
4811
|
+
* Decide whether `BEDROCK_CLI` should select the clack-backed default
|
|
4812
|
+
* progress adapter. Exported for direct unit coverage of the boundary
|
|
4813
|
+
* (`undefined` and empty string both flip to no-op; any non-empty value
|
|
4814
|
+
* picks clack).
|
|
4815
|
+
*
|
|
4816
|
+
* @param value - Raw `BEDROCK_CLI` value as returned by `getEnv`.
|
|
4817
|
+
* @returns `true` if the clack adapter should be the default.
|
|
4818
|
+
*/
|
|
4819
|
+
function isCliEnvironmentFlagSet(value) {
|
|
4820
|
+
return value !== void 0 && value !== "";
|
|
4821
|
+
}
|
|
4822
|
+
/**
|
|
4587
4823
|
* Run a full reconcile end-to-end. Default-constructs missing deps from
|
|
4588
|
-
* the project config and the environment variables `
|
|
4589
|
-
* `BEDROCK_API_KEY`;
|
|
4590
|
-
*
|
|
4824
|
+
* the project config and the environment variables `BEDROCK_GITHUB_TOKEN`
|
|
4825
|
+
* and `BEDROCK_API_KEY`; emits a terminal `deploySuccess` or `deployFailure`
|
|
4826
|
+
* event through the resolved `progress` port. When `progress` is omitted,
|
|
4827
|
+
* the default port comes from `BEDROCK_CLI`: a non-empty value selects the
|
|
4828
|
+
* clack-backed adapter, any other reading selects the no-op adapter. No
|
|
4829
|
+
* environment lookups happen when `statePort`, `registry`, `config`, and
|
|
4830
|
+
* `progress` are all supplied explicitly.
|
|
4591
4831
|
*
|
|
4592
4832
|
* @param options - Target environment plus optional overrides.
|
|
4593
4833
|
* @returns The persisted `BedrockState` on success, or a stage-tagged
|
|
@@ -4648,9 +4888,15 @@ function attributeLoadError(err, cwd) {
|
|
|
4648
4888
|
* ```
|
|
4649
4889
|
*/
|
|
4650
4890
|
async function deploy(options) {
|
|
4651
|
-
|
|
4652
|
-
if (!
|
|
4653
|
-
return
|
|
4891
|
+
if (options.progress !== void 0) return runAndEmit(options, options.progress);
|
|
4892
|
+
if (!isCliEnvironmentFlagSet(getEnvironmentOf(options)("BEDROCK_CLI"))) return runAndEmit(options, createNoOpProgressAdapter());
|
|
4893
|
+
return runWithDeferredClackProgress(options);
|
|
4894
|
+
}
|
|
4895
|
+
function readProcessEnvironment(name) {
|
|
4896
|
+
return process.env[name];
|
|
4897
|
+
}
|
|
4898
|
+
function getEnvironmentOf(options) {
|
|
4899
|
+
return options.getEnv ?? readProcessEnvironment;
|
|
4654
4900
|
}
|
|
4655
4901
|
async function pickConfig(options) {
|
|
4656
4902
|
if (options.config !== void 0) return {
|
|
@@ -4670,12 +4916,6 @@ async function pickConfig(options) {
|
|
|
4670
4916
|
success: true
|
|
4671
4917
|
};
|
|
4672
4918
|
}
|
|
4673
|
-
function readProcessEnvironment(name) {
|
|
4674
|
-
return process.env[name];
|
|
4675
|
-
}
|
|
4676
|
-
function getEnvironmentOf(options) {
|
|
4677
|
-
return options.getEnv ?? readProcessEnvironment;
|
|
4678
|
-
}
|
|
4679
4919
|
function pickStatePort(options, config) {
|
|
4680
4920
|
if (options.statePort !== void 0) return {
|
|
4681
4921
|
data: options.statePort,
|
|
@@ -4724,7 +4964,6 @@ async function resolveDeps(options) {
|
|
|
4724
4964
|
return {
|
|
4725
4965
|
data: {
|
|
4726
4966
|
config: effective,
|
|
4727
|
-
progress: options.progress,
|
|
4728
4967
|
readFile: readFile$2,
|
|
4729
4968
|
registry: registry.data,
|
|
4730
4969
|
statePort: statePort.data
|
|
@@ -4785,7 +5024,7 @@ async function runReconcile(environment, deps) {
|
|
|
4785
5024
|
success: false
|
|
4786
5025
|
};
|
|
4787
5026
|
const priorResources = prior.data?.resources ?? [];
|
|
4788
|
-
const validated =
|
|
5027
|
+
const validated = assertAllReconcilable(desired.data, priorResources);
|
|
4789
5028
|
if (!validated.success) return {
|
|
4790
5029
|
err: {
|
|
4791
5030
|
cause: validated.err,
|
|
@@ -4793,7 +5032,7 @@ async function runReconcile(environment, deps) {
|
|
|
4793
5032
|
},
|
|
4794
5033
|
success: false
|
|
4795
5034
|
};
|
|
4796
|
-
const applied = await applyOps(diff(desired.data, priorResources), deps.registry,
|
|
5035
|
+
const applied = await applyOps(diff(desired.data, priorResources), deps.registry, {
|
|
4797
5036
|
environment,
|
|
4798
5037
|
progress: deps.progress
|
|
4799
5038
|
});
|
|
@@ -4803,7 +5042,7 @@ async function runReconcile(environment, deps) {
|
|
|
4803
5042
|
priorResources
|
|
4804
5043
|
});
|
|
4805
5044
|
const written = await deps.statePort.write(merged);
|
|
4806
|
-
if (written.success) deps.progress
|
|
5045
|
+
if (written.success) deps.progress.emit({
|
|
4807
5046
|
environment,
|
|
4808
5047
|
kind: "stateWritten"
|
|
4809
5048
|
});
|
|
@@ -4813,6 +5052,61 @@ async function runReconcile(environment, deps) {
|
|
|
4813
5052
|
written
|
|
4814
5053
|
});
|
|
4815
5054
|
}
|
|
5055
|
+
async function runDeploy(options, progress) {
|
|
5056
|
+
const resolved = await resolveDeps(options);
|
|
5057
|
+
if (!resolved.success) return resolved;
|
|
5058
|
+
return runReconcile(options.environment, {
|
|
5059
|
+
...resolved.data,
|
|
5060
|
+
progress
|
|
5061
|
+
});
|
|
5062
|
+
}
|
|
5063
|
+
function emitTerminalEvent(inputs) {
|
|
5064
|
+
const { environment, progress, result } = inputs;
|
|
5065
|
+
if (result.success) {
|
|
5066
|
+
progress.emit({
|
|
5067
|
+
environment,
|
|
5068
|
+
kind: "deploySuccess",
|
|
5069
|
+
resourceCount: result.data.resources.length
|
|
5070
|
+
});
|
|
5071
|
+
return;
|
|
5072
|
+
}
|
|
5073
|
+
progress.emit({
|
|
5074
|
+
environment,
|
|
5075
|
+
error: result.err,
|
|
5076
|
+
kind: "deployFailure"
|
|
5077
|
+
});
|
|
5078
|
+
}
|
|
5079
|
+
async function runAndEmit(options, progress) {
|
|
5080
|
+
const result = await runDeploy(options, progress);
|
|
5081
|
+
emitTerminalEvent({
|
|
5082
|
+
environment: options.environment,
|
|
5083
|
+
progress,
|
|
5084
|
+
result
|
|
5085
|
+
});
|
|
5086
|
+
return result;
|
|
5087
|
+
}
|
|
5088
|
+
async function runWithDeferredClackProgress(options) {
|
|
5089
|
+
const resolved = await resolveDeps(options);
|
|
5090
|
+
const progress = createDefaultProgressAdapter(resolved.success ? resolved.data.config : options.config);
|
|
5091
|
+
if (!resolved.success) {
|
|
5092
|
+
emitTerminalEvent({
|
|
5093
|
+
environment: options.environment,
|
|
5094
|
+
progress,
|
|
5095
|
+
result: resolved
|
|
5096
|
+
});
|
|
5097
|
+
return resolved;
|
|
5098
|
+
}
|
|
5099
|
+
const result = await runReconcile(options.environment, {
|
|
5100
|
+
...resolved.data,
|
|
5101
|
+
progress
|
|
5102
|
+
});
|
|
5103
|
+
emitTerminalEvent({
|
|
5104
|
+
environment: options.environment,
|
|
5105
|
+
progress,
|
|
5106
|
+
result
|
|
5107
|
+
});
|
|
5108
|
+
return result;
|
|
5109
|
+
}
|
|
4816
5110
|
//#endregion
|
|
4817
5111
|
//#region src/core/migrate/build-state.ts
|
|
4818
5112
|
/**
|
|
@@ -6963,6 +7257,6 @@ function isFileMissing(err) {
|
|
|
6963
7257
|
return typeof err === "object" && err !== null && "code" in err && typeof err.code === "string" && FILE_MISSING_CODES.has(err.code);
|
|
6964
7258
|
}
|
|
6965
7259
|
//#endregion
|
|
6966
|
-
export {
|
|
7260
|
+
export { renderStateWriteError as $, createGamePassDriver as A, asSha256Hex as B, createPlaceDriver as C, createGistStateAdapter as D, createNoOpProgressAdapter as E, validateConfig as F, renderBuildStatePortError as G, isRobloxAssetId as H, shouldReuploadIcon as I, renderMigrateParseError as J, renderDeployError as K, validateEnvironmentName as L, derivePriceFields as M, createClackProgressAdapter as N, parseStateFile as O, isGistStateConfig as P, renderParseError as Q, asResourceKey as R, createUniverseDriver as S, UNIVERSE_SINGLETON_KEY as T, isSha256Hex as U, isResourceKey as V, resolveStateConfig as W, renderOverrideDiscoveryError as X, renderMigrationSummary as Y, renderOverrideError as Z, diff as _, assertAllReconcilable as a, buildCredentialOverrides as b, buildDefaultRegistry as c, selectEnvironment as d, createClackPort as et, selectMergedEnvironment as f, renderDisplayNamePrefix as g, DEFAULT_PREFIX_FORMAT as h, loadConfig$1 as i, createDeveloperProductDriver as j, serializeStateFile as k, applyOps as l, flattenConfig as m, serializeConfig as n, buildStatePort as o, collectRedactionAnnotations as p, renderMigrateError as q, deploy as r, buildDesired as s, migrateMantleState as t, extractResourceRedaction as u, defaultKindRegistry as v, SOCIAL_LINK_FIELDS as w, createDefaultSpawner as x, dispatchOverride as y, asRobloxAssetId as z };
|
|
6967
7261
|
|
|
6968
|
-
//# sourceMappingURL=migrate-mantle-state-
|
|
7262
|
+
//# sourceMappingURL=migrate-mantle-state-ClQ40EFD.mjs.map
|