@livestore/common 0.0.58-dev.0 → 0.0.58-dev.10

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 (142) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/adapter-types.d.ts +48 -6
  3. package/dist/adapter-types.d.ts.map +1 -1
  4. package/dist/adapter-types.js +16 -1
  5. package/dist/adapter-types.js.map +1 -1
  6. package/dist/bounded-collections.js.map +1 -1
  7. package/dist/derived-mutations.d.ts +5 -5
  8. package/dist/derived-mutations.d.ts.map +1 -1
  9. package/dist/derived-mutations.js +4 -2
  10. package/dist/derived-mutations.js.map +1 -1
  11. package/dist/derived-mutations.test.js +1 -0
  12. package/dist/derived-mutations.test.js.map +1 -1
  13. package/dist/devtools/devtools-bridge.d.ts +1 -1
  14. package/dist/devtools/devtools-bridge.d.ts.map +1 -1
  15. package/dist/devtools/devtools-messages.d.ts +91 -13
  16. package/dist/devtools/devtools-messages.d.ts.map +1 -1
  17. package/dist/devtools/devtools-messages.js +13 -4
  18. package/dist/devtools/devtools-messages.js.map +1 -1
  19. package/dist/devtools/index.d.ts +1 -0
  20. package/dist/devtools/index.d.ts.map +1 -1
  21. package/dist/devtools/index.js +2 -0
  22. package/dist/devtools/index.js.map +1 -1
  23. package/dist/index.d.ts +4 -0
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/rehydrate-from-mutationlog.d.ts.map +1 -1
  26. package/dist/rehydrate-from-mutationlog.js +11 -5
  27. package/dist/rehydrate-from-mutationlog.js.map +1 -1
  28. package/dist/schema/index.d.ts +2 -2
  29. package/dist/schema/index.d.ts.map +1 -1
  30. package/dist/schema/index.js +1 -1
  31. package/dist/schema/index.js.map +1 -1
  32. package/dist/schema/mutations.d.ts +137 -17
  33. package/dist/schema/mutations.d.ts.map +1 -1
  34. package/dist/schema/mutations.js +42 -16
  35. package/dist/schema/mutations.js.map +1 -1
  36. package/dist/schema/schema-helpers.js +1 -1
  37. package/dist/schema/schema-helpers.js.map +1 -1
  38. package/dist/schema/system-tables.d.ts +119 -6
  39. package/dist/schema/system-tables.d.ts.map +1 -1
  40. package/dist/schema/system-tables.js +22 -9
  41. package/dist/schema/system-tables.js.map +1 -1
  42. package/dist/schema/table-def.d.ts +3 -3
  43. package/dist/schema/table-def.d.ts.map +1 -1
  44. package/dist/schema/table-def.js +1 -1
  45. package/dist/schema/table-def.js.map +1 -1
  46. package/dist/schema-management/migrations.d.ts +1 -1
  47. package/dist/schema-management/migrations.d.ts.map +1 -1
  48. package/dist/schema-management/migrations.js +2 -2
  49. package/dist/schema-management/migrations.js.map +1 -1
  50. package/dist/sql-queries/sql-queries.d.ts +10 -3
  51. package/dist/sql-queries/sql-queries.d.ts.map +1 -1
  52. package/dist/sql-queries/sql-queries.js +8 -7
  53. package/dist/sql-queries/sql-queries.js.map +1 -1
  54. package/dist/sql-queries/sql-query-builder.d.ts +1 -1
  55. package/dist/sql-queries/sql-query-builder.d.ts.map +1 -1
  56. package/dist/sql-queries/types.d.ts +2 -2
  57. package/dist/sql-queries/types.d.ts.map +1 -1
  58. package/dist/sync/next/compact-events.d.ts +15 -0
  59. package/dist/sync/next/compact-events.d.ts.map +1 -0
  60. package/dist/sync/next/compact-events.js +176 -0
  61. package/dist/sync/next/compact-events.js.map +1 -0
  62. package/dist/sync/next/facts.d.ts +37 -0
  63. package/dist/sync/next/facts.d.ts.map +1 -0
  64. package/dist/sync/next/facts.js +156 -0
  65. package/dist/sync/next/facts.js.map +1 -0
  66. package/dist/sync/next/graphology.d.ts +8 -0
  67. package/dist/sync/next/graphology.d.ts.map +1 -0
  68. package/dist/sync/next/graphology.js +36 -0
  69. package/dist/sync/next/graphology.js.map +1 -0
  70. package/dist/sync/next/graphology_.d.ts +3 -0
  71. package/dist/sync/next/graphology_.d.ts.map +1 -0
  72. package/dist/sync/next/graphology_.js +3 -0
  73. package/dist/sync/next/graphology_.js.map +1 -0
  74. package/dist/sync/next/history-dag.d.ts +30 -0
  75. package/dist/sync/next/history-dag.d.ts.map +1 -0
  76. package/dist/sync/next/history-dag.js +69 -0
  77. package/dist/sync/next/history-dag.js.map +1 -0
  78. package/dist/sync/next/mod.d.ts +5 -0
  79. package/dist/sync/next/mod.d.ts.map +1 -0
  80. package/dist/sync/next/mod.js +5 -0
  81. package/dist/sync/next/mod.js.map +1 -0
  82. package/dist/sync/next/rebase-events.d.ts +27 -0
  83. package/dist/sync/next/rebase-events.d.ts.map +1 -0
  84. package/dist/sync/next/rebase-events.js +41 -0
  85. package/dist/sync/next/rebase-events.js.map +1 -0
  86. package/dist/sync/next/test/compact-events.calculator.test.d.ts +2 -0
  87. package/dist/sync/next/test/compact-events.calculator.test.d.ts.map +1 -0
  88. package/dist/sync/next/test/compact-events.calculator.test.js +101 -0
  89. package/dist/sync/next/test/compact-events.calculator.test.js.map +1 -0
  90. package/dist/sync/next/test/compact-events.test.d.ts +2 -0
  91. package/dist/sync/next/test/compact-events.test.d.ts.map +1 -0
  92. package/dist/sync/next/test/compact-events.test.js +201 -0
  93. package/dist/sync/next/test/compact-events.test.js.map +1 -0
  94. package/dist/sync/next/test/mod.d.ts +2 -0
  95. package/dist/sync/next/test/mod.d.ts.map +1 -0
  96. package/dist/sync/next/test/mod.js +2 -0
  97. package/dist/sync/next/test/mod.js.map +1 -0
  98. package/dist/sync/next/test/mutation-fixtures.d.ts +73 -0
  99. package/dist/sync/next/test/mutation-fixtures.d.ts.map +1 -0
  100. package/dist/sync/next/test/mutation-fixtures.js +161 -0
  101. package/dist/sync/next/test/mutation-fixtures.js.map +1 -0
  102. package/dist/sync/sync.d.ts +19 -6
  103. package/dist/sync/sync.d.ts.map +1 -1
  104. package/dist/sync/sync.js.map +1 -1
  105. package/dist/version.d.ts +8 -0
  106. package/dist/version.d.ts.map +1 -1
  107. package/dist/version.js +9 -0
  108. package/dist/version.js.map +1 -1
  109. package/package.json +21 -4
  110. package/src/adapter-types.ts +46 -7
  111. package/src/bounded-collections.ts +1 -1
  112. package/src/derived-mutations.test.ts +2 -1
  113. package/src/derived-mutations.ts +10 -10
  114. package/src/devtools/devtools-bridge.ts +1 -1
  115. package/src/devtools/devtools-messages.ts +12 -2
  116. package/src/devtools/index.ts +2 -0
  117. package/src/index.ts +6 -0
  118. package/src/rehydrate-from-mutationlog.ts +16 -7
  119. package/src/schema/index.ts +4 -3
  120. package/src/schema/mutations.ts +175 -30
  121. package/src/schema/schema-helpers.ts +1 -1
  122. package/src/schema/system-tables.ts +30 -9
  123. package/src/schema/table-def.ts +3 -3
  124. package/src/schema-management/migrations.ts +2 -2
  125. package/src/sql-queries/sql-queries.ts +21 -10
  126. package/src/sql-queries/sql-query-builder.ts +1 -1
  127. package/src/sql-queries/types.ts +2 -2
  128. package/src/sync/next/ambient.d.ts +3 -0
  129. package/src/sync/next/compact-events.ts +218 -0
  130. package/src/sync/next/facts.ts +229 -0
  131. package/src/sync/next/graphology.ts +49 -0
  132. package/src/sync/next/graphology_.ts +2 -0
  133. package/src/sync/next/history-dag.ts +109 -0
  134. package/src/sync/next/mod.ts +4 -0
  135. package/src/sync/next/rebase-events.ts +97 -0
  136. package/src/sync/next/test/compact-events.calculator.test.ts +121 -0
  137. package/src/sync/next/test/compact-events.test.ts +232 -0
  138. package/src/sync/next/test/mod.ts +1 -0
  139. package/src/sync/next/test/mutation-fixtures.ts +230 -0
  140. package/src/sync/sync.ts +30 -6
  141. package/src/version.ts +10 -0
  142. package/tsconfig.json +1 -1
@@ -0,0 +1,161 @@
1
+ import { Schema } from '@livestore/utils/effect';
2
+ import { ROOT_ID } from '../../../adapter-types.js';
3
+ import { defineFacts, defineMutation } from '../../../schema/mutations.js';
4
+ import { factsSnapshotForDag, getFactsGroupForMutationArgs } from '../facts.js';
5
+ import { historyDagFromNodes, rootEventNode } from '../history-dag.js';
6
+ /** Used for conflict detection and event history compaction */
7
+ export const facts = defineFacts({
8
+ todoExists: (id) => `todo-exists-${id}`,
9
+ todoIsWriteable: (id, writeable) => [`todo-is-writeable-${id}`, writeable],
10
+ todoCompleted: (id, completed) => [`todo-completed-${id}`, completed],
11
+ todoTextUpdated: (id) => `todo-text-updated-${id}`,
12
+ inputValue: (id) => `input-value-${id}`,
13
+ });
14
+ export const mutations = {
15
+ createTodo: defineMutation('createTodo', Schema.Struct({ id: Schema.String, text: Schema.String }), 'INSERT INTO todos (id, text) VALUES ($id, $text)', {
16
+ facts: ({ id }) => ({
17
+ modify: {
18
+ set: [facts.todoExists(id), facts.todoIsWriteable(id, true), facts.todoCompleted(id, false)],
19
+ },
20
+ }),
21
+ }),
22
+ upsertTodo: defineMutation('upsertTodo', Schema.Struct({ id: Schema.String, text: Schema.optional(Schema.String) }), 'INSERT INTO todos (id, text) VALUES ($id, $text) ON CONFLICT (id) DO UPDATE SET text = $text', {
23
+ facts: ({ id }, currentFacts) =>
24
+ // TODO enable an API along the lines of `map.has(key, value)`
25
+ currentFacts.has(facts.todoExists(id)) && currentFacts.get(facts.todoIsWriteable(id, true)[0]) === false
26
+ ? { require: [facts.todoExists(id), facts.todoIsWriteable(id, true)] }
27
+ : { modify: { set: [facts.todoExists(id), facts.todoIsWriteable(id, true), facts.todoTextUpdated(id)] } },
28
+ }),
29
+ completeTodo: defineMutation('completeTodo', Schema.Struct({ id: Schema.String }),
30
+ // consider `RETURNING` to validate before applying facts
31
+ 'UPDATE todos SET completed = true WHERE id = $id', {
32
+ // prewrite assertions from DB
33
+ // enables more concurrency
34
+ // turning database inside out
35
+ // similar to upsert semantics
36
+ facts: ({ id }) => ({
37
+ require: [facts.todoExists(id), facts.todoIsWriteable(id, true)],
38
+ modify: { set: [facts.todoCompleted(id, true)] },
39
+ }),
40
+ }),
41
+ uncompleteTodo: defineMutation('uncompleteTodo', Schema.Struct({ id: Schema.String }), 'UPDATE todos SET completed = false WHERE id = $id', {
42
+ facts: ({ id }) => ({
43
+ require: [facts.todoExists(id), facts.todoIsWriteable(id, true)],
44
+ modify: { set: [facts.todoCompleted(id, false)] },
45
+ }),
46
+ }),
47
+ completeTodos: defineMutation('completeTodos', Schema.Struct({ ids: Schema.Array(Schema.String) }), 'UPDATE todos SET completed = true WHERE id IN ($ids:csv)', {
48
+ facts: ({ ids }) => ({
49
+ require: ids.flatMap((id) => [facts.todoExists(id), facts.todoIsWriteable(id, true)]),
50
+ modify: { set: ids.map((id) => facts.todoCompleted(id, true)) },
51
+ }),
52
+ }),
53
+ toggleTodo: defineMutation('toggleTodo', Schema.Struct({ id: Schema.String }), 'UPDATE todos SET completed = NOT completed WHERE id = $id', {
54
+ facts: ({ id }, currentFacts) => {
55
+ const currentIsCompleted = currentFacts.get(facts.todoCompleted(id, true)[0]) === true;
56
+ return {
57
+ require: [facts.todoExists(id), facts.todoIsWriteable(id, true)],
58
+ modify: {
59
+ // remove: [facts.todoCompleted(id, currentIsCompleted)],
60
+ set: [facts.todoCompleted(id, !currentIsCompleted)],
61
+ },
62
+ };
63
+ },
64
+ }),
65
+ setReadonlyTodo: defineMutation('setReadonlyTodo', Schema.Struct({ id: Schema.String, readonly: Schema.Boolean }), 'UPDATE todos SET readonly = $readonly WHERE id = $id', {
66
+ facts: ({ id, readonly }) => ({
67
+ require: [facts.todoExists(id)],
68
+ modify: { set: [facts.todoIsWriteable(id, !readonly)] },
69
+ }),
70
+ }),
71
+ setTextTodo: defineMutation('setTextTodo', Schema.Struct({ id: Schema.String, text: Schema.String }), 'UPDATE todos SET text = $text WHERE id = $id', {
72
+ facts: ({ id }) => ({
73
+ require: [facts.todoExists(id), facts.todoIsWriteable(id, true)],
74
+ modify: { set: [facts.todoTextUpdated(id)] },
75
+ }),
76
+ }),
77
+ setInputValue: defineMutation('setInputValue', Schema.Struct({ id: Schema.String, text: Schema.String }), 'UPDATE todos SET text = $text WHERE id = $id', {
78
+ localOnly: true,
79
+ facts: ({ id }) => ({ modify: { set: [facts.inputValue(id)] } }),
80
+ }),
81
+ };
82
+ export const toEventNodes = (partialEvents, mutationDefs) => {
83
+ const nodesAcc = [rootEventNode];
84
+ let currentEventId = ROOT_ID;
85
+ const getNextEventId = (mutationDef) => {
86
+ if (mutationDef.options.localOnly) {
87
+ return { global: currentEventId.global, local: currentEventId.local + 1 };
88
+ }
89
+ return { global: currentEventId.global + 1, local: 0 };
90
+ };
91
+ const eventNodes = partialEvents.map((partialEvent) => {
92
+ const mutationDef = mutationDefs[partialEvent.mutation];
93
+ const eventId = getNextEventId(mutationDef);
94
+ currentEventId = eventId;
95
+ const factsSnapshot = factsSnapshotForDag(historyDagFromNodes(nodesAcc, { skipFactsCheck: true }), undefined);
96
+ // console.log('factsSnapshot', eventId, factsSnapshot)
97
+ // const depRead: MutationEventFactsSnapshot = new Map<string, any>()
98
+ // const factsSnapshotProxy = new Proxy(factsSnapshot, {
99
+ // get: (target, prop) => {
100
+ // if (prop === 'has') {
101
+ // return (key: string) => {
102
+ // depRead.set(key, EMPTY_FACT_VALUE)
103
+ // return target.has(key)
104
+ // }
105
+ // } else if (prop === 'get') {
106
+ // return (key: string) => {
107
+ // depRead.set(key, EMPTY_FACT_VALUE)
108
+ // return target.get(key)
109
+ // }
110
+ // }
111
+ // notYetImplemented(`toEventNodes: ${prop.toString()} is not yet implemented`)
112
+ // },
113
+ // })
114
+ // const factsRes = mutationDef.options.facts?.(partialEvent.args, factsSnapshotProxy)
115
+ // console.log('factsRes', factsRes?.modify, factsRes?.require)
116
+ // const iterableToMap = (iterable: Iterable<MutationEventFactInput>) => {
117
+ // const map = new Map()
118
+ // for (const item of iterable) {
119
+ // if (typeof item === 'string') {
120
+ // map.set(item, EMPTY_FACT_VALUE)
121
+ // } else {
122
+ // map.set(item[0], item[1])
123
+ // }
124
+ // }
125
+ // return map
126
+ // }
127
+ // const facts = {
128
+ // modifyAdd: factsRes?.modify.add ? iterableToMap(factsRes.modify.add) : new Map(),
129
+ // modifyRemove: factsRes?.modify.remove ? iterableToMap(factsRes.modify.remove) : new Map(),
130
+ // depRequire: factsRes?.require ? iterableToMap(factsRes.require) : new Map(),
131
+ // depRead,
132
+ // } satisfies MutationEventFactsGroup
133
+ // applyFactGroup(facts, factsSnapshot)
134
+ const facts = getFactsGroupForMutationArgs({
135
+ factsCallback: mutationDef.options.facts,
136
+ args: partialEvent.args,
137
+ currentFacts: factsSnapshot,
138
+ });
139
+ const node = {
140
+ id: eventId,
141
+ parentId: getParentId(eventId),
142
+ mutation: partialEvent.mutation,
143
+ args: partialEvent.args,
144
+ factsGroup: facts,
145
+ };
146
+ nodesAcc.push(node);
147
+ return node;
148
+ });
149
+ eventNodes.unshift(rootEventNode);
150
+ // console.log('eventNodes', eventNodes)
151
+ return eventNodes;
152
+ };
153
+ const getParentId = (eventId) => {
154
+ const globalParentId = eventId.global;
155
+ const localParentId = eventId.local - 1;
156
+ if (localParentId < 0) {
157
+ return { global: globalParentId - 1, local: 0 };
158
+ }
159
+ return { global: globalParentId, local: localParentId };
160
+ };
161
+ //# sourceMappingURL=mutation-fixtures.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mutation-fixtures.js","sourceRoot":"","sources":["../../../../src/sync/next/test/mutation-fixtures.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAA;AAEhD,OAAO,EAAgB,OAAO,EAAE,MAAM,2BAA2B,CAAA;AAEjE,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAA;AAC1E,OAAO,EAAE,mBAAmB,EAAE,4BAA4B,EAAE,MAAM,aAAa,CAAA;AAE/E,OAAO,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAEtE,+DAA+D;AAC/D,MAAM,CAAC,MAAM,KAAK,GAAG,WAAW,CAAC;IAC/B,UAAU,EAAE,CAAC,EAAU,EAAE,EAAE,CAAC,eAAe,EAAE,EAAE;IAC/C,eAAe,EAAE,CAAC,EAAU,EAAE,SAAkB,EAAE,EAAE,CAAC,CAAC,qBAAqB,EAAE,EAAE,EAAE,SAAS,CAAC;IAC3F,aAAa,EAAE,CAAC,EAAU,EAAE,SAAkB,EAAE,EAAE,CAAC,CAAC,kBAAkB,EAAE,EAAE,EAAE,SAAS,CAAC;IACtF,eAAe,EAAE,CAAC,EAAU,EAAE,EAAE,CAAC,qBAAqB,EAAE,EAAE;IAC1D,UAAU,EAAE,CAAC,EAAU,EAAE,EAAE,CAAC,eAAe,EAAE,EAAE;CAChD,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,UAAU,EAAE,cAAc,CACxB,YAAY,EACZ,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,EACzD,kDAAkD,EAClD;QACE,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;YAClB,MAAM,EAAE;gBACN,GAAG,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,eAAe,CAAC,EAAE,EAAE,IAAI,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;aAC7F;SACF,CAAC;KACH,CACF;IACD,UAAU,EAAE,cAAc,CACxB,YAAY,EACZ,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,EAC1E,8FAA8F,EAC9F;QACE,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE;QAC9B,8DAA8D;QAC9D,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,IAAI,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,eAAe,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,KAAK;YACtG,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,eAAe,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC,EAAE;YACtE,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,eAAe,CAAC,EAAE,EAAE,IAAI,CAAC,EAAE,KAAK,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE;KAC9G,CACF;IACD,YAAY,EAAE,cAAc,CAC1B,cAAc,EACd,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC;IACpC,yDAAyD;IACzD,kDAAkD,EAClD;QACE,8BAA8B;QAC9B,2BAA2B;QAC3B,8BAA8B;QAC9B,8BAA8B;QAC9B,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;YAClB,OAAO,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,eAAe,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;YAChE,MAAM,EAAE,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,aAAa,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC,EAAE;SACjD,CAAC;KACH,CACF;IACD,cAAc,EAAE,cAAc,CAC5B,gBAAgB,EAChB,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,EACpC,mDAAmD,EACnD;QACE,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;YAClB,OAAO,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,eAAe,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;YAChE,MAAM,EAAE,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,aAAa,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC,EAAE;SAClD,CAAC;KACH,CACF;IACD,aAAa,EAAE,cAAc,CAC3B,eAAe,EACf,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,EACnD,0DAA0D,EAC1D;QACE,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,CAAC;YACnB,OAAO,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,eAAe,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC;YACrF,MAAM,EAAE,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,KAAK,CAAC,aAAa,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC,EAAE;SAChE,CAAC;KACH,CACF;IACD,UAAU,EAAE,cAAc,CACxB,YAAY,EACZ,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,EACpC,2DAA2D,EAC3D;QACE,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE;YAC9B,MAAM,kBAAkB,GAAG,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,aAAa,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,CAAA;YACtF,OAAO;gBACL,OAAO,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,eAAe,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;gBAChE,MAAM,EAAE;oBACN,yDAAyD;oBACzD,GAAG,EAAE,CAAC,KAAK,CAAC,aAAa,CAAC,EAAE,EAAE,CAAC,kBAAkB,CAAC,CAAC;iBACpD;aACF,CAAA;QACH,CAAC;KACF,CACF;IACD,eAAe,EAAE,cAAc,CAC7B,iBAAiB,EACjB,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,EAC9D,sDAAsD,EACtD;QACE,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;YAC5B,OAAO,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;YAC/B,MAAM,EAAE,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,eAAe,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE;SACxD,CAAC;KACH,CACF;IACD,WAAW,EAAE,cAAc,CACzB,aAAa,EACb,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,EACzD,8CAA8C,EAC9C;QACE,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;YAClB,OAAO,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,eAAe,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;YAChE,MAAM,EAAE,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC,EAAE;SAC7C,CAAC;KACH,CACF;IACD,aAAa,EAAE,cAAc,CAC3B,eAAe,EACf,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,EACzD,8CAA8C,EAC9C;QACE,SAAS,EAAE,IAAI;QACf,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC;KACjE,CACF;CACF,CAAA;AAID,MAAM,CAAC,MAAM,YAAY,GAAG,CAC1B,aAA6B,EAC7B,YAA6C,EAC3B,EAAE;IACpB,MAAM,QAAQ,GAAqB,CAAC,aAAa,CAAC,CAAA;IAElD,IAAI,cAAc,GAAY,OAAO,CAAA;IAErC,MAAM,cAAc,GAAG,CAAC,WAA4B,EAAW,EAAE;QAC/D,IAAI,WAAW,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC;YAClC,OAAO,EAAE,MAAM,EAAE,cAAc,CAAC,MAAM,EAAE,KAAK,EAAE,cAAc,CAAC,KAAK,GAAG,CAAC,EAAE,CAAA;QAC3E,CAAC;QACD,OAAO,EAAE,MAAM,EAAE,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAA;IACxD,CAAC,CAAA;IAED,MAAM,UAAU,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,YAAY,EAAE,EAAE;QACpD,MAAM,WAAW,GAAG,YAAY,CAAC,YAAY,CAAC,QAAQ,CAAE,CAAA;QACxD,MAAM,OAAO,GAAG,cAAc,CAAC,WAAW,CAAC,CAAA;QAC3C,cAAc,GAAG,OAAO,CAAA;QAExB,MAAM,aAAa,GAAG,mBAAmB,CAAC,mBAAmB,CAAC,QAAQ,EAAE,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC,EAAE,SAAS,CAAC,CAAA;QAC7G,uDAAuD;QACvD,qEAAqE;QACrE,wDAAwD;QACxD,6BAA6B;QAC7B,4BAA4B;QAC5B,kCAAkC;QAClC,6CAA6C;QAC7C,iCAAiC;QACjC,UAAU;QACV,mCAAmC;QACnC,kCAAkC;QAClC,6CAA6C;QAC7C,iCAAiC;QACjC,UAAU;QACV,QAAQ;QAER,mFAAmF;QACnF,OAAO;QACP,KAAK;QAEL,sFAAsF;QACtF,+DAA+D;QAC/D,0EAA0E;QAC1E,0BAA0B;QAC1B,mCAAmC;QACnC,sCAAsC;QACtC,wCAAwC;QACxC,eAAe;QACf,kCAAkC;QAClC,QAAQ;QACR,MAAM;QACN,eAAe;QACf,IAAI;QACJ,kBAAkB;QAClB,sFAAsF;QACtF,+FAA+F;QAC/F,iFAAiF;QACjF,aAAa;QACb,sCAAsC;QAEtC,uCAAuC;QAEvC,MAAM,KAAK,GAAG,4BAA4B,CAAC;YACzC,aAAa,EAAE,WAAW,CAAC,OAAO,CAAC,KAAK;YACxC,IAAI,EAAE,YAAY,CAAC,IAAI;YACvB,YAAY,EAAE,aAAa;SAC5B,CAAC,CAAA;QAEF,MAAM,IAAI,GAAG;YACX,EAAE,EAAE,OAAO;YACX,QAAQ,EAAE,WAAW,CAAC,OAAO,CAAC;YAC9B,QAAQ,EAAE,YAAY,CAAC,QAAQ;YAC/B,IAAI,EAAE,YAAY,CAAC,IAAI;YACvB,UAAU,EAAE,KAAK;SACO,CAAA;QAC1B,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACnB,OAAO,IAAI,CAAA;IACb,CAAC,CAAC,CAAA;IAEF,UAAU,CAAC,OAAO,CAAC,aAAsB,CAAC,CAAA;IAE1C,wCAAwC;IAExC,OAAO,UAAU,CAAA;AACnB,CAAC,CAAA;AAED,MAAM,WAAW,GAAG,CAAC,OAAgB,EAAW,EAAE;IAChD,MAAM,cAAc,GAAG,OAAO,CAAC,MAAM,CAAA;IACrC,MAAM,aAAa,GAAG,OAAO,CAAC,KAAK,GAAG,CAAC,CAAA;IAEvC,IAAI,aAAa,GAAG,CAAC,EAAE,CAAC;QACtB,OAAO,EAAE,MAAM,EAAE,cAAc,GAAG,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAA;IACjD,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,KAAK,EAAE,aAAa,EAAE,CAAA;AACzD,CAAC,CAAA"}
@@ -1,12 +1,25 @@
1
- import { type Effect, Schema, type Stream, type SubscriptionRef } from '@livestore/utils/effect';
1
+ import type { Effect, HttpClient, Option, Stream, SubscriptionRef } from '@livestore/utils/effect';
2
+ import { Schema } from '@livestore/utils/effect';
3
+ import type { EventId } from '../adapter-types.js';
2
4
  import type { MutationEvent } from '../schema/mutations.js';
3
- export type SyncImpl = {
4
- pull: (cursor: string | undefined) => Stream.Stream<MutationEvent.AnyEncoded, IsOfflineError | InvalidPullError>;
5
- pushes: Stream.Stream<{
5
+ export interface SyncBackendOptionsBase {
6
+ type: string;
7
+ [key: string]: Schema.JsonValue;
8
+ }
9
+ export type SyncBackend<TSyncMetadata = Schema.JsonValue> = {
10
+ pull: (args: Option.Option<{
11
+ cursor: EventId;
12
+ metadata: Option.Option<TSyncMetadata>;
13
+ }>, options: {
14
+ listenForNew: boolean;
15
+ }) => Stream.Stream<{
6
16
  mutationEventEncoded: MutationEvent.AnyEncoded;
17
+ metadata: Option.Option<TSyncMetadata>;
7
18
  persisted: boolean;
8
- }>;
9
- push: (mutationEventEncoded: MutationEvent.AnyEncoded, persisted: boolean) => Effect.Effect<void, IsOfflineError | InvalidPushError>;
19
+ }, IsOfflineError | InvalidPullError, HttpClient.HttpClient>;
20
+ push: (mutationEventEncoded: MutationEvent.AnyEncoded, persisted: boolean) => Effect.Effect<{
21
+ metadata: Option.Option<TSyncMetadata>;
22
+ }, IsOfflineError | InvalidPushError, HttpClient.HttpClient>;
10
23
  isConnected: SubscriptionRef.SubscriptionRef<boolean>;
11
24
  };
12
25
  declare const IsOfflineError_base: Schema.TaggedErrorClass<IsOfflineError, "IsOfflineError", {
@@ -1 +1 @@
1
- {"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/sync/sync.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,EAAE,KAAK,MAAM,EAAE,KAAK,eAAe,EAAE,MAAM,yBAAyB,CAAA;AAEhG,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAA;AAE3D,MAAM,MAAM,QAAQ,GAAG;IAErB,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,KAAK,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,UAAU,EAAE,cAAc,GAAG,gBAAgB,CAAC,CAAA;IAChH,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC;QAAE,oBAAoB,EAAE,aAAa,CAAC,UAAU,CAAC;QAAC,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC,CAAA;IAC7F,IAAI,EAAE,CACJ,oBAAoB,EAAE,aAAa,CAAC,UAAU,EAC9C,SAAS,EAAE,OAAO,KACf,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,cAAc,GAAG,gBAAgB,CAAC,CAAA;IAC3D,WAAW,EAAE,eAAe,CAAC,eAAe,CAAC,OAAO,CAAC,CAAA;CACtD,CAAA;;;;AAED,qBAAa,cAAe,SAAQ,mBAA0D;CAAG;;;;;;AACjG,qBAAa,gBAAiB,SAAQ,qBAEpC;CAAG;;;;;;AACL,qBAAa,gBAAiB,SAAQ,qBAEpC;CAAG"}
1
+ {"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/sync/sync.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AAClG,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAA;AAEhD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAA;AAE3D,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAA;IACZ,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC,SAAS,CAAA;CAChC;AAED,MAAM,MAAM,WAAW,CAAC,aAAa,GAAG,MAAM,CAAC,SAAS,IAAI;IAC1D,IAAI,EAAE,CACJ,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC;QAClB,MAAM,EAAE,OAAO,CAAA;QACf,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,CAAA;KACvC,CAAC,EACF,OAAO,EAAE;QAAE,YAAY,EAAE,OAAO,CAAA;KAAE,KAC/B,MAAM,CAAC,MAAM,CAChB;QACE,oBAAoB,EAAE,aAAa,CAAC,UAAU,CAAA;QAC9C,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,CAAA;QACtC,SAAS,EAAE,OAAO,CAAA;KACnB,EACD,cAAc,GAAG,gBAAgB,EACjC,UAAU,CAAC,UAAU,CACtB,CAAA;IAED,IAAI,EAAE,CACJ,oBAAoB,EAAE,aAAa,CAAC,UAAU,EAC9C,SAAS,EAAE,OAAO,KACf,MAAM,CAAC,MAAM,CAChB;QAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,CAAA;KAAE,EAC1C,cAAc,GAAG,gBAAgB,EACjC,UAAU,CAAC,UAAU,CACtB,CAAA;IACD,WAAW,EAAE,eAAe,CAAC,eAAe,CAAC,OAAO,CAAC,CAAA;CACtD,CAAA;;;;AAED,qBAAa,cAAe,SAAQ,mBAA0D;CAAG;;;;;;AACjG,qBAAa,gBAAiB,SAAQ,qBAEpC;CAAG;;;;;;AACL,qBAAa,gBAAiB,SAAQ,qBAEpC;CAAG"}
@@ -1 +1 @@
1
- {"version":3,"file":"sync.js","sourceRoot":"","sources":["../../src/sync/sync.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,MAAM,EAAqC,MAAM,yBAAyB,CAAA;AAehG,MAAM,OAAO,cAAe,SAAQ,MAAM,CAAC,WAAW,EAAkB,CAAC,gBAAgB,EAAE,EAAE,CAAC;CAAG;AACjG,MAAM,OAAO,gBAAiB,SAAQ,MAAM,CAAC,WAAW,EAAoB,CAAC,kBAAkB,EAAE;IAC/F,OAAO,EAAE,MAAM,CAAC,MAAM;CACvB,CAAC;CAAG;AACL,MAAM,OAAO,gBAAiB,SAAQ,MAAM,CAAC,WAAW,EAAoB,CAAC,kBAAkB,EAAE;IAC/F,OAAO,EAAE,MAAM,CAAC,MAAM;CACvB,CAAC;CAAG"}
1
+ {"version":3,"file":"sync.js","sourceRoot":"","sources":["../../src/sync/sync.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAA;AAsChD,MAAM,OAAO,cAAe,SAAQ,MAAM,CAAC,WAAW,EAAkB,CAAC,gBAAgB,EAAE,EAAE,CAAC;CAAG;AACjG,MAAM,OAAO,gBAAiB,SAAQ,MAAM,CAAC,WAAW,EAAoB,CAAC,kBAAkB,EAAE;IAC/F,OAAO,EAAE,MAAM,CAAC,MAAM;CACvB,CAAC;CAAG;AACL,MAAM,OAAO,gBAAiB,SAAQ,MAAM,CAAC,WAAW,EAAoB,CAAC,kBAAkB,EAAE;IAC/F,OAAO,EAAE,MAAM,CAAC,MAAM;CACvB,CAAC;CAAG"}
package/dist/version.d.ts CHANGED
@@ -1,2 +1,10 @@
1
1
  export declare const liveStoreVersion: string;
2
+ /**
3
+ * This version number is incremented whenever the internal storage format changes in a breaking way.
4
+ * Whenever this version changes, LiveStore will start with fresh database files. Old database files are not deleted.
5
+ *
6
+ * While LiveStore is in alpha, this might happen more frequently.
7
+ * In the future, LiveStore will provide a migration path for older database files to avoid the impression of data loss.
8
+ */
9
+ export declare const liveStoreStorageFormatVersion = 2;
2
10
  //# sourceMappingURL=version.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,gBAAgB,QAAsB,CAAA"}
1
+ {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,gBAAgB,QAAsB,CAAA;AAEnD;;;;;;GAMG;AACH,eAAO,MAAM,6BAA6B,IAAI,CAAA"}
package/dist/version.js CHANGED
@@ -1,3 +1,12 @@
1
1
  import packageJson from '../package.json' with { type: 'json' };
2
+ // import packageJson from '../package.json' assert { type: 'json' }
2
3
  export const liveStoreVersion = packageJson.version;
4
+ /**
5
+ * This version number is incremented whenever the internal storage format changes in a breaking way.
6
+ * Whenever this version changes, LiveStore will start with fresh database files. Old database files are not deleted.
7
+ *
8
+ * While LiveStore is in alpha, this might happen more frequently.
9
+ * In the future, LiveStore will provide a migration path for older database files to avoid the impression of data loss.
10
+ */
11
+ export const liveStoreStorageFormatVersion = 2;
3
12
  //# sourceMappingURL=version.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,OAAO,WAAW,MAAM,iBAAiB,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAA;AAE/D,MAAM,CAAC,MAAM,gBAAgB,GAAG,WAAW,CAAC,OAAO,CAAA"}
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,OAAO,WAAW,MAAM,iBAAiB,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAA;AAC/D,oEAAoE;AAEpE,MAAM,CAAC,MAAM,gBAAgB,GAAG,WAAW,CAAC,OAAO,CAAA;AAEnD;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,6BAA6B,GAAG,CAAC,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@livestore/common",
3
- "version": "0.0.58-dev.0",
3
+ "version": "0.0.58-dev.10",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -14,6 +14,14 @@
14
14
  "./schema": {
15
15
  "types": "./dist/schema/index.d.ts",
16
16
  "default": "./dist/schema/index.js"
17
+ },
18
+ "./sync/next": {
19
+ "types": "./dist/sync/next/mod.d.ts",
20
+ "default": "./dist/sync/next/mod.js"
21
+ },
22
+ "./sync/next/test": {
23
+ "types": "./dist/sync/next/test/mod.d.ts",
24
+ "default": "./dist/sync/next/test/mod.js"
17
25
  }
18
26
  },
19
27
  "types": "./dist/index.d.ts",
@@ -24,16 +32,25 @@
24
32
  ],
25
33
  "sql-queries": [
26
34
  "./dist/sql-queries/index.d.ts"
35
+ ],
36
+ "sync/next": [
37
+ "./dist/sync/next/mod.d.ts"
38
+ ],
39
+ "sync/next/test": [
40
+ "./dist/sync/next/test/mod.d.ts"
27
41
  ]
28
42
  }
29
43
  },
30
44
  "dependencies": {
31
45
  "@opentelemetry/api": "^1.9.0",
32
- "@livestore/utils": "0.0.58-dev.0",
33
- "effect-db-schema": "0.0.58-dev.0"
46
+ "graphology": "0.26.0-alpha1",
47
+ "graphology-dag": "0.4.1",
48
+ "graphology-types": "0.24.7",
49
+ "@livestore/utils": "0.0.58-dev.10",
50
+ "@livestore/db-schema": "0.0.58-dev.10"
34
51
  },
35
52
  "devDependencies": {
36
- "vitest": "^2.0.4"
53
+ "vitest": "^2.1.4"
37
54
  },
38
55
  "publishConfig": {
39
56
  "access": "public"
@@ -9,9 +9,10 @@ export interface PreparedStatement {
9
9
  execute(bindValues: PreparedBindValues | undefined, options?: { onRowsChanged?: (rowsChanged: number) => void }): void
10
10
  select<T>(bindValues: PreparedBindValues | undefined): ReadonlyArray<T>
11
11
  finalize(): void
12
+ sql: string
12
13
  }
13
14
 
14
- export type StoreAdapter = {
15
+ export type ClientSession = {
15
16
  /** SQLite database with synchronous API running in the same thread (usually in-memory) */
16
17
  syncDb: SynchronousDatabase
17
18
  /** The coordinator is responsible for persisting the database, syncing etc */
@@ -28,6 +29,7 @@ export type SynchronousDatabase = {
28
29
  ): void
29
30
  select<T>(queryStr: string, bindValues?: PreparedBindValues | undefined): ReadonlyArray<T>
30
31
  export(): Uint8Array
32
+ close(): void
31
33
  }
32
34
 
33
35
  export type ResetMode = 'all-data' | 'only-app-db'
@@ -60,24 +62,62 @@ export type BootStatus = typeof BootStatus.Type
60
62
  export type Coordinator = {
61
63
  devtools: {
62
64
  enabled: boolean
65
+ // TODO incorporate sessionId and rethink appHostId
63
66
  appHostId: string
64
67
  }
68
+ sessionId: string
65
69
  // TODO is exposing the lock status really needed (or only relevant for web adapter?)
66
70
  lockStatus: SubscriptionRef.SubscriptionRef<LockStatus>
67
- syncMutations: Stream.Stream<MutationEvent.AnyEncoded, UnexpectedError>
71
+ syncMutations: Stream.Stream<MutationEvent.Any, UnexpectedError>
68
72
  execute(queryStr: string, bindValues: PreparedBindValues | undefined): Effect.Effect<void, UnexpectedError>
69
- mutate(mutationEventEncoded: MutationEvent.Any, options: { persisted: boolean }): Effect.Effect<void, UnexpectedError>
73
+ mutate(
74
+ mutationEventEncoded: MutationEvent.AnyEncoded,
75
+ options: { persisted: boolean },
76
+ ): Effect.Effect<void, UnexpectedError>
77
+ /** Can be called synchronously */
78
+ nextMutationEventIdPair: (opts: { localOnly: boolean }) => Effect.Effect<EventIdPair, UnexpectedError>
79
+ /** Used to initially get the current mutation event id to use as `parentId` for the next mutation event */
80
+ getCurrentMutationEventId: Effect.Effect<EventId, UnexpectedError>
70
81
  export: Effect.Effect<Uint8Array | undefined, UnexpectedError>
71
82
  getMutationLogData: Effect.Effect<Uint8Array, UnexpectedError>
72
83
  networkStatus: SubscriptionRef.SubscriptionRef<NetworkStatus>
73
84
  }
74
85
 
86
+ /**
87
+ * Can be used in queries to refer to the current session id.
88
+ * Will be replaced with the actual session id at runtime
89
+ *
90
+ * Example:
91
+ * ```ts
92
+ * const query$ = rowQuery(tables.app, SessionIdSymbol)
93
+ * ```
94
+ */
95
+ export const SessionIdSymbol = Symbol.for('@livestore/session-id')
96
+ export type SessionIdSymbol = typeof SessionIdSymbol
97
+
75
98
  export type LockStatus = 'has-lock' | 'no-lock'
76
99
 
100
+ /**
101
+ * LiveStore event id value consisting of a globally unique event sequence number
102
+ * and a local sequence number.
103
+ *
104
+ * The local sequence number is only used for localOnly mutations and starts from 0 for each global sequence number.
105
+ */
106
+ export type EventId = { global: number; local: number }
107
+
108
+ export const EventId = Schema.Struct({
109
+ global: Schema.Number,
110
+ local: Schema.Number,
111
+ }).annotations({ title: 'LiveStore.EventId' })
112
+
113
+ export type EventIdPair = { id: EventId; parentId: EventId }
114
+
115
+ export const ROOT_ID = { global: -1, local: 0 } satisfies EventId
116
+
77
117
  export type BootDb = {
78
118
  _tag: 'BootDb'
79
119
  execute(queryStr: string, bindValues?: PreparedBindValues): void
80
- mutate: <const TMutationArg extends ReadonlyArray<MutationEvent.Any>>(...list: TMutationArg) => void
120
+ mutate: <const TMutationArg extends ReadonlyArray<MutationEvent.PartialAny>>(...list: TMutationArg) => void
81
121
  select<T>(queryStr: string, bindValues?: PreparedBindValues): ReadonlyArray<T>
82
122
  txn(callback: () => void): void
83
123
  }
@@ -89,7 +129,6 @@ export class UnexpectedError extends Schema.TaggedError<UnexpectedError>()('Live
89
129
  }) {
90
130
  static mapToUnexpectedError = <A, E, R>(effect: Effect.Effect<A, E, R>) =>
91
131
  effect.pipe(
92
- Effect.tapCauseLogPretty,
93
132
  Effect.mapError((cause) => (Schema.is(UnexpectedError)(cause) ? cause : new UnexpectedError({ cause }))),
94
133
  Effect.catchAllDefect((cause) => new UnexpectedError({ cause })),
95
134
  )
@@ -167,11 +206,11 @@ export type ConnectDevtoolsToStore = (
167
206
  storeDevtoolsChannel: StoreDevtoolsChannel,
168
207
  ) => Effect.Effect<void, UnexpectedError, Scope.Scope>
169
208
 
170
- export type StoreAdapterFactory = (opts: {
209
+ export type Adapter = (opts: {
171
210
  schema: LiveStoreSchema
172
211
  storeId: string
173
212
  devtoolsEnabled: boolean
174
213
  bootStatusQueue: Queue.Queue<BootStatus>
175
214
  shutdown: (cause: Cause.Cause<any>) => Effect.Effect<void>
176
215
  connectDevtoolsToStore: ConnectDevtoolsToStore
177
- }) => Effect.Effect<StoreAdapter, UnexpectedError, Scope.Scope>
216
+ }) => Effect.Effect<ClientSession, UnexpectedError, Scope.Scope>
@@ -16,7 +16,7 @@ export class BoundMap<K, V> {
16
16
  this.#map.set(key, value)
17
17
  // console.log(this.#map.size, this.#sizeLimit);
18
18
  if (this.#map.size > this.#sizeLimit) {
19
- const firstKey = this.#map.keys().next().value
19
+ const firstKey = this.#map.keys().next().value as K
20
20
  const deletedValue = this.#map.get(firstKey)!
21
21
  this.#map.delete(firstKey)
22
22
  if (this.onEvict) {
@@ -94,7 +94,8 @@ describe('derived mutations', () => {
94
94
  })
95
95
  })
96
96
 
97
- const patchId = (muationEvent: MutationEvent.Any) => {
97
+ const patchId = (muationEvent: MutationEvent.PartialAny) => {
98
+ // TODO use new id paradigm
98
99
  const id = `00000000-0000-0000-0000-000000000000`
99
100
  return { ...muationEvent, id }
100
101
  }
@@ -1,6 +1,6 @@
1
+ import type { SqliteDsl } from '@livestore/db-schema'
1
2
  import type { GetValForKey } from '@livestore/utils'
2
3
  import { ReadonlyRecord, Schema } from '@livestore/utils/effect'
3
- import type { SqliteDsl } from 'effect-db-schema'
4
4
 
5
5
  import type { MutationEvent } from './schema/mutations.js'
6
6
  import { defineMutation } from './schema/mutations.js'
@@ -38,9 +38,9 @@ export const deriveCreateMutationDef = <
38
38
  (col) => col.nullable === false && col.default._tag === 'None',
39
39
  )
40
40
 
41
- const insertSchema = Schema.Struct(ReadonlyRecord.map(requiredColumns, (col) => col.schema)).pipe(
42
- Schema.extend(Schema.partial(Schema.Struct(ReadonlyRecord.map(optionalFields, (col) => col.schema)))),
43
- )
41
+ const insertSchema = Schema.Struct(ReadonlyRecord.map(requiredColumns, (col) => col.schema))
42
+ .pipe(Schema.extend(Schema.partial(Schema.Struct(ReadonlyRecord.map(optionalFields, (col) => col.schema)))))
43
+ .annotations({ title: `${tableName}:Insert` })
44
44
 
45
45
  return defineMutation(
46
46
  `_Derived_Create_${tableName}`,
@@ -76,7 +76,7 @@ export const deriveUpdateMutationDef = <
76
76
  Schema.Struct({
77
77
  where: Schema.partial(table.schema),
78
78
  values: Schema.partial(table.schema),
79
- }),
79
+ }).annotations({ title: `${tableName}:Update` }),
80
80
  ({ where, values }) => {
81
81
  const [sql, bindValues] = updateRows({
82
82
  tableName: table.sqliteDef.name,
@@ -140,8 +140,8 @@ export namespace DerivedMutationHelperFns {
140
140
  > = SqliteDsl.AnyIfConstained<
141
141
  TColumns,
142
142
  UseShortcut<TOptions> extends true
143
- ? (values?: GetValForKey<SqliteDsl.FromColumns.InsertRowDecoded<TColumns>, 'value'>) => MutationEvent.Any
144
- : (values: SqliteDsl.FromColumns.InsertRowDecoded<TColumns>) => MutationEvent.Any
143
+ ? (values?: GetValForKey<SqliteDsl.FromColumns.InsertRowDecoded<TColumns>, 'value'>) => MutationEvent.PartialAny
144
+ : (values: SqliteDsl.FromColumns.InsertRowDecoded<TColumns>) => MutationEvent.PartialAny
145
145
  >
146
146
 
147
147
  export type UpdateMutationFn<
@@ -150,17 +150,17 @@ export namespace DerivedMutationHelperFns {
150
150
  > = SqliteDsl.AnyIfConstained<
151
151
  TColumns,
152
152
  UseShortcut<TOptions> extends true
153
- ? (values: Partial<GetValForKey<SqliteDsl.FromColumns.RowDecoded<TColumns>, 'value'>>) => MutationEvent.Any
153
+ ? (values: Partial<GetValForKey<SqliteDsl.FromColumns.RowDecoded<TColumns>, 'value'>>) => MutationEvent.PartialAny
154
154
  : (args: {
155
155
  where: Partial<SqliteDsl.FromColumns.RowDecoded<TColumns>>
156
156
  values: Partial<SqliteDsl.FromColumns.RowDecoded<TColumns>>
157
- }) => MutationEvent.Any
157
+ }) => MutationEvent.PartialAny
158
158
  >
159
159
 
160
160
  export type DeleteMutationFn<
161
161
  TColumns extends SqliteDsl.ConstraintColumns,
162
162
  _TOptions extends DbSchema.TableOptions,
163
- > = (args: { where: Partial<SqliteDsl.FromColumns.RowDecoded<TColumns>> }) => MutationEvent.Any
163
+ > = (args: { where: Partial<SqliteDsl.FromColumns.RowDecoded<TColumns>> }) => MutationEvent.PartialAny
164
164
 
165
165
  type UseShortcut<TOptions extends DbSchema.TableOptions> = TOptions['isSingleColumn'] extends true
166
166
  ? TOptions['isSingleton'] extends true
@@ -9,5 +9,5 @@ export type PrepareDevtoolsBridge = {
9
9
  appHostId: string
10
10
  copyToClipboard: (text: string) => Effect.Effect<void>
11
11
  sendEscapeKey?: Effect.Effect<void>
12
- isLeaderTab: boolean
12
+ isLeader: boolean
13
13
  }
@@ -74,7 +74,7 @@ export class MutationBroadcast extends LSDMessage('LSD.MutationBroadcast', {
74
74
  }) {}
75
75
 
76
76
  export class RunMutationReq extends LSDReqResMessage('LSD.RunMutationReq', {
77
- mutationEventEncoded: mutationEventSchemaEncodedAny,
77
+ mutationEventEncoded: mutationEventSchemaEncodedAny.pipe(Schema.omit('id', 'parentId')),
78
78
  persisted: Schema.Boolean,
79
79
  }) {}
80
80
 
@@ -158,12 +158,19 @@ export class SyncingInfoRes extends LSDReqResMessage('LSD.SyncingInfoRes', {
158
158
  syncingInfo: SyncingInfo,
159
159
  }) {}
160
160
 
161
+ export class SyncHistorySubscribe extends LSDReqResMessage('LSD.SyncHistorySubscribe', {}) {}
162
+ export class SyncHistoryUnsubscribe extends LSDReqResMessage('LSD.SyncHistoryUnsubscribe', {}) {}
163
+ export class SyncHistoryRes extends LSDReqResMessage('LSD.SyncHistoryRes', {
164
+ mutationEventEncoded: mutationEventSchemaEncodedAny,
165
+ metadata: Schema.Option(Schema.JsonValue),
166
+ }) {}
167
+
161
168
  export class DevtoolsReady extends LSDMessage('LSD.DevtoolsReady', {}) {}
162
169
 
163
170
  export class DevtoolsConnected extends LSDChannelMessage('LSD.DevtoolsConnected', {}) {}
164
171
 
165
172
  export class AppHostReady extends LSDChannelMessage('LSD.AppHostReady', {
166
- isLeaderTab: Schema.Boolean,
173
+ isLeader: Schema.Boolean,
167
174
  }) {}
168
175
 
169
176
  export class Disconnect extends LSDChannelMessage('LSD.Disconnect', {}) {}
@@ -186,6 +193,8 @@ export const MessageToAppHostCoordinator = Schema.Union(
186
193
  RunMutationReq,
187
194
  Ping,
188
195
  DatabaseFileInfoReq,
196
+ SyncHistorySubscribe,
197
+ SyncHistoryUnsubscribe,
189
198
  SyncingInfoReq,
190
199
  ).annotations({ identifier: 'LSD.MessageToAppHostCoordinator' })
191
200
 
@@ -219,6 +228,7 @@ export const MessageFromAppHostCoordinator = Schema.Union(
219
228
  RunMutationRes,
220
229
  Pong,
221
230
  DatabaseFileInfoRes,
231
+ SyncHistoryRes,
222
232
  SyncingInfoRes,
223
233
  ).annotations({ identifier: 'LSD.MessageFromAppHostCoordinator' })
224
234
 
@@ -8,6 +8,7 @@ export * from './devtools-bridge.js'
8
8
  export namespace WebBridge {
9
9
  export class AppHostReady extends Schema.TaggedStruct('LSD.WebBridge.AppHostReady', {
10
10
  appHostId: Schema.String,
11
+ // storeId: Schema.String,
11
12
  isLeader: Schema.Boolean,
12
13
  }) {}
13
14
 
@@ -24,6 +25,7 @@ export namespace WebBridge {
24
25
  */
25
26
  webBridgeId: Schema.String,
26
27
  isLeader: Schema.Boolean,
28
+ storeId: Schema.String,
27
29
  }) {}
28
30
 
29
31
  export class AppHostWillDisconnect extends Schema.TaggedStruct('LSD.WebBridge.AppHostWillDisconnect', {
package/src/index.ts CHANGED
@@ -12,3 +12,9 @@ export * as Devtools from './devtools/index.js'
12
12
  export * from './debug-info.js'
13
13
  export * from './bounded-collections.js'
14
14
  export * from './version.js'
15
+
16
+ declare global {
17
+ interface LiveStoreGlobal {
18
+ // syncBackend: never
19
+ }
20
+ }
@@ -3,7 +3,7 @@ import { Chunk, Effect, Option, Schema, Stream } from '@livestore/utils/effect'
3
3
 
4
4
  import { type MigrationOptionsFromMutationLog, type SynchronousDatabase, UnexpectedError } from './adapter-types.js'
5
5
  import { getExecArgsFromMutation } from './mutation.js'
6
- import type { LiveStoreSchema, MutationDef, MutationLogMetaRow } from './schema/index.js'
6
+ import type { LiveStoreSchema, MutationDef, MutationEvent, MutationLogMetaRow } from './schema/index.js'
7
7
  import { MUTATION_LOG_META_TABLE } from './schema/index.js'
8
8
  import type { PreparedBindValues } from './util.js'
9
9
  import { sql } from './util.js'
@@ -54,10 +54,11 @@ This likely means the schema has changed in an incompatible way.
54
54
  )
55
55
 
56
56
  const mutationEventDecoded = {
57
- id: row.id,
57
+ id: { global: row.idGlobal, local: row.idLocal },
58
+ parentId: { global: row.parentIdGlobal, local: row.parentIdLocal },
58
59
  mutation: row.mutation,
59
60
  args: argsDecoded,
60
- }
61
+ } satisfies MutationEvent.Any
61
62
 
62
63
  const execArgsArr = getExecArgsFromMutation({ mutationDef, mutationEventDecoded })
63
64
 
@@ -84,8 +85,8 @@ This likely means the schema has changed in an incompatible way.
84
85
 
85
86
  const stmt = logDb.prepare(sql`\
86
87
  SELECT * FROM ${MUTATION_LOG_META_TABLE}
87
- WHERE id > COALESCE($id, '')
88
- ORDER BY id ASC
88
+ WHERE idGlobal > COALESCE($idGlobal, '') AND idLocal > COALESCE($idLocal, '')
89
+ ORDER BY idGlobal ASC, idLocal ASC
89
90
  LIMIT ${CHUNK_SIZE}
90
91
  `)
91
92
 
@@ -97,9 +98,17 @@ LIMIT ${CHUNK_SIZE}
97
98
  // End stream if no more rows
98
99
  if (Chunk.isChunk(item) && item.length === 0) return Option.none()
99
100
 
100
- const lastId = Chunk.isChunk(item) ? Chunk.last(item).pipe(Option.getOrUndefined)?.id : undefined
101
+ const lastId = Chunk.isChunk(item)
102
+ ? Chunk.last(item).pipe(
103
+ Option.map((_) => ({ global: _.idGlobal, local: _.idLocal })),
104
+ Option.getOrUndefined,
105
+ )
106
+ : undefined
101
107
  const nextItem = Chunk.fromIterable(
102
- stmt.select<MutationLogMetaRow>({ $id: lastId } as any as PreparedBindValues),
108
+ stmt.select<MutationLogMetaRow>({
109
+ $idGlobal: lastId?.global,
110
+ $idLocal: lastId?.local,
111
+ } as any as PreparedBindValues),
103
112
  )
104
113
  const prevItem = Chunk.isChunk(item) ? item : Chunk.empty()
105
114
  return Option.some([prevItem, nextItem])