@spacefast/common 0.0.5 → 0.0.7
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/spacefast-favicon.svg +16 -0
- package/brand-assets/spacefast-sf-full-bleed.svg +13 -0
- package/brand-assets/spacefast-wordmark.svg +5 -0
- package/dist/agents/connect-targets.d.ts +138 -0
- package/dist/agents/connect-targets.js +243 -0
- package/dist/brand-assets-build.d.ts +26 -0
- package/dist/brand-assets-build.js +81 -0
- package/dist/brand-assets.d.ts +42 -0
- package/dist/brand-assets.js +30 -0
- package/dist/brand.d.ts +1 -0
- package/dist/brand.js +5 -0
- package/dist/config/domains.d.ts +12 -6
- package/dist/config/domains.js +30 -38
- package/dist/contracts/access.d.ts +967 -124
- package/dist/contracts/access.js +804 -148
- package/dist/contracts/activity.d.ts +1 -1
- package/dist/contracts/activity.js +28 -2
- package/dist/contracts/annotations.d.ts +2 -4
- package/dist/contracts/api-keys.d.ts +126 -4
- package/dist/contracts/api-keys.js +74 -6
- package/dist/contracts/archives.d.ts +8 -2
- package/dist/contracts/archives.js +11 -4
- package/dist/contracts/billing.d.ts +0 -9
- package/dist/contracts/builds.d.ts +65 -21
- package/dist/contracts/builds.js +29 -0
- package/dist/contracts/common.d.ts +2 -0
- package/dist/contracts/common.js +4 -0
- package/dist/contracts/continuation.d.ts +0 -3
- package/dist/contracts/countries.d.ts +2 -0
- package/dist/contracts/countries.js +261 -0
- package/dist/contracts/deployments.d.ts +34 -34
- package/dist/contracts/device-auth.d.ts +4 -11
- package/dist/contracts/device-auth.js +4 -0
- package/dist/contracts/docs.d.ts +119 -0
- package/dist/contracts/docs.js +51 -0
- package/dist/contracts/domains.d.ts +96 -54
- package/dist/contracts/domains.js +26 -4
- package/dist/contracts/enums.d.ts +15 -16
- package/dist/contracts/enums.js +26 -14
- package/dist/contracts/error-code-meta.d.ts +184 -0
- package/dist/contracts/error-code-meta.js +124 -10
- package/dist/contracts/error-codes.d.ts +1 -1
- package/dist/contracts/error-codes.js +42 -0
- package/dist/contracts/events.d.ts +1 -1
- package/dist/contracts/feature-lifecycle-core.d.ts +36 -0
- package/dist/contracts/feature-lifecycle-core.js +220 -0
- package/dist/contracts/feature-lifecycle.d.ts +7 -0
- package/dist/contracts/feature-lifecycle.js +31 -0
- package/dist/contracts/features.d.ts +76 -6
- package/dist/contracts/features.js +178 -13
- package/dist/contracts/generated-feature-launch-entries.d.ts +2 -0
- package/dist/contracts/generated-feature-launch-entries.js +518 -0
- package/dist/contracts/git.d.ts +2 -8
- package/dist/contracts/ids.d.ts +9 -0
- package/dist/contracts/ids.js +13 -0
- package/dist/contracts/mcp.d.ts +8 -10
- package/dist/contracts/me.js +1 -1
- package/dist/contracts/oauth-resources.d.ts +19 -8
- package/dist/contracts/oauth-resources.js +44 -13
- package/dist/contracts/operations.d.ts +161 -1
- package/dist/contracts/operations.js +49 -1
- package/dist/contracts/platform.d.ts +6 -102
- package/dist/contracts/platform.js +6 -50
- package/dist/contracts/publishes.d.ts +130 -0
- package/dist/contracts/publishes.js +145 -0
- package/dist/contracts/push-new.d.ts +4 -4
- package/dist/contracts/quotas.d.ts +2 -0
- package/dist/contracts/quotas.js +8 -0
- package/dist/contracts/repository-connections.d.ts +9 -8
- package/dist/contracts/repository-connections.js +13 -0
- package/dist/contracts/resources.d.ts +99 -13
- package/dist/contracts/resources.js +36 -4
- package/dist/contracts/routes.d.ts +0 -2
- package/dist/contracts/runtime-api.d.ts +363 -39
- package/dist/contracts/runtime-api.js +276 -6
- package/dist/contracts/sf-config-v1.d.ts +75 -0
- package/dist/contracts/sf-config-v1.js +6 -0
- package/dist/contracts/sites.d.ts +19 -2
- package/dist/contracts/sites.js +21 -0
- package/dist/contracts/space-config.d.ts +201 -2
- package/dist/contracts/space-config.js +135 -5
- package/dist/contracts/spaces.d.ts +376 -196
- package/dist/contracts/spaces.js +62 -10
- package/dist/contracts/superadmin-emails.d.ts +4 -2
- package/dist/contracts/superadmin-emails.js +8 -0
- package/dist/contracts/superadmin-queues.d.ts +26 -26
- package/dist/contracts/superadmin-spaces.d.ts +172 -42
- package/dist/contracts/superadmin-tenants.d.ts +6 -6
- package/dist/contracts/superadmin-tenants.js +6 -11
- package/dist/contracts/superadmin.d.ts +6 -93
- package/dist/contracts/superadmin.js +6 -14
- package/dist/contracts/tags.d.ts +10 -9
- package/dist/contracts/tags.js +2 -1
- package/dist/contracts/teams.d.ts +26 -30
- package/dist/contracts/teams.js +11 -17
- package/dist/contracts/theme-json.d.ts +30 -0
- package/dist/contracts/theme-json.js +48 -0
- package/dist/contracts/variables.d.ts +0 -2
- package/dist/contracts/variables.js +0 -5
- package/dist/contracts/webhooks.d.ts +2 -0
- package/dist/contracts/webhooks.js +1 -1
- package/dist/contracts/zero.d.ts +5 -10
- package/dist/docs/agent-prose.d.ts +43 -3
- package/dist/docs/agent-prose.js +146 -7
- package/dist/docs/agent-setup.d.ts +51 -0
- package/dist/docs/agent-setup.js +211 -14
- package/dist/docs/agent-solutions.d.ts +5 -5
- package/dist/docs/agent-solutions.js +2 -2
- package/dist/docs/catalog.d.ts +650 -0
- package/dist/docs/catalog.js +758 -0
- package/dist/docs/error-docs.d.ts +0 -2
- package/dist/docs/error-docs.js +189 -35
- package/dist/docs/index-build.d.ts +17 -0
- package/dist/docs/index-build.js +24 -0
- package/dist/docs/search.d.ts +54 -0
- package/dist/docs/search.js +96 -0
- package/dist/docs/skill-distribution.d.ts +5 -3
- package/dist/docs/skill-distribution.js +8 -8
- package/dist/slug-policy/blocklist.d.ts +4 -0
- package/dist/slug-policy/blocklist.js +122 -0
- package/dist/slug-policy/index.d.ts +17 -0
- package/dist/slug-policy/index.js +86 -0
- package/dist/utils/access-match.d.ts +39 -0
- package/dist/utils/access-match.js +296 -0
- package/dist/utils/auth-redirect.d.ts +3 -1
- package/dist/utils/auth-redirect.js +21 -1
- package/dist/utils/build-settings.d.ts +6 -0
- package/dist/utils/build-settings.js +429 -46
- package/dist/utils/claim-token.d.ts +8 -0
- package/dist/utils/claim-token.js +23 -2
- package/dist/utils/concurrency.d.ts +1 -0
- package/dist/utils/concurrency.js +22 -0
- package/dist/utils/content-type.d.ts +1 -0
- package/dist/utils/content-type.js +1 -1
- package/dist/utils/credential-policy.d.ts +54 -0
- package/dist/utils/{access-policy.js → credential-policy.js} +28 -2
- package/dist/utils/email.d.ts +1 -0
- package/dist/utils/email.js +3 -0
- package/dist/utils/gate-theme.d.ts +61 -0
- package/dist/utils/gate-theme.js +217 -0
- package/dist/utils/git-repository.d.ts +0 -1
- package/dist/utils/git-repository.js +0 -3
- package/dist/utils/id-hints.d.ts +8 -0
- package/dist/utils/id-hints.js +61 -0
- package/dist/utils/local-space-state.d.ts +91 -0
- package/dist/utils/local-space-state.js +251 -0
- package/dist/utils/oauth-signed-query.d.ts +8 -0
- package/dist/utils/oauth-signed-query.js +26 -0
- package/dist/utils/publish-detection.d.ts +0 -8
- package/dist/utils/publish-detection.js +0 -93
- package/dist/utils/publish-policy.d.ts +6 -5
- package/dist/utils/publish-policy.js +71 -7
- package/dist/utils/query-keys.d.ts +16 -4
- package/dist/utils/query-keys.js +31 -4
- package/dist/utils/runtime-paths.d.ts +0 -1
- package/dist/utils/runtime-paths.js +0 -1
- package/dist/utils/runtime-upload-batch.d.ts +18 -0
- package/dist/utils/runtime-upload-batch.js +90 -0
- package/dist/utils/runtime-upload.d.ts +2 -0
- package/dist/utils/runtime-upload.js +5 -0
- package/dist/utils/sf-config-v1.d.ts +2 -0
- package/dist/utils/sf-config-v1.js +542 -0
- package/dist/utils/space-config.d.ts +4 -0
- package/dist/utils/space-config.js +10 -1
- package/dist/utils/space-theme.d.ts +2 -0
- package/dist/utils/space-theme.js +9 -0
- package/dist/utils/static-runtime-policy.d.ts +0 -21
- package/dist/utils/static-runtime-policy.js +19 -103
- package/dist/utils/upload-session.d.ts +1 -1
- package/dist/utils/wpcom-auth-redirects.d.ts +32 -2
- package/dist/utils/wpcom-auth-redirects.js +26 -10
- package/dist/vocabulary.d.ts +130 -30
- package/dist/vocabulary.js +77 -36
- package/package.json +35 -1
- package/dist/contracts/account.d.ts +0 -28
- package/dist/contracts/account.js +0 -54
- package/dist/contracts/intercom.d.ts +0 -6
- package/dist/contracts/intercom.js +0 -5
- package/dist/contracts/telemetry.d.ts +0 -2
- package/dist/contracts/telemetry.js +0 -1
- package/dist/utils/access-policy.d.ts +0 -39
- package/dist/utils/error-display.d.ts +0 -7
- package/dist/utils/error-display.js +0 -42
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// Pure, offline search + tier filtering over a docs index. Shared by the CLI's
|
|
2
|
+
// `sf docs` (offline, against the bundled JSON) and the control-plane's
|
|
3
|
+
// GET /v1/docs (in memory, against the catalog-derived index) so both rank and
|
|
4
|
+
// gate identically. Tier-aware — ESSENTIAL by default, widening to FULL
|
|
5
|
+
// (`--full`) and REFERENCE (`--all`).
|
|
6
|
+
import { tierVisibleAt } from "../vocabulary.js";
|
|
7
|
+
function toResult(entry) {
|
|
8
|
+
return {
|
|
9
|
+
slug: entry.slug,
|
|
10
|
+
title: entry.title,
|
|
11
|
+
kind: entry.kind,
|
|
12
|
+
tier: entry.tier,
|
|
13
|
+
summary: entry.summary,
|
|
14
|
+
url: entry.url,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function toTopic(entry) {
|
|
18
|
+
return { ...toResult(entry), body: entry.body };
|
|
19
|
+
}
|
|
20
|
+
/** Entries visible at `viewTier`, in catalog order. */
|
|
21
|
+
export function visibleEntries(index, viewTier) {
|
|
22
|
+
return index.entries.filter((entry) => tierVisibleAt(viewTier, entry.tier));
|
|
23
|
+
}
|
|
24
|
+
/** Exact topic lookup by slug (case-insensitive). Ignores tier — explicit intent wins. */
|
|
25
|
+
export function findTopic(index, slug) {
|
|
26
|
+
const needle = slug.trim().toLowerCase();
|
|
27
|
+
if (!needle)
|
|
28
|
+
return undefined;
|
|
29
|
+
return index.entries.find((entry) => entry.slug.toLowerCase() === needle);
|
|
30
|
+
}
|
|
31
|
+
function scoreEntry(entry, tokens) {
|
|
32
|
+
const slug = entry.slug.toLowerCase();
|
|
33
|
+
const title = entry.title.toLowerCase();
|
|
34
|
+
const keywords = entry.keywords.map((keyword) => keyword.toLowerCase());
|
|
35
|
+
let total = 0;
|
|
36
|
+
for (const token of tokens) {
|
|
37
|
+
let tokenScore = 0;
|
|
38
|
+
if (slug === token)
|
|
39
|
+
tokenScore = Math.max(tokenScore, 5);
|
|
40
|
+
else if (slug.includes(token))
|
|
41
|
+
tokenScore = Math.max(tokenScore, 3);
|
|
42
|
+
if (title.includes(token))
|
|
43
|
+
tokenScore = Math.max(tokenScore, 3);
|
|
44
|
+
if (keywords.some((keyword) => keyword.includes(token)))
|
|
45
|
+
tokenScore = Math.max(tokenScore, 2);
|
|
46
|
+
if (tokenScore === 0 && entry.haystack.includes(token))
|
|
47
|
+
tokenScore = 1;
|
|
48
|
+
// AND semantics: every query token must match somewhere.
|
|
49
|
+
if (tokenScore === 0)
|
|
50
|
+
return 0;
|
|
51
|
+
total += tokenScore;
|
|
52
|
+
}
|
|
53
|
+
return total;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Ranked query results are capped so a broad term (e.g. one that hits a chunk
|
|
57
|
+
* of the ~390 generated error-reference entries) can never return the whole
|
|
58
|
+
* catalog in one response. Listing a tier with no query is unaffected — that's
|
|
59
|
+
* an intentional "show me everything at this tier" request, not a search.
|
|
60
|
+
*/
|
|
61
|
+
export const SEARCH_DOCS_RESULT_LIMIT = 25;
|
|
62
|
+
/**
|
|
63
|
+
* Rank docs matching `query` within the tier. Empty query returns the full tier
|
|
64
|
+
* listing (catalog order), uncapped. A non-empty query ranks matches and caps
|
|
65
|
+
* the result count at {@link SEARCH_DOCS_RESULT_LIMIT}.
|
|
66
|
+
*/
|
|
67
|
+
export function searchDocs(index, options) {
|
|
68
|
+
const pool = visibleEntries(index, options.tier);
|
|
69
|
+
const tokens = (options.query ?? "").toLowerCase().split(/\s+/).filter(Boolean);
|
|
70
|
+
if (tokens.length === 0)
|
|
71
|
+
return pool;
|
|
72
|
+
return pool
|
|
73
|
+
.map((entry) => ({ entry, score: scoreEntry(entry, tokens) }))
|
|
74
|
+
.filter((scored) => scored.score > 0)
|
|
75
|
+
.toSorted((a, b) => b.score - a.score || a.entry.slug.localeCompare(b.entry.slug))
|
|
76
|
+
.slice(0, SEARCH_DOCS_RESULT_LIMIT)
|
|
77
|
+
.map((scored) => scored.entry);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Resolve the `sf docs [query]` request: an exact-slug query prints that topic;
|
|
81
|
+
* any other query searches; an empty query lists the tier. Pure + offline.
|
|
82
|
+
*/
|
|
83
|
+
export function resolveDocsCommand(index, options) {
|
|
84
|
+
const trimmed = (options.query ?? "").trim();
|
|
85
|
+
const exact = trimmed ? findTopic(index, trimmed) : undefined;
|
|
86
|
+
if (exact) {
|
|
87
|
+
return {
|
|
88
|
+
query: trimmed || null,
|
|
89
|
+
tier: options.tier,
|
|
90
|
+
topic: toTopic(exact),
|
|
91
|
+
results: [toResult(exact)],
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
const results = searchDocs(index, { query: trimmed, tier: options.tier }).map(toResult);
|
|
95
|
+
return { query: trimmed || null, tier: options.tier, topic: null, results };
|
|
96
|
+
}
|
|
@@ -3,10 +3,12 @@ export declare const spacefastSkillRepoOwner = "spacefast";
|
|
|
3
3
|
export declare const spacefastSkillRepoName = "plugins";
|
|
4
4
|
export declare const spacefastSkillRepoRef = "spacefast/plugins";
|
|
5
5
|
export declare const spacefastSkillUrl: string;
|
|
6
|
-
export declare const
|
|
6
|
+
export declare const spacefastSkillFetchCommand: string;
|
|
7
|
+
export declare const spacefastSkillInstallCommand = "npx -y skills add spacefast/plugins --skill spacefast -g -y";
|
|
7
8
|
export declare const spacefastInstallCurlCommand: string;
|
|
8
9
|
export declare const spacefastInstallPowerShellCommand: string;
|
|
9
10
|
export declare const spacefastClaudePluginAddCommand = "claude plugin marketplace add spacefast/plugins";
|
|
10
11
|
export declare const spacefastClaudePluginInstallCommand = "claude plugin install spacefast@spacefast";
|
|
11
|
-
export declare const
|
|
12
|
-
export declare const
|
|
12
|
+
export declare const spacefastCodexPluginAddCommand = "codex plugin marketplace add spacefast/plugins";
|
|
13
|
+
export declare const spacefastCodexPluginInstallCommand = "codex plugin add spacefast@spacefast";
|
|
14
|
+
export declare const spacefastCodexSkillInstallCommand = "npx -y skills add spacefast/plugins --skill spacefast -g -a codex -y";
|
|
@@ -4,15 +4,15 @@ export const spacefastSkillRepoOwner = "spacefast";
|
|
|
4
4
|
export const spacefastSkillRepoName = "plugins";
|
|
5
5
|
export const spacefastSkillRepoRef = `${spacefastSkillRepoOwner}/${spacefastSkillRepoName}`;
|
|
6
6
|
export const spacefastSkillUrl = `${BRAND.apiOrigin}/skill`;
|
|
7
|
-
export const
|
|
7
|
+
export const spacefastSkillFetchCommand = `curl -fsSL ${spacefastSkillUrl}`;
|
|
8
|
+
export const spacefastSkillInstallCommand = `npx -y skills add ${spacefastSkillRepoRef} --skill ${spacefastSkillName} -g -y`;
|
|
8
9
|
export const spacefastInstallCurlCommand = `curl -fsSL ${BRAND.websiteOrigin}/install.sh | bash`;
|
|
9
10
|
export const spacefastInstallPowerShellCommand = `irm ${BRAND.websiteOrigin}/install.ps1 | iex`;
|
|
10
11
|
export const spacefastClaudePluginAddCommand = `claude plugin marketplace add ${spacefastSkillRepoRef}`;
|
|
11
12
|
export const spacefastClaudePluginInstallCommand = `claude plugin install ${spacefastSkillName}@${spacefastSkillName}`;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
If your environment supports manual skill import, fetch the official ${BRAND.productName} skill from: ${spacefastSkillUrl}`;
|
|
13
|
+
// Codex mirrors the Claude plugin commands (same marketplace repo, same
|
|
14
|
+
// plugin name); the release workflow validates both installs against the
|
|
15
|
+
// assembled dist/plugins tree.
|
|
16
|
+
export const spacefastCodexPluginAddCommand = `codex plugin marketplace add ${spacefastSkillRepoRef}`;
|
|
17
|
+
export const spacefastCodexPluginInstallCommand = `codex plugin add ${spacefastSkillName}@${spacefastSkillName}`;
|
|
18
|
+
export const spacefastCodexSkillInstallCommand = `npx -y skills add ${spacefastSkillRepoRef} --skill ${spacefastSkillName} -g -a codex -y`;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare const INFRA_SLUG_BLOCKLIST: readonly ["www", "www1", "www2", "www3", "web", "mail", "webmail", "smtp", "imap", "pop", "mx", "mx1", "mx2", "ns", "ns1", "ns2", "ns3", "ns4", "dns", "ftp", "ssh", "vpn", "git", "svn", "cdn", "assets", "static", "img", "images", "files", "media", "local", "localhost", "host", "server", "remote", "portal", "owa", "exchange", "gov", "nic", "whois"];
|
|
2
|
+
export declare const PRODUCT_PLATFORM_SLUG_BLOCKLIST: readonly ["spacefast", "stattic", "viewfast", "view-fast", "sf", "my", "api", "app", "apps", "admin", "superadmin", "dashboard", "console", "login", "logout", "auth", "oauth", "sso", "signup", "signin", "register", "claim", "billing", "checkout", "mcp", "cli", "sdk", "cast", "preview", "previews", "internal", "ops", "handbook"];
|
|
3
|
+
export declare const TRADEMARK_EXACT_SLUG_BLOCKLIST: readonly ["automattic", "wordpress", "woocommerce", "jetpack", "wpcloud", "google", "facebook", "instagram", "twitter", "x", "tiktok", "youtube", "amazon", "apple", "microsoft", "netflix", "paypal", "stripe", "visa", "mastercard", "coinbase", "metamask", "openai", "anthropic", "claude", "github", "gitlab", "vercel", "netlify", "cloudflare"];
|
|
4
|
+
export declare const SUBSTRING_SLUG_BLOCKLIST: readonly ["spacefast", "stattic", "automattic", "wordpress", "paypal"];
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
// Infrastructure hostnames reserved by WordPress.com managed-subdomain policy.
|
|
2
|
+
export const INFRA_SLUG_BLOCKLIST = [
|
|
3
|
+
"www",
|
|
4
|
+
"www1",
|
|
5
|
+
"www2",
|
|
6
|
+
"www3",
|
|
7
|
+
"web",
|
|
8
|
+
"mail",
|
|
9
|
+
"webmail",
|
|
10
|
+
"smtp",
|
|
11
|
+
"imap",
|
|
12
|
+
"pop",
|
|
13
|
+
"mx",
|
|
14
|
+
"mx1",
|
|
15
|
+
"mx2",
|
|
16
|
+
"ns",
|
|
17
|
+
"ns1",
|
|
18
|
+
"ns2",
|
|
19
|
+
"ns3",
|
|
20
|
+
"ns4",
|
|
21
|
+
"dns",
|
|
22
|
+
"ftp",
|
|
23
|
+
"ssh",
|
|
24
|
+
"vpn",
|
|
25
|
+
"git",
|
|
26
|
+
"svn",
|
|
27
|
+
"cdn",
|
|
28
|
+
"assets",
|
|
29
|
+
"static",
|
|
30
|
+
"img",
|
|
31
|
+
"images",
|
|
32
|
+
"files",
|
|
33
|
+
"media",
|
|
34
|
+
"local",
|
|
35
|
+
"localhost",
|
|
36
|
+
"host",
|
|
37
|
+
"server",
|
|
38
|
+
"remote",
|
|
39
|
+
"portal",
|
|
40
|
+
"owa",
|
|
41
|
+
"exchange",
|
|
42
|
+
"gov",
|
|
43
|
+
"nic",
|
|
44
|
+
"whois",
|
|
45
|
+
];
|
|
46
|
+
// Spacefast product, platform, and public surface names.
|
|
47
|
+
export const PRODUCT_PLATFORM_SLUG_BLOCKLIST = [
|
|
48
|
+
"spacefast",
|
|
49
|
+
"stattic",
|
|
50
|
+
"viewfast",
|
|
51
|
+
"view-fast",
|
|
52
|
+
"sf",
|
|
53
|
+
"my",
|
|
54
|
+
"api",
|
|
55
|
+
"app",
|
|
56
|
+
"apps",
|
|
57
|
+
"admin",
|
|
58
|
+
"superadmin",
|
|
59
|
+
"dashboard",
|
|
60
|
+
"console",
|
|
61
|
+
"login",
|
|
62
|
+
"logout",
|
|
63
|
+
"auth",
|
|
64
|
+
"oauth",
|
|
65
|
+
"sso",
|
|
66
|
+
"signup",
|
|
67
|
+
"signin",
|
|
68
|
+
"register",
|
|
69
|
+
"claim",
|
|
70
|
+
"billing",
|
|
71
|
+
"checkout",
|
|
72
|
+
"mcp",
|
|
73
|
+
"cli",
|
|
74
|
+
"sdk",
|
|
75
|
+
"cast",
|
|
76
|
+
"preview",
|
|
77
|
+
"previews",
|
|
78
|
+
"internal",
|
|
79
|
+
"ops",
|
|
80
|
+
"handbook",
|
|
81
|
+
];
|
|
82
|
+
// Exact trademark and impersonation names reserved in every managed namespace.
|
|
83
|
+
export const TRADEMARK_EXACT_SLUG_BLOCKLIST = [
|
|
84
|
+
"automattic",
|
|
85
|
+
"wordpress",
|
|
86
|
+
"woocommerce",
|
|
87
|
+
"jetpack",
|
|
88
|
+
"wpcloud",
|
|
89
|
+
"google",
|
|
90
|
+
"facebook",
|
|
91
|
+
"instagram",
|
|
92
|
+
"twitter",
|
|
93
|
+
"x",
|
|
94
|
+
"tiktok",
|
|
95
|
+
"youtube",
|
|
96
|
+
"amazon",
|
|
97
|
+
"apple",
|
|
98
|
+
"microsoft",
|
|
99
|
+
"netflix",
|
|
100
|
+
"paypal",
|
|
101
|
+
"stripe",
|
|
102
|
+
"visa",
|
|
103
|
+
"mastercard",
|
|
104
|
+
"coinbase",
|
|
105
|
+
"metamask",
|
|
106
|
+
"openai",
|
|
107
|
+
"anthropic",
|
|
108
|
+
"claude",
|
|
109
|
+
"github",
|
|
110
|
+
"gitlab",
|
|
111
|
+
"vercel",
|
|
112
|
+
"netlify",
|
|
113
|
+
"cloudflare",
|
|
114
|
+
];
|
|
115
|
+
// Minimal substring rules for high-risk product and impersonation strings.
|
|
116
|
+
export const SUBSTRING_SLUG_BLOCKLIST = [
|
|
117
|
+
"spacefast",
|
|
118
|
+
"stattic",
|
|
119
|
+
"automattic",
|
|
120
|
+
"wordpress",
|
|
121
|
+
"paypal",
|
|
122
|
+
];
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export { INFRA_SLUG_BLOCKLIST, PRODUCT_PLATFORM_SLUG_BLOCKLIST, SUBSTRING_SLUG_BLOCKLIST, TRADEMARK_EXACT_SLUG_BLOCKLIST, } from "./blocklist.js";
|
|
2
|
+
export type SlugPolicyOptions = {
|
|
3
|
+
maxLength?: number;
|
|
4
|
+
};
|
|
5
|
+
export type SlugPolicyVerdict = {
|
|
6
|
+
ok: true;
|
|
7
|
+
normalized: string;
|
|
8
|
+
} | {
|
|
9
|
+
ok: false;
|
|
10
|
+
reason: "slug_invalid" | "slug_reserved";
|
|
11
|
+
detail: string;
|
|
12
|
+
};
|
|
13
|
+
export declare const SLUG_MIN_LENGTH = 3;
|
|
14
|
+
export declare const TEAM_SLUG_MAX_LENGTH = 63;
|
|
15
|
+
export declare const SPACE_SLUG_MAX_LENGTH = 55;
|
|
16
|
+
export declare function evaluateSlugCandidate(input: string, options?: SlugPolicyOptions): SlugPolicyVerdict;
|
|
17
|
+
export declare function slugPolicyFailureSurvivesSuffix(normalized: string): boolean;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { MANAGEMENT_LABEL_PREFIX } from "../config/domains.js";
|
|
2
|
+
import { INFRA_SLUG_BLOCKLIST, PRODUCT_PLATFORM_SLUG_BLOCKLIST, SUBSTRING_SLUG_BLOCKLIST, TRADEMARK_EXACT_SLUG_BLOCKLIST, } from "./blocklist.js";
|
|
3
|
+
export { INFRA_SLUG_BLOCKLIST, PRODUCT_PLATFORM_SLUG_BLOCKLIST, SUBSTRING_SLUG_BLOCKLIST, TRADEMARK_EXACT_SLUG_BLOCKLIST, } from "./blocklist.js";
|
|
4
|
+
export const SLUG_MIN_LENGTH = 3;
|
|
5
|
+
export const TEAM_SLUG_MAX_LENGTH = 63;
|
|
6
|
+
export const SPACE_SLUG_MAX_LENGTH = 55;
|
|
7
|
+
const infraReservedSlugs = new Set(INFRA_SLUG_BLOCKLIST);
|
|
8
|
+
const productPlatformReservedSlugs = new Set(PRODUCT_PLATFORM_SLUG_BLOCKLIST);
|
|
9
|
+
const trademarkExactReservedSlugs = new Set(TRADEMARK_EXACT_SLUG_BLOCKLIST);
|
|
10
|
+
export function evaluateSlugCandidate(input, options = {}) {
|
|
11
|
+
const maxLength = options.maxLength ?? TEAM_SLUG_MAX_LENGTH;
|
|
12
|
+
// Explicit user choices are rejected rather than silently reshaped. Case,
|
|
13
|
+
// surrounding whitespace, and diacritics are the only accepted canonicalization.
|
|
14
|
+
const casefolded = input
|
|
15
|
+
.trim()
|
|
16
|
+
.normalize("NFD")
|
|
17
|
+
.replace(/[\u0300-\u036f]/g, "")
|
|
18
|
+
.toLowerCase();
|
|
19
|
+
if (!/^[a-z0-9-]+$/.test(casefolded)) {
|
|
20
|
+
return invalid("Slug may only contain lowercase letters, numbers, and dashes.");
|
|
21
|
+
}
|
|
22
|
+
const structuralCandidate = casefolded;
|
|
23
|
+
if (structuralCandidate.includes("--")) {
|
|
24
|
+
return invalid("Slug cannot contain -- because it is the version/branch hostname separator.");
|
|
25
|
+
}
|
|
26
|
+
const normalized = casefolded.replace(/^-+|-+$/g, "");
|
|
27
|
+
const structuralVerdict = evaluateStructuralRules(normalized, structuralCandidate, maxLength);
|
|
28
|
+
if (!structuralVerdict.ok) {
|
|
29
|
+
return structuralVerdict;
|
|
30
|
+
}
|
|
31
|
+
const reservedVerdict = evaluateReservedRules(normalized);
|
|
32
|
+
if (!reservedVerdict.ok) {
|
|
33
|
+
return reservedVerdict;
|
|
34
|
+
}
|
|
35
|
+
return { ok: true, normalized };
|
|
36
|
+
}
|
|
37
|
+
function evaluateStructuralRules(normalized, structuralCandidate, maxLength) {
|
|
38
|
+
if (normalized.length < SLUG_MIN_LENGTH) {
|
|
39
|
+
return invalid(`Slug must be at least ${SLUG_MIN_LENGTH} characters.`);
|
|
40
|
+
}
|
|
41
|
+
if (normalized.length > maxLength) {
|
|
42
|
+
return invalid(`Slug must be ${maxLength} characters or fewer.`);
|
|
43
|
+
}
|
|
44
|
+
if (structuralCandidate.startsWith("-") || structuralCandidate.endsWith("-")) {
|
|
45
|
+
return invalid("Slug cannot start or end with a dash.");
|
|
46
|
+
}
|
|
47
|
+
if (/^[0-9]+$/.test(normalized)) {
|
|
48
|
+
return invalid("Slug cannot be all numeric.");
|
|
49
|
+
}
|
|
50
|
+
if (normalized.startsWith(MANAGEMENT_LABEL_PREFIX)) {
|
|
51
|
+
return reserved(`Slug cannot start with reserved prefix ${MANAGEMENT_LABEL_PREFIX}.`);
|
|
52
|
+
}
|
|
53
|
+
return { ok: true, normalized };
|
|
54
|
+
}
|
|
55
|
+
// A substring reservation survives numeric suffixing (`paypal-2` still
|
|
56
|
+
// contains `paypal`). Suggestion generators use this structured signal to
|
|
57
|
+
// abandon the base instead of exhausting every suffix.
|
|
58
|
+
function matchingReservedSubstring(normalized) {
|
|
59
|
+
return SUBSTRING_SLUG_BLOCKLIST.find((reservedSubstring) => normalized.includes(reservedSubstring));
|
|
60
|
+
}
|
|
61
|
+
export function slugPolicyFailureSurvivesSuffix(normalized) {
|
|
62
|
+
return (matchingReservedSubstring(normalized) !== undefined ||
|
|
63
|
+
normalized.startsWith(MANAGEMENT_LABEL_PREFIX));
|
|
64
|
+
}
|
|
65
|
+
function evaluateReservedRules(normalized) {
|
|
66
|
+
if (infraReservedSlugs.has(normalized)) {
|
|
67
|
+
return reserved("Slug is reserved for infrastructure.");
|
|
68
|
+
}
|
|
69
|
+
if (productPlatformReservedSlugs.has(normalized)) {
|
|
70
|
+
return reserved("Slug is reserved for Spacefast product and platform use.");
|
|
71
|
+
}
|
|
72
|
+
if (trademarkExactReservedSlugs.has(normalized)) {
|
|
73
|
+
return reserved("Slug is reserved for trademark and impersonation protection.");
|
|
74
|
+
}
|
|
75
|
+
const substringRule = matchingReservedSubstring(normalized);
|
|
76
|
+
if (substringRule) {
|
|
77
|
+
return reserved(`Slug contains reserved substring ${substringRule}.`);
|
|
78
|
+
}
|
|
79
|
+
return { ok: true, normalized };
|
|
80
|
+
}
|
|
81
|
+
function invalid(detail) {
|
|
82
|
+
return { ok: false, reason: "slug_invalid", detail };
|
|
83
|
+
}
|
|
84
|
+
function reserved(detail) {
|
|
85
|
+
return { ok: false, reason: "slug_reserved", detail };
|
|
86
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export declare function filterGrantsByNamespace(grants: string[], namespaces: string[]): string[];
|
|
2
|
+
export declare function grantGlobMatches(pattern: string, grant: string): boolean;
|
|
3
|
+
export declare function grantsIntersect(grants: string[], requiredGrants: string[]): boolean;
|
|
4
|
+
export declare function grantsSatisfyRule(input: {
|
|
5
|
+
grants: string[];
|
|
6
|
+
requiredGrants: string[];
|
|
7
|
+
grantNamespaces: string[];
|
|
8
|
+
}): boolean;
|
|
9
|
+
export declare function canonicalizeHost(host: string): string;
|
|
10
|
+
export declare function hostGlobMatches(pattern: string, host: string): boolean;
|
|
11
|
+
export declare function hostTemplateMatches(template: string, host: string): boolean;
|
|
12
|
+
export declare function pathGlobMatches(pattern: string, path: string): boolean;
|
|
13
|
+
export declare function agentMatches(needle: string, agent: string): boolean;
|
|
14
|
+
export declare function ipInCidr(ip: string, cidr: string): boolean;
|
|
15
|
+
export declare function ipInAnyCidr(ip: string, cidrs: string[]): boolean;
|
|
16
|
+
export type RuleMatchContext = {
|
|
17
|
+
host: string;
|
|
18
|
+
path: string;
|
|
19
|
+
channel?: string | undefined;
|
|
20
|
+
ip?: string | undefined;
|
|
21
|
+
agent?: string | undefined;
|
|
22
|
+
country?: string | undefined;
|
|
23
|
+
headers?: Record<string, string> | undefined;
|
|
24
|
+
};
|
|
25
|
+
export type RuleMatchInput = {
|
|
26
|
+
host?: string | undefined;
|
|
27
|
+
hostPattern?: string | undefined;
|
|
28
|
+
hostTemplate?: string | undefined;
|
|
29
|
+
pathPattern?: string | undefined;
|
|
30
|
+
channel?: string | undefined;
|
|
31
|
+
ipCidrs?: string[] | undefined;
|
|
32
|
+
agent?: string | undefined;
|
|
33
|
+
country?: string | undefined;
|
|
34
|
+
header?: {
|
|
35
|
+
name: string;
|
|
36
|
+
value: string;
|
|
37
|
+
} | undefined;
|
|
38
|
+
};
|
|
39
|
+
export declare function ruleMatchApplies(match: RuleMatchInput, context: RuleMatchContext): boolean;
|