@livestore/livestore 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (205) hide show
  1. package/README.md +108 -0
  2. package/dist/.tsbuildinfo +1 -0
  3. package/dist/LiveRiffleStore.d.ts +42 -0
  4. package/dist/LiveRiffleStore.d.ts.map +1 -0
  5. package/dist/LiveRiffleStore.js +36 -0
  6. package/dist/LiveRiffleStore.js.map +1 -0
  7. package/dist/QueryCache.d.ts +20 -0
  8. package/dist/QueryCache.d.ts.map +1 -0
  9. package/dist/QueryCache.js +71 -0
  10. package/dist/QueryCache.js.map +1 -0
  11. package/dist/__tests__/react/fixture.d.ts +141 -0
  12. package/dist/__tests__/react/fixture.d.ts.map +1 -0
  13. package/dist/__tests__/react/fixture.js +72 -0
  14. package/dist/__tests__/react/fixture.js.map +1 -0
  15. package/dist/__tests__/react/useLiveStoreComponent.test.d.ts +2 -0
  16. package/dist/__tests__/react/useLiveStoreComponent.test.d.ts.map +1 -0
  17. package/dist/__tests__/react/useLiveStoreComponent.test.js +78 -0
  18. package/dist/__tests__/react/useLiveStoreComponent.test.js.map +1 -0
  19. package/dist/__tests__/react/useRiffleComponent.test.d.ts +2 -0
  20. package/dist/__tests__/react/useRiffleComponent.test.d.ts.map +1 -0
  21. package/dist/__tests__/react/useRiffleComponent.test.js +78 -0
  22. package/dist/__tests__/react/useRiffleComponent.test.js.map +1 -0
  23. package/dist/__tests__/reactive.test.d.ts +2 -0
  24. package/dist/__tests__/reactive.test.d.ts.map +1 -0
  25. package/dist/__tests__/reactive.test.js +167 -0
  26. package/dist/__tests__/reactive.test.js.map +1 -0
  27. package/dist/backends/base.d.ts +13 -0
  28. package/dist/backends/base.d.ts.map +1 -0
  29. package/dist/backends/base.js +53 -0
  30. package/dist/backends/base.js.map +1 -0
  31. package/dist/backends/index.d.ts +41 -0
  32. package/dist/backends/index.d.ts.map +1 -0
  33. package/dist/backends/index.js +38 -0
  34. package/dist/backends/index.js.map +1 -0
  35. package/dist/backends/noop.d.ts +18 -0
  36. package/dist/backends/noop.d.ts.map +1 -0
  37. package/dist/backends/noop.js +21 -0
  38. package/dist/backends/noop.js.map +1 -0
  39. package/dist/backends/tauri.d.ts +24 -0
  40. package/dist/backends/tauri.d.ts.map +1 -0
  41. package/dist/backends/tauri.js +48 -0
  42. package/dist/backends/tauri.js.map +1 -0
  43. package/dist/backends/utils/idb.d.ts +10 -0
  44. package/dist/backends/utils/idb.d.ts.map +1 -0
  45. package/dist/backends/utils/idb.js +58 -0
  46. package/dist/backends/utils/idb.js.map +1 -0
  47. package/dist/backends/web-in-memory.d.ts +24 -0
  48. package/dist/backends/web-in-memory.d.ts.map +1 -0
  49. package/dist/backends/web-in-memory.js +46 -0
  50. package/dist/backends/web-in-memory.js.map +1 -0
  51. package/dist/backends/web-worker.d.ts +17 -0
  52. package/dist/backends/web-worker.d.ts.map +1 -0
  53. package/dist/backends/web-worker.js +139 -0
  54. package/dist/backends/web-worker.js.map +1 -0
  55. package/dist/backends/web.d.ts +28 -0
  56. package/dist/backends/web.d.ts.map +1 -0
  57. package/dist/backends/web.js +64 -0
  58. package/dist/backends/web.js.map +1 -0
  59. package/dist/bounded-collections.d.ts +34 -0
  60. package/dist/bounded-collections.d.ts.map +1 -0
  61. package/dist/bounded-collections.js +103 -0
  62. package/dist/bounded-collections.js.map +1 -0
  63. package/dist/componentKey.d.ts +20 -0
  64. package/dist/componentKey.d.ts.map +1 -0
  65. package/dist/componentKey.js +3 -0
  66. package/dist/componentKey.js.map +1 -0
  67. package/dist/effect/LiveStore.d.ts +42 -0
  68. package/dist/effect/LiveStore.d.ts.map +1 -0
  69. package/dist/effect/LiveStore.js +36 -0
  70. package/dist/effect/LiveStore.js.map +1 -0
  71. package/dist/effect/index.d.ts +2 -0
  72. package/dist/effect/index.d.ts.map +1 -0
  73. package/dist/effect/index.js +2 -0
  74. package/dist/effect/index.js.map +1 -0
  75. package/dist/events.d.ts +7 -0
  76. package/dist/events.d.ts.map +1 -0
  77. package/dist/events.js +2 -0
  78. package/dist/events.js.map +1 -0
  79. package/dist/inMemoryDatabase.d.ts +65 -0
  80. package/dist/inMemoryDatabase.d.ts.map +1 -0
  81. package/dist/inMemoryDatabase.js +241 -0
  82. package/dist/inMemoryDatabase.js.map +1 -0
  83. package/dist/index.d.ts +20 -0
  84. package/dist/index.d.ts.map +1 -0
  85. package/dist/index.js +10 -0
  86. package/dist/index.js.map +1 -0
  87. package/dist/otel.d.ts +5 -0
  88. package/dist/otel.d.ts.map +1 -0
  89. package/dist/otel.js +17 -0
  90. package/dist/otel.js.map +1 -0
  91. package/dist/react/LiveStoreContext.d.ts +11 -0
  92. package/dist/react/LiveStoreContext.d.ts.map +1 -0
  93. package/dist/react/LiveStoreContext.js +10 -0
  94. package/dist/react/LiveStoreContext.js.map +1 -0
  95. package/dist/react/LiveStoreProvider.d.ts +21 -0
  96. package/dist/react/LiveStoreProvider.d.ts.map +1 -0
  97. package/dist/react/LiveStoreProvider.js +48 -0
  98. package/dist/react/LiveStoreProvider.js.map +1 -0
  99. package/dist/react/RiffleProvider.d.ts +21 -0
  100. package/dist/react/RiffleProvider.d.ts.map +1 -0
  101. package/dist/react/RiffleProvider.js +48 -0
  102. package/dist/react/RiffleProvider.js.map +1 -0
  103. package/dist/react/StoreContext.d.ts +11 -0
  104. package/dist/react/StoreContext.d.ts.map +1 -0
  105. package/dist/react/StoreContext.js +10 -0
  106. package/dist/react/StoreContext.js.map +1 -0
  107. package/dist/react/index.d.ts +7 -0
  108. package/dist/react/index.d.ts.map +1 -0
  109. package/dist/react/index.js +6 -0
  110. package/dist/react/index.js.map +1 -0
  111. package/dist/react/useGlobalQuery.d.ts +3 -0
  112. package/dist/react/useGlobalQuery.d.ts.map +1 -0
  113. package/dist/react/useGlobalQuery.js +25 -0
  114. package/dist/react/useGlobalQuery.js.map +1 -0
  115. package/dist/react/useGraphQL.d.ts +11 -0
  116. package/dist/react/useGraphQL.d.ts.map +1 -0
  117. package/dist/react/useGraphQL.js +68 -0
  118. package/dist/react/useGraphQL.js.map +1 -0
  119. package/dist/react/useLiveStoreComponent.d.ts +70 -0
  120. package/dist/react/useLiveStoreComponent.d.ts.map +1 -0
  121. package/dist/react/useLiveStoreComponent.js +261 -0
  122. package/dist/react/useLiveStoreComponent.js.map +1 -0
  123. package/dist/react/useRiffleComponent.d.ts +70 -0
  124. package/dist/react/useRiffleComponent.d.ts.map +1 -0
  125. package/dist/react/useRiffleComponent.js +261 -0
  126. package/dist/react/useRiffleComponent.js.map +1 -0
  127. package/dist/react/useRiffleJsonHook.d.ts +4 -0
  128. package/dist/react/useRiffleJsonHook.d.ts.map +1 -0
  129. package/dist/react/useRiffleJsonHook.js +21 -0
  130. package/dist/react/useRiffleJsonHook.js.map +1 -0
  131. package/dist/react/utils/useStateRefWithReactiveInput.d.ts +13 -0
  132. package/dist/react/utils/useStateRefWithReactiveInput.d.ts.map +1 -0
  133. package/dist/react/utils/useStateRefWithReactiveInput.js +38 -0
  134. package/dist/react/utils/useStateRefWithReactiveInput.js.map +1 -0
  135. package/dist/reactive.d.ts +140 -0
  136. package/dist/reactive.d.ts.map +1 -0
  137. package/dist/reactive.js +301 -0
  138. package/dist/reactive.js.map +1 -0
  139. package/dist/reactiveQueries/base-class.d.ts +24 -0
  140. package/dist/reactiveQueries/base-class.d.ts.map +1 -0
  141. package/dist/reactiveQueries/base-class.js +22 -0
  142. package/dist/reactiveQueries/base-class.js.map +1 -0
  143. package/dist/reactiveQueries/graphql.d.ts +25 -0
  144. package/dist/reactiveQueries/graphql.d.ts.map +1 -0
  145. package/dist/reactiveQueries/graphql.js +14 -0
  146. package/dist/reactiveQueries/graphql.js.map +1 -0
  147. package/dist/reactiveQueries/js.d.ts +19 -0
  148. package/dist/reactiveQueries/js.d.ts.map +1 -0
  149. package/dist/reactiveQueries/js.js +13 -0
  150. package/dist/reactiveQueries/js.js.map +1 -0
  151. package/dist/reactiveQueries/sql.d.ts +31 -0
  152. package/dist/reactiveQueries/sql.d.ts.map +1 -0
  153. package/dist/reactiveQueries/sql.js +28 -0
  154. package/dist/reactiveQueries/sql.js.map +1 -0
  155. package/dist/schema.d.ts +163 -0
  156. package/dist/schema.d.ts.map +1 -0
  157. package/dist/schema.js +92 -0
  158. package/dist/schema.js.map +1 -0
  159. package/dist/store.d.ts +175 -0
  160. package/dist/store.d.ts.map +1 -0
  161. package/dist/store.js +546 -0
  162. package/dist/store.js.map +1 -0
  163. package/dist/util.d.ts +24 -0
  164. package/dist/util.d.ts.map +1 -0
  165. package/dist/util.js +51 -0
  166. package/dist/util.js.map +1 -0
  167. package/package.json +52 -0
  168. package/src/QueryCache.ts +81 -0
  169. package/src/__tests__/react/fixture.tsx +106 -0
  170. package/src/__tests__/react/useLiveStoreComponent.test.tsx +111 -0
  171. package/src/__tests__/reactive.test.ts +227 -0
  172. package/src/ambient.d.ts +7 -0
  173. package/src/backends/base.ts +67 -0
  174. package/src/backends/index.ts +94 -0
  175. package/src/backends/noop.ts +32 -0
  176. package/src/backends/tauri.ts +74 -0
  177. package/src/backends/utils/idb.ts +71 -0
  178. package/src/backends/web-in-memory.ts +65 -0
  179. package/src/backends/web-worker.ts +176 -0
  180. package/src/backends/web.ts +96 -0
  181. package/src/bounded-collections.ts +112 -0
  182. package/src/componentKey.ts +9 -0
  183. package/src/effect/LiveStore.ts +123 -0
  184. package/src/effect/index.ts +7 -0
  185. package/src/events.ts +8 -0
  186. package/src/inMemoryDatabase.ts +347 -0
  187. package/src/index.ts +47 -0
  188. package/src/otel.ts +20 -0
  189. package/src/react/LiveStoreContext.ts +23 -0
  190. package/src/react/LiveStoreProvider.tsx +93 -0
  191. package/src/react/index.ts +11 -0
  192. package/src/react/useGlobalQuery.ts +40 -0
  193. package/src/react/useGraphQL.ts +113 -0
  194. package/src/react/useLiveStoreComponent.ts +493 -0
  195. package/src/react/utils/useStateRefWithReactiveInput.ts +51 -0
  196. package/src/reactive.ts +538 -0
  197. package/src/reactiveQueries/base-class.ts +49 -0
  198. package/src/reactiveQueries/graphql.ts +52 -0
  199. package/src/reactiveQueries/js.ts +38 -0
  200. package/src/reactiveQueries/sql.ts +65 -0
  201. package/src/schema.ts +219 -0
  202. package/src/store.ts +889 -0
  203. package/src/util.ts +59 -0
  204. package/tsconfig.json +15 -0
  205. package/vitest.config.js +13 -0
@@ -0,0 +1,93 @@
1
+ import type * as otel from '@opentelemetry/api'
2
+ import { mapValues } from 'lodash-es'
3
+ import type { ReactElement, ReactNode } from 'react'
4
+ import React from 'react'
5
+
6
+ import type { Backend, BackendOptions } from '../backends/index.js'
7
+ import type {
8
+ GlobalQueryDefs,
9
+ LiveStoreContext as StoreContext_,
10
+ LiveStoreCreateStoreOptions,
11
+ } from '../effect/LiveStore.js'
12
+ import type { Schema } from '../schema.js'
13
+ import type { BaseGraphQLContext, GraphQLOptions } from '../store.js'
14
+ import { createStore } from '../store.js'
15
+ import { LiveStoreContext } from './LiveStoreContext.js'
16
+
17
+ interface LiveStoreProviderProps<GraphQLContext> {
18
+ schema: Schema
19
+ backendOptions: BackendOptions
20
+ boot?: (backend: Backend, parentSpan: otel.Span) => Promise<void>
21
+ globalQueryDefs: GlobalQueryDefs
22
+ graphQLOptions?: GraphQLOptions<GraphQLContext>
23
+ otelTracer?: otel.Tracer
24
+ otelRootSpanContext?: otel.Context
25
+ fallback: ReactElement
26
+ }
27
+
28
+ export const LiveStoreProvider = <GraphQLContext extends BaseGraphQLContext>({
29
+ fallback,
30
+ globalQueryDefs,
31
+ backendOptions,
32
+ graphQLOptions,
33
+ otelTracer,
34
+ otelRootSpanContext,
35
+ children,
36
+ schema,
37
+ boot,
38
+ }: LiveStoreProviderProps<GraphQLContext> & { children?: ReactNode }): JSX.Element => {
39
+ const store = useCreateStore({
40
+ schema,
41
+ globalQueryDefs,
42
+ backendOptions,
43
+ graphQLOptions,
44
+ otelTracer,
45
+ otelRootSpanContext,
46
+ boot,
47
+ })
48
+
49
+ if (store === undefined) {
50
+ return fallback
51
+ }
52
+
53
+ return <LiveStoreContext.Provider value={store}>{children}</LiveStoreContext.Provider>
54
+ }
55
+
56
+ const useCreateStore = <GraphQLContext extends BaseGraphQLContext>({
57
+ schema,
58
+ globalQueryDefs,
59
+ backendOptions,
60
+ graphQLOptions,
61
+ otelTracer,
62
+ otelRootSpanContext,
63
+ boot,
64
+ }: LiveStoreCreateStoreOptions<GraphQLContext>) => {
65
+ const [ctxValue, setCtxValue] = React.useState<StoreContext_ | undefined>()
66
+
67
+ React.useEffect(() => {
68
+ void (async () => {
69
+ try {
70
+ const store = await createStore({
71
+ schema,
72
+ backendOptions,
73
+ graphQLOptions,
74
+ otelTracer,
75
+ otelRootSpanContext,
76
+ boot,
77
+ })
78
+ store.otel.tracer.startActiveSpan('LiveStore:makeGlobalQueries', {}, store.otel.queriesSpanContext, (span) => {
79
+ const globalQueries = mapValues(globalQueryDefs, (queryDef) => queryDef(store))
80
+ setCtxValue({ store, globalQueries })
81
+ span.end()
82
+ })
83
+ } catch (e) {
84
+ console.error(`Error creating LiveStore store:`, e)
85
+ throw e
86
+ }
87
+ })()
88
+
89
+ // TODO: do we need to return any cleanup function here?
90
+ }, [schema, backendOptions, globalQueryDefs, graphQLOptions, otelTracer, otelRootSpanContext, boot])
91
+
92
+ return ctxValue
93
+ }
@@ -0,0 +1,11 @@
1
+ export type {
2
+ UseLiveStoreComponentProps as LiveStoreComponentConfig,
3
+ ReactiveGraphQL,
4
+ ReactiveSQL,
5
+ Setters,
6
+ } from './useLiveStoreComponent.js'
7
+ export { LiveStoreContext, useStore } from './LiveStoreContext.js'
8
+ export { LiveStoreProvider } from './LiveStoreProvider.js'
9
+ export { useLiveStoreComponent } from './useLiveStoreComponent.js'
10
+ export { useGraphQL } from './useGraphQL.js'
11
+ export { useGlobalQuery } from './useGlobalQuery.js'
@@ -0,0 +1,40 @@
1
+ import { useEffect, useState } from 'react'
2
+
3
+ import { labelForKey } from '../componentKey.js'
4
+ import { TODO_REMOVE_trackLongRunningSpan } from '../otel.js'
5
+ import type { LiveStoreQuery, QueryResult } from '../store.js'
6
+
7
+ export const useGlobalQuery = <Q extends LiveStoreQuery>(query: Q): QueryResult<Q> => {
8
+ // We know the query has a result by the time we use it; so we can synchronously populate a default state
9
+ const [value, setValue] = useState<QueryResult<Q>>(query.results$.result)
10
+
11
+ // Subscribe to future updates for this query
12
+ useEffect(() => {
13
+ return query.store.otel.tracer.startActiveSpan(
14
+ `LiveStore:useGlobalQuery:${labelForKey(query.componentKey)}:${query.label}`,
15
+ {},
16
+ query.store.otel.queriesSpanContext,
17
+ (span) => {
18
+ TODO_REMOVE_trackLongRunningSpan(span)
19
+
20
+ const cancel = query.store.subscribe(
21
+ query,
22
+ (v) => {
23
+ // NOTE: we return a reference to the result object within LiveStore;
24
+ // this implies that app code must not mutate the results, or else
25
+ // there may be weird reactivity bugs.
26
+ return setValue(v)
27
+ },
28
+ undefined,
29
+ { label: query.label },
30
+ )
31
+ return () => {
32
+ cancel()
33
+ span.end()
34
+ }
35
+ },
36
+ )
37
+ }, [query])
38
+
39
+ return value
40
+ }
@@ -0,0 +1,113 @@
1
+ import type { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core'
2
+ import * as otel from '@opentelemetry/api'
3
+ import { isEqual } from 'lodash-es'
4
+ import React from 'react'
5
+
6
+ import { labelForKey } from '../componentKey.js'
7
+ import { useStore } from './LiveStoreContext.js'
8
+ import { type ComponentKeyConfig, useComponentKey } from './useLiveStoreComponent.js'
9
+ import { useStateRefWithReactiveInput } from './utils/useStateRefWithReactiveInput.js'
10
+
11
+ export type UseLiveStoreComponentProps<TResult extends Record<string, any>, TVariables extends Record<string, any>> = {
12
+ query: DocumentNode<TResult, TVariables>
13
+ variables: TVariables
14
+ componentKey: ComponentKeyConfig
15
+ reactDeps?: React.DependencyList
16
+ }
17
+
18
+ /**
19
+ * This is needed because the `React.useMemo` call below, can sometimes be called multiple times 🤷,
20
+ * so we need to "cache" the fact that we've already started a span for this component.
21
+ * The map entry is being removed again in the `React.useEffect` call below.
22
+ */
23
+ const spanAlreadyStartedCache = new Map<string, { span: otel.Span; otelCtx: otel.Context }>()
24
+
25
+ // TODO 1) figure out a way to make `variables` optional if the query doesn't have any variables (probably requires positional args)
26
+ // TODO 2) allow `.pipe` on the resulting query (possibly as a separate optional prop)
27
+ export const useGraphQL = <TResult extends Record<string, any>, TVariables extends Record<string, any> = {}>({
28
+ query,
29
+ variables,
30
+ componentKey: componentKeyConfig,
31
+ reactDeps = [],
32
+ }: UseLiveStoreComponentProps<TResult, TVariables>): Readonly<TResult> => {
33
+ const componentKey = useComponentKey(componentKeyConfig, reactDeps)
34
+ const { store } = useStore()
35
+
36
+ const componentKeyLabel = React.useMemo(() => labelForKey(componentKey), [componentKey])
37
+
38
+ // The following `React.useMemo` and `React.useEffect` calls are used to start and end a span for the lifetime of this component.
39
+ const { span, otelCtx } = React.useMemo(() => {
40
+ const existingSpan = spanAlreadyStartedCache.get(componentKeyLabel)
41
+ if (existingSpan !== undefined) return existingSpan
42
+
43
+ const span = store.otel.tracer.startSpan(
44
+ `LiveStore:useGraphQL:${componentKeyLabel}`,
45
+ {},
46
+ store.otel.queriesSpanContext,
47
+ )
48
+
49
+ const otelCtx = otel.trace.setSpan(otel.context.active(), span)
50
+
51
+ spanAlreadyStartedCache.set(componentKeyLabel, { span, otelCtx })
52
+
53
+ return { span, otelCtx }
54
+ }, [componentKeyLabel, store.otel.queriesSpanContext, store.otel.tracer])
55
+
56
+ React.useEffect(
57
+ () => () => {
58
+ spanAlreadyStartedCache.delete(componentKeyLabel)
59
+ span.end()
60
+ },
61
+ [componentKeyLabel, span],
62
+ )
63
+
64
+ const makeLiveStoreQuery = React.useCallback(
65
+ () => store.queryGraphQL(query, () => variables ?? ({} as TVariables), { componentKey }, otelCtx),
66
+ // NOTE: we don't include the queries function passed in by the user here;
67
+ // the reason is that we don't want to force them to memoize that function.
68
+ // Instead, we just assume that the function always has the same contents.
69
+ // This makes sense for LiveStore because the component config should be static.
70
+ // TODO: document this and consider whether it's the right API surface.
71
+ // eslint-disable-next-line react-hooks/exhaustive-deps
72
+ [componentKey, store],
73
+ )
74
+
75
+ // TODO get rid of the temporary query workaround
76
+ const initialQueryResults = React.useMemo(
77
+ () => store.inTempQueryContext(() => makeLiveStoreQuery().results$.result),
78
+ [makeLiveStoreQuery, store],
79
+ )
80
+
81
+ const [queryResultsRef, setQueryResults_] = useStateRefWithReactiveInput<TResult>(initialQueryResults)
82
+
83
+ React.useEffect(() => {
84
+ const liveStoreQuery = makeLiveStoreQuery()
85
+ const unsubscribe = store.subscribe(
86
+ liveStoreQuery,
87
+ (results) => {
88
+ if (isEqual(results, queryResultsRef.current) === false) {
89
+ setQueryResults_(results)
90
+ }
91
+ },
92
+ undefined,
93
+ { label: `useGraphQL:query:subscribe:${liveStoreQuery.label}` },
94
+ )
95
+
96
+ return () => {
97
+ unsubscribe()
98
+ }
99
+ // NOTE `setQueryResults_` from the deps array as it seems to cause an infinite loop
100
+ // This should probably be improved
101
+ // eslint-disable-next-line react-hooks/exhaustive-deps
102
+ }, [
103
+ otelCtx,
104
+ makeLiveStoreQuery,
105
+ // setQueryResults_,
106
+ store,
107
+ ])
108
+
109
+ // Very important: remove any queries / other resources associated w/ this component
110
+ React.useEffect(() => () => store.unmountComponent(componentKey), [store, componentKey])
111
+
112
+ return queryResultsRef.current
113
+ }