@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/SKILL.md CHANGED
@@ -18,6 +18,12 @@ Compose turns that declaration into the code Next.js actually builds:
18
18
  per composed Module that ships `server/registrations.ts`
19
19
  - one `page.tsx` / `route.ts` stub under `apps/web/src/app/**` per declared
20
20
  mount and endpoint
21
+ - `apps/web/tsconfig.build.gen.json` (apps with a Module tree only) — extends
22
+ `tsconfig.json` and excludes every Module `--enabled` left out, plus each
23
+ kept Module's `enhancements/<excluded>/`. Their source stays on disk but their
24
+ stubs are gone, so `next build` must type-check through this file
25
+ (`typescript.tsconfigPath` in `next.config.ts`), never through `tsconfig.json`.
26
+ Gitignore it; do not hand-maintain an exclude list.
21
27
 
22
28
  ## Running it
23
29
 
@@ -69,7 +75,9 @@ a silent fallback. It holds deployment facts: gitignore it (`apps/web/.gitignore
69
75
  installed Module. Library and contract Modules are always composed. A name
70
76
  that is not installed fails with exit 1. Falls back to
71
77
  `NEXT_PUBLIC_STARDECK_ENABLED_MODULES` (how the platform passes the selection
72
- at deploy time); the flag wins.
78
+ at deploy time); the flag wins. A Module whose `module.json` sets
79
+ `"defaultEnabled": false` is still composed when `--enabled` is unset: the key
80
+ only tells the platform to leave it off when it creates a Blueprint fork.
73
81
  - `--prune` — after composing, delete the source of every excluded Module, plus
74
82
  any enhancement slice an enabled Module keeps for an excluded peer (the slice
75
83
  imports its peer, so leaving it breaks `next build`). Requires `--enabled`,
package/dist/cli.js CHANGED
@@ -2354,7 +2354,13 @@ var moduleManifestSchema = import_zod.z.object({
2354
2354
  * `module-boundaries/ui-layer` lint rule enforces it — so a fork may
2355
2355
  * soft-eject them. See docs/module-ui-soft-eject-plan.md.
2356
2356
  */
2357
- ui: import_zod.z.object({ separable: import_zod.z.boolean() }).strict().optional()
2357
+ ui: import_zod.z.object({ separable: import_zod.z.boolean() }).strict().optional(),
2358
+ /**
2359
+ * `false` keeps a feature Module off in a new Blueprint fork unless the
2360
+ * creator ticks it: the fork picker leaves it unchecked and "every Module"
2361
+ * is written out without it. Omitted means on. Library Modules are always on.
2362
+ */
2363
+ defaultEnabled: import_zod.z.boolean().optional()
2358
2364
  }).strict().superRefine((manifest, ctx) => {
2359
2365
  if (manifest.uses.includes(manifest.name)) {
2360
2366
  ctx.addIssue({
@@ -3889,6 +3895,7 @@ function analyzeClientSafeEntryImports(sourceText, fileName) {
3889
3895
 
3890
3896
  // ../../packages/lib/src/shared/platform-owned-paths.ts
3891
3897
  var APP_WEB_PREFIX = "apps/web/";
3898
+ var BLUEPRINT_LINT_MARKER = `${APP_WEB_PREFIX}eslint-rules/module-boundaries.mjs`;
3892
3899
  var MODULE_STUB_MARKER = "@stardeck-module-rail-generated";
3893
3900
  var ENABLED_MODULES_ENV = "NEXT_PUBLIC_STARDECK_ENABLED_MODULES";
3894
3901
  var HEADLESS_MOUNTS_ENV = "NEXT_PUBLIC_STARDECK_HEADLESS_MOUNTS";
@@ -3901,7 +3908,7 @@ var MODULES_GEN_PATH = `${APP_WEB_PREFIX}src/modules.gen.ts`;
3901
3908
  var APP_EXTENSIONS_PATH = `${APP_WEB_PREFIX}src/lib/modules/app-extensions.ts`;
3902
3909
 
3903
3910
  // src/cli.ts
3904
- var import_typescript6 = __toESM(require("typescript"));
3911
+ var import_typescript7 = __toESM(require("typescript"));
3905
3912
  var import_node_path4 = __toESM(require("path"));
3906
3913
 
3907
3914
  // src/index.ts
@@ -3966,147 +3973,29 @@ function shellQuote(value) {
3966
3973
  return `'${value.replace(/'/g, `'\\''`)}'`;
3967
3974
  }
3968
3975
 
3969
- // ../../packages/lib/src/shared/module-composition.ts
3970
- var import_zod6 = require("zod");
3971
- var moduleRefSchema = import_zod6.z.object({
3972
- homeProjectId: import_zod6.z.string().uuid(),
3973
- moduleName: moduleNameSchema
3974
- });
3975
- var creationSourceBlankSchema = import_zod6.z.object({
3976
- type: import_zod6.z.literal("blank")
3977
- });
3978
- var creationSourceBlueprintSchema = import_zod6.z.object({
3979
- type: import_zod6.z.literal("blueprint"),
3980
- /** Internal template/fork project id — keep template naming in identifiers. */
3981
- blueprintProjectId: import_zod6.z.string().uuid(),
3982
- /**
3983
- * `null`/absent means every feature Module. `[]` is refused: the platform
3984
- * serialises it to an empty `NEXT_PUBLIC_STARDECK_ENABLED_MODULES`, which the
3985
- * enablement parser reads back as "all" (`module-enablement.ts`) — the
3986
- * opposite of what an empty selection means.
3987
- */
3988
- enabledModules: import_zod6.z.array(moduleNameSchema).min(1, "Enable at least one Module").max(100).nullable().optional()
3989
- });
3990
- var creationSourceModulesSchema = import_zod6.z.object({
3991
- type: import_zod6.z.literal("modules"),
3992
- requested: import_zod6.z.array(moduleRefSchema).min(1)
3993
- });
3994
- var creationSourceSchema = import_zod6.z.discriminatedUnion("type", [
3995
- creationSourceBlankSchema,
3996
- creationSourceBlueprintSchema,
3997
- creationSourceModulesSchema
3998
- ]);
3999
- var moduleCompositionStatusSchema = import_zod6.z.enum([
4000
- "pending",
4001
- "installing",
4002
- "ready",
4003
- "partial",
4004
- "failed"
4005
- ]);
4006
- var resolvedModuleSnapshotSchema = moduleRefSchema.extend({
4007
- version: import_zod6.z.string().min(1),
4008
- homeCommitSha: import_zod6.z.string().min(1)
4009
- });
4010
- var moduleInstallHandoffSchema = import_zod6.z.object({
4011
- moduleName: import_zod6.z.string().min(1),
4012
- homeProjectId: import_zod6.z.string().uuid(),
4013
- installedVersion: import_zod6.z.string().min(1),
4014
- baselineSha: import_zod6.z.string().min(1),
4015
- mounts: import_zod6.z.array(
4016
- import_zod6.z.object({
4017
- group: import_zod6.z.string(),
4018
- segment: import_zod6.z.string()
4019
- })
4020
- ),
4021
- integration: import_zod6.z.array(import_zod6.z.string()),
4022
- workflows: import_zod6.z.array(import_zod6.z.string()),
4023
- prunedEnhancements: import_zod6.z.array(import_zod6.z.string()),
4024
- restoredEnhancements: import_zod6.z.array(import_zod6.z.string()).optional(),
4025
- permissions: import_zod6.z.array(
4026
- import_zod6.z.object({
4027
- key: import_zod6.z.string().min(1),
4028
- description: import_zod6.z.string().min(1)
4029
- })
4030
- ).optional()
4031
- });
4032
- var moduleCompositionRunAttemptSchema = import_zod6.z.object({
4033
- module: moduleRefSchema,
4034
- runId: import_zod6.z.string().min(1),
4035
- status: import_zod6.z.string().min(1),
4036
- schemaApplied: import_zod6.z.boolean(),
4037
- error: import_zod6.z.string().optional(),
4038
- /** Prefer full ModuleInstallHandoff; identity fields optional for older rows. */
4039
- handoff: moduleInstallHandoffSchema.partial({ moduleName: true, homeProjectId: true }).optional()
4040
- });
4041
- var initialModuleCompositionSchema = import_zod6.z.object({
4042
- version: import_zod6.z.literal(1),
4043
- status: moduleCompositionStatusSchema,
4044
- requested: import_zod6.z.array(moduleRefSchema).min(1),
4045
- resolved: import_zod6.z.array(resolvedModuleSnapshotSchema),
4046
- installOrder: import_zod6.z.array(moduleRefSchema),
4047
- runs: import_zod6.z.array(moduleCompositionRunAttemptSchema).default([]),
4048
- /**
4049
- * True when any failed/partial attempt applied schema before failing.
4050
- * Never claim transactional rollback across git/schema.
4051
- */
4052
- schemaApplied: import_zod6.z.boolean().default(false),
4053
- /**
4054
- * Present when retry would silently change pinned home SHAs — caller must
4055
- * reconfirm the new plan rather than continue.
4056
- */
4057
- reconfirmationRequired: import_zod6.z.object({
4058
- reason: import_zod6.z.string(),
4059
- previous: import_zod6.z.array(resolvedModuleSnapshotSchema),
4060
- next: import_zod6.z.array(resolvedModuleSnapshotSchema)
4061
- }).optional(),
4062
- error: import_zod6.z.string().optional(),
4063
- /**
4064
- * First-turn system handoff lifecycle. Absent/`pending` means inject when
4065
- * status is ready; `consumed` means a successful agent turn already used it.
4066
- */
4067
- systemHandoffStatus: import_zod6.z.enum(["pending", "consumed"]).optional(),
4068
- /**
4069
- * CAS ownership token for concurrent retry attempts. Only the attempt that
4070
- * currently owns this value may reset or compensate durable composition state.
4071
- */
4072
- retryAttemptId: import_zod6.z.string().min(1).optional(),
4073
- /**
4074
- * When the current `installing` attempt was claimed. A setup run that dies
4075
- * with its host (a killed Vercel instance) leaves the composition `installing`
4076
- * forever, and the creating page's poll only stops on ready/partial/failed —
4077
- * so staleness, not a queue's stall detector, is what settles it. Absent on
4078
- * rows claimed before this field existed; treated as stale.
4079
- */
4080
- attemptStartedAt: import_zod6.z.string().datetime().optional(),
4081
- /** Outside-module files the agent changed during connection work. */
4082
- integrationFiles: import_zod6.z.array(import_zod6.z.string()).optional(),
4083
- verificationSummary: import_zod6.z.string().optional()
4084
- });
4085
- var MODULE_SETUP_ATTEMPT_STALE_MS = 10 * 60 * 1e3;
4086
-
4087
3976
  // ../../packages/lib/src/shared/compose-inputs.ts
4088
- var import_zod7 = require("zod");
3977
+ var import_zod6 = require("zod");
4089
3978
  var COMPOSE_INPUTS_PATH = "apps/web/stardeck.compose.json";
4090
- var composeInputsSchema = import_zod7.z.strictObject({
4091
- connectedStores: import_zod7.z.array(
4092
- import_zod7.z.strictObject({
4093
- storeId: import_zod7.z.string().min(1),
4094
- slug: import_zod7.z.string().min(1),
3979
+ var composeInputsSchema = import_zod6.z.strictObject({
3980
+ connectedStores: import_zod6.z.array(
3981
+ import_zod6.z.strictObject({
3982
+ storeId: import_zod6.z.string().min(1),
3983
+ slug: import_zod6.z.string().min(1),
4095
3984
  // A row with an empty binding key must not fail a deploy: the rail
4096
3985
  // treats falsy as "no binding key", so normalise rather than reject.
4097
- bindingKey: import_zod7.z.string().nullish().transform((value) => value || null),
4098
- accessLevel: import_zod7.z.enum(["read", "write", "admin"]),
3986
+ bindingKey: import_zod6.z.string().nullish().transform((value) => value || null),
3987
+ accessLevel: import_zod6.z.enum(["read", "write", "admin"]),
4099
3988
  // Defaulted so a file written before the platform emitted store types
4100
3989
  // (database rows only) still parses. The platform starts writing
4101
3990
  // storage rows, with `type` set, once forks run a compose that reads it.
4102
- type: import_zod7.z.enum(["database", "storage"]).default("database")
3991
+ type: import_zod6.z.enum(["database", "storage"]).default("database")
4103
3992
  })
4104
3993
  ).default([]),
4105
- installs: import_zod7.z.record(
4106
- import_zod7.z.string(),
4107
- import_zod7.z.strictObject({
4108
- installedVersion: import_zod7.z.string().min(1),
4109
- datastoreBindings: import_zod7.z.record(import_zod7.z.string(), import_zod7.z.string()).nullish()
3994
+ installs: import_zod6.z.record(
3995
+ import_zod6.z.string(),
3996
+ import_zod6.z.strictObject({
3997
+ installedVersion: import_zod6.z.string().min(1),
3998
+ datastoreBindings: import_zod6.z.record(import_zod6.z.string(), import_zod6.z.string()).nullish()
4110
3999
  })
4111
4000
  ).default({})
4112
4001
  });
@@ -6043,6 +5932,52 @@ function rejectPseudoModuleEntries(entries, parentDir, label) {
6043
5932
  return dirs;
6044
5933
  }
6045
5934
 
5935
+ // ../../packages/lib/src/shared/module-composition.ts
5936
+ var import_zod7 = require("zod");
5937
+ var creationSourceBlankSchema = import_zod7.z.object({
5938
+ type: import_zod7.z.literal("blank")
5939
+ });
5940
+ var creationSourceBlueprintSchema = import_zod7.z.object({
5941
+ type: import_zod7.z.literal("blueprint"),
5942
+ /** Internal template/fork project id — keep template naming in identifiers. */
5943
+ blueprintProjectId: import_zod7.z.string().uuid(),
5944
+ /**
5945
+ * `null`/absent means every feature Module: the platform stores that as the
5946
+ * explicit list of feature Modules in the published Blueprint version the fork
5947
+ * is cut from, so a Module a later Blueprint update adds arrives off. `[]` is
5948
+ * refused: the platform serialises it to an empty
5949
+ * `NEXT_PUBLIC_STARDECK_ENABLED_MODULES`, which the enablement parser reads
5950
+ * back as "all" (`module-enablement.ts`) — the opposite of an empty selection.
5951
+ */
5952
+ enabledModules: import_zod7.z.array(moduleNameSchema).min(1, "Enable at least one Module").max(100).nullable().optional()
5953
+ });
5954
+ var creationSourceSchema = import_zod7.z.discriminatedUnion("type", [
5955
+ creationSourceBlankSchema,
5956
+ creationSourceBlueprintSchema
5957
+ ]);
5958
+ var moduleInstallHandoffSchema = import_zod7.z.object({
5959
+ moduleName: import_zod7.z.string().min(1),
5960
+ homeProjectId: import_zod7.z.string().uuid(),
5961
+ installedVersion: import_zod7.z.string().min(1),
5962
+ baselineSha: import_zod7.z.string().min(1),
5963
+ mounts: import_zod7.z.array(
5964
+ import_zod7.z.object({
5965
+ group: import_zod7.z.string(),
5966
+ segment: import_zod7.z.string()
5967
+ })
5968
+ ),
5969
+ integration: import_zod7.z.array(import_zod7.z.string()),
5970
+ workflows: import_zod7.z.array(import_zod7.z.string()),
5971
+ prunedEnhancements: import_zod7.z.array(import_zod7.z.string()),
5972
+ restoredEnhancements: import_zod7.z.array(import_zod7.z.string()).optional(),
5973
+ permissions: import_zod7.z.array(
5974
+ import_zod7.z.object({
5975
+ key: import_zod7.z.string().min(1),
5976
+ description: import_zod7.z.string().min(1)
5977
+ })
5978
+ ).optional()
5979
+ });
5980
+
6046
5981
  // ../../packages/lib/src/shared/schema-ops.ts
6047
5982
  var import_zod8 = require("zod");
6048
5983
  var columnSpecSchema = import_zod8.z.object({
@@ -7506,6 +7441,9 @@ async function reconcileCompositionArtifacts(context, options = {}) {
7506
7441
  var HOME_I18N_SUBSTRATE_DIR = `${APP_PACKAGE_DIR}/src/lib/i18n/substrate`;
7507
7442
  var HOME_I18N_MESSAGES_DIR = `${APP_PACKAGE_DIR}/src/lib/i18n/messages`;
7508
7443
 
7444
+ // src/index.ts
7445
+ var import_typescript6 = __toESM(require("typescript"));
7446
+
7509
7447
  // src/module-boundaries.ts
7510
7448
  var import_node_fs2 = __toESM(require("fs"));
7511
7449
  var import_node_path2 = __toESM(require("path"));
@@ -7775,6 +7713,41 @@ function assertNoAppLayerImporters(cwd, excluded) {
7775
7713
  );
7776
7714
  }
7777
7715
  }
7716
+ var TSCONFIG_PATH = "apps/web/tsconfig.json";
7717
+ var BUILD_TSCONFIG_PATH = "apps/web/tsconfig.build.gen.json";
7718
+ var BUILD_TSCONFIG_HEADER = "// Generated by stardeck-compose \u2014 the tsconfig `next build` type-checks. Do not edit or commit.";
7719
+ function writeBuildTsconfig(cwd, excluded) {
7720
+ const baseFile = import_node_path3.default.join(cwd, TSCONFIG_PATH);
7721
+ if (!import_node_fs3.default.existsSync(import_node_path3.default.join(cwd, MODULES_DIR2)) || !import_node_fs3.default.existsSync(baseFile)) return;
7722
+ const base = import_typescript6.default.readConfigFile(baseFile, import_typescript6.default.sys.readFile);
7723
+ if (base.error) {
7724
+ throw new Error(
7725
+ `[compose] cannot read ${TSCONFIG_PATH}: ${import_typescript6.default.flattenDiagnosticMessageText(base.error.messageText, "\n")}`
7726
+ );
7727
+ }
7728
+ const baseExclude = base.config?.exclude ?? ["node_modules"];
7729
+ const modulesDir = import_node_path3.default.posix.relative("apps/web", MODULES_DIR2);
7730
+ const config = {
7731
+ extends: "./tsconfig.json",
7732
+ exclude: [
7733
+ ...baseExclude,
7734
+ ...excluded.flatMap((name) => [
7735
+ `${modulesDir}/${name}`,
7736
+ `${modulesDir}/*/enhancements/${name}`
7737
+ ])
7738
+ ]
7739
+ };
7740
+ const content = `${BUILD_TSCONFIG_HEADER}
7741
+ ${JSON.stringify(config, null, 2)}
7742
+ `;
7743
+ const file = import_node_path3.default.join(cwd, BUILD_TSCONFIG_PATH);
7744
+ if (!import_node_fs3.default.existsSync(file) || import_node_fs3.default.readFileSync(file, "utf8") !== content) {
7745
+ import_node_fs3.default.writeFileSync(file, content);
7746
+ }
7747
+ console.log(
7748
+ `[compose] ${BUILD_TSCONFIG_PATH} excludes ${excluded.length} Module(s): ${excluded.join(",") || "none"}`
7749
+ );
7750
+ }
7778
7751
  function removeDir(absolute) {
7779
7752
  if (!import_node_fs3.default.existsSync(absolute)) return false;
7780
7753
  import_node_fs3.default.rmSync(absolute, { recursive: true, force: true });
@@ -7822,6 +7795,7 @@ async function compose(options) {
7822
7795
  skipComposeInputs: true
7823
7796
  });
7824
7797
  const gitignoreEntries = options.enabled ? null : writeStubGitignore(options.cwd, result.artifactPaths);
7798
+ writeBuildTsconfig(options.cwd, result.excludedModules);
7825
7799
  let pruned = 0;
7826
7800
  if (options.prune) {
7827
7801
  for (const name of result.excludedModules) {
@@ -7845,7 +7819,7 @@ async function compose(options) {
7845
7819
 
7846
7820
  // src/cli.ts
7847
7821
  async function main() {
7848
- assertSupportedTypescript(import_typescript6.default.versionMajorMinor);
7822
+ assertSupportedTypescript(import_typescript7.default.versionMajorMinor);
7849
7823
  const { values } = (0, import_node_util.parseArgs)({
7850
7824
  options: {
7851
7825
  enabled: { type: "string" },
package/dist/index.d.mts CHANGED
@@ -129,10 +129,11 @@ declare function assertSupportedTypescript(versionMajorMinor: string): void;
129
129
  * rules those out today; a total helper keeps it that way).
130
130
  */
131
131
  declare function escapeGitignore(pattern: string): string;
132
+ declare const BUILD_TSCONFIG_PATH = "apps/web/tsconfig.build.gen.json";
132
133
  /**
133
134
  * Regenerate the Module-rail artifacts (the six `src/*.gen.ts` files and every
134
135
  * route/endpoint stub under `src/app`) for the app rooted at `options.cwd`.
135
136
  */
136
137
  declare function compose(options: ComposeOptions): Promise<ComposeResult>;
137
138
 
138
- export { type BoundaryViolation, COMPOSE_INPUTS_PATH, type ComposeInputs, type ComposeOptions, type ComposeResult, assertSupportedTypescript, compose, escapeGitignore, findModuleBoundaryViolations, findOptionalPeerViolations, formatBoundaryViolations, readComposeInputs };
139
+ export { BUILD_TSCONFIG_PATH, type BoundaryViolation, COMPOSE_INPUTS_PATH, type ComposeInputs, type ComposeOptions, type ComposeResult, assertSupportedTypescript, compose, escapeGitignore, findModuleBoundaryViolations, findOptionalPeerViolations, formatBoundaryViolations, readComposeInputs };
package/dist/index.d.ts CHANGED
@@ -129,10 +129,11 @@ declare function assertSupportedTypescript(versionMajorMinor: string): void;
129
129
  * rules those out today; a total helper keeps it that way).
130
130
  */
131
131
  declare function escapeGitignore(pattern: string): string;
132
+ declare const BUILD_TSCONFIG_PATH = "apps/web/tsconfig.build.gen.json";
132
133
  /**
133
134
  * Regenerate the Module-rail artifacts (the six `src/*.gen.ts` files and every
134
135
  * route/endpoint stub under `src/app`) for the app rooted at `options.cwd`.
135
136
  */
136
137
  declare function compose(options: ComposeOptions): Promise<ComposeResult>;
137
138
 
138
- export { type BoundaryViolation, COMPOSE_INPUTS_PATH, type ComposeInputs, type ComposeOptions, type ComposeResult, assertSupportedTypescript, compose, escapeGitignore, findModuleBoundaryViolations, findOptionalPeerViolations, formatBoundaryViolations, readComposeInputs };
139
+ export { BUILD_TSCONFIG_PATH, type BoundaryViolation, COMPOSE_INPUTS_PATH, type ComposeInputs, type ComposeOptions, type ComposeResult, assertSupportedTypescript, compose, escapeGitignore, findModuleBoundaryViolations, findOptionalPeerViolations, formatBoundaryViolations, readComposeInputs };