@stardeck-customer-apps/compose 0.14.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1143,16 +1143,16 @@ var require_range = __commonJS({
1143
1143
  };
1144
1144
  var replaceTilde = (comp, options) => {
1145
1145
  const r = options.loose ? re[t.TILDELOOSE] : re[t.TILDE];
1146
- const z10 = options.includePrerelease ? "-0" : "";
1146
+ const z11 = options.includePrerelease ? "-0" : "";
1147
1147
  return comp.replace(r, (_, M, m, p, pr) => {
1148
1148
  debug("tilde", comp, _, M, m, p, pr);
1149
1149
  let ret;
1150
1150
  if (isX(M)) {
1151
1151
  ret = "";
1152
1152
  } else if (isX(m)) {
1153
- ret = `>=${M}.0.0${z10} <${+M + 1}.0.0-0`;
1153
+ ret = `>=${M}.0.0${z11} <${+M + 1}.0.0-0`;
1154
1154
  } else if (isX(p)) {
1155
- ret = `>=${M}.${m}.0${z10} <${M}.${+m + 1}.0-0`;
1155
+ ret = `>=${M}.${m}.0${z11} <${M}.${+m + 1}.0-0`;
1156
1156
  } else if (pr) {
1157
1157
  debug("replaceTilde pr", pr);
1158
1158
  ret = `>=${M}.${m}.${p}-${pr} <${M}.${+m + 1}.0-0`;
@@ -1169,19 +1169,19 @@ var require_range = __commonJS({
1169
1169
  var replaceCaret = (comp, options) => {
1170
1170
  debug("caret", comp, options);
1171
1171
  const r = options.loose ? re[t.CARETLOOSE] : re[t.CARET];
1172
- const z10 = options.includePrerelease ? "-0" : "";
1172
+ const z11 = options.includePrerelease ? "-0" : "";
1173
1173
  return comp.replace(r, (_, M, m, p, pr) => {
1174
1174
  debug("caret", comp, _, M, m, p, pr);
1175
1175
  let ret;
1176
1176
  if (isX(M)) {
1177
1177
  ret = "";
1178
1178
  } else if (isX(m)) {
1179
- ret = `>=${M}.0.0${z10} <${+M + 1}.0.0-0`;
1179
+ ret = `>=${M}.0.0${z11} <${+M + 1}.0.0-0`;
1180
1180
  } else if (isX(p)) {
1181
1181
  if (M === "0") {
1182
- ret = `>=${M}.${m}.0${z10} <${M}.${+m + 1}.0-0`;
1182
+ ret = `>=${M}.${m}.0${z11} <${M}.${+m + 1}.0-0`;
1183
1183
  } else {
1184
- ret = `>=${M}.${m}.0${z10} <${+M + 1}.0.0-0`;
1184
+ ret = `>=${M}.${m}.0${z11} <${+M + 1}.0.0-0`;
1185
1185
  }
1186
1186
  } else if (pr) {
1187
1187
  debug("replaceCaret pr", pr);
@@ -2078,6 +2078,7 @@ var JSON_SECRET_PAIR = new RegExp(
2078
2078
  "g"
2079
2079
  );
2080
2080
  var SECRET_QUOTE_OPENER = new RegExp(String.raw`\b${SECRET_NAME}\s*=\s*(['"])`, "g");
2081
+ var SHELL_QUOTE_CONTINUATION = String.raw`\''`;
2081
2082
  function redactSecrets(text) {
2082
2083
  return text.replace(/x-access-token:[^@\s]+@/g, "x-access-token:***@").replace(/https:\/\/(?!x-access-token:)[^/\s:@]+:[^/\s@]+@/gi, "https://***:***@").replace(/\b(gh[pousr]_)[A-Za-z0-9]+/g, "$1***").replace(/\b(github_pat_)[A-Za-z0-9_]+/g, "$1***").replace(/\b(postgres(?:ql)?:\/\/)([^:\s/@]+):([^@\s/]+)@/gi, "$1$2:***@").replace(/\b(npm_[A-Za-z0-9]+)/g, "npm_***").replace(/(_authToken=)([^\s&]+)/gi, "$1***").replace(SECRET_ASSIGNMENT, "$1=***").replace(JSON_SECRET_PAIR, '"$1"$2"***"').replace(/\b(Authorization:\s*(?:Bearer|Basic)\s+)\S+/gi, "$1***");
2083
2084
  }
@@ -2369,10 +2370,12 @@ var modulePermissionClaimSchema = z2.object({
2369
2370
  /** Human label for the deployment auth config row. */
2370
2371
  description: descriptionSchema,
2371
2372
  /**
2372
- * The default organization roles that hold this key when the platform
2373
- * first registers it on an app (a Blueprint fork, a Module install). Admin
2374
- * holds every key without being listed; External Viewer never holds one.
2375
- * Changing a role's grants afterwards is the organization's business.
2373
+ * The standard organization roles that hold this key. The platform sets them on every app
2374
+ * (a Blueprint fork, a Module install or uninstall, every Blueprint update), so this list is
2375
+ * the truth: a role added here gains the key, a role removed loses it, and nobody can edit a
2376
+ * standard role's grants on a Module key. Admin holds every key without being listed;
2377
+ * External Viewer never holds one. An organization that wants different access duplicates the
2378
+ * standard role and edits the copy.
2376
2379
  */
2377
2380
  roles: z2.array(z2.enum(MODULE_PERMISSION_DEFAULT_ROLES)).optional()
2378
2381
  }).strict();
@@ -4014,6 +4017,24 @@ function analyzeClientSafeEntryImports(sourceText, fileName) {
4014
4017
  return analyzeClientSafeEntryImportsWithTs(ts3, sourceText, fileName);
4015
4018
  }
4016
4019
 
4020
+ // ../../packages/lib/src/shared/ownership-record.ts
4021
+ import { z as z7 } from "zod";
4022
+ var OWNERSHIP_RECORD_VERSION = 1;
4023
+ var moduleList = z7.array(z7.string().refine(isValidModuleName, "not a Module name"));
4024
+ var ownershipRecordSchema = z7.object({
4025
+ version: z7.literal(OWNERSHIP_RECORD_VERSION),
4026
+ /** False once the app stopped all Stardeck updates: then every file is the app's. */
4027
+ stardeckUpdates: z7.boolean(),
4028
+ /** projects.ejected_modules: updates stopped for these Modules, so their folders are the app's. */
4029
+ updatesStoppedModules: moduleList,
4030
+ /** projects.ui_ejected_modules: Customize UI, so their screens are the app's. */
4031
+ customizedUiModules: moduleList,
4032
+ /** projects.external_modules: installed from another app, so the app owns the copy. */
4033
+ externalModules: moduleList,
4034
+ /** projects.removed_modules names: uninstalled on purpose. */
4035
+ uninstalledModules: moduleList
4036
+ });
4037
+
4017
4038
  // ../../packages/lib/src/shared/platform-owned-paths.ts
4018
4039
  var APP_WEB_PREFIX = "apps/web/";
4019
4040
  var BLUEPRINT_LINT_MARKER = `${APP_WEB_PREFIX}eslint-rules/module-boundaries.mjs`;
@@ -4818,6 +4839,9 @@ function enhancementImport(owner, peer) {
4818
4839
  function scopeLabel(scope) {
4819
4840
  return scope.kind === "root" ? "root" : `enhancement:${scope.peer}`;
4820
4841
  }
4842
+ function endpointMarkerOwnsStub(marker, stub) {
4843
+ return stub != null && marker?.kind === "endpoint" && marker.owner === stub.owner;
4844
+ }
4821
4845
  function formatEndpointMarker(owner, scope) {
4822
4846
  return `// ${MODULE_STUB_MARKER} owner=${owner} scope=${scopeLabel(scope)}`;
4823
4847
  }
@@ -5519,7 +5543,7 @@ function planCompositionArtifacts(input) {
5519
5543
  const legacy = parseLegacyApiAdapter(pagePath, content);
5520
5544
  const plannedRoute = plannedRouteByPath.get(pagePath);
5521
5545
  const plannedEndpoint = plannedEndpointByPath.get(pagePath);
5522
- const isExactPlannedMatch = plannedRoute != null && marker != null && marker.kind === "route" && marker.owner === plannedRoute.owner && marker.nextRelativePath === plannedRoute.nextRelativePath || plannedEndpoint != null && (marker != null && marker.kind === "endpoint" && marker.owner === plannedEndpoint.owner && scopeLabel(marker.scope) === scopeLabel(plannedEndpoint.scope) || legacy != null) || plannedRoute != null && legacyRouteAdapterMatchesStub(pagePath, content, plannedRoute);
5546
+ const isExactPlannedMatch = plannedRoute != null && marker != null && marker.kind === "route" && marker.owner === plannedRoute.owner && marker.nextRelativePath === plannedRoute.nextRelativePath || plannedEndpoint != null && (endpointMarkerOwnsStub(marker, plannedEndpoint) || legacy != null) || plannedRoute != null && legacyRouteAdapterMatchesStub(pagePath, content, plannedRoute);
5523
5547
  if (isExactPlannedMatch) {
5524
5548
  continue;
5525
5549
  }
@@ -5662,9 +5686,7 @@ function planCompositionArtifacts(input) {
5662
5686
  }
5663
5687
  if (marker.kind === "endpoint") {
5664
5688
  const stub = endpointStubByPath.get(path4);
5665
- const ownerMatches = stub != null && marker.owner === stub.owner;
5666
- const scopeMatches = stub != null && scopeLabel(marker.scope) === scopeLabel(stub.scope);
5667
- if (!ownerMatches || !scopeMatches) {
5689
+ if (!endpointMarkerOwnsStub(marker, stub)) {
5668
5690
  return {
5669
5691
  ok: false,
5670
5692
  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) : "?"})`,
@@ -5967,14 +5989,14 @@ function rejectPseudoModuleEntries(entries, parentDir, label) {
5967
5989
  }
5968
5990
 
5969
5991
  // ../../packages/lib/src/shared/module-composition.ts
5970
- import { z as z7 } from "zod";
5971
- var creationSourceBlankSchema = z7.object({
5972
- type: z7.literal("blank")
5992
+ import { z as z8 } from "zod";
5993
+ var creationSourceBlankSchema = z8.object({
5994
+ type: z8.literal("blank")
5973
5995
  });
5974
- var creationSourceBlueprintSchema = z7.object({
5975
- type: z7.literal("blueprint"),
5996
+ var creationSourceBlueprintSchema = z8.object({
5997
+ type: z8.literal("blueprint"),
5976
5998
  /** Internal template/fork project id — keep template naming in identifiers. */
5977
- blueprintProjectId: z7.string().uuid(),
5999
+ blueprintProjectId: z8.string().uuid(),
5978
6000
  /**
5979
6001
  * `null`/absent means every feature Module: the platform stores that as the
5980
6002
  * explicit list of feature Modules in the published Blueprint version the fork
@@ -5982,192 +6004,197 @@ var creationSourceBlueprintSchema = z7.object({
5982
6004
  * a custom app: no feature Module, Library Modules only (`resolvePlatformEnv`
5983
6005
  * emits it as a set value naming no Module).
5984
6006
  */
5985
- enabledModules: z7.array(moduleNameSchema).max(100).nullable().optional()
6007
+ enabledModules: z8.array(moduleNameSchema).max(100).nullable().optional(),
6008
+ /**
6009
+ * The kind of business (a preset id the published version defines). Its Modules are built
6010
+ * and `enabledModules` adds extras on top; for the general kind `enabledModules` is the pick.
6011
+ */
6012
+ businessKind: z8.string().regex(/^[a-z][a-z0-9-]{0,39}$/).optional()
5986
6013
  });
5987
- var creationSourceSchema = z7.discriminatedUnion("type", [
6014
+ var creationSourceSchema = z8.discriminatedUnion("type", [
5988
6015
  creationSourceBlankSchema,
5989
6016
  creationSourceBlueprintSchema
5990
6017
  ]);
5991
- var moduleInstallHandoffSchema = z7.object({
5992
- moduleName: z7.string().min(1),
5993
- homeProjectId: z7.string().uuid(),
5994
- installedVersion: z7.string().min(1),
5995
- baselineSha: z7.string().min(1),
5996
- mounts: z7.array(
5997
- z7.object({
5998
- group: z7.string(),
5999
- segment: z7.string()
6018
+ var moduleInstallHandoffSchema = z8.object({
6019
+ moduleName: z8.string().min(1),
6020
+ homeProjectId: z8.string().uuid(),
6021
+ installedVersion: z8.string().min(1),
6022
+ baselineSha: z8.string().min(1),
6023
+ mounts: z8.array(
6024
+ z8.object({
6025
+ group: z8.string(),
6026
+ segment: z8.string()
6000
6027
  })
6001
6028
  ),
6002
- integration: z7.array(z7.string()),
6003
- workflows: z7.array(z7.string()),
6004
- prunedEnhancements: z7.array(z7.string()),
6005
- restoredEnhancements: z7.array(z7.string()).optional(),
6006
- permissions: z7.array(
6007
- z7.object({
6008
- key: z7.string().min(1),
6009
- description: z7.string().min(1)
6029
+ integration: z8.array(z8.string()),
6030
+ workflows: z8.array(z8.string()),
6031
+ prunedEnhancements: z8.array(z8.string()),
6032
+ restoredEnhancements: z8.array(z8.string()).optional(),
6033
+ permissions: z8.array(
6034
+ z8.object({
6035
+ key: z8.string().min(1),
6036
+ description: z8.string().min(1)
6010
6037
  })
6011
6038
  ).optional()
6012
6039
  });
6013
6040
 
6014
6041
  // ../../packages/lib/src/shared/schema-ops.ts
6015
- import { z as z8 } from "zod";
6016
- var columnSpecSchema = z8.object({
6017
- name: z8.string().min(1).max(63),
6018
- pgType: z8.string().min(1),
6019
- nullable: z8.boolean(),
6042
+ import { z as z9 } from "zod";
6043
+ var columnSpecSchema = z9.object({
6044
+ name: z9.string().min(1).max(63),
6045
+ pgType: z9.string().min(1),
6046
+ nullable: z9.boolean(),
6020
6047
  /**
6021
6048
  * A SQL expression used as the column default, as a raw string (e.g. `'t'`,
6022
6049
  * `0`, `now()`, `gen_random_uuid()`). `null` or omitted means no default.
6023
6050
  * NOT a JS literal — must already be a valid SQL expression.
6024
6051
  */
6025
- default: z8.string().nullable().optional(),
6026
- fieldType: z8.string().optional(),
6027
- fieldConfig: z8.record(z8.string(), z8.unknown()).optional()
6052
+ default: z9.string().nullable().optional(),
6053
+ fieldType: z9.string().optional(),
6054
+ fieldConfig: z9.record(z9.string(), z9.unknown()).optional()
6028
6055
  });
6029
- var referentialActionSchema = z8.enum([
6056
+ var referentialActionSchema = z9.enum([
6030
6057
  "cascade",
6031
6058
  "set null",
6032
6059
  "set default",
6033
6060
  "restrict",
6034
6061
  "no action"
6035
6062
  ]);
6036
- var schemaOpSchema = z8.discriminatedUnion("type", [
6063
+ var schemaOpSchema = z9.discriminatedUnion("type", [
6037
6064
  // Tables
6038
- z8.object({
6039
- type: z8.literal("createTable"),
6040
- table: z8.string().min(1).max(63),
6041
- columns: z8.array(columnSpecSchema).min(1),
6042
- primaryKey: z8.array(z8.string()).optional(),
6043
- ifNotExists: z8.boolean().optional()
6065
+ z9.object({
6066
+ type: z9.literal("createTable"),
6067
+ table: z9.string().min(1).max(63),
6068
+ columns: z9.array(columnSpecSchema).min(1),
6069
+ primaryKey: z9.array(z9.string()).optional(),
6070
+ ifNotExists: z9.boolean().optional()
6044
6071
  }),
6045
- z8.object({
6046
- type: z8.literal("dropTable"),
6047
- table: z8.string().min(1).max(63),
6048
- cascade: z8.boolean().optional(),
6049
- ifExists: z8.boolean().optional()
6072
+ z9.object({
6073
+ type: z9.literal("dropTable"),
6074
+ table: z9.string().min(1).max(63),
6075
+ cascade: z9.boolean().optional(),
6076
+ ifExists: z9.boolean().optional()
6050
6077
  }),
6051
- z8.object({
6052
- type: z8.literal("renameTable"),
6053
- from: z8.string().min(1).max(63),
6054
- to: z8.string().min(1).max(63)
6078
+ z9.object({
6079
+ type: z9.literal("renameTable"),
6080
+ from: z9.string().min(1).max(63),
6081
+ to: z9.string().min(1).max(63)
6055
6082
  }),
6056
6083
  // Columns
6057
- z8.object({
6058
- type: z8.literal("addColumn"),
6059
- table: z8.string().min(1).max(63),
6084
+ z9.object({
6085
+ type: z9.literal("addColumn"),
6086
+ table: z9.string().min(1).max(63),
6060
6087
  column: columnSpecSchema,
6061
- ifNotExists: z8.boolean().optional()
6088
+ ifNotExists: z9.boolean().optional()
6062
6089
  }),
6063
- z8.object({
6064
- type: z8.literal("dropColumn"),
6065
- table: z8.string().min(1).max(63),
6066
- column: z8.string().min(1).max(63),
6067
- cascade: z8.boolean().optional(),
6068
- ifExists: z8.boolean().optional()
6090
+ z9.object({
6091
+ type: z9.literal("dropColumn"),
6092
+ table: z9.string().min(1).max(63),
6093
+ column: z9.string().min(1).max(63),
6094
+ cascade: z9.boolean().optional(),
6095
+ ifExists: z9.boolean().optional()
6069
6096
  }),
6070
- z8.object({
6071
- type: z8.literal("renameColumn"),
6072
- table: z8.string().min(1).max(63),
6073
- from: z8.string().min(1).max(63),
6074
- to: z8.string().min(1).max(63)
6097
+ z9.object({
6098
+ type: z9.literal("renameColumn"),
6099
+ table: z9.string().min(1).max(63),
6100
+ from: z9.string().min(1).max(63),
6101
+ to: z9.string().min(1).max(63)
6075
6102
  }),
6076
- z8.object({
6077
- type: z8.literal("alterColumnType"),
6078
- table: z8.string().min(1).max(63),
6079
- column: z8.string().min(1).max(63),
6080
- newType: z8.string().min(1),
6103
+ z9.object({
6104
+ type: z9.literal("alterColumnType"),
6105
+ table: z9.string().min(1).max(63),
6106
+ column: z9.string().min(1).max(63),
6107
+ newType: z9.string().min(1),
6081
6108
  /** Optional USING expression for potentially-lossy casts */
6082
- using: z8.string().optional()
6109
+ using: z9.string().optional()
6083
6110
  }),
6084
- z8.object({
6085
- type: z8.literal("alterColumnNullable"),
6086
- table: z8.string().min(1).max(63),
6087
- column: z8.string().min(1).max(63),
6088
- nullable: z8.boolean()
6111
+ z9.object({
6112
+ type: z9.literal("alterColumnNullable"),
6113
+ table: z9.string().min(1).max(63),
6114
+ column: z9.string().min(1).max(63),
6115
+ nullable: z9.boolean()
6089
6116
  }),
6090
- z8.object({
6091
- type: z8.literal("alterColumnDefault"),
6092
- table: z8.string().min(1).max(63),
6093
- column: z8.string().min(1).max(63),
6117
+ z9.object({
6118
+ type: z9.literal("alterColumnDefault"),
6119
+ table: z9.string().min(1).max(63),
6120
+ column: z9.string().min(1).max(63),
6094
6121
  /** null means drop the default; otherwise a SQL expression string. */
6095
- default: z8.string().nullable()
6122
+ default: z9.string().nullable()
6096
6123
  }),
6097
6124
  // Indexes
6098
- z8.object({
6099
- type: z8.literal("addIndex"),
6100
- table: z8.string().min(1).max(63),
6101
- name: z8.string().min(1).max(63),
6102
- columns: z8.array(z8.string().min(1)).min(1),
6103
- unique: z8.boolean().optional(),
6125
+ z9.object({
6126
+ type: z9.literal("addIndex"),
6127
+ table: z9.string().min(1).max(63),
6128
+ name: z9.string().min(1).max(63),
6129
+ columns: z9.array(z9.string().min(1)).min(1),
6130
+ unique: z9.boolean().optional(),
6104
6131
  /** Partial-index WHERE clause, as a raw SQL expression */
6105
- where: z8.string().optional(),
6106
- ifNotExists: z8.boolean().optional()
6132
+ where: z9.string().optional(),
6133
+ ifNotExists: z9.boolean().optional()
6107
6134
  }),
6108
- z8.object({
6109
- type: z8.literal("dropIndex"),
6110
- name: z8.string().min(1).max(63),
6111
- cascade: z8.boolean().optional(),
6112
- ifExists: z8.boolean().optional()
6135
+ z9.object({
6136
+ type: z9.literal("dropIndex"),
6137
+ name: z9.string().min(1).max(63),
6138
+ cascade: z9.boolean().optional(),
6139
+ ifExists: z9.boolean().optional()
6113
6140
  }),
6114
6141
  // Constraints
6115
- z8.object({
6116
- type: z8.literal("addForeignKey"),
6117
- table: z8.string().min(1).max(63),
6118
- name: z8.string().min(1).max(63),
6119
- columns: z8.array(z8.string().min(1)).min(1),
6120
- refTable: z8.string().min(1).max(63),
6121
- refColumns: z8.array(z8.string().min(1)).min(1),
6142
+ z9.object({
6143
+ type: z9.literal("addForeignKey"),
6144
+ table: z9.string().min(1).max(63),
6145
+ name: z9.string().min(1).max(63),
6146
+ columns: z9.array(z9.string().min(1)).min(1),
6147
+ refTable: z9.string().min(1).max(63),
6148
+ refColumns: z9.array(z9.string().min(1)).min(1),
6122
6149
  onDelete: referentialActionSchema.optional(),
6123
6150
  onUpdate: referentialActionSchema.optional()
6124
6151
  }),
6125
- z8.object({
6126
- type: z8.literal("addUniqueConstraint"),
6127
- table: z8.string().min(1).max(63),
6128
- name: z8.string().min(1).max(63),
6129
- columns: z8.array(z8.string().min(1)).min(1)
6152
+ z9.object({
6153
+ type: z9.literal("addUniqueConstraint"),
6154
+ table: z9.string().min(1).max(63),
6155
+ name: z9.string().min(1).max(63),
6156
+ columns: z9.array(z9.string().min(1)).min(1)
6130
6157
  }),
6131
- z8.object({
6132
- type: z8.literal("addCheckConstraint"),
6133
- table: z8.string().min(1).max(63),
6134
- name: z8.string().min(1).max(63),
6135
- expression: z8.string().min(1)
6158
+ z9.object({
6159
+ type: z9.literal("addCheckConstraint"),
6160
+ table: z9.string().min(1).max(63),
6161
+ name: z9.string().min(1).max(63),
6162
+ expression: z9.string().min(1)
6136
6163
  }),
6137
- z8.object({
6138
- type: z8.literal("dropConstraint"),
6139
- table: z8.string().min(1).max(63),
6140
- name: z8.string().min(1).max(63),
6141
- cascade: z8.boolean().optional(),
6142
- ifExists: z8.boolean().optional()
6164
+ z9.object({
6165
+ type: z9.literal("dropConstraint"),
6166
+ table: z9.string().min(1).max(63),
6167
+ name: z9.string().min(1).max(63),
6168
+ cascade: z9.boolean().optional(),
6169
+ ifExists: z9.boolean().optional()
6143
6170
  }),
6144
6171
  // Enum types
6145
- z8.object({
6146
- type: z8.literal("createEnum"),
6147
- name: z8.string().min(1).max(63),
6148
- values: z8.array(z8.string().min(1)).min(1)
6172
+ z9.object({
6173
+ type: z9.literal("createEnum"),
6174
+ name: z9.string().min(1).max(63),
6175
+ values: z9.array(z9.string().min(1)).min(1)
6149
6176
  }),
6150
- z8.object({
6151
- type: z8.literal("alterEnumAddValue"),
6152
- name: z8.string().min(1).max(63),
6153
- value: z8.string().min(1),
6177
+ z9.object({
6178
+ type: z9.literal("alterEnumAddValue"),
6179
+ name: z9.string().min(1).max(63),
6180
+ value: z9.string().min(1),
6154
6181
  /** Position the new value before an existing one (mutually exclusive with `after`) */
6155
- before: z8.string().optional(),
6182
+ before: z9.string().optional(),
6156
6183
  /** Position the new value after an existing one (mutually exclusive with `before`) */
6157
- after: z8.string().optional(),
6158
- ifNotExists: z8.boolean().optional()
6184
+ after: z9.string().optional(),
6185
+ ifNotExists: z9.boolean().optional()
6159
6186
  }),
6160
- z8.object({
6161
- type: z8.literal("dropEnum"),
6162
- name: z8.string().min(1).max(63),
6163
- cascade: z8.boolean().optional(),
6164
- ifExists: z8.boolean().optional()
6187
+ z9.object({
6188
+ type: z9.literal("dropEnum"),
6189
+ name: z9.string().min(1).max(63),
6190
+ cascade: z9.boolean().optional(),
6191
+ ifExists: z9.boolean().optional()
6165
6192
  }),
6166
6193
  // Extensions
6167
- z8.object({
6168
- type: z8.literal("createExtension"),
6169
- name: z8.string().min(1),
6170
- ifNotExists: z8.boolean().optional()
6194
+ z9.object({
6195
+ type: z9.literal("createExtension"),
6196
+ name: z9.string().min(1),
6197
+ ifNotExists: z9.boolean().optional()
6171
6198
  }),
6172
6199
  // Data operations
6173
6200
  /**
@@ -6175,26 +6202,26 @@ var schemaOpSchema = z8.discriminatedUnion("type", [
6175
6202
  * destructive — requires explicit confirm to replay on prod. The
6176
6203
  * `description` is what the agent/user sees at confirm time.
6177
6204
  */
6178
- z8.object({
6179
- type: z8.literal("backfill"),
6180
- description: z8.string().min(1),
6181
- sql: z8.string().min(1)
6205
+ z9.object({
6206
+ type: z9.literal("backfill"),
6207
+ description: z9.string().min(1),
6208
+ sql: z9.string().min(1)
6182
6209
  }),
6183
6210
  /**
6184
6211
  * Escape hatch for DDL the structured ops can't express. `destructive`
6185
6212
  * forces classification — use false only for genuinely additive-only SQL
6186
6213
  * (e.g. CREATE EXTENSION variants the structured op doesn't cover).
6187
6214
  */
6188
- z8.object({
6189
- type: z8.literal("rawSql"),
6190
- description: z8.string().min(1),
6191
- sql: z8.string().min(1),
6192
- destructive: z8.boolean()
6215
+ z9.object({
6216
+ type: z9.literal("rawSql"),
6217
+ description: z9.string().min(1),
6218
+ sql: z9.string().min(1),
6219
+ destructive: z9.boolean()
6193
6220
  })
6194
6221
  ]);
6195
6222
 
6196
6223
  // ../../packages/lib/src/server/module-rail/ops.ts
6197
- import { z as z9 } from "zod";
6224
+ import { z as z10 } from "zod";
6198
6225
 
6199
6226
  // ../../packages/lib/src/server/module-rail/reconcile.ts
6200
6227
  var SANDBOX_BATCH_SIZE = 200;
@@ -6307,39 +6334,24 @@ async function assertNoRootRoutesWhenMountsEmpty(context, moduleName) {
6307
6334
  );
6308
6335
  }
6309
6336
  }
6310
- async function assertNoEnhancementRoutes(context, moduleName) {
6311
- const enhancementsDir = `${MODULES_SANDBOX_DIR}/${moduleName}/enhancements`;
6312
- const enhancementEntries = await listImmediateEntries(context.sandbox, enhancementsDir, {
6313
- optional: true,
6314
- label: `enhancements for ${moduleName}`
6315
- });
6316
- for (const entry of enhancementEntries) {
6317
- if (entry.kind !== "dir") continue;
6318
- const sliceRoutes = `${enhancementsDir}/${entry.name}/routes`;
6319
- const sliceProbe = await context.sandbox.exec(
6320
- [
6321
- `p=${shellQuote(sliceRoutes)}`,
6322
- `if test -L "$p" || test -e "$p"; then exit 2; else exit 0; fi`
6323
- ].join("\n"),
6324
- { raiseOnError: false }
6325
- );
6326
- if (sliceProbe.exitCode === 2) {
6337
+ async function assertNoEnhancementRoutePaths(context, paths) {
6338
+ const kinds = await classifySandboxPathPresenceKindMany(context, paths, "enhancement-route-kind");
6339
+ for (const sliceRoutes of paths) {
6340
+ const kind = kinds.get(sliceRoutes);
6341
+ if (kind === "absent") continue;
6342
+ if (kind !== "unreadable" && kind !== void 0) {
6327
6343
  throw new Error(
6328
6344
  `[ModuleRail] composition artifacts: enhancement slice routes/ are forbidden at ${sliceRoutes}`
6329
6345
  );
6330
6346
  }
6331
- if (sliceProbe.exitCode !== 0) {
6332
- const detail = typeof sliceProbe.error === "string" && sliceProbe.error.trim() || sliceProbe.output.trim() || `exit ${sliceProbe.exitCode}`;
6333
- throw new Error(
6334
- `[ModuleRail] composition artifacts: cannot probe ${sliceRoutes}: ${redactSecrets(detail)}`
6335
- );
6336
- }
6347
+ throw new Error(
6348
+ `[ModuleRail] composition artifacts: cannot probe ${sliceRoutes}: entry-kind:${kind ?? "missing"}`
6349
+ );
6337
6350
  }
6338
6351
  }
6339
- async function discoverModuleRouteEntries(context, moduleName) {
6352
+ async function discoverRootRouteEntries(context, moduleName) {
6340
6353
  const modulePath = `${MODULES_SANDBOX_DIR}/${moduleName}`;
6341
6354
  const routesDir = `${modulePath}/routes`;
6342
- await assertNoEnhancementRoutes(context, moduleName);
6343
6355
  const quotedRoutes = shellQuote(routesDir);
6344
6356
  const listResult = await context.sandbox.exec(
6345
6357
  [
@@ -6793,7 +6805,8 @@ async function classifySandboxPathPresenceKindMany(context, paths, probeToken) {
6793
6805
  `# Leaf is missing. A missing intermediate parent means the path is simply`,
6794
6806
  `# absent (new nested writes). Only an existing-but-inaccessible parent`,
6795
6807
  `# is unreadable \u2014 never conflate ENOENT parents with EACCES.`,
6796
- `parent=$(dirname -- "$p")`,
6808
+ // Probe paths are nested repo-relative paths; do not spawn dirname per missing file.
6809
+ `parent=\${p%/*}`,
6797
6810
  `if test ! -e "$parent" && test ! -L "$parent"; then`,
6798
6811
  ` printf 'entry-kind:absent\\t%s\\n' "$p"`,
6799
6812
  `elif ls -ld -- "$parent" >/dev/null 2>&1; then`,
@@ -7338,14 +7351,21 @@ async function reconcileCompositionArtifacts(context, options = {}) {
7338
7351
  manifestsByName
7339
7352
  );
7340
7353
  const routeEntriesByOwner = {};
7354
+ await assertNoEnhancementRoutePaths(
7355
+ context,
7356
+ moduleRuntimeFacts.flatMap(
7357
+ ({ moduleName, onDiskEnhancementDirs }) => onDiskEnhancementDirs.map(
7358
+ (peer) => `${MODULES_SANDBOX_DIR}/${moduleName}/enhancements/${peer}/routes`
7359
+ )
7360
+ )
7361
+ );
7341
7362
  for (const moduleName of composedNames) {
7342
7363
  const manifest = manifestsByName[moduleName];
7343
- await assertNoEnhancementRoutes(context, moduleName);
7344
7364
  if (manifest.mounts.length === 0) {
7345
7365
  await assertNoRootRoutesWhenMountsEmpty(context, moduleName);
7346
7366
  routeEntriesByOwner[moduleName] = [];
7347
7367
  } else {
7348
- routeEntriesByOwner[moduleName] = await discoverModuleRouteEntries(context, moduleName);
7368
+ routeEntriesByOwner[moduleName] = await discoverRootRouteEntries(context, moduleName);
7349
7369
  }
7350
7370
  }
7351
7371
  const existingGeneratedPaths = await listExistingGeneratedStubPaths(context);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stardeck-customer-apps/compose",
3
- "version": "0.14.0",
3
+ "version": "0.15.0",
4
4
  "description": "Regenerates a Stardeck app's Module-rail artifacts — the six src/*.gen.ts registries and every route/endpoint stub — from src/modules/*/module.json",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -34,7 +34,7 @@
34
34
  "build": "rm -rf dist && tsup",
35
35
  "dev": "tsup --watch",
36
36
  "format": "prettier --write . && oxlint . --fix",
37
- "typecheck": "tsgo -p tsconfig.test.json",
37
+ "typecheck": "node ../../scripts/typecheck.mjs -p tsconfig.test.json",
38
38
  "lint": "oxlint src/",
39
39
  "test": "vitest run",
40
40
  "test:watch": "vitest"