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.
@@ -35,31 +35,34 @@ var RecordsClient = class {
35
35
  constructor(http) {
36
36
  this.http = http;
37
37
  }
38
- get path() {
39
- return `/api/${this.http.bucketKey}`;
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 (options?.showHistory) params["showHistory"] = "true";
52
- return this.http.get(this.path, params, options);
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, opts) {
62
- return this.http.get(this.path, { recordId, generation }, opts);
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({ limit: 50, sortOrder: 'desc' });
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
- if (options?.filters) validateFilters(options.filters);
87
- const params = buildQueryParams(options ?? {});
88
- return this.http.get(this.path, params, 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);
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
- * queryableFields: ['name'],
98
- * userEmail: 'alice@example.com',
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, opts) {
102
- return this.http.post(this.path, payload, opts);
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
- * values: { score: 100 },
112
- * track_record_history: true,
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, opts) {
116
- return this.http.patch(this.path, payload, opts);
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, opts) {
126
- return this.http.delete(this.path, { recordId }, opts);
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, opts) {
138
- const res = await this.http.head(this.path, { recordId }, opts);
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
- * { recordId: 'rec_1', values: { status: 'archived' } },
157
- * { recordId: 'rec_2', values: { status: 'archived' } },
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, opts) {
162
- return this.http.post(
163
- `${this.path}/batch/update`,
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({ recordIds: ['rec_1', 'rec_2', 'rec_3'] });
175
+ * await db.records.batchDelete(
176
+ * { recordIds: ['rec_1', 'rec_2'] },
177
+ * { bucketKey: 'orders' }
178
+ * );
174
179
  */
175
- async batchDelete(payload, opts) {
176
- return this.http.post(
177
- `${this.path}/batch/delete`,
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
- * queryableFields: ['name'],
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, opts) {
194
- return this.http.post(
195
- `${this.path}/batch/insert`,
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(cursor ? { ...options, cursor } : { ...options });
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": "1.1.0",
4
- "description": "Official TypeScript SDK for HydrousDB a cloud-native record store with built-in auth, batch operations, analytics, and cursor-based pagination.",
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",