create-next-hydra 0.3.0 → 0.4.1

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.
Files changed (207) hide show
  1. package/README.md +126 -27
  2. package/dist/add-command.d.ts +11 -0
  3. package/dist/add-command.d.ts.map +1 -0
  4. package/dist/add-command.js +75 -0
  5. package/dist/addition.d.ts +67 -0
  6. package/dist/addition.d.ts.map +1 -0
  7. package/dist/addition.js +422 -0
  8. package/dist/application-git.d.ts +20 -0
  9. package/dist/application-git.d.ts.map +1 -0
  10. package/dist/application-git.js +57 -0
  11. package/dist/baseline.d.ts +22 -0
  12. package/dist/baseline.d.ts.map +1 -0
  13. package/dist/baseline.js +90 -0
  14. package/dist/cli.js +5 -18
  15. package/dist/commands.d.ts +22 -0
  16. package/dist/commands.d.ts.map +1 -0
  17. package/dist/commands.js +197 -0
  18. package/dist/composition-lint.d.ts +20 -0
  19. package/dist/composition-lint.d.ts.map +1 -0
  20. package/dist/composition-lint.js +288 -0
  21. package/dist/composition.d.ts +24 -0
  22. package/dist/composition.d.ts.map +1 -0
  23. package/dist/composition.js +420 -0
  24. package/dist/create-command.d.ts +21 -0
  25. package/dist/create-command.d.ts.map +1 -0
  26. package/dist/create-command.js +128 -0
  27. package/dist/demo-instrumentation.d.ts +5 -0
  28. package/dist/demo-instrumentation.d.ts.map +1 -0
  29. package/dist/demo-instrumentation.js +300 -0
  30. package/dist/environment.d.ts +7 -0
  31. package/dist/environment.d.ts.map +1 -0
  32. package/dist/environment.js +81 -0
  33. package/dist/errors.d.ts +156 -0
  34. package/dist/errors.d.ts.map +1 -0
  35. package/dist/errors.js +140 -0
  36. package/dist/file-policy.d.ts +6 -0
  37. package/dist/file-policy.d.ts.map +1 -0
  38. package/dist/file-policy.js +38 -0
  39. package/dist/files.d.ts +7 -0
  40. package/dist/files.d.ts.map +1 -0
  41. package/dist/files.js +66 -0
  42. package/dist/hosting.d.ts +5 -0
  43. package/dist/hosting.d.ts.map +1 -0
  44. package/dist/hosting.js +107 -0
  45. package/dist/index.d.ts +4 -6
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +17 -89
  48. package/dist/initialization.d.ts +14 -0
  49. package/dist/initialization.d.ts.map +1 -0
  50. package/dist/initialization.js +266 -0
  51. package/dist/lint-workspaces.d.ts +2 -0
  52. package/dist/lint-workspaces.d.ts.map +1 -0
  53. package/dist/lint-workspaces.js +62 -0
  54. package/dist/model.d.ts +143 -0
  55. package/dist/model.d.ts.map +1 -0
  56. package/dist/model.js +61 -0
  57. package/dist/package-catalog.d.ts +8 -0
  58. package/dist/package-catalog.d.ts.map +1 -0
  59. package/dist/package-catalog.js +61 -0
  60. package/dist/packages.d.ts +57 -0
  61. package/dist/packages.d.ts.map +1 -0
  62. package/dist/packages.js +237 -0
  63. package/dist/process-group.d.ts +5 -0
  64. package/dist/process-group.d.ts.map +1 -0
  65. package/dist/process-group.js +23 -0
  66. package/dist/reference-workspace-bindings.d.ts +38 -0
  67. package/dist/reference-workspace-bindings.d.ts.map +1 -0
  68. package/dist/reference-workspace-bindings.js +41 -0
  69. package/dist/reference-workspace.d.ts +6 -0
  70. package/dist/reference-workspace.d.ts.map +1 -0
  71. package/dist/reference-workspace.js +62 -0
  72. package/dist/registry.d.ts +187 -0
  73. package/dist/registry.d.ts.map +1 -0
  74. package/dist/registry.js +107 -0
  75. package/dist/runtime.d.ts +9 -0
  76. package/dist/runtime.d.ts.map +1 -0
  77. package/dist/runtime.js +22 -0
  78. package/dist/selection.d.ts +704 -0
  79. package/dist/selection.d.ts.map +1 -0
  80. package/dist/selection.js +424 -0
  81. package/dist/shadcn-protocol.d.ts +22 -0
  82. package/dist/shadcn-protocol.d.ts.map +1 -0
  83. package/dist/shadcn-protocol.js +13 -0
  84. package/dist/shadcn-worker.d.ts +2 -0
  85. package/dist/shadcn-worker.d.ts.map +1 -0
  86. package/dist/shadcn-worker.js +43 -0
  87. package/dist/shadcn.d.ts +22 -0
  88. package/dist/shadcn.d.ts.map +1 -0
  89. package/dist/shadcn.js +101 -0
  90. package/dist/source-changes.d.ts +55 -0
  91. package/dist/source-changes.d.ts.map +1 -0
  92. package/dist/source-changes.js +225 -0
  93. package/dist/source-inventory.d.ts +25 -0
  94. package/dist/source-inventory.d.ts.map +1 -0
  95. package/dist/source-inventory.js +212 -0
  96. package/dist/source-root.d.ts +4 -0
  97. package/dist/source-root.d.ts.map +1 -0
  98. package/dist/source-root.js +20 -0
  99. package/dist/staging.d.ts +19 -0
  100. package/dist/staging.d.ts.map +1 -0
  101. package/dist/staging.js +42 -0
  102. package/dist/tasks.d.ts +44 -0
  103. package/dist/tasks.d.ts.map +1 -0
  104. package/dist/tasks.js +137 -0
  105. package/dist/templates.d.ts +217 -0
  106. package/dist/templates.d.ts.map +1 -0
  107. package/dist/templates.js +161 -0
  108. package/dist/test-reference-workspace.d.ts +2 -0
  109. package/dist/test-reference-workspace.d.ts.map +1 -0
  110. package/dist/test-reference-workspace.js +6 -0
  111. package/dist/types.d.ts +2 -9
  112. package/dist/types.d.ts.map +1 -1
  113. package/dist/typescript-paths.d.ts +10 -0
  114. package/dist/typescript-paths.d.ts.map +1 -0
  115. package/dist/typescript-paths.js +52 -0
  116. package/dist/workspace-dependencies.d.ts +26 -0
  117. package/dist/workspace-dependencies.d.ts.map +1 -0
  118. package/dist/workspace-dependencies.js +478 -0
  119. package/dist/workspace-files.d.ts +24 -0
  120. package/dist/workspace-files.d.ts.map +1 -0
  121. package/dist/workspace-files.js +395 -0
  122. package/dist/workspace-lock.d.ts +12 -0
  123. package/dist/workspace-lock.d.ts.map +1 -0
  124. package/dist/workspace-lock.js +104 -0
  125. package/dist/workspace-snapshots.d.ts +24 -0
  126. package/dist/workspace-snapshots.d.ts.map +1 -0
  127. package/dist/workspace-snapshots.js +218 -0
  128. package/dist/workspace-sources.d.ts +27 -0
  129. package/dist/workspace-sources.d.ts.map +1 -0
  130. package/dist/workspace-sources.js +88 -0
  131. package/dist/workspace-state.d.ts +325 -0
  132. package/dist/workspace-state.d.ts.map +1 -0
  133. package/dist/workspace-state.js +571 -0
  134. package/dist/workspaces.d.ts +73 -0
  135. package/dist/workspaces.d.ts.map +1 -0
  136. package/dist/workspaces.js +396 -0
  137. package/package.json +25 -20
  138. package/schema/selection-definition.json +143 -4
  139. package/dist/clone.d.ts +0 -9
  140. package/dist/clone.d.ts.map +0 -1
  141. package/dist/clone.js +0 -19
  142. package/dist/composition/add.d.ts +0 -14
  143. package/dist/composition/add.d.ts.map +0 -1
  144. package/dist/composition/add.js +0 -484
  145. package/dist/composition/catalog.d.ts +0 -26
  146. package/dist/composition/catalog.d.ts.map +0 -1
  147. package/dist/composition/catalog.js +0 -317
  148. package/dist/composition/errors.d.ts +0 -5
  149. package/dist/composition/errors.d.ts.map +0 -1
  150. package/dist/composition/errors.js +0 -8
  151. package/dist/composition/format.d.ts +0 -6
  152. package/dist/composition/format.d.ts.map +0 -1
  153. package/dist/composition/format.js +0 -65
  154. package/dist/composition/install.d.ts +0 -16
  155. package/dist/composition/install.d.ts.map +0 -1
  156. package/dist/composition/install.js +0 -175
  157. package/dist/composition/packages.d.ts +0 -6
  158. package/dist/composition/packages.d.ts.map +0 -1
  159. package/dist/composition/packages.js +0 -55
  160. package/dist/composition/paths.d.ts +0 -4
  161. package/dist/composition/paths.d.ts.map +0 -1
  162. package/dist/composition/paths.js +0 -47
  163. package/dist/composition/planner.d.ts +0 -4
  164. package/dist/composition/planner.d.ts.map +0 -1
  165. package/dist/composition/planner.js +0 -366
  166. package/dist/composition/schema.d.ts +0 -294
  167. package/dist/composition/schema.d.ts.map +0 -1
  168. package/dist/composition/schema.js +0 -166
  169. package/dist/composition/types.d.ts +0 -111
  170. package/dist/composition/types.d.ts.map +0 -1
  171. package/dist/composition/types.js +0 -6
  172. package/dist/composition/typescript-paths.d.ts +0 -6
  173. package/dist/composition/typescript-paths.d.ts.map +0 -1
  174. package/dist/composition/typescript-paths.js +0 -157
  175. package/dist/composition/use.d.ts +0 -23
  176. package/dist/composition/use.d.ts.map +0 -1
  177. package/dist/composition/use.js +0 -187
  178. package/dist/composition/workspace.d.ts +0 -10
  179. package/dist/composition/workspace.d.ts.map +0 -1
  180. package/dist/composition/workspace.js +0 -210
  181. package/dist/constants.d.ts +0 -11
  182. package/dist/constants.d.ts.map +0 -1
  183. package/dist/constants.js +0 -39
  184. package/dist/fs-utils.d.ts +0 -9
  185. package/dist/fs-utils.d.ts.map +0 -1
  186. package/dist/fs-utils.js +0 -64
  187. package/dist/git.d.ts +0 -28
  188. package/dist/git.d.ts.map +0 -1
  189. package/dist/git.js +0 -107
  190. package/dist/instruction-renderer.d.ts +0 -17
  191. package/dist/instruction-renderer.d.ts.map +0 -1
  192. package/dist/instruction-renderer.js +0 -132
  193. package/dist/logger.d.ts +0 -13
  194. package/dist/logger.d.ts.map +0 -1
  195. package/dist/logger.js +0 -26
  196. package/dist/prompts.d.ts +0 -7
  197. package/dist/prompts.d.ts.map +0 -1
  198. package/dist/prompts.js +0 -54
  199. package/dist/sanitize.d.ts +0 -10
  200. package/dist/sanitize.d.ts.map +0 -1
  201. package/dist/sanitize.js +0 -110
  202. package/dist/scaffold.d.ts +0 -7
  203. package/dist/scaffold.d.ts.map +0 -1
  204. package/dist/scaffold.js +0 -330
  205. package/dist/version.d.ts +0 -2
  206. package/dist/version.d.ts.map +0 -1
  207. package/dist/version.js +0 -2
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # create-next-hydra
2
2
 
3
- `create-next-hydra` composes a Next Hydra workspace from one Auth Provider, one CMS Provider, one Commerce Provider, and any compatible Add-ons. It uses [ShadCN registry items](https://ui.shadcn.com/docs/registry/registry-item-json) to copy code and adds Next Hydra's stack validation and package-specific manifest changes.
3
+ `create-next-hydra` composes applications from selected Providers and compatible Add-ons. There is one shared web application: CMS is required, Auth is optional, and Commerce requires Auth. Commerce is installed as one complete package, not separately selectable catalog, cart, or checkout features. It uses [ShadCN registry items](https://ui.shadcn.com/docs/registry/registry-item-json) to copy code and adds stack validation and package-specific manifest changes.
4
4
 
5
5
  ## Create a project
6
6
 
@@ -10,67 +10,160 @@ Interactive scaffolding asks for every required Provider:
10
10
  pnpm dlx create-next-hydra@latest my-project
11
11
  ```
12
12
 
13
- For a non-interactive scaffold, provide all three Providers or a Preset:
13
+ Search is an optional Provider Slot. Selecting Algolia adds the storefront search pages, header autocomplete, proxy runtime, and `search` CLI command through registry recipes; it currently requires Commerce. CMS-only sites omit Search. The local navigation-search add-on is an alternative header binding and cannot be selected alongside Algolia.
14
+
15
+ For a non-interactive scaffold, select a Provider or explicitly omit each optional slot, or use a Preset:
14
16
 
15
17
  ```bash
16
18
  pnpm dlx create-next-hydra@latest my-project --yes \
17
19
  --auth workos \
18
20
  --cms drupal \
19
- --commerce commercetools
21
+ --commerce commercetools \
22
+ --search algolia
20
23
 
21
24
  pnpm dlx create-next-hydra@latest my-project --yes --preset standard
25
+
26
+ pnpm dlx create-next-hydra@latest content-site --yes \
27
+ --cms contentstack --without auth --without commerce --without search
22
28
  ```
23
29
 
24
30
  Use `--add-on <selection>` more than once to include compatible Add-ons. A selection can be an official shorthand, a local registry-item JSON file, a URL, a public GitHub address such as `owner/repository/item#ref`, or a configured ShadCN registry name.
25
31
 
26
- The destination must be missing or empty. If scaffolding fails, the CLI leaves the partial project in place and reports what completed and what was not attempted.
32
+ Use `--repo-url <repository>` to scaffold from another Git repository and `--ref <revision>` to select a revision. Local repository paths, including `--repo-url .`, resolve from the directory where you invoke the command. Scaffolding reads the committed revision, not uncommitted source edits.
27
33
 
28
- ## Change the maintainer workspace
34
+ After installation, the CLI prints setup instructions supplied by the selected registry items. Follow those instructions before starting the application; composition does not run external provisioning commands for you.
29
35
 
30
- The repository root is a Maintainer Workspace because it contains `next-hydra.json` and the source registry. Build the local CLI, then change a Provider or apply a Preset:
36
+ The project destination must be missing or empty. Named workspaces support safe initialization and updates instead. New applications retain the registry's existing `vercel.json` defaults and app-local skip-CI scripts. Maintainer composition commands and cache-location settings are not added to these files.
31
37
 
32
- ```bash
38
+ Projects include Portless. Run `pnpm dev` in the installed project for stable `<app>.<project>.localhost` origins and automatically allocated application ports. The package name is normalized from the destination name without renaming the folder: `My App` becomes `my-app`. The project hostname label also replaces dots/underscores with hyphens and is limited to 63 characters. Package-owned dev scripts, including API tunneling, remain intact. Local HTTPS may require Portless's one-time proxy/certificate setup; remote authentication callback allowlists must match the actual development origins. Drupal's `dev:web` command keeps Portless while pinning the internal frontend port for DDEV revalidation.
39
+
40
+ ## Initialize and update named workspaces
41
+
42
+ The preferred maintainer workflow uses definitions in `workspaces/`. New definitions are visible to Git before being committed; no parent ignore exception is needed. Compose seeds a missing workspace-local `.gitignore` with defaults that expose settings and hide materialized output, then leaves that file under your control. An existing file, including an empty one, is never replaced. No `apps` field or app-profile flag is needed: the shared web application is implicit, and selected packages extend its templates.
43
+
44
+ ```sh
45
+ pnpm --filter create-next-hydra compose cms-contentstack --copy-env
46
+ pnpm --filter create-next-hydra compose cms-contentstack
47
+ pnpm --filter create-next-hydra compose --all --copy-env
48
+ pnpm --filter create-next-hydra compose cms-contentstack --watch
49
+ pnpm --filter create-next-hydra compose --all --check
50
+ pnpm --filter create-next-hydra compose cms-contentstack --explain 'apps/web/app/[locale]/layout.tsx'
51
+ pnpm --dir workspaces/cms-contentstack typecheck
52
+ pnpm --dir workspaces/cms-contentstack dev
53
+ ```
54
+
55
+ The same command initializes or updates the same folder. All files are physical copies, including ordinary source and template output. Refresh preserves caches and does not reinstall unchanged dependencies. Unowned local files remain untouched and do not block refresh or `--check`. Conflicting edits to managed files and unowned files occupying new output targets still block changes. `--diff` lists eligible unowned files for optional reconciliation without adopting them. `--no-install` lets you manage installation yourself with pnpm inside the workspace, `--offline` uses the local store, and `--copy-env` explicitly copies only missing local env files. Omit it for deployment. Watch uses the same synchronization and installation policy on every refresh; it does not run an application server. External provisioning is separate.
56
+
57
+ `--check` verifies that composed files match the current definition and source. It does not inspect `.npmrc` or installed dependencies, and it does not run pnpm. A files-only composition can pass Check before dependencies are installed. Install dependencies separately before running application commands.
58
+
59
+ See [Named workspaces](../../workspaces/README.md) for the four definitions, stable Portless hostnames, deployment settings, interruption recovery, and reconciliation instructions. Definitions, workspace-owned `.gitignore` files, optional READMEs, app-local `vercel.json` settings are tracked; materialized runtime manifests, installed files and applied state are ignored. Compose preserves these ignore rules rather than regenerating them. The workspace name determines each Next app's `<app>.<workspace>.localhost` hostname (with Portless's branch prefix inside a Git worktree).
60
+
61
+ Adding a definition or changing its selection requires no task-inventory synchronization. Compose it, then run Turbo inside its installed workspace. Deployments compose directly and use ordinary application-task caching. The source checkout's pnpm workspace declarations still supply canonical package discovery. A filtered tooling install can omit source application dependencies; see [application-root caching](../../workspaces/README.md#application-root-caching).
62
+
63
+ ## Command boundaries
64
+
65
+ - `create-next-hydra <directory>` creates a project. It acquires the requested source revision in a temporary directory, constructs only selected files and hands over ordinary copied source. There is no ongoing composition step.
66
+ - `create-next-hydra compose <name>` initializes or refreshes a named workspace from canonical source. Application source is always copied. `compose --all` covers Git-visible definitions, including new untracked ones; `compose --all --check` checks their local state.
67
+ - `create-next-hydra add <item>` adds registry items to an existing project.
68
+
69
+ The source checkout contains canonical modules, templates and registry ownership; runnable applications live in named workspaces. `--explain <file>` shows the selected owner and edit location without updating. Compose explicitly before running application tasks inside the workspace's own Turbo graph. Root `pnpm test` runs package/provider suites and composition checks from source, with common app integration tests only in that WorkOS + Contentstack + commercetools reference; root build/typecheck target the composition CLI. Project creation and named composition share preparation for the baseline, selected files, dependency closure, registry transformations, templates, aliases and patches. Scaffolding owns source acquisition and Git initialization; named composition owns safe refresh and local change inspection.
70
+
71
+ ## Deploy a named workspace
72
+
73
+ Deploy the same named folder used for development:
74
+
75
+ ```sh
76
+ pnpm --filter create-next-hydra compose storefront-contentstack
77
+ pnpm --dir workspaces/storefront-contentstack exec turbo run build --filter=web
78
+ ```
79
+
80
+ Named composition uses the same application task configuration as project creation. Source, dependency and environment changes invalidate caches; unchanged inputs can restore prior outputs. Host-specific cache placement belongs in the build invocation, not a different generated Turbo configuration. Portless remains installed; named workspaces retain optional `development.port`; source credentials are copied only with explicit `--copy-env`. Existing destination environment files are preserved, not scrubbed.
81
+
82
+ Put selections in `next-hydra.json`. Compose updates that named folder and preserves caches automatically. Use the root creation command for a new application. See [Vercel configuration and installation order](../../workspaces/README.md#deploy-a-composed-application).
83
+
84
+ ## Local source authoring and environment files
85
+
86
+ Use a named definition and `compose <name>` for local development. Add `--copy-env` when you want to initialize missing environment files from your checkout.
87
+
88
+ Each workspace has its own manifests, lockfile, dependencies and physical application files. Canonical modules, template output and third-party registry content use the same materialization as scaffolded projects. Provider aliases and application runtime aliases resolve inside that workspace; no maintainer-only resolution layer is installed.
89
+
90
+ With `--copy-env`, ignored local environment files named `.env` or `.env.*` are copied into matching materialized paths. The primary checkout supplies defaults; the current worktree takes precedence. Copies have owner-only permissions (`0600`). Existing destination files are preserved; occupied non-file destinations are rejected before copying; dependency/build directories and example files are excluded. Contents are never printed or recorded in ownership state or Git snapshots. Composition does not seed values from examples: provisioning owns that. These are local credentials, not distribution assets, and unused capability credentials inside a selected env file are not filtered out.
91
+
92
+ Provider-owned application files are copied from their canonical registry sources. Use `--explain <file>` when the edit location is unclear, edit that source, then refresh. Selection changes never rewrite tracked source applications or manifests.
93
+
94
+ Authoring constraints:
95
+
96
+ - New files and edits to physical copies are **work to preserve**. Named workspaces provide `compose --check`, but adoption remains manual. Reconcile changes into canonical source and registry ownership, run `pnpm registry:sync` and `pnpm registry:check`, then refresh and verify the same workspace. Preserve unreconciled output until this is done; applied state is not a backup.
97
+ - Refresh the same workspace after changing selections, templates, or dependency manifests. Failed updates preserve before/after fingerprints and can be retried after resolving the reported issue.
98
+
99
+ Named workspaces maintain `.workspace-composition.json` ownership fingerprints for safe refresh and save a private Git snapshot outside the application: `compose <name> --diff` shows local edits, deletions and unregistered files, while `--explain` without a filename lists selected files and their canonical edit locations. See [Inspect workspace changes](../../workspaces/README.md#inspect-workspace-changes) for the authoring workflow and exclusions. This is not application runtime input. Project creation retains no receipt, private snapshot or source-root metadata and requires no ongoing generation step.
100
+
101
+ ## Verification
102
+
103
+ The implementation uses one shared planner and module-reference renderer. Composition recipes bind normal modules into shared templates; materialized modules have ordinary filenames. The CLI builds with its own pinned TypeScript compiler rather than depending on a root-level executable.
104
+
105
+ Composition lint runs optional package `lint:prepare` scripts in its disposable workspace before checking types. Use these for local generated modules, such as the Drupal Canvas component registry; they must not require credentials or provision external services.
106
+
107
+ Run the bounded local suite before accepting composition changes:
108
+
109
+ ```sh
33
110
  pnpm --filter create-next-hydra build
34
- node packages/create-next-hydra/dist/cli.js use --cms contentstack
35
- node packages/create-next-hydra/dist/cli.js use --preset standard
36
- node packages/create-next-hydra/dist/cli.js use --check
111
+ pnpm --filter create-next-hydra test --maxWorkers 2 --testTimeout 30000
112
+ pnpm registry:check
113
+ node packages/create-next-hydra/dist/cli.js compose --all --check
37
114
  ```
38
115
 
39
- `use` updates `next-hydra.json`, governed package entries, Provider-owned application files such as Next.js routes, and the lockfile. It does not remove canonical Provider or Add-on implementation source from the maintainer repository. If an operation fails, it leaves the Git diff in place for inspection or repair.
116
+ The suite checks materialization, template ownership and path collisions, working-tree deletions, environment isolation, overwrite refusal, installation recovery, observation and package closure through the workspace and CLI boundaries. Install-heavy E2E tests use the executable to create independent applications and need sufficient local disk/store capacity. Filesystem paths are trusted; ownership checks are not a sandbox for hostile package lifecycle scripts or concurrent directory mutation. See the [implementation guide](src/README.md) for services, state, packaging exercises and platform limits.
117
+
118
+ Live watcher fixtures resolve their temporary root with `FileSystem.realPath` before deriving source and application paths. Preserve this when adding fixtures: on macOS, `/var/folders/...` can refer to the same directory as `/private/var/folders/...`, while native watcher events use the physical path. Comparing these different path strings can discard valid events and cause timeouts. If watcher tests time out, check the fixture root and reported event paths before increasing timeouts or attributing the failure to a code change. The normal test command requires no `TMPDIR` override.
119
+
120
+ Whole-package and exact-route materialization are covered by local tests. Named-workspace tests also cover template refresh, dependency retry, local-edit protection, unregistered files and interruption recovery. Fresh external provisioning, hosted authentication, payment journeys and production builds remain separate integration gates. `compose --check` reports reconciliation work; automatic adoption is not implemented.
40
121
 
41
- ## Add code to a customer workspace
122
+ ## Add code to your project
42
123
 
43
- Run `add` from an existing customer-owned workspace:
124
+ Run `add` from an existing project:
44
125
 
45
126
  ```bash
46
127
  pnpm dlx create-next-hydra@latest add owner/repository/drupal-dam
47
128
  ```
48
129
 
49
- The command walks the complete registry dependency graph before ShadCN flattens it, rejects duplicate file targets, and lists every prospective file, package entry, and other ShadCN-managed effect. It verifies known Provider requirements through exact aliases in `apps/web/package.json` and labels compatibility requirements that cannot be proven without a customer receipt. The inspected graph is prepared locally so the exact approved artifacts are also the installation input. To keep that preview truthful, customer `add` accepts only explicitly targeted, exact-copy `registry:file` and `registry:item` file entries; file types that ShadCN would transform are rejected. It creates missing files, skips files ShadCN considers identical after newline and surrounding-whitespace normalization, and asks before replacing a changed file or dependency. Like ShadCN, `--yes` skips confirmation prompts and `--overwrite` authorizes replacing changed targets. Combine them for a fully non-interactive installation after reviewing the disclosed plan. `--yes` alone still refuses changed customer code. When package-specific entries change, `add` runs the root `pnpm install` to settle the lockfile and workspace links. The command never deletes files and does not retain a receipt.
130
+ The command inspects the complete registry graph, checks known Provider requirements against installed aliases, and discloses compatibility assumptions it cannot prove without selection state. Inspection and execution retain the same artifacts and recheck project preconditions. ShadCN performs native installation in the existing project; Hydra then reconciles package-local requirements. Configuration-dependent changes need an explicitly configured ShadCN context: the CLI does not invent a workspace-root `components.json` or CSS configuration. Like ShadCN, `--yes` skips confirmation prompts and `--overwrite` authorizes replacing changed targets. `--yes` alone never approves replacing code or dependency versions. Dependency-bearing additions install on every explicit invocation, including retries. Addition has no project lock, receipt or composition snapshot. A native operation or installation failure may leave changes; inspect them before retrying rather than assuming rollback.
131
+
132
+ `add` accepts ordinary registry items and Next Hydra Add-ons. It does not switch Providers or apply Presets. In v1, an Add-on that declares separate binary assets or pnpm patches must be selected in a named workspace or during a new scaffold.
133
+
134
+ After a successful addition, the CLI prints setup instructions from the installed item and its registry dependencies.
50
135
 
51
- Customer `add` accepts ordinary registry items and Next Hydra Add-ons. It does not switch Providers or apply Presets. In v1, an Add-on that declares separate binary assets or pnpm patches must be selected in a Maintainer Workspace or during a new scaffold.
136
+ ## Author a Provider, Recipe or Add-on
52
137
 
53
- ## Author a Provider or Add-on
138
+ Commerce's registry item depends on the composition recipes `commerce-web`, `commerce-api`, and `commerce-admin`. These wire the complete Commerce package into other applications; they are not independent shopping-feature choices. Auth owns its provider routes and its Commerce identity/webhook recipe. No application profile or separate web shell is required. The planner includes `app-web` automatically; the shared constructor includes only selected applications and their package dependency closure. Unselected source never enters the destination.
139
+
140
+ Recipe describes an assembly unit, not its size: recipes can depend on smaller recipes through `registryDependencies`. A recipe may install files and dependencies, bind module exports into template slots, or combine these. The individual placements are **slot bindings**, not separate recipes. Provider-specific **provisioning recipes**, such as Drupal configuration or Contentstack content blocks, configure external systems in a separate provisioning step. Composition can include their files but never executes them.
54
141
 
55
142
  Keep source in its normal package or application directory and place a `registry.json` beside it. Add that registry file to the root `registry.json` `include` list. A registry containing Selection Definitions uses [`source-registry.json`](./schema/source-registry.json), which applies the complete [`selection-definition.json`](./schema/selection-definition.json) registry-item schema whenever an item contains `meta.nextHydra`. Ordinary registry items continue to use ShadCN's schema.
56
143
 
57
144
  A Selection Definition declares:
58
145
 
59
- - its stable ID and whether it is a Provider, Add-on, or Preset;
146
+ - its stable ID and whether it is a Provider, Package, Recipe, Add-on, or Preset;
60
147
  - a Provider Slot and one package `binding` when it is a Provider;
61
- - an optional `binding.sourcePath` for resolving a maintained Provider directly to workspace source;
62
- - `providerDependencies` for packages contributed by the selection that consume a Provider Slot;
148
+ - an optional `binding.sourcePath` for resolving a Provider to its package inside the materialized workspace;
149
+ - `providerDependencies` for packages installed by the selection that consume a Provider Slot;
63
150
  - standard ShadCN `registryDependencies` for any other registry items it needs;
151
+ - conditional dependencies for recipes activated by occupied Provider Slots;
152
+ - `providerSlots` requirements on a Package or Recipe, such as Commerce requiring Auth;
64
153
  - required and conflicting selection IDs;
65
154
  - ordinary exact package entries;
66
- - exact pnpm patch entries and their patch-file assets; and
155
+ - exact pnpm patch entries and their patch-file assets;
156
+ - composition templates, named insertion slots, and ordered slot bindings;
157
+ - selection-owned TypeScript aliases needed for composed modules to resolve inside the workspace;
67
158
  - setup instructions through the registry item's standard `docs` field.
68
159
 
69
160
  Every copied registry file must have an explicit workspace-root target such as `~/packages/cms-drupal/src/index.ts`. The source `path` remains relative to the colocated registry, so maintainers edit and test canonical Provider code in its normal package or application.
70
161
 
71
- When a Provider or Add-on needs to place a file outside its own source directory, keep that source under a colocated `registry/` directory using its final workspace path. For example, `packages/cms-drupal/registry/apps/web/app/api/draft/route.ts` is installed as `~/apps/web/app/api/draft/route.ts`. These files are ordinary ShadCN registry files: ShadCN installs them during scaffolding and customer `add`, while maintainer `use` may remove or replace their known targets when the selected stack changes.
162
+ When a Provider or Add-on needs to place a file outside its own source directory, keep that source under a colocated `registry/` directory using its final workspace path. For example, `packages/cms-drupal/registry/apps/web/app/api/draft/route.ts` is installed as `~/apps/web/app/api/draft/route.ts`. These files are ordinary ShadCN registry files: ShadCN installs them during scaffolding and `add`, while named `compose` copies their canonical sources and may remove or replace unchanged owned targets when the selected stack changes.
163
+
164
+ A composition recipe may instead reference ordinary files already authored in the destination app, as `commerce-web` does for Checkout. Its colocated app registry declares those files, and the owning package depends on that item. This preserves direct source authoring without making duplicate route templates or using unsupported parent-directory registry paths.
72
165
 
73
- Run these commands after adding, moving, or removing contribution files:
166
+ Run these commands after adding, moving, or removing registry-owned files:
74
167
 
75
168
  ```bash
76
169
  pnpm registry:sync
@@ -81,6 +174,12 @@ pnpm registry:check
81
174
 
82
175
  Standard ShadCN `dependencies` and `devDependencies` apply to the workspace root. Use `meta.nextHydra.packages` only when an ordinary dependency must be added to a specific workspace package. Stable Provider aliases are derived from the slot and cannot be declared in `packages`.
83
176
 
177
+ Project creation and named `compose` record those standard dependency fields through the same manifest logic before the final package install, including with `--no-install`. Only selected registry items and their transitive dependencies participate. Bare package names preserve existing requirements; a new bare name uses `latest` until installation resolves it. Explicit versions, tags, and named aliases are preserved, and conflicting explicit requirements fail during preparation. URL or local-path requirements must include a package name (`name@specifier`); unnamed sources cannot be resolved in an install-free plan. `add` continues to use ShadCN's dependency installer.
178
+
179
+ Use `meta.composition.templates` for shared-file structure and `meta.composition.slotBindings` for references to ordinary module exports. This renderer is shared by `compose` and initial scaffolding. Modules remain normal TypeScript/TSX source, and targets have ordinary filenames with no ongoing generation step in scaffolded projects. A selection may declare `typeScriptAliases` for application-level module overrides that must persist in both named and scaffolded workspaces. Both target and alias are catalog-governed. `add` rejects composition recipes and template recomposition because it cannot safely regenerate files you may have edited.
180
+
181
+ Contentstack declares `conditionalDependencies: [{ providers: ["commerce"], items: ["cms-contentstack-commerce"] }]`; Drupal uses `cms-drupal-commerce` in the same way. These `kind: "recipe"` items connect the selected CMS to Commerce core, not to a specific Commerce provider. They install the CMS mapping and `@repo/commerce` dependency automatically when both capabilities are selected, rather than exposing product collection as a selectable Add-on. Registry discovery fetches possible recipes, while planning installs only applicable ones. Contentstack's `recipe/recipes/product-collection.json` remains a feature-specific provisioning recipe: the provisioning command applies it to the base content type and seed entry before importing the stack.
182
+
84
183
  A Provider declares its installable package once:
85
184
 
86
185
  ```json
@@ -93,7 +192,7 @@ A Provider declares its installable package once:
93
192
  }
94
193
  ```
95
194
 
96
- An Add-on that contributes a package which imports the selected CMS declares the consumer, not a concrete CMS package:
195
+ An Add-on that installs a package which imports the selected CMS declares the consumer, not a concrete CMS package:
97
196
 
98
197
  ```json
99
198
  {
@@ -108,7 +207,7 @@ An Add-on that contributes a package which imports the selected CMS declares the
108
207
  }
109
208
  ```
110
209
 
111
- Scaffold and maintainer `use` resolve that dependency to `@repo/cms` using the selected Provider's `binding.specifier`. If the Provider also declares `binding.sourcePath`, they write the exact alias and its `/*` wildcard into every selected consumer's `tsconfig.json` for direct-source development. Without `sourcePath`, pnpm's installed alias is the only resolution path and Next Hydra writes no TypeScript path override. Catalog-governed overrides are removed when they are no longer selected, and `use --check` reports incorrect or stale paths. Customer `add`, which has no retained selection receipt, copies the exact Provider alias already present in `apps/web/package.json` into each contributed consumer.
210
+ Scaffold and `compose` resolve that dependency to `@repo/cms` using the selected Provider's `binding.specifier`. If the Provider also declares `binding.sourcePath`, they write the exact alias and its `/*` wildcard into every selected consumer's `tsconfig.json`, resolving source inside the materialized project. These aliases are identical in named and scaffolded workspaces. Without `sourcePath`, pnpm's installed alias is the only resolution path and Next Hydra writes no TypeScript path override. Catalog-governed overrides are removed when they are no longer selected; named `compose --check` reports changed or stale physical configuration. `add`, which has no retained selection receipt, copies the exact Provider alias already present in `apps/web/package.json` into each installed consumer.
112
211
 
113
212
  ShadCN reads registry source files as text. For a binary file that must survive byte-for-byte, or a root patch file referenced by `pnpmPatches`, an official or locally included source-registry selection may declare a typed `assets` source and target instead. Separately fetched external selections cannot contribute assets in v1.
114
213
 
@@ -116,7 +215,7 @@ ShadCN registry mappings, including private registry URLs and environment-backed
116
215
 
117
216
  ## Current limits
118
217
 
119
- - Auth, CMS, and Commerce each require exactly one Provider.
120
- - A scaffolded Customer Workspace has no composition receipt and cannot be automatically upgraded or switched later.
121
- - Composition installs local code and JavaScript dependencies. Remote service setup, real secrets, Composer changes, Drupal module enablement, and deployed extensions remain manual.
218
+ - The official registry requires CMS. Auth is optional; Commerce requires Auth. A source catalog must include the shared `app-web` item.
219
+ - A scaffolded project has no composition receipt and cannot be automatically upgraded or switched later.
220
+ - Composition installs local code and JavaScript dependencies. External service setup runs separately through explicit provider administration commands or manual setup; composition never provisions remote systems.
122
221
  - Selection Definitions are declarative and cannot run arbitrary hooks.
@@ -0,0 +1,11 @@
1
+ import { Stdio } from "effect";
2
+ import { Command, Prompt } from "effect/unstable/cli";
3
+ import { AdditionConflict } from "./addition.ts";
4
+ import { InvalidComposition } from "./errors.ts";
5
+ import { Workspaces } from "./workspaces.ts";
6
+ export declare const addCommand: (cwd: string) => Command.Command<"add", {
7
+ readonly overwrite: boolean;
8
+ readonly reference: string;
9
+ readonly yes: boolean;
10
+ }, {}, InvalidComposition | import("./errors.ts").RegistryFailure | import("./errors.ts").StagingRetained | import("./errors.ts").DependencyInstallationFailed | import("effect/Schema").SchemaError | import("effect/PlatformError").PlatformError | import("./addition.ts").AdditionChanged | AdditionConflict | import("./addition.ts").AdditionFailed | import("effect/Terminal").QuitError, Workspaces | Stdio.Stdio | Prompt.Environment>;
11
+ //# sourceMappingURL=add-command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"add-command.d.ts","sourceRoot":"","sources":["../src/add-command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,KAAK,EAAE,MAAM,QAAQ,CAAC;AAChD,OAAO,EAAY,OAAO,EAAQ,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAEtE,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,eAAO,MAAM,UAAU,GAAI,KAAK,MAAM;;;;+aAmGmC,CAAC"}
@@ -0,0 +1,75 @@
1
+ import { Console, Effect, Stdio } from "effect";
2
+ import { Argument, Command, Flag, Prompt } from "effect/unstable/cli";
3
+ import { AdditionConflict } from "./addition.js";
4
+ import { InvalidComposition } from "./errors.js";
5
+ import { Workspaces } from "./workspaces.js";
6
+ export const addCommand = (cwd) => Command.make("add", {
7
+ overwrite: Flag.Boolean("overwrite").pipe(Flag.withAlias("o"), Flag.withDefault(false), Flag.withDescription("Replace changed files and package entries.")),
8
+ reference: Argument.String("reference").pipe(Argument.withDescription("Registry item URL, namespaced reference or local JSON file.")),
9
+ yes: Flag.Boolean("yes").pipe(Flag.withAlias("y"), Flag.withDefault(false), Flag.withDescription("Skip confirmation prompts; does not permit replacement.")),
10
+ }, (options) => Effect.gen(function* () {
11
+ const workspace = yield* (yield* Workspaces).existing({ root: cwd });
12
+ const inspection = yield* workspace.inspectAdd({
13
+ reference: options.reference,
14
+ });
15
+ for (const assumption of inspection.assumptions) {
16
+ yield* Console.warn(assumption);
17
+ }
18
+ if (inspection.environment.length) {
19
+ yield* Console.warn(`Registry environment defaults may be added by ShadCN: ${inspection.environment.join(", ")}`);
20
+ }
21
+ const changes = [
22
+ ...inspection.files.map((file) => ({ ...file, label: file.target })),
23
+ ...inspection.packages.map((entry) => ({
24
+ ...entry,
25
+ label: `${entry.target}: ${entry.section}.${entry.name}`,
26
+ })),
27
+ ];
28
+ for (const change of changes) {
29
+ yield* Console.log(`${change.status}: ${change.label}`);
30
+ }
31
+ const conflicts = changes.filter((change) => change.status === "changed");
32
+ let { overwrite } = options;
33
+ if (options.yes) {
34
+ if (conflicts.length && !overwrite) {
35
+ return yield* new AdditionConflict({
36
+ paths: conflicts.map((change) => change.label),
37
+ });
38
+ }
39
+ }
40
+ else {
41
+ const stdio = yield* Stdio.Stdio;
42
+ if (!(yield* stdio.stdinIsTerminal) ||
43
+ !(yield* stdio.stdoutIsTerminal)) {
44
+ return yield* new InvalidComposition({
45
+ message: "Addition requires confirmation. Use --yes for noninteractive installation and --overwrite only to permit replacements.",
46
+ });
47
+ }
48
+ if (!overwrite) {
49
+ for (const conflict of conflicts) {
50
+ if (!(yield* Prompt.Confirm({
51
+ initial: false,
52
+ message: `Replace ${conflict.label}?`,
53
+ }))) {
54
+ return;
55
+ }
56
+ }
57
+ overwrite = conflicts.length > 0;
58
+ }
59
+ if (!(yield* Prompt.Confirm({
60
+ initial: false,
61
+ message: "Install this registry item and its dependencies?",
62
+ }))) {
63
+ return;
64
+ }
65
+ }
66
+ const result = yield* workspace.add({
67
+ expected: inspection.precondition,
68
+ overwrite,
69
+ reference: options.reference,
70
+ });
71
+ yield* Console.log("Registry item installed.");
72
+ for (const instruction of result.instructions) {
73
+ yield* Console.log(`${instruction.item}:\n${instruction.text}`);
74
+ }
75
+ })).pipe(Command.withDescription("Add a registry item to this project."));
@@ -0,0 +1,67 @@
1
+ import { Effect, FileSystem, Path, Schema } from "effect";
2
+ import { InvalidComposition } from "./errors.ts";
3
+ import type { PackageRequirement } from "./packages.ts";
4
+ import { Shadcn } from "./shadcn.ts";
5
+ import { WorkspaceDependencies } from "./workspace-dependencies.ts";
6
+ declare const AdditionChanged_base: Schema.Class<AdditionChanged, Schema.TaggedStruct<"AdditionChanged", {}>, import("effect/Cause").YieldableError>;
7
+ export declare class AdditionChanged extends AdditionChanged_base {
8
+ readonly message = "The registry or project changed after inspection. Inspect the addition again before applying it. No installation was started.";
9
+ }
10
+ declare const AdditionConflict_base: Schema.Class<AdditionConflict, Schema.TaggedStruct<"AdditionConflict", {
11
+ readonly paths: Schema.$Array<Schema.String>;
12
+ }>, import("effect/Cause").YieldableError>;
13
+ export declare class AdditionConflict extends AdditionConflict_base {
14
+ get message(): string;
15
+ }
16
+ declare const AdditionFailed_base: Schema.Class<AdditionFailed, Schema.TaggedStruct<"AdditionFailed", {
17
+ readonly diagnostic: Schema.Redacted<Schema.String>;
18
+ readonly directory: Schema.String;
19
+ readonly phase: Schema.Literals<readonly ["registry", "packages"]>;
20
+ }>, import("effect/Cause").YieldableError>;
21
+ export declare class AdditionFailed extends AdditionFailed_base {
22
+ get message(): string;
23
+ }
24
+ export interface AddRequest {
25
+ readonly reference: string;
26
+ readonly overwrite: boolean;
27
+ readonly expected?: string;
28
+ }
29
+ export interface AddInspection {
30
+ readonly assumptions: readonly string[];
31
+ readonly environment: readonly string[];
32
+ readonly files: readonly {
33
+ readonly target: string;
34
+ readonly status: "create" | "identical" | "changed";
35
+ }[];
36
+ readonly packages: readonly (Omit<PackageRequirement, "specifier"> & {
37
+ readonly status: "create" | "identical" | "changed";
38
+ })[];
39
+ readonly precondition: string;
40
+ }
41
+ export declare const existingWorkspace: (root: string) => Effect.Effect<{
42
+ add: (request: AddRequest) => Effect.Effect<{
43
+ directory: string;
44
+ files: {
45
+ readonly target: string;
46
+ readonly status: "create" | "identical" | "changed";
47
+ }[];
48
+ instructions: readonly import("./model.ts").RegistryInstruction[];
49
+ }, InvalidComposition | import("./errors.ts").RegistryFailure | import("./errors.ts").StagingRetained | import("./errors.ts").DependencyInstallationFailed | Schema.SchemaError | import("effect/PlatformError").PlatformError | AdditionChanged | AdditionConflict | AdditionFailed, never>;
50
+ inspectAdd: (request: {
51
+ readonly reference: string;
52
+ }) => Effect.Effect<{
53
+ assumptions: string[];
54
+ environment: string[];
55
+ files: {
56
+ readonly target: string;
57
+ readonly status: "create" | "identical" | "changed";
58
+ }[];
59
+ packages: (Omit<PackageRequirement, "specifier"> & {
60
+ readonly status: "create" | "identical" | "changed";
61
+ })[];
62
+ precondition: string;
63
+ }, InvalidComposition | import("./errors.ts").RegistryFailure | Schema.SchemaError | import("effect/PlatformError").PlatformError, never>;
64
+ }, never, FileSystem.FileSystem | Path.Path | Shadcn | WorkspaceDependencies>;
65
+ export type ExistingWorkspace = Effect.Success<ReturnType<typeof existingWorkspace>>;
66
+ export {};
67
+ //# sourceMappingURL=addition.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"addition.d.ts","sourceRoot":"","sources":["../src/addition.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,MAAM,EACN,UAAU,EAEV,IAAI,EAEJ,MAAM,EACP,MAAM,QAAQ,CAAC;AAEhB,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAGjD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAGxD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,OAAO,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;;AAEpE,qBAAa,eAAgB,SAAQ,oBAGpC;IACC,QAAQ,CAAC,OAAO,mIACkH;CACnI;;;;AACD,qBAAa,gBAAiB,SAAQ,qBAGrC;IACC,IAAI,OAAO,WAEV;CACF;;;;;;AACD,qBAAa,cAAe,SAAQ,mBAOnC;IACC,IAAI,OAAO,WAEV;CACF;AACD,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AACD,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC,QAAQ,CAAC,KAAK,EAAE,SAAS;QACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,MAAM,EAAE,QAAQ,GAAG,WAAW,GAAG,SAAS,CAAC;KACrD,EAAE,CAAC;IACJ,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC,IAAI,CAAC,kBAAkB,EAAE,WAAW,CAAC,GAAG;QACnE,QAAQ,CAAC,MAAM,EAAE,QAAQ,GAAG,WAAW,GAAG,SAAS,CAAC;KACrD,CAAC,EAAE,CAAC;IACL,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAoND,eAAO,MAAM,iBAAiB;;;;6BA3NT,MAAM;6BACN,QAAQ,GAAG,WAAW,GAAG,SAAS;;;;;4BAoZT,MAAM;;;;;6BArZ/B,MAAM;6BACN,QAAQ,GAAG,WAAW,GAAG,SAAS;;;6BAGlC,QAAQ,GAAG,WAAW,GAAG,SAAS;;;;6EAghBrD,CAAC;AAEH,MAAM,MAAM,iBAAiB,GAAG,MAAM,CAAC,OAAO,CAC5C,UAAU,CAAC,OAAO,iBAAiB,CAAC,CACrC,CAAC"}