@stardeck-customer-apps/compose 0.8.0 → 0.9.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 +19 -0
- package/dist/cli.js +378 -221
- package/dist/index.d.mts +42 -1
- package/dist/index.d.ts +42 -1
- package/dist/index.js +367 -218
- package/dist/index.mjs +364 -218
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2031,12 +2031,15 @@ __export(index_exports, {
|
|
|
2031
2031
|
assertSupportedTypescript: () => assertSupportedTypescript,
|
|
2032
2032
|
compose: () => compose,
|
|
2033
2033
|
escapeGitignore: () => escapeGitignore,
|
|
2034
|
+
findModuleBoundaryViolations: () => findModuleBoundaryViolations,
|
|
2035
|
+
findOptionalPeerViolations: () => findOptionalPeerViolations,
|
|
2036
|
+
formatBoundaryViolations: () => formatBoundaryViolations,
|
|
2034
2037
|
readComposeInputs: () => readComposeInputs
|
|
2035
2038
|
});
|
|
2036
2039
|
module.exports = __toCommonJS(index_exports);
|
|
2037
2040
|
var import_node_child_process2 = require("child_process");
|
|
2038
|
-
var
|
|
2039
|
-
var
|
|
2041
|
+
var import_node_fs3 = __toESM(require("fs"));
|
|
2042
|
+
var import_node_path3 = __toESM(require("path"));
|
|
2040
2043
|
|
|
2041
2044
|
// ../../packages/lib/src/server/module-rail/local-fs-sandbox.ts
|
|
2042
2045
|
var import_node_child_process = require("child_process");
|
|
@@ -2141,23 +2144,23 @@ function isValidModuleName(value) {
|
|
|
2141
2144
|
var projectSlugSchema = import_zod.z.string().refine((value) => validateSlug(value).valid, "must be a valid project slug");
|
|
2142
2145
|
var moduleKindSchema = import_zod.z.enum(["feature", "library", "contract"]);
|
|
2143
2146
|
var endpointMethodSchema = import_zod.z.enum(ENDPOINT_METHODS);
|
|
2144
|
-
function endpointPathValidationError(
|
|
2145
|
-
if (!
|
|
2147
|
+
function endpointPathValidationError(path4) {
|
|
2148
|
+
if (!path4.startsWith("/api/")) {
|
|
2146
2149
|
return 'must begin with "/api/"';
|
|
2147
2150
|
}
|
|
2148
|
-
if (
|
|
2151
|
+
if (path4.endsWith("/")) {
|
|
2149
2152
|
return "must not end with a trailing slash";
|
|
2150
2153
|
}
|
|
2151
|
-
if (
|
|
2154
|
+
if (path4.includes("?") || path4.includes("#")) {
|
|
2152
2155
|
return "must not include query or hash";
|
|
2153
2156
|
}
|
|
2154
|
-
if (
|
|
2157
|
+
if (path4.includes("\\")) {
|
|
2155
2158
|
return "must not include backslashes";
|
|
2156
2159
|
}
|
|
2157
|
-
if (
|
|
2160
|
+
if (path4.includes("%")) {
|
|
2158
2161
|
return "must not include percent-encoded segments";
|
|
2159
2162
|
}
|
|
2160
|
-
const parts =
|
|
2163
|
+
const parts = path4.split("/");
|
|
2161
2164
|
if (parts.length < 3 || parts[0] !== "" || parts[1] !== "api") {
|
|
2162
2165
|
return 'must begin with "/api/"';
|
|
2163
2166
|
}
|
|
@@ -2207,12 +2210,12 @@ function endpointPathValidationError(path3) {
|
|
|
2207
2210
|
}
|
|
2208
2211
|
return null;
|
|
2209
2212
|
}
|
|
2210
|
-
function normalizeEndpointPathForCollision(
|
|
2211
|
-
const error = endpointPathValidationError(
|
|
2213
|
+
function normalizeEndpointPathForCollision(path4) {
|
|
2214
|
+
const error = endpointPathValidationError(path4);
|
|
2212
2215
|
if (error != null) {
|
|
2213
2216
|
throw new Error(`invalid endpoint path: ${error}`);
|
|
2214
2217
|
}
|
|
2215
|
-
return
|
|
2218
|
+
return path4.split("/").map((segment, index) => {
|
|
2216
2219
|
if (index < 2) {
|
|
2217
2220
|
return segment;
|
|
2218
2221
|
}
|
|
@@ -2225,8 +2228,8 @@ function normalizeEndpointPathForCollision(path3) {
|
|
|
2225
2228
|
return segment;
|
|
2226
2229
|
}).join("/");
|
|
2227
2230
|
}
|
|
2228
|
-
var endpointPathSchema = import_zod.z.string().superRefine((
|
|
2229
|
-
const error = endpointPathValidationError(
|
|
2231
|
+
var endpointPathSchema = import_zod.z.string().superRefine((path4, ctx) => {
|
|
2232
|
+
const error = endpointPathValidationError(path4);
|
|
2230
2233
|
if (error != null) {
|
|
2231
2234
|
ctx.addIssue({ code: import_zod.z.ZodIssueCode.custom, message: error });
|
|
2232
2235
|
}
|
|
@@ -2473,16 +2476,16 @@ var moduleManifestSchema = import_zod.z.object({
|
|
|
2473
2476
|
}
|
|
2474
2477
|
}
|
|
2475
2478
|
const seenWorkflows = /* @__PURE__ */ new Map();
|
|
2476
|
-
const noteWorkflow = (name,
|
|
2479
|
+
const noteWorkflow = (name, path4) => {
|
|
2477
2480
|
const existing = seenWorkflows.get(name);
|
|
2478
2481
|
if (existing != null) {
|
|
2479
2482
|
ctx.addIssue({
|
|
2480
2483
|
code: import_zod.z.ZodIssueCode.custom,
|
|
2481
2484
|
message: `duplicate workflow name "${name}" (also at ${existing})`,
|
|
2482
|
-
path:
|
|
2485
|
+
path: path4
|
|
2483
2486
|
});
|
|
2484
2487
|
} else {
|
|
2485
|
-
seenWorkflows.set(name,
|
|
2488
|
+
seenWorkflows.set(name, path4.join("."));
|
|
2486
2489
|
}
|
|
2487
2490
|
};
|
|
2488
2491
|
for (let i = 0; i < manifest.workflows.length; i += 1) {
|
|
@@ -2494,7 +2497,7 @@ var moduleManifestSchema = import_zod.z.object({
|
|
|
2494
2497
|
}
|
|
2495
2498
|
}
|
|
2496
2499
|
const seenEndpointKeys = /* @__PURE__ */ new Map();
|
|
2497
|
-
const noteEndpoint = (claim,
|
|
2500
|
+
const noteEndpoint = (claim, path4) => {
|
|
2498
2501
|
let key;
|
|
2499
2502
|
try {
|
|
2500
2503
|
key = normalizeEndpointPathForCollision(claim.path);
|
|
@@ -2506,10 +2509,10 @@ var moduleManifestSchema = import_zod.z.object({
|
|
|
2506
2509
|
ctx.addIssue({
|
|
2507
2510
|
code: import_zod.z.ZodIssueCode.custom,
|
|
2508
2511
|
message: `duplicate normalized endpoint path "${key}" (also at ${existing})`,
|
|
2509
|
-
path:
|
|
2512
|
+
path: path4
|
|
2510
2513
|
});
|
|
2511
2514
|
} else {
|
|
2512
|
-
seenEndpointKeys.set(key,
|
|
2515
|
+
seenEndpointKeys.set(key, path4.join("."));
|
|
2513
2516
|
}
|
|
2514
2517
|
};
|
|
2515
2518
|
for (let i = 0; i < manifest.endpoints.length; i += 1) {
|
|
@@ -2610,8 +2613,8 @@ var moduleManifestSchema = import_zod.z.object({
|
|
|
2610
2613
|
}).transform(({ version: _legacyVersion, ...manifest }) => manifest);
|
|
2611
2614
|
function formatZodErrors(error) {
|
|
2612
2615
|
return error.issues.map((issue) => {
|
|
2613
|
-
const
|
|
2614
|
-
return `${
|
|
2616
|
+
const path4 = issue.path.length > 0 ? issue.path.join(".") : "root";
|
|
2617
|
+
return `${path4}: ${issue.message}`;
|
|
2615
2618
|
});
|
|
2616
2619
|
}
|
|
2617
2620
|
function parseModuleManifest(raw) {
|
|
@@ -3011,16 +3014,16 @@ var collectedE2eFactsSchema = import_zod3.z.object({
|
|
|
3011
3014
|
function validateRouteExportEvidenceCongruence(input) {
|
|
3012
3015
|
const entrySet = new Set(input.routeEntries);
|
|
3013
3016
|
const exportPaths = input.routePageExports.map((e) => e.nextRelativePath);
|
|
3014
|
-
for (const
|
|
3015
|
-
if (!entrySet.has(
|
|
3016
|
-
input.onIssue(`routePageExports path "${
|
|
3017
|
+
for (const path4 of exportPaths) {
|
|
3018
|
+
if (!entrySet.has(path4)) {
|
|
3019
|
+
input.onIssue(`routePageExports path "${path4}" is not listed in routeEntries`);
|
|
3017
3020
|
}
|
|
3018
3021
|
}
|
|
3019
3022
|
if (input.routeCollectionErrors.length === 0) {
|
|
3020
3023
|
const exportSet = new Set(exportPaths);
|
|
3021
|
-
for (const
|
|
3022
|
-
if (!exportSet.has(
|
|
3023
|
-
input.onIssue(`routeEntries path "${
|
|
3024
|
+
for (const path4 of input.routeEntries) {
|
|
3025
|
+
if (!exportSet.has(path4)) {
|
|
3026
|
+
input.onIssue(`routeEntries path "${path4}" is missing from routePageExports`);
|
|
3024
3027
|
}
|
|
3025
3028
|
}
|
|
3026
3029
|
}
|
|
@@ -4271,13 +4274,13 @@ function assertClientSafeConventionalEntryImports(source, pathLabel, kind) {
|
|
|
4271
4274
|
function createMessageTree() {
|
|
4272
4275
|
return /* @__PURE__ */ Object.create(null);
|
|
4273
4276
|
}
|
|
4274
|
-
function assertMessageTree(value,
|
|
4277
|
+
function assertMessageTree(value, path4) {
|
|
4275
4278
|
if (typeof value === "string") return;
|
|
4276
4279
|
if (value === null || Array.isArray(value) || typeof value !== "object") {
|
|
4277
|
-
throw new Error(`Invalid message at ${
|
|
4280
|
+
throw new Error(`Invalid message at ${path4}: expected string or nested object`);
|
|
4278
4281
|
}
|
|
4279
4282
|
for (const [key, child] of Object.entries(value)) {
|
|
4280
|
-
assertMessageTree(child, `${
|
|
4283
|
+
assertMessageTree(child, `${path4}.${key}`);
|
|
4281
4284
|
}
|
|
4282
4285
|
}
|
|
4283
4286
|
function cloneTree(value) {
|
|
@@ -4287,17 +4290,17 @@ function cloneTree(value) {
|
|
|
4287
4290
|
}
|
|
4288
4291
|
return clone;
|
|
4289
4292
|
}
|
|
4290
|
-
function collectLeafPaths(value,
|
|
4293
|
+
function collectLeafPaths(value, path4, out) {
|
|
4291
4294
|
if (typeof value === "string") {
|
|
4292
|
-
out.push(
|
|
4295
|
+
out.push(path4);
|
|
4293
4296
|
return;
|
|
4294
4297
|
}
|
|
4295
|
-
assertMessageTree(value,
|
|
4296
|
-
for (const [key, child] of Object.entries(value)) collectLeafPaths(child, `${
|
|
4298
|
+
assertMessageTree(value, path4);
|
|
4299
|
+
for (const [key, child] of Object.entries(value)) collectLeafPaths(child, `${path4}.${key}`, out);
|
|
4297
4300
|
}
|
|
4298
|
-
function overrideInto(target, source, owner,
|
|
4301
|
+
function overrideInto(target, source, owner, path4, missing) {
|
|
4299
4302
|
for (const [key, value] of Object.entries(source)) {
|
|
4300
|
-
const nextPath =
|
|
4303
|
+
const nextPath = path4 ? `${path4}.${key}` : key;
|
|
4301
4304
|
if (!Object.prototype.hasOwnProperty.call(target, key)) {
|
|
4302
4305
|
collectLeafPaths(value, nextPath, missing);
|
|
4303
4306
|
continue;
|
|
@@ -4324,9 +4327,9 @@ function overrideInto(target, source, owner, path3, missing) {
|
|
|
4324
4327
|
function composeLocaleMessagesForPlan(layers) {
|
|
4325
4328
|
const result = createMessageTree();
|
|
4326
4329
|
const leafOwners = /* @__PURE__ */ new Map();
|
|
4327
|
-
function mergeInto(target, source, owner,
|
|
4330
|
+
function mergeInto(target, source, owner, path4) {
|
|
4328
4331
|
for (const [key, value] of Object.entries(source)) {
|
|
4329
|
-
const nextPath =
|
|
4332
|
+
const nextPath = path4 ? `${path4}.${key}` : key;
|
|
4330
4333
|
const hasExisting = Object.prototype.hasOwnProperty.call(target, key);
|
|
4331
4334
|
const existing = hasExisting ? target[key] : void 0;
|
|
4332
4335
|
if (typeof value === "string") {
|
|
@@ -4870,8 +4873,8 @@ var METHOD_ORDER = {
|
|
|
4870
4873
|
function compareNames2(a, b) {
|
|
4871
4874
|
return a < b ? -1 : a > b ? 1 : 0;
|
|
4872
4875
|
}
|
|
4873
|
-
function endpointPairKey(method,
|
|
4874
|
-
return `${method} ${
|
|
4876
|
+
function endpointPairKey(method, path4) {
|
|
4877
|
+
return `${method} ${path4}`;
|
|
4875
4878
|
}
|
|
4876
4879
|
function apiPathToRouteFile(apiPath) {
|
|
4877
4880
|
if (!apiPath.startsWith("/api/")) {
|
|
@@ -4885,11 +4888,11 @@ function routeEntryToAppPageFile(nextRelativePath) {
|
|
|
4885
4888
|
}
|
|
4886
4889
|
var APP_ROUTE_FILE_PREFIX = `${APP_PACKAGE_DIR}/src/app/`;
|
|
4887
4890
|
var API_ROUTE_FILE_SUFFIXES = ["/route.ts", "/route.tsx", "/route.jsx", "/route.js"];
|
|
4888
|
-
function appRouteFileToNextRelativePath(
|
|
4889
|
-
const pagePath = appPageFileToNextRelativePath(
|
|
4891
|
+
function appRouteFileToNextRelativePath(path4) {
|
|
4892
|
+
const pagePath = appPageFileToNextRelativePath(path4, APP_PACKAGE_DIR);
|
|
4890
4893
|
if (pagePath != null) return pagePath;
|
|
4891
|
-
if (!
|
|
4892
|
-
const relative =
|
|
4894
|
+
if (!path4.startsWith(APP_ROUTE_FILE_PREFIX)) return null;
|
|
4895
|
+
const relative = path4.slice(APP_ROUTE_FILE_PREFIX.length);
|
|
4893
4896
|
if (!relative.startsWith("api/")) return null;
|
|
4894
4897
|
const suffix = API_ROUTE_FILE_SUFFIXES.find((candidate) => relative.endsWith(candidate));
|
|
4895
4898
|
if (!suffix) return null;
|
|
@@ -4981,9 +4984,9 @@ function legacyHandlerPropertyMatchesPath(property, method, physicalPath, bindin
|
|
|
4981
4984
|
const expectedTokens = knownAliases[`${binding} ${method} ${physicalPath}`] ?? pathTokens;
|
|
4982
4985
|
return handlerTokens.length === expectedTokens.length && handlerTokens.every((token, index) => token === expectedTokens[index]);
|
|
4983
4986
|
}
|
|
4984
|
-
function parseLegacyApiAdapter(
|
|
4985
|
-
const physicalPath = appRouteFileToNextRelativePath(
|
|
4986
|
-
if (expectedMarkerKindForPath(
|
|
4987
|
+
function parseLegacyApiAdapter(path4, source) {
|
|
4988
|
+
const physicalPath = appRouteFileToNextRelativePath(path4);
|
|
4989
|
+
if (expectedMarkerKindForPath(path4) !== "endpoint" || physicalPath == null) return null;
|
|
4987
4990
|
const firstLine = source.split(/\r?\n/, 1)[0]?.trim() ?? "";
|
|
4988
4991
|
const match = firstLine.match(
|
|
4989
4992
|
new RegExp(
|
|
@@ -4993,7 +4996,7 @@ function parseLegacyApiAdapter(path3, source) {
|
|
|
4993
4996
|
if (!match || match[2] !== physicalPath) return null;
|
|
4994
4997
|
const owner = match[1];
|
|
4995
4998
|
if (!isValidModuleName(owner)) return null;
|
|
4996
|
-
const file = import_typescript4.default.createSourceFile(
|
|
4999
|
+
const file = import_typescript4.default.createSourceFile(path4, source, import_typescript4.default.ScriptTarget.Latest, false, import_typescript4.default.ScriptKind.TS);
|
|
4997
5000
|
if (file.parseDiagnostics.length > 0) {
|
|
4998
5001
|
return null;
|
|
4999
5002
|
}
|
|
@@ -5035,9 +5038,9 @@ function parseLegacyApiAdapter(path3, source) {
|
|
|
5035
5038
|
}
|
|
5036
5039
|
return handlerBinding != null && methods.size > 0 ? { owner, methods: [...methods] } : null;
|
|
5037
5040
|
}
|
|
5038
|
-
function parseLegacyRouteAdapter(
|
|
5039
|
-
const physicalPath = appRouteFileToNextRelativePath(
|
|
5040
|
-
if (expectedMarkerKindForPath(
|
|
5041
|
+
function parseLegacyRouteAdapter(path4, source) {
|
|
5042
|
+
const physicalPath = appRouteFileToNextRelativePath(path4);
|
|
5043
|
+
if (expectedMarkerKindForPath(path4) !== "route" || physicalPath == null) return null;
|
|
5041
5044
|
const normalized = source.replace(/\r\n/g, "\n");
|
|
5042
5045
|
const match = normalized.match(
|
|
5043
5046
|
new RegExp(
|
|
@@ -5049,30 +5052,30 @@ function parseLegacyRouteAdapter(path3, source) {
|
|
|
5049
5052
|
const nextRelativePath = match[2];
|
|
5050
5053
|
return isValidModuleName(owner) && nextRelativePath === physicalPath ? { owner, nextRelativePath } : null;
|
|
5051
5054
|
}
|
|
5052
|
-
function legacyRouteAdapterMatchesStub(
|
|
5053
|
-
const legacy = parseLegacyRouteAdapter(
|
|
5055
|
+
function legacyRouteAdapterMatchesStub(path4, source, stub) {
|
|
5056
|
+
const legacy = parseLegacyRouteAdapter(path4, source);
|
|
5054
5057
|
return legacy != null && legacy.owner === stub.owner;
|
|
5055
5058
|
}
|
|
5056
5059
|
var API_ROUTE_DIR_PREFIX = `${APP_PACKAGE_DIR}/src/app/api/`;
|
|
5057
5060
|
var APP_DIR_PREFIX = `${APP_PACKAGE_DIR}/src/app/`;
|
|
5058
5061
|
var PAGE_FILE_SUFFIXES = ["/page.tsx", "/page.ts", "/page.jsx", "/page.js"];
|
|
5059
|
-
function expectedMarkerKindForPath(
|
|
5060
|
-
if (
|
|
5061
|
-
if (
|
|
5062
|
-
if (
|
|
5063
|
-
if (
|
|
5064
|
-
if (
|
|
5065
|
-
if (
|
|
5066
|
-
if (
|
|
5067
|
-
if (
|
|
5062
|
+
function expectedMarkerKindForPath(path4) {
|
|
5063
|
+
if (path4 === MODULES_GEN_PATH) return "registry";
|
|
5064
|
+
if (path4 === MODULE_CONTRIBUTIONS_GEN_PATH) return "contributions";
|
|
5065
|
+
if (path4 === MODULE_DATASTORES_GEN_PATH) return "datastores";
|
|
5066
|
+
if (path4 === MODULE_I18N_GEN_PATH) return "i18n";
|
|
5067
|
+
if (path4 === MODULE_INIT_SERVER_GEN_PATH) return "init";
|
|
5068
|
+
if (path4 === MODULE_SERVER_GEN_PATH) return "server";
|
|
5069
|
+
if (path4.startsWith(API_ROUTE_DIR_PREFIX) && path4.endsWith("/route.ts")) return "endpoint";
|
|
5070
|
+
if (path4.startsWith(APP_DIR_PREFIX) && !path4.startsWith(API_ROUTE_DIR_PREFIX) && PAGE_FILE_SUFFIXES.some((suffix) => path4.endsWith(suffix))) {
|
|
5068
5071
|
return "route";
|
|
5069
5072
|
}
|
|
5070
5073
|
return null;
|
|
5071
5074
|
}
|
|
5072
|
-
function markerCongruentWithPath(
|
|
5073
|
-
if (expectedMarkerKindForPath(
|
|
5075
|
+
function markerCongruentWithPath(path4, marker) {
|
|
5076
|
+
if (expectedMarkerKindForPath(path4) !== marker.kind) return false;
|
|
5074
5077
|
if (marker.kind === "route") {
|
|
5075
|
-
const derived = appPageFileToNextRelativePath(
|
|
5078
|
+
const derived = appPageFileToNextRelativePath(path4, APP_PACKAGE_DIR);
|
|
5076
5079
|
return derived != null && derived === marker.nextRelativePath;
|
|
5077
5080
|
}
|
|
5078
5081
|
return true;
|
|
@@ -5343,8 +5346,8 @@ function buildGeneratedModuleRegistry(input) {
|
|
|
5343
5346
|
function activeEnhancementPeers(registryModule) {
|
|
5344
5347
|
return new Set(registryModule.enhancements.filter((e) => e.active).map((e) => e.peer));
|
|
5345
5348
|
}
|
|
5346
|
-
function endpointBucketKey(
|
|
5347
|
-
return `${
|
|
5349
|
+
function endpointBucketKey(path4, owner, scope) {
|
|
5350
|
+
return `${path4}\0${owner}\0${scopeLabel(scope)}`;
|
|
5348
5351
|
}
|
|
5349
5352
|
function planRouteStubs(input) {
|
|
5350
5353
|
const endpointBuckets = /* @__PURE__ */ new Map();
|
|
@@ -5468,7 +5471,7 @@ function planRouteStubs(input) {
|
|
|
5468
5471
|
for (const entry of entries) {
|
|
5469
5472
|
const nextRelativePath = entry.nextRelativePath;
|
|
5470
5473
|
const destination = normalizeAppRouterDestinationPattern(nextRelativePath);
|
|
5471
|
-
const
|
|
5474
|
+
const path4 = routeEntryToAppPageFile(nextRelativePath);
|
|
5472
5475
|
const existing = routeDestinationOwners.get(destination);
|
|
5473
5476
|
if (existing) {
|
|
5474
5477
|
throw new Error(
|
|
@@ -5478,13 +5481,13 @@ function planRouteStubs(input) {
|
|
|
5478
5481
|
routeDestinationOwners.set(destination, {
|
|
5479
5482
|
owner: regMod.name,
|
|
5480
5483
|
nextRelativePath,
|
|
5481
|
-
path:
|
|
5484
|
+
path: path4
|
|
5482
5485
|
});
|
|
5483
5486
|
const importModule = routeEntryImport(regMod.name, nextRelativePath);
|
|
5484
5487
|
const analyzed = analyzeRouteEntryPath(nextRelativePath);
|
|
5485
5488
|
const mount = mounts.find((claim) => routeEntryCoveredByMountClaim(analyzed, claim));
|
|
5486
5489
|
routeStubs.push({
|
|
5487
|
-
path:
|
|
5490
|
+
path: path4,
|
|
5488
5491
|
owner: regMod.name,
|
|
5489
5492
|
nextRelativePath,
|
|
5490
5493
|
importModule,
|
|
@@ -5545,16 +5548,16 @@ function planCompositionArtifacts(input) {
|
|
|
5545
5548
|
...stubs.routeStubs.map((stub) => stub.path)
|
|
5546
5549
|
]);
|
|
5547
5550
|
const existingGeneratedPathSet = new Set(input.existingGeneratedPaths);
|
|
5548
|
-
const isProvenStaleDeletionCandidate = (
|
|
5549
|
-
if (
|
|
5550
|
-
if (COMPOSITION_RUNTIME_GEN_PATHS.includes(
|
|
5551
|
-
if (desiredPaths.has(
|
|
5552
|
-
if (!existingGeneratedPathSet.has(
|
|
5553
|
-
const legacy = parseLegacyApiAdapter(
|
|
5551
|
+
const isProvenStaleDeletionCandidate = (path4, content) => {
|
|
5552
|
+
if (path4 === MODULES_GEN_PATH) return false;
|
|
5553
|
+
if (COMPOSITION_RUNTIME_GEN_PATHS.includes(path4)) return false;
|
|
5554
|
+
if (desiredPaths.has(path4)) return false;
|
|
5555
|
+
if (!existingGeneratedPathSet.has(path4)) return false;
|
|
5556
|
+
const legacy = parseLegacyApiAdapter(path4, content);
|
|
5554
5557
|
if (legacy) return true;
|
|
5555
|
-
if (parseLegacyRouteAdapter(
|
|
5558
|
+
if (parseLegacyRouteAdapter(path4, content)) return true;
|
|
5556
5559
|
const marker = parseCompositionArtifactMarker(content);
|
|
5557
|
-
return marker != null && markerCongruentWithPath(
|
|
5560
|
+
return marker != null && markerCongruentWithPath(path4, marker);
|
|
5558
5561
|
};
|
|
5559
5562
|
const plannedByDestination = /* @__PURE__ */ new Map();
|
|
5560
5563
|
for (const stub of stubs.routeStubs) {
|
|
@@ -5708,13 +5711,13 @@ function planCompositionArtifacts(input) {
|
|
|
5708
5711
|
for (const stub of stubs.routeStubs) desired.set(stub.path, stub.content);
|
|
5709
5712
|
const endpointStubByPath = new Map(stubs.endpointStubs.map((stub) => [stub.path, stub]));
|
|
5710
5713
|
const routeStubByPath = new Map(stubs.routeStubs.map((stub) => [stub.path, stub]));
|
|
5711
|
-
for (const [
|
|
5712
|
-
const existing = input.existingFiles[
|
|
5714
|
+
for (const [path4] of desired) {
|
|
5715
|
+
const existing = input.existingFiles[path4];
|
|
5713
5716
|
if (existing == null) continue;
|
|
5714
5717
|
let marker = parseCompositionArtifactMarker(existing);
|
|
5715
|
-
const plannedEndpoint = endpointStubByPath.get(
|
|
5716
|
-
const plannedRoute = routeStubByPath.get(
|
|
5717
|
-
const legacy = parseLegacyApiAdapter(
|
|
5718
|
+
const plannedEndpoint = endpointStubByPath.get(path4);
|
|
5719
|
+
const plannedRoute = routeStubByPath.get(path4);
|
|
5720
|
+
const legacy = parseLegacyApiAdapter(path4, existing);
|
|
5718
5721
|
if (legacy && plannedEndpoint) {
|
|
5719
5722
|
marker = {
|
|
5720
5723
|
kind: "endpoint",
|
|
@@ -5722,66 +5725,66 @@ function planCompositionArtifacts(input) {
|
|
|
5722
5725
|
scope: plannedEndpoint.scope
|
|
5723
5726
|
};
|
|
5724
5727
|
}
|
|
5725
|
-
if (!marker && plannedRoute && legacyRouteAdapterMatchesStub(
|
|
5728
|
+
if (!marker && plannedRoute && legacyRouteAdapterMatchesStub(path4, existing, plannedRoute)) {
|
|
5726
5729
|
marker = {
|
|
5727
5730
|
kind: "route",
|
|
5728
5731
|
owner: plannedRoute.owner,
|
|
5729
5732
|
nextRelativePath: plannedRoute.nextRelativePath
|
|
5730
5733
|
};
|
|
5731
5734
|
}
|
|
5732
|
-
const isGenRuntimePath = COMPOSITION_RUNTIME_GEN_PATHS.includes(
|
|
5735
|
+
const isGenRuntimePath = COMPOSITION_RUNTIME_GEN_PATHS.includes(path4);
|
|
5733
5736
|
if (!marker) {
|
|
5734
5737
|
return {
|
|
5735
5738
|
ok: false,
|
|
5736
|
-
error:
|
|
5737
|
-
path:
|
|
5739
|
+
error: path4 === MODULES_GEN_PATH ? `[ModuleRail] composition artifacts: refusing to overwrite unmanaged registry at ${path4}` : isGenRuntimePath ? `[ModuleRail] composition artifacts: refusing to overwrite unmanaged generated file at ${path4}` : `[ModuleRail] composition artifacts: refusing to overwrite unmanaged route at ${path4}`,
|
|
5740
|
+
path: path4
|
|
5738
5741
|
};
|
|
5739
5742
|
}
|
|
5740
|
-
const expectedKind = expectedMarkerKindForPath(
|
|
5743
|
+
const expectedKind = expectedMarkerKindForPath(path4);
|
|
5741
5744
|
if (marker.kind !== expectedKind) {
|
|
5742
5745
|
return {
|
|
5743
5746
|
ok: false,
|
|
5744
|
-
error:
|
|
5745
|
-
path:
|
|
5747
|
+
error: path4 === MODULES_GEN_PATH ? `[ModuleRail] composition artifacts: refusing to overwrite unmanaged registry at ${path4} (incongruent marker ownership: found ${marker.kind} marker)` : isGenRuntimePath ? `[ModuleRail] composition artifacts: refusing to overwrite unmanaged generated file at ${path4} (incongruent marker ownership: found ${marker.kind} marker)` : `[ModuleRail] composition artifacts: refusing to overwrite incongruent marker ownership at ${path4} (expected ${expectedKind ?? "no"} marker, found ${marker.kind} marker)`,
|
|
5748
|
+
path: path4
|
|
5746
5749
|
};
|
|
5747
5750
|
}
|
|
5748
5751
|
if (marker.kind === "endpoint") {
|
|
5749
|
-
const stub = endpointStubByPath.get(
|
|
5752
|
+
const stub = endpointStubByPath.get(path4);
|
|
5750
5753
|
const ownerMatches = stub != null && marker.owner === stub.owner;
|
|
5751
5754
|
const scopeMatches = stub != null && scopeLabel(marker.scope) === scopeLabel(stub.scope);
|
|
5752
5755
|
if (!ownerMatches || !scopeMatches) {
|
|
5753
5756
|
return {
|
|
5754
5757
|
ok: false,
|
|
5755
|
-
error: `[ModuleRail] composition artifacts: refusing to overwrite incongruent marker ownership at ${
|
|
5756
|
-
path:
|
|
5758
|
+
error: `[ModuleRail] composition artifacts: refusing to overwrite incongruent marker ownership at ${path4} (existing owner=${marker.owner} scope=${scopeLabel(marker.scope)} vs planned owner=${stub?.owner ?? "?"} scope=${stub ? scopeLabel(stub.scope) : "?"})`,
|
|
5759
|
+
path: path4,
|
|
5757
5760
|
owner: stub?.owner
|
|
5758
5761
|
};
|
|
5759
5762
|
}
|
|
5760
5763
|
} else if (marker.kind === "route") {
|
|
5761
|
-
const stub = routeStubByPath.get(
|
|
5764
|
+
const stub = routeStubByPath.get(path4);
|
|
5762
5765
|
const ownerMatches = stub != null && marker.owner === stub.owner;
|
|
5763
5766
|
const pathMatches = stub != null && marker.nextRelativePath === stub.nextRelativePath;
|
|
5764
5767
|
if (!ownerMatches || !pathMatches) {
|
|
5765
5768
|
return {
|
|
5766
5769
|
ok: false,
|
|
5767
|
-
error: `[ModuleRail] composition artifacts: refusing to overwrite incongruent marker ownership at ${
|
|
5768
|
-
path:
|
|
5770
|
+
error: `[ModuleRail] composition artifacts: refusing to overwrite incongruent marker ownership at ${path4} (existing owner=${marker.owner} path=${marker.nextRelativePath} vs planned owner=${stub?.owner ?? "?"} path=${stub?.nextRelativePath ?? "?"})`,
|
|
5771
|
+
path: path4,
|
|
5769
5772
|
owner: stub?.owner
|
|
5770
5773
|
};
|
|
5771
5774
|
}
|
|
5772
5775
|
}
|
|
5773
5776
|
}
|
|
5774
5777
|
const deletes = [];
|
|
5775
|
-
for (const
|
|
5776
|
-
const existing = input.existingFiles[
|
|
5778
|
+
for (const path4 of input.existingGeneratedPaths) {
|
|
5779
|
+
const existing = input.existingFiles[path4];
|
|
5777
5780
|
if (existing == null) continue;
|
|
5778
|
-
if (!isProvenStaleDeletionCandidate(
|
|
5781
|
+
if (!isProvenStaleDeletionCandidate(path4, existing)) {
|
|
5779
5782
|
continue;
|
|
5780
5783
|
}
|
|
5781
|
-
deletes.push(
|
|
5784
|
+
deletes.push(path4);
|
|
5782
5785
|
}
|
|
5783
5786
|
deletes.sort(compareNames2);
|
|
5784
|
-
const writes = [...desired.entries()].filter(([
|
|
5787
|
+
const writes = [...desired.entries()].filter(([path4, content]) => input.existingFiles[path4] !== content).map(([path4, content]) => ({ path: path4, content })).sort((a, b) => compareNames2(a.path, b.path));
|
|
5785
5788
|
return {
|
|
5786
5789
|
ok: true,
|
|
5787
5790
|
plan: {
|
|
@@ -5810,8 +5813,16 @@ async function checkoutParsesTypedStores(sandbox) {
|
|
|
5810
5813
|
try {
|
|
5811
5814
|
if (!await sandbox.fileExists(APP_PACKAGE_JSON_PATH)) return false;
|
|
5812
5815
|
const pkg = JSON.parse(await sandbox.readFile(APP_PACKAGE_JSON_PATH));
|
|
5813
|
-
|
|
5814
|
-
|
|
5816
|
+
return composeRangeParsesTypedStores(
|
|
5817
|
+
pkg.devDependencies?.[COMPOSE_PACKAGE] ?? pkg.dependencies?.[COMPOSE_PACKAGE]
|
|
5818
|
+
);
|
|
5819
|
+
} catch {
|
|
5820
|
+
return false;
|
|
5821
|
+
}
|
|
5822
|
+
}
|
|
5823
|
+
function composeRangeParsesTypedStores(range) {
|
|
5824
|
+
if (!range) return false;
|
|
5825
|
+
try {
|
|
5815
5826
|
const lowest = import_semver.default.minVersion(range);
|
|
5816
5827
|
return lowest != null && import_semver.default.gte(lowest, TYPED_STORES_MIN_COMPOSE_VERSION);
|
|
5817
5828
|
} catch {
|
|
@@ -6256,11 +6267,11 @@ function countedCompositionContext(context) {
|
|
|
6256
6267
|
counts.sandboxExecs += 1;
|
|
6257
6268
|
return context.sandbox.exec(...args);
|
|
6258
6269
|
},
|
|
6259
|
-
readFile: (
|
|
6270
|
+
readFile: (path4) => {
|
|
6260
6271
|
counts.sandboxReads += 1;
|
|
6261
|
-
return context.sandbox.readFile(
|
|
6272
|
+
return context.sandbox.readFile(path4);
|
|
6262
6273
|
},
|
|
6263
|
-
fileExists: (
|
|
6274
|
+
fileExists: (path4) => context.sandbox.fileExists(path4),
|
|
6264
6275
|
fetchRemoteRef: (...args) => context.sandbox.fetchRemoteRef(...args),
|
|
6265
6276
|
addDependencies: (specs) => context.sandbox.addDependencies(specs)
|
|
6266
6277
|
}
|
|
@@ -6472,14 +6483,14 @@ async function discoverModuleRouteEntries(context, moduleName) {
|
|
|
6472
6483
|
}
|
|
6473
6484
|
return pages.sort((a, b) => a.nextRelativePath.localeCompare(b.nextRelativePath));
|
|
6474
6485
|
}
|
|
6475
|
-
async function writeSandboxFileAtomic(target,
|
|
6476
|
-
const dir = dirnamePosix(
|
|
6477
|
-
const tmp = `${
|
|
6486
|
+
async function writeSandboxFileAtomic(target, path4, content) {
|
|
6487
|
+
const dir = dirnamePosix(path4);
|
|
6488
|
+
const tmp = `${path4}.tmp.${target.runId}`;
|
|
6478
6489
|
const b64 = Buffer.from(content, "utf8").toString("base64");
|
|
6479
6490
|
const cmd = [
|
|
6480
6491
|
`mkdir -p ${shellQuote(dir)}`,
|
|
6481
6492
|
`printf '%s' ${shellQuote(b64)} | base64 -d > ${shellQuote(tmp)}`,
|
|
6482
|
-
`mv -f ${shellQuote(tmp)} ${shellQuote(
|
|
6493
|
+
`mv -f ${shellQuote(tmp)} ${shellQuote(path4)}`
|
|
6483
6494
|
].join(" && ");
|
|
6484
6495
|
const result = await target.sandbox.exec(cmd, { raiseOnError: false });
|
|
6485
6496
|
if (result.exitCode !== 0) {
|
|
@@ -6494,14 +6505,14 @@ async function writeSandboxFileAtomic(target, path3, content) {
|
|
|
6494
6505
|
}
|
|
6495
6506
|
const detail = typeof result.error === "string" && result.error.trim() || result.output.trim() || `exit ${result.exitCode}`;
|
|
6496
6507
|
throw new Error(
|
|
6497
|
-
`[ModuleRail] composition artifacts: failed to write ${redactSecrets(
|
|
6508
|
+
`[ModuleRail] composition artifacts: failed to write ${redactSecrets(path4)}: ${redactSecrets(detail)}`
|
|
6498
6509
|
);
|
|
6499
6510
|
}
|
|
6500
6511
|
}
|
|
6501
6512
|
async function collectAbsentWriteParentDirs(context, writePaths) {
|
|
6502
6513
|
const absent = /* @__PURE__ */ new Set();
|
|
6503
|
-
for (const
|
|
6504
|
-
let dir = dirnamePosix(
|
|
6514
|
+
for (const path4 of writePaths) {
|
|
6515
|
+
let dir = dirnamePosix(path4);
|
|
6505
6516
|
while (dir !== "." && dir !== "") {
|
|
6506
6517
|
if (absent.has(dir)) {
|
|
6507
6518
|
dir = dirnamePosix(dir);
|
|
@@ -6538,12 +6549,12 @@ async function removeAbsentWriteParentDirsOnRollback(context, dirsDeepestFirst)
|
|
|
6538
6549
|
);
|
|
6539
6550
|
}
|
|
6540
6551
|
}
|
|
6541
|
-
async function deleteSandboxFile(context,
|
|
6542
|
-
const result = await context.sandbox.exec(`rm -f ${shellQuote(
|
|
6552
|
+
async function deleteSandboxFile(context, path4) {
|
|
6553
|
+
const result = await context.sandbox.exec(`rm -f ${shellQuote(path4)}`, { raiseOnError: false });
|
|
6543
6554
|
if (result.exitCode !== 0) {
|
|
6544
6555
|
const detail = typeof result.error === "string" && result.error.trim() || result.output.trim() || `exit ${result.exitCode}`;
|
|
6545
6556
|
throw new Error(
|
|
6546
|
-
`[ModuleRail] composition artifacts: failed to delete ${redactSecrets(
|
|
6557
|
+
`[ModuleRail] composition artifacts: failed to delete ${redactSecrets(path4)}: ${redactSecrets(detail)}`
|
|
6547
6558
|
);
|
|
6548
6559
|
}
|
|
6549
6560
|
}
|
|
@@ -6610,37 +6621,37 @@ async function applyCompositionArtifactMutations(context, plan, existingFiles) {
|
|
|
6610
6621
|
await writeSandboxFileAtomic(context, write.path, write.content);
|
|
6611
6622
|
appliedWrites.push({ path: write.path, previous });
|
|
6612
6623
|
}
|
|
6613
|
-
for (const
|
|
6614
|
-
const previous = existingFiles[
|
|
6624
|
+
for (const path4 of plan.deletes) {
|
|
6625
|
+
const previous = existingFiles[path4];
|
|
6615
6626
|
if (previous == null) continue;
|
|
6616
|
-
await deleteSandboxFile(context,
|
|
6617
|
-
appliedDeletes.push({ path:
|
|
6627
|
+
await deleteSandboxFile(context, path4);
|
|
6628
|
+
appliedDeletes.push({ path: path4, previous });
|
|
6618
6629
|
}
|
|
6619
6630
|
} catch (error) {
|
|
6620
6631
|
console.log(
|
|
6621
6632
|
`[ModuleRail] runId=${context.runId} composition artifacts mid-apply failure; rolling back writes=${appliedWrites.length} deletes=${appliedDeletes.length} newDirs=${newlyCreatedParentDirs.length}`
|
|
6622
6633
|
);
|
|
6623
|
-
for (const { path:
|
|
6634
|
+
for (const { path: path4, previous } of [...appliedDeletes].reverse()) {
|
|
6624
6635
|
try {
|
|
6625
|
-
await writeSandboxFileAtomic(context,
|
|
6636
|
+
await writeSandboxFileAtomic(context, path4, previous);
|
|
6626
6637
|
} catch (rollbackError) {
|
|
6627
6638
|
console.log(
|
|
6628
|
-
`[ModuleRail] runId=${context.runId} composition artifacts rollback restore failed for ${redactSecrets(
|
|
6639
|
+
`[ModuleRail] runId=${context.runId} composition artifacts rollback restore failed for ${redactSecrets(path4)}: ${redactSecrets(
|
|
6629
6640
|
rollbackError instanceof Error ? rollbackError.message : String(rollbackError)
|
|
6630
6641
|
)}`
|
|
6631
6642
|
);
|
|
6632
6643
|
}
|
|
6633
6644
|
}
|
|
6634
|
-
for (const { path:
|
|
6645
|
+
for (const { path: path4, previous } of [...appliedWrites].reverse()) {
|
|
6635
6646
|
try {
|
|
6636
6647
|
if (previous == null) {
|
|
6637
|
-
await deleteSandboxFile(context,
|
|
6648
|
+
await deleteSandboxFile(context, path4);
|
|
6638
6649
|
} else {
|
|
6639
|
-
await writeSandboxFileAtomic(context,
|
|
6650
|
+
await writeSandboxFileAtomic(context, path4, previous);
|
|
6640
6651
|
}
|
|
6641
6652
|
} catch (rollbackError) {
|
|
6642
6653
|
console.log(
|
|
6643
|
-
`[ModuleRail] runId=${context.runId} composition artifacts rollback write failed for ${redactSecrets(
|
|
6654
|
+
`[ModuleRail] runId=${context.runId} composition artifacts rollback write failed for ${redactSecrets(path4)}: ${redactSecrets(
|
|
6644
6655
|
rollbackError instanceof Error ? rollbackError.message : String(rollbackError)
|
|
6645
6656
|
)}`
|
|
6646
6657
|
);
|
|
@@ -6674,39 +6685,39 @@ async function listExistingGeneratedStubPaths(context) {
|
|
|
6674
6685
|
COMPOSITION_ARTIFACT_KIND_PROBE_TOKEN
|
|
6675
6686
|
);
|
|
6676
6687
|
const presentPaths = [];
|
|
6677
|
-
for (const
|
|
6688
|
+
for (const path4 of candidates) {
|
|
6678
6689
|
const presence = assertRealCompositionArtifactFilePresence(
|
|
6679
|
-
|
|
6680
|
-
kinds.get(
|
|
6690
|
+
path4,
|
|
6691
|
+
kinds.get(path4) ?? "unreadable"
|
|
6681
6692
|
);
|
|
6682
|
-
if (presence === "file") presentPaths.push(
|
|
6693
|
+
if (presence === "file") presentPaths.push(path4);
|
|
6683
6694
|
}
|
|
6684
6695
|
let contents;
|
|
6685
6696
|
try {
|
|
6686
6697
|
contents = await readFilesMany(context, presentPaths);
|
|
6687
6698
|
} catch (error) {
|
|
6688
|
-
const
|
|
6699
|
+
const path4 = readFailurePath(error);
|
|
6689
6700
|
throw new Error(
|
|
6690
|
-
`[ModuleRail] composition artifacts: cannot read candidate stub ${redactSecrets(
|
|
6701
|
+
`[ModuleRail] composition artifacts: cannot read candidate stub ${redactSecrets(path4)}: ${redactSecrets(
|
|
6691
6702
|
error instanceof Error ? error.message : String(error)
|
|
6692
6703
|
)}`
|
|
6693
6704
|
);
|
|
6694
6705
|
}
|
|
6695
6706
|
const owned = [];
|
|
6696
|
-
for (const
|
|
6697
|
-
const content = contents.get(
|
|
6707
|
+
for (const path4 of presentPaths) {
|
|
6708
|
+
const content = contents.get(path4);
|
|
6698
6709
|
const marker = parseCompositionArtifactMarker(content);
|
|
6699
|
-
if (parseLegacyApiAdapter(
|
|
6700
|
-
owned.push(
|
|
6710
|
+
if (parseLegacyApiAdapter(path4, content) || parseLegacyRouteAdapter(path4, content)) {
|
|
6711
|
+
owned.push(path4);
|
|
6701
6712
|
continue;
|
|
6702
6713
|
}
|
|
6703
6714
|
if (marker == null) continue;
|
|
6704
|
-
if (!markerCongruentWithPath(
|
|
6715
|
+
if (!markerCongruentWithPath(path4, marker)) {
|
|
6705
6716
|
throw new Error(
|
|
6706
|
-
`[ModuleRail] composition artifacts: incongruent marker at ${redactSecrets(
|
|
6717
|
+
`[ModuleRail] composition artifacts: incongruent marker at ${redactSecrets(path4)} (found ${marker.kind} marker on a path shaped for ${expectedMarkerKindForPath(path4) ?? "no"} artifacts; refusing to reconcile a swapped marker)`
|
|
6707
6718
|
);
|
|
6708
6719
|
}
|
|
6709
|
-
owned.push(
|
|
6720
|
+
owned.push(path4);
|
|
6710
6721
|
}
|
|
6711
6722
|
return owned;
|
|
6712
6723
|
}
|
|
@@ -6839,7 +6850,7 @@ async function classifySandboxPathPresenceKindMany(context, paths, probeToken) {
|
|
|
6839
6850
|
{ raiseOnError: false }
|
|
6840
6851
|
);
|
|
6841
6852
|
if (result.exitCode !== 0) {
|
|
6842
|
-
for (const
|
|
6853
|
+
for (const path4 of chunk) kinds.set(path4, "unreadable");
|
|
6843
6854
|
continue;
|
|
6844
6855
|
}
|
|
6845
6856
|
const lines = result.output.split("\n").filter(Boolean);
|
|
@@ -6850,31 +6861,31 @@ async function classifySandboxPathPresenceKindMany(context, paths, probeToken) {
|
|
|
6850
6861
|
}
|
|
6851
6862
|
} else {
|
|
6852
6863
|
for (const line of lines) {
|
|
6853
|
-
const [rawKind,
|
|
6864
|
+
const [rawKind, path4] = line.split(" ", 2);
|
|
6854
6865
|
const kind = rawKind?.replace(/^entry-kind:/, "");
|
|
6855
|
-
if (
|
|
6856
|
-
kinds.set(
|
|
6866
|
+
if (path4 && chunk.includes(path4) && CONVENTIONAL_ENTRY_PRESENCE_KINDS.includes(kind ?? "")) {
|
|
6867
|
+
kinds.set(path4, kind);
|
|
6857
6868
|
}
|
|
6858
6869
|
}
|
|
6859
6870
|
}
|
|
6860
|
-
for (const
|
|
6861
|
-
if (!kinds.has(
|
|
6871
|
+
for (const path4 of chunk) {
|
|
6872
|
+
if (!kinds.has(path4)) kinds.set(path4, "unreadable");
|
|
6862
6873
|
}
|
|
6863
6874
|
}
|
|
6864
6875
|
return kinds;
|
|
6865
6876
|
}
|
|
6866
|
-
async function classifySandboxPathPresenceKind(context,
|
|
6867
|
-
return (await classifySandboxPathPresenceKindMany(context, [
|
|
6877
|
+
async function classifySandboxPathPresenceKind(context, path4, probeToken) {
|
|
6878
|
+
return (await classifySandboxPathPresenceKindMany(context, [path4], probeToken)).get(path4);
|
|
6868
6879
|
}
|
|
6869
6880
|
var LOCALE_JSON_KIND_PROBE_TOKEN = "locale-json-kind";
|
|
6870
|
-
function assertRealLocaleJsonFilePresence(
|
|
6881
|
+
function assertRealLocaleJsonFilePresence(path4, presence) {
|
|
6871
6882
|
if (presence === "file") return;
|
|
6872
6883
|
if (presence === "absent") {
|
|
6873
|
-
throw new Error(`[ModuleRail] composition artifacts: locale file missing at ${
|
|
6884
|
+
throw new Error(`[ModuleRail] composition artifacts: locale file missing at ${path4}`);
|
|
6874
6885
|
}
|
|
6875
|
-
const presenceError = formatConventionalEntryPresenceError(
|
|
6886
|
+
const presenceError = formatConventionalEntryPresenceError(path4, presence);
|
|
6876
6887
|
throw new Error(
|
|
6877
|
-
`[ModuleRail] composition artifacts: ${presenceError ?? `${
|
|
6888
|
+
`[ModuleRail] composition artifacts: ${presenceError ?? `${path4} is not a real locale JSON file`}`
|
|
6878
6889
|
);
|
|
6879
6890
|
}
|
|
6880
6891
|
function splitTaggedLine(line) {
|
|
@@ -6949,11 +6960,11 @@ async function listLocaleBasenamesIfDirMany(context, dirs) {
|
|
|
6949
6960
|
}
|
|
6950
6961
|
async function readFilesMany(context, paths) {
|
|
6951
6962
|
const sortedPaths = [...paths].sort((a, b) => a.localeCompare(b));
|
|
6952
|
-
const settled = await mapConcurrent(sortedPaths, SANDBOX_READ_CONCURRENCY, async (
|
|
6963
|
+
const settled = await mapConcurrent(sortedPaths, SANDBOX_READ_CONCURRENCY, async (path4) => {
|
|
6953
6964
|
try {
|
|
6954
|
-
return { path:
|
|
6965
|
+
return { path: path4, value: await context.sandbox.readFile(path4) };
|
|
6955
6966
|
} catch (error) {
|
|
6956
|
-
return { path:
|
|
6967
|
+
return { path: path4, error };
|
|
6957
6968
|
}
|
|
6958
6969
|
});
|
|
6959
6970
|
const files = /* @__PURE__ */ new Map();
|
|
@@ -6976,58 +6987,58 @@ function readFailurePath(error) {
|
|
|
6976
6987
|
var CONVENTIONAL_ENTRY_KIND_PROBE_TOKEN = "conventional-entry-kind";
|
|
6977
6988
|
var SERVER_REGISTRATIONS_FILE = "server/registrations.ts";
|
|
6978
6989
|
var COMPOSITION_ARTIFACT_KIND_PROBE_TOKEN = "composition-artifact-kind";
|
|
6979
|
-
async function classifyCompositionArtifactPathKind(context,
|
|
6980
|
-
return classifySandboxPathPresenceKind(context,
|
|
6990
|
+
async function classifyCompositionArtifactPathKind(context, path4) {
|
|
6991
|
+
return classifySandboxPathPresenceKind(context, path4, COMPOSITION_ARTIFACT_KIND_PROBE_TOKEN);
|
|
6981
6992
|
}
|
|
6982
|
-
function assertRealCompositionArtifactFilePresence(
|
|
6993
|
+
function assertRealCompositionArtifactFilePresence(path4, presence) {
|
|
6983
6994
|
if (presence === "file") return "file";
|
|
6984
6995
|
if (presence === "absent") return "absent";
|
|
6985
|
-
const presenceError = formatConventionalEntryPresenceError(
|
|
6996
|
+
const presenceError = formatConventionalEntryPresenceError(path4, presence);
|
|
6986
6997
|
throw new Error(
|
|
6987
|
-
`[ModuleRail] composition artifacts: ${presenceError ?? `${
|
|
6998
|
+
`[ModuleRail] composition artifacts: ${presenceError ?? `${path4} is not a real composition artifact file`}`
|
|
6988
6999
|
);
|
|
6989
7000
|
}
|
|
6990
|
-
async function readConventionalEntryIfRealFile(
|
|
6991
|
-
const presence = presences.get(
|
|
6992
|
-
const presenceError = formatConventionalEntryPresenceError(
|
|
7001
|
+
async function readConventionalEntryIfRealFile(path4, kind, presences, files) {
|
|
7002
|
+
const presence = presences.get(path4) ?? "unreadable";
|
|
7003
|
+
const presenceError = formatConventionalEntryPresenceError(path4, presence);
|
|
6993
7004
|
if (presenceError) {
|
|
6994
7005
|
throw new Error(`[ModuleRail] composition artifacts: ${presenceError}`);
|
|
6995
7006
|
}
|
|
6996
7007
|
if (presence === "absent") {
|
|
6997
7008
|
return null;
|
|
6998
7009
|
}
|
|
6999
|
-
const source = files.get(
|
|
7010
|
+
const source = files.get(path4);
|
|
7000
7011
|
switch (kind) {
|
|
7001
7012
|
case "contributions":
|
|
7002
|
-
assertContributionsExport(source,
|
|
7003
|
-
assertClientSafeConventionalEntryImports(source,
|
|
7013
|
+
assertContributionsExport(source, path4);
|
|
7014
|
+
assertClientSafeConventionalEntryImports(source, path4, "contributions");
|
|
7004
7015
|
break;
|
|
7005
7016
|
case "slotCatalogs":
|
|
7006
|
-
assertSlotCatalogsExport(source,
|
|
7007
|
-
assertClientSafeConventionalEntryImports(source,
|
|
7017
|
+
assertSlotCatalogsExport(source, path4);
|
|
7018
|
+
assertClientSafeConventionalEntryImports(source, path4, "slotCatalogs");
|
|
7008
7019
|
break;
|
|
7009
7020
|
case "initializeEnhancement":
|
|
7010
|
-
assertInitializeEnhancementExport(source,
|
|
7021
|
+
assertInitializeEnhancementExport(source, path4);
|
|
7011
7022
|
break;
|
|
7012
7023
|
}
|
|
7013
7024
|
return source;
|
|
7014
7025
|
}
|
|
7015
7026
|
function hasRealServerRegistrations(modulePath, presences) {
|
|
7016
|
-
const
|
|
7017
|
-
return assertRealCompositionArtifactFilePresence(
|
|
7027
|
+
const path4 = `${modulePath}/${SERVER_REGISTRATIONS_FILE}`;
|
|
7028
|
+
return assertRealCompositionArtifactFilePresence(path4, presences.get(path4) ?? "unreadable") === "file";
|
|
7018
7029
|
}
|
|
7019
|
-
async function readJsonObjectFile(
|
|
7020
|
-
const raw = files.get(
|
|
7030
|
+
async function readJsonObjectFile(path4, files) {
|
|
7031
|
+
const raw = files.get(path4);
|
|
7021
7032
|
let parsed;
|
|
7022
7033
|
try {
|
|
7023
7034
|
parsed = JSON.parse(raw);
|
|
7024
7035
|
} catch (error) {
|
|
7025
7036
|
throw new Error(
|
|
7026
|
-
`[ModuleRail] composition artifacts: invalid JSON at ${
|
|
7037
|
+
`[ModuleRail] composition artifacts: invalid JSON at ${path4}: ${error instanceof Error ? error.message : String(error)}`
|
|
7027
7038
|
);
|
|
7028
7039
|
}
|
|
7029
7040
|
if (parsed === null || Array.isArray(parsed) || typeof parsed !== "object") {
|
|
7030
|
-
throw new Error(`[ModuleRail] composition artifacts: ${
|
|
7041
|
+
throw new Error(`[ModuleRail] composition artifacts: ${path4} root must be an object`);
|
|
7031
7042
|
}
|
|
7032
7043
|
return parsed;
|
|
7033
7044
|
}
|
|
@@ -7076,11 +7087,11 @@ async function collectCompositionEntryFacts(context, registry) {
|
|
|
7076
7087
|
classifySandboxPathPresenceKindMany(context, localePaths, LOCALE_JSON_KIND_PROBE_TOKEN)
|
|
7077
7088
|
]);
|
|
7078
7089
|
const presentConventionalPaths = [];
|
|
7079
|
-
for (const
|
|
7080
|
-
const presence = conventionalPresences.get(
|
|
7081
|
-
if (presence === "file") presentConventionalPaths.push(
|
|
7090
|
+
for (const path4 of conventionalPaths) {
|
|
7091
|
+
const presence = conventionalPresences.get(path4) ?? "unreadable";
|
|
7092
|
+
if (presence === "file") presentConventionalPaths.push(path4);
|
|
7082
7093
|
}
|
|
7083
|
-
const presentLocalePaths = localePaths.filter((
|
|
7094
|
+
const presentLocalePaths = localePaths.filter((path4) => localePresences.get(path4) === "file");
|
|
7084
7095
|
const files = await readFilesMany(context, [...presentConventionalPaths, ...presentLocalePaths]);
|
|
7085
7096
|
const substrateLocales = [];
|
|
7086
7097
|
for (const locale of substrateNames) {
|
|
@@ -7178,8 +7189,8 @@ async function collectCompositionEntryFacts(context, registry) {
|
|
|
7178
7189
|
});
|
|
7179
7190
|
if (rootLocales.length > 0) {
|
|
7180
7191
|
for (const locale of rootLocales) {
|
|
7181
|
-
const
|
|
7182
|
-
assertRealLocaleJsonFilePresence(
|
|
7192
|
+
const path4 = `${modulePath}/i18n/${locale}.json`;
|
|
7193
|
+
assertRealLocaleJsonFilePresence(path4, localePresences.get(path4) ?? "unreadable");
|
|
7183
7194
|
}
|
|
7184
7195
|
const enMessages = await readJsonObjectFile(`${modulePath}/i18n/en.json`, files);
|
|
7185
7196
|
const localeMessages = { en: enMessages };
|
|
@@ -7203,8 +7214,8 @@ async function collectCompositionEntryFacts(context, registry) {
|
|
|
7203
7214
|
if (!enh.active || enh.locales.length === 0) continue;
|
|
7204
7215
|
const peerDir = `${modulePath}/enhancements/${enh.peer}`;
|
|
7205
7216
|
for (const locale of enh.locales) {
|
|
7206
|
-
const
|
|
7207
|
-
assertRealLocaleJsonFilePresence(
|
|
7217
|
+
const path4 = `${peerDir}/i18n/${locale}.json`;
|
|
7218
|
+
assertRealLocaleJsonFilePresence(path4, localePresences.get(path4) ?? "unreadable");
|
|
7208
7219
|
}
|
|
7209
7220
|
const enMessages = await readJsonObjectFile(`${peerDir}/i18n/en.json`, files);
|
|
7210
7221
|
const localeMessages = { en: enMessages };
|
|
@@ -7405,16 +7416,16 @@ async function reconcileCompositionArtifacts(context, options = {}) {
|
|
|
7405
7416
|
for (const stub of probePlan.plan.routeStubs) candidatePaths.add(stub.path);
|
|
7406
7417
|
const existingFiles = {};
|
|
7407
7418
|
const remainingCandidatePaths = [];
|
|
7408
|
-
for (const
|
|
7409
|
-
if (Object.prototype.hasOwnProperty.call(existingAppPages,
|
|
7410
|
-
existingFiles[
|
|
7419
|
+
for (const path4 of [...candidatePaths].sort((a, b) => a.localeCompare(b))) {
|
|
7420
|
+
if (Object.prototype.hasOwnProperty.call(existingAppPages, path4)) {
|
|
7421
|
+
existingFiles[path4] = existingAppPages[path4];
|
|
7411
7422
|
continue;
|
|
7412
7423
|
}
|
|
7413
|
-
if (Object.prototype.hasOwnProperty.call(existingAppRoutes,
|
|
7414
|
-
existingFiles[
|
|
7424
|
+
if (Object.prototype.hasOwnProperty.call(existingAppRoutes, path4)) {
|
|
7425
|
+
existingFiles[path4] = existingAppRoutes[path4];
|
|
7415
7426
|
continue;
|
|
7416
7427
|
}
|
|
7417
|
-
remainingCandidatePaths.push(
|
|
7428
|
+
remainingCandidatePaths.push(path4);
|
|
7418
7429
|
}
|
|
7419
7430
|
const candidateKinds = await classifySandboxPathPresenceKindMany(
|
|
7420
7431
|
context,
|
|
@@ -7422,12 +7433,12 @@ async function reconcileCompositionArtifacts(context, options = {}) {
|
|
|
7422
7433
|
COMPOSITION_ARTIFACT_KIND_PROBE_TOKEN
|
|
7423
7434
|
);
|
|
7424
7435
|
const presentCandidatePaths = [];
|
|
7425
|
-
for (const
|
|
7436
|
+
for (const path4 of remainingCandidatePaths) {
|
|
7426
7437
|
const presence = assertRealCompositionArtifactFilePresence(
|
|
7427
|
-
|
|
7428
|
-
candidateKinds.get(
|
|
7438
|
+
path4,
|
|
7439
|
+
candidateKinds.get(path4) ?? "unreadable"
|
|
7429
7440
|
);
|
|
7430
|
-
if (presence === "file") presentCandidatePaths.push(
|
|
7441
|
+
if (presence === "file") presentCandidatePaths.push(path4);
|
|
7431
7442
|
}
|
|
7432
7443
|
try {
|
|
7433
7444
|
Object.assign(
|
|
@@ -7435,9 +7446,9 @@ async function reconcileCompositionArtifacts(context, options = {}) {
|
|
|
7435
7446
|
Object.fromEntries(await readFilesMany(context, presentCandidatePaths))
|
|
7436
7447
|
);
|
|
7437
7448
|
} catch (error) {
|
|
7438
|
-
const
|
|
7449
|
+
const path4 = readFailurePath(error);
|
|
7439
7450
|
throw new Error(
|
|
7440
|
-
`[ModuleRail] composition artifacts: cannot read ${redactSecrets(
|
|
7451
|
+
`[ModuleRail] composition artifacts: cannot read ${redactSecrets(path4)} during preflight: ${redactSecrets(
|
|
7441
7452
|
error instanceof Error ? error.message : String(error)
|
|
7442
7453
|
)}`
|
|
7443
7454
|
);
|
|
@@ -7506,14 +7517,139 @@ async function reconcileCompositionArtifacts(context, options = {}) {
|
|
|
7506
7517
|
var HOME_I18N_SUBSTRATE_DIR = `${APP_PACKAGE_DIR}/src/lib/i18n/substrate`;
|
|
7507
7518
|
var HOME_I18N_MESSAGES_DIR = `${APP_PACKAGE_DIR}/src/lib/i18n/messages`;
|
|
7508
7519
|
|
|
7520
|
+
// src/module-boundaries.ts
|
|
7521
|
+
var import_node_fs2 = __toESM(require("fs"));
|
|
7522
|
+
var import_node_path2 = __toESM(require("path"));
|
|
7523
|
+
var import_typescript5 = __toESM(require("typescript"));
|
|
7524
|
+
var SOURCE_FILE = /\.[cm]?[jt]sx?$/;
|
|
7525
|
+
var RESOLVE_SUFFIXES = ["", ".ts", ".tsx", ".js", ".jsx", "/index.ts", "/index.tsx", "/index.js"];
|
|
7526
|
+
var DATA_FILE = /\.(?:sql|json)$/;
|
|
7527
|
+
function dataPathsIn(fileName, source) {
|
|
7528
|
+
if (!/\.(?:sql|json)["'`]/.test(source)) return [];
|
|
7529
|
+
const paths = [];
|
|
7530
|
+
const visit = (node) => {
|
|
7531
|
+
if ((import_typescript5.default.isStringLiteralLike(node) || import_typescript5.default.isTemplateTail(node)) && DATA_FILE.test(node.text)) {
|
|
7532
|
+
paths.push(node.text);
|
|
7533
|
+
}
|
|
7534
|
+
import_typescript5.default.forEachChild(node, visit);
|
|
7535
|
+
};
|
|
7536
|
+
visit(import_typescript5.default.createSourceFile(fileName, source, import_typescript5.default.ScriptTarget.Latest, false));
|
|
7537
|
+
return paths;
|
|
7538
|
+
}
|
|
7539
|
+
function scanModule(modulesDir, module2) {
|
|
7540
|
+
const scanned = /* @__PURE__ */ new Map();
|
|
7541
|
+
const moduleDir = import_node_path2.default.join(modulesDir, module2);
|
|
7542
|
+
if (!import_node_fs2.default.existsSync(moduleDir)) return scanned;
|
|
7543
|
+
const files = import_node_fs2.default.readdirSync(moduleDir, { recursive: true, encoding: "utf8" }).filter((relative) => SOURCE_FILE.test(relative) && !relative.includes("node_modules"));
|
|
7544
|
+
for (const relative of files) {
|
|
7545
|
+
const absolute = import_node_path2.default.join(moduleDir, relative);
|
|
7546
|
+
if (!import_node_fs2.default.statSync(absolute).isFile()) continue;
|
|
7547
|
+
const source = import_node_fs2.default.readFileSync(absolute, "utf8");
|
|
7548
|
+
const specifiers = import_typescript5.default.preProcessFile(source, true, true).importedFiles.map((f) => f.fileName);
|
|
7549
|
+
const dataPaths = dataPathsIn(absolute, source);
|
|
7550
|
+
const targets = [...specifiers, ...dataPaths].flatMap((spec) => {
|
|
7551
|
+
if (spec.startsWith("@/modules/"))
|
|
7552
|
+
return [import_node_path2.default.join(modulesDir, spec.slice("@/modules/".length))];
|
|
7553
|
+
if (spec.startsWith("./") || spec.startsWith("../")) {
|
|
7554
|
+
return [import_node_path2.default.resolve(import_node_path2.default.dirname(absolute), spec)];
|
|
7555
|
+
}
|
|
7556
|
+
return [];
|
|
7557
|
+
});
|
|
7558
|
+
scanned.set(absolute, {
|
|
7559
|
+
module: module2,
|
|
7560
|
+
relative: relative.split(import_node_path2.default.sep).join("/"),
|
|
7561
|
+
targets,
|
|
7562
|
+
dataPaths
|
|
7563
|
+
});
|
|
7564
|
+
}
|
|
7565
|
+
return scanned;
|
|
7566
|
+
}
|
|
7567
|
+
function findModuleBoundaryViolations(modulesDir, peersByModule, { pruning = false } = {}) {
|
|
7568
|
+
const files = /* @__PURE__ */ new Map();
|
|
7569
|
+
for (const [module2, peers] of Object.entries(peersByModule)) {
|
|
7570
|
+
if (peers.length === 0) continue;
|
|
7571
|
+
for (const [absolute, file] of scanModule(modulesDir, module2)) files.set(absolute, file);
|
|
7572
|
+
}
|
|
7573
|
+
const exempt = (file, peer) => (pruning ? peersByModule[file.module] ?? [] : [peer]).some(
|
|
7574
|
+
(q) => file.relative.startsWith(`enhancements/${q}/`)
|
|
7575
|
+
);
|
|
7576
|
+
const goneWith = (target, peer) => {
|
|
7577
|
+
const relative = import_node_path2.default.relative(modulesDir, target).split(import_node_path2.default.sep).join("/");
|
|
7578
|
+
return relative === peer || relative.startsWith(`${peer}/`) || new RegExp(`^[^/]+/enhancements/${peer}(/|$)`).test(relative);
|
|
7579
|
+
};
|
|
7580
|
+
const tainted = /* @__PURE__ */ new Map();
|
|
7581
|
+
const queue = [];
|
|
7582
|
+
const taint = (absolute, peer) => {
|
|
7583
|
+
const peers = tainted.get(absolute) ?? /* @__PURE__ */ new Set();
|
|
7584
|
+
if (peers.has(peer)) return;
|
|
7585
|
+
peers.add(peer);
|
|
7586
|
+
tainted.set(absolute, peers);
|
|
7587
|
+
queue.push([absolute, peer]);
|
|
7588
|
+
};
|
|
7589
|
+
const importers = /* @__PURE__ */ new Map();
|
|
7590
|
+
for (const [absolute, file] of files) {
|
|
7591
|
+
for (const target of file.targets) {
|
|
7592
|
+
const hit = RESOLVE_SUFFIXES.map((suffix) => target + suffix).find((c) => files.has(c));
|
|
7593
|
+
if (hit) importers.set(hit, [...importers.get(hit) ?? [], absolute]);
|
|
7594
|
+
}
|
|
7595
|
+
for (const peer of peersByModule[file.module] ?? []) {
|
|
7596
|
+
if (exempt(file, peer)) continue;
|
|
7597
|
+
if (file.targets.some((target) => goneWith(target, peer)) || file.dataPaths.some(
|
|
7598
|
+
(p) => p.startsWith(`modules/${peer}/`) || p.includes(`/modules/${peer}/`)
|
|
7599
|
+
)) {
|
|
7600
|
+
taint(absolute, peer);
|
|
7601
|
+
}
|
|
7602
|
+
}
|
|
7603
|
+
}
|
|
7604
|
+
for (let next = queue.shift(); next; next = queue.shift()) {
|
|
7605
|
+
const [target, peer] = next;
|
|
7606
|
+
for (const importer of importers.get(target) ?? []) {
|
|
7607
|
+
const file = files.get(importer);
|
|
7608
|
+
if ((peersByModule[file.module] ?? []).includes(peer) && !exempt(file, peer)) {
|
|
7609
|
+
taint(importer, peer);
|
|
7610
|
+
}
|
|
7611
|
+
}
|
|
7612
|
+
}
|
|
7613
|
+
const violations = [];
|
|
7614
|
+
for (const [absolute, peers] of tainted) {
|
|
7615
|
+
const file = files.get(absolute);
|
|
7616
|
+
for (const peer of peers) {
|
|
7617
|
+
violations.push({ file: `${file.module}/${file.relative}`, module: file.module, peer });
|
|
7618
|
+
}
|
|
7619
|
+
}
|
|
7620
|
+
return violations.sort((a, b) => a.file.localeCompare(b.file) || a.peer.localeCompare(b.peer));
|
|
7621
|
+
}
|
|
7622
|
+
function findOptionalPeerViolations(modulesDir) {
|
|
7623
|
+
if (!import_node_fs2.default.existsSync(modulesDir)) return [];
|
|
7624
|
+
const manifests = {};
|
|
7625
|
+
for (const name of import_node_fs2.default.readdirSync(modulesDir)) {
|
|
7626
|
+
const manifestPath = import_node_path2.default.join(modulesDir, name, "module.json");
|
|
7627
|
+
if (!import_node_fs2.default.existsSync(manifestPath)) continue;
|
|
7628
|
+
manifests[name] = JSON.parse(import_node_fs2.default.readFileSync(manifestPath, "utf8"));
|
|
7629
|
+
}
|
|
7630
|
+
const features = Object.keys(manifests).filter((name) => manifests[name].kind === "feature");
|
|
7631
|
+
const peersByModule = {};
|
|
7632
|
+
for (const [name, manifest] of Object.entries(manifests)) {
|
|
7633
|
+
const uses = new Set(manifest.uses ?? []);
|
|
7634
|
+
peersByModule[name] = features.filter((peer) => peer !== name && !uses.has(peer));
|
|
7635
|
+
}
|
|
7636
|
+
return findModuleBoundaryViolations(modulesDir, peersByModule);
|
|
7637
|
+
}
|
|
7638
|
+
function formatBoundaryViolations(modulesDirLabel, violations) {
|
|
7639
|
+
const files = new Set(violations.map((v) => v.file)).size;
|
|
7640
|
+
return `${files} Module file(s) import a peer Module that may be absent (not in the Module's \`uses\`, or excluded by --enabled).
|
|
7641
|
+
\`next build\` type-checks them after the peer is gone. For each line, move the file into ${modulesDirLabel}/<module>/enhancements/<peer>/ or declare <peer> in the module's module.json \`uses\` (and enable it).
|
|
7642
|
+
` + violations.map((v) => `${modulesDirLabel}/${v.file}: ${v.peer}`).join("\n");
|
|
7643
|
+
}
|
|
7644
|
+
|
|
7509
7645
|
// src/index.ts
|
|
7510
7646
|
var MODULES_DIR2 = MODULES_SANDBOX_DIR;
|
|
7511
7647
|
function readComposeInputs(cwd) {
|
|
7512
|
-
const file =
|
|
7513
|
-
if (!
|
|
7648
|
+
const file = import_node_path3.default.join(cwd, COMPOSE_INPUTS_PATH);
|
|
7649
|
+
if (!import_node_fs3.default.existsSync(file)) return { connectedStores: [], installs: {} };
|
|
7514
7650
|
let raw;
|
|
7515
7651
|
try {
|
|
7516
|
-
raw = JSON.parse(
|
|
7652
|
+
raw = JSON.parse(import_node_fs3.default.readFileSync(file, "utf8"));
|
|
7517
7653
|
} catch (error) {
|
|
7518
7654
|
throw new Error(
|
|
7519
7655
|
`[compose] ${file} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`
|
|
@@ -7555,18 +7691,18 @@ function stubGitignoreEntries(artifactPaths) {
|
|
|
7555
7691
|
function writeStubGitignore(cwd, artifactPaths) {
|
|
7556
7692
|
const entries = stubGitignoreEntries(artifactPaths);
|
|
7557
7693
|
const content = [STUB_GITIGNORE_HEADER, ...entries].join("\n") + "\n";
|
|
7558
|
-
const file =
|
|
7559
|
-
const existing =
|
|
7694
|
+
const file = import_node_path3.default.join(cwd, STUB_GITIGNORE_PATH);
|
|
7695
|
+
const existing = import_node_fs3.default.existsSync(file) ? import_node_fs3.default.readFileSync(file, "utf8") : null;
|
|
7560
7696
|
if (existing !== content) {
|
|
7561
|
-
|
|
7562
|
-
|
|
7697
|
+
import_node_fs3.default.mkdirSync(import_node_path3.default.dirname(file), { recursive: true });
|
|
7698
|
+
import_node_fs3.default.writeFileSync(file, content);
|
|
7563
7699
|
}
|
|
7564
7700
|
return entries.length;
|
|
7565
7701
|
}
|
|
7566
7702
|
function assertStubGitignoreOwned(cwd) {
|
|
7567
|
-
const file =
|
|
7568
|
-
if (!
|
|
7569
|
-
const firstLine =
|
|
7703
|
+
const file = import_node_path3.default.join(cwd, STUB_GITIGNORE_PATH);
|
|
7704
|
+
if (!import_node_fs3.default.existsSync(file)) return;
|
|
7705
|
+
const firstLine = import_node_fs3.default.readFileSync(file, "utf8").split("\n", 1)[0] ?? "";
|
|
7570
7706
|
if (firstLine !== STUB_GITIGNORE_HEADER) {
|
|
7571
7707
|
throw new Error(
|
|
7572
7708
|
`[compose] refusing to overwrite ${STUB_GITIGNORE_PATH}: it is not compose-generated (line 1 is not the generated header)`
|
|
@@ -7574,8 +7710,8 @@ function assertStubGitignoreOwned(cwd) {
|
|
|
7574
7710
|
}
|
|
7575
7711
|
}
|
|
7576
7712
|
function assertAppRoot(cwd) {
|
|
7577
|
-
const appPath =
|
|
7578
|
-
if (!
|
|
7713
|
+
const appPath = import_node_path3.default.join(cwd, APP_PACKAGE_JSON);
|
|
7714
|
+
if (!import_node_fs3.default.existsSync(appPath)) {
|
|
7579
7715
|
throw new Error(
|
|
7580
7716
|
`[compose] ${appPath} not found \u2014 run this from the repo root of a Stardeck app`
|
|
7581
7717
|
);
|
|
@@ -7621,9 +7757,9 @@ ${porcelain.trim()}`
|
|
|
7621
7757
|
}
|
|
7622
7758
|
var APP_SRC_DIR = "apps/web/src";
|
|
7623
7759
|
function walkSources(dir, skip, out) {
|
|
7624
|
-
if (!
|
|
7625
|
-
for (const entry of
|
|
7626
|
-
const full =
|
|
7760
|
+
if (!import_node_fs3.default.existsSync(dir)) return;
|
|
7761
|
+
for (const entry of import_node_fs3.default.readdirSync(dir, { withFileTypes: true })) {
|
|
7762
|
+
const full = import_node_path3.default.join(dir, entry.name);
|
|
7627
7763
|
if (full === skip) continue;
|
|
7628
7764
|
if (entry.isDirectory()) walkSources(full, skip, out);
|
|
7629
7765
|
else if (/\.[cm]?[jt]sx?$/.test(entry.name)) out.push(full);
|
|
@@ -7632,13 +7768,13 @@ function walkSources(dir, skip, out) {
|
|
|
7632
7768
|
function assertNoAppLayerImporters(cwd, excluded) {
|
|
7633
7769
|
if (excluded.length === 0) return;
|
|
7634
7770
|
const files = [];
|
|
7635
|
-
walkSources(
|
|
7771
|
+
walkSources(import_node_path3.default.join(cwd, APP_SRC_DIR), import_node_path3.default.join(cwd, MODULES_DIR2), files);
|
|
7636
7772
|
const offenders = [];
|
|
7637
7773
|
for (const file of files) {
|
|
7638
|
-
const source =
|
|
7774
|
+
const source = import_node_fs3.default.readFileSync(file, "utf8");
|
|
7639
7775
|
for (const name of excluded) {
|
|
7640
7776
|
if (new RegExp(`@/modules/${name}(?=["'\`/])`).test(source)) {
|
|
7641
|
-
offenders.push(`${
|
|
7777
|
+
offenders.push(`${import_node_path3.default.relative(cwd, file)}: ${name}`);
|
|
7642
7778
|
}
|
|
7643
7779
|
}
|
|
7644
7780
|
}
|
|
@@ -7651,8 +7787,8 @@ function assertNoAppLayerImporters(cwd, excluded) {
|
|
|
7651
7787
|
}
|
|
7652
7788
|
}
|
|
7653
7789
|
function removeDir(absolute) {
|
|
7654
|
-
if (!
|
|
7655
|
-
|
|
7790
|
+
if (!import_node_fs3.default.existsSync(absolute)) return false;
|
|
7791
|
+
import_node_fs3.default.rmSync(absolute, { recursive: true, force: true });
|
|
7656
7792
|
return true;
|
|
7657
7793
|
}
|
|
7658
7794
|
async function compose(options) {
|
|
@@ -7679,6 +7815,16 @@ async function compose(options) {
|
|
|
7679
7815
|
dryRun: true
|
|
7680
7816
|
});
|
|
7681
7817
|
assertNoAppLayerImporters(options.cwd, preview.excludedModules);
|
|
7818
|
+
const violations = findModuleBoundaryViolations(
|
|
7819
|
+
import_node_path3.default.join(options.cwd, MODULES_DIR2),
|
|
7820
|
+
Object.fromEntries(preview.composedModules.map((name) => [name, preview.excludedModules])),
|
|
7821
|
+
{ pruning: true }
|
|
7822
|
+
);
|
|
7823
|
+
if (violations.length > 0) {
|
|
7824
|
+
throw new Error(
|
|
7825
|
+
`[compose] refusing --prune: ${formatBoundaryViolations(MODULES_DIR2, violations)}`
|
|
7826
|
+
);
|
|
7827
|
+
}
|
|
7682
7828
|
}
|
|
7683
7829
|
const result = await reconcileCompositionArtifacts(context, {
|
|
7684
7830
|
enabledModulesRaw: options.enabled,
|
|
@@ -7690,11 +7836,11 @@ async function compose(options) {
|
|
|
7690
7836
|
let pruned = 0;
|
|
7691
7837
|
if (options.prune) {
|
|
7692
7838
|
for (const name of result.excludedModules) {
|
|
7693
|
-
if (removeDir(
|
|
7839
|
+
if (removeDir(import_node_path3.default.join(options.cwd, MODULES_DIR2, name))) pruned++;
|
|
7694
7840
|
}
|
|
7695
7841
|
for (const owner of result.composedModules) {
|
|
7696
7842
|
for (const peer of result.excludedModules) {
|
|
7697
|
-
if (removeDir(
|
|
7843
|
+
if (removeDir(import_node_path3.default.join(options.cwd, MODULES_DIR2, owner, "enhancements", peer))) pruned++;
|
|
7698
7844
|
}
|
|
7699
7845
|
}
|
|
7700
7846
|
}
|
|
@@ -7713,5 +7859,8 @@ async function compose(options) {
|
|
|
7713
7859
|
assertSupportedTypescript,
|
|
7714
7860
|
compose,
|
|
7715
7861
|
escapeGitignore,
|
|
7862
|
+
findModuleBoundaryViolations,
|
|
7863
|
+
findOptionalPeerViolations,
|
|
7864
|
+
formatBoundaryViolations,
|
|
7716
7865
|
readComposeInputs
|
|
7717
7866
|
});
|