@spacefast/common 0.0.24 → 0.2.1

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 (320) hide show
  1. package/brand-assets/LICENSE-Haskoy.txt +93 -0
  2. package/brand-assets/haskoy-latin-variable.woff2 +0 -0
  3. package/brand-assets/spacefast-favicon.svg +4 -4
  4. package/brand-assets/spacefast-sf-full-bleed.svg +3 -3
  5. package/brand-assets/spacefast-wordmark.svg +2 -4
  6. package/dist/agents/connect-targets.d.ts +6 -146
  7. package/dist/agents/connect-targets.js +63 -295
  8. package/dist/agents/private-key-oauth.d.ts +31 -2
  9. package/dist/agents/private-key-oauth.js +166 -92
  10. package/dist/agents/registry/core.d.ts +292 -0
  11. package/dist/agents/registry/core.js +1388 -0
  12. package/dist/agents/registry/deeplink-probes.d.ts +23 -0
  13. package/dist/agents/registry/deeplink-probes.js +18 -0
  14. package/dist/agents/registry/node.d.ts +56 -0
  15. package/dist/agents/registry/node.js +339 -0
  16. package/dist/brand-assets-build.js +3 -6
  17. package/dist/brand-assets.d.ts +5 -15
  18. package/dist/brand-assets.js +12 -14
  19. package/dist/brand.d.ts +17 -0
  20. package/dist/brand.js +24 -4
  21. package/dist/config/domains.d.ts +79 -12
  22. package/dist/config/domains.js +128 -29
  23. package/dist/config/index.js +1 -2
  24. package/dist/contracts/abuse.js +9 -24
  25. package/dist/contracts/access-profiles.d.ts +86 -0
  26. package/dist/contracts/access-profiles.js +89 -0
  27. package/dist/contracts/access.d.ts +24 -30
  28. package/dist/contracts/access.js +33 -64
  29. package/dist/contracts/activity.d.ts +18 -1
  30. package/dist/contracts/activity.js +72 -26
  31. package/dist/contracts/analytics.js +8 -10
  32. package/dist/contracts/api-keys.d.ts +77 -52
  33. package/dist/contracts/api-keys.js +44 -24
  34. package/dist/contracts/application-journal.d.ts +216 -0
  35. package/dist/contracts/application-journal.js +167 -0
  36. package/dist/contracts/auth.d.ts +90 -17
  37. package/dist/contracts/auth.js +86 -17
  38. package/dist/contracts/beta.d.ts +9 -4
  39. package/dist/contracts/beta.js +20 -16
  40. package/dist/contracts/billing.d.ts +2 -1
  41. package/dist/contracts/billing.js +37 -11
  42. package/dist/contracts/bootstrap.d.ts +791 -0
  43. package/dist/contracts/bootstrap.js +51 -0
  44. package/dist/contracts/builds.d.ts +209 -18
  45. package/dist/contracts/builds.js +93 -27
  46. package/dist/contracts/channels.d.ts +43 -0
  47. package/dist/contracts/channels.js +25 -9
  48. package/dist/contracts/cli.d.ts +7 -0
  49. package/dist/contracts/cli.js +6 -0
  50. package/dist/contracts/collab-public.d.ts +1 -1
  51. package/dist/contracts/collab-public.js +5 -8
  52. package/dist/contracts/comments.d.ts +44 -34
  53. package/dist/contracts/comments.js +107 -77
  54. package/dist/contracts/commerce.d.ts +165 -0
  55. package/dist/contracts/commerce.js +146 -0
  56. package/dist/contracts/common.d.ts +3 -3
  57. package/dist/contracts/common.js +20 -31
  58. package/dist/contracts/content-contract-verification.d.ts +521 -0
  59. package/dist/contracts/content-contract-verification.js +320 -0
  60. package/dist/contracts/content-platform-fixture.d.ts +1278 -0
  61. package/dist/contracts/content-platform-fixture.js +44 -0
  62. package/dist/contracts/content-program.d.ts +994 -0
  63. package/dist/contracts/content-program.js +658 -0
  64. package/dist/contracts/content-sync.d.ts +290 -0
  65. package/dist/contracts/content-sync.js +167 -0
  66. package/dist/contracts/content.d.ts +432 -0
  67. package/dist/contracts/content.js +303 -0
  68. package/dist/contracts/continuation.d.ts +17 -1
  69. package/dist/contracts/continuation.js +23 -23
  70. package/dist/contracts/countries.js +3 -4
  71. package/dist/contracts/crons.d.ts +52 -0
  72. package/dist/contracts/crons.js +60 -0
  73. package/dist/contracts/dashboard-prefs.d.ts +56 -0
  74. package/dist/contracts/dashboard-prefs.js +51 -0
  75. package/dist/contracts/device-auth.d.ts +234 -37
  76. package/dist/contracts/device-auth.js +267 -53
  77. package/dist/contracts/docs.d.ts +1 -5
  78. package/dist/contracts/docs.js +7 -15
  79. package/dist/contracts/domains.d.ts +501 -93
  80. package/dist/contracts/domains.js +193 -55
  81. package/dist/contracts/email-preferences.d.ts +3 -3
  82. package/dist/contracts/email-preferences.js +6 -7
  83. package/dist/contracts/enums.d.ts +27 -26
  84. package/dist/contracts/enums.js +68 -61
  85. package/dist/contracts/error-code-meta.d.ts +283 -7
  86. package/dist/contracts/error-code-meta.js +97 -5
  87. package/dist/contracts/error-codes.d.ts +5 -5
  88. package/dist/contracts/error-codes.js +82 -17
  89. package/dist/contracts/events.d.ts +100 -20
  90. package/dist/contracts/events.js +107 -20
  91. package/dist/contracts/execution.d.ts +48 -64
  92. package/dist/contracts/execution.js +52 -62
  93. package/dist/contracts/feature-lifecycle.d.ts +73 -27
  94. package/dist/contracts/feature-lifecycle.js +86 -26
  95. package/dist/contracts/features.d.ts +68 -19
  96. package/dist/contracts/features.js +42 -41
  97. package/dist/contracts/fixtures/content-platform-v1.json +1108 -0
  98. package/dist/contracts/frame-session.d.ts +192 -0
  99. package/dist/contracts/frame-session.js +164 -0
  100. package/dist/contracts/functions.d.ts +338 -164
  101. package/dist/contracts/functions.js +367 -198
  102. package/dist/contracts/git.d.ts +27 -33
  103. package/dist/contracts/git.js +10 -11
  104. package/dist/contracts/grant-copy.d.ts +43 -0
  105. package/dist/contracts/grant-copy.js +44 -0
  106. package/dist/contracts/grants.d.ts +62 -54
  107. package/dist/contracts/grants.js +124 -23
  108. package/dist/contracts/ids.d.ts +4 -1
  109. package/dist/contracts/ids.js +6 -3
  110. package/dist/contracts/internal.d.ts +8 -18
  111. package/dist/contracts/internal.js +13 -14
  112. package/dist/contracts/mcp.d.ts +28 -0
  113. package/dist/contracts/mcp.js +28 -0
  114. package/dist/contracts/me.d.ts +6 -1
  115. package/dist/contracts/me.js +12 -6
  116. package/dist/contracts/notifications.d.ts +27 -29
  117. package/dist/contracts/notifications.js +9 -4
  118. package/dist/contracts/oauth-resources.d.ts +112 -10
  119. package/dist/contracts/oauth-resources.js +116 -11
  120. package/dist/contracts/oauth-scope-actions.d.ts +90 -0
  121. package/dist/contracts/oauth-scope-actions.js +126 -0
  122. package/dist/contracts/operations.d.ts +27 -4
  123. package/dist/contracts/operations.js +12 -6
  124. package/dist/contracts/pages.d.ts +31 -7
  125. package/dist/contracts/pages.js +22 -5
  126. package/dist/contracts/partner-notifications.d.ts +3 -0
  127. package/dist/contracts/partner-notifications.js +65 -0
  128. package/dist/contracts/{platform.d.ts → partner.d.ts} +29 -42
  129. package/dist/contracts/{platform.js → partner.js} +27 -40
  130. package/dist/contracts/plan-policy.js +1 -1
  131. package/dist/contracts/principal-assertion.d.ts +43 -0
  132. package/dist/contracts/principal-assertion.js +67 -0
  133. package/dist/contracts/principals.d.ts +73 -0
  134. package/dist/contracts/principals.js +59 -0
  135. package/dist/contracts/privacy.d.ts +11 -0
  136. package/dist/contracts/privacy.js +17 -0
  137. package/dist/contracts/problem-document.js +3 -4
  138. package/dist/contracts/publish-archive.d.ts +269 -161
  139. package/dist/contracts/publish-archive.js +11 -1
  140. package/dist/contracts/push-new.d.ts +8 -8
  141. package/dist/contracts/quotas.js +5 -5
  142. package/dist/contracts/realtime.d.ts +52 -0
  143. package/dist/contracts/realtime.js +54 -0
  144. package/dist/contracts/repository-connections.d.ts +44 -31
  145. package/dist/contracts/repository-connections.js +17 -5
  146. package/dist/contracts/resources.d.ts +105 -14
  147. package/dist/contracts/resources.js +150 -26
  148. package/dist/contracts/route-inventory.d.ts +358 -0
  149. package/dist/contracts/route-inventory.js +141 -0
  150. package/dist/contracts/runtime-api.d.ts +206 -82
  151. package/dist/contracts/runtime-api.js +131 -70
  152. package/dist/contracts/runtime-app.d.ts +13 -23
  153. package/dist/contracts/runtime-app.js +39 -24
  154. package/dist/contracts/runtime-components.d.ts +143 -0
  155. package/dist/contracts/runtime-components.js +199 -0
  156. package/dist/contracts/runtime-db.d.ts +49 -23
  157. package/dist/contracts/runtime-db.js +87 -22
  158. package/dist/contracts/runtime-purge.d.ts +8 -0
  159. package/dist/contracts/runtime-purge.js +5 -0
  160. package/dist/contracts/runtime-services.d.ts +43 -75
  161. package/dist/contracts/runtime-services.js +29 -49
  162. package/dist/contracts/runtime-storage.d.ts +15 -14
  163. package/dist/contracts/runtime-storage.js +30 -14
  164. package/dist/contracts/sf-config-v1.d.ts +48 -14
  165. package/dist/contracts/slugs.js +13 -4
  166. package/dist/contracts/space-config.d.ts +283 -19
  167. package/dist/contracts/space-config.js +430 -80
  168. package/dist/contracts/spaces.d.ts +376 -290
  169. package/dist/contracts/spaces.js +200 -135
  170. package/dist/contracts/superadmin-activity.d.ts +3 -43
  171. package/dist/contracts/superadmin-activity.js +6 -30
  172. package/dist/contracts/superadmin-emails.d.ts +1 -0
  173. package/dist/contracts/superadmin-emails.js +17 -1
  174. package/dist/contracts/superadmin-runtime.d.ts +199 -0
  175. package/dist/contracts/superadmin-runtime.js +141 -0
  176. package/dist/contracts/superadmin-search.d.ts +22 -2
  177. package/dist/contracts/superadmin-search.js +14 -6
  178. package/dist/contracts/superadmin-spaces.d.ts +438 -189
  179. package/dist/contracts/superadmin-spaces.js +131 -25
  180. package/dist/contracts/superadmin-teams.d.ts +5 -2
  181. package/dist/contracts/superadmin-teams.js +1 -2
  182. package/dist/contracts/superadmin-tenants.d.ts +627 -12
  183. package/dist/contracts/superadmin-tenants.js +32 -13
  184. package/dist/contracts/superadmin.d.ts +232 -37
  185. package/dist/contracts/superadmin.js +106 -22
  186. package/dist/contracts/tags.d.ts +85 -58
  187. package/dist/contracts/tags.js +47 -37
  188. package/dist/contracts/teams.d.ts +86 -26
  189. package/dist/contracts/teams.js +90 -31
  190. package/dist/contracts/tenants.d.ts +135 -0
  191. package/dist/contracts/tenants.js +120 -0
  192. package/dist/contracts/test-triggers.d.ts +70 -0
  193. package/dist/contracts/test-triggers.js +116 -0
  194. package/dist/contracts/theme-json.js +3 -8
  195. package/dist/contracts/transfers.d.ts +1 -0
  196. package/dist/contracts/transfers.js +3 -2
  197. package/dist/contracts/usage.d.ts +209 -0
  198. package/dist/contracts/usage.js +124 -12
  199. package/dist/contracts/variables.d.ts +2 -2
  200. package/dist/contracts/variables.js +9 -13
  201. package/dist/contracts/webhooks.d.ts +13 -5
  202. package/dist/contracts/webhooks.js +3 -1
  203. package/dist/contracts/zero.d.ts +72 -81
  204. package/dist/contracts/zero.js +80 -78
  205. package/dist/dashboard-paths/index.d.ts +24 -21
  206. package/dist/dashboard-paths/index.js +35 -47
  207. package/dist/dashboard-paths/route-reservations.generated.d.ts +2 -0
  208. package/dist/dashboard-paths/route-reservations.generated.js +36 -0
  209. package/dist/docs/agent-arrival.d.ts +92 -0
  210. package/dist/docs/agent-arrival.js +227 -0
  211. package/dist/docs/agent-output-policy.d.ts +6 -0
  212. package/dist/docs/agent-output-policy.js +22 -0
  213. package/dist/docs/agent-prose.d.ts +12 -18
  214. package/dist/docs/agent-prose.js +73 -51
  215. package/dist/docs/agent-setup.d.ts +72 -41
  216. package/dist/docs/agent-setup.js +339 -436
  217. package/dist/docs/catalog.d.ts +153 -95
  218. package/dist/docs/catalog.js +163 -48
  219. package/dist/docs/error-docs.d.ts +2098 -1
  220. package/dist/docs/error-docs.js +421 -144
  221. package/dist/docs/index-build.d.ts +0 -1
  222. package/dist/docs/index-build.js +2 -3
  223. package/dist/docs/page-context-prompt.d.ts +77 -0
  224. package/dist/docs/page-context-prompt.js +131 -0
  225. package/dist/docs/reference-urls.d.ts +46 -0
  226. package/dist/docs/reference-urls.js +79 -0
  227. package/dist/docs/search.d.ts +6 -8
  228. package/dist/docs/search.js +10 -13
  229. package/dist/docs/skill-distribution.d.ts +4 -1
  230. package/dist/docs/skill-distribution.js +10 -5
  231. package/dist/docs/skills.d.ts +120 -120
  232. package/dist/docs/skills.js +121 -73
  233. package/dist/docs/start-prompts.d.ts +45 -0
  234. package/dist/docs/start-prompts.js +62 -0
  235. package/dist/domain-exploration.js +9 -13
  236. package/dist/og-template.d.ts +56 -0
  237. package/dist/og-template.js +56 -0
  238. package/dist/slug-policy/index.js +5 -11
  239. package/dist/test-helpers/fetch-stub.js +1 -0
  240. package/dist/utils/browser-credential.d.ts +13 -6
  241. package/dist/utils/browser-credential.js +14 -7
  242. package/dist/utils/build-settings.d.ts +21 -1
  243. package/dist/utils/build-settings.js +133 -15
  244. package/dist/utils/canonical-json.d.ts +3 -5
  245. package/dist/utils/canonical-json.js +4 -5
  246. package/dist/utils/cast-transport.d.ts +5 -17
  247. package/dist/utils/cast-transport.js +6 -19
  248. package/dist/utils/claim-token.js +1 -2
  249. package/dist/utils/comment-avatars.js +4 -6
  250. package/dist/utils/concurrency.js +3 -3
  251. package/dist/utils/content-type.d.ts +7 -6
  252. package/dist/utils/content-type.js +73 -51
  253. package/dist/utils/credential-policy.d.ts +50 -6
  254. package/dist/utils/credential-policy.js +126 -59
  255. package/dist/utils/dns-instructions.js +9 -9
  256. package/dist/utils/egress-policy.fixtures.json +1 -1
  257. package/dist/utils/error-code.js +1 -0
  258. package/dist/utils/generate-space-name.d.ts +3 -8
  259. package/dist/utils/generate-space-name.js +3 -8
  260. package/dist/utils/grant-decision.fixtures.json +2 -2
  261. package/dist/utils/grants.d.ts +0 -1
  262. package/dist/utils/grants.js +7 -8
  263. package/dist/utils/gravatar.d.ts +2 -4
  264. package/dist/utils/gravatar.js +6 -12
  265. package/dist/utils/id-hints.js +14 -23
  266. package/dist/utils/idempotency.d.ts +3 -3
  267. package/dist/utils/idempotency.js +6 -7
  268. package/dist/utils/local-space-state.d.ts +23 -26
  269. package/dist/utils/local-space-state.js +46 -46
  270. package/dist/utils/oauth-signed-query.d.ts +3 -5
  271. package/dist/utils/oauth-signed-query.js +6 -9
  272. package/dist/utils/one-shot-replay.d.ts +7 -6
  273. package/dist/utils/one-shot-replay.js +3 -5
  274. package/dist/utils/page-colors.d.ts +15 -19
  275. package/dist/utils/page-colors.js +23 -45
  276. package/dist/utils/page-fonts.d.ts +1 -1
  277. package/dist/utils/page-fonts.js +8 -20
  278. package/dist/utils/page-preview.d.ts +0 -1
  279. package/dist/utils/page-preview.js +0 -1
  280. package/dist/utils/pages.d.ts +33 -3
  281. package/dist/utils/pages.js +83 -30
  282. package/dist/utils/privacy-regions.d.ts +19 -0
  283. package/dist/utils/privacy-regions.js +91 -0
  284. package/dist/utils/publish-form-data.d.ts +4 -5
  285. package/dist/utils/publish-form-data.js +2 -3
  286. package/dist/utils/publish-policy.d.ts +11 -17
  287. package/dist/utils/publish-policy.fixtures.json +11 -5
  288. package/dist/utils/publish-policy.js +65 -110
  289. package/dist/utils/query-client.d.ts +2 -0
  290. package/dist/utils/query-client.js +10 -1
  291. package/dist/utils/runtime-paths.d.ts +4 -4
  292. package/dist/utils/runtime-paths.js +6 -8
  293. package/dist/utils/runtime-upload.d.ts +9 -0
  294. package/dist/utils/runtime-upload.js +12 -0
  295. package/dist/utils/secure-local-file.d.ts +3 -3
  296. package/dist/utils/secure-local-file.js +23 -40
  297. package/dist/utils/space-config.d.ts +12 -15
  298. package/dist/utils/space-config.js +13 -16
  299. package/dist/utils/static-runtime-policy.d.ts +10 -7
  300. package/dist/utils/static-runtime-policy.fixtures.json +38 -41
  301. package/dist/utils/static-runtime-policy.generated.d.ts +16 -0
  302. package/dist/utils/static-runtime-policy.generated.js +70 -0
  303. package/dist/utils/static-runtime-policy.js +31 -132
  304. package/dist/utils/storage-policy.js +1 -2
  305. package/dist/utils/upload-session.d.ts +4 -1
  306. package/dist/utils/upload-session.js +41 -19
  307. package/dist/utils/version-path.js +11 -19
  308. package/dist/utils/wpcom-auth-redirects.d.ts +5 -14
  309. package/dist/utils/wpcom-auth-redirects.js +1 -4
  310. package/dist/vocabulary.d.ts +13 -13
  311. package/dist/vocabulary.js +52 -27
  312. package/package.json +15 -18
  313. package/dist/agents/client-registry.d.ts +0 -22
  314. package/dist/agents/client-registry.js +0 -41
  315. package/dist/docs/agent-handoff-document.d.ts +0 -15
  316. package/dist/docs/agent-handoff-document.js +0 -180
  317. package/dist/docs/agent-solutions.d.ts +0 -110
  318. package/dist/docs/agent-solutions.js +0 -183
  319. package/dist/utils/query-keys.d.ts +0 -84
  320. package/dist/utils/query-keys.js +0 -108
@@ -1,48 +1,35 @@
1
1
  import { z } from "zod";
2
- import { appRoutePathSchema, executionCapabilitiesSchema, executionSha256Schema, httpMethodSchema, } from "./execution.js";
2
+ import { appRoutePathSchema, executionBase64Schema, executionCapabilitiesSchema, executionSha256Schema, httpMethodSchema, } from "./execution.js";
3
3
  /**
4
4
  * Spacefast Functions: tenant workers run as Cloudflare Dynamic Workers.
5
5
  *
6
- * The shape of this product follows from two constraints that are not
7
- * negotiable and that together rule out the obvious architecture:
6
+ * Two constraints shape the product:
8
7
  *
9
- * 1. **Cloudflare is reached from wp.cloud, never from the control plane.**
10
- * The control plane records intent; the space's own origin performs every
8
+ * 1. Cloudflare is reached from wp.cloud, never from the control plane. The
9
+ * control plane records intent; the space's own origin makes every
11
10
  * Cloudflare call.
12
- * 2. **Every asset is served by wp.cloud.** We deliberately decline
13
- * Cloudflare's static-asset support for user workers. A worker receives
14
- * only the requests that could not be answered from bytes on disk, and
15
- * `/_next/static/*` and friends never leave our origin.
11
+ * 2. Every asset is served by wp.cloud. A worker receives only the requests
12
+ * that could not be answered from bytes on disk, so `/_next/static/*` and
13
+ * friends never leave our origin.
16
14
  *
17
- * Dynamic Workers is what makes both hold at once. There is no upload: a
18
- * version's bundle stays in its own content store on wp.cloud, and the host
19
- * worker pulls it on demand through the Worker Loader binding. So there is no
20
- * script lifecycle here no namespace, no script name, no tag budget, no
21
- * eviction, no garbage collection because there is no artefact of ours living
22
- * on Cloudflare to manage. What Cloudflare holds is a warm isolate, which is a
23
- * cache, not a record.
24
- *
25
- * That also keeps the exit cheap. The contract below is "here is a module
26
- * graph, here is its configuration, run it", which is workerd's own shape; the
27
- * managed runtime is an implementation of it rather than the thing we built
28
- * against.
29
- *
30
- * What the worker gets in exchange is the thing we could not build: a real V8
31
- * isolate that runs npm packages and framework output — OpenNext Next.js among
32
- * them — without us having to sandbox any of it.
15
+ * Dynamic Workers holds both at once. There is no upload: a version's bundle
16
+ * stays in its own content store on wp.cloud and the host worker pulls it
17
+ * through the Worker Loader binding, so no artefact of ours lives on Cloudflare
18
+ * and there is no script lifecycle to manage. What the worker buys is a real V8
19
+ * isolate running npm packages and framework output, OpenNext Next.js among
20
+ * them, without us sandboxing any of it.
33
21
  *
34
22
  * The cost is that the worker is a continent away from the space's MySQL.
35
23
  * `ctx.db` therefore travels back over an HTTPS relay to the same PHP broker
36
24
  * the local tier speaks to over a pipe, which is slower and batched rather
37
25
  * than transactional. That is why capsules are a different product rather
38
- * than a placement option see `endpointIsPortable` in `./execution.js`.
26
+ * than a placement option. See `endpointIsPortable` in `./execution.js`.
39
27
  */
40
28
  /**
41
- * The tag a compiled worker artifact carries in `functionsSourceFinalizeSchema`
42
- * and the runtime config built from it so the PHP origin can tell what it is
43
- * dispatching (`functions-dispatch.php` refuses a config tagged anything else).
44
- * It is a property of the artifact the tree produced, not a kind a space or a
45
- * version declares: a publish can carry this beside a capsule.
29
+ * The tag a compiled worker artifact carries, so the PHP origin can tell what
30
+ * it is dispatching. `functions-dispatch.php` refuses any other tag. It is a
31
+ * property of the artifact, not a kind a space or a version declares: a publish
32
+ * can carry this beside a capsule.
46
33
  */
47
34
  export const FUNCTIONS_RUNTIME_KIND = "functions";
48
35
  export const FUNCTIONS_SOURCE_METADATA_KEY = "spacefastFunctions";
@@ -51,62 +38,206 @@ export const FUNCTIONS_RUNTIME_ROUTE_PATHS = {
51
38
  logs: `${FUNCTIONS_PLATFORM_ROUTE_PREFIX}/logs`,
52
39
  /** Where a dispatched worker calls back for brokered capabilities. */
53
40
  relay: `${FUNCTIONS_PLATFORM_ROUTE_PREFIX}/relay`,
41
+ /**
42
+ * Where the execution host asks the space origin to drop cached responses:
43
+ * an OpenNext `revalidatePath` must reach the CDN in front of wp.cloud, and
44
+ * only the origin can speak for its own cache. Authenticated with the
45
+ * version-scoped purge credential in the `sf-purge-token` request header.
46
+ */
47
+ purge: `${FUNCTIONS_PLATFORM_ROUTE_PREFIX}/purge`,
54
48
  };
55
49
  /**
56
- * Where invocation counts are reported, on the CONTROL PLANE rather than the
57
- * space origin.
50
+ * The header the execution host presents at the origin purge route. Outside the
51
+ * `sf-fx-` namespace because that prefix names dispatch instructions the origin
52
+ * SENDS, and is stripped from anything inbound before tenant code.
53
+ */
54
+ export const FUNCTIONS_PURGE_TOKEN_HEADER = "sf-purge-token";
55
+ /**
56
+ * The credential a space origin presents to the execution host on every
57
+ * dispatch (`sf-fx-dispatch-token`), and the audience that identifies it.
58
+ *
59
+ * It is a PER-SITE token, not a fleet secret. The control plane mints one
60
+ * Ed25519 JWT per wp.cloud box, bound to that box's `runtime_instance_id`, and
61
+ * writes it into that box's persistent data. The host verifies the signature
62
+ * against the control plane's public key and takes the site identity from the
63
+ * verified claim rather than from anything the caller asserts.
58
64
  *
59
- * The two destinations are separate on purpose. Logs are the tenant's content
60
- * and stay on their own origin; usage is a number the platform bills from and
61
- * goes straight to us. Sharing one channel would make either destination a
62
- * plausible place to look for the other's data, and would put log volume on the
63
- * path that has to stay cheap.
65
+ * Tenant PHP runs on the same box and can read the box's own configuration, so
66
+ * assume the tenant can read this credential. A per-site token read once still
67
+ * speaks only for its own site: the host refuses any bundle whose signed token
68
+ * names a different `runtime_instance_id`. Signing is EdDSA rather than HMAC so
69
+ * that no signing material sits on the edge, where a host compromise would mint
70
+ * dispatch credentials for the whole fleet.
71
+ */
72
+ export const FUNCTIONS_DISPATCH_TOKEN_AUD = "spacefast-functions-dispatch";
73
+ /**
74
+ * Pinned by every verifier rather than read from the token's own header.
75
+ * Accepting the presented `alg` is how `alg: none` forgeries work.
76
+ */
77
+ export const FUNCTIONS_TOKEN_ALG = "EdDSA";
78
+ /**
79
+ * Ten years, like the bundle and relay credentials: the token is not re-mintable
80
+ * on any request path. It reaches a box only through a wp.cloud persistent-data
81
+ * sync at provisioning and on maintenance sweeps, so a shorter clock would take
82
+ * a quiet box offline rather than protect it. Scope is the boundary, and
83
+ * rotating the platform signing key invalidates every token at once.
84
+ */
85
+ export const FUNCTIONS_DISPATCH_TOKEN_TTL_SECONDS = 10 * 365 * 24 * 60 * 60;
86
+ /**
87
+ * What the execution host requires of a dispatch credential before it believes
88
+ * anything else in the request. One signing key mints every platform token, so
89
+ * the pinned audience is what keeps them from meaning one authority.
90
+ */
91
+ export const functionsDispatchTokenClaimsSchema = z.object({
92
+ aud: z.literal(FUNCTIONS_DISPATCH_TOKEN_AUD),
93
+ runtime_instance_id: z.string().min(1),
94
+ exp: z.number(),
95
+ nbf: z.number().optional(),
96
+ });
97
+ /**
98
+ * The claims carried by the token inside a signed bundle or seed URL. The
99
+ * audience stays open because both artifacts share this shape and are separated
100
+ * by their own audience check at the point of use: a bundle grant must never
101
+ * authorise a seed read.
102
+ */
103
+ export const functionsArtifactTokenClaimsSchema = z.object({
104
+ aud: z.string().min(1),
105
+ runtime_instance_id: z.string().min(1),
106
+ space_id: z.string().min(1),
107
+ version_id: z.string().min(1),
108
+ sha256: z.string().min(1),
109
+ exp: z.number(),
110
+ nbf: z.number().optional(),
111
+ });
112
+ const functionsTokenHeaderSchema = z.object({ alg: z.literal(FUNCTIONS_TOKEN_ALG) });
113
+ /**
114
+ * Undecodable text and a failed schema return the same null on purpose: a reader
115
+ * that told them apart would tell a forger which half was wrong.
116
+ */
117
+ function parsedTokenJson(json, schema) {
118
+ try {
119
+ const parsed = schema.safeParse(JSON.parse(json));
120
+ return parsed.success ? parsed.data : null;
121
+ }
122
+ catch {
123
+ return null;
124
+ }
125
+ }
126
+ /**
127
+ * Whether a compact JWT's protected header declares the one algorithm this
128
+ * platform signs with. Pinned rather than trusted: a verifier that takes the
129
+ * token's own word for `alg` accepts `alg: none`.
130
+ */
131
+ export function functionsTokenHeaderIsSigned(headerJson) {
132
+ return parsedTokenJson(headerJson, functionsTokenHeaderSchema) !== null;
133
+ }
134
+ /**
135
+ * Claim readers for an already signature-verified payload. They live beside the
136
+ * mint-side constants so the control plane's audiences and the host's required
137
+ * claims stay one definition rather than two that agree today.
138
+ */
139
+ export function parseFunctionsDispatchTokenClaims(claimsJson) {
140
+ return parsedTokenJson(claimsJson, functionsDispatchTokenClaimsSchema);
141
+ }
142
+ export function parseFunctionsArtifactTokenClaims(claimsJson) {
143
+ return parsedTokenJson(claimsJson, functionsArtifactTokenClaimsSchema);
144
+ }
145
+ /**
146
+ * Where invocation counts are reported, on the CONTROL PLANE rather than the
147
+ * space origin. Logs are the tenant's content and stay on their own origin;
148
+ * usage is a number the platform bills from. One shared channel would put log
149
+ * volume on the path that has to stay cheap.
64
150
  */
65
151
  export const FUNCTIONS_USAGE_INTAKE_PATH = "/v1/runtime/functions/usage";
66
152
  /** Public, signed read path. Distinct from storage so the token is never a filename. */
67
153
  export const FUNCTIONS_BUNDLE_ROUTE_PREFIX = `${FUNCTIONS_PLATFORM_ROUTE_PREFIX}/b`;
68
154
  export const FUNCTIONS_RUNTIME_STORAGE_ROOT = "__spacefast/functions";
69
155
  /**
70
- * Where a version's compiled bundle lives.
156
+ * Where a version's compiled bundle lives, under the same `__spacefast/` prefix
157
+ * as every other platform-owned path. Two consequences, both wanted:
71
158
  *
72
- * Under the same `__spacefast/` prefix as every other platform-owned path, so
73
- * there is one convention rather than one per product. Two consequences fall
74
- * out of that placement, and both are wanted:
159
+ * - Not servable as static content. The runtime treats the whole prefix as
160
+ * control paths, so a tenant's compiled source is not readable at a guessable
161
+ * URL, and the digest that would make it guessable appears in build output.
162
+ * - Written by the ordinary publish path. The upload policy carves out exactly
163
+ * this subtree (`publish-policy.ts`), so the bundle rides the normal manifest
164
+ * and upload passes.
75
165
  *
76
- * - It is not servable as static content. The runtime treats the whole prefix
77
- * as control paths, so a tenant's compiled source is not readable at a
78
- * guessable URL and the digest that would make it guessable appears in
79
- * build output.
80
- * - It is written by the ordinary publish path anyway. The upload policy
81
- * carves out exactly this subtree (`publish-policy.ts`), so the bundle rides
82
- * the normal manifest and upload passes instead of needing a parallel one.
83
- *
84
- * The dispatch configuration — host, capabilities, relay credential — is not
85
- * here and is not content at all. The engine writes it beside the version's
86
- * file tree rather than inside it, exactly as it does for Zero, so a publish
87
- * cannot reach it: no carve-out, no upload path, nothing to guard. A publish
88
- * able to write that file would be a publish able to grant itself authority it
89
- * was never given.
166
+ * The dispatch configuration is not here and is not content at all. The engine
167
+ * writes it beside the version's file tree, as it does for Zero, so a publish
168
+ * cannot reach it and cannot grant itself authority it was never given.
90
169
  */
91
170
  export const FUNCTIONS_RUNTIME_BUNDLE_ROOT = `${FUNCTIONS_RUNTIME_STORAGE_ROOT}/bundles`;
171
+ /**
172
+ * Where a version's Next-cache seed document lives: the build-time prerender
173
+ * and fetch-cache entries that warm the execution host's cache object, so the
174
+ * first visitor gets the page the build rendered. A sibling of `bundles/` with
175
+ * the same properties, publishable through the ordinary upload path with its
176
+ * own carve-out in publish-policy, never servable as static content, read back
177
+ * only through the signed artifact route.
178
+ */
179
+ export const FUNCTIONS_RUNTIME_SEED_ROOT = `${FUNCTIONS_RUNTIME_STORAGE_ROOT}/seeds`;
92
180
  export const FUNCTIONS_BUNDLE_FORMAT = "spacefast.functions.bundle.v1";
181
+ export const FUNCTIONS_SEED_FORMAT = "spacefast.functions.seed.v1";
93
182
  /**
94
- * Ceiling on a bundle's decoded size.
95
- *
96
- * The whole module graph arrives in one response so a cold isolate costs a
97
- * single round trip, which means this number is also how much the host holds in
98
- * memory before instantiating anything. Set below the runtime's own script
99
- * limit so an oversized build fails in our tooling, where we can say which
183
+ * Ceiling on a bundle's decoded size, and so on how much the host holds in
184
+ * memory before instantiating: the whole module graph arrives in one response,
185
+ * so a cold isolate costs a single round trip. Set below the runtime's own
186
+ * script limit, so an oversized build fails in our tooling, which can say which
100
187
  * modules are large, rather than at dispatch, where the tenant sees a 502.
101
188
  */
102
189
  export const FUNCTIONS_BUNDLE_MAX_BYTES = 8 * 1024 * 1024;
190
+ /**
191
+ * Ceilings on the cache seed document. Per-entry mirrors the cache object's own
192
+ * row limit; the total bounds what the host pulls and ingests on a cold cache.
193
+ * Both are enforced in the CLI, where an overflow can say WHICH pages were left
194
+ * out. An omitted entry renders live on first request like any cache miss.
195
+ */
196
+ export const FUNCTIONS_SEED_ENTRY_MAX_BYTES = 2 * 1024 * 1024 - 8192;
197
+ // Far below Cloudflare's 128 MiB SHARED-isolate ceiling: the Durable Object
198
+ // buffers the JSON and holds the parsed graph beside it, and several objects can
199
+ // occupy one isolate. The host enforces this bound again while streaming.
200
+ export const FUNCTIONS_SEED_MAX_BYTES = 8 * 1024 * 1024;
201
+ /**
202
+ * One warm-cache entry: what the runtime's incremental-cache client would have
203
+ * written for `key` had it rendered the page itself. `valueJson` stays the JSON
204
+ * string it already is on disk (OpenNext's `.cache` / fetch-cache files), so the
205
+ * seed writer and the cache client cannot disagree about serialization.
206
+ */
207
+ export const functionsSeedEntrySchema = z
208
+ .object({
209
+ key: z.string().min(1).max(2048),
210
+ cacheType: z.enum(["cache", "fetch"]),
211
+ valueJson: z.string().min(1),
212
+ /** Epoch milliseconds the entry was built, so staleness math holds. */
213
+ lastModified: z.number().int().nonnegative(),
214
+ })
215
+ .strict();
216
+ /** The document served at a seed URL; host and CLI validate the same shape. */
217
+ export const functionsSeedManifestSchema = z
218
+ .object({
219
+ format: z.literal(FUNCTIONS_SEED_FORMAT),
220
+ entries: z.array(functionsSeedEntrySchema),
221
+ })
222
+ .strict();
103
223
  const functionsRoutePathSchema = appRoutePathSchema({
104
224
  subject: "Function routes",
105
225
  });
106
226
  /**
107
- * A worker's dynamic route table. Anything not matched here is answered from
108
- * the version's static content by wp.cloud and never reaches Cloudflare, which
109
- * is both the cheaper path and the one that keeps assets ours.
227
+ * How many entries the origin's dispatch table accepts. Exported because the CLI
228
+ * classifier compacts its route claims BEFORE assembling the artifact: meeting
229
+ * the ceiling as a finalize error would fail a build that classified fine.
230
+ */
231
+ export const FUNCTIONS_MAX_ROUTES = 256;
232
+ /**
233
+ * A worker's dynamic route table. Anything not matched here is answered from the
234
+ * version's static content by wp.cloud and never reaches Cloudflare.
235
+ *
236
+ * A `subtree` entry covers its own path AND everything below it: the engine
237
+ * expands one into both `path` and `path/:splat`
238
+ * (`_stattic_runtime_functions_routes_artifact`, runtime/engine/admin/management.php).
239
+ * So `wholeSiteRoutes()` is a single `/` subtree entry, and an exact entry beside
240
+ * a subtree entry at the same path is dead weight.
110
241
  */
111
242
  export const functionsRouteSchema = z
112
243
  .object({
@@ -120,22 +251,22 @@ export const functionsRouteSchema = z
120
251
  * One module in a bundle, in the shape the Worker Loader consumes.
121
252
  *
122
253
  * `commonjs` exists because bundlers still emit it and rejecting it would fail
123
- * builds for a format the runtime accepts. Binary module types are deliberately
124
- * absent: nothing we build today emits them, and adding one later is additive.
254
+ * builds for a format the runtime accepts. Binary modules cross this JSON wire
255
+ * format as base64; the Functions host turns them back into ArrayBuffers at the
256
+ * Worker Loader boundary.
125
257
  */
126
258
  export const functionsBundleModuleSchema = z.union([
127
259
  z.object({ js: z.string() }).strict(),
128
260
  z.object({ cjs: z.string() }).strict(),
129
261
  z.object({ text: z.string() }).strict(),
130
262
  z.object({ json: z.unknown() }).strict(),
263
+ z.object({ wasm: executionBase64Schema }).strict(),
264
+ z.object({ data: executionBase64Schema }).strict(),
131
265
  ]);
132
266
  /**
133
- * The document served at a bundle URL and parsed by the host.
134
- *
135
- * Both sides of that exchange validate against this schema, which is the point
136
- * of it living here: the CLI's bundler and the host's loader are separate
137
- * deployments that must agree, and the only thing keeping them honest is a
138
- * shared definition of the bytes in between.
267
+ * The document served at a bundle URL and parsed by the host. The CLI's bundler
268
+ * and the host's loader are separate deployments, and this shared definition is
269
+ * the only thing keeping them agreed on the bytes in between.
139
270
  */
140
271
  export const functionsBundleManifestSchema = z
141
272
  .object({
@@ -150,10 +281,9 @@ export const functionsBundleManifestSchema = z
150
281
  });
151
282
  /**
152
283
  * Where the worker's compiled bytes live in the version's own content store.
153
- * Unlike Zero's bundles these are not carried inline in the finalize payload:
154
- * framework output routinely runs to megabytes, and inlining it would put the
155
- * whole thing through the control plane, which the direct-from-wp.cloud rule
156
- * forbids.
284
+ * Not inline in the finalize payload like Zero's bundles: framework output runs
285
+ * to megabytes, and inlining it would push the whole thing through the control
286
+ * plane, which the direct-from-wp.cloud rule forbids.
157
287
  */
158
288
  export const functionsBundleRefSchema = z
159
289
  .object({
@@ -169,47 +299,73 @@ export const functionsBundleRefSchema = z
169
299
  })
170
300
  .strict();
171
301
  /**
172
- * The path a bundle is served from.
173
- *
174
- * Content-addressed, so the response is immutable and can be cached at our edge
175
- * forever — a cold isolate anywhere in the world costs one CDN hit rather than
176
- * a round trip to the space's origin.
302
+ * Where a version's cache seed document sits in its content store, the bundle
303
+ * ref's sibling with the seed's own root and ceiling. `entries` is carried so
304
+ * status surfaces can say what the seed holds without fetching it.
305
+ */
306
+ export const functionsSeedRefSchema = z
307
+ .object({
308
+ path: z
309
+ .string()
310
+ .min(1)
311
+ .startsWith(`${FUNCTIONS_RUNTIME_SEED_ROOT}/`)
312
+ .refine((path) => !path.split("/").includes(".."), {
313
+ message: "Function seed paths cannot traverse outside the seed root.",
314
+ }),
315
+ sha256: executionSha256Schema,
316
+ size: z.number().int().nonnegative().max(FUNCTIONS_SEED_MAX_BYTES),
317
+ entries: z.number().int().nonnegative(),
318
+ })
319
+ .strict();
320
+ /**
321
+ * The path a bundle is served from. Content-addressed, so the response is
322
+ * immutable and cacheable at our edge forever: a cold isolate anywhere costs one
323
+ * CDN hit rather than a round trip to the space's origin.
177
324
  *
178
- * The token rides in the path rather than the query string for exactly that
179
- * reason: a query parameter is subject to cache-key normalisation, and a token
180
- * that gets stripped from the key would let one space's cached bundle answer
181
- * another space's URL.
325
+ * The token rides in the path rather than the query string for the same reason.
326
+ * A query parameter is subject to cache-key normalisation, and a token stripped
327
+ * from the key would let one space's cached bundle answer another space's URL.
182
328
  *
183
- * Both halves are load-bearing. The digest alone is unguessable to anyone who
184
- * lacks the bytes, but it is derivable by anyone who *has* them, and it shows up
185
- * in build output and `sf runtime status`. The token adds the dimension the
186
- * digest cannot: holding the code does not entitle you to fetch it from us, and
187
- * rotating the site secret invalidates every URL at once.
329
+ * Both halves are load-bearing. The digest is derivable by anyone holding the
330
+ * bytes and shows up in build output and `sf runtime status`. The token adds
331
+ * what the digest cannot: holding the code does not entitle you to fetch it from
332
+ * us, and rotating the site secret invalidates every URL at once.
188
333
  */
189
334
  export function functionsBundlePath(input) {
190
335
  return `${FUNCTIONS_BUNDLE_ROUTE_PREFIX}/${bundleDigest(input.sha256)}/${input.token}/bundle.json`;
191
336
  }
192
337
  /**
193
- * Where the bundle actually sits in the published version the same path minus
194
- * the token segment. The token authorises the read; it does not name the file,
195
- * so rotating the signing key changes every URL without moving a single byte.
338
+ * The seed document's read path: the bundle route with a different terminal
339
+ * filename and its own token audience. One signed artifact route on the origin
340
+ * serves both.
341
+ */
342
+ export function functionsSeedPath(input) {
343
+ return `${FUNCTIONS_BUNDLE_ROUTE_PREFIX}/${bundleDigest(input.sha256)}/${input.token}/seed.json`;
344
+ }
345
+ /**
346
+ * Where the bundle sits in the published version, the same path minus the token
347
+ * segment. The token authorises the read; it does not name the file, so rotating
348
+ * the signing key changes every URL without moving a byte.
196
349
  */
197
350
  export function functionsBundleStoragePath(input) {
198
351
  return `${FUNCTIONS_RUNTIME_BUNDLE_ROOT}/${bundleDigest(input.sha256)}/bundle.json`;
199
352
  }
353
+ /** The seed's storage twin of `functionsBundleStoragePath`. */
354
+ export function functionsSeedStoragePath(input) {
355
+ return `${FUNCTIONS_RUNTIME_SEED_ROOT}/${bundleDigest(input.sha256)}/seed.json`;
356
+ }
200
357
  /**
201
358
  * The digest is carried in contracts as `sha256:<hex>` but appears in paths as
202
- * bare hex: a colon is legal in a path segment yet needlessly invites
203
- * normalisation differences between the edge, the origin, and fetch.
359
+ * bare hex: a colon in a path segment invites normalisation differences between
360
+ * the edge, the origin, and fetch.
204
361
  */
205
362
  function bundleDigest(sha256) {
206
363
  return sha256.startsWith("sha256:") ? sha256.slice(7) : sha256;
207
364
  }
208
365
  /**
209
- * Cloudflare pins runtime behaviour to a date rather than a version number, so
210
- * a worker built today keeps today's semantics forever. We record the author's
211
- * choice on the version, which is what makes rollback honest: promoting an old
212
- * version restores the compatibility date it was built against.
366
+ * Cloudflare pins runtime behaviour to a date rather than a version number, so a
367
+ * worker built today keeps today's semantics forever. Recorded on the version,
368
+ * so promoting an old version restores the date it was built against.
213
369
  */
214
370
  const functionsCompatibilityDateSchema = z.string().regex(/^\d{4}-\d{2}-\d{2}$/);
215
371
  export const functionsWorkerArtifactSchema = z
@@ -218,32 +374,47 @@ export const functionsWorkerArtifactSchema = z
218
374
  appName: z.string().min(1).max(128),
219
375
  /**
220
376
  * What the author wrote, for display: `handler.ts`, `.open-next/worker.js`.
221
- * It names a file in their project, which after bundling no longer exists.
377
+ * The file no longer exists after bundling.
222
378
  */
223
379
  entry: z.string().min(1).max(1024),
224
380
  /**
225
- * The entry module's name *inside the bundle manifest* what the loader
226
- * instantiates. Carried here rather than read from the manifest because the
227
- * origin dispatches without ever opening the bundle: the bytes go from our
228
- * CDN straight to the isolate, and the origin only routes.
381
+ * The entry module's name *inside the bundle manifest*, which is what the
382
+ * loader instantiates. Carried here rather than read from the manifest
383
+ * because the origin dispatches without opening the bundle: the bytes go
384
+ * from our CDN straight to the isolate.
229
385
  */
230
386
  mainModule: z.string().min(1).max(1024),
231
387
  bundle: functionsBundleRefSchema,
388
+ /**
389
+ * The build-time cache seed, or null when the build produced nothing worth
390
+ * warming. A ref, not content: the document rides the version's own file
391
+ * tree like the bundle, and the host pulls it through a signed URL minted
392
+ * at finalize.
393
+ */
394
+ seed: functionsSeedRefSchema.nullable().default(null),
232
395
  compatibilityDate: functionsCompatibilityDateSchema,
233
396
  compatibilityFlags: z.array(z.string().min(1).max(128)).max(32).default([]),
234
- routes: z.array(functionsRouteSchema).max(256).default([]),
397
+ routes: z.array(functionsRouteSchema).max(FUNCTIONS_MAX_ROUTES).default([]),
235
398
  /**
236
- * Aggregate of what the worker's handlers reach, used to decide which
237
- * brokered capabilities the relay will honour for this version. A worker
238
- * that never declares `db` gets a relay that refuses `db.read` outright,
239
- * so an injected dependency cannot widen authority at runtime.
399
+ * Cookie names whose presence must skip the origin's static fast path on
400
+ * claimed routes, the framework's draft/preview session markers. Data from
401
+ * the classifier, not names the engine knows: the origin checks the Cookie
402
+ * header against this list and dispatches instead of serving the extracted
403
+ * file, which lets a fully-static page honor a draft session.
404
+ */
405
+ bypassCookies: z.array(z.string().min(1).max(128)).max(8).default([]),
406
+ /**
407
+ * Aggregate of what the worker's handlers reach, deciding which brokered
408
+ * capabilities the relay honours for this version. A worker that never
409
+ * declares `db` gets a relay that refuses `db.read` outright, so an injected
410
+ * dependency cannot widen authority at runtime.
240
411
  */
241
412
  capabilities: executionCapabilitiesSchema,
242
413
  /**
243
- * Platform-owned runtime services this build needs. This is deliberately
244
- * separate from author capabilities: an OpenNext build needs durable cache
245
- * storage even when the application itself declares no database or fetch
246
- * access, and that storage must never become a general tenant credential.
414
+ * Platform-owned runtime services this build needs, separate from author
415
+ * capabilities: an OpenNext build needs durable cache storage even when the
416
+ * application declares no database or fetch access, and that storage must
417
+ * never become a general tenant credential.
247
418
  */
248
419
  platform: z
249
420
  .object({
@@ -255,12 +426,10 @@ export const functionsWorkerArtifactSchema = z
255
426
  .strict();
256
427
  /**
257
428
  * The authority a dispatched worker may exercise, in the vocabulary the host
258
- * speaks on the wire.
259
- *
260
- * Deliberately a different, narrower list than `executionCapabilitiesSchema`.
261
- * That one records what a build *declared* it uses; this one is what the origin
262
- * *grants*, resolved by the control plane at finalize so the origin forwards a
263
- * decision rather than making one. Anything absent fails closed at the relay.
429
+ * speaks on the wire. Narrower than `executionCapabilitiesSchema`, which records
430
+ * what a build *declared* it uses; this is what the origin *grants*, resolved by
431
+ * the control plane at finalize so the origin forwards a decision rather than
432
+ * making one. Anything absent fails closed at the relay.
264
433
  */
265
434
  export const FUNCTIONS_DISPATCH_CAPABILITIES = [
266
435
  "storage.read",
@@ -278,29 +447,22 @@ export const functionsDispatchCapabilitySchema = z.enum(FUNCTIONS_DISPATCH_CAPAB
278
447
  /**
279
448
  * Resolves a build's declared capabilities into the grant a dispatch carries.
280
449
  *
281
- * Three declared capabilities have no dispatch counterpart, each for its own
282
- * reason:
450
+ * Three declared capabilities have no dispatch counterpart:
283
451
  *
284
452
  * - `env` is not a runtime call at all. Values are resolved at finalize and
285
453
  * travel in the config, so a worker reading `env` reaches nothing.
286
- * - `auth` and `realtime` bind a handler to the local tier see
287
- * `endpointIsPortable` — so code declaring them deploys as a capsule and
288
- * never arrives here.
454
+ * - `auth` and `realtime` bind a handler to the local tier, so code declaring
455
+ * them deploys as a capsule and never arrives here. See `endpointIsPortable`.
289
456
  *
290
- * `db` widens to both read and write because a declaration says a handler
291
- * touches the database, not how. Narrowing it would take per-statement analysis
292
- * we do not do, and guessing "read-only" from a declaration would be a grant
293
- * the author never asked for and cannot see.
294
- *
295
- * The three platform-service grants are named for the operation an author
296
- * reaches for, and each covers its service's whole operation set for the same
297
- * reason `db` widens: a declaration says a handler uses spam checking, not that
298
- * it never files a correction.
457
+ * `db` widens to both read and write, and each platform-service grant covers its
458
+ * service's whole operation set, because a declaration says a handler touches
459
+ * the database or checks for spam, not how. Narrowing would take per-statement
460
+ * analysis we do not do.
299
461
  */
300
462
  export function functionsDispatchCapabilities(capabilities, platform = {}) {
301
- // Storage is the durable, unversioned substrate every app receives. It is a
302
- // base binding like the Zero client's `storage`, not a source-declared
303
- // capability that disappears when a new version omits an import.
463
+ // Storage is a base binding every app receives, like the Zero client's
464
+ // `storage`, not a source-declared capability that disappears when a new
465
+ // version omits an import.
304
466
  const granted = new Set(["storage.read", "storage.write"]);
305
467
  if (capabilities.db) {
306
468
  granted.add("db.read");
@@ -325,8 +487,7 @@ export function functionsDispatchCapabilities(capabilities, platform = {}) {
325
487
  granted.add("next.cache");
326
488
  }
327
489
  // Filtered from the canonical list so the order is stable: the grant is part
328
- // of the host's isolate identity, and a reordering would retire warm isolates
329
- // for no reason.
490
+ // of the host's isolate identity, and reordering would retire warm isolates.
330
491
  return FUNCTIONS_DISPATCH_CAPABILITIES.filter((capability) => granted.has(capability));
331
492
  }
332
493
  /** Logs and the host-local Next cache do not transit the origin relay. */
@@ -334,12 +495,11 @@ export function functionsGrantNeedsRelay(granted) {
334
495
  return granted.some((capability) => capability !== "log" && capability !== "next.cache");
335
496
  }
336
497
  /**
337
- * What the space origin needs in order to dispatch to the host on its own. The
338
- * shared secret is a wp.cloud site secret; it is never part of a version's
339
- * content and never reaches tenant code, so it is not modelled here.
340
- *
341
- * There is no account id and no namespace: the origin talks to one host worker
342
- * over HTTPS and holds no Cloudflare API credential at all.
498
+ * What the space origin needs to dispatch to the host on its own. The shared
499
+ * secret is a wp.cloud site secret, never part of a version's content and never
500
+ * reachable by tenant code, so it is not modelled here. There is no account id
501
+ * and no namespace: the origin talks to one host worker over HTTPS and holds no
502
+ * Cloudflare API credential.
343
503
  */
344
504
  export const functionsHostConfigSchema = z
345
505
  .object({
@@ -350,32 +510,36 @@ export const functionsHostConfigSchema = z
350
510
  * finalize.
351
511
  *
352
512
  * It is here rather than on the artifact because the artifact records what
353
- * was built a path and a digest, both stable while this records how to
513
+ * was built, meaning a path and a digest, both stable. This records how to
354
514
  * reach it, which depends on a signing key and a hostname the build knew
355
515
  * nothing about. Rotating the key changes every URL without changing a
356
516
  * single artifact.
357
517
  */
358
518
  bundleUrl: z.string().url(),
519
+ /**
520
+ * Where the host pulls the version's cache seed, minted beside the bundle
521
+ * URL and null exactly when the artifact carries no seed. It feeds the cache
522
+ * object's cold-start path, never request serving: an unreachable seed is a
523
+ * cold cache, not an error.
524
+ */
525
+ seedUrl: z.string().url().nullable().default(null),
359
526
  })
360
527
  .strict();
361
528
  /**
362
529
  * The callback a dispatched worker uses for `ctx.db`, `ctx.fetch` and logging.
363
530
  *
364
- * Each relay call re-enters the space's PHP-FPM pool, which is the deadlock
365
- * hazard this product's admission control exists to bound: a proxied request
366
- * occupies a worker for its whole life while its relay calls demand more. The
367
- * batching allowance below is the other half of the answer a handler that
368
- * issues twenty statements must cost one relay round trip, not twenty. Measured
369
- * against the PHP broker, batching twenty writes into one call is 8.3x faster
370
- * than twenty separate ones.
531
+ * Each relay call re-enters the space's PHP-FPM pool, the deadlock hazard this
532
+ * product's admission control bounds: a proxied request occupies a worker for
533
+ * its whole life while its relay calls demand more. Batching is the other half.
534
+ * Against the PHP broker, twenty writes in one call is 8.3x faster than twenty
535
+ * separate ones.
371
536
  */
372
537
  export const functionsRelayConfigSchema = z
373
538
  .object({
374
539
  url: z.string().url(),
375
540
  /**
376
- * Bearer credential minted per version. Rotating it also retires the warm
377
- * isolate holding it: the host folds this token into the isolate's identity
378
- * precisely so that a revoked credential cannot outlive its revocation.
541
+ * Bearer credential minted per version. The host folds it into the warm
542
+ * isolate's identity, so rotating it retires the isolate holding it.
379
543
  */
380
544
  token: z.string().min(1),
381
545
  maxBatchStatements: z.number().int().min(1).max(256),
@@ -383,35 +547,46 @@ export const functionsRelayConfigSchema = z
383
547
  })
384
548
  .strict();
385
549
  /**
386
- * Where the execution host reports how much this version ran.
387
- *
388
- * Absent means uncounted, not refused: a space whose configuration predates
389
- * usage reporting must keep serving, and an execution tier that cannot reach
390
- * the control plane must never fail a request over it. Counting is reporting
391
- * only — nothing here throttles, and no limit is enforced anywhere from it.
550
+ * Where the execution host reports how much this version ran. Absent means
551
+ * uncounted, not refused: a space whose configuration predates usage reporting
552
+ * must keep serving, and a tier that cannot reach the control plane must never
553
+ * fail a request over it. Nothing here throttles.
392
554
  */
393
555
  export const functionsUsageConfigSchema = z
394
556
  .object({
395
557
  url: z.string().url(),
396
558
  /**
397
559
  * Minted and verified by the control plane alone; the space only forwards
398
- * it. Version-scoped like the relay credential, and folded into the host's
399
- * isolate identity for the same reason: a rotated credential must not
400
- * outlive its rotation inside a warm isolate.
560
+ * it. Version-scoped and folded into the isolate identity like the relay
561
+ * credential, so a rotated credential cannot outlive its rotation.
401
562
  */
402
563
  token: z.string().min(1),
403
564
  })
404
565
  .strict();
405
566
  /**
406
- * What a build declares about its worker — everything the CLI can know from the
567
+ * The credential the execution host presents at the origin's purge route.
568
+ *
569
+ * Only the token lives here: the purge route is on whichever hostname the
570
+ * visitor hit, so the origin composes the URL from the request host at dispatch
571
+ * time rather than freezing one hostname at finalize. A third credential rather
572
+ * than a reuse of the relay's, because a worker whose relay is unusable must
573
+ * still be able to invalidate what it rendered. Version-scoped, so a republish
574
+ * revokes it.
575
+ */
576
+ export const functionsPurgeConfigSchema = z
577
+ .object({
578
+ token: z.string().min(1),
579
+ })
580
+ .strict();
581
+ /**
582
+ * What a build declares about its worker, everything the CLI can know from the
407
583
  * author's project alone.
408
584
  *
409
- * Unlike Zero, this is *not* the same document the runtime receives. A build
410
- * cannot name the host it will be dispatched to, cannot mint the credential its
411
- * relay calls will carry, and cannot decide what authority it is granted; all
412
- * three are the platform's to say. Keeping them out of this schema is what makes
413
- * that structural rather than a convention — a version's own metadata has no
414
- * field in which to assert authority it was not given.
585
+ * Unlike Zero, this is *not* the document the runtime receives. A build cannot
586
+ * name the host it is dispatched to, mint the credential its relay calls carry,
587
+ * or decide what authority it is granted. Keeping all three out of this schema
588
+ * leaves a version's own metadata no field in which to assert authority it was
589
+ * not given.
415
590
  */
416
591
  export const functionsSourceFinalizeSchema = z
417
592
  .object({
@@ -430,10 +605,9 @@ export const functionsSourceFinalizeSchema = z
430
605
  * The document the control plane sends to the runtime, which the engine stores
431
606
  * beside the version's files and the origin reads on every dispatch.
432
607
  *
433
- * It is the build's declaration plus the things only the platform can supply:
434
- * where to dispatch, what the worker is allowed to do, the credentials that let
435
- * it do those things and report what it did, and the resolved values of its
436
- * variables.
608
+ * It is the build's declaration plus what only the platform can supply: where to
609
+ * dispatch, what the worker may do, the credentials for doing and reporting it,
610
+ * and the resolved variable values.
437
611
  */
438
612
  export const functionsRuntimeConfigSchema = functionsSourceFinalizeSchema
439
613
  .extend({
@@ -441,9 +615,10 @@ export const functionsRuntimeConfigSchema = functionsSourceFinalizeSchema
441
615
  grantedCapabilities: z.array(functionsDispatchCapabilitySchema).max(10).default([]),
442
616
  relay: functionsRelayConfigSchema.nullable().default(null),
443
617
  usage: functionsUsageConfigSchema.nullable().default(null),
618
+ purge: functionsPurgeConfigSchema.nullable().default(null),
444
619
  /**
445
620
  * Resolved at finalize, so a worker reading configuration never calls back
446
- * to us for it and so rotating a variable is a republish with an audit
621
+ * to us for it, and so rotating a variable is a republish with an audit
447
622
  * trail rather than a live mutation of what running code sees.
448
623
  */
449
624
  variableValues: z.record(z.string(), z.string()).default({}),
@@ -460,11 +635,9 @@ export const functionsDeployMetadataSchema = z
460
635
  })
461
636
  .strict();
462
637
  /**
463
- * Builds the declaration a publish attaches to its version.
464
- *
465
- * Exists so the CLI's publish step and every test that needs a Functions
466
- * version agree on the shape by construction rather than by copying an object
467
- * literal around — the same reason `createZeroRuntimeFinalizePayload` exists.
638
+ * Builds the declaration a publish attaches to its version, so the CLI's publish
639
+ * step and every test needing a Functions version agree on the shape by
640
+ * construction. Mirrors `createZeroRuntimeFinalizePayload`.
468
641
  */
469
642
  export function createFunctionsSourceFinalize(input) {
470
643
  return functionsSourceFinalizeSchema.parse({
@@ -475,9 +648,11 @@ export function createFunctionsSourceFinalize(input) {
475
648
  entry: input.entry,
476
649
  mainModule: input.mainModule,
477
650
  bundle: input.bundle,
651
+ seed: input.seed ?? null,
478
652
  compatibilityDate: input.compatibilityDate,
479
653
  compatibilityFlags: input.compatibilityFlags ?? [],
480
654
  routes: input.routes ?? [],
655
+ bypassCookies: input.bypassCookies ?? [],
481
656
  capabilities: input.capabilities ?? {},
482
657
  platform: { nextCache: input.nextCache ?? false },
483
658
  },
@@ -489,12 +664,9 @@ export function createFunctionsSourceFinalize(input) {
489
664
  }
490
665
  /**
491
666
  * What a version's worker is currently doing, as an observer of the space can
492
- * actually determine.
493
- *
494
- * There is no upload state to report. A bundle is either in the version's
495
- * content store or it is not, and dispatch is decided by which version is
496
- * serving — so these three cover the whole space of answers, and each one is
497
- * checkable rather than inferred from a job we ran earlier.
667
+ * determine. There is no upload state: a bundle is either in the version's
668
+ * content store or it is not, and dispatch follows whichever version is serving,
669
+ * so each of these three is checkable rather than inferred from an earlier job.
498
670
  */
499
671
  export const functionsExecutionStateSchema = z.enum([
500
672
  /** No bundle at the version's bundle path. */
@@ -507,17 +679,14 @@ export const functionsExecutionStateSchema = z.enum([
507
679
  /**
508
680
  * What a live worker is, as the `worker` member of a space's runtime status.
509
681
  *
510
- * It carries no kind tag. The member it rides in already says which product
511
- * answered, and the same tree can carry a capsule beside it a discriminant
512
- * here could only repeat the field name or contradict its sibling.
513
- *
514
- * No `versionId`: the status response it rides in already names the live
515
- * version.
682
+ * No kind tag: the member it rides in already says which product answered, and
683
+ * the same tree can carry a capsule beside it. No `versionId`: the status
684
+ * response already names the live version. No `database` field:
685
+ * `capabilities.db` is the fact the whole `/db` family branches on, and stating
686
+ * it twice invites the two to disagree.
516
687
  *
517
- * Everything but `executionState` is a field of the version's own finalize
518
- * metadata, so this answers whether or not the origin is reachable. There is no
519
- * `database` field: `capabilities.db` is the fact the whole `/db` family
520
- * branches on, and stating it twice invites the two to disagree.
688
+ * Everything but `executionState` comes from the version's finalize metadata, so
689
+ * this answers whether or not the origin is reachable.
521
690
  */
522
691
  export const functionsAppSchema = z
523
692
  .object({