sapixdb 0.1.0 → 0.3.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/dist/admin.d.ts +88 -0
- package/dist/admin.d.ts.map +1 -0
- package/dist/admin.js +101 -0
- package/dist/admin.js.map +1 -0
- package/dist/client.d.ts +83 -27
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +189 -40
- package/dist/client.js.map +1 -1
- package/dist/collection.d.ts +130 -37
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +186 -66
- package/dist/collection.js.map +1 -1
- package/dist/errors.d.ts +25 -7
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +50 -11
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +6 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -3
- package/dist/index.js.map +1 -1
- package/dist/stream.d.ts +42 -0
- package/dist/stream.d.ts.map +1 -0
- package/dist/stream.js +60 -0
- package/dist/stream.js.map +1 -0
- package/dist/types.d.ts +191 -50
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -1
- package/package.json +15 -25
- package/README.md +0 -295
- package/dist/graph.d.ts +0 -28
- package/dist/graph.d.ts.map +0 -1
- package/dist/graph.js +0 -44
- package/dist/graph.js.map +0 -1
- package/dist/http.d.ts +0 -3
- package/dist/http.d.ts.map +0 -1
- package/dist/http.js +0 -42
- package/dist/http.js.map +0 -1
package/dist/collection.d.ts
CHANGED
|
@@ -1,41 +1,134 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import type { AgentStatusResponse, ChainHeadResponse, DeletedRecord, QueryResult, RecordView, SaqlQuery, StrandRecordsResponse, WriteResponse } from "./types.js";
|
|
2
|
+
type Requester = <T>(method: string, path: string, body?: unknown) => Promise<T>;
|
|
3
|
+
/**
|
|
4
|
+
* A read-only view of a collection scoped to a specific point in time.
|
|
5
|
+
* Returned by `CollectionClient.asOf(timestampHlc)`.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* const snapshot = db.collection("orders").asOf(record.timestamp_hlc);
|
|
9
|
+
* const results = await snapshot.latest({ limit: 10 });
|
|
10
|
+
*/
|
|
11
|
+
export declare class CollectionSnapshot {
|
|
4
12
|
private readonly name;
|
|
5
|
-
private readonly
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
private readonly timestampHlc;
|
|
14
|
+
private readonly req;
|
|
15
|
+
constructor(name: string, timestampHlc: number, req: Requester);
|
|
16
|
+
/** Records written at or before this snapshot timestamp, newest-first. */
|
|
17
|
+
latest(opts?: {
|
|
18
|
+
limit?: number;
|
|
19
|
+
}): Promise<QueryResult>;
|
|
20
|
+
/**
|
|
21
|
+
* Equality-filter scan bounded by the snapshot timestamp.
|
|
22
|
+
* `filter` is a `{field: value}` object — first key is used.
|
|
23
|
+
*/
|
|
24
|
+
find(filter: Record<string, unknown>, opts?: {
|
|
25
|
+
limit?: number;
|
|
26
|
+
}): Promise<QueryResult>;
|
|
27
|
+
findOne(filter: Record<string, unknown>): Promise<RecordView | null>;
|
|
16
28
|
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
29
|
+
/**
|
|
30
|
+
* Client for a named SapixDB agent (collection).
|
|
31
|
+
* All operations route to `/v1/agents/{name}/...`.
|
|
32
|
+
*
|
|
33
|
+
* Obtain via `SapixClient.collection(name)` or `SapixClient.agent(name)`.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* const orders = db.collection("orders");
|
|
37
|
+
*
|
|
38
|
+
* // Write
|
|
39
|
+
* const record = await orders.write({ total: 99.99, status: "pending" });
|
|
40
|
+
*
|
|
41
|
+
* // Read
|
|
42
|
+
* const latest = await orders.latest({ limit: 20 });
|
|
43
|
+
* const pending = await orders.find({ status: "pending" });
|
|
44
|
+
* const one = await orders.findOne({ status: "pending" });
|
|
45
|
+
* const byHash = await orders.get(record.content_hash);
|
|
46
|
+
*
|
|
47
|
+
* // History (chronological)
|
|
48
|
+
* const history = await orders.history({ limit: 100, offset: 0 });
|
|
49
|
+
*
|
|
50
|
+
* // Time travel
|
|
51
|
+
* const snapshot = await orders.asOf(record.timestamp_hlc).latest();
|
|
52
|
+
*
|
|
53
|
+
* // Direct SaQL
|
|
54
|
+
* const result = await orders.query({ type: "latest", limit: 5 });
|
|
55
|
+
*
|
|
56
|
+
* // Realtime — handled on SapixClient
|
|
57
|
+
* client.subscribeAgent("orders", (event) => console.log(event));
|
|
58
|
+
*/
|
|
59
|
+
export declare class CollectionClient {
|
|
60
|
+
readonly name: string;
|
|
61
|
+
private readonly req;
|
|
62
|
+
constructor(name: string, req: Requester);
|
|
63
|
+
/**
|
|
64
|
+
* Append a JSON record to the agent's strand.
|
|
65
|
+
* `data` can be any JSON-serialisable object.
|
|
66
|
+
*/
|
|
67
|
+
write(data: Record<string, unknown>): Promise<WriteResponse>;
|
|
68
|
+
/** Alias for `write` — matches Python/Go SDK naming. */
|
|
69
|
+
writeJson(data: Record<string, unknown>): Promise<WriteResponse>;
|
|
70
|
+
/**
|
|
71
|
+
* Fetch a single record by its BLAKE3 content hash.
|
|
72
|
+
* Use `record.content_hash` from a previous write or query.
|
|
73
|
+
*/
|
|
74
|
+
get(contentHash: string): Promise<RecordView>;
|
|
75
|
+
/**
|
|
76
|
+
* Return the most recent records, newest-first.
|
|
77
|
+
*
|
|
78
|
+
* Pass `filter: { field: value }` to run an equality scan (uses a field
|
|
79
|
+
* index when one is available for that field, otherwise a full strand scan).
|
|
80
|
+
*/
|
|
81
|
+
latest(opts?: {
|
|
82
|
+
limit?: number;
|
|
83
|
+
filter?: Record<string, unknown>;
|
|
84
|
+
}): Promise<QueryResult>;
|
|
85
|
+
/**
|
|
86
|
+
* Return records in chronological order (oldest-first) with pagination.
|
|
87
|
+
* Uses `GET /v1/agents/{name}/strand/records`.
|
|
88
|
+
*/
|
|
89
|
+
history(opts?: {
|
|
90
|
+
limit?: number;
|
|
91
|
+
offset?: number;
|
|
92
|
+
}): Promise<StrandRecordsResponse>;
|
|
93
|
+
/**
|
|
94
|
+
* Equality-filter scan across the full strand.
|
|
95
|
+
* `filter` is a `{field: value}` object — first key is used.
|
|
96
|
+
*
|
|
97
|
+
* Uses a field index when one exists for that field; otherwise falls back
|
|
98
|
+
* to a full strand scan on the server.
|
|
99
|
+
*/
|
|
100
|
+
find(filter: Record<string, unknown>, opts?: {
|
|
101
|
+
limit?: number;
|
|
102
|
+
}): Promise<QueryResult>;
|
|
103
|
+
/** Return the first matching record, or `null` if none. */
|
|
104
|
+
findOne(filter: Record<string, unknown>): Promise<RecordView | null>;
|
|
105
|
+
/** Execute any SaQL query directly against this agent. */
|
|
106
|
+
query(q: SaqlQuery): Promise<QueryResult>;
|
|
107
|
+
/** Current chain head hash and record count. */
|
|
108
|
+
head(): Promise<ChainHeadResponse>;
|
|
109
|
+
/** Agent status: record count, chain head, zone, schema version, caged state. */
|
|
110
|
+
status(): Promise<AgentStatusResponse>;
|
|
111
|
+
/**
|
|
112
|
+
* Soft-delete a record by content hash. Writes a tombstone — the record is
|
|
113
|
+
* excluded from `latest()` and `find()` but remains in the strand for audit.
|
|
114
|
+
* Restore with `restore(contentHash)`.
|
|
115
|
+
*/
|
|
116
|
+
softDelete(contentHash: string): Promise<void>;
|
|
117
|
+
/** List all soft-deleted (tombstoned) records for this agent. */
|
|
118
|
+
listDeleted(): Promise<DeletedRecord[]>;
|
|
119
|
+
/** Restore a previously soft-deleted record. */
|
|
120
|
+
restore(contentHash: string): Promise<void>;
|
|
121
|
+
/**
|
|
122
|
+
* Scope reads to a point in time identified by an HLC timestamp.
|
|
123
|
+
* Returns a `CollectionSnapshot` — call `.latest()` or `.find()` on it.
|
|
124
|
+
*
|
|
125
|
+
* @param timestampHlc — `NucleotideRecord.timestamp_hlc` from any prior record
|
|
126
|
+
*
|
|
127
|
+
* @example
|
|
128
|
+
* const snapshot = orders.asOf(record.timestamp_hlc);
|
|
129
|
+
* const state = await snapshot.latest({ limit: 50 });
|
|
130
|
+
*/
|
|
131
|
+
asOf(timestampHlc: number): CollectionSnapshot;
|
|
40
132
|
}
|
|
133
|
+
export {};
|
|
41
134
|
//# sourceMappingURL=collection.d.ts.map
|
package/dist/collection.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collection.d.ts","sourceRoot":"","sources":["../src/collection.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"collection.d.ts","sourceRoot":"","sources":["../src/collection.ts"],"names":[],"mappings":"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"}
|
package/dist/collection.js
CHANGED
|
@@ -1,87 +1,207 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
// ── CollectionSnapshot ────────────────────────────────────────────────────────
|
|
2
|
+
/**
|
|
3
|
+
* A read-only view of a collection scoped to a specific point in time.
|
|
4
|
+
* Returned by `CollectionClient.asOf(timestampHlc)`.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* const snapshot = db.collection("orders").asOf(record.timestamp_hlc);
|
|
8
|
+
* const results = await snapshot.latest({ limit: 10 });
|
|
9
|
+
*/
|
|
10
|
+
export class CollectionSnapshot {
|
|
11
|
+
name;
|
|
12
|
+
timestampHlc;
|
|
13
|
+
req;
|
|
14
|
+
constructor(name, timestampHlc, req) {
|
|
6
15
|
this.name = name;
|
|
7
|
-
this.
|
|
16
|
+
this.timestampHlc = timestampHlc;
|
|
17
|
+
this.req = req;
|
|
8
18
|
}
|
|
9
|
-
/**
|
|
10
|
-
|
|
11
|
-
return this.
|
|
19
|
+
/** Records written at or before this snapshot timestamp, newest-first. */
|
|
20
|
+
latest(opts = {}) {
|
|
21
|
+
return this.req("POST", `/v1/agents/${this.name}/query`, {
|
|
22
|
+
type: "as_of",
|
|
23
|
+
timestamp_hlc: this.timestampHlc,
|
|
24
|
+
limit: opts.limit ?? 20,
|
|
25
|
+
});
|
|
12
26
|
}
|
|
13
|
-
/**
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
27
|
+
/**
|
|
28
|
+
* Equality-filter scan bounded by the snapshot timestamp.
|
|
29
|
+
* `filter` is a `{field: value}` object — first key is used.
|
|
30
|
+
*/
|
|
31
|
+
find(filter, opts = {}) {
|
|
32
|
+
const [field, value] = Object.entries(filter)[0] ?? [];
|
|
33
|
+
const whereFilter = field
|
|
34
|
+
? { field, op: "eq", value }
|
|
35
|
+
: undefined;
|
|
36
|
+
return this.req("POST", `/v1/agents/${this.name}/query`, {
|
|
37
|
+
type: "scan",
|
|
38
|
+
limit: opts.limit ?? 20,
|
|
39
|
+
...(whereFilter ? { filter: whereFilter } : {}),
|
|
40
|
+
});
|
|
20
41
|
}
|
|
21
|
-
/** Find one record matching a filter, or null. */
|
|
22
42
|
async findOne(filter) {
|
|
23
|
-
const
|
|
24
|
-
return
|
|
25
|
-
}
|
|
26
|
-
async _query(options) {
|
|
27
|
-
const body = {
|
|
28
|
-
collection: this.name,
|
|
29
|
-
...options,
|
|
30
|
-
};
|
|
31
|
-
if (this.asOfTimestamp)
|
|
32
|
-
body.as_of = this.asOfTimestamp;
|
|
33
|
-
const res = await request(this.config, "POST", `/v1/${this.config.agent}/strand/query`, body);
|
|
34
|
-
return res.results;
|
|
43
|
+
const result = await this.find(filter, { limit: 1 });
|
|
44
|
+
return result.records[0] ?? null;
|
|
35
45
|
}
|
|
36
46
|
}
|
|
47
|
+
// ── CollectionClient ──────────────────────────────────────────────────────────
|
|
48
|
+
/**
|
|
49
|
+
* Client for a named SapixDB agent (collection).
|
|
50
|
+
* All operations route to `/v1/agents/{name}/...`.
|
|
51
|
+
*
|
|
52
|
+
* Obtain via `SapixClient.collection(name)` or `SapixClient.agent(name)`.
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* const orders = db.collection("orders");
|
|
56
|
+
*
|
|
57
|
+
* // Write
|
|
58
|
+
* const record = await orders.write({ total: 99.99, status: "pending" });
|
|
59
|
+
*
|
|
60
|
+
* // Read
|
|
61
|
+
* const latest = await orders.latest({ limit: 20 });
|
|
62
|
+
* const pending = await orders.find({ status: "pending" });
|
|
63
|
+
* const one = await orders.findOne({ status: "pending" });
|
|
64
|
+
* const byHash = await orders.get(record.content_hash);
|
|
65
|
+
*
|
|
66
|
+
* // History (chronological)
|
|
67
|
+
* const history = await orders.history({ limit: 100, offset: 0 });
|
|
68
|
+
*
|
|
69
|
+
* // Time travel
|
|
70
|
+
* const snapshot = await orders.asOf(record.timestamp_hlc).latest();
|
|
71
|
+
*
|
|
72
|
+
* // Direct SaQL
|
|
73
|
+
* const result = await orders.query({ type: "latest", limit: 5 });
|
|
74
|
+
*
|
|
75
|
+
* // Realtime — handled on SapixClient
|
|
76
|
+
* client.subscribeAgent("orders", (event) => console.log(event));
|
|
77
|
+
*/
|
|
37
78
|
export class CollectionClient {
|
|
38
|
-
|
|
39
|
-
|
|
79
|
+
name;
|
|
80
|
+
req;
|
|
81
|
+
constructor(name, req) {
|
|
40
82
|
this.name = name;
|
|
83
|
+
this.req = req;
|
|
41
84
|
}
|
|
85
|
+
// ── Writes ──────────────────────────────────────────────────────────────────
|
|
42
86
|
/**
|
|
43
|
-
*
|
|
44
|
-
*
|
|
87
|
+
* Append a JSON record to the agent's strand.
|
|
88
|
+
* `data` can be any JSON-serialisable object.
|
|
45
89
|
*/
|
|
46
|
-
|
|
47
|
-
return
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
|
|
51
|
-
return
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
90
|
+
write(data) {
|
|
91
|
+
return this.req("POST", `/v1/agents/${this.name}/records/json`, { data });
|
|
92
|
+
}
|
|
93
|
+
/** Alias for `write` — matches Python/Go SDK naming. */
|
|
94
|
+
writeJson(data) {
|
|
95
|
+
return this.write(data);
|
|
96
|
+
}
|
|
97
|
+
// ── Reads ───────────────────────────────────────────────────────────────────
|
|
98
|
+
/**
|
|
99
|
+
* Fetch a single record by its BLAKE3 content hash.
|
|
100
|
+
* Use `record.content_hash` from a previous write or query.
|
|
101
|
+
*/
|
|
102
|
+
get(contentHash) {
|
|
103
|
+
return this.req("GET", `/v1/agents/${this.name}/records/${contentHash}`);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Return the most recent records, newest-first.
|
|
107
|
+
*
|
|
108
|
+
* Pass `filter: { field: value }` to run an equality scan (uses a field
|
|
109
|
+
* index when one is available for that field, otherwise a full strand scan).
|
|
110
|
+
*/
|
|
111
|
+
latest(opts = {}) {
|
|
112
|
+
if (opts.filter) {
|
|
113
|
+
const [field, value] = Object.entries(opts.filter)[0] ?? [];
|
|
114
|
+
if (field !== undefined) {
|
|
115
|
+
return this.req("POST", `/v1/agents/${this.name}/query`, {
|
|
116
|
+
type: "scan",
|
|
117
|
+
limit: opts.limit ?? 20,
|
|
118
|
+
filter: { field, op: "eq", value },
|
|
119
|
+
});
|
|
66
120
|
}
|
|
67
|
-
throw err;
|
|
68
121
|
}
|
|
122
|
+
return this.req("POST", `/v1/agents/${this.name}/query`, {
|
|
123
|
+
type: "latest",
|
|
124
|
+
limit: opts.limit ?? 20,
|
|
125
|
+
});
|
|
69
126
|
}
|
|
70
|
-
/**
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
127
|
+
/**
|
|
128
|
+
* Return records in chronological order (oldest-first) with pagination.
|
|
129
|
+
* Uses `GET /v1/agents/{name}/strand/records`.
|
|
130
|
+
*/
|
|
131
|
+
history(opts = {}) {
|
|
132
|
+
const params = new URLSearchParams();
|
|
133
|
+
if (opts.limit !== undefined)
|
|
134
|
+
params.set("limit", String(opts.limit));
|
|
135
|
+
if (opts.offset !== undefined)
|
|
136
|
+
params.set("offset", String(opts.offset));
|
|
137
|
+
const qs = params.size > 0 ? `?${params}` : "";
|
|
138
|
+
return this.req("GET", `/v1/agents/${this.name}/strand/records${qs}`);
|
|
77
139
|
}
|
|
78
|
-
/**
|
|
79
|
-
|
|
80
|
-
|
|
140
|
+
/**
|
|
141
|
+
* Equality-filter scan across the full strand.
|
|
142
|
+
* `filter` is a `{field: value}` object — first key is used.
|
|
143
|
+
*
|
|
144
|
+
* Uses a field index when one exists for that field; otherwise falls back
|
|
145
|
+
* to a full strand scan on the server.
|
|
146
|
+
*/
|
|
147
|
+
find(filter, opts = {}) {
|
|
148
|
+
const [field, value] = Object.entries(filter)[0] ?? [];
|
|
149
|
+
const whereFilter = field
|
|
150
|
+
? { field, op: "eq", value }
|
|
151
|
+
: undefined;
|
|
152
|
+
return this.req("POST", `/v1/agents/${this.name}/query`, {
|
|
153
|
+
type: "scan",
|
|
154
|
+
limit: opts.limit ?? 20,
|
|
155
|
+
...(whereFilter ? { filter: whereFilter } : {}),
|
|
156
|
+
});
|
|
81
157
|
}
|
|
82
|
-
/**
|
|
158
|
+
/** Return the first matching record, or `null` if none. */
|
|
83
159
|
async findOne(filter) {
|
|
84
|
-
|
|
160
|
+
const result = await this.find(filter, { limit: 1 });
|
|
161
|
+
return result.records[0] ?? null;
|
|
162
|
+
}
|
|
163
|
+
/** Execute any SaQL query directly against this agent. */
|
|
164
|
+
query(q) {
|
|
165
|
+
return this.req("POST", `/v1/agents/${this.name}/query`, q);
|
|
166
|
+
}
|
|
167
|
+
/** Current chain head hash and record count. */
|
|
168
|
+
head() {
|
|
169
|
+
return this.req("GET", `/v1/agents/${this.name}/strand/head`);
|
|
170
|
+
}
|
|
171
|
+
/** Agent status: record count, chain head, zone, schema version, caged state. */
|
|
172
|
+
status() {
|
|
173
|
+
return this.req("GET", `/v1/agents/${this.name}/status`);
|
|
174
|
+
}
|
|
175
|
+
// ── Soft delete ─────────────────────────────────────────────────────────────
|
|
176
|
+
/**
|
|
177
|
+
* Soft-delete a record by content hash. Writes a tombstone — the record is
|
|
178
|
+
* excluded from `latest()` and `find()` but remains in the strand for audit.
|
|
179
|
+
* Restore with `restore(contentHash)`.
|
|
180
|
+
*/
|
|
181
|
+
softDelete(contentHash) {
|
|
182
|
+
return this.req("DELETE", `/v1/agents/${this.name}/records/${contentHash}`);
|
|
183
|
+
}
|
|
184
|
+
/** List all soft-deleted (tombstoned) records for this agent. */
|
|
185
|
+
listDeleted() {
|
|
186
|
+
return this.req("GET", `/v1/agents/${this.name}/records/deleted`);
|
|
187
|
+
}
|
|
188
|
+
/** Restore a previously soft-deleted record. */
|
|
189
|
+
restore(contentHash) {
|
|
190
|
+
return this.req("POST", `/v1/agents/${this.name}/records/${contentHash}/restore`);
|
|
191
|
+
}
|
|
192
|
+
// ── Time travel ─────────────────────────────────────────────────────────────
|
|
193
|
+
/**
|
|
194
|
+
* Scope reads to a point in time identified by an HLC timestamp.
|
|
195
|
+
* Returns a `CollectionSnapshot` — call `.latest()` or `.find()` on it.
|
|
196
|
+
*
|
|
197
|
+
* @param timestampHlc — `NucleotideRecord.timestamp_hlc` from any prior record
|
|
198
|
+
*
|
|
199
|
+
* @example
|
|
200
|
+
* const snapshot = orders.asOf(record.timestamp_hlc);
|
|
201
|
+
* const state = await snapshot.latest({ limit: 50 });
|
|
202
|
+
*/
|
|
203
|
+
asOf(timestampHlc) {
|
|
204
|
+
return new CollectionSnapshot(this.name, timestampHlc, this.req);
|
|
85
205
|
}
|
|
86
206
|
}
|
|
87
207
|
//# sourceMappingURL=collection.js.map
|
package/dist/collection.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collection.js","sourceRoot":"","sources":["../src/collection.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"collection.js","sourceRoot":"","sources":["../src/collection.ts"],"names":[],"mappings":"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"}
|
package/dist/errors.d.ts
CHANGED
|
@@ -1,12 +1,30 @@
|
|
|
1
1
|
export declare class SapixError extends Error {
|
|
2
2
|
readonly status?: number | undefined;
|
|
3
|
-
readonly
|
|
4
|
-
constructor(message: string, status?: number | undefined,
|
|
3
|
+
readonly body?: string | undefined;
|
|
4
|
+
constructor(message: string, status?: number | undefined, body?: string | undefined);
|
|
5
|
+
static fromResponse(resp: Response): Promise<SapixError>;
|
|
5
6
|
}
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Thrown when an API key exceeds its configured requests-per-second limit (HTTP 429).
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* import { SapixRateLimitError } from "@sapixdb/client";
|
|
12
|
+
*
|
|
13
|
+
* try {
|
|
14
|
+
* await orders.write(data);
|
|
15
|
+
* } catch (err) {
|
|
16
|
+
* if (err instanceof SapixRateLimitError) {
|
|
17
|
+
* const delay = err.rpsLimit ? 1000 / err.rpsLimit : 1000;
|
|
18
|
+
* await new Promise(r => setTimeout(r, delay));
|
|
19
|
+
* await orders.write(data); // retry once
|
|
20
|
+
* } else throw err;
|
|
21
|
+
* }
|
|
22
|
+
*/
|
|
23
|
+
export declare class SapixRateLimitError extends SapixError {
|
|
24
|
+
/** The key's configured limit in requests/second, parsed from the server message. */
|
|
25
|
+
readonly rpsLimit?: number | undefined;
|
|
26
|
+
constructor(message: string,
|
|
27
|
+
/** The key's configured limit in requests/second, parsed from the server message. */
|
|
28
|
+
rpsLimit?: number | undefined);
|
|
11
29
|
}
|
|
12
30
|
//# sourceMappingURL=errors.d.ts.map
|
package/dist/errors.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,UAAW,SAAQ,KAAK;aAGjB,MAAM,CAAC,EAAE,MAAM;aACf,IAAI,CAAC,EAAE,MAAM;gBAF7B,OAAO,EAAE,MAAM,EACC,MAAM,CAAC,EAAE,MAAM,YAAA,EACf,IAAI,CAAC,EAAE,MAAM,YAAA;
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,UAAW,SAAQ,KAAK;aAGjB,MAAM,CAAC,EAAE,MAAM;aACf,IAAI,CAAC,EAAE,MAAM;gBAF7B,OAAO,EAAE,MAAM,EACC,MAAM,CAAC,EAAE,MAAM,YAAA,EACf,IAAI,CAAC,EAAE,MAAM,YAAA;WAMlB,YAAY,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAC;CA0B/D;AAED;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,mBAAoB,SAAQ,UAAU;IAG/C,qFAAqF;aACrE,QAAQ,CAAC,EAAE,MAAM;gBAFjC,OAAO,EAAE,MAAM;IACf,qFAAqF;IACrE,QAAQ,CAAC,EAAE,MAAM,YAAA;CAKpC"}
|
package/dist/errors.js
CHANGED
|
@@ -1,21 +1,60 @@
|
|
|
1
1
|
export class SapixError extends Error {
|
|
2
|
-
|
|
2
|
+
status;
|
|
3
|
+
body;
|
|
4
|
+
constructor(message, status, body) {
|
|
3
5
|
super(message);
|
|
4
6
|
this.status = status;
|
|
5
|
-
this.
|
|
7
|
+
this.body = body;
|
|
6
8
|
this.name = "SapixError";
|
|
7
9
|
}
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
static async fromResponse(resp) {
|
|
11
|
+
let body;
|
|
12
|
+
try {
|
|
13
|
+
body = await resp.text();
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
body = "<unreadable>";
|
|
17
|
+
}
|
|
18
|
+
if (resp.status === 429) {
|
|
19
|
+
let message = "rate limit exceeded";
|
|
20
|
+
let rpsLimit;
|
|
21
|
+
try {
|
|
22
|
+
const data = JSON.parse(body);
|
|
23
|
+
message = data.message ?? data.error ?? message;
|
|
24
|
+
const m = message.match(/rate limit of (\d+) req\/s/);
|
|
25
|
+
if (m)
|
|
26
|
+
rpsLimit = Number(m[1]);
|
|
27
|
+
}
|
|
28
|
+
catch { /* body was not JSON */ }
|
|
29
|
+
return new SapixRateLimitError(message, rpsLimit);
|
|
30
|
+
}
|
|
31
|
+
return new SapixError(`sapix-agent returned ${resp.status} ${resp.statusText}: ${body}`, resp.status, body);
|
|
13
32
|
}
|
|
14
33
|
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
34
|
+
/**
|
|
35
|
+
* Thrown when an API key exceeds its configured requests-per-second limit (HTTP 429).
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* import { SapixRateLimitError } from "@sapixdb/client";
|
|
39
|
+
*
|
|
40
|
+
* try {
|
|
41
|
+
* await orders.write(data);
|
|
42
|
+
* } catch (err) {
|
|
43
|
+
* if (err instanceof SapixRateLimitError) {
|
|
44
|
+
* const delay = err.rpsLimit ? 1000 / err.rpsLimit : 1000;
|
|
45
|
+
* await new Promise(r => setTimeout(r, delay));
|
|
46
|
+
* await orders.write(data); // retry once
|
|
47
|
+
* } else throw err;
|
|
48
|
+
* }
|
|
49
|
+
*/
|
|
50
|
+
export class SapixRateLimitError extends SapixError {
|
|
51
|
+
rpsLimit;
|
|
52
|
+
constructor(message,
|
|
53
|
+
/** The key's configured limit in requests/second, parsed from the server message. */
|
|
54
|
+
rpsLimit) {
|
|
55
|
+
super(message, 429);
|
|
56
|
+
this.rpsLimit = rpsLimit;
|
|
57
|
+
this.name = "SapixRateLimitError";
|
|
19
58
|
}
|
|
20
59
|
}
|
|
21
60
|
//# sourceMappingURL=errors.js.map
|
package/dist/errors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,UAAW,SAAQ,KAAK;
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,UAAW,SAAQ,KAAK;IAGjB;IACA;IAHlB,YACE,OAAe,EACC,MAAe,EACf,IAAa;QAE7B,KAAK,CAAC,OAAO,CAAC,CAAC;QAHC,WAAM,GAAN,MAAM,CAAS;QACf,SAAI,GAAJ,IAAI,CAAS;QAG7B,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC;IAC3B,CAAC;IAED,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,IAAc;QACtC,IAAI,IAAY,CAAC;QACjB,IAAI,CAAC;YACH,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QAC3B,CAAC;QAAC,MAAM,CAAC;YACP,IAAI,GAAG,cAAc,CAAC;QACxB,CAAC;QAED,IAAI,IAAI,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YACxB,IAAI,OAAO,GAAG,qBAAqB,CAAC;YACpC,IAAI,QAA4B,CAAC;YACjC,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAyC,CAAC;gBACtE,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,KAAK,IAAI,OAAO,CAAC;gBAChD,MAAM,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,4BAA4B,CAAC,CAAC;gBACtD,IAAI,CAAC;oBAAE,QAAQ,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACjC,CAAC;YAAC,MAAM,CAAC,CAAC,uBAAuB,CAAC,CAAC;YACnC,OAAO,IAAI,mBAAmB,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QACpD,CAAC;QAED,OAAO,IAAI,UAAU,CACnB,wBAAwB,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,UAAU,KAAK,IAAI,EAAE,EACjE,IAAI,CAAC,MAAM,EACX,IAAI,CACL,CAAC;IACJ,CAAC;CACF;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,OAAO,mBAAoB,SAAQ,UAAU;IAI/B;IAHlB,YACE,OAAe;IACf,qFAAqF;IACrE,QAAiB;QAEjC,KAAK,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;QAFJ,aAAQ,GAAR,QAAQ,CAAS;QAGjC,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;IACpC,CAAC;CACF"}
|