@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
package/src/schema/schema.ts
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
import { isReadonlyArray, shouldNeverHappen } from '@livestore/utils'
|
|
2
2
|
|
|
3
3
|
import type { MigrationOptions } from '../adapter-types.ts'
|
|
4
|
-
import type { EventDef, EventDefRecord, Materializer } from './EventDef.ts'
|
|
4
|
+
import type { EventDef, EventDefRecord, Materializer } from './EventDef/mod.ts'
|
|
5
5
|
import { tableIsClientDocumentTable } from './state/sqlite/client-document-def.ts'
|
|
6
6
|
import type { SqliteDsl } from './state/sqlite/db-schema/mod.ts'
|
|
7
|
-
import { stateSystemTables } from './state/sqlite/system-tables.ts'
|
|
7
|
+
import { stateSystemTables } from './state/sqlite/system-tables/state-tables.ts'
|
|
8
8
|
import type { TableDef } from './state/sqlite/table-def.ts'
|
|
9
|
+
import type { UnknownEvents } from './unknown-events.ts'
|
|
10
|
+
import { normalizeUnknownEventHandling } from './unknown-events.ts'
|
|
9
11
|
|
|
10
12
|
export const LiveStoreSchemaSymbol = Symbol.for('livestore.LiveStoreSchema')
|
|
11
13
|
export type LiveStoreSchemaSymbol = typeof LiveStoreSchemaSymbol
|
|
12
14
|
|
|
15
|
+
export const UNKNOWN_EVENT_SCHEMA_HASH = -1
|
|
16
|
+
|
|
13
17
|
export interface LiveStoreSchema<
|
|
14
18
|
TDbSchema extends SqliteDsl.DbSchema = SqliteDsl.DbSchema,
|
|
15
19
|
TEventsDefRecord extends EventDefRecord = EventDefRecord,
|
|
@@ -22,6 +26,7 @@ export interface LiveStoreSchema<
|
|
|
22
26
|
|
|
23
27
|
readonly state: InternalState
|
|
24
28
|
readonly eventsDefsMap: Map<string, EventDef.AnyWithoutFn>
|
|
29
|
+
readonly unknownEventHandling: UnknownEvents.HandlingConfig
|
|
25
30
|
readonly devtools: {
|
|
26
31
|
/** @default 'default' */
|
|
27
32
|
readonly alias: string
|
|
@@ -32,6 +37,30 @@ export namespace LiveStoreSchema {
|
|
|
32
37
|
export type Any = LiveStoreSchema<any, any>
|
|
33
38
|
}
|
|
34
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Runtime type guard for LiveStoreSchema.
|
|
42
|
+
*
|
|
43
|
+
* The guard intentionally performs lightweight structural checks that are
|
|
44
|
+
* stable across implementations. It verifies the identifying symbol marker
|
|
45
|
+
* and the presence of core maps/state used at runtime.
|
|
46
|
+
*/
|
|
47
|
+
export const isLiveStoreSchema = (value: unknown): value is LiveStoreSchema<any, any> => {
|
|
48
|
+
if (typeof value !== 'object' || value === null) return false
|
|
49
|
+
|
|
50
|
+
const v: any = value
|
|
51
|
+
|
|
52
|
+
// Identity marker must match exactly
|
|
53
|
+
if (v.LiveStoreSchemaSymbol !== LiveStoreSchemaSymbol) return false
|
|
54
|
+
|
|
55
|
+
// Core structures used at runtime
|
|
56
|
+
const hasEventsMap = v.eventsDefsMap instanceof Map
|
|
57
|
+
const hasStateSqliteTables = v.state?.sqlite?.tables instanceof Map
|
|
58
|
+
const hasStateMaterializers = v.state?.materializers instanceof Map
|
|
59
|
+
const hasDevtoolsAlias = typeof v.devtools?.alias === 'string'
|
|
60
|
+
|
|
61
|
+
return hasEventsMap && hasStateSqliteTables && hasStateMaterializers && hasDevtoolsAlias
|
|
62
|
+
}
|
|
63
|
+
|
|
35
64
|
// TODO abstract this further away from sqlite/tables
|
|
36
65
|
export interface InternalState {
|
|
37
66
|
readonly sqlite: {
|
|
@@ -55,6 +84,10 @@ export interface InputSchema {
|
|
|
55
84
|
*/
|
|
56
85
|
readonly alias?: string
|
|
57
86
|
}
|
|
87
|
+
/**
|
|
88
|
+
* Configures how unknown events should be handled. Defaults to `{ strategy: 'warn' }`.
|
|
89
|
+
*/
|
|
90
|
+
readonly unknownEventHandling?: UnknownEvents.HandlingConfig
|
|
58
91
|
}
|
|
59
92
|
|
|
60
93
|
export const makeSchema = <TInputSchema extends InputSchema>(
|
|
@@ -89,12 +122,15 @@ export const makeSchema = <TInputSchema extends InputSchema>(
|
|
|
89
122
|
}
|
|
90
123
|
}
|
|
91
124
|
|
|
125
|
+
const unknownEventHandling = normalizeUnknownEventHandling(inputSchema.unknownEventHandling)
|
|
126
|
+
|
|
92
127
|
return {
|
|
93
128
|
LiveStoreSchemaSymbol,
|
|
94
129
|
_DbSchemaType: Symbol.for('livestore.DbSchemaType') as any,
|
|
95
130
|
_EventDefMapType: Symbol.for('livestore.EventDefMapType') as any,
|
|
96
131
|
state,
|
|
97
132
|
eventsDefsMap,
|
|
133
|
+
unknownEventHandling,
|
|
98
134
|
devtools: {
|
|
99
135
|
alias: inputSchema.devtools?.alias ?? 'default',
|
|
100
136
|
},
|
|
@@ -110,7 +146,7 @@ export const getEventDef = <TSchema extends LiveStoreSchema>(
|
|
|
110
146
|
} => {
|
|
111
147
|
const eventDef = schema.eventsDefsMap.get(eventName)
|
|
112
148
|
if (eventDef === undefined) {
|
|
113
|
-
return shouldNeverHappen(`No
|
|
149
|
+
return shouldNeverHappen(`No event definition found for \`${eventName}\`.`)
|
|
114
150
|
}
|
|
115
151
|
const materializer = schema.state.materializers.get(eventName)
|
|
116
152
|
if (materializer === undefined) {
|
|
@@ -2,7 +2,7 @@ import { Schema } from '@livestore/utils/effect'
|
|
|
2
2
|
import { describe, expect, test } from 'vitest'
|
|
3
3
|
|
|
4
4
|
import { tables } from '../../../__tests__/fixture.ts'
|
|
5
|
-
import type * as LiveStoreEvent from '../../LiveStoreEvent.ts'
|
|
5
|
+
import type * as LiveStoreEvent from '../../LiveStoreEvent/mod.ts'
|
|
6
6
|
import { ClientDocumentTableDefSymbol, clientDocument, mergeDefaultValues } from './client-document-def.ts'
|
|
7
7
|
|
|
8
8
|
describe('client document table', () => {
|
|
@@ -50,6 +50,7 @@ describe('client document table', () => {
|
|
|
50
50
|
currentFacts: new Map(),
|
|
51
51
|
query: {} as any, // unused
|
|
52
52
|
eventDef: Doc[ClientDocumentTableDefSymbol].derived.setEventDef,
|
|
53
|
+
event: {} as any, // unused in this test
|
|
53
54
|
})
|
|
54
55
|
}
|
|
55
56
|
|
|
@@ -230,10 +231,26 @@ describe('client document table', () => {
|
|
|
230
231
|
}
|
|
231
232
|
`)
|
|
232
233
|
})
|
|
234
|
+
|
|
235
|
+
test('any value (Schema.Any) should fully replace', () => {
|
|
236
|
+
expect(forSchema(Schema.Any, { a: 1 }, 'id1')).toMatchInlineSnapshot(`
|
|
237
|
+
{
|
|
238
|
+
"bindValues": [
|
|
239
|
+
"id1",
|
|
240
|
+
"{"a":1}",
|
|
241
|
+
"{"a":1}",
|
|
242
|
+
],
|
|
243
|
+
"sql": "INSERT INTO 'test' (id, value) VALUES (?, ?) ON CONFLICT (id) DO UPDATE SET value = ?",
|
|
244
|
+
"writeTables": Set {
|
|
245
|
+
"test",
|
|
246
|
+
},
|
|
247
|
+
}
|
|
248
|
+
`)
|
|
249
|
+
})
|
|
233
250
|
})
|
|
234
251
|
})
|
|
235
252
|
|
|
236
|
-
const patchId = (muationEvent: LiveStoreEvent.
|
|
253
|
+
const patchId = (muationEvent: LiveStoreEvent.Input.Decoded) => {
|
|
237
254
|
// TODO use new id paradigm
|
|
238
255
|
const id = `00000000-0000-0000-0000-000000000000`
|
|
239
256
|
return { ...muationEvent, id }
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { shouldNeverHappen } from '@livestore/utils'
|
|
2
2
|
import type { Option, Types } from '@livestore/utils/effect'
|
|
3
|
-
import { Schema
|
|
3
|
+
import { Schema } from '@livestore/utils/effect'
|
|
4
4
|
|
|
5
5
|
import { SessionIdSymbol } from '../../../adapter-types.ts'
|
|
6
6
|
import { sql } from '../../../util.ts'
|
|
7
|
-
import type { EventDef, Materializer } from '../../EventDef.ts'
|
|
8
|
-
import { defineEvent, defineMaterializer } from '../../EventDef.ts'
|
|
7
|
+
import type { EventDef, Materializer } from '../../EventDef/mod.ts'
|
|
8
|
+
import { defineEvent, defineMaterializer } from '../../EventDef/mod.ts'
|
|
9
9
|
import { SqliteDsl } from './db-schema/mod.ts'
|
|
10
10
|
import type { QueryBuilder, QueryBuilderAst } from './query-builder/mod.ts'
|
|
11
11
|
import { QueryBuilderAstSymbol, QueryBuilderTypeId } from './query-builder/mod.ts'
|
|
@@ -62,9 +62,16 @@ export const clientDocument = <
|
|
|
62
62
|
},
|
|
63
63
|
} satisfies ClientDocumentTableOptions<TType>
|
|
64
64
|
|
|
65
|
+
// Column needs optimistic schema to read historical data formats
|
|
66
|
+
const optimisticColumnSchema = createOptimisticEventSchema({
|
|
67
|
+
valueSchema,
|
|
68
|
+
defaultValue: options.default.value,
|
|
69
|
+
partialSet: false, // Column always stores full documents
|
|
70
|
+
})
|
|
71
|
+
|
|
65
72
|
const columns = {
|
|
66
73
|
id: SqliteDsl.text({ primaryKey: true }),
|
|
67
|
-
value: SqliteDsl.json({ schema:
|
|
74
|
+
value: SqliteDsl.json({ schema: optimisticColumnSchema }),
|
|
68
75
|
}
|
|
69
76
|
|
|
70
77
|
const tableDef = table({ name, columns })
|
|
@@ -140,6 +147,105 @@ export const mergeDefaultValues = <T>(defaultValues: T, explicitDefaultValues: T
|
|
|
140
147
|
}, {} as any)
|
|
141
148
|
}
|
|
142
149
|
|
|
150
|
+
/**
|
|
151
|
+
* Creates an optimistic schema that accepts historical event formats
|
|
152
|
+
* and transforms them to the current schema, preserving data and intent.
|
|
153
|
+
*
|
|
154
|
+
* Decision Matrix for Schema Changes:
|
|
155
|
+
*
|
|
156
|
+
* | Change Type | Partial Set | Full Set | Strategy |
|
|
157
|
+
* |---------------------|---------------------|----------------------------------|-------------------------|
|
|
158
|
+
* | **Compatible Changes** |
|
|
159
|
+
* | Add optional field | Preserve existing | Preserve existing, new field undefined | Direct decode or merge |
|
|
160
|
+
* | Add required field | Preserve existing | Preserve existing, new field from default | Merge with defaults |
|
|
161
|
+
* | **Incompatible Changes** |
|
|
162
|
+
* | Remove field | Drop removed field | Drop removed field, preserve others | Filter & decode |
|
|
163
|
+
* | Type change | Use default for field | Use default for changed field | Selective merge |
|
|
164
|
+
* | Rename field | Use default | Use default (can't detect rename) | Fall back to default |
|
|
165
|
+
* | **Edge Cases** |
|
|
166
|
+
* | Empty event | Return {} | Return full default | Fallback handling |
|
|
167
|
+
* | Invalid structure | Return {} | Return full default | Fallback handling |
|
|
168
|
+
*/
|
|
169
|
+
export const createOptimisticEventSchema = ({
|
|
170
|
+
valueSchema,
|
|
171
|
+
defaultValue,
|
|
172
|
+
partialSet,
|
|
173
|
+
}: {
|
|
174
|
+
valueSchema: Schema.Schema<any, any>
|
|
175
|
+
defaultValue: any
|
|
176
|
+
partialSet: boolean
|
|
177
|
+
}) => {
|
|
178
|
+
const targetSchema = partialSet ? Schema.partial(valueSchema) : valueSchema
|
|
179
|
+
|
|
180
|
+
return Schema.transform(
|
|
181
|
+
Schema.Unknown, // Accept any historical event structure
|
|
182
|
+
targetSchema, // Output current schema
|
|
183
|
+
{
|
|
184
|
+
decode: (eventValue) => {
|
|
185
|
+
// Try direct decode first (for current schema events)
|
|
186
|
+
try {
|
|
187
|
+
return Schema.decodeUnknownSync(targetSchema)(eventValue)
|
|
188
|
+
} catch {
|
|
189
|
+
// Optimistic decoding for historical events
|
|
190
|
+
|
|
191
|
+
// Handle null/undefined/non-object cases
|
|
192
|
+
if (typeof eventValue !== 'object' || eventValue === null) {
|
|
193
|
+
console.warn(`Client document: Non-object event value, using ${partialSet ? 'empty partial' : 'defaults'}`)
|
|
194
|
+
return partialSet ? {} : defaultValue
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (partialSet) {
|
|
198
|
+
// For partial sets: only preserve fields that exist in new schema
|
|
199
|
+
const partialResult: Record<string, unknown> = {}
|
|
200
|
+
let hasValidFields = false
|
|
201
|
+
|
|
202
|
+
for (const [key, value] of Object.entries(eventValue as Record<string, unknown>)) {
|
|
203
|
+
if (key in defaultValue) {
|
|
204
|
+
partialResult[key] = value
|
|
205
|
+
hasValidFields = true
|
|
206
|
+
}
|
|
207
|
+
// Drop fields that don't exist in new schema
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (hasValidFields) {
|
|
211
|
+
try {
|
|
212
|
+
return Schema.decodeUnknownSync(targetSchema)(partialResult)
|
|
213
|
+
} catch {
|
|
214
|
+
// Even filtered fields don't match schema
|
|
215
|
+
console.warn('Client document: Partial fields incompatible, returning empty partial')
|
|
216
|
+
return {}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return {}
|
|
220
|
+
} else {
|
|
221
|
+
// Full set: merge old data with new defaults
|
|
222
|
+
const merged: Record<string, unknown> = { ...defaultValue }
|
|
223
|
+
|
|
224
|
+
// Override defaults with valid fields from old event
|
|
225
|
+
for (const [key, value] of Object.entries(eventValue as Record<string, unknown>)) {
|
|
226
|
+
if (key in defaultValue) {
|
|
227
|
+
merged[key] = value
|
|
228
|
+
}
|
|
229
|
+
// Drop fields that don't exist in new schema
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Try to decode the merged value
|
|
233
|
+
try {
|
|
234
|
+
return Schema.decodeUnknownSync(valueSchema)(merged)
|
|
235
|
+
} catch {
|
|
236
|
+
// Merged value still doesn't match (e.g., type changes)
|
|
237
|
+
// Fall back to pure defaults
|
|
238
|
+
console.warn('Client document: Could not preserve event data, using defaults')
|
|
239
|
+
return defaultValue
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
},
|
|
244
|
+
encode: (value) => value, // Pass-through for encoding
|
|
245
|
+
},
|
|
246
|
+
)
|
|
247
|
+
}
|
|
248
|
+
|
|
143
249
|
export const deriveEventAndMaterializer = ({
|
|
144
250
|
name,
|
|
145
251
|
valueSchema,
|
|
@@ -155,7 +261,7 @@ export const deriveEventAndMaterializer = ({
|
|
|
155
261
|
name: `${name}Set`,
|
|
156
262
|
schema: Schema.Struct({
|
|
157
263
|
id: Schema.Union(Schema.String, Schema.UniqueSymbolFromSelf(SessionIdSymbol)),
|
|
158
|
-
value:
|
|
264
|
+
value: createOptimisticEventSchema({ valueSchema, defaultValue, partialSet }),
|
|
159
265
|
}).annotations({ title: `${name}Set:Args` }),
|
|
160
266
|
clientOnly: true,
|
|
161
267
|
derived: true,
|
|
@@ -167,7 +273,7 @@ export const deriveEventAndMaterializer = ({
|
|
|
167
273
|
}
|
|
168
274
|
|
|
169
275
|
// Override the full value if it's not an object or no partial set is allowed
|
|
170
|
-
const schemaProps =
|
|
276
|
+
const schemaProps = Schema.getResolvedPropertySignatures(valueSchema)
|
|
171
277
|
if (schemaProps.length === 0 || partialSet === false) {
|
|
172
278
|
const valueColJsonSchema = Schema.parseJson(valueSchema)
|
|
173
279
|
const encodedInsertValue = Schema.encodeSyncDebug(valueColJsonSchema)(value ?? defaultValue)
|
|
@@ -281,8 +387,14 @@ export namespace ClientDocumentTableOptions {
|
|
|
281
387
|
}
|
|
282
388
|
}
|
|
283
389
|
|
|
390
|
+
type IsStructLike<T> = T extends {} ? true : false
|
|
391
|
+
|
|
284
392
|
export type WithDefaults<TInput extends Input<any>> = {
|
|
285
|
-
partialSet: TInput['partialSet'] extends false
|
|
393
|
+
partialSet: TInput['partialSet'] extends false
|
|
394
|
+
? false
|
|
395
|
+
: IsStructLike<TInput['default']['value']> extends true
|
|
396
|
+
? true
|
|
397
|
+
: false
|
|
286
398
|
default: {
|
|
287
399
|
id: TInput['default']['id'] extends string | SessionIdSymbol ? TInput['default']['id'] : undefined
|
|
288
400
|
value: TInput['default']['value']
|
|
@@ -467,5 +579,5 @@ export namespace ClientDocumentTableDef {
|
|
|
467
579
|
) => QueryBuilder<TType, ClientDocumentTableDef.TableDefBase_<TName, TType>, QueryBuilder.ApiFeature>
|
|
468
580
|
}
|
|
469
581
|
|
|
470
|
-
export const ClientDocumentTableDefSymbol = Symbol('ClientDocumentTableDef')
|
|
582
|
+
export const ClientDocumentTableDefSymbol = Symbol.for('livestore.ClientDocumentTableDef')
|
|
471
583
|
export type ClientDocumentTableDefSymbol = typeof ClientDocumentTableDefSymbol
|
|
@@ -65,7 +65,7 @@ describe.concurrent('annotations', () => {
|
|
|
65
65
|
})
|
|
66
66
|
|
|
67
67
|
test('Union of same type with compatible column type', () => {
|
|
68
|
-
const unionSchema = Schema.
|
|
68
|
+
const unionSchema = Schema.Literal('a', 'b')
|
|
69
69
|
expect(() => withColumnType(unionSchema, 'text')).not.toThrow()
|
|
70
70
|
})
|
|
71
71
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Schema } from '@livestore/utils/effect'
|
|
2
|
-
import { dual } from '@livestore/utils/effect'
|
|
2
|
+
import { dual, Option, SchemaAST } from '@livestore/utils/effect'
|
|
3
3
|
import type { SqliteDsl } from './db-schema/mod.ts'
|
|
4
4
|
|
|
5
5
|
export const PrimaryKeyId = Symbol.for('livestore/state/sqlite/annotations/primary-key')
|
|
@@ -32,7 +32,7 @@ Here are the knobs you can turn per-column when you CREATE TABLE (or ALTER TABLE
|
|
|
32
32
|
* Adds a primary key annotation to a schema.
|
|
33
33
|
*/
|
|
34
34
|
export const withPrimaryKey = <T extends Schema.Schema.All>(schema: T) =>
|
|
35
|
-
schema
|
|
35
|
+
applyAnnotations(schema, { [PrimaryKeyId]: true })
|
|
36
36
|
|
|
37
37
|
/**
|
|
38
38
|
* Adds a column type annotation to a schema.
|
|
@@ -43,19 +43,19 @@ export const withColumnType: {
|
|
|
43
43
|
<T extends Schema.Schema.All>(schema: T, type: SqliteDsl.FieldColumnType): T
|
|
44
44
|
} = dual(2, <T extends Schema.Schema.All>(schema: T, type: SqliteDsl.FieldColumnType) => {
|
|
45
45
|
validateSchemaColumnTypeCompatibility(schema, type)
|
|
46
|
-
return schema
|
|
46
|
+
return applyAnnotations(schema, { [ColumnType]: type })
|
|
47
47
|
})
|
|
48
48
|
|
|
49
49
|
/**
|
|
50
50
|
* Adds an auto-increment annotation to a schema.
|
|
51
51
|
*/
|
|
52
52
|
export const withAutoIncrement = <T extends Schema.Schema.All>(schema: T) =>
|
|
53
|
-
schema
|
|
53
|
+
applyAnnotations(schema, { [AutoIncrement]: true })
|
|
54
54
|
|
|
55
55
|
/**
|
|
56
56
|
* Adds a unique constraint annotation to a schema.
|
|
57
57
|
*/
|
|
58
|
-
export const withUnique = <T extends Schema.Schema.All>(schema: T) => schema
|
|
58
|
+
export const withUnique = <T extends Schema.Schema.All>(schema: T) => applyAnnotations(schema, { [Unique]: true })
|
|
59
59
|
|
|
60
60
|
/**
|
|
61
61
|
* Adds a default value annotation to a schema.
|
|
@@ -64,7 +64,7 @@ export const withDefault: {
|
|
|
64
64
|
// TODO make type safe
|
|
65
65
|
<T extends Schema.Schema.All>(schema: T, value: unknown): T
|
|
66
66
|
(value: unknown): <T extends Schema.Schema.All>(schema: T) => T
|
|
67
|
-
} = dual(2, <T extends Schema.Schema.All>(schema: T, value: unknown) => schema
|
|
67
|
+
} = dual(2, <T extends Schema.Schema.All>(schema: T, value: unknown) => applyAnnotations(schema, { [Default]: value }))
|
|
68
68
|
|
|
69
69
|
/**
|
|
70
70
|
* Validates that a schema is compatible with the specified SQLite column type
|
|
@@ -75,3 +75,13 @@ const validateSchemaColumnTypeCompatibility = (
|
|
|
75
75
|
): void => {
|
|
76
76
|
// TODO actually implement this
|
|
77
77
|
}
|
|
78
|
+
|
|
79
|
+
const applyAnnotations = <T extends Schema.Schema.All>(schema: T, overrides: Record<PropertyKey, unknown>): T => {
|
|
80
|
+
const identifier = SchemaAST.getIdentifierAnnotation(schema.ast)
|
|
81
|
+
const shouldPreserveIdentifier = Option.isSome(identifier) && !(SchemaAST.IdentifierAnnotationId in overrides)
|
|
82
|
+
const annotations: Record<PropertyKey, unknown> = shouldPreserveIdentifier
|
|
83
|
+
? { ...overrides, [SchemaAST.IdentifierAnnotationId]: identifier.value }
|
|
84
|
+
: overrides
|
|
85
|
+
|
|
86
|
+
return schema.annotations(annotations) as T
|
|
87
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Schema } from '@livestore/utils/effect'
|
|
2
|
-
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { assert, describe, expect, it } from 'vitest'
|
|
3
3
|
|
|
4
4
|
import * as State from '../mod.ts'
|
|
5
5
|
import { withAutoIncrement, withColumnType, withDefault, withPrimaryKey, withUnique } from './column-annotations.ts'
|
|
@@ -273,17 +273,34 @@ describe('getColumnDefForSchema', () => {
|
|
|
273
273
|
INACTIVE: 'inactive',
|
|
274
274
|
})
|
|
275
275
|
|
|
276
|
-
const StatusUnion = Schema.
|
|
276
|
+
const StatusUnion = Schema.Literal('pending', 'active', 'inactive')
|
|
277
277
|
|
|
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
|
|
|
@@ -381,6 +398,19 @@ describe('getColumnDefForSchema', () => {
|
|
|
381
398
|
expect((table.rowSchema as any).fields.count.toString()).toBe('Int | null')
|
|
382
399
|
})
|
|
383
400
|
|
|
401
|
+
it('should treat unions of string literals as text columns without JSON parsing', () => {
|
|
402
|
+
const schema = Schema.Struct({
|
|
403
|
+
id: Schema.String,
|
|
404
|
+
status: Schema.Literal('idle', 'running', 'stopped'),
|
|
405
|
+
})
|
|
406
|
+
|
|
407
|
+
const table = State.SQLite.table({ name: 'timers', schema })
|
|
408
|
+
|
|
409
|
+
expect(table.sqliteDef.columns.status.columnType).toBe('text')
|
|
410
|
+
expect(table.sqliteDef.columns.status.schema.toString()).toBe('"idle" | "running" | "stopped"')
|
|
411
|
+
expect((table.rowSchema as any).fields.status.toString()).toBe('"idle" | "running" | "stopped"')
|
|
412
|
+
})
|
|
413
|
+
|
|
384
414
|
it('should handle Schema.NullOr with complex types', () => {
|
|
385
415
|
const schema = Schema.Struct({
|
|
386
416
|
data: Schema.NullOr(Schema.Struct({ value: Schema.Number })),
|
|
@@ -539,7 +569,7 @@ describe('getColumnDefForSchema', () => {
|
|
|
539
569
|
|
|
540
570
|
it('should work with column type annotation', () => {
|
|
541
571
|
const UserSchema = Schema.Struct({
|
|
542
|
-
id: Schema.Number.pipe(withColumnType('integer')
|
|
572
|
+
id: Schema.Number.pipe(withColumnType('integer'), withPrimaryKey),
|
|
543
573
|
name: Schema.String,
|
|
544
574
|
})
|
|
545
575
|
|
|
@@ -571,7 +601,7 @@ describe('getColumnDefForSchema', () => {
|
|
|
571
601
|
describe('withAutoIncrement', () => {
|
|
572
602
|
it('should add autoIncrement annotation to schema', () => {
|
|
573
603
|
const UserSchema = Schema.Struct({
|
|
574
|
-
id: Schema.Int.pipe(withPrimaryKey
|
|
604
|
+
id: Schema.Int.pipe(withPrimaryKey, withAutoIncrement),
|
|
575
605
|
name: Schema.String,
|
|
576
606
|
})
|
|
577
607
|
const userTable = State.SQLite.table({
|
|
@@ -623,6 +653,29 @@ describe('getColumnDefForSchema', () => {
|
|
|
623
653
|
table2.sqliteDef.columns.count.default._tag === 'Some' && table2.sqliteDef.columns.count.default.value,
|
|
624
654
|
).toBe(0)
|
|
625
655
|
})
|
|
656
|
+
|
|
657
|
+
it('should support thunk defaults without eager evaluation', () => {
|
|
658
|
+
let counter = 0
|
|
659
|
+
const UserSchema = Schema.Struct({
|
|
660
|
+
id: Schema.String.pipe(
|
|
661
|
+
withDefault(() => {
|
|
662
|
+
counter += 1
|
|
663
|
+
return `user-${counter}`
|
|
664
|
+
}),
|
|
665
|
+
),
|
|
666
|
+
})
|
|
667
|
+
|
|
668
|
+
const table = State.SQLite.table({ name: 'users_with_thunk', schema: UserSchema })
|
|
669
|
+
|
|
670
|
+
expect(counter).toBe(0)
|
|
671
|
+
expect(table.sqliteDef.columns.id.default._tag).toBe('Some')
|
|
672
|
+
if (table.sqliteDef.columns.id.default._tag === 'Some') {
|
|
673
|
+
const defaultThunk = table.sqliteDef.columns.id.default.value
|
|
674
|
+
assert(typeof defaultThunk === 'function')
|
|
675
|
+
expect(defaultThunk()).toBe('user-1')
|
|
676
|
+
expect(defaultThunk()).toBe('user-2')
|
|
677
|
+
}
|
|
678
|
+
})
|
|
626
679
|
})
|
|
627
680
|
|
|
628
681
|
describe('withUnique', () => {
|
|
@@ -668,7 +721,7 @@ describe('getColumnDefForSchema', () => {
|
|
|
668
721
|
|
|
669
722
|
describe('combined annotations', () => {
|
|
670
723
|
it('should work with multiple annotations', () => {
|
|
671
|
-
const schema = Schema.Uint8ArrayFromBase64.pipe(withColumnType('blob')
|
|
724
|
+
const schema = Schema.Uint8ArrayFromBase64.pipe(withColumnType('blob'), withPrimaryKey)
|
|
672
725
|
|
|
673
726
|
const UserSchema = Schema.Struct({
|
|
674
727
|
id: schema,
|
|
@@ -686,7 +739,7 @@ describe('getColumnDefForSchema', () => {
|
|
|
686
739
|
|
|
687
740
|
it('should combine all annotations', () => {
|
|
688
741
|
const UserSchema = Schema.Struct({
|
|
689
|
-
id: Schema.Int.pipe(withPrimaryKey
|
|
742
|
+
id: Schema.Int.pipe(withPrimaryKey, withAutoIncrement),
|
|
690
743
|
email: Schema.String.pipe(withUnique),
|
|
691
744
|
status: Schema.String.pipe(withDefault('active')),
|
|
692
745
|
metadata: Schema.Unknown.pipe(withColumnType('text')),
|
|
@@ -179,26 +179,17 @@ 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 })
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
// Literals based on their encoded type
|
|
189
|
-
if (SchemaAST.isLiteral(encodedAst)) {
|
|
190
|
-
const value = encodedAst.literal
|
|
191
|
-
if (typeof value === 'string') return SqliteDsl.text({ schema: coreSchema, nullable })
|
|
192
|
-
if (typeof value === 'number') {
|
|
193
|
-
// Check if the original schema is Int
|
|
194
|
-
const id = SchemaAST.getIdentifierAnnotation(coreAst).pipe(Option.getOrElse(() => ''))
|
|
195
|
-
if (id === 'Int') {
|
|
196
|
-
return SqliteDsl.integer({ schema: coreSchema, nullable })
|
|
197
|
-
}
|
|
198
|
-
return SqliteDsl.real({ schema: coreSchema, nullable })
|
|
199
|
-
}
|
|
182
|
+
if (isUint8ArraySchema(coreAst) || isUint8ArraySchema(encodedAst)) {
|
|
183
|
+
return SqliteDsl.blob({ schema: Schema.Uint8ArrayFromSelf as Schema.Schema<Uint8Array<ArrayBuffer>>, nullable })
|
|
200
184
|
}
|
|
201
185
|
|
|
186
|
+
const literalColumn = getLiteralColumnDefinition(encodedAst, coreSchema, nullable, coreAst)
|
|
187
|
+
if (literalColumn) return literalColumn
|
|
188
|
+
|
|
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
|
|
192
|
+
|
|
202
193
|
// Everything else needs JSON encoding
|
|
203
194
|
return SqliteDsl.json({ schema: coreSchema, nullable })
|
|
204
195
|
}
|
|
@@ -206,10 +197,86 @@ const getColumnForSchema = (schema: Schema.Schema.AnyNoContext, nullable = false
|
|
|
206
197
|
const stripNullable = (ast: SchemaAST.AST): SchemaAST.AST => {
|
|
207
198
|
if (!SchemaAST.isUnion(ast)) return ast
|
|
208
199
|
|
|
209
|
-
//
|
|
210
|
-
const
|
|
200
|
+
// Filter out null/undefined members while preserving any annotations on the union
|
|
201
|
+
const coreTypes = ast.types.filter(
|
|
211
202
|
(type) => !(SchemaAST.isLiteral(type) && type.literal === null) && !SchemaAST.isUndefinedKeyword(type),
|
|
212
203
|
)
|
|
213
204
|
|
|
214
|
-
|
|
205
|
+
if (coreTypes.length === 0 || coreTypes.length === ast.types.length) {
|
|
206
|
+
return ast
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (coreTypes.length === 1) {
|
|
210
|
+
return coreTypes[0]!
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return SchemaAST.Union.make(coreTypes, ast.annotations)
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const getLiteralColumnDefinition = (
|
|
217
|
+
ast: SchemaAST.AST,
|
|
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
|
|
215
282
|
}
|