@spacefast/common 0.0.23 → 0.0.26
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/brand-assets/LICENSE-Haskoy.txt +93 -0
- package/brand-assets/haskoy-latin-variable.woff2 +0 -0
- package/brand-assets/spacefast-favicon.svg +4 -4
- package/brand-assets/spacefast-sf-full-bleed.svg +3 -3
- package/brand-assets/spacefast-wordmark.svg +2 -4
- package/dist/agents/connect-targets.js +1 -1
- package/dist/agents/private-key-oauth.d.ts +0 -1
- package/dist/agents/private-key-oauth.js +46 -63
- package/dist/brand-assets-build.js +4 -6
- package/dist/brand-assets.d.ts +5 -15
- package/dist/brand-assets.js +15 -14
- package/dist/config/domains.d.ts +47 -7
- package/dist/config/domains.js +66 -10
- 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 +124 -12
- package/dist/contracts/access.js +73 -9
- package/dist/contracts/activity.d.ts +1 -1
- package/dist/contracts/activity.js +5 -12
- package/dist/contracts/api-keys.d.ts +21 -21
- package/dist/contracts/api-keys.js +2 -2
- package/dist/contracts/beta.js +1 -1
- package/dist/contracts/billing.d.ts +3 -35
- package/dist/contracts/billing.js +33 -52
- package/dist/contracts/builds.d.ts +30 -18
- package/dist/contracts/builds.js +28 -9
- package/dist/contracts/channels.js +1 -1
- 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 +282 -53
- package/dist/contracts/comments.js +173 -52
- package/dist/contracts/common.d.ts +3 -3
- package/dist/contracts/common.js +5 -5
- package/dist/contracts/crons.d.ts +35 -0
- package/dist/contracts/crons.js +42 -0
- package/dist/contracts/device-auth.d.ts +6 -6
- package/dist/contracts/device-auth.js +2 -2
- package/dist/contracts/docs.js +2 -2
- package/dist/contracts/domains.d.ts +38 -27
- package/dist/contracts/domains.js +29 -2
- package/dist/contracts/enums.d.ts +34 -30
- package/dist/contracts/enums.js +62 -48
- package/dist/contracts/error-code-meta.d.ts +50 -125
- package/dist/contracts/error-code-meta.js +29 -41
- package/dist/contracts/error-codes.d.ts +1 -1
- package/dist/contracts/error-codes.js +14 -33
- package/dist/contracts/execution.d.ts +47 -66
- package/dist/contracts/execution.js +53 -49
- 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 +12 -52
- package/dist/contracts/feature-lifecycle.js +16 -59
- package/dist/contracts/features.d.ts +48 -60
- package/dist/contracts/features.js +59 -51
- package/dist/contracts/functions.d.ts +266 -72
- package/dist/contracts/functions.js +260 -37
- package/dist/contracts/git.d.ts +28 -34
- package/dist/contracts/git.js +10 -11
- package/dist/contracts/grants.d.ts +9 -5
- package/dist/contracts/grants.js +18 -9
- package/dist/contracts/ids.js +2 -2
- package/dist/contracts/internal.d.ts +8 -6
- package/dist/contracts/internal.js +12 -12
- package/dist/contracts/mcp.d.ts +11 -0
- package/dist/contracts/mcp.js +5 -0
- package/dist/contracts/me.js +1 -1
- 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 +77 -3
- package/dist/contracts/pages.d.ts +12 -7
- package/dist/contracts/pages.js +24 -1
- package/dist/contracts/plan-policy.d.ts +0 -2
- package/dist/contracts/plan-policy.js +2 -3
- package/dist/contracts/platform.d.ts +61 -148
- package/dist/contracts/platform.js +70 -126
- package/dist/contracts/publish-archive.d.ts +508 -0
- package/dist/contracts/publish-archive.js +32 -0
- package/dist/contracts/push-new.d.ts +4 -4
- package/dist/contracts/quotas.js +2 -2
- package/dist/contracts/repository-connections.d.ts +43 -30
- package/dist/contracts/repository-connections.js +11 -3
- package/dist/contracts/resources.d.ts +12 -24
- package/dist/contracts/resources.js +36 -16
- package/dist/contracts/runtime-api.d.ts +433 -192
- package/dist/contracts/runtime-api.js +421 -356
- package/dist/contracts/runtime-app.d.ts +127 -120
- package/dist/contracts/runtime-app.js +19 -10
- package/dist/contracts/runtime-db.d.ts +5 -5
- package/dist/contracts/runtime-db.js +2 -2
- package/dist/contracts/runtime-purge.d.ts +8 -0
- package/dist/contracts/runtime-purge.js +7 -0
- package/dist/contracts/runtime-services.d.ts +219 -0
- package/dist/contracts/runtime-services.js +437 -0
- package/dist/contracts/runtime-storage.d.ts +46 -32
- package/dist/contracts/runtime-storage.js +31 -8
- package/dist/contracts/sf-config-v1.d.ts +48 -10
- package/dist/contracts/space-config.d.ts +69 -18
- package/dist/contracts/space-config.js +226 -27
- package/dist/contracts/spaces.d.ts +236 -450
- package/dist/contracts/spaces.js +131 -151
- 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 +361 -92
- 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-tenants.d.ts +0 -6
- package/dist/contracts/superadmin-tenants.js +1 -7
- package/dist/contracts/superadmin.d.ts +135 -54
- package/dist/contracts/superadmin.js +46 -23
- package/dist/contracts/tags.d.ts +12 -12
- package/dist/contracts/tags.js +3 -3
- package/dist/contracts/teams.d.ts +49 -14
- package/dist/contracts/teams.js +40 -22
- package/dist/contracts/transfers.d.ts +0 -49
- package/dist/contracts/transfers.js +4 -8
- 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 +9 -8
- package/dist/contracts/webhooks.d.ts +2 -2
- package/dist/contracts/zero.d.ts +102 -73
- package/dist/contracts/zero.js +94 -32
- package/dist/dashboard-paths/index.d.ts +1 -1
- package/dist/dashboard-paths/index.js +1 -0
- package/dist/docs/agent-prose.d.ts +2 -2
- package/dist/docs/agent-prose.js +63 -70
- package/dist/docs/agent-setup.js +45 -15
- package/dist/docs/catalog.d.ts +24 -24
- package/dist/docs/catalog.js +35 -24
- package/dist/docs/error-docs.js +194 -270
- package/dist/docs/skills.d.ts +12 -12
- package/dist/docs/skills.js +4 -4
- package/dist/test-helpers/fetch-stub.d.ts +16 -0
- package/dist/test-helpers/fetch-stub.js +25 -0
- package/dist/utils/build-settings.d.ts +5 -1
- package/dist/utils/build-settings.js +91 -13
- package/dist/utils/canonical-json.d.ts +10 -0
- package/dist/utils/canonical-json.js +22 -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/concurrency.js +1 -0
- package/dist/utils/credential-policy.d.ts +1 -1
- package/dist/utils/credential-policy.js +4 -3
- package/dist/utils/dns-instructions.js +9 -9
- 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 +8 -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 +26 -1
- 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 +9 -16
- package/dist/utils/local-space-state.js +20 -50
- package/dist/utils/one-shot-replay.d.ts +4 -1
- package/dist/utils/page-colors.d.ts +14 -12
- package/dist/utils/page-colors.js +15 -13
- package/dist/utils/page-fonts.d.ts +2 -2
- package/dist/utils/page-fonts.js +15 -4
- package/dist/utils/pages.d.ts +21 -3
- package/dist/utils/pages.js +42 -13
- 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 +27 -0
- package/dist/utils/publish-policy.fixtures.json +403 -0
- package/dist/utils/publish-policy.js +45 -20
- package/dist/utils/push-new.d.ts +7 -0
- package/dist/utils/push-new.js +7 -0
- package/dist/utils/query-keys.d.ts +4 -5
- package/dist/utils/query-keys.js +2 -3
- 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 +6 -11
- package/dist/utils/runtime-upload.js +10 -13
- package/dist/utils/secure-local-file.js +5 -16
- package/dist/utils/space-config.d.ts +1 -34
- package/dist/utils/space-config.js +8 -326
- package/dist/utils/static-runtime-policy.d.ts +9 -3
- package/dist/utils/static-runtime-policy.fixtures.json +38 -41
- package/dist/utils/static-runtime-policy.js +22 -56
- 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/upload-session.d.ts +4 -1
- package/dist/utils/upload-session.js +33 -5
- package/dist/utils/version-path.js +13 -4
- package/dist/vocabulary.d.ts +13 -11
- package/dist/vocabulary.js +42 -28
- package/package.json +7 -17
- 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
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CONFIG_PUBLIC_GRANT_SOURCE_REFERENCE, grantSchema, } from "../contracts/grants.js";
|
|
2
|
-
import { SPACE_CONFIG_CAPS, SPACE_CONFIG_CANONICAL_FILE,
|
|
2
|
+
import { SPACE_CONFIG_CAPS, SPACE_CONFIG_CANONICAL_FILE, } from "../contracts/space-config.js";
|
|
3
3
|
const TEXT_BYTES = new TextEncoder();
|
|
4
4
|
export const CONFIG_PUBLIC_GRANT_COMPILED_ID = "config:access.public";
|
|
5
5
|
function byteLength(value) {
|
|
@@ -57,6 +57,7 @@ function capIssues(candidate) {
|
|
|
57
57
|
}
|
|
58
58
|
else {
|
|
59
59
|
for (const key of ["head", "bodyStart", "bodyEnd", "noscript"]) {
|
|
60
|
+
// SAFETY: The enclosing branch proves inject is a non-null, non-array object.
|
|
60
61
|
const snippets = inject[key];
|
|
61
62
|
if (snippets === undefined) {
|
|
62
63
|
continue;
|
|
@@ -107,6 +108,7 @@ function capIssues(candidate) {
|
|
|
107
108
|
["title", SPACE_CONFIG_CAPS.metaTitleChars],
|
|
108
109
|
["description", SPACE_CONFIG_CAPS.metaDescriptionChars],
|
|
109
110
|
["image", SPACE_CONFIG_CAPS.metaImageChars],
|
|
111
|
+
["favicon", SPACE_CONFIG_CAPS.metaImageChars],
|
|
110
112
|
];
|
|
111
113
|
for (const [key, limit] of caps) {
|
|
112
114
|
const value = meta[key];
|
|
@@ -123,75 +125,6 @@ function capIssues(candidate) {
|
|
|
123
125
|
}
|
|
124
126
|
return issues;
|
|
125
127
|
}
|
|
126
|
-
// File-only keys (`name`, `runtime`) never reach the API overlay, so their
|
|
127
|
-
// checks live beside the shared caps rather than inside them. Both run BEFORE
|
|
128
|
-
// zod so a bad declaration reports its stable code instead of a shape error.
|
|
129
|
-
const RUNTIME_KINDS = new Set(["zero", "functions"]);
|
|
130
|
-
function fileOnlyIssues(candidate) {
|
|
131
|
-
const issues = [];
|
|
132
|
-
const name = candidate.name;
|
|
133
|
-
if (typeof name === "string" && name.trim().length > SPACE_CONFIG_CAPS.nameChars) {
|
|
134
|
-
issues.push({
|
|
135
|
-
severity: "error",
|
|
136
|
-
code: "config_name_too_long",
|
|
137
|
-
message: `name supports up to ${SPACE_CONFIG_CAPS.nameChars} characters.`,
|
|
138
|
-
path: "name",
|
|
139
|
-
details: { length: name.trim().length, limit: SPACE_CONFIG_CAPS.nameChars },
|
|
140
|
-
});
|
|
141
|
-
}
|
|
142
|
-
const runtime = candidate.runtime;
|
|
143
|
-
if (runtime === undefined) {
|
|
144
|
-
return issues;
|
|
145
|
-
}
|
|
146
|
-
if (typeof runtime !== "object" || runtime === null || Array.isArray(runtime)) {
|
|
147
|
-
issues.push({
|
|
148
|
-
severity: "error",
|
|
149
|
-
code: "config_runtime_invalid_kind",
|
|
150
|
-
message: 'runtime must be an object declaring a kind of "zero" or "functions".',
|
|
151
|
-
path: "runtime",
|
|
152
|
-
});
|
|
153
|
-
return issues;
|
|
154
|
-
}
|
|
155
|
-
const declaration = runtime;
|
|
156
|
-
const kind = declaration.kind;
|
|
157
|
-
if (typeof kind !== "string" || !RUNTIME_KINDS.has(kind)) {
|
|
158
|
-
issues.push({
|
|
159
|
-
severity: "error",
|
|
160
|
-
code: "config_runtime_invalid_kind",
|
|
161
|
-
message: 'runtime.kind must be "zero" or "functions".',
|
|
162
|
-
path: "runtime.kind",
|
|
163
|
-
...(typeof kind === "string" ? { details: { kind } } : {}),
|
|
164
|
-
});
|
|
165
|
-
return issues;
|
|
166
|
-
}
|
|
167
|
-
if (kind === "zero") {
|
|
168
|
-
// Zero is never inferred, so a declaration that names neither entry has
|
|
169
|
-
// nothing to compile — failing here beats publishing static files under a
|
|
170
|
-
// config that promised a database app.
|
|
171
|
-
for (const key of ["server", "client"]) {
|
|
172
|
-
const value = declaration[key];
|
|
173
|
-
if (typeof value !== "string" || value.trim().length === 0) {
|
|
174
|
-
issues.push({
|
|
175
|
-
severity: "error",
|
|
176
|
-
code: "config_runtime_entry_missing",
|
|
177
|
-
message: `runtime.${key} is required and must name a module when runtime.kind is "zero".`,
|
|
178
|
-
path: `runtime.${key}`,
|
|
179
|
-
});
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
return issues;
|
|
183
|
-
}
|
|
184
|
-
const entry = declaration.entry;
|
|
185
|
-
if (entry !== undefined && (typeof entry !== "string" || entry.trim().length === 0)) {
|
|
186
|
-
issues.push({
|
|
187
|
-
severity: "error",
|
|
188
|
-
code: "config_runtime_entry_missing",
|
|
189
|
-
message: "runtime.entry must name a module when present; omit it to detect the worker instead.",
|
|
190
|
-
path: "runtime.entry",
|
|
191
|
-
});
|
|
192
|
-
}
|
|
193
|
-
return issues;
|
|
194
|
-
}
|
|
195
128
|
/**
|
|
196
129
|
* Keys a committed `sf.jsonc` owns outright: they are facts about the content
|
|
197
130
|
* of a version (who may read it, what it is called, what runs it), so they roll
|
|
@@ -233,26 +166,16 @@ export function spaceConfigOverlayIssue(config) {
|
|
|
233
166
|
}
|
|
234
167
|
return capIssues(config)[0] ?? null;
|
|
235
168
|
}
|
|
236
|
-
//
|
|
237
|
-
//
|
|
238
|
-
|
|
239
|
-
// OUR routing vocabulary. These keys are real sf.jsonc keys, owned end to end
|
|
240
|
-
// by the routing compiler (crates/stattic-runtime-core): the strict v1 compiler
|
|
241
|
-
// validates them and the merge folds them in behind `_redirects`/`_headers`.
|
|
242
|
-
// This lane resolves the serving config, so it neither validates nor carries
|
|
243
|
-
// them — it just leaves them alone.
|
|
244
|
-
const ROUTING_RULE_KEYS = new Set(["redirects", "rewrites", "headers"]);
|
|
245
|
-
// Replaced by the `runtime` declaration; never silently ignored like other
|
|
246
|
-
// unknown keys, because it is the one key whose loss changes what gets served.
|
|
247
|
-
const REMOVED_FUNCTIONS_KEY = "functions";
|
|
248
|
-
const KNOWN_FILE_KEYS = new Set(Object.keys(spaceConfigFileSchema.shape));
|
|
249
|
-
// Strips JSONC comments and trailing commas (string-aware single pass).
|
|
169
|
+
// Strips JSONC comments and trailing commas (string-aware single pass). The
|
|
170
|
+
// config file itself is parsed by the crate; this stays for the OTHER JSONC
|
|
171
|
+
// files the CLI reads (agent configs, exported platform env).
|
|
250
172
|
export function stripJsonComments(raw) {
|
|
251
173
|
let output = "";
|
|
252
174
|
let inString = false;
|
|
253
175
|
let inLineComment = false;
|
|
254
176
|
let inBlockComment = false;
|
|
255
177
|
for (let i = 0; i < raw.length; i += 1) {
|
|
178
|
+
// SAFETY: The loop condition proves i addresses a character in raw.
|
|
256
179
|
const char = raw[i];
|
|
257
180
|
const next = raw[i + 1];
|
|
258
181
|
if (inLineComment) {
|
|
@@ -299,6 +222,7 @@ export function stripJsonComments(raw) {
|
|
|
299
222
|
// Trailing comma: skip when the next non-whitespace/non-comment char closes.
|
|
300
223
|
let j = i + 1;
|
|
301
224
|
while (j < raw.length) {
|
|
225
|
+
// SAFETY: The loop condition proves j addresses a character in raw.
|
|
302
226
|
const ahead = raw[j];
|
|
303
227
|
if (/\s/.test(ahead)) {
|
|
304
228
|
j += 1;
|
|
@@ -326,245 +250,3 @@ export function stripJsonComments(raw) {
|
|
|
326
250
|
}
|
|
327
251
|
return output;
|
|
328
252
|
}
|
|
329
|
-
/**
|
|
330
|
-
* Parses a raw `sf.jsonc` at finalize: 256 KB cap, JSONC tolerance,
|
|
331
|
-
* routing-key rejection, unknown-key diagnostics, shared shape caps. Errors
|
|
332
|
-
* fail the version; warnings ride version diagnostics.
|
|
333
|
-
*/
|
|
334
|
-
export function parseSpaceConfigFile(raw, options = {}) {
|
|
335
|
-
const configPath = options.path ?? SPACE_CONFIG_CANONICAL_FILE;
|
|
336
|
-
if (byteLength(raw) > SPACE_CONFIG_CAPS.fileBytes) {
|
|
337
|
-
return {
|
|
338
|
-
config: null,
|
|
339
|
-
issues: [
|
|
340
|
-
{
|
|
341
|
-
severity: "error",
|
|
342
|
-
code: "config_file_too_large",
|
|
343
|
-
message: `${configPath} supports up to ${SPACE_CONFIG_CAPS.fileBytes} bytes.`,
|
|
344
|
-
path: configPath,
|
|
345
|
-
details: { size: byteLength(raw), limit: SPACE_CONFIG_CAPS.fileBytes },
|
|
346
|
-
},
|
|
347
|
-
],
|
|
348
|
-
};
|
|
349
|
-
}
|
|
350
|
-
let candidate;
|
|
351
|
-
try {
|
|
352
|
-
candidate = JSON.parse(stripJsonComments(raw));
|
|
353
|
-
}
|
|
354
|
-
catch (error) {
|
|
355
|
-
return {
|
|
356
|
-
config: null,
|
|
357
|
-
issues: [
|
|
358
|
-
{
|
|
359
|
-
severity: "error",
|
|
360
|
-
code: "config_invalid",
|
|
361
|
-
message: `${configPath} is not valid JSONC: ${error instanceof Error ? error.message : "parse error"}.`,
|
|
362
|
-
path: configPath,
|
|
363
|
-
},
|
|
364
|
-
],
|
|
365
|
-
};
|
|
366
|
-
}
|
|
367
|
-
if (typeof candidate !== "object" || candidate === null || Array.isArray(candidate)) {
|
|
368
|
-
return {
|
|
369
|
-
config: null,
|
|
370
|
-
issues: [
|
|
371
|
-
{
|
|
372
|
-
severity: "error",
|
|
373
|
-
code: "config_invalid",
|
|
374
|
-
message: `${configPath} must contain a JSON object.`,
|
|
375
|
-
path: configPath,
|
|
376
|
-
},
|
|
377
|
-
],
|
|
378
|
-
};
|
|
379
|
-
}
|
|
380
|
-
const record = candidate;
|
|
381
|
-
const issues = [];
|
|
382
|
-
for (const key of Object.keys(record)) {
|
|
383
|
-
if (KNOWN_FILE_KEYS.has(key)) {
|
|
384
|
-
continue;
|
|
385
|
-
}
|
|
386
|
-
if (ROUTING_RULE_KEYS.has(key)) {
|
|
387
|
-
delete record[key];
|
|
388
|
-
continue;
|
|
389
|
-
}
|
|
390
|
-
if (key === REMOVED_FUNCTIONS_KEY) {
|
|
391
|
-
// The runtime declaration replaced this key outright (spec "The old
|
|
392
|
-
// top-level functions.* key is removed in the same cutover"): warning-and-
|
|
393
|
-
// strip would publish static files under a config that asked for a
|
|
394
|
-
// worker, so it blocks with its own code instead.
|
|
395
|
-
issues.push({
|
|
396
|
-
severity: "error",
|
|
397
|
-
code: "config_functions_key_removed",
|
|
398
|
-
message: `The "${REMOVED_FUNCTIONS_KEY}" key was removed from ${configPath}; declare runtime.kind "functions" instead.`,
|
|
399
|
-
path: key,
|
|
400
|
-
});
|
|
401
|
-
}
|
|
402
|
-
else if (ROUTING_KEYS.has(key)) {
|
|
403
|
-
issues.push({
|
|
404
|
-
severity: "error",
|
|
405
|
-
code: "config_invalid",
|
|
406
|
-
message: `Routing never lives in ${configPath}; move "${key}" into _redirects/_headers.`,
|
|
407
|
-
path: key,
|
|
408
|
-
});
|
|
409
|
-
}
|
|
410
|
-
else {
|
|
411
|
-
issues.push({
|
|
412
|
-
severity: "warning",
|
|
413
|
-
code: "config_invalid",
|
|
414
|
-
message: `Unknown ${configPath} key "${key}" was ignored.`,
|
|
415
|
-
path: key,
|
|
416
|
-
});
|
|
417
|
-
}
|
|
418
|
-
delete record[key];
|
|
419
|
-
}
|
|
420
|
-
issues.push(...capIssues(record), ...fileOnlyIssues(record));
|
|
421
|
-
if (issues.some((issue) => issue.severity === "error")) {
|
|
422
|
-
return { config: null, issues };
|
|
423
|
-
}
|
|
424
|
-
const parsed = spaceConfigFileSchema.safeParse(record);
|
|
425
|
-
if (!parsed.success) {
|
|
426
|
-
for (const issue of parsed.error.issues) {
|
|
427
|
-
issues.push({
|
|
428
|
-
severity: "error",
|
|
429
|
-
code: "config_invalid",
|
|
430
|
-
message: issue.message,
|
|
431
|
-
path: issue.path.map(String).join(".") || configPath,
|
|
432
|
-
});
|
|
433
|
-
}
|
|
434
|
-
return { config: null, issues };
|
|
435
|
-
}
|
|
436
|
-
return { config: parsed.data, issues };
|
|
437
|
-
}
|
|
438
|
-
// Mirrors the runtime's private-file rules (_stattic_runtime_file_private):
|
|
439
|
-
// convention/config files and dot-segment paths never serve, so they don't
|
|
440
|
-
// count toward root HTML inference below.
|
|
441
|
-
const PRIVATE_SERVING_FILES = new Set([
|
|
442
|
-
"_redirects",
|
|
443
|
-
"_headers",
|
|
444
|
-
"_config.json",
|
|
445
|
-
"_routes.json",
|
|
446
|
-
...SPACE_CONFIG_PRIVATE_FILES,
|
|
447
|
-
]);
|
|
448
|
-
function privateServingPath(path) {
|
|
449
|
-
if (PRIVATE_SERVING_FILES.has(path) ||
|
|
450
|
-
path === ".well-known/spacefast-runtime" ||
|
|
451
|
-
path === ".well-known/stattic-runtime") {
|
|
452
|
-
return true;
|
|
453
|
-
}
|
|
454
|
-
const segments = path.toLowerCase().split("/");
|
|
455
|
-
return segments.some((segment, index) => !(index === 0 && segment === ".well-known") && segment.startsWith("."));
|
|
456
|
-
}
|
|
457
|
-
function rootHtmlPaths(manifestPaths) {
|
|
458
|
-
const htmlPaths = [];
|
|
459
|
-
for (const path of manifestPaths) {
|
|
460
|
-
if (path === "" || privateServingPath(path)) {
|
|
461
|
-
continue;
|
|
462
|
-
}
|
|
463
|
-
if (!path.includes("/") && /\.html?$/i.test(path)) {
|
|
464
|
-
htmlPaths.push(path);
|
|
465
|
-
}
|
|
466
|
-
}
|
|
467
|
-
return htmlPaths.toSorted((left, right) => left.localeCompare(right));
|
|
468
|
-
}
|
|
469
|
-
function normalizeFallback(fallback) {
|
|
470
|
-
if (fallback === undefined) {
|
|
471
|
-
return null;
|
|
472
|
-
}
|
|
473
|
-
if (typeof fallback === "string") {
|
|
474
|
-
return { path: fallback.replace(/^\/+/, ""), status: 200 };
|
|
475
|
-
}
|
|
476
|
-
const status = fallback.status === 404 ? 404 : 200;
|
|
477
|
-
return { path: fallback.path.replace(/^\/+/, ""), status };
|
|
478
|
-
}
|
|
479
|
-
/**
|
|
480
|
-
* Resolves the effective SpaceConfig for a version at finalize (or
|
|
481
|
-
* config-only regeneration). `manifestPaths` is the canonical committed file
|
|
482
|
-
* set; `fileConfig` the parsed sf.jsonc (null when absent); `overlay`
|
|
483
|
-
* the stored space config overlay (null when nothing overridden).
|
|
484
|
-
*/
|
|
485
|
-
export function resolveEffectiveSpaceConfig(input) {
|
|
486
|
-
const rootHtml = rootHtmlPaths(input.manifestPaths);
|
|
487
|
-
const hasRootIndex = rootHtml.includes("index.html");
|
|
488
|
-
const inferredRootHtmlIndex = hasRootIndex ? "index.html" : (rootHtml[0] ?? null);
|
|
489
|
-
const explicitServingShape = input.fileConfig?.index !== undefined ||
|
|
490
|
-
input.fileConfig?.listing !== undefined ||
|
|
491
|
-
input.overlay?.index !== undefined ||
|
|
492
|
-
input.overlay?.listing !== undefined;
|
|
493
|
-
// Content-inferred defaults (spec: the old website/files modes are exactly
|
|
494
|
-
// these two default bundles; never API fields). A Functions version is an
|
|
495
|
-
// application even when it has no root HTML: unresolved requests, including
|
|
496
|
-
// `/`, belong to its worker. Static versions retain the content inference —
|
|
497
|
-
// a root index.html means website mode, any other root HTML becomes the
|
|
498
|
-
// homepage rewrite, and no root HTML means files mode.
|
|
499
|
-
const functionsApplication = input.runtimeKind === "functions";
|
|
500
|
-
const defaults = inferredRootHtmlIndex
|
|
501
|
-
? { index: inferredRootHtmlIndex, listing: false }
|
|
502
|
-
: { index: false, listing: !functionsApplication };
|
|
503
|
-
const merged = { ...defaults };
|
|
504
|
-
for (const layer of [input.fileConfig, input.overlay]) {
|
|
505
|
-
if (!layer) {
|
|
506
|
-
continue;
|
|
507
|
-
}
|
|
508
|
-
for (const key of Object.keys(spaceConfigSchema.shape)) {
|
|
509
|
-
const value = layer[key];
|
|
510
|
-
if (value !== undefined) {
|
|
511
|
-
merged[key] = value;
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
|
-
const issues = [];
|
|
516
|
-
const fallback = normalizeFallback(merged.fallback);
|
|
517
|
-
// Invariant (spec "Serving resolution"): a fallback must point at a
|
|
518
|
-
// committed file — no implicit extension guessing, fail at finalize.
|
|
519
|
-
if (fallback && !input.manifestPaths.has(fallback.path)) {
|
|
520
|
-
issues.push({
|
|
521
|
-
severity: "error",
|
|
522
|
-
code: "config_invalid",
|
|
523
|
-
message: `fallback path "/${fallback.path}" is not a committed file in this version.`,
|
|
524
|
-
path: "fallback.path",
|
|
525
|
-
details: { path: fallback.path },
|
|
526
|
-
});
|
|
527
|
-
}
|
|
528
|
-
const listing = merged.listing ?? (!functionsApplication && !inferredRootHtmlIndex);
|
|
529
|
-
const serving = {
|
|
530
|
-
index: merged.index ?? inferredRootHtmlIndex ?? false,
|
|
531
|
-
fallback,
|
|
532
|
-
// Clean URLs (runtime W7.1): an explicit config value wins; the default is
|
|
533
|
-
// ON for plain static sites and OFF when a 200-status SPA fallback owns
|
|
534
|
-
// extensionless routes (the fallback answers them instead). The runtime
|
|
535
|
-
// resolver (management.php) applies the same default for artifacts
|
|
536
|
-
// finalized without the key.
|
|
537
|
-
cleanUrls: merged.cleanUrls ?? !(fallback && fallback.status === 200),
|
|
538
|
-
listing,
|
|
539
|
-
// The single-file viewer rides the listing bundle (artifact-style spaces).
|
|
540
|
-
viewer: listing,
|
|
541
|
-
meta: merged.meta ?? null,
|
|
542
|
-
pageTheme: merged.theme && Object.keys(merged.theme).length > 0 ? merged.theme : null,
|
|
543
|
-
experimentalGutenberg: merged.experimental_gutenberg === true,
|
|
544
|
-
inject: merged.inject ?? null,
|
|
545
|
-
};
|
|
546
|
-
if (inferredRootHtmlIndex !== null && !hasRootIndex && serving.index === inferredRootHtmlIndex) {
|
|
547
|
-
// Surfaced on the publish receipt and version diagnostics so the CLI and
|
|
548
|
-
// agents learn the rewrite without probing the live URL.
|
|
549
|
-
issues.push({
|
|
550
|
-
severity: "info",
|
|
551
|
-
code: "single_html_index",
|
|
552
|
-
message: `${inferredRootHtmlIndex} is the root HTML file selected for this version, so it serves as the homepage: / rewrites to /${inferredRootHtmlIndex}.`,
|
|
553
|
-
path: "index",
|
|
554
|
-
details: { path: inferredRootHtmlIndex, rootHtmlCount: rootHtml.length },
|
|
555
|
-
});
|
|
556
|
-
}
|
|
557
|
-
if (inferredRootHtmlIndex === null &&
|
|
558
|
-
serving.index === false &&
|
|
559
|
-
serving.listing &&
|
|
560
|
-
!explicitServingShape) {
|
|
561
|
-
issues.push({
|
|
562
|
-
severity: "info",
|
|
563
|
-
code: "files_mode_inferred",
|
|
564
|
-
message: "No HTML files were found in the root of this version, so it serves in files mode with directory listing enabled.",
|
|
565
|
-
path: "listing",
|
|
566
|
-
details: { rootHtmlCount: 0 },
|
|
567
|
-
});
|
|
568
|
-
}
|
|
569
|
-
return { config: merged, serving, issues };
|
|
570
|
-
}
|
|
@@ -5,14 +5,20 @@ export declare const PLATFORM_MANAGED_RESPONSE_HEADERS: string[];
|
|
|
5
5
|
export declare const PLATFORM_MANAGED_RESPONSE_HEADER_PREFIXES: string[];
|
|
6
6
|
/** True when user rules may neither set nor remove this response header. */
|
|
7
7
|
export declare function isPlatformManagedResponseHeader(name: string): boolean;
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
/**
|
|
9
|
+
* The token grammar a `_headers` rule accepts for a header NAME. The routing
|
|
10
|
+
* compiler enforces it; anything that GENERATES `_headers` rules (the Next
|
|
11
|
+
* materializer's framework sections) must gate on the same pattern, so a
|
|
12
|
+
* generated rule can never fail the author's publish.
|
|
13
|
+
*/
|
|
14
|
+
export declare const HEADER_RULE_NAME_PATTERN: RegExp;
|
|
15
|
+
/** `content-type` → `Content-Type`: the casing compiled rules and generated `_headers` carry. */
|
|
16
|
+
export declare function canonicalHeaderName(input: string): string;
|
|
10
17
|
/** Lowercased extension without the leading dot, posix semantics. Dotfiles have no extension. */
|
|
11
18
|
export declare function pathExtension(filePath: string): string;
|
|
12
19
|
/** Basename without its extension (the "stem"), posix semantics. */
|
|
13
20
|
export declare function pathStem(filePath: string): string;
|
|
14
21
|
export declare function isPhpLikeStaticPath(filePath: string): boolean;
|
|
15
|
-
export declare function isImmutableAssetPath(filePath: string): boolean;
|
|
16
22
|
/** Forced inert serving policy for a committed static file. PHP-like files are never executable. */
|
|
17
23
|
export declare function runtimeFileSafety(filePath: string): {
|
|
18
24
|
executable: false;
|
|
@@ -1,44 +1,33 @@
|
|
|
1
1
|
{
|
|
2
2
|
"phpLike": [
|
|
3
|
-
{
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
{
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
{
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
{
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
{
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
{
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
{ "path": "assets/index.js", "expected": false },
|
|
32
|
-
{ "path": "assets/client-widget.js", "expected": false },
|
|
33
|
-
{ "path": "app.1a2b3c4d.css", "expected": false },
|
|
34
|
-
{ "path": "vendor.3f9a2b7e.js", "expected": false },
|
|
35
|
-
{ "path": "static/js/main.d41d8cd98f00b204e9800998.js", "expected": false },
|
|
36
|
-
{ "path": "index.html", "expected": false },
|
|
37
|
-
{ "path": "robots.txt", "expected": false },
|
|
38
|
-
{ "path": "config.xml", "expected": false },
|
|
39
|
-
{ "path": "page.php", "expected": false },
|
|
40
|
-
{ "path": "MyComponent.js", "expected": false },
|
|
41
|
-
{ "path": "image01.png", "expected": false }
|
|
3
|
+
{
|
|
4
|
+
"path": "index.php",
|
|
5
|
+
"expected": true
|
|
6
|
+
},
|
|
7
|
+
{
|
|
8
|
+
"path": "nested/app.phtml",
|
|
9
|
+
"expected": true
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"path": "lib.phar",
|
|
13
|
+
"expected": true
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"path": "legacy.php5",
|
|
17
|
+
"expected": true
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"path": "index.html",
|
|
21
|
+
"expected": false
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"path": "assets/main.js",
|
|
25
|
+
"expected": false
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"path": ".env",
|
|
29
|
+
"expected": false
|
|
30
|
+
}
|
|
42
31
|
],
|
|
43
32
|
"safeContentType": [
|
|
44
33
|
{
|
|
@@ -46,12 +35,20 @@
|
|
|
46
35
|
"mime": "application/x-httpd-php",
|
|
47
36
|
"expected": "text/plain; charset=utf-8"
|
|
48
37
|
},
|
|
49
|
-
{
|
|
38
|
+
{
|
|
39
|
+
"path": "lib.phar",
|
|
40
|
+
"mime": "application/zip",
|
|
41
|
+
"expected": "application/octet-stream"
|
|
42
|
+
},
|
|
50
43
|
{
|
|
51
44
|
"path": "assets/main.js",
|
|
52
45
|
"mime": "text/javascript; charset=utf-8",
|
|
53
46
|
"expected": "text/javascript; charset=utf-8"
|
|
54
47
|
},
|
|
55
|
-
{
|
|
48
|
+
{
|
|
49
|
+
"path": "logo.svg",
|
|
50
|
+
"mime": "image/svg+xml",
|
|
51
|
+
"expected": "image/svg+xml"
|
|
52
|
+
}
|
|
56
53
|
]
|
|
57
54
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Single source of truth for static-runtime content policy: PHP-like inert
|
|
2
|
-
// classification
|
|
2
|
+
// classification and safe content-type forcing.
|
|
3
3
|
//
|
|
4
4
|
// This module is intentionally pure (no node:path, no I/O) so the exact same
|
|
5
5
|
// rules can be mechanically code-generated into the PHP runtime (runtime
|
|
@@ -30,7 +30,7 @@ export const INTERNAL_REDIRECT_RESPONSE_HEADERS = [
|
|
|
30
30
|
"x-reproxy-url",
|
|
31
31
|
"x-sendfile",
|
|
32
32
|
];
|
|
33
|
-
// Rejected/platform-managed response headers (
|
|
33
|
+
// Rejected/platform-managed response headers (the platform spec "Routing And
|
|
34
34
|
// Headers"): user `_headers` rules can never set or remove these. The
|
|
35
35
|
// internal-redirect/sendfile family above is spread in, so it stays a strict subset —
|
|
36
36
|
// anything the proxy lane refuses to relay is also refused from `_headers`. The rest is
|
|
@@ -87,52 +87,27 @@ export function isPlatformManagedResponseHeader(name) {
|
|
|
87
87
|
return (PLATFORM_MANAGED_RESPONSE_HEADER_SET.has(lower) ||
|
|
88
88
|
PLATFORM_MANAGED_RESPONSE_HEADER_PREFIXES.some((prefix) => lower.startsWith(prefix)));
|
|
89
89
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
// Each prefix is HIGH-CONFIDENCE guaranteed-hashed:
|
|
106
|
-
// - `_next/static/` Next.js immutable build assets (chunks/css/media), all
|
|
107
|
-
// [contenthash]; Next itself serves this dir with `immutable`.
|
|
108
|
-
// - `_app/immutable/` SvelteKit (Vite) — the directory is literally named `immutable`
|
|
109
|
-
// and holds only fingerprinted chunks/assets.
|
|
110
|
-
// - `_nuxt/` Nuxt build dir — Vite-hashed client bundle, served from a
|
|
111
|
-
// reserved app-owned path.
|
|
112
|
-
// - `_astro/` Astro bundled assets — all content-hashed.
|
|
113
|
-
//
|
|
114
|
-
// DELIBERATELY EXCLUDED: generic user-writable dirs (Vite's `assets/`, CRA/Gatsby's
|
|
115
|
-
// `static/`). Users drop NON-hashed files there, so widening to them would create
|
|
116
|
-
// year-long false positives. Deployments of bundler output declare those dirs immutable
|
|
117
|
-
// themselves via a `_headers` rule (e.g. `/assets/*` → `Cache-Control: public,
|
|
118
|
-
// max-age=31536000, immutable`), which the publisher — a build we ran, an agent, a CI
|
|
119
|
-
// deploy — writes because it KNOWS the output is content-hashed.
|
|
120
|
-
export const GUARANTEED_IMMUTABLE_PREFIXES = [
|
|
121
|
-
"_next/static/",
|
|
122
|
-
"_app/immutable/",
|
|
123
|
-
"_nuxt/",
|
|
124
|
-
"_astro/",
|
|
125
|
-
];
|
|
126
|
-
/** True when the path lives under a framework dir where every emitted asset is content-hashed. */
|
|
127
|
-
function isUnderGuaranteedImmutablePrefix(filePath) {
|
|
128
|
-
const normalized = filePath.startsWith("/") ? filePath.slice(1) : filePath;
|
|
129
|
-
for (const prefix of GUARANTEED_IMMUTABLE_PREFIXES) {
|
|
130
|
-
if (normalized.startsWith(prefix)) {
|
|
131
|
-
return true;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
return false;
|
|
90
|
+
/**
|
|
91
|
+
* The token grammar a `_headers` rule accepts for a header NAME. The routing
|
|
92
|
+
* compiler enforces it; anything that GENERATES `_headers` rules (the Next
|
|
93
|
+
* materializer's framework sections) must gate on the same pattern, so a
|
|
94
|
+
* generated rule can never fail the author's publish.
|
|
95
|
+
*/
|
|
96
|
+
export const HEADER_RULE_NAME_PATTERN = /^[A-Za-z0-9-]+$/;
|
|
97
|
+
/** `content-type` → `Content-Type`: the casing compiled rules and generated `_headers` carry. */
|
|
98
|
+
export function canonicalHeaderName(input) {
|
|
99
|
+
return input
|
|
100
|
+
.trim()
|
|
101
|
+
.toLowerCase()
|
|
102
|
+
.split("-")
|
|
103
|
+
.map((part) => (part ? `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}` : part))
|
|
104
|
+
.join("-");
|
|
135
105
|
}
|
|
106
|
+
const PHP_LIKE_EXTENSION_SET = new Set(PHP_LIKE_EXTENSIONS);
|
|
107
|
+
// Immutable-asset detection is NOT decided here: `compiled_cache_class` in
|
|
108
|
+
// crates/stattic-runtime-core (responses.rs) is the one implementation, at
|
|
109
|
+
// finalize. A TS re-implementation lived here once, drifted from the
|
|
110
|
+
// enforcer, and was deleted rather than re-synced.
|
|
136
111
|
function lastSegment(filePath) {
|
|
137
112
|
const slash = filePath.lastIndexOf("/");
|
|
138
113
|
return slash >= 0 ? filePath.slice(slash + 1) : filePath;
|
|
@@ -152,15 +127,6 @@ export function pathStem(filePath) {
|
|
|
152
127
|
export function isPhpLikeStaticPath(filePath) {
|
|
153
128
|
return PHP_LIKE_EXTENSION_SET.has(pathExtension(filePath));
|
|
154
129
|
}
|
|
155
|
-
export function isImmutableAssetPath(filePath) {
|
|
156
|
-
if (NON_IMMUTABLE_EXTENSION_SET.has(pathExtension(filePath))) {
|
|
157
|
-
return false;
|
|
158
|
-
}
|
|
159
|
-
// Path-convention layer: anything under a guaranteed-content-hashed framework build
|
|
160
|
-
// dir is immutable regardless of filename. The extension gate above still applies,
|
|
161
|
-
// so an `index.html` accidentally under `_next/static/` is never pinned.
|
|
162
|
-
return isUnderGuaranteedImmutablePrefix(filePath);
|
|
163
|
-
}
|
|
164
130
|
/** Forced inert serving policy for a committed static file. PHP-like files are never executable. */
|
|
165
131
|
export function runtimeFileSafety(filePath) {
|
|
166
132
|
return {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export declare const STORAGE_OBJECT_MAX_FILE_BYTES: number;
|
|
2
|
-
export declare const STORAGE_OBJECT_MAX_OWNER_BYTES: number;
|
|
3
2
|
export declare const STORAGE_CONTENT_SNIFF_BYTES = 512;
|
|
3
|
+
export declare const STORAGE_ANON_DAILY_BYTES: number;
|
|
4
4
|
export declare const STORAGE_BLOCKED_CONTENT_TYPES: string[];
|
|
5
5
|
export declare const STORAGE_BLOCKED_CONTENT_TYPE_FRAGMENTS: string[];
|
|
6
6
|
export declare const STORAGE_BLOCKED_MAGIC_HEX: string[];
|
|
@@ -5,8 +5,10 @@
|
|
|
5
5
|
// `bun --filter @spacefast/control-plane runtime:codegen-policy`. Both execute
|
|
6
6
|
// the same storage-policy.fixtures.json corpus.
|
|
7
7
|
export const STORAGE_OBJECT_MAX_FILE_BYTES = 5 * 1024 * 1024;
|
|
8
|
-
export const STORAGE_OBJECT_MAX_OWNER_BYTES = 100 * 1024 * 1024;
|
|
9
8
|
export const STORAGE_CONTENT_SNIFF_BYTES = 512;
|
|
9
|
+
// Anonymous commenters share a per-space rolling daily upload budget; identified
|
|
10
|
+
// principals and Functions relays are accountable and carry no budget.
|
|
11
|
+
export const STORAGE_ANON_DAILY_BYTES = 200 * 1024 * 1024;
|
|
10
12
|
export const STORAGE_BLOCKED_CONTENT_TYPES = [
|
|
11
13
|
"application/java-archive",
|
|
12
14
|
"application/java-vm",
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare class StructuredError extends Error {
|
|
2
|
+
readonly code: string;
|
|
3
|
+
readonly details: Record<string, unknown> | undefined;
|
|
4
|
+
readonly exitCode: number;
|
|
5
|
+
readonly type: string | undefined;
|
|
6
|
+
constructor(input: {
|
|
7
|
+
code: string;
|
|
8
|
+
message: string;
|
|
9
|
+
details?: Record<string, unknown> | undefined;
|
|
10
|
+
exitCode?: number | undefined;
|
|
11
|
+
type?: string | undefined;
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
export declare function structuredValidationError(message: string, details?: Record<string, unknown>): StructuredError;
|