hydrousdb 1.1.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,34 +1,47 @@
1
- import { a9 as HttpClient, G as GetRecordOptions, V as RequestOptions, v as GetRecordResponse, w as GetSnapshotResponse, S as RecordData, Q as QueryOptions, R as QueryResponse, I as InsertRecordPayload, J as InsertRecordResponse, a4 as UpdateRecordPayload, a5 as UpdateRecordResponse, o as DeleteRecordResponse, T as RecordExistsInfo, m as BatchUpdatePayload, n as BatchUpdateResponse, B as BatchDeletePayload, h as BatchDeleteResponse, i as BatchInsertPayload, j as BatchInsertResponse, z as HydrousRecord } from '../http-CIXLF5GV.js';
2
- export { s as Filter, t as FilterOp, y as HistoryEntry } from '../http-CIXLF5GV.js';
1
+ import { a9 as HttpClient, G as GetRecordOptions, V as RequestOptions, v as GetRecordResponse, w as GetSnapshotResponse, S as RecordData, Q as QueryOptions, R as QueryResponse, I as InsertRecordPayload, J as InsertRecordResponse, a4 as UpdateRecordPayload, a5 as UpdateRecordResponse, o as DeleteRecordResponse, T as RecordExistsInfo, m as BatchUpdatePayload, n as BatchUpdateResponse, B as BatchDeletePayload, h as BatchDeleteResponse, i as BatchInsertPayload, j as BatchInsertResponse, z as HydrousRecord } from '../http-DTukpdAU.js';
2
+ export { s as Filter, t as FilterOp, y as HistoryEntry } from '../http-DTukpdAU.js';
3
3
 
4
+ /** Options accepted by every records method that needs a bucket */
5
+ interface BucketOptions {
6
+ /**
7
+ * The bucket to read from or write to.
8
+ * Required on all records and analytics calls.
9
+ *
10
+ * @example
11
+ * await db.records.insert({ values: { name: 'Alice' } }, { bucketKey: 'users' });
12
+ */
13
+ bucketKey: string;
14
+ }
4
15
  declare class RecordsClient {
5
16
  private readonly http;
6
17
  constructor(http: HttpClient);
7
- private get path();
18
+ /** Builds the base path for a given bucket: /api/:bucketKey/:securityKey */
19
+ private path;
8
20
  /**
9
21
  * Fetch a single record by ID.
10
22
  *
11
23
  * @example
12
- * const { data } = await db.records.get('rec_abc123');
13
- * const { data, history } = await db.records.get('rec_abc123', { showHistory: true });
24
+ * const { data } = await db.records.get('rec_abc123', { bucketKey: 'users' });
25
+ * const { data, history } = await db.records.get('rec_abc123', { bucketKey: 'users', showHistory: true });
14
26
  */
15
- get(recordId: string, options?: GetRecordOptions & RequestOptions): Promise<GetRecordResponse>;
27
+ get(recordId: string, options: GetRecordOptions & BucketOptions & RequestOptions): Promise<GetRecordResponse>;
16
28
  /**
17
29
  * Fetch a specific historical version (generation) of a record.
18
30
  *
19
31
  * @example
20
- * const { data } = await db.records.getSnapshot('rec_abc123', '1700000000000000');
32
+ * const { data } = await db.records.getSnapshot('rec_abc123', '1700000000000000', { bucketKey: 'users' });
21
33
  */
22
- getSnapshot(recordId: string, generation: string, opts?: RequestOptions): Promise<GetSnapshotResponse>;
34
+ getSnapshot(recordId: string, generation: string, options: BucketOptions & RequestOptions): Promise<GetSnapshotResponse>;
23
35
  /**
24
36
  * Query a collection with optional filters, sorting, and pagination.
25
37
  *
26
38
  * @example
27
39
  * // Simple query
28
- * const { data, meta } = await db.records.query({ limit: 50, sortOrder: 'desc' });
40
+ * const { data, meta } = await db.records.query({ bucketKey: 'orders', limit: 50 });
29
41
  *
30
42
  * // Filtered query
31
43
  * const { data } = await db.records.query({
44
+ * bucketKey: 'orders',
32
45
  * filters: [{ field: 'status', op: '==', value: 'active' }],
33
46
  * timeScope: '7d',
34
47
  * });
@@ -36,89 +49,89 @@ declare class RecordsClient {
36
49
  * // Paginated
37
50
  * let cursor: string | null = null;
38
51
  * do {
39
- * const result = await db.records.query({ limit: 100, cursor: cursor ?? undefined });
52
+ * const result = await db.records.query({ bucketKey: 'orders', limit: 100, cursor: cursor ?? undefined });
40
53
  * cursor = result.meta.nextCursor;
41
54
  * } while (cursor);
42
55
  */
43
- query<T extends RecordData = RecordData>(options?: QueryOptions & RequestOptions): Promise<QueryResponse<T>>;
56
+ query<T extends RecordData = RecordData>(options: QueryOptions & BucketOptions & RequestOptions): Promise<QueryResponse<T>>;
44
57
  /**
45
- * Insert a new record.
58
+ * Insert a new record into the specified bucket.
46
59
  *
47
60
  * @example
48
- * const { data, meta } = await db.records.insert({
49
- * values: { name: 'Alice', score: 99 },
50
- * queryableFields: ['name'],
51
- * userEmail: 'alice@example.com',
52
- * });
61
+ * const { data, meta } = await db.records.insert(
62
+ * { values: { name: 'Alice', score: 99 }, queryableFields: ['name'] },
63
+ * { bucketKey: 'users' }
64
+ * );
53
65
  */
54
- insert(payload: InsertRecordPayload, opts?: RequestOptions): Promise<InsertRecordResponse>;
66
+ insert(payload: InsertRecordPayload, options: BucketOptions & RequestOptions): Promise<InsertRecordResponse>;
55
67
  /**
56
68
  * Update an existing record.
57
69
  *
58
70
  * @example
59
- * await db.records.update({
60
- * recordId: 'rec_abc123',
61
- * values: { score: 100 },
62
- * track_record_history: true,
63
- * });
71
+ * await db.records.update(
72
+ * { recordId: 'rec_abc123', values: { score: 100 }, track_record_history: true },
73
+ * { bucketKey: 'users' }
74
+ * );
64
75
  */
65
- update(payload: UpdateRecordPayload, opts?: RequestOptions): Promise<UpdateRecordResponse>;
76
+ update(payload: UpdateRecordPayload, options: BucketOptions & RequestOptions): Promise<UpdateRecordResponse>;
66
77
  /**
67
78
  * Delete a record permanently.
68
79
  *
69
80
  * @example
70
- * await db.records.delete('rec_abc123');
81
+ * await db.records.delete('rec_abc123', { bucketKey: 'users' });
71
82
  */
72
- delete(recordId: string, opts?: RequestOptions): Promise<DeleteRecordResponse>;
83
+ delete(recordId: string, options: BucketOptions & RequestOptions): Promise<DeleteRecordResponse>;
73
84
  /**
74
85
  * Check whether a record exists without fetching its full data.
75
86
  * Returns `null` if the record is not found.
76
87
  *
77
88
  * @example
78
- * const info = await db.records.exists('rec_abc123');
89
+ * const info = await db.records.exists('rec_abc123', { bucketKey: 'users' });
79
90
  * if (info?.exists) console.log('found at', info.updatedAt);
80
91
  */
81
- exists(recordId: string, opts?: RequestOptions): Promise<RecordExistsInfo | null>;
92
+ exists(recordId: string, options: BucketOptions & RequestOptions): Promise<RecordExistsInfo | null>;
82
93
  /**
83
94
  * Update up to 500 records in a single request.
84
95
  *
85
96
  * @example
86
- * await db.records.batchUpdate({
87
- * updates: [
88
- * { recordId: 'rec_1', values: { status: 'archived' } },
89
- * { recordId: 'rec_2', values: { status: 'archived' } },
90
- * ],
91
- * });
97
+ * await db.records.batchUpdate(
98
+ * { updates: [{ recordId: 'rec_1', values: { status: 'archived' } }] },
99
+ * { bucketKey: 'orders' }
100
+ * );
92
101
  */
93
- batchUpdate(payload: BatchUpdatePayload, opts?: RequestOptions): Promise<BatchUpdateResponse>;
102
+ batchUpdate(payload: BatchUpdatePayload, options: BucketOptions & RequestOptions): Promise<BatchUpdateResponse>;
94
103
  /**
95
104
  * Delete up to 500 records in a single request.
96
105
  *
97
106
  * @example
98
- * await db.records.batchDelete({ recordIds: ['rec_1', 'rec_2', 'rec_3'] });
107
+ * await db.records.batchDelete(
108
+ * { recordIds: ['rec_1', 'rec_2'] },
109
+ * { bucketKey: 'orders' }
110
+ * );
99
111
  */
100
- batchDelete(payload: BatchDeletePayload, opts?: RequestOptions): Promise<BatchDeleteResponse>;
112
+ batchDelete(payload: BatchDeletePayload, options: BucketOptions & RequestOptions): Promise<BatchDeleteResponse>;
101
113
  /**
102
114
  * Insert up to 500 records in a single request.
103
115
  * Returns HTTP 207 (multi-status) — check `meta.failed` for partial failures.
104
116
  *
105
117
  * @example
106
- * const result = await db.records.batchInsert({
107
- * records: [{ name: 'Alice' }, { name: 'Bob' }],
108
- * queryableFields: ['name'],
109
- * });
118
+ * const result = await db.records.batchInsert(
119
+ * { records: [{ name: 'Alice' }, { name: 'Bob' }], queryableFields: ['name'] },
120
+ * { bucketKey: 'users' }
121
+ * );
110
122
  */
111
- batchInsert(payload: BatchInsertPayload, opts?: RequestOptions): Promise<BatchInsertResponse>;
123
+ batchInsert(payload: BatchInsertPayload, options: BucketOptions & RequestOptions): Promise<BatchInsertResponse>;
112
124
  /**
113
125
  * Fetch ALL records matching a query, automatically following cursors.
114
126
  * Use with care on large collections — prefer `query()` with manual pagination.
115
127
  *
116
128
  * @example
117
129
  * const allRecords = await db.records.queryAll({
130
+ * bucketKey: 'orders',
118
131
  * filters: [{ field: 'type', op: '==', value: 'invoice' }],
119
132
  * });
120
133
  */
121
- queryAll<T extends RecordData = RecordData>(options?: Omit<QueryOptions, 'cursor'> & RequestOptions): Promise<(T & HydrousRecord)[]>;
134
+ queryAll<T extends RecordData = RecordData>(options: Omit<QueryOptions, 'cursor'> & BucketOptions & RequestOptions): Promise<(T & HydrousRecord)[]>;
122
135
  }
123
136
 
124
- export { BatchDeletePayload, BatchDeleteResponse, BatchInsertPayload, BatchInsertResponse, BatchUpdatePayload, BatchUpdateResponse, DeleteRecordResponse, GetRecordOptions, GetRecordResponse, GetSnapshotResponse, HydrousRecord, InsertRecordPayload, InsertRecordResponse, QueryOptions, QueryResponse, RecordData, RecordExistsInfo, RecordsClient, UpdateRecordPayload, UpdateRecordResponse };
137
+ export { BatchDeletePayload, BatchDeleteResponse, BatchInsertPayload, BatchInsertResponse, BatchUpdatePayload, BatchUpdateResponse, type BucketOptions, DeleteRecordResponse, GetRecordOptions, GetRecordResponse, GetSnapshotResponse, HydrousRecord, InsertRecordPayload, InsertRecordResponse, QueryOptions, QueryResponse, RecordData, RecordExistsInfo, RecordsClient, UpdateRecordPayload, UpdateRecordResponse };
@@ -37,31 +37,34 @@ var RecordsClient = class {
37
37
  constructor(http) {
38
38
  this.http = http;
39
39
  }
40
- get path() {
41
- return `/api/${this.http.bucketKey}`;
40
+ /** Builds the base path for a given bucket: /api/:bucketKey/:securityKey */
41
+ path(bucketKey) {
42
+ return `/api/${bucketKey}/${this.http.securityKey}`;
42
43
  }
43
44
  // ── GET — single record ────────────────────────────────────────────────────
44
45
  /**
45
46
  * Fetch a single record by ID.
46
47
  *
47
48
  * @example
48
- * const { data } = await db.records.get('rec_abc123');
49
- * const { data, history } = await db.records.get('rec_abc123', { showHistory: true });
49
+ * const { data } = await db.records.get('rec_abc123', { bucketKey: 'users' });
50
+ * const { data, history } = await db.records.get('rec_abc123', { bucketKey: 'users', showHistory: true });
50
51
  */
51
52
  async get(recordId, options) {
53
+ const { bucketKey, showHistory, ...rest } = options;
52
54
  const params = { recordId };
53
- if (options?.showHistory) params["showHistory"] = "true";
54
- return this.http.get(this.path, params, options);
55
+ if (showHistory) params["showHistory"] = "true";
56
+ return this.http.get(this.path(bucketKey), params, rest);
55
57
  }
56
58
  // ── GET — historical snapshot ──────────────────────────────────────────────
57
59
  /**
58
60
  * Fetch a specific historical version (generation) of a record.
59
61
  *
60
62
  * @example
61
- * const { data } = await db.records.getSnapshot('rec_abc123', '1700000000000000');
63
+ * const { data } = await db.records.getSnapshot('rec_abc123', '1700000000000000', { bucketKey: 'users' });
62
64
  */
63
- async getSnapshot(recordId, generation, opts) {
64
- return this.http.get(this.path, { recordId, generation }, opts);
65
+ async getSnapshot(recordId, generation, options) {
66
+ const { bucketKey, ...rest } = options;
67
+ return this.http.get(this.path(bucketKey), { recordId, generation }, rest);
65
68
  }
66
69
  // ── GET — collection query ─────────────────────────────────────────────────
67
70
  /**
@@ -69,10 +72,11 @@ var RecordsClient = class {
69
72
  *
70
73
  * @example
71
74
  * // Simple query
72
- * const { data, meta } = await db.records.query({ limit: 50, sortOrder: 'desc' });
75
+ * const { data, meta } = await db.records.query({ bucketKey: 'orders', limit: 50 });
73
76
  *
74
77
  * // Filtered query
75
78
  * const { data } = await db.records.query({
79
+ * bucketKey: 'orders',
76
80
  * filters: [{ field: 'status', op: '==', value: 'active' }],
77
81
  * timeScope: '7d',
78
82
  * });
@@ -80,52 +84,54 @@ var RecordsClient = class {
80
84
  * // Paginated
81
85
  * let cursor: string | null = null;
82
86
  * do {
83
- * const result = await db.records.query({ limit: 100, cursor: cursor ?? undefined });
87
+ * const result = await db.records.query({ bucketKey: 'orders', limit: 100, cursor: cursor ?? undefined });
84
88
  * cursor = result.meta.nextCursor;
85
89
  * } while (cursor);
86
90
  */
87
91
  async query(options) {
88
- if (options?.filters) validateFilters(options.filters);
89
- const params = buildQueryParams(options ?? {});
90
- return this.http.get(this.path, params, options);
92
+ const { bucketKey, ...rest } = options;
93
+ if (rest.filters) validateFilters(rest.filters);
94
+ const params = buildQueryParams(rest);
95
+ return this.http.get(this.path(bucketKey), params, rest);
91
96
  }
92
97
  // ── POST — insert ──────────────────────────────────────────────────────────
93
98
  /**
94
- * Insert a new record.
99
+ * Insert a new record into the specified bucket.
95
100
  *
96
101
  * @example
97
- * const { data, meta } = await db.records.insert({
98
- * values: { name: 'Alice', score: 99 },
99
- * queryableFields: ['name'],
100
- * userEmail: 'alice@example.com',
101
- * });
102
+ * const { data, meta } = await db.records.insert(
103
+ * { values: { name: 'Alice', score: 99 }, queryableFields: ['name'] },
104
+ * { bucketKey: 'users' }
105
+ * );
102
106
  */
103
- async insert(payload, opts) {
104
- return this.http.post(this.path, payload, opts);
107
+ async insert(payload, options) {
108
+ const { bucketKey, ...rest } = options;
109
+ return this.http.post(this.path(bucketKey), payload, rest);
105
110
  }
106
111
  // ── PATCH — update ─────────────────────────────────────────────────────────
107
112
  /**
108
113
  * Update an existing record.
109
114
  *
110
115
  * @example
111
- * await db.records.update({
112
- * recordId: 'rec_abc123',
113
- * values: { score: 100 },
114
- * track_record_history: true,
115
- * });
116
+ * await db.records.update(
117
+ * { recordId: 'rec_abc123', values: { score: 100 }, track_record_history: true },
118
+ * { bucketKey: 'users' }
119
+ * );
116
120
  */
117
- async update(payload, opts) {
118
- return this.http.patch(this.path, payload, opts);
121
+ async update(payload, options) {
122
+ const { bucketKey, ...rest } = options;
123
+ return this.http.patch(this.path(bucketKey), payload, rest);
119
124
  }
120
125
  // ── DELETE ─────────────────────────────────────────────────────────────────
121
126
  /**
122
127
  * Delete a record permanently.
123
128
  *
124
129
  * @example
125
- * await db.records.delete('rec_abc123');
130
+ * await db.records.delete('rec_abc123', { bucketKey: 'users' });
126
131
  */
127
- async delete(recordId, opts) {
128
- return this.http.delete(this.path, { recordId }, opts);
132
+ async delete(recordId, options) {
133
+ const { bucketKey, ...rest } = options;
134
+ return this.http.delete(this.path(bucketKey), { recordId }, rest);
129
135
  }
130
136
  // ── HEAD — existence check ─────────────────────────────────────────────────
131
137
  /**
@@ -133,11 +139,12 @@ var RecordsClient = class {
133
139
  * Returns `null` if the record is not found.
134
140
  *
135
141
  * @example
136
- * const info = await db.records.exists('rec_abc123');
142
+ * const info = await db.records.exists('rec_abc123', { bucketKey: 'users' });
137
143
  * if (info?.exists) console.log('found at', info.updatedAt);
138
144
  */
139
- async exists(recordId, opts) {
140
- const res = await this.http.head(this.path, { recordId }, opts);
145
+ async exists(recordId, options) {
146
+ const { bucketKey, ...rest } = options;
147
+ const res = await this.http.head(this.path(bucketKey), { recordId }, rest);
141
148
  if (res.status === 404) return null;
142
149
  if (!res.ok) return null;
143
150
  return {
@@ -153,33 +160,28 @@ var RecordsClient = class {
153
160
  * Update up to 500 records in a single request.
154
161
  *
155
162
  * @example
156
- * await db.records.batchUpdate({
157
- * updates: [
158
- * { recordId: 'rec_1', values: { status: 'archived' } },
159
- * { recordId: 'rec_2', values: { status: 'archived' } },
160
- * ],
161
- * });
163
+ * await db.records.batchUpdate(
164
+ * { updates: [{ recordId: 'rec_1', values: { status: 'archived' } }] },
165
+ * { bucketKey: 'orders' }
166
+ * );
162
167
  */
163
- async batchUpdate(payload, opts) {
164
- return this.http.post(
165
- `${this.path}/batch/update`,
166
- payload,
167
- opts
168
- );
168
+ async batchUpdate(payload, options) {
169
+ const { bucketKey, ...rest } = options;
170
+ return this.http.post(`${this.path(bucketKey)}/batch/update`, payload, rest);
169
171
  }
170
172
  // ── Batch — delete ─────────────────────────────────────────────────────────
171
173
  /**
172
174
  * Delete up to 500 records in a single request.
173
175
  *
174
176
  * @example
175
- * await db.records.batchDelete({ recordIds: ['rec_1', 'rec_2', 'rec_3'] });
177
+ * await db.records.batchDelete(
178
+ * { recordIds: ['rec_1', 'rec_2'] },
179
+ * { bucketKey: 'orders' }
180
+ * );
176
181
  */
177
- async batchDelete(payload, opts) {
178
- return this.http.post(
179
- `${this.path}/batch/delete`,
180
- payload,
181
- opts
182
- );
182
+ async batchDelete(payload, options) {
183
+ const { bucketKey, ...rest } = options;
184
+ return this.http.post(`${this.path(bucketKey)}/batch/delete`, payload, rest);
183
185
  }
184
186
  // ── Batch — insert ─────────────────────────────────────────────────────────
185
187
  /**
@@ -187,17 +189,14 @@ var RecordsClient = class {
187
189
  * Returns HTTP 207 (multi-status) — check `meta.failed` for partial failures.
188
190
  *
189
191
  * @example
190
- * const result = await db.records.batchInsert({
191
- * records: [{ name: 'Alice' }, { name: 'Bob' }],
192
- * queryableFields: ['name'],
193
- * });
192
+ * const result = await db.records.batchInsert(
193
+ * { records: [{ name: 'Alice' }, { name: 'Bob' }], queryableFields: ['name'] },
194
+ * { bucketKey: 'users' }
195
+ * );
194
196
  */
195
- async batchInsert(payload, opts) {
196
- return this.http.post(
197
- `${this.path}/batch/insert`,
198
- payload,
199
- opts
200
- );
197
+ async batchInsert(payload, options) {
198
+ const { bucketKey, ...rest } = options;
199
+ return this.http.post(`${this.path(bucketKey)}/batch/insert`, payload, rest);
201
200
  }
202
201
  // ── Helpers ────────────────────────────────────────────────────────────────
203
202
  /**
@@ -206,6 +205,7 @@ var RecordsClient = class {
206
205
  *
207
206
  * @example
208
207
  * const allRecords = await db.records.queryAll({
208
+ * bucketKey: 'orders',
209
209
  * filters: [{ field: 'type', op: '==', value: 'invoice' }],
210
210
  * });
211
211
  */
@@ -213,7 +213,9 @@ var RecordsClient = class {
213
213
  const all = [];
214
214
  let cursor = null;
215
215
  do {
216
- const result = await this.query(cursor ? { ...options, cursor } : { ...options });
216
+ const result = await this.query(
217
+ cursor ? { ...options, cursor } : { ...options }
218
+ );
217
219
  all.push(...result.data);
218
220
  cursor = result.meta.nextCursor ?? null;
219
221
  } while (cursor);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utils/query.ts","../../src/records/client.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { Filter, QueryOptions } from '../types/index.js';\n\n/**\n * Converts a QueryOptions object into a flat Record suitable for URLSearchParams.\n *\n * Filter fields use the `field[op]=value` convention:\n * { field: 'status', op: '==', value: 'active' } → status=active\n * { field: 'score', op: '>', value: 50 } → score[>]=50\n */\nexport function buildQueryParams(\n opts: QueryOptions\n): Record<string, string | number | boolean> {\n const params: Record<string, string | number | boolean> = {};\n\n if (opts.timeScope) params['timeScope'] = opts.timeScope;\n if (opts.year) params['year'] = opts.year;\n if (opts.sortOrder) params['sortOrder'] = opts.sortOrder;\n if (opts.limit) params['limit'] = opts.limit;\n if (opts.cursor) params['cursor'] = opts.cursor;\n if (opts.fields) params['fields'] = opts.fields;\n\n for (const filter of opts.filters ?? []) {\n const paramKey = filter.op === '==' ? filter.field : `${filter.field}[${filter.op}]`;\n params[paramKey] = filter.value as string | number | boolean;\n }\n\n return params;\n}\n\n/**\n * Validates a filter array and throws a descriptive error if invalid.\n */\nexport function validateFilters(filters: Filter[]): void {\n const VALID_OPS = new Set(['==', '!=', '>', '<', '>=', '<=', 'contains']);\n\n if (filters.length > 3) {\n throw new Error('HydrousDB supports a maximum of 3 filters per query.');\n }\n\n for (const f of filters) {\n if (!VALID_OPS.has(f.op)) {\n throw new Error(\n `Invalid filter operator \"${f.op}\". Valid operators: ${[...VALID_OPS].join(', ')}`\n );\n }\n if (!f.field || typeof f.field !== 'string') {\n throw new Error('Each filter must have a non-empty \"field\" string.');\n }\n }\n}\n","import type { HttpClient } from '../utils/http.js';\nimport { buildQueryParams, validateFilters } from '../utils/query.js';\nimport type {\n GetRecordOptions,\n GetRecordResponse,\n GetSnapshotResponse,\n QueryOptions,\n QueryResponse,\n InsertRecordPayload,\n InsertRecordResponse,\n UpdateRecordPayload,\n UpdateRecordResponse,\n DeleteRecordResponse,\n RecordExistsInfo,\n BatchUpdatePayload,\n BatchUpdateResponse,\n BatchDeletePayload,\n BatchDeleteResponse,\n BatchInsertPayload,\n BatchInsertResponse,\n RecordData,\n HydrousRecord,\n RequestOptions,\n} from '../types/index.js';\n\nexport class RecordsClient {\n constructor(private readonly http: HttpClient) { }\n\n private get path() {\n return `/api/${this.http.bucketKey}`;\n }\n\n // ── GET — single record ────────────────────────────────────────────────────\n\n /**\n * Fetch a single record by ID.\n *\n * @example\n * const { data } = await db.records.get('rec_abc123');\n * const { data, history } = await db.records.get('rec_abc123', { showHistory: true });\n */\n async get(recordId: string, options?: GetRecordOptions & RequestOptions): Promise<GetRecordResponse> {\n const params: Record<string, string | number | boolean | undefined | null> = { recordId };\n if (options?.showHistory) params['showHistory'] = 'true';\n return this.http.get<GetRecordResponse>(this.path, params, options);\n }\n\n // ── GET — historical snapshot ──────────────────────────────────────────────\n\n /**\n * Fetch a specific historical version (generation) of a record.\n *\n * @example\n * const { data } = await db.records.getSnapshot('rec_abc123', '1700000000000000');\n */\n async getSnapshot(recordId: string, generation: string, opts?: RequestOptions): Promise<GetSnapshotResponse> {\n return this.http.get<GetSnapshotResponse>(this.path, { recordId, generation }, opts);\n }\n\n // ── GET — collection query ─────────────────────────────────────────────────\n\n /**\n * Query a collection with optional filters, sorting, and pagination.\n *\n * @example\n * // Simple query\n * const { data, meta } = await db.records.query({ limit: 50, sortOrder: 'desc' });\n *\n * // Filtered query\n * const { data } = await db.records.query({\n * filters: [{ field: 'status', op: '==', value: 'active' }],\n * timeScope: '7d',\n * });\n *\n * // Paginated\n * let cursor: string | null = null;\n * do {\n * const result = await db.records.query({ limit: 100, cursor: cursor ?? undefined });\n * cursor = result.meta.nextCursor;\n * } while (cursor);\n */\n async query<T extends RecordData = RecordData>(\n options?: QueryOptions & RequestOptions\n ): Promise<QueryResponse<T>> {\n if (options?.filters) validateFilters(options.filters);\n const params = buildQueryParams(options ?? {});\n return this.http.get<QueryResponse<T>>(this.path, params, options);\n }\n\n // ── POST — insert ──────────────────────────────────────────────────────────\n\n /**\n * Insert a new record.\n *\n * @example\n * const { data, meta } = await db.records.insert({\n * values: { name: 'Alice', score: 99 },\n * queryableFields: ['name'],\n * userEmail: 'alice@example.com',\n * });\n */\n async insert(payload: InsertRecordPayload, opts?: RequestOptions): Promise<InsertRecordResponse> {\n return this.http.post<InsertRecordResponse>(this.path, payload, opts);\n }\n\n // ── PATCH — update ─────────────────────────────────────────────────────────\n\n /**\n * Update an existing record.\n *\n * @example\n * await db.records.update({\n * recordId: 'rec_abc123',\n * values: { score: 100 },\n * track_record_history: true,\n * });\n */\n async update(payload: UpdateRecordPayload, opts?: RequestOptions): Promise<UpdateRecordResponse> {\n return this.http.patch<UpdateRecordResponse>(this.path, payload, opts);\n }\n\n // ── DELETE ─────────────────────────────────────────────────────────────────\n\n /**\n * Delete a record permanently.\n *\n * @example\n * await db.records.delete('rec_abc123');\n */\n async delete(recordId: string, opts?: RequestOptions): Promise<DeleteRecordResponse> {\n return this.http.delete<DeleteRecordResponse>(this.path, { recordId }, opts);\n }\n\n // ── HEAD — existence check ─────────────────────────────────────────────────\n\n /**\n * Check whether a record exists without fetching its full data.\n * Returns `null` if the record is not found.\n *\n * @example\n * const info = await db.records.exists('rec_abc123');\n * if (info?.exists) console.log('found at', info.updatedAt);\n */\n async exists(recordId: string, opts?: RequestOptions): Promise<RecordExistsInfo | null> {\n const res = await this.http.head(this.path, { recordId }, opts);\n\n if (res.status === 404) return null;\n if (!res.ok) return null;\n\n return {\n exists: true,\n id: res.headers.get('X-Record-Id') ?? recordId,\n createdAt: res.headers.get('X-Created-At') ?? '',\n updatedAt: res.headers.get('X-Updated-At') ?? '',\n sizeBytes: res.headers.get('X-Size-Bytes') ?? '',\n };\n }\n\n // ── Batch — update ─────────────────────────────────────────────────────────\n\n /**\n * Update up to 500 records in a single request.\n *\n * @example\n * await db.records.batchUpdate({\n * updates: [\n * { recordId: 'rec_1', values: { status: 'archived' } },\n * { recordId: 'rec_2', values: { status: 'archived' } },\n * ],\n * });\n */\n async batchUpdate(payload: BatchUpdatePayload, opts?: RequestOptions): Promise<BatchUpdateResponse> {\n return this.http.post<BatchUpdateResponse>(\n `${this.path}/batch/update`,\n payload,\n opts\n );\n }\n\n // ── Batch — delete ─────────────────────────────────────────────────────────\n\n /**\n * Delete up to 500 records in a single request.\n *\n * @example\n * await db.records.batchDelete({ recordIds: ['rec_1', 'rec_2', 'rec_3'] });\n */\n async batchDelete(payload: BatchDeletePayload, opts?: RequestOptions): Promise<BatchDeleteResponse> {\n return this.http.post<BatchDeleteResponse>(\n `${this.path}/batch/delete`,\n payload,\n opts\n );\n }\n\n // ── Batch — insert ─────────────────────────────────────────────────────────\n\n /**\n * Insert up to 500 records in a single request.\n * Returns HTTP 207 (multi-status) — check `meta.failed` for partial failures.\n *\n * @example\n * const result = await db.records.batchInsert({\n * records: [{ name: 'Alice' }, { name: 'Bob' }],\n * queryableFields: ['name'],\n * });\n */\n async batchInsert(payload: BatchInsertPayload, opts?: RequestOptions): Promise<BatchInsertResponse> {\n return this.http.post<BatchInsertResponse>(\n `${this.path}/batch/insert`,\n payload,\n opts\n );\n }\n\n // ── Helpers ────────────────────────────────────────────────────────────────\n\n /**\n * Fetch ALL records matching a query, automatically following cursors.\n * Use with care on large collections — prefer `query()` with manual pagination.\n *\n * @example\n * const allRecords = await db.records.queryAll({\n * filters: [{ field: 'type', op: '==', value: 'invoice' }],\n * });\n */\n async queryAll<T extends RecordData = RecordData>(\n options?: Omit<QueryOptions, 'cursor'> & RequestOptions\n ): Promise<(T & HydrousRecord)[]> {\n const all: (T & HydrousRecord)[] = [];\n let cursor: string | null = null;\n\n do {\n const result: any = await this.query<T>(cursor ? { ...options, cursor } : { ...options });\n all.push(...result.data);\n cursor = result.meta.nextCursor ?? null;\n } while (cursor);\n\n return all;\n }\n}\n"]}
1
+ {"version":3,"sources":["../../src/utils/query.ts","../../src/records/client.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { Filter, QueryOptions } from '../types/index.js';\n\n/**\n * Converts a QueryOptions object into a flat Record suitable for URLSearchParams.\n *\n * Filter fields use the `field[op]=value` convention:\n * { field: 'status', op: '==', value: 'active' } → status=active\n * { field: 'score', op: '>', value: 50 } → score[>]=50\n */\nexport function buildQueryParams(\n opts: QueryOptions\n): Record<string, string | number | boolean> {\n const params: Record<string, string | number | boolean> = {};\n\n if (opts.timeScope) params['timeScope'] = opts.timeScope;\n if (opts.year) params['year'] = opts.year;\n if (opts.sortOrder) params['sortOrder'] = opts.sortOrder;\n if (opts.limit) params['limit'] = opts.limit;\n if (opts.cursor) params['cursor'] = opts.cursor;\n if (opts.fields) params['fields'] = opts.fields;\n\n for (const filter of opts.filters ?? []) {\n const paramKey = filter.op === '==' ? filter.field : `${filter.field}[${filter.op}]`;\n params[paramKey] = filter.value as string | number | boolean;\n }\n\n return params;\n}\n\n/**\n * Validates a filter array and throws a descriptive error if invalid.\n */\nexport function validateFilters(filters: Filter[]): void {\n const VALID_OPS = new Set(['==', '!=', '>', '<', '>=', '<=', 'contains']);\n\n if (filters.length > 3) {\n throw new Error('HydrousDB supports a maximum of 3 filters per query.');\n }\n\n for (const f of filters) {\n if (!VALID_OPS.has(f.op)) {\n throw new Error(\n `Invalid filter operator \"${f.op}\". Valid operators: ${[...VALID_OPS].join(', ')}`\n );\n }\n if (!f.field || typeof f.field !== 'string') {\n throw new Error('Each filter must have a non-empty \"field\" string.');\n }\n }\n}\n","import type { HttpClient } from '../utils/http.js';\nimport { buildQueryParams, validateFilters } from '../utils/query.js';\nimport type {\n GetRecordOptions,\n GetRecordResponse,\n GetSnapshotResponse,\n QueryOptions,\n QueryResponse,\n InsertRecordPayload,\n InsertRecordResponse,\n UpdateRecordPayload,\n UpdateRecordResponse,\n DeleteRecordResponse,\n RecordExistsInfo,\n BatchUpdatePayload,\n BatchUpdateResponse,\n BatchDeletePayload,\n BatchDeleteResponse,\n BatchInsertPayload,\n BatchInsertResponse,\n RecordData,\n HydrousRecord,\n RequestOptions,\n} from '../types/index.js';\n\n/** Options accepted by every records method that needs a bucket */\nexport interface BucketOptions {\n /**\n * The bucket to read from or write to.\n * Required on all records and analytics calls.\n *\n * @example\n * await db.records.insert({ values: { name: 'Alice' } }, { bucketKey: 'users' });\n */\n bucketKey: string;\n}\n\nexport class RecordsClient {\n constructor(private readonly http: HttpClient) {}\n\n /** Builds the base path for a given bucket: /api/:bucketKey/:securityKey */\n private path(bucketKey: string): string {\n return `/api/${bucketKey}/${this.http.securityKey}`;\n }\n\n // ── GET — single record ────────────────────────────────────────────────────\n\n /**\n * Fetch a single record by ID.\n *\n * @example\n * const { data } = await db.records.get('rec_abc123', { bucketKey: 'users' });\n * const { data, history } = await db.records.get('rec_abc123', { bucketKey: 'users', showHistory: true });\n */\n async get(\n recordId: string,\n options: GetRecordOptions & BucketOptions & RequestOptions\n ): Promise<GetRecordResponse> {\n const { bucketKey, showHistory, ...rest } = options;\n const params: Record<string, string | number | boolean | undefined | null> = { recordId };\n if (showHistory) params['showHistory'] = 'true';\n return this.http.get<GetRecordResponse>(this.path(bucketKey), params, rest);\n }\n\n // ── GET — historical snapshot ──────────────────────────────────────────────\n\n /**\n * Fetch a specific historical version (generation) of a record.\n *\n * @example\n * const { data } = await db.records.getSnapshot('rec_abc123', '1700000000000000', { bucketKey: 'users' });\n */\n async getSnapshot(\n recordId: string,\n generation: string,\n options: BucketOptions & RequestOptions\n ): Promise<GetSnapshotResponse> {\n const { bucketKey, ...rest } = options;\n return this.http.get<GetSnapshotResponse>(this.path(bucketKey), { recordId, generation }, rest);\n }\n\n // ── GET — collection query ─────────────────────────────────────────────────\n\n /**\n * Query a collection with optional filters, sorting, and pagination.\n *\n * @example\n * // Simple query\n * const { data, meta } = await db.records.query({ bucketKey: 'orders', limit: 50 });\n *\n * // Filtered query\n * const { data } = await db.records.query({\n * bucketKey: 'orders',\n * filters: [{ field: 'status', op: '==', value: 'active' }],\n * timeScope: '7d',\n * });\n *\n * // Paginated\n * let cursor: string | null = null;\n * do {\n * const result = await db.records.query({ bucketKey: 'orders', limit: 100, cursor: cursor ?? undefined });\n * cursor = result.meta.nextCursor;\n * } while (cursor);\n */\n async query<T extends RecordData = RecordData>(\n options: QueryOptions & BucketOptions & RequestOptions\n ): Promise<QueryResponse<T>> {\n const { bucketKey, ...rest } = options;\n if (rest.filters) validateFilters(rest.filters);\n const params = buildQueryParams(rest);\n return this.http.get<QueryResponse<T>>(this.path(bucketKey), params, rest);\n }\n\n // ── POST — insert ──────────────────────────────────────────────────────────\n\n /**\n * Insert a new record into the specified bucket.\n *\n * @example\n * const { data, meta } = await db.records.insert(\n * { values: { name: 'Alice', score: 99 }, queryableFields: ['name'] },\n * { bucketKey: 'users' }\n * );\n */\n async insert(\n payload: InsertRecordPayload,\n options: BucketOptions & RequestOptions\n ): Promise<InsertRecordResponse> {\n const { bucketKey, ...rest } = options;\n return this.http.post<InsertRecordResponse>(this.path(bucketKey), payload, rest);\n }\n\n // ── PATCH — update ─────────────────────────────────────────────────────────\n\n /**\n * Update an existing record.\n *\n * @example\n * await db.records.update(\n * { recordId: 'rec_abc123', values: { score: 100 }, track_record_history: true },\n * { bucketKey: 'users' }\n * );\n */\n async update(\n payload: UpdateRecordPayload,\n options: BucketOptions & RequestOptions\n ): Promise<UpdateRecordResponse> {\n const { bucketKey, ...rest } = options;\n return this.http.patch<UpdateRecordResponse>(this.path(bucketKey), payload, rest);\n }\n\n // ── DELETE ─────────────────────────────────────────────────────────────────\n\n /**\n * Delete a record permanently.\n *\n * @example\n * await db.records.delete('rec_abc123', { bucketKey: 'users' });\n */\n async delete(\n recordId: string,\n options: BucketOptions & RequestOptions\n ): Promise<DeleteRecordResponse> {\n const { bucketKey, ...rest } = options;\n return this.http.delete<DeleteRecordResponse>(this.path(bucketKey), { recordId }, rest);\n }\n\n // ── HEAD — existence check ─────────────────────────────────────────────────\n\n /**\n * Check whether a record exists without fetching its full data.\n * Returns `null` if the record is not found.\n *\n * @example\n * const info = await db.records.exists('rec_abc123', { bucketKey: 'users' });\n * if (info?.exists) console.log('found at', info.updatedAt);\n */\n async exists(\n recordId: string,\n options: BucketOptions & RequestOptions\n ): Promise<RecordExistsInfo | null> {\n const { bucketKey, ...rest } = options;\n const res = await this.http.head(this.path(bucketKey), { recordId }, rest);\n\n if (res.status === 404) return null;\n if (!res.ok) return null;\n\n return {\n exists: true,\n id: res.headers.get('X-Record-Id') ?? recordId,\n createdAt: res.headers.get('X-Created-At') ?? '',\n updatedAt: res.headers.get('X-Updated-At') ?? '',\n sizeBytes: res.headers.get('X-Size-Bytes') ?? '',\n };\n }\n\n // ── Batch — update ─────────────────────────────────────────────────────────\n\n /**\n * Update up to 500 records in a single request.\n *\n * @example\n * await db.records.batchUpdate(\n * { updates: [{ recordId: 'rec_1', values: { status: 'archived' } }] },\n * { bucketKey: 'orders' }\n * );\n */\n async batchUpdate(\n payload: BatchUpdatePayload,\n options: BucketOptions & RequestOptions\n ): Promise<BatchUpdateResponse> {\n const { bucketKey, ...rest } = options;\n return this.http.post<BatchUpdateResponse>(`${this.path(bucketKey)}/batch/update`, payload, rest);\n }\n\n // ── Batch — delete ─────────────────────────────────────────────────────────\n\n /**\n * Delete up to 500 records in a single request.\n *\n * @example\n * await db.records.batchDelete(\n * { recordIds: ['rec_1', 'rec_2'] },\n * { bucketKey: 'orders' }\n * );\n */\n async batchDelete(\n payload: BatchDeletePayload,\n options: BucketOptions & RequestOptions\n ): Promise<BatchDeleteResponse> {\n const { bucketKey, ...rest } = options;\n return this.http.post<BatchDeleteResponse>(`${this.path(bucketKey)}/batch/delete`, payload, rest);\n }\n\n // ── Batch — insert ─────────────────────────────────────────────────────────\n\n /**\n * Insert up to 500 records in a single request.\n * Returns HTTP 207 (multi-status) — check `meta.failed` for partial failures.\n *\n * @example\n * const result = await db.records.batchInsert(\n * { records: [{ name: 'Alice' }, { name: 'Bob' }], queryableFields: ['name'] },\n * { bucketKey: 'users' }\n * );\n */\n async batchInsert(\n payload: BatchInsertPayload,\n options: BucketOptions & RequestOptions\n ): Promise<BatchInsertResponse> {\n const { bucketKey, ...rest } = options;\n return this.http.post<BatchInsertResponse>(`${this.path(bucketKey)}/batch/insert`, payload, rest);\n }\n\n // ── Helpers ────────────────────────────────────────────────────────────────\n\n /**\n * Fetch ALL records matching a query, automatically following cursors.\n * Use with care on large collections — prefer `query()` with manual pagination.\n *\n * @example\n * const allRecords = await db.records.queryAll({\n * bucketKey: 'orders',\n * filters: [{ field: 'type', op: '==', value: 'invoice' }],\n * });\n */\n async queryAll<T extends RecordData = RecordData>(\n options: Omit<QueryOptions, 'cursor'> & BucketOptions & RequestOptions\n ): Promise<(T & HydrousRecord)[]> {\n const all: (T & HydrousRecord)[] = [];\n let cursor: string | null = null;\n\n do {\n const result: any = await this.query<T>(\n cursor ? { ...options, cursor } : { ...options }\n );\n all.push(...result.data);\n cursor = result.meta.nextCursor ?? null;\n } while (cursor);\n\n return all;\n }\n}\n"]}