@spacefast/common 0.0.21 → 0.0.24
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/dist/agents/connect-targets.d.ts +9 -6
- package/dist/agents/connect-targets.js +9 -6
- package/dist/config/domains.d.ts +1 -1
- package/dist/config/domains.js +1 -0
- package/dist/config/index.d.ts +0 -1
- package/dist/config/index.js +0 -1
- package/dist/contracts/abuse.d.ts +2 -0
- package/dist/contracts/abuse.js +12 -0
- package/dist/contracts/access.d.ts +121 -9
- package/dist/contracts/access.js +73 -9
- package/dist/contracts/activity.d.ts +1 -1
- package/dist/contracts/activity.js +0 -9
- package/dist/contracts/api-keys.d.ts +21 -21
- package/dist/contracts/beta.js +1 -1
- package/dist/contracts/billing.d.ts +1 -34
- package/dist/contracts/billing.js +4 -53
- package/dist/contracts/builds.d.ts +8 -8
- package/dist/contracts/builds.js +10 -3
- package/dist/contracts/cli.d.ts +10 -0
- package/dist/contracts/cli.js +10 -0
- package/dist/contracts/collab-public.d.ts +63 -0
- package/dist/contracts/collab-public.js +46 -0
- package/dist/contracts/comments.d.ts +283 -54
- package/dist/contracts/comments.js +173 -52
- package/dist/contracts/common.d.ts +3 -3
- package/dist/contracts/device-auth.d.ts +6 -6
- package/dist/contracts/device-auth.js +1 -1
- package/dist/contracts/docs.d.ts +5 -5
- package/dist/contracts/domains.d.ts +36 -36
- package/dist/contracts/enums.d.ts +33 -30
- package/dist/contracts/enums.js +52 -38
- package/dist/contracts/error-code-meta.d.ts +37 -126
- package/dist/contracts/error-code-meta.js +12 -41
- package/dist/contracts/error-codes.d.ts +1 -1
- package/dist/contracts/error-codes.js +10 -32
- package/dist/contracts/execution.d.ts +35 -57
- package/dist/contracts/execution.js +41 -40
- package/dist/contracts/feature-lifecycle-core.d.ts +0 -6
- package/dist/contracts/feature-lifecycle-core.js +2 -31
- package/dist/contracts/feature-lifecycle.d.ts +8 -38
- package/dist/contracts/feature-lifecycle.js +16 -48
- package/dist/contracts/features.d.ts +15 -13
- package/dist/contracts/features.js +39 -38
- package/dist/contracts/functions.d.ts +97 -70
- package/dist/contracts/functions.js +101 -34
- package/dist/contracts/git.d.ts +1 -1
- package/dist/contracts/grants.d.ts +7 -3
- package/dist/contracts/grants.js +18 -9
- package/dist/contracts/internal.d.ts +14 -1
- package/dist/contracts/internal.js +17 -7
- package/dist/contracts/oauth-resources.d.ts +4 -3
- package/dist/contracts/oauth-resources.js +12 -3
- package/dist/contracts/operations.d.ts +90 -5
- package/dist/contracts/operations.js +75 -1
- package/dist/contracts/pages.d.ts +6 -1
- package/dist/contracts/pages.js +24 -1
- package/dist/contracts/plan-policy.d.ts +1 -3
- package/dist/contracts/plan-policy.js +1 -2
- package/dist/contracts/platform.d.ts +61 -148
- package/dist/contracts/platform.js +70 -126
- package/dist/contracts/publish-archive.d.ts +498 -0
- package/dist/contracts/publish-archive.js +25 -0
- package/dist/contracts/resources.d.ts +12 -24
- package/dist/contracts/resources.js +32 -12
- package/dist/contracts/runtime-api.d.ts +369 -175
- package/dist/contracts/runtime-api.js +372 -348
- package/dist/contracts/runtime-app.d.ts +127 -120
- package/dist/contracts/runtime-app.js +19 -10
- package/dist/contracts/runtime-db.d.ts +1 -1
- package/dist/contracts/runtime-services.d.ts +219 -0
- package/dist/contracts/runtime-services.js +437 -0
- package/dist/contracts/runtime-storage.d.ts +45 -32
- package/dist/contracts/runtime-storage.js +30 -8
- package/dist/contracts/space-config.d.ts +10 -16
- package/dist/contracts/space-config.js +14 -10
- package/dist/contracts/spaces.d.ts +241 -436
- package/dist/contracts/spaces.js +128 -145
- package/dist/contracts/superadmin-activity.d.ts +57 -4
- package/dist/contracts/superadmin-activity.js +48 -2
- package/dist/contracts/superadmin-search.d.ts +126 -0
- package/dist/contracts/superadmin-search.js +61 -0
- package/dist/contracts/superadmin-spaces.d.ts +358 -89
- package/dist/contracts/superadmin-spaces.js +97 -24
- package/dist/contracts/superadmin-teams.d.ts +28 -10
- package/dist/contracts/superadmin-teams.js +5 -8
- package/dist/contracts/superadmin.d.ts +0 -32
- package/dist/contracts/superadmin.js +3 -11
- package/dist/contracts/tags.d.ts +9 -9
- package/dist/contracts/teams.d.ts +44 -2
- package/dist/contracts/teams.js +43 -23
- package/dist/contracts/transfers.d.ts +0 -49
- package/dist/contracts/transfers.js +2 -6
- package/dist/contracts/usage.d.ts +61 -0
- package/dist/contracts/usage.js +64 -0
- package/dist/contracts/variables.d.ts +2 -7
- package/dist/contracts/variables.js +6 -5
- package/dist/contracts/webhooks.d.ts +2 -2
- package/dist/contracts/zero.d.ts +87 -56
- package/dist/contracts/zero.js +82 -20
- package/dist/dashboard-paths/index.d.ts +1 -1
- package/dist/dashboard-paths/index.js +1 -0
- package/dist/docs/agent-prose.d.ts +1 -1
- package/dist/docs/agent-prose.js +62 -66
- package/dist/docs/agent-setup.js +49 -23
- package/dist/docs/agent-solutions.d.ts +4 -4
- package/dist/docs/agent-solutions.js +8 -8
- package/dist/docs/catalog.d.ts +19 -19
- package/dist/docs/catalog.js +30 -19
- package/dist/docs/error-docs.js +63 -151
- package/dist/docs/skills.d.ts +3 -3
- package/dist/docs/skills.js +1 -1
- package/dist/test-helpers/fetch-stub.d.ts +16 -0
- package/dist/test-helpers/fetch-stub.js +24 -0
- package/dist/utils/build-settings.js +9 -3
- package/dist/utils/canonical-json.d.ts +10 -0
- package/dist/utils/canonical-json.js +21 -0
- package/dist/utils/cast-transport.d.ts +28 -0
- package/dist/utils/cast-transport.js +52 -0
- package/dist/utils/claim-token.d.ts +0 -8
- package/dist/utils/claim-token.js +3 -15
- package/dist/utils/credential-policy.d.ts +1 -1
- package/dist/utils/env-file.d.ts +17 -0
- package/dist/utils/env-file.js +96 -0
- package/dist/utils/error-code.d.ts +2 -0
- package/dist/utils/error-code.js +7 -0
- package/dist/utils/generate-space-name.d.ts +8 -10
- package/dist/utils/generate-space-name.js +206 -21
- package/dist/utils/grant-decision.fixtures.json +2938 -0
- package/dist/utils/grants.d.ts +2 -0
- package/dist/utils/grants.js +25 -2
- package/dist/utils/gravatar.d.ts +9 -0
- package/dist/utils/gravatar.js +31 -0
- package/dist/utils/idempotency.d.ts +6 -0
- package/dist/utils/idempotency.js +28 -0
- package/dist/utils/local-space-state.d.ts +1 -5
- package/dist/utils/local-space-state.js +7 -26
- package/dist/utils/page-fonts.d.ts +2 -2
- package/dist/utils/page-fonts.js +22 -4
- package/dist/utils/pages.d.ts +1 -2
- package/dist/utils/pages.js +21 -5
- package/dist/utils/publish-form-data.d.ts +12 -0
- package/dist/utils/publish-form-data.js +10 -0
- package/dist/utils/publish-policy.d.ts +20 -0
- package/dist/utils/publish-policy.fixtures.json +397 -0
- package/dist/utils/publish-policy.js +36 -19
- package/dist/utils/push-new.d.ts +7 -0
- package/dist/utils/push-new.js +7 -0
- package/dist/utils/query-keys.d.ts +0 -1
- package/dist/utils/query-keys.js +0 -1
- package/dist/utils/runtime-paths.d.ts +5 -0
- package/dist/utils/runtime-paths.js +5 -0
- package/dist/utils/runtime-upload-batch.d.ts +0 -5
- package/dist/utils/runtime-upload-batch.js +1 -27
- package/dist/utils/runtime-upload.d.ts +3 -13
- package/dist/utils/runtime-upload.js +4 -14
- package/dist/utils/space-config.d.ts +0 -33
- package/dist/utils/space-config.js +5 -326
- package/dist/utils/storage-policy.d.ts +1 -1
- package/dist/utils/storage-policy.js +3 -1
- package/dist/utils/structured-error.d.ts +14 -0
- package/dist/utils/structured-error.js +22 -0
- package/dist/utils/version-path.js +12 -3
- package/dist/vocabulary.d.ts +5 -3
- package/dist/vocabulary.js +13 -10
- package/package.json +10 -5
- package/dist/config/postgres-budget.d.ts +0 -129
- package/dist/config/postgres-budget.js +0 -140
- package/dist/config/postgres-budget.json +0 -51
- package/dist/contracts/archives.d.ts +0 -331
- package/dist/contracts/archives.js +0 -120
- package/dist/contracts/deployments.d.ts +0 -574
- package/dist/contracts/deployments.js +0 -97
- package/dist/contracts/sites.d.ts +0 -416
- package/dist/contracts/sites.js +0 -168
- package/dist/contracts/superadmin-queues.d.ts +0 -414
- package/dist/contracts/superadmin-queues.js +0 -202
- package/dist/contracts/superadmin-runtime.d.ts +0 -27
- package/dist/contracts/superadmin-runtime.js +0 -38
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/utils/api.d.ts +0 -4
- package/dist/utils/api.js +0 -13
package/dist/utils/grants.d.ts
CHANGED
|
@@ -44,6 +44,8 @@ export type GrantEvaluation = {
|
|
|
44
44
|
matches: GrantMatch[];
|
|
45
45
|
};
|
|
46
46
|
export declare function grantResourcePatternMatches(pattern: string, resource: string, resourceKind?: "route" | "artifact"): boolean;
|
|
47
|
+
/** Does a Grant's resource set reach this path? Include, minus exclude. */
|
|
48
|
+
export declare function grantResourceSetMatches(resources: Grant["resources"], resource: string, resourceKind?: "route" | "artifact"): boolean;
|
|
47
49
|
export type GrantEvaluationExplanation = {
|
|
48
50
|
grantId: string;
|
|
49
51
|
failures: GrantCheckFailure[];
|
package/dist/utils/grants.js
CHANGED
|
@@ -28,7 +28,7 @@ function targetMatches(selector, target) {
|
|
|
28
28
|
case "branch":
|
|
29
29
|
return target.kind === "branch" && target.branch === selector.branch;
|
|
30
30
|
case "all_versions":
|
|
31
|
-
return target.kind === "version";
|
|
31
|
+
return target.kind === "live" || target.kind === "version";
|
|
32
32
|
}
|
|
33
33
|
throw new Error("Unsupported Grant target.");
|
|
34
34
|
}
|
|
@@ -70,6 +70,29 @@ export function grantResourcePatternMatches(pattern, resource, resourceKind = "r
|
|
|
70
70
|
};
|
|
71
71
|
return matches(0, 0);
|
|
72
72
|
}
|
|
73
|
+
/**
|
|
74
|
+
* Exclude patterns read wider than include patterns, and that asymmetry is the
|
|
75
|
+
* rule, not an accident: the route lane collapses a terminal `index.html`
|
|
76
|
+
* (normalizeAccessScopePath) while the artifact lane keeps it, so an exclude
|
|
77
|
+
* naming an index artifact must also close the route that artifact serves.
|
|
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
|
|
80
|
+
* (access-rules.php _stattic_grant_exclude_pattern_variants).
|
|
81
|
+
*/
|
|
82
|
+
function grantExcludePatternMatches(pattern, resource, resourceKind) {
|
|
83
|
+
if (grantResourcePatternMatches(pattern, resource, resourceKind))
|
|
84
|
+
return true;
|
|
85
|
+
const canonical = normalizeGrantResourcePattern(pattern);
|
|
86
|
+
if (!canonical.endsWith("/index.html"))
|
|
87
|
+
return false;
|
|
88
|
+
const stripped = canonical.slice(0, -"/index.html".length);
|
|
89
|
+
return grantResourcePatternMatches(stripped === "" ? "/" : stripped, resource, resourceKind);
|
|
90
|
+
}
|
|
91
|
+
/** Does a Grant's resource set reach this path? Include, minus exclude. */
|
|
92
|
+
export function grantResourceSetMatches(resources, resource, resourceKind = "route") {
|
|
93
|
+
return (resources.include.some((pattern) => grantResourcePatternMatches(pattern, resource, resourceKind)) &&
|
|
94
|
+
!resources.exclude.some((pattern) => grantExcludePatternMatches(pattern, resource, resourceKind)));
|
|
95
|
+
}
|
|
73
96
|
function grantCheckFailures(grant, input, principal, now) {
|
|
74
97
|
const failures = [];
|
|
75
98
|
if (grant.source.kind === "config" && !input.spaceClaimed) {
|
|
@@ -120,7 +143,7 @@ function grantCheckFailures(grant, input, principal, now) {
|
|
|
120
143
|
if (!included) {
|
|
121
144
|
failures.push("resource_not_included");
|
|
122
145
|
}
|
|
123
|
-
else if (grant.resources.exclude.some((pattern) =>
|
|
146
|
+
else if (grant.resources.exclude.some((pattern) => grantExcludePatternMatches(pattern, input.resource, input.resourceKind ?? "route"))) {
|
|
124
147
|
failures.push("resource_excluded");
|
|
125
148
|
}
|
|
126
149
|
return failures;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Gravatar's canonical address form: trimmed and lowercased. */
|
|
2
|
+
export declare function normalizeGravatarEmail(email: string | null | undefined): string | null;
|
|
3
|
+
/** The canonical avatar URL for an already-computed SHA-256 hash. */
|
|
4
|
+
export declare function gravatarUrlFromHash(hashHex: string): string;
|
|
5
|
+
/**
|
|
6
|
+
* SHA-256 hex of a normalized email — Gravatar's identifier. Uses WebCrypto so
|
|
7
|
+
* a browser can derive it locally and never send the raw address anywhere.
|
|
8
|
+
*/
|
|
9
|
+
export declare function gravatarEmailHash(email: string | null | undefined): Promise<string | null>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one Gravatar URL shape Spacefast emits. Every surface — control-plane
|
|
3
|
+
* profiles, the Comments SDK, Slack notifications — derives the same address
|
|
4
|
+
* for the same person, so an avatar is recognizable wherever it appears and
|
|
5
|
+
* Cast can validate a guest avatar against a single pattern.
|
|
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.
|
|
10
|
+
*/
|
|
11
|
+
const GRAVATAR_AVATAR_QUERY = "d=404&r=g&s=160";
|
|
12
|
+
/** Gravatar's canonical address form: trimmed and lowercased. */
|
|
13
|
+
export function normalizeGravatarEmail(email) {
|
|
14
|
+
const normalized = email?.trim().toLowerCase();
|
|
15
|
+
return normalized ? normalized : null;
|
|
16
|
+
}
|
|
17
|
+
/** The canonical avatar URL for an already-computed SHA-256 hash. */
|
|
18
|
+
export function gravatarUrlFromHash(hashHex) {
|
|
19
|
+
return `https://gravatar.com/avatar/${hashHex}?${GRAVATAR_AVATAR_QUERY}`;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* SHA-256 hex of a normalized email — Gravatar's identifier. Uses WebCrypto so
|
|
23
|
+
* a browser can derive it locally and never send the raw address anywhere.
|
|
24
|
+
*/
|
|
25
|
+
export async function gravatarEmailHash(email) {
|
|
26
|
+
const normalized = normalizeGravatarEmail(email);
|
|
27
|
+
if (!normalized || !globalThis.crypto?.subtle)
|
|
28
|
+
return null;
|
|
29
|
+
const digest = await globalThis.crypto.subtle.digest("SHA-256", new TextEncoder().encode(normalized));
|
|
30
|
+
return [...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
31
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stable, secret-safe key for one exact logical mutation attempt. Shared by
|
|
3
|
+
* every client surface: the same publish issued through the CLI and through
|
|
4
|
+
* MCP must land on one key or the API stops deduplicating the retry.
|
|
5
|
+
*/
|
|
6
|
+
export declare function stableIdempotencyKey(prefix: string, parts: unknown): string;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
/**
|
|
3
|
+
* Key-sorted JSON that treats an absent key and an `undefined` value as the
|
|
4
|
+
* same thing. Deliberately *not* `canonicalJson`: a key the CLI omits and one
|
|
5
|
+
* the MCP server sets to `undefined` describe the same mutation, so they have
|
|
6
|
+
* to hash alike. Merging the two spellings would change every minted key.
|
|
7
|
+
*/
|
|
8
|
+
function keyJson(value) {
|
|
9
|
+
if (Array.isArray(value)) {
|
|
10
|
+
return `[${value.map(keyJson).join(",")}]`;
|
|
11
|
+
}
|
|
12
|
+
if (value && typeof value === "object") {
|
|
13
|
+
return `{${Object.entries(value)
|
|
14
|
+
.filter(([, entry]) => entry !== undefined)
|
|
15
|
+
.toSorted(([left], [right]) => left.localeCompare(right))
|
|
16
|
+
.map(([key, entry]) => `${JSON.stringify(key)}:${keyJson(entry)}`)
|
|
17
|
+
.join(",")}}`;
|
|
18
|
+
}
|
|
19
|
+
return JSON.stringify(value ?? null);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Stable, secret-safe key for one exact logical mutation attempt. Shared by
|
|
23
|
+
* every client surface: the same publish issued through the CLI and through
|
|
24
|
+
* MCP must land on one key or the API stops deduplicating the retry.
|
|
25
|
+
*/
|
|
26
|
+
export function stableIdempotencyKey(prefix, parts) {
|
|
27
|
+
return `${prefix}-${createHash("sha256").update(keyJson(parts)).digest("hex")}`;
|
|
28
|
+
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
export declare const LOCAL_SPACE_STATE_DIR_NAME = ".spacefast";
|
|
3
|
-
export declare const LEGACY_LOCAL_SPACE_STATE_DIR_NAME = ".stattic";
|
|
4
3
|
export declare const LOCAL_SPACE_LINK_FILE_NAME = "space.json";
|
|
5
4
|
export declare const LOCAL_SPACE_STATE_FILE_NAME = "state.json";
|
|
6
5
|
/**
|
|
@@ -127,10 +126,7 @@ export declare function localSpaceStateDir(root: string): string;
|
|
|
127
126
|
export declare function localSpaceStatePath(root: string): string;
|
|
128
127
|
export declare function localSpaceLinkPath(root: string): string;
|
|
129
128
|
export declare function readLocalSpaceLink(root: string): Promise<LocalSpaceLink | null>;
|
|
130
|
-
/**
|
|
131
|
-
* Like readLocalSpaceLink, but reports WHICH file matched — callers surface the
|
|
132
|
-
* legacy `.stattic/space.json` path to users so migrations stay visible.
|
|
133
|
-
*/
|
|
129
|
+
/** Like readLocalSpaceLink, but reports the file path alongside the link. */
|
|
134
130
|
export declare function readLocalSpaceLinkWithPath(root: string): Promise<{
|
|
135
131
|
link: LocalSpaceLink;
|
|
136
132
|
path: string;
|
|
@@ -5,7 +5,6 @@ import { z } from "zod";
|
|
|
5
5
|
import { createOneShotReplayIdentity } from "./one-shot-replay.js";
|
|
6
6
|
import { SecureLocalFileConflictError, secureReadFile, secureWriteFile, } from "./secure-local-file.js";
|
|
7
7
|
export const LOCAL_SPACE_STATE_DIR_NAME = ".spacefast";
|
|
8
|
-
export const LEGACY_LOCAL_SPACE_STATE_DIR_NAME = ".stattic";
|
|
9
8
|
export const LOCAL_SPACE_LINK_FILE_NAME = "space.json";
|
|
10
9
|
export const LOCAL_SPACE_STATE_FILE_NAME = "state.json";
|
|
11
10
|
/**
|
|
@@ -99,9 +98,6 @@ export function localSpaceStatePath(root) {
|
|
|
99
98
|
export function localSpaceLinkPath(root) {
|
|
100
99
|
return path.join(localSpaceStateDir(root), LOCAL_SPACE_LINK_FILE_NAME);
|
|
101
100
|
}
|
|
102
|
-
function legacyLinkPath(root) {
|
|
103
|
-
return path.join(root, LEGACY_LOCAL_SPACE_STATE_DIR_NAME, LOCAL_SPACE_LINK_FILE_NAME);
|
|
104
|
-
}
|
|
105
101
|
async function readJsonFileWithRaw(root, relativePath) {
|
|
106
102
|
const raw = await secureReadFile({ root, relativePath, encoding: "utf8" }).catch((error) => {
|
|
107
103
|
if (error.code === "ENOENT")
|
|
@@ -156,28 +152,17 @@ function linkSpaceId(link) {
|
|
|
156
152
|
export async function readLocalSpaceLink(root) {
|
|
157
153
|
return (await readLocalSpaceLinkWithPath(root))?.link ?? null;
|
|
158
154
|
}
|
|
159
|
-
/**
|
|
160
|
-
* Like readLocalSpaceLink, but reports WHICH file matched — callers surface the
|
|
161
|
-
* legacy `.stattic/space.json` path to users so migrations stay visible.
|
|
162
|
-
*/
|
|
155
|
+
/** Like readLocalSpaceLink, but reports the file path alongside the link. */
|
|
163
156
|
export async function readLocalSpaceLinkWithPath(root) {
|
|
164
157
|
const primary = parseLink(await readJsonFile(root, path.join(LOCAL_SPACE_STATE_DIR_NAME, LOCAL_SPACE_LINK_FILE_NAME)));
|
|
165
|
-
|
|
166
|
-
return { link: primary, path: localSpaceLinkPath(root) };
|
|
167
|
-
}
|
|
168
|
-
const legacy = parseLink(await readJsonFile(root, path.join(LEGACY_LOCAL_SPACE_STATE_DIR_NAME, LOCAL_SPACE_LINK_FILE_NAME)));
|
|
169
|
-
return legacy ? { link: legacy, path: legacyLinkPath(root) } : null;
|
|
158
|
+
return primary ? { link: primary, path: localSpaceLinkPath(root) } : null;
|
|
170
159
|
}
|
|
171
160
|
export async function readLocalSpaceStateAt(root) {
|
|
172
161
|
const statePath = localSpaceStatePath(root);
|
|
173
162
|
const primaryState = await readStateFile(root, path.join(LOCAL_SPACE_STATE_DIR_NAME, LOCAL_SPACE_STATE_FILE_NAME));
|
|
174
|
-
const
|
|
175
|
-
? undefined
|
|
176
|
-
: await readStateFile(root, path.join(LEGACY_LOCAL_SPACE_STATE_DIR_NAME, LOCAL_SPACE_STATE_FILE_NAME));
|
|
177
|
-
const state = primaryState?.state ?? legacyState?.state ?? null;
|
|
163
|
+
const state = primaryState?.state ?? null;
|
|
178
164
|
const linkPath = localSpaceLinkPath(root);
|
|
179
|
-
const link = parseLink(await readJsonFile(root, path.join(LOCAL_SPACE_STATE_DIR_NAME, LOCAL_SPACE_LINK_FILE_NAME)))
|
|
180
|
-
parseLink(await readJsonFile(root, path.join(LEGACY_LOCAL_SPACE_STATE_DIR_NAME, LOCAL_SPACE_LINK_FILE_NAME)));
|
|
165
|
+
const link = parseLink(await readJsonFile(root, path.join(LOCAL_SPACE_STATE_DIR_NAME, LOCAL_SPACE_LINK_FILE_NAME)));
|
|
181
166
|
if (!state && !link)
|
|
182
167
|
return null;
|
|
183
168
|
const committedSpaceId = linkSpaceId(link);
|
|
@@ -334,11 +319,7 @@ export async function mutateLocalSpaceState(root, mutate, options = {}) {
|
|
|
334
319
|
/* eslint-disable no-await-in-loop -- compare-and-swap retries are intentionally serialized against the latest committed state. */
|
|
335
320
|
for (let attempt = 1; attempt <= LOCAL_STATE_MUTATION_ATTEMPTS; attempt += 1) {
|
|
336
321
|
try {
|
|
337
|
-
const
|
|
338
|
-
const legacyState = primaryState
|
|
339
|
-
? undefined
|
|
340
|
-
: await readJsonFileWithRaw(root, path.join(LEGACY_LOCAL_SPACE_STATE_DIR_NAME, LOCAL_SPACE_STATE_FILE_NAME));
|
|
341
|
-
const source = primaryState ?? legacyState;
|
|
322
|
+
const source = await readJsonFileWithRaw(root, stateRelativePath);
|
|
342
323
|
const parsed = source ? localSpaceStateSchema.safeParse(source.value) : null;
|
|
343
324
|
if (source && !parsed?.success) {
|
|
344
325
|
throw new Error(`Local Spacefast state at ${localSpaceStatePath(root)} is invalid.`);
|
|
@@ -353,14 +334,14 @@ export async function mutateLocalSpaceState(root, mutate, options = {}) {
|
|
|
353
334
|
schemaVersion: LOCAL_SPACE_STATE_SCHEMA_VERSION,
|
|
354
335
|
});
|
|
355
336
|
const serialized = `${JSON.stringify(payload, null, 2)}\n`;
|
|
356
|
-
if (
|
|
337
|
+
if (source?.raw === serialized)
|
|
357
338
|
return payload;
|
|
358
339
|
await writeJsonAtomic({
|
|
359
340
|
root,
|
|
360
341
|
relativePath: stateRelativePath,
|
|
361
342
|
value: payload,
|
|
362
343
|
mode: 0o600,
|
|
363
|
-
expectedContent:
|
|
344
|
+
expectedContent: source?.raw ?? null,
|
|
364
345
|
...(options.beforeCommit ? { beforeCommit: options.beforeCommit } : {}),
|
|
365
346
|
...(options.afterBackup ? { afterBackup: options.afterBackup } : {}),
|
|
366
347
|
});
|