@livestore/common 0.4.0-dev.2 → 0.4.0-dev.20
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 +17 -12
- package/dist/ClientSessionLeaderThreadProxy.d.ts.map +1 -1
- package/dist/ClientSessionLeaderThreadProxy.js.map +1 -1
- package/dist/adapter-types.d.ts +14 -6
- package/dist/adapter-types.d.ts.map +1 -1
- package/dist/adapter-types.js.map +1 -1
- package/dist/debug-info.d.ts.map +1 -1
- package/dist/debug-info.js +33 -6
- package/dist/debug-info.js.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 +7 -14
- package/dist/devtools/devtools-messages-common.d.ts.map +1 -1
- package/dist/devtools/devtools-messages-common.js +1 -6
- package/dist/devtools/devtools-messages-common.js.map +1 -1
- package/dist/devtools/devtools-messages-leader.d.ts +36 -29
- 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/devtools/devtools-sessioninfo.d.ts +14 -2
- package/dist/devtools/devtools-sessioninfo.d.ts.map +1 -1
- package/dist/devtools/devtools-sessioninfo.js +7 -4
- package/dist/devtools/devtools-sessioninfo.js.map +1 -1
- package/dist/devtools/mod.d.ts +13 -2
- package/dist/devtools/mod.d.ts.map +1 -1
- package/dist/devtools/mod.js +10 -3
- package/dist/devtools/mod.js.map +1 -1
- package/dist/errors.d.ts +52 -10
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +25 -6
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/leader-thread/LeaderSyncProcessor.d.ts +8 -4
- package/dist/leader-thread/LeaderSyncProcessor.d.ts.map +1 -1
- package/dist/leader-thread/LeaderSyncProcessor.js +156 -73
- package/dist/leader-thread/LeaderSyncProcessor.js.map +1 -1
- package/dist/leader-thread/eventlog.d.ts +15 -21
- package/dist/leader-thread/eventlog.d.ts.map +1 -1
- package/dist/leader-thread/eventlog.js +18 -18
- 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 +30 -42
- package/dist/leader-thread/leader-worker-devtools.js.map +1 -1
- package/dist/leader-thread/make-leader-thread-layer.d.ts +6 -6
- package/dist/leader-thread/make-leader-thread-layer.d.ts.map +1 -1
- package/dist/leader-thread/make-leader-thread-layer.js +79 -27
- package/dist/leader-thread/make-leader-thread-layer.js.map +1 -1
- package/dist/leader-thread/make-leader-thread-layer.test.d.ts +2 -0
- package/dist/leader-thread/make-leader-thread-layer.test.d.ts.map +1 -0
- package/dist/leader-thread/make-leader-thread-layer.test.js +32 -0
- package/dist/leader-thread/make-leader-thread-layer.test.js.map +1 -0
- package/dist/leader-thread/materialize-event.d.ts +3 -3
- package/dist/leader-thread/materialize-event.d.ts.map +1 -1
- package/dist/leader-thread/materialize-event.js +25 -11
- package/dist/leader-thread/materialize-event.js.map +1 -1
- package/dist/leader-thread/recreate-db.d.ts +2 -3
- 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 +21 -19
- package/dist/leader-thread/types.d.ts.map +1 -1
- package/dist/leader-thread/types.js.map +1 -1
- package/dist/logging.d.ts +40 -0
- package/dist/logging.d.ts.map +1 -0
- package/dist/logging.js +33 -0
- package/dist/logging.js.map +1 -0
- package/dist/make-client-session.d.ts +5 -3
- package/dist/make-client-session.d.ts.map +1 -1
- package/dist/make-client-session.js +5 -2
- package/dist/make-client-session.js.map +1 -1
- package/dist/materializer-helper.d.ts +6 -6
- package/dist/materializer-helper.d.ts.map +1 -1
- package/dist/materializer-helper.js +20 -4
- 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 +29 -20
- 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/client.js +193 -0
- 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 +43 -43
- package/dist/schema/EventSequenceNumber.test.js.map +1 -1
- package/dist/schema/{LiveStoreEvent.d.ts → LiveStoreEvent/client.d.ts} +89 -106
- package/dist/schema/LiveStoreEvent/client.d.ts.map +1 -0
- package/dist/schema/{LiveStoreEvent.js → LiveStoreEvent/client.js} +74 -58
- 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 +6 -4
- package/dist/schema/mod.d.ts.map +1 -1
- package/dist/schema/mod.js +5 -4
- package/dist/schema/mod.js.map +1 -1
- package/dist/schema/schema.d.ts +16 -1
- package/dist/schema/schema.d.ts.map +1 -1
- package/dist/schema/schema.js +27 -2
- package/dist/schema/schema.js.map +1 -1
- package/dist/schema/state/sqlite/client-document-def.d.ts +36 -6
- package/dist/schema/state/sqlite/client-document-def.d.ts.map +1 -1
- package/dist/schema/state/sqlite/client-document-def.js +97 -6
- package/dist/schema/state/sqlite/client-document-def.js.map +1 -1
- package/dist/schema/state/sqlite/client-document-def.test.js +16 -0
- package/dist/schema/state/sqlite/client-document-def.test.js.map +1 -1
- package/dist/schema/state/sqlite/column-annotations.d.ts.map +1 -1
- package/dist/schema/state/sqlite/column-annotations.js +14 -6
- package/dist/schema/state/sqlite/column-annotations.js.map +1 -1
- package/dist/schema/state/sqlite/column-annotations.test.js +1 -1
- package/dist/schema/state/sqlite/column-annotations.test.js.map +1 -1
- package/dist/schema/state/sqlite/column-def.js +69 -22
- package/dist/schema/state/sqlite/column-def.js.map +1 -1
- package/dist/schema/state/sqlite/column-def.test.js +46 -7
- package/dist/schema/state/sqlite/column-def.test.js.map +1 -1
- package/dist/schema/state/sqlite/column-spec.d.ts.map +1 -1
- package/dist/schema/state/sqlite/column-spec.js +30 -12
- package/dist/schema/state/sqlite/column-spec.js.map +1 -1
- package/dist/schema/state/sqlite/column-spec.test.js +23 -14
- package/dist/schema/state/sqlite/column-spec.test.js.map +1 -1
- package/dist/schema/state/sqlite/db-schema/ast/sqlite.d.ts +2 -1
- package/dist/schema/state/sqlite/db-schema/ast/sqlite.d.ts.map +1 -1
- package/dist/schema/state/sqlite/db-schema/ast/sqlite.js +23 -6
- package/dist/schema/state/sqlite/db-schema/ast/sqlite.js.map +1 -1
- package/dist/schema/state/sqlite/db-schema/dsl/field-defs.d.ts +14 -8
- package/dist/schema/state/sqlite/db-schema/dsl/field-defs.d.ts.map +1 -1
- package/dist/schema/state/sqlite/db-schema/dsl/field-defs.js +5 -3
- package/dist/schema/state/sqlite/db-schema/dsl/field-defs.js.map +1 -1
- package/dist/schema/state/sqlite/db-schema/dsl/mod.d.ts.map +1 -1
- package/dist/schema/state/sqlite/db-schema/dsl/mod.js +2 -1
- package/dist/schema/state/sqlite/db-schema/dsl/mod.js.map +1 -1
- package/dist/schema/state/sqlite/mod.d.ts +3 -3
- package/dist/schema/state/sqlite/mod.d.ts.map +1 -1
- package/dist/schema/state/sqlite/mod.js +3 -3
- package/dist/schema/state/sqlite/mod.js.map +1 -1
- package/dist/schema/state/sqlite/query-builder/api.d.ts +17 -10
- 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 +22 -15
- 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 +6 -3
- package/dist/schema/state/sqlite/query-builder/impl.js.map +1 -1
- package/dist/schema/state/sqlite/query-builder/impl.test.js +252 -88
- package/dist/schema/state/sqlite/query-builder/impl.test.js.map +1 -1
- package/dist/schema/state/sqlite/schema-helpers.d.ts +2 -2
- package/dist/schema/state/sqlite/schema-helpers.d.ts.map +1 -1
- package/dist/schema/state/sqlite/schema-helpers.js +22 -12
- package/dist/schema/state/sqlite/schema-helpers.js.map +1 -1
- package/dist/schema/state/sqlite/schema-helpers.test.d.ts +2 -0
- package/dist/schema/state/sqlite/schema-helpers.test.d.ts.map +1 -0
- package/dist/schema/state/sqlite/schema-helpers.test.js +36 -0
- package/dist/schema/state/sqlite/schema-helpers.test.js.map +1 -0
- package/dist/schema/state/sqlite/{system-tables.d.ts → system-tables/eventlog-tables.d.ts} +63 -456
- package/dist/schema/state/sqlite/system-tables/eventlog-tables.d.ts.map +1 -0
- package/dist/schema/state/sqlite/system-tables/eventlog-tables.js +54 -0
- package/dist/schema/state/sqlite/system-tables/eventlog-tables.js.map +1 -0
- package/dist/schema/state/sqlite/system-tables/mod.d.ts +3 -0
- package/dist/schema/state/sqlite/system-tables/mod.d.ts.map +1 -0
- package/dist/schema/state/sqlite/system-tables/mod.js +3 -0
- package/dist/schema/state/sqlite/system-tables/mod.js.map +1 -0
- package/dist/schema/state/sqlite/system-tables/state-tables.d.ts +456 -0
- package/dist/schema/state/sqlite/system-tables/state-tables.d.ts.map +1 -0
- package/dist/schema/state/sqlite/system-tables/state-tables.js +55 -0
- package/dist/schema/state/sqlite/system-tables/state-tables.js.map +1 -0
- package/dist/schema/state/sqlite/table-def.d.ts +4 -4
- package/dist/schema/state/sqlite/table-def.d.ts.map +1 -1
- package/dist/schema/state/sqlite/table-def.js +2 -2
- package/dist/schema/state/sqlite/table-def.js.map +1 -1
- package/dist/schema/state/sqlite/table-def.test.js +80 -0
- package/dist/schema/state/sqlite/table-def.test.js.map +1 -1
- package/dist/schema/unknown-events.d.ts +47 -0
- package/dist/schema/unknown-events.d.ts.map +1 -0
- package/dist/schema/unknown-events.js +69 -0
- package/dist/schema/unknown-events.js.map +1 -0
- package/dist/schema-management/__tests__/migrations-autoincrement-quoting.test.d.ts +2 -0
- package/dist/schema-management/__tests__/migrations-autoincrement-quoting.test.d.ts.map +1 -0
- package/dist/schema-management/__tests__/migrations-autoincrement-quoting.test.js +73 -0
- package/dist/schema-management/__tests__/migrations-autoincrement-quoting.test.js.map +1 -0
- package/dist/schema-management/migrations.d.ts +32 -2
- package/dist/schema-management/migrations.d.ts.map +1 -1
- package/dist/schema-management/migrations.js +37 -5
- 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/sql-queries/sql-queries.d.ts.map +1 -1
- package/dist/sql-queries/sql-queries.js +11 -1
- package/dist/sql-queries/sql-queries.js.map +1 -1
- package/dist/sql-queries/sql-query-builder.d.ts.map +1 -1
- package/dist/sql-queries/sql-query-builder.js +2 -1
- package/dist/sql-queries/sql-query-builder.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 +11 -13
- package/dist/sync/ClientSessionSyncProcessor.d.ts.map +1 -1
- package/dist/sync/ClientSessionSyncProcessor.js +45 -42
- package/dist/sync/ClientSessionSyncProcessor.js.map +1 -1
- package/dist/sync/errors.d.ts +66 -0
- package/dist/sync/errors.d.ts.map +1 -0
- package/dist/sync/errors.js +36 -0
- package/dist/sync/errors.js.map +1 -0
- package/dist/sync/index.d.ts +3 -0
- package/dist/sync/index.d.ts.map +1 -1
- package/dist/sync/index.js +3 -0
- package/dist/sync/index.js.map +1 -1
- package/dist/sync/mock-sync-backend.d.ts +23 -0
- package/dist/sync/mock-sync-backend.d.ts.map +1 -0
- package/dist/sync/mock-sync-backend.js +114 -0
- package/dist/sync/mock-sync-backend.js.map +1 -0
- package/dist/sync/next/compact-events.d.ts.map +1 -1
- package/dist/sync/next/compact-events.js +6 -7
- 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 +1 -2
- package/dist/sync/next/facts.js.map +1 -1
- package/dist/sync/next/history-dag-common.d.ts +54 -15
- package/dist/sync/next/history-dag-common.d.ts.map +1 -1
- package/dist/sync/next/history-dag-common.js +198 -9
- 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 +10 -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 +7 -0
- package/dist/sync/sync-backend-kv.d.ts.map +1 -0
- package/dist/sync/sync-backend-kv.js +18 -0
- package/dist/sync/sync-backend-kv.js.map +1 -0
- package/dist/sync/sync-backend.d.ts +105 -0
- package/dist/sync/sync-backend.d.ts.map +1 -0
- package/dist/sync/sync-backend.js +61 -0
- package/dist/sync/sync-backend.js.map +1 -0
- package/dist/sync/sync.d.ts +9 -86
- package/dist/sync/sync.d.ts.map +1 -1
- package/dist/sync/sync.js +2 -27
- package/dist/sync/sync.js.map +1 -1
- package/dist/sync/syncstate.d.ts +57 -44
- package/dist/sync/syncstate.d.ts.map +1 -1
- package/dist/sync/syncstate.js +50 -45
- package/dist/sync/syncstate.js.map +1 -1
- package/dist/sync/syncstate.test.js +83 -46
- package/dist/sync/syncstate.test.js.map +1 -1
- package/dist/sync/transport-chunking.d.ts +36 -0
- package/dist/sync/transport-chunking.d.ts.map +1 -0
- package/dist/sync/transport-chunking.js +56 -0
- package/dist/sync/transport-chunking.js.map +1 -0
- package/dist/sync/validate-push-payload.d.ts +2 -2
- package/dist/sync/validate-push-payload.d.ts.map +1 -1
- package/dist/sync/validate-push-payload.js +6 -6
- package/dist/sync/validate-push-payload.js.map +1 -1
- package/dist/testing/event-factory.d.ts +68 -0
- package/dist/testing/event-factory.d.ts.map +1 -0
- package/dist/testing/event-factory.js +78 -0
- package/dist/testing/event-factory.js.map +1 -0
- package/dist/testing/mod.d.ts +2 -0
- package/dist/testing/mod.d.ts.map +1 -0
- package/dist/testing/mod.js +2 -0
- package/dist/testing/mod.js.map +1 -0
- package/dist/version.d.ts +16 -6
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +16 -6
- package/dist/version.js.map +1 -1
- package/package.json +7 -8
- package/src/ClientSessionLeaderThreadProxy.ts +17 -12
- package/src/adapter-types.ts +18 -6
- package/src/debug-info.ts +37 -6
- package/src/devtools/devtools-messages-client-session.ts +2 -2
- package/src/devtools/devtools-messages-common.ts +1 -8
- package/src/devtools/devtools-messages-leader.ts +8 -8
- package/src/devtools/devtools-sessioninfo.ts +8 -5
- package/src/devtools/mod.ts +11 -2
- package/src/errors.ts +38 -11
- package/src/index.ts +2 -1
- package/src/leader-thread/LeaderSyncProcessor.ts +242 -103
- package/src/leader-thread/eventlog.ts +33 -34
- package/src/leader-thread/leader-worker-devtools.ts +50 -54
- package/src/leader-thread/make-leader-thread-layer.test.ts +44 -0
- package/src/leader-thread/make-leader-thread-layer.ts +156 -37
- package/src/leader-thread/materialize-event.ts +37 -12
- package/src/leader-thread/recreate-db.ts +15 -7
- package/src/leader-thread/shutdown-channel.ts +16 -2
- package/src/leader-thread/types.ts +21 -19
- package/src/logging.ts +62 -0
- package/src/make-client-session.ts +9 -3
- package/src/materializer-helper.ts +27 -10
- package/src/rematerialize-from-eventlog.ts +37 -27
- 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 +70 -52
- 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 +6 -4
- package/src/schema/schema.ts +39 -3
- package/src/schema/state/sqlite/client-document-def.test.ts +19 -2
- package/src/schema/state/sqlite/client-document-def.ts +120 -8
- package/src/schema/state/sqlite/column-annotations.test.ts +1 -1
- package/src/schema/state/sqlite/column-annotations.ts +16 -6
- package/src/schema/state/sqlite/column-def.test.ts +60 -7
- package/src/schema/state/sqlite/column-def.ts +88 -21
- package/src/schema/state/sqlite/column-spec.test.ts +29 -16
- package/src/schema/state/sqlite/column-spec.ts +36 -11
- package/src/schema/state/sqlite/db-schema/ast/sqlite.ts +26 -6
- package/src/schema/state/sqlite/db-schema/dsl/field-defs.ts +29 -12
- package/src/schema/state/sqlite/db-schema/dsl/mod.ts +2 -1
- package/src/schema/state/sqlite/mod.ts +4 -3
- package/src/schema/state/sqlite/query-builder/api.ts +19 -10
- package/src/schema/state/sqlite/query-builder/astToSql.ts +23 -14
- package/src/schema/state/sqlite/query-builder/impl.test.ts +305 -92
- package/src/schema/state/sqlite/query-builder/impl.ts +8 -3
- package/src/schema/state/sqlite/schema-helpers.test.ts +44 -0
- package/src/schema/state/sqlite/schema-helpers.ts +28 -20
- package/src/schema/state/sqlite/system-tables/eventlog-tables.ts +64 -0
- package/src/schema/state/sqlite/system-tables/mod.ts +2 -0
- package/src/schema/state/sqlite/system-tables/state-tables.ts +69 -0
- package/src/schema/state/sqlite/table-def.test.ts +101 -0
- package/src/schema/state/sqlite/table-def.ts +9 -8
- package/src/schema/unknown-events.ts +131 -0
- package/src/schema-management/__tests__/migrations-autoincrement-quoting.test.ts +86 -0
- package/src/schema-management/migrations.ts +41 -8
- package/src/schema-management/validate-schema.ts +3 -3
- package/src/sql-queries/sql-queries.ts +9 -1
- package/src/sql-queries/sql-query-builder.ts +2 -1
- package/src/sqlite-types.ts +3 -3
- package/src/sync/ClientSessionSyncProcessor.ts +69 -62
- package/src/sync/errors.ts +38 -0
- package/src/sync/index.ts +3 -0
- package/src/sync/mock-sync-backend.ts +184 -0
- package/src/sync/next/compact-events.ts +6 -7
- package/src/sync/next/facts.ts +7 -9
- package/src/sync/next/history-dag-common.ts +277 -26
- package/src/sync/next/history-dag.ts +16 -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 +22 -0
- package/src/sync/sync-backend.ts +185 -0
- package/src/sync/sync.ts +9 -91
- package/src/sync/syncstate.test.ts +96 -52
- package/src/sync/syncstate.ts +69 -58
- package/src/sync/transport-chunking.ts +90 -0
- package/src/sync/validate-push-payload.ts +8 -9
- package/src/testing/event-factory.ts +131 -0
- package/src/testing/mod.ts +1 -0
- package/src/version.ts +16 -6
- package/dist/schema/EventDef.d.ts +0 -123
- 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 -80
- package/dist/schema/EventSequenceNumber.d.ts.map +0 -1
- package/dist/schema/EventSequenceNumber.js +0 -139
- 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/dist/schema/state/sqlite/system-tables.d.ts.map +0 -1
- package/dist/schema/state/sqlite/system-tables.js +0 -79
- package/dist/schema/state/sqlite/system-tables.js.map +0 -1
- package/dist/schema-management/migrations.test.d.ts +0 -2
- package/dist/schema-management/migrations.test.d.ts.map +0 -1
- package/dist/schema-management/migrations.test.js +0 -52
- package/dist/schema-management/migrations.test.js.map +0 -1
- package/dist/sync/next/graphology.d.ts +0 -8
- package/dist/sync/next/graphology.d.ts.map +0 -1
- package/dist/sync/next/graphology.js +0 -30
- package/dist/sync/next/graphology.js.map +0 -1
- package/dist/sync/next/graphology_.d.ts +0 -3
- package/dist/sync/next/graphology_.d.ts.map +0 -1
- package/dist/sync/next/graphology_.js +0 -3
- package/dist/sync/next/graphology_.js.map +0 -1
- package/src/schema/EventDef.ts +0 -219
- package/src/schema/EventSequenceNumber.ts +0 -199
- package/src/schema/LiveStoreEvent.ts +0 -287
- package/src/schema/state/sqlite/system-tables.ts +0 -104
- package/src/sync/next/ambient.d.ts +0 -3
- package/src/sync/next/graphology.ts +0 -41
- package/src/sync/next/graphology_.ts +0 -2
|
@@ -35,7 +35,6 @@ export const makeQueryBuilder = <TResult, TTableDef extends TableDefBase>(
|
|
|
35
35
|
select: { columns },
|
|
36
36
|
}) as any
|
|
37
37
|
},
|
|
38
|
-
// biome-ignore lint/complexity/useArrowFunction: prefer function over arrow function for this case
|
|
39
38
|
where: function () {
|
|
40
39
|
if (ast._tag === 'InsertQuery') return invalidQueryBuilder('Cannot use where with insert')
|
|
41
40
|
if (ast._tag === 'RowQuery') return invalidQueryBuilder('Cannot use where with row')
|
|
@@ -219,21 +218,27 @@ export const makeQueryBuilder = <TResult, TTableDef extends TableDefBase>(
|
|
|
219
218
|
update: (values) => {
|
|
220
219
|
const filteredValues = Object.fromEntries(Object.entries(values).filter(([, value]) => value !== undefined))
|
|
221
220
|
|
|
221
|
+
// Preserve where clauses if coming from a SelectQuery
|
|
222
|
+
const whereClause = ast._tag === 'SelectQuery' ? ast.where : []
|
|
223
|
+
|
|
222
224
|
return makeQueryBuilder(tableDef, {
|
|
223
225
|
_tag: 'UpdateQuery',
|
|
224
226
|
tableDef,
|
|
225
227
|
values: filteredValues,
|
|
226
|
-
where:
|
|
228
|
+
where: whereClause,
|
|
227
229
|
returning: undefined,
|
|
228
230
|
resultSchema: Schema.Void,
|
|
229
231
|
}) as any
|
|
230
232
|
},
|
|
231
233
|
|
|
232
234
|
delete: () => {
|
|
235
|
+
// Preserve where clauses if coming from a SelectQuery
|
|
236
|
+
const whereClause = ast._tag === 'SelectQuery' ? ast.where : []
|
|
237
|
+
|
|
233
238
|
return makeQueryBuilder(tableDef, {
|
|
234
239
|
_tag: 'DeleteQuery',
|
|
235
240
|
tableDef,
|
|
236
|
-
where:
|
|
241
|
+
where: whereClause,
|
|
237
242
|
returning: undefined,
|
|
238
243
|
resultSchema: Schema.Void,
|
|
239
244
|
}) as any
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
|
|
3
|
+
import * as State from '../mod.ts'
|
|
4
|
+
import { getDefaultValuesDecoded, getDefaultValuesEncoded } from './schema-helpers.ts'
|
|
5
|
+
|
|
6
|
+
describe('schema-helpers', () => {
|
|
7
|
+
it('resolves thunk defaults when decoding values', () => {
|
|
8
|
+
let counter = 0
|
|
9
|
+
const table = State.SQLite.table({
|
|
10
|
+
name: 'sessions',
|
|
11
|
+
columns: {
|
|
12
|
+
id: State.SQLite.text({ primaryKey: true }),
|
|
13
|
+
token: State.SQLite.text({ default: () => `token-${++counter}` }),
|
|
14
|
+
},
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
expect(counter).toBe(0)
|
|
18
|
+
|
|
19
|
+
const firstDefaults = getDefaultValuesDecoded(table)
|
|
20
|
+
const secondDefaults = getDefaultValuesDecoded(table)
|
|
21
|
+
|
|
22
|
+
expect(firstDefaults.token).toBe('token-1')
|
|
23
|
+
expect(secondDefaults.token).toBe('token-2')
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it('resolves thunk defaults when encoding values', () => {
|
|
27
|
+
let counter = 0
|
|
28
|
+
const table = State.SQLite.table({
|
|
29
|
+
name: 'sessions_encoded',
|
|
30
|
+
columns: {
|
|
31
|
+
id: State.SQLite.text({ primaryKey: true }),
|
|
32
|
+
token: State.SQLite.text({ default: () => `encoded-${++counter}` }),
|
|
33
|
+
},
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
expect(counter).toBe(0)
|
|
37
|
+
|
|
38
|
+
const firstDefaults = getDefaultValuesEncoded(table)
|
|
39
|
+
const secondDefaults = getDefaultValuesEncoded(table)
|
|
40
|
+
|
|
41
|
+
expect(firstDefaults.token).toBe('encoded-1')
|
|
42
|
+
expect(secondDefaults.token).toBe('encoded-2')
|
|
43
|
+
})
|
|
44
|
+
})
|
|
@@ -2,9 +2,9 @@ import { shouldNeverHappen } from '@livestore/utils'
|
|
|
2
2
|
import { pipe, ReadonlyRecord, Schema } from '@livestore/utils/effect'
|
|
3
3
|
|
|
4
4
|
import { SqliteDsl } from './db-schema/mod.ts'
|
|
5
|
-
import type {
|
|
5
|
+
import type { TableDefBase } from './table-def.ts'
|
|
6
6
|
|
|
7
|
-
export const getDefaultValuesEncoded = <TTableDef extends
|
|
7
|
+
export const getDefaultValuesEncoded = <TTableDef extends TableDefBase>(
|
|
8
8
|
tableDef: TTableDef,
|
|
9
9
|
fallbackValues?: Record<string, any>,
|
|
10
10
|
) =>
|
|
@@ -15,15 +15,19 @@ export const getDefaultValuesEncoded = <TTableDef extends TableDef>(
|
|
|
15
15
|
if (key === 'id') return false
|
|
16
16
|
return col!.default._tag === 'None' || SqliteDsl.isSqlDefaultValue(col!.default.value) === false
|
|
17
17
|
}),
|
|
18
|
-
ReadonlyRecord.map((column, columnName) =>
|
|
19
|
-
fallbackValues?.[columnName]
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
18
|
+
ReadonlyRecord.map((column, columnName) => {
|
|
19
|
+
if (fallbackValues?.[columnName] !== undefined) return fallbackValues[columnName]
|
|
20
|
+
if (column!.default._tag === 'None') {
|
|
21
|
+
return column!.nullable === true
|
|
22
|
+
? null
|
|
23
|
+
: shouldNeverHappen(`Column ${columnName} has no default value and is not nullable`)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const defaultValue = column!.default.value
|
|
27
|
+
const resolvedDefault = SqliteDsl.resolveColumnDefault(defaultValue)
|
|
28
|
+
|
|
29
|
+
return Schema.encodeSync(column!.schema)(resolvedDefault)
|
|
30
|
+
}),
|
|
27
31
|
)
|
|
28
32
|
|
|
29
33
|
export const getDefaultValuesDecoded = <TTableDef extends TableDefBase>(
|
|
@@ -37,13 +41,17 @@ export const getDefaultValuesDecoded = <TTableDef extends TableDefBase>(
|
|
|
37
41
|
if (key === 'id') return false
|
|
38
42
|
return col!.default._tag === 'None' || SqliteDsl.isSqlDefaultValue(col!.default.value) === false
|
|
39
43
|
}),
|
|
40
|
-
ReadonlyRecord.map((column, columnName) =>
|
|
41
|
-
fallbackValues?.[columnName]
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
44
|
+
ReadonlyRecord.map((column, columnName) => {
|
|
45
|
+
if (fallbackValues?.[columnName] !== undefined) return fallbackValues[columnName]
|
|
46
|
+
if (column!.default._tag === 'None') {
|
|
47
|
+
return column!.nullable === true
|
|
48
|
+
? null
|
|
49
|
+
: shouldNeverHappen(`Column ${columnName} has no default value and is not nullable`)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const defaultValue = column!.default.value
|
|
53
|
+
const resolvedDefault = SqliteDsl.resolveColumnDefault(defaultValue)
|
|
54
|
+
|
|
55
|
+
return Schema.validateSync(column!.schema)(resolvedDefault)
|
|
56
|
+
}),
|
|
49
57
|
)
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { Schema } from '@livestore/utils/effect'
|
|
2
|
+
|
|
3
|
+
import * as EventSequenceNumber from '../../../EventSequenceNumber/mod.ts'
|
|
4
|
+
import { SqliteDsl } from '../db-schema/mod.ts'
|
|
5
|
+
import { table } from '../table-def.ts'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* EVENTLOG DATABASE SYSTEM TABLES
|
|
9
|
+
*
|
|
10
|
+
* ⚠️ CRITICAL: NEVER modify eventlog schemas without bumping `liveStoreStorageFormatVersion`!
|
|
11
|
+
* Eventlog is the source of truth - schema changes cause permanent data loss.
|
|
12
|
+
*
|
|
13
|
+
* TODO: Implement proper eventlog versioning system to prevent accidental data loss
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
export const EVENTLOG_META_TABLE = 'eventlog'
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Main client-side event log storing all events (global and local/rebased).
|
|
20
|
+
*/
|
|
21
|
+
export const eventlogMetaTable = table({
|
|
22
|
+
name: EVENTLOG_META_TABLE,
|
|
23
|
+
columns: {
|
|
24
|
+
// TODO Adjust modeling so a global event never needs a client id component
|
|
25
|
+
seqNumGlobal: SqliteDsl.integer({ primaryKey: true, schema: EventSequenceNumber.Global.Schema }),
|
|
26
|
+
seqNumClient: SqliteDsl.integer({ primaryKey: true, schema: EventSequenceNumber.Client.Schema }),
|
|
27
|
+
seqNumRebaseGeneration: SqliteDsl.integer({ primaryKey: true }),
|
|
28
|
+
parentSeqNumGlobal: SqliteDsl.integer({ schema: EventSequenceNumber.Global.Schema }),
|
|
29
|
+
parentSeqNumClient: SqliteDsl.integer({ schema: EventSequenceNumber.Client.Schema }),
|
|
30
|
+
parentSeqNumRebaseGeneration: SqliteDsl.integer({}),
|
|
31
|
+
/** Event definition name */
|
|
32
|
+
name: SqliteDsl.text({}),
|
|
33
|
+
argsJson: SqliteDsl.text({ schema: Schema.parseJson(Schema.Any) }),
|
|
34
|
+
clientId: SqliteDsl.text({}),
|
|
35
|
+
sessionId: SqliteDsl.text({}),
|
|
36
|
+
schemaHash: SqliteDsl.integer({}),
|
|
37
|
+
syncMetadataJson: SqliteDsl.text({ schema: Schema.parseJson(Schema.Option(Schema.JsonValue)) }),
|
|
38
|
+
},
|
|
39
|
+
indexes: [
|
|
40
|
+
{ columns: ['seqNumGlobal'], name: 'idx_eventlog_seqNumGlobal' },
|
|
41
|
+
{ columns: ['seqNumGlobal', 'seqNumClient', 'seqNumRebaseGeneration'], name: 'idx_eventlog_seqNum' },
|
|
42
|
+
],
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
export type EventlogMetaRow = typeof eventlogMetaTable.Type
|
|
46
|
+
|
|
47
|
+
export const SYNC_STATUS_TABLE = '__livestore_sync_status'
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Tracks sync status including the remote head position and backend identity.
|
|
51
|
+
*/
|
|
52
|
+
// TODO support sync backend identity (to detect if sync backend changes)
|
|
53
|
+
export const syncStatusTable = table({
|
|
54
|
+
name: SYNC_STATUS_TABLE,
|
|
55
|
+
columns: {
|
|
56
|
+
head: SqliteDsl.integer({ primaryKey: true }),
|
|
57
|
+
// Null means the sync backend is not yet connected and we haven't yet seen a backend ID
|
|
58
|
+
backendId: SqliteDsl.text({ nullable: true }),
|
|
59
|
+
},
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
export type SyncStatusRow = typeof syncStatusTable.Type
|
|
63
|
+
|
|
64
|
+
export const eventlogSystemTables = [eventlogMetaTable, syncStatusTable] as const
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import * as EventSequenceNumber from '../../../EventSequenceNumber/mod.ts'
|
|
2
|
+
import { SqliteDsl } from '../db-schema/mod.ts'
|
|
3
|
+
import { table } from '../table-def.ts'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* STATE DATABASE SYSTEM TABLES
|
|
7
|
+
*
|
|
8
|
+
* ⚠️ SAFE TO CHANGE: State tables are automatically rebuilt from eventlog when schema changes.
|
|
9
|
+
* No need to bump `liveStoreStorageFormatVersion` (uses hash-based migration via SqliteAst.hash()).
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export const SCHEMA_META_TABLE = '__livestore_schema'
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Tracks schema hashes for user-defined tables to detect schema changes.
|
|
16
|
+
*/
|
|
17
|
+
export const schemaMetaTable = table({
|
|
18
|
+
name: SCHEMA_META_TABLE,
|
|
19
|
+
columns: {
|
|
20
|
+
tableName: SqliteDsl.text({ primaryKey: true }),
|
|
21
|
+
schemaHash: SqliteDsl.integer({ nullable: false }),
|
|
22
|
+
/** ISO date format */
|
|
23
|
+
updatedAt: SqliteDsl.text({ nullable: false }),
|
|
24
|
+
},
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
export type SchemaMetaRow = typeof schemaMetaTable.Type
|
|
28
|
+
|
|
29
|
+
export const SCHEMA_EVENT_DEFS_META_TABLE = '__livestore_schema_event_defs'
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Tracks schema hashes for event definitions to detect event schema changes.
|
|
33
|
+
*/
|
|
34
|
+
export const schemaEventDefsMetaTable = table({
|
|
35
|
+
name: SCHEMA_EVENT_DEFS_META_TABLE,
|
|
36
|
+
columns: {
|
|
37
|
+
eventName: SqliteDsl.text({ primaryKey: true }),
|
|
38
|
+
schemaHash: SqliteDsl.integer({ nullable: false }),
|
|
39
|
+
/** ISO date format */
|
|
40
|
+
updatedAt: SqliteDsl.text({ nullable: false }),
|
|
41
|
+
},
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
export type SchemaEventDefsMetaRow = typeof schemaEventDefsMetaTable.Type
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Table which stores SQLite changeset blobs which is used for rolling back
|
|
48
|
+
* read-model state during rebasing.
|
|
49
|
+
*/
|
|
50
|
+
export const SESSION_CHANGESET_META_TABLE = '__livestore_session_changeset'
|
|
51
|
+
|
|
52
|
+
export const sessionChangesetMetaTable = table({
|
|
53
|
+
name: SESSION_CHANGESET_META_TABLE,
|
|
54
|
+
columns: {
|
|
55
|
+
// TODO bring back primary key
|
|
56
|
+
seqNumGlobal: SqliteDsl.integer({ schema: EventSequenceNumber.Global.Schema }),
|
|
57
|
+
seqNumClient: SqliteDsl.integer({ schema: EventSequenceNumber.Client.Schema }),
|
|
58
|
+
seqNumRebaseGeneration: SqliteDsl.integer({}),
|
|
59
|
+
changeset: SqliteDsl.blob({ nullable: true }),
|
|
60
|
+
debug: SqliteDsl.json({ nullable: true }),
|
|
61
|
+
},
|
|
62
|
+
indexes: [{ columns: ['seqNumGlobal', 'seqNumClient'], name: 'idx_session_changeset_id' }],
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
export type SessionChangesetMetaRow = typeof sessionChangesetMetaTable.Type
|
|
66
|
+
|
|
67
|
+
export const stateSystemTables = [schemaMetaTable, schemaEventDefsMetaTable, sessionChangesetMetaTable] as const
|
|
68
|
+
|
|
69
|
+
export const isStateSystemTable = (tableName: string) => stateSystemTables.some((_) => _.sqliteDef.name === tableName)
|
|
@@ -178,6 +178,59 @@ describe('table function overloads', () => {
|
|
|
178
178
|
expect(userTable.sqliteDef.columns.age.columnType).toBe('integer')
|
|
179
179
|
})
|
|
180
180
|
|
|
181
|
+
it('should support schemas that transform flat columns into nested types', () => {
|
|
182
|
+
const Flat = Schema.Struct({
|
|
183
|
+
id: Schema.String.pipe(State.SQLite.withPrimaryKey),
|
|
184
|
+
contactFirstName: Schema.String,
|
|
185
|
+
contactLastName: Schema.String,
|
|
186
|
+
contactEmail: Schema.String.pipe(State.SQLite.withUnique),
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
const Nested = Schema.transform(
|
|
190
|
+
Flat,
|
|
191
|
+
Schema.Struct({
|
|
192
|
+
id: Schema.String,
|
|
193
|
+
contact: Schema.Struct({
|
|
194
|
+
firstName: Schema.String,
|
|
195
|
+
lastName: Schema.String,
|
|
196
|
+
email: Schema.String,
|
|
197
|
+
}),
|
|
198
|
+
}),
|
|
199
|
+
{
|
|
200
|
+
decode: ({ id, contactFirstName, contactLastName, contactEmail }) => ({
|
|
201
|
+
id,
|
|
202
|
+
contact: {
|
|
203
|
+
firstName: contactFirstName,
|
|
204
|
+
lastName: contactLastName,
|
|
205
|
+
email: contactEmail,
|
|
206
|
+
},
|
|
207
|
+
}),
|
|
208
|
+
encode: ({ id, contact }) => ({
|
|
209
|
+
id,
|
|
210
|
+
contactFirstName: contact.firstName,
|
|
211
|
+
contactLastName: contact.lastName,
|
|
212
|
+
contactEmail: contact.email,
|
|
213
|
+
}),
|
|
214
|
+
},
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
const contactsTable = State.SQLite.table({
|
|
218
|
+
name: 'contacts',
|
|
219
|
+
schema: Nested,
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
const columns = contactsTable.sqliteDef.columns
|
|
223
|
+
|
|
224
|
+
expect(Object.keys(columns)).toEqual(['id', 'contactFirstName', 'contactLastName', 'contactEmail'])
|
|
225
|
+
expect(columns.id.primaryKey).toBe(true)
|
|
226
|
+
expect(columns.contactEmail.columnType).toBe('text')
|
|
227
|
+
expect(contactsTable.sqliteDef.indexes).toContainEqual({
|
|
228
|
+
name: 'idx_contacts_contactEmail_unique',
|
|
229
|
+
columns: ['contactEmail'],
|
|
230
|
+
isUnique: true,
|
|
231
|
+
})
|
|
232
|
+
})
|
|
233
|
+
|
|
181
234
|
it('should extract table name from Schema.Class identifier', () => {
|
|
182
235
|
class TodoItem extends Schema.Class<TodoItem>('TodoItem')({
|
|
183
236
|
id: Schema.String,
|
|
@@ -247,4 +300,52 @@ describe('table function overloads', () => {
|
|
|
247
300
|
expect(userTable.sqliteDef.columns.metadata.columnType).toBe('text')
|
|
248
301
|
expect(userTable.sqliteDef.columns.metadata.nullable).toBe(true)
|
|
249
302
|
})
|
|
303
|
+
|
|
304
|
+
it('supports discriminated unions with parsed JSON payloads', () => {
|
|
305
|
+
const CircleDataSchema = Schema.Struct({
|
|
306
|
+
radius: Schema.Number,
|
|
307
|
+
})
|
|
308
|
+
const CircleSchema = Schema.Struct({
|
|
309
|
+
kind: Schema.Literal('circle'),
|
|
310
|
+
data: Schema.parseJson(CircleDataSchema),
|
|
311
|
+
})
|
|
312
|
+
|
|
313
|
+
const SquareDataSchema = Schema.Struct({
|
|
314
|
+
sideLength: Schema.Number,
|
|
315
|
+
})
|
|
316
|
+
const SquareSchema = Schema.Struct({
|
|
317
|
+
kind: Schema.Literal('square'),
|
|
318
|
+
data: Schema.parseJson(SquareDataSchema),
|
|
319
|
+
})
|
|
320
|
+
|
|
321
|
+
const ShapeSchema = Schema.Union(CircleSchema, SquareSchema)
|
|
322
|
+
|
|
323
|
+
const shapes = State.SQLite.table({
|
|
324
|
+
name: 'shapes',
|
|
325
|
+
schema: ShapeSchema,
|
|
326
|
+
})
|
|
327
|
+
|
|
328
|
+
expect(shapes.sqliteDef.columns.kind.columnType).toBe('text')
|
|
329
|
+
|
|
330
|
+
const kindSchema = shapes.sqliteDef.columns.kind.schema.toString()
|
|
331
|
+
expect(kindSchema).toContain('"circle" | "square"')
|
|
332
|
+
|
|
333
|
+
expect(() =>
|
|
334
|
+
shapes
|
|
335
|
+
.insert({
|
|
336
|
+
kind: 'square',
|
|
337
|
+
data: { sideLength: 10 },
|
|
338
|
+
})
|
|
339
|
+
.asSql(),
|
|
340
|
+
).not.toThrow()
|
|
341
|
+
|
|
342
|
+
expect(() =>
|
|
343
|
+
shapes
|
|
344
|
+
.insert({
|
|
345
|
+
kind: 'circle',
|
|
346
|
+
data: { radius: 5 },
|
|
347
|
+
})
|
|
348
|
+
.asSql(),
|
|
349
|
+
).not.toThrow()
|
|
350
|
+
})
|
|
250
351
|
})
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type Nullable, shouldNeverHappen } from '@livestore/utils'
|
|
2
|
-
import { Option,
|
|
2
|
+
import { Option, Schema, SchemaAST, type Types } from '@livestore/utils/effect'
|
|
3
3
|
|
|
4
4
|
import { getColumnDefForSchema, schemaFieldsToColumns } from './column-def.ts'
|
|
5
5
|
import { SqliteDsl } from './db-schema/mod.ts'
|
|
@@ -221,7 +221,7 @@ export function table<
|
|
|
221
221
|
) as SqliteDsl.Columns
|
|
222
222
|
additionalIndexes = []
|
|
223
223
|
} else if ('schema' in args) {
|
|
224
|
-
const result = schemaFieldsToColumns(
|
|
224
|
+
const result = schemaFieldsToColumns(Schema.getResolvedPropertySignatures(args.schema))
|
|
225
225
|
columns = result.columns
|
|
226
226
|
|
|
227
227
|
// We'll set tableName first, then use it for index names
|
|
@@ -381,12 +381,13 @@ export declare namespace SchemaToColumns {
|
|
|
381
381
|
export type ColumnDefForType<TEncoded, TType> = SqliteDsl.ColumnDefinition<TEncoded, TType>
|
|
382
382
|
|
|
383
383
|
// Create columns type from schema Type and Encoded
|
|
384
|
-
export type FromTypes<TType, TEncoded> =
|
|
385
|
-
?
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
384
|
+
export type FromTypes<TType, TEncoded> = TEncoded extends Record<string, any>
|
|
385
|
+
? {
|
|
386
|
+
[K in keyof TEncoded]-?: ColumnDefForType<
|
|
387
|
+
TEncoded[K],
|
|
388
|
+
TType extends Record<string, any> ? (K extends keyof TType ? TType[K] : TEncoded[K]) : TEncoded[K]
|
|
389
|
+
>
|
|
390
|
+
}
|
|
390
391
|
: SqliteDsl.Columns
|
|
391
392
|
}
|
|
392
393
|
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { Effect } from '@livestore/utils/effect'
|
|
2
|
+
|
|
3
|
+
import { UnknownEventError } from '../errors.ts'
|
|
4
|
+
import type { EventDef, Materializer } from './EventDef/mod.ts'
|
|
5
|
+
import type * as LiveStoreEvent from './LiveStoreEvent/mod.ts'
|
|
6
|
+
import type { LiveStoreSchema } from './schema.ts'
|
|
7
|
+
|
|
8
|
+
export type UnknownEventContext = {
|
|
9
|
+
readonly event: Pick<LiveStoreEvent.Client.Encoded, 'name' | 'args' | 'seqNum' | 'clientId' | 'sessionId'>
|
|
10
|
+
readonly reason: 'event-definition-missing' | 'materializer-missing'
|
|
11
|
+
readonly operation: string
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export namespace UnknownEvents {
|
|
15
|
+
export type HandlingStrategy = 'warn' | 'fail' | 'ignore' | 'callback'
|
|
16
|
+
|
|
17
|
+
export type Callback = (
|
|
18
|
+
context: UnknownEventContext,
|
|
19
|
+
error: UnknownEventError,
|
|
20
|
+
) => Effect.SyncOrPromiseOrEffect<void, unknown, never>
|
|
21
|
+
|
|
22
|
+
export type HandlingConfig =
|
|
23
|
+
| { readonly strategy: 'warn' }
|
|
24
|
+
| { readonly strategy: 'ignore' }
|
|
25
|
+
| { readonly strategy: 'fail' }
|
|
26
|
+
| { readonly strategy: 'callback'; readonly onUnknownEvent: Callback }
|
|
27
|
+
|
|
28
|
+
export type Reason = UnknownEventContext['reason']
|
|
29
|
+
|
|
30
|
+
export type ResolveContext = Omit<UnknownEventContext, 'reason'>
|
|
31
|
+
|
|
32
|
+
export type Resolved =
|
|
33
|
+
| {
|
|
34
|
+
readonly _tag: 'known'
|
|
35
|
+
readonly eventDef: EventDef.AnyWithoutFn
|
|
36
|
+
readonly materializer: Materializer
|
|
37
|
+
}
|
|
38
|
+
| {
|
|
39
|
+
readonly _tag: 'unknown'
|
|
40
|
+
readonly reason: Reason
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const DEFAULT_UNKNOWN_EVENT_HANDLING: UnknownEvents.HandlingConfig = { strategy: 'warn' }
|
|
45
|
+
|
|
46
|
+
export const normalizeUnknownEventHandling = (
|
|
47
|
+
input: UnknownEvents.HandlingConfig | undefined,
|
|
48
|
+
): UnknownEvents.HandlingConfig => input ?? DEFAULT_UNKNOWN_EVENT_HANDLING
|
|
49
|
+
|
|
50
|
+
const handleUnknownEvent = ({
|
|
51
|
+
schema,
|
|
52
|
+
context,
|
|
53
|
+
}: {
|
|
54
|
+
schema: LiveStoreSchema
|
|
55
|
+
context: UnknownEventContext
|
|
56
|
+
}): Effect.Effect<void, UnknownEventError> =>
|
|
57
|
+
Effect.gen(function* () {
|
|
58
|
+
const config = schema.unknownEventHandling
|
|
59
|
+
const error = new UnknownEventError(context)
|
|
60
|
+
|
|
61
|
+
switch (config.strategy) {
|
|
62
|
+
case 'fail': {
|
|
63
|
+
return yield* Effect.fail(error)
|
|
64
|
+
}
|
|
65
|
+
case 'warn': {
|
|
66
|
+
yield* Effect.logWarning('@livestore/common:schema:unknown-event', context)
|
|
67
|
+
return
|
|
68
|
+
}
|
|
69
|
+
case 'ignore': {
|
|
70
|
+
return
|
|
71
|
+
}
|
|
72
|
+
case 'callback': {
|
|
73
|
+
const callback = config.onUnknownEvent
|
|
74
|
+
|
|
75
|
+
yield* Effect.tryAll<void>(() => callback(context, error)).pipe(
|
|
76
|
+
Effect.catchAll((cause) =>
|
|
77
|
+
Effect.logWarning('@livestore/common:schema:unknown-event:callback-error', {
|
|
78
|
+
event: context.event,
|
|
79
|
+
reason: context.reason,
|
|
80
|
+
operation: context.operation,
|
|
81
|
+
cause,
|
|
82
|
+
}),
|
|
83
|
+
),
|
|
84
|
+
)
|
|
85
|
+
return
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Resolves the runtime event definition + materializer for a given event name.
|
|
92
|
+
*
|
|
93
|
+
* Behaviour is intentionally split across the result and error channels:
|
|
94
|
+
* - For `'fail'` handling, we surface an `UnknownEventError` via the failure channel so
|
|
95
|
+
* callers can convert it into the appropriate domain error (for example `MaterializeError`).
|
|
96
|
+
* - For all other strategies (`warn`, `ignore`, `callback`) we succeed with an
|
|
97
|
+
* `{ _tag: 'unknown' }` value, signalling that the caller should skip the event while
|
|
98
|
+
* continuing normal processing.
|
|
99
|
+
*/
|
|
100
|
+
export const resolveEventDef = (
|
|
101
|
+
schema: LiveStoreSchema,
|
|
102
|
+
context: UnknownEvents.ResolveContext,
|
|
103
|
+
): Effect.Effect<UnknownEvents.Resolved, UnknownEventError> =>
|
|
104
|
+
Effect.gen(function* () {
|
|
105
|
+
const eventName = context.event.name
|
|
106
|
+
const eventDef = schema.eventsDefsMap.get(eventName)
|
|
107
|
+
if (eventDef === undefined) {
|
|
108
|
+
yield* handleUnknownEvent({
|
|
109
|
+
schema,
|
|
110
|
+
context: {
|
|
111
|
+
event: context.event,
|
|
112
|
+
reason: 'event-definition-missing',
|
|
113
|
+
operation: context.operation,
|
|
114
|
+
},
|
|
115
|
+
})
|
|
116
|
+
return { _tag: 'unknown', reason: 'event-definition-missing' }
|
|
117
|
+
}
|
|
118
|
+
const materializer = schema.state.materializers.get(eventName)
|
|
119
|
+
if (materializer === undefined) {
|
|
120
|
+
yield* handleUnknownEvent({
|
|
121
|
+
schema,
|
|
122
|
+
context: {
|
|
123
|
+
event: context.event,
|
|
124
|
+
reason: 'materializer-missing',
|
|
125
|
+
operation: context.operation,
|
|
126
|
+
},
|
|
127
|
+
})
|
|
128
|
+
return { _tag: 'unknown', reason: 'materializer-missing' }
|
|
129
|
+
}
|
|
130
|
+
return { _tag: 'known', eventDef, materializer }
|
|
131
|
+
})
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { Effect, Option, Schema } from '@livestore/utils/effect'
|
|
2
|
+
import { describe, expect, it } from 'vitest'
|
|
3
|
+
import { SqliteAst } from '../../schema/state/sqlite/db-schema/mod.ts'
|
|
4
|
+
import type { PreparedStatement, SqliteDb } from '../../sqlite-types.ts'
|
|
5
|
+
import type { PreparedBindValues } from '../../util.ts'
|
|
6
|
+
import { migrateTable } from '../migrations.ts'
|
|
7
|
+
|
|
8
|
+
const makeStubDb = () => {
|
|
9
|
+
const executed: string[] = []
|
|
10
|
+
|
|
11
|
+
const db: SqliteDb = {
|
|
12
|
+
_tag: 'SqliteDb',
|
|
13
|
+
metadata: { dbPointer: 0, persistenceInfo: { fileName: ':memory:' } } as any,
|
|
14
|
+
debug: { head: 0 as any },
|
|
15
|
+
prepare: (queryStr: string): PreparedStatement => ({
|
|
16
|
+
sql: queryStr,
|
|
17
|
+
execute: (_bind: PreparedBindValues | undefined) => {
|
|
18
|
+
executed.push(queryStr)
|
|
19
|
+
},
|
|
20
|
+
select: <T>(_bind: PreparedBindValues | undefined) => [] as unknown as ReadonlyArray<T>,
|
|
21
|
+
finalize: () => {},
|
|
22
|
+
}),
|
|
23
|
+
execute: () => {},
|
|
24
|
+
select: () => [],
|
|
25
|
+
export: () => new Uint8Array(),
|
|
26
|
+
import: () => {},
|
|
27
|
+
close: () => {},
|
|
28
|
+
destroy: () => {},
|
|
29
|
+
session: () => ({ changeset: () => undefined, finish: () => {} }),
|
|
30
|
+
makeChangeset: () => ({ invert: () => ({ invert: () => ({}) as any, apply: () => {} }) as any, apply: () => {} }),
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return { db, executed }
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
describe('migrateTable - quoting and autoincrement', () => {
|
|
37
|
+
it('creates valid CREATE TABLE with inline INTEGER PRIMARY KEY AUTOINCREMENT and double-quoted identifiers', () => {
|
|
38
|
+
const { db, executed } = makeStubDb()
|
|
39
|
+
|
|
40
|
+
const table = SqliteAst.table(
|
|
41
|
+
'todos',
|
|
42
|
+
[
|
|
43
|
+
SqliteAst.column({
|
|
44
|
+
name: 'id',
|
|
45
|
+
type: { _tag: 'integer' },
|
|
46
|
+
nullable: false,
|
|
47
|
+
primaryKey: true,
|
|
48
|
+
autoIncrement: true,
|
|
49
|
+
default: Option.none(),
|
|
50
|
+
schema: Schema.Number,
|
|
51
|
+
}),
|
|
52
|
+
SqliteAst.column({
|
|
53
|
+
name: 'text',
|
|
54
|
+
type: { _tag: 'text' },
|
|
55
|
+
nullable: false,
|
|
56
|
+
primaryKey: false,
|
|
57
|
+
autoIncrement: false,
|
|
58
|
+
default: Option.some(''),
|
|
59
|
+
schema: Schema.String,
|
|
60
|
+
}),
|
|
61
|
+
SqliteAst.column({
|
|
62
|
+
name: 'completed',
|
|
63
|
+
type: { _tag: 'integer' },
|
|
64
|
+
nullable: false,
|
|
65
|
+
primaryKey: false,
|
|
66
|
+
autoIncrement: false,
|
|
67
|
+
default: Option.some(0),
|
|
68
|
+
schema: Schema.Number,
|
|
69
|
+
}),
|
|
70
|
+
],
|
|
71
|
+
[],
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
migrateTable({ db, tableAst: table, behaviour: 'create-if-not-exists', skipMetaTable: true }).pipe(Effect.runSync)
|
|
75
|
+
|
|
76
|
+
const createStmt = executed.find((s) => /create table if not exists/i.test(s))
|
|
77
|
+
expect(createStmt).toBeDefined()
|
|
78
|
+
|
|
79
|
+
// Identifiers must be double-quoted, not single-quoted
|
|
80
|
+
expect(createStmt!).toContain('create table if not exists "todos"')
|
|
81
|
+
expect(createStmt!).toContain('"id" integer primary key autoincrement')
|
|
82
|
+
expect(createStmt!).toContain(" default ''")
|
|
83
|
+
expect(createStmt!).not.toContain("PRIMARY KEY ('id')")
|
|
84
|
+
expect(createStmt!).not.toMatch(/'todos'|'id'|'text'/)
|
|
85
|
+
})
|
|
86
|
+
})
|