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