@bcts/hubert 1.0.0-alpha.17
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/LICENSE +48 -0
- package/README.md +18 -0
- package/dist/arid-derivation-1CJuU-kZ.cjs +150 -0
- package/dist/arid-derivation-1CJuU-kZ.cjs.map +1 -0
- package/dist/arid-derivation-CbqACjdg.mjs +126 -0
- package/dist/arid-derivation-CbqACjdg.mjs.map +1 -0
- package/dist/bin/hubert.cjs +384 -0
- package/dist/bin/hubert.cjs.map +1 -0
- package/dist/bin/hubert.d.cts +1 -0
- package/dist/bin/hubert.d.mts +1 -0
- package/dist/bin/hubert.mjs +383 -0
- package/dist/bin/hubert.mjs.map +1 -0
- package/dist/chunk-CbDLau6x.cjs +34 -0
- package/dist/hybrid/index.cjs +14 -0
- package/dist/hybrid/index.d.cts +3 -0
- package/dist/hybrid/index.d.mts +3 -0
- package/dist/hybrid/index.mjs +6 -0
- package/dist/hybrid-BZhumygj.mjs +356 -0
- package/dist/hybrid-BZhumygj.mjs.map +1 -0
- package/dist/hybrid-dX5JLumO.cjs +410 -0
- package/dist/hybrid-dX5JLumO.cjs.map +1 -0
- package/dist/index-BEzpUC7r.d.mts +380 -0
- package/dist/index-BEzpUC7r.d.mts.map +1 -0
- package/dist/index-C2F6ugLL.d.mts +210 -0
- package/dist/index-C2F6ugLL.d.mts.map +1 -0
- package/dist/index-CUnDouMb.d.mts +215 -0
- package/dist/index-CUnDouMb.d.mts.map +1 -0
- package/dist/index-CV6lZJqY.d.cts +380 -0
- package/dist/index-CV6lZJqY.d.cts.map +1 -0
- package/dist/index-CY3TCzIm.d.cts +217 -0
- package/dist/index-CY3TCzIm.d.cts.map +1 -0
- package/dist/index-DEr4SR1J.d.cts +215 -0
- package/dist/index-DEr4SR1J.d.cts.map +1 -0
- package/dist/index-T1LHanIb.d.mts +217 -0
- package/dist/index-T1LHanIb.d.mts.map +1 -0
- package/dist/index-jyzuOhFB.d.cts +210 -0
- package/dist/index-jyzuOhFB.d.cts.map +1 -0
- package/dist/index.cjs +60 -0
- package/dist/index.d.cts +161 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +161 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +10 -0
- package/dist/ipfs/index.cjs +13 -0
- package/dist/ipfs/index.d.cts +3 -0
- package/dist/ipfs/index.d.mts +3 -0
- package/dist/ipfs/index.mjs +5 -0
- package/dist/ipfs-BRMMCBjv.mjs +1 -0
- package/dist/ipfs-CetOVQcO.cjs +0 -0
- package/dist/kv-BAmhmMOo.cjs +425 -0
- package/dist/kv-BAmhmMOo.cjs.map +1 -0
- package/dist/kv-C-emxv0w.mjs +375 -0
- package/dist/kv-C-emxv0w.mjs.map +1 -0
- package/dist/kv-DJiKvypY.mjs +403 -0
- package/dist/kv-DJiKvypY.mjs.map +1 -0
- package/dist/kv-store-DmngWWuw.d.mts +183 -0
- package/dist/kv-store-DmngWWuw.d.mts.map +1 -0
- package/dist/kv-store-ww-AUyLd.d.cts +183 -0
- package/dist/kv-store-ww-AUyLd.d.cts.map +1 -0
- package/dist/kv-yjvQa_LH.cjs +457 -0
- package/dist/kv-yjvQa_LH.cjs.map +1 -0
- package/dist/logging-hmzNzifq.mjs +158 -0
- package/dist/logging-hmzNzifq.mjs.map +1 -0
- package/dist/logging-qc9uMgil.cjs +212 -0
- package/dist/logging-qc9uMgil.cjs.map +1 -0
- package/dist/mainline/index.cjs +12 -0
- package/dist/mainline/index.d.cts +3 -0
- package/dist/mainline/index.d.mts +3 -0
- package/dist/mainline/index.mjs +5 -0
- package/dist/mainline-D_jfeFMh.cjs +0 -0
- package/dist/mainline-cFIuXbo-.mjs +1 -0
- package/dist/server/index.cjs +14 -0
- package/dist/server/index.d.cts +3 -0
- package/dist/server/index.d.mts +3 -0
- package/dist/server/index.mjs +3 -0
- package/dist/server-BBNRZ30D.cjs +912 -0
- package/dist/server-BBNRZ30D.cjs.map +1 -0
- package/dist/server-DVyk9gqU.mjs +836 -0
- package/dist/server-DVyk9gqU.mjs.map +1 -0
- package/package.json +125 -0
- package/src/arid-derivation.ts +155 -0
- package/src/bin/hubert.ts +667 -0
- package/src/error.ts +89 -0
- package/src/hybrid/error.ts +77 -0
- package/src/hybrid/index.ts +24 -0
- package/src/hybrid/kv.ts +236 -0
- package/src/hybrid/reference.ts +176 -0
- package/src/index.ts +145 -0
- package/src/ipfs/error.ts +83 -0
- package/src/ipfs/index.ts +24 -0
- package/src/ipfs/kv.ts +476 -0
- package/src/ipfs/value.ts +85 -0
- package/src/kv-store.ts +128 -0
- package/src/logging.ts +88 -0
- package/src/mainline/error.ts +108 -0
- package/src/mainline/index.ts +23 -0
- package/src/mainline/kv.ts +411 -0
- package/src/server/error.ts +83 -0
- package/src/server/index.ts +29 -0
- package/src/server/kv.ts +211 -0
- package/src/server/memory-kv.ts +191 -0
- package/src/server/server-kv.ts +92 -0
- package/src/server/server.ts +369 -0
- package/src/server/sqlite-kv.ts +295 -0
|
@@ -0,0 +1,912 @@
|
|
|
1
|
+
const require_chunk = require('./chunk-CbDLau6x.cjs');
|
|
2
|
+
const require_logging = require('./logging-qc9uMgil.cjs');
|
|
3
|
+
let better_sqlite3 = require("better-sqlite3");
|
|
4
|
+
better_sqlite3 = require_chunk.__toESM(better_sqlite3);
|
|
5
|
+
let fs = require("fs");
|
|
6
|
+
fs = require_chunk.__toESM(fs);
|
|
7
|
+
let path = require("path");
|
|
8
|
+
path = require_chunk.__toESM(path);
|
|
9
|
+
let fastify = require("fastify");
|
|
10
|
+
fastify = require_chunk.__toESM(fastify);
|
|
11
|
+
let _bcts_components = require("@bcts/components");
|
|
12
|
+
let _bcts_envelope = require("@bcts/envelope");
|
|
13
|
+
|
|
14
|
+
//#region src/server/error.ts
|
|
15
|
+
/**
|
|
16
|
+
* Server-specific error types.
|
|
17
|
+
*
|
|
18
|
+
* Port of server/error.rs from hubert-rust.
|
|
19
|
+
*
|
|
20
|
+
* @module
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* Base error class for server errors.
|
|
24
|
+
*
|
|
25
|
+
* @category Server Errors
|
|
26
|
+
*/
|
|
27
|
+
var ServerError = class extends require_logging.HubertError {
|
|
28
|
+
constructor(message) {
|
|
29
|
+
super(message);
|
|
30
|
+
this.name = "ServerError";
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* General server error.
|
|
35
|
+
*
|
|
36
|
+
* Port of `Error::General(String)` from server/error.rs line 4.
|
|
37
|
+
*
|
|
38
|
+
* @category Server Errors
|
|
39
|
+
*/
|
|
40
|
+
var ServerGeneralError = class extends ServerError {
|
|
41
|
+
constructor(message) {
|
|
42
|
+
super(`Server error: ${message}`);
|
|
43
|
+
this.name = "ServerGeneralError";
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Network error during server communication.
|
|
48
|
+
*
|
|
49
|
+
* Port of `Error::NetworkError(String)` from server/error.rs line 7.
|
|
50
|
+
*
|
|
51
|
+
* @category Server Errors
|
|
52
|
+
*/
|
|
53
|
+
var ServerNetworkError = class extends ServerError {
|
|
54
|
+
constructor(message) {
|
|
55
|
+
super(`Network error: ${message}`);
|
|
56
|
+
this.name = "ServerNetworkError";
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* Parse error during data handling.
|
|
61
|
+
*
|
|
62
|
+
* Port of `Error::ParseError(String)` from server/error.rs line 10.
|
|
63
|
+
*
|
|
64
|
+
* @category Server Errors
|
|
65
|
+
*/
|
|
66
|
+
var ServerParseError = class extends ServerError {
|
|
67
|
+
constructor(message) {
|
|
68
|
+
super(`Parse error: ${message}`);
|
|
69
|
+
this.name = "ServerParseError";
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* SQLite database error.
|
|
74
|
+
*
|
|
75
|
+
* Port of `Error::Sqlite(e)` from server/error.rs line 19.
|
|
76
|
+
*
|
|
77
|
+
* @category Server Errors
|
|
78
|
+
*/
|
|
79
|
+
var SqliteError = class extends ServerError {
|
|
80
|
+
/** The underlying error */
|
|
81
|
+
cause;
|
|
82
|
+
constructor(message, cause) {
|
|
83
|
+
super(`SQLite error: ${message}`);
|
|
84
|
+
this.name = "SqliteError";
|
|
85
|
+
if (cause !== void 0) this.cause = cause;
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
//#endregion
|
|
90
|
+
//#region src/server/memory-kv.ts
|
|
91
|
+
/**
|
|
92
|
+
* In-memory key-value store for Gordian Envelopes.
|
|
93
|
+
*
|
|
94
|
+
* Provides volatile storage with TTL support and automatic cleanup of
|
|
95
|
+
* expired entries.
|
|
96
|
+
*
|
|
97
|
+
* Port of `struct MemoryKv` from server/memory_kv.rs lines 14-21.
|
|
98
|
+
*
|
|
99
|
+
* @category Server Backend
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* ```typescript
|
|
103
|
+
* const store = new MemoryKv();
|
|
104
|
+
* const arid = ARID.new();
|
|
105
|
+
* const envelope = Envelope.new("Hello, Memory!");
|
|
106
|
+
*
|
|
107
|
+
* await store.put(arid, envelope, 3600); // 1 hour TTL
|
|
108
|
+
* const result = await store.get(arid);
|
|
109
|
+
* ```
|
|
110
|
+
*/
|
|
111
|
+
var MemoryKv = class {
|
|
112
|
+
storage;
|
|
113
|
+
/**
|
|
114
|
+
* Create a new in-memory key-value store.
|
|
115
|
+
*
|
|
116
|
+
* Port of `MemoryKv::new()` from server/memory_kv.rs lines 29-33.
|
|
117
|
+
*/
|
|
118
|
+
constructor() {
|
|
119
|
+
this.storage = /* @__PURE__ */ new Map();
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Check if an ARID exists and is not expired.
|
|
123
|
+
*
|
|
124
|
+
* Port of `check_exists()` from server/memory_kv.rs lines 36-53.
|
|
125
|
+
*
|
|
126
|
+
* @internal
|
|
127
|
+
*/
|
|
128
|
+
checkExists(arid) {
|
|
129
|
+
const key = arid.urString();
|
|
130
|
+
const entry = this.storage.get(key);
|
|
131
|
+
if (entry) {
|
|
132
|
+
if (entry.expiresAt !== void 0 && Date.now() >= entry.expiresAt) {
|
|
133
|
+
this.storage.delete(key);
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Store an envelope at the given ARID.
|
|
142
|
+
*
|
|
143
|
+
* Port of `KvStore::put()` implementation from server/memory_kv.rs lines 62-102.
|
|
144
|
+
*/
|
|
145
|
+
async put(arid, envelope, ttlSeconds, verbose) {
|
|
146
|
+
const key = arid.urString();
|
|
147
|
+
if (this.storage.has(key)) {
|
|
148
|
+
if (verbose) require_logging.verbosePrintln(`PUT ${key} ALREADY_EXISTS`);
|
|
149
|
+
throw new require_logging.AlreadyExistsError(key);
|
|
150
|
+
}
|
|
151
|
+
const expiresAt = ttlSeconds !== void 0 ? Date.now() + ttlSeconds * 1e3 : void 0;
|
|
152
|
+
const entry = { envelopeCbor: envelope.taggedCborData() };
|
|
153
|
+
if (expiresAt !== void 0) entry.expiresAt = expiresAt;
|
|
154
|
+
this.storage.set(key, entry);
|
|
155
|
+
if (verbose) require_logging.verbosePrintln(`PUT ${key}${ttlSeconds !== void 0 ? ` (TTL ${ttlSeconds}s)` : ""} OK (Memory)`);
|
|
156
|
+
return "Stored in memory";
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Retrieve an envelope for the given ARID.
|
|
160
|
+
*
|
|
161
|
+
* Port of `KvStore::get()` implementation from server/memory_kv.rs lines 104-181.
|
|
162
|
+
*/
|
|
163
|
+
async get(arid, timeoutSeconds, verbose) {
|
|
164
|
+
const timeout = timeoutSeconds ?? 30;
|
|
165
|
+
const start = Date.now();
|
|
166
|
+
let firstAttempt = true;
|
|
167
|
+
const key = arid.urString();
|
|
168
|
+
const { EnvelopeDecoder } = await import("@bcts/envelope");
|
|
169
|
+
while (true) {
|
|
170
|
+
const entry = this.storage.get(key);
|
|
171
|
+
if (entry) {
|
|
172
|
+
if (entry.expiresAt !== void 0 && Date.now() >= entry.expiresAt) {
|
|
173
|
+
this.storage.delete(key);
|
|
174
|
+
if (verbose) require_logging.verbosePrintln(`GET ${key} EXPIRED`);
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
try {
|
|
178
|
+
const envelope = EnvelopeDecoder.tryFromCborData(entry.envelopeCbor);
|
|
179
|
+
if (verbose) require_logging.verbosePrintln(`GET ${key} OK (Memory)`);
|
|
180
|
+
return envelope;
|
|
181
|
+
} catch {
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
if ((Date.now() - start) / 1e3 >= timeout) {
|
|
186
|
+
if (verbose) require_logging.verbosePrintln(`GET ${key} NOT_FOUND (timeout after ${timeout}s)`);
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
if (firstAttempt && verbose) {
|
|
190
|
+
require_logging.verbosePrintln(`Polling for ${key} (timeout: ${timeout}s)`);
|
|
191
|
+
firstAttempt = false;
|
|
192
|
+
} else if (verbose) process.stdout.write(".");
|
|
193
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Check if an envelope exists at the given ARID.
|
|
198
|
+
*
|
|
199
|
+
* Port of `KvStore::exists()` implementation from server/memory_kv.rs lines 183-186.
|
|
200
|
+
*/
|
|
201
|
+
async exists(arid) {
|
|
202
|
+
return this.checkExists(arid);
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
//#endregion
|
|
207
|
+
//#region src/server/sqlite-kv.ts
|
|
208
|
+
/**
|
|
209
|
+
* SQLite-backed key-value store for Gordian Envelopes.
|
|
210
|
+
*
|
|
211
|
+
* Port of server/sqlite_kv.rs from hubert-rust.
|
|
212
|
+
*
|
|
213
|
+
* @module
|
|
214
|
+
*/
|
|
215
|
+
/**
|
|
216
|
+
* SQLite-backed key-value store for Gordian Envelopes.
|
|
217
|
+
*
|
|
218
|
+
* Provides persistent storage with TTL support and automatic cleanup of
|
|
219
|
+
* expired entries.
|
|
220
|
+
*
|
|
221
|
+
* Port of `struct SqliteKv` from server/sqlite_kv.rs lines 16-24.
|
|
222
|
+
*
|
|
223
|
+
* @category Server Backend
|
|
224
|
+
*
|
|
225
|
+
* @example
|
|
226
|
+
* ```typescript
|
|
227
|
+
* const store = new SqliteKv("./hubert.db");
|
|
228
|
+
* const arid = ARID.new();
|
|
229
|
+
* const envelope = Envelope.new("Hello, SQLite!");
|
|
230
|
+
*
|
|
231
|
+
* await store.put(arid, envelope, 3600); // 1 hour TTL
|
|
232
|
+
* const result = await store.get(arid);
|
|
233
|
+
*
|
|
234
|
+
* // Cleanup when done
|
|
235
|
+
* store.close();
|
|
236
|
+
* ```
|
|
237
|
+
*/
|
|
238
|
+
var SqliteKv = class {
|
|
239
|
+
db;
|
|
240
|
+
dbPath;
|
|
241
|
+
cleanupInterval = null;
|
|
242
|
+
/**
|
|
243
|
+
* Create a new SQLite-backed key-value store.
|
|
244
|
+
*
|
|
245
|
+
* Port of `SqliteKv::new()` from server/sqlite_kv.rs lines 26-67.
|
|
246
|
+
*
|
|
247
|
+
* @param dbPath - Path to the SQLite database file. Will be created if it doesn't exist.
|
|
248
|
+
* @throws {SqliteError} If database initialization fails
|
|
249
|
+
*/
|
|
250
|
+
constructor(dbPath) {
|
|
251
|
+
this.dbPath = dbPath;
|
|
252
|
+
const parentDir = path.dirname(dbPath);
|
|
253
|
+
if (parentDir && parentDir !== "." && !fs.existsSync(parentDir)) fs.mkdirSync(parentDir, { recursive: true });
|
|
254
|
+
try {
|
|
255
|
+
this.db = new better_sqlite3.default(dbPath);
|
|
256
|
+
this.db.exec(`
|
|
257
|
+
CREATE TABLE IF NOT EXISTS hubert_store (
|
|
258
|
+
arid TEXT PRIMARY KEY,
|
|
259
|
+
envelope TEXT NOT NULL,
|
|
260
|
+
expires_at INTEGER
|
|
261
|
+
);
|
|
262
|
+
CREATE INDEX IF NOT EXISTS idx_expires_at ON hubert_store(expires_at);
|
|
263
|
+
`);
|
|
264
|
+
} catch (error) {
|
|
265
|
+
throw new SqliteError(`Failed to initialize database: ${error instanceof Error ? error.message : String(error)}`, error instanceof Error ? error : void 0);
|
|
266
|
+
}
|
|
267
|
+
this.startCleanupTask();
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Start a background task that prunes expired entries every minute.
|
|
271
|
+
*
|
|
272
|
+
* Port of `start_cleanup_task()` from server/sqlite_kv.rs lines 70-126.
|
|
273
|
+
*
|
|
274
|
+
* @internal
|
|
275
|
+
*/
|
|
276
|
+
startCleanupTask() {
|
|
277
|
+
this.cleanupInterval = setInterval(() => {
|
|
278
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
279
|
+
try {
|
|
280
|
+
const arids = this.db.prepare("SELECT arid FROM hubert_store WHERE expires_at IS NOT NULL AND expires_at <= ?").all(now).map((row) => row.arid);
|
|
281
|
+
if (arids.length > 0) {
|
|
282
|
+
this.db.prepare("DELETE FROM hubert_store WHERE expires_at IS NOT NULL AND expires_at <= ?").run(now);
|
|
283
|
+
const count = arids.length;
|
|
284
|
+
const aridList = arids.join(" ");
|
|
285
|
+
require_logging.verbosePrintln(`Pruned ${count} expired ${count === 1 ? "entry" : "entries"}: ${aridList}`);
|
|
286
|
+
}
|
|
287
|
+
} catch {}
|
|
288
|
+
}, 60 * 1e3);
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Check if an ARID exists and is not expired.
|
|
292
|
+
*
|
|
293
|
+
* Port of `check_exists()` from server/sqlite_kv.rs lines 129-170.
|
|
294
|
+
*
|
|
295
|
+
* @internal
|
|
296
|
+
*/
|
|
297
|
+
checkExists(arid) {
|
|
298
|
+
const aridStr = arid.urString();
|
|
299
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
300
|
+
try {
|
|
301
|
+
const row = this.db.prepare("SELECT expires_at FROM hubert_store WHERE arid = ?").get(aridStr);
|
|
302
|
+
if (row) {
|
|
303
|
+
if (row.expires_at !== null && now >= row.expires_at) {
|
|
304
|
+
this.db.prepare("DELETE FROM hubert_store WHERE arid = ?").run(aridStr);
|
|
305
|
+
return false;
|
|
306
|
+
}
|
|
307
|
+
return true;
|
|
308
|
+
}
|
|
309
|
+
return false;
|
|
310
|
+
} catch {
|
|
311
|
+
return false;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Store an envelope at the given ARID.
|
|
316
|
+
*
|
|
317
|
+
* Port of `KvStore::put()` implementation from server/sqlite_kv.rs lines 175-236.
|
|
318
|
+
*/
|
|
319
|
+
async put(arid, envelope, ttlSeconds, verbose) {
|
|
320
|
+
if (this.checkExists(arid)) {
|
|
321
|
+
if (verbose) require_logging.verbosePrintln(`PUT ${arid.urString()} ALREADY_EXISTS`);
|
|
322
|
+
throw new require_logging.AlreadyExistsError(arid.urString());
|
|
323
|
+
}
|
|
324
|
+
const aridStr = arid.urString();
|
|
325
|
+
const envelopeStr = envelope.urString();
|
|
326
|
+
const expiresAt = ttlSeconds !== void 0 ? Math.floor(Date.now() / 1e3) + ttlSeconds : null;
|
|
327
|
+
try {
|
|
328
|
+
this.db.prepare("INSERT INTO hubert_store (arid, envelope, expires_at) VALUES (?, ?, ?)").run(aridStr, envelopeStr, expiresAt);
|
|
329
|
+
if (verbose) require_logging.verbosePrintln(`PUT ${aridStr}${ttlSeconds !== void 0 ? ` (TTL ${ttlSeconds}s)` : ""} OK (SQLite: ${this.dbPath})`);
|
|
330
|
+
return `Stored in SQLite: ${this.dbPath}`;
|
|
331
|
+
} catch (error) {
|
|
332
|
+
throw new SqliteError(`Failed to insert: ${error instanceof Error ? error.message : String(error)}`, error instanceof Error ? error : void 0);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Retrieve an envelope for the given ARID.
|
|
337
|
+
*
|
|
338
|
+
* Port of `KvStore::get()` implementation from server/sqlite_kv.rs lines 238-354.
|
|
339
|
+
*/
|
|
340
|
+
async get(arid, timeoutSeconds, verbose) {
|
|
341
|
+
const timeout = timeoutSeconds ?? 30;
|
|
342
|
+
const start = Date.now();
|
|
343
|
+
let firstAttempt = true;
|
|
344
|
+
const aridStr = arid.urString();
|
|
345
|
+
const { Envelope } = await import("@bcts/envelope");
|
|
346
|
+
while (true) {
|
|
347
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
348
|
+
try {
|
|
349
|
+
const row = this.db.prepare("SELECT envelope, expires_at FROM hubert_store WHERE arid = ?").get(aridStr);
|
|
350
|
+
if (row) {
|
|
351
|
+
if (row.expires_at !== null && now >= row.expires_at) {
|
|
352
|
+
this.db.prepare("DELETE FROM hubert_store WHERE arid = ?").run(aridStr);
|
|
353
|
+
if (verbose) require_logging.verbosePrintln(`GET ${aridStr} EXPIRED`);
|
|
354
|
+
return null;
|
|
355
|
+
}
|
|
356
|
+
const envelope = Envelope.fromUrString(row.envelope);
|
|
357
|
+
if (verbose) require_logging.verbosePrintln(`GET ${aridStr} OK (SQLite: ${this.dbPath})`);
|
|
358
|
+
return envelope;
|
|
359
|
+
}
|
|
360
|
+
} catch {}
|
|
361
|
+
if ((Date.now() - start) / 1e3 >= timeout) {
|
|
362
|
+
if (verbose) require_logging.verbosePrintln(`GET ${aridStr} NOT_FOUND (timeout after ${timeout}s)`);
|
|
363
|
+
return null;
|
|
364
|
+
}
|
|
365
|
+
if (firstAttempt && verbose) {
|
|
366
|
+
require_logging.verbosePrintln(`Polling for ${aridStr} (timeout: ${timeout}s)`);
|
|
367
|
+
firstAttempt = false;
|
|
368
|
+
} else if (verbose) process.stdout.write(".");
|
|
369
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Check if an envelope exists at the given ARID.
|
|
374
|
+
*
|
|
375
|
+
* Port of `KvStore::exists()` implementation from server/sqlite_kv.rs lines 356-359.
|
|
376
|
+
*/
|
|
377
|
+
async exists(arid) {
|
|
378
|
+
return this.checkExists(arid);
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Close the database connection and stop the cleanup task.
|
|
382
|
+
*/
|
|
383
|
+
close() {
|
|
384
|
+
if (this.cleanupInterval) {
|
|
385
|
+
clearInterval(this.cleanupInterval);
|
|
386
|
+
this.cleanupInterval = null;
|
|
387
|
+
}
|
|
388
|
+
this.db.close();
|
|
389
|
+
}
|
|
390
|
+
};
|
|
391
|
+
|
|
392
|
+
//#endregion
|
|
393
|
+
//#region src/server/server-kv.ts
|
|
394
|
+
/**
|
|
395
|
+
* Create a new in-memory server KV store.
|
|
396
|
+
*
|
|
397
|
+
* Port of `ServerKv::memory()` from server/server_kv.rs line 19.
|
|
398
|
+
*
|
|
399
|
+
* @returns A new MemoryKv instance
|
|
400
|
+
* @category Server Backend
|
|
401
|
+
*/
|
|
402
|
+
function createMemoryKv() {
|
|
403
|
+
return new MemoryKv();
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* Create a new SQLite-backed server KV store.
|
|
407
|
+
*
|
|
408
|
+
* Port of `ServerKv::sqlite()` from server/server_kv.rs line 22.
|
|
409
|
+
*
|
|
410
|
+
* @param store - The SqliteKv instance to use
|
|
411
|
+
* @returns The same SqliteKv instance
|
|
412
|
+
* @category Server Backend
|
|
413
|
+
*/
|
|
414
|
+
function createSqliteKv(store) {
|
|
415
|
+
return store;
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* Synchronously put an envelope into the store.
|
|
419
|
+
*
|
|
420
|
+
* This function wraps the async KvStore trait implementation for use
|
|
421
|
+
* in synchronous HTTP handlers.
|
|
422
|
+
*
|
|
423
|
+
* Port of `put_sync()` from server/server_kv.rs lines 27-52.
|
|
424
|
+
*
|
|
425
|
+
* @param store - The KvStore to use
|
|
426
|
+
* @param arid - The ARID to store at
|
|
427
|
+
* @param envelope - The envelope to store
|
|
428
|
+
* @param ttlSeconds - TTL in seconds
|
|
429
|
+
* @returns Promise that resolves when storage is complete
|
|
430
|
+
* @internal
|
|
431
|
+
*/
|
|
432
|
+
async function putSync(store, arid, envelope, ttlSeconds) {
|
|
433
|
+
await store.put(arid, envelope, ttlSeconds, false);
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Synchronously get an envelope from the store.
|
|
437
|
+
*
|
|
438
|
+
* This function wraps the async KvStore trait implementation for use
|
|
439
|
+
* in synchronous HTTP handlers.
|
|
440
|
+
*
|
|
441
|
+
* Port of `get_sync()` from server/server_kv.rs lines 58-71.
|
|
442
|
+
*
|
|
443
|
+
* @param store - The KvStore to use
|
|
444
|
+
* @param arid - The ARID to retrieve
|
|
445
|
+
* @returns The envelope if found, or null
|
|
446
|
+
* @internal
|
|
447
|
+
*/
|
|
448
|
+
async function getSync(store, arid) {
|
|
449
|
+
return await store.get(arid, 0, false);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
//#endregion
|
|
453
|
+
//#region src/server/server.ts
|
|
454
|
+
/**
|
|
455
|
+
* Hubert HTTP server implementation.
|
|
456
|
+
*
|
|
457
|
+
* Port of server/server.rs from hubert-rust.
|
|
458
|
+
*
|
|
459
|
+
* @module
|
|
460
|
+
*/
|
|
461
|
+
/**
|
|
462
|
+
* Package version for health endpoint.
|
|
463
|
+
*/
|
|
464
|
+
const VERSION = "1.0.0-alpha.1";
|
|
465
|
+
/**
|
|
466
|
+
* Default server configuration.
|
|
467
|
+
*
|
|
468
|
+
* Port of `impl Default for ServerConfig` from server/server.rs lines 32-40.
|
|
469
|
+
*/
|
|
470
|
+
const defaultServerConfig = {
|
|
471
|
+
port: 45678,
|
|
472
|
+
maxTtl: 86400,
|
|
473
|
+
verbose: false
|
|
474
|
+
};
|
|
475
|
+
/**
|
|
476
|
+
* Shared server state.
|
|
477
|
+
*
|
|
478
|
+
* Port of `struct ServerState` from server/server.rs lines 43-47.
|
|
479
|
+
*
|
|
480
|
+
* @internal
|
|
481
|
+
*/
|
|
482
|
+
var ServerState = class {
|
|
483
|
+
storage;
|
|
484
|
+
config;
|
|
485
|
+
constructor(config, storage) {
|
|
486
|
+
this.storage = storage;
|
|
487
|
+
this.config = config;
|
|
488
|
+
}
|
|
489
|
+
/**
|
|
490
|
+
* Put an envelope into storage.
|
|
491
|
+
*
|
|
492
|
+
* Port of `ServerState::put()` from server/server.rs lines 54-101.
|
|
493
|
+
*/
|
|
494
|
+
async put(arid, envelope, requestedTtl, clientIp) {
|
|
495
|
+
const maxTtl = this.config.maxTtl;
|
|
496
|
+
let ttl;
|
|
497
|
+
if (requestedTtl !== void 0) ttl = requestedTtl > maxTtl ? maxTtl : requestedTtl;
|
|
498
|
+
else ttl = maxTtl;
|
|
499
|
+
try {
|
|
500
|
+
await putSync(this.storage, arid, envelope, ttl);
|
|
501
|
+
if (this.config.verbose) require_logging.verbosePrintln(`${clientIp ? `${clientIp}: ` : ""}PUT ${arid.urString()} (TTL ${ttl}s) OK`);
|
|
502
|
+
} catch (error) {
|
|
503
|
+
if (this.config.verbose) {
|
|
504
|
+
const ipStr = clientIp ? `${clientIp}: ` : "";
|
|
505
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
506
|
+
require_logging.verbosePrintln(`${ipStr}PUT ${arid.urString()} (TTL ${ttl}s) ERROR: ${errorMsg}`);
|
|
507
|
+
}
|
|
508
|
+
throw error;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
/**
|
|
512
|
+
* Get an envelope from storage.
|
|
513
|
+
*
|
|
514
|
+
* Port of `ServerState::get()` from server/server.rs lines 103-126.
|
|
515
|
+
*/
|
|
516
|
+
async get(arid, clientIp) {
|
|
517
|
+
const result = await getSync(this.storage, arid);
|
|
518
|
+
if (this.config.verbose) {
|
|
519
|
+
const ipStr = clientIp ? `${clientIp}: ` : "";
|
|
520
|
+
const status = result ? "OK" : "NOT_FOUND";
|
|
521
|
+
require_logging.verbosePrintln(`${ipStr}GET ${arid.urString()} ${status}`);
|
|
522
|
+
}
|
|
523
|
+
return result;
|
|
524
|
+
}
|
|
525
|
+
};
|
|
526
|
+
/**
|
|
527
|
+
* Hubert HTTP server.
|
|
528
|
+
*
|
|
529
|
+
* Port of `struct Server` from server/server.rs lines 128-133.
|
|
530
|
+
*
|
|
531
|
+
* @category Server
|
|
532
|
+
*
|
|
533
|
+
* @example
|
|
534
|
+
* ```typescript
|
|
535
|
+
* const server = Server.newMemory({ port: 8080, maxTtl: 3600, verbose: true });
|
|
536
|
+
* await server.run();
|
|
537
|
+
* ```
|
|
538
|
+
*/
|
|
539
|
+
var Server = class Server {
|
|
540
|
+
config;
|
|
541
|
+
state;
|
|
542
|
+
fastify;
|
|
543
|
+
/**
|
|
544
|
+
* Create a new server with the given configuration and storage backend.
|
|
545
|
+
*
|
|
546
|
+
* Port of `Server::new()` from server/server.rs lines 135-139.
|
|
547
|
+
*/
|
|
548
|
+
constructor(config, storage) {
|
|
549
|
+
this.config = config;
|
|
550
|
+
this.state = new ServerState(config, storage);
|
|
551
|
+
this.fastify = (0, fastify.default)({ logger: false });
|
|
552
|
+
this.setupRoutes();
|
|
553
|
+
}
|
|
554
|
+
/**
|
|
555
|
+
* Create a new server with in-memory storage.
|
|
556
|
+
*
|
|
557
|
+
* Port of `Server::new_memory()` from server/server.rs lines 142-144.
|
|
558
|
+
*/
|
|
559
|
+
static newMemory(config = {}) {
|
|
560
|
+
return new Server({
|
|
561
|
+
...defaultServerConfig,
|
|
562
|
+
...config
|
|
563
|
+
}, new MemoryKv());
|
|
564
|
+
}
|
|
565
|
+
/**
|
|
566
|
+
* Create a new server with SQLite storage.
|
|
567
|
+
*
|
|
568
|
+
* Port of `Server::new_sqlite()` from server/server.rs lines 147-149.
|
|
569
|
+
*/
|
|
570
|
+
static newSqlite(config = {}, storage) {
|
|
571
|
+
return new Server({
|
|
572
|
+
...defaultServerConfig,
|
|
573
|
+
...config
|
|
574
|
+
}, storage);
|
|
575
|
+
}
|
|
576
|
+
/**
|
|
577
|
+
* Setup HTTP routes.
|
|
578
|
+
* @internal
|
|
579
|
+
*/
|
|
580
|
+
setupRoutes() {
|
|
581
|
+
this.fastify.get("/health", this.handleHealth.bind(this));
|
|
582
|
+
this.fastify.post("/put", this.handlePut.bind(this));
|
|
583
|
+
this.fastify.post("/get", this.handleGet.bind(this));
|
|
584
|
+
}
|
|
585
|
+
/**
|
|
586
|
+
* Handle health check requests.
|
|
587
|
+
*
|
|
588
|
+
* Port of `handle_health()` from server/server.rs lines 179-187.
|
|
589
|
+
*/
|
|
590
|
+
async handleHealth(_request, reply) {
|
|
591
|
+
reply.send({
|
|
592
|
+
server: "hubert",
|
|
593
|
+
version: VERSION,
|
|
594
|
+
status: "ok"
|
|
595
|
+
});
|
|
596
|
+
}
|
|
597
|
+
/**
|
|
598
|
+
* Handle PUT requests.
|
|
599
|
+
*
|
|
600
|
+
* Port of `handle_put()` from server/server.rs lines 195-238.
|
|
601
|
+
*/
|
|
602
|
+
async handlePut(request, reply) {
|
|
603
|
+
try {
|
|
604
|
+
const bodyStr = request.body;
|
|
605
|
+
if (typeof bodyStr !== "string") {
|
|
606
|
+
reply.status(400).send("Expected text body");
|
|
607
|
+
return;
|
|
608
|
+
}
|
|
609
|
+
const lines = bodyStr.split("\n");
|
|
610
|
+
if (lines.length < 2) {
|
|
611
|
+
reply.status(400).send("Expected at least 2 lines: ur:arid and ur:envelope");
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
614
|
+
let arid;
|
|
615
|
+
try {
|
|
616
|
+
arid = _bcts_components.ARID.fromUrString(lines[0]);
|
|
617
|
+
} catch {
|
|
618
|
+
reply.status(400).send("Invalid ur:arid");
|
|
619
|
+
return;
|
|
620
|
+
}
|
|
621
|
+
let envelope;
|
|
622
|
+
try {
|
|
623
|
+
envelope = _bcts_envelope.Envelope.fromUrString(lines[1]);
|
|
624
|
+
} catch {
|
|
625
|
+
reply.status(400).send("Invalid ur:envelope");
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
628
|
+
let ttl;
|
|
629
|
+
if (lines.length > 2 && lines[2].trim() !== "") {
|
|
630
|
+
const parsed = parseInt(lines[2], 10);
|
|
631
|
+
if (isNaN(parsed)) {
|
|
632
|
+
reply.status(400).send("Invalid TTL");
|
|
633
|
+
return;
|
|
634
|
+
}
|
|
635
|
+
ttl = parsed;
|
|
636
|
+
}
|
|
637
|
+
const clientIp = request.ip;
|
|
638
|
+
await this.state.put(arid, envelope, ttl, clientIp);
|
|
639
|
+
reply.status(200).send("OK");
|
|
640
|
+
} catch (error) {
|
|
641
|
+
if (error instanceof Error && error.name === "AlreadyExistsError") reply.status(409).send(error.message);
|
|
642
|
+
else reply.status(500).send(error instanceof Error ? error.message : "Internal server error");
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
/**
|
|
646
|
+
* Handle GET requests.
|
|
647
|
+
*
|
|
648
|
+
* Port of `handle_get()` from server/server.rs lines 244-269.
|
|
649
|
+
*/
|
|
650
|
+
async handleGet(request, reply) {
|
|
651
|
+
try {
|
|
652
|
+
const bodyStr = request.body;
|
|
653
|
+
if (typeof bodyStr !== "string") {
|
|
654
|
+
reply.status(400).send("Expected text body");
|
|
655
|
+
return;
|
|
656
|
+
}
|
|
657
|
+
const aridStr = bodyStr.trim();
|
|
658
|
+
if (aridStr === "") {
|
|
659
|
+
reply.status(400).send("Expected ur:arid");
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
let arid;
|
|
663
|
+
try {
|
|
664
|
+
arid = _bcts_components.ARID.fromUrString(aridStr);
|
|
665
|
+
} catch {
|
|
666
|
+
reply.status(400).send("Invalid ur:arid");
|
|
667
|
+
return;
|
|
668
|
+
}
|
|
669
|
+
const clientIp = request.ip;
|
|
670
|
+
const envelope = await this.state.get(arid, clientIp);
|
|
671
|
+
if (envelope) reply.status(200).send(envelope.urString());
|
|
672
|
+
else reply.status(404).send("Not found");
|
|
673
|
+
} catch (error) {
|
|
674
|
+
reply.status(500).send(error instanceof Error ? error.message : "Internal server error");
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
/**
|
|
678
|
+
* Run the server.
|
|
679
|
+
*
|
|
680
|
+
* Port of `Server::run()` from server/server.rs lines 152-170.
|
|
681
|
+
*/
|
|
682
|
+
async run() {
|
|
683
|
+
const addr = `127.0.0.1:${this.config.port}`;
|
|
684
|
+
this.fastify.addContentTypeParser("text/plain", { parseAs: "string" }, (_request, payload, done) => {
|
|
685
|
+
done(null, payload);
|
|
686
|
+
});
|
|
687
|
+
this.fastify.addContentTypeParser("application/octet-stream", { parseAs: "string" }, (_request, payload, done) => {
|
|
688
|
+
done(null, payload);
|
|
689
|
+
});
|
|
690
|
+
await this.fastify.listen({
|
|
691
|
+
port: this.config.port,
|
|
692
|
+
host: "127.0.0.1"
|
|
693
|
+
});
|
|
694
|
+
console.log(`✓ Hubert server listening on ${addr}`);
|
|
695
|
+
}
|
|
696
|
+
/**
|
|
697
|
+
* Get the port the server is configured to listen on.
|
|
698
|
+
*
|
|
699
|
+
* Port of `Server::port()` from server/server.rs line 173.
|
|
700
|
+
*/
|
|
701
|
+
port() {
|
|
702
|
+
return this.config.port;
|
|
703
|
+
}
|
|
704
|
+
/**
|
|
705
|
+
* Stop the server.
|
|
706
|
+
*/
|
|
707
|
+
async close() {
|
|
708
|
+
await this.fastify.close();
|
|
709
|
+
}
|
|
710
|
+
};
|
|
711
|
+
|
|
712
|
+
//#endregion
|
|
713
|
+
//#region src/server/kv.ts
|
|
714
|
+
/**
|
|
715
|
+
* Server-backed key-value store using HTTP API.
|
|
716
|
+
*
|
|
717
|
+
* This implementation communicates with a Hubert server via HTTP POST requests.
|
|
718
|
+
*
|
|
719
|
+
* Port of `struct ServerKvClient` from server/kv.rs lines 6-37.
|
|
720
|
+
*
|
|
721
|
+
* @category Server Backend
|
|
722
|
+
*
|
|
723
|
+
* @example
|
|
724
|
+
* ```typescript
|
|
725
|
+
* const store = new ServerKvClient("http://127.0.0.1:45678");
|
|
726
|
+
* const arid = ARID.new();
|
|
727
|
+
* const envelope = Envelope.new("Hello, Server!");
|
|
728
|
+
*
|
|
729
|
+
* // Put envelope (write-once)
|
|
730
|
+
* await store.put(arid, envelope);
|
|
731
|
+
*
|
|
732
|
+
* // Get envelope with verbose logging
|
|
733
|
+
* const retrieved = await store.get(arid, undefined, true);
|
|
734
|
+
* ```
|
|
735
|
+
*/
|
|
736
|
+
var ServerKvClient = class {
|
|
737
|
+
baseUrl;
|
|
738
|
+
/**
|
|
739
|
+
* Create a new server KV store client.
|
|
740
|
+
*
|
|
741
|
+
* Port of `ServerKvClient::new()` from server/kv.rs lines 39-46.
|
|
742
|
+
*
|
|
743
|
+
* @param baseUrl - Base URL of the Hubert server (e.g., "http://127.0.0.1:45678")
|
|
744
|
+
*/
|
|
745
|
+
constructor(baseUrl) {
|
|
746
|
+
this.baseUrl = baseUrl;
|
|
747
|
+
}
|
|
748
|
+
/**
|
|
749
|
+
* Store an envelope at the given ARID.
|
|
750
|
+
*
|
|
751
|
+
* Port of `KvStore::put()` implementation from server/kv.rs lines 67-122.
|
|
752
|
+
*/
|
|
753
|
+
async put(arid, envelope, ttlSeconds, verbose) {
|
|
754
|
+
if (verbose) require_logging.verbosePrintln("Starting server put operation");
|
|
755
|
+
let body;
|
|
756
|
+
if (ttlSeconds !== void 0) body = `${arid.urString()}\n${envelope.urString()}\n${ttlSeconds}`;
|
|
757
|
+
else body = `${arid.urString()}\n${envelope.urString()}`;
|
|
758
|
+
if (verbose) require_logging.verbosePrintln("Sending PUT request to server");
|
|
759
|
+
try {
|
|
760
|
+
const response = await fetch(`${this.baseUrl}/put`, {
|
|
761
|
+
method: "POST",
|
|
762
|
+
body,
|
|
763
|
+
headers: { "Content-Type": "text/plain" }
|
|
764
|
+
});
|
|
765
|
+
if (response.status === 200) {
|
|
766
|
+
if (verbose) require_logging.verbosePrintln("Server put operation completed");
|
|
767
|
+
return "Stored successfully";
|
|
768
|
+
} else if (response.status === 409) {
|
|
769
|
+
if (verbose) require_logging.verbosePrintln("Server put operation failed");
|
|
770
|
+
throw new require_logging.AlreadyExistsError(arid.urString());
|
|
771
|
+
} else {
|
|
772
|
+
if (verbose) require_logging.verbosePrintln("Server put operation failed");
|
|
773
|
+
throw new ServerGeneralError(await response.text());
|
|
774
|
+
}
|
|
775
|
+
} catch (error) {
|
|
776
|
+
if (error instanceof require_logging.AlreadyExistsError || error instanceof ServerGeneralError) throw error;
|
|
777
|
+
throw new ServerNetworkError(error instanceof Error ? error.message : String(error));
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
/**
|
|
781
|
+
* Retrieve an envelope for the given ARID.
|
|
782
|
+
*
|
|
783
|
+
* Port of `KvStore::get()` implementation from server/kv.rs lines 124-212.
|
|
784
|
+
*/
|
|
785
|
+
async get(arid, timeoutSeconds, verbose) {
|
|
786
|
+
let printedDot = false;
|
|
787
|
+
if (verbose) require_logging.verbosePrintln("Starting server get operation");
|
|
788
|
+
const timeout = timeoutSeconds ?? 30;
|
|
789
|
+
const deadline = Date.now() + timeout * 1e3;
|
|
790
|
+
const pollInterval = 1e3;
|
|
791
|
+
if (verbose) require_logging.verbosePrintln("Polling server for value");
|
|
792
|
+
while (true) {
|
|
793
|
+
const body = arid.urString();
|
|
794
|
+
try {
|
|
795
|
+
const response = await fetch(`${this.baseUrl}/get`, {
|
|
796
|
+
method: "POST",
|
|
797
|
+
body,
|
|
798
|
+
headers: { "Content-Type": "text/plain" }
|
|
799
|
+
});
|
|
800
|
+
if (response.status === 200) {
|
|
801
|
+
if (verbose && printedDot) require_logging.verboseNewline();
|
|
802
|
+
if (verbose) require_logging.verbosePrintln("Value found on server");
|
|
803
|
+
const envelopeStr = await response.text();
|
|
804
|
+
try {
|
|
805
|
+
const envelope = _bcts_envelope.Envelope.fromUrString(envelopeStr);
|
|
806
|
+
if (verbose) require_logging.verbosePrintln("Server get operation completed");
|
|
807
|
+
return envelope;
|
|
808
|
+
} catch (error) {
|
|
809
|
+
throw new ServerParseError(error instanceof Error ? error.message : String(error));
|
|
810
|
+
}
|
|
811
|
+
} else if (response.status === 404) {
|
|
812
|
+
if (Date.now() >= deadline) {
|
|
813
|
+
if (verbose && printedDot) require_logging.verboseNewline();
|
|
814
|
+
if (verbose) require_logging.verbosePrintln("Timeout reached, value not found");
|
|
815
|
+
return null;
|
|
816
|
+
}
|
|
817
|
+
if (verbose) {
|
|
818
|
+
require_logging.verbosePrintDot();
|
|
819
|
+
printedDot = true;
|
|
820
|
+
}
|
|
821
|
+
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
|
822
|
+
} else throw new ServerGeneralError(await response.text());
|
|
823
|
+
} catch (error) {
|
|
824
|
+
if (error instanceof ServerGeneralError || error instanceof ServerParseError) throw error;
|
|
825
|
+
throw new ServerNetworkError(error instanceof Error ? error.message : String(error));
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
/**
|
|
830
|
+
* Check if an envelope exists at the given ARID.
|
|
831
|
+
*
|
|
832
|
+
* Port of `KvStore::exists()` implementation from server/kv.rs lines 214-218.
|
|
833
|
+
*/
|
|
834
|
+
async exists(arid) {
|
|
835
|
+
return await this.get(arid, 1, false) !== null;
|
|
836
|
+
}
|
|
837
|
+
};
|
|
838
|
+
|
|
839
|
+
//#endregion
|
|
840
|
+
Object.defineProperty(exports, 'MemoryKv', {
|
|
841
|
+
enumerable: true,
|
|
842
|
+
get: function () {
|
|
843
|
+
return MemoryKv;
|
|
844
|
+
}
|
|
845
|
+
});
|
|
846
|
+
Object.defineProperty(exports, 'Server', {
|
|
847
|
+
enumerable: true,
|
|
848
|
+
get: function () {
|
|
849
|
+
return Server;
|
|
850
|
+
}
|
|
851
|
+
});
|
|
852
|
+
Object.defineProperty(exports, 'ServerError', {
|
|
853
|
+
enumerable: true,
|
|
854
|
+
get: function () {
|
|
855
|
+
return ServerError;
|
|
856
|
+
}
|
|
857
|
+
});
|
|
858
|
+
Object.defineProperty(exports, 'ServerGeneralError', {
|
|
859
|
+
enumerable: true,
|
|
860
|
+
get: function () {
|
|
861
|
+
return ServerGeneralError;
|
|
862
|
+
}
|
|
863
|
+
});
|
|
864
|
+
Object.defineProperty(exports, 'ServerKvClient', {
|
|
865
|
+
enumerable: true,
|
|
866
|
+
get: function () {
|
|
867
|
+
return ServerKvClient;
|
|
868
|
+
}
|
|
869
|
+
});
|
|
870
|
+
Object.defineProperty(exports, 'ServerNetworkError', {
|
|
871
|
+
enumerable: true,
|
|
872
|
+
get: function () {
|
|
873
|
+
return ServerNetworkError;
|
|
874
|
+
}
|
|
875
|
+
});
|
|
876
|
+
Object.defineProperty(exports, 'ServerParseError', {
|
|
877
|
+
enumerable: true,
|
|
878
|
+
get: function () {
|
|
879
|
+
return ServerParseError;
|
|
880
|
+
}
|
|
881
|
+
});
|
|
882
|
+
Object.defineProperty(exports, 'SqliteError', {
|
|
883
|
+
enumerable: true,
|
|
884
|
+
get: function () {
|
|
885
|
+
return SqliteError;
|
|
886
|
+
}
|
|
887
|
+
});
|
|
888
|
+
Object.defineProperty(exports, 'SqliteKv', {
|
|
889
|
+
enumerable: true,
|
|
890
|
+
get: function () {
|
|
891
|
+
return SqliteKv;
|
|
892
|
+
}
|
|
893
|
+
});
|
|
894
|
+
Object.defineProperty(exports, 'createMemoryKv', {
|
|
895
|
+
enumerable: true,
|
|
896
|
+
get: function () {
|
|
897
|
+
return createMemoryKv;
|
|
898
|
+
}
|
|
899
|
+
});
|
|
900
|
+
Object.defineProperty(exports, 'createSqliteKv', {
|
|
901
|
+
enumerable: true,
|
|
902
|
+
get: function () {
|
|
903
|
+
return createSqliteKv;
|
|
904
|
+
}
|
|
905
|
+
});
|
|
906
|
+
Object.defineProperty(exports, 'defaultServerConfig', {
|
|
907
|
+
enumerable: true,
|
|
908
|
+
get: function () {
|
|
909
|
+
return defaultServerConfig;
|
|
910
|
+
}
|
|
911
|
+
});
|
|
912
|
+
//# sourceMappingURL=server-BBNRZ30D.cjs.map
|