@forgecart/cli 2.202610280649.0 → 2.202610280926.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/commands/init-questions.d.ts +12 -11
- package/dist/src/commands/init-questions.js +15 -23
- package/dist/src/commands/init-questions.js.map +1 -1
- package/dist/src/commands/init.d.ts +13 -13
- package/dist/src/commands/init.js +75 -144
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/commands/refresh.d.ts +3 -3
- package/dist/src/commands/refresh.js +4 -14
- package/dist/src/commands/refresh.js.map +1 -1
- package/dist/src/commands/template-manifest.d.ts +2 -5
- package/dist/src/commands/template-manifest.js +1 -1
- package/dist/src/commands/template-manifest.js.map +1 -1
- package/dist/src/commands/type/init.input.d.ts +1 -1
- package/dist/src/commands/type/init.types.d.ts +4 -4
- package/dist/src/template/template-set.d.ts +17 -27
- package/dist/src/template/template-set.js +25 -42
- package/dist/src/template/template-set.js.map +1 -1
- package/dist/src/template/template-unavailable-error.d.ts +3 -4
- package/dist/src/template/template-unavailable-error.js +3 -4
- package/dist/src/template/template-unavailable-error.js.map +1 -1
- package/dist/src/template/type/template-set.types.d.ts +14 -17
- package/dist/src/template/type/template-set.types.js +6 -1
- package/dist/src/template/type/template-set.types.js.map +1 -1
- package/dist/src/template/util/template-identity.util.d.ts +1 -3
- package/dist/src/template/util/template-identity.util.js +0 -5
- package/dist/src/template/util/template-identity.util.js.map +1 -1
- package/package.json +1 -1
- package/templates/storefront-blank/.forgecartignore +2 -0
- package/templates/storefront-blank/Procfile +1 -0
- package/templates/storefront-blank/README.md +86 -0
- package/templates/storefront-blank/SEO-MIGRATION.md +718 -0
- package/templates/storefront-blank/_gitignore +21 -0
- package/templates/storefront-blank/next.config.js +105 -0
- package/templates/storefront-blank/package.json +35 -0
- package/templates/storefront-blank/postcss.config.js +5 -0
- package/templates/storefront-blank/src/app/%5F%5Ffc/identify/route.ts +205 -0
- package/templates/storefront-blank/src/app/%5F%5Ffc/track/route.ts +189 -0
- package/templates/storefront-blank/src/app/%5F%5Fforge_beacon/route.ts +87 -0
- package/templates/storefront-blank/src/app/api/%5F%5Fbackend/methods/route.ts +27 -0
- package/templates/storefront-blank/src/app/error.tsx +21 -0
- package/templates/storefront-blank/src/app/global-error.tsx +23 -0
- package/templates/storefront-blank/src/app/globals.css +110 -0
- package/templates/storefront-blank/src/app/layout.tsx +102 -0
- package/templates/storefront-blank/src/app/page.tsx +52 -0
- package/templates/storefront-blank/src/app/pages/[slug]/not-found.tsx +23 -0
- package/templates/storefront-blank/src/app/pages/[slug]/page.tsx +115 -0
- package/templates/storefront-blank/src/app/ping/route.ts +21 -0
- package/templates/storefront-blank/src/app/robots.ts +69 -0
- package/templates/storefront-blank/src/app/sitemap.ts +107 -0
- package/templates/storefront-blank/src/components/ForgeErrorBeacon.tsx +102 -0
- package/templates/storefront-blank/src/components/ForgeTracker.tsx +479 -0
- package/templates/storefront-blank/src/components/ForgecartDesigner.tsx +43 -0
- package/templates/storefront-blank/src/components/LocaleLink.tsx +49 -0
- package/templates/storefront-blank/src/instrumentation.ts +109 -0
- package/templates/storefront-blank/src/lib/account/account-link.ts +76 -0
- package/templates/storefront-blank/src/lib/account/account-state.ts +152 -0
- package/templates/storefront-blank/src/lib/account/register-state.ts +133 -0
- package/templates/storefront-blank/src/lib/account/reset-password-state.ts +111 -0
- package/templates/storefront-blank/src/lib/account/verify-state.ts +56 -0
- package/templates/storefront-blank/src/lib/account-actions.ts +76 -0
- package/templates/storefront-blank/src/lib/account-session.ts +114 -0
- package/templates/storefront-blank/src/lib/action-result.ts +30 -0
- package/templates/storefront-blank/src/lib/asset-alt.ts +34 -0
- package/templates/storefront-blank/src/lib/backend-actions.ts +20 -0
- package/templates/storefront-blank/src/lib/backend-client.ts +47 -0
- package/templates/storefront-blank/src/lib/cart-context.tsx +236 -0
- package/templates/storefront-blank/src/lib/checkout-session.ts +185 -0
- package/templates/storefront-blank/src/lib/content/page-metadata.ts +113 -0
- package/templates/storefront-blank/src/lib/content/render-fields.tsx +256 -0
- package/templates/storefront-blank/src/lib/content/resolve-page.ts +143 -0
- package/templates/storefront-blank/src/lib/error-messages.ts +118 -0
- package/templates/storefront-blank/src/lib/experiments.ts +333 -0
- package/templates/storefront-blank/src/lib/forgecart.ts +464 -0
- package/templates/storefront-blank/src/lib/format.ts +89 -0
- package/templates/storefront-blank/src/lib/identify-forward.ts +152 -0
- package/templates/storefront-blank/src/lib/locale/channel-locales-loader.ts +169 -0
- package/templates/storefront-blank/src/lib/locale/channel-locales-map.ts +46 -0
- package/templates/storefront-blank/src/lib/locale/channel-locales.ts +191 -0
- package/templates/storefront-blank/src/lib/locale/grammar.ts +194 -0
- package/templates/storefront-blank/src/lib/locale/localized-path.ts +55 -0
- package/templates/storefront-blank/src/lib/locale/middleware-plan.ts +107 -0
- package/templates/storefront-blank/src/lib/locale/request-binding.ts +80 -0
- package/templates/storefront-blank/src/lib/locale/request-locale.ts +66 -0
- package/templates/storefront-blank/src/lib/marketing-params.ts +213 -0
- package/templates/storefront-blank/src/lib/money.ts +50 -0
- package/templates/storefront-blank/src/lib/seo/alternates.ts +123 -0
- package/templates/storefront-blank/src/lib/seo/json-ld.ts +266 -0
- package/templates/storefront-blank/src/lib/seo/metadata.ts +419 -0
- package/templates/storefront-blank/src/lib/seo/noindex.ts +223 -0
- package/templates/storefront-blank/src/lib/seo/public-origin.ts +166 -0
- package/templates/storefront-blank/src/lib/seo/redirect-plan.ts +86 -0
- package/templates/storefront-blank/src/lib/seo/resolve-path.ts +107 -0
- package/templates/storefront-blank/src/lib/seo/scaffolded-routes.ts +83 -0
- package/templates/storefront-blank/src/lib/seo/sidecar.ts +75 -0
- package/templates/storefront-blank/src/lib/seo/site-verification.ts +98 -0
- package/templates/storefront-blank/src/lib/seo/sitemap-cache.ts +114 -0
- package/templates/storefront-blank/src/lib/seo/sitemap-entries.ts +310 -0
- package/templates/storefront-blank/src/lib/session-actions.ts +61 -0
- package/templates/storefront-blank/src/lib/session-cookies.ts +98 -0
- package/templates/storefront-blank/src/lib/shop-config.ts +51 -0
- package/templates/storefront-blank/src/lib/shop-session.ts +204 -0
- package/templates/storefront-blank/src/lib/track-forward.ts +200 -0
- package/templates/storefront-blank/src/lib/uuid.ts +19 -0
- package/templates/storefront-blank/src/middleware.ts +379 -0
- package/templates/storefront-blank/src/seo/redirects.ts +44 -0
- package/templates/storefront-blank/src/seo/static-routes.ts +12 -0
- package/templates/storefront-blank/src/server/app.module.ts +18 -0
- package/templates/storefront-blank/src/server/backend-api.ts +21 -0
- package/templates/storefront-blank/src/server/backend-method.decorator.ts +23 -0
- package/templates/storefront-blank/src/server/bootstrap.ts +122 -0
- package/templates/storefront-blank/src/server/forge/live-revision.ts +158 -0
- package/templates/storefront-blank/src/server/forgecart/forgecart-client.factory.ts +73 -0
- package/templates/storefront-blank/src/server/forgecart/forgecart.module.ts +9 -0
- package/templates/storefront-blank/src/server/runner.ts +90 -0
- package/templates/storefront-blank/src/server/types.ts +36 -0
- package/templates/storefront-blank/tsconfig.json +25 -0
- package/templates/storefront-blank-sdk-floor.json +1174 -0
- package/templates/storefront-shadcn-blank/.forgecartignore +2 -0
- package/templates/storefront-shadcn-blank/Procfile +1 -0
- package/templates/storefront-shadcn-blank/README.md +93 -0
- package/templates/storefront-shadcn-blank/SEO-MIGRATION.md +718 -0
- package/templates/storefront-shadcn-blank/_gitignore +21 -0
- package/templates/storefront-shadcn-blank/components.json +20 -0
- package/templates/storefront-shadcn-blank/next.config.js +105 -0
- package/templates/storefront-shadcn-blank/package.json +42 -0
- package/templates/storefront-shadcn-blank/postcss.config.js +5 -0
- package/templates/storefront-shadcn-blank/src/app/%5F%5Ffc/identify/route.ts +205 -0
- package/templates/storefront-shadcn-blank/src/app/%5F%5Ffc/track/route.ts +189 -0
- package/templates/storefront-shadcn-blank/src/app/%5F%5Fforge_beacon/route.ts +87 -0
- package/templates/storefront-shadcn-blank/src/app/api/%5F%5Fbackend/methods/route.ts +27 -0
- package/templates/storefront-shadcn-blank/src/app/error.tsx +23 -0
- package/templates/storefront-shadcn-blank/src/app/global-error.tsx +23 -0
- package/templates/storefront-shadcn-blank/src/app/globals.css +156 -0
- package/templates/storefront-shadcn-blank/src/app/layout.tsx +102 -0
- package/templates/storefront-shadcn-blank/src/app/page.tsx +53 -0
- package/templates/storefront-shadcn-blank/src/app/pages/[slug]/not-found.tsx +24 -0
- package/templates/storefront-shadcn-blank/src/app/pages/[slug]/page.tsx +115 -0
- package/templates/storefront-shadcn-blank/src/app/ping/route.ts +21 -0
- package/templates/storefront-shadcn-blank/src/app/robots.ts +69 -0
- package/templates/storefront-shadcn-blank/src/app/sitemap.ts +107 -0
- package/templates/storefront-shadcn-blank/src/components/ForgeErrorBeacon.tsx +102 -0
- package/templates/storefront-shadcn-blank/src/components/ForgeTracker.tsx +479 -0
- package/templates/storefront-shadcn-blank/src/components/ForgecartDesigner.tsx +43 -0
- package/templates/storefront-shadcn-blank/src/components/LocaleLink.tsx +49 -0
- package/templates/storefront-shadcn-blank/src/components/ui/alert.tsx +75 -0
- package/templates/storefront-shadcn-blank/src/components/ui/badge.tsx +40 -0
- package/templates/storefront-shadcn-blank/src/components/ui/button.tsx +64 -0
- package/templates/storefront-shadcn-blank/src/components/ui/card.tsx +28 -0
- package/templates/storefront-shadcn-blank/src/components/ui/input.tsx +26 -0
- package/templates/storefront-shadcn-blank/src/components/ui/label.tsx +22 -0
- package/templates/storefront-shadcn-blank/src/components/ui/native-select.tsx +27 -0
- package/templates/storefront-shadcn-blank/src/components/ui/skeleton.tsx +21 -0
- package/templates/storefront-shadcn-blank/src/components/ui/utils.ts +16 -0
- package/templates/storefront-shadcn-blank/src/instrumentation.ts +109 -0
- package/templates/storefront-shadcn-blank/src/lib/account/account-link.ts +76 -0
- package/templates/storefront-shadcn-blank/src/lib/account/account-state.ts +152 -0
- package/templates/storefront-shadcn-blank/src/lib/account/register-state.ts +133 -0
- package/templates/storefront-shadcn-blank/src/lib/account/reset-password-state.ts +111 -0
- package/templates/storefront-shadcn-blank/src/lib/account/verify-state.ts +56 -0
- package/templates/storefront-shadcn-blank/src/lib/account-actions.ts +76 -0
- package/templates/storefront-shadcn-blank/src/lib/account-session.ts +114 -0
- package/templates/storefront-shadcn-blank/src/lib/action-result.ts +30 -0
- package/templates/storefront-shadcn-blank/src/lib/asset-alt.ts +34 -0
- package/templates/storefront-shadcn-blank/src/lib/backend-actions.ts +20 -0
- package/templates/storefront-shadcn-blank/src/lib/backend-client.ts +47 -0
- package/templates/storefront-shadcn-blank/src/lib/cart-context.tsx +236 -0
- package/templates/storefront-shadcn-blank/src/lib/checkout-session.ts +185 -0
- package/templates/storefront-shadcn-blank/src/lib/content/page-metadata.ts +113 -0
- package/templates/storefront-shadcn-blank/src/lib/content/render-fields.tsx +256 -0
- package/templates/storefront-shadcn-blank/src/lib/content/resolve-page.ts +143 -0
- package/templates/storefront-shadcn-blank/src/lib/error-messages.ts +118 -0
- package/templates/storefront-shadcn-blank/src/lib/experiments.ts +333 -0
- package/templates/storefront-shadcn-blank/src/lib/forgecart.ts +464 -0
- package/templates/storefront-shadcn-blank/src/lib/format.ts +89 -0
- package/templates/storefront-shadcn-blank/src/lib/identify-forward.ts +152 -0
- package/templates/storefront-shadcn-blank/src/lib/locale/channel-locales-loader.ts +169 -0
- package/templates/storefront-shadcn-blank/src/lib/locale/channel-locales-map.ts +46 -0
- package/templates/storefront-shadcn-blank/src/lib/locale/channel-locales.ts +191 -0
- package/templates/storefront-shadcn-blank/src/lib/locale/grammar.ts +194 -0
- package/templates/storefront-shadcn-blank/src/lib/locale/localized-path.ts +55 -0
- package/templates/storefront-shadcn-blank/src/lib/locale/middleware-plan.ts +107 -0
- package/templates/storefront-shadcn-blank/src/lib/locale/request-binding.ts +80 -0
- package/templates/storefront-shadcn-blank/src/lib/locale/request-locale.ts +66 -0
- package/templates/storefront-shadcn-blank/src/lib/marketing-params.ts +213 -0
- package/templates/storefront-shadcn-blank/src/lib/money.ts +50 -0
- package/templates/storefront-shadcn-blank/src/lib/seo/alternates.ts +123 -0
- package/templates/storefront-shadcn-blank/src/lib/seo/json-ld.ts +266 -0
- package/templates/storefront-shadcn-blank/src/lib/seo/metadata.ts +419 -0
- package/templates/storefront-shadcn-blank/src/lib/seo/noindex.ts +223 -0
- package/templates/storefront-shadcn-blank/src/lib/seo/public-origin.ts +166 -0
- package/templates/storefront-shadcn-blank/src/lib/seo/redirect-plan.ts +86 -0
- package/templates/storefront-shadcn-blank/src/lib/seo/resolve-path.ts +107 -0
- package/templates/storefront-shadcn-blank/src/lib/seo/scaffolded-routes.ts +83 -0
- package/templates/storefront-shadcn-blank/src/lib/seo/sidecar.ts +75 -0
- package/templates/storefront-shadcn-blank/src/lib/seo/site-verification.ts +98 -0
- package/templates/storefront-shadcn-blank/src/lib/seo/sitemap-cache.ts +114 -0
- package/templates/storefront-shadcn-blank/src/lib/seo/sitemap-entries.ts +310 -0
- package/templates/storefront-shadcn-blank/src/lib/session-actions.ts +61 -0
- package/templates/storefront-shadcn-blank/src/lib/session-cookies.ts +98 -0
- package/templates/storefront-shadcn-blank/src/lib/shop-config.ts +51 -0
- package/templates/storefront-shadcn-blank/src/lib/shop-session.ts +204 -0
- package/templates/storefront-shadcn-blank/src/lib/track-forward.ts +200 -0
- package/templates/storefront-shadcn-blank/src/lib/uuid.ts +19 -0
- package/templates/storefront-shadcn-blank/src/middleware.ts +379 -0
- package/templates/storefront-shadcn-blank/src/seo/redirects.ts +44 -0
- package/templates/storefront-shadcn-blank/src/seo/static-routes.ts +12 -0
- package/templates/storefront-shadcn-blank/src/server/app.module.ts +18 -0
- package/templates/storefront-shadcn-blank/src/server/backend-api.ts +21 -0
- package/templates/storefront-shadcn-blank/src/server/backend-method.decorator.ts +23 -0
- package/templates/storefront-shadcn-blank/src/server/bootstrap.ts +122 -0
- package/templates/storefront-shadcn-blank/src/server/forge/live-revision.ts +158 -0
- package/templates/storefront-shadcn-blank/src/server/forgecart/forgecart-client.factory.ts +73 -0
- package/templates/storefront-shadcn-blank/src/server/forgecart/forgecart.module.ts +9 -0
- package/templates/storefront-shadcn-blank/src/server/runner.ts +90 -0
- package/templates/storefront-shadcn-blank/src/server/types.ts +36 -0
- package/templates/storefront-shadcn-blank/tsconfig.json +25 -0
- package/templates/storefront-shadcn-blank-sdk-floor.json +1174 -0
- package/templates/template-set.json +15 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { MetadataRoute } from 'next';
|
|
2
|
+
|
|
3
|
+
import { NOINDEX_PATHS, NOINDEX_QUERY_KEYS } from '../lib/seo/noindex';
|
|
4
|
+
import { getPublicOrigin } from '../lib/seo/public-origin';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* `robots.txt` (#1347, epic launch#54 W1-9) — the crawler-facing half of the
|
|
8
|
+
* indexability contract in `lib/seo/noindex.ts`.
|
|
9
|
+
*
|
|
10
|
+
* FORCE-DYNAMIC, and that is load-bearing rather than cautious. Next
|
|
11
|
+
* pre-renders metadata routes at build time by default, and a storefront is
|
|
12
|
+
* built once then run with its per-channel environment supplied afterwards. A
|
|
13
|
+
* build-time answer would freeze the BUILDER's environment — which never has a
|
|
14
|
+
* channel's public origin — into `Disallow: /` for every store shipped from
|
|
15
|
+
* that image, permanently, with no way to correct it short of a rebuild. The
|
|
16
|
+
* scaffold is frozen at provisioning time (#1041), so "permanently" is literal.
|
|
17
|
+
*/
|
|
18
|
+
export const dynamic = 'force-dynamic';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Infrastructure routes that are never a destination for a human or a crawler:
|
|
22
|
+
* the tracking relay, the error beacon, the SDK proxy and the health probe.
|
|
23
|
+
*
|
|
24
|
+
* Deliberately NOT derived from the locale grammar's `UNPREFIXED_FIRST_SEGMENTS`
|
|
25
|
+
* despite the overlap. That set answers "which first segments are not a
|
|
26
|
+
* language?" and correctly contains `sitemap.xml`, `robots.txt`, `_next` and
|
|
27
|
+
* `favicon.ico` — every one of which a crawler MUST be allowed to fetch.
|
|
28
|
+
* Sharing one list would make the next infrastructure route silently
|
|
29
|
+
* uncrawlable, or a crawlable asset silently unprefixed.
|
|
30
|
+
*/
|
|
31
|
+
const INFRASTRUCTURE_DISALLOW: readonly string[] = [
|
|
32
|
+
'/api/',
|
|
33
|
+
'/__fc/',
|
|
34
|
+
'/__forge_beacon',
|
|
35
|
+
'/ping',
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Without a public origin the storefront asserts nothing, so `robots.txt`
|
|
40
|
+
* refuses the whole tree. With one, it mirrors the noindex set — the page
|
|
41
|
+
* rules are DERIVED from `noindex.ts` rather than restated, because a
|
|
42
|
+
* `robots.txt` that disagrees with the pages' own `robots` metadata is the
|
|
43
|
+
* exact drift this module exists to prevent.
|
|
44
|
+
*
|
|
45
|
+
* The `Sitemap:` line rides the origin BRANCH, not merely the origin value: the
|
|
46
|
+
* URL it names has to be absolute, and the document it points at is empty
|
|
47
|
+
* without an origin. Announcing an empty sitemap is a claim about the catalog;
|
|
48
|
+
* announcing nothing is the absence of one, which is all a deployment that has
|
|
49
|
+
* not been told its own address is entitled to say.
|
|
50
|
+
*/
|
|
51
|
+
export default function robots(): MetadataRoute.Robots {
|
|
52
|
+
const publicOrigin = getPublicOrigin();
|
|
53
|
+
if (publicOrigin === null) {
|
|
54
|
+
return { rules: { userAgent: '*', disallow: '/' } };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
rules: {
|
|
59
|
+
userAgent: '*',
|
|
60
|
+
allow: '/',
|
|
61
|
+
disallow: [
|
|
62
|
+
...NOINDEX_PATHS,
|
|
63
|
+
...INFRASTRUCTURE_DISALLOW,
|
|
64
|
+
...NOINDEX_QUERY_KEYS.map((key) => `/*?${key}=`),
|
|
65
|
+
],
|
|
66
|
+
},
|
|
67
|
+
sitemap: `${publicOrigin}/sitemap.xml`,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import type { MetadataRoute } from 'next';
|
|
2
|
+
|
|
3
|
+
import { getProductSeoEntries, getServedPageRoutes } from '../lib/forgecart';
|
|
4
|
+
import { resolveChannelLocales } from '../lib/locale/channel-locales-loader';
|
|
5
|
+
import { getPublicOrigin } from '../lib/seo/public-origin';
|
|
6
|
+
import { readScaffoldedRoutes } from '../lib/seo/scaffolded-routes';
|
|
7
|
+
import { createSitemapCache, sitemapWindowMs } from '../lib/seo/sitemap-cache';
|
|
8
|
+
import {
|
|
9
|
+
MAX_SITEMAP_URLS,
|
|
10
|
+
collectProductSitemapUrls,
|
|
11
|
+
mergeScaffoldedRoutes,
|
|
12
|
+
pageSitemapRoutes,
|
|
13
|
+
sitemapBudgetWarning,
|
|
14
|
+
staticSitemapUrls,
|
|
15
|
+
} from '../lib/seo/sitemap-entries';
|
|
16
|
+
import { STATIC_ROUTES } from '../seo/static-routes';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* `sitemap.xml` (#1347, epic launch#54 W1-9) — the discovery half of the SEO
|
|
20
|
+
* contract, listing every URL this deployment is willing to be indexed at.
|
|
21
|
+
*
|
|
22
|
+
* FORCE-DYNAMIC for the same load-bearing reason as `robots.ts`: Next
|
|
23
|
+
* pre-renders metadata routes at build time, the storefront image is built
|
|
24
|
+
* once and handed its channel environment afterwards, and the scaffold is
|
|
25
|
+
* frozen at provisioning time (#1041). A build-time answer would freeze the
|
|
26
|
+
* BUILDER's empty environment — no origin, no channel, no catalog — into an
|
|
27
|
+
* empty sitemap for every store shipped from that image, permanently. The
|
|
28
|
+
* 60s window is held in-process by `createSitemapCache` for exactly the same
|
|
29
|
+
* reason ISR is refused here: `revalidate` would pre-render the first response
|
|
30
|
+
* at build time too, which is the identical failure wearing a TTL.
|
|
31
|
+
*/
|
|
32
|
+
export const dynamic = 'force-dynamic';
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* An empty document without a public origin, and no `<lastmod>` claims — the
|
|
36
|
+
* same refusal `robots.ts` and every page's `robots` metadata make in that
|
|
37
|
+
* state. Every URL a sitemap lists is absolute, so a deployment with no origin
|
|
38
|
+
* has nothing truthful to put in one.
|
|
39
|
+
*
|
|
40
|
+
* A backend failure is deliberately NOT caught: an unreachable shop makes this
|
|
41
|
+
* route 5xx, which tells a crawler to come back. Serving an empty sitemap
|
|
42
|
+
* instead would assert that the catalog is empty — the one answer that is
|
|
43
|
+
* actively false, and the one a crawler would act on by dropping URLs.
|
|
44
|
+
*/
|
|
45
|
+
const cache = createSitemapCache(build, { ttlMs: sitemapWindowMs });
|
|
46
|
+
|
|
47
|
+
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
|
|
48
|
+
return cache.get();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* The refusal is inside the window rather than guarding it, because the origin
|
|
53
|
+
* is memoized for the life of the process: an unconfigured deployment stays
|
|
54
|
+
* unconfigured until it restarts, so caching its empty answer cannot make it
|
|
55
|
+
* any staler than the memo already has.
|
|
56
|
+
*/
|
|
57
|
+
async function build(): Promise<MetadataRoute.Sitemap> {
|
|
58
|
+
const publicOrigin = getPublicOrigin();
|
|
59
|
+
if (publicOrigin === null) return [];
|
|
60
|
+
|
|
61
|
+
const { channel } = await resolveChannelLocales(null);
|
|
62
|
+
const { languageCodes, defaultLanguageCode } = channel;
|
|
63
|
+
|
|
64
|
+
// The row's own routes (`src/seo/static-routes.ts`, #3478), every page
|
|
65
|
+
// scaffolded since, and every ACF page the shop says it currently SERVES
|
|
66
|
+
// (#1934). Read per build rather than at module load, so a page created a
|
|
67
|
+
// moment ago is in the document on the next window — the reason
|
|
68
|
+
// `createStorefrontPage` writes a row at all, and the reason the ACF feed is
|
|
69
|
+
// a live query rather than a file: an ACF page is published through the API
|
|
70
|
+
// and writes nothing to this pod's disk.
|
|
71
|
+
//
|
|
72
|
+
// PRECEDENCE falls out of the merge and is worth naming: the base table wins
|
|
73
|
+
// over both, and within the appended list the FIRST claimant of a path wins —
|
|
74
|
+
// so a code-scaffolded page at `/pages/x` outranks an ACF page at the same
|
|
75
|
+
// slug. That is also how Next routes them (a literal `app/pages/x/page.tsx`
|
|
76
|
+
// shadows the dynamic `[slug]`), so the document cannot disagree with the
|
|
77
|
+
// router about which page an address serves.
|
|
78
|
+
const [scaffolded, servedPageRoutes] = await Promise.all([
|
|
79
|
+
readScaffoldedRoutes(),
|
|
80
|
+
getServedPageRoutes(defaultLanguageCode),
|
|
81
|
+
]);
|
|
82
|
+
const routes = mergeScaffoldedRoutes(STATIC_ROUTES, [
|
|
83
|
+
...scaffolded,
|
|
84
|
+
...pageSitemapRoutes(servedPageRoutes),
|
|
85
|
+
]);
|
|
86
|
+
const staticUrls = staticSitemapUrls(languageCodes, defaultLanguageCode, publicOrigin, routes);
|
|
87
|
+
const productUrls = await collectProductSitemapUrls(
|
|
88
|
+
async (skip, take) => getProductSeoEntries(defaultLanguageCode, { skip, take }),
|
|
89
|
+
defaultLanguageCode,
|
|
90
|
+
publicOrigin,
|
|
91
|
+
Math.max(0, MAX_SITEMAP_URLS - staticUrls.length),
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
const urls = [...staticUrls, ...productUrls];
|
|
95
|
+
|
|
96
|
+
// The one place this template writes to the console, and it earns it: a
|
|
97
|
+
// catalog that outgrows a single XML sitemap would otherwise stop being
|
|
98
|
+
// submitted for indexing in complete silence, inside the feature whose whole
|
|
99
|
+
// job is getting pages indexed. There is no logger in a scaffolded storefront
|
|
100
|
+
// and no channelId to tag — only the channel token, which is a credential and
|
|
101
|
+
// must never reach a log — so the pod's stdout is the channel, and the 60s
|
|
102
|
+
// window bounds this to at most one line a minute.
|
|
103
|
+
const warning = sitemapBudgetWarning(urls.length);
|
|
104
|
+
if (warning !== null) console.warn(warning);
|
|
105
|
+
|
|
106
|
+
return urls;
|
|
107
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useEffect } from 'react';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* DEV-ONLY storefront error beacon. Renders nothing.
|
|
7
|
+
*
|
|
8
|
+
* Registers `window.onerror` + `window.onunhandledrejection` and POSTs the failure
|
|
9
|
+
* detail to the same-origin Next API route `/__forge_beacon`, which forwards it
|
|
10
|
+
* server-side to the in-pod workspace-manager receiver. This surfaces the crash
|
|
11
|
+
* class the pod's dev-server stderr scan CANNOT see: a client-component runtime
|
|
12
|
+
* error or a hydration/SSR-boundary throw that never reaches the dev-server's
|
|
13
|
+
* output. The browser only ever talks SAME-ORIGIN — the API route is the only thing
|
|
14
|
+
* that knows the pod-internal receiver address.
|
|
15
|
+
*
|
|
16
|
+
* Production builds ship zero bytes of this: the literal `NODE_ENV` check is inlined
|
|
17
|
+
* by the bundler, so the effect body is dead-code-eliminated from `next build` /
|
|
18
|
+
* `next start` output — a deployed storefront registers no global error handlers and
|
|
19
|
+
* has no beacon API route reachable in prod (the route itself is dev-gated too).
|
|
20
|
+
*
|
|
21
|
+
* The beacon is best-effort and never blocks the page: the POST is fire-and-forget
|
|
22
|
+
* with `keepalive` so it survives an unload, and a delivery failure is swallowed —
|
|
23
|
+
* the page's own error boundary still renders. The full
|
|
24
|
+
* browser → API route → pod → supervisor-fold path is exercised at the e2e layer,
|
|
25
|
+
* not in this template.
|
|
26
|
+
*
|
|
27
|
+
* Every report carries the revision the PAGE WAS SERVED AT (#847), read out of the
|
|
28
|
+
* `forge-revision` meta tag the server wrote into this document rather than looked
|
|
29
|
+
* up fresh at throw time. That distinction is the entire point: a fresh lookup would
|
|
30
|
+
* answer whatever is live NOW, so a promote landing between serve and throw would
|
|
31
|
+
* make this report blame the new revision for the old one's crash — and the pod's
|
|
32
|
+
* same-revision fold would then mark a healthy tree DEGRADED. The tag cannot drift,
|
|
33
|
+
* because it was rendered with the markup that broke.
|
|
34
|
+
*/
|
|
35
|
+
export function ForgeErrorBeacon() {
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
if (process.env.NODE_ENV !== 'development') {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* The revision this document was SERVED at, or `undefined` when the server
|
|
43
|
+
* did not stamp one (production, or a pod that cannot resolve it — both
|
|
44
|
+
* normal). `'forge-revision'` is repeated here as a literal on purpose: the
|
|
45
|
+
* resolver lives in `src/server/forge/live-revision.ts`, which a client
|
|
46
|
+
* component must not import, so this is the same repeated-contract shape the
|
|
47
|
+
* `/__forge_beacon` URL already uses across this feature's producers.
|
|
48
|
+
*
|
|
49
|
+
* An absent tag, an absent attribute and an empty value all answer
|
|
50
|
+
* `undefined`, which `JSON.stringify` then omits from the body entirely —
|
|
51
|
+
* so an unstamped page sends exactly the payload it sent before, and the pod
|
|
52
|
+
* keeps its existing untagged behaviour instead of receiving a blank string
|
|
53
|
+
* it would have to interpret.
|
|
54
|
+
*/
|
|
55
|
+
const readServedRevision = (): string | undefined => {
|
|
56
|
+
const content = document
|
|
57
|
+
.querySelector('meta[name="forge-revision"]')
|
|
58
|
+
?.getAttribute('content');
|
|
59
|
+
if (!content) return undefined;
|
|
60
|
+
return content;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const send = (message: string, stack?: string) => {
|
|
64
|
+
const body = JSON.stringify({
|
|
65
|
+
message,
|
|
66
|
+
stack,
|
|
67
|
+
route: window.location.pathname,
|
|
68
|
+
source: 'browser',
|
|
69
|
+
atRevision: readServedRevision(),
|
|
70
|
+
});
|
|
71
|
+
// Same-origin POST; keepalive lets it survive a navigation/unload. A failed
|
|
72
|
+
// delivery is intentionally swallowed — the beacon is a backstop signal, not
|
|
73
|
+
// a hard dependency of the page.
|
|
74
|
+
void fetch('/__forge_beacon', {
|
|
75
|
+
method: 'POST',
|
|
76
|
+
headers: { 'content-type': 'application/json' },
|
|
77
|
+
body,
|
|
78
|
+
keepalive: true,
|
|
79
|
+
}).catch(() => undefined);
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const onError = (event: ErrorEvent) => {
|
|
83
|
+
send(event.message || 'window.onerror', event.error?.stack);
|
|
84
|
+
};
|
|
85
|
+
const onRejection = (event: PromiseRejectionEvent) => {
|
|
86
|
+
const reason = event.reason;
|
|
87
|
+
const message =
|
|
88
|
+
reason instanceof Error ? reason.message : String(reason ?? 'unhandledrejection');
|
|
89
|
+
const stack = reason instanceof Error ? reason.stack : undefined;
|
|
90
|
+
send(message, stack);
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
window.addEventListener('error', onError);
|
|
94
|
+
window.addEventListener('unhandledrejection', onRejection);
|
|
95
|
+
return () => {
|
|
96
|
+
window.removeEventListener('error', onError);
|
|
97
|
+
window.removeEventListener('unhandledrejection', onRejection);
|
|
98
|
+
};
|
|
99
|
+
}, []);
|
|
100
|
+
|
|
101
|
+
return null;
|
|
102
|
+
}
|