hydrousdb 2.0.1 → 2.0.3

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/index.mjs CHANGED
@@ -1,14 +1,14 @@
1
1
  // src/utils/errors.ts
2
- var HydrousSDKError = class extends Error {
2
+ var HydrousDBError = class extends Error {
3
3
  constructor(message, code = "SDK_ERROR", status) {
4
4
  super(message);
5
- this.name = "HydrousSDKError";
5
+ this.name = "HydrousDBError";
6
6
  this.code = code;
7
7
  this.status = status;
8
8
  }
9
9
  };
10
10
  function toHydrousError(err) {
11
- if (err instanceof HydrousSDKError) {
11
+ if (err instanceof HydrousDBError) {
12
12
  return { message: err.message, code: err.code, status: err.status };
13
13
  }
14
14
  if (err instanceof Error) {
@@ -17,7 +17,7 @@ function toHydrousError(err) {
17
17
  return { message: String(err), code: "UNKNOWN_ERROR" };
18
18
  }
19
19
  function isHydrousError(err) {
20
- return err instanceof HydrousSDKError;
20
+ return err instanceof HydrousDBError;
21
21
  }
22
22
 
23
23
  // src/utils/http.ts
@@ -26,16 +26,14 @@ async function parseResponse(res) {
26
26
  try {
27
27
  body = await res.json();
28
28
  } catch (e) {
29
- if (!res.ok) {
30
- throw new HydrousSDKError(`HTTP ${res.status}`, "HTTP_ERROR", res.status);
31
- }
29
+ if (!res.ok) throw new HydrousDBError(`HTTP ${res.status}`, "HTTP_ERROR", res.status);
32
30
  return void 0;
33
31
  }
34
32
  if (!res.ok) {
35
- const err = body;
36
- throw new HydrousSDKError(
37
- err.error || err.message || `HTTP ${res.status}`,
38
- err.code || "HTTP_ERROR",
33
+ const e = body;
34
+ throw new HydrousDBError(
35
+ e.error || e.message || `HTTP ${res.status}`,
36
+ e.code || "HTTP_ERROR",
39
37
  res.status
40
38
  );
41
39
  }
@@ -45,15 +43,13 @@ function buildUrl(base, path, params) {
45
43
  const url = new URL(path, base.endsWith("/") ? base : base + "/");
46
44
  if (params) {
47
45
  for (const [k, v] of Object.entries(params)) {
48
- if (v !== void 0 && v !== null) {
49
- url.searchParams.set(k, String(v));
50
- }
46
+ if (v !== void 0 && v !== null) url.searchParams.set(k, String(v));
51
47
  }
52
48
  }
53
49
  return url.toString();
54
50
  }
55
- function mergeHeaders(defaults, overrides) {
56
- return { ...defaults, ...overrides };
51
+ function mergeHeaders(a, b) {
52
+ return { ...a, ...b };
57
53
  }
58
54
  async function readSSEStream(response, onEvent) {
59
55
  if (!response.body) return;
@@ -87,17 +83,32 @@ async function readSSEStream(response, onEvent) {
87
83
  }
88
84
  if (buf.trim()) flush("");
89
85
  }
90
- function xhrUpload(url, body, headers, onXhrProgress) {
86
+ function parseSSEText(text, onEvent) {
87
+ const blocks = text.split("\n\n");
88
+ for (const block of blocks) {
89
+ if (!block.trim()) continue;
90
+ let eventType = "message";
91
+ let dataLine = null;
92
+ for (const line of block.split("\n")) {
93
+ if (line.startsWith("event:")) eventType = line.slice(6).trim();
94
+ if (line.startsWith("data:")) dataLine = line.slice(5).trim();
95
+ }
96
+ if (dataLine === null) continue;
97
+ try {
98
+ onEvent(eventType, JSON.parse(dataLine));
99
+ } catch (e) {
100
+ }
101
+ }
102
+ }
103
+ function xhrUpload(url, body, headers, onProgress) {
91
104
  return new Promise((resolve, reject) => {
92
105
  const xhr = new XMLHttpRequest();
93
106
  xhr.open("POST", url);
94
- for (const [key, val] of Object.entries(headers)) {
95
- xhr.setRequestHeader(key, val);
96
- }
107
+ for (const [k, v] of Object.entries(headers)) xhr.setRequestHeader(k, v);
97
108
  xhr.responseType = "text";
98
- if (onXhrProgress) {
109
+ if (onProgress) {
99
110
  xhr.upload.onprogress = (e) => {
100
- if (e.lengthComputable) onXhrProgress(e.loaded, e.total);
111
+ if (e.lengthComputable) onProgress(e.loaded, e.total);
101
112
  };
102
113
  }
103
114
  xhr.onload = () => {
@@ -107,35 +118,18 @@ function xhrUpload(url, body, headers, onXhrProgress) {
107
118
  } else {
108
119
  try {
109
120
  const d = JSON.parse(xhr.responseText);
110
- reject(new HydrousSDKError((_a = d.error) != null ? _a : `HTTP ${xhr.status}`, "HTTP_ERROR", xhr.status));
121
+ reject(new HydrousDBError((_a = d.error) != null ? _a : `HTTP ${xhr.status}`, "HTTP_ERROR", xhr.status));
111
122
  } catch (e) {
112
- reject(new HydrousSDKError(`HTTP ${xhr.status}`, "HTTP_ERROR", xhr.status));
123
+ reject(new HydrousDBError(`HTTP ${xhr.status}`, "HTTP_ERROR", xhr.status));
113
124
  }
114
125
  }
115
126
  };
116
- xhr.onerror = () => reject(new HydrousSDKError("Network error", "NETWORK_ERROR"));
117
- xhr.onabort = () => reject(new HydrousSDKError("Upload aborted", "UPLOAD_ABORTED"));
118
- xhr.ontimeout = () => reject(new HydrousSDKError("Upload timed out", "UPLOAD_TIMEOUT"));
127
+ xhr.onerror = () => reject(new HydrousDBError("Network error", "NETWORK_ERROR"));
128
+ xhr.onabort = () => reject(new HydrousDBError("Upload aborted", "UPLOAD_ABORTED"));
129
+ xhr.ontimeout = () => reject(new HydrousDBError("Upload timed out", "UPLOAD_TIMEOUT"));
119
130
  xhr.send(body);
120
131
  });
121
132
  }
122
- function parseSSEText(text, onEvent) {
123
- const blocks = text.split("\n\n");
124
- for (const block of blocks) {
125
- if (!block.trim()) continue;
126
- let eventType = "message";
127
- let dataLine = null;
128
- for (const line of block.split("\n")) {
129
- if (line.startsWith("event:")) eventType = line.slice(6).trim();
130
- if (line.startsWith("data:")) dataLine = line.slice(5).trim();
131
- }
132
- if (dataLine === null) continue;
133
- try {
134
- onEvent(eventType, JSON.parse(dataLine));
135
- } catch (e) {
136
- }
137
- }
138
- }
139
133
 
140
134
  // src/auth/client.ts
141
135
  var AuthClient = class {
@@ -144,23 +138,13 @@ var AuthClient = class {
144
138
  this.baseUrl = config.url;
145
139
  this.headers = {
146
140
  "Content-Type": "application/json",
147
- "Authorization": `Bearer ${config.apiKey}`
141
+ "Authorization": `Bearer ${config.authKey}`
148
142
  };
149
143
  }
150
- // ─── SIGN UP ───────────────────────────────────────────────────────────────
151
- /**
152
- * Create a new user account and return a session.
153
- *
154
- * @example
155
- * const { data, error } = await hydrous.auth.signUp({
156
- * email: 'user@example.com',
157
- * password: 'supersecret',
158
- * });
159
- */
144
+ /** Create a new user account */
160
145
  async signUp(options) {
161
146
  try {
162
- const url = buildUrl(this.baseUrl, "auth/signup");
163
- const res = await fetch(url, {
147
+ const res = await fetch(buildUrl(this.baseUrl, "auth/signup"), {
164
148
  method: "POST",
165
149
  headers: this.headers,
166
150
  body: JSON.stringify(options)
@@ -172,21 +156,10 @@ var AuthClient = class {
172
156
  return { data: null, error: toHydrousError(err) };
173
157
  }
174
158
  }
175
- // ─── SIGN IN ───────────────────────────────────────────────────────────────
176
- /**
177
- * Sign in with email and password.
178
- *
179
- * @example
180
- * const { data, error } = await hydrous.auth.signIn({
181
- * email: 'user@example.com',
182
- * password: 'supersecret',
183
- * });
184
- * if (data) console.log('Signed in as', data.user.email);
185
- */
159
+ /** Sign in with email and password */
186
160
  async signIn(options) {
187
161
  try {
188
- const url = buildUrl(this.baseUrl, "auth/signin");
189
- const res = await fetch(url, {
162
+ const res = await fetch(buildUrl(this.baseUrl, "auth/signin"), {
190
163
  method: "POST",
191
164
  headers: this.headers,
192
165
  body: JSON.stringify(options)
@@ -198,14 +171,10 @@ var AuthClient = class {
198
171
  return { data: null, error: toHydrousError(err) };
199
172
  }
200
173
  }
201
- // ─── SIGN OUT ──────────────────────────────────────────────────────────────
202
- /**
203
- * Sign out the current user and invalidate their session.
204
- */
174
+ /** Sign out and invalidate the current session */
205
175
  async signOut() {
206
176
  try {
207
- const url = buildUrl(this.baseUrl, "auth/signout");
208
- const res = await fetch(url, {
177
+ const res = await fetch(buildUrl(this.baseUrl, "auth/signout"), {
209
178
  method: "POST",
210
179
  headers: mergeHeaders(this.headers, this._sessionHeader())
211
180
  });
@@ -216,12 +185,10 @@ var AuthClient = class {
216
185
  return { data: null, error: toHydrousError(err) };
217
186
  }
218
187
  }
219
- // ─── GET USER ──────────────────────────────────────────────────────────────
220
- /** Return the currently authenticated user, or null if not signed in. */
188
+ /** Get the currently authenticated user */
221
189
  async getUser() {
222
190
  try {
223
- const url = buildUrl(this.baseUrl, "auth/user");
224
- const res = await fetch(url, {
191
+ const res = await fetch(buildUrl(this.baseUrl, "auth/user"), {
225
192
  headers: mergeHeaders(this.headers, this._sessionHeader())
226
193
  });
227
194
  const json = await parseResponse(res);
@@ -230,19 +197,14 @@ var AuthClient = class {
230
197
  return { data: null, error: toHydrousError(err) };
231
198
  }
232
199
  }
233
- // ─── REFRESH TOKEN ────────────────────────────────────────────────────────
234
- /**
235
- * Refresh the access token using the stored refresh token.
236
- * Called automatically by the SDK when a 401 is received.
237
- */
200
+ /** Refresh the access token using the stored refresh token */
238
201
  async refreshSession() {
239
202
  var _a;
240
203
  if (!((_a = this.session) == null ? void 0 : _a.refreshToken)) {
241
- return { data: null, error: { message: "No session", code: "NO_SESSION" } };
204
+ return { data: null, error: { message: "No active session", code: "NO_SESSION" } };
242
205
  }
243
206
  try {
244
- const url = buildUrl(this.baseUrl, "auth/refresh");
245
- const res = await fetch(url, {
207
+ const res = await fetch(buildUrl(this.baseUrl, "auth/refresh"), {
246
208
  method: "POST",
247
209
  headers: this.headers,
248
210
  body: JSON.stringify({ refreshToken: this.session.refreshToken })
@@ -254,7 +216,7 @@ var AuthClient = class {
254
216
  return { data: null, error: toHydrousError(err) };
255
217
  }
256
218
  }
257
- /** Return the current in-memory session (may be null). */
219
+ /** Return the current in-memory session (may be null) */
258
220
  getSession() {
259
221
  return this.session;
260
222
  }
@@ -270,34 +232,22 @@ function serialiseQuery(opts = {}) {
270
232
  const params = {};
271
233
  if (opts.limit !== void 0) params["limit"] = String(opts.limit);
272
234
  if (opts.offset !== void 0) params["offset"] = String(opts.offset);
273
- if (opts.select && opts.select.length > 0) {
274
- params["select"] = opts.select.join(",");
275
- }
235
+ if (opts.select && opts.select.length > 0) params["select"] = opts.select.join(",");
276
236
  if (opts.orderBy) {
277
237
  params["orderBy"] = opts.orderBy.field;
278
238
  params["direction"] = (_a = opts.orderBy.direction) != null ? _a : "asc";
279
239
  }
280
240
  const filters = opts.where ? Array.isArray(opts.where) ? opts.where : [opts.where] : [];
281
- if (filters.length > 0) {
282
- params["where"] = JSON.stringify(filters);
283
- }
241
+ if (filters.length > 0) params["where"] = JSON.stringify(filters);
284
242
  return params;
285
243
  }
286
- function eq(field, value) {
287
- return { field, operator: "eq", value };
288
- }
289
- function neq(field, value) {
290
- return { field, operator: "neq", value };
291
- }
292
- function gt(field, value) {
293
- return { field, operator: "gt", value };
294
- }
295
- function lt(field, value) {
296
- return { field, operator: "lt", value };
297
- }
298
- function inArray(field, value) {
299
- return { field, operator: "in", value };
300
- }
244
+ var eq = (field, value) => ({ field, operator: "eq", value });
245
+ var neq = (field, value) => ({ field, operator: "neq", value });
246
+ var gt = (field, value) => ({ field, operator: "gt", value });
247
+ var lt = (field, value) => ({ field, operator: "lt", value });
248
+ var gte = (field, value) => ({ field, operator: "gte", value });
249
+ var lte = (field, value) => ({ field, operator: "lte", value });
250
+ var inArray = (field, value) => ({ field, operator: "in", value });
301
251
 
302
252
  // src/records/client.ts
303
253
  var RecordsClient = class {
@@ -305,27 +255,13 @@ var RecordsClient = class {
305
255
  this.baseUrl = config.url;
306
256
  this.headers = {
307
257
  "Content-Type": "application/json",
308
- "Authorization": `Bearer ${config.apiKey}`
258
+ "Authorization": `Bearer ${config.bucketSecurityKey}`
309
259
  };
310
260
  }
311
- // ─── SELECT ────────────────────────────────────────────────────────────────
312
- /**
313
- * Query records from a collection.
314
- *
315
- * @param collection - Collection name (e.g. "users")
316
- * @param options - Filters, ordering, pagination
317
- *
318
- * @example
319
- * const { data, error } = await hydrous.records.select('users', {
320
- * where: { field: 'role', operator: 'eq', value: 'admin' },
321
- * orderBy: { field: 'createdAt', direction: 'desc' },
322
- * limit: 20,
323
- * });
324
- */
261
+ /** Query records from a collection */
325
262
  async select(collection, options = {}) {
326
263
  try {
327
- const params = serialiseQuery(options);
328
- const url = buildUrl(this.baseUrl, `records/${collection}`, params);
264
+ const url = buildUrl(this.baseUrl, `records/${collection}`, serialiseQuery(options));
329
265
  const res = await fetch(url, { headers: this.headers });
330
266
  const json = await parseResponse(res);
331
267
  return { data: json.data, count: json.count, error: null };
@@ -333,45 +269,20 @@ var RecordsClient = class {
333
269
  return { data: [], count: 0, error: toHydrousError(err) };
334
270
  }
335
271
  }
336
- // ─── GET ONE ───────────────────────────────────────────────────────────────
337
- /**
338
- * Fetch a single record by its ID.
339
- *
340
- * @example
341
- * const { data, error } = await hydrous.records.get('users', 'user_abc123');
342
- */
272
+ /** Fetch a single record by ID */
343
273
  async get(collection, id) {
344
274
  try {
345
- const url = buildUrl(this.baseUrl, `records/${collection}/${id}`);
346
- const res = await fetch(url, { headers: this.headers });
275
+ const res = await fetch(buildUrl(this.baseUrl, `records/${collection}/${id}`), { headers: this.headers });
347
276
  const json = await parseResponse(res);
348
277
  return { data: json.data, error: null };
349
278
  } catch (err) {
350
279
  return { data: null, error: toHydrousError(err) };
351
280
  }
352
281
  }
353
- // ─── INSERT ────────────────────────────────────────────────────────────────
354
- /**
355
- * Insert one or more records into a collection.
356
- *
357
- * @param collection - Collection name
358
- * @param payload - A single record object or an array of record objects
359
- *
360
- * @example
361
- * // Single insert
362
- * const { data, error } = await hydrous.records.insert('users', {
363
- * name: 'Alice', email: 'alice@example.com'
364
- * });
365
- *
366
- * // Bulk insert
367
- * const { data, error } = await hydrous.records.insert('users', [
368
- * { name: 'Alice' }, { name: 'Bob' }
369
- * ]);
370
- */
282
+ /** Insert one or more records */
371
283
  async insert(collection, payload) {
372
284
  try {
373
- const url = buildUrl(this.baseUrl, `records/${collection}`);
374
- const res = await fetch(url, {
285
+ const res = await fetch(buildUrl(this.baseUrl, `records/${collection}`), {
375
286
  method: "POST",
376
287
  headers: this.headers,
377
288
  body: JSON.stringify(payload)
@@ -382,19 +293,10 @@ var RecordsClient = class {
382
293
  return { data: [], count: 0, error: toHydrousError(err) };
383
294
  }
384
295
  }
385
- // ─── UPDATE ────────────────────────────────────────────────────────────────
386
- /**
387
- * Update a record by ID.
388
- *
389
- * @example
390
- * const { data, error } = await hydrous.records.update('users', 'user_abc123', {
391
- * name: 'Alice Smith'
392
- * });
393
- */
296
+ /** Update a record by ID */
394
297
  async update(collection, id, payload) {
395
298
  try {
396
- const url = buildUrl(this.baseUrl, `records/${collection}/${id}`);
397
- const res = await fetch(url, {
299
+ const res = await fetch(buildUrl(this.baseUrl, `records/${collection}/${id}`), {
398
300
  method: "PATCH",
399
301
  headers: this.headers,
400
302
  body: JSON.stringify(payload)
@@ -405,17 +307,13 @@ var RecordsClient = class {
405
307
  return { data: null, error: toHydrousError(err) };
406
308
  }
407
309
  }
408
- // ─── DELETE ────────────────────────────────────────────────────────────────
409
- /**
410
- * Delete a record by ID.
411
- *
412
- * @example
413
- * const { error } = await hydrous.records.delete('users', 'user_abc123');
414
- */
310
+ /** Delete a record by ID */
415
311
  async delete(collection, id) {
416
312
  try {
417
- const url = buildUrl(this.baseUrl, `records/${collection}/${id}`);
418
- const res = await fetch(url, { method: "DELETE", headers: this.headers });
313
+ const res = await fetch(buildUrl(this.baseUrl, `records/${collection}/${id}`), {
314
+ method: "DELETE",
315
+ headers: this.headers
316
+ });
419
317
  await parseResponse(res);
420
318
  return { data: void 0, error: null };
421
319
  } catch (err) {
@@ -430,31 +328,17 @@ var AnalyticsClient = class {
430
328
  this.baseUrl = config.url;
431
329
  this.headers = {
432
330
  "Content-Type": "application/json",
433
- "Authorization": `Bearer ${config.apiKey}`
331
+ "Authorization": `Bearer ${config.bucketSecurityKey}`
434
332
  };
435
333
  }
436
- // ─── TRACK ────────────────────────────────────────────────────────────────
437
- /**
438
- * Track an analytics event.
439
- *
440
- * @example
441
- * await hydrous.analytics.track({
442
- * event: 'page_view',
443
- * properties: { page: '/home', referrer: 'google.com' },
444
- * userId: 'user_abc123',
445
- * });
446
- */
334
+ /** Track a single analytics event */
447
335
  async track(options) {
448
336
  var _a;
449
337
  try {
450
- const url = buildUrl(this.baseUrl, "analytics/track");
451
- const res = await fetch(url, {
338
+ const res = await fetch(buildUrl(this.baseUrl, "analytics/track"), {
452
339
  method: "POST",
453
340
  headers: this.headers,
454
- body: JSON.stringify({
455
- ...options,
456
- timestamp: (_a = options.timestamp) != null ? _a : Date.now()
457
- })
341
+ body: JSON.stringify({ ...options, timestamp: (_a = options.timestamp) != null ? _a : Date.now() })
458
342
  });
459
343
  await parseResponse(res);
460
344
  return { data: void 0, error: null };
@@ -462,18 +346,25 @@ var AnalyticsClient = class {
462
346
  return { data: null, error: toHydrousError(err) };
463
347
  }
464
348
  }
465
- // ─── QUERY ────────────────────────────────────────────────────────────────
466
- /**
467
- * Query recorded analytics events.
468
- *
469
- * @example
470
- * const { data } = await hydrous.analytics.query({
471
- * event: 'page_view',
472
- * from: '2024-01-01',
473
- * to: '2024-01-31',
474
- * limit: 100,
475
- * });
476
- */
349
+ /** Track many events in one request */
350
+ async trackBatch(events) {
351
+ try {
352
+ const stamped = events.map((e) => {
353
+ var _a;
354
+ return { ...e, timestamp: (_a = e.timestamp) != null ? _a : Date.now() };
355
+ });
356
+ const res = await fetch(buildUrl(this.baseUrl, "analytics/track/batch"), {
357
+ method: "POST",
358
+ headers: this.headers,
359
+ body: JSON.stringify({ events: stamped })
360
+ });
361
+ await parseResponse(res);
362
+ return { data: void 0, error: null };
363
+ } catch (err) {
364
+ return { data: null, error: toHydrousError(err) };
365
+ }
366
+ }
367
+ /** Query recorded analytics events */
477
368
  async query(options = {}) {
478
369
  try {
479
370
  const params = {};
@@ -490,56 +381,23 @@ var AnalyticsClient = class {
490
381
  return { data: [], count: 0, error: toHydrousError(err) };
491
382
  }
492
383
  }
493
- // ─── BATCH TRACK ─────────────────────────────────────────────────────────
494
- /**
495
- * Track multiple events in a single request (more efficient than
496
- * calling `track` in a loop).
497
- *
498
- * @example
499
- * await hydrous.analytics.trackBatch([
500
- * { event: 'signup', userId: 'u1' },
501
- * { event: 'onboarded', userId: 'u1' },
502
- * ]);
503
- */
504
- async trackBatch(events) {
505
- try {
506
- const url = buildUrl(this.baseUrl, "analytics/track/batch");
507
- const stamped = events.map((e) => {
508
- var _a;
509
- return {
510
- ...e,
511
- timestamp: (_a = e.timestamp) != null ? _a : Date.now()
512
- };
513
- });
514
- const res = await fetch(url, {
515
- method: "POST",
516
- headers: this.headers,
517
- body: JSON.stringify({ events: stamped })
518
- });
519
- await parseResponse(res);
520
- return { data: void 0, error: null };
521
- } catch (err) {
522
- return { data: null, error: toHydrousError(err) };
523
- }
524
- }
525
384
  };
526
385
 
527
- // src/storage/client.ts
386
+ // src/storage/scoped.ts
528
387
  var isBrowser = typeof window !== "undefined" && typeof XMLHttpRequest !== "undefined";
529
- function bucketFromKey(key) {
530
- return encodeURIComponent(key);
531
- }
532
- function storageUrl(base, bucketKey, path) {
533
- const bucket = bucketFromKey(bucketKey);
534
- return `${base.replace(/\/$/, "")}/storage/${bucket}/${path.replace(/^\//, "")}`;
388
+ function storageBase(url, bucketKey) {
389
+ return `${url.replace(/\/$/, "")}/storage/${encodeURIComponent(bucketKey)}`;
535
390
  }
536
391
  function storageHeaders(bucketKey) {
537
392
  return { "X-Storage-Key": bucketKey };
538
393
  }
539
- function drainSSEProgress(rawText, onProgress) {
394
+ function jsonHeaders(bucketKey) {
395
+ return { "X-Storage-Key": bucketKey, "Content-Type": "application/json" };
396
+ }
397
+ function drainSSE(raw, onProgress) {
540
398
  const results = [];
541
399
  const errors = [];
542
- parseSSEText(rawText, (eventType, data) => {
400
+ parseSSEText(raw, (eventType, data) => {
543
401
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
544
402
  const d = data;
545
403
  if (eventType === "progress" && onProgress) {
@@ -561,11 +419,9 @@ function drainSSEProgress(rawText, onProgress) {
561
419
  if (eventType === "done") {
562
420
  if (d["path"]) {
563
421
  results.push(d);
564
- } else if (Array.isArray(d["errors"])) {
565
- const succeeded = (_j = d["succeeded"]) != null ? _j : [];
566
- const errs = d["errors"];
567
- results.push(...succeeded);
568
- errors.push(...errs);
422
+ } else if (Array.isArray(d["succeeded"])) {
423
+ results.push(...d["succeeded"]);
424
+ errors.push(...(_j = d["errors"]) != null ? _j : []);
569
425
  }
570
426
  }
571
427
  if (eventType === "error") {
@@ -578,51 +434,44 @@ function drainSSEProgress(rawText, onProgress) {
578
434
  });
579
435
  return { results, errors };
580
436
  }
581
- var StorageClient = class {
582
- constructor(config) {
583
- this.baseUrl = config.url;
437
+ var ScopedStorageClient = class {
438
+ constructor(baseUrl, keyName, bucketKey) {
439
+ this.base = storageBase(baseUrl, bucketKey);
440
+ this.key = bucketKey;
441
+ this.keyName = keyName;
584
442
  }
585
443
  // ══════════════════════════════════════════════════════════════════════════
586
- // UPLOAD
444
+ // UPLOAD — single file
587
445
  // ══════════════════════════════════════════════════════════════════════════
588
446
  /**
589
- * Upload a single file to a bucket.
447
+ * Upload a single file.
590
448
  *
591
- * The bucket key **always comes first**.
592
- * Supply an `onProgress` callback to receive live upload progress including
593
- * bytes transferred, speed (bytes/sec), ETA, and lifecycle stage.
449
+ * Supply `onProgress` to receive live upload ticks including bytes
450
+ * transferred, speed (bytes/sec), ETA, and lifecycle stage.
594
451
  *
595
- * ### Stages fired via `onProgress`
596
- * | Stage | Meaning |
597
- * |-------------|------------------------------------------|
598
- * | `pending` | Queued, not yet started |
599
- * | `compressing` | Server is compressing the file |
600
- * | `uploading` | Bytes flowing to cloud storage |
601
- * | `done` | Confirmed written to cloud storage |
602
- * | `error` | Something went wrong |
452
+ * **Stage sequence:**
453
+ * `pending → compressing → uploading → done | error`
603
454
  *
604
- * @param bucketKey Your storage bucket key (`ssk_…`)
605
- * @param file A `File`, `Blob`, or `Buffer` (Node)
606
- * @param options Path, overwrite flag, progress callback
455
+ * In browsers the progress is tracked at the network level via XHR, so
456
+ * `percent` reflects actual bytes leaving the device. `done` only fires
457
+ * after the server confirms the write to cloud storage, so 100% is real.
607
458
  *
608
459
  * @example
609
- * const { data, error } = await hydrous.storage.upload(
610
- * 'ssk_my_bucket_key',
611
- * file,
612
- * {
613
- * path: 'avatars/alice.jpg',
614
- * overwrite: true,
615
- * onProgress: (p) => {
616
- * console.log(`${p.stage} — ${p.percent}% ${p.bytesPerSecond} B/s ETA ${p.eta}s`);
617
- * },
618
- * }
619
- * );
460
+ * const { data, error } = await db.storage('avatars').upload(file, {
461
+ * path: 'users/alice.jpg',
462
+ * overwrite: true,
463
+ * onProgress: (p) => {
464
+ * setProgress(p.percent); // e.g. drive a <progress> bar
465
+ * setSpeed(`${p.bytesPerSecond} B/s`);
466
+ * setEta(`${p.eta}s remaining`);
467
+ * },
468
+ * });
620
469
  */
621
- async upload(bucketKey, file, options = {}) {
470
+ async upload(file, options = {}) {
622
471
  var _a, _b;
623
472
  const { path, overwrite = false, onProgress } = options;
624
473
  try {
625
- const url = storageUrl(this.baseUrl, bucketKey, "upload");
474
+ const url = `${this.base}/upload`;
626
475
  const form = new FormData();
627
476
  if (file instanceof Uint8Array) {
628
477
  form.append("file", new Blob([file.buffer]), path != null ? path : "file");
@@ -633,25 +482,23 @@ var StorageClient = class {
633
482
  }
634
483
  if (path) form.append("path", path);
635
484
  if (overwrite) form.append("overwrite", "true");
636
- const headers = storageHeaders(bucketKey);
485
+ const headers = storageHeaders(this.key);
637
486
  if (isBrowser) {
638
487
  const totalBytes = file instanceof Blob ? file.size : file instanceof Uint8Array ? file.byteLength : file.byteLength;
639
488
  const rawBody = await xhrUpload(url, form, headers, (loaded, total) => {
640
- if (onProgress) {
641
- onProgress({
642
- index: 0,
643
- total: 1,
644
- path: path != null ? path : "",
645
- stage: "uploading",
646
- bytesUploaded: loaded,
647
- totalBytes: total || totalBytes,
648
- percent: Math.min(99, Math.round(loaded / (total || totalBytes) * 100)),
649
- bytesPerSecond: null,
650
- eta: null
651
- });
652
- }
489
+ onProgress == null ? void 0 : onProgress({
490
+ index: 0,
491
+ total: 1,
492
+ path: path != null ? path : "",
493
+ stage: "uploading",
494
+ bytesUploaded: loaded,
495
+ totalBytes: total || totalBytes,
496
+ percent: Math.min(99, Math.round(loaded / (total || totalBytes) * 100)),
497
+ bytesPerSecond: null,
498
+ eta: null
499
+ });
653
500
  });
654
- const { results, errors } = drainSSEProgress(rawBody, onProgress);
501
+ const { results, errors } = drainSSE(rawBody, onProgress);
655
502
  if (errors.length > 0 && results.length === 0) {
656
503
  return { data: null, error: { message: errors[0].error, code: errors[0].code } };
657
504
  }
@@ -674,33 +521,32 @@ var StorageClient = class {
674
521
  }
675
522
  const res = await fetch(url, { method: "POST", headers, body: form });
676
523
  if (!res.ok) {
677
- const err = await res.json().catch(() => ({}));
678
- throw new HydrousSDKError((_b = err.error) != null ? _b : `HTTP ${res.status}`, "HTTP_ERROR", res.status);
524
+ const e = await res.json().catch(() => ({}));
525
+ throw new HydrousDBError((_b = e.error) != null ? _b : `HTTP ${res.status}`, "HTTP_ERROR", res.status);
679
526
  }
680
527
  let finalResult = null;
681
528
  await readSSEStream(res, (eventType, data) => {
682
- var _a2, _b2, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
529
+ var _a2, _b2, _c, _d, _e, _f, _g, _h;
683
530
  const d = data;
684
531
  if (eventType === "progress" && onProgress) {
685
532
  onProgress({
686
- index: (_a2 = d["index"]) != null ? _a2 : 0,
687
- total: (_b2 = d["total"]) != null ? _b2 : 1,
688
- path: (_d = (_c = d["path"]) != null ? _c : path) != null ? _d : "",
689
- stage: (_e = d["stage"]) != null ? _e : "uploading",
690
- bytesUploaded: (_f = d["bytesUploaded"]) != null ? _f : 0,
691
- totalBytes: (_g = d["totalBytes"]) != null ? _g : 0,
692
- percent: (_h = d["percent"]) != null ? _h : 0,
693
- bytesPerSecond: (_i = d["bytesPerSecond"]) != null ? _i : null,
694
- eta: (_j = d["eta"]) != null ? _j : null,
695
- result: d["result"],
696
- error: d["error"]
533
+ index: 0,
534
+ total: 1,
535
+ path: path != null ? path : "",
536
+ stage: (_a2 = d["stage"]) != null ? _a2 : "uploading",
537
+ bytesUploaded: (_b2 = d["bytesUploaded"]) != null ? _b2 : 0,
538
+ totalBytes: (_c = d["totalBytes"]) != null ? _c : 0,
539
+ percent: (_d = d["percent"]) != null ? _d : 0,
540
+ bytesPerSecond: (_e = d["bytesPerSecond"]) != null ? _e : null,
541
+ eta: (_f = d["eta"]) != null ? _f : null,
542
+ result: d["result"]
697
543
  });
698
544
  }
699
545
  if (eventType === "done") finalResult = data;
700
546
  if (eventType === "error") {
701
- throw new HydrousSDKError(
702
- (_k = d["error"]) != null ? _k : "Upload failed",
703
- (_l = d["code"]) != null ? _l : "UPLOAD_ERROR"
547
+ throw new HydrousDBError(
548
+ (_g = d["error"]) != null ? _g : "Upload failed",
549
+ (_h = d["code"]) != null ? _h : "UPLOAD_ERROR"
704
550
  );
705
551
  }
706
552
  });
@@ -710,39 +556,31 @@ var StorageClient = class {
710
556
  }
711
557
  }
712
558
  // ══════════════════════════════════════════════════════════════════════════
713
- // UPLOAD RAW (text / JSON / binary from string)
559
+ // UPLOAD TEXT / JSON
714
560
  // ══════════════════════════════════════════════════════════════════════════
715
561
  /**
716
- * Upload raw text or JSON content directly — no `File` object needed.
717
- * Great for saving generated content, config files, or JSON records.
718
- *
719
- * @param bucketKey Your storage bucket key (`ssk_…`)
720
- * @param path Destination path (e.g. `"configs/settings.json"`)
721
- * @param content String content to store
722
- * @param options `mimeType`, `overwrite`, `onProgress`
562
+ * Upload raw text or JSON content directly — no File object needed.
723
563
  *
724
564
  * @example
725
- * await hydrous.storage.uploadText(
726
- * 'ssk_my_bucket_key',
727
- * 'reports/summary.txt',
728
- * 'Hello from Hydrous!',
729
- * { mimeType: 'text/plain' }
565
+ * // Save a JSON config
566
+ * await db.storage('configs').uploadText(
567
+ * 'settings/app.json',
568
+ * JSON.stringify({ theme: 'dark' }),
569
+ * { mimeType: 'application/json' }
730
570
  * );
731
571
  */
732
- async uploadText(bucketKey, path, content, options = {}) {
572
+ async uploadText(path, content, options = {}) {
733
573
  var _a;
734
574
  const { mimeType = "text/plain", overwrite = false, onProgress } = options;
735
575
  try {
736
- const url = storageUrl(this.baseUrl, bucketKey, "upload-raw");
737
- const headers = { ...storageHeaders(bucketKey), "Content-Type": "application/json" };
738
- const res = await fetch(url, {
576
+ const res = await fetch(`${this.base}/upload-raw`, {
739
577
  method: "POST",
740
- headers,
578
+ headers: jsonHeaders(this.key),
741
579
  body: JSON.stringify({ path, content, mimeType, overwrite })
742
580
  });
743
581
  if (!res.ok) {
744
582
  const e = await res.json().catch(() => ({}));
745
- throw new HydrousSDKError((_a = e.error) != null ? _a : `HTTP ${res.status}`, "HTTP_ERROR", res.status);
583
+ throw new HydrousDBError((_a = e.error) != null ? _a : `HTTP ${res.status}`, "HTTP_ERROR", res.status);
746
584
  }
747
585
  let finalResult = null;
748
586
  await readSSEStream(res, (eventType, data) => {
@@ -774,84 +612,62 @@ var StorageClient = class {
774
612
  /**
775
613
  * Upload multiple files in one request.
776
614
  *
777
- * `onProgress` fires for **every file individually** the `index` field
778
- * tells you which file the event belongs to (0-based, same order as `files`).
779
- * All files receive a `pending` event upfront before any uploads start,
780
- * so you can render all progress bars immediately.
781
- *
782
- * @param bucketKey Your storage bucket key (`ssk_…`)
783
- * @param files Array of `File` objects (browser) or `{ name, data }` objects (Node)
784
- * @param options Prefix, per-file paths, overwrite, concurrency, onProgress
615
+ * `onProgress` fires per file — use `p.index` to identify which file.
616
+ * All files receive a `pending` event upfront so you can render progress
617
+ * bars immediately before any data is sent.
785
618
  *
786
619
  * @example
787
- * await hydrous.storage.batchUpload(
788
- * 'ssk_my_bucket_key',
789
- * fileArray,
790
- * {
791
- * prefix: 'uploads/2024/',
792
- * onProgress: (p) => {
793
- * console.log(`File ${p.index}: ${p.stage} ${p.percent}%`);
794
- * },
795
- * }
796
- * );
620
+ * await db.storage('documents').batchUpload(files, {
621
+ * prefix: 'reports/2024/',
622
+ * onProgress: (p) => updateBar(p.index, p.percent),
623
+ * });
797
624
  */
798
- async batchUpload(bucketKey, files, options = {}) {
625
+ async batchUpload(files, options = {}) {
799
626
  var _a;
800
627
  const { prefix = "", paths, overwrite = false, onProgress } = options;
801
628
  try {
802
- const url = storageUrl(this.baseUrl, bucketKey, "batch-upload");
629
+ const url = `${this.base}/batch-upload`;
630
+ const resolvedPaths = files.map((f, i) => {
631
+ var _a2;
632
+ return (_a2 = paths == null ? void 0 : paths[i]) != null ? _a2 : `${prefix}${f.name}`;
633
+ });
803
634
  const form = new FormData();
804
- const resolvedPaths = files.map(
805
- (f, i) => {
806
- var _a2;
807
- return (_a2 = paths == null ? void 0 : paths[i]) != null ? _a2 : `${prefix}${f.name}`;
808
- }
809
- );
810
635
  files.forEach((f) => form.append("files", f, f.name));
811
636
  form.append("paths", JSON.stringify(resolvedPaths));
812
637
  if (overwrite) form.append("overwrite", "true");
813
- const headers = storageHeaders(bucketKey);
638
+ const headers = storageHeaders(this.key);
814
639
  if (isBrowser) {
815
640
  const totalBytes = files.reduce((s, f) => s + f.size, 0);
816
641
  const rawBody = await xhrUpload(url, form, headers, (loaded, total) => {
817
- if (onProgress) {
818
- let cursor = 0;
819
- for (let i = 0; i < files.length; i++) {
820
- const share = files[i].size / (totalBytes || 1);
821
- const myStart = cursor;
822
- const myEnd = cursor + share;
823
- const fileLoaded = Math.max(0, Math.min(
824
- files[i].size,
825
- (loaded / (total || totalBytes) - myStart) / share * files[i].size
826
- ));
827
- onProgress({
828
- index: i,
829
- total: files.length,
830
- path: resolvedPaths[i],
831
- stage: "uploading",
832
- bytesUploaded: Math.round(fileLoaded),
833
- totalBytes: files[i].size,
834
- percent: Math.min(99, Math.round(fileLoaded / files[i].size * 100)),
835
- bytesPerSecond: null,
836
- eta: null
837
- });
838
- cursor = myEnd;
839
- }
642
+ if (!onProgress) return;
643
+ let cursor = 0;
644
+ for (let i = 0; i < files.length; i++) {
645
+ const share = files[i].size / (totalBytes || 1);
646
+ const fileLoaded = Math.max(0, Math.min(
647
+ files[i].size,
648
+ (loaded / (total || totalBytes) - cursor) / share * files[i].size
649
+ ));
650
+ onProgress({
651
+ index: i,
652
+ total: files.length,
653
+ path: resolvedPaths[i],
654
+ stage: "uploading",
655
+ bytesUploaded: Math.round(fileLoaded),
656
+ totalBytes: files[i].size,
657
+ percent: Math.min(99, Math.round(fileLoaded / files[i].size * 100)),
658
+ bytesPerSecond: null,
659
+ eta: null
660
+ });
661
+ cursor += share;
840
662
  }
841
663
  });
842
- const { results, errors } = drainSSEProgress(rawBody, onProgress);
843
- return {
844
- data: {
845
- succeeded: results,
846
- failed: errors
847
- },
848
- error: null
849
- };
664
+ const { results, errors } = drainSSE(rawBody, onProgress);
665
+ return { data: { succeeded: results, failed: errors }, error: null };
850
666
  }
851
667
  const res = await fetch(url, { method: "POST", headers, body: form });
852
668
  if (!res.ok) {
853
669
  const e = await res.json().catch(() => ({}));
854
- throw new HydrousSDKError((_a = e.error) != null ? _a : `HTTP ${res.status}`, "HTTP_ERROR", res.status);
670
+ throw new HydrousDBError((_a = e.error) != null ? _a : `HTTP ${res.status}`, "HTTP_ERROR", res.status);
855
671
  }
856
672
  const succeeded = [];
857
673
  const failed = [];
@@ -868,10 +684,7 @@ var StorageClient = class {
868
684
  totalBytes: (_f = d["totalBytes"]) != null ? _f : 0,
869
685
  percent: (_g = d["percent"]) != null ? _g : 0,
870
686
  bytesPerSecond: (_h = d["bytesPerSecond"]) != null ? _h : null,
871
- eta: (_i = d["eta"]) != null ? _i : null,
872
- result: d["result"],
873
- error: d["error"],
874
- code: d["code"]
687
+ eta: (_i = d["eta"]) != null ? _i : null
875
688
  });
876
689
  }
877
690
  if (eventType === "done" && d["succeeded"]) {
@@ -890,30 +703,22 @@ var StorageClient = class {
890
703
  /**
891
704
  * Download a single file and return its content as an `ArrayBuffer`.
892
705
  *
893
- * @param bucketKey Your storage bucket key (`ssk_…`)
894
- * @param filePath Path of the file within your bucket
895
- *
896
706
  * @example
897
- * const { data, error } = await hydrous.storage.download(
898
- * 'ssk_my_bucket_key',
899
- * 'avatars/alice.jpg'
900
- * );
901
- * if (data) {
902
- * const blob = new Blob([data]);
903
- * const url = URL.createObjectURL(blob);
904
- * }
707
+ * const { data } = await db.storage('avatars').download('users/alice.jpg');
708
+ * const blob = new Blob([data!]);
709
+ * img.src = URL.createObjectURL(blob);
905
710
  */
906
- async download(bucketKey, filePath) {
711
+ async download(filePath) {
907
712
  var _a;
908
713
  try {
909
- const url = storageUrl(this.baseUrl, bucketKey, `download/${filePath}`);
910
- const res = await fetch(url, { headers: storageHeaders(bucketKey) });
714
+ const res = await fetch(`${this.base}/download/${filePath}`, {
715
+ headers: storageHeaders(this.key)
716
+ });
911
717
  if (!res.ok) {
912
718
  const e = await res.json().catch(() => ({}));
913
- throw new HydrousSDKError((_a = e.error) != null ? _a : `HTTP ${res.status}`, "HTTP_ERROR", res.status);
719
+ throw new HydrousDBError((_a = e.error) != null ? _a : `HTTP ${res.status}`, "HTTP_ERROR", res.status);
914
720
  }
915
- const buffer = await res.arrayBuffer();
916
- return { data: buffer, error: null };
721
+ return { data: await res.arrayBuffer(), error: null };
917
722
  } catch (err) {
918
723
  return { data: null, error: toHydrousError(err) };
919
724
  }
@@ -924,36 +729,26 @@ var StorageClient = class {
924
729
  /**
925
730
  * Download multiple files in one request.
926
731
  *
927
- * When `autoSave: true` (browser only) each file is automatically saved
928
- * to the user's Downloads folder as it arrives.
929
- *
930
- * @param bucketKey Your storage bucket key (`ssk_…`)
931
- * @param filePaths Array of file paths within your bucket
932
- * @param options Concurrency, onProgress, autoSave
732
+ * Set `autoSave: true` (browser only) to trigger a Save dialog per file.
933
733
  *
934
734
  * @example
935
- * const { data } = await hydrous.storage.batchDownload(
936
- * 'ssk_my_bucket_key',
937
- * ['reports/jan.pdf', 'reports/feb.pdf'],
938
- * {
939
- * onProgress: (p) => console.log(p.path, p.status),
940
- * autoSave: true, // triggers browser file-save dialog per file
941
- * }
735
+ * const { data } = await db.storage('reports').batchDownload(
736
+ * ['jan.pdf', 'feb.pdf'],
737
+ * { autoSave: true, onProgress: (p) => console.log(p.path, p.status) }
942
738
  * );
943
739
  */
944
- async batchDownload(bucketKey, filePaths, options = {}) {
740
+ async batchDownload(filePaths, options = {}) {
945
741
  var _a;
946
742
  const { concurrency = 5, onProgress, autoSave = false } = options;
947
743
  try {
948
- const url = storageUrl(this.baseUrl, bucketKey, "batch-download");
949
- const res = await fetch(url, {
744
+ const res = await fetch(`${this.base}/batch-download`, {
950
745
  method: "POST",
951
- headers: { ...storageHeaders(bucketKey), "Content-Type": "application/json" },
746
+ headers: jsonHeaders(this.key),
952
747
  body: JSON.stringify({ paths: filePaths, concurrency })
953
748
  });
954
749
  if (!res.ok) {
955
750
  const e = await res.json().catch(() => ({}));
956
- throw new HydrousSDKError((_a = e.error) != null ? _a : `HTTP ${res.status}`, "HTTP_ERROR", res.status);
751
+ throw new HydrousDBError((_a = e.error) != null ? _a : `HTTP ${res.status}`, "HTTP_ERROR", res.status);
957
752
  }
958
753
  const downloadedFiles = [];
959
754
  await readSSEStream(res, (eventType, data) => {
@@ -968,20 +763,10 @@ var StorageClient = class {
968
763
  const binary = atob(base64);
969
764
  const bytes = new Uint8Array(binary.length);
970
765
  for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
971
- const content = bytes.buffer;
972
- downloadedFiles.push({ path, content, mimeType, size });
973
- if (onProgress) {
974
- onProgress({
975
- index,
976
- total: filePaths.length,
977
- path,
978
- status: "success",
979
- size,
980
- mimeType
981
- });
982
- }
766
+ downloadedFiles.push({ path, content: bytes.buffer, mimeType, size });
767
+ onProgress == null ? void 0 : onProgress({ index, total: filePaths.length, path, status: "success", size, mimeType });
983
768
  if (autoSave && isBrowser) {
984
- const blob = new Blob([content], { type: mimeType });
769
+ const blob = new Blob([bytes.buffer], { type: mimeType });
985
770
  const blobUrl = URL.createObjectURL(blob);
986
771
  const a = document.createElement("a");
987
772
  a.href = blobUrl;
@@ -1010,37 +795,30 @@ var StorageClient = class {
1010
795
  // LIST
1011
796
  // ══════════════════════════════════════════════════════════════════════════
1012
797
  /**
1013
- * List files and folders inside a bucket (or a folder within it).
1014
- *
1015
- * Results are paginated — use `pagination.nextCursor` to fetch the next page.
1016
- *
1017
- * @param bucketKey Your storage bucket key (`ssk_…`)
1018
- * @param options `prefix`, `limit`, `cursor`
798
+ * List files and folders (paginated).
1019
799
  *
1020
800
  * @example
1021
- * const { data } = await hydrous.storage.list('ssk_my_bucket_key', {
1022
- * prefix: 'avatars/',
1023
- * limit: 50,
1024
- * });
1025
- * for (const item of data.items) {
1026
- * console.log(item.type, item.path);
801
+ * const { data } = await db.storage('avatars').list({ prefix: 'users/' });
802
+ * for (const item of data!.items) {
803
+ * console.log(item.type, item.path, item.size);
1027
804
  * }
1028
805
  */
1029
- async list(bucketKey, options = {}) {
806
+ async list(options = {}) {
1030
807
  const { prefix = "", limit = 50, cursor } = options;
1031
808
  try {
1032
- const params = {
809
+ const url = buildUrl(this.base, "list", {
1033
810
  prefix: prefix || void 0,
1034
811
  limit,
1035
812
  cursor: cursor || void 0
1036
- };
1037
- const url = buildUrl(
1038
- this.baseUrl,
1039
- `storage/${bucketFromKey(bucketKey)}/list`,
1040
- params
1041
- );
1042
- const res = await fetch(url, { headers: storageHeaders(bucketKey) });
1043
- const json = await parseResponse(res);
813
+ });
814
+ const res = await fetch(url.replace(this.base + "/list", `${this.base}/list`), {
815
+ headers: storageHeaders(this.key)
816
+ });
817
+ const u = `${this.base}/list?${new URLSearchParams(
818
+ Object.entries({ prefix: prefix || "", limit: String(limit), ...cursor ? { cursor } : {} }).filter(([, v]) => v !== "")
819
+ ).toString()}`;
820
+ const r = await fetch(u, { headers: storageHeaders(this.key) });
821
+ const json = await parseResponse(r);
1044
822
  return { data: json, error: null };
1045
823
  } catch (err) {
1046
824
  return { data: null, error: toHydrousError(err) };
@@ -1050,22 +828,17 @@ var StorageClient = class {
1050
828
  // METADATA
1051
829
  // ══════════════════════════════════════════════════════════════════════════
1052
830
  /**
1053
- * Get metadata for a specific file (size, MIME type, compression info, etc.)
1054
- *
1055
- * @param bucketKey Your storage bucket key (`ssk_…`)
1056
- * @param filePath Path of the file within your bucket
831
+ * Get metadata for a file (size, MIME type, compression, etc.)
1057
832
  *
1058
833
  * @example
1059
- * const { data } = await hydrous.storage.metadata(
1060
- * 'ssk_my_bucket_key',
1061
- * 'avatars/alice.jpg'
1062
- * );
1063
- * console.log(data.size, data.mimeType);
834
+ * const { data: meta } = await db.storage('docs').metadata('report.pdf');
835
+ * console.log(meta!.size, meta!.mimeType, meta!.isCompressed);
1064
836
  */
1065
- async metadata(bucketKey, filePath) {
837
+ async metadata(filePath) {
1066
838
  try {
1067
- const url = storageUrl(this.baseUrl, bucketKey, `metadata/${filePath}`);
1068
- const res = await fetch(url, { headers: storageHeaders(bucketKey) });
839
+ const res = await fetch(`${this.base}/metadata/${filePath}`, {
840
+ headers: storageHeaders(this.key)
841
+ });
1069
842
  const json = await parseResponse(res);
1070
843
  return { data: json.data, error: null };
1071
844
  } catch (err) {
@@ -1073,23 +846,14 @@ var StorageClient = class {
1073
846
  }
1074
847
  }
1075
848
  // ══════════════════════════════════════════════════════════════════════════
1076
- // DELETE FILE
849
+ // DELETE
1077
850
  // ══════════════════════════════════════════════════════════════════════════
1078
- /**
1079
- * Delete a single file.
1080
- *
1081
- * @param bucketKey Your storage bucket key (`ssk_…`)
1082
- * @param filePath Path of the file to delete
1083
- *
1084
- * @example
1085
- * await hydrous.storage.deleteFile('ssk_my_bucket_key', 'avatars/old.jpg');
1086
- */
1087
- async deleteFile(bucketKey, filePath) {
851
+ /** Delete a single file */
852
+ async deleteFile(filePath) {
1088
853
  try {
1089
- const url = storageUrl(this.baseUrl, bucketKey, "file");
1090
- const res = await fetch(url, {
854
+ const res = await fetch(`${this.base}/file`, {
1091
855
  method: "DELETE",
1092
- headers: { ...storageHeaders(bucketKey), "Content-Type": "application/json" },
856
+ headers: jsonHeaders(this.key),
1093
857
  body: JSON.stringify({ path: filePath })
1094
858
  });
1095
859
  await parseResponse(res);
@@ -1098,24 +862,12 @@ var StorageClient = class {
1098
862
  return { data: null, error: toHydrousError(err) };
1099
863
  }
1100
864
  }
1101
- // ══════════════════════════════════════════════════════════════════════════
1102
- // DELETE FOLDER
1103
- // ══════════════════════════════════════════════════════════════════════════
1104
- /**
1105
- * Recursively delete a folder and all of its contents.
1106
- *
1107
- * @param bucketKey Your storage bucket key (`ssk_…`)
1108
- * @param folderPath Folder path to delete (e.g. `"old-uploads/"`)
1109
- *
1110
- * @example
1111
- * await hydrous.storage.deleteFolder('ssk_my_bucket_key', 'temp/');
1112
- */
1113
- async deleteFolder(bucketKey, folderPath) {
865
+ /** Recursively delete a folder and all its contents */
866
+ async deleteFolder(folderPath) {
1114
867
  try {
1115
- const url = storageUrl(this.baseUrl, bucketKey, "folder");
1116
- const res = await fetch(url, {
868
+ const res = await fetch(`${this.base}/folder`, {
1117
869
  method: "DELETE",
1118
- headers: { ...storageHeaders(bucketKey), "Content-Type": "application/json" },
870
+ headers: jsonHeaders(this.key),
1119
871
  body: JSON.stringify({ path: folderPath })
1120
872
  });
1121
873
  await parseResponse(res);
@@ -1124,24 +876,12 @@ var StorageClient = class {
1124
876
  return { data: null, error: toHydrousError(err) };
1125
877
  }
1126
878
  }
1127
- // ══════════════════════════════════════════════════════════════════════════
1128
- // CREATE FOLDER
1129
- // ══════════════════════════════════════════════════════════════════════════
1130
- /**
1131
- * Create an empty folder.
1132
- *
1133
- * @param bucketKey Your storage bucket key (`ssk_…`)
1134
- * @param folderPath Path for the new folder (e.g. `"avatars/2024/"`)
1135
- *
1136
- * @example
1137
- * await hydrous.storage.createFolder('ssk_my_bucket_key', 'avatars/2024/');
1138
- */
1139
- async createFolder(bucketKey, folderPath) {
879
+ /** Create an empty folder */
880
+ async createFolder(folderPath) {
1140
881
  try {
1141
- const url = storageUrl(this.baseUrl, bucketKey, "folder");
1142
- const res = await fetch(url, {
882
+ const res = await fetch(`${this.base}/folder`, {
1143
883
  method: "POST",
1144
- headers: { ...storageHeaders(bucketKey), "Content-Type": "application/json" },
884
+ headers: jsonHeaders(this.key),
1145
885
  body: JSON.stringify({ path: folderPath })
1146
886
  });
1147
887
  await parseResponse(res);
@@ -1151,28 +891,14 @@ var StorageClient = class {
1151
891
  }
1152
892
  }
1153
893
  // ══════════════════════════════════════════════════════════════════════════
1154
- // MOVE
894
+ // MOVE & COPY
1155
895
  // ══════════════════════════════════════════════════════════════════════════
1156
- /**
1157
- * Move (rename) a file to a new path.
1158
- *
1159
- * @param bucketKey Your storage bucket key (`ssk_…`)
1160
- * @param fromPath Current path of the file
1161
- * @param toPath New path for the file
1162
- *
1163
- * @example
1164
- * await hydrous.storage.move(
1165
- * 'ssk_my_bucket_key',
1166
- * 'drafts/report.pdf',
1167
- * 'published/report.pdf'
1168
- * );
1169
- */
1170
- async move(bucketKey, fromPath, toPath) {
896
+ /** Move (rename) a file */
897
+ async move(fromPath, toPath) {
1171
898
  try {
1172
- const url = storageUrl(this.baseUrl, bucketKey, "move");
1173
- const res = await fetch(url, {
899
+ const res = await fetch(`${this.base}/move`, {
1174
900
  method: "POST",
1175
- headers: { ...storageHeaders(bucketKey), "Content-Type": "application/json" },
901
+ headers: jsonHeaders(this.key),
1176
902
  body: JSON.stringify({ from: fromPath, to: toPath })
1177
903
  });
1178
904
  await parseResponse(res);
@@ -1181,29 +907,12 @@ var StorageClient = class {
1181
907
  return { data: null, error: toHydrousError(err) };
1182
908
  }
1183
909
  }
1184
- // ══════════════════════════════════════════════════════════════════════════
1185
- // COPY
1186
- // ══════════════════════════════════════════════════════════════════════════
1187
- /**
1188
- * Copy a file to a new path (original is kept).
1189
- *
1190
- * @param bucketKey Your storage bucket key (`ssk_…`)
1191
- * @param fromPath Source path
1192
- * @param toPath Destination path
1193
- *
1194
- * @example
1195
- * await hydrous.storage.copy(
1196
- * 'ssk_my_bucket_key',
1197
- * 'templates/invoice.pdf',
1198
- * 'invoices/invoice-001.pdf'
1199
- * );
1200
- */
1201
- async copy(bucketKey, fromPath, toPath) {
910
+ /** Copy a file (original is kept) */
911
+ async copy(fromPath, toPath) {
1202
912
  try {
1203
- const url = storageUrl(this.baseUrl, bucketKey, "copy");
1204
- const res = await fetch(url, {
913
+ const res = await fetch(`${this.base}/copy`, {
1205
914
  method: "POST",
1206
- headers: { ...storageHeaders(bucketKey), "Content-Type": "application/json" },
915
+ headers: jsonHeaders(this.key),
1207
916
  body: JSON.stringify({ from: fromPath, to: toPath })
1208
917
  });
1209
918
  await parseResponse(res);
@@ -1218,25 +927,16 @@ var StorageClient = class {
1218
927
  /**
1219
928
  * Generate a time-limited public URL for a private file.
1220
929
  *
1221
- * @param bucketKey Your storage bucket key (`ssk_…`)
1222
- * @param filePath Path of the file
1223
- * @param options `expiresIn` seconds (default: 3600)
1224
- *
1225
930
  * @example
1226
- * const { data } = await hydrous.storage.signedUrl(
1227
- * 'ssk_my_bucket_key',
1228
- * 'private/contract.pdf',
1229
- * { expiresIn: 300 } // 5 minutes
1230
- * );
1231
- * console.log(data.signedUrl); // share this URL
931
+ * const { data } = await db.storage('contracts').signedUrl('nda.pdf', { expiresIn: 300 });
932
+ * console.log(data!.signedUrl); // share this
1232
933
  */
1233
- async signedUrl(bucketKey, filePath, options = {}) {
934
+ async signedUrl(filePath, options = {}) {
1234
935
  const { expiresIn = 3600 } = options;
1235
936
  try {
1236
- const url = storageUrl(this.baseUrl, bucketKey, "signed-url");
1237
- const res = await fetch(url, {
937
+ const res = await fetch(`${this.base}/signed-url`, {
1238
938
  method: "POST",
1239
- headers: { ...storageHeaders(bucketKey), "Content-Type": "application/json" },
939
+ headers: jsonHeaders(this.key),
1240
940
  body: JSON.stringify({ path: filePath, expiresInSeconds: expiresIn })
1241
941
  });
1242
942
  const json = await parseResponse(res);
@@ -1249,18 +949,17 @@ var StorageClient = class {
1249
949
  // STATS
1250
950
  // ══════════════════════════════════════════════════════════════════════════
1251
951
  /**
1252
- * Get usage and billing statistics for this bucket key.
1253
- *
1254
- * @param bucketKey Your storage bucket key (`ssk_…`)
952
+ * Get usage and billing stats for this storage key.
1255
953
  *
1256
954
  * @example
1257
- * const { data } = await hydrous.storage.stats('ssk_my_bucket_key');
1258
- * console.log(`${data.totalFiles} files, ${data.totalSizeBytes} bytes stored`);
955
+ * const { data } = await db.storage('main').stats();
956
+ * console.log(data!.totalFiles, data!.totalSizeBytes);
1259
957
  */
1260
- async stats(bucketKey) {
958
+ async stats() {
1261
959
  try {
1262
- const url = buildUrl(this.baseUrl, `storage/${bucketFromKey(bucketKey)}/stats`);
1263
- const res = await fetch(url, { headers: storageHeaders(bucketKey) });
960
+ const res = await fetch(`${this.base}/stats`, {
961
+ headers: storageHeaders(this.key)
962
+ });
1264
963
  const json = await parseResponse(res);
1265
964
  return { data: json.data, error: null };
1266
965
  } catch (err) {
@@ -1269,15 +968,63 @@ var StorageClient = class {
1269
968
  }
1270
969
  };
1271
970
 
971
+ // src/storage/manager.ts
972
+ var StorageManager = class {
973
+ constructor(config) {
974
+ this.cache = /* @__PURE__ */ new Map();
975
+ this.baseUrl = config.url;
976
+ this._keys = config.storageKeys;
977
+ }
978
+ /**
979
+ * Get a storage client scoped to a named key.
980
+ *
981
+ * @param keyName - Must match a property you declared in `storageKeys`
982
+ *
983
+ * @example
984
+ * const avatarStore = db.storage('avatars');
985
+ * const documentStore = db.storage('documents');
986
+ *
987
+ * await avatarStore.upload(file, { path: 'users/alice.jpg' });
988
+ * const list = await documentStore.list({ prefix: 'invoices/' });
989
+ */
990
+ use(keyName) {
991
+ const bucketKey = this._keys[keyName];
992
+ if (!bucketKey) {
993
+ const available = Object.keys(this._keys).join(", ");
994
+ throw new HydrousDBError(
995
+ `Storage key "${keyName}" is not defined.
996
+ Available: ${available || "(none)"}`,
997
+ "UNKNOWN_STORAGE_KEY"
998
+ );
999
+ }
1000
+ if (!this.cache.has(keyName)) {
1001
+ this.cache.set(keyName, new ScopedStorageClient(this.baseUrl, keyName, bucketKey));
1002
+ }
1003
+ return this.cache.get(keyName);
1004
+ }
1005
+ /** Return the names of all configured storage keys */
1006
+ keyNames() {
1007
+ return Object.keys(this._keys);
1008
+ }
1009
+ };
1010
+
1272
1011
  // src/client.ts
1273
1012
  var HydrousClient = class {
1274
1013
  constructor(config) {
1275
- if (!config.url) throw new Error("[Hydrous] config.url is required");
1276
- if (!config.apiKey) throw new Error("[Hydrous] config.apiKey is required");
1014
+ if (!config.url) throw new Error("[HydrousDB] config.url is required");
1015
+ if (!config.authKey) throw new Error("[HydrousDB] config.authKey is required");
1016
+ if (!config.bucketSecurityKey) throw new Error("[HydrousDB] config.bucketSecurityKey is required");
1017
+ if (!config.storageKeys || typeof config.storageKeys !== "object") {
1018
+ throw new Error("[HydrousDB] config.storageKeys must be an object of named keys");
1019
+ }
1277
1020
  this.auth = new AuthClient(config);
1278
1021
  this.records = new RecordsClient(config);
1279
1022
  this.analytics = new AnalyticsClient(config);
1280
- this.storage = new StorageClient(config);
1023
+ const manager = new StorageManager(config);
1024
+ const fn = (keyName) => manager.use(keyName);
1025
+ fn.use = (keyName) => manager.use(keyName);
1026
+ fn.keyNames = () => manager.keyNames();
1027
+ this.storage = fn;
1281
1028
  }
1282
1029
  };
1283
1030
 
@@ -1286,6 +1033,6 @@ function createClient(config) {
1286
1033
  return new HydrousClient(config);
1287
1034
  }
1288
1035
 
1289
- export { AnalyticsClient, AuthClient, HydrousClient, HydrousSDKError, RecordsClient, StorageClient, createClient, eq, gt, inArray, isHydrousError, lt, neq };
1036
+ export { AnalyticsClient, AuthClient, HydrousClient, HydrousDBError, RecordsClient, ScopedStorageClient, StorageManager, createClient, eq, gt, gte, inArray, isHydrousError, lt, lte, neq };
1290
1037
  //# sourceMappingURL=index.mjs.map
1291
1038
  //# sourceMappingURL=index.mjs.map