@livestore/livestore 0.4.0-dev.21 → 0.4.0-dev.23

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 (216) hide show
  1. package/README.md +0 -1
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/QueryCache.js +1 -1
  4. package/dist/QueryCache.js.map +1 -1
  5. package/dist/SqliteDbWrapper.d.ts +5 -5
  6. package/dist/SqliteDbWrapper.d.ts.map +1 -1
  7. package/dist/SqliteDbWrapper.js +8 -8
  8. package/dist/SqliteDbWrapper.js.map +1 -1
  9. package/dist/SqliteDbWrapper.test.js +2 -2
  10. package/dist/SqliteDbWrapper.test.js.map +1 -1
  11. package/dist/effect/LiveStore.d.ts +130 -2
  12. package/dist/effect/LiveStore.d.ts.map +1 -1
  13. package/dist/effect/LiveStore.js +185 -6
  14. package/dist/effect/LiveStore.js.map +1 -1
  15. package/dist/effect/LiveStore.test.d.ts +2 -0
  16. package/dist/effect/LiveStore.test.d.ts.map +1 -0
  17. package/dist/effect/LiveStore.test.js +42 -0
  18. package/dist/effect/LiveStore.test.js.map +1 -0
  19. package/dist/effect/mod.d.ts +1 -1
  20. package/dist/effect/mod.d.ts.map +1 -1
  21. package/dist/effect/mod.js +3 -1
  22. package/dist/effect/mod.js.map +1 -1
  23. package/dist/live-queries/base-class.d.ts +3 -3
  24. package/dist/live-queries/base-class.d.ts.map +1 -1
  25. package/dist/live-queries/base-class.js +2 -2
  26. package/dist/live-queries/base-class.js.map +1 -1
  27. package/dist/live-queries/client-document-get-query.d.ts +1 -1
  28. package/dist/live-queries/client-document-get-query.d.ts.map +1 -1
  29. package/dist/live-queries/client-document-get-query.js +1 -1
  30. package/dist/live-queries/client-document-get-query.js.map +1 -1
  31. package/dist/live-queries/computed.d.ts.map +1 -1
  32. package/dist/live-queries/computed.js +2 -2
  33. package/dist/live-queries/computed.js.map +1 -1
  34. package/dist/live-queries/db-query.js +14 -14
  35. package/dist/live-queries/db-query.js.map +1 -1
  36. package/dist/live-queries/db-query.test.js +2 -2
  37. package/dist/live-queries/db-query.test.js.map +1 -1
  38. package/dist/live-queries/signal.test.js +2 -2
  39. package/dist/live-queries/signal.test.js.map +1 -1
  40. package/dist/mod.d.ts +2 -1
  41. package/dist/mod.d.ts.map +1 -1
  42. package/dist/mod.js +1 -0
  43. package/dist/mod.js.map +1 -1
  44. package/dist/reactive.d.ts +9 -9
  45. package/dist/reactive.d.ts.map +1 -1
  46. package/dist/reactive.js +9 -26
  47. package/dist/reactive.js.map +1 -1
  48. package/dist/reactive.test.js +2 -2
  49. package/dist/reactive.test.js.map +1 -1
  50. package/dist/store/StoreRegistry.d.ts +215 -0
  51. package/dist/store/StoreRegistry.d.ts.map +1 -0
  52. package/dist/store/StoreRegistry.js +267 -0
  53. package/dist/store/StoreRegistry.js.map +1 -0
  54. package/dist/store/StoreRegistry.test.d.ts +2 -0
  55. package/dist/store/StoreRegistry.test.d.ts.map +1 -0
  56. package/dist/store/StoreRegistry.test.js +381 -0
  57. package/dist/store/StoreRegistry.test.js.map +1 -0
  58. package/dist/store/create-store.d.ts +56 -6
  59. package/dist/store/create-store.d.ts.map +1 -1
  60. package/dist/store/create-store.js +32 -7
  61. package/dist/store/create-store.js.map +1 -1
  62. package/dist/store/devtools.d.ts +1 -1
  63. package/dist/store/devtools.d.ts.map +1 -1
  64. package/dist/store/devtools.js +16 -3
  65. package/dist/store/devtools.js.map +1 -1
  66. package/dist/store/store-eventstream.test.js +2 -2
  67. package/dist/store/store-eventstream.test.js.map +1 -1
  68. package/dist/store/store-types.d.ts +59 -9
  69. package/dist/store/store-types.d.ts.map +1 -1
  70. package/dist/store/store-types.js.map +1 -1
  71. package/dist/store/store-types.test.js +1 -1
  72. package/dist/store/store-types.test.js.map +1 -1
  73. package/dist/store/store.d.ts +102 -6
  74. package/dist/store/store.d.ts.map +1 -1
  75. package/dist/store/store.js +148 -47
  76. package/dist/store/store.js.map +1 -1
  77. package/dist/utils/dev.js.map +1 -1
  78. package/dist/utils/stack-info.js +2 -2
  79. package/dist/utils/stack-info.js.map +1 -1
  80. package/dist/utils/tests/fixture.d.ts +1 -1
  81. package/dist/utils/tests/fixture.d.ts.map +1 -1
  82. package/dist/utils/tests/fixture.js.map +1 -1
  83. package/dist/utils/tests/otel.d.ts.map +1 -1
  84. package/dist/utils/tests/otel.js +5 -5
  85. package/dist/utils/tests/otel.js.map +1 -1
  86. package/package.json +59 -18
  87. package/src/QueryCache.ts +1 -1
  88. package/src/SqliteDbWrapper.test.ts +4 -2
  89. package/src/SqliteDbWrapper.ts +12 -11
  90. package/src/ambient.d.ts +0 -7
  91. package/src/effect/LiveStore.test.ts +61 -0
  92. package/src/effect/LiveStore.ts +381 -8
  93. package/src/effect/mod.ts +13 -1
  94. package/src/live-queries/__snapshots__/db-query.test.ts.snap +336 -231
  95. package/src/live-queries/base-class.ts +7 -6
  96. package/src/live-queries/client-document-get-query.ts +4 -2
  97. package/src/live-queries/computed.ts +3 -2
  98. package/src/live-queries/db-query.test.ts +3 -2
  99. package/src/live-queries/db-query.ts +15 -15
  100. package/src/live-queries/signal.test.ts +3 -2
  101. package/src/mod.ts +2 -0
  102. package/src/reactive.test.ts +3 -2
  103. package/src/reactive.ts +22 -23
  104. package/src/store/StoreRegistry.test.ts +540 -0
  105. package/src/store/StoreRegistry.ts +418 -0
  106. package/src/store/create-store.ts +76 -15
  107. package/src/store/devtools.ts +20 -6
  108. package/src/store/store-eventstream.test.ts +4 -2
  109. package/src/store/store-types.test.ts +3 -1
  110. package/src/store/store-types.ts +64 -13
  111. package/src/store/store.ts +197 -60
  112. package/src/utils/dev.ts +2 -2
  113. package/src/utils/stack-info.ts +2 -2
  114. package/src/utils/tests/fixture.ts +2 -1
  115. package/src/utils/tests/otel.ts +8 -7
  116. package/docs/api/index.md +0 -3
  117. package/docs/building-with-livestore/complex-ui-state/index.md +0 -5
  118. package/docs/building-with-livestore/crud/index.md +0 -5
  119. package/docs/building-with-livestore/data-modeling/index.md +0 -1
  120. package/docs/building-with-livestore/debugging/index.md +0 -17
  121. package/docs/building-with-livestore/devtools/index.md +0 -79
  122. package/docs/building-with-livestore/events/index.md +0 -355
  123. package/docs/building-with-livestore/examples/ai-agent/index.md +0 -5
  124. package/docs/building-with-livestore/examples/index.md +0 -30
  125. package/docs/building-with-livestore/examples/todo-workspaces/index.md +0 -891
  126. package/docs/building-with-livestore/examples/turnbased-game/index.md +0 -7
  127. package/docs/building-with-livestore/opentelemetry/index.md +0 -208
  128. package/docs/building-with-livestore/production-checklist/index.md +0 -5
  129. package/docs/building-with-livestore/reactivity-system/index.md +0 -202
  130. package/docs/building-with-livestore/rules-for-ai-agents/index.md +0 -9
  131. package/docs/building-with-livestore/state/materializers/index.md +0 -300
  132. package/docs/building-with-livestore/state/sql-queries/index.md +0 -72
  133. package/docs/building-with-livestore/state/sqlite/index.md +0 -45
  134. package/docs/building-with-livestore/state/sqlite-schema/index.md +0 -306
  135. package/docs/building-with-livestore/state/sqlite-schema-effect/index.md +0 -300
  136. package/docs/building-with-livestore/store/index.md +0 -281
  137. package/docs/building-with-livestore/syncing/index.md +0 -136
  138. package/docs/building-with-livestore/tools/cli/index.md +0 -177
  139. package/docs/building-with-livestore/tools/mcp/index.md +0 -187
  140. package/docs/examples/cloudflare-adapter/index.md +0 -44
  141. package/docs/examples/expo-adapter/index.md +0 -44
  142. package/docs/examples/index.md +0 -55
  143. package/docs/examples/node-adapter/index.md +0 -44
  144. package/docs/examples/web-adapter/index.md +0 -52
  145. package/docs/framework-integrations/custom-elements/index.md +0 -142
  146. package/docs/framework-integrations/react-integration/index.md +0 -918
  147. package/docs/framework-integrations/solid-integration/index.md +0 -293
  148. package/docs/framework-integrations/svelte-integration/index.md +0 -42
  149. package/docs/framework-integrations/vue-integration/index.md +0 -294
  150. package/docs/getting-started/expo/index.md +0 -736
  151. package/docs/getting-started/node/index.md +0 -115
  152. package/docs/getting-started/react-web/index.md +0 -573
  153. package/docs/getting-started/solid/index.md +0 -3
  154. package/docs/getting-started/vue/index.md +0 -471
  155. package/docs/index.md +0 -209
  156. package/docs/llms.txt +0 -147
  157. package/docs/misc/CODE_OF_CONDUCT/index.md +0 -133
  158. package/docs/misc/FAQ/index.md +0 -37
  159. package/docs/misc/community/index.md +0 -88
  160. package/docs/misc/credits/index.md +0 -14
  161. package/docs/misc/design-partners/index.md +0 -13
  162. package/docs/misc/package-management/index.md +0 -21
  163. package/docs/misc/performance/index.md +0 -25
  164. package/docs/misc/resources/index.md +0 -46
  165. package/docs/misc/state-of-the-project/index.md +0 -37
  166. package/docs/misc/troubleshooting/index.md +0 -82
  167. package/docs/overview/concepts/index.md +0 -78
  168. package/docs/overview/how-livestore-works/index.md +0 -56
  169. package/docs/overview/introduction/index.md +0 -5
  170. package/docs/overview/technology-comparison/index.md +0 -40
  171. package/docs/overview/when-livestore/index.md +0 -81
  172. package/docs/overview/why-livestore/index.md +0 -5
  173. package/docs/patterns/ai/index.md +0 -15
  174. package/docs/patterns/anonymous-user-transition/index.md +0 -10
  175. package/docs/patterns/app-evolution/index.md +0 -72
  176. package/docs/patterns/auth/index.md +0 -226
  177. package/docs/patterns/effect/index.md +0 -1495
  178. package/docs/patterns/encryption/index.md +0 -6
  179. package/docs/patterns/external-data/index.md +0 -5
  180. package/docs/patterns/file-management/index.md +0 -11
  181. package/docs/patterns/file-structure/index.md +0 -14
  182. package/docs/patterns/list-ordering/index.md +0 -369
  183. package/docs/patterns/offline/index.md +0 -32
  184. package/docs/patterns/orm/index.md +0 -18
  185. package/docs/patterns/presence/index.md +0 -11
  186. package/docs/patterns/rich-text-editing/index.md +0 -11
  187. package/docs/patterns/server-side-clients/index.md +0 -97
  188. package/docs/patterns/side-effects/index.md +0 -11
  189. package/docs/patterns/state-machines/index.md +0 -11
  190. package/docs/patterns/storybook/index.md +0 -192
  191. package/docs/patterns/undo-redo/index.md +0 -9
  192. package/docs/patterns/version-control/index.md +0 -8
  193. package/docs/platform-adapters/cloudflare-durable-object-adapter/index.md +0 -453
  194. package/docs/platform-adapters/electron-adapter/index.md +0 -15
  195. package/docs/platform-adapters/expo-adapter/index.md +0 -245
  196. package/docs/platform-adapters/node-adapter/index.md +0 -160
  197. package/docs/platform-adapters/tauri-adapter/index.md +0 -15
  198. package/docs/platform-adapters/web-adapter/index.md +0 -218
  199. package/docs/sustainable-open-source/contributing/docs/index.md +0 -94
  200. package/docs/sustainable-open-source/contributing/info/index.md +0 -63
  201. package/docs/sustainable-open-source/contributing/monorepo/index.md +0 -195
  202. package/docs/sustainable-open-source/sponsoring/index.md +0 -104
  203. package/docs/sync-providers/cloudflare/index.md +0 -773
  204. package/docs/sync-providers/custom/index.md +0 -65
  205. package/docs/sync-providers/electricsql/index.md +0 -159
  206. package/docs/sync-providers/s2/index.md +0 -230
  207. package/docs/tutorial/0-welcome/index.md +0 -48
  208. package/docs/tutorial/1-setup-starter-project/index.md +0 -105
  209. package/docs/tutorial/2-deploy-to-cloudflare/index.md +0 -195
  210. package/docs/tutorial/3-read-and-write-todos-via-livestore/index.md +0 -511
  211. package/docs/tutorial/4-sync-data-via-cloudflare/index.md +0 -210
  212. package/docs/tutorial/5-expand-business-logic/index.md +0 -174
  213. package/docs/tutorial/6-persist-ui-state/index.md +0 -453
  214. package/docs/tutorial/7-next-steps/index.md +0 -22
  215. package/docs/understanding-livestore/design-decisions/index.md +0 -33
  216. package/docs/understanding-livestore/event-sourcing/index.md +0 -40
@@ -1,11 +1,13 @@
1
1
  import type { UnknownError } from '@livestore/common'
2
- import type { LiveStoreSchema } from '@livestore/common/schema'
2
+ import type { LiveStoreEvent, LiveStoreSchema } from '@livestore/common/schema'
3
3
  import { omitUndefineds } from '@livestore/utils'
4
4
  import type { Cause, OtelTracer, Scope } from '@livestore/utils/effect'
5
- import { Deferred, Duration, Effect, Layer, pipe } from '@livestore/utils/effect'
5
+ import { Context, Deferred, Duration, Effect, Layer, pipe } from '@livestore/utils/effect'
6
6
 
7
7
  import type { LiveStoreContextProps } from '../store/create-store.ts'
8
8
  import { createStore, DeferredStoreContext, LiveStoreContextRunning } from '../store/create-store.ts'
9
+ import type { LiveStoreContextRunning as LiveStoreContextRunningType, Queryable } from '../store/store-types.ts'
10
+ import type { Store as StoreClass } from '../store/store.ts'
9
11
 
10
12
  export const makeLiveStoreContext = <TSchema extends LiveStoreSchema, TContext = {}>({
11
13
  schema,
@@ -33,12 +35,6 @@ export const makeLiveStoreContext = <TSchema extends LiveStoreSchema, TContext =
33
35
  ...omitUndefineds({ context, boot, disableDevtools, onBootStatus, syncPayload, syncPayloadSchema }),
34
36
  })
35
37
 
36
- globalThis.__debugLiveStore ??= {}
37
- if (Object.keys(globalThis.__debugLiveStore).length === 0) {
38
- globalThis.__debugLiveStore._ = store
39
- }
40
- globalThis.__debugLiveStore[storeId] = store
41
-
42
38
  return { stage: 'running', store } as any as LiveStoreContextRunning['Type']
43
39
  }),
44
40
  Effect.tapErrorCause((cause) => Effect.flatMap(DeferredStoreContext, (def) => Deferred.failCause(def, cause))),
@@ -49,6 +45,19 @@ export const makeLiveStoreContext = <TSchema extends LiveStoreSchema, TContext =
49
45
  Effect.withSpan('@livestore/livestore/effect:makeLiveStoreContext'),
50
46
  )
51
47
 
48
+ /**
49
+ * @deprecated Use `Store.Tag(schema, storeId)` instead for type-safe store contexts.
50
+ *
51
+ * @example Migration
52
+ * ```ts
53
+ * // Before
54
+ * const layer = LiveStoreContextLayer({ schema, adapter, ... })
55
+ *
56
+ * // After
57
+ * class MainStore extends Store.Tag(schema, 'main') {}
58
+ * const layer = MainStore.layer({ adapter, ... })
59
+ * ```
60
+ */
52
61
  export const LiveStoreContextLayer = <TSchema extends LiveStoreSchema, TContext = {}>(
53
62
  props: LiveStoreContextProps<TSchema, TContext>,
54
63
  ): Layer.Layer<LiveStoreContextRunning, UnknownError | Cause.TimeoutException, OtelTracer.OtelTracer> =>
@@ -57,7 +66,371 @@ export const LiveStoreContextLayer = <TSchema extends LiveStoreSchema, TContext
57
66
  Layer.provide(LiveStoreContextDeferred),
58
67
  )
59
68
 
69
+ /**
70
+ * @deprecated Use `Store.Tag(schema, storeId)` and `MainStore.DeferredLayer` instead.
71
+ */
60
72
  export const LiveStoreContextDeferred = Layer.effect(
61
73
  DeferredStoreContext,
62
74
  Deferred.make<LiveStoreContextRunning['Type'], UnknownError>(),
63
75
  )
76
+
77
+ // =============================================================================
78
+ // Store.Tag - Idiomatic Effect API
79
+ // =============================================================================
80
+
81
+ /** Branded type for unique store context identity */
82
+ declare const StoreContextTypeId: unique symbol
83
+
84
+ /** Phantom type carrying schema and storeId information */
85
+ export interface StoreContextId<TSchema extends LiveStoreSchema, TStoreId extends string> {
86
+ readonly [StoreContextTypeId]: {
87
+ readonly schema: TSchema
88
+ readonly storeId: TStoreId
89
+ }
90
+ }
91
+
92
+ /** Phantom type for deferred store context */
93
+ declare const DeferredContextTypeId: unique symbol
94
+
95
+ export interface DeferredContextId<TStoreId extends string> {
96
+ readonly [DeferredContextTypeId]: {
97
+ readonly storeId: TStoreId
98
+ }
99
+ }
100
+
101
+ /** Props for creating a store layer (schema and storeId are already provided) */
102
+ export type StoreLayerProps<TSchema extends LiveStoreSchema, TContext = {}> = Omit<
103
+ LiveStoreContextProps<TSchema, TContext>,
104
+ 'storeId' | 'schema'
105
+ >
106
+
107
+ /**
108
+ * Type for a Store.Tag class. This is the return type of `Store.Tag(schema, storeId)`.
109
+ * Can be extended as a class and is yieldable in Effect.gen.
110
+ *
111
+ * Note: This uses a type alias with a new() signature to make it extendable.
112
+ */
113
+ /**
114
+ * Type for a Store.Tag class. Uses self-referential identifier so that `yield*`
115
+ * and method R channels resolve to the same type, enabling proper layer composition.
116
+ *
117
+ * Uses `interface` (not `type`) to allow the self-referential identifier without
118
+ * triggering TS2456 (circular type alias).
119
+ */
120
+ export interface StoreTagClass<TSchema extends LiveStoreSchema, TStoreId extends string>
121
+ extends Context.Tag<StoreTagClass<TSchema, TStoreId>, LiveStoreContextRunningType<TSchema>> {
122
+ /** Constructor signature (makes the type extendable as a class) */
123
+ new (): Context.Tag<StoreTagClass<TSchema, TStoreId>, LiveStoreContextRunningType<TSchema>>
124
+
125
+ /** Tag identity type (from Context.Tag) */
126
+ readonly Id: StoreTagClass<TSchema, TStoreId>
127
+
128
+ /** Service type (from Context.Tag) */
129
+ readonly Type: LiveStoreContextRunningType<TSchema>
130
+
131
+ /** The LiveStore schema for this store */
132
+ readonly schema: TSchema
133
+
134
+ /** Unique identifier for this store */
135
+ readonly storeId: TStoreId
136
+
137
+ /** Creates a layer that initializes the store */
138
+ layer<TContext>(
139
+ props: StoreLayerProps<TSchema, TContext>,
140
+ ): Layer.Layer<StoreTagClass<TSchema, TStoreId>, UnknownError | Cause.TimeoutException, OtelTracer.OtelTracer>
141
+
142
+ /** Deferred store tag for async initialization patterns */
143
+ readonly Deferred: Context.Tag<
144
+ DeferredContextId<TStoreId>,
145
+ Deferred.Deferred<LiveStoreContextRunningType<TSchema>, UnknownError>
146
+ >
147
+
148
+ /** Layer that provides the Deferred tag */
149
+ readonly DeferredLayer: Layer.Layer<DeferredContextId<TStoreId>>
150
+
151
+ /** Layer that waits for Deferred and provides the running store */
152
+ readonly fromDeferred: Layer.Layer<StoreTagClass<TSchema, TStoreId>, UnknownError, DeferredContextId<TStoreId>>
153
+
154
+ /** Query the store. Returns an Effect that yields the query result. */
155
+ query<TResult>(query: Queryable<TResult>): Effect.Effect<TResult, never, StoreTagClass<TSchema, TStoreId>>
156
+
157
+ /** Commit events to the store. */
158
+ commit(
159
+ ...eventInputs: LiveStoreEvent.Input.ForSchema<TSchema>[]
160
+ ): Effect.Effect<void, never, StoreTagClass<TSchema, TStoreId>>
161
+
162
+ /** Use the store with a callback function. */
163
+ use<A, E, R>(
164
+ f: (ctx: LiveStoreContextRunningType<TSchema>) => Effect.Effect<A, E, R>,
165
+ ): Effect.Effect<A, E, R | StoreTagClass<TSchema, TStoreId>>
166
+ }
167
+
168
+ /**
169
+ * Create a typed store context class for use with Effect.
170
+ *
171
+ * Returns a class that extends `Context.Tag`, making it directly yieldable in Effect code.
172
+ * The class includes static methods for creating layers and accessors for common operations.
173
+ *
174
+ * @param schema - The LiveStore schema (used for type inference and runtime)
175
+ * @param storeId - Unique identifier for this store
176
+ *
177
+ * @example Basic usage
178
+ * ```ts
179
+ * import { Store } from '@livestore/livestore/effect'
180
+ * import { schema } from './schema.ts'
181
+ *
182
+ * // Define your store (once per store)
183
+ * export class MainStore extends Store.Tag(schema, 'main') {}
184
+ *
185
+ * // Create the layer
186
+ * const storeLayer = MainStore.layer({
187
+ * adapter: myAdapter,
188
+ * batchUpdates: ReactDOM.unstable_batchedUpdates,
189
+ * })
190
+ *
191
+ * // Use in Effect code
192
+ * Effect.gen(function* () {
193
+ * const { store } = yield* MainStore
194
+ * // ^? Store<typeof schema> - fully typed!
195
+ *
196
+ * // Or use accessors
197
+ * const users = yield* MainStore.query(tables.users.all())
198
+ * yield* MainStore.commit(events.createUser({ id: '1', name: 'Alice' }))
199
+ * })
200
+ * ```
201
+ *
202
+ * @example Multiple stores
203
+ * ```ts
204
+ * class MainStore extends Store.Tag(mainSchema, 'main') {}
205
+ * class SettingsStore extends Store.Tag(settingsSchema, 'settings') {}
206
+ *
207
+ * // Both available in same Effect context
208
+ * Effect.gen(function* () {
209
+ * const main = yield* MainStore
210
+ * const settings = yield* SettingsStore
211
+ * })
212
+ *
213
+ * const layer = Layer.mergeAll(
214
+ * MainStore.layer({ adapter: mainAdapter }),
215
+ * SettingsStore.layer({ adapter: settingsAdapter }),
216
+ * )
217
+ * ```
218
+ */
219
+ const makeStoreTag = <TSchema extends LiveStoreSchema, TStoreId extends string>(
220
+ schema: TSchema,
221
+ storeId: TStoreId,
222
+ ): StoreTagClass<TSchema, TStoreId> => {
223
+ type RunningType = LiveStoreContextRunningType<TSchema>
224
+ type DeferredType = Deferred.Deferred<RunningType, UnknownError>
225
+
226
+ // Create the deferred tag and layers upfront
227
+ const _DeferredTag = Context.GenericTag<DeferredContextId<TStoreId>, DeferredType>(
228
+ `@livestore/store-deferred/${storeId}`,
229
+ )
230
+
231
+ const _DeferredLayer = Layer.effect(_DeferredTag, Deferred.make<RunningType, UnknownError>())
232
+
233
+ class Tag extends Context.Tag(`@livestore/store/${storeId}`)<Tag, RunningType>() {
234
+ static readonly schema: TSchema = schema
235
+ static readonly storeId: TStoreId = storeId
236
+
237
+ static layer<TContext = {}>(props: StoreLayerProps<TSchema, TContext>) {
238
+ return pipe(
239
+ Effect.gen(function* () {
240
+ const store = yield* createStore({
241
+ schema,
242
+ storeId,
243
+ adapter: props.adapter,
244
+ batchUpdates: props.batchUpdates,
245
+ ...omitUndefineds({
246
+ context: props.context,
247
+ boot: props.boot,
248
+ disableDevtools: props.disableDevtools,
249
+ onBootStatus: props.onBootStatus,
250
+ syncPayload: props.syncPayload,
251
+ syncPayloadSchema: props.syncPayloadSchema,
252
+ }),
253
+ })
254
+
255
+ const ctx: RunningType = { stage: 'running', store: store as StoreClass<TSchema> }
256
+
257
+ // Also fulfill the deferred if it exists in context
258
+ yield* Effect.flatMap(Effect.serviceOption(_DeferredTag), (optDeferred) =>
259
+ optDeferred._tag === 'Some' ? Deferred.succeed(optDeferred.value, ctx) : Effect.void,
260
+ )
261
+
262
+ return ctx
263
+ }),
264
+ Effect.timeout(Duration.minutes(5)),
265
+ Effect.withSpan(`@livestore/effect:Store.Tag:${storeId}`),
266
+ Layer.scoped(Tag),
267
+ Layer.withSpan(`LiveStore:${storeId}`),
268
+ Layer.provide(_DeferredLayer),
269
+ )
270
+ }
271
+
272
+ static readonly Deferred = _DeferredTag
273
+ static readonly DeferredLayer = _DeferredLayer
274
+
275
+ static readonly fromDeferred = pipe(
276
+ Effect.gen(function* () {
277
+ const deferred = yield* _DeferredTag
278
+ const ctx = yield* deferred
279
+ return Layer.succeed(Tag, ctx)
280
+ }),
281
+ Layer.unwrapScoped,
282
+ )
283
+
284
+ static query<TResult>(query: Queryable<TResult>) {
285
+ return Effect.map(Tag, ({ store }) => store.query(query))
286
+ }
287
+
288
+ static commit(...eventInputs: LiveStoreEvent.Input.ForSchema<TSchema>[]) {
289
+ return Effect.map(Tag, ({ store }) => {
290
+ store.commit(...eventInputs)
291
+ })
292
+ }
293
+
294
+ static use<A, E, R>(f: (ctx: RunningType) => Effect.Effect<A, E, R>) {
295
+ return Effect.flatMap(Tag, f)
296
+ }
297
+ }
298
+
299
+ return Tag as unknown as StoreTagClass<TSchema, TStoreId>
300
+ }
301
+
302
+ /**
303
+ * Store utilities for Effect integration.
304
+ *
305
+ * @example
306
+ * ```ts
307
+ * import { Store } from '@livestore/livestore/effect'
308
+ *
309
+ * export class MainStore extends Store.Tag(schema, 'main') {}
310
+ * ```
311
+ */
312
+ export const Store = {
313
+ /**
314
+ * Create a typed store context class for use with Effect.
315
+ * @see {@link makeStoreTag} for full documentation
316
+ */
317
+ Tag: makeStoreTag,
318
+ }
319
+
320
+ // =============================================================================
321
+ // Legacy API (deprecated)
322
+ // =============================================================================
323
+
324
+ /**
325
+ * @deprecated Use `Store.Tag(schema, storeId)` instead.
326
+ *
327
+ * @example Migration
328
+ * ```ts
329
+ * // Before
330
+ * const MainStoreContext = makeStoreContext<typeof schema>()('main')
331
+ * export const MainStore = MainStoreContext.Tag
332
+ * export const MainStoreLayer = MainStoreContext.Layer
333
+ *
334
+ * // After
335
+ * export class MainStore extends Store.Tag(schema, 'main') {}
336
+ * // MainStore.layer({ ... }) for the layer
337
+ * ```
338
+ */
339
+ export interface StoreContext<TSchema extends LiveStoreSchema, TStoreId extends string> {
340
+ readonly storeId: TStoreId
341
+ readonly Tag: Context.Tag<StoreContextId<TSchema, TStoreId>, LiveStoreContextRunningType<TSchema>>
342
+ readonly DeferredTag: Context.Tag<
343
+ DeferredContextId<TStoreId>,
344
+ Deferred.Deferred<LiveStoreContextRunningType<TSchema>, UnknownError>
345
+ >
346
+ readonly Layer: <TContext = {}>(
347
+ props: Omit<LiveStoreContextProps<TSchema, TContext>, 'storeId'>,
348
+ ) => Layer.Layer<StoreContextId<TSchema, TStoreId>, UnknownError | Cause.TimeoutException, OtelTracer.OtelTracer>
349
+ readonly DeferredLayer: Layer.Layer<DeferredContextId<TStoreId>>
350
+ readonly fromDeferred: Layer.Layer<StoreContextId<TSchema, TStoreId>, UnknownError, DeferredContextId<TStoreId>>
351
+ }
352
+
353
+ /**
354
+ * @deprecated Use `Store.Tag(schema, storeId)` instead.
355
+ *
356
+ * @example Migration
357
+ * ```ts
358
+ * // Before
359
+ * const MainStoreContext = makeStoreContext<typeof schema>()('main')
360
+ *
361
+ * // After
362
+ * class MainStore extends Store.Tag(schema, 'main') {}
363
+ * ```
364
+ */
365
+ export const makeStoreContext =
366
+ <TSchema extends LiveStoreSchema>() =>
367
+ <TStoreId extends string>(storeId: TStoreId): StoreContext<TSchema, TStoreId> => {
368
+ type RunningType = LiveStoreContextRunningType<TSchema>
369
+ type DeferredType = Deferred.Deferred<RunningType, UnknownError>
370
+
371
+ const Tag = Context.GenericTag<StoreContextId<TSchema, TStoreId>, RunningType>(`@livestore/store/${storeId}`)
372
+
373
+ const DeferredTag = Context.GenericTag<DeferredContextId<TStoreId>, DeferredType>(
374
+ `@livestore/store-deferred/${storeId}`,
375
+ )
376
+
377
+ const DeferredLayer = Layer.effect(DeferredTag, Deferred.make<RunningType, UnknownError>())
378
+
379
+ const makeLayer = <TContext = {}>(
380
+ props: Omit<LiveStoreContextProps<TSchema, TContext>, 'storeId'>,
381
+ ): Layer.Layer<StoreContextId<TSchema, TStoreId>, UnknownError | Cause.TimeoutException, OtelTracer.OtelTracer> =>
382
+ pipe(
383
+ Effect.gen(function* () {
384
+ const store = yield* createStore({
385
+ schema: props.schema,
386
+ storeId,
387
+ adapter: props.adapter,
388
+ batchUpdates: props.batchUpdates,
389
+ ...omitUndefineds({
390
+ context: props.context,
391
+ boot: props.boot,
392
+ disableDevtools: props.disableDevtools,
393
+ onBootStatus: props.onBootStatus,
394
+ syncPayload: props.syncPayload,
395
+ syncPayloadSchema: props.syncPayloadSchema,
396
+ }),
397
+ })
398
+
399
+ const ctx: RunningType = { stage: 'running', store: store as StoreClass<TSchema> }
400
+
401
+ // Also fulfill the deferred if it exists in context
402
+ yield* Effect.flatMap(Effect.serviceOption(DeferredTag), (optDeferred) =>
403
+ optDeferred._tag === 'Some' ? Deferred.succeed(optDeferred.value, ctx) : Effect.void,
404
+ )
405
+
406
+ return ctx
407
+ }),
408
+ Effect.timeout(Duration.minutes(5)),
409
+ Effect.withSpan(`@livestore/effect:makeStoreContext:${storeId}`),
410
+ Layer.scoped(Tag),
411
+ Layer.withSpan(`LiveStore:${storeId}`),
412
+ Layer.provide(DeferredLayer),
413
+ )
414
+
415
+ const fromDeferred: Layer.Layer<
416
+ StoreContextId<TSchema, TStoreId>,
417
+ UnknownError,
418
+ DeferredContextId<TStoreId>
419
+ > = pipe(
420
+ Effect.gen(function* () {
421
+ const deferred = yield* DeferredTag
422
+ const ctx = yield* deferred
423
+ return Layer.succeed(Tag, ctx)
424
+ }),
425
+ Layer.unwrapScoped,
426
+ )
427
+
428
+ return {
429
+ storeId,
430
+ Tag,
431
+ DeferredTag,
432
+ Layer: makeLayer,
433
+ DeferredLayer,
434
+ fromDeferred,
435
+ }
436
+ }
package/src/effect/mod.ts CHANGED
@@ -4,4 +4,16 @@ export {
4
4
  LiveStoreContextRunning as LiveStoreContext,
5
5
  LiveStoreContextRunning,
6
6
  } from '../store/create-store.ts'
7
- export { LiveStoreContextDeferred, LiveStoreContextLayer } from './LiveStore.ts'
7
+ // Store.Tag - Idiomatic Effect API
8
+ // Legacy API (deprecated)
9
+ export {
10
+ type DeferredContextId,
11
+ LiveStoreContextDeferred,
12
+ LiveStoreContextLayer,
13
+ makeStoreContext,
14
+ Store,
15
+ type StoreContext,
16
+ type StoreContextId,
17
+ type StoreLayerProps,
18
+ type StoreTagClass,
19
+ } from './LiveStore.ts'