@pineliner/odb-client 1.4.0 → 1.5.1

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.
@@ -1 +1 @@
1
- {"version":3,"file":"odblite-socket.d.ts","sourceRoot":"","sources":["../../../src/database/adapters/odblite-socket.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,eAAe,EAAkC,MAAM,UAAU,CAAA;AAExG,OAAO,EAAgB,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAA;AA0KjE,MAAM,WAAW,mBAAoB,SAAQ,YAAY;CAAG;AAE5D,qBAAa,oBAAqB,YAAW,eAAe;IAC1D,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAmB;IAC7C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;gBAEzB,MAAM,EAAE,mBAAmB;IAIjC,OAAO,CAAC,MAAM,EAAE,GAAG,GAAG,OAAO,CAAC,UAAU,CAAC;IAMzC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAI3B,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;CAmBpC;AAED;;mCAEmC;AACnC,eAAO,MAAM,sBAAsB,IAAI,CAAA;AAEvC,sFAAsF;AACtF,eAAO,MAAM,mBAAmB,sBAAsB,CAAA;AAEtD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,CAAC,EAAE,MAAM,GAAG,YAAY,CAO1D;AAED;;;;;;;;;;;;GAYG;AACH,qBAAa,kBAAmB,YAAW,eAAe;IACxD,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAY;IACtC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA6C;IAClE,OAAO,CAAC,MAAM,CAA0B;IACxC,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,WAAW,CAAI;gBAEX,IAAI,EAAE;QAAE,MAAM,CAAC,EAAE,mBAAmB,CAAC;QAAC,IAAI,EAAE,GAAG,CAAA;KAAE;YAK/C,IAAI;IAWlB,oEAAoE;YACtD,SAAS;IAgBvB;;8CAE0C;YAC5B,YAAY;IAY1B;uFACmF;IACnF,OAAO,CAAC,UAAU;IAgBZ,OAAO,CAAC,MAAM,EAAE,GAAG,GAAG,OAAO,CAAC,UAAU,CAAC;IAIzC,UAAU,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAG5C,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;CAGpC"}
1
+ {"version":3,"file":"odblite-socket.d.ts","sourceRoot":"","sources":["../../../src/database/adapters/odblite-socket.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,eAAe,EAAkC,MAAM,UAAU,CAAA;AAExG,OAAO,EAAgB,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAA;AA+KjE,MAAM,WAAW,mBAAoB,SAAQ,YAAY;CAAG;AAE5D,qBAAa,oBAAqB,YAAW,eAAe;IAC1D,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAmB;IAC7C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;gBAEzB,MAAM,EAAE,mBAAmB;IAIjC,OAAO,CAAC,MAAM,EAAE,GAAG,GAAG,OAAO,CAAC,UAAU,CAAC;IAMzC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAI3B,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;CAmBpC;AAED;;mCAEmC;AACnC,eAAO,MAAM,sBAAsB,IAAI,CAAA;AAEvC,sFAAsF;AACtF,eAAO,MAAM,mBAAmB,sBAAsB,CAAA;AAEtD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,CAAC,EAAE,MAAM,GAAG,YAAY,CAO1D;AAED;;;;;;;;;;;;GAYG;AACH,qBAAa,kBAAmB,YAAW,eAAe;IACxD,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAY;IACtC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA6C;IAClE,OAAO,CAAC,MAAM,CAA0B;IACxC,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,WAAW,CAAI;gBAEX,IAAI,EAAE;QAAE,MAAM,CAAC,EAAE,mBAAmB,CAAC;QAAC,IAAI,EAAE,GAAG,CAAA;KAAE;YAK/C,IAAI;IAWlB,oEAAoE;YACtD,SAAS;IAgBvB;;8CAE0C;YAC5B,YAAY;IAY1B;uFACmF;IACnF,OAAO,CAAC,UAAU;IAgBZ,OAAO,CAAC,MAAM,EAAE,GAAG,GAAG,OAAO,CAAC,UAAU,CAAC;IAIzC,UAAU,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAG5C,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;CAGpC"}
@@ -31,6 +31,10 @@ export declare class SocketClient {
31
31
  * dropped connection FAILS its requests (the caller retries) instead of hanging. */
32
32
  private failPending;
33
33
  private connect;
34
+ /** Reconnect with bounded backoff — bridges a router restart so a request landing in the down-window
35
+ * waits it out instead of failing. After the last attempt the error propagates (caller retries / the
36
+ * auto-adapter can fall back to HTTP). Backoff: 50·100·200·400·800·1600ms ≈ 3s total by default. */
37
+ private connectWithRetry;
34
38
  /** Allocate a fresh stream id for an interactive transaction (unique per connection). The server
35
39
  * pins one SQLite connection per stream id, so all of a tx's statements run on it (read-your-own-
36
40
  * writes). The id is carried in every frame of that tx via pipeline()'s `stream` arg. */
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../../src/database/adapters/socket/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,KAAK,aAAa,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAA;AAE5F;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAaD,qBAAa,YAAY;IASX,OAAO,CAAC,QAAQ,CAAC,MAAM;IARnC,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA6B;IACrD,OAAO,CAAC,MAAM,CAAI;IAClB,OAAO,CAAC,YAAY,CAAI;IACxB,OAAO,CAAC,KAAK,CAAe;gBAEC,MAAM,EAAE,YAAY;IAOjD;yFACqF;IACrF,OAAO,CAAC,WAAW;YAQL,OAAO;IAuCrB;;8FAE0F;IAC1F,WAAW,IAAI,MAAM;IAIf,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,EAAE,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAwBnG,KAAK,IAAI,IAAI;CAKd"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../../src/database/adapters/socket/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,KAAK,aAAa,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAA;AAE5F;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAqBD,qBAAa,YAAY;IASX,OAAO,CAAC,QAAQ,CAAC,MAAM;IARnC,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA6B;IACrD,OAAO,CAAC,MAAM,CAAI;IAClB,OAAO,CAAC,YAAY,CAAI;IACxB,OAAO,CAAC,KAAK,CAAe;gBAEC,MAAM,EAAE,YAAY;IAOjD;yFACqF;IACrF,OAAO,CAAC,WAAW;YAQL,OAAO;IAsDrB;;yGAEqG;YACvF,gBAAgB;IAc9B;;8FAE0F;IAC1F,WAAW,IAAI,MAAM;IAIf,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,EAAE,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IA0BnG,KAAK,IAAI,IAAI;CAKd"}
@@ -1 +1 @@
1
- {"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../../../src/database/adapters/socket/protocol.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../../../src/database/adapters/socket/protocol.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,MAAM,MAAM,UAAU,GAClB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAClC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAChC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC/B;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAA;AAEpC,wBAAgB,OAAO,CAAC,CAAC,EAAE,GAAG,GAAG,UAAU,CAQ1C;AAED,wBAAgB,SAAS,CAAC,CAAC,EAAE,UAAU,GAAG,GAAG,CAe5C;AAGD,MAAM,WAAW,IAAI;IACnB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,UAAU,EAAE,CAAA;IACnB,UAAU,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,UAAU,CAAA;KAAE,EAAE,CAAA;IAClD,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB;AACD,MAAM,MAAM,SAAS,GACjB;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC5B;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC/B;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,SAAS,CAAA;CAAE,GAChC;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,SAAS,EAAE,CAAA;CAAE,GACnC;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,SAAS,EAAE,CAAA;CAAE,GAClC;IAAE,IAAI,EAAE,eAAe,CAAA;CAAE,CAAA;AAC7B,MAAM,WAAW,SAAS;IACxB,SAAS,CAAC,EAAE,SAAS,GAAG,IAAI,CAAA;IAC5B,IAAI,EAAE,IAAI,CAAA;IACV,MAAM,CAAC,EAAE;QAAE,QAAQ,EAAE,KAAK,GAAG,YAAY,GAAG,MAAM,CAAA;KAAE,CAAA;CACrD;AACD,MAAM,WAAW,KAAK;IACpB,KAAK,EAAE,SAAS,EAAE,CAAA;CACnB;AACD,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,EAAE,CAAA;IAC/B,IAAI,EAAE,UAAU,EAAE,EAAE,CAAA;IACpB,kBAAkB,EAAE,MAAM,CAAA;IAC1B,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAA;CACjC;AACD,MAAM,WAAW,WAAW;IAC1B,YAAY,EAAE,CAAC,UAAU,GAAG,IAAI,CAAC,EAAE,CAAA;IACnC,WAAW,EAAE,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC,EAAE,CAAA;CAC3D;AACD,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AACD,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,IAAI,CAAA;CAAE,GAC/B;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,KAAK,CAAA;CAAE,GAC/B;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GACjC;IAAE,IAAI,EAAE,gBAAgB,CAAA;CAAE,GAC1B;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,GACvB;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,CAAA;AACrB,MAAM,MAAM,YAAY,GACpB;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,GAAG,CAAA;CAAE,GAC7B;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,UAAU,CAAA;CAAE,CAAA;AAGxC,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,CAAC,EAAE,GAAG,EAAE,CAAA;IACd,MAAM,CAAC,EAAE,KAAK,GAAG,YAAY,GAAG,MAAM,CAAA;CACvC;AACD,wBAAgB,OAAO,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,KAAK,CAQlD;AAOD,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEpD;AAGD,UAAU,QAAQ;IAChB,KAAK,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAAA;CAChC;AACD,qBAAa,WAAW;IAEV,OAAO,CAAC,QAAQ,CAAC,MAAM;IADnC,OAAO,CAAC,OAAO,CAA0B;gBACZ,MAAM,EAAE,QAAQ;IAC7C,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAO3B,KAAK,IAAI,IAAI;CAOd;AAKD,qBAAa,WAAW;IACtB,OAAO,CAAC,GAAG,CAA0B;IACrC,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE;CAY9B"}
package/dist/index.cjs CHANGED
@@ -2235,6 +2235,7 @@ var __webpack_exports__ = {};
2235
2235
  }
2236
2236
  }
2237
2237
  }
2238
+ const MAX_FRAME = 1024 * (Number(globalThis.process?.env?.ODB_MAX_FRAME_MB) || 64) * 1024;
2238
2239
  class FrameReader {
2239
2240
  buf = Buffer.alloc(0);
2240
2241
  push(chunk) {
@@ -2245,6 +2246,7 @@ var __webpack_exports__ = {};
2245
2246
  const out = [];
2246
2247
  while(this.buf.length >= 4){
2247
2248
  const len = this.buf.readUInt32LE(0);
2249
+ if (len > MAX_FRAME) throw new Error(`odblite reply frame too large: ${len} bytes (max ${MAX_FRAME})`);
2248
2250
  if (this.buf.length < 4 + len) break;
2249
2251
  out.push(this.buf.subarray(4, 4 + len));
2250
2252
  this.buf = this.buf.subarray(4 + len);
@@ -2253,6 +2255,9 @@ var __webpack_exports__ = {};
2253
2255
  }
2254
2256
  }
2255
2257
  const REQUEST_TIMEOUT_MS = Number(globalThis.process?.env?.ODB_SOCKET_REQUEST_TIMEOUT_MS) || 30000;
2258
+ const RECONNECT_ATTEMPTS = Number(globalThis.process?.env?.ODB_SOCKET_RECONNECT_ATTEMPTS) || 6;
2259
+ const RECONNECT_BASE_MS = Number(globalThis.process?.env?.ODB_SOCKET_RECONNECT_BASE_MS) || 50;
2260
+ const sleep = (ms)=>new Promise((r)=>setTimeout(r, ms));
2256
2261
  class SocketClient {
2257
2262
  target;
2258
2263
  socket;
@@ -2286,8 +2291,20 @@ var __webpack_exports__ = {};
2286
2291
  this.socket = await Bun.connect({
2287
2292
  ...opts,
2288
2293
  socket: {
2289
- data: (_s, chunk)=>{
2290
- for (const frame of this.reader.push(chunk)){
2294
+ data: (s, chunk)=>{
2295
+ let frames;
2296
+ try {
2297
+ frames = this.reader.push(chunk);
2298
+ } catch (err) {
2299
+ this.socket = void 0;
2300
+ this.reader = new FrameReader();
2301
+ try {
2302
+ s.end?.();
2303
+ } catch {}
2304
+ this.failPending(err instanceof Error ? err : new Error(`odblite framing error: ${err}`));
2305
+ return;
2306
+ }
2307
+ for (const frame of frames){
2291
2308
  const msg = JSON.parse(frame.toString('utf8'));
2292
2309
  const p = this.pending.get(msg.id);
2293
2310
  if (p) {
@@ -2312,13 +2329,24 @@ var __webpack_exports__ = {};
2312
2329
  });
2313
2330
  this.writer = new FrameWriter(this.socket);
2314
2331
  }
2332
+ async connectWithRetry() {
2333
+ let lastErr;
2334
+ for(let attempt = 0; attempt < RECONNECT_ATTEMPTS; attempt++)try {
2335
+ await this.connect();
2336
+ return;
2337
+ } catch (err) {
2338
+ lastErr = err;
2339
+ if (attempt < RECONNECT_ATTEMPTS - 1) await sleep(RECONNECT_BASE_MS * 2 ** attempt);
2340
+ }
2341
+ throw lastErr instanceof Error ? lastErr : new Error(`odblite socket reconnect failed: ${lastErr}`);
2342
+ }
2315
2343
  allocStream() {
2316
2344
  return this.nextStreamId++;
2317
2345
  }
2318
2346
  async pipeline(tenant, requests, stream) {
2319
- await this.ready;
2347
+ await this.ready.catch(()=>{});
2320
2348
  if (!this.socket) {
2321
- this.ready = this.connect();
2349
+ this.ready = this.connectWithRetry();
2322
2350
  this.ready.catch(()=>{});
2323
2351
  await this.ready;
2324
2352
  }
@@ -2424,13 +2452,15 @@ var __webpack_exports__ = {};
2424
2452
  return toQueryResult(res);
2425
2453
  }
2426
2454
  async query(sql, params = []) {
2427
- if (null != this.streamId) return await this.streamExec(sql, params);
2455
+ const s = 'string' == typeof sql ? sql : sql.sql;
2456
+ const p = 'string' == typeof sql ? params : sql.args ?? [];
2457
+ if (null != this.streamId) return await this.streamExec(s, p);
2428
2458
  return toQueryResult(await this.one([
2429
2459
  {
2430
2460
  type: 'execute',
2431
2461
  stmt: {
2432
- sql,
2433
- args: params.map(toHrana)
2462
+ sql: s,
2463
+ args: p.map(toHrana)
2434
2464
  }
2435
2465
  }
2436
2466
  ]));
package/dist/index.js CHANGED
@@ -2145,6 +2145,7 @@ class FrameWriter {
2145
2145
  }
2146
2146
  }
2147
2147
  }
2148
+ const MAX_FRAME = 1024 * (Number(globalThis.process?.env?.ODB_MAX_FRAME_MB) || 64) * 1024;
2148
2149
  class FrameReader {
2149
2150
  buf = Buffer.alloc(0);
2150
2151
  push(chunk) {
@@ -2155,6 +2156,7 @@ class FrameReader {
2155
2156
  const out = [];
2156
2157
  while(this.buf.length >= 4){
2157
2158
  const len = this.buf.readUInt32LE(0);
2159
+ if (len > MAX_FRAME) throw new Error(`odblite reply frame too large: ${len} bytes (max ${MAX_FRAME})`);
2158
2160
  if (this.buf.length < 4 + len) break;
2159
2161
  out.push(this.buf.subarray(4, 4 + len));
2160
2162
  this.buf = this.buf.subarray(4 + len);
@@ -2163,6 +2165,9 @@ class FrameReader {
2163
2165
  }
2164
2166
  }
2165
2167
  const REQUEST_TIMEOUT_MS = Number(globalThis.process?.env?.ODB_SOCKET_REQUEST_TIMEOUT_MS) || 30000;
2168
+ const RECONNECT_ATTEMPTS = Number(globalThis.process?.env?.ODB_SOCKET_RECONNECT_ATTEMPTS) || 6;
2169
+ const RECONNECT_BASE_MS = Number(globalThis.process?.env?.ODB_SOCKET_RECONNECT_BASE_MS) || 50;
2170
+ const sleep = (ms)=>new Promise((r)=>setTimeout(r, ms));
2166
2171
  class SocketClient {
2167
2172
  target;
2168
2173
  socket;
@@ -2196,8 +2201,20 @@ class SocketClient {
2196
2201
  this.socket = await Bun.connect({
2197
2202
  ...opts,
2198
2203
  socket: {
2199
- data: (_s, chunk)=>{
2200
- for (const frame of this.reader.push(chunk)){
2204
+ data: (s, chunk)=>{
2205
+ let frames;
2206
+ try {
2207
+ frames = this.reader.push(chunk);
2208
+ } catch (err) {
2209
+ this.socket = void 0;
2210
+ this.reader = new FrameReader();
2211
+ try {
2212
+ s.end?.();
2213
+ } catch {}
2214
+ this.failPending(err instanceof Error ? err : new Error(`odblite framing error: ${err}`));
2215
+ return;
2216
+ }
2217
+ for (const frame of frames){
2201
2218
  const msg = JSON.parse(frame.toString('utf8'));
2202
2219
  const p = this.pending.get(msg.id);
2203
2220
  if (p) {
@@ -2222,13 +2239,24 @@ class SocketClient {
2222
2239
  });
2223
2240
  this.writer = new FrameWriter(this.socket);
2224
2241
  }
2242
+ async connectWithRetry() {
2243
+ let lastErr;
2244
+ for(let attempt = 0; attempt < RECONNECT_ATTEMPTS; attempt++)try {
2245
+ await this.connect();
2246
+ return;
2247
+ } catch (err) {
2248
+ lastErr = err;
2249
+ if (attempt < RECONNECT_ATTEMPTS - 1) await sleep(RECONNECT_BASE_MS * 2 ** attempt);
2250
+ }
2251
+ throw lastErr instanceof Error ? lastErr : new Error(`odblite socket reconnect failed: ${lastErr}`);
2252
+ }
2225
2253
  allocStream() {
2226
2254
  return this.nextStreamId++;
2227
2255
  }
2228
2256
  async pipeline(tenant, requests, stream) {
2229
- await this.ready;
2257
+ await this.ready.catch(()=>{});
2230
2258
  if (!this.socket) {
2231
- this.ready = this.connect();
2259
+ this.ready = this.connectWithRetry();
2232
2260
  this.ready.catch(()=>{});
2233
2261
  await this.ready;
2234
2262
  }
@@ -2334,13 +2362,15 @@ class OdbliteSocketConnection {
2334
2362
  return toQueryResult(res);
2335
2363
  }
2336
2364
  async query(sql, params = []) {
2337
- if (null != this.streamId) return await this.streamExec(sql, params);
2365
+ const s = 'string' == typeof sql ? sql : sql.sql;
2366
+ const p = 'string' == typeof sql ? params : sql.args ?? [];
2367
+ if (null != this.streamId) return await this.streamExec(s, p);
2338
2368
  return toQueryResult(await this.one([
2339
2369
  {
2340
2370
  type: 'execute',
2341
2371
  stmt: {
2342
- sql,
2343
- args: params.map(toHrana)
2372
+ sql: s,
2373
+ args: p.map(toHrana)
2344
2374
  }
2345
2375
  }
2346
2376
  ]));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pineliner/odb-client",
3
- "version": "1.4.0",
3
+ "version": "1.5.1",
4
4
  "description": "Isomorphic client for ODB-Lite with postgres.js-like template string SQL support",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",
@@ -91,9 +91,14 @@ class OdbliteSocketConnection implements Connection {
91
91
  return toQueryResult(res)
92
92
  }
93
93
 
94
- async query<T = any>(sql: string, params: any[] = []): Promise<QueryResult<T>> {
95
- if (this.streamId != null) return (await this.streamExec(sql, params)) as QueryResult<T>
96
- return toQueryResult(await this.one([{ type: 'execute', stmt: { sql, args: params.map(toHrana) } }])) as QueryResult<T>
94
+ async query<T = any>(sql: string | { sql: string; args?: any[] }, params: any[] = []): Promise<QueryResult<T>> {
95
+ // Accept both query(sql, params) and query({ sql, args }) symmetric with
96
+ // execute() and the other adapters. Without this, object-form calls sent the
97
+ // object itself as the SQL string ("Expected 'query' to be a string, got 'object'").
98
+ const s = typeof sql === 'string' ? sql : sql.sql
99
+ const p = typeof sql === 'string' ? params : (sql.args ?? [])
100
+ if (this.streamId != null) return (await this.streamExec(s, p)) as QueryResult<T>
101
+ return toQueryResult(await this.one([{ type: 'execute', stmt: { sql: s, args: p.map(toHrana) } }])) as QueryResult<T>
97
102
  }
98
103
 
99
104
  async execute(sql: string | { sql: string; args?: any[] }, params: any[] = []): Promise<QueryResult> {
@@ -24,6 +24,14 @@ export interface SocketTarget {
24
24
  // hang forever. Generous by default; tune via ODB_SOCKET_REQUEST_TIMEOUT_MS.
25
25
  const REQUEST_TIMEOUT_MS = Number((globalThis as any).process?.env?.ODB_SOCKET_REQUEST_TIMEOUT_MS) || 30000
26
26
 
27
+ // Auto-reconnect: when the socket is down (router restarting), retry the CONNECT with
28
+ // backoff so a request that lands during the restart window waits it out instead of
29
+ // failing — a redeploy/restart becomes transparent. Bounded so a truly-dead server
30
+ // still fails the request (caller retries / falls back to HTTP) rather than hanging.
31
+ const RECONNECT_ATTEMPTS = Number((globalThis as any).process?.env?.ODB_SOCKET_RECONNECT_ATTEMPTS) || 6
32
+ const RECONNECT_BASE_MS = Number((globalThis as any).process?.env?.ODB_SOCKET_RECONNECT_BASE_MS) || 50
33
+ const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms))
34
+
27
35
  interface Pending {
28
36
  resolve: (r: StreamResult[]) => void
29
37
  reject: (e: any) => void
@@ -65,8 +73,23 @@ export class SocketClient {
65
73
  this.socket = await Bun.connect({
66
74
  ...opts,
67
75
  socket: {
68
- data: (_s: any, chunk: Buffer) => {
69
- for (const frame of this.reader.push(chunk)) {
76
+ data: (s: any, chunk: Buffer) => {
77
+ // A framing error (oversized/corrupt length prefix from a buggy/hostile server) makes the byte
78
+ // stream unrecoverable — drop the connection and fail in-flight requests, exactly like a close.
79
+ // Without this the throw escapes the Bun `data` callback uncaught.
80
+ let frames: Buffer[]
81
+ try {
82
+ frames = this.reader.push(chunk)
83
+ } catch (err: any) {
84
+ this.socket = undefined
85
+ this.reader = new FrameReader()
86
+ try {
87
+ s.end?.()
88
+ } catch {}
89
+ this.failPending(err instanceof Error ? err : new Error(`odblite framing error: ${err}`))
90
+ return
91
+ }
92
+ for (const frame of frames) {
70
93
  const msg = JSON.parse(frame.toString('utf8')) as { id: number; results: StreamResult[] }
71
94
  const p = this.pending.get(msg.id)
72
95
  if (p) {
@@ -95,6 +118,23 @@ export class SocketClient {
95
118
  this.writer = new FrameWriter(this.socket)
96
119
  }
97
120
 
121
+ /** Reconnect with bounded backoff — bridges a router restart so a request landing in the down-window
122
+ * waits it out instead of failing. After the last attempt the error propagates (caller retries / the
123
+ * auto-adapter can fall back to HTTP). Backoff: 50·100·200·400·800·1600ms ≈ 3s total by default. */
124
+ private async connectWithRetry(): Promise<void> {
125
+ let lastErr: any
126
+ for (let attempt = 0; attempt < RECONNECT_ATTEMPTS; attempt++) {
127
+ try {
128
+ await this.connect()
129
+ return
130
+ } catch (err) {
131
+ lastErr = err
132
+ if (attempt < RECONNECT_ATTEMPTS - 1) await sleep(RECONNECT_BASE_MS * 2 ** attempt)
133
+ }
134
+ }
135
+ throw lastErr instanceof Error ? lastErr : new Error(`odblite socket reconnect failed: ${lastErr}`)
136
+ }
137
+
98
138
  /** Allocate a fresh stream id for an interactive transaction (unique per connection). The server
99
139
  * pins one SQLite connection per stream id, so all of a tx's statements run on it (read-your-own-
100
140
  * writes). The id is carried in every frame of that tx via pipeline()'s `stream` arg. */
@@ -106,9 +146,11 @@ export class SocketClient {
106
146
  // Await the in-flight connect FIRST — only reconnect if it's actually gone
107
147
  // (closed/failed). Checking `!this.socket` before awaiting would start a
108
148
  // second connect while the first is still resolving (a leaked connection).
109
- await this.ready
149
+ await this.ready.catch(() => {}) // a prior failed connect must not poison this await; we re-check below
110
150
  if (!this.socket) {
111
- this.ready = this.connect()
151
+ // Reconnect with backoff (bridges a restart). Shared promise so concurrent pipeline() callers
152
+ // ride ONE reconnect, not N. If it ultimately fails, every caller rejects (then retries / HTTP).
153
+ this.ready = this.connectWithRetry()
112
154
  void this.ready.catch(() => {})
113
155
  await this.ready
114
156
  }
@@ -141,6 +141,10 @@ export class FrameWriter {
141
141
  }
142
142
  }
143
143
  }
144
+ // Reject an insane length prefix instead of buffering gigabytes for bytes that never arrive — a buggy or
145
+ // hostile server shouldn't be able to OOM the client inside the Bun `data` callback. Matches the router's
146
+ // frame cap; tune via ODB_MAX_FRAME_MB.
147
+ const MAX_FRAME = (Number((globalThis as any).process?.env?.ODB_MAX_FRAME_MB) || 64) * 1024 * 1024
144
148
  export class FrameReader {
145
149
  private buf: Buffer = Buffer.alloc(0)
146
150
  push(chunk: Buffer): Buffer[] {
@@ -148,6 +152,7 @@ export class FrameReader {
148
152
  const out: Buffer[] = []
149
153
  while (this.buf.length >= 4) {
150
154
  const len = this.buf.readUInt32LE(0)
155
+ if (len > MAX_FRAME) throw new Error(`odblite reply frame too large: ${len} bytes (max ${MAX_FRAME})`)
151
156
  if (this.buf.length < 4 + len) break
152
157
  out.push(this.buf.subarray(4, 4 + len))
153
158
  this.buf = this.buf.subarray(4 + len)