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.
- package/README.md +224 -133
- package/dist/analytics/index.d.mts +64 -57
- package/dist/analytics/index.d.ts +64 -57
- package/dist/analytics/index.js +57 -94
- package/dist/analytics/index.js.map +1 -1
- package/dist/analytics/index.mjs +57 -94
- package/dist/analytics/index.mjs.map +1 -1
- package/dist/auth/index.d.mts +2 -2
- package/dist/auth/index.d.ts +2 -2
- package/dist/auth/index.js +1 -1
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/index.mjs +1 -1
- package/dist/auth/index.mjs.map +1 -1
- package/dist/{http-CIXLF5GV.d.mts → http-DTukpdAU.d.mts} +15 -11
- package/dist/{http-CIXLF5GV.d.ts → http-DTukpdAU.d.ts} +15 -11
- package/dist/index.d.mts +16 -10
- package/dist/index.d.ts +16 -10
- package/dist/index.js +129 -164
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +129 -164
- package/dist/index.mjs.map +1 -1
- package/dist/records/index.d.mts +57 -44
- package/dist/records/index.d.ts +57 -44
- package/dist/records/index.js +67 -65
- package/dist/records/index.js.map +1 -1
- package/dist/records/index.mjs +67 -65
- package/dist/records/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/records/index.mjs
CHANGED
|
@@ -35,31 +35,34 @@ var RecordsClient = class {
|
|
|
35
35
|
constructor(http) {
|
|
36
36
|
this.http = http;
|
|
37
37
|
}
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
/** Builds the base path for a given bucket: /api/:bucketKey/:securityKey */
|
|
39
|
+
path(bucketKey) {
|
|
40
|
+
return `/api/${bucketKey}/${this.http.securityKey}`;
|
|
40
41
|
}
|
|
41
42
|
// ── GET — single record ────────────────────────────────────────────────────
|
|
42
43
|
/**
|
|
43
44
|
* Fetch a single record by ID.
|
|
44
45
|
*
|
|
45
46
|
* @example
|
|
46
|
-
* const { data } = await db.records.get('rec_abc123');
|
|
47
|
-
* const { data, history } = await db.records.get('rec_abc123', { showHistory: true });
|
|
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 });
|
|
48
49
|
*/
|
|
49
50
|
async get(recordId, options) {
|
|
51
|
+
const { bucketKey, showHistory, ...rest } = options;
|
|
50
52
|
const params = { recordId };
|
|
51
|
-
if (
|
|
52
|
-
return this.http.get(this.path, params,
|
|
53
|
+
if (showHistory) params["showHistory"] = "true";
|
|
54
|
+
return this.http.get(this.path(bucketKey), params, rest);
|
|
53
55
|
}
|
|
54
56
|
// ── GET — historical snapshot ──────────────────────────────────────────────
|
|
55
57
|
/**
|
|
56
58
|
* Fetch a specific historical version (generation) of a record.
|
|
57
59
|
*
|
|
58
60
|
* @example
|
|
59
|
-
* const { data } = await db.records.getSnapshot('rec_abc123', '1700000000000000');
|
|
61
|
+
* const { data } = await db.records.getSnapshot('rec_abc123', '1700000000000000', { bucketKey: 'users' });
|
|
60
62
|
*/
|
|
61
|
-
async getSnapshot(recordId, generation,
|
|
62
|
-
|
|
63
|
+
async getSnapshot(recordId, generation, options) {
|
|
64
|
+
const { bucketKey, ...rest } = options;
|
|
65
|
+
return this.http.get(this.path(bucketKey), { recordId, generation }, rest);
|
|
63
66
|
}
|
|
64
67
|
// ── GET — collection query ─────────────────────────────────────────────────
|
|
65
68
|
/**
|
|
@@ -67,10 +70,11 @@ var RecordsClient = class {
|
|
|
67
70
|
*
|
|
68
71
|
* @example
|
|
69
72
|
* // Simple query
|
|
70
|
-
* const { data, meta } = await db.records.query({
|
|
73
|
+
* const { data, meta } = await db.records.query({ bucketKey: 'orders', limit: 50 });
|
|
71
74
|
*
|
|
72
75
|
* // Filtered query
|
|
73
76
|
* const { data } = await db.records.query({
|
|
77
|
+
* bucketKey: 'orders',
|
|
74
78
|
* filters: [{ field: 'status', op: '==', value: 'active' }],
|
|
75
79
|
* timeScope: '7d',
|
|
76
80
|
* });
|
|
@@ -78,52 +82,54 @@ var RecordsClient = class {
|
|
|
78
82
|
* // Paginated
|
|
79
83
|
* let cursor: string | null = null;
|
|
80
84
|
* do {
|
|
81
|
-
* const result = await db.records.query({ limit: 100, cursor: cursor ?? undefined });
|
|
85
|
+
* const result = await db.records.query({ bucketKey: 'orders', limit: 100, cursor: cursor ?? undefined });
|
|
82
86
|
* cursor = result.meta.nextCursor;
|
|
83
87
|
* } while (cursor);
|
|
84
88
|
*/
|
|
85
89
|
async query(options) {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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);
|
|
89
94
|
}
|
|
90
95
|
// ── POST — insert ──────────────────────────────────────────────────────────
|
|
91
96
|
/**
|
|
92
|
-
* Insert a new record.
|
|
97
|
+
* Insert a new record into the specified bucket.
|
|
93
98
|
*
|
|
94
99
|
* @example
|
|
95
|
-
* const { data, meta } = await db.records.insert(
|
|
96
|
-
* values: { name: 'Alice', score: 99 },
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
* });
|
|
100
|
+
* const { data, meta } = await db.records.insert(
|
|
101
|
+
* { values: { name: 'Alice', score: 99 }, queryableFields: ['name'] },
|
|
102
|
+
* { bucketKey: 'users' }
|
|
103
|
+
* );
|
|
100
104
|
*/
|
|
101
|
-
async insert(payload,
|
|
102
|
-
|
|
105
|
+
async insert(payload, options) {
|
|
106
|
+
const { bucketKey, ...rest } = options;
|
|
107
|
+
return this.http.post(this.path(bucketKey), payload, rest);
|
|
103
108
|
}
|
|
104
109
|
// ── PATCH — update ─────────────────────────────────────────────────────────
|
|
105
110
|
/**
|
|
106
111
|
* Update an existing record.
|
|
107
112
|
*
|
|
108
113
|
* @example
|
|
109
|
-
* await db.records.update(
|
|
110
|
-
* recordId: 'rec_abc123',
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
* });
|
|
114
|
+
* await db.records.update(
|
|
115
|
+
* { recordId: 'rec_abc123', values: { score: 100 }, track_record_history: true },
|
|
116
|
+
* { bucketKey: 'users' }
|
|
117
|
+
* );
|
|
114
118
|
*/
|
|
115
|
-
async update(payload,
|
|
116
|
-
|
|
119
|
+
async update(payload, options) {
|
|
120
|
+
const { bucketKey, ...rest } = options;
|
|
121
|
+
return this.http.patch(this.path(bucketKey), payload, rest);
|
|
117
122
|
}
|
|
118
123
|
// ── DELETE ─────────────────────────────────────────────────────────────────
|
|
119
124
|
/**
|
|
120
125
|
* Delete a record permanently.
|
|
121
126
|
*
|
|
122
127
|
* @example
|
|
123
|
-
* await db.records.delete('rec_abc123');
|
|
128
|
+
* await db.records.delete('rec_abc123', { bucketKey: 'users' });
|
|
124
129
|
*/
|
|
125
|
-
async delete(recordId,
|
|
126
|
-
|
|
130
|
+
async delete(recordId, options) {
|
|
131
|
+
const { bucketKey, ...rest } = options;
|
|
132
|
+
return this.http.delete(this.path(bucketKey), { recordId }, rest);
|
|
127
133
|
}
|
|
128
134
|
// ── HEAD — existence check ─────────────────────────────────────────────────
|
|
129
135
|
/**
|
|
@@ -131,11 +137,12 @@ var RecordsClient = class {
|
|
|
131
137
|
* Returns `null` if the record is not found.
|
|
132
138
|
*
|
|
133
139
|
* @example
|
|
134
|
-
* const info = await db.records.exists('rec_abc123');
|
|
140
|
+
* const info = await db.records.exists('rec_abc123', { bucketKey: 'users' });
|
|
135
141
|
* if (info?.exists) console.log('found at', info.updatedAt);
|
|
136
142
|
*/
|
|
137
|
-
async exists(recordId,
|
|
138
|
-
const
|
|
143
|
+
async exists(recordId, options) {
|
|
144
|
+
const { bucketKey, ...rest } = options;
|
|
145
|
+
const res = await this.http.head(this.path(bucketKey), { recordId }, rest);
|
|
139
146
|
if (res.status === 404) return null;
|
|
140
147
|
if (!res.ok) return null;
|
|
141
148
|
return {
|
|
@@ -151,33 +158,28 @@ var RecordsClient = class {
|
|
|
151
158
|
* Update up to 500 records in a single request.
|
|
152
159
|
*
|
|
153
160
|
* @example
|
|
154
|
-
* await db.records.batchUpdate(
|
|
155
|
-
* updates: [
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
* ],
|
|
159
|
-
* });
|
|
161
|
+
* await db.records.batchUpdate(
|
|
162
|
+
* { updates: [{ recordId: 'rec_1', values: { status: 'archived' } }] },
|
|
163
|
+
* { bucketKey: 'orders' }
|
|
164
|
+
* );
|
|
160
165
|
*/
|
|
161
|
-
async batchUpdate(payload,
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
payload,
|
|
165
|
-
opts
|
|
166
|
-
);
|
|
166
|
+
async batchUpdate(payload, options) {
|
|
167
|
+
const { bucketKey, ...rest } = options;
|
|
168
|
+
return this.http.post(`${this.path(bucketKey)}/batch/update`, payload, rest);
|
|
167
169
|
}
|
|
168
170
|
// ── Batch — delete ─────────────────────────────────────────────────────────
|
|
169
171
|
/**
|
|
170
172
|
* Delete up to 500 records in a single request.
|
|
171
173
|
*
|
|
172
174
|
* @example
|
|
173
|
-
* await db.records.batchDelete(
|
|
175
|
+
* await db.records.batchDelete(
|
|
176
|
+
* { recordIds: ['rec_1', 'rec_2'] },
|
|
177
|
+
* { bucketKey: 'orders' }
|
|
178
|
+
* );
|
|
174
179
|
*/
|
|
175
|
-
async batchDelete(payload,
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
payload,
|
|
179
|
-
opts
|
|
180
|
-
);
|
|
180
|
+
async batchDelete(payload, options) {
|
|
181
|
+
const { bucketKey, ...rest } = options;
|
|
182
|
+
return this.http.post(`${this.path(bucketKey)}/batch/delete`, payload, rest);
|
|
181
183
|
}
|
|
182
184
|
// ── Batch — insert ─────────────────────────────────────────────────────────
|
|
183
185
|
/**
|
|
@@ -185,17 +187,14 @@ var RecordsClient = class {
|
|
|
185
187
|
* Returns HTTP 207 (multi-status) — check `meta.failed` for partial failures.
|
|
186
188
|
*
|
|
187
189
|
* @example
|
|
188
|
-
* const result = await db.records.batchInsert(
|
|
189
|
-
* records: [{ name: 'Alice' }, { name: 'Bob' }],
|
|
190
|
-
*
|
|
191
|
-
*
|
|
190
|
+
* const result = await db.records.batchInsert(
|
|
191
|
+
* { records: [{ name: 'Alice' }, { name: 'Bob' }], queryableFields: ['name'] },
|
|
192
|
+
* { bucketKey: 'users' }
|
|
193
|
+
* );
|
|
192
194
|
*/
|
|
193
|
-
async batchInsert(payload,
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
payload,
|
|
197
|
-
opts
|
|
198
|
-
);
|
|
195
|
+
async batchInsert(payload, options) {
|
|
196
|
+
const { bucketKey, ...rest } = options;
|
|
197
|
+
return this.http.post(`${this.path(bucketKey)}/batch/insert`, payload, rest);
|
|
199
198
|
}
|
|
200
199
|
// ── Helpers ────────────────────────────────────────────────────────────────
|
|
201
200
|
/**
|
|
@@ -204,6 +203,7 @@ var RecordsClient = class {
|
|
|
204
203
|
*
|
|
205
204
|
* @example
|
|
206
205
|
* const allRecords = await db.records.queryAll({
|
|
206
|
+
* bucketKey: 'orders',
|
|
207
207
|
* filters: [{ field: 'type', op: '==', value: 'invoice' }],
|
|
208
208
|
* });
|
|
209
209
|
*/
|
|
@@ -211,7 +211,9 @@ var RecordsClient = class {
|
|
|
211
211
|
const all = [];
|
|
212
212
|
let cursor = null;
|
|
213
213
|
do {
|
|
214
|
-
const result = await this.query(
|
|
214
|
+
const result = await this.query(
|
|
215
|
+
cursor ? { ...options, cursor } : { ...options }
|
|
216
|
+
);
|
|
215
217
|
all.push(...result.data);
|
|
216
218
|
cursor = result.meta.nextCursor ?? null;
|
|
217
219
|
} while (cursor);
|
|
@@ -1 +1 @@
|
|
|
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"]}
|
|
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\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"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hydrousdb",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "Official TypeScript SDK for HydrousDB
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "Official TypeScript SDK for HydrousDB \u2014 a cloud-native record store with built-in auth, batch operations, analytics, and cursor-based pagination.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
7
7
|
"types": "./dist/index.d.ts",
|