@tanstack/db 0.0.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.
Files changed (154) hide show
  1. package/README.md +37 -0
  2. package/dist/cjs/SortedMap.cjs +140 -0
  3. package/dist/cjs/SortedMap.cjs.map +1 -0
  4. package/dist/cjs/SortedMap.d.cts +91 -0
  5. package/dist/cjs/collection.cjs +597 -0
  6. package/dist/cjs/collection.cjs.map +1 -0
  7. package/dist/cjs/collection.d.cts +176 -0
  8. package/dist/cjs/deferred.cjs +25 -0
  9. package/dist/cjs/deferred.cjs.map +1 -0
  10. package/dist/cjs/deferred.d.cts +20 -0
  11. package/dist/cjs/errors.cjs +10 -0
  12. package/dist/cjs/errors.cjs.map +1 -0
  13. package/dist/cjs/errors.d.cts +3 -0
  14. package/dist/cjs/index.cjs +33 -0
  15. package/dist/cjs/index.cjs.map +1 -0
  16. package/dist/cjs/index.d.cts +9 -0
  17. package/dist/cjs/proxy.cjs +654 -0
  18. package/dist/cjs/proxy.cjs.map +1 -0
  19. package/dist/cjs/proxy.d.cts +59 -0
  20. package/dist/cjs/query/compiled-query.cjs +162 -0
  21. package/dist/cjs/query/compiled-query.cjs.map +1 -0
  22. package/dist/cjs/query/compiled-query.d.cts +22 -0
  23. package/dist/cjs/query/evaluators.cjs +146 -0
  24. package/dist/cjs/query/evaluators.cjs.map +1 -0
  25. package/dist/cjs/query/evaluators.d.cts +9 -0
  26. package/dist/cjs/query/extractors.cjs +122 -0
  27. package/dist/cjs/query/extractors.cjs.map +1 -0
  28. package/dist/cjs/query/extractors.d.cts +22 -0
  29. package/dist/cjs/query/functions.cjs +152 -0
  30. package/dist/cjs/query/functions.cjs.map +1 -0
  31. package/dist/cjs/query/functions.d.cts +21 -0
  32. package/dist/cjs/query/group-by.cjs +91 -0
  33. package/dist/cjs/query/group-by.cjs.map +1 -0
  34. package/dist/cjs/query/group-by.d.cts +40 -0
  35. package/dist/cjs/query/index.d.cts +5 -0
  36. package/dist/cjs/query/joins.cjs +155 -0
  37. package/dist/cjs/query/joins.cjs.map +1 -0
  38. package/dist/cjs/query/joins.d.cts +14 -0
  39. package/dist/cjs/query/key-by.cjs +43 -0
  40. package/dist/cjs/query/key-by.cjs.map +1 -0
  41. package/dist/cjs/query/key-by.d.cts +3 -0
  42. package/dist/cjs/query/order-by.cjs +229 -0
  43. package/dist/cjs/query/order-by.cjs.map +1 -0
  44. package/dist/cjs/query/order-by.d.cts +3 -0
  45. package/dist/cjs/query/pipeline-compiler.cjs +94 -0
  46. package/dist/cjs/query/pipeline-compiler.cjs.map +1 -0
  47. package/dist/cjs/query/pipeline-compiler.d.cts +9 -0
  48. package/dist/cjs/query/query-builder.cjs +314 -0
  49. package/dist/cjs/query/query-builder.cjs.map +1 -0
  50. package/dist/cjs/query/query-builder.d.cts +219 -0
  51. package/dist/cjs/query/schema.d.cts +98 -0
  52. package/dist/cjs/query/select.cjs +107 -0
  53. package/dist/cjs/query/select.cjs.map +1 -0
  54. package/dist/cjs/query/select.d.cts +3 -0
  55. package/dist/cjs/query/types.d.cts +188 -0
  56. package/dist/cjs/query/utils.cjs +154 -0
  57. package/dist/cjs/query/utils.cjs.map +1 -0
  58. package/dist/cjs/query/utils.d.cts +37 -0
  59. package/dist/cjs/transactions.cjs +137 -0
  60. package/dist/cjs/transactions.cjs.map +1 -0
  61. package/dist/cjs/transactions.d.cts +27 -0
  62. package/dist/cjs/types.d.cts +94 -0
  63. package/dist/cjs/utils.cjs +17 -0
  64. package/dist/cjs/utils.cjs.map +1 -0
  65. package/dist/cjs/utils.d.cts +3 -0
  66. package/dist/esm/SortedMap.d.ts +91 -0
  67. package/dist/esm/SortedMap.js +140 -0
  68. package/dist/esm/SortedMap.js.map +1 -0
  69. package/dist/esm/collection.d.ts +176 -0
  70. package/dist/esm/collection.js +597 -0
  71. package/dist/esm/collection.js.map +1 -0
  72. package/dist/esm/deferred.d.ts +20 -0
  73. package/dist/esm/deferred.js +25 -0
  74. package/dist/esm/deferred.js.map +1 -0
  75. package/dist/esm/errors.d.ts +3 -0
  76. package/dist/esm/errors.js +10 -0
  77. package/dist/esm/errors.js.map +1 -0
  78. package/dist/esm/index.d.ts +9 -0
  79. package/dist/esm/index.js +33 -0
  80. package/dist/esm/index.js.map +1 -0
  81. package/dist/esm/proxy.d.ts +59 -0
  82. package/dist/esm/proxy.js +654 -0
  83. package/dist/esm/proxy.js.map +1 -0
  84. package/dist/esm/query/compiled-query.d.ts +22 -0
  85. package/dist/esm/query/compiled-query.js +162 -0
  86. package/dist/esm/query/compiled-query.js.map +1 -0
  87. package/dist/esm/query/evaluators.d.ts +9 -0
  88. package/dist/esm/query/evaluators.js +146 -0
  89. package/dist/esm/query/evaluators.js.map +1 -0
  90. package/dist/esm/query/extractors.d.ts +22 -0
  91. package/dist/esm/query/extractors.js +122 -0
  92. package/dist/esm/query/extractors.js.map +1 -0
  93. package/dist/esm/query/functions.d.ts +21 -0
  94. package/dist/esm/query/functions.js +152 -0
  95. package/dist/esm/query/functions.js.map +1 -0
  96. package/dist/esm/query/group-by.d.ts +40 -0
  97. package/dist/esm/query/group-by.js +91 -0
  98. package/dist/esm/query/group-by.js.map +1 -0
  99. package/dist/esm/query/index.d.ts +5 -0
  100. package/dist/esm/query/joins.d.ts +14 -0
  101. package/dist/esm/query/joins.js +155 -0
  102. package/dist/esm/query/joins.js.map +1 -0
  103. package/dist/esm/query/key-by.d.ts +3 -0
  104. package/dist/esm/query/key-by.js +43 -0
  105. package/dist/esm/query/key-by.js.map +1 -0
  106. package/dist/esm/query/order-by.d.ts +3 -0
  107. package/dist/esm/query/order-by.js +229 -0
  108. package/dist/esm/query/order-by.js.map +1 -0
  109. package/dist/esm/query/pipeline-compiler.d.ts +9 -0
  110. package/dist/esm/query/pipeline-compiler.js +94 -0
  111. package/dist/esm/query/pipeline-compiler.js.map +1 -0
  112. package/dist/esm/query/query-builder.d.ts +219 -0
  113. package/dist/esm/query/query-builder.js +314 -0
  114. package/dist/esm/query/query-builder.js.map +1 -0
  115. package/dist/esm/query/schema.d.ts +98 -0
  116. package/dist/esm/query/select.d.ts +3 -0
  117. package/dist/esm/query/select.js +107 -0
  118. package/dist/esm/query/select.js.map +1 -0
  119. package/dist/esm/query/types.d.ts +188 -0
  120. package/dist/esm/query/utils.d.ts +37 -0
  121. package/dist/esm/query/utils.js +154 -0
  122. package/dist/esm/query/utils.js.map +1 -0
  123. package/dist/esm/transactions.d.ts +27 -0
  124. package/dist/esm/transactions.js +137 -0
  125. package/dist/esm/transactions.js.map +1 -0
  126. package/dist/esm/types.d.ts +94 -0
  127. package/dist/esm/utils.d.ts +3 -0
  128. package/dist/esm/utils.js +17 -0
  129. package/dist/esm/utils.js.map +1 -0
  130. package/package.json +57 -0
  131. package/src/SortedMap.ts +163 -0
  132. package/src/collection.ts +919 -0
  133. package/src/deferred.ts +47 -0
  134. package/src/errors.ts +6 -0
  135. package/src/index.ts +12 -0
  136. package/src/proxy.ts +1104 -0
  137. package/src/query/compiled-query.ts +193 -0
  138. package/src/query/evaluators.ts +222 -0
  139. package/src/query/extractors.ts +211 -0
  140. package/src/query/functions.ts +297 -0
  141. package/src/query/group-by.ts +137 -0
  142. package/src/query/index.ts +5 -0
  143. package/src/query/joins.ts +247 -0
  144. package/src/query/key-by.ts +61 -0
  145. package/src/query/order-by.ts +312 -0
  146. package/src/query/pipeline-compiler.ts +152 -0
  147. package/src/query/query-builder.ts +898 -0
  148. package/src/query/schema.ts +255 -0
  149. package/src/query/select.ts +173 -0
  150. package/src/query/types.ts +417 -0
  151. package/src/query/utils.ts +245 -0
  152. package/src/transactions.ts +198 -0
  153. package/src/types.ts +125 -0
  154. package/src/utils.ts +15 -0
@@ -0,0 +1,198 @@
1
+ import { createDeferred } from "./deferred"
2
+ import type { Deferred } from "./deferred"
3
+ import type {
4
+ PendingMutation,
5
+ TransactionConfig,
6
+ TransactionState,
7
+ } from "./types"
8
+
9
+ function generateUUID() {
10
+ // Check if crypto.randomUUID is available (modern browsers and Node.js 15+)
11
+ if (
12
+ typeof crypto !== `undefined` &&
13
+ typeof crypto.randomUUID === `function`
14
+ ) {
15
+ return crypto.randomUUID()
16
+ }
17
+
18
+ // Fallback implementation for older environments
19
+ return `xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx`.replace(/[xy]/g, function (c) {
20
+ const r = (Math.random() * 16) | 0
21
+ const v = c === `x` ? r : (r & 0x3) | 0x8
22
+ return v.toString(16)
23
+ })
24
+ }
25
+
26
+ const transactions: Array<Transaction> = []
27
+
28
+ export function createTransaction(config: TransactionConfig): Transaction {
29
+ if (typeof config.mutationFn === `undefined`) {
30
+ throw `mutationFn is required when creating a transaction`
31
+ }
32
+
33
+ let transactionId = config.id
34
+ if (!transactionId) {
35
+ transactionId = generateUUID()
36
+ }
37
+ const newTransaction = new Transaction({ ...config, id: transactionId })
38
+ transactions.push(newTransaction)
39
+
40
+ return newTransaction
41
+ }
42
+
43
+ let transactionStack: Array<Transaction> = []
44
+
45
+ export function getActiveTransaction(): Transaction | undefined {
46
+ if (transactionStack.length > 0) {
47
+ return transactionStack.slice(-1)[0]
48
+ } else {
49
+ return undefined
50
+ }
51
+ }
52
+
53
+ function registerTransaction(tx: Transaction) {
54
+ transactionStack.push(tx)
55
+ }
56
+
57
+ function unregisterTransaction(tx: Transaction) {
58
+ transactionStack = transactionStack.filter((t) => t.id !== tx.id)
59
+ }
60
+
61
+ export class Transaction {
62
+ public id: string
63
+ public state: TransactionState
64
+ public mutationFn
65
+ public mutations: Array<PendingMutation<any>>
66
+ public isPersisted: Deferred<Transaction>
67
+ public autoCommit: boolean
68
+ public createdAt: Date
69
+ public metadata: Record<string, unknown>
70
+ public error?: {
71
+ message: string
72
+ error: Error
73
+ }
74
+
75
+ constructor(config: TransactionConfig) {
76
+ this.id = config.id!
77
+ this.mutationFn = config.mutationFn
78
+ this.state = `pending`
79
+ this.mutations = []
80
+ this.isPersisted = createDeferred()
81
+ this.autoCommit = config.autoCommit ?? true
82
+ this.createdAt = new Date()
83
+ this.metadata = config.metadata ?? {}
84
+ }
85
+
86
+ setState(newState: TransactionState) {
87
+ this.state = newState
88
+ }
89
+
90
+ mutate(callback: () => void): Transaction {
91
+ if (this.state !== `pending`) {
92
+ throw `You can no longer call .mutate() as the transaction is no longer pending`
93
+ }
94
+
95
+ registerTransaction(this)
96
+ try {
97
+ callback()
98
+ } finally {
99
+ unregisterTransaction(this)
100
+ }
101
+
102
+ if (this.autoCommit) {
103
+ this.commit()
104
+ }
105
+
106
+ return this
107
+ }
108
+
109
+ applyMutations(mutations: Array<PendingMutation<any>>): void {
110
+ for (const newMutation of mutations) {
111
+ const existingIndex = this.mutations.findIndex(
112
+ (m) => m.key === newMutation.key
113
+ )
114
+
115
+ if (existingIndex >= 0) {
116
+ // Replace existing mutation
117
+ this.mutations[existingIndex] = newMutation
118
+ } else {
119
+ // Insert new mutation
120
+ this.mutations.push(newMutation)
121
+ }
122
+ }
123
+ }
124
+
125
+ rollback(config?: { isSecondaryRollback?: boolean }): Transaction {
126
+ const isSecondaryRollback = config?.isSecondaryRollback ?? false
127
+ if (this.state === `completed`) {
128
+ throw `You can no longer call .rollback() as the transaction is already completed`
129
+ }
130
+
131
+ this.setState(`failed`)
132
+
133
+ // See if there's any other transactions w/ mutations on the same keys
134
+ // and roll them back as well.
135
+ if (!isSecondaryRollback) {
136
+ const mutationKeys = new Set()
137
+ this.mutations.forEach((m) => mutationKeys.add(m.key))
138
+ transactions.forEach(
139
+ (t) =>
140
+ t.state === `pending` &&
141
+ t.mutations.some((m) => mutationKeys.has(m.key)) &&
142
+ t.rollback({ isSecondaryRollback: true })
143
+ )
144
+ }
145
+
146
+ // Reject the promise
147
+ this.isPersisted.reject(this.error?.error)
148
+
149
+ this.touchCollection()
150
+
151
+ return this
152
+ }
153
+
154
+ // Tell collection that something has changed with the transaction
155
+ touchCollection(): void {
156
+ const hasCalled = new Set()
157
+ this.mutations.forEach((mutation) => {
158
+ if (!hasCalled.has(mutation.collection.id)) {
159
+ mutation.collection.transactions.setState((state) => state)
160
+ mutation.collection.commitPendingTransactions()
161
+ hasCalled.add(mutation.collection.id)
162
+ }
163
+ })
164
+ }
165
+
166
+ async commit(): Promise<Transaction> {
167
+ if (this.state !== `pending`) {
168
+ throw `You can no longer call .commit() as the transaction is no longer pending`
169
+ }
170
+
171
+ this.setState(`persisting`)
172
+
173
+ if (this.mutations.length === 0) {
174
+ this.setState(`completed`)
175
+ }
176
+
177
+ // Run mutationFn
178
+ try {
179
+ await this.mutationFn({ transaction: this })
180
+
181
+ this.setState(`completed`)
182
+ this.touchCollection()
183
+
184
+ this.isPersisted.resolve(this)
185
+ } catch (error) {
186
+ // Update transaction with error information
187
+ this.error = {
188
+ message: error instanceof Error ? error.message : String(error),
189
+ error: error instanceof Error ? error : new Error(String(error)),
190
+ }
191
+
192
+ // rollback the transaction
193
+ return this.rollback()
194
+ }
195
+
196
+ return this
197
+ }
198
+ }
package/src/types.ts ADDED
@@ -0,0 +1,125 @@
1
+ import type { Collection } from "./collection"
2
+ import type { StandardSchemaV1 } from "@standard-schema/spec"
3
+ import type { Transaction } from "./transactions"
4
+
5
+ export type TransactionState = `pending` | `persisting` | `completed` | `failed`
6
+
7
+ /**
8
+ * Represents a pending mutation within a transaction
9
+ * Contains information about the original and modified data, as well as metadata
10
+ */
11
+ export interface PendingMutation<T extends object = Record<string, unknown>> {
12
+ mutationId: string
13
+ original: Record<string, unknown>
14
+ modified: Record<string, unknown>
15
+ changes: Record<string, unknown>
16
+ key: string
17
+ type: OperationType
18
+ metadata: unknown
19
+ syncMetadata: Record<string, unknown>
20
+ createdAt: Date
21
+ updatedAt: Date
22
+ collection: Collection<T>
23
+ }
24
+
25
+ /**
26
+ * Configuration options for creating a new transaction
27
+ */
28
+ export type MutationFnParams = {
29
+ transaction: Transaction
30
+ }
31
+
32
+ export type MutationFn = (params: MutationFnParams) => Promise<any>
33
+
34
+ export interface TransactionConfig {
35
+ /** Unique identifier for the transaction */
36
+ id?: string
37
+ /* If the transaction should autocommit after a mutate call or should commit be called explicitly */
38
+ autoCommit?: boolean
39
+ mutationFn: MutationFn
40
+ /** Custom metadata to associate with the transaction */
41
+ metadata?: Record<string, unknown>
42
+ }
43
+
44
+ export type { Transaction }
45
+
46
+ type Value<TExtensions = never> =
47
+ | string
48
+ | number
49
+ | boolean
50
+ | bigint
51
+ | null
52
+ | TExtensions
53
+ | Array<Value<TExtensions>>
54
+ | { [key: string]: Value<TExtensions> }
55
+
56
+ export type Row<TExtensions = never> = Record<string, Value<TExtensions>>
57
+
58
+ export type OperationType = `insert` | `update` | `delete`
59
+
60
+ export interface SyncConfig<T extends object = Record<string, unknown>> {
61
+ sync: (params: {
62
+ collection: Collection<T>
63
+ begin: () => void
64
+ write: (message: ChangeMessage<T>) => void
65
+ commit: () => void
66
+ }) => void
67
+
68
+ /**
69
+ * Get the sync metadata for insert operations
70
+ * @returns Record containing primaryKey and relation information
71
+ */
72
+ getSyncMetadata?: () => Record<string, unknown>
73
+ }
74
+
75
+ export interface ChangeMessage<T extends object = Record<string, unknown>> {
76
+ key: string
77
+ value: T
78
+ previousValue?: T
79
+ type: OperationType
80
+ metadata?: Record<string, unknown>
81
+ }
82
+
83
+ export interface OptimisticChangeMessage<
84
+ T extends object = Record<string, unknown>,
85
+ > extends ChangeMessage<T> {
86
+ // Is this change message part of an active transaction. Only applies to optimistic changes.
87
+ isActive?: boolean
88
+ }
89
+
90
+ /**
91
+ * The Standard Schema interface.
92
+ * This follows the standard-schema specification: https://github.com/standard-schema/standard-schema
93
+ */
94
+ export type StandardSchema<T> = StandardSchemaV1 & {
95
+ "~standard": {
96
+ types?: {
97
+ input: T
98
+ output: T
99
+ }
100
+ }
101
+ }
102
+
103
+ /**
104
+ * Type alias for StandardSchema
105
+ */
106
+ export type StandardSchemaAlias<T = unknown> = StandardSchema<T>
107
+
108
+ export interface OperationConfig {
109
+ metadata?: Record<string, unknown>
110
+ }
111
+
112
+ export interface InsertConfig {
113
+ key?: string | Array<string | undefined>
114
+ metadata?: Record<string, unknown>
115
+ }
116
+
117
+ export interface CollectionConfig<T extends object = Record<string, unknown>> {
118
+ id: string
119
+ sync: SyncConfig<T>
120
+ schema?: StandardSchema<T>
121
+ }
122
+
123
+ export type ChangesPayload<T extends object = Record<string, unknown>> = Array<
124
+ ChangeMessage<T>
125
+ >
package/src/utils.ts ADDED
@@ -0,0 +1,15 @@
1
+ export function getLockedObjects(): Set<string> {
2
+ // Stub implementation that returns an empty Set
3
+ return new Set()
4
+ }
5
+
6
+ let globalVersion = 0
7
+
8
+ export function getGlobalVersion(): number {
9
+ return globalVersion
10
+ }
11
+
12
+ export function advanceGlobalVersion(): number {
13
+ console.log(`==== advancing global version`, globalVersion + 1)
14
+ return globalVersion++
15
+ }