@stardeck-customer-apps/compose 0.14.1 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SKILL.md +16 -0
- package/dist/cli.js +709 -416
- package/dist/index.js +177 -154
- package/dist/index.mjs +177 -154
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1145,16 +1145,16 @@ var require_range = __commonJS({
|
|
|
1145
1145
|
};
|
|
1146
1146
|
var replaceTilde = (comp, options) => {
|
|
1147
1147
|
const r = options.loose ? re[t.TILDELOOSE] : re[t.TILDE];
|
|
1148
|
-
const
|
|
1148
|
+
const z11 = options.includePrerelease ? "-0" : "";
|
|
1149
1149
|
return comp.replace(r, (_, M, m, p, pr) => {
|
|
1150
1150
|
debug("tilde", comp, _, M, m, p, pr);
|
|
1151
1151
|
let ret;
|
|
1152
1152
|
if (isX(M)) {
|
|
1153
1153
|
ret = "";
|
|
1154
1154
|
} else if (isX(m)) {
|
|
1155
|
-
ret = `>=${M}.0.0${
|
|
1155
|
+
ret = `>=${M}.0.0${z11} <${+M + 1}.0.0-0`;
|
|
1156
1156
|
} else if (isX(p)) {
|
|
1157
|
-
ret = `>=${M}.${m}.0${
|
|
1157
|
+
ret = `>=${M}.${m}.0${z11} <${M}.${+m + 1}.0-0`;
|
|
1158
1158
|
} else if (pr) {
|
|
1159
1159
|
debug("replaceTilde pr", pr);
|
|
1160
1160
|
ret = `>=${M}.${m}.${p}-${pr} <${M}.${+m + 1}.0-0`;
|
|
@@ -1171,19 +1171,19 @@ var require_range = __commonJS({
|
|
|
1171
1171
|
var replaceCaret = (comp, options) => {
|
|
1172
1172
|
debug("caret", comp, options);
|
|
1173
1173
|
const r = options.loose ? re[t.CARETLOOSE] : re[t.CARET];
|
|
1174
|
-
const
|
|
1174
|
+
const z11 = options.includePrerelease ? "-0" : "";
|
|
1175
1175
|
return comp.replace(r, (_, M, m, p, pr) => {
|
|
1176
1176
|
debug("caret", comp, _, M, m, p, pr);
|
|
1177
1177
|
let ret;
|
|
1178
1178
|
if (isX(M)) {
|
|
1179
1179
|
ret = "";
|
|
1180
1180
|
} else if (isX(m)) {
|
|
1181
|
-
ret = `>=${M}.0.0${
|
|
1181
|
+
ret = `>=${M}.0.0${z11} <${+M + 1}.0.0-0`;
|
|
1182
1182
|
} else if (isX(p)) {
|
|
1183
1183
|
if (M === "0") {
|
|
1184
|
-
ret = `>=${M}.${m}.0${
|
|
1184
|
+
ret = `>=${M}.${m}.0${z11} <${M}.${+m + 1}.0-0`;
|
|
1185
1185
|
} else {
|
|
1186
|
-
ret = `>=${M}.${m}.0${
|
|
1186
|
+
ret = `>=${M}.${m}.0${z11} <${+M + 1}.0.0-0`;
|
|
1187
1187
|
}
|
|
1188
1188
|
} else if (pr) {
|
|
1189
1189
|
debug("replaceCaret pr", pr);
|
|
@@ -2021,8 +2021,13 @@ var require_semver2 = __commonJS({
|
|
|
2021
2021
|
});
|
|
2022
2022
|
|
|
2023
2023
|
// src/cli.ts
|
|
2024
|
+
var import_node_child_process3 = require("child_process");
|
|
2024
2025
|
var import_node_util = require("util");
|
|
2025
2026
|
|
|
2027
|
+
// ../../packages/lib/src/server/module-rail/ownership-guard.ts
|
|
2028
|
+
var import_node_fs = __toESM(require("fs"));
|
|
2029
|
+
var import_node_path = __toESM(require("path"));
|
|
2030
|
+
|
|
2026
2031
|
// ../../packages/module-contract/src/module-manifest.ts
|
|
2027
2032
|
var import_zod = require("zod");
|
|
2028
2033
|
|
|
@@ -2053,6 +2058,13 @@ function validateSlug(slug) {
|
|
|
2053
2058
|
var MODULES_DIR = "src/modules";
|
|
2054
2059
|
var APP_PACKAGE_DIR = "apps/web";
|
|
2055
2060
|
var MODULES_SANDBOX_DIR = `${APP_PACKAGE_DIR}/${MODULES_DIR}`;
|
|
2061
|
+
var MODULE_MANIFEST_FILENAME = "module.json";
|
|
2062
|
+
var MODULE_UI_SUBPATHS = [
|
|
2063
|
+
"components/",
|
|
2064
|
+
"routes/",
|
|
2065
|
+
"enhancements/*/components/",
|
|
2066
|
+
"enhancements/*/routes/"
|
|
2067
|
+
];
|
|
2056
2068
|
var KEBAB_SLUG_REGEX = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
|
|
2057
2069
|
var SNAKE_TABLE_REGEX = /^[a-z][a-z0-9_]*$/;
|
|
2058
2070
|
var ROUTE_GROUP_REGEX = /^\([a-z0-9-]+\)$/;
|
|
@@ -2066,23 +2078,23 @@ function isValidModuleName(value) {
|
|
|
2066
2078
|
var projectSlugSchema = import_zod.z.string().refine((value) => validateSlug(value).valid, "must be a valid project slug");
|
|
2067
2079
|
var moduleKindSchema = import_zod.z.enum(["feature", "library", "contract"]);
|
|
2068
2080
|
var endpointMethodSchema = import_zod.z.enum(ENDPOINT_METHODS);
|
|
2069
|
-
function endpointPathValidationError(
|
|
2070
|
-
if (!
|
|
2081
|
+
function endpointPathValidationError(path6) {
|
|
2082
|
+
if (!path6.startsWith("/api/")) {
|
|
2071
2083
|
return 'must begin with "/api/"';
|
|
2072
2084
|
}
|
|
2073
|
-
if (
|
|
2085
|
+
if (path6.endsWith("/")) {
|
|
2074
2086
|
return "must not end with a trailing slash";
|
|
2075
2087
|
}
|
|
2076
|
-
if (
|
|
2088
|
+
if (path6.includes("?") || path6.includes("#")) {
|
|
2077
2089
|
return "must not include query or hash";
|
|
2078
2090
|
}
|
|
2079
|
-
if (
|
|
2091
|
+
if (path6.includes("\\")) {
|
|
2080
2092
|
return "must not include backslashes";
|
|
2081
2093
|
}
|
|
2082
|
-
if (
|
|
2094
|
+
if (path6.includes("%")) {
|
|
2083
2095
|
return "must not include percent-encoded segments";
|
|
2084
2096
|
}
|
|
2085
|
-
const parts =
|
|
2097
|
+
const parts = path6.split("/");
|
|
2086
2098
|
if (parts.length < 3 || parts[0] !== "" || parts[1] !== "api") {
|
|
2087
2099
|
return 'must begin with "/api/"';
|
|
2088
2100
|
}
|
|
@@ -2132,12 +2144,12 @@ function endpointPathValidationError(path5) {
|
|
|
2132
2144
|
}
|
|
2133
2145
|
return null;
|
|
2134
2146
|
}
|
|
2135
|
-
function normalizeEndpointPathForCollision(
|
|
2136
|
-
const error = endpointPathValidationError(
|
|
2147
|
+
function normalizeEndpointPathForCollision(path6) {
|
|
2148
|
+
const error = endpointPathValidationError(path6);
|
|
2137
2149
|
if (error != null) {
|
|
2138
2150
|
throw new Error(`invalid endpoint path: ${error}`);
|
|
2139
2151
|
}
|
|
2140
|
-
return
|
|
2152
|
+
return path6.split("/").map((segment, index) => {
|
|
2141
2153
|
if (index < 2) {
|
|
2142
2154
|
return segment;
|
|
2143
2155
|
}
|
|
@@ -2150,8 +2162,8 @@ function normalizeEndpointPathForCollision(path5) {
|
|
|
2150
2162
|
return segment;
|
|
2151
2163
|
}).join("/");
|
|
2152
2164
|
}
|
|
2153
|
-
var endpointPathSchema = import_zod.z.string().superRefine((
|
|
2154
|
-
const error = endpointPathValidationError(
|
|
2165
|
+
var endpointPathSchema = import_zod.z.string().superRefine((path6, ctx) => {
|
|
2166
|
+
const error = endpointPathValidationError(path6);
|
|
2155
2167
|
if (error != null) {
|
|
2156
2168
|
ctx.addIssue({ code: import_zod.z.ZodIssueCode.custom, message: error });
|
|
2157
2169
|
}
|
|
@@ -2414,16 +2426,16 @@ var moduleManifestSchema = import_zod.z.object({
|
|
|
2414
2426
|
}
|
|
2415
2427
|
}
|
|
2416
2428
|
const seenWorkflows = /* @__PURE__ */ new Map();
|
|
2417
|
-
const noteWorkflow = (name,
|
|
2429
|
+
const noteWorkflow = (name, path6) => {
|
|
2418
2430
|
const existing = seenWorkflows.get(name);
|
|
2419
2431
|
if (existing != null) {
|
|
2420
2432
|
ctx.addIssue({
|
|
2421
2433
|
code: import_zod.z.ZodIssueCode.custom,
|
|
2422
2434
|
message: `duplicate workflow name "${name}" (also at ${existing})`,
|
|
2423
|
-
path:
|
|
2435
|
+
path: path6
|
|
2424
2436
|
});
|
|
2425
2437
|
} else {
|
|
2426
|
-
seenWorkflows.set(name,
|
|
2438
|
+
seenWorkflows.set(name, path6.join("."));
|
|
2427
2439
|
}
|
|
2428
2440
|
};
|
|
2429
2441
|
for (let i = 0; i < manifest.workflows.length; i += 1) {
|
|
@@ -2435,7 +2447,7 @@ var moduleManifestSchema = import_zod.z.object({
|
|
|
2435
2447
|
}
|
|
2436
2448
|
}
|
|
2437
2449
|
const seenEndpointKeys = /* @__PURE__ */ new Map();
|
|
2438
|
-
const noteEndpoint = (claim,
|
|
2450
|
+
const noteEndpoint = (claim, path6) => {
|
|
2439
2451
|
let key;
|
|
2440
2452
|
try {
|
|
2441
2453
|
key = normalizeEndpointPathForCollision(claim.path);
|
|
@@ -2447,10 +2459,10 @@ var moduleManifestSchema = import_zod.z.object({
|
|
|
2447
2459
|
ctx.addIssue({
|
|
2448
2460
|
code: import_zod.z.ZodIssueCode.custom,
|
|
2449
2461
|
message: `duplicate normalized endpoint path "${key}" (also at ${existing})`,
|
|
2450
|
-
path:
|
|
2462
|
+
path: path6
|
|
2451
2463
|
});
|
|
2452
2464
|
} else {
|
|
2453
|
-
seenEndpointKeys.set(key,
|
|
2465
|
+
seenEndpointKeys.set(key, path6.join("."));
|
|
2454
2466
|
}
|
|
2455
2467
|
};
|
|
2456
2468
|
for (let i = 0; i < manifest.endpoints.length; i += 1) {
|
|
@@ -2551,8 +2563,8 @@ var moduleManifestSchema = import_zod.z.object({
|
|
|
2551
2563
|
}).transform(({ version: _legacyVersion, ...manifest }) => manifest);
|
|
2552
2564
|
function formatZodErrors(error) {
|
|
2553
2565
|
return error.issues.map((issue) => {
|
|
2554
|
-
const
|
|
2555
|
-
return `${
|
|
2566
|
+
const path6 = issue.path.length > 0 ? issue.path.join(".") : "root";
|
|
2567
|
+
return `${path6}: ${issue.message}`;
|
|
2556
2568
|
});
|
|
2557
2569
|
}
|
|
2558
2570
|
function parseModuleManifest(raw) {
|
|
@@ -2952,16 +2964,16 @@ var collectedE2eFactsSchema = import_zod3.z.object({
|
|
|
2952
2964
|
function validateRouteExportEvidenceCongruence(input) {
|
|
2953
2965
|
const entrySet = new Set(input.routeEntries);
|
|
2954
2966
|
const exportPaths = input.routePageExports.map((e) => e.nextRelativePath);
|
|
2955
|
-
for (const
|
|
2956
|
-
if (!entrySet.has(
|
|
2957
|
-
input.onIssue(`routePageExports path "${
|
|
2967
|
+
for (const path6 of exportPaths) {
|
|
2968
|
+
if (!entrySet.has(path6)) {
|
|
2969
|
+
input.onIssue(`routePageExports path "${path6}" is not listed in routeEntries`);
|
|
2958
2970
|
}
|
|
2959
2971
|
}
|
|
2960
2972
|
if (input.routeCollectionErrors.length === 0) {
|
|
2961
2973
|
const exportSet = new Set(exportPaths);
|
|
2962
|
-
for (const
|
|
2963
|
-
if (!exportSet.has(
|
|
2964
|
-
input.onIssue(`routeEntries path "${
|
|
2974
|
+
for (const path6 of input.routeEntries) {
|
|
2975
|
+
if (!exportSet.has(path6)) {
|
|
2976
|
+
input.onIssue(`routeEntries path "${path6}" is missing from routePageExports`);
|
|
2965
2977
|
}
|
|
2966
2978
|
}
|
|
2967
2979
|
}
|
|
@@ -3906,6 +3918,50 @@ function analyzeClientSafeEntryImports(sourceText, fileName) {
|
|
|
3906
3918
|
return analyzeClientSafeEntryImportsWithTs(import_typescript3.default, sourceText, fileName);
|
|
3907
3919
|
}
|
|
3908
3920
|
|
|
3921
|
+
// ../../packages/lib/src/shared/ownership-record.ts
|
|
3922
|
+
var import_zod6 = require("zod");
|
|
3923
|
+
var OWNERSHIP_RECORD_PATH = "stardeck.ownership.json";
|
|
3924
|
+
var OWNERSHIP_RECORD_VERSION = 1;
|
|
3925
|
+
var moduleList = import_zod6.z.array(import_zod6.z.string().refine(isValidModuleName, "not a Module name"));
|
|
3926
|
+
var ownershipRecordSchema = import_zod6.z.object({
|
|
3927
|
+
version: import_zod6.z.literal(OWNERSHIP_RECORD_VERSION),
|
|
3928
|
+
/** False once the app stopped all Stardeck updates: then every file is the app's. */
|
|
3929
|
+
stardeckUpdates: import_zod6.z.boolean(),
|
|
3930
|
+
/** projects.ejected_modules: updates stopped for these Modules, so their folders are the app's. */
|
|
3931
|
+
updatesStoppedModules: moduleList,
|
|
3932
|
+
/** projects.ui_ejected_modules: Customize UI, so their screens are the app's. */
|
|
3933
|
+
customizedUiModules: moduleList,
|
|
3934
|
+
/** projects.external_modules: installed from another app, so the app owns the copy. */
|
|
3935
|
+
externalModules: moduleList,
|
|
3936
|
+
/** projects.removed_modules names: uninstalled on purpose. */
|
|
3937
|
+
uninstalledModules: moduleList
|
|
3938
|
+
});
|
|
3939
|
+
function parseOwnershipRecord(content) {
|
|
3940
|
+
let json;
|
|
3941
|
+
try {
|
|
3942
|
+
json = JSON.parse(content);
|
|
3943
|
+
} catch (error) {
|
|
3944
|
+
return { ok: false, error: `${OWNERSHIP_RECORD_PATH} is not valid JSON: ${String(error)}` };
|
|
3945
|
+
}
|
|
3946
|
+
const parsed = ownershipRecordSchema.safeParse(json);
|
|
3947
|
+
if (!parsed.success) {
|
|
3948
|
+
return {
|
|
3949
|
+
ok: false,
|
|
3950
|
+
error: `${OWNERSHIP_RECORD_PATH} is not a record Stardeck wrote: ${parsed.error.issues.map((issue) => `${issue.path.join(".") || "(root)"}: ${issue.message}`).join("; ")}`
|
|
3951
|
+
};
|
|
3952
|
+
}
|
|
3953
|
+
return { ok: true, record: parsed.data };
|
|
3954
|
+
}
|
|
3955
|
+
var BLUEPRINT_RELEASE_COMMITTER_EMAIL = "ci@stardeck.ai";
|
|
3956
|
+
var BLUEPRINT_RELEASE_SUBJECT_PREFIX = "publish: ";
|
|
3957
|
+
var STARDECK_OWNED_ALTERNATIVES = [
|
|
3958
|
+
"If the Module has a setting for this, change the setting.",
|
|
3959
|
+
"If a slot contribution can do it, write it in app code outside the Module folder (`apps/web/src/lib/modules/app-extensions.ts` registers it).",
|
|
3960
|
+
"To rework a Module's screens, the app owner can choose **Customize UI** in the dashboard's Modules tab (run **Sync** there first if it is refused). The app then owns that Module's `components/` and `routes/`, and its logic keeps updating.",
|
|
3961
|
+
"If the Module's logic is wrong for every app, report it to Stardeck so every app gets the fix.",
|
|
3962
|
+
"If the app cannot wait, the app owner can stop Stardeck updates for this Module, or for the whole app, in the Modules tab. Both are permanent."
|
|
3963
|
+
];
|
|
3964
|
+
|
|
3909
3965
|
// ../../packages/lib/src/shared/platform-owned-paths.ts
|
|
3910
3966
|
var APP_WEB_PREFIX = "apps/web/";
|
|
3911
3967
|
var BLUEPRINT_LINT_MARKER = `${APP_WEB_PREFIX}eslint-rules/module-boundaries.mjs`;
|
|
@@ -3919,20 +3975,217 @@ var MODULE_INIT_SERVER_GEN_PATH = `${APP_WEB_PREFIX}src/module-init.server.gen.t
|
|
|
3919
3975
|
var MODULE_SERVER_GEN_PATH = `${APP_WEB_PREFIX}src/module-server.gen.ts`;
|
|
3920
3976
|
var MODULES_GEN_PATH = `${APP_WEB_PREFIX}src/modules.gen.ts`;
|
|
3921
3977
|
var APP_EXTENSIONS_PATH = `${APP_WEB_PREFIX}src/lib/modules/app-extensions.ts`;
|
|
3978
|
+
var APP_OWNED_PATHS = [APP_EXTENSIONS_PATH];
|
|
3979
|
+
function platformOwnedSet(input) {
|
|
3980
|
+
const externalModules = input.externalModules ?? [];
|
|
3981
|
+
const removedModules = input.removedModules ?? [];
|
|
3982
|
+
const moduleNames = [
|
|
3983
|
+
...input.moduleNames,
|
|
3984
|
+
...input.ejectedModules,
|
|
3985
|
+
...input.uiEjectedModules,
|
|
3986
|
+
...externalModules,
|
|
3987
|
+
...removedModules
|
|
3988
|
+
];
|
|
3989
|
+
for (const moduleName of moduleNames) {
|
|
3990
|
+
if (!isValidModuleName(moduleName)) {
|
|
3991
|
+
throw new Error(`Invalid Module name: ${moduleName}`);
|
|
3992
|
+
}
|
|
3993
|
+
}
|
|
3994
|
+
const appOwned = /* @__PURE__ */ new Set([...input.ejectedModules, ...externalModules]);
|
|
3995
|
+
const activeModules = input.moduleNames.filter((name) => !appOwned.has(name));
|
|
3996
|
+
const uiEjectedModules = new Set(input.uiEjectedModules);
|
|
3997
|
+
return {
|
|
3998
|
+
prefixes: [
|
|
3999
|
+
...activeModules.map((name) => `${APP_WEB_PREFIX}src/modules/${name}/`),
|
|
4000
|
+
`${APP_WEB_PREFIX}src/lib/modules/`
|
|
4001
|
+
].sort(),
|
|
4002
|
+
// Build outputs, not source: compose regenerates them from Module source on
|
|
4003
|
+
// every dev/build/lint/typecheck/test run and git ignores them, so there is
|
|
4004
|
+
// nothing for the write refusal or the Blueprint updater to protect.
|
|
4005
|
+
files: [],
|
|
4006
|
+
// Detach dominates: a detached Module has no prefix, so it needs no carve-out.
|
|
4007
|
+
carveOuts: activeModules.filter((name) => uiEjectedModules.has(name)).flatMap(
|
|
4008
|
+
(name) => MODULE_UI_SUBPATHS.map((subpath) => `${APP_WEB_PREFIX}src/modules/${name}/${subpath}`)
|
|
4009
|
+
).sort(),
|
|
4010
|
+
externalModules: [...externalModules].sort(),
|
|
4011
|
+
removedModules: [...removedModules].sort()
|
|
4012
|
+
};
|
|
4013
|
+
}
|
|
4014
|
+
function appOwnedExcludePathspecs(set) {
|
|
4015
|
+
return [
|
|
4016
|
+
...APP_OWNED_PATHS.map((file) => `:(exclude)${file}`),
|
|
4017
|
+
...set.carveOuts.map((carveOut) => `:(glob,exclude)${carveOut}**`)
|
|
4018
|
+
];
|
|
4019
|
+
}
|
|
4020
|
+
function platformOwnedDiffPathspecs(set) {
|
|
4021
|
+
return [...set.prefixes, ...set.files, ...appOwnedExcludePathspecs(set)];
|
|
4022
|
+
}
|
|
4023
|
+
|
|
4024
|
+
// ../../packages/lib/src/server/module-rail/ownership-guard.ts
|
|
4025
|
+
var STRICT_RECORD = {
|
|
4026
|
+
version: 1,
|
|
4027
|
+
stardeckUpdates: true,
|
|
4028
|
+
updatesStoppedModules: [],
|
|
4029
|
+
customizedUiModules: [],
|
|
4030
|
+
externalModules: [],
|
|
4031
|
+
uninstalledModules: []
|
|
4032
|
+
};
|
|
4033
|
+
var MANIFEST_PATH = new RegExp(
|
|
4034
|
+
`^${MODULES_SANDBOX_DIR.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}/([^/]+)/${MODULE_MANIFEST_FILENAME.replace(".", "\\.")}$`
|
|
4035
|
+
);
|
|
4036
|
+
function realpath(dir) {
|
|
4037
|
+
try {
|
|
4038
|
+
return import_node_fs.default.realpathSync(dir);
|
|
4039
|
+
} catch {
|
|
4040
|
+
return import_node_path.default.resolve(dir);
|
|
4041
|
+
}
|
|
4042
|
+
}
|
|
4043
|
+
function zList(stdout) {
|
|
4044
|
+
return stdout.split("\0").filter(Boolean);
|
|
4045
|
+
}
|
|
4046
|
+
async function findRelease(git) {
|
|
4047
|
+
const found = await git([
|
|
4048
|
+
"rev-list",
|
|
4049
|
+
"-n1",
|
|
4050
|
+
"--topo-order",
|
|
4051
|
+
`--committer=${BLUEPRINT_RELEASE_COMMITTER_EMAIL.replace(/\./g, "\\.")}`,
|
|
4052
|
+
`--grep=^${BLUEPRINT_RELEASE_SUBJECT_PREFIX}`,
|
|
4053
|
+
"HEAD"
|
|
4054
|
+
]);
|
|
4055
|
+
const sha = found.status === 0 ? found.stdout.trim() : "";
|
|
4056
|
+
return sha || null;
|
|
4057
|
+
}
|
|
4058
|
+
async function noReleaseReason(git) {
|
|
4059
|
+
const shallow = await git(["rev-parse", "--is-shallow-repository"]);
|
|
4060
|
+
if (shallow.status === 0 && shallow.stdout.trim() === "true") {
|
|
4061
|
+
return {
|
|
4062
|
+
status: "skipped",
|
|
4063
|
+
warning: true,
|
|
4064
|
+
reason: "this is a shallow clone, so Stardeck's version of the Module files is not in its history. Run `git fetch --unshallow` and lint again to check that no Stardeck-owned file changed."
|
|
4065
|
+
};
|
|
4066
|
+
}
|
|
4067
|
+
return {
|
|
4068
|
+
status: "skipped",
|
|
4069
|
+
warning: false,
|
|
4070
|
+
reason: "no Stardeck release in this repo's history, so it holds no Stardeck-owned files"
|
|
4071
|
+
};
|
|
4072
|
+
}
|
|
4073
|
+
function readRecord(repoRoot) {
|
|
4074
|
+
let content;
|
|
4075
|
+
try {
|
|
4076
|
+
content = import_node_fs.default.readFileSync(import_node_path.default.join(repoRoot, OWNERSHIP_RECORD_PATH), "utf8");
|
|
4077
|
+
} catch (error) {
|
|
4078
|
+
if (error.code === "ENOENT") {
|
|
4079
|
+
return { ok: true, record: STRICT_RECORD, found: false };
|
|
4080
|
+
}
|
|
4081
|
+
throw error;
|
|
4082
|
+
}
|
|
4083
|
+
const parsed = parseOwnershipRecord(content);
|
|
4084
|
+
return parsed.ok ? { ok: true, record: parsed.record, found: true } : parsed;
|
|
4085
|
+
}
|
|
4086
|
+
async function releaseModules(git, release, record) {
|
|
4087
|
+
const listed = await git([
|
|
4088
|
+
"ls-tree",
|
|
4089
|
+
"-r",
|
|
4090
|
+
"-z",
|
|
4091
|
+
"--name-only",
|
|
4092
|
+
release,
|
|
4093
|
+
"--",
|
|
4094
|
+
MODULES_SANDBOX_DIR
|
|
4095
|
+
]);
|
|
4096
|
+
if (listed.status !== 0) throw new Error(`git ls-tree ${release} failed`);
|
|
4097
|
+
const uninstalled = new Set(record.uninstalledModules);
|
|
4098
|
+
return zList(listed.stdout).map((file) => MANIFEST_PATH.exec(file)?.[1]).filter((name) => !!name && !uninstalled.has(name));
|
|
4099
|
+
}
|
|
4100
|
+
async function checkStardeckOwnedFiles(input) {
|
|
4101
|
+
const { git, repoRoot } = input;
|
|
4102
|
+
const top = await git(["rev-parse", "--show-toplevel"]);
|
|
4103
|
+
if (top.status !== 0) {
|
|
4104
|
+
return { status: "skipped", warning: false, reason: "not a git checkout" };
|
|
4105
|
+
}
|
|
4106
|
+
if (realpath(top.stdout.trim()) !== realpath(repoRoot)) {
|
|
4107
|
+
return {
|
|
4108
|
+
status: "skipped",
|
|
4109
|
+
warning: false,
|
|
4110
|
+
reason: `${repoRoot} is not the root of its git repo, so it is not an app's own repo`
|
|
4111
|
+
};
|
|
4112
|
+
}
|
|
4113
|
+
const release = await findRelease(git);
|
|
4114
|
+
if (!release) return noReleaseReason(git);
|
|
4115
|
+
const read = readRecord(repoRoot);
|
|
4116
|
+
if (!read.ok) return { status: "invalid-record", error: read.error };
|
|
4117
|
+
const { record } = read;
|
|
4118
|
+
if (!record.stardeckUpdates) {
|
|
4119
|
+
return {
|
|
4120
|
+
status: "skipped",
|
|
4121
|
+
warning: false,
|
|
4122
|
+
reason: "this app stopped all Stardeck updates, so every file is the app's"
|
|
4123
|
+
};
|
|
4124
|
+
}
|
|
4125
|
+
const moduleNames = await releaseModules(git, release, record);
|
|
4126
|
+
const set = platformOwnedSet({
|
|
4127
|
+
moduleNames,
|
|
4128
|
+
ejectedModules: record.updatesStoppedModules,
|
|
4129
|
+
uiEjectedModules: record.customizedUiModules,
|
|
4130
|
+
externalModules: record.externalModules,
|
|
4131
|
+
removedModules: record.uninstalledModules
|
|
4132
|
+
});
|
|
4133
|
+
const pathspecs = [
|
|
4134
|
+
...platformOwnedDiffPathspecs(set),
|
|
4135
|
+
...record.uninstalledModules.map(
|
|
4136
|
+
(name) => `:(glob,exclude)${MODULES_SANDBOX_DIR}/*/enhancements/${name}/**`
|
|
4137
|
+
)
|
|
4138
|
+
];
|
|
4139
|
+
const [changed, added] = await Promise.all([
|
|
4140
|
+
git([
|
|
4141
|
+
"diff",
|
|
4142
|
+
"--name-only",
|
|
4143
|
+
"-z",
|
|
4144
|
+
"--no-renames",
|
|
4145
|
+
"--no-ext-diff",
|
|
4146
|
+
release,
|
|
4147
|
+
"--",
|
|
4148
|
+
...pathspecs
|
|
4149
|
+
]),
|
|
4150
|
+
git(["ls-files", "-z", "--others", "--exclude-standard", "--", ...pathspecs])
|
|
4151
|
+
]);
|
|
4152
|
+
if (changed.status !== 0 || added.status !== 0) {
|
|
4153
|
+
throw new Error(`git could not compare the checkout with Stardeck's version ${release}`);
|
|
4154
|
+
}
|
|
4155
|
+
const paths = [.../* @__PURE__ */ new Set([...zList(changed.stdout), ...zList(added.stdout)])].sort();
|
|
4156
|
+
console.log(
|
|
4157
|
+
`[OwnershipGuard] release=${release.slice(0, 12)} record=${read.found ? "found" : "missing"} modules=${moduleNames.length} carveOuts=${set.carveOuts.length} changed=${paths.length}`
|
|
4158
|
+
);
|
|
4159
|
+
return paths.length > 0 ? { status: "changed", release, paths } : { status: "clean", release };
|
|
4160
|
+
}
|
|
4161
|
+
function formatOwnershipGuardFailure(result) {
|
|
4162
|
+
if (result.status === "invalid-record") {
|
|
4163
|
+
return `${result.error}
|
|
4164
|
+
Restore it with \`git checkout origin/HEAD -- ${OWNERSHIP_RECORD_PATH}\`. Stardeck writes this file; ownership changes in the dashboard's Modules tab.`;
|
|
4165
|
+
}
|
|
4166
|
+
const count = `${result.paths.length} Stardeck-owned ${result.paths.length === 1 ? "file" : "files"}`;
|
|
4167
|
+
return [
|
|
4168
|
+
`${count} changed from Stardeck's version (${result.release.slice(0, 12)}). The next Stardeck update replaces them, so these changes would be lost:`,
|
|
4169
|
+
...result.paths.map((file) => ` - ${file}`),
|
|
4170
|
+
"",
|
|
4171
|
+
`Undo them (\`git checkout ${result.release.slice(0, 12)} -- <path>\`, or delete a new file) and do one of these instead:`,
|
|
4172
|
+
...STARDECK_OWNED_ALTERNATIVES.map((line) => ` - ${line.replace(/\*\*/g, "")}`)
|
|
4173
|
+
].join("\n");
|
|
4174
|
+
}
|
|
3922
4175
|
|
|
3923
4176
|
// src/cli.ts
|
|
3924
4177
|
var import_typescript7 = __toESM(require("typescript"));
|
|
3925
|
-
var
|
|
4178
|
+
var import_node_path5 = __toESM(require("path"));
|
|
3926
4179
|
|
|
3927
4180
|
// src/index.ts
|
|
3928
4181
|
var import_node_child_process2 = require("child_process");
|
|
3929
|
-
var
|
|
3930
|
-
var
|
|
4182
|
+
var import_node_fs4 = __toESM(require("fs"));
|
|
4183
|
+
var import_node_path4 = __toESM(require("path"));
|
|
3931
4184
|
|
|
3932
4185
|
// ../../packages/lib/src/server/module-rail/local-fs-sandbox.ts
|
|
3933
4186
|
var import_node_child_process = require("child_process");
|
|
3934
|
-
var
|
|
3935
|
-
var
|
|
4187
|
+
var import_node_fs2 = __toESM(require("fs"));
|
|
4188
|
+
var import_node_path2 = __toESM(require("path"));
|
|
3936
4189
|
function localFsSandbox(root) {
|
|
3937
4190
|
return {
|
|
3938
4191
|
async exec(command, options) {
|
|
@@ -3956,13 +4209,13 @@ ${result.error}`);
|
|
|
3956
4209
|
},
|
|
3957
4210
|
async fileExists(relative) {
|
|
3958
4211
|
try {
|
|
3959
|
-
return
|
|
4212
|
+
return import_node_fs2.default.statSync(import_node_path2.default.join(root, relative)).isFile();
|
|
3960
4213
|
} catch {
|
|
3961
4214
|
return false;
|
|
3962
4215
|
}
|
|
3963
4216
|
},
|
|
3964
4217
|
async readFile(relative) {
|
|
3965
|
-
return
|
|
4218
|
+
return import_node_fs2.default.readFileSync(import_node_path2.default.join(root, relative), "utf8");
|
|
3966
4219
|
},
|
|
3967
4220
|
async fetchRemoteRef() {
|
|
3968
4221
|
throw new Error("[ModuleRail] local filesystem sandbox cannot reach the network");
|
|
@@ -3995,28 +4248,28 @@ function shellQuote(value) {
|
|
|
3995
4248
|
}
|
|
3996
4249
|
|
|
3997
4250
|
// ../../packages/lib/src/shared/compose-inputs.ts
|
|
3998
|
-
var
|
|
4251
|
+
var import_zod7 = require("zod");
|
|
3999
4252
|
var COMPOSE_INPUTS_PATH = "apps/web/stardeck.compose.json";
|
|
4000
|
-
var composeInputsSchema =
|
|
4001
|
-
connectedStores:
|
|
4002
|
-
|
|
4003
|
-
storeId:
|
|
4004
|
-
slug:
|
|
4253
|
+
var composeInputsSchema = import_zod7.z.strictObject({
|
|
4254
|
+
connectedStores: import_zod7.z.array(
|
|
4255
|
+
import_zod7.z.strictObject({
|
|
4256
|
+
storeId: import_zod7.z.string().min(1),
|
|
4257
|
+
slug: import_zod7.z.string().min(1),
|
|
4005
4258
|
// A row with an empty binding key must not fail a deploy: the rail
|
|
4006
4259
|
// treats falsy as "no binding key", so normalise rather than reject.
|
|
4007
|
-
bindingKey:
|
|
4008
|
-
accessLevel:
|
|
4260
|
+
bindingKey: import_zod7.z.string().nullish().transform((value) => value || null),
|
|
4261
|
+
accessLevel: import_zod7.z.enum(["read", "write", "admin"]),
|
|
4009
4262
|
// Defaulted so a file written before the platform emitted store types
|
|
4010
4263
|
// (database rows only) still parses. The platform starts writing
|
|
4011
4264
|
// storage rows, with `type` set, once forks run a compose that reads it.
|
|
4012
|
-
type:
|
|
4265
|
+
type: import_zod7.z.enum(["database", "storage"]).default("database")
|
|
4013
4266
|
})
|
|
4014
4267
|
).default([]),
|
|
4015
|
-
installs:
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
installedVersion:
|
|
4019
|
-
datastoreBindings:
|
|
4268
|
+
installs: import_zod7.z.record(
|
|
4269
|
+
import_zod7.z.string(),
|
|
4270
|
+
import_zod7.z.strictObject({
|
|
4271
|
+
installedVersion: import_zod7.z.string().min(1),
|
|
4272
|
+
datastoreBindings: import_zod7.z.record(import_zod7.z.string(), import_zod7.z.string()).nullish()
|
|
4020
4273
|
})
|
|
4021
4274
|
).default({})
|
|
4022
4275
|
});
|
|
@@ -4176,13 +4429,13 @@ function assertClientSafeConventionalEntryImports(source, pathLabel, kind) {
|
|
|
4176
4429
|
function createMessageTree() {
|
|
4177
4430
|
return /* @__PURE__ */ Object.create(null);
|
|
4178
4431
|
}
|
|
4179
|
-
function assertMessageTree(value,
|
|
4432
|
+
function assertMessageTree(value, path6) {
|
|
4180
4433
|
if (typeof value === "string") return;
|
|
4181
4434
|
if (value === null || Array.isArray(value) || typeof value !== "object") {
|
|
4182
|
-
throw new Error(`Invalid message at ${
|
|
4435
|
+
throw new Error(`Invalid message at ${path6}: expected string or nested object`);
|
|
4183
4436
|
}
|
|
4184
4437
|
for (const [key, child] of Object.entries(value)) {
|
|
4185
|
-
assertMessageTree(child, `${
|
|
4438
|
+
assertMessageTree(child, `${path6}.${key}`);
|
|
4186
4439
|
}
|
|
4187
4440
|
}
|
|
4188
4441
|
function cloneTree(value) {
|
|
@@ -4192,17 +4445,17 @@ function cloneTree(value) {
|
|
|
4192
4445
|
}
|
|
4193
4446
|
return clone;
|
|
4194
4447
|
}
|
|
4195
|
-
function collectLeafPaths(value,
|
|
4448
|
+
function collectLeafPaths(value, path6, out) {
|
|
4196
4449
|
if (typeof value === "string") {
|
|
4197
|
-
out.push(
|
|
4450
|
+
out.push(path6);
|
|
4198
4451
|
return;
|
|
4199
4452
|
}
|
|
4200
|
-
assertMessageTree(value,
|
|
4201
|
-
for (const [key, child] of Object.entries(value)) collectLeafPaths(child, `${
|
|
4453
|
+
assertMessageTree(value, path6);
|
|
4454
|
+
for (const [key, child] of Object.entries(value)) collectLeafPaths(child, `${path6}.${key}`, out);
|
|
4202
4455
|
}
|
|
4203
|
-
function overrideInto(target, source, owner,
|
|
4456
|
+
function overrideInto(target, source, owner, path6, missing) {
|
|
4204
4457
|
for (const [key, value] of Object.entries(source)) {
|
|
4205
|
-
const nextPath =
|
|
4458
|
+
const nextPath = path6 ? `${path6}.${key}` : key;
|
|
4206
4459
|
if (!Object.prototype.hasOwnProperty.call(target, key)) {
|
|
4207
4460
|
collectLeafPaths(value, nextPath, missing);
|
|
4208
4461
|
continue;
|
|
@@ -4229,9 +4482,9 @@ function overrideInto(target, source, owner, path5, missing) {
|
|
|
4229
4482
|
function composeLocaleMessagesForPlan(layers) {
|
|
4230
4483
|
const result = createMessageTree();
|
|
4231
4484
|
const leafOwners = /* @__PURE__ */ new Map();
|
|
4232
|
-
function mergeInto(target, source, owner,
|
|
4485
|
+
function mergeInto(target, source, owner, path6) {
|
|
4233
4486
|
for (const [key, value] of Object.entries(source)) {
|
|
4234
|
-
const nextPath =
|
|
4487
|
+
const nextPath = path6 ? `${path6}.${key}` : key;
|
|
4235
4488
|
const hasExisting = Object.prototype.hasOwnProperty.call(target, key);
|
|
4236
4489
|
const existing = hasExisting ? target[key] : void 0;
|
|
4237
4490
|
if (typeof value === "string") {
|
|
@@ -4792,8 +5045,8 @@ var METHOD_ORDER = {
|
|
|
4792
5045
|
function compareNames2(a, b) {
|
|
4793
5046
|
return a < b ? -1 : a > b ? 1 : 0;
|
|
4794
5047
|
}
|
|
4795
|
-
function endpointPairKey(method,
|
|
4796
|
-
return `${method} ${
|
|
5048
|
+
function endpointPairKey(method, path6) {
|
|
5049
|
+
return `${method} ${path6}`;
|
|
4797
5050
|
}
|
|
4798
5051
|
function apiPathToRouteFile(apiPath) {
|
|
4799
5052
|
if (!apiPath.startsWith("/api/")) {
|
|
@@ -4807,11 +5060,11 @@ function routeEntryToAppPageFile(nextRelativePath) {
|
|
|
4807
5060
|
}
|
|
4808
5061
|
var APP_ROUTE_FILE_PREFIX = `${APP_PACKAGE_DIR}/src/app/`;
|
|
4809
5062
|
var API_ROUTE_FILE_SUFFIXES = ["/route.ts", "/route.tsx", "/route.jsx", "/route.js"];
|
|
4810
|
-
function appRouteFileToNextRelativePath(
|
|
4811
|
-
const pagePath = appPageFileToNextRelativePath(
|
|
5063
|
+
function appRouteFileToNextRelativePath(path6) {
|
|
5064
|
+
const pagePath = appPageFileToNextRelativePath(path6, APP_PACKAGE_DIR);
|
|
4812
5065
|
if (pagePath != null) return pagePath;
|
|
4813
|
-
if (!
|
|
4814
|
-
const relative =
|
|
5066
|
+
if (!path6.startsWith(APP_ROUTE_FILE_PREFIX)) return null;
|
|
5067
|
+
const relative = path6.slice(APP_ROUTE_FILE_PREFIX.length);
|
|
4815
5068
|
if (!relative.startsWith("api/")) return null;
|
|
4816
5069
|
const suffix = API_ROUTE_FILE_SUFFIXES.find((candidate) => relative.endsWith(candidate));
|
|
4817
5070
|
if (!suffix) return null;
|
|
@@ -4906,9 +5159,9 @@ function legacyHandlerPropertyMatchesPath(property, method, physicalPath, bindin
|
|
|
4906
5159
|
const expectedTokens = knownAliases[`${binding} ${method} ${physicalPath}`] ?? pathTokens;
|
|
4907
5160
|
return handlerTokens.length === expectedTokens.length && handlerTokens.every((token, index) => token === expectedTokens[index]);
|
|
4908
5161
|
}
|
|
4909
|
-
function parseLegacyApiAdapter(
|
|
4910
|
-
const physicalPath = appRouteFileToNextRelativePath(
|
|
4911
|
-
if (expectedMarkerKindForPath(
|
|
5162
|
+
function parseLegacyApiAdapter(path6, source) {
|
|
5163
|
+
const physicalPath = appRouteFileToNextRelativePath(path6);
|
|
5164
|
+
if (expectedMarkerKindForPath(path6) !== "endpoint" || physicalPath == null) return null;
|
|
4912
5165
|
const firstLine = source.split(/\r?\n/, 1)[0]?.trim() ?? "";
|
|
4913
5166
|
const match = firstLine.match(
|
|
4914
5167
|
new RegExp(
|
|
@@ -4918,7 +5171,7 @@ function parseLegacyApiAdapter(path5, source) {
|
|
|
4918
5171
|
if (!match || match[2] !== physicalPath) return null;
|
|
4919
5172
|
const owner = match[1];
|
|
4920
5173
|
if (!isValidModuleName(owner)) return null;
|
|
4921
|
-
const file = import_typescript4.default.createSourceFile(
|
|
5174
|
+
const file = import_typescript4.default.createSourceFile(path6, source, import_typescript4.default.ScriptTarget.Latest, false, import_typescript4.default.ScriptKind.TS);
|
|
4922
5175
|
if (file.parseDiagnostics.length > 0) {
|
|
4923
5176
|
return null;
|
|
4924
5177
|
}
|
|
@@ -4960,9 +5213,9 @@ function parseLegacyApiAdapter(path5, source) {
|
|
|
4960
5213
|
}
|
|
4961
5214
|
return handlerBinding != null && methods.size > 0 ? { owner, methods: [...methods] } : null;
|
|
4962
5215
|
}
|
|
4963
|
-
function parseLegacyRouteAdapter(
|
|
4964
|
-
const physicalPath = appRouteFileToNextRelativePath(
|
|
4965
|
-
if (expectedMarkerKindForPath(
|
|
5216
|
+
function parseLegacyRouteAdapter(path6, source) {
|
|
5217
|
+
const physicalPath = appRouteFileToNextRelativePath(path6);
|
|
5218
|
+
if (expectedMarkerKindForPath(path6) !== "route" || physicalPath == null) return null;
|
|
4966
5219
|
const normalized = source.replace(/\r\n/g, "\n");
|
|
4967
5220
|
const match = normalized.match(
|
|
4968
5221
|
new RegExp(
|
|
@@ -4974,30 +5227,30 @@ function parseLegacyRouteAdapter(path5, source) {
|
|
|
4974
5227
|
const nextRelativePath = match[2];
|
|
4975
5228
|
return isValidModuleName(owner) && nextRelativePath === physicalPath ? { owner, nextRelativePath } : null;
|
|
4976
5229
|
}
|
|
4977
|
-
function legacyRouteAdapterMatchesStub(
|
|
4978
|
-
const legacy = parseLegacyRouteAdapter(
|
|
5230
|
+
function legacyRouteAdapterMatchesStub(path6, source, stub) {
|
|
5231
|
+
const legacy = parseLegacyRouteAdapter(path6, source);
|
|
4979
5232
|
return legacy != null && legacy.owner === stub.owner;
|
|
4980
5233
|
}
|
|
4981
5234
|
var API_ROUTE_DIR_PREFIX = `${APP_PACKAGE_DIR}/src/app/api/`;
|
|
4982
5235
|
var APP_DIR_PREFIX = `${APP_PACKAGE_DIR}/src/app/`;
|
|
4983
5236
|
var PAGE_FILE_SUFFIXES = ["/page.tsx", "/page.ts", "/page.jsx", "/page.js"];
|
|
4984
|
-
function expectedMarkerKindForPath(
|
|
4985
|
-
if (
|
|
4986
|
-
if (
|
|
4987
|
-
if (
|
|
4988
|
-
if (
|
|
4989
|
-
if (
|
|
4990
|
-
if (
|
|
4991
|
-
if (
|
|
4992
|
-
if (
|
|
5237
|
+
function expectedMarkerKindForPath(path6) {
|
|
5238
|
+
if (path6 === MODULES_GEN_PATH) return "registry";
|
|
5239
|
+
if (path6 === MODULE_CONTRIBUTIONS_GEN_PATH) return "contributions";
|
|
5240
|
+
if (path6 === MODULE_DATASTORES_GEN_PATH) return "datastores";
|
|
5241
|
+
if (path6 === MODULE_I18N_GEN_PATH) return "i18n";
|
|
5242
|
+
if (path6 === MODULE_INIT_SERVER_GEN_PATH) return "init";
|
|
5243
|
+
if (path6 === MODULE_SERVER_GEN_PATH) return "server";
|
|
5244
|
+
if (path6.startsWith(API_ROUTE_DIR_PREFIX) && path6.endsWith("/route.ts")) return "endpoint";
|
|
5245
|
+
if (path6.startsWith(APP_DIR_PREFIX) && !path6.startsWith(API_ROUTE_DIR_PREFIX) && PAGE_FILE_SUFFIXES.some((suffix) => path6.endsWith(suffix))) {
|
|
4993
5246
|
return "route";
|
|
4994
5247
|
}
|
|
4995
5248
|
return null;
|
|
4996
5249
|
}
|
|
4997
|
-
function markerCongruentWithPath(
|
|
4998
|
-
if (expectedMarkerKindForPath(
|
|
5250
|
+
function markerCongruentWithPath(path6, marker) {
|
|
5251
|
+
if (expectedMarkerKindForPath(path6) !== marker.kind) return false;
|
|
4999
5252
|
if (marker.kind === "route") {
|
|
5000
|
-
const derived = appPageFileToNextRelativePath(
|
|
5253
|
+
const derived = appPageFileToNextRelativePath(path6, APP_PACKAGE_DIR);
|
|
5001
5254
|
return derived != null && derived === marker.nextRelativePath;
|
|
5002
5255
|
}
|
|
5003
5256
|
return true;
|
|
@@ -5273,8 +5526,8 @@ function buildGeneratedModuleRegistry(input) {
|
|
|
5273
5526
|
function activeEnhancementPeers(registryModule) {
|
|
5274
5527
|
return new Set(registryModule.enhancements.filter((e) => e.active).map((e) => e.peer));
|
|
5275
5528
|
}
|
|
5276
|
-
function endpointBucketKey(
|
|
5277
|
-
return `${
|
|
5529
|
+
function endpointBucketKey(path6, owner, scope) {
|
|
5530
|
+
return `${path6}\0${owner}\0${scopeLabel(scope)}`;
|
|
5278
5531
|
}
|
|
5279
5532
|
function planRouteStubs(input) {
|
|
5280
5533
|
const endpointBuckets = /* @__PURE__ */ new Map();
|
|
@@ -5398,7 +5651,7 @@ function planRouteStubs(input) {
|
|
|
5398
5651
|
for (const entry of entries) {
|
|
5399
5652
|
const nextRelativePath = entry.nextRelativePath;
|
|
5400
5653
|
const destination = normalizeAppRouterDestinationPattern(nextRelativePath);
|
|
5401
|
-
const
|
|
5654
|
+
const path6 = routeEntryToAppPageFile(nextRelativePath);
|
|
5402
5655
|
const existing = routeDestinationOwners.get(destination);
|
|
5403
5656
|
if (existing) {
|
|
5404
5657
|
throw new Error(
|
|
@@ -5408,13 +5661,13 @@ function planRouteStubs(input) {
|
|
|
5408
5661
|
routeDestinationOwners.set(destination, {
|
|
5409
5662
|
owner: regMod.name,
|
|
5410
5663
|
nextRelativePath,
|
|
5411
|
-
path:
|
|
5664
|
+
path: path6
|
|
5412
5665
|
});
|
|
5413
5666
|
const importModule = routeEntryImport(regMod.name, nextRelativePath);
|
|
5414
5667
|
const analyzed = analyzeRouteEntryPath(nextRelativePath);
|
|
5415
5668
|
const mount = mounts.find((claim) => routeEntryCoveredByMountClaim(analyzed, claim));
|
|
5416
5669
|
routeStubs.push({
|
|
5417
|
-
path:
|
|
5670
|
+
path: path6,
|
|
5418
5671
|
owner: regMod.name,
|
|
5419
5672
|
nextRelativePath,
|
|
5420
5673
|
importModule,
|
|
@@ -5475,16 +5728,16 @@ function planCompositionArtifacts(input) {
|
|
|
5475
5728
|
...stubs.routeStubs.map((stub) => stub.path)
|
|
5476
5729
|
]);
|
|
5477
5730
|
const existingGeneratedPathSet = new Set(input.existingGeneratedPaths);
|
|
5478
|
-
const isProvenStaleDeletionCandidate = (
|
|
5479
|
-
if (
|
|
5480
|
-
if (COMPOSITION_RUNTIME_GEN_PATHS.includes(
|
|
5481
|
-
if (desiredPaths.has(
|
|
5482
|
-
if (!existingGeneratedPathSet.has(
|
|
5483
|
-
const legacy = parseLegacyApiAdapter(
|
|
5731
|
+
const isProvenStaleDeletionCandidate = (path6, content) => {
|
|
5732
|
+
if (path6 === MODULES_GEN_PATH) return false;
|
|
5733
|
+
if (COMPOSITION_RUNTIME_GEN_PATHS.includes(path6)) return false;
|
|
5734
|
+
if (desiredPaths.has(path6)) return false;
|
|
5735
|
+
if (!existingGeneratedPathSet.has(path6)) return false;
|
|
5736
|
+
const legacy = parseLegacyApiAdapter(path6, content);
|
|
5484
5737
|
if (legacy) return true;
|
|
5485
|
-
if (parseLegacyRouteAdapter(
|
|
5738
|
+
if (parseLegacyRouteAdapter(path6, content)) return true;
|
|
5486
5739
|
const marker = parseCompositionArtifactMarker(content);
|
|
5487
|
-
return marker != null && markerCongruentWithPath(
|
|
5740
|
+
return marker != null && markerCongruentWithPath(path6, marker);
|
|
5488
5741
|
};
|
|
5489
5742
|
const plannedByDestination = /* @__PURE__ */ new Map();
|
|
5490
5743
|
for (const stub of stubs.routeStubs) {
|
|
@@ -5638,13 +5891,13 @@ function planCompositionArtifacts(input) {
|
|
|
5638
5891
|
for (const stub of stubs.routeStubs) desired.set(stub.path, stub.content);
|
|
5639
5892
|
const endpointStubByPath = new Map(stubs.endpointStubs.map((stub) => [stub.path, stub]));
|
|
5640
5893
|
const routeStubByPath = new Map(stubs.routeStubs.map((stub) => [stub.path, stub]));
|
|
5641
|
-
for (const [
|
|
5642
|
-
const existing = input.existingFiles[
|
|
5894
|
+
for (const [path6] of desired) {
|
|
5895
|
+
const existing = input.existingFiles[path6];
|
|
5643
5896
|
if (existing == null) continue;
|
|
5644
5897
|
let marker = parseCompositionArtifactMarker(existing);
|
|
5645
|
-
const plannedEndpoint = endpointStubByPath.get(
|
|
5646
|
-
const plannedRoute = routeStubByPath.get(
|
|
5647
|
-
const legacy = parseLegacyApiAdapter(
|
|
5898
|
+
const plannedEndpoint = endpointStubByPath.get(path6);
|
|
5899
|
+
const plannedRoute = routeStubByPath.get(path6);
|
|
5900
|
+
const legacy = parseLegacyApiAdapter(path6, existing);
|
|
5648
5901
|
if (legacy && plannedEndpoint) {
|
|
5649
5902
|
marker = {
|
|
5650
5903
|
kind: "endpoint",
|
|
@@ -5652,64 +5905,64 @@ function planCompositionArtifacts(input) {
|
|
|
5652
5905
|
scope: plannedEndpoint.scope
|
|
5653
5906
|
};
|
|
5654
5907
|
}
|
|
5655
|
-
if (!marker && plannedRoute && legacyRouteAdapterMatchesStub(
|
|
5908
|
+
if (!marker && plannedRoute && legacyRouteAdapterMatchesStub(path6, existing, plannedRoute)) {
|
|
5656
5909
|
marker = {
|
|
5657
5910
|
kind: "route",
|
|
5658
5911
|
owner: plannedRoute.owner,
|
|
5659
5912
|
nextRelativePath: plannedRoute.nextRelativePath
|
|
5660
5913
|
};
|
|
5661
5914
|
}
|
|
5662
|
-
const isGenRuntimePath = COMPOSITION_RUNTIME_GEN_PATHS.includes(
|
|
5915
|
+
const isGenRuntimePath = COMPOSITION_RUNTIME_GEN_PATHS.includes(path6);
|
|
5663
5916
|
if (!marker) {
|
|
5664
5917
|
return {
|
|
5665
5918
|
ok: false,
|
|
5666
|
-
error:
|
|
5667
|
-
path:
|
|
5919
|
+
error: path6 === MODULES_GEN_PATH ? `[ModuleRail] composition artifacts: refusing to overwrite unmanaged registry at ${path6}` : isGenRuntimePath ? `[ModuleRail] composition artifacts: refusing to overwrite unmanaged generated file at ${path6}` : `[ModuleRail] composition artifacts: refusing to overwrite unmanaged route at ${path6}`,
|
|
5920
|
+
path: path6
|
|
5668
5921
|
};
|
|
5669
5922
|
}
|
|
5670
|
-
const expectedKind = expectedMarkerKindForPath(
|
|
5923
|
+
const expectedKind = expectedMarkerKindForPath(path6);
|
|
5671
5924
|
if (marker.kind !== expectedKind) {
|
|
5672
5925
|
return {
|
|
5673
5926
|
ok: false,
|
|
5674
|
-
error:
|
|
5675
|
-
path:
|
|
5927
|
+
error: path6 === MODULES_GEN_PATH ? `[ModuleRail] composition artifacts: refusing to overwrite unmanaged registry at ${path6} (incongruent marker ownership: found ${marker.kind} marker)` : isGenRuntimePath ? `[ModuleRail] composition artifacts: refusing to overwrite unmanaged generated file at ${path6} (incongruent marker ownership: found ${marker.kind} marker)` : `[ModuleRail] composition artifacts: refusing to overwrite incongruent marker ownership at ${path6} (expected ${expectedKind ?? "no"} marker, found ${marker.kind} marker)`,
|
|
5928
|
+
path: path6
|
|
5676
5929
|
};
|
|
5677
5930
|
}
|
|
5678
5931
|
if (marker.kind === "endpoint") {
|
|
5679
|
-
const stub = endpointStubByPath.get(
|
|
5932
|
+
const stub = endpointStubByPath.get(path6);
|
|
5680
5933
|
if (!endpointMarkerOwnsStub(marker, stub)) {
|
|
5681
5934
|
return {
|
|
5682
5935
|
ok: false,
|
|
5683
|
-
error: `[ModuleRail] composition artifacts: refusing to overwrite incongruent marker ownership at ${
|
|
5684
|
-
path:
|
|
5936
|
+
error: `[ModuleRail] composition artifacts: refusing to overwrite incongruent marker ownership at ${path6} (existing owner=${marker.owner} scope=${scopeLabel(marker.scope)} vs planned owner=${stub?.owner ?? "?"} scope=${stub ? scopeLabel(stub.scope) : "?"})`,
|
|
5937
|
+
path: path6,
|
|
5685
5938
|
owner: stub?.owner
|
|
5686
5939
|
};
|
|
5687
5940
|
}
|
|
5688
5941
|
} else if (marker.kind === "route") {
|
|
5689
|
-
const stub = routeStubByPath.get(
|
|
5942
|
+
const stub = routeStubByPath.get(path6);
|
|
5690
5943
|
const ownerMatches = stub != null && marker.owner === stub.owner;
|
|
5691
5944
|
const pathMatches = stub != null && marker.nextRelativePath === stub.nextRelativePath;
|
|
5692
5945
|
if (!ownerMatches || !pathMatches) {
|
|
5693
5946
|
return {
|
|
5694
5947
|
ok: false,
|
|
5695
|
-
error: `[ModuleRail] composition artifacts: refusing to overwrite incongruent marker ownership at ${
|
|
5696
|
-
path:
|
|
5948
|
+
error: `[ModuleRail] composition artifacts: refusing to overwrite incongruent marker ownership at ${path6} (existing owner=${marker.owner} path=${marker.nextRelativePath} vs planned owner=${stub?.owner ?? "?"} path=${stub?.nextRelativePath ?? "?"})`,
|
|
5949
|
+
path: path6,
|
|
5697
5950
|
owner: stub?.owner
|
|
5698
5951
|
};
|
|
5699
5952
|
}
|
|
5700
5953
|
}
|
|
5701
5954
|
}
|
|
5702
5955
|
const deletes = [];
|
|
5703
|
-
for (const
|
|
5704
|
-
const existing = input.existingFiles[
|
|
5956
|
+
for (const path6 of input.existingGeneratedPaths) {
|
|
5957
|
+
const existing = input.existingFiles[path6];
|
|
5705
5958
|
if (existing == null) continue;
|
|
5706
|
-
if (!isProvenStaleDeletionCandidate(
|
|
5959
|
+
if (!isProvenStaleDeletionCandidate(path6, existing)) {
|
|
5707
5960
|
continue;
|
|
5708
5961
|
}
|
|
5709
|
-
deletes.push(
|
|
5962
|
+
deletes.push(path6);
|
|
5710
5963
|
}
|
|
5711
5964
|
deletes.sort(compareNames2);
|
|
5712
|
-
const writes = [...desired.entries()].filter(([
|
|
5965
|
+
const writes = [...desired.entries()].filter(([path6, content]) => input.existingFiles[path6] !== content).map(([path6, content]) => ({ path: path6, content })).sort((a, b) => compareNames2(a.path, b.path));
|
|
5713
5966
|
return {
|
|
5714
5967
|
ok: true,
|
|
5715
5968
|
plan: {
|
|
@@ -5980,14 +6233,14 @@ function rejectPseudoModuleEntries(entries, parentDir, label) {
|
|
|
5980
6233
|
}
|
|
5981
6234
|
|
|
5982
6235
|
// ../../packages/lib/src/shared/module-composition.ts
|
|
5983
|
-
var
|
|
5984
|
-
var creationSourceBlankSchema =
|
|
5985
|
-
type:
|
|
6236
|
+
var import_zod8 = require("zod");
|
|
6237
|
+
var creationSourceBlankSchema = import_zod8.z.object({
|
|
6238
|
+
type: import_zod8.z.literal("blank")
|
|
5986
6239
|
});
|
|
5987
|
-
var creationSourceBlueprintSchema =
|
|
5988
|
-
type:
|
|
6240
|
+
var creationSourceBlueprintSchema = import_zod8.z.object({
|
|
6241
|
+
type: import_zod8.z.literal("blueprint"),
|
|
5989
6242
|
/** Internal template/fork project id — keep template naming in identifiers. */
|
|
5990
|
-
blueprintProjectId:
|
|
6243
|
+
blueprintProjectId: import_zod8.z.string().uuid(),
|
|
5991
6244
|
/**
|
|
5992
6245
|
* `null`/absent means every feature Module: the platform stores that as the
|
|
5993
6246
|
* explicit list of feature Modules in the published Blueprint version the fork
|
|
@@ -5995,192 +6248,197 @@ var creationSourceBlueprintSchema = import_zod7.z.object({
|
|
|
5995
6248
|
* a custom app: no feature Module, Library Modules only (`resolvePlatformEnv`
|
|
5996
6249
|
* emits it as a set value naming no Module).
|
|
5997
6250
|
*/
|
|
5998
|
-
enabledModules:
|
|
6251
|
+
enabledModules: import_zod8.z.array(moduleNameSchema).max(100).nullable().optional(),
|
|
6252
|
+
/**
|
|
6253
|
+
* The kind of business (a preset id the published version defines). Its Modules are built
|
|
6254
|
+
* and `enabledModules` adds extras on top; for the general kind `enabledModules` is the pick.
|
|
6255
|
+
*/
|
|
6256
|
+
businessKind: import_zod8.z.string().regex(/^[a-z][a-z0-9-]{0,39}$/).optional()
|
|
5999
6257
|
});
|
|
6000
|
-
var creationSourceSchema =
|
|
6258
|
+
var creationSourceSchema = import_zod8.z.discriminatedUnion("type", [
|
|
6001
6259
|
creationSourceBlankSchema,
|
|
6002
6260
|
creationSourceBlueprintSchema
|
|
6003
6261
|
]);
|
|
6004
|
-
var moduleInstallHandoffSchema =
|
|
6005
|
-
moduleName:
|
|
6006
|
-
homeProjectId:
|
|
6007
|
-
installedVersion:
|
|
6008
|
-
baselineSha:
|
|
6009
|
-
mounts:
|
|
6010
|
-
|
|
6011
|
-
group:
|
|
6012
|
-
segment:
|
|
6262
|
+
var moduleInstallHandoffSchema = import_zod8.z.object({
|
|
6263
|
+
moduleName: import_zod8.z.string().min(1),
|
|
6264
|
+
homeProjectId: import_zod8.z.string().uuid(),
|
|
6265
|
+
installedVersion: import_zod8.z.string().min(1),
|
|
6266
|
+
baselineSha: import_zod8.z.string().min(1),
|
|
6267
|
+
mounts: import_zod8.z.array(
|
|
6268
|
+
import_zod8.z.object({
|
|
6269
|
+
group: import_zod8.z.string(),
|
|
6270
|
+
segment: import_zod8.z.string()
|
|
6013
6271
|
})
|
|
6014
6272
|
),
|
|
6015
|
-
integration:
|
|
6016
|
-
workflows:
|
|
6017
|
-
prunedEnhancements:
|
|
6018
|
-
restoredEnhancements:
|
|
6019
|
-
permissions:
|
|
6020
|
-
|
|
6021
|
-
key:
|
|
6022
|
-
description:
|
|
6273
|
+
integration: import_zod8.z.array(import_zod8.z.string()),
|
|
6274
|
+
workflows: import_zod8.z.array(import_zod8.z.string()),
|
|
6275
|
+
prunedEnhancements: import_zod8.z.array(import_zod8.z.string()),
|
|
6276
|
+
restoredEnhancements: import_zod8.z.array(import_zod8.z.string()).optional(),
|
|
6277
|
+
permissions: import_zod8.z.array(
|
|
6278
|
+
import_zod8.z.object({
|
|
6279
|
+
key: import_zod8.z.string().min(1),
|
|
6280
|
+
description: import_zod8.z.string().min(1)
|
|
6023
6281
|
})
|
|
6024
6282
|
).optional()
|
|
6025
6283
|
});
|
|
6026
6284
|
|
|
6027
6285
|
// ../../packages/lib/src/shared/schema-ops.ts
|
|
6028
|
-
var
|
|
6029
|
-
var columnSpecSchema =
|
|
6030
|
-
name:
|
|
6031
|
-
pgType:
|
|
6032
|
-
nullable:
|
|
6286
|
+
var import_zod9 = require("zod");
|
|
6287
|
+
var columnSpecSchema = import_zod9.z.object({
|
|
6288
|
+
name: import_zod9.z.string().min(1).max(63),
|
|
6289
|
+
pgType: import_zod9.z.string().min(1),
|
|
6290
|
+
nullable: import_zod9.z.boolean(),
|
|
6033
6291
|
/**
|
|
6034
6292
|
* A SQL expression used as the column default, as a raw string (e.g. `'t'`,
|
|
6035
6293
|
* `0`, `now()`, `gen_random_uuid()`). `null` or omitted means no default.
|
|
6036
6294
|
* NOT a JS literal — must already be a valid SQL expression.
|
|
6037
6295
|
*/
|
|
6038
|
-
default:
|
|
6039
|
-
fieldType:
|
|
6040
|
-
fieldConfig:
|
|
6296
|
+
default: import_zod9.z.string().nullable().optional(),
|
|
6297
|
+
fieldType: import_zod9.z.string().optional(),
|
|
6298
|
+
fieldConfig: import_zod9.z.record(import_zod9.z.string(), import_zod9.z.unknown()).optional()
|
|
6041
6299
|
});
|
|
6042
|
-
var referentialActionSchema =
|
|
6300
|
+
var referentialActionSchema = import_zod9.z.enum([
|
|
6043
6301
|
"cascade",
|
|
6044
6302
|
"set null",
|
|
6045
6303
|
"set default",
|
|
6046
6304
|
"restrict",
|
|
6047
6305
|
"no action"
|
|
6048
6306
|
]);
|
|
6049
|
-
var schemaOpSchema =
|
|
6307
|
+
var schemaOpSchema = import_zod9.z.discriminatedUnion("type", [
|
|
6050
6308
|
// Tables
|
|
6051
|
-
|
|
6052
|
-
type:
|
|
6053
|
-
table:
|
|
6054
|
-
columns:
|
|
6055
|
-
primaryKey:
|
|
6056
|
-
ifNotExists:
|
|
6309
|
+
import_zod9.z.object({
|
|
6310
|
+
type: import_zod9.z.literal("createTable"),
|
|
6311
|
+
table: import_zod9.z.string().min(1).max(63),
|
|
6312
|
+
columns: import_zod9.z.array(columnSpecSchema).min(1),
|
|
6313
|
+
primaryKey: import_zod9.z.array(import_zod9.z.string()).optional(),
|
|
6314
|
+
ifNotExists: import_zod9.z.boolean().optional()
|
|
6057
6315
|
}),
|
|
6058
|
-
|
|
6059
|
-
type:
|
|
6060
|
-
table:
|
|
6061
|
-
cascade:
|
|
6062
|
-
ifExists:
|
|
6316
|
+
import_zod9.z.object({
|
|
6317
|
+
type: import_zod9.z.literal("dropTable"),
|
|
6318
|
+
table: import_zod9.z.string().min(1).max(63),
|
|
6319
|
+
cascade: import_zod9.z.boolean().optional(),
|
|
6320
|
+
ifExists: import_zod9.z.boolean().optional()
|
|
6063
6321
|
}),
|
|
6064
|
-
|
|
6065
|
-
type:
|
|
6066
|
-
from:
|
|
6067
|
-
to:
|
|
6322
|
+
import_zod9.z.object({
|
|
6323
|
+
type: import_zod9.z.literal("renameTable"),
|
|
6324
|
+
from: import_zod9.z.string().min(1).max(63),
|
|
6325
|
+
to: import_zod9.z.string().min(1).max(63)
|
|
6068
6326
|
}),
|
|
6069
6327
|
// Columns
|
|
6070
|
-
|
|
6071
|
-
type:
|
|
6072
|
-
table:
|
|
6328
|
+
import_zod9.z.object({
|
|
6329
|
+
type: import_zod9.z.literal("addColumn"),
|
|
6330
|
+
table: import_zod9.z.string().min(1).max(63),
|
|
6073
6331
|
column: columnSpecSchema,
|
|
6074
|
-
ifNotExists:
|
|
6332
|
+
ifNotExists: import_zod9.z.boolean().optional()
|
|
6075
6333
|
}),
|
|
6076
|
-
|
|
6077
|
-
type:
|
|
6078
|
-
table:
|
|
6079
|
-
column:
|
|
6080
|
-
cascade:
|
|
6081
|
-
ifExists:
|
|
6334
|
+
import_zod9.z.object({
|
|
6335
|
+
type: import_zod9.z.literal("dropColumn"),
|
|
6336
|
+
table: import_zod9.z.string().min(1).max(63),
|
|
6337
|
+
column: import_zod9.z.string().min(1).max(63),
|
|
6338
|
+
cascade: import_zod9.z.boolean().optional(),
|
|
6339
|
+
ifExists: import_zod9.z.boolean().optional()
|
|
6082
6340
|
}),
|
|
6083
|
-
|
|
6084
|
-
type:
|
|
6085
|
-
table:
|
|
6086
|
-
from:
|
|
6087
|
-
to:
|
|
6341
|
+
import_zod9.z.object({
|
|
6342
|
+
type: import_zod9.z.literal("renameColumn"),
|
|
6343
|
+
table: import_zod9.z.string().min(1).max(63),
|
|
6344
|
+
from: import_zod9.z.string().min(1).max(63),
|
|
6345
|
+
to: import_zod9.z.string().min(1).max(63)
|
|
6088
6346
|
}),
|
|
6089
|
-
|
|
6090
|
-
type:
|
|
6091
|
-
table:
|
|
6092
|
-
column:
|
|
6093
|
-
newType:
|
|
6347
|
+
import_zod9.z.object({
|
|
6348
|
+
type: import_zod9.z.literal("alterColumnType"),
|
|
6349
|
+
table: import_zod9.z.string().min(1).max(63),
|
|
6350
|
+
column: import_zod9.z.string().min(1).max(63),
|
|
6351
|
+
newType: import_zod9.z.string().min(1),
|
|
6094
6352
|
/** Optional USING expression for potentially-lossy casts */
|
|
6095
|
-
using:
|
|
6353
|
+
using: import_zod9.z.string().optional()
|
|
6096
6354
|
}),
|
|
6097
|
-
|
|
6098
|
-
type:
|
|
6099
|
-
table:
|
|
6100
|
-
column:
|
|
6101
|
-
nullable:
|
|
6355
|
+
import_zod9.z.object({
|
|
6356
|
+
type: import_zod9.z.literal("alterColumnNullable"),
|
|
6357
|
+
table: import_zod9.z.string().min(1).max(63),
|
|
6358
|
+
column: import_zod9.z.string().min(1).max(63),
|
|
6359
|
+
nullable: import_zod9.z.boolean()
|
|
6102
6360
|
}),
|
|
6103
|
-
|
|
6104
|
-
type:
|
|
6105
|
-
table:
|
|
6106
|
-
column:
|
|
6361
|
+
import_zod9.z.object({
|
|
6362
|
+
type: import_zod9.z.literal("alterColumnDefault"),
|
|
6363
|
+
table: import_zod9.z.string().min(1).max(63),
|
|
6364
|
+
column: import_zod9.z.string().min(1).max(63),
|
|
6107
6365
|
/** null means drop the default; otherwise a SQL expression string. */
|
|
6108
|
-
default:
|
|
6366
|
+
default: import_zod9.z.string().nullable()
|
|
6109
6367
|
}),
|
|
6110
6368
|
// Indexes
|
|
6111
|
-
|
|
6112
|
-
type:
|
|
6113
|
-
table:
|
|
6114
|
-
name:
|
|
6115
|
-
columns:
|
|
6116
|
-
unique:
|
|
6369
|
+
import_zod9.z.object({
|
|
6370
|
+
type: import_zod9.z.literal("addIndex"),
|
|
6371
|
+
table: import_zod9.z.string().min(1).max(63),
|
|
6372
|
+
name: import_zod9.z.string().min(1).max(63),
|
|
6373
|
+
columns: import_zod9.z.array(import_zod9.z.string().min(1)).min(1),
|
|
6374
|
+
unique: import_zod9.z.boolean().optional(),
|
|
6117
6375
|
/** Partial-index WHERE clause, as a raw SQL expression */
|
|
6118
|
-
where:
|
|
6119
|
-
ifNotExists:
|
|
6376
|
+
where: import_zod9.z.string().optional(),
|
|
6377
|
+
ifNotExists: import_zod9.z.boolean().optional()
|
|
6120
6378
|
}),
|
|
6121
|
-
|
|
6122
|
-
type:
|
|
6123
|
-
name:
|
|
6124
|
-
cascade:
|
|
6125
|
-
ifExists:
|
|
6379
|
+
import_zod9.z.object({
|
|
6380
|
+
type: import_zod9.z.literal("dropIndex"),
|
|
6381
|
+
name: import_zod9.z.string().min(1).max(63),
|
|
6382
|
+
cascade: import_zod9.z.boolean().optional(),
|
|
6383
|
+
ifExists: import_zod9.z.boolean().optional()
|
|
6126
6384
|
}),
|
|
6127
6385
|
// Constraints
|
|
6128
|
-
|
|
6129
|
-
type:
|
|
6130
|
-
table:
|
|
6131
|
-
name:
|
|
6132
|
-
columns:
|
|
6133
|
-
refTable:
|
|
6134
|
-
refColumns:
|
|
6386
|
+
import_zod9.z.object({
|
|
6387
|
+
type: import_zod9.z.literal("addForeignKey"),
|
|
6388
|
+
table: import_zod9.z.string().min(1).max(63),
|
|
6389
|
+
name: import_zod9.z.string().min(1).max(63),
|
|
6390
|
+
columns: import_zod9.z.array(import_zod9.z.string().min(1)).min(1),
|
|
6391
|
+
refTable: import_zod9.z.string().min(1).max(63),
|
|
6392
|
+
refColumns: import_zod9.z.array(import_zod9.z.string().min(1)).min(1),
|
|
6135
6393
|
onDelete: referentialActionSchema.optional(),
|
|
6136
6394
|
onUpdate: referentialActionSchema.optional()
|
|
6137
6395
|
}),
|
|
6138
|
-
|
|
6139
|
-
type:
|
|
6140
|
-
table:
|
|
6141
|
-
name:
|
|
6142
|
-
columns:
|
|
6396
|
+
import_zod9.z.object({
|
|
6397
|
+
type: import_zod9.z.literal("addUniqueConstraint"),
|
|
6398
|
+
table: import_zod9.z.string().min(1).max(63),
|
|
6399
|
+
name: import_zod9.z.string().min(1).max(63),
|
|
6400
|
+
columns: import_zod9.z.array(import_zod9.z.string().min(1)).min(1)
|
|
6143
6401
|
}),
|
|
6144
|
-
|
|
6145
|
-
type:
|
|
6146
|
-
table:
|
|
6147
|
-
name:
|
|
6148
|
-
expression:
|
|
6402
|
+
import_zod9.z.object({
|
|
6403
|
+
type: import_zod9.z.literal("addCheckConstraint"),
|
|
6404
|
+
table: import_zod9.z.string().min(1).max(63),
|
|
6405
|
+
name: import_zod9.z.string().min(1).max(63),
|
|
6406
|
+
expression: import_zod9.z.string().min(1)
|
|
6149
6407
|
}),
|
|
6150
|
-
|
|
6151
|
-
type:
|
|
6152
|
-
table:
|
|
6153
|
-
name:
|
|
6154
|
-
cascade:
|
|
6155
|
-
ifExists:
|
|
6408
|
+
import_zod9.z.object({
|
|
6409
|
+
type: import_zod9.z.literal("dropConstraint"),
|
|
6410
|
+
table: import_zod9.z.string().min(1).max(63),
|
|
6411
|
+
name: import_zod9.z.string().min(1).max(63),
|
|
6412
|
+
cascade: import_zod9.z.boolean().optional(),
|
|
6413
|
+
ifExists: import_zod9.z.boolean().optional()
|
|
6156
6414
|
}),
|
|
6157
6415
|
// Enum types
|
|
6158
|
-
|
|
6159
|
-
type:
|
|
6160
|
-
name:
|
|
6161
|
-
values:
|
|
6416
|
+
import_zod9.z.object({
|
|
6417
|
+
type: import_zod9.z.literal("createEnum"),
|
|
6418
|
+
name: import_zod9.z.string().min(1).max(63),
|
|
6419
|
+
values: import_zod9.z.array(import_zod9.z.string().min(1)).min(1)
|
|
6162
6420
|
}),
|
|
6163
|
-
|
|
6164
|
-
type:
|
|
6165
|
-
name:
|
|
6166
|
-
value:
|
|
6421
|
+
import_zod9.z.object({
|
|
6422
|
+
type: import_zod9.z.literal("alterEnumAddValue"),
|
|
6423
|
+
name: import_zod9.z.string().min(1).max(63),
|
|
6424
|
+
value: import_zod9.z.string().min(1),
|
|
6167
6425
|
/** Position the new value before an existing one (mutually exclusive with `after`) */
|
|
6168
|
-
before:
|
|
6426
|
+
before: import_zod9.z.string().optional(),
|
|
6169
6427
|
/** Position the new value after an existing one (mutually exclusive with `before`) */
|
|
6170
|
-
after:
|
|
6171
|
-
ifNotExists:
|
|
6428
|
+
after: import_zod9.z.string().optional(),
|
|
6429
|
+
ifNotExists: import_zod9.z.boolean().optional()
|
|
6172
6430
|
}),
|
|
6173
|
-
|
|
6174
|
-
type:
|
|
6175
|
-
name:
|
|
6176
|
-
cascade:
|
|
6177
|
-
ifExists:
|
|
6431
|
+
import_zod9.z.object({
|
|
6432
|
+
type: import_zod9.z.literal("dropEnum"),
|
|
6433
|
+
name: import_zod9.z.string().min(1).max(63),
|
|
6434
|
+
cascade: import_zod9.z.boolean().optional(),
|
|
6435
|
+
ifExists: import_zod9.z.boolean().optional()
|
|
6178
6436
|
}),
|
|
6179
6437
|
// Extensions
|
|
6180
|
-
|
|
6181
|
-
type:
|
|
6182
|
-
name:
|
|
6183
|
-
ifNotExists:
|
|
6438
|
+
import_zod9.z.object({
|
|
6439
|
+
type: import_zod9.z.literal("createExtension"),
|
|
6440
|
+
name: import_zod9.z.string().min(1),
|
|
6441
|
+
ifNotExists: import_zod9.z.boolean().optional()
|
|
6184
6442
|
}),
|
|
6185
6443
|
// Data operations
|
|
6186
6444
|
/**
|
|
@@ -6188,26 +6446,26 @@ var schemaOpSchema = import_zod8.z.discriminatedUnion("type", [
|
|
|
6188
6446
|
* destructive — requires explicit confirm to replay on prod. The
|
|
6189
6447
|
* `description` is what the agent/user sees at confirm time.
|
|
6190
6448
|
*/
|
|
6191
|
-
|
|
6192
|
-
type:
|
|
6193
|
-
description:
|
|
6194
|
-
sql:
|
|
6449
|
+
import_zod9.z.object({
|
|
6450
|
+
type: import_zod9.z.literal("backfill"),
|
|
6451
|
+
description: import_zod9.z.string().min(1),
|
|
6452
|
+
sql: import_zod9.z.string().min(1)
|
|
6195
6453
|
}),
|
|
6196
6454
|
/**
|
|
6197
6455
|
* Escape hatch for DDL the structured ops can't express. `destructive`
|
|
6198
6456
|
* forces classification — use false only for genuinely additive-only SQL
|
|
6199
6457
|
* (e.g. CREATE EXTENSION variants the structured op doesn't cover).
|
|
6200
6458
|
*/
|
|
6201
|
-
|
|
6202
|
-
type:
|
|
6203
|
-
description:
|
|
6204
|
-
sql:
|
|
6205
|
-
destructive:
|
|
6459
|
+
import_zod9.z.object({
|
|
6460
|
+
type: import_zod9.z.literal("rawSql"),
|
|
6461
|
+
description: import_zod9.z.string().min(1),
|
|
6462
|
+
sql: import_zod9.z.string().min(1),
|
|
6463
|
+
destructive: import_zod9.z.boolean()
|
|
6206
6464
|
})
|
|
6207
6465
|
]);
|
|
6208
6466
|
|
|
6209
6467
|
// ../../packages/lib/src/server/module-rail/ops.ts
|
|
6210
|
-
var
|
|
6468
|
+
var import_zod10 = require("zod");
|
|
6211
6469
|
|
|
6212
6470
|
// ../../packages/lib/src/server/module-rail/reconcile.ts
|
|
6213
6471
|
var SANDBOX_BATCH_SIZE = 200;
|
|
@@ -6237,11 +6495,11 @@ function countedCompositionContext(context) {
|
|
|
6237
6495
|
counts.sandboxExecs += 1;
|
|
6238
6496
|
return context.sandbox.exec(...args);
|
|
6239
6497
|
},
|
|
6240
|
-
readFile: (
|
|
6498
|
+
readFile: (path6) => {
|
|
6241
6499
|
counts.sandboxReads += 1;
|
|
6242
|
-
return context.sandbox.readFile(
|
|
6500
|
+
return context.sandbox.readFile(path6);
|
|
6243
6501
|
},
|
|
6244
|
-
fileExists: (
|
|
6502
|
+
fileExists: (path6) => context.sandbox.fileExists(path6),
|
|
6245
6503
|
fetchRemoteRef: (...args) => context.sandbox.fetchRemoteRef(...args),
|
|
6246
6504
|
addDependencies: (specs) => context.sandbox.addDependencies(specs)
|
|
6247
6505
|
}
|
|
@@ -6438,14 +6696,14 @@ async function discoverRootRouteEntries(context, moduleName) {
|
|
|
6438
6696
|
}
|
|
6439
6697
|
return pages.sort((a, b) => a.nextRelativePath.localeCompare(b.nextRelativePath));
|
|
6440
6698
|
}
|
|
6441
|
-
async function writeSandboxFileAtomic(target,
|
|
6442
|
-
const dir = dirnamePosix(
|
|
6443
|
-
const tmp = `${
|
|
6699
|
+
async function writeSandboxFileAtomic(target, path6, content) {
|
|
6700
|
+
const dir = dirnamePosix(path6);
|
|
6701
|
+
const tmp = `${path6}.tmp.${target.runId}`;
|
|
6444
6702
|
const b64 = Buffer.from(content, "utf8").toString("base64");
|
|
6445
6703
|
const cmd = [
|
|
6446
6704
|
`mkdir -p ${shellQuote(dir)}`,
|
|
6447
6705
|
`printf '%s' ${shellQuote(b64)} | base64 -d > ${shellQuote(tmp)}`,
|
|
6448
|
-
`mv -f ${shellQuote(tmp)} ${shellQuote(
|
|
6706
|
+
`mv -f ${shellQuote(tmp)} ${shellQuote(path6)}`
|
|
6449
6707
|
].join(" && ");
|
|
6450
6708
|
const result = await target.sandbox.exec(cmd, { raiseOnError: false });
|
|
6451
6709
|
if (result.exitCode !== 0) {
|
|
@@ -6460,14 +6718,14 @@ async function writeSandboxFileAtomic(target, path5, content) {
|
|
|
6460
6718
|
}
|
|
6461
6719
|
const detail = typeof result.error === "string" && result.error.trim() || result.output.trim() || `exit ${result.exitCode}`;
|
|
6462
6720
|
throw new Error(
|
|
6463
|
-
`[ModuleRail] composition artifacts: failed to write ${redactSecrets(
|
|
6721
|
+
`[ModuleRail] composition artifacts: failed to write ${redactSecrets(path6)}: ${redactSecrets(detail)}`
|
|
6464
6722
|
);
|
|
6465
6723
|
}
|
|
6466
6724
|
}
|
|
6467
6725
|
async function collectAbsentWriteParentDirs(context, writePaths) {
|
|
6468
6726
|
const absent = /* @__PURE__ */ new Set();
|
|
6469
|
-
for (const
|
|
6470
|
-
let dir = dirnamePosix(
|
|
6727
|
+
for (const path6 of writePaths) {
|
|
6728
|
+
let dir = dirnamePosix(path6);
|
|
6471
6729
|
while (dir !== "." && dir !== "") {
|
|
6472
6730
|
if (absent.has(dir)) {
|
|
6473
6731
|
dir = dirnamePosix(dir);
|
|
@@ -6504,12 +6762,12 @@ async function removeAbsentWriteParentDirsOnRollback(context, dirsDeepestFirst)
|
|
|
6504
6762
|
);
|
|
6505
6763
|
}
|
|
6506
6764
|
}
|
|
6507
|
-
async function deleteSandboxFile(context,
|
|
6508
|
-
const result = await context.sandbox.exec(`rm -f ${shellQuote(
|
|
6765
|
+
async function deleteSandboxFile(context, path6) {
|
|
6766
|
+
const result = await context.sandbox.exec(`rm -f ${shellQuote(path6)}`, { raiseOnError: false });
|
|
6509
6767
|
if (result.exitCode !== 0) {
|
|
6510
6768
|
const detail = typeof result.error === "string" && result.error.trim() || result.output.trim() || `exit ${result.exitCode}`;
|
|
6511
6769
|
throw new Error(
|
|
6512
|
-
`[ModuleRail] composition artifacts: failed to delete ${redactSecrets(
|
|
6770
|
+
`[ModuleRail] composition artifacts: failed to delete ${redactSecrets(path6)}: ${redactSecrets(detail)}`
|
|
6513
6771
|
);
|
|
6514
6772
|
}
|
|
6515
6773
|
}
|
|
@@ -6576,37 +6834,37 @@ async function applyCompositionArtifactMutations(context, plan, existingFiles) {
|
|
|
6576
6834
|
await writeSandboxFileAtomic(context, write.path, write.content);
|
|
6577
6835
|
appliedWrites.push({ path: write.path, previous });
|
|
6578
6836
|
}
|
|
6579
|
-
for (const
|
|
6580
|
-
const previous = existingFiles[
|
|
6837
|
+
for (const path6 of plan.deletes) {
|
|
6838
|
+
const previous = existingFiles[path6];
|
|
6581
6839
|
if (previous == null) continue;
|
|
6582
|
-
await deleteSandboxFile(context,
|
|
6583
|
-
appliedDeletes.push({ path:
|
|
6840
|
+
await deleteSandboxFile(context, path6);
|
|
6841
|
+
appliedDeletes.push({ path: path6, previous });
|
|
6584
6842
|
}
|
|
6585
6843
|
} catch (error) {
|
|
6586
6844
|
console.log(
|
|
6587
6845
|
`[ModuleRail] runId=${context.runId} composition artifacts mid-apply failure; rolling back writes=${appliedWrites.length} deletes=${appliedDeletes.length} newDirs=${newlyCreatedParentDirs.length}`
|
|
6588
6846
|
);
|
|
6589
|
-
for (const { path:
|
|
6847
|
+
for (const { path: path6, previous } of [...appliedDeletes].reverse()) {
|
|
6590
6848
|
try {
|
|
6591
|
-
await writeSandboxFileAtomic(context,
|
|
6849
|
+
await writeSandboxFileAtomic(context, path6, previous);
|
|
6592
6850
|
} catch (rollbackError) {
|
|
6593
6851
|
console.log(
|
|
6594
|
-
`[ModuleRail] runId=${context.runId} composition artifacts rollback restore failed for ${redactSecrets(
|
|
6852
|
+
`[ModuleRail] runId=${context.runId} composition artifacts rollback restore failed for ${redactSecrets(path6)}: ${redactSecrets(
|
|
6595
6853
|
rollbackError instanceof Error ? rollbackError.message : String(rollbackError)
|
|
6596
6854
|
)}`
|
|
6597
6855
|
);
|
|
6598
6856
|
}
|
|
6599
6857
|
}
|
|
6600
|
-
for (const { path:
|
|
6858
|
+
for (const { path: path6, previous } of [...appliedWrites].reverse()) {
|
|
6601
6859
|
try {
|
|
6602
6860
|
if (previous == null) {
|
|
6603
|
-
await deleteSandboxFile(context,
|
|
6861
|
+
await deleteSandboxFile(context, path6);
|
|
6604
6862
|
} else {
|
|
6605
|
-
await writeSandboxFileAtomic(context,
|
|
6863
|
+
await writeSandboxFileAtomic(context, path6, previous);
|
|
6606
6864
|
}
|
|
6607
6865
|
} catch (rollbackError) {
|
|
6608
6866
|
console.log(
|
|
6609
|
-
`[ModuleRail] runId=${context.runId} composition artifacts rollback write failed for ${redactSecrets(
|
|
6867
|
+
`[ModuleRail] runId=${context.runId} composition artifacts rollback write failed for ${redactSecrets(path6)}: ${redactSecrets(
|
|
6610
6868
|
rollbackError instanceof Error ? rollbackError.message : String(rollbackError)
|
|
6611
6869
|
)}`
|
|
6612
6870
|
);
|
|
@@ -6640,39 +6898,39 @@ async function listExistingGeneratedStubPaths(context) {
|
|
|
6640
6898
|
COMPOSITION_ARTIFACT_KIND_PROBE_TOKEN
|
|
6641
6899
|
);
|
|
6642
6900
|
const presentPaths = [];
|
|
6643
|
-
for (const
|
|
6901
|
+
for (const path6 of candidates) {
|
|
6644
6902
|
const presence = assertRealCompositionArtifactFilePresence(
|
|
6645
|
-
|
|
6646
|
-
kinds.get(
|
|
6903
|
+
path6,
|
|
6904
|
+
kinds.get(path6) ?? "unreadable"
|
|
6647
6905
|
);
|
|
6648
|
-
if (presence === "file") presentPaths.push(
|
|
6906
|
+
if (presence === "file") presentPaths.push(path6);
|
|
6649
6907
|
}
|
|
6650
6908
|
let contents;
|
|
6651
6909
|
try {
|
|
6652
6910
|
contents = await readFilesMany(context, presentPaths);
|
|
6653
6911
|
} catch (error) {
|
|
6654
|
-
const
|
|
6912
|
+
const path6 = readFailurePath(error);
|
|
6655
6913
|
throw new Error(
|
|
6656
|
-
`[ModuleRail] composition artifacts: cannot read candidate stub ${redactSecrets(
|
|
6914
|
+
`[ModuleRail] composition artifacts: cannot read candidate stub ${redactSecrets(path6)}: ${redactSecrets(
|
|
6657
6915
|
error instanceof Error ? error.message : String(error)
|
|
6658
6916
|
)}`
|
|
6659
6917
|
);
|
|
6660
6918
|
}
|
|
6661
6919
|
const owned = [];
|
|
6662
|
-
for (const
|
|
6663
|
-
const content = contents.get(
|
|
6920
|
+
for (const path6 of presentPaths) {
|
|
6921
|
+
const content = contents.get(path6);
|
|
6664
6922
|
const marker = parseCompositionArtifactMarker(content);
|
|
6665
|
-
if (parseLegacyApiAdapter(
|
|
6666
|
-
owned.push(
|
|
6923
|
+
if (parseLegacyApiAdapter(path6, content) || parseLegacyRouteAdapter(path6, content)) {
|
|
6924
|
+
owned.push(path6);
|
|
6667
6925
|
continue;
|
|
6668
6926
|
}
|
|
6669
6927
|
if (marker == null) continue;
|
|
6670
|
-
if (!markerCongruentWithPath(
|
|
6928
|
+
if (!markerCongruentWithPath(path6, marker)) {
|
|
6671
6929
|
throw new Error(
|
|
6672
|
-
`[ModuleRail] composition artifacts: incongruent marker at ${redactSecrets(
|
|
6930
|
+
`[ModuleRail] composition artifacts: incongruent marker at ${redactSecrets(path6)} (found ${marker.kind} marker on a path shaped for ${expectedMarkerKindForPath(path6) ?? "no"} artifacts; refusing to reconcile a swapped marker)`
|
|
6673
6931
|
);
|
|
6674
6932
|
}
|
|
6675
|
-
owned.push(
|
|
6933
|
+
owned.push(path6);
|
|
6676
6934
|
}
|
|
6677
6935
|
return owned;
|
|
6678
6936
|
}
|
|
@@ -6806,7 +7064,7 @@ async function classifySandboxPathPresenceKindMany(context, paths, probeToken) {
|
|
|
6806
7064
|
{ raiseOnError: false }
|
|
6807
7065
|
);
|
|
6808
7066
|
if (result.exitCode !== 0) {
|
|
6809
|
-
for (const
|
|
7067
|
+
for (const path6 of chunk) kinds.set(path6, "unreadable");
|
|
6810
7068
|
continue;
|
|
6811
7069
|
}
|
|
6812
7070
|
const lines = result.output.split("\n").filter(Boolean);
|
|
@@ -6817,31 +7075,31 @@ async function classifySandboxPathPresenceKindMany(context, paths, probeToken) {
|
|
|
6817
7075
|
}
|
|
6818
7076
|
} else {
|
|
6819
7077
|
for (const line of lines) {
|
|
6820
|
-
const [rawKind,
|
|
7078
|
+
const [rawKind, path6] = line.split(" ", 2);
|
|
6821
7079
|
const kind = rawKind?.replace(/^entry-kind:/, "");
|
|
6822
|
-
if (
|
|
6823
|
-
kinds.set(
|
|
7080
|
+
if (path6 && chunk.includes(path6) && CONVENTIONAL_ENTRY_PRESENCE_KINDS.includes(kind ?? "")) {
|
|
7081
|
+
kinds.set(path6, kind);
|
|
6824
7082
|
}
|
|
6825
7083
|
}
|
|
6826
7084
|
}
|
|
6827
|
-
for (const
|
|
6828
|
-
if (!kinds.has(
|
|
7085
|
+
for (const path6 of chunk) {
|
|
7086
|
+
if (!kinds.has(path6)) kinds.set(path6, "unreadable");
|
|
6829
7087
|
}
|
|
6830
7088
|
}
|
|
6831
7089
|
return kinds;
|
|
6832
7090
|
}
|
|
6833
|
-
async function classifySandboxPathPresenceKind(context,
|
|
6834
|
-
return (await classifySandboxPathPresenceKindMany(context, [
|
|
7091
|
+
async function classifySandboxPathPresenceKind(context, path6, probeToken) {
|
|
7092
|
+
return (await classifySandboxPathPresenceKindMany(context, [path6], probeToken)).get(path6);
|
|
6835
7093
|
}
|
|
6836
7094
|
var LOCALE_JSON_KIND_PROBE_TOKEN = "locale-json-kind";
|
|
6837
|
-
function assertRealLocaleJsonFilePresence(
|
|
7095
|
+
function assertRealLocaleJsonFilePresence(path6, presence) {
|
|
6838
7096
|
if (presence === "file") return;
|
|
6839
7097
|
if (presence === "absent") {
|
|
6840
|
-
throw new Error(`[ModuleRail] composition artifacts: locale file missing at ${
|
|
7098
|
+
throw new Error(`[ModuleRail] composition artifacts: locale file missing at ${path6}`);
|
|
6841
7099
|
}
|
|
6842
|
-
const presenceError = formatConventionalEntryPresenceError(
|
|
7100
|
+
const presenceError = formatConventionalEntryPresenceError(path6, presence);
|
|
6843
7101
|
throw new Error(
|
|
6844
|
-
`[ModuleRail] composition artifacts: ${presenceError ?? `${
|
|
7102
|
+
`[ModuleRail] composition artifacts: ${presenceError ?? `${path6} is not a real locale JSON file`}`
|
|
6845
7103
|
);
|
|
6846
7104
|
}
|
|
6847
7105
|
function splitTaggedLine(line) {
|
|
@@ -6916,11 +7174,11 @@ async function listLocaleBasenamesIfDirMany(context, dirs) {
|
|
|
6916
7174
|
}
|
|
6917
7175
|
async function readFilesMany(context, paths) {
|
|
6918
7176
|
const sortedPaths = [...paths].sort((a, b) => a.localeCompare(b));
|
|
6919
|
-
const settled = await mapConcurrent(sortedPaths, SANDBOX_READ_CONCURRENCY, async (
|
|
7177
|
+
const settled = await mapConcurrent(sortedPaths, SANDBOX_READ_CONCURRENCY, async (path6) => {
|
|
6920
7178
|
try {
|
|
6921
|
-
return { path:
|
|
7179
|
+
return { path: path6, value: await context.sandbox.readFile(path6) };
|
|
6922
7180
|
} catch (error) {
|
|
6923
|
-
return { path:
|
|
7181
|
+
return { path: path6, error };
|
|
6924
7182
|
}
|
|
6925
7183
|
});
|
|
6926
7184
|
const files = /* @__PURE__ */ new Map();
|
|
@@ -6943,61 +7201,61 @@ function readFailurePath(error) {
|
|
|
6943
7201
|
var CONVENTIONAL_ENTRY_KIND_PROBE_TOKEN = "conventional-entry-kind";
|
|
6944
7202
|
var SERVER_REGISTRATIONS_FILE = "server/registrations.ts";
|
|
6945
7203
|
var COMPOSITION_ARTIFACT_KIND_PROBE_TOKEN = "composition-artifact-kind";
|
|
6946
|
-
async function classifyCompositionArtifactPathKind(context,
|
|
6947
|
-
return classifySandboxPathPresenceKind(context,
|
|
7204
|
+
async function classifyCompositionArtifactPathKind(context, path6) {
|
|
7205
|
+
return classifySandboxPathPresenceKind(context, path6, COMPOSITION_ARTIFACT_KIND_PROBE_TOKEN);
|
|
6948
7206
|
}
|
|
6949
|
-
function assertRealCompositionArtifactFilePresence(
|
|
7207
|
+
function assertRealCompositionArtifactFilePresence(path6, presence) {
|
|
6950
7208
|
if (presence === "file") return "file";
|
|
6951
7209
|
if (presence === "absent") return "absent";
|
|
6952
|
-
const presenceError = formatConventionalEntryPresenceError(
|
|
7210
|
+
const presenceError = formatConventionalEntryPresenceError(path6, presence);
|
|
6953
7211
|
throw new Error(
|
|
6954
|
-
`[ModuleRail] composition artifacts: ${presenceError ?? `${
|
|
7212
|
+
`[ModuleRail] composition artifacts: ${presenceError ?? `${path6} is not a real composition artifact file`}`
|
|
6955
7213
|
);
|
|
6956
7214
|
}
|
|
6957
|
-
async function readConventionalEntryIfRealFile(
|
|
6958
|
-
const presence = presences.get(
|
|
6959
|
-
const presenceError = formatConventionalEntryPresenceError(
|
|
7215
|
+
async function readConventionalEntryIfRealFile(path6, kind, presences, files) {
|
|
7216
|
+
const presence = presences.get(path6) ?? "unreadable";
|
|
7217
|
+
const presenceError = formatConventionalEntryPresenceError(path6, presence);
|
|
6960
7218
|
if (presenceError) {
|
|
6961
7219
|
throw new Error(`[ModuleRail] composition artifacts: ${presenceError}`);
|
|
6962
7220
|
}
|
|
6963
7221
|
if (presence === "absent") {
|
|
6964
7222
|
return null;
|
|
6965
7223
|
}
|
|
6966
|
-
const source = files.get(
|
|
7224
|
+
const source = files.get(path6);
|
|
6967
7225
|
switch (kind) {
|
|
6968
7226
|
case "contributions":
|
|
6969
|
-
assertContributionsExport(source,
|
|
6970
|
-
assertClientSafeConventionalEntryImports(source,
|
|
7227
|
+
assertContributionsExport(source, path6);
|
|
7228
|
+
assertClientSafeConventionalEntryImports(source, path6, "contributions");
|
|
6971
7229
|
break;
|
|
6972
7230
|
case "slotCatalogs":
|
|
6973
|
-
assertSlotCatalogsExport(source,
|
|
6974
|
-
assertClientSafeConventionalEntryImports(source,
|
|
7231
|
+
assertSlotCatalogsExport(source, path6);
|
|
7232
|
+
assertClientSafeConventionalEntryImports(source, path6, "slotCatalogs");
|
|
6975
7233
|
break;
|
|
6976
7234
|
case "initializeEnhancement":
|
|
6977
|
-
assertInitializeEnhancementExport(source,
|
|
7235
|
+
assertInitializeEnhancementExport(source, path6);
|
|
6978
7236
|
break;
|
|
6979
7237
|
case "initializeModule":
|
|
6980
|
-
assertInitializeModuleExport(source,
|
|
7238
|
+
assertInitializeModuleExport(source, path6);
|
|
6981
7239
|
break;
|
|
6982
7240
|
}
|
|
6983
7241
|
return source;
|
|
6984
7242
|
}
|
|
6985
7243
|
function hasRealServerRegistrations(modulePath, presences) {
|
|
6986
|
-
const
|
|
6987
|
-
return assertRealCompositionArtifactFilePresence(
|
|
7244
|
+
const path6 = `${modulePath}/${SERVER_REGISTRATIONS_FILE}`;
|
|
7245
|
+
return assertRealCompositionArtifactFilePresence(path6, presences.get(path6) ?? "unreadable") === "file";
|
|
6988
7246
|
}
|
|
6989
|
-
async function readJsonObjectFile(
|
|
6990
|
-
const raw = files.get(
|
|
7247
|
+
async function readJsonObjectFile(path6, files) {
|
|
7248
|
+
const raw = files.get(path6);
|
|
6991
7249
|
let parsed;
|
|
6992
7250
|
try {
|
|
6993
7251
|
parsed = JSON.parse(raw);
|
|
6994
7252
|
} catch (error) {
|
|
6995
7253
|
throw new Error(
|
|
6996
|
-
`[ModuleRail] composition artifacts: invalid JSON at ${
|
|
7254
|
+
`[ModuleRail] composition artifacts: invalid JSON at ${path6}: ${error instanceof Error ? error.message : String(error)}`
|
|
6997
7255
|
);
|
|
6998
7256
|
}
|
|
6999
7257
|
if (parsed === null || Array.isArray(parsed) || typeof parsed !== "object") {
|
|
7000
|
-
throw new Error(`[ModuleRail] composition artifacts: ${
|
|
7258
|
+
throw new Error(`[ModuleRail] composition artifacts: ${path6} root must be an object`);
|
|
7001
7259
|
}
|
|
7002
7260
|
return parsed;
|
|
7003
7261
|
}
|
|
@@ -7050,11 +7308,11 @@ async function collectCompositionEntryFacts(context, registry) {
|
|
|
7050
7308
|
classifySandboxPathPresenceKindMany(context, localePaths, LOCALE_JSON_KIND_PROBE_TOKEN)
|
|
7051
7309
|
]);
|
|
7052
7310
|
const presentConventionalPaths = [];
|
|
7053
|
-
for (const
|
|
7054
|
-
const presence = conventionalPresences.get(
|
|
7055
|
-
if (presence === "file") presentConventionalPaths.push(
|
|
7311
|
+
for (const path6 of conventionalPaths) {
|
|
7312
|
+
const presence = conventionalPresences.get(path6) ?? "unreadable";
|
|
7313
|
+
if (presence === "file") presentConventionalPaths.push(path6);
|
|
7056
7314
|
}
|
|
7057
|
-
const presentLocalePaths = localePaths.filter((
|
|
7315
|
+
const presentLocalePaths = localePaths.filter((path6) => localePresences.get(path6) === "file");
|
|
7058
7316
|
const files = await readFilesMany(context, [...presentConventionalPaths, ...presentLocalePaths]);
|
|
7059
7317
|
const substrateLocales = [];
|
|
7060
7318
|
for (const locale of substrateNames) {
|
|
@@ -7164,8 +7422,8 @@ async function collectCompositionEntryFacts(context, registry) {
|
|
|
7164
7422
|
});
|
|
7165
7423
|
if (rootLocales.length > 0) {
|
|
7166
7424
|
for (const locale of rootLocales) {
|
|
7167
|
-
const
|
|
7168
|
-
assertRealLocaleJsonFilePresence(
|
|
7425
|
+
const path6 = `${modulePath}/i18n/${locale}.json`;
|
|
7426
|
+
assertRealLocaleJsonFilePresence(path6, localePresences.get(path6) ?? "unreadable");
|
|
7169
7427
|
}
|
|
7170
7428
|
const enMessages = await readJsonObjectFile(`${modulePath}/i18n/en.json`, files);
|
|
7171
7429
|
const localeMessages = { en: enMessages };
|
|
@@ -7189,8 +7447,8 @@ async function collectCompositionEntryFacts(context, registry) {
|
|
|
7189
7447
|
if (!enh.active || enh.locales.length === 0) continue;
|
|
7190
7448
|
const peerDir = `${modulePath}/enhancements/${enh.peer}`;
|
|
7191
7449
|
for (const locale of enh.locales) {
|
|
7192
|
-
const
|
|
7193
|
-
assertRealLocaleJsonFilePresence(
|
|
7450
|
+
const path6 = `${peerDir}/i18n/${locale}.json`;
|
|
7451
|
+
assertRealLocaleJsonFilePresence(path6, localePresences.get(path6) ?? "unreadable");
|
|
7194
7452
|
}
|
|
7195
7453
|
const enMessages = await readJsonObjectFile(`${peerDir}/i18n/en.json`, files);
|
|
7196
7454
|
const localeMessages = { en: enMessages };
|
|
@@ -7398,16 +7656,16 @@ async function reconcileCompositionArtifacts(context, options = {}) {
|
|
|
7398
7656
|
for (const stub of probePlan.plan.routeStubs) candidatePaths.add(stub.path);
|
|
7399
7657
|
const existingFiles = {};
|
|
7400
7658
|
const remainingCandidatePaths = [];
|
|
7401
|
-
for (const
|
|
7402
|
-
if (Object.prototype.hasOwnProperty.call(existingAppPages,
|
|
7403
|
-
existingFiles[
|
|
7659
|
+
for (const path6 of [...candidatePaths].sort((a, b) => a.localeCompare(b))) {
|
|
7660
|
+
if (Object.prototype.hasOwnProperty.call(existingAppPages, path6)) {
|
|
7661
|
+
existingFiles[path6] = existingAppPages[path6];
|
|
7404
7662
|
continue;
|
|
7405
7663
|
}
|
|
7406
|
-
if (Object.prototype.hasOwnProperty.call(existingAppRoutes,
|
|
7407
|
-
existingFiles[
|
|
7664
|
+
if (Object.prototype.hasOwnProperty.call(existingAppRoutes, path6)) {
|
|
7665
|
+
existingFiles[path6] = existingAppRoutes[path6];
|
|
7408
7666
|
continue;
|
|
7409
7667
|
}
|
|
7410
|
-
remainingCandidatePaths.push(
|
|
7668
|
+
remainingCandidatePaths.push(path6);
|
|
7411
7669
|
}
|
|
7412
7670
|
const candidateKinds = await classifySandboxPathPresenceKindMany(
|
|
7413
7671
|
context,
|
|
@@ -7415,12 +7673,12 @@ async function reconcileCompositionArtifacts(context, options = {}) {
|
|
|
7415
7673
|
COMPOSITION_ARTIFACT_KIND_PROBE_TOKEN
|
|
7416
7674
|
);
|
|
7417
7675
|
const presentCandidatePaths = [];
|
|
7418
|
-
for (const
|
|
7676
|
+
for (const path6 of remainingCandidatePaths) {
|
|
7419
7677
|
const presence = assertRealCompositionArtifactFilePresence(
|
|
7420
|
-
|
|
7421
|
-
candidateKinds.get(
|
|
7678
|
+
path6,
|
|
7679
|
+
candidateKinds.get(path6) ?? "unreadable"
|
|
7422
7680
|
);
|
|
7423
|
-
if (presence === "file") presentCandidatePaths.push(
|
|
7681
|
+
if (presence === "file") presentCandidatePaths.push(path6);
|
|
7424
7682
|
}
|
|
7425
7683
|
try {
|
|
7426
7684
|
Object.assign(
|
|
@@ -7428,9 +7686,9 @@ async function reconcileCompositionArtifacts(context, options = {}) {
|
|
|
7428
7686
|
Object.fromEntries(await readFilesMany(context, presentCandidatePaths))
|
|
7429
7687
|
);
|
|
7430
7688
|
} catch (error) {
|
|
7431
|
-
const
|
|
7689
|
+
const path6 = readFailurePath(error);
|
|
7432
7690
|
throw new Error(
|
|
7433
|
-
`[ModuleRail] composition artifacts: cannot read ${redactSecrets(
|
|
7691
|
+
`[ModuleRail] composition artifacts: cannot read ${redactSecrets(path6)} during preflight: ${redactSecrets(
|
|
7434
7692
|
error instanceof Error ? error.message : String(error)
|
|
7435
7693
|
)}`
|
|
7436
7694
|
);
|
|
@@ -7503,8 +7761,8 @@ var HOME_I18N_MESSAGES_DIR = `${APP_PACKAGE_DIR}/src/lib/i18n/messages`;
|
|
|
7503
7761
|
var import_typescript6 = __toESM(require("typescript"));
|
|
7504
7762
|
|
|
7505
7763
|
// src/module-boundaries.ts
|
|
7506
|
-
var
|
|
7507
|
-
var
|
|
7764
|
+
var import_node_fs3 = __toESM(require("fs"));
|
|
7765
|
+
var import_node_path3 = __toESM(require("path"));
|
|
7508
7766
|
var import_typescript5 = __toESM(require("typescript"));
|
|
7509
7767
|
var SOURCE_FILE = /\.[cm]?[jt]sx?$/;
|
|
7510
7768
|
var RESOLVE_SUFFIXES = ["", ".ts", ".tsx", ".js", ".jsx", "/index.ts", "/index.tsx", "/index.js"];
|
|
@@ -7523,26 +7781,26 @@ function dataPathsIn(fileName, source) {
|
|
|
7523
7781
|
}
|
|
7524
7782
|
function scanModule(modulesDir, module2) {
|
|
7525
7783
|
const scanned = /* @__PURE__ */ new Map();
|
|
7526
|
-
const moduleDir =
|
|
7527
|
-
if (!
|
|
7528
|
-
const files =
|
|
7784
|
+
const moduleDir = import_node_path3.default.join(modulesDir, module2);
|
|
7785
|
+
if (!import_node_fs3.default.existsSync(moduleDir)) return scanned;
|
|
7786
|
+
const files = import_node_fs3.default.readdirSync(moduleDir, { recursive: true, encoding: "utf8" }).filter((relative) => SOURCE_FILE.test(relative) && !relative.includes("node_modules"));
|
|
7529
7787
|
for (const relative of files) {
|
|
7530
|
-
const absolute =
|
|
7531
|
-
if (!
|
|
7532
|
-
const source =
|
|
7788
|
+
const absolute = import_node_path3.default.join(moduleDir, relative);
|
|
7789
|
+
if (!import_node_fs3.default.statSync(absolute).isFile()) continue;
|
|
7790
|
+
const source = import_node_fs3.default.readFileSync(absolute, "utf8");
|
|
7533
7791
|
const specifiers = import_typescript5.default.preProcessFile(source, true, true).importedFiles.map((f) => f.fileName);
|
|
7534
7792
|
const dataPaths = dataPathsIn(absolute, source);
|
|
7535
7793
|
const targets = [...specifiers, ...dataPaths].flatMap((spec) => {
|
|
7536
7794
|
if (spec.startsWith("@/modules/"))
|
|
7537
|
-
return [
|
|
7795
|
+
return [import_node_path3.default.join(modulesDir, spec.slice("@/modules/".length))];
|
|
7538
7796
|
if (spec.startsWith("./") || spec.startsWith("../")) {
|
|
7539
|
-
return [
|
|
7797
|
+
return [import_node_path3.default.resolve(import_node_path3.default.dirname(absolute), spec)];
|
|
7540
7798
|
}
|
|
7541
7799
|
return [];
|
|
7542
7800
|
});
|
|
7543
7801
|
scanned.set(absolute, {
|
|
7544
7802
|
module: module2,
|
|
7545
|
-
relative: relative.split(
|
|
7803
|
+
relative: relative.split(import_node_path3.default.sep).join("/"),
|
|
7546
7804
|
targets,
|
|
7547
7805
|
dataPaths
|
|
7548
7806
|
});
|
|
@@ -7559,7 +7817,7 @@ function findModuleBoundaryViolations(modulesDir, peersByModule, { pruning = fal
|
|
|
7559
7817
|
(q) => file.relative.startsWith(`enhancements/${q}/`)
|
|
7560
7818
|
);
|
|
7561
7819
|
const goneWith = (target, peer) => {
|
|
7562
|
-
const relative =
|
|
7820
|
+
const relative = import_node_path3.default.relative(modulesDir, target).split(import_node_path3.default.sep).join("/");
|
|
7563
7821
|
return relative === peer || relative.startsWith(`${peer}/`) || new RegExp(`^[^/]+/enhancements/${peer}(/|$)`).test(relative);
|
|
7564
7822
|
};
|
|
7565
7823
|
const tainted = /* @__PURE__ */ new Map();
|
|
@@ -7605,12 +7863,12 @@ function findModuleBoundaryViolations(modulesDir, peersByModule, { pruning = fal
|
|
|
7605
7863
|
return violations.sort((a, b) => a.file.localeCompare(b.file) || a.peer.localeCompare(b.peer));
|
|
7606
7864
|
}
|
|
7607
7865
|
function findOptionalPeerViolations(modulesDir) {
|
|
7608
|
-
if (!
|
|
7866
|
+
if (!import_node_fs3.default.existsSync(modulesDir)) return [];
|
|
7609
7867
|
const manifests = {};
|
|
7610
|
-
for (const name of
|
|
7611
|
-
const manifestPath =
|
|
7612
|
-
if (!
|
|
7613
|
-
manifests[name] = JSON.parse(
|
|
7868
|
+
for (const name of import_node_fs3.default.readdirSync(modulesDir)) {
|
|
7869
|
+
const manifestPath = import_node_path3.default.join(modulesDir, name, "module.json");
|
|
7870
|
+
if (!import_node_fs3.default.existsSync(manifestPath)) continue;
|
|
7871
|
+
manifests[name] = JSON.parse(import_node_fs3.default.readFileSync(manifestPath, "utf8"));
|
|
7614
7872
|
}
|
|
7615
7873
|
const features = Object.keys(manifests).filter((name) => manifests[name].kind === "feature");
|
|
7616
7874
|
const peersByModule = {};
|
|
@@ -7630,11 +7888,11 @@ function formatBoundaryViolations(modulesDirLabel, violations) {
|
|
|
7630
7888
|
// src/index.ts
|
|
7631
7889
|
var MODULES_DIR2 = MODULES_SANDBOX_DIR;
|
|
7632
7890
|
function readComposeInputs(cwd) {
|
|
7633
|
-
const file =
|
|
7634
|
-
if (!
|
|
7891
|
+
const file = import_node_path4.default.join(cwd, COMPOSE_INPUTS_PATH);
|
|
7892
|
+
if (!import_node_fs4.default.existsSync(file)) return { connectedStores: [], installs: {} };
|
|
7635
7893
|
let raw;
|
|
7636
7894
|
try {
|
|
7637
|
-
raw = JSON.parse(
|
|
7895
|
+
raw = JSON.parse(import_node_fs4.default.readFileSync(file, "utf8"));
|
|
7638
7896
|
} catch (error) {
|
|
7639
7897
|
throw new Error(
|
|
7640
7898
|
`[compose] ${file} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`
|
|
@@ -7676,18 +7934,18 @@ function stubGitignoreEntries(artifactPaths) {
|
|
|
7676
7934
|
function writeStubGitignore(cwd, artifactPaths) {
|
|
7677
7935
|
const entries = stubGitignoreEntries(artifactPaths);
|
|
7678
7936
|
const content = [STUB_GITIGNORE_HEADER, ...entries].join("\n") + "\n";
|
|
7679
|
-
const file =
|
|
7680
|
-
const existing =
|
|
7937
|
+
const file = import_node_path4.default.join(cwd, STUB_GITIGNORE_PATH);
|
|
7938
|
+
const existing = import_node_fs4.default.existsSync(file) ? import_node_fs4.default.readFileSync(file, "utf8") : null;
|
|
7681
7939
|
if (existing !== content) {
|
|
7682
|
-
|
|
7683
|
-
|
|
7940
|
+
import_node_fs4.default.mkdirSync(import_node_path4.default.dirname(file), { recursive: true });
|
|
7941
|
+
import_node_fs4.default.writeFileSync(file, content);
|
|
7684
7942
|
}
|
|
7685
7943
|
return entries.length;
|
|
7686
7944
|
}
|
|
7687
7945
|
function assertStubGitignoreOwned(cwd) {
|
|
7688
|
-
const file =
|
|
7689
|
-
if (!
|
|
7690
|
-
const firstLine =
|
|
7946
|
+
const file = import_node_path4.default.join(cwd, STUB_GITIGNORE_PATH);
|
|
7947
|
+
if (!import_node_fs4.default.existsSync(file)) return;
|
|
7948
|
+
const firstLine = import_node_fs4.default.readFileSync(file, "utf8").split("\n", 1)[0] ?? "";
|
|
7691
7949
|
if (firstLine !== STUB_GITIGNORE_HEADER) {
|
|
7692
7950
|
throw new Error(
|
|
7693
7951
|
`[compose] refusing to overwrite ${STUB_GITIGNORE_PATH}: it is not compose-generated (line 1 is not the generated header)`
|
|
@@ -7695,8 +7953,8 @@ function assertStubGitignoreOwned(cwd) {
|
|
|
7695
7953
|
}
|
|
7696
7954
|
}
|
|
7697
7955
|
function assertAppRoot(cwd) {
|
|
7698
|
-
const appPath =
|
|
7699
|
-
if (!
|
|
7956
|
+
const appPath = import_node_path4.default.join(cwd, APP_PACKAGE_JSON);
|
|
7957
|
+
if (!import_node_fs4.default.existsSync(appPath)) {
|
|
7700
7958
|
throw new Error(
|
|
7701
7959
|
`[compose] ${appPath} not found \u2014 run this from the repo root of a Stardeck app`
|
|
7702
7960
|
);
|
|
@@ -7742,9 +8000,9 @@ ${porcelain.trim()}`
|
|
|
7742
8000
|
}
|
|
7743
8001
|
var APP_SRC_DIR = "apps/web/src";
|
|
7744
8002
|
function walkSources(dir, skip, out) {
|
|
7745
|
-
if (!
|
|
7746
|
-
for (const entry of
|
|
7747
|
-
const full =
|
|
8003
|
+
if (!import_node_fs4.default.existsSync(dir)) return;
|
|
8004
|
+
for (const entry of import_node_fs4.default.readdirSync(dir, { withFileTypes: true })) {
|
|
8005
|
+
const full = import_node_path4.default.join(dir, entry.name);
|
|
7748
8006
|
if (full === skip) continue;
|
|
7749
8007
|
if (entry.isDirectory()) walkSources(full, skip, out);
|
|
7750
8008
|
else if (/\.[cm]?[jt]sx?$/.test(entry.name)) out.push(full);
|
|
@@ -7753,13 +8011,13 @@ function walkSources(dir, skip, out) {
|
|
|
7753
8011
|
function assertNoAppLayerImporters(cwd, excluded) {
|
|
7754
8012
|
if (excluded.length === 0) return;
|
|
7755
8013
|
const files = [];
|
|
7756
|
-
walkSources(
|
|
8014
|
+
walkSources(import_node_path4.default.join(cwd, APP_SRC_DIR), import_node_path4.default.join(cwd, MODULES_DIR2), files);
|
|
7757
8015
|
const offenders = [];
|
|
7758
8016
|
for (const file of files) {
|
|
7759
|
-
const source =
|
|
8017
|
+
const source = import_node_fs4.default.readFileSync(file, "utf8");
|
|
7760
8018
|
for (const name of excluded) {
|
|
7761
8019
|
if (new RegExp(`@/modules/${name}(?=["'\`/])`).test(source)) {
|
|
7762
|
-
offenders.push(`${
|
|
8020
|
+
offenders.push(`${import_node_path4.default.relative(cwd, file)}: ${name}`);
|
|
7763
8021
|
}
|
|
7764
8022
|
}
|
|
7765
8023
|
}
|
|
@@ -7793,8 +8051,8 @@ var BUILD_TYPECHECK_SKIP = [
|
|
|
7793
8051
|
"playwright.config.ts"
|
|
7794
8052
|
];
|
|
7795
8053
|
function writeBuildTsconfig(cwd, excluded) {
|
|
7796
|
-
const baseFile =
|
|
7797
|
-
if (!
|
|
8054
|
+
const baseFile = import_node_path4.default.join(cwd, TSCONFIG_PATH);
|
|
8055
|
+
if (!import_node_fs4.default.existsSync(import_node_path4.default.join(cwd, MODULES_DIR2)) || !import_node_fs4.default.existsSync(baseFile)) return;
|
|
7798
8056
|
const base = import_typescript6.default.readConfigFile(baseFile, import_typescript6.default.sys.readFile);
|
|
7799
8057
|
if (base.error) {
|
|
7800
8058
|
throw new Error(
|
|
@@ -7802,7 +8060,7 @@ function writeBuildTsconfig(cwd, excluded) {
|
|
|
7802
8060
|
);
|
|
7803
8061
|
}
|
|
7804
8062
|
const baseExclude = base.config?.exclude ?? ["node_modules"];
|
|
7805
|
-
const modulesDir =
|
|
8063
|
+
const modulesDir = import_node_path4.default.posix.relative("apps/web", MODULES_DIR2);
|
|
7806
8064
|
const config = {
|
|
7807
8065
|
extends: "./tsconfig.json",
|
|
7808
8066
|
exclude: [
|
|
@@ -7817,17 +8075,17 @@ function writeBuildTsconfig(cwd, excluded) {
|
|
|
7817
8075
|
const content = `${BUILD_TSCONFIG_HEADER}
|
|
7818
8076
|
${JSON.stringify(config, null, 2)}
|
|
7819
8077
|
`;
|
|
7820
|
-
const file =
|
|
7821
|
-
if (!
|
|
7822
|
-
|
|
8078
|
+
const file = import_node_path4.default.join(cwd, BUILD_TSCONFIG_PATH);
|
|
8079
|
+
if (!import_node_fs4.default.existsSync(file) || import_node_fs4.default.readFileSync(file, "utf8") !== content) {
|
|
8080
|
+
import_node_fs4.default.writeFileSync(file, content);
|
|
7823
8081
|
}
|
|
7824
8082
|
console.log(
|
|
7825
8083
|
`[compose] ${BUILD_TSCONFIG_PATH} excludes ${excluded.length} Module(s): ${excluded.join(",") || "none"}`
|
|
7826
8084
|
);
|
|
7827
8085
|
}
|
|
7828
8086
|
function removeDir(absolute) {
|
|
7829
|
-
if (!
|
|
7830
|
-
|
|
8087
|
+
if (!import_node_fs4.default.existsSync(absolute)) return false;
|
|
8088
|
+
import_node_fs4.default.rmSync(absolute, { recursive: true, force: true });
|
|
7831
8089
|
return true;
|
|
7832
8090
|
}
|
|
7833
8091
|
async function compose(options) {
|
|
@@ -7855,7 +8113,7 @@ async function compose(options) {
|
|
|
7855
8113
|
});
|
|
7856
8114
|
assertNoAppLayerImporters(options.cwd, preview.excludedModules);
|
|
7857
8115
|
const violations = findModuleBoundaryViolations(
|
|
7858
|
-
|
|
8116
|
+
import_node_path4.default.join(options.cwd, MODULES_DIR2),
|
|
7859
8117
|
Object.fromEntries(preview.composedModules.map((name) => [name, preview.excludedModules])),
|
|
7860
8118
|
{ pruning: true }
|
|
7861
8119
|
);
|
|
@@ -7876,11 +8134,11 @@ async function compose(options) {
|
|
|
7876
8134
|
let pruned = 0;
|
|
7877
8135
|
if (options.prune) {
|
|
7878
8136
|
for (const name of result.excludedModules) {
|
|
7879
|
-
if (removeDir(
|
|
8137
|
+
if (removeDir(import_node_path4.default.join(options.cwd, MODULES_DIR2, name))) pruned++;
|
|
7880
8138
|
}
|
|
7881
8139
|
for (const owner of result.composedModules) {
|
|
7882
8140
|
for (const peer of result.excludedModules) {
|
|
7883
|
-
if (removeDir(
|
|
8141
|
+
if (removeDir(import_node_path4.default.join(options.cwd, MODULES_DIR2, owner, "enhancements", peer))) pruned++;
|
|
7884
8142
|
}
|
|
7885
8143
|
}
|
|
7886
8144
|
}
|
|
@@ -7902,13 +8160,18 @@ async function main() {
|
|
|
7902
8160
|
enabled: { type: "string" },
|
|
7903
8161
|
prune: { type: "boolean", default: false },
|
|
7904
8162
|
cwd: { type: "string" },
|
|
7905
|
-
"check-boundaries": { type: "boolean", default: false }
|
|
8163
|
+
"check-boundaries": { type: "boolean", default: false },
|
|
8164
|
+
"check-ownership": { type: "boolean", default: false }
|
|
7906
8165
|
},
|
|
7907
8166
|
strict: true
|
|
7908
8167
|
});
|
|
8168
|
+
if (values["check-ownership"]) {
|
|
8169
|
+
await checkOwnership(values.cwd ?? process.cwd());
|
|
8170
|
+
return;
|
|
8171
|
+
}
|
|
7909
8172
|
if (values["check-boundaries"]) {
|
|
7910
8173
|
const violations = findOptionalPeerViolations(
|
|
7911
|
-
|
|
8174
|
+
import_node_path5.default.join(values.cwd ?? process.cwd(), MODULES_SANDBOX_DIR)
|
|
7912
8175
|
);
|
|
7913
8176
|
if (violations.length > 0) {
|
|
7914
8177
|
throw new Error(
|
|
@@ -7933,6 +8196,36 @@ async function main() {
|
|
|
7933
8196
|
`[compose] modules=${result.composedModules.join(",") || "none"} excluded=${result.excludedModules.join(",") || "none"} writes=${result.writes} deletes=${result.deletes} pruned=${result.pruned} gitignore=${result.gitignoreEntries ?? "kept"} stub paths`
|
|
7934
8197
|
);
|
|
7935
8198
|
}
|
|
8199
|
+
function gitAt(cwd) {
|
|
8200
|
+
return (args) => new Promise((resolve, reject) => {
|
|
8201
|
+
const child = (0, import_node_child_process3.spawn)("git", ["-c", "core.quotePath=false", ...args], {
|
|
8202
|
+
cwd,
|
|
8203
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
8204
|
+
});
|
|
8205
|
+
let stdout = "";
|
|
8206
|
+
child.stdout.setEncoding("utf8").on("data", (chunk) => stdout += chunk);
|
|
8207
|
+
child.on("error", reject);
|
|
8208
|
+
child.on("close", (status) => resolve({ status: status ?? 1, stdout }));
|
|
8209
|
+
});
|
|
8210
|
+
}
|
|
8211
|
+
async function checkOwnership(cwd) {
|
|
8212
|
+
const repoRoot = import_node_path5.default.resolve(cwd);
|
|
8213
|
+
const result = await checkStardeckOwnedFiles({ repoRoot, git: gitAt(repoRoot) });
|
|
8214
|
+
switch (result.status) {
|
|
8215
|
+
case "skipped":
|
|
8216
|
+
if (result.warning)
|
|
8217
|
+
console.warn(`[compose] --check-ownership: not checked: ${result.reason}`);
|
|
8218
|
+
else console.log(`[compose] --check-ownership: skipped: ${result.reason}`);
|
|
8219
|
+
return;
|
|
8220
|
+
case "clean":
|
|
8221
|
+
console.log(
|
|
8222
|
+
`[compose] --check-ownership: no Stardeck-owned file changed from Stardeck's version (${result.release.slice(0, 12)})`
|
|
8223
|
+
);
|
|
8224
|
+
return;
|
|
8225
|
+
default:
|
|
8226
|
+
throw new Error(`[compose] --check-ownership: ${formatOwnershipGuardFailure(result)}`);
|
|
8227
|
+
}
|
|
8228
|
+
}
|
|
7936
8229
|
main().catch((error) => {
|
|
7937
8230
|
console.error(error instanceof Error ? error.message : String(error));
|
|
7938
8231
|
process.exit(1);
|