@forgecart/cli 2.202608221935.0 → 2.202609190800.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/dist/src/cli.js +3 -21
  2. package/dist/src/cli.js.map +1 -1
  3. package/dist/src/commands/__test__/cli-harness.d.ts +21 -0
  4. package/dist/src/commands/__test__/cli-harness.js +29 -0
  5. package/dist/src/commands/__test__/cli-harness.js.map +1 -0
  6. package/dist/src/commands/init.d.ts +28 -2
  7. package/dist/src/commands/init.js +100 -15
  8. package/dist/src/commands/init.js.map +1 -1
  9. package/dist/src/commands/refresh.d.ts +40 -0
  10. package/dist/src/commands/refresh.js +147 -0
  11. package/dist/src/commands/refresh.js.map +1 -0
  12. package/dist/src/commands/template-manifest.d.ts +38 -0
  13. package/dist/src/commands/template-manifest.js +116 -0
  14. package/dist/src/commands/template-manifest.js.map +1 -0
  15. package/dist/src/version.d.ts +10 -0
  16. package/dist/src/version.js +25 -0
  17. package/dist/src/version.js.map +1 -0
  18. package/package.json +1 -1
  19. package/templates/storefront/README.md +42 -4
  20. package/templates/storefront/next.config.js +29 -7
  21. package/templates/storefront/src/app/%5F%5Ffc/identify/route.ts +205 -0
  22. package/templates/storefront/src/app/%5F%5Ffc/track/route.ts +28 -25
  23. package/templates/storefront/src/app/__forge_beacon/route.ts +1 -1
  24. package/templates/storefront/src/app/cart/page.tsx +14 -2
  25. package/templates/storefront/src/app/checkout/page.tsx +14 -2
  26. package/templates/storefront/src/app/layout.tsx +85 -22
  27. package/templates/storefront/src/app/page.tsx +63 -20
  28. package/templates/storefront/src/app/pages/[slug]/not-found.tsx +23 -0
  29. package/templates/storefront/src/app/pages/[slug]/page.tsx +114 -0
  30. package/templates/storefront/src/app/ping/route.ts +1 -1
  31. package/templates/storefront/src/app/products/[slug]/not-found.tsx +6 -4
  32. package/templates/storefront/src/app/products/[slug]/page.tsx +204 -21
  33. package/templates/storefront/src/app/products/page.tsx +41 -6
  34. package/templates/storefront/src/app/register/page.tsx +54 -0
  35. package/templates/storefront/src/app/reset-password/page.tsx +60 -0
  36. package/templates/storefront/src/app/robots.ts +69 -0
  37. package/templates/storefront/src/app/sitemap.ts +106 -0
  38. package/templates/storefront/src/app/verify/page.tsx +155 -0
  39. package/templates/storefront/src/components/CartView.tsx +26 -7
  40. package/templates/storefront/src/components/ForgeTracker.tsx +108 -1
  41. package/templates/storefront/src/components/Header.tsx +30 -10
  42. package/templates/storefront/src/components/LanguageSwitcher.tsx +88 -0
  43. package/templates/storefront/src/components/LocaleLink.tsx +49 -0
  44. package/templates/storefront/src/components/ProductCard.tsx +10 -4
  45. package/templates/storefront/src/components/account/AccountMessage.tsx +59 -0
  46. package/templates/storefront/src/components/account/RegisterForm.tsx +283 -0
  47. package/templates/storefront/src/components/account/RequestPasswordResetForm.tsx +96 -0
  48. package/templates/storefront/src/components/account/ResetPasswordForm.tsx +169 -0
  49. package/templates/storefront/src/components/checkout/CheckoutGate.tsx +12 -4
  50. package/templates/storefront/src/lib/account/account-link.ts +76 -0
  51. package/templates/storefront/src/lib/account/register-state.ts +133 -0
  52. package/templates/storefront/src/lib/account/reset-password-state.ts +111 -0
  53. package/templates/storefront/src/lib/account/verify-state.ts +56 -0
  54. package/templates/storefront/src/lib/account-actions.ts +76 -0
  55. package/templates/storefront/src/lib/account-session.ts +47 -0
  56. package/templates/storefront/src/lib/asset-alt.ts +34 -0
  57. package/templates/storefront/src/lib/content/render-fields.tsx +256 -0
  58. package/templates/storefront/src/lib/content/resolve-page.ts +143 -0
  59. package/templates/storefront/src/lib/experiments.ts +1 -1
  60. package/templates/storefront/src/lib/forgecart.ts +300 -27
  61. package/templates/storefront/src/lib/format.ts +12 -14
  62. package/templates/storefront/src/lib/identify-forward.ts +152 -0
  63. package/templates/storefront/src/lib/locale/channel-locales-loader.ts +169 -0
  64. package/templates/storefront/src/lib/locale/channel-locales-map.ts +46 -0
  65. package/templates/storefront/src/lib/locale/channel-locales.ts +191 -0
  66. package/templates/storefront/src/lib/locale/grammar.ts +194 -0
  67. package/templates/storefront/src/lib/locale/localized-path.ts +55 -0
  68. package/templates/storefront/src/lib/locale/middleware-plan.ts +107 -0
  69. package/templates/storefront/src/lib/locale/request-binding.ts +80 -0
  70. package/templates/storefront/src/lib/locale/request-locale.ts +66 -0
  71. package/templates/storefront/src/lib/marketing-params.ts +213 -0
  72. package/templates/storefront/src/lib/money.ts +50 -0
  73. package/templates/storefront/src/lib/seo/alternates.ts +120 -0
  74. package/templates/storefront/src/lib/seo/json-ld.ts +266 -0
  75. package/templates/storefront/src/lib/seo/metadata.ts +323 -0
  76. package/templates/storefront/src/lib/seo/noindex.ts +218 -0
  77. package/templates/storefront/src/lib/seo/public-origin.ts +166 -0
  78. package/templates/storefront/src/lib/seo/redirect-plan.ts +86 -0
  79. package/templates/storefront/src/lib/seo/resolve-path.ts +126 -0
  80. package/templates/storefront/src/lib/seo/scaffolded-routes.ts +83 -0
  81. package/templates/storefront/src/lib/seo/sitemap-cache.ts +114 -0
  82. package/templates/storefront/src/lib/seo/sitemap-entries.ts +321 -0
  83. package/templates/storefront/src/lib/session-actions.ts +15 -8
  84. package/templates/storefront/src/lib/session-cookies.ts +98 -0
  85. package/templates/storefront/src/lib/shop-config.ts +9 -2
  86. package/templates/storefront/src/lib/shop-session.ts +42 -5
  87. package/templates/storefront/src/lib/track-forward.ts +43 -14
  88. package/templates/storefront/src/middleware.ts +196 -16
  89. package/templates/storefront/src/seo/redirects.ts +44 -0
  90. package/templates/storefront/src/server/runner.ts +1 -2
package/dist/src/cli.js CHANGED
@@ -1,28 +1,9 @@
1
- import * as fs from 'node:fs';
2
- import * as path from 'node:path';
3
- import { fileURLToPath } from 'node:url';
4
1
  import { Command } from 'commander';
5
2
  import { createInitCommand } from './commands/init.js';
6
3
  import { createDeleteCommand, createDeployCommand, createEnvCommand, createInitApplicationCommand, createListCommand, createLogsCommand, createScaleCommand, createStatusCommand, createWhoamiCommand, } from './commands/placeholders.js';
4
+ import { createRefreshCommand } from './commands/refresh.js';
7
5
  import { printError } from './output.js';
8
- /**
9
- * Resolve the package manifest from this module's location. The depth differs
10
- * by layout — `src/cli.ts` (dev) sits one level below the package root,
11
- * `dist/src/cli.js` (published) two — and `dist/` never contains a
12
- * `package.json`, so the first existing candidate is always the right one.
13
- */
14
- function readPackageVersion() {
15
- const here = path.dirname(fileURLToPath(import.meta.url));
16
- const candidates = [
17
- path.join(here, '..', 'package.json'),
18
- path.join(here, '..', '..', 'package.json'),
19
- ];
20
- const manifest = candidates.find((candidate) => fs.existsSync(candidate));
21
- if (!manifest) {
22
- throw new Error(`package.json not found near ${here}; cannot resolve the CLI version.`);
23
- }
24
- return JSON.parse(fs.readFileSync(manifest, 'utf-8')).version;
25
- }
6
+ import { readPackageVersion } from './version.js';
26
7
  /**
27
8
  * CLI version, read from `package.json` (the single source of truth) so the
28
9
  * `--version` output can never drift from the published version.
@@ -42,6 +23,7 @@ export function createProgram() {
42
23
  .description('CLI for scaffolding and operating ForgeCart channel storefronts')
43
24
  .version(CLI_VERSION);
44
25
  program.addCommand(createInitCommand());
26
+ program.addCommand(createRefreshCommand());
45
27
  program.addCommand(createInitApplicationCommand());
46
28
  program.addCommand(createDeployCommand());
47
29
  program.addCommand(createListCommand());
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EACL,mBAAmB,EACnB,mBAAmB,EACnB,gBAAgB,EAChB,4BAA4B,EAC5B,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAElD;;;GAGG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,kBAAkB,EAAE,CAAC;AAEhD;;;;;;GAMG;AACH,MAAM,UAAU,aAAa;IAC3B,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;IAE9B,OAAO;SACJ,IAAI,CAAC,WAAW,CAAC;SACjB,WAAW,CAAC,iEAAiE,CAAC;SAC9E,OAAO,CAAC,WAAW,CAAC,CAAC;IAExB,OAAO,CAAC,UAAU,CAAC,iBAAiB,EAAE,CAAC,CAAC;IACxC,OAAO,CAAC,UAAU,CAAC,oBAAoB,EAAE,CAAC,CAAC;IAC3C,OAAO,CAAC,UAAU,CAAC,4BAA4B,EAAE,CAAC,CAAC;IACnD,OAAO,CAAC,UAAU,CAAC,mBAAmB,EAAE,CAAC,CAAC;IAC1C,OAAO,CAAC,UAAU,CAAC,iBAAiB,EAAE,CAAC,CAAC;IACxC,OAAO,CAAC,UAAU,CAAC,mBAAmB,EAAE,CAAC,CAAC;IAC1C,OAAO,CAAC,UAAU,CAAC,iBAAiB,EAAE,CAAC,CAAC;IACxC,OAAO,CAAC,UAAU,CAAC,mBAAmB,EAAE,CAAC,CAAC;IAC1C,OAAO,CAAC,UAAU,CAAC,kBAAkB,EAAE,CAAC,CAAC;IACzC,OAAO,CAAC,UAAU,CAAC,gBAAgB,EAAE,CAAC,CAAC;IACvC,OAAO,CAAC,UAAU,CAAC,mBAAmB,EAAE,CAAC,CAAC;IAE1C,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,GAAG,CAAC,OAAiB,OAAO,CAAC,IAAI;IACrD,MAAM,OAAO,GAAG,aAAa,EAAE,CAAC;IAChC,IAAI,CAAC;QACH,MAAM,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IACjC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACvE,UAAU,CAAC,UAAU,OAAO,EAAE,CAAC,CAAC;QAChC,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;IACvB,CAAC;AACH,CAAC"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Shared black-box harness for the CLI command specs.
3
+ *
4
+ * We run the BUILT CLI (the artifact that ships to npm) against a temp dir and
5
+ * assert on its outputs, rather than importing the source: the package is ESM
6
+ * (`import.meta.url`, `.js` import specifiers) and the repo's jest transform is
7
+ * CJS, so importing the source would break. Spawning the real binary also
8
+ * verifies what users actually get. The `test` target dependsOn `build`, so
9
+ * `dist/bin/forgecart.js` exists when these run.
10
+ */
11
+ export declare const CLI: string;
12
+ /** The bundled storefront template the built CLI scaffolds from. */
13
+ export declare const TEMPLATE_SRC: string;
14
+ export interface CliRun {
15
+ status: number | null;
16
+ /** Combined stdout+stderr, for message assertions. */
17
+ out: string;
18
+ /** stdout alone, for `--json` report parsing. */
19
+ stdout: string;
20
+ }
21
+ export declare function runCli(args: string[], cwd: string, envOverrides?: Record<string, string>): CliRun;
@@ -0,0 +1,29 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { resolve } from 'node:path';
3
+ /**
4
+ * Shared black-box harness for the CLI command specs.
5
+ *
6
+ * We run the BUILT CLI (the artifact that ships to npm) against a temp dir and
7
+ * assert on its outputs, rather than importing the source: the package is ESM
8
+ * (`import.meta.url`, `.js` import specifiers) and the repo's jest transform is
9
+ * CJS, so importing the source would break. Spawning the real binary also
10
+ * verifies what users actually get. The `test` target dependsOn `build`, so
11
+ * `dist/bin/forgecart.js` exists when these run.
12
+ */
13
+ export const CLI = resolve(__dirname, '..', '..', '..', 'dist', 'bin', 'forgecart.js');
14
+ /** The bundled storefront template the built CLI scaffolds from. */
15
+ export const TEMPLATE_SRC = resolve(__dirname, '..', '..', '..', 'templates', 'storefront');
16
+ export function runCli(args, cwd, envOverrides = {}) {
17
+ // Hermetic by default: a WORKSPACE_TEMPLATE_DIR exported in the developer's
18
+ // shell must not flip the cold-path tests into the warmed path.
19
+ const env = { ...process.env };
20
+ delete env.WORKSPACE_TEMPLATE_DIR;
21
+ Object.assign(env, envOverrides);
22
+ const result = spawnSync(process.execPath, [CLI, ...args], { cwd, encoding: 'utf8', env });
23
+ return {
24
+ status: result.status,
25
+ out: `${result.stdout ?? ''}${result.stderr ?? ''}`,
26
+ stdout: result.stdout ?? '',
27
+ };
28
+ }
29
+ //# sourceMappingURL=cli-harness.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-harness.js","sourceRoot":"","sources":["../../../../src/commands/__test__/cli-harness.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,GAAG,GAAG,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,CAAC,CAAC;AAEvF,oEAAoE;AACpE,MAAM,CAAC,MAAM,YAAY,GAAG,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,YAAY,CAAC,CAAC;AAU5F,MAAM,UAAU,MAAM,CACpB,IAAc,EACd,GAAW,EACX,eAAuC,EAAE;IAEzC,4EAA4E;IAC5E,gEAAgE;IAChE,MAAM,GAAG,GAAuC,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IACnE,OAAO,GAAG,CAAC,sBAAsB,CAAC;IAClC,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;IAEjC,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;IAC3F,OAAO;QACL,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,GAAG,EAAE,GAAG,MAAM,CAAC,MAAM,IAAI,EAAE,GAAG,MAAM,CAAC,MAAM,IAAI,EAAE,EAAE;QACnD,MAAM,EAAE,MAAM,CAAC,MAAM,IAAI,EAAE;KAC5B,CAAC;AACJ,CAAC"}
@@ -1,6 +1,14 @@
1
1
  import { Command } from 'commander';
2
- /** Default shop API base. Overridable via `--api-url`. */
3
- export declare const DEFAULT_SHOP_API_URL = "https://api.forgecart.com";
2
+ /**
3
+ * Default shop API base, INCLUDING the mounted path. The API serves the shop
4
+ * GraphQL endpoint at `/shop-api` (`app/forgecart/src/main.ts`), and the SDK
5
+ * takes this value verbatim as its endpoint — it swaps the scheme for the
6
+ * websocket and appends nothing to the path — so a pathless origin scaffolds a
7
+ * storefront whose every read dials a socket nothing answers (#2102): the
8
+ * operation rides graphql-ws's whole retry ladder and the page hangs with
9
+ * nothing logged. Overridable via `--api-url`.
10
+ */
11
+ export declare const DEFAULT_SHOP_API_URL = "https://api.forgecart.com/shop-api";
4
12
  /**
5
13
  * Environment variable naming a dependency-warmed storefront template — a copy
6
14
  * of the bundled template with `node_modules` (and the generated
@@ -40,6 +48,17 @@ export interface InitOptions {
40
48
  * sides (deployed storefronts, loopback dev).
41
49
  */
42
50
  browserApiUrl?: string;
51
+ /**
52
+ * The storefront's PUBLIC origin (`https://shop.example.com`) — the address
53
+ * shoppers and crawlers reach it at.
54
+ *
55
+ * Supplied for a published storefront and deliberately OMITTED for preview
56
+ * pods, whose hostnames are ephemeral. The template treats absence as "this
57
+ * deployment has no stable address to be canonical about" and serves every
58
+ * route noindex with `Disallow: /` (`lib/seo/public-origin.ts`), so omitting
59
+ * it is the safe state rather than an incomplete one.
60
+ */
61
+ publicOrigin?: string;
43
62
  /** Whether to also scaffold a NestJS application (planned for a later phase). */
44
63
  application?: boolean;
45
64
  /**
@@ -51,6 +70,13 @@ export interface InitOptions {
51
70
  */
52
71
  configOnly?: boolean;
53
72
  }
73
+ /**
74
+ * Absolute path to the bundled storefront scaffold.
75
+ *
76
+ * At runtime this file lives at `<pkg>/dist/src/commands/init.js`, so the
77
+ * `templates/` directory shipped alongside `dist/` is three levels up.
78
+ */
79
+ export declare function getStorefrontTemplateDir(): string;
54
80
  /**
55
81
  * Run `forgecart init`.
56
82
  *
@@ -3,8 +3,18 @@ import { dirname, join, resolve } from 'node:path';
3
3
  import { fileURLToPath } from 'node:url';
4
4
  import { Command } from 'commander';
5
5
  import { print } from '../output.js';
6
- /** Default shop API base. Overridable via `--api-url`. */
7
- export const DEFAULT_SHOP_API_URL = 'https://api.forgecart.com';
6
+ import { readPackageVersion } from '../version.js';
7
+ import { writeTemplateManifest } from './template-manifest.js';
8
+ /**
9
+ * Default shop API base, INCLUDING the mounted path. The API serves the shop
10
+ * GraphQL endpoint at `/shop-api` (`app/forgecart/src/main.ts`), and the SDK
11
+ * takes this value verbatim as its endpoint — it swaps the scheme for the
12
+ * websocket and appends nothing to the path — so a pathless origin scaffolds a
13
+ * storefront whose every read dials a socket nothing answers (#2102): the
14
+ * operation rides graphql-ws's whole retry ladder and the page hangs with
15
+ * nothing logged. Overridable via `--api-url`.
16
+ */
17
+ export const DEFAULT_SHOP_API_URL = 'https://api.forgecart.com/shop-api';
8
18
  /**
9
19
  * Environment variable naming a dependency-warmed storefront template — a copy
10
20
  * of the bundled template with `node_modules` (and the generated
@@ -28,7 +38,7 @@ export const DEPS_READY_MARKER_PATH = '.forgecart/deps-ready.json';
28
38
  * At runtime this file lives at `<pkg>/dist/src/commands/init.js`, so the
29
39
  * `templates/` directory shipped alongside `dist/` is three levels up.
30
40
  */
31
- function getStorefrontTemplateDir() {
41
+ export function getStorefrontTemplateDir() {
32
42
  const here = dirname(fileURLToPath(import.meta.url));
33
43
  return resolve(here, '..', '..', '..', 'templates', 'storefront');
34
44
  }
@@ -123,35 +133,106 @@ function buildConfig(channelToken, options) {
123
133
  storefrontPath: '.',
124
134
  };
125
135
  }
126
- /** Build the `.env` contents for the storefront SDK clients. */
127
- function buildEnv(channelToken, options) {
128
- const lines = [
129
- `FORGECART_CHANNEL_TOKEN=${channelToken}`,
130
- `FORGECART_SHOP_API_URL=${options.apiUrl}`,
131
- ];
136
+ /**
137
+ * The platform-owned `.env` keys this invocation manages, in canonical render
138
+ * order. Conditional keys (browser URL, admin secret) enter the map only when
139
+ * their flag is present — an existing line for an unprovided key is
140
+ * workspace-owned for THIS run and survives untouched (#1123: the prewarmed
141
+ * config-only path re-runs init on every provisioning drive, and a drive that
142
+ * omits `--admin-secret` must not strip a secret an earlier drive wrote).
143
+ */
144
+ function buildPlatformEnv(channelToken, options) {
145
+ const entries = new Map();
146
+ entries.set('FORGECART_CHANNEL_TOKEN', channelToken);
147
+ entries.set('FORGECART_SHOP_API_URL', options.apiUrl);
132
148
  const browserApiUrl = options.browserApiUrl?.trim();
133
149
  if (browserApiUrl) {
134
- lines.push(`FORGECART_SHOP_API_BROWSER_URL=${browserApiUrl}`);
150
+ entries.set('FORGECART_SHOP_API_BROWSER_URL', browserApiUrl);
135
151
  }
136
152
  const secret = options.adminSecret?.trim();
137
153
  if (secret) {
138
- lines.push(`FORGECART_ADMIN_SECRET=${secret}`);
154
+ entries.set('FORGECART_ADMIN_SECRET', secret);
155
+ }
156
+ // Conditional for the same #1123 reason as the two above, and with a sharper
157
+ // consequence: a preview drive omits it, and stripping an origin an earlier
158
+ // drive wrote would silently de-index a live storefront.
159
+ const publicOrigin = options.publicOrigin?.trim();
160
+ if (publicOrigin) {
161
+ entries.set('FORGECART_PUBLIC_ORIGIN', publicOrigin);
162
+ }
163
+ return entries;
164
+ }
165
+ /**
166
+ * Merge the platform-owned keys into an existing `.env`, preserving every
167
+ * other line — merchant keys, comments, ordering — verbatim (#1123: a
168
+ * wholesale rewrite wiped the demo store's DEMO_* config on three
169
+ * provisioning drives). Platform keys are refreshed in place at their
170
+ * existing position; duplicates collapse onto the first occurrence; managed
171
+ * keys with no existing line append in canonical order. Trailing blank lines
172
+ * normalize to the single final newline. An empty `existing` renders the
173
+ * canonical fresh `.env`, byte-identical to the pre-merge scaffold output.
174
+ */
175
+ function mergeEnv(existing, channelToken, options) {
176
+ const platform = buildPlatformEnv(channelToken, options);
177
+ const refreshed = new Set();
178
+ const merged = [];
179
+ for (const line of existing.split('\n')) {
180
+ const out = refreshPlatformLine(line, platform, refreshed);
181
+ if (out !== null) {
182
+ merged.push(out);
183
+ }
184
+ }
185
+ while (merged.length > 0 && merged[merged.length - 1] === '') {
186
+ merged.pop();
187
+ }
188
+ for (const [key, value] of platform) {
189
+ if (!refreshed.has(key)) {
190
+ merged.push(`${key}=${value}`);
191
+ }
192
+ }
193
+ merged.push('');
194
+ return merged.join('\n');
195
+ }
196
+ /**
197
+ * Map one existing `.env` line through the merge: workspace-owned lines pass
198
+ * through verbatim, a managed platform key refreshes in place on first sight,
199
+ * and its later duplicates drop (null).
200
+ */
201
+ function refreshPlatformLine(line, platform, refreshed) {
202
+ const key = /^([A-Z][A-Z0-9_]*)=/.exec(line)?.[1];
203
+ if (!key || !platform.has(key))
204
+ return line;
205
+ if (refreshed.has(key))
206
+ return null;
207
+ refreshed.add(key);
208
+ return `${key}=${platform.get(key) ?? ''}`;
209
+ }
210
+ /** Read a file that may legitimately not exist yet; any other failure propagates. */
211
+ async function readFileIfPresent(path) {
212
+ try {
213
+ return await readFile(path, 'utf8');
214
+ }
215
+ catch (error) {
216
+ if (error.code === 'ENOENT')
217
+ return '';
218
+ throw error;
139
219
  }
140
- lines.push('');
141
- return lines.join('\n');
142
220
  }
143
221
  /**
144
222
  * Write the per-channel config the storefront reads at runtime: `.forgecart/
145
223
  * config.json` (channel token, api url, app flag) and `.env` (the SDK
146
224
  * client env). Shared by the full scaffold and the `--config-only` serve-in-place
147
225
  * path, so both write byte-identical config — the only difference is whether the
148
- * template tree was copied first.
226
+ * template tree was copied first. `config.json` is pure platform state and is
227
+ * rewritten wholesale; `.env` is merge-preserved (#1123 — see mergeEnv).
149
228
  */
150
229
  async function writeChannelConfig(targetDir, channelToken, options) {
151
230
  const configDir = join(targetDir, '.forgecart');
152
231
  await mkdir(configDir, { recursive: true });
153
232
  await writeFile(join(configDir, 'config.json'), `${JSON.stringify(buildConfig(channelToken, options), null, 2)}\n`, 'utf8');
154
- await writeFile(join(targetDir, '.env'), buildEnv(channelToken, options), 'utf8');
233
+ const envPath = join(targetDir, '.env');
234
+ const existingEnv = await readFileIfPresent(envPath);
235
+ await writeFile(envPath, mergeEnv(existingEnv, channelToken, options), 'utf8');
155
236
  }
156
237
  /** Print the post-scaffold next steps. */
157
238
  function printNextSteps(targetDir, application, depsInstalled) {
@@ -224,6 +305,9 @@ export async function runInit(name, options) {
224
305
  }
225
306
  await writeChannelConfig(targetDir, channelToken, options);
226
307
  await writeDepsReadyMarker(targetDir, plan);
308
+ // Inventory the render for `forgecart refresh` (#1116) — hashed from the
309
+ // plan's template dir, the exact bytes the copy above landed.
310
+ await writeTemplateManifest(targetDir, plan.templateDir, readPackageVersion());
227
311
  printNextSteps(name ?? '.', options.application ?? false, plan.source === 'warmed');
228
312
  }
229
313
  /** Build the `init [name]` command. */
@@ -235,6 +319,7 @@ export function createInitCommand() {
235
319
  .option('--admin-secret <secret>', 'Channel admin secret for server-side admin operations (written to .env only)')
236
320
  .option('--api-url <url>', 'Shop API base URL written to the storefront environment', DEFAULT_SHOP_API_URL)
237
321
  .option('--browser-api-url <url>', 'Shop API URL for the shopper browser when it differs from --api-url (split-horizon pods; must be HTTPS so the session socket can use WSS)')
322
+ .option('--public-origin <url>', 'Public origin the storefront is served at (https://shop.example.com). Omit for preview deployments: without it every route is noindex and robots.txt disallows the whole tree')
238
323
  .option('-a, --application', 'Also scaffold a NestJS application (TODO: next phase)')
239
324
  .option('--config-only', 'Write only per-channel config into an existing pre-warmed storefront (skip the template copy)')
240
325
  .action(async (name, options) => {
@@ -1 +1 @@
1
- {"version":3,"file":"init.js","sourceRoot":"","sources":["../../../src/commands/init.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../../../src/commands/init.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ import { Command } from 'commander';
2
+ /**
3
+ * `forgecart refresh` (#1116) — re-render the platform-owned template files
4
+ * of an EXISTING workspace from the CLI's bundled template, under the
5
+ * file-ownership partition:
6
+ *
7
+ * - a platform file whose content still matches its manifest hash (pristine
8
+ * since generation) re-renders to the current template;
9
+ * - a platform file that DIVERGED from its manifest hash was edited in the
10
+ * workspace — conflict: skipped and flagged, never clobbered;
11
+ * - a file the template does not ship is workspace-owned and never touched;
12
+ * - `.env` and `.forgecart/` are outside refresh's jurisdiction entirely
13
+ * (channel config rides `init`'s own write paths; `.env` rides the #1123
14
+ * preserve-and-merge).
15
+ *
16
+ * Workspaces generated before the manifest existed cannot prove pristineness,
17
+ * so every divergent file refuses by default; `--adopt <relpath>` takes the
18
+ * template version for explicitly reviewed paths. The refresh always (re)writes
19
+ * the manifest, so a pre-manifest workspace becomes precise after its first
20
+ * refresh. The JSON report (`--json`) is the machine seam the provisioning
21
+ * orchestrator parses.
22
+ */
23
+ interface RefreshOptions {
24
+ json?: boolean;
25
+ adopt?: string[];
26
+ }
27
+ /** Per-file verdicts, in report shape. */
28
+ export interface RefreshReport {
29
+ rendered: string[];
30
+ conflicts: string[];
31
+ adopted: string[];
32
+ upToDate: string[];
33
+ /** True when package.json was re-rendered — the driver must re-install. */
34
+ dependenciesChanged: boolean;
35
+ }
36
+ /** Run `forgecart refresh`. */
37
+ export declare function runRefresh(name: string | undefined, options: RefreshOptions): Promise<void>;
38
+ /** Build the `refresh [name]` command. */
39
+ export declare function createRefreshCommand(): Command;
40
+ export {};
@@ -0,0 +1,147 @@
1
+ import { mkdir, readFile, stat, writeFile } from 'node:fs/promises';
2
+ import { dirname, join, resolve } from 'node:path';
3
+ import { Command } from 'commander';
4
+ import { print } from '../output.js';
5
+ import { readPackageVersion } from '../version.js';
6
+ import { getStorefrontTemplateDir } from './init.js';
7
+ import { TEMPLATE_MANIFEST_RELPATH, TEMPLATE_MANIFEST_SCHEMA_VERSION, hashContent, listTemplateFiles, readTemplateManifest, } from './template-manifest.js';
8
+ /** True if the path exists and is a regular file. */
9
+ async function isFile(path) {
10
+ try {
11
+ return (await stat(path)).isFile();
12
+ }
13
+ catch {
14
+ return false;
15
+ }
16
+ }
17
+ /** Read file bytes; null when absent (any other failure propagates). */
18
+ async function readBytesIfPresent(path) {
19
+ try {
20
+ return await readFile(path);
21
+ }
22
+ catch (error) {
23
+ if (error.code === 'ENOENT')
24
+ return null;
25
+ throw error;
26
+ }
27
+ }
28
+ /**
29
+ * Decide one file's fate. `recordedHash` is the manifest's inventory entry
30
+ * (undefined on pre-manifest workspaces and template-added files).
31
+ */
32
+ function decideFile(workspaceHash, templateHash, recordedHash, adopt) {
33
+ if (workspaceHash === null)
34
+ return 'rendered';
35
+ if (workspaceHash === templateHash)
36
+ return 'upToDate';
37
+ if (adopt)
38
+ return 'adopted';
39
+ if (recordedHash !== undefined && workspaceHash === recordedHash)
40
+ return 'rendered';
41
+ return 'conflict';
42
+ }
43
+ /**
44
+ * Judge one template file against the workspace and apply the write when the
45
+ * verdict says so. Returns the verdict plus the template hash the manifest
46
+ * update needs.
47
+ */
48
+ async function refreshTemplateFile(targetDir, templateDir, relpath, recordedHash, adopt) {
49
+ const templateBytes = await readFile(join(templateDir, ...relpath.split('/')));
50
+ const templateHash = hashContent(templateBytes);
51
+ const workspacePath = join(targetDir, ...relpath.split('/'));
52
+ const workspaceBytes = await readBytesIfPresent(workspacePath);
53
+ const verdict = decideFile(workspaceBytes === null ? null : hashContent(workspaceBytes), templateHash, recordedHash, adopt);
54
+ if (verdict === 'rendered' || verdict === 'adopted') {
55
+ await mkdir(dirname(workspacePath), { recursive: true });
56
+ await writeFile(workspacePath, templateBytes);
57
+ }
58
+ return { verdict, templateHash };
59
+ }
60
+ /**
61
+ * Fold one file's verdict into the report and the next manifest: bucket the
62
+ * relpath, flip the install flag when package.json was (re)written, and
63
+ * record the inventory entry.
64
+ */
65
+ function recordVerdict(report, nextFiles, relpath, verdict, templateHash, recordedHash) {
66
+ if (relpath === 'package.json' && (verdict === 'rendered' || verdict === 'adopted')) {
67
+ report.dependenciesChanged = true;
68
+ }
69
+ report[verdict === 'conflict' ? 'conflicts' : verdict].push(relpath);
70
+ const nextHash = nextManifestHash(verdict, templateHash, recordedHash);
71
+ if (nextHash !== undefined) {
72
+ nextFiles[relpath] = nextHash;
73
+ }
74
+ }
75
+ /**
76
+ * The next manifest entry for a file: written/current files record the
77
+ * template hash; a conflict keeps its previous inventory (or stays
78
+ * uninventoried), so it keeps conflicting until adopted.
79
+ */
80
+ function nextManifestHash(verdict, templateHash, recordedHash) {
81
+ if (verdict === 'conflict')
82
+ return recordedHash;
83
+ return templateHash;
84
+ }
85
+ /** Run `forgecart refresh`. */
86
+ export async function runRefresh(name, options) {
87
+ const targetDir = name ? resolve(process.cwd(), name) : process.cwd();
88
+ if (!(await isFile(join(targetDir, 'package.json')))) {
89
+ throw new Error(`refresh requires an existing storefront at ${targetDir} (no package.json found).`);
90
+ }
91
+ const templateDir = getStorefrontTemplateDir();
92
+ const manifest = await readTemplateManifest(targetDir);
93
+ const adoptSet = new Set(options.adopt ?? []);
94
+ const report = {
95
+ rendered: [],
96
+ conflicts: [],
97
+ adopted: [],
98
+ upToDate: [],
99
+ dependenciesChanged: false,
100
+ };
101
+ const nextFiles = {};
102
+ for (const relpath of await listTemplateFiles(templateDir)) {
103
+ const recordedHash = manifest?.files[relpath];
104
+ const { verdict, templateHash } = await refreshTemplateFile(targetDir, templateDir, relpath, recordedHash, adoptSet.has(relpath));
105
+ recordVerdict(report, nextFiles, relpath, verdict, templateHash, recordedHash);
106
+ }
107
+ const nextManifest = {
108
+ schemaVersion: TEMPLATE_MANIFEST_SCHEMA_VERSION,
109
+ templateVersion: readPackageVersion(),
110
+ generatedAt: new Date().toISOString(),
111
+ files: nextFiles,
112
+ };
113
+ await mkdir(join(targetDir, '.forgecart'), { recursive: true });
114
+ await writeFile(join(targetDir, ...TEMPLATE_MANIFEST_RELPATH.split('/')), `${JSON.stringify(nextManifest, null, 2)}\n`, 'utf8');
115
+ if (options.json) {
116
+ print(JSON.stringify(report));
117
+ return;
118
+ }
119
+ printSummary(report);
120
+ }
121
+ function printSummary(report) {
122
+ print(`Refreshed ${String(report.rendered.length)} platform file(s).`);
123
+ if (report.adopted.length > 0) {
124
+ print(`Adopted ${String(report.adopted.length)} file(s) onto the template version.`);
125
+ }
126
+ if (report.conflicts.length > 0) {
127
+ print('Conflicts (workspace-edited platform files, left untouched):');
128
+ for (const relpath of report.conflicts) {
129
+ print(` ${relpath} (re-run with --adopt ${relpath} to take the template version)`);
130
+ }
131
+ }
132
+ if (report.dependenciesChanged) {
133
+ print('package.json changed — run your package manager install before the next dev boot.');
134
+ }
135
+ }
136
+ /** Build the `refresh [name]` command. */
137
+ export function createRefreshCommand() {
138
+ return new Command('refresh')
139
+ .description('Re-render platform-owned template files onto an existing storefront (workspace-owned files survive; edited platform files flag as conflicts)')
140
+ .argument('[name]', 'Target directory name (defaults to the current directory)')
141
+ .option('--adopt <relpath...>', 'Take the template version of specific flagged files (reviewed adoption)')
142
+ .option('--json', 'Print a machine-readable JSON report instead of the human summary')
143
+ .action(async (name, options) => {
144
+ await runRefresh(name, options);
145
+ });
146
+ }
147
+ //# sourceMappingURL=refresh.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refresh.js","sourceRoot":"","sources":["../../../src/commands/refresh.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACpE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEnD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AACrC,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAEnD,OAAO,EAAE,wBAAwB,EAAE,MAAM,WAAW,CAAC;AACrD,OAAO,EACL,yBAAyB,EACzB,gCAAgC,EAChC,WAAW,EACX,iBAAiB,EACjB,oBAAoB,GAErB,MAAM,wBAAwB,CAAC;AAwChC,qDAAqD;AACrD,KAAK,UAAU,MAAM,CAAC,IAAY;IAChC,IAAI,CAAC;QACH,OAAO,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IACrC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,wEAAwE;AACxE,KAAK,UAAU,kBAAkB,CAAC,IAAY;IAC5C,IAAI,CAAC;QACH,OAAO,MAAM,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC9B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC;QACpE,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,SAAS,UAAU,CACjB,aAA4B,EAC5B,YAAoB,EACpB,YAAgC,EAChC,KAAc;IAEd,IAAI,aAAa,KAAK,IAAI;QAAE,OAAO,UAAU,CAAC;IAC9C,IAAI,aAAa,KAAK,YAAY;QAAE,OAAO,UAAU,CAAC;IACtD,IAAI,KAAK;QAAE,OAAO,SAAS,CAAC;IAC5B,IAAI,YAAY,KAAK,SAAS,IAAI,aAAa,KAAK,YAAY;QAAE,OAAO,UAAU,CAAC;IACpF,OAAO,UAAU,CAAC;AACpB,CAAC;AAED;;;;GAIG;AACH,KAAK,UAAU,mBAAmB,CAChC,SAAiB,EACjB,WAAmB,EACnB,OAAe,EACf,YAAgC,EAChC,KAAc;IAEd,MAAM,aAAa,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IAC/E,MAAM,YAAY,GAAG,WAAW,CAAC,aAAa,CAAC,CAAC;IAChD,MAAM,aAAa,GAAG,IAAI,CAAC,SAAS,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;IAC7D,MAAM,cAAc,GAAG,MAAM,kBAAkB,CAAC,aAAa,CAAC,CAAC;IAE/D,MAAM,OAAO,GAAG,UAAU,CACxB,cAAc,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC,cAAc,CAAC,EAC5D,YAAY,EACZ,YAAY,EACZ,KAAK,CACN,CAAC;IACF,IAAI,OAAO,KAAK,UAAU,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QACpD,MAAM,KAAK,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACzD,MAAM,SAAS,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC;IAChD,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC;AACnC,CAAC;AAED;;;;GAIG;AACH,SAAS,aAAa,CACpB,MAAqB,EACrB,SAAiC,EACjC,OAAe,EACf,OAAoB,EACpB,YAAoB,EACpB,YAAgC;IAEhC,IAAI,OAAO,KAAK,cAAc,IAAI,CAAC,OAAO,KAAK,UAAU,IAAI,OAAO,KAAK,SAAS,CAAC,EAAE,CAAC;QACpF,MAAM,CAAC,mBAAmB,GAAG,IAAI,CAAC;IACpC,CAAC;IACD,MAAM,CAAC,OAAO,KAAK,UAAU,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAErE,MAAM,QAAQ,GAAG,gBAAgB,CAAC,OAAO,EAAE,YAAY,EAAE,YAAY,CAAC,CAAC;IACvE,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC3B,SAAS,CAAC,OAAO,CAAC,GAAG,QAAQ,CAAC;IAChC,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,SAAS,gBAAgB,CACvB,OAAoB,EACpB,YAAoB,EACpB,YAAgC;IAEhC,IAAI,OAAO,KAAK,UAAU;QAAE,OAAO,YAAY,CAAC;IAChD,OAAO,YAAY,CAAC;AACtB,CAAC;AAED,+BAA+B;AAC/B,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAwB,EAAE,OAAuB;IAChF,MAAM,SAAS,GAAG,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;IACtE,IAAI,CAAC,CAAC,MAAM,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC,CAAC,EAAE,CAAC;QACrD,MAAM,IAAI,KAAK,CACb,8CAA8C,SAAS,2BAA2B,CACnF,CAAC;IACJ,CAAC;IAED,MAAM,WAAW,GAAG,wBAAwB,EAAE,CAAC;IAC/C,MAAM,QAAQ,GAAG,MAAM,oBAAoB,CAAC,SAAS,CAAC,CAAC;IACvD,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;IAE9C,MAAM,MAAM,GAAkB;QAC5B,QAAQ,EAAE,EAAE;QACZ,SAAS,EAAE,EAAE;QACb,OAAO,EAAE,EAAE;QACX,QAAQ,EAAE,EAAE;QACZ,mBAAmB,EAAE,KAAK;KAC3B,CAAC;IACF,MAAM,SAAS,GAA2B,EAAE,CAAC;IAE7C,KAAK,MAAM,OAAO,IAAI,MAAM,iBAAiB,CAAC,WAAW,CAAC,EAAE,CAAC;QAC3D,MAAM,YAAY,GAAG,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;QAC9C,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,GAAG,MAAM,mBAAmB,CACzD,SAAS,EACT,WAAW,EACX,OAAO,EACP,YAAY,EACZ,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CACtB,CAAC;QACF,aAAa,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,YAAY,CAAC,CAAC;IACjF,CAAC;IAED,MAAM,YAAY,GAAqB;QACrC,aAAa,EAAE,gCAAgC;QAC/C,eAAe,EAAE,kBAAkB,EAAE;QACrC,WAAW,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACrC,KAAK,EAAE,SAAS;KACjB,CAAC;IACF,MAAM,KAAK,CAAC,IAAI,CAAC,SAAS,EAAE,YAAY,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAChE,MAAM,SAAS,CACb,IAAI,CAAC,SAAS,EAAE,GAAG,yBAAyB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,EACxD,GAAG,IAAI,CAAC,SAAS,CAAC,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAC5C,MAAM,CACP,CAAC;IAEF,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;QACjB,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;QAC9B,OAAO;IACT,CAAC;IACD,YAAY,CAAC,MAAM,CAAC,CAAC;AACvB,CAAC;AAED,SAAS,YAAY,CAAC,MAAqB;IACzC,KAAK,CAAC,aAAa,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;IACvE,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC9B,KAAK,CAAC,WAAW,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,qCAAqC,CAAC,CAAC;IACvF,CAAC;IACD,IAAI,MAAM,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAChC,KAAK,CAAC,8DAA8D,CAAC,CAAC;QACtE,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;YACvC,KAAK,CAAC,KAAK,OAAO,0BAA0B,OAAO,gCAAgC,CAAC,CAAC;QACvF,CAAC;IACH,CAAC;IACD,IAAI,MAAM,CAAC,mBAAmB,EAAE,CAAC;QAC/B,KAAK,CAAC,mFAAmF,CAAC,CAAC;IAC7F,CAAC;AACH,CAAC;AAED,0CAA0C;AAC1C,MAAM,UAAU,oBAAoB;IAClC,OAAO,IAAI,OAAO,CAAC,SAAS,CAAC;SAC1B,WAAW,CACV,8IAA8I,CAC/I;SACA,QAAQ,CAAC,QAAQ,EAAE,2DAA2D,CAAC;SAC/E,MAAM,CACL,sBAAsB,EACtB,yEAAyE,CAC1E;SACA,MAAM,CAAC,QAAQ,EAAE,mEAAmE,CAAC;SACrF,MAAM,CAAC,KAAK,EAAE,IAAwB,EAAE,OAAuB,EAAE,EAAE;QAClE,MAAM,UAAU,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * The template manifest — the file-ownership partition's source of truth
3
+ * (#1116). Written by `forgecart init` when it renders the template, read by
4
+ * `forgecart refresh` to distinguish platform-owned files (inventoried here;
5
+ * re-render when unmodified since generation) from workspace-owned state
6
+ * (everything else; never touched) and from agent-EDITED platform files
7
+ * (inventoried but diverged; conflict — skip and flag, never clobber).
8
+ */
9
+ export declare const TEMPLATE_MANIFEST_RELPATH = ".forgecart/template-manifest.json";
10
+ export interface TemplateManifest {
11
+ schemaVersion: number;
12
+ /** The CLI version whose bundled template rendered this workspace. */
13
+ templateVersion: string;
14
+ generatedAt: string;
15
+ /** posix relpath → sha256 hex of the rendered content. */
16
+ files: Record<string, string>;
17
+ }
18
+ export declare const TEMPLATE_MANIFEST_SCHEMA_VERSION = 1;
19
+ /** sha256 hex of file content — the manifest's and refresh's shared hash. */
20
+ export declare function hashContent(content: Buffer | string): string;
21
+ /**
22
+ * List the template's files as posix relpaths, depth-first, with the ignore
23
+ * set applied to top-level names (both shipped entries are top-level dirs;
24
+ * nested paths inherit their root's verdict).
25
+ */
26
+ export declare function listTemplateFiles(templateDir: string): Promise<string[]>;
27
+ /**
28
+ * Inventory the just-rendered template into the workspace's manifest. Hashes
29
+ * the TEMPLATE side of the copy — the bytes that actually landed — so a
30
+ * later refresh can prove per-file pristineness.
31
+ */
32
+ export declare function writeTemplateManifest(targetDir: string, templateDir: string, templateVersion: string): Promise<void>;
33
+ /**
34
+ * Read a workspace's manifest. Absent OR unreadable → null: the workspace is
35
+ * treated as pre-manifest, where refresh refuses every divergent file until
36
+ * explicitly adopted — the safe direction for a corrupt inventory too.
37
+ */
38
+ export declare function readTemplateManifest(targetDir: string): Promise<TemplateManifest | null>;