@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,103 @@
|
|
|
1
|
+
// Single source of truth for static-runtime content policy: PHP-like inert
|
|
2
|
+
// classification, immutable-asset detection, and safe content-type forcing.
|
|
3
|
+
//
|
|
4
|
+
// This module is intentionally pure (no node:path, no I/O) so the exact same
|
|
5
|
+
// rules can be mechanically code-generated into the PHP runtime (runtime
|
|
6
|
+
// policies) and exercised by both TypeScript and PHP against one shared fixture
|
|
7
|
+
// (static-runtime-policy.fixtures.json). Do not add platform/filesystem
|
|
8
|
+
// behavior here.
|
|
9
|
+
export const PHP_LIKE_EXTENSIONS = ["php", "php3", "php4", "php5", "php7", "php8", "phtml", "phar"];
|
|
10
|
+
// Rejected/platform-managed response headers (internal-docs/platform.md "Routing And
|
|
11
|
+
// Headers"): user `_headers` rules can never set or remove these. The list includes the
|
|
12
|
+
// internal-redirect/sendfile family (X-Accel-Redirect, X-Sendfile, X-LIGHTTPD-send-file,
|
|
13
|
+
// X-Accel-Buffering) because under nginx+php-fpm those headers turn a response into a
|
|
14
|
+
// server-side file read — a private-path disclosure primitive. The routing compiler
|
|
15
|
+
// rejects them at publish; the PHP runtime refuses them again at apply time so a compiler
|
|
16
|
+
// gap can never emit one.
|
|
17
|
+
export const PLATFORM_MANAGED_RESPONSE_HEADERS = [
|
|
18
|
+
"accept-ranges",
|
|
19
|
+
"age",
|
|
20
|
+
"allow",
|
|
21
|
+
"alt-svc",
|
|
22
|
+
"cdn-cache-control",
|
|
23
|
+
"cloudflare-cdn-cache-control",
|
|
24
|
+
"connection",
|
|
25
|
+
"content-encoding",
|
|
26
|
+
"content-length",
|
|
27
|
+
"content-range",
|
|
28
|
+
"cookie",
|
|
29
|
+
"date",
|
|
30
|
+
"host",
|
|
31
|
+
"location",
|
|
32
|
+
"netlify-cdn-cache-control",
|
|
33
|
+
"server",
|
|
34
|
+
"set-cookie",
|
|
35
|
+
"strict-transport-security",
|
|
36
|
+
"surrogate-control",
|
|
37
|
+
"trailer",
|
|
38
|
+
"transfer-encoding",
|
|
39
|
+
"upgrade",
|
|
40
|
+
"vary",
|
|
41
|
+
"x-accel-buffering",
|
|
42
|
+
"x-accel-redirect",
|
|
43
|
+
"x-lighttpd-send-file",
|
|
44
|
+
"x-sendfile",
|
|
45
|
+
];
|
|
46
|
+
export const NON_IMMUTABLE_EXTENSIONS = ["html", "htm", "php", "txt", "xml"];
|
|
47
|
+
const PHP_LIKE_EXTENSION_SET = new Set(PHP_LIKE_EXTENSIONS);
|
|
48
|
+
const NON_IMMUTABLE_EXTENSION_SET = new Set(NON_IMMUTABLE_EXTENSIONS);
|
|
49
|
+
const FINGERPRINTED_ASSET_SEGMENT = /^[A-Za-z0-9_-]{8,}$/;
|
|
50
|
+
function lastSegment(filePath) {
|
|
51
|
+
const slash = filePath.lastIndexOf("/");
|
|
52
|
+
return slash >= 0 ? filePath.slice(slash + 1) : filePath;
|
|
53
|
+
}
|
|
54
|
+
/** Lowercased extension without the leading dot, posix semantics. Dotfiles have no extension. */
|
|
55
|
+
export function pathExtension(filePath) {
|
|
56
|
+
const base = lastSegment(filePath);
|
|
57
|
+
const dot = base.lastIndexOf(".");
|
|
58
|
+
return dot > 0 ? base.slice(dot + 1).toLowerCase() : "";
|
|
59
|
+
}
|
|
60
|
+
/** Basename without its extension (the "stem"), posix semantics. */
|
|
61
|
+
export function pathStem(filePath) {
|
|
62
|
+
const base = lastSegment(filePath);
|
|
63
|
+
const dot = base.lastIndexOf(".");
|
|
64
|
+
return dot > 0 ? base.slice(0, dot) : base;
|
|
65
|
+
}
|
|
66
|
+
export function isPhpLikeStaticPath(filePath) {
|
|
67
|
+
return PHP_LIKE_EXTENSION_SET.has(pathExtension(filePath));
|
|
68
|
+
}
|
|
69
|
+
export function looksLikeBundlerFingerprint(segment) {
|
|
70
|
+
return (FINGERPRINTED_ASSET_SEGMENT.test(segment) &&
|
|
71
|
+
(/\d/.test(segment) || /_/.test(segment) || (/[a-z]/.test(segment) && /[A-Z]/.test(segment))));
|
|
72
|
+
}
|
|
73
|
+
export function isImmutableAssetPath(filePath) {
|
|
74
|
+
if (NON_IMMUTABLE_EXTENSION_SET.has(pathExtension(filePath))) {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
const stem = pathStem(filePath);
|
|
78
|
+
for (let index = 0; index < stem.length; index += 1) {
|
|
79
|
+
if (index > 0 && stem[index - 1] !== "." && stem[index - 1] !== "-") {
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
if (looksLikeBundlerFingerprint(stem.slice(index))) {
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
/** Forced inert serving policy for a committed static file. PHP-like files are never executable. */
|
|
89
|
+
export function runtimeFileSafety(filePath) {
|
|
90
|
+
return {
|
|
91
|
+
executable: false,
|
|
92
|
+
forced_download_or_text: isPhpLikeStaticPath(filePath),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
/** Forces a safe content type for PHP-like files; passes other MIME types through. */
|
|
96
|
+
export function safeRuntimeContentType(filePath, mime) {
|
|
97
|
+
if (!isPhpLikeStaticPath(filePath)) {
|
|
98
|
+
return mime;
|
|
99
|
+
}
|
|
100
|
+
return pathExtension(filePath) === "phar"
|
|
101
|
+
? "application/octet-stream"
|
|
102
|
+
: "text/plain; charset=utf-8";
|
|
103
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { z } from "zod";
|
|
2
|
+
import type { uploadInstructionsSchema, uploadTargetSchema } from "../contracts/internal.js";
|
|
3
|
+
export type UploadSession = z.infer<typeof uploadInstructionsSchema>;
|
|
4
|
+
export type UploadTarget = z.infer<typeof uploadTargetSchema>;
|
|
5
|
+
export type UploadTargetBody = {
|
|
6
|
+
body: NonNullable<RequestInit["body"]>;
|
|
7
|
+
headers?: Record<string, string>;
|
|
8
|
+
contentLength?: number | undefined;
|
|
9
|
+
duplex?: "half";
|
|
10
|
+
};
|
|
11
|
+
export type UploadSessionEvent = {
|
|
12
|
+
type: "target-start";
|
|
13
|
+
target: UploadTarget;
|
|
14
|
+
} | {
|
|
15
|
+
type: "target-done";
|
|
16
|
+
target: UploadTarget;
|
|
17
|
+
} | {
|
|
18
|
+
type: "target-error";
|
|
19
|
+
target: UploadTarget;
|
|
20
|
+
error: unknown;
|
|
21
|
+
};
|
|
22
|
+
export declare class UploadTargetError extends Error {
|
|
23
|
+
readonly target: UploadTarget;
|
|
24
|
+
readonly status: number;
|
|
25
|
+
constructor(target: UploadTarget, status: number, message: string);
|
|
26
|
+
}
|
|
27
|
+
export declare function executeUploadSession(input: {
|
|
28
|
+
session: UploadSession;
|
|
29
|
+
targets?: readonly UploadTarget[] | undefined;
|
|
30
|
+
concurrency?: number | undefined;
|
|
31
|
+
fetch?: typeof fetch | undefined;
|
|
32
|
+
prepareTarget?: ((target: UploadTarget) => Promise<UploadTargetBody> | UploadTargetBody) | undefined;
|
|
33
|
+
onEvent?: ((event: UploadSessionEvent) => void) | undefined;
|
|
34
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
export class UploadTargetError extends Error {
|
|
2
|
+
target;
|
|
3
|
+
status;
|
|
4
|
+
constructor(target, status, message) {
|
|
5
|
+
super(message);
|
|
6
|
+
this.target = target;
|
|
7
|
+
this.status = status;
|
|
8
|
+
this.name = "UploadTargetError";
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
async function runConcurrent(items, concurrency, fn) {
|
|
12
|
+
let nextIndex = 0;
|
|
13
|
+
const runWorker = async () => {
|
|
14
|
+
const item = items[nextIndex++];
|
|
15
|
+
if (item === undefined) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
await fn(item);
|
|
19
|
+
return runWorker();
|
|
20
|
+
};
|
|
21
|
+
await Promise.all(Array.from({ length: Math.min(Math.max(concurrency, 1), items.length || 1) }).map(() => runWorker()));
|
|
22
|
+
}
|
|
23
|
+
export async function executeUploadSession(input) {
|
|
24
|
+
const targets = input.targets ?? input.session.targets;
|
|
25
|
+
if (targets.length === 0) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const fetchImpl = input.fetch ?? fetch;
|
|
29
|
+
await runConcurrent(targets, input.concurrency ?? 4, async (target) => {
|
|
30
|
+
input.onEvent?.({ type: "target-start", target });
|
|
31
|
+
try {
|
|
32
|
+
const prepared = input.prepareTarget
|
|
33
|
+
? await input.prepareTarget(target)
|
|
34
|
+
: defaultUploadTargetBody(target);
|
|
35
|
+
const headers = {
|
|
36
|
+
...target.headers,
|
|
37
|
+
...prepared.headers,
|
|
38
|
+
...(prepared.contentLength === undefined
|
|
39
|
+
? {}
|
|
40
|
+
: { "content-length": String(prepared.contentLength) }),
|
|
41
|
+
};
|
|
42
|
+
const response = await fetchImpl(target.url, {
|
|
43
|
+
method: target.method,
|
|
44
|
+
headers,
|
|
45
|
+
body: prepared.body,
|
|
46
|
+
...(prepared.duplex ? { duplex: prepared.duplex } : {}),
|
|
47
|
+
});
|
|
48
|
+
if (!response.ok) {
|
|
49
|
+
const details = await response.text().catch(() => "");
|
|
50
|
+
const suffix = details.trim() ? `: ${details.trim().slice(0, 1_000)}` : "";
|
|
51
|
+
throw new UploadTargetError(target, response.status, `Upload failed for ${target.path} with ${response.status} ${response.statusText}${suffix}.`);
|
|
52
|
+
}
|
|
53
|
+
input.onEvent?.({ type: "target-done", target });
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
input.onEvent?.({ type: "target-error", target, error });
|
|
57
|
+
throw error;
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
function defaultUploadTargetBody(target) {
|
|
62
|
+
if (target.body?.kind === "url") {
|
|
63
|
+
return {
|
|
64
|
+
body: JSON.stringify({ url: target.body.url }),
|
|
65
|
+
headers: { "content-type": "application/json" },
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
throw new Error(`Upload target ${target.path} requires caller-provided bytes.`);
|
|
69
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export function cleanBaseUrl(value, fallback) {
|
|
2
|
+
return (value?.trim() || fallback).replace(/\/$/, "");
|
|
3
|
+
}
|
|
4
|
+
export function joinBaseUrl(baseUrl, path = "/") {
|
|
5
|
+
const normalizedPath = path.startsWith("/") ? path : `/${path}`;
|
|
6
|
+
return `${baseUrl}${normalizedPath}`;
|
|
7
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// Canonical object-path form (internal-docs/platform.md "Upload Contract"):
|
|
2
|
+
// UTF-8, Unicode NFC, percent-decoded exactly once. This canonical form is
|
|
3
|
+
// what manifest paths, finalize's normalized paths, route-map keys, and dedup
|
|
4
|
+
// keys all mean. The PHP runtime applies the same transform (rawurldecode once
|
|
5
|
+
// + Normalizer::normalize) at upload and serve time.
|
|
6
|
+
const CONTROL_CHARS_PATTERN = /[\u0000-\u001f\u007f]/;
|
|
7
|
+
// Structural normalization shared by lookups and intake: forward slashes,
|
|
8
|
+
// no leading slashes, no empty/"."/".." segments. Idempotent; safe to apply
|
|
9
|
+
// to already-canonical stored paths (NFC is idempotent, no percent-decoding).
|
|
10
|
+
export function normalizeVersionPath(input) {
|
|
11
|
+
const normalized = input.replace(/\\/g, "/").replace(/^\/+/, "").normalize("NFC");
|
|
12
|
+
const segments = [];
|
|
13
|
+
for (const segment of normalized.split("/")) {
|
|
14
|
+
if (!segment || segment === ".") {
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
if (segment === "..") {
|
|
18
|
+
throw new Error(`invalid_version_path:${input}`);
|
|
19
|
+
}
|
|
20
|
+
segments.push(segment);
|
|
21
|
+
}
|
|
22
|
+
const safePath = segments.join("/");
|
|
23
|
+
if (!safePath) {
|
|
24
|
+
throw new Error(`invalid_version_path:${input}`);
|
|
25
|
+
}
|
|
26
|
+
return safePath;
|
|
27
|
+
}
|
|
28
|
+
// Intake canonicalization: percent-decode EXACTLY once, then NFC, then the
|
|
29
|
+
// structural rules. Applied wherever paths enter the system (manifest intake,
|
|
30
|
+
// archive extraction, runtime upload, serve-time lookup) and never to paths
|
|
31
|
+
// that are already canonical — decode-once is not idempotent. Throws on
|
|
32
|
+
// malformed percent sequences, control characters, traversal, and empty paths.
|
|
33
|
+
export function canonicalizeVersionPath(input) {
|
|
34
|
+
let decoded;
|
|
35
|
+
try {
|
|
36
|
+
decoded = decodeURIComponent(input);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
throw new Error(`invalid_version_path:${input}`);
|
|
40
|
+
}
|
|
41
|
+
const canonical = decoded.normalize("NFC");
|
|
42
|
+
// Backslashes as separators are denied at intake (spec "Static path
|
|
43
|
+
// policy"); only client-side helpers translate Windows separators.
|
|
44
|
+
if (CONTROL_CHARS_PATTERN.test(canonical) || canonical.includes("\\")) {
|
|
45
|
+
throw new Error(`invalid_version_path:${input}`);
|
|
46
|
+
}
|
|
47
|
+
return normalizeVersionPath(canonical);
|
|
48
|
+
}
|
|
49
|
+
export function versionPathForFile(input) {
|
|
50
|
+
return normalizeVersionPath(input.webkitRelativePath || input.name);
|
|
51
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type LoginIntentSearch = {
|
|
2
|
+
direct?: boolean | undefined;
|
|
3
|
+
returnTo?: string | undefined;
|
|
4
|
+
};
|
|
5
|
+
export declare function buildDashboardLoginPath(input: LoginIntentSearch): string;
|
|
6
|
+
export declare function buildWpcomAuthStartPath(input: LoginIntentSearch): string;
|
|
7
|
+
export declare function buildPostAuthPath(input: LoginIntentSearch): string;
|
|
8
|
+
export declare function buildWpcomLoginUrl(authorizeUrl: string): string;
|
|
9
|
+
export declare function buildWpcomSignInOptions(input: LoginIntentSearch, dashboardOrigin: string): {
|
|
10
|
+
readonly provider: "wpcom";
|
|
11
|
+
readonly callbackURL: string;
|
|
12
|
+
readonly errorCallbackURL: string;
|
|
13
|
+
readonly disableRedirect: true;
|
|
14
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { sanitizeDashboardReturnTo } from "@spacefast/common/utils/auth-redirect";
|
|
2
|
+
export function buildDashboardLoginPath(input) {
|
|
3
|
+
const url = new URL("/sign-in", "http://dashboard.local");
|
|
4
|
+
if (input.direct) {
|
|
5
|
+
url.searchParams.set("direct", "1");
|
|
6
|
+
}
|
|
7
|
+
const returnTo = sanitizeDashboardReturnTo(input.returnTo);
|
|
8
|
+
if (returnTo) {
|
|
9
|
+
url.searchParams.set("returnTo", returnTo);
|
|
10
|
+
}
|
|
11
|
+
return `${url.pathname}${url.search}`;
|
|
12
|
+
}
|
|
13
|
+
export function buildWpcomAuthStartPath(input) {
|
|
14
|
+
const url = new URL("/v1/auth/wpcom/start", "http://api.local");
|
|
15
|
+
const returnTo = sanitizeDashboardReturnTo(input.returnTo);
|
|
16
|
+
if (returnTo) {
|
|
17
|
+
url.searchParams.set("returnTo", returnTo);
|
|
18
|
+
}
|
|
19
|
+
return `${url.pathname}${url.search}`;
|
|
20
|
+
}
|
|
21
|
+
export function buildPostAuthPath(input) {
|
|
22
|
+
const url = new URL("/sign-in", "http://dashboard.local");
|
|
23
|
+
url.searchParams.set("auth_callback", "1");
|
|
24
|
+
const returnTo = sanitizeDashboardReturnTo(input.returnTo);
|
|
25
|
+
if (returnTo) {
|
|
26
|
+
url.searchParams.set("returnTo", returnTo);
|
|
27
|
+
}
|
|
28
|
+
return `${url.pathname}${url.search}`;
|
|
29
|
+
}
|
|
30
|
+
export function buildWpcomLoginUrl(authorizeUrl) {
|
|
31
|
+
const authUrl = new URL(authorizeUrl);
|
|
32
|
+
const loginUrl = new URL("https://wordpress.com/log-in");
|
|
33
|
+
const clientId = authUrl.searchParams.get("client_id");
|
|
34
|
+
if (clientId) {
|
|
35
|
+
loginUrl.searchParams.set("client_id", clientId);
|
|
36
|
+
}
|
|
37
|
+
loginUrl.searchParams.set("redirect_to", authUrl.toString());
|
|
38
|
+
return loginUrl.toString();
|
|
39
|
+
}
|
|
40
|
+
export function buildWpcomSignInOptions(input, dashboardOrigin) {
|
|
41
|
+
const errorCallbackUrl = new URL(buildDashboardLoginPath(input), dashboardOrigin);
|
|
42
|
+
errorCallbackUrl.searchParams.set("claim_error", "login");
|
|
43
|
+
return {
|
|
44
|
+
provider: "wpcom",
|
|
45
|
+
callbackURL: new URL(buildPostAuthPath(input), dashboardOrigin).toString(),
|
|
46
|
+
errorCallbackURL: errorCallbackUrl.toString(),
|
|
47
|
+
disableRedirect: true,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type ZeroRealtimeEvent } from "../contracts/zero.js";
|
|
2
|
+
export declare const ZERO_REALTIME_EVENT_SIGNATURE_HEADER = "x-spacefast-zero-event-signature";
|
|
3
|
+
export declare function canonicalZeroRealtimeEventPayload(event: ZeroRealtimeEvent): string;
|
|
4
|
+
export declare function signZeroRealtimeEvent(input: {
|
|
5
|
+
event: ZeroRealtimeEvent;
|
|
6
|
+
secret: string | null | undefined;
|
|
7
|
+
}): string | null;
|
|
8
|
+
export declare function verifyZeroRealtimeEventSignature(input: {
|
|
9
|
+
event: ZeroRealtimeEvent;
|
|
10
|
+
secret: string | null | undefined;
|
|
11
|
+
signature: string | null | undefined;
|
|
12
|
+
}): boolean;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { createHmac, timingSafeEqual } from "node:crypto";
|
|
2
|
+
import { zeroRealtimeEventSchema } from "../contracts/zero.js";
|
|
3
|
+
const ZERO_REALTIME_EVENT_SIGNATURE_PREFIX = "sha256=";
|
|
4
|
+
export const ZERO_REALTIME_EVENT_SIGNATURE_HEADER = "x-spacefast-zero-event-signature";
|
|
5
|
+
function canonicalize(value) {
|
|
6
|
+
if (value === null ||
|
|
7
|
+
typeof value === "boolean" ||
|
|
8
|
+
typeof value === "number" ||
|
|
9
|
+
typeof value === "string") {
|
|
10
|
+
return value;
|
|
11
|
+
}
|
|
12
|
+
if (Array.isArray(value)) {
|
|
13
|
+
return value.map((item) => canonicalize(item));
|
|
14
|
+
}
|
|
15
|
+
if (value && typeof value === "object") {
|
|
16
|
+
return Object.fromEntries(Object.entries(value)
|
|
17
|
+
.filter(([, item]) => item !== undefined)
|
|
18
|
+
.toSorted(([left], [right]) => left.localeCompare(right))
|
|
19
|
+
.map(([key, item]) => [key, canonicalize(item)]));
|
|
20
|
+
}
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
export function canonicalZeroRealtimeEventPayload(event) {
|
|
24
|
+
return JSON.stringify(canonicalize(zeroRealtimeEventSchema.parse(event)));
|
|
25
|
+
}
|
|
26
|
+
export function signZeroRealtimeEvent(input) {
|
|
27
|
+
const secret = input.secret?.trim();
|
|
28
|
+
if (!secret) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
return `${ZERO_REALTIME_EVENT_SIGNATURE_PREFIX}${createHmac("sha256", secret)
|
|
32
|
+
.update(canonicalZeroRealtimeEventPayload(input.event))
|
|
33
|
+
.digest("hex")}`;
|
|
34
|
+
}
|
|
35
|
+
export function verifyZeroRealtimeEventSignature(input) {
|
|
36
|
+
const expected = signZeroRealtimeEvent({ event: input.event, secret: input.secret });
|
|
37
|
+
const actual = input.signature?.trim();
|
|
38
|
+
if (!expected || !actual) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
const expectedBytes = Buffer.from(expected);
|
|
42
|
+
const actualBytes = Buffer.from(actual);
|
|
43
|
+
return (expectedBytes.byteLength === actualBytes.byteLength &&
|
|
44
|
+
timingSafeEqual(expectedBytes, actualBytes));
|
|
45
|
+
}
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Contexts in which an otherwise-discouraged external term is still allowed.
|
|
3
|
+
* A drift-guard uses these to tell a real leak from a sanctioned use.
|
|
4
|
+
*/
|
|
5
|
+
export type TermContext =
|
|
6
|
+
/** The public platform/tenant API surface (contracts, OpenAPI, SDK). */
|
|
7
|
+
"platform-api"
|
|
8
|
+
/** The GitHub integration vocabulary (push deploys, deployment status). */
|
|
9
|
+
| "github-integration"
|
|
10
|
+
/** A fixed marketing phrase, spelled out verbatim in `allowedPhrases`. */
|
|
11
|
+
| "fixed-phrase";
|
|
12
|
+
/** A single canonical product concept and the words allowed to express it. */
|
|
13
|
+
export interface Concept {
|
|
14
|
+
/** Stable machine id for cross-referencing (never user-visible). */
|
|
15
|
+
readonly id: string;
|
|
16
|
+
/** The one noun to use on user surfaces (already cased for prose). */
|
|
17
|
+
readonly canonicalNoun: string;
|
|
18
|
+
/** The verb form, when the concept is also an action (e.g. publish). */
|
|
19
|
+
readonly verb?: string;
|
|
20
|
+
/** Equivalent words that are fine to use interchangeably on any surface. */
|
|
21
|
+
readonly aliases: readonly string[];
|
|
22
|
+
/**
|
|
23
|
+
* External-facing synonyms that are NOT allowed on general user surfaces but
|
|
24
|
+
* are sanctioned in specific contexts. Each appears in EXTERNAL_TERM_ALLOWLIST
|
|
25
|
+
* with its allowed contexts.
|
|
26
|
+
*/
|
|
27
|
+
readonly externalTerms?: readonly string[];
|
|
28
|
+
/**
|
|
29
|
+
* Retired terms that must never appear on any surface. A drift-guard should
|
|
30
|
+
* flag these unconditionally and suggest `canonicalNoun`/`verb`.
|
|
31
|
+
*/
|
|
32
|
+
readonly deprecated?: readonly string[];
|
|
33
|
+
/** One-line rationale, for humans reading the registry. */
|
|
34
|
+
readonly description: string;
|
|
35
|
+
}
|
|
36
|
+
export declare const CONCEPTS: {
|
|
37
|
+
readonly space: {
|
|
38
|
+
readonly id: "space";
|
|
39
|
+
readonly canonicalNoun: "space";
|
|
40
|
+
readonly aliases: readonly [];
|
|
41
|
+
readonly externalTerms: readonly ["site"];
|
|
42
|
+
readonly deprecated: readonly ["project"];
|
|
43
|
+
readonly description: string;
|
|
44
|
+
};
|
|
45
|
+
readonly publish: {
|
|
46
|
+
readonly id: "publish";
|
|
47
|
+
readonly canonicalNoun: "publish";
|
|
48
|
+
readonly verb: "publish";
|
|
49
|
+
readonly aliases: readonly ["go live"];
|
|
50
|
+
readonly externalTerms: readonly ["deployment", "deploy"];
|
|
51
|
+
readonly deprecated: readonly ["dep_", "dep_build_", "dep_version_"];
|
|
52
|
+
readonly description: string;
|
|
53
|
+
};
|
|
54
|
+
readonly version: {
|
|
55
|
+
readonly id: "version";
|
|
56
|
+
readonly canonicalNoun: "version";
|
|
57
|
+
readonly verb: "publish";
|
|
58
|
+
readonly aliases: readonly [];
|
|
59
|
+
readonly deprecated: readonly ["deployment"];
|
|
60
|
+
readonly description: string;
|
|
61
|
+
};
|
|
62
|
+
readonly channel: {
|
|
63
|
+
readonly id: "channel";
|
|
64
|
+
readonly canonicalNoun: "channel";
|
|
65
|
+
readonly aliases: readonly [];
|
|
66
|
+
readonly description: "A named serving lane a version is published to (e.g. production, preview).";
|
|
67
|
+
};
|
|
68
|
+
readonly build: {
|
|
69
|
+
readonly id: "build";
|
|
70
|
+
readonly canonicalNoun: "build";
|
|
71
|
+
readonly verb: "build";
|
|
72
|
+
readonly aliases: readonly [];
|
|
73
|
+
readonly externalTerms: readonly ["E2B"];
|
|
74
|
+
readonly description: string;
|
|
75
|
+
};
|
|
76
|
+
readonly apiKey: {
|
|
77
|
+
readonly id: "apiKey";
|
|
78
|
+
readonly canonicalNoun: "API key";
|
|
79
|
+
readonly aliases: readonly [];
|
|
80
|
+
readonly deprecated: readonly ["access token", "access-token", "accessToken"];
|
|
81
|
+
readonly description: "The credential users create to authenticate the API/CLI. Never 'access token'.";
|
|
82
|
+
};
|
|
83
|
+
readonly collab: {
|
|
84
|
+
readonly id: "collab";
|
|
85
|
+
readonly canonicalNoun: "Collab";
|
|
86
|
+
readonly aliases: readonly [];
|
|
87
|
+
readonly description: "The collaboration feature (the comment surface). Capital-C as a feature name.";
|
|
88
|
+
};
|
|
89
|
+
readonly comment: {
|
|
90
|
+
readonly id: "comment";
|
|
91
|
+
readonly canonicalNoun: "comment";
|
|
92
|
+
readonly aliases: readonly [];
|
|
93
|
+
readonly description: "A single unit posted within Collab. Lowercase as a common noun.";
|
|
94
|
+
};
|
|
95
|
+
readonly superpowers: {
|
|
96
|
+
readonly id: "superpowers";
|
|
97
|
+
readonly canonicalNoun: "Superpowers";
|
|
98
|
+
readonly aliases: readonly [];
|
|
99
|
+
readonly description: "The page-enhancement feature. Capital-S as a feature name.";
|
|
100
|
+
};
|
|
101
|
+
readonly infra: {
|
|
102
|
+
readonly id: "infra";
|
|
103
|
+
readonly canonicalNoun: "infra";
|
|
104
|
+
readonly aliases: readonly ["infrastructure"];
|
|
105
|
+
readonly externalTerms: readonly ["WP.Cloud"];
|
|
106
|
+
readonly deprecated: readonly ["code.storage", "Pierre"];
|
|
107
|
+
readonly description: string;
|
|
108
|
+
};
|
|
109
|
+
};
|
|
110
|
+
export type ConceptId = keyof typeof CONCEPTS;
|
|
111
|
+
/** One sanctioned exception: a discouraged term that is OK in listed contexts. */
|
|
112
|
+
export interface ExternalTermRule {
|
|
113
|
+
/** The discouraged external term (matched case-insensitively by guards). */
|
|
114
|
+
readonly term: string;
|
|
115
|
+
/** The concept this term is an external synonym of. */
|
|
116
|
+
readonly concept: ConceptId;
|
|
117
|
+
/** Contexts where the term is allowed. Anywhere else is a leak. */
|
|
118
|
+
readonly allowedContexts: readonly TermContext[];
|
|
119
|
+
/** For `fixed-phrase` contexts, the exact phrases that license the term. */
|
|
120
|
+
readonly allowedPhrases?: readonly string[];
|
|
121
|
+
/** The canonical replacement to suggest when the use is NOT allowed. */
|
|
122
|
+
readonly replacement: string;
|
|
123
|
+
readonly reason: string;
|
|
124
|
+
}
|
|
125
|
+
export declare const EXTERNAL_TERM_ALLOWLIST: readonly [{
|
|
126
|
+
readonly term: "deployment";
|
|
127
|
+
readonly concept: "publish";
|
|
128
|
+
readonly allowedContexts: readonly ["github-integration", "platform-api"];
|
|
129
|
+
readonly replacement: "version / publish";
|
|
130
|
+
readonly reason: string;
|
|
131
|
+
}, {
|
|
132
|
+
readonly term: "deploy";
|
|
133
|
+
readonly concept: "publish";
|
|
134
|
+
readonly allowedContexts: readonly ["github-integration", "platform-api"];
|
|
135
|
+
readonly replacement: "publish";
|
|
136
|
+
readonly reason: "Push-deploys and the deploy status come from the GitHub integration.";
|
|
137
|
+
}, {
|
|
138
|
+
readonly term: "site";
|
|
139
|
+
readonly concept: "space";
|
|
140
|
+
readonly allowedContexts: readonly ["platform-api"];
|
|
141
|
+
readonly replacement: "space";
|
|
142
|
+
readonly reason: string;
|
|
143
|
+
}, {
|
|
144
|
+
readonly term: "WP.Cloud";
|
|
145
|
+
readonly concept: "infra";
|
|
146
|
+
readonly allowedContexts: readonly ["platform-api", "fixed-phrase"];
|
|
147
|
+
readonly allowedPhrases: readonly ["built on WP.Cloud"];
|
|
148
|
+
readonly replacement: "infra";
|
|
149
|
+
readonly reason: string;
|
|
150
|
+
}];
|
|
151
|
+
/** An internal provider name and how it must be referred to in public copy. */
|
|
152
|
+
export interface ProviderHygieneRule {
|
|
153
|
+
/** Internal/vendor name that must not appear on public surfaces. */
|
|
154
|
+
readonly internalName: string;
|
|
155
|
+
/** What to say instead, externally. */
|
|
156
|
+
readonly replacement: string;
|
|
157
|
+
/** Contexts (if any) where the internal name is still permitted. */
|
|
158
|
+
readonly allowedContexts: readonly TermContext[];
|
|
159
|
+
/** Exact phrases that license the internal name in `fixed-phrase` contexts. */
|
|
160
|
+
readonly allowedPhrases?: readonly string[];
|
|
161
|
+
readonly reason: string;
|
|
162
|
+
}
|
|
163
|
+
export declare const PROVIDER_HYGIENE: {
|
|
164
|
+
readonly e2b: {
|
|
165
|
+
readonly internalName: "E2B";
|
|
166
|
+
readonly replacement: "Spacefast Builds / Spacefast CI";
|
|
167
|
+
readonly allowedContexts: readonly [];
|
|
168
|
+
readonly reason: "The build sandbox vendor is never named; it is 'Spacefast Builds'.";
|
|
169
|
+
};
|
|
170
|
+
readonly wpcloud: {
|
|
171
|
+
readonly internalName: "WP.Cloud";
|
|
172
|
+
readonly replacement: "infra";
|
|
173
|
+
readonly allowedContexts: readonly ["platform-api", "fixed-phrase"];
|
|
174
|
+
readonly allowedPhrases: readonly ["built on WP.Cloud"];
|
|
175
|
+
readonly reason: string;
|
|
176
|
+
};
|
|
177
|
+
readonly codeStorage: {
|
|
178
|
+
readonly internalName: "code.storage";
|
|
179
|
+
readonly replacement: "infra";
|
|
180
|
+
readonly allowedContexts: readonly [];
|
|
181
|
+
readonly reason: "Internal storage layer; never surfaced.";
|
|
182
|
+
};
|
|
183
|
+
readonly pierre: {
|
|
184
|
+
readonly internalName: "Pierre";
|
|
185
|
+
readonly replacement: "Spacefast Builds / Spacefast CI";
|
|
186
|
+
readonly allowedContexts: readonly [];
|
|
187
|
+
readonly reason: "Internal git/CI dependency; never surfaced.";
|
|
188
|
+
};
|
|
189
|
+
};
|
|
190
|
+
export type ProviderHygieneId = keyof typeof PROVIDER_HYGIENE;
|
|
191
|
+
/**
|
|
192
|
+
* Canonical MCP tool naming. Tools are exposed under a namespaced canonical id
|
|
193
|
+
* (`spacefast_<verb>`) plus a bare alias (`<verb>`) for clients where per-server
|
|
194
|
+
* namespacing is already guaranteed.
|
|
195
|
+
*
|
|
196
|
+
* This replaces the current inverted form (`execute_spacefast`,
|
|
197
|
+
* `resume_spacefast`) and the un-namespaced verbs (`publish`, `prepare_publish`).
|
|
198
|
+
*/
|
|
199
|
+
export declare const MCP_TOOL_NAMING: {
|
|
200
|
+
/** Prefix that makes a tool id unambiguous across servers. */
|
|
201
|
+
readonly namespacePrefix: "spacefast_";
|
|
202
|
+
/** Canonical, namespaced tool id for a verb (e.g. "publish" → "spacefast_publish"). */
|
|
203
|
+
readonly canonical: (verb: string) => string;
|
|
204
|
+
/** Bare alias for a verb, valid only where per-server namespacing is guaranteed. */
|
|
205
|
+
readonly alias: (verb: string) => string;
|
|
206
|
+
/** Both names a verb should register under. */
|
|
207
|
+
readonly names: (verb: string) => {
|
|
208
|
+
readonly canonical: string;
|
|
209
|
+
readonly alias: string;
|
|
210
|
+
};
|
|
211
|
+
};
|
|
212
|
+
/**
|
|
213
|
+
* Verbs that have a locked canonical MCP tool name. Generators should emit
|
|
214
|
+
* `spacefast_<verb>` as the primary id and `<verb>` as the bare alias.
|
|
215
|
+
*/
|
|
216
|
+
export declare const MCP_CANONICAL_VERBS: readonly ["publish", "execute", "prepare_publish", "resume_publish", "finalize_publish"];
|
|
217
|
+
export type McpCanonicalVerb = (typeof MCP_CANONICAL_VERBS)[number];
|
|
218
|
+
/**
|
|
219
|
+
* Canonical CLI verb → accepted aliases. Reused across command groups so every
|
|
220
|
+
* `list`/`remove`/`create` command exposes the same shorthands. A command file
|
|
221
|
+
* spreads `CLI_ALIASES[canonical]` into its `aliases` array.
|
|
222
|
+
*/
|
|
223
|
+
export declare const CLI_ALIASES: {
|
|
224
|
+
readonly list: readonly ["ls"];
|
|
225
|
+
readonly remove: readonly ["rm", "delete"];
|
|
226
|
+
readonly create: readonly ["add"];
|
|
227
|
+
};
|
|
228
|
+
export type CliCanonicalVerb = keyof typeof CLI_ALIASES;
|
|
229
|
+
/** Aliases registered for a canonical CLI verb (empty for unknown verbs). */
|
|
230
|
+
export declare function cliAliasesFor(verb: CliCanonicalVerb): readonly string[];
|