@koolbase/react-native 9.2.0 → 10.0.1
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/CHANGELOG.md +1348 -0
- package/README.md +403 -568
- package/dist/{auth-storage.d.ts → cjs/auth-storage.d.ts} +1 -1
- package/dist/cjs/index.d.ts +19 -0
- package/dist/cjs/index.js +125 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/platform.d.ts +2 -0
- package/dist/cjs/platform.js +43 -0
- package/dist/esm/auth-storage.d.ts +26 -0
- package/dist/esm/auth-storage.js +100 -0
- package/dist/esm/index.d.ts +19 -0
- package/dist/esm/index.js +106 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/platform.d.ts +2 -0
- package/dist/esm/platform.js +37 -0
- package/package.json +30 -30
- package/dist/analytics.d.ts +0 -24
- package/dist/analytics.js +0 -114
- package/dist/apple-auth.d.ts +0 -22
- package/dist/apple-auth.js +0 -74
- package/dist/auth-errors.d.ts +0 -117
- package/dist/auth-errors.js +0 -250
- package/dist/auth.d.ts +0 -213
- package/dist/auth.js +0 -810
- package/dist/cache-store.d.ts +0 -50
- package/dist/cache-store.js +0 -197
- package/dist/code-push.d.ts +0 -59
- package/dist/code-push.js +0 -255
- package/dist/conflict.d.ts +0 -80
- package/dist/conflict.js +0 -84
- package/dist/database-errors.d.ts +0 -101
- package/dist/database-errors.js +0 -200
- package/dist/database.d.ts +0 -298
- package/dist/database.js +0 -852
- package/dist/device-id.d.ts +0 -1
- package/dist/device-id.js +0 -60
- package/dist/device-metadata.d.ts +0 -36
- package/dist/device-metadata.js +0 -102
- package/dist/errors.d.ts +0 -64
- package/dist/errors.js +0 -85
- package/dist/flags.d.ts +0 -15
- package/dist/flags.js +0 -76
- package/dist/function-errors.d.ts +0 -51
- package/dist/function-errors.js +0 -103
- package/dist/functions.d.ts +0 -15
- package/dist/functions.js +0 -83
- package/dist/index.d.ts +0 -49
- package/dist/index.js +0 -204
- package/dist/logic-engine.d.ts +0 -17
- package/dist/logic-engine.js +0 -193
- package/dist/messaging.d.ts +0 -13
- package/dist/messaging.js +0 -36
- package/dist/offline-state.d.ts +0 -97
- package/dist/offline-state.js +0 -200
- package/dist/pending-write.d.ts +0 -47
- package/dist/pending-write.js +0 -22
- package/dist/realtime.d.ts +0 -44
- package/dist/realtime.js +0 -195
- package/dist/record.d.ts +0 -2
- package/dist/record.js +0 -23
- package/dist/storage-errors.d.ts +0 -163
- package/dist/storage-errors.js +0 -253
- package/dist/storage.d.ts +0 -198
- package/dist/storage.js +0 -451
- package/dist/sync-engine.d.ts +0 -30
- package/dist/sync-engine.js +0 -290
- package/dist/types.d.ts +0 -487
- package/dist/types.js +0 -40
- /package/dist/{auth-storage.js → cjs/auth-storage.js} +0 -0
package/dist/storage.js
DELETED
|
@@ -1,451 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.KoolbaseStorage = void 0;
|
|
4
|
-
const errors_1 = require("./errors");
|
|
5
|
-
const storage_errors_1 = require("./storage-errors");
|
|
6
|
-
// --- Cloudflare image-transform URL helpers -------------------------------
|
|
7
|
-
// Module-private — callers use KoolbaseStorage.publicUrl / publicUrlForObject.
|
|
8
|
-
function clampInt(v, min, max) {
|
|
9
|
-
return Math.max(min, Math.min(max, Math.floor(v)));
|
|
10
|
-
}
|
|
11
|
-
/**
|
|
12
|
-
* Serializes a transform spec to Cloudflare's comma-separated key=value
|
|
13
|
-
* options segment (e.g. `width=400,format=webp,quality=80`). Returns the
|
|
14
|
-
* empty string when no fields are set — callers can use that to skip the
|
|
15
|
-
* `/cdn-cgi/image/` URL prefix entirely.
|
|
16
|
-
*/
|
|
17
|
-
function serializeTransform(t) {
|
|
18
|
-
const parts = [];
|
|
19
|
-
if (t.width != null)
|
|
20
|
-
parts.push(`width=${clampInt(t.width, 1, 2000)}`);
|
|
21
|
-
if (t.height != null)
|
|
22
|
-
parts.push(`height=${clampInt(t.height, 1, 2000)}`);
|
|
23
|
-
if (t.format)
|
|
24
|
-
parts.push(`format=${t.format}`);
|
|
25
|
-
if (t.quality != null)
|
|
26
|
-
parts.push(`quality=${clampInt(t.quality, 1, 100)}`);
|
|
27
|
-
if (t.fit)
|
|
28
|
-
parts.push(`fit=${t.fit}`);
|
|
29
|
-
if (t.dpr != null)
|
|
30
|
-
parts.push(`dpr=${clampInt(t.dpr, 1, 3)}`);
|
|
31
|
-
if (t.gravity)
|
|
32
|
-
parts.push(`gravity=${t.gravity}`);
|
|
33
|
-
return parts.join(',');
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
* Koolbase storage client — uploads, downloads, and deletes via presigned
|
|
37
|
-
* Cloudflare R2 URLs.
|
|
38
|
-
*
|
|
39
|
-
* Uploads are **safe-by-default** (v5+): an upload to a path where an object
|
|
40
|
-
* already exists is rejected with {@link KoolbaseStorageConflictError} unless
|
|
41
|
-
* `overwrite: true` is passed.
|
|
42
|
-
*/
|
|
43
|
-
class KoolbaseStorage {
|
|
44
|
-
constructor(config, getToken, onSessionExpired) {
|
|
45
|
-
this.config = config;
|
|
46
|
-
this.getToken = getToken;
|
|
47
|
-
this.onSessionExpired = onSessionExpired;
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
* Builds the error for a failed response and clears the session when the
|
|
51
|
-
* credentials were refused, before the error reaches the caller.
|
|
52
|
-
*/
|
|
53
|
-
async error(res, fallback) {
|
|
54
|
-
const err = await (0, storage_errors_1.koolbaseStorageErrorFromResponse)(res, fallback);
|
|
55
|
-
if (err instanceof errors_1.KoolbaseUnauthenticatedError) {
|
|
56
|
-
await this.onSessionExpired?.();
|
|
57
|
-
}
|
|
58
|
-
return err;
|
|
59
|
-
}
|
|
60
|
-
async buildHeaders() {
|
|
61
|
-
const token = await this.getToken();
|
|
62
|
-
return {
|
|
63
|
-
'x-api-key': this.config.publicKey,
|
|
64
|
-
...(token ? { Authorization: `Bearer ${token}` } : {}),
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
/**
|
|
68
|
-
* Upload a file to a bucket. Returns the object metadata and a download URL.
|
|
69
|
-
*
|
|
70
|
-
* By default (`overwrite: false`), uploads to a path where an object
|
|
71
|
-
* already exists are **rejected** with a {@link KoolbaseStorageConflictError}.
|
|
72
|
-
* Catch it to prompt the user, then retry with `overwrite: true` to replace
|
|
73
|
-
* the existing object — or with a different `path`.
|
|
74
|
-
*
|
|
75
|
-
* Set `overwrite: true` for true upsert semantics — silently replace any
|
|
76
|
-
* existing object at this path.
|
|
77
|
-
*
|
|
78
|
-
* Pass `options.metadata` to attach arbitrary user-defined key/value pairs
|
|
79
|
-
* to the object at confirm time. Subject to the limits documented on
|
|
80
|
-
* {@link KoolbaseObject.metadata}; violations throw
|
|
81
|
-
* `KoolbaseStorageMetadataInvalidError`. On the `overwrite: true` path the
|
|
82
|
-
* metadata REPLACES any prior metadata at this path (matches GCS semantics).
|
|
83
|
-
* Use {@link updateMetadata} for post-upload merge changes.
|
|
84
|
-
*
|
|
85
|
-
* **Breaking change in v5.0.0**: the default flipped from silent overwrite
|
|
86
|
-
* (legacy behavior) to safe-by-default. If you previously relied on uploads
|
|
87
|
-
* overwriting silently, pass `overwrite: true` explicitly.
|
|
88
|
-
*/
|
|
89
|
-
async upload(options) {
|
|
90
|
-
const overwrite = options.overwrite ?? false;
|
|
91
|
-
const contentType = options.file.type;
|
|
92
|
-
// ─── Step 1: Get presigned upload URL ───
|
|
93
|
-
const urlRes = await fetch(`${this.config.baseUrl}/v1/sdk/storage/upload-url`, {
|
|
94
|
-
method: 'POST',
|
|
95
|
-
headers: {
|
|
96
|
-
...(await this.buildHeaders()),
|
|
97
|
-
'Content-Type': 'application/json',
|
|
98
|
-
},
|
|
99
|
-
body: JSON.stringify({
|
|
100
|
-
bucket: options.bucket,
|
|
101
|
-
path: options.path,
|
|
102
|
-
content_type: contentType,
|
|
103
|
-
overwrite,
|
|
104
|
-
}),
|
|
105
|
-
});
|
|
106
|
-
if (!urlRes.ok) {
|
|
107
|
-
throw await this.error(urlRes, 'Failed to get upload URL');
|
|
108
|
-
}
|
|
109
|
-
const { upload_url } = (await urlRes.json());
|
|
110
|
-
// ─── Step 2: Upload directly to R2 ───
|
|
111
|
-
// RN's fetch resolves local file URIs and Blob bodies on a raw PUT.
|
|
112
|
-
// R2 presigned URLs expect raw binary, NOT multipart/form-data.
|
|
113
|
-
const fileResp = await fetch(options.file.uri);
|
|
114
|
-
const fileBlob = await fileResp.blob();
|
|
115
|
-
const fileSize = fileBlob.size;
|
|
116
|
-
const uploadRes = await fetch(upload_url, {
|
|
117
|
-
method: 'PUT',
|
|
118
|
-
headers: { 'Content-Type': contentType },
|
|
119
|
-
body: fileBlob,
|
|
120
|
-
});
|
|
121
|
-
if (!uploadRes.ok) {
|
|
122
|
-
// R2 PUT errors don't follow the Koolbase error shape — surface as a
|
|
123
|
-
// generic storage error rather than trying to decode a Koolbase body.
|
|
124
|
-
throw new storage_errors_1.KoolbaseStorageError(`Upload to storage failed: ${uploadRes.status}`);
|
|
125
|
-
}
|
|
126
|
-
const etag = uploadRes.headers.get('etag') ?? '';
|
|
127
|
-
// ─── Step 3: Confirm upload ───
|
|
128
|
-
// Build the body conditionally so the `metadata` field is only sent
|
|
129
|
-
// when the caller passed it — keeps the wire shape clean for callers
|
|
130
|
-
// that don't care, and lets the server's omitempty path treat absent
|
|
131
|
-
// as "no metadata."
|
|
132
|
-
const confirmBody = {
|
|
133
|
-
bucket: options.bucket,
|
|
134
|
-
path: options.path,
|
|
135
|
-
size: fileSize,
|
|
136
|
-
content_type: contentType,
|
|
137
|
-
etag,
|
|
138
|
-
overwrite,
|
|
139
|
-
};
|
|
140
|
-
if (options.metadata !== undefined) {
|
|
141
|
-
confirmBody.metadata = options.metadata;
|
|
142
|
-
}
|
|
143
|
-
const confirmRes = await fetch(`${this.config.baseUrl}/v1/sdk/storage/confirm`, {
|
|
144
|
-
method: 'POST',
|
|
145
|
-
headers: {
|
|
146
|
-
...(await this.buildHeaders()),
|
|
147
|
-
'Content-Type': 'application/json',
|
|
148
|
-
},
|
|
149
|
-
body: JSON.stringify(confirmBody),
|
|
150
|
-
});
|
|
151
|
-
if (!confirmRes.ok) {
|
|
152
|
-
throw await this.error(confirmRes, 'Failed to confirm upload');
|
|
153
|
-
}
|
|
154
|
-
const raw = await confirmRes.json();
|
|
155
|
-
const object = mapObjectFromServer(raw);
|
|
156
|
-
// ─── Step 4: Get download URL ───
|
|
157
|
-
const downloadUrl = await this.getDownloadUrl(options.bucket, options.path);
|
|
158
|
-
return { object, downloadUrl };
|
|
159
|
-
}
|
|
160
|
-
/**
|
|
161
|
-
* Apply a partial metadata update to an existing object. Returns the
|
|
162
|
-
* post-update {@link KoolbaseObject} with the merged metadata.
|
|
163
|
-
*
|
|
164
|
-
* **Merge semantics** (mirrors the server's JSONB merge):
|
|
165
|
-
*
|
|
166
|
-
* - Keys with a non-null string value are SET — added if missing,
|
|
167
|
-
* replacing any existing value at the key otherwise.
|
|
168
|
-
* - Keys with `null` are DELETED from the stored metadata.
|
|
169
|
-
* - Keys ABSENT from `metadata` are untouched — pre-existing entries
|
|
170
|
-
* for those keys remain unchanged.
|
|
171
|
-
*
|
|
172
|
-
* Validation runs server-side against the same rules as upload-time
|
|
173
|
-
* metadata; violations throw `KoolbaseStorageMetadataInvalidError`,
|
|
174
|
-
* whose `detail` field names the failing key and rule. The check is
|
|
175
|
-
* performed against the projected post-merge state, so adding a key
|
|
176
|
-
* that would push the object past the 50-key or 8KB ceiling is
|
|
177
|
-
* rejected before the row is mutated.
|
|
178
|
-
*
|
|
179
|
-
* @example
|
|
180
|
-
* // Add a tag, update an existing key, and drop another in one call:
|
|
181
|
-
* const updated = await Koolbase.storage.updateMetadata(
|
|
182
|
-
* 'photos',
|
|
183
|
-
* 'sunset.jpg',
|
|
184
|
-
* {
|
|
185
|
-
* category: 'landscape', // SET or UPDATE
|
|
186
|
-
* tag: 'sunset', // SET or UPDATE
|
|
187
|
-
* owner: null, // DELETE
|
|
188
|
-
* }
|
|
189
|
-
* );
|
|
190
|
-
* console.log(updated.metadata);
|
|
191
|
-
* // -> { category: 'landscape', tag: 'sunset' }
|
|
192
|
-
*/
|
|
193
|
-
async updateMetadata(bucket, path, metadata) {
|
|
194
|
-
const res = await fetch(`${this.config.baseUrl}/v1/sdk/storage/objects/metadata`, {
|
|
195
|
-
method: 'PATCH',
|
|
196
|
-
headers: {
|
|
197
|
-
...(await this.buildHeaders()),
|
|
198
|
-
'Content-Type': 'application/json',
|
|
199
|
-
},
|
|
200
|
-
body: JSON.stringify({ bucket, path, metadata }),
|
|
201
|
-
});
|
|
202
|
-
if (!res.ok) {
|
|
203
|
-
throw await this.error(res, 'Failed to update metadata');
|
|
204
|
-
}
|
|
205
|
-
const raw = await res.json();
|
|
206
|
-
return mapObjectFromServer(raw);
|
|
207
|
-
}
|
|
208
|
-
/**
|
|
209
|
-
* Get a signed download URL for a file.
|
|
210
|
-
*/
|
|
211
|
-
async getDownloadUrl(bucket, path, versionId) {
|
|
212
|
-
let url = `${this.config.baseUrl}/v1/sdk/storage/download-url` +
|
|
213
|
-
`?bucket=${encodeURIComponent(bucket)}&path=${encodeURIComponent(path)}`;
|
|
214
|
-
if (versionId) {
|
|
215
|
-
url += `&version_id=${encodeURIComponent(versionId)}`;
|
|
216
|
-
}
|
|
217
|
-
const res = await fetch(url, { headers: await this.buildHeaders() });
|
|
218
|
-
if (!res.ok) {
|
|
219
|
-
throw await this.error(res, 'Failed to get download URL');
|
|
220
|
-
}
|
|
221
|
-
const data = (await res.json());
|
|
222
|
-
return data.url;
|
|
223
|
-
}
|
|
224
|
-
/**
|
|
225
|
-
* Build the stable public CDN URL for a file in a public bucket.
|
|
226
|
-
*
|
|
227
|
-
* Returns the URL unconditionally — no check on whether the file
|
|
228
|
-
* exists or whether the bucket is actually public. Use when you
|
|
229
|
-
* know the file is in a public bucket and want the URL without a
|
|
230
|
-
* network round-trip (build-time URL generation, server-side
|
|
231
|
-
* rendering, batch image processing, etc.).
|
|
232
|
-
*
|
|
233
|
-
* For safer construction from an Object you already have, use
|
|
234
|
-
* {@link KoolbaseStorage.publicUrlForObject} — it checks the stored
|
|
235
|
-
* `r2Bucket` value and returns `null` when the object isn't in the
|
|
236
|
-
* public R2 bucket.
|
|
237
|
-
*/
|
|
238
|
-
static publicUrl(args) {
|
|
239
|
-
// Encode each path segment individually so slashes are preserved
|
|
240
|
-
// while spaces, parens, hashes, and query characters are escaped.
|
|
241
|
-
const encoded = args.path.split('/').map(encodeURIComponent).join('/');
|
|
242
|
-
const opts = args.transform ? serializeTransform(args.transform) : '';
|
|
243
|
-
if (!opts) {
|
|
244
|
-
return `https://cdn.koolbase.com/${args.projectId}/${args.bucket}/${encoded}`;
|
|
245
|
-
}
|
|
246
|
-
return `https://cdn.koolbase.com/cdn-cgi/image/${opts}/${args.projectId}/${args.bucket}/${encoded}`;
|
|
247
|
-
}
|
|
248
|
-
/**
|
|
249
|
-
* Returns the stable CDN URL for an object when its bytes physically
|
|
250
|
-
* live in the public R2 bucket, `null` otherwise.
|
|
251
|
-
*
|
|
252
|
-
* Returns `null` for:
|
|
253
|
-
* - Files in private buckets (no public URL ever)
|
|
254
|
-
* - Legacy files in public buckets whose bytes still live in the
|
|
255
|
-
* private R2 bucket from before Gap #2 (no permanent URL until
|
|
256
|
-
* they're re-uploaded)
|
|
257
|
-
*
|
|
258
|
-
* The bucket name must be supplied because {@link KoolbaseObject}
|
|
259
|
-
* carries only the bucket ID, not its name. Typically the caller
|
|
260
|
-
* already knows which bucket they queried.
|
|
261
|
-
*/
|
|
262
|
-
static publicUrlForObject(obj, bucket, options) {
|
|
263
|
-
if (obj.r2Bucket !== 'koolbase-storage-public')
|
|
264
|
-
return null;
|
|
265
|
-
return KoolbaseStorage.publicUrl({
|
|
266
|
-
projectId: obj.projectId,
|
|
267
|
-
bucket,
|
|
268
|
-
path: obj.path,
|
|
269
|
-
transform: options?.transform,
|
|
270
|
-
});
|
|
271
|
-
}
|
|
272
|
-
/**
|
|
273
|
-
* Builds a named-preset CDN URL. The preset is resolved at the Cloudflare
|
|
274
|
-
* edge by the koolbase-cdn-worker, which looks up
|
|
275
|
-
* `preset:{project_id}:{preset_name}` in Workers KV and applies the stored
|
|
276
|
-
* transformation options. Presets are managed in the dashboard under
|
|
277
|
-
* Storage → Presets.
|
|
278
|
-
*
|
|
279
|
-
* Unknown preset names yield a 404 at the edge — the URL itself always
|
|
280
|
-
* constructs successfully without a network round-trip.
|
|
281
|
-
*
|
|
282
|
-
* For safer construction from an Object you already have, use
|
|
283
|
-
* {@link KoolbaseStorage.publicUrlForObjectWithPreset} — it checks the
|
|
284
|
-
* stored `r2Bucket` value and returns `null` when the object isn't in the
|
|
285
|
-
* public R2 bucket.
|
|
286
|
-
*/
|
|
287
|
-
static publicUrlWithPreset(args) {
|
|
288
|
-
const encoded = args.path.split('/').map(encodeURIComponent).join('/');
|
|
289
|
-
return `https://cdn.koolbase.com/p/${args.projectId}/${args.presetName}/${args.bucket}/${encoded}`;
|
|
290
|
-
}
|
|
291
|
-
/**
|
|
292
|
-
* Returns the named-preset CDN URL for the given object, or `null` if the
|
|
293
|
-
* object isn't in the public R2 bucket.
|
|
294
|
-
*/
|
|
295
|
-
static publicUrlForObjectWithPreset(obj, bucket, presetName) {
|
|
296
|
-
if (obj.r2Bucket !== 'koolbase-storage-public')
|
|
297
|
-
return null;
|
|
298
|
-
return KoolbaseStorage.publicUrlWithPreset({
|
|
299
|
-
projectId: obj.projectId,
|
|
300
|
-
presetName,
|
|
301
|
-
bucket,
|
|
302
|
-
path: obj.path,
|
|
303
|
-
});
|
|
304
|
-
}
|
|
305
|
-
/**
|
|
306
|
-
* Delete a file from a bucket.
|
|
307
|
-
*/
|
|
308
|
-
async delete(bucket, path, forcePurge) {
|
|
309
|
-
const url = forcePurge
|
|
310
|
-
? `${this.config.baseUrl}/v1/sdk/storage/object?force_purge=true`
|
|
311
|
-
: `${this.config.baseUrl}/v1/sdk/storage/object`;
|
|
312
|
-
const res = await fetch(url, {
|
|
313
|
-
method: 'DELETE',
|
|
314
|
-
headers: {
|
|
315
|
-
...(await this.buildHeaders()),
|
|
316
|
-
'Content-Type': 'application/json',
|
|
317
|
-
},
|
|
318
|
-
body: JSON.stringify({ bucket, path }),
|
|
319
|
-
});
|
|
320
|
-
if (res.status === 204)
|
|
321
|
-
return;
|
|
322
|
-
if (!res.ok) {
|
|
323
|
-
throw await this.error(res, 'Failed to delete file');
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
/**
|
|
327
|
-
* List all versions of a file path, newest-first. Returns a flat list
|
|
328
|
-
* mixing the current row (with `isCurrent: true`) and all history
|
|
329
|
-
* rows. Delete markers are included so callers can render the full
|
|
330
|
-
* timeline; filter client-side to hide them if the UI only wants
|
|
331
|
-
* restorable versions.
|
|
332
|
-
*
|
|
333
|
-
* Returns an empty array (not an error) when the path has no history
|
|
334
|
-
* and no current row.
|
|
335
|
-
*/
|
|
336
|
-
async listVersions(bucket, path) {
|
|
337
|
-
const url = `${this.config.baseUrl}/v1/sdk/storage/object-versions` +
|
|
338
|
-
`?bucket=${encodeURIComponent(bucket)}&path=${encodeURIComponent(path)}`;
|
|
339
|
-
const res = await fetch(url, { headers: await this.buildHeaders() });
|
|
340
|
-
if (!res.ok) {
|
|
341
|
-
throw await this.error(res, 'Failed to list versions');
|
|
342
|
-
}
|
|
343
|
-
const data = (await res.json());
|
|
344
|
-
const list = Array.isArray(data.versions) ? data.versions : [];
|
|
345
|
-
return list.map((v) => fromVersionJson(v));
|
|
346
|
-
}
|
|
347
|
-
/**
|
|
348
|
-
* Fetch metadata for a single version by id. Works against both the
|
|
349
|
-
* current row and any history row — the response's `isCurrent` tells
|
|
350
|
-
* you which.
|
|
351
|
-
*/
|
|
352
|
-
async getVersion(bucket, path, versionId) {
|
|
353
|
-
const url = `${this.config.baseUrl}/v1/sdk/storage/object-versions/${encodeURIComponent(versionId)}` +
|
|
354
|
-
`?bucket=${encodeURIComponent(bucket)}&path=${encodeURIComponent(path)}`;
|
|
355
|
-
const res = await fetch(url, { headers: await this.buildHeaders() });
|
|
356
|
-
if (!res.ok) {
|
|
357
|
-
throw await this.error(res, 'Failed to fetch version');
|
|
358
|
-
}
|
|
359
|
-
return fromVersionJson((await res.json()));
|
|
360
|
-
}
|
|
361
|
-
/**
|
|
362
|
-
* Bring a history version back as the current version. The
|
|
363
|
-
* previously-current row (if any) is snapshotted into history first,
|
|
364
|
-
* so this operation is itself a versioned event you can undo. The
|
|
365
|
-
* restored row gets a freshly-minted version_id; the target stays in
|
|
366
|
-
* history at its original version_id.
|
|
367
|
-
*
|
|
368
|
-
* Throws if the bucket has versioning off, if the target is the
|
|
369
|
-
* already-current version, or if the target is a delete marker.
|
|
370
|
-
*/
|
|
371
|
-
async restoreVersion(bucket, path, versionId) {
|
|
372
|
-
const url = `${this.config.baseUrl}/v1/sdk/storage/object-versions/${encodeURIComponent(versionId)}/restore` +
|
|
373
|
-
`?bucket=${encodeURIComponent(bucket)}&path=${encodeURIComponent(path)}`;
|
|
374
|
-
const res = await fetch(url, {
|
|
375
|
-
method: 'POST',
|
|
376
|
-
headers: await this.buildHeaders(),
|
|
377
|
-
});
|
|
378
|
-
if (!res.ok) {
|
|
379
|
-
throw await this.error(res, 'Failed to restore version');
|
|
380
|
-
}
|
|
381
|
-
return mapObjectFromServer(await res.json());
|
|
382
|
-
}
|
|
383
|
-
/**
|
|
384
|
-
* Hard-remove a single history version — both the metadata row and
|
|
385
|
-
* the .versions/ R2 bytes (or just the row, for delete markers).
|
|
386
|
-
* Refuses to operate on the current version; use {@link delete} with
|
|
387
|
-
* `forcePurge: true` to wipe everything for a path.
|
|
388
|
-
*/
|
|
389
|
-
async purgeVersion(bucket, path, versionId) {
|
|
390
|
-
const url = `${this.config.baseUrl}/v1/sdk/storage/object-versions/${encodeURIComponent(versionId)}` +
|
|
391
|
-
`?bucket=${encodeURIComponent(bucket)}&path=${encodeURIComponent(path)}`;
|
|
392
|
-
const res = await fetch(url, {
|
|
393
|
-
method: 'DELETE',
|
|
394
|
-
headers: await this.buildHeaders(),
|
|
395
|
-
});
|
|
396
|
-
if (res.status === 204)
|
|
397
|
-
return;
|
|
398
|
-
if (!res.ok) {
|
|
399
|
-
throw await this.error(res, 'Failed to purge version');
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
exports.KoolbaseStorage = KoolbaseStorage;
|
|
404
|
-
/**
|
|
405
|
-
* Maps the snake_case server JSON to the camelCase {@link KoolbaseObject}.
|
|
406
|
-
* Defensive: missing or null `metadata` (older / non-Koolbase responses)
|
|
407
|
-
* is coerced to an empty object so callers always see a typed
|
|
408
|
-
* `Record<string, string>` rather than null.
|
|
409
|
-
*/
|
|
410
|
-
function mapObjectFromServer(raw) {
|
|
411
|
-
return {
|
|
412
|
-
id: raw.id,
|
|
413
|
-
projectId: raw.project_id,
|
|
414
|
-
bucketId: raw.bucket_id,
|
|
415
|
-
userId: raw.user_id ?? null,
|
|
416
|
-
path: raw.path,
|
|
417
|
-
size: raw.size ?? 0,
|
|
418
|
-
contentType: raw.content_type ?? null,
|
|
419
|
-
metadata: raw.metadata ?? {},
|
|
420
|
-
r2Bucket: raw.r2_bucket ?? 'koolbase-storage',
|
|
421
|
-
createdAt: raw.created_at,
|
|
422
|
-
updatedAt: raw.updated_at,
|
|
423
|
-
};
|
|
424
|
-
}
|
|
425
|
-
/**
|
|
426
|
-
* Maps the snake_case server JSON of a version row to the camelCase
|
|
427
|
-
* {@link KoolbaseObjectVersion}. Mirrors `fromObjectJson` shape.
|
|
428
|
-
*/
|
|
429
|
-
function fromVersionJson(j) {
|
|
430
|
-
const rawMeta = j.metadata;
|
|
431
|
-
const metadata = {};
|
|
432
|
-
if (rawMeta && typeof rawMeta === 'object') {
|
|
433
|
-
for (const [k, v] of Object.entries(rawMeta)) {
|
|
434
|
-
if (typeof v === 'string')
|
|
435
|
-
metadata[k] = v;
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
return {
|
|
439
|
-
versionId: j.version_id ?? null,
|
|
440
|
-
path: j.path,
|
|
441
|
-
size: Number(j.size ?? 0),
|
|
442
|
-
contentType: j.content_type ?? null,
|
|
443
|
-
etag: j.etag ?? null,
|
|
444
|
-
metadata,
|
|
445
|
-
r2Bucket: j.r2_bucket ?? '',
|
|
446
|
-
userId: j.user_id ?? null,
|
|
447
|
-
isDeleteMarker: Boolean(j.is_delete_marker),
|
|
448
|
-
isCurrent: Boolean(j.is_current),
|
|
449
|
-
createdAt: j.created_at,
|
|
450
|
-
};
|
|
451
|
-
}
|
package/dist/sync-engine.d.ts
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { KoolbaseConfig } from './types';
|
|
2
|
-
type SyncCallback = () => void;
|
|
3
|
-
export declare class SyncEngine {
|
|
4
|
-
private config;
|
|
5
|
-
private getUserId;
|
|
6
|
-
private getToken;
|
|
7
|
-
private onSyncComplete?;
|
|
8
|
-
/**
|
|
9
|
-
* Called when the server rejects the caller's credentials during replay.
|
|
10
|
-
*
|
|
11
|
-
* Background sync is the likeliest place to meet a dead session: the queue
|
|
12
|
-
* replays writes made long before the session stopped being honoured.
|
|
13
|
-
*/
|
|
14
|
-
private onSessionExpired?;
|
|
15
|
-
private unsubscribe?;
|
|
16
|
-
private isSyncing;
|
|
17
|
-
constructor(config: KoolbaseConfig, getUserId: () => string | null, getToken: () => Promise<string | null>, onSyncComplete?: SyncCallback, onSessionExpired?: () => Promise<void>);
|
|
18
|
-
start(): void;
|
|
19
|
-
stop(): void;
|
|
20
|
-
flush(): Promise<void>;
|
|
21
|
-
/**
|
|
22
|
-
* Sends one queued write, returning the revision the record now carries.
|
|
23
|
-
*
|
|
24
|
-
* The revision matters to whatever is queued behind this for the same record:
|
|
25
|
-
* those were composed against this one's result and cannot know its revision
|
|
26
|
-
* until the server assigns it.
|
|
27
|
-
*/
|
|
28
|
-
private executeWrite;
|
|
29
|
-
}
|
|
30
|
-
export {};
|