@stardeck-customer-apps/compose 0.14.1 → 0.16.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 +721 -417
- package/dist/index.js +189 -155
- package/dist/index.mjs +189 -155
- 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
|
}
|
|
@@ -2266,7 +2278,18 @@ var modulePermissionClaimSchema = import_zod.z.object({
|
|
|
2266
2278
|
* External Viewer never holds one. An organization that wants different access duplicates the
|
|
2267
2279
|
* standard role and edits the copy.
|
|
2268
2280
|
*/
|
|
2269
|
-
roles: import_zod.z.array(import_zod.z.enum(MODULE_PERMISSION_DEFAULT_ROLES)).optional()
|
|
2281
|
+
roles: import_zod.z.array(import_zod.z.enum(MODULE_PERMISSION_DEFAULT_ROLES)).optional(),
|
|
2282
|
+
/**
|
|
2283
|
+
* Keys this one takes over (a retired or renamed key). On the update that stops declaring
|
|
2284
|
+
* them, every custom role still holding one is granted this key before the old one goes,
|
|
2285
|
+
* so nobody loses access in the change. Standard roles follow `roles` as always.
|
|
2286
|
+
*/
|
|
2287
|
+
replaces: import_zod.z.array(
|
|
2288
|
+
import_zod.z.string().regex(
|
|
2289
|
+
/^[a-z0-9][a-z0-9-]*:[a-z0-9][a-z0-9-]*$/,
|
|
2290
|
+
"must be a lowercase resource:action key"
|
|
2291
|
+
)
|
|
2292
|
+
).max(20).optional()
|
|
2270
2293
|
}).strict();
|
|
2271
2294
|
var seedBundleSchema = import_zod.z.object({
|
|
2272
2295
|
table: tableNameSchema,
|
|
@@ -2414,16 +2437,16 @@ var moduleManifestSchema = import_zod.z.object({
|
|
|
2414
2437
|
}
|
|
2415
2438
|
}
|
|
2416
2439
|
const seenWorkflows = /* @__PURE__ */ new Map();
|
|
2417
|
-
const noteWorkflow = (name,
|
|
2440
|
+
const noteWorkflow = (name, path6) => {
|
|
2418
2441
|
const existing = seenWorkflows.get(name);
|
|
2419
2442
|
if (existing != null) {
|
|
2420
2443
|
ctx.addIssue({
|
|
2421
2444
|
code: import_zod.z.ZodIssueCode.custom,
|
|
2422
2445
|
message: `duplicate workflow name "${name}" (also at ${existing})`,
|
|
2423
|
-
path:
|
|
2446
|
+
path: path6
|
|
2424
2447
|
});
|
|
2425
2448
|
} else {
|
|
2426
|
-
seenWorkflows.set(name,
|
|
2449
|
+
seenWorkflows.set(name, path6.join("."));
|
|
2427
2450
|
}
|
|
2428
2451
|
};
|
|
2429
2452
|
for (let i = 0; i < manifest.workflows.length; i += 1) {
|
|
@@ -2435,7 +2458,7 @@ var moduleManifestSchema = import_zod.z.object({
|
|
|
2435
2458
|
}
|
|
2436
2459
|
}
|
|
2437
2460
|
const seenEndpointKeys = /* @__PURE__ */ new Map();
|
|
2438
|
-
const noteEndpoint = (claim,
|
|
2461
|
+
const noteEndpoint = (claim, path6) => {
|
|
2439
2462
|
let key;
|
|
2440
2463
|
try {
|
|
2441
2464
|
key = normalizeEndpointPathForCollision(claim.path);
|
|
@@ -2447,10 +2470,10 @@ var moduleManifestSchema = import_zod.z.object({
|
|
|
2447
2470
|
ctx.addIssue({
|
|
2448
2471
|
code: import_zod.z.ZodIssueCode.custom,
|
|
2449
2472
|
message: `duplicate normalized endpoint path "${key}" (also at ${existing})`,
|
|
2450
|
-
path:
|
|
2473
|
+
path: path6
|
|
2451
2474
|
});
|
|
2452
2475
|
} else {
|
|
2453
|
-
seenEndpointKeys.set(key,
|
|
2476
|
+
seenEndpointKeys.set(key, path6.join("."));
|
|
2454
2477
|
}
|
|
2455
2478
|
};
|
|
2456
2479
|
for (let i = 0; i < manifest.endpoints.length; i += 1) {
|
|
@@ -2551,8 +2574,8 @@ var moduleManifestSchema = import_zod.z.object({
|
|
|
2551
2574
|
}).transform(({ version: _legacyVersion, ...manifest }) => manifest);
|
|
2552
2575
|
function formatZodErrors(error) {
|
|
2553
2576
|
return error.issues.map((issue) => {
|
|
2554
|
-
const
|
|
2555
|
-
return `${
|
|
2577
|
+
const path6 = issue.path.length > 0 ? issue.path.join(".") : "root";
|
|
2578
|
+
return `${path6}: ${issue.message}`;
|
|
2556
2579
|
});
|
|
2557
2580
|
}
|
|
2558
2581
|
function parseModuleManifest(raw) {
|
|
@@ -2952,16 +2975,16 @@ var collectedE2eFactsSchema = import_zod3.z.object({
|
|
|
2952
2975
|
function validateRouteExportEvidenceCongruence(input) {
|
|
2953
2976
|
const entrySet = new Set(input.routeEntries);
|
|
2954
2977
|
const exportPaths = input.routePageExports.map((e) => e.nextRelativePath);
|
|
2955
|
-
for (const
|
|
2956
|
-
if (!entrySet.has(
|
|
2957
|
-
input.onIssue(`routePageExports path "${
|
|
2978
|
+
for (const path6 of exportPaths) {
|
|
2979
|
+
if (!entrySet.has(path6)) {
|
|
2980
|
+
input.onIssue(`routePageExports path "${path6}" is not listed in routeEntries`);
|
|
2958
2981
|
}
|
|
2959
2982
|
}
|
|
2960
2983
|
if (input.routeCollectionErrors.length === 0) {
|
|
2961
2984
|
const exportSet = new Set(exportPaths);
|
|
2962
|
-
for (const
|
|
2963
|
-
if (!exportSet.has(
|
|
2964
|
-
input.onIssue(`routeEntries path "${
|
|
2985
|
+
for (const path6 of input.routeEntries) {
|
|
2986
|
+
if (!exportSet.has(path6)) {
|
|
2987
|
+
input.onIssue(`routeEntries path "${path6}" is missing from routePageExports`);
|
|
2965
2988
|
}
|
|
2966
2989
|
}
|
|
2967
2990
|
}
|
|
@@ -3906,6 +3929,50 @@ function analyzeClientSafeEntryImports(sourceText, fileName) {
|
|
|
3906
3929
|
return analyzeClientSafeEntryImportsWithTs(import_typescript3.default, sourceText, fileName);
|
|
3907
3930
|
}
|
|
3908
3931
|
|
|
3932
|
+
// ../../packages/lib/src/shared/ownership-record.ts
|
|
3933
|
+
var import_zod6 = require("zod");
|
|
3934
|
+
var OWNERSHIP_RECORD_PATH = "stardeck.ownership.json";
|
|
3935
|
+
var OWNERSHIP_RECORD_VERSION = 1;
|
|
3936
|
+
var moduleList = import_zod6.z.array(import_zod6.z.string().refine(isValidModuleName, "not a Module name"));
|
|
3937
|
+
var ownershipRecordSchema = import_zod6.z.object({
|
|
3938
|
+
version: import_zod6.z.literal(OWNERSHIP_RECORD_VERSION),
|
|
3939
|
+
/** False once the app stopped all Stardeck updates: then every file is the app's. */
|
|
3940
|
+
stardeckUpdates: import_zod6.z.boolean(),
|
|
3941
|
+
/** projects.ejected_modules: updates stopped for these Modules, so their folders are the app's. */
|
|
3942
|
+
updatesStoppedModules: moduleList,
|
|
3943
|
+
/** projects.ui_ejected_modules: Customize UI, so their screens are the app's. */
|
|
3944
|
+
customizedUiModules: moduleList,
|
|
3945
|
+
/** projects.external_modules: installed from another app, so the app owns the copy. */
|
|
3946
|
+
externalModules: moduleList,
|
|
3947
|
+
/** projects.removed_modules names: uninstalled on purpose. */
|
|
3948
|
+
uninstalledModules: moduleList
|
|
3949
|
+
});
|
|
3950
|
+
function parseOwnershipRecord(content) {
|
|
3951
|
+
let json;
|
|
3952
|
+
try {
|
|
3953
|
+
json = JSON.parse(content);
|
|
3954
|
+
} catch (error) {
|
|
3955
|
+
return { ok: false, error: `${OWNERSHIP_RECORD_PATH} is not valid JSON: ${String(error)}` };
|
|
3956
|
+
}
|
|
3957
|
+
const parsed = ownershipRecordSchema.safeParse(json);
|
|
3958
|
+
if (!parsed.success) {
|
|
3959
|
+
return {
|
|
3960
|
+
ok: false,
|
|
3961
|
+
error: `${OWNERSHIP_RECORD_PATH} is not a record Stardeck wrote: ${parsed.error.issues.map((issue) => `${issue.path.join(".") || "(root)"}: ${issue.message}`).join("; ")}`
|
|
3962
|
+
};
|
|
3963
|
+
}
|
|
3964
|
+
return { ok: true, record: parsed.data };
|
|
3965
|
+
}
|
|
3966
|
+
var BLUEPRINT_RELEASE_COMMITTER_EMAIL = "ci@stardeck.ai";
|
|
3967
|
+
var BLUEPRINT_RELEASE_SUBJECT_PREFIX = "publish: ";
|
|
3968
|
+
var STARDECK_OWNED_ALTERNATIVES = [
|
|
3969
|
+
"If the Module has a setting for this, change the setting.",
|
|
3970
|
+
"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).",
|
|
3971
|
+
"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.",
|
|
3972
|
+
"If the Module's logic is wrong for every app, report it to Stardeck so every app gets the fix.",
|
|
3973
|
+
"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."
|
|
3974
|
+
];
|
|
3975
|
+
|
|
3909
3976
|
// ../../packages/lib/src/shared/platform-owned-paths.ts
|
|
3910
3977
|
var APP_WEB_PREFIX = "apps/web/";
|
|
3911
3978
|
var BLUEPRINT_LINT_MARKER = `${APP_WEB_PREFIX}eslint-rules/module-boundaries.mjs`;
|
|
@@ -3919,20 +3986,217 @@ var MODULE_INIT_SERVER_GEN_PATH = `${APP_WEB_PREFIX}src/module-init.server.gen.t
|
|
|
3919
3986
|
var MODULE_SERVER_GEN_PATH = `${APP_WEB_PREFIX}src/module-server.gen.ts`;
|
|
3920
3987
|
var MODULES_GEN_PATH = `${APP_WEB_PREFIX}src/modules.gen.ts`;
|
|
3921
3988
|
var APP_EXTENSIONS_PATH = `${APP_WEB_PREFIX}src/lib/modules/app-extensions.ts`;
|
|
3989
|
+
var APP_OWNED_PATHS = [APP_EXTENSIONS_PATH];
|
|
3990
|
+
function platformOwnedSet(input) {
|
|
3991
|
+
const externalModules = input.externalModules ?? [];
|
|
3992
|
+
const removedModules = input.removedModules ?? [];
|
|
3993
|
+
const moduleNames = [
|
|
3994
|
+
...input.moduleNames,
|
|
3995
|
+
...input.ejectedModules,
|
|
3996
|
+
...input.uiEjectedModules,
|
|
3997
|
+
...externalModules,
|
|
3998
|
+
...removedModules
|
|
3999
|
+
];
|
|
4000
|
+
for (const moduleName of moduleNames) {
|
|
4001
|
+
if (!isValidModuleName(moduleName)) {
|
|
4002
|
+
throw new Error(`Invalid Module name: ${moduleName}`);
|
|
4003
|
+
}
|
|
4004
|
+
}
|
|
4005
|
+
const appOwned = /* @__PURE__ */ new Set([...input.ejectedModules, ...externalModules]);
|
|
4006
|
+
const activeModules = input.moduleNames.filter((name) => !appOwned.has(name));
|
|
4007
|
+
const uiEjectedModules = new Set(input.uiEjectedModules);
|
|
4008
|
+
return {
|
|
4009
|
+
prefixes: [
|
|
4010
|
+
...activeModules.map((name) => `${APP_WEB_PREFIX}src/modules/${name}/`),
|
|
4011
|
+
`${APP_WEB_PREFIX}src/lib/modules/`
|
|
4012
|
+
].sort(),
|
|
4013
|
+
// Build outputs, not source: compose regenerates them from Module source on
|
|
4014
|
+
// every dev/build/lint/typecheck/test run and git ignores them, so there is
|
|
4015
|
+
// nothing for the write refusal or the Blueprint updater to protect.
|
|
4016
|
+
files: [],
|
|
4017
|
+
// Detach dominates: a detached Module has no prefix, so it needs no carve-out.
|
|
4018
|
+
carveOuts: activeModules.filter((name) => uiEjectedModules.has(name)).flatMap(
|
|
4019
|
+
(name) => MODULE_UI_SUBPATHS.map((subpath) => `${APP_WEB_PREFIX}src/modules/${name}/${subpath}`)
|
|
4020
|
+
).sort(),
|
|
4021
|
+
externalModules: [...externalModules].sort(),
|
|
4022
|
+
removedModules: [...removedModules].sort()
|
|
4023
|
+
};
|
|
4024
|
+
}
|
|
4025
|
+
function appOwnedExcludePathspecs(set) {
|
|
4026
|
+
return [
|
|
4027
|
+
...APP_OWNED_PATHS.map((file) => `:(exclude)${file}`),
|
|
4028
|
+
...set.carveOuts.map((carveOut) => `:(glob,exclude)${carveOut}**`)
|
|
4029
|
+
];
|
|
4030
|
+
}
|
|
4031
|
+
function platformOwnedDiffPathspecs(set) {
|
|
4032
|
+
return [...set.prefixes, ...set.files, ...appOwnedExcludePathspecs(set)];
|
|
4033
|
+
}
|
|
4034
|
+
|
|
4035
|
+
// ../../packages/lib/src/server/module-rail/ownership-guard.ts
|
|
4036
|
+
var STRICT_RECORD = {
|
|
4037
|
+
version: 1,
|
|
4038
|
+
stardeckUpdates: true,
|
|
4039
|
+
updatesStoppedModules: [],
|
|
4040
|
+
customizedUiModules: [],
|
|
4041
|
+
externalModules: [],
|
|
4042
|
+
uninstalledModules: []
|
|
4043
|
+
};
|
|
4044
|
+
var MANIFEST_PATH = new RegExp(
|
|
4045
|
+
`^${MODULES_SANDBOX_DIR.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}/([^/]+)/${MODULE_MANIFEST_FILENAME.replace(".", "\\.")}$`
|
|
4046
|
+
);
|
|
4047
|
+
function realpath(dir) {
|
|
4048
|
+
try {
|
|
4049
|
+
return import_node_fs.default.realpathSync(dir);
|
|
4050
|
+
} catch {
|
|
4051
|
+
return import_node_path.default.resolve(dir);
|
|
4052
|
+
}
|
|
4053
|
+
}
|
|
4054
|
+
function zList(stdout) {
|
|
4055
|
+
return stdout.split("\0").filter(Boolean);
|
|
4056
|
+
}
|
|
4057
|
+
async function findRelease(git) {
|
|
4058
|
+
const found = await git([
|
|
4059
|
+
"rev-list",
|
|
4060
|
+
"-n1",
|
|
4061
|
+
"--topo-order",
|
|
4062
|
+
`--committer=${BLUEPRINT_RELEASE_COMMITTER_EMAIL.replace(/\./g, "\\.")}`,
|
|
4063
|
+
`--grep=^${BLUEPRINT_RELEASE_SUBJECT_PREFIX}`,
|
|
4064
|
+
"HEAD"
|
|
4065
|
+
]);
|
|
4066
|
+
const sha = found.status === 0 ? found.stdout.trim() : "";
|
|
4067
|
+
return sha || null;
|
|
4068
|
+
}
|
|
4069
|
+
async function noReleaseReason(git) {
|
|
4070
|
+
const shallow = await git(["rev-parse", "--is-shallow-repository"]);
|
|
4071
|
+
if (shallow.status === 0 && shallow.stdout.trim() === "true") {
|
|
4072
|
+
return {
|
|
4073
|
+
status: "skipped",
|
|
4074
|
+
warning: true,
|
|
4075
|
+
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."
|
|
4076
|
+
};
|
|
4077
|
+
}
|
|
4078
|
+
return {
|
|
4079
|
+
status: "skipped",
|
|
4080
|
+
warning: false,
|
|
4081
|
+
reason: "no Stardeck release in this repo's history, so it holds no Stardeck-owned files"
|
|
4082
|
+
};
|
|
4083
|
+
}
|
|
4084
|
+
function readRecord(repoRoot) {
|
|
4085
|
+
let content;
|
|
4086
|
+
try {
|
|
4087
|
+
content = import_node_fs.default.readFileSync(import_node_path.default.join(repoRoot, OWNERSHIP_RECORD_PATH), "utf8");
|
|
4088
|
+
} catch (error) {
|
|
4089
|
+
if (error.code === "ENOENT") {
|
|
4090
|
+
return { ok: true, record: STRICT_RECORD, found: false };
|
|
4091
|
+
}
|
|
4092
|
+
throw error;
|
|
4093
|
+
}
|
|
4094
|
+
const parsed = parseOwnershipRecord(content);
|
|
4095
|
+
return parsed.ok ? { ok: true, record: parsed.record, found: true } : parsed;
|
|
4096
|
+
}
|
|
4097
|
+
async function releaseModules(git, release, record) {
|
|
4098
|
+
const listed = await git([
|
|
4099
|
+
"ls-tree",
|
|
4100
|
+
"-r",
|
|
4101
|
+
"-z",
|
|
4102
|
+
"--name-only",
|
|
4103
|
+
release,
|
|
4104
|
+
"--",
|
|
4105
|
+
MODULES_SANDBOX_DIR
|
|
4106
|
+
]);
|
|
4107
|
+
if (listed.status !== 0) throw new Error(`git ls-tree ${release} failed`);
|
|
4108
|
+
const uninstalled = new Set(record.uninstalledModules);
|
|
4109
|
+
return zList(listed.stdout).map((file) => MANIFEST_PATH.exec(file)?.[1]).filter((name) => !!name && !uninstalled.has(name));
|
|
4110
|
+
}
|
|
4111
|
+
async function checkStardeckOwnedFiles(input) {
|
|
4112
|
+
const { git, repoRoot } = input;
|
|
4113
|
+
const top = await git(["rev-parse", "--show-toplevel"]);
|
|
4114
|
+
if (top.status !== 0) {
|
|
4115
|
+
return { status: "skipped", warning: false, reason: "not a git checkout" };
|
|
4116
|
+
}
|
|
4117
|
+
if (realpath(top.stdout.trim()) !== realpath(repoRoot)) {
|
|
4118
|
+
return {
|
|
4119
|
+
status: "skipped",
|
|
4120
|
+
warning: false,
|
|
4121
|
+
reason: `${repoRoot} is not the root of its git repo, so it is not an app's own repo`
|
|
4122
|
+
};
|
|
4123
|
+
}
|
|
4124
|
+
const release = await findRelease(git);
|
|
4125
|
+
if (!release) return noReleaseReason(git);
|
|
4126
|
+
const read = readRecord(repoRoot);
|
|
4127
|
+
if (!read.ok) return { status: "invalid-record", error: read.error };
|
|
4128
|
+
const { record } = read;
|
|
4129
|
+
if (!record.stardeckUpdates) {
|
|
4130
|
+
return {
|
|
4131
|
+
status: "skipped",
|
|
4132
|
+
warning: false,
|
|
4133
|
+
reason: "this app stopped all Stardeck updates, so every file is the app's"
|
|
4134
|
+
};
|
|
4135
|
+
}
|
|
4136
|
+
const moduleNames = await releaseModules(git, release, record);
|
|
4137
|
+
const set = platformOwnedSet({
|
|
4138
|
+
moduleNames,
|
|
4139
|
+
ejectedModules: record.updatesStoppedModules,
|
|
4140
|
+
uiEjectedModules: record.customizedUiModules,
|
|
4141
|
+
externalModules: record.externalModules,
|
|
4142
|
+
removedModules: record.uninstalledModules
|
|
4143
|
+
});
|
|
4144
|
+
const pathspecs = [
|
|
4145
|
+
...platformOwnedDiffPathspecs(set),
|
|
4146
|
+
...record.uninstalledModules.map(
|
|
4147
|
+
(name) => `:(glob,exclude)${MODULES_SANDBOX_DIR}/*/enhancements/${name}/**`
|
|
4148
|
+
)
|
|
4149
|
+
];
|
|
4150
|
+
const [changed, added] = await Promise.all([
|
|
4151
|
+
git([
|
|
4152
|
+
"diff",
|
|
4153
|
+
"--name-only",
|
|
4154
|
+
"-z",
|
|
4155
|
+
"--no-renames",
|
|
4156
|
+
"--no-ext-diff",
|
|
4157
|
+
release,
|
|
4158
|
+
"--",
|
|
4159
|
+
...pathspecs
|
|
4160
|
+
]),
|
|
4161
|
+
git(["ls-files", "-z", "--others", "--exclude-standard", "--", ...pathspecs])
|
|
4162
|
+
]);
|
|
4163
|
+
if (changed.status !== 0 || added.status !== 0) {
|
|
4164
|
+
throw new Error(`git could not compare the checkout with Stardeck's version ${release}`);
|
|
4165
|
+
}
|
|
4166
|
+
const paths = [.../* @__PURE__ */ new Set([...zList(changed.stdout), ...zList(added.stdout)])].sort();
|
|
4167
|
+
console.log(
|
|
4168
|
+
`[OwnershipGuard] release=${release.slice(0, 12)} record=${read.found ? "found" : "missing"} modules=${moduleNames.length} carveOuts=${set.carveOuts.length} changed=${paths.length}`
|
|
4169
|
+
);
|
|
4170
|
+
return paths.length > 0 ? { status: "changed", release, paths } : { status: "clean", release };
|
|
4171
|
+
}
|
|
4172
|
+
function formatOwnershipGuardFailure(result) {
|
|
4173
|
+
if (result.status === "invalid-record") {
|
|
4174
|
+
return `${result.error}
|
|
4175
|
+
Restore it with \`git checkout origin/HEAD -- ${OWNERSHIP_RECORD_PATH}\`. Stardeck writes this file; ownership changes in the dashboard's Modules tab.`;
|
|
4176
|
+
}
|
|
4177
|
+
const count = `${result.paths.length} Stardeck-owned ${result.paths.length === 1 ? "file" : "files"}`;
|
|
4178
|
+
return [
|
|
4179
|
+
`${count} changed from Stardeck's version (${result.release.slice(0, 12)}). The next Stardeck update replaces them, so these changes would be lost:`,
|
|
4180
|
+
...result.paths.map((file) => ` - ${file}`),
|
|
4181
|
+
"",
|
|
4182
|
+
`Undo them (\`git checkout ${result.release.slice(0, 12)} -- <path>\`, or delete a new file) and do one of these instead:`,
|
|
4183
|
+
...STARDECK_OWNED_ALTERNATIVES.map((line) => ` - ${line.replace(/\*\*/g, "")}`)
|
|
4184
|
+
].join("\n");
|
|
4185
|
+
}
|
|
3922
4186
|
|
|
3923
4187
|
// src/cli.ts
|
|
3924
4188
|
var import_typescript7 = __toESM(require("typescript"));
|
|
3925
|
-
var
|
|
4189
|
+
var import_node_path5 = __toESM(require("path"));
|
|
3926
4190
|
|
|
3927
4191
|
// src/index.ts
|
|
3928
4192
|
var import_node_child_process2 = require("child_process");
|
|
3929
|
-
var
|
|
3930
|
-
var
|
|
4193
|
+
var import_node_fs4 = __toESM(require("fs"));
|
|
4194
|
+
var import_node_path4 = __toESM(require("path"));
|
|
3931
4195
|
|
|
3932
4196
|
// ../../packages/lib/src/server/module-rail/local-fs-sandbox.ts
|
|
3933
4197
|
var import_node_child_process = require("child_process");
|
|
3934
|
-
var
|
|
3935
|
-
var
|
|
4198
|
+
var import_node_fs2 = __toESM(require("fs"));
|
|
4199
|
+
var import_node_path2 = __toESM(require("path"));
|
|
3936
4200
|
function localFsSandbox(root) {
|
|
3937
4201
|
return {
|
|
3938
4202
|
async exec(command, options) {
|
|
@@ -3956,13 +4220,13 @@ ${result.error}`);
|
|
|
3956
4220
|
},
|
|
3957
4221
|
async fileExists(relative) {
|
|
3958
4222
|
try {
|
|
3959
|
-
return
|
|
4223
|
+
return import_node_fs2.default.statSync(import_node_path2.default.join(root, relative)).isFile();
|
|
3960
4224
|
} catch {
|
|
3961
4225
|
return false;
|
|
3962
4226
|
}
|
|
3963
4227
|
},
|
|
3964
4228
|
async readFile(relative) {
|
|
3965
|
-
return
|
|
4229
|
+
return import_node_fs2.default.readFileSync(import_node_path2.default.join(root, relative), "utf8");
|
|
3966
4230
|
},
|
|
3967
4231
|
async fetchRemoteRef() {
|
|
3968
4232
|
throw new Error("[ModuleRail] local filesystem sandbox cannot reach the network");
|
|
@@ -3995,28 +4259,28 @@ function shellQuote(value) {
|
|
|
3995
4259
|
}
|
|
3996
4260
|
|
|
3997
4261
|
// ../../packages/lib/src/shared/compose-inputs.ts
|
|
3998
|
-
var
|
|
4262
|
+
var import_zod7 = require("zod");
|
|
3999
4263
|
var COMPOSE_INPUTS_PATH = "apps/web/stardeck.compose.json";
|
|
4000
|
-
var composeInputsSchema =
|
|
4001
|
-
connectedStores:
|
|
4002
|
-
|
|
4003
|
-
storeId:
|
|
4004
|
-
slug:
|
|
4264
|
+
var composeInputsSchema = import_zod7.z.strictObject({
|
|
4265
|
+
connectedStores: import_zod7.z.array(
|
|
4266
|
+
import_zod7.z.strictObject({
|
|
4267
|
+
storeId: import_zod7.z.string().min(1),
|
|
4268
|
+
slug: import_zod7.z.string().min(1),
|
|
4005
4269
|
// A row with an empty binding key must not fail a deploy: the rail
|
|
4006
4270
|
// treats falsy as "no binding key", so normalise rather than reject.
|
|
4007
|
-
bindingKey:
|
|
4008
|
-
accessLevel:
|
|
4271
|
+
bindingKey: import_zod7.z.string().nullish().transform((value) => value || null),
|
|
4272
|
+
accessLevel: import_zod7.z.enum(["read", "write", "admin"]),
|
|
4009
4273
|
// Defaulted so a file written before the platform emitted store types
|
|
4010
4274
|
// (database rows only) still parses. The platform starts writing
|
|
4011
4275
|
// storage rows, with `type` set, once forks run a compose that reads it.
|
|
4012
|
-
type:
|
|
4276
|
+
type: import_zod7.z.enum(["database", "storage"]).default("database")
|
|
4013
4277
|
})
|
|
4014
4278
|
).default([]),
|
|
4015
|
-
installs:
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
installedVersion:
|
|
4019
|
-
datastoreBindings:
|
|
4279
|
+
installs: import_zod7.z.record(
|
|
4280
|
+
import_zod7.z.string(),
|
|
4281
|
+
import_zod7.z.strictObject({
|
|
4282
|
+
installedVersion: import_zod7.z.string().min(1),
|
|
4283
|
+
datastoreBindings: import_zod7.z.record(import_zod7.z.string(), import_zod7.z.string()).nullish()
|
|
4020
4284
|
})
|
|
4021
4285
|
).default({})
|
|
4022
4286
|
});
|
|
@@ -4176,13 +4440,13 @@ function assertClientSafeConventionalEntryImports(source, pathLabel, kind) {
|
|
|
4176
4440
|
function createMessageTree() {
|
|
4177
4441
|
return /* @__PURE__ */ Object.create(null);
|
|
4178
4442
|
}
|
|
4179
|
-
function assertMessageTree(value,
|
|
4443
|
+
function assertMessageTree(value, path6) {
|
|
4180
4444
|
if (typeof value === "string") return;
|
|
4181
4445
|
if (value === null || Array.isArray(value) || typeof value !== "object") {
|
|
4182
|
-
throw new Error(`Invalid message at ${
|
|
4446
|
+
throw new Error(`Invalid message at ${path6}: expected string or nested object`);
|
|
4183
4447
|
}
|
|
4184
4448
|
for (const [key, child] of Object.entries(value)) {
|
|
4185
|
-
assertMessageTree(child, `${
|
|
4449
|
+
assertMessageTree(child, `${path6}.${key}`);
|
|
4186
4450
|
}
|
|
4187
4451
|
}
|
|
4188
4452
|
function cloneTree(value) {
|
|
@@ -4192,17 +4456,17 @@ function cloneTree(value) {
|
|
|
4192
4456
|
}
|
|
4193
4457
|
return clone;
|
|
4194
4458
|
}
|
|
4195
|
-
function collectLeafPaths(value,
|
|
4459
|
+
function collectLeafPaths(value, path6, out) {
|
|
4196
4460
|
if (typeof value === "string") {
|
|
4197
|
-
out.push(
|
|
4461
|
+
out.push(path6);
|
|
4198
4462
|
return;
|
|
4199
4463
|
}
|
|
4200
|
-
assertMessageTree(value,
|
|
4201
|
-
for (const [key, child] of Object.entries(value)) collectLeafPaths(child, `${
|
|
4464
|
+
assertMessageTree(value, path6);
|
|
4465
|
+
for (const [key, child] of Object.entries(value)) collectLeafPaths(child, `${path6}.${key}`, out);
|
|
4202
4466
|
}
|
|
4203
|
-
function overrideInto(target, source, owner,
|
|
4467
|
+
function overrideInto(target, source, owner, path6, missing) {
|
|
4204
4468
|
for (const [key, value] of Object.entries(source)) {
|
|
4205
|
-
const nextPath =
|
|
4469
|
+
const nextPath = path6 ? `${path6}.${key}` : key;
|
|
4206
4470
|
if (!Object.prototype.hasOwnProperty.call(target, key)) {
|
|
4207
4471
|
collectLeafPaths(value, nextPath, missing);
|
|
4208
4472
|
continue;
|
|
@@ -4229,9 +4493,9 @@ function overrideInto(target, source, owner, path5, missing) {
|
|
|
4229
4493
|
function composeLocaleMessagesForPlan(layers) {
|
|
4230
4494
|
const result = createMessageTree();
|
|
4231
4495
|
const leafOwners = /* @__PURE__ */ new Map();
|
|
4232
|
-
function mergeInto(target, source, owner,
|
|
4496
|
+
function mergeInto(target, source, owner, path6) {
|
|
4233
4497
|
for (const [key, value] of Object.entries(source)) {
|
|
4234
|
-
const nextPath =
|
|
4498
|
+
const nextPath = path6 ? `${path6}.${key}` : key;
|
|
4235
4499
|
const hasExisting = Object.prototype.hasOwnProperty.call(target, key);
|
|
4236
4500
|
const existing = hasExisting ? target[key] : void 0;
|
|
4237
4501
|
if (typeof value === "string") {
|
|
@@ -4792,8 +5056,8 @@ var METHOD_ORDER = {
|
|
|
4792
5056
|
function compareNames2(a, b) {
|
|
4793
5057
|
return a < b ? -1 : a > b ? 1 : 0;
|
|
4794
5058
|
}
|
|
4795
|
-
function endpointPairKey(method,
|
|
4796
|
-
return `${method} ${
|
|
5059
|
+
function endpointPairKey(method, path6) {
|
|
5060
|
+
return `${method} ${path6}`;
|
|
4797
5061
|
}
|
|
4798
5062
|
function apiPathToRouteFile(apiPath) {
|
|
4799
5063
|
if (!apiPath.startsWith("/api/")) {
|
|
@@ -4807,11 +5071,11 @@ function routeEntryToAppPageFile(nextRelativePath) {
|
|
|
4807
5071
|
}
|
|
4808
5072
|
var APP_ROUTE_FILE_PREFIX = `${APP_PACKAGE_DIR}/src/app/`;
|
|
4809
5073
|
var API_ROUTE_FILE_SUFFIXES = ["/route.ts", "/route.tsx", "/route.jsx", "/route.js"];
|
|
4810
|
-
function appRouteFileToNextRelativePath(
|
|
4811
|
-
const pagePath = appPageFileToNextRelativePath(
|
|
5074
|
+
function appRouteFileToNextRelativePath(path6) {
|
|
5075
|
+
const pagePath = appPageFileToNextRelativePath(path6, APP_PACKAGE_DIR);
|
|
4812
5076
|
if (pagePath != null) return pagePath;
|
|
4813
|
-
if (!
|
|
4814
|
-
const relative =
|
|
5077
|
+
if (!path6.startsWith(APP_ROUTE_FILE_PREFIX)) return null;
|
|
5078
|
+
const relative = path6.slice(APP_ROUTE_FILE_PREFIX.length);
|
|
4815
5079
|
if (!relative.startsWith("api/")) return null;
|
|
4816
5080
|
const suffix = API_ROUTE_FILE_SUFFIXES.find((candidate) => relative.endsWith(candidate));
|
|
4817
5081
|
if (!suffix) return null;
|
|
@@ -4906,9 +5170,9 @@ function legacyHandlerPropertyMatchesPath(property, method, physicalPath, bindin
|
|
|
4906
5170
|
const expectedTokens = knownAliases[`${binding} ${method} ${physicalPath}`] ?? pathTokens;
|
|
4907
5171
|
return handlerTokens.length === expectedTokens.length && handlerTokens.every((token, index) => token === expectedTokens[index]);
|
|
4908
5172
|
}
|
|
4909
|
-
function parseLegacyApiAdapter(
|
|
4910
|
-
const physicalPath = appRouteFileToNextRelativePath(
|
|
4911
|
-
if (expectedMarkerKindForPath(
|
|
5173
|
+
function parseLegacyApiAdapter(path6, source) {
|
|
5174
|
+
const physicalPath = appRouteFileToNextRelativePath(path6);
|
|
5175
|
+
if (expectedMarkerKindForPath(path6) !== "endpoint" || physicalPath == null) return null;
|
|
4912
5176
|
const firstLine = source.split(/\r?\n/, 1)[0]?.trim() ?? "";
|
|
4913
5177
|
const match = firstLine.match(
|
|
4914
5178
|
new RegExp(
|
|
@@ -4918,7 +5182,7 @@ function parseLegacyApiAdapter(path5, source) {
|
|
|
4918
5182
|
if (!match || match[2] !== physicalPath) return null;
|
|
4919
5183
|
const owner = match[1];
|
|
4920
5184
|
if (!isValidModuleName(owner)) return null;
|
|
4921
|
-
const file = import_typescript4.default.createSourceFile(
|
|
5185
|
+
const file = import_typescript4.default.createSourceFile(path6, source, import_typescript4.default.ScriptTarget.Latest, false, import_typescript4.default.ScriptKind.TS);
|
|
4922
5186
|
if (file.parseDiagnostics.length > 0) {
|
|
4923
5187
|
return null;
|
|
4924
5188
|
}
|
|
@@ -4960,9 +5224,9 @@ function parseLegacyApiAdapter(path5, source) {
|
|
|
4960
5224
|
}
|
|
4961
5225
|
return handlerBinding != null && methods.size > 0 ? { owner, methods: [...methods] } : null;
|
|
4962
5226
|
}
|
|
4963
|
-
function parseLegacyRouteAdapter(
|
|
4964
|
-
const physicalPath = appRouteFileToNextRelativePath(
|
|
4965
|
-
if (expectedMarkerKindForPath(
|
|
5227
|
+
function parseLegacyRouteAdapter(path6, source) {
|
|
5228
|
+
const physicalPath = appRouteFileToNextRelativePath(path6);
|
|
5229
|
+
if (expectedMarkerKindForPath(path6) !== "route" || physicalPath == null) return null;
|
|
4966
5230
|
const normalized = source.replace(/\r\n/g, "\n");
|
|
4967
5231
|
const match = normalized.match(
|
|
4968
5232
|
new RegExp(
|
|
@@ -4974,30 +5238,30 @@ function parseLegacyRouteAdapter(path5, source) {
|
|
|
4974
5238
|
const nextRelativePath = match[2];
|
|
4975
5239
|
return isValidModuleName(owner) && nextRelativePath === physicalPath ? { owner, nextRelativePath } : null;
|
|
4976
5240
|
}
|
|
4977
|
-
function legacyRouteAdapterMatchesStub(
|
|
4978
|
-
const legacy = parseLegacyRouteAdapter(
|
|
5241
|
+
function legacyRouteAdapterMatchesStub(path6, source, stub) {
|
|
5242
|
+
const legacy = parseLegacyRouteAdapter(path6, source);
|
|
4979
5243
|
return legacy != null && legacy.owner === stub.owner;
|
|
4980
5244
|
}
|
|
4981
5245
|
var API_ROUTE_DIR_PREFIX = `${APP_PACKAGE_DIR}/src/app/api/`;
|
|
4982
5246
|
var APP_DIR_PREFIX = `${APP_PACKAGE_DIR}/src/app/`;
|
|
4983
5247
|
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 (
|
|
5248
|
+
function expectedMarkerKindForPath(path6) {
|
|
5249
|
+
if (path6 === MODULES_GEN_PATH) return "registry";
|
|
5250
|
+
if (path6 === MODULE_CONTRIBUTIONS_GEN_PATH) return "contributions";
|
|
5251
|
+
if (path6 === MODULE_DATASTORES_GEN_PATH) return "datastores";
|
|
5252
|
+
if (path6 === MODULE_I18N_GEN_PATH) return "i18n";
|
|
5253
|
+
if (path6 === MODULE_INIT_SERVER_GEN_PATH) return "init";
|
|
5254
|
+
if (path6 === MODULE_SERVER_GEN_PATH) return "server";
|
|
5255
|
+
if (path6.startsWith(API_ROUTE_DIR_PREFIX) && path6.endsWith("/route.ts")) return "endpoint";
|
|
5256
|
+
if (path6.startsWith(APP_DIR_PREFIX) && !path6.startsWith(API_ROUTE_DIR_PREFIX) && PAGE_FILE_SUFFIXES.some((suffix) => path6.endsWith(suffix))) {
|
|
4993
5257
|
return "route";
|
|
4994
5258
|
}
|
|
4995
5259
|
return null;
|
|
4996
5260
|
}
|
|
4997
|
-
function markerCongruentWithPath(
|
|
4998
|
-
if (expectedMarkerKindForPath(
|
|
5261
|
+
function markerCongruentWithPath(path6, marker) {
|
|
5262
|
+
if (expectedMarkerKindForPath(path6) !== marker.kind) return false;
|
|
4999
5263
|
if (marker.kind === "route") {
|
|
5000
|
-
const derived = appPageFileToNextRelativePath(
|
|
5264
|
+
const derived = appPageFileToNextRelativePath(path6, APP_PACKAGE_DIR);
|
|
5001
5265
|
return derived != null && derived === marker.nextRelativePath;
|
|
5002
5266
|
}
|
|
5003
5267
|
return true;
|
|
@@ -5273,8 +5537,8 @@ function buildGeneratedModuleRegistry(input) {
|
|
|
5273
5537
|
function activeEnhancementPeers(registryModule) {
|
|
5274
5538
|
return new Set(registryModule.enhancements.filter((e) => e.active).map((e) => e.peer));
|
|
5275
5539
|
}
|
|
5276
|
-
function endpointBucketKey(
|
|
5277
|
-
return `${
|
|
5540
|
+
function endpointBucketKey(path6, owner, scope) {
|
|
5541
|
+
return `${path6}\0${owner}\0${scopeLabel(scope)}`;
|
|
5278
5542
|
}
|
|
5279
5543
|
function planRouteStubs(input) {
|
|
5280
5544
|
const endpointBuckets = /* @__PURE__ */ new Map();
|
|
@@ -5398,7 +5662,7 @@ function planRouteStubs(input) {
|
|
|
5398
5662
|
for (const entry of entries) {
|
|
5399
5663
|
const nextRelativePath = entry.nextRelativePath;
|
|
5400
5664
|
const destination = normalizeAppRouterDestinationPattern(nextRelativePath);
|
|
5401
|
-
const
|
|
5665
|
+
const path6 = routeEntryToAppPageFile(nextRelativePath);
|
|
5402
5666
|
const existing = routeDestinationOwners.get(destination);
|
|
5403
5667
|
if (existing) {
|
|
5404
5668
|
throw new Error(
|
|
@@ -5408,13 +5672,13 @@ function planRouteStubs(input) {
|
|
|
5408
5672
|
routeDestinationOwners.set(destination, {
|
|
5409
5673
|
owner: regMod.name,
|
|
5410
5674
|
nextRelativePath,
|
|
5411
|
-
path:
|
|
5675
|
+
path: path6
|
|
5412
5676
|
});
|
|
5413
5677
|
const importModule = routeEntryImport(regMod.name, nextRelativePath);
|
|
5414
5678
|
const analyzed = analyzeRouteEntryPath(nextRelativePath);
|
|
5415
5679
|
const mount = mounts.find((claim) => routeEntryCoveredByMountClaim(analyzed, claim));
|
|
5416
5680
|
routeStubs.push({
|
|
5417
|
-
path:
|
|
5681
|
+
path: path6,
|
|
5418
5682
|
owner: regMod.name,
|
|
5419
5683
|
nextRelativePath,
|
|
5420
5684
|
importModule,
|
|
@@ -5475,16 +5739,16 @@ function planCompositionArtifacts(input) {
|
|
|
5475
5739
|
...stubs.routeStubs.map((stub) => stub.path)
|
|
5476
5740
|
]);
|
|
5477
5741
|
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(
|
|
5742
|
+
const isProvenStaleDeletionCandidate = (path6, content) => {
|
|
5743
|
+
if (path6 === MODULES_GEN_PATH) return false;
|
|
5744
|
+
if (COMPOSITION_RUNTIME_GEN_PATHS.includes(path6)) return false;
|
|
5745
|
+
if (desiredPaths.has(path6)) return false;
|
|
5746
|
+
if (!existingGeneratedPathSet.has(path6)) return false;
|
|
5747
|
+
const legacy = parseLegacyApiAdapter(path6, content);
|
|
5484
5748
|
if (legacy) return true;
|
|
5485
|
-
if (parseLegacyRouteAdapter(
|
|
5749
|
+
if (parseLegacyRouteAdapter(path6, content)) return true;
|
|
5486
5750
|
const marker = parseCompositionArtifactMarker(content);
|
|
5487
|
-
return marker != null && markerCongruentWithPath(
|
|
5751
|
+
return marker != null && markerCongruentWithPath(path6, marker);
|
|
5488
5752
|
};
|
|
5489
5753
|
const plannedByDestination = /* @__PURE__ */ new Map();
|
|
5490
5754
|
for (const stub of stubs.routeStubs) {
|
|
@@ -5638,13 +5902,13 @@ function planCompositionArtifacts(input) {
|
|
|
5638
5902
|
for (const stub of stubs.routeStubs) desired.set(stub.path, stub.content);
|
|
5639
5903
|
const endpointStubByPath = new Map(stubs.endpointStubs.map((stub) => [stub.path, stub]));
|
|
5640
5904
|
const routeStubByPath = new Map(stubs.routeStubs.map((stub) => [stub.path, stub]));
|
|
5641
|
-
for (const [
|
|
5642
|
-
const existing = input.existingFiles[
|
|
5905
|
+
for (const [path6] of desired) {
|
|
5906
|
+
const existing = input.existingFiles[path6];
|
|
5643
5907
|
if (existing == null) continue;
|
|
5644
5908
|
let marker = parseCompositionArtifactMarker(existing);
|
|
5645
|
-
const plannedEndpoint = endpointStubByPath.get(
|
|
5646
|
-
const plannedRoute = routeStubByPath.get(
|
|
5647
|
-
const legacy = parseLegacyApiAdapter(
|
|
5909
|
+
const plannedEndpoint = endpointStubByPath.get(path6);
|
|
5910
|
+
const plannedRoute = routeStubByPath.get(path6);
|
|
5911
|
+
const legacy = parseLegacyApiAdapter(path6, existing);
|
|
5648
5912
|
if (legacy && plannedEndpoint) {
|
|
5649
5913
|
marker = {
|
|
5650
5914
|
kind: "endpoint",
|
|
@@ -5652,64 +5916,64 @@ function planCompositionArtifacts(input) {
|
|
|
5652
5916
|
scope: plannedEndpoint.scope
|
|
5653
5917
|
};
|
|
5654
5918
|
}
|
|
5655
|
-
if (!marker && plannedRoute && legacyRouteAdapterMatchesStub(
|
|
5919
|
+
if (!marker && plannedRoute && legacyRouteAdapterMatchesStub(path6, existing, plannedRoute)) {
|
|
5656
5920
|
marker = {
|
|
5657
5921
|
kind: "route",
|
|
5658
5922
|
owner: plannedRoute.owner,
|
|
5659
5923
|
nextRelativePath: plannedRoute.nextRelativePath
|
|
5660
5924
|
};
|
|
5661
5925
|
}
|
|
5662
|
-
const isGenRuntimePath = COMPOSITION_RUNTIME_GEN_PATHS.includes(
|
|
5926
|
+
const isGenRuntimePath = COMPOSITION_RUNTIME_GEN_PATHS.includes(path6);
|
|
5663
5927
|
if (!marker) {
|
|
5664
5928
|
return {
|
|
5665
5929
|
ok: false,
|
|
5666
|
-
error:
|
|
5667
|
-
path:
|
|
5930
|
+
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}`,
|
|
5931
|
+
path: path6
|
|
5668
5932
|
};
|
|
5669
5933
|
}
|
|
5670
|
-
const expectedKind = expectedMarkerKindForPath(
|
|
5934
|
+
const expectedKind = expectedMarkerKindForPath(path6);
|
|
5671
5935
|
if (marker.kind !== expectedKind) {
|
|
5672
5936
|
return {
|
|
5673
5937
|
ok: false,
|
|
5674
|
-
error:
|
|
5675
|
-
path:
|
|
5938
|
+
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)`,
|
|
5939
|
+
path: path6
|
|
5676
5940
|
};
|
|
5677
5941
|
}
|
|
5678
5942
|
if (marker.kind === "endpoint") {
|
|
5679
|
-
const stub = endpointStubByPath.get(
|
|
5943
|
+
const stub = endpointStubByPath.get(path6);
|
|
5680
5944
|
if (!endpointMarkerOwnsStub(marker, stub)) {
|
|
5681
5945
|
return {
|
|
5682
5946
|
ok: false,
|
|
5683
|
-
error: `[ModuleRail] composition artifacts: refusing to overwrite incongruent marker ownership at ${
|
|
5684
|
-
path:
|
|
5947
|
+
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) : "?"})`,
|
|
5948
|
+
path: path6,
|
|
5685
5949
|
owner: stub?.owner
|
|
5686
5950
|
};
|
|
5687
5951
|
}
|
|
5688
5952
|
} else if (marker.kind === "route") {
|
|
5689
|
-
const stub = routeStubByPath.get(
|
|
5953
|
+
const stub = routeStubByPath.get(path6);
|
|
5690
5954
|
const ownerMatches = stub != null && marker.owner === stub.owner;
|
|
5691
5955
|
const pathMatches = stub != null && marker.nextRelativePath === stub.nextRelativePath;
|
|
5692
5956
|
if (!ownerMatches || !pathMatches) {
|
|
5693
5957
|
return {
|
|
5694
5958
|
ok: false,
|
|
5695
|
-
error: `[ModuleRail] composition artifacts: refusing to overwrite incongruent marker ownership at ${
|
|
5696
|
-
path:
|
|
5959
|
+
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 ?? "?"})`,
|
|
5960
|
+
path: path6,
|
|
5697
5961
|
owner: stub?.owner
|
|
5698
5962
|
};
|
|
5699
5963
|
}
|
|
5700
5964
|
}
|
|
5701
5965
|
}
|
|
5702
5966
|
const deletes = [];
|
|
5703
|
-
for (const
|
|
5704
|
-
const existing = input.existingFiles[
|
|
5967
|
+
for (const path6 of input.existingGeneratedPaths) {
|
|
5968
|
+
const existing = input.existingFiles[path6];
|
|
5705
5969
|
if (existing == null) continue;
|
|
5706
|
-
if (!isProvenStaleDeletionCandidate(
|
|
5970
|
+
if (!isProvenStaleDeletionCandidate(path6, existing)) {
|
|
5707
5971
|
continue;
|
|
5708
5972
|
}
|
|
5709
|
-
deletes.push(
|
|
5973
|
+
deletes.push(path6);
|
|
5710
5974
|
}
|
|
5711
5975
|
deletes.sort(compareNames2);
|
|
5712
|
-
const writes = [...desired.entries()].filter(([
|
|
5976
|
+
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
5977
|
return {
|
|
5714
5978
|
ok: true,
|
|
5715
5979
|
plan: {
|
|
@@ -5980,14 +6244,14 @@ function rejectPseudoModuleEntries(entries, parentDir, label) {
|
|
|
5980
6244
|
}
|
|
5981
6245
|
|
|
5982
6246
|
// ../../packages/lib/src/shared/module-composition.ts
|
|
5983
|
-
var
|
|
5984
|
-
var creationSourceBlankSchema =
|
|
5985
|
-
type:
|
|
6247
|
+
var import_zod8 = require("zod");
|
|
6248
|
+
var creationSourceBlankSchema = import_zod8.z.object({
|
|
6249
|
+
type: import_zod8.z.literal("blank")
|
|
5986
6250
|
});
|
|
5987
|
-
var creationSourceBlueprintSchema =
|
|
5988
|
-
type:
|
|
6251
|
+
var creationSourceBlueprintSchema = import_zod8.z.object({
|
|
6252
|
+
type: import_zod8.z.literal("blueprint"),
|
|
5989
6253
|
/** Internal template/fork project id — keep template naming in identifiers. */
|
|
5990
|
-
blueprintProjectId:
|
|
6254
|
+
blueprintProjectId: import_zod8.z.string().uuid(),
|
|
5991
6255
|
/**
|
|
5992
6256
|
* `null`/absent means every feature Module: the platform stores that as the
|
|
5993
6257
|
* explicit list of feature Modules in the published Blueprint version the fork
|
|
@@ -5995,192 +6259,197 @@ var creationSourceBlueprintSchema = import_zod7.z.object({
|
|
|
5995
6259
|
* a custom app: no feature Module, Library Modules only (`resolvePlatformEnv`
|
|
5996
6260
|
* emits it as a set value naming no Module).
|
|
5997
6261
|
*/
|
|
5998
|
-
enabledModules:
|
|
6262
|
+
enabledModules: import_zod8.z.array(moduleNameSchema).max(100).nullable().optional(),
|
|
6263
|
+
/**
|
|
6264
|
+
* The kind of business (a preset id the published version defines). Its Modules are built
|
|
6265
|
+
* and `enabledModules` adds extras on top; for the general kind `enabledModules` is the pick.
|
|
6266
|
+
*/
|
|
6267
|
+
businessKind: import_zod8.z.string().regex(/^[a-z][a-z0-9-]{0,39}$/).optional()
|
|
5999
6268
|
});
|
|
6000
|
-
var creationSourceSchema =
|
|
6269
|
+
var creationSourceSchema = import_zod8.z.discriminatedUnion("type", [
|
|
6001
6270
|
creationSourceBlankSchema,
|
|
6002
6271
|
creationSourceBlueprintSchema
|
|
6003
6272
|
]);
|
|
6004
|
-
var moduleInstallHandoffSchema =
|
|
6005
|
-
moduleName:
|
|
6006
|
-
homeProjectId:
|
|
6007
|
-
installedVersion:
|
|
6008
|
-
baselineSha:
|
|
6009
|
-
mounts:
|
|
6010
|
-
|
|
6011
|
-
group:
|
|
6012
|
-
segment:
|
|
6273
|
+
var moduleInstallHandoffSchema = import_zod8.z.object({
|
|
6274
|
+
moduleName: import_zod8.z.string().min(1),
|
|
6275
|
+
homeProjectId: import_zod8.z.string().uuid(),
|
|
6276
|
+
installedVersion: import_zod8.z.string().min(1),
|
|
6277
|
+
baselineSha: import_zod8.z.string().min(1),
|
|
6278
|
+
mounts: import_zod8.z.array(
|
|
6279
|
+
import_zod8.z.object({
|
|
6280
|
+
group: import_zod8.z.string(),
|
|
6281
|
+
segment: import_zod8.z.string()
|
|
6013
6282
|
})
|
|
6014
6283
|
),
|
|
6015
|
-
integration:
|
|
6016
|
-
workflows:
|
|
6017
|
-
prunedEnhancements:
|
|
6018
|
-
restoredEnhancements:
|
|
6019
|
-
permissions:
|
|
6020
|
-
|
|
6021
|
-
key:
|
|
6022
|
-
description:
|
|
6284
|
+
integration: import_zod8.z.array(import_zod8.z.string()),
|
|
6285
|
+
workflows: import_zod8.z.array(import_zod8.z.string()),
|
|
6286
|
+
prunedEnhancements: import_zod8.z.array(import_zod8.z.string()),
|
|
6287
|
+
restoredEnhancements: import_zod8.z.array(import_zod8.z.string()).optional(),
|
|
6288
|
+
permissions: import_zod8.z.array(
|
|
6289
|
+
import_zod8.z.object({
|
|
6290
|
+
key: import_zod8.z.string().min(1),
|
|
6291
|
+
description: import_zod8.z.string().min(1)
|
|
6023
6292
|
})
|
|
6024
6293
|
).optional()
|
|
6025
6294
|
});
|
|
6026
6295
|
|
|
6027
6296
|
// ../../packages/lib/src/shared/schema-ops.ts
|
|
6028
|
-
var
|
|
6029
|
-
var columnSpecSchema =
|
|
6030
|
-
name:
|
|
6031
|
-
pgType:
|
|
6032
|
-
nullable:
|
|
6297
|
+
var import_zod9 = require("zod");
|
|
6298
|
+
var columnSpecSchema = import_zod9.z.object({
|
|
6299
|
+
name: import_zod9.z.string().min(1).max(63),
|
|
6300
|
+
pgType: import_zod9.z.string().min(1),
|
|
6301
|
+
nullable: import_zod9.z.boolean(),
|
|
6033
6302
|
/**
|
|
6034
6303
|
* A SQL expression used as the column default, as a raw string (e.g. `'t'`,
|
|
6035
6304
|
* `0`, `now()`, `gen_random_uuid()`). `null` or omitted means no default.
|
|
6036
6305
|
* NOT a JS literal — must already be a valid SQL expression.
|
|
6037
6306
|
*/
|
|
6038
|
-
default:
|
|
6039
|
-
fieldType:
|
|
6040
|
-
fieldConfig:
|
|
6307
|
+
default: import_zod9.z.string().nullable().optional(),
|
|
6308
|
+
fieldType: import_zod9.z.string().optional(),
|
|
6309
|
+
fieldConfig: import_zod9.z.record(import_zod9.z.string(), import_zod9.z.unknown()).optional()
|
|
6041
6310
|
});
|
|
6042
|
-
var referentialActionSchema =
|
|
6311
|
+
var referentialActionSchema = import_zod9.z.enum([
|
|
6043
6312
|
"cascade",
|
|
6044
6313
|
"set null",
|
|
6045
6314
|
"set default",
|
|
6046
6315
|
"restrict",
|
|
6047
6316
|
"no action"
|
|
6048
6317
|
]);
|
|
6049
|
-
var schemaOpSchema =
|
|
6318
|
+
var schemaOpSchema = import_zod9.z.discriminatedUnion("type", [
|
|
6050
6319
|
// Tables
|
|
6051
|
-
|
|
6052
|
-
type:
|
|
6053
|
-
table:
|
|
6054
|
-
columns:
|
|
6055
|
-
primaryKey:
|
|
6056
|
-
ifNotExists:
|
|
6320
|
+
import_zod9.z.object({
|
|
6321
|
+
type: import_zod9.z.literal("createTable"),
|
|
6322
|
+
table: import_zod9.z.string().min(1).max(63),
|
|
6323
|
+
columns: import_zod9.z.array(columnSpecSchema).min(1),
|
|
6324
|
+
primaryKey: import_zod9.z.array(import_zod9.z.string()).optional(),
|
|
6325
|
+
ifNotExists: import_zod9.z.boolean().optional()
|
|
6057
6326
|
}),
|
|
6058
|
-
|
|
6059
|
-
type:
|
|
6060
|
-
table:
|
|
6061
|
-
cascade:
|
|
6062
|
-
ifExists:
|
|
6327
|
+
import_zod9.z.object({
|
|
6328
|
+
type: import_zod9.z.literal("dropTable"),
|
|
6329
|
+
table: import_zod9.z.string().min(1).max(63),
|
|
6330
|
+
cascade: import_zod9.z.boolean().optional(),
|
|
6331
|
+
ifExists: import_zod9.z.boolean().optional()
|
|
6063
6332
|
}),
|
|
6064
|
-
|
|
6065
|
-
type:
|
|
6066
|
-
from:
|
|
6067
|
-
to:
|
|
6333
|
+
import_zod9.z.object({
|
|
6334
|
+
type: import_zod9.z.literal("renameTable"),
|
|
6335
|
+
from: import_zod9.z.string().min(1).max(63),
|
|
6336
|
+
to: import_zod9.z.string().min(1).max(63)
|
|
6068
6337
|
}),
|
|
6069
6338
|
// Columns
|
|
6070
|
-
|
|
6071
|
-
type:
|
|
6072
|
-
table:
|
|
6339
|
+
import_zod9.z.object({
|
|
6340
|
+
type: import_zod9.z.literal("addColumn"),
|
|
6341
|
+
table: import_zod9.z.string().min(1).max(63),
|
|
6073
6342
|
column: columnSpecSchema,
|
|
6074
|
-
ifNotExists:
|
|
6343
|
+
ifNotExists: import_zod9.z.boolean().optional()
|
|
6075
6344
|
}),
|
|
6076
|
-
|
|
6077
|
-
type:
|
|
6078
|
-
table:
|
|
6079
|
-
column:
|
|
6080
|
-
cascade:
|
|
6081
|
-
ifExists:
|
|
6345
|
+
import_zod9.z.object({
|
|
6346
|
+
type: import_zod9.z.literal("dropColumn"),
|
|
6347
|
+
table: import_zod9.z.string().min(1).max(63),
|
|
6348
|
+
column: import_zod9.z.string().min(1).max(63),
|
|
6349
|
+
cascade: import_zod9.z.boolean().optional(),
|
|
6350
|
+
ifExists: import_zod9.z.boolean().optional()
|
|
6082
6351
|
}),
|
|
6083
|
-
|
|
6084
|
-
type:
|
|
6085
|
-
table:
|
|
6086
|
-
from:
|
|
6087
|
-
to:
|
|
6352
|
+
import_zod9.z.object({
|
|
6353
|
+
type: import_zod9.z.literal("renameColumn"),
|
|
6354
|
+
table: import_zod9.z.string().min(1).max(63),
|
|
6355
|
+
from: import_zod9.z.string().min(1).max(63),
|
|
6356
|
+
to: import_zod9.z.string().min(1).max(63)
|
|
6088
6357
|
}),
|
|
6089
|
-
|
|
6090
|
-
type:
|
|
6091
|
-
table:
|
|
6092
|
-
column:
|
|
6093
|
-
newType:
|
|
6358
|
+
import_zod9.z.object({
|
|
6359
|
+
type: import_zod9.z.literal("alterColumnType"),
|
|
6360
|
+
table: import_zod9.z.string().min(1).max(63),
|
|
6361
|
+
column: import_zod9.z.string().min(1).max(63),
|
|
6362
|
+
newType: import_zod9.z.string().min(1),
|
|
6094
6363
|
/** Optional USING expression for potentially-lossy casts */
|
|
6095
|
-
using:
|
|
6364
|
+
using: import_zod9.z.string().optional()
|
|
6096
6365
|
}),
|
|
6097
|
-
|
|
6098
|
-
type:
|
|
6099
|
-
table:
|
|
6100
|
-
column:
|
|
6101
|
-
nullable:
|
|
6366
|
+
import_zod9.z.object({
|
|
6367
|
+
type: import_zod9.z.literal("alterColumnNullable"),
|
|
6368
|
+
table: import_zod9.z.string().min(1).max(63),
|
|
6369
|
+
column: import_zod9.z.string().min(1).max(63),
|
|
6370
|
+
nullable: import_zod9.z.boolean()
|
|
6102
6371
|
}),
|
|
6103
|
-
|
|
6104
|
-
type:
|
|
6105
|
-
table:
|
|
6106
|
-
column:
|
|
6372
|
+
import_zod9.z.object({
|
|
6373
|
+
type: import_zod9.z.literal("alterColumnDefault"),
|
|
6374
|
+
table: import_zod9.z.string().min(1).max(63),
|
|
6375
|
+
column: import_zod9.z.string().min(1).max(63),
|
|
6107
6376
|
/** null means drop the default; otherwise a SQL expression string. */
|
|
6108
|
-
default:
|
|
6377
|
+
default: import_zod9.z.string().nullable()
|
|
6109
6378
|
}),
|
|
6110
6379
|
// Indexes
|
|
6111
|
-
|
|
6112
|
-
type:
|
|
6113
|
-
table:
|
|
6114
|
-
name:
|
|
6115
|
-
columns:
|
|
6116
|
-
unique:
|
|
6380
|
+
import_zod9.z.object({
|
|
6381
|
+
type: import_zod9.z.literal("addIndex"),
|
|
6382
|
+
table: import_zod9.z.string().min(1).max(63),
|
|
6383
|
+
name: import_zod9.z.string().min(1).max(63),
|
|
6384
|
+
columns: import_zod9.z.array(import_zod9.z.string().min(1)).min(1),
|
|
6385
|
+
unique: import_zod9.z.boolean().optional(),
|
|
6117
6386
|
/** Partial-index WHERE clause, as a raw SQL expression */
|
|
6118
|
-
where:
|
|
6119
|
-
ifNotExists:
|
|
6387
|
+
where: import_zod9.z.string().optional(),
|
|
6388
|
+
ifNotExists: import_zod9.z.boolean().optional()
|
|
6120
6389
|
}),
|
|
6121
|
-
|
|
6122
|
-
type:
|
|
6123
|
-
name:
|
|
6124
|
-
cascade:
|
|
6125
|
-
ifExists:
|
|
6390
|
+
import_zod9.z.object({
|
|
6391
|
+
type: import_zod9.z.literal("dropIndex"),
|
|
6392
|
+
name: import_zod9.z.string().min(1).max(63),
|
|
6393
|
+
cascade: import_zod9.z.boolean().optional(),
|
|
6394
|
+
ifExists: import_zod9.z.boolean().optional()
|
|
6126
6395
|
}),
|
|
6127
6396
|
// Constraints
|
|
6128
|
-
|
|
6129
|
-
type:
|
|
6130
|
-
table:
|
|
6131
|
-
name:
|
|
6132
|
-
columns:
|
|
6133
|
-
refTable:
|
|
6134
|
-
refColumns:
|
|
6397
|
+
import_zod9.z.object({
|
|
6398
|
+
type: import_zod9.z.literal("addForeignKey"),
|
|
6399
|
+
table: import_zod9.z.string().min(1).max(63),
|
|
6400
|
+
name: import_zod9.z.string().min(1).max(63),
|
|
6401
|
+
columns: import_zod9.z.array(import_zod9.z.string().min(1)).min(1),
|
|
6402
|
+
refTable: import_zod9.z.string().min(1).max(63),
|
|
6403
|
+
refColumns: import_zod9.z.array(import_zod9.z.string().min(1)).min(1),
|
|
6135
6404
|
onDelete: referentialActionSchema.optional(),
|
|
6136
6405
|
onUpdate: referentialActionSchema.optional()
|
|
6137
6406
|
}),
|
|
6138
|
-
|
|
6139
|
-
type:
|
|
6140
|
-
table:
|
|
6141
|
-
name:
|
|
6142
|
-
columns:
|
|
6407
|
+
import_zod9.z.object({
|
|
6408
|
+
type: import_zod9.z.literal("addUniqueConstraint"),
|
|
6409
|
+
table: import_zod9.z.string().min(1).max(63),
|
|
6410
|
+
name: import_zod9.z.string().min(1).max(63),
|
|
6411
|
+
columns: import_zod9.z.array(import_zod9.z.string().min(1)).min(1)
|
|
6143
6412
|
}),
|
|
6144
|
-
|
|
6145
|
-
type:
|
|
6146
|
-
table:
|
|
6147
|
-
name:
|
|
6148
|
-
expression:
|
|
6413
|
+
import_zod9.z.object({
|
|
6414
|
+
type: import_zod9.z.literal("addCheckConstraint"),
|
|
6415
|
+
table: import_zod9.z.string().min(1).max(63),
|
|
6416
|
+
name: import_zod9.z.string().min(1).max(63),
|
|
6417
|
+
expression: import_zod9.z.string().min(1)
|
|
6149
6418
|
}),
|
|
6150
|
-
|
|
6151
|
-
type:
|
|
6152
|
-
table:
|
|
6153
|
-
name:
|
|
6154
|
-
cascade:
|
|
6155
|
-
ifExists:
|
|
6419
|
+
import_zod9.z.object({
|
|
6420
|
+
type: import_zod9.z.literal("dropConstraint"),
|
|
6421
|
+
table: import_zod9.z.string().min(1).max(63),
|
|
6422
|
+
name: import_zod9.z.string().min(1).max(63),
|
|
6423
|
+
cascade: import_zod9.z.boolean().optional(),
|
|
6424
|
+
ifExists: import_zod9.z.boolean().optional()
|
|
6156
6425
|
}),
|
|
6157
6426
|
// Enum types
|
|
6158
|
-
|
|
6159
|
-
type:
|
|
6160
|
-
name:
|
|
6161
|
-
values:
|
|
6427
|
+
import_zod9.z.object({
|
|
6428
|
+
type: import_zod9.z.literal("createEnum"),
|
|
6429
|
+
name: import_zod9.z.string().min(1).max(63),
|
|
6430
|
+
values: import_zod9.z.array(import_zod9.z.string().min(1)).min(1)
|
|
6162
6431
|
}),
|
|
6163
|
-
|
|
6164
|
-
type:
|
|
6165
|
-
name:
|
|
6166
|
-
value:
|
|
6432
|
+
import_zod9.z.object({
|
|
6433
|
+
type: import_zod9.z.literal("alterEnumAddValue"),
|
|
6434
|
+
name: import_zod9.z.string().min(1).max(63),
|
|
6435
|
+
value: import_zod9.z.string().min(1),
|
|
6167
6436
|
/** Position the new value before an existing one (mutually exclusive with `after`) */
|
|
6168
|
-
before:
|
|
6437
|
+
before: import_zod9.z.string().optional(),
|
|
6169
6438
|
/** Position the new value after an existing one (mutually exclusive with `before`) */
|
|
6170
|
-
after:
|
|
6171
|
-
ifNotExists:
|
|
6439
|
+
after: import_zod9.z.string().optional(),
|
|
6440
|
+
ifNotExists: import_zod9.z.boolean().optional()
|
|
6172
6441
|
}),
|
|
6173
|
-
|
|
6174
|
-
type:
|
|
6175
|
-
name:
|
|
6176
|
-
cascade:
|
|
6177
|
-
ifExists:
|
|
6442
|
+
import_zod9.z.object({
|
|
6443
|
+
type: import_zod9.z.literal("dropEnum"),
|
|
6444
|
+
name: import_zod9.z.string().min(1).max(63),
|
|
6445
|
+
cascade: import_zod9.z.boolean().optional(),
|
|
6446
|
+
ifExists: import_zod9.z.boolean().optional()
|
|
6178
6447
|
}),
|
|
6179
6448
|
// Extensions
|
|
6180
|
-
|
|
6181
|
-
type:
|
|
6182
|
-
name:
|
|
6183
|
-
ifNotExists:
|
|
6449
|
+
import_zod9.z.object({
|
|
6450
|
+
type: import_zod9.z.literal("createExtension"),
|
|
6451
|
+
name: import_zod9.z.string().min(1),
|
|
6452
|
+
ifNotExists: import_zod9.z.boolean().optional()
|
|
6184
6453
|
}),
|
|
6185
6454
|
// Data operations
|
|
6186
6455
|
/**
|
|
@@ -6188,26 +6457,26 @@ var schemaOpSchema = import_zod8.z.discriminatedUnion("type", [
|
|
|
6188
6457
|
* destructive — requires explicit confirm to replay on prod. The
|
|
6189
6458
|
* `description` is what the agent/user sees at confirm time.
|
|
6190
6459
|
*/
|
|
6191
|
-
|
|
6192
|
-
type:
|
|
6193
|
-
description:
|
|
6194
|
-
sql:
|
|
6460
|
+
import_zod9.z.object({
|
|
6461
|
+
type: import_zod9.z.literal("backfill"),
|
|
6462
|
+
description: import_zod9.z.string().min(1),
|
|
6463
|
+
sql: import_zod9.z.string().min(1)
|
|
6195
6464
|
}),
|
|
6196
6465
|
/**
|
|
6197
6466
|
* Escape hatch for DDL the structured ops can't express. `destructive`
|
|
6198
6467
|
* forces classification — use false only for genuinely additive-only SQL
|
|
6199
6468
|
* (e.g. CREATE EXTENSION variants the structured op doesn't cover).
|
|
6200
6469
|
*/
|
|
6201
|
-
|
|
6202
|
-
type:
|
|
6203
|
-
description:
|
|
6204
|
-
sql:
|
|
6205
|
-
destructive:
|
|
6470
|
+
import_zod9.z.object({
|
|
6471
|
+
type: import_zod9.z.literal("rawSql"),
|
|
6472
|
+
description: import_zod9.z.string().min(1),
|
|
6473
|
+
sql: import_zod9.z.string().min(1),
|
|
6474
|
+
destructive: import_zod9.z.boolean()
|
|
6206
6475
|
})
|
|
6207
6476
|
]);
|
|
6208
6477
|
|
|
6209
6478
|
// ../../packages/lib/src/server/module-rail/ops.ts
|
|
6210
|
-
var
|
|
6479
|
+
var import_zod10 = require("zod");
|
|
6211
6480
|
|
|
6212
6481
|
// ../../packages/lib/src/server/module-rail/reconcile.ts
|
|
6213
6482
|
var SANDBOX_BATCH_SIZE = 200;
|
|
@@ -6237,11 +6506,11 @@ function countedCompositionContext(context) {
|
|
|
6237
6506
|
counts.sandboxExecs += 1;
|
|
6238
6507
|
return context.sandbox.exec(...args);
|
|
6239
6508
|
},
|
|
6240
|
-
readFile: (
|
|
6509
|
+
readFile: (path6) => {
|
|
6241
6510
|
counts.sandboxReads += 1;
|
|
6242
|
-
return context.sandbox.readFile(
|
|
6511
|
+
return context.sandbox.readFile(path6);
|
|
6243
6512
|
},
|
|
6244
|
-
fileExists: (
|
|
6513
|
+
fileExists: (path6) => context.sandbox.fileExists(path6),
|
|
6245
6514
|
fetchRemoteRef: (...args) => context.sandbox.fetchRemoteRef(...args),
|
|
6246
6515
|
addDependencies: (specs) => context.sandbox.addDependencies(specs)
|
|
6247
6516
|
}
|
|
@@ -6438,14 +6707,14 @@ async function discoverRootRouteEntries(context, moduleName) {
|
|
|
6438
6707
|
}
|
|
6439
6708
|
return pages.sort((a, b) => a.nextRelativePath.localeCompare(b.nextRelativePath));
|
|
6440
6709
|
}
|
|
6441
|
-
async function writeSandboxFileAtomic(target,
|
|
6442
|
-
const dir = dirnamePosix(
|
|
6443
|
-
const tmp = `${
|
|
6710
|
+
async function writeSandboxFileAtomic(target, path6, content) {
|
|
6711
|
+
const dir = dirnamePosix(path6);
|
|
6712
|
+
const tmp = `${path6}.tmp.${target.runId}`;
|
|
6444
6713
|
const b64 = Buffer.from(content, "utf8").toString("base64");
|
|
6445
6714
|
const cmd = [
|
|
6446
6715
|
`mkdir -p ${shellQuote(dir)}`,
|
|
6447
6716
|
`printf '%s' ${shellQuote(b64)} | base64 -d > ${shellQuote(tmp)}`,
|
|
6448
|
-
`mv -f ${shellQuote(tmp)} ${shellQuote(
|
|
6717
|
+
`mv -f ${shellQuote(tmp)} ${shellQuote(path6)}`
|
|
6449
6718
|
].join(" && ");
|
|
6450
6719
|
const result = await target.sandbox.exec(cmd, { raiseOnError: false });
|
|
6451
6720
|
if (result.exitCode !== 0) {
|
|
@@ -6460,14 +6729,14 @@ async function writeSandboxFileAtomic(target, path5, content) {
|
|
|
6460
6729
|
}
|
|
6461
6730
|
const detail = typeof result.error === "string" && result.error.trim() || result.output.trim() || `exit ${result.exitCode}`;
|
|
6462
6731
|
throw new Error(
|
|
6463
|
-
`[ModuleRail] composition artifacts: failed to write ${redactSecrets(
|
|
6732
|
+
`[ModuleRail] composition artifacts: failed to write ${redactSecrets(path6)}: ${redactSecrets(detail)}`
|
|
6464
6733
|
);
|
|
6465
6734
|
}
|
|
6466
6735
|
}
|
|
6467
6736
|
async function collectAbsentWriteParentDirs(context, writePaths) {
|
|
6468
6737
|
const absent = /* @__PURE__ */ new Set();
|
|
6469
|
-
for (const
|
|
6470
|
-
let dir = dirnamePosix(
|
|
6738
|
+
for (const path6 of writePaths) {
|
|
6739
|
+
let dir = dirnamePosix(path6);
|
|
6471
6740
|
while (dir !== "." && dir !== "") {
|
|
6472
6741
|
if (absent.has(dir)) {
|
|
6473
6742
|
dir = dirnamePosix(dir);
|
|
@@ -6504,12 +6773,12 @@ async function removeAbsentWriteParentDirsOnRollback(context, dirsDeepestFirst)
|
|
|
6504
6773
|
);
|
|
6505
6774
|
}
|
|
6506
6775
|
}
|
|
6507
|
-
async function deleteSandboxFile(context,
|
|
6508
|
-
const result = await context.sandbox.exec(`rm -f ${shellQuote(
|
|
6776
|
+
async function deleteSandboxFile(context, path6) {
|
|
6777
|
+
const result = await context.sandbox.exec(`rm -f ${shellQuote(path6)}`, { raiseOnError: false });
|
|
6509
6778
|
if (result.exitCode !== 0) {
|
|
6510
6779
|
const detail = typeof result.error === "string" && result.error.trim() || result.output.trim() || `exit ${result.exitCode}`;
|
|
6511
6780
|
throw new Error(
|
|
6512
|
-
`[ModuleRail] composition artifacts: failed to delete ${redactSecrets(
|
|
6781
|
+
`[ModuleRail] composition artifacts: failed to delete ${redactSecrets(path6)}: ${redactSecrets(detail)}`
|
|
6513
6782
|
);
|
|
6514
6783
|
}
|
|
6515
6784
|
}
|
|
@@ -6576,37 +6845,37 @@ async function applyCompositionArtifactMutations(context, plan, existingFiles) {
|
|
|
6576
6845
|
await writeSandboxFileAtomic(context, write.path, write.content);
|
|
6577
6846
|
appliedWrites.push({ path: write.path, previous });
|
|
6578
6847
|
}
|
|
6579
|
-
for (const
|
|
6580
|
-
const previous = existingFiles[
|
|
6848
|
+
for (const path6 of plan.deletes) {
|
|
6849
|
+
const previous = existingFiles[path6];
|
|
6581
6850
|
if (previous == null) continue;
|
|
6582
|
-
await deleteSandboxFile(context,
|
|
6583
|
-
appliedDeletes.push({ path:
|
|
6851
|
+
await deleteSandboxFile(context, path6);
|
|
6852
|
+
appliedDeletes.push({ path: path6, previous });
|
|
6584
6853
|
}
|
|
6585
6854
|
} catch (error) {
|
|
6586
6855
|
console.log(
|
|
6587
6856
|
`[ModuleRail] runId=${context.runId} composition artifacts mid-apply failure; rolling back writes=${appliedWrites.length} deletes=${appliedDeletes.length} newDirs=${newlyCreatedParentDirs.length}`
|
|
6588
6857
|
);
|
|
6589
|
-
for (const { path:
|
|
6858
|
+
for (const { path: path6, previous } of [...appliedDeletes].reverse()) {
|
|
6590
6859
|
try {
|
|
6591
|
-
await writeSandboxFileAtomic(context,
|
|
6860
|
+
await writeSandboxFileAtomic(context, path6, previous);
|
|
6592
6861
|
} catch (rollbackError) {
|
|
6593
6862
|
console.log(
|
|
6594
|
-
`[ModuleRail] runId=${context.runId} composition artifacts rollback restore failed for ${redactSecrets(
|
|
6863
|
+
`[ModuleRail] runId=${context.runId} composition artifacts rollback restore failed for ${redactSecrets(path6)}: ${redactSecrets(
|
|
6595
6864
|
rollbackError instanceof Error ? rollbackError.message : String(rollbackError)
|
|
6596
6865
|
)}`
|
|
6597
6866
|
);
|
|
6598
6867
|
}
|
|
6599
6868
|
}
|
|
6600
|
-
for (const { path:
|
|
6869
|
+
for (const { path: path6, previous } of [...appliedWrites].reverse()) {
|
|
6601
6870
|
try {
|
|
6602
6871
|
if (previous == null) {
|
|
6603
|
-
await deleteSandboxFile(context,
|
|
6872
|
+
await deleteSandboxFile(context, path6);
|
|
6604
6873
|
} else {
|
|
6605
|
-
await writeSandboxFileAtomic(context,
|
|
6874
|
+
await writeSandboxFileAtomic(context, path6, previous);
|
|
6606
6875
|
}
|
|
6607
6876
|
} catch (rollbackError) {
|
|
6608
6877
|
console.log(
|
|
6609
|
-
`[ModuleRail] runId=${context.runId} composition artifacts rollback write failed for ${redactSecrets(
|
|
6878
|
+
`[ModuleRail] runId=${context.runId} composition artifacts rollback write failed for ${redactSecrets(path6)}: ${redactSecrets(
|
|
6610
6879
|
rollbackError instanceof Error ? rollbackError.message : String(rollbackError)
|
|
6611
6880
|
)}`
|
|
6612
6881
|
);
|
|
@@ -6640,39 +6909,39 @@ async function listExistingGeneratedStubPaths(context) {
|
|
|
6640
6909
|
COMPOSITION_ARTIFACT_KIND_PROBE_TOKEN
|
|
6641
6910
|
);
|
|
6642
6911
|
const presentPaths = [];
|
|
6643
|
-
for (const
|
|
6912
|
+
for (const path6 of candidates) {
|
|
6644
6913
|
const presence = assertRealCompositionArtifactFilePresence(
|
|
6645
|
-
|
|
6646
|
-
kinds.get(
|
|
6914
|
+
path6,
|
|
6915
|
+
kinds.get(path6) ?? "unreadable"
|
|
6647
6916
|
);
|
|
6648
|
-
if (presence === "file") presentPaths.push(
|
|
6917
|
+
if (presence === "file") presentPaths.push(path6);
|
|
6649
6918
|
}
|
|
6650
6919
|
let contents;
|
|
6651
6920
|
try {
|
|
6652
6921
|
contents = await readFilesMany(context, presentPaths);
|
|
6653
6922
|
} catch (error) {
|
|
6654
|
-
const
|
|
6923
|
+
const path6 = readFailurePath(error);
|
|
6655
6924
|
throw new Error(
|
|
6656
|
-
`[ModuleRail] composition artifacts: cannot read candidate stub ${redactSecrets(
|
|
6925
|
+
`[ModuleRail] composition artifacts: cannot read candidate stub ${redactSecrets(path6)}: ${redactSecrets(
|
|
6657
6926
|
error instanceof Error ? error.message : String(error)
|
|
6658
6927
|
)}`
|
|
6659
6928
|
);
|
|
6660
6929
|
}
|
|
6661
6930
|
const owned = [];
|
|
6662
|
-
for (const
|
|
6663
|
-
const content = contents.get(
|
|
6931
|
+
for (const path6 of presentPaths) {
|
|
6932
|
+
const content = contents.get(path6);
|
|
6664
6933
|
const marker = parseCompositionArtifactMarker(content);
|
|
6665
|
-
if (parseLegacyApiAdapter(
|
|
6666
|
-
owned.push(
|
|
6934
|
+
if (parseLegacyApiAdapter(path6, content) || parseLegacyRouteAdapter(path6, content)) {
|
|
6935
|
+
owned.push(path6);
|
|
6667
6936
|
continue;
|
|
6668
6937
|
}
|
|
6669
6938
|
if (marker == null) continue;
|
|
6670
|
-
if (!markerCongruentWithPath(
|
|
6939
|
+
if (!markerCongruentWithPath(path6, marker)) {
|
|
6671
6940
|
throw new Error(
|
|
6672
|
-
`[ModuleRail] composition artifacts: incongruent marker at ${redactSecrets(
|
|
6941
|
+
`[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
6942
|
);
|
|
6674
6943
|
}
|
|
6675
|
-
owned.push(
|
|
6944
|
+
owned.push(path6);
|
|
6676
6945
|
}
|
|
6677
6946
|
return owned;
|
|
6678
6947
|
}
|
|
@@ -6806,7 +7075,7 @@ async function classifySandboxPathPresenceKindMany(context, paths, probeToken) {
|
|
|
6806
7075
|
{ raiseOnError: false }
|
|
6807
7076
|
);
|
|
6808
7077
|
if (result.exitCode !== 0) {
|
|
6809
|
-
for (const
|
|
7078
|
+
for (const path6 of chunk) kinds.set(path6, "unreadable");
|
|
6810
7079
|
continue;
|
|
6811
7080
|
}
|
|
6812
7081
|
const lines = result.output.split("\n").filter(Boolean);
|
|
@@ -6817,31 +7086,31 @@ async function classifySandboxPathPresenceKindMany(context, paths, probeToken) {
|
|
|
6817
7086
|
}
|
|
6818
7087
|
} else {
|
|
6819
7088
|
for (const line of lines) {
|
|
6820
|
-
const [rawKind,
|
|
7089
|
+
const [rawKind, path6] = line.split(" ", 2);
|
|
6821
7090
|
const kind = rawKind?.replace(/^entry-kind:/, "");
|
|
6822
|
-
if (
|
|
6823
|
-
kinds.set(
|
|
7091
|
+
if (path6 && chunk.includes(path6) && CONVENTIONAL_ENTRY_PRESENCE_KINDS.includes(kind ?? "")) {
|
|
7092
|
+
kinds.set(path6, kind);
|
|
6824
7093
|
}
|
|
6825
7094
|
}
|
|
6826
7095
|
}
|
|
6827
|
-
for (const
|
|
6828
|
-
if (!kinds.has(
|
|
7096
|
+
for (const path6 of chunk) {
|
|
7097
|
+
if (!kinds.has(path6)) kinds.set(path6, "unreadable");
|
|
6829
7098
|
}
|
|
6830
7099
|
}
|
|
6831
7100
|
return kinds;
|
|
6832
7101
|
}
|
|
6833
|
-
async function classifySandboxPathPresenceKind(context,
|
|
6834
|
-
return (await classifySandboxPathPresenceKindMany(context, [
|
|
7102
|
+
async function classifySandboxPathPresenceKind(context, path6, probeToken) {
|
|
7103
|
+
return (await classifySandboxPathPresenceKindMany(context, [path6], probeToken)).get(path6);
|
|
6835
7104
|
}
|
|
6836
7105
|
var LOCALE_JSON_KIND_PROBE_TOKEN = "locale-json-kind";
|
|
6837
|
-
function assertRealLocaleJsonFilePresence(
|
|
7106
|
+
function assertRealLocaleJsonFilePresence(path6, presence) {
|
|
6838
7107
|
if (presence === "file") return;
|
|
6839
7108
|
if (presence === "absent") {
|
|
6840
|
-
throw new Error(`[ModuleRail] composition artifacts: locale file missing at ${
|
|
7109
|
+
throw new Error(`[ModuleRail] composition artifacts: locale file missing at ${path6}`);
|
|
6841
7110
|
}
|
|
6842
|
-
const presenceError = formatConventionalEntryPresenceError(
|
|
7111
|
+
const presenceError = formatConventionalEntryPresenceError(path6, presence);
|
|
6843
7112
|
throw new Error(
|
|
6844
|
-
`[ModuleRail] composition artifacts: ${presenceError ?? `${
|
|
7113
|
+
`[ModuleRail] composition artifacts: ${presenceError ?? `${path6} is not a real locale JSON file`}`
|
|
6845
7114
|
);
|
|
6846
7115
|
}
|
|
6847
7116
|
function splitTaggedLine(line) {
|
|
@@ -6916,11 +7185,11 @@ async function listLocaleBasenamesIfDirMany(context, dirs) {
|
|
|
6916
7185
|
}
|
|
6917
7186
|
async function readFilesMany(context, paths) {
|
|
6918
7187
|
const sortedPaths = [...paths].sort((a, b) => a.localeCompare(b));
|
|
6919
|
-
const settled = await mapConcurrent(sortedPaths, SANDBOX_READ_CONCURRENCY, async (
|
|
7188
|
+
const settled = await mapConcurrent(sortedPaths, SANDBOX_READ_CONCURRENCY, async (path6) => {
|
|
6920
7189
|
try {
|
|
6921
|
-
return { path:
|
|
7190
|
+
return { path: path6, value: await context.sandbox.readFile(path6) };
|
|
6922
7191
|
} catch (error) {
|
|
6923
|
-
return { path:
|
|
7192
|
+
return { path: path6, error };
|
|
6924
7193
|
}
|
|
6925
7194
|
});
|
|
6926
7195
|
const files = /* @__PURE__ */ new Map();
|
|
@@ -6943,61 +7212,61 @@ function readFailurePath(error) {
|
|
|
6943
7212
|
var CONVENTIONAL_ENTRY_KIND_PROBE_TOKEN = "conventional-entry-kind";
|
|
6944
7213
|
var SERVER_REGISTRATIONS_FILE = "server/registrations.ts";
|
|
6945
7214
|
var COMPOSITION_ARTIFACT_KIND_PROBE_TOKEN = "composition-artifact-kind";
|
|
6946
|
-
async function classifyCompositionArtifactPathKind(context,
|
|
6947
|
-
return classifySandboxPathPresenceKind(context,
|
|
7215
|
+
async function classifyCompositionArtifactPathKind(context, path6) {
|
|
7216
|
+
return classifySandboxPathPresenceKind(context, path6, COMPOSITION_ARTIFACT_KIND_PROBE_TOKEN);
|
|
6948
7217
|
}
|
|
6949
|
-
function assertRealCompositionArtifactFilePresence(
|
|
7218
|
+
function assertRealCompositionArtifactFilePresence(path6, presence) {
|
|
6950
7219
|
if (presence === "file") return "file";
|
|
6951
7220
|
if (presence === "absent") return "absent";
|
|
6952
|
-
const presenceError = formatConventionalEntryPresenceError(
|
|
7221
|
+
const presenceError = formatConventionalEntryPresenceError(path6, presence);
|
|
6953
7222
|
throw new Error(
|
|
6954
|
-
`[ModuleRail] composition artifacts: ${presenceError ?? `${
|
|
7223
|
+
`[ModuleRail] composition artifacts: ${presenceError ?? `${path6} is not a real composition artifact file`}`
|
|
6955
7224
|
);
|
|
6956
7225
|
}
|
|
6957
|
-
async function readConventionalEntryIfRealFile(
|
|
6958
|
-
const presence = presences.get(
|
|
6959
|
-
const presenceError = formatConventionalEntryPresenceError(
|
|
7226
|
+
async function readConventionalEntryIfRealFile(path6, kind, presences, files) {
|
|
7227
|
+
const presence = presences.get(path6) ?? "unreadable";
|
|
7228
|
+
const presenceError = formatConventionalEntryPresenceError(path6, presence);
|
|
6960
7229
|
if (presenceError) {
|
|
6961
7230
|
throw new Error(`[ModuleRail] composition artifacts: ${presenceError}`);
|
|
6962
7231
|
}
|
|
6963
7232
|
if (presence === "absent") {
|
|
6964
7233
|
return null;
|
|
6965
7234
|
}
|
|
6966
|
-
const source = files.get(
|
|
7235
|
+
const source = files.get(path6);
|
|
6967
7236
|
switch (kind) {
|
|
6968
7237
|
case "contributions":
|
|
6969
|
-
assertContributionsExport(source,
|
|
6970
|
-
assertClientSafeConventionalEntryImports(source,
|
|
7238
|
+
assertContributionsExport(source, path6);
|
|
7239
|
+
assertClientSafeConventionalEntryImports(source, path6, "contributions");
|
|
6971
7240
|
break;
|
|
6972
7241
|
case "slotCatalogs":
|
|
6973
|
-
assertSlotCatalogsExport(source,
|
|
6974
|
-
assertClientSafeConventionalEntryImports(source,
|
|
7242
|
+
assertSlotCatalogsExport(source, path6);
|
|
7243
|
+
assertClientSafeConventionalEntryImports(source, path6, "slotCatalogs");
|
|
6975
7244
|
break;
|
|
6976
7245
|
case "initializeEnhancement":
|
|
6977
|
-
assertInitializeEnhancementExport(source,
|
|
7246
|
+
assertInitializeEnhancementExport(source, path6);
|
|
6978
7247
|
break;
|
|
6979
7248
|
case "initializeModule":
|
|
6980
|
-
assertInitializeModuleExport(source,
|
|
7249
|
+
assertInitializeModuleExport(source, path6);
|
|
6981
7250
|
break;
|
|
6982
7251
|
}
|
|
6983
7252
|
return source;
|
|
6984
7253
|
}
|
|
6985
7254
|
function hasRealServerRegistrations(modulePath, presences) {
|
|
6986
|
-
const
|
|
6987
|
-
return assertRealCompositionArtifactFilePresence(
|
|
7255
|
+
const path6 = `${modulePath}/${SERVER_REGISTRATIONS_FILE}`;
|
|
7256
|
+
return assertRealCompositionArtifactFilePresence(path6, presences.get(path6) ?? "unreadable") === "file";
|
|
6988
7257
|
}
|
|
6989
|
-
async function readJsonObjectFile(
|
|
6990
|
-
const raw = files.get(
|
|
7258
|
+
async function readJsonObjectFile(path6, files) {
|
|
7259
|
+
const raw = files.get(path6);
|
|
6991
7260
|
let parsed;
|
|
6992
7261
|
try {
|
|
6993
7262
|
parsed = JSON.parse(raw);
|
|
6994
7263
|
} catch (error) {
|
|
6995
7264
|
throw new Error(
|
|
6996
|
-
`[ModuleRail] composition artifacts: invalid JSON at ${
|
|
7265
|
+
`[ModuleRail] composition artifacts: invalid JSON at ${path6}: ${error instanceof Error ? error.message : String(error)}`
|
|
6997
7266
|
);
|
|
6998
7267
|
}
|
|
6999
7268
|
if (parsed === null || Array.isArray(parsed) || typeof parsed !== "object") {
|
|
7000
|
-
throw new Error(`[ModuleRail] composition artifacts: ${
|
|
7269
|
+
throw new Error(`[ModuleRail] composition artifacts: ${path6} root must be an object`);
|
|
7001
7270
|
}
|
|
7002
7271
|
return parsed;
|
|
7003
7272
|
}
|
|
@@ -7050,11 +7319,11 @@ async function collectCompositionEntryFacts(context, registry) {
|
|
|
7050
7319
|
classifySandboxPathPresenceKindMany(context, localePaths, LOCALE_JSON_KIND_PROBE_TOKEN)
|
|
7051
7320
|
]);
|
|
7052
7321
|
const presentConventionalPaths = [];
|
|
7053
|
-
for (const
|
|
7054
|
-
const presence = conventionalPresences.get(
|
|
7055
|
-
if (presence === "file") presentConventionalPaths.push(
|
|
7322
|
+
for (const path6 of conventionalPaths) {
|
|
7323
|
+
const presence = conventionalPresences.get(path6) ?? "unreadable";
|
|
7324
|
+
if (presence === "file") presentConventionalPaths.push(path6);
|
|
7056
7325
|
}
|
|
7057
|
-
const presentLocalePaths = localePaths.filter((
|
|
7326
|
+
const presentLocalePaths = localePaths.filter((path6) => localePresences.get(path6) === "file");
|
|
7058
7327
|
const files = await readFilesMany(context, [...presentConventionalPaths, ...presentLocalePaths]);
|
|
7059
7328
|
const substrateLocales = [];
|
|
7060
7329
|
for (const locale of substrateNames) {
|
|
@@ -7164,8 +7433,8 @@ async function collectCompositionEntryFacts(context, registry) {
|
|
|
7164
7433
|
});
|
|
7165
7434
|
if (rootLocales.length > 0) {
|
|
7166
7435
|
for (const locale of rootLocales) {
|
|
7167
|
-
const
|
|
7168
|
-
assertRealLocaleJsonFilePresence(
|
|
7436
|
+
const path6 = `${modulePath}/i18n/${locale}.json`;
|
|
7437
|
+
assertRealLocaleJsonFilePresence(path6, localePresences.get(path6) ?? "unreadable");
|
|
7169
7438
|
}
|
|
7170
7439
|
const enMessages = await readJsonObjectFile(`${modulePath}/i18n/en.json`, files);
|
|
7171
7440
|
const localeMessages = { en: enMessages };
|
|
@@ -7189,8 +7458,8 @@ async function collectCompositionEntryFacts(context, registry) {
|
|
|
7189
7458
|
if (!enh.active || enh.locales.length === 0) continue;
|
|
7190
7459
|
const peerDir = `${modulePath}/enhancements/${enh.peer}`;
|
|
7191
7460
|
for (const locale of enh.locales) {
|
|
7192
|
-
const
|
|
7193
|
-
assertRealLocaleJsonFilePresence(
|
|
7461
|
+
const path6 = `${peerDir}/i18n/${locale}.json`;
|
|
7462
|
+
assertRealLocaleJsonFilePresence(path6, localePresences.get(path6) ?? "unreadable");
|
|
7194
7463
|
}
|
|
7195
7464
|
const enMessages = await readJsonObjectFile(`${peerDir}/i18n/en.json`, files);
|
|
7196
7465
|
const localeMessages = { en: enMessages };
|
|
@@ -7398,16 +7667,16 @@ async function reconcileCompositionArtifacts(context, options = {}) {
|
|
|
7398
7667
|
for (const stub of probePlan.plan.routeStubs) candidatePaths.add(stub.path);
|
|
7399
7668
|
const existingFiles = {};
|
|
7400
7669
|
const remainingCandidatePaths = [];
|
|
7401
|
-
for (const
|
|
7402
|
-
if (Object.prototype.hasOwnProperty.call(existingAppPages,
|
|
7403
|
-
existingFiles[
|
|
7670
|
+
for (const path6 of [...candidatePaths].sort((a, b) => a.localeCompare(b))) {
|
|
7671
|
+
if (Object.prototype.hasOwnProperty.call(existingAppPages, path6)) {
|
|
7672
|
+
existingFiles[path6] = existingAppPages[path6];
|
|
7404
7673
|
continue;
|
|
7405
7674
|
}
|
|
7406
|
-
if (Object.prototype.hasOwnProperty.call(existingAppRoutes,
|
|
7407
|
-
existingFiles[
|
|
7675
|
+
if (Object.prototype.hasOwnProperty.call(existingAppRoutes, path6)) {
|
|
7676
|
+
existingFiles[path6] = existingAppRoutes[path6];
|
|
7408
7677
|
continue;
|
|
7409
7678
|
}
|
|
7410
|
-
remainingCandidatePaths.push(
|
|
7679
|
+
remainingCandidatePaths.push(path6);
|
|
7411
7680
|
}
|
|
7412
7681
|
const candidateKinds = await classifySandboxPathPresenceKindMany(
|
|
7413
7682
|
context,
|
|
@@ -7415,12 +7684,12 @@ async function reconcileCompositionArtifacts(context, options = {}) {
|
|
|
7415
7684
|
COMPOSITION_ARTIFACT_KIND_PROBE_TOKEN
|
|
7416
7685
|
);
|
|
7417
7686
|
const presentCandidatePaths = [];
|
|
7418
|
-
for (const
|
|
7687
|
+
for (const path6 of remainingCandidatePaths) {
|
|
7419
7688
|
const presence = assertRealCompositionArtifactFilePresence(
|
|
7420
|
-
|
|
7421
|
-
candidateKinds.get(
|
|
7689
|
+
path6,
|
|
7690
|
+
candidateKinds.get(path6) ?? "unreadable"
|
|
7422
7691
|
);
|
|
7423
|
-
if (presence === "file") presentCandidatePaths.push(
|
|
7692
|
+
if (presence === "file") presentCandidatePaths.push(path6);
|
|
7424
7693
|
}
|
|
7425
7694
|
try {
|
|
7426
7695
|
Object.assign(
|
|
@@ -7428,9 +7697,9 @@ async function reconcileCompositionArtifacts(context, options = {}) {
|
|
|
7428
7697
|
Object.fromEntries(await readFilesMany(context, presentCandidatePaths))
|
|
7429
7698
|
);
|
|
7430
7699
|
} catch (error) {
|
|
7431
|
-
const
|
|
7700
|
+
const path6 = readFailurePath(error);
|
|
7432
7701
|
throw new Error(
|
|
7433
|
-
`[ModuleRail] composition artifacts: cannot read ${redactSecrets(
|
|
7702
|
+
`[ModuleRail] composition artifacts: cannot read ${redactSecrets(path6)} during preflight: ${redactSecrets(
|
|
7434
7703
|
error instanceof Error ? error.message : String(error)
|
|
7435
7704
|
)}`
|
|
7436
7705
|
);
|
|
@@ -7503,8 +7772,8 @@ var HOME_I18N_MESSAGES_DIR = `${APP_PACKAGE_DIR}/src/lib/i18n/messages`;
|
|
|
7503
7772
|
var import_typescript6 = __toESM(require("typescript"));
|
|
7504
7773
|
|
|
7505
7774
|
// src/module-boundaries.ts
|
|
7506
|
-
var
|
|
7507
|
-
var
|
|
7775
|
+
var import_node_fs3 = __toESM(require("fs"));
|
|
7776
|
+
var import_node_path3 = __toESM(require("path"));
|
|
7508
7777
|
var import_typescript5 = __toESM(require("typescript"));
|
|
7509
7778
|
var SOURCE_FILE = /\.[cm]?[jt]sx?$/;
|
|
7510
7779
|
var RESOLVE_SUFFIXES = ["", ".ts", ".tsx", ".js", ".jsx", "/index.ts", "/index.tsx", "/index.js"];
|
|
@@ -7523,26 +7792,26 @@ function dataPathsIn(fileName, source) {
|
|
|
7523
7792
|
}
|
|
7524
7793
|
function scanModule(modulesDir, module2) {
|
|
7525
7794
|
const scanned = /* @__PURE__ */ new Map();
|
|
7526
|
-
const moduleDir =
|
|
7527
|
-
if (!
|
|
7528
|
-
const files =
|
|
7795
|
+
const moduleDir = import_node_path3.default.join(modulesDir, module2);
|
|
7796
|
+
if (!import_node_fs3.default.existsSync(moduleDir)) return scanned;
|
|
7797
|
+
const files = import_node_fs3.default.readdirSync(moduleDir, { recursive: true, encoding: "utf8" }).filter((relative) => SOURCE_FILE.test(relative) && !relative.includes("node_modules"));
|
|
7529
7798
|
for (const relative of files) {
|
|
7530
|
-
const absolute =
|
|
7531
|
-
if (!
|
|
7532
|
-
const source =
|
|
7799
|
+
const absolute = import_node_path3.default.join(moduleDir, relative);
|
|
7800
|
+
if (!import_node_fs3.default.statSync(absolute).isFile()) continue;
|
|
7801
|
+
const source = import_node_fs3.default.readFileSync(absolute, "utf8");
|
|
7533
7802
|
const specifiers = import_typescript5.default.preProcessFile(source, true, true).importedFiles.map((f) => f.fileName);
|
|
7534
7803
|
const dataPaths = dataPathsIn(absolute, source);
|
|
7535
7804
|
const targets = [...specifiers, ...dataPaths].flatMap((spec) => {
|
|
7536
7805
|
if (spec.startsWith("@/modules/"))
|
|
7537
|
-
return [
|
|
7806
|
+
return [import_node_path3.default.join(modulesDir, spec.slice("@/modules/".length))];
|
|
7538
7807
|
if (spec.startsWith("./") || spec.startsWith("../")) {
|
|
7539
|
-
return [
|
|
7808
|
+
return [import_node_path3.default.resolve(import_node_path3.default.dirname(absolute), spec)];
|
|
7540
7809
|
}
|
|
7541
7810
|
return [];
|
|
7542
7811
|
});
|
|
7543
7812
|
scanned.set(absolute, {
|
|
7544
7813
|
module: module2,
|
|
7545
|
-
relative: relative.split(
|
|
7814
|
+
relative: relative.split(import_node_path3.default.sep).join("/"),
|
|
7546
7815
|
targets,
|
|
7547
7816
|
dataPaths
|
|
7548
7817
|
});
|
|
@@ -7559,7 +7828,7 @@ function findModuleBoundaryViolations(modulesDir, peersByModule, { pruning = fal
|
|
|
7559
7828
|
(q) => file.relative.startsWith(`enhancements/${q}/`)
|
|
7560
7829
|
);
|
|
7561
7830
|
const goneWith = (target, peer) => {
|
|
7562
|
-
const relative =
|
|
7831
|
+
const relative = import_node_path3.default.relative(modulesDir, target).split(import_node_path3.default.sep).join("/");
|
|
7563
7832
|
return relative === peer || relative.startsWith(`${peer}/`) || new RegExp(`^[^/]+/enhancements/${peer}(/|$)`).test(relative);
|
|
7564
7833
|
};
|
|
7565
7834
|
const tainted = /* @__PURE__ */ new Map();
|
|
@@ -7605,12 +7874,12 @@ function findModuleBoundaryViolations(modulesDir, peersByModule, { pruning = fal
|
|
|
7605
7874
|
return violations.sort((a, b) => a.file.localeCompare(b.file) || a.peer.localeCompare(b.peer));
|
|
7606
7875
|
}
|
|
7607
7876
|
function findOptionalPeerViolations(modulesDir) {
|
|
7608
|
-
if (!
|
|
7877
|
+
if (!import_node_fs3.default.existsSync(modulesDir)) return [];
|
|
7609
7878
|
const manifests = {};
|
|
7610
|
-
for (const name of
|
|
7611
|
-
const manifestPath =
|
|
7612
|
-
if (!
|
|
7613
|
-
manifests[name] = JSON.parse(
|
|
7879
|
+
for (const name of import_node_fs3.default.readdirSync(modulesDir)) {
|
|
7880
|
+
const manifestPath = import_node_path3.default.join(modulesDir, name, "module.json");
|
|
7881
|
+
if (!import_node_fs3.default.existsSync(manifestPath)) continue;
|
|
7882
|
+
manifests[name] = JSON.parse(import_node_fs3.default.readFileSync(manifestPath, "utf8"));
|
|
7614
7883
|
}
|
|
7615
7884
|
const features = Object.keys(manifests).filter((name) => manifests[name].kind === "feature");
|
|
7616
7885
|
const peersByModule = {};
|
|
@@ -7630,11 +7899,11 @@ function formatBoundaryViolations(modulesDirLabel, violations) {
|
|
|
7630
7899
|
// src/index.ts
|
|
7631
7900
|
var MODULES_DIR2 = MODULES_SANDBOX_DIR;
|
|
7632
7901
|
function readComposeInputs(cwd) {
|
|
7633
|
-
const file =
|
|
7634
|
-
if (!
|
|
7902
|
+
const file = import_node_path4.default.join(cwd, COMPOSE_INPUTS_PATH);
|
|
7903
|
+
if (!import_node_fs4.default.existsSync(file)) return { connectedStores: [], installs: {} };
|
|
7635
7904
|
let raw;
|
|
7636
7905
|
try {
|
|
7637
|
-
raw = JSON.parse(
|
|
7906
|
+
raw = JSON.parse(import_node_fs4.default.readFileSync(file, "utf8"));
|
|
7638
7907
|
} catch (error) {
|
|
7639
7908
|
throw new Error(
|
|
7640
7909
|
`[compose] ${file} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`
|
|
@@ -7676,18 +7945,18 @@ function stubGitignoreEntries(artifactPaths) {
|
|
|
7676
7945
|
function writeStubGitignore(cwd, artifactPaths) {
|
|
7677
7946
|
const entries = stubGitignoreEntries(artifactPaths);
|
|
7678
7947
|
const content = [STUB_GITIGNORE_HEADER, ...entries].join("\n") + "\n";
|
|
7679
|
-
const file =
|
|
7680
|
-
const existing =
|
|
7948
|
+
const file = import_node_path4.default.join(cwd, STUB_GITIGNORE_PATH);
|
|
7949
|
+
const existing = import_node_fs4.default.existsSync(file) ? import_node_fs4.default.readFileSync(file, "utf8") : null;
|
|
7681
7950
|
if (existing !== content) {
|
|
7682
|
-
|
|
7683
|
-
|
|
7951
|
+
import_node_fs4.default.mkdirSync(import_node_path4.default.dirname(file), { recursive: true });
|
|
7952
|
+
import_node_fs4.default.writeFileSync(file, content);
|
|
7684
7953
|
}
|
|
7685
7954
|
return entries.length;
|
|
7686
7955
|
}
|
|
7687
7956
|
function assertStubGitignoreOwned(cwd) {
|
|
7688
|
-
const file =
|
|
7689
|
-
if (!
|
|
7690
|
-
const firstLine =
|
|
7957
|
+
const file = import_node_path4.default.join(cwd, STUB_GITIGNORE_PATH);
|
|
7958
|
+
if (!import_node_fs4.default.existsSync(file)) return;
|
|
7959
|
+
const firstLine = import_node_fs4.default.readFileSync(file, "utf8").split("\n", 1)[0] ?? "";
|
|
7691
7960
|
if (firstLine !== STUB_GITIGNORE_HEADER) {
|
|
7692
7961
|
throw new Error(
|
|
7693
7962
|
`[compose] refusing to overwrite ${STUB_GITIGNORE_PATH}: it is not compose-generated (line 1 is not the generated header)`
|
|
@@ -7695,8 +7964,8 @@ function assertStubGitignoreOwned(cwd) {
|
|
|
7695
7964
|
}
|
|
7696
7965
|
}
|
|
7697
7966
|
function assertAppRoot(cwd) {
|
|
7698
|
-
const appPath =
|
|
7699
|
-
if (!
|
|
7967
|
+
const appPath = import_node_path4.default.join(cwd, APP_PACKAGE_JSON);
|
|
7968
|
+
if (!import_node_fs4.default.existsSync(appPath)) {
|
|
7700
7969
|
throw new Error(
|
|
7701
7970
|
`[compose] ${appPath} not found \u2014 run this from the repo root of a Stardeck app`
|
|
7702
7971
|
);
|
|
@@ -7742,9 +8011,9 @@ ${porcelain.trim()}`
|
|
|
7742
8011
|
}
|
|
7743
8012
|
var APP_SRC_DIR = "apps/web/src";
|
|
7744
8013
|
function walkSources(dir, skip, out) {
|
|
7745
|
-
if (!
|
|
7746
|
-
for (const entry of
|
|
7747
|
-
const full =
|
|
8014
|
+
if (!import_node_fs4.default.existsSync(dir)) return;
|
|
8015
|
+
for (const entry of import_node_fs4.default.readdirSync(dir, { withFileTypes: true })) {
|
|
8016
|
+
const full = import_node_path4.default.join(dir, entry.name);
|
|
7748
8017
|
if (full === skip) continue;
|
|
7749
8018
|
if (entry.isDirectory()) walkSources(full, skip, out);
|
|
7750
8019
|
else if (/\.[cm]?[jt]sx?$/.test(entry.name)) out.push(full);
|
|
@@ -7753,13 +8022,13 @@ function walkSources(dir, skip, out) {
|
|
|
7753
8022
|
function assertNoAppLayerImporters(cwd, excluded) {
|
|
7754
8023
|
if (excluded.length === 0) return;
|
|
7755
8024
|
const files = [];
|
|
7756
|
-
walkSources(
|
|
8025
|
+
walkSources(import_node_path4.default.join(cwd, APP_SRC_DIR), import_node_path4.default.join(cwd, MODULES_DIR2), files);
|
|
7757
8026
|
const offenders = [];
|
|
7758
8027
|
for (const file of files) {
|
|
7759
|
-
const source =
|
|
8028
|
+
const source = import_node_fs4.default.readFileSync(file, "utf8");
|
|
7760
8029
|
for (const name of excluded) {
|
|
7761
8030
|
if (new RegExp(`@/modules/${name}(?=["'\`/])`).test(source)) {
|
|
7762
|
-
offenders.push(`${
|
|
8031
|
+
offenders.push(`${import_node_path4.default.relative(cwd, file)}: ${name}`);
|
|
7763
8032
|
}
|
|
7764
8033
|
}
|
|
7765
8034
|
}
|
|
@@ -7793,8 +8062,8 @@ var BUILD_TYPECHECK_SKIP = [
|
|
|
7793
8062
|
"playwright.config.ts"
|
|
7794
8063
|
];
|
|
7795
8064
|
function writeBuildTsconfig(cwd, excluded) {
|
|
7796
|
-
const baseFile =
|
|
7797
|
-
if (!
|
|
8065
|
+
const baseFile = import_node_path4.default.join(cwd, TSCONFIG_PATH);
|
|
8066
|
+
if (!import_node_fs4.default.existsSync(import_node_path4.default.join(cwd, MODULES_DIR2)) || !import_node_fs4.default.existsSync(baseFile)) return;
|
|
7798
8067
|
const base = import_typescript6.default.readConfigFile(baseFile, import_typescript6.default.sys.readFile);
|
|
7799
8068
|
if (base.error) {
|
|
7800
8069
|
throw new Error(
|
|
@@ -7802,7 +8071,7 @@ function writeBuildTsconfig(cwd, excluded) {
|
|
|
7802
8071
|
);
|
|
7803
8072
|
}
|
|
7804
8073
|
const baseExclude = base.config?.exclude ?? ["node_modules"];
|
|
7805
|
-
const modulesDir =
|
|
8074
|
+
const modulesDir = import_node_path4.default.posix.relative("apps/web", MODULES_DIR2);
|
|
7806
8075
|
const config = {
|
|
7807
8076
|
extends: "./tsconfig.json",
|
|
7808
8077
|
exclude: [
|
|
@@ -7817,17 +8086,17 @@ function writeBuildTsconfig(cwd, excluded) {
|
|
|
7817
8086
|
const content = `${BUILD_TSCONFIG_HEADER}
|
|
7818
8087
|
${JSON.stringify(config, null, 2)}
|
|
7819
8088
|
`;
|
|
7820
|
-
const file =
|
|
7821
|
-
if (!
|
|
7822
|
-
|
|
8089
|
+
const file = import_node_path4.default.join(cwd, BUILD_TSCONFIG_PATH);
|
|
8090
|
+
if (!import_node_fs4.default.existsSync(file) || import_node_fs4.default.readFileSync(file, "utf8") !== content) {
|
|
8091
|
+
import_node_fs4.default.writeFileSync(file, content);
|
|
7823
8092
|
}
|
|
7824
8093
|
console.log(
|
|
7825
8094
|
`[compose] ${BUILD_TSCONFIG_PATH} excludes ${excluded.length} Module(s): ${excluded.join(",") || "none"}`
|
|
7826
8095
|
);
|
|
7827
8096
|
}
|
|
7828
8097
|
function removeDir(absolute) {
|
|
7829
|
-
if (!
|
|
7830
|
-
|
|
8098
|
+
if (!import_node_fs4.default.existsSync(absolute)) return false;
|
|
8099
|
+
import_node_fs4.default.rmSync(absolute, { recursive: true, force: true });
|
|
7831
8100
|
return true;
|
|
7832
8101
|
}
|
|
7833
8102
|
async function compose(options) {
|
|
@@ -7855,7 +8124,7 @@ async function compose(options) {
|
|
|
7855
8124
|
});
|
|
7856
8125
|
assertNoAppLayerImporters(options.cwd, preview.excludedModules);
|
|
7857
8126
|
const violations = findModuleBoundaryViolations(
|
|
7858
|
-
|
|
8127
|
+
import_node_path4.default.join(options.cwd, MODULES_DIR2),
|
|
7859
8128
|
Object.fromEntries(preview.composedModules.map((name) => [name, preview.excludedModules])),
|
|
7860
8129
|
{ pruning: true }
|
|
7861
8130
|
);
|
|
@@ -7876,11 +8145,11 @@ async function compose(options) {
|
|
|
7876
8145
|
let pruned = 0;
|
|
7877
8146
|
if (options.prune) {
|
|
7878
8147
|
for (const name of result.excludedModules) {
|
|
7879
|
-
if (removeDir(
|
|
8148
|
+
if (removeDir(import_node_path4.default.join(options.cwd, MODULES_DIR2, name))) pruned++;
|
|
7880
8149
|
}
|
|
7881
8150
|
for (const owner of result.composedModules) {
|
|
7882
8151
|
for (const peer of result.excludedModules) {
|
|
7883
|
-
if (removeDir(
|
|
8152
|
+
if (removeDir(import_node_path4.default.join(options.cwd, MODULES_DIR2, owner, "enhancements", peer))) pruned++;
|
|
7884
8153
|
}
|
|
7885
8154
|
}
|
|
7886
8155
|
}
|
|
@@ -7902,13 +8171,18 @@ async function main() {
|
|
|
7902
8171
|
enabled: { type: "string" },
|
|
7903
8172
|
prune: { type: "boolean", default: false },
|
|
7904
8173
|
cwd: { type: "string" },
|
|
7905
|
-
"check-boundaries": { type: "boolean", default: false }
|
|
8174
|
+
"check-boundaries": { type: "boolean", default: false },
|
|
8175
|
+
"check-ownership": { type: "boolean", default: false }
|
|
7906
8176
|
},
|
|
7907
8177
|
strict: true
|
|
7908
8178
|
});
|
|
8179
|
+
if (values["check-ownership"]) {
|
|
8180
|
+
await checkOwnership(values.cwd ?? process.cwd());
|
|
8181
|
+
return;
|
|
8182
|
+
}
|
|
7909
8183
|
if (values["check-boundaries"]) {
|
|
7910
8184
|
const violations = findOptionalPeerViolations(
|
|
7911
|
-
|
|
8185
|
+
import_node_path5.default.join(values.cwd ?? process.cwd(), MODULES_SANDBOX_DIR)
|
|
7912
8186
|
);
|
|
7913
8187
|
if (violations.length > 0) {
|
|
7914
8188
|
throw new Error(
|
|
@@ -7933,6 +8207,36 @@ async function main() {
|
|
|
7933
8207
|
`[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
8208
|
);
|
|
7935
8209
|
}
|
|
8210
|
+
function gitAt(cwd) {
|
|
8211
|
+
return (args) => new Promise((resolve, reject) => {
|
|
8212
|
+
const child = (0, import_node_child_process3.spawn)("git", ["-c", "core.quotePath=false", ...args], {
|
|
8213
|
+
cwd,
|
|
8214
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
8215
|
+
});
|
|
8216
|
+
let stdout = "";
|
|
8217
|
+
child.stdout.setEncoding("utf8").on("data", (chunk) => stdout += chunk);
|
|
8218
|
+
child.on("error", reject);
|
|
8219
|
+
child.on("close", (status) => resolve({ status: status ?? 1, stdout }));
|
|
8220
|
+
});
|
|
8221
|
+
}
|
|
8222
|
+
async function checkOwnership(cwd) {
|
|
8223
|
+
const repoRoot = import_node_path5.default.resolve(cwd);
|
|
8224
|
+
const result = await checkStardeckOwnedFiles({ repoRoot, git: gitAt(repoRoot) });
|
|
8225
|
+
switch (result.status) {
|
|
8226
|
+
case "skipped":
|
|
8227
|
+
if (result.warning)
|
|
8228
|
+
console.warn(`[compose] --check-ownership: not checked: ${result.reason}`);
|
|
8229
|
+
else console.log(`[compose] --check-ownership: skipped: ${result.reason}`);
|
|
8230
|
+
return;
|
|
8231
|
+
case "clean":
|
|
8232
|
+
console.log(
|
|
8233
|
+
`[compose] --check-ownership: no Stardeck-owned file changed from Stardeck's version (${result.release.slice(0, 12)})`
|
|
8234
|
+
);
|
|
8235
|
+
return;
|
|
8236
|
+
default:
|
|
8237
|
+
throw new Error(`[compose] --check-ownership: ${formatOwnershipGuardFailure(result)}`);
|
|
8238
|
+
}
|
|
8239
|
+
}
|
|
7936
8240
|
main().catch((error) => {
|
|
7937
8241
|
console.error(error instanceof Error ? error.message : String(error));
|
|
7938
8242
|
process.exit(1);
|