@livestore/common 0.1.0-dev.9 → 0.2.0-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.
Files changed (77) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/__tests__/fixture.d.ts +46 -34
  3. package/dist/__tests__/fixture.d.ts.map +1 -1
  4. package/dist/adapter-types.d.ts +1 -1
  5. package/dist/adapter-types.js +1 -1
  6. package/dist/derived-mutations.d.ts +4 -4
  7. package/dist/derived-mutations.d.ts.map +1 -1
  8. package/dist/derived-mutations.js.map +1 -1
  9. package/dist/devtools/devtools-messages.d.ts +53 -53
  10. package/dist/devtools/devtools-messages.js +1 -1
  11. package/dist/devtools/devtools-messages.js.map +1 -1
  12. package/dist/index.d.ts +1 -0
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +1 -0
  15. package/dist/index.js.map +1 -1
  16. package/dist/mutation.d.ts +1 -1
  17. package/dist/mutation.d.ts.map +1 -1
  18. package/dist/mutation.js +6 -1
  19. package/dist/mutation.js.map +1 -1
  20. package/dist/query-builder/api.d.ts +190 -0
  21. package/dist/query-builder/api.d.ts.map +1 -0
  22. package/dist/query-builder/api.js +8 -0
  23. package/dist/query-builder/api.js.map +1 -0
  24. package/dist/query-builder/impl.d.ts +12 -0
  25. package/dist/query-builder/impl.d.ts.map +1 -0
  26. package/dist/query-builder/impl.js +226 -0
  27. package/dist/query-builder/impl.js.map +1 -0
  28. package/dist/query-builder/impl.test.d.ts +2 -0
  29. package/dist/query-builder/impl.test.d.ts.map +1 -0
  30. package/dist/query-builder/impl.test.js +183 -0
  31. package/dist/query-builder/impl.test.js.map +1 -0
  32. package/dist/query-builder/mod.d.ts +10 -0
  33. package/dist/query-builder/mod.d.ts.map +1 -0
  34. package/dist/query-builder/mod.js +10 -0
  35. package/dist/query-builder/mod.js.map +1 -0
  36. package/dist/query-info.d.ts +29 -39
  37. package/dist/query-info.d.ts.map +1 -1
  38. package/dist/query-info.js +4 -35
  39. package/dist/query-info.js.map +1 -1
  40. package/dist/schema/index.d.ts +2 -2
  41. package/dist/schema/index.d.ts.map +1 -1
  42. package/dist/schema/index.js +1 -0
  43. package/dist/schema/index.js.map +1 -1
  44. package/dist/schema/mutations.d.ts +8 -8
  45. package/dist/schema/schema-helpers.d.ts +2 -2
  46. package/dist/schema/schema-helpers.d.ts.map +1 -1
  47. package/dist/schema/system-tables.d.ts +246 -204
  48. package/dist/schema/system-tables.d.ts.map +1 -1
  49. package/dist/schema/table-def.d.ts +45 -24
  50. package/dist/schema/table-def.d.ts.map +1 -1
  51. package/dist/schema/table-def.js +10 -1
  52. package/dist/schema/table-def.js.map +1 -1
  53. package/dist/sql-queries/sql-queries.d.ts +1 -0
  54. package/dist/sql-queries/sql-queries.d.ts.map +1 -1
  55. package/dist/sql-queries/sql-queries.js +8 -5
  56. package/dist/sql-queries/sql-queries.js.map +1 -1
  57. package/dist/sync/next/test/mutation-fixtures.d.ts +7 -7
  58. package/dist/version.d.ts +1 -1
  59. package/dist/version.d.ts.map +1 -1
  60. package/dist/version.js +1 -1
  61. package/dist/version.js.map +1 -1
  62. package/package.json +3 -3
  63. package/src/adapter-types.ts +1 -1
  64. package/src/derived-mutations.ts +4 -8
  65. package/src/devtools/devtools-messages.ts +1 -1
  66. package/src/index.ts +1 -0
  67. package/src/mutation.ts +9 -2
  68. package/src/query-builder/api.ts +288 -0
  69. package/src/query-builder/impl.test.ts +205 -0
  70. package/src/query-builder/impl.ts +268 -0
  71. package/src/query-builder/mod.ts +10 -0
  72. package/src/query-info.ts +66 -93
  73. package/src/schema/index.ts +4 -2
  74. package/src/schema/schema-helpers.ts +2 -2
  75. package/src/schema/table-def.ts +99 -68
  76. package/src/sql-queries/sql-queries.ts +9 -6
  77. package/src/version.ts +1 -1
@@ -5,76 +5,83 @@ import { ReadonlyRecord, Schema } from '@livestore/utils/effect'
5
5
 
6
6
  import type { DerivedMutationHelperFns } from '../derived-mutations.js'
7
7
  import { makeDerivedMutationDefsForTable } from '../derived-mutations.js'
8
+ import type { QueryBuilder } from '../query-builder/mod.js'
9
+ import { makeQueryBuilder } from '../query-builder/mod.js'
8
10
 
9
11
  export const { blob, boolean, column, datetime, integer, isColumnDefinition, json, real, text } = SqliteDsl
10
12
 
11
- export { type SqliteDsl } from '@livestore/db-schema'
13
+ export { SqliteDsl } from '@livestore/db-schema'
12
14
 
13
15
  export type StateType = 'singleton' | 'dynamic'
14
16
 
15
17
  export type DefaultSqliteTableDef = SqliteDsl.TableDefinition<string, SqliteDsl.Columns>
16
18
  export type DefaultSqliteTableDefConstrained = SqliteDsl.TableDefinition<string, SqliteDsl.ConstraintColumns>
17
19
 
18
- // export type TableDefConstraint<
19
- // TSqliteDef extends DefaultSqliteTableDef = DefaultSqliteTableDef,
20
- // TIsSingleColumn extends boolean = boolean,
21
- // TOptions extends TableOptions = TableOptions,
22
- // > = TableDefBase<TSqliteDef, TIsSingleColumn, TOptions> & { schema: Schema.Schema<any> }
23
-
24
- // /**
25
- // * NOTE in the past we used to have a single `TableDef` but there are some TS issues when indroducing
26
- // * `schema: SqliteDsl.StructSchemaForColumns<TSqliteDef>` so we split it into two types
27
- // * and only use `TableDefConstraint` in some places
28
- // */
29
- // export type TableDefBase<
30
- // TSqliteDef extends DefaultSqliteTableDef = DefaultSqliteTableDef,
31
- // TIsSingleColumn extends boolean = boolean,
32
- // TOptions extends TableOptions = TableOptions,
33
- // > = {
34
- // sqliteDef: TSqliteDef
35
- // // schema: SqliteDsl.StructSchemaForColumns<TSqliteDef>
36
- // // schema: any;
37
- // isSingleColumn: TIsSingleColumn
38
- // options: TOptions
39
- // }
20
+ export type TableDefBase<
21
+ TSqliteDef extends DefaultSqliteTableDef = DefaultSqliteTableDefConstrained,
22
+ TOptions extends TableOptions = TableOptions,
23
+ TSchema = SqliteDsl.StructSchemaForColumns<TSqliteDef['columns']>,
24
+ > = {
25
+ sqliteDef: TSqliteDef
26
+ options: TOptions
27
+ // Derived from `sqliteDef`, so only exposed for convenience
28
+ schema: TSchema
29
+ }
40
30
 
41
31
  export type TableDef<
42
32
  TSqliteDef extends DefaultSqliteTableDef = DefaultSqliteTableDefConstrained,
43
- TIsSingleColumn extends boolean = boolean,
44
33
  TOptions extends TableOptions = TableOptions,
45
34
  // NOTE we're not using `SqliteDsl.StructSchemaForColumns<TSqliteDef['columns']>`
46
- // as we don't want the alias type for users to show up
35
+ // as we don't want the alias type for users to show up, so we're redefining it here
47
36
  TSchema = Schema.Schema<
48
37
  SqliteDsl.AnyIfConstained<
49
38
  TSqliteDef['columns'],
50
- { readonly [K in keyof TSqliteDef['columns']]: Schema.Schema.Type<TSqliteDef['columns'][K]['schema']> }
39
+ { readonly [K in keyof TSqliteDef['columns']]: TSqliteDef['columns'][K]['schema']['Type'] }
51
40
  >,
52
41
  SqliteDsl.AnyIfConstained<
53
42
  TSqliteDef['columns'],
54
- { readonly [K in keyof TSqliteDef['columns']]: Schema.Schema.Encoded<TSqliteDef['columns'][K]['schema']> }
43
+ { readonly [K in keyof TSqliteDef['columns']]: TSqliteDef['columns'][K]['schema']['Encoded'] }
55
44
  >
56
45
  >,
57
46
  > = {
58
47
  sqliteDef: TSqliteDef
59
- // TODO move this into options (for now it's duplicated)
60
- isSingleColumn: TIsSingleColumn
61
48
  options: TOptions
49
+ // Derived from `sqliteDef`, so only exposed for convenience
62
50
  schema: TSchema
51
+ query: QueryBuilder<ReadonlyArray<Schema.Schema.Type<TSchema>>, TableDef<TSqliteDef & {}, TOptions>>
63
52
  } & (TOptions['deriveMutations']['enabled'] extends true
64
53
  ? DerivedMutationHelperFns<TSqliteDef['columns'], TOptions>
65
54
  : {})
66
55
 
67
- export type TableOptionsInput = Partial<
68
- Omit<TableOptions, 'isSingleColumn' | 'deriveMutations'> & {
69
- indexes: SqliteDsl.Index[]
70
- deriveMutations:
71
- | boolean
72
- | {
73
- enabled: true
74
- localOnly?: boolean
75
- }
76
- }
77
- >
56
+ export type TableOptionsInput = Partial<{
57
+ indexes: SqliteDsl.Index[]
58
+ disableAutomaticIdColumn: boolean
59
+ isSingleton: boolean
60
+ deriveMutations:
61
+ | boolean
62
+ | {
63
+ enabled: true
64
+ localOnly?: boolean
65
+ }
66
+ }>
67
+
68
+ type ToColumns<TColumns extends SqliteDsl.Columns | SqliteDsl.ColumnDefinition<any, any>> =
69
+ TColumns extends SqliteDsl.Columns
70
+ ? TColumns
71
+ : TColumns extends SqliteDsl.ColumnDefinition<any, any>
72
+ ? { value: TColumns }
73
+ : never
74
+
75
+ type ValidateTableOptionsInput<
76
+ TColumns extends SqliteDsl.Columns | SqliteDsl.ColumnDefinition<any, any>,
77
+ TOptionsInput extends TableOptionsInput,
78
+ TPassthroughIfValid,
79
+ > =
80
+ SqliteDsl.FromColumns.RequiresInsertValues<ToColumns<TColumns>> extends true
81
+ ? TOptionsInput['isSingleton'] extends true
82
+ ? 'Error: To use `isSingleton: true` with this table, each column must have a default value or be nullable'
83
+ : TPassthroughIfValid
84
+ : TPassthroughIfValid
78
85
 
79
86
  export type TableOptions = {
80
87
  /**
@@ -85,8 +92,10 @@ export type TableOptions = {
85
92
  *
86
93
  * @default false
87
94
  */
88
- isSingleton: boolean
89
- disableAutomaticIdColumn: boolean
95
+ readonly isSingleton: boolean
96
+
97
+ readonly disableAutomaticIdColumn: boolean
98
+
90
99
  /**
91
100
  * Setting this to true will automatically derive insert, update and delete mutations for this table. Example:
92
101
  *
@@ -99,7 +108,7 @@ export type TableOptions = {
99
108
  *
100
109
  * Important: When using this option, make sure you're following the "Rules of mutations" for the table schema.
101
110
  */
102
- deriveMutations:
111
+ readonly deriveMutations:
103
112
  | { enabled: false }
104
113
  | {
105
114
  enabled: true
@@ -108,30 +117,34 @@ export type TableOptions = {
108
117
  */
109
118
  localOnly: boolean
110
119
  }
120
+
111
121
  /** Derived based on whether the table definition has one or more columns (besides the `id` column) */
112
- isSingleColumn: boolean
122
+ readonly isSingleColumn: boolean
123
+
124
+ /**
125
+ * Derived based on whether the table definition has one or more columns (besides the `id` column) that require
126
+ * insert values (i.e. are not nullable and don't have a default value)
127
+ *
128
+ * `isSingleton` tables always imply `requiresInsertValues: false`
129
+ */
130
+ readonly requiredInsertColumnNames: string
113
131
  }
114
132
 
115
133
  export const table = <
116
134
  TName extends string,
117
135
  TColumns extends SqliteDsl.Columns | SqliteDsl.ColumnDefinition<any, any>,
118
- const TOptionsInput extends TableOptionsInput = TableOptionsInput,
136
+ TOptionsInput extends TableOptionsInput = TableOptionsInput,
119
137
  >(
120
138
  name: TName,
121
139
  columnOrColumns: TColumns,
122
140
  options?: TOptionsInput,
123
- ): TableDef<
124
- SqliteDsl.TableDefinition<
125
- TName,
126
- PrettifyFlat<
127
- WithId<
128
- TColumns extends SqliteDsl.Columns ? TColumns : { value: TColumns },
129
- WithDefaults<TOptionsInput, SqliteDsl.IsSingleColumn<TColumns>>
130
- >
131
- >
132
- >,
133
- SqliteDsl.IsSingleColumn<TColumns>,
134
- WithDefaults<TOptionsInput, SqliteDsl.IsSingleColumn<TColumns>>
141
+ ): ValidateTableOptionsInput<
142
+ TColumns,
143
+ TOptionsInput,
144
+ TableDef<
145
+ SqliteTableDefForInput<TName, TColumns, WithDefaults<TOptionsInput, TColumns>>,
146
+ WithDefaults<TOptionsInput, TColumns>
147
+ >
135
148
  > => {
136
149
  const tablePath = name
137
150
 
@@ -147,6 +160,7 @@ export const table = <
147
160
  ? { enabled: false as const }
148
161
  : { enabled: true as const, localOnly: options.deriveMutations.localOnly ?? false },
149
162
  isSingleColumn: SqliteDsl.isColumnDefinition(columnOrColumns) === true,
163
+ requiredInsertColumnNames: 'type-level-only',
150
164
  }
151
165
 
152
166
  const columns = (
@@ -183,7 +197,14 @@ export const table = <
183
197
  const isSingleColumn = SqliteDsl.isColumnDefinition(columnOrColumns) === true
184
198
 
185
199
  const schema = SqliteDsl.structSchemaForTable(sqliteDef)
186
- const tableDef = { sqliteDef, isSingleColumn, options: options_, schema } satisfies TableDef
200
+ const tableDef = { sqliteDef, options: options_, schema } satisfies TableDefBase
201
+ const query = makeQueryBuilder(tableDef)
202
+ // const tableDef = { ...tableDefBase, query } satisfies TableDef
203
+
204
+ // NOTE we're currently patching the existing tableDef object
205
+ // as it's being used as part of the query builder API
206
+ // @ts-expect-error TODO properly implement this
207
+ tableDef.query = query
187
208
 
188
209
  if (tableHasDerivedMutations(tableDef)) {
189
210
  const derivedMutationDefs = makeDerivedMutationDefsForTable(tableDef)
@@ -210,17 +231,23 @@ export const table = <
210
231
  return tableDef as any
211
232
  }
212
233
 
213
- export const tableHasDerivedMutations = <TTableDef extends TableDef>(
234
+ export const tableHasDerivedMutations = <TTableDef extends TableDefBase>(
214
235
  tableDef: TTableDef,
215
236
  ): tableDef is TTableDef & {
216
237
  options: { deriveMutations: { enabled: true; localOnly: boolean } }
217
238
  } & DerivedMutationHelperFns<TTableDef['sqliteDef']['columns'], TTableDef['options']> =>
218
239
  tableDef.options.deriveMutations.enabled === true
219
240
 
220
- export const tableIsSingleton = <TTableDef extends TableDef>(
241
+ export const tableIsSingleton = <TTableDef extends TableDefBase>(
221
242
  tableDef: TTableDef,
222
243
  ): tableDef is TTableDef & { options: { isSingleton: true } } => tableDef.options.isSingleton === true
223
244
 
245
+ type SqliteTableDefForInput<
246
+ TName extends string,
247
+ TColumns extends SqliteDsl.Columns | SqliteDsl.ColumnDefinition<any, any>,
248
+ TOptions extends TableOptions,
249
+ > = SqliteDsl.TableDefinition<TName, PrettifyFlat<WithId<ToColumns<TColumns>, TOptions>>>
250
+
224
251
  type WithId<TColumns extends SqliteDsl.Columns, TOptions extends TableOptions> = TColumns &
225
252
  ('id' extends keyof TColumns
226
253
  ? {}
@@ -234,7 +261,10 @@ type WithId<TColumns extends SqliteDsl.Columns, TOptions extends TableOptions> =
234
261
  id: SqliteDsl.ColumnDefinition<string, string>
235
262
  })
236
263
 
237
- type WithDefaults<TOptionsInput extends TableOptionsInput, TIsSingleColumn extends boolean> = {
264
+ type WithDefaults<
265
+ TOptionsInput extends TableOptionsInput,
266
+ TColumns extends SqliteDsl.Columns | SqliteDsl.ColumnDefinition<any, any>,
267
+ > = {
238
268
  isSingleton: TOptionsInput['isSingleton'] extends true ? true : false
239
269
  disableAutomaticIdColumn: TOptionsInput['disableAutomaticIdColumn'] extends true ? true : false
240
270
  deriveMutations: TOptionsInput['deriveMutations'] extends true
@@ -247,36 +277,37 @@ type WithDefaults<TOptionsInput extends TableOptionsInput, TIsSingleColumn exten
247
277
  localOnly: TOptionsInput['deriveMutations']['localOnly'] extends true ? true : false
248
278
  }
249
279
  : never
250
- isSingleColumn: TIsSingleColumn
280
+ isSingleColumn: SqliteDsl.IsSingleColumn<TColumns>
281
+ requiredInsertColumnNames: SqliteDsl.FromColumns.RequiredInsertColumnNames<ToColumns<TColumns>>
251
282
  }
252
283
 
253
284
  export namespace FromTable {
254
285
  // TODO this sometimes doesn't preserve the order of columns
255
- export type RowDecoded<TTableDef extends TableDef> = PrettifyFlat<
286
+ export type RowDecoded<TTableDef extends TableDefBase> = PrettifyFlat<
256
287
  Nullable<Pick<RowDecodedAll<TTableDef>, NullableColumnNames<TTableDef>>> &
257
288
  Omit<RowDecodedAll<TTableDef>, NullableColumnNames<TTableDef>>
258
289
  >
259
290
 
260
- export type NullableColumnNames<TTableDef extends TableDef> = FromColumns.NullableColumnNames<
291
+ export type NullableColumnNames<TTableDef extends TableDefBase> = FromColumns.NullableColumnNames<
261
292
  TTableDef['sqliteDef']['columns']
262
293
  >
263
294
 
264
- export type Columns<TTableDef extends TableDef> = {
295
+ export type Columns<TTableDef extends TableDefBase> = {
265
296
  [K in keyof TTableDef['sqliteDef']['columns']]: TTableDef['sqliteDef']['columns'][K]['columnType']
266
297
  }
267
298
 
268
- export type RowEncodeNonNullable<TTableDef extends TableDef> = {
299
+ export type RowEncodeNonNullable<TTableDef extends TableDefBase> = {
269
300
  [K in keyof TTableDef['sqliteDef']['columns']]: Schema.Schema.Encoded<
270
301
  TTableDef['sqliteDef']['columns'][K]['schema']
271
302
  >
272
303
  }
273
304
 
274
- export type RowEncoded<TTableDef extends TableDef> = PrettifyFlat<
305
+ export type RowEncoded<TTableDef extends TableDefBase> = PrettifyFlat<
275
306
  Nullable<Pick<RowEncodeNonNullable<TTableDef>, NullableColumnNames<TTableDef>>> &
276
307
  Omit<RowEncodeNonNullable<TTableDef>, NullableColumnNames<TTableDef>>
277
308
  >
278
309
 
279
- export type RowDecodedAll<TTableDef extends TableDef> = {
310
+ export type RowDecodedAll<TTableDef extends TableDefBase> = {
280
311
  [K in keyof TTableDef['sqliteDef']['columns']]: Schema.Schema.Type<TTableDef['sqliteDef']['columns'][K]['schema']>
281
312
  }
282
313
  }
@@ -58,7 +58,11 @@ export const insertRow = <TColumns extends SqliteDsl.Columns>({
58
58
  values: ClientTypes.DecodedValuesForColumns<TColumns>
59
59
  options?: { orReplace: boolean }
60
60
  }): [string, BindValues] => {
61
- const stmt = insertRowPrepared({ tableName, columns, options })
61
+ const stmt = insertRowPrepared({
62
+ tableName,
63
+ columns,
64
+ options: { orReplace: options?.orReplace, keys: Object.keys(values) },
65
+ })
62
66
 
63
67
  return [stmt, makeBindValues({ columns, values })]
64
68
  }
@@ -70,12 +74,11 @@ export const insertRowPrepared = <TColumns extends SqliteDsl.Columns>({
70
74
  }: {
71
75
  tableName: string
72
76
  columns: TColumns
73
- options?: { orReplace: boolean }
77
+ options?: { orReplace: boolean; keys?: string[] }
74
78
  }): string => {
75
- const keysStr = Object.keys(columns).join(', ')
76
- const valuesStr = Object.keys(columns)
77
- .map((key) => `$${key}`)
78
- .join(', ')
79
+ const keys = options?.keys ?? Object.keys(columns)
80
+ const keysStr = keys.join(', ')
81
+ const valuesStr = keys.map((key) => `$${key}`).join(', ')
79
82
 
80
83
  return sql`INSERT ${options.orReplace ? 'OR REPLACE ' : ''}INTO ${tableName} (${keysStr}) VALUES (${valuesStr})`
81
84
  }
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.1.0-dev.8' as const
5
+ export const liveStoreVersion = '0.1.0' as const
6
6
 
7
7
  /**
8
8
  * This version number is incremented whenever the internal storage format changes in a breaking way.