@hyperframes/share 0.9.27 → 0.9.28-alpha.0
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/README.md +58 -1
- package/dist/index.d.mts +1 -0
- package/dist/projectLock.d.mts +2 -2
- package/dist/projectSnapshot.d.mts +3 -1
- package/dist/reviewExport.d.mts +11 -1
- package/dist/reviewUpload.d.mts +2 -0
- package/dist/shareSources.d.mts +24 -0
- package/dist/sources/adapterTypes.d.ts +39 -0
- package/dist/sources/api.d.mts +29 -0
- package/dist/sources/capture.d.mts +24 -0
- package/dist/sources/contract.d.mts +2 -0
- package/dist/sources/errors.d.mts +24 -0
- package/dist/sources/limits.d.mts +12 -0
- package/dist/sources/manifest.d.mts +23 -0
- package/dist/sources/pathPolicy.d.mts +25 -0
- package/dist/sources/projectState.d.mts +9 -0
- package/dist/sources/store.d.mts +3 -0
- package/dist/sources/transfer.d.mts +45 -0
- package/dist/sources/types.d.ts +353 -0
- package/dist/sources/unicode.d.mts +15 -0
- package/dist/sources/upload.d.mts +9 -0
- package/dist/sources/uploadFiles.d.mts +31 -0
- package/dist/sources/wire.d.mts +21 -0
- package/package.json +1 -1
- package/scripts/copy-types.mjs +3 -0
- package/src/index.mjs +6 -0
- package/src/projectLock.mjs +9 -2
- package/src/projectSnapshot.mjs +11 -4
- package/src/reviewExport.mjs +145 -37
- package/src/reviewUpload.mjs +18 -16
- package/src/shareSources.mjs +236 -0
- package/src/sources/adapterTypes.d.ts +39 -0
- package/src/sources/api.mjs +114 -0
- package/src/sources/capture.mjs +262 -0
- package/src/sources/contract/LICENSE-Unicode.txt +39 -0
- package/src/sources/contract/path-policy.json +311 -0
- package/src/sources/contract/unicode-15.1.0.json +6179 -0
- package/src/sources/contract.mjs +13 -0
- package/src/sources/errors.mjs +77 -0
- package/src/sources/limits.mjs +48 -0
- package/src/sources/manifest.mjs +154 -0
- package/src/sources/pathPolicy.mjs +197 -0
- package/src/sources/projectState.mjs +59 -0
- package/src/sources/store.mjs +82 -0
- package/src/sources/transfer.mjs +196 -0
- package/src/sources/types.d.ts +353 -0
- package/src/sources/unicode.mjs +163 -0
- package/src/sources/upload.mjs +201 -0
- package/src/sources/uploadFiles.mjs +263 -0
- package/src/sources/wire.mjs +205 -0
- package/LICENSE +0 -190
package/README.md
CHANGED
|
@@ -35,7 +35,7 @@ const [current, version] = await Promise.all([
|
|
|
35
35
|
|
|
36
36
|
`access(refusedToken?)` returns `{ token, heygen, heygenApi, owner }` or null. It is read for every request. A refused bearer is passed back so the host can refresh it. `heygen` is the review API base. A host can inject its own `signedInRequest` to retain network tracing. `execFile` can likewise be injected to retain native process policy.
|
|
37
37
|
|
|
38
|
-
Video uploads require exact storage origins from trusted host configuration. Set `reviewUploadOrigins` on the host or supply a comma-separated `HYPERFRAMES_REVIEW_UPLOAD_ORIGINS` environment variable. Configure the bucket-specific HTTPS origins used by the signer. Use URL origins without paths, trailing slashes, or wildcards. Local tests can explicitly allow an HTTP origin on `127.0.0.1`. An empty list refuses every
|
|
38
|
+
Video and source uploads require exact storage origins from trusted host configuration. Set `reviewUploadOrigins` on the host or supply a comma-separated `HYPERFRAMES_REVIEW_UPLOAD_ORIGINS` environment variable. Configure the bucket-specific HTTPS origins used by the signer. Use URL origins without paths, trailing slashes, or wildcards. Local tests can explicitly allow an HTTP origin on `127.0.0.1`. An empty list refuses every upload.
|
|
39
39
|
|
|
40
40
|
Before first registration the host must obtain explicit audience consent and prepare a current MP4. `shareForReview` registers/enables the link; it does not imply playable media. Await `addVersion`, then check the server project/latest ready version before reporting success. `addFirstVersion` starts background work and is for long-lived hosts only. `open: () => boolean` on `addVersion` bounds the polling lifetime without discarding the saved operation. `filesForExport(projectPath, renderedFingerprint)` optionally supplies an already-synced matching snapshot. Omit it for ordinary video sharing.
|
|
41
41
|
|
|
@@ -50,3 +50,60 @@ Metadata mutations and version uploads use separate cross-process locks under `.
|
|
|
50
50
|
`snapshotProject` skips symlinks by default, retaining Desktop watcher, change-set and saved-render compatibility; `snapshotFiles` also skips symlink entries. Set `followSymlinks: true` for a link-aware full snapshot. The CLI-facing `projectFingerprint` opts into following linked media (with cycle detection), so edited linked files invalidate its cached render. Both comparisons ignore HTML ID stamps. Export/history metadata, caches, hidden folders and installed packages are excluded. Hosts must record the fingerprint of the actual rendered source and must not relabel an old render with a newer fingerprint.
|
|
51
51
|
|
|
52
52
|
Public type exports include `ShareHost`, `ReviewClient`, `ReviewShare`, `VersionRequest`, and `Wire`, plus `@hyperframes/share/types/reviewWire` and `types/share`. Per-module entry points preserve Desktop imports during adoption.
|
|
53
|
+
|
|
54
|
+
## Source snapshots
|
|
55
|
+
|
|
56
|
+
`shareSources({access, spaceId})` prepares editable source for a rendered version:
|
|
57
|
+
|
|
58
|
+
```js
|
|
59
|
+
const sources = shareSources({ access, spaceId });
|
|
60
|
+
const result = await sources.prepare(projectPath, renderedFingerprint);
|
|
61
|
+
if (result.at === "ready") {
|
|
62
|
+
await addVersion({
|
|
63
|
+
projectPath,
|
|
64
|
+
title,
|
|
65
|
+
client,
|
|
66
|
+
filesForExport: sources.filesForExport,
|
|
67
|
+
requireSource: true,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Handle `unavailable.reason` before creating the video version. Plan ineligibility
|
|
73
|
+
can use video-only; changed source, conflicts and unexpected upload failures need
|
|
74
|
+
an explicit choice to omit source. The CLI exposes `--video-only` for that choice.
|
|
75
|
+
`filesForExport` only validates already-prepared source; it does not upload.
|
|
76
|
+
|
|
77
|
+
The adapter uses the portable-source-v4 capture/manifest rules and
|
|
78
|
+
multipart transport. The pinned policy, Unicode 15.1 registry (with its license),
|
|
79
|
+
and manifest test vectors are checked into this repository. Source is captured through
|
|
80
|
+
verified file handles; upload bytes are streamed and checked against their hash.
|
|
81
|
+
Workspace limits, upload receipts and published snapshot identity are checked.
|
|
82
|
+
Pending uploads retain their original idempotency key across retries. A conflict
|
|
83
|
+
is never auto-resolved. When local source changes, the adapter cancels an older
|
|
84
|
+
pending upload before starting another; uploads under verification are preserved.
|
|
85
|
+
A stalled storage PUT stops after 60 seconds without progress and can retry with
|
|
86
|
+
the same upload identity.
|
|
87
|
+
|
|
88
|
+
Default state uses `.hyperframes/share-sync.<backend-hash>.json`, with strict
|
|
89
|
+
account/backend/realpath binding, private atomic writes, and a process lock.
|
|
90
|
+
Hosts can inject a compatible `store` or read-only `existingBinding` resolver.
|
|
91
|
+
An external Desktop binding is reused only when exact and settled; this adapter
|
|
92
|
+
never writes Desktop's binding or starts a second upload on its behalf.
|
|
93
|
+
|
|
94
|
+
Source sharing requires an authorized account and a workspace that supports editable project files.
|
|
95
|
+
Source-plan/feature eligibility remains server controlled. Automated fixtures do
|
|
96
|
+
not establish live entitlement, moderation, source download or outside-editor
|
|
97
|
+
acceptance. See [the source guide](https://hyperframes.heygen.com/developers/share-source).
|
|
98
|
+
|
|
99
|
+
Tests run locally without an account or service credentials:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
node --test packages/share/tests/*.test.mjs
|
|
103
|
+
bun run --cwd packages/share build
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
The bundled Unicode data records SHA-256 hashes for its inputs in `sources`.
|
|
107
|
+
Those files are published in the [Unicode 15.1 database](https://www.unicode.org/Public/15.1.0/ucd/).
|
|
108
|
+
Keep the included Unicode license with redistributed data. The pinned version
|
|
109
|
+
keeps path comparison consistent across Node versions and operating systems.
|
package/dist/index.d.mts
CHANGED
|
@@ -19,3 +19,4 @@ export { linkStateOf, reviewLinkStates } from "./reviewLinkStates.mjs";
|
|
|
19
19
|
export { AccountUnreachable, UNREACHABLE, readOrUnreachable, orIfUnreachable, signedInRequest } from "./signedInRequest.mjs";
|
|
20
20
|
export { HF_ID_ATTRIBUTE, contentKey, snapshotProject, snapshotFingerprint, snapshotFiles, diffChangedFiles, projectFingerprint } from "./projectSnapshot.mjs";
|
|
21
21
|
export { ShareBusyError, withProjectShareLock } from "./projectLock.mjs";
|
|
22
|
+
export { shareSources, shareSourceFingerprint, createProjectShareSourceStore } from "./shareSources.mjs";
|
package/dist/projectLock.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
/** @template T @param {string} projectPath @param {"review" | "export"} lane @param {() => Promise<T>} run
|
|
1
|
+
/** @template T @param {string} projectPath @param {"review" | "export" | "source"} lane @param {() => Promise<T>} run
|
|
2
2
|
* @param {{ timeoutMs?: number, signal?: AbortSignal }} [options] @returns {Promise<T>} */
|
|
3
|
-
export function withProjectShareLock<T>(projectPath: string, lane: "review" | "export", run: () => Promise<T>, { timeoutMs, signal }?: {
|
|
3
|
+
export function withProjectShareLock<T>(projectPath: string, lane: "review" | "export" | "source", run: () => Promise<T>, { timeoutMs, signal }?: {
|
|
4
4
|
timeoutMs?: number;
|
|
5
5
|
signal?: AbortSignal;
|
|
6
6
|
}): Promise<T>;
|
|
@@ -16,5 +16,7 @@ export function snapshotFiles(dir: string, files: Iterable<string>): Promise<Map
|
|
|
16
16
|
/** @param {Snapshot} before @param {Snapshot} after */
|
|
17
17
|
export function diffChangedFiles(before: Snapshot, after: Snapshot): string[];
|
|
18
18
|
export const HF_ID_ATTRIBUTE: RegExp;
|
|
19
|
-
export function projectFingerprint(projectPath: string
|
|
19
|
+
export function projectFingerprint(projectPath: string, options?: {
|
|
20
|
+
byContent?: boolean;
|
|
21
|
+
}): Promise<string>;
|
|
20
22
|
export type Snapshot = Map<string, unknown>;
|
package/dist/reviewExport.d.mts
CHANGED
|
@@ -56,6 +56,7 @@ export function versionFacts(dir: string): Promise<{
|
|
|
56
56
|
exportedAt: string;
|
|
57
57
|
projectId: string;
|
|
58
58
|
files?: boolean;
|
|
59
|
+
snapshot?: ExportRequest["snapshot"];
|
|
59
60
|
checksum?: string;
|
|
60
61
|
} | null;
|
|
61
62
|
source: "ready" | "none" | "not-mp4" | "stale";
|
|
@@ -69,6 +70,7 @@ export function versionOnPage(dir: string): Promise<{
|
|
|
69
70
|
exportedAt: string;
|
|
70
71
|
projectId: string;
|
|
71
72
|
files?: boolean;
|
|
73
|
+
snapshot?: ExportRequest["snapshot"];
|
|
72
74
|
checksum?: string;
|
|
73
75
|
} | null>;
|
|
74
76
|
/** Records that `versionId` carries files now, if it is still the last ready version.
|
|
@@ -116,6 +118,7 @@ export type VersionRecord = {
|
|
|
116
118
|
body: ExportRequest;
|
|
117
119
|
videoId: string | null;
|
|
118
120
|
projectId?: string | null;
|
|
121
|
+
renderKey?: string;
|
|
119
122
|
checkingSince?: number;
|
|
120
123
|
snapshotRefused?: boolean;
|
|
121
124
|
unanswered?: boolean;
|
|
@@ -127,8 +130,12 @@ export type VersionRecord = {
|
|
|
127
130
|
exportedAt: string;
|
|
128
131
|
projectId: string;
|
|
129
132
|
files?: boolean;
|
|
133
|
+
snapshot?: ExportRequest["snapshot"];
|
|
130
134
|
checksum?: string;
|
|
131
135
|
} | null;
|
|
136
|
+
failure?: (Failure & {
|
|
137
|
+
projectId: string;
|
|
138
|
+
}) | null;
|
|
132
139
|
};
|
|
133
140
|
/**
|
|
134
141
|
* `onAdded`: a version went ready, with whether it carries files and whether it is the page's first from here.
|
|
@@ -142,7 +149,7 @@ export type VersionHooks = {
|
|
|
142
149
|
/**
|
|
143
150
|
* `projectId` overrides the recorded review project; `open`: a held video is watched only while the project is open.
|
|
144
151
|
* `filesForExport`: the synced snapshot a new version carries, null for
|
|
145
|
-
* none.
|
|
152
|
+
* none. `allowPending` lets a host treat accepted processing or screening as a successful return.
|
|
146
153
|
*/
|
|
147
154
|
export type VersionRequest = VersionHooks & {
|
|
148
155
|
projectPath: string;
|
|
@@ -155,6 +162,9 @@ export type VersionRequest = VersionHooks & {
|
|
|
155
162
|
projectId?: string | null;
|
|
156
163
|
filesForExport?: FilesForRender;
|
|
157
164
|
fromReview?: FromReview | null;
|
|
165
|
+
allowPending?: boolean;
|
|
166
|
+
requireSource?: boolean;
|
|
167
|
+
omitSource?: boolean;
|
|
158
168
|
};
|
|
159
169
|
/**
|
|
160
170
|
* The snapshot of `rendered`, the project as the export was rendered; null when the folder is not that render.
|
package/dist/reviewUpload.d.mts
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
* @param {string} file @returns {Promise<string>} */
|
|
3
3
|
export function fileSha256Base64(file: string): Promise<string>;
|
|
4
4
|
/** @typedef {"sent" | "expired" | "refused" | "offline"} PutOutcome */
|
|
5
|
+
/** @param {string} url */
|
|
6
|
+
export function trustedUploadUrl(url: string): URL | null;
|
|
5
7
|
/** Streams the file to the signed URL through main's fetch (the app's proxy), the server's headers as given plus
|
|
6
8
|
* the length, and no bearer. Nothing sent or answered for `stallMs` ends it as offline, never "Uploading" forever.
|
|
7
9
|
* @param {{ put_url: string, headers: Record<string, string> }} upload @param {string} file @param {number} size
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** Content identity for sharing, including portable hidden render inputs. The Desktop watch inventory stays separate.
|
|
2
|
+
* @param {string} projectPath @returns {Promise<string | null>} */
|
|
3
|
+
export function shareSourceFingerprint(projectPath: string): Promise<string | null>;
|
|
4
|
+
/** Prepare source explicitly before adding the video version. No unexpected network or filesystem failure is
|
|
5
|
+
* converted to video-only. `filesForExport` revalidates the prepared bytes and never starts another upload.
|
|
6
|
+
* @param {import("./sources/adapterTypes.js").SourceOptions} options */
|
|
7
|
+
export function shareSources(options: import("./sources/adapterTypes.js").SourceOptions): {
|
|
8
|
+
prepare: (projectPath: string, rendered: string | null) => Promise<SourceResult>;
|
|
9
|
+
filesForExport: (projectPath: string, rendered: string | null) => Promise<import("./types/reviewWire.js").SnapshotRef | null>;
|
|
10
|
+
};
|
|
11
|
+
export { createProjectShareSourceStore } from "./sources/store.mjs";
|
|
12
|
+
export type Preparation = {
|
|
13
|
+
binding: Binding;
|
|
14
|
+
own: boolean;
|
|
15
|
+
external: boolean;
|
|
16
|
+
api: import("./sources/api.mjs").SourceApi;
|
|
17
|
+
store: import("./sources/adapterTypes.js").SourceStore;
|
|
18
|
+
options: import("./sources/adapterTypes.js").SourceOptions;
|
|
19
|
+
signal: AbortSignal;
|
|
20
|
+
fingerprint: (path: string) => Promise<string | null>;
|
|
21
|
+
rendered: string;
|
|
22
|
+
};
|
|
23
|
+
export type SourceResult = import("./sources/adapterTypes.js").SourceResult;
|
|
24
|
+
export type Binding = import("./sources/types.js").Binding;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { Binding, BindingKey, Manifest } from "./types.js";
|
|
2
|
+
import type { SnapshotRef } from "../types/reviewWire.js";
|
|
3
|
+
import type { SignedInAccess } from "../signedInRequest.mjs";
|
|
4
|
+
import type { SourceApi } from "./api.mjs";
|
|
5
|
+
|
|
6
|
+
export interface SourceStore {
|
|
7
|
+
read(key: BindingKey): Promise<Binding | null>;
|
|
8
|
+
write(binding: Binding): Promise<void>;
|
|
9
|
+
}
|
|
10
|
+
export type SourceResult =
|
|
11
|
+
| { at: "ready"; snapshot: SnapshotRef }
|
|
12
|
+
| { at: "unavailable"; reason: string };
|
|
13
|
+
export interface SourceOptions {
|
|
14
|
+
access: SignedInAccess;
|
|
15
|
+
spaceId: string;
|
|
16
|
+
store?: SourceStore;
|
|
17
|
+
enabled?: () => boolean;
|
|
18
|
+
fingerprint?: (path: string) => Promise<string | null>;
|
|
19
|
+
/** Host-owned Desktop bindings may be read for exact reuse, never overwritten. */
|
|
20
|
+
existingBinding?: (key: BindingKey) => Promise<Binding | null>;
|
|
21
|
+
fetchImpl?: typeof fetch;
|
|
22
|
+
signal?: AbortSignal;
|
|
23
|
+
verificationTimeoutMs?: number;
|
|
24
|
+
onProgress?: (bytes: number) => void;
|
|
25
|
+
/** Test/host seams; the default is the authenticated v3 client. */
|
|
26
|
+
api?: SourceApi;
|
|
27
|
+
wait?: (ms: number, signal?: AbortSignal) => Promise<void>;
|
|
28
|
+
}
|
|
29
|
+
export interface SourceRun {
|
|
30
|
+
binding: Binding;
|
|
31
|
+
manifest: Manifest;
|
|
32
|
+
api: SourceApi;
|
|
33
|
+
store: SourceStore;
|
|
34
|
+
fetchImpl: typeof fetch;
|
|
35
|
+
signal: AbortSignal;
|
|
36
|
+
wait: (ms: number, signal?: AbortSignal) => Promise<void>;
|
|
37
|
+
timeoutMs: number;
|
|
38
|
+
onProgress: (bytes: number) => void;
|
|
39
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/** @param {{ access: import("../signedInRequest.mjs").SignedInAccess, fetchImpl?: typeof fetch }} deps */
|
|
2
|
+
export function sourceApi({ access, fetchImpl }: {
|
|
3
|
+
access: import("../signedInRequest.mjs").SignedInAccess;
|
|
4
|
+
fetchImpl?: typeof fetch;
|
|
5
|
+
}): {
|
|
6
|
+
/** @param {string} space */
|
|
7
|
+
limits: (space: string) => Promise<{
|
|
8
|
+
at: "read";
|
|
9
|
+
syncAllowed: boolean;
|
|
10
|
+
maxCompositions: number;
|
|
11
|
+
compositions: number;
|
|
12
|
+
push: import("./types.js").Limits;
|
|
13
|
+
}>;
|
|
14
|
+
/** @param {string} space @param {string} composition @param {string} title */
|
|
15
|
+
create: (space: string, composition: string, title: string) => Promise<import("./types.js").Composition>;
|
|
16
|
+
/** @param {string} space @param {string} composition @param {string} snapshot */
|
|
17
|
+
snapshot: (space: string, composition: string, snapshot: string) => Promise<import("./types.js").Snapshot>;
|
|
18
|
+
/** @param {string} space @param {string} composition @param {import("./types.js").BeginRequest} body */
|
|
19
|
+
begin: (space: string, composition: string, body: import("./types.js").BeginRequest) => Promise<import("./types.js").BeginResult>;
|
|
20
|
+
/** @param {string} space @param {string} composition @param {string} upload @param {import("./types.js").GrantRequest[]} files */
|
|
21
|
+
uploadUrls: (space: string, composition: string, upload: string, files: import("./types.js").GrantRequest[]) => Promise<import("./types.js").FileGrant[]>;
|
|
22
|
+
/** @param {string} space @param {string} composition @param {string} upload */
|
|
23
|
+
status: (space: string, composition: string, upload: string) => Promise<import("./types.js").UploadReceipt>;
|
|
24
|
+
/** @param {string} space @param {string} composition @param {string} upload */
|
|
25
|
+
cancel: (space: string, composition: string, upload: string) => Promise<import("./types.js").UploadReceipt>;
|
|
26
|
+
/** @param {string} space @param {string} composition @param {string} upload */
|
|
27
|
+
submit: (space: string, composition: string, upload: string) => Promise<import("./types.js").UploadReceipt>;
|
|
28
|
+
};
|
|
29
|
+
export type SourceApi = ReturnType<typeof sourceApi>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** A file's SHA-256 by path, size, time and inode, kept for the launch so a check after a focus reads no bytes.
|
|
2
|
+
* Only a settled file's hash is kept. */
|
|
3
|
+
export function hashMemo(now?: () => number): {
|
|
4
|
+
/** @param {Found} file */
|
|
5
|
+
get: (file: Found) => string | null;
|
|
6
|
+
/** @param {Found} file @param {string} sha256 */
|
|
7
|
+
set: (file: Found, sha256: string) => void;
|
|
8
|
+
};
|
|
9
|
+
/** The file's SHA-256, read through the handle `openListed` checked; null when the file is not the one listed, or its
|
|
10
|
+
* bytes changed while being read. `root` is the project's real path; `resolve` finds a folder's real path.
|
|
11
|
+
* @param {string} root @param {Found} file @param {typeof realpath} [resolve] */
|
|
12
|
+
export function hashListedFile(root: string, file: Found, resolve?: typeof realpath): Promise<string | null>;
|
|
13
|
+
/** Captures `root` (a project's real path). `memo` reuses hashes of unchanged files; a send should pass a fresh one.
|
|
14
|
+
* @param {string} root @param {{ memo?: ReturnType<typeof hashMemo>, limits?: import("./types.js").Limits }} [options]
|
|
15
|
+
* @returns {Promise<Capture>} */
|
|
16
|
+
export function captureProject(root: string, { memo, limits }?: {
|
|
17
|
+
memo?: ReturnType<typeof hashMemo>;
|
|
18
|
+
limits?: import("./types.js").Limits;
|
|
19
|
+
}): Promise<Capture>;
|
|
20
|
+
export type CaptureProblem = import("./types.js").CaptureProblem;
|
|
21
|
+
export type Found = import("./types.js").Found;
|
|
22
|
+
export type Walk = import("./types.js").Walk;
|
|
23
|
+
export type Capture = import("./types.js").Capture;
|
|
24
|
+
import { realpath } from "node:fs/promises";
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** The typed error for a refusal: the envelope's code when it is one of the sync routes', else the status's.
|
|
2
|
+
* @param {Response} reply @returns {Promise<SyncApiError>} */
|
|
3
|
+
export function refusalOf(reply: Response): Promise<SyncApiError>;
|
|
4
|
+
/** A sync route's refusal or failure. `status` is 0 when the server was never reached. */
|
|
5
|
+
export class SyncApiError extends Error {
|
|
6
|
+
/** @param {SyncErrorKind} kind @param {{ status?: number, code?: string | null, param?: string | null,
|
|
7
|
+
* requestId?: string | null, retryAfterMs?: number | null, detail?: string }} [about] */
|
|
8
|
+
constructor(kind: SyncErrorKind, about?: {
|
|
9
|
+
status?: number;
|
|
10
|
+
code?: string | null;
|
|
11
|
+
param?: string | null;
|
|
12
|
+
requestId?: string | null;
|
|
13
|
+
retryAfterMs?: number | null;
|
|
14
|
+
detail?: string;
|
|
15
|
+
});
|
|
16
|
+
kind: import("./types.js").SyncErrorKind;
|
|
17
|
+
status: number;
|
|
18
|
+
code: string | null;
|
|
19
|
+
param: string | null;
|
|
20
|
+
requestId: string | null;
|
|
21
|
+
retryAfterMs: number | null;
|
|
22
|
+
detail: string | null;
|
|
23
|
+
}
|
|
24
|
+
export type SyncErrorKind = import("./types.js").SyncErrorKind;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** The first limit broken, in the server's fixed order (file count, file size, project size, manifest size), with
|
|
2
|
+
* the biggest files first; null when the push fits. @param {readonly { path: string, size: number }[]} files
|
|
3
|
+
* @param {number} manifestBytes @param {Limits} [limits] @returns {import("./types.js").OverLimit | null} */
|
|
4
|
+
export function overLimit(files: readonly {
|
|
5
|
+
path: string;
|
|
6
|
+
size: number;
|
|
7
|
+
}[], manifestBytes: number, limits?: Limits): import("./types.js").OverLimit | null;
|
|
8
|
+
/** @typedef {import("./types.js").Limits} Limits @typedef {import("./types.js").LimitName} LimitName */
|
|
9
|
+
/** @type {Limits} */
|
|
10
|
+
export const V1_LIMITS: Limits;
|
|
11
|
+
export type Limits = import("./types.js").Limits;
|
|
12
|
+
export type LimitName = import("./types.js").LimitName;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** The canonical bytes of a valid document. @param {ManifestDocument} document */
|
|
2
|
+
export function canonicalBytes(document: ManifestDocument): Buffer<ArrayBuffer>;
|
|
3
|
+
/** Validates a manifest document as the server does; throws ManifestInvalid. @param {unknown} raw
|
|
4
|
+
* @param {boolean} [stored] @returns {Manifest} */
|
|
5
|
+
export function parseManifest(raw: unknown, stored?: boolean): Manifest;
|
|
6
|
+
/** Stored manifest bytes, which must already be canonical so their hash is the snapshot's manifest_sha256.
|
|
7
|
+
* @param {Uint8Array} body @returns {Manifest} */
|
|
8
|
+
export function parseCanonicalManifest(body: Uint8Array): Manifest;
|
|
9
|
+
export const ENTRYPOINT: "index.html";
|
|
10
|
+
/** @typedef {import("./types.js").ManifestFile} ManifestFile @typedef {import("./types.js").Violation} Violation
|
|
11
|
+
* @typedef {import("./types.js").ManifestDocument} ManifestDocument @typedef {import("./types.js").Manifest} Manifest */
|
|
12
|
+
/** Why a manifest was refused; `code` is the first violation's, in the server's order. */
|
|
13
|
+
export class ManifestInvalid extends Error {
|
|
14
|
+
/** @param {Violation[]} violations */
|
|
15
|
+
constructor(violations: Violation[]);
|
|
16
|
+
violations: import("./types.js").Violation[];
|
|
17
|
+
code: string;
|
|
18
|
+
}
|
|
19
|
+
export function parseStoredManifest(raw: unknown): Manifest;
|
|
20
|
+
export type ManifestFile = import("./types.js").ManifestFile;
|
|
21
|
+
export type Violation = import("./types.js").Violation;
|
|
22
|
+
export type ManifestDocument = import("./types.js").ManifestDocument;
|
|
23
|
+
export type Manifest = import("./types.js").Manifest;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** The first rule matching a path's folded spelling. @param {string} path @returns {PathRule} */
|
|
2
|
+
export function classifyPath(path: string): PathRule;
|
|
3
|
+
/** `path` unchanged when every client can store it and the policy lets it travel; throws PathViolation otherwise,
|
|
4
|
+
* naming the first failing check in the registry's order. @param {string} path
|
|
5
|
+
* @param {boolean} [stored] */
|
|
6
|
+
export function validatePath(path: string, stored?: boolean): string;
|
|
7
|
+
/** Refuses duplicates, and any two paths (or a path and a directory prefix) one filesystem would store as one name.
|
|
8
|
+
* Each path must already pass validatePath. @param {readonly string[]} paths */
|
|
9
|
+
export function checkInventoryPaths(paths: readonly string[]): void;
|
|
10
|
+
/** Whether no file under folder `dir` can be listed, so a capture need not open it: its first matching rule
|
|
11
|
+
* excludes the folder and all below it, and no include rule before that one can reach inside. @param {string} dir */
|
|
12
|
+
export function excludedFolder(dir: string): boolean;
|
|
13
|
+
/** @typedef {import("./types.js").PathRule} PathRule @typedef {import("./types.js").PolicyDocument} PolicyDocument */
|
|
14
|
+
/** A path the policy refuses: `code` is the golden vectors' shared vocabulary, `rule` the excluding rule's id. */
|
|
15
|
+
export class PathViolation extends Error {
|
|
16
|
+
/** @param {string} code @param {string} path @param {string} [rule] */
|
|
17
|
+
constructor(code: string, path: string, rule?: string);
|
|
18
|
+
code: string;
|
|
19
|
+
path: string;
|
|
20
|
+
rule: string | null;
|
|
21
|
+
}
|
|
22
|
+
export function prefiltered(name: string): boolean;
|
|
23
|
+
export function skippedSymlink(path: string): boolean;
|
|
24
|
+
export type PathRule = import("./types.js").PathRule;
|
|
25
|
+
export type PolicyDocument = import("./types.js").PolicyDocument;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** A predicate for the paths the project's render-outputs.json lists, and everything under them; none when the file
|
|
2
|
+
* is missing or not version 1. @param {string} root @returns {Promise<(rel: string) => boolean>} */
|
|
3
|
+
export function readRenderOutputs(root: string): Promise<(rel: string) => boolean>;
|
|
4
|
+
/** An open agent turn (its history-turn.json exists) or a project lock whose holder still runs.
|
|
5
|
+
* @param {string} root */
|
|
6
|
+
export function settledState(root: string): Promise<{
|
|
7
|
+
agentTurnOpen: boolean;
|
|
8
|
+
projectLocked: boolean;
|
|
9
|
+
}>;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
/** Project-local state is excluded by the portable source policy. Account/root mismatches fail closed, including copied folders.
|
|
2
|
+
* @returns {import("./adapterTypes.js").SourceStore} */
|
|
3
|
+
export function createProjectShareSourceStore(): import("./adapterTypes.js").SourceStore;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/** PUTs bytes of `file` to a signed URL with exactly the headers the grant signed. Resolves once storage keeps them;
|
|
2
|
+
* the bytes sent must hash to `sha256`, or it throws `changed` whatever storage answered.
|
|
3
|
+
* @param {{ fetchImpl: typeof fetch, url: string, headers: Record<string, string>, file: string, path: string,
|
|
4
|
+
* start: number, length: number, sha256: string, signal: AbortSignal, onBytes?: (bytes: number) => void, stallMs?: number }} put */
|
|
5
|
+
export function putRange(put: {
|
|
6
|
+
fetchImpl: typeof fetch;
|
|
7
|
+
url: string;
|
|
8
|
+
headers: Record<string, string>;
|
|
9
|
+
file: string;
|
|
10
|
+
path: string;
|
|
11
|
+
start: number;
|
|
12
|
+
length: number;
|
|
13
|
+
sha256: string;
|
|
14
|
+
signal: AbortSignal;
|
|
15
|
+
onBytes?: (bytes: number) => void;
|
|
16
|
+
stallMs?: number;
|
|
17
|
+
}): Promise<void>;
|
|
18
|
+
/** Each part's SHA-256 (parts of `partBytes`, the last one shorter) and the whole file's, in one read.
|
|
19
|
+
* @param {string} file @param {number} size @param {number} partBytes @param {AbortSignal} signal */
|
|
20
|
+
export function partDigests(file: string, size: number, partBytes: number, signal: AbortSignal): Promise<{
|
|
21
|
+
parts: string[];
|
|
22
|
+
whole: string;
|
|
23
|
+
complete: boolean;
|
|
24
|
+
}>;
|
|
25
|
+
/** Runs `work` over `items`, `limit` at a time. The first failure stops new work; the rest finish, then it throws.
|
|
26
|
+
* @template T @param {readonly T[]} items @param {number} limit @param {(item: T) => Promise<void>} work */
|
|
27
|
+
export function eachLimit<T>(items: readonly T[], limit: number, work: (item: T) => Promise<void>): Promise<void>;
|
|
28
|
+
/** Why a transfer stopped. `expired`: the signed URL no longer works (ask for a new one). `exists`: a create-only PUT
|
|
29
|
+
* found the object already written. `changed`: the file's bytes are not the ones captured. `unreadable`: the file
|
|
30
|
+
* could not be read (a drive unplugged, a file deleted). `corrupt`: downloaded bytes do not hash to what was asked.
|
|
31
|
+
* `transient`: the network or the store failed; the same request may be sent again. */
|
|
32
|
+
export class TransferError extends Error {
|
|
33
|
+
/** @param {"expired" | "exists" | "changed" | "unreadable" | "corrupt" | "transient" | "refused"} kind
|
|
34
|
+
* @param {{ status?: number, path?: string }} [about] */
|
|
35
|
+
constructor(kind: "expired" | "exists" | "changed" | "unreadable" | "corrupt" | "transient" | "refused", about?: {
|
|
36
|
+
status?: number;
|
|
37
|
+
path?: string;
|
|
38
|
+
});
|
|
39
|
+
kind: "expired" | "refused" | "changed" | "unreadable" | "exists" | "corrupt" | "transient";
|
|
40
|
+
status: number;
|
|
41
|
+
path: string | null;
|
|
42
|
+
}
|
|
43
|
+
export function isAbort(error: unknown): boolean;
|
|
44
|
+
export function realWait(ms: number, signal?: AbortSignal): Promise<undefined>;
|
|
45
|
+
export function backoffMs(attempt: number, askedMs?: number | null, baseMs?: number, maxMs?: number): number;
|