@spacefast/common 0.0.12 → 0.0.13
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/contracts/access.d.ts +386 -1201
- package/dist/contracts/access.js +240 -1057
- package/dist/contracts/activity.d.ts +1 -1
- package/dist/contracts/activity.js +6 -14
- package/dist/contracts/api-keys.d.ts +16 -0
- package/dist/contracts/api-keys.js +2 -0
- package/dist/contracts/archives.d.ts +1 -2
- package/dist/contracts/archives.js +4 -6
- package/dist/contracts/comments.d.ts +58 -85
- package/dist/contracts/comments.js +86 -67
- package/dist/contracts/countries.js +2 -7
- package/dist/contracts/data-source-routes.d.ts +20 -0
- package/dist/contracts/data-source-routes.js +15 -0
- package/dist/contracts/device-auth.d.ts +7 -3
- package/dist/contracts/enums.d.ts +2 -4
- package/dist/contracts/enums.js +3 -8
- package/dist/contracts/error-code-meta.d.ts +17 -20
- package/dist/contracts/error-code-meta.js +8 -5
- package/dist/contracts/error-codes.d.ts +1 -1
- package/dist/contracts/error-codes.js +4 -5
- package/dist/contracts/events.d.ts +13 -13
- package/dist/contracts/execution.d.ts +212 -0
- package/dist/contracts/execution.js +288 -0
- package/dist/contracts/feature-lifecycle.d.ts +21 -167
- package/dist/contracts/feature-lifecycle.js +27 -173
- package/dist/contracts/features.d.ts +13 -115
- package/dist/contracts/features.js +1 -31
- package/dist/contracts/functions.d.ts +729 -0
- package/dist/contracts/functions.js +485 -0
- package/dist/contracts/git.d.ts +1 -1
- package/dist/contracts/grants.d.ts +1158 -0
- package/dist/contracts/grants.js +696 -0
- package/dist/contracts/ids.d.ts +5 -3
- package/dist/contracts/ids.js +5 -3
- package/dist/contracts/mcp.d.ts +6 -6
- package/dist/contracts/oauth-resources.d.ts +5 -5
- package/dist/contracts/oauth-resources.js +1 -0
- package/dist/contracts/operations.d.ts +2 -2
- package/dist/contracts/pages.d.ts +6 -14
- package/dist/contracts/pages.js +6 -23
- package/dist/contracts/plan-policy.d.ts +1 -8
- package/dist/contracts/plan-policy.js +1 -4
- package/dist/contracts/platform.d.ts +78 -30
- package/dist/contracts/platform.js +59 -12
- package/dist/contracts/push-new.d.ts +26 -2
- package/dist/contracts/push-new.js +8 -0
- package/dist/contracts/quotas.d.ts +0 -1
- package/dist/contracts/quotas.js +0 -5
- package/dist/contracts/repository-connections.d.ts +1 -1
- package/dist/contracts/resources.d.ts +10 -66
- package/dist/contracts/resources.js +4 -10
- package/dist/contracts/runtime-api.d.ts +558 -265
- package/dist/contracts/runtime-api.js +178 -91
- package/dist/contracts/runtime-app.d.ts +138 -0
- package/dist/contracts/runtime-app.js +21 -0
- package/dist/contracts/sf-config-v1.d.ts +5 -0
- package/dist/contracts/sites.d.ts +5 -0
- package/dist/contracts/sites.js +12 -1
- package/dist/contracts/space-config.d.ts +46 -116
- package/dist/contracts/space-config.js +320 -24
- package/dist/contracts/spaces.d.ts +253 -158
- package/dist/contracts/spaces.js +68 -54
- package/dist/contracts/superadmin-emails.d.ts +2 -2
- package/dist/contracts/superadmin-spaces.d.ts +15 -130
- package/dist/contracts/superadmin-spaces.js +7 -0
- package/dist/contracts/tags.d.ts +23 -23
- package/dist/contracts/teams.d.ts +9 -9
- package/dist/contracts/transfers.d.ts +0 -1
- package/dist/contracts/zero.d.ts +195 -273
- package/dist/contracts/zero.js +78 -206
- package/dist/dashboard-paths/index.d.ts +2 -2
- package/dist/dashboard-paths/index.js +1 -0
- package/dist/docs/agent-prose.js +10 -8
- package/dist/docs/agent-setup.d.ts +2 -2
- package/dist/docs/agent-setup.js +11 -9
- package/dist/docs/agent-solutions.js +1 -1
- package/dist/docs/catalog.d.ts +28 -28
- package/dist/docs/catalog.js +29 -29
- package/dist/docs/error-docs.js +23 -27
- package/dist/utils/asset-fingerprint.d.ts +11 -0
- package/dist/utils/asset-fingerprint.js +46 -0
- package/dist/utils/auth-redirect.d.ts +1 -3
- package/dist/utils/auth-redirect.js +1 -21
- package/dist/utils/build-settings.d.ts +2 -0
- package/dist/utils/build-settings.js +61 -9
- package/dist/utils/credential-policy.d.ts +1 -1
- package/dist/utils/credential-policy.js +4 -0
- package/dist/utils/egress-policy.fixtures.json +1 -1
- package/dist/utils/grants.d.ts +53 -0
- package/dist/utils/grants.js +160 -0
- package/dist/utils/local-space-state.js +1 -0
- package/dist/utils/pages.d.ts +11 -5
- package/dist/utils/pages.js +19 -32
- package/dist/utils/publish-policy.d.ts +34 -4
- package/dist/utils/publish-policy.js +132 -61
- package/dist/utils/query-keys.d.ts +1 -6
- package/dist/utils/query-keys.js +1 -6
- package/dist/utils/space-config.d.ts +5 -1
- package/dist/utils/space-config.js +35 -0
- package/dist/utils/static-runtime-policy.d.ts +5 -0
- package/dist/utils/static-runtime-policy.js +54 -10
- package/dist/utils/upload-session.d.ts +3 -0
- package/dist/utils/upload-session.js +31 -0
- package/package.json +2 -1
- package/dist/utils/access-grant.d.ts +0 -14
- package/dist/utils/access-grant.js +0 -88
- package/dist/utils/access-match.d.ts +0 -46
- package/dist/utils/access-match.fixtures.json +0 -513
- package/dist/utils/access-match.js +0 -312
- package/dist/utils/access-verdict.fixtures.json +0 -421
- package/dist/utils/sf-config-v1.d.ts +0 -2
- package/dist/utils/sf-config-v1.fixtures.json +0 -9
- package/dist/utils/sf-config-v1.js +0 -551
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* The platform execution layer: everything true of running tenant code on
|
|
4
|
+
* Spacefast regardless of which product is running it.
|
|
5
|
+
*
|
|
6
|
+
* Two products sit on top of this file, and they are genuinely different
|
|
7
|
+
* things rather than two configurations of one thing:
|
|
8
|
+
*
|
|
9
|
+
* - **Zero** (`./zero.js`) runs capsules — schema, queries, mutations and
|
|
10
|
+
* endpoints that share one parent-owned transaction — in the native runner
|
|
11
|
+
* on a dedicated wp.cloud site, next to that site's MySQL.
|
|
12
|
+
* - **Functions** (`./functions.js`) runs workers on Cloudflare Workers for
|
|
13
|
+
* Platforms, carrying npm and framework output that we could never sandbox
|
|
14
|
+
* as well ourselves.
|
|
15
|
+
*
|
|
16
|
+
* The only surface both can host is an endpoint that touches neither the
|
|
17
|
+
* database nor the caller's identity — see `endpointIsPortable`. That is the
|
|
18
|
+
* portable seam, and it is deliberately narrow: a capsule handler's semantics
|
|
19
|
+
* depend on the transaction wrapped around it, which does not exist on an
|
|
20
|
+
* isolate a continent away from the database.
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* What a live version runs. `static` is bytes on disk with no execution at
|
|
24
|
+
* all, and remains the overwhelming majority of traffic; the other two each
|
|
25
|
+
* name a product.
|
|
26
|
+
*/
|
|
27
|
+
export declare const runtimeKindSchema: z.ZodEnum<{
|
|
28
|
+
zero: "zero";
|
|
29
|
+
functions: "functions";
|
|
30
|
+
static: "static";
|
|
31
|
+
}>;
|
|
32
|
+
export type RuntimeKind = z.infer<typeof runtimeKindSchema>;
|
|
33
|
+
export declare const EXECUTION_ENV_MAX_KEYS = 64;
|
|
34
|
+
export declare const EXECUTION_ENV_NAME_MAX_BYTES = 128;
|
|
35
|
+
export declare const EXECUTION_ENV_VALUE_MAX_BYTES: number;
|
|
36
|
+
export declare const EXECUTION_ENV_TOTAL_VALUE_MAX_BYTES: number;
|
|
37
|
+
/**
|
|
38
|
+
* Names the platform reserves for itself in the tenant environment. Tenant
|
|
39
|
+
* code cannot set these, so it cannot spoof the values the broker trusts.
|
|
40
|
+
*/
|
|
41
|
+
export declare const EXECUTION_ENV_RESERVED_PREFIXES: readonly ["SPACEFAST_", "ZERO_"];
|
|
42
|
+
/** Paths the platform owns on every space, whatever the product. */
|
|
43
|
+
export declare const PLATFORM_ROUTE_PREFIX = "/__spacefast";
|
|
44
|
+
/**
|
|
45
|
+
* First path segments the platform claims. Matched as segment prefixes rather
|
|
46
|
+
* than whole segments because the runtime already reserves the whole family —
|
|
47
|
+
* `/__stattic_probe` is a live serving path, and `upload-policy.php` refuses
|
|
48
|
+
* any first segment starting with either of these. Encoding it the same way
|
|
49
|
+
* here keeps a capsule or worker from declaring a route the origin would
|
|
50
|
+
* intercept before it ever reached them.
|
|
51
|
+
*/
|
|
52
|
+
export declare const PLATFORM_RESERVED_ROUTE_SEGMENT_PREFIXES: readonly ["__spacefast", "__stattic", "__span"];
|
|
53
|
+
export declare const executionSha256Schema: z.ZodString;
|
|
54
|
+
export declare const executionBase64Schema: z.ZodString;
|
|
55
|
+
export declare const executionIdentifierSchema: z.ZodString;
|
|
56
|
+
export declare const executionEnvNameSchema: z.ZodString;
|
|
57
|
+
/**
|
|
58
|
+
* Builds the route-path schema for a product. Structural rules — normalization,
|
|
59
|
+
* traversal, wildcards, the platform's own prefixes — are identical everywhere
|
|
60
|
+
* and live here. Each product passes the extra paths and prefixes its own app
|
|
61
|
+
* shell claims, because those differ: Zero's client router owns `/auth/*`,
|
|
62
|
+
* a Functions worker owns nothing beyond what the platform reserves.
|
|
63
|
+
*/
|
|
64
|
+
export declare function appRoutePathSchema(options: {
|
|
65
|
+
subject: string;
|
|
66
|
+
reservedPaths?: readonly string[];
|
|
67
|
+
reservedPrefixes?: readonly string[];
|
|
68
|
+
}): z.ZodString;
|
|
69
|
+
export declare const httpMethodSchema: z.ZodEnum<{
|
|
70
|
+
POST: "POST";
|
|
71
|
+
PUT: "PUT";
|
|
72
|
+
GET: "GET";
|
|
73
|
+
HEAD: "HEAD";
|
|
74
|
+
PATCH: "PATCH";
|
|
75
|
+
DELETE: "DELETE";
|
|
76
|
+
OPTIONS: "OPTIONS";
|
|
77
|
+
}>;
|
|
78
|
+
export type HttpMethod = z.infer<typeof httpMethodSchema>;
|
|
79
|
+
/**
|
|
80
|
+
* What a handler is allowed to reach. Declared at build time from what the
|
|
81
|
+
* source actually imports, not from what the author claims, and printed in the
|
|
82
|
+
* publish receipt so placement is never a surprise.
|
|
83
|
+
*/
|
|
84
|
+
export declare const executionCapabilitiesSchema: z.ZodDefault<z.ZodObject<{
|
|
85
|
+
db: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
|
|
86
|
+
fetch: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
|
|
87
|
+
auth: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
|
|
88
|
+
env: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
|
|
89
|
+
realtime: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
|
|
90
|
+
logging: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
|
|
91
|
+
}, z.core.$strip>>;
|
|
92
|
+
export type ExecutionCapabilities = z.infer<typeof executionCapabilitiesSchema>;
|
|
93
|
+
/**
|
|
94
|
+
* The portable seam, stated as one predicate.
|
|
95
|
+
*
|
|
96
|
+
* `db` and `auth` are the two capabilities that bind a handler to the local
|
|
97
|
+
* tier. `db` because Zero's store guarantees every handler runs inside one
|
|
98
|
+
* parent-owned transaction, which an isolate reaching the database over a
|
|
99
|
+
* relay cannot offer. `auth` because capsule identity is resolved against that
|
|
100
|
+
* same transaction's view. Everything else — fetch, env, realtime, logging —
|
|
101
|
+
* is brokered identically on both tiers and does not constrain placement.
|
|
102
|
+
*
|
|
103
|
+
* Nothing here decides placement automatically: the product a version deploys
|
|
104
|
+
* to is a property of what its author wrote, and this predicate only reports
|
|
105
|
+
* whether the code could also have run on the other tier.
|
|
106
|
+
*/
|
|
107
|
+
export declare function endpointIsPortable(capabilities: ExecutionCapabilities): boolean;
|
|
108
|
+
/**
|
|
109
|
+
* A source-carrying handler, before it is compiled to whichever tier will run
|
|
110
|
+
* it. Both products publish these; only the capability set decides which of
|
|
111
|
+
* them could accept a given one.
|
|
112
|
+
*/
|
|
113
|
+
export declare function executionEndpointSourceSchema(routePathSchema: z.ZodType<string>): z.ZodObject<{
|
|
114
|
+
method: z.ZodEnum<{
|
|
115
|
+
POST: "POST";
|
|
116
|
+
PUT: "PUT";
|
|
117
|
+
GET: "GET";
|
|
118
|
+
HEAD: "HEAD";
|
|
119
|
+
PATCH: "PATCH";
|
|
120
|
+
DELETE: "DELETE";
|
|
121
|
+
OPTIONS: "OPTIONS";
|
|
122
|
+
}>;
|
|
123
|
+
path: z.ZodType<string, unknown, z.core.$ZodTypeInternals<string, unknown>>;
|
|
124
|
+
endpointId: z.ZodString;
|
|
125
|
+
source: z.ZodString;
|
|
126
|
+
capabilities: z.ZodDefault<z.ZodObject<{
|
|
127
|
+
db: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
|
|
128
|
+
fetch: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
|
|
129
|
+
auth: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
|
|
130
|
+
env: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
|
|
131
|
+
realtime: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
|
|
132
|
+
logging: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
|
|
133
|
+
}, z.core.$strip>>;
|
|
134
|
+
}, z.core.$strict>;
|
|
135
|
+
/**
|
|
136
|
+
* Byte-carrying artifacts travel inline in the finalize payload rather than as
|
|
137
|
+
* a separate upload, so a version's code lands in the same atomic write as the
|
|
138
|
+
* rest of it. The storage root and ceiling differ per product, hence a factory.
|
|
139
|
+
*/
|
|
140
|
+
export declare function executionBundleSchema(options: {
|
|
141
|
+
bundleRoot: string;
|
|
142
|
+
maxBytes: number;
|
|
143
|
+
}): z.ZodObject<{
|
|
144
|
+
path: z.ZodString;
|
|
145
|
+
sha256: z.ZodString;
|
|
146
|
+
size: z.ZodNumber;
|
|
147
|
+
contentBase64: z.ZodString;
|
|
148
|
+
}, z.core.$strict>;
|
|
149
|
+
export declare function executionStaticFileSchema(options: {
|
|
150
|
+
storageRoot: string;
|
|
151
|
+
maxBytes: number;
|
|
152
|
+
}): z.ZodObject<{
|
|
153
|
+
path: z.ZodString;
|
|
154
|
+
contentType: z.ZodString;
|
|
155
|
+
sha256: z.ZodString;
|
|
156
|
+
size: z.ZodNumber;
|
|
157
|
+
contentBase64: z.ZodString;
|
|
158
|
+
}, z.core.$strict>;
|
|
159
|
+
/**
|
|
160
|
+
* Whether a version's execution surface may be read by anyone or only by the
|
|
161
|
+
* space's own members. Both products expose an inspect route; the policy is
|
|
162
|
+
* the same question on each.
|
|
163
|
+
*/
|
|
164
|
+
export declare const executionInspectPolicySchema: z.ZodEnum<{
|
|
165
|
+
public: "public";
|
|
166
|
+
private: "private";
|
|
167
|
+
}>;
|
|
168
|
+
export type ExecutionInspectPolicy = z.infer<typeof executionInspectPolicySchema>;
|
|
169
|
+
export declare const executionLogEntrySchema: z.ZodObject<{
|
|
170
|
+
id: z.ZodString;
|
|
171
|
+
timestamp: z.ZodString;
|
|
172
|
+
level: z.ZodEnum<{
|
|
173
|
+
info: "info";
|
|
174
|
+
error: "error";
|
|
175
|
+
debug: "debug";
|
|
176
|
+
warn: "warn";
|
|
177
|
+
}>;
|
|
178
|
+
requestId: z.ZodNullable<z.ZodString>;
|
|
179
|
+
versionId: z.ZodNullable<z.ZodString>;
|
|
180
|
+
handlerName: z.ZodNullable<z.ZodString>;
|
|
181
|
+
message: z.ZodString;
|
|
182
|
+
metadata: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
183
|
+
}, z.core.$strict>;
|
|
184
|
+
export type ExecutionLogEntry = z.infer<typeof executionLogEntrySchema>;
|
|
185
|
+
export declare const executionLogListQuerySchema: z.ZodObject<{
|
|
186
|
+
limit: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
187
|
+
cursor: z.ZodOptional<z.ZodString>;
|
|
188
|
+
requestId: z.ZodOptional<z.ZodString>;
|
|
189
|
+
handlerName: z.ZodOptional<z.ZodString>;
|
|
190
|
+
}, z.core.$strict>;
|
|
191
|
+
export type ExecutionLogListQuery = z.infer<typeof executionLogListQuerySchema>;
|
|
192
|
+
export declare const executionLogListResponseSchema: z.ZodObject<{
|
|
193
|
+
data: z.ZodArray<z.ZodObject<{
|
|
194
|
+
id: z.ZodString;
|
|
195
|
+
timestamp: z.ZodString;
|
|
196
|
+
level: z.ZodEnum<{
|
|
197
|
+
info: "info";
|
|
198
|
+
error: "error";
|
|
199
|
+
debug: "debug";
|
|
200
|
+
warn: "warn";
|
|
201
|
+
}>;
|
|
202
|
+
requestId: z.ZodNullable<z.ZodString>;
|
|
203
|
+
versionId: z.ZodNullable<z.ZodString>;
|
|
204
|
+
handlerName: z.ZodNullable<z.ZodString>;
|
|
205
|
+
message: z.ZodString;
|
|
206
|
+
metadata: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
207
|
+
}, z.core.$strict>>;
|
|
208
|
+
pagination: z.ZodObject<{
|
|
209
|
+
nextCursor: z.ZodNullable<z.ZodString>;
|
|
210
|
+
}, z.core.$strict>;
|
|
211
|
+
}, z.core.$strict>;
|
|
212
|
+
export type ExecutionLogListResponse = z.infer<typeof executionLogListResponseSchema>;
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* The platform execution layer: everything true of running tenant code on
|
|
4
|
+
* Spacefast regardless of which product is running it.
|
|
5
|
+
*
|
|
6
|
+
* Two products sit on top of this file, and they are genuinely different
|
|
7
|
+
* things rather than two configurations of one thing:
|
|
8
|
+
*
|
|
9
|
+
* - **Zero** (`./zero.js`) runs capsules — schema, queries, mutations and
|
|
10
|
+
* endpoints that share one parent-owned transaction — in the native runner
|
|
11
|
+
* on a dedicated wp.cloud site, next to that site's MySQL.
|
|
12
|
+
* - **Functions** (`./functions.js`) runs workers on Cloudflare Workers for
|
|
13
|
+
* Platforms, carrying npm and framework output that we could never sandbox
|
|
14
|
+
* as well ourselves.
|
|
15
|
+
*
|
|
16
|
+
* The only surface both can host is an endpoint that touches neither the
|
|
17
|
+
* database nor the caller's identity — see `endpointIsPortable`. That is the
|
|
18
|
+
* portable seam, and it is deliberately narrow: a capsule handler's semantics
|
|
19
|
+
* depend on the transaction wrapped around it, which does not exist on an
|
|
20
|
+
* isolate a continent away from the database.
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* What a live version runs. `static` is bytes on disk with no execution at
|
|
24
|
+
* all, and remains the overwhelming majority of traffic; the other two each
|
|
25
|
+
* name a product.
|
|
26
|
+
*/
|
|
27
|
+
export const runtimeKindSchema = z.enum(["static", "zero", "functions"]);
|
|
28
|
+
export const EXECUTION_ENV_MAX_KEYS = 64;
|
|
29
|
+
export const EXECUTION_ENV_NAME_MAX_BYTES = 128;
|
|
30
|
+
export const EXECUTION_ENV_VALUE_MAX_BYTES = 16 * 1024;
|
|
31
|
+
export const EXECUTION_ENV_TOTAL_VALUE_MAX_BYTES = 64 * 1024;
|
|
32
|
+
/**
|
|
33
|
+
* Names the platform reserves for itself in the tenant environment. Tenant
|
|
34
|
+
* code cannot set these, so it cannot spoof the values the broker trusts.
|
|
35
|
+
*/
|
|
36
|
+
export const EXECUTION_ENV_RESERVED_PREFIXES = ["SPACEFAST_", "ZERO_"];
|
|
37
|
+
/** Paths the platform owns on every space, whatever the product. */
|
|
38
|
+
export const PLATFORM_ROUTE_PREFIX = "/__spacefast";
|
|
39
|
+
/**
|
|
40
|
+
* First path segments the platform claims. Matched as segment prefixes rather
|
|
41
|
+
* than whole segments because the runtime already reserves the whole family —
|
|
42
|
+
* `/__stattic_probe` is a live serving path, and `upload-policy.php` refuses
|
|
43
|
+
* any first segment starting with either of these. Encoding it the same way
|
|
44
|
+
* here keeps a capsule or worker from declaring a route the origin would
|
|
45
|
+
* intercept before it ever reached them.
|
|
46
|
+
*/
|
|
47
|
+
export const PLATFORM_RESERVED_ROUTE_SEGMENT_PREFIXES = [
|
|
48
|
+
"__spacefast",
|
|
49
|
+
"__stattic",
|
|
50
|
+
"__span",
|
|
51
|
+
];
|
|
52
|
+
export const executionSha256Schema = z.string().regex(/^sha256:[a-f0-9]{64}$/);
|
|
53
|
+
export const executionBase64Schema = z
|
|
54
|
+
.string()
|
|
55
|
+
.regex(/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/);
|
|
56
|
+
export const executionIdentifierSchema = z
|
|
57
|
+
.string()
|
|
58
|
+
.min(1)
|
|
59
|
+
.max(128)
|
|
60
|
+
.regex(/^[A-Za-z_][A-Za-z0-9_]*$/);
|
|
61
|
+
export const executionEnvNameSchema = z
|
|
62
|
+
.string()
|
|
63
|
+
.min(1)
|
|
64
|
+
.max(EXECUTION_ENV_NAME_MAX_BYTES)
|
|
65
|
+
.regex(/^[A-Za-z_][A-Za-z0-9_]*$/)
|
|
66
|
+
.refine((name) => !EXECUTION_ENV_RESERVED_PREFIXES.some((prefix) => name.toUpperCase().startsWith(prefix)), { message: "Environment variable name uses a reserved prefix." });
|
|
67
|
+
/**
|
|
68
|
+
* Rejects lone surrogates. A path that survives JSON but not UTF-8 encoding
|
|
69
|
+
* round-trips differently through PHP, Rust and Cloudflare's router, so it is
|
|
70
|
+
* refused at the contract rather than resolved differently on each tier.
|
|
71
|
+
*/
|
|
72
|
+
function hasWellFormedUnicode(value) {
|
|
73
|
+
for (let index = 0; index < value.length; index++) {
|
|
74
|
+
const codeUnit = value.charCodeAt(index);
|
|
75
|
+
if (codeUnit >= 0xd800 && codeUnit <= 0xdbff) {
|
|
76
|
+
if (index + 1 >= value.length)
|
|
77
|
+
return false;
|
|
78
|
+
const next = value.charCodeAt(index + 1);
|
|
79
|
+
if (next < 0xdc00 || next > 0xdfff)
|
|
80
|
+
return false;
|
|
81
|
+
index++;
|
|
82
|
+
}
|
|
83
|
+
else if (codeUnit >= 0xdc00 && codeUnit <= 0xdfff) {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
function hasNoAsciiControlCharacters(value) {
|
|
90
|
+
for (let index = 0; index < value.length; index++) {
|
|
91
|
+
const codeUnit = value.charCodeAt(index);
|
|
92
|
+
if (codeUnit <= 0x1f || codeUnit === 0x7f)
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Builds the route-path schema for a product. Structural rules — normalization,
|
|
99
|
+
* traversal, wildcards, the platform's own prefixes — are identical everywhere
|
|
100
|
+
* and live here. Each product passes the extra paths and prefixes its own app
|
|
101
|
+
* shell claims, because those differ: Zero's client router owns `/auth/*`,
|
|
102
|
+
* a Functions worker owns nothing beyond what the platform reserves.
|
|
103
|
+
*/
|
|
104
|
+
export function appRoutePathSchema(options) {
|
|
105
|
+
const reservedPaths = options.reservedPaths ?? [];
|
|
106
|
+
const reservedPrefixes = options.reservedPrefixes ?? [];
|
|
107
|
+
return z
|
|
108
|
+
.string()
|
|
109
|
+
.min(1)
|
|
110
|
+
.max(2048)
|
|
111
|
+
.startsWith("/")
|
|
112
|
+
.refine(hasWellFormedUnicode, {
|
|
113
|
+
message: `${options.subject} must contain well-formed Unicode.`,
|
|
114
|
+
})
|
|
115
|
+
.refine((path) => path.normalize("NFC") === path, {
|
|
116
|
+
message: `${options.subject} must use NFC-normalized Unicode.`,
|
|
117
|
+
})
|
|
118
|
+
.refine(hasNoAsciiControlCharacters, {
|
|
119
|
+
message: `${options.subject} must not contain ASCII control characters.`,
|
|
120
|
+
})
|
|
121
|
+
.refine((path) => !path.includes("//") &&
|
|
122
|
+
!path.includes("\\") &&
|
|
123
|
+
!path.includes("*") &&
|
|
124
|
+
!path.includes("?") &&
|
|
125
|
+
!path.includes("#") &&
|
|
126
|
+
!path.includes("%") &&
|
|
127
|
+
// The root is the one path allowed to end in a slash. Zero reserves it
|
|
128
|
+
// for its app shell anyway, but a Functions worker serving a framework
|
|
129
|
+
// app needs to claim "/" as its index route.
|
|
130
|
+
(path === "/" || !path.endsWith("/")) &&
|
|
131
|
+
path
|
|
132
|
+
.split("/")
|
|
133
|
+
.every((segment, index, segments) => segment !== "." &&
|
|
134
|
+
segment !== ".." &&
|
|
135
|
+
segment !== ":" &&
|
|
136
|
+
(segment !== ":splat" || index === segments.length - 1)), {
|
|
137
|
+
message: `${options.subject} must be absolute app paths without encoded, empty, traversal, unnamed parameter, * wildcard, non-terminal splat, \\, ?, or # segments.`,
|
|
138
|
+
})
|
|
139
|
+
.refine((path) => !PLATFORM_RESERVED_ROUTE_SEGMENT_PREFIXES.some((segmentPrefix) => (path.split("/")[1] ?? "").startsWith(segmentPrefix)), { message: `${options.subject} cannot use a path the platform reserves.` })
|
|
140
|
+
.refine((path) => !reservedPaths.some((reservedPath) => path === reservedPath), {
|
|
141
|
+
message: `${options.subject} cannot use a reserved path.`,
|
|
142
|
+
})
|
|
143
|
+
.refine((path) => !reservedPrefixes.some((prefix) => path.startsWith(prefix)), {
|
|
144
|
+
message: `${options.subject} cannot use a reserved path prefix.`,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
export const httpMethodSchema = z.enum([
|
|
148
|
+
"GET",
|
|
149
|
+
"HEAD",
|
|
150
|
+
"POST",
|
|
151
|
+
"PUT",
|
|
152
|
+
"PATCH",
|
|
153
|
+
"DELETE",
|
|
154
|
+
"OPTIONS",
|
|
155
|
+
]);
|
|
156
|
+
/**
|
|
157
|
+
* What a handler is allowed to reach. Declared at build time from what the
|
|
158
|
+
* source actually imports, not from what the author claims, and printed in the
|
|
159
|
+
* publish receipt so placement is never a surprise.
|
|
160
|
+
*/
|
|
161
|
+
export const executionCapabilitiesSchema = z
|
|
162
|
+
.object({
|
|
163
|
+
db: z.boolean().default(false),
|
|
164
|
+
fetch: z.boolean().default(false),
|
|
165
|
+
auth: z.boolean().default(false),
|
|
166
|
+
env: z.boolean().default(false),
|
|
167
|
+
realtime: z.boolean().default(false),
|
|
168
|
+
logging: z.boolean().default(false),
|
|
169
|
+
})
|
|
170
|
+
.partial()
|
|
171
|
+
.default({});
|
|
172
|
+
/**
|
|
173
|
+
* The portable seam, stated as one predicate.
|
|
174
|
+
*
|
|
175
|
+
* `db` and `auth` are the two capabilities that bind a handler to the local
|
|
176
|
+
* tier. `db` because Zero's store guarantees every handler runs inside one
|
|
177
|
+
* parent-owned transaction, which an isolate reaching the database over a
|
|
178
|
+
* relay cannot offer. `auth` because capsule identity is resolved against that
|
|
179
|
+
* same transaction's view. Everything else — fetch, env, realtime, logging —
|
|
180
|
+
* is brokered identically on both tiers and does not constrain placement.
|
|
181
|
+
*
|
|
182
|
+
* Nothing here decides placement automatically: the product a version deploys
|
|
183
|
+
* to is a property of what its author wrote, and this predicate only reports
|
|
184
|
+
* whether the code could also have run on the other tier.
|
|
185
|
+
*/
|
|
186
|
+
export function endpointIsPortable(capabilities) {
|
|
187
|
+
return capabilities.db !== true && capabilities.auth !== true;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* A source-carrying handler, before it is compiled to whichever tier will run
|
|
191
|
+
* it. Both products publish these; only the capability set decides which of
|
|
192
|
+
* them could accept a given one.
|
|
193
|
+
*/
|
|
194
|
+
export function executionEndpointSourceSchema(routePathSchema) {
|
|
195
|
+
return z
|
|
196
|
+
.object({
|
|
197
|
+
method: httpMethodSchema,
|
|
198
|
+
path: routePathSchema,
|
|
199
|
+
endpointId: z.string().min(1).max(256),
|
|
200
|
+
source: z
|
|
201
|
+
.string()
|
|
202
|
+
.min(1)
|
|
203
|
+
.max(2 * 1024 * 1024),
|
|
204
|
+
capabilities: executionCapabilitiesSchema,
|
|
205
|
+
})
|
|
206
|
+
.strict();
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Byte-carrying artifacts travel inline in the finalize payload rather than as
|
|
210
|
+
* a separate upload, so a version's code lands in the same atomic write as the
|
|
211
|
+
* rest of it. The storage root and ceiling differ per product, hence a factory.
|
|
212
|
+
*/
|
|
213
|
+
export function executionBundleSchema(options) {
|
|
214
|
+
return z
|
|
215
|
+
.object({
|
|
216
|
+
path: z
|
|
217
|
+
.string()
|
|
218
|
+
.min(1)
|
|
219
|
+
.startsWith(`${options.bundleRoot}/`)
|
|
220
|
+
.refine((bundlePath) => !bundlePath.split("/").includes(".."), {
|
|
221
|
+
message: "Bundle paths cannot traverse outside the runtime bundle root.",
|
|
222
|
+
}),
|
|
223
|
+
sha256: executionSha256Schema,
|
|
224
|
+
size: z.number().int().nonnegative().max(options.maxBytes),
|
|
225
|
+
contentBase64: executionBase64Schema,
|
|
226
|
+
})
|
|
227
|
+
.strict();
|
|
228
|
+
}
|
|
229
|
+
export function executionStaticFileSchema(options) {
|
|
230
|
+
return z
|
|
231
|
+
.object({
|
|
232
|
+
path: z
|
|
233
|
+
.string()
|
|
234
|
+
.min(1)
|
|
235
|
+
.max(2048)
|
|
236
|
+
.refine((path) => !path.startsWith("/") && !path.split("/").includes(".."), {
|
|
237
|
+
message: "Static file paths must be relative and cannot traverse.",
|
|
238
|
+
})
|
|
239
|
+
.refine((path) => !path.startsWith(`${options.storageRoot}/`), {
|
|
240
|
+
message: `Static files cannot use ${options.storageRoot}.`,
|
|
241
|
+
})
|
|
242
|
+
.refine((path) => !path.startsWith("_") && !path.startsWith("."), {
|
|
243
|
+
message: "Static file paths cannot use private root prefixes.",
|
|
244
|
+
}),
|
|
245
|
+
contentType: z.string().min(1).max(255),
|
|
246
|
+
sha256: executionSha256Schema,
|
|
247
|
+
size: z.number().int().nonnegative().max(options.maxBytes),
|
|
248
|
+
contentBase64: executionBase64Schema,
|
|
249
|
+
})
|
|
250
|
+
.strict();
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Whether a version's execution surface may be read by anyone or only by the
|
|
254
|
+
* space's own members. Both products expose an inspect route; the policy is
|
|
255
|
+
* the same question on each.
|
|
256
|
+
*/
|
|
257
|
+
export const executionInspectPolicySchema = z.enum(["private", "public"]);
|
|
258
|
+
export const executionLogEntrySchema = z
|
|
259
|
+
.object({
|
|
260
|
+
id: z.string().min(1),
|
|
261
|
+
timestamp: z.string().datetime(),
|
|
262
|
+
level: z.enum(["debug", "info", "warn", "error"]),
|
|
263
|
+
requestId: z.string().min(1).nullable(),
|
|
264
|
+
versionId: z.string().min(1).nullable(),
|
|
265
|
+
/** Zero names the mutation; Functions names the handler. */
|
|
266
|
+
handlerName: z.string().min(1).nullable(),
|
|
267
|
+
message: z.string(),
|
|
268
|
+
metadata: z.record(z.string(), z.unknown()).default({}),
|
|
269
|
+
})
|
|
270
|
+
.strict();
|
|
271
|
+
export const executionLogListQuerySchema = z
|
|
272
|
+
.object({
|
|
273
|
+
limit: z.coerce.number().int().min(1).max(100).default(50),
|
|
274
|
+
cursor: z.string().min(1).optional(),
|
|
275
|
+
requestId: z.string().min(1).optional(),
|
|
276
|
+
handlerName: z.string().min(1).optional(),
|
|
277
|
+
})
|
|
278
|
+
.strict();
|
|
279
|
+
export const executionLogListResponseSchema = z
|
|
280
|
+
.object({
|
|
281
|
+
data: z.array(executionLogEntrySchema),
|
|
282
|
+
pagination: z
|
|
283
|
+
.object({
|
|
284
|
+
nextCursor: z.string().nullable(),
|
|
285
|
+
})
|
|
286
|
+
.strict(),
|
|
287
|
+
})
|
|
288
|
+
.strict();
|