hatchkit 0.1.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 (149) hide show
  1. package/dist/config.d.ts +131 -0
  2. package/dist/config.d.ts.map +1 -0
  3. package/dist/config.js +629 -0
  4. package/dist/config.js.map +1 -0
  5. package/dist/deploy/coolify.d.ts +4 -0
  6. package/dist/deploy/coolify.d.ts.map +1 -0
  7. package/dist/deploy/coolify.js +20 -0
  8. package/dist/deploy/coolify.js.map +1 -0
  9. package/dist/deploy/github.d.ts +4 -0
  10. package/dist/deploy/github.d.ts.map +1 -0
  11. package/dist/deploy/github.js +39 -0
  12. package/dist/deploy/github.js.map +1 -0
  13. package/dist/deploy/gpu.d.ts +4 -0
  14. package/dist/deploy/gpu.d.ts.map +1 -0
  15. package/dist/deploy/gpu.js +97 -0
  16. package/dist/deploy/gpu.js.map +1 -0
  17. package/dist/deploy/keys.d.ts +9 -0
  18. package/dist/deploy/keys.d.ts.map +1 -0
  19. package/dist/deploy/keys.js +73 -0
  20. package/dist/deploy/keys.js.map +1 -0
  21. package/dist/deploy/terraform.d.ts +4 -0
  22. package/dist/deploy/terraform.d.ts.map +1 -0
  23. package/dist/deploy/terraform.js +55 -0
  24. package/dist/deploy/terraform.js.map +1 -0
  25. package/dist/index.d.ts +3 -0
  26. package/dist/index.d.ts.map +1 -0
  27. package/dist/index.js +599 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/prompts.d.ts +52 -0
  30. package/dist/prompts.d.ts.map +1 -0
  31. package/dist/prompts.js +313 -0
  32. package/dist/prompts.js.map +1 -0
  33. package/dist/provision/glitchtip.d.ts +6 -0
  34. package/dist/provision/glitchtip.d.ts.map +1 -0
  35. package/dist/provision/glitchtip.js +46 -0
  36. package/dist/provision/glitchtip.js.map +1 -0
  37. package/dist/provision/index.d.ts +9 -0
  38. package/dist/provision/index.d.ts.map +1 -0
  39. package/dist/provision/index.js +108 -0
  40. package/dist/provision/index.js.map +1 -0
  41. package/dist/provision/openpanel.d.ts +8 -0
  42. package/dist/provision/openpanel.d.ts.map +1 -0
  43. package/dist/provision/openpanel.js +66 -0
  44. package/dist/provision/openpanel.js.map +1 -0
  45. package/dist/provision/resend.d.ts +16 -0
  46. package/dist/provision/resend.d.ts.map +1 -0
  47. package/dist/provision/resend.js +43 -0
  48. package/dist/provision/resend.js.map +1 -0
  49. package/dist/scaffold/app.d.ts +13 -0
  50. package/dist/scaffold/app.d.ts.map +1 -0
  51. package/dist/scaffold/app.js +340 -0
  52. package/dist/scaffold/app.js.map +1 -0
  53. package/dist/scaffold/dotenvx.d.ts +30 -0
  54. package/dist/scaffold/dotenvx.d.ts.map +1 -0
  55. package/dist/scaffold/dotenvx.js +142 -0
  56. package/dist/scaffold/dotenvx.js.map +1 -0
  57. package/dist/scaffold/infra.d.ts +17 -0
  58. package/dist/scaffold/infra.d.ts.map +1 -0
  59. package/dist/scaffold/infra.js +200 -0
  60. package/dist/scaffold/infra.js.map +1 -0
  61. package/dist/scaffold/manifest.d.ts +50 -0
  62. package/dist/scaffold/manifest.d.ts.map +1 -0
  63. package/dist/scaffold/manifest.js +83 -0
  64. package/dist/scaffold/manifest.js.map +1 -0
  65. package/dist/scaffold/ml-client.d.ts +20 -0
  66. package/dist/scaffold/ml-client.d.ts.map +1 -0
  67. package/dist/scaffold/ml-client.js +38 -0
  68. package/dist/scaffold/ml-client.js.map +1 -0
  69. package/dist/scaffold/pkg-json.d.ts +20 -0
  70. package/dist/scaffold/pkg-json.d.ts.map +1 -0
  71. package/dist/scaffold/pkg-json.js +113 -0
  72. package/dist/scaffold/pkg-json.js.map +1 -0
  73. package/dist/scaffold/starter-files.d.ts +40 -0
  74. package/dist/scaffold/starter-files.d.ts.map +1 -0
  75. package/dist/scaffold/starter-files.js +197 -0
  76. package/dist/scaffold/starter-files.js.map +1 -0
  77. package/dist/scaffold/update.d.ts +8 -0
  78. package/dist/scaffold/update.d.ts.map +1 -0
  79. package/dist/scaffold/update.js +255 -0
  80. package/dist/scaffold/update.js.map +1 -0
  81. package/dist/templates/addons/analytics/middleware.ts.hbs +13 -0
  82. package/dist/templates/addons/analytics/sentry.ts.hbs +16 -0
  83. package/dist/templates/addons/storage/s3.ts.hbs +40 -0
  84. package/dist/templates/addons/storage/upload.ts.hbs +23 -0
  85. package/dist/templates/addons/stripe/checkout.ts.hbs +27 -0
  86. package/dist/templates/addons/stripe/client.ts.hbs +6 -0
  87. package/dist/templates/addons/stripe/webhook.ts.hbs +39 -0
  88. package/dist/templates/addons/websocket/redis.ts.hbs +25 -0
  89. package/dist/templates/addons/websocket/ws.ts.hbs +32 -0
  90. package/dist/templates/base/.dockerignore.hbs +7 -0
  91. package/dist/templates/base/Dockerfile.hbs +18 -0
  92. package/dist/templates/base/env.example.hbs +60 -0
  93. package/dist/templates/base/github-actions.yml.hbs +35 -0
  94. package/dist/templates/base/gitignore.hbs +5 -0
  95. package/dist/templates/base/package.json.hbs +36 -0
  96. package/dist/templates/base/src/auth/auth.ts.hbs +13 -0
  97. package/dist/templates/base/src/auth/routes.ts.hbs +19 -0
  98. package/dist/templates/base/src/config.ts.hbs +36 -0
  99. package/dist/templates/base/src/db.ts.hbs +12 -0
  100. package/dist/templates/base/src/index.ts.hbs +80 -0
  101. package/dist/templates/base/src/routes/health.ts.hbs +12 -0
  102. package/dist/templates/base/tsconfig.json.hbs +18 -0
  103. package/dist/templates/ml-clients/3d-extraction.ts.hbs +20 -0
  104. package/dist/templates/ml-clients/background-removal.ts.hbs +17 -0
  105. package/dist/templates/ml-clients/custom-hf.ts.hbs +20 -0
  106. package/dist/templates/ml-clients/image-recognition.ts.hbs +22 -0
  107. package/dist/templates/ml-clients/subtitles.ts.hbs +26 -0
  108. package/dist/utils/coolify-api.d.ts +45 -0
  109. package/dist/utils/coolify-api.d.ts.map +1 -0
  110. package/dist/utils/coolify-api.js +72 -0
  111. package/dist/utils/coolify-api.js.map +1 -0
  112. package/dist/utils/errors.d.ts +2 -0
  113. package/dist/utils/errors.d.ts.map +1 -0
  114. package/dist/utils/errors.js +31 -0
  115. package/dist/utils/errors.js.map +1 -0
  116. package/dist/utils/exec.d.ts +23 -0
  117. package/dist/utils/exec.d.ts.map +1 -0
  118. package/dist/utils/exec.js +47 -0
  119. package/dist/utils/exec.js.map +1 -0
  120. package/dist/utils/flags.d.ts +17 -0
  121. package/dist/utils/flags.d.ts.map +1 -0
  122. package/dist/utils/flags.js +116 -0
  123. package/dist/utils/flags.js.map +1 -0
  124. package/dist/utils/hf-api.d.ts +13 -0
  125. package/dist/utils/hf-api.d.ts.map +1 -0
  126. package/dist/utils/hf-api.js +30 -0
  127. package/dist/utils/hf-api.js.map +1 -0
  128. package/dist/utils/ports.d.ts +29 -0
  129. package/dist/utils/ports.d.ts.map +1 -0
  130. package/dist/utils/ports.js +86 -0
  131. package/dist/utils/ports.js.map +1 -0
  132. package/dist/utils/secrets.d.ts +25 -0
  133. package/dist/utils/secrets.d.ts.map +1 -0
  134. package/dist/utils/secrets.js +51 -0
  135. package/dist/utils/secrets.js.map +1 -0
  136. package/dist/utils/template.d.ts +7 -0
  137. package/dist/utils/template.d.ts.map +1 -0
  138. package/dist/utils/template.js +35 -0
  139. package/dist/utils/template.js.map +1 -0
  140. package/dist/utils/validate.d.ts +16 -0
  141. package/dist/utils/validate.d.ts.map +1 -0
  142. package/dist/utils/validate.js +60 -0
  143. package/dist/utils/validate.js.map +1 -0
  144. package/dist/utils/version.d.ts +2 -0
  145. package/dist/utils/version.d.ts.map +1 -0
  146. package/dist/utils/version.js +21 -0
  147. package/dist/utils/version.js.map +1 -0
  148. package/package.json +48 -0
  149. package/scripts/copy-templates.mjs +20 -0
@@ -0,0 +1,66 @@
1
+ /*
2
+ * OpenPanel provisioning — creates a project + a client (write key
3
+ * pair) inside an existing self-hosted OpenPanel instance.
4
+ *
5
+ * OpenPanel's self-hosted API is still evolving, so this uses a
6
+ * best-effort path and falls back to a paste-in flow if the API call
7
+ * fails. Either way the resulting client id/secret are cached in the
8
+ * keychain under `openpanel:<name>:client-secret` so re-runs are idempotent.
9
+ */
10
+ import { input, password as passwordPrompt } from "@inquirer/prompts";
11
+ import { ensureOpenpanel } from "../config.js";
12
+ import { SECRET_KEYS, getSecret, setSecret } from "../utils/secrets.js";
13
+ export async function provisionOpenpanelClient(clientName) {
14
+ const cfg = await ensureOpenpanel();
15
+ const { url, organizationSlug, token } = cfg;
16
+ if (!organizationSlug) {
17
+ throw new Error("OpenPanel config is missing organization slug. Re-run `hatchkit config add openpanel`.");
18
+ }
19
+ // Reuse a previously-provisioned client for the same name if one
20
+ // exists — lets you re-run the command without creating duplicates.
21
+ const cachedSecret = await getSecret(SECRET_KEYS.openpanelClientSecret(clientName));
22
+ const cachedIdKey = SECRET_KEYS.openpanelClientSecret(`${clientName}:id`);
23
+ const cachedId = await getSecret(cachedIdKey);
24
+ if (cachedSecret && cachedId) {
25
+ return { projectName: clientName, clientId: cachedId, clientSecret: cachedSecret, apiUrl: url };
26
+ }
27
+ // Try the API first (best-effort). If it fails, prompt the user to
28
+ // paste the values from the OpenPanel dashboard.
29
+ let clientId = null;
30
+ let clientSecret = null;
31
+ try {
32
+ const res = await fetch(`${url}/api/client`, {
33
+ method: "POST",
34
+ headers: {
35
+ Authorization: `Bearer ${token}`,
36
+ "Content-Type": "application/json",
37
+ },
38
+ body: JSON.stringify({
39
+ organizationSlug,
40
+ project: { name: clientName },
41
+ client: { name: clientName, type: "write" },
42
+ }),
43
+ });
44
+ if (res.ok) {
45
+ const body = (await res.json());
46
+ if (body.clientId && body.clientSecret) {
47
+ clientId = body.clientId;
48
+ clientSecret = body.clientSecret;
49
+ }
50
+ }
51
+ }
52
+ catch {
53
+ // fall through to paste flow
54
+ }
55
+ if (!clientId || !clientSecret) {
56
+ console.log(` OpenPanel API couldn't auto-create the client for '${clientName}'.\n` +
57
+ ` Open ${url}/${organizationSlug} → create a project named "${clientName}" →\n` +
58
+ ` add a write client, then paste the credentials below.`);
59
+ clientId = await input({ message: `OpenPanel clientId for ${clientName}:` });
60
+ clientSecret = await passwordPrompt({ message: `OpenPanel clientSecret for ${clientName}:` });
61
+ }
62
+ await setSecret(SECRET_KEYS.openpanelClientSecret(clientName), clientSecret);
63
+ await setSecret(cachedIdKey, clientId);
64
+ return { projectName: clientName, clientId, clientSecret, apiUrl: url };
65
+ }
66
+ //# sourceMappingURL=openpanel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openpanel.js","sourceRoot":"","sources":["../../src/provision/openpanel.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ export interface ResendClient {
2
+ keyName: string;
3
+ apiKey: string;
4
+ domainId?: string;
5
+ /** The full API key is only returned by Resend once; we print it
6
+ * inline and also return it so the caller can persist. */
7
+ raw: string;
8
+ }
9
+ export declare function provisionResendClient(clientName: string, domainId?: string): Promise<ResendClient>;
10
+ /** List verified Resend domains so the caller can pick one. */
11
+ export declare function listResendDomains(): Promise<Array<{
12
+ id: string;
13
+ name: string;
14
+ status: string;
15
+ }>>;
16
+ //# sourceMappingURL=resend.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resend.d.ts","sourceRoot":"","sources":["../../src/provision/resend.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;+DAC2D;IAC3D,GAAG,EAAE,MAAM,CAAC;CACb;AAED,wBAAsB,qBAAqB,CACzC,UAAU,EAAE,MAAM,EAClB,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,YAAY,CAAC,CAsBvB;AAED,+DAA+D;AAC/D,wBAAsB,iBAAiB,IAAI,OAAO,CAChD,KAAK,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CACpD,CAQA"}
@@ -0,0 +1,43 @@
1
+ /*
2
+ * Resend provisioning — creates a restricted API key scoped to a
3
+ * specific verified sending domain. One key per env (dev / prod)
4
+ * keeps blast radius small if either leaks.
5
+ *
6
+ * API: https://resend.com/docs/api-reference/api-keys/create-api-key
7
+ * POST /api-keys { name, permission: "sending_access", domain_id }
8
+ */
9
+ import { ensureResend } from "../config.js";
10
+ export async function provisionResendClient(clientName, domainId) {
11
+ const cfg = await ensureResend();
12
+ const body = {
13
+ name: clientName,
14
+ permission: "sending_access",
15
+ };
16
+ if (domainId)
17
+ body.domain_id = domainId;
18
+ const res = await fetch("https://api.resend.com/api-keys", {
19
+ method: "POST",
20
+ headers: {
21
+ Authorization: `Bearer ${cfg.apiKey}`,
22
+ "Content-Type": "application/json",
23
+ },
24
+ body: JSON.stringify(body),
25
+ });
26
+ if (!res.ok) {
27
+ throw new Error(`Resend create key failed: HTTP ${res.status} ${await res.text()}`);
28
+ }
29
+ const data = (await res.json());
30
+ return { keyName: clientName, apiKey: data.token, domainId, raw: data.token };
31
+ }
32
+ /** List verified Resend domains so the caller can pick one. */
33
+ export async function listResendDomains() {
34
+ const cfg = await ensureResend();
35
+ const res = await fetch("https://api.resend.com/domains", {
36
+ headers: { Authorization: `Bearer ${cfg.apiKey}` },
37
+ });
38
+ if (!res.ok)
39
+ throw new Error(`Resend list domains failed: HTTP ${res.status}`);
40
+ const body = (await res.json());
41
+ return body.data ?? [];
42
+ }
43
+ //# sourceMappingURL=resend.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resend.js","sourceRoot":"","sources":["../../src/provision/resend.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAW5C,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,UAAkB,EAClB,QAAiB;IAEjB,MAAM,GAAG,GAAG,MAAM,YAAY,EAAE,CAAC;IAEjC,MAAM,IAAI,GAA4B;QACpC,IAAI,EAAE,UAAU;QAChB,UAAU,EAAE,gBAAgB;KAC7B,CAAC;IACF,IAAI,QAAQ;QAAE,IAAI,CAAC,SAAS,GAAG,QAAQ,CAAC;IAExC,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,iCAAiC,EAAE;QACzD,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,aAAa,EAAE,UAAU,GAAG,CAAC,MAAM,EAAE;YACrC,cAAc,EAAE,kBAAkB;SACnC;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;KAC3B,CAAC,CAAC;IACH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,kCAAkC,GAAG,CAAC,MAAM,IAAI,MAAM,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IACtF,CAAC;IACD,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAkC,CAAC;IACjE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC;AAChF,CAAC;AAED,+DAA+D;AAC/D,MAAM,CAAC,KAAK,UAAU,iBAAiB;IAGrC,MAAM,GAAG,GAAG,MAAM,YAAY,EAAE,CAAC;IACjC,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,gCAAgC,EAAE;QACxD,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,GAAG,CAAC,MAAM,EAAE,EAAE;KACnD,CAAC,CAAC;IACH,IAAI,CAAC,GAAG,CAAC,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,oCAAoC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;IAC/E,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAkE,CAAC;IACjG,OAAO,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC;AACzB,CAAC"}
@@ -0,0 +1,13 @@
1
+ import type { ProjectConfig } from "../prompts.js";
2
+ import { type ProjectPorts } from "../utils/ports.js";
3
+ import { type DotenvxSeedResult } from "./dotenvx.js";
4
+ export interface ScaffoldResult {
5
+ modifications: string[];
6
+ ports: ProjectPorts;
7
+ /** Populated by seedDotenvxProduction on real (non-dry-run) scaffolds.
8
+ * The private key is also mirrored into the OS keychain. */
9
+ dotenvx?: DotenvxSeedResult;
10
+ }
11
+ /** Scaffold a new app by copying the starter template and customizing it. */
12
+ export declare function scaffoldApp(config: ProjectConfig, outputDir: string): Promise<ScaffoldResult>;
13
+ //# sourceMappingURL=app.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../src/scaffold/app.ts"],"names":[],"mappings":"AA6BA,OAAO,KAAK,EAAa,aAAa,EAAE,MAAM,eAAe,CAAC;AAE9D,OAAO,EAAE,KAAK,YAAY,EAAoB,MAAM,mBAAmB,CAAC;AAExE,OAAO,EAAE,KAAK,iBAAiB,EAAyB,MAAM,cAAc,CAAC;AAqB7E,MAAM,WAAW,cAAc;IAC7B,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,KAAK,EAAE,YAAY,CAAC;IACpB;iEAC6D;IAC7D,OAAO,CAAC,EAAE,iBAAiB,CAAC;CAC7B;AAED,6EAA6E;AAC7E,wBAAsB,WAAW,CAC/B,MAAM,EAAE,aAAa,EACrB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,cAAc,CAAC,CA8FzB"}
@@ -0,0 +1,340 @@
1
+ /*
2
+ * scaffoldApp orchestrator.
3
+ *
4
+ * Responsibilities in order:
5
+ * 1. Validate preconditions (starter submodule present, target empty).
6
+ * 2. Copy the starter template to `outputDir` (fs cpSync with filter).
7
+ * 3. Customize the copy: project name, env files, feature flag strip,
8
+ * bundle IDs, port assignment + propagation, ML playground prune,
9
+ * next.config for static export when native is selected.
10
+ * 4. Roll back (rm output dir + unregister reserved ports) if any
11
+ * step after the copy throws.
12
+ *
13
+ * The actual file-rewriting logic lives in starter-files.ts and
14
+ * pkg-json.ts; this file is the control flow.
15
+ */
16
+ import { cpSync, existsSync, readFileSync, readdirSync, realpathSync, rmSync, writeFileSync, } from "node:fs";
17
+ import { join, resolve } from "node:path";
18
+ import chalk from "chalk";
19
+ import ora from "ora";
20
+ import { addUsedPorts, getUsedPorts, removeUsedPorts } from "../config.js";
21
+ import { explainFsError } from "../utils/errors.js";
22
+ import { pickProjectPorts } from "../utils/ports.js";
23
+ import { getCliVersion } from "../utils/version.js";
24
+ import { seedDotenvxProduction } from "./dotenvx.js";
25
+ import { MANIFEST_FILENAME, toManifest, writeManifest } from "./manifest.js";
26
+ import { stripPackageJsonBuildBlock, stripPackageJsonDeps, stripPackageJsonScripts, unchainTypecheckScript, } from "./pkg-json.js";
27
+ import { applyPorts, flipNextConfigToStaticExport, removeIfExists, replaceInFile, stripMobileBridgeFromLayout, updateEnvExample, } from "./starter-files.js";
28
+ // Monorepo root → starter submodule
29
+ const MONOREPO_ROOT = resolve(join(import.meta.dirname, "..", "..", ".."));
30
+ const STARTER_ROOT = join(MONOREPO_ROOT, "starter");
31
+ /** Scaffold a new app by copying the starter template and customizing it. */
32
+ export async function scaffoldApp(config, outputDir) {
33
+ if (config.dryRun) {
34
+ return {
35
+ modifications: scaffoldDryRun(config, outputDir),
36
+ // Dry-run still picks ports so the summary is accurate, but
37
+ // doesn't persist them.
38
+ ports: await pickProjectPorts(getUsedPorts(), {
39
+ nativeHmr: config.features.includes("desktop") || config.features.includes("mobile"),
40
+ }),
41
+ };
42
+ }
43
+ if (!existsSync(STARTER_ROOT)) {
44
+ throw new Error(`Starter template not found at ${STARTER_ROOT}. Run 'git submodule update --init' in the monorepo root.`);
45
+ }
46
+ // Bail if the target already exists — without this, cpSync silently
47
+ // merges new files into whatever is there, mixing old and new state.
48
+ if (existsSync(outputDir)) {
49
+ const entries = readdirSync(outputDir);
50
+ if (entries.length > 0) {
51
+ // If the target looks like a previously-scaffolded project,
52
+ // nudge the user toward `update` instead of a hard fail.
53
+ const hasManifest = entries.includes(MANIFEST_FILENAME);
54
+ const hint = hasManifest
55
+ ? ` This looks like a previously-scaffolded project (${MANIFEST_FILENAME} is present). Try \`hatchkit update\` from inside it to add features.`
56
+ : "";
57
+ throw new Error(`Output directory ${outputDir} already exists and is not empty. Move or remove it first.${hint}`);
58
+ }
59
+ }
60
+ // Resolve symlinks — if the submodule path is itself a symlink (tests,
61
+ // local dev linking to a sibling checkout), cpSync would otherwise try
62
+ // to recreate the symlink at outputDir and fail with EEXIST.
63
+ const resolvedStarter = realpathSync(STARTER_ROOT);
64
+ // Track claimed resources so a mid-scaffold failure can be rolled
65
+ // back: filesystem + port registrations.
66
+ const reservedPorts = [];
67
+ const rollback = () => {
68
+ if (existsSync(outputDir)) {
69
+ try {
70
+ rmSync(outputDir, { recursive: true, force: true });
71
+ }
72
+ catch {
73
+ /* ignore */
74
+ }
75
+ }
76
+ if (reservedPorts.length > 0) {
77
+ try {
78
+ removeUsedPorts(reservedPorts);
79
+ }
80
+ catch {
81
+ /* ignore */
82
+ }
83
+ }
84
+ };
85
+ const copyStart = Date.now();
86
+ const copySpinner = ora(`Copying starter from ${resolvedStarter}`).start();
87
+ try {
88
+ cpSync(resolvedStarter, outputDir, {
89
+ recursive: true,
90
+ filter: (src) => {
91
+ const rel = src.replace(resolvedStarter, "");
92
+ if (rel === "/.git" || rel.startsWith("/.git/"))
93
+ return false;
94
+ if (rel.includes("/node_modules"))
95
+ return false;
96
+ if (rel.includes("/.next"))
97
+ return false;
98
+ if (rel.includes("/dist/"))
99
+ return false;
100
+ return true;
101
+ },
102
+ });
103
+ copySpinner.succeed(`Starter copied (${elapsed(copyStart)})`);
104
+ }
105
+ catch (err) {
106
+ copySpinner.fail("Starter copy failed");
107
+ rollback();
108
+ throw new Error(explainFsError(err, "Failed to copy starter template"));
109
+ }
110
+ const customizeStart = Date.now();
111
+ const customizeSpinner = ora("Customizing for your project").start();
112
+ try {
113
+ const result = await runScaffoldSteps(config, outputDir, reservedPorts);
114
+ customizeSpinner.succeed(`Scaffolded ${result.modifications.length} modifications (${elapsed(customizeStart)})`);
115
+ return result;
116
+ }
117
+ catch (err) {
118
+ customizeSpinner.fail("Customization failed");
119
+ rollback();
120
+ throw err;
121
+ }
122
+ }
123
+ /** Format ms-since-start as a compact "123ms" or "1.4s". */
124
+ function elapsed(startMs) {
125
+ const ms = Date.now() - startMs;
126
+ return ms < 1000 ? `${ms}ms` : `${(ms / 1000).toFixed(1)}s`;
127
+ }
128
+ /** Customize the starter copy. Mutates `reservedPorts` in place so the
129
+ * outer orchestrator can unregister them on failure. */
130
+ async function runScaffoldSteps(config, outputDir, reservedPorts) {
131
+ const modifications = [];
132
+ // Rename the project in package.json
133
+ replaceInFile(join(outputDir, "package.json"), "node-realtime-starter", config.name);
134
+ modifications.push("package.json (renamed project)");
135
+ // .env.example files: production URLs for this project's domain.
136
+ // .env.development is left alone (local dev defaults should stay pointing at localhost).
137
+ updateEnvExample(outputDir, "packages/server/.env.example", config);
138
+ updateEnvExample(outputDir, "packages/client/.env.example", config);
139
+ modifications.push("updated .env.example files with production URLs");
140
+ // Feature-flag removal
141
+ if (!config.features.includes("websocket")) {
142
+ removeIfExists(join(outputDir, "packages/server/src/ws"));
143
+ modifications.push("removed: ws/ (WebSocket not selected)");
144
+ }
145
+ if (!config.features.includes("stripe")) {
146
+ removeIfExists(join(outputDir, "packages/server/src/services/stripe.ts"));
147
+ modifications.push("removed: stripe service (Stripe not selected)");
148
+ }
149
+ const wantsDesktop = config.features.includes("desktop");
150
+ const wantsMobile = config.features.includes("mobile");
151
+ const bundleId = config.name.replace(/[^a-z0-9]/gi, "").toLowerCase();
152
+ // Port assignment — tested-free via isPortFree + persisted into the
153
+ // CLI registry so subsequent scaffolds can't collide.
154
+ const ports = await pickProjectPorts(getUsedPorts(), {
155
+ nativeHmr: wantsDesktop || wantsMobile,
156
+ });
157
+ const claimed = [ports.server, ports.client, ports.nativeHmr].filter((p) => p !== undefined);
158
+ addUsedPorts(claimed);
159
+ reservedPorts.push(...claimed);
160
+ applyPorts(outputDir, ports, { wantsDesktop, wantsMobile });
161
+ modifications.push(`assigned ports: server=${ports.server} client=${ports.client}` +
162
+ (ports.nativeHmr ? ` native=${ports.nativeHmr}` : ""));
163
+ // Desktop (Electron) strip / substitute
164
+ if (!wantsDesktop) {
165
+ removeIfExists(join(outputDir, "electron"));
166
+ removeIfExists(join(outputDir, ".github/workflows/desktop-release.yml"));
167
+ removeIfExists(join(outputDir, "build"));
168
+ removeIfExists(join(outputDir, "packages/client/src/types/electron.d.ts"));
169
+ stripPackageJsonScripts(outputDir, [
170
+ "dev:desktop",
171
+ "dev:electron",
172
+ "build:desktop",
173
+ "electron:compile",
174
+ "electron:build",
175
+ "electron:preview",
176
+ "typecheck:electron",
177
+ "icons:desktop",
178
+ "itch:push:mac",
179
+ "itch:push:win",
180
+ "itch:push:linux",
181
+ ]);
182
+ unchainTypecheckScript(outputDir);
183
+ stripPackageJsonBuildBlock(outputDir);
184
+ stripPackageJsonDeps(outputDir, [
185
+ "electron",
186
+ "electron-builder",
187
+ "electron-icon-builder",
188
+ "wait-on",
189
+ ]);
190
+ modifications.push("removed: desktop (Electron) scaffolding");
191
+ }
192
+ else {
193
+ replaceInFile(join(outputDir, "package.json"), "{{projectName}}", config.name);
194
+ replaceInFile(join(outputDir, "package.json"), "{{bundleId}}", bundleId);
195
+ }
196
+ // Mobile (Capacitor) strip / substitute
197
+ if (!wantsMobile) {
198
+ removeIfExists(join(outputDir, "ios"));
199
+ removeIfExists(join(outputDir, "android"));
200
+ removeIfExists(join(outputDir, "capacitor.config.ts"));
201
+ removeIfExists(join(outputDir, "packages/client/src/mobile"));
202
+ removeIfExists(join(outputDir, "scripts/android-dev.sh"));
203
+ removeIfExists(join(outputDir, "scripts/android-env.sh"));
204
+ removeIfExists(join(outputDir, "scripts/ios-dev.sh"));
205
+ removeIfExists(join(outputDir, ".github/workflows/mobile-release.yml"));
206
+ removeIfExists(join(outputDir, "resources"));
207
+ stripMobileBridgeFromLayout(outputDir);
208
+ stripPackageJsonScripts(outputDir, [
209
+ "dev:android",
210
+ "dev:ios",
211
+ "build:mobile",
212
+ "cap:add:ios",
213
+ "cap:add:android",
214
+ "cap:sync",
215
+ "cap:run:ios",
216
+ "cap:run:android",
217
+ "build:ios:release",
218
+ "build:android:release",
219
+ "build:android:apk",
220
+ "mobile:assets",
221
+ ]);
222
+ stripPackageJsonDeps(outputDir, [
223
+ "@capacitor/core",
224
+ "@capacitor/cli",
225
+ "@capacitor/ios",
226
+ "@capacitor/android",
227
+ "@capacitor/splash-screen",
228
+ "@capacitor/status-bar",
229
+ "@capacitor/screen-orientation",
230
+ "@capacitor/preferences",
231
+ "@capacitor/app",
232
+ "@capacitor/assets",
233
+ ]);
234
+ modifications.push("removed: mobile (Capacitor) scaffolding");
235
+ }
236
+ else {
237
+ replaceInFile(join(outputDir, "capacitor.config.ts"), "{{projectName}}", config.name);
238
+ replaceInFile(join(outputDir, "capacitor.config.ts"), "{{bundleId}}", bundleId);
239
+ }
240
+ if (wantsDesktop || wantsMobile) {
241
+ flipNextConfigToStaticExport(outputDir);
242
+ modifications.push("next.config.ts: output 'standalone' → 'export'");
243
+ }
244
+ // ML playground prune — remove unselected service pages.
245
+ const allMlServices = [
246
+ "background-removal",
247
+ "subtitles",
248
+ "image-recognition",
249
+ "3d-extraction",
250
+ ];
251
+ for (const service of allMlServices) {
252
+ if (!config.mlServices.includes(service)) {
253
+ removeIfExists(join(outputDir, `packages/client/src/app/(protected)/playground/${service}`));
254
+ modifications.push(`removed: playground/${service} (not selected)`);
255
+ }
256
+ }
257
+ // No ML services at all → remove the entire playground + infrastructure.
258
+ if (config.mlServices.length === 0) {
259
+ removeIfExists(join(outputDir, "packages/client/src/app/(protected)/playground"));
260
+ removeIfExists(join(outputDir, "packages/client/src/components/ml"));
261
+ removeIfExists(join(outputDir, "packages/server/src/trpc/routers/ml.ts"));
262
+ removeIfExists(join(outputDir, "packages/server/src/services/ml.ts"));
263
+ removeIfExists(join(outputDir, "packages/shared/src/ml-types.ts"));
264
+ // Strip ml router from the tRPC router registration.
265
+ const routerPath = join(outputDir, "packages/server/src/trpc/router.ts");
266
+ if (existsSync(routerPath)) {
267
+ let content = readFileSync(routerPath, "utf-8");
268
+ content = content.replace('import { mlRouter } from "./routers/ml.js";\n', "");
269
+ content = content.replace(" ml: mlRouter,\n", "");
270
+ writeFileSync(routerPath, content, "utf-8");
271
+ }
272
+ // Strip ml-types export from shared barrel.
273
+ const sharedIndexPath = join(outputDir, "packages/shared/src/index.ts");
274
+ if (existsSync(sharedIndexPath)) {
275
+ let content = readFileSync(sharedIndexPath, "utf-8");
276
+ content = content.replace('export * from "./ml-types.js";\n', "");
277
+ writeFileSync(sharedIndexPath, content, "utf-8");
278
+ }
279
+ // Strip Playground from protected navbar.
280
+ const layoutPath = join(outputDir, "packages/client/src/app/(protected)/layout.tsx");
281
+ if (existsSync(layoutPath)) {
282
+ let content = readFileSync(layoutPath, "utf-8");
283
+ content = content.replace(/\s*<Link\s+href="\/playground"[^>]*>[^<]*<\/Link>/, "");
284
+ writeFileSync(layoutPath, content, "utf-8");
285
+ }
286
+ modifications.push("removed: ML playground, ML router, ML types, ML navbar link");
287
+ }
288
+ // Write the sanitized manifest so `hatchkit update` can diff
289
+ // against this scaffold's choices later. See manifest.ts for the
290
+ // strict list of fields that are safe to persist.
291
+ writeManifest(outputDir, toManifest(config, ports, getCliVersion()));
292
+ modifications.push(".hatchkit.json (project manifest)");
293
+ // Seed .env.production via dotenvx: encrypt supplied values, mint a
294
+ // keypair, mirror the private key into the OS keychain. Unsupplied
295
+ // keys land as plaintext CHANGE_ME_<KEY> placeholders.
296
+ const dotenvx = await seedDotenvxProduction(outputDir, config, config.envValues ?? {});
297
+ modifications.push(`dotenvx: ${dotenvx.encryptedKeys.length} encrypted, ${dotenvx.placeholderKeys.length} placeholders`);
298
+ return { modifications, ports, dotenvx };
299
+ }
300
+ /** Dry run — list what would happen without touching disk. */
301
+ function scaffoldDryRun(config, outputDir) {
302
+ console.log(chalk.bold("\n [dry-run] Would scaffold from starter template:\n"));
303
+ console.log(chalk.dim(` Source: ${STARTER_ROOT}`));
304
+ console.log(chalk.dim(` Target: ${outputDir}`));
305
+ console.log();
306
+ const actions = [];
307
+ actions.push("Copy starter template");
308
+ actions.push(`Rename project to "${config.name}"`);
309
+ actions.push(`Set domain to "${config.domain}"`);
310
+ if (!config.features.includes("websocket"))
311
+ actions.push("Remove WebSocket support");
312
+ if (!config.features.includes("stripe"))
313
+ actions.push("Remove Stripe integration");
314
+ if (!config.features.includes("desktop"))
315
+ actions.push("Remove desktop (Electron) scaffolding");
316
+ if (!config.features.includes("mobile"))
317
+ actions.push("Remove mobile (Capacitor) scaffolding");
318
+ if (config.features.includes("desktop") || config.features.includes("mobile")) {
319
+ actions.push("Flip next.config.ts to output: 'export' (static)");
320
+ }
321
+ if (config.mlServices.length === 0) {
322
+ actions.push("Remove ML playground, router, types");
323
+ }
324
+ else {
325
+ const removed = [
326
+ "background-removal",
327
+ "subtitles",
328
+ "image-recognition",
329
+ "3d-extraction",
330
+ ].filter((s) => !config.mlServices.includes(s));
331
+ if (removed.length > 0) {
332
+ actions.push(`Remove unused ML pages: ${removed.join(", ")}`);
333
+ }
334
+ }
335
+ for (const action of actions) {
336
+ console.log(chalk.dim(` - ${action}`));
337
+ }
338
+ return actions;
339
+ }
340
+ //# sourceMappingURL=app.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app.js","sourceRoot":"","sources":["../../src/scaffold/app.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ import type { ProjectConfig } from "../prompts.js";
2
+ export interface DotenvxValues {
3
+ /** Values to encrypt into .env.production. User-supplied only —
4
+ * empty string / undefined = "I'll fill this in later" (then we
5
+ * write a plaintext CHANGE_ME_<KEY> placeholder instead). */
6
+ [envKey: string]: string | undefined;
7
+ }
8
+ export interface DotenvxSeedResult {
9
+ /** Full DOTENV_PRIVATE_KEY_PRODUCTION (hex). Stored in keychain. */
10
+ privateKey: string;
11
+ /** Corresponding public key — also embedded at the top of
12
+ * `.env.production` by dotenvx. Safe to log / commit. */
13
+ publicKey: string;
14
+ /** Keys that ended up as plaintext CHANGE_ME (user didn't supply). */
15
+ placeholderKeys: string[];
16
+ /** Keys that got encrypted. */
17
+ encryptedKeys: string[];
18
+ }
19
+ /** Populate the starter's `packages/server/.env.production` with
20
+ * encrypted values + a plaintext CHANGE_ME_<KEY> for anything the
21
+ * user didn't supply. Mirrors the generated private key into the
22
+ * macOS Keychain (or equivalent) and returns it so callers can push
23
+ * it to Coolify / print a retrieval hint. */
24
+ export declare function seedDotenvxProduction(outputDir: string, config: ProjectConfig, userValues: DotenvxValues): Promise<DotenvxSeedResult>;
25
+ /** Prompt the user for the subset of candidate keys that are
26
+ * sensitive + likely already known at scaffold time. Anything they
27
+ * leave blank falls through to CHANGE_ME. Skipped entirely in
28
+ * non-interactive mode — presets should carry the values instead. */
29
+ export declare function printDotenvxSummary(result: DotenvxSeedResult, projectName: string): void;
30
+ //# sourceMappingURL=dotenvx.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dotenvx.d.ts","sourceRoot":"","sources":["../../src/scaffold/dotenvx.ts"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAGnD,MAAM,WAAW,aAAa;IAC5B;;kEAE8D;IAC9D,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;CACtC;AAED,MAAM,WAAW,iBAAiB;IAChC,oEAAoE;IACpE,UAAU,EAAE,MAAM,CAAC;IACnB;8DAC0D;IAC1D,SAAS,EAAE,MAAM,CAAC;IAClB,sEAAsE;IACtE,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,+BAA+B;IAC/B,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB;AAWD;;;;8CAI8C;AAC9C,wBAAsB,qBAAqB,CACzC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,aAAa,EACrB,UAAU,EAAE,aAAa,GACxB,OAAO,CAAC,iBAAiB,CAAC,CAiD5B;AAwDD;;;sEAGsE;AACtE,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,CAmBxF"}