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