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