@spacefast/common 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -0
- package/dist/brand.d.ts +15 -0
- package/dist/brand.js +24 -0
- package/dist/config/domains.d.ts +30 -0
- package/dist/config/domains.js +75 -0
- package/dist/config/index.d.ts +1 -0
- package/dist/config/index.js +3 -0
- package/dist/contracts/abuse.d.ts +93 -0
- package/dist/contracts/abuse.js +50 -0
- package/dist/contracts/access.d.ts +409 -0
- package/dist/contracts/access.js +396 -0
- package/dist/contracts/account.d.ts +28 -0
- package/dist/contracts/account.js +54 -0
- package/dist/contracts/activity.d.ts +118 -0
- package/dist/contracts/activity.js +180 -0
- package/dist/contracts/analytics.d.ts +56 -0
- package/dist/contracts/analytics.js +91 -0
- package/dist/contracts/annotations.d.ts +327 -0
- package/dist/contracts/annotations.js +234 -0
- package/dist/contracts/api-keys.d.ts +517 -0
- package/dist/contracts/api-keys.js +235 -0
- package/dist/contracts/archives.d.ts +332 -0
- package/dist/contracts/archives.js +120 -0
- package/dist/contracts/auth.d.ts +86 -0
- package/dist/contracts/auth.js +82 -0
- package/dist/contracts/beta.d.ts +106 -0
- package/dist/contracts/beta.js +104 -0
- package/dist/contracts/billing.d.ts +328 -0
- package/dist/contracts/billing.js +203 -0
- package/dist/contracts/builds.d.ts +504 -0
- package/dist/contracts/builds.js +248 -0
- package/dist/contracts/channels.d.ts +49 -0
- package/dist/contracts/channels.js +51 -0
- package/dist/contracts/common.d.ts +238 -0
- package/dist/contracts/common.js +175 -0
- package/dist/contracts/deployments.d.ts +574 -0
- package/dist/contracts/deployments.js +97 -0
- package/dist/contracts/device-auth.d.ts +194 -0
- package/dist/contracts/device-auth.js +162 -0
- package/dist/contracts/domains.d.ts +6115 -0
- package/dist/contracts/domains.js +1435 -0
- package/dist/contracts/enums.d.ts +196 -0
- package/dist/contracts/enums.js +211 -0
- package/dist/contracts/error-code-meta.d.ts +1560 -0
- package/dist/contracts/error-code-meta.js +400 -0
- package/dist/contracts/error-codes.d.ts +13 -0
- package/dist/contracts/error-codes.js +416 -0
- package/dist/contracts/events.d.ts +398 -0
- package/dist/contracts/events.js +320 -0
- package/dist/contracts/fast-domain-search.d.ts +19 -0
- package/dist/contracts/fast-domain-search.js +22 -0
- package/dist/contracts/features.d.ts +240 -0
- package/dist/contracts/features.js +406 -0
- package/dist/contracts/git.d.ts +288 -0
- package/dist/contracts/git.js +116 -0
- package/dist/contracts/ids.d.ts +35 -0
- package/dist/contracts/ids.js +45 -0
- package/dist/contracts/intercom.d.ts +6 -0
- package/dist/contracts/intercom.js +5 -0
- package/dist/contracts/internal.d.ts +103 -0
- package/dist/contracts/internal.js +92 -0
- package/dist/contracts/mcp.d.ts +788 -0
- package/dist/contracts/mcp.js +161 -0
- package/dist/contracts/me.d.ts +81 -0
- package/dist/contracts/me.js +104 -0
- package/dist/contracts/oauth-resources.d.ts +30 -0
- package/dist/contracts/oauth-resources.js +61 -0
- package/dist/contracts/operations.d.ts +54 -0
- package/dist/contracts/operations.js +14 -0
- package/dist/contracts/plan-policy.d.ts +85 -0
- package/dist/contracts/plan-policy.js +75 -0
- package/dist/contracts/platform.d.ts +359 -0
- package/dist/contracts/platform.js +216 -0
- package/dist/contracts/push-new.d.ts +156 -0
- package/dist/contracts/push-new.js +52 -0
- package/dist/contracts/quotas.d.ts +16 -0
- package/dist/contracts/quotas.js +36 -0
- package/dist/contracts/repository-connections.d.ts +231 -0
- package/dist/contracts/repository-connections.js +106 -0
- package/dist/contracts/resources.d.ts +529 -0
- package/dist/contracts/resources.js +209 -0
- package/dist/contracts/routes.d.ts +45 -0
- package/dist/contracts/routes.js +31 -0
- package/dist/contracts/runtime-api.d.ts +449 -0
- package/dist/contracts/runtime-api.js +513 -0
- package/dist/contracts/sites.d.ts +393 -0
- package/dist/contracts/sites.js +121 -0
- package/dist/contracts/slugs.d.ts +31 -0
- package/dist/contracts/slugs.js +47 -0
- package/dist/contracts/space-config.d.ts +216 -0
- package/dist/contracts/space-config.js +237 -0
- package/dist/contracts/space-theme.d.ts +25 -0
- package/dist/contracts/space-theme.js +49 -0
- package/dist/contracts/spaces.d.ts +2760 -0
- package/dist/contracts/spaces.js +589 -0
- package/dist/contracts/superadmin-activity.d.ts +205 -0
- package/dist/contracts/superadmin-activity.js +107 -0
- package/dist/contracts/superadmin-emails.d.ts +212 -0
- package/dist/contracts/superadmin-emails.js +112 -0
- package/dist/contracts/superadmin-queues.d.ts +414 -0
- package/dist/contracts/superadmin-queues.js +202 -0
- package/dist/contracts/superadmin-runtime.d.ts +27 -0
- package/dist/contracts/superadmin-runtime.js +38 -0
- package/dist/contracts/superadmin-spaces.d.ts +429 -0
- package/dist/contracts/superadmin-spaces.js +82 -0
- package/dist/contracts/superadmin-tenants.d.ts +180 -0
- package/dist/contracts/superadmin-tenants.js +117 -0
- package/dist/contracts/superadmin.d.ts +654 -0
- package/dist/contracts/superadmin.js +309 -0
- package/dist/contracts/tags.d.ts +1140 -0
- package/dist/contracts/tags.js +1050 -0
- package/dist/contracts/teams.d.ts +233 -0
- package/dist/contracts/teams.js +174 -0
- package/dist/contracts/telemetry.d.ts +2 -0
- package/dist/contracts/telemetry.js +1 -0
- package/dist/contracts/transfers.d.ts +105 -0
- package/dist/contracts/transfers.js +48 -0
- package/dist/contracts/variables.d.ts +189 -0
- package/dist/contracts/variables.js +82 -0
- package/dist/contracts/webhooks.d.ts +159 -0
- package/dist/contracts/webhooks.js +59 -0
- package/dist/contracts/zero.d.ts +1538 -0
- package/dist/contracts/zero.js +755 -0
- package/dist/docs/agent-prose.d.ts +35 -0
- package/dist/docs/agent-prose.js +153 -0
- package/dist/docs/agent-setup.d.ts +6 -0
- package/dist/docs/agent-setup.js +164 -0
- package/dist/docs/agent-solutions.d.ts +110 -0
- package/dist/docs/agent-solutions.js +181 -0
- package/dist/docs/error-docs.d.ts +12 -0
- package/dist/docs/error-docs.js +1601 -0
- package/dist/docs/skill-distribution.d.ts +12 -0
- package/dist/docs/skill-distribution.js +18 -0
- package/dist/domain-exploration.d.ts +108 -0
- package/dist/domain-exploration.js +488 -0
- package/dist/domain-normalize.d.ts +1 -0
- package/dist/domain-normalize.js +14 -0
- package/dist/domain-zones.d.ts +1 -0
- package/dist/domain-zones.js +5272 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/utils/access-policy.d.ts +39 -0
- package/dist/utils/access-policy.js +232 -0
- package/dist/utils/api.d.ts +4 -0
- package/dist/utils/api.js +13 -0
- package/dist/utils/auth-redirect.d.ts +1 -0
- package/dist/utils/auth-redirect.js +48 -0
- package/dist/utils/auth.d.ts +5 -0
- package/dist/utils/auth.js +13 -0
- package/dist/utils/build-settings.d.ts +49 -0
- package/dist/utils/build-settings.js +366 -0
- package/dist/utils/claim-token.d.ts +1 -0
- package/dist/utils/claim-token.js +9 -0
- package/dist/utils/collections.d.ts +1 -0
- package/dist/utils/collections.js +5 -0
- package/dist/utils/concurrency.d.ts +1 -0
- package/dist/utils/concurrency.js +18 -0
- package/dist/utils/content-type.d.ts +10 -0
- package/dist/utils/content-type.js +145 -0
- package/dist/utils/error-display.d.ts +7 -0
- package/dist/utils/error-display.js +42 -0
- package/dist/utils/error.d.ts +1 -0
- package/dist/utils/error.js +3 -0
- package/dist/utils/generate-space-name.d.ts +14 -0
- package/dist/utils/generate-space-name.js +95 -0
- package/dist/utils/git-repository.d.ts +5 -0
- package/dist/utils/git-repository.js +99 -0
- package/dist/utils/guards.d.ts +1 -0
- package/dist/utils/guards.js +3 -0
- package/dist/utils/publish-detection.d.ts +10 -0
- package/dist/utils/publish-detection.js +118 -0
- package/dist/utils/publish-policy.d.ts +69 -0
- package/dist/utils/publish-policy.js +429 -0
- package/dist/utils/query-client.d.ts +7 -0
- package/dist/utils/query-client.js +21 -0
- package/dist/utils/query-keys.d.ts +75 -0
- package/dist/utils/query-keys.js +78 -0
- package/dist/utils/runtime-paths.d.ts +7 -0
- package/dist/utils/runtime-paths.js +28 -0
- package/dist/utils/runtime-upload.d.ts +19 -0
- package/dist/utils/runtime-upload.js +27 -0
- package/dist/utils/slug.d.ts +5 -0
- package/dist/utils/slug.js +13 -0
- package/dist/utils/space-config.d.ts +75 -0
- package/dist/utils/space-config.js +415 -0
- package/dist/utils/space-theme.d.ts +16 -0
- package/dist/utils/space-theme.js +89 -0
- package/dist/utils/static-runtime-policy.d.ts +17 -0
- package/dist/utils/static-runtime-policy.js +103 -0
- package/dist/utils/upload-session.d.ts +34 -0
- package/dist/utils/upload-session.js +69 -0
- package/dist/utils/urls.d.ts +2 -0
- package/dist/utils/urls.js +7 -0
- package/dist/utils/version-path.d.ts +6 -0
- package/dist/utils/version-path.js +51 -0
- package/dist/utils/wpcom-auth-redirects.d.ts +14 -0
- package/dist/utils/wpcom-auth-redirects.js +49 -0
- package/dist/utils/zero-realtime-signature.d.ts +12 -0
- package/dist/utils/zero-realtime-signature.js +45 -0
- package/dist/vocabulary.d.ts +230 -0
- package/dist/vocabulary.js +218 -0
- package/package.json +58 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export declare const RUNTIME_UPLOAD_METHOD: "PUT";
|
|
2
|
+
export declare function runtimeUploadCanonicalPath(path: string): string;
|
|
3
|
+
export declare function runtimeUploadSessionBaseUrl(input: {
|
|
4
|
+
baseUrl: string;
|
|
5
|
+
uploadId: string;
|
|
6
|
+
}): string;
|
|
7
|
+
export declare function runtimeUploadBatchUrl(input: {
|
|
8
|
+
baseUrl: string;
|
|
9
|
+
uploadId: string;
|
|
10
|
+
}): string;
|
|
11
|
+
export declare function runtimeUploadFetchUrl(input: {
|
|
12
|
+
baseUrl: string;
|
|
13
|
+
uploadId: string;
|
|
14
|
+
path: string;
|
|
15
|
+
}): string;
|
|
16
|
+
export declare function runtimeUploadFileUrl(input: {
|
|
17
|
+
baseUrl: string;
|
|
18
|
+
path: string;
|
|
19
|
+
}): string;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { runtimeUploadPath } from "./runtime-paths.js";
|
|
2
|
+
export const RUNTIME_UPLOAD_METHOD = "PUT";
|
|
3
|
+
export function runtimeUploadCanonicalPath(path) {
|
|
4
|
+
// Canonical path form (spec "Upload Contract"): UTF-8, Unicode NFC,
|
|
5
|
+
// percent-encoded once. The runtime rejects non-canonical encodings.
|
|
6
|
+
return path
|
|
7
|
+
.normalize("NFC")
|
|
8
|
+
.split("/")
|
|
9
|
+
.map((segment) => encodeURIComponent(segment))
|
|
10
|
+
.join("/");
|
|
11
|
+
}
|
|
12
|
+
export function runtimeUploadSessionBaseUrl(input) {
|
|
13
|
+
return new URL(runtimeUploadPath({ op: "file", upload_id: input.uploadId, path: "" }), input.baseUrl).toString();
|
|
14
|
+
}
|
|
15
|
+
export function runtimeUploadBatchUrl(input) {
|
|
16
|
+
return new URL(runtimeUploadPath({ op: "batch", upload_id: input.uploadId }), input.baseUrl).toString();
|
|
17
|
+
}
|
|
18
|
+
export function runtimeUploadFetchUrl(input) {
|
|
19
|
+
return new URL(runtimeUploadPath({
|
|
20
|
+
op: "fetch",
|
|
21
|
+
upload_id: input.uploadId,
|
|
22
|
+
path: runtimeUploadCanonicalPath(input.path),
|
|
23
|
+
}), input.baseUrl).toString();
|
|
24
|
+
}
|
|
25
|
+
export function runtimeUploadFileUrl(input) {
|
|
26
|
+
return `${input.baseUrl}${runtimeUploadCanonicalPath(input.path)}`;
|
|
27
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export function normalizeSlug(input, options) {
|
|
2
|
+
const maxLength = options?.maxLength ?? Number.POSITIVE_INFINITY;
|
|
3
|
+
return input
|
|
4
|
+
.trim()
|
|
5
|
+
.normalize("NFKD")
|
|
6
|
+
.replace(/[\u0300-\u036f]/g, "")
|
|
7
|
+
.toLowerCase()
|
|
8
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
9
|
+
.replace(/^-+|-+$/g, "")
|
|
10
|
+
.replace(/-{2,}/g, "-")
|
|
11
|
+
.slice(0, maxLength)
|
|
12
|
+
.replace(/^-+|-+$/g, "");
|
|
13
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { type SpaceConfig, type SpaceConfigFile } from "../contracts/space-config.js";
|
|
2
|
+
export type SpaceConfigBlockingIssueCode = "config_file_too_large" | "config_templates_over_limit" | "config_meta_too_long" | "config_invalid" | "inject_snippet_too_large" | "inject_invalid";
|
|
3
|
+
export type SpaceConfigIssue = {
|
|
4
|
+
severity: "info" | "warning" | "error";
|
|
5
|
+
code: SpaceConfigBlockingIssueCode | "single_html_index" | "files_mode_inferred";
|
|
6
|
+
message: string;
|
|
7
|
+
/** JSON-path-lite pointer into the config shape (e.g. `meta.title`). */
|
|
8
|
+
path?: string;
|
|
9
|
+
details?: Record<string, unknown>;
|
|
10
|
+
};
|
|
11
|
+
type SpaceConfigBlockingIssue = SpaceConfigIssue & {
|
|
12
|
+
code: SpaceConfigBlockingIssueCode;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Validates a space `config` overlay at API intake (PATCH /spaces, publish
|
|
16
|
+
* `config`): the 16 KB compiled-overlay cap plus the shared shape caps.
|
|
17
|
+
* Returns the first blocking issue, or null when the overlay is acceptable.
|
|
18
|
+
*/
|
|
19
|
+
export declare function spaceConfigOverlayIssue(config: SpaceConfig): SpaceConfigBlockingIssue | null;
|
|
20
|
+
export declare function stripJsonComments(raw: string): string;
|
|
21
|
+
export type ParsedSpaceConfigFile = {
|
|
22
|
+
/** Parsed config (cap- and shape-valid keys only); null when unusable. */
|
|
23
|
+
config: SpaceConfigFile | null;
|
|
24
|
+
issues: SpaceConfigIssue[];
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Parses a raw `sf.jsonc` at finalize: 256 KB cap, JSONC tolerance,
|
|
28
|
+
* routing-key rejection, unknown-key diagnostics, shared shape caps. Errors
|
|
29
|
+
* fail the version; warnings ride version diagnostics.
|
|
30
|
+
*/
|
|
31
|
+
export declare function parseSpaceConfigFile(raw: string, options?: {
|
|
32
|
+
path?: string;
|
|
33
|
+
}): ParsedSpaceConfigFile;
|
|
34
|
+
export type ResolvedServingConfig = {
|
|
35
|
+
index: string | false;
|
|
36
|
+
fallback: {
|
|
37
|
+
path: string;
|
|
38
|
+
status: 200 | 404;
|
|
39
|
+
} | null;
|
|
40
|
+
listing: boolean;
|
|
41
|
+
viewer: boolean;
|
|
42
|
+
meta: NonNullable<SpaceConfig["meta"]> | null;
|
|
43
|
+
/** Hidden experimental build-pipeline flag; false keeps today's behavior. */
|
|
44
|
+
experimentalGutenberg: boolean;
|
|
45
|
+
/** Resolved inject snippets; only honored when experimentalGutenberg is true. */
|
|
46
|
+
inject: NonNullable<SpaceConfig["inject"]> | null;
|
|
47
|
+
};
|
|
48
|
+
export type EffectiveSpaceConfig = {
|
|
49
|
+
/** The merged SpaceConfig (defaults + file + overlay, key-wise). */
|
|
50
|
+
config: SpaceConfig;
|
|
51
|
+
/** Compiled serving primitives the runtime artifact stores. */
|
|
52
|
+
serving: ResolvedServingConfig;
|
|
53
|
+
issues: SpaceConfigIssue[];
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Resolves the effective SpaceConfig for a version at finalize (or
|
|
57
|
+
* config-only regeneration). `manifestPaths` is the canonical committed file
|
|
58
|
+
* set; `fileConfig` the parsed sf.jsonc (null when absent); `overlay`
|
|
59
|
+
* the stored space config overlay (null when nothing overridden).
|
|
60
|
+
*/
|
|
61
|
+
export declare function resolveEffectiveSpaceConfig(input: {
|
|
62
|
+
manifestPaths: ReadonlySet<string>;
|
|
63
|
+
fileConfig: SpaceConfig | null;
|
|
64
|
+
overlay: SpaceConfig | null;
|
|
65
|
+
}): EffectiveSpaceConfig;
|
|
66
|
+
export type SpaceSettingsPatch = {
|
|
67
|
+
mode?: "website" | "files" | undefined;
|
|
68
|
+
spa?: boolean | undefined;
|
|
69
|
+
viewer?: {
|
|
70
|
+
title?: string | null | undefined;
|
|
71
|
+
description?: string | null | undefined;
|
|
72
|
+
ogImagePath?: string | null | undefined;
|
|
73
|
+
} | undefined;
|
|
74
|
+
};
|
|
75
|
+
export {};
|
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
import { SPACE_CONFIG_CAPS, SPACE_CONFIG_CANONICAL_FILE, SPACE_CONFIG_PRIVATE_FILES, spaceConfigFileSchema, spaceConfigSchema, } from "../contracts/space-config.js";
|
|
2
|
+
const TEXT_BYTES = new TextEncoder();
|
|
3
|
+
function byteLength(value) {
|
|
4
|
+
return TEXT_BYTES.encode(value).length;
|
|
5
|
+
}
|
|
6
|
+
function capIssues(candidate) {
|
|
7
|
+
const issues = [];
|
|
8
|
+
const templates = candidate.templates;
|
|
9
|
+
if (Array.isArray(templates) && templates.length > SPACE_CONFIG_CAPS.templates) {
|
|
10
|
+
issues.push({
|
|
11
|
+
severity: "error",
|
|
12
|
+
code: "config_templates_over_limit",
|
|
13
|
+
message: `templates supports up to ${SPACE_CONFIG_CAPS.templates} entries.`,
|
|
14
|
+
path: "templates",
|
|
15
|
+
details: { count: templates.length, limit: SPACE_CONFIG_CAPS.templates },
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
// inject snippets (hidden feature, honored only with experimental_gutenberg):
|
|
19
|
+
// shape and byte caps fail with the stable codes here, before zod, so intake
|
|
20
|
+
// reports inject_invalid / inject_snippet_too_large instead of shape errors.
|
|
21
|
+
const inject = candidate.inject;
|
|
22
|
+
if (inject !== undefined) {
|
|
23
|
+
if (typeof inject !== "object" || inject === null || Array.isArray(inject)) {
|
|
24
|
+
issues.push({
|
|
25
|
+
severity: "error",
|
|
26
|
+
code: "inject_invalid",
|
|
27
|
+
message: "inject must be an object with static-placement snippet arrays.",
|
|
28
|
+
path: "inject",
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
for (const key of ["head", "bodyStart", "bodyEnd", "noscript"]) {
|
|
33
|
+
const snippets = inject[key];
|
|
34
|
+
if (snippets === undefined) {
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
if (!Array.isArray(snippets)) {
|
|
38
|
+
issues.push({
|
|
39
|
+
severity: "error",
|
|
40
|
+
code: "inject_invalid",
|
|
41
|
+
message: `inject.${key} must be an array of snippet strings.`,
|
|
42
|
+
path: `inject.${key}`,
|
|
43
|
+
});
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
if (snippets.length > SPACE_CONFIG_CAPS.injectSnippets) {
|
|
47
|
+
issues.push({
|
|
48
|
+
severity: "error",
|
|
49
|
+
code: "inject_invalid",
|
|
50
|
+
message: `inject.${key} supports up to ${SPACE_CONFIG_CAPS.injectSnippets} snippets.`,
|
|
51
|
+
path: `inject.${key}`,
|
|
52
|
+
details: { count: snippets.length, limit: SPACE_CONFIG_CAPS.injectSnippets },
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
snippets.forEach((snippet, index) => {
|
|
56
|
+
if (typeof snippet !== "string") {
|
|
57
|
+
issues.push({
|
|
58
|
+
severity: "error",
|
|
59
|
+
code: "inject_invalid",
|
|
60
|
+
message: `inject.${key}[${index}] must be a string.`,
|
|
61
|
+
path: `inject.${key}.${index}`,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
else if (byteLength(snippet) > SPACE_CONFIG_CAPS.injectSnippetBytes) {
|
|
65
|
+
issues.push({
|
|
66
|
+
severity: "error",
|
|
67
|
+
code: "inject_snippet_too_large",
|
|
68
|
+
message: `inject.${key}[${index}] supports up to ${SPACE_CONFIG_CAPS.injectSnippetBytes} bytes.`,
|
|
69
|
+
path: `inject.${key}.${index}`,
|
|
70
|
+
details: { size: byteLength(snippet), limit: SPACE_CONFIG_CAPS.injectSnippetBytes },
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
const meta = candidate.meta;
|
|
78
|
+
if (meta && typeof meta === "object") {
|
|
79
|
+
const caps = [
|
|
80
|
+
["title", SPACE_CONFIG_CAPS.metaTitleChars],
|
|
81
|
+
["description", SPACE_CONFIG_CAPS.metaDescriptionChars],
|
|
82
|
+
["image", SPACE_CONFIG_CAPS.metaImageChars],
|
|
83
|
+
];
|
|
84
|
+
for (const [key, limit] of caps) {
|
|
85
|
+
const value = meta[key];
|
|
86
|
+
if (typeof value === "string" && value.length > limit) {
|
|
87
|
+
issues.push({
|
|
88
|
+
severity: "error",
|
|
89
|
+
code: "config_meta_too_long",
|
|
90
|
+
message: `meta.${key} supports up to ${limit} characters.`,
|
|
91
|
+
path: `meta.${key}`,
|
|
92
|
+
details: { length: value.length, limit },
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return issues;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Validates a space `config` overlay at API intake (PATCH /spaces, publish
|
|
101
|
+
* `config`): the 16 KB compiled-overlay cap plus the shared shape caps.
|
|
102
|
+
* Returns the first blocking issue, or null when the overlay is acceptable.
|
|
103
|
+
*/
|
|
104
|
+
export function spaceConfigOverlayIssue(config) {
|
|
105
|
+
const encoded = JSON.stringify(config);
|
|
106
|
+
if (byteLength(encoded) > SPACE_CONFIG_CAPS.overlayBytes) {
|
|
107
|
+
return {
|
|
108
|
+
severity: "error",
|
|
109
|
+
code: "config_file_too_large",
|
|
110
|
+
message: `Space config overlays support up to ${SPACE_CONFIG_CAPS.overlayBytes} bytes.`,
|
|
111
|
+
path: "config",
|
|
112
|
+
details: { size: byteLength(encoded), limit: SPACE_CONFIG_CAPS.overlayBytes },
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
return capIssues(config)[0] ?? null;
|
|
116
|
+
}
|
|
117
|
+
// Routing concerns never live in sf.jsonc (spec: redirects, rewrites,
|
|
118
|
+
// proxies, and headers stay exclusively in `_redirects`/`_headers`).
|
|
119
|
+
const ROUTING_KEYS = new Set(["redirects", "rewrites", "proxies", "proxy", "headers", "routes"]);
|
|
120
|
+
const KNOWN_FILE_KEYS = new Set(Object.keys(spaceConfigFileSchema.shape));
|
|
121
|
+
// Strips JSONC comments and trailing commas (string-aware single pass).
|
|
122
|
+
export function stripJsonComments(raw) {
|
|
123
|
+
let output = "";
|
|
124
|
+
let inString = false;
|
|
125
|
+
let inLineComment = false;
|
|
126
|
+
let inBlockComment = false;
|
|
127
|
+
for (let i = 0; i < raw.length; i += 1) {
|
|
128
|
+
const char = raw[i];
|
|
129
|
+
const next = raw[i + 1];
|
|
130
|
+
if (inLineComment) {
|
|
131
|
+
if (char === "\n") {
|
|
132
|
+
inLineComment = false;
|
|
133
|
+
output += char;
|
|
134
|
+
}
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
if (inBlockComment) {
|
|
138
|
+
if (char === "*" && next === "/") {
|
|
139
|
+
inBlockComment = false;
|
|
140
|
+
i += 1;
|
|
141
|
+
}
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
if (inString) {
|
|
145
|
+
output += char;
|
|
146
|
+
if (char === "\\") {
|
|
147
|
+
output += next ?? "";
|
|
148
|
+
i += 1;
|
|
149
|
+
}
|
|
150
|
+
else if (char === '"') {
|
|
151
|
+
inString = false;
|
|
152
|
+
}
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
if (char === '"') {
|
|
156
|
+
inString = true;
|
|
157
|
+
output += char;
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
if (char === "/" && next === "/") {
|
|
161
|
+
inLineComment = true;
|
|
162
|
+
i += 1;
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
if (char === "/" && next === "*") {
|
|
166
|
+
inBlockComment = true;
|
|
167
|
+
i += 1;
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
if (char === ",") {
|
|
171
|
+
// Trailing comma: skip when the next non-whitespace/non-comment char closes.
|
|
172
|
+
let j = i + 1;
|
|
173
|
+
while (j < raw.length) {
|
|
174
|
+
const ahead = raw[j];
|
|
175
|
+
if (/\s/.test(ahead)) {
|
|
176
|
+
j += 1;
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
if (ahead === "/" && raw[j + 1] === "/") {
|
|
180
|
+
while (j < raw.length && raw[j] !== "\n")
|
|
181
|
+
j += 1;
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
if (ahead === "/" && raw[j + 1] === "*") {
|
|
185
|
+
j += 2;
|
|
186
|
+
while (j < raw.length && !(raw[j] === "*" && raw[j + 1] === "/"))
|
|
187
|
+
j += 1;
|
|
188
|
+
j += 2;
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
break;
|
|
192
|
+
}
|
|
193
|
+
if (raw[j] === "}" || raw[j] === "]") {
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
output += char;
|
|
198
|
+
}
|
|
199
|
+
return output;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Parses a raw `sf.jsonc` at finalize: 256 KB cap, JSONC tolerance,
|
|
203
|
+
* routing-key rejection, unknown-key diagnostics, shared shape caps. Errors
|
|
204
|
+
* fail the version; warnings ride version diagnostics.
|
|
205
|
+
*/
|
|
206
|
+
export function parseSpaceConfigFile(raw, options = {}) {
|
|
207
|
+
const configPath = options.path ?? SPACE_CONFIG_CANONICAL_FILE;
|
|
208
|
+
if (byteLength(raw) > SPACE_CONFIG_CAPS.fileBytes) {
|
|
209
|
+
return {
|
|
210
|
+
config: null,
|
|
211
|
+
issues: [
|
|
212
|
+
{
|
|
213
|
+
severity: "error",
|
|
214
|
+
code: "config_file_too_large",
|
|
215
|
+
message: `${configPath} supports up to ${SPACE_CONFIG_CAPS.fileBytes} bytes.`,
|
|
216
|
+
path: configPath,
|
|
217
|
+
details: { size: byteLength(raw), limit: SPACE_CONFIG_CAPS.fileBytes },
|
|
218
|
+
},
|
|
219
|
+
],
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
let candidate;
|
|
223
|
+
try {
|
|
224
|
+
candidate = JSON.parse(stripJsonComments(raw));
|
|
225
|
+
}
|
|
226
|
+
catch (error) {
|
|
227
|
+
return {
|
|
228
|
+
config: null,
|
|
229
|
+
issues: [
|
|
230
|
+
{
|
|
231
|
+
severity: "error",
|
|
232
|
+
code: "config_invalid",
|
|
233
|
+
message: `${configPath} is not valid JSONC: ${error instanceof Error ? error.message : "parse error"}.`,
|
|
234
|
+
path: configPath,
|
|
235
|
+
},
|
|
236
|
+
],
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
if (typeof candidate !== "object" || candidate === null || Array.isArray(candidate)) {
|
|
240
|
+
return {
|
|
241
|
+
config: null,
|
|
242
|
+
issues: [
|
|
243
|
+
{
|
|
244
|
+
severity: "error",
|
|
245
|
+
code: "config_invalid",
|
|
246
|
+
message: `${configPath} must contain a JSON object.`,
|
|
247
|
+
path: configPath,
|
|
248
|
+
},
|
|
249
|
+
],
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
const record = candidate;
|
|
253
|
+
const issues = [];
|
|
254
|
+
for (const key of Object.keys(record)) {
|
|
255
|
+
if (KNOWN_FILE_KEYS.has(key)) {
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
if (ROUTING_KEYS.has(key)) {
|
|
259
|
+
issues.push({
|
|
260
|
+
severity: "error",
|
|
261
|
+
code: "config_invalid",
|
|
262
|
+
message: `Routing never lives in ${configPath}; move "${key}" into _redirects/_headers.`,
|
|
263
|
+
path: key,
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
issues.push({
|
|
268
|
+
severity: "warning",
|
|
269
|
+
code: "config_invalid",
|
|
270
|
+
message: `Unknown ${configPath} key "${key}" was ignored.`,
|
|
271
|
+
path: key,
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
delete record[key];
|
|
275
|
+
}
|
|
276
|
+
const caps = capIssues(record);
|
|
277
|
+
issues.push(...caps);
|
|
278
|
+
if (issues.some((issue) => issue.severity === "error")) {
|
|
279
|
+
return { config: null, issues };
|
|
280
|
+
}
|
|
281
|
+
const parsed = spaceConfigFileSchema.safeParse(record);
|
|
282
|
+
if (!parsed.success) {
|
|
283
|
+
for (const issue of parsed.error.issues) {
|
|
284
|
+
issues.push({
|
|
285
|
+
severity: "error",
|
|
286
|
+
code: "config_invalid",
|
|
287
|
+
message: issue.message,
|
|
288
|
+
path: issue.path.map(String).join(".") || configPath,
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
return { config: null, issues };
|
|
292
|
+
}
|
|
293
|
+
return { config: parsed.data, issues };
|
|
294
|
+
}
|
|
295
|
+
// Mirrors the runtime's private-file rules (_stattic_runtime_file_private):
|
|
296
|
+
// convention/config files and dot-segment paths never serve, so they don't
|
|
297
|
+
// count toward root HTML inference below.
|
|
298
|
+
const PRIVATE_SERVING_FILES = new Set([
|
|
299
|
+
"_redirects",
|
|
300
|
+
"_headers",
|
|
301
|
+
"_config.json",
|
|
302
|
+
"_routes.json",
|
|
303
|
+
...SPACE_CONFIG_PRIVATE_FILES,
|
|
304
|
+
]);
|
|
305
|
+
function privateServingPath(path) {
|
|
306
|
+
if (PRIVATE_SERVING_FILES.has(path) || path === ".well-known/stattic-runtime") {
|
|
307
|
+
return true;
|
|
308
|
+
}
|
|
309
|
+
const segments = path.toLowerCase().split("/");
|
|
310
|
+
return segments.some((segment, index) => !(index === 0 && segment === ".well-known") && segment.startsWith("."));
|
|
311
|
+
}
|
|
312
|
+
function rootHtmlPaths(manifestPaths) {
|
|
313
|
+
const htmlPaths = [];
|
|
314
|
+
for (const path of manifestPaths) {
|
|
315
|
+
if (path === "" || privateServingPath(path)) {
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
if (!path.includes("/") && /\.html?$/i.test(path)) {
|
|
319
|
+
htmlPaths.push(path);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
return htmlPaths.toSorted((left, right) => left.localeCompare(right));
|
|
323
|
+
}
|
|
324
|
+
function normalizeFallback(fallback) {
|
|
325
|
+
if (fallback === undefined) {
|
|
326
|
+
return null;
|
|
327
|
+
}
|
|
328
|
+
if (typeof fallback === "string") {
|
|
329
|
+
return { path: fallback.replace(/^\/+/, ""), status: 200 };
|
|
330
|
+
}
|
|
331
|
+
const status = fallback.status === 404 ? 404 : 200;
|
|
332
|
+
return { path: fallback.path.replace(/^\/+/, ""), status };
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Resolves the effective SpaceConfig for a version at finalize (or
|
|
336
|
+
* config-only regeneration). `manifestPaths` is the canonical committed file
|
|
337
|
+
* set; `fileConfig` the parsed sf.jsonc (null when absent); `overlay`
|
|
338
|
+
* the stored space config overlay (null when nothing overridden).
|
|
339
|
+
*/
|
|
340
|
+
export function resolveEffectiveSpaceConfig(input) {
|
|
341
|
+
const rootHtml = rootHtmlPaths(input.manifestPaths);
|
|
342
|
+
const hasRootIndex = rootHtml.includes("index.html");
|
|
343
|
+
const inferredRootHtmlIndex = hasRootIndex ? "index.html" : (rootHtml[0] ?? null);
|
|
344
|
+
const explicitServingShape = input.fileConfig?.index !== undefined ||
|
|
345
|
+
input.fileConfig?.listing !== undefined ||
|
|
346
|
+
input.overlay?.index !== undefined ||
|
|
347
|
+
input.overlay?.listing !== undefined;
|
|
348
|
+
// Content-inferred defaults (spec: the old website/files modes are exactly
|
|
349
|
+
// these two default bundles; never API fields). A root index.html always
|
|
350
|
+
// means website mode. Without it, any root-level HTML file is treated as the
|
|
351
|
+
// homepage rewrite. No root HTML means files mode.
|
|
352
|
+
const defaults = inferredRootHtmlIndex
|
|
353
|
+
? { index: inferredRootHtmlIndex, listing: false }
|
|
354
|
+
: { index: false, listing: true };
|
|
355
|
+
const merged = { ...defaults };
|
|
356
|
+
for (const layer of [input.fileConfig, input.overlay]) {
|
|
357
|
+
if (!layer) {
|
|
358
|
+
continue;
|
|
359
|
+
}
|
|
360
|
+
for (const key of Object.keys(spaceConfigSchema.shape)) {
|
|
361
|
+
const value = layer[key];
|
|
362
|
+
if (value !== undefined) {
|
|
363
|
+
merged[key] = value;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
const issues = [];
|
|
368
|
+
const fallback = normalizeFallback(merged.fallback);
|
|
369
|
+
// Invariant (spec "Serving resolution"): a fallback must point at a
|
|
370
|
+
// committed file — no implicit extension guessing, fail at finalize.
|
|
371
|
+
if (fallback && !input.manifestPaths.has(fallback.path)) {
|
|
372
|
+
issues.push({
|
|
373
|
+
severity: "error",
|
|
374
|
+
code: "config_invalid",
|
|
375
|
+
message: `fallback path "/${fallback.path}" is not a committed file in this version.`,
|
|
376
|
+
path: "fallback.path",
|
|
377
|
+
details: { path: fallback.path },
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
const listing = merged.listing ?? !inferredRootHtmlIndex;
|
|
381
|
+
const serving = {
|
|
382
|
+
index: merged.index ?? inferredRootHtmlIndex ?? false,
|
|
383
|
+
fallback,
|
|
384
|
+
listing,
|
|
385
|
+
// The single-file viewer rides the listing bundle (artifact-style spaces).
|
|
386
|
+
viewer: listing,
|
|
387
|
+
meta: merged.meta ?? null,
|
|
388
|
+
experimentalGutenberg: merged.experimental_gutenberg === true,
|
|
389
|
+
inject: merged.inject ?? null,
|
|
390
|
+
};
|
|
391
|
+
if (inferredRootHtmlIndex !== null && !hasRootIndex && serving.index === inferredRootHtmlIndex) {
|
|
392
|
+
// Surfaced on the publish receipt and version diagnostics so the CLI and
|
|
393
|
+
// agents learn the rewrite without probing the live URL.
|
|
394
|
+
issues.push({
|
|
395
|
+
severity: "info",
|
|
396
|
+
code: "single_html_index",
|
|
397
|
+
message: `${inferredRootHtmlIndex} is the root HTML file selected for this version, so it serves as the homepage: / rewrites to /${inferredRootHtmlIndex}.`,
|
|
398
|
+
path: "index",
|
|
399
|
+
details: { path: inferredRootHtmlIndex, rootHtmlCount: rootHtml.length },
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
if (inferredRootHtmlIndex === null &&
|
|
403
|
+
serving.index === false &&
|
|
404
|
+
serving.listing &&
|
|
405
|
+
!explicitServingShape) {
|
|
406
|
+
issues.push({
|
|
407
|
+
severity: "info",
|
|
408
|
+
code: "files_mode_inferred",
|
|
409
|
+
message: "No HTML files were found in the root of this version, so it serves in files mode with directory listing enabled.",
|
|
410
|
+
path: "listing",
|
|
411
|
+
details: { rootHtmlCount: 0 },
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
return { config: merged, serving, issues };
|
|
415
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { SpaceTheme } from "../contracts/space-theme.js";
|
|
2
|
+
export type SpaceThemeWarning = {
|
|
3
|
+
severity: "warning";
|
|
4
|
+
code: "theme_value_invalid";
|
|
5
|
+
message: string;
|
|
6
|
+
/** JSON-path-lite pointer into the theme shape. */
|
|
7
|
+
path: string;
|
|
8
|
+
};
|
|
9
|
+
export type CompiledSpaceTheme = {
|
|
10
|
+
/** CSS custom-property declarations (no selector), or null when empty. */
|
|
11
|
+
css: string | null;
|
|
12
|
+
warnings: SpaceThemeWarning[];
|
|
13
|
+
};
|
|
14
|
+
/** Engine-enforced ceiling for the compiled declaration block. */
|
|
15
|
+
export declare const SPACE_THEME_CSS_MAX_BYTES: number;
|
|
16
|
+
export declare function compileSpaceTheme(theme: SpaceTheme | null | undefined): CompiledSpaceTheme;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/** Engine-enforced ceiling for the compiled declaration block. */
|
|
2
|
+
export const SPACE_THEME_CSS_MAX_BYTES = 16 * 1024;
|
|
3
|
+
// Categorically forbidden anywhere in a value: network reach, style escape,
|
|
4
|
+
// markup injection. Mirrors the runtime's defense-in-depth re-check.
|
|
5
|
+
const FORBIDDEN_VALUE_PATTERN = /url\s*\(|@import|expression\s*\(|data:|[<>{};\\]|\/\*|\*\//i;
|
|
6
|
+
const COLOR_PATTERN = new RegExp([
|
|
7
|
+
"^(?:",
|
|
8
|
+
"#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})",
|
|
9
|
+
"|rgba?\\([0-9.,\\s%/]{1,64}\\)",
|
|
10
|
+
"|hsla?\\([0-9.,\\s%deg/]{1,64}\\)",
|
|
11
|
+
"|[a-zA-Z]{3,30}", // CSS named colors (transparent, currentColor, rebeccapurple, ...)
|
|
12
|
+
"|var\\(--wp--preset--color--[a-z0-9-]{1,64}\\)",
|
|
13
|
+
")$",
|
|
14
|
+
].join(""));
|
|
15
|
+
const SIZE_PATTERN = new RegExp([
|
|
16
|
+
"^(?:",
|
|
17
|
+
"-?\\d{1,5}(?:\\.\\d{1,4})?(?:px|rem|em|%|vw|vh|vmin|vmax|pt|ch|ex)?",
|
|
18
|
+
"|var\\(--wp--preset--(?:font-size|spacing)--[a-z0-9-]{1,64}\\)",
|
|
19
|
+
")$",
|
|
20
|
+
].join(""));
|
|
21
|
+
// Font family stacks: platform-provided or validated-generic family names
|
|
22
|
+
// only — letters, digits, spaces, commas, hyphens, straight quotes. No src.
|
|
23
|
+
const FONT_FAMILY_PATTERN = /^[A-Za-z0-9 ,'"-]{1,256}$/;
|
|
24
|
+
function safeValue(value, pattern) {
|
|
25
|
+
const trimmed = value.trim();
|
|
26
|
+
return trimmed.length > 0 && !FORBIDDEN_VALUE_PATTERN.test(trimmed) && pattern.test(trimmed);
|
|
27
|
+
}
|
|
28
|
+
export function compileSpaceTheme(theme) {
|
|
29
|
+
if (!theme) {
|
|
30
|
+
return { css: null, warnings: [] };
|
|
31
|
+
}
|
|
32
|
+
const declarations = [];
|
|
33
|
+
const warnings = [];
|
|
34
|
+
const drop = (path) => {
|
|
35
|
+
warnings.push({
|
|
36
|
+
severity: "warning",
|
|
37
|
+
code: "theme_value_invalid",
|
|
38
|
+
message: `Theme value at ${path} is not a safe preset token and was dropped.`,
|
|
39
|
+
path,
|
|
40
|
+
});
|
|
41
|
+
};
|
|
42
|
+
theme.color?.palette?.forEach((entry, index) => {
|
|
43
|
+
if (safeValue(entry.color, COLOR_PATTERN)) {
|
|
44
|
+
declarations.push(`--wp--preset--color--${entry.slug}:${entry.color.trim()}`);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
drop(`theme.color.palette[${index}].color`);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
theme.typography?.fontSizes?.forEach((entry, index) => {
|
|
51
|
+
if (safeValue(entry.size, SIZE_PATTERN)) {
|
|
52
|
+
declarations.push(`--wp--preset--font-size--${entry.slug}:${entry.size.trim()}`);
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
drop(`theme.typography.fontSizes[${index}].size`);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
const fontFamily = theme.typography?.fontFamily;
|
|
59
|
+
if (fontFamily !== undefined) {
|
|
60
|
+
if (safeValue(fontFamily, FONT_FAMILY_PATTERN)) {
|
|
61
|
+
declarations.push(`--sf--font-family:${fontFamily.trim()}`);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
drop("theme.typography.fontFamily");
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
theme.spacing?.spacingScale?.forEach((entry, index) => {
|
|
68
|
+
if (safeValue(entry.size, SIZE_PATTERN)) {
|
|
69
|
+
declarations.push(`--wp--preset--spacing--${entry.slug}:${entry.size.trim()}`);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
drop(`theme.spacing.spacingScale[${index}].size`);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
// Bounded output: drop trailing declarations past the engine ceiling with a
|
|
76
|
+
// warning instead of failing the version.
|
|
77
|
+
let css = declarations.join(";");
|
|
78
|
+
while (css.length > SPACE_THEME_CSS_MAX_BYTES && declarations.length > 0) {
|
|
79
|
+
declarations.pop();
|
|
80
|
+
warnings.push({
|
|
81
|
+
severity: "warning",
|
|
82
|
+
code: "theme_value_invalid",
|
|
83
|
+
message: `Compiled theme exceeds ${SPACE_THEME_CSS_MAX_BYTES} bytes; trailing tokens were dropped.`,
|
|
84
|
+
path: "theme",
|
|
85
|
+
});
|
|
86
|
+
css = declarations.join(";");
|
|
87
|
+
}
|
|
88
|
+
return { css: css.length > 0 ? css : null, warnings };
|
|
89
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export declare const PHP_LIKE_EXTENSIONS: string[];
|
|
2
|
+
export declare const PLATFORM_MANAGED_RESPONSE_HEADERS: string[];
|
|
3
|
+
export declare const NON_IMMUTABLE_EXTENSIONS: string[];
|
|
4
|
+
/** Lowercased extension without the leading dot, posix semantics. Dotfiles have no extension. */
|
|
5
|
+
export declare function pathExtension(filePath: string): string;
|
|
6
|
+
/** Basename without its extension (the "stem"), posix semantics. */
|
|
7
|
+
export declare function pathStem(filePath: string): string;
|
|
8
|
+
export declare function isPhpLikeStaticPath(filePath: string): boolean;
|
|
9
|
+
export declare function looksLikeBundlerFingerprint(segment: string): boolean;
|
|
10
|
+
export declare function isImmutableAssetPath(filePath: string): boolean;
|
|
11
|
+
/** Forced inert serving policy for a committed static file. PHP-like files are never executable. */
|
|
12
|
+
export declare function runtimeFileSafety(filePath: string): {
|
|
13
|
+
executable: false;
|
|
14
|
+
forced_download_or_text: boolean;
|
|
15
|
+
};
|
|
16
|
+
/** Forces a safe content type for PHP-like files; passes other MIME types through. */
|
|
17
|
+
export declare function safeRuntimeContentType(filePath: string, mime: string): string;
|