@spacefast/common 0.0.5 → 0.0.6

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 (150) hide show
  1. package/dist/agents/connect-targets.d.ts +133 -0
  2. package/dist/agents/connect-targets.js +224 -0
  3. package/dist/brand.d.ts +1 -0
  4. package/dist/brand.js +5 -0
  5. package/dist/config/domains.d.ts +12 -6
  6. package/dist/config/domains.js +30 -38
  7. package/dist/contracts/access.d.ts +825 -125
  8. package/dist/contracts/access.js +771 -148
  9. package/dist/contracts/activity.d.ts +1 -1
  10. package/dist/contracts/activity.js +28 -2
  11. package/dist/contracts/annotations.d.ts +2 -4
  12. package/dist/contracts/api-keys.d.ts +3 -4
  13. package/dist/contracts/api-keys.js +9 -6
  14. package/dist/contracts/archives.d.ts +8 -2
  15. package/dist/contracts/archives.js +11 -4
  16. package/dist/contracts/billing.d.ts +0 -9
  17. package/dist/contracts/builds.d.ts +25 -21
  18. package/dist/contracts/builds.js +12 -0
  19. package/dist/contracts/common.d.ts +2 -0
  20. package/dist/contracts/common.js +4 -0
  21. package/dist/contracts/continuation.d.ts +0 -3
  22. package/dist/contracts/countries.d.ts +2 -0
  23. package/dist/contracts/countries.js +261 -0
  24. package/dist/contracts/deployments.d.ts +34 -34
  25. package/dist/contracts/device-auth.d.ts +4 -11
  26. package/dist/contracts/device-auth.js +4 -0
  27. package/dist/contracts/docs.d.ts +119 -0
  28. package/dist/contracts/docs.js +51 -0
  29. package/dist/contracts/domains.d.ts +96 -54
  30. package/dist/contracts/domains.js +26 -4
  31. package/dist/contracts/enums.d.ts +15 -16
  32. package/dist/contracts/enums.js +25 -14
  33. package/dist/contracts/error-code-meta.d.ts +72 -0
  34. package/dist/contracts/error-code-meta.js +96 -10
  35. package/dist/contracts/error-codes.d.ts +1 -1
  36. package/dist/contracts/error-codes.js +14 -0
  37. package/dist/contracts/events.d.ts +1 -1
  38. package/dist/contracts/features.d.ts +76 -6
  39. package/dist/contracts/features.js +178 -13
  40. package/dist/contracts/git.d.ts +2 -8
  41. package/dist/contracts/ids.d.ts +5 -0
  42. package/dist/contracts/ids.js +5 -0
  43. package/dist/contracts/mcp.d.ts +8 -10
  44. package/dist/contracts/me.js +1 -1
  45. package/dist/contracts/oauth-resources.d.ts +19 -8
  46. package/dist/contracts/oauth-resources.js +44 -13
  47. package/dist/contracts/platform.d.ts +0 -102
  48. package/dist/contracts/platform.js +0 -50
  49. package/dist/contracts/push-new.d.ts +4 -4
  50. package/dist/contracts/quotas.d.ts +2 -0
  51. package/dist/contracts/quotas.js +8 -0
  52. package/dist/contracts/repository-connections.d.ts +6 -8
  53. package/dist/contracts/resources.d.ts +97 -13
  54. package/dist/contracts/resources.js +26 -2
  55. package/dist/contracts/routes.d.ts +0 -2
  56. package/dist/contracts/runtime-api.d.ts +361 -39
  57. package/dist/contracts/runtime-api.js +275 -6
  58. package/dist/contracts/sites.d.ts +19 -2
  59. package/dist/contracts/sites.js +21 -0
  60. package/dist/contracts/space-config.d.ts +201 -2
  61. package/dist/contracts/space-config.js +135 -5
  62. package/dist/contracts/spaces.d.ts +364 -193
  63. package/dist/contracts/spaces.js +40 -8
  64. package/dist/contracts/superadmin-emails.d.ts +4 -2
  65. package/dist/contracts/superadmin-emails.js +8 -0
  66. package/dist/contracts/superadmin-queues.d.ts +26 -26
  67. package/dist/contracts/superadmin-spaces.d.ts +170 -42
  68. package/dist/contracts/superadmin.d.ts +6 -93
  69. package/dist/contracts/superadmin.js +6 -14
  70. package/dist/contracts/tags.d.ts +10 -9
  71. package/dist/contracts/tags.js +2 -1
  72. package/dist/contracts/teams.d.ts +21 -22
  73. package/dist/contracts/teams.js +0 -1
  74. package/dist/contracts/theme-json.d.ts +30 -0
  75. package/dist/contracts/theme-json.js +48 -0
  76. package/dist/contracts/variables.d.ts +0 -2
  77. package/dist/contracts/variables.js +0 -5
  78. package/dist/contracts/webhooks.d.ts +2 -0
  79. package/dist/contracts/webhooks.js +1 -1
  80. package/dist/contracts/zero.d.ts +5 -10
  81. package/dist/docs/agent-prose.d.ts +43 -3
  82. package/dist/docs/agent-prose.js +146 -7
  83. package/dist/docs/agent-setup.js +45 -13
  84. package/dist/docs/agent-solutions.d.ts +5 -5
  85. package/dist/docs/agent-solutions.js +2 -2
  86. package/dist/docs/catalog.d.ts +650 -0
  87. package/dist/docs/catalog.js +758 -0
  88. package/dist/docs/error-docs.d.ts +0 -2
  89. package/dist/docs/error-docs.js +77 -35
  90. package/dist/docs/index-build.d.ts +17 -0
  91. package/dist/docs/index-build.js +24 -0
  92. package/dist/docs/search.d.ts +54 -0
  93. package/dist/docs/search.js +96 -0
  94. package/dist/docs/skill-distribution.d.ts +5 -3
  95. package/dist/docs/skill-distribution.js +8 -8
  96. package/dist/utils/access-match.d.ts +39 -0
  97. package/dist/utils/access-match.js +296 -0
  98. package/dist/utils/auth-redirect.d.ts +3 -1
  99. package/dist/utils/auth-redirect.js +21 -1
  100. package/dist/utils/build-settings.d.ts +6 -0
  101. package/dist/utils/build-settings.js +429 -46
  102. package/dist/utils/claim-token.d.ts +8 -0
  103. package/dist/utils/claim-token.js +23 -2
  104. package/dist/utils/content-type.d.ts +1 -0
  105. package/dist/utils/content-type.js +1 -1
  106. package/dist/utils/credential-policy.d.ts +54 -0
  107. package/dist/utils/{access-policy.js → credential-policy.js} +28 -2
  108. package/dist/utils/gate-theme.d.ts +61 -0
  109. package/dist/utils/gate-theme.js +217 -0
  110. package/dist/utils/git-repository.d.ts +0 -1
  111. package/dist/utils/git-repository.js +0 -3
  112. package/dist/utils/id-hints.d.ts +8 -0
  113. package/dist/utils/id-hints.js +61 -0
  114. package/dist/utils/local-space-state.d.ts +91 -0
  115. package/dist/utils/local-space-state.js +251 -0
  116. package/dist/utils/oauth-signed-query.d.ts +8 -0
  117. package/dist/utils/oauth-signed-query.js +26 -0
  118. package/dist/utils/publish-detection.d.ts +0 -8
  119. package/dist/utils/publish-detection.js +0 -93
  120. package/dist/utils/publish-policy.d.ts +6 -5
  121. package/dist/utils/publish-policy.js +71 -7
  122. package/dist/utils/query-keys.d.ts +15 -4
  123. package/dist/utils/query-keys.js +27 -4
  124. package/dist/utils/runtime-paths.d.ts +0 -1
  125. package/dist/utils/runtime-paths.js +0 -1
  126. package/dist/utils/runtime-upload-batch.d.ts +18 -0
  127. package/dist/utils/runtime-upload-batch.js +90 -0
  128. package/dist/utils/runtime-upload.d.ts +2 -0
  129. package/dist/utils/runtime-upload.js +5 -0
  130. package/dist/utils/space-config.d.ts +4 -0
  131. package/dist/utils/space-config.js +10 -1
  132. package/dist/utils/space-theme.d.ts +2 -0
  133. package/dist/utils/space-theme.js +9 -0
  134. package/dist/utils/static-runtime-policy.d.ts +0 -21
  135. package/dist/utils/static-runtime-policy.js +19 -103
  136. package/dist/utils/upload-session.d.ts +1 -1
  137. package/dist/utils/wpcom-auth-redirects.d.ts +32 -2
  138. package/dist/utils/wpcom-auth-redirects.js +26 -10
  139. package/dist/vocabulary.d.ts +130 -30
  140. package/dist/vocabulary.js +77 -36
  141. package/package.json +16 -1
  142. package/dist/contracts/account.d.ts +0 -28
  143. package/dist/contracts/account.js +0 -54
  144. package/dist/contracts/intercom.d.ts +0 -6
  145. package/dist/contracts/intercom.js +0 -5
  146. package/dist/contracts/telemetry.d.ts +0 -2
  147. package/dist/contracts/telemetry.js +0 -1
  148. package/dist/utils/access-policy.d.ts +0 -39
  149. package/dist/utils/error-display.d.ts +0 -7
  150. package/dist/utils/error-display.js +0 -42
@@ -0,0 +1,296 @@
1
+ // The ONE TS implementation of the runtime's grant-satisfaction kernel
2
+ // (internal-docs/access-plan.html X-9): glob-aware `grants ∩ requiredGrants`
3
+ // intersection plus signing-key grant-namespace filtering. The PHP evaluator
4
+ // (runtime/engine/runtime/access-rules.php `_stattic_unified_grants_intersect`
5
+ // / `_stattic_unified_filter_grants_by_namespace`) is the enforcer of record;
6
+ // this matcher exists so the cloud's authorize pre-flight (and, later,
7
+ // `access simulate`) never mints a token the runtime would reject.
8
+ //
9
+ // Parity is pinned by a shared JSON corpus (`access-match.fixtures.json`),
10
+ // asserted against this module in packages/common and against the PHP in the
11
+ // control plane's php-policy-parity test — the one sanctioned
12
+ // generated↔source drift-guard pattern. Do not change semantics here without
13
+ // extending the corpus.
14
+ // Drops every grant not prefixed by one of the signing key's namespaces. This
15
+ // bounds a leaked external key's blast radius to exactly the grant namespaces
16
+ // the owner pointed it at (and bounds the runtime-local pw: key to `pw:`).
17
+ export function filterGrantsByNamespace(grants, namespaces) {
18
+ const kept = [];
19
+ for (const grant of grants) {
20
+ if (typeof grant !== "string" || grant === "") {
21
+ continue;
22
+ }
23
+ for (const namespace of namespaces) {
24
+ if (typeof namespace === "string" && namespace !== "" && grant.startsWith(namespace)) {
25
+ kept.push(grant);
26
+ break;
27
+ }
28
+ }
29
+ }
30
+ return kept;
31
+ }
32
+ // `*` in a requiredGrant matches any run of characters (grants have no path
33
+ // structure to anchor on, unlike paths). Exact match when no `*` present.
34
+ export function grantGlobMatches(pattern, grant) {
35
+ if (!pattern.includes("*")) {
36
+ return pattern === grant;
37
+ }
38
+ const regex = pattern
39
+ .split("*")
40
+ .map((part) => part.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"))
41
+ .join(".*");
42
+ return new RegExp(`^${regex}$`).test(grant);
43
+ }
44
+ // Glob-aware intersection: a visitor passes when ANY verified concrete grant
45
+ // matches ANY requiredGrant. requiredGrants may carry `*` wildcards (e.g.
46
+ // "email:*@acme.com"); verified grants are concrete.
47
+ export function grantsIntersect(grants, requiredGrants) {
48
+ for (const required of requiredGrants) {
49
+ if (typeof required !== "string" || required === "") {
50
+ continue;
51
+ }
52
+ for (const grant of grants) {
53
+ if (typeof grant !== "string" || grant === "") {
54
+ continue;
55
+ }
56
+ if (grantGlobMatches(required, grant)) {
57
+ return true;
58
+ }
59
+ }
60
+ }
61
+ return false;
62
+ }
63
+ // The composed satisfaction test the authorize pre-flight runs: namespace-drop
64
+ // the visitor's grants by the signing key, then intersect with the rule's
65
+ // requiredGrants — exactly what the runtime does after verifying a signature.
66
+ export function grantsSatisfyRule(input) {
67
+ return grantsIntersect(filterGrantsByNamespace(input.grants, input.grantNamespaces), input.requiredGrants);
68
+ }
69
+ // ---------------------------------------------------------------------------
70
+ // Rule-match evaluation (the `match` half of the kernel)
71
+ // ---------------------------------------------------------------------------
72
+ //
73
+ // TS mirror of the PHP `_stattic_unified_rule_matches` /
74
+ // `_stattic_unified_path_glob_matches` / `_stattic_unified_ip_in_cidr` family
75
+ // (runtime/engine/runtime/access-rules.php). Powers `access simulate` and the
76
+ // authorize pre-flight's path scoping. Parity is pinned by the same shared
77
+ // corpus (`access-match.fixtures.json`, sections `pathGlob` + `cidr`) asserted
78
+ // against the PHP in php-policy-parity.test.ts.
79
+ // Mirrors `_spacefast_canonicalize_host`.
80
+ export function canonicalizeHost(host) {
81
+ return host.trim().toLowerCase().replace(/\.+$/, "");
82
+ }
83
+ export function hostGlobMatches(pattern, host) {
84
+ const regex = pattern
85
+ .split("*")
86
+ .map((part) => part.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"))
87
+ .join(".*");
88
+ return new RegExp(`^${regex}$`, "i").test(canonicalizeHost(host));
89
+ }
90
+ export function hostTemplateMatches(template, host) {
91
+ let regex = "";
92
+ let index = 0;
93
+ while (index < template.length) {
94
+ const char = template[index];
95
+ if (char === "*") {
96
+ regex += ".*";
97
+ index += 1;
98
+ continue;
99
+ }
100
+ if (char === ":") {
101
+ const name = template.slice(index + 1).match(/^[A-Za-z][A-Za-z0-9_]*/)?.[0] ?? "";
102
+ if (!name) {
103
+ return false;
104
+ }
105
+ regex += "[^.]+";
106
+ index += name.length + 1;
107
+ continue;
108
+ }
109
+ regex += char.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
110
+ index += 1;
111
+ }
112
+ return new RegExp(`^${regex}$`, "i").test(canonicalizeHost(host));
113
+ }
114
+ // Glob path matcher: "/docs/**" (any depth), "/docs/*" (one segment), exact
115
+ // otherwise. Empty pattern matches everything.
116
+ export function pathGlobMatches(pattern, path) {
117
+ if (pattern === "") {
118
+ return true;
119
+ }
120
+ if (!pattern.includes("*")) {
121
+ return pattern === path;
122
+ }
123
+ let regex = "";
124
+ let i = 0;
125
+ while (i < pattern.length) {
126
+ const char = pattern[i];
127
+ if (char === "*") {
128
+ if (pattern[i + 1] === "*") {
129
+ regex += ".*";
130
+ i += 2;
131
+ continue;
132
+ }
133
+ regex += "[^/]*";
134
+ i += 1;
135
+ continue;
136
+ }
137
+ regex += char.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
138
+ i += 1;
139
+ }
140
+ return new RegExp(`^${regex}$`).test(path);
141
+ }
142
+ // Case-insensitive substring match on the user agent; empty needle matches.
143
+ export function agentMatches(needle, agent) {
144
+ if (needle === "") {
145
+ return true;
146
+ }
147
+ return agent.toLowerCase().includes(needle.toLowerCase());
148
+ }
149
+ // inet_pton equivalent: dotted-quad IPv4 or RFC 4291 IPv6 (including "::"
150
+ // compression and an embedded IPv4 tail) → bytes, or null when unparseable.
151
+ function ipToBytes(ip) {
152
+ if (/^\d{1,3}(\.\d{1,3}){3}$/.test(ip)) {
153
+ const parts = ip.split(".").map(Number);
154
+ if (parts.some((part) => part > 255)) {
155
+ return null;
156
+ }
157
+ return Uint8Array.from(parts);
158
+ }
159
+ if (!ip.includes(":")) {
160
+ return null;
161
+ }
162
+ let head = ip;
163
+ let v4Tail = null;
164
+ const lastColon = ip.lastIndexOf(":");
165
+ const tail = ip.slice(lastColon + 1);
166
+ if (tail.includes(".")) {
167
+ const tailBytes = ipToBytes(tail);
168
+ if (!tailBytes || tailBytes.length !== 4) {
169
+ return null;
170
+ }
171
+ v4Tail = [...tailBytes];
172
+ head = ip.slice(0, lastColon + 1) + "0:0"; // placeholder groups, replaced below
173
+ }
174
+ const doubleColonSplits = head.split("::");
175
+ if (doubleColonSplits.length > 2) {
176
+ return null;
177
+ }
178
+ const parseGroups = (raw) => {
179
+ if (raw === "") {
180
+ return [];
181
+ }
182
+ const groups = [];
183
+ for (const part of raw.split(":")) {
184
+ if (!/^[0-9a-fA-F]{1,4}$/.test(part)) {
185
+ return null;
186
+ }
187
+ groups.push(Number.parseInt(part, 16));
188
+ }
189
+ return groups;
190
+ };
191
+ let groups;
192
+ if (doubleColonSplits.length === 2) {
193
+ const left = parseGroups(doubleColonSplits[0] ?? "");
194
+ const right = parseGroups(doubleColonSplits[1] ?? "");
195
+ if (left === null || right === null || left.length + right.length > 8) {
196
+ return null;
197
+ }
198
+ groups = [...left, ...Array(8 - left.length - right.length).fill(0), ...right];
199
+ }
200
+ else {
201
+ groups = parseGroups(head);
202
+ if (groups === null || groups.length !== 8) {
203
+ return null;
204
+ }
205
+ }
206
+ const bytes = new Uint8Array(16);
207
+ groups.forEach((group, index) => {
208
+ bytes[index * 2] = group >> 8;
209
+ bytes[index * 2 + 1] = group & 0xff;
210
+ });
211
+ if (v4Tail) {
212
+ bytes[12] = v4Tail[0];
213
+ bytes[13] = v4Tail[1];
214
+ bytes[14] = v4Tail[2];
215
+ bytes[15] = v4Tail[3];
216
+ }
217
+ return bytes;
218
+ }
219
+ // Mirrors `_stattic_unified_ip_in_cidr`: a bare address (no "/") is an exact
220
+ // string compare; mixed families never match; malformed input never matches.
221
+ export function ipInCidr(ip, cidr) {
222
+ if (cidr === "") {
223
+ return false;
224
+ }
225
+ if (!cidr.includes("/")) {
226
+ return ip === cidr;
227
+ }
228
+ const [subnet = "", bitsRaw = ""] = cidr.split("/", 2);
229
+ const ipBytes = ipToBytes(ip);
230
+ const subnetBytes = ipToBytes(subnet);
231
+ if (!ipBytes || !subnetBytes || ipBytes.length !== subnetBytes.length) {
232
+ return false;
233
+ }
234
+ if (!/^\d+$/.test(bitsRaw)) {
235
+ return false;
236
+ }
237
+ const bits = Number.parseInt(bitsRaw, 10);
238
+ const maxBits = ipBytes.length * 8;
239
+ if (bits < 0 || bits > maxBits) {
240
+ return false;
241
+ }
242
+ const fullBytes = Math.floor(bits / 8);
243
+ for (let index = 0; index < fullBytes; index += 1) {
244
+ if (ipBytes[index] !== subnetBytes[index]) {
245
+ return false;
246
+ }
247
+ }
248
+ const remainder = bits % 8;
249
+ if (remainder === 0) {
250
+ return true;
251
+ }
252
+ const mask = (0xff << (8 - remainder)) & 0xff;
253
+ return (ipBytes[fullBytes] & mask) === (subnetBytes[fullBytes] & mask);
254
+ }
255
+ export function ipInAnyCidr(ip, cidrs) {
256
+ if (ip === "") {
257
+ return false;
258
+ }
259
+ return cidrs.some((cidr) => typeof cidr === "string" && ipInCidr(ip, cidr));
260
+ }
261
+ // AND-combined match — mirrors `_stattic_unified_rule_matches` exactly. An
262
+ // empty match matches everything in scope.
263
+ export function ruleMatchApplies(match, context) {
264
+ if (match.host !== undefined && canonicalizeHost(match.host) !== canonicalizeHost(context.host)) {
265
+ return false;
266
+ }
267
+ if (match.hostPattern !== undefined && !hostGlobMatches(match.hostPattern, context.host)) {
268
+ return false;
269
+ }
270
+ if (match.hostTemplate !== undefined && !hostTemplateMatches(match.hostTemplate, context.host)) {
271
+ return false;
272
+ }
273
+ if (match.pathPattern !== undefined && !pathGlobMatches(match.pathPattern, context.path)) {
274
+ return false;
275
+ }
276
+ if (match.channel !== undefined && match.channel !== (context.channel ?? "")) {
277
+ return false;
278
+ }
279
+ if (match.ipCidrs !== undefined && !ipInAnyCidr(context.ip ?? "", match.ipCidrs)) {
280
+ return false;
281
+ }
282
+ if (match.agent !== undefined && !agentMatches(match.agent, context.agent ?? "")) {
283
+ return false;
284
+ }
285
+ if (match.country !== undefined && match.country.toUpperCase() !== (context.country ?? "")) {
286
+ return false;
287
+ }
288
+ if (match.header !== undefined) {
289
+ const name = match.header.name.toLowerCase();
290
+ const actual = Object.entries(context.headers ?? {}).find(([key]) => key.toLowerCase() === name)?.[1];
291
+ if (match.header.name === "" || (actual ?? "") !== match.header.value) {
292
+ return false;
293
+ }
294
+ }
295
+ return true;
296
+ }
@@ -1 +1,3 @@
1
- export declare function sanitizeDashboardReturnTo(returnTo: string | null | undefined): string | undefined;
1
+ export declare function sanitizeDashboardReturnTo(returnTo: string | null | undefined, options?: {
2
+ apiOrigin?: string | null;
3
+ }): string | undefined;
@@ -17,9 +17,29 @@ function hasUnsafeReturnToChars(value) {
17
17
  }
18
18
  return false;
19
19
  }
20
- export function sanitizeDashboardReturnTo(returnTo) {
20
+ // The ONE absolute-URL exception to the same-origin returnTo policy
21
+ // (access-plan X-41): the api-origin GET /v1/access/authorize URL exactly —
22
+ // the sign-in leg of the visitor redirect chain. Everything else absolute is
23
+ // an open-redirect surface and stays rejected.
24
+ const API_AUTHORIZE_PATH = "/v1/access/authorize";
25
+ export function sanitizeDashboardReturnTo(returnTo, options) {
21
26
  const normalized = returnTo?.trim();
22
27
  const decoded = normalized ? decodedReturnTo(normalized) : null;
28
+ if (normalized && decoded && options?.apiOrigin) {
29
+ try {
30
+ const candidate = new URL(normalized);
31
+ const apiOrigin = new URL(options.apiOrigin).origin;
32
+ if (candidate.origin === apiOrigin &&
33
+ candidate.pathname === API_AUTHORIZE_PATH &&
34
+ !hasUnsafeReturnToChars(normalized) &&
35
+ !hasUnsafeReturnToChars(decoded)) {
36
+ return candidate.toString();
37
+ }
38
+ }
39
+ catch {
40
+ // Not an absolute URL — fall through to the same-origin path rules.
41
+ }
42
+ }
23
43
  if (!normalized ||
24
44
  !decoded ||
25
45
  !normalized.startsWith("/") ||
@@ -46,4 +46,10 @@ export type PlatformConfig = {
46
46
  export declare function stringField(value: Record<string, unknown> | null, key: string): string | null;
47
47
  export declare function numberField(value: Record<string, unknown> | null, key: string): number | null;
48
48
  export declare function detectPlatformConfigs(input: Omit<BuildSettingsInput, "fileNames" | "packageJson">): PlatformConfig[];
49
+ export type PlatformRoutingConversion = {
50
+ redirectLines: string[];
51
+ headerBlocks: string[];
52
+ notes: string[];
53
+ };
54
+ export declare function convertPlatformRouting(configs: PlatformConfig[]): PlatformRoutingConversion;
49
55
  export declare function detectBuildSettings(input: BuildSettingsInput): DetectedBuildSettings;