@livestore/common 0.4.0-dev.17 → 0.4.0-dev.19
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/.tsbuildinfo +1 -1
- package/dist/ClientSessionLeaderThreadProxy.d.ts +10 -10
- package/dist/ClientSessionLeaderThreadProxy.d.ts.map +1 -1
- package/dist/adapter-types.d.ts +5 -5
- package/dist/adapter-types.d.ts.map +1 -1
- package/dist/devtools/devtools-messages-client-session.d.ts +28 -23
- package/dist/devtools/devtools-messages-client-session.d.ts.map +1 -1
- package/dist/devtools/devtools-messages-client-session.js +2 -2
- package/dist/devtools/devtools-messages-client-session.js.map +1 -1
- package/dist/devtools/devtools-messages-common.d.ts +6 -6
- package/dist/devtools/devtools-messages-leader.d.ts +33 -28
- package/dist/devtools/devtools-messages-leader.d.ts.map +1 -1
- package/dist/devtools/devtools-messages-leader.js +8 -8
- package/dist/devtools/devtools-messages-leader.js.map +1 -1
- package/dist/errors.d.ts +6 -6
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +7 -7
- package/dist/errors.js.map +1 -1
- package/dist/leader-thread/LeaderSyncProcessor.d.ts +2 -2
- package/dist/leader-thread/LeaderSyncProcessor.d.ts.map +1 -1
- package/dist/leader-thread/LeaderSyncProcessor.js +28 -28
- package/dist/leader-thread/LeaderSyncProcessor.js.map +1 -1
- package/dist/leader-thread/eventlog.d.ts +13 -13
- package/dist/leader-thread/eventlog.d.ts.map +1 -1
- package/dist/leader-thread/eventlog.js +12 -11
- package/dist/leader-thread/eventlog.js.map +1 -1
- package/dist/leader-thread/leader-worker-devtools.d.ts +2 -2
- package/dist/leader-thread/leader-worker-devtools.d.ts.map +1 -1
- package/dist/leader-thread/leader-worker-devtools.js +3 -3
- package/dist/leader-thread/leader-worker-devtools.js.map +1 -1
- package/dist/leader-thread/make-leader-thread-layer.d.ts +2 -2
- package/dist/leader-thread/make-leader-thread-layer.d.ts.map +1 -1
- package/dist/leader-thread/make-leader-thread-layer.js +10 -8
- package/dist/leader-thread/make-leader-thread-layer.js.map +1 -1
- package/dist/leader-thread/materialize-event.d.ts +1 -1
- package/dist/leader-thread/materialize-event.d.ts.map +1 -1
- package/dist/leader-thread/materialize-event.js +2 -2
- package/dist/leader-thread/materialize-event.js.map +1 -1
- package/dist/leader-thread/recreate-db.d.ts +2 -2
- package/dist/leader-thread/recreate-db.d.ts.map +1 -1
- package/dist/leader-thread/recreate-db.js +5 -5
- package/dist/leader-thread/recreate-db.js.map +1 -1
- package/dist/leader-thread/shutdown-channel.d.ts +2 -2
- package/dist/leader-thread/shutdown-channel.d.ts.map +1 -1
- package/dist/leader-thread/shutdown-channel.js +2 -2
- package/dist/leader-thread/shutdown-channel.js.map +1 -1
- package/dist/leader-thread/types.d.ts +15 -15
- package/dist/leader-thread/types.d.ts.map +1 -1
- package/dist/make-client-session.d.ts +2 -2
- package/dist/make-client-session.d.ts.map +1 -1
- package/dist/materializer-helper.d.ts +5 -5
- package/dist/materializer-helper.d.ts.map +1 -1
- package/dist/materializer-helper.js.map +1 -1
- package/dist/rematerialize-from-eventlog.d.ts +2 -2
- package/dist/rematerialize-from-eventlog.d.ts.map +1 -1
- package/dist/rematerialize-from-eventlog.js +6 -6
- package/dist/rematerialize-from-eventlog.js.map +1 -1
- package/dist/schema/EventDef/define.d.ts +147 -0
- package/dist/schema/EventDef/define.d.ts.map +1 -0
- package/dist/schema/EventDef/define.js +139 -0
- package/dist/schema/EventDef/define.js.map +1 -0
- package/dist/schema/EventDef/event-def.d.ts +106 -0
- package/dist/schema/EventDef/event-def.d.ts.map +1 -0
- package/dist/schema/EventDef/event-def.js +2 -0
- package/dist/schema/EventDef/event-def.js.map +1 -0
- package/dist/schema/EventDef/facts.d.ts +118 -0
- package/dist/schema/EventDef/facts.d.ts.map +1 -0
- package/dist/schema/EventDef/facts.js +53 -0
- package/dist/schema/EventDef/facts.js.map +1 -0
- package/dist/schema/EventDef/materializer.d.ts +155 -0
- package/dist/schema/EventDef/materializer.d.ts.map +1 -0
- package/dist/schema/EventDef/materializer.js +83 -0
- package/dist/schema/EventDef/materializer.js.map +1 -0
- package/dist/schema/EventDef/mod.d.ts +5 -0
- package/dist/schema/EventDef/mod.d.ts.map +1 -0
- package/dist/schema/EventDef/mod.js +5 -0
- package/dist/schema/EventDef/mod.js.map +1 -0
- package/dist/schema/EventSequenceNumber/client.d.ts +136 -0
- package/dist/schema/EventSequenceNumber/client.d.ts.map +1 -0
- package/dist/schema/{EventSequenceNumber.js → EventSequenceNumber/client.js} +86 -39
- package/dist/schema/EventSequenceNumber/client.js.map +1 -0
- package/dist/schema/EventSequenceNumber/global.d.ts +15 -0
- package/dist/schema/EventSequenceNumber/global.d.ts.map +1 -0
- package/dist/schema/EventSequenceNumber/global.js +14 -0
- package/dist/schema/EventSequenceNumber/global.js.map +1 -0
- package/dist/schema/EventSequenceNumber/mod.d.ts +37 -0
- package/dist/schema/EventSequenceNumber/mod.d.ts.map +1 -0
- package/dist/schema/EventSequenceNumber/mod.js +37 -0
- package/dist/schema/EventSequenceNumber/mod.js.map +1 -0
- package/dist/schema/EventSequenceNumber.test.js +41 -41
- package/dist/schema/EventSequenceNumber.test.js.map +1 -1
- package/dist/schema/{LiveStoreEvent.d.ts → LiveStoreEvent/client.d.ts} +84 -101
- package/dist/schema/LiveStoreEvent/client.d.ts.map +1 -0
- package/dist/schema/{LiveStoreEvent.js → LiveStoreEvent/client.js} +69 -52
- package/dist/schema/LiveStoreEvent/client.js.map +1 -0
- package/dist/schema/LiveStoreEvent/for-event-def.d.ts +52 -0
- package/dist/schema/LiveStoreEvent/for-event-def.d.ts.map +1 -0
- package/dist/schema/LiveStoreEvent/for-event-def.js +2 -0
- package/dist/schema/LiveStoreEvent/for-event-def.js.map +1 -0
- package/dist/schema/LiveStoreEvent/global.d.ts +36 -0
- package/dist/schema/LiveStoreEvent/global.d.ts.map +1 -0
- package/dist/schema/LiveStoreEvent/global.js +31 -0
- package/dist/schema/LiveStoreEvent/global.js.map +1 -0
- package/dist/schema/LiveStoreEvent/input.d.ts +46 -0
- package/dist/schema/LiveStoreEvent/input.d.ts.map +1 -0
- package/dist/schema/LiveStoreEvent/input.js +26 -0
- package/dist/schema/LiveStoreEvent/input.js.map +1 -0
- package/dist/schema/LiveStoreEvent/mod.d.ts +5 -0
- package/dist/schema/LiveStoreEvent/mod.d.ts.map +1 -0
- package/dist/schema/LiveStoreEvent/mod.js +5 -0
- package/dist/schema/LiveStoreEvent/mod.js.map +1 -0
- package/dist/schema/events.d.ts +1 -1
- package/dist/schema/events.d.ts.map +1 -1
- package/dist/schema/events.js +1 -1
- package/dist/schema/events.js.map +1 -1
- package/dist/schema/mod.d.ts +3 -3
- package/dist/schema/mod.d.ts.map +1 -1
- package/dist/schema/mod.js +3 -3
- package/dist/schema/mod.js.map +1 -1
- package/dist/schema/schema.d.ts +1 -1
- package/dist/schema/schema.d.ts.map +1 -1
- package/dist/schema/state/sqlite/client-document-def.d.ts +1 -1
- package/dist/schema/state/sqlite/client-document-def.d.ts.map +1 -1
- package/dist/schema/state/sqlite/client-document-def.js +2 -2
- package/dist/schema/state/sqlite/client-document-def.js.map +1 -1
- package/dist/schema/state/sqlite/client-document-def.test.js.map +1 -1
- package/dist/schema/state/sqlite/column-def.js +60 -28
- package/dist/schema/state/sqlite/column-def.js.map +1 -1
- package/dist/schema/state/sqlite/column-def.test.js +12 -1
- package/dist/schema/state/sqlite/column-def.test.js.map +1 -1
- package/dist/schema/state/sqlite/mod.d.ts +2 -2
- package/dist/schema/state/sqlite/mod.d.ts.map +1 -1
- package/dist/schema/state/sqlite/mod.js +1 -1
- package/dist/schema/state/sqlite/mod.js.map +1 -1
- package/dist/schema/state/sqlite/query-builder/api.d.ts +12 -8
- package/dist/schema/state/sqlite/query-builder/api.d.ts.map +1 -1
- package/dist/schema/state/sqlite/query-builder/astToSql.d.ts.map +1 -1
- package/dist/schema/state/sqlite/query-builder/astToSql.js +18 -11
- package/dist/schema/state/sqlite/query-builder/astToSql.js.map +1 -1
- package/dist/schema/state/sqlite/query-builder/impl.d.ts.map +1 -1
- package/dist/schema/state/sqlite/query-builder/impl.js +0 -1
- package/dist/schema/state/sqlite/query-builder/impl.js.map +1 -1
- package/dist/schema/state/sqlite/query-builder/impl.test.js +119 -90
- package/dist/schema/state/sqlite/query-builder/impl.test.js.map +1 -1
- package/dist/schema/state/sqlite/system-tables/eventlog-tables.js +5 -5
- package/dist/schema/state/sqlite/system-tables/eventlog-tables.js.map +1 -1
- package/dist/schema/state/sqlite/system-tables/state-tables.js +3 -3
- package/dist/schema/state/sqlite/system-tables/state-tables.js.map +1 -1
- package/dist/schema/unknown-events.d.ts +3 -3
- package/dist/schema/unknown-events.d.ts.map +1 -1
- package/dist/schema-management/migrations.d.ts +2 -2
- package/dist/schema-management/migrations.d.ts.map +1 -1
- package/dist/schema-management/migrations.js.map +1 -1
- package/dist/schema-management/validate-schema.d.ts +3 -3
- package/dist/schema-management/validate-schema.d.ts.map +1 -1
- package/dist/schema-management/validate-schema.js +2 -2
- package/dist/schema-management/validate-schema.js.map +1 -1
- package/dist/sqlite-types.d.ts +3 -3
- package/dist/sqlite-types.d.ts.map +1 -1
- package/dist/sync/ClientSessionSyncProcessor.d.ts +5 -5
- package/dist/sync/ClientSessionSyncProcessor.d.ts.map +1 -1
- package/dist/sync/ClientSessionSyncProcessor.js +12 -12
- package/dist/sync/ClientSessionSyncProcessor.js.map +1 -1
- package/dist/sync/errors.d.ts +9 -4
- package/dist/sync/errors.d.ts.map +1 -1
- package/dist/sync/errors.js +6 -6
- package/dist/sync/errors.js.map +1 -1
- package/dist/sync/mock-sync-backend.d.ts +6 -6
- package/dist/sync/mock-sync-backend.d.ts.map +1 -1
- package/dist/sync/mock-sync-backend.js +4 -4
- package/dist/sync/mock-sync-backend.js.map +1 -1
- package/dist/sync/next/compact-events.js +2 -2
- package/dist/sync/next/compact-events.js.map +1 -1
- package/dist/sync/next/facts.d.ts +5 -5
- package/dist/sync/next/facts.d.ts.map +1 -1
- package/dist/sync/next/facts.js.map +1 -1
- package/dist/sync/next/history-dag-common.d.ts +5 -5
- package/dist/sync/next/history-dag-common.d.ts.map +1 -1
- package/dist/sync/next/history-dag-common.js +5 -5
- package/dist/sync/next/history-dag-common.js.map +1 -1
- package/dist/sync/next/history-dag.d.ts.map +1 -1
- package/dist/sync/next/history-dag.js +8 -8
- package/dist/sync/next/history-dag.js.map +1 -1
- package/dist/sync/next/rebase-events.d.ts +5 -5
- package/dist/sync/next/rebase-events.d.ts.map +1 -1
- package/dist/sync/next/rebase-events.js +5 -5
- package/dist/sync/next/rebase-events.js.map +1 -1
- package/dist/sync/next/test/event-fixtures.d.ts +2 -2
- package/dist/sync/next/test/event-fixtures.d.ts.map +1 -1
- package/dist/sync/next/test/event-fixtures.js +9 -9
- package/dist/sync/next/test/event-fixtures.js.map +1 -1
- package/dist/sync/sync-backend-kv.d.ts +3 -3
- package/dist/sync/sync-backend-kv.d.ts.map +1 -1
- package/dist/sync/sync-backend-kv.js +3 -3
- package/dist/sync/sync-backend-kv.js.map +1 -1
- package/dist/sync/sync-backend.d.ts +9 -9
- package/dist/sync/sync-backend.d.ts.map +1 -1
- package/dist/sync/syncstate.d.ts +55 -42
- package/dist/sync/syncstate.d.ts.map +1 -1
- package/dist/sync/syncstate.js +42 -42
- package/dist/sync/syncstate.js.map +1 -1
- package/dist/sync/syncstate.test.js +40 -40
- package/dist/sync/syncstate.test.js.map +1 -1
- package/dist/sync/validate-push-payload.d.ts +1 -1
- package/dist/sync/validate-push-payload.d.ts.map +1 -1
- package/dist/sync/validate-push-payload.js +2 -2
- package/dist/sync/validate-push-payload.js.map +1 -1
- package/dist/testing/event-factory.d.ts +3 -3
- package/dist/testing/event-factory.d.ts.map +1 -1
- package/dist/testing/event-factory.js +5 -7
- package/dist/testing/event-factory.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +4 -4
- package/src/ClientSessionLeaderThreadProxy.ts +10 -10
- package/src/adapter-types.ts +5 -5
- package/src/devtools/devtools-messages-client-session.ts +2 -2
- package/src/devtools/devtools-messages-leader.ts +8 -8
- package/src/errors.ts +11 -13
- package/src/leader-thread/LeaderSyncProcessor.ts +54 -56
- package/src/leader-thread/eventlog.ts +21 -26
- package/src/leader-thread/leader-worker-devtools.ts +3 -3
- package/src/leader-thread/make-leader-thread-layer.ts +12 -10
- package/src/leader-thread/materialize-event.ts +3 -3
- package/src/leader-thread/recreate-db.ts +6 -6
- package/src/leader-thread/shutdown-channel.ts +2 -2
- package/src/leader-thread/types.ts +15 -15
- package/src/make-client-session.ts +2 -2
- package/src/materializer-helper.ts +5 -5
- package/src/rematerialize-from-eventlog.ts +6 -6
- package/src/schema/EventDef/define.ts +201 -0
- package/src/schema/EventDef/event-def.ts +120 -0
- package/src/schema/EventDef/facts.ts +135 -0
- package/src/schema/EventDef/materializer.ts +172 -0
- package/src/schema/EventDef/mod.ts +4 -0
- package/src/schema/EventSequenceNumber/client.ts +257 -0
- package/src/schema/EventSequenceNumber/global.ts +19 -0
- package/src/schema/EventSequenceNumber/mod.ts +37 -0
- package/src/schema/EventSequenceNumber.test.ts +68 -50
- package/src/schema/LiveStoreEvent/client.ts +221 -0
- package/src/schema/LiveStoreEvent/for-event-def.ts +60 -0
- package/src/schema/LiveStoreEvent/global.ts +45 -0
- package/src/schema/LiveStoreEvent/input.ts +63 -0
- package/src/schema/LiveStoreEvent/mod.ts +4 -0
- package/src/schema/events.ts +1 -1
- package/src/schema/mod.ts +3 -3
- package/src/schema/schema.ts +1 -1
- package/src/schema/state/sqlite/client-document-def.test.ts +2 -2
- package/src/schema/state/sqlite/client-document-def.ts +3 -3
- package/src/schema/state/sqlite/column-def.test.ts +18 -1
- package/src/schema/state/sqlite/column-def.ts +73 -30
- package/src/schema/state/sqlite/mod.ts +2 -2
- package/src/schema/state/sqlite/query-builder/api.ts +12 -8
- package/src/schema/state/sqlite/query-builder/astToSql.ts +20 -11
- package/src/schema/state/sqlite/query-builder/impl.test.ts +122 -90
- package/src/schema/state/sqlite/query-builder/impl.ts +0 -1
- package/src/schema/state/sqlite/system-tables/eventlog-tables.ts +5 -5
- package/src/schema/state/sqlite/system-tables/state-tables.ts +3 -3
- package/src/schema/unknown-events.ts +3 -3
- package/src/schema-management/migrations.ts +2 -2
- package/src/schema-management/validate-schema.ts +3 -3
- package/src/sqlite-types.ts +3 -3
- package/src/sync/ClientSessionSyncProcessor.ts +17 -17
- package/src/sync/errors.ts +6 -6
- package/src/sync/mock-sync-backend.ts +16 -16
- package/src/sync/next/compact-events.ts +2 -2
- package/src/sync/next/facts.ts +6 -6
- package/src/sync/next/history-dag-common.ts +8 -8
- package/src/sync/next/history-dag.ts +14 -10
- package/src/sync/next/rebase-events.ts +11 -11
- package/src/sync/next/test/event-fixtures.ts +11 -11
- package/src/sync/sync-backend-kv.ts +3 -3
- package/src/sync/sync-backend.ts +9 -9
- package/src/sync/syncstate.test.ts +46 -46
- package/src/sync/syncstate.ts +59 -55
- package/src/sync/validate-push-payload.ts +4 -4
- package/src/testing/event-factory.ts +10 -12
- package/src/version.ts +1 -1
- package/dist/schema/EventDef.d.ts +0 -126
- package/dist/schema/EventDef.d.ts.map +0 -1
- package/dist/schema/EventDef.js +0 -46
- package/dist/schema/EventDef.js.map +0 -1
- package/dist/schema/EventSequenceNumber.d.ts +0 -89
- package/dist/schema/EventSequenceNumber.d.ts.map +0 -1
- package/dist/schema/EventSequenceNumber.js.map +0 -1
- package/dist/schema/LiveStoreEvent.d.ts.map +0 -1
- package/dist/schema/LiveStoreEvent.js.map +0 -1
- package/src/schema/EventDef.ts +0 -222
- package/src/schema/EventSequenceNumber.ts +0 -208
- package/src/schema/LiveStoreEvent.ts +0 -286
|
@@ -278,12 +278,29 @@ describe('getColumnDefForSchema', () => {
|
|
|
278
278
|
expect(State.SQLite.getColumnDefForSchema(StatusEnum).columnType).toBe('text')
|
|
279
279
|
expect(State.SQLite.getColumnDefForSchema(StatusUnion).columnType).toBe('text')
|
|
280
280
|
})
|
|
281
|
+
|
|
282
|
+
it('should handle unions of numeric literals as integer column', () => {
|
|
283
|
+
const IntervalSchema = Schema.Literal(1, 5, 15, 30)
|
|
284
|
+
|
|
285
|
+
const columnDef = State.SQLite.getColumnDefForSchema(IntervalSchema)
|
|
286
|
+
|
|
287
|
+
expect(columnDef.columnType).toBe('integer')
|
|
288
|
+
})
|
|
289
|
+
|
|
290
|
+
it('should handle unions of non-integer numeric literals as real column', () => {
|
|
291
|
+
const PercentSchema = Schema.Literal(0.1, 0.2, 0.25)
|
|
292
|
+
|
|
293
|
+
const columnDef = State.SQLite.getColumnDefForSchema(PercentSchema)
|
|
294
|
+
|
|
295
|
+
expect(columnDef.columnType).toBe('real')
|
|
296
|
+
})
|
|
281
297
|
})
|
|
282
298
|
|
|
283
299
|
describe('binary data', () => {
|
|
284
300
|
it('should handle Uint8Array as blob column', () => {
|
|
285
301
|
const columnDef = State.SQLite.getColumnDefForSchema(Schema.Uint8Array)
|
|
286
|
-
expect(columnDef.columnType).toBe('
|
|
302
|
+
expect(columnDef.columnType).toBe('blob')
|
|
303
|
+
expect(columnDef.schema.toString()).toBe('Uint8ArrayFromSelf')
|
|
287
304
|
})
|
|
288
305
|
})
|
|
289
306
|
|
|
@@ -179,33 +179,16 @@ const getColumnForSchema = (schema: Schema.Schema.AnyNoContext, nullable = false
|
|
|
179
179
|
return SqliteDsl.real({ schema: coreSchema, nullable })
|
|
180
180
|
}
|
|
181
181
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
const value = coreAst.literal
|
|
185
|
-
if (typeof value === 'boolean') return SqliteDsl.boolean({ nullable })
|
|
182
|
+
if (isUint8ArraySchema(coreAst) || isUint8ArraySchema(encodedAst)) {
|
|
183
|
+
return SqliteDsl.blob({ schema: Schema.Uint8ArrayFromSelf as Schema.Schema<Uint8Array<ArrayBuffer>>, nullable })
|
|
186
184
|
}
|
|
187
185
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// Literals based on their encoded type
|
|
193
|
-
if (SchemaAST.isLiteral(encodedAst)) {
|
|
194
|
-
const value = encodedAst.literal
|
|
195
|
-
if (typeof value === 'string') return SqliteDsl.text({ schema: coreSchema, nullable })
|
|
196
|
-
if (typeof value === 'number') {
|
|
197
|
-
// Check if the original schema is Int
|
|
198
|
-
const id = SchemaAST.getIdentifierAnnotation(coreAst).pipe(Option.getOrElse(() => ''))
|
|
199
|
-
if (id === 'Int') {
|
|
200
|
-
return SqliteDsl.integer({ schema: coreSchema, nullable })
|
|
201
|
-
}
|
|
202
|
-
return SqliteDsl.real({ schema: coreSchema, nullable })
|
|
203
|
-
}
|
|
204
|
-
}
|
|
186
|
+
const literalColumn = getLiteralColumnDefinition(encodedAst, coreSchema, nullable, coreAst)
|
|
187
|
+
if (literalColumn) return literalColumn
|
|
205
188
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
189
|
+
// Fallback to checking the original AST in case the encoded schema differs
|
|
190
|
+
const coreLiteralColumn = getLiteralColumnDefinition(coreAst, coreSchema, nullable, coreAst)
|
|
191
|
+
if (coreLiteralColumn) return coreLiteralColumn
|
|
209
192
|
|
|
210
193
|
// Everything else needs JSON encoding
|
|
211
194
|
return SqliteDsl.json({ schema: coreSchema, nullable })
|
|
@@ -230,10 +213,70 @@ const stripNullable = (ast: SchemaAST.AST): SchemaAST.AST => {
|
|
|
230
213
|
return SchemaAST.Union.make(coreTypes, ast.annotations)
|
|
231
214
|
}
|
|
232
215
|
|
|
233
|
-
const
|
|
216
|
+
const getLiteralColumnDefinition = (
|
|
234
217
|
ast: SchemaAST.AST,
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
SchemaAST.
|
|
238
|
-
|
|
239
|
-
|
|
218
|
+
schema: Schema.Schema.AnyNoContext,
|
|
219
|
+
nullable: boolean,
|
|
220
|
+
sourceAst: SchemaAST.AST,
|
|
221
|
+
): SqliteDsl.ColumnDefinition.Any | null => {
|
|
222
|
+
const literalValues = extractLiteralValues(ast)
|
|
223
|
+
if (!literalValues) return null
|
|
224
|
+
|
|
225
|
+
const literalType = getLiteralValueType(literalValues)
|
|
226
|
+
switch (literalType) {
|
|
227
|
+
case 'string':
|
|
228
|
+
return SqliteDsl.text({ schema, nullable })
|
|
229
|
+
case 'number': {
|
|
230
|
+
const id = SchemaAST.getIdentifierAnnotation(sourceAst).pipe(Option.getOrElse(() => ''))
|
|
231
|
+
if (id === 'Int' || id === 'DateFromNumber') {
|
|
232
|
+
return SqliteDsl.integer({ schema, nullable })
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const useIntegerColumn =
|
|
236
|
+
literalValues.length > 1 && literalValues.every((value) => typeof value === 'number' && Number.isInteger(value))
|
|
237
|
+
|
|
238
|
+
return useIntegerColumn ? SqliteDsl.integer({ schema, nullable }) : SqliteDsl.real({ schema, nullable })
|
|
239
|
+
}
|
|
240
|
+
case 'boolean':
|
|
241
|
+
return SqliteDsl.boolean({ nullable })
|
|
242
|
+
case 'bigint':
|
|
243
|
+
return SqliteDsl.integer({ schema, nullable })
|
|
244
|
+
default:
|
|
245
|
+
return null
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const extractLiteralValues = (ast: SchemaAST.AST): ReadonlyArray<SchemaAST.LiteralValue> | null => {
|
|
250
|
+
if (SchemaAST.isLiteral(ast)) return [ast.literal]
|
|
251
|
+
|
|
252
|
+
if (SchemaAST.isUnion(ast) && ast.types.length > 0 && ast.types.every((type) => SchemaAST.isLiteral(type))) {
|
|
253
|
+
return ast.types.map((type) => (type as SchemaAST.Literal).literal)
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return null
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const getLiteralValueType = (
|
|
260
|
+
literals: ReadonlyArray<SchemaAST.LiteralValue>,
|
|
261
|
+
): 'string' | 'number' | 'boolean' | 'bigint' | null => {
|
|
262
|
+
const literalTypes = new Set(literals.map((value) => (value === null ? 'null' : typeof value)))
|
|
263
|
+
if (literalTypes.size !== 1) return null
|
|
264
|
+
|
|
265
|
+
const [literalType] = literalTypes
|
|
266
|
+
return literalType === 'string' || literalType === 'number' || literalType === 'boolean' || literalType === 'bigint'
|
|
267
|
+
? literalType
|
|
268
|
+
: null
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const isUint8ArraySchema = (ast: SchemaAST.AST): boolean => {
|
|
272
|
+
const identifier = SchemaAST.getIdentifierAnnotation(ast)
|
|
273
|
+
if (Option.isSome(identifier) && identifier.value.includes('Uint8Array')) {
|
|
274
|
+
return true
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (SchemaAST.isTupleType(ast)) {
|
|
278
|
+
return ast.elements.length === 0 && ast.rest.length === 1 && SchemaAST.isNumberKeyword(ast.rest[0]!.type)
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
return false
|
|
282
|
+
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { shouldNeverHappen } from '@livestore/utils'
|
|
2
2
|
|
|
3
3
|
import type { MigrationOptions } from '../../../adapter-types.ts'
|
|
4
|
-
import type { Materializer } from '../../EventDef.ts'
|
|
4
|
+
import type { Materializer } from '../../EventDef/mod.ts'
|
|
5
5
|
import type { InternalState } from '../../schema.ts'
|
|
6
6
|
import { ClientDocumentTableDefSymbol, tableIsClientDocumentTable } from './client-document-def.ts'
|
|
7
7
|
import { SqliteAst } from './db-schema/mod.ts'
|
|
8
8
|
import { stateSystemTables } from './system-tables/state-tables.ts'
|
|
9
9
|
import type { TableDef, TableDefBase } from './table-def.ts'
|
|
10
10
|
|
|
11
|
-
export * from '../../EventDef.ts'
|
|
11
|
+
export * from '../../EventDef/mod.ts'
|
|
12
12
|
export {
|
|
13
13
|
type ClientDocumentTableDef,
|
|
14
14
|
ClientDocumentTableDefSymbol,
|
|
@@ -347,22 +347,26 @@ export namespace QueryBuilder {
|
|
|
347
347
|
>
|
|
348
348
|
|
|
349
349
|
/**
|
|
350
|
-
*
|
|
350
|
+
* Upsert: insert a row, or handle conflicts on existing rows.
|
|
351
|
+
* Equivalent to SQLite's `INSERT ... ON CONFLICT` clause.
|
|
352
|
+
*
|
|
353
|
+
* Actions:
|
|
354
|
+
* - `'ignore'`: Skip the insert if a row with the same key exists
|
|
355
|
+
* - `'replace'`: Delete the existing row and insert the new one
|
|
356
|
+
* - `'update'`: Update specific columns on the existing row
|
|
357
|
+
*
|
|
351
358
|
* ```ts
|
|
359
|
+
* // Ignore: skip if row exists
|
|
352
360
|
* db.todos.insert({ id: '123', text: 'Buy milk', status: 'active' }).onConflict('id', 'ignore')
|
|
353
|
-
* ```
|
|
354
361
|
*
|
|
355
|
-
*
|
|
356
|
-
* ```ts
|
|
362
|
+
* // Replace: delete existing row and insert new one
|
|
357
363
|
* db.todos.insert({ id: '123', text: 'Buy milk', status: 'active' }).onConflict('id', 'replace')
|
|
358
|
-
* ```
|
|
359
364
|
*
|
|
360
|
-
*
|
|
361
|
-
* ```ts
|
|
365
|
+
* // Update: merge specific columns into existing row
|
|
362
366
|
* db.todos.insert({ id: '123', text: 'Buy milk', status: 'active' }).onConflict('id', 'update', { text: 'Buy soy milk' })
|
|
363
367
|
* ```
|
|
364
368
|
*
|
|
365
|
-
* NOTE
|
|
369
|
+
* NOTE: Composite primary keys are not yet supported.
|
|
366
370
|
*/
|
|
367
371
|
readonly onConflict: {
|
|
368
372
|
<TTarget extends SingleOrReadonlyArray<keyof TTableDef['sqliteDef']['columns']>>(
|
|
@@ -7,6 +7,8 @@ import type { State } from '../../../mod.ts'
|
|
|
7
7
|
import type { QueryBuilderAst } from './api.ts'
|
|
8
8
|
|
|
9
9
|
// Helper functions for SQL generation
|
|
10
|
+
const quoteIdentifier = (identifier: string): string => `"${identifier.replace(/"/g, '""')}"`
|
|
11
|
+
|
|
10
12
|
const formatWhereClause = (
|
|
11
13
|
whereConditions: ReadonlyArray<QueryBuilderAst.Where>,
|
|
12
14
|
tableDef: State.SQLite.TableDefBase,
|
|
@@ -16,13 +18,15 @@ const formatWhereClause = (
|
|
|
16
18
|
|
|
17
19
|
const whereClause = whereConditions
|
|
18
20
|
.map(({ col, op, value }) => {
|
|
21
|
+
const quotedCol = quoteIdentifier(col)
|
|
22
|
+
|
|
19
23
|
// Handle NULL values
|
|
20
24
|
if (value === null) {
|
|
21
25
|
if (op !== '=' && op !== '!=') {
|
|
22
26
|
throw new Error(`Unsupported operator for NULL value: ${op}`)
|
|
23
27
|
}
|
|
24
28
|
const opStmt = op === '=' ? 'IS' : 'IS NOT'
|
|
25
|
-
return `${
|
|
29
|
+
return `${quotedCol} ${opStmt} NULL`
|
|
26
30
|
}
|
|
27
31
|
|
|
28
32
|
// Get column definition and encode value
|
|
@@ -48,11 +52,11 @@ const formatWhereClause = (
|
|
|
48
52
|
const encodedValues = value.map((v) => Schema.encodeSync(colDef.schema)(v)) as SqlValue[]
|
|
49
53
|
bindValues.push(...encodedValues)
|
|
50
54
|
const placeholders = encodedValues.map(() => '?').join(', ')
|
|
51
|
-
return `${
|
|
55
|
+
return `${quotedCol} ${op} (${placeholders})`
|
|
52
56
|
} else {
|
|
53
57
|
const encodedValue = Schema.encodeSync(colDef.schema)(value)
|
|
54
58
|
bindValues.push(encodedValue as SqlValue)
|
|
55
|
-
return `${
|
|
59
|
+
return `${quotedCol} ${op} ?`
|
|
56
60
|
}
|
|
57
61
|
})
|
|
58
62
|
.join(' AND ')
|
|
@@ -62,7 +66,7 @@ const formatWhereClause = (
|
|
|
62
66
|
|
|
63
67
|
const formatReturningClause = (returning?: string[]): string => {
|
|
64
68
|
if (!returning || returning.length === 0) return ''
|
|
65
|
-
return ` RETURNING ${returning.join(', ')}`
|
|
69
|
+
return ` RETURNING ${returning.map(quoteIdentifier).join(', ')}`
|
|
66
70
|
}
|
|
67
71
|
|
|
68
72
|
export const astToSql = (ast: QueryBuilderAst): { query: string; bindValues: SqlValue[]; usedTables: Set<string> } => {
|
|
@@ -72,6 +76,7 @@ export const astToSql = (ast: QueryBuilderAst): { query: string; bindValues: Sql
|
|
|
72
76
|
// INSERT query
|
|
73
77
|
if (ast._tag === 'InsertQuery') {
|
|
74
78
|
const columns = Object.keys(ast.values)
|
|
79
|
+
const quotedColumns = columns.map(quoteIdentifier)
|
|
75
80
|
const placeholders = columns.map(() => '?').join(', ')
|
|
76
81
|
const encodedValues = Schema.encodeSync(ast.tableDef.insertSchema)(ast.values)
|
|
77
82
|
|
|
@@ -91,7 +96,8 @@ export const astToSql = (ast: QueryBuilderAst): { query: string; bindValues: Sql
|
|
|
91
96
|
// For REPLACE, the conflict target is implied and no further clause is needed
|
|
92
97
|
} else {
|
|
93
98
|
// Build the ON CONFLICT clause for IGNORE or UPDATE
|
|
94
|
-
|
|
99
|
+
const conflictTargets = ast.onConflict.targets.map(quoteIdentifier).join(', ')
|
|
100
|
+
conflictClause = ` ON CONFLICT (${conflictTargets}) `
|
|
95
101
|
if (ast.onConflict.action._tag === 'ignore') {
|
|
96
102
|
conflictClause += 'DO NOTHING'
|
|
97
103
|
} else {
|
|
@@ -105,8 +111,9 @@ export const astToSql = (ast: QueryBuilderAst): { query: string; bindValues: Sql
|
|
|
105
111
|
const updates = updateCols
|
|
106
112
|
.map((col) => {
|
|
107
113
|
const value = updateValues[col]
|
|
114
|
+
const quotedCol = quoteIdentifier(col)
|
|
108
115
|
// If the value is undefined, use excluded.col
|
|
109
|
-
return value === undefined ? `${
|
|
116
|
+
return value === undefined ? `${quotedCol} = excluded.${quotedCol}` : `${quotedCol} = ?`
|
|
110
117
|
})
|
|
111
118
|
.join(', ')
|
|
112
119
|
|
|
@@ -129,7 +136,7 @@ export const astToSql = (ast: QueryBuilderAst): { query: string; bindValues: Sql
|
|
|
129
136
|
}
|
|
130
137
|
|
|
131
138
|
// Construct the main query part
|
|
132
|
-
let query = `${insertVerb} INTO '${ast.tableDef.sqliteDef.name}' (${
|
|
139
|
+
let query = `${insertVerb} INTO '${ast.tableDef.sqliteDef.name}' (${quotedColumns.join(', ')}) VALUES (${placeholders})`
|
|
133
140
|
|
|
134
141
|
// Append the conflict clause if it was generated (i.e., not for REPLACE)
|
|
135
142
|
query += conflictClause
|
|
@@ -157,7 +164,9 @@ export const astToSql = (ast: QueryBuilderAst): { query: string; bindValues: Sql
|
|
|
157
164
|
bindValues.push(encodedValues[col] as SqlValue)
|
|
158
165
|
})
|
|
159
166
|
|
|
160
|
-
let query = `UPDATE '${ast.tableDef.sqliteDef.name}' SET ${setColumns
|
|
167
|
+
let query = `UPDATE '${ast.tableDef.sqliteDef.name}' SET ${setColumns
|
|
168
|
+
.map((col) => `${quoteIdentifier(col)} = ?`)
|
|
169
|
+
.join(', ')}`
|
|
161
170
|
|
|
162
171
|
const whereClause = formatWhereClause(ast.where, ast.tableDef, bindValues)
|
|
163
172
|
if (whereClause) query += ` ${whereClause}`
|
|
@@ -201,21 +210,21 @@ export const astToSql = (ast: QueryBuilderAst): { query: string; bindValues: Sql
|
|
|
201
210
|
const encodedId = ast.id === SessionIdSymbol ? ast.id : Schema.encodeSync(idColDef.schema)(ast.id)
|
|
202
211
|
|
|
203
212
|
return {
|
|
204
|
-
query: `SELECT * FROM '${ast.tableDef.sqliteDef.name}' WHERE id = ?`,
|
|
213
|
+
query: `SELECT * FROM '${ast.tableDef.sqliteDef.name}' WHERE ${quoteIdentifier('id')} = ?`,
|
|
205
214
|
bindValues: [encodedId as SqlValue],
|
|
206
215
|
usedTables,
|
|
207
216
|
}
|
|
208
217
|
}
|
|
209
218
|
|
|
210
219
|
// SELECT query
|
|
211
|
-
const columnsStmt = ast.select.columns.length === 0 ? '*' : ast.select.columns.join(', ')
|
|
220
|
+
const columnsStmt = ast.select.columns.length === 0 ? '*' : ast.select.columns.map(quoteIdentifier).join(', ')
|
|
212
221
|
const selectStmt = `SELECT ${columnsStmt}`
|
|
213
222
|
const fromStmt = `FROM '${ast.tableDef.sqliteDef.name}'`
|
|
214
223
|
const whereStmt = formatWhereClause(ast.where, ast.tableDef, bindValues)
|
|
215
224
|
|
|
216
225
|
const orderByStmt =
|
|
217
226
|
ast.orderBy.length > 0
|
|
218
|
-
? `ORDER BY ${ast.orderBy.map(({ col, direction }) => `${col} ${direction}`).join(', ')}`
|
|
227
|
+
? `ORDER BY ${ast.orderBy.map(({ col, direction }) => `${quoteIdentifier(col)} ${direction}`).join(', ')}`
|
|
219
228
|
: ''
|
|
220
229
|
|
|
221
230
|
const limitStmt = ast.limit._tag === 'Some' ? `LIMIT ?` : ''
|