@spacefast/common 0.0.13 → 0.0.18
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/client-registry.d.ts +22 -0
- package/dist/agents/client-registry.js +41 -0
- package/dist/agents/connect-targets.d.ts +51 -6
- package/dist/agents/connect-targets.js +93 -28
- package/dist/brand.d.ts +0 -11
- package/dist/brand.js +0 -20
- package/dist/config/domains.d.ts +15 -6
- package/dist/config/domains.js +24 -10
- package/dist/config/postgres-budget.d.ts +3 -0
- package/dist/config/postgres-budget.js +6 -1
- package/dist/config/postgres-budget.json +2 -1
- package/dist/contracts/abuse.js +1 -1
- package/dist/contracts/access.d.ts +149 -44
- package/dist/contracts/access.js +94 -8
- package/dist/contracts/activity.js +5 -1
- package/dist/contracts/api-keys.js +9 -9
- package/dist/contracts/archives.js +11 -11
- package/dist/contracts/beta.js +1 -1
- package/dist/contracts/builds.js +14 -14
- package/dist/contracts/channels.js +2 -2
- package/dist/contracts/comments.d.ts +86 -15
- package/dist/contracts/comments.js +116 -20
- package/dist/contracts/common.d.ts +12 -12
- package/dist/contracts/common.js +33 -30
- package/dist/contracts/continuation.js +5 -5
- package/dist/contracts/data-source-routes.js +1 -1
- package/dist/contracts/device-auth.d.ts +6 -6
- package/dist/contracts/device-auth.js +12 -12
- package/dist/contracts/docs.d.ts +10 -10
- package/dist/contracts/domains.js +39 -33
- package/dist/contracts/enums.d.ts +2 -10
- package/dist/contracts/enums.js +0 -2
- package/dist/contracts/error-code-meta.d.ts +116 -47
- package/dist/contracts/error-code-meta.js +37 -16
- package/dist/contracts/error-codes.d.ts +7 -4
- package/dist/contracts/error-codes.js +46 -21
- package/dist/contracts/events.d.ts +10 -3
- package/dist/contracts/events.js +33 -5
- package/dist/contracts/execution.d.ts +26 -0
- package/dist/contracts/execution.js +21 -0
- package/dist/contracts/feature-lifecycle.d.ts +21 -24
- package/dist/contracts/feature-lifecycle.js +28 -26
- package/dist/contracts/features.js +4 -4
- package/dist/contracts/functions.d.ts +8 -140
- package/dist/contracts/functions.js +9 -19
- package/dist/contracts/git.d.ts +1 -1
- package/dist/contracts/grants.d.ts +96 -79
- package/dist/contracts/grants.js +130 -43
- package/dist/contracts/ids.d.ts +2 -2
- package/dist/contracts/ids.js +2 -4
- package/dist/contracts/internal.js +2 -2
- package/dist/contracts/mcp.d.ts +17 -556
- package/dist/contracts/mcp.js +9 -103
- package/dist/contracts/me.js +3 -4
- package/dist/contracts/notifications.d.ts +108 -0
- package/dist/contracts/notifications.js +54 -0
- package/dist/contracts/oauth-resources.d.ts +18 -1
- package/dist/contracts/oauth-resources.js +22 -2
- package/dist/contracts/operations.d.ts +2 -50
- package/dist/contracts/operations.js +0 -19
- package/dist/contracts/pages.d.ts +8 -4
- package/dist/contracts/pages.js +16 -3
- package/dist/contracts/platform.js +10 -8
- package/dist/contracts/problem-document.d.ts +14 -0
- package/dist/contracts/problem-document.js +35 -0
- package/dist/contracts/push-new.d.ts +21 -6
- package/dist/contracts/push-new.js +3 -7
- package/dist/contracts/resources.d.ts +8 -7
- package/dist/contracts/resources.js +9 -6
- package/dist/contracts/runtime-api.d.ts +162 -92
- package/dist/contracts/runtime-api.js +99 -27
- package/dist/contracts/runtime-app.d.ts +41 -11
- package/dist/contracts/runtime-app.js +22 -1
- package/dist/contracts/runtime-db.d.ts +424 -0
- package/dist/contracts/runtime-db.js +108 -0
- package/dist/contracts/runtime-storage.d.ts +120 -0
- package/dist/contracts/runtime-storage.js +40 -0
- package/dist/contracts/sf-config-v1.d.ts +49 -1
- package/dist/contracts/sf-config-v1.js +2 -2
- package/dist/contracts/sites.d.ts +1 -0
- package/dist/contracts/sites.js +20 -12
- package/dist/contracts/space-config.d.ts +62 -22
- package/dist/contracts/space-config.js +173 -47
- package/dist/contracts/spaces.d.ts +79 -45
- package/dist/contracts/spaces.js +66 -50
- package/dist/contracts/superadmin-spaces.d.ts +13 -2
- package/dist/contracts/superadmin-spaces.js +15 -0
- package/dist/contracts/superadmin-tenants.js +4 -4
- package/dist/contracts/superadmin.d.ts +4 -0
- package/dist/contracts/superadmin.js +14 -1
- package/dist/contracts/tags.d.ts +17 -17
- package/dist/contracts/tags.js +1 -1
- package/dist/contracts/teams.js +25 -16
- package/dist/contracts/theme-json.js +1 -1
- package/dist/contracts/transfers.js +4 -4
- package/dist/contracts/variables.js +3 -3
- package/dist/contracts/zero.d.ts +204 -338
- package/dist/contracts/zero.js +94 -98
- package/dist/dashboard-paths/index.d.ts +16 -1
- package/dist/dashboard-paths/index.js +18 -0
- package/dist/docs/agent-handoff-document.d.ts +2 -2
- package/dist/docs/agent-handoff-document.js +8 -7
- package/dist/docs/agent-prose.d.ts +22 -3
- package/dist/docs/agent-prose.js +221 -51
- package/dist/docs/agent-setup.d.ts +13 -10
- package/dist/docs/agent-setup.js +279 -53
- package/dist/docs/agent-solutions.d.ts +6 -6
- package/dist/docs/agent-solutions.js +23 -21
- package/dist/docs/catalog.d.ts +29 -74
- package/dist/docs/catalog.js +65 -92
- package/dist/docs/error-docs.js +293 -221
- package/dist/docs/skill-distribution.d.ts +2 -0
- package/dist/docs/skill-distribution.js +7 -4
- package/dist/docs/skills.d.ts +546 -0
- package/dist/docs/skills.js +340 -0
- package/dist/utils/browser-credential.d.ts +19 -0
- package/dist/utils/browser-credential.js +62 -0
- package/dist/utils/comment-avatars.d.ts +7 -0
- package/dist/utils/comment-avatars.js +42 -0
- package/dist/utils/content-type.d.ts +3 -3
- package/dist/utils/content-type.js +38 -61
- package/dist/utils/domain-diagnostics.d.ts +5 -5
- package/dist/utils/domain-diagnostics.js +21 -10
- package/dist/utils/local-space-state.d.ts +78 -4
- package/dist/utils/local-space-state.js +383 -82
- package/dist/utils/one-shot-replay.d.ts +14 -0
- package/dist/utils/one-shot-replay.js +21 -0
- package/dist/utils/page-preview.d.ts +7 -0
- package/dist/utils/page-preview.js +9 -0
- package/dist/utils/pages.d.ts +2 -11
- package/dist/utils/pages.js +58 -14
- package/dist/utils/publish-form-data.d.ts +22 -0
- package/dist/utils/publish-form-data.js +26 -0
- package/dist/utils/publish-policy.js +20 -4
- package/dist/utils/query-keys.d.ts +7 -4
- package/dist/utils/query-keys.js +13 -4
- package/dist/utils/runtime-paths.d.ts +3 -0
- package/dist/utils/runtime-paths.js +7 -0
- package/dist/utils/secure-local-file.d.ts +73 -0
- package/dist/utils/secure-local-file.js +1373 -0
- package/dist/utils/space-config.d.ts +13 -4
- package/dist/utils/space-config.js +123 -15
- package/dist/utils/storage-policy.d.ts +20 -0
- package/dist/utils/storage-policy.fixtures.json +174 -0
- package/dist/utils/storage-policy.js +193 -0
- package/dist/utils/upload-session.d.ts +12 -1
- package/dist/utils/upload-session.js +37 -2
- package/dist/utils/version-file-browser.d.ts +13 -0
- package/dist/utils/version-file-browser.js +59 -0
- package/package.json +19 -1
- package/dist/contracts/mounts.d.ts +0 -142
- package/dist/contracts/mounts.js +0 -56
- package/dist/utils/asset-fingerprint.d.ts +0 -64
- package/dist/utils/asset-fingerprint.js +0 -620
package/dist/utils/pages.js
CHANGED
|
@@ -1,20 +1,10 @@
|
|
|
1
|
+
import { defaultTreeAdapter, parse } from "parse5";
|
|
1
2
|
import { SPACEFAST_WORDMARK_PATH } from "../brand-assets.js";
|
|
2
3
|
import { CUSTOM_PAGE_IDS, PAGE_CATALOG, PAGE_ELEMENTS, PAGE_LAYOUT_FILE, PAGE_SOURCE_MAX_BYTES, SITE_PAGE_IDS, } from "../contracts/pages.js";
|
|
3
4
|
import { SPACE_CONFIG_ACCEPTED_FILES } from "../contracts/space-config.js";
|
|
4
5
|
import { derivePageColors, PAGE_COLOR_DEFAULTS } from "./page-colors.js";
|
|
5
6
|
import { PAGE_EMBEDDED_FONT_FACES, PAGE_FONT_FACES } from "./page-fonts.js";
|
|
6
|
-
|
|
7
|
-
* One URL, one representation. A file's bare path always serves its bytes;
|
|
8
|
-
* the preview page is reachable only under this query flag. The alternative —
|
|
9
|
-
* negotiating on `Accept` at the bare path — depends on `Vary`, which the edge
|
|
10
|
-
* ignores, so whichever client arrived first pinned that representation for
|
|
11
|
-
* everyone (an `<img>` could poison the human's link for a year).
|
|
12
|
-
*/
|
|
13
|
-
export const PAGE_PREVIEW_QUERY = "spacefast_view";
|
|
14
|
-
/** The preview page for a file whose bytes live at `fileHref`. */
|
|
15
|
-
export function previewPageHref(fileHref) {
|
|
16
|
-
return `${fileHref}${fileHref.includes("?") ? "&" : "?"}${PAGE_PREVIEW_QUERY}=1`;
|
|
17
|
-
}
|
|
7
|
+
export { PAGE_PREVIEW_QUERY, previewPageHref } from "./page-preview.js";
|
|
18
8
|
const encoder = new TextEncoder();
|
|
19
9
|
const knownElements = new Set(PAGE_ELEMENTS);
|
|
20
10
|
const customPageIds = new Set(CUSTOM_PAGE_IDS);
|
|
@@ -102,6 +92,10 @@ function renderElement(element, context) {
|
|
|
102
92
|
return context.content ?? "";
|
|
103
93
|
case "sf-denial-context":
|
|
104
94
|
return runtimeSlot("denial", '<p class="sf-copy">This page is private.</p>');
|
|
95
|
+
case "sf-access-status":
|
|
96
|
+
return runtimeSlot("access-status", "");
|
|
97
|
+
case "sf-access-lanes":
|
|
98
|
+
return runtimeSlot("access-lanes", '<p class="sf-copy">This page is private.</p>');
|
|
105
99
|
case "sf-page-path":
|
|
106
100
|
return context.displayPath
|
|
107
101
|
? escapePageHtml(context.displayPath)
|
|
@@ -150,6 +144,35 @@ function foundElements(source) {
|
|
|
150
144
|
result.add((match[1] ?? "").toLowerCase());
|
|
151
145
|
return [...result];
|
|
152
146
|
}
|
|
147
|
+
function accessCspBlocksForms(source) {
|
|
148
|
+
const documentNodes = [...parse(source).childNodes];
|
|
149
|
+
const head = documentNodes
|
|
150
|
+
.flatMap((node) => defaultTreeAdapter.isElementNode(node) && node.tagName === "html" ? node.childNodes : [])
|
|
151
|
+
.find((node) => defaultTreeAdapter.isElementNode(node) && node.tagName === "head");
|
|
152
|
+
if (!head || !defaultTreeAdapter.isElementNode(head))
|
|
153
|
+
return false;
|
|
154
|
+
const pending = [...head.childNodes];
|
|
155
|
+
while (pending.length > 0) {
|
|
156
|
+
const node = pending.pop();
|
|
157
|
+
if (!node || !defaultTreeAdapter.isElementNode(node))
|
|
158
|
+
continue;
|
|
159
|
+
if (node.tagName === "meta") {
|
|
160
|
+
const attributes = new Map(node.attrs.map(({ name, value }) => [name, value]));
|
|
161
|
+
if (attributes.get("http-equiv")?.trim().toLowerCase() === "content-security-policy") {
|
|
162
|
+
const policy = (attributes.get("content") ?? "")
|
|
163
|
+
.split(";")
|
|
164
|
+
.map((directive) => directive.trim().split(/\s+/))
|
|
165
|
+
.filter((directive) => directive[0]);
|
|
166
|
+
const formAction = policy.find((directive) => directive[0]?.toLowerCase() === "form-action");
|
|
167
|
+
if (formAction &&
|
|
168
|
+
!formAction.slice(1).some((expression) => expression.toLowerCase() === "'self'"))
|
|
169
|
+
return true;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
pending.push(...node.childNodes);
|
|
173
|
+
}
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
153
176
|
export function validatePageSources(input) {
|
|
154
177
|
const diagnostics = [];
|
|
155
178
|
for (const [sourcePath, source] of Object.entries(input.pages)) {
|
|
@@ -193,6 +216,7 @@ export function validatePageSources(input) {
|
|
|
193
216
|
element === "sf-spacefast-branding" ||
|
|
194
217
|
(id === "404" && element === "sf-page-path") ||
|
|
195
218
|
(id === "denied" && element === "sf-denial-context") ||
|
|
219
|
+
(id === "access" && (element === "sf-access-status" || element === "sf-access-lanes")) ||
|
|
196
220
|
(id === "index" &&
|
|
197
221
|
(element === "sf-files" ||
|
|
198
222
|
element === "sf-page-path" ||
|
|
@@ -215,7 +239,23 @@ export function validatePageSources(input) {
|
|
|
215
239
|
message: "The index page does not render <sf-files>.",
|
|
216
240
|
path,
|
|
217
241
|
});
|
|
218
|
-
|
|
242
|
+
// Without the lane slot the runtime has nowhere to inject the working
|
|
243
|
+
// sign-in/password/request blocks and the template ships a dead gate.
|
|
244
|
+
if (id === "access" && !/<\s*sf-access-lanes\b/i.test(source))
|
|
245
|
+
diagnostics.push({
|
|
246
|
+
severity: "error",
|
|
247
|
+
code: "access_lanes_missing",
|
|
248
|
+
message: "The access page must render <sf-access-lanes> so visitors keep a way in.",
|
|
249
|
+
path,
|
|
250
|
+
});
|
|
251
|
+
if (id === "access" && accessCspBlocksForms(source))
|
|
252
|
+
diagnostics.push({
|
|
253
|
+
severity: "error",
|
|
254
|
+
code: "access_csp_form_action_missing_self",
|
|
255
|
+
message: "The access page CSP must allow its runtime forms: use form-action 'self' when declaring form-action.",
|
|
256
|
+
path,
|
|
257
|
+
});
|
|
258
|
+
if ((id === "denied" || id === "access") &&
|
|
219
259
|
/<\s*meta\b[^>]*\bname\s*=\s*["']robots["'][^>]*\bcontent\s*=\s*["'][^"']*\bindex\b/i.test(source))
|
|
220
260
|
diagnostics.push({
|
|
221
261
|
severity: "warning",
|
|
@@ -322,15 +362,19 @@ export function defaultPageBody(id) {
|
|
|
322
362
|
// themes, so buttons and links remain readable for every accepted accent.
|
|
323
363
|
const defaultPageColors = derivePageColors({});
|
|
324
364
|
const sharedCss = `/*sf-default-fonts*/:root{color-scheme:light dark;--sf-accent:${PAGE_COLOR_DEFAULTS.accent};--sf-bg:${PAGE_COLOR_DEFAULTS.bg};--sf-fg:${PAGE_COLOR_DEFAULTS.fg};--sf-muted:${PAGE_COLOR_DEFAULTS.muted};--sf-card:${PAGE_COLOR_DEFAULTS.card};--sf-border:${PAGE_COLOR_DEFAULTS.border};--sf-link:${defaultPageColors.link};--sf-on-accent:${defaultPageColors.onAccent};--sf-error:#a82d1a;--sf-font:"Haskoy",-apple-system,BlinkMacSystemFont,ui-sans-serif,"Segoe UI",sans-serif;--sf-display-font:"Merriweather",Georgia,serif;--sf-mono:ui-monospace,"SF Mono",Menlo,Consolas,monospace}*{box-sizing:border-box}html{min-width:320px;background:var(--sf-bg);-webkit-font-smoothing:antialiased}body{margin:0;min-height:100vh;background:var(--sf-bg);color:var(--sf-fg);font:14px/1.65 var(--sf-font)}a{color:inherit}.sf-page{min-height:100vh;display:flex;flex-direction:column;padding:clamp(22px,4vw,44px)}.sf-header{display:flex;align-items:center;min-height:32px}.sf-page-center .sf-header{justify-content:center}.sf-brand{display:inline-flex;min-width:0;align-items:center;gap:10px;color:var(--sf-fg);text-decoration:none}.sf-brand-mark{width:7px;height:7px;flex:none;background:var(--sf-accent)}.sf-logo{width:28px;height:28px;flex:none;object-fit:contain}.sf-brand strong{min-width:0;overflow-wrap:anywhere;font-size:13px;font-weight:600}.sf-main{flex:1;width:100%;padding:clamp(34px,9vh,84px) 0}.sf-page-plain .sf-main{max-width:680px}.sf-page-center .sf-main{max-width:620px;margin:auto;display:flex;flex-direction:column;align-items:center;text-align:center}.sf-eyebrow{margin:0 0 14px;color:var(--sf-muted);font:500 11px/1.5 var(--sf-mono);letter-spacing:.12em;text-transform:uppercase;overflow-wrap:anywhere}.sf-page-center .sf-eyebrow{margin-bottom:16px}h1{max-width:20ch;margin:0 0 12px;color:var(--sf-fg);font:430 clamp(1.9rem,5vw,2.8rem)/1.22 var(--sf-display-font);letter-spacing:-.012em;text-wrap:balance}.sf-copy,.sf-sub{max-width:52ch;margin:0;color:var(--sf-muted);text-wrap:pretty}.sf-inline-path{font-family:var(--sf-mono);font-size:.9em;color:var(--sf-fg);overflow-wrap:anywhere}.sf-actions{display:flex;flex-wrap:wrap;align-items:center;gap:14px;margin-top:22px}.sf-page-center .sf-actions{justify-content:center}.sf-button{display:inline-flex;min-height:44px;align-items:center;justify-content:center;border:0;background:var(--sf-accent);color:var(--sf-on-accent);padding:10px 16px;font:600 13px/1.4 var(--sf-font);text-decoration:none;cursor:pointer}.sf-ghost{color:var(--sf-muted);text-underline-offset:3px}.sf-button:hover{filter:brightness(1.06)}a:focus-visible,button:focus-visible{outline:2px solid var(--sf-link);outline-offset:2px}.sf-copy+.sf-copy{margin-top:12px}.sf-files{width:100%;margin:24px 0 0;padding:0;list-style:none}.sf-files li{display:flex;min-width:0;align-items:baseline;gap:18px;border-bottom:1px solid var(--sf-border)}.sf-files a{min-width:0;flex:1;padding:10px 2px;color:var(--sf-fg);font-weight:500;text-decoration:none}.sf-files a:hover{color:var(--sf-link)}.sf-file-name{overflow-wrap:anywhere}.sf-file-size{flex:none;padding:10px 2px;color:var(--sf-muted);font:12px/1.5 var(--sf-mono);white-space:nowrap}.sf-empty{max-width:420px;margin:24px 0 0;border:1px dashed var(--sf-border);padding:18px 20px;color:var(--sf-muted)}.sf-preview-wrap{width:min(100%,560px);aspect-ratio:16/9;margin:10px 0 18px;overflow:hidden;background:#fff;outline:1px solid color-mix(in srgb,var(--sf-fg) 10%,transparent);outline-offset:-1px}.sf-preview{display:block;width:100%;height:100%;border:0;object-fit:contain}.sf-file-meta{width:min(100%,560px);margin:0}.sf-file-meta div{display:flex;min-width:0;justify-content:space-between;gap:20px;border-bottom:1px solid var(--sf-border);padding:9px 2px}.sf-file-meta dt{color:var(--sf-fg);font-weight:600}.sf-file-meta dd{min-width:0;margin:0;color:var(--sf-muted);font-family:var(--sf-mono);overflow-wrap:anywhere;text-align:right}.sf-colophon{display:flex;flex-direction:column;align-items:center;gap:10px;padding:44px 0 26px;margin-top:auto;color:var(--sf-muted)}.sf-page-plain .sf-colophon{align-items:flex-start}.sf-colophon-row{display:flex;align-items:center;gap:20px;flex-wrap:wrap}.sf-powered{display:inline-flex;align-items:center;gap:10px;cursor:pointer;outline:none}.sf-powered-line{display:none;font-size:12px;color:var(--sf-muted)}.sf-powered:hover .sf-powered-line,.sf-powered:focus .sf-powered-line,.sf-powered:focus-within .sf-powered-line{display:inline}.sf-powered-line a{color:var(--sf-link);text-decoration:underline;text-underline-offset:3px}.sf-help{font-size:12px;color:var(--sf-muted);text-decoration:underline;text-underline-offset:3px}.sf-help:hover{color:var(--sf-fg)}.sf-wordmark{display:block;flex:none;width:auto;height:11px}.sf-wordmark-shadow{fill:var(--sf-accent)}.sf-wordmark-plate{fill:#141419}.sf-wordmark-paper{fill:#fbf8f1}@media(max-width:520px){.sf-page{padding:20px}.sf-button{min-height:48px}.sf-file-meta div{flex-direction:column;gap:2px}.sf-file-meta dd{text-align:left}.sf-colophon{padding-top:32px}}@media(prefers-color-scheme:dark){:root{--sf-bg:${PAGE_COLOR_DEFAULTS.darkBg};--sf-fg:${PAGE_COLOR_DEFAULTS.darkFg};--sf-muted:${PAGE_COLOR_DEFAULTS.darkMuted};--sf-card:${PAGE_COLOR_DEFAULTS.darkCard};--sf-border:${PAGE_COLOR_DEFAULTS.darkBorder};--sf-link:${defaultPageColors.linkDark};--sf-error:#ff8a7a}.sf-preview-wrap{outline-color:#ffffff1a}}@media(forced-colors:active){.sf-brand-mark{background:CanvasText}.sf-button{border:1px solid ButtonText}.sf-powered:focus{outline:1px solid CanvasText;outline-offset:3px}}`;
|
|
365
|
+
const accessCss = `.sf-page.access .sf-main{max-width:26rem;padding:clamp(42px,8vh,72px) 0}.sf-page.access h1{max-width:18ch;margin-bottom:14px;font:600 clamp(2rem,7vw,2.65rem)/1.12 var(--sf-font);letter-spacing:-.035em}.sf-page.access .sf-copy{font-size:15px;line-height:1.6}.sf-access-default{display:flex;width:100%;flex-direction:column;align-items:center}.sf-access-status{padding:10px 12px;border:1px solid color-mix(in srgb,var(--sf-fg) 10%,transparent);border-radius:8px;background:color-mix(in srgb,var(--sf-fg) 4%,transparent);color:var(--sf-fg)}.sf-access-lanes{display:flex;flex-direction:column;gap:16px;width:min(23rem,100%);margin-top:30px;text-align:left}.sf-access-lanes form{display:flex;flex-direction:column;gap:14px;margin:0}.sf-input{box-sizing:border-box;width:100%;min-height:46px;padding:11px 12px;border:1px solid color-mix(in srgb,var(--sf-fg) 18%,transparent);border-radius:8px;outline:2px solid transparent;outline-offset:-1px;background:color-mix(in srgb,var(--sf-bg) 92%,var(--sf-fg));color:var(--sf-fg);font:500 15px/1.45 var(--sf-font);appearance:none}.sf-input::placeholder{color:color-mix(in srgb,var(--sf-muted) 82%,transparent);font:inherit}.sf-input:hover{border-color:color-mix(in srgb,var(--sf-fg) 34%,transparent)}.sf-input:focus-visible{border-color:var(--sf-accent);outline-color:var(--sf-accent)}textarea.sf-input{min-height:92px;resize:vertical}.sf-label{color:var(--sf-fg);font:600 13px/1.4 var(--sf-font)}.sf-access-lanes .sf-button{width:100%;min-height:46px;border-radius:8px}.sf-access-or{display:flex;align-items:center;gap:12px;color:var(--sf-muted);font-size:13px}.sf-access-or:before,.sf-access-or:after{content:"";flex:1;border-top:1px solid color-mix(in srgb,var(--sf-fg) 12%,transparent)}.sf-error{margin:0;color:var(--sf-error);font-size:14px}.sf-access-password-row{display:flex;gap:8px}.sf-access-password-row .sf-input{min-width:0;flex:1}.sf-access-password-row .sf-button{width:auto;flex:none}.sf-access-request{padding-top:2px}.sf-access-request summary{padding:4px 0;cursor:pointer;color:var(--sf-muted);font:550 14px/1.5 var(--sf-font)}.sf-access-request summary:hover{color:var(--sf-fg)}.sf-access-request[open] summary{color:var(--sf-fg)}.sf-access-request form{margin-top:16px}@media(max-width:520px){.sf-page.access .sf-main{padding:38px 0}.sf-page.access h1{font-size:2rem}.sf-page.access .sf-copy{font-size:16px}.sf-input{min-height:48px;font-size:16px}.sf-access-lanes .sf-button{min-height:48px}.sf-access-password-row{flex-direction:column}.sf-access-password-row .sf-button{width:100%}}@media(prefers-color-scheme:dark){.sf-access-status{background:color-mix(in srgb,var(--sf-fg) 5%,transparent)}}`;
|
|
325
366
|
const pageHeader = '<header class="sf-header"><a class="sf-brand" href="/" aria-label="Homepage"><span class="sf-brand-mark" aria-hidden="true"></span><sf-logo></sf-logo><strong><sf-name></sf-name></strong></a></header>';
|
|
326
367
|
const pageFooter = "<sf-spacefast-branding></sf-spacefast-branding>";
|
|
327
368
|
function document(id, title, body, layout) {
|
|
328
369
|
const noindex = PAGE_CATALOG[id].noindex ? '<meta name="robots" content="noindex">' : "";
|
|
329
|
-
|
|
370
|
+
const accessClass = id === "access" ? " access" : "";
|
|
371
|
+
const pageCss = id === "access" ? sharedCss + accessCss : sharedCss;
|
|
372
|
+
return `<!doctype html>\n<html lang="en">\n<head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">${noindex}<title>${title}</title><style>${pageCss}</style></head>\n<body><div class="sf-page${accessClass} sf-page-${layout}">${pageHeader}<main class="sf-main">${body}</main>${pageFooter}</div></body>\n</html>\n`;
|
|
330
373
|
}
|
|
331
374
|
export const DEFAULT_PAGE_TEMPLATES = {
|
|
332
375
|
"404": document("404", "Page not found", '<p class="sf-eyebrow">404 · Not Found</p><h1>Page not found</h1><p class="sf-copy">There’s nothing at <span class="sf-inline-path"><sf-page-path></sf-page-path></span>.</p><div class="sf-actions"><a class="sf-button" href="/">Go to the homepage</a></div>', "center"),
|
|
333
376
|
denied: document("denied", "Access denied", "<h1>Access denied</h1><sf-denial-context></sf-denial-context>", "center"),
|
|
377
|
+
access: document("access", "This space is private", '<h1>This space is private</h1><div class="sf-access-default"><sf-access-status></sf-access-status><sf-access-lanes></sf-access-lanes></div>', "center"),
|
|
334
378
|
index: document("index", "<sf-page-title></sf-page-title>", '<p class="sf-eyebrow"><sf-page-path></sf-page-path></p><h1><sf-page-title></sf-page-title></h1><p class="sf-sub"><sf-page-summary></sf-page-summary></p><sf-files></sf-files>', "plain"),
|
|
335
379
|
preview: document("preview", "<sf-page-title></sf-page-title>", '<p class="sf-eyebrow"><sf-page-path></sf-page-path></p><h1><sf-page-title></sf-page-title></h1><sf-file></sf-file>', "plain"),
|
|
336
380
|
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export type DirectPublishUpload = {
|
|
2
|
+
kind: "archive";
|
|
3
|
+
name: string;
|
|
4
|
+
bytes: Uint8Array;
|
|
5
|
+
contentType: string;
|
|
6
|
+
} | {
|
|
7
|
+
kind: "files";
|
|
8
|
+
files: Array<{
|
|
9
|
+
path: string;
|
|
10
|
+
bytes: Uint8Array;
|
|
11
|
+
contentType?: string | undefined;
|
|
12
|
+
}>;
|
|
13
|
+
};
|
|
14
|
+
/** Build the canonical multipart body used by direct one-shot publish clients. */
|
|
15
|
+
export declare function createPublishFormData(input: {
|
|
16
|
+
upload: DirectPublishUpload;
|
|
17
|
+
clientAttribution: string;
|
|
18
|
+
spaceId?: string | undefined;
|
|
19
|
+
slug?: string | undefined;
|
|
20
|
+
title?: string | undefined;
|
|
21
|
+
description?: string | undefined;
|
|
22
|
+
}): FormData;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/** Build the canonical multipart body used by direct one-shot publish clients. */
|
|
2
|
+
export function createPublishFormData(input) {
|
|
3
|
+
const form = new FormData();
|
|
4
|
+
if (input.spaceId)
|
|
5
|
+
form.append("spaceId", input.spaceId);
|
|
6
|
+
if (input.description)
|
|
7
|
+
form.append("description", input.description);
|
|
8
|
+
if (input.slug || input.title) {
|
|
9
|
+
form.append("space", JSON.stringify({
|
|
10
|
+
...(input.slug ? { slug: input.slug } : {}),
|
|
11
|
+
...(input.title ? { title: input.title } : {}),
|
|
12
|
+
}));
|
|
13
|
+
}
|
|
14
|
+
form.append("clientAttribution", input.clientAttribution);
|
|
15
|
+
form.append("source", JSON.stringify({ kind: "agent", client: input.clientAttribution }));
|
|
16
|
+
if (input.upload.kind === "archive") {
|
|
17
|
+
form.append("archive", new File([Uint8Array.from(input.upload.bytes)], input.upload.name, {
|
|
18
|
+
type: input.upload.contentType,
|
|
19
|
+
}));
|
|
20
|
+
return form;
|
|
21
|
+
}
|
|
22
|
+
for (const file of input.upload.files.toSorted((left, right) => left.path.localeCompare(right.path))) {
|
|
23
|
+
form.append("files", new File([Uint8Array.from(file.bytes)], file.path, file.contentType ? { type: file.contentType } : undefined));
|
|
24
|
+
}
|
|
25
|
+
return form;
|
|
26
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { BRAND } from "../brand.js";
|
|
2
2
|
import { WORST_CASE_QUOTAS } from "../contracts/quotas.js";
|
|
3
3
|
import { SPACE_CONFIG_PRIVATE_FILES } from "../contracts/space-config.js";
|
|
4
|
+
import { ZERO_RUNTIME_DEPLOY_PATH } from "../contracts/zero.js";
|
|
4
5
|
// The ONE static publish policy (internal-docs/platform.md "Upload Contract"):
|
|
5
6
|
// path denials, manifest scale caps, plan caps, duplicate detection, and the
|
|
6
7
|
// free-plan content rules live here. Consumed by API manifest validation, CLI
|
|
@@ -95,8 +96,22 @@ export function normalizePublishPath(relativePath) {
|
|
|
95
96
|
* could mint its own tokens.
|
|
96
97
|
*/
|
|
97
98
|
const PUBLISHABLE_BUILD_ARTIFACT_PREFIX = "__spacefast/functions/bundles/";
|
|
98
|
-
|
|
99
|
-
|
|
99
|
+
/**
|
|
100
|
+
* The one FILE inside the control prefix a publish may write.
|
|
101
|
+
*
|
|
102
|
+
* A Zero capsule's compiled server travels as version metadata, not content, so
|
|
103
|
+
* a server-only change ships a byte-identical app shell and the version intent's
|
|
104
|
+
* manifest comparison reads it as "no changes". `sf publish` writes the deploy's
|
|
105
|
+
* digest here so the manifest moves whenever the deploy does — same reasoning as
|
|
106
|
+
* the Functions bundle above, same channel.
|
|
107
|
+
*
|
|
108
|
+
* One exact path, not a prefix: its siblings under `__spacefast/zero/` are the
|
|
109
|
+
* runtime's own artifact, config and binary, and a publish that could overwrite
|
|
110
|
+
* those would be a publish that could replace the runtime.
|
|
111
|
+
*/
|
|
112
|
+
function isPublishableBuildArtifactPath(normalizedPath, lowerPath) {
|
|
113
|
+
return (lowerPath.startsWith(PUBLISHABLE_BUILD_ARTIFACT_PREFIX) ||
|
|
114
|
+
normalizedPath === ZERO_RUNTIME_DEPLOY_PATH);
|
|
100
115
|
}
|
|
101
116
|
export function publishPathIgnored(relativePath) {
|
|
102
117
|
const path = normalizePublishPath(relativePath);
|
|
@@ -104,7 +119,8 @@ export function publishPathIgnored(relativePath) {
|
|
|
104
119
|
// (space.json). Drop only those files at any depth so a checkout archived one
|
|
105
120
|
// level too high never ships a claim token — but keep serving legitimate
|
|
106
121
|
// content in the same dirs (e.g. the legacy .stattic/routes.json convention).
|
|
107
|
-
if (/(?:^|\/)\.(?:spacefast|stattic)\/(?:state|space)\.json$/.test(path)
|
|
122
|
+
if (/(?:^|\/)\.(?:spacefast|stattic)\/(?:state|space)\.json$/.test(path) ||
|
|
123
|
+
/(?:^|\/)\.spacefast-(?:[0-9a-f-]{36}\.(?:tmp|rollback|retired)|[0-9a-f]{24}\.lock)$/.test(path)) {
|
|
108
124
|
return true;
|
|
109
125
|
}
|
|
110
126
|
if (IGNORED_EXACT_PATHS.has(path) || /(?:^|\/)\.next\/cache(?:\/|$)/.test(path)) {
|
|
@@ -209,7 +225,7 @@ export function staticUploadPathViolation(path, options = {}) {
|
|
|
209
225
|
const lowerSegment = segment.toLowerCase();
|
|
210
226
|
return STATIC_RUNTIME_CONTROL_SEGMENT_PREFIXES.some((prefix) => lowerSegment.startsWith(prefix));
|
|
211
227
|
});
|
|
212
|
-
if (controlSegment && !isPublishableBuildArtifactPath(lowerPath)) {
|
|
228
|
+
if (controlSegment && !isPublishableBuildArtifactPath(normalizedPath, lowerPath)) {
|
|
213
229
|
return pathViolation("static_runtime_control_path_not_supported", "Static deploys cannot upload runtime control paths.", normalizedPath);
|
|
214
230
|
}
|
|
215
231
|
if (STATIC_RUNTIME_CONTROL_PREFIXES.some((prefix) => lowerPath === prefix || lowerPath.startsWith(`${prefix}/`))) {
|
|
@@ -8,7 +8,6 @@ export declare const qk: {
|
|
|
8
8
|
agentAccounts: (teamId: string) => readonly ["agent-accounts", string];
|
|
9
9
|
myShelf: () => readonly ["my-shelf"];
|
|
10
10
|
mcpDashboard: (teamId: string) => readonly ["mcp", "dashboard", string];
|
|
11
|
-
mcpApproval: (teamId: string, id: string) => readonly ["mcp", "approval", string, string];
|
|
12
11
|
teams: () => readonly ["teams", "list"];
|
|
13
12
|
team: (ref: string) => readonly ["teams", "by-ref", string];
|
|
14
13
|
teamMembers: (id: string) => readonly ["teams", string, "members"];
|
|
@@ -53,7 +52,6 @@ export declare const qk: {
|
|
|
53
52
|
spaceVersionComments: (id: string, versionId: string, status?: string) => readonly ["spaces", string, "versions", string, "comments"] | readonly ["spaces", string, "versions", string, "comments", string];
|
|
54
53
|
spaceVersionFiles: (id: string, versionId: string) => readonly ["spaces", string, "versions", string, "files"];
|
|
55
54
|
spaceVersionFile: (id: string, versionId: string, path: string) => readonly ["spaces", string, "versions", string, "files", string];
|
|
56
|
-
spaceVersionFileDirectUrl: (id: string, versionId: string, path: string) => readonly ["spaces", string, "versions", string, "files", string, "direct-url"];
|
|
57
55
|
spaceVersionFileUrlPreview: (id: string, versionId: string, path: string) => readonly ["spaces", string, "versions", string, "files", string, "url-preview"];
|
|
58
56
|
spaceVersionFileDiff: (id: string, versionId: string, baseVersionId?: string) => readonly ["spaces", string, "versions", string, "files", "diff", string];
|
|
59
57
|
spaceVersionConventionArtifacts: (id: string, versionId: string) => readonly ["spaces", string, "versions", string, "convention-artifacts"];
|
|
@@ -63,9 +61,11 @@ export declare const qk: {
|
|
|
63
61
|
spaceDomains: (id: string) => readonly ["spaces", string, "domains"];
|
|
64
62
|
spaceRuntimeStatus: (id: string) => readonly ["spaces", string, "runtime"];
|
|
65
63
|
spaceApplyState: (id: string) => readonly ["spaces", string, "apply-state"];
|
|
66
|
-
|
|
64
|
+
spaceDatabase: (id: string) => readonly ["spaces", string, "db"];
|
|
65
|
+
spaceDatabaseDump: (id: string, table: string | undefined, limit: number) => readonly ["spaces", string, "db", "dump", string, number];
|
|
66
|
+
spaceStorage: (id: string) => readonly ["spaces", string, "storage"];
|
|
67
67
|
spaceRuntimeLogs: (id: string, limit: number) => readonly ["spaces", string, "logs", "runtime", number];
|
|
68
|
-
|
|
68
|
+
spaceRuntimeRealtimeEvents: (id: string, limit: number) => readonly ["spaces", string, "runtime", "realtime", "events", number];
|
|
69
69
|
spaceVariables: (id: string) => readonly ["spaces", string, "variables"];
|
|
70
70
|
spaceCertificates: (id: string) => readonly ["spaces", string, "runtime", "certificates"];
|
|
71
71
|
spaceLogs: (id: string, kind: "build" | "access" | "runtime") => readonly ["spaces", string, "logs", "build" | "runtime" | "access"];
|
|
@@ -79,4 +79,7 @@ export declare const qk: {
|
|
|
79
79
|
domainDnsOperations: (domainId: string) => readonly ["domains", string, "dns", "operations"];
|
|
80
80
|
domainActions: (domainId: string) => readonly ["domains", string, "actions"];
|
|
81
81
|
domainProviderConnections: (teamId: string) => readonly ["domains", "provider-connections", string];
|
|
82
|
+
notifications: () => readonly ["notifications"];
|
|
83
|
+
notificationsList: (unreadOnly: boolean) => readonly ["notifications", "list", boolean];
|
|
84
|
+
notificationsUnreadCount: () => readonly ["notifications", "unread-count"];
|
|
82
85
|
};
|
package/dist/utils/query-keys.js
CHANGED
|
@@ -10,7 +10,6 @@ export const qk = {
|
|
|
10
10
|
agentAccounts: (teamId) => ["agent-accounts", teamId],
|
|
11
11
|
myShelf: () => ["my-shelf"],
|
|
12
12
|
mcpDashboard: (teamId) => ["mcp", "dashboard", teamId],
|
|
13
|
-
mcpApproval: (teamId, id) => ["mcp", "approval", teamId, id],
|
|
14
13
|
// The teams LIST gets its own leaf so invalidating it never cascades into
|
|
15
14
|
// ["teams", id, ...] resource queries (members, spaces, domains, ...).
|
|
16
15
|
teams: () => ["teams", "list"],
|
|
@@ -71,7 +70,6 @@ export const qk = {
|
|
|
71
70
|
: ["spaces", id, "versions", versionId, "comments", status],
|
|
72
71
|
spaceVersionFiles: (id, versionId) => ["spaces", id, "versions", versionId, "files"],
|
|
73
72
|
spaceVersionFile: (id, versionId, path) => ["spaces", id, "versions", versionId, "files", path],
|
|
74
|
-
spaceVersionFileDirectUrl: (id, versionId, path) => ["spaces", id, "versions", versionId, "files", path, "direct-url"],
|
|
75
73
|
spaceVersionFileUrlPreview: (id, versionId, path) => ["spaces", id, "versions", versionId, "files", path, "url-preview"],
|
|
76
74
|
spaceVersionFileDiff: (id, versionId, baseVersionId) => ["spaces", id, "versions", versionId, "files", "diff", baseVersionId ?? ""],
|
|
77
75
|
spaceVersionConventionArtifacts: (id, versionId) => ["spaces", id, "versions", versionId, "convention-artifacts"],
|
|
@@ -81,9 +79,15 @@ export const qk = {
|
|
|
81
79
|
spaceDomains: (id) => ["spaces", id, "domains"],
|
|
82
80
|
spaceRuntimeStatus: (id) => ["spaces", id, "runtime"],
|
|
83
81
|
spaceApplyState: (id) => ["spaces", id, "apply-state"],
|
|
84
|
-
|
|
82
|
+
// A space has one database, whichever runtime claims it — the key says `db`,
|
|
83
|
+
// not `zero`, for the same reason the route does.
|
|
84
|
+
spaceDatabase: (id) => ["spaces", id, "db"],
|
|
85
|
+
// Row reads are per table and per page size; the bare prefix above sweeps
|
|
86
|
+
// every dump alongside the schema read.
|
|
87
|
+
spaceDatabaseDump: (id, table, limit) => [...qk.spaceDatabase(id), "dump", table ?? "", limit],
|
|
88
|
+
spaceStorage: (id) => ["spaces", id, "storage"],
|
|
85
89
|
spaceRuntimeLogs: (id, limit) => ["spaces", id, "logs", "runtime", limit],
|
|
86
|
-
|
|
90
|
+
spaceRuntimeRealtimeEvents: (id, limit) => [...qk.spaceRuntimeStatus(id), "realtime", "events", limit],
|
|
87
91
|
spaceVariables: (id) => ["spaces", id, "variables"],
|
|
88
92
|
spaceCertificates: (id) => ["spaces", id, "runtime", "certificates"],
|
|
89
93
|
spaceLogs: (id, kind) => ["spaces", id, "logs", kind],
|
|
@@ -97,4 +101,9 @@ export const qk = {
|
|
|
97
101
|
domainDnsOperations: (domainId) => ["domains", domainId, "dns", "operations"],
|
|
98
102
|
domainActions: (domainId) => ["domains", domainId, "actions"],
|
|
99
103
|
domainProviderConnections: (teamId) => ["domains", "provider-connections", teamId],
|
|
104
|
+
// The shell's notification bell. The bare prefix is the invalidate-all sweep
|
|
105
|
+
// that a "mark read" performs across both the count and every list view.
|
|
106
|
+
notifications: () => ["notifications"],
|
|
107
|
+
notificationsList: (unreadOnly) => ["notifications", "list", unreadOnly],
|
|
108
|
+
notificationsUnreadCount: () => ["notifications", "unread-count"],
|
|
100
109
|
};
|
|
@@ -2,5 +2,8 @@ export declare const SPACEFAST_RUNTIME_NAMESPACE: "/__spacefast";
|
|
|
2
2
|
export declare const RUNTIME_MANAGEMENT_API_PATH: "/__spacefast/api.php";
|
|
3
3
|
export declare const RUNTIME_UPLOAD_API_PATH: "/__spacefast/upload.php";
|
|
4
4
|
export declare const RUNTIME_HEALTH_PATH: "/__spacefast/health.php";
|
|
5
|
+
export declare const RUNTIME_COMMENTS_CONFIG_PATH: "/__spacefast/comments/config";
|
|
6
|
+
export declare const RUNTIME_COMMENTS_TICKET_PATH: "/__spacefast/comments/ticket";
|
|
7
|
+
export declare const RUNTIME_COMMENTS_VERSION_URLS_PATH: "/__spacefast/comments/version-urls";
|
|
5
8
|
export declare function runtimeManagementPath(suffix: string): string;
|
|
6
9
|
export declare function runtimeUploadPath(params: URLSearchParams | Record<string, string>): string;
|
|
@@ -2,6 +2,13 @@ export const SPACEFAST_RUNTIME_NAMESPACE = "/__spacefast";
|
|
|
2
2
|
export const RUNTIME_MANAGEMENT_API_PATH = `${SPACEFAST_RUNTIME_NAMESPACE}/api.php`;
|
|
3
3
|
export const RUNTIME_UPLOAD_API_PATH = `${SPACEFAST_RUNTIME_NAMESPACE}/upload.php`;
|
|
4
4
|
export const RUNTIME_HEALTH_PATH = `${SPACEFAST_RUNTIME_NAMESPACE}/health.php`;
|
|
5
|
+
// Same-origin Comments exchange paths served on every published host. The PHP
|
|
6
|
+
// mirror is SPACEFAST_COMMENTS_*_PATH in runtime/engine/shared/context.php;
|
|
7
|
+
// runtime/tests/spacefast-sdk.test.ts pins the PHP side and
|
|
8
|
+
// packages/collab-sdk/test/entry.test.ts pins the SDK side to these literals.
|
|
9
|
+
export const RUNTIME_COMMENTS_CONFIG_PATH = `${SPACEFAST_RUNTIME_NAMESPACE}/comments/config`;
|
|
10
|
+
export const RUNTIME_COMMENTS_TICKET_PATH = `${SPACEFAST_RUNTIME_NAMESPACE}/comments/ticket`;
|
|
11
|
+
export const RUNTIME_COMMENTS_VERSION_URLS_PATH = `${SPACEFAST_RUNTIME_NAMESPACE}/comments/version-urls`;
|
|
5
12
|
export function runtimeManagementPath(suffix) {
|
|
6
13
|
const route = normalizeRuntimeRoute(suffix);
|
|
7
14
|
if (route === "/") {
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { type FileHandle } from "node:fs/promises";
|
|
2
|
+
export declare class SecureLocalFileError extends Error {
|
|
3
|
+
name: string;
|
|
4
|
+
}
|
|
5
|
+
export declare class SecureLocalFileConflictError extends SecureLocalFileError {
|
|
6
|
+
name: string;
|
|
7
|
+
}
|
|
8
|
+
export type SecureWriteOptions = {
|
|
9
|
+
root: string;
|
|
10
|
+
relativePath: string;
|
|
11
|
+
data?: Uint8Array | string;
|
|
12
|
+
write?: (file: FileHandle) => Promise<void>;
|
|
13
|
+
overwrite?: boolean;
|
|
14
|
+
mode?: number;
|
|
15
|
+
rootMode?: number;
|
|
16
|
+
expectedContent?: Uint8Array | string | null;
|
|
17
|
+
beforeCommit?: () => void | Promise<void>;
|
|
18
|
+
/** Fault-injection hook after rollback custody exists but before the atomic rename. */
|
|
19
|
+
afterBackup?: () => void | Promise<void>;
|
|
20
|
+
};
|
|
21
|
+
export type SecureReadOptions = {
|
|
22
|
+
root: string;
|
|
23
|
+
relativePath: string;
|
|
24
|
+
encoding?: BufferEncoding;
|
|
25
|
+
};
|
|
26
|
+
export type SecureDirectoryOptions = {
|
|
27
|
+
root: string;
|
|
28
|
+
relativePath: string;
|
|
29
|
+
mode?: number;
|
|
30
|
+
};
|
|
31
|
+
export type SecureLocalEntryIdentity = {
|
|
32
|
+
ctimeNs: bigint;
|
|
33
|
+
dev: bigint;
|
|
34
|
+
ino: bigint;
|
|
35
|
+
mode: bigint;
|
|
36
|
+
};
|
|
37
|
+
export type SecureRemoveOptions = {
|
|
38
|
+
root: string;
|
|
39
|
+
relativePath: string;
|
|
40
|
+
expectedIdentity: SecureLocalEntryIdentity;
|
|
41
|
+
};
|
|
42
|
+
export type SecureCreatedDirectory = {
|
|
43
|
+
path: string;
|
|
44
|
+
validate: () => Promise<void>;
|
|
45
|
+
commit: () => Promise<void>;
|
|
46
|
+
rollback: () => Promise<void>;
|
|
47
|
+
};
|
|
48
|
+
export declare function secureOutputLocation(trustedPath: string, outputPath: string): {
|
|
49
|
+
root: string;
|
|
50
|
+
relativePath: string;
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Writes beneath an explicitly trusted root without following output-tree
|
|
54
|
+
* symlinks. Linux uses descriptor-relative operations; other supported
|
|
55
|
+
* platforms use exclusive staging plus before-and-after ancestor identity
|
|
56
|
+
* validation and fail closed if any binding changes.
|
|
57
|
+
*/
|
|
58
|
+
export declare function secureWriteFile(options: SecureWriteOptions): Promise<void>;
|
|
59
|
+
export declare function secureReadFile(options: SecureReadOptions & {
|
|
60
|
+
encoding: BufferEncoding;
|
|
61
|
+
}): Promise<string>;
|
|
62
|
+
export declare function secureReadFile(options: SecureReadOptions): Promise<Buffer>;
|
|
63
|
+
/**
|
|
64
|
+
* Removes only the exact non-directory leaf identity previously inspected.
|
|
65
|
+
* The same leaf lock used by secureWriteFile covers the identity check and
|
|
66
|
+
* rename, so a cooperating writer cannot replace the leaf between them.
|
|
67
|
+
*/
|
|
68
|
+
export declare function secureRemoveLocalEntry(options: SecureRemoveOptions): Promise<boolean>;
|
|
69
|
+
/**
|
|
70
|
+
* Creates one new directory leaf beneath a trusted root. The returned rollback
|
|
71
|
+
* removes only the exact directory inode created by this call.
|
|
72
|
+
*/
|
|
73
|
+
export declare function secureCreateDirectory(options: SecureDirectoryOptions): Promise<SecureCreatedDirectory>;
|