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
@@ -0,0 +1,420 @@
1
+ import { Context, Effect, FileSystem, Layer, Path, Schema } from "effect";
2
+ import { format } from "oxfmt";
3
+ import { applicationIgnoreRules, applicationManifest, applicationWorkspace, normalizeApplicationName, readWorkspaceSettings, } from "./baseline.js";
4
+ import { demoArchitecturePackage, eraseDemoInstrumentation, } from "./demo-instrumentation.js";
5
+ import { InvalidComposition, SourceChanged } from "./errors.js";
6
+ import { isEnvironmentFile, eligiblePackageFile } from "./file-policy.js";
7
+ import { collectFiles, relativeFile, writeFile } from "./files.js";
8
+ import { scopeApplicationHosts } from "./hosting.js";
9
+ import { packageCatalog } from "./package-catalog.js";
10
+ import { completePackages } from "./packages.js";
11
+ import { acquireRegistry, registryInstructions } from "./registry.js";
12
+ import { selectRegistryItems } from "./selection.js";
13
+ import { Shadcn } from "./shadcn.js";
14
+ import { SourceInventory } from "./source-inventory.js";
15
+ import { withStaging } from "./staging.js";
16
+ import { applicationTasks } from "./tasks.js";
17
+ import { readTemplateDefinitions, orderBindings, renderTemplate, } from "./templates.js";
18
+ import { applyTypeScriptAliases } from "./typescript-paths.js";
19
+ const encodeJson = Schema.encodeEffect(Schema.fromJsonString(Schema.Unknown));
20
+ export class Composition extends Context.Service()("create-next-hydra/Composition") {
21
+ static layer = Layer.effect(Composition, Effect.gen(function* () {
22
+ const fs = yield* FileSystem.FileSystem;
23
+ const path = yield* Path.Path;
24
+ const shadcn = yield* Shadcn;
25
+ const inventory = yield* SourceInventory;
26
+ const prepare = Effect.fn("Composition.prepare")(function* (request) {
27
+ const applicationName = normalizeApplicationName(request.name);
28
+ const options = {
29
+ cwd: request.source.root,
30
+ registryFile: "registry.json",
31
+ };
32
+ const sourceRegistry = yield* shadcn.loadRegistry(options);
33
+ const acquired = yield* acquireRegistry(request.source.root, {
34
+ registry: sourceRegistry,
35
+ selection: request.selection,
36
+ }).pipe(Effect.provideService(Shadcn, shadcn), Effect.provideService(FileSystem.FileSystem, fs), Effect.provideService(Path.Path, path));
37
+ const { registry } = acquired;
38
+ const { aliases, assets, ownedAssetTargets, ownedAliases, ownedRequirements, patches, references, requirements, selectedItems, } = yield* selectRegistryItems(registry, request.selection, acquired.references);
39
+ const captures = new Map();
40
+ const capture = Effect.fn("Composition.capture")(function* (input) {
41
+ const relative = yield* relativeFile(input);
42
+ const cached = captures.get(relative);
43
+ if (cached) {
44
+ return cached;
45
+ }
46
+ const absolute = path.join(request.source.root, relative);
47
+ const stat = yield* fs.stat(absolute);
48
+ const file = {
49
+ content: yield* fs.readFile(absolute),
50
+ mode: stat.mode % 0o1000,
51
+ };
52
+ captures.set(relative, file);
53
+ return file;
54
+ });
55
+ const retained = Effect.fn("Composition.retained")(function* (input) {
56
+ const file = captures.get(yield* relativeFile(input));
57
+ if (!file) {
58
+ return yield* Effect.die(new Error(`Missing captured input: ${input}`));
59
+ }
60
+ return file;
61
+ });
62
+ const rootManifest = yield* capture("package.json");
63
+ const initialManifest = {
64
+ ...rootManifest,
65
+ content: yield* applicationManifest(rootManifest.content, applicationName, selectedItems.some((item) => item.files?.some((file) => file.target?.replace(/^~\//u, "") === "tests/e2e/package.json"))),
66
+ };
67
+ for (const item of selectedItems) {
68
+ if (item.type !== "registry:item" ||
69
+ item.css ||
70
+ item.cssVars ||
71
+ item.tailwind ||
72
+ item.envVars) {
73
+ return yield* new InvalidComposition({
74
+ message: `Registry directives outside exact-file installation are not implemented in this entrypoint: ${item.name}`,
75
+ });
76
+ }
77
+ }
78
+ const definitions = yield* readTemplateDefinitions(registry.items, acquired.external);
79
+ const templates = selectedItems.flatMap((item) => (definitions.get(item.name)?.templates ?? []).map((template) => ({
80
+ ...template,
81
+ owner: item.name,
82
+ })));
83
+ const bindings = selectedItems.flatMap((item) => (definitions.get(item.name)?.slotBindings ?? []).map((binding) => ({
84
+ ...binding,
85
+ owner: item.name,
86
+ })));
87
+ const targets = new Map();
88
+ const selectedManifests = new Map([["package.json", initialManifest]]);
89
+ const claim = (target, mode, origin) => Effect.gen(function* () {
90
+ const relative = yield* relativeFile(target);
91
+ if (isEnvironmentFile(relative)) {
92
+ return yield* new InvalidComposition({
93
+ message: `Composition cannot create runtime environment files: ${relative}. Keep examples as documentation; use provisioning or explicit local environment copying for values.`,
94
+ });
95
+ }
96
+ if (targets.has(relative)) {
97
+ return yield* new InvalidComposition({
98
+ message: `Duplicate output target: ${relative}`,
99
+ });
100
+ }
101
+ targets.set(relative, { mode, origin });
102
+ });
103
+ yield* claim("package.json", rootManifest.mode, {
104
+ kind: "policy",
105
+ policy: "application-manifest",
106
+ sources: ["package.json", "registry.json"],
107
+ });
108
+ for (const item of selectedItems) {
109
+ for (const file of item.files ?? []) {
110
+ if (!file.target ||
111
+ !["registry:file", "registry:page"].includes(file.type)) {
112
+ return yield* new InvalidComposition({
113
+ message: `Registry file needs an exact target: ${file.path}`,
114
+ });
115
+ }
116
+ const remote = acquired.external.has(item.name);
117
+ if (remote && file.content === undefined) {
118
+ return yield* new InvalidComposition({
119
+ message: `Published artifact has no file content: ${item.name}`,
120
+ });
121
+ }
122
+ const captured = remote
123
+ ? { content: new TextEncoder().encode(file.content), mode: 0o644 }
124
+ : yield* capture(file.path);
125
+ yield* claim(file.target, captured.mode, remote
126
+ ? { kind: "registry", owner: item.name }
127
+ : {
128
+ kind: "source",
129
+ owner: item.name,
130
+ source: yield* relativeFile(file.path),
131
+ });
132
+ if (file.target.endsWith("/package.json")) {
133
+ selectedManifests.set(yield* relativeFile(file.target), captured);
134
+ }
135
+ }
136
+ }
137
+ for (const template of templates) {
138
+ const captured = yield* capture(template.source);
139
+ yield* claim(template.target, captured.mode, {
140
+ bindings: orderBindings(bindings.filter((binding) => binding.target === template.target)),
141
+ kind: "template",
142
+ owner: template.owner,
143
+ source: template.source,
144
+ });
145
+ }
146
+ for (const asset of assets) {
147
+ yield* claim(asset.target, (yield* capture(asset.source)).mode, {
148
+ kind: "source",
149
+ owner: asset.owner,
150
+ source: asset.source,
151
+ });
152
+ }
153
+ for (const binding of bindings) {
154
+ if (!templates.some((template) => template.target === binding.target &&
155
+ Object.hasOwn(template.slots, binding.slot))) {
156
+ return yield* new InvalidComposition({
157
+ message: `Missing template slot: ${binding.target}#${binding.slot}`,
158
+ });
159
+ }
160
+ }
161
+ const governed = new Set(ownedAssetTargets);
162
+ for (const item of registry.items) {
163
+ for (const template of definitions.get(item.name)?.templates ?? []) {
164
+ governed.add(yield* relativeFile(template.target));
165
+ }
166
+ for (const file of item.files ?? []) {
167
+ if (file.target) {
168
+ governed.add(yield* relativeFile(file.target));
169
+ }
170
+ }
171
+ }
172
+ const sourceFiles = yield* inventory.list(request.source.root);
173
+ const settings = yield* readWorkspaceSettings((yield* capture("pnpm-workspace.yaml")).content);
174
+ const catalog = packageCatalog(sourceFiles, settings.data.packages);
175
+ const { baseline, directories: packageDirectories, manifests, } = yield* completePackages({
176
+ capture,
177
+ files: sourceFiles,
178
+ governed,
179
+ omittedDependencies: request.demoArchitecture
180
+ ? undefined
181
+ : new Set([demoArchitecturePackage]),
182
+ ownedRequirements,
183
+ packageManifests: catalog.manifests,
184
+ registryItems: selectedItems,
185
+ requirements,
186
+ selectedManifests,
187
+ });
188
+ for (const target of baseline) {
189
+ yield* claim(target, (yield* retained(target)).mode, {
190
+ kind: "source",
191
+ owner: null,
192
+ source: target,
193
+ });
194
+ }
195
+ const rootFiles = yield* applicationWorkspace(settings, (yield* capture("pnpm-lock.yaml")).content, new Set(targets.keys()), patches);
196
+ for (const target of rootFiles.keys()) {
197
+ yield* claim(target, (yield* retained(target)).mode, {
198
+ kind: "policy",
199
+ policy: target === "pnpm-lock.yaml"
200
+ ? "lockfile-seed"
201
+ : "workspace-settings",
202
+ sources: [target, "registry.json"],
203
+ });
204
+ }
205
+ yield* claim("turbo.json", 0o644, {
206
+ kind: "policy",
207
+ policy: "application-tasks",
208
+ sources: [],
209
+ });
210
+ yield* claim(".gitignore", 0o644, {
211
+ kind: "policy",
212
+ policy: "application-ignore",
213
+ sources: [],
214
+ });
215
+ const observedInventory = (files) => files.filter((file) => catalog.matchesManifest(file) ||
216
+ packageDirectories.some((directory) => file.startsWith(`${directory}/`) && eligiblePackageFile(file)));
217
+ return yield* withStaging((staging) => Effect.gen(function* () {
218
+ for (const item of selectedItems) {
219
+ if (acquired.external.has(item.name)) {
220
+ continue;
221
+ }
222
+ for (const file of item.files ?? []) {
223
+ yield* writeFile(staging.inputs, {
224
+ target: file.path,
225
+ ...(yield* retained(file.path)),
226
+ });
227
+ }
228
+ }
229
+ for (const target of baseline) {
230
+ yield* writeFile(staging.application, {
231
+ target,
232
+ ...(yield* retained(target)),
233
+ });
234
+ }
235
+ for (const [target, content] of rootFiles) {
236
+ yield* writeFile(staging.application, {
237
+ ...(yield* retained(target)),
238
+ content,
239
+ target,
240
+ });
241
+ }
242
+ yield* writeFile(staging.inputs, {
243
+ content: new TextEncoder().encode(yield* encodeJson({
244
+ homepage: registry.homepage,
245
+ items: selectedItems,
246
+ name: registry.name,
247
+ })),
248
+ mode: 0o600,
249
+ target: "registry.json",
250
+ });
251
+ yield* writeFile(staging.application, {
252
+ ...initialManifest,
253
+ target: "package.json",
254
+ });
255
+ const artifacts = [];
256
+ for (const item of selectedItems) {
257
+ artifacts.push(acquired.external.has(item.name)
258
+ ? item
259
+ : yield* shadcn.loadRegistryItem(item.name, {
260
+ cwd: staging.inputs,
261
+ registryFile: "registry.json",
262
+ }));
263
+ }
264
+ const artifactPaths = new Map(artifacts.map((item, index) => [
265
+ item.name,
266
+ path.join(staging.registry, `${index}.json`),
267
+ ]));
268
+ for (const [index, artifact] of artifacts.entries()) {
269
+ const registryDependencies = [];
270
+ for (const reference of artifact.registryDependencies ?? []) {
271
+ const name = references.get(reference);
272
+ const target = name ? artifactPaths.get(name) : undefined;
273
+ if (!target) {
274
+ return yield* new InvalidComposition({
275
+ message: `Unresolved registry dependency: ${reference}`,
276
+ });
277
+ }
278
+ registryDependencies.push(target);
279
+ }
280
+ yield* writeFile(staging.registry, {
281
+ content: new TextEncoder().encode(yield* encodeJson({
282
+ ...artifact,
283
+ dependencies: undefined,
284
+ devDependencies: undefined,
285
+ docs: undefined,
286
+ registryDependencies,
287
+ })),
288
+ mode: 0o600,
289
+ target: `${index}.json`,
290
+ });
291
+ }
292
+ if (artifacts.length) {
293
+ yield* shadcn.install(staging, [...artifactPaths.values()]);
294
+ }
295
+ for (const [target, file] of manifests) {
296
+ yield* writeFile(staging.application, { ...file, target });
297
+ }
298
+ for (const asset of assets) {
299
+ yield* writeFile(staging.application, {
300
+ ...(yield* retained(asset.source)),
301
+ target: asset.target,
302
+ });
303
+ }
304
+ for (const template of templates) {
305
+ const source = yield* retained(template.source);
306
+ const content = yield* renderTemplate(template, bindings.filter((binding) => binding.target === template.target), new TextDecoder().decode(source.content));
307
+ yield* writeFile(staging.application, {
308
+ content: new TextEncoder().encode(content),
309
+ mode: source.mode,
310
+ target: template.target,
311
+ });
312
+ }
313
+ yield* applyTypeScriptAliases(staging.application, aliases, ownedAliases);
314
+ const files = yield* scopeApplicationHosts(yield* collectFiles(staging.application), applicationName, new Set(assets.map((asset) => asset.target)), request.port);
315
+ const tasksSource = yield* encodeJson(yield* applicationTasks(files));
316
+ const tasks = yield* Effect.tryPromise({
317
+ catch: () => new InvalidComposition({ message: "Cannot format turbo.json" }),
318
+ try: async () => await format("turbo.json", tasksSource, { printWidth: 80 }),
319
+ });
320
+ if (tasks.errors.length) {
321
+ return yield* new InvalidComposition({
322
+ message: "Invalid generated turbo.json",
323
+ });
324
+ }
325
+ files.push({
326
+ content: new TextEncoder().encode(tasks.code),
327
+ mode: 0o644,
328
+ target: "turbo.json",
329
+ }, {
330
+ content: new TextEncoder().encode(applicationIgnoreRules),
331
+ mode: 0o644,
332
+ target: ".gitignore",
333
+ });
334
+ const changed = [];
335
+ for (const [relative, captured] of captures) {
336
+ const absolute = path.join(request.source.root, relative);
337
+ const bytes = yield* fs.readFile(absolute);
338
+ const stat = yield* fs.stat(absolute);
339
+ if (stat.mode % 0o1000 !== captured.mode ||
340
+ bytes.length !== captured.content.length ||
341
+ bytes.some((byte, index) => byte !== captured.content[index])) {
342
+ changed.push(relative);
343
+ }
344
+ }
345
+ if ((yield* encodeJson(yield* shadcn.loadRegistry(options))) !==
346
+ (yield* encodeJson(sourceRegistry))) {
347
+ changed.push("registry.json");
348
+ }
349
+ for (const [file, content] of acquired.observed) {
350
+ if ((yield* fs.readFileString(file)) !== content) {
351
+ changed.push(file);
352
+ }
353
+ }
354
+ const currentInventory = observedInventory(yield* inventory.list(request.source.root));
355
+ const capturedInventory = observedInventory(sourceFiles);
356
+ if (currentInventory.length !== capturedInventory.length ||
357
+ currentInventory.some((file, index) => file !== capturedInventory[index])) {
358
+ const before = new Set(capturedInventory);
359
+ const after = new Set(currentInventory);
360
+ changed.push(...currentInventory.filter((file) => !before.has(file)), ...capturedInventory.filter((file) => !after.has(file)));
361
+ }
362
+ if (changed.length) {
363
+ return yield* new SourceChanged({ paths: changed });
364
+ }
365
+ const output = [];
366
+ for (const file of files) {
367
+ const claimed = targets.get(file.target);
368
+ if (!claimed) {
369
+ return yield* new InvalidComposition({
370
+ message: `Unplanned output target: ${file.target}`,
371
+ });
372
+ }
373
+ if (/\.(?:tsx|jsx|ts|js|mts|cts|mjs|cjs)$/u.test(file.target) &&
374
+ !assets.some((asset) => asset.target === file.target)) {
375
+ const source = new TextDecoder().decode(file.content);
376
+ const erased = yield* eraseDemoInstrumentation(file.target, source);
377
+ if (!request.demoArchitecture && erased !== source) {
378
+ const { origin } = claimed;
379
+ if (origin.kind !== "source" &&
380
+ origin.kind !== "template" &&
381
+ origin.kind !== "registry") {
382
+ return yield* new InvalidComposition({
383
+ message: `Cannot transform policy output: ${file.target}`,
384
+ });
385
+ }
386
+ output.push({
387
+ ...file,
388
+ ...claimed,
389
+ content: new TextEncoder().encode(erased),
390
+ origin: {
391
+ ...origin,
392
+ transforms: ["remove-demo-architecture"],
393
+ },
394
+ });
395
+ continue;
396
+ }
397
+ }
398
+ output.push({ ...file, ...claimed });
399
+ }
400
+ return {
401
+ files: output,
402
+ inputs: {
403
+ excluded: [...governed],
404
+ files: [
405
+ ...captures.keys(),
406
+ ...[...acquired.observed.keys()].map((file) => path
407
+ .relative(request.source.root, file)
408
+ .split(path.sep)
409
+ .join("/")),
410
+ ],
411
+ packagePatterns: catalog.patterns,
412
+ packages: packageDirectories,
413
+ },
414
+ instructions: registryInstructions(artifacts),
415
+ };
416
+ })).pipe(Effect.provideService(FileSystem.FileSystem, fs), Effect.provideService(Path.Path, path));
417
+ });
418
+ return Composition.of({ prepare });
419
+ }));
420
+ }
@@ -0,0 +1,21 @@
1
+ import { Option, Stdio } from "effect";
2
+ import { Command, Prompt } from "effect/unstable/cli";
3
+ import { InvalidComposition } from "./errors.ts";
4
+ import { Workspaces } from "./workspaces.ts";
5
+ export declare const createCommand: (cwd: string) => Command.Command<"create-next-hydra", {
6
+ readonly addOns: readonly string[];
7
+ readonly auth: Option.Option<string>;
8
+ readonly cms: Option.Option<string>;
9
+ readonly commerce: Option.Option<string>;
10
+ readonly search: Option.Option<string>;
11
+ readonly commit: boolean;
12
+ readonly directory: Option.Option<string>;
13
+ readonly preset: Option.Option<string>;
14
+ readonly ref: Option.Option<string>;
15
+ readonly repository: string;
16
+ readonly skipGit: boolean;
17
+ readonly verbose: boolean;
18
+ readonly without: readonly ("search" | "auth" | "cms" | "commerce")[];
19
+ readonly yes: boolean;
20
+ }, {}, import("./errors.ts").IncompatibleSelection | InvalidComposition | import("./errors.ts").SourceChanged | import("./errors.ts").SourceAcquisitionFailed | import("./errors.ts").SourceStorageRetained | import("./errors.ts").DestinationNotEmpty | import("./errors.ts").RegistryFailure | import("./errors.ts").StagingRetained | import("./errors.ts").RegistryWorkerFailure | import("./errors.ts").MaterializationFailed | import("./errors.ts").EnvironmentInitializationFailed | import("./errors.ts").WorkspaceBusy | import("./errors.ts").WorkspaceLockAuthorizationRequired | import("./errors.ts").WorkspaceLockChanged | import("./errors.ts").WorkspaceConflict | import("./errors.ts").WorkspaceRecoveryRequired | import("./errors.ts").WorkspaceStateInvalid | import("./errors.ts").DependencyInstallationFailed | import("effect/Schema").SchemaError | import("effect/PlatformError").PlatformError | import("./application-git.ts").ApplicationGitFailed | import("effect/Terminal").QuitError, Workspaces | Stdio.Stdio | Prompt.Environment>;
21
+ //# sourceMappingURL=create-command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-command.d.ts","sourceRoot":"","sources":["../src/create-command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,MAAM,EAAQ,KAAK,EAAE,MAAM,QAAQ,CAAC;AAC9D,OAAO,EAAY,OAAO,EAAQ,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAEtE,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEjD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAgB7C,eAAO,MAAM,aAAa,GAAI,KAAK,MAAM;;;;;;;;;;;;;;;ygCAuIqC,CAAC"}
@@ -0,0 +1,128 @@
1
+ import { Console, Effect, Option, Path, Stdio } from "effect";
2
+ import { Argument, Command, Flag, Prompt } from "effect/unstable/cli";
3
+ import { InvalidComposition } from "./errors.js";
4
+ import { Workspaces } from "./workspaces.js";
5
+ const slots = ["auth", "cms", "commerce", "search"];
6
+ const choices = {
7
+ auth: [
8
+ { title: "WorkOS", value: "workos" },
9
+ { title: "Clerk", value: "clerk" },
10
+ ],
11
+ cms: [
12
+ { title: "Drupal", value: "drupal" },
13
+ { title: "Contentstack", value: "contentstack" },
14
+ ],
15
+ commerce: [{ title: "Commercetools", value: "commercetools" }],
16
+ search: [{ title: "Algolia", value: "algolia" }],
17
+ };
18
+ export const createCommand = (cwd) => Command.make("create-next-hydra", {
19
+ addOns: Flag.String("add-on").pipe(Flag.atLeast(0)),
20
+ auth: Flag.String("auth").pipe(Flag.optional),
21
+ cms: Flag.String("cms").pipe(Flag.optional),
22
+ commerce: Flag.String("commerce").pipe(Flag.optional),
23
+ search: Flag.String("search").pipe(Flag.optional),
24
+ commit: Flag.Boolean("commit").pipe(Flag.withDefault(true)),
25
+ directory: Argument.String("project-directory").pipe(Argument.optional),
26
+ preset: Flag.String("preset").pipe(Flag.optional),
27
+ ref: Flag.String("ref").pipe(Flag.optional),
28
+ repository: Flag.String("repo-url").pipe(Flag.withDefault("https://github.com/jakala-na/next-hydra.git")),
29
+ skipGit: Flag.Boolean("skip-git").pipe(Flag.withDefault(false)),
30
+ verbose: Flag.Boolean("verbose").pipe(Flag.withDefault(false)),
31
+ without: Flag.Literals("without", slots).pipe(Flag.atLeast(0)),
32
+ yes: Flag.Boolean("yes").pipe(Flag.withAlias("y"), Flag.withDefault(false)),
33
+ }, (options) => Effect.gen(function* () {
34
+ const path = yield* Path.Path;
35
+ const stdio = yield* Stdio.Stdio;
36
+ const interactive = !options.yes &&
37
+ (yield* stdio.stdinIsTerminal) &&
38
+ (yield* stdio.stdoutIsTerminal);
39
+ let directory = Option.getOrUndefined(options.directory)?.trim();
40
+ if (!directory) {
41
+ if (!interactive) {
42
+ return yield* new InvalidComposition({
43
+ message: "Provide a project directory for noninteractive creation.",
44
+ });
45
+ }
46
+ directory = yield* Prompt.String({
47
+ message: "Where should the project be created?",
48
+ validate: (value) => value.trim()
49
+ ? Effect.succeed(value.trim())
50
+ : Effect.fail("Enter a directory."),
51
+ });
52
+ }
53
+ const selected = {
54
+ auth: Option.getOrUndefined(options.auth),
55
+ cms: Option.getOrUndefined(options.cms),
56
+ commerce: Option.getOrUndefined(options.commerce),
57
+ search: Option.getOrUndefined(options.search),
58
+ };
59
+ if (slots.some((slot) => options.without.includes(slot) && selected[slot] !== undefined)) {
60
+ return yield* new InvalidComposition({
61
+ message: "A provider slot cannot be selected and excluded together.",
62
+ });
63
+ }
64
+ let selection;
65
+ if (Option.isSome(options.preset)) {
66
+ if (options.without.length || slots.some((slot) => selected[slot])) {
67
+ return yield* new InvalidComposition({
68
+ message: "--preset cannot be combined with provider or --without flags.",
69
+ });
70
+ }
71
+ selection = { addOns: options.addOns, preset: options.preset.value };
72
+ }
73
+ else {
74
+ const providers = {};
75
+ for (const slot of slots) {
76
+ if (options.without.includes(slot)) {
77
+ continue;
78
+ }
79
+ const explicit = selected[slot];
80
+ if (!explicit && !interactive) {
81
+ return yield* new InvalidComposition({
82
+ message: "Select every provider slot or leave it empty with --without, or use --preset.",
83
+ });
84
+ }
85
+ providers[slot] =
86
+ explicit ??
87
+ (yield* Prompt.Select({
88
+ choices: choices[slot],
89
+ message: `Choose the ${slot} provider`,
90
+ }));
91
+ }
92
+ selection = { addOns: options.addOns, providers };
93
+ }
94
+ const destination = path.resolve(cwd, directory);
95
+ // Git treats a colon before the first slash as remote syntax (URLs or scp).
96
+ const repository = path.isAbsolute(options.repository) ||
97
+ /^[^/]+:/u.test(options.repository)
98
+ ? options.repository
99
+ : path.resolve(cwd, options.repository);
100
+ const workspace = yield* (yield* Workspaces).fresh({
101
+ destination,
102
+ name: path.basename(destination),
103
+ selection,
104
+ source: {
105
+ kind: "repository",
106
+ ref: Option.getOrUndefined(options.ref),
107
+ repository,
108
+ },
109
+ });
110
+ if (options.verbose) {
111
+ yield* Console.log("Acquiring the requested source revision, composing files, then installing dependencies.");
112
+ }
113
+ let git = "initialize";
114
+ if (options.skipGit) {
115
+ git = "skip";
116
+ }
117
+ else if (options.commit) {
118
+ git = "commit";
119
+ }
120
+ const result = yield* workspace.materialize({ git });
121
+ if (result.git?.warning) {
122
+ yield* Console.warn(result.git.warning);
123
+ }
124
+ for (const instruction of result.instructions) {
125
+ yield* Console.log(`${instruction.item}:\n${instruction.text}`);
126
+ }
127
+ yield* Console.log(`Created ${result.destination}. Dependencies ${result.dependencies}. Run pnpm dev from the project directory.`);
128
+ })).pipe(Command.withDescription("Compose an application from the registry."));
@@ -0,0 +1,5 @@
1
+ import { Effect } from "effect";
2
+ import { InvalidComposition } from "./errors.ts";
3
+ export declare const demoArchitecturePackage = "@repo/demo-architecture";
4
+ export declare const eraseDemoInstrumentation: (target: string, source: string) => Effect.Effect<string, InvalidComposition, never>;
5
+ //# sourceMappingURL=demo-instrumentation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"demo-instrumentation.d.ts","sourceRoot":"","sources":["../src/demo-instrumentation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwB,MAAM,EAAS,MAAM,QAAQ,CAAC;AAG7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEjD,eAAO,MAAM,uBAAuB,4BAA4B,CAAC;AA4KjE,eAAO,MAAM,wBAAwB,GAAI,QAAQ,MAAM,EAAE,QAAQ,MAAM,qDAsPnE,CAAC"}