@spacefast/common 0.0.23 → 0.0.26

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 (224) hide show
  1. package/brand-assets/LICENSE-Haskoy.txt +93 -0
  2. package/brand-assets/haskoy-latin-variable.woff2 +0 -0
  3. package/brand-assets/spacefast-favicon.svg +4 -4
  4. package/brand-assets/spacefast-sf-full-bleed.svg +3 -3
  5. package/brand-assets/spacefast-wordmark.svg +2 -4
  6. package/dist/agents/connect-targets.js +1 -1
  7. package/dist/agents/private-key-oauth.d.ts +0 -1
  8. package/dist/agents/private-key-oauth.js +46 -63
  9. package/dist/brand-assets-build.js +4 -6
  10. package/dist/brand-assets.d.ts +5 -15
  11. package/dist/brand-assets.js +15 -14
  12. package/dist/config/domains.d.ts +47 -7
  13. package/dist/config/domains.js +66 -10
  14. package/dist/config/index.d.ts +0 -1
  15. package/dist/config/index.js +0 -1
  16. package/dist/contracts/abuse.d.ts +2 -0
  17. package/dist/contracts/abuse.js +12 -0
  18. package/dist/contracts/access.d.ts +124 -12
  19. package/dist/contracts/access.js +73 -9
  20. package/dist/contracts/activity.d.ts +1 -1
  21. package/dist/contracts/activity.js +5 -12
  22. package/dist/contracts/api-keys.d.ts +21 -21
  23. package/dist/contracts/api-keys.js +2 -2
  24. package/dist/contracts/beta.js +1 -1
  25. package/dist/contracts/billing.d.ts +3 -35
  26. package/dist/contracts/billing.js +33 -52
  27. package/dist/contracts/builds.d.ts +30 -18
  28. package/dist/contracts/builds.js +28 -9
  29. package/dist/contracts/channels.js +1 -1
  30. package/dist/contracts/cli.d.ts +10 -0
  31. package/dist/contracts/cli.js +10 -0
  32. package/dist/contracts/collab-public.d.ts +63 -0
  33. package/dist/contracts/collab-public.js +46 -0
  34. package/dist/contracts/comments.d.ts +282 -53
  35. package/dist/contracts/comments.js +173 -52
  36. package/dist/contracts/common.d.ts +3 -3
  37. package/dist/contracts/common.js +5 -5
  38. package/dist/contracts/crons.d.ts +35 -0
  39. package/dist/contracts/crons.js +42 -0
  40. package/dist/contracts/device-auth.d.ts +6 -6
  41. package/dist/contracts/device-auth.js +2 -2
  42. package/dist/contracts/docs.js +2 -2
  43. package/dist/contracts/domains.d.ts +38 -27
  44. package/dist/contracts/domains.js +29 -2
  45. package/dist/contracts/enums.d.ts +34 -30
  46. package/dist/contracts/enums.js +62 -48
  47. package/dist/contracts/error-code-meta.d.ts +50 -125
  48. package/dist/contracts/error-code-meta.js +29 -41
  49. package/dist/contracts/error-codes.d.ts +1 -1
  50. package/dist/contracts/error-codes.js +14 -33
  51. package/dist/contracts/execution.d.ts +47 -66
  52. package/dist/contracts/execution.js +53 -49
  53. package/dist/contracts/feature-lifecycle-core.d.ts +0 -6
  54. package/dist/contracts/feature-lifecycle-core.js +2 -31
  55. package/dist/contracts/feature-lifecycle.d.ts +12 -52
  56. package/dist/contracts/feature-lifecycle.js +16 -59
  57. package/dist/contracts/features.d.ts +48 -60
  58. package/dist/contracts/features.js +59 -51
  59. package/dist/contracts/functions.d.ts +266 -72
  60. package/dist/contracts/functions.js +260 -37
  61. package/dist/contracts/git.d.ts +28 -34
  62. package/dist/contracts/git.js +10 -11
  63. package/dist/contracts/grants.d.ts +9 -5
  64. package/dist/contracts/grants.js +18 -9
  65. package/dist/contracts/ids.js +2 -2
  66. package/dist/contracts/internal.d.ts +8 -6
  67. package/dist/contracts/internal.js +12 -12
  68. package/dist/contracts/mcp.d.ts +11 -0
  69. package/dist/contracts/mcp.js +5 -0
  70. package/dist/contracts/me.js +1 -1
  71. package/dist/contracts/oauth-resources.d.ts +4 -3
  72. package/dist/contracts/oauth-resources.js +12 -3
  73. package/dist/contracts/operations.d.ts +90 -5
  74. package/dist/contracts/operations.js +77 -3
  75. package/dist/contracts/pages.d.ts +12 -7
  76. package/dist/contracts/pages.js +24 -1
  77. package/dist/contracts/plan-policy.d.ts +0 -2
  78. package/dist/contracts/plan-policy.js +2 -3
  79. package/dist/contracts/platform.d.ts +61 -148
  80. package/dist/contracts/platform.js +70 -126
  81. package/dist/contracts/publish-archive.d.ts +508 -0
  82. package/dist/contracts/publish-archive.js +32 -0
  83. package/dist/contracts/push-new.d.ts +4 -4
  84. package/dist/contracts/quotas.js +2 -2
  85. package/dist/contracts/repository-connections.d.ts +43 -30
  86. package/dist/contracts/repository-connections.js +11 -3
  87. package/dist/contracts/resources.d.ts +12 -24
  88. package/dist/contracts/resources.js +36 -16
  89. package/dist/contracts/runtime-api.d.ts +433 -192
  90. package/dist/contracts/runtime-api.js +421 -356
  91. package/dist/contracts/runtime-app.d.ts +127 -120
  92. package/dist/contracts/runtime-app.js +19 -10
  93. package/dist/contracts/runtime-db.d.ts +5 -5
  94. package/dist/contracts/runtime-db.js +2 -2
  95. package/dist/contracts/runtime-purge.d.ts +8 -0
  96. package/dist/contracts/runtime-purge.js +7 -0
  97. package/dist/contracts/runtime-services.d.ts +219 -0
  98. package/dist/contracts/runtime-services.js +437 -0
  99. package/dist/contracts/runtime-storage.d.ts +46 -32
  100. package/dist/contracts/runtime-storage.js +31 -8
  101. package/dist/contracts/sf-config-v1.d.ts +48 -10
  102. package/dist/contracts/space-config.d.ts +69 -18
  103. package/dist/contracts/space-config.js +226 -27
  104. package/dist/contracts/spaces.d.ts +236 -450
  105. package/dist/contracts/spaces.js +131 -151
  106. package/dist/contracts/superadmin-activity.d.ts +57 -4
  107. package/dist/contracts/superadmin-activity.js +48 -2
  108. package/dist/contracts/superadmin-search.d.ts +126 -0
  109. package/dist/contracts/superadmin-search.js +61 -0
  110. package/dist/contracts/superadmin-spaces.d.ts +361 -92
  111. package/dist/contracts/superadmin-spaces.js +97 -24
  112. package/dist/contracts/superadmin-teams.d.ts +28 -10
  113. package/dist/contracts/superadmin-teams.js +5 -8
  114. package/dist/contracts/superadmin-tenants.d.ts +0 -6
  115. package/dist/contracts/superadmin-tenants.js +1 -7
  116. package/dist/contracts/superadmin.d.ts +135 -54
  117. package/dist/contracts/superadmin.js +46 -23
  118. package/dist/contracts/tags.d.ts +12 -12
  119. package/dist/contracts/tags.js +3 -3
  120. package/dist/contracts/teams.d.ts +49 -14
  121. package/dist/contracts/teams.js +40 -22
  122. package/dist/contracts/transfers.d.ts +0 -49
  123. package/dist/contracts/transfers.js +4 -8
  124. package/dist/contracts/usage.d.ts +61 -0
  125. package/dist/contracts/usage.js +64 -0
  126. package/dist/contracts/variables.d.ts +2 -7
  127. package/dist/contracts/variables.js +9 -8
  128. package/dist/contracts/webhooks.d.ts +2 -2
  129. package/dist/contracts/zero.d.ts +102 -73
  130. package/dist/contracts/zero.js +94 -32
  131. package/dist/dashboard-paths/index.d.ts +1 -1
  132. package/dist/dashboard-paths/index.js +1 -0
  133. package/dist/docs/agent-prose.d.ts +2 -2
  134. package/dist/docs/agent-prose.js +63 -70
  135. package/dist/docs/agent-setup.js +45 -15
  136. package/dist/docs/catalog.d.ts +24 -24
  137. package/dist/docs/catalog.js +35 -24
  138. package/dist/docs/error-docs.js +194 -270
  139. package/dist/docs/skills.d.ts +12 -12
  140. package/dist/docs/skills.js +4 -4
  141. package/dist/test-helpers/fetch-stub.d.ts +16 -0
  142. package/dist/test-helpers/fetch-stub.js +25 -0
  143. package/dist/utils/build-settings.d.ts +5 -1
  144. package/dist/utils/build-settings.js +91 -13
  145. package/dist/utils/canonical-json.d.ts +10 -0
  146. package/dist/utils/canonical-json.js +22 -0
  147. package/dist/utils/cast-transport.d.ts +28 -0
  148. package/dist/utils/cast-transport.js +52 -0
  149. package/dist/utils/claim-token.d.ts +0 -8
  150. package/dist/utils/claim-token.js +3 -15
  151. package/dist/utils/concurrency.js +1 -0
  152. package/dist/utils/credential-policy.d.ts +1 -1
  153. package/dist/utils/credential-policy.js +4 -3
  154. package/dist/utils/dns-instructions.js +9 -9
  155. package/dist/utils/env-file.d.ts +17 -0
  156. package/dist/utils/env-file.js +96 -0
  157. package/dist/utils/error-code.d.ts +2 -0
  158. package/dist/utils/error-code.js +8 -0
  159. package/dist/utils/generate-space-name.d.ts +8 -10
  160. package/dist/utils/generate-space-name.js +206 -21
  161. package/dist/utils/grant-decision.fixtures.json +2938 -0
  162. package/dist/utils/grants.d.ts +2 -0
  163. package/dist/utils/grants.js +26 -1
  164. package/dist/utils/gravatar.d.ts +9 -0
  165. package/dist/utils/gravatar.js +31 -0
  166. package/dist/utils/idempotency.d.ts +6 -0
  167. package/dist/utils/idempotency.js +28 -0
  168. package/dist/utils/local-space-state.d.ts +9 -16
  169. package/dist/utils/local-space-state.js +20 -50
  170. package/dist/utils/one-shot-replay.d.ts +4 -1
  171. package/dist/utils/page-colors.d.ts +14 -12
  172. package/dist/utils/page-colors.js +15 -13
  173. package/dist/utils/page-fonts.d.ts +2 -2
  174. package/dist/utils/page-fonts.js +15 -4
  175. package/dist/utils/pages.d.ts +21 -3
  176. package/dist/utils/pages.js +42 -13
  177. package/dist/utils/publish-form-data.d.ts +12 -0
  178. package/dist/utils/publish-form-data.js +10 -0
  179. package/dist/utils/publish-policy.d.ts +27 -0
  180. package/dist/utils/publish-policy.fixtures.json +403 -0
  181. package/dist/utils/publish-policy.js +45 -20
  182. package/dist/utils/push-new.d.ts +7 -0
  183. package/dist/utils/push-new.js +7 -0
  184. package/dist/utils/query-keys.d.ts +4 -5
  185. package/dist/utils/query-keys.js +2 -3
  186. package/dist/utils/runtime-paths.d.ts +5 -0
  187. package/dist/utils/runtime-paths.js +5 -0
  188. package/dist/utils/runtime-upload-batch.d.ts +0 -5
  189. package/dist/utils/runtime-upload-batch.js +1 -27
  190. package/dist/utils/runtime-upload.d.ts +6 -11
  191. package/dist/utils/runtime-upload.js +10 -13
  192. package/dist/utils/secure-local-file.js +5 -16
  193. package/dist/utils/space-config.d.ts +1 -34
  194. package/dist/utils/space-config.js +8 -326
  195. package/dist/utils/static-runtime-policy.d.ts +9 -3
  196. package/dist/utils/static-runtime-policy.fixtures.json +38 -41
  197. package/dist/utils/static-runtime-policy.js +22 -56
  198. package/dist/utils/storage-policy.d.ts +1 -1
  199. package/dist/utils/storage-policy.js +3 -1
  200. package/dist/utils/structured-error.d.ts +14 -0
  201. package/dist/utils/structured-error.js +22 -0
  202. package/dist/utils/upload-session.d.ts +4 -1
  203. package/dist/utils/upload-session.js +33 -5
  204. package/dist/utils/version-path.js +13 -4
  205. package/dist/vocabulary.d.ts +13 -11
  206. package/dist/vocabulary.js +42 -28
  207. package/package.json +7 -17
  208. package/dist/config/postgres-budget.d.ts +0 -129
  209. package/dist/config/postgres-budget.js +0 -140
  210. package/dist/config/postgres-budget.json +0 -51
  211. package/dist/contracts/archives.d.ts +0 -331
  212. package/dist/contracts/archives.js +0 -120
  213. package/dist/contracts/deployments.d.ts +0 -574
  214. package/dist/contracts/deployments.js +0 -97
  215. package/dist/contracts/sites.d.ts +0 -416
  216. package/dist/contracts/sites.js +0 -168
  217. package/dist/contracts/superadmin-queues.d.ts +0 -414
  218. package/dist/contracts/superadmin-queues.js +0 -202
  219. package/dist/contracts/superadmin-runtime.d.ts +0 -27
  220. package/dist/contracts/superadmin-runtime.js +0 -38
  221. package/dist/index.d.ts +0 -1
  222. package/dist/index.js +0 -1
  223. package/dist/utils/api.d.ts +0 -4
  224. package/dist/utils/api.js +0 -13
@@ -2,7 +2,7 @@ import { BRAND } from "../brand.js";
2
2
  import { WORST_CASE_QUOTAS } from "../contracts/quotas.js";
3
3
  import { SPACE_CONFIG_PRIVATE_FILES } from "../contracts/space-config.js";
4
4
  import { ZERO_RUNTIME_DEPLOY_PATH } from "../contracts/zero.js";
5
- // The ONE static publish policy (internal-docs/platform.md "Upload Contract"):
5
+ // The ONE static publish policy (the platform spec "Upload Contract"):
6
6
  // path denials, manifest scale caps, plan caps, duplicate detection, and the
7
7
  // free-plan content rules live here. Consumed by API manifest validation, CLI
8
8
  // local preflight, inline archive extraction, and the runtime parity fixtures
@@ -55,24 +55,40 @@ const SENSITIVE_FILE_PATTERNS = [
55
55
  /\.(pem|key|p12|pfx)$/i,
56
56
  ];
57
57
  // Denied runtime private/control paths (spec "Static path policy").
58
- const STATIC_EXECUTION_CONTROL_FILES = new Set([".htaccess", ".user.ini"]);
58
+ // Every list below is generated verbatim into runtime/engine/admin/upload-policy.php
59
+ // by apps/control-plane/src/scripts/codegen-php-policy.ts, so the two enforcers
60
+ // cannot hold different literals. Lowercase only — both sides fold the path first.
61
+ export const STATIC_EXECUTION_CONTROL_FILES = [".htaccess", ".user.ini"];
59
62
  // Reserved runtime namespace segments are matched by PREFIX: the runtime owns
60
63
  // `/__spacefast/*` plus `__spacefast_generated/`, and the `__stattic` spelling
61
64
  // (`/__stattic_probe` today, more `/__stattic/*` routes planned) stays
62
65
  // reserved the same way. Mid-segment occurrences are ordinary content.
63
- const STATIC_RUNTIME_CONTROL_SEGMENT_PREFIXES = ["__spacefast", "__stattic"];
64
- // Compact platform endpoints live below `/__sf`. Reserve the complete root
65
- // segment prefix before the first endpoint ships so uploaded content can never
66
- // shadow a later platform route. Nested `__sf*` directories remain content.
67
- const STATIC_RUNTIME_ROOT_SEGMENT_PREFIXES = ["__sf"];
66
+ export const STATIC_RUNTIME_CONTROL_SEGMENT_PREFIXES = ["__spacefast", "__stattic"];
67
+ // Reserved as WHOLE root segments. In runtime/engine/shared/context.php the
68
+ // control table gives `/__sf` and `/__span` `match => 'namespace', fold => true`
69
+ // rows, and `_stattic_control_path_row()` reads `namespace` as `=== '/__sf' ||
70
+ // starts_with('/__sf/')`, so `/__sfx/...` and `/__spanish/page` are ordinary
71
+ // tenant paths. Reserving more than the front door claims would refuse a publish
72
+ // of content the front door then happily serves as tenant content.
73
+ //
74
+ // This is a SUBSET of what the control table reserves, not a mirror of it: the
75
+ // table also reserves the nested exact path `/__/collab` (the review link),
76
+ // while the `/__/` prefix around it is tenant-publishable. Publish deliberately
77
+ // does not claim that one — the reservation that matters is enforced at request
78
+ // time, where serve.php refuses to let either a published file or a routing rule
79
+ // answer a reserved route, and that refusal is what runtime/tests/collab-frame.test.ts
80
+ // exercises by publishing a file at `__/collab` on purpose. The control table is
81
+ // the authority; this list is a publish-time convenience, so keep it a subset
82
+ // and never let it contradict the table.
83
+ export const STATIC_RUNTIME_ROOT_SEGMENTS = ["__sf", "__span"];
68
84
  // Both the current and the legacy control-path spellings stay denied: spaces
69
85
  // published before the `.stattic` → `.spacefast` rename still serve from the
70
86
  // legacy on-disk layout, and user archives may carry either name.
71
- const STATIC_RUNTIME_CONTROL_PATHS = new Set([
87
+ export const STATIC_RUNTIME_CONTROL_PATHS = [
72
88
  ".well-known/spacefast-runtime",
73
89
  ".well-known/stattic-runtime",
74
- ]);
75
- const STATIC_RUNTIME_CONTROL_PREFIXES = [
90
+ ];
91
+ export const STATIC_RUNTIME_CONTROL_PREFIXES = [
76
92
  ".spacefast/storage",
77
93
  ".spacefast/engine",
78
94
  ".stattic/storage",
@@ -95,7 +111,14 @@ export function normalizePublishPath(relativePath) {
95
111
  * minted with, and a publish that could overwrite it would be a publish that
96
112
  * could mint its own tokens.
97
113
  */
98
- const PUBLISHABLE_BUILD_ARTIFACT_PREFIX = "__spacefast/functions/bundles/";
114
+ export const PUBLISHABLE_BUILD_ARTIFACT_PREFIX = "__spacefast/functions/bundles/";
115
+ /**
116
+ * The bundle carve-out's sibling for the Next-cache seed document: the same
117
+ * reasoning (travel through the ordinary manifest and upload path, stay
118
+ * unservable by default), the same deliberate narrowness (a prefix of its
119
+ * own, never the whole `functions/` directory — see the bundle comment).
120
+ */
121
+ export const PUBLISHABLE_SEED_ARTIFACT_PREFIX = "__spacefast/functions/seeds/";
99
122
  /**
100
123
  * The one FILE inside the control prefix a publish may write.
101
124
  *
@@ -111,6 +134,7 @@ const PUBLISHABLE_BUILD_ARTIFACT_PREFIX = "__spacefast/functions/bundles/";
111
134
  */
112
135
  function isPublishableBuildArtifactPath(normalizedPath, lowerPath) {
113
136
  return (lowerPath.startsWith(PUBLISHABLE_BUILD_ARTIFACT_PREFIX) ||
137
+ lowerPath.startsWith(PUBLISHABLE_SEED_ARTIFACT_PREFIX) ||
114
138
  normalizedPath === ZERO_RUNTIME_DEPLOY_PATH);
115
139
  }
116
140
  export function publishPathIgnored(relativePath) {
@@ -137,8 +161,9 @@ export function publishPathIsConvention(relativePath) {
137
161
  }
138
162
  // Provider runtime features that have no Spacefast equivalent (spec "Routing And
139
163
  // Headers", migration diagnostics): Cloudflare Pages `_worker.js`/`_routes.json`
140
- // and Pages/Netlify function directories publish as inert content with a
141
- // warning so migrations are never silent.
164
+ // and Netlify's provider-specific function directory publish as inert content
165
+ // with a warning so migrations are never silent. A root `functions/` directory
166
+ // is Spacefast's own file-router convention and must never reach this warning.
142
167
  function providerRuntimeFeatureForPath(path) {
143
168
  if (path === "_worker.js") {
144
169
  return "Cloudflare Pages _worker.js";
@@ -146,9 +171,6 @@ function providerRuntimeFeatureForPath(path) {
146
171
  if (path === "_routes.json") {
147
172
  return "Cloudflare Pages _routes.json";
148
173
  }
149
- if (path.startsWith("functions/")) {
150
- return "Pages Functions (functions/)";
151
- }
152
174
  if (path.startsWith("netlify/functions/")) {
153
175
  return "Netlify Functions (netlify/functions/)";
154
176
  }
@@ -196,7 +218,10 @@ export function staticUploadPathViolation(path, options = {}) {
196
218
  return pathViolation("invalid_file_path", "File path is invalid.", rawPath);
197
219
  }
198
220
  if (normalizedPath !== rawPath) {
199
- return pathViolation("invalid_file_path", "File path is invalid.", normalizedPath);
221
+ // The path the caller sent, not the normal form it missed: echoing a
222
+ // rewritten path suggests the rewrite was accepted, and it is the one place
223
+ // this policy used to report a different string than the runtime's copy.
224
+ return pathViolation("invalid_file_path", "File path is invalid.", rawPath);
200
225
  }
201
226
  if (new TextEncoder().encode(normalizedPath).length > MANIFEST_MAX_PATH_BYTES) {
202
227
  return pathViolation("path_too_long", `File paths support up to ${MANIFEST_MAX_PATH_BYTES} bytes in canonical form.`, normalizedPath);
@@ -208,7 +233,7 @@ export function staticUploadPathViolation(path, options = {}) {
208
233
  if (invalidSegment) {
209
234
  return pathViolation("invalid_file_path", "File path is invalid.", normalizedPath);
210
235
  }
211
- if (STATIC_EXECUTION_CONTROL_FILES.has(lowerName)) {
236
+ if (STATIC_EXECUTION_CONTROL_FILES.includes(lowerName)) {
212
237
  return pathViolation("static_control_file_not_supported", "Static deploys cannot upload execution-control files.", normalizedPath);
213
238
  }
214
239
  const reservedRuntimeControlFiles = options.reservedRuntimeControlFiles
@@ -218,7 +243,7 @@ export function staticUploadPathViolation(path, options = {}) {
218
243
  return pathViolation("static_runtime_control_path_not_supported", "Static deploys cannot upload runtime control paths.", normalizedPath);
219
244
  }
220
245
  const lowerRootSegment = segments[0]?.toLowerCase() ?? "";
221
- if (STATIC_RUNTIME_ROOT_SEGMENT_PREFIXES.some((prefix) => lowerRootSegment.startsWith(prefix))) {
246
+ if (STATIC_RUNTIME_ROOT_SEGMENTS.includes(lowerRootSegment)) {
222
247
  return pathViolation("static_runtime_control_path_not_supported", "Static deploys cannot upload runtime control paths.", normalizedPath);
223
248
  }
224
249
  const controlSegment = segments.find((segment) => {
@@ -231,7 +256,7 @@ export function staticUploadPathViolation(path, options = {}) {
231
256
  if (STATIC_RUNTIME_CONTROL_PREFIXES.some((prefix) => lowerPath === prefix || lowerPath.startsWith(`${prefix}/`))) {
232
257
  return pathViolation("static_runtime_control_path_not_supported", "Static deploys cannot upload runtime control paths.", normalizedPath);
233
258
  }
234
- if (STATIC_RUNTIME_CONTROL_PATHS.has(lowerPath)) {
259
+ if (STATIC_RUNTIME_CONTROL_PATHS.includes(lowerPath)) {
235
260
  return pathViolation("static_runtime_control_path_not_supported", "Static deploys cannot upload runtime control paths.", normalizedPath);
236
261
  }
237
262
  return null;
@@ -0,0 +1,7 @@
1
+ import { type DeployEnv } from "../config/domains.js";
2
+ export declare function pushNewRemoteUrl(input: {
3
+ slug: string;
4
+ token: string;
5
+ origin?: string;
6
+ environment?: Extract<DeployEnv, "dev" | "prod">;
7
+ }): string;
@@ -0,0 +1,7 @@
1
+ import { serviceOrigin } from "../config/domains.js";
2
+ export function pushNewRemoteUrl(input) {
3
+ const url = new URL(`/${encodeURIComponent(input.slug)}`, input.origin ?? serviceOrigin("push", input.environment ?? "prod"));
4
+ url.username = "t";
5
+ url.password = input.token;
6
+ return url.toString();
7
+ }
@@ -6,10 +6,10 @@ export declare const qk: {
6
6
  accountInvitations: () => readonly ["account", "invitations"];
7
7
  apiTokens: (teamId?: string) => readonly ["api-tokens"] | readonly ["api-tokens", string];
8
8
  agentAccounts: (teamId: string) => readonly ["agent-accounts", string];
9
- myShelf: () => readonly ["my-shelf"];
10
9
  mcpDashboard: (teamId: string) => readonly ["mcp", "dashboard", string];
11
10
  teams: () => readonly ["teams", "list"];
12
11
  team: (ref: string) => readonly ["teams", "by-ref", string];
12
+ teamDetail: (id: string) => readonly ["teams", string, "detail"];
13
13
  teamMembers: (id: string) => readonly ["teams", string, "members"];
14
14
  teamInvites: (id: string) => readonly ["teams", string, "invites"];
15
15
  teamBilling: (id: string) => readonly ["teams", string, "billing"];
@@ -31,7 +31,6 @@ export declare const qk: {
31
31
  spaceComments: (id: string) => readonly ["spaces", string, "comments"];
32
32
  spaceCommentThread: (id: string, threadId: string) => readonly ["spaces", string, "comments", string];
33
33
  spaceActivity: (id: string, limit: number) => readonly ["spaces", string, "activity", number];
34
- spaceDeployments: (id: string) => readonly ["spaces", string, "deployments"];
35
34
  spaceTags: (id: string) => readonly ["spaces", string, "tags"];
36
35
  spaceTagVersion: (id: string, versionId: string) => readonly ["spaces", string, "tags", string];
37
36
  spaceTagDiff: (id: string, versionId: string) => readonly ["spaces", string, "tags", string, "diff"];
@@ -42,7 +41,7 @@ export declare const qk: {
42
41
  spaceBuilds: (id: string) => readonly ["spaces", string, "builds"];
43
42
  spaceRepositoryConnection: (id: string, connectionType?: string) => readonly ["spaces", string, "repository-connection", string];
44
43
  repositoryInstallations: (spaceId: string) => readonly ["spaces", string, "repository-installations"];
45
- repositoryInstallationRepositories: (installationId: string) => readonly ["repository-connections", "installations", string, "repositories"];
44
+ repositoryInstallationRepositories: (spaceId: string, installationId: string) => readonly ["spaces", string, "repository-installations", string, "repositories"];
46
45
  spaceRepositoryAnalyze: (id: string, connectionType?: string, ref?: string, root?: string) => readonly ["spaces", string, "repository-connection", string, "analyze", string, string];
47
46
  spaceRepositoryWebhookDeliveries: (id: string, connectionType?: string) => readonly ["spaces", string, "repository-webhook-deliveries", string];
48
47
  teamWebhooks: (teamId: string) => readonly ["teams", string, "webhooks"];
@@ -55,7 +54,7 @@ export declare const qk: {
55
54
  spaceVersionFileUrlPreview: (id: string, versionId: string, path: string) => readonly ["spaces", string, "versions", string, "files", string, "url-preview"];
56
55
  spaceVersionFileDiff: (id: string, versionId: string, baseVersionId?: string) => readonly ["spaces", string, "versions", string, "files", "diff", string];
57
56
  spaceVersionConventionArtifacts: (id: string, versionId: string) => readonly ["spaces", string, "versions", string, "convention-artifacts"];
58
- spaceVersionConventionArtifact: (id: string, versionId: string, kind: "redirects" | "headers") => readonly ["spaces", string, "versions", string, "convention-artifacts", "redirects" | "headers"];
57
+ spaceVersionConventionArtifact: (id: string, versionId: string, kind: "redirects" | "headers") => readonly ["spaces", string, "versions", string, "convention-artifacts", "headers" | "redirects"];
59
58
  spaceVersionRoutingSummary: (id: string, versionId: string) => readonly ["spaces", string, "versions", string, "routing-summary"];
60
59
  spaceDiagnostics: (id: string) => readonly ["spaces", string, "diagnostics"];
61
60
  spaceDomains: (id: string) => readonly ["spaces", string, "domains"];
@@ -68,7 +67,7 @@ export declare const qk: {
68
67
  spaceRuntimeRealtimeEvents: (id: string, limit: number) => readonly ["spaces", string, "runtime", "realtime", "events", number];
69
68
  spaceVariables: (id: string) => readonly ["spaces", string, "variables"];
70
69
  spaceCertificates: (id: string) => readonly ["spaces", string, "runtime", "certificates"];
71
- spaceLogs: (id: string, kind: "build" | "access" | "runtime") => readonly ["spaces", string, "logs", "build" | "runtime" | "access"];
70
+ spaceLogs: (id: string, kind: "build" | "access" | "runtime") => readonly ["spaces", string, "logs", "build" | "access" | "runtime"];
72
71
  spaceAnalytics: (id: string, window: string) => readonly ["spaces", string, "analytics", string];
73
72
  teamAgentHandoffs: (teamId: string) => readonly ["teams", string, "agent-handoffs"];
74
73
  teamAccessDefaults: (teamId: string) => readonly ["teams", string, "access", "defaults"];
@@ -8,13 +8,13 @@ export const qk = {
8
8
  // teamId yields the bare prefix (personal scope, and invalidate-all sweeps).
9
9
  apiTokens: (teamId) => teamId === undefined ? ["api-tokens"] : ["api-tokens", teamId],
10
10
  agentAccounts: (teamId) => ["agent-accounts", teamId],
11
- myShelf: () => ["my-shelf"],
12
11
  mcpDashboard: (teamId) => ["mcp", "dashboard", teamId],
13
12
  // The teams LIST gets its own leaf so invalidating it never cascades into
14
13
  // ["teams", id, ...] resource queries (members, spaces, domains, ...).
15
14
  teams: () => ["teams", "list"],
16
15
  // Keyed by slug (the URL ref), so it lives outside the id namespace.
17
16
  team: (ref) => ["teams", "by-ref", ref],
17
+ teamDetail: (id) => ["teams", id, "detail"],
18
18
  teamMembers: (id) => ["teams", id, "members"],
19
19
  teamInvites: (id) => ["teams", id, "invites"],
20
20
  teamBilling: (id) => ["teams", id, "billing"],
@@ -39,7 +39,6 @@ export const qk = {
39
39
  spaceComments: (id) => ["spaces", id, "comments"],
40
40
  spaceCommentThread: (id, threadId) => [...qk.spaceComments(id), threadId],
41
41
  spaceActivity: (id, limit) => ["spaces", id, "activity", limit],
42
- spaceDeployments: (id) => ["spaces", id, "deployments"],
43
42
  spaceTags: (id) => ["spaces", id, "tags"],
44
43
  spaceTagVersion: (id, versionId) => ["spaces", id, "tags", versionId],
45
44
  spaceTagDiff: (id, versionId) => ["spaces", id, "tags", versionId, "diff"],
@@ -56,7 +55,7 @@ export const qk = {
56
55
  spaceBuilds: (id) => ["spaces", id, "builds"],
57
56
  spaceRepositoryConnection: (id, connectionType = "connected") => ["spaces", id, "repository-connection", connectionType],
58
57
  repositoryInstallations: (spaceId) => [...qk.space(spaceId), "repository-installations"],
59
- repositoryInstallationRepositories: (installationId) => ["repository-connections", "installations", installationId, "repositories"],
58
+ repositoryInstallationRepositories: (spaceId, installationId) => [...qk.space(spaceId), "repository-installations", installationId, "repositories"],
60
59
  spaceRepositoryAnalyze: (id, connectionType = "connected", ref = "", root = "") => ["spaces", id, "repository-connection", connectionType, "analyze", ref, root],
61
60
  spaceRepositoryWebhookDeliveries: (id, connectionType = "connected") => ["spaces", id, "repository-webhook-deliveries", connectionType],
62
61
  teamWebhooks: (teamId) => ["teams", teamId, "webhooks"],
@@ -5,5 +5,10 @@ export declare const RUNTIME_HEALTH_PATH: "/__spacefast/health.php";
5
5
  export declare const RUNTIME_COMMENTS_CONFIG_PATH: "/__spacefast/comments/config";
6
6
  export declare const RUNTIME_COMMENTS_TICKET_PATH: "/__spacefast/comments/ticket";
7
7
  export declare const RUNTIME_COMMENTS_VERSION_URLS_PATH: "/__spacefast/comments/version-urls";
8
+ /** The review workspace the orb expands into — a reserved control path on the
9
+ * Space's own origin (collab-frame-plan §4), not part of the `/__spacefast`
10
+ * exchange namespace because people paste this one into Slack.
11
+ * The PHP mirror is SPACEFAST_COLLAB_FRAME_PATH in runtime/engine/shared/context.php. */
12
+ export declare const RUNTIME_COLLAB_FRAME_PATH: "/__/collab";
8
13
  export declare function runtimeManagementPath(suffix: string): string;
9
14
  export declare function runtimeUploadPath(params: URLSearchParams | Record<string, string>): string;
@@ -9,6 +9,11 @@ export const RUNTIME_HEALTH_PATH = `${SPACEFAST_RUNTIME_NAMESPACE}/health.php`;
9
9
  export const RUNTIME_COMMENTS_CONFIG_PATH = `${SPACEFAST_RUNTIME_NAMESPACE}/comments/config`;
10
10
  export const RUNTIME_COMMENTS_TICKET_PATH = `${SPACEFAST_RUNTIME_NAMESPACE}/comments/ticket`;
11
11
  export const RUNTIME_COMMENTS_VERSION_URLS_PATH = `${SPACEFAST_RUNTIME_NAMESPACE}/comments/version-urls`;
12
+ /** The review workspace the orb expands into — a reserved control path on the
13
+ * Space's own origin (collab-frame-plan §4), not part of the `/__spacefast`
14
+ * exchange namespace because people paste this one into Slack.
15
+ * The PHP mirror is SPACEFAST_COLLAB_FRAME_PATH in runtime/engine/shared/context.php. */
16
+ export const RUNTIME_COLLAB_FRAME_PATH = "/__/collab";
12
17
  export function runtimeManagementPath(suffix) {
13
18
  const route = normalizeRuntimeRoute(suffix);
14
19
  if (route === "/") {
@@ -1,13 +1,8 @@
1
1
  import { Buffer } from "node:buffer";
2
- import { RUNTIME_UPLOAD_BATCH_MAX_BYTES, RUNTIME_UPLOAD_BATCH_MAX_FILES } from "./runtime-upload.js";
3
- import { type UploadTarget } from "./upload-session.js";
4
- export { RUNTIME_UPLOAD_BATCH_MAX_BYTES, RUNTIME_UPLOAD_BATCH_MAX_FILES };
5
2
  export type RuntimeBatchArchiveEntry = {
6
3
  path: string;
7
4
  getBytes(): Buffer | Promise<Buffer>;
8
5
  };
9
- export declare function runtimeBatchUploadUnsupported(error: unknown): boolean;
10
- export declare function batchTargetForUploadTarget(uploadId: string, target: UploadTarget): UploadTarget | null;
11
6
  export declare function tarPathParts(filePath: string): {
12
7
  name: string;
13
8
  prefix: string;
@@ -1,31 +1,4 @@
1
1
  import { Buffer } from "node:buffer";
2
- import { RUNTIME_UPLOAD_BATCH_MAX_BYTES, RUNTIME_UPLOAD_BATCH_MAX_FILES, } from "./runtime-upload.js";
3
- import { UploadTargetError } from "./upload-session.js";
4
- // Batch planning + tar-writing wire contract shared by the CLI and the control
5
- // plane's inline publish path. This is the sender half of the runtime's tar
6
- // batch parser (runtime/engine/admin/upload.php); any change here must stay in
7
- // lockstep with that file.
8
- export { RUNTIME_UPLOAD_BATCH_MAX_BYTES, RUNTIME_UPLOAD_BATCH_MAX_FILES };
9
- export function runtimeBatchUploadUnsupported(error) {
10
- return error instanceof UploadTargetError && (error.status === 404 || error.status === 405);
11
- }
12
- export function batchTargetForUploadTarget(uploadId, target) {
13
- const url = new URL(target.url);
14
- if (!url.pathname.endsWith("/upload.php")) {
15
- return null;
16
- }
17
- url.search = new URLSearchParams({ op: "batch", upload_id: uploadId }).toString();
18
- return {
19
- ...target,
20
- path: "__spacefast_batch.tar",
21
- method: "POST",
22
- url: url.toString(),
23
- headers: {
24
- ...target.headers,
25
- "content-type": "application/x-tar",
26
- },
27
- };
28
- }
29
2
  export function tarPathParts(filePath) {
30
3
  if (Buffer.byteLength(filePath) <= 100) {
31
4
  return { name: filePath, prefix: "" };
@@ -57,6 +30,7 @@ export async function tarArchive(entries, pathTooLongError) {
57
30
  if (!parts) {
58
31
  throw pathTooLongError(entry.path);
59
32
  }
33
+ // oxlint-disable-next-line no-await-in-loop -- Tar entries must retain declared order.
60
34
  const body = Buffer.from(await entry.getBytes());
61
35
  const header = Buffer.alloc(512);
62
36
  header.write(parts.name, 0, Math.min(100, Buffer.byteLength(parts.name)), "utf8");
@@ -1,15 +1,4 @@
1
- export declare const RUNTIME_UPLOAD_METHOD: "PUT";
2
- export declare const RUNTIME_UPLOAD_BATCH_MAX_FILES = 500;
3
- export declare const RUNTIME_UPLOAD_BATCH_MAX_BYTES: number;
4
1
  export declare function runtimeUploadCanonicalPath(path: string): string;
5
- export declare function runtimeUploadSessionBaseUrl(input: {
6
- baseUrl: string;
7
- uploadId: string;
8
- }): string;
9
- export declare function runtimeUploadBatchUrl(input: {
10
- baseUrl: string;
11
- uploadId: string;
12
- }): string;
13
2
  export declare function runtimeUploadFetchUrl(input: {
14
3
  baseUrl: string;
15
4
  uploadId: string;
@@ -17,5 +6,11 @@ export declare function runtimeUploadFetchUrl(input: {
17
6
  }): string;
18
7
  export declare function runtimeUploadFileUrl(input: {
19
8
  baseUrl: string;
9
+ uploadId: string;
20
10
  path: string;
21
11
  }): string;
12
+ export declare function runtimeUploadBlobUrl(input: {
13
+ baseUrl: string;
14
+ spaceId: string;
15
+ sha256: string;
16
+ }): string;
@@ -1,10 +1,4 @@
1
1
  import { runtimeUploadPath } from "./runtime-paths.js";
2
- export const RUNTIME_UPLOAD_METHOD = "PUT";
3
- // Mirrors STATTIC_RUNTIME_UPLOAD_BATCH_MAX_FILES/_MAX_BYTES in
4
- // runtime/engine/admin/upload.php (parity-guarded in
5
- // apps/control-plane/src/runtime/php-policy-parity.test.ts).
6
- export const RUNTIME_UPLOAD_BATCH_MAX_FILES = 500;
7
- export const RUNTIME_UPLOAD_BATCH_MAX_BYTES = 8 * 1024 * 1024;
8
2
  export function runtimeUploadCanonicalPath(path) {
9
3
  // Canonical path form (spec "Upload Contract"): UTF-8, Unicode NFC,
10
4
  // percent-encoded once. The runtime rejects non-canonical encodings.
@@ -14,12 +8,6 @@ export function runtimeUploadCanonicalPath(path) {
14
8
  .map((segment) => encodeURIComponent(segment))
15
9
  .join("/");
16
10
  }
17
- export function runtimeUploadSessionBaseUrl(input) {
18
- return new URL(runtimeUploadPath({ op: "file", upload_id: input.uploadId, path: "" }), input.baseUrl).toString();
19
- }
20
- export function runtimeUploadBatchUrl(input) {
21
- return new URL(runtimeUploadPath({ op: "batch", upload_id: input.uploadId }), input.baseUrl).toString();
22
- }
23
11
  export function runtimeUploadFetchUrl(input) {
24
12
  return new URL(runtimeUploadPath({
25
13
  op: "fetch",
@@ -28,5 +16,14 @@ export function runtimeUploadFetchUrl(input) {
28
16
  }), input.baseUrl).toString();
29
17
  }
30
18
  export function runtimeUploadFileUrl(input) {
31
- return `${input.baseUrl}${runtimeUploadCanonicalPath(input.path)}`;
19
+ return new URL(runtimeUploadPath({
20
+ op: "file",
21
+ upload_id: input.uploadId,
22
+ path: runtimeUploadCanonicalPath(input.path),
23
+ }), input.baseUrl).toString();
24
+ }
25
+ export function runtimeUploadBlobUrl(input) {
26
+ return new URL(runtimeUploadPath({
27
+ route: `/spaces/${encodeURIComponent(input.spaceId)}/blobs/${encodeURIComponent(input.sha256)}`,
28
+ }), input.baseUrl).toString();
32
29
  }
@@ -273,7 +273,6 @@ function lockName(leaf) {
273
273
  const digest = createHash("sha256").update(identity).digest("hex").slice(0, 24);
274
274
  return `.spacefast-${digest}.lock`;
275
275
  }
276
- const LEGACY_LEAF_LOCK_STALE_MS = 2 * 60_000;
277
276
  function serializeLeafLockOwner(owner) {
278
277
  const { phase, ...identity } = owner;
279
278
  return `${phase === "active" ? "A" : "R"}${JSON.stringify(identity)}\n`;
@@ -293,6 +292,7 @@ function parseLeafLockOwner(raw) {
293
292
  (value.temporaryName === undefined ||
294
293
  (typeof value.temporaryName === "string" &&
295
294
  /^\.spacefast-[0-9a-f-]{36}\.tmp$/.test(value.temporaryName)))) {
295
+ // SAFETY: The preceding checks establish every required LeafLockOwner field and the optional temporaryName field.
296
296
  return { ...value, phase };
297
297
  }
298
298
  }
@@ -360,14 +360,8 @@ function processIsAlive(pid) {
360
360
  }
361
361
  }
362
362
  async function leafLockRecordIsStale(record) {
363
- // The original lock format was an empty exclusive file. It carries no owner
364
- // identity, so a short age fence is the only safe compatibility signal: a
365
- // fresh legacy writer stays exclusive, while crash wreckage can recover.
366
- if (record?.kind === "legacy") {
367
- return Date.now() - record.modifiedAtMs > LEGACY_LEAF_LOCK_STALE_MS;
368
- }
369
- // A malformed non-empty record is not proof that no writer owns it. New lock
370
- // records are atomically published, so corruption still needs explicit recovery.
363
+ // A malformed record is not proof that no writer owns it. New lock records are
364
+ // atomically published, so corruption still needs explicit recovery.
371
365
  if (record?.kind !== "owned")
372
366
  return false;
373
367
  const { owner } = record;
@@ -399,13 +393,8 @@ async function readLeafLockRecord(filePath) {
399
393
  visibleMetadata.ino !== metadata.ino) {
400
394
  return { kind: "invalid" };
401
395
  }
402
- const raw = await file.readFile("utf8");
403
- const owner = parseLeafLockOwner(raw);
404
- if (owner)
405
- return { kind: "owned", owner };
406
- return raw.length === 0
407
- ? { kind: "legacy", modifiedAtMs: metadata.mtimeMs }
408
- : { kind: "invalid" };
396
+ const owner = parseLeafLockOwner(await file.readFile("utf8"));
397
+ return owner ? { kind: "owned", owner } : { kind: "invalid" };
409
398
  }
410
399
  finally {
411
400
  await file.close();
@@ -1,10 +1,9 @@
1
1
  import { type Grant } from "../contracts/grants.js";
2
- import type { RuntimeKind } from "../contracts/execution.js";
3
2
  import { type SpaceConfig, type SpaceConfigFile } from "../contracts/space-config.js";
4
3
  export type SpaceConfigBlockingIssueCode = "config_file_too_large" | "config_templates_over_limit" | "config_meta_too_long" | "config_name_too_long" | "config_functions_key_removed" | "config_invalid" | "config_runtime_invalid_kind" | "config_runtime_entry_missing" | "inject_snippet_too_large" | "inject_invalid";
5
4
  export type SpaceConfigIssue = {
6
5
  severity: "info" | "warning" | "error";
7
- code: SpaceConfigBlockingIssueCode | "single_html_index" | "files_mode_inferred";
6
+ code: SpaceConfigBlockingIssueCode | "single_html_index" | "files_mode_inferred" | "listing_shadows_fallback";
8
7
  message: string;
9
8
  /** JSON-path-lite pointer into the config shape (e.g. `meta.title`). */
10
9
  path?: string;
@@ -32,19 +31,6 @@ export declare const SPACE_CONFIG_FILE_ONLY_KEYS: readonly ["access", "name", "r
32
31
  */
33
32
  export declare function spaceConfigOverlayIssue(config: unknown): SpaceConfigBlockingIssue | null;
34
33
  export declare function stripJsonComments(raw: string): string;
35
- export type ParsedSpaceConfigFile = {
36
- /** Parsed config (cap- and shape-valid keys only); null when unusable. */
37
- config: SpaceConfigFile | null;
38
- issues: SpaceConfigIssue[];
39
- };
40
- /**
41
- * Parses a raw `sf.jsonc` at finalize: 256 KB cap, JSONC tolerance,
42
- * routing-key rejection, unknown-key diagnostics, shared shape caps. Errors
43
- * fail the version; warnings ride version diagnostics.
44
- */
45
- export declare function parseSpaceConfigFile(raw: string, options?: {
46
- path?: string;
47
- }): ParsedSpaceConfigFile;
48
34
  export type ResolvedServingConfig = {
49
35
  index: string | false;
50
36
  fallback: {
@@ -63,25 +49,6 @@ export type ResolvedServingConfig = {
63
49
  /** Resolved inject snippets; only honored when experimentalGutenberg is true. */
64
50
  inject: NonNullable<SpaceConfig["inject"]> | null;
65
51
  };
66
- export type EffectiveSpaceConfig = {
67
- /** The merged SpaceConfig (defaults + file + overlay, key-wise). */
68
- config: SpaceConfig;
69
- /** Compiled serving primitives the runtime artifact stores. */
70
- serving: ResolvedServingConfig;
71
- issues: SpaceConfigIssue[];
72
- };
73
- /**
74
- * Resolves the effective SpaceConfig for a version at finalize (or
75
- * config-only regeneration). `manifestPaths` is the canonical committed file
76
- * set; `fileConfig` the parsed sf.jsonc (null when absent); `overlay`
77
- * the stored space config overlay (null when nothing overridden).
78
- */
79
- export declare function resolveEffectiveSpaceConfig(input: {
80
- manifestPaths: ReadonlySet<string>;
81
- fileConfig: SpaceConfig | null;
82
- overlay: SpaceConfig | null;
83
- runtimeKind?: RuntimeKind;
84
- }): EffectiveSpaceConfig;
85
52
  export type SpaceSettingsPatch = {
86
53
  mode?: "website" | "files" | undefined;
87
54
  spa?: boolean | undefined;