@tokenoftrust/storefront-runner 2.0.3 → 2.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. package/apps/storefront/.astro/content.d.ts +159 -0
  2. package/apps/storefront/.astro/integrations/_astrojs_cloudflare/cloudflare.d.ts +1 -0
  3. package/apps/storefront/.astro/types.d.ts +3 -0
  4. package/apps/storefront/node_modules/.bin/acorn +43 -0
  5. package/apps/storefront/node_modules/.bin/astro +43 -0
  6. package/apps/storefront/node_modules/.bin/astro-check +43 -0
  7. package/apps/storefront/node_modules/.bin/browserslist +43 -0
  8. package/apps/storefront/node_modules/.bin/cf-wrangler +43 -0
  9. package/apps/storefront/node_modules/.bin/depcruise +43 -0
  10. package/apps/storefront/node_modules/.bin/depcruise-baseline +43 -0
  11. package/apps/storefront/node_modules/.bin/depcruise-fmt +43 -0
  12. package/apps/storefront/node_modules/.bin/depcruise-wrap-stream-in-html +43 -0
  13. package/apps/storefront/node_modules/.bin/dependency-cruise +43 -0
  14. package/apps/storefront/node_modules/.bin/dependency-cruiser +43 -0
  15. package/apps/storefront/node_modules/.bin/drizzle-kit +43 -0
  16. package/apps/storefront/node_modules/.bin/esbuild +43 -0
  17. package/apps/storefront/node_modules/.bin/jiti +43 -0
  18. package/apps/storefront/node_modules/.bin/playwright +43 -0
  19. package/apps/storefront/node_modules/.bin/playwright-core +43 -0
  20. package/apps/storefront/node_modules/.bin/prettier +43 -0
  21. package/apps/storefront/node_modules/.bin/rollup +43 -0
  22. package/apps/storefront/node_modules/.bin/tsc +43 -0
  23. package/apps/storefront/node_modules/.bin/tsserver +43 -0
  24. package/apps/storefront/node_modules/.bin/tsx +43 -0
  25. package/apps/storefront/node_modules/.bin/vite +43 -0
  26. package/apps/storefront/node_modules/.bin/vitest +43 -0
  27. package/apps/storefront/node_modules/.bin/workerd +43 -0
  28. package/apps/storefront/node_modules/.bin/wrangler +43 -0
  29. package/apps/storefront/node_modules/.bin/wrangler2 +43 -0
  30. package/apps/storefront/node_modules/.bin/yaml +43 -0
  31. package/apps/storefront/node_modules/.vite/deps/@astrojs_preact_client__js.js +58 -0
  32. package/apps/storefront/node_modules/.vite/deps/@astrojs_preact_client__js.js.map +1 -0
  33. package/apps/storefront/node_modules/.vite/deps/@astrojs_preact_n_@preact_signals.js +741 -0
  34. package/apps/storefront/node_modules/.vite/deps/@astrojs_preact_n_@preact_signals.js.map +1 -0
  35. package/apps/storefront/node_modules/.vite/deps/@astrojs_preact_server__js.js +2549 -0
  36. package/apps/storefront/node_modules/.vite/deps/@astrojs_preact_server__js.js.map +1 -0
  37. package/apps/storefront/node_modules/.vite/deps/_metadata.json +150 -0
  38. package/apps/storefront/node_modules/.vite/deps/astro-BIHuWFxm.js +434 -0
  39. package/apps/storefront/node_modules/.vite/deps/astro-BIHuWFxm.js.map +1 -0
  40. package/apps/storefront/node_modules/.vite/deps/astro_n_aria-query.js +6390 -0
  41. package/apps/storefront/node_modules/.vite/deps/astro_n_aria-query.js.map +1 -0
  42. package/apps/storefront/node_modules/.vite/deps/astro_n_axobject-query.js +2705 -0
  43. package/apps/storefront/node_modules/.vite/deps/astro_n_axobject-query.js.map +1 -0
  44. package/apps/storefront/node_modules/.vite/deps/astro_n_html-escaper.js +65 -0
  45. package/apps/storefront/node_modules/.vite/deps/astro_n_html-escaper.js.map +1 -0
  46. package/apps/storefront/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js +455 -0
  47. package/apps/storefront/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js.map +1 -0
  48. package/apps/storefront/node_modules/.vite/deps/audit-DKDpcdEI.js +1535 -0
  49. package/apps/storefront/node_modules/.vite/deps/audit-DKDpcdEI.js.map +1 -0
  50. package/apps/storefront/node_modules/.vite/deps/highlight-B9UoX1wa.js +48 -0
  51. package/apps/storefront/node_modules/.vite/deps/highlight-B9UoX1wa.js.map +1 -0
  52. package/apps/storefront/node_modules/.vite/deps/icons-BWi2wI9n.js +42 -0
  53. package/apps/storefront/node_modules/.vite/deps/icons-BWi2wI9n.js.map +1 -0
  54. package/apps/storefront/node_modules/.vite/deps/package.json +3 -0
  55. package/apps/storefront/node_modules/.vite/deps/preact.js +310 -0
  56. package/apps/storefront/node_modules/.vite/deps/preact.js.map +1 -0
  57. package/apps/storefront/node_modules/.vite/deps/preact_compat.js +420 -0
  58. package/apps/storefront/node_modules/.vite/deps/preact_compat.js.map +1 -0
  59. package/apps/storefront/node_modules/.vite/deps/preact_compat_jsx-runtime.js +4 -0
  60. package/apps/storefront/node_modules/.vite/deps/preact_debug.js +236 -0
  61. package/apps/storefront/node_modules/.vite/deps/preact_debug.js.map +1 -0
  62. package/apps/storefront/node_modules/.vite/deps/preact_devtools.js +12 -0
  63. package/apps/storefront/node_modules/.vite/deps/preact_devtools.js.map +1 -0
  64. package/apps/storefront/node_modules/.vite/deps/preact_hooks.js +192 -0
  65. package/apps/storefront/node_modules/.vite/deps/preact_hooks.js.map +1 -0
  66. package/apps/storefront/node_modules/.vite/deps/preact_jsx-dev-runtime.js +3 -0
  67. package/apps/storefront/node_modules/.vite/deps/preact_jsx-runtime.js +92 -0
  68. package/apps/storefront/node_modules/.vite/deps/preact_jsx-runtime.js.map +1 -0
  69. package/apps/storefront/node_modules/.vite/deps/preact_test-utils.js +59 -0
  70. package/apps/storefront/node_modules/.vite/deps/preact_test-utils.js.map +1 -0
  71. package/apps/storefront/node_modules/.vite/deps/rolldown-runtime-BPOCksWG.js +24 -0
  72. package/apps/storefront/node_modules/.vite/deps/settings-BVDV0Roj.js +189 -0
  73. package/apps/storefront/node_modules/.vite/deps/settings-BVDV0Roj.js.map +1 -0
  74. package/apps/storefront/node_modules/.vite/deps/static-html-DveYqwMo.js +13 -0
  75. package/apps/storefront/node_modules/.vite/deps/static-html-DveYqwMo.js.map +1 -0
  76. package/apps/storefront/node_modules/.vite/deps/toolbar-B3VIHCXg.js +458 -0
  77. package/apps/storefront/node_modules/.vite/deps/toolbar-B3VIHCXg.js.map +1 -0
  78. package/apps/storefront/node_modules/.vite/deps/ui-library-BK6ricUd.js +1016 -0
  79. package/apps/storefront/node_modules/.vite/deps/ui-library-BK6ricUd.js.map +1 -0
  80. package/apps/storefront/node_modules/.vite/deps/window-BvxDenKN.js +32 -0
  81. package/apps/storefront/node_modules/.vite/deps/window-BvxDenKN.js.map +1 -0
  82. package/apps/storefront/node_modules/.vite/deps/xray-YFiRUU0Z.js +113 -0
  83. package/apps/storefront/node_modules/.vite/deps/xray-YFiRUU0Z.js.map +1 -0
  84. package/apps/storefront/node_modules/.vite/deps_ssr/_metadata.json +8 -0
  85. package/apps/storefront/node_modules/.vite/deps_ssr/package.json +3 -0
  86. package/apps/storefront/src/layouts/Layout.astro +1 -1
  87. package/apps/storefront/src/lib/apps/registryAccess.ts +45 -0
  88. package/apps/storefront/src/lib/auth/adminEntry.ts +4 -4
  89. package/apps/storefront/src/lib/shared/capability.ts +68 -0
  90. package/apps/storefront/src/lib/shared/developerInvites.ts +1250 -0
  91. package/apps/storefront/src/lib/shared/invitePreflight.ts +123 -0
  92. package/apps/storefront/src/lib/webhooks/deliveryEngine.ts +38 -4
  93. package/apps/storefront/src/lib/webhooks/emit.ts +1 -1
  94. package/apps/storefront/src/lib/webhooks/getDispatcher.ts +1 -1
  95. package/apps/storefront/src/pages/api/auth/magic-exchange.ts +1 -1
  96. package/apps/storefront/src/pages/index.astro +1 -1
  97. package/apps/storefront/src/pages/products/[handle].astro +1 -1
  98. package/package.json +1 -1
  99. package/packages/public-runtime/node_modules/.bin/tsc +43 -0
  100. package/packages/public-runtime/node_modules/.bin/tsserver +43 -0
  101. package/packages/public-runtime/node_modules/.bin/vitest +43 -0
  102. package/pnpm-workspace.yaml +3 -0
  103. package/apps/storefront/src/lib/apps/adminService.ts +0 -116
  104. package/apps/storefront/src/lib/apps/adminSession.ts +0 -214
  105. package/apps/storefront/src/lib/apps/apiAuth.ts +0 -91
  106. package/apps/storefront/src/lib/apps/apiRoute.ts +0 -35
  107. package/apps/storefront/src/lib/apps/catalogMapper.ts +0 -39
  108. package/apps/storefront/src/lib/apps/consentConfigStore.ts +0 -228
  109. package/apps/storefront/src/lib/apps/gtmMigrationInstall.ts +0 -447
  110. package/apps/storefront/src/lib/apps/gtmMigrationReport.ts +0 -367
  111. package/apps/storefront/src/lib/apps/gtmMigrationTab.ts +0 -315
  112. package/apps/storefront/src/lib/apps/healthAggregate.ts +0 -66
  113. package/apps/storefront/src/lib/apps/integrationLifecycle.ts +0 -614
  114. package/apps/storefront/src/lib/apps/integrationLifecycleController.ts +0 -125
  115. package/apps/storefront/src/lib/apps/integrationReadModel.ts +0 -563
  116. package/apps/storefront/src/lib/apps/integrationsTab.ts +0 -547
  117. package/apps/storefront/src/lib/apps/integrationsView.ts +0 -106
  118. package/apps/storefront/src/lib/apps/managedAnalyticsService.ts +0 -599
  119. package/apps/storefront/src/lib/apps/tenantIntegrationMaterialization.ts +0 -170
  120. package/apps/storefront/src/lib/apps/tokenIssuer.ts +0 -121
  121. package/apps/storefront/src/lib/apps/tokenVerifier.ts +0 -148
  122. package/apps/storefront/src/lib/apps/v84CleanCloneReplayContext.ts +0 -16
  123. package/apps/storefront/src/lib/apps/v84CloneLifecycleCacheEvidence.ts +0 -105
  124. package/apps/storefront/src/lib/apps/v84CloneLifecycleEvidence.ts +0 -177
  125. package/apps/storefront/src/lib/apps/v84CloneLifecycleMaterialization.ts +0 -251
  126. package/apps/storefront/src/lib/apps/v84CloneLifecycleSemantics.ts +0 -241
  127. package/apps/storefront/src/lib/apps/v84CloneMigrationEvidence.ts +0 -211
  128. package/apps/storefront/src/lib/apps/v84CloneNativeInstallEvidence.ts +0 -409
  129. package/apps/storefront/src/lib/apps/v84NonportableCodeCoaching.ts +0 -368
  130. package/apps/storefront/src/lib/apps/v84NonportableRouteContract.ts +0 -188
  131. package/apps/storefront/src/lib/apps/v84NonportableRouteDogfood.ts +0 -291
  132. package/apps/storefront/src/lib/apps/ws1ManagedAnalyticsClient.ts +0 -95
  133. package/apps/storefront/src/lib/content-edit/applyDiff.ts +0 -91
  134. package/apps/storefront/src/lib/content-edit/client.ts +0 -286
  135. package/apps/storefront/src/lib/content-edit/index.ts +0 -14
  136. package/apps/storefront/src/lib/content-edit/route.ts +0 -25
  137. package/apps/storefront/src/lib/content-edit/types.ts +0 -19
  138. package/apps/storefront/src/pages/api/content-edit/ai-edit.ts +0 -164
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Invite preflight — block a developer invite when the target store's repo isn't
3
+ * provisioned on THIS environment, so a broken connection surfaces to the INVITER
4
+ * at invite time instead of dead-ending later in the invitee's terminal on
5
+ * "Repo <owner>-main is not provisioned".
6
+ *
7
+ * The check is the MCP's credential-free resource-registry read — the SAME read
8
+ * `tenant_checkout` gates on — reached the SAME way the app-binding writer +
9
+ * cli-signin-code seam reach the MCP:
10
+ * GET {origin(MCP_APP_BINDING_URL)}/internal/preview-status?tenant=<appDomain>&app=storefront&tag=main
11
+ * header x-mcp-service-secret: <MCP_SERVICE_SHARED_SECRET>
12
+ * → 200 { owner, app, repo, provisioned, previewReleased } | 400 | 502
13
+ * Contract source of truth: tot-mcp src/server/preview-status-routes.ts. We gate on
14
+ * `provisioned` — the exact precondition `tenant_checkout` needs. We do NOT gate on
15
+ * `previewReleased`: it is (provisioned && preview-approved), which is FALSE for every
16
+ * store that hasn't shipped a preview yet — gating on it would block invites for
17
+ * provisioned-but-unreleased stores whose checkout would actually succeed.
18
+ *
19
+ * FAIL-OPEN: we BLOCK only on a DEFINITIVE not-ready answer (a clean 200 with
20
+ * previewReleased:false). An unconfigured seam (no URL/secret), a non-200, a
21
+ * transport/parse fault, or a timeout is INCONCLUSIVE → allow (log a warning),
22
+ * mirroring recordDeveloperAppBinding's "skipped when not-configured" stance — we
23
+ * never break invites just because the MCP is briefly unreachable.
24
+ */
25
+ import { readEnv } from "@/lib/env";
26
+
27
+ /** How long we'll wait on the preview-status read before failing open. */
28
+ const PREFLIGHT_TIMEOUT_MS = 4000;
29
+
30
+ /** GET transport seam (injectable for tests) — mirrors HttpTransport but read-only. */
31
+ export interface PreflightTransport {
32
+ get(
33
+ url: string,
34
+ headers: Record<string, string>,
35
+ ): Promise<{ status: number; json(): Promise<unknown> }>;
36
+ }
37
+
38
+ /** The real transport: a timeout-bounded GET. */
39
+ export class FetchPreflightTransport implements PreflightTransport {
40
+ async get(url: string, headers: Record<string, string>) {
41
+ const ctrl = new AbortController();
42
+ const timer = setTimeout(() => ctrl.abort(), PREFLIGHT_TIMEOUT_MS);
43
+ try {
44
+ const res = await fetch(url, { method: "GET", headers, signal: ctrl.signal });
45
+ return { status: res.status, json: () => res.json() };
46
+ } finally {
47
+ clearTimeout(timer);
48
+ }
49
+ }
50
+ }
51
+
52
+ /** BLOCK carries the inviter-facing message; ALLOW carries a reason (for logging). */
53
+ export type PreflightDecision =
54
+ | { block: true; reason: "not-provisioned"; message: string }
55
+ | { block: false; reason: string };
56
+
57
+ /** The inviter-facing block message, naming the store. */
58
+ function blockMessage(tenant: string): string {
59
+ return (
60
+ `${tenant} isn't set up on this environment yet — its store repo isn't ` +
61
+ `provisioned. Finish provisioning that store before inviting a developer to it.`
62
+ );
63
+ }
64
+
65
+ /**
66
+ * Preflight `tenant` (the target appDomain). Returns a BLOCK only on a definitive
67
+ * not-provisioned answer; every inconclusive condition allows (fail-open). Never
68
+ * throws.
69
+ */
70
+ export async function checkStorePreviewReady(
71
+ tenant: string,
72
+ deps: { transport?: PreflightTransport } = {},
73
+ ): Promise<PreflightDecision> {
74
+ const bindingUrl = await readEnv("MCP_APP_BINDING_URL");
75
+ const secret = await readEnv("MCP_SERVICE_SHARED_SECRET");
76
+ if (!bindingUrl || !secret) {
77
+ // Seam not configured (the MCP route isn't even mounted without the secret) —
78
+ // inconclusive, so allow rather than break every invite.
79
+ return { block: false, reason: "not-configured" };
80
+ }
81
+
82
+ let endpoint: string;
83
+ try {
84
+ const origin = new URL(bindingUrl).origin;
85
+ const q = new URLSearchParams({ tenant: tenant.toLowerCase(), app: "storefront", tag: "main" });
86
+ // Canonical, namespace-neutral path (the read is generic, not dev-role-scoped).
87
+ endpoint = `${origin}/internal/preview-status?${q.toString()}`;
88
+ } catch {
89
+ return { block: false, reason: "bad-mcp-url" };
90
+ }
91
+
92
+ const transport = deps.transport ?? new FetchPreflightTransport();
93
+ let res: { status: number; json(): Promise<unknown> };
94
+ try {
95
+ res = await transport.get(endpoint, { "x-mcp-service-secret": secret });
96
+ } catch (cause) {
97
+ return {
98
+ block: false,
99
+ reason: "preflight request failed: " + (cause instanceof Error ? cause.message : String(cause)),
100
+ };
101
+ }
102
+
103
+ // Only a clean 200 is a conclusive answer; anything else is inconclusive → allow.
104
+ if (res.status < 200 || res.status >= 300) {
105
+ return { block: false, reason: `preflight returned status ${res.status}` };
106
+ }
107
+ let payload: { provisioned?: unknown; previewReleased?: unknown };
108
+ try {
109
+ payload = (await res.json()) as typeof payload;
110
+ } catch {
111
+ return { block: false, reason: "unparseable preflight response" };
112
+ }
113
+ // Gate on `provisioned` — the ONE precondition the invitee's first `tot start`
114
+ // checkout needs (a missing repo is the austin→sample-store failure). Treat only
115
+ // an explicit `false` as a definitive block; a missing/non-boolean field is
116
+ // inconclusive → allow. NOT `previewReleased` (see the file header): that now means
117
+ // provisioned && preview-approved, which is false for every not-yet-shipped store and
118
+ // would block invites whose checkout actually succeeds.
119
+ if (payload.provisioned === false) {
120
+ return { block: true, reason: "not-provisioned", message: blockMessage(tenant) };
121
+ }
122
+ return { block: false, reason: "provisioned" };
123
+ }
@@ -24,8 +24,6 @@
24
24
  */
25
25
  import { WebhookDeliveryStore, type WebhookDelivery, type WebhookEvent, type WebhookTopic } from "./deliveryStore.js";
26
26
  import type { AppInstall } from "../apps/registryService.js";
27
- import { loadWebhookSigningConfig, type WebhookSigningConfig } from "./webhookSigningKey.js";
28
- import { signWebhookRequest, type SignWebhookRequestResult } from "./signing.js";
29
27
  import { buildCloudEventEnvelope } from "./events.js";
30
28
  import { computeBackoffDelaySeconds, MAX_DELIVERY_ATTEMPTS, type AttemptOutcome } from "./dispatcher.js";
31
29
  import { validateWebhookEndpoint } from "./endpointGuard.js";
@@ -33,6 +31,28 @@ import { validateWebhookEndpoint } from "./endpointGuard.js";
33
31
  /** Bounded per-attempt timeout — a hung receiver must not hold a dispatcher slot open indefinitely. */
34
32
  const DEFAULT_TIMEOUT_MS = 10_000;
35
33
 
34
+ // Both signing.js and webhookSigningKey.js ultimately re-export from
35
+ // @tokenoftrust/private-apps-devkit, a workspace-only package never vendored
36
+ // into the public storefront-runner — a static import of either (type-only
37
+ // included: `tsc` resolves a module for `import type` too) would be a
38
+ // dangling import in that build. The VALUES are loaded via dynamic import at
39
+ // their call sites below (see attemptDelivery/deliverSigned); these two
40
+ // shapes are duplicated here, type-only, so this module's own types don't
41
+ // need those files to exist in the runner. Keep in sync with
42
+ // webhookSigningKey.ts's WebhookSigningConfig and
43
+ // private-apps-devkit/src/signing.ts's SignWebhookRequestResult.
44
+ interface WebhookSigningConfig {
45
+ key: CryptoKey;
46
+ kid: string;
47
+ }
48
+ interface SignWebhookRequestResult {
49
+ headers: {
50
+ "content-digest": string;
51
+ "signature-input": string;
52
+ signature: string;
53
+ };
54
+ }
55
+
36
56
  /** The minimal registry seam the engine needs: resolving the target install by id. Satisfied by `AppRegistryService`. */
37
57
  export interface InstallResolver {
38
58
  get(installId: string): Promise<AppInstall | null>;
@@ -83,7 +103,17 @@ export async function attemptDelivery(deliveryId: string, deps: AttemptDeliveryD
83
103
  const now = deps.now ?? (() => new Date());
84
104
  const timeoutMs = deps.timeoutMs ?? DEFAULT_TIMEOUT_MS;
85
105
  const fetchImpl = deps.fetchImpl ?? fetch;
86
- const loadSigningConfig = deps.loadSigningConfig ?? loadWebhookSigningConfig;
106
+ const loadSigningConfig =
107
+ deps.loadSigningConfig ??
108
+ (async () => {
109
+ // Absent from the public storefront-runner (dropped; see WebhookSigningConfig above).
110
+ // ts-ignore, not ts-expect-error: this module DOES exist in the full repo's own
111
+ // typecheck (only missing from the runner-extracted tree), so there's no error to
112
+ // "expect" here in that context — ts-expect-error would itself error as unused.
113
+ // @ts-ignore
114
+ const mod = await import("./webhookSigningKey.js");
115
+ return mod.loadWebhookSigningConfig() as Promise<WebhookSigningConfig>;
116
+ });
87
117
 
88
118
  const delivery = await store.getDelivery(deliveryId);
89
119
  if (!delivery) {
@@ -172,7 +202,11 @@ async function deliverSigned(
172
202
  now: ctx.now,
173
203
  });
174
204
  const body = JSON.stringify(envelope);
175
- const { headers } = await signWebhookRequest({
205
+ // Absent from the public storefront-runner (dropped; see SignWebhookRequestResult above);
206
+ // ts-ignore not ts-expect-error, same reasoning as loadSigningConfig above.
207
+ // @ts-ignore
208
+ const { signWebhookRequest } = await import("./signing.js");
209
+ const { headers }: SignWebhookRequestResult = await signWebhookRequest({
176
210
  method: "POST",
177
211
  url: install.webhookEndpoint!,
178
212
  body,
@@ -48,7 +48,7 @@
48
48
  import { WebhookDeliveryStore, type WebhookTopic } from "./deliveryStore.js";
49
49
  import { TOPIC_REQUIRED_SCOPE } from "./events.js";
50
50
  import { getDispatcher } from "./getDispatcher.js";
51
- import { getRegistryService } from "../apps/adminService.js";
51
+ import { getRegistryService } from "../apps/registryAccess.js";
52
52
  import type { AppInstall } from "../apps/registryService.js";
53
53
  import { fromD1 } from "../d1/catalog.js";
54
54
  import { readD1 } from "../env.js";
@@ -21,7 +21,7 @@ import { CloudflareQueueDispatcher, type CloudflareQueueSender } from "./cloudfl
21
21
  import { AppRegistryService } from "../apps/registryService.js";
22
22
  import { D1CredentialStore } from "../apps/credentials.js";
23
23
  import { DEFAULT_APP_GATEWAY_KID, APP_GATEWAY_KID_ENV } from "../apps/gatewayKeys.js";
24
- import { json } from "../apps/adminService.js";
24
+ import { json } from "../apps/registryAccess.js";
25
25
  import { fromD1 } from "../d1/catalog.js";
26
26
  import { readD1, readEnv, readQueue } from "../env.js";
27
27
 
@@ -25,7 +25,7 @@ import {
25
25
  } from "@/lib/auth/identityToken";
26
26
  import { createSession, DEFAULT_SESSION_TTL_SECONDS } from "@/lib/auth/session";
27
27
  import { normalizeEmailHint } from "@/lib/privacy/emailHint";
28
- import { acceptDeveloperInvite } from "@/lib/grants/developerInvites";
28
+ import { acceptDeveloperInvite } from "@/lib/shared/developerInvites";
29
29
 
30
30
  export const prerender = false;
31
31
 
@@ -17,7 +17,7 @@ import TierCards from "@/components/membership/TierCards.astro";
17
17
  import { withBase } from "@/lib/basePath";
18
18
  import { rawHtmlPolicy, rawHtmlResponse } from "@/lib/rawMarketingHtml";
19
19
  import AppWidgetFrame from "@/components/apps/AppWidgetFrame.astro";
20
- import { getRegistryService } from "@/lib/apps/adminService.js";
20
+ import { getRegistryService } from "@/lib/apps/registryAccess.js";
21
21
  import { renderWidgetSlot } from "@/lib/apps/widgets/renderSlot.js";
22
22
  import type { CatalogCollection, CatalogProduct } from "@tot/public-runtime";
23
23
  import {
@@ -42,7 +42,7 @@ import { productLd, breadcrumbLd, reviewLd } from "@/lib/jsonld";
42
42
  import { isUsableImageUrl } from "@/lib/placeholder";
43
43
  import { withBase } from "@/lib/basePath";
44
44
  import AppWidgetFrame from "@/components/apps/AppWidgetFrame.astro";
45
- import { getRegistryService } from "@/lib/apps/adminService.js";
45
+ import { getRegistryService } from "@/lib/apps/registryAccess.js";
46
46
  import { renderWidgetSlot } from "@/lib/apps/widgets/renderSlot.js";
47
47
 
48
48
  const { handle } = Astro.params;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tokenoftrust/storefront-runner",
3
- "version": "2.0.3",
3
+ "version": "2.0.4",
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",
@@ -0,0 +1,43 @@
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/typescript@5.9.3/node_modules/typescript/node_modules:/home/runner/work/storefront/storefront/storefront-runner-dist/node_modules/.pnpm/typescript@5.9.3/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/typescript@5.9.3/node_modules/typescript/node_modules:/home/runner/work/storefront/storefront/storefront-runner-dist/node_modules/.pnpm/typescript@5.9.3/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/../typescript/bin/tsc" "$@"
34
+ elif [ -x "$basedir/node" ]; then
35
+ exec "$basedir/node" "$basedir/../typescript/bin/tsc" "$@"
36
+ elif command -v node >/dev/null 2>&1; then
37
+ exec node "$basedir/../typescript/bin/tsc" "$@"
38
+ elif [ -n "$exe" ] && command -v node.exe >/dev/null 2>&1; then
39
+ exec node.exe "$basedir_win/../typescript/bin/tsc" "$@"
40
+ else
41
+ exec node "$basedir/../typescript/bin/tsc" "$@"
42
+ fi
43
+ # cmd-shim-target=/home/runner/work/storefront/storefront/storefront-runner-dist/packages/public-runtime/node_modules/typescript/bin/tsc
@@ -0,0 +1,43 @@
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/typescript@5.9.3/node_modules/typescript/node_modules:/home/runner/work/storefront/storefront/storefront-runner-dist/node_modules/.pnpm/typescript@5.9.3/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/typescript@5.9.3/node_modules/typescript/node_modules:/home/runner/work/storefront/storefront/storefront-runner-dist/node_modules/.pnpm/typescript@5.9.3/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/../typescript/bin/tsserver" "$@"
34
+ elif [ -x "$basedir/node" ]; then
35
+ exec "$basedir/node" "$basedir/../typescript/bin/tsserver" "$@"
36
+ elif command -v node >/dev/null 2>&1; then
37
+ exec node "$basedir/../typescript/bin/tsserver" "$@"
38
+ elif [ -n "$exe" ] && command -v node.exe >/dev/null 2>&1; then
39
+ exec node.exe "$basedir_win/../typescript/bin/tsserver" "$@"
40
+ else
41
+ exec node "$basedir/../typescript/bin/tsserver" "$@"
42
+ fi
43
+ # cmd-shim-target=/home/runner/work/storefront/storefront/storefront-runner-dist/packages/public-runtime/node_modules/typescript/bin/tsserver
@@ -0,0 +1,43 @@
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/vitest@2.1.8_@types+node@26.4.1_happy-dom@20.13.1_lightningcss@1.33.0/node_modules/vitest/node_modules:/home/runner/work/storefront/storefront/storefront-runner-dist/node_modules/.pnpm/vitest@2.1.8_@types+node@26.4.1_happy-dom@20.13.1_lightningcss@1.33.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/vitest@2.1.8_@types+node@26.4.1_happy-dom@20.13.1_lightningcss@1.33.0/node_modules/vitest/node_modules:/home/runner/work/storefront/storefront/storefront-runner-dist/node_modules/.pnpm/vitest@2.1.8_@types+node@26.4.1_happy-dom@20.13.1_lightningcss@1.33.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/../vitest/vitest.mjs" "$@"
34
+ elif [ -x "$basedir/node" ]; then
35
+ exec "$basedir/node" "$basedir/../vitest/vitest.mjs" "$@"
36
+ elif command -v node >/dev/null 2>&1; then
37
+ exec node "$basedir/../vitest/vitest.mjs" "$@"
38
+ elif [ -n "$exe" ] && command -v node.exe >/dev/null 2>&1; then
39
+ exec node.exe "$basedir_win/../vitest/vitest.mjs" "$@"
40
+ else
41
+ exec node "$basedir/../vitest/vitest.mjs" "$@"
42
+ fi
43
+ # cmd-shim-target=/home/runner/work/storefront/storefront/storefront-runner-dist/packages/public-runtime/node_modules/vitest/vitest.mjs
@@ -1,6 +1,9 @@
1
1
  packages:
2
2
  - "apps/*"
3
3
  - "packages/*"
4
+ allowBuilds:
5
+ esbuild: set this to true or false
6
+ workerd: set this to true or false
4
7
  linkWorkspacePackages: true
5
8
  onlyBuiltDependencies:
6
9
  - esbuild
@@ -1,116 +0,0 @@
1
- /**
2
- * Composition root + shared helpers for the `/api/apps/admin/*` routes —
3
- * builds an `AppRegistryService` over the
4
- * `STOREFRONT_APPS_DB` `Queryable` binding and a `D1CredentialStore`, the same
5
- * construction `api/apps/oauth/token.ts` uses, and mirrors
6
- * `../grants/route.ts`'s `getService()` + no-store `json()` + 503-when-
7
- * unconfigured pattern. Kept out of the pure service module so that stays
8
- * transport-free and unit-testable against a fake `Queryable`.
9
- */
10
- import {
11
- AppRegistryService,
12
- RegistryError,
13
- type AppInstall,
14
- type RegistryErrorCode,
15
- } from "./registryService.js";
16
- import { D1CredentialStore } from "./credentials.js";
17
- import { DEFAULT_APP_GATEWAY_KID, APP_GATEWAY_KID_ENV } from "./gatewayKeys.js";
18
- import { fromD1 } from "../d1/catalog.js";
19
- import { readD1, readEnv } from "../env.js";
20
- import { MANAGE_ROLES, type AdminAuthResult } from "./adminSession.js";
21
- import { ManagedAnalyticsApiV1Facade } from "./managedAnalyticsService.js";
22
-
23
- /**
24
- * No-store JSON response — install/rotate bodies carry a one-time client
25
- * secret and must never be edge- or browser-cached.
26
- */
27
- export function json(body: unknown, status = 200): Response {
28
- return new Response(JSON.stringify(body), {
29
- status,
30
- headers: { "content-type": "application/json", "cache-control": "no-store" },
31
- });
32
- }
33
-
34
- /**
35
- * Build an `AppRegistryService` over the real `STOREFRONT_APPS_DB` D1
36
- * binding, or return a 503 Response when that binding isn't configured in
37
- * this runtime. Never leaks binding/secret detail in the response body.
38
- */
39
- export async function getRegistryService(): Promise<AppRegistryService | Response> {
40
- const d1 = await readD1("STOREFRONT_APPS_DB");
41
- if (!d1) return json({ error: "app registry not configured" }, 503);
42
- const db = fromD1(d1);
43
- const activeKid = (await readEnv(APP_GATEWAY_KID_ENV)) ?? DEFAULT_APP_GATEWAY_KID;
44
- const credentials = new D1CredentialStore(db, activeKid);
45
- return new AppRegistryService(db, credentials);
46
- }
47
-
48
- /** Build the managed-analytics v1 service over the same tenant control-plane D1. */
49
- export async function getManagedAnalyticsService(): Promise<ManagedAnalyticsApiV1Facade | Response> {
50
- const d1 = await readD1("STOREFRONT_APPS_DB");
51
- if (!d1) return json({ error: "app registry not configured" }, 503);
52
- return new ManagedAnalyticsApiV1Facade(fromD1(d1));
53
- }
54
-
55
- /** HTTP status per `RegistryError` code — the one shared fail-closed mapping. */
56
- const STATUS_BY_REGISTRY_CODE: Record<RegistryErrorCode, number> = {
57
- scope_denied: 400,
58
- not_found: 404,
59
- already_installed: 409,
60
- invalid_state: 409,
61
- invalid_webhook_endpoint: 400,
62
- invalid_widget_placement: 400,
63
- unknown_version: 404,
64
- invalid_version_facets: 400,
65
- };
66
-
67
- /** Map a `RegistryError` to its HTTP status. Never echoes internals — code + message only. */
68
- export function registryErrorResponse(err: RegistryError): Response {
69
- const body: Record<string, unknown> = { error: err.message, code: err.code };
70
- if (err.denials) body.denials = err.denials;
71
- return json(body, STATUS_BY_REGISTRY_CODE[err.code]);
72
- }
73
-
74
- /** Parse `{ installId }` from a JSON body — shared by every installId-bearing admin route. */
75
- export function parseInstallId(body: unknown): string | null {
76
- if (!body || typeof body !== "object") return null;
77
- const installId = (body as { installId?: unknown }).installId;
78
- return typeof installId === "string" && installId.trim() ? installId.trim() : null;
79
- }
80
-
81
- /**
82
- * Resolve `installId` and verify it belongs to `tenantId` — every mutating
83
- * admin route (update/suspend/resume/uninstall/rotate) MUST call this BEFORE
84
- * invoking a service method, since those methods take a bare `installId` with
85
- * no tenant check of their own. A foreign-tenant
86
- * `installId` is reported IDENTICALLY to a truly-missing one — 404, same
87
- * body shape as `RegistryError("not_found")` — so there is no cross-tenant
88
- * existence oracle.
89
- */
90
- export async function requireOwnedInstall(
91
- registry: AppRegistryService,
92
- installId: string,
93
- tenantId: string,
94
- ): Promise<AppInstall | Response> {
95
- const install = await registry.get(installId);
96
- if (!install || install.tenantId !== tenantId) {
97
- return json({ error: "App install not found.", code: "not_found" }, 404);
98
- }
99
- return install;
100
- }
101
-
102
- /**
103
- * Gate a mutating admin route on the resolved auth's role — call AFTER
104
- * `resolveOperatorSession` for every write route (suspend/resume/uninstall/
105
- * update/rotate/replay). A console session's `role` must be in
106
- * `MANAGE_ROLES`: a `developer` is read-only and gets a 403 here. The
107
- * host-bound/Bearer path (`resolveAdminSession`) carries no `role` at all —
108
- * it is already manage-capable (gated by `ADMIN_CAPABILITIES`/the dedicated
109
- * secret) — so an `undefined` role is exempt, not denied.
110
- */
111
- export function requireManageRole(auth: Extract<AdminAuthResult, { ok: true }>): Response | null {
112
- if (auth.role !== undefined && !MANAGE_ROLES.has(auth.role)) {
113
- return json({ error: "not authorized to manage apps for this tenant" }, 403);
114
- }
115
- return null;
116
- }