@tokenoftrust/storefront-runner 2.0.8 → 2.0.10
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/apps/storefront/integrations/materialize-guard.mjs +8 -2
- package/apps/storefront/src/lib/auth/brokerAssertion.ts +31 -5
- package/apps/storefront/src/lib/shared/principalGrants.ts +215 -94
- package/apps/storefront/src/lib/tot/syncState.ts +109 -0
- package/package.json +1 -1
- package/packages/public-runtime/src/customization-reconcile.ts +15 -1
- package/packages/public-runtime/src/evidence.ts +14 -11
- package/pnpm-workspace.yaml +0 -3
- package/scripts/tenant/materialize-tenant-content.mjs +107 -7
- package/apps/storefront/.astro/content.d.ts +0 -159
- package/apps/storefront/.astro/integrations/_astrojs_cloudflare/cloudflare.d.ts +0 -1
- package/apps/storefront/.astro/types.d.ts +0 -3
- package/apps/storefront/node_modules/.bin/acorn +0 -43
- package/apps/storefront/node_modules/.bin/astro +0 -43
- package/apps/storefront/node_modules/.bin/astro-check +0 -43
- package/apps/storefront/node_modules/.bin/browserslist +0 -43
- package/apps/storefront/node_modules/.bin/cf-wrangler +0 -43
- package/apps/storefront/node_modules/.bin/depcruise +0 -43
- package/apps/storefront/node_modules/.bin/depcruise-baseline +0 -43
- package/apps/storefront/node_modules/.bin/depcruise-fmt +0 -43
- package/apps/storefront/node_modules/.bin/depcruise-wrap-stream-in-html +0 -43
- package/apps/storefront/node_modules/.bin/dependency-cruise +0 -43
- package/apps/storefront/node_modules/.bin/dependency-cruiser +0 -43
- package/apps/storefront/node_modules/.bin/drizzle-kit +0 -43
- package/apps/storefront/node_modules/.bin/esbuild +0 -43
- package/apps/storefront/node_modules/.bin/jiti +0 -43
- package/apps/storefront/node_modules/.bin/playwright +0 -43
- package/apps/storefront/node_modules/.bin/playwright-core +0 -43
- package/apps/storefront/node_modules/.bin/prettier +0 -43
- package/apps/storefront/node_modules/.bin/rollup +0 -43
- package/apps/storefront/node_modules/.bin/tsc +0 -43
- package/apps/storefront/node_modules/.bin/tsserver +0 -43
- package/apps/storefront/node_modules/.bin/tsx +0 -43
- package/apps/storefront/node_modules/.bin/vite +0 -43
- package/apps/storefront/node_modules/.bin/vitest +0 -43
- package/apps/storefront/node_modules/.bin/workerd +0 -43
- package/apps/storefront/node_modules/.bin/wrangler +0 -43
- package/apps/storefront/node_modules/.bin/wrangler2 +0 -43
- package/apps/storefront/node_modules/.bin/yaml +0 -43
- package/apps/storefront/node_modules/.vite/deps/@astrojs_preact_client__js.js +0 -58
- package/apps/storefront/node_modules/.vite/deps/@astrojs_preact_client__js.js.map +0 -1
- package/apps/storefront/node_modules/.vite/deps/@astrojs_preact_n_@preact_signals.js +0 -741
- package/apps/storefront/node_modules/.vite/deps/@astrojs_preact_n_@preact_signals.js.map +0 -1
- package/apps/storefront/node_modules/.vite/deps/@astrojs_preact_server__js.js +0 -2549
- package/apps/storefront/node_modules/.vite/deps/@astrojs_preact_server__js.js.map +0 -1
- package/apps/storefront/node_modules/.vite/deps/_metadata.json +0 -150
- package/apps/storefront/node_modules/.vite/deps/astro-BIHuWFxm.js +0 -434
- package/apps/storefront/node_modules/.vite/deps/astro-BIHuWFxm.js.map +0 -1
- package/apps/storefront/node_modules/.vite/deps/astro_n_aria-query.js +0 -6390
- package/apps/storefront/node_modules/.vite/deps/astro_n_aria-query.js.map +0 -1
- package/apps/storefront/node_modules/.vite/deps/astro_n_axobject-query.js +0 -2705
- package/apps/storefront/node_modules/.vite/deps/astro_n_axobject-query.js.map +0 -1
- package/apps/storefront/node_modules/.vite/deps/astro_n_html-escaper.js +0 -65
- package/apps/storefront/node_modules/.vite/deps/astro_n_html-escaper.js.map +0 -1
- package/apps/storefront/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js +0 -455
- package/apps/storefront/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js.map +0 -1
- package/apps/storefront/node_modules/.vite/deps/audit-DKDpcdEI.js +0 -1535
- package/apps/storefront/node_modules/.vite/deps/audit-DKDpcdEI.js.map +0 -1
- package/apps/storefront/node_modules/.vite/deps/highlight-B9UoX1wa.js +0 -48
- package/apps/storefront/node_modules/.vite/deps/highlight-B9UoX1wa.js.map +0 -1
- package/apps/storefront/node_modules/.vite/deps/icons-BWi2wI9n.js +0 -42
- package/apps/storefront/node_modules/.vite/deps/icons-BWi2wI9n.js.map +0 -1
- package/apps/storefront/node_modules/.vite/deps/package.json +0 -3
- package/apps/storefront/node_modules/.vite/deps/preact.js +0 -310
- package/apps/storefront/node_modules/.vite/deps/preact.js.map +0 -1
- package/apps/storefront/node_modules/.vite/deps/preact_compat.js +0 -420
- package/apps/storefront/node_modules/.vite/deps/preact_compat.js.map +0 -1
- package/apps/storefront/node_modules/.vite/deps/preact_compat_jsx-runtime.js +0 -4
- package/apps/storefront/node_modules/.vite/deps/preact_debug.js +0 -236
- package/apps/storefront/node_modules/.vite/deps/preact_debug.js.map +0 -1
- package/apps/storefront/node_modules/.vite/deps/preact_devtools.js +0 -12
- package/apps/storefront/node_modules/.vite/deps/preact_devtools.js.map +0 -1
- package/apps/storefront/node_modules/.vite/deps/preact_hooks.js +0 -192
- package/apps/storefront/node_modules/.vite/deps/preact_hooks.js.map +0 -1
- package/apps/storefront/node_modules/.vite/deps/preact_jsx-dev-runtime.js +0 -3
- package/apps/storefront/node_modules/.vite/deps/preact_jsx-runtime.js +0 -92
- package/apps/storefront/node_modules/.vite/deps/preact_jsx-runtime.js.map +0 -1
- package/apps/storefront/node_modules/.vite/deps/preact_test-utils.js +0 -59
- package/apps/storefront/node_modules/.vite/deps/preact_test-utils.js.map +0 -1
- package/apps/storefront/node_modules/.vite/deps/rolldown-runtime-BPOCksWG.js +0 -24
- package/apps/storefront/node_modules/.vite/deps/settings-BVDV0Roj.js +0 -189
- package/apps/storefront/node_modules/.vite/deps/settings-BVDV0Roj.js.map +0 -1
- package/apps/storefront/node_modules/.vite/deps/static-html-DveYqwMo.js +0 -13
- package/apps/storefront/node_modules/.vite/deps/static-html-DveYqwMo.js.map +0 -1
- package/apps/storefront/node_modules/.vite/deps/toolbar-B3VIHCXg.js +0 -458
- package/apps/storefront/node_modules/.vite/deps/toolbar-B3VIHCXg.js.map +0 -1
- package/apps/storefront/node_modules/.vite/deps/ui-library-BK6ricUd.js +0 -1016
- package/apps/storefront/node_modules/.vite/deps/ui-library-BK6ricUd.js.map +0 -1
- package/apps/storefront/node_modules/.vite/deps/window-BvxDenKN.js +0 -32
- package/apps/storefront/node_modules/.vite/deps/window-BvxDenKN.js.map +0 -1
- package/apps/storefront/node_modules/.vite/deps/xray-YFiRUU0Z.js +0 -113
- package/apps/storefront/node_modules/.vite/deps/xray-YFiRUU0Z.js.map +0 -1
- package/apps/storefront/node_modules/.vite/deps_ssr/_metadata.json +0 -8
- package/apps/storefront/node_modules/.vite/deps_ssr/package.json +0 -3
- package/packages/public-runtime/node_modules/.bin/tsc +0 -43
- package/packages/public-runtime/node_modules/.bin/tsserver +0 -43
- package/packages/public-runtime/node_modules/.bin/vitest +0 -43
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Worker-side reader for the durable per-tenant catalog sync state (hsby-B1).
|
|
3
|
+
* The migration CLI writes one JSON document per tenant scope at
|
|
4
|
+
* out/catalog/<scope>/sync-state.json (packages/migration/src/load/sync-state.ts,
|
|
5
|
+
* `SyncStateStore`/`nextSyncState`) — this module is the Worker's read side of
|
|
6
|
+
* that same file, via the same Vite glob mechanism `ToTClient.ts` uses for
|
|
7
|
+
* products.json/collections.json/reviews.json. OPTIONAL: a tenant with no
|
|
8
|
+
* recorded sync (or one migrated before hsby-B1 landed) just gets `null`.
|
|
9
|
+
*
|
|
10
|
+
* This is intentionally a thin, standalone read — not wired into `ToTClient`,
|
|
11
|
+
* since not every caller needs sync provenance. hsby-B3's readiness projection
|
|
12
|
+
* is the expected first consumer.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/** Mirrors packages/migration/src/load/sync-state.ts's `SourcePlatform`. */
|
|
16
|
+
export type SyncStateSourcePlatform =
|
|
17
|
+
| "shopify-admin"
|
|
18
|
+
| "shopify-public"
|
|
19
|
+
| "fixtures"
|
|
20
|
+
| "existing";
|
|
21
|
+
|
|
22
|
+
/** Mirrors packages/migration/src/load/sync-state.ts's `FidelityLevel`. */
|
|
23
|
+
export type SyncStateFidelityLevel = "full" | "reduced" | "unknown";
|
|
24
|
+
|
|
25
|
+
export interface SyncStateFidelity {
|
|
26
|
+
level: SyncStateFidelityLevel;
|
|
27
|
+
note: string;
|
|
28
|
+
missing: string[];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface SyncStateSourceMode {
|
|
32
|
+
platform: SyncStateSourcePlatform;
|
|
33
|
+
mode: "full" | "incremental";
|
|
34
|
+
fullSweepOnly: boolean;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface SyncStateEntity {
|
|
38
|
+
checksums: Record<string, string>;
|
|
39
|
+
lastUpdatedAt: string | null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Mirrors packages/migration/src/load/sync-state.ts's `EntityChangeSet`. */
|
|
43
|
+
export interface SyncStateEntityChangeSet {
|
|
44
|
+
added: string[];
|
|
45
|
+
updated: string[];
|
|
46
|
+
removed: string[];
|
|
47
|
+
unchanged: number;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Mirrors packages/migration/src/load/sync-state.ts's `SweepChanges`. */
|
|
51
|
+
export interface SyncStateSweepChanges {
|
|
52
|
+
computedAt: string;
|
|
53
|
+
mode: "full" | "incremental";
|
|
54
|
+
removalsAssessed: boolean;
|
|
55
|
+
fullSweepOnly: boolean;
|
|
56
|
+
products: SyncStateEntityChangeSet;
|
|
57
|
+
collections: SyncStateEntityChangeSet;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** The on-disk shape written by the migration CLI (schema "tot.sync-state/1"). */
|
|
61
|
+
export interface CatalogSyncState {
|
|
62
|
+
schema: string;
|
|
63
|
+
tenantId: string;
|
|
64
|
+
scope: string;
|
|
65
|
+
source: SyncStateSourceMode;
|
|
66
|
+
fidelity: SyncStateFidelity;
|
|
67
|
+
lastSyncAt: string | null;
|
|
68
|
+
products: SyncStateEntity;
|
|
69
|
+
collections: SyncStateEntity;
|
|
70
|
+
/**
|
|
71
|
+
* The content-hash delta of the most recent sweep. Optional: a catalog
|
|
72
|
+
* migrated before hsby-B2 (change detection) has no `lastChanges`.
|
|
73
|
+
*/
|
|
74
|
+
lastChanges?: SyncStateSweepChanges;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Vite resolves this glob at build time and bundles every tenant's sync-state
|
|
78
|
+
// document, so the Worker has no runtime filesystem dependency — same pattern
|
|
79
|
+
// as productGlobs/collectionGlobs/reviewGlobs in ToTClient.ts.
|
|
80
|
+
const syncStateGlobs = import.meta.glob<CatalogSyncState>(
|
|
81
|
+
"../../../../../packages/migration/out/catalog/*/sync-state.json",
|
|
82
|
+
{ import: "default" },
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
const syncStateCacheByScope = new Map<string, CatalogSyncState | null>();
|
|
86
|
+
|
|
87
|
+
/** Pick the glob entry for a tenant scope (matches .../catalog/<scope>/file). */
|
|
88
|
+
function pickByScope(scope: string): (() => Promise<CatalogSyncState>) | null {
|
|
89
|
+
const suffix = `/catalog/${scope}/`;
|
|
90
|
+
const key = Object.keys(syncStateGlobs).find((k) => k.includes(suffix));
|
|
91
|
+
return (key ? syncStateGlobs[key] : null) ?? null;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* The tenant's durable sync state, or null when none was ever recorded (no
|
|
96
|
+
* migration run yet, or a pre-hsby-B1 catalog). Never throws.
|
|
97
|
+
*/
|
|
98
|
+
export async function getSyncState(scope: string): Promise<CatalogSyncState | null> {
|
|
99
|
+
if (syncStateCacheByScope.has(scope)) return syncStateCacheByScope.get(scope)!;
|
|
100
|
+
const loader = pickByScope(scope);
|
|
101
|
+
const state = loader ? await loader() : null;
|
|
102
|
+
syncStateCacheByScope.set(scope, state);
|
|
103
|
+
return state;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** Test seam: drop the per-isolate cache. */
|
|
107
|
+
export function clearSyncStateCache(): void {
|
|
108
|
+
syncStateCacheByScope.clear();
|
|
109
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tokenoftrust/storefront-runner",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.10",
|
|
4
4
|
"license": "SEE LICENSE IN LICENSE",
|
|
5
5
|
"description": "World-shareable storefront runner: multi-tenant renderer on Astro/Cloudflare. No control plane.",
|
|
6
6
|
"packageManager": "pnpm@11.9.0",
|
|
@@ -90,7 +90,21 @@ export interface PreviewState {
|
|
|
90
90
|
* post-hoc merge sets it, after `reportPreviewEvidence` computes it. Absent and
|
|
91
91
|
* `null` both mean "no verdict yet"; callers should treat them the same.
|
|
92
92
|
*/
|
|
93
|
-
evidence?: {
|
|
93
|
+
evidence?: {
|
|
94
|
+
checks: PreviewStateEvidenceCheck[];
|
|
95
|
+
promotable: boolean;
|
|
96
|
+
generatedAt: string;
|
|
97
|
+
/**
|
|
98
|
+
* Set when the MCP `change_report_evidence` push for this verdict failed —
|
|
99
|
+
* `promotable` above is already forced `false` in that case (fail-closed: MCP
|
|
100
|
+
* has no evidence for this commit, so the local record must not claim
|
|
101
|
+
* shippability the other source of truth doesn't share). Mirrors
|
|
102
|
+
* `ReviewEnvironmentEvidence.pushFailed` (candidate-index.ts).
|
|
103
|
+
*/
|
|
104
|
+
pushFailed?: boolean;
|
|
105
|
+
/** The push failure's error message, when `pushFailed` is true. */
|
|
106
|
+
pushError?: string;
|
|
107
|
+
} | null;
|
|
94
108
|
}
|
|
95
109
|
|
|
96
110
|
export function previewStateKey(tenantId: string): string {
|
|
@@ -9,16 +9,16 @@
|
|
|
9
9
|
* See `epics/dev-loop.md` (D4 + the MCP-foundations block).
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
export const EVIDENCE_CONTRACT_VERSION =
|
|
12
|
+
// v2 (2026-08): added the `assets_materialized` check — reconcile now fetch-verifies
|
|
13
|
+
// that a version's public/app asset subtree actually SERVES (the pagination-truncation
|
|
14
|
+
// class of silent drop, fb-1787794734109-hjru1k, reported success + promotable while
|
|
15
|
+
// the newest asset subtree 404'd). GRANDFATHER: a consumer must treat an
|
|
16
|
+
// already-passed v1 report as still valid — the new check is version-gated, so a v1
|
|
17
|
+
// report (which cannot carry it) is never retroactively failed for its absence. The
|
|
18
|
+
// promote-gate addition (`assets_materialized` → REQUIRED_FOR_PROMOTE) is
|
|
19
|
+
// version-gated to v2 on the CONSUMER (tot-mcp); this producer emits the check but
|
|
20
|
+
// leaves REQUIRED_FOR_PROMOTE unchanged so a v1-shaped promote rule still holds.
|
|
21
|
+
export const EVIDENCE_CONTRACT_VERSION = 2;
|
|
22
22
|
|
|
23
23
|
/** The per-check verdicts. `skip` = not applicable / not yet measured. */
|
|
24
24
|
export const EVIDENCE_STATUSES = ["pass", "warn", "fail", "skip"] as const;
|
|
@@ -37,7 +37,10 @@ export const EVIDENCE_CHECK_IDS = [
|
|
|
37
37
|
"json_ld",
|
|
38
38
|
"sitemap",
|
|
39
39
|
"cutover_crawl",
|
|
40
|
-
//
|
|
40
|
+
// v2: did this version's public/app asset subtree actually materialize + serve
|
|
41
|
+
// (fetch-verified against R2 at the published versionId). Fail-closed — a silent
|
|
42
|
+
// drop (pagination truncation, a failed ingest) reads `fail`, never a vacuous pass.
|
|
43
|
+
"assets_materialized",
|
|
41
44
|
] as const;
|
|
42
45
|
export type EvidenceCheckId = (typeof EVIDENCE_CHECK_IDS)[number];
|
|
43
46
|
|
package/pnpm-workspace.yaml
CHANGED
|
@@ -38,6 +38,12 @@
|
|
|
38
38
|
* node scripts/tenant/materialize-tenant-content.mjs --check-pinned [--expect-ref <sha>] [--allow-unpinned]
|
|
39
39
|
* # stricter deploy gate: verify populated AND materialized from Gitea at a
|
|
40
40
|
* # PINNED ref (.materialized.json present, pinned, on the expected ref); no network
|
|
41
|
+
* node scripts/tenant/materialize-tenant-content.mjs --check-pinned --verify-fresh
|
|
42
|
+
* # opt-in NETWORK addition to --check-pinned: for any stamp pinned to a
|
|
43
|
+
* # MOVABLE ref (a branch name, or an unpinned default-branch materialize),
|
|
44
|
+
* # resolve that ref's CURRENT tip on Gitea and refuse if the stamped sha no
|
|
45
|
+
* # longer matches it (`git ls-remote`, no clone). A stamp pinned to an exact
|
|
46
|
+
* # commit SHA is frozen by design and can never be "stale" under this check.
|
|
41
47
|
*
|
|
42
48
|
* Auth (never logged; tokens are redacted from every error):
|
|
43
49
|
* TENANT_GIT_BASE clone base. Default https://git.tokenoftrust.com/storefront
|
|
@@ -71,6 +77,7 @@ const repoRoot = resolve(here, "..", "..");
|
|
|
71
77
|
|
|
72
78
|
export const MANIFEST_PATH = "tenants/.gitea-tenants.json";
|
|
73
79
|
const TENANT_ID_RE = /^[a-z0-9][a-z0-9.-]{1,60}[a-z0-9]$/;
|
|
80
|
+
const FULL_SHA_RE = /^[0-9a-f]{40}$/i;
|
|
74
81
|
const DEFAULT_GIT_BASE = "https://git.tokenoftrust.com/storefront";
|
|
75
82
|
|
|
76
83
|
/** Load the Gitea-tenant manifest. Returns { tenants: { <id>: { repo } } }. */
|
|
@@ -380,9 +387,87 @@ export function checkPinned(
|
|
|
380
387
|
return { ok: problems.length === 0, problems };
|
|
381
388
|
}
|
|
382
389
|
|
|
383
|
-
/**
|
|
384
|
-
*
|
|
385
|
-
*
|
|
390
|
+
/**
|
|
391
|
+
* `--verify-fresh` (opt-in, NETWORK): the one thing `checkPinned` deliberately cannot tell you —
|
|
392
|
+
* whether a pinned stamp is still current. `checkPinned` only proves the stamp is pinned to SOME
|
|
393
|
+
* ref (and, with `expectRef`, that it matches a caller-supplied value); it never talks to Gitea,
|
|
394
|
+
* so a stamp pinned to a MOVABLE ref (a branch name, or an unpinned default-branch materialize)
|
|
395
|
+
* can silently drift arbitrarily far behind that ref's current tip and keep passing forever.
|
|
396
|
+
*
|
|
397
|
+
* A stamp pinned to an exact commit SHA is excluded on purpose: that's a deliberate promotion to
|
|
398
|
+
* an immutable commit, frozen by design, and is never "stale" — re-promoting to a newer commit is
|
|
399
|
+
* a separate, explicit act (a fresh `--ref <sha>` materialize), not something this check should
|
|
400
|
+
* flag as a problem.
|
|
401
|
+
*
|
|
402
|
+
* For each in-scope tenant with a stamp, resolves the stamped ref's CURRENT tip via
|
|
403
|
+
* `git ls-remote` (no clone) and compares it to the stamped sha. Requires read access to Gitea —
|
|
404
|
+
* same TENANT_GIT_USER/TENANT_GIT_TOKEN as materializeTenant. Tenants that already fail
|
|
405
|
+
* `checkPinned` (absent/missing/unpinned-without-allowUnpinned/off-ref) are left to those
|
|
406
|
+
* existing reasons and skipped here to avoid double-reporting the same tenant.
|
|
407
|
+
*
|
|
408
|
+
* Returns { ok, problems: [{ tenant, reason }] } where reason ∈ "unreachable" | "stale":
|
|
409
|
+
* unreachable the live tip could not be resolved (network/auth failure, deleted ref, etc.) —
|
|
410
|
+
* never treated as fresh; an unreachable Gitea must not silently pass this gate.
|
|
411
|
+
* stale the stamped sha no longer matches the ref's live tip.
|
|
412
|
+
*
|
|
413
|
+
* @param {string} [root]
|
|
414
|
+
* @param {object} [opts]
|
|
415
|
+
* @param {string} [opts.gitBase]
|
|
416
|
+
* @param {string} [opts.user]
|
|
417
|
+
* @param {string} [opts.token]
|
|
418
|
+
* @param {string[]|null} [opts.onlyTenants]
|
|
419
|
+
*/
|
|
420
|
+
export function checkFresh(
|
|
421
|
+
root = repoRoot,
|
|
422
|
+
{
|
|
423
|
+
gitBase = DEFAULT_GIT_BASE,
|
|
424
|
+
user = process.env.TENANT_GIT_USER,
|
|
425
|
+
token = process.env.TENANT_GIT_TOKEN,
|
|
426
|
+
onlyTenants = null,
|
|
427
|
+
} = {},
|
|
428
|
+
) {
|
|
429
|
+
const manifest = loadManifest(root);
|
|
430
|
+
const problems = [];
|
|
431
|
+
const scope = onlyTenants ? new Set(onlyTenants) : null;
|
|
432
|
+
for (const tenant of Object.keys(manifest.tenants)) {
|
|
433
|
+
if (scope && !scope.has(tenant)) continue;
|
|
434
|
+
const stampPath = join(root, "tenants", tenant, ".materialized.json");
|
|
435
|
+
if (!existsSync(stampPath)) continue; // checkPinned already reports absent/missing
|
|
436
|
+
let stamp;
|
|
437
|
+
try {
|
|
438
|
+
stamp = JSON.parse(readFileSync(stampPath, "utf8"));
|
|
439
|
+
} catch {
|
|
440
|
+
continue; // checkPinned already reports this as "missing"
|
|
441
|
+
}
|
|
442
|
+
const sha = typeof stamp.sha === "string" ? stamp.sha : "";
|
|
443
|
+
const rawRef = typeof stamp.ref === "string" ? stamp.ref : "";
|
|
444
|
+
if (!sha || !FULL_SHA_RE.test(sha)) continue; // no usable sha to compare — leave to checkPinned
|
|
445
|
+
if (FULL_SHA_RE.test(rawRef)) continue; // pinned to an exact commit — frozen by design
|
|
446
|
+
const movableRef = rawRef === "" || rawRef === "(default branch)" ? "HEAD" : rawRef;
|
|
447
|
+
const repo = manifest.tenants[tenant]?.repo ?? tenant;
|
|
448
|
+
const cloneUrl = buildCloneUrl({ base: gitBase, repo, user, token });
|
|
449
|
+
let liveSha = "";
|
|
450
|
+
try {
|
|
451
|
+
const out = execFileSync("git", ["ls-remote", cloneUrl, movableRef], {
|
|
452
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
453
|
+
}).toString();
|
|
454
|
+
liveSha = (out.split(/\s+/)[0] || "").trim();
|
|
455
|
+
} catch {
|
|
456
|
+
problems.push({ tenant, reason: "unreachable" });
|
|
457
|
+
continue;
|
|
458
|
+
}
|
|
459
|
+
if (!FULL_SHA_RE.test(liveSha)) {
|
|
460
|
+
problems.push({ tenant, reason: "unreachable" });
|
|
461
|
+
continue;
|
|
462
|
+
}
|
|
463
|
+
if (liveSha !== sha) problems.push({ tenant, reason: "stale" });
|
|
464
|
+
}
|
|
465
|
+
return { ok: problems.length === 0, problems };
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
/** Human-readable refusal for a failed `checkPinned` (+ optional `checkFresh`) run (shared by
|
|
469
|
+
* the CLI + the build-time guard). `problems` is checkPinned().problems, optionally concatenated
|
|
470
|
+
* with checkFresh().problems; `expectRef` is echoed into the off-ref explanation when set. */
|
|
386
471
|
export function describePinnedProblems(problems, /** @type {string|null} */ expectRef = null) {
|
|
387
472
|
const how = {
|
|
388
473
|
absent: "not materialized (no tenant content on disk at all)",
|
|
@@ -390,6 +475,8 @@ export function describePinnedProblems(problems, /** @type {string|null} */ expe
|
|
|
390
475
|
unpinned:
|
|
391
476
|
"materialized from the tenant repo's DEFAULT branch (no pinned ref) — may be stale",
|
|
392
477
|
"off-ref": `materialized at a different ref than expected (${expectRef})`,
|
|
478
|
+
stale: "pinned ref's live Gitea tip has moved past the materialized sha — re-materialize",
|
|
479
|
+
unreachable: "could not resolve the tenant repo's live tip (network/auth failure)",
|
|
393
480
|
};
|
|
394
481
|
return (
|
|
395
482
|
"REFUSING: tenant content is not materialized from Gitea at a pinned ref.\n" +
|
|
@@ -438,6 +525,7 @@ function parseArgs(argv) {
|
|
|
438
525
|
checkPinned: false,
|
|
439
526
|
expectRef: null,
|
|
440
527
|
allowUnpinned: false,
|
|
528
|
+
verifyFresh: false,
|
|
441
529
|
help: false,
|
|
442
530
|
};
|
|
443
531
|
for (let i = 0; i < argv.length; i++) {
|
|
@@ -449,11 +537,15 @@ function parseArgs(argv) {
|
|
|
449
537
|
else if (t === "--check-pinned") a.checkPinned = true;
|
|
450
538
|
else if (t === "--expect-ref") a.expectRef = argv[++i];
|
|
451
539
|
else if (t === "--allow-unpinned") a.allowUnpinned = true;
|
|
540
|
+
else if (t === "--verify-fresh") a.verifyFresh = true;
|
|
452
541
|
else if (t === "--help" || t === "-h") a.help = true;
|
|
453
542
|
else if (!t.startsWith("--") && !a.tenant) a.tenant = t;
|
|
454
543
|
else throw new Error(`unknown argument: ${t}`);
|
|
455
544
|
}
|
|
456
545
|
if (a.remote && a.all) throw new Error("--remote targets ONE tenant; not valid with --all");
|
|
546
|
+
if (a.verifyFresh && !a.checkPinned) {
|
|
547
|
+
throw new Error("--verify-fresh is only valid with --check-pinned");
|
|
548
|
+
}
|
|
457
549
|
return a;
|
|
458
550
|
}
|
|
459
551
|
|
|
@@ -463,6 +555,8 @@ const USAGE = `materialize-tenant-content — populate tenants/<id>/ from its Gi
|
|
|
463
555
|
node scripts/tenant/materialize-tenant-content.mjs --all
|
|
464
556
|
node scripts/tenant/materialize-tenant-content.mjs --check
|
|
465
557
|
node scripts/tenant/materialize-tenant-content.mjs --check-pinned [--expect-ref <sha>] [--allow-unpinned]
|
|
558
|
+
node scripts/tenant/materialize-tenant-content.mjs --check-pinned --verify-fresh
|
|
559
|
+
# opt-in NETWORK addition: refuse if a movable-ref stamp's sha is behind Gitea's live tip
|
|
466
560
|
|
|
467
561
|
env: TENANT_GIT_BASE (default ${DEFAULT_GIT_BASE}), TENANT_GIT_USER, TENANT_GIT_TOKEN`;
|
|
468
562
|
|
|
@@ -486,15 +580,21 @@ function main() {
|
|
|
486
580
|
return;
|
|
487
581
|
}
|
|
488
582
|
if (args.checkPinned) {
|
|
489
|
-
const {
|
|
583
|
+
const { problems } = checkPinned(repoRoot, {
|
|
490
584
|
expectRef: args.expectRef,
|
|
491
585
|
allowUnpinned: args.allowUnpinned,
|
|
492
586
|
});
|
|
493
|
-
|
|
494
|
-
|
|
587
|
+
const freshProblems = args.verifyFresh ? checkFresh(repoRoot, { gitBase }).problems : [];
|
|
588
|
+
const allProblems = [...problems, ...freshProblems];
|
|
589
|
+
if (allProblems.length > 0) {
|
|
590
|
+
console.error(describePinnedProblems(allProblems, args.expectRef));
|
|
495
591
|
process.exit(1);
|
|
496
592
|
}
|
|
497
|
-
console.log(
|
|
593
|
+
console.log(
|
|
594
|
+
args.verifyFresh
|
|
595
|
+
? "All Gitea-classified tenants materialized from Gitea at a pinned ref, verified fresh against the live tip."
|
|
596
|
+
: "All Gitea-classified tenants materialized from Gitea at a pinned ref.",
|
|
597
|
+
);
|
|
498
598
|
return;
|
|
499
599
|
}
|
|
500
600
|
const manifest = loadManifest();
|
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
declare module 'astro:content' {
|
|
2
|
-
export interface RenderResult {
|
|
3
|
-
Content: import('astro/runtime/server/index.js').AstroComponentFactory;
|
|
4
|
-
headings: import('astro').MarkdownHeading[];
|
|
5
|
-
remarkPluginFrontmatter: Record<string, any>;
|
|
6
|
-
}
|
|
7
|
-
interface Render {
|
|
8
|
-
'.md': Promise<RenderResult>;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export interface RenderedContent {
|
|
12
|
-
html: string;
|
|
13
|
-
metadata?: {
|
|
14
|
-
imagePaths: Array<string>;
|
|
15
|
-
[key: string]: unknown;
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
type Flatten<T> = T extends { [K: string]: infer U } ? U : never;
|
|
20
|
-
|
|
21
|
-
export type CollectionKey = keyof DataEntryMap;
|
|
22
|
-
export type CollectionEntry<C extends CollectionKey> = Flatten<DataEntryMap[C]>;
|
|
23
|
-
|
|
24
|
-
type AllValuesOf<T> = T extends any ? T[keyof T] : never;
|
|
25
|
-
|
|
26
|
-
export type ReferenceDataEntry<
|
|
27
|
-
C extends CollectionKey,
|
|
28
|
-
E extends keyof DataEntryMap[C] = string,
|
|
29
|
-
> = {
|
|
30
|
-
collection: C;
|
|
31
|
-
id: E;
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
export type ReferenceLiveEntry<C extends keyof LiveContentConfig['collections']> = {
|
|
35
|
-
collection: C;
|
|
36
|
-
id: string;
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
export function getCollection<C extends keyof DataEntryMap, E extends CollectionEntry<C>>(
|
|
40
|
-
collection: C,
|
|
41
|
-
filter?: (entry: CollectionEntry<C>) => entry is E,
|
|
42
|
-
): Promise<E[]>;
|
|
43
|
-
export function getCollection<C extends keyof DataEntryMap>(
|
|
44
|
-
collection: C,
|
|
45
|
-
filter?: (entry: CollectionEntry<C>) => unknown,
|
|
46
|
-
): Promise<CollectionEntry<C>[]>;
|
|
47
|
-
|
|
48
|
-
export function getLiveCollection<C extends keyof LiveContentConfig['collections']>(
|
|
49
|
-
collection: C,
|
|
50
|
-
filter?: LiveLoaderCollectionFilterType<C>,
|
|
51
|
-
): Promise<
|
|
52
|
-
import('astro').LiveDataCollectionResult<LiveLoaderDataType<C>, LiveLoaderErrorType<C>>
|
|
53
|
-
>;
|
|
54
|
-
|
|
55
|
-
export function getEntry<
|
|
56
|
-
C extends keyof DataEntryMap,
|
|
57
|
-
E extends keyof DataEntryMap[C] | (string & {}),
|
|
58
|
-
>(
|
|
59
|
-
entry: ReferenceDataEntry<C, E>,
|
|
60
|
-
): E extends keyof DataEntryMap[C]
|
|
61
|
-
? Promise<DataEntryMap[C][E]>
|
|
62
|
-
: Promise<CollectionEntry<C> | undefined>;
|
|
63
|
-
export function getEntry<
|
|
64
|
-
C extends keyof DataEntryMap,
|
|
65
|
-
E extends keyof DataEntryMap[C] | (string & {}),
|
|
66
|
-
>(
|
|
67
|
-
collection: C,
|
|
68
|
-
id: E,
|
|
69
|
-
): E extends keyof DataEntryMap[C]
|
|
70
|
-
? string extends keyof DataEntryMap[C]
|
|
71
|
-
? Promise<DataEntryMap[C][E]> | undefined
|
|
72
|
-
: Promise<DataEntryMap[C][E]>
|
|
73
|
-
: Promise<CollectionEntry<C> | undefined>;
|
|
74
|
-
export function getLiveEntry<C extends keyof LiveContentConfig['collections']>(
|
|
75
|
-
collection: C,
|
|
76
|
-
filter: string | LiveLoaderEntryFilterType<C>,
|
|
77
|
-
): Promise<import('astro').LiveDataEntryResult<LiveLoaderDataType<C>, LiveLoaderErrorType<C>>>;
|
|
78
|
-
|
|
79
|
-
/** Resolve an array of entry references from the same collection */
|
|
80
|
-
export function getEntries<C extends keyof DataEntryMap>(
|
|
81
|
-
entries: ReferenceDataEntry<C, keyof DataEntryMap[C]>[],
|
|
82
|
-
): Promise<CollectionEntry<C>[]>;
|
|
83
|
-
|
|
84
|
-
export function render<C extends keyof DataEntryMap>(
|
|
85
|
-
entry: DataEntryMap[C][string],
|
|
86
|
-
): Promise<RenderResult>;
|
|
87
|
-
|
|
88
|
-
export function render<C extends keyof LiveContentConfig['collections']>(
|
|
89
|
-
entry: import('astro').LiveDataEntry<LiveLoaderDataType<C>>,
|
|
90
|
-
): Promise<RenderResult>;
|
|
91
|
-
|
|
92
|
-
export function reference<
|
|
93
|
-
C extends
|
|
94
|
-
| keyof DataEntryMap
|
|
95
|
-
// Allow generic `string` to avoid excessive type errors in the config
|
|
96
|
-
// if `dev` is not running to update as you edit.
|
|
97
|
-
// Invalid collection names will be caught at build time.
|
|
98
|
-
| (string & {}),
|
|
99
|
-
>(
|
|
100
|
-
collection: C,
|
|
101
|
-
): import('astro/zod').ZodPipe<
|
|
102
|
-
import('astro/zod').ZodString,
|
|
103
|
-
import('astro/zod').ZodTransform<
|
|
104
|
-
C extends keyof DataEntryMap
|
|
105
|
-
? {
|
|
106
|
-
collection: C;
|
|
107
|
-
id: string;
|
|
108
|
-
}
|
|
109
|
-
: never,
|
|
110
|
-
string
|
|
111
|
-
>
|
|
112
|
-
>;
|
|
113
|
-
|
|
114
|
-
type ReturnTypeOrOriginal<T> = T extends (...args: any[]) => infer R ? R : T;
|
|
115
|
-
type InferEntrySchema<C extends keyof DataEntryMap> = import('astro/zod').infer<
|
|
116
|
-
ReturnTypeOrOriginal<Required<ContentConfig['collections'][C]>['schema']>
|
|
117
|
-
>;
|
|
118
|
-
type ExtractLoaderConfig<T> = T extends { loader: infer L } ? L : never;
|
|
119
|
-
type InferLoaderSchema<
|
|
120
|
-
C extends keyof DataEntryMap,
|
|
121
|
-
L = ExtractLoaderConfig<ContentConfig['collections'][C]>,
|
|
122
|
-
> = L extends { schema: import('astro/zod').ZodSchema }
|
|
123
|
-
? import('astro/zod').infer<L['schema']>
|
|
124
|
-
: any;
|
|
125
|
-
|
|
126
|
-
type DataEntryMap = {
|
|
127
|
-
|
|
128
|
-
};
|
|
129
|
-
|
|
130
|
-
type ExtractLoaderTypes<T> = T extends import('astro/loaders').LiveLoader<
|
|
131
|
-
infer TData,
|
|
132
|
-
infer TEntryFilter,
|
|
133
|
-
infer TCollectionFilter,
|
|
134
|
-
infer TError
|
|
135
|
-
>
|
|
136
|
-
? { data: TData; entryFilter: TEntryFilter; collectionFilter: TCollectionFilter; error: TError }
|
|
137
|
-
: { data: never; entryFilter: never; collectionFilter: never; error: never };
|
|
138
|
-
type ExtractEntryFilterType<T> = ExtractLoaderTypes<T>['entryFilter'];
|
|
139
|
-
type ExtractCollectionFilterType<T> = ExtractLoaderTypes<T>['collectionFilter'];
|
|
140
|
-
type ExtractErrorType<T> = ExtractLoaderTypes<T>['error'];
|
|
141
|
-
type ExtractDataType<T> = ExtractLoaderTypes<T>['data'];
|
|
142
|
-
|
|
143
|
-
type LiveLoaderDataType<C extends keyof LiveContentConfig['collections']> =
|
|
144
|
-
LiveContentConfig['collections'][C]['schema'] extends undefined
|
|
145
|
-
? ExtractDataType<LiveContentConfig['collections'][C]['loader']>
|
|
146
|
-
: import('astro/zod').infer<
|
|
147
|
-
Exclude<LiveContentConfig['collections'][C]['schema'], undefined>
|
|
148
|
-
>;
|
|
149
|
-
type LiveLoaderEntryFilterType<C extends keyof LiveContentConfig['collections']> =
|
|
150
|
-
ExtractEntryFilterType<LiveContentConfig['collections'][C]['loader']>;
|
|
151
|
-
type LiveLoaderCollectionFilterType<C extends keyof LiveContentConfig['collections']> =
|
|
152
|
-
ExtractCollectionFilterType<LiveContentConfig['collections'][C]['loader']>;
|
|
153
|
-
type LiveLoaderErrorType<C extends keyof LiveContentConfig['collections']> = ExtractErrorType<
|
|
154
|
-
LiveContentConfig['collections'][C]['loader']
|
|
155
|
-
>;
|
|
156
|
-
|
|
157
|
-
export type ContentConfig = never;
|
|
158
|
-
export type LiveContentConfig = never;
|
|
159
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
/// <reference types="@astrojs/cloudflare/types.d.ts" />
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
#!/bin/sh
|
|
2
|
-
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
|
3
|
-
basedir_win="$basedir"
|
|
4
|
-
exe=""
|
|
5
|
-
msys=""
|
|
6
|
-
|
|
7
|
-
case `uname -a` in
|
|
8
|
-
*CYGWIN*|*MINGW*|*MSYS*)
|
|
9
|
-
if command -v cygpath > /dev/null 2>&1; then
|
|
10
|
-
basedir_win=`cygpath -w "$basedir"`
|
|
11
|
-
fi
|
|
12
|
-
exe=".exe"
|
|
13
|
-
msys="true"
|
|
14
|
-
;;
|
|
15
|
-
*WSL2*)
|
|
16
|
-
if command -v wslpath > /dev/null 2>&1; then
|
|
17
|
-
basedir_win="$(wslpath -w "$basedir" 2> /dev/null)"
|
|
18
|
-
if [ $? -ne 0 ] || [ -z "$basedir_win" ]; then
|
|
19
|
-
basedir_win="$basedir"
|
|
20
|
-
else
|
|
21
|
-
exe=".exe"
|
|
22
|
-
fi
|
|
23
|
-
fi
|
|
24
|
-
;;
|
|
25
|
-
esac
|
|
26
|
-
|
|
27
|
-
if [ -z "$NODE_PATH" ]; then
|
|
28
|
-
export NODE_PATH="/home/runner/work/storefront/storefront/storefront-runner-dist/node_modules/.pnpm/acorn@8.18.0/node_modules/acorn/node_modules:/home/runner/work/storefront/storefront/storefront-runner-dist/node_modules/.pnpm/acorn@8.18.0/node_modules:/home/runner/work/storefront/storefront/storefront-runner-dist/node_modules/.pnpm/node_modules"
|
|
29
|
-
else
|
|
30
|
-
export NODE_PATH="/home/runner/work/storefront/storefront/storefront-runner-dist/node_modules/.pnpm/acorn@8.18.0/node_modules/acorn/node_modules:/home/runner/work/storefront/storefront/storefront-runner-dist/node_modules/.pnpm/acorn@8.18.0/node_modules:/home/runner/work/storefront/storefront/storefront-runner-dist/node_modules/.pnpm/node_modules:$NODE_PATH"
|
|
31
|
-
fi
|
|
32
|
-
if [ -n "$exe" ] && [ -x "$basedir/node.exe" ]; then
|
|
33
|
-
exec "$basedir/node.exe" "$basedir_win/../../../../node_modules/.pnpm/acorn@8.18.0/node_modules/acorn/bin/acorn" "$@"
|
|
34
|
-
elif [ -x "$basedir/node" ]; then
|
|
35
|
-
exec "$basedir/node" "$basedir/../../../../node_modules/.pnpm/acorn@8.18.0/node_modules/acorn/bin/acorn" "$@"
|
|
36
|
-
elif command -v node >/dev/null 2>&1; then
|
|
37
|
-
exec node "$basedir/../../../../node_modules/.pnpm/acorn@8.18.0/node_modules/acorn/bin/acorn" "$@"
|
|
38
|
-
elif [ -n "$exe" ] && command -v node.exe >/dev/null 2>&1; then
|
|
39
|
-
exec node.exe "$basedir_win/../../../../node_modules/.pnpm/acorn@8.18.0/node_modules/acorn/bin/acorn" "$@"
|
|
40
|
-
else
|
|
41
|
-
exec node "$basedir/../../../../node_modules/.pnpm/acorn@8.18.0/node_modules/acorn/bin/acorn" "$@"
|
|
42
|
-
fi
|
|
43
|
-
# cmd-shim-target=/home/runner/work/storefront/storefront/storefront-runner-dist/node_modules/.pnpm/acorn@8.18.0/node_modules/acorn/bin/acorn
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
#!/bin/sh
|
|
2
|
-
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
|
3
|
-
basedir_win="$basedir"
|
|
4
|
-
exe=""
|
|
5
|
-
msys=""
|
|
6
|
-
|
|
7
|
-
case `uname -a` in
|
|
8
|
-
*CYGWIN*|*MINGW*|*MSYS*)
|
|
9
|
-
if command -v cygpath > /dev/null 2>&1; then
|
|
10
|
-
basedir_win=`cygpath -w "$basedir"`
|
|
11
|
-
fi
|
|
12
|
-
exe=".exe"
|
|
13
|
-
msys="true"
|
|
14
|
-
;;
|
|
15
|
-
*WSL2*)
|
|
16
|
-
if command -v wslpath > /dev/null 2>&1; then
|
|
17
|
-
basedir_win="$(wslpath -w "$basedir" 2> /dev/null)"
|
|
18
|
-
if [ $? -ne 0 ] || [ -z "$basedir_win" ]; then
|
|
19
|
-
basedir_win="$basedir"
|
|
20
|
-
else
|
|
21
|
-
exe=".exe"
|
|
22
|
-
fi
|
|
23
|
-
fi
|
|
24
|
-
;;
|
|
25
|
-
esac
|
|
26
|
-
|
|
27
|
-
if [ -z "$NODE_PATH" ]; then
|
|
28
|
-
export NODE_PATH="/home/runner/work/storefront/storefront/storefront-runner-dist/node_modules/.pnpm/astro@7.2.10_@emnapi+core@1.11.1_@emnapi+runtime@1.11.3_@types+node@26.4.1_jiti@2.7.0_tsx@4.23.13_yaml@2.9.0/node_modules/astro/node_modules:/home/runner/work/storefront/storefront/storefront-runner-dist/node_modules/.pnpm/astro@7.2.10_@emnapi+core@1.11.1_@emnapi+runtime@1.11.3_@types+node@26.4.1_jiti@2.7.0_tsx@4.23.13_yaml@2.9.0/node_modules:/home/runner/work/storefront/storefront/storefront-runner-dist/node_modules/.pnpm/node_modules"
|
|
29
|
-
else
|
|
30
|
-
export NODE_PATH="/home/runner/work/storefront/storefront/storefront-runner-dist/node_modules/.pnpm/astro@7.2.10_@emnapi+core@1.11.1_@emnapi+runtime@1.11.3_@types+node@26.4.1_jiti@2.7.0_tsx@4.23.13_yaml@2.9.0/node_modules/astro/node_modules:/home/runner/work/storefront/storefront/storefront-runner-dist/node_modules/.pnpm/astro@7.2.10_@emnapi+core@1.11.1_@emnapi+runtime@1.11.3_@types+node@26.4.1_jiti@2.7.0_tsx@4.23.13_yaml@2.9.0/node_modules:/home/runner/work/storefront/storefront/storefront-runner-dist/node_modules/.pnpm/node_modules:$NODE_PATH"
|
|
31
|
-
fi
|
|
32
|
-
if [ -n "$exe" ] && [ -x "$basedir/node.exe" ]; then
|
|
33
|
-
exec "$basedir/node.exe" "$basedir_win/../astro/bin/astro.mjs" "$@"
|
|
34
|
-
elif [ -x "$basedir/node" ]; then
|
|
35
|
-
exec "$basedir/node" "$basedir/../astro/bin/astro.mjs" "$@"
|
|
36
|
-
elif command -v node >/dev/null 2>&1; then
|
|
37
|
-
exec node "$basedir/../astro/bin/astro.mjs" "$@"
|
|
38
|
-
elif [ -n "$exe" ] && command -v node.exe >/dev/null 2>&1; then
|
|
39
|
-
exec node.exe "$basedir_win/../astro/bin/astro.mjs" "$@"
|
|
40
|
-
else
|
|
41
|
-
exec node "$basedir/../astro/bin/astro.mjs" "$@"
|
|
42
|
-
fi
|
|
43
|
-
# cmd-shim-target=/home/runner/work/storefront/storefront/storefront-runner-dist/apps/storefront/node_modules/astro/bin/astro.mjs
|