@onyx.dev/onyx-database 1.0.2 → 1.0.3

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.
@@ -220,6 +220,10 @@ async function resolveConfig(input) {
220
220
  const fetchImpl = merged.fetch ?? (typeof gfetch === "function" ? (u, i) => gfetch(u, i) : async () => {
221
221
  throw new OnyxConfigError("No fetch available; provide OnyxConfig.fetch");
222
222
  });
223
+ const retryConfig = input?.retry ?? env.retry ?? cfgPath.retry ?? project.retry ?? home.retry ?? {};
224
+ const retryEnabled = retryConfig.enabled ?? true;
225
+ const maxRetries = retryConfig.maxRetries ?? 3;
226
+ const retryInitialDelayMs = retryConfig.initialDelayMs ?? 300;
223
227
  const missing = [];
224
228
  if (!databaseId) missing.push("databaseId");
225
229
  if (!apiKey) missing.push("apiKey");
@@ -242,7 +246,16 @@ async function resolveConfig(input) {
242
246
  `Missing required config: ${missing.join(", ")}. Sources: ${sources.join(", ")}`
243
247
  );
244
248
  }
245
- const resolved = { baseUrl, databaseId, apiKey, apiSecret, fetch: fetchImpl };
249
+ const resolved = {
250
+ baseUrl,
251
+ databaseId,
252
+ apiKey,
253
+ apiSecret,
254
+ fetch: fetchImpl,
255
+ retryEnabled,
256
+ maxRetries,
257
+ retryInitialDelayMs
258
+ };
246
259
  const source = {
247
260
  databaseId: input?.databaseId ? "explicit config" : env.databaseId ? "env" : cfgPath.databaseId ? "env ONYX_CONFIG_PATH" : project.databaseId ? "project file" : home.databaseId ? "home profile" : "unknown",
248
261
  apiKey: input?.apiKey ? "explicit config" : env.apiKey ? "env" : cfgPath.apiKey ? "env ONYX_CONFIG_PATH" : project.apiKey ? "project file" : home.apiKey ? "home profile" : "unknown",
@@ -320,7 +333,7 @@ function parseJsonAllowNaN(txt) {
320
333
  return JSON.parse(fixed);
321
334
  }
322
335
  }
323
- var HttpClient = class {
336
+ var HttpClient = class _HttpClient {
324
337
  baseUrl;
325
338
  apiKey;
326
339
  apiSecret;
@@ -328,6 +341,25 @@ var HttpClient = class {
328
341
  defaults;
329
342
  requestLoggingEnabled;
330
343
  responseLoggingEnabled;
344
+ retryEnabled;
345
+ maxRetries;
346
+ retryInitialDelayMs;
347
+ shouldRetry;
348
+ static parseRetryAfter(header) {
349
+ if (!header) return null;
350
+ const trimmed = header.trim();
351
+ if (trimmed === "") return null;
352
+ const seconds = Number(trimmed);
353
+ if (Number.isFinite(seconds)) {
354
+ return Math.max(0, seconds * 1e3);
355
+ }
356
+ const dateMs = Date.parse(trimmed);
357
+ if (!Number.isNaN(dateMs)) {
358
+ const now = Date.now();
359
+ return Math.max(0, dateMs - now);
360
+ }
361
+ return null;
362
+ }
331
363
  constructor(opts) {
332
364
  if (!opts.baseUrl || opts.baseUrl.trim() === "") {
333
365
  throw new OnyxConfigError("baseUrl is required");
@@ -352,6 +384,10 @@ var HttpClient = class {
352
384
  const envDebug = globalThis.process?.env?.ONYX_DEBUG === "true";
353
385
  this.requestLoggingEnabled = !!opts.requestLoggingEnabled || envDebug;
354
386
  this.responseLoggingEnabled = !!opts.responseLoggingEnabled || envDebug;
387
+ this.retryEnabled = opts.retryEnabled ?? true;
388
+ this.maxRetries = Math.max(0, opts.maxRetries ?? 2);
389
+ this.retryInitialDelayMs = Math.max(0, opts.retryInitialDelayMs ?? 100);
390
+ this.shouldRetry = (method, path2) => method === "GET" || path2.startsWith("/query/");
355
391
  }
356
392
  headers(extra) {
357
393
  const extras = { ...extra ?? {} };
@@ -392,9 +428,8 @@ var HttpClient = class {
392
428
  headers,
393
429
  body: payload
394
430
  };
395
- const isQuery = path2.includes("/query/") && !/\/query\/(?:update|delete)\//.test(path2);
396
- const canRetry = method === "GET" || isQuery;
397
- const maxAttempts = canRetry ? 3 : 1;
431
+ const canRetry = this.retryEnabled && this.shouldRetry(method, path2);
432
+ const maxAttempts = canRetry ? this.maxRetries + 1 : 1;
398
433
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
399
434
  try {
400
435
  const res = await this.fetchImpl(url, init);
@@ -412,7 +447,10 @@ var HttpClient = class {
412
447
  if (!res.ok) {
413
448
  const msg = typeof data === "object" && data !== null && "error" in data && typeof data.error?.message === "string" ? String(data.error.message) : `${res.status} ${res.statusText}`;
414
449
  if (canRetry && res.status >= 500 && attempt + 1 < maxAttempts) {
415
- await new Promise((r) => setTimeout(r, 100 * 2 ** attempt));
450
+ const serverRetry = _HttpClient.parseRetryAfter(res.headers.get("retry-after"));
451
+ const backoff = this.retryInitialDelayMs * 2 ** attempt;
452
+ const delay = serverRetry ?? backoff;
453
+ await new Promise((r) => setTimeout(r, delay));
416
454
  continue;
417
455
  }
418
456
  throw new OnyxHttpError(msg, res.status, res.statusText, data, raw);
@@ -421,7 +459,8 @@ var HttpClient = class {
421
459
  } catch (err) {
422
460
  const retryable = canRetry && (!(err instanceof OnyxHttpError) || err.status >= 500);
423
461
  if (attempt + 1 < maxAttempts && retryable) {
424
- await new Promise((r) => setTimeout(r, 100 * 2 ** attempt));
462
+ const delay = this.retryInitialDelayMs * 2 ** attempt;
463
+ await new Promise((r) => setTimeout(r, delay));
425
464
  continue;
426
465
  }
427
466
  throw err;
@@ -982,14 +1021,6 @@ var CascadeRelationshipBuilder = class {
982
1021
  }
983
1022
  };
984
1023
 
985
- // src/errors/onyx-error.ts
986
- var OnyxError = class extends Error {
987
- name = "OnyxError";
988
- constructor(message) {
989
- super(message);
990
- }
991
- };
992
-
993
1024
  // src/helpers/schema-diff.ts
994
1025
  function mapByName(items) {
995
1026
  const map = /* @__PURE__ */ new Map();
@@ -1281,23 +1312,8 @@ ${bodyLines.join("\n")}
1281
1312
  `;
1282
1313
  }
1283
1314
 
1284
- // src/impl/onyx.ts
1315
+ // src/impl/onyx-core.ts
1285
1316
  var DEFAULT_CACHE_TTL = 5 * 60 * 1e3;
1286
- var cachedCfg = null;
1287
- function resolveConfigWithCache(config) {
1288
- const ttl = config?.ttl ?? DEFAULT_CACHE_TTL;
1289
- const now = Date.now();
1290
- if (cachedCfg && cachedCfg.expires > now) {
1291
- return cachedCfg.promise;
1292
- }
1293
- const { ttl: _ttl, requestLoggingEnabled: _reqLog, responseLoggingEnabled: _resLog, ...rest } = config ?? {};
1294
- const promise = resolveConfig(rest);
1295
- cachedCfg = { promise, expires: now + ttl };
1296
- return promise;
1297
- }
1298
- function clearCacheConfig() {
1299
- cachedCfg = null;
1300
- }
1301
1317
  function toSingleCondition(criteria) {
1302
1318
  return { conditionType: "SingleCondition", criteria };
1303
1319
  }
@@ -1387,7 +1403,7 @@ var OnyxDatabaseImpl = class {
1387
1403
  requestLoggingEnabled;
1388
1404
  responseLoggingEnabled;
1389
1405
  defaultPartition;
1390
- constructor(config) {
1406
+ constructor(config, resolveConfigWithCache) {
1391
1407
  this.requestLoggingEnabled = !!config?.requestLoggingEnabled;
1392
1408
  this.responseLoggingEnabled = !!config?.responseLoggingEnabled;
1393
1409
  this.defaultPartition = config?.partition;
@@ -1404,7 +1420,10 @@ var OnyxDatabaseImpl = class {
1404
1420
  apiSecret: this.resolved.apiSecret,
1405
1421
  fetchImpl: this.resolved.fetch,
1406
1422
  requestLoggingEnabled: this.requestLoggingEnabled,
1407
- responseLoggingEnabled: this.responseLoggingEnabled
1423
+ responseLoggingEnabled: this.responseLoggingEnabled,
1424
+ retryEnabled: this.resolved.retryEnabled,
1425
+ maxRetries: this.resolved.maxRetries,
1426
+ retryInitialDelayMs: this.resolved.retryInitialDelayMs
1408
1427
  });
1409
1428
  }
1410
1429
  return {
@@ -1869,7 +1888,6 @@ var QueryBuilderImpl = class {
1869
1888
  }
1870
1889
  async firstOrNull() {
1871
1890
  if (this.mode !== "select") throw new Error("Cannot call firstOrNull() in update mode.");
1872
- if (!this.conditions) throw new OnyxError("firstOrNull() requires a where() clause.");
1873
1891
  this.limitValue = 1;
1874
1892
  const pg = await this.page();
1875
1893
  return Array.isArray(pg.records) && pg.records.length > 0 ? pg.records[0] : null;
@@ -1961,12 +1979,32 @@ var CascadeBuilderImpl = class {
1961
1979
  return this.db.delete(table, primaryKey, opts);
1962
1980
  }
1963
1981
  };
1964
- var onyx = {
1965
- init(config) {
1966
- return new OnyxDatabaseImpl(config);
1967
- },
1968
- clearCacheConfig
1969
- };
1982
+ function createOnyxFacade(resolveConfig2) {
1983
+ let cachedCfg = null;
1984
+ function resolveConfigWithCache(config) {
1985
+ const ttl = config?.ttl ?? DEFAULT_CACHE_TTL;
1986
+ const now = Date.now();
1987
+ if (cachedCfg && cachedCfg.expires > now) {
1988
+ return cachedCfg.promise;
1989
+ }
1990
+ const { ttl: _ttl, requestLoggingEnabled: _reqLog, responseLoggingEnabled: _resLog, ...rest } = config ?? {};
1991
+ const promise = resolveConfig2(rest);
1992
+ cachedCfg = { promise, expires: now + ttl };
1993
+ return promise;
1994
+ }
1995
+ function clearCacheConfig() {
1996
+ cachedCfg = null;
1997
+ }
1998
+ return {
1999
+ init(config) {
2000
+ return new OnyxDatabaseImpl(config, resolveConfigWithCache);
2001
+ },
2002
+ clearCacheConfig
2003
+ };
2004
+ }
2005
+
2006
+ // src/impl/onyx.ts
2007
+ var onyx = createOnyxFacade((config) => resolveConfig(config));
1970
2008
 
1971
2009
  // schema/cli/schema.ts
1972
2010
  var DEFAULT_SCHEMA_PATH = "./onyx.schema.json";