@tanstack/db-sqlite-persistence-core 0.4.3 → 0.4.5

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.
@@ -1,4 +1,7 @@
1
- import { SQLiteBigIntOutOfRangeError } from './errors'
1
+ import {
2
+ InvalidPersistedCollectionConfigError,
3
+ SQLiteBigIntOutOfRangeError,
4
+ } from './errors'
2
5
 
3
6
  export const SQLITE_BIGINT_MIN = -9_223_372_036_854_775_808n
4
7
  export const SQLITE_BIGINT_MAX = 9_223_372_036_854_775_807n
@@ -28,3 +31,97 @@ export function serializeSQLiteBigInt(value: bigint): SerializedSQLiteBigInt {
28
31
  [PERSISTED_VALUE_TAG]: value.toString(),
29
32
  }
30
33
  }
34
+
35
+ type SQLiteTemporalKind = `Temporal.Instant` | `Temporal.PlainDate`
36
+ type SQLiteTemporal = {
37
+ epochNanoseconds: bigint
38
+ year: number
39
+ month: number
40
+ day: number
41
+ withCalendar: (calendar: string) => SQLiteTemporal
42
+ }
43
+ type TemporalConstructor = {
44
+ from: (value: string) => SQLiteTemporal
45
+ prototype: { toString: () => string }
46
+ }
47
+
48
+ export type SerializedSQLiteTemporal = {
49
+ [PERSISTED_TYPE_TAG]: SQLiteTemporalKind
50
+ [PERSISTED_VALUE_TAG]: string
51
+ order: string
52
+ }
53
+
54
+ export function sqliteTemporalKind(
55
+ value: unknown,
56
+ ): SQLiteTemporalKind | undefined {
57
+ if (value === null || typeof value !== `object`) return
58
+ const tag = (value as { [Symbol.toStringTag]?: unknown })[Symbol.toStringTag]
59
+ if (tag === `Temporal.Instant` || tag === `Temporal.PlainDate`) return tag
60
+ return undefined
61
+ }
62
+
63
+ function requireSQLiteTemporalConstructor(
64
+ kind: SQLiteTemporalKind,
65
+ ): TemporalConstructor {
66
+ const name = kind.slice(`Temporal.`.length)
67
+ const constructor = (
68
+ globalThis as { Temporal?: Record<string, TemporalConstructor> }
69
+ ).Temporal?.[name]
70
+ if (
71
+ typeof constructor?.from !== `function` ||
72
+ typeof constructor.prototype.toString !== `function`
73
+ ) {
74
+ throw new InvalidPersistedCollectionConfigError(
75
+ `Missing global ${kind} constructor for SQLite persistence`,
76
+ )
77
+ }
78
+ return constructor
79
+ }
80
+
81
+ export function reviveSQLiteTemporal(
82
+ kind: SQLiteTemporalKind,
83
+ value: string,
84
+ ): unknown {
85
+ return requireSQLiteTemporalConstructor(kind).from(value)
86
+ }
87
+
88
+ /** Registered prototype methods validate the brand; user toString is ignored. */
89
+ export function serializeSQLiteTemporal(
90
+ value: unknown,
91
+ ): SerializedSQLiteTemporal | undefined {
92
+ if (value === null || typeof value !== `object`) return
93
+ const prototype = Object.getPrototypeOf(value)
94
+ if (prototype === null || prototype === Object.prototype) return
95
+ const tag = (value as { [Symbol.toStringTag]?: unknown })[Symbol.toStringTag]
96
+ if (typeof tag !== `string` || !tag.startsWith(`Temporal.`)) return
97
+ const kind = sqliteTemporalKind(value)
98
+ if (!kind)
99
+ throw new InvalidPersistedCollectionConfigError(
100
+ `Unsupported ${tag} value for SQLite persistence`,
101
+ )
102
+ const constructor = requireSQLiteTemporalConstructor(kind)
103
+ let text: string
104
+ try {
105
+ text = constructor.prototype.toString.call(value)
106
+ } catch {
107
+ throw new InvalidPersistedCollectionConfigError(
108
+ `Invalid ${kind} value for the registered global constructor`,
109
+ )
110
+ }
111
+ const native = constructor.from(text)
112
+ const iso =
113
+ kind === `Temporal.PlainDate` ? native.withCalendar(`iso8601`) : undefined
114
+ // Text keys avoid both floating-point precision loss and SQLite's int64 cap.
115
+ const order = iso
116
+ ? `${String(iso.year + 1_000_000).padStart(7, `0`)}${String(iso.month).padStart(2, `0`)}${String(iso.day).padStart(2, `0`)}`
117
+ : (native.epochNanoseconds + 10_000_000_000_000_000_000_000n)
118
+ .toString()
119
+ .padStart(23, `0`)
120
+ return { [PERSISTED_TYPE_TAG]: kind, [PERSISTED_VALUE_TAG]: text, order }
121
+ }
122
+
123
+ export function sqliteTemporalIdentity(
124
+ value: SerializedSQLiteTemporal,
125
+ ): string {
126
+ return `${value[PERSISTED_TYPE_TAG]}:${value[PERSISTED_VALUE_TAG]}`
127
+ }