@spacefast/common 0.0.2

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 (202) hide show
  1. package/README.md +42 -0
  2. package/dist/brand.d.ts +15 -0
  3. package/dist/brand.js +24 -0
  4. package/dist/config/domains.d.ts +30 -0
  5. package/dist/config/domains.js +75 -0
  6. package/dist/config/index.d.ts +1 -0
  7. package/dist/config/index.js +3 -0
  8. package/dist/contracts/abuse.d.ts +93 -0
  9. package/dist/contracts/abuse.js +50 -0
  10. package/dist/contracts/access.d.ts +409 -0
  11. package/dist/contracts/access.js +396 -0
  12. package/dist/contracts/account.d.ts +28 -0
  13. package/dist/contracts/account.js +54 -0
  14. package/dist/contracts/activity.d.ts +118 -0
  15. package/dist/contracts/activity.js +180 -0
  16. package/dist/contracts/analytics.d.ts +56 -0
  17. package/dist/contracts/analytics.js +91 -0
  18. package/dist/contracts/annotations.d.ts +327 -0
  19. package/dist/contracts/annotations.js +234 -0
  20. package/dist/contracts/api-keys.d.ts +517 -0
  21. package/dist/contracts/api-keys.js +235 -0
  22. package/dist/contracts/archives.d.ts +332 -0
  23. package/dist/contracts/archives.js +120 -0
  24. package/dist/contracts/auth.d.ts +86 -0
  25. package/dist/contracts/auth.js +82 -0
  26. package/dist/contracts/beta.d.ts +106 -0
  27. package/dist/contracts/beta.js +104 -0
  28. package/dist/contracts/billing.d.ts +328 -0
  29. package/dist/contracts/billing.js +203 -0
  30. package/dist/contracts/builds.d.ts +504 -0
  31. package/dist/contracts/builds.js +248 -0
  32. package/dist/contracts/channels.d.ts +49 -0
  33. package/dist/contracts/channels.js +51 -0
  34. package/dist/contracts/common.d.ts +238 -0
  35. package/dist/contracts/common.js +175 -0
  36. package/dist/contracts/deployments.d.ts +574 -0
  37. package/dist/contracts/deployments.js +97 -0
  38. package/dist/contracts/device-auth.d.ts +194 -0
  39. package/dist/contracts/device-auth.js +162 -0
  40. package/dist/contracts/domains.d.ts +6115 -0
  41. package/dist/contracts/domains.js +1435 -0
  42. package/dist/contracts/enums.d.ts +196 -0
  43. package/dist/contracts/enums.js +211 -0
  44. package/dist/contracts/error-code-meta.d.ts +1560 -0
  45. package/dist/contracts/error-code-meta.js +400 -0
  46. package/dist/contracts/error-codes.d.ts +13 -0
  47. package/dist/contracts/error-codes.js +416 -0
  48. package/dist/contracts/events.d.ts +398 -0
  49. package/dist/contracts/events.js +320 -0
  50. package/dist/contracts/fast-domain-search.d.ts +19 -0
  51. package/dist/contracts/fast-domain-search.js +22 -0
  52. package/dist/contracts/features.d.ts +240 -0
  53. package/dist/contracts/features.js +406 -0
  54. package/dist/contracts/git.d.ts +288 -0
  55. package/dist/contracts/git.js +116 -0
  56. package/dist/contracts/ids.d.ts +35 -0
  57. package/dist/contracts/ids.js +45 -0
  58. package/dist/contracts/intercom.d.ts +6 -0
  59. package/dist/contracts/intercom.js +5 -0
  60. package/dist/contracts/internal.d.ts +103 -0
  61. package/dist/contracts/internal.js +92 -0
  62. package/dist/contracts/mcp.d.ts +788 -0
  63. package/dist/contracts/mcp.js +161 -0
  64. package/dist/contracts/me.d.ts +81 -0
  65. package/dist/contracts/me.js +104 -0
  66. package/dist/contracts/oauth-resources.d.ts +30 -0
  67. package/dist/contracts/oauth-resources.js +61 -0
  68. package/dist/contracts/operations.d.ts +54 -0
  69. package/dist/contracts/operations.js +14 -0
  70. package/dist/contracts/plan-policy.d.ts +85 -0
  71. package/dist/contracts/plan-policy.js +75 -0
  72. package/dist/contracts/platform.d.ts +359 -0
  73. package/dist/contracts/platform.js +216 -0
  74. package/dist/contracts/push-new.d.ts +156 -0
  75. package/dist/contracts/push-new.js +52 -0
  76. package/dist/contracts/quotas.d.ts +16 -0
  77. package/dist/contracts/quotas.js +36 -0
  78. package/dist/contracts/repository-connections.d.ts +231 -0
  79. package/dist/contracts/repository-connections.js +106 -0
  80. package/dist/contracts/resources.d.ts +529 -0
  81. package/dist/contracts/resources.js +209 -0
  82. package/dist/contracts/routes.d.ts +45 -0
  83. package/dist/contracts/routes.js +31 -0
  84. package/dist/contracts/runtime-api.d.ts +449 -0
  85. package/dist/contracts/runtime-api.js +513 -0
  86. package/dist/contracts/sites.d.ts +393 -0
  87. package/dist/contracts/sites.js +121 -0
  88. package/dist/contracts/slugs.d.ts +31 -0
  89. package/dist/contracts/slugs.js +47 -0
  90. package/dist/contracts/space-config.d.ts +216 -0
  91. package/dist/contracts/space-config.js +237 -0
  92. package/dist/contracts/space-theme.d.ts +25 -0
  93. package/dist/contracts/space-theme.js +49 -0
  94. package/dist/contracts/spaces.d.ts +2760 -0
  95. package/dist/contracts/spaces.js +589 -0
  96. package/dist/contracts/superadmin-activity.d.ts +205 -0
  97. package/dist/contracts/superadmin-activity.js +107 -0
  98. package/dist/contracts/superadmin-emails.d.ts +212 -0
  99. package/dist/contracts/superadmin-emails.js +112 -0
  100. package/dist/contracts/superadmin-queues.d.ts +414 -0
  101. package/dist/contracts/superadmin-queues.js +202 -0
  102. package/dist/contracts/superadmin-runtime.d.ts +27 -0
  103. package/dist/contracts/superadmin-runtime.js +38 -0
  104. package/dist/contracts/superadmin-spaces.d.ts +429 -0
  105. package/dist/contracts/superadmin-spaces.js +82 -0
  106. package/dist/contracts/superadmin-tenants.d.ts +180 -0
  107. package/dist/contracts/superadmin-tenants.js +117 -0
  108. package/dist/contracts/superadmin.d.ts +654 -0
  109. package/dist/contracts/superadmin.js +309 -0
  110. package/dist/contracts/tags.d.ts +1140 -0
  111. package/dist/contracts/tags.js +1050 -0
  112. package/dist/contracts/teams.d.ts +233 -0
  113. package/dist/contracts/teams.js +174 -0
  114. package/dist/contracts/telemetry.d.ts +2 -0
  115. package/dist/contracts/telemetry.js +1 -0
  116. package/dist/contracts/transfers.d.ts +105 -0
  117. package/dist/contracts/transfers.js +48 -0
  118. package/dist/contracts/variables.d.ts +189 -0
  119. package/dist/contracts/variables.js +82 -0
  120. package/dist/contracts/webhooks.d.ts +159 -0
  121. package/dist/contracts/webhooks.js +59 -0
  122. package/dist/contracts/zero.d.ts +1538 -0
  123. package/dist/contracts/zero.js +755 -0
  124. package/dist/docs/agent-prose.d.ts +35 -0
  125. package/dist/docs/agent-prose.js +153 -0
  126. package/dist/docs/agent-setup.d.ts +6 -0
  127. package/dist/docs/agent-setup.js +164 -0
  128. package/dist/docs/agent-solutions.d.ts +110 -0
  129. package/dist/docs/agent-solutions.js +181 -0
  130. package/dist/docs/error-docs.d.ts +12 -0
  131. package/dist/docs/error-docs.js +1601 -0
  132. package/dist/docs/skill-distribution.d.ts +12 -0
  133. package/dist/docs/skill-distribution.js +18 -0
  134. package/dist/domain-exploration.d.ts +108 -0
  135. package/dist/domain-exploration.js +488 -0
  136. package/dist/domain-normalize.d.ts +1 -0
  137. package/dist/domain-normalize.js +14 -0
  138. package/dist/domain-zones.d.ts +1 -0
  139. package/dist/domain-zones.js +5272 -0
  140. package/dist/index.d.ts +1 -0
  141. package/dist/index.js +1 -0
  142. package/dist/utils/access-policy.d.ts +39 -0
  143. package/dist/utils/access-policy.js +232 -0
  144. package/dist/utils/api.d.ts +4 -0
  145. package/dist/utils/api.js +13 -0
  146. package/dist/utils/auth-redirect.d.ts +1 -0
  147. package/dist/utils/auth-redirect.js +48 -0
  148. package/dist/utils/auth.d.ts +5 -0
  149. package/dist/utils/auth.js +13 -0
  150. package/dist/utils/build-settings.d.ts +49 -0
  151. package/dist/utils/build-settings.js +366 -0
  152. package/dist/utils/claim-token.d.ts +1 -0
  153. package/dist/utils/claim-token.js +9 -0
  154. package/dist/utils/collections.d.ts +1 -0
  155. package/dist/utils/collections.js +5 -0
  156. package/dist/utils/concurrency.d.ts +1 -0
  157. package/dist/utils/concurrency.js +18 -0
  158. package/dist/utils/content-type.d.ts +10 -0
  159. package/dist/utils/content-type.js +145 -0
  160. package/dist/utils/error-display.d.ts +7 -0
  161. package/dist/utils/error-display.js +42 -0
  162. package/dist/utils/error.d.ts +1 -0
  163. package/dist/utils/error.js +3 -0
  164. package/dist/utils/generate-space-name.d.ts +14 -0
  165. package/dist/utils/generate-space-name.js +95 -0
  166. package/dist/utils/git-repository.d.ts +5 -0
  167. package/dist/utils/git-repository.js +99 -0
  168. package/dist/utils/guards.d.ts +1 -0
  169. package/dist/utils/guards.js +3 -0
  170. package/dist/utils/publish-detection.d.ts +10 -0
  171. package/dist/utils/publish-detection.js +118 -0
  172. package/dist/utils/publish-policy.d.ts +69 -0
  173. package/dist/utils/publish-policy.js +429 -0
  174. package/dist/utils/query-client.d.ts +7 -0
  175. package/dist/utils/query-client.js +21 -0
  176. package/dist/utils/query-keys.d.ts +75 -0
  177. package/dist/utils/query-keys.js +78 -0
  178. package/dist/utils/runtime-paths.d.ts +7 -0
  179. package/dist/utils/runtime-paths.js +28 -0
  180. package/dist/utils/runtime-upload.d.ts +19 -0
  181. package/dist/utils/runtime-upload.js +27 -0
  182. package/dist/utils/slug.d.ts +5 -0
  183. package/dist/utils/slug.js +13 -0
  184. package/dist/utils/space-config.d.ts +75 -0
  185. package/dist/utils/space-config.js +415 -0
  186. package/dist/utils/space-theme.d.ts +16 -0
  187. package/dist/utils/space-theme.js +89 -0
  188. package/dist/utils/static-runtime-policy.d.ts +17 -0
  189. package/dist/utils/static-runtime-policy.js +103 -0
  190. package/dist/utils/upload-session.d.ts +34 -0
  191. package/dist/utils/upload-session.js +69 -0
  192. package/dist/utils/urls.d.ts +2 -0
  193. package/dist/utils/urls.js +7 -0
  194. package/dist/utils/version-path.d.ts +6 -0
  195. package/dist/utils/version-path.js +51 -0
  196. package/dist/utils/wpcom-auth-redirects.d.ts +14 -0
  197. package/dist/utils/wpcom-auth-redirects.js +49 -0
  198. package/dist/utils/zero-realtime-signature.d.ts +12 -0
  199. package/dist/utils/zero-realtime-signature.js +45 -0
  200. package/dist/vocabulary.d.ts +230 -0
  201. package/dist/vocabulary.js +218 -0
  202. package/package.json +58 -0
@@ -0,0 +1,513 @@
1
+ import { z } from "zod";
2
+ import { policyDocSchema } from "./access.js";
3
+ import { conventionFilesSchema, manifestFileSchema } from "./internal.js";
4
+ const runtimeConventionFilesSchema = conventionFilesSchema.omit({ routes: true }).strict();
5
+ const hostCanonicalRedirectSchema = z.object({
6
+ from: z.string().min(1),
7
+ to: z.string().min(1),
8
+ status: z.literal(308).default(308),
9
+ });
10
+ // Immutable version hosts carry their own target version: the intent is the
11
+ // COMPLETE declarative hostname map for the space (every retained ready
12
+ // version), so a route update can never orphan an older version URL.
13
+ const versionHostnameEntrySchema = z.object({
14
+ hostname: z.string().min(1),
15
+ version_id: z.string().min(1),
16
+ });
17
+ const proxyHostRouteEntrySchema = z.object({
18
+ hostname: z.string().min(1),
19
+ path_prefix: z.string().min(1).default("/"),
20
+ upstream: z.string().url(),
21
+ noindex: z.boolean().default(false),
22
+ });
23
+ const runtimeRouteIntentSchema = z.object({
24
+ production_hostnames: z.array(z.string().min(1)).default([]),
25
+ noindex_production_hostnames: z.array(z.string().min(1)).default([]),
26
+ version_hostnames: z.array(versionHostnameEntrySchema).default([]),
27
+ host_canonical_redirects: z.array(hostCanonicalRedirectSchema).default([]),
28
+ proxy_host_routes: z.array(proxyHostRouteEntrySchema).default([]),
29
+ });
30
+ // Changed-path purge contract (internal-docs/platform.md "Cache Management"):
31
+ // finalize computes the changed-path set against the previous pointer target
32
+ // — expanded to canonical request paths (leading slash, directory-index
33
+ // variants included) — and carries it through the runtime callback journal;
34
+ // the callback receiver translates journal events into edge purges scoped to
35
+ // changed paths, never a blanket purge when a diff exists. Past this cap (or
36
+ // when no previous pointer target exists) the control plane omits the set and
37
+ // purge handling falls back to one host-wide purge.
38
+ export const RUNTIME_CHANGED_PATHS_MAX = 500;
39
+ const changedPathsSchema = z
40
+ .array(z
41
+ .string()
42
+ .min(1)
43
+ .max(2048)
44
+ .describe("Canonical request path (decoded, leading slash) whose served bytes changed."))
45
+ .max(RUNTIME_CHANGED_PATHS_MAX)
46
+ .optional()
47
+ .describe("Request paths changed vs the previous pointer target; absent = no diff available (host-wide purge fallback). Echoed verbatim into the route_updated journal event.");
48
+ const previousVersionIdSchema = z
49
+ .string()
50
+ .min(1)
51
+ .optional()
52
+ .describe("The pointer target this activation replaces (diff baseline).");
53
+ export const runtimeVersionCreateRequestSchema = z.object({
54
+ version_id: z.string().min(1).optional(),
55
+ files: z.array(manifestFileSchema),
56
+ retained_files: z.array(manifestFileSchema).default([]),
57
+ reusable_version_id: z.string().min(1).optional(),
58
+ // "declared" sessions validate every PUT against the declared manifest.
59
+ // "open" sessions (internal-docs/platform.md "Upload Contract" shape 2) have
60
+ // no manifest: the runtime enforces path policy and the session caps per
61
+ // PUT, computes streaming hashes, and finalize derives the manifest.
62
+ // Plan-policy caps ride in session state, never in the upload JWT, and are
63
+ // valid only on open sessions.
64
+ session_mode: z.enum(["declared", "open"]).default("declared"),
65
+ max_total_bytes: z.number().int().positive().optional(),
66
+ max_file_count: z.number().int().positive().optional(),
67
+ // Session state stores the upload truth (spec "Upload Contract"): the version's
68
+ // manifest hash and the draft expiry so resume answers come from runtime state.
69
+ manifest_hash: z.string().optional(),
70
+ expires_at: z.string().optional(),
71
+ metadata: z.record(z.string(), z.unknown()).default({}),
72
+ convention_files: runtimeConventionFilesSchema.default({}),
73
+ });
74
+ export const runtimeVersionCreateResponseSchema = z.object({
75
+ space_id: z.string(),
76
+ version_id: z.string(),
77
+ upload_id: z.string(),
78
+ created_at: z.string(),
79
+ });
80
+ // Runtime-truth upload session state (spec "Upload Contract"): the resume flow asks
81
+ // the runtime which declared files/chunks are actually present instead of guessing
82
+ // from control-plane pending-upload state.
83
+ export const runtimeUploadSessionResponseSchema = z.object({
84
+ space_id: z.string(),
85
+ version_id: z.string(),
86
+ upload_id: z.string(),
87
+ session_mode: z.enum(["declared", "open"]),
88
+ created_at: z.string().nullable(),
89
+ expires_at: z.string().nullable(),
90
+ manifest_hash: z.string().nullable(),
91
+ files: z.array(z.object({
92
+ path: z.string(),
93
+ size: z.number().int().nonnegative(),
94
+ sha256: z.string().optional(),
95
+ uploaded: z.boolean(),
96
+ })),
97
+ // Declared paths the runtime has not committed yet (empty for open sessions).
98
+ pending_paths: z.array(z.string()),
99
+ // Staged chunked-upload parts by canonical path (contiguity is checked at complete).
100
+ chunks: z.record(z.string(), z.array(z.number().int().positive())),
101
+ });
102
+ // Route-pointer config is pure serving policy: THE resolved access policy plus
103
+ // the anonymous claim countdown. Space passwords are NOT a separate config
104
+ // channel — they ride inside `policy.rules` as a password challenge rule (one
105
+ // access model, spec "Runtime"). Per-version serving behavior
106
+ // (index/fallback/listing) is a version artifact concern, never route config.
107
+ const runtimeRouteConfigSchema = z.object({
108
+ // THE unified policy lane ({ rules: RuntimeRule[] }) the runtime enforcer
109
+ // (access-rules.php) evaluates first-match-wins for every host entry this
110
+ // space serves — route hosts, custom domains, and immutable version hosts
111
+ // alike. Carries platform + file + cloud rules (platform prepended, always
112
+ // wins). Explicit null clears the stored policy; absence leaves it untouched.
113
+ policy: policyDocSchema.nullable().optional(),
114
+ // Serving-secret map (name -> value) the policy's password rules reference by
115
+ // `auth.password.ref` = "secret:<name>". Carries the space-password bcrypt
116
+ // verifier hash the runtime resolves and `password_verify`s. Explicit null
117
+ // clears stored secrets; absence leaves them untouched.
118
+ secrets: z.record(z.string().min(1), z.string().min(1)).nullable().optional(),
119
+ // Anonymous claim-window countdown (ISO timestamp), compiled into serving
120
+ // state so the runtime can render the expiry-rescue banner for visitors
121
+ // holding the claim-link viewer session. Null/absent once claimed.
122
+ anonymous_expires_at: z.string().nullable().optional(),
123
+ });
124
+ // Compiled SpaceConfig serving primitives (spec "Serving resolution"): the finalize
125
+ // compiler resolves content defaults -> sf.jsonc -> space overlay into this shape;
126
+ // the runtime stores it on the version's serving artifact and never re-infers. When
127
+ // absent, the runtime infers content defaults (root index.html => website-style,
128
+ // otherwise artifact-style listing + viewer).
129
+ const runtimeServingConfigSchema = z.object({
130
+ index: z
131
+ .union([z.string().min(1), z.literal(false)])
132
+ .optional()
133
+ .describe("Directory index file name, or false to disable directory index resolution."),
134
+ fallback: z
135
+ .object({
136
+ path: z.string().min(1).describe("Committed file served when no exact route matches."),
137
+ status: z
138
+ .union([z.literal(200), z.literal(404)])
139
+ .describe("Response status for the fallback; 200 with /index.html is the SPA shape."),
140
+ })
141
+ .nullable()
142
+ .optional(),
143
+ listing: z
144
+ .boolean()
145
+ .optional()
146
+ .describe("Generate directory-listing pages for directories without an index file."),
147
+ viewer: z
148
+ .boolean()
149
+ .optional()
150
+ .describe("Generate the single-file viewer page when a version has exactly one public file."),
151
+ meta: z
152
+ .object({
153
+ title: z.string().max(300).optional().describe("Listing/viewer/OG title."),
154
+ description: z.string().max(1000).optional().describe("Listing/viewer/OG description."),
155
+ image: z.string().max(2000).optional().describe("OG image path."),
156
+ })
157
+ .nullable()
158
+ .optional()
159
+ .describe("Finalize-resolved listing/viewer/OG metadata (effective SpaceConfig meta); wins over session metadata."),
160
+ // Hidden experimental build-pipeline flag: absent on every version finalized
161
+ // without it (byte-identical payload to today). Only ever sent as true.
162
+ experimental_gutenberg: z.boolean().optional(),
163
+ // Platform-owned head metadata injection for normal HTML finalization. This is
164
+ // distinct from the hidden user build-pipeline flag: it only enables missing
165
+ // title/description/OG/Twitter tags derived from finalized SpaceConfig meta.
166
+ platform_meta: z.boolean().optional(),
167
+ // Platform-owned same-host SDK bootstrap metadata. The public Cast base is
168
+ // finalized with the version so locally-created remote runtimes do not guess
169
+ // between local/prod; resource identity is also per-version because one
170
+ // runtime/Atomic can serve many spaces.
171
+ spacefast_sdk: z
172
+ .object({
173
+ cast_api_base: z.string().url().max(2000).nullable().optional(),
174
+ cast_ws_url: z.string().url().max(2000).nullable().optional(),
175
+ cast_resource_key: z.string().min(1).max(256).nullable().optional(),
176
+ })
177
+ .optional(),
178
+ // CP-validated inject snippets (<= 8 KB each, <= 16 per array); sent only
179
+ // when experimental_gutenberg is true. Char caps here are a sanity bound —
180
+ // the byte caps are enforced by the shared SpaceConfig validator.
181
+ inject: z
182
+ .object({
183
+ head: z.array(z.string().max(8192)).max(16).optional(),
184
+ bodyStart: z.array(z.string().max(8192)).max(16).optional(),
185
+ bodyEnd: z.array(z.string().max(8192)).max(16).optional(),
186
+ noscript: z.array(z.string().max(8192)).max(16).optional(),
187
+ })
188
+ .optional(),
189
+ });
190
+ const runtimeZeroEndpointCapabilitiesSchema = z
191
+ .object({
192
+ db: z.boolean().optional(),
193
+ fetch: z.boolean().optional(),
194
+ auth: z.boolean().optional(),
195
+ env: z.boolean().optional(),
196
+ realtime: z.boolean().optional(),
197
+ })
198
+ .default({});
199
+ const runtimeZeroEndpointSchema = z.object({
200
+ method: z.enum(["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"]),
201
+ path: z.string().min(1).max(2048),
202
+ source: z
203
+ .string()
204
+ .min(1)
205
+ .max(2 * 1024 * 1024),
206
+ endpoint_id: z.string().min(1).max(256).optional(),
207
+ schema_hash: z.string().min(1).max(256).optional(),
208
+ capabilities: runtimeZeroEndpointCapabilitiesSchema.optional(),
209
+ db: z.record(z.string(), z.unknown()).optional(),
210
+ });
211
+ const runtimeZeroRunSchema = z.object({
212
+ run_id: z.string().min(1).max(256),
213
+ source: z
214
+ .string()
215
+ .min(1)
216
+ .max(2 * 1024 * 1024),
217
+ schema_hash: z.string().min(1).max(256).optional(),
218
+ capabilities: runtimeZeroEndpointCapabilitiesSchema.optional(),
219
+ db: z.record(z.string(), z.unknown()).optional(),
220
+ });
221
+ export const runtimeVersionFinalizeRequestSchema = z.object({
222
+ upload_id: z.string().min(1),
223
+ // The version's authoritative ready timestamp (epoch seconds), passed by the
224
+ // control plane when the version already reached ready (re-finalize, import
225
+ // re-materialization, provider migration). The engine stamps it ONCE into the
226
+ // version metadata; access-window rules (windowDays) compare against this
227
+ // stored value — never a filesystem mtime that resets on rebuilds.
228
+ ready_at: z.number().int().positive().optional(),
229
+ convention_files: runtimeConventionFilesSchema.optional(),
230
+ // Finalize-time `{{ vars.NAME }}` substitution output (spec "Variable
231
+ // rules"): substituted bytes replace the served files; the engine keeps the
232
+ // pre-substitution bytes in `files-original/` so declared-sha dedup still
233
+ // verifies upload identity. Keyed by canonical version-relative path.
234
+ template_files: z
235
+ .record(z.string().min(1).describe("Canonical committed path declared in `templates`."), z
236
+ .string()
237
+ .max(2 * 1024 * 1024)
238
+ .describe("Substituted file contents (secret values never reach this payload)."))
239
+ .refine((files) => Object.keys(files).length <= 100, {
240
+ message: "template_files supports up to 100 entries.",
241
+ })
242
+ .optional()
243
+ .describe("Substituted template contents applied over the committed tree at finalize."),
244
+ // Per-channel template variants (spec "Per-channel values"): channelValues
245
+ // overrides compile channel-specific contents of declared template files as
246
+ // channel-scoped serving artifacts. Keyed by RUNTIME ROUTE NAME (route names
247
+ // are runtime-only vocabulary; the live channel serves via `production`).
248
+ // Variants serve only on the route's hosts — permanent Version URLs always
249
+ // serve the version's own finalize-resolved values.
250
+ template_variants: z
251
+ .record(z
252
+ .string()
253
+ .min(1)
254
+ .max(128)
255
+ .regex(/^[A-Za-z0-9._-]+$/)
256
+ .describe("Runtime route name this variant set serves."), z
257
+ .record(z.string().min(1).describe("Canonical committed path declared in `templates`."), z
258
+ .string()
259
+ .max(2 * 1024 * 1024)
260
+ .describe("Variant file contents (secret values never reach this payload)."))
261
+ .refine((files) => Object.keys(files).length <= 100, {
262
+ message: "template_variants supports up to 100 entries per route.",
263
+ }))
264
+ .refine((routes) => Object.keys(routes).length <= 8, {
265
+ message: "template_variants supports up to 8 routes.",
266
+ })
267
+ .optional()
268
+ .describe("Channel-variant template contents, keyed by runtime route name."),
269
+ serving: z
270
+ .object({
271
+ headers_exact: z.record(z.string(), z.array(z.record(z.string(), z.unknown()))).default({}),
272
+ headers_pattern: z.array(z.record(z.string(), z.unknown())).default([]),
273
+ redirects_exact: z.record(z.string(), z.array(z.record(z.string(), z.unknown()))).default({}),
274
+ redirects_pattern: z.array(z.record(z.string(), z.unknown())).default([]),
275
+ config: runtimeServingConfigSchema.optional(),
276
+ // Compiled SpaceTheme custom-property declarations consumed by themeable pages
277
+ // (directory listings, single-file viewers, platform pages). The finalize
278
+ // compiler validates the token grammar; the runtime re-rejects anything that
279
+ // could escape a <style> element or reach the network.
280
+ theme_css: z.string().max(16384).optional(),
281
+ // Trusted Zero endpoint source compiled by the runtime into private
282
+ // endpoint-specific JS, QuickJS bytecode, and PHP manifest invoke actions.
283
+ zero_endpoints: z.array(runtimeZeroEndpointSchema).max(128).optional(),
284
+ // Trusted Zero run-handler source compiled into private artifacts for
285
+ // non-HTTP Zero execution surfaces. These are not PHP route actions.
286
+ zero_runs: z.array(runtimeZeroRunSchema).max(128).optional(),
287
+ })
288
+ .optional(),
289
+ activate: z
290
+ .object({
291
+ route_name: z
292
+ .string()
293
+ .min(1)
294
+ .default("production")
295
+ .describe("Runtime route-pointer to activate; route names are runtime-only vocabulary."),
296
+ config: runtimeRouteConfigSchema.optional(),
297
+ changed_paths: changedPathsSchema,
298
+ previous_version_id: previousVersionIdSchema,
299
+ production_hostnames: runtimeRouteIntentSchema.shape.production_hostnames,
300
+ noindex_production_hostnames: runtimeRouteIntentSchema.shape.noindex_production_hostnames,
301
+ version_hostnames: runtimeRouteIntentSchema.shape.version_hostnames,
302
+ host_canonical_redirects: runtimeRouteIntentSchema.shape.host_canonical_redirects,
303
+ proxy_host_routes: runtimeRouteIntentSchema.shape.proxy_host_routes,
304
+ })
305
+ .optional(),
306
+ });
307
+ export const runtimeVersionFinalizeResponseSchema = z.object({
308
+ space_id: z.string(),
309
+ version_id: z.string(),
310
+ status: z.literal("ready"),
311
+ // Open sessions: the manifest derived from what was actually uploaded
312
+ // (path, size, streaming sha256). Absent for declared sessions.
313
+ manifest: z.array(manifestFileSchema).optional(),
314
+ });
315
+ export const runtimeRouteUpdateRequestSchema = z.object({
316
+ version_id: z.string().min(1),
317
+ config: runtimeRouteConfigSchema.optional(),
318
+ changed_paths: changedPathsSchema,
319
+ previous_version_id: previousVersionIdSchema,
320
+ production_hostnames: runtimeRouteIntentSchema.shape.production_hostnames,
321
+ noindex_production_hostnames: runtimeRouteIntentSchema.shape.noindex_production_hostnames,
322
+ version_hostnames: runtimeRouteIntentSchema.shape.version_hostnames,
323
+ host_canonical_redirects: runtimeRouteIntentSchema.shape.host_canonical_redirects,
324
+ proxy_host_routes: runtimeRouteIntentSchema.shape.proxy_host_routes,
325
+ });
326
+ export const runtimeRouteUpdateResponseSchema = z.object({
327
+ space_id: z.string(),
328
+ route_name: z.string(),
329
+ version_id: z.string(),
330
+ });
331
+ export const runtimeTombstonesUpdateRequestSchema = z.object({
332
+ hostnames: runtimeRouteIntentSchema.shape.production_hostnames,
333
+ mode: z.enum(["replace", "add", "remove"]).default("replace"),
334
+ // Optional reason/category drive a differentiated tombstone variant on the
335
+ // runtime (e.g. csam -> bare 404, copyright -> 451, suspension -> 402). The
336
+ // runtime falls back to a generic 404 when both are absent, so these are
337
+ // additive and back-compatible.
338
+ reason: z.string().optional(),
339
+ category: z.string().optional(),
340
+ });
341
+ export const runtimeTombstonesUpdateResponseSchema = z.object({
342
+ space_id: z.string(),
343
+ tombstone_count: z.number().int().nonnegative(),
344
+ });
345
+ export const runtimeSpaceDeleteResponseSchema = z.object({
346
+ space_id: z.string(),
347
+ status: z.literal("deleted"),
348
+ });
349
+ export const runtimeVersionDeleteResponseSchema = z.object({
350
+ space_id: z.string(),
351
+ version_id: z.string(),
352
+ status: z.literal("deleted"),
353
+ });
354
+ export const runtimeSpaceRepairResponseSchema = z.object({
355
+ space_id: z.string(),
356
+ status: z.literal("repaired"),
357
+ });
358
+ const runtimeExportImportStatusSchema = z.enum([
359
+ "waiting_for_archive",
360
+ "pending",
361
+ "running",
362
+ "complete",
363
+ ]);
364
+ export const runtimeSpaceExportStartRequestSchema = z
365
+ .object({
366
+ version_ids: z.array(z.string().min(1)).min(1).optional(),
367
+ // Honest export options (spec SpaceExportRequest): include_files=false
368
+ // produces a config-only archive; include_explicit_config=false skips the
369
+ // space access policy entry. Recorded in stattic.json `includes`.
370
+ include_files: z.boolean().optional(),
371
+ include_explicit_config: z.boolean().optional(),
372
+ })
373
+ .strict();
374
+ export const runtimeSpaceExportResponseSchema = z.object({
375
+ type: z.literal("space_export"),
376
+ export_id: z.string(),
377
+ space_id: z.string(),
378
+ status: runtimeExportImportStatusSchema,
379
+ cursor: z.number().int().nonnegative(),
380
+ processed_files: z.number().int().nonnegative(),
381
+ total_files: z.number().int().nonnegative(),
382
+ version_ids: z.array(z.string()),
383
+ archive_size: z.number().int().nonnegative().optional(),
384
+ // SHA-256 of the finished archive (64 lowercase hex), computed once at
385
+ // completion so the control plane can expose SpaceExport.archive.sha256.
386
+ archive_sha256: z.string().optional(),
387
+ created_at: z.string(),
388
+ updated_at: z.string(),
389
+ completed_at: z.string().optional(),
390
+ });
391
+ export const runtimeSpaceImportStartRequestSchema = z
392
+ .object({
393
+ version_id_map: z.record(z.string(), z.string()).optional(),
394
+ })
395
+ .strict();
396
+ // Deferred version-id mapping: control-plane-driven uploads land on the
397
+ // runtime before the control plane has read the archive, so the freshly
398
+ // minted target ids are supplied here before the first step.
399
+ export const runtimeSpaceImportMapRequestSchema = z
400
+ .object({
401
+ version_id_map: z.record(z.string(), z.string()),
402
+ })
403
+ .strict();
404
+ export const runtimeSpaceImportResponseSchema = z.object({
405
+ type: z.literal("space_import"),
406
+ import_id: z.string(),
407
+ space_id: z.string(),
408
+ status: runtimeExportImportStatusSchema,
409
+ cursor: z.number().int().nonnegative(),
410
+ processed_files: z.number().int().nonnegative(),
411
+ total_files: z.number().int().nonnegative(),
412
+ version_ids: z.array(z.string()),
413
+ created_at: z.string(),
414
+ updated_at: z.string(),
415
+ completed_at: z.string().optional(),
416
+ });
417
+ // Per-space generation state reported by the authed state endpoint. The
418
+ // reconciliation sweep compares this against control-plane expectations
419
+ // (space liveVersionId vs the runtime's route pointers) and repairs targeted
420
+ // mismatches — never a blanket re-sync (internal-docs/platform.md "Cache
421
+ // Management": missed callbacks degrade to bounded staleness).
422
+ export const runtimeSpaceStateSchema = z.object({
423
+ space_id: z.string().min(1).describe("Space whose runtime route pointers follow."),
424
+ routes: z
425
+ .record(z.string(), z.string())
426
+ .describe("Route pointer map: route name -> version id currently compiled for serving."),
427
+ tombstone_count: z
428
+ .number()
429
+ .int()
430
+ .nonnegative()
431
+ .describe("Hostnames the runtime serves a tombstone page for on this space."),
432
+ });
433
+ export const runtimeHealthResponseSchema = z.object({
434
+ ok: z.literal(true),
435
+ runtime: z.literal("stattic-php"),
436
+ schema: z.string().min(1).optional(),
437
+ engine_version: z.string().min(1).optional(),
438
+ engine_revision: z
439
+ .string()
440
+ .min(1)
441
+ .optional()
442
+ .describe("Runtime engine bundle revision. Missing means the engine predates revision health."),
443
+ site_state: z.string().min(1).optional(),
444
+ request_hostname: z.string().optional(),
445
+ });
446
+ export const runtimeStateResponseSchema = z.object({
447
+ ok: z.literal(true),
448
+ runtime: z.literal("stattic-php"),
449
+ engine_revision: z
450
+ .string()
451
+ .min(1)
452
+ .optional()
453
+ .describe("Runtime engine bundle revision. Missing means the engine predates revision state."),
454
+ // Optional generation-state fields (absent on engines predating the reconcile
455
+ // handshake; the sweep treats absence as "cannot compare").
456
+ routes_generation: z
457
+ .string()
458
+ .nullable()
459
+ .optional()
460
+ .describe("Active route-index generation id (routes/current.php pointer)."),
461
+ spaces: z
462
+ .array(runtimeSpaceStateSchema)
463
+ .optional()
464
+ .describe("Per-space route-pointer state for reconcile comparison."),
465
+ });
466
+ // Runtime callback journal event (internal-docs/platform.md "Cache
467
+ // Management"): the engine journals every management mutation and delivers it
468
+ // at-least-once to the control-plane callback receiver, which translates
469
+ // serving-affecting events into provider edge purges. `event_id` is the stable
470
+ // journal id — purge enqueues are idempotent on (event_id, hostname) so
471
+ // replays are no-ops. The object is intentionally loose: engines may journal
472
+ // extra diagnostic fields without breaking older control planes.
473
+ export const runtimeCallbackEventSchema = z.looseObject({
474
+ event: z.string().min(1).describe("Journal event name (e.g. route_updated, space_deleted)."),
475
+ event_id: z
476
+ .string()
477
+ .min(1)
478
+ .optional()
479
+ .describe("Stable journal/callback id; purge handling replays as no-ops on this id."),
480
+ operation_id: z
481
+ .string()
482
+ .optional()
483
+ .describe("Control-plane operation the management call ran under."),
484
+ space_id: z.string().optional().describe("Space the event applies to, when space-scoped."),
485
+ version_id: z.string().optional().describe("Version the event applies to, when version-scoped."),
486
+ route_name: z.string().optional().describe("Route pointer the event updated, when route-scoped."),
487
+ hostnames: z
488
+ .array(z.string().min(1))
489
+ .optional()
490
+ .describe("Hostnames whose serving output this event changed; purge targets."),
491
+ changed_paths: changedPathsSchema,
492
+ previous_version_id: previousVersionIdSchema,
493
+ });
494
+ export const runtimeEventsDrainResponseSchema = z.object({
495
+ delivered_count: z.number().int().nonnegative(),
496
+ failed_count: z.number().int().nonnegative(),
497
+ expired_count: z
498
+ .number()
499
+ .int()
500
+ .nonnegative()
501
+ .optional()
502
+ .describe("Undeliverable callbacks dropped after exceeding the attempt cap."),
503
+ returned_count: z.number().int().nonnegative().optional(),
504
+ // Pull fallback: events whose push origin was unreachable are handed back to
505
+ // the drain caller (the control plane) instead of rotting in the journal.
506
+ // Processing replays are no-ops on (event_id, hostname).
507
+ events: z
508
+ .array(z.object({
509
+ operation_id: z.string().optional(),
510
+ event: runtimeCallbackEventSchema,
511
+ }))
512
+ .optional(),
513
+ });