@livestore/livestore 0.0.41 → 0.0.42-dev.0
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/{inMemoryDatabase.d.ts → MainDatabaseWrapper.d.ts} +11 -18
- package/dist/MainDatabaseWrapper.d.ts.map +1 -0
- package/dist/{inMemoryDatabase.js → MainDatabaseWrapper.js} +26 -72
- package/dist/MainDatabaseWrapper.js.map +1 -0
- package/dist/__tests__/react/fixture.d.ts +41 -40
- package/dist/__tests__/react/fixture.d.ts.map +1 -1
- package/dist/__tests__/react/fixture.js +2 -8
- package/dist/__tests__/react/fixture.js.map +1 -1
- package/dist/cud.d.ts +9 -9
- package/dist/cud.d.ts.map +1 -1
- package/dist/cud.js +7 -8
- package/dist/cud.js.map +1 -1
- package/dist/effect/LiveStore.d.ts +10 -10
- package/dist/effect/LiveStore.d.ts.map +1 -1
- package/dist/effect/LiveStore.js +2 -11
- package/dist/effect/LiveStore.js.map +1 -1
- package/dist/global-state.d.ts +2 -2
- package/dist/global-state.d.ts.map +1 -1
- package/dist/global-state.js.map +1 -1
- package/dist/index.d.ts +6 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -3
- package/dist/index.js.map +1 -1
- package/dist/migrations.d.ts +5 -5
- package/dist/migrations.d.ts.map +1 -1
- package/dist/migrations.js +37 -21
- package/dist/migrations.js.map +1 -1
- package/dist/query-info.d.ts +8 -9
- package/dist/query-info.d.ts.map +1 -1
- package/dist/query-info.js +1 -1
- package/dist/query-info.js.map +1 -1
- package/dist/react/LiveStoreProvider.d.ts +7 -7
- package/dist/react/LiveStoreProvider.d.ts.map +1 -1
- package/dist/react/LiveStoreProvider.js +11 -19
- package/dist/react/LiveStoreProvider.js.map +1 -1
- package/dist/react/useRow.d.ts +6 -6
- package/dist/react/useRow.d.ts.map +1 -1
- package/dist/react/useRow.js +2 -2
- package/dist/react/useRow.js.map +1 -1
- package/dist/reactiveQueries/sql.js +2 -2
- package/dist/reactiveQueries/sql.js.map +1 -1
- package/dist/reactiveQueries/sql.test.js +12 -31
- package/dist/reactiveQueries/sql.test.js.map +1 -1
- package/dist/row-query.d.ts +6 -6
- package/dist/row-query.d.ts.map +1 -1
- package/dist/row-query.js +9 -12
- package/dist/row-query.js.map +1 -1
- package/dist/store.d.ts +18 -18
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +80 -86
- package/dist/store.js.map +1 -1
- package/dist/utils/bounded-collections.d.ts +1 -1
- package/dist/utils/bounded-collections.d.ts.map +1 -1
- package/dist/utils/bounded-collections.js +2 -1
- package/dist/utils/bounded-collections.js.map +1 -1
- package/dist/utils/util.d.ts +0 -16
- package/dist/utils/util.d.ts.map +1 -1
- package/dist/utils/util.js +0 -38
- package/dist/utils/util.js.map +1 -1
- package/package.json +8 -44
- package/src/{inMemoryDatabase.ts → MainDatabaseWrapper.ts} +40 -94
- package/src/__tests__/react/fixture.tsx +3 -9
- package/src/cud.ts +17 -16
- package/src/effect/LiveStore.ts +12 -24
- package/src/global-state.ts +3 -2
- package/src/index.ts +7 -23
- package/src/migrations.ts +51 -34
- package/src/query-info.ts +10 -9
- package/src/react/LiveStoreProvider.tsx +19 -27
- package/src/react/useRow.ts +24 -12
- package/src/reactiveQueries/sql.test.ts +12 -31
- package/src/reactiveQueries/sql.ts +2 -2
- package/src/row-query.ts +32 -29
- package/src/store.ts +98 -103
- package/src/utils/bounded-collections.ts +3 -2
- package/src/utils/util.ts +0 -44
- package/tsconfig.json +1 -1
- package/vitest.config.js +4 -0
- package/dist/inMemoryDatabase.d.ts.map +0 -1
- package/dist/inMemoryDatabase.js.map +0 -1
- package/dist/schema/index.d.ts +0 -42
- package/dist/schema/index.d.ts.map +0 -1
- package/dist/schema/index.js +0 -42
- package/dist/schema/index.js.map +0 -1
- package/dist/schema/mutations.d.ts +0 -81
- package/dist/schema/mutations.d.ts.map +0 -1
- package/dist/schema/mutations.js +0 -29
- package/dist/schema/mutations.js.map +0 -1
- package/dist/schema/parse-utils.d.ts +0 -6
- package/dist/schema/parse-utils.d.ts.map +0 -1
- package/dist/schema/parse-utils.js +0 -22
- package/dist/schema/parse-utils.js.map +0 -1
- package/dist/schema/system-tables.d.ts +0 -76
- package/dist/schema/system-tables.d.ts.map +0 -1
- package/dist/schema/system-tables.js +0 -11
- package/dist/schema/system-tables.js.map +0 -1
- package/dist/schema/table-def.d.ts +0 -100
- package/dist/schema/table-def.d.ts.map +0 -1
- package/dist/schema/table-def.js +0 -70
- package/dist/schema/table-def.js.map +0 -1
- package/dist/storage/in-memory/index.d.ts +0 -19
- package/dist/storage/in-memory/index.d.ts.map +0 -1
- package/dist/storage/in-memory/index.js +0 -16
- package/dist/storage/in-memory/index.js.map +0 -1
- package/dist/storage/index.d.ts +0 -18
- package/dist/storage/index.d.ts.map +0 -1
- package/dist/storage/index.js +0 -9
- package/dist/storage/index.js.map +0 -1
- package/dist/storage/tauri/index.d.ts +0 -23
- package/dist/storage/tauri/index.d.ts.map +0 -1
- package/dist/storage/tauri/index.js +0 -46
- package/dist/storage/tauri/index.js.map +0 -1
- package/dist/storage/utils/idb.d.ts +0 -11
- package/dist/storage/utils/idb.d.ts.map +0 -1
- package/dist/storage/utils/idb.js +0 -71
- package/dist/storage/utils/idb.js.map +0 -1
- package/dist/storage/web-worker/common.d.ts +0 -11
- package/dist/storage/web-worker/common.d.ts.map +0 -1
- package/dist/storage/web-worker/common.js +0 -2
- package/dist/storage/web-worker/common.js.map +0 -1
- package/dist/storage/web-worker/index.d.ts +0 -34
- package/dist/storage/web-worker/index.d.ts.map +0 -1
- package/dist/storage/web-worker/index.js +0 -134
- package/dist/storage/web-worker/index.js.map +0 -1
- package/dist/storage/web-worker/make-worker.d.ts +0 -20
- package/dist/storage/web-worker/make-worker.d.ts.map +0 -1
- package/dist/storage/web-worker/make-worker.js +0 -155
- package/dist/storage/web-worker/make-worker.js.map +0 -1
- package/dist/storage/web-worker/vite-dev-polyfill.d.ts +0 -2
- package/dist/storage/web-worker/vite-dev-polyfill.d.ts.map +0 -1
- package/dist/storage/web-worker/vite-dev-polyfill.js +0 -35
- package/dist/storage/web-worker/vite-dev-polyfill.js.map +0 -1
- package/src/schema/index.ts +0 -100
- package/src/schema/mutations.ts +0 -128
- package/src/schema/parse-utils.ts +0 -42
- package/src/schema/system-tables.ts +0 -21
- package/src/schema/table-def.ts +0 -270
- package/src/storage/in-memory/index.ts +0 -28
- package/src/storage/index.ts +0 -36
- package/src/storage/tauri/index.ts +0 -66
- package/src/storage/utils/idb.ts +0 -85
- package/src/storage/web-worker/common.ts +0 -6
- package/src/storage/web-worker/index.ts +0 -185
- package/src/storage/web-worker/make-worker.ts +0 -214
- package/src/storage/web-worker/vite-dev-polyfill.ts +0 -33
package/src/schema/index.ts
DELETED
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
import type { ReadonlyArray } from '@livestore/utils/effect'
|
|
2
|
-
import type { SqliteDsl } from 'effect-db-schema'
|
|
3
|
-
|
|
4
|
-
import { isReadonlyArray } from '../utils/util.js'
|
|
5
|
-
import {
|
|
6
|
-
type MutationDef,
|
|
7
|
-
type MutationDefMap,
|
|
8
|
-
type MutationDefRecord,
|
|
9
|
-
type RawSqlMutation,
|
|
10
|
-
rawSqlMutation,
|
|
11
|
-
} from './mutations.js'
|
|
12
|
-
import { systemTables } from './system-tables.js'
|
|
13
|
-
import type { TableDef } from './table-def.js'
|
|
14
|
-
|
|
15
|
-
export * from './system-tables.js'
|
|
16
|
-
export * as DbSchema from './table-def.js'
|
|
17
|
-
export * as ParseUtils from './parse-utils.js'
|
|
18
|
-
export * from './mutations.js'
|
|
19
|
-
|
|
20
|
-
export type LiveStoreSchema<
|
|
21
|
-
TDbSchema extends SqliteDsl.DbSchema = SqliteDsl.DbSchema,
|
|
22
|
-
TMutationsDefRecord extends MutationDefRecord = MutationDefRecord,
|
|
23
|
-
> = {
|
|
24
|
-
/** Only used on type-level */
|
|
25
|
-
readonly _DbSchemaType: TDbSchema
|
|
26
|
-
/** Only used on type-level */
|
|
27
|
-
readonly _MutationDefMapType: TMutationsDefRecord
|
|
28
|
-
|
|
29
|
-
readonly tables: Map<string, TableDef>
|
|
30
|
-
readonly mutations: MutationDefMap
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export type InputSchema = {
|
|
34
|
-
readonly tables: Record<string, TableDef> | ReadonlyArray<TableDef>
|
|
35
|
-
readonly mutations?: ReadonlyArray<MutationDef.Any> | Record<string, MutationDef.Any>
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export const makeSchema = <TInputSchema extends InputSchema>(
|
|
39
|
-
/** Note when using the object-notation for tables/mutations, the object keys are ignored and not used as table/mutation names */
|
|
40
|
-
schema: TInputSchema,
|
|
41
|
-
): LiveStoreSchema<
|
|
42
|
-
DbSchemaFromInputSchemaTables<TInputSchema['tables']>,
|
|
43
|
-
MutationDefRecordFromInputSchemaMutations<TInputSchema['mutations']>
|
|
44
|
-
> => {
|
|
45
|
-
const inputTables: ReadonlyArray<TableDef> = Array.isArray(schema.tables)
|
|
46
|
-
? schema.tables
|
|
47
|
-
: // TODO validate that table names are unique in this case
|
|
48
|
-
Object.values(schema.tables)
|
|
49
|
-
|
|
50
|
-
const tables = new Map<string, TableDef>()
|
|
51
|
-
|
|
52
|
-
for (const tableDef of inputTables) {
|
|
53
|
-
// TODO validate tables (e.g. index names are unique)
|
|
54
|
-
tables.set(tableDef.sqliteDef.ast.name, tableDef)
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
for (const tableDef of systemTables) {
|
|
58
|
-
tables.set(tableDef.sqliteDef.name, tableDef)
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const mutations: MutationDefMap = new Map()
|
|
62
|
-
|
|
63
|
-
if (isReadonlyArray(schema.mutations)) {
|
|
64
|
-
for (const mutation of schema.mutations) {
|
|
65
|
-
mutations.set(mutation.name, mutation)
|
|
66
|
-
}
|
|
67
|
-
} else {
|
|
68
|
-
for (const [name, mutation] of Object.entries(schema.mutations ?? {})) {
|
|
69
|
-
mutations.set(name, mutation)
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
mutations.set('livestore.RawSql', rawSqlMutation)
|
|
74
|
-
|
|
75
|
-
return {
|
|
76
|
-
_DbSchemaType: Symbol('livestore.DbSchemaType') as any,
|
|
77
|
-
_MutationDefMapType: Symbol('livestore.MutationDefMapType') as any,
|
|
78
|
-
tables,
|
|
79
|
-
mutations,
|
|
80
|
-
} satisfies LiveStoreSchema
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* In case of ...
|
|
85
|
-
* - array: we use the table name of each array item (= table definition) as the object key
|
|
86
|
-
* - object: we discard the keys of the input object and use the table name of each object value (= table definition) as the new object key
|
|
87
|
-
*/
|
|
88
|
-
export type DbSchemaFromInputSchemaTables<TTables extends InputSchema['tables']> =
|
|
89
|
-
TTables extends ReadonlyArray<TableDef>
|
|
90
|
-
? { [K in TTables[number] as K['sqliteDef']['name']]: K['sqliteDef'] }
|
|
91
|
-
: TTables extends Record<string, TableDef>
|
|
92
|
-
? { [K in keyof TTables as TTables[K]['sqliteDef']['name']]: TTables[K]['sqliteDef'] }
|
|
93
|
-
: never
|
|
94
|
-
|
|
95
|
-
export type MutationDefRecordFromInputSchemaMutations<TMutations extends InputSchema['mutations']> =
|
|
96
|
-
TMutations extends ReadonlyArray<MutationDef.Any>
|
|
97
|
-
? { [K in TMutations[number] as K['name']]: K } & { 'livestore.RawSql': RawSqlMutation }
|
|
98
|
-
: TMutations extends { [name: string]: MutationDef.Any }
|
|
99
|
-
? { [K in keyof TMutations as TMutations[K]['name']]: TMutations[K] } & { 'livestore.RawSql': RawSqlMutation }
|
|
100
|
-
: never
|
package/src/schema/mutations.ts
DELETED
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
import type { BindValues } from '@livestore/sql-queries'
|
|
2
|
-
import { cuid } from '@livestore/utils/cuid'
|
|
3
|
-
import { Schema } from '@livestore/utils/effect'
|
|
4
|
-
|
|
5
|
-
import type { LiveStoreSchema } from './index.js'
|
|
6
|
-
|
|
7
|
-
export type MutationDefMap = Map<string | 'livestore.RawSql', MutationDef.Any>
|
|
8
|
-
export type MutationDefRecord = {
|
|
9
|
-
'livestore.RawSql': RawSqlMutation
|
|
10
|
-
[name: string]: MutationDef.Any
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export type InternalMutationSchema<TRecord extends MutationDefRecord = MutationDefRecord> = {
|
|
14
|
-
_DefRecord: TRecord
|
|
15
|
-
|
|
16
|
-
map: Map<keyof TRecord, TRecord[keyof TRecord]>
|
|
17
|
-
schemaHashMap: Map<keyof TRecord, number>
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export type MutationDef<TName extends string, TFrom, TTo> = {
|
|
21
|
-
name: TName
|
|
22
|
-
schema: Schema.Schema<TTo, TFrom>
|
|
23
|
-
sql:
|
|
24
|
-
| string
|
|
25
|
-
| ((args: TTo) =>
|
|
26
|
-
| string
|
|
27
|
-
| {
|
|
28
|
-
sql: string
|
|
29
|
-
/** Note args need to be manually encoded to `BindValues` when returning this argument */
|
|
30
|
-
bindValues: BindValues
|
|
31
|
-
writeTables?: ReadonlySet<string>
|
|
32
|
-
})
|
|
33
|
-
|
|
34
|
-
/** Helper function to construct mutation event */
|
|
35
|
-
(args: TTo): { mutation: TName; args: TTo; id: string }
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export namespace MutationDef {
|
|
39
|
-
export type Any = MutationDef<string, any, any>
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// TODO possibly also allow for mutation event subsumption behaviour
|
|
43
|
-
export const defineMutation = <TName extends string, TFrom, TTo>(
|
|
44
|
-
name: TName,
|
|
45
|
-
schema: Schema.Schema<TTo, TFrom>,
|
|
46
|
-
sql: string | ((args: TTo) => string | { sql: string; bindValues: BindValues; writeTables?: ReadonlySet<string> }),
|
|
47
|
-
): MutationDef<TName, TFrom, TTo> => {
|
|
48
|
-
const makeEvent = (args: TTo) => ({ mutation: name, args, id: cuid() })
|
|
49
|
-
|
|
50
|
-
Object.defineProperty(makeEvent, 'name', { value: name })
|
|
51
|
-
Object.defineProperty(makeEvent, 'schema', { value: schema })
|
|
52
|
-
Object.defineProperty(makeEvent, 'sql', { value: sql })
|
|
53
|
-
|
|
54
|
-
return makeEvent as MutationDef<TName, TFrom, TTo>
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export const makeMutationDefRecord = <TInputRecord extends Record<string, MutationDef.Any>>(
|
|
58
|
-
inputRecord: TInputRecord,
|
|
59
|
-
): {
|
|
60
|
-
[K in TInputRecord[keyof TInputRecord]['name']]: Extract<TInputRecord[keyof TInputRecord], { name: K }>
|
|
61
|
-
} => {
|
|
62
|
-
const result: any = {}
|
|
63
|
-
|
|
64
|
-
for (const [name, def] of Object.entries(inputRecord)) {
|
|
65
|
-
result[name] = def
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
result['livestore.RawSql'] = rawSqlMutation
|
|
69
|
-
|
|
70
|
-
return result
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
export const rawSqlMutation = defineMutation(
|
|
74
|
-
'livestore.RawSql',
|
|
75
|
-
Schema.struct({
|
|
76
|
-
sql: Schema.string,
|
|
77
|
-
bindValues: Schema.optional(Schema.record(Schema.string, Schema.any)),
|
|
78
|
-
writeTables: Schema.optional(Schema.readonlySet(Schema.string)),
|
|
79
|
-
}),
|
|
80
|
-
({ sql, bindValues, writeTables }) => ({ sql, bindValues: bindValues ?? {}, writeTables }),
|
|
81
|
-
)
|
|
82
|
-
|
|
83
|
-
export type RawSqlMutation = typeof rawSqlMutation
|
|
84
|
-
export type RawSqlMutationEvent = ReturnType<typeof rawSqlMutation>
|
|
85
|
-
|
|
86
|
-
export type MutationEvent<TMutationsDef extends MutationDef.Any> = {
|
|
87
|
-
mutation: TMutationsDef['name']
|
|
88
|
-
args: Schema.Schema.To<TMutationsDef['schema']>
|
|
89
|
-
id: string
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
export namespace MutationEvent {
|
|
93
|
-
export type Any = MutationEvent<MutationDef.Any>
|
|
94
|
-
|
|
95
|
-
export type ForSchema<TSchema extends LiveStoreSchema> = {
|
|
96
|
-
[K in keyof TSchema['_MutationDefMapType']]: MutationEvent<TSchema['_MutationDefMapType'][K]>
|
|
97
|
-
}[keyof TSchema['_MutationDefMapType']]
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
export type MutationEventSchema<TMutationsDefRecord extends MutationDefRecord> = Schema.Schema<
|
|
101
|
-
{
|
|
102
|
-
[K in keyof TMutationsDefRecord]: {
|
|
103
|
-
mutation: K
|
|
104
|
-
args: Schema.Schema.To<TMutationsDefRecord[K]['schema']>
|
|
105
|
-
id: string
|
|
106
|
-
}
|
|
107
|
-
}[keyof TMutationsDefRecord],
|
|
108
|
-
{
|
|
109
|
-
[K in keyof TMutationsDefRecord]: {
|
|
110
|
-
mutation: K
|
|
111
|
-
args: Schema.Schema.From<TMutationsDefRecord[K]['schema']>
|
|
112
|
-
id: string
|
|
113
|
-
}
|
|
114
|
-
}[keyof TMutationsDefRecord]
|
|
115
|
-
>
|
|
116
|
-
|
|
117
|
-
export const makeMutationEventSchema = <TMutationsDefRecord extends MutationDefRecord>(
|
|
118
|
-
mutationDefRecord: TMutationsDefRecord,
|
|
119
|
-
): MutationEventSchema<TMutationsDefRecord> =>
|
|
120
|
-
Schema.union(
|
|
121
|
-
...Object.values(mutationDefRecord).map((def) =>
|
|
122
|
-
Schema.struct({
|
|
123
|
-
mutation: Schema.literal(def.name),
|
|
124
|
-
args: def.schema,
|
|
125
|
-
id: Schema.string,
|
|
126
|
-
}),
|
|
127
|
-
),
|
|
128
|
-
) as any
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { shouldNeverHappen } from '@livestore/utils'
|
|
2
|
-
import type { ReadonlyArray } from '@livestore/utils/effect'
|
|
3
|
-
import { pipe, ReadonlyRecord, Schema, TreeFormatter } from '@livestore/utils/effect'
|
|
4
|
-
import { SqliteDsl } from 'effect-db-schema' // eslint-disable-line
|
|
5
|
-
|
|
6
|
-
import { type FromColumns, type FromTable, getDefaultValuesDecoded, type TableDef } from './table-def.js'
|
|
7
|
-
|
|
8
|
-
export const many = <TTableDef extends TableDef>(
|
|
9
|
-
table: TTableDef,
|
|
10
|
-
): ((rawRows: ReadonlyArray<any>) => ReadonlyArray<FromTable.RowDecoded<TTableDef>>) => {
|
|
11
|
-
return Schema.decodeSync(Schema.array(table.schema)) as TODO
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export const first =
|
|
15
|
-
<TTableDef extends TableDef>(
|
|
16
|
-
table: TTableDef,
|
|
17
|
-
fallback?: FromColumns.InsertRowDecoded<TTableDef['sqliteDef']['columns']>,
|
|
18
|
-
) =>
|
|
19
|
-
(rawRows: ReadonlyArray<any>) => {
|
|
20
|
-
const rows = Schema.decodeSync(Schema.array(table.schema))(rawRows)
|
|
21
|
-
|
|
22
|
-
if (rows.length === 0) {
|
|
23
|
-
const schemaDefaultValues = getDefaultValuesDecoded(table)
|
|
24
|
-
|
|
25
|
-
const defaultValuesResult = pipe(
|
|
26
|
-
table.sqliteDef.columns,
|
|
27
|
-
ReadonlyRecord.map((_column, columnName) => (fallback as any)?.[columnName] ?? schemaDefaultValues[columnName]),
|
|
28
|
-
Schema.validateEither(table.schema),
|
|
29
|
-
)
|
|
30
|
-
|
|
31
|
-
if (defaultValuesResult._tag === 'Right') {
|
|
32
|
-
return defaultValuesResult.right
|
|
33
|
-
} else {
|
|
34
|
-
console.error('decode error', TreeFormatter.formatError(defaultValuesResult.left))
|
|
35
|
-
return shouldNeverHappen(
|
|
36
|
-
`Expected query (for table ${table.sqliteDef.name}) to return at least one result but found none. Also can't fallback to default values as some were not provided.`,
|
|
37
|
-
)
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return rows[0]!
|
|
42
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { type SqliteAst as __SqliteAst, SqliteDsl } from 'effect-db-schema'
|
|
2
|
-
|
|
3
|
-
import type { FromTable } from './table-def.js'
|
|
4
|
-
import { table } from './table-def.js'
|
|
5
|
-
|
|
6
|
-
export const SCHEMA_META_TABLE = '__livestore_schema'
|
|
7
|
-
|
|
8
|
-
const schemaMetaTable = table(
|
|
9
|
-
SCHEMA_META_TABLE,
|
|
10
|
-
{
|
|
11
|
-
tableName: SqliteDsl.text({ primaryKey: true }),
|
|
12
|
-
schemaHash: SqliteDsl.integer({ nullable: false }),
|
|
13
|
-
/** ISO date format */
|
|
14
|
-
updatedAt: SqliteDsl.text({ nullable: false }),
|
|
15
|
-
},
|
|
16
|
-
{ disableAutomaticIdColumn: true },
|
|
17
|
-
)
|
|
18
|
-
|
|
19
|
-
export type SchemaMetaRow = FromTable.RowDecoded<typeof schemaMetaTable>
|
|
20
|
-
|
|
21
|
-
export const systemTables = [schemaMetaTable]
|
package/src/schema/table-def.ts
DELETED
|
@@ -1,270 +0,0 @@
|
|
|
1
|
-
import { shouldNeverHappen } from '@livestore/utils'
|
|
2
|
-
import { pipe, ReadonlyRecord, Schema } from '@livestore/utils/effect'
|
|
3
|
-
import type { Nullable, PrettifyFlat } from 'effect-db-schema'
|
|
4
|
-
import { SqliteAst, SqliteDsl } from 'effect-db-schema'
|
|
5
|
-
|
|
6
|
-
export const { blob, boolean, column, datetime, integer, isColumnDefinition, json, real, text } = SqliteDsl
|
|
7
|
-
|
|
8
|
-
export { type SqliteDsl } from 'effect-db-schema'
|
|
9
|
-
|
|
10
|
-
import { dynamicallyRegisteredTables } from '../global-state.js'
|
|
11
|
-
|
|
12
|
-
export type StateType = 'singleton' | 'dynamic'
|
|
13
|
-
|
|
14
|
-
export type DefaultSqliteTableDef = SqliteDsl.TableDefinition<string, SqliteDsl.Columns>
|
|
15
|
-
export type DefaultSqliteTableDefConstrained = SqliteDsl.TableDefinition<string, SqliteDsl.ConstraintColumns>
|
|
16
|
-
|
|
17
|
-
// export type TableDefConstraint<
|
|
18
|
-
// TSqliteDef extends DefaultSqliteTableDef = DefaultSqliteTableDef,
|
|
19
|
-
// TIsSingleColumn extends boolean = boolean,
|
|
20
|
-
// TOptions extends TableOptions = TableOptions,
|
|
21
|
-
// > = TableDefBase<TSqliteDef, TIsSingleColumn, TOptions> & { schema: Schema.Schema<any> }
|
|
22
|
-
|
|
23
|
-
// /**
|
|
24
|
-
// * NOTE in the past we used to have a single `TableDef` but there are some TS issues when indroducing
|
|
25
|
-
// * `schema: SqliteDsl.StructSchemaForColumns<TSqliteDef>` so we split it into two types
|
|
26
|
-
// * and only use `TableDefConstraint` in some places
|
|
27
|
-
// */
|
|
28
|
-
// export type TableDefBase<
|
|
29
|
-
// TSqliteDef extends DefaultSqliteTableDef = DefaultSqliteTableDef,
|
|
30
|
-
// TIsSingleColumn extends boolean = boolean,
|
|
31
|
-
// TOptions extends TableOptions = TableOptions,
|
|
32
|
-
// > = {
|
|
33
|
-
// sqliteDef: TSqliteDef
|
|
34
|
-
// // schema: SqliteDsl.StructSchemaForColumns<TSqliteDef>
|
|
35
|
-
// // schema: any;
|
|
36
|
-
// isSingleColumn: TIsSingleColumn
|
|
37
|
-
// options: TOptions
|
|
38
|
-
// }
|
|
39
|
-
|
|
40
|
-
export type TableDef<
|
|
41
|
-
TSqliteDef extends DefaultSqliteTableDef = DefaultSqliteTableDefConstrained,
|
|
42
|
-
TIsSingleColumn extends boolean = boolean,
|
|
43
|
-
TOptions extends TableOptions = TableOptions,
|
|
44
|
-
// NOTE we're not using `SqliteDsl.StructSchemaForColumns<TSqliteDef['columns']>`
|
|
45
|
-
// as we don't want the alias type for users to show up
|
|
46
|
-
TSchema = Schema.Schema<
|
|
47
|
-
SqliteDsl.AnyIfConstained<
|
|
48
|
-
TSqliteDef['columns'],
|
|
49
|
-
{ readonly [K in keyof TSqliteDef['columns']]: Schema.Schema.To<TSqliteDef['columns'][K]['schema']> }
|
|
50
|
-
>,
|
|
51
|
-
SqliteDsl.AnyIfConstained<
|
|
52
|
-
TSqliteDef['columns'],
|
|
53
|
-
{ readonly [K in keyof TSqliteDef['columns']]: Schema.Schema.From<TSqliteDef['columns'][K]['schema']> }
|
|
54
|
-
>
|
|
55
|
-
>,
|
|
56
|
-
> = {
|
|
57
|
-
sqliteDef: TSqliteDef
|
|
58
|
-
isSingleColumn: TIsSingleColumn
|
|
59
|
-
options: TOptions
|
|
60
|
-
schema: TSchema
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export type TableOptionsInput = Partial<TableOptions & { indexes: SqliteDsl.Index[] }>
|
|
64
|
-
|
|
65
|
-
export type TableOptions = {
|
|
66
|
-
/**
|
|
67
|
-
* Setting this to true will have the following consequences:
|
|
68
|
-
* - An `id` column will be added with `primaryKey: true` and `"singleton"` as default value and only allowed value
|
|
69
|
-
* - LiveStore will automatically create the singleton row when the table is created
|
|
70
|
-
* - LiveStore will fail if there is already a column defined with `primaryKey: true`
|
|
71
|
-
*
|
|
72
|
-
* @default false
|
|
73
|
-
*/
|
|
74
|
-
isSingleton: boolean
|
|
75
|
-
// TODO
|
|
76
|
-
dynamicRegistration: boolean
|
|
77
|
-
disableAutomaticIdColumn: boolean
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export const table = <
|
|
81
|
-
TName extends string,
|
|
82
|
-
TColumns extends SqliteDsl.Columns | SqliteDsl.ColumnDefinition<any, any>,
|
|
83
|
-
const TOptionsInput extends TableOptionsInput = TableOptionsInput,
|
|
84
|
-
>(
|
|
85
|
-
name: TName,
|
|
86
|
-
columnOrColumns: TColumns,
|
|
87
|
-
// type?: TStateType,
|
|
88
|
-
options?: TOptionsInput,
|
|
89
|
-
): TableDef<
|
|
90
|
-
SqliteDsl.TableDefinition<
|
|
91
|
-
TName,
|
|
92
|
-
PrettifyFlat<
|
|
93
|
-
WithId<TColumns extends SqliteDsl.Columns ? TColumns : { value: TColumns }, WithDefaults<TOptionsInput>>
|
|
94
|
-
>
|
|
95
|
-
>,
|
|
96
|
-
TColumns extends SqliteDsl.ColumnDefinition<any, any> ? true : false,
|
|
97
|
-
WithDefaults<TOptionsInput>
|
|
98
|
-
> => {
|
|
99
|
-
const tablePath = name
|
|
100
|
-
|
|
101
|
-
const options_: TableOptions = {
|
|
102
|
-
isSingleton: options?.isSingleton ?? false,
|
|
103
|
-
dynamicRegistration: options?.dynamicRegistration ?? false,
|
|
104
|
-
disableAutomaticIdColumn: options?.disableAutomaticIdColumn ?? false,
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const columns = (
|
|
108
|
-
SqliteDsl.isColumnDefinition(columnOrColumns) ? { value: columnOrColumns } : columnOrColumns
|
|
109
|
-
) as SqliteDsl.Columns
|
|
110
|
-
|
|
111
|
-
if (options_.disableAutomaticIdColumn === true) {
|
|
112
|
-
if (columns.id === undefined && options_.isSingleton === true) {
|
|
113
|
-
shouldNeverHappen(
|
|
114
|
-
`Cannot create table ${name} with "isSingleton: true" because there is no column with name "id" and "disableAutomaticIdColumn: true" is set`,
|
|
115
|
-
)
|
|
116
|
-
}
|
|
117
|
-
} else if (columns.id === undefined && ReadonlyRecord.some(columns, (_) => _.primaryKey === true) === false) {
|
|
118
|
-
if (options_.isSingleton) {
|
|
119
|
-
columns.id = SqliteDsl.text({ schema: Schema.literal('singleton'), primaryKey: true, default: 'singleton' })
|
|
120
|
-
} else {
|
|
121
|
-
columns.id = SqliteDsl.text({ primaryKey: true })
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
const sqliteDef = SqliteDsl.table(tablePath, columns, options?.indexes ?? [])
|
|
126
|
-
|
|
127
|
-
if (options_.isSingleton) {
|
|
128
|
-
for (const column of sqliteDef.ast.columns) {
|
|
129
|
-
if (column.nullable === false && column.default._tag === 'None') {
|
|
130
|
-
shouldNeverHappen(
|
|
131
|
-
`When creating a singleton table, each column must be either nullable or have a default value. Column '${column.name}' is neither.`,
|
|
132
|
-
)
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
const isSingleColumn = SqliteDsl.isColumnDefinition(columnOrColumns) === true
|
|
138
|
-
|
|
139
|
-
const schema = SqliteDsl.structSchemaForTable(sqliteDef)
|
|
140
|
-
const tableDef = { sqliteDef, isSingleColumn, options: options_, schema } satisfies TableDef
|
|
141
|
-
|
|
142
|
-
if (dynamicallyRegisteredTables.has(tablePath)) {
|
|
143
|
-
if (SqliteAst.hash(dynamicallyRegisteredTables.get(tablePath)!.sqliteDef.ast) !== SqliteAst.hash(sqliteDef.ast)) {
|
|
144
|
-
console.error('previous tableDef', dynamicallyRegisteredTables.get(tablePath), 'new tableDef', sqliteDef.ast)
|
|
145
|
-
shouldNeverHappen(`Table with name "${name}" was already previously defined with a different definition`)
|
|
146
|
-
}
|
|
147
|
-
} else {
|
|
148
|
-
dynamicallyRegisteredTables.set(tablePath, tableDef)
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
return tableDef as any
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
export const tableIsSingleton = <TTableDef extends TableDef>(
|
|
155
|
-
tableDef: TTableDef,
|
|
156
|
-
): tableDef is TTableDef & { options: { isSingleton: true } } => tableDef.options.isSingleton === true
|
|
157
|
-
|
|
158
|
-
export const getDefaultValuesEncoded = <TTableDef extends TableDef>(
|
|
159
|
-
tableDef: TTableDef,
|
|
160
|
-
fallbackValues?: Record<string, any>,
|
|
161
|
-
) =>
|
|
162
|
-
pipe(
|
|
163
|
-
tableDef.sqliteDef.columns,
|
|
164
|
-
ReadonlyRecord.filter((col, key) => {
|
|
165
|
-
if (fallbackValues?.[key] !== undefined) return true
|
|
166
|
-
if (key === 'id') return false
|
|
167
|
-
return col!.default._tag === 'None' || SqliteDsl.isSqlDefaultValue(col!.default.value) === false
|
|
168
|
-
}),
|
|
169
|
-
ReadonlyRecord.map((column, columnName) =>
|
|
170
|
-
fallbackValues?.[columnName] === undefined
|
|
171
|
-
? column!.default._tag === 'None'
|
|
172
|
-
? column!.nullable === true
|
|
173
|
-
? null
|
|
174
|
-
: shouldNeverHappen(`Column ${columnName} has no default value and is not nullable`)
|
|
175
|
-
: Schema.encodeSync(column!.schema)(column!.default.value)
|
|
176
|
-
: fallbackValues[columnName],
|
|
177
|
-
),
|
|
178
|
-
)
|
|
179
|
-
|
|
180
|
-
export const getDefaultValuesDecoded = <TTableDef extends TableDef>(tableDef: TTableDef) =>
|
|
181
|
-
pipe(
|
|
182
|
-
tableDef.sqliteDef.columns,
|
|
183
|
-
ReadonlyRecord.filter((_, key) => key !== 'id'),
|
|
184
|
-
ReadonlyRecord.filter(
|
|
185
|
-
(col) => col!.default._tag === 'None' || SqliteDsl.isSqlDefaultValue(col!.default.value) === false,
|
|
186
|
-
),
|
|
187
|
-
ReadonlyRecord.map((column, columnName) =>
|
|
188
|
-
column!.default._tag === 'None'
|
|
189
|
-
? column!.nullable === true
|
|
190
|
-
? null
|
|
191
|
-
: shouldNeverHappen(`Column ${columnName} has no default value and is not nullable`)
|
|
192
|
-
: Schema.validateSync(column!.schema)(column!.default.value),
|
|
193
|
-
),
|
|
194
|
-
)
|
|
195
|
-
|
|
196
|
-
type WithId<TColumns extends SqliteDsl.Columns, TOptions extends TableOptions> = TColumns &
|
|
197
|
-
(TOptions['disableAutomaticIdColumn'] extends true
|
|
198
|
-
? {}
|
|
199
|
-
: TOptions['isSingleton'] extends true
|
|
200
|
-
? {
|
|
201
|
-
id: SqliteDsl.ColumnDefinition<'singleton', 'singleton'>
|
|
202
|
-
}
|
|
203
|
-
: {
|
|
204
|
-
id: SqliteDsl.ColumnDefinition<string, string>
|
|
205
|
-
})
|
|
206
|
-
|
|
207
|
-
type WithDefaults<TOptionsInput extends TableOptionsInput> = {
|
|
208
|
-
isSingleton: TOptionsInput['isSingleton'] extends true ? true : false
|
|
209
|
-
dynamicRegistration: TOptionsInput['dynamicRegistration'] extends true ? true : false
|
|
210
|
-
disableAutomaticIdColumn: TOptionsInput['disableAutomaticIdColumn'] extends true ? true : false
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
export namespace FromTable {
|
|
214
|
-
// TODO this sometimes doesn't preserve the order of columns
|
|
215
|
-
export type RowDecoded<TTableDef extends TableDef> = PrettifyFlat<
|
|
216
|
-
Nullable<Pick<RowDecodedAll<TTableDef>, NullableColumnNames<TTableDef>>> &
|
|
217
|
-
Omit<RowDecodedAll<TTableDef>, NullableColumnNames<TTableDef>>
|
|
218
|
-
>
|
|
219
|
-
|
|
220
|
-
export type NullableColumnNames<TTableDef extends TableDef> = FromColumns.NullableColumnNames<
|
|
221
|
-
TTableDef['sqliteDef']['columns']
|
|
222
|
-
>
|
|
223
|
-
|
|
224
|
-
export type Columns<TTableDef extends TableDef> = {
|
|
225
|
-
[K in keyof TTableDef['sqliteDef']['columns']]: TTableDef['sqliteDef']['columns'][K]['columnType']
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
export type RowEncodeNonNullable<TTableDef extends TableDef> = {
|
|
229
|
-
[K in keyof TTableDef['sqliteDef']['columns']]: Schema.Schema.From<TTableDef['sqliteDef']['columns'][K]['schema']>
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
export type RowEncoded<TTableDef extends TableDef> = PrettifyFlat<
|
|
233
|
-
Nullable<Pick<RowEncodeNonNullable<TTableDef>, NullableColumnNames<TTableDef>>> &
|
|
234
|
-
Omit<RowEncodeNonNullable<TTableDef>, NullableColumnNames<TTableDef>>
|
|
235
|
-
>
|
|
236
|
-
|
|
237
|
-
export type RowDecodedAll<TTableDef extends TableDef> = {
|
|
238
|
-
[K in keyof TTableDef['sqliteDef']['columns']]: Schema.Schema.To<TTableDef['sqliteDef']['columns'][K]['schema']>
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
export namespace FromColumns {
|
|
243
|
-
// TODO this sometimes doesn't preserve the order of columns
|
|
244
|
-
export type RowDecoded<TColumns extends SqliteDsl.Columns> = PrettifyFlat<
|
|
245
|
-
Nullable<Pick<RowDecodedAll<TColumns>, NullableColumnNames<TColumns>>> &
|
|
246
|
-
Omit<RowDecodedAll<TColumns>, NullableColumnNames<TColumns>>
|
|
247
|
-
>
|
|
248
|
-
|
|
249
|
-
export type RowDecodedAll<TColumns extends SqliteDsl.Columns> = {
|
|
250
|
-
[K in keyof TColumns]: Schema.Schema.To<TColumns[K]['schema']>
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
export type RowEncoded<TColumns extends SqliteDsl.Columns> = PrettifyFlat<
|
|
254
|
-
Nullable<Pick<RowEncodeNonNullable<TColumns>, NullableColumnNames<TColumns>>> &
|
|
255
|
-
Omit<RowEncodeNonNullable<TColumns>, NullableColumnNames<TColumns>>
|
|
256
|
-
>
|
|
257
|
-
|
|
258
|
-
export type RowEncodeNonNullable<TColumns extends SqliteDsl.Columns> = {
|
|
259
|
-
[K in keyof TColumns]: Schema.Schema.From<TColumns[K]['schema']>
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
export type NullableColumnNames<TColumns extends SqliteDsl.Columns> = keyof {
|
|
263
|
-
[K in keyof TColumns as TColumns[K]['default'] extends true ? K : never]: {}
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
export type RequiredInsertColumnNames<TColumns extends SqliteDsl.Columns> =
|
|
267
|
-
SqliteDsl.FromColumns.RequiredInsertColumnNames<TColumns>
|
|
268
|
-
|
|
269
|
-
export type InsertRowDecoded<TColumns extends SqliteDsl.Columns> = SqliteDsl.FromColumns.InsertRowDecoded<TColumns>
|
|
270
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import type * as otel from '@opentelemetry/api'
|
|
2
|
-
|
|
3
|
-
import type { MutationEvent } from '../../index.js'
|
|
4
|
-
import type { PreparedBindValues } from '../../utils/util.js'
|
|
5
|
-
import type { Storage, StorageOtelProps } from '../index.js'
|
|
6
|
-
|
|
7
|
-
export type StorageOptionsWebInMemory = {
|
|
8
|
-
type: 'web-in-memory'
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
/** NOTE: This storage is currently only used for testing */
|
|
12
|
-
export class InMemoryStorage implements Storage {
|
|
13
|
-
constructor(readonly otelTracer: otel.Tracer) {}
|
|
14
|
-
|
|
15
|
-
static load = async (_options?: StorageOptionsWebInMemory) => {
|
|
16
|
-
return ({ otelTracer }: StorageOtelProps) => new InMemoryStorage(otelTracer)
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
execute = (_query: string, _bindValues?: PreparedBindValues): void => {}
|
|
20
|
-
|
|
21
|
-
mutate = (_mutationEventEncoded: MutationEvent.Any, _parentSpan?: otel.Span | undefined) => {}
|
|
22
|
-
|
|
23
|
-
getPersistedData = async (): Promise<Uint8Array> => new Uint8Array()
|
|
24
|
-
|
|
25
|
-
getMutationLogData = async (): Promise<Uint8Array> => new Uint8Array()
|
|
26
|
-
|
|
27
|
-
dangerouslyReset = async () => {}
|
|
28
|
-
}
|
package/src/storage/index.ts
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
// A storage represents a raw SQLite database.
|
|
2
|
-
// Examples include:
|
|
3
|
-
// - A native SQLite process running in a Tauri Rust process
|
|
4
|
-
// - A SQL.js WASM version of SQLite running in a web worker
|
|
5
|
-
//
|
|
6
|
-
// We can send commands to execute various kinds of queries,
|
|
7
|
-
// and respond to various events from the database.
|
|
8
|
-
|
|
9
|
-
import type * as otel from '@opentelemetry/api'
|
|
10
|
-
|
|
11
|
-
import type { MutationEvent } from '../index.js'
|
|
12
|
-
import type { PreparedBindValues } from '../utils/util.js'
|
|
13
|
-
|
|
14
|
-
export type StorageInit = (otelProps: StorageOtelProps) => Promise<Storage> | Storage
|
|
15
|
-
|
|
16
|
-
export interface Storage {
|
|
17
|
-
// TODO consider transferables for `bindValues` (e.g. Uint8Array values)
|
|
18
|
-
execute(query: string, bindValues?: PreparedBindValues, parentSpan?: otel.Span): void
|
|
19
|
-
|
|
20
|
-
// TODO consider transferables for `bindValues` (e.g. Uint8Array values)
|
|
21
|
-
mutate(mutationEventEncoded: MutationEvent.Any, parentSpan?: otel.Span): void
|
|
22
|
-
|
|
23
|
-
/** Return a snapshot of persisted data from the storage */
|
|
24
|
-
getPersistedData(parentSpan?: otel.Span): Promise<Uint8Array>
|
|
25
|
-
|
|
26
|
-
getMutationLogData(parentSpan?: otel.Span): Promise<Uint8Array>
|
|
27
|
-
|
|
28
|
-
dangerouslyReset(): Promise<void>
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export type StorageType = 'tauri' | 'web' | 'web-in-memory'
|
|
32
|
-
|
|
33
|
-
export type StorageOtelProps = {
|
|
34
|
-
otelTracer: otel.Tracer
|
|
35
|
-
parentSpan: otel.Span
|
|
36
|
-
}
|