@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,85 +1,21 @@
1
1
  // Single source of truth for static-runtime content policy: PHP-like inert
2
- // classification, immutable-asset detection, and safe content-type forcing.
2
+ // classification and safe content-type forcing.
3
3
  //
4
- // This module is intentionally pure (no node:path, no I/O) so the exact same
5
- // rules can be mechanically code-generated into the PHP runtime (runtime
6
- // policies) and exercised by both TypeScript and PHP against one shared fixture
7
- // (static-runtime-policy.fixtures.json). Do not add platform/filesystem
4
+ // Pure by design (no node:path, no I/O) so the same rules can be code-generated
5
+ // into the PHP runtime and exercised against one shared fixture
6
+ // (static-runtime-policy.fixtures.json). Do not add platform or filesystem
8
7
  // behavior here.
9
- export const PHP_LIKE_EXTENSIONS = ["php", "php3", "php4", "php5", "php7", "php8", "phtml", "phar"];
10
- // The internal-redirect/sendfile family: headers that do not describe a response, they
11
- // instruct the web server to produce a different one. Under nginx+php-fpm
12
- // X-Accel-Redirect (and every vendor spelling of it) turns a response into a server-side
13
- // read of an arbitrary internal path — a private-path disclosure primitive — while
14
- // X-Accel-Limit-Rate pins the serving worker for the length of the transfer.
15
- //
16
- // These are dangerous from ANY origin we do not control, so this family is factored out
17
- // of the platform-managed list below: a relay lane (runtime/engine/runtime/proxy.php)
18
- // must drop exactly this set from an untrusted upstream while still relaying the
19
- // ordinary response headers (location, content-encoding, content-range, allow) that the
20
- // rest of the managed list contains.
21
- export const INTERNAL_REDIRECT_RESPONSE_HEADERS = [
22
- "x-accel-buffering",
23
- "x-accel-charset",
24
- "x-accel-expires",
25
- "x-accel-limit-rate",
26
- "x-accel-redirect",
27
- "x-lighttpd-send-file",
28
- "x-lighttpd-sendfile",
29
- "x-lighttpd-sendfile2",
30
- "x-reproxy-url",
31
- "x-sendfile",
32
- ];
33
- // Rejected/platform-managed response headers (internal-docs/platform.md "Routing And
34
- // Headers"): user `_headers` rules can never set or remove these. The
35
- // internal-redirect/sendfile family above is spread in, so it stays a strict subset —
36
- // anything the proxy lane refuses to relay is also refused from `_headers`. The rest is
37
- // the connection and transport surface: hop-by-hop names (Connection, Keep-Alive, TE,
38
- // Trailer, Transfer-Encoding, Upgrade, Proxy-*), framing, and the edge-cache trio.
39
- //
40
- // `isPlatformManagedResponseHeader` — not this list alone — is the policy: the reserved
41
- // `x-spacefast-*` / `x-stattic-*` namespaces below carry the platform's own response
42
- // signature (X-Spacefast-Runtime, X-Spacefast-Version, X-Spacefast-Reason, …), so user
43
- // rules can never forge or clobber them.
8
+ // The platform-managed response-header policy is authored in Rust
9
+ // (crates/stattic-runtime-policy) and generated into the .generated.ts beside
10
+ // this file and into runtime/engine/shared/safety.php, so every managed header
11
+ // name is written down exactly once.
44
12
  //
45
- // Every enforcement point derives from here: the `_headers` compiler in
46
- // @spacefast/routing imports it directly, the PHP runtime maps and prefixes are
47
- // generated from it into runtime/engine/shared/safety.php, and the Rust authorities
48
- // (crates/stattic-runtime-core policy.rs, crates/stattic-zero-runner
49
- // response_headers.rs) are drift-guarded against it by
50
- // apps/control-plane/src/runtime/php-policy-parity.test.ts.
51
- export const PLATFORM_MANAGED_RESPONSE_HEADERS = [
52
- "accept-ranges",
53
- "age",
54
- "allow",
55
- "alt-svc",
56
- "cdn-cache-control",
57
- "cloudflare-cdn-cache-control",
58
- "connection",
59
- "content-encoding",
60
- "content-length",
61
- "content-range",
62
- "cookie",
63
- "date",
64
- "host",
65
- "keep-alive",
66
- "location",
67
- "netlify-cdn-cache-control",
68
- "proxy-authenticate",
69
- "proxy-authorization",
70
- "server",
71
- "set-cookie",
72
- "strict-transport-security",
73
- "surrogate-control",
74
- "te",
75
- "trailer",
76
- "transfer-encoding",
77
- "upgrade",
78
- "vary",
79
- ...INTERNAL_REDIRECT_RESPONSE_HEADERS,
80
- ];
81
- /** Reserved response-header namespaces owned by the platform's own serving signature. */
82
- export const PLATFORM_MANAGED_RESPONSE_HEADER_PREFIXES = ["x-spacefast-", "x-stattic-"];
13
+ // To change it: edit the Rust crate, run
14
+ // `bun scripts/check-finalizer-protocol.mjs --write`, then
15
+ // `bun --filter @spacefast/control-plane runtime:codegen-policy`.
16
+ import { INTERNAL_REDIRECT_RESPONSE_HEADERS, PLATFORM_MANAGED_RESPONSE_HEADER_PREFIXES, PLATFORM_MANAGED_RESPONSE_HEADERS, } from "./static-runtime-policy.generated.js";
17
+ export { INTERNAL_REDIRECT_RESPONSE_HEADERS, PLATFORM_MANAGED_RESPONSE_HEADER_PREFIXES, PLATFORM_MANAGED_RESPONSE_HEADERS, };
18
+ export const PHP_LIKE_EXTENSIONS = ["php", "php3", "php4", "php5", "php7", "php8", "phtml", "phar"];
83
19
  const PLATFORM_MANAGED_RESPONSE_HEADER_SET = new Set(PLATFORM_MANAGED_RESPONSE_HEADERS);
84
20
  /** True when user rules may neither set nor remove this response header. */
85
21
  export function isPlatformManagedResponseHeader(name) {
@@ -87,52 +23,24 @@ export function isPlatformManagedResponseHeader(name) {
87
23
  return (PLATFORM_MANAGED_RESPONSE_HEADER_SET.has(lower) ||
88
24
  PLATFORM_MANAGED_RESPONSE_HEADER_PREFIXES.some((prefix) => lower.startsWith(prefix)));
89
25
  }
90
- export const NON_IMMUTABLE_EXTENSIONS = ["html", "htm", "php", "txt", "xml"];
91
- const PHP_LIKE_EXTENSION_SET = new Set(PHP_LIKE_EXTENSIONS);
92
- const NON_IMMUTABLE_EXTENSION_SET = new Set(NON_IMMUTABLE_EXTENSIONS);
93
- // Immutable caching is decided by DETERMINISTIC signals only — reserved framework
94
- // build directories below, plus user-declared `_headers` Cache-Control rules that
95
- // override the baked default at serve time. There is deliberately NO filename-entropy
96
- // heuristic: an 8-char base64url bundler hash is character-for-character
97
- // indistinguishable from a short human name (D3GeoMap, McKayCV2), so any guess either
98
- // pins a user's mutable file for a year (stale site nobody can fix — browsers have no
99
- // bust channel) or silently downgrades real hashes to revalidate-every-load, which
100
- // under the wp.cloud edge per-IP rate limiter turned dashboard page loads into 429
101
- // storms (2026-07-06). Unrecognized files default to the safe direction:
102
- // `max-age=0, must-revalidate` for browsers, long s-maxage + purge-on-publish at the
103
- // edge.
104
- //
105
- // Each prefix is HIGH-CONFIDENCE guaranteed-hashed:
106
- // - `_next/static/` Next.js immutable build assets (chunks/css/media), all
107
- // [contenthash]; Next itself serves this dir with `immutable`.
108
- // - `_app/immutable/` SvelteKit (Vite) — the directory is literally named `immutable`
109
- // and holds only fingerprinted chunks/assets.
110
- // - `_nuxt/` Nuxt build dir — Vite-hashed client bundle, served from a
111
- // reserved app-owned path.
112
- // - `_astro/` Astro bundled assets — all content-hashed.
113
- //
114
- // DELIBERATELY EXCLUDED: generic user-writable dirs (Vite's `assets/`, CRA/Gatsby's
115
- // `static/`). Users drop NON-hashed files there, so widening to them would create
116
- // year-long false positives. Deployments of bundler output declare those dirs immutable
117
- // themselves via a `_headers` rule (e.g. `/assets/*` → `Cache-Control: public,
118
- // max-age=31536000, immutable`), which the publisher — a build we ran, an agent, a CI
119
- // deploy — writes because it KNOWS the output is content-hashed.
120
- export const GUARANTEED_IMMUTABLE_PREFIXES = [
121
- "_next/static/",
122
- "_app/immutable/",
123
- "_nuxt/",
124
- "_astro/",
125
- ];
126
- /** True when the path lives under a framework dir where every emitted asset is content-hashed. */
127
- function isUnderGuaranteedImmutablePrefix(filePath) {
128
- const normalized = filePath.startsWith("/") ? filePath.slice(1) : filePath;
129
- for (const prefix of GUARANTEED_IMMUTABLE_PREFIXES) {
130
- if (normalized.startsWith(prefix)) {
131
- return true;
132
- }
133
- }
134
- return false;
26
+ /**
27
+ * The token grammar a `_headers` rule accepts for a header NAME. Anything that
28
+ * GENERATES `_headers` rules must gate on it too, so a generated rule can
29
+ * never fail the author's publish.
30
+ */
31
+ export const HEADER_RULE_NAME_PATTERN = /^[A-Za-z0-9-]+$/;
32
+ /** `content-type` `Content-Type`: the casing compiled rules and generated `_headers` carry. */
33
+ export function canonicalHeaderName(input) {
34
+ return input
35
+ .trim()
36
+ .toLowerCase()
37
+ .split("-")
38
+ .map((part) => (part ? `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}` : part))
39
+ .join("-");
135
40
  }
41
+ const PHP_LIKE_EXTENSION_SET = new Set(PHP_LIKE_EXTENSIONS);
42
+ // Immutable-asset detection is NOT decided here: `compiled_cache_class` in
43
+ // crates/stattic-runtime-core (responses.rs) is the one implementation.
136
44
  function lastSegment(filePath) {
137
45
  const slash = filePath.lastIndexOf("/");
138
46
  return slash >= 0 ? filePath.slice(slash + 1) : filePath;
@@ -152,15 +60,6 @@ export function pathStem(filePath) {
152
60
  export function isPhpLikeStaticPath(filePath) {
153
61
  return PHP_LIKE_EXTENSION_SET.has(pathExtension(filePath));
154
62
  }
155
- export function isImmutableAssetPath(filePath) {
156
- if (NON_IMMUTABLE_EXTENSION_SET.has(pathExtension(filePath))) {
157
- return false;
158
- }
159
- // Path-convention layer: anything under a guaranteed-content-hashed framework build
160
- // dir is immutable regardless of filename. The extension gate above still applies,
161
- // so an `index.html` accidentally under `_next/static/` is never pinned.
162
- return isUnderGuaranteedImmutablePrefix(filePath);
163
- }
164
63
  /** Forced inert serving policy for a committed static file. PHP-like files are never executable. */
165
64
  export function runtimeFileSafety(filePath) {
166
65
  return {
@@ -156,8 +156,7 @@ function storageHttpDateTimestamp(weekday, day, month, year, hour, minute, secon
156
156
  return date.getTime() + (second === 60 ? 1_000 : 0);
157
157
  }
158
158
  // HTTP-date has exactly three accepted wire forms (RFC 9110 §5.6.7). General
159
- // date parsers also accept relative/free-form input such as `tomorrow`, so
160
- // validators must parse the grammar and validate its calendar components.
159
+ // date parsers also accept `tomorrow`, so this parses the grammar itself.
161
160
  export function storageParseHttpDate(value) {
162
161
  const input = value.trim();
163
162
  let match = /^(Sun|Mon|Tue|Wed|Thu|Fri|Sat), (\d{2}) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) (\d{4}) (\d{2}):(\d{2}):(\d{2}) GMT$/.exec(input);
@@ -33,8 +33,10 @@ export declare class UploadTargetError extends Error {
33
33
  readonly target: UploadTarget;
34
34
  readonly status: number;
35
35
  readonly serverError?: UploadTargetServerError | undefined;
36
- constructor(target: UploadTarget, status: number, message: string, serverError?: UploadTargetServerError | undefined);
36
+ readonly retryAfterMs?: number | undefined;
37
+ constructor(target: UploadTarget, status: number, message: string, serverError?: UploadTargetServerError | undefined, retryAfterMs?: number | undefined);
37
38
  }
39
+ export declare function uploadRetryAfterMs(value: string | null, now?: number): number | undefined;
38
40
  export declare function uploadPageHasRemainder(page: UploadSession): boolean;
39
41
  export declare const UPLOAD_STALLED_RESUME_MAX_ATTEMPTS = 3;
40
42
  export declare function uploadQueueAdvanced(before: UploadSession, after: UploadSession | null): boolean;
@@ -42,6 +44,7 @@ export declare function executeUploadSession(input: {
42
44
  session: UploadSession;
43
45
  targets?: readonly UploadTarget[] | undefined;
44
46
  concurrency?: number | undefined;
47
+ settleOnFailure?: boolean | undefined;
45
48
  fetch?: ((...args: Parameters<typeof fetch>) => ReturnType<typeof fetch>) | undefined;
46
49
  prepareTarget?: ((target: UploadTarget) => Promise<UploadTargetBody> | UploadTargetBody) | undefined;
47
50
  onEvent?: ((event: UploadSessionEvent) => void) | undefined;
@@ -2,14 +2,30 @@ export class UploadTargetError extends Error {
2
2
  target;
3
3
  status;
4
4
  serverError;
5
- constructor(target, status, message, serverError) {
5
+ retryAfterMs;
6
+ constructor(target, status, message, serverError, retryAfterMs) {
6
7
  super(message);
7
8
  this.target = target;
8
9
  this.status = status;
9
10
  this.serverError = serverError;
11
+ this.retryAfterMs = retryAfterMs;
10
12
  this.name = "UploadTargetError";
11
13
  }
12
14
  }
15
+ export function uploadRetryAfterMs(value, now = Date.now()) {
16
+ if (!value) {
17
+ return undefined;
18
+ }
19
+ const seconds = Number(value);
20
+ if (Number.isFinite(seconds) && seconds >= 0) {
21
+ return Math.ceil(seconds * 1000);
22
+ }
23
+ const date = Date.parse(value);
24
+ if (!Number.isFinite(date)) {
25
+ return undefined;
26
+ }
27
+ return Math.max(0, date - now);
28
+ }
13
29
  function parseUploadTargetServerError(raw) {
14
30
  let parsed;
15
31
  try {
@@ -42,24 +58,18 @@ function parseUploadTargetServerError(raw) {
42
58
  ...(requestId ? { requestId } : {}),
43
59
  };
44
60
  }
45
- // Manifest paging policy — ONE definition, shared by every uploader (the CLI
46
- // and the control plane's inline uploader).
61
+ // Manifest paging policy, shared by every uploader.
47
62
  //
48
- // `targets` is a single instruction page, bounded by the instruction payload
49
- // budget; `summary.upload` counts the whole declared manifest. A short page
50
- // therefore means a remainder that only `resume` hands out, and an uploader
51
- // that stops at the first page finalizes a version the runtime rejects with
52
- // `version_upload_incomplete`. A complete page means there is nothing left to
53
- // ask for — don't re-resume to double-check the uploads, since the runtime
54
- // verifies size and sha256 on every PUT and finalize independently enumerates
55
- // anything still missing.
63
+ // `targets` is one instruction page; `summary.upload` counts the whole
64
+ // manifest. A short page means a remainder only `resume` hands out, and an
65
+ // uploader that stops there finalizes a version the runtime rejects with
66
+ // `version_upload_incomplete`. Don't re-resume a complete page: finalize
67
+ // enumerates anything still missing.
56
68
  export function uploadPageHasRemainder(page) {
57
69
  return page.summary.upload > page.targets.length;
58
70
  }
59
- // Resume mints the next page from runtime session truth, so a converging
60
- // session always hands back different work. A page that repeats the previous
61
- // one is a session that is not draining; retry it a bounded number of times,
62
- // then fail loudly rather than page forever.
71
+ // An active session always hands back different work. A page that repeats
72
+ // the previous one is not draining, so retry boundedly rather than page forever.
63
73
  export const UPLOAD_STALLED_RESUME_MAX_ATTEMPTS = 3;
64
74
  export function uploadQueueAdvanced(before, after) {
65
75
  if (!after || after.targets.length === 0) {
@@ -73,7 +83,7 @@ export function uploadQueueAdvanced(before, after) {
73
83
  }
74
84
  return after.targets.some((target, index) => target.path !== before.targets[index]?.path);
75
85
  }
76
- async function runConcurrent(items, concurrency, fn) {
86
+ async function runConcurrent(items, concurrency, settleOnFailure, fn) {
77
87
  let nextIndex = 0;
78
88
  const runWorker = async () => {
79
89
  const item = items[nextIndex++];
@@ -83,7 +93,18 @@ async function runConcurrent(items, concurrency, fn) {
83
93
  await fn(item);
84
94
  return runWorker();
85
95
  };
86
- await Promise.all(Array.from({ length: Math.min(Math.max(concurrency, 1), items.length || 1) }).map(() => runWorker()));
96
+ const workers = Array.from({
97
+ length: Math.min(Math.max(concurrency, 1), items.length || 1),
98
+ }).map(() => runWorker());
99
+ if (!settleOnFailure) {
100
+ await Promise.all(workers);
101
+ return;
102
+ }
103
+ const outcomes = await Promise.allSettled(workers);
104
+ const failed = outcomes.find((outcome) => outcome.status === "rejected");
105
+ if (failed) {
106
+ throw failed.reason;
107
+ }
87
108
  }
88
109
  export async function executeUploadSession(input) {
89
110
  const targets = input.targets ?? input.session.targets;
@@ -91,7 +112,8 @@ export async function executeUploadSession(input) {
91
112
  return;
92
113
  }
93
114
  const fetchImpl = input.fetch ?? fetch;
94
- await runConcurrent(targets, input.concurrency ?? 4, async (target) => {
115
+ const settleOnFailure = input.settleOnFailure ?? false;
116
+ await runConcurrent(targets, input.concurrency ?? 4, settleOnFailure, async (target) => {
95
117
  input.onEvent?.({ type: "target-start", target });
96
118
  try {
97
119
  const prepared = input.prepareTarget
@@ -114,7 +136,7 @@ export async function executeUploadSession(input) {
114
136
  const details = await response.text().catch(() => "");
115
137
  const serverError = parseUploadTargetServerError(details);
116
138
  const suffix = details.trim() ? `: ${details.trim().slice(0, 1_000)}` : "";
117
- throw new UploadTargetError(target, response.status, `Upload failed for ${target.path} with ${response.status} ${response.statusText}${suffix}.`, serverError);
139
+ throw new UploadTargetError(target, response.status, `Upload failed for ${target.path} with ${response.status} ${response.statusText}${suffix}.`, serverError, uploadRetryAfterMs(response.headers.get("retry-after")));
118
140
  }
119
141
  input.onEvent?.({ type: "target-done", target });
120
142
  }
@@ -1,19 +1,14 @@
1
- // Canonical object-path form (internal-docs/platform.md "Upload Contract"):
2
- // UTF-8, Unicode NFC, percent-decoded exactly once. This canonical form is
3
- // what manifest paths, finalize's normalized paths, route-map keys, and dedup
4
- // keys all mean. The PHP runtime applies the same transform (rawurldecode once
5
- // + Normalizer::normalize) at upload and serve time.
1
+ // Canonical object-path form (spec "Upload Contract"): UTF-8, Unicode NFC,
2
+ // percent-decoded exactly once. Manifest paths, route-map keys and dedup keys
3
+ // all mean this form. The PHP runtime applies the same transform.
6
4
  // oxlint-disable-next-line no-control-regex -- matching control characters is the point: they are never part of a servable path.
7
5
  const CONTROL_CHARS_PATTERN = /[\u0000-\u001f\u007f]/;
8
- // Structural normalization shared by lookups and intake: forward slashes,
9
- // no leading slashes, no control characters, no empty/"."/".." segments.
10
- // Idempotent; safe to apply to already-canonical stored paths (NFC is
11
- // idempotent, no percent-decoding).
6
+ // Structural normalization shared by lookups and intake. Idempotent, so it is
7
+ // safe on already-canonical stored paths (no percent-decoding here).
12
8
  export function normalizeVersionPath(input) {
13
9
  const normalized = input.replace(/\\/g, "/").replace(/^\/+/, "").normalize("NFC");
14
- // Here rather than only in canonicalizeVersionPath: publish-policy's
15
- // staticUploadPathViolation validates with this function, so a control
16
- // character in an uploaded path used to pass the policy unclassified.
10
+ // Here, not only in canonicalizeVersionPath: staticUploadPathViolation
11
+ // validates with this function.
17
12
  if (CONTROL_CHARS_PATTERN.test(normalized)) {
18
13
  throw new Error(`invalid_version_path:${input}`);
19
14
  }
@@ -34,10 +29,8 @@ export function normalizeVersionPath(input) {
34
29
  return safePath;
35
30
  }
36
31
  // Intake canonicalization: percent-decode EXACTLY once, then NFC, then the
37
- // structural rules. Applied wherever paths enter the system (manifest intake,
38
- // archive extraction, runtime upload, serve-time lookup) and never to paths
39
- // that are already canonical — decode-once is not idempotent. Throws on
40
- // malformed percent sequences, control characters, traversal, and empty paths.
32
+ // structural rules. Applied wherever paths enter the system, never to paths
33
+ // that are already canonical, because decode-once is not idempotent.
41
34
  export function canonicalizeVersionPath(input) {
42
35
  let decoded;
43
36
  try {
@@ -47,9 +40,8 @@ export function canonicalizeVersionPath(input) {
47
40
  throw new Error(`invalid_version_path:${input}`);
48
41
  }
49
42
  const canonical = decoded.normalize("NFC");
50
- // Backslashes as separators are denied at intake (spec "Static path
51
- // policy"); only client-side helpers translate Windows separators.
52
- // Control characters are rejected by normalizeVersionPath below.
43
+ // Backslashes are denied at intake (spec "Static path policy"); only
44
+ // client-side helpers translate Windows separators.
53
45
  if (canonical.includes("\\")) {
54
46
  throw new Error(`invalid_version_path:${input}`);
55
47
  }
@@ -3,31 +3,22 @@ export type LoginIntentSearch = {
3
3
  returnTo?: string | undefined;
4
4
  };
5
5
  /**
6
- * Optional query params WordPress.com's OAuth2 authorize endpoint understands
7
- * (wpcom `wp-content/mu-plugins/oauth2/oauth2.php`). All are cosmetic/flow
8
- * hints — dropping them never breaks the OAuth handshake.
6
+ * Optional query params WordPress.com's OAuth2 authorize endpoint understands.
7
+ * All are cosmetic hints, so dropping them never breaks the handshake.
9
8
  */
10
9
  export type WpcomAuthorizeExtras = {
11
10
  /** Prefills the email field on the WP.com login screen (`user_email`). */
12
11
  userEmail?: string | undefined;
13
12
  /** Localizes the WP.com login/consent/signup screens (`locale`). */
14
13
  locale?: string | undefined;
15
- /**
16
- * `implicit=false`: WP.com signs the user out of WordPress.com and shows a
17
- * fresh login instead of silently reusing the current WP.com session. The
18
- * only way to let a user switch WP.com accounts — without it, sign-out on
19
- * our side loops them straight back into the same account.
20
- */
21
- switchAccount?: boolean | undefined;
22
14
  };
23
15
  export declare function decorateWpcomAuthorizeUrl(authorizeUrl: string, extras?: WpcomAuthorizeExtras): string;
24
16
  export declare function buildDashboardLoginPath(input: LoginIntentSearch): string;
25
17
  export declare function buildWpcomAuthStartPath(input: LoginIntentSearch & {
26
18
  /**
27
- * A signed OAuth authorize query (Better Auth's login-page redirect).
28
- * Carried through the WordPress.com round trip so the auth server can
29
- * resume the authorization flow the moment the session exists. Opaque
30
- * here — the auth server verifies its signature.
19
+ * A signed OAuth authorize query, carried through the WordPress.com round
20
+ * trip so the auth server can resume the flow once the session exists.
21
+ * Opaque here; the auth server verifies its signature.
31
22
  */
32
23
  oauthQuery?: string | undefined;
33
24
  }): string;
@@ -6,14 +6,11 @@ export function decorateWpcomAuthorizeUrl(authorizeUrl, extras = {}) {
6
6
  url.searchParams.set("user_email", email);
7
7
  }
8
8
  // WP.com validates locales against its own list and falls back to English,
9
- // so a light shape check is enough here. English is the default omit it.
9
+ // so a light shape check is enough here. English is the default, so omit it.
10
10
  const locale = extras.locale?.trim().toLowerCase().replace("_", "-");
11
11
  if (locale && /^[a-z]{2}(-[a-z]{2})?$/.test(locale) && locale !== "en") {
12
12
  url.searchParams.set("locale", locale);
13
13
  }
14
- if (extras.switchAccount) {
15
- url.searchParams.set("implicit", "false");
16
- }
17
14
  return url.toString();
18
15
  }
19
16
  export function buildDashboardLoginPath(input) {
@@ -104,8 +104,8 @@ export declare const CONCEPTS: {
104
104
  readonly id: "infra";
105
105
  readonly canonicalNoun: "infra";
106
106
  readonly aliases: readonly ["infrastructure"];
107
- readonly externalTerms: readonly ["WP.Cloud"];
108
- readonly deprecated: readonly ["code.storage", "Pierre"];
107
+ readonly externalTerms: readonly ["WP Cloud"];
108
+ readonly deprecated: readonly ["code.storage", "Pierre", "the hosting provider"];
109
109
  readonly description: string;
110
110
  };
111
111
  };
@@ -143,16 +143,16 @@ export declare const EXTERNAL_TERM_ALLOWLIST: readonly [{
143
143
  readonly replacement: "space";
144
144
  readonly reason: string;
145
145
  }, {
146
- readonly term: "WP.Cloud";
146
+ readonly term: "WP Cloud";
147
147
  readonly concept: "infra";
148
- readonly allowedContexts: readonly ["platform-api", "fixed-phrase"];
149
- readonly allowedPhrases: readonly ["built on WP.Cloud"];
148
+ readonly allowedContexts: readonly ["fixed-phrase"];
149
+ readonly allowedPhrases: readonly ["the same infra as Automattic, WP Cloud, Pressable, and WordPress.com", "the same infrastructure as WordPress.com, Tumblr, WordPress VIP and WP Cloud"];
150
150
  readonly replacement: "infra";
151
151
  readonly reason: string;
152
152
  }];
153
153
  /**
154
154
  * AMENDMENT (DECIDED 2026-07-02): the deploy vocabulary stays, as documented,
155
- * first-class ALIASES of the canonical vocabulary `deploy` is an exact alias of
155
+ * first-class ALIASES of the canonical vocabulary. `deploy` is an exact alias of
156
156
  * `publish` (the verb) and `deployments` is an alias of `versions` (the noun).
157
157
  * The canonical lifecycle nouns are unchanged: version / channel / build.
158
158
  *
@@ -164,7 +164,7 @@ export declare const EXTERNAL_TERM_ALLOWLIST: readonly [{
164
164
  * - Wherever the alias is introduced, attach DEPLOY_ALIAS_DISAMBIGUATION
165
165
  * (the one sanctioned disambiguation sentence) so readers land on the
166
166
  * canonical nouns.
167
- * - A bare "deployment" noun in FRESH prose is still discouraged — write
167
+ * - A bare "deployment" noun in FRESH prose is still discouraged. Write
168
168
  * "version" (the thing) or "publish" (the act) unless deliberately using
169
169
  * the alias, in which case disambiguate.
170
170
  */
@@ -208,10 +208,10 @@ export declare const PROVIDER_HYGIENE: {
208
208
  readonly reason: "The build sandbox vendor is never named; it is 'Spacefast Builds'.";
209
209
  };
210
210
  readonly wpcloud: {
211
- readonly internalName: "WP.Cloud";
211
+ readonly internalName: "WP Cloud";
212
212
  readonly replacement: "infra";
213
- readonly allowedContexts: readonly ["platform-api", "fixed-phrase"];
214
- readonly allowedPhrases: readonly ["built on WP.Cloud"];
213
+ readonly allowedContexts: readonly ["fixed-phrase"];
214
+ readonly allowedPhrases: readonly ["the same infra as Automattic, WP Cloud, Pressable, and WordPress.com", "the same infrastructure as WordPress.com, Tumblr, WordPress VIP and WP Cloud"];
215
215
  readonly reason: string;
216
216
  };
217
217
  readonly codeStorage: {
@@ -247,10 +247,10 @@ export type CliCanonicalVerb = keyof typeof CLI_ALIASES;
247
247
  * Disclosure tiers gate how much of a surface (CLI help, `sf docs`) shows by
248
248
  * default, so neither humans nor agents are overwhelmed:
249
249
  *
250
- * - `essential` the handful of commands/docs a new user or agent needs to
250
+ * - `essential`: the handful of commands/docs a new user or agent needs to
251
251
  * make a first publish and manage a space. The default everywhere.
252
- * - `full` the complete product CLI surface (everything not hidden).
253
- * - `reference` absolutely everything, including hidden/advanced commands.
252
+ * - `full`: the complete product CLI surface (everything not hidden).
253
+ * - `reference`: absolutely everything, including hidden/advanced commands.
254
254
  *
255
255
  * Tiers are cumulative for ranked surfaces (`reference` ⊇ `full` ⊇ `essential`);
256
256
  * see {@link tierVisibleAt}. This is THE source of truth for tier ids + labels.