@mdbase-dev/connect 0.1.0-beta.31 → 0.1.0-beta.33
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 +112 -34
- package/dist/advanced.d.ts +9 -0
- package/dist/advanced.js +6 -0
- package/dist/application-session.d.ts +39 -14
- package/dist/application-session.js +201 -70
- package/dist/async.js +11 -5
- package/dist/authority-proof.d.ts +3 -0
- package/dist/authority-proof.js +24 -0
- package/dist/browser/integrity.json +1 -1
- package/dist/browser/mdbase-connect.min.js +3 -3
- package/dist/browser/mdbase-connect.min.js.sha384 +1 -1
- package/dist/browser.d.ts +3 -0
- package/dist/browser.js +5 -0
- package/dist/capabilities.js +2 -1
- package/dist/collection-client.d.ts +36 -33
- package/dist/collection-client.js +549 -104
- package/dist/connect-authorization-helpers.d.ts +3 -0
- package/dist/connect-authorization-helpers.js +13 -0
- package/dist/connect-options.d.ts +12 -4
- package/dist/connect.d.ts +12 -4
- package/dist/connect.js +102 -103
- package/dist/connection-file-transport.d.ts +2 -2
- package/dist/connection-file-transport.js +4 -4
- package/dist/connection-notifications.d.ts +10 -4
- package/dist/connection-notifications.js +41 -23
- package/dist/connection-transport.d.ts +19 -15
- package/dist/connection-transport.js +202 -202
- package/dist/connection-types.d.ts +5 -4
- package/dist/connection.d.ts +47 -46
- package/dist/connection.js +320 -162
- package/dist/crypto-entry.d.ts +7 -0
- package/dist/crypto-entry.js +4 -0
- package/dist/crypto.js +4 -4
- package/dist/external-store.d.ts +10 -0
- package/dist/external-store.js +10 -0
- package/dist/file-transfer-internals.d.ts +23 -0
- package/dist/file-transfer-internals.js +224 -0
- package/dist/files.d.ts +36 -14
- package/dist/files.js +92 -222
- package/dist/hosted-file-request.js +3 -3
- package/dist/index.d.ts +24 -19
- package/dist/index.js +10 -18
- package/dist/internal-types.d.ts +9 -1
- package/dist/local-file-transport.d.ts +2 -2
- package/dist/local-file-transport.js +13 -13
- package/dist/mdbase-connect.d.ts +6 -4
- package/dist/mdbase-connect.js +9 -8
- package/dist/notifications.d.ts +3 -2
- package/dist/operation-helpers.d.ts +3 -3
- package/dist/operation-helpers.js +17 -34
- package/dist/operation-types.d.ts +435 -45
- package/dist/outcomes.d.ts +15 -15
- package/dist/outcomes.js +13 -1
- package/dist/pending-mutation-store.d.ts +13 -0
- package/dist/pending-mutation-store.js +64 -0
- package/dist/request-budget.d.ts +27 -0
- package/dist/request-budget.js +95 -0
- package/dist/runtime-utils.d.ts +2 -0
- package/dist/runtime-utils.js +19 -0
- package/dist/session.d.ts +5 -4
- package/dist/session.js +6 -5
- package/dist/stored-token.d.ts +10 -0
- package/dist/stored-token.js +60 -0
- package/dist/token-response.d.ts +11 -0
- package/dist/token-response.js +77 -0
- package/dist/watch-policy.d.ts +8 -0
- package/dist/watch-policy.js +12 -0
- package/package.json +12 -2
package/dist/crypto.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { OPERATION_TRANSPORT_PROTOCOL_VERSION, GRANT_ENCRYPTION_PROTOCOL_VERSION, AUTHORITY_PROOF_DOMAIN, AUTHORITY_PROOF_HEADERS, AUTHORITY_PROOF_VERSION, RELAY_ENCRYPTION_SUITE, isConnectProblem } from "@mdbase-dev/connect-protocol";
|
|
2
2
|
const DATABASE_NAME = "mdbase-connect-keys";
|
|
3
3
|
const STORE_NAME = "grant-keys";
|
|
4
4
|
const MAX_U64 = 18446744073709551615n;
|
|
@@ -272,7 +272,7 @@ function context(binding) {
|
|
|
272
272
|
const value = binding.encryption;
|
|
273
273
|
return [
|
|
274
274
|
"mdbase-connect",
|
|
275
|
-
|
|
275
|
+
OPERATION_TRANSPORT_PROTOCOL_VERSION,
|
|
276
276
|
value.suite,
|
|
277
277
|
binding.grantId,
|
|
278
278
|
binding.applicationId,
|
|
@@ -288,7 +288,7 @@ function aad(metadata, direction) {
|
|
|
288
288
|
function envelopeMetadata(metadata) {
|
|
289
289
|
const encryption = metadata.binding.encryption;
|
|
290
290
|
return {
|
|
291
|
-
protocol_version:
|
|
291
|
+
protocol_version: OPERATION_TRANSPORT_PROTOCOL_VERSION,
|
|
292
292
|
suite: RELAY_ENCRYPTION_SUITE,
|
|
293
293
|
request_id: metadata.requestId,
|
|
294
294
|
grant_id: metadata.binding.grantId,
|
|
@@ -317,7 +317,7 @@ function sameEnvelopeMetadata(request, response) {
|
|
|
317
317
|
&& request.counter === response.counter;
|
|
318
318
|
}
|
|
319
319
|
export function validateGrantEncryption(encryption) {
|
|
320
|
-
if (encryption.protocol_version !==
|
|
320
|
+
if (encryption.protocol_version !== GRANT_ENCRYPTION_PROTOCOL_VERSION
|
|
321
321
|
|| encryption.suite !== RELAY_ENCRYPTION_SUITE
|
|
322
322
|
|| !Number.isSafeInteger(encryption.scope_epoch)
|
|
323
323
|
|| encryption.scope_epoch <= 0
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Minimal framework-neutral shape consumed by React's useSyncExternalStore. */
|
|
2
|
+
export interface MdbaseExternalStore<Snapshot> {
|
|
3
|
+
subscribe(listener: () => void): () => void;
|
|
4
|
+
getSnapshot(): Snapshot;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Adapt an existing SDK session without introducing another cache or state
|
|
8
|
+
* owner. The stable object can be passed directly to useSyncExternalStore.
|
|
9
|
+
*/
|
|
10
|
+
export declare function externalStore<Snapshot>(source: MdbaseExternalStore<Snapshot>): MdbaseExternalStore<Snapshot>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adapt an existing SDK session without introducing another cache or state
|
|
3
|
+
* owner. The stable object can be passed directly to useSyncExternalStore.
|
|
4
|
+
*/
|
|
5
|
+
export function externalStore(source) {
|
|
6
|
+
return {
|
|
7
|
+
subscribe: (listener) => source.subscribe(listener),
|
|
8
|
+
getSnapshot: () => source.getSnapshot()
|
|
9
|
+
};
|
|
10
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { FileTransferSession, FileTransferStatus, PreparedFilePart } from "@mdbase-dev/connect-protocol";
|
|
2
|
+
import { MdbaseConnectError } from "./errors.js";
|
|
3
|
+
export declare const MAX_OBJECT_ATTEMPTS = 3;
|
|
4
|
+
export declare const MAX_BUFFERED_DOWNLOAD_BYTES: number;
|
|
5
|
+
export declare const UUID: RegExp;
|
|
6
|
+
export declare const SHA256_DIGEST: RegExp;
|
|
7
|
+
export type FileSource = Blob | ArrayBuffer | ArrayBufferView;
|
|
8
|
+
export type UploadPartBody = Blob | Uint8Array<ArrayBuffer>;
|
|
9
|
+
export declare function hashBlob(blob: Blob, signal: AbortSignal | undefined, progress: (transferredBytes: number) => void): Promise<string>;
|
|
10
|
+
export declare function sourceBlob(source: FileSource, mediaType?: string): Blob;
|
|
11
|
+
export declare function uploadBodyLength(body: UploadPartBody): number;
|
|
12
|
+
export declare function uploadBodyBytes(body: UploadPartBody): Promise<Uint8Array<ArrayBuffer>>;
|
|
13
|
+
export declare function validConcurrency(value?: number): number;
|
|
14
|
+
export declare function validPageSize(value: number): number;
|
|
15
|
+
export declare function requireTransferSession(session: FileTransferSession, transferId: string, direction: "upload" | "download"): void;
|
|
16
|
+
export declare function requireTransferStatus(status: FileTransferStatus, session: FileTransferSession): void;
|
|
17
|
+
export declare function chunkLength(totalSize: number, partSize: number, index: number): number;
|
|
18
|
+
export declare function retryChunk<Result>(work: () => Promise<Result>, signal?: AbortSignal): Promise<Result>;
|
|
19
|
+
export declare function requirePreparedPart(part: PreparedFilePart, transferId: string, partIndex: number, offset: number, contentLength: number, method: "PUT"): void;
|
|
20
|
+
export declare function browserObjectHeaders(headers: Record<string, string>): Headers;
|
|
21
|
+
export declare function mapConcurrent<Result>(count: number, concurrency: number, work: (index: number) => Promise<Result>): Promise<Result[]>;
|
|
22
|
+
export declare function throwIfAborted(signal?: AbortSignal): void;
|
|
23
|
+
export declare function normalizeFileError(error: unknown): MdbaseConnectError;
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import { MdbaseConnectError, connectError } from "./errors.js";
|
|
2
|
+
import { IncrementalSha256 } from "./file-sha256.js";
|
|
3
|
+
const HASH_CHUNK_BYTES = 1024 * 1024;
|
|
4
|
+
const DEFAULT_CONCURRENCY = 4;
|
|
5
|
+
const MAX_CONCURRENCY = 8;
|
|
6
|
+
export const MAX_OBJECT_ATTEMPTS = 3;
|
|
7
|
+
export const MAX_BUFFERED_DOWNLOAD_BYTES = 64 * 1024 * 1024;
|
|
8
|
+
export const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu;
|
|
9
|
+
export const SHA256_DIGEST = /^sha256:[0-9a-f]{64}$/u;
|
|
10
|
+
export async function hashBlob(blob, signal, progress) {
|
|
11
|
+
const hash = new IncrementalSha256();
|
|
12
|
+
for (let offset = 0; offset < blob.size; offset += HASH_CHUNK_BYTES) {
|
|
13
|
+
throwIfAborted(signal);
|
|
14
|
+
const chunk = new Uint8Array(await blob.slice(offset, offset + HASH_CHUNK_BYTES).arrayBuffer());
|
|
15
|
+
hash.update(chunk);
|
|
16
|
+
progress(Math.min(blob.size, offset + chunk.byteLength));
|
|
17
|
+
}
|
|
18
|
+
if (blob.size === 0)
|
|
19
|
+
progress(0);
|
|
20
|
+
return hash.digestHex();
|
|
21
|
+
}
|
|
22
|
+
export function sourceBlob(source, mediaType) {
|
|
23
|
+
if (source instanceof Blob) {
|
|
24
|
+
return mediaType && mediaType !== source.type
|
|
25
|
+
? source.slice(0, source.size, mediaType)
|
|
26
|
+
: source;
|
|
27
|
+
}
|
|
28
|
+
if (source instanceof ArrayBuffer)
|
|
29
|
+
return new Blob([source], { type: mediaType });
|
|
30
|
+
const copy = new Uint8Array(source.byteLength);
|
|
31
|
+
copy.set(new Uint8Array(source.buffer, source.byteOffset, source.byteLength));
|
|
32
|
+
return new Blob([copy.buffer], { type: mediaType });
|
|
33
|
+
}
|
|
34
|
+
export function uploadBodyLength(body) {
|
|
35
|
+
return body instanceof Blob ? body.size : body.byteLength;
|
|
36
|
+
}
|
|
37
|
+
export async function uploadBodyBytes(body) {
|
|
38
|
+
return body instanceof Blob
|
|
39
|
+
? new Uint8Array(await body.arrayBuffer())
|
|
40
|
+
: body;
|
|
41
|
+
}
|
|
42
|
+
export function validConcurrency(value = DEFAULT_CONCURRENCY) {
|
|
43
|
+
if (!Number.isInteger(value) || value < 1 || value > MAX_CONCURRENCY) {
|
|
44
|
+
throw connectError("invalid_request", `File concurrency must be between 1 and ${MAX_CONCURRENCY}.`);
|
|
45
|
+
}
|
|
46
|
+
return value;
|
|
47
|
+
}
|
|
48
|
+
export function validPageSize(value) {
|
|
49
|
+
if (!Number.isInteger(value) || value < 1 || value > 1_000) {
|
|
50
|
+
throw connectError("invalid_request", "File page size must be between 1 and 1000.");
|
|
51
|
+
}
|
|
52
|
+
return value;
|
|
53
|
+
}
|
|
54
|
+
export function requireTransferSession(session, transferId, direction) {
|
|
55
|
+
if (session.protocol_version !== 1
|
|
56
|
+
|| session.type !== "file_transfer"
|
|
57
|
+
|| session.transfer_id !== transferId
|
|
58
|
+
|| session.direction !== direction
|
|
59
|
+
|| !Number.isSafeInteger(session.total_size)
|
|
60
|
+
|| session.total_size < 0
|
|
61
|
+
|| !Array.isArray(session.received)
|
|
62
|
+
|| !validTransferStrategy(session)) {
|
|
63
|
+
throw connectError("invalid_operation_response", "The authority returned an invalid file transfer session.");
|
|
64
|
+
}
|
|
65
|
+
const partSize = session.strategy.kind === "framed_chunks"
|
|
66
|
+
? session.strategy.chunk_size
|
|
67
|
+
: session.strategy.kind === "object_put"
|
|
68
|
+
? Math.max(1, session.total_size)
|
|
69
|
+
: session.strategy.part_size;
|
|
70
|
+
const partCount = session.strategy.kind === "object_put"
|
|
71
|
+
? 1
|
|
72
|
+
: Math.ceil(session.total_size / partSize);
|
|
73
|
+
if (new Set(session.received).size !== session.received.length
|
|
74
|
+
|| session.received.some((index) => !Number.isSafeInteger(index) || index < 0 || index >= partCount)) {
|
|
75
|
+
throw connectError("invalid_operation_response", "The authority returned invalid transfer progress.");
|
|
76
|
+
}
|
|
77
|
+
const uploadedParts = session.uploaded_parts ?? [];
|
|
78
|
+
if (!Array.isArray(uploadedParts)
|
|
79
|
+
|| uploadedParts.some((part, index) => !Number.isSafeInteger(part?.part_number)
|
|
80
|
+
|| part.part_number < 1
|
|
81
|
+
|| part.part_number > partCount
|
|
82
|
+
|| typeof part.etag !== "string"
|
|
83
|
+
|| part.etag.length === 0
|
|
84
|
+
|| part.etag.length > 255
|
|
85
|
+
|| (index > 0 && uploadedParts[index - 1].part_number >= part.part_number))
|
|
86
|
+
|| (session.strategy.kind === "object_multipart"
|
|
87
|
+
? uploadedParts.length !== session.received.length
|
|
88
|
+
|| uploadedParts.some((part, index) => part.part_number - 1 !== session.received[index])
|
|
89
|
+
: uploadedParts.length !== 0)) {
|
|
90
|
+
throw connectError("invalid_operation_response", "The authority returned invalid uploaded part receipts.");
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
export function requireTransferStatus(status, session) {
|
|
94
|
+
const partSize = session.strategy.kind === "framed_chunks"
|
|
95
|
+
? session.strategy.chunk_size
|
|
96
|
+
: session.strategy.kind === "object_put"
|
|
97
|
+
? Math.max(1, session.total_size)
|
|
98
|
+
: session.strategy.part_size;
|
|
99
|
+
const partCount = session.strategy.kind === "object_put"
|
|
100
|
+
? 1
|
|
101
|
+
: Math.ceil(session.total_size / partSize);
|
|
102
|
+
if (status?.protocol_version !== 1
|
|
103
|
+
|| status.type !== "file_transfer_status"
|
|
104
|
+
|| status.transfer_id !== session.transfer_id
|
|
105
|
+
|| !["open", "committed", "aborted", "expired"].includes(status.state)
|
|
106
|
+
|| !Array.isArray(status.received)
|
|
107
|
+
|| new Set(status.received).size !== status.received.length
|
|
108
|
+
|| status.received.some((index) => !Number.isSafeInteger(index) || index < 0 || index >= partCount)
|
|
109
|
+
|| !Number.isSafeInteger(status.received_bytes)
|
|
110
|
+
|| status.received_bytes < 0
|
|
111
|
+
|| status.received_bytes > session.total_size
|
|
112
|
+
|| status.received.reduce((total, index) => total + chunkLength(session.total_size, partSize, index), 0) !== status.received_bytes) {
|
|
113
|
+
throw connectError("invalid_operation_response", "The authority returned invalid transfer status.");
|
|
114
|
+
}
|
|
115
|
+
const uploadedParts = status.uploaded_parts;
|
|
116
|
+
if (!Array.isArray(uploadedParts)
|
|
117
|
+
|| uploadedParts.some((part, index) => !Number.isSafeInteger(part?.part_number)
|
|
118
|
+
|| part.part_number < 1
|
|
119
|
+
|| part.part_number > partCount
|
|
120
|
+
|| typeof part.etag !== "string"
|
|
121
|
+
|| part.etag.length === 0
|
|
122
|
+
|| part.etag.length > 255
|
|
123
|
+
|| (index > 0 && uploadedParts[index - 1].part_number >= part.part_number))
|
|
124
|
+
|| (session.strategy.kind === "object_multipart"
|
|
125
|
+
? uploadedParts.length !== status.received.length
|
|
126
|
+
|| uploadedParts.some((part, index) => part.part_number - 1 !== status.received[index])
|
|
127
|
+
: uploadedParts.length !== 0)) {
|
|
128
|
+
throw connectError("invalid_operation_response", "The authority returned invalid uploaded part status.");
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
function validTransferStrategy(session) {
|
|
132
|
+
if (session.strategy.kind === "framed_chunks") {
|
|
133
|
+
return session.protection === "grant_aead_v1"
|
|
134
|
+
&& Number.isSafeInteger(session.strategy.chunk_size)
|
|
135
|
+
&& session.strategy.chunk_size > 0;
|
|
136
|
+
}
|
|
137
|
+
if (session.protection !== "transport_tls")
|
|
138
|
+
return false;
|
|
139
|
+
if (session.strategy.kind === "object_put")
|
|
140
|
+
return true;
|
|
141
|
+
return Number.isSafeInteger(session.strategy.part_size) && session.strategy.part_size > 0;
|
|
142
|
+
}
|
|
143
|
+
export function chunkLength(totalSize, partSize, index) {
|
|
144
|
+
return Math.min(partSize, Math.max(0, totalSize - index * partSize));
|
|
145
|
+
}
|
|
146
|
+
export async function retryChunk(work, signal) {
|
|
147
|
+
for (let attempt = 1; attempt <= MAX_OBJECT_ATTEMPTS; attempt += 1) {
|
|
148
|
+
throwIfAborted(signal);
|
|
149
|
+
try {
|
|
150
|
+
return await work();
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
if (signal?.aborted || attempt === MAX_OBJECT_ATTEMPTS)
|
|
154
|
+
throw error;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
throw new Error("Unreachable file chunk retry state.");
|
|
158
|
+
}
|
|
159
|
+
export function requirePreparedPart(part, transferId, partIndex, offset, contentLength, method) {
|
|
160
|
+
let url;
|
|
161
|
+
try {
|
|
162
|
+
url = new URL(part.url);
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
throw connectError("invalid_operation_response", "The authority returned an invalid object URL.");
|
|
166
|
+
}
|
|
167
|
+
if (part.protocol_version !== 1
|
|
168
|
+
|| part.type !== "file_part"
|
|
169
|
+
|| part.transfer_id !== transferId
|
|
170
|
+
|| part.part_index !== partIndex
|
|
171
|
+
|| part.offset !== offset
|
|
172
|
+
|| part.content_length !== contentLength
|
|
173
|
+
|| part.method.toUpperCase() !== method
|
|
174
|
+
|| url.protocol !== "https:"
|
|
175
|
+
&& !(url.protocol === "http:" && ["localhost", "127.0.0.1", "[::1]", "::1"].includes(url.hostname))
|
|
176
|
+
|| url.username
|
|
177
|
+
|| url.password) {
|
|
178
|
+
throw connectError("invalid_operation_response", "The authority returned an invalid prepared file part.");
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
export function browserObjectHeaders(headers) {
|
|
182
|
+
const result = new Headers();
|
|
183
|
+
for (const [name, value] of Object.entries(headers)) {
|
|
184
|
+
if (["host", "content-length"].includes(name.toLowerCase()))
|
|
185
|
+
continue;
|
|
186
|
+
result.set(name, value);
|
|
187
|
+
}
|
|
188
|
+
return result;
|
|
189
|
+
}
|
|
190
|
+
export async function mapConcurrent(count, concurrency, work) {
|
|
191
|
+
const results = new Array(count);
|
|
192
|
+
let next = 0;
|
|
193
|
+
await Promise.all(Array.from({ length: Math.min(count, concurrency) }, async () => {
|
|
194
|
+
while (next < count) {
|
|
195
|
+
const index = next;
|
|
196
|
+
next += 1;
|
|
197
|
+
results[index] = await work(index);
|
|
198
|
+
}
|
|
199
|
+
}));
|
|
200
|
+
return results;
|
|
201
|
+
}
|
|
202
|
+
export function throwIfAborted(signal) {
|
|
203
|
+
if (signal?.aborted) {
|
|
204
|
+
throw connectError("operation_cancelled", "The file transfer was cancelled.", {
|
|
205
|
+
operationOutcome: "not_sent",
|
|
206
|
+
cause: signal.reason
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
export function normalizeFileError(error) {
|
|
211
|
+
if (error instanceof MdbaseConnectError)
|
|
212
|
+
return error;
|
|
213
|
+
if (typeof DOMException !== "undefined"
|
|
214
|
+
&& error instanceof DOMException
|
|
215
|
+
&& error.name === "AbortError") {
|
|
216
|
+
return connectError("operation_cancelled", "The file transfer was cancelled.", {
|
|
217
|
+
operationOutcome: "not_sent",
|
|
218
|
+
cause: error
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
return connectError("temporarily_unavailable", "The file transfer could not be completed.", {
|
|
222
|
+
cause: error
|
|
223
|
+
});
|
|
224
|
+
}
|
package/dist/files.d.ts
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
1
|
+
import type { FileCapability, FileTransferSession } from "@mdbase-dev/connect-protocol";
|
|
2
|
+
import { type FileSource } from "./file-transfer-internals.js";
|
|
3
|
+
import type { ConnectRequestOptions } from "./operation-types.js";
|
|
4
|
+
import { type ResolvedConnectTimeouts } from "./request-budget.js";
|
|
5
|
+
export type MdbaseFileSource = FileSource;
|
|
6
|
+
export interface CollectionFileDescriptor {
|
|
7
|
+
fileId: string;
|
|
8
|
+
path: string;
|
|
9
|
+
revision: string;
|
|
10
|
+
contentDigest: `sha256:${string}`;
|
|
11
|
+
size: number;
|
|
12
|
+
mediaType?: string;
|
|
13
|
+
mediaClass: import("@mdbase-dev/connect-protocol").FileMediaClass;
|
|
14
|
+
modifiedAt: string;
|
|
15
|
+
}
|
|
16
|
+
export interface MdbaseFileDeleteReceipt {
|
|
17
|
+
protocolVersion: 1;
|
|
18
|
+
type: "file_deleted";
|
|
19
|
+
mutationId: string;
|
|
20
|
+
fileId: string;
|
|
21
|
+
previousPath: string;
|
|
22
|
+
revision: string;
|
|
23
|
+
}
|
|
3
24
|
export interface MdbaseFileStreamSource {
|
|
4
25
|
/** Exact plaintext byte length. */
|
|
5
26
|
size: number;
|
|
@@ -17,37 +38,32 @@ export interface MdbaseFileProgress {
|
|
|
17
38
|
transferredBytes: number;
|
|
18
39
|
totalBytes: number;
|
|
19
40
|
}
|
|
20
|
-
export interface MdbaseFileListOptions {
|
|
41
|
+
export interface MdbaseFileListOptions extends ConnectRequestOptions {
|
|
21
42
|
folder?: string;
|
|
22
43
|
pageSize?: number;
|
|
23
|
-
signal?: AbortSignal;
|
|
24
44
|
}
|
|
25
|
-
export interface MdbaseFileUploadOptions {
|
|
45
|
+
export interface MdbaseFileUploadOptions extends ConnectRequestOptions {
|
|
26
46
|
mediaType?: string;
|
|
27
47
|
ifRevision?: string;
|
|
28
48
|
concurrency?: number;
|
|
29
49
|
/** Stable retry key. Reuse it after an ambiguous failure to resume or replay commit. */
|
|
30
50
|
transferId?: string;
|
|
31
|
-
signal?: AbortSignal;
|
|
32
51
|
onProgress?: (progress: MdbaseFileProgress) => void;
|
|
33
52
|
}
|
|
34
53
|
export type MdbaseFileStreamUploadOptions = Omit<MdbaseFileUploadOptions, "concurrency">;
|
|
35
|
-
export interface MdbaseFileDownloadOptions {
|
|
54
|
+
export interface MdbaseFileDownloadOptions extends ConnectRequestOptions {
|
|
36
55
|
concurrency?: number;
|
|
37
|
-
signal?: AbortSignal;
|
|
38
56
|
onProgress?: (progress: MdbaseFileProgress) => void;
|
|
39
57
|
}
|
|
40
|
-
export interface MdbaseFileMoveOptions {
|
|
58
|
+
export interface MdbaseFileMoveOptions extends ConnectRequestOptions {
|
|
41
59
|
ifRevision?: string;
|
|
42
60
|
/** Reuse after an ambiguous network failure to receive the original receipt. */
|
|
43
61
|
mutationId?: string;
|
|
44
|
-
signal?: AbortSignal;
|
|
45
62
|
}
|
|
46
|
-
export interface MdbaseFileDeleteOptions {
|
|
63
|
+
export interface MdbaseFileDeleteOptions extends ConnectRequestOptions {
|
|
47
64
|
ifRevision?: string;
|
|
48
65
|
/** Reuse after an ambiguous network failure to receive the original receipt. */
|
|
49
66
|
mutationId?: string;
|
|
50
|
-
signal?: AbortSignal;
|
|
51
67
|
}
|
|
52
68
|
/** Internal transport seam used by direct and relayed encrypted chunk delivery. */
|
|
53
69
|
export interface MdbaseFramedFileTransport {
|
|
@@ -65,9 +81,11 @@ export declare class MdbaseFileClient {
|
|
|
65
81
|
private readonly request;
|
|
66
82
|
private readonly framed?;
|
|
67
83
|
private readonly hosted?;
|
|
68
|
-
|
|
84
|
+
private readonly timeouts;
|
|
85
|
+
constructor(capability: () => FileCapability | null, request: ControlRequest, framed?: MdbaseFramedFileTransport | undefined, hosted?: MdbaseHostedFileTransport | undefined, timeouts?: ResolvedConnectTimeouts);
|
|
69
86
|
list(options?: MdbaseFileListOptions): AsyncGenerator<CollectionFileDescriptor>;
|
|
70
87
|
upload(path: string, source: MdbaseFileSource, options?: MdbaseFileUploadOptions): Promise<CollectionFileDescriptor>;
|
|
88
|
+
private uploadWithinBudget;
|
|
71
89
|
/**
|
|
72
90
|
* Upload a one-shot byte stream without accumulating multiple file parts.
|
|
73
91
|
* SDK memory is bounded by one assembled negotiated part plus at most one
|
|
@@ -75,13 +93,17 @@ export declare class MdbaseFileClient {
|
|
|
75
93
|
* Reuse `transferId` with a newly opened source after an ambiguous failure.
|
|
76
94
|
*/
|
|
77
95
|
uploadStream(path: string, source: MdbaseFileStreamSource, options?: MdbaseFileStreamUploadOptions): Promise<CollectionFileDescriptor>;
|
|
96
|
+
private uploadStreamWithinBudget;
|
|
78
97
|
private uploadKnownSource;
|
|
79
98
|
download(file: CollectionFileDescriptor, options?: MdbaseFileDownloadOptions): Promise<Blob>;
|
|
80
99
|
/** Download and verify a file with network backpressure and bounded buffering. */
|
|
81
100
|
downloadStream(file: CollectionFileDescriptor, options?: MdbaseFileDownloadOptions): Promise<ReadableStream<Uint8Array>>;
|
|
101
|
+
private openDownloadStream;
|
|
82
102
|
downloadBytes(file: CollectionFileDescriptor, options?: MdbaseFileDownloadOptions): Promise<Uint8Array>;
|
|
83
103
|
move(file: CollectionFileDescriptor, path: string, options?: MdbaseFileMoveOptions): Promise<CollectionFileDescriptor>;
|
|
84
|
-
|
|
104
|
+
private moveWithinBudget;
|
|
105
|
+
delete(file: CollectionFileDescriptor, options?: MdbaseFileDeleteOptions): Promise<MdbaseFileDeleteReceipt>;
|
|
106
|
+
private deleteWithinBudget;
|
|
85
107
|
private uploadPart;
|
|
86
108
|
private tryReplayUploadCommit;
|
|
87
109
|
private commitUpload;
|