hydrousdb 1.1.1 → 2.0.1

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