@spacefast/common 0.0.23 → 0.0.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config/domains.d.ts +1 -1
- package/dist/config/domains.js +1 -0
- package/dist/config/index.d.ts +0 -1
- package/dist/config/index.js +0 -1
- package/dist/contracts/abuse.d.ts +2 -0
- package/dist/contracts/abuse.js +12 -0
- package/dist/contracts/access.d.ts +121 -9
- package/dist/contracts/access.js +73 -9
- package/dist/contracts/activity.d.ts +1 -1
- package/dist/contracts/activity.js +0 -9
- package/dist/contracts/api-keys.d.ts +21 -21
- package/dist/contracts/beta.js +1 -1
- package/dist/contracts/billing.d.ts +1 -34
- package/dist/contracts/billing.js +4 -53
- package/dist/contracts/builds.d.ts +8 -8
- package/dist/contracts/builds.js +10 -3
- package/dist/contracts/cli.d.ts +10 -0
- package/dist/contracts/cli.js +10 -0
- package/dist/contracts/collab-public.d.ts +63 -0
- package/dist/contracts/collab-public.js +46 -0
- package/dist/contracts/comments.d.ts +283 -54
- package/dist/contracts/comments.js +173 -52
- package/dist/contracts/common.d.ts +3 -3
- package/dist/contracts/device-auth.d.ts +6 -6
- package/dist/contracts/device-auth.js +1 -1
- package/dist/contracts/docs.d.ts +5 -5
- package/dist/contracts/domains.d.ts +36 -36
- package/dist/contracts/enums.d.ts +33 -30
- package/dist/contracts/enums.js +52 -38
- package/dist/contracts/error-code-meta.d.ts +37 -126
- package/dist/contracts/error-code-meta.js +12 -41
- package/dist/contracts/error-codes.d.ts +1 -1
- package/dist/contracts/error-codes.js +10 -32
- package/dist/contracts/execution.d.ts +35 -57
- package/dist/contracts/execution.js +41 -40
- package/dist/contracts/feature-lifecycle-core.d.ts +0 -6
- package/dist/contracts/feature-lifecycle-core.js +2 -31
- package/dist/contracts/feature-lifecycle.d.ts +8 -38
- package/dist/contracts/feature-lifecycle.js +16 -48
- package/dist/contracts/features.d.ts +15 -13
- package/dist/contracts/features.js +39 -38
- package/dist/contracts/functions.d.ts +97 -70
- package/dist/contracts/functions.js +101 -34
- package/dist/contracts/git.d.ts +1 -1
- package/dist/contracts/grants.d.ts +7 -3
- package/dist/contracts/grants.js +18 -9
- package/dist/contracts/internal.d.ts +14 -1
- package/dist/contracts/internal.js +17 -7
- package/dist/contracts/oauth-resources.d.ts +4 -3
- package/dist/contracts/oauth-resources.js +12 -3
- package/dist/contracts/operations.d.ts +90 -5
- package/dist/contracts/operations.js +75 -1
- package/dist/contracts/pages.d.ts +6 -1
- package/dist/contracts/pages.js +24 -1
- package/dist/contracts/plan-policy.d.ts +1 -3
- package/dist/contracts/plan-policy.js +1 -2
- package/dist/contracts/platform.d.ts +61 -148
- package/dist/contracts/platform.js +70 -126
- package/dist/contracts/publish-archive.d.ts +498 -0
- package/dist/contracts/publish-archive.js +25 -0
- package/dist/contracts/resources.d.ts +12 -24
- package/dist/contracts/resources.js +32 -12
- package/dist/contracts/runtime-api.d.ts +369 -175
- package/dist/contracts/runtime-api.js +372 -348
- package/dist/contracts/runtime-app.d.ts +127 -120
- package/dist/contracts/runtime-app.js +19 -10
- package/dist/contracts/runtime-db.d.ts +1 -1
- package/dist/contracts/runtime-services.d.ts +219 -0
- package/dist/contracts/runtime-services.js +437 -0
- package/dist/contracts/runtime-storage.d.ts +45 -32
- package/dist/contracts/runtime-storage.js +30 -8
- package/dist/contracts/space-config.d.ts +10 -16
- package/dist/contracts/space-config.js +14 -10
- package/dist/contracts/spaces.d.ts +241 -436
- package/dist/contracts/spaces.js +128 -145
- package/dist/contracts/superadmin-activity.d.ts +57 -4
- package/dist/contracts/superadmin-activity.js +48 -2
- package/dist/contracts/superadmin-search.d.ts +126 -0
- package/dist/contracts/superadmin-search.js +61 -0
- package/dist/contracts/superadmin-spaces.d.ts +358 -89
- package/dist/contracts/superadmin-spaces.js +97 -24
- package/dist/contracts/superadmin-teams.d.ts +28 -10
- package/dist/contracts/superadmin-teams.js +5 -8
- package/dist/contracts/superadmin.d.ts +0 -32
- package/dist/contracts/superadmin.js +3 -11
- package/dist/contracts/tags.d.ts +9 -9
- package/dist/contracts/teams.d.ts +38 -0
- package/dist/contracts/teams.js +34 -16
- package/dist/contracts/transfers.d.ts +0 -49
- package/dist/contracts/transfers.js +2 -6
- package/dist/contracts/usage.d.ts +61 -0
- package/dist/contracts/usage.js +64 -0
- package/dist/contracts/variables.d.ts +2 -7
- package/dist/contracts/variables.js +6 -5
- package/dist/contracts/webhooks.d.ts +2 -2
- package/dist/contracts/zero.d.ts +87 -56
- package/dist/contracts/zero.js +82 -20
- package/dist/dashboard-paths/index.d.ts +1 -1
- package/dist/dashboard-paths/index.js +1 -0
- package/dist/docs/agent-prose.d.ts +1 -1
- package/dist/docs/agent-prose.js +57 -61
- package/dist/docs/agent-setup.js +40 -13
- package/dist/docs/catalog.d.ts +19 -19
- package/dist/docs/catalog.js +30 -19
- package/dist/docs/error-docs.js +63 -151
- package/dist/test-helpers/fetch-stub.d.ts +16 -0
- package/dist/test-helpers/fetch-stub.js +24 -0
- package/dist/utils/build-settings.js +9 -3
- package/dist/utils/canonical-json.d.ts +10 -0
- package/dist/utils/canonical-json.js +21 -0
- package/dist/utils/cast-transport.d.ts +28 -0
- package/dist/utils/cast-transport.js +52 -0
- package/dist/utils/claim-token.d.ts +0 -8
- package/dist/utils/claim-token.js +3 -15
- package/dist/utils/credential-policy.d.ts +1 -1
- package/dist/utils/env-file.d.ts +17 -0
- package/dist/utils/env-file.js +96 -0
- package/dist/utils/error-code.d.ts +2 -0
- package/dist/utils/error-code.js +7 -0
- package/dist/utils/generate-space-name.d.ts +8 -10
- package/dist/utils/generate-space-name.js +206 -21
- package/dist/utils/grant-decision.fixtures.json +2938 -0
- package/dist/utils/grants.d.ts +2 -0
- package/dist/utils/grants.js +25 -2
- package/dist/utils/gravatar.d.ts +9 -0
- package/dist/utils/gravatar.js +31 -0
- package/dist/utils/idempotency.d.ts +6 -0
- package/dist/utils/idempotency.js +28 -0
- package/dist/utils/local-space-state.d.ts +1 -5
- package/dist/utils/local-space-state.js +7 -26
- package/dist/utils/page-fonts.d.ts +2 -2
- package/dist/utils/page-fonts.js +22 -4
- package/dist/utils/pages.d.ts +1 -2
- package/dist/utils/pages.js +21 -5
- package/dist/utils/publish-form-data.d.ts +12 -0
- package/dist/utils/publish-form-data.js +10 -0
- package/dist/utils/publish-policy.d.ts +20 -0
- package/dist/utils/publish-policy.fixtures.json +397 -0
- package/dist/utils/publish-policy.js +36 -19
- package/dist/utils/push-new.d.ts +7 -0
- package/dist/utils/push-new.js +7 -0
- package/dist/utils/query-keys.d.ts +0 -1
- package/dist/utils/query-keys.js +0 -1
- package/dist/utils/runtime-paths.d.ts +5 -0
- package/dist/utils/runtime-paths.js +5 -0
- package/dist/utils/runtime-upload-batch.d.ts +0 -5
- package/dist/utils/runtime-upload-batch.js +1 -27
- package/dist/utils/runtime-upload.d.ts +3 -13
- package/dist/utils/runtime-upload.js +4 -14
- package/dist/utils/space-config.d.ts +0 -33
- package/dist/utils/space-config.js +5 -326
- package/dist/utils/storage-policy.d.ts +1 -1
- package/dist/utils/storage-policy.js +3 -1
- package/dist/utils/structured-error.d.ts +14 -0
- package/dist/utils/structured-error.js +22 -0
- package/dist/utils/version-path.js +12 -3
- package/dist/vocabulary.d.ts +5 -3
- package/dist/vocabulary.js +13 -10
- package/package.json +10 -5
- package/dist/config/postgres-budget.d.ts +0 -129
- package/dist/config/postgres-budget.js +0 -140
- package/dist/config/postgres-budget.json +0 -51
- package/dist/contracts/archives.d.ts +0 -331
- package/dist/contracts/archives.js +0 -120
- package/dist/contracts/deployments.d.ts +0 -574
- package/dist/contracts/deployments.js +0 -97
- package/dist/contracts/sites.d.ts +0 -416
- package/dist/contracts/sites.js +0 -168
- package/dist/contracts/superadmin-queues.d.ts +0 -414
- package/dist/contracts/superadmin-queues.js +0 -202
- package/dist/contracts/superadmin-runtime.d.ts +0 -27
- package/dist/contracts/superadmin-runtime.js +0 -38
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/utils/api.d.ts +0 -4
- package/dist/utils/api.js +0 -13
|
@@ -2,38 +2,27 @@ import { z } from "zod";
|
|
|
2
2
|
import { accessFenceKindSchema } from "./access.js";
|
|
3
3
|
import { functionsRuntimeConfigSchema } from "./functions.js";
|
|
4
4
|
import { grantSchema } from "./grants.js";
|
|
5
|
-
import {
|
|
5
|
+
import { manifestFileSchema } from "./internal.js";
|
|
6
6
|
import { runtimePagesSchema } from "./pages.js";
|
|
7
|
+
import { SPACE_CONFIG_CAPS } from "./space-config.js";
|
|
7
8
|
import { zeroEndpointSchema, zeroRuntimeConfigSchema } from "./zero.js";
|
|
8
|
-
const runtimeConventionFilesSchema = conventionFilesSchema.omit({ routes: true }).strict();
|
|
9
9
|
const hostCanonicalRedirectSchema = z.object({
|
|
10
10
|
from: z.string().min(1),
|
|
11
11
|
to: z.string().url(),
|
|
12
12
|
status: z.union([z.literal(301), z.literal(302), z.literal(307), z.literal(308)]).default(308),
|
|
13
13
|
});
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
// version), so a route update can never orphan an older version URL.
|
|
14
|
+
// The intent is the COMPLETE declarative hostname map for the space (every
|
|
15
|
+
// retained ready version), so a route update can never orphan a version URL.
|
|
17
16
|
const versionHostnameEntrySchema = z.object({
|
|
18
17
|
hostname: z.string().min(1),
|
|
19
18
|
version_id: z.string().min(1),
|
|
20
19
|
});
|
|
21
|
-
// Future expansion: this intent could support explicit path-to-artifact composition
|
|
22
|
-
// after independently deployable components have a real product model.
|
|
23
20
|
const runtimeRouteIntentSchema = z.object({
|
|
24
21
|
production_hostnames: z.array(z.string().min(1)).default([]),
|
|
25
22
|
noindex_production_hostnames: z.array(z.string().min(1)).default([]),
|
|
26
23
|
version_hostnames: z.array(versionHostnameEntrySchema).default([]),
|
|
27
24
|
host_canonical_redirects: z.array(hostCanonicalRedirectSchema).default([]),
|
|
28
25
|
});
|
|
29
|
-
// Changed-path purge contract (internal-docs/platform.md "Cache Management"):
|
|
30
|
-
// finalize computes the changed-path set against the previous pointer target
|
|
31
|
-
// — expanded to canonical request paths (leading slash, directory-index
|
|
32
|
-
// variants included) — and carries it through the runtime callback journal;
|
|
33
|
-
// the callback receiver translates journal events into edge purges scoped to
|
|
34
|
-
// changed paths, never a blanket purge when a diff exists. Past this cap (or
|
|
35
|
-
// when no previous pointer target exists) the control plane omits the set and
|
|
36
|
-
// purge handling falls back to one host-wide purge.
|
|
37
26
|
export const RUNTIME_CHANGED_PATHS_MAX = 900;
|
|
38
27
|
const changedPathsSchema = z
|
|
39
28
|
.array(z
|
|
@@ -43,23 +32,58 @@ const changedPathsSchema = z
|
|
|
43
32
|
.describe("Canonical request path (decoded, leading slash) whose served bytes changed."))
|
|
44
33
|
.max(RUNTIME_CHANGED_PATHS_MAX)
|
|
45
34
|
.optional()
|
|
46
|
-
.describe("Request paths changed from the previous pointer target. When absent,
|
|
35
|
+
.describe("Request paths changed from the previous pointer target. When absent, the runtime purges the whole domain.");
|
|
47
36
|
const previousVersionIdSchema = z
|
|
48
37
|
.string()
|
|
49
38
|
.min(1)
|
|
50
39
|
.optional()
|
|
51
40
|
.describe("The pointer target this activation replaces (diff baseline).");
|
|
41
|
+
export const runtimePurgeReceiptSchema = z.object({
|
|
42
|
+
status: z.enum(["ok", "failed", "pending"]),
|
|
43
|
+
mode: z.enum(["urls", "domain"]),
|
|
44
|
+
urls: z.number().int().nonnegative().optional(),
|
|
45
|
+
});
|
|
46
|
+
const runtimeMutationPurgeShape = {
|
|
47
|
+
purge: runtimePurgeReceiptSchema.optional(),
|
|
48
|
+
};
|
|
49
|
+
export const runtimeSpacePurgeRequestSchema = z.object({
|
|
50
|
+
urls: z.array(z.string().startsWith("/")).optional(),
|
|
51
|
+
});
|
|
52
|
+
export const runtimeSpacePurgeResponseSchema = z.object({
|
|
53
|
+
space_id: z.string().min(1),
|
|
54
|
+
purge: runtimePurgeReceiptSchema,
|
|
55
|
+
});
|
|
56
|
+
// One bounded page of a version's file catalog — the runtime's own answer to
|
|
57
|
+
// "which files does this version have". `view=source` is the uploaded,
|
|
58
|
+
// pre-substitution object; `view=served` is the immutable byte a visitor
|
|
59
|
+
// receives, and `variant_route` marks a channel's substituted bytes within it.
|
|
60
|
+
export const runtimeVersionFilesResponseSchema = z.object({
|
|
61
|
+
space_id: z.string().min(1),
|
|
62
|
+
version_id: z.string().min(1),
|
|
63
|
+
view: z.enum(["source", "served"]),
|
|
64
|
+
files: z.array(z.object({
|
|
65
|
+
path: z.string().min(1),
|
|
66
|
+
size: z.number().int().nonnegative(),
|
|
67
|
+
sha256: z.string().regex(/^[a-f0-9]{64}$/),
|
|
68
|
+
content_type: z.string().min(1).optional(),
|
|
69
|
+
public: z.boolean(),
|
|
70
|
+
variant_route: z.string().min(1).optional(),
|
|
71
|
+
})),
|
|
72
|
+
next_cursor: z.string().min(1).nullable().optional(),
|
|
73
|
+
});
|
|
52
74
|
export const runtimeVersionCreateRequestSchema = z.object({
|
|
53
75
|
version_id: z.string().min(1).optional(),
|
|
54
76
|
files: z.array(manifestFileSchema),
|
|
55
|
-
|
|
77
|
+
// Retention is expressed as "everything from `reusable_version_id`": naming
|
|
78
|
+
// the base and NO list is the retain-all signal, which the runtime
|
|
79
|
+
// materializes from that version's own catalog at finalize (under the space
|
|
80
|
+
// lock). An explicit list is the narrower form — it retains exactly these
|
|
81
|
+
// paths — and is what a publish that prunes the base uses.
|
|
82
|
+
retained_files: z.array(manifestFileSchema).optional(),
|
|
56
83
|
reusable_version_id: z.string().min(1).optional(),
|
|
57
|
-
// Session state stores the upload truth (spec "Upload Contract"): the version's
|
|
58
|
-
// manifest hash and the draft expiry so resume answers come from runtime state.
|
|
59
84
|
manifest_hash: z.string().optional(),
|
|
60
85
|
expires_at: z.string().optional(),
|
|
61
86
|
metadata: z.record(z.string(), z.unknown()).default({}),
|
|
62
|
-
convention_files: runtimeConventionFilesSchema.default({}),
|
|
63
87
|
});
|
|
64
88
|
export const runtimeVersionCreateResponseSchema = z.object({
|
|
65
89
|
space_id: z.string(),
|
|
@@ -75,26 +99,19 @@ export const runtimeUploadSessionDescriptorSchema = z.object({
|
|
|
75
99
|
manifest_hash: z.string().optional(),
|
|
76
100
|
files: z.array(manifestFileSchema),
|
|
77
101
|
// Retained files only travel in the authenticated finalize request: they are
|
|
78
|
-
// unbounded and unused while accepting a PUT.
|
|
102
|
+
// unbounded and unused while accepting a PUT. Omitting them beside a
|
|
103
|
+
// `reusable_version_id` is the retain-all signal (see the create request),
|
|
104
|
+
// which is also what keeps a lazy session's descriptor inside its JWT budget.
|
|
79
105
|
retained_files: z.array(manifestFileSchema).optional(),
|
|
80
106
|
reusable_version_id: z.string().min(1).optional(),
|
|
81
107
|
metadata: z.record(z.string(), z.unknown()).default({}),
|
|
82
|
-
convention_files: runtimeConventionFilesSchema.default({}),
|
|
83
108
|
});
|
|
84
|
-
// Candidate budget for the half that rides in the upload JWT (everything
|
|
85
|
-
// except the retained manifest). This bounds signing work, but is not itself a
|
|
86
|
-
// header-size proof: callback capabilities also ride in auto-finalize tokens.
|
|
87
109
|
export const RUNTIME_UPLOAD_SESSION_DESCRIPTOR_MAX_BYTES = 4096;
|
|
88
110
|
// Hard wire budget for the complete `Authorization: Bearer <jwt>\r\n` line sent
|
|
89
111
|
// on every PUT. Lazy-session selection measures the signed line and falls back
|
|
90
112
|
// to eager runtime creation rather than returning an oversized credential.
|
|
91
113
|
export const RUNTIME_UPLOAD_AUTHORIZATION_HEADER_MAX_BYTES = 8 * 1024;
|
|
92
|
-
// Budget for the whole descriptor, which is persisted on the version row and
|
|
93
|
-
// replayed inside the durable finalize receipt.
|
|
94
114
|
export const RUNTIME_UPLOAD_SESSION_STORED_MAX_BYTES = 256 * 1024;
|
|
95
|
-
// Runtime-truth upload session state (spec "Upload Contract"): the resume flow asks
|
|
96
|
-
// the runtime which declared files/chunks are actually present instead of guessing
|
|
97
|
-
// from control-plane pending-upload state.
|
|
98
115
|
export const runtimeUploadSessionResponseSchema = z.object({
|
|
99
116
|
space_id: z.string(),
|
|
100
117
|
version_id: z.string(),
|
|
@@ -110,12 +127,7 @@ export const runtimeUploadSessionResponseSchema = z.object({
|
|
|
110
127
|
})),
|
|
111
128
|
// Declared paths the runtime has not committed yet.
|
|
112
129
|
pending_paths: z.array(z.string()),
|
|
113
|
-
// Staged chunked-upload parts by canonical path (contiguity is checked at complete).
|
|
114
|
-
chunks: z.record(z.string(), z.array(z.number().int().positive())),
|
|
115
130
|
});
|
|
116
|
-
// Route-pointer config is pure serving policy plus the anonymous claim
|
|
117
|
-
// countdown. Per-version serving behavior (index/fallback/listing) remains a
|
|
118
|
-
// version artifact concern.
|
|
119
131
|
export const visitorJwkSchema = z.object({
|
|
120
132
|
kty: z.literal("OKP"),
|
|
121
133
|
crv: z.literal("Ed25519"),
|
|
@@ -136,20 +148,13 @@ export const runtimePublicExposureDescriptorSchema = z.object({
|
|
|
136
148
|
unmodeled: z.string(),
|
|
137
149
|
});
|
|
138
150
|
// Everything the runtime needs to render the visitor access page itself.
|
|
139
|
-
// Presentation-only: which lanes exist is derived runtime-side from the
|
|
140
|
-
// projected grants; this descriptor carries what the grants cannot — the
|
|
141
|
-
// space's display identity, the central endpoints each lane talks to, and the
|
|
142
|
-
// deterministic credential that authenticates the runtime's own
|
|
143
|
-
// server-to-server exchange calls (password, verified email, request-invite)
|
|
144
|
-
// so the central authority may trust the visitor context the runtime forwards.
|
|
145
151
|
export const runtimeAccessPageDescriptorSchema = z
|
|
146
152
|
.object({
|
|
147
153
|
displayName: z.string().min(1).max(200).nullable(),
|
|
148
154
|
// API-origin account lane (`/v1/access/acquire/<target>`), null until the
|
|
149
155
|
// space is claimed. Doubles as the silent-SSO probe target.
|
|
150
156
|
accountUrl: z.string().url().nullable(),
|
|
151
|
-
//
|
|
152
|
-
// `host`/`return` to `startUrl` at render time.
|
|
157
|
+
// The runtime appends `host`/`return` to `startUrl` at render time.
|
|
153
158
|
connections: z.array(z.object({
|
|
154
159
|
id: z.string().min(1).max(64),
|
|
155
160
|
label: z.string().min(1).max(200),
|
|
@@ -158,15 +163,11 @@ export const runtimeAccessPageDescriptorSchema = z
|
|
|
158
163
|
exchange: z
|
|
159
164
|
.object({
|
|
160
165
|
passwordUrl: z.string().url(),
|
|
161
|
-
// Browser entry links and integration credentials are exchanged over
|
|
162
|
-
// separate runtime-authenticated lanes. Neither credential reaches
|
|
163
|
-
// customer code.
|
|
164
166
|
linkUrl: z.string().url(),
|
|
165
167
|
tokenUrl: z.string().url(),
|
|
166
168
|
emailUrl: z.string().url(),
|
|
167
169
|
requestUrl: z.string().url(),
|
|
168
170
|
logoutUrl: z.string().url(),
|
|
169
|
-
commentsConfigUrl: z.string().url().optional(),
|
|
170
171
|
commentsTicketUrl: z.string().url().optional(),
|
|
171
172
|
commentsVersionUrlsUrl: z.string().url().optional(),
|
|
172
173
|
zeroRealtimeTicketUrl: z.string().url().optional(),
|
|
@@ -175,17 +176,11 @@ export const runtimeAccessPageDescriptorSchema = z
|
|
|
175
176
|
.nullable(),
|
|
176
177
|
})
|
|
177
178
|
.strict();
|
|
178
|
-
// Runtime route artifacts are compiled into an authority-keyed index. Keep a
|
|
179
|
-
// hard ceiling as a last-resort safety boundary for route-apply CPU, artifact
|
|
180
|
-
// size, and a compromised producer; indexing, not this limit, owns hot-path
|
|
181
|
-
// performance.
|
|
182
179
|
export const RUNTIME_AUTHORIZATION_GRANT_LIMIT = 1_024;
|
|
183
180
|
export const runtimeAuthorizationProjectionSchema = z
|
|
184
181
|
.object({
|
|
185
182
|
generation: z.number().int().nonnegative(),
|
|
186
|
-
//
|
|
187
|
-
// ordinary policy/ownership projections. Only an explicit logout-all
|
|
188
|
-
// rotation invalidates every otherwise-valid session.
|
|
183
|
+
// Only an explicit logout-all rotation invalidates otherwise-valid sessions.
|
|
189
184
|
sessionVersion: z.number().int().nonnegative(),
|
|
190
185
|
fence: accessFenceKindSchema,
|
|
191
186
|
acquireUrl: z.string().url(),
|
|
@@ -193,48 +188,78 @@ export const runtimeAuthorizationProjectionSchema = z
|
|
|
193
188
|
spaceClaimed: z.boolean(),
|
|
194
189
|
grants: z
|
|
195
190
|
.array(grantSchema.extend({
|
|
196
|
-
// The
|
|
197
|
-
//
|
|
198
|
-
// therefore removes only that authority from the session.
|
|
191
|
+
// The browser session records this per acquired authority, so rotating
|
|
192
|
+
// one Grant removes only that authority from the session.
|
|
199
193
|
generation: z.number().int().positive(),
|
|
200
194
|
}))
|
|
201
195
|
.max(RUNTIME_AUTHORIZATION_GRANT_LIMIT),
|
|
202
196
|
})
|
|
203
197
|
.strict();
|
|
198
|
+
// The space-level Comments projection the runtime serves as the whole answer
|
|
199
|
+
// for `/__spacefast/comments/config`; the runtime adds only per-request fields.
|
|
200
|
+
const runtimeSdkCommentsSchema = z.object({
|
|
201
|
+
// Comments boot on the live/published host.
|
|
202
|
+
live: z.boolean(),
|
|
203
|
+
// ...and on an immutable version (preview) host.
|
|
204
|
+
preview: z.boolean(),
|
|
205
|
+
// Null before the Space has ever published.
|
|
206
|
+
live_url: z.string().url().max(2000).nullable(),
|
|
207
|
+
theme: z.object({
|
|
208
|
+
accent: z
|
|
209
|
+
.string()
|
|
210
|
+
.regex(/^#[0-9a-fA-F]{6}$/)
|
|
211
|
+
.nullable(),
|
|
212
|
+
hide_branding: z.boolean(),
|
|
213
|
+
}),
|
|
214
|
+
features: z.object({
|
|
215
|
+
picker: z.boolean(),
|
|
216
|
+
drawing: z.boolean(),
|
|
217
|
+
capture: z.boolean(),
|
|
218
|
+
attachments: z.boolean(),
|
|
219
|
+
notices: z.boolean(),
|
|
220
|
+
}),
|
|
221
|
+
});
|
|
222
|
+
const runtimeSdkProjectionSchema = z.object({
|
|
223
|
+
// Content-derived identity, used to version the injected
|
|
224
|
+
// `/__spacefast/sdk.js?v=` URL.
|
|
225
|
+
revision: z.string().min(1).max(256),
|
|
226
|
+
config: z.object({
|
|
227
|
+
cast_api_base: z.string().url().max(2000).nullable().optional(),
|
|
228
|
+
cast_ws_url: z.string().url().max(2000).nullable().optional(),
|
|
229
|
+
cast_resource_key: z.string().min(1).max(256).nullable().optional(),
|
|
230
|
+
comments: runtimeSdkCommentsSchema.nullable().optional(),
|
|
231
|
+
}),
|
|
232
|
+
// Production tag JavaScript, served inside the same loader response so
|
|
233
|
+
// private Spaces never fetch an anonymously readable per-Space artifact.
|
|
234
|
+
body: z
|
|
235
|
+
.string()
|
|
236
|
+
.max(5 * 1024 * 1024)
|
|
237
|
+
.nullable()
|
|
238
|
+
.optional(),
|
|
239
|
+
});
|
|
204
240
|
const runtimeRouteConfigSchema = z.object({
|
|
205
|
-
//
|
|
206
|
-
// authority; the legacy rule document below is ignored.
|
|
241
|
+
// The sole serving admission authority when present.
|
|
207
242
|
authorization: runtimeAuthorizationProjectionSchema.nullable().optional(),
|
|
208
|
-
// Digest of every input that can change anonymous/public exposure
|
|
209
|
-
//
|
|
210
|
-
//
|
|
211
|
-
// stable so Link/Person changes do not block on an unnecessary edge purge.
|
|
243
|
+
// Digest of every input that can change anonymous/public exposure.
|
|
244
|
+
// Authority-only rotation deliberately leaves it stable, so Link/Person
|
|
245
|
+
// changes do not block on an unnecessary edge purge.
|
|
212
246
|
public_exposure_digest: z
|
|
213
247
|
.string()
|
|
214
248
|
.regex(/^[a-f0-9]{64}$/)
|
|
215
249
|
.optional(),
|
|
216
|
-
// The digest detects equality; the descriptor
|
|
217
|
-
//
|
|
218
|
-
// reconstructing a previous config from mutable control-plane state.
|
|
250
|
+
// The digest detects equality; the descriptor lets the consumer distinguish
|
|
251
|
+
// widening from narrowing.
|
|
219
252
|
public_exposure: runtimePublicExposureDescriptorSchema.optional(),
|
|
220
|
-
//
|
|
221
|
-
// bound to this exact generation, Space, and host. Browser sessions use the
|
|
222
|
-
// projection's independent sessionVersion revocation floor.
|
|
253
|
+
// Short-lived handoffs are bound to this exact generation, Space, and host.
|
|
223
254
|
projection_generation: z.number().int().nonnegative().nullable().optional(),
|
|
224
|
-
// First-party visitor-token verification is explicit.
|
|
225
255
|
visitor_issuer: z.string().min(1).optional(),
|
|
226
256
|
visitor_jwks: visitorJwksSchema.optional(),
|
|
227
|
-
// Anonymous claim-window countdown (ISO timestamp)
|
|
228
|
-
// state so the runtime can render the expiry-rescue banner for visitors
|
|
229
|
-
// holding the claim-link viewer session. Null/absent once claimed.
|
|
257
|
+
// Anonymous claim-window countdown (ISO timestamp). Null/absent once claimed.
|
|
230
258
|
anonymous_expires_at: z.string().nullable().optional(),
|
|
231
|
-
// Serve-time content-type allowlist
|
|
232
|
-
// no notion of WHY a file type is blocked — it refuses (403, blocked_message
|
|
259
|
+
// Serve-time content-type allowlist: the engine refuses (403, blocked_message
|
|
233
260
|
// body) any file whose stored Content-Type matches no pattern. Patterns are
|
|
234
|
-
// exact types or `prefix/*` wildcards ("text/*").
|
|
235
|
-
//
|
|
236
|
-
// null clears the stored policy — route config is rebuilt wholesale, so a
|
|
237
|
-
// claim clears it structurally on the next sync.
|
|
261
|
+
// exact types or `prefix/*` wildcards ("text/*"). Explicit null clears the
|
|
262
|
+
// stored policy.
|
|
238
263
|
content_types: z
|
|
239
264
|
.object({
|
|
240
265
|
allowed: z.array(z.string().min(1)),
|
|
@@ -242,28 +267,18 @@ const runtimeRouteConfigSchema = z.object({
|
|
|
242
267
|
})
|
|
243
268
|
.nullable()
|
|
244
269
|
.optional(),
|
|
245
|
-
//
|
|
246
|
-
//
|
|
247
|
-
//
|
|
248
|
-
// in V1 (set via the superadmin admission endpoint); absent means the
|
|
249
|
-
// env-knob default applies.
|
|
270
|
+
// In-flight concurrency limit the engine enforces on uncacheable requests for
|
|
271
|
+
// this space, superseding SPACEFAST_UNCACHEABLE_CONC_PER_SPACE. Absent means
|
|
272
|
+
// the env-knob default applies.
|
|
250
273
|
admission: z.object({ concurrency: z.number().int().positive() }).optional(),
|
|
251
|
-
//
|
|
252
|
-
//
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
.optional(),
|
|
260
|
-
// Serve-time plan entitlements (proxy-routes.md: "the rule stays in your
|
|
261
|
-
// config and activates the moment you upgrade — no redeploy needed"). Stored
|
|
262
|
-
// per space beside `policy`; a `planGated` compiled proxy rule
|
|
263
|
-
// (packages/routing plan.ts) is checked against this doc at REQUEST time by
|
|
264
|
-
// runtime/redirects.php, never baked into the compiled artifact. Explicit
|
|
265
|
-
// null clears the stored doc (falls back to fail-closed); absence leaves it
|
|
266
|
-
// untouched.
|
|
274
|
+
// Projection for the same-host loader at `/__spacefast/sdk.js`, and the
|
|
275
|
+
// source of the space overlay the runtime answers
|
|
276
|
+
// `/__spacefast/comments/config` from.
|
|
277
|
+
sdk: runtimeSdkProjectionSchema.nullable().optional(),
|
|
278
|
+
// Serve-time plan entitlements. A `planGated` compiled proxy rule is checked
|
|
279
|
+
// against this doc at REQUEST time, never baked into the compiled artifact.
|
|
280
|
+
// Explicit null clears the stored doc (falls back to fail-closed); absence
|
|
281
|
+
// leaves it untouched.
|
|
267
282
|
entitlements: z
|
|
268
283
|
.object({
|
|
269
284
|
externalProxy: z.boolean(),
|
|
@@ -271,11 +286,9 @@ const runtimeRouteConfigSchema = z.object({
|
|
|
271
286
|
.nullable()
|
|
272
287
|
.optional(),
|
|
273
288
|
});
|
|
274
|
-
// Compiled SpaceConfig serving primitives
|
|
275
|
-
//
|
|
276
|
-
//
|
|
277
|
-
// absent, the runtime infers content defaults (root index.html => website-style,
|
|
278
|
-
// otherwise artifact-style listing + viewer).
|
|
289
|
+
// Compiled SpaceConfig serving primitives. When absent, the runtime infers
|
|
290
|
+
// content defaults (root index.html => website-style, otherwise artifact-style
|
|
291
|
+
// listing + viewer).
|
|
279
292
|
const runtimeServingConfigSchema = z.object({
|
|
280
293
|
index: z
|
|
281
294
|
.union([z.string().min(1), z.literal(false)])
|
|
@@ -304,36 +317,34 @@ const runtimeServingConfigSchema = z.object({
|
|
|
304
317
|
.describe("Generate the single-file viewer page when a version has exactly one public file."),
|
|
305
318
|
meta: z
|
|
306
319
|
.object({
|
|
307
|
-
title: z
|
|
308
|
-
|
|
309
|
-
|
|
320
|
+
title: z
|
|
321
|
+
.string()
|
|
322
|
+
.max(SPACE_CONFIG_CAPS.metaTitleChars)
|
|
323
|
+
.optional()
|
|
324
|
+
.describe("Listing, viewer, and OG title."),
|
|
325
|
+
description: z
|
|
326
|
+
.string()
|
|
327
|
+
.max(SPACE_CONFIG_CAPS.metaDescriptionChars)
|
|
328
|
+
.optional()
|
|
329
|
+
.describe("Listing, viewer, and OG description."),
|
|
330
|
+
image: z.string().max(SPACE_CONFIG_CAPS.metaImageChars).optional().describe("OG image path."),
|
|
331
|
+
favicon: z
|
|
332
|
+
.string()
|
|
333
|
+
.max(SPACE_CONFIG_CAPS.metaImageChars)
|
|
334
|
+
.optional()
|
|
335
|
+
.describe("Favicon path."),
|
|
310
336
|
})
|
|
311
337
|
.nullable()
|
|
312
338
|
.optional()
|
|
313
|
-
.describe("Finalize-resolved listing, viewer, and
|
|
339
|
+
.describe("Finalize-resolved listing, viewer, sharing, and browser icon metadata (effective SpaceConfig metadata). This value wins over session metadata."),
|
|
314
340
|
pages: runtimePagesSchema.optional(),
|
|
315
|
-
// Hidden experimental build-pipeline flag
|
|
316
|
-
// without it (byte-identical payload to today). Only ever sent as true.
|
|
341
|
+
// Hidden experimental build-pipeline flag. Only ever sent as true.
|
|
317
342
|
experimental_gutenberg: z.boolean().optional(),
|
|
318
|
-
// Platform-owned head metadata injection
|
|
319
|
-
//
|
|
320
|
-
// title/description/OG/Twitter tags derived from finalized SpaceConfig meta.
|
|
343
|
+
// Platform-owned head metadata injection: enables missing title,
|
|
344
|
+
// description, favicon, OG, and Twitter tags from finalized SpaceConfig meta.
|
|
321
345
|
platform_meta: z.boolean().optional(),
|
|
322
|
-
//
|
|
323
|
-
//
|
|
324
|
-
// between local/prod; resource identity is also per-version because one
|
|
325
|
-
// runtime/Atomic can serve many spaces.
|
|
326
|
-
spacefast_sdk: z
|
|
327
|
-
.object({
|
|
328
|
-
cast_api_base: z.string().url().max(2000).nullable().optional(),
|
|
329
|
-
cast_ws_url: z.string().url().max(2000).nullable().optional(),
|
|
330
|
-
cast_resource_key: z.string().min(1).max(256).nullable().optional(),
|
|
331
|
-
published_comments: z.boolean().optional(),
|
|
332
|
-
})
|
|
333
|
-
.optional(),
|
|
334
|
-
// CP-validated inject snippets (<= 8 KB each, <= 16 per array); sent only
|
|
335
|
-
// when experimental_gutenberg is true. Char caps here are a sanity bound —
|
|
336
|
-
// the byte caps are enforced by the shared SpaceConfig validator.
|
|
346
|
+
// Sent only when experimental_gutenberg is true. The char caps here are a
|
|
347
|
+
// sanity bound; the byte caps live in the shared SpaceConfig validator.
|
|
337
348
|
inject: z
|
|
338
349
|
.object({
|
|
339
350
|
head: z.array(z.string().max(8192)).max(16).optional(),
|
|
@@ -375,61 +386,49 @@ const runtimeZeroRunSchema = z.object({
|
|
|
375
386
|
capabilities: runtimeZeroEndpointCapabilitiesSchema.optional(),
|
|
376
387
|
db: z.record(z.string(), z.unknown()).optional(),
|
|
377
388
|
});
|
|
378
|
-
//
|
|
379
|
-
//
|
|
380
|
-
//
|
|
381
|
-
|
|
382
|
-
|
|
389
|
+
// One variable scope as finalize resolved it. Secrets are NEVER resolved: a
|
|
390
|
+
// secret match at any scope fails the publish in the control plane, so the
|
|
391
|
+
// runtime receives the flag and no value.
|
|
392
|
+
const runtimeVariableScopeSchema = z.object({
|
|
393
|
+
kind: z.enum(["space", "principal"]),
|
|
394
|
+
values: z.record(z.string().min(1), z.object({
|
|
395
|
+
value: z.string().optional(),
|
|
396
|
+
secret: z.boolean(),
|
|
397
|
+
channelValues: z.record(z.string().min(1), z.string()).optional(),
|
|
398
|
+
})),
|
|
399
|
+
});
|
|
400
|
+
const runtimeSubstitutionChannelSchema = z.object({
|
|
401
|
+
name: z.string().min(1).max(128),
|
|
402
|
+
route_name: z
|
|
403
|
+
.string()
|
|
404
|
+
.min(1)
|
|
405
|
+
.max(128)
|
|
406
|
+
.regex(/^[A-Za-z0-9._-]+$/)
|
|
407
|
+
.describe("Runtime route name this channel's variant files serve under."),
|
|
408
|
+
});
|
|
383
409
|
export const runtimeVersionFinalizeRequestSchema = z.object({
|
|
384
410
|
upload_id: z.string().min(1),
|
|
385
411
|
session: runtimeUploadSessionDescriptorSchema.optional(),
|
|
386
|
-
zero_mode: runtimeZeroModeSchema.optional(),
|
|
387
412
|
zero: zeroRuntimeConfigSchema.optional(),
|
|
388
413
|
// Dispatch configuration for a Functions version. Absent for static versions,
|
|
389
|
-
// and absent when
|
|
390
|
-
//
|
|
391
|
-
//
|
|
414
|
+
// and absent when no execution host is configured — the version then serves
|
|
415
|
+
// as pure static content, because this config is what makes the origin
|
|
416
|
+
// dispatch at all.
|
|
392
417
|
functions: functionsRuntimeConfigSchema.optional(),
|
|
393
|
-
|
|
394
|
-
//
|
|
395
|
-
//
|
|
396
|
-
//
|
|
397
|
-
//
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
.describe("Substituted template contents applied over the committed tree at finalize."),
|
|
408
|
-
// Per-channel template variants (spec "Per-channel values"): channelValues
|
|
409
|
-
// overrides compile channel-specific contents of declared template files as
|
|
410
|
-
// channel-scoped serving artifacts. Keyed by RUNTIME ROUTE NAME (route names
|
|
411
|
-
// are runtime-only vocabulary; the live channel serves via `production`).
|
|
412
|
-
// Variants serve only on the route's hosts — permanent Version URLs always
|
|
413
|
-
// serve the version's own finalize-resolved values.
|
|
414
|
-
template_variants: z
|
|
415
|
-
.record(z
|
|
416
|
-
.string()
|
|
417
|
-
.min(1)
|
|
418
|
-
.max(128)
|
|
419
|
-
.regex(/^[A-Za-z0-9._-]+$/)
|
|
420
|
-
.describe("Runtime route name this variant set serves."), z
|
|
421
|
-
.record(z.string().min(1).describe("Canonical committed path declared in `templates`."), z
|
|
422
|
-
.string()
|
|
423
|
-
.max(2 * 1024 * 1024)
|
|
424
|
-
.describe("Variant file contents (secret values never reach this payload)."))
|
|
425
|
-
.refine((files) => Object.keys(files).length <= 100, {
|
|
426
|
-
message: "template_variants supports up to 100 entries per route.",
|
|
427
|
-
}))
|
|
428
|
-
.refine((routes) => Object.keys(routes).length <= 8, {
|
|
429
|
-
message: "template_variants supports up to 8 routes.",
|
|
430
|
-
})
|
|
431
|
-
.optional()
|
|
432
|
-
.describe("Channel-variant template contents, keyed by runtime route name."),
|
|
418
|
+
// Variable resolution is the control plane's half of substitution and stays
|
|
419
|
+
// there: scopes come from its database, secret values are never decrypted for
|
|
420
|
+
// it, and `SPACEFAST_*` names are platform-provided. The finalizer owns the
|
|
421
|
+
// substitution itself — it has the staged bytes and the config that declares
|
|
422
|
+
// which paths are templates, so nothing pre-substituted crosses this wire.
|
|
423
|
+
variable_scopes: z.array(runtimeVariableScopeSchema).optional(),
|
|
424
|
+
system_variables: z.record(z.string().min(1), z.string()).optional(),
|
|
425
|
+
// The space's channels, so the finalizer can compile each one's variant files
|
|
426
|
+
// under the route name that serves them.
|
|
427
|
+
channels: z.array(runtimeSubstitutionChannelSchema).max(8).optional(),
|
|
428
|
+
// Hostnames this space answers on, which convention compilation needs to tell
|
|
429
|
+
// an internal redirect from an external proxy. Only the control plane knows
|
|
430
|
+
// the assignment.
|
|
431
|
+
routing_assigned_hostnames: z.array(z.string().min(1)).optional(),
|
|
433
432
|
// Finalize-rendered Pages documents stored outside the public file tree.
|
|
434
433
|
page_artifacts: z
|
|
435
434
|
.record(z.string().min(1).max(240), z.string().max(2 * 1024 * 1024))
|
|
@@ -437,18 +436,24 @@ export const runtimeVersionFinalizeRequestSchema = z.object({
|
|
|
437
436
|
message: "page_artifacts supports up to 100,000 rendered documents.",
|
|
438
437
|
})
|
|
439
438
|
.optional(),
|
|
439
|
+
// Serving inputs only the control plane can produce. The compiled routing
|
|
440
|
+
// rules are NOT here: the finalizer compiles `_redirects`, `_headers` and the
|
|
441
|
+
// config's routing sections from the staged files itself.
|
|
440
442
|
serving: z
|
|
441
443
|
.object({
|
|
442
|
-
|
|
443
|
-
headers_pattern: z.array(z.record(z.string(), z.unknown())).default([]),
|
|
444
|
-
redirects_exact: z.record(z.string(), z.array(z.record(z.string(), z.unknown()))).default({}),
|
|
445
|
-
redirects_pattern: z.array(z.record(z.string(), z.unknown())).default([]),
|
|
444
|
+
noindexHost: z.boolean().optional(),
|
|
446
445
|
config: runtimeServingConfigSchema.optional(),
|
|
447
|
-
//
|
|
448
|
-
//
|
|
446
|
+
// Origins the platform's own browser code loads from, keyed by the CSP
|
|
447
|
+
// directive each belongs in. Which origins those are is a control-plane
|
|
448
|
+
// decision — they come from its environment and are validated against the
|
|
449
|
+
// host this version will serve from — while the merge itself belongs to
|
|
450
|
+
// the finalizer, which is the only side that sees every CSP the version
|
|
451
|
+
// sets, in `_headers` and in config rules alike.
|
|
452
|
+
platform_csp_sources: z
|
|
453
|
+
.record(z.string().min(1).max(64), z.array(z.string().min(1).max(512)).max(16))
|
|
454
|
+
.optional(),
|
|
449
455
|
zero_endpoints: z.array(runtimeZeroEndpointSchema).max(128).optional(),
|
|
450
|
-
//
|
|
451
|
-
// non-HTTP Zero execution surfaces. These are not PHP route actions.
|
|
456
|
+
// Non-HTTP Zero execution surfaces; not PHP route actions.
|
|
452
457
|
zero_runs: z.array(runtimeZeroRunSchema).max(128).optional(),
|
|
453
458
|
})
|
|
454
459
|
.optional(),
|
|
@@ -460,10 +465,8 @@ export const runtimeVersionFinalizeRequestSchema = z.object({
|
|
|
460
465
|
.default("production")
|
|
461
466
|
.describe("Runtime route-pointer to activate; route names are runtime-only vocabulary."),
|
|
462
467
|
config: runtimeRouteConfigSchema.optional(),
|
|
463
|
-
// Same contract as the route PUT's config_digest
|
|
464
|
-
// pointer so later conditional route PUTs can no-op against it.
|
|
468
|
+
// Same contract as the route PUT's config_digest.
|
|
465
469
|
config_digest: z.string().min(1).optional(),
|
|
466
|
-
changed_paths: changedPathsSchema,
|
|
467
470
|
previous_version_id: previousVersionIdSchema,
|
|
468
471
|
production_hostnames: runtimeRouteIntentSchema.shape.production_hostnames,
|
|
469
472
|
noindex_production_hostnames: runtimeRouteIntentSchema.shape.noindex_production_hostnames,
|
|
@@ -486,23 +489,79 @@ export const runtimeReadinessTargetSchema = z.object({
|
|
|
486
489
|
path: z.string().startsWith("/"),
|
|
487
490
|
expected_statuses: z.array(z.union([runtimePublicReadinessStatusSchema, z.literal(404)])).min(1),
|
|
488
491
|
});
|
|
492
|
+
// The scalars the control plane stores instead of a file list. Both digests are
|
|
493
|
+
// over the version's own catalog: `source` folds path → uploaded sha (two
|
|
494
|
+
// publishes of identical bytes agree even when serving config differs),
|
|
495
|
+
// `served` folds what the version actually answers with, per channel included.
|
|
496
|
+
export const runtimeCatalogDigestsSchema = z.object({
|
|
497
|
+
source: z.string().regex(/^sha256:[a-f0-9]{64}$/),
|
|
498
|
+
served: z.string().regex(/^sha256:[a-f0-9]{64}$/),
|
|
499
|
+
});
|
|
500
|
+
// What this publish changed against the version it supersedes. The runtime
|
|
501
|
+
// diffs two catalogs it already holds; the control plane persists the three
|
|
502
|
+
// counts for the changelog and forwards nothing.
|
|
503
|
+
export const runtimeCatalogDeltaSchema = z.object({
|
|
504
|
+
added: z.number().int().nonnegative(),
|
|
505
|
+
changed: z.number().int().nonnegative(),
|
|
506
|
+
removed: z.number().int().nonnegative(),
|
|
507
|
+
});
|
|
508
|
+
// One compiled diagnostic from the finalizer. `path` is the committed file it
|
|
509
|
+
// points at, carrying `file:line` when the compiler knew a line; `details` is
|
|
510
|
+
// what it points at INSIDE that file — the variable, the channel, the colliding
|
|
511
|
+
// routes — which is the difference between a publish failure you can act on and
|
|
512
|
+
// one you can only retry.
|
|
513
|
+
export const runtimeCompileDiagnosticSchema = z.object({
|
|
514
|
+
severity: z.enum(["info", "warning", "error"]),
|
|
515
|
+
code: z.string().min(1),
|
|
516
|
+
message: z.string(),
|
|
517
|
+
path: z.string().optional(),
|
|
518
|
+
details: z.record(z.string(), z.unknown()).optional(),
|
|
519
|
+
});
|
|
520
|
+
// What the version's conventions and config compiled TO. The counts are the
|
|
521
|
+
// control plane's version-row projection; `proxy_rules` is what it overlays the
|
|
522
|
+
// publishing team's plan onto, because the compiled artifact never bakes a plan
|
|
523
|
+
// verdict.
|
|
524
|
+
export const runtimeCompiledRoutingSchema = z.object({
|
|
525
|
+
redirect_rule_count: z.number().int().nonnegative(),
|
|
526
|
+
header_rule_count: z.number().int().nonnegative(),
|
|
527
|
+
proxy_rule_count: z.number().int().nonnegative(),
|
|
528
|
+
proxy_rules: z
|
|
529
|
+
.array(z.object({ source: z.string(), destination: z.string() }))
|
|
530
|
+
.max(1000)
|
|
531
|
+
.default([]),
|
|
532
|
+
});
|
|
489
533
|
export const runtimeVersionFinalizeResponseSchema = z.object({
|
|
490
534
|
space_id: z.string(),
|
|
491
535
|
version_id: z.string(),
|
|
492
536
|
status: z.literal("ready"),
|
|
493
|
-
// Runtime-compiled truth from zero/endpoints-index.json. Optional so older
|
|
494
|
-
// runtimes remain parseable during rolling deploys.
|
|
495
537
|
zero_endpoint_count: z.number().int().nonnegative().optional(),
|
|
496
|
-
//
|
|
497
|
-
//
|
|
498
|
-
//
|
|
538
|
+
// The finalizer is the one conventions/config compiler, so what it compiled is
|
|
539
|
+
// reported back rather than recomputed: the control plane runs no second
|
|
540
|
+
// compile pass to learn its own version row.
|
|
541
|
+
diagnostics: z.array(runtimeCompileDiagnosticSchema).default([]),
|
|
542
|
+
routing: runtimeCompiledRoutingSchema.optional(),
|
|
543
|
+
// Provenance for the substitution the finalizer performed: dependency name
|
|
544
|
+
// (`NAME` or `NAME@channel`) -> SHA-256 of the value it resolved to. This is
|
|
545
|
+
// what answers "does changing this variable require a republish?".
|
|
546
|
+
variable_digests: z.record(z.string().min(1), z.string()).default({}),
|
|
547
|
+
system_variable_dependencies: z.array(z.string().min(1)).default([]),
|
|
548
|
+
// Canonical runtime-committed manifest, returned by both first and idempotent
|
|
549
|
+
// finalize responses so recovery never needs a consumed upload session.
|
|
499
550
|
manifest: z.array(manifestFileSchema).optional(),
|
|
551
|
+
catalog_digests: runtimeCatalogDigestsSchema.optional(),
|
|
552
|
+
// Absent when this version supersedes nothing, or when the previous version
|
|
553
|
+
// predates the catalog.
|
|
554
|
+
delta: runtimeCatalogDeltaSchema.optional(),
|
|
555
|
+
// The served public image the space card shows for this version, chosen from
|
|
556
|
+
// the catalog at finalize. Null when the version serves no image.
|
|
557
|
+
preview_image_path: z.string().min(1).nullable().optional(),
|
|
500
558
|
readiness_target: runtimeReadinessTargetSchema,
|
|
501
559
|
activation_event_id: z
|
|
502
560
|
.string()
|
|
503
561
|
.min(1)
|
|
504
562
|
.optional()
|
|
505
563
|
.describe("Stable route_updated event id when finalize also activated a route."),
|
|
564
|
+
...runtimeMutationPurgeShape,
|
|
506
565
|
});
|
|
507
566
|
export const runtimeZeroMigrationsApplyResponseSchema = z.object({
|
|
508
567
|
space_id: z.string(),
|
|
@@ -512,12 +571,12 @@ export const runtimeZeroMigrationsApplyResponseSchema = z.object({
|
|
|
512
571
|
export const runtimeRouteUpdateRequestSchema = z.object({
|
|
513
572
|
version_id: z.string().min(1),
|
|
514
573
|
config: runtimeRouteConfigSchema.optional(),
|
|
515
|
-
//
|
|
516
|
-
//
|
|
517
|
-
// store, no journal event, no edge purge — when version, digest, and
|
|
574
|
+
// The engine stores this with the route pointer and no-ops the whole PUT — no
|
|
575
|
+
// policy store, no journal event, no edge purge — when version, digest, and
|
|
518
576
|
// hostname intent all match the stored route. Absent = always write.
|
|
519
577
|
config_digest: z.string().min(1).optional(),
|
|
520
|
-
changed_paths
|
|
578
|
+
// No `changed_paths`: the runtime holds both catalogs and scopes the purge
|
|
579
|
+
// from `previous_version_id` itself.
|
|
521
580
|
previous_version_id: previousVersionIdSchema,
|
|
522
581
|
production_hostnames: runtimeRouteIntentSchema.shape.production_hostnames,
|
|
523
582
|
noindex_production_hostnames: runtimeRouteIntentSchema.shape.noindex_production_hostnames,
|
|
@@ -535,36 +594,36 @@ export const runtimeRouteUpdateResponseSchema = z.object({
|
|
|
535
594
|
route_name: z.string(),
|
|
536
595
|
version_id: z.string(),
|
|
537
596
|
// True when the engine skipped the write because version_id, config_digest,
|
|
538
|
-
// and hostname intent all matched
|
|
539
|
-
//
|
|
597
|
+
// and hostname intent all matched: nothing was journaled, so callers skip the
|
|
598
|
+
// event drain too.
|
|
540
599
|
unchanged: z.boolean().optional(),
|
|
541
600
|
activation_event_id: z
|
|
542
601
|
.string()
|
|
543
602
|
.min(1)
|
|
544
603
|
.optional()
|
|
545
604
|
.describe("Stable route_updated event id for this exact route command, including retries."),
|
|
605
|
+
...runtimeMutationPurgeShape,
|
|
546
606
|
});
|
|
547
607
|
export const runtimeHostnameIntentUpdateResponseSchema = z.object({
|
|
548
608
|
space_id: z.string(),
|
|
549
609
|
route_count: z.number().int().nonnegative(),
|
|
610
|
+
...runtimeMutationPurgeShape,
|
|
550
611
|
});
|
|
551
612
|
export const runtimeTombstonesUpdateRequestSchema = z.object({
|
|
552
613
|
hostnames: runtimeRouteIntentSchema.shape.production_hostnames,
|
|
553
614
|
mode: z.enum(["replace", "add", "remove"]).default("replace"),
|
|
554
|
-
//
|
|
555
|
-
//
|
|
556
|
-
// runtime falls back to a generic 404 when both are absent, so these are
|
|
557
|
-
// additive and back-compatible.
|
|
615
|
+
// Select a differentiated tombstone variant (csam -> bare 404, copyright ->
|
|
616
|
+
// 451, suspension -> 402); absent means a generic 404.
|
|
558
617
|
reason: z.string().optional(),
|
|
559
618
|
category: z.string().optional(),
|
|
560
619
|
});
|
|
561
620
|
export const runtimeTombstonesUpdateResponseSchema = z.object({
|
|
562
621
|
space_id: z.string(),
|
|
563
622
|
tombstone_count: z.number().int().nonnegative(),
|
|
623
|
+
...runtimeMutationPurgeShape,
|
|
564
624
|
});
|
|
565
|
-
//
|
|
566
|
-
//
|
|
567
|
-
// its housekeeping tick prunes trees after re-checking live route pointers.
|
|
625
|
+
// The control plane computes prunable version ids; the engine stores the list
|
|
626
|
+
// and its housekeeping tick prunes trees after re-checking live route pointers.
|
|
568
627
|
// An empty list clears the policy.
|
|
569
628
|
export const runtimeRetentionPolicyUpdateRequestSchema = z.object({
|
|
570
629
|
prunable_version_ids: z.array(z.string()),
|
|
@@ -572,127 +631,93 @@ export const runtimeRetentionPolicyUpdateRequestSchema = z.object({
|
|
|
572
631
|
export const runtimeRetentionPolicyUpdateResponseSchema = z.object({
|
|
573
632
|
space_id: z.string(),
|
|
574
633
|
prunable_count: z.number().int().nonnegative(),
|
|
634
|
+
...runtimeMutationPurgeShape,
|
|
575
635
|
});
|
|
576
636
|
export const runtimeSpaceDeleteResponseSchema = z.object({
|
|
577
637
|
space_id: z.string(),
|
|
578
638
|
status: z.literal("deleted"),
|
|
639
|
+
...runtimeMutationPurgeShape,
|
|
579
640
|
});
|
|
580
641
|
export const runtimeVersionDeleteResponseSchema = z.object({
|
|
581
642
|
space_id: z.string(),
|
|
582
643
|
version_id: z.string(),
|
|
583
644
|
status: z.literal("deleted"),
|
|
645
|
+
...runtimeMutationPurgeShape,
|
|
584
646
|
});
|
|
585
647
|
export const runtimeSpaceRepairResponseSchema = z.object({
|
|
586
648
|
space_id: z.string(),
|
|
587
649
|
status: z.literal("repaired"),
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
650
|
+
...runtimeMutationPurgeShape,
|
|
651
|
+
});
|
|
652
|
+
// A pin freezes a set of blob SHAs against the collector for as long as it
|
|
653
|
+
// lives. `snapshot` pins everything a set of versions plus space storage
|
|
654
|
+
// currently reference; `shas` pins an explicit list. `expires_at` is the
|
|
655
|
+
// engine's hard backstop, so a mover that dies mid-flight never wedges the
|
|
656
|
+
// collector.
|
|
657
|
+
const runtimeSha256Schema = z.string().regex(/^[a-f0-9]{64}$/);
|
|
658
|
+
export const runtimePinCreateRequestSchema = z.union([
|
|
659
|
+
z.object({
|
|
660
|
+
mode: z.literal("snapshot"),
|
|
661
|
+
version_ids: z.array(z.string().min(1)),
|
|
662
|
+
include_uploads: z.boolean().default(true),
|
|
663
|
+
ttl_seconds: z
|
|
664
|
+
.number()
|
|
665
|
+
.int()
|
|
666
|
+
.positive()
|
|
667
|
+
.max(24 * 60 * 60),
|
|
668
|
+
}),
|
|
669
|
+
z.object({
|
|
670
|
+
mode: z.literal("shas"),
|
|
671
|
+
shas: z.array(runtimeSha256Schema).max(100_000),
|
|
672
|
+
ttl_seconds: z
|
|
673
|
+
.number()
|
|
674
|
+
.int()
|
|
675
|
+
.positive()
|
|
676
|
+
.max(24 * 60 * 60),
|
|
677
|
+
}),
|
|
594
678
|
]);
|
|
595
|
-
export const
|
|
596
|
-
.object({
|
|
597
|
-
version_ids: z.array(z.string().min(1)).min(1).optional(),
|
|
598
|
-
// include_files=false produces a metadata-only archive that cannot be
|
|
599
|
-
// imported as a serving Space. Recorded in stattic.json `includes`.
|
|
600
|
-
include_files: z.boolean().optional(),
|
|
601
|
-
})
|
|
602
|
-
.strict();
|
|
603
|
-
export const runtimeSpaceExportResponseSchema = z.object({
|
|
604
|
-
type: z.literal("space_export"),
|
|
605
|
-
export_id: z.string(),
|
|
606
|
-
space_id: z.string(),
|
|
607
|
-
status: runtimeExportImportStatusSchema,
|
|
608
|
-
cursor: z.number().int().nonnegative(),
|
|
609
|
-
processed_files: z.number().int().nonnegative(),
|
|
610
|
-
total_files: z.number().int().nonnegative(),
|
|
611
|
-
version_ids: z.array(z.string()),
|
|
612
|
-
archive_size: z.number().int().nonnegative().optional(),
|
|
613
|
-
// SHA-256 of the finished archive (64 lowercase hex), computed once at
|
|
614
|
-
// completion so the control plane can expose SpaceExport.archive.sha256.
|
|
615
|
-
archive_sha256: z.string().optional(),
|
|
616
|
-
created_at: z.string(),
|
|
617
|
-
updated_at: z.string(),
|
|
618
|
-
completed_at: z.string().optional(),
|
|
619
|
-
});
|
|
620
|
-
export const runtimeSpaceImportStartRequestSchema = z
|
|
621
|
-
.object({
|
|
622
|
-
version_id_map: z.record(z.string(), z.string()).optional(),
|
|
623
|
-
// Plan §9 gated activation, extended to import (I-4): shares
|
|
624
|
-
// runtimeZeroModeSchema with runtimeVersionFinalizeRequestSchema.
|
|
625
|
-
// Absent/anything other than 'active' is the engine's own fail-safe
|
|
626
|
-
// 'activating'.
|
|
627
|
-
zero_mode: runtimeZeroModeSchema.optional(),
|
|
628
|
-
})
|
|
629
|
-
.strict();
|
|
630
|
-
// Deferred version-id mapping: control-plane-driven uploads land on the
|
|
631
|
-
// runtime before the control plane has read the archive, so the freshly
|
|
632
|
-
// minted target ids (and, for upload-sourced imports, zero_mode) are
|
|
633
|
-
// supplied here before the first step.
|
|
634
|
-
export const runtimeSpaceImportMapRequestSchema = z
|
|
635
|
-
.object({
|
|
636
|
-
version_id_map: z.record(z.string(), z.string()),
|
|
637
|
-
zero_mode: runtimeZeroModeSchema.optional(),
|
|
638
|
-
})
|
|
639
|
-
.strict();
|
|
640
|
-
export const runtimeSpaceImportResponseSchema = z.object({
|
|
641
|
-
type: z.literal("space_import"),
|
|
642
|
-
import_id: z.string(),
|
|
643
|
-
space_id: z.string(),
|
|
644
|
-
status: runtimeExportImportStatusSchema,
|
|
645
|
-
cursor: z.number().int().nonnegative(),
|
|
646
|
-
processed_files: z.number().int().nonnegative(),
|
|
647
|
-
total_files: z.number().int().nonnegative(),
|
|
648
|
-
version_ids: z.array(z.string()),
|
|
649
|
-
created_at: z.string(),
|
|
650
|
-
updated_at: z.string(),
|
|
651
|
-
completed_at: z.string().optional(),
|
|
652
|
-
});
|
|
653
|
-
const runtimeTransferBundleFileSchema = z.object({
|
|
654
|
-
path: z.string().min(1),
|
|
655
|
-
content_b64: z.string(),
|
|
656
|
-
});
|
|
657
|
-
export const runtimeTransferBundleSourceRequestSchema = z.object({
|
|
679
|
+
export const runtimePinCreateResponseSchema = z.object({
|
|
658
680
|
space_id: z.string().min(1),
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
681
|
+
pin_id: z.string().min(1),
|
|
682
|
+
expires_at: z.string().min(1),
|
|
683
|
+
sha_count: z.number().int().nonnegative(),
|
|
662
684
|
});
|
|
663
|
-
export const
|
|
685
|
+
export const runtimePinReleaseResponseSchema = z.object({
|
|
664
686
|
space_id: z.string().min(1),
|
|
665
|
-
|
|
666
|
-
|
|
687
|
+
pin_id: z.string().min(1),
|
|
688
|
+
released: z.boolean(),
|
|
689
|
+
});
|
|
690
|
+
// Path-blind on purpose: the response names only the SHAs the space does not
|
|
691
|
+
// already hold.
|
|
692
|
+
export const runtimeBlobsHaveRequestSchema = z.object({
|
|
693
|
+
shas: z.array(runtimeSha256Schema),
|
|
694
|
+
});
|
|
695
|
+
export const runtimeBlobsHaveResponseSchema = z.object({
|
|
696
|
+
missing: z.array(runtimeSha256Schema),
|
|
697
|
+
});
|
|
698
|
+
export const runtimeBlobPutResponseSchema = z.object({
|
|
699
|
+
sha256: runtimeSha256Schema,
|
|
700
|
+
size: z.number().int().nonnegative(),
|
|
701
|
+
stored: z.boolean(),
|
|
702
|
+
});
|
|
703
|
+
// The mirror of the paged export lane: pages replay in the same table/ordering
|
|
704
|
+
// against a schema the target already migrated. Import is idempotent on primary
|
|
705
|
+
// key, so a retried page is not a duplicate row.
|
|
706
|
+
export const runtimeZeroDbImportRequestSchema = z.object({
|
|
707
|
+
schema_hash: z.string().min(1),
|
|
708
|
+
table: z.string().min(1),
|
|
709
|
+
rows: z.array(z.record(z.string(), z.unknown())).max(1_000),
|
|
667
710
|
done: z.boolean(),
|
|
668
711
|
});
|
|
669
|
-
export const
|
|
670
|
-
chunk: z.object({ files: z.array(runtimeTransferBundleFileSchema) }),
|
|
671
|
-
next_cursor: z.string().nullable(),
|
|
672
|
-
});
|
|
673
|
-
export const runtimeTransferBundleStoredResponseSchema = z.object({
|
|
674
|
-
stored: z.literal(true),
|
|
675
|
-
});
|
|
676
|
-
export const runtimeTransferCommitRequestSchema = z.object({
|
|
677
|
-
space_id: z.string().min(1),
|
|
678
|
-
version_ids: z.array(z.string().min(1)),
|
|
679
|
-
live_version_id: z.string().nullable(),
|
|
680
|
-
install_serving_state: z.boolean().optional(),
|
|
681
|
-
});
|
|
682
|
-
export const runtimeTransferCommitResponseSchema = z.object({
|
|
683
|
-
installed: z.literal(true),
|
|
684
|
-
});
|
|
685
|
-
export const runtimeTransferAbortRequestSchema = z.object({
|
|
712
|
+
export const runtimeZeroDbImportResponseSchema = z.object({
|
|
686
713
|
space_id: z.string().min(1),
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
714
|
+
version_id: z.string().min(1),
|
|
715
|
+
table: z.string().min(1),
|
|
716
|
+
imported: z.number().int().nonnegative(),
|
|
690
717
|
});
|
|
691
718
|
// Per-space generation state reported by the authed state endpoint. The
|
|
692
|
-
// reconciliation sweep compares
|
|
693
|
-
//
|
|
694
|
-
// mismatches — never a blanket re-sync (internal-docs/platform.md "Cache
|
|
695
|
-
// Management": missed callbacks degrade to bounded staleness).
|
|
719
|
+
// reconciliation sweep compares it against control-plane expectations and
|
|
720
|
+
// repairs targeted mismatches — never a blanket re-sync.
|
|
696
721
|
export const runtimeSpaceStateSchema = z.object({
|
|
697
722
|
space_id: z.string().min(1).describe("Space whose runtime route pointers follow."),
|
|
698
723
|
routes: z
|
|
@@ -703,12 +728,6 @@ export const runtimeSpaceStateSchema = z.object({
|
|
|
703
728
|
.int()
|
|
704
729
|
.nonnegative()
|
|
705
730
|
.describe("Hostnames the runtime serves a tombstone page for on this space."),
|
|
706
|
-
// Optional for the same reason the whole block is: the control plane deploys
|
|
707
|
-
// in one step while engines pull their bundle on their own schedule, so every
|
|
708
|
-
// field added here is absent on the fleet for hours. Absence means "this
|
|
709
|
-
// engine cannot report hostname scope", never "the scope is empty" — callers
|
|
710
|
-
// must degrade rather than sign a narrower callback claim than the engine
|
|
711
|
-
// will report.
|
|
712
731
|
intent_hostnames: z
|
|
713
732
|
.array(z.string().min(1))
|
|
714
733
|
.optional()
|
|
@@ -739,25 +758,19 @@ export const runtimeStateResponseSchema = z.object({
|
|
|
739
758
|
.min(1)
|
|
740
759
|
.optional()
|
|
741
760
|
.describe("Runtime engine bundle revision. Missing means the engine predates revision state."),
|
|
742
|
-
// Optional generation-state fields (absent on engines predating the reconcile
|
|
743
|
-
// handshake; the sweep treats absence as "cannot compare").
|
|
744
761
|
routes_generation: z
|
|
745
762
|
.string()
|
|
746
763
|
.nullable()
|
|
747
764
|
.optional()
|
|
748
|
-
.describe("Active route-index generation id (routes/current.
|
|
765
|
+
.describe("Active route-index generation id (routes/current.json pointer)."),
|
|
749
766
|
spaces: z
|
|
750
767
|
.array(runtimeSpaceStateSchema)
|
|
751
768
|
.optional()
|
|
752
769
|
.describe("Per-space route-pointer state for reconcile comparison."),
|
|
753
770
|
});
|
|
754
|
-
// Runtime callback journal event (
|
|
755
|
-
//
|
|
756
|
-
//
|
|
757
|
-
// serving-affecting events into provider edge purges. `event_id` is the stable
|
|
758
|
-
// journal id — purge enqueues are idempotent on (event_id, hostname) so
|
|
759
|
-
// replays are no-ops. The object is intentionally loose: engines may journal
|
|
760
|
-
// extra diagnostic fields without breaking older control planes.
|
|
771
|
+
// Runtime callback journal event. Purging is complete (or durably queued for a
|
|
772
|
+
// maintenance retry) before a mutation returns, so this lane carries only
|
|
773
|
+
// observability and background events.
|
|
761
774
|
export const runtimeCallbackEventSchema = z.looseObject({
|
|
762
775
|
event: z
|
|
763
776
|
.string()
|
|
@@ -767,7 +780,7 @@ export const runtimeCallbackEventSchema = z.looseObject({
|
|
|
767
780
|
.string()
|
|
768
781
|
.min(1)
|
|
769
782
|
.optional()
|
|
770
|
-
.describe("Stable journal/callback id
|
|
783
|
+
.describe("Stable journal/callback id for idempotent delivery."),
|
|
771
784
|
operation_id: z
|
|
772
785
|
.string()
|
|
773
786
|
.optional()
|
|
@@ -815,6 +828,13 @@ const runtimeEventDeliveryIdentitySchema = z.object({
|
|
|
815
828
|
delivery_id: z.string().min(1),
|
|
816
829
|
});
|
|
817
830
|
const runtimeEventDrainSessionIdSchema = z.string().min(1).max(128);
|
|
831
|
+
export const runtimeJournalCursorSchema = z
|
|
832
|
+
.object({
|
|
833
|
+
file: z.string(),
|
|
834
|
+
offset: z.number().int().nonnegative(),
|
|
835
|
+
inode: z.number().int().nonnegative().optional(),
|
|
836
|
+
})
|
|
837
|
+
.strict();
|
|
818
838
|
export const runtimeEventsDrainRequestSchema = z
|
|
819
839
|
.object({
|
|
820
840
|
session_id: runtimeEventDrainSessionIdSchema,
|
|
@@ -841,49 +861,55 @@ export const runtimeEventsDrainRequestSchema = z
|
|
|
841
861
|
context.addIssue({ code: "custom", message: "A drain page requires page_id." });
|
|
842
862
|
});
|
|
843
863
|
export const runtimeEventsDrainResponseSchema = z.object({
|
|
864
|
+
// Cursor runtimes keep these at zero. Older engines report their legacy
|
|
865
|
+
// delivery counters here while a rolling bundle update is still converging.
|
|
844
866
|
delivered_count: z.number().int().nonnegative(),
|
|
845
867
|
failed_count: z.number().int().nonnegative(),
|
|
846
|
-
expired_count: z
|
|
847
|
-
.number()
|
|
848
|
-
.int()
|
|
849
|
-
.nonnegative()
|
|
850
|
-
.optional()
|
|
851
|
-
.describe("Undeliverable callbacks dropped after exceeding the attempt cap."),
|
|
868
|
+
expired_count: z.number().int().nonnegative().optional(),
|
|
852
869
|
returned_count: z.number().int().nonnegative().optional(),
|
|
853
870
|
pending_count: z
|
|
854
871
|
.number()
|
|
855
872
|
.int()
|
|
856
873
|
.nonnegative()
|
|
857
874
|
.optional()
|
|
858
|
-
.describe("
|
|
859
|
-
// The drain is pull-first: the caller IS the control plane and is blocked on
|
|
860
|
-
// this response, so the engine hands its journal back here instead of posting
|
|
861
|
-
// each event over a separate HTTP round trip. Processing replays are no-ops
|
|
862
|
-
// on (event_id, hostname).
|
|
875
|
+
.describe("Whether the journal has records beyond this page. A non-zero value means a later drain still has work."),
|
|
863
876
|
events: z
|
|
864
877
|
.array(z.object({
|
|
865
878
|
delivery_id: z.string().min(1),
|
|
866
879
|
operation_id: z.string().optional(),
|
|
867
|
-
callback_token: z.string().min(1),
|
|
868
880
|
event: runtimeCallbackEventSchema,
|
|
869
881
|
}))
|
|
870
882
|
.optional(),
|
|
883
|
+
cursor: runtimeJournalCursorSchema
|
|
884
|
+
.optional()
|
|
885
|
+
.describe("The journal commit coordinate. Absent on pre-cursor runtime bundles."),
|
|
871
886
|
});
|
|
872
|
-
export const runtimeEventsAckRequestSchema = z
|
|
873
|
-
|
|
874
|
-
|
|
887
|
+
export const runtimeEventsAckRequestSchema = z
|
|
888
|
+
.object({
|
|
889
|
+
// The cursor is the authoritative commit coordinate.
|
|
890
|
+
cursor: runtimeJournalCursorSchema.optional(),
|
|
891
|
+
session_id: runtimeEventDrainSessionIdSchema.optional(),
|
|
892
|
+
deliveries: z.array(runtimeEventDeliveryIdentitySchema).max(100).default([]),
|
|
893
|
+
})
|
|
894
|
+
.superRefine((value, context) => {
|
|
895
|
+
if (!value.cursor && (!value.session_id || value.deliveries.length === 0)) {
|
|
896
|
+
context.addIssue({
|
|
897
|
+
code: "custom",
|
|
898
|
+
message: "An event acknowledgement requires a cursor or legacy deliveries.",
|
|
899
|
+
});
|
|
900
|
+
}
|
|
875
901
|
});
|
|
876
902
|
export const runtimeEventsAckResponseSchema = z.object({
|
|
877
903
|
acknowledged_count: z.number().int().nonnegative(),
|
|
878
904
|
idempotent_count: z.number().int().nonnegative(),
|
|
879
905
|
stale_count: z.number().int().nonnegative(),
|
|
906
|
+
cursor: runtimeJournalCursorSchema
|
|
907
|
+
.optional()
|
|
908
|
+
.describe("The committed journal coordinate. Absent after a legacy delivery acknowledgement."),
|
|
880
909
|
});
|
|
881
|
-
//
|
|
882
|
-
//
|
|
883
|
-
|
|
884
|
-
// reads status from either the tick response or a direct GET. Field names
|
|
885
|
-
// mirror the engine's job record (§22 "Storage layout & job record").
|
|
886
|
-
export const runtimeEngineJobLaneSchema = z.enum(["interactive", "bulk"]);
|
|
910
|
+
// One lane; the enum keeps its single value because the engine's job record and
|
|
911
|
+
// tick response still carry the field.
|
|
912
|
+
export const runtimeEngineJobLaneSchema = z.literal("bulk");
|
|
887
913
|
export const runtimeEngineJobStatusSchema = z.enum(["pending", "running", "complete", "failed"]);
|
|
888
914
|
export const runtimeEngineJobErrorSchema = z.object({
|
|
889
915
|
code: z.string().min(1),
|
|
@@ -912,15 +938,13 @@ export const runtimeEngineJobRecordSchema = z.object({
|
|
|
912
938
|
export const runtimeEngineJobCreateRequestSchema = z.object({
|
|
913
939
|
type: z.string().min(1),
|
|
914
940
|
payload: z.record(z.string(), z.unknown()).default({}),
|
|
915
|
-
// Unique per (type, space, operation) — create is upsert-by-key
|
|
941
|
+
// Unique per (type, space, operation) — create is upsert-by-key.
|
|
916
942
|
idempotency_key: z.string().min(1),
|
|
917
943
|
});
|
|
918
944
|
export const runtimeEngineJobResponseSchema = z.object({
|
|
919
945
|
job: runtimeEngineJobRecordSchema,
|
|
920
946
|
});
|
|
921
|
-
//
|
|
922
|
-
// claimed/advanced this call, so a driver loop can usually avoid a follow-up
|
|
923
|
-
// GET; `job` is absent/null when the lane had nothing eligible to run.
|
|
947
|
+
// `job` is absent/null when the lane had nothing eligible to run.
|
|
924
948
|
export const runtimeEngineJobTickResponseSchema = z.object({
|
|
925
949
|
lane: runtimeEngineJobLaneSchema,
|
|
926
950
|
job: runtimeEngineJobRecordSchema.nullable().optional(),
|