@livestore/common 0.4.0-dev.21 → 0.4.0-dev.23
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 +16 -9
- package/dist/ClientSessionLeaderThreadProxy.d.ts.map +1 -1
- package/dist/ClientSessionLeaderThreadProxy.js.map +1 -1
- package/dist/WorkerTransportError.d.ts +11 -0
- package/dist/WorkerTransportError.d.ts.map +1 -0
- package/dist/WorkerTransportError.js +11 -0
- package/dist/WorkerTransportError.js.map +1 -0
- package/dist/adapter-types.d.ts +26 -3
- package/dist/adapter-types.d.ts.map +1 -1
- package/dist/adapter-types.js +27 -1
- package/dist/adapter-types.js.map +1 -1
- package/dist/bounded-collections.d.ts.map +1 -1
- package/dist/bounded-collections.js +6 -4
- package/dist/bounded-collections.js.map +1 -1
- package/dist/debug-info.js +4 -4
- package/dist/debug-info.js.map +1 -1
- package/dist/devtools/devtools-messages-client-session.d.ts +42 -22
- package/dist/devtools/devtools-messages-client-session.d.ts.map +1 -1
- package/dist/devtools/devtools-messages-client-session.js +12 -1
- package/dist/devtools/devtools-messages-client-session.js.map +1 -1
- package/dist/devtools/devtools-messages-common.d.ts +12 -6
- package/dist/devtools/devtools-messages-common.d.ts.map +1 -1
- package/dist/devtools/devtools-messages-common.js +8 -3
- package/dist/devtools/devtools-messages-common.js.map +1 -1
- package/dist/devtools/devtools-messages-leader.d.ts +45 -25
- package/dist/devtools/devtools-messages-leader.d.ts.map +1 -1
- package/dist/devtools/devtools-messages-leader.js +12 -1
- package/dist/devtools/devtools-messages-leader.js.map +1 -1
- package/dist/devtools/mod.js +1 -1
- package/dist/devtools/mod.js.map +1 -1
- package/dist/errors.d.ts +15 -15
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +11 -11
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/leader-thread/LeaderSyncProcessor.d.ts +20 -6
- package/dist/leader-thread/LeaderSyncProcessor.d.ts.map +1 -1
- package/dist/leader-thread/LeaderSyncProcessor.js +283 -253
- package/dist/leader-thread/LeaderSyncProcessor.js.map +1 -1
- package/dist/leader-thread/RejectedPushError.d.ts +107 -0
- package/dist/leader-thread/RejectedPushError.d.ts.map +1 -0
- package/dist/leader-thread/RejectedPushError.js +78 -0
- package/dist/leader-thread/RejectedPushError.js.map +1 -0
- package/dist/leader-thread/connection.js +1 -1
- package/dist/leader-thread/connection.js.map +1 -1
- package/dist/leader-thread/eventlog.d.ts.map +1 -1
- package/dist/leader-thread/eventlog.js +12 -11
- package/dist/leader-thread/eventlog.js.map +1 -1
- package/dist/leader-thread/leader-worker-devtools.d.ts +1 -2
- package/dist/leader-thread/leader-worker-devtools.d.ts.map +1 -1
- package/dist/leader-thread/leader-worker-devtools.js +34 -14
- package/dist/leader-thread/leader-worker-devtools.js.map +1 -1
- package/dist/leader-thread/make-leader-thread-layer.d.ts +12 -5
- package/dist/leader-thread/make-leader-thread-layer.d.ts.map +1 -1
- package/dist/leader-thread/make-leader-thread-layer.js +12 -11
- package/dist/leader-thread/make-leader-thread-layer.js.map +1 -1
- package/dist/leader-thread/make-leader-thread-layer.test.js +1 -1
- package/dist/leader-thread/make-leader-thread-layer.test.js.map +1 -1
- package/dist/leader-thread/materialize-event.d.ts.map +1 -1
- package/dist/leader-thread/materialize-event.js +7 -4
- package/dist/leader-thread/materialize-event.js.map +1 -1
- package/dist/leader-thread/recreate-db.js +1 -1
- 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/stream-events.d.ts.map +1 -1
- package/dist/leader-thread/stream-events.js +4 -3
- package/dist/leader-thread/stream-events.js.map +1 -1
- package/dist/leader-thread/types.d.ts +7 -6
- package/dist/leader-thread/types.d.ts.map +1 -1
- package/dist/leader-thread/types.js.map +1 -1
- package/dist/logging.js +4 -4
- package/dist/logging.js.map +1 -1
- package/dist/make-client-session.js +2 -2
- package/dist/make-client-session.js.map +1 -1
- package/dist/materializer-helper.js +6 -6
- package/dist/materializer-helper.js.map +1 -1
- package/dist/otel.d.ts +1 -1
- package/dist/otel.d.ts.map +1 -1
- package/dist/otel.js +2 -2
- package/dist/otel.js.map +1 -1
- package/dist/rematerialize-from-eventlog.d.ts +1 -1
- package/dist/rematerialize-from-eventlog.d.ts.map +1 -1
- package/dist/rematerialize-from-eventlog.js +11 -9
- package/dist/rematerialize-from-eventlog.js.map +1 -1
- package/dist/schema/EventDef/define.d.ts +16 -2
- package/dist/schema/EventDef/define.d.ts.map +1 -1
- package/dist/schema/EventDef/define.js +5 -4
- package/dist/schema/EventDef/define.js.map +1 -1
- package/dist/schema/EventDef/deprecated.d.ts +99 -0
- package/dist/schema/EventDef/deprecated.d.ts.map +1 -0
- package/dist/schema/EventDef/deprecated.js +144 -0
- package/dist/schema/EventDef/deprecated.js.map +1 -0
- package/dist/schema/EventDef/deprecated.test.d.ts +2 -0
- package/dist/schema/EventDef/deprecated.test.d.ts.map +1 -0
- package/dist/schema/EventDef/deprecated.test.js +95 -0
- package/dist/schema/EventDef/deprecated.test.js.map +1 -0
- package/dist/schema/EventDef/event-def.d.ts +4 -0
- package/dist/schema/EventDef/event-def.d.ts.map +1 -1
- package/dist/schema/EventDef/mod.d.ts +1 -0
- package/dist/schema/EventDef/mod.d.ts.map +1 -1
- package/dist/schema/EventDef/mod.js +1 -0
- package/dist/schema/EventDef/mod.js.map +1 -1
- package/dist/schema/EventSequenceNumber/client.d.ts.map +1 -1
- package/dist/schema/EventSequenceNumber/client.js +11 -11
- package/dist/schema/EventSequenceNumber/client.js.map +1 -1
- package/dist/schema/EventSequenceNumber.test.js +1 -1
- package/dist/schema/EventSequenceNumber.test.js.map +1 -1
- package/dist/schema/LiveStoreEvent/client.d.ts +6 -6
- package/dist/schema/LiveStoreEvent/client.d.ts.map +1 -1
- package/dist/schema/LiveStoreEvent/client.js +6 -3
- package/dist/schema/LiveStoreEvent/client.js.map +1 -1
- package/dist/schema/LiveStoreEvent/client.test.d.ts +2 -0
- package/dist/schema/LiveStoreEvent/client.test.d.ts.map +1 -0
- package/dist/schema/LiveStoreEvent/client.test.js +83 -0
- package/dist/schema/LiveStoreEvent/client.test.js.map +1 -0
- package/dist/schema/schema.d.ts.map +1 -1
- package/dist/schema/schema.js +7 -4
- package/dist/schema/schema.js.map +1 -1
- package/dist/schema/state/sqlite/client-document-def.d.ts +1 -0
- package/dist/schema/state/sqlite/client-document-def.d.ts.map +1 -1
- package/dist/schema/state/sqlite/client-document-def.js +34 -13
- package/dist/schema/state/sqlite/client-document-def.js.map +1 -1
- package/dist/schema/state/sqlite/client-document-def.test.js +121 -2
- 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 +1 -1
- 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.d.ts.map +1 -1
- package/dist/schema/state/sqlite/column-def.js +36 -34
- package/dist/schema/state/sqlite/column-def.js.map +1 -1
- package/dist/schema/state/sqlite/column-def.test.js +7 -6
- 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 +8 -8
- package/dist/schema/state/sqlite/column-spec.js.map +1 -1
- package/dist/schema/state/sqlite/column-spec.test.js +1 -1
- package/dist/schema/state/sqlite/column-spec.test.js.map +1 -1
- package/dist/schema/state/sqlite/db-schema/ast/sqlite.js +2 -2
- 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 +2 -2
- 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 +11 -2
- package/dist/schema/state/sqlite/db-schema/dsl/field-defs.js.map +1 -1
- package/dist/schema/state/sqlite/db-schema/dsl/field-defs.test.js +1 -1
- package/dist/schema/state/sqlite/db-schema/dsl/field-defs.test.js.map +1 -1
- package/dist/schema/state/sqlite/db-schema/dsl/mod.d.ts +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 +1 -1
- package/dist/schema/state/sqlite/db-schema/dsl/mod.js.map +1 -1
- package/dist/schema/state/sqlite/mod.d.ts.map +1 -1
- package/dist/schema/state/sqlite/mod.js +3 -5
- package/dist/schema/state/sqlite/mod.js.map +1 -1
- package/dist/schema/state/sqlite/query-builder/api.d.ts +37 -13
- 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 +77 -7
- package/dist/schema/state/sqlite/query-builder/astToSql.js.map +1 -1
- package/dist/schema/state/sqlite/query-builder/impl.d.ts +1 -1
- package/dist/schema/state/sqlite/query-builder/impl.d.ts.map +1 -1
- package/dist/schema/state/sqlite/query-builder/impl.js +28 -14
- package/dist/schema/state/sqlite/query-builder/impl.js.map +1 -1
- package/dist/schema/state/sqlite/query-builder/impl.test.js +112 -3
- package/dist/schema/state/sqlite/query-builder/impl.test.js.map +1 -1
- package/dist/schema/state/sqlite/schema-helpers.js +2 -2
- package/dist/schema/state/sqlite/schema-helpers.js.map +1 -1
- package/dist/schema/state/sqlite/table-def.d.ts +5 -3
- package/dist/schema/state/sqlite/table-def.d.ts.map +1 -1
- package/dist/schema/state/sqlite/table-def.js +1 -1
- package/dist/schema/state/sqlite/table-def.js.map +1 -1
- package/dist/schema/state/sqlite/table-def.test.js +57 -4
- package/dist/schema/state/sqlite/table-def.test.js.map +1 -1
- package/dist/schema/unknown-events.d.ts +1 -1
- package/dist/schema/unknown-events.d.ts.map +1 -1
- package/dist/schema/unknown-events.js +1 -1
- package/dist/schema/unknown-events.js.map +1 -1
- package/dist/schema-management/__tests__/migrations-autoincrement-quoting.test.js +1 -1
- package/dist/schema-management/__tests__/migrations-autoincrement-quoting.test.js.map +1 -1
- package/dist/schema-management/common.js +2 -2
- package/dist/schema-management/common.js.map +1 -1
- package/dist/schema-management/migrations.js +1 -1
- package/dist/schema-management/migrations.js.map +1 -1
- package/dist/sql-queries/sql-queries.js +8 -6
- 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.map +1 -1
- package/dist/sqlite-db-helper.js +3 -3
- package/dist/sqlite-db-helper.js.map +1 -1
- package/dist/sqlite-types.d.ts +2 -2
- package/dist/sqlite-types.d.ts.map +1 -1
- package/dist/sqlite-types.js.map +1 -1
- package/dist/sync/ClientSessionSyncProcessor.d.ts +8 -9
- package/dist/sync/ClientSessionSyncProcessor.d.ts.map +1 -1
- package/dist/sync/ClientSessionSyncProcessor.js +93 -107
- package/dist/sync/ClientSessionSyncProcessor.js.map +1 -1
- package/dist/sync/errors.d.ts +0 -38
- package/dist/sync/errors.d.ts.map +1 -1
- package/dist/sync/errors.js +3 -20
- package/dist/sync/errors.js.map +1 -1
- package/dist/sync/mock-sync-backend.d.ts +5 -3
- package/dist/sync/mock-sync-backend.d.ts.map +1 -1
- package/dist/sync/mock-sync-backend.js +70 -68
- package/dist/sync/mock-sync-backend.js.map +1 -1
- package/dist/sync/next/compact-events.js +6 -6
- package/dist/sync/next/compact-events.js.map +1 -1
- package/dist/sync/next/facts.d.ts.map +1 -1
- package/dist/sync/next/facts.js +6 -6
- package/dist/sync/next/facts.js.map +1 -1
- package/dist/sync/next/history-dag-common.d.ts.map +1 -1
- package/dist/sync/next/history-dag-common.js +6 -6
- package/dist/sync/next/history-dag-common.js.map +1 -1
- package/dist/sync/next/history-dag.js +3 -3
- package/dist/sync/next/history-dag.js.map +1 -1
- package/dist/sync/next/rebase-events.js +1 -1
- package/dist/sync/next/rebase-events.js.map +1 -1
- package/dist/sync/next/test/compact-events.calculator.test.js +2 -2
- package/dist/sync/next/test/compact-events.calculator.test.js.map +1 -1
- package/dist/sync/next/test/compact-events.test.d.ts.map +1 -1
- package/dist/sync/next/test/compact-events.test.js +2 -2
- package/dist/sync/next/test/compact-events.test.js.map +1 -1
- package/dist/sync/next/test/event-fixtures.d.ts.map +1 -1
- package/dist/sync/next/test/event-fixtures.js +2 -2
- package/dist/sync/next/test/event-fixtures.js.map +1 -1
- package/dist/sync/sync-backend-kv.d.ts.map +1 -1
- package/dist/sync/sync-backend-kv.js.map +1 -1
- package/dist/sync/sync-backend.d.ts +3 -3
- package/dist/sync/sync-backend.d.ts.map +1 -1
- package/dist/sync/sync-backend.js +1 -1
- package/dist/sync/sync-backend.js.map +1 -1
- package/dist/sync/sync.d.ts +20 -0
- package/dist/sync/sync.d.ts.map +1 -1
- package/dist/sync/syncstate.d.ts +4 -17
- package/dist/sync/syncstate.d.ts.map +1 -1
- package/dist/sync/syncstate.js +51 -74
- package/dist/sync/syncstate.js.map +1 -1
- package/dist/sync/syncstate.test.js +112 -96
- package/dist/sync/syncstate.test.js.map +1 -1
- package/dist/sync/transport-chunking.js +3 -3
- package/dist/sync/transport-chunking.js.map +1 -1
- 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 +4 -6
- package/dist/sync/validate-push-payload.js.map +1 -1
- package/dist/util.js +2 -2
- package/dist/util.js.map +1 -1
- package/dist/version.d.ts +7 -1
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +8 -4
- package/dist/version.js.map +1 -1
- package/package.json +66 -12
- package/src/ClientSessionLeaderThreadProxy.ts +16 -9
- package/src/WorkerTransportError.ts +12 -0
- package/src/adapter-types.ts +39 -3
- package/src/bounded-collections.ts +6 -5
- package/src/debug-info.ts +4 -4
- package/src/devtools/devtools-messages-client-session.ts +12 -0
- package/src/devtools/devtools-messages-common.ts +8 -4
- package/src/devtools/devtools-messages-leader.ts +12 -0
- package/src/devtools/mod.ts +1 -1
- package/src/errors.ts +18 -17
- package/src/index.ts +2 -0
- package/src/leader-thread/LeaderSyncProcessor.ts +417 -347
- package/src/leader-thread/RejectedPushError.ts +106 -0
- package/src/leader-thread/connection.ts +1 -1
- package/src/leader-thread/eventlog.ts +16 -14
- package/src/leader-thread/leader-worker-devtools.ts +107 -66
- package/src/leader-thread/make-leader-thread-layer.test.ts +1 -1
- package/src/leader-thread/make-leader-thread-layer.ts +41 -31
- package/src/leader-thread/materialize-event.ts +8 -4
- package/src/leader-thread/recreate-db.ts +1 -1
- package/src/leader-thread/shutdown-channel.ts +2 -6
- package/src/leader-thread/stream-events.ts +10 -5
- package/src/leader-thread/types.ts +7 -6
- package/src/logging.ts +4 -4
- package/src/make-client-session.ts +2 -2
- package/src/materializer-helper.ts +9 -9
- package/src/otel.ts +3 -2
- package/src/rematerialize-from-eventlog.ts +60 -60
- package/src/schema/EventDef/define.ts +22 -6
- package/src/schema/EventDef/deprecated.test.ts +129 -0
- package/src/schema/EventDef/deprecated.ts +175 -0
- package/src/schema/EventDef/event-def.ts +5 -0
- package/src/schema/EventDef/mod.ts +1 -0
- package/src/schema/EventSequenceNumber/client.ts +11 -11
- package/src/schema/EventSequenceNumber.test.ts +2 -1
- package/src/schema/LiveStoreEvent/client.test.ts +97 -0
- package/src/schema/LiveStoreEvent/client.ts +6 -3
- package/src/schema/schema.ts +9 -4
- package/src/schema/state/sqlite/client-document-def.test.ts +142 -3
- package/src/schema/state/sqlite/client-document-def.ts +37 -14
- package/src/schema/state/sqlite/column-annotations.test.ts +2 -1
- package/src/schema/state/sqlite/column-annotations.ts +2 -1
- package/src/schema/state/sqlite/column-def.test.ts +8 -6
- package/src/schema/state/sqlite/column-def.ts +41 -36
- package/src/schema/state/sqlite/column-spec.test.ts +3 -1
- package/src/schema/state/sqlite/column-spec.ts +9 -8
- package/src/schema/state/sqlite/db-schema/ast/sqlite.ts +2 -2
- package/src/schema/state/sqlite/db-schema/dsl/field-defs.test.ts +2 -1
- package/src/schema/state/sqlite/db-schema/dsl/field-defs.ts +13 -4
- package/src/schema/state/sqlite/db-schema/dsl/mod.ts +3 -3
- package/src/schema/state/sqlite/mod.ts +4 -5
- package/src/schema/state/sqlite/query-builder/api.ts +37 -8
- package/src/schema/state/sqlite/query-builder/astToSql.ts +87 -7
- package/src/schema/state/sqlite/query-builder/impl.test.ts +145 -3
- package/src/schema/state/sqlite/query-builder/impl.ts +26 -12
- package/src/schema/state/sqlite/schema-helpers.ts +2 -2
- package/src/schema/state/sqlite/table-def.test.ts +67 -4
- package/src/schema/state/sqlite/table-def.ts +8 -15
- package/src/schema/unknown-events.ts +2 -2
- package/src/schema-management/__tests__/migrations-autoincrement-quoting.test.ts +3 -1
- package/src/schema-management/common.ts +2 -2
- package/src/schema-management/migrations.ts +1 -1
- package/src/sql-queries/sql-queries.ts +10 -6
- package/src/sql-queries/sql-query-builder.ts +1 -0
- package/src/sqlite-db-helper.ts +3 -3
- package/src/sqlite-types.ts +3 -2
- package/src/sync/ClientSessionSyncProcessor.ts +142 -133
- package/src/sync/errors.ts +10 -22
- package/src/sync/mock-sync-backend.ts +139 -97
- package/src/sync/next/compact-events.ts +5 -5
- package/src/sync/next/facts.ts +7 -6
- package/src/sync/next/history-dag-common.ts +9 -6
- package/src/sync/next/history-dag.ts +3 -3
- package/src/sync/next/rebase-events.ts +1 -1
- package/src/sync/next/test/compact-events.calculator.test.ts +3 -2
- package/src/sync/next/test/compact-events.test.ts +4 -3
- package/src/sync/next/test/event-fixtures.ts +2 -2
- package/src/sync/sync-backend-kv.ts +1 -0
- package/src/sync/sync-backend.ts +5 -4
- package/src/sync/sync.ts +21 -0
- package/src/sync/syncstate.test.ts +513 -435
- package/src/sync/syncstate.ts +80 -86
- package/src/sync/transport-chunking.ts +3 -3
- package/src/sync/validate-push-payload.ts +4 -6
- package/src/util.ts +2 -2
- package/src/version.ts +8 -4
|
@@ -95,10 +95,12 @@ export const clientDocument = <
|
|
|
95
95
|
Object.defineProperty(setEventDef, 'schema', {
|
|
96
96
|
value: Schema.Struct({
|
|
97
97
|
id: Schema.String,
|
|
98
|
-
value: options.partialSet ? Schema.partial(valueSchema) : valueSchema,
|
|
98
|
+
value: options.partialSet === true ? Schema.partial(valueSchema) : valueSchema,
|
|
99
99
|
}).annotations({ title: `${name}Set:Args` }),
|
|
100
100
|
})
|
|
101
|
-
Object.defineProperty(setEventDef, 'options', {
|
|
101
|
+
Object.defineProperty(setEventDef, 'options', {
|
|
102
|
+
value: { derived: true, clientOnly: true, facts: undefined, deprecated: undefined },
|
|
103
|
+
})
|
|
102
104
|
|
|
103
105
|
const clientDocumentTableDefTrait: ClientDocumentTableDef.Trait<
|
|
104
106
|
TName,
|
|
@@ -106,20 +108,26 @@ export const clientDocument = <
|
|
|
106
108
|
TEncoded,
|
|
107
109
|
ClientDocumentTableOptions<TType>
|
|
108
110
|
> = {
|
|
111
|
+
// oxlint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- dynamic table def composition; type safety ensured by ClientDocumentTableDef.Trait
|
|
109
112
|
get: makeGetQueryBuilder(() => clientDocumentTableDef) as any,
|
|
113
|
+
// oxlint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- dynamic table def composition; type safety ensured by SetEventDefLike signature
|
|
110
114
|
set: setEventDef as any,
|
|
115
|
+
// oxlint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- phantom type field for generic inference only
|
|
111
116
|
Value: 'only-for-type-inference' as any,
|
|
112
117
|
default: options.default,
|
|
113
118
|
valueSchema,
|
|
114
119
|
[ClientDocumentTableDefSymbol]: {
|
|
115
120
|
derived: {
|
|
121
|
+
// oxlint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- dynamic event def composition; type checked at usage sites
|
|
116
122
|
setEventDef: derivedSetEventDef as any,
|
|
123
|
+
// oxlint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- dynamic materializer composition; type checked at usage sites
|
|
117
124
|
setMaterializer: derivedSetMaterializer as any,
|
|
118
125
|
},
|
|
119
126
|
options,
|
|
120
127
|
},
|
|
121
128
|
}
|
|
122
129
|
|
|
130
|
+
// oxlint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- composing tableDef + clientDocumentTableDefTrait into final type; validated by satisfies above
|
|
123
131
|
const clientDocumentTableDef = {
|
|
124
132
|
...tableDef,
|
|
125
133
|
...clientDocumentTableDefTrait,
|
|
@@ -139,11 +147,14 @@ export const mergeDefaultValues = <T>(defaultValues: T, explicitDefaultValues: T
|
|
|
139
147
|
}
|
|
140
148
|
|
|
141
149
|
// Get all unique keys from both objects
|
|
150
|
+
// oxlint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- Object.keys requires indexable type; T is constrained to object by preceding typeof checks
|
|
142
151
|
const allKeys = new Set([...Object.keys(defaultValues as any), ...Object.keys(explicitDefaultValues as any)])
|
|
143
152
|
|
|
144
153
|
return Array.from(allKeys).reduce((acc, key) => {
|
|
154
|
+
// oxlint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- dynamic key access on generic object; keys validated from Object.keys above
|
|
145
155
|
acc[key] = (explicitDefaultValues as any)[key] ?? (defaultValues as any)[key]
|
|
146
156
|
return acc
|
|
157
|
+
// oxlint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- reduce accumulator type; result type is T
|
|
147
158
|
}, {} as any)
|
|
148
159
|
}
|
|
149
160
|
|
|
@@ -175,7 +186,10 @@ export const createOptimisticEventSchema = ({
|
|
|
175
186
|
defaultValue: any
|
|
176
187
|
partialSet: boolean
|
|
177
188
|
}) => {
|
|
178
|
-
const targetSchema = partialSet ? Schema.partial(valueSchema) : valueSchema
|
|
189
|
+
const targetSchema = partialSet === true ? Schema.partial(valueSchema) : valueSchema
|
|
190
|
+
// The transform decode must yield values in the target schema's ENCODED shape.
|
|
191
|
+
// This keeps JSON columns consistent when Encoded != Type (e.g. Option).
|
|
192
|
+
const encodeTarget = Schema.encodeSync(targetSchema)
|
|
179
193
|
|
|
180
194
|
return Schema.transform(
|
|
181
195
|
Schema.Unknown, // Accept any historical event structure
|
|
@@ -184,17 +198,21 @@ export const createOptimisticEventSchema = ({
|
|
|
184
198
|
decode: (eventValue) => {
|
|
185
199
|
// Try direct decode first (for current schema events)
|
|
186
200
|
try {
|
|
187
|
-
|
|
201
|
+
const decoded = Schema.decodeUnknownSync(targetSchema)(eventValue)
|
|
202
|
+
// Re-encode so downstream parseJson/column decoding sees encoded values.
|
|
203
|
+
return encodeTarget(decoded)
|
|
188
204
|
} catch {
|
|
189
205
|
// Optimistic decoding for historical events
|
|
190
206
|
|
|
191
207
|
// Handle null/undefined/non-object cases
|
|
192
208
|
if (typeof eventValue !== 'object' || eventValue === null) {
|
|
193
|
-
console.warn(
|
|
194
|
-
|
|
209
|
+
console.warn(
|
|
210
|
+
`Client document: Non-object event value, using ${partialSet === true ? 'empty partial' : 'defaults'}`,
|
|
211
|
+
)
|
|
212
|
+
return encodeTarget(partialSet === true ? {} : defaultValue)
|
|
195
213
|
}
|
|
196
214
|
|
|
197
|
-
if (partialSet) {
|
|
215
|
+
if (partialSet === true) {
|
|
198
216
|
// For partial sets: only preserve fields that exist in new schema
|
|
199
217
|
const partialResult: Record<string, unknown> = {}
|
|
200
218
|
let hasValidFields = false
|
|
@@ -207,16 +225,17 @@ export const createOptimisticEventSchema = ({
|
|
|
207
225
|
// Drop fields that don't exist in new schema
|
|
208
226
|
}
|
|
209
227
|
|
|
210
|
-
if (hasValidFields) {
|
|
228
|
+
if (hasValidFields === true) {
|
|
211
229
|
try {
|
|
212
|
-
|
|
230
|
+
const decoded = Schema.decodeUnknownSync(targetSchema)(partialResult)
|
|
231
|
+
return encodeTarget(decoded)
|
|
213
232
|
} catch {
|
|
214
233
|
// Even filtered fields don't match schema
|
|
215
234
|
console.warn('Client document: Partial fields incompatible, returning empty partial')
|
|
216
|
-
return {}
|
|
235
|
+
return encodeTarget({})
|
|
217
236
|
}
|
|
218
237
|
}
|
|
219
|
-
return {}
|
|
238
|
+
return encodeTarget({})
|
|
220
239
|
} else {
|
|
221
240
|
// Full set: merge old data with new defaults
|
|
222
241
|
const merged: Record<string, unknown> = { ...defaultValue }
|
|
@@ -231,12 +250,13 @@ export const createOptimisticEventSchema = ({
|
|
|
231
250
|
|
|
232
251
|
// Try to decode the merged value
|
|
233
252
|
try {
|
|
234
|
-
|
|
253
|
+
const decoded = Schema.decodeUnknownSync(valueSchema)(merged)
|
|
254
|
+
return encodeTarget(decoded)
|
|
235
255
|
} catch {
|
|
236
256
|
// Merged value still doesn't match (e.g., type changes)
|
|
237
257
|
// Fall back to pure defaults
|
|
238
258
|
console.warn('Client document: Could not preserve event data, using defaults')
|
|
239
|
-
return defaultValue
|
|
259
|
+
return encodeTarget(defaultValue)
|
|
240
260
|
}
|
|
241
261
|
}
|
|
242
262
|
}
|
|
@@ -357,11 +377,14 @@ const makeGetQueryBuilder = <TTableDef extends ClientDocumentTableDef<any, any,
|
|
|
357
377
|
return {
|
|
358
378
|
[QueryBuilderTypeId]: QueryBuilderTypeId,
|
|
359
379
|
[QueryBuilderAstSymbol]: ast,
|
|
380
|
+
// oxlint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- phantom type field for generic inference only
|
|
360
381
|
ResultType: 'only-for-type-inference' as any,
|
|
361
382
|
asSql: () => ({ query, bindValues: [id] }),
|
|
362
383
|
toString: () => query.toString(),
|
|
384
|
+
// oxlint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- spreading empty object to satisfy QueryBuilder interface requirements
|
|
363
385
|
...({} as any), // Needed for type cast
|
|
364
386
|
}
|
|
387
|
+
// oxlint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- makeGetQueryBuilder return type uses complex conditional generics
|
|
365
388
|
}) as any
|
|
366
389
|
}
|
|
367
390
|
|
|
@@ -544,7 +567,7 @@ export namespace ClientDocumentTableDef {
|
|
|
544
567
|
readonly name: `${TName}Set`
|
|
545
568
|
readonly args: { id: string; value: TType }
|
|
546
569
|
}
|
|
547
|
-
readonly options: { derived: true; clientOnly: true; facts: undefined }
|
|
570
|
+
readonly options: { derived: true; clientOnly: true; facts: undefined; deprecated: undefined }
|
|
548
571
|
}
|
|
549
572
|
|
|
550
573
|
export type SetEventDef<TName extends string, TType, TOptions extends ClientDocumentTableOptions<TType>> = EventDef<
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { Schema, SchemaAST } from '@livestore/utils/effect'
|
|
2
1
|
import { describe, expect, test } from 'vitest'
|
|
3
2
|
|
|
3
|
+
import { Schema, SchemaAST } from '@livestore/utils/effect'
|
|
4
|
+
|
|
4
5
|
import { withColumnType, withPrimaryKey } from './column-annotations.ts'
|
|
5
6
|
|
|
6
7
|
describe.concurrent('annotations', () => {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Schema } from '@livestore/utils/effect'
|
|
2
2
|
import { dual, Option, SchemaAST } from '@livestore/utils/effect'
|
|
3
|
+
|
|
3
4
|
import type { SqliteDsl } from './db-schema/mod.ts'
|
|
4
5
|
|
|
5
6
|
export const PrimaryKeyId = Symbol.for('livestore/state/sqlite/annotations/primary-key')
|
|
@@ -79,7 +80,7 @@ const validateSchemaColumnTypeCompatibility = (
|
|
|
79
80
|
const applyAnnotations = <T extends Schema.Schema.All>(schema: T, overrides: Record<PropertyKey, unknown>): T => {
|
|
80
81
|
const identifier = SchemaAST.getIdentifierAnnotation(schema.ast)
|
|
81
82
|
const shouldPreserveIdentifier = Option.isSome(identifier) && !(SchemaAST.IdentifierAnnotationId in overrides)
|
|
82
|
-
const annotations: Record<PropertyKey, unknown> = shouldPreserveIdentifier
|
|
83
|
+
const annotations: Record<PropertyKey, unknown> = shouldPreserveIdentifier === true
|
|
83
84
|
? { ...overrides, [SchemaAST.IdentifierAnnotationId]: identifier.value }
|
|
84
85
|
: overrides
|
|
85
86
|
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { Schema } from '@livestore/utils/effect'
|
|
2
1
|
import { assert, describe, expect, it } from 'vitest'
|
|
3
2
|
|
|
3
|
+
import { Schema } from '@livestore/utils/effect'
|
|
4
|
+
import { objectToString } from '@livestore/utils'
|
|
5
|
+
|
|
4
6
|
import * as State from '../mod.ts'
|
|
5
7
|
import { withAutoIncrement, withColumnType, withDefault, withPrimaryKey, withUnique } from './column-annotations.ts'
|
|
6
8
|
|
|
@@ -299,7 +301,7 @@ describe('getColumnDefForSchema', () => {
|
|
|
299
301
|
it('should handle Uint8Array as blob column', () => {
|
|
300
302
|
const columnDef = State.SQLite.getColumnDefForSchema(Schema.Uint8Array)
|
|
301
303
|
expect(columnDef.columnType).toBe('blob')
|
|
302
|
-
expect(columnDef.schema
|
|
304
|
+
expect(objectToString(columnDef.schema)).toBe('Uint8ArrayFromSelf')
|
|
303
305
|
})
|
|
304
306
|
})
|
|
305
307
|
|
|
@@ -356,8 +358,8 @@ describe('getColumnDefForSchema', () => {
|
|
|
356
358
|
|
|
357
359
|
expect(table.sqliteDef.columns.active.nullable).toBe(true)
|
|
358
360
|
expect(table.sqliteDef.columns.active.columnType).toBe('integer')
|
|
359
|
-
expect(table.sqliteDef.columns.active.schema
|
|
360
|
-
expect((table.rowSchema as any).fields.active
|
|
361
|
+
expect(objectToString(table.sqliteDef.columns.active.schema)).toBe('(number <-> boolean) | null')
|
|
362
|
+
expect(String((table.rowSchema as any).fields.active)).toBe('(number <-> boolean) | null')
|
|
361
363
|
})
|
|
362
364
|
|
|
363
365
|
it('should handle optional complex types with JSON encoding', () => {
|
|
@@ -406,8 +408,8 @@ describe('getColumnDefForSchema', () => {
|
|
|
406
408
|
const table = State.SQLite.table({ name: 'timers', schema })
|
|
407
409
|
|
|
408
410
|
expect(table.sqliteDef.columns.status.columnType).toBe('text')
|
|
409
|
-
expect(table.sqliteDef.columns.status.schema
|
|
410
|
-
expect((table.rowSchema as any).fields.status
|
|
411
|
+
expect(objectToString(table.sqliteDef.columns.status.schema)).toBe('"idle" | "running" | "stopped"')
|
|
412
|
+
expect(String((table.rowSchema as any).fields.status)).toBe('"idle" | "running" | "stopped"')
|
|
411
413
|
})
|
|
412
414
|
|
|
413
415
|
it('should handle Schema.NullOr with complex types', () => {
|
|
@@ -20,19 +20,20 @@ export const getColumnDefForSchema = (
|
|
|
20
20
|
|
|
21
21
|
// Extract annotations
|
|
22
22
|
const getAnnotation = <T>(annotationId: symbol): Option.Option<T> =>
|
|
23
|
-
propertySignature
|
|
23
|
+
propertySignature !== undefined
|
|
24
24
|
? hasPropertyAnnotation<T>(propertySignature, annotationId)
|
|
25
25
|
: SchemaAST.getAnnotation<T>(annotationId)(ast)
|
|
26
26
|
|
|
27
27
|
const columnType = SchemaAST.getAnnotation<SqliteDsl.FieldColumnType>(ColumnType)(ast)
|
|
28
28
|
|
|
29
29
|
// Check if schema has null (e.g., Schema.NullOr) or undefined or if it's forced nullable (optional field)
|
|
30
|
-
const isNullable = forceNullable || hasNull(ast) || hasUndefined(ast)
|
|
30
|
+
const isNullable = forceNullable === true || hasNull(ast) === true || hasUndefined(ast) === true
|
|
31
31
|
|
|
32
32
|
// Get base column definition with nullable flag
|
|
33
|
-
const baseColumn =
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
const baseColumn =
|
|
34
|
+
Option.isSome(columnType) === true
|
|
35
|
+
? getColumnForType(columnType.value, isNullable)
|
|
36
|
+
: getColumnForSchema(schema, isNullable)
|
|
36
37
|
|
|
37
38
|
// Apply annotations
|
|
38
39
|
const primaryKey = getAnnotation<boolean>(PrimaryKeyId).pipe(Option.getOrElse(() => false))
|
|
@@ -41,9 +42,9 @@ export const getColumnDefForSchema = (
|
|
|
41
42
|
|
|
42
43
|
return {
|
|
43
44
|
...baseColumn,
|
|
44
|
-
...(primaryKey
|
|
45
|
-
...(autoIncrement
|
|
46
|
-
...(Option.isSome(defaultValue)
|
|
45
|
+
...(primaryKey === true ? { primaryKey: true } : {}),
|
|
46
|
+
...(autoIncrement === true ? { autoIncrement: true } : {}),
|
|
47
|
+
...(Option.isSome(defaultValue) === true ? { default: Option.some(defaultValue.value) } : {}),
|
|
47
48
|
}
|
|
48
49
|
}
|
|
49
50
|
|
|
@@ -51,9 +52,9 @@ const hasPropertyAnnotation = <T>(
|
|
|
51
52
|
propertySignature: SchemaAST.PropertySignature,
|
|
52
53
|
annotationId: symbol,
|
|
53
54
|
): Option.Option<T> => {
|
|
54
|
-
if ('annotations' in propertySignature && propertySignature.annotations) {
|
|
55
|
+
if ('annotations' in propertySignature && propertySignature.annotations !== undefined) {
|
|
55
56
|
const annotation = SchemaAST.getAnnotation<T>(annotationId)(propertySignature as any)
|
|
56
|
-
if (Option.isSome(annotation)) return annotation
|
|
57
|
+
if (Option.isSome(annotation) === true) return annotation
|
|
57
58
|
}
|
|
58
59
|
return SchemaAST.getAnnotation<T>(annotationId)(propertySignature.type)
|
|
59
60
|
}
|
|
@@ -74,15 +75,15 @@ export const schemaFieldsToColumns = (
|
|
|
74
75
|
const fieldSchema = Schema.make(prop.type)
|
|
75
76
|
|
|
76
77
|
// Warn about lossy conversion for fields with both null and undefined
|
|
77
|
-
if (prop.isOptional) {
|
|
78
|
+
if (prop.isOptional === true) {
|
|
78
79
|
const { hasNull, hasUndefined } = checkNullUndefined(fieldSchema.ast)
|
|
79
|
-
if (hasNull && hasUndefined) {
|
|
80
|
+
if (hasNull === true && hasUndefined === true) {
|
|
80
81
|
console.warn(`Field '${prop.name}' has both null and undefined - treating | undefined as | null`)
|
|
81
82
|
}
|
|
82
83
|
}
|
|
83
84
|
|
|
84
85
|
// Get column definition - pass nullable flag for optional fields
|
|
85
|
-
const columnDef = getColumnDefForSchema(fieldSchema, prop, prop.isOptional)
|
|
86
|
+
const columnDef = getColumnDefForSchema(fieldSchema, prop, prop.isOptional === true)
|
|
86
87
|
|
|
87
88
|
// Check for primary key and unique annotations
|
|
88
89
|
const hasPrimaryKey = hasPropertyAnnotation<boolean>(prop, PrimaryKeyId).pipe(Option.getOrElse(() => false))
|
|
@@ -91,16 +92,16 @@ export const schemaFieldsToColumns = (
|
|
|
91
92
|
// Build final column
|
|
92
93
|
columns[prop.name] = {
|
|
93
94
|
...columnDef,
|
|
94
|
-
...(hasPrimaryKey
|
|
95
|
+
...(hasPrimaryKey === true ? { primaryKey: true } : {}),
|
|
95
96
|
}
|
|
96
97
|
|
|
97
98
|
// Validate primary key + nullable
|
|
98
99
|
const column = columns[prop.name]
|
|
99
|
-
if (column?.primaryKey && column.nullable) {
|
|
100
|
+
if (column?.primaryKey === true && column.nullable === true) {
|
|
100
101
|
throw new Error('Primary key columns cannot be nullable')
|
|
101
102
|
}
|
|
102
103
|
|
|
103
|
-
if (hasUnique) uniqueColumns.push(prop.name)
|
|
104
|
+
if (hasUnique === true) uniqueColumns.push(prop.name)
|
|
104
105
|
}
|
|
105
106
|
|
|
106
107
|
return { columns, uniqueColumns }
|
|
@@ -111,9 +112,9 @@ const checkNullUndefined = (ast: SchemaAST.AST): { hasNull: boolean; hasUndefine
|
|
|
111
112
|
let hasUndefined = false
|
|
112
113
|
|
|
113
114
|
const visit = (type: SchemaAST.AST): void => {
|
|
114
|
-
if (SchemaAST.isUndefinedKeyword(type)) hasUndefined = true
|
|
115
|
-
else if (SchemaAST.isLiteral(type) && type.literal === null) hasNull = true
|
|
116
|
-
else if (SchemaAST.isUnion(type)) type.types.forEach(visit)
|
|
115
|
+
if (SchemaAST.isUndefinedKeyword(type) === true) hasUndefined = true
|
|
116
|
+
else if (SchemaAST.isLiteral(type) === true && type.literal === null) hasNull = true
|
|
117
|
+
else if (SchemaAST.isUnion(type) === true) type.types.forEach(visit)
|
|
117
118
|
}
|
|
118
119
|
|
|
119
120
|
visit(ast)
|
|
@@ -121,16 +122,16 @@ const checkNullUndefined = (ast: SchemaAST.AST): { hasNull: boolean; hasUndefine
|
|
|
121
122
|
}
|
|
122
123
|
|
|
123
124
|
const hasNull = (ast: SchemaAST.AST): boolean => {
|
|
124
|
-
if (SchemaAST.isLiteral(ast) && ast.literal === null) return true
|
|
125
|
-
if (SchemaAST.isUnion(ast)) {
|
|
125
|
+
if (SchemaAST.isLiteral(ast) === true && ast.literal === null) return true
|
|
126
|
+
if (SchemaAST.isUnion(ast) === true) {
|
|
126
127
|
return ast.types.some((type) => hasNull(type))
|
|
127
128
|
}
|
|
128
129
|
return false
|
|
129
130
|
}
|
|
130
131
|
|
|
131
132
|
const hasUndefined = (ast: SchemaAST.AST): boolean => {
|
|
132
|
-
if (SchemaAST.isUndefinedKeyword(ast)) return true
|
|
133
|
-
if (SchemaAST.isUnion(ast)) {
|
|
133
|
+
if (SchemaAST.isUndefinedKeyword(ast) === true) return true
|
|
134
|
+
if (SchemaAST.isUnion(ast) === true) {
|
|
134
135
|
return ast.types.some((type) => hasUndefined(type))
|
|
135
136
|
}
|
|
136
137
|
return false
|
|
@@ -158,7 +159,7 @@ const getColumnForSchema = (schema: Schema.Schema.AnyNoContext, nullable = false
|
|
|
158
159
|
const coreSchema = stripNullable(ast) === ast ? schema : Schema.make(coreAst)
|
|
159
160
|
|
|
160
161
|
// Special case: Boolean is transformed to integer in SQLite
|
|
161
|
-
if (SchemaAST.isBooleanKeyword(coreAst)) {
|
|
162
|
+
if (SchemaAST.isBooleanKeyword(coreAst) === true) {
|
|
162
163
|
return SqliteDsl.boolean({ nullable })
|
|
163
164
|
}
|
|
164
165
|
|
|
@@ -166,11 +167,11 @@ const getColumnForSchema = (schema: Schema.Schema.AnyNoContext, nullable = false
|
|
|
166
167
|
const encodedAst = Schema.encodedSchema(coreSchema).ast
|
|
167
168
|
|
|
168
169
|
// Check if the encoded type matches SQLite native types
|
|
169
|
-
if (SchemaAST.isStringKeyword(encodedAst)) {
|
|
170
|
+
if (SchemaAST.isStringKeyword(encodedAst) === true) {
|
|
170
171
|
return SqliteDsl.text({ schema: coreSchema, nullable })
|
|
171
172
|
}
|
|
172
173
|
|
|
173
|
-
if (SchemaAST.isNumberKeyword(encodedAst)) {
|
|
174
|
+
if (SchemaAST.isNumberKeyword(encodedAst) === true) {
|
|
174
175
|
// Special cases for integer columns
|
|
175
176
|
const id = SchemaAST.getIdentifierAnnotation(coreAst).pipe(Option.getOrElse(() => ''))
|
|
176
177
|
if (id === 'Int' || id === 'DateFromNumber') {
|
|
@@ -179,23 +180,23 @@ const getColumnForSchema = (schema: Schema.Schema.AnyNoContext, nullable = false
|
|
|
179
180
|
return SqliteDsl.real({ schema: coreSchema, nullable })
|
|
180
181
|
}
|
|
181
182
|
|
|
182
|
-
if (isUint8ArraySchema(coreAst) || isUint8ArraySchema(encodedAst)) {
|
|
183
|
+
if (isUint8ArraySchema(coreAst) === true || isUint8ArraySchema(encodedAst) === true) {
|
|
183
184
|
return SqliteDsl.blob({ schema: Schema.Uint8ArrayFromSelf as Schema.Schema<Uint8Array<ArrayBuffer>>, nullable })
|
|
184
185
|
}
|
|
185
186
|
|
|
186
187
|
const literalColumn = getLiteralColumnDefinition(encodedAst, coreSchema, nullable, coreAst)
|
|
187
|
-
if (literalColumn) return literalColumn
|
|
188
|
+
if (literalColumn !== null) return literalColumn
|
|
188
189
|
|
|
189
190
|
// Fallback to checking the original AST in case the encoded schema differs
|
|
190
191
|
const coreLiteralColumn = getLiteralColumnDefinition(coreAst, coreSchema, nullable, coreAst)
|
|
191
|
-
if (coreLiteralColumn) return coreLiteralColumn
|
|
192
|
+
if (coreLiteralColumn !== null) return coreLiteralColumn
|
|
192
193
|
|
|
193
194
|
// Everything else needs JSON encoding
|
|
194
195
|
return SqliteDsl.json({ schema: coreSchema, nullable })
|
|
195
196
|
}
|
|
196
197
|
|
|
197
198
|
const stripNullable = (ast: SchemaAST.AST): SchemaAST.AST => {
|
|
198
|
-
if (
|
|
199
|
+
if (SchemaAST.isUnion(ast) === false) return ast
|
|
199
200
|
|
|
200
201
|
// Filter out null/undefined members while preserving any annotations on the union
|
|
201
202
|
const coreTypes = ast.types.filter(
|
|
@@ -220,7 +221,7 @@ const getLiteralColumnDefinition = (
|
|
|
220
221
|
sourceAst: SchemaAST.AST,
|
|
221
222
|
): SqliteDsl.ColumnDefinition.Any | null => {
|
|
222
223
|
const literalValues = extractLiteralValues(ast)
|
|
223
|
-
if (
|
|
224
|
+
if (literalValues == null) return null
|
|
224
225
|
|
|
225
226
|
const literalType = getLiteralValueType(literalValues)
|
|
226
227
|
switch (literalType) {
|
|
@@ -235,7 +236,7 @@ const getLiteralColumnDefinition = (
|
|
|
235
236
|
const useIntegerColumn =
|
|
236
237
|
literalValues.length > 1 && literalValues.every((value) => typeof value === 'number' && Number.isInteger(value))
|
|
237
238
|
|
|
238
|
-
return useIntegerColumn ? SqliteDsl.integer({ schema, nullable }) : SqliteDsl.real({ schema, nullable })
|
|
239
|
+
return useIntegerColumn === true ? SqliteDsl.integer({ schema, nullable }) : SqliteDsl.real({ schema, nullable })
|
|
239
240
|
}
|
|
240
241
|
case 'boolean':
|
|
241
242
|
return SqliteDsl.boolean({ nullable })
|
|
@@ -247,9 +248,13 @@ const getLiteralColumnDefinition = (
|
|
|
247
248
|
}
|
|
248
249
|
|
|
249
250
|
const extractLiteralValues = (ast: SchemaAST.AST): ReadonlyArray<SchemaAST.LiteralValue> | null => {
|
|
250
|
-
if (SchemaAST.isLiteral(ast)) return [ast.literal]
|
|
251
|
+
if (SchemaAST.isLiteral(ast) === true) return [ast.literal]
|
|
251
252
|
|
|
252
|
-
if (
|
|
253
|
+
if (
|
|
254
|
+
SchemaAST.isUnion(ast) === true &&
|
|
255
|
+
ast.types.length > 0 &&
|
|
256
|
+
ast.types.every((type) => SchemaAST.isLiteral(type)) === true
|
|
257
|
+
) {
|
|
253
258
|
return ast.types.map((type) => (type as SchemaAST.Literal).literal)
|
|
254
259
|
}
|
|
255
260
|
|
|
@@ -270,11 +275,11 @@ const getLiteralValueType = (
|
|
|
270
275
|
|
|
271
276
|
const isUint8ArraySchema = (ast: SchemaAST.AST): boolean => {
|
|
272
277
|
const identifier = SchemaAST.getIdentifierAnnotation(ast)
|
|
273
|
-
if (Option.isSome(identifier) && identifier.value.includes('Uint8Array')) {
|
|
278
|
+
if (Option.isSome(identifier) === true && identifier.value.includes('Uint8Array') === true) {
|
|
274
279
|
return true
|
|
275
280
|
}
|
|
276
281
|
|
|
277
|
-
if (SchemaAST.isTupleType(ast)) {
|
|
282
|
+
if (SchemaAST.isTupleType(ast) === true) {
|
|
278
283
|
return ast.elements.length === 0 && ast.rest.length === 1 && SchemaAST.isNumberKeyword(ast.rest[0]!.type)
|
|
279
284
|
}
|
|
280
285
|
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import { Option, Schema } from '@livestore/utils/effect'
|
|
2
1
|
import { describe, expect, it } from 'vitest'
|
|
2
|
+
|
|
3
|
+
import { Option, Schema } from '@livestore/utils/effect'
|
|
4
|
+
|
|
3
5
|
import { makeColumnSpec } from './column-spec.ts'
|
|
4
6
|
import { SqliteAst } from './db-schema/mod.ts'
|
|
5
7
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Schema } from '@livestore/utils/effect'
|
|
2
|
+
|
|
2
3
|
import { type SqliteAst, SqliteDsl } from './db-schema/mod.ts'
|
|
3
4
|
|
|
4
5
|
/**
|
|
@@ -12,13 +13,13 @@ import { type SqliteAst, SqliteDsl } from './db-schema/mod.ts'
|
|
|
12
13
|
export const makeColumnSpec = (tableAst: SqliteAst.Table) => {
|
|
13
14
|
const pkColumns = tableAst.columns.filter((_) => _.primaryKey)
|
|
14
15
|
const hasSinglePk = pkColumns.length === 1
|
|
15
|
-
const pkColumn = hasSinglePk ? pkColumns[0] : undefined
|
|
16
|
+
const pkColumn = hasSinglePk === true ? pkColumns[0] : undefined
|
|
16
17
|
|
|
17
18
|
// Build column definitions, handling the special SQLite rule that AUTOINCREMENT
|
|
18
19
|
// is only valid on a single column declared as INTEGER PRIMARY KEY (column-level).
|
|
19
20
|
const columnDefStrs = tableAst.columns.map((column) =>
|
|
20
21
|
toSqliteColumnSpec(column, {
|
|
21
|
-
inlinePrimaryKey: hasSinglePk && column === pkColumn &&
|
|
22
|
+
inlinePrimaryKey: hasSinglePk && column === pkColumn && column.primaryKey,
|
|
22
23
|
}),
|
|
23
24
|
)
|
|
24
25
|
|
|
@@ -36,24 +37,24 @@ const toSqliteColumnSpec = (column: SqliteAst.Column, opts: { inlinePrimaryKey:
|
|
|
36
37
|
const columnTypeStr = column.type._tag
|
|
37
38
|
// When PRIMARY KEY is declared inline, NOT NULL is implied and should not be emitted,
|
|
38
39
|
// and AUTOINCREMENT must immediately follow PRIMARY KEY within the same constraint.
|
|
39
|
-
const nullableStr = opts.inlinePrimaryKey ? '' : column.nullable === false ? 'not null' : ''
|
|
40
|
+
const nullableStr = opts.inlinePrimaryKey === true ? '' : column.nullable === false ? 'not null' : ''
|
|
40
41
|
|
|
41
42
|
// Only include AUTOINCREMENT when it's valid: single-column INTEGER PRIMARY KEY
|
|
42
|
-
const includeAutoIncrement = opts.inlinePrimaryKey && column.type._tag === 'integer' && column.autoIncrement === true
|
|
43
|
+
const includeAutoIncrement = opts.inlinePrimaryKey === true && column.type._tag === 'integer' && column.autoIncrement === true
|
|
43
44
|
|
|
44
|
-
const pkStr = opts.inlinePrimaryKey ? 'primary key' : ''
|
|
45
|
-
const autoIncrementStr = includeAutoIncrement ? 'autoincrement' : ''
|
|
45
|
+
const pkStr = opts.inlinePrimaryKey === true ? 'primary key' : ''
|
|
46
|
+
const autoIncrementStr = includeAutoIncrement === true ? 'autoincrement' : ''
|
|
46
47
|
|
|
47
48
|
const defaultValueStr = (() => {
|
|
48
49
|
if (column.default._tag === 'None') return ''
|
|
49
50
|
|
|
50
51
|
const defaultValue = column.default.value
|
|
51
|
-
if (SqliteDsl.isDefaultThunk(defaultValue)) return ''
|
|
52
|
+
if (SqliteDsl.isDefaultThunk(defaultValue) === true) return ''
|
|
52
53
|
|
|
53
54
|
const resolvedDefault = SqliteDsl.resolveColumnDefault(defaultValue)
|
|
54
55
|
|
|
55
56
|
if (resolvedDefault === null) return 'default null'
|
|
56
|
-
if (SqliteDsl.isSqlDefaultValue(resolvedDefault)) return `default ${resolvedDefault.sql}`
|
|
57
|
+
if (SqliteDsl.isSqlDefaultValue(resolvedDefault) === true) return `default ${resolvedDefault.sql}`
|
|
57
58
|
|
|
58
59
|
const encodeValue = Schema.encodeSync(column.schema)
|
|
59
60
|
const encodedDefaultValue = encodeValue(resolvedDefault)
|
|
@@ -124,7 +124,7 @@ const trimInfoForHasing = (obj: Table | Column | Index | ForeignKey | DbSchema):
|
|
|
124
124
|
|
|
125
125
|
// NEW: Include schema hash for JSON columns
|
|
126
126
|
// This ensures that changes to the JSON schema are detected
|
|
127
|
-
if (isJsonColumn(obj) && obj.schema) {
|
|
127
|
+
if (isJsonColumn(obj) === true && obj.schema !== undefined) {
|
|
128
128
|
// Use Effect's Schema.hash for consistent hashing
|
|
129
129
|
baseInfo.jsonSchemaHash = Schema.hash(obj.schema)
|
|
130
130
|
}
|
|
@@ -155,7 +155,7 @@ const trimInfoForHasing = (obj: Table | Column | Index | ForeignKey | DbSchema):
|
|
|
155
155
|
}
|
|
156
156
|
}
|
|
157
157
|
default: {
|
|
158
|
-
throw new Error(`Unreachable: ${obj}`)
|
|
158
|
+
throw new Error(`Unreachable: ${String(obj)}`)
|
|
159
159
|
}
|
|
160
160
|
}
|
|
161
161
|
}
|
|
@@ -6,6 +6,7 @@ export type SqlDefaultValue = {
|
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
export const isSqlDefaultValue = (value: unknown): value is SqlDefaultValue => {
|
|
9
|
+
// oxlint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- type guard property access after structural check
|
|
9
10
|
return typeof value === 'object' && value !== null && 'sql' in value && typeof (value as any).sql === 'string'
|
|
10
11
|
}
|
|
11
12
|
|
|
@@ -16,14 +17,14 @@ export const isDefaultThunk = (value: unknown): value is ColumnDefaultThunk<unkn
|
|
|
16
17
|
export type ColumnDefaultValue<T> = T | null | ColumnDefaultThunk<T | null> | SqlDefaultValue
|
|
17
18
|
|
|
18
19
|
export const resolveColumnDefault = <T>(value: ColumnDefaultValue<T>): T | null | SqlDefaultValue =>
|
|
19
|
-
isDefaultThunk(value) ?
|
|
20
|
+
isDefaultThunk(value) === true ? value() : value
|
|
20
21
|
|
|
21
|
-
export type ColumnDefinition<TEncoded, TDecoded> = {
|
|
22
|
+
export type ColumnDefinition<TEncoded, TDecoded, TNullable extends boolean = boolean> = {
|
|
22
23
|
readonly columnType: FieldColumnType
|
|
23
24
|
readonly schema: Schema.Schema<TDecoded, TEncoded>
|
|
24
25
|
readonly default: Option.Option<ColumnDefaultValue<TDecoded>>
|
|
25
26
|
/** @default false */
|
|
26
|
-
readonly nullable:
|
|
27
|
+
readonly nullable: TNullable
|
|
27
28
|
/** @default false */
|
|
28
29
|
readonly primaryKey: boolean
|
|
29
30
|
/** @default false */
|
|
@@ -40,6 +41,7 @@ export const isColumnDefinition = (value: unknown): value is ColumnDefinition.An
|
|
|
40
41
|
typeof value === 'object' &&
|
|
41
42
|
value !== null &&
|
|
42
43
|
'columnType' in value &&
|
|
44
|
+
// oxlint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- type guard narrowing; columnType checked to be in valid set
|
|
43
45
|
validColumnTypes.includes(value.columnType as any)
|
|
44
46
|
)
|
|
45
47
|
}
|
|
@@ -105,6 +107,7 @@ const makeColDef =
|
|
|
105
107
|
const schema = nullable === true ? Schema.NullOr(schemaWithoutNull) : schemaWithoutNull
|
|
106
108
|
const default_ = def?.default === undefined || def.default === NoDefault ? Option.none() : Option.some(def.default)
|
|
107
109
|
|
|
110
|
+
// oxlint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- column factory return type uses complex conditional generics; consumer type safety enforced by ColDefFn signature
|
|
108
111
|
return {
|
|
109
112
|
columnType,
|
|
110
113
|
schema,
|
|
@@ -200,10 +203,12 @@ type MakeSpecializedColDefFn = {
|
|
|
200
203
|
|
|
201
204
|
const makeSpecializedColDef: MakeSpecializedColDefFn = (columnType, opts) => (def?: ColumnDefinitionInput) => {
|
|
202
205
|
const nullable = def?.nullable ?? false
|
|
206
|
+
// oxlint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- schema type variance; custom schema compatibility checked at call site
|
|
203
207
|
const schemaWithoutNull = opts._tag === 'baseSchemaFn' ? opts.baseSchemaFn(def?.schema as any) : opts.baseSchema
|
|
204
208
|
const schema = nullable === true ? Schema.NullOr(schemaWithoutNull) : schemaWithoutNull
|
|
205
209
|
const default_ = def?.default === undefined || def.default === NoDefault ? Option.none() : Option.some(def.default)
|
|
206
210
|
|
|
211
|
+
// oxlint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- specialized column factory return type uses complex conditional generics; consumer type safety enforced by SpecializedColDefFn signature
|
|
207
212
|
return {
|
|
208
213
|
columnType,
|
|
209
214
|
schema,
|
|
@@ -236,7 +241,7 @@ export const boolean: SpecializedColDefFn<'integer', false, boolean> = makeSpeci
|
|
|
236
241
|
_tag: 'baseSchema',
|
|
237
242
|
baseSchema: Schema.transform(Schema.Number, Schema.Boolean, {
|
|
238
243
|
decode: (_) => _ === 1,
|
|
239
|
-
encode: (_) => (_ ? 1 : 0),
|
|
244
|
+
encode: (_) => (_ === true ? 1 : 0),
|
|
240
245
|
}),
|
|
241
246
|
})
|
|
242
247
|
|
|
@@ -259,15 +264,19 @@ export const defaultSchemaForColumnType = <TColumnType extends FieldColumnType>(
|
|
|
259
264
|
|
|
260
265
|
switch (columnType) {
|
|
261
266
|
case 'text': {
|
|
267
|
+
// oxlint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- switch-based type narrowing for column type to schema mapping; each case is correct for its branch
|
|
262
268
|
return Schema.String as any as Schema.Schema<T>
|
|
263
269
|
}
|
|
264
270
|
case 'integer': {
|
|
271
|
+
// oxlint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- switch-based type narrowing for column type to schema mapping; each case is correct for its branch
|
|
265
272
|
return Schema.Number as any as Schema.Schema<T>
|
|
266
273
|
}
|
|
267
274
|
case 'real': {
|
|
275
|
+
// oxlint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- switch-based type narrowing for column type to schema mapping; each case is correct for its branch
|
|
268
276
|
return Schema.Number as any as Schema.Schema<T>
|
|
269
277
|
}
|
|
270
278
|
case 'blob': {
|
|
279
|
+
// oxlint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- switch-based type narrowing for column type to schema mapping; each case is correct for its branch
|
|
271
280
|
return Schema.Uint8ArrayFromSelf as any as Schema.Schema<T>
|
|
272
281
|
}
|
|
273
282
|
default: {
|
|
@@ -31,7 +31,7 @@ export type DbSchemaFromInputSchema<TSchemaInput extends DbSchemaInput> =
|
|
|
31
31
|
export const makeDbSchema = <TDbSchemaInput extends DbSchemaInput>(
|
|
32
32
|
schema: TDbSchemaInput,
|
|
33
33
|
): DbSchemaFromInputSchema<TDbSchemaInput> => {
|
|
34
|
-
return Array.isArray(schema) ? Object.fromEntries(schema.map((_) => [_.name, _])) : (schema as any)
|
|
34
|
+
return Array.isArray(schema) === true ? Object.fromEntries(schema.map((_) => [_.name, _])) : (schema as any)
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
export const table = <TTableName extends string, TColumns extends Columns, TIndexes extends Index[]>(
|
|
@@ -76,6 +76,7 @@ export const insertStructSchemaForTable = <TTableDefinition extends TableDefinit
|
|
|
76
76
|
Object.fromEntries(
|
|
77
77
|
tableDef.ast.columns.map((column) => [
|
|
78
78
|
column.name,
|
|
79
|
+
|
|
79
80
|
column.nullable === true || column.default._tag === 'Some' ? Schema.optional(column.schema) : column.schema,
|
|
80
81
|
]),
|
|
81
82
|
),
|
|
@@ -195,8 +196,7 @@ export namespace FromColumns {
|
|
|
195
196
|
}
|
|
196
197
|
|
|
197
198
|
export type NullableColumnNames<TColumns extends Columns> = keyof {
|
|
198
|
-
|
|
199
|
-
[K in keyof TColumns as TColumns[K] extends ColumnDefinition<any, true> ? K : never]: {}
|
|
199
|
+
[K in keyof TColumns as TColumns[K]['nullable'] extends true ? K : never]: {}
|
|
200
200
|
}
|
|
201
201
|
|
|
202
202
|
export type RequiredInsertColumns<TColumns extends Columns> = {
|