@tokenoftrust/storefront-runner 1.4.0 → 1.4.2-rc.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/apps/storefront/astro.config.mjs +17 -1
- package/apps/storefront/integrations/materialize-guard.mjs +56 -0
- package/apps/storefront/migrations-apps/0001_woozy_lyja.sql +22 -0
- package/apps/storefront/migrations-apps/0002_good_switch.sql +26 -0
- package/apps/storefront/migrations-apps/0003_mute_marauders.sql +12 -0
- package/apps/storefront/migrations-apps/meta/0001_snapshot.json +990 -0
- package/apps/storefront/migrations-apps/meta/0002_snapshot.json +1160 -0
- package/apps/storefront/migrations-apps/meta/0003_snapshot.json +1235 -0
- package/apps/storefront/migrations-apps/meta/_journal.json +21 -0
- package/apps/storefront/package.json +0 -1
- package/apps/storefront/public/js/dashboard-team.js +38 -2
- package/apps/storefront/src/components/Seo.astro +65 -1
- package/apps/storefront/src/components/admin/AdminBlogDetailRail.astro +878 -0
- package/apps/storefront/src/components/admin/AdminBlogTab.astro +1432 -0
- package/apps/storefront/src/components/admin/AdminCustomersTab.astro +3 -7
- package/apps/storefront/src/components/admin/AdminPublishTab.astro +1756 -1382
- package/apps/storefront/src/components/admin/AdminSubscriptionsTab.astro +1 -3
- package/apps/storefront/src/components/blog/BlogPaywall.astro +87 -0
- package/apps/storefront/src/components/blog/PostAccessBadge.astro +43 -0
- package/apps/storefront/src/components/blog/PostGrid.astro +57 -0
- package/apps/storefront/src/components/blog/ReactionBar.astro +214 -0
- package/apps/storefront/src/layouts/Layout.astro +48 -3
- package/apps/storefront/src/lib/activity/alerts.ts +426 -0
- package/apps/storefront/src/lib/activity/changeActorAttribution.ts +85 -0
- package/apps/storefront/src/lib/activity/deployVersion.ts +120 -0
- package/apps/storefront/src/lib/activity/ingest.ts +188 -0
- package/apps/storefront/src/lib/activity/ingestAuth.ts +105 -0
- package/apps/storefront/src/lib/activity/killSwitch.ts +80 -0
- package/apps/storefront/src/lib/activity/query.ts +403 -0
- package/apps/storefront/src/lib/activity/recordActivity.ts +101 -0
- package/apps/storefront/src/lib/activity/store.ts +120 -0
- package/apps/storefront/src/lib/activity/uiActor.ts +150 -0
- package/apps/storefront/src/lib/activity/workerCommit.ts +70 -0
- package/apps/storefront/src/lib/admin/adminShell.ts +290 -0
- package/apps/storefront/src/lib/admin/ai/approvalMode.ts +2 -3
- package/apps/storefront/src/lib/admin/ai/audit.ts +3 -5
- package/apps/storefront/src/lib/admin/checkoutStyleInput.ts +7 -7
- package/apps/storefront/src/lib/admin/checkoutStyleTarget.ts +3 -3
- package/apps/storefront/src/lib/admin/orderClient.ts +3 -5
- package/apps/storefront/src/lib/admin/services/orders.ts +17 -19
- package/apps/storefront/src/lib/admin/viewmodels/aiWorkflows.ts +4 -6
- package/apps/storefront/src/lib/admin/viewmodels/settings.ts +1 -1
- package/apps/storefront/src/lib/apps/adminService.ts +4 -4
- package/apps/storefront/src/lib/apps/adminSession.ts +53 -44
- package/apps/storefront/src/lib/apps/apiAuth.ts +2 -2
- package/apps/storefront/src/lib/apps/apiRoute.ts +2 -2
- package/apps/storefront/src/lib/apps/catalogMapper.ts +1 -1
- package/apps/storefront/src/lib/apps/credentials.ts +2 -2
- package/apps/storefront/src/lib/apps/gatewayKeys.ts +4 -4
- package/apps/storefront/src/lib/apps/healthAggregate.ts +5 -5
- package/apps/storefront/src/lib/apps/orders/attributionService.ts +2 -2
- package/apps/storefront/src/lib/apps/orders/customerHash.ts +1 -1
- package/apps/storefront/src/lib/apps/orders/foxyOrderClient.ts +12 -12
- package/apps/storefront/src/lib/apps/orders/idempotency.ts +7 -7
- package/apps/storefront/src/lib/apps/orders/orderForwardReceiver.ts +1 -1
- package/apps/storefront/src/lib/apps/orders/orderMapper.ts +2 -2
- package/apps/storefront/src/lib/apps/orders/ordersStore.ts +6 -6
- package/apps/storefront/src/lib/apps/registryService.ts +6 -6
- package/apps/storefront/src/lib/apps/scopes.ts +2 -2
- package/apps/storefront/src/lib/apps/tokenIssuer.ts +4 -4
- package/apps/storefront/src/lib/apps/tokenVerifier.ts +6 -6
- package/apps/storefront/src/lib/apps/widgets/eligibility.ts +2 -2
- package/apps/storefront/src/lib/apps/widgets/frameProps.ts +3 -3
- package/apps/storefront/src/lib/apps/widgets/launchToken.ts +4 -4
- package/apps/storefront/src/lib/apps/widgets/placements.ts +2 -2
- package/apps/storefront/src/lib/apps/widgets/renderSlot.ts +1 -1
- package/apps/storefront/src/lib/auth/adminEntry.ts +9 -9
- package/apps/storefront/src/lib/auth/identityToken.ts +6 -14
- package/apps/storefront/src/lib/auth/loginGate.ts +75 -13
- package/apps/storefront/src/lib/auth/mcpClientAssertion.ts +6 -4
- package/apps/storefront/src/lib/auth/operatorSession.ts +113 -0
- package/apps/storefront/src/lib/auth/route.ts +44 -4
- package/apps/storefront/src/lib/auth/session.ts +7 -8
- package/apps/storefront/src/lib/auth/totAccessClient.ts +8 -18
- package/apps/storefront/src/lib/basePath.ts +9 -2
- package/apps/storefront/src/lib/blog/access.ts +29 -0
- package/apps/storefront/src/lib/blog/collection.ts +313 -0
- package/apps/storefront/src/lib/blog/markdown.ts +174 -0
- package/apps/storefront/src/lib/blog/provider.ts +5 -0
- package/apps/storefront/src/lib/blog/reactions.ts +87 -0
- package/apps/storefront/src/lib/blog/rss.ts +92 -0
- package/apps/storefront/src/lib/blog/teaser.ts +83 -0
- package/apps/storefront/src/lib/blog/types.ts +34 -0
- package/apps/storefront/src/lib/blog/viewCounts.ts +243 -0
- package/apps/storefront/src/lib/d1/catalog.ts +2 -2
- package/apps/storefront/src/lib/d1/schema-apps.ts +147 -14
- package/apps/storefront/src/lib/dev/activityIngestToken.ts +2 -4
- package/apps/storefront/src/lib/dev/activityStore.ts +3 -5
- package/apps/storefront/src/lib/dev/apiBase.ts +1 -4
- package/apps/storefront/src/lib/dev/cockpitStore.ts +1 -9
- package/apps/storefront/src/lib/dev/devDraft.ts +1 -1
- package/apps/storefront/src/lib/dev/previewStatus.ts +9 -21
- package/apps/storefront/src/lib/dev/rendezvousBroker.ts +9 -24
- package/apps/storefront/src/lib/edgeCache.ts +17 -0
- package/apps/storefront/src/lib/env.ts +51 -0
- package/apps/storefront/src/lib/http/fetchTransport.ts +63 -0
- package/apps/storefront/src/lib/i18n.ts +25 -0
- package/apps/storefront/src/lib/jsonld.ts +118 -0
- package/apps/storefront/src/lib/monitoring/manifest.ts +11 -11
- package/apps/storefront/src/lib/newsletter/confirmationEmail.ts +30 -0
- package/apps/storefront/src/lib/newsletter/consent.ts +23 -0
- package/apps/storefront/src/lib/newsletter/digest.ts +117 -0
- package/apps/storefront/src/lib/newsletter/resendClient.ts +79 -0
- package/apps/storefront/src/lib/newsletter/send.ts +265 -0
- package/apps/storefront/src/lib/newsletter/subscribers.ts +156 -0
- package/apps/storefront/src/lib/publish/adminPublishTab.ts +3519 -0
- package/apps/storefront/src/lib/publish/apex-readiness.ts +41 -18
- package/apps/storefront/src/lib/publish/dispatchHealth.ts +5 -7
- package/apps/storefront/src/lib/publish/domainState.ts +22 -13
- package/apps/storefront/src/lib/publish/panelPolling.ts +78 -0
- package/apps/storefront/src/lib/publish/shipWorkspace.ts +100 -42
- package/apps/storefront/src/lib/publish/stageBundleUpload.ts +215 -0
- package/apps/storefront/src/lib/rate/kvWindow.ts +53 -0
- package/apps/storefront/src/lib/search/index.ts +2 -2
- package/apps/storefront/src/lib/seo/alternates.ts +42 -0
- package/apps/storefront/src/lib/seo/meta.ts +65 -0
- package/apps/storefront/src/lib/social/golive.ts +212 -0
- package/apps/storefront/src/lib/social/notify.ts +32 -0
- package/apps/storefront/src/lib/social/tokenStore.ts +171 -0
- package/apps/storefront/src/lib/the-build/provider.ts +27 -0
- package/apps/storefront/src/lib/the-build/readingTime.ts +35 -0
- package/apps/storefront/src/lib/the-build/toc.ts +95 -0
- package/apps/storefront/src/lib/tot/ToTClient.ts +1 -1
- package/apps/storefront/src/lib/tot/d1Client.ts +2 -2
- package/apps/storefront/src/lib/tot/totClientInterface.ts +3 -4
- package/apps/storefront/src/lib/webhooks/deliveryStore.ts +16 -17
- package/apps/storefront/src/middleware/index.ts +130 -2
- package/apps/storefront/src/pages/admin/ops-timeline.astro +420 -0
- package/apps/storefront/src/pages/admin.astro +52 -1206
- package/apps/storefront/src/pages/api/activity.ts +137 -0
- package/apps/storefront/src/pages/api/admin/activity-alerts.ts +73 -0
- package/apps/storefront/src/pages/api/apps/admin/credentials/rotate.ts +27 -1
- package/apps/storefront/src/pages/api/apps/admin/install.ts +26 -1
- package/apps/storefront/src/pages/api/apps/admin/resume.ts +24 -1
- package/apps/storefront/src/pages/api/apps/admin/suspend.ts +24 -1
- package/apps/storefront/src/pages/api/apps/admin/uninstall.ts +25 -1
- package/apps/storefront/src/pages/api/apps/admin/update.ts +24 -1
- package/apps/storefront/src/pages/api/apps/admin/webhooks/deliveries/[deliveryId]/replay.ts +11 -0
- package/apps/storefront/src/pages/api/apps/internal/order-forward.ts +11 -0
- package/apps/storefront/src/pages/api/apps/v1/attribution.ts +20 -0
- package/apps/storefront/src/pages/api/apps/v1/webhooks/deliveries/[deliveryId]/replay.ts +12 -0
- package/apps/storefront/src/pages/api/blog/[collection]/[slug]/react.ts +104 -0
- package/apps/storefront/src/pages/api/cache-purge.ts +11 -0
- package/apps/storefront/src/pages/api/dashboard/enter-vendor.ts +30 -0
- package/apps/storefront/src/pages/api/newsletter/[collection]/subscribe.ts +146 -0
- package/apps/storefront/src/pages/api/newsletter/confirm.ts +75 -0
- package/apps/storefront/src/pages/api/newsletter/unsubscribe.ts +90 -0
- package/apps/storefront/src/pages/api/publish/stage-bundle.ts +70 -0
- package/apps/storefront/src/pages/api/rum/vitals.ts +21 -2
- package/apps/storefront/src/pages/auth/login.astro +5 -4
- package/apps/storefront/src/pages/blog/[slug].astro +96 -16
- package/apps/storefront/src/pages/blog/author/[author].astro +140 -0
- package/apps/storefront/src/pages/blog/category/[category].astro +67 -0
- package/apps/storefront/src/pages/blog/index.astro +2 -0
- package/apps/storefront/src/pages/blog/tag/[tag].astro +65 -0
- package/apps/storefront/src/pages/cockpit.astro +74 -12
- package/apps/storefront/src/pages/dashboard/[appDomain]/team.astro +15 -11
- package/apps/storefront/src/pages/search.astro +1 -1
- package/apps/storefront/src/pages/sitemap.xml.ts +20 -0
- package/apps/storefront/src/pages/the-build/[slug].astro +129 -0
- package/apps/storefront/src/pages/the-build/index.astro +146 -0
- package/apps/storefront/src/pages/the-build/rss.xml.ts +33 -0
- package/apps/storefront/src/styles/admin.css +1080 -0
- package/apps/storefront/tsconfig.json +5 -0
- package/package.json +1 -1
- package/packages/public-runtime/package.json +1 -0
- package/packages/public-runtime/src/activity/README.md +146 -0
- package/packages/public-runtime/src/activity/catalog.ts +553 -0
- package/packages/public-runtime/src/activity/event.ts +168 -0
- package/packages/public-runtime/src/activity/index.ts +16 -0
- package/packages/public-runtime/src/activity/redaction.ts +263 -0
- package/packages/public-runtime/src/candidate-index.ts +13 -0
- package/packages/public-runtime/src/catalog-d1.ts +1 -1
- package/packages/public-runtime/src/customization-preview.ts +25 -2
- package/packages/public-runtime/src/customization-runtime.ts +125 -3
- package/packages/public-runtime/src/customization-versioning.ts +10 -0
- package/packages/public-runtime/src/index.ts +7 -0
- package/packages/public-runtime/src/static-bundle.ts +357 -0
- package/packages/public-runtime/src/tenant.ts +36 -8
- package/scripts/build/copy-tenant-assets.mjs +14 -4
- package/scripts/dev/ai-edit.mjs +1 -1
- package/scripts/dev/checkout-watch.mjs +2 -2
- package/scripts/dev/file-browser.mjs +2 -4
- package/scripts/dev/file-writer.mjs +1 -1
- package/scripts/dev/git-status.mjs +2 -2
- package/scripts/dev/locate-handler.mjs +3 -3
- package/scripts/dev/port-check.mjs +3 -3
- package/scripts/dev/publish.mjs +5 -9
- package/scripts/dev/shot.mjs +1 -1
- package/scripts/dev/unified-diff.mjs +8 -3
- package/scripts/tot-dev.mjs +6 -9
- package/apps/storefront/src/lib/webhooks/signing.ts +0 -29
- package/apps/storefront/src/lib/webhooks/webhookSigningKey.ts +0 -146
|
@@ -5,13 +5,11 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Follows the scripts/dev delegation pattern (see ai-edit.mjs): all logic lives
|
|
7
7
|
* here, dependency-free and testable under `node --test`; a thin dev-only Vite
|
|
8
|
-
* plugin (dev-plugins/dev-file-browser.mjs
|
|
8
|
+
* plugin (dev-plugins/dev-file-browser.mjs) maps
|
|
9
9
|
* req/res onto `handleTree` / `handleFile`. Every path is routed through
|
|
10
10
|
* safe-path.mjs; the source root comes from tenant-source-root.mjs (never SSR).
|
|
11
11
|
*
|
|
12
|
-
*
|
|
13
|
-
* the tree here carries structure only, so this module owns no git surface and
|
|
14
|
-
* doesn't collide with C4.
|
|
12
|
+
* The tree here carries structure only, so this module owns no git surface.
|
|
15
13
|
*/
|
|
16
14
|
import { readFileSync, readdirSync, statSync } from "node:fs";
|
|
17
15
|
import { join, extname, sep } from "node:path";
|
|
@@ -123,7 +123,7 @@ export function handleWrite(env = process.env, body = {}) {
|
|
|
123
123
|
return { status: 400, json: { ok: false, status: "no_path", message: "path is required" } };
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
-
const result = writeSourceFile(root.sourceRoot, relPath, body.content, {
|
|
126
|
+
const result = writeSourceFile(root.sourceRoot, relPath, /** @type {string} */ (body.content), {
|
|
127
127
|
baseVersion: body ? body.baseVersion : undefined,
|
|
128
128
|
});
|
|
129
129
|
const status = result.ok
|
|
@@ -118,10 +118,10 @@ export function computeDiff(gitCwd, sourceRoot, relPath) {
|
|
|
118
118
|
} catch {
|
|
119
119
|
workingExists = false;
|
|
120
120
|
}
|
|
121
|
-
if (
|
|
121
|
+
if (st && st.isDirectory()) {
|
|
122
122
|
return { ok: false, status: "is_dir", message: `${relPath} is a directory` };
|
|
123
123
|
}
|
|
124
|
-
if (workingExists) {
|
|
124
|
+
if (workingExists && st) {
|
|
125
125
|
if (BINARY_EXT.has(extname(posix).toLowerCase())) {
|
|
126
126
|
return { ok: true, path: posix, status: fileStatus(gitCwd, relPath), shown: false, reason: "binary" };
|
|
127
127
|
}
|
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
* POST /__tot/locate {domPath, path} → {ok, path, line, col} | {status}
|
|
4
4
|
* POST /__tot/open-in-editor {path, line, col} → {ok} | {status}
|
|
5
5
|
*
|
|
6
|
-
* Keeps locate.mjs a PURE parse5 walk
|
|
6
|
+
* Keeps locate.mjs a PURE parse5 walk; this module
|
|
7
7
|
* adds the env/path-resolution/refusal concerns and is itself unit-testable under
|
|
8
8
|
* `node --test` with no running dev server (the scripts/dev delegation pattern — see
|
|
9
9
|
* file-browser.mjs). The thin dev-plugins/dev-locate.mjs wrapper maps req/res onto it.
|
|
10
10
|
*
|
|
11
|
-
*
|
|
11
|
+
* The served page wraps
|
|
12
12
|
* the raw content region (<main>, the whole of content/home.html) in the storefront's
|
|
13
13
|
* own chrome (skip-link, header, nav, footer) that is NOT in the tenant source. So we
|
|
14
14
|
* resolve the clicked URL to its raw content source and walk THAT from <body>:
|
|
@@ -90,7 +90,7 @@ export function handleLocate(env = process.env, body = {}) {
|
|
|
90
90
|
|
|
91
91
|
const html = readFileSync(abs, "utf8");
|
|
92
92
|
const found = locate(html, body.domPath);
|
|
93
|
-
if (typeof found.line === "number") {
|
|
93
|
+
if ("line" in found && typeof found.line === "number") {
|
|
94
94
|
return { status: 200, json: { ok: true, path: rel, line: found.line, col: found.col } };
|
|
95
95
|
}
|
|
96
96
|
// The walk missed: the element is outside the raw content region — i.e. the storefront
|
|
@@ -8,9 +8,9 @@ import net from "node:net";
|
|
|
8
8
|
* predicts astro's own bind on either IP family.
|
|
9
9
|
*
|
|
10
10
|
* WHY THIS EXISTS: Vite's `strictPort` defaults OFF, so when the requested port is
|
|
11
|
-
* taken astro SILENTLY moves to the next free port
|
|
12
|
-
*
|
|
13
|
-
*
|
|
11
|
+
* taken astro SILENTLY moves to the next free port — stranding the CLI's liveness
|
|
12
|
+
* poll on the ORIGINAL port (answered by a stale foreign server) → a false
|
|
13
|
+
* "✨ You're live" pointing at the wrong server. Pre-flighting the port and
|
|
14
14
|
* failing clearly removes that whole class of silent failure.
|
|
15
15
|
*
|
|
16
16
|
* Dependency-free (node:net only) so it copies into the standalone runner and is
|
package/scripts/dev/publish.mjs
CHANGED
|
@@ -20,15 +20,11 @@
|
|
|
20
20
|
* Auth: the developer's own cached OAuth session (`tot login`,
|
|
21
21
|
* `~/.tot/credentials.json`), via `packages/cli/src/auth.mjs`'s
|
|
22
22
|
* `resolveDeveloperSession` — reads, silently refreshes, and attaches the
|
|
23
|
-
* Bearer exactly as `tot submit` does.
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* OAuth bearer just to complete `initialize`. There is no service-account /
|
|
29
|
-
* client-credentials grant on this MCP (confirmed against its OAuth
|
|
30
|
-
* authorization-server metadata), so a human `tot login` (one-time; refresh
|
|
31
|
-
* is silent afterward, ~30-day refresh tokens) is the only way in.
|
|
23
|
+
* Bearer exactly as `tot submit` does. Every caller needs a real OAuth bearer
|
|
24
|
+
* just to complete `initialize`; there is no service-account / client-credentials
|
|
25
|
+
* grant on this MCP (confirmed against its OAuth authorization-server metadata),
|
|
26
|
+
* so a human `tot login` (one-time; refresh is silent afterward, ~30-day refresh
|
|
27
|
+
* tokens) is the only way in.
|
|
32
28
|
*/
|
|
33
29
|
import { createHash } from "node:crypto";
|
|
34
30
|
import { readFile } from "./file-browser.mjs";
|
package/scripts/dev/shot.mjs
CHANGED
|
@@ -20,7 +20,7 @@ const CHROME_PATHS = [
|
|
|
20
20
|
"/usr/bin/chromium-browser",
|
|
21
21
|
"/usr/bin/chromium",
|
|
22
22
|
].filter(Boolean);
|
|
23
|
-
const executablePath = CHROME_PATHS.find((p) => existsSync(p));
|
|
23
|
+
const executablePath = CHROME_PATHS.find((p) => !!p && existsSync(p));
|
|
24
24
|
|
|
25
25
|
const args = process.argv.slice(2);
|
|
26
26
|
const url = args[0];
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Minimal unified-diff applier for the inline-AI edit loop (WS5 G1).
|
|
3
3
|
*
|
|
4
|
-
* The MCP `content_edit` tool returns a per-file unified diff (
|
|
5
|
-
*
|
|
4
|
+
* The MCP `content_edit` tool returns a per-file unified diff (a `---`/`+++`
|
|
5
|
+
* file header, then hunk headers of the form "-l,c +l,c") rather than a full-file
|
|
6
6
|
* rewrite — this is what turns that diff back into the file's new content, so
|
|
7
7
|
* `tot dev` can apply it, hot-reload, and undo it cleanly. Dependency-free
|
|
8
8
|
* (matches the rest of the tenant tooling — see packages/cli/src/mcp.mjs).
|
|
@@ -12,10 +12,15 @@
|
|
|
12
12
|
* that's exactly the shape content_edit's system prompt asks the model for.
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
/**
|
|
15
|
+
/**
|
|
16
|
+
* One hunk (its "-l,c +l,c" header): the old/new starting line + its body lines.
|
|
17
|
+
* @typedef {{ oldStart: number, body: string[] }} Hunk
|
|
18
|
+
*/
|
|
16
19
|
function parseHunks(diff) {
|
|
17
20
|
const lines = diff.split(/\r?\n/);
|
|
21
|
+
/** @type {Hunk[]} */
|
|
18
22
|
const hunks = [];
|
|
23
|
+
/** @type {Hunk | null} */
|
|
19
24
|
let current = null;
|
|
20
25
|
const hunkHeader = /^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/;
|
|
21
26
|
|
package/scripts/tot-dev.mjs
CHANGED
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
* Vite trusts) and run a recursive `fs.watch` on the checkout that re-syncs each
|
|
32
32
|
* changed file back into the in-tree copy on save — which is what fires Vite HMR.
|
|
33
33
|
*
|
|
34
|
-
* Two things
|
|
34
|
+
* Two things to know (see the two-liners below):
|
|
35
35
|
* - Tenant is selected by the URL path-prefix (`/<appDomain>/`), NOT by
|
|
36
36
|
* DEFAULT_TENANT_ID — the @astrojs/cloudflare dev runtime (workerd) reads env
|
|
37
37
|
* from .dev.vars/bindings, so a spawned process.env var never reaches it, and
|
|
@@ -76,9 +76,8 @@ const flag = (name, def) => {
|
|
|
76
76
|
|
|
77
77
|
// `--version` / `-v` / `version` → print the runner's PUBLISHED version and exit.
|
|
78
78
|
// Reads the emitted root package.json (build-runner.mjs stamps RUNNER_VERSION in
|
|
79
|
-
// PUBLISH mode)
|
|
80
|
-
// runner
|
|
81
|
-
// so making it directly checkable (npm bin `storefront-runner`) is part of the fix.
|
|
79
|
+
// PUBLISH mode), so the runner's version is directly checkable (npm bin
|
|
80
|
+
// `storefront-runner`).
|
|
82
81
|
if (argv[0] === "--version" || argv[0] === "-v" || argv[0] === "version") {
|
|
83
82
|
let v = "0.0.0";
|
|
84
83
|
try {
|
|
@@ -165,7 +164,7 @@ if (!domain) {
|
|
|
165
164
|
// Records let us undo everything on exit (leave the tree exactly as found).
|
|
166
165
|
/** @type {{ target: string, backup: string|null }[]} */
|
|
167
166
|
const grafts = [];
|
|
168
|
-
/** @type {
|
|
167
|
+
/** @type {Array<{ close: () => void }>} */
|
|
169
168
|
const watchers = [];
|
|
170
169
|
let cleanedUp = false;
|
|
171
170
|
|
|
@@ -295,8 +294,7 @@ if (workspacePath) {
|
|
|
295
294
|
|
|
296
295
|
// Resolve the port BEFORE printing a URL or booting astro. Vite's `strictPort`
|
|
297
296
|
// defaults off, so a taken port makes astro SILENTLY drift to the next free one —
|
|
298
|
-
// which then mismatches the URL we (and the CLI's liveness poll) advertise
|
|
299
|
-
// stale server on the original port produced a false "You're live" (2026-07-14).
|
|
297
|
+
// which then mismatches the URL we (and the CLI's liveness poll) advertise.
|
|
300
298
|
// Instead we pick the first free port ourselves and PRINT it, so a busy default
|
|
301
299
|
// port "just works" (better UX than failing). See port-check.mjs.
|
|
302
300
|
const requestedPort = Number(flag("port", "4321"));
|
|
@@ -362,8 +360,7 @@ const spawnEnv = workspacePath
|
|
|
362
360
|
// require pnpm at RUNTIME), so resolve astro's real entry through Node's own
|
|
363
361
|
// resolver from the app package and exec it with our Node. No package-manager
|
|
364
362
|
// binary, no .bin shims, and no coupling to the package's name (the build
|
|
365
|
-
// renames @tot/storefront → @tot/storefront-runner in the shipped tree
|
|
366
|
-
// the old `pnpm --filter <name> exec` had to track).
|
|
363
|
+
// renames @tot/storefront → @tot/storefront-runner in the shipped tree).
|
|
367
364
|
const appDir = resolve(repoRoot, "apps/storefront");
|
|
368
365
|
const astroEntry = resolve(
|
|
369
366
|
dirname(createRequire(join(appDir, "package.json")).resolve("astro/package.json")),
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* HTTP Message Signatures (RFC 9421) + Content-Digest (RFC 9530) signer for
|
|
3
|
-
* outbound webhook delivery (PrivateApps epic, D4 Chunk B).
|
|
4
|
-
*
|
|
5
|
-
* The implementation MOVED to `@tokenoftrust/private-apps-devkit`
|
|
6
|
-
* (PrivateApps epic D6 Chunk B) so the `tot app` CLI harness (D6 Chunk D) can
|
|
7
|
-
* verify webhook signatures without importing Astro-internal code — without
|
|
8
|
-
* forking security-critical crypto into a second, drifting copy. This file
|
|
9
|
-
* is now a thin re-export; the devkit package is the ONE source of truth.
|
|
10
|
-
* See `packages/private-apps-devkit/src/signing.ts` for the full scope
|
|
11
|
-
* notes (hand-rolled/narrowly-scoped, not a general httpsig implementation)
|
|
12
|
-
* and `packages/private-apps-devkit/tests/signing.test.ts` for the RFC-vector
|
|
13
|
-
* proofs. `signing.test.ts` alongside this file still imports from here —
|
|
14
|
-
* unchanged — so it now exercises those same functions through this
|
|
15
|
-
* re-export.
|
|
16
|
-
*/
|
|
17
|
-
export {
|
|
18
|
-
SIGNATURE_LABEL,
|
|
19
|
-
WEBHOOK_SIGNATURE_COVERED_COMPONENTS,
|
|
20
|
-
computeContentDigestSha256,
|
|
21
|
-
buildSignatureParamsValue,
|
|
22
|
-
buildSignatureBase,
|
|
23
|
-
signWebhookRequest,
|
|
24
|
-
verifyWebhookSignature,
|
|
25
|
-
type SignWebhookRequestInput,
|
|
26
|
-
type SignWebhookRequestResult,
|
|
27
|
-
type WebhookVerifyResult,
|
|
28
|
-
type VerifyWebhookSignatureInput,
|
|
29
|
-
} from "@tokenoftrust/private-apps-devkit";
|
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* RS256 signing-key loader for outbound webhook delivery (PrivateApps epic,
|
|
3
|
-
* D4 Chunk B) — the key `signing.ts` uses to attach HTTP Message Signatures
|
|
4
|
-
* (RFC 9421) to every CloudEvents webhook POST.
|
|
5
|
-
*
|
|
6
|
-
* Mirrors `../apps/gatewayKeys.ts`'s `loadGatewaySigningConfig`/
|
|
7
|
-
* `loadGatewayJwks` structure EXACTLY (import cache keyed on the base64
|
|
8
|
-
* source, JWKS derivation that strips every private RSA field), but over a
|
|
9
|
-
* SEPARATE key (`WEBHOOK_SIGNING_PRIVATE_KEY_B64`) with a separate kid
|
|
10
|
-
* (`webhook-signing-1`) — per `d4-design-resolutions`, this key must never be
|
|
11
|
-
* the same as the D2 gateway key. The gateway key attests requests an app
|
|
12
|
-
* makes INTO the storefront (app → gateway trust); the webhook signing key
|
|
13
|
-
* attests events the storefront pushes OUT to an app (gateway → app trust,
|
|
14
|
-
* the opposite direction). Sharing a key would let a receiver's verification
|
|
15
|
-
* key double as the ability to (offline) verify gateway tokens, and rotating
|
|
16
|
-
* one would force rotating the other.
|
|
17
|
-
*
|
|
18
|
-
* The private key never leaves this module. `loadWebhookJwks` derives the
|
|
19
|
-
* PUBLIC-only JWKS from it, published (Chunk B) via a new field on
|
|
20
|
-
* `/.well-known/tot-app-authorization-metadata`.
|
|
21
|
-
*/
|
|
22
|
-
import { importPKCS8, exportJWK, type CryptoKey, type JWK, type JSONWebKeySet } from "jose";
|
|
23
|
-
import { readEnv } from "@/lib/env";
|
|
24
|
-
// Re-exported (not re-defined) — `@tokenoftrust/private-apps-devkit`'s
|
|
25
|
-
// signing.ts is the ONE source of truth for this constant since D6 Chunk B
|
|
26
|
-
// (it's the `alg` its sign/verify functions produce/accept); this module
|
|
27
|
-
// just keeps the historical import path (`./webhookSigningKey.js`) working.
|
|
28
|
-
export { WEBHOOK_SIGNING_HTTPSIG_ALG } from "@tokenoftrust/private-apps-devkit";
|
|
29
|
-
|
|
30
|
-
/** The only algorithm the webhook signer signs with (verifiers reject anything else). */
|
|
31
|
-
export const WEBHOOK_SIGNING_ALG = "RS256";
|
|
32
|
-
|
|
33
|
-
/** Default key id (overridable via `WEBHOOK_SIGNING_KID`). Names the published key. */
|
|
34
|
-
export const DEFAULT_WEBHOOK_SIGNING_KID = "webhook-signing-1";
|
|
35
|
-
|
|
36
|
-
/** Env keys the loader reads (server-only). */
|
|
37
|
-
export const WEBHOOK_SIGNING_PRIVATE_KEY_ENV = "WEBHOOK_SIGNING_PRIVATE_KEY_B64";
|
|
38
|
-
export const WEBHOOK_SIGNING_KID_ENV = "WEBHOOK_SIGNING_KID";
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Thrown when the webhook signer can't load its signing key — the private
|
|
42
|
-
* key env is absent/malformed. Callers FAIL CLOSED: with no key, no delivery
|
|
43
|
-
* can be signed, and the metadata route omits the webhook JWKS field rather
|
|
44
|
-
* than publish a key that will never actually sign anything.
|
|
45
|
-
*/
|
|
46
|
-
export class WebhookSigningKeyError extends Error {}
|
|
47
|
-
|
|
48
|
-
/** Optional test/DI overrides (avoids touching process env in unit tests). */
|
|
49
|
-
export interface WebhookSigningKeyOverrides {
|
|
50
|
-
/** base64 of the PKCS8 PEM private key (defaults to the env value). */
|
|
51
|
-
privateKeyB64?: string;
|
|
52
|
-
kid?: string;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export interface WebhookSigningConfig {
|
|
56
|
-
key: CryptoKey;
|
|
57
|
-
kid: string;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// Same cache shape as gatewayKeys.ts's signingKeyCache/publicJwkCache, keyed
|
|
61
|
-
// on the base64 string so a rotated key transparently re-imports/re-derives.
|
|
62
|
-
const signingKeyCache = new Map<string, CryptoKey>();
|
|
63
|
-
const publicJwkCache = new Map<string, JWK>();
|
|
64
|
-
|
|
65
|
-
async function importPrivateKey(privateKeyB64: string): Promise<CryptoKey> {
|
|
66
|
-
const cached = signingKeyCache.get(privateKeyB64);
|
|
67
|
-
if (cached) return cached;
|
|
68
|
-
let pem: string;
|
|
69
|
-
try {
|
|
70
|
-
pem = Buffer.from(privateKeyB64, "base64").toString("utf8");
|
|
71
|
-
} catch (cause) {
|
|
72
|
-
throw new WebhookSigningKeyError(
|
|
73
|
-
`webhook signing: ${WEBHOOK_SIGNING_PRIVATE_KEY_ENV} is not valid base64 ` +
|
|
74
|
-
(cause instanceof Error ? cause.message : String(cause)),
|
|
75
|
-
);
|
|
76
|
-
}
|
|
77
|
-
if (!/-----BEGIN (RSA )?PRIVATE KEY-----/.test(pem)) {
|
|
78
|
-
throw new WebhookSigningKeyError(
|
|
79
|
-
`webhook signing: decoded ${WEBHOOK_SIGNING_PRIVATE_KEY_ENV} is not a PEM private key`,
|
|
80
|
-
);
|
|
81
|
-
}
|
|
82
|
-
let key: CryptoKey;
|
|
83
|
-
try {
|
|
84
|
-
// extractable: true — required so `loadWebhookJwks` below can derive the
|
|
85
|
-
// public JWK from this same key, and so `signing.ts` can sign raw bytes
|
|
86
|
-
// with `crypto.subtle.sign` directly (no JWS wrapping needed for RFC
|
|
87
|
-
// 9421 — the signature is over the derived signature base, not a JWT).
|
|
88
|
-
key = (await importPKCS8(pem, WEBHOOK_SIGNING_ALG, { extractable: true })) as CryptoKey;
|
|
89
|
-
} catch (cause) {
|
|
90
|
-
throw new WebhookSigningKeyError(
|
|
91
|
-
`webhook signing: failed to import the RS256 private key ` +
|
|
92
|
-
(cause instanceof Error ? cause.message : String(cause)),
|
|
93
|
-
);
|
|
94
|
-
}
|
|
95
|
-
signingKeyCache.set(privateKeyB64, key);
|
|
96
|
-
return key;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Resolve the active signing config (key + kid) from env, or `overrides` in
|
|
101
|
-
* tests. FAILS CLOSED: throws {@link WebhookSigningKeyError} when the private
|
|
102
|
-
* key env is absent/malformed.
|
|
103
|
-
*/
|
|
104
|
-
export async function loadWebhookSigningConfig(
|
|
105
|
-
overrides: WebhookSigningKeyOverrides = {},
|
|
106
|
-
): Promise<WebhookSigningConfig> {
|
|
107
|
-
const privateKeyB64 =
|
|
108
|
-
overrides.privateKeyB64 ?? (await readEnv(WEBHOOK_SIGNING_PRIVATE_KEY_ENV));
|
|
109
|
-
if (!privateKeyB64) {
|
|
110
|
-
throw new WebhookSigningKeyError(
|
|
111
|
-
`webhook signing: ${WEBHOOK_SIGNING_PRIVATE_KEY_ENV} is not configured (fail closed)`,
|
|
112
|
-
);
|
|
113
|
-
}
|
|
114
|
-
const kid = overrides.kid ?? (await readEnv(WEBHOOK_SIGNING_KID_ENV)) ?? DEFAULT_WEBHOOK_SIGNING_KID;
|
|
115
|
-
const key = await importPrivateKey(privateKeyB64);
|
|
116
|
-
return { key, kid };
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Derive the PUBLIC JWKS for the active webhook signing key — sanitized
|
|
121
|
-
* (every private RSA field stripped) and cached by the source private-key
|
|
122
|
-
* base64. V1 always returns exactly one key; the envelope supports more
|
|
123
|
-
* without a shape change later (rotation-with-overlap).
|
|
124
|
-
*/
|
|
125
|
-
export async function loadWebhookJwks(
|
|
126
|
-
overrides: WebhookSigningKeyOverrides = {},
|
|
127
|
-
): Promise<JSONWebKeySet> {
|
|
128
|
-
const privateKeyB64 =
|
|
129
|
-
overrides.privateKeyB64 ?? (await readEnv(WEBHOOK_SIGNING_PRIVATE_KEY_ENV));
|
|
130
|
-
if (!privateKeyB64) {
|
|
131
|
-
throw new WebhookSigningKeyError(
|
|
132
|
-
`webhook signing: ${WEBHOOK_SIGNING_PRIVATE_KEY_ENV} is not configured (fail closed)`,
|
|
133
|
-
);
|
|
134
|
-
}
|
|
135
|
-
const { key, kid } = await loadWebhookSigningConfig(overrides);
|
|
136
|
-
let jwk = publicJwkCache.get(privateKeyB64);
|
|
137
|
-
if (!jwk) {
|
|
138
|
-
const full = (await exportJWK(key)) as Record<string, unknown>;
|
|
139
|
-
// Strip every private RSA field (RFC 7518 §6.3.2) — only the public parts
|
|
140
|
-
// (kty, n, e) are ever published. This line is the one place that matters.
|
|
141
|
-
const { d: _d, p: _p, q: _q, dp: _dp, dq: _dq, qi: _qi, oth: _oth, ...publicOnly } = full;
|
|
142
|
-
jwk = { ...publicOnly, kid, alg: WEBHOOK_SIGNING_ALG, use: "sig" };
|
|
143
|
-
publicJwkCache.set(privateKeyB64, jwk);
|
|
144
|
-
}
|
|
145
|
-
return { keys: [jwk] };
|
|
146
|
-
}
|