@livestore/common 0.0.0-snapshot-29dc6acb4ddfcb70ac29c4ae18419710d194e555 → 0.0.0-snapshot-669b49b56c8abe87f4e11263af7cbf506deab38e

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 (70) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/__tests__/fixture.d.ts +34 -46
  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 +45 -45
  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 +0 -1
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +0 -1
  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 +1 -6
  19. package/dist/mutation.js.map +1 -1
  20. package/dist/query-info.d.ts +39 -29
  21. package/dist/query-info.d.ts.map +1 -1
  22. package/dist/query-info.js +35 -4
  23. package/dist/query-info.js.map +1 -1
  24. package/dist/schema/index.d.ts +2 -2
  25. package/dist/schema/index.d.ts.map +1 -1
  26. package/dist/schema/index.js +0 -1
  27. package/dist/schema/index.js.map +1 -1
  28. package/dist/schema/schema-helpers.d.ts +2 -2
  29. package/dist/schema/schema-helpers.d.ts.map +1 -1
  30. package/dist/schema/system-tables.d.ts +204 -246
  31. package/dist/schema/system-tables.d.ts.map +1 -1
  32. package/dist/schema/table-def.d.ts +24 -45
  33. package/dist/schema/table-def.d.ts.map +1 -1
  34. package/dist/schema/table-def.js +1 -10
  35. package/dist/schema/table-def.js.map +1 -1
  36. package/dist/version.d.ts +1 -1
  37. package/dist/version.d.ts.map +1 -1
  38. package/dist/version.js +1 -1
  39. package/dist/version.js.map +1 -1
  40. package/package.json +3 -3
  41. package/src/adapter-types.ts +1 -1
  42. package/src/derived-mutations.ts +8 -4
  43. package/src/devtools/devtools-messages.ts +1 -1
  44. package/src/index.ts +0 -1
  45. package/src/mutation.ts +2 -9
  46. package/src/query-info.ts +93 -66
  47. package/src/schema/index.ts +2 -4
  48. package/src/schema/schema-helpers.ts +2 -2
  49. package/src/schema/table-def.ts +68 -99
  50. package/src/version.ts +1 -1
  51. package/dist/query-builder/api.d.ts +0 -190
  52. package/dist/query-builder/api.d.ts.map +0 -1
  53. package/dist/query-builder/api.js +0 -8
  54. package/dist/query-builder/api.js.map +0 -1
  55. package/dist/query-builder/impl.d.ts +0 -12
  56. package/dist/query-builder/impl.d.ts.map +0 -1
  57. package/dist/query-builder/impl.js +0 -227
  58. package/dist/query-builder/impl.js.map +0 -1
  59. package/dist/query-builder/impl.test.d.ts +0 -2
  60. package/dist/query-builder/impl.test.d.ts.map +0 -1
  61. package/dist/query-builder/impl.test.js +0 -189
  62. package/dist/query-builder/impl.test.js.map +0 -1
  63. package/dist/query-builder/mod.d.ts +0 -10
  64. package/dist/query-builder/mod.d.ts.map +0 -1
  65. package/dist/query-builder/mod.js +0 -10
  66. package/dist/query-builder/mod.js.map +0 -1
  67. package/src/query-builder/api.ts +0 -288
  68. package/src/query-builder/impl.test.ts +0 -212
  69. package/src/query-builder/impl.ts +0 -269
  70. package/src/query-builder/mod.ts +0 -10
@@ -1,269 +0,0 @@
1
- import { Option, Predicate, Schema } from '@livestore/utils/effect'
2
-
3
- import type { QueryInfo } from '../query-info.js'
4
- import type { DbSchema } from '../schema/index.js'
5
- import type { QueryBuilder, QueryBuilderAst } from './api.js'
6
- import { QueryBuilderAstSymbol, TypeId } from './api.js'
7
-
8
- export const makeQueryBuilder = <TResult, TTableDef extends DbSchema.TableDefBase>(
9
- tableDef: TTableDef,
10
- ast: QueryBuilderAst = emptyAst(tableDef),
11
- ): QueryBuilder<TResult, TTableDef, never, QueryInfo.None> => {
12
- const api = {
13
- // eslint-disable-next-line prefer-arrow/prefer-arrow-functions
14
- select() {
15
- assertQueryBuilderAst(ast)
16
-
17
- // eslint-disable-next-line prefer-rest-params
18
- const params = [...arguments]
19
-
20
- if (params.length === 2 && typeof params[0] === 'string' && typeof params[1] === 'object') {
21
- const [col, options] = params as any as [string, { pluck: boolean }]
22
- return makeQueryBuilder(tableDef, {
23
- ...ast,
24
- resultSchemaSingle: options.pluck ? ast.resultSchemaSingle.pipe(Schema.pluck(col)) : ast.resultSchemaSingle,
25
- select: { columns: [col] },
26
- })
27
- }
28
-
29
- const columns = params as unknown as ReadonlyArray<string>
30
-
31
- return makeQueryBuilder(tableDef, {
32
- ...ast,
33
- resultSchemaSingle:
34
- columns.length === 0 ? ast.resultSchemaSingle : ast.resultSchemaSingle.pipe(Schema.pick(...columns)),
35
- select: { columns },
36
- }) as any
37
- },
38
- // eslint-disable-next-line prefer-arrow/prefer-arrow-functions
39
- where() {
40
- if (isRowQuery(ast)) return invalidQueryBuilder()
41
-
42
- if (arguments.length === 1) {
43
- // eslint-disable-next-line prefer-rest-params
44
- const params = arguments[0]
45
- const newOps = Object.entries(params)
46
- .filter(([, value]) => value !== undefined)
47
- .map<QueryBuilderAst.Where>(([col, value]) =>
48
- Predicate.hasProperty(value, 'op') && Predicate.hasProperty(value, 'value')
49
- ? ({ col, op: value.op, value: value.value } as any)
50
- : { col, op: '=', value },
51
- )
52
-
53
- return makeQueryBuilder(tableDef, {
54
- ...ast,
55
- where: [...ast.where, ...newOps],
56
- }) as any
57
- }
58
-
59
- // eslint-disable-next-line prefer-rest-params
60
- const [col, opOrValue, valueOrUndefined] = arguments
61
- const op = valueOrUndefined === undefined ? '=' : opOrValue
62
- const value = valueOrUndefined === undefined ? opOrValue : valueOrUndefined
63
- return makeQueryBuilder(tableDef, {
64
- ...ast,
65
- where: [...ast.where, { col, op, value }],
66
- })
67
- },
68
- // eslint-disable-next-line prefer-arrow/prefer-arrow-functions
69
- orderBy() {
70
- assertQueryBuilderAst(ast)
71
-
72
- if (arguments.length === 0 || arguments.length > 2) return invalidQueryBuilder()
73
-
74
- if (arguments.length === 1) {
75
- // eslint-disable-next-line prefer-rest-params
76
- const params = arguments[0] as QueryBuilder.OrderByParams<TTableDef>
77
- return makeQueryBuilder(tableDef, {
78
- ...ast,
79
- orderBy: [...ast.orderBy, ...params],
80
- })
81
- }
82
-
83
- // eslint-disable-next-line prefer-rest-params
84
- const [col, direction] = arguments as any as [keyof TTableDef['sqliteDef']['columns'] & string, 'asc' | 'desc']
85
-
86
- return makeQueryBuilder(tableDef, {
87
- ...ast,
88
- orderBy: [...ast.orderBy, { col, direction }],
89
- }) as any
90
- },
91
- limit: (limit) => {
92
- assertQueryBuilderAst(ast)
93
-
94
- return makeQueryBuilder(tableDef, { ...ast, limit: Option.some(limit) })
95
- },
96
- offset: (offset) => {
97
- assertQueryBuilderAst(ast)
98
-
99
- return makeQueryBuilder(tableDef, { ...ast, offset: Option.some(offset) })
100
- },
101
- count: () => {
102
- if (isRowQuery(ast)) return invalidQueryBuilder()
103
-
104
- return makeQueryBuilder(tableDef, {
105
- ...ast,
106
- resultSchema: Schema.Struct({ count: Schema.Number }).pipe(
107
- Schema.pluck('count'),
108
- Schema.Array,
109
- Schema.headOrElse(),
110
- ),
111
- _tag: 'CountQuery',
112
- })
113
- },
114
- first: (options) => {
115
- assertQueryBuilderAst(ast)
116
-
117
- if (ast.limit._tag === 'Some') return invalidQueryBuilder(`.first() can't be called after .limit()`)
118
-
119
- return makeQueryBuilder(tableDef, {
120
- ...ast,
121
- limit: Option.some(1),
122
- // TODO improve
123
- pickFirst: options?.fallback ? { fallback: options.fallback } : { fallback: () => undefined },
124
- })
125
- },
126
- // eslint-disable-next-line prefer-arrow/prefer-arrow-functions
127
- row() {
128
- // eslint-disable-next-line prefer-rest-params
129
- const params = [...arguments]
130
-
131
- let id: string
132
-
133
- if (tableDef.options.isSingleton) {
134
- id = tableDef.sqliteDef.columns.id!.default.pipe(Option.getOrThrow)
135
- } else {
136
- id = params[0] as string
137
- if (id === undefined) {
138
- invalidQueryBuilder(`Id missing for row query on non-singleton table ${tableDef.sqliteDef.name}`)
139
- }
140
- }
141
-
142
- // TODO validate all required columns are present and values are matching the schema
143
- const insertValues: Record<string, unknown> = params[1]?.insertValues ?? {}
144
-
145
- return makeQueryBuilder(tableDef, {
146
- _tag: 'RowQuery',
147
- id,
148
- tableDef,
149
- insertValues,
150
- }) as any
151
- },
152
- } satisfies QueryBuilder.ApiFull<TResult, TTableDef, never, QueryInfo.None>
153
-
154
- return {
155
- [TypeId]: TypeId,
156
- [QueryBuilderAstSymbol]: ast,
157
- asSql: () => astToSql(ast),
158
- toString: () => astToSql(ast).query,
159
- ...api,
160
- } satisfies QueryBuilder<TResult, TTableDef>
161
- }
162
-
163
- const emptyAst = (tableDef: DbSchema.TableDefBase) =>
164
- ({
165
- _tag: 'SelectQuery',
166
- columns: [],
167
- pickFirst: false,
168
- select: { columns: [] },
169
- orderBy: [],
170
- offset: Option.none(),
171
- limit: Option.none(),
172
- tableDef,
173
- where: [],
174
- resultSchemaSingle: tableDef.schema,
175
- }) satisfies QueryBuilderAst
176
-
177
- const astToSql = (ast: QueryBuilderAst) => {
178
- if (isRowQuery(ast)) {
179
- // TODO
180
- return { query: `SELECT * FROM '${ast.tableDef.sqliteDef.name}' WHERE id = ?`, bindValues: [ast.id as TODO] }
181
- }
182
-
183
- const bindValues: unknown[] = []
184
-
185
- // TODO bind values
186
- const whereStmt =
187
- ast.where.length > 0
188
- ? `WHERE ${ast.where
189
- .map(({ col, op, value }) => {
190
- if (value === null) {
191
- if (op !== '=' && op !== '!=') {
192
- throw new Error(`Unsupported operator for NULL value: ${op}`)
193
- }
194
- const opStmt = op === '=' ? 'IS' : 'IS NOT'
195
- return `${col} ${opStmt} NULL`
196
- } else {
197
- const colDef = ast.tableDef.sqliteDef.columns[col]
198
- if (colDef === undefined) {
199
- throw new Error(`Column ${col} not found`)
200
- }
201
- const encodedValue = Schema.encodeSync(colDef.schema)(value)
202
- bindValues.push(encodedValue)
203
- return `${col} ${op} ?`
204
- }
205
- })
206
- .join(' AND ')}`
207
- : ''
208
-
209
- if (ast._tag === 'CountQuery') {
210
- const selectFromStmt = `SELECT COUNT(*) as count FROM '${ast.tableDef.sqliteDef.name}'`
211
- const query = [selectFromStmt, whereStmt].filter((_) => _.length > 0).join(' ')
212
- return { query, bindValues }
213
- }
214
- const columnsStmt = ast.select.columns.length === 0 ? '*' : ast.select.columns.join(', ')
215
- const selectStmt = `SELECT ${columnsStmt}`
216
- const fromStmt = `FROM '${ast.tableDef.sqliteDef.name}'`
217
-
218
- const orderByStmt =
219
- ast.orderBy.length > 0
220
- ? `ORDER BY ${ast.orderBy.map(({ col, direction }) => `${col} ${direction}`).join(', ')}`
221
- : ''
222
-
223
- const limitStmt = ast.limit._tag === 'Some' ? `LIMIT ?` : ''
224
- if (ast.limit._tag === 'Some') bindValues.push(ast.limit.value)
225
-
226
- const offsetStmt = ast.offset._tag === 'Some' ? `OFFSET ?` : ''
227
- if (ast.offset._tag === 'Some') bindValues.push(ast.offset.value)
228
-
229
- const query = [selectStmt, fromStmt, whereStmt, orderByStmt, offsetStmt, limitStmt]
230
- .map((_) => _.trim())
231
- .filter((_) => _.length > 0)
232
- .join(' ')
233
-
234
- // TODO bind values
235
- return { query, bindValues }
236
- }
237
-
238
- // eslint-disable-next-line prefer-arrow/prefer-arrow-functions
239
- function assertQueryBuilderAst(ast: QueryBuilderAst): asserts ast is QueryBuilderAst.SelectQuery {
240
- if (ast._tag !== 'SelectQuery') {
241
- throw new Error('Expected SelectQuery but got ' + ast._tag)
242
- }
243
- }
244
-
245
- const isRowQuery = (ast: QueryBuilderAst): ast is QueryBuilderAst.RowQuery => ast._tag === 'RowQuery'
246
-
247
- export const invalidQueryBuilder = (msg?: string) => {
248
- throw new Error('Invalid query builder' + (msg ? `: ${msg}` : ''))
249
- }
250
-
251
- export const getResultSchema = (qb: QueryBuilder<any, any, any>) => {
252
- const queryAst = qb[QueryBuilderAstSymbol]
253
- if (queryAst._tag === 'SelectQuery') {
254
- const arraySchema = Schema.Array(queryAst.resultSchemaSingle)
255
- if (queryAst.pickFirst !== false) {
256
- return arraySchema.pipe(Schema.headOrElse(queryAst.pickFirst.fallback))
257
- }
258
-
259
- return arraySchema
260
- } else if (queryAst._tag === 'CountQuery') {
261
- return Schema.Struct({ count: Schema.Number }).pipe(Schema.pluck('count'), Schema.Array, Schema.headOrElse())
262
- } else {
263
- if (queryAst.tableDef.options.isSingleColumn) {
264
- return queryAst.tableDef.schema.pipe(Schema.pluck('value'), Schema.Array, Schema.headOrElse())
265
- } else {
266
- return queryAst.tableDef.schema.pipe(Schema.Array, Schema.headOrElse())
267
- }
268
- }
269
- }
@@ -1,10 +0,0 @@
1
- export * from './api.js'
2
- export * from './impl.js'
3
-
4
- /**
5
- * Design decisions:
6
- *
7
- * - Close abstraction to SQLite to provide a simple & convenient API with predictable behaviour
8
- * - Use table schema definitions to parse, map & validate query results
9
- * - Implementation detail: Separate type-level & AST-based runtime implementation
10
- */