@techfinityedge/koolbase-react-native 5.1.1 → 5.2.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/storage-errors.d.ts +40 -0
- package/dist/storage-errors.js +43 -1
- package/dist/storage.d.ts +42 -1
- package/dist/storage.js +75 -8
- package/dist/types.d.ts +22 -0
- package/package.json +1 -1
package/dist/storage-errors.d.ts
CHANGED
|
@@ -103,6 +103,46 @@ export declare class KoolbaseStorageFileTooLargeError extends KoolbaseStorageErr
|
|
|
103
103
|
export declare class KoolbaseStorageMimeTypeError extends KoolbaseStorageError {
|
|
104
104
|
constructor(message?: string);
|
|
105
105
|
}
|
|
106
|
+
/**
|
|
107
|
+
* Thrown when an object metadata payload (either at upload-confirm time
|
|
108
|
+
* or via `updateMetadata`) fails server-side validation — the server
|
|
109
|
+
* responds with 400 and code `metadata_invalid`.
|
|
110
|
+
*
|
|
111
|
+
* The `detail` field carries the specific reason from the server — e.g.
|
|
112
|
+
* `'key "foo bar": must match [a-z0-9_]+'`, `'exceeds 50 keys (got 53)'`,
|
|
113
|
+
* or `'exceeds 8192 bytes total (sum of all key + value lengths)'`. The
|
|
114
|
+
* detail names the failing key and rule so callers can fix the offending
|
|
115
|
+
* entry without guessing what shape rule was violated.
|
|
116
|
+
*
|
|
117
|
+
* Validation rules (enforced server-side):
|
|
118
|
+
* - At most 50 keys per object.
|
|
119
|
+
* - At most 8KB total (sum of byte lengths across all keys + values).
|
|
120
|
+
* - Keys: 1–64 chars, must match `[a-z0-9_]+`.
|
|
121
|
+
* - Keys with a leading underscore are reserved for system use.
|
|
122
|
+
* - Values: at most 1024 chars each.
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* try {
|
|
126
|
+
* await Koolbase.storage.updateMetadata('photos', 'sunset.jpg', {
|
|
127
|
+
* tag: 'sunset',
|
|
128
|
+
* 'BAD KEY': 'oops',
|
|
129
|
+
* });
|
|
130
|
+
* } catch (e) {
|
|
131
|
+
* if (e instanceof KoolbaseStorageMetadataInvalidError) {
|
|
132
|
+
* console.warn('Metadata rejected:', e.detail);
|
|
133
|
+
* // -> 'Metadata rejected: key "BAD KEY": must match [a-z0-9_]+'
|
|
134
|
+
* }
|
|
135
|
+
* }
|
|
136
|
+
*/
|
|
137
|
+
export declare class KoolbaseStorageMetadataInvalidError extends KoolbaseStorageError {
|
|
138
|
+
/**
|
|
139
|
+
* The specific validation failure reported by the server. Names the
|
|
140
|
+
* failing key (when applicable) and the rule that was violated.
|
|
141
|
+
* Surface this directly to developer logs or user-facing UI.
|
|
142
|
+
*/
|
|
143
|
+
detail?: string;
|
|
144
|
+
constructor(message?: string, detail?: string);
|
|
145
|
+
}
|
|
106
146
|
/**
|
|
107
147
|
* Maps a non-2xx storage-layer response to a typed
|
|
108
148
|
* {@link KoolbaseStorageError}, preferring the server's stable `code` and
|
package/dist/storage-errors.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.KoolbaseStorageMimeTypeError = exports.KoolbaseStorageFileTooLargeError = exports.KoolbaseStorageQuotaError = exports.KoolbaseStoragePermissionError = exports.KoolbaseStorageValidationError = exports.KoolbaseStorageNotFoundError = exports.KoolbaseStorageConflictError = exports.KoolbaseStorageError = void 0;
|
|
3
|
+
exports.KoolbaseStorageMetadataInvalidError = exports.KoolbaseStorageMimeTypeError = exports.KoolbaseStorageFileTooLargeError = exports.KoolbaseStorageQuotaError = exports.KoolbaseStoragePermissionError = exports.KoolbaseStorageValidationError = exports.KoolbaseStorageNotFoundError = exports.KoolbaseStorageConflictError = exports.KoolbaseStorageError = void 0;
|
|
4
4
|
exports.koolbaseStorageError = koolbaseStorageError;
|
|
5
5
|
exports.koolbaseStorageErrorFromResponse = koolbaseStorageErrorFromResponse;
|
|
6
6
|
/**
|
|
@@ -148,6 +148,46 @@ class KoolbaseStorageMimeTypeError extends KoolbaseStorageError {
|
|
|
148
148
|
}
|
|
149
149
|
}
|
|
150
150
|
exports.KoolbaseStorageMimeTypeError = KoolbaseStorageMimeTypeError;
|
|
151
|
+
/**
|
|
152
|
+
* Thrown when an object metadata payload (either at upload-confirm time
|
|
153
|
+
* or via `updateMetadata`) fails server-side validation — the server
|
|
154
|
+
* responds with 400 and code `metadata_invalid`.
|
|
155
|
+
*
|
|
156
|
+
* The `detail` field carries the specific reason from the server — e.g.
|
|
157
|
+
* `'key "foo bar": must match [a-z0-9_]+'`, `'exceeds 50 keys (got 53)'`,
|
|
158
|
+
* or `'exceeds 8192 bytes total (sum of all key + value lengths)'`. The
|
|
159
|
+
* detail names the failing key and rule so callers can fix the offending
|
|
160
|
+
* entry without guessing what shape rule was violated.
|
|
161
|
+
*
|
|
162
|
+
* Validation rules (enforced server-side):
|
|
163
|
+
* - At most 50 keys per object.
|
|
164
|
+
* - At most 8KB total (sum of byte lengths across all keys + values).
|
|
165
|
+
* - Keys: 1–64 chars, must match `[a-z0-9_]+`.
|
|
166
|
+
* - Keys with a leading underscore are reserved for system use.
|
|
167
|
+
* - Values: at most 1024 chars each.
|
|
168
|
+
*
|
|
169
|
+
* @example
|
|
170
|
+
* try {
|
|
171
|
+
* await Koolbase.storage.updateMetadata('photos', 'sunset.jpg', {
|
|
172
|
+
* tag: 'sunset',
|
|
173
|
+
* 'BAD KEY': 'oops',
|
|
174
|
+
* });
|
|
175
|
+
* } catch (e) {
|
|
176
|
+
* if (e instanceof KoolbaseStorageMetadataInvalidError) {
|
|
177
|
+
* console.warn('Metadata rejected:', e.detail);
|
|
178
|
+
* // -> 'Metadata rejected: key "BAD KEY": must match [a-z0-9_]+'
|
|
179
|
+
* }
|
|
180
|
+
* }
|
|
181
|
+
*/
|
|
182
|
+
class KoolbaseStorageMetadataInvalidError extends KoolbaseStorageError {
|
|
183
|
+
constructor(message, detail) {
|
|
184
|
+
super(message ?? 'Metadata payload is invalid', 'metadata_invalid');
|
|
185
|
+
this.detail = detail;
|
|
186
|
+
this.name = 'KoolbaseStorageMetadataInvalidError';
|
|
187
|
+
Object.setPrototypeOf(this, KoolbaseStorageMetadataInvalidError.prototype);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
exports.KoolbaseStorageMetadataInvalidError = KoolbaseStorageMetadataInvalidError;
|
|
151
191
|
/**
|
|
152
192
|
* Maps a non-2xx storage-layer response to a typed
|
|
153
193
|
* {@link KoolbaseStorageError}, preferring the server's stable `code` and
|
|
@@ -173,6 +213,8 @@ function koolbaseStorageError(status, body, fallbackMessage = 'Storage request f
|
|
|
173
213
|
return new KoolbaseStorageFileTooLargeError(message);
|
|
174
214
|
case 'mime_not_allowed':
|
|
175
215
|
return new KoolbaseStorageMimeTypeError(message);
|
|
216
|
+
case 'metadata_invalid':
|
|
217
|
+
return new KoolbaseStorageMetadataInvalidError(message, body?.detail);
|
|
176
218
|
}
|
|
177
219
|
// ─── status fallback (pre-code servers or uncoded paths) ───
|
|
178
220
|
switch (status) {
|
package/dist/storage.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { KoolbaseConfig, UploadOptions, UploadResult } from './types';
|
|
1
|
+
import { KoolbaseConfig, UploadOptions, UploadResult, KoolbaseObject } from './types';
|
|
2
2
|
/**
|
|
3
3
|
* Koolbase storage client — uploads, downloads, and deletes via presigned
|
|
4
4
|
* Cloudflare R2 URLs.
|
|
@@ -23,11 +23,52 @@ export declare class KoolbaseStorage {
|
|
|
23
23
|
* Set `overwrite: true` for true upsert semantics — silently replace any
|
|
24
24
|
* existing object at this path.
|
|
25
25
|
*
|
|
26
|
+
* Pass `options.metadata` to attach arbitrary user-defined key/value pairs
|
|
27
|
+
* to the object at confirm time. Subject to the limits documented on
|
|
28
|
+
* {@link KoolbaseObject.metadata}; violations throw
|
|
29
|
+
* `KoolbaseStorageMetadataInvalidError`. On the `overwrite: true` path the
|
|
30
|
+
* metadata REPLACES any prior metadata at this path (matches GCS semantics).
|
|
31
|
+
* Use {@link updateMetadata} for post-upload merge changes.
|
|
32
|
+
*
|
|
26
33
|
* **Breaking change in v5.0.0**: the default flipped from silent overwrite
|
|
27
34
|
* (legacy behavior) to safe-by-default. If you previously relied on uploads
|
|
28
35
|
* overwriting silently, pass `overwrite: true` explicitly.
|
|
29
36
|
*/
|
|
30
37
|
upload(options: UploadOptions): Promise<UploadResult>;
|
|
38
|
+
/**
|
|
39
|
+
* Apply a partial metadata update to an existing object. Returns the
|
|
40
|
+
* post-update {@link KoolbaseObject} with the merged metadata.
|
|
41
|
+
*
|
|
42
|
+
* **Merge semantics** (mirrors the server's JSONB merge):
|
|
43
|
+
*
|
|
44
|
+
* - Keys with a non-null string value are SET — added if missing,
|
|
45
|
+
* replacing any existing value at the key otherwise.
|
|
46
|
+
* - Keys with `null` are DELETED from the stored metadata.
|
|
47
|
+
* - Keys ABSENT from `metadata` are untouched — pre-existing entries
|
|
48
|
+
* for those keys remain unchanged.
|
|
49
|
+
*
|
|
50
|
+
* Validation runs server-side against the same rules as upload-time
|
|
51
|
+
* metadata; violations throw `KoolbaseStorageMetadataInvalidError`,
|
|
52
|
+
* whose `detail` field names the failing key and rule. The check is
|
|
53
|
+
* performed against the projected post-merge state, so adding a key
|
|
54
|
+
* that would push the object past the 50-key or 8KB ceiling is
|
|
55
|
+
* rejected before the row is mutated.
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* // Add a tag, update an existing key, and drop another in one call:
|
|
59
|
+
* const updated = await Koolbase.storage.updateMetadata(
|
|
60
|
+
* 'photos',
|
|
61
|
+
* 'sunset.jpg',
|
|
62
|
+
* {
|
|
63
|
+
* category: 'landscape', // SET or UPDATE
|
|
64
|
+
* tag: 'sunset', // SET or UPDATE
|
|
65
|
+
* owner: null, // DELETE
|
|
66
|
+
* }
|
|
67
|
+
* );
|
|
68
|
+
* console.log(updated.metadata);
|
|
69
|
+
* // -> { category: 'landscape', tag: 'sunset' }
|
|
70
|
+
*/
|
|
71
|
+
updateMetadata(bucket: string, path: string, metadata: Record<string, string | null>): Promise<KoolbaseObject>;
|
|
31
72
|
/**
|
|
32
73
|
* Get a signed download URL for a file.
|
|
33
74
|
*/
|
package/dist/storage.js
CHANGED
|
@@ -33,6 +33,13 @@ class KoolbaseStorage {
|
|
|
33
33
|
* Set `overwrite: true` for true upsert semantics — silently replace any
|
|
34
34
|
* existing object at this path.
|
|
35
35
|
*
|
|
36
|
+
* Pass `options.metadata` to attach arbitrary user-defined key/value pairs
|
|
37
|
+
* to the object at confirm time. Subject to the limits documented on
|
|
38
|
+
* {@link KoolbaseObject.metadata}; violations throw
|
|
39
|
+
* `KoolbaseStorageMetadataInvalidError`. On the `overwrite: true` path the
|
|
40
|
+
* metadata REPLACES any prior metadata at this path (matches GCS semantics).
|
|
41
|
+
* Use {@link updateMetadata} for post-upload merge changes.
|
|
42
|
+
*
|
|
36
43
|
* **Breaking change in v5.0.0**: the default flipped from silent overwrite
|
|
37
44
|
* (legacy behavior) to safe-by-default. If you previously relied on uploads
|
|
38
45
|
* overwriting silently, pass `overwrite: true` explicitly.
|
|
@@ -76,20 +83,28 @@ class KoolbaseStorage {
|
|
|
76
83
|
}
|
|
77
84
|
const etag = uploadRes.headers.get('etag') ?? '';
|
|
78
85
|
// ─── Step 3: Confirm upload ───
|
|
86
|
+
// Build the body conditionally so the `metadata` field is only sent
|
|
87
|
+
// when the caller passed it — keeps the wire shape clean for callers
|
|
88
|
+
// that don't care, and lets the server's omitempty path treat absent
|
|
89
|
+
// as "no metadata."
|
|
90
|
+
const confirmBody = {
|
|
91
|
+
bucket: options.bucket,
|
|
92
|
+
path: options.path,
|
|
93
|
+
size: fileSize,
|
|
94
|
+
content_type: contentType,
|
|
95
|
+
etag,
|
|
96
|
+
overwrite,
|
|
97
|
+
};
|
|
98
|
+
if (options.metadata !== undefined) {
|
|
99
|
+
confirmBody.metadata = options.metadata;
|
|
100
|
+
}
|
|
79
101
|
const confirmRes = await fetch(`${this.config.baseUrl}/v1/sdk/storage/confirm`, {
|
|
80
102
|
method: 'POST',
|
|
81
103
|
headers: {
|
|
82
104
|
...(await this.buildHeaders()),
|
|
83
105
|
'Content-Type': 'application/json',
|
|
84
106
|
},
|
|
85
|
-
body: JSON.stringify(
|
|
86
|
-
bucket: options.bucket,
|
|
87
|
-
path: options.path,
|
|
88
|
-
size: fileSize,
|
|
89
|
-
content_type: contentType,
|
|
90
|
-
etag,
|
|
91
|
-
overwrite,
|
|
92
|
-
}),
|
|
107
|
+
body: JSON.stringify(confirmBody),
|
|
93
108
|
});
|
|
94
109
|
if (!confirmRes.ok) {
|
|
95
110
|
throw await (0, storage_errors_1.koolbaseStorageErrorFromResponse)(confirmRes, 'Failed to confirm upload');
|
|
@@ -100,6 +115,54 @@ class KoolbaseStorage {
|
|
|
100
115
|
const downloadUrl = await this.getDownloadUrl(options.bucket, options.path);
|
|
101
116
|
return { object, downloadUrl };
|
|
102
117
|
}
|
|
118
|
+
/**
|
|
119
|
+
* Apply a partial metadata update to an existing object. Returns the
|
|
120
|
+
* post-update {@link KoolbaseObject} with the merged metadata.
|
|
121
|
+
*
|
|
122
|
+
* **Merge semantics** (mirrors the server's JSONB merge):
|
|
123
|
+
*
|
|
124
|
+
* - Keys with a non-null string value are SET — added if missing,
|
|
125
|
+
* replacing any existing value at the key otherwise.
|
|
126
|
+
* - Keys with `null` are DELETED from the stored metadata.
|
|
127
|
+
* - Keys ABSENT from `metadata` are untouched — pre-existing entries
|
|
128
|
+
* for those keys remain unchanged.
|
|
129
|
+
*
|
|
130
|
+
* Validation runs server-side against the same rules as upload-time
|
|
131
|
+
* metadata; violations throw `KoolbaseStorageMetadataInvalidError`,
|
|
132
|
+
* whose `detail` field names the failing key and rule. The check is
|
|
133
|
+
* performed against the projected post-merge state, so adding a key
|
|
134
|
+
* that would push the object past the 50-key or 8KB ceiling is
|
|
135
|
+
* rejected before the row is mutated.
|
|
136
|
+
*
|
|
137
|
+
* @example
|
|
138
|
+
* // Add a tag, update an existing key, and drop another in one call:
|
|
139
|
+
* const updated = await Koolbase.storage.updateMetadata(
|
|
140
|
+
* 'photos',
|
|
141
|
+
* 'sunset.jpg',
|
|
142
|
+
* {
|
|
143
|
+
* category: 'landscape', // SET or UPDATE
|
|
144
|
+
* tag: 'sunset', // SET or UPDATE
|
|
145
|
+
* owner: null, // DELETE
|
|
146
|
+
* }
|
|
147
|
+
* );
|
|
148
|
+
* console.log(updated.metadata);
|
|
149
|
+
* // -> { category: 'landscape', tag: 'sunset' }
|
|
150
|
+
*/
|
|
151
|
+
async updateMetadata(bucket, path, metadata) {
|
|
152
|
+
const res = await fetch(`${this.config.baseUrl}/v1/sdk/storage/objects/metadata`, {
|
|
153
|
+
method: 'PATCH',
|
|
154
|
+
headers: {
|
|
155
|
+
...(await this.buildHeaders()),
|
|
156
|
+
'Content-Type': 'application/json',
|
|
157
|
+
},
|
|
158
|
+
body: JSON.stringify({ bucket, path, metadata }),
|
|
159
|
+
});
|
|
160
|
+
if (!res.ok) {
|
|
161
|
+
throw await (0, storage_errors_1.koolbaseStorageErrorFromResponse)(res, 'Failed to update metadata');
|
|
162
|
+
}
|
|
163
|
+
const raw = await res.json();
|
|
164
|
+
return mapObjectFromServer(raw);
|
|
165
|
+
}
|
|
103
166
|
/**
|
|
104
167
|
* Get a signed download URL for a file.
|
|
105
168
|
*/
|
|
@@ -135,6 +198,9 @@ class KoolbaseStorage {
|
|
|
135
198
|
exports.KoolbaseStorage = KoolbaseStorage;
|
|
136
199
|
/**
|
|
137
200
|
* Maps the snake_case server JSON to the camelCase {@link KoolbaseObject}.
|
|
201
|
+
* Defensive: missing or null `metadata` (older / non-Koolbase responses)
|
|
202
|
+
* is coerced to an empty object so callers always see a typed
|
|
203
|
+
* `Record<string, string>` rather than null.
|
|
138
204
|
*/
|
|
139
205
|
function mapObjectFromServer(raw) {
|
|
140
206
|
return {
|
|
@@ -145,6 +211,7 @@ function mapObjectFromServer(raw) {
|
|
|
145
211
|
path: raw.path,
|
|
146
212
|
size: raw.size ?? 0,
|
|
147
213
|
contentType: raw.content_type ?? null,
|
|
214
|
+
metadata: raw.metadata ?? {},
|
|
148
215
|
createdAt: raw.created_at,
|
|
149
216
|
updatedAt: raw.updated_at,
|
|
150
217
|
};
|
package/dist/types.d.ts
CHANGED
|
@@ -161,6 +161,20 @@ export interface UploadOptions {
|
|
|
161
161
|
* `true` to silently replace the existing object.
|
|
162
162
|
*/
|
|
163
163
|
overwrite?: boolean;
|
|
164
|
+
/**
|
|
165
|
+
* User-defined key/value metadata to attach to the object at confirm
|
|
166
|
+
* time. Optional — when omitted, the object stores empty metadata `{}`.
|
|
167
|
+
*
|
|
168
|
+
* Subject to server-side validation (≤50 keys, ≤8KB total, keys 1–64
|
|
169
|
+
* chars matching `[a-z0-9_]+`, values ≤1024 chars, leading underscore
|
|
170
|
+
* reserved); violations throw `KoolbaseStorageMetadataInvalidError`.
|
|
171
|
+
*
|
|
172
|
+
* On the `overwrite: true` path, metadata REPLACES any prior metadata
|
|
173
|
+
* at this path (matches GCS semantics — a new upload at a path
|
|
174
|
+
* produces a new object, not a patch of the old). Use `updateMetadata`
|
|
175
|
+
* for post-upload merge changes.
|
|
176
|
+
*/
|
|
177
|
+
metadata?: Record<string, string>;
|
|
164
178
|
onProgress?: (percent: number) => void;
|
|
165
179
|
}
|
|
166
180
|
/**
|
|
@@ -175,6 +189,14 @@ export interface KoolbaseObject {
|
|
|
175
189
|
path: string;
|
|
176
190
|
size: number;
|
|
177
191
|
contentType: string | null;
|
|
192
|
+
/**
|
|
193
|
+
* User-defined key/value metadata attached to this object. Always
|
|
194
|
+
* non-null — empty object when no metadata has been set (the server
|
|
195
|
+
* returns `{}` rather than `null` so callers can treat it as a
|
|
196
|
+
* guaranteed object without null checks). Set on upload via
|
|
197
|
+
* `upload({ metadata })` or mutated post-upload via `updateMetadata`.
|
|
198
|
+
*/
|
|
199
|
+
metadata: Record<string, string>;
|
|
178
200
|
/** ISO 8601 timestamp from the server. */
|
|
179
201
|
createdAt: string;
|
|
180
202
|
/** ISO 8601 timestamp from the server. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@techfinityedge/koolbase-react-native",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.2.0",
|
|
4
4
|
"description": "React Native SDK for Koolbase — auth, database, storage, realtime, feature flags, and functions in one package.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|