@objectstack/driver-sql 9.8.0 → 9.9.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.
package/dist/index.d.mts CHANGED
@@ -403,6 +403,16 @@ declare class SqlDriver implements IDataDriver {
403
403
  * `initObjects`). Returns null when the builder is not table-scoped yet.
404
404
  */
405
405
  protected tableNameForBuilder(builder: any): string | null;
406
+ /**
407
+ * Collapse a `Field.date` value to a timezone-naive `YYYY-MM-DD`
408
+ * calendar-day string (ADR-0053 Phase 1). A `Date` collapses to its UTC
409
+ * calendar day; a string keeps its leading date and drops any time
410
+ * component. Anything else (and `null`/`undefined`) passes through
411
+ * unchanged. This is the single source of truth for date-only truncation,
412
+ * shared by the filter (`coerceFilterValue`), write (`formatInput`) and
413
+ * read (`formatOutput`) paths so all three agree on what a date *is*.
414
+ */
415
+ protected toDateOnly(value: any): any;
406
416
  /**
407
417
  * Normalise a filter value for a single column so the comparison the
408
418
  * driver sends to SQLite matches the on-disk representation.
package/dist/index.d.ts CHANGED
@@ -403,6 +403,16 @@ declare class SqlDriver implements IDataDriver {
403
403
  * `initObjects`). Returns null when the builder is not table-scoped yet.
404
404
  */
405
405
  protected tableNameForBuilder(builder: any): string | null;
406
+ /**
407
+ * Collapse a `Field.date` value to a timezone-naive `YYYY-MM-DD`
408
+ * calendar-day string (ADR-0053 Phase 1). A `Date` collapses to its UTC
409
+ * calendar day; a string keeps its leading date and drops any time
410
+ * component. Anything else (and `null`/`undefined`) passes through
411
+ * unchanged. This is the single source of truth for date-only truncation,
412
+ * shared by the filter (`coerceFilterValue`), write (`formatInput`) and
413
+ * read (`formatOutput`) paths so all three agree on what a date *is*.
414
+ */
415
+ protected toDateOnly(value: any): any;
406
416
  /**
407
417
  * Normalise a filter value for a single column so the comparison the
408
418
  * driver sends to SQLite matches the on-disk representation.
package/dist/index.js CHANGED
@@ -44,6 +44,22 @@ var import_node_crypto = require("crypto");
44
44
  var DEFAULT_ID_LENGTH = 16;
45
45
  var SEQUENCES_TABLE = "_objectstack_sequences";
46
46
  var GLOBAL_TENANT = "__global__";
47
+ var JSON_COLUMN_TYPES = /* @__PURE__ */ new Set([
48
+ "json",
49
+ "object",
50
+ "array",
51
+ "image",
52
+ "file",
53
+ "avatar",
54
+ "location",
55
+ "address",
56
+ "composite",
57
+ "multiselect",
58
+ "checkboxes",
59
+ "tags",
60
+ "repeater",
61
+ "vector"
62
+ ]);
47
63
  var SqlDriver = class {
48
64
  constructor(config) {
49
65
  // IDataDriver metadata
@@ -330,10 +346,8 @@ var SqlDriver = class {
330
346
  if (!Array.isArray(results)) {
331
347
  return [];
332
348
  }
333
- if (this.isSqlite) {
334
- for (const row of results) {
335
- this.formatOutput(object, row);
336
- }
349
+ for (const row of results) {
350
+ this.formatOutput(object, row);
337
351
  }
338
352
  return results;
339
353
  }
@@ -1162,6 +1176,30 @@ var SqlDriver = class {
1162
1176
  if (typeof t === "string") return t;
1163
1177
  return null;
1164
1178
  }
1179
+ /**
1180
+ * Collapse a `Field.date` value to a timezone-naive `YYYY-MM-DD`
1181
+ * calendar-day string (ADR-0053 Phase 1). A `Date` collapses to its UTC
1182
+ * calendar day; a string keeps its leading date and drops any time
1183
+ * component. Anything else (and `null`/`undefined`) passes through
1184
+ * unchanged. This is the single source of truth for date-only truncation,
1185
+ * shared by the filter (`coerceFilterValue`), write (`formatInput`) and
1186
+ * read (`formatOutput`) paths so all three agree on what a date *is*.
1187
+ */
1188
+ toDateOnly(value) {
1189
+ if (value == null) return value;
1190
+ if (value instanceof Date) {
1191
+ if (Number.isNaN(value.getTime())) return value;
1192
+ const y = value.getUTCFullYear();
1193
+ const m = String(value.getUTCMonth() + 1).padStart(2, "0");
1194
+ const d = String(value.getUTCDate()).padStart(2, "0");
1195
+ return `${y}-${m}-${d}`;
1196
+ }
1197
+ if (typeof value === "string") {
1198
+ const trimmed = value.trim();
1199
+ if (/^\d{4}-\d{2}-\d{2}/.test(trimmed)) return trimmed.slice(0, 10);
1200
+ }
1201
+ return value;
1202
+ }
1165
1203
  /**
1166
1204
  * Normalise a filter value for a single column so the comparison the
1167
1205
  * driver sends to SQLite matches the on-disk representation.
@@ -1203,17 +1241,7 @@ var SqlDriver = class {
1203
1241
  const ms = toMs(value);
1204
1242
  return ms == null ? value : ms;
1205
1243
  }
1206
- if (value instanceof Date) {
1207
- const y = value.getUTCFullYear();
1208
- const m = String(value.getUTCMonth() + 1).padStart(2, "0");
1209
- const d = String(value.getUTCDate()).padStart(2, "0");
1210
- return `${y}-${m}-${d}`;
1211
- }
1212
- if (typeof value === "string") {
1213
- const trimmed = value.trim();
1214
- if (/^\d{4}-\d{2}-\d{2}/.test(trimmed)) return trimmed.slice(0, 10);
1215
- }
1216
- return value;
1244
+ return this.toDateOnly(value);
1217
1245
  }
1218
1246
  applyFilters(builder, filters) {
1219
1247
  if (!filters) return;
@@ -1457,6 +1485,13 @@ var SqlDriver = class {
1457
1485
  case "file":
1458
1486
  case "avatar":
1459
1487
  case "location":
1488
+ case "address":
1489
+ case "composite":
1490
+ case "multiselect":
1491
+ case "checkboxes":
1492
+ case "tags":
1493
+ case "repeater":
1494
+ case "vector":
1460
1495
  col = table.json(name);
1461
1496
  break;
1462
1497
  case "lookup":
@@ -1560,7 +1595,7 @@ var SqlDriver = class {
1560
1595
  }
1561
1596
  }
1562
1597
  isJsonField(type, field) {
1563
- return ["json", "object", "array", "image", "file", "avatar", "location"].includes(type) || field.multiple;
1598
+ return JSON_COLUMN_TYPES.has(type) || !!field.multiple;
1564
1599
  }
1565
1600
  // ── SQLite serialisation ────────────────────────────────────────────────────
1566
1601
  formatInput(object, data) {
@@ -1579,16 +1614,42 @@ var SqlDriver = class {
1579
1614
  }
1580
1615
  }
1581
1616
  }
1617
+ const dateFields = this.dateFields[object];
1618
+ if (dateFields && dateFields.size > 0 && copy && typeof copy === "object") {
1619
+ for (const field of dateFields) {
1620
+ const v = copy[field];
1621
+ if (v == null) continue;
1622
+ const normalized = this.toDateOnly(v);
1623
+ if (normalized !== v) {
1624
+ if (!copied) {
1625
+ copy = { ...copy };
1626
+ copied = true;
1627
+ }
1628
+ copy[field] = normalized;
1629
+ }
1630
+ }
1631
+ }
1582
1632
  if (!this.isSqlite) return copy;
1583
1633
  const fields = this.jsonFields[object];
1584
- if (!fields || fields.length === 0) return copy;
1585
- if (!copied) {
1586
- copy = { ...copy };
1587
- copied = true;
1588
- }
1589
- for (const field of fields) {
1590
- if (copy[field] !== void 0 && typeof copy[field] === "object" && copy[field] !== null) {
1591
- copy[field] = JSON.stringify(copy[field]);
1634
+ if (fields && fields.length > 0) {
1635
+ if (!copied) {
1636
+ copy = { ...copy };
1637
+ copied = true;
1638
+ }
1639
+ for (const field of fields) {
1640
+ if (copy[field] !== void 0 && typeof copy[field] === "object" && copy[field] !== null) {
1641
+ copy[field] = JSON.stringify(copy[field]);
1642
+ }
1643
+ }
1644
+ }
1645
+ for (const key of Object.keys(copy)) {
1646
+ const v = copy[key];
1647
+ if (v !== null && typeof v === "object" && !(v instanceof Date) && !Buffer.isBuffer(v)) {
1648
+ if (!copied) {
1649
+ copy = { ...copy };
1650
+ copied = true;
1651
+ }
1652
+ copy[key] = JSON.stringify(v);
1592
1653
  }
1593
1654
  }
1594
1655
  return copy;
@@ -1616,6 +1677,15 @@ var SqlDriver = class {
1616
1677
  }
1617
1678
  }
1618
1679
  }
1680
+ const dateFields = this.dateFields[object];
1681
+ if (dateFields && dateFields.size > 0) {
1682
+ for (const field of dateFields) {
1683
+ const v = data[field];
1684
+ if (v == null) continue;
1685
+ const normalized = this.toDateOnly(v);
1686
+ if (normalized !== v) data[field] = normalized;
1687
+ }
1688
+ }
1619
1689
  return data;
1620
1690
  }
1621
1691
  // ── Introspection internals ─────────────────────────────────────────────────