@livestore/common 0.3.0-dev.34 → 0.3.0-dev.37
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/devtools/devtools-messages-client-session.d.ts +21 -21
- package/dist/devtools/devtools-messages-common.d.ts +6 -6
- package/dist/devtools/devtools-messages-leader.d.ts +24 -24
- package/dist/devtools/devtools-sessioninfo.d.ts +2 -0
- package/dist/devtools/devtools-sessioninfo.d.ts.map +1 -1
- package/dist/devtools/devtools-sessioninfo.js +1 -0
- package/dist/devtools/devtools-sessioninfo.js.map +1 -1
- package/dist/leader-thread/LeaderSyncProcessor.js +10 -10
- package/dist/leader-thread/LeaderSyncProcessor.js.map +1 -1
- package/dist/leader-thread/materialize-event.d.ts.map +1 -1
- package/dist/leader-thread/materialize-event.js +3 -1
- package/dist/leader-thread/materialize-event.js.map +1 -1
- package/dist/materializer-helper.d.ts +5 -5
- package/dist/materializer-helper.d.ts.map +1 -1
- package/dist/materializer-helper.js +16 -2
- package/dist/materializer-helper.js.map +1 -1
- package/dist/schema/EventDef.d.ts +11 -1
- package/dist/schema/EventDef.d.ts.map +1 -1
- package/dist/schema/EventDef.js.map +1 -1
- package/dist/schema/schema.d.ts +13 -0
- package/dist/schema/schema.d.ts.map +1 -1
- package/dist/schema/schema.js +3 -0
- package/dist/schema/schema.js.map +1 -1
- package/dist/schema/state/sqlite/client-document-def.test.js +5 -5
- package/dist/schema/state/sqlite/client-document-def.test.js.map +1 -1
- package/dist/schema/state/sqlite/query-builder/api.d.ts +4 -3
- package/dist/schema/state/sqlite/query-builder/api.d.ts.map +1 -1
- package/dist/schema/state/sqlite/query-builder/impl.d.ts.map +1 -1
- package/dist/schema/state/sqlite/query-builder/impl.js +2 -2
- package/dist/schema/state/sqlite/query-builder/impl.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +3 -3
- package/src/devtools/devtools-sessioninfo.ts +1 -0
- package/src/leader-thread/LeaderSyncProcessor.ts +10 -10
- package/src/leader-thread/materialize-event.ts +3 -1
- package/src/materializer-helper.ts +33 -9
- package/src/schema/EventDef.ts +12 -1
- package/src/schema/schema.ts +16 -0
- package/src/schema/state/sqlite/client-document-def.test.ts +5 -6
- package/src/schema/state/sqlite/query-builder/api.ts +4 -3
- package/src/schema/state/sqlite/query-builder/impl.ts +3 -2
- package/src/version.ts +1 -1
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@livestore/common",
|
3
|
-
"version": "0.3.0-dev.
|
3
|
+
"version": "0.3.0-dev.37",
|
4
4
|
"type": "module",
|
5
5
|
"sideEffects": false,
|
6
6
|
"exports": {
|
@@ -54,11 +54,11 @@
|
|
54
54
|
"graphology": "0.26.0-alpha1",
|
55
55
|
"graphology-dag": "0.4.1",
|
56
56
|
"graphology-types": "0.24.8",
|
57
|
-
"@livestore/utils": "0.3.0-dev.
|
57
|
+
"@livestore/utils": "0.3.0-dev.37"
|
58
58
|
},
|
59
59
|
"devDependencies": {
|
60
60
|
"vitest": "^3.1.1",
|
61
|
-
"@livestore/utils-dev": "0.3.0-dev.
|
61
|
+
"@livestore/utils-dev": "0.3.0-dev.37"
|
62
62
|
},
|
63
63
|
"files": [
|
64
64
|
"package.json",
|
@@ -107,8 +107,8 @@ export const makeLeaderSyncProcessor = ({
|
|
107
107
|
const syncStateSref = yield* SubscriptionRef.make<SyncState.SyncState | undefined>(undefined)
|
108
108
|
|
109
109
|
const isClientEvent = (eventEncoded: LiveStoreEvent.EncodedWithMeta) => {
|
110
|
-
const eventDef = getEventDef(schema, eventEncoded.name)
|
111
|
-
return eventDef.
|
110
|
+
const { eventDef } = getEventDef(schema, eventEncoded.name)
|
111
|
+
return eventDef.options.clientOnly
|
112
112
|
}
|
113
113
|
|
114
114
|
const connectedClientSessionPullQueues = yield* makePullQueueSet
|
@@ -196,14 +196,14 @@ export const makeLeaderSyncProcessor = ({
|
|
196
196
|
const syncState = yield* syncStateSref
|
197
197
|
if (syncState === undefined) return shouldNeverHappen('Not initialized')
|
198
198
|
|
199
|
-
const eventDef = getEventDef(schema, name)
|
199
|
+
const { eventDef } = getEventDef(schema, name)
|
200
200
|
|
201
201
|
const eventEncoded = new LiveStoreEvent.EncodedWithMeta({
|
202
202
|
name,
|
203
203
|
args,
|
204
204
|
clientId,
|
205
205
|
sessionId,
|
206
|
-
...EventId.nextPair(syncState.localHead, eventDef.
|
206
|
+
...EventId.nextPair(syncState.localHead, eventDef.options.clientOnly),
|
207
207
|
})
|
208
208
|
|
209
209
|
yield* push([eventEncoded])
|
@@ -251,8 +251,8 @@ export const makeLeaderSyncProcessor = ({
|
|
251
251
|
const globalPendingEvents = pendingEvents
|
252
252
|
// Don't sync clientOnly events
|
253
253
|
.filter((eventEncoded) => {
|
254
|
-
const eventDef = getEventDef(schema, eventEncoded.name)
|
255
|
-
return eventDef.
|
254
|
+
const { eventDef } = getEventDef(schema, eventEncoded.name)
|
255
|
+
return eventDef.options.clientOnly === false
|
256
256
|
})
|
257
257
|
|
258
258
|
if (globalPendingEvents.length > 0) {
|
@@ -540,8 +540,8 @@ const backgroundApplyLocalPushes = ({
|
|
540
540
|
|
541
541
|
// Don't sync clientOnly events
|
542
542
|
const filteredBatch = mergeResult.newEvents.filter((eventEncoded) => {
|
543
|
-
const eventDef = getEventDef(schema, eventEncoded.name)
|
544
|
-
return eventDef.
|
543
|
+
const { eventDef } = getEventDef(schema, eventEncoded.name)
|
544
|
+
return eventDef.options.clientOnly === false
|
545
545
|
})
|
546
546
|
|
547
547
|
yield* BucketQueue.offerAll(syncBackendPushQueue, filteredBatch)
|
@@ -688,8 +688,8 @@ const backgroundBackendPulling = ({
|
|
688
688
|
})
|
689
689
|
|
690
690
|
const globalRebasedPendingEvents = mergeResult.newSyncState.pending.filter((event) => {
|
691
|
-
const eventDef = getEventDef(schema, event.name)
|
692
|
-
return eventDef.
|
691
|
+
const { eventDef } = getEventDef(schema, event.name)
|
692
|
+
return eventDef.options.clientOnly === false
|
693
693
|
})
|
694
694
|
yield* restartBackendPushing(globalRebasedPendingEvents)
|
695
695
|
|
@@ -33,10 +33,12 @@ export const makeMaterializeEvent = ({
|
|
33
33
|
const skipEventlog = options?.skipEventlog ?? false
|
34
34
|
|
35
35
|
const eventName = eventEncoded.name
|
36
|
-
const eventDef = getEventDef(schema, eventName)
|
36
|
+
const { eventDef, materializer } = getEventDef(schema, eventName)
|
37
37
|
|
38
38
|
const execArgsArr = getExecArgsFromEvent({
|
39
39
|
eventDef,
|
40
|
+
materializer,
|
41
|
+
db,
|
40
42
|
event: { decoded: undefined, encoded: eventEncoded },
|
41
43
|
})
|
42
44
|
|
@@ -1,22 +1,26 @@
|
|
1
1
|
import { isReadonlyArray } from '@livestore/utils'
|
2
2
|
import { Schema } from '@livestore/utils/effect'
|
3
3
|
|
4
|
+
import type { SqliteDb } from './adapter-types.js'
|
4
5
|
import { SessionIdSymbol } from './adapter-types.js'
|
5
|
-
import type { EventDef, Materializer, MaterializerResult } from './schema/EventDef.js'
|
6
|
+
import type { EventDef, Materializer, MaterializerContextQuery, MaterializerResult } from './schema/EventDef.js'
|
6
7
|
import type * as LiveStoreEvent from './schema/LiveStoreEvent.js'
|
8
|
+
import type { QueryBuilder } from './schema/state/sqlite/query-builder/api.js'
|
7
9
|
import { isQueryBuilder } from './schema/state/sqlite/query-builder/api.js'
|
8
|
-
import
|
9
|
-
import type
|
10
|
+
import { getResultSchema } from './schema/state/sqlite/query-builder/impl.js'
|
11
|
+
import { type BindValues } from './sql-queries/sql-queries.js'
|
12
|
+
import type { ParamsObject, PreparedBindValues } from './util.js'
|
10
13
|
import { prepareBindValues } from './util.js'
|
11
14
|
|
12
15
|
export const getExecArgsFromEvent = ({
|
13
|
-
eventDef
|
16
|
+
eventDef,
|
17
|
+
materializer,
|
18
|
+
db,
|
14
19
|
event,
|
15
20
|
}: {
|
16
|
-
eventDef:
|
17
|
-
|
18
|
-
|
19
|
-
}
|
21
|
+
eventDef: EventDef.AnyWithoutFn
|
22
|
+
materializer: Materializer
|
23
|
+
db: SqliteDb
|
20
24
|
/** Both encoded and decoded events are supported to reduce the number of times we need to decode/encode */
|
21
25
|
event:
|
22
26
|
| {
|
@@ -34,8 +38,28 @@ export const getExecArgsFromEvent = ({
|
|
34
38
|
}> => {
|
35
39
|
const eventArgsDecoded = event.decoded?.args ?? Schema.decodeUnknownSync(eventDef.schema)(event.encoded!.args)
|
36
40
|
|
41
|
+
const query: MaterializerContextQuery = (
|
42
|
+
rawQueryOrQueryBuilder:
|
43
|
+
| {
|
44
|
+
query: string
|
45
|
+
bindValues: ParamsObject
|
46
|
+
}
|
47
|
+
| QueryBuilder.Any,
|
48
|
+
) => {
|
49
|
+
if (isQueryBuilder(rawQueryOrQueryBuilder)) {
|
50
|
+
const { query, bindValues } = rawQueryOrQueryBuilder.asSql()
|
51
|
+
const rawResults = db.select(query, prepareBindValues(bindValues, query))
|
52
|
+
const resultSchema = getResultSchema(rawQueryOrQueryBuilder)
|
53
|
+
return Schema.decodeSync(resultSchema)(rawResults)
|
54
|
+
} else {
|
55
|
+
const { query, bindValues } = rawQueryOrQueryBuilder
|
56
|
+
return db.select(query, prepareBindValues(bindValues, query))
|
57
|
+
}
|
58
|
+
}
|
59
|
+
|
37
60
|
const res = materializer(eventArgsDecoded, {
|
38
|
-
|
61
|
+
eventDef,
|
62
|
+
query,
|
39
63
|
// TODO properly implement this
|
40
64
|
currentFacts: new Map(),
|
41
65
|
})
|
package/src/schema/EventDef.ts
CHANGED
@@ -3,6 +3,7 @@ import { shouldNeverHappen } from '@livestore/utils'
|
|
3
3
|
import { Schema } from '@livestore/utils/effect'
|
4
4
|
|
5
5
|
import type { BindValues } from '../sql-queries/sql-queries.js'
|
6
|
+
import type { ParamsObject } from '../util.js'
|
6
7
|
import type { QueryBuilder } from './state/sqlite/query-builder/mod.js'
|
7
8
|
|
8
9
|
export type EventDefMap = {
|
@@ -167,9 +168,19 @@ export type MaterializerResult =
|
|
167
168
|
| QueryBuilder.Any
|
168
169
|
| string
|
169
170
|
|
171
|
+
export type MaterializerContextQuery = {
|
172
|
+
(args: { query: string; bindValues: ParamsObject }): ReadonlyArray<unknown>
|
173
|
+
<TResult>(qb: QueryBuilder<TResult, any, any>): TResult
|
174
|
+
}
|
175
|
+
|
170
176
|
export type Materializer<TEventDef extends EventDef.AnyWithoutFn = EventDef.AnyWithoutFn> = (
|
171
177
|
event: TEventDef['schema']['Type'],
|
172
|
-
context: {
|
178
|
+
context: {
|
179
|
+
currentFacts: EventDefFacts
|
180
|
+
eventDef: TEventDef
|
181
|
+
/** Can be used to query the current state */
|
182
|
+
query: MaterializerContextQuery
|
183
|
+
},
|
173
184
|
) => SingleOrReadonlyArray<MaterializerResult>
|
174
185
|
|
175
186
|
export const defineMaterializer = <TEventDef extends EventDef.AnyWithoutFn>(
|
package/src/schema/schema.ts
CHANGED
@@ -23,6 +23,10 @@ export type LiveStoreSchema<
|
|
23
23
|
|
24
24
|
readonly state: State
|
25
25
|
readonly eventsDefsMap: Map<string, EventDef.AnyWithoutFn>
|
26
|
+
readonly devtools: {
|
27
|
+
/** @default 'default' */
|
28
|
+
readonly alias: string
|
29
|
+
}
|
26
30
|
}
|
27
31
|
|
28
32
|
// TODO abstract this further away from sqlite/tables
|
@@ -39,6 +43,15 @@ export type State = {
|
|
39
43
|
export type InputSchema = {
|
40
44
|
readonly events: ReadonlyArray<EventDef.AnyWithoutFn> | Record<string, EventDef.AnyWithoutFn>
|
41
45
|
readonly state: State
|
46
|
+
readonly devtools?: {
|
47
|
+
/**
|
48
|
+
* This alias value is used to disambiguate between multiple schemas in the devtools.
|
49
|
+
* Only needed when an app uses multiple schemas.
|
50
|
+
*
|
51
|
+
* @default 'default'
|
52
|
+
*/
|
53
|
+
readonly alias?: string
|
54
|
+
}
|
42
55
|
}
|
43
56
|
|
44
57
|
export const makeSchema = <TInputSchema extends InputSchema>(
|
@@ -81,6 +94,9 @@ export const makeSchema = <TInputSchema extends InputSchema>(
|
|
81
94
|
_EventDefMapType: Symbol.for('livestore.EventDefMapType') as any,
|
82
95
|
state,
|
83
96
|
eventsDefsMap,
|
97
|
+
devtools: {
|
98
|
+
alias: inputSchema.devtools?.alias ?? 'default',
|
99
|
+
},
|
84
100
|
} satisfies LiveStoreSchema
|
85
101
|
}
|
86
102
|
|
@@ -5,11 +5,6 @@ import { tables } from '../../../__tests__/fixture.js'
|
|
5
5
|
import type * as LiveStoreEvent from '../../LiveStoreEvent.js'
|
6
6
|
import { clientDocument, ClientDocumentTableDefSymbol } from './client-document-def.js'
|
7
7
|
|
8
|
-
const materializerContext = {
|
9
|
-
currentFacts: new Map(),
|
10
|
-
clientOnly: false,
|
11
|
-
}
|
12
|
-
|
13
8
|
describe('client document table', () => {
|
14
9
|
test('set event', () => {
|
15
10
|
expect(patchId(tables.UiState.set({ showSidebar: false }, 'session-1'))).toMatchInlineSnapshot(`
|
@@ -51,7 +46,11 @@ describe('client document table', () => {
|
|
51
46
|
|
52
47
|
const materializer = Doc[ClientDocumentTableDefSymbol].derived.setMaterializer
|
53
48
|
|
54
|
-
return materializer(Doc.set(value, id as any).args,
|
49
|
+
return materializer(Doc.set(value, id as any).args, {
|
50
|
+
currentFacts: new Map(),
|
51
|
+
query: {} as any, // unused
|
52
|
+
eventDef: Doc[ClientDocumentTableDefSymbol].derived.setEventDef,
|
53
|
+
})
|
55
54
|
}
|
56
55
|
|
57
56
|
test('string value', () => {
|
@@ -19,7 +19,7 @@ export namespace QueryBuilderAst {
|
|
19
19
|
export interface SelectQuery {
|
20
20
|
readonly _tag: 'SelectQuery'
|
21
21
|
readonly columns: string[]
|
22
|
-
readonly pickFirst: false | { fallback: () => any } | '
|
22
|
+
readonly pickFirst: false | { fallback: () => any } | 'throws'
|
23
23
|
readonly select: {
|
24
24
|
columns: ReadonlyArray<string>
|
25
25
|
}
|
@@ -288,10 +288,11 @@ export namespace QueryBuilder {
|
|
288
288
|
* db.todos.where('id', '123').first()
|
289
289
|
* ```
|
290
290
|
*
|
291
|
-
* Query will
|
291
|
+
* Query will throw if no rows are returned and no fallback is provided.
|
292
292
|
*/
|
293
293
|
readonly first: <TFallback = never>(options?: {
|
294
|
-
|
294
|
+
/** @default 'throws' */
|
295
|
+
fallback?: (() => TFallback | GetSingle<TResult>) | 'throws'
|
295
296
|
}) => QueryBuilder<
|
296
297
|
TFallback | GetSingle<TResult>,
|
297
298
|
TTableDef,
|
@@ -143,7 +143,8 @@ export const makeQueryBuilder = <TResult, TTableDef extends TableDefBase>(
|
|
143
143
|
return makeQueryBuilder(tableDef, {
|
144
144
|
...ast,
|
145
145
|
limit: Option.some(1),
|
146
|
-
pickFirst:
|
146
|
+
pickFirst:
|
147
|
+
options?.fallback !== undefined && options.fallback !== 'throws' ? { fallback: options.fallback } : 'throws',
|
147
148
|
})
|
148
149
|
},
|
149
150
|
// // eslint-disable-next-line prefer-arrow/prefer-arrow-functions
|
@@ -309,7 +310,7 @@ export const getResultSchema = (qb: QueryBuilder<any, any, any>): Schema.Schema<
|
|
309
310
|
const arraySchema = Schema.Array(queryAst.resultSchemaSingle)
|
310
311
|
if (queryAst.pickFirst === false) {
|
311
312
|
return arraySchema
|
312
|
-
} else if (queryAst.pickFirst === '
|
313
|
+
} else if (queryAst.pickFirst === 'throws') {
|
313
314
|
// Will throw if the array is empty
|
314
315
|
return arraySchema.pipe(Schema.headOrElse())
|
315
316
|
} else {
|
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.3.0-dev.
|
5
|
+
export const liveStoreVersion = '0.3.0-dev.37' as const
|
6
6
|
|
7
7
|
/**
|
8
8
|
* This version number is incremented whenever the internal storage format changes in a breaking way.
|