@koolbase/core 12.15.0 → 12.17.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/dist/cjs/index.d.ts +3 -0
- package/dist/cjs/index.js +3 -1
- package/dist/cjs/storage-errors.d.ts +57 -0
- package/dist/cjs/storage-errors.js +109 -1
- package/dist/cjs/storage-files.d.ts +53 -0
- package/dist/cjs/storage-files.js +61 -0
- package/dist/cjs/storage-transfer.d.ts +88 -0
- package/dist/cjs/storage-transfer.js +214 -0
- package/dist/cjs/storage.d.ts +72 -0
- package/dist/cjs/storage.js +129 -8
- package/dist/cjs/types.d.ts +12 -1
- package/dist/esm/index.d.ts +3 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/storage-errors.d.ts +57 -0
- package/dist/esm/storage-errors.js +101 -0
- package/dist/esm/storage-files.d.ts +53 -0
- package/dist/esm/storage-files.js +56 -0
- package/dist/esm/storage-transfer.d.ts +88 -0
- package/dist/esm/storage-transfer.js +208 -0
- package/dist/esm/storage.d.ts +72 -0
- package/dist/esm/storage.js +129 -8
- package/dist/esm/types.d.ts +12 -1
- package/package.json +1 -1
package/dist/cjs/storage.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { KoolbaseConfig, UploadOptions, UploadResult, KoolbaseObject, KoolbaseObjectVersion, KoolbaseImageTransform } from './types.js';
|
|
2
|
+
import { KoolbaseUploadTask } from './storage-transfer.js';
|
|
3
|
+
import { KoolbaseFileInfo, KoolbaseFilePage, ListFilesOptions } from './storage-files.js';
|
|
2
4
|
/**
|
|
3
5
|
* Koolbase storage client — uploads, downloads, and deletes via presigned
|
|
4
6
|
* Cloudflare R2 URLs.
|
|
@@ -53,6 +55,76 @@ export declare class KoolbaseStorage {
|
|
|
53
55
|
* overwriting silently, pass `overwrite: true` explicitly.
|
|
54
56
|
*/
|
|
55
57
|
upload(options: UploadOptions): Promise<UploadResult>;
|
|
58
|
+
/**
|
|
59
|
+
* Starts an upload you can follow and cancel: the same upload as
|
|
60
|
+
* {@link upload}, returned at once as a {@link KoolbaseUploadTask}.
|
|
61
|
+
*
|
|
62
|
+
* The task goes pending -> uploading (bytes sent of total) -> verifying
|
|
63
|
+
* -> completed, or failed / cancelled. `cancel()` stops it until the
|
|
64
|
+
* server begins checking the bytes (verifying); nothing is stored. Only
|
|
65
|
+
* completed means the file can be referenced from a record.
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* const task = Koolbase.storage.startUpload({ bucket: 'posters', path: 'gig.png', file });
|
|
69
|
+
* const stop = task.onProgress((p) => setPercent(Math.round(p.fraction * 100)));
|
|
70
|
+
* cancelButton.onclick = () => task.cancel();
|
|
71
|
+
* const { object } = await task.result;
|
|
72
|
+
*/
|
|
73
|
+
startUpload(options: UploadOptions): KoolbaseUploadTask;
|
|
74
|
+
private runUpload;
|
|
75
|
+
/**
|
|
76
|
+
* One page of a bucket's files. The bucket's view rule is applied to every
|
|
77
|
+
* file: the page holds only files the signed-in person may view. Pass the
|
|
78
|
+
* page's `nextCursor` back as `cursor` for the next one; a page can hold
|
|
79
|
+
* fewer files than `limit` and still have a next.
|
|
80
|
+
*
|
|
81
|
+
* @example
|
|
82
|
+
* let cursor: string | undefined;
|
|
83
|
+
* do {
|
|
84
|
+
* const page = await Koolbase.storage.listFiles({ bucket: 'posters', prefix: 'events/', cursor });
|
|
85
|
+
* show(page.files);
|
|
86
|
+
* cursor = page.nextCursor ?? undefined;
|
|
87
|
+
* } while (cursor);
|
|
88
|
+
*/
|
|
89
|
+
listFiles(options: ListFilesOptions): Promise<KoolbaseFilePage>;
|
|
90
|
+
/**
|
|
91
|
+
* One file's details -- size, type, metadata, who uploaded it, and its
|
|
92
|
+
* permanent link while its bucket is public. Throws
|
|
93
|
+
* `KoolbaseStorageNotFoundError` for a file that is not there or that
|
|
94
|
+
* the bucket's view rule hides from the caller.
|
|
95
|
+
*/
|
|
96
|
+
getFileInfo(bucket: string, path: string): Promise<KoolbaseFileInfo>;
|
|
97
|
+
/**
|
|
98
|
+
* Move a file to `to`, in its bucket or into `toBucket` (S1b). Nothing is
|
|
99
|
+
* uploaded again. The bucket's rules decide: delete on the file, and
|
|
100
|
+
* upload at the destination, whose limits apply as to an upload. The
|
|
101
|
+
* destination must be free.
|
|
102
|
+
*
|
|
103
|
+
* Within one bucket the file keeps its kept versions. Refused with
|
|
104
|
+
* `KoolbaseStorageFileInUseError` while records point at the file: change
|
|
105
|
+
* them first, or {@link copy} it.
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* await Koolbase.storage.move({ bucket: 'inbox', from: 'scan.png', toBucket: 'vault', to: '2026/scan.png' });
|
|
109
|
+
*/
|
|
110
|
+
move(options: {
|
|
111
|
+
bucket: string;
|
|
112
|
+
from: string;
|
|
113
|
+
to: string;
|
|
114
|
+
toBucket?: string;
|
|
115
|
+
}): Promise<KoolbaseObject>;
|
|
116
|
+
/**
|
|
117
|
+
* Copy a file to `to`, in its bucket or into `toBucket` (S1b): a new file,
|
|
118
|
+
* the caller's, with the same bytes. The bucket's rules decide: view on
|
|
119
|
+
* the file, and upload at the destination.
|
|
120
|
+
*/
|
|
121
|
+
copy(options: {
|
|
122
|
+
bucket: string;
|
|
123
|
+
from: string;
|
|
124
|
+
to: string;
|
|
125
|
+
toBucket?: string;
|
|
126
|
+
}): Promise<KoolbaseObject>;
|
|
127
|
+
private transfer;
|
|
56
128
|
/**
|
|
57
129
|
* Apply a partial metadata update to an existing object. Returns the
|
|
58
130
|
* post-update {@link KoolbaseObject} with the merged metadata.
|
package/dist/cjs/storage.js
CHANGED
|
@@ -5,6 +5,8 @@ const storage_errors_js_1 = require("./storage-errors.js");
|
|
|
5
5
|
const network_js_1 = require("./network.js");
|
|
6
6
|
const errors_js_1 = require("./errors.js");
|
|
7
7
|
const storage_errors_js_2 = require("./storage-errors.js");
|
|
8
|
+
const storage_transfer_js_1 = require("./storage-transfer.js");
|
|
9
|
+
const storage_files_js_1 = require("./storage-files.js");
|
|
8
10
|
// --- Cloudflare image-transform URL helpers -------------------------------
|
|
9
11
|
// Module-private — callers use KoolbaseStorage.publicUrl / publicUrlForObject.
|
|
10
12
|
function clampInt(v, min, max) {
|
|
@@ -91,6 +93,46 @@ class KoolbaseStorage {
|
|
|
91
93
|
* overwriting silently, pass `overwrite: true` explicitly.
|
|
92
94
|
*/
|
|
93
95
|
async upload(options) {
|
|
96
|
+
const task = this.startUpload(options);
|
|
97
|
+
const signal = options.signal;
|
|
98
|
+
const onAbort = () => task.cancel();
|
|
99
|
+
if (signal) {
|
|
100
|
+
if (signal.aborted)
|
|
101
|
+
task.cancel();
|
|
102
|
+
else
|
|
103
|
+
signal.addEventListener('abort', onAbort);
|
|
104
|
+
}
|
|
105
|
+
const stop = options.onProgress
|
|
106
|
+
? task.onProgress((p) => options.onProgress?.(Math.round(p.fraction * 100), p))
|
|
107
|
+
: null;
|
|
108
|
+
try {
|
|
109
|
+
return await task.result;
|
|
110
|
+
}
|
|
111
|
+
finally {
|
|
112
|
+
stop?.();
|
|
113
|
+
signal?.removeEventListener('abort', onAbort);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Starts an upload you can follow and cancel: the same upload as
|
|
118
|
+
* {@link upload}, returned at once as a {@link KoolbaseUploadTask}.
|
|
119
|
+
*
|
|
120
|
+
* The task goes pending -> uploading (bytes sent of total) -> verifying
|
|
121
|
+
* -> completed, or failed / cancelled. `cancel()` stops it until the
|
|
122
|
+
* server begins checking the bytes (verifying); nothing is stored. Only
|
|
123
|
+
* completed means the file can be referenced from a record.
|
|
124
|
+
*
|
|
125
|
+
* @example
|
|
126
|
+
* const task = Koolbase.storage.startUpload({ bucket: 'posters', path: 'gig.png', file });
|
|
127
|
+
* const stop = task.onProgress((p) => setPercent(Math.round(p.fraction * 100)));
|
|
128
|
+
* cancelButton.onclick = () => task.cancel();
|
|
129
|
+
* const { object } = await task.result;
|
|
130
|
+
*/
|
|
131
|
+
startUpload(options) {
|
|
132
|
+
return (0, storage_transfer_js_1.runUploadTask)((task) => this.runUpload(options, task));
|
|
133
|
+
}
|
|
134
|
+
async runUpload(options, task) {
|
|
135
|
+
task.checkCancelled();
|
|
94
136
|
const overwrite = options.overwrite ?? false;
|
|
95
137
|
// A Blob (browser File included) knows its own type; the React Native
|
|
96
138
|
// URI form carries it alongside. Falling back to octet-stream keeps a
|
|
@@ -100,6 +142,8 @@ class KoolbaseStorage {
|
|
|
100
142
|
const contentType = isBlob
|
|
101
143
|
? options.file.type || 'application/octet-stream'
|
|
102
144
|
: options.file.type;
|
|
145
|
+
if (isBlob)
|
|
146
|
+
task.setTotal(options.file.size);
|
|
103
147
|
// ─── Step 1: Get presigned upload URL ───
|
|
104
148
|
const urlRes = await (0, network_js_1.sessionFetch)(`${this.config.baseUrl}/v1/sdk/storage/upload-url`, {
|
|
105
149
|
method: 'POST',
|
|
@@ -117,7 +161,8 @@ class KoolbaseStorage {
|
|
|
117
161
|
if (!urlRes.ok) {
|
|
118
162
|
throw await this.error(urlRes, 'Failed to get upload URL');
|
|
119
163
|
}
|
|
120
|
-
const { upload_url } = (await urlRes.json());
|
|
164
|
+
const { upload_url, upload_id } = (await urlRes.json());
|
|
165
|
+
task.checkCancelled();
|
|
121
166
|
// ─── Step 2: Upload directly to R2 ───
|
|
122
167
|
// R2 presigned URLs expect raw binary, NOT multipart/form-data.
|
|
123
168
|
//
|
|
@@ -129,17 +174,17 @@ class KoolbaseStorage {
|
|
|
129
174
|
? options.file
|
|
130
175
|
: await (await (0, network_js_1.sessionFetch)(options.file.uri)).blob();
|
|
131
176
|
const fileSize = fileBlob.size;
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
body: fileBlob,
|
|
136
|
-
});
|
|
137
|
-
if (!uploadRes.ok) {
|
|
177
|
+
task.setTotal(fileSize);
|
|
178
|
+
const uploadRes = await (0, storage_transfer_js_1.putWithProgress)(task, upload_url, fileBlob, contentType);
|
|
179
|
+
if (uploadRes.status < 200 || uploadRes.status >= 300) {
|
|
138
180
|
// R2 PUT errors don't follow the Koolbase error shape — surface as a
|
|
139
181
|
// generic storage error rather than trying to decode a Koolbase body.
|
|
140
182
|
throw new storage_errors_js_2.KoolbaseStorageError(`Upload to storage failed: ${uploadRes.status}`);
|
|
141
183
|
}
|
|
142
|
-
const etag = uploadRes.
|
|
184
|
+
const etag = uploadRes.etag;
|
|
185
|
+
// From here the server checks the bytes and puts them in place; the
|
|
186
|
+
// upload can no longer be cancelled.
|
|
187
|
+
task.verifying();
|
|
143
188
|
// ─── Step 3: Confirm upload ───
|
|
144
189
|
// Build the body conditionally so the `metadata` field is only sent
|
|
145
190
|
// when the caller passed it — keeps the wire shape clean for callers
|
|
@@ -153,6 +198,10 @@ class KoolbaseStorage {
|
|
|
153
198
|
etag,
|
|
154
199
|
overwrite,
|
|
155
200
|
};
|
|
201
|
+
// The confirm names its upload, so it takes exactly these bytes.
|
|
202
|
+
if (upload_id) {
|
|
203
|
+
confirmBody.upload_id = upload_id;
|
|
204
|
+
}
|
|
156
205
|
if (options.metadata !== undefined) {
|
|
157
206
|
confirmBody.metadata = options.metadata;
|
|
158
207
|
}
|
|
@@ -173,6 +222,78 @@ class KoolbaseStorage {
|
|
|
173
222
|
const downloadUrl = await this.getDownloadUrl(options.bucket, options.path);
|
|
174
223
|
return { object, downloadUrl };
|
|
175
224
|
}
|
|
225
|
+
/**
|
|
226
|
+
* One page of a bucket's files. The bucket's view rule is applied to every
|
|
227
|
+
* file: the page holds only files the signed-in person may view. Pass the
|
|
228
|
+
* page's `nextCursor` back as `cursor` for the next one; a page can hold
|
|
229
|
+
* fewer files than `limit` and still have a next.
|
|
230
|
+
*
|
|
231
|
+
* @example
|
|
232
|
+
* let cursor: string | undefined;
|
|
233
|
+
* do {
|
|
234
|
+
* const page = await Koolbase.storage.listFiles({ bucket: 'posters', prefix: 'events/', cursor });
|
|
235
|
+
* show(page.files);
|
|
236
|
+
* cursor = page.nextCursor ?? undefined;
|
|
237
|
+
* } while (cursor);
|
|
238
|
+
*/
|
|
239
|
+
async listFiles(options) {
|
|
240
|
+
const res = await (0, network_js_1.sessionFetch)(`${this.config.baseUrl}/v1/sdk/storage/files?${(0, storage_files_js_1.listFilesQuery)(options)}`, { headers: await this.buildHeaders() });
|
|
241
|
+
if (!res.ok) {
|
|
242
|
+
throw await this.error(res, 'Failed to list files');
|
|
243
|
+
}
|
|
244
|
+
return (0, storage_files_js_1.filePageFromJson)((await res.json()));
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* One file's details -- size, type, metadata, who uploaded it, and its
|
|
248
|
+
* permanent link while its bucket is public. Throws
|
|
249
|
+
* `KoolbaseStorageNotFoundError` for a file that is not there or that
|
|
250
|
+
* the bucket's view rule hides from the caller.
|
|
251
|
+
*/
|
|
252
|
+
async getFileInfo(bucket, path) {
|
|
253
|
+
const res = await (0, network_js_1.sessionFetch)(`${this.config.baseUrl}/v1/sdk/storage/file?bucket=${encodeURIComponent(bucket)}&path=${encodeURIComponent(path)}`, { headers: await this.buildHeaders() });
|
|
254
|
+
if (!res.ok) {
|
|
255
|
+
throw await this.error(res, 'Failed to get the file');
|
|
256
|
+
}
|
|
257
|
+
return (0, storage_files_js_1.fileInfoFromJson)((await res.json()));
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Move a file to `to`, in its bucket or into `toBucket` (S1b). Nothing is
|
|
261
|
+
* uploaded again. The bucket's rules decide: delete on the file, and
|
|
262
|
+
* upload at the destination, whose limits apply as to an upload. The
|
|
263
|
+
* destination must be free.
|
|
264
|
+
*
|
|
265
|
+
* Within one bucket the file keeps its kept versions. Refused with
|
|
266
|
+
* `KoolbaseStorageFileInUseError` while records point at the file: change
|
|
267
|
+
* them first, or {@link copy} it.
|
|
268
|
+
*
|
|
269
|
+
* @example
|
|
270
|
+
* await Koolbase.storage.move({ bucket: 'inbox', from: 'scan.png', toBucket: 'vault', to: '2026/scan.png' });
|
|
271
|
+
*/
|
|
272
|
+
async move(options) {
|
|
273
|
+
return this.transfer('move', options);
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Copy a file to `to`, in its bucket or into `toBucket` (S1b): a new file,
|
|
277
|
+
* the caller's, with the same bytes. The bucket's rules decide: view on
|
|
278
|
+
* the file, and upload at the destination.
|
|
279
|
+
*/
|
|
280
|
+
async copy(options) {
|
|
281
|
+
return this.transfer('copy', options);
|
|
282
|
+
}
|
|
283
|
+
async transfer(op, o) {
|
|
284
|
+
const body = { bucket: o.bucket, from: o.from, to: o.to };
|
|
285
|
+
if (o.toBucket && o.toBucket !== o.bucket)
|
|
286
|
+
body.to_bucket = o.toBucket;
|
|
287
|
+
const res = await (0, network_js_1.sessionFetch)(`${this.config.baseUrl}/v1/sdk/storage/${op}`, {
|
|
288
|
+
method: 'POST',
|
|
289
|
+
headers: { ...(await this.buildHeaders()), 'Content-Type': 'application/json' },
|
|
290
|
+
body: JSON.stringify(body),
|
|
291
|
+
});
|
|
292
|
+
if (!res.ok) {
|
|
293
|
+
throw await this.error(res, op === 'move' ? 'Failed to move the file' : 'Failed to copy the file');
|
|
294
|
+
}
|
|
295
|
+
return mapObjectFromServer(await res.json());
|
|
296
|
+
}
|
|
176
297
|
/**
|
|
177
298
|
* Apply a partial metadata update to an existing object. Returns the
|
|
178
299
|
* post-update {@link KoolbaseObject} with the merged metadata.
|
package/dist/cjs/types.d.ts
CHANGED
|
@@ -369,7 +369,18 @@ export interface UploadOptions {
|
|
|
369
369
|
* for post-upload merge changes.
|
|
370
370
|
*/
|
|
371
371
|
metadata?: Record<string, string>;
|
|
372
|
-
|
|
372
|
+
/**
|
|
373
|
+
* Called as the upload goes: `percent` is 0 to 100 of the bytes sent;
|
|
374
|
+
* `progress` says where the upload is (uploading, verifying, completed,
|
|
375
|
+
* ...). For a cancellable upload use `KoolbaseStorage.startUpload`.
|
|
376
|
+
*/
|
|
377
|
+
onProgress?: (percent: number, progress: import('./storage-transfer.js').KoolbaseUploadProgress) => void;
|
|
378
|
+
/**
|
|
379
|
+
* Aborting this signal cancels the upload, if the server has not begun
|
|
380
|
+
* checking it yet; the upload then rejects with
|
|
381
|
+
* `KoolbaseUploadCancelledError`.
|
|
382
|
+
*/
|
|
383
|
+
signal?: AbortSignal;
|
|
373
384
|
}
|
|
374
385
|
/**
|
|
375
386
|
* A stored object's server-side metadata. Field names are camelCase here
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -20,6 +20,9 @@ export { KoolbaseRealtime } from './realtime.js';
|
|
|
20
20
|
export { KoolbaseConnectivity } from './connectivity.js';
|
|
21
21
|
export type { ConnectivityState } from './connectivity.js';
|
|
22
22
|
export { KoolbaseStorage } from './storage.js';
|
|
23
|
+
export { KoolbaseUploadTask } from './storage-transfer.js';
|
|
24
|
+
export type { KoolbaseUploadProgress, KoolbaseUploadState } from './storage-transfer.js';
|
|
25
|
+
export type { KoolbaseFileInfo, KoolbaseFilePage, KoolbaseFileSort, ListFilesOptions } from './storage-files.js';
|
|
23
26
|
export { KoolbaseAnalytics, disabledAnalytics } from './analytics.js';
|
|
24
27
|
export { KoolbaseFiscal, KoolbaseFiscalError, decodeFiscalResponse, isFiscalized, isPending } from './fiscal.js';
|
|
25
28
|
export type { FiscalIntentResult, FiscalStatus } from './fiscal.js';
|
package/dist/esm/index.js
CHANGED
|
@@ -22,6 +22,7 @@ export { KoolbaseFunctions } from './functions.js';
|
|
|
22
22
|
export { KoolbaseRealtime } from './realtime.js';
|
|
23
23
|
export { KoolbaseConnectivity } from './connectivity.js';
|
|
24
24
|
export { KoolbaseStorage } from './storage.js';
|
|
25
|
+
export { KoolbaseUploadTask } from './storage-transfer.js';
|
|
25
26
|
export { KoolbaseAnalytics, disabledAnalytics } from './analytics.js';
|
|
26
27
|
export { KoolbaseFiscal, KoolbaseFiscalError, decodeFiscalResponse, isFiscalized, isPending } from './fiscal.js';
|
|
27
28
|
export { KoolbaseMessaging } from './messaging.js';
|
|
@@ -187,6 +187,63 @@ export declare class KoolbaseStorageMetadataInvalidError extends KoolbaseStorage
|
|
|
187
187
|
detail?: string;
|
|
188
188
|
constructor(message?: string, detail?: string);
|
|
189
189
|
}
|
|
190
|
+
/**
|
|
191
|
+
* An upload that was cancelled -- by `KoolbaseUploadTask.cancel()` or the
|
|
192
|
+
* upload's `signal` -- before the server began checking it. Nothing was
|
|
193
|
+
* stored. Code: `upload_cancelled`.
|
|
194
|
+
*/
|
|
195
|
+
export declare class KoolbaseUploadCancelledError extends KoolbaseStorageError {
|
|
196
|
+
constructor(message?: string);
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* The confirm named an upload the server does not have: it expired, was
|
|
200
|
+
* confirmed already, or belongs to someone else. Upload the file again.
|
|
201
|
+
* Code: `upload_not_found`.
|
|
202
|
+
*/
|
|
203
|
+
export declare class KoolbaseUploadNotFoundError extends KoolbaseStorageError {
|
|
204
|
+
constructor(message?: string);
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* The bucket is busy with other changes (a large move, many uploads at
|
|
208
|
+
* once). Nothing was changed; retry shortly. Code: `bucket_busy`.
|
|
209
|
+
*/
|
|
210
|
+
export declare class KoolbaseStorageBusyError extends KoolbaseStorageError {
|
|
211
|
+
constructor(message?: string);
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* A listing's cursor was not one this listing issued -- made up, or from a
|
|
215
|
+
* listing with another sort or order. Start again from the first page.
|
|
216
|
+
* Code: `invalid_cursor`.
|
|
217
|
+
*/
|
|
218
|
+
export declare class KoolbaseStorageInvalidCursorError extends KoolbaseStorageError {
|
|
219
|
+
constructor(message?: string);
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* The bucket's rules allow this only to someone signed in, and nobody is.
|
|
223
|
+
* Not a session that ended: the SDK's session is left alone. Code:
|
|
224
|
+
* `sign_in_required`.
|
|
225
|
+
*/
|
|
226
|
+
export declare class KoolbaseStorageSignInRequiredError extends KoolbaseStorageError {
|
|
227
|
+
constructor(message?: string);
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* A move refused while records point at the file (S1b): `field` holds its
|
|
231
|
+
* path in `records` records. Change those first, or copy the file. Code:
|
|
232
|
+
* `file_in_use`.
|
|
233
|
+
*/
|
|
234
|
+
export declare class KoolbaseStorageFileInUseError extends KoolbaseStorageError {
|
|
235
|
+
field?: string;
|
|
236
|
+
records: number;
|
|
237
|
+
constructor(message?: string, field?: string, records?: number);
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* A move, within a bucket, to a name a deleted file's kept versions still
|
|
241
|
+
* use (S1b): the two histories would become one. Choose another name.
|
|
242
|
+
* Code: `name_has_history`.
|
|
243
|
+
*/
|
|
244
|
+
export declare class KoolbaseStorageNameHasHistoryError extends KoolbaseStorageError {
|
|
245
|
+
constructor(message?: string);
|
|
246
|
+
}
|
|
190
247
|
/**
|
|
191
248
|
* Maps a non-2xx storage-layer response to a typed
|
|
192
249
|
* {@link KoolbaseStorageError}, preferring the server's stable `code` and
|
|
@@ -234,6 +234,91 @@ export class KoolbaseStorageMetadataInvalidError extends KoolbaseStorageError {
|
|
|
234
234
|
Object.setPrototypeOf(this, KoolbaseStorageMetadataInvalidError.prototype);
|
|
235
235
|
}
|
|
236
236
|
}
|
|
237
|
+
/**
|
|
238
|
+
* An upload that was cancelled -- by `KoolbaseUploadTask.cancel()` or the
|
|
239
|
+
* upload's `signal` -- before the server began checking it. Nothing was
|
|
240
|
+
* stored. Code: `upload_cancelled`.
|
|
241
|
+
*/
|
|
242
|
+
export class KoolbaseUploadCancelledError extends KoolbaseStorageError {
|
|
243
|
+
constructor(message) {
|
|
244
|
+
super(message ?? 'The upload was cancelled', 'upload_cancelled');
|
|
245
|
+
this.name = 'KoolbaseUploadCancelledError';
|
|
246
|
+
Object.setPrototypeOf(this, KoolbaseUploadCancelledError.prototype);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* The confirm named an upload the server does not have: it expired, was
|
|
251
|
+
* confirmed already, or belongs to someone else. Upload the file again.
|
|
252
|
+
* Code: `upload_not_found`.
|
|
253
|
+
*/
|
|
254
|
+
export class KoolbaseUploadNotFoundError extends KoolbaseStorageError {
|
|
255
|
+
constructor(message) {
|
|
256
|
+
super(message ?? 'This upload was not found -- please upload the file again', 'upload_not_found');
|
|
257
|
+
this.name = 'KoolbaseUploadNotFoundError';
|
|
258
|
+
Object.setPrototypeOf(this, KoolbaseUploadNotFoundError.prototype);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* The bucket is busy with other changes (a large move, many uploads at
|
|
263
|
+
* once). Nothing was changed; retry shortly. Code: `bucket_busy`.
|
|
264
|
+
*/
|
|
265
|
+
export class KoolbaseStorageBusyError extends KoolbaseStorageError {
|
|
266
|
+
constructor(message) {
|
|
267
|
+
super(message ?? 'The bucket is busy -- try again shortly', 'bucket_busy');
|
|
268
|
+
this.name = 'KoolbaseStorageBusyError';
|
|
269
|
+
Object.setPrototypeOf(this, KoolbaseStorageBusyError.prototype);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* A listing's cursor was not one this listing issued -- made up, or from a
|
|
274
|
+
* listing with another sort or order. Start again from the first page.
|
|
275
|
+
* Code: `invalid_cursor`.
|
|
276
|
+
*/
|
|
277
|
+
export class KoolbaseStorageInvalidCursorError extends KoolbaseStorageError {
|
|
278
|
+
constructor(message) {
|
|
279
|
+
super(message ?? 'The cursor is not valid for this listing', 'invalid_cursor');
|
|
280
|
+
this.name = 'KoolbaseStorageInvalidCursorError';
|
|
281
|
+
Object.setPrototypeOf(this, KoolbaseStorageInvalidCursorError.prototype);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* The bucket's rules allow this only to someone signed in, and nobody is.
|
|
286
|
+
* Not a session that ended: the SDK's session is left alone. Code:
|
|
287
|
+
* `sign_in_required`.
|
|
288
|
+
*/
|
|
289
|
+
export class KoolbaseStorageSignInRequiredError extends KoolbaseStorageError {
|
|
290
|
+
constructor(message) {
|
|
291
|
+
super(message ?? 'Sign in to do this', 'sign_in_required');
|
|
292
|
+
this.name = 'KoolbaseStorageSignInRequiredError';
|
|
293
|
+
Object.setPrototypeOf(this, KoolbaseStorageSignInRequiredError.prototype);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* A move refused while records point at the file (S1b): `field` holds its
|
|
298
|
+
* path in `records` records. Change those first, or copy the file. Code:
|
|
299
|
+
* `file_in_use`.
|
|
300
|
+
*/
|
|
301
|
+
export class KoolbaseStorageFileInUseError extends KoolbaseStorageError {
|
|
302
|
+
constructor(message, field, records = 0) {
|
|
303
|
+
super(message ?? 'Records still point at this file', 'file_in_use');
|
|
304
|
+
this.field = field;
|
|
305
|
+
this.records = records;
|
|
306
|
+
this.name = 'KoolbaseStorageFileInUseError';
|
|
307
|
+
Object.setPrototypeOf(this, KoolbaseStorageFileInUseError.prototype);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* A move, within a bucket, to a name a deleted file's kept versions still
|
|
312
|
+
* use (S1b): the two histories would become one. Choose another name.
|
|
313
|
+
* Code: `name_has_history`.
|
|
314
|
+
*/
|
|
315
|
+
export class KoolbaseStorageNameHasHistoryError extends KoolbaseStorageError {
|
|
316
|
+
constructor(message) {
|
|
317
|
+
super(message ?? 'A deleted file by that name still has kept versions; choose another name', 'name_has_history');
|
|
318
|
+
this.name = 'KoolbaseStorageNameHasHistoryError';
|
|
319
|
+
Object.setPrototypeOf(this, KoolbaseStorageNameHasHistoryError.prototype);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
237
322
|
/**
|
|
238
323
|
* Maps a non-2xx storage-layer response to a typed
|
|
239
324
|
* {@link KoolbaseStorageError}, preferring the server's stable `code` and
|
|
@@ -271,6 +356,22 @@ export function koolbaseStorageError(status, body, fallbackMessage = 'Storage re
|
|
|
271
356
|
return new KoolbaseStorageMetadataInvalidError(message, body?.detail);
|
|
272
357
|
case 'upload_url_failed':
|
|
273
358
|
return new KoolbaseUploadURLFailedError(message);
|
|
359
|
+
case 'upload_not_found':
|
|
360
|
+
return new KoolbaseUploadNotFoundError(message);
|
|
361
|
+
case 'bucket_busy':
|
|
362
|
+
return new KoolbaseStorageBusyError(message);
|
|
363
|
+
case 'invalid_cursor':
|
|
364
|
+
return new KoolbaseStorageInvalidCursorError(message);
|
|
365
|
+
case 'sign_in_required':
|
|
366
|
+
return new KoolbaseStorageSignInRequiredError(message);
|
|
367
|
+
case 'not_allowed':
|
|
368
|
+
return new KoolbaseStoragePermissionError(message);
|
|
369
|
+
case 'file_in_use': {
|
|
370
|
+
const d = (body?.details ?? {});
|
|
371
|
+
return new KoolbaseStorageFileInUseError(message, d.field, Number(d.records ?? 0));
|
|
372
|
+
}
|
|
373
|
+
case 'name_has_history':
|
|
374
|
+
return new KoolbaseStorageNameHasHistoryError(message);
|
|
274
375
|
}
|
|
275
376
|
// ─── status fallback (pre-code servers or uncoded paths) ───
|
|
276
377
|
switch (status) {
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/** What a listing is ordered by. */
|
|
2
|
+
export type KoolbaseFileSort = 'path' | 'createdAt' | 'updatedAt' | 'size';
|
|
3
|
+
export interface ListFilesOptions {
|
|
4
|
+
bucket: string;
|
|
5
|
+
/** Only files whose path starts with this, taken literally. */
|
|
6
|
+
prefix?: string;
|
|
7
|
+
/** Files per page: 50 unless set, at most 100. */
|
|
8
|
+
limit?: number;
|
|
9
|
+
/** A page's `nextCursor`, for the page after it. */
|
|
10
|
+
cursor?: string;
|
|
11
|
+
/** By path unless set. */
|
|
12
|
+
sort?: KoolbaseFileSort;
|
|
13
|
+
/** Z to A, newest first, largest first. */
|
|
14
|
+
descending?: boolean;
|
|
15
|
+
}
|
|
16
|
+
/** One file. */
|
|
17
|
+
export interface KoolbaseFileInfo {
|
|
18
|
+
/** Its path in the bucket: what a record stores to point at it. */
|
|
19
|
+
path: string;
|
|
20
|
+
/** The last part of its path. */
|
|
21
|
+
name: string;
|
|
22
|
+
size: number;
|
|
23
|
+
contentType: string | null;
|
|
24
|
+
etag: string | null;
|
|
25
|
+
metadata: Record<string, string>;
|
|
26
|
+
versionId: string | null;
|
|
27
|
+
/** Who uploaded it: a project user's id; null when the project did. */
|
|
28
|
+
uploadedBy: string | null;
|
|
29
|
+
/** ISO 8601. */
|
|
30
|
+
createdAt: string;
|
|
31
|
+
/** ISO 8601. */
|
|
32
|
+
updatedAt: string;
|
|
33
|
+
/**
|
|
34
|
+
* Its permanent link, while its bucket is public; null otherwise -- ask
|
|
35
|
+
* for a download link instead.
|
|
36
|
+
*/
|
|
37
|
+
publicUrl: string | null;
|
|
38
|
+
}
|
|
39
|
+
/** One page of a listing. */
|
|
40
|
+
export interface KoolbaseFilePage {
|
|
41
|
+
files: KoolbaseFileInfo[];
|
|
42
|
+
/**
|
|
43
|
+
* Pass to the next call for the page after this one; null at the end. A
|
|
44
|
+
* page can hold fewer files than asked for -- the server passes over
|
|
45
|
+
* files you may not view -- and still have a next one.
|
|
46
|
+
*/
|
|
47
|
+
nextCursor: string | null;
|
|
48
|
+
hasMore: boolean;
|
|
49
|
+
}
|
|
50
|
+
/** The query string for a listing. */
|
|
51
|
+
export declare function listFilesQuery(o: ListFilesOptions): string;
|
|
52
|
+
export declare function fileInfoFromJson(j: Record<string, unknown>): KoolbaseFileInfo;
|
|
53
|
+
export declare function filePageFromJson(j: Record<string, unknown>): KoolbaseFilePage;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// A bucket's files, as an app sees them (S1).
|
|
2
|
+
//
|
|
3
|
+
// The server applies the bucket's view rule to every file: a listing holds
|
|
4
|
+
// only files the signed-in person may view, and says nothing of the rest.
|
|
5
|
+
const sortWire = {
|
|
6
|
+
path: 'path',
|
|
7
|
+
createdAt: 'created_at',
|
|
8
|
+
updatedAt: 'updated_at',
|
|
9
|
+
size: 'size',
|
|
10
|
+
};
|
|
11
|
+
/** The query string for a listing. */
|
|
12
|
+
export function listFilesQuery(o) {
|
|
13
|
+
const q = new URLSearchParams();
|
|
14
|
+
q.set('bucket', o.bucket);
|
|
15
|
+
if (o.prefix)
|
|
16
|
+
q.set('prefix', o.prefix);
|
|
17
|
+
if (o.limit != null)
|
|
18
|
+
q.set('limit', String(o.limit));
|
|
19
|
+
if (o.cursor)
|
|
20
|
+
q.set('cursor', o.cursor);
|
|
21
|
+
q.set('sort', sortWire[o.sort ?? 'path'] ?? 'path');
|
|
22
|
+
q.set('order', o.descending ? 'desc' : 'asc');
|
|
23
|
+
return q.toString();
|
|
24
|
+
}
|
|
25
|
+
const str = (v) => (typeof v === 'string' && v !== '' ? v : null);
|
|
26
|
+
export function fileInfoFromJson(j) {
|
|
27
|
+
const path = String(j.path ?? '');
|
|
28
|
+
const metadata = {};
|
|
29
|
+
if (j.metadata && typeof j.metadata === 'object') {
|
|
30
|
+
for (const [k, v] of Object.entries(j.metadata)) {
|
|
31
|
+
metadata[k] = typeof v === 'string' ? v : String(v);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
path,
|
|
36
|
+
name: path.split('/').pop() ?? path,
|
|
37
|
+
size: Number(j.size ?? 0),
|
|
38
|
+
contentType: str(j.content_type),
|
|
39
|
+
etag: str(j.etag),
|
|
40
|
+
metadata,
|
|
41
|
+
versionId: str(j.version_id),
|
|
42
|
+
uploadedBy: str(j.uploaded_by),
|
|
43
|
+
createdAt: String(j.created_at ?? ''),
|
|
44
|
+
updatedAt: String(j.updated_at ?? ''),
|
|
45
|
+
publicUrl: str(j.public_url),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
export function filePageFromJson(j) {
|
|
49
|
+
const files = Array.isArray(j.files) ? j.files : [];
|
|
50
|
+
const nextCursor = str(j.next_cursor);
|
|
51
|
+
return {
|
|
52
|
+
files: files.map((f) => fileInfoFromJson(f)),
|
|
53
|
+
nextCursor,
|
|
54
|
+
hasMore: nextCursor !== null,
|
|
55
|
+
};
|
|
56
|
+
}
|