@koolbase/react-native 9.2.0 → 10.0.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.
Files changed (69) hide show
  1. package/CHANGELOG.md +1342 -0
  2. package/README.md +403 -568
  3. package/dist/{auth-storage.d.ts → cjs/auth-storage.d.ts} +1 -1
  4. package/dist/cjs/index.d.ts +19 -0
  5. package/dist/cjs/index.js +125 -0
  6. package/dist/cjs/package.json +3 -0
  7. package/dist/cjs/platform.d.ts +2 -0
  8. package/dist/cjs/platform.js +43 -0
  9. package/dist/esm/auth-storage.d.ts +26 -0
  10. package/dist/esm/auth-storage.js +100 -0
  11. package/dist/esm/index.d.ts +19 -0
  12. package/dist/esm/index.js +106 -0
  13. package/dist/esm/package.json +3 -0
  14. package/dist/esm/platform.d.ts +2 -0
  15. package/dist/esm/platform.js +37 -0
  16. package/package.json +30 -30
  17. package/dist/analytics.d.ts +0 -24
  18. package/dist/analytics.js +0 -114
  19. package/dist/apple-auth.d.ts +0 -22
  20. package/dist/apple-auth.js +0 -74
  21. package/dist/auth-errors.d.ts +0 -117
  22. package/dist/auth-errors.js +0 -250
  23. package/dist/auth.d.ts +0 -213
  24. package/dist/auth.js +0 -810
  25. package/dist/cache-store.d.ts +0 -50
  26. package/dist/cache-store.js +0 -197
  27. package/dist/code-push.d.ts +0 -59
  28. package/dist/code-push.js +0 -255
  29. package/dist/conflict.d.ts +0 -80
  30. package/dist/conflict.js +0 -84
  31. package/dist/database-errors.d.ts +0 -101
  32. package/dist/database-errors.js +0 -200
  33. package/dist/database.d.ts +0 -298
  34. package/dist/database.js +0 -852
  35. package/dist/device-id.d.ts +0 -1
  36. package/dist/device-id.js +0 -60
  37. package/dist/device-metadata.d.ts +0 -36
  38. package/dist/device-metadata.js +0 -102
  39. package/dist/errors.d.ts +0 -64
  40. package/dist/errors.js +0 -85
  41. package/dist/flags.d.ts +0 -15
  42. package/dist/flags.js +0 -76
  43. package/dist/function-errors.d.ts +0 -51
  44. package/dist/function-errors.js +0 -103
  45. package/dist/functions.d.ts +0 -15
  46. package/dist/functions.js +0 -83
  47. package/dist/index.d.ts +0 -49
  48. package/dist/index.js +0 -204
  49. package/dist/logic-engine.d.ts +0 -17
  50. package/dist/logic-engine.js +0 -193
  51. package/dist/messaging.d.ts +0 -13
  52. package/dist/messaging.js +0 -36
  53. package/dist/offline-state.d.ts +0 -97
  54. package/dist/offline-state.js +0 -200
  55. package/dist/pending-write.d.ts +0 -47
  56. package/dist/pending-write.js +0 -22
  57. package/dist/realtime.d.ts +0 -44
  58. package/dist/realtime.js +0 -195
  59. package/dist/record.d.ts +0 -2
  60. package/dist/record.js +0 -23
  61. package/dist/storage-errors.d.ts +0 -163
  62. package/dist/storage-errors.js +0 -253
  63. package/dist/storage.d.ts +0 -198
  64. package/dist/storage.js +0 -451
  65. package/dist/sync-engine.d.ts +0 -30
  66. package/dist/sync-engine.js +0 -290
  67. package/dist/types.d.ts +0 -487
  68. package/dist/types.js +0 -40
  69. /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
- }
@@ -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 {};