@onyx.dev/onyx-database 1.0.2 → 1.1.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/index.js CHANGED
@@ -209,6 +209,10 @@ async function resolveConfig(input) {
209
209
  const fetchImpl = merged.fetch ?? (typeof gfetch === "function" ? (u, i) => gfetch(u, i) : async () => {
210
210
  throw new OnyxConfigError("No fetch available; provide OnyxConfig.fetch");
211
211
  });
212
+ const retryConfig = input?.retry ?? env.retry ?? cfgPath.retry ?? project.retry ?? home.retry ?? {};
213
+ const retryEnabled = retryConfig.enabled ?? true;
214
+ const maxRetries = retryConfig.maxRetries ?? 3;
215
+ const retryInitialDelayMs = retryConfig.initialDelayMs ?? 300;
212
216
  const missing = [];
213
217
  if (!databaseId) missing.push("databaseId");
214
218
  if (!apiKey) missing.push("apiKey");
@@ -231,7 +235,16 @@ async function resolveConfig(input) {
231
235
  `Missing required config: ${missing.join(", ")}. Sources: ${sources.join(", ")}`
232
236
  );
233
237
  }
234
- const resolved = { baseUrl, databaseId, apiKey, apiSecret, fetch: fetchImpl };
238
+ const resolved = {
239
+ baseUrl,
240
+ databaseId,
241
+ apiKey,
242
+ apiSecret,
243
+ fetch: fetchImpl,
244
+ retryEnabled,
245
+ maxRetries,
246
+ retryInitialDelayMs
247
+ };
235
248
  const source = {
236
249
  databaseId: input?.databaseId ? "explicit config" : env.databaseId ? "env" : cfgPath.databaseId ? "env ONYX_CONFIG_PATH" : project.databaseId ? "project file" : home.databaseId ? "home profile" : "unknown",
237
250
  apiKey: input?.apiKey ? "explicit config" : env.apiKey ? "env" : cfgPath.apiKey ? "env ONYX_CONFIG_PATH" : project.apiKey ? "project file" : home.apiKey ? "home profile" : "unknown",
@@ -288,7 +301,7 @@ function parseJsonAllowNaN(txt) {
288
301
  return JSON.parse(fixed);
289
302
  }
290
303
  }
291
- var HttpClient = class {
304
+ var HttpClient = class _HttpClient {
292
305
  baseUrl;
293
306
  apiKey;
294
307
  apiSecret;
@@ -296,6 +309,25 @@ var HttpClient = class {
296
309
  defaults;
297
310
  requestLoggingEnabled;
298
311
  responseLoggingEnabled;
312
+ retryEnabled;
313
+ maxRetries;
314
+ retryInitialDelayMs;
315
+ shouldRetry;
316
+ static parseRetryAfter(header) {
317
+ if (!header) return null;
318
+ const trimmed = header.trim();
319
+ if (trimmed === "") return null;
320
+ const seconds = Number(trimmed);
321
+ if (Number.isFinite(seconds)) {
322
+ return Math.max(0, seconds * 1e3);
323
+ }
324
+ const dateMs = Date.parse(trimmed);
325
+ if (!Number.isNaN(dateMs)) {
326
+ const now = Date.now();
327
+ return Math.max(0, dateMs - now);
328
+ }
329
+ return null;
330
+ }
299
331
  constructor(opts) {
300
332
  if (!opts.baseUrl || opts.baseUrl.trim() === "") {
301
333
  throw new OnyxConfigError("baseUrl is required");
@@ -320,6 +352,10 @@ var HttpClient = class {
320
352
  const envDebug = globalThis.process?.env?.ONYX_DEBUG === "true";
321
353
  this.requestLoggingEnabled = !!opts.requestLoggingEnabled || envDebug;
322
354
  this.responseLoggingEnabled = !!opts.responseLoggingEnabled || envDebug;
355
+ this.retryEnabled = opts.retryEnabled ?? true;
356
+ this.maxRetries = Math.max(0, opts.maxRetries ?? 2);
357
+ this.retryInitialDelayMs = Math.max(0, opts.retryInitialDelayMs ?? 100);
358
+ this.shouldRetry = (method, path) => method === "GET" || path.startsWith("/query/");
323
359
  }
324
360
  headers(extra) {
325
361
  const extras = { ...extra ?? {} };
@@ -360,9 +396,8 @@ var HttpClient = class {
360
396
  headers,
361
397
  body: payload
362
398
  };
363
- const isQuery = path.includes("/query/") && !/\/query\/(?:update|delete)\//.test(path);
364
- const canRetry = method === "GET" || isQuery;
365
- const maxAttempts = canRetry ? 3 : 1;
399
+ const canRetry = this.retryEnabled && this.shouldRetry(method, path);
400
+ const maxAttempts = canRetry ? this.maxRetries + 1 : 1;
366
401
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
367
402
  try {
368
403
  const res = await this.fetchImpl(url, init);
@@ -380,7 +415,10 @@ var HttpClient = class {
380
415
  if (!res.ok) {
381
416
  const msg = typeof data === "object" && data !== null && "error" in data && typeof data.error?.message === "string" ? String(data.error.message) : `${res.status} ${res.statusText}`;
382
417
  if (canRetry && res.status >= 500 && attempt + 1 < maxAttempts) {
383
- await new Promise((r) => setTimeout(r, 100 * 2 ** attempt));
418
+ const serverRetry = _HttpClient.parseRetryAfter(res.headers.get("retry-after"));
419
+ const backoff = this.retryInitialDelayMs * 2 ** attempt;
420
+ const delay = serverRetry ?? backoff;
421
+ await new Promise((r) => setTimeout(r, delay));
384
422
  continue;
385
423
  }
386
424
  throw new OnyxHttpError(msg, res.status, res.statusText, data, raw);
@@ -389,7 +427,8 @@ var HttpClient = class {
389
427
  } catch (err) {
390
428
  const retryable = canRetry && (!(err instanceof OnyxHttpError) || err.status >= 500);
391
429
  if (attempt + 1 < maxAttempts && retryable) {
392
- await new Promise((r) => setTimeout(r, 100 * 2 ** attempt));
430
+ const delay = this.retryInitialDelayMs * 2 ** attempt;
431
+ await new Promise((r) => setTimeout(r, delay));
393
432
  continue;
394
433
  }
395
434
  throw err;
@@ -950,14 +989,6 @@ var CascadeRelationshipBuilder = class {
950
989
  }
951
990
  };
952
991
 
953
- // src/errors/onyx-error.ts
954
- var OnyxError = class extends Error {
955
- name = "OnyxError";
956
- constructor(message) {
957
- super(message);
958
- }
959
- };
960
-
961
992
  // src/helpers/schema-diff.ts
962
993
  function mapByName(items) {
963
994
  const map = /* @__PURE__ */ new Map();
@@ -1146,23 +1177,8 @@ function computeSchemaDiff(apiSchema, localSchema) {
1146
1177
  return { newTables, removedTables, changedTables };
1147
1178
  }
1148
1179
 
1149
- // src/impl/onyx.ts
1180
+ // src/impl/onyx-core.ts
1150
1181
  var DEFAULT_CACHE_TTL = 5 * 60 * 1e3;
1151
- var cachedCfg = null;
1152
- function resolveConfigWithCache(config) {
1153
- const ttl = config?.ttl ?? DEFAULT_CACHE_TTL;
1154
- const now = Date.now();
1155
- if (cachedCfg && cachedCfg.expires > now) {
1156
- return cachedCfg.promise;
1157
- }
1158
- const { ttl: _ttl, requestLoggingEnabled: _reqLog, responseLoggingEnabled: _resLog, ...rest } = config ?? {};
1159
- const promise = resolveConfig(rest);
1160
- cachedCfg = { promise, expires: now + ttl };
1161
- return promise;
1162
- }
1163
- function clearCacheConfig() {
1164
- cachedCfg = null;
1165
- }
1166
1182
  function toSingleCondition(criteria) {
1167
1183
  return { conditionType: "SingleCondition", criteria };
1168
1184
  }
@@ -1252,7 +1268,7 @@ var OnyxDatabaseImpl = class {
1252
1268
  requestLoggingEnabled;
1253
1269
  responseLoggingEnabled;
1254
1270
  defaultPartition;
1255
- constructor(config) {
1271
+ constructor(config, resolveConfigWithCache) {
1256
1272
  this.requestLoggingEnabled = !!config?.requestLoggingEnabled;
1257
1273
  this.responseLoggingEnabled = !!config?.responseLoggingEnabled;
1258
1274
  this.defaultPartition = config?.partition;
@@ -1269,7 +1285,10 @@ var OnyxDatabaseImpl = class {
1269
1285
  apiSecret: this.resolved.apiSecret,
1270
1286
  fetchImpl: this.resolved.fetch,
1271
1287
  requestLoggingEnabled: this.requestLoggingEnabled,
1272
- responseLoggingEnabled: this.responseLoggingEnabled
1288
+ responseLoggingEnabled: this.responseLoggingEnabled,
1289
+ retryEnabled: this.resolved.retryEnabled,
1290
+ maxRetries: this.resolved.maxRetries,
1291
+ retryInitialDelayMs: this.resolved.retryInitialDelayMs
1273
1292
  });
1274
1293
  }
1275
1294
  return {
@@ -1304,6 +1323,14 @@ var OnyxDatabaseImpl = class {
1304
1323
  qb.select(...fields);
1305
1324
  return qb;
1306
1325
  }
1326
+ search(queryText, minScore) {
1327
+ const qb = new QueryBuilderImpl(
1328
+ this,
1329
+ "ALL",
1330
+ this.defaultPartition
1331
+ );
1332
+ return qb.search(queryText, minScore);
1333
+ }
1307
1334
  cascade(...relationships) {
1308
1335
  const cb = new CascadeBuilderImpl(this);
1309
1336
  return cb.cascade(...relationships);
@@ -1590,6 +1617,7 @@ var QueryBuilderImpl = class {
1590
1617
  toSelectQuery() {
1591
1618
  return {
1592
1619
  type: "SelectQuery",
1620
+ table: this.table,
1593
1621
  fields: this.fields,
1594
1622
  conditions: this.serializableConditions(),
1595
1623
  sort: this.sort,
@@ -1629,6 +1657,13 @@ var QueryBuilderImpl = class {
1629
1657
  this.resolvers = flat.length > 0 ? flat : null;
1630
1658
  return this;
1631
1659
  }
1660
+ search(queryText, minScore) {
1661
+ return this.and({
1662
+ field: "__full_text__",
1663
+ operator: "MATCHES",
1664
+ value: { queryText, minScore: minScore ?? null }
1665
+ });
1666
+ }
1632
1667
  where(condition) {
1633
1668
  const c2 = toCondition(condition);
1634
1669
  if (!this.conditions) {
@@ -1734,7 +1769,6 @@ var QueryBuilderImpl = class {
1734
1769
  }
1735
1770
  async firstOrNull() {
1736
1771
  if (this.mode !== "select") throw new Error("Cannot call firstOrNull() in update mode.");
1737
- if (!this.conditions) throw new OnyxError("firstOrNull() requires a where() clause.");
1738
1772
  this.limitValue = 1;
1739
1773
  const pg = await this.page();
1740
1774
  return Array.isArray(pg.records) && pg.records.length > 0 ? pg.records[0] : null;
@@ -1826,12 +1860,32 @@ var CascadeBuilderImpl = class {
1826
1860
  return this.db.delete(table, primaryKey, opts);
1827
1861
  }
1828
1862
  };
1829
- var onyx = {
1830
- init(config) {
1831
- return new OnyxDatabaseImpl(config);
1832
- },
1833
- clearCacheConfig
1834
- };
1863
+ function createOnyxFacade(resolveConfig2) {
1864
+ let cachedCfg = null;
1865
+ function resolveConfigWithCache(config) {
1866
+ const ttl = config?.ttl ?? DEFAULT_CACHE_TTL;
1867
+ const now = Date.now();
1868
+ if (cachedCfg && cachedCfg.expires > now) {
1869
+ return cachedCfg.promise;
1870
+ }
1871
+ const { ttl: _ttl, requestLoggingEnabled: _reqLog, responseLoggingEnabled: _resLog, ...rest } = config ?? {};
1872
+ const promise = resolveConfig2(rest);
1873
+ cachedCfg = { promise, expires: now + ttl };
1874
+ return promise;
1875
+ }
1876
+ function clearCacheConfig() {
1877
+ cachedCfg = null;
1878
+ }
1879
+ return {
1880
+ init(config) {
1881
+ return new OnyxDatabaseImpl(config, resolveConfigWithCache);
1882
+ },
1883
+ clearCacheConfig
1884
+ };
1885
+ }
1886
+
1887
+ // src/impl/onyx.ts
1888
+ var onyx = createOnyxFacade((config) => resolveConfig(config));
1835
1889
 
1836
1890
  // src/helpers/sort.ts
1837
1891
  var asc = (field) => ({ field, order: "ASC" });
@@ -1961,6 +2015,10 @@ var ConditionBuilderImpl = class {
1961
2015
 
1962
2016
  // src/helpers/conditions.ts
1963
2017
  var c = (field, operator, value) => new ConditionBuilderImpl({ field, operator, value });
2018
+ var fullText = (queryText, minScore) => ({
2019
+ queryText,
2020
+ minScore: minScore ?? null
2021
+ });
1964
2022
  var eq = (field, value) => c(field, "EQUAL", value);
1965
2023
  var neq = (field, value) => c(field, "NOT_EQUAL", value);
1966
2024
  function inOp(field, values) {
@@ -1983,6 +2041,7 @@ var gte = (field, value) => c(field, "GREATER_THAN_EQUAL", value);
1983
2041
  var lt = (field, value) => c(field, "LESS_THAN", value);
1984
2042
  var lte = (field, value) => c(field, "LESS_THAN_EQUAL", value);
1985
2043
  var matches = (field, regex) => c(field, "MATCHES", regex);
2044
+ var search = (queryText, minScore) => c("__full_text__", "MATCHES", fullText(queryText, minScore));
1986
2045
  var notMatches = (field, regex) => c(field, "NOT_MATCHES", regex);
1987
2046
  var like = (field, pattern) => c(field, "LIKE", pattern);
1988
2047
  var notLike = (field, pattern) => c(field, "NOT_LIKE", pattern);
@@ -2014,6 +2073,6 @@ var percentile = (attribute, p) => `percentile(${attribute}, ${p})`;
2014
2073
  var sdkName = "@onyx.dev/onyx-database";
2015
2074
  var sdkVersion = "0.1.0";
2016
2075
 
2017
- export { QueryResults, asc, avg, between, contains, containsIgnoreCase, count, desc, eq, gt, gte, inOp, isNull, like, lower, lt, lte, matches, max, median, min, neq, notContains, notContainsIgnoreCase, notIn, notLike, notMatches, notNull, notStartsWith, notWithin, onyx, percentile, replace, sdkName, sdkVersion, startsWith, std, substring, sum, upper, variance, within };
2076
+ export { QueryResults, asc, avg, between, contains, containsIgnoreCase, count, desc, eq, gt, gte, inOp, isNull, like, lower, lt, lte, matches, max, median, min, neq, notContains, notContainsIgnoreCase, notIn, notLike, notMatches, notNull, notStartsWith, notWithin, onyx, percentile, replace, sdkName, sdkVersion, search, startsWith, std, substring, sum, upper, variance, within };
2018
2077
  //# sourceMappingURL=index.js.map
2019
2078
  //# sourceMappingURL=index.js.map