@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.
- package/dist/src/cli.js +3 -21
- package/dist/src/cli.js.map +1 -1
- package/dist/src/commands/__test__/cli-harness.d.ts +21 -0
- package/dist/src/commands/__test__/cli-harness.js +29 -0
- package/dist/src/commands/__test__/cli-harness.js.map +1 -0
- package/dist/src/commands/init.d.ts +28 -2
- package/dist/src/commands/init.js +100 -15
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/commands/refresh.d.ts +40 -0
- package/dist/src/commands/refresh.js +147 -0
- package/dist/src/commands/refresh.js.map +1 -0
- package/dist/src/commands/template-manifest.d.ts +38 -0
- package/dist/src/commands/template-manifest.js +116 -0
- package/dist/src/commands/template-manifest.js.map +1 -0
- package/dist/src/version.d.ts +10 -0
- package/dist/src/version.js +25 -0
- package/dist/src/version.js.map +1 -0
- package/package.json +1 -1
- package/templates/storefront/README.md +42 -4
- package/templates/storefront/next.config.js +29 -7
- package/templates/storefront/src/app/%5F%5Ffc/identify/route.ts +205 -0
- package/templates/storefront/src/app/%5F%5Ffc/track/route.ts +28 -25
- package/templates/storefront/src/app/__forge_beacon/route.ts +1 -1
- package/templates/storefront/src/app/cart/page.tsx +14 -2
- package/templates/storefront/src/app/checkout/page.tsx +14 -2
- package/templates/storefront/src/app/layout.tsx +85 -22
- package/templates/storefront/src/app/page.tsx +63 -20
- package/templates/storefront/src/app/pages/[slug]/not-found.tsx +23 -0
- package/templates/storefront/src/app/pages/[slug]/page.tsx +114 -0
- package/templates/storefront/src/app/ping/route.ts +1 -1
- package/templates/storefront/src/app/products/[slug]/not-found.tsx +6 -4
- package/templates/storefront/src/app/products/[slug]/page.tsx +204 -21
- package/templates/storefront/src/app/products/page.tsx +41 -6
- package/templates/storefront/src/app/register/page.tsx +54 -0
- package/templates/storefront/src/app/reset-password/page.tsx +60 -0
- package/templates/storefront/src/app/robots.ts +69 -0
- package/templates/storefront/src/app/sitemap.ts +106 -0
- package/templates/storefront/src/app/verify/page.tsx +155 -0
- package/templates/storefront/src/components/CartView.tsx +26 -7
- package/templates/storefront/src/components/ForgeTracker.tsx +108 -1
- package/templates/storefront/src/components/Header.tsx +30 -10
- package/templates/storefront/src/components/LanguageSwitcher.tsx +88 -0
- package/templates/storefront/src/components/LocaleLink.tsx +49 -0
- package/templates/storefront/src/components/ProductCard.tsx +10 -4
- package/templates/storefront/src/components/account/AccountMessage.tsx +59 -0
- package/templates/storefront/src/components/account/RegisterForm.tsx +283 -0
- package/templates/storefront/src/components/account/RequestPasswordResetForm.tsx +96 -0
- package/templates/storefront/src/components/account/ResetPasswordForm.tsx +169 -0
- package/templates/storefront/src/components/checkout/CheckoutGate.tsx +12 -4
- package/templates/storefront/src/lib/account/account-link.ts +76 -0
- package/templates/storefront/src/lib/account/register-state.ts +133 -0
- package/templates/storefront/src/lib/account/reset-password-state.ts +111 -0
- package/templates/storefront/src/lib/account/verify-state.ts +56 -0
- package/templates/storefront/src/lib/account-actions.ts +76 -0
- package/templates/storefront/src/lib/account-session.ts +47 -0
- package/templates/storefront/src/lib/asset-alt.ts +34 -0
- package/templates/storefront/src/lib/content/render-fields.tsx +256 -0
- package/templates/storefront/src/lib/content/resolve-page.ts +143 -0
- package/templates/storefront/src/lib/experiments.ts +1 -1
- package/templates/storefront/src/lib/forgecart.ts +300 -27
- package/templates/storefront/src/lib/format.ts +12 -14
- package/templates/storefront/src/lib/identify-forward.ts +152 -0
- package/templates/storefront/src/lib/locale/channel-locales-loader.ts +169 -0
- package/templates/storefront/src/lib/locale/channel-locales-map.ts +46 -0
- package/templates/storefront/src/lib/locale/channel-locales.ts +191 -0
- package/templates/storefront/src/lib/locale/grammar.ts +194 -0
- package/templates/storefront/src/lib/locale/localized-path.ts +55 -0
- package/templates/storefront/src/lib/locale/middleware-plan.ts +107 -0
- package/templates/storefront/src/lib/locale/request-binding.ts +80 -0
- package/templates/storefront/src/lib/locale/request-locale.ts +66 -0
- package/templates/storefront/src/lib/marketing-params.ts +213 -0
- package/templates/storefront/src/lib/money.ts +50 -0
- package/templates/storefront/src/lib/seo/alternates.ts +120 -0
- package/templates/storefront/src/lib/seo/json-ld.ts +266 -0
- package/templates/storefront/src/lib/seo/metadata.ts +323 -0
- package/templates/storefront/src/lib/seo/noindex.ts +218 -0
- package/templates/storefront/src/lib/seo/public-origin.ts +166 -0
- package/templates/storefront/src/lib/seo/redirect-plan.ts +86 -0
- package/templates/storefront/src/lib/seo/resolve-path.ts +126 -0
- package/templates/storefront/src/lib/seo/scaffolded-routes.ts +83 -0
- package/templates/storefront/src/lib/seo/sitemap-cache.ts +114 -0
- package/templates/storefront/src/lib/seo/sitemap-entries.ts +321 -0
- package/templates/storefront/src/lib/session-actions.ts +15 -8
- package/templates/storefront/src/lib/session-cookies.ts +98 -0
- package/templates/storefront/src/lib/shop-config.ts +9 -2
- package/templates/storefront/src/lib/shop-session.ts +42 -5
- package/templates/storefront/src/lib/track-forward.ts +43 -14
- package/templates/storefront/src/middleware.ts +196 -16
- package/templates/storefront/src/seo/redirects.ts +44 -0
- 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());
|
package/dist/src/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
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
|
-
/**
|
|
3
|
-
|
|
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
|
-
|
|
7
|
-
|
|
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
|
-
/**
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
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
|
-
|
|
150
|
+
entries.set('FORGECART_SHOP_API_BROWSER_URL', browserApiUrl);
|
|
135
151
|
}
|
|
136
152
|
const secret = options.adminSecret?.trim();
|
|
137
153
|
if (secret) {
|
|
138
|
-
|
|
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
|
-
|
|
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":"AAAA,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAC5E,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;
|
|
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":"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"}
|
|
@@ -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>;
|