@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/dist/index.mjs CHANGED
@@ -2020,8 +2020,8 @@ var require_semver2 = __commonJS({
2020
2020
 
2021
2021
  // src/index.ts
2022
2022
  import { spawnSync as spawnSync2 } from "child_process";
2023
- import fs2 from "fs";
2024
- import path2 from "path";
2023
+ import fs3 from "fs";
2024
+ import path3 from "path";
2025
2025
 
2026
2026
  // ../../packages/lib/src/server/module-rail/local-fs-sandbox.ts
2027
2027
  import { spawnSync } from "child_process";
@@ -2126,23 +2126,23 @@ function isValidModuleName(value) {
2126
2126
  var projectSlugSchema = z.string().refine((value) => validateSlug(value).valid, "must be a valid project slug");
2127
2127
  var moduleKindSchema = z.enum(["feature", "library", "contract"]);
2128
2128
  var endpointMethodSchema = z.enum(ENDPOINT_METHODS);
2129
- function endpointPathValidationError(path3) {
2130
- if (!path3.startsWith("/api/")) {
2129
+ function endpointPathValidationError(path4) {
2130
+ if (!path4.startsWith("/api/")) {
2131
2131
  return 'must begin with "/api/"';
2132
2132
  }
2133
- if (path3.endsWith("/")) {
2133
+ if (path4.endsWith("/")) {
2134
2134
  return "must not end with a trailing slash";
2135
2135
  }
2136
- if (path3.includes("?") || path3.includes("#")) {
2136
+ if (path4.includes("?") || path4.includes("#")) {
2137
2137
  return "must not include query or hash";
2138
2138
  }
2139
- if (path3.includes("\\")) {
2139
+ if (path4.includes("\\")) {
2140
2140
  return "must not include backslashes";
2141
2141
  }
2142
- if (path3.includes("%")) {
2142
+ if (path4.includes("%")) {
2143
2143
  return "must not include percent-encoded segments";
2144
2144
  }
2145
- const parts = path3.split("/");
2145
+ const parts = path4.split("/");
2146
2146
  if (parts.length < 3 || parts[0] !== "" || parts[1] !== "api") {
2147
2147
  return 'must begin with "/api/"';
2148
2148
  }
@@ -2192,12 +2192,12 @@ function endpointPathValidationError(path3) {
2192
2192
  }
2193
2193
  return null;
2194
2194
  }
2195
- function normalizeEndpointPathForCollision(path3) {
2196
- const error = endpointPathValidationError(path3);
2195
+ function normalizeEndpointPathForCollision(path4) {
2196
+ const error = endpointPathValidationError(path4);
2197
2197
  if (error != null) {
2198
2198
  throw new Error(`invalid endpoint path: ${error}`);
2199
2199
  }
2200
- return path3.split("/").map((segment, index) => {
2200
+ return path4.split("/").map((segment, index) => {
2201
2201
  if (index < 2) {
2202
2202
  return segment;
2203
2203
  }
@@ -2210,8 +2210,8 @@ function normalizeEndpointPathForCollision(path3) {
2210
2210
  return segment;
2211
2211
  }).join("/");
2212
2212
  }
2213
- var endpointPathSchema = z.string().superRefine((path3, ctx) => {
2214
- const error = endpointPathValidationError(path3);
2213
+ var endpointPathSchema = z.string().superRefine((path4, ctx) => {
2214
+ const error = endpointPathValidationError(path4);
2215
2215
  if (error != null) {
2216
2216
  ctx.addIssue({ code: z.ZodIssueCode.custom, message: error });
2217
2217
  }
@@ -2458,16 +2458,16 @@ var moduleManifestSchema = z.object({
2458
2458
  }
2459
2459
  }
2460
2460
  const seenWorkflows = /* @__PURE__ */ new Map();
2461
- const noteWorkflow = (name, path3) => {
2461
+ const noteWorkflow = (name, path4) => {
2462
2462
  const existing = seenWorkflows.get(name);
2463
2463
  if (existing != null) {
2464
2464
  ctx.addIssue({
2465
2465
  code: z.ZodIssueCode.custom,
2466
2466
  message: `duplicate workflow name "${name}" (also at ${existing})`,
2467
- path: path3
2467
+ path: path4
2468
2468
  });
2469
2469
  } else {
2470
- seenWorkflows.set(name, path3.join("."));
2470
+ seenWorkflows.set(name, path4.join("."));
2471
2471
  }
2472
2472
  };
2473
2473
  for (let i = 0; i < manifest.workflows.length; i += 1) {
@@ -2479,7 +2479,7 @@ var moduleManifestSchema = z.object({
2479
2479
  }
2480
2480
  }
2481
2481
  const seenEndpointKeys = /* @__PURE__ */ new Map();
2482
- const noteEndpoint = (claim, path3) => {
2482
+ const noteEndpoint = (claim, path4) => {
2483
2483
  let key;
2484
2484
  try {
2485
2485
  key = normalizeEndpointPathForCollision(claim.path);
@@ -2491,10 +2491,10 @@ var moduleManifestSchema = z.object({
2491
2491
  ctx.addIssue({
2492
2492
  code: z.ZodIssueCode.custom,
2493
2493
  message: `duplicate normalized endpoint path "${key}" (also at ${existing})`,
2494
- path: path3
2494
+ path: path4
2495
2495
  });
2496
2496
  } else {
2497
- seenEndpointKeys.set(key, path3.join("."));
2497
+ seenEndpointKeys.set(key, path4.join("."));
2498
2498
  }
2499
2499
  };
2500
2500
  for (let i = 0; i < manifest.endpoints.length; i += 1) {
@@ -2595,8 +2595,8 @@ var moduleManifestSchema = z.object({
2595
2595
  }).transform(({ version: _legacyVersion, ...manifest }) => manifest);
2596
2596
  function formatZodErrors(error) {
2597
2597
  return error.issues.map((issue) => {
2598
- const path3 = issue.path.length > 0 ? issue.path.join(".") : "root";
2599
- return `${path3}: ${issue.message}`;
2598
+ const path4 = issue.path.length > 0 ? issue.path.join(".") : "root";
2599
+ return `${path4}: ${issue.message}`;
2600
2600
  });
2601
2601
  }
2602
2602
  function parseModuleManifest(raw) {
@@ -2996,16 +2996,16 @@ var collectedE2eFactsSchema = z3.object({
2996
2996
  function validateRouteExportEvidenceCongruence(input) {
2997
2997
  const entrySet = new Set(input.routeEntries);
2998
2998
  const exportPaths = input.routePageExports.map((e) => e.nextRelativePath);
2999
- for (const path3 of exportPaths) {
3000
- if (!entrySet.has(path3)) {
3001
- input.onIssue(`routePageExports path "${path3}" is not listed in routeEntries`);
2999
+ for (const path4 of exportPaths) {
3000
+ if (!entrySet.has(path4)) {
3001
+ input.onIssue(`routePageExports path "${path4}" is not listed in routeEntries`);
3002
3002
  }
3003
3003
  }
3004
3004
  if (input.routeCollectionErrors.length === 0) {
3005
3005
  const exportSet = new Set(exportPaths);
3006
- for (const path3 of input.routeEntries) {
3007
- if (!exportSet.has(path3)) {
3008
- input.onIssue(`routeEntries path "${path3}" is missing from routePageExports`);
3006
+ for (const path4 of input.routeEntries) {
3007
+ if (!exportSet.has(path4)) {
3008
+ input.onIssue(`routeEntries path "${path4}" is missing from routePageExports`);
3009
3009
  }
3010
3010
  }
3011
3011
  }
@@ -4256,13 +4256,13 @@ function assertClientSafeConventionalEntryImports(source, pathLabel, kind) {
4256
4256
  function createMessageTree() {
4257
4257
  return /* @__PURE__ */ Object.create(null);
4258
4258
  }
4259
- function assertMessageTree(value, path3) {
4259
+ function assertMessageTree(value, path4) {
4260
4260
  if (typeof value === "string") return;
4261
4261
  if (value === null || Array.isArray(value) || typeof value !== "object") {
4262
- throw new Error(`Invalid message at ${path3}: expected string or nested object`);
4262
+ throw new Error(`Invalid message at ${path4}: expected string or nested object`);
4263
4263
  }
4264
4264
  for (const [key, child] of Object.entries(value)) {
4265
- assertMessageTree(child, `${path3}.${key}`);
4265
+ assertMessageTree(child, `${path4}.${key}`);
4266
4266
  }
4267
4267
  }
4268
4268
  function cloneTree(value) {
@@ -4272,17 +4272,17 @@ function cloneTree(value) {
4272
4272
  }
4273
4273
  return clone;
4274
4274
  }
4275
- function collectLeafPaths(value, path3, out) {
4275
+ function collectLeafPaths(value, path4, out) {
4276
4276
  if (typeof value === "string") {
4277
- out.push(path3);
4277
+ out.push(path4);
4278
4278
  return;
4279
4279
  }
4280
- assertMessageTree(value, path3);
4281
- for (const [key, child] of Object.entries(value)) collectLeafPaths(child, `${path3}.${key}`, out);
4280
+ assertMessageTree(value, path4);
4281
+ for (const [key, child] of Object.entries(value)) collectLeafPaths(child, `${path4}.${key}`, out);
4282
4282
  }
4283
- function overrideInto(target, source, owner, path3, missing) {
4283
+ function overrideInto(target, source, owner, path4, missing) {
4284
4284
  for (const [key, value] of Object.entries(source)) {
4285
- const nextPath = path3 ? `${path3}.${key}` : key;
4285
+ const nextPath = path4 ? `${path4}.${key}` : key;
4286
4286
  if (!Object.prototype.hasOwnProperty.call(target, key)) {
4287
4287
  collectLeafPaths(value, nextPath, missing);
4288
4288
  continue;
@@ -4309,9 +4309,9 @@ function overrideInto(target, source, owner, path3, missing) {
4309
4309
  function composeLocaleMessagesForPlan(layers) {
4310
4310
  const result = createMessageTree();
4311
4311
  const leafOwners = /* @__PURE__ */ new Map();
4312
- function mergeInto(target, source, owner, path3) {
4312
+ function mergeInto(target, source, owner, path4) {
4313
4313
  for (const [key, value] of Object.entries(source)) {
4314
- const nextPath = path3 ? `${path3}.${key}` : key;
4314
+ const nextPath = path4 ? `${path4}.${key}` : key;
4315
4315
  const hasExisting = Object.prototype.hasOwnProperty.call(target, key);
4316
4316
  const existing = hasExisting ? target[key] : void 0;
4317
4317
  if (typeof value === "string") {
@@ -4855,8 +4855,8 @@ var METHOD_ORDER = {
4855
4855
  function compareNames2(a, b) {
4856
4856
  return a < b ? -1 : a > b ? 1 : 0;
4857
4857
  }
4858
- function endpointPairKey(method, path3) {
4859
- return `${method} ${path3}`;
4858
+ function endpointPairKey(method, path4) {
4859
+ return `${method} ${path4}`;
4860
4860
  }
4861
4861
  function apiPathToRouteFile(apiPath) {
4862
4862
  if (!apiPath.startsWith("/api/")) {
@@ -4870,11 +4870,11 @@ function routeEntryToAppPageFile(nextRelativePath) {
4870
4870
  }
4871
4871
  var APP_ROUTE_FILE_PREFIX = `${APP_PACKAGE_DIR}/src/app/`;
4872
4872
  var API_ROUTE_FILE_SUFFIXES = ["/route.ts", "/route.tsx", "/route.jsx", "/route.js"];
4873
- function appRouteFileToNextRelativePath(path3) {
4874
- const pagePath = appPageFileToNextRelativePath(path3, APP_PACKAGE_DIR);
4873
+ function appRouteFileToNextRelativePath(path4) {
4874
+ const pagePath = appPageFileToNextRelativePath(path4, APP_PACKAGE_DIR);
4875
4875
  if (pagePath != null) return pagePath;
4876
- if (!path3.startsWith(APP_ROUTE_FILE_PREFIX)) return null;
4877
- const relative = path3.slice(APP_ROUTE_FILE_PREFIX.length);
4876
+ if (!path4.startsWith(APP_ROUTE_FILE_PREFIX)) return null;
4877
+ const relative = path4.slice(APP_ROUTE_FILE_PREFIX.length);
4878
4878
  if (!relative.startsWith("api/")) return null;
4879
4879
  const suffix = API_ROUTE_FILE_SUFFIXES.find((candidate) => relative.endsWith(candidate));
4880
4880
  if (!suffix) return null;
@@ -4966,9 +4966,9 @@ function legacyHandlerPropertyMatchesPath(property, method, physicalPath, bindin
4966
4966
  const expectedTokens = knownAliases[`${binding} ${method} ${physicalPath}`] ?? pathTokens;
4967
4967
  return handlerTokens.length === expectedTokens.length && handlerTokens.every((token, index) => token === expectedTokens[index]);
4968
4968
  }
4969
- function parseLegacyApiAdapter(path3, source) {
4970
- const physicalPath = appRouteFileToNextRelativePath(path3);
4971
- if (expectedMarkerKindForPath(path3) !== "endpoint" || physicalPath == null) return null;
4969
+ function parseLegacyApiAdapter(path4, source) {
4970
+ const physicalPath = appRouteFileToNextRelativePath(path4);
4971
+ if (expectedMarkerKindForPath(path4) !== "endpoint" || physicalPath == null) return null;
4972
4972
  const firstLine = source.split(/\r?\n/, 1)[0]?.trim() ?? "";
4973
4973
  const match = firstLine.match(
4974
4974
  new RegExp(
@@ -4978,7 +4978,7 @@ function parseLegacyApiAdapter(path3, source) {
4978
4978
  if (!match || match[2] !== physicalPath) return null;
4979
4979
  const owner = match[1];
4980
4980
  if (!isValidModuleName(owner)) return null;
4981
- const file = ts4.createSourceFile(path3, source, ts4.ScriptTarget.Latest, false, ts4.ScriptKind.TS);
4981
+ const file = ts4.createSourceFile(path4, source, ts4.ScriptTarget.Latest, false, ts4.ScriptKind.TS);
4982
4982
  if (file.parseDiagnostics.length > 0) {
4983
4983
  return null;
4984
4984
  }
@@ -5020,9 +5020,9 @@ function parseLegacyApiAdapter(path3, source) {
5020
5020
  }
5021
5021
  return handlerBinding != null && methods.size > 0 ? { owner, methods: [...methods] } : null;
5022
5022
  }
5023
- function parseLegacyRouteAdapter(path3, source) {
5024
- const physicalPath = appRouteFileToNextRelativePath(path3);
5025
- if (expectedMarkerKindForPath(path3) !== "route" || physicalPath == null) return null;
5023
+ function parseLegacyRouteAdapter(path4, source) {
5024
+ const physicalPath = appRouteFileToNextRelativePath(path4);
5025
+ if (expectedMarkerKindForPath(path4) !== "route" || physicalPath == null) return null;
5026
5026
  const normalized = source.replace(/\r\n/g, "\n");
5027
5027
  const match = normalized.match(
5028
5028
  new RegExp(
@@ -5034,30 +5034,30 @@ function parseLegacyRouteAdapter(path3, source) {
5034
5034
  const nextRelativePath = match[2];
5035
5035
  return isValidModuleName(owner) && nextRelativePath === physicalPath ? { owner, nextRelativePath } : null;
5036
5036
  }
5037
- function legacyRouteAdapterMatchesStub(path3, source, stub) {
5038
- const legacy = parseLegacyRouteAdapter(path3, source);
5037
+ function legacyRouteAdapterMatchesStub(path4, source, stub) {
5038
+ const legacy = parseLegacyRouteAdapter(path4, source);
5039
5039
  return legacy != null && legacy.owner === stub.owner;
5040
5040
  }
5041
5041
  var API_ROUTE_DIR_PREFIX = `${APP_PACKAGE_DIR}/src/app/api/`;
5042
5042
  var APP_DIR_PREFIX = `${APP_PACKAGE_DIR}/src/app/`;
5043
5043
  var PAGE_FILE_SUFFIXES = ["/page.tsx", "/page.ts", "/page.jsx", "/page.js"];
5044
- function expectedMarkerKindForPath(path3) {
5045
- if (path3 === MODULES_GEN_PATH) return "registry";
5046
- if (path3 === MODULE_CONTRIBUTIONS_GEN_PATH) return "contributions";
5047
- if (path3 === MODULE_DATASTORES_GEN_PATH) return "datastores";
5048
- if (path3 === MODULE_I18N_GEN_PATH) return "i18n";
5049
- if (path3 === MODULE_INIT_SERVER_GEN_PATH) return "init";
5050
- if (path3 === MODULE_SERVER_GEN_PATH) return "server";
5051
- if (path3.startsWith(API_ROUTE_DIR_PREFIX) && path3.endsWith("/route.ts")) return "endpoint";
5052
- if (path3.startsWith(APP_DIR_PREFIX) && !path3.startsWith(API_ROUTE_DIR_PREFIX) && PAGE_FILE_SUFFIXES.some((suffix) => path3.endsWith(suffix))) {
5044
+ function expectedMarkerKindForPath(path4) {
5045
+ if (path4 === MODULES_GEN_PATH) return "registry";
5046
+ if (path4 === MODULE_CONTRIBUTIONS_GEN_PATH) return "contributions";
5047
+ if (path4 === MODULE_DATASTORES_GEN_PATH) return "datastores";
5048
+ if (path4 === MODULE_I18N_GEN_PATH) return "i18n";
5049
+ if (path4 === MODULE_INIT_SERVER_GEN_PATH) return "init";
5050
+ if (path4 === MODULE_SERVER_GEN_PATH) return "server";
5051
+ if (path4.startsWith(API_ROUTE_DIR_PREFIX) && path4.endsWith("/route.ts")) return "endpoint";
5052
+ if (path4.startsWith(APP_DIR_PREFIX) && !path4.startsWith(API_ROUTE_DIR_PREFIX) && PAGE_FILE_SUFFIXES.some((suffix) => path4.endsWith(suffix))) {
5053
5053
  return "route";
5054
5054
  }
5055
5055
  return null;
5056
5056
  }
5057
- function markerCongruentWithPath(path3, marker) {
5058
- if (expectedMarkerKindForPath(path3) !== marker.kind) return false;
5057
+ function markerCongruentWithPath(path4, marker) {
5058
+ if (expectedMarkerKindForPath(path4) !== marker.kind) return false;
5059
5059
  if (marker.kind === "route") {
5060
- const derived = appPageFileToNextRelativePath(path3, APP_PACKAGE_DIR);
5060
+ const derived = appPageFileToNextRelativePath(path4, APP_PACKAGE_DIR);
5061
5061
  return derived != null && derived === marker.nextRelativePath;
5062
5062
  }
5063
5063
  return true;
@@ -5328,8 +5328,8 @@ function buildGeneratedModuleRegistry(input) {
5328
5328
  function activeEnhancementPeers(registryModule) {
5329
5329
  return new Set(registryModule.enhancements.filter((e) => e.active).map((e) => e.peer));
5330
5330
  }
5331
- function endpointBucketKey(path3, owner, scope) {
5332
- return `${path3}\0${owner}\0${scopeLabel(scope)}`;
5331
+ function endpointBucketKey(path4, owner, scope) {
5332
+ return `${path4}\0${owner}\0${scopeLabel(scope)}`;
5333
5333
  }
5334
5334
  function planRouteStubs(input) {
5335
5335
  const endpointBuckets = /* @__PURE__ */ new Map();
@@ -5453,7 +5453,7 @@ function planRouteStubs(input) {
5453
5453
  for (const entry of entries) {
5454
5454
  const nextRelativePath = entry.nextRelativePath;
5455
5455
  const destination = normalizeAppRouterDestinationPattern(nextRelativePath);
5456
- const path3 = routeEntryToAppPageFile(nextRelativePath);
5456
+ const path4 = routeEntryToAppPageFile(nextRelativePath);
5457
5457
  const existing = routeDestinationOwners.get(destination);
5458
5458
  if (existing) {
5459
5459
  throw new Error(
@@ -5463,13 +5463,13 @@ function planRouteStubs(input) {
5463
5463
  routeDestinationOwners.set(destination, {
5464
5464
  owner: regMod.name,
5465
5465
  nextRelativePath,
5466
- path: path3
5466
+ path: path4
5467
5467
  });
5468
5468
  const importModule = routeEntryImport(regMod.name, nextRelativePath);
5469
5469
  const analyzed = analyzeRouteEntryPath(nextRelativePath);
5470
5470
  const mount = mounts.find((claim) => routeEntryCoveredByMountClaim(analyzed, claim));
5471
5471
  routeStubs.push({
5472
- path: path3,
5472
+ path: path4,
5473
5473
  owner: regMod.name,
5474
5474
  nextRelativePath,
5475
5475
  importModule,
@@ -5530,16 +5530,16 @@ function planCompositionArtifacts(input) {
5530
5530
  ...stubs.routeStubs.map((stub) => stub.path)
5531
5531
  ]);
5532
5532
  const existingGeneratedPathSet = new Set(input.existingGeneratedPaths);
5533
- const isProvenStaleDeletionCandidate = (path3, content) => {
5534
- if (path3 === MODULES_GEN_PATH) return false;
5535
- if (COMPOSITION_RUNTIME_GEN_PATHS.includes(path3)) return false;
5536
- if (desiredPaths.has(path3)) return false;
5537
- if (!existingGeneratedPathSet.has(path3)) return false;
5538
- const legacy = parseLegacyApiAdapter(path3, content);
5533
+ const isProvenStaleDeletionCandidate = (path4, content) => {
5534
+ if (path4 === MODULES_GEN_PATH) return false;
5535
+ if (COMPOSITION_RUNTIME_GEN_PATHS.includes(path4)) return false;
5536
+ if (desiredPaths.has(path4)) return false;
5537
+ if (!existingGeneratedPathSet.has(path4)) return false;
5538
+ const legacy = parseLegacyApiAdapter(path4, content);
5539
5539
  if (legacy) return true;
5540
- if (parseLegacyRouteAdapter(path3, content)) return true;
5540
+ if (parseLegacyRouteAdapter(path4, content)) return true;
5541
5541
  const marker = parseCompositionArtifactMarker(content);
5542
- return marker != null && markerCongruentWithPath(path3, marker);
5542
+ return marker != null && markerCongruentWithPath(path4, marker);
5543
5543
  };
5544
5544
  const plannedByDestination = /* @__PURE__ */ new Map();
5545
5545
  for (const stub of stubs.routeStubs) {
@@ -5693,13 +5693,13 @@ function planCompositionArtifacts(input) {
5693
5693
  for (const stub of stubs.routeStubs) desired.set(stub.path, stub.content);
5694
5694
  const endpointStubByPath = new Map(stubs.endpointStubs.map((stub) => [stub.path, stub]));
5695
5695
  const routeStubByPath = new Map(stubs.routeStubs.map((stub) => [stub.path, stub]));
5696
- for (const [path3] of desired) {
5697
- const existing = input.existingFiles[path3];
5696
+ for (const [path4] of desired) {
5697
+ const existing = input.existingFiles[path4];
5698
5698
  if (existing == null) continue;
5699
5699
  let marker = parseCompositionArtifactMarker(existing);
5700
- const plannedEndpoint = endpointStubByPath.get(path3);
5701
- const plannedRoute = routeStubByPath.get(path3);
5702
- const legacy = parseLegacyApiAdapter(path3, existing);
5700
+ const plannedEndpoint = endpointStubByPath.get(path4);
5701
+ const plannedRoute = routeStubByPath.get(path4);
5702
+ const legacy = parseLegacyApiAdapter(path4, existing);
5703
5703
  if (legacy && plannedEndpoint) {
5704
5704
  marker = {
5705
5705
  kind: "endpoint",
@@ -5707,66 +5707,66 @@ function planCompositionArtifacts(input) {
5707
5707
  scope: plannedEndpoint.scope
5708
5708
  };
5709
5709
  }
5710
- if (!marker && plannedRoute && legacyRouteAdapterMatchesStub(path3, existing, plannedRoute)) {
5710
+ if (!marker && plannedRoute && legacyRouteAdapterMatchesStub(path4, existing, plannedRoute)) {
5711
5711
  marker = {
5712
5712
  kind: "route",
5713
5713
  owner: plannedRoute.owner,
5714
5714
  nextRelativePath: plannedRoute.nextRelativePath
5715
5715
  };
5716
5716
  }
5717
- const isGenRuntimePath = COMPOSITION_RUNTIME_GEN_PATHS.includes(path3);
5717
+ const isGenRuntimePath = COMPOSITION_RUNTIME_GEN_PATHS.includes(path4);
5718
5718
  if (!marker) {
5719
5719
  return {
5720
5720
  ok: false,
5721
- error: path3 === MODULES_GEN_PATH ? `[ModuleRail] composition artifacts: refusing to overwrite unmanaged registry at ${path3}` : isGenRuntimePath ? `[ModuleRail] composition artifacts: refusing to overwrite unmanaged generated file at ${path3}` : `[ModuleRail] composition artifacts: refusing to overwrite unmanaged route at ${path3}`,
5722
- path: path3
5721
+ 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}`,
5722
+ path: path4
5723
5723
  };
5724
5724
  }
5725
- const expectedKind = expectedMarkerKindForPath(path3);
5725
+ const expectedKind = expectedMarkerKindForPath(path4);
5726
5726
  if (marker.kind !== expectedKind) {
5727
5727
  return {
5728
5728
  ok: false,
5729
- error: path3 === MODULES_GEN_PATH ? `[ModuleRail] composition artifacts: refusing to overwrite unmanaged registry at ${path3} (incongruent marker ownership: found ${marker.kind} marker)` : isGenRuntimePath ? `[ModuleRail] composition artifacts: refusing to overwrite unmanaged generated file at ${path3} (incongruent marker ownership: found ${marker.kind} marker)` : `[ModuleRail] composition artifacts: refusing to overwrite incongruent marker ownership at ${path3} (expected ${expectedKind ?? "no"} marker, found ${marker.kind} marker)`,
5730
- path: path3
5729
+ 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)`,
5730
+ path: path4
5731
5731
  };
5732
5732
  }
5733
5733
  if (marker.kind === "endpoint") {
5734
- const stub = endpointStubByPath.get(path3);
5734
+ const stub = endpointStubByPath.get(path4);
5735
5735
  const ownerMatches = stub != null && marker.owner === stub.owner;
5736
5736
  const scopeMatches = stub != null && scopeLabel(marker.scope) === scopeLabel(stub.scope);
5737
5737
  if (!ownerMatches || !scopeMatches) {
5738
5738
  return {
5739
5739
  ok: false,
5740
- error: `[ModuleRail] composition artifacts: refusing to overwrite incongruent marker ownership at ${path3} (existing owner=${marker.owner} scope=${scopeLabel(marker.scope)} vs planned owner=${stub?.owner ?? "?"} scope=${stub ? scopeLabel(stub.scope) : "?"})`,
5741
- path: path3,
5740
+ 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) : "?"})`,
5741
+ path: path4,
5742
5742
  owner: stub?.owner
5743
5743
  };
5744
5744
  }
5745
5745
  } else if (marker.kind === "route") {
5746
- const stub = routeStubByPath.get(path3);
5746
+ const stub = routeStubByPath.get(path4);
5747
5747
  const ownerMatches = stub != null && marker.owner === stub.owner;
5748
5748
  const pathMatches = stub != null && marker.nextRelativePath === stub.nextRelativePath;
5749
5749
  if (!ownerMatches || !pathMatches) {
5750
5750
  return {
5751
5751
  ok: false,
5752
- error: `[ModuleRail] composition artifacts: refusing to overwrite incongruent marker ownership at ${path3} (existing owner=${marker.owner} path=${marker.nextRelativePath} vs planned owner=${stub?.owner ?? "?"} path=${stub?.nextRelativePath ?? "?"})`,
5753
- path: path3,
5752
+ 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 ?? "?"})`,
5753
+ path: path4,
5754
5754
  owner: stub?.owner
5755
5755
  };
5756
5756
  }
5757
5757
  }
5758
5758
  }
5759
5759
  const deletes = [];
5760
- for (const path3 of input.existingGeneratedPaths) {
5761
- const existing = input.existingFiles[path3];
5760
+ for (const path4 of input.existingGeneratedPaths) {
5761
+ const existing = input.existingFiles[path4];
5762
5762
  if (existing == null) continue;
5763
- if (!isProvenStaleDeletionCandidate(path3, existing)) {
5763
+ if (!isProvenStaleDeletionCandidate(path4, existing)) {
5764
5764
  continue;
5765
5765
  }
5766
- deletes.push(path3);
5766
+ deletes.push(path4);
5767
5767
  }
5768
5768
  deletes.sort(compareNames2);
5769
- const writes = [...desired.entries()].filter(([path3, content]) => input.existingFiles[path3] !== content).map(([path3, content]) => ({ path: path3, content })).sort((a, b) => compareNames2(a.path, b.path));
5769
+ 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));
5770
5770
  return {
5771
5771
  ok: true,
5772
5772
  plan: {
@@ -5795,8 +5795,16 @@ async function checkoutParsesTypedStores(sandbox) {
5795
5795
  try {
5796
5796
  if (!await sandbox.fileExists(APP_PACKAGE_JSON_PATH)) return false;
5797
5797
  const pkg = JSON.parse(await sandbox.readFile(APP_PACKAGE_JSON_PATH));
5798
- const range = pkg.devDependencies?.[COMPOSE_PACKAGE] ?? pkg.dependencies?.[COMPOSE_PACKAGE];
5799
- if (!range) return false;
5798
+ return composeRangeParsesTypedStores(
5799
+ pkg.devDependencies?.[COMPOSE_PACKAGE] ?? pkg.dependencies?.[COMPOSE_PACKAGE]
5800
+ );
5801
+ } catch {
5802
+ return false;
5803
+ }
5804
+ }
5805
+ function composeRangeParsesTypedStores(range) {
5806
+ if (!range) return false;
5807
+ try {
5800
5808
  const lowest = import_semver.default.minVersion(range);
5801
5809
  return lowest != null && import_semver.default.gte(lowest, TYPED_STORES_MIN_COMPOSE_VERSION);
5802
5810
  } catch {
@@ -6241,11 +6249,11 @@ function countedCompositionContext(context) {
6241
6249
  counts.sandboxExecs += 1;
6242
6250
  return context.sandbox.exec(...args);
6243
6251
  },
6244
- readFile: (path3) => {
6252
+ readFile: (path4) => {
6245
6253
  counts.sandboxReads += 1;
6246
- return context.sandbox.readFile(path3);
6254
+ return context.sandbox.readFile(path4);
6247
6255
  },
6248
- fileExists: (path3) => context.sandbox.fileExists(path3),
6256
+ fileExists: (path4) => context.sandbox.fileExists(path4),
6249
6257
  fetchRemoteRef: (...args) => context.sandbox.fetchRemoteRef(...args),
6250
6258
  addDependencies: (specs) => context.sandbox.addDependencies(specs)
6251
6259
  }
@@ -6457,14 +6465,14 @@ async function discoverModuleRouteEntries(context, moduleName) {
6457
6465
  }
6458
6466
  return pages.sort((a, b) => a.nextRelativePath.localeCompare(b.nextRelativePath));
6459
6467
  }
6460
- async function writeSandboxFileAtomic(target, path3, content) {
6461
- const dir = dirnamePosix(path3);
6462
- const tmp = `${path3}.tmp.${target.runId}`;
6468
+ async function writeSandboxFileAtomic(target, path4, content) {
6469
+ const dir = dirnamePosix(path4);
6470
+ const tmp = `${path4}.tmp.${target.runId}`;
6463
6471
  const b64 = Buffer.from(content, "utf8").toString("base64");
6464
6472
  const cmd = [
6465
6473
  `mkdir -p ${shellQuote(dir)}`,
6466
6474
  `printf '%s' ${shellQuote(b64)} | base64 -d > ${shellQuote(tmp)}`,
6467
- `mv -f ${shellQuote(tmp)} ${shellQuote(path3)}`
6475
+ `mv -f ${shellQuote(tmp)} ${shellQuote(path4)}`
6468
6476
  ].join(" && ");
6469
6477
  const result = await target.sandbox.exec(cmd, { raiseOnError: false });
6470
6478
  if (result.exitCode !== 0) {
@@ -6479,14 +6487,14 @@ async function writeSandboxFileAtomic(target, path3, content) {
6479
6487
  }
6480
6488
  const detail = typeof result.error === "string" && result.error.trim() || result.output.trim() || `exit ${result.exitCode}`;
6481
6489
  throw new Error(
6482
- `[ModuleRail] composition artifacts: failed to write ${redactSecrets(path3)}: ${redactSecrets(detail)}`
6490
+ `[ModuleRail] composition artifacts: failed to write ${redactSecrets(path4)}: ${redactSecrets(detail)}`
6483
6491
  );
6484
6492
  }
6485
6493
  }
6486
6494
  async function collectAbsentWriteParentDirs(context, writePaths) {
6487
6495
  const absent = /* @__PURE__ */ new Set();
6488
- for (const path3 of writePaths) {
6489
- let dir = dirnamePosix(path3);
6496
+ for (const path4 of writePaths) {
6497
+ let dir = dirnamePosix(path4);
6490
6498
  while (dir !== "." && dir !== "") {
6491
6499
  if (absent.has(dir)) {
6492
6500
  dir = dirnamePosix(dir);
@@ -6523,12 +6531,12 @@ async function removeAbsentWriteParentDirsOnRollback(context, dirsDeepestFirst)
6523
6531
  );
6524
6532
  }
6525
6533
  }
6526
- async function deleteSandboxFile(context, path3) {
6527
- const result = await context.sandbox.exec(`rm -f ${shellQuote(path3)}`, { raiseOnError: false });
6534
+ async function deleteSandboxFile(context, path4) {
6535
+ const result = await context.sandbox.exec(`rm -f ${shellQuote(path4)}`, { raiseOnError: false });
6528
6536
  if (result.exitCode !== 0) {
6529
6537
  const detail = typeof result.error === "string" && result.error.trim() || result.output.trim() || `exit ${result.exitCode}`;
6530
6538
  throw new Error(
6531
- `[ModuleRail] composition artifacts: failed to delete ${redactSecrets(path3)}: ${redactSecrets(detail)}`
6539
+ `[ModuleRail] composition artifacts: failed to delete ${redactSecrets(path4)}: ${redactSecrets(detail)}`
6532
6540
  );
6533
6541
  }
6534
6542
  }
@@ -6595,37 +6603,37 @@ async function applyCompositionArtifactMutations(context, plan, existingFiles) {
6595
6603
  await writeSandboxFileAtomic(context, write.path, write.content);
6596
6604
  appliedWrites.push({ path: write.path, previous });
6597
6605
  }
6598
- for (const path3 of plan.deletes) {
6599
- const previous = existingFiles[path3];
6606
+ for (const path4 of plan.deletes) {
6607
+ const previous = existingFiles[path4];
6600
6608
  if (previous == null) continue;
6601
- await deleteSandboxFile(context, path3);
6602
- appliedDeletes.push({ path: path3, previous });
6609
+ await deleteSandboxFile(context, path4);
6610
+ appliedDeletes.push({ path: path4, previous });
6603
6611
  }
6604
6612
  } catch (error) {
6605
6613
  console.log(
6606
6614
  `[ModuleRail] runId=${context.runId} composition artifacts mid-apply failure; rolling back writes=${appliedWrites.length} deletes=${appliedDeletes.length} newDirs=${newlyCreatedParentDirs.length}`
6607
6615
  );
6608
- for (const { path: path3, previous } of [...appliedDeletes].reverse()) {
6616
+ for (const { path: path4, previous } of [...appliedDeletes].reverse()) {
6609
6617
  try {
6610
- await writeSandboxFileAtomic(context, path3, previous);
6618
+ await writeSandboxFileAtomic(context, path4, previous);
6611
6619
  } catch (rollbackError) {
6612
6620
  console.log(
6613
- `[ModuleRail] runId=${context.runId} composition artifacts rollback restore failed for ${redactSecrets(path3)}: ${redactSecrets(
6621
+ `[ModuleRail] runId=${context.runId} composition artifacts rollback restore failed for ${redactSecrets(path4)}: ${redactSecrets(
6614
6622
  rollbackError instanceof Error ? rollbackError.message : String(rollbackError)
6615
6623
  )}`
6616
6624
  );
6617
6625
  }
6618
6626
  }
6619
- for (const { path: path3, previous } of [...appliedWrites].reverse()) {
6627
+ for (const { path: path4, previous } of [...appliedWrites].reverse()) {
6620
6628
  try {
6621
6629
  if (previous == null) {
6622
- await deleteSandboxFile(context, path3);
6630
+ await deleteSandboxFile(context, path4);
6623
6631
  } else {
6624
- await writeSandboxFileAtomic(context, path3, previous);
6632
+ await writeSandboxFileAtomic(context, path4, previous);
6625
6633
  }
6626
6634
  } catch (rollbackError) {
6627
6635
  console.log(
6628
- `[ModuleRail] runId=${context.runId} composition artifacts rollback write failed for ${redactSecrets(path3)}: ${redactSecrets(
6636
+ `[ModuleRail] runId=${context.runId} composition artifacts rollback write failed for ${redactSecrets(path4)}: ${redactSecrets(
6629
6637
  rollbackError instanceof Error ? rollbackError.message : String(rollbackError)
6630
6638
  )}`
6631
6639
  );
@@ -6659,39 +6667,39 @@ async function listExistingGeneratedStubPaths(context) {
6659
6667
  COMPOSITION_ARTIFACT_KIND_PROBE_TOKEN
6660
6668
  );
6661
6669
  const presentPaths = [];
6662
- for (const path3 of candidates) {
6670
+ for (const path4 of candidates) {
6663
6671
  const presence = assertRealCompositionArtifactFilePresence(
6664
- path3,
6665
- kinds.get(path3) ?? "unreadable"
6672
+ path4,
6673
+ kinds.get(path4) ?? "unreadable"
6666
6674
  );
6667
- if (presence === "file") presentPaths.push(path3);
6675
+ if (presence === "file") presentPaths.push(path4);
6668
6676
  }
6669
6677
  let contents;
6670
6678
  try {
6671
6679
  contents = await readFilesMany(context, presentPaths);
6672
6680
  } catch (error) {
6673
- const path3 = readFailurePath(error);
6681
+ const path4 = readFailurePath(error);
6674
6682
  throw new Error(
6675
- `[ModuleRail] composition artifacts: cannot read candidate stub ${redactSecrets(path3)}: ${redactSecrets(
6683
+ `[ModuleRail] composition artifacts: cannot read candidate stub ${redactSecrets(path4)}: ${redactSecrets(
6676
6684
  error instanceof Error ? error.message : String(error)
6677
6685
  )}`
6678
6686
  );
6679
6687
  }
6680
6688
  const owned = [];
6681
- for (const path3 of presentPaths) {
6682
- const content = contents.get(path3);
6689
+ for (const path4 of presentPaths) {
6690
+ const content = contents.get(path4);
6683
6691
  const marker = parseCompositionArtifactMarker(content);
6684
- if (parseLegacyApiAdapter(path3, content) || parseLegacyRouteAdapter(path3, content)) {
6685
- owned.push(path3);
6692
+ if (parseLegacyApiAdapter(path4, content) || parseLegacyRouteAdapter(path4, content)) {
6693
+ owned.push(path4);
6686
6694
  continue;
6687
6695
  }
6688
6696
  if (marker == null) continue;
6689
- if (!markerCongruentWithPath(path3, marker)) {
6697
+ if (!markerCongruentWithPath(path4, marker)) {
6690
6698
  throw new Error(
6691
- `[ModuleRail] composition artifacts: incongruent marker at ${redactSecrets(path3)} (found ${marker.kind} marker on a path shaped for ${expectedMarkerKindForPath(path3) ?? "no"} artifacts; refusing to reconcile a swapped marker)`
6699
+ `[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)`
6692
6700
  );
6693
6701
  }
6694
- owned.push(path3);
6702
+ owned.push(path4);
6695
6703
  }
6696
6704
  return owned;
6697
6705
  }
@@ -6824,7 +6832,7 @@ async function classifySandboxPathPresenceKindMany(context, paths, probeToken) {
6824
6832
  { raiseOnError: false }
6825
6833
  );
6826
6834
  if (result.exitCode !== 0) {
6827
- for (const path3 of chunk) kinds.set(path3, "unreadable");
6835
+ for (const path4 of chunk) kinds.set(path4, "unreadable");
6828
6836
  continue;
6829
6837
  }
6830
6838
  const lines = result.output.split("\n").filter(Boolean);
@@ -6835,31 +6843,31 @@ async function classifySandboxPathPresenceKindMany(context, paths, probeToken) {
6835
6843
  }
6836
6844
  } else {
6837
6845
  for (const line of lines) {
6838
- const [rawKind, path3] = line.split(" ", 2);
6846
+ const [rawKind, path4] = line.split(" ", 2);
6839
6847
  const kind = rawKind?.replace(/^entry-kind:/, "");
6840
- if (path3 && chunk.includes(path3) && CONVENTIONAL_ENTRY_PRESENCE_KINDS.includes(kind ?? "")) {
6841
- kinds.set(path3, kind);
6848
+ if (path4 && chunk.includes(path4) && CONVENTIONAL_ENTRY_PRESENCE_KINDS.includes(kind ?? "")) {
6849
+ kinds.set(path4, kind);
6842
6850
  }
6843
6851
  }
6844
6852
  }
6845
- for (const path3 of chunk) {
6846
- if (!kinds.has(path3)) kinds.set(path3, "unreadable");
6853
+ for (const path4 of chunk) {
6854
+ if (!kinds.has(path4)) kinds.set(path4, "unreadable");
6847
6855
  }
6848
6856
  }
6849
6857
  return kinds;
6850
6858
  }
6851
- async function classifySandboxPathPresenceKind(context, path3, probeToken) {
6852
- return (await classifySandboxPathPresenceKindMany(context, [path3], probeToken)).get(path3);
6859
+ async function classifySandboxPathPresenceKind(context, path4, probeToken) {
6860
+ return (await classifySandboxPathPresenceKindMany(context, [path4], probeToken)).get(path4);
6853
6861
  }
6854
6862
  var LOCALE_JSON_KIND_PROBE_TOKEN = "locale-json-kind";
6855
- function assertRealLocaleJsonFilePresence(path3, presence) {
6863
+ function assertRealLocaleJsonFilePresence(path4, presence) {
6856
6864
  if (presence === "file") return;
6857
6865
  if (presence === "absent") {
6858
- throw new Error(`[ModuleRail] composition artifacts: locale file missing at ${path3}`);
6866
+ throw new Error(`[ModuleRail] composition artifacts: locale file missing at ${path4}`);
6859
6867
  }
6860
- const presenceError = formatConventionalEntryPresenceError(path3, presence);
6868
+ const presenceError = formatConventionalEntryPresenceError(path4, presence);
6861
6869
  throw new Error(
6862
- `[ModuleRail] composition artifacts: ${presenceError ?? `${path3} is not a real locale JSON file`}`
6870
+ `[ModuleRail] composition artifacts: ${presenceError ?? `${path4} is not a real locale JSON file`}`
6863
6871
  );
6864
6872
  }
6865
6873
  function splitTaggedLine(line) {
@@ -6934,11 +6942,11 @@ async function listLocaleBasenamesIfDirMany(context, dirs) {
6934
6942
  }
6935
6943
  async function readFilesMany(context, paths) {
6936
6944
  const sortedPaths = [...paths].sort((a, b) => a.localeCompare(b));
6937
- const settled = await mapConcurrent(sortedPaths, SANDBOX_READ_CONCURRENCY, async (path3) => {
6945
+ const settled = await mapConcurrent(sortedPaths, SANDBOX_READ_CONCURRENCY, async (path4) => {
6938
6946
  try {
6939
- return { path: path3, value: await context.sandbox.readFile(path3) };
6947
+ return { path: path4, value: await context.sandbox.readFile(path4) };
6940
6948
  } catch (error) {
6941
- return { path: path3, error };
6949
+ return { path: path4, error };
6942
6950
  }
6943
6951
  });
6944
6952
  const files = /* @__PURE__ */ new Map();
@@ -6961,58 +6969,58 @@ function readFailurePath(error) {
6961
6969
  var CONVENTIONAL_ENTRY_KIND_PROBE_TOKEN = "conventional-entry-kind";
6962
6970
  var SERVER_REGISTRATIONS_FILE = "server/registrations.ts";
6963
6971
  var COMPOSITION_ARTIFACT_KIND_PROBE_TOKEN = "composition-artifact-kind";
6964
- async function classifyCompositionArtifactPathKind(context, path3) {
6965
- return classifySandboxPathPresenceKind(context, path3, COMPOSITION_ARTIFACT_KIND_PROBE_TOKEN);
6972
+ async function classifyCompositionArtifactPathKind(context, path4) {
6973
+ return classifySandboxPathPresenceKind(context, path4, COMPOSITION_ARTIFACT_KIND_PROBE_TOKEN);
6966
6974
  }
6967
- function assertRealCompositionArtifactFilePresence(path3, presence) {
6975
+ function assertRealCompositionArtifactFilePresence(path4, presence) {
6968
6976
  if (presence === "file") return "file";
6969
6977
  if (presence === "absent") return "absent";
6970
- const presenceError = formatConventionalEntryPresenceError(path3, presence);
6978
+ const presenceError = formatConventionalEntryPresenceError(path4, presence);
6971
6979
  throw new Error(
6972
- `[ModuleRail] composition artifacts: ${presenceError ?? `${path3} is not a real composition artifact file`}`
6980
+ `[ModuleRail] composition artifacts: ${presenceError ?? `${path4} is not a real composition artifact file`}`
6973
6981
  );
6974
6982
  }
6975
- async function readConventionalEntryIfRealFile(path3, kind, presences, files) {
6976
- const presence = presences.get(path3) ?? "unreadable";
6977
- const presenceError = formatConventionalEntryPresenceError(path3, presence);
6983
+ async function readConventionalEntryIfRealFile(path4, kind, presences, files) {
6984
+ const presence = presences.get(path4) ?? "unreadable";
6985
+ const presenceError = formatConventionalEntryPresenceError(path4, presence);
6978
6986
  if (presenceError) {
6979
6987
  throw new Error(`[ModuleRail] composition artifacts: ${presenceError}`);
6980
6988
  }
6981
6989
  if (presence === "absent") {
6982
6990
  return null;
6983
6991
  }
6984
- const source = files.get(path3);
6992
+ const source = files.get(path4);
6985
6993
  switch (kind) {
6986
6994
  case "contributions":
6987
- assertContributionsExport(source, path3);
6988
- assertClientSafeConventionalEntryImports(source, path3, "contributions");
6995
+ assertContributionsExport(source, path4);
6996
+ assertClientSafeConventionalEntryImports(source, path4, "contributions");
6989
6997
  break;
6990
6998
  case "slotCatalogs":
6991
- assertSlotCatalogsExport(source, path3);
6992
- assertClientSafeConventionalEntryImports(source, path3, "slotCatalogs");
6999
+ assertSlotCatalogsExport(source, path4);
7000
+ assertClientSafeConventionalEntryImports(source, path4, "slotCatalogs");
6993
7001
  break;
6994
7002
  case "initializeEnhancement":
6995
- assertInitializeEnhancementExport(source, path3);
7003
+ assertInitializeEnhancementExport(source, path4);
6996
7004
  break;
6997
7005
  }
6998
7006
  return source;
6999
7007
  }
7000
7008
  function hasRealServerRegistrations(modulePath, presences) {
7001
- const path3 = `${modulePath}/${SERVER_REGISTRATIONS_FILE}`;
7002
- return assertRealCompositionArtifactFilePresence(path3, presences.get(path3) ?? "unreadable") === "file";
7009
+ const path4 = `${modulePath}/${SERVER_REGISTRATIONS_FILE}`;
7010
+ return assertRealCompositionArtifactFilePresence(path4, presences.get(path4) ?? "unreadable") === "file";
7003
7011
  }
7004
- async function readJsonObjectFile(path3, files) {
7005
- const raw = files.get(path3);
7012
+ async function readJsonObjectFile(path4, files) {
7013
+ const raw = files.get(path4);
7006
7014
  let parsed;
7007
7015
  try {
7008
7016
  parsed = JSON.parse(raw);
7009
7017
  } catch (error) {
7010
7018
  throw new Error(
7011
- `[ModuleRail] composition artifacts: invalid JSON at ${path3}: ${error instanceof Error ? error.message : String(error)}`
7019
+ `[ModuleRail] composition artifacts: invalid JSON at ${path4}: ${error instanceof Error ? error.message : String(error)}`
7012
7020
  );
7013
7021
  }
7014
7022
  if (parsed === null || Array.isArray(parsed) || typeof parsed !== "object") {
7015
- throw new Error(`[ModuleRail] composition artifacts: ${path3} root must be an object`);
7023
+ throw new Error(`[ModuleRail] composition artifacts: ${path4} root must be an object`);
7016
7024
  }
7017
7025
  return parsed;
7018
7026
  }
@@ -7061,11 +7069,11 @@ async function collectCompositionEntryFacts(context, registry) {
7061
7069
  classifySandboxPathPresenceKindMany(context, localePaths, LOCALE_JSON_KIND_PROBE_TOKEN)
7062
7070
  ]);
7063
7071
  const presentConventionalPaths = [];
7064
- for (const path3 of conventionalPaths) {
7065
- const presence = conventionalPresences.get(path3) ?? "unreadable";
7066
- if (presence === "file") presentConventionalPaths.push(path3);
7072
+ for (const path4 of conventionalPaths) {
7073
+ const presence = conventionalPresences.get(path4) ?? "unreadable";
7074
+ if (presence === "file") presentConventionalPaths.push(path4);
7067
7075
  }
7068
- const presentLocalePaths = localePaths.filter((path3) => localePresences.get(path3) === "file");
7076
+ const presentLocalePaths = localePaths.filter((path4) => localePresences.get(path4) === "file");
7069
7077
  const files = await readFilesMany(context, [...presentConventionalPaths, ...presentLocalePaths]);
7070
7078
  const substrateLocales = [];
7071
7079
  for (const locale of substrateNames) {
@@ -7163,8 +7171,8 @@ async function collectCompositionEntryFacts(context, registry) {
7163
7171
  });
7164
7172
  if (rootLocales.length > 0) {
7165
7173
  for (const locale of rootLocales) {
7166
- const path3 = `${modulePath}/i18n/${locale}.json`;
7167
- assertRealLocaleJsonFilePresence(path3, localePresences.get(path3) ?? "unreadable");
7174
+ const path4 = `${modulePath}/i18n/${locale}.json`;
7175
+ assertRealLocaleJsonFilePresence(path4, localePresences.get(path4) ?? "unreadable");
7168
7176
  }
7169
7177
  const enMessages = await readJsonObjectFile(`${modulePath}/i18n/en.json`, files);
7170
7178
  const localeMessages = { en: enMessages };
@@ -7188,8 +7196,8 @@ async function collectCompositionEntryFacts(context, registry) {
7188
7196
  if (!enh.active || enh.locales.length === 0) continue;
7189
7197
  const peerDir = `${modulePath}/enhancements/${enh.peer}`;
7190
7198
  for (const locale of enh.locales) {
7191
- const path3 = `${peerDir}/i18n/${locale}.json`;
7192
- assertRealLocaleJsonFilePresence(path3, localePresences.get(path3) ?? "unreadable");
7199
+ const path4 = `${peerDir}/i18n/${locale}.json`;
7200
+ assertRealLocaleJsonFilePresence(path4, localePresences.get(path4) ?? "unreadable");
7193
7201
  }
7194
7202
  const enMessages = await readJsonObjectFile(`${peerDir}/i18n/en.json`, files);
7195
7203
  const localeMessages = { en: enMessages };
@@ -7390,16 +7398,16 @@ async function reconcileCompositionArtifacts(context, options = {}) {
7390
7398
  for (const stub of probePlan.plan.routeStubs) candidatePaths.add(stub.path);
7391
7399
  const existingFiles = {};
7392
7400
  const remainingCandidatePaths = [];
7393
- for (const path3 of [...candidatePaths].sort((a, b) => a.localeCompare(b))) {
7394
- if (Object.prototype.hasOwnProperty.call(existingAppPages, path3)) {
7395
- existingFiles[path3] = existingAppPages[path3];
7401
+ for (const path4 of [...candidatePaths].sort((a, b) => a.localeCompare(b))) {
7402
+ if (Object.prototype.hasOwnProperty.call(existingAppPages, path4)) {
7403
+ existingFiles[path4] = existingAppPages[path4];
7396
7404
  continue;
7397
7405
  }
7398
- if (Object.prototype.hasOwnProperty.call(existingAppRoutes, path3)) {
7399
- existingFiles[path3] = existingAppRoutes[path3];
7406
+ if (Object.prototype.hasOwnProperty.call(existingAppRoutes, path4)) {
7407
+ existingFiles[path4] = existingAppRoutes[path4];
7400
7408
  continue;
7401
7409
  }
7402
- remainingCandidatePaths.push(path3);
7410
+ remainingCandidatePaths.push(path4);
7403
7411
  }
7404
7412
  const candidateKinds = await classifySandboxPathPresenceKindMany(
7405
7413
  context,
@@ -7407,12 +7415,12 @@ async function reconcileCompositionArtifacts(context, options = {}) {
7407
7415
  COMPOSITION_ARTIFACT_KIND_PROBE_TOKEN
7408
7416
  );
7409
7417
  const presentCandidatePaths = [];
7410
- for (const path3 of remainingCandidatePaths) {
7418
+ for (const path4 of remainingCandidatePaths) {
7411
7419
  const presence = assertRealCompositionArtifactFilePresence(
7412
- path3,
7413
- candidateKinds.get(path3) ?? "unreadable"
7420
+ path4,
7421
+ candidateKinds.get(path4) ?? "unreadable"
7414
7422
  );
7415
- if (presence === "file") presentCandidatePaths.push(path3);
7423
+ if (presence === "file") presentCandidatePaths.push(path4);
7416
7424
  }
7417
7425
  try {
7418
7426
  Object.assign(
@@ -7420,9 +7428,9 @@ async function reconcileCompositionArtifacts(context, options = {}) {
7420
7428
  Object.fromEntries(await readFilesMany(context, presentCandidatePaths))
7421
7429
  );
7422
7430
  } catch (error) {
7423
- const path3 = readFailurePath(error);
7431
+ const path4 = readFailurePath(error);
7424
7432
  throw new Error(
7425
- `[ModuleRail] composition artifacts: cannot read ${redactSecrets(path3)} during preflight: ${redactSecrets(
7433
+ `[ModuleRail] composition artifacts: cannot read ${redactSecrets(path4)} during preflight: ${redactSecrets(
7426
7434
  error instanceof Error ? error.message : String(error)
7427
7435
  )}`
7428
7436
  );
@@ -7491,14 +7499,139 @@ async function reconcileCompositionArtifacts(context, options = {}) {
7491
7499
  var HOME_I18N_SUBSTRATE_DIR = `${APP_PACKAGE_DIR}/src/lib/i18n/substrate`;
7492
7500
  var HOME_I18N_MESSAGES_DIR = `${APP_PACKAGE_DIR}/src/lib/i18n/messages`;
7493
7501
 
7502
+ // src/module-boundaries.ts
7503
+ import fs2 from "fs";
7504
+ import path2 from "path";
7505
+ import ts5 from "typescript";
7506
+ var SOURCE_FILE = /\.[cm]?[jt]sx?$/;
7507
+ var RESOLVE_SUFFIXES = ["", ".ts", ".tsx", ".js", ".jsx", "/index.ts", "/index.tsx", "/index.js"];
7508
+ var DATA_FILE = /\.(?:sql|json)$/;
7509
+ function dataPathsIn(fileName, source) {
7510
+ if (!/\.(?:sql|json)["'`]/.test(source)) return [];
7511
+ const paths = [];
7512
+ const visit = (node) => {
7513
+ if ((ts5.isStringLiteralLike(node) || ts5.isTemplateTail(node)) && DATA_FILE.test(node.text)) {
7514
+ paths.push(node.text);
7515
+ }
7516
+ ts5.forEachChild(node, visit);
7517
+ };
7518
+ visit(ts5.createSourceFile(fileName, source, ts5.ScriptTarget.Latest, false));
7519
+ return paths;
7520
+ }
7521
+ function scanModule(modulesDir, module) {
7522
+ const scanned = /* @__PURE__ */ new Map();
7523
+ const moduleDir = path2.join(modulesDir, module);
7524
+ if (!fs2.existsSync(moduleDir)) return scanned;
7525
+ const files = fs2.readdirSync(moduleDir, { recursive: true, encoding: "utf8" }).filter((relative) => SOURCE_FILE.test(relative) && !relative.includes("node_modules"));
7526
+ for (const relative of files) {
7527
+ const absolute = path2.join(moduleDir, relative);
7528
+ if (!fs2.statSync(absolute).isFile()) continue;
7529
+ const source = fs2.readFileSync(absolute, "utf8");
7530
+ const specifiers = ts5.preProcessFile(source, true, true).importedFiles.map((f) => f.fileName);
7531
+ const dataPaths = dataPathsIn(absolute, source);
7532
+ const targets = [...specifiers, ...dataPaths].flatMap((spec) => {
7533
+ if (spec.startsWith("@/modules/"))
7534
+ return [path2.join(modulesDir, spec.slice("@/modules/".length))];
7535
+ if (spec.startsWith("./") || spec.startsWith("../")) {
7536
+ return [path2.resolve(path2.dirname(absolute), spec)];
7537
+ }
7538
+ return [];
7539
+ });
7540
+ scanned.set(absolute, {
7541
+ module,
7542
+ relative: relative.split(path2.sep).join("/"),
7543
+ targets,
7544
+ dataPaths
7545
+ });
7546
+ }
7547
+ return scanned;
7548
+ }
7549
+ function findModuleBoundaryViolations(modulesDir, peersByModule, { pruning = false } = {}) {
7550
+ const files = /* @__PURE__ */ new Map();
7551
+ for (const [module, peers] of Object.entries(peersByModule)) {
7552
+ if (peers.length === 0) continue;
7553
+ for (const [absolute, file] of scanModule(modulesDir, module)) files.set(absolute, file);
7554
+ }
7555
+ const exempt = (file, peer) => (pruning ? peersByModule[file.module] ?? [] : [peer]).some(
7556
+ (q) => file.relative.startsWith(`enhancements/${q}/`)
7557
+ );
7558
+ const goneWith = (target, peer) => {
7559
+ const relative = path2.relative(modulesDir, target).split(path2.sep).join("/");
7560
+ return relative === peer || relative.startsWith(`${peer}/`) || new RegExp(`^[^/]+/enhancements/${peer}(/|$)`).test(relative);
7561
+ };
7562
+ const tainted = /* @__PURE__ */ new Map();
7563
+ const queue = [];
7564
+ const taint = (absolute, peer) => {
7565
+ const peers = tainted.get(absolute) ?? /* @__PURE__ */ new Set();
7566
+ if (peers.has(peer)) return;
7567
+ peers.add(peer);
7568
+ tainted.set(absolute, peers);
7569
+ queue.push([absolute, peer]);
7570
+ };
7571
+ const importers = /* @__PURE__ */ new Map();
7572
+ for (const [absolute, file] of files) {
7573
+ for (const target of file.targets) {
7574
+ const hit = RESOLVE_SUFFIXES.map((suffix) => target + suffix).find((c) => files.has(c));
7575
+ if (hit) importers.set(hit, [...importers.get(hit) ?? [], absolute]);
7576
+ }
7577
+ for (const peer of peersByModule[file.module] ?? []) {
7578
+ if (exempt(file, peer)) continue;
7579
+ if (file.targets.some((target) => goneWith(target, peer)) || file.dataPaths.some(
7580
+ (p) => p.startsWith(`modules/${peer}/`) || p.includes(`/modules/${peer}/`)
7581
+ )) {
7582
+ taint(absolute, peer);
7583
+ }
7584
+ }
7585
+ }
7586
+ for (let next = queue.shift(); next; next = queue.shift()) {
7587
+ const [target, peer] = next;
7588
+ for (const importer of importers.get(target) ?? []) {
7589
+ const file = files.get(importer);
7590
+ if ((peersByModule[file.module] ?? []).includes(peer) && !exempt(file, peer)) {
7591
+ taint(importer, peer);
7592
+ }
7593
+ }
7594
+ }
7595
+ const violations = [];
7596
+ for (const [absolute, peers] of tainted) {
7597
+ const file = files.get(absolute);
7598
+ for (const peer of peers) {
7599
+ violations.push({ file: `${file.module}/${file.relative}`, module: file.module, peer });
7600
+ }
7601
+ }
7602
+ return violations.sort((a, b) => a.file.localeCompare(b.file) || a.peer.localeCompare(b.peer));
7603
+ }
7604
+ function findOptionalPeerViolations(modulesDir) {
7605
+ if (!fs2.existsSync(modulesDir)) return [];
7606
+ const manifests = {};
7607
+ for (const name of fs2.readdirSync(modulesDir)) {
7608
+ const manifestPath = path2.join(modulesDir, name, "module.json");
7609
+ if (!fs2.existsSync(manifestPath)) continue;
7610
+ manifests[name] = JSON.parse(fs2.readFileSync(manifestPath, "utf8"));
7611
+ }
7612
+ const features = Object.keys(manifests).filter((name) => manifests[name].kind === "feature");
7613
+ const peersByModule = {};
7614
+ for (const [name, manifest] of Object.entries(manifests)) {
7615
+ const uses = new Set(manifest.uses ?? []);
7616
+ peersByModule[name] = features.filter((peer) => peer !== name && !uses.has(peer));
7617
+ }
7618
+ return findModuleBoundaryViolations(modulesDir, peersByModule);
7619
+ }
7620
+ function formatBoundaryViolations(modulesDirLabel, violations) {
7621
+ const files = new Set(violations.map((v) => v.file)).size;
7622
+ return `${files} Module file(s) import a peer Module that may be absent (not in the Module's \`uses\`, or excluded by --enabled).
7623
+ \`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).
7624
+ ` + violations.map((v) => `${modulesDirLabel}/${v.file}: ${v.peer}`).join("\n");
7625
+ }
7626
+
7494
7627
  // src/index.ts
7495
7628
  var MODULES_DIR2 = MODULES_SANDBOX_DIR;
7496
7629
  function readComposeInputs(cwd) {
7497
- const file = path2.join(cwd, COMPOSE_INPUTS_PATH);
7498
- if (!fs2.existsSync(file)) return { connectedStores: [], installs: {} };
7630
+ const file = path3.join(cwd, COMPOSE_INPUTS_PATH);
7631
+ if (!fs3.existsSync(file)) return { connectedStores: [], installs: {} };
7499
7632
  let raw;
7500
7633
  try {
7501
- raw = JSON.parse(fs2.readFileSync(file, "utf8"));
7634
+ raw = JSON.parse(fs3.readFileSync(file, "utf8"));
7502
7635
  } catch (error) {
7503
7636
  throw new Error(
7504
7637
  `[compose] ${file} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`
@@ -7540,18 +7673,18 @@ function stubGitignoreEntries(artifactPaths) {
7540
7673
  function writeStubGitignore(cwd, artifactPaths) {
7541
7674
  const entries = stubGitignoreEntries(artifactPaths);
7542
7675
  const content = [STUB_GITIGNORE_HEADER, ...entries].join("\n") + "\n";
7543
- const file = path2.join(cwd, STUB_GITIGNORE_PATH);
7544
- const existing = fs2.existsSync(file) ? fs2.readFileSync(file, "utf8") : null;
7676
+ const file = path3.join(cwd, STUB_GITIGNORE_PATH);
7677
+ const existing = fs3.existsSync(file) ? fs3.readFileSync(file, "utf8") : null;
7545
7678
  if (existing !== content) {
7546
- fs2.mkdirSync(path2.dirname(file), { recursive: true });
7547
- fs2.writeFileSync(file, content);
7679
+ fs3.mkdirSync(path3.dirname(file), { recursive: true });
7680
+ fs3.writeFileSync(file, content);
7548
7681
  }
7549
7682
  return entries.length;
7550
7683
  }
7551
7684
  function assertStubGitignoreOwned(cwd) {
7552
- const file = path2.join(cwd, STUB_GITIGNORE_PATH);
7553
- if (!fs2.existsSync(file)) return;
7554
- const firstLine = fs2.readFileSync(file, "utf8").split("\n", 1)[0] ?? "";
7685
+ const file = path3.join(cwd, STUB_GITIGNORE_PATH);
7686
+ if (!fs3.existsSync(file)) return;
7687
+ const firstLine = fs3.readFileSync(file, "utf8").split("\n", 1)[0] ?? "";
7555
7688
  if (firstLine !== STUB_GITIGNORE_HEADER) {
7556
7689
  throw new Error(
7557
7690
  `[compose] refusing to overwrite ${STUB_GITIGNORE_PATH}: it is not compose-generated (line 1 is not the generated header)`
@@ -7559,8 +7692,8 @@ function assertStubGitignoreOwned(cwd) {
7559
7692
  }
7560
7693
  }
7561
7694
  function assertAppRoot(cwd) {
7562
- const appPath = path2.join(cwd, APP_PACKAGE_JSON);
7563
- if (!fs2.existsSync(appPath)) {
7695
+ const appPath = path3.join(cwd, APP_PACKAGE_JSON);
7696
+ if (!fs3.existsSync(appPath)) {
7564
7697
  throw new Error(
7565
7698
  `[compose] ${appPath} not found \u2014 run this from the repo root of a Stardeck app`
7566
7699
  );
@@ -7606,9 +7739,9 @@ ${porcelain.trim()}`
7606
7739
  }
7607
7740
  var APP_SRC_DIR = "apps/web/src";
7608
7741
  function walkSources(dir, skip, out) {
7609
- if (!fs2.existsSync(dir)) return;
7610
- for (const entry of fs2.readdirSync(dir, { withFileTypes: true })) {
7611
- const full = path2.join(dir, entry.name);
7742
+ if (!fs3.existsSync(dir)) return;
7743
+ for (const entry of fs3.readdirSync(dir, { withFileTypes: true })) {
7744
+ const full = path3.join(dir, entry.name);
7612
7745
  if (full === skip) continue;
7613
7746
  if (entry.isDirectory()) walkSources(full, skip, out);
7614
7747
  else if (/\.[cm]?[jt]sx?$/.test(entry.name)) out.push(full);
@@ -7617,13 +7750,13 @@ function walkSources(dir, skip, out) {
7617
7750
  function assertNoAppLayerImporters(cwd, excluded) {
7618
7751
  if (excluded.length === 0) return;
7619
7752
  const files = [];
7620
- walkSources(path2.join(cwd, APP_SRC_DIR), path2.join(cwd, MODULES_DIR2), files);
7753
+ walkSources(path3.join(cwd, APP_SRC_DIR), path3.join(cwd, MODULES_DIR2), files);
7621
7754
  const offenders = [];
7622
7755
  for (const file of files) {
7623
- const source = fs2.readFileSync(file, "utf8");
7756
+ const source = fs3.readFileSync(file, "utf8");
7624
7757
  for (const name of excluded) {
7625
7758
  if (new RegExp(`@/modules/${name}(?=["'\`/])`).test(source)) {
7626
- offenders.push(`${path2.relative(cwd, file)}: ${name}`);
7759
+ offenders.push(`${path3.relative(cwd, file)}: ${name}`);
7627
7760
  }
7628
7761
  }
7629
7762
  }
@@ -7636,8 +7769,8 @@ function assertNoAppLayerImporters(cwd, excluded) {
7636
7769
  }
7637
7770
  }
7638
7771
  function removeDir(absolute) {
7639
- if (!fs2.existsSync(absolute)) return false;
7640
- fs2.rmSync(absolute, { recursive: true, force: true });
7772
+ if (!fs3.existsSync(absolute)) return false;
7773
+ fs3.rmSync(absolute, { recursive: true, force: true });
7641
7774
  return true;
7642
7775
  }
7643
7776
  async function compose(options) {
@@ -7664,6 +7797,16 @@ async function compose(options) {
7664
7797
  dryRun: true
7665
7798
  });
7666
7799
  assertNoAppLayerImporters(options.cwd, preview.excludedModules);
7800
+ const violations = findModuleBoundaryViolations(
7801
+ path3.join(options.cwd, MODULES_DIR2),
7802
+ Object.fromEntries(preview.composedModules.map((name) => [name, preview.excludedModules])),
7803
+ { pruning: true }
7804
+ );
7805
+ if (violations.length > 0) {
7806
+ throw new Error(
7807
+ `[compose] refusing --prune: ${formatBoundaryViolations(MODULES_DIR2, violations)}`
7808
+ );
7809
+ }
7667
7810
  }
7668
7811
  const result = await reconcileCompositionArtifacts(context, {
7669
7812
  enabledModulesRaw: options.enabled,
@@ -7675,11 +7818,11 @@ async function compose(options) {
7675
7818
  let pruned = 0;
7676
7819
  if (options.prune) {
7677
7820
  for (const name of result.excludedModules) {
7678
- if (removeDir(path2.join(options.cwd, MODULES_DIR2, name))) pruned++;
7821
+ if (removeDir(path3.join(options.cwd, MODULES_DIR2, name))) pruned++;
7679
7822
  }
7680
7823
  for (const owner of result.composedModules) {
7681
7824
  for (const peer of result.excludedModules) {
7682
- if (removeDir(path2.join(options.cwd, MODULES_DIR2, owner, "enhancements", peer))) pruned++;
7825
+ if (removeDir(path3.join(options.cwd, MODULES_DIR2, owner, "enhancements", peer))) pruned++;
7683
7826
  }
7684
7827
  }
7685
7828
  }
@@ -7697,5 +7840,8 @@ export {
7697
7840
  assertSupportedTypescript,
7698
7841
  compose,
7699
7842
  escapeGitignore,
7843
+ findModuleBoundaryViolations,
7844
+ findOptionalPeerViolations,
7845
+ formatBoundaryViolations,
7700
7846
  readComposeInputs
7701
7847
  };