@objectstack/driver-sql 9.9.1 → 9.10.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.d.mts +22 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +71 -18
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +71 -18
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/dist/index.mjs
CHANGED
|
@@ -11,9 +11,12 @@ var JSON_COLUMN_TYPES = /* @__PURE__ */ new Set([
|
|
|
11
11
|
"json",
|
|
12
12
|
"object",
|
|
13
13
|
"array",
|
|
14
|
+
"record",
|
|
14
15
|
"image",
|
|
15
16
|
"file",
|
|
16
17
|
"avatar",
|
|
18
|
+
"video",
|
|
19
|
+
"audio",
|
|
17
20
|
"location",
|
|
18
21
|
"address",
|
|
19
22
|
"composite",
|
|
@@ -23,6 +26,18 @@ var JSON_COLUMN_TYPES = /* @__PURE__ */ new Set([
|
|
|
23
26
|
"repeater",
|
|
24
27
|
"vector"
|
|
25
28
|
]);
|
|
29
|
+
var NUMERIC_SCALAR_TYPES = /* @__PURE__ */ new Set([
|
|
30
|
+
"integer",
|
|
31
|
+
"int",
|
|
32
|
+
"float",
|
|
33
|
+
"number",
|
|
34
|
+
"currency",
|
|
35
|
+
"percent",
|
|
36
|
+
"summary",
|
|
37
|
+
"rating",
|
|
38
|
+
"slider",
|
|
39
|
+
"progress"
|
|
40
|
+
]);
|
|
26
41
|
var SqlDriver = class {
|
|
27
42
|
constructor(config) {
|
|
28
43
|
// IDataDriver metadata
|
|
@@ -30,6 +45,7 @@ var SqlDriver = class {
|
|
|
30
45
|
this.version = "1.0.0";
|
|
31
46
|
this.jsonFields = {};
|
|
32
47
|
this.booleanFields = {};
|
|
48
|
+
this.numericFields = {};
|
|
33
49
|
this.dateFields = {};
|
|
34
50
|
this.datetimeFields = {};
|
|
35
51
|
this.tablesWithTimestamps = /* @__PURE__ */ new Set();
|
|
@@ -810,6 +826,7 @@ var SqlDriver = class {
|
|
|
810
826
|
const tableName = StorageNameMapping.resolveTableName(obj);
|
|
811
827
|
const jsonCols = [];
|
|
812
828
|
const booleanCols = [];
|
|
829
|
+
const numericCols = [];
|
|
813
830
|
const autoNumberCols = [];
|
|
814
831
|
const tenancyDecl = obj?.tenancy;
|
|
815
832
|
let tenantField = null;
|
|
@@ -829,9 +846,12 @@ var SqlDriver = class {
|
|
|
829
846
|
if (this.isJsonField(type, field)) {
|
|
830
847
|
jsonCols.push(name);
|
|
831
848
|
}
|
|
832
|
-
if (type === "boolean") {
|
|
849
|
+
if (type === "boolean" || type === "toggle") {
|
|
833
850
|
booleanCols.push(name);
|
|
834
851
|
}
|
|
852
|
+
if (NUMERIC_SCALAR_TYPES.has(type) && !field.multiple) {
|
|
853
|
+
numericCols.push(name);
|
|
854
|
+
}
|
|
835
855
|
if (type === "date") {
|
|
836
856
|
((_a = this.dateFields)[tableName] ?? (_a[tableName] = /* @__PURE__ */ new Set())).add(name);
|
|
837
857
|
}
|
|
@@ -850,6 +870,7 @@ var SqlDriver = class {
|
|
|
850
870
|
}
|
|
851
871
|
this.jsonFields[tableName] = jsonCols;
|
|
852
872
|
this.booleanFields[tableName] = booleanCols;
|
|
873
|
+
this.numericFields[tableName] = numericCols;
|
|
853
874
|
this.autoNumberFields[tableName] = autoNumberCols;
|
|
854
875
|
this.tenantFieldByTable[tableName] = tenantField;
|
|
855
876
|
let exists = await this.knex.schema.hasTable(tableName);
|
|
@@ -1201,11 +1222,35 @@ var SqlDriver = class {
|
|
|
1201
1222
|
return null;
|
|
1202
1223
|
};
|
|
1203
1224
|
if (isDatetime) {
|
|
1225
|
+
if (!this.isSqlite) return value;
|
|
1204
1226
|
const ms = toMs(value);
|
|
1205
1227
|
return ms == null ? value : ms;
|
|
1206
1228
|
}
|
|
1207
1229
|
return this.toDateOnly(value);
|
|
1208
1230
|
}
|
|
1231
|
+
/**
|
|
1232
|
+
* Public, dialect-correct temporal filter-value coercion for callers that
|
|
1233
|
+
* build SQL *outside* the normal `find()`/`applyFilters()` path — chiefly the
|
|
1234
|
+
* analytics native-SQL strategy, which compiles a raw `SELECT … WHERE col >= $N`
|
|
1235
|
+
* and binds the value directly, bypassing `coerceFilterValue`.
|
|
1236
|
+
*
|
|
1237
|
+
* Given a logical object (table) name, a field name and a filter value
|
|
1238
|
+
* (typically an ISO date/datetime string from a dashboard relative-date
|
|
1239
|
+
* token like `{12_months_ago}`), this returns the value in the column's
|
|
1240
|
+
* on-disk storage form:
|
|
1241
|
+
* - SQLite `Field.datetime` → epoch milliseconds (INTEGER), so the
|
|
1242
|
+
* comparison matches the stored integer rather than failing a
|
|
1243
|
+
* TEXT-vs-INTEGER affinity compare.
|
|
1244
|
+
* - `Field.date` (any dialect) → `YYYY-MM-DD` text.
|
|
1245
|
+
* - Native-timestamp dialects / non-temporal fields → value unchanged.
|
|
1246
|
+
*
|
|
1247
|
+
* This is a thin, intentionally narrow wrapper over the same `coerceFilterValue`
|
|
1248
|
+
* the driver already uses, so there is exactly one source of truth for the
|
|
1249
|
+
* storage convention and the analytics path can never drift from CRUD.
|
|
1250
|
+
*/
|
|
1251
|
+
temporalFilterValue(objectName, field, value) {
|
|
1252
|
+
return this.coerceFilterValue(objectName, field, value);
|
|
1253
|
+
}
|
|
1209
1254
|
applyFilters(builder, filters) {
|
|
1210
1255
|
if (!filters) return;
|
|
1211
1256
|
const table = this.tableNameForBuilder(builder);
|
|
@@ -1427,9 +1472,23 @@ var SqlDriver = class {
|
|
|
1427
1472
|
case "number":
|
|
1428
1473
|
case "currency":
|
|
1429
1474
|
case "percent":
|
|
1475
|
+
// `rating`/`slider`/`progress` are authored as numeric scalars (a star
|
|
1476
|
+
// count, a slider position, a percent-of-completion). Without an explicit
|
|
1477
|
+
// case they fell to `default → table.string`, giving the column TEXT
|
|
1478
|
+
// affinity so SQLite coerced the written number to a string ('4' not 4) —
|
|
1479
|
+
// a silent type-fidelity leak the value-loss tests didn't catch. REAL
|
|
1480
|
+
// affinity round-trips them as JS numbers (#field-zoo).
|
|
1481
|
+
case "rating":
|
|
1482
|
+
case "slider":
|
|
1483
|
+
case "progress":
|
|
1430
1484
|
col = table.float(name);
|
|
1431
1485
|
break;
|
|
1486
|
+
// `toggle` is a boolean rendered as a switch. Same leak as above (TEXT
|
|
1487
|
+
// affinity stored '1'); a boolean column gives NUMERIC affinity and the
|
|
1488
|
+
// `booleanFields` read-coercion below converts the stored 1/0 back to a
|
|
1489
|
+
// real JS boolean.
|
|
1432
1490
|
case "boolean":
|
|
1491
|
+
case "toggle":
|
|
1433
1492
|
col = table.boolean(name);
|
|
1434
1493
|
break;
|
|
1435
1494
|
case "date":
|
|
@@ -1441,22 +1500,6 @@ var SqlDriver = class {
|
|
|
1441
1500
|
case "time":
|
|
1442
1501
|
col = table.time(name);
|
|
1443
1502
|
break;
|
|
1444
|
-
case "json":
|
|
1445
|
-
case "object":
|
|
1446
|
-
case "array":
|
|
1447
|
-
case "image":
|
|
1448
|
-
case "file":
|
|
1449
|
-
case "avatar":
|
|
1450
|
-
case "location":
|
|
1451
|
-
case "address":
|
|
1452
|
-
case "composite":
|
|
1453
|
-
case "multiselect":
|
|
1454
|
-
case "checkboxes":
|
|
1455
|
-
case "tags":
|
|
1456
|
-
case "repeater":
|
|
1457
|
-
case "vector":
|
|
1458
|
-
col = table.json(name);
|
|
1459
|
-
break;
|
|
1460
1503
|
case "lookup":
|
|
1461
1504
|
col = table.string(name);
|
|
1462
1505
|
if (field.reference_to) {
|
|
@@ -1474,7 +1517,7 @@ var SqlDriver = class {
|
|
|
1474
1517
|
return;
|
|
1475
1518
|
// Virtual — no column
|
|
1476
1519
|
default:
|
|
1477
|
-
col = table.string(name);
|
|
1520
|
+
col = JSON_COLUMN_TYPES.has(type) ? table.json(name) : table.string(name);
|
|
1478
1521
|
}
|
|
1479
1522
|
if (col) {
|
|
1480
1523
|
if (field.unique) col.unique();
|
|
@@ -1639,6 +1682,16 @@ var SqlDriver = class {
|
|
|
1639
1682
|
}
|
|
1640
1683
|
}
|
|
1641
1684
|
}
|
|
1685
|
+
const numericFields = this.numericFields[object];
|
|
1686
|
+
if (numericFields && numericFields.length > 0) {
|
|
1687
|
+
for (const field of numericFields) {
|
|
1688
|
+
const v = data[field];
|
|
1689
|
+
if (typeof v === "string" && v.trim() !== "") {
|
|
1690
|
+
const n = Number(v);
|
|
1691
|
+
if (!Number.isNaN(n)) data[field] = n;
|
|
1692
|
+
}
|
|
1693
|
+
}
|
|
1694
|
+
}
|
|
1642
1695
|
}
|
|
1643
1696
|
const dateFields = this.dateFields[object];
|
|
1644
1697
|
if (dateFields && dateFields.size > 0) {
|