@hyperframes/share 0.0.0-stage → 0.9.21

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.
Files changed (54) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +51 -2
  3. package/dist/appOwnedPaths.d.mts +6 -0
  4. package/dist/fromReview.d.mts +15 -0
  5. package/dist/grantRefusal.d.mts +8 -0
  6. package/dist/host.d.mts +29 -0
  7. package/dist/hosts.d.ts +39 -0
  8. package/dist/index.d.mts +21 -0
  9. package/dist/projectLock.d.mts +17 -0
  10. package/dist/projectSnapshot.d.mts +20 -0
  11. package/dist/reviewAudience.d.mts +3 -0
  12. package/dist/reviewClient.d.mts +122 -0
  13. package/dist/reviewExport.d.mts +172 -0
  14. package/dist/reviewFiles.d.mts +43 -0
  15. package/dist/reviewLinkStates.d.mts +18 -0
  16. package/dist/reviewOpenCalls.d.mts +16 -0
  17. package/dist/reviewShare.d.mts +106 -0
  18. package/dist/reviewSwitch.d.mts +53 -0
  19. package/dist/reviewUpload.d.mts +16 -0
  20. package/dist/reviewVersionSentences.d.mts +2 -0
  21. package/dist/signedInRequest.d.mts +56 -0
  22. package/dist/storage.d.mts +20 -0
  23. package/dist/types/access.d.ts +12 -0
  24. package/dist/types/review.d.ts +59 -0
  25. package/dist/types/reviewWire.d.ts +200 -0
  26. package/dist/types/share.d.ts +119 -0
  27. package/dist/types/shareOpen.d.ts +96 -0
  28. package/package.json +112 -3
  29. package/scripts/copy-types.mjs +3 -0
  30. package/src/appOwnedPaths.mjs +27 -0
  31. package/src/fromReview.mjs +27 -0
  32. package/src/grantRefusal.mjs +28 -0
  33. package/src/host.mjs +46 -0
  34. package/src/hosts.d.ts +39 -0
  35. package/src/index.mjs +82 -0
  36. package/src/projectLock.mjs +120 -0
  37. package/src/projectSnapshot.mjs +120 -0
  38. package/src/reviewAudience.mjs +24 -0
  39. package/src/reviewClient.mjs +260 -0
  40. package/src/reviewExport.mjs +827 -0
  41. package/src/reviewFiles.mjs +98 -0
  42. package/src/reviewLinkStates.mjs +47 -0
  43. package/src/reviewOpenCalls.mjs +40 -0
  44. package/src/reviewShare.mjs +645 -0
  45. package/src/reviewSwitch.mjs +430 -0
  46. package/src/reviewUpload.mjs +98 -0
  47. package/src/reviewVersionSentences.mjs +48 -0
  48. package/src/signedInRequest.mjs +125 -0
  49. package/src/storage.mjs +116 -0
  50. package/src/types/access.d.ts +12 -0
  51. package/src/types/review.d.ts +59 -0
  52. package/src/types/reviewWire.d.ts +200 -0
  53. package/src/types/share.d.ts +119 -0
  54. package/src/types/shareOpen.d.ts +96 -0
@@ -0,0 +1,172 @@
1
+ /** ffprobe's JSON as facts, or null when it names no picture or no length. @param {string} stdout */
2
+ export function factsOf(stdout: string): {
3
+ duration: number;
4
+ width: any;
5
+ height: any;
6
+ } | null;
7
+ /** What the popover shows about versions. `last.current`: the project as it is now and, when the server says, still
8
+ * its latest ready version (a removed one, or a newer one from elsewhere, is not "on the page").
9
+ * @param {string} projectPath @param {{ latestReadyVersionId?: string | null }} [server] */
10
+ export function reviewVersion(projectPath: string, server?: {
11
+ latestReadyVersionId?: string | null;
12
+ }): Promise<{
13
+ held?: boolean | undefined;
14
+ failure: string;
15
+ retryable: boolean;
16
+ exportHelps: boolean;
17
+ last: {
18
+ exportedAt: string;
19
+ current: boolean;
20
+ } | null;
21
+ source: "ready" | "none" | "not-mp4" | "stale";
22
+ exporting: import("./types/share.js").ReviewExportProgress | null;
23
+ } | {
24
+ failure: null;
25
+ last: {
26
+ exportedAt: string;
27
+ current: boolean;
28
+ } | null;
29
+ source: "ready" | "none" | "not-mp4" | "stale";
30
+ exporting: import("./types/share.js").ReviewExportProgress | null;
31
+ }>;
32
+ /** Adds the newest export as a version and resolves once it is ready, failed, or out of watch, with the sentence when
33
+ * it did not go up. One run per project; a second asks nothing and resolves at once. A run onto another project keeps
34
+ * a failure from before the upload to itself: the recorded page is not the one it failed on.
35
+ * @param {VersionRequest} request @returns {Promise<string | null>} */
36
+ export function addVersion(request: VersionRequest): Promise<string | null>;
37
+ /** The popover opening reads a held video again: a running watch reads now, and one that ran out reads the video
38
+ * back once, so a video staff approved or refused since shows as it is. A read that finds it still held changes
39
+ * nothing; Check again starts a new watch.
40
+ * @param {string} projectPath @param {ReviewClient} client @param {FromReview | null} [fromReview] */
41
+ export function recheckHeld(projectPath: string, client: ReviewClient, fromReview?: FromReview | null): Promise<undefined>;
42
+ /** Settle a saved upload after its host exits, without competing with an active uploader.
43
+ * @param {string} projectPath @param {ReviewClient} client @param {FromReview | null} [fromReview] */
44
+ export function refreshReviewExport(projectPath: string, client: ReviewClient, fromReview?: FromReview | null): Promise<void>;
45
+ /** The first share puts the newest export up with the link, when there is one to put and no version yet.
46
+ * @param {VersionRequest} request */
47
+ export function addFirstVersion(request: VersionRequest): Promise<void>;
48
+ /** What the files line and a later attach read of a project's versions: the newest one this app put on the page, with
49
+ * whether the project is still as it was then; whether the newest export is the project now; whether a run is out.
50
+ * @param {string} dir */
51
+ export function versionFacts(dir: string): Promise<{
52
+ last: {
53
+ current: boolean;
54
+ fingerprint: string;
55
+ versionId: string;
56
+ exportedAt: string;
57
+ projectId: string;
58
+ files?: boolean;
59
+ checksum?: string;
60
+ } | null;
61
+ source: "ready" | "none" | "not-mp4" | "stale";
62
+ running: boolean;
63
+ }>;
64
+ /** The newest version this app put on the page, read without hashing the project; null while a run is out.
65
+ * @param {string} dir */
66
+ export function versionOnPage(dir: string): Promise<{
67
+ fingerprint: string;
68
+ versionId: string;
69
+ exportedAt: string;
70
+ projectId: string;
71
+ files?: boolean;
72
+ checksum?: string;
73
+ } | null>;
74
+ /** Records that `versionId` carries files now, if it is still the last ready version.
75
+ * @param {string} dir @param {string} versionId @param {string} [projectId] */
76
+ export function markFiles(dir: string, versionId: string, projectId?: string): Promise<void>;
77
+ /** A pending operation is not the last ready version. @param {string} projectPath */
78
+ export function pendingReviewExport(projectPath: string): Promise<{
79
+ projectId: string | null;
80
+ videoId: string | null;
81
+ operationId: string;
82
+ fingerprint: string | null;
83
+ checksum: string;
84
+ } | null>;
85
+ export type SavedLast = Omit<NonNullable<VersionRecord["last"]>, "fingerprint"> & {
86
+ fingerprint?: string;
87
+ };
88
+ export type ReviewClient = import("./reviewClient.mjs").ReviewClient;
89
+ export type ExportDTO = import("./types/reviewWire.js").ExportDTO;
90
+ export type ExportRequest = import("./types/reviewWire.js").ExportRequest;
91
+ export type ReviewVersion = import("./types/share.js").ReviewVersion;
92
+ export type ReviewExportProgress = import("./types/share.js").ReviewExportProgress;
93
+ export type VideoFacts = {
94
+ duration: number;
95
+ width: number;
96
+ height: number;
97
+ };
98
+ export type FromReview = import("./fromReview.mjs").FromReview;
99
+ export type ReviewSent = {
100
+ since: number;
101
+ value?: "yes" | "no";
102
+ };
103
+ export type Added = {
104
+ files: boolean;
105
+ first: boolean;
106
+ fromReview?: "yes" | "no";
107
+ };
108
+ /**
109
+ * `pending`: the create body as sent and its video once answered, so a retry is the same; `snapshotRefused`: the
110
+ * server refused its snapshot, so it goes up video-only; `unanswered`: its create may have gone out without a
111
+ * definitive result; `reviewSent`: cleanup cutoff, with attribution only when the accepted attempt is
112
+ * known; `last`: the newest version.
113
+ */
114
+ export type VersionRecord = {
115
+ pending: {
116
+ body: ExportRequest;
117
+ videoId: string | null;
118
+ projectId?: string | null;
119
+ checkingSince?: number;
120
+ snapshotRefused?: boolean;
121
+ unanswered?: boolean;
122
+ reviewSent?: ReviewSent;
123
+ } | null;
124
+ last: {
125
+ fingerprint: string;
126
+ versionId: string;
127
+ exportedAt: string;
128
+ projectId: string;
129
+ files?: boolean;
130
+ checksum?: string;
131
+ } | null;
132
+ };
133
+ /**
134
+ * `onAdded`: a version went ready, with whether it carries files and whether it is the page's first from here.
135
+ */
136
+ export type VersionHooks = {
137
+ onProgress?: (progress: ReviewExportProgress | null) => void;
138
+ onUploaded?: () => Promise<void>;
139
+ open?: () => boolean;
140
+ onAdded?: (added: Added) => void;
141
+ };
142
+ /**
143
+ * `projectId` overrides the recorded review project; `open`: a held video is watched only while the project is open.
144
+ * `filesForExport`: the synced snapshot a new version carries, null for
145
+ * none.
146
+ */
147
+ export type VersionRequest = VersionHooks & {
148
+ projectPath: string;
149
+ title: string;
150
+ client: ReviewClient;
151
+ put?: typeof putFile;
152
+ sleep?: (ms: number) => Promise<void>;
153
+ probe?: (file: string) => Promise<VideoFacts | null>;
154
+ now?: () => number;
155
+ projectId?: string | null;
156
+ filesForExport?: FilesForRender;
157
+ fromReview?: FromReview | null;
158
+ };
159
+ /**
160
+ * The snapshot of `rendered`, the project as the export was rendered; null when the folder is not that render.
161
+ */
162
+ export type FilesForRender = (projectPath: string, rendered: string | null) => Promise<import("./types/reviewWire.js").SnapshotRef | null>;
163
+ export type Said = {
164
+ sentence: string;
165
+ final: boolean;
166
+ exportHelps: boolean;
167
+ held?: boolean;
168
+ };
169
+ export type Failure = Said & {
170
+ exportKey: string;
171
+ };
172
+ import { putFile } from "./reviewUpload.mjs";
@@ -0,0 +1,43 @@
1
+ /** Files vs. the last sync: `off` (share_files or sync off), `elsewhere`
2
+ * (syncs to `workspace`, not ours), `differs` (no sync, syncing, writing, changed), `matches` (a version can carry it).
3
+ * @typedef {{ at: "off" } | { at: "differs" } | { at: "elsewhere", workspace: string }
4
+ * | { at: "matches", snapshot: import("./types/reviewWire.js").SnapshotRef }} SyncedFiles */
5
+ /** @typedef {import("./types/share.js").ReviewFiles} FilesAnswer */
6
+ /** The files line from what is known: the version on the page carrying files says so first; a project synced to
7
+ * another workspace says which, since neither a sync nor a render here fixes it; a project moved past its render is
8
+ * `changed`; a render not on the page yet is `included` when the version it would add carries files.
9
+ * @param {Awaited<ReturnType<typeof versionFacts>>} facts @param {SyncedFiles} found @returns {FilesAnswer} */
10
+ export function filesStateOf({ last, source }: Awaited<ReturnType<typeof versionFacts>>, found: SyncedFiles): FilesAnswer;
11
+ /** `match` answers the files line; `forRender` is the only way to files for a version, held to its render (the same
12
+ * one an export's `filesForExport` is). @param {{ client: Pick<import("./reviewClient.mjs").ReviewClient,
13
+ * "attachSnapshot">, match: (projectPath: string) => Promise<SyncedFiles>,
14
+ * forRender: import("./reviewExport.mjs").FilesForRender }} deps */
15
+ export function reviewFiles({ client, match, forRender }: {
16
+ client: Pick<import("./reviewClient.mjs").ReviewClient, "attachSnapshot">;
17
+ match: (projectPath: string) => Promise<SyncedFiles>;
18
+ forRender: import("./reviewExport.mjs").FilesForRender;
19
+ }): {
20
+ /** Attaches the last sync to the version on the page when it can; anything else leaves it as it is. */
21
+ attach: (dir: string) => Promise<void>;
22
+ /** The files line, once any attach a sync made possible has gone in. @param {string} dir
23
+ * @returns {Promise<FilesAnswer>} */
24
+ state: (dir: string) => Promise<FilesAnswer>;
25
+ };
26
+ /**
27
+ * Files vs. the last sync: `off` (share_files or sync off), `elsewhere`
28
+ * (syncs to `workspace`, not ours), `differs` (no sync, syncing, writing, changed), `matches` (a version can carry it).
29
+ */
30
+ export type SyncedFiles = {
31
+ at: "off";
32
+ } | {
33
+ at: "differs";
34
+ } | {
35
+ at: "elsewhere";
36
+ workspace: string;
37
+ } | {
38
+ at: "matches";
39
+ snapshot: import("./types/reviewWire.js").SnapshotRef;
40
+ };
41
+ export type FilesAnswer = import("./types/share.js").ReviewFiles;
42
+ export type ReviewFiles = ReturnType<typeof reviewFiles>;
43
+ import { versionFacts } from "./reviewExport.mjs";
@@ -0,0 +1,18 @@
1
+ /** @param {import("./types/reviewWire.js").ShareDTO} share @returns {LinkStateRead} */
2
+ export function linkStateOf(share: import("./types/reviewWire.js").ShareDTO): LinkStateRead;
3
+ /** Each project's link, read a few at a time; a read that fails leaves that project out, so its row shows no state.
4
+ * @param {{ client: { share: (id: string) => Promise<import("./types/reviewWire.js").ShareDTO> },
5
+ * projectIds: string[] }} request
6
+ * @returns {Promise<Record<string, LinkStateRead>>} */
7
+ export function reviewLinkStates({ client, projectIds }: {
8
+ client: {
9
+ share: (id: string) => Promise<import("./types/reviewWire.js").ShareDTO>;
10
+ };
11
+ projectIds: string[];
12
+ }): Promise<Record<string, LinkStateRead>>;
13
+ export type LinkState = "on" | "off" | "paused" | "taken-down";
14
+ export type LinkStateRead = {
15
+ state: LinkState;
16
+ access: import("./types/share.js").AccessMode;
17
+ pausedFor?: "report" | "plan";
18
+ };
@@ -0,0 +1,16 @@
1
+ /** @param {(what: string, method: string, route: string, body?: unknown) => Promise<any>} tokenCall */
2
+ export function shareOpenCalls(tokenCall: (what: string, method: string, route: string, body?: unknown) => Promise<any>): {
3
+ /** C1, for the requested version or the latest. @param {string} token @param {string | null} versionId
4
+ * @returns {Promise<import("./types/shareOpen.js").OpenSummaryDTO>} */
5
+ openSummary: (token: string, versionId: string | null) => Promise<import("./types/shareOpen.js").OpenSummaryDTO>;
6
+ /** C2: the snapshot's manifest and the first signed URLs (at most 500 files).
7
+ * @param {string} token @param {string} versionId @returns {Promise<import("./types/shareOpen.js").VersionFilesDTO>} */
8
+ versionFiles: (token: string, versionId: string) => Promise<import("./types/shareOpen.js").VersionFilesDTO>;
9
+ /** C2's refresh: URLs for `sha256s` (at most 500) of the same snapshot, by the token the last answer carried.
10
+ * @param {string} token @param {string} versionId @param {string} continuation @param {string[]} sha256s
11
+ * @returns {Promise<import("./types/shareOpen.js").FileUrlsDTO>} */
12
+ fileUrls: (token: string, versionId: string, continuation: string, sha256s: string[]) => Promise<import("./types/shareOpen.js").FileUrlsDTO>;
13
+ /** C3: a copy finished; the same handoff again is the same copy. The server answers 204 with no body.
14
+ * @param {string} token @param {string} versionId @param {string | null} handoffId @returns {Promise<null>} */
15
+ logCopy: (token: string, versionId: string, handoffId: string | null) => Promise<null>;
16
+ };
@@ -0,0 +1,106 @@
1
+ /** Every space the account is in, the signed-in one marked: the later workspace switch lists them; v1 names one.
2
+ * @param {{ client: ReviewClient }} request @returns {Promise<ReviewSpace[]>} */
3
+ export function reviewSpaces({ client }: {
4
+ client: ReviewClient;
5
+ }): Promise<ReviewSpace[]>;
6
+ /** The share as the person sees it: owner routes do not hold a token to its workspace, so a page in another one
7
+ * than Desktop is signed into works as any other, and is only named (`elsewhere`). A failed list names nothing.
8
+ * @param {ReviewClient} client @param {ShareDTO} share @returns {Promise<ReviewShare>} */
9
+ export function shown(client: ReviewClient, share: ShareDTO): Promise<ReviewShare>;
10
+ /** A line per project: each step runs once the one before it settled; `busy` while one runs or waits. */
11
+ export function projectLine(): {
12
+ inTurn: <T>(projectPath: string, run: () => Promise<T>) => Promise<T>;
13
+ busy: (projectPath: string) => boolean;
14
+ };
15
+ /** Applies the person's choice over whatever else the share holds; a stale revision is read again once.
16
+ * @param {ReviewClient} client @param {string} id @param {ShareDTO} share
17
+ * @param {Partial<Pick<ShareDTO, "access_mode" | "default_role">>} change @returns {Promise<ShareDTO>} */
18
+ export function changeShare(client: ReviewClient, id: string, share: ShareDTO, change: Partial<Pick<ShareDTO, "access_mode" | "default_role">>): Promise<ShareDTO>;
19
+ /** The server's id for a project this app finished sharing, else null; the comment feed reads it.
20
+ * @param {string} projectPath @returns {Promise<string | null>} */
21
+ export function reviewProjectId(projectPath: string): Promise<string | null>;
22
+ /** Which local project each review project was shared from: review project id to its folder.
23
+ * @param {string[]} paths @returns {Promise<Record<string, string>>} */
24
+ export function localReviewProjects(paths: string[]): Promise<Record<string, string>>;
25
+ /** @param {string} dir @returns {Promise<import("./reviewSwitch.mjs").Switch | null>} */
26
+ export function readReviewSwitch(dir: string): Promise<import("./reviewSwitch.mjs").Switch | null>;
27
+ /** Points the record at another review project, keeping every other field the record holds.
28
+ * @param {string} projectPath @param {string} projectId @param {string} spaceId */
29
+ export function rebindRecord(projectPath: string, projectId: string, spaceId: string): Promise<void>;
30
+ /** Shares a project anew from the signed-in account: its record set aside, then `share` makes the new link, and the
31
+ * feed in view follows to the new page.
32
+ * @template T @param {string} projectPath @param {() => Promise<T>} share @returns {Promise<T>} */
33
+ export function shareAnew<T>(projectPath: string, share: () => Promise<T>): Promise<T>;
34
+ /** The link as the server holds it now; null until the first share finished.
35
+ * @param {{ projectPath: string, client: ReviewClient }} request @returns {Promise<ReviewShare | null>} */
36
+ export function reviewStatus({ projectPath, client }: {
37
+ projectPath: string;
38
+ client: ReviewClient;
39
+ }): Promise<ReviewShare | null>;
40
+ /** @param {ReviewClient} client @param {string} id @param {ShareDTO} share
41
+ * @param {import("./types/share.js").AccessMode} [audience] */
42
+ export function applyFirstAudience(client: ReviewClient, id: string, share: ShareDTO, audience?: import("./types/share.js").AccessMode): Promise<import("./types/reviewWire.js").ShareDTO>;
43
+ /** The one sentence a person reads for a failed call; the log keeps the status and reason. `fallback` names the
44
+ * call when the reason has no sentence of its own.
45
+ * @param {unknown} error @param {string} [fallback] */
46
+ export function reviewSentence(error: unknown, fallback?: string): string;
47
+ /** Locks each open binding involved in this removal before sending DELETE, including a move's source.
48
+ * @param {string} projectId @param {ReviewClient} client @param {string[]} projectPaths */
49
+ export function removeReviewProject(projectId: string, client: ReviewClient, projectPaths: string[]): Promise<void>;
50
+ /** The owner workspace of an unfinished binding takes precedence over the active workspace.
51
+ * @param {{client: ReviewClient, projectPath: string}} request @returns {Promise<ReviewSpace | null>} */
52
+ export function reviewDestination({ client, projectPath }: {
53
+ client: ReviewClient;
54
+ projectPath: string;
55
+ }): Promise<ReviewSpace | null>;
56
+ export function setReviewSite(now: string): void;
57
+ export function reviewRecordPath(projectPath: string): string;
58
+ export function takenDown(share: ShareDTO): boolean;
59
+ export function listSpaces(client: ReviewClient): Promise<import("./types/reviewWire.js").SpaceDTO[] | never[]>;
60
+ export function ownerOf(reason: string): string | null;
61
+ export function oneAtATime<T>(projectPath: string, run: () => Promise<T>): Promise<T>;
62
+ export function reviewRecord(projectPath: string): Promise<ReviewRecord | null>;
63
+ export function reviewSwitchPath(projectPath: string): string;
64
+ export function onRebound(listener: (projectPath: string) => void): undefined;
65
+ export function setAsideReview(projectPath: string): Promise<void>;
66
+ export function readShare(client: ReviewClient, id: string, projectPath?: string): Promise<import("./types/reviewWire.js").ShareDTO>;
67
+ export function shareForReview({ projectPath, title, client, spaceId, audience }: {
68
+ projectPath: string;
69
+ title: string;
70
+ client: ReviewClient;
71
+ spaceId?: string | null;
72
+ audience?: import("./types/share.js").AccessMode | undefined;
73
+ }): Promise<import("./types/share.js").ReviewShare>;
74
+ export function setReviewAccess({ projectPath, client, change }: {
75
+ projectPath: string;
76
+ client: ReviewClient;
77
+ change: ReviewAccessChange;
78
+ }): Promise<ReviewShare>;
79
+ export function turnOffReviewLink({ projectPath, client }: {
80
+ projectPath: string;
81
+ client: ReviewClient;
82
+ }): Promise<ReviewShare>;
83
+ export function markReviewRemoved(projectPath: string, projectId: string): Promise<void>;
84
+ export function removeLocalReview(projectPath: string, projectId: string, client: ReviewClient): Promise<void>;
85
+ export type ReviewClient = import("./reviewClient.mjs").ReviewClient;
86
+ export type ShareDTO = import("./types/reviewWire.js").ShareDTO;
87
+ export type ReviewShare = import("./types/share.js").ReviewShare;
88
+ export type ReviewSpace = import("./types/share.js").ReviewSpace;
89
+ export type ReviewAccessChange = import("./types/share.js").ReviewAccessChange;
90
+ /**
91
+ * Persist studioProjectId/spaceId before sending so a lost reply retries the same project and space; projectId after.
92
+ * opened marks the first completed share; site/ownerEmail bind the record to its backend/account.
93
+ */
94
+ export type ReviewRecord = {
95
+ studioProjectId: string;
96
+ spaceId: string | null;
97
+ projectId: string | null;
98
+ opened: boolean;
99
+ site: string;
100
+ ownerEmail: string | null;
101
+ removed?: boolean;
102
+ registration?: import("./types/reviewWire.js").RegisterProjectRequest | undefined;
103
+ };
104
+ export type SavedIdentity = {
105
+ studioProjectId: string;
106
+ } & Partial<Pick<ReviewRecord, "spaceId" | "projectId" | "opened" | "removed">>;
@@ -0,0 +1,53 @@
1
+ /** What the popover says of an old link still on: where it is, and whether retries keep failing.
2
+ * @param {string} projectPath @returns {Promise<ReviewShare["oldLink"]>} */
3
+ export function oldLinkOf(projectPath: string): Promise<ReviewShare["oldLink"]>;
4
+ /** The sentence for a failed change of space; any other failure reads as the share's, or as `fallback`.
5
+ * @param {unknown} error @param {string} [fallback] */
6
+ export function switchSentence(error: unknown, fallback?: string): string;
7
+ export function switchOperationId(studioProjectId: string, spaceId: string, from: string, title: string): string;
8
+ export function retryOldLink({ projectPath, client }: {
9
+ projectPath: string;
10
+ client: ReviewClient;
11
+ }): Promise<void>;
12
+ export function changeSpace({ projectPath, title, client, spaceId, upload, canUpload, audience }: {
13
+ projectPath: string;
14
+ title: string;
15
+ client: ReviewClient;
16
+ spaceId: string;
17
+ upload: Upload;
18
+ canUpload: CanUpload;
19
+ audience?: import("./types/share.js").AccessMode | undefined;
20
+ }): Promise<ReviewShare>;
21
+ export function resumeSpaceChange({ projectPath, client, upload, canUpload }: {
22
+ projectPath: string;
23
+ client: ReviewClient;
24
+ upload: Upload;
25
+ canUpload: CanUpload;
26
+ }): Promise<void>;
27
+ export type ReviewClient = import("./reviewClient.mjs").ReviewClient;
28
+ export type ReviewShare = import("./types/share.js").ReviewShare;
29
+ /**
30
+ * `body`: the registration as sent, so a retry is exact; `revokeFailures`: failed turn-offs of the old link.
31
+ */
32
+ export type Switch = {
33
+ id?: string;
34
+ from: string;
35
+ fromSpace?: string;
36
+ spaceId: string;
37
+ space: string;
38
+ projectId: string | null;
39
+ opened?: boolean;
40
+ paused?: boolean;
41
+ audience?: import("./types/share.js").AccessMode | undefined;
42
+ revokeFailures?: number;
43
+ body: import("./types/reviewWire.js").RegisterProjectRequest;
44
+ };
45
+ /**
46
+ * Puts the newest export on `projectId`; `onUploaded` runs once the server has it. Answers the sentence when it did
47
+ * not go up.
48
+ */
49
+ export type Upload = (projectId: string, onUploaded: () => Promise<void>) => Promise<string | null>;
50
+ /**
51
+ * Why the newest export cannot go up now, or null when it can.
52
+ */
53
+ export type CanUpload = () => Promise<"export_first" | "version_running" | null>;
@@ -0,0 +1,16 @@
1
+ /** The checksum the export route takes: the SHA-256 digest as padded base64, never hex.
2
+ * @param {string} file @returns {Promise<string>} */
3
+ export function fileSha256Base64(file: string): Promise<string>;
4
+ /** @typedef {"sent" | "expired" | "refused" | "offline"} PutOutcome */
5
+ /** Streams the file to the signed URL through main's fetch (the app's proxy), the server's headers as given plus
6
+ * the length, and no bearer. Nothing sent or answered for `stallMs` ends it as offline, never "Uploading" forever.
7
+ * @param {{ put_url: string, headers: Record<string, string> }} upload @param {string} file @param {number} size
8
+ * @param {(sent: number) => void} onSent @param {{ stallMs?: number, fetchImpl?: typeof fetch }} [options] */
9
+ export function putFile(upload: {
10
+ put_url: string;
11
+ headers: Record<string, string>;
12
+ }, file: string, size: number, onSent: (sent: number) => void, { stallMs, fetchImpl }?: {
13
+ stallMs?: number;
14
+ fetchImpl?: typeof fetch;
15
+ }): Promise<PutOutcome>;
16
+ export type PutOutcome = "sent" | "expired" | "refused" | "offline";
@@ -0,0 +1,2 @@
1
+ /** @param {unknown} error */
2
+ export function versionSentence(error: unknown): string;
@@ -0,0 +1,56 @@
1
+ /** `access(refused)`, or UNREACHABLE when the saved sign-in's renewal could not reach HeyGen; other errors rethrow.
2
+ * @template T @param {(refused?: string) => Promise<T>} access @param {string} [refused] */
3
+ export function readOrUnreachable<T>(access: (refused?: string) => Promise<T>, refused?: string): Promise<"unreachable" | T>;
4
+ /** The sign-in each send reads again: the bearer, the backend it is for and the account it is; null signed out, and
5
+ * a reader may throw AccountUnreachable. A host's reader may take a bearer a server refused, which these requests
6
+ * never pass.
7
+ * @typedef {(refused?: string) => Promise<{ token: string, heygen: string, heygenApi: string, owner?: string | null }
8
+ * | null>} SignedInAccess */
9
+ /** `host` picks the sign-in host or HeyGen's /v3 API; `trace` marks `answers` as answers, not failures.
10
+ * `headers` go on every send, beneath a call's own.
11
+ * @typedef {{ access: SignedInAccess, fetchImpl?: typeof fetch, timeoutMs: number, tag: string, offline: () => Error,
12
+ * headers?: Record<string, string>,
13
+ * signedOut: () => Error, host?: "heygen" | "heygenApi",
14
+ * trace?: (init: RequestInit, answers: number[]) => RequestInit }} SignedInDeps */
15
+ /** Requests to a HeyGen host as the signed-in person. A 401 reads the sign-in again and retries only with a
16
+ * renewed token; a second 401 comes back as the answer. Signed out, or the same token refused, throws `signedOut()`;
17
+ * an unreachable server throws `offline()`. @param {SignedInDeps} deps */
18
+ export function signedInRequest({ access, fetchImpl, timeoutMs, tag, offline, signedOut, host, headers: always, trace, }: SignedInDeps): (method: string, route: string, body?: unknown, headers?: Record<string, string>, answers?: number[] | null, label?: string) => Promise<{
19
+ reply: Response;
20
+ heygen: string;
21
+ owner: string | null;
22
+ }>;
23
+ /** The sign-in exists but its renewal could not reach HeyGen: a reader throws it, and a request reads it as offline,
24
+ * never as signed out. */
25
+ export class AccountUnreachable extends Error {
26
+ constructor();
27
+ }
28
+ /** What a sign-in read yields, through readOrUnreachable, when its renewal could not reach HeyGen. */
29
+ export const UNREACHABLE: "unreachable";
30
+ export function orIfUnreachable<A extends any[], R>(run: (...args: A) => Promise<R>, value: R): (/** @type {A} */ ...args: A) => Promise<R>;
31
+ /**
32
+ * The sign-in each send reads again: the bearer, the backend it is for and the account it is; null signed out, and
33
+ * a reader may throw AccountUnreachable. A host's reader may take a bearer a server refused, which these requests
34
+ * never pass.
35
+ */
36
+ export type SignedInAccess = (refused?: string) => Promise<{
37
+ token: string;
38
+ heygen: string;
39
+ heygenApi: string;
40
+ owner?: string | null;
41
+ } | null>;
42
+ /**
43
+ * `host` picks the sign-in host or HeyGen's /v3 API; `trace` marks `answers` as answers, not failures.
44
+ * `headers` go on every send, beneath a call's own.
45
+ */
46
+ export type SignedInDeps = {
47
+ access: SignedInAccess;
48
+ fetchImpl?: typeof fetch;
49
+ timeoutMs: number;
50
+ tag: string;
51
+ offline: () => Error;
52
+ headers?: Record<string, string>;
53
+ signedOut: () => Error;
54
+ host?: "heygen" | "heygenApi";
55
+ trace?: (init: RequestInit, answers: number[]) => RequestInit;
56
+ };
@@ -0,0 +1,20 @@
1
+ /** Create metadata folders only inside an existing project. @param {string} root @param {string} rel */
2
+ export function mkdirInside(root: string, rel: string): Promise<string>;
3
+ /** @param {string} path @param {unknown} data */
4
+ export function writeJsonAtomic(path: string, data: unknown): Promise<void>;
5
+ /** @template T @param {string} path @param {T} fallback @returns {Promise<Record<string, unknown> | Exclude<T, object>>} */
6
+ export function readJsonOr<T>(path: string, fallback: T): Promise<Record<string, unknown> | Exclude<T, object>>;
7
+ /** An absent file is the only source of an empty record. @param {string} path */
8
+ export function readJsonRecord(path: string): Promise<Record<string, unknown> | null>;
9
+ export function errorCode(error: unknown): unknown;
10
+ export function rename(from: string, to: string): Promise<void>;
11
+ export function rm(path: string, options?: import("node:fs").RmOptions): Promise<void>;
12
+ export class SharingStateError extends Error {
13
+ /** @param {string} path @param {unknown} [cause] */
14
+ constructor(path: string, cause?: unknown);
15
+ }
16
+ export function isJsonObject(value: unknown): value is Record<string, unknown>;
17
+ export function isSavedId(value: unknown): value is string;
18
+ export function isNullableSavedId(value: unknown): value is string | null;
19
+ export function isOptionalSavedId(value: unknown): value is string | null | undefined;
20
+ export function isOptionalBoolean(value: unknown): value is boolean | undefined;
@@ -0,0 +1,12 @@
1
+ /** Read for each request, so a share that outlives a token meets the renewed one; pass a bearer a server refused.
2
+ * `owner` is the account the token is (accountPrincipal), for what is counted per account. */
3
+ /** `orOffline`: a saved sign-in whose renewal could not reach HeyGen throws AccountUnreachable instead of null. */
4
+ export type ApiAccess = (
5
+ refused?: string,
6
+ orOffline?: boolean,
7
+ ) => Promise<{
8
+ token: string;
9
+ owner: string | null;
10
+ heygen: string;
11
+ heygenApi: string;
12
+ } | null>;
@@ -0,0 +1,59 @@
1
+ export type ReviewPoint = [number, number];
2
+ /** Where on the video a comment points, as the share page sends it. */
3
+ export type ReviewAnchor =
4
+ | { version: 1; kind: "moment"; time: number; at?: ReviewPoint | null; from?: ReviewPoint | null }
5
+ | { version: 1; kind: "range"; start: number; end: number }
6
+ | { version: 1; kind: "region"; time: number; points: ReviewPoint[]; elements?: string[] }
7
+ | { version: 1; kind: "element"; time: number; selector: string; label?: string | null }
8
+ | { version: 1; kind: "track"; time: number; track: number };
9
+
10
+ /** A review comment as the window sees it: no signed URL, no author id. */
11
+ export interface ReviewComment {
12
+ id: string;
13
+ parentId: string | null;
14
+ body: string;
15
+ anchor: ReviewAnchor | null;
16
+ /** The export the comment was left on. */
17
+ origin: { versionId: string; exportedAt: string; width: number; height: number } | null;
18
+ /** Project-relative path of the saved frame once it is on disk; null until then, when there is none, or once the
19
+ * thread is resolved. */
20
+ framePath: string | null;
21
+ /** The comment carries a saved frame (the share page's capture can fail, leaving an anchored comment without one). */
22
+ hasFrame: boolean;
23
+ author: string;
24
+ authorRole: "owner" | "editor" | "commenter" | "viewer" | "guest" | null;
25
+ status: "open" | "resolved" | null;
26
+ /** `completed`: accepted, and the edit it asked for is done (the `resolve` action). */
27
+ resolution: "accepted" | "dismissed" | "completed" | null;
28
+ /** The thread was forwarded for follow-up. Defaults to false when absent. */
29
+ forwarded: boolean;
30
+ revision: number;
31
+ statusRevision: number | null;
32
+ createdAt: string;
33
+ deleted: boolean;
34
+ canResolve: boolean;
35
+ /** The server's hashed, project-scoped ref for a root comment, sent only to those who can decide; absent from older
36
+ * servers. Usage events alone carry it: it is never shown and never goes into a request to the agent. */
37
+ analyticsRef?: string;
38
+ }
39
+
40
+ /** The thread decisions the review server takes on its status route. */
41
+ export type ReviewAction = "accept" | "dismiss" | "reopen" | "resolve";
42
+
43
+ /** A thread write's answer: the thread's new status revision, or why it was refused. */
44
+ export type ReviewWriteResult =
45
+ | { ok: true; statusRevision: number | null }
46
+ | {
47
+ ok: false;
48
+ reason: "conflict" | "forwarded" | "offline" | "signed-out" | "denied" | "gone" | "failed";
49
+ };
50
+
51
+ /** The project's review comments, kept current while the project is in view. */
52
+ export interface ReviewFeedView {
53
+ /** off: the flag is off, or the project was never shared for review. */
54
+ state: "off" | "loading" | "ready" | "reconnecting" | "signed-out" | "denied" | "gone";
55
+ comments: ReviewComment[];
56
+ latestVersionId: string | null;
57
+ reviewUrl: string | null;
58
+ syncedAt: number | null;
59
+ }