@spacefast/common 0.0.24 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/brand-assets/LICENSE-Haskoy.txt +93 -0
- package/brand-assets/haskoy-latin-variable.woff2 +0 -0
- package/brand-assets/spacefast-favicon.svg +4 -4
- package/brand-assets/spacefast-sf-full-bleed.svg +3 -3
- package/brand-assets/spacefast-wordmark.svg +2 -4
- package/dist/agents/connect-targets.d.ts +6 -146
- package/dist/agents/connect-targets.js +63 -295
- package/dist/agents/private-key-oauth.d.ts +31 -2
- package/dist/agents/private-key-oauth.js +166 -92
- package/dist/agents/registry/core.d.ts +292 -0
- package/dist/agents/registry/core.js +1388 -0
- package/dist/agents/registry/deeplink-probes.d.ts +23 -0
- package/dist/agents/registry/deeplink-probes.js +18 -0
- package/dist/agents/registry/node.d.ts +56 -0
- package/dist/agents/registry/node.js +339 -0
- package/dist/brand-assets-build.js +3 -6
- package/dist/brand-assets.d.ts +5 -15
- package/dist/brand-assets.js +12 -14
- package/dist/brand.d.ts +17 -0
- package/dist/brand.js +24 -4
- package/dist/config/domains.d.ts +79 -12
- package/dist/config/domains.js +128 -29
- package/dist/config/index.js +1 -2
- package/dist/contracts/abuse.js +9 -24
- package/dist/contracts/access-profiles.d.ts +86 -0
- package/dist/contracts/access-profiles.js +89 -0
- package/dist/contracts/access.d.ts +24 -30
- package/dist/contracts/access.js +33 -64
- package/dist/contracts/activity.d.ts +18 -1
- package/dist/contracts/activity.js +72 -26
- package/dist/contracts/analytics.js +8 -10
- package/dist/contracts/api-keys.d.ts +77 -52
- package/dist/contracts/api-keys.js +44 -24
- package/dist/contracts/application-journal.d.ts +216 -0
- package/dist/contracts/application-journal.js +167 -0
- package/dist/contracts/auth.d.ts +90 -17
- package/dist/contracts/auth.js +86 -17
- package/dist/contracts/beta.d.ts +9 -4
- package/dist/contracts/beta.js +20 -16
- package/dist/contracts/billing.d.ts +2 -1
- package/dist/contracts/billing.js +37 -11
- package/dist/contracts/bootstrap.d.ts +791 -0
- package/dist/contracts/bootstrap.js +51 -0
- package/dist/contracts/builds.d.ts +209 -18
- package/dist/contracts/builds.js +93 -27
- package/dist/contracts/channels.d.ts +43 -0
- package/dist/contracts/channels.js +25 -9
- package/dist/contracts/cli.d.ts +7 -0
- package/dist/contracts/cli.js +6 -0
- package/dist/contracts/collab-public.d.ts +1 -1
- package/dist/contracts/collab-public.js +5 -8
- package/dist/contracts/comments.d.ts +44 -34
- package/dist/contracts/comments.js +107 -77
- package/dist/contracts/commerce.d.ts +165 -0
- package/dist/contracts/commerce.js +146 -0
- package/dist/contracts/common.d.ts +3 -3
- package/dist/contracts/common.js +20 -31
- package/dist/contracts/content-contract-verification.d.ts +521 -0
- package/dist/contracts/content-contract-verification.js +320 -0
- package/dist/contracts/content-platform-fixture.d.ts +1278 -0
- package/dist/contracts/content-platform-fixture.js +44 -0
- package/dist/contracts/content-program.d.ts +994 -0
- package/dist/contracts/content-program.js +658 -0
- package/dist/contracts/content-sync.d.ts +290 -0
- package/dist/contracts/content-sync.js +167 -0
- package/dist/contracts/content.d.ts +432 -0
- package/dist/contracts/content.js +303 -0
- package/dist/contracts/continuation.d.ts +17 -1
- package/dist/contracts/continuation.js +23 -23
- package/dist/contracts/countries.js +3 -4
- package/dist/contracts/crons.d.ts +52 -0
- package/dist/contracts/crons.js +60 -0
- package/dist/contracts/dashboard-prefs.d.ts +56 -0
- package/dist/contracts/dashboard-prefs.js +51 -0
- package/dist/contracts/device-auth.d.ts +234 -37
- package/dist/contracts/device-auth.js +267 -53
- package/dist/contracts/docs.d.ts +1 -5
- package/dist/contracts/docs.js +7 -15
- package/dist/contracts/domains.d.ts +501 -93
- package/dist/contracts/domains.js +193 -55
- package/dist/contracts/email-preferences.d.ts +3 -3
- package/dist/contracts/email-preferences.js +6 -7
- package/dist/contracts/enums.d.ts +27 -26
- package/dist/contracts/enums.js +68 -61
- package/dist/contracts/error-code-meta.d.ts +283 -7
- package/dist/contracts/error-code-meta.js +97 -5
- package/dist/contracts/error-codes.d.ts +5 -5
- package/dist/contracts/error-codes.js +82 -17
- package/dist/contracts/events.d.ts +100 -20
- package/dist/contracts/events.js +107 -20
- package/dist/contracts/execution.d.ts +48 -64
- package/dist/contracts/execution.js +52 -62
- package/dist/contracts/feature-lifecycle.d.ts +73 -27
- package/dist/contracts/feature-lifecycle.js +86 -26
- package/dist/contracts/features.d.ts +68 -19
- package/dist/contracts/features.js +42 -41
- package/dist/contracts/fixtures/content-platform-v1.json +1108 -0
- package/dist/contracts/frame-session.d.ts +192 -0
- package/dist/contracts/frame-session.js +164 -0
- package/dist/contracts/functions.d.ts +338 -164
- package/dist/contracts/functions.js +367 -198
- package/dist/contracts/git.d.ts +27 -33
- package/dist/contracts/git.js +10 -11
- package/dist/contracts/grant-copy.d.ts +43 -0
- package/dist/contracts/grant-copy.js +44 -0
- package/dist/contracts/grants.d.ts +62 -54
- package/dist/contracts/grants.js +124 -23
- package/dist/contracts/ids.d.ts +4 -1
- package/dist/contracts/ids.js +6 -3
- package/dist/contracts/internal.d.ts +8 -18
- package/dist/contracts/internal.js +13 -14
- package/dist/contracts/mcp.d.ts +28 -0
- package/dist/contracts/mcp.js +28 -0
- package/dist/contracts/me.d.ts +6 -1
- package/dist/contracts/me.js +12 -6
- package/dist/contracts/notifications.d.ts +27 -29
- package/dist/contracts/notifications.js +9 -4
- package/dist/contracts/oauth-resources.d.ts +112 -10
- package/dist/contracts/oauth-resources.js +116 -11
- package/dist/contracts/oauth-scope-actions.d.ts +90 -0
- package/dist/contracts/oauth-scope-actions.js +126 -0
- package/dist/contracts/operations.d.ts +27 -4
- package/dist/contracts/operations.js +12 -6
- package/dist/contracts/pages.d.ts +31 -7
- package/dist/contracts/pages.js +22 -5
- package/dist/contracts/partner-notifications.d.ts +3 -0
- package/dist/contracts/partner-notifications.js +65 -0
- package/dist/contracts/{platform.d.ts → partner.d.ts} +29 -42
- package/dist/contracts/{platform.js → partner.js} +27 -40
- package/dist/contracts/plan-policy.js +1 -1
- package/dist/contracts/principal-assertion.d.ts +43 -0
- package/dist/contracts/principal-assertion.js +67 -0
- package/dist/contracts/principals.d.ts +73 -0
- package/dist/contracts/principals.js +59 -0
- package/dist/contracts/privacy.d.ts +11 -0
- package/dist/contracts/privacy.js +17 -0
- package/dist/contracts/problem-document.js +3 -4
- package/dist/contracts/publish-archive.d.ts +269 -161
- package/dist/contracts/publish-archive.js +11 -1
- package/dist/contracts/push-new.d.ts +8 -8
- package/dist/contracts/quotas.js +5 -5
- package/dist/contracts/realtime.d.ts +52 -0
- package/dist/contracts/realtime.js +54 -0
- package/dist/contracts/repository-connections.d.ts +44 -31
- package/dist/contracts/repository-connections.js +17 -5
- package/dist/contracts/resources.d.ts +105 -14
- package/dist/contracts/resources.js +150 -26
- package/dist/contracts/route-inventory.d.ts +358 -0
- package/dist/contracts/route-inventory.js +141 -0
- package/dist/contracts/runtime-api.d.ts +206 -82
- package/dist/contracts/runtime-api.js +131 -70
- package/dist/contracts/runtime-app.d.ts +13 -23
- package/dist/contracts/runtime-app.js +39 -24
- package/dist/contracts/runtime-components.d.ts +143 -0
- package/dist/contracts/runtime-components.js +199 -0
- package/dist/contracts/runtime-db.d.ts +49 -23
- package/dist/contracts/runtime-db.js +87 -22
- package/dist/contracts/runtime-purge.d.ts +8 -0
- package/dist/contracts/runtime-purge.js +5 -0
- package/dist/contracts/runtime-services.d.ts +43 -75
- package/dist/contracts/runtime-services.js +29 -49
- package/dist/contracts/runtime-storage.d.ts +15 -14
- package/dist/contracts/runtime-storage.js +30 -14
- package/dist/contracts/sf-config-v1.d.ts +48 -14
- package/dist/contracts/slugs.js +13 -4
- package/dist/contracts/space-config.d.ts +283 -19
- package/dist/contracts/space-config.js +430 -80
- package/dist/contracts/spaces.d.ts +376 -290
- package/dist/contracts/spaces.js +200 -135
- package/dist/contracts/superadmin-activity.d.ts +3 -43
- package/dist/contracts/superadmin-activity.js +6 -30
- package/dist/contracts/superadmin-emails.d.ts +1 -0
- package/dist/contracts/superadmin-emails.js +17 -1
- package/dist/contracts/superadmin-runtime.d.ts +199 -0
- package/dist/contracts/superadmin-runtime.js +141 -0
- package/dist/contracts/superadmin-search.d.ts +22 -2
- package/dist/contracts/superadmin-search.js +14 -6
- package/dist/contracts/superadmin-spaces.d.ts +438 -189
- package/dist/contracts/superadmin-spaces.js +131 -25
- package/dist/contracts/superadmin-teams.d.ts +5 -2
- package/dist/contracts/superadmin-teams.js +1 -2
- package/dist/contracts/superadmin-tenants.d.ts +627 -12
- package/dist/contracts/superadmin-tenants.js +32 -13
- package/dist/contracts/superadmin.d.ts +232 -37
- package/dist/contracts/superadmin.js +106 -22
- package/dist/contracts/tags.d.ts +85 -58
- package/dist/contracts/tags.js +47 -37
- package/dist/contracts/teams.d.ts +86 -26
- package/dist/contracts/teams.js +90 -31
- package/dist/contracts/tenants.d.ts +135 -0
- package/dist/contracts/tenants.js +120 -0
- package/dist/contracts/test-triggers.d.ts +70 -0
- package/dist/contracts/test-triggers.js +116 -0
- package/dist/contracts/theme-json.js +3 -8
- package/dist/contracts/transfers.d.ts +1 -0
- package/dist/contracts/transfers.js +3 -2
- package/dist/contracts/usage.d.ts +209 -0
- package/dist/contracts/usage.js +124 -12
- package/dist/contracts/variables.d.ts +2 -2
- package/dist/contracts/variables.js +9 -13
- package/dist/contracts/webhooks.d.ts +13 -5
- package/dist/contracts/webhooks.js +3 -1
- package/dist/contracts/zero.d.ts +72 -81
- package/dist/contracts/zero.js +80 -78
- package/dist/dashboard-paths/index.d.ts +24 -21
- package/dist/dashboard-paths/index.js +35 -47
- package/dist/dashboard-paths/route-reservations.generated.d.ts +2 -0
- package/dist/dashboard-paths/route-reservations.generated.js +36 -0
- package/dist/docs/agent-arrival.d.ts +92 -0
- package/dist/docs/agent-arrival.js +227 -0
- package/dist/docs/agent-output-policy.d.ts +6 -0
- package/dist/docs/agent-output-policy.js +22 -0
- package/dist/docs/agent-prose.d.ts +12 -18
- package/dist/docs/agent-prose.js +73 -51
- package/dist/docs/agent-setup.d.ts +72 -41
- package/dist/docs/agent-setup.js +339 -436
- package/dist/docs/catalog.d.ts +153 -95
- package/dist/docs/catalog.js +163 -48
- package/dist/docs/error-docs.d.ts +2098 -1
- package/dist/docs/error-docs.js +421 -144
- package/dist/docs/index-build.d.ts +0 -1
- package/dist/docs/index-build.js +2 -3
- package/dist/docs/page-context-prompt.d.ts +77 -0
- package/dist/docs/page-context-prompt.js +131 -0
- package/dist/docs/reference-urls.d.ts +46 -0
- package/dist/docs/reference-urls.js +79 -0
- package/dist/docs/search.d.ts +6 -8
- package/dist/docs/search.js +10 -13
- package/dist/docs/skill-distribution.d.ts +4 -1
- package/dist/docs/skill-distribution.js +10 -5
- package/dist/docs/skills.d.ts +120 -120
- package/dist/docs/skills.js +121 -73
- package/dist/docs/start-prompts.d.ts +45 -0
- package/dist/docs/start-prompts.js +62 -0
- package/dist/domain-exploration.js +9 -13
- package/dist/og-template.d.ts +56 -0
- package/dist/og-template.js +56 -0
- package/dist/slug-policy/index.js +5 -11
- package/dist/test-helpers/fetch-stub.js +1 -0
- package/dist/utils/browser-credential.d.ts +13 -6
- package/dist/utils/browser-credential.js +14 -7
- package/dist/utils/build-settings.d.ts +21 -1
- package/dist/utils/build-settings.js +133 -15
- package/dist/utils/canonical-json.d.ts +3 -5
- package/dist/utils/canonical-json.js +4 -5
- package/dist/utils/cast-transport.d.ts +5 -17
- package/dist/utils/cast-transport.js +6 -19
- package/dist/utils/claim-token.js +1 -2
- package/dist/utils/comment-avatars.js +4 -6
- package/dist/utils/concurrency.js +3 -3
- package/dist/utils/content-type.d.ts +7 -6
- package/dist/utils/content-type.js +73 -51
- package/dist/utils/credential-policy.d.ts +50 -6
- package/dist/utils/credential-policy.js +126 -59
- package/dist/utils/dns-instructions.js +9 -9
- package/dist/utils/egress-policy.fixtures.json +1 -1
- package/dist/utils/error-code.js +1 -0
- package/dist/utils/generate-space-name.d.ts +3 -8
- package/dist/utils/generate-space-name.js +3 -8
- package/dist/utils/grant-decision.fixtures.json +2 -2
- package/dist/utils/grants.d.ts +0 -1
- package/dist/utils/grants.js +7 -8
- package/dist/utils/gravatar.d.ts +2 -4
- package/dist/utils/gravatar.js +6 -12
- package/dist/utils/id-hints.js +14 -23
- package/dist/utils/idempotency.d.ts +3 -3
- package/dist/utils/idempotency.js +6 -7
- package/dist/utils/local-space-state.d.ts +23 -26
- package/dist/utils/local-space-state.js +46 -46
- package/dist/utils/oauth-signed-query.d.ts +3 -5
- package/dist/utils/oauth-signed-query.js +6 -9
- package/dist/utils/one-shot-replay.d.ts +7 -6
- package/dist/utils/one-shot-replay.js +3 -5
- package/dist/utils/page-colors.d.ts +15 -19
- package/dist/utils/page-colors.js +23 -45
- package/dist/utils/page-fonts.d.ts +1 -1
- package/dist/utils/page-fonts.js +8 -20
- package/dist/utils/page-preview.d.ts +0 -1
- package/dist/utils/page-preview.js +0 -1
- package/dist/utils/pages.d.ts +33 -3
- package/dist/utils/pages.js +83 -30
- package/dist/utils/privacy-regions.d.ts +19 -0
- package/dist/utils/privacy-regions.js +91 -0
- package/dist/utils/publish-form-data.d.ts +4 -5
- package/dist/utils/publish-form-data.js +2 -3
- package/dist/utils/publish-policy.d.ts +11 -17
- package/dist/utils/publish-policy.fixtures.json +11 -5
- package/dist/utils/publish-policy.js +65 -110
- package/dist/utils/query-client.d.ts +2 -0
- package/dist/utils/query-client.js +10 -1
- package/dist/utils/runtime-paths.d.ts +4 -4
- package/dist/utils/runtime-paths.js +6 -8
- package/dist/utils/runtime-upload.d.ts +9 -0
- package/dist/utils/runtime-upload.js +12 -0
- package/dist/utils/secure-local-file.d.ts +3 -3
- package/dist/utils/secure-local-file.js +23 -40
- package/dist/utils/space-config.d.ts +12 -15
- package/dist/utils/space-config.js +13 -16
- package/dist/utils/static-runtime-policy.d.ts +10 -7
- package/dist/utils/static-runtime-policy.fixtures.json +38 -41
- package/dist/utils/static-runtime-policy.generated.d.ts +16 -0
- package/dist/utils/static-runtime-policy.generated.js +70 -0
- package/dist/utils/static-runtime-policy.js +31 -132
- package/dist/utils/storage-policy.js +1 -2
- package/dist/utils/upload-session.d.ts +4 -1
- package/dist/utils/upload-session.js +41 -19
- package/dist/utils/version-path.js +11 -19
- package/dist/utils/wpcom-auth-redirects.d.ts +5 -14
- package/dist/utils/wpcom-auth-redirects.js +1 -4
- package/dist/vocabulary.d.ts +13 -13
- package/dist/vocabulary.js +52 -27
- package/package.json +15 -18
- package/dist/agents/client-registry.d.ts +0 -22
- package/dist/agents/client-registry.js +0 -41
- package/dist/docs/agent-handoff-document.d.ts +0 -15
- package/dist/docs/agent-handoff-document.js +0 -180
- package/dist/docs/agent-solutions.d.ts +0 -110
- package/dist/docs/agent-solutions.js +0 -183
- package/dist/utils/query-keys.d.ts +0 -84
- package/dist/utils/query-keys.js +0 -108
package/dist/utils/grants.js
CHANGED
|
@@ -27,8 +27,10 @@ function targetMatches(selector, target) {
|
|
|
27
27
|
return target.kind === "version" && target.versionId === selector.versionId;
|
|
28
28
|
case "branch":
|
|
29
29
|
return target.kind === "branch" && target.branch === selector.branch;
|
|
30
|
+
// Immutable Version hosts only. The live host is its own target: sharing
|
|
31
|
+
// "every Version" does not thereby publish the live site.
|
|
30
32
|
case "all_versions":
|
|
31
|
-
return target.kind === "
|
|
33
|
+
return target.kind === "version";
|
|
32
34
|
}
|
|
33
35
|
throw new Error("Unsupported Grant target.");
|
|
34
36
|
}
|
|
@@ -71,12 +73,10 @@ export function grantResourcePatternMatches(pattern, resource, resourceKind = "r
|
|
|
71
73
|
return matches(0, 0);
|
|
72
74
|
}
|
|
73
75
|
/**
|
|
74
|
-
* Exclude
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
* Include never gets the same courtesy — granting the stripped route would hand
|
|
79
|
-
* out access nobody wrote down. The serving engine compiles the identical pair
|
|
76
|
+
* Exclude reads wider than include, deliberately: the route lane collapses a
|
|
77
|
+
* terminal `index.html`, so an exclude naming an index artifact must also close
|
|
78
|
+
* the route it serves. Include must not, or it would grant a route nobody wrote
|
|
79
|
+
* down. The serving engine compiles the same pair
|
|
80
80
|
* (access-rules.php _stattic_grant_exclude_pattern_variants).
|
|
81
81
|
*/
|
|
82
82
|
function grantExcludePatternMatches(pattern, resource, resourceKind) {
|
|
@@ -88,7 +88,6 @@ function grantExcludePatternMatches(pattern, resource, resourceKind) {
|
|
|
88
88
|
const stripped = canonical.slice(0, -"/index.html".length);
|
|
89
89
|
return grantResourcePatternMatches(stripped === "" ? "/" : stripped, resource, resourceKind);
|
|
90
90
|
}
|
|
91
|
-
/** Does a Grant's resource set reach this path? Include, minus exclude. */
|
|
92
91
|
export function grantResourceSetMatches(resources, resource, resourceKind = "route") {
|
|
93
92
|
return (resources.include.some((pattern) => grantResourcePatternMatches(pattern, resource, resourceKind)) &&
|
|
94
93
|
!resources.exclude.some((pattern) => grantExcludePatternMatches(pattern, resource, resourceKind)));
|
package/dist/utils/gravatar.d.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
/** Gravatar's canonical address form: trimmed and lowercased. */
|
|
2
1
|
export declare function normalizeGravatarEmail(email: string | null | undefined): string | null;
|
|
3
|
-
/** The canonical avatar URL for an already-computed SHA-256 hash. */
|
|
4
2
|
export declare function gravatarUrlFromHash(hashHex: string): string;
|
|
5
3
|
/**
|
|
6
|
-
* SHA-256 hex of a normalized email
|
|
7
|
-
* a browser
|
|
4
|
+
* Gravatar's identifier, the SHA-256 hex of a normalized email. WebCrypto lets
|
|
5
|
+
* a browser derive it locally and never send the raw address anywhere.
|
|
8
6
|
*/
|
|
9
7
|
export declare function gravatarEmailHash(email: string | null | undefined): Promise<string | null>;
|
package/dist/utils/gravatar.js
CHANGED
|
@@ -1,26 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The one Gravatar URL shape Spacefast emits
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* `d=404` is deliberate: a person without a Gravatar gets no image at all, and
|
|
8
|
-
* the consumer renders its own initials/palette fallback instead of a stock
|
|
9
|
-
* silhouette. `s=160` covers the largest avatar we render, retina included.
|
|
2
|
+
* The one Gravatar URL shape Spacefast emits, so Cast can validate a guest
|
|
3
|
+
* avatar against a single pattern. `d=404` returns no image for a person
|
|
4
|
+
* without a Gravatar, leaving the consumer's own fallback to render; `s=160`
|
|
5
|
+
* covers the largest avatar we render, retina included.
|
|
10
6
|
*/
|
|
11
7
|
const GRAVATAR_AVATAR_QUERY = "d=404&r=g&s=160";
|
|
12
|
-
/** Gravatar's canonical address form: trimmed and lowercased. */
|
|
13
8
|
export function normalizeGravatarEmail(email) {
|
|
14
9
|
const normalized = email?.trim().toLowerCase();
|
|
15
10
|
return normalized ? normalized : null;
|
|
16
11
|
}
|
|
17
|
-
/** The canonical avatar URL for an already-computed SHA-256 hash. */
|
|
18
12
|
export function gravatarUrlFromHash(hashHex) {
|
|
19
13
|
return `https://gravatar.com/avatar/${hashHex}?${GRAVATAR_AVATAR_QUERY}`;
|
|
20
14
|
}
|
|
21
15
|
/**
|
|
22
|
-
* SHA-256 hex of a normalized email
|
|
23
|
-
* a browser
|
|
16
|
+
* Gravatar's identifier, the SHA-256 hex of a normalized email. WebCrypto lets
|
|
17
|
+
* a browser derive it locally and never send the raw address anywhere.
|
|
24
18
|
*/
|
|
25
19
|
export async function gravatarEmailHash(email) {
|
|
26
20
|
const normalized = normalizeGravatarEmail(email);
|
package/dist/utils/id-hints.js
CHANGED
|
@@ -1,17 +1,12 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
// to the caller principal — this module never touches the DB or auth.
|
|
7
|
-
// How many caller-scoped rows a hint builder will scan to rank candidates. Above
|
|
8
|
-
// this the collection is treated as too large to rank for free, so the builder
|
|
9
|
-
// returns only the recovery hint (no candidate ids).
|
|
1
|
+
// One closest-candidate ranking for every resource's 404 hint builder (spaces,
|
|
2
|
+
// versions, teams, domains). Callers scope the rows to the caller principal;
|
|
3
|
+
// this module never touches the DB or auth.
|
|
4
|
+
// Above this many rows the collection is too large to rank for free, and the
|
|
5
|
+
// builder returns the recovery hint without candidate ids.
|
|
10
6
|
export const HINT_SCAN_CAP = 200;
|
|
11
7
|
export const HINT_MAX_CANDIDATES = 5;
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
// from turning attacker-controlled refs or stored labels into quadratic work.
|
|
8
|
+
// Every hint key (ids, slugs, hostnames, version refs) fits here. Rejecting
|
|
9
|
+
// larger values keeps attacker-controlled refs out of quadratic work.
|
|
15
10
|
export const HINT_MAX_KEY_LENGTH = 256;
|
|
16
11
|
function boundedLevenshtein(a, b, maxDistance) {
|
|
17
12
|
if (Math.abs(a.length - b.length) > maxDistance)
|
|
@@ -48,9 +43,8 @@ export function levenshtein(a, b) {
|
|
|
48
43
|
}
|
|
49
44
|
return boundedLevenshtein(a, b, Math.max(a.length, b.length));
|
|
50
45
|
}
|
|
51
|
-
//
|
|
52
|
-
//
|
|
53
|
-
// containment = 1; otherwise a typo within a length-bounded edit distance.
|
|
46
|
+
// Lower is closer, +Infinity when not close at all: exact = 0, substring
|
|
47
|
+
// containment = 1, otherwise 1 + a length-bounded edit distance.
|
|
54
48
|
export function refCloseness(needle, candidate) {
|
|
55
49
|
if (!candidate)
|
|
56
50
|
return Number.POSITIVE_INFINITY;
|
|
@@ -61,22 +55,19 @@ export function refCloseness(needle, candidate) {
|
|
|
61
55
|
return 0;
|
|
62
56
|
if (candidate.includes(needle) || needle.includes(candidate))
|
|
63
57
|
return 1;
|
|
64
|
-
//
|
|
65
|
-
// string so short refs don't match everything.
|
|
58
|
+
// Bound the distance by the shorter string so short refs don't match everything.
|
|
66
59
|
const tolerance = Math.max(2, Math.floor(Math.min(needle.length, candidate.length) / 3));
|
|
67
60
|
const distance = boundedLevenshtein(needle, candidate, tolerance);
|
|
68
61
|
return distance <= tolerance ? 1 + distance : Number.POSITIVE_INFINITY;
|
|
69
62
|
}
|
|
70
|
-
// Rank `rows` by
|
|
71
|
-
//
|
|
72
|
-
//
|
|
73
|
-
// (e.g. recency); keys are matched case-insensitively.
|
|
63
|
+
// Rank `rows` by their closest key to `ref`, at most `max` rows. Rows with no
|
|
64
|
+
// close key are dropped. Ties break by the optional comparator; keys match
|
|
65
|
+
// case-insensitively.
|
|
74
66
|
export function rankCandidates(ref, rows, keysOf, options) {
|
|
75
67
|
if (rows.length > HINT_SCAN_CAP || ref.length > HINT_MAX_KEY_LENGTH)
|
|
76
68
|
return [];
|
|
77
69
|
const needle = ref.toLowerCase();
|
|
78
|
-
//
|
|
79
|
-
// that normalized representation under the same work ceiling too.
|
|
70
|
+
// Lowercasing can expand a string (U+0130), so re-check the ceiling.
|
|
80
71
|
if (needle.length > HINT_MAX_KEY_LENGTH)
|
|
81
72
|
return [];
|
|
82
73
|
const max = options?.max ?? HINT_MAX_CANDIDATES;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Stable, secret-safe key for one
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* Stable, secret-safe key for one logical mutation attempt. The same publish
|
|
3
|
+
* from the CLI and from MCP must land on one key, or the API stops
|
|
4
|
+
* deduplicating the retry.
|
|
5
5
|
*/
|
|
6
6
|
export declare function stableIdempotencyKey(prefix: string, parts: unknown): string;
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
/**
|
|
3
|
-
* Key-sorted JSON that treats an absent key and an `undefined` value
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* to hash alike. Merging the two spellings would change every minted key.
|
|
3
|
+
* Key-sorted JSON that treats an absent key and an `undefined` value alike: a
|
|
4
|
+
* key the CLI omits and one MCP sets to `undefined` are the same mutation.
|
|
5
|
+
* Deliberately not `canonicalJson`; merging the two would change every key.
|
|
7
6
|
*/
|
|
8
7
|
function keyJson(value) {
|
|
9
8
|
if (Array.isArray(value)) {
|
|
@@ -19,9 +18,9 @@ function keyJson(value) {
|
|
|
19
18
|
return JSON.stringify(value ?? null);
|
|
20
19
|
}
|
|
21
20
|
/**
|
|
22
|
-
* Stable, secret-safe key for one
|
|
23
|
-
*
|
|
24
|
-
*
|
|
21
|
+
* Stable, secret-safe key for one logical mutation attempt. The same publish
|
|
22
|
+
* from the CLI and from MCP must land on one key, or the API stops
|
|
23
|
+
* deduplicating the retry.
|
|
25
24
|
*/
|
|
26
25
|
export function stableIdempotencyKey(prefix, parts) {
|
|
27
26
|
return `${prefix}-${createHash("sha256").update(keyJson(parts)).digest("hex")}`;
|
|
@@ -5,16 +5,13 @@ export declare const LOCAL_SPACE_STATE_FILE_NAME = "state.json";
|
|
|
5
5
|
/**
|
|
6
6
|
* Local Spacefast checkout contract.
|
|
7
7
|
*
|
|
8
|
-
* `.spacefast/space.json` is the committable selection axis:
|
|
9
|
-
*
|
|
10
|
-
* coordinates (`team`, `apiBaseUrl`). It must not carry credentials.
|
|
8
|
+
* `.spacefast/space.json` is the committable selection axis: which space a
|
|
9
|
+
* checkout targets, plus non-secret provider/team coordinates. Never credentials.
|
|
11
10
|
*
|
|
12
|
-
* `.spacefast/state.json` is the
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* The strict versioned schema fails closed instead of letting an older tool
|
|
17
|
-
* reinterpret fields written under a contract it does not understand.
|
|
11
|
+
* `.spacefast/state.json` is the secret axis: credentials and small publish
|
|
12
|
+
* caches, machine-written, gitignored, merge-written. Its strict versioned
|
|
13
|
+
* schema fails closed rather than let an older tool reinterpret fields written
|
|
14
|
+
* under a contract it does not understand.
|
|
18
15
|
*/
|
|
19
16
|
export declare const localSpaceLinkSchema: z.ZodObject<{
|
|
20
17
|
team: z.ZodOptional<z.ZodString>;
|
|
@@ -22,6 +19,12 @@ export declare const localSpaceLinkSchema: z.ZodObject<{
|
|
|
22
19
|
apiBaseUrl: z.ZodOptional<z.ZodString>;
|
|
23
20
|
}, z.core.$loose>;
|
|
24
21
|
export declare const LOCAL_SPACE_STATE_SCHEMA_VERSION = 1;
|
|
22
|
+
export declare const creationAttemptSchema: z.ZodObject<{
|
|
23
|
+
key: z.ZodString;
|
|
24
|
+
principal: z.ZodString;
|
|
25
|
+
kind: z.ZodLiteral<"authenticated">;
|
|
26
|
+
createdAt: z.ZodString;
|
|
27
|
+
}, z.core.$strict>;
|
|
25
28
|
export declare const localSpaceStateSchema: z.ZodObject<{
|
|
26
29
|
schemaVersion: z.ZodLiteral<1>;
|
|
27
30
|
spaceId: z.ZodOptional<z.ZodString>;
|
|
@@ -41,22 +44,14 @@ export declare const localSpaceStateSchema: z.ZodObject<{
|
|
|
41
44
|
pendingPublish: z.ZodOptional<z.ZodObject<{
|
|
42
45
|
key: z.ZodString;
|
|
43
46
|
principal: z.ZodString;
|
|
44
|
-
kind: z.
|
|
45
|
-
anonymous: "anonymous";
|
|
46
|
-
authenticated: "authenticated";
|
|
47
|
-
}>;
|
|
48
|
-
intent: z.ZodOptional<z.ZodString>;
|
|
47
|
+
kind: z.ZodLiteral<"authenticated">;
|
|
49
48
|
createdAt: z.ZodString;
|
|
50
49
|
}, z.core.$strict>>;
|
|
51
50
|
/** Settled creation receipt identity retained for invocations that began before its commit. */
|
|
52
51
|
creationReplay: z.ZodOptional<z.ZodObject<{
|
|
53
52
|
key: z.ZodString;
|
|
54
53
|
principal: z.ZodString;
|
|
55
|
-
kind: z.
|
|
56
|
-
anonymous: "anonymous";
|
|
57
|
-
authenticated: "authenticated";
|
|
58
|
-
}>;
|
|
59
|
-
intent: z.ZodOptional<z.ZodString>;
|
|
54
|
+
kind: z.ZodLiteral<"authenticated">;
|
|
60
55
|
createdAt: z.ZodString;
|
|
61
56
|
}, z.core.$strict>>;
|
|
62
57
|
/** Random replay capability reserved before consuming a claim credential. */
|
|
@@ -126,17 +121,15 @@ export declare function localSpaceStateDir(root: string): string;
|
|
|
126
121
|
export declare function localSpaceStatePath(root: string): string;
|
|
127
122
|
export declare function localSpaceLinkPath(root: string): string;
|
|
128
123
|
export declare function readLocalSpaceLink(root: string): Promise<LocalSpaceLink | null>;
|
|
129
|
-
/** Like readLocalSpaceLink, but reports the file path alongside the link. */
|
|
130
124
|
export declare function readLocalSpaceLinkWithPath(root: string): Promise<{
|
|
131
125
|
link: LocalSpaceLink;
|
|
132
126
|
path: string;
|
|
133
127
|
} | null>;
|
|
134
128
|
export declare function readLocalSpaceStateAt(root: string): Promise<DiscoveredLocalSpaceState | null>;
|
|
135
129
|
/**
|
|
136
|
-
* Walk up from `startDir` to the filesystem root (or `boundary
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
* discovery) so the traversal rules live in one place.
|
|
130
|
+
* Walk up from `startDir` to the filesystem root (or `boundary`), returning the
|
|
131
|
+
* first non-null `probe` result. Every "nearest checkout marker" lookup shares
|
|
132
|
+
* it so the traversal rules live in one place.
|
|
140
133
|
*/
|
|
141
134
|
export declare function findUpward<T>(probe: (dir: string) => Promise<T | null>, startDir?: string, boundary?: string): Promise<T | null>;
|
|
142
135
|
export declare function discoverLocalSpaceState(startDir?: string, boundary?: string): Promise<DiscoveredLocalSpaceState | null>;
|
|
@@ -146,9 +139,9 @@ export declare function mutateLocalSpaceState(root: string, mutate: (current: Lo
|
|
|
146
139
|
afterBackup?: (() => void | Promise<void>) | undefined;
|
|
147
140
|
/** Fault-injection observer used to synchronize concurrency tests. */
|
|
148
141
|
onConflict?: (() => void | Promise<void>) | undefined;
|
|
142
|
+
/** Reports whether this mutation changed `.gitignore`. */
|
|
143
|
+
onGitignoreResult?: ((changed: boolean) => void) | undefined;
|
|
149
144
|
}): Promise<LocalSpaceStateFile>;
|
|
150
|
-
export declare function writeLocalSpaceState(root: string, state: LocalSpaceStateWrite): Promise<string>;
|
|
151
|
-
/** Reserve random replay custody before a claim token can be consumed. */
|
|
152
145
|
export declare function reserveLocalCredentialReplay(root: string, input: {
|
|
153
146
|
operation: LocalCredentialReplayOperation;
|
|
154
147
|
claimToken: string;
|
|
@@ -158,4 +151,8 @@ export declare function saveLocalSpaceState(root: string, input: SaveLocalSpaceS
|
|
|
158
151
|
statePath: string;
|
|
159
152
|
linkPath: string;
|
|
160
153
|
wroteLink: boolean;
|
|
154
|
+
gitignore: {
|
|
155
|
+
path: string;
|
|
156
|
+
changed: boolean;
|
|
157
|
+
};
|
|
161
158
|
}>;
|
|
@@ -10,16 +10,13 @@ export const LOCAL_SPACE_STATE_FILE_NAME = "state.json";
|
|
|
10
10
|
/**
|
|
11
11
|
* Local Spacefast checkout contract.
|
|
12
12
|
*
|
|
13
|
-
* `.spacefast/space.json` is the committable selection axis:
|
|
14
|
-
*
|
|
15
|
-
* coordinates (`team`, `apiBaseUrl`). It must not carry credentials.
|
|
13
|
+
* `.spacefast/space.json` is the committable selection axis: which space a
|
|
14
|
+
* checkout targets, plus non-secret provider/team coordinates. Never credentials.
|
|
16
15
|
*
|
|
17
|
-
* `.spacefast/state.json` is the
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* The strict versioned schema fails closed instead of letting an older tool
|
|
22
|
-
* reinterpret fields written under a contract it does not understand.
|
|
16
|
+
* `.spacefast/state.json` is the secret axis: credentials and small publish
|
|
17
|
+
* caches, machine-written, gitignored, merge-written. Its strict versioned
|
|
18
|
+
* schema fails closed rather than let an older tool reinterpret fields written
|
|
19
|
+
* under a contract it does not understand.
|
|
23
20
|
*/
|
|
24
21
|
export const localSpaceLinkSchema = z
|
|
25
22
|
.object({
|
|
@@ -29,6 +26,17 @@ export const localSpaceLinkSchema = z
|
|
|
29
26
|
})
|
|
30
27
|
.loose();
|
|
31
28
|
export const LOCAL_SPACE_STATE_SCHEMA_VERSION = 1;
|
|
29
|
+
// One reservation, two slots: it is written as `pendingPublish` and moved to
|
|
30
|
+
// `creationReplay` once its receipt commits. Space creation is the only
|
|
31
|
+
// mutation that reserves one. Credential-free publishes are not idempotent.
|
|
32
|
+
export const creationAttemptSchema = z
|
|
33
|
+
.object({
|
|
34
|
+
key: z.string().min(1),
|
|
35
|
+
principal: z.string().regex(/^[0-9a-f]{64}$/),
|
|
36
|
+
kind: z.literal("authenticated"),
|
|
37
|
+
createdAt: z.string().datetime(),
|
|
38
|
+
})
|
|
39
|
+
.strict();
|
|
32
40
|
export const localSpaceStateSchema = z.strictObject({
|
|
33
41
|
schemaVersion: z.literal(LOCAL_SPACE_STATE_SCHEMA_VERSION),
|
|
34
42
|
spaceId: z.string().min(1).optional(),
|
|
@@ -45,27 +53,9 @@ export const localSpaceStateSchema = z.strictObject({
|
|
|
45
53
|
/** Secret per-checkout entropy used only to namespace replay-safe first publishes. */
|
|
46
54
|
anonymousPublishNonce: z.string().min(32).optional(),
|
|
47
55
|
/** Unsettled first-publish mutation. Retries must reuse this exact key. */
|
|
48
|
-
pendingPublish:
|
|
49
|
-
.object({
|
|
50
|
-
key: z.string().min(1),
|
|
51
|
-
principal: z.string().regex(/^[0-9a-f]{64}$/),
|
|
52
|
-
kind: z.enum(["anonymous", "authenticated"]),
|
|
53
|
-
intent: z.string().min(1).optional(),
|
|
54
|
-
createdAt: z.string().datetime(),
|
|
55
|
-
})
|
|
56
|
-
.strict()
|
|
57
|
-
.optional(),
|
|
56
|
+
pendingPublish: creationAttemptSchema.optional(),
|
|
58
57
|
/** Settled creation receipt identity retained for invocations that began before its commit. */
|
|
59
|
-
creationReplay:
|
|
60
|
-
.object({
|
|
61
|
-
key: z.string().min(1),
|
|
62
|
-
principal: z.string().regex(/^[0-9a-f]{64}$/),
|
|
63
|
-
kind: z.enum(["anonymous", "authenticated"]),
|
|
64
|
-
intent: z.string().min(1).optional(),
|
|
65
|
-
createdAt: z.string().datetime(),
|
|
66
|
-
})
|
|
67
|
-
.strict()
|
|
68
|
-
.optional(),
|
|
58
|
+
creationReplay: creationAttemptSchema.optional(),
|
|
69
59
|
/** Random replay capability reserved before consuming a claim credential. */
|
|
70
60
|
pendingCredentialReplay: z
|
|
71
61
|
.object({
|
|
@@ -152,7 +142,6 @@ function linkSpaceId(link) {
|
|
|
152
142
|
export async function readLocalSpaceLink(root) {
|
|
153
143
|
return (await readLocalSpaceLinkWithPath(root))?.link ?? null;
|
|
154
144
|
}
|
|
155
|
-
/** Like readLocalSpaceLink, but reports the file path alongside the link. */
|
|
156
145
|
export async function readLocalSpaceLinkWithPath(root) {
|
|
157
146
|
const primary = parseLink(await readJsonFile(root, path.join(LOCAL_SPACE_STATE_DIR_NAME, LOCAL_SPACE_LINK_FILE_NAME)));
|
|
158
147
|
return primary ? { link: primary, path: localSpaceLinkPath(root) } : null;
|
|
@@ -190,10 +179,9 @@ function isWithinBoundary(current, boundary) {
|
|
|
190
179
|
return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative));
|
|
191
180
|
}
|
|
192
181
|
/**
|
|
193
|
-
* Walk up from `startDir` to the filesystem root (or `boundary
|
|
194
|
-
*
|
|
195
|
-
*
|
|
196
|
-
* discovery) so the traversal rules live in one place.
|
|
182
|
+
* Walk up from `startDir` to the filesystem root (or `boundary`), returning the
|
|
183
|
+
* first non-null `probe` result. Every "nearest checkout marker" lookup shares
|
|
184
|
+
* it so the traversal rules live in one place.
|
|
197
185
|
*/
|
|
198
186
|
export async function findUpward(probe, startDir = process.cwd(), boundary) {
|
|
199
187
|
let current = path.resolve(startDir);
|
|
@@ -258,7 +246,7 @@ async function ensureStateGitignored(root, onConflict) {
|
|
|
258
246
|
const lines = raw.split(/\r?\n/);
|
|
259
247
|
const missing = entries.filter((entry) => !lines.includes(entry));
|
|
260
248
|
if (missing.length === 0)
|
|
261
|
-
return;
|
|
249
|
+
return false;
|
|
262
250
|
const prefix = raw.length > 0 && !raw.endsWith("\n") ? "\n" : "";
|
|
263
251
|
await secureWriteFile({
|
|
264
252
|
root,
|
|
@@ -268,7 +256,7 @@ async function ensureStateGitignored(root, onConflict) {
|
|
|
268
256
|
mode: 0o644,
|
|
269
257
|
expectedContent: existing,
|
|
270
258
|
});
|
|
271
|
-
return;
|
|
259
|
+
return true;
|
|
272
260
|
}
|
|
273
261
|
catch (error) {
|
|
274
262
|
if (error instanceof SecureLocalFileConflictError)
|
|
@@ -281,6 +269,7 @@ async function ensureStateGitignored(root, onConflict) {
|
|
|
281
269
|
}
|
|
282
270
|
}
|
|
283
271
|
/* eslint-enable no-await-in-loop */
|
|
272
|
+
throw new LocalSpaceStateConflictError("Local .gitignore stayed busy for too long.");
|
|
284
273
|
}
|
|
285
274
|
function assignDefined(target, input) {
|
|
286
275
|
for (const [key, value] of Object.entries(input)) {
|
|
@@ -315,7 +304,8 @@ export async function writeLocalSpaceLink(root, link) {
|
|
|
315
304
|
}
|
|
316
305
|
export async function mutateLocalSpaceState(root, mutate, options = {}) {
|
|
317
306
|
const stateRelativePath = path.join(LOCAL_SPACE_STATE_DIR_NAME, LOCAL_SPACE_STATE_FILE_NAME);
|
|
318
|
-
await ensureStateGitignored(root, options.onConflict);
|
|
307
|
+
const gitignoreChanged = await ensureStateGitignored(root, options.onConflict);
|
|
308
|
+
options.onGitignoreResult?.(gitignoreChanged);
|
|
319
309
|
/* eslint-disable no-await-in-loop -- compare-and-swap retries are intentionally serialized against the latest committed state. */
|
|
320
310
|
for (let attempt = 1; attempt <= LOCAL_STATE_MUTATION_ATTEMPTS; attempt += 1) {
|
|
321
311
|
try {
|
|
@@ -327,8 +317,8 @@ export async function mutateLocalSpaceState(root, mutate, options = {}) {
|
|
|
327
317
|
const current = parsed?.success
|
|
328
318
|
? parsed.data
|
|
329
319
|
: { schemaVersion: LOCAL_SPACE_STATE_SCHEMA_VERSION };
|
|
330
|
-
// The callback may run again after a compare-and-swap collision
|
|
331
|
-
// synchronous
|
|
320
|
+
// The callback may run again after a compare-and-swap collision; keeping
|
|
321
|
+
// it synchronous rules out one-shot work inside it.
|
|
332
322
|
const payload = localSpaceStateSchema.parse({
|
|
333
323
|
...mutate(structuredClone(current)),
|
|
334
324
|
schemaVersion: LOCAL_SPACE_STATE_SCHEMA_VERSION,
|
|
@@ -360,10 +350,6 @@ export async function mutateLocalSpaceState(root, mutate, options = {}) {
|
|
|
360
350
|
/* eslint-enable no-await-in-loop */
|
|
361
351
|
throw new LocalSpaceStateConflictError("Local Spacefast state stayed busy for too long.");
|
|
362
352
|
}
|
|
363
|
-
export async function writeLocalSpaceState(root, state) {
|
|
364
|
-
await mutateLocalSpaceState(root, () => state);
|
|
365
|
-
return localSpaceStatePath(root);
|
|
366
|
-
}
|
|
367
353
|
function credentialFingerprint(credential) {
|
|
368
354
|
return createHash("sha256").update(credential).digest("hex");
|
|
369
355
|
}
|
|
@@ -373,7 +359,6 @@ function sameCredentialReplay(left, right) {
|
|
|
373
359
|
left.key === right.key &&
|
|
374
360
|
left.principal === right.principal);
|
|
375
361
|
}
|
|
376
|
-
/** Reserve random replay custody before a claim token can be consumed. */
|
|
377
362
|
export async function reserveLocalCredentialReplay(root, input) {
|
|
378
363
|
const identity = createOneShotReplayIdentity(`sf-${input.operation}`);
|
|
379
364
|
const candidate = {
|
|
@@ -407,6 +392,7 @@ function credentialMatches(state, expected) {
|
|
|
407
392
|
return state.claimToken === expected.claimToken && state.accessToken === expected.accessToken;
|
|
408
393
|
}
|
|
409
394
|
export async function saveLocalSpaceState(root, input) {
|
|
395
|
+
let gitignoreChanged = false;
|
|
410
396
|
await mutateLocalSpaceState(root, (existing) => {
|
|
411
397
|
if (existing.spaceId && existing.spaceId !== input.spaceId && !input.replaceSpace) {
|
|
412
398
|
throw new LocalSpaceStateConflictError(`Local Spacefast state changed from ${input.spaceId} to ${existing.spaceId}.`);
|
|
@@ -484,6 +470,10 @@ export async function saveLocalSpaceState(root, input) {
|
|
|
484
470
|
delete next.pendingCredentialReplay;
|
|
485
471
|
}
|
|
486
472
|
return localSpaceStateSchema.parse(next);
|
|
473
|
+
}, {
|
|
474
|
+
onGitignoreResult: (changed) => {
|
|
475
|
+
gitignoreChanged = changed;
|
|
476
|
+
},
|
|
487
477
|
});
|
|
488
478
|
const statePath = localSpaceStatePath(root);
|
|
489
479
|
const linkRelativePath = path.join(LOCAL_SPACE_STATE_DIR_NAME, LOCAL_SPACE_LINK_FILE_NAME);
|
|
@@ -506,7 +496,12 @@ export async function saveLocalSpaceState(root, input) {
|
|
|
506
496
|
throw new LocalSpaceStateConflictError(`The committed checkout link selects ${inheritedLink.space}, not ${input.spaceId}.`);
|
|
507
497
|
}
|
|
508
498
|
if (inheritedLink) {
|
|
509
|
-
return {
|
|
499
|
+
return {
|
|
500
|
+
statePath,
|
|
501
|
+
linkPath: localSpaceLinkPath(root),
|
|
502
|
+
wroteLink,
|
|
503
|
+
gitignore: { path: path.join(root, ".gitignore"), changed: gitignoreChanged },
|
|
504
|
+
};
|
|
510
505
|
}
|
|
511
506
|
}
|
|
512
507
|
try {
|
|
@@ -530,5 +525,10 @@ export async function saveLocalSpaceState(root, input) {
|
|
|
530
525
|
else if (primaryLink.space !== input.spaceId) {
|
|
531
526
|
throw new LocalSpaceStateConflictError(`The committed checkout link selects ${primaryLink.space}, not ${input.spaceId}.`);
|
|
532
527
|
}
|
|
533
|
-
return {
|
|
528
|
+
return {
|
|
529
|
+
statePath,
|
|
530
|
+
linkPath: localSpaceLinkPath(root),
|
|
531
|
+
wroteLink,
|
|
532
|
+
gitignore: { path: path.join(root, ".gitignore"), changed: gitignoreChanged },
|
|
533
|
+
};
|
|
534
534
|
}
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The signed
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* the URL (a returnTo, a tracking param) must be dropped before the query is
|
|
6
|
-
* sent back, or verification fails.
|
|
2
|
+
* The signed authorize continuation in `query`, or null when there isn't one.
|
|
3
|
+
* The signature covers only the params `ba_param` names, so anything that
|
|
4
|
+
* joined the URL later must be dropped or verification fails.
|
|
7
5
|
*/
|
|
8
6
|
export declare function signedOAuthQuerySubset(query: string): string | null;
|
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
// Better Auth
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
// Verification hashes the query verbatim, so it must round-trip byte-exact.
|
|
1
|
+
// Better Auth redirects interaction prompts to the dashboard with the entire
|
|
2
|
+
// authorize query, HMAC-signed. Verification hashes it verbatim, so the query
|
|
3
|
+
// must round-trip byte-exact.
|
|
5
4
|
/**
|
|
6
|
-
* The signed
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* the URL (a returnTo, a tracking param) must be dropped before the query is
|
|
10
|
-
* sent back, or verification fails.
|
|
5
|
+
* The signed authorize continuation in `query`, or null when there isn't one.
|
|
6
|
+
* The signature covers only the params `ba_param` names, so anything that
|
|
7
|
+
* joined the URL later must be dropped or verification fails.
|
|
11
8
|
*/
|
|
12
9
|
export function signedOAuthQuerySubset(query) {
|
|
13
10
|
const params = new URLSearchParams(query);
|
|
@@ -3,12 +3,13 @@ export type OneShotReplayIdentity = {
|
|
|
3
3
|
principal: string;
|
|
4
4
|
};
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* caller holding only that old credential therefore cannot locate the stored
|
|
10
|
-
* response after the credential is revoked.
|
|
6
|
+
* An unguessable capability for replaying one shown-once response. Neither
|
|
7
|
+
* value derives from the credential the mutation consumes, so a caller holding
|
|
8
|
+
* only that revoked credential cannot locate the stored response.
|
|
11
9
|
*/
|
|
12
10
|
export declare function createOneShotReplayIdentity(prefix: string): OneShotReplayIdentity;
|
|
13
11
|
/** Production headers for one one-shot attempt (or one device-poll sequence). */
|
|
14
|
-
export declare function oneShotReplayHeaders(identity: OneShotReplayIdentity, sequence?: number):
|
|
12
|
+
export declare function oneShotReplayHeaders(identity: OneShotReplayIdentity, sequence?: number): {
|
|
13
|
+
"idempotency-key": string;
|
|
14
|
+
"x-spacefast-idempotency-principal": string;
|
|
15
|
+
};
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import { randomBytes } from "node:crypto";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* caller holding only that old credential therefore cannot locate the stored
|
|
7
|
-
* response after the credential is revoked.
|
|
3
|
+
* An unguessable capability for replaying one shown-once response. Neither
|
|
4
|
+
* value derives from the credential the mutation consumes, so a caller holding
|
|
5
|
+
* only that revoked credential cannot locate the stored response.
|
|
8
6
|
*/
|
|
9
7
|
export function createOneShotReplayIdentity(prefix) {
|
|
10
8
|
return {
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import type { SpacePageTheme } from "../contracts/space-config.js";
|
|
2
|
-
/** Default page palette
|
|
2
|
+
/** Default page palette, mirrored into the shared template CSS. */
|
|
3
3
|
export declare const PAGE_COLOR_DEFAULTS: {
|
|
4
|
-
readonly accent: "#
|
|
5
|
-
readonly bg: "#
|
|
6
|
-
readonly fg: "#
|
|
7
|
-
readonly muted: "#
|
|
8
|
-
readonly card: "#
|
|
9
|
-
readonly border: "#
|
|
10
|
-
readonly darkBg: "#
|
|
11
|
-
readonly darkFg: "#
|
|
12
|
-
readonly darkMuted: "#
|
|
13
|
-
readonly darkCard: "#
|
|
14
|
-
readonly darkBorder: "#
|
|
4
|
+
readonly accent: "#d8f24b";
|
|
5
|
+
readonly bg: "#f7f5ef";
|
|
6
|
+
readonly fg: "#0c0c0c";
|
|
7
|
+
readonly muted: "#6e6a5e";
|
|
8
|
+
readonly card: "#f7f5ef";
|
|
9
|
+
readonly border: "#e5e1d4";
|
|
10
|
+
readonly darkBg: "#0c0c0c";
|
|
11
|
+
readonly darkFg: "#f0eee6";
|
|
12
|
+
readonly darkMuted: "#a6a193";
|
|
13
|
+
readonly darkCard: "#0c0c0c";
|
|
14
|
+
readonly darkBorder: "#2b2823";
|
|
15
15
|
};
|
|
16
16
|
export type PageSchemeColors = {
|
|
17
17
|
bg: string;
|
|
@@ -26,7 +26,6 @@ export type DerivedPageColors = {
|
|
|
26
26
|
scheme: "adaptive" | "light" | "dark";
|
|
27
27
|
/** The accent exactly as pushed (fills keep the brand color verbatim). */
|
|
28
28
|
accent: string;
|
|
29
|
-
/** Label color on accent fills. */
|
|
30
29
|
onAccent: string;
|
|
31
30
|
/** Accent-derived link color, readable on the light (or pinned) surface. */
|
|
32
31
|
link: string;
|
|
@@ -36,15 +35,12 @@ export type DerivedPageColors = {
|
|
|
36
35
|
pinned?: PageSchemeColors;
|
|
37
36
|
};
|
|
38
37
|
/**
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
* cannot reach the target (e.g. a mid-gray surface).
|
|
38
|
+
* Moves `color`'s OKLCH lightness (hue and chroma preserved) just far enough
|
|
39
|
+
* to meet `target` on `surface`. Falls back to black or white when the full
|
|
40
|
+
* lightness range cannot reach it, as on a mid-gray surface.
|
|
43
41
|
*/
|
|
44
42
|
export declare function readableOn(color: string, surface: string, target: number): string;
|
|
45
|
-
/** Button-label color for an accent fill: white when it can carry, else ink. */
|
|
46
43
|
export declare function onAccentColor(accent: string): string;
|
|
47
|
-
/** The one entry point: raw theme in, everything the templates paint with out. */
|
|
48
44
|
export declare function derivePageColors(theme: Pick<SpacePageTheme, "accent" | "background">): DerivedPageColors;
|
|
49
45
|
/** Hex form of any schema-accepted color, for color-picker inputs. */
|
|
50
46
|
export declare function pageColorToHex(value: string): string | null;
|