@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
|
@@ -236,10 +236,9 @@ async function canonicalPortableWriteRoot(rootInput) {
|
|
|
236
236
|
async function openPortableChildDirectory(identities, visibleParent, component, create) {
|
|
237
237
|
assertSafeComponent(component);
|
|
238
238
|
const childPath = path.join(visibleParent, component);
|
|
239
|
-
// A missing directory is the normal "file not found"
|
|
240
|
-
//
|
|
241
|
-
//
|
|
242
|
-
// fails closed because an observed ancestor binding changed mid-read.
|
|
239
|
+
// A missing directory is the normal "file not found" for secure reads, so
|
|
240
|
+
// probe before identity capture. A later disappearance still fails closed in
|
|
241
|
+
// visibleDirectoryIdentity.
|
|
243
242
|
if (!create) {
|
|
244
243
|
await lstat(childPath);
|
|
245
244
|
}
|
|
@@ -273,7 +272,6 @@ function lockName(leaf) {
|
|
|
273
272
|
const digest = createHash("sha256").update(identity).digest("hex").slice(0, 24);
|
|
274
273
|
return `.spacefast-${digest}.lock`;
|
|
275
274
|
}
|
|
276
|
-
const LEGACY_LEAF_LOCK_STALE_MS = 2 * 60_000;
|
|
277
275
|
function serializeLeafLockOwner(owner) {
|
|
278
276
|
const { phase, ...identity } = owner;
|
|
279
277
|
return `${phase === "active" ? "A" : "R"}${JSON.stringify(identity)}\n`;
|
|
@@ -293,6 +291,7 @@ function parseLeafLockOwner(raw) {
|
|
|
293
291
|
(value.temporaryName === undefined ||
|
|
294
292
|
(typeof value.temporaryName === "string" &&
|
|
295
293
|
/^\.spacefast-[0-9a-f-]{36}\.tmp$/.test(value.temporaryName)))) {
|
|
294
|
+
// SAFETY: The preceding checks establish every required LeafLockOwner field and the optional temporaryName field.
|
|
296
295
|
return { ...value, phase };
|
|
297
296
|
}
|
|
298
297
|
}
|
|
@@ -360,14 +359,8 @@ function processIsAlive(pid) {
|
|
|
360
359
|
}
|
|
361
360
|
}
|
|
362
361
|
async function leafLockRecordIsStale(record) {
|
|
363
|
-
//
|
|
364
|
-
//
|
|
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.
|
|
362
|
+
// A malformed record is not proof that no writer owns it. New lock records are
|
|
363
|
+
// atomically published, so corruption still needs explicit recovery.
|
|
371
364
|
if (record?.kind !== "owned")
|
|
372
365
|
return false;
|
|
373
366
|
const { owner } = record;
|
|
@@ -399,13 +392,8 @@ async function readLeafLockRecord(filePath) {
|
|
|
399
392
|
visibleMetadata.ino !== metadata.ino) {
|
|
400
393
|
return { kind: "invalid" };
|
|
401
394
|
}
|
|
402
|
-
const
|
|
403
|
-
|
|
404
|
-
if (owner)
|
|
405
|
-
return { kind: "owned", owner };
|
|
406
|
-
return raw.length === 0
|
|
407
|
-
? { kind: "legacy", modifiedAtMs: metadata.mtimeMs }
|
|
408
|
-
: { kind: "invalid" };
|
|
395
|
+
const owner = parseLeafLockOwner(await file.readFile("utf8"));
|
|
396
|
+
return owner ? { kind: "owned", owner } : { kind: "invalid" };
|
|
409
397
|
}
|
|
410
398
|
finally {
|
|
411
399
|
await file.close();
|
|
@@ -476,8 +464,7 @@ async function unlinkOwnedLeafLockArtifact(lockPath, artifactPath, nonce) {
|
|
|
476
464
|
return;
|
|
477
465
|
}
|
|
478
466
|
// The pathname changed after its owner was inspected. Restore the exact inode
|
|
479
|
-
// when the name is
|
|
480
|
-
// one displaced so contenders detect it instead of deleting a fresh owner.
|
|
467
|
+
// when the name is free; otherwise keep it displaced so contenders detect it.
|
|
481
468
|
let restored = false;
|
|
482
469
|
try {
|
|
483
470
|
await link(retiredPath, artifactPath);
|
|
@@ -555,14 +542,12 @@ async function acquireLeafLock(lockPath, temporaryPath) {
|
|
|
555
542
|
if (released)
|
|
556
543
|
return;
|
|
557
544
|
released = true;
|
|
558
|
-
// Move the canonical name into unique
|
|
559
|
-
//
|
|
560
|
-
// publish, but cleanup only ever unlinks the retired name it inspected.
|
|
545
|
+
// Move the canonical name into unique custody while its inode is still
|
|
546
|
+
// active; cleanup only ever unlinks the retired name it inspected.
|
|
561
547
|
await unlinkOwnedLeafLockArtifact(lockPath, lockPath, owner.nonce).catch(() => undefined);
|
|
562
548
|
try {
|
|
563
|
-
// A delayed stale reclaimer may
|
|
564
|
-
//
|
|
565
|
-
// reclaimed even though this process remains alive.
|
|
549
|
+
// A delayed stale reclaimer may hold this inode at a displaced name.
|
|
550
|
+
// Mark it complete through the handle so it can still be reclaimed.
|
|
566
551
|
const releasedWrite = await acquiredHandle.write(Buffer.from("R"), 0, 1, 0);
|
|
567
552
|
if (releasedWrite.bytesWritten === 1)
|
|
568
553
|
await acquiredHandle.sync();
|
|
@@ -609,16 +594,15 @@ async function acquireLeafLock(lockPath, temporaryPath) {
|
|
|
609
594
|
}
|
|
610
595
|
const displaced = await readLeafLockRecord(displacedPath);
|
|
611
596
|
if (!(await leafLockRecordIsStale(displaced))) {
|
|
612
|
-
// A fresh owner won between our stale read and rename. Put its exact
|
|
613
|
-
// back
|
|
614
|
-
//
|
|
597
|
+
// A fresh owner won between our stale read and rename. Put its exact
|
|
598
|
+
// inode back; contenders scan displaced names before entering, so a live
|
|
599
|
+
// writer is never silently unlocked.
|
|
615
600
|
try {
|
|
616
601
|
await link(displacedPath, lockPath);
|
|
617
602
|
}
|
|
618
603
|
catch (error) {
|
|
619
604
|
if (isMissing(error)) {
|
|
620
|
-
// Its owner
|
|
621
|
-
// Nothing remains to restore; compete again for the now-current name.
|
|
605
|
+
// Its owner finished cleanup; compete again for the current name.
|
|
622
606
|
await delay(LEAF_LOCK_RETRY_DELAY_MS);
|
|
623
607
|
continue;
|
|
624
608
|
}
|
|
@@ -868,10 +852,9 @@ async function commitPortableStagedFile(input) {
|
|
|
868
852
|
}
|
|
869
853
|
}
|
|
870
854
|
async function secureWriteFilePortable(options) {
|
|
871
|
-
//
|
|
872
|
-
//
|
|
873
|
-
//
|
|
874
|
-
// component-by-component without following symlinks.
|
|
855
|
+
// Canonicalize the trusted root once so system aliases like macOS
|
|
856
|
+
// `/var -> /private/var` don't look like attacker-controlled children.
|
|
857
|
+
// Everything below it is still walked without following symlinks.
|
|
875
858
|
const root = await canonicalPortableWriteRoot(options.root);
|
|
876
859
|
const relativePath = validateRelativePath(options.relativePath, "write");
|
|
877
860
|
const components = relativePath.split(path.sep);
|
|
@@ -934,9 +917,9 @@ async function secureWriteFilePortable(options) {
|
|
|
934
917
|
}
|
|
935
918
|
/**
|
|
936
919
|
* Writes beneath an explicitly trusted root without following output-tree
|
|
937
|
-
* symlinks. Linux uses descriptor-relative operations; other
|
|
938
|
-
*
|
|
939
|
-
*
|
|
920
|
+
* symlinks. Linux uses descriptor-relative operations; other platforms use
|
|
921
|
+
* exclusive staging plus before-and-after ancestor identity validation, and
|
|
922
|
+
* fail closed if any binding changes.
|
|
940
923
|
*/
|
|
941
924
|
export async function secureWriteFile(options) {
|
|
942
925
|
if ((options.data === undefined) === (options.write === undefined)) {
|
|
@@ -3,7 +3,7 @@ import { type SpaceConfig, type SpaceConfigFile } from "../contracts/space-confi
|
|
|
3
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";
|
|
4
4
|
export type SpaceConfigIssue = {
|
|
5
5
|
severity: "info" | "warning" | "error";
|
|
6
|
-
code: SpaceConfigBlockingIssueCode | "single_html_index" | "files_mode_inferred";
|
|
6
|
+
code: SpaceConfigBlockingIssueCode | "single_html_index" | "files_mode_inferred" | "listing_shadows_fallback";
|
|
7
7
|
message: string;
|
|
8
8
|
/** JSON-path-lite pointer into the config shape (e.g. `meta.title`). */
|
|
9
9
|
path?: string;
|
|
@@ -16,18 +16,15 @@ type SpaceConfigBlockingIssue = SpaceConfigIssue & {
|
|
|
16
16
|
code: SpaceConfigBlockingIssueCode;
|
|
17
17
|
};
|
|
18
18
|
/**
|
|
19
|
-
* Keys a committed `sf.jsonc` owns outright
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* overlay. API intake refuses them rather than storing or dropping them.
|
|
19
|
+
* Keys a committed `sf.jsonc` owns outright. They are facts about a version's
|
|
20
|
+
* content, so they roll forward and back with it instead of living in the
|
|
21
|
+
* space's mutable overlay. API intake refuses them.
|
|
23
22
|
*/
|
|
24
|
-
export declare const SPACE_CONFIG_FILE_ONLY_KEYS: readonly ["access", "name", "runtime"];
|
|
23
|
+
export declare const SPACE_CONFIG_FILE_ONLY_KEYS: readonly ["access", "name", "runtime", "system"];
|
|
25
24
|
/**
|
|
26
|
-
* Validates a space `config` overlay at API intake
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* acceptable. Takes the raw request value, because the overlay schema strips
|
|
30
|
-
* file-only keys and this is what has to see them.
|
|
25
|
+
* Validates a space `config` overlay at API intake, returning the first
|
|
26
|
+
* blocking issue or null. Takes the RAW request value: the overlay schema
|
|
27
|
+
* strips file-only keys, and this is what has to see them.
|
|
31
28
|
*/
|
|
32
29
|
export declare function spaceConfigOverlayIssue(config: unknown): SpaceConfigBlockingIssue | null;
|
|
33
30
|
export declare function stripJsonComments(raw: string): string;
|
|
@@ -37,16 +34,16 @@ export type ResolvedServingConfig = {
|
|
|
37
34
|
path: string;
|
|
38
35
|
status: 200 | 404;
|
|
39
36
|
} | null;
|
|
40
|
-
/**
|
|
37
|
+
/** Serve flat `<name>.html` at its extensionless URL. */
|
|
41
38
|
cleanUrls: boolean;
|
|
42
39
|
listing: boolean;
|
|
43
40
|
viewer: boolean;
|
|
44
41
|
meta: NonNullable<SpaceConfig["meta"]> | null;
|
|
45
|
-
/** Theme tokens for every Spacefast-authored page
|
|
42
|
+
/** Theme tokens for every Spacefast-authored page. */
|
|
46
43
|
pageTheme: NonNullable<SpaceConfig["theme"]> | null;
|
|
47
|
-
/** Hidden experimental build-pipeline flag
|
|
44
|
+
/** Hidden experimental build-pipeline flag. */
|
|
48
45
|
experimentalGutenberg: boolean;
|
|
49
|
-
/**
|
|
46
|
+
/** Only honored when experimentalGutenberg is true. */
|
|
50
47
|
inject: NonNullable<SpaceConfig["inject"]> | null;
|
|
51
48
|
};
|
|
52
49
|
export type SpaceSettingsPatch = {
|
|
@@ -42,9 +42,7 @@ function capIssues(candidate) {
|
|
|
42
42
|
details: { count: templates.length, limit: SPACE_CONFIG_CAPS.templates },
|
|
43
43
|
});
|
|
44
44
|
}
|
|
45
|
-
// inject
|
|
46
|
-
// shape and byte caps fail with the stable codes here, before zod, so intake
|
|
47
|
-
// reports inject_invalid / inject_snippet_too_large instead of shape errors.
|
|
45
|
+
// inject is a hidden feature, honored only with experimental_gutenberg.
|
|
48
46
|
const inject = candidate.inject;
|
|
49
47
|
if (inject !== undefined) {
|
|
50
48
|
if (typeof inject !== "object" || inject === null || Array.isArray(inject)) {
|
|
@@ -57,6 +55,7 @@ function capIssues(candidate) {
|
|
|
57
55
|
}
|
|
58
56
|
else {
|
|
59
57
|
for (const key of ["head", "bodyStart", "bodyEnd", "noscript"]) {
|
|
58
|
+
// SAFETY: The enclosing branch proves inject is a non-null, non-array object.
|
|
60
59
|
const snippets = inject[key];
|
|
61
60
|
if (snippets === undefined) {
|
|
62
61
|
continue;
|
|
@@ -125,18 +124,15 @@ function capIssues(candidate) {
|
|
|
125
124
|
return issues;
|
|
126
125
|
}
|
|
127
126
|
/**
|
|
128
|
-
* Keys a committed `sf.jsonc` owns outright
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
* overlay. API intake refuses them rather than storing or dropping them.
|
|
127
|
+
* Keys a committed `sf.jsonc` owns outright. They are facts about a version's
|
|
128
|
+
* content, so they roll forward and back with it instead of living in the
|
|
129
|
+
* space's mutable overlay. API intake refuses them.
|
|
132
130
|
*/
|
|
133
|
-
export const SPACE_CONFIG_FILE_ONLY_KEYS = ["access", "name", "runtime"];
|
|
131
|
+
export const SPACE_CONFIG_FILE_ONLY_KEYS = ["access", "name", "runtime", "system"];
|
|
134
132
|
/**
|
|
135
|
-
* Validates a space `config` overlay at API intake
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
* acceptable. Takes the raw request value, because the overlay schema strips
|
|
139
|
-
* file-only keys and this is what has to see them.
|
|
133
|
+
* Validates a space `config` overlay at API intake, returning the first
|
|
134
|
+
* blocking issue or null. Takes the RAW request value: the overlay schema
|
|
135
|
+
* strips file-only keys, and this is what has to see them.
|
|
140
136
|
*/
|
|
141
137
|
export function spaceConfigOverlayIssue(config) {
|
|
142
138
|
if (config === null || config === undefined || typeof config !== "object") {
|
|
@@ -165,15 +161,15 @@ export function spaceConfigOverlayIssue(config) {
|
|
|
165
161
|
}
|
|
166
162
|
return capIssues(config)[0] ?? null;
|
|
167
163
|
}
|
|
168
|
-
//
|
|
169
|
-
//
|
|
170
|
-
// files the CLI reads (agent configs, exported platform env).
|
|
164
|
+
// The config file itself is parsed by the crate; this stays for the OTHER
|
|
165
|
+
// JSONC files the CLI reads (agent configs, exported platform env).
|
|
171
166
|
export function stripJsonComments(raw) {
|
|
172
167
|
let output = "";
|
|
173
168
|
let inString = false;
|
|
174
169
|
let inLineComment = false;
|
|
175
170
|
let inBlockComment = false;
|
|
176
171
|
for (let i = 0; i < raw.length; i += 1) {
|
|
172
|
+
// SAFETY: The loop condition proves i addresses a character in raw.
|
|
177
173
|
const char = raw[i];
|
|
178
174
|
const next = raw[i + 1];
|
|
179
175
|
if (inLineComment) {
|
|
@@ -220,6 +216,7 @@ export function stripJsonComments(raw) {
|
|
|
220
216
|
// Trailing comma: skip when the next non-whitespace/non-comment char closes.
|
|
221
217
|
let j = i + 1;
|
|
222
218
|
while (j < raw.length) {
|
|
219
|
+
// SAFETY: The loop condition proves j addresses a character in raw.
|
|
223
220
|
const ahead = raw[j];
|
|
224
221
|
if (/\s/.test(ahead)) {
|
|
225
222
|
j += 1;
|
|
@@ -1,18 +1,21 @@
|
|
|
1
|
+
import { INTERNAL_REDIRECT_RESPONSE_HEADERS, PLATFORM_MANAGED_RESPONSE_HEADER_PREFIXES, PLATFORM_MANAGED_RESPONSE_HEADERS } from "./static-runtime-policy.generated.js";
|
|
2
|
+
export { INTERNAL_REDIRECT_RESPONSE_HEADERS, PLATFORM_MANAGED_RESPONSE_HEADER_PREFIXES, PLATFORM_MANAGED_RESPONSE_HEADERS, };
|
|
1
3
|
export declare const PHP_LIKE_EXTENSIONS: string[];
|
|
2
|
-
export declare const INTERNAL_REDIRECT_RESPONSE_HEADERS: string[];
|
|
3
|
-
export declare const PLATFORM_MANAGED_RESPONSE_HEADERS: string[];
|
|
4
|
-
/** Reserved response-header namespaces owned by the platform's own serving signature. */
|
|
5
|
-
export declare const PLATFORM_MANAGED_RESPONSE_HEADER_PREFIXES: string[];
|
|
6
4
|
/** True when user rules may neither set nor remove this response header. */
|
|
7
5
|
export declare function isPlatformManagedResponseHeader(name: string): boolean;
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
/**
|
|
7
|
+
* The token grammar a `_headers` rule accepts for a header NAME. Anything that
|
|
8
|
+
* GENERATES `_headers` rules must gate on it too, so a generated rule can
|
|
9
|
+
* never fail the author's publish.
|
|
10
|
+
*/
|
|
11
|
+
export declare const HEADER_RULE_NAME_PATTERN: RegExp;
|
|
12
|
+
/** `content-type` → `Content-Type`: the casing compiled rules and generated `_headers` carry. */
|
|
13
|
+
export declare function canonicalHeaderName(input: string): string;
|
|
10
14
|
/** Lowercased extension without the leading dot, posix semantics. Dotfiles have no extension. */
|
|
11
15
|
export declare function pathExtension(filePath: string): string;
|
|
12
16
|
/** Basename without its extension (the "stem"), posix semantics. */
|
|
13
17
|
export declare function pathStem(filePath: string): string;
|
|
14
18
|
export declare function isPhpLikeStaticPath(filePath: string): boolean;
|
|
15
|
-
export declare function isImmutableAssetPath(filePath: string): boolean;
|
|
16
19
|
/** Forced inert serving policy for a committed static file. PHP-like files are never executable. */
|
|
17
20
|
export declare function runtimeFileSafety(filePath: string): {
|
|
18
21
|
executable: false;
|
|
@@ -1,44 +1,33 @@
|
|
|
1
1
|
{
|
|
2
2
|
"phpLike": [
|
|
3
|
-
{
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
{
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
{
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
{
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
{
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
{
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
{ "path": "assets/index.js", "expected": false },
|
|
32
|
-
{ "path": "assets/client-widget.js", "expected": false },
|
|
33
|
-
{ "path": "app.1a2b3c4d.css", "expected": false },
|
|
34
|
-
{ "path": "vendor.3f9a2b7e.js", "expected": false },
|
|
35
|
-
{ "path": "static/js/main.d41d8cd98f00b204e9800998.js", "expected": false },
|
|
36
|
-
{ "path": "index.html", "expected": false },
|
|
37
|
-
{ "path": "robots.txt", "expected": false },
|
|
38
|
-
{ "path": "config.xml", "expected": false },
|
|
39
|
-
{ "path": "page.php", "expected": false },
|
|
40
|
-
{ "path": "MyComponent.js", "expected": false },
|
|
41
|
-
{ "path": "image01.png", "expected": false }
|
|
3
|
+
{
|
|
4
|
+
"path": "index.php",
|
|
5
|
+
"expected": true
|
|
6
|
+
},
|
|
7
|
+
{
|
|
8
|
+
"path": "nested/app.phtml",
|
|
9
|
+
"expected": true
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"path": "lib.phar",
|
|
13
|
+
"expected": true
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"path": "legacy.php5",
|
|
17
|
+
"expected": true
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"path": "index.html",
|
|
21
|
+
"expected": false
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"path": "assets/main.js",
|
|
25
|
+
"expected": false
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"path": ".env",
|
|
29
|
+
"expected": false
|
|
30
|
+
}
|
|
42
31
|
],
|
|
43
32
|
"safeContentType": [
|
|
44
33
|
{
|
|
@@ -46,12 +35,20 @@
|
|
|
46
35
|
"mime": "application/x-httpd-php",
|
|
47
36
|
"expected": "text/plain; charset=utf-8"
|
|
48
37
|
},
|
|
49
|
-
{
|
|
38
|
+
{
|
|
39
|
+
"path": "lib.phar",
|
|
40
|
+
"mime": "application/zip",
|
|
41
|
+
"expected": "application/octet-stream"
|
|
42
|
+
},
|
|
50
43
|
{
|
|
51
44
|
"path": "assets/main.js",
|
|
52
45
|
"mime": "text/javascript; charset=utf-8",
|
|
53
46
|
"expected": "text/javascript; charset=utf-8"
|
|
54
47
|
},
|
|
55
|
-
{
|
|
48
|
+
{
|
|
49
|
+
"path": "logo.svg",
|
|
50
|
+
"mime": "image/svg+xml",
|
|
51
|
+
"expected": "image/svg+xml"
|
|
52
|
+
}
|
|
56
53
|
]
|
|
57
54
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** Reserved response-header namespaces carrying the platform's own serving signature. */
|
|
2
|
+
export declare const PLATFORM_MANAGED_RESPONSE_HEADER_PREFIXES: readonly string[];
|
|
3
|
+
/**
|
|
4
|
+
* The internal-redirect/sendfile family, every vendor spelling: headers that make the web
|
|
5
|
+
* server produce a different response instead of describing this one. Factored out of the
|
|
6
|
+
* managed set below so the proxy relay can drop exactly these from an untrusted upstream
|
|
7
|
+
* while still relaying ordinary response headers the managed set also lists.
|
|
8
|
+
*/
|
|
9
|
+
export declare const INTERNAL_REDIRECT_RESPONSE_HEADERS: readonly string[];
|
|
10
|
+
/**
|
|
11
|
+
* Rejected/platform-managed response headers: user `_headers` rules can never set or remove
|
|
12
|
+
* these. The internal-redirect family above is a strict subset — anything the proxy lane
|
|
13
|
+
* refuses to relay is also refused from `_headers`. Ask isPlatformManagedResponseHeader(),
|
|
14
|
+
* not this list alone: the reserved prefixes are the other half of the policy.
|
|
15
|
+
*/
|
|
16
|
+
export declare const PLATFORM_MANAGED_RESPONSE_HEADERS: readonly string[];
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// @generated by `cargo run -p stattic-runtime-compiler --bin protocol-codegen -- --policy-ts`.
|
|
2
|
+
// `crates/stattic-runtime-policy/src/lib.rs` is the only editable authority.
|
|
3
|
+
/** Reserved response-header namespaces carrying the platform's own serving signature. */
|
|
4
|
+
export const PLATFORM_MANAGED_RESPONSE_HEADER_PREFIXES = [
|
|
5
|
+
"x-spacefast-",
|
|
6
|
+
"x-stattic-",
|
|
7
|
+
];
|
|
8
|
+
/**
|
|
9
|
+
* The internal-redirect/sendfile family, every vendor spelling: headers that make the web
|
|
10
|
+
* server produce a different response instead of describing this one. Factored out of the
|
|
11
|
+
* managed set below so the proxy relay can drop exactly these from an untrusted upstream
|
|
12
|
+
* while still relaying ordinary response headers the managed set also lists.
|
|
13
|
+
*/
|
|
14
|
+
export const INTERNAL_REDIRECT_RESPONSE_HEADERS = [
|
|
15
|
+
"x-accel-buffering",
|
|
16
|
+
"x-accel-charset",
|
|
17
|
+
"x-accel-expires",
|
|
18
|
+
"x-accel-limit-rate",
|
|
19
|
+
"x-accel-redirect",
|
|
20
|
+
"x-lighttpd-send-file",
|
|
21
|
+
"x-lighttpd-sendfile",
|
|
22
|
+
"x-lighttpd-sendfile2",
|
|
23
|
+
"x-reproxy-url",
|
|
24
|
+
"x-sendfile",
|
|
25
|
+
];
|
|
26
|
+
/**
|
|
27
|
+
* Rejected/platform-managed response headers: user `_headers` rules can never set or remove
|
|
28
|
+
* these. The internal-redirect family above is a strict subset — anything the proxy lane
|
|
29
|
+
* refuses to relay is also refused from `_headers`. Ask isPlatformManagedResponseHeader(),
|
|
30
|
+
* not this list alone: the reserved prefixes are the other half of the policy.
|
|
31
|
+
*/
|
|
32
|
+
export const PLATFORM_MANAGED_RESPONSE_HEADERS = [
|
|
33
|
+
"accept-ranges",
|
|
34
|
+
"age",
|
|
35
|
+
"allow",
|
|
36
|
+
"alt-svc",
|
|
37
|
+
"cdn-cache-control",
|
|
38
|
+
"cloudflare-cdn-cache-control",
|
|
39
|
+
"connection",
|
|
40
|
+
"content-encoding",
|
|
41
|
+
"content-length",
|
|
42
|
+
"content-range",
|
|
43
|
+
"cookie",
|
|
44
|
+
"date",
|
|
45
|
+
"host",
|
|
46
|
+
"keep-alive",
|
|
47
|
+
"location",
|
|
48
|
+
"netlify-cdn-cache-control",
|
|
49
|
+
"proxy-authenticate",
|
|
50
|
+
"proxy-authorization",
|
|
51
|
+
"server",
|
|
52
|
+
"set-cookie",
|
|
53
|
+
"strict-transport-security",
|
|
54
|
+
"surrogate-control",
|
|
55
|
+
"te",
|
|
56
|
+
"trailer",
|
|
57
|
+
"transfer-encoding",
|
|
58
|
+
"upgrade",
|
|
59
|
+
"vary",
|
|
60
|
+
"x-accel-buffering",
|
|
61
|
+
"x-accel-charset",
|
|
62
|
+
"x-accel-expires",
|
|
63
|
+
"x-accel-limit-rate",
|
|
64
|
+
"x-accel-redirect",
|
|
65
|
+
"x-lighttpd-send-file",
|
|
66
|
+
"x-lighttpd-sendfile",
|
|
67
|
+
"x-lighttpd-sendfile2",
|
|
68
|
+
"x-reproxy-url",
|
|
69
|
+
"x-sendfile",
|
|
70
|
+
];
|