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/dist/errors.js ADDED
@@ -0,0 +1,140 @@
1
+ import { Runtime, Schema } from "effect";
2
+ export class ApplicationTaskFailed extends Schema.TaggedError()("ApplicationTaskFailed", {
3
+ code: Schema.Int,
4
+ directory: Schema.String,
5
+ }) {
6
+ get message() {
7
+ return `Application task failed in ${this.directory} (exit ${this.code}).`;
8
+ }
9
+ get [Runtime.errorExitCode]() {
10
+ return this.code;
11
+ }
12
+ }
13
+ export class IncompatibleSelection extends Schema.TaggedError()("IncompatibleSelection", {
14
+ conflicts: Schema.Array(Schema.String),
15
+ missing: Schema.Array(Schema.String),
16
+ selection: Schema.String,
17
+ }) {
18
+ get message() {
19
+ const reasons = [];
20
+ if (this.missing.length) {
21
+ reasons.push(`requires ${this.missing.join(", ")}`);
22
+ }
23
+ if (this.conflicts.length) {
24
+ reasons.push(`conflicts with ${this.conflicts.join(", ")}`);
25
+ }
26
+ return `${this.selection} ${reasons.join("; ")}`;
27
+ }
28
+ }
29
+ export class InvalidComposition extends Schema.TaggedError()("InvalidComposition", { message: Schema.String }) {
30
+ }
31
+ export class SourceChanged extends Schema.TaggedError()("SourceChanged", { paths: Schema.Array(Schema.String) }) {
32
+ }
33
+ export class SourceAcquisitionFailed extends Schema.TaggedError()("SourceAcquisitionFailed", { diagnostic: Schema.Redacted(Schema.Unknown), phase: Schema.String }) {
34
+ get message() {
35
+ return `Source acquisition failed during ${this.phase}; no application files were published.`;
36
+ }
37
+ }
38
+ export class SourceStorageRetained extends Schema.TaggedError()("SourceStorageRetained", { directory: Schema.String }) {
39
+ }
40
+ export class DestinationNotEmpty extends Schema.TaggedError()("DestinationNotEmpty", { directory: Schema.String }) {
41
+ }
42
+ export class RegistryFailure extends Schema.TaggedError()("RegistryFailure", { diagnostic: Schema.Redacted(Schema.String), operation: Schema.String }) {
43
+ }
44
+ export class StagingRetained extends Schema.TaggedError()("StagingRetained", { directory: Schema.String }) {
45
+ }
46
+ export class RegistryWorkerFailure extends Schema.TaggedError()("RegistryWorkerFailure", { phase: Schema.Literals(["launch", "exit", "protocol", "stop"]) }) {
47
+ }
48
+ export class MaterializationFailed extends Schema.TaggedError()("MaterializationFailed", {
49
+ completedFiles: Schema.Array(Schema.String),
50
+ diagnostic: Schema.Redacted(Schema.Unknown),
51
+ directory: Schema.String,
52
+ failedFile: Schema.String,
53
+ }) {
54
+ }
55
+ export class EnvironmentInitializationFailed extends Schema.TaggedError()("EnvironmentInitializationFailed", {
56
+ createdFiles: Schema.Array(Schema.String),
57
+ diagnostic: Schema.Redacted(Schema.Unknown),
58
+ directory: Schema.String,
59
+ failedFile: Schema.String,
60
+ }) {
61
+ get message() {
62
+ return `Environment initialization failed at ${this.failedFile}. Application files may already be present. Preserve and inspect this file before retrying; it may be incomplete.`;
63
+ }
64
+ }
65
+ export class WorkspaceBusy extends Schema.TaggedError()("WorkspaceBusy", { directory: Schema.String }) {
66
+ get message() {
67
+ return `Workspace write lock exists: ${this.directory}. Another operation may be running or may have stopped unexpectedly; no files were changed.`;
68
+ }
69
+ }
70
+ export class WorkspaceLockAuthorizationRequired extends Schema.TaggedError()("WorkspaceLockAuthorizationRequired", { directory: Schema.String, reason: Schema.String, token: Schema.String }) {
71
+ get message() {
72
+ return `Lock recovery needs authorization in ${this.directory}: ${this.reason}. Run Compose in an interactive terminal to authorize breaking this lock. Files and recovery evidence have been preserved.`;
73
+ }
74
+ }
75
+ export class WorkspaceLockChanged extends Schema.TaggedError()("WorkspaceLockChanged", { directory: Schema.String }) {
76
+ get message() {
77
+ return `The lock in ${this.directory} changed. This operation cannot continue; run Compose again to inspect the current lock.`;
78
+ }
79
+ }
80
+ export class WorkspaceConflict extends Schema.TaggedError()("WorkspaceConflict", { paths: Schema.Array(Schema.String) }) {
81
+ get message() {
82
+ return `Refresh blocked by local changes: ${this.paths.join(", ")}`;
83
+ }
84
+ }
85
+ export class WorkspaceRecoveryRequired extends Schema.TaggedError()("WorkspaceRecoveryRequired", {
86
+ directory: Schema.String,
87
+ paths: Schema.optionalKey(Schema.Array(Schema.String)),
88
+ }) {
89
+ get message() {
90
+ if (this.paths !== undefined && this.paths.length > 0) {
91
+ return `Initialization needs inspection in ${this.directory}: ${this.paths.join(", ")}. A write started but completion was not recorded. Preserve and inspect these files; they may be incomplete.`;
92
+ }
93
+ return `An incomplete workspace operation needs inspection: ${this.directory}. Files have been preserved; unresolved initialization, installation or write coordination cannot be recovered automatically.`;
94
+ }
95
+ }
96
+ export class WorkspaceStateInvalid extends Schema.TaggedError()("WorkspaceStateInvalid", { directory: Schema.String }) {
97
+ get message() {
98
+ return `Workspace ownership state is invalid or has changed: ${this.directory}. No ownership was inferred from application files.`;
99
+ }
100
+ }
101
+ export class WorkspaceNotCurrent extends Schema.TaggedError()("WorkspaceNotCurrent", { directory: Schema.String }) {
102
+ get message() {
103
+ return `Workspace needs attention: ${this.directory}. Check did not change any files or install dependencies.`;
104
+ }
105
+ }
106
+ export class WorkspaceBatchFailed extends Schema.TaggedError()("WorkspaceBatchFailed", { names: Schema.Array(Schema.String) }) {
107
+ get message() {
108
+ return `Workspaces needing attention: ${this.names.join(", ")}. Other workspaces were processed independently; rerun after resolving the reported issues.`;
109
+ }
110
+ }
111
+ export class SnapshotUnavailable extends Schema.TaggedError()("SnapshotUnavailable", { directory: Schema.String }) {
112
+ get message() {
113
+ return `No usable composition snapshot for ${this.directory}. Existing files remain untouched; synchronize only after reconciling local edits.`;
114
+ }
115
+ }
116
+ export class WorkspaceUninitialized extends Schema.TaggedError()("WorkspaceUninitialized", { directory: Schema.String }) {
117
+ get message() {
118
+ return `No recorded composition exists for ${this.directory}. Existing application files were not adopted.`;
119
+ }
120
+ }
121
+ export class SnapshotFailed extends Schema.TaggedError()("SnapshotFailed", { directory: Schema.String, operation: Schema.String }) {
122
+ get message() {
123
+ return `Composition snapshot ${this.operation} failed for ${this.directory}. The saved baseline was not advanced.`;
124
+ }
125
+ }
126
+ export class DependencyInstallationFailed extends Schema.TaggedError()("DependencyInstallationFailed", {
127
+ directory: Schema.String,
128
+ phase: Schema.Literals([
129
+ "launch",
130
+ "exit",
131
+ "stop",
132
+ "inputs",
133
+ "outputs",
134
+ "version",
135
+ ]),
136
+ }) {
137
+ get message() {
138
+ return `Dependency installation failed (${this.phase}) in ${this.directory}. Application files remain applied; dependencies are not current.`;
139
+ }
140
+ }
@@ -0,0 +1,6 @@
1
+ export declare const cacheDirectories: ReadonlySet<string>;
2
+ export declare function environmentFileType(target: string): "runtime" | "example" | undefined;
3
+ export declare function isEnvironmentFile(target: string): boolean;
4
+ export declare function isCanonicalSource(file: string): boolean;
5
+ export declare function eligiblePackageFile(file: string): boolean;
6
+ //# sourceMappingURL=file-policy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-policy.d.ts","sourceRoot":"","sources":["../src/file-policy.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,gBAAgB,EAAE,WAAW,CAAC,MAAM,CAQ/C,CAAC;AAIH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,MAAM,GACb,SAAS,GAAG,SAAS,GAAG,SAAS,CAQnC;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAEzD;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAYvD;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAQzD"}
@@ -0,0 +1,38 @@
1
+ // Classification is shared; each operation still decides which categories it owns.
2
+ export const cacheDirectories = new Set([
3
+ "node_modules",
4
+ "dist",
5
+ ".next",
6
+ ".turbo",
7
+ ".cache",
8
+ ".swc",
9
+ ".vercel",
10
+ ]);
11
+ const environmentExamples = [".example", ".sample", ".template"];
12
+ export function environmentFileType(target) {
13
+ const name = target.slice(target.lastIndexOf("/") + 1);
14
+ if (name !== ".env" && !name.startsWith(".env.")) {
15
+ return undefined;
16
+ }
17
+ return environmentExamples.some((suffix) => name.endsWith(suffix))
18
+ ? "example"
19
+ : "runtime";
20
+ }
21
+ export function isEnvironmentFile(target) {
22
+ return environmentFileType(target) === "runtime";
23
+ }
24
+ export function isCanonicalSource(file) {
25
+ const parts = file.split("/");
26
+ return (parts[0] !== "workspaces" &&
27
+ !parts.some((part) => part === ".git" ||
28
+ cacheDirectories.has(part) ||
29
+ part.startsWith(".workspace-composition")) &&
30
+ !isEnvironmentFile(file));
31
+ }
32
+ export function eligiblePackageFile(file) {
33
+ const name = file.slice(file.lastIndexOf("/") + 1);
34
+ return (!file.includes("/registry/") &&
35
+ name !== "registry.json" &&
36
+ !name.endsWith(".template") &&
37
+ !isEnvironmentFile(file));
38
+ }
@@ -0,0 +1,7 @@
1
+ import { Effect, FileSystem, Path } from "effect";
2
+ import { InvalidComposition } from "./errors.ts";
3
+ import type { PreparedFile } from "./model.ts";
4
+ export declare const relativeFile: (input: string) => Effect.Effect<string, InvalidComposition>;
5
+ export declare const writeFile: (root: string, file: PreparedFile, exclusive?: boolean | undefined) => Effect.Effect<void, InvalidComposition | import("effect/PlatformError").PlatformError, FileSystem.FileSystem | Path.Path>;
6
+ export declare const collectFiles: (root: string) => Effect.Effect<PreparedFile[], InvalidComposition | import("effect/PlatformError").PlatformError, FileSystem.FileSystem | Path.Path>;
7
+ //# sourceMappingURL=files.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"files.d.ts","sourceRoot":"","sources":["../src/files.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAElD,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,eAAO,MAAM,YAAY,GACvB,OAAO,MAAM,KACZ,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAc1C,CAAC;AAEF,eAAO,MAAM,SAAS,kMAmBpB,CAAC;AAEH,eAAO,MAAM,YAAY,uJAkCvB,CAAC"}
package/dist/files.js ADDED
@@ -0,0 +1,66 @@
1
+ import { Effect, FileSystem, Path } from "effect";
2
+ import { InvalidComposition } from "./errors.js";
3
+ export const relativeFile = (input) => {
4
+ const value = input.startsWith("~/") ? input.slice(2) : input;
5
+ return value.length === 0 ||
6
+ value
7
+ .split("/")
8
+ .some((part) => ["", ".", "..", ".git", "node_modules"].includes(part)) ||
9
+ /[:\\]/u.test(value) ||
10
+ value.includes(String.fromCodePoint(0))
11
+ ? Effect.fail(new InvalidComposition({
12
+ message: `Expected a workspace-relative file: ${input}`,
13
+ }))
14
+ : Effect.succeed(value);
15
+ };
16
+ export const writeFile = Effect.fn("Composition.writeFile")(function* (root, file, exclusive) {
17
+ const fs = yield* FileSystem.FileSystem;
18
+ const path = yield* Path.Path;
19
+ const target = path.join(root, yield* relativeFile(file.target));
20
+ // Join each mutation before its storage owner can release the directory.
21
+ yield* fs
22
+ .makeDirectory(path.dirname(target), { recursive: true })
23
+ .pipe(Effect.uninterruptible);
24
+ yield* fs
25
+ .writeFile(target, file.content, {
26
+ flag: exclusive === true ? "wx" : "w",
27
+ mode: file.mode,
28
+ })
29
+ .pipe(Effect.uninterruptible);
30
+ yield* fs.chmod(target, file.mode).pipe(Effect.uninterruptible);
31
+ });
32
+ export const collectFiles = Effect.fn("Composition.collectFiles")(function* (root) {
33
+ const fs = yield* FileSystem.FileSystem;
34
+ const path = yield* Path.Path;
35
+ const files = [];
36
+ const pending = [""];
37
+ while (pending.length > 0) {
38
+ const directory = pending.pop();
39
+ if (directory === undefined) {
40
+ break;
41
+ }
42
+ for (const name of yield* fs
43
+ .readDirectory(path.join(root, directory))
44
+ .pipe(Effect.uninterruptible)) {
45
+ const target = directory ? `${directory}/${name}` : name;
46
+ const absolute = path.join(root, target);
47
+ const stat = yield* fs.stat(absolute).pipe(Effect.uninterruptible);
48
+ if (stat.type === "Directory") {
49
+ pending.push(target);
50
+ }
51
+ else if (stat.type === "File") {
52
+ files.push({
53
+ content: yield* fs.readFile(absolute).pipe(Effect.uninterruptible),
54
+ mode: stat.mode % 0o1000,
55
+ target,
56
+ });
57
+ }
58
+ else {
59
+ return yield* new InvalidComposition({
60
+ message: `Unsupported materialized entry: ${target}`,
61
+ });
62
+ }
63
+ }
64
+ }
65
+ return files;
66
+ });
@@ -0,0 +1,5 @@
1
+ import { Effect, Schema } from "effect";
2
+ import { InvalidComposition } from "./errors.ts";
3
+ import type { PreparedFile } from "./model.ts";
4
+ export declare const scopeApplicationHosts: (files: readonly PreparedFile[], name: string, opaqueTargets: ReadonlySet<string>, port?: number | undefined) => Effect.Effect<PreparedFile[], InvalidComposition | Schema.SchemaError, never>;
5
+ //# sourceMappingURL=hosting.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hosting.d.ts","sourceRoot":"","sources":["../src/hosting.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAGxC,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AA2B/C,eAAO,MAAM,qBAAqB,gMAyHhC,CAAC"}
@@ -0,0 +1,107 @@
1
+ import { isUtf8 } from "node:buffer";
2
+ import { Effect, Schema } from "effect";
3
+ import { yamlDocument } from "./baseline.js";
4
+ import { InvalidComposition } from "./errors.js";
5
+ import { DevelopmentPort } from "./model.js";
6
+ const extras = [Schema.Record(Schema.String, Schema.Unknown)];
7
+ const DdevConfig = Schema.Struct({
8
+ name: Schema.optionalKey(Schema.NonEmptyString),
9
+ project_tld: Schema.optionalKey(Schema.NonEmptyString),
10
+ });
11
+ const Manifest = Schema.fromJsonString(Schema.StructWithRest(Schema.Struct({
12
+ portless: Schema.optionalKey(Schema.StructWithRest(Schema.Struct({
13
+ name: Schema.optionalKey(Schema.String),
14
+ script: Schema.String,
15
+ }), extras)),
16
+ scripts: Schema.optionalKey(Schema.Record(Schema.String, Schema.String)),
17
+ }), extras), { space: 2 });
18
+ export const scopeApplicationHosts = Effect.fn("Composition.scopeApplicationHosts")(function* (files, name, opaqueTargets, port) {
19
+ if (port !== undefined) {
20
+ yield* Schema.decodeEffect(DevelopmentPort)(port);
21
+ }
22
+ const namespace = name
23
+ .replaceAll(/[._]/gu, "-")
24
+ .slice(0, 63)
25
+ .replace(/-+$/u, "");
26
+ const hosts = new Map();
27
+ const output = [];
28
+ let ddevTarget;
29
+ for (const file of files) {
30
+ if (!opaqueTargets.has(file.target) &&
31
+ /^apps\/[^/]+\/\.ddev\/config\.yaml$/u.test(file.target)) {
32
+ if (ddevTarget !== undefined) {
33
+ return yield* new InvalidComposition({
34
+ message: `Only one DDEV app can use the application name: ${ddevTarget}, ${file.target}`,
35
+ });
36
+ }
37
+ ddevTarget = file.target;
38
+ const { data, document } = yield* yamlDocument(file.content, file.target, DdevConfig);
39
+ const originalName = data.name ?? file.target.split("/")[1];
40
+ const tld = data.project_tld ?? "ddev.site";
41
+ hosts.set(`${originalName}.${tld}`, `${namespace}.${tld}`);
42
+ document.set("name", namespace);
43
+ output.push({
44
+ ...file,
45
+ content: new TextEncoder().encode(document.toString()),
46
+ });
47
+ continue;
48
+ }
49
+ if (opaqueTargets.has(file.target) ||
50
+ !/^apps\/[^/]+\/package\.json$/u.test(file.target)) {
51
+ output.push(file);
52
+ continue;
53
+ }
54
+ const manifest = yield* Schema.decodeEffect(Manifest)(new TextDecoder().decode(file.content));
55
+ if (!manifest.portless) {
56
+ output.push(file);
57
+ continue;
58
+ }
59
+ const scripts = manifest.scripts ?? {};
60
+ if (!(scripts.dev === "portless" || scripts.dev?.startsWith("portless ")) ||
61
+ manifest.portless.script === "dev" ||
62
+ !scripts[manifest.portless.script]) {
63
+ return yield* new InvalidComposition({
64
+ message: `Invalid Portless development command: ${file.target}`,
65
+ });
66
+ }
67
+ const [, application] = file.target.split("/");
68
+ const appPort = port === undefined
69
+ ? undefined
70
+ : new Map([
71
+ ["web", port],
72
+ ["api", port + 1],
73
+ ["admin", port + 2],
74
+ ]).get(application ?? "");
75
+ const scopedName = `${application}.${namespace}`;
76
+ if (manifest.portless.name !== undefined) {
77
+ const hostname = `${manifest.portless.name}.localhost`;
78
+ if (hosts.has(hostname)) {
79
+ return yield* new InvalidComposition({
80
+ message: `Duplicate Portless hostname: ${hostname}`,
81
+ });
82
+ }
83
+ hosts.set(hostname, `${scopedName}.localhost`);
84
+ }
85
+ output.push({
86
+ ...file,
87
+ content: new TextEncoder().encode(`${yield* Schema.encodeEffect(Manifest)({ ...manifest, portless: { ...manifest.portless, appPort, name: scopedName } })}\n`),
88
+ });
89
+ }
90
+ if (hosts.size === 0) {
91
+ return output;
92
+ }
93
+ const escaped = [...hosts.keys()].map((hostname) => hostname.replaceAll(/[.*+?^${}()|[\]\\]/gu, "\\$&"));
94
+ const pattern = new RegExp(`(?<![\\w.-])(?:${escaped.join("|")})(?![\\w.-])`, "gu");
95
+ return output.map((file) => {
96
+ if (opaqueTargets.has(file.target) ||
97
+ !isUtf8(file.content) ||
98
+ file.content.includes(0)) {
99
+ return file;
100
+ }
101
+ const text = new TextDecoder().decode(file.content);
102
+ const updated = text.replaceAll(pattern, (hostname) => hosts.get(hostname) ?? hostname);
103
+ return updated === text
104
+ ? file
105
+ : { ...file, content: new TextEncoder().encode(updated) };
106
+ });
107
+ });
package/dist/index.d.ts CHANGED
@@ -1,7 +1,5 @@
1
- import { useComposition } from "./composition/use.js";
2
- type CliDependencies = {
3
- useComposition?: typeof useComposition;
4
- };
5
- export declare function runCli(argv?: string[], dependencies?: CliDependencies): Promise<void>;
6
- export type { CreateOptions, ResolvedCreateOptions, ScaffoldResult, StarterDefinition, } from "./types.js";
1
+ import { Effect, FileSystem, Path, Schema } from "effect";
2
+ export declare const cliProgram: (argv?: readonly string[]) => Effect.Effect<void, import("./errors.ts").DestinationNotEmpty | 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").WorkspaceNotCurrent | import("./errors.ts").WorkspaceBatchFailed | import("./errors.ts").SnapshotUnavailable | import("./errors.ts").WorkspaceUninitialized | import("./errors.ts").SnapshotFailed | import("./errors.ts").DependencyInstallationFailed | import("effect/PlatformError").BadArgument | import("./addition.ts").AdditionChanged | import("./addition.ts").AdditionConflict | import("./addition.ts").AdditionFailed | import("./application-git.ts").ApplicationGitFailed | import("./source-changes.ts").SourceWatchFailure | (import("./errors.ts").IncompatibleSelection | import("./errors.ts").InvalidComposition | import("./errors.ts").SourceChanged | import("./errors.ts").SourceAcquisitionFailed | import("./errors.ts").SourceStorageRetained | import("./errors.ts").RegistryFailure | import("./errors.ts").StagingRetained | import("./errors.ts").RegistryWorkerFailure | Schema.SchemaError | import("effect/PlatformError").PlatformError) | import("effect/unstable/cli/CliError").CliError, FileSystem.FileSystem | Path.Path | import("effect/unstable/process/ChildProcessSpawner").ChildProcessSpawner | import("./workspaces.ts").Workspaces | import("effect/Terminal").Terminal | import("effect/Stdio").Stdio>;
3
+ export declare const runCli: (argv?: readonly string[]) => Promise<void>;
4
+ export type { CreateOptions, ResolvedCreateOptions, ScaffoldResult, StarterDefinition, } from "./types.ts";
7
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAsBtD,KAAK,eAAe,GAAG;IACrB,cAAc,CAAC,EAAE,OAAO,cAAc,CAAC;CACxC,CAAC;AA0BF,wBAAsB,MAAM,CAC1B,IAAI,WAAe,EACnB,YAAY,GAAE,eAAoB,GACjC,OAAO,CAAC,IAAI,CAAC,CA4Ff;AACD,YAAY,EACV,aAAa,EACb,qBAAqB,EACrB,cAAc,EACd,iBAAiB,GAClB,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAK1D,eAAO,MAAM,UAAU,GAAI,OAAM,SAAS,MAAM,EAAiB,kpDAiB7D,CAAC;AAIL,eAAO,MAAM,MAAM,GACjB,OAAM,SAAS,MAAM,EAAiB,KACrC,OAAO,CAAC,IAAI,CAEd,CAAC;AAEF,YAAY,EACV,aAAa,EACb,qBAAqB,EACrB,cAAc,EACd,iBAAiB,GAClB,MAAM,YAAY,CAAC"}
package/dist/index.js CHANGED
@@ -1,89 +1,17 @@
1
- import { Command } from "commander";
2
- import { addRegistryItem } from "./composition/add.js";
3
- import { useComposition } from "./composition/use.js";
4
- import { CLI_NAME, DEFAULT_REPO_URL } from "./constants.js";
5
- import { promptForTargetDirectory } from "./prompts.js";
6
- import { scaffoldProject } from "./scaffold.js";
7
- import { CLI_VERSION } from "./version.js";
8
- function buildCreateOptions(targetDir, rawOptions) {
9
- return {
10
- addOns: rawOptions.addOn,
11
- auth: rawOptions.auth,
12
- cms: rawOptions.cms,
13
- commerce: rawOptions.commerce,
14
- commit: rawOptions.commit ?? true,
15
- preset: rawOptions.preset,
16
- ref: rawOptions.ref,
17
- repoUrl: rawOptions.repoUrl ?? DEFAULT_REPO_URL,
18
- skipGit: rawOptions.skipGit ?? false,
19
- targetDir,
20
- verbose: rawOptions.verbose ?? false,
21
- yes: rawOptions.yes ?? false,
22
- };
23
- }
24
- export async function runCli(argv = process.argv, dependencies = {}) {
25
- const program = new Command().enablePositionalOptions();
26
- program
27
- .name(CLI_NAME)
28
- .description("Scaffold a new next-hydra project")
29
- .argument("[project-directory]", "Target directory")
30
- .option("-y, --yes", "Skip prompts (requires [project-directory])")
31
- .option("--skip-git", "Skip git initialization")
32
- .option("--no-commit", "Initialize git but skip initial commit")
33
- .option("--ref <git-ref>", "Clone and checkout a specific git ref")
34
- .option("--repo-url <url>", "Override the starter repo URL")
35
- .option("--auth <provider>", "Select the Auth provider")
36
- .option("--cms <provider>", "Select the CMS provider")
37
- .option("--commerce <provider>", "Select the Commerce provider")
38
- .option("--add-on <selection>", "Select an Add-on (repeatable)", (value, previous) => [
39
- ...(previous ?? []),
40
- value,
41
- ])
42
- .option("--preset <selection>", "Use a portable next-hydra preset")
43
- .option("--verbose", "Print git command output")
44
- .version(CLI_VERSION)
45
- .action(async (projectDirectory, rawOptions) => {
46
- let targetDir = projectDirectory?.trim();
47
- if (!targetDir) {
48
- if (rawOptions.yes) {
49
- throw new Error("`--yes` requires a target directory, e.g. `create-next-hydra my-app --yes`.");
50
- }
51
- targetDir = await promptForTargetDirectory();
52
- }
53
- await scaffoldProject(buildCreateOptions(targetDir, rawOptions));
54
- });
55
- program
56
- .command("use")
57
- .description("Change or check the maintainer workspace composition")
58
- .option("--auth <provider>", "Select the Auth provider")
59
- .option("--cms <provider>", "Select the CMS provider")
60
- .option("--commerce <provider>", "Select the Commerce provider")
61
- .option("--add-on <selection>", "Select an Add-on (repeatable)", (value, previous) => [
62
- ...(previous ?? []),
63
- value,
64
- ])
65
- .option("--preset <selection>", "Use a portable next-hydra preset")
66
- .option("--check", "Check for composition drift without writing")
67
- .option("--dry-run", "Preview and validate changes without writing")
68
- .option("-y, --yes", "Apply changes without confirmation")
69
- .option("--verbose", "Print package-manager output")
70
- .action(async (rawOptions) => {
71
- await (dependencies.useComposition ?? useComposition)({
72
- ...rawOptions,
73
- addOns: rawOptions.addOn,
74
- });
75
- });
76
- program
77
- .command("add")
78
- .description("Add a registry item to a customer-owned workspace")
79
- .argument("<item-or-url>", "Registry item, URL, or local item JSON")
80
- .option("-y, --yes", "Skip confirmation prompts")
81
- .option("-o, --overwrite", "Overwrite changed files and package entries")
82
- .action(async (reference, rawOptions) => {
83
- await addRegistryItem(reference, {
84
- overwrite: rawOptions.overwrite,
85
- yes: rawOptions.yes,
86
- });
87
- });
88
- await program.parseAsync(argv);
89
- }
1
+ import { Effect, FileSystem, Path, Schema } from "effect";
2
+ import { runCommand } from "./commands.js";
3
+ import { runtime } from "./runtime.js";
4
+ export const cliProgram = (argv = process.argv) => Effect.gen(function* () {
5
+ const fs = yield* FileSystem.FileSystem;
6
+ const path = yield* Path.Path;
7
+ const packagePath = yield* path.fromFileUrl(new URL("../package.json", import.meta.url));
8
+ const metadata = yield* fs
9
+ .readFileString(packagePath)
10
+ .pipe(Effect.flatMap(Schema.decodeEffect(Schema.fromJsonString(Schema.Struct({ version: Schema.String })))));
11
+ yield* runCommand(process.cwd(), argv.slice(2), metadata.version);
12
+ });
13
+ // The Promise boundary belongs to callers embedding the CLI. All commands share
14
+ // the same Effect program and Layer as the executable.
15
+ export const runCli = async (argv = process.argv) => {
16
+ await Effect.runPromise(cliProgram(argv).pipe(Effect.provide(runtime)));
17
+ };
@@ -0,0 +1,14 @@
1
+ import { Effect, FileSystem, Path } from "effect";
2
+ import { DestinationNotEmpty, InvalidComposition, SourceChanged } from "./errors.ts";
3
+ type WorkspaceSettingKind = "definition" | "readme" | "ignore" | "deployment";
4
+ export declare function workspaceSetting(target: string): WorkspaceSettingKind | undefined;
5
+ export declare const validatePreservedFiles: (targets: readonly string[]) => Effect.Effect<undefined, InvalidComposition, never>;
6
+ export declare const namedInitializationFiles: (files: readonly (import("./model.ts").PreparedFile & {
7
+ readonly origin: import("./model.ts").FileOrigin;
8
+ })[], preserved: ReadonlySet<string>, preserve?: readonly string[] | undefined) => Effect.Effect<(import("./model.ts").PreparedFile & {
9
+ readonly origin: import("./model.ts").FileOrigin;
10
+ })[], InvalidComposition, never>;
11
+ export declare const listUnregisteredFiles: (destination: string, owned: ReadonlySet<string>) => Effect.Effect<string[], DestinationNotEmpty | import("effect/PlatformError").PlatformError, FileSystem.FileSystem | Path.Path>;
12
+ export declare const inspectInitialization: (destination: string, definition?: string | undefined) => Effect.Effect<Set<string>, SourceChanged | DestinationNotEmpty | import("effect/PlatformError").PlatformError, FileSystem.FileSystem | Path.Path>;
13
+ export {};
14
+ //# sourceMappingURL=initialization.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"initialization.d.ts","sourceRoot":"","sources":["../src/initialization.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAElD,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,aAAa,EACd,MAAM,aAAa,CAAC;AAKrB,KAAK,oBAAoB,GAAG,YAAY,GAAG,QAAQ,GAAG,QAAQ,GAAG,YAAY,CAAC;AAsB9E,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,MAAM,GACb,oBAAoB,GAAG,SAAS,CAgBlC;AAED,eAAO,MAAM,sBAAsB,qFAiCjC,CAAC;AA+BH,eAAO,MAAM,wBAAwB;;;;gCA2DnC,CAAC;AAEH,eAAO,MAAM,qBAAqB,qLA6DhC,CAAC;AA+BH,eAAO,MAAM,qBAAqB,6MAmDhC,CAAC"}