@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.
Files changed (202) hide show
  1. package/README.md +42 -0
  2. package/dist/brand.d.ts +15 -0
  3. package/dist/brand.js +24 -0
  4. package/dist/config/domains.d.ts +30 -0
  5. package/dist/config/domains.js +75 -0
  6. package/dist/config/index.d.ts +1 -0
  7. package/dist/config/index.js +3 -0
  8. package/dist/contracts/abuse.d.ts +93 -0
  9. package/dist/contracts/abuse.js +50 -0
  10. package/dist/contracts/access.d.ts +409 -0
  11. package/dist/contracts/access.js +396 -0
  12. package/dist/contracts/account.d.ts +28 -0
  13. package/dist/contracts/account.js +54 -0
  14. package/dist/contracts/activity.d.ts +118 -0
  15. package/dist/contracts/activity.js +180 -0
  16. package/dist/contracts/analytics.d.ts +56 -0
  17. package/dist/contracts/analytics.js +91 -0
  18. package/dist/contracts/annotations.d.ts +327 -0
  19. package/dist/contracts/annotations.js +234 -0
  20. package/dist/contracts/api-keys.d.ts +517 -0
  21. package/dist/contracts/api-keys.js +235 -0
  22. package/dist/contracts/archives.d.ts +332 -0
  23. package/dist/contracts/archives.js +120 -0
  24. package/dist/contracts/auth.d.ts +86 -0
  25. package/dist/contracts/auth.js +82 -0
  26. package/dist/contracts/beta.d.ts +106 -0
  27. package/dist/contracts/beta.js +104 -0
  28. package/dist/contracts/billing.d.ts +328 -0
  29. package/dist/contracts/billing.js +203 -0
  30. package/dist/contracts/builds.d.ts +504 -0
  31. package/dist/contracts/builds.js +248 -0
  32. package/dist/contracts/channels.d.ts +49 -0
  33. package/dist/contracts/channels.js +51 -0
  34. package/dist/contracts/common.d.ts +238 -0
  35. package/dist/contracts/common.js +175 -0
  36. package/dist/contracts/deployments.d.ts +574 -0
  37. package/dist/contracts/deployments.js +97 -0
  38. package/dist/contracts/device-auth.d.ts +194 -0
  39. package/dist/contracts/device-auth.js +162 -0
  40. package/dist/contracts/domains.d.ts +6115 -0
  41. package/dist/contracts/domains.js +1435 -0
  42. package/dist/contracts/enums.d.ts +196 -0
  43. package/dist/contracts/enums.js +211 -0
  44. package/dist/contracts/error-code-meta.d.ts +1560 -0
  45. package/dist/contracts/error-code-meta.js +400 -0
  46. package/dist/contracts/error-codes.d.ts +13 -0
  47. package/dist/contracts/error-codes.js +416 -0
  48. package/dist/contracts/events.d.ts +398 -0
  49. package/dist/contracts/events.js +320 -0
  50. package/dist/contracts/fast-domain-search.d.ts +19 -0
  51. package/dist/contracts/fast-domain-search.js +22 -0
  52. package/dist/contracts/features.d.ts +240 -0
  53. package/dist/contracts/features.js +406 -0
  54. package/dist/contracts/git.d.ts +288 -0
  55. package/dist/contracts/git.js +116 -0
  56. package/dist/contracts/ids.d.ts +35 -0
  57. package/dist/contracts/ids.js +45 -0
  58. package/dist/contracts/intercom.d.ts +6 -0
  59. package/dist/contracts/intercom.js +5 -0
  60. package/dist/contracts/internal.d.ts +103 -0
  61. package/dist/contracts/internal.js +92 -0
  62. package/dist/contracts/mcp.d.ts +788 -0
  63. package/dist/contracts/mcp.js +161 -0
  64. package/dist/contracts/me.d.ts +81 -0
  65. package/dist/contracts/me.js +104 -0
  66. package/dist/contracts/oauth-resources.d.ts +30 -0
  67. package/dist/contracts/oauth-resources.js +61 -0
  68. package/dist/contracts/operations.d.ts +54 -0
  69. package/dist/contracts/operations.js +14 -0
  70. package/dist/contracts/plan-policy.d.ts +85 -0
  71. package/dist/contracts/plan-policy.js +75 -0
  72. package/dist/contracts/platform.d.ts +359 -0
  73. package/dist/contracts/platform.js +216 -0
  74. package/dist/contracts/push-new.d.ts +156 -0
  75. package/dist/contracts/push-new.js +52 -0
  76. package/dist/contracts/quotas.d.ts +16 -0
  77. package/dist/contracts/quotas.js +36 -0
  78. package/dist/contracts/repository-connections.d.ts +231 -0
  79. package/dist/contracts/repository-connections.js +106 -0
  80. package/dist/contracts/resources.d.ts +529 -0
  81. package/dist/contracts/resources.js +209 -0
  82. package/dist/contracts/routes.d.ts +45 -0
  83. package/dist/contracts/routes.js +31 -0
  84. package/dist/contracts/runtime-api.d.ts +449 -0
  85. package/dist/contracts/runtime-api.js +513 -0
  86. package/dist/contracts/sites.d.ts +393 -0
  87. package/dist/contracts/sites.js +121 -0
  88. package/dist/contracts/slugs.d.ts +31 -0
  89. package/dist/contracts/slugs.js +47 -0
  90. package/dist/contracts/space-config.d.ts +216 -0
  91. package/dist/contracts/space-config.js +237 -0
  92. package/dist/contracts/space-theme.d.ts +25 -0
  93. package/dist/contracts/space-theme.js +49 -0
  94. package/dist/contracts/spaces.d.ts +2760 -0
  95. package/dist/contracts/spaces.js +589 -0
  96. package/dist/contracts/superadmin-activity.d.ts +205 -0
  97. package/dist/contracts/superadmin-activity.js +107 -0
  98. package/dist/contracts/superadmin-emails.d.ts +212 -0
  99. package/dist/contracts/superadmin-emails.js +112 -0
  100. package/dist/contracts/superadmin-queues.d.ts +414 -0
  101. package/dist/contracts/superadmin-queues.js +202 -0
  102. package/dist/contracts/superadmin-runtime.d.ts +27 -0
  103. package/dist/contracts/superadmin-runtime.js +38 -0
  104. package/dist/contracts/superadmin-spaces.d.ts +429 -0
  105. package/dist/contracts/superadmin-spaces.js +82 -0
  106. package/dist/contracts/superadmin-tenants.d.ts +180 -0
  107. package/dist/contracts/superadmin-tenants.js +117 -0
  108. package/dist/contracts/superadmin.d.ts +654 -0
  109. package/dist/contracts/superadmin.js +309 -0
  110. package/dist/contracts/tags.d.ts +1140 -0
  111. package/dist/contracts/tags.js +1050 -0
  112. package/dist/contracts/teams.d.ts +233 -0
  113. package/dist/contracts/teams.js +174 -0
  114. package/dist/contracts/telemetry.d.ts +2 -0
  115. package/dist/contracts/telemetry.js +1 -0
  116. package/dist/contracts/transfers.d.ts +105 -0
  117. package/dist/contracts/transfers.js +48 -0
  118. package/dist/contracts/variables.d.ts +189 -0
  119. package/dist/contracts/variables.js +82 -0
  120. package/dist/contracts/webhooks.d.ts +159 -0
  121. package/dist/contracts/webhooks.js +59 -0
  122. package/dist/contracts/zero.d.ts +1538 -0
  123. package/dist/contracts/zero.js +755 -0
  124. package/dist/docs/agent-prose.d.ts +35 -0
  125. package/dist/docs/agent-prose.js +153 -0
  126. package/dist/docs/agent-setup.d.ts +6 -0
  127. package/dist/docs/agent-setup.js +164 -0
  128. package/dist/docs/agent-solutions.d.ts +110 -0
  129. package/dist/docs/agent-solutions.js +181 -0
  130. package/dist/docs/error-docs.d.ts +12 -0
  131. package/dist/docs/error-docs.js +1601 -0
  132. package/dist/docs/skill-distribution.d.ts +12 -0
  133. package/dist/docs/skill-distribution.js +18 -0
  134. package/dist/domain-exploration.d.ts +108 -0
  135. package/dist/domain-exploration.js +488 -0
  136. package/dist/domain-normalize.d.ts +1 -0
  137. package/dist/domain-normalize.js +14 -0
  138. package/dist/domain-zones.d.ts +1 -0
  139. package/dist/domain-zones.js +5272 -0
  140. package/dist/index.d.ts +1 -0
  141. package/dist/index.js +1 -0
  142. package/dist/utils/access-policy.d.ts +39 -0
  143. package/dist/utils/access-policy.js +232 -0
  144. package/dist/utils/api.d.ts +4 -0
  145. package/dist/utils/api.js +13 -0
  146. package/dist/utils/auth-redirect.d.ts +1 -0
  147. package/dist/utils/auth-redirect.js +48 -0
  148. package/dist/utils/auth.d.ts +5 -0
  149. package/dist/utils/auth.js +13 -0
  150. package/dist/utils/build-settings.d.ts +49 -0
  151. package/dist/utils/build-settings.js +366 -0
  152. package/dist/utils/claim-token.d.ts +1 -0
  153. package/dist/utils/claim-token.js +9 -0
  154. package/dist/utils/collections.d.ts +1 -0
  155. package/dist/utils/collections.js +5 -0
  156. package/dist/utils/concurrency.d.ts +1 -0
  157. package/dist/utils/concurrency.js +18 -0
  158. package/dist/utils/content-type.d.ts +10 -0
  159. package/dist/utils/content-type.js +145 -0
  160. package/dist/utils/error-display.d.ts +7 -0
  161. package/dist/utils/error-display.js +42 -0
  162. package/dist/utils/error.d.ts +1 -0
  163. package/dist/utils/error.js +3 -0
  164. package/dist/utils/generate-space-name.d.ts +14 -0
  165. package/dist/utils/generate-space-name.js +95 -0
  166. package/dist/utils/git-repository.d.ts +5 -0
  167. package/dist/utils/git-repository.js +99 -0
  168. package/dist/utils/guards.d.ts +1 -0
  169. package/dist/utils/guards.js +3 -0
  170. package/dist/utils/publish-detection.d.ts +10 -0
  171. package/dist/utils/publish-detection.js +118 -0
  172. package/dist/utils/publish-policy.d.ts +69 -0
  173. package/dist/utils/publish-policy.js +429 -0
  174. package/dist/utils/query-client.d.ts +7 -0
  175. package/dist/utils/query-client.js +21 -0
  176. package/dist/utils/query-keys.d.ts +75 -0
  177. package/dist/utils/query-keys.js +78 -0
  178. package/dist/utils/runtime-paths.d.ts +7 -0
  179. package/dist/utils/runtime-paths.js +28 -0
  180. package/dist/utils/runtime-upload.d.ts +19 -0
  181. package/dist/utils/runtime-upload.js +27 -0
  182. package/dist/utils/slug.d.ts +5 -0
  183. package/dist/utils/slug.js +13 -0
  184. package/dist/utils/space-config.d.ts +75 -0
  185. package/dist/utils/space-config.js +415 -0
  186. package/dist/utils/space-theme.d.ts +16 -0
  187. package/dist/utils/space-theme.js +89 -0
  188. package/dist/utils/static-runtime-policy.d.ts +17 -0
  189. package/dist/utils/static-runtime-policy.js +103 -0
  190. package/dist/utils/upload-session.d.ts +34 -0
  191. package/dist/utils/upload-session.js +69 -0
  192. package/dist/utils/urls.d.ts +2 -0
  193. package/dist/utils/urls.js +7 -0
  194. package/dist/utils/version-path.d.ts +6 -0
  195. package/dist/utils/version-path.js +51 -0
  196. package/dist/utils/wpcom-auth-redirects.d.ts +14 -0
  197. package/dist/utils/wpcom-auth-redirects.js +49 -0
  198. package/dist/utils/zero-realtime-signature.d.ts +12 -0
  199. package/dist/utils/zero-realtime-signature.js +45 -0
  200. package/dist/vocabulary.d.ts +230 -0
  201. package/dist/vocabulary.js +218 -0
  202. package/package.json +58 -0
@@ -0,0 +1,12 @@
1
+ export declare const spacefastSkillName = "spacefast";
2
+ export declare const spacefastSkillRepoOwner = "spacefast";
3
+ export declare const spacefastSkillRepoName = "plugins";
4
+ export declare const spacefastSkillRepoRef = "spacefast/plugins";
5
+ export declare const spacefastSkillUrl: string;
6
+ export declare const spacefastSkillInstallCommand: string;
7
+ export declare const spacefastInstallCurlCommand: string;
8
+ export declare const spacefastInstallPowerShellCommand: string;
9
+ export declare const spacefastClaudePluginAddCommand = "claude plugin marketplace add spacefast/plugins";
10
+ export declare const spacefastClaudePluginInstallCommand = "claude plugin install spacefast@spacefast";
11
+ export declare const spacefastCodexSkillInstallCommand = "npx skills add spacefast/plugins --skill spacefast -g";
12
+ export declare const spacefastAgentSetupPrompt: string;
@@ -0,0 +1,18 @@
1
+ import { BRAND } from "../brand.js";
2
+ export const spacefastSkillName = "spacefast";
3
+ export const spacefastSkillRepoOwner = "spacefast";
4
+ export const spacefastSkillRepoName = "plugins";
5
+ export const spacefastSkillRepoRef = `${spacefastSkillRepoOwner}/${spacefastSkillRepoName}`;
6
+ export const spacefastSkillUrl = `${BRAND.apiOrigin}/skill`;
7
+ export const spacefastSkillInstallCommand = `curl -fsSL ${spacefastSkillUrl}`;
8
+ export const spacefastInstallCurlCommand = `curl -fsSL ${BRAND.websiteOrigin}/install.sh | bash`;
9
+ export const spacefastInstallPowerShellCommand = `irm ${BRAND.websiteOrigin}/install.ps1 | iex`;
10
+ export const spacefastClaudePluginAddCommand = `claude plugin marketplace add ${spacefastSkillRepoRef}`;
11
+ export const spacefastClaudePluginInstallCommand = `claude plugin install ${spacefastSkillName}@${spacefastSkillName}`;
12
+ export const spacefastCodexSkillInstallCommand = `npx skills add ${spacefastSkillRepoRef} --skill ${spacefastSkillName} -g`;
13
+ export const spacefastAgentSetupPrompt = `I'd like you to set up ${BRAND.websiteOrigin}, the static host for AI agents.
14
+
15
+ Install the ${BRAND.productName} CLI with: ${spacefastInstallCurlCommand}
16
+ On Windows PowerShell, use: ${spacefastInstallPowerShellCommand}
17
+
18
+ If your environment supports manual skill import, fetch the official ${BRAND.productName} skill from: ${spacefastSkillUrl}`;
@@ -0,0 +1,108 @@
1
+ import { z } from "zod";
2
+ export type DomainSuggestionSource = "exact" | "tld" | "hack" | "variant" | "ai";
3
+ export type DomainSuggestionCategory = "best" | "fast" | "creative" | "ideas" | "short" | "classic" | "all";
4
+ export type DomainSearchMode = "suggestions" | "short" | "all";
5
+ export type DomainSuggestion = {
6
+ domain: string;
7
+ label: string;
8
+ sld: string;
9
+ tld: string;
10
+ source: DomainSuggestionSource;
11
+ category: DomainSuggestionCategory;
12
+ score: number;
13
+ supportedForPurchase: boolean;
14
+ purchasable: boolean;
15
+ connectable: boolean;
16
+ reason: string;
17
+ };
18
+ export type DomainSuggestionCheck = {
19
+ available?: boolean | null;
20
+ purchasable?: boolean | null;
21
+ supportedForPurchase?: boolean | null;
22
+ };
23
+ export type DomainAvailabilityStatus = "available" | "available_elsewhere" | "taken" | "unknown" | "unsupported_tld";
24
+ export type DomainAvailabilitySource = "dsapi" | "rdap" | "local";
25
+ export type DomainAvailabilityPrice = {
26
+ amount: number;
27
+ formatted: string;
28
+ currency: "USD";
29
+ };
30
+ export type DomainAvailability = {
31
+ domain: string;
32
+ available: boolean | null;
33
+ supportedForPurchase: boolean;
34
+ purchasable: boolean;
35
+ connectable: boolean;
36
+ price: DomainAvailabilityPrice | null;
37
+ premium: boolean;
38
+ status: DomainAvailabilityStatus;
39
+ source: DomainAvailabilitySource;
40
+ checkedAt: string;
41
+ error: string | null;
42
+ };
43
+ export declare const whoisLookupResultSchema: z.ZodObject<{
44
+ domain: z.ZodString;
45
+ source: z.ZodEnum<{
46
+ rdap: "rdap";
47
+ whois: "whois";
48
+ }>;
49
+ status: z.ZodArray<z.ZodString>;
50
+ registrar: z.ZodNullable<z.ZodString>;
51
+ nameservers: z.ZodArray<z.ZodString>;
52
+ events: z.ZodArray<z.ZodObject<{
53
+ action: z.ZodString;
54
+ date: z.ZodString;
55
+ }, z.core.$strip>>;
56
+ raw: z.ZodUnknown;
57
+ }, z.core.$strip>;
58
+ export type WhoisLookupResult = z.infer<typeof whoisLookupResultSchema>;
59
+ export type DomainSuggestionRankingOptions = {
60
+ mode?: DomainSearchMode | undefined;
61
+ checks?: Record<string, DomainSuggestionCheck | undefined> | undefined;
62
+ showUnavailable?: boolean | undefined;
63
+ limit?: number | undefined;
64
+ stableOrder?: boolean | undefined;
65
+ };
66
+ export declare const DOMAIN_SEARCH_MODES: readonly ["suggestions", "short", "all"];
67
+ export declare const DOMAIN_AVAILABILITY_BATCH_LIMIT = 80;
68
+ export declare const PURCHASABLE_TLDS: readonly ["com", "net", "org", "blog", "art", "app", "dev", "io", "co", "ai", "me", "site", "online", "store", "xyz", "space", "studio", "design", "cloud", "tech", "agency", "digital", "social", "world", "live", "today", "works", "website", "email", "link", "page"];
69
+ export declare function isRegistrableZone(zone: string): boolean;
70
+ export declare const SEARCH_TLDS: string[];
71
+ export declare function domainSuggestionLimitForMode(mode: DomainSearchMode, fallback?: number): number;
72
+ export declare function domainParts(domain: string): {
73
+ sld: string;
74
+ tld: string;
75
+ };
76
+ export declare function isSupportedForDomainPurchase(tld: string): boolean;
77
+ export declare function isKnownDomainZone(tld: string): boolean;
78
+ export declare function domainSearchProfile(domain: string): {
79
+ domain: string;
80
+ normalized: string;
81
+ sld: string;
82
+ tld: string;
83
+ valid: boolean;
84
+ knownTld: boolean;
85
+ supportedForPurchase: boolean;
86
+ connectable: boolean;
87
+ };
88
+ export declare function domainAvailabilityStatus(input: {
89
+ available: boolean | null;
90
+ supportedForPurchase: boolean;
91
+ purchasable: boolean;
92
+ knownTld?: boolean | undefined;
93
+ }): DomainAvailabilityStatus;
94
+ export declare function localDomainAvailability(domain: string, input?: {
95
+ available?: boolean | null | undefined;
96
+ error?: string | null | undefined;
97
+ checkedAt?: string | undefined;
98
+ }): DomainAvailability;
99
+ export declare function uniqDomainSuggestions(items: DomainSuggestion[]): DomainSuggestion[];
100
+ export declare function isShortDomainSuggestion(item: DomainSuggestion): boolean;
101
+ export declare function domainSuggestionMatchesMode(item: DomainSuggestion, mode: DomainSearchMode): boolean;
102
+ export declare function rankDomainSuggestions(items: DomainSuggestion[], { mode, checks, showUnavailable, limit, stableOrder, }?: DomainSuggestionRankingOptions): DomainSuggestion[];
103
+ export declare function buildBrowserDomainSuggestions(query: string, { mode, limit, aiLimit, checks, showUnavailable, }?: DomainSuggestionRankingOptions & {
104
+ aiLimit?: number;
105
+ }): DomainSuggestion[];
106
+ export declare function buildAllTldDomainSuggestions(query: string): DomainSuggestion[];
107
+ export declare function buildDomainSuggestions(query: string, limit?: number): DomainSuggestion[];
108
+ export declare function buildAiStyleDomainSuggestions(query: string, limit?: number): DomainSuggestion[];
@@ -0,0 +1,488 @@
1
+ import { z } from "zod";
2
+ import { normalizeDomainQuery } from "./domain-normalize.js";
3
+ import { ALL_DOMAIN_ZONES } from "./domain-zones.js";
4
+ export const whoisLookupResultSchema = z.object({
5
+ domain: z.string(),
6
+ source: z.enum(["rdap", "whois"]),
7
+ status: z.array(z.string()),
8
+ registrar: z.string().nullable(),
9
+ nameservers: z.array(z.string()),
10
+ events: z.array(z.object({ action: z.string(), date: z.string() })),
11
+ raw: z.unknown(),
12
+ });
13
+ export const DOMAIN_SEARCH_MODES = ["suggestions", "short", "all"];
14
+ export const DOMAIN_AVAILABILITY_BATCH_LIMIT = 80;
15
+ export const PURCHASABLE_TLDS = [
16
+ "com",
17
+ "net",
18
+ "org",
19
+ "blog",
20
+ "art",
21
+ "app",
22
+ "dev",
23
+ "io",
24
+ "co",
25
+ "ai",
26
+ "me",
27
+ "site",
28
+ "online",
29
+ "store",
30
+ "xyz",
31
+ "space",
32
+ "studio",
33
+ "design",
34
+ "cloud",
35
+ "tech",
36
+ "agency",
37
+ "digital",
38
+ "social",
39
+ "world",
40
+ "live",
41
+ "today",
42
+ "works",
43
+ "website",
44
+ "email",
45
+ "link",
46
+ "page",
47
+ ];
48
+ const DOMAINR_BASE_ZONES = [
49
+ "com",
50
+ "net",
51
+ "org",
52
+ "co",
53
+ "io",
54
+ "us",
55
+ "de",
56
+ "at",
57
+ "nz",
58
+ "be",
59
+ "cl",
60
+ "se",
61
+ "si",
62
+ "ad",
63
+ "ею",
64
+ "ua",
65
+ "рф",
66
+ "co.us",
67
+ ];
68
+ const KEYWORD_ZONES = [
69
+ { words: ["ai", "agent", "model", "prompt"], zones: ["ai", "app", "tools"] },
70
+ { words: ["app", "mobile", "saas", "tool"], zones: ["app", "io", "dev"] },
71
+ { words: ["code", "dev", "api", "cloud"], zones: ["dev", "cloud", "systems"] },
72
+ { words: ["studio", "brand", "design"], zones: ["studio", "design", "agency"] },
73
+ { words: ["shop", "store", "market"], zones: ["shop", "store", "co"] },
74
+ { words: ["coffee", "tea", "roast", "cafe"], zones: ["coffee", "cafe", "shop"] },
75
+ { words: ["video", "audio", "radio", "podcast"], zones: ["tv", "fm", "media"] },
76
+ { words: ["finance", "fund", "bank"], zones: ["finance", "capital", "co"] },
77
+ { words: ["game", "games", "guild"], zones: ["games", "gg", "app"] },
78
+ ];
79
+ function unique(values) {
80
+ return [...new Set(values)];
81
+ }
82
+ function zonePriority(zone) {
83
+ const domainrIndex = DOMAINR_BASE_ZONES.indexOf(zone);
84
+ if (domainrIndex >= 0) {
85
+ return domainrIndex;
86
+ }
87
+ const purchasableIndex = PURCHASABLE_TLDS.indexOf(zone);
88
+ if (purchasableIndex >= 0) {
89
+ return 100 + purchasableIndex;
90
+ }
91
+ return 1_000;
92
+ }
93
+ // The raw zone list contains reverse-DNS (`*.in-addr.arpa`), numeric, and
94
+ // punycode-less IDN entries (`001.рус`) that are never something a person
95
+ // registers. Keep only ASCII zones of one or two letter-initial labels.
96
+ const REGISTRABLE_LABEL = /^[a-z][a-z0-9-]*$/;
97
+ export function isRegistrableZone(zone) {
98
+ if (zone.includes("arpa")) {
99
+ return false;
100
+ }
101
+ const labels = zone.split(".");
102
+ if (labels.length < 1 || labels.length > 2) {
103
+ return false;
104
+ }
105
+ const tld = labels[labels.length - 1] ?? "";
106
+ return tld.length >= 2 && labels.every((label) => REGISTRABLE_LABEL.test(label));
107
+ }
108
+ export const SEARCH_TLDS = unique([
109
+ ...DOMAINR_BASE_ZONES,
110
+ ...PURCHASABLE_TLDS,
111
+ ...ALL_DOMAIN_ZONES,
112
+ ])
113
+ .filter(isRegistrableZone)
114
+ .toSorted((left, right) => zonePriority(left) - zonePriority(right) || left.localeCompare(right));
115
+ export function domainSuggestionLimitForMode(mode, fallback = 160) {
116
+ return mode === "all" ? SEARCH_TLDS.length + 1_000 : fallback;
117
+ }
118
+ const HACK_TLDS = new Set([
119
+ "ai",
120
+ "am",
121
+ "art",
122
+ "at",
123
+ "be",
124
+ "bio",
125
+ "co",
126
+ "de",
127
+ "fm",
128
+ "gg",
129
+ "io",
130
+ "is",
131
+ "it",
132
+ "me",
133
+ "so",
134
+ "to",
135
+ "tv",
136
+ "us",
137
+ ]);
138
+ const purchaseSupportedSet = new Set(PURCHASABLE_TLDS);
139
+ const zoneSet = new Set(SEARCH_TLDS);
140
+ const matchableHackZones = SEARCH_TLDS.filter((zone) => !zone.includes(".") &&
141
+ !zone.includes("-") &&
142
+ !zone.endsWith("arpa") &&
143
+ zone.length >= 2 &&
144
+ zone.length <= 12).toSorted((left, right) => right.length - left.length || left.localeCompare(right));
145
+ export function domainParts(domain) {
146
+ const normalized = normalizeDomainQuery(domain);
147
+ const labels = normalized.split(".").filter(Boolean);
148
+ if (labels.length < 2) {
149
+ return { sld: normalized.replace(/\./g, ""), tld: "" };
150
+ }
151
+ for (let index = 0; index < labels.length; index += 1) {
152
+ const zone = labels.slice(index).join(".");
153
+ if (!zoneSet.has(zone)) {
154
+ continue;
155
+ }
156
+ return {
157
+ sld: labels.slice(0, index).join("."),
158
+ tld: zone,
159
+ };
160
+ }
161
+ return {
162
+ sld: labels.slice(0, -1).join("."),
163
+ tld: labels[labels.length - 1] ?? "",
164
+ };
165
+ }
166
+ export function isSupportedForDomainPurchase(tld) {
167
+ return purchaseSupportedSet.has(tld.toLowerCase().replace(/^\./, ""));
168
+ }
169
+ export function isKnownDomainZone(tld) {
170
+ return zoneSet.has(tld.toLowerCase().replace(/^\./, ""));
171
+ }
172
+ export function domainSearchProfile(domain) {
173
+ const normalized = normalizeDomainQuery(domain);
174
+ const { sld, tld } = domainParts(normalized);
175
+ const valid = Boolean(sld && tld && sld.length <= 63 && normalized.length <= 253);
176
+ const supportedForPurchase = valid && isSupportedForDomainPurchase(tld);
177
+ return {
178
+ domain: valid ? `${sld}.${tld}` : normalized,
179
+ normalized,
180
+ sld,
181
+ tld,
182
+ valid,
183
+ knownTld: Boolean(tld && isKnownDomainZone(tld)),
184
+ supportedForPurchase,
185
+ connectable: valid && Boolean(tld && isKnownDomainZone(tld)),
186
+ };
187
+ }
188
+ export function domainAvailabilityStatus(input) {
189
+ if (input.available === true) {
190
+ return input.purchasable
191
+ ? "available"
192
+ : input.supportedForPurchase
193
+ ? "unknown"
194
+ : "available_elsewhere";
195
+ }
196
+ if (input.available === false) {
197
+ return "taken";
198
+ }
199
+ return input.supportedForPurchase || input.knownTld !== false ? "unknown" : "unsupported_tld";
200
+ }
201
+ export function localDomainAvailability(domain, input = {}) {
202
+ const profile = domainSearchProfile(domain);
203
+ const available = input.available ?? null;
204
+ const purchasable = false;
205
+ return {
206
+ domain: profile.domain,
207
+ available,
208
+ supportedForPurchase: profile.supportedForPurchase,
209
+ purchasable,
210
+ connectable: profile.connectable,
211
+ price: null,
212
+ premium: false,
213
+ status: domainAvailabilityStatus({
214
+ available,
215
+ supportedForPurchase: profile.supportedForPurchase,
216
+ purchasable,
217
+ knownTld: profile.knownTld,
218
+ }),
219
+ source: "local",
220
+ checkedAt: input.checkedAt ?? new Date().toISOString(),
221
+ error: input.error ?? null,
222
+ };
223
+ }
224
+ function zonesForTokens(tokens) {
225
+ const zones = [];
226
+ for (const token of tokens) {
227
+ for (const group of KEYWORD_ZONES) {
228
+ if (group.words.includes(token)) {
229
+ zones.push(...group.zones);
230
+ }
231
+ }
232
+ }
233
+ return unique(zones);
234
+ }
235
+ function domainHacks(values, base) {
236
+ const hacks = new Set();
237
+ for (const value of values) {
238
+ for (const zone of matchableHackZones) {
239
+ let offset = value.indexOf(zone);
240
+ while (offset >= 0) {
241
+ const left = value.slice(0, offset).replace(/^-+|-+$/g, "");
242
+ if (left.length > 0) {
243
+ hacks.add(`${left}.${zone}`);
244
+ }
245
+ offset = value.indexOf(zone, offset + 1);
246
+ }
247
+ }
248
+ }
249
+ return [...hacks].toSorted((left, right) => hackWeight(left, base) - hackWeight(right, base) || left.localeCompare(right));
250
+ }
251
+ function hackWeight(domain, base) {
252
+ const leftLength = domain.split(".")[0]?.length ?? 0;
253
+ const exact = domain.replace(".", "") === base;
254
+ return (exact ? 0 : 18) + Math.max(0, leftLength - 4);
255
+ }
256
+ export function uniqDomainSuggestions(items) {
257
+ const seen = new Set();
258
+ return items.filter((item) => {
259
+ if (seen.has(item.domain)) {
260
+ return false;
261
+ }
262
+ seen.add(item.domain);
263
+ return true;
264
+ });
265
+ }
266
+ export function isShortDomainSuggestion(item) {
267
+ return item.source === "hack" || item.sld.replace(/-/g, "").length <= 8;
268
+ }
269
+ export function domainSuggestionMatchesMode(item, mode) {
270
+ if (mode === "short") {
271
+ // Hacks and compact names regardless of where they came from — AI ideas
272
+ // that happen to be short belong here too.
273
+ return isShortDomainSuggestion(item);
274
+ }
275
+ return true;
276
+ }
277
+ export function rankDomainSuggestions(items, { mode = "suggestions", checks = {}, showUnavailable = true, limit, stableOrder = false, } = {}) {
278
+ const ranked = uniqDomainSuggestions(items)
279
+ .toSorted((left, right) => {
280
+ // Score (then domain) is a pure function of the query, so this order is
281
+ // fully stable across availability updates.
282
+ const byScore = right.score - left.score || left.domain.localeCompare(right.domain);
283
+ if (stableOrder) {
284
+ return byScore;
285
+ }
286
+ const leftCheck = checks[left.domain];
287
+ const rightCheck = checks[right.domain];
288
+ const availabilityBoost = (rightCheck?.available === true ? 100 : 0) - (leftCheck?.available === true ? 100 : 0);
289
+ const purchasableBoost = (rightCheck?.purchasable || rightCheck?.supportedForPurchase ? 35 : 0) -
290
+ (leftCheck?.purchasable || leftCheck?.supportedForPurchase ? 35 : 0);
291
+ return availabilityBoost + purchasableBoost || byScore;
292
+ })
293
+ .filter((item) => {
294
+ const check = checks[item.domain];
295
+ if (!showUnavailable && check?.available === false) {
296
+ return false;
297
+ }
298
+ return domainSuggestionMatchesMode(item, mode);
299
+ });
300
+ return typeof limit === "number" ? ranked.slice(0, limit) : ranked;
301
+ }
302
+ export function buildBrowserDomainSuggestions(query, { mode = "suggestions", limit = domainSuggestionLimitForMode(mode), aiLimit = 24, checks, showUnavailable, } = {}) {
303
+ if (mode === "all") {
304
+ return rankDomainSuggestions(buildAllTldDomainSuggestions(query), {
305
+ mode,
306
+ checks,
307
+ showUnavailable,
308
+ limit: undefined,
309
+ });
310
+ }
311
+ const local = buildDomainSuggestions(query, domainSuggestionLimitForMode(mode, limit));
312
+ const ideas = buildAiStyleDomainSuggestions(query, aiLimit);
313
+ return rankDomainSuggestions([...local, ...ideas], {
314
+ mode,
315
+ checks,
316
+ showUnavailable,
317
+ limit,
318
+ });
319
+ }
320
+ export function buildAllTldDomainSuggestions(query) {
321
+ const normalized = normalizeDomainQuery(query);
322
+ if (!normalized) {
323
+ return [];
324
+ }
325
+ const requested = normalized.includes(".") ? domainParts(normalized) : null;
326
+ const sld = (requested?.sld || normalized).replace(/\./g, "").replace(/^-+|-+$/g, "");
327
+ if (!sld || sld.length > 63) {
328
+ return [];
329
+ }
330
+ return SEARCH_TLDS.map((tld, index) => {
331
+ const domain = `${sld}.${tld}`;
332
+ const supportedForPurchase = isSupportedForDomainPurchase(tld);
333
+ const classicBoost = ["com", "net", "org"].includes(tld) ? 16 : 0;
334
+ const tldBoost = supportedForPurchase ? 18 : 0;
335
+ return {
336
+ domain,
337
+ label: domain,
338
+ sld,
339
+ tld,
340
+ source: "tld",
341
+ category: "all",
342
+ score: Math.round(120 + tldBoost + classicBoost - index * 0.05),
343
+ supportedForPurchase,
344
+ purchasable: supportedForPurchase,
345
+ connectable: true,
346
+ reason: `Direct match on .${tld}.`,
347
+ };
348
+ });
349
+ }
350
+ export function buildDomainSuggestions(query, limit = 96) {
351
+ const normalized = normalizeDomainQuery(query);
352
+ if (!normalized) {
353
+ return [];
354
+ }
355
+ const requested = normalized.includes(".") ? domainParts(normalized) : null;
356
+ const base = requested?.sld || normalized.replace(/\./g, "");
357
+ const compact = base.replace(/-/g, "");
358
+ const variants = new Set([base, compact]);
359
+ const tokens = base.split("-").filter(Boolean);
360
+ if (tokens.length > 1) {
361
+ variants.add(tokens.join(""));
362
+ variants.add(tokens.join("-"));
363
+ variants.add(tokens.toReversed().join(""));
364
+ }
365
+ if (compact.length > 4) {
366
+ variants.add(`${compact}hq`);
367
+ variants.add(`get${compact}`);
368
+ variants.add(`${compact}app`);
369
+ variants.add(`${compact}labs`);
370
+ }
371
+ const suggestions = new Map();
372
+ const add = (sld, tld, source, reason, index) => {
373
+ if (!sld || !tld || sld.length > 63) {
374
+ return;
375
+ }
376
+ const domain = `${sld}.${tld}`;
377
+ if (suggestions.has(domain)) {
378
+ return;
379
+ }
380
+ const exact = source === "exact" || (sld === compact && tld === requested?.tld);
381
+ const short = sld.length <= 8;
382
+ const supportedForPurchase = isSupportedForDomainPurchase(tld);
383
+ const purchasable = supportedForPurchase;
384
+ const category = exact
385
+ ? "best"
386
+ : short
387
+ ? "short"
388
+ : HACK_TLDS.has(tld)
389
+ ? "creative"
390
+ : supportedForPurchase
391
+ ? "fast"
392
+ : "all";
393
+ const tldBoost = supportedForPurchase ? 18 : 0;
394
+ const classicBoost = ["com", "net", "org"].includes(tld) ? 16 : 0;
395
+ const hackBoost = HACK_TLDS.has(tld) ? 9 : 0;
396
+ const lengthPenalty = Math.max(0, sld.length - 10) * 1.4;
397
+ const baseScore = source === "exact" ? 180 : source === "hack" ? 126 : 100;
398
+ const score = Math.round(baseScore + tldBoost + classicBoost + hackBoost - index * 0.75 - lengthPenalty);
399
+ suggestions.set(domain, {
400
+ domain,
401
+ label: domain,
402
+ sld,
403
+ tld,
404
+ source,
405
+ category,
406
+ score,
407
+ supportedForPurchase,
408
+ purchasable,
409
+ connectable: true,
410
+ reason,
411
+ });
412
+ };
413
+ if (requested?.tld) {
414
+ add(requested.sld, requested.tld, "exact", "The exact domain you typed.", 0);
415
+ }
416
+ SEARCH_TLDS.forEach((tld, index) => add(compact, tld, "tld", `Direct match on .${tld}.`, index));
417
+ for (const hack of domainHacks([...variants], compact)) {
418
+ const parts = domainParts(hack);
419
+ add(parts.sld, parts.tld, "hack", `Domain hack using .${parts.tld}.`, hackWeight(hack, compact));
420
+ }
421
+ for (const zone of zonesForTokens(tokens)) {
422
+ add(compact, zone, "variant", "Keyword-matched extension.", 14);
423
+ }
424
+ let variantIndex = 0;
425
+ for (const variant of variants) {
426
+ for (const tld of ["com", "blog", "app", "dev", "io", "co", "ai", "studio", "site"]) {
427
+ add(variant, tld, "variant", "Name variation.", 18 + variantIndex++);
428
+ }
429
+ }
430
+ return Array.from(suggestions.values())
431
+ .toSorted((left, right) => right.score - left.score || left.domain.localeCompare(right.domain))
432
+ .slice(0, limit);
433
+ }
434
+ export function buildAiStyleDomainSuggestions(query, limit = 24) {
435
+ const normalized = normalizeDomainQuery(query);
436
+ const base = domainParts(normalized).sld || normalized;
437
+ if (!base) {
438
+ return [];
439
+ }
440
+ const compact = base.replace(/-/g, "");
441
+ const stems = [
442
+ `${compact}lab`,
443
+ `${compact}works`,
444
+ `use${compact}`,
445
+ `try${compact}`,
446
+ `${compact}stack`,
447
+ `${compact}base`,
448
+ `${compact}grid`,
449
+ `${compact}signal`,
450
+ `${compact}index`,
451
+ `${compact}run`,
452
+ ];
453
+ const tlds = ["com", "ai", "app", "dev", "io", "co", "studio", "world", "tools", "systems"];
454
+ const items = [];
455
+ // Score with the same components as buildDomainSuggestions so ideas
456
+ // interleave by merit in the merged "suggestions" view: base 96 puts the
457
+ // best idea (96 + 18 + 16 = 130) just under the direct .com match (134) and
458
+ // well under the exact query (180), while per-stem/per-tld decay spreads
459
+ // the tail through the TLD sweep instead of forming a separate block.
460
+ stems.forEach((stem, stemIndex) => {
461
+ tlds.forEach((tld, tldIndex) => {
462
+ const domain = `${stem}.${tld}`;
463
+ const supportedForPurchase = isSupportedForDomainPurchase(tld);
464
+ const tldBoost = supportedForPurchase ? 18 : 0;
465
+ const classicBoost = ["com", "net", "org"].includes(tld) ? 16 : 0;
466
+ const lengthPenalty = Math.max(0, stem.length - 10) * 1.4;
467
+ items.push({
468
+ domain,
469
+ label: domain,
470
+ sld: stem,
471
+ tld,
472
+ source: "ai",
473
+ category: "ideas",
474
+ score: Math.round(96 + tldBoost + classicBoost - stemIndex * 4 - tldIndex * 1.5 - lengthPenalty),
475
+ supportedForPurchase,
476
+ purchasable: supportedForPurchase,
477
+ connectable: true,
478
+ reason: "Generated name idea.",
479
+ });
480
+ });
481
+ });
482
+ // Scores are not monotonic with generation order (a later stem's .com beats
483
+ // an earlier stem's long-tail TLD), so build every stem × tld combination
484
+ // and cut by score — never by generation order — when applying the limit.
485
+ return items
486
+ .toSorted((left, right) => right.score - left.score || left.domain.localeCompare(right.domain))
487
+ .slice(0, limit);
488
+ }
@@ -0,0 +1 @@
1
+ export declare function normalizeDomainQuery(query: string): string;
@@ -0,0 +1,14 @@
1
+ export function normalizeDomainQuery(query) {
2
+ const value = query
3
+ .trim()
4
+ .toLowerCase()
5
+ .normalize("NFKD")
6
+ .replace(/[\u0300-\u036f]/g, "")
7
+ .replace(/^https?:\/\//, "")
8
+ .replace(/^www\./, "")
9
+ .split(/[/?#]/)[0] ?? "";
10
+ return value
11
+ .replace(/[^\p{L}\p{N}.-]+/gu, "-")
12
+ .replace(/^-+|-+$/g, "")
13
+ .replace(/-{2,}/g, "-");
14
+ }