@onyx.dev/onyx-database 2.1.1 → 2.4.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.
@@ -1287,6 +1287,374 @@ function computeSchemaDiff(apiSchema, localSchema) {
1287
1287
  return { newTables, removedTables, changedTables };
1288
1288
  }
1289
1289
 
1290
+ // src/helpers/query-formatters.ts
1291
+ var DEFAULT_TABLE_OPTIONS = {
1292
+ headers: true,
1293
+ maxColumnWidth: 80,
1294
+ flattenNestedObjects: false,
1295
+ nestedSeparator: ".",
1296
+ nullValue: ""
1297
+ };
1298
+ var DEFAULT_TREE_OPTIONS = {
1299
+ rootLabel: "results",
1300
+ keyField: "",
1301
+ includeRoot: true,
1302
+ maxDepth: Number.POSITIVE_INFINITY,
1303
+ nullValue: ""
1304
+ };
1305
+ var DEFAULT_CSV_OPTIONS = {
1306
+ headers: true,
1307
+ delimiter: ",",
1308
+ quote: '"',
1309
+ escape: '"',
1310
+ newline: "\n",
1311
+ flattenNestedObjects: true,
1312
+ nestedSeparator: ".",
1313
+ nullValue: ""
1314
+ };
1315
+ var DEFAULT_JSON_OPTIONS = {
1316
+ pretty: true,
1317
+ indent: 2
1318
+ };
1319
+ function isDate(value) {
1320
+ return value instanceof Date;
1321
+ }
1322
+ function isRecord(value) {
1323
+ return value !== null && typeof value === "object" && !Array.isArray(value) && !isDate(value);
1324
+ }
1325
+ function normalizeRecord(value) {
1326
+ if (isRecord(value)) {
1327
+ return value;
1328
+ }
1329
+ return { value };
1330
+ }
1331
+ function normalizeScalar(value, nullValue) {
1332
+ if (value == null) return nullValue;
1333
+ if (isDate(value)) return value.toISOString();
1334
+ if (typeof value === "string") return value;
1335
+ if (typeof value === "bigint") return value.toString();
1336
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
1337
+ return JSON.stringify(value);
1338
+ }
1339
+ function inlineValue(value, nullValue) {
1340
+ if (value == null) return nullValue;
1341
+ if (isDate(value)) return value.toISOString();
1342
+ if (Array.isArray(value)) {
1343
+ return value.length === 0 ? "[]" : `[${value.map((entry) => inlineValue(entry, nullValue)).join(", ")}]`;
1344
+ }
1345
+ if (isRecord(value)) {
1346
+ const entries = Object.entries(value);
1347
+ if (entries.length === 0) return "{}";
1348
+ return entries.map(([key, entry]) => `${key}=${inlineValue(entry, nullValue)}`).join(", ");
1349
+ }
1350
+ if (typeof value === "bigint") return value.toString();
1351
+ return String(value);
1352
+ }
1353
+ function escapeTableCell(value) {
1354
+ return value.replace(/\r/g, "\\r").replace(/\n/g, "\\n");
1355
+ }
1356
+ function truncate(value, maxWidth) {
1357
+ if (maxWidth < 1) return "";
1358
+ if (value.length <= maxWidth) return value;
1359
+ if (maxWidth <= 3) return ".".repeat(maxWidth);
1360
+ return `${value.slice(0, Math.max(0, maxWidth - 3))}...`;
1361
+ }
1362
+ function padRight(value, width) {
1363
+ if (value.length >= width) return value;
1364
+ return value + " ".repeat(width - value.length);
1365
+ }
1366
+ function flattenValue(value, separator, prefix, out) {
1367
+ if (isRecord(value)) {
1368
+ const entries = Object.entries(value);
1369
+ if (entries.length === 0) {
1370
+ out[prefix] = {};
1371
+ return;
1372
+ }
1373
+ for (const [key, entry] of entries) {
1374
+ const path = prefix ? `${prefix}${separator}${key}` : key;
1375
+ flattenValue(entry, separator, path, out);
1376
+ }
1377
+ return;
1378
+ }
1379
+ if (Array.isArray(value)) {
1380
+ if (value.length === 0) {
1381
+ out[prefix] = [];
1382
+ return;
1383
+ }
1384
+ value.forEach((entry, index) => {
1385
+ const path = prefix ? `${prefix}${separator}${index}` : String(index);
1386
+ flattenValue(entry, separator, path, out);
1387
+ });
1388
+ return;
1389
+ }
1390
+ out[prefix] = value;
1391
+ }
1392
+ function toFlatRow(record, separator) {
1393
+ const out = {};
1394
+ for (const [key, value] of Object.entries(record)) {
1395
+ flattenValue(value, separator, key, out);
1396
+ }
1397
+ return out;
1398
+ }
1399
+ function discoveredColumns(rows) {
1400
+ const columns = [];
1401
+ const seen = /* @__PURE__ */ new Set();
1402
+ for (const row of rows) {
1403
+ for (const key of Object.keys(row)) {
1404
+ if (!seen.has(key)) {
1405
+ seen.add(key);
1406
+ columns.push(key);
1407
+ }
1408
+ }
1409
+ }
1410
+ return columns;
1411
+ }
1412
+ function orderColumns(discovered, preferred, flattenNestedObjects, separator) {
1413
+ if (!preferred || preferred.length === 0) return discovered;
1414
+ const ordered = [];
1415
+ const seen = /* @__PURE__ */ new Set();
1416
+ const addColumn = (column) => {
1417
+ if (!seen.has(column)) {
1418
+ seen.add(column);
1419
+ ordered.push(column);
1420
+ }
1421
+ };
1422
+ for (const field of preferred) {
1423
+ if (flattenNestedObjects) {
1424
+ let matched = false;
1425
+ for (const column of discovered) {
1426
+ if (column === field || column.startsWith(`${field}${separator}`)) {
1427
+ addColumn(column);
1428
+ matched = true;
1429
+ }
1430
+ }
1431
+ if (!matched) {
1432
+ addColumn(field);
1433
+ }
1434
+ continue;
1435
+ }
1436
+ addColumn(field);
1437
+ }
1438
+ for (const column of discovered) {
1439
+ addColumn(column);
1440
+ }
1441
+ return ordered;
1442
+ }
1443
+ function normalizeTableRows(records, preferredColumns, options) {
1444
+ const normalized = records.map(normalizeRecord);
1445
+ if (options.flattenNestedObjects) {
1446
+ const flatRows = normalized.map((record) => toFlatRow(record, options.nestedSeparator));
1447
+ const columns2 = orderColumns(
1448
+ discoveredColumns(flatRows),
1449
+ preferredColumns,
1450
+ true,
1451
+ options.nestedSeparator
1452
+ );
1453
+ const rows2 = flatRows.map((row) => columns2.map((column) => escapeTableCell(truncate(
1454
+ inlineValue(row[column], options.nullValue),
1455
+ options.maxColumnWidth
1456
+ ))));
1457
+ return { columns: columns2, rows: rows2 };
1458
+ }
1459
+ const topLevelRows = normalized.map((record) => ({ ...record }));
1460
+ const columns = orderColumns(
1461
+ discoveredColumns(topLevelRows),
1462
+ preferredColumns,
1463
+ false,
1464
+ options.nestedSeparator
1465
+ );
1466
+ const rows = topLevelRows.map((row) => columns.map((column) => escapeTableCell(truncate(
1467
+ inlineValue(row[column], options.nullValue),
1468
+ options.maxColumnWidth
1469
+ ))));
1470
+ return { columns, rows };
1471
+ }
1472
+ function formatQueryResultsAsTable(records, options, preferredColumns) {
1473
+ const final = { ...DEFAULT_TABLE_OPTIONS, ...options };
1474
+ const { columns, rows } = normalizeTableRows(records, preferredColumns, final);
1475
+ if (columns.length === 0) return "";
1476
+ const headerCells = columns.map((column) => truncate(column, final.maxColumnWidth));
1477
+ const widths = columns.map((_, index) => {
1478
+ const headerWidth = final.headers ? headerCells[index].length : 0;
1479
+ const rowWidth = rows.reduce((max, row) => Math.max(max, row[index].length), 0);
1480
+ return Math.max(headerWidth, rowWidth);
1481
+ });
1482
+ const border = (left, join, right) => `${left}${widths.map((width) => "\u2500".repeat(width + 2)).join(join)}${right}`;
1483
+ const renderRow = (cells) => `\u2502 ${cells.map((cell, index) => padRight(cell, widths[index])).join(" \u2502 ")} \u2502`;
1484
+ const lines = [border("\u250C", "\u252C", "\u2510")];
1485
+ if (final.headers) {
1486
+ lines.push(renderRow(headerCells));
1487
+ lines.push(border("\u251C", "\u253C", "\u2524"));
1488
+ }
1489
+ for (const row of rows) {
1490
+ lines.push(renderRow(row));
1491
+ }
1492
+ lines.push(border("\u2514", "\u2534", "\u2518"));
1493
+ return lines.join("\n");
1494
+ }
1495
+ function encodeCsvCell(value, options) {
1496
+ const escapedQuote = `${options.escape}${options.quote}`;
1497
+ const escaped = value.split(options.quote).join(escapedQuote);
1498
+ const needsQuoting = escaped.includes(options.delimiter) || escaped.includes(options.quote) || escaped.includes("\n") || escaped.includes("\r");
1499
+ if (!needsQuoting) return escaped;
1500
+ return `${options.quote}${escaped}${options.quote}`;
1501
+ }
1502
+ function normalizeCsvRows(records, preferredColumns, options) {
1503
+ const normalized = records.map(normalizeRecord);
1504
+ if (options.flattenNestedObjects) {
1505
+ const flatRows = normalized.map((record) => toFlatRow(record, options.nestedSeparator));
1506
+ const columns2 = orderColumns(
1507
+ discoveredColumns(flatRows),
1508
+ preferredColumns,
1509
+ true,
1510
+ options.nestedSeparator
1511
+ );
1512
+ const rows2 = flatRows.map((row) => columns2.map((column) => normalizeScalar(row[column], options.nullValue)));
1513
+ return { columns: columns2, rows: rows2 };
1514
+ }
1515
+ const topLevelRows = normalized.map((record) => ({ ...record }));
1516
+ const columns = orderColumns(
1517
+ discoveredColumns(topLevelRows),
1518
+ preferredColumns,
1519
+ false,
1520
+ options.nestedSeparator
1521
+ );
1522
+ const rows = topLevelRows.map((row) => columns.map((column) => inlineValue(row[column], options.nullValue)));
1523
+ return { columns, rows };
1524
+ }
1525
+ function formatQueryResultsAsCsv(records, options, preferredColumns) {
1526
+ const final = { ...DEFAULT_CSV_OPTIONS, ...options };
1527
+ const { columns, rows } = normalizeCsvRows(records, preferredColumns, final);
1528
+ const lines = [];
1529
+ if (final.headers && columns.length > 0) {
1530
+ lines.push(columns.map((column) => encodeCsvCell(column, final)).join(final.delimiter));
1531
+ }
1532
+ for (const row of rows) {
1533
+ lines.push(row.map((cell) => encodeCsvCell(cell, final)).join(final.delimiter));
1534
+ }
1535
+ return lines.join(final.newline);
1536
+ }
1537
+ function normalizeJsonValue(value) {
1538
+ if (value === void 0) return null;
1539
+ if (value === null) return null;
1540
+ if (isDate(value)) return value.toISOString();
1541
+ if (typeof value === "bigint") return value.toString();
1542
+ if (Array.isArray(value)) return value.map((entry) => normalizeJsonValue(entry));
1543
+ if (isRecord(value)) {
1544
+ const out = {};
1545
+ for (const [key, entry] of Object.entries(value)) {
1546
+ out[key] = normalizeJsonValue(entry);
1547
+ }
1548
+ return out;
1549
+ }
1550
+ return value;
1551
+ }
1552
+ function formatQueryResultsAsJson(records, options) {
1553
+ const final = { ...DEFAULT_JSON_OPTIONS, ...options };
1554
+ const normalized = records.map((record) => normalizeJsonValue(normalizeRecord(record)));
1555
+ return final.pretty ? JSON.stringify(normalized, null, final.indent) : JSON.stringify(normalized);
1556
+ }
1557
+ function findDefaultTreeKeyField(record) {
1558
+ for (const candidate of ["code", "id", "name"]) {
1559
+ if (candidate in record) return candidate;
1560
+ }
1561
+ const keys = Object.keys(record);
1562
+ return keys.length > 0 ? keys[0] : void 0;
1563
+ }
1564
+ function buildTreeNodesFromValue(value, options, depth) {
1565
+ if (depth >= options.maxDepth) {
1566
+ return [{ label: inlineValue(value, options.nullValue) }];
1567
+ }
1568
+ if (Array.isArray(value)) {
1569
+ return value.map((entry, index) => {
1570
+ if (!isRecord(entry) && !Array.isArray(entry)) {
1571
+ return { label: `[${index}]: ${inlineValue(entry, options.nullValue)}` };
1572
+ }
1573
+ return {
1574
+ label: `[${index}]`,
1575
+ children: buildTreeNodesFromValue(entry, options, depth + 1)
1576
+ };
1577
+ });
1578
+ }
1579
+ return Object.entries(value).map(([key, entry]) => {
1580
+ if (isRecord(entry) || Array.isArray(entry)) {
1581
+ if (depth + 1 >= options.maxDepth) {
1582
+ return { label: `${key}: ${inlineValue(entry, options.nullValue)}` };
1583
+ }
1584
+ return {
1585
+ label: key,
1586
+ children: buildTreeNodesFromValue(entry, options, depth + 1)
1587
+ };
1588
+ }
1589
+ return { label: `${key}: ${inlineValue(entry, options.nullValue)}` };
1590
+ });
1591
+ }
1592
+ function buildRecordTreeNode(record, index, options) {
1593
+ const keyField = options.keyField || findDefaultTreeKeyField(record);
1594
+ const entries = Object.entries(record);
1595
+ if (!keyField || !(keyField in record)) {
1596
+ return {
1597
+ label: `row ${index + 1}`,
1598
+ children: buildTreeNodesFromValue(record, options, 0)
1599
+ };
1600
+ }
1601
+ const labelValue = inlineValue(record[keyField], options.nullValue);
1602
+ const childEntries = entries.filter(([key]) => key !== keyField);
1603
+ return {
1604
+ label: labelValue,
1605
+ children: childEntries.length > 0 ? childEntries.map(([key, value]) => {
1606
+ if (isRecord(value) || Array.isArray(value)) {
1607
+ if (1 > options.maxDepth) {
1608
+ return { label: `${key}: ${inlineValue(value, options.nullValue)}` };
1609
+ }
1610
+ return {
1611
+ label: key,
1612
+ children: buildTreeNodesFromValue(value, options, 1)
1613
+ };
1614
+ }
1615
+ return { label: `${key}: ${inlineValue(value, options.nullValue)}` };
1616
+ }) : void 0
1617
+ };
1618
+ }
1619
+ function renderTreeNodes(nodes, prefix = "") {
1620
+ const lines = [];
1621
+ nodes.forEach((node, index) => {
1622
+ const isLast = index === nodes.length - 1;
1623
+ const branch = isLast ? "\u2514\u2500 " : "\u251C\u2500 ";
1624
+ lines.push(`${prefix}${branch}${node.label}`);
1625
+ if (node.children && node.children.length > 0) {
1626
+ const childPrefix = `${prefix}${isLast ? " " : "\u2502 "}`;
1627
+ lines.push(...renderTreeNodes(node.children, childPrefix));
1628
+ }
1629
+ });
1630
+ return lines;
1631
+ }
1632
+ function formatQueryResultsAsTree(records, options) {
1633
+ const final = { ...DEFAULT_TREE_OPTIONS, ...options };
1634
+ const nodes = records.map((record, index) => buildRecordTreeNode(normalizeRecord(record), index, final));
1635
+ if (!final.includeRoot) {
1636
+ return renderTreeNodes(nodes).join("\n");
1637
+ }
1638
+ const lines = [final.rootLabel];
1639
+ if (nodes.length > 0) {
1640
+ lines.push(...renderTreeNodes(nodes));
1641
+ }
1642
+ return lines.join("\n");
1643
+ }
1644
+ async function collectAllQueryRecords(getPage, initialNextPage) {
1645
+ const records = [];
1646
+ let nextPage = initialNextPage;
1647
+ while (true) {
1648
+ const page = await getPage(nextPage);
1649
+ if (Array.isArray(page.records)) {
1650
+ records.push(...page.records);
1651
+ }
1652
+ if (!page.nextPage) break;
1653
+ nextPage = page.nextPage;
1654
+ }
1655
+ return records;
1656
+ }
1657
+
1290
1658
  // src/impl/onyx-core.ts
1291
1659
  var DEFAULT_CACHE_TTL = 5 * 60 * 1e3;
1292
1660
  function toSingleCondition(criteria) {
@@ -1520,6 +1888,27 @@ var OnyxDatabaseImpl = class {
1520
1888
  const { http } = await this.ensureAiClient();
1521
1889
  return http.request("POST", "/api/script-approvals", input);
1522
1890
  }
1891
+ async predict(publishedModelId, inputs) {
1892
+ const { http, databaseId } = await this.ensureClient();
1893
+ const path = `/data/${encodeURIComponent(
1894
+ databaseId
1895
+ )}/model-builder/published-model/${encodeURIComponent(publishedModelId)}/predict`;
1896
+ return http.request(
1897
+ "POST",
1898
+ path,
1899
+ serializeDates({ inputs })
1900
+ );
1901
+ }
1902
+ async predictFromScript(publishedModelId, scriptId, scriptParameters = {}) {
1903
+ const { http, databaseId } = await this.ensureClient();
1904
+ const path = `/data/${encodeURIComponent(
1905
+ databaseId
1906
+ )}/model-builder/published-model/${encodeURIComponent(publishedModelId)}/predict/script`;
1907
+ return http.request("POST", path, {
1908
+ scriptId,
1909
+ scriptParameters
1910
+ });
1911
+ }
1523
1912
  from(table) {
1524
1913
  return new QueryBuilderImpl(this, String(table), this.defaultPartition);
1525
1914
  }
@@ -1794,7 +2183,7 @@ var OnyxDatabaseImpl = class {
1794
2183
  };
1795
2184
  var QueryBuilderImpl = class {
1796
2185
  db;
1797
- table;
2186
+ tableName;
1798
2187
  fields = null;
1799
2188
  resolvers = null;
1800
2189
  conditions = null;
@@ -1813,12 +2202,12 @@ var QueryBuilderImpl = class {
1813
2202
  onItemListener = null;
1814
2203
  constructor(db, table, partition) {
1815
2204
  this.db = db;
1816
- this.table = table;
2205
+ this.tableName = table;
1817
2206
  this.partitionValue = partition;
1818
2207
  }
1819
2208
  ensureTable() {
1820
- if (!this.table) throw new Error("Table is not defined. Call from(<table>) first.");
1821
- return this.table;
2209
+ if (!this.tableName) throw new Error("Table is not defined. Call from(<table>) first.");
2210
+ return this.tableName;
1822
2211
  }
1823
2212
  serializableConditions() {
1824
2213
  return normalizeCondition(this.conditions);
@@ -1826,7 +2215,7 @@ var QueryBuilderImpl = class {
1826
2215
  toSelectQuery() {
1827
2216
  return {
1828
2217
  type: "SelectQuery",
1829
- table: this.table,
2218
+ table: this.tableName,
1830
2219
  fields: this.fields,
1831
2220
  conditions: this.serializableConditions(),
1832
2221
  sort: this.sort,
@@ -1847,13 +2236,25 @@ var QueryBuilderImpl = class {
1847
2236
  partition: this.partitionValue ?? null
1848
2237
  };
1849
2238
  }
2239
+ async getAllRecordsForFormatting() {
2240
+ if (this.mode !== "select") throw new Error("Formatting is only applicable in select mode.");
2241
+ const table = this.ensureTable();
2242
+ const select = this.toSelectQuery();
2243
+ const pageSize = this.pageSizeValue ?? void 0;
2244
+ const initialNextPage = this.nextPageValue ?? void 0;
2245
+ return collectAllQueryRecords((nextPage) => this.db._queryPage(table, select, {
2246
+ pageSize,
2247
+ nextPage,
2248
+ partition: this.partitionValue
2249
+ }), initialNextPage);
2250
+ }
1850
2251
  toSerializableQueryObject() {
1851
2252
  const table = this.ensureTable();
1852
2253
  const payload = this.mode === "update" ? this.toUpdateQuery() : this.toSelectQuery();
1853
2254
  return { ...payload, table };
1854
2255
  }
1855
2256
  from(table) {
1856
- this.table = table;
2257
+ this.tableName = table;
1857
2258
  return this;
1858
2259
  }
1859
2260
  select(...fields) {
@@ -1985,6 +2386,26 @@ var QueryBuilderImpl = class {
1985
2386
  async one() {
1986
2387
  return this.firstOrNull();
1987
2388
  }
2389
+ async table(options) {
2390
+ return formatQueryResultsAsTable(
2391
+ await this.getAllRecordsForFormatting(),
2392
+ options,
2393
+ this.fields ?? void 0
2394
+ );
2395
+ }
2396
+ async tree(options) {
2397
+ return formatQueryResultsAsTree(await this.getAllRecordsForFormatting(), options);
2398
+ }
2399
+ async csv(options) {
2400
+ return formatQueryResultsAsCsv(
2401
+ await this.getAllRecordsForFormatting(),
2402
+ options,
2403
+ this.fields ?? void 0
2404
+ );
2405
+ }
2406
+ async json(options) {
2407
+ return formatQueryResultsAsJson(await this.getAllRecordsForFormatting(), options);
2408
+ }
1988
2409
  async delete() {
1989
2410
  if (this.mode !== "select") throw new Error("delete() is only applicable in select mode.");
1990
2411
  const table = this.ensureTable();