@livestore/common 0.3.2-dev.9 → 0.4.0-dev.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.tsbuildinfo +1 -1
- package/dist/ClientSessionLeaderThreadProxy.d.ts +2 -2
- package/dist/ClientSessionLeaderThreadProxy.d.ts.map +1 -1
- package/dist/adapter-types.d.ts +4 -4
- package/dist/adapter-types.d.ts.map +1 -1
- package/dist/debug-info.d.ts +17 -17
- package/dist/devtools/devtools-messages-client-session.d.ts +38 -38
- package/dist/devtools/devtools-messages-common.d.ts +6 -6
- package/dist/devtools/devtools-messages-leader.d.ts +28 -28
- package/dist/devtools/devtools-messages-leader.d.ts.map +1 -1
- package/dist/devtools/devtools-messages-leader.js.map +1 -1
- package/dist/leader-thread/LeaderSyncProcessor.js +3 -1
- package/dist/leader-thread/LeaderSyncProcessor.js.map +1 -1
- package/dist/leader-thread/make-leader-thread-layer.d.ts.map +1 -1
- package/dist/leader-thread/make-leader-thread-layer.js +21 -4
- package/dist/leader-thread/make-leader-thread-layer.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 +1 -1
- package/dist/leader-thread/types.d.ts.map +1 -1
- package/dist/materializer-helper.d.ts +3 -3
- package/dist/materializer-helper.d.ts.map +1 -1
- package/dist/materializer-helper.js +2 -2
- package/dist/materializer-helper.js.map +1 -1
- package/dist/rematerialize-from-eventlog.js +1 -1
- package/dist/rematerialize-from-eventlog.js.map +1 -1
- package/dist/schema/EventDef.d.ts +104 -178
- package/dist/schema/EventSequenceNumber.d.ts +5 -0
- package/dist/schema/EventSequenceNumber.d.ts.map +1 -1
- package/dist/schema/EventSequenceNumber.js +7 -2
- package/dist/schema/EventSequenceNumber.js.map +1 -1
- package/dist/schema/EventSequenceNumber.test.js +2 -2
- package/dist/schema/LiveStoreEvent.d.ts +6 -5
- package/dist/schema/LiveStoreEvent.d.ts.map +1 -1
- package/dist/schema/LiveStoreEvent.js +5 -0
- package/dist/schema/LiveStoreEvent.js.map +1 -1
- package/dist/schema/schema.d.ts +3 -0
- package/dist/schema/schema.d.ts.map +1 -1
- package/dist/schema/schema.js.map +1 -1
- package/dist/schema/state/sqlite/client-document-def.d.ts +3 -2
- package/dist/schema/state/sqlite/client-document-def.d.ts.map +1 -1
- package/dist/schema/state/sqlite/client-document-def.js +6 -4
- package/dist/schema/state/sqlite/client-document-def.js.map +1 -1
- package/dist/schema/state/sqlite/client-document-def.test.js +76 -1
- package/dist/schema/state/sqlite/client-document-def.test.js.map +1 -1
- package/dist/schema/state/sqlite/column-annotations.d.ts +34 -0
- package/dist/schema/state/sqlite/column-annotations.d.ts.map +1 -0
- package/dist/schema/state/sqlite/column-annotations.js +50 -0
- package/dist/schema/state/sqlite/column-annotations.js.map +1 -0
- package/dist/schema/state/sqlite/column-annotations.test.d.ts +2 -0
- package/dist/schema/state/sqlite/column-annotations.test.d.ts.map +1 -0
- package/dist/schema/state/sqlite/column-annotations.test.js +179 -0
- package/dist/schema/state/sqlite/column-annotations.test.js.map +1 -0
- package/dist/schema/state/sqlite/column-def.d.ts +15 -0
- package/dist/schema/state/sqlite/column-def.d.ts.map +1 -0
- package/dist/schema/state/sqlite/column-def.js +242 -0
- package/dist/schema/state/sqlite/column-def.js.map +1 -0
- package/dist/schema/state/sqlite/column-def.test.d.ts +2 -0
- package/dist/schema/state/sqlite/column-def.test.d.ts.map +1 -0
- package/dist/schema/state/sqlite/column-def.test.js +529 -0
- package/dist/schema/state/sqlite/column-def.test.js.map +1 -0
- package/dist/schema/state/sqlite/column-spec.d.ts +11 -0
- package/dist/schema/state/sqlite/column-spec.d.ts.map +1 -0
- package/dist/schema/state/sqlite/column-spec.js +39 -0
- package/dist/schema/state/sqlite/column-spec.js.map +1 -0
- package/dist/schema/state/sqlite/column-spec.test.d.ts +2 -0
- package/dist/schema/state/sqlite/column-spec.test.d.ts.map +1 -0
- package/dist/schema/state/sqlite/column-spec.test.js +146 -0
- package/dist/schema/state/sqlite/column-spec.test.js.map +1 -0
- package/dist/schema/state/sqlite/db-schema/ast/sqlite.d.ts +1 -0
- package/dist/schema/state/sqlite/db-schema/ast/sqlite.d.ts.map +1 -1
- package/dist/schema/state/sqlite/db-schema/ast/sqlite.js +1 -0
- 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 +17 -4
- 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 +2 -0
- 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 +65 -165
- 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 -0
- package/dist/schema/state/sqlite/db-schema/dsl/mod.js.map +1 -1
- package/dist/schema/state/sqlite/mod.d.ts +2 -0
- package/dist/schema/state/sqlite/mod.d.ts.map +1 -1
- package/dist/schema/state/sqlite/mod.js +2 -0
- package/dist/schema/state/sqlite/mod.js.map +1 -1
- package/dist/schema/state/sqlite/query-builder/api.d.ts +309 -560
- package/dist/schema/state/sqlite/query-builder/api.d.ts.map +1 -1
- package/dist/schema/state/sqlite/query-builder/astToSql.d.ts +1 -0
- package/dist/schema/state/sqlite/query-builder/astToSql.d.ts.map +1 -1
- package/dist/schema/state/sqlite/query-builder/astToSql.js +8 -6
- package/dist/schema/state/sqlite/query-builder/astToSql.js.map +1 -1
- package/dist/schema/state/sqlite/system-tables.d.ts +464 -46
- package/dist/schema/state/sqlite/system-tables.d.ts.map +1 -1
- package/dist/schema/state/sqlite/table-def.d.ts +159 -152
- package/dist/schema/state/sqlite/table-def.d.ts.map +1 -1
- package/dist/schema/state/sqlite/table-def.js +45 -6
- package/dist/schema/state/sqlite/table-def.js.map +1 -1
- package/dist/schema/state/sqlite/table-def.test.d.ts +2 -0
- package/dist/schema/state/sqlite/table-def.test.d.ts.map +1 -0
- package/dist/schema/state/sqlite/table-def.test.js +192 -0
- package/dist/schema/state/sqlite/table-def.test.js.map +1 -0
- package/dist/schema-management/common.d.ts +1 -1
- package/dist/schema-management/common.d.ts.map +1 -1
- package/dist/schema-management/common.js +11 -2
- package/dist/schema-management/common.js.map +1 -1
- package/dist/schema-management/migrations.d.ts +0 -1
- package/dist/schema-management/migrations.d.ts.map +1 -1
- package/dist/schema-management/migrations.js +4 -30
- package/dist/schema-management/migrations.js.map +1 -1
- package/dist/schema-management/migrations.test.d.ts +2 -0
- package/dist/schema-management/migrations.test.d.ts.map +1 -0
- package/dist/schema-management/migrations.test.js +52 -0
- package/dist/schema-management/migrations.test.js.map +1 -0
- package/dist/sql-queries/types.d.ts +37 -133
- package/dist/sqlite-db-helper.d.ts +3 -1
- package/dist/sqlite-db-helper.d.ts.map +1 -1
- package/dist/sqlite-db-helper.js +16 -0
- package/dist/sqlite-db-helper.js.map +1 -1
- package/dist/sqlite-types.d.ts +4 -4
- package/dist/sqlite-types.d.ts.map +1 -1
- package/dist/sync/ClientSessionSyncProcessor.d.ts +2 -2
- package/dist/sync/ClientSessionSyncProcessor.d.ts.map +1 -1
- package/dist/sync/ClientSessionSyncProcessor.js +8 -7
- package/dist/sync/ClientSessionSyncProcessor.js.map +1 -1
- package/dist/sync/sync.d.ts.map +1 -1
- package/dist/sync/sync.js.map +1 -1
- package/dist/util.d.ts +3 -3
- package/dist/util.d.ts.map +1 -1
- package/dist/util.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +4 -4
- package/src/ClientSessionLeaderThreadProxy.ts +2 -2
- package/src/adapter-types.ts +6 -4
- package/src/devtools/devtools-messages-leader.ts +3 -3
- package/src/leader-thread/LeaderSyncProcessor.ts +3 -1
- package/src/leader-thread/make-leader-thread-layer.ts +26 -7
- package/src/leader-thread/shutdown-channel.ts +2 -2
- package/src/leader-thread/types.ts +1 -1
- package/src/materializer-helper.ts +5 -11
- package/src/rematerialize-from-eventlog.ts +2 -2
- package/src/schema/EventSequenceNumber.test.ts +2 -2
- package/src/schema/EventSequenceNumber.ts +8 -2
- package/src/schema/LiveStoreEvent.ts +7 -1
- package/src/schema/schema.ts +4 -0
- package/src/schema/state/sqlite/client-document-def.test.ts +89 -1
- package/src/schema/state/sqlite/client-document-def.ts +7 -4
- package/src/schema/state/sqlite/column-annotations.test.ts +212 -0
- package/src/schema/state/sqlite/column-annotations.ts +77 -0
- package/src/schema/state/sqlite/column-def.test.ts +665 -0
- package/src/schema/state/sqlite/column-def.ts +290 -0
- package/src/schema/state/sqlite/column-spec.test.ts +223 -0
- package/src/schema/state/sqlite/column-spec.ts +42 -0
- package/src/schema/state/sqlite/db-schema/ast/sqlite.ts +2 -0
- package/src/schema/state/sqlite/db-schema/dsl/__snapshots__/field-defs.test.ts.snap +15 -0
- package/src/schema/state/sqlite/db-schema/dsl/field-defs.ts +20 -2
- package/src/schema/state/sqlite/db-schema/dsl/mod.ts +1 -0
- package/src/schema/state/sqlite/mod.ts +2 -0
- package/src/schema/state/sqlite/query-builder/api.ts +4 -3
- package/src/schema/state/sqlite/query-builder/astToSql.ts +9 -7
- package/src/schema/state/sqlite/table-def.test.ts +241 -0
- package/src/schema/state/sqlite/table-def.ts +222 -16
- package/src/schema-management/common.ts +10 -3
- package/src/schema-management/migrations.ts +4 -33
- package/src/sqlite-db-helper.ts +19 -1
- package/src/sqlite-types.ts +4 -4
- package/src/sync/ClientSessionSyncProcessor.ts +13 -8
- package/src/sync/sync.ts +2 -0
- package/src/util.ts +7 -2
- package/src/version.ts +1 -1
@@ -1,12 +1,16 @@
|
|
1
|
-
import type
|
2
|
-
import type
|
1
|
+
import { type Nullable, shouldNeverHappen } from '@livestore/utils'
|
2
|
+
import { Option, type Schema, SchemaAST, type Types } from '@livestore/utils/effect'
|
3
3
|
|
4
|
+
import { getColumnDefForSchema, schemaFieldsToColumns } from './column-def.ts'
|
4
5
|
import { SqliteDsl } from './db-schema/mod.ts'
|
5
6
|
import type { QueryBuilder } from './query-builder/mod.ts'
|
6
7
|
import { makeQueryBuilder, QueryBuilderAstSymbol, QueryBuilderTypeId } from './query-builder/mod.ts'
|
7
8
|
|
8
9
|
export const { blob, boolean, column, datetime, integer, isColumnDefinition, json, real, text } = SqliteDsl
|
9
10
|
|
11
|
+
// Re-export the column definition function
|
12
|
+
export { getColumnDefForSchema }
|
13
|
+
|
10
14
|
export type StateType = 'singleton' | 'dynamic'
|
11
15
|
|
12
16
|
export type DefaultSqliteTableDef = SqliteDsl.TableDefinition<string, SqliteDsl.Columns>
|
@@ -17,6 +21,7 @@ export const TableDefInternalsSymbol = Symbol('TableDefInternals')
|
|
17
21
|
export type TableDefInternalsSymbol = typeof TableDefInternalsSymbol
|
18
22
|
|
19
23
|
export type TableDefBase<
|
24
|
+
// TODO replace SqliteDef type param with Effect Schema (see below)
|
20
25
|
TSqliteDef extends DefaultSqliteTableDef = DefaultSqliteTableDefConstrained,
|
21
26
|
TOptions extends TableOptions = TableOptions,
|
22
27
|
> = {
|
@@ -28,10 +33,14 @@ export type TableDefBase<
|
|
28
33
|
}
|
29
34
|
|
30
35
|
export type TableDef<
|
36
|
+
// TODO replace SqliteDef type param with Effect Schema
|
37
|
+
// We can only do this with Effect Schema v4 once the default values are tracked on the type level
|
38
|
+
// https://github.com/livestorejs/livestore/issues/382
|
31
39
|
TSqliteDef extends DefaultSqliteTableDef = DefaultSqliteTableDefConstrained,
|
32
40
|
TOptions extends TableOptions = TableOptions,
|
33
41
|
// NOTE we're not using `SqliteDsl.StructSchemaForColumns<TSqliteDef['columns']>`
|
34
42
|
// as we don't want the alias type for users to show up, so we're redefining it here
|
43
|
+
// TODO adjust this to `TSchema = Schema.TypeLiteral<` but requires some advance type-level work
|
35
44
|
TSchema = Schema.Schema<
|
36
45
|
SqliteDsl.AnyIfConstained<
|
37
46
|
TSqliteDef['columns'],
|
@@ -66,28 +75,192 @@ export type TableOptions = {
|
|
66
75
|
readonly isClientDocumentTable: boolean
|
67
76
|
}
|
68
77
|
|
69
|
-
|
78
|
+
/**
|
79
|
+
* Creates a SQLite table definition from columns or an Effect Schema.
|
80
|
+
*
|
81
|
+
* This function supports two main ways to define a table:
|
82
|
+
* 1. Using explicit column definitions
|
83
|
+
* 2. Using an Effect Schema (either the `name` property needs to be provided or the schema needs to have a title/identifier)
|
84
|
+
*
|
85
|
+
* ```ts
|
86
|
+
* // Using explicit columns
|
87
|
+
* const usersTable = State.SQLite.table({
|
88
|
+
* name: 'users',
|
89
|
+
* columns: {
|
90
|
+
* id: State.SQLite.text({ primaryKey: true }),
|
91
|
+
* name: State.SQLite.text({ nullable: false }),
|
92
|
+
* email: State.SQLite.text({ nullable: false }),
|
93
|
+
* age: State.SQLite.integer({ nullable: true }),
|
94
|
+
* },
|
95
|
+
* })
|
96
|
+
* ```
|
97
|
+
*
|
98
|
+
* ```ts
|
99
|
+
* // Using Effect Schema with annotations
|
100
|
+
* import { Schema } from '@livestore/utils/effect'
|
101
|
+
*
|
102
|
+
* const UserSchema = Schema.Struct({
|
103
|
+
* id: Schema.Int.pipe(State.SQLite.withPrimaryKey).pipe(State.SQLite.withAutoIncrement),
|
104
|
+
* email: Schema.String.pipe(State.SQLite.withUnique),
|
105
|
+
* name: Schema.String,
|
106
|
+
* active: Schema.Boolean.pipe(State.SQLite.withDefault(true)),
|
107
|
+
* createdAt: Schema.optional(Schema.Date),
|
108
|
+
* })
|
109
|
+
*
|
110
|
+
* // Option 1: With explicit name
|
111
|
+
* const usersTable = State.SQLite.table({
|
112
|
+
* name: 'users',
|
113
|
+
* schema: UserSchema,
|
114
|
+
* })
|
115
|
+
*
|
116
|
+
* // Option 2: With name from schema annotation (title or identifier)
|
117
|
+
* const AnnotatedUserSchema = UserSchema.annotations({ title: 'users' })
|
118
|
+
* const usersTable2 = State.SQLite.table({
|
119
|
+
* schema: AnnotatedUserSchema,
|
120
|
+
* })
|
121
|
+
* ```
|
122
|
+
*
|
123
|
+
* ```ts
|
124
|
+
* // Adding indexes
|
125
|
+
* const PostSchema = Schema.Struct({
|
126
|
+
* id: Schema.String.pipe(State.SQLite.withPrimaryKey),
|
127
|
+
* title: Schema.String,
|
128
|
+
* authorId: Schema.String,
|
129
|
+
* createdAt: Schema.Date,
|
130
|
+
* }).annotations({ identifier: 'posts' })
|
131
|
+
*
|
132
|
+
* const postsTable = State.SQLite.table({
|
133
|
+
* schema: PostSchema,
|
134
|
+
* indexes: [
|
135
|
+
* { name: 'idx_posts_author', columns: ['authorId'] },
|
136
|
+
* { name: 'idx_posts_created', columns: ['createdAt'], isUnique: false },
|
137
|
+
* ],
|
138
|
+
* })
|
139
|
+
* ```
|
140
|
+
*
|
141
|
+
* @remarks
|
142
|
+
* - Primary key columns are automatically non-nullable
|
143
|
+
* - Columns with `State.SQLite.withUnique` annotation automatically get unique indexes
|
144
|
+
* - The `State.SQLite.withAutoIncrement` annotation only works with integer primary keys
|
145
|
+
* - Default values can be literal values or SQL expressions
|
146
|
+
* - When using Effect Schema without explicit name, the schema must have a title or identifier annotation
|
147
|
+
*/
|
148
|
+
// Overload 1: With columns
|
149
|
+
// TODO drop support for `column` when Effect Schema v4 is released
|
150
|
+
export function table<
|
70
151
|
TName extends string,
|
71
|
-
TColumns extends SqliteDsl.Columns | SqliteDsl.ColumnDefinition
|
152
|
+
TColumns extends SqliteDsl.Columns | SqliteDsl.ColumnDefinition.Any,
|
72
153
|
const TOptionsInput extends TableOptionsInput = TableOptionsInput,
|
73
154
|
>(
|
74
155
|
args: {
|
75
156
|
name: TName
|
76
157
|
columns: TColumns
|
77
158
|
} & Partial<TOptionsInput>,
|
78
|
-
): TableDef<SqliteTableDefForInput<TName, TColumns>, WithDefaults<TColumns>>
|
79
|
-
|
80
|
-
|
159
|
+
): TableDef<SqliteTableDefForInput<TName, TColumns>, WithDefaults<TColumns>>
|
160
|
+
|
161
|
+
// Overload 2: With schema and explicit name
|
162
|
+
export function table<
|
163
|
+
TName extends string,
|
164
|
+
TSchema extends Schema.Schema.AnyNoContext,
|
165
|
+
const TOptionsInput extends TableOptionsInput = TableOptionsInput,
|
166
|
+
>(
|
167
|
+
args: {
|
168
|
+
name: TName
|
169
|
+
schema: TSchema
|
170
|
+
} & Partial<TOptionsInput>,
|
171
|
+
): TableDef<
|
172
|
+
SqliteTableDefForSchemaInput<TName, Schema.Schema.Type<TSchema>, Schema.Schema.Encoded<TSchema>, TSchema>,
|
173
|
+
TableOptions
|
174
|
+
>
|
175
|
+
|
176
|
+
// Overload 3: With schema and no name (uses schema annotations)
|
177
|
+
export function table<
|
178
|
+
TSchema extends Schema.Schema.AnyNoContext,
|
179
|
+
const TOptionsInput extends TableOptionsInput = TableOptionsInput,
|
180
|
+
>(
|
181
|
+
args: {
|
182
|
+
schema: TSchema
|
183
|
+
} & Partial<TOptionsInput>,
|
184
|
+
): TableDef<
|
185
|
+
SqliteTableDefForSchemaInput<string, Schema.Schema.Type<TSchema>, Schema.Schema.Encoded<TSchema>, TSchema>,
|
186
|
+
TableOptions
|
187
|
+
>
|
188
|
+
|
189
|
+
// Implementation
|
190
|
+
export function table<
|
191
|
+
TName extends string,
|
192
|
+
TColumns extends SqliteDsl.Columns | SqliteDsl.ColumnDefinition.Any,
|
193
|
+
const TOptionsInput extends TableOptionsInput = TableOptionsInput,
|
194
|
+
>(
|
195
|
+
args: (
|
196
|
+
| {
|
197
|
+
name: TName
|
198
|
+
columns: TColumns
|
199
|
+
}
|
200
|
+
| {
|
201
|
+
name: TName
|
202
|
+
schema: Schema.Schema.AnyNoContext
|
203
|
+
}
|
204
|
+
| {
|
205
|
+
schema: Schema.Schema.AnyNoContext
|
206
|
+
}
|
207
|
+
) &
|
208
|
+
Partial<TOptionsInput>,
|
209
|
+
): TableDef<any, any> {
|
210
|
+
const { ...options } = args
|
211
|
+
|
212
|
+
let tableName: string
|
213
|
+
let columns: SqliteDsl.Columns
|
214
|
+
let additionalIndexes: SqliteDsl.Index[] = []
|
215
|
+
|
216
|
+
if ('columns' in args) {
|
217
|
+
tableName = args.name
|
218
|
+
const columnOrColumns = args.columns
|
219
|
+
columns = (
|
220
|
+
SqliteDsl.isColumnDefinition(columnOrColumns) ? { value: columnOrColumns } : columnOrColumns
|
221
|
+
) as SqliteDsl.Columns
|
222
|
+
additionalIndexes = []
|
223
|
+
} else if ('schema' in args) {
|
224
|
+
const result = schemaFieldsToColumns(SchemaAST.getPropertySignatures(args.schema.ast))
|
225
|
+
columns = result.columns
|
226
|
+
|
227
|
+
// We'll set tableName first, then use it for index names
|
228
|
+
let tempTableName: string
|
229
|
+
|
230
|
+
// If name is provided, use it; otherwise extract from schema annotations
|
231
|
+
if ('name' in args) {
|
232
|
+
tempTableName = args.name
|
233
|
+
} else {
|
234
|
+
// Use title or identifier, with preference for title
|
235
|
+
tempTableName = SchemaAST.getTitleAnnotation(args.schema.ast).pipe(
|
236
|
+
Option.orElse(() => SchemaAST.getIdentifierAnnotation(args.schema.ast)),
|
237
|
+
Option.getOrElse(() =>
|
238
|
+
shouldNeverHappen(
|
239
|
+
'When using schema without explicit name, the schema must have a title or identifier annotation',
|
240
|
+
),
|
241
|
+
),
|
242
|
+
)
|
243
|
+
}
|
244
|
+
|
245
|
+
tableName = tempTableName
|
246
|
+
|
247
|
+
// Create unique indexes for columns with unique annotation
|
248
|
+
additionalIndexes = (result.uniqueColumns || []).map((columnName) => ({
|
249
|
+
name: `idx_${tableName}_${columnName}_unique`,
|
250
|
+
columns: [columnName],
|
251
|
+
isUnique: true,
|
252
|
+
}))
|
253
|
+
} else {
|
254
|
+
return shouldNeverHappen('Either `columns` or `schema` must be provided when calling `table()`')
|
255
|
+
}
|
81
256
|
|
82
257
|
const options_: TableOptions = {
|
83
258
|
isClientDocumentTable: false,
|
84
259
|
}
|
85
260
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
const sqliteDef = SqliteDsl.table(tablePath, columns, options?.indexes ?? [])
|
261
|
+
// Combine user-provided indexes with unique column indexes
|
262
|
+
const allIndexes = [...(options?.indexes ?? []), ...additionalIndexes]
|
263
|
+
const sqliteDef = SqliteDsl.table(tableName, columns, allIndexes)
|
91
264
|
|
92
265
|
const rowSchema = SqliteDsl.structSchemaForTable(sqliteDef)
|
93
266
|
const insertSchema = SqliteDsl.insertStructSchemaForTable(sqliteDef)
|
@@ -179,19 +352,52 @@ export namespace FromColumns {
|
|
179
352
|
|
180
353
|
export type SqliteTableDefForInput<
|
181
354
|
TName extends string,
|
182
|
-
TColumns extends SqliteDsl.Columns | SqliteDsl.ColumnDefinition
|
355
|
+
TColumns extends SqliteDsl.Columns | SqliteDsl.ColumnDefinition.Any,
|
183
356
|
> = SqliteDsl.TableDefinition<TName, PrettifyFlat<ToColumns<TColumns>>>
|
184
357
|
|
185
|
-
type
|
358
|
+
export type SqliteTableDefForSchemaInput<
|
359
|
+
TName extends string,
|
360
|
+
TType,
|
361
|
+
TEncoded,
|
362
|
+
_TSchema = any,
|
363
|
+
> = TableDefInput.ForSchema<TName, TType, TEncoded, _TSchema>
|
364
|
+
|
365
|
+
export type WithDefaults<TColumns extends SqliteDsl.Columns | SqliteDsl.ColumnDefinition.Any> = {
|
186
366
|
isClientDocumentTable: false
|
187
367
|
requiredInsertColumnNames: SqliteDsl.FromColumns.RequiredInsertColumnNames<ToColumns<TColumns>>
|
188
368
|
}
|
189
369
|
|
190
370
|
export type PrettifyFlat<T> = T extends infer U ? { [K in keyof U]: U[K] } : never
|
191
371
|
|
192
|
-
type ToColumns<TColumns extends SqliteDsl.Columns | SqliteDsl.ColumnDefinition
|
372
|
+
export type ToColumns<TColumns extends SqliteDsl.Columns | SqliteDsl.ColumnDefinition.Any> =
|
193
373
|
TColumns extends SqliteDsl.Columns
|
194
374
|
? TColumns
|
195
|
-
: TColumns extends SqliteDsl.ColumnDefinition
|
375
|
+
: TColumns extends SqliteDsl.ColumnDefinition.Any
|
196
376
|
? { value: TColumns }
|
197
377
|
: never
|
378
|
+
|
379
|
+
export declare namespace SchemaToColumns {
|
380
|
+
// Type helper to create column definition with proper schema
|
381
|
+
export type ColumnDefForType<TEncoded, TType> = SqliteDsl.ColumnDefinition<TEncoded, TType>
|
382
|
+
|
383
|
+
// Create columns type from schema Type and Encoded
|
384
|
+
export type FromTypes<TType, TEncoded> = TType extends Record<string, any>
|
385
|
+
? TEncoded extends Record<string, any>
|
386
|
+
? {
|
387
|
+
[K in keyof TType & keyof TEncoded]: ColumnDefForType<TEncoded[K], TType[K]>
|
388
|
+
}
|
389
|
+
: SqliteDsl.Columns
|
390
|
+
: SqliteDsl.Columns
|
391
|
+
}
|
392
|
+
|
393
|
+
export declare namespace TableDefInput {
|
394
|
+
export type ForColumns<
|
395
|
+
TName extends string,
|
396
|
+
TColumns extends SqliteDsl.Columns | SqliteDsl.ColumnDefinition.Any,
|
397
|
+
> = SqliteDsl.TableDefinition<TName, PrettifyFlat<ToColumns<TColumns>>>
|
398
|
+
|
399
|
+
export type ForSchema<TName extends string, TType, TEncoded, _TSchema = any> = SqliteDsl.TableDefinition<
|
400
|
+
TName,
|
401
|
+
SchemaToColumns.FromTypes<TType, TEncoded>
|
402
|
+
>
|
403
|
+
}
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import type
|
1
|
+
import { type SqliteDb, SqliteError } from '../adapter-types.ts'
|
2
2
|
import type { ParamsObject } from '../util.ts'
|
3
3
|
import { prepareBindValues } from '../util.ts'
|
4
4
|
|
@@ -15,9 +15,16 @@ export const dbExecute = (db: SqliteDb, queryStr: string, bindValues?: ParamsObj
|
|
15
15
|
|
16
16
|
const preparedBindValues = bindValues ? prepareBindValues(bindValues, queryStr) : undefined
|
17
17
|
|
18
|
-
|
18
|
+
try {
|
19
|
+
stmt.execute(preparedBindValues)
|
19
20
|
|
20
|
-
|
21
|
+
stmt.finalize()
|
22
|
+
} catch (cause) {
|
23
|
+
throw new SqliteError({
|
24
|
+
cause,
|
25
|
+
query: { sql: queryStr, bindValues: preparedBindValues ?? {} },
|
26
|
+
})
|
27
|
+
}
|
21
28
|
}
|
22
29
|
|
23
30
|
export const dbSelect = <T>(db: SqliteDb, queryStr: string, bindValues?: ParamsObject) => {
|
@@ -1,11 +1,12 @@
|
|
1
1
|
import { memoizeByStringifyArgs } from '@livestore/utils'
|
2
|
-
import { Effect
|
2
|
+
import { Effect } from '@livestore/utils/effect'
|
3
3
|
|
4
4
|
import type { SqliteDb } from '../adapter-types.ts'
|
5
5
|
import type { MigrationsReport, MigrationsReportEntry } from '../defs.ts'
|
6
6
|
import type { UnexpectedError } from '../errors.ts'
|
7
7
|
import type { LiveStoreSchema } from '../schema/mod.ts'
|
8
|
-
import {
|
8
|
+
import { makeColumnSpec } from '../schema/state/sqlite/column-spec.ts'
|
9
|
+
import { SqliteAst } from '../schema/state/sqlite/db-schema/mod.ts'
|
9
10
|
import type { SchemaEventDefsMetaRow, SchemaMetaRow } from '../schema/state/sqlite/system-tables.ts'
|
10
11
|
import {
|
11
12
|
isStateSystemTable,
|
@@ -169,35 +170,5 @@ export const migrateTable = ({
|
|
169
170
|
|
170
171
|
const createIndexFromDefinition = (tableName: string, index: SqliteAst.Index) => {
|
171
172
|
const uniqueStr = index.unique ? 'UNIQUE' : ''
|
172
|
-
return sql`create ${uniqueStr} index if not exists '${index.name}' on '${tableName}' (${index.columns.join(', ')})`
|
173
|
-
}
|
174
|
-
|
175
|
-
export const makeColumnSpec = (tableAst: SqliteAst.Table) => {
|
176
|
-
const primaryKeys = tableAst.columns.filter((_) => _.primaryKey).map((_) => `'${_.name}'`)
|
177
|
-
const columnDefStrs = tableAst.columns.map(toSqliteColumnSpec)
|
178
|
-
if (primaryKeys.length > 0) {
|
179
|
-
columnDefStrs.push(`PRIMARY KEY (${primaryKeys.join(', ')})`)
|
180
|
-
}
|
181
|
-
|
182
|
-
return columnDefStrs.join(', ')
|
183
|
-
}
|
184
|
-
|
185
|
-
/** NOTE primary keys are applied on a table level not on a column level to account for multi-column primary keys */
|
186
|
-
const toSqliteColumnSpec = (column: SqliteAst.Column) => {
|
187
|
-
const columnTypeStr = column.type._tag
|
188
|
-
const nullableStr = column.nullable === false ? 'not null' : ''
|
189
|
-
const defaultValueStr = (() => {
|
190
|
-
if (column.default._tag === 'None') return ''
|
191
|
-
|
192
|
-
if (column.default.value === null) return 'default null'
|
193
|
-
if (SqliteDsl.isSqlDefaultValue(column.default.value)) return `default ${column.default.value.sql}`
|
194
|
-
|
195
|
-
const encodeValue = EffectSchema.encodeSync(column.schema)
|
196
|
-
const encodedDefaultValue = encodeValue(column.default.value)
|
197
|
-
|
198
|
-
if (columnTypeStr === 'text') return `default '${encodedDefaultValue}'`
|
199
|
-
return `default ${encodedDefaultValue}`
|
200
|
-
})()
|
201
|
-
|
202
|
-
return `'${column.name}' ${columnTypeStr} ${nullableStr} ${defaultValueStr}`
|
173
|
+
return sql`create ${uniqueStr} index if not exists '${index.name}' on '${tableName}' (${index.columns.map((col) => `'${col}'`).join(', ')})`
|
203
174
|
}
|
package/src/sqlite-db-helper.ts
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
import { Schema } from '@livestore/utils/effect'
|
2
2
|
|
3
|
-
import type
|
3
|
+
import { type SqliteDb, SqliteError } from './adapter-types.ts'
|
4
4
|
import { getResultSchema, isQueryBuilder } from './schema/state/sqlite/query-builder/mod.ts'
|
5
5
|
import type { PreparedBindValues } from './util.ts'
|
6
6
|
|
@@ -39,3 +39,21 @@ export const makeSelect = <T>(
|
|
39
39
|
}
|
40
40
|
}
|
41
41
|
}
|
42
|
+
|
43
|
+
export const validateSnapshot = (snapshot: Uint8Array) => {
|
44
|
+
const headerBytes = new TextDecoder().decode(snapshot.slice(0, 16))
|
45
|
+
const hasValidHeader = headerBytes.startsWith('SQLite format 3')
|
46
|
+
|
47
|
+
if (!hasValidHeader) {
|
48
|
+
throw new SqliteError({
|
49
|
+
cause: 'Invalid SQLite header',
|
50
|
+
note: `Expected header to start with 'SQLite format 3', but got: ${headerBytes}`,
|
51
|
+
})
|
52
|
+
}
|
53
|
+
}
|
54
|
+
|
55
|
+
export const makeExport = (exportFn: () => Uint8Array<ArrayBuffer>) => () => {
|
56
|
+
const snapshot = exportFn()
|
57
|
+
validateSnapshot(snapshot)
|
58
|
+
return snapshot
|
59
|
+
}
|
package/src/sqlite-types.ts
CHANGED
@@ -25,12 +25,12 @@ export interface SqliteDb<TReq = any, TMetadata extends TReq = TReq> {
|
|
25
25
|
select<T>(queryStr: string, bindValues?: PreparedBindValues | undefined): ReadonlyArray<T>
|
26
26
|
select<T>(queryBuilder: QueryBuilder<T, any, any>): T
|
27
27
|
|
28
|
-
export(): Uint8Array
|
29
|
-
import: (data: Uint8Array | SqliteDb<TReq>) => void
|
28
|
+
export(): Uint8Array<ArrayBuffer>
|
29
|
+
import: (data: Uint8Array<ArrayBuffer> | SqliteDb<TReq>) => void
|
30
30
|
close(): void
|
31
31
|
destroy(): void
|
32
32
|
session(): SqliteDbSession
|
33
|
-
makeChangeset: (data: Uint8Array) => SqliteDbChangeset
|
33
|
+
makeChangeset: (data: Uint8Array<ArrayBuffer>) => SqliteDbChangeset
|
34
34
|
}
|
35
35
|
|
36
36
|
export type SqliteDebugInfo = { head: EventSequenceNumber.EventSequenceNumber }
|
@@ -56,7 +56,7 @@ export interface PreparedStatement {
|
|
56
56
|
}
|
57
57
|
|
58
58
|
export type SqliteDbSession = {
|
59
|
-
changeset: () => Uint8Array | undefined
|
59
|
+
changeset: () => Uint8Array<ArrayBuffer> | undefined
|
60
60
|
finish: () => void
|
61
61
|
}
|
62
62
|
|
@@ -3,6 +3,7 @@ import { LS_DEV, shouldNeverHappen, TRACE_VERBOSE } from '@livestore/utils'
|
|
3
3
|
import {
|
4
4
|
BucketQueue,
|
5
5
|
Effect,
|
6
|
+
Exit,
|
6
7
|
FiberHandle,
|
7
8
|
Option,
|
8
9
|
Queue,
|
@@ -54,10 +55,13 @@ export const makeClientSessionSyncProcessor = ({
|
|
54
55
|
options: { otelContext: otel.Context; withChangeset: boolean; materializerHashLeader: Option.Option<number> },
|
55
56
|
) => {
|
56
57
|
writeTables: Set<string>
|
57
|
-
sessionChangeset:
|
58
|
+
sessionChangeset:
|
59
|
+
| { _tag: 'sessionChangeset'; data: Uint8Array<ArrayBuffer>; debug: any }
|
60
|
+
| { _tag: 'no-op' }
|
61
|
+
| { _tag: 'unset' }
|
58
62
|
materializerHash: Option.Option<number>
|
59
63
|
}
|
60
|
-
rollback: (changeset: Uint8Array) => void
|
64
|
+
rollback: (changeset: Uint8Array<ArrayBuffer>) => void
|
61
65
|
refreshTables: (tables: Set<string>) => void
|
62
66
|
span: otel.Span
|
63
67
|
params: {
|
@@ -316,7 +320,7 @@ export const makeClientSessionSyncProcessor = ({
|
|
316
320
|
refreshTables(writeTables)
|
317
321
|
}).pipe(
|
318
322
|
Effect.tapCauseLogPretty,
|
319
|
-
Effect.catchAllCause((cause) => clientSession.shutdown(cause)),
|
323
|
+
Effect.catchAllCause((cause) => clientSession.shutdown(Exit.failCause(cause))),
|
320
324
|
),
|
321
325
|
),
|
322
326
|
Stream.runDrain,
|
@@ -381,13 +385,14 @@ export interface ClientSessionSyncProcessor {
|
|
381
385
|
// TODO turn this into a build-time "macro" so all simulation snippets are removed for production builds
|
382
386
|
const SIMULATION_ENABLED = true
|
383
387
|
|
388
|
+
// Warning: High values for the simulation params can lead to very long test runs since those get multiplied with the number of events
|
384
389
|
export const ClientSessionSyncProcessorSimulationParams = Schema.Struct({
|
385
390
|
pull: Schema.Struct({
|
386
|
-
'1_before_leader_push_fiber_interrupt': Schema.Int.pipe(Schema.between(0,
|
387
|
-
'2_before_leader_push_queue_clear': Schema.Int.pipe(Schema.between(0,
|
388
|
-
'3_before_rebase_rollback': Schema.Int.pipe(Schema.between(0,
|
389
|
-
'4_before_leader_push_queue_offer': Schema.Int.pipe(Schema.between(0,
|
390
|
-
'5_before_leader_push_fiber_run': Schema.Int.pipe(Schema.between(0,
|
391
|
+
'1_before_leader_push_fiber_interrupt': Schema.Int.pipe(Schema.between(0, 25)),
|
392
|
+
'2_before_leader_push_queue_clear': Schema.Int.pipe(Schema.between(0, 25)),
|
393
|
+
'3_before_rebase_rollback': Schema.Int.pipe(Schema.between(0, 25)),
|
394
|
+
'4_before_leader_push_queue_offer': Schema.Int.pipe(Schema.between(0, 25)),
|
395
|
+
'5_before_leader_push_fiber_run': Schema.Int.pipe(Schema.between(0, 25)),
|
391
396
|
}),
|
392
397
|
})
|
393
398
|
type ClientSessionSyncProcessorSimulationParams = typeof ClientSessionSyncProcessorSimulationParams.Type
|
package/src/sync/sync.ts
CHANGED
@@ -31,6 +31,7 @@ export type SyncOptions = {
|
|
31
31
|
onSyncError?: 'shutdown' | 'ignore'
|
32
32
|
}
|
33
33
|
|
34
|
+
// TODO rename to `SyncProviderClientConstructor`
|
34
35
|
export type SyncBackendConstructor<TSyncMetadata = Schema.JsonValue> = (
|
35
36
|
args: MakeBackendArgs,
|
36
37
|
) => Effect.Effect<SyncBackend<TSyncMetadata>, UnexpectedError, Scope.Scope | HttpClient.HttpClient>
|
@@ -41,6 +42,7 @@ export type SyncBackendConstructor<TSyncMetadata = Schema.JsonValue> = (
|
|
41
42
|
// - dynamic sync backend data;
|
42
43
|
// - data center location (e.g. colo on CF workers)
|
43
44
|
|
45
|
+
// TODO rename to `SyncProviderClient`
|
44
46
|
export type SyncBackend<TSyncMetadata = Schema.JsonValue> = {
|
45
47
|
/**
|
46
48
|
* Can be implemented to prepare a connection to the sync backend to speed up the first pull/push.
|
package/src/util.ts
CHANGED
@@ -4,11 +4,16 @@ import type { Brand } from '@livestore/utils/effect'
|
|
4
4
|
import { Schema } from '@livestore/utils/effect'
|
5
5
|
|
6
6
|
export type ParamsObject = Record<string, SqlValue>
|
7
|
-
export type SqlValue = string | number | Uint8Array | null
|
7
|
+
export type SqlValue = string | number | Uint8Array<ArrayBuffer> | null
|
8
8
|
|
9
9
|
export type Bindable = ReadonlyArray<SqlValue> | ParamsObject
|
10
10
|
|
11
|
-
export const SqlValueSchema = Schema.Union(
|
11
|
+
export const SqlValueSchema = Schema.Union(
|
12
|
+
Schema.String,
|
13
|
+
Schema.Number,
|
14
|
+
Schema.Uint8Array as any as Schema.Schema<Uint8Array<ArrayBuffer>>,
|
15
|
+
Schema.Null,
|
16
|
+
)
|
12
17
|
|
13
18
|
export const PreparedBindValues = Schema.Union(
|
14
19
|
Schema.Array(SqlValueSchema),
|
package/src/version.ts
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
// import packageJson from '../package.json' with { type: 'json' }
|
3
3
|
// export const liveStoreVersion = packageJson.version
|
4
4
|
|
5
|
-
export const liveStoreVersion = '0.
|
5
|
+
export const liveStoreVersion = '0.4.0-dev.1' as const
|
6
6
|
|
7
7
|
/**
|
8
8
|
* This version number is incremented whenever the internal storage format changes in a breaking way.
|