@stardeck-customer-apps/compose 0.9.0 → 0.11.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
@@ -2080,11 +2080,54 @@ function shellQuote(value) {
2080
2080
  return `'${value.replace(/'/g, `'\\''`)}'`;
2081
2081
  }
2082
2082
 
2083
- // ../../packages/lib/src/shared/module-composition.ts
2084
- import { z as z6 } from "zod";
2083
+ // ../../packages/lib/src/shared/compose-inputs.ts
2084
+ import { z } from "zod";
2085
+ var COMPOSE_INPUTS_PATH = "apps/web/stardeck.compose.json";
2086
+ var composeInputsSchema = z.strictObject({
2087
+ connectedStores: z.array(
2088
+ z.strictObject({
2089
+ storeId: z.string().min(1),
2090
+ slug: z.string().min(1),
2091
+ // A row with an empty binding key must not fail a deploy: the rail
2092
+ // treats falsy as "no binding key", so normalise rather than reject.
2093
+ bindingKey: z.string().nullish().transform((value) => value || null),
2094
+ accessLevel: z.enum(["read", "write", "admin"]),
2095
+ // Defaulted so a file written before the platform emitted store types
2096
+ // (database rows only) still parses. The platform starts writing
2097
+ // storage rows, with `type` set, once forks run a compose that reads it.
2098
+ type: z.enum(["database", "storage"]).default("database")
2099
+ })
2100
+ ).default([]),
2101
+ installs: z.record(
2102
+ z.string(),
2103
+ z.strictObject({
2104
+ installedVersion: z.string().min(1),
2105
+ datastoreBindings: z.record(z.string(), z.string()).nullish()
2106
+ })
2107
+ ).default({})
2108
+ });
2109
+ var STUB_IGNORE_PATH = "apps/web/src/app/.gitignore";
2110
+ var STUB_IGNORE_HEADER = "# Generated by stardeck-compose \u2014 Module-rail stubs are build outputs; do not edit or commit them.";
2111
+ function escapeStubIgnorePattern(pattern) {
2112
+ const escaped = pattern.replace(/[\\[\]*?]/g, (char) => `\\${char}`);
2113
+ return /^[#!]/.test(escaped) ? `\\${escaped}` : escaped;
2114
+ }
2115
+ function buildStubIgnore(artifactPaths) {
2116
+ const prefix = "apps/web/src/app/";
2117
+ const entries = artifactPaths.filter((artifact) => artifact.startsWith(prefix)).map((artifact) => escapeStubIgnorePattern(`/${artifact.slice(prefix.length)}`)).sort();
2118
+ return [STUB_IGNORE_HEADER, ...entries].join("\n") + "\n";
2119
+ }
2120
+
2121
+ // ../../packages/lib/src/shared/module-enablement.ts
2122
+ function isModuleEnabledByList(raw, name, kindOf) {
2123
+ if (!raw) return true;
2124
+ const kind = kindOf(name);
2125
+ if (kind != null && kind !== "feature") return true;
2126
+ return raw.split(",").includes(name);
2127
+ }
2085
2128
 
2086
2129
  // ../../packages/module-contract/src/module-manifest.ts
2087
- import { z } from "zod";
2130
+ import { z as z2 } from "zod";
2088
2131
 
2089
2132
  // ../../packages/module-contract/src/slug.ts
2090
2133
  function validateSlug(slug) {
@@ -2119,13 +2162,13 @@ var ROUTE_GROUP_REGEX = /^\([a-z0-9-]+\)$/;
2119
2162
  var ENDPOINT_STATIC_SEGMENT_REGEX = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
2120
2163
  var ENDPOINT_PARAM_NAME_REGEX = /^[a-z][a-z0-9_-]*$/;
2121
2164
  var ENDPOINT_METHODS = ["GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"];
2122
- var moduleNameSchema = z.string().max(40).regex(KEBAB_SLUG_REGEX, "must be a kebab-case slug (lowercase letters, digits, hyphens)");
2165
+ var moduleNameSchema = z2.string().max(40).regex(KEBAB_SLUG_REGEX, "must be a kebab-case slug (lowercase letters, digits, hyphens)");
2123
2166
  function isValidModuleName(value) {
2124
2167
  return moduleNameSchema.safeParse(value).success;
2125
2168
  }
2126
- var projectSlugSchema = z.string().refine((value) => validateSlug(value).valid, "must be a valid project slug");
2127
- var moduleKindSchema = z.enum(["feature", "library", "contract"]);
2128
- var endpointMethodSchema = z.enum(ENDPOINT_METHODS);
2169
+ var projectSlugSchema = z2.string().refine((value) => validateSlug(value).valid, "must be a valid project slug");
2170
+ var moduleKindSchema = z2.enum(["feature", "library", "contract"]);
2171
+ var endpointMethodSchema = z2.enum(ENDPOINT_METHODS);
2129
2172
  function endpointPathValidationError(path4) {
2130
2173
  if (!path4.startsWith("/api/")) {
2131
2174
  return 'must begin with "/api/"';
@@ -2210,21 +2253,21 @@ function normalizeEndpointPathForCollision(path4) {
2210
2253
  return segment;
2211
2254
  }).join("/");
2212
2255
  }
2213
- var endpointPathSchema = z.string().superRefine((path4, ctx) => {
2256
+ var endpointPathSchema = z2.string().superRefine((path4, ctx) => {
2214
2257
  const error = endpointPathValidationError(path4);
2215
2258
  if (error != null) {
2216
- ctx.addIssue({ code: z.ZodIssueCode.custom, message: error });
2259
+ ctx.addIssue({ code: z2.ZodIssueCode.custom, message: error });
2217
2260
  }
2218
2261
  });
2219
- var endpointClaimSchema = z.object({
2262
+ var endpointClaimSchema = z2.object({
2220
2263
  path: endpointPathSchema,
2221
- methods: z.array(endpointMethodSchema).min(1, "methods must be non-empty").superRefine((methods, ctx) => {
2264
+ methods: z2.array(endpointMethodSchema).min(1, "methods must be non-empty").superRefine((methods, ctx) => {
2222
2265
  const seen = /* @__PURE__ */ new Set();
2223
2266
  for (let i = 0; i < methods.length; i += 1) {
2224
2267
  const method = methods[i];
2225
2268
  if (seen.has(method)) {
2226
2269
  ctx.addIssue({
2227
- code: z.ZodIssueCode.custom,
2270
+ code: z2.ZodIssueCode.custom,
2228
2271
  message: `duplicate method "${method}"`,
2229
2272
  path: [i]
2230
2273
  });
@@ -2261,14 +2304,14 @@ function mountSegmentValidationError(segment) {
2261
2304
  }
2262
2305
  return null;
2263
2306
  }
2264
- var mountSegmentSchema = z.string().superRefine((segment, ctx) => {
2307
+ var mountSegmentSchema = z2.string().superRefine((segment, ctx) => {
2265
2308
  const error = mountSegmentValidationError(segment);
2266
2309
  if (error != null) {
2267
- ctx.addIssue({ code: z.ZodIssueCode.custom, message: error });
2310
+ ctx.addIssue({ code: z2.ZodIssueCode.custom, message: error });
2268
2311
  }
2269
2312
  });
2270
- var mountSchema = z.object({
2271
- group: z.string().regex(ROUTE_GROUP_REGEX, 'must be a Next.js route group like "(admin)"'),
2313
+ var mountSchema = z2.object({
2314
+ group: z2.string().regex(ROUTE_GROUP_REGEX, 'must be a Next.js route group like "(admin)"'),
2272
2315
  segment: mountSegmentSchema
2273
2316
  }).strict();
2274
2317
  function splitMountSegment(segment) {
@@ -2281,21 +2324,21 @@ function splitMountSegment(segment) {
2281
2324
  }
2282
2325
  return segment.split("/");
2283
2326
  }
2284
- var tableNameSchema = z.string().regex(SNAKE_TABLE_REGEX, "must be snake_case");
2327
+ var tableNameSchema = z2.string().regex(SNAKE_TABLE_REGEX, "must be snake_case");
2285
2328
  var NPM_PACKAGE_NAME_REGEX = /^(@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/;
2286
- var npmPackageNameSchema = z.string().max(214).regex(NPM_PACKAGE_NAME_REGEX, "must be a valid npm package name");
2287
- var descriptionSchema = z.string().max(200, "must be at most 200 characters").refine((value) => value.trim().length > 0, "must be non-empty after trimming");
2288
- var crossAppSchema = z.object({
2289
- exposes: z.array(z.unknown()).max(0, "must be empty until cross-app contracts are defined"),
2290
- consumes: z.array(z.unknown()).max(0, "must be empty until cross-app contracts are defined")
2329
+ var npmPackageNameSchema = z2.string().max(214).regex(NPM_PACKAGE_NAME_REGEX, "must be a valid npm package name");
2330
+ var descriptionSchema = z2.string().max(200, "must be at most 200 characters").refine((value) => value.trim().length > 0, "must be non-empty after trimming");
2331
+ var crossAppSchema = z2.object({
2332
+ exposes: z2.array(z2.unknown()).max(0, "must be empty until cross-app contracts are defined"),
2333
+ consumes: z2.array(z2.unknown()).max(0, "must be empty until cross-app contracts are defined")
2291
2334
  }).strict();
2292
- var enhancementWorkflowsSchema = z.record(moduleNameSchema, z.array(z.string().min(1))).default({});
2293
- var enhancementEndpointsSchema = z.record(moduleNameSchema, z.array(endpointClaimSchema)).default({});
2294
- var datastoreRequirementSchema = z.object({
2335
+ var enhancementWorkflowsSchema = z2.record(moduleNameSchema, z2.array(z2.string().min(1))).default({});
2336
+ var enhancementEndpointsSchema = z2.record(moduleNameSchema, z2.array(endpointClaimSchema)).default({});
2337
+ var datastoreRequirementSchema = z2.object({
2295
2338
  /** Logical name, module-local, snake_case. Never a physical store key. */
2296
2339
  id: tableNameSchema,
2297
2340
  /** "app" = this project's app-local store; "org" = an org-level shared store. */
2298
- scope: z.enum(["app", "org"]).default("app"),
2341
+ scope: z2.enum(["app", "org"]).default("app"),
2299
2342
  /**
2300
2343
  * Which kind of connected store satisfies this requirement. A "database"
2301
2344
  * requirement only ever binds to a database store and a "storage" one
@@ -2303,34 +2346,34 @@ var datastoreRequirementSchema = z.object({
2303
2346
  * rail never crosses the two. Defaults to "database", the only kind the
2304
2347
  * rail resolved before this field existed.
2305
2348
  */
2306
- type: z.enum(["database", "storage"]).default("database"),
2349
+ type: z2.enum(["database", "storage"]).default("database"),
2307
2350
  /** true → install fails when no store resolves; false → binding omitted, module degrades. */
2308
- required: z.boolean().default(true),
2351
+ required: z2.boolean().default(true),
2309
2352
  /** Checked against the connected store's accessLevel at install. */
2310
- access: z.enum(["read", "write", "admin"]).default("write")
2353
+ access: z2.enum(["read", "write", "admin"]).default("write")
2311
2354
  }).strict();
2312
- var permissionClaimSchema = z.object({
2355
+ var permissionClaimSchema = z2.object({
2313
2356
  /**
2314
2357
  * Lowercase resource:action with one colon and no underscores. This grammar
2315
2358
  * is deliberately narrower than the platform permission tool's charset.
2316
2359
  */
2317
- key: z.string().regex(/^[a-z0-9][a-z0-9-]*:[a-z0-9][a-z0-9-]*$/, "must be a lowercase resource:action key").max(100, "must be at most 100 characters (platform permission tool limit)"),
2360
+ key: z2.string().regex(/^[a-z0-9][a-z0-9-]*:[a-z0-9][a-z0-9-]*$/, "must be a lowercase resource:action key").max(100, "must be at most 100 characters (platform permission tool limit)"),
2318
2361
  /** Human label for the deployment auth config row. */
2319
2362
  description: descriptionSchema
2320
2363
  }).strict();
2321
- var seedBundleSchema = z.object({
2364
+ var seedBundleSchema = z2.object({
2322
2365
  table: tableNameSchema,
2323
2366
  /** Columns forming the ON CONFLICT key — must appear in every row. */
2324
- conflictTarget: z.array(z.string().min(1)).min(1),
2367
+ conflictTarget: z2.array(z2.string().min(1)).min(1),
2325
2368
  /** Scalar JSON values only (no nested objects/arrays). */
2326
- rows: z.array(z.record(z.string(), z.union([z.string(), z.number(), z.boolean(), z.null()]))).min(1)
2369
+ rows: z2.array(z2.record(z2.string(), z2.union([z2.string(), z2.number(), z2.boolean(), z2.null()]))).min(1)
2327
2370
  }).strict().superRefine((bundle, ctx) => {
2328
2371
  const rowKeys = bundle.rows.map((row) => Object.keys(row).sort().join("\0"));
2329
2372
  const canonical = rowKeys[0];
2330
2373
  for (let i = 1; i < rowKeys.length; i += 1) {
2331
2374
  if (rowKeys[i] !== canonical) {
2332
2375
  ctx.addIssue({
2333
- code: z.ZodIssueCode.custom,
2376
+ code: z2.ZodIssueCode.custom,
2334
2377
  message: "all rows in a seed bundle must have identical key sets",
2335
2378
  path: ["rows", i]
2336
2379
  });
@@ -2342,7 +2385,7 @@ var seedBundleSchema = z.object({
2342
2385
  for (const col of bundle.conflictTarget) {
2343
2386
  if (!keys.has(col)) {
2344
2387
  ctx.addIssue({
2345
- code: z.ZodIssueCode.custom,
2388
+ code: z2.ZodIssueCode.custom,
2346
2389
  message: `conflictTarget column "${col}" must be present in every row`,
2347
2390
  path: ["rows", i]
2348
2391
  });
@@ -2350,35 +2393,35 @@ var seedBundleSchema = z.object({
2350
2393
  }
2351
2394
  }
2352
2395
  });
2353
- var moduleManifestSchema = z.object({
2396
+ var moduleManifestSchema = z2.object({
2354
2397
  name: moduleNameSchema,
2355
2398
  kind: moduleKindSchema,
2356
2399
  // Release versions are platform-owned in module_versions. Accept this
2357
2400
  // legacy field while existing Module source ages out, but strip it from the
2358
2401
  // parsed contract so no runtime can treat module.json as version authority.
2359
- version: z.unknown().optional(),
2402
+ version: z2.unknown().optional(),
2360
2403
  home: projectSlugSchema,
2361
2404
  /** Machine-checkable purpose line — required, ≤200 code units, non-blank when trimmed. */
2362
2405
  description: descriptionSchema,
2363
- uses: z.array(moduleNameSchema).default([]),
2406
+ uses: z2.array(moduleNameSchema).default([]),
2364
2407
  /**
2365
2408
  * Soft peers: this module ships `enhancements/<peer>/` directories that are
2366
2409
  * composition-pruned — the installer includes them only when the peer is
2367
2410
  * present, so the peer is NOT an install-time requirement. Disjoint from
2368
2411
  * `uses`; entries may name modules absent from the composition.
2369
2412
  */
2370
- enhancements: z.array(moduleNameSchema).default([]),
2371
- tables: z.array(tableNameSchema).default([]),
2372
- mounts: z.array(mountSchema).default([]),
2373
- integration: z.array(z.string()).default([]),
2374
- workflows: z.array(z.string().min(1)).default([]),
2413
+ enhancements: z2.array(moduleNameSchema).default([]),
2414
+ tables: z2.array(tableNameSchema).default([]),
2415
+ mounts: z2.array(mountSchema).default([]),
2416
+ integration: z2.array(z2.string()).default([]),
2417
+ workflows: z2.array(z2.string().min(1)).default([]),
2375
2418
  /**
2376
2419
  * Slice-scoped workflow names keyed by peer. Keys must be ⊆ `enhancements`;
2377
2420
  * names must be unique across root `workflows` and every slice list.
2378
2421
  */
2379
2422
  enhancementWorkflows: enhancementWorkflowsSchema,
2380
2423
  /** Root API endpoint path claims. */
2381
- endpoints: z.array(endpointClaimSchema).default([]),
2424
+ endpoints: z2.array(endpointClaimSchema).default([]),
2382
2425
  /**
2383
2426
  * Slice-scoped endpoint claims keyed by peer. Keys must be ⊆ `enhancements`;
2384
2427
  * normalized paths must be unique across root and every slice.
@@ -2394,38 +2437,44 @@ var moduleManifestSchema = z.object({
2394
2437
  * to a physical connected store and generates module-datastores.gen.ts;
2395
2438
  * unlike seeds, this field drives generated output.
2396
2439
  */
2397
- datastores: z.array(datastoreRequirementSchema).optional(),
2440
+ datastores: z2.array(datastoreRequirementSchema).optional(),
2398
2441
  /**
2399
2442
  * Keys this module's guards enforce. Install registers them in deployment
2400
2443
  * auth config; registration only — declaring is never granting.
2401
2444
  */
2402
- permissions: z.array(permissionClaimSchema).optional(),
2445
+ permissions: z2.array(permissionClaimSchema).optional(),
2403
2446
  /**
2404
2447
  * Optional starter rows for tables this module owns. Replay is
2405
2448
  * `INSERT ... ON CONFLICT (conflictTarget) DO NOTHING` — idempotent by
2406
2449
  * construction; the rail (not this schema) performs the replay.
2407
2450
  */
2408
- seeds: z.array(seedBundleSchema).optional(),
2451
+ seeds: z2.array(seedBundleSchema).optional(),
2409
2452
  /** npm packages the source imports beyond the base app. Names only; the rail installs each at the home app's range. */
2410
- packages: z.array(npmPackageNameSchema).optional(),
2453
+ packages: z2.array(npmPackageNameSchema).optional(),
2411
2454
  /**
2412
2455
  * UI-layer contract. `separable: true` asserts the Module's components/ and
2413
2456
  * routes/ hold presentation only — the Blueprint's
2414
2457
  * `module-boundaries/ui-layer` lint rule enforces it — so a fork may
2415
2458
  * soft-eject them. See docs/module-ui-soft-eject-plan.md.
2416
2459
  */
2417
- ui: z.object({ separable: z.boolean() }).strict().optional()
2460
+ ui: z2.object({ separable: z2.boolean() }).strict().optional(),
2461
+ /**
2462
+ * `false` keeps a feature Module off in a new Blueprint fork unless the
2463
+ * creator ticks it: the fork picker leaves it unchecked and "every Module"
2464
+ * is written out without it. Omitted means on. Library Modules are always on.
2465
+ */
2466
+ defaultEnabled: z2.boolean().optional()
2418
2467
  }).strict().superRefine((manifest, ctx) => {
2419
2468
  if (manifest.uses.includes(manifest.name)) {
2420
2469
  ctx.addIssue({
2421
- code: z.ZodIssueCode.custom,
2470
+ code: z2.ZodIssueCode.custom,
2422
2471
  message: "a module cannot list itself in uses",
2423
2472
  path: ["uses"]
2424
2473
  });
2425
2474
  }
2426
2475
  if (manifest.enhancements.includes(manifest.name)) {
2427
2476
  ctx.addIssue({
2428
- code: z.ZodIssueCode.custom,
2477
+ code: z2.ZodIssueCode.custom,
2429
2478
  message: "a module cannot list itself in enhancements",
2430
2479
  path: ["enhancements"]
2431
2480
  });
@@ -2433,7 +2482,7 @@ var moduleManifestSchema = z.object({
2433
2482
  const hardAndSoft = manifest.enhancements.filter((peer) => manifest.uses.includes(peer));
2434
2483
  if (hardAndSoft.length > 0) {
2435
2484
  ctx.addIssue({
2436
- code: z.ZodIssueCode.custom,
2485
+ code: z2.ZodIssueCode.custom,
2437
2486
  message: `uses and enhancements must be disjoint (a peer is either a hard dep or a soft one): ${hardAndSoft.join(", ")}`,
2438
2487
  path: ["enhancements"]
2439
2488
  });
@@ -2442,7 +2491,7 @@ var moduleManifestSchema = z.object({
2442
2491
  for (const peer of Object.keys(manifest.enhancementWorkflows)) {
2443
2492
  if (!enhancementPeers.has(peer)) {
2444
2493
  ctx.addIssue({
2445
- code: z.ZodIssueCode.custom,
2494
+ code: z2.ZodIssueCode.custom,
2446
2495
  message: `enhancementWorkflows key "${peer}" must be listed in enhancements`,
2447
2496
  path: ["enhancementWorkflows", peer]
2448
2497
  });
@@ -2451,7 +2500,7 @@ var moduleManifestSchema = z.object({
2451
2500
  for (const peer of Object.keys(manifest.enhancementEndpoints)) {
2452
2501
  if (!enhancementPeers.has(peer)) {
2453
2502
  ctx.addIssue({
2454
- code: z.ZodIssueCode.custom,
2503
+ code: z2.ZodIssueCode.custom,
2455
2504
  message: `enhancementEndpoints key "${peer}" must be listed in enhancements`,
2456
2505
  path: ["enhancementEndpoints", peer]
2457
2506
  });
@@ -2462,7 +2511,7 @@ var moduleManifestSchema = z.object({
2462
2511
  const existing = seenWorkflows.get(name);
2463
2512
  if (existing != null) {
2464
2513
  ctx.addIssue({
2465
- code: z.ZodIssueCode.custom,
2514
+ code: z2.ZodIssueCode.custom,
2466
2515
  message: `duplicate workflow name "${name}" (also at ${existing})`,
2467
2516
  path: path4
2468
2517
  });
@@ -2489,7 +2538,7 @@ var moduleManifestSchema = z.object({
2489
2538
  const existing = seenEndpointKeys.get(key);
2490
2539
  if (existing != null) {
2491
2540
  ctx.addIssue({
2492
- code: z.ZodIssueCode.custom,
2541
+ code: z2.ZodIssueCode.custom,
2493
2542
  message: `duplicate normalized endpoint path "${key}" (also at ${existing})`,
2494
2543
  path: path4
2495
2544
  });
@@ -2511,7 +2560,7 @@ var moduleManifestSchema = z.object({
2511
2560
  const id = manifest.datastores[i].id;
2512
2561
  if (seenDatastoreIds.has(id)) {
2513
2562
  ctx.addIssue({
2514
- code: z.ZodIssueCode.custom,
2563
+ code: z2.ZodIssueCode.custom,
2515
2564
  message: `duplicate datastore id "${id}"`,
2516
2565
  path: ["datastores", i, "id"]
2517
2566
  });
@@ -2526,7 +2575,7 @@ var moduleManifestSchema = z.object({
2526
2575
  const key = manifest.permissions[i].key;
2527
2576
  if (seenPermissionKeys.has(key)) {
2528
2577
  ctx.addIssue({
2529
- code: z.ZodIssueCode.custom,
2578
+ code: z2.ZodIssueCode.custom,
2530
2579
  message: `duplicate permission key "${key}"`,
2531
2580
  path: ["permissions", i, "key"]
2532
2581
  });
@@ -2538,35 +2587,35 @@ var moduleManifestSchema = z.object({
2538
2587
  if (manifest.kind === "library") {
2539
2588
  if (manifest.tables.length > 0) {
2540
2589
  ctx.addIssue({
2541
- code: z.ZodIssueCode.custom,
2590
+ code: z2.ZodIssueCode.custom,
2542
2591
  message: "library modules must not declare tables",
2543
2592
  path: ["tables"]
2544
2593
  });
2545
2594
  }
2546
2595
  if (manifest.mounts.length > 0) {
2547
2596
  ctx.addIssue({
2548
- code: z.ZodIssueCode.custom,
2597
+ code: z2.ZodIssueCode.custom,
2549
2598
  message: "library modules must not declare mounts",
2550
2599
  path: ["mounts"]
2551
2600
  });
2552
2601
  }
2553
2602
  if (manifest.seeds != null && manifest.seeds.length > 0) {
2554
2603
  ctx.addIssue({
2555
- code: z.ZodIssueCode.custom,
2604
+ code: z2.ZodIssueCode.custom,
2556
2605
  message: "library modules must not declare seeds",
2557
2606
  path: ["seeds"]
2558
2607
  });
2559
2608
  }
2560
2609
  if (manifest.datastores != null && manifest.datastores.length > 0) {
2561
2610
  ctx.addIssue({
2562
- code: z.ZodIssueCode.custom,
2611
+ code: z2.ZodIssueCode.custom,
2563
2612
  message: "library modules must not declare datastores",
2564
2613
  path: ["datastores"]
2565
2614
  });
2566
2615
  }
2567
2616
  if (manifest.permissions != null && manifest.permissions.length > 0) {
2568
2617
  ctx.addIssue({
2569
- code: z.ZodIssueCode.custom,
2618
+ code: z2.ZodIssueCode.custom,
2570
2619
  message: "library modules must not declare permissions",
2571
2620
  path: ["permissions"]
2572
2621
  });
@@ -2574,7 +2623,7 @@ var moduleManifestSchema = z.object({
2574
2623
  }
2575
2624
  if (manifest.kind === "contract" && manifest.mounts.length > 0) {
2576
2625
  ctx.addIssue({
2577
- code: z.ZodIssueCode.custom,
2626
+ code: z2.ZodIssueCode.custom,
2578
2627
  message: "contract modules must not declare mounts",
2579
2628
  path: ["mounts"]
2580
2629
  });
@@ -2585,7 +2634,7 @@ var moduleManifestSchema = z.object({
2585
2634
  const bundle = manifest.seeds[i];
2586
2635
  if (!ownedTables.has(bundle.table)) {
2587
2636
  ctx.addIssue({
2588
- code: z.ZodIssueCode.custom,
2637
+ code: z2.ZodIssueCode.custom,
2589
2638
  message: `seeds may only target tables listed in tables (got "${bundle.table}")`,
2590
2639
  path: ["seeds", i, "table"]
2591
2640
  });
@@ -2636,10 +2685,10 @@ var MANDATORY_MODULE_CHECK_DESCRIPTORS = Object.freeze([
2636
2685
  ]);
2637
2686
 
2638
2687
  // ../../packages/module-contract/src/module-structure.ts
2639
- import { z as z3 } from "zod";
2688
+ import { z as z4 } from "zod";
2640
2689
 
2641
2690
  // ../../packages/module-contract/src/module-e2e-manifest.ts
2642
- import { z as z2 } from "zod";
2691
+ import { z as z3 } from "zod";
2643
2692
  function e2eSpecFileValidationError(file) {
2644
2693
  if (file.length === 0) {
2645
2694
  return "must be a non-empty relative path ending in .spec.ts";
@@ -2667,22 +2716,22 @@ function e2eSpecFileValidationError(file) {
2667
2716
  }
2668
2717
  return null;
2669
2718
  }
2670
- var e2eSpecFileSchema = z2.string().superRefine((file, ctx) => {
2719
+ var e2eSpecFileSchema = z3.string().superRefine((file, ctx) => {
2671
2720
  const error = e2eSpecFileValidationError(file);
2672
2721
  if (error != null) {
2673
- ctx.addIssue({ code: z2.ZodIssueCode.custom, message: error });
2722
+ ctx.addIssue({ code: z3.ZodIssueCode.custom, message: error });
2674
2723
  }
2675
2724
  });
2676
- var e2eSpecSchema = z2.object({
2725
+ var e2eSpecSchema = z3.object({
2677
2726
  file: e2eSpecFileSchema,
2678
- requires: z2.array(moduleNameSchema).min(1, "requires must be non-empty")
2727
+ requires: z3.array(moduleNameSchema).min(1, "requires must be non-empty")
2679
2728
  }).strict().superRefine((spec, ctx) => {
2680
2729
  const seen = /* @__PURE__ */ new Set();
2681
2730
  for (let i = 0; i < spec.requires.length; i += 1) {
2682
2731
  const name = spec.requires[i];
2683
2732
  if (seen.has(name)) {
2684
2733
  ctx.addIssue({
2685
- code: z2.ZodIssueCode.custom,
2734
+ code: z3.ZodIssueCode.custom,
2686
2735
  message: `duplicate requirement "${name}"`,
2687
2736
  path: ["requires", i]
2688
2737
  });
@@ -2691,15 +2740,15 @@ var e2eSpecSchema = z2.object({
2691
2740
  }
2692
2741
  }
2693
2742
  });
2694
- var moduleE2eManifestSchema = z2.object({
2695
- specs: z2.array(e2eSpecSchema).min(1, "specs must contain at least one entry")
2743
+ var moduleE2eManifestSchema = z3.object({
2744
+ specs: z3.array(e2eSpecSchema).min(1, "specs must contain at least one entry")
2696
2745
  }).strict().superRefine((manifest, ctx) => {
2697
2746
  const seenFiles = /* @__PURE__ */ new Set();
2698
2747
  for (let i = 0; i < manifest.specs.length; i += 1) {
2699
2748
  const file = manifest.specs[i].file;
2700
2749
  if (seenFiles.has(file)) {
2701
2750
  ctx.addIssue({
2702
- code: z2.ZodIssueCode.custom,
2751
+ code: z3.ZodIssueCode.custom,
2703
2752
  message: `duplicate file "${file}"`,
2704
2753
  path: ["specs", i, "file"]
2705
2754
  });
@@ -2941,7 +2990,7 @@ function compareCodeUnit(a, b) {
2941
2990
  // ../../packages/module-contract/src/module-structure.ts
2942
2991
  var MODULE_STRUCTURE_JSON_BYTE_CAP = 64 * 1024;
2943
2992
  function uniqueSortedStringArray(label) {
2944
- return z3.array(z3.string()).superRefine((arr, ctx) => {
2993
+ return z4.array(z4.string()).superRefine((arr, ctx) => {
2945
2994
  if (new Set(arr).size !== arr.length) {
2946
2995
  ctx.addIssue({ code: "custom", message: `${label} must be unique` });
2947
2996
  return;
@@ -2957,10 +3006,10 @@ function uniqueSortedStringArray(label) {
2957
3006
  });
2958
3007
  }
2959
3008
  function contributionPermissionsSchema(label) {
2960
- return z3.array(
2961
- z3.object({
2962
- contributionId: z3.string().min(1),
2963
- permission: z3.string().min(1)
3009
+ return z4.array(
3010
+ z4.object({
3011
+ contributionId: z4.string().min(1),
3012
+ permission: z4.string().min(1)
2964
3013
  }).strict()
2965
3014
  ).superRefine((entries, ctx) => {
2966
3015
  const keys = entries.map((entry) => `${entry.contributionId}\0${entry.permission}`);
@@ -2978,17 +3027,17 @@ function contributionPermissionsSchema(label) {
2978
3027
  }
2979
3028
  }).optional();
2980
3029
  }
2981
- var collectedFilePayloadSchema = z3.discriminatedUnion("status", [
2982
- z3.object({ status: z3.literal("ok"), text: z3.string() }).strict(),
2983
- z3.object({ status: z3.literal("missing") }).strict(),
2984
- z3.object({ status: z3.literal("too-large") }).strict(),
2985
- z3.object({ status: z3.literal("unreadable") }).strict()
3030
+ var collectedFilePayloadSchema = z4.discriminatedUnion("status", [
3031
+ z4.object({ status: z4.literal("ok"), text: z4.string() }).strict(),
3032
+ z4.object({ status: z4.literal("missing") }).strict(),
3033
+ z4.object({ status: z4.literal("too-large") }).strict(),
3034
+ z4.object({ status: z4.literal("unreadable") }).strict()
2986
3035
  ]);
2987
- var collectedLocaleFileSchema = z3.object({
2988
- locale: z3.string().min(1),
3036
+ var collectedLocaleFileSchema = z4.object({
3037
+ locale: z4.string().min(1),
2989
3038
  payload: collectedFilePayloadSchema
2990
3039
  }).strict();
2991
- var collectedE2eFactsSchema = z3.object({
3040
+ var collectedE2eFactsSchema = z4.object({
2992
3041
  manifest: collectedFilePayloadSchema,
2993
3042
  specFiles: uniqueSortedStringArray("specFiles"),
2994
3043
  escapingSymlinks: uniqueSortedStringArray("escapingSymlinks")
@@ -3010,20 +3059,20 @@ function validateRouteExportEvidenceCongruence(input) {
3010
3059
  }
3011
3060
  }
3012
3061
  }
3013
- var collectedEnhancementFactsSchema = z3.object({
3014
- peer: z3.string().min(1),
3015
- hasContributionsTs: z3.boolean(),
3062
+ var collectedEnhancementFactsSchema = z4.object({
3063
+ peer: z4.string().min(1),
3064
+ hasContributionsTs: z4.boolean(),
3016
3065
  contributionPermissions: contributionPermissionsSchema("enhancement contributionPermissions"),
3017
- hasInitializeServerTs: z3.boolean(),
3066
+ hasInitializeServerTs: z4.boolean(),
3018
3067
  contributionsCollectionErrors: uniqueSortedStringArray(
3019
3068
  "enhancement contributionsCollectionErrors"
3020
3069
  ),
3021
3070
  initializeServerCollectionErrors: uniqueSortedStringArray(
3022
3071
  "enhancement initializeServerCollectionErrors"
3023
3072
  ),
3024
- hasRoutesDir: z3.boolean(),
3073
+ hasRoutesDir: z4.boolean(),
3025
3074
  routeCollectionErrors: uniqueSortedStringArray("enhancement routeCollectionErrors"),
3026
- locales: z3.array(collectedLocaleFileSchema).superRefine((locales, ctx) => {
3075
+ locales: z4.array(collectedLocaleFileSchema).superRefine((locales, ctx) => {
3027
3076
  const names = locales.map((l) => l.locale);
3028
3077
  if (new Set(names).size !== names.length) {
3029
3078
  ctx.addIssue({ code: "custom", message: "enhancement locales must be unique by locale" });
@@ -3038,27 +3087,27 @@ var collectedEnhancementFactsSchema = z3.object({
3038
3087
  }),
3039
3088
  e2e: collectedE2eFactsSchema.nullable()
3040
3089
  }).strict();
3041
- var moduleStructureFactsSchema = z3.object({
3042
- collected: z3.literal(true),
3043
- dirName: z3.string().min(1),
3044
- hasIndexTs: z3.boolean(),
3045
- hasContributionsTs: z3.boolean(),
3090
+ var moduleStructureFactsSchema = z4.object({
3091
+ collected: z4.literal(true),
3092
+ dirName: z4.string().min(1),
3093
+ hasIndexTs: z4.boolean(),
3094
+ hasContributionsTs: z4.boolean(),
3046
3095
  contributionPermissions: contributionPermissionsSchema("contributionPermissions"),
3047
- hasSlotsTs: z3.boolean(),
3096
+ hasSlotsTs: z4.boolean(),
3048
3097
  contributionsCollectionErrors: uniqueSortedStringArray("contributionsCollectionErrors"),
3049
3098
  slotsCollectionErrors: uniqueSortedStringArray("slotsCollectionErrors"),
3050
- hasTestsDir: z3.boolean(),
3051
- hasDatastoreDir: z3.boolean(),
3052
- datastoreFileCount: z3.number().int().nonnegative(),
3053
- hasServerIndexTs: z3.boolean(),
3054
- hasOtherServerTs: z3.boolean(),
3055
- hasE2eDir: z3.boolean(),
3056
- hasRoutesDir: z3.boolean(),
3099
+ hasTestsDir: z4.boolean(),
3100
+ hasDatastoreDir: z4.boolean(),
3101
+ datastoreFileCount: z4.number().int().nonnegative(),
3102
+ hasServerIndexTs: z4.boolean(),
3103
+ hasOtherServerTs: z4.boolean(),
3104
+ hasE2eDir: z4.boolean(),
3105
+ hasRoutesDir: z4.boolean(),
3057
3106
  routeEntries: uniqueSortedStringArray("routeEntries"),
3058
- routePageExports: z3.array(
3059
- z3.object({
3060
- nextRelativePath: z3.string().min(1),
3061
- hasGenerateMetadata: z3.boolean()
3107
+ routePageExports: z4.array(
3108
+ z4.object({
3109
+ nextRelativePath: z4.string().min(1),
3110
+ hasGenerateMetadata: z4.boolean()
3062
3111
  }).strict()
3063
3112
  ).superRefine((entries, ctx) => {
3064
3113
  const paths = entries.map((e) => e.nextRelativePath);
@@ -3080,7 +3129,7 @@ var moduleStructureFactsSchema = z3.object({
3080
3129
  }
3081
3130
  }),
3082
3131
  routeCollectionErrors: uniqueSortedStringArray("routeCollectionErrors"),
3083
- rootLocales: z3.array(collectedLocaleFileSchema).superRefine((locales, ctx) => {
3132
+ rootLocales: z4.array(collectedLocaleFileSchema).superRefine((locales, ctx) => {
3084
3133
  const names = locales.map((l) => l.locale);
3085
3134
  if (new Set(names).size !== names.length) {
3086
3135
  ctx.addIssue({ code: "custom", message: "rootLocales must be unique by locale" });
@@ -3094,7 +3143,7 @@ var moduleStructureFactsSchema = z3.object({
3094
3143
  }
3095
3144
  }),
3096
3145
  rootE2e: collectedE2eFactsSchema.nullable(),
3097
- enhancements: z3.array(collectedEnhancementFactsSchema).superRefine((enhancements, ctx) => {
3146
+ enhancements: z4.array(collectedEnhancementFactsSchema).superRefine((enhancements, ctx) => {
3098
3147
  const peers = enhancements.map((e) => e.peer);
3099
3148
  if (new Set(peers).size !== peers.length) {
3100
3149
  ctx.addIssue({ code: "custom", message: "enhancement peers must be unique" });
@@ -3115,10 +3164,10 @@ var moduleStructureFactsSchema = z3.object({
3115
3164
  onIssue: (message) => ctx.addIssue({ code: "custom", message })
3116
3165
  });
3117
3166
  });
3118
- var moduleStructureCollectionSchema = z3.object({
3167
+ var moduleStructureCollectionSchema = z4.object({
3119
3168
  homeLocales: uniqueSortedStringArray("homeLocales"),
3120
- modules: z3.array(moduleStructureFactsSchema),
3121
- truncated: z3.boolean().optional(),
3169
+ modules: z4.array(moduleStructureFactsSchema),
3170
+ truncated: z4.boolean().optional(),
3122
3171
  collectionErrors: uniqueSortedStringArray("collectionErrors").optional()
3123
3172
  }).strict().superRefine((collection, ctx) => {
3124
3173
  const dirs = collection.modules.map((m) => m.dirName);
@@ -3135,9 +3184,9 @@ var moduleStructureCollectionSchema = z3.object({
3135
3184
  });
3136
3185
 
3137
3186
  // ../../packages/module-contract/src/locale.ts
3138
- import { z as z4 } from "zod";
3187
+ import { z as z5 } from "zod";
3139
3188
  var LOCALE_BASENAME_REGEX = /^[a-z]{2,3}(-[A-Za-z0-9]+)*$/;
3140
- var localeBasenameSchema = z4.string().max(35).regex(LOCALE_BASENAME_REGEX, "must be a BCP47-like locale tag (e.g. en, th, en-US)");
3189
+ var localeBasenameSchema = z5.string().max(35).regex(LOCALE_BASENAME_REGEX, "must be a BCP47-like locale tag (e.g. en, th, en-US)");
3141
3190
  function isValidLocaleBasename(value) {
3142
3191
  return localeBasenameSchema.safeParse(value).success;
3143
3192
  }
@@ -3540,43 +3589,43 @@ function formatRoutePageExportErrors(nextRelativePath, validation) {
3540
3589
  import ts2 from "typescript";
3541
3590
 
3542
3591
  // ../../packages/module-contract/src/module-registry.ts
3543
- import { z as z5 } from "zod";
3544
- var projectSlugSchema2 = z5.string().refine((value) => validateSlug(value).valid, "must be a valid project slug");
3545
- var registryEnhancementSchema = z5.object({
3592
+ import { z as z6 } from "zod";
3593
+ var projectSlugSchema2 = z6.string().refine((value) => validateSlug(value).valid, "must be a valid project slug");
3594
+ var registryEnhancementSchema = z6.object({
3546
3595
  peer: moduleNameSchema,
3547
- active: z5.boolean()
3596
+ active: z6.boolean()
3548
3597
  }).strict();
3549
- var registryModuleSchema = z5.object({
3598
+ var registryModuleSchema = z6.object({
3550
3599
  name: moduleNameSchema,
3551
3600
  /** Installed version as recorded by the rail (non-empty string). */
3552
- version: z5.string().min(1),
3601
+ version: z6.string().min(1),
3553
3602
  home: projectSlugSchema2,
3554
3603
  kind: moduleKindSchema,
3555
- mounts: z5.array(mountSchema),
3556
- enhancements: z5.array(registryEnhancementSchema)
3604
+ mounts: z6.array(mountSchema),
3605
+ enhancements: z6.array(registryEnhancementSchema)
3557
3606
  }).strict().superRefine((mod, ctx) => {
3558
3607
  for (let i = 0; i < mod.enhancements.length; i += 1) {
3559
3608
  if (mod.enhancements[i].peer === mod.name) {
3560
3609
  ctx.addIssue({
3561
- code: z5.ZodIssueCode.custom,
3610
+ code: z6.ZodIssueCode.custom,
3562
3611
  message: "enhancement peer must not equal the module's own name",
3563
3612
  path: ["enhancements", i, "peer"]
3564
3613
  });
3565
3614
  }
3566
3615
  }
3567
3616
  });
3568
- var generatedModuleRegistrySchema = z5.object({
3569
- registryVersion: z5.literal(1),
3617
+ var generatedModuleRegistrySchema = z6.object({
3618
+ registryVersion: z6.literal(1),
3570
3619
  /** Rail run id, or `"manual"` for fixtures. */
3571
- generatedBy: z5.string().min(1),
3572
- modules: z5.array(registryModuleSchema)
3620
+ generatedBy: z6.string().min(1),
3621
+ modules: z6.array(registryModuleSchema)
3573
3622
  }).strict().superRefine((registry, ctx) => {
3574
3623
  const seen = /* @__PURE__ */ new Set();
3575
3624
  for (let i = 0; i < registry.modules.length; i += 1) {
3576
3625
  const name = registry.modules[i].name;
3577
3626
  if (seen.has(name)) {
3578
3627
  ctx.addIssue({
3579
- code: z5.ZodIssueCode.custom,
3628
+ code: z6.ZodIssueCode.custom,
3580
3629
  message: `duplicate module name "${name}"`,
3581
3630
  path: ["modules", i, "name"]
3582
3631
  });
@@ -3947,171 +3996,9 @@ function analyzeClientSafeEntryImports(sourceText, fileName) {
3947
3996
  return analyzeClientSafeEntryImportsWithTs(ts3, sourceText, fileName);
3948
3997
  }
3949
3998
 
3950
- // ../../packages/lib/src/shared/module-composition.ts
3951
- var moduleRefSchema = z6.object({
3952
- homeProjectId: z6.string().uuid(),
3953
- moduleName: moduleNameSchema
3954
- });
3955
- var creationSourceBlankSchema = z6.object({
3956
- type: z6.literal("blank")
3957
- });
3958
- var creationSourceBlueprintSchema = z6.object({
3959
- type: z6.literal("blueprint"),
3960
- /** Internal template/fork project id — keep template naming in identifiers. */
3961
- blueprintProjectId: z6.string().uuid(),
3962
- /**
3963
- * `null`/absent means every feature Module. `[]` is refused: the platform
3964
- * serialises it to an empty `NEXT_PUBLIC_STARDECK_ENABLED_MODULES`, which the
3965
- * enablement parser reads back as "all" (`module-enablement.ts`) — the
3966
- * opposite of what an empty selection means.
3967
- */
3968
- enabledModules: z6.array(moduleNameSchema).min(1, "Enable at least one Module").max(100).nullable().optional()
3969
- });
3970
- var creationSourceModulesSchema = z6.object({
3971
- type: z6.literal("modules"),
3972
- requested: z6.array(moduleRefSchema).min(1)
3973
- });
3974
- var creationSourceSchema = z6.discriminatedUnion("type", [
3975
- creationSourceBlankSchema,
3976
- creationSourceBlueprintSchema,
3977
- creationSourceModulesSchema
3978
- ]);
3979
- var moduleCompositionStatusSchema = z6.enum([
3980
- "pending",
3981
- "installing",
3982
- "ready",
3983
- "partial",
3984
- "failed"
3985
- ]);
3986
- var resolvedModuleSnapshotSchema = moduleRefSchema.extend({
3987
- version: z6.string().min(1),
3988
- homeCommitSha: z6.string().min(1)
3989
- });
3990
- var moduleInstallHandoffSchema = z6.object({
3991
- moduleName: z6.string().min(1),
3992
- homeProjectId: z6.string().uuid(),
3993
- installedVersion: z6.string().min(1),
3994
- baselineSha: z6.string().min(1),
3995
- mounts: z6.array(
3996
- z6.object({
3997
- group: z6.string(),
3998
- segment: z6.string()
3999
- })
4000
- ),
4001
- integration: z6.array(z6.string()),
4002
- workflows: z6.array(z6.string()),
4003
- prunedEnhancements: z6.array(z6.string()),
4004
- restoredEnhancements: z6.array(z6.string()).optional(),
4005
- permissions: z6.array(
4006
- z6.object({
4007
- key: z6.string().min(1),
4008
- description: z6.string().min(1)
4009
- })
4010
- ).optional()
4011
- });
4012
- var moduleCompositionRunAttemptSchema = z6.object({
4013
- module: moduleRefSchema,
4014
- runId: z6.string().min(1),
4015
- status: z6.string().min(1),
4016
- schemaApplied: z6.boolean(),
4017
- error: z6.string().optional(),
4018
- /** Prefer full ModuleInstallHandoff; identity fields optional for older rows. */
4019
- handoff: moduleInstallHandoffSchema.partial({ moduleName: true, homeProjectId: true }).optional()
4020
- });
4021
- var initialModuleCompositionSchema = z6.object({
4022
- version: z6.literal(1),
4023
- status: moduleCompositionStatusSchema,
4024
- requested: z6.array(moduleRefSchema).min(1),
4025
- resolved: z6.array(resolvedModuleSnapshotSchema),
4026
- installOrder: z6.array(moduleRefSchema),
4027
- runs: z6.array(moduleCompositionRunAttemptSchema).default([]),
4028
- /**
4029
- * True when any failed/partial attempt applied schema before failing.
4030
- * Never claim transactional rollback across git/schema.
4031
- */
4032
- schemaApplied: z6.boolean().default(false),
4033
- /**
4034
- * Present when retry would silently change pinned home SHAs — caller must
4035
- * reconfirm the new plan rather than continue.
4036
- */
4037
- reconfirmationRequired: z6.object({
4038
- reason: z6.string(),
4039
- previous: z6.array(resolvedModuleSnapshotSchema),
4040
- next: z6.array(resolvedModuleSnapshotSchema)
4041
- }).optional(),
4042
- error: z6.string().optional(),
4043
- /**
4044
- * First-turn system handoff lifecycle. Absent/`pending` means inject when
4045
- * status is ready; `consumed` means a successful agent turn already used it.
4046
- */
4047
- systemHandoffStatus: z6.enum(["pending", "consumed"]).optional(),
4048
- /**
4049
- * CAS ownership token for concurrent retry attempts. Only the attempt that
4050
- * currently owns this value may reset or compensate durable composition state.
4051
- */
4052
- retryAttemptId: z6.string().min(1).optional(),
4053
- /**
4054
- * When the current `installing` attempt was claimed. A setup run that dies
4055
- * with its host (a killed Vercel instance) leaves the composition `installing`
4056
- * forever, and the creating page's poll only stops on ready/partial/failed —
4057
- * so staleness, not a queue's stall detector, is what settles it. Absent on
4058
- * rows claimed before this field existed; treated as stale.
4059
- */
4060
- attemptStartedAt: z6.string().datetime().optional(),
4061
- /** Outside-module files the agent changed during connection work. */
4062
- integrationFiles: z6.array(z6.string()).optional(),
4063
- verificationSummary: z6.string().optional()
4064
- });
4065
- var MODULE_SETUP_ATTEMPT_STALE_MS = 10 * 60 * 1e3;
4066
-
4067
- // ../../packages/lib/src/shared/compose-inputs.ts
4068
- import { z as z7 } from "zod";
4069
- var COMPOSE_INPUTS_PATH = "apps/web/stardeck.compose.json";
4070
- var composeInputsSchema = z7.strictObject({
4071
- connectedStores: z7.array(
4072
- z7.strictObject({
4073
- storeId: z7.string().min(1),
4074
- slug: z7.string().min(1),
4075
- // A row with an empty binding key must not fail a deploy: the rail
4076
- // treats falsy as "no binding key", so normalise rather than reject.
4077
- bindingKey: z7.string().nullish().transform((value) => value || null),
4078
- accessLevel: z7.enum(["read", "write", "admin"]),
4079
- // Defaulted so a file written before the platform emitted store types
4080
- // (database rows only) still parses. The platform starts writing
4081
- // storage rows, with `type` set, once forks run a compose that reads it.
4082
- type: z7.enum(["database", "storage"]).default("database")
4083
- })
4084
- ).default([]),
4085
- installs: z7.record(
4086
- z7.string(),
4087
- z7.strictObject({
4088
- installedVersion: z7.string().min(1),
4089
- datastoreBindings: z7.record(z7.string(), z7.string()).nullish()
4090
- })
4091
- ).default({})
4092
- });
4093
- var STUB_IGNORE_PATH = "apps/web/src/app/.gitignore";
4094
- var STUB_IGNORE_HEADER = "# Generated by stardeck-compose \u2014 Module-rail stubs are build outputs; do not edit or commit them.";
4095
- function escapeStubIgnorePattern(pattern) {
4096
- const escaped = pattern.replace(/[\\[\]*?]/g, (char) => `\\${char}`);
4097
- return /^[#!]/.test(escaped) ? `\\${escaped}` : escaped;
4098
- }
4099
- function buildStubIgnore(artifactPaths) {
4100
- const prefix = "apps/web/src/app/";
4101
- const entries = artifactPaths.filter((artifact) => artifact.startsWith(prefix)).map((artifact) => escapeStubIgnorePattern(`/${artifact.slice(prefix.length)}`)).sort();
4102
- return [STUB_IGNORE_HEADER, ...entries].join("\n") + "\n";
4103
- }
4104
-
4105
- // ../../packages/lib/src/shared/module-enablement.ts
4106
- function isModuleEnabledByList(raw, name, kindOf) {
4107
- if (!raw) return true;
4108
- const kind = kindOf(name);
4109
- if (kind != null && kind !== "feature") return true;
4110
- return raw.split(",").includes(name);
4111
- }
4112
-
4113
3999
  // ../../packages/lib/src/shared/platform-owned-paths.ts
4114
4000
  var APP_WEB_PREFIX = "apps/web/";
4001
+ var BLUEPRINT_LINT_MARKER = `${APP_WEB_PREFIX}eslint-rules/module-boundaries.mjs`;
4115
4002
  var MODULE_STUB_MARKER = "@stardeck-module-rail-generated";
4116
4003
  var ENABLED_MODULES_ENV = "NEXT_PUBLIC_STARDECK_ENABLED_MODULES";
4117
4004
  var HEADLESS_MOUNTS_ENV = "NEXT_PUBLIC_STARDECK_HEADLESS_MOUNTS";
@@ -6036,6 +5923,52 @@ function rejectPseudoModuleEntries(entries, parentDir, label) {
6036
5923
  return dirs;
6037
5924
  }
6038
5925
 
5926
+ // ../../packages/lib/src/shared/module-composition.ts
5927
+ import { z as z7 } from "zod";
5928
+ var creationSourceBlankSchema = z7.object({
5929
+ type: z7.literal("blank")
5930
+ });
5931
+ var creationSourceBlueprintSchema = z7.object({
5932
+ type: z7.literal("blueprint"),
5933
+ /** Internal template/fork project id — keep template naming in identifiers. */
5934
+ blueprintProjectId: z7.string().uuid(),
5935
+ /**
5936
+ * `null`/absent means every feature Module: the platform stores that as the
5937
+ * explicit list of feature Modules in the published Blueprint version the fork
5938
+ * is cut from, so a Module a later Blueprint update adds arrives off. `[]` is
5939
+ * refused: the platform serialises it to an empty
5940
+ * `NEXT_PUBLIC_STARDECK_ENABLED_MODULES`, which the enablement parser reads
5941
+ * back as "all" (`module-enablement.ts`) — the opposite of an empty selection.
5942
+ */
5943
+ enabledModules: z7.array(moduleNameSchema).min(1, "Enable at least one Module").max(100).nullable().optional()
5944
+ });
5945
+ var creationSourceSchema = z7.discriminatedUnion("type", [
5946
+ creationSourceBlankSchema,
5947
+ creationSourceBlueprintSchema
5948
+ ]);
5949
+ var moduleInstallHandoffSchema = z7.object({
5950
+ moduleName: z7.string().min(1),
5951
+ homeProjectId: z7.string().uuid(),
5952
+ installedVersion: z7.string().min(1),
5953
+ baselineSha: z7.string().min(1),
5954
+ mounts: z7.array(
5955
+ z7.object({
5956
+ group: z7.string(),
5957
+ segment: z7.string()
5958
+ })
5959
+ ),
5960
+ integration: z7.array(z7.string()),
5961
+ workflows: z7.array(z7.string()),
5962
+ prunedEnhancements: z7.array(z7.string()),
5963
+ restoredEnhancements: z7.array(z7.string()).optional(),
5964
+ permissions: z7.array(
5965
+ z7.object({
5966
+ key: z7.string().min(1),
5967
+ description: z7.string().min(1)
5968
+ })
5969
+ ).optional()
5970
+ });
5971
+
6039
5972
  // ../../packages/lib/src/shared/schema-ops.ts
6040
5973
  import { z as z8 } from "zod";
6041
5974
  var columnSpecSchema = z8.object({
@@ -7499,6 +7432,9 @@ async function reconcileCompositionArtifacts(context, options = {}) {
7499
7432
  var HOME_I18N_SUBSTRATE_DIR = `${APP_PACKAGE_DIR}/src/lib/i18n/substrate`;
7500
7433
  var HOME_I18N_MESSAGES_DIR = `${APP_PACKAGE_DIR}/src/lib/i18n/messages`;
7501
7434
 
7435
+ // src/index.ts
7436
+ import ts6 from "typescript";
7437
+
7502
7438
  // src/module-boundaries.ts
7503
7439
  import fs2 from "fs";
7504
7440
  import path2 from "path";
@@ -7768,6 +7704,41 @@ function assertNoAppLayerImporters(cwd, excluded) {
7768
7704
  );
7769
7705
  }
7770
7706
  }
7707
+ var TSCONFIG_PATH = "apps/web/tsconfig.json";
7708
+ var BUILD_TSCONFIG_PATH = "apps/web/tsconfig.build.gen.json";
7709
+ var BUILD_TSCONFIG_HEADER = "// Generated by stardeck-compose \u2014 the tsconfig `next build` type-checks. Do not edit or commit.";
7710
+ function writeBuildTsconfig(cwd, excluded) {
7711
+ const baseFile = path3.join(cwd, TSCONFIG_PATH);
7712
+ if (!fs3.existsSync(path3.join(cwd, MODULES_DIR2)) || !fs3.existsSync(baseFile)) return;
7713
+ const base = ts6.readConfigFile(baseFile, ts6.sys.readFile);
7714
+ if (base.error) {
7715
+ throw new Error(
7716
+ `[compose] cannot read ${TSCONFIG_PATH}: ${ts6.flattenDiagnosticMessageText(base.error.messageText, "\n")}`
7717
+ );
7718
+ }
7719
+ const baseExclude = base.config?.exclude ?? ["node_modules"];
7720
+ const modulesDir = path3.posix.relative("apps/web", MODULES_DIR2);
7721
+ const config = {
7722
+ extends: "./tsconfig.json",
7723
+ exclude: [
7724
+ ...baseExclude,
7725
+ ...excluded.flatMap((name) => [
7726
+ `${modulesDir}/${name}`,
7727
+ `${modulesDir}/*/enhancements/${name}`
7728
+ ])
7729
+ ]
7730
+ };
7731
+ const content = `${BUILD_TSCONFIG_HEADER}
7732
+ ${JSON.stringify(config, null, 2)}
7733
+ `;
7734
+ const file = path3.join(cwd, BUILD_TSCONFIG_PATH);
7735
+ if (!fs3.existsSync(file) || fs3.readFileSync(file, "utf8") !== content) {
7736
+ fs3.writeFileSync(file, content);
7737
+ }
7738
+ console.log(
7739
+ `[compose] ${BUILD_TSCONFIG_PATH} excludes ${excluded.length} Module(s): ${excluded.join(",") || "none"}`
7740
+ );
7741
+ }
7771
7742
  function removeDir(absolute) {
7772
7743
  if (!fs3.existsSync(absolute)) return false;
7773
7744
  fs3.rmSync(absolute, { recursive: true, force: true });
@@ -7815,6 +7786,7 @@ async function compose(options) {
7815
7786
  skipComposeInputs: true
7816
7787
  });
7817
7788
  const gitignoreEntries = options.enabled ? null : writeStubGitignore(options.cwd, result.artifactPaths);
7789
+ writeBuildTsconfig(options.cwd, result.excludedModules);
7818
7790
  let pruned = 0;
7819
7791
  if (options.prune) {
7820
7792
  for (const name of result.excludedModules) {
@@ -7836,6 +7808,7 @@ async function compose(options) {
7836
7808
  };
7837
7809
  }
7838
7810
  export {
7811
+ BUILD_TSCONFIG_PATH,
7839
7812
  COMPOSE_INPUTS_PATH,
7840
7813
  assertSupportedTypescript,
7841
7814
  compose,