@livestore/livestore 0.0.12 → 0.0.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -7
- package/dist/.tsbuildinfo +1 -0
- package/dist/QueryCache.d.ts +20 -0
- package/dist/QueryCache.d.ts.map +1 -0
- package/dist/QueryCache.js +71 -0
- package/dist/QueryCache.js.map +1 -0
- package/dist/__tests__/react/fixture.d.ts +25 -0
- package/dist/__tests__/react/fixture.d.ts.map +1 -0
- package/dist/__tests__/react/fixture.js +60 -0
- package/dist/__tests__/react/fixture.js.map +1 -0
- package/dist/__tests__/react/useLiveStoreComponent.test.d.ts +2 -0
- package/dist/__tests__/react/useLiveStoreComponent.test.d.ts.map +1 -0
- package/dist/__tests__/react/useLiveStoreComponent.test.js +78 -0
- package/dist/__tests__/react/useLiveStoreComponent.test.js.map +1 -0
- package/dist/__tests__/reactive.test.d.ts +2 -0
- package/dist/__tests__/reactive.test.d.ts.map +1 -0
- package/dist/__tests__/reactive.test.js +197 -0
- package/dist/__tests__/reactive.test.js.map +1 -0
- package/dist/bounded-collections.d.ts +34 -0
- package/dist/bounded-collections.d.ts.map +1 -0
- package/dist/bounded-collections.js +103 -0
- package/dist/bounded-collections.js.map +1 -0
- package/dist/componentKey.d.ts +20 -0
- package/dist/componentKey.d.ts.map +1 -0
- package/dist/componentKey.js +3 -0
- package/dist/componentKey.js.map +1 -0
- package/dist/effect/LiveStore.d.ts +36 -0
- package/dist/effect/LiveStore.d.ts.map +1 -0
- package/dist/effect/LiveStore.js +41 -0
- package/dist/effect/LiveStore.js.map +1 -0
- package/dist/effect/index.d.ts +2 -0
- package/dist/effect/index.d.ts.map +1 -0
- package/dist/effect/index.js +2 -0
- package/dist/effect/index.js.map +1 -0
- package/dist/events.d.ts +7 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +2 -0
- package/dist/events.js.map +1 -0
- package/dist/inMemoryDatabase.d.ts +56 -0
- package/dist/inMemoryDatabase.d.ts.map +1 -0
- package/dist/inMemoryDatabase.js +223 -0
- package/dist/inMemoryDatabase.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/migrations.d.ts +16 -0
- package/dist/migrations.d.ts.map +1 -0
- package/dist/migrations.js +67 -0
- package/dist/migrations.js.map +1 -0
- package/dist/otel.d.ts +4 -0
- package/dist/otel.d.ts.map +1 -0
- package/dist/otel.js +6 -0
- package/dist/otel.js.map +1 -0
- package/dist/react/LiveStoreContext.d.ts +11 -0
- package/dist/react/LiveStoreContext.d.ts.map +1 -0
- package/dist/react/LiveStoreContext.js +10 -0
- package/dist/react/LiveStoreContext.js.map +1 -0
- package/dist/react/LiveStoreProvider.d.ts +20 -0
- package/dist/react/LiveStoreProvider.d.ts.map +1 -0
- package/dist/react/LiveStoreProvider.js +52 -0
- package/dist/react/LiveStoreProvider.js.map +1 -0
- package/dist/react/index.d.ts +8 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +6 -0
- package/dist/react/index.js.map +1 -0
- package/dist/react/useGraphQL.d.ts +13 -0
- package/dist/react/useGraphQL.d.ts.map +1 -0
- package/dist/react/useGraphQL.js +85 -0
- package/dist/react/useGraphQL.js.map +1 -0
- package/dist/react/useLiveStoreComponent.d.ts +75 -0
- package/dist/react/useLiveStoreComponent.d.ts.map +1 -0
- package/dist/react/useLiveStoreComponent.js +317 -0
- package/dist/react/useLiveStoreComponent.js.map +1 -0
- package/dist/react/useQuery.d.ts +3 -0
- package/dist/react/useQuery.d.ts.map +1 -0
- package/dist/react/useQuery.js +38 -0
- package/dist/react/useQuery.js.map +1 -0
- package/dist/react/utils/useStateRefWithReactiveInput.d.ts +13 -0
- package/dist/react/utils/useStateRefWithReactiveInput.d.ts.map +1 -0
- package/dist/react/utils/useStateRefWithReactiveInput.js +38 -0
- package/dist/react/utils/useStateRefWithReactiveInput.js.map +1 -0
- package/dist/reactive.d.ts +140 -0
- package/dist/reactive.d.ts.map +1 -0
- package/dist/reactive.js +302 -0
- package/dist/reactive.js.map +1 -0
- package/dist/reactiveQueries/base-class.d.ts +27 -0
- package/dist/reactiveQueries/base-class.d.ts.map +1 -0
- package/dist/reactiveQueries/base-class.js +23 -0
- package/dist/reactiveQueries/base-class.js.map +1 -0
- package/dist/reactiveQueries/graphql.d.ts +25 -0
- package/dist/reactiveQueries/graphql.d.ts.map +1 -0
- package/dist/reactiveQueries/graphql.js +18 -0
- package/dist/reactiveQueries/graphql.js.map +1 -0
- package/dist/reactiveQueries/js.d.ts +19 -0
- package/dist/reactiveQueries/js.d.ts.map +1 -0
- package/dist/reactiveQueries/js.js +13 -0
- package/dist/reactiveQueries/js.js.map +1 -0
- package/dist/reactiveQueries/sql.d.ts +31 -0
- package/dist/reactiveQueries/sql.d.ts.map +1 -0
- package/dist/reactiveQueries/sql.js +32 -0
- package/dist/reactiveQueries/sql.js.map +1 -0
- package/dist/schema.d.ts +81 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +46 -0
- package/dist/schema.js.map +1 -0
- package/dist/storage/in-memory/index.d.ts +15 -0
- package/dist/storage/in-memory/index.d.ts.map +1 -0
- package/dist/storage/in-memory/index.js +14 -0
- package/dist/storage/in-memory/index.js.map +1 -0
- package/dist/storage/index.d.ts +14 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +9 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/tauri/index.d.ts +19 -0
- package/dist/storage/tauri/index.d.ts.map +1 -0
- package/dist/storage/tauri/index.js +38 -0
- package/dist/storage/tauri/index.js.map +1 -0
- package/dist/storage/utils/idb.d.ts +10 -0
- package/dist/storage/utils/idb.d.ts.map +1 -0
- package/dist/storage/utils/idb.js +58 -0
- package/dist/storage/utils/idb.js.map +1 -0
- package/dist/storage/web-worker/index.d.ts +27 -0
- package/dist/storage/web-worker/index.d.ts.map +1 -0
- package/dist/storage/web-worker/index.js +74 -0
- package/dist/storage/web-worker/index.js.map +1 -0
- package/dist/storage/web-worker/worker.d.ts +13 -0
- package/dist/storage/web-worker/worker.d.ts.map +1 -0
- package/dist/storage/web-worker/worker.js +110 -0
- package/dist/storage/web-worker/worker.js.map +1 -0
- package/dist/store.d.ts +199 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +603 -0
- package/dist/store.js.map +1 -0
- package/dist/util.d.ts +28 -0
- package/dist/util.d.ts.map +1 -0
- package/dist/util.js +55 -0
- package/dist/util.js.map +1 -0
- package/package.json +46 -19
- package/src/__tests__/react/fixture.tsx +23 -32
- package/src/__tests__/reactive.test.ts +3 -4
- package/src/effect/LiveStore.ts +22 -31
- package/src/events.ts +1 -1
- package/src/inMemoryDatabase.ts +115 -140
- package/src/index.ts +20 -20
- package/src/migrations.ts +119 -0
- package/src/otel.ts +0 -11
- package/src/react/LiveStoreProvider.tsx +24 -23
- package/src/react/index.ts +10 -1
- package/src/react/useGraphQL.ts +28 -2
- package/src/react/useLiveStoreComponent.ts +134 -50
- package/src/react/useQuery.ts +56 -0
- package/src/reactive.ts +6 -4
- package/src/reactiveQueries/base-class.ts +9 -3
- package/src/reactiveQueries/graphql.ts +4 -4
- package/src/reactiveQueries/js.ts +2 -2
- package/src/reactiveQueries/sql.ts +6 -6
- package/src/schema.ts +69 -145
- package/src/storage/in-memory/index.ts +21 -0
- package/src/storage/index.ts +27 -0
- package/src/{backends/tauri.ts → storage/tauri/index.ts} +14 -28
- package/src/storage/web-worker/index.ts +116 -0
- package/src/{backends/web-worker.ts → storage/web-worker/worker.ts} +17 -52
- package/src/store.ts +171 -98
- package/src/util.ts +13 -3
- package/tsconfig.json +1 -3
- package/src/backends/base.ts +0 -67
- package/src/backends/index.ts +0 -98
- package/src/backends/noop.ts +0 -32
- package/src/backends/web-in-memory.ts +0 -65
- package/src/backends/web.ts +0 -97
- package/src/react/useGlobalQuery.ts +0 -40
- /package/src/{backends → storage}/utils/idb.ts +0 -0
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
import { omit, shouldNeverHappen } from '@livestore/utils';
|
|
2
|
+
import { Schema } from '@livestore/utils/effect';
|
|
3
|
+
import * as otel from '@opentelemetry/api';
|
|
4
|
+
import { SqliteAst, SqliteDsl } from 'effect-db-schema';
|
|
5
|
+
import { isEqual, mapValues } from 'lodash-es';
|
|
6
|
+
import React from 'react';
|
|
7
|
+
import { v4 as uuid } from 'uuid';
|
|
8
|
+
import { labelForKey, tableNameForComponentKey } from '../componentKey.js';
|
|
9
|
+
import { migrateTable } from '../migrations.js';
|
|
10
|
+
import { SCHEMA_META_TABLE } from '../schema.js';
|
|
11
|
+
import { sql } from '../util.js';
|
|
12
|
+
import { useStore } from './LiveStoreContext.js';
|
|
13
|
+
import { useStateRefWithReactiveInput } from './utils/useStateRefWithReactiveInput.js';
|
|
14
|
+
// type ComponentState = {
|
|
15
|
+
// /** Equivalent to `componentKey.key` */
|
|
16
|
+
// id: string
|
|
17
|
+
// [key: string]: string | number | boolean | null
|
|
18
|
+
// }
|
|
19
|
+
/**
|
|
20
|
+
* This is needed because the `React.useMemo` call below, can sometimes be called multiple times 🤷,
|
|
21
|
+
* so we need to "cache" the fact that we've already started a span for this component.
|
|
22
|
+
* The map entry is being removed again in the `React.useEffect` call below.
|
|
23
|
+
*/
|
|
24
|
+
const spanAlreadyStartedCache = new Map();
|
|
25
|
+
/**
|
|
26
|
+
* Create reactive queries within a component.
|
|
27
|
+
* @param config.queries A function that returns a map of named reactive queries.
|
|
28
|
+
* @param config.componentKey A function that returns a unique key for this component.
|
|
29
|
+
* @param config.reactDeps A list of React-level dependencies that will refresh the queries.
|
|
30
|
+
*/
|
|
31
|
+
export const useLiveStoreComponent = ({ stateSchema: stateSchema_, queries = () => ({}), componentKey: componentKeyConfig, reactDeps = [], }) => {
|
|
32
|
+
// TODO validate schema to make sure each column has a default value
|
|
33
|
+
// TODO we should clean up the state schema handling to remove this special handling for the `id` column
|
|
34
|
+
const stateSchema = React.useMemo(() => (stateSchema_ ? { ...stateSchema_, columns: omit(stateSchema_.columns, 'id') } : undefined), [stateSchema_]);
|
|
35
|
+
// performance.mark('useLiveStoreComponent:start')
|
|
36
|
+
const componentKey = useComponentKey(componentKeyConfig, reactDeps);
|
|
37
|
+
const { store } = useStore();
|
|
38
|
+
const componentKeyLabel = React.useMemo(() => labelForKey(componentKey), [componentKey]);
|
|
39
|
+
// The following `React.useMemo` and `React.useEffect` calls are used to start and end a span for the lifetime of this component.
|
|
40
|
+
const { span, otelContext } = React.useMemo(() => {
|
|
41
|
+
const existingSpan = spanAlreadyStartedCache.get(componentKeyLabel);
|
|
42
|
+
if (existingSpan !== undefined)
|
|
43
|
+
return existingSpan;
|
|
44
|
+
const span = store.otel.tracer.startSpan(`LiveStore:useLiveStoreComponent:${componentKeyLabel}`, {}, store.otel.queriesSpanContext);
|
|
45
|
+
const otelContext = otel.trace.setSpan(otel.context.active(), span);
|
|
46
|
+
spanAlreadyStartedCache.set(componentKeyLabel, { span, otelContext });
|
|
47
|
+
return { span, otelContext };
|
|
48
|
+
}, [componentKeyLabel, store.otel.queriesSpanContext, store.otel.tracer]);
|
|
49
|
+
React.useEffect(() => () => {
|
|
50
|
+
spanAlreadyStartedCache.delete(componentKeyLabel);
|
|
51
|
+
span.end();
|
|
52
|
+
}, [componentKeyLabel, span]);
|
|
53
|
+
const generateQueries = React.useCallback(({ state$, otelContext, registerSubscription, isTemporaryQuery, }) => queries({
|
|
54
|
+
rxSQL: (genQuery, queriedTables, bindValues) => store.querySQL(genQuery, { queriedTables, bindValues, otelContext, componentKey }),
|
|
55
|
+
rxGraphQL: (query, genVariableValues, label) => store.queryGraphQL(query, genVariableValues, { componentKey, label, otelContext }),
|
|
56
|
+
rxJS: (genQuery) => store.queryJS(genQuery, { componentKey, otelContext }),
|
|
57
|
+
state$,
|
|
58
|
+
subscribe: registerSubscription,
|
|
59
|
+
isTemporaryQuery,
|
|
60
|
+
}),
|
|
61
|
+
// NOTE: we don't include the queries function passed in by the user here;
|
|
62
|
+
// the reason is that we don't want to force them to memoize that function.
|
|
63
|
+
// Instead, we just assume that the function always has the same contents.
|
|
64
|
+
// This makes sense for LiveStore because the component config should be static.
|
|
65
|
+
// TODO: document this and consider whether it's the right API surface.
|
|
66
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
67
|
+
[store, componentKey]);
|
|
68
|
+
const defaultComponentState = React.useMemo(() => {
|
|
69
|
+
const defaultState = (stateSchema === undefined ? {} : mapValues(stateSchema.columns, (c) => c.default));
|
|
70
|
+
// @ts-expect-error TODO fix typing
|
|
71
|
+
defaultState.id = componentKeyConfig.id;
|
|
72
|
+
return defaultState;
|
|
73
|
+
}, [componentKeyConfig.id, stateSchema]);
|
|
74
|
+
const componentStateEffectSchema = React.useMemo(() => (stateSchema ? SqliteDsl.structSchemaForTable(stateSchema) : Schema.any), [stateSchema]);
|
|
75
|
+
// Step 1:
|
|
76
|
+
// Synchronously create state and queries for initial render pass.
|
|
77
|
+
// We do this in a temporary query context which cleans up after itself, making it idempotent
|
|
78
|
+
// TODO get rid of the temporary query workaround
|
|
79
|
+
const { initialComponentState, initialQueryResults } = React.useMemo(() => {
|
|
80
|
+
return store.otel.tracer.startActiveSpan('LiveStore:useLiveStoreComponent:initial', {}, otelContext, (span) => {
|
|
81
|
+
const otelContext = otel.trace.setSpan(otel.context.active(), span);
|
|
82
|
+
// NOTE `inTempQueryContext` automatically destroys the queries once the callback is done
|
|
83
|
+
return store.inTempQueryContext(() => {
|
|
84
|
+
try {
|
|
85
|
+
// create state query
|
|
86
|
+
let state$;
|
|
87
|
+
if (stateSchema === undefined) {
|
|
88
|
+
// TODO don't set up a query if there's no state schema (keeps the graph more clean)
|
|
89
|
+
state$ = store.queryJS(() => ({}), {
|
|
90
|
+
componentKey,
|
|
91
|
+
otelContext,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
const componentTableName = tableNameForComponentKey(componentKey);
|
|
96
|
+
const whereClause = componentKey._tag === 'singleton' ? '' : `where id = '${componentKey.id}'`;
|
|
97
|
+
// TODO find a better solution for this
|
|
98
|
+
if (store.tableRefs[componentTableName] === undefined) {
|
|
99
|
+
const schemaHash = SqliteAst.hash(stateSchema.ast);
|
|
100
|
+
const res = store.inMemoryDB.select(sql `SELECT schemaHash FROM ${SCHEMA_META_TABLE} WHERE tableName = '${componentTableName}'`);
|
|
101
|
+
if (res.length === 0 || res[0].schemaHash !== schemaHash) {
|
|
102
|
+
migrateTable({ db: store._proxyDb, tableDef: stateSchema.ast, otelContext, schemaHash });
|
|
103
|
+
}
|
|
104
|
+
store.tableRefs[componentTableName] = store.graph.makeRef(null, {
|
|
105
|
+
equal: () => false,
|
|
106
|
+
label: componentTableName,
|
|
107
|
+
meta: { liveStoreRefType: 'table' },
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
state$ = store
|
|
111
|
+
.querySQL(() => sql `select * from ${componentTableName} ${whereClause} limit 1`, {
|
|
112
|
+
queriedTables: [componentTableName],
|
|
113
|
+
componentKey,
|
|
114
|
+
label: `localState:query:${componentKeyLabel}`,
|
|
115
|
+
otelContext,
|
|
116
|
+
})
|
|
117
|
+
// TODO consider to instead of just returning the default value, to write the default component state to the DB
|
|
118
|
+
.pipe((results) => results.length === 1
|
|
119
|
+
? Schema.parseSync(componentStateEffectSchema)(results[0])
|
|
120
|
+
: defaultComponentState);
|
|
121
|
+
}
|
|
122
|
+
const initialComponentState = state$.results$.result;
|
|
123
|
+
const queries = generateQueries({
|
|
124
|
+
state$: state$,
|
|
125
|
+
otelContext,
|
|
126
|
+
registerSubscription: () => { },
|
|
127
|
+
isTemporaryQuery: true,
|
|
128
|
+
});
|
|
129
|
+
for (const [name, query] of Object.entries(queries)) {
|
|
130
|
+
query.label = name;
|
|
131
|
+
}
|
|
132
|
+
const initialQueryResults = mapValues(queries, (query) => query.results$.result);
|
|
133
|
+
return { initialComponentState, initialQueryResults };
|
|
134
|
+
}
|
|
135
|
+
finally {
|
|
136
|
+
span.end();
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
}, [
|
|
141
|
+
store,
|
|
142
|
+
otelContext,
|
|
143
|
+
stateSchema,
|
|
144
|
+
generateQueries,
|
|
145
|
+
componentKey,
|
|
146
|
+
componentKeyLabel,
|
|
147
|
+
componentStateEffectSchema,
|
|
148
|
+
defaultComponentState,
|
|
149
|
+
]);
|
|
150
|
+
// Now that we've computed the initial state synchronously,
|
|
151
|
+
// we can set up our useState calls w/ a default value populated...
|
|
152
|
+
const [componentStateRef, setComponentState_] = useStateRefWithReactiveInput(initialComponentState);
|
|
153
|
+
const [queryResultsRef, setQueryResults_] = useStateRefWithReactiveInput(initialQueryResults);
|
|
154
|
+
const setState = (stateSchema === undefined
|
|
155
|
+
? {}
|
|
156
|
+
: // TODO: do we have a better type for the values that can go in SQLite?
|
|
157
|
+
mapValues(stateSchema.columns, (column, columnName) => (value) => {
|
|
158
|
+
// Don't update the state if it's the same as the value already seen in the component
|
|
159
|
+
// @ts-expect-error TODO fix typing
|
|
160
|
+
if (componentStateRef.current[columnName] === value)
|
|
161
|
+
return;
|
|
162
|
+
const encodedValue = Schema.encodeSync(column.type.codec)(value);
|
|
163
|
+
if (['componentKey', 'columnNames'].includes(columnName)) {
|
|
164
|
+
shouldNeverHappen(`Can't use reserved column name ${columnName}`);
|
|
165
|
+
}
|
|
166
|
+
return store.applyEvent('updateComponentState', {
|
|
167
|
+
componentKey,
|
|
168
|
+
columnNames: [columnName],
|
|
169
|
+
[columnName]: encodedValue,
|
|
170
|
+
});
|
|
171
|
+
}));
|
|
172
|
+
setState.setMany = (columnValues) => {
|
|
173
|
+
// TODO use hashing instead
|
|
174
|
+
// Don't update the state if it's the same as the value already seen in the component
|
|
175
|
+
// @ts-expect-error TODO fix typing
|
|
176
|
+
if (Object.entries(columnValues).every(([columnName, value]) => componentStateRef.current[columnName] === value)) {
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
const columnNames = Object.keys(columnValues);
|
|
180
|
+
return store.applyEvent('updateComponentState', { componentKey, columnNames, ...columnValues });
|
|
181
|
+
};
|
|
182
|
+
// OK, now all the synchronous work is done;
|
|
183
|
+
// time to set up our long-running queries in an effect
|
|
184
|
+
React.useEffect(() => {
|
|
185
|
+
return store.otel.tracer.startActiveSpan('LiveStore:useLiveStoreComponent:long-running', { attributes: {} }, otelContext, (span) => {
|
|
186
|
+
const otelContext = otel.trace.setSpan(otel.context.active(), span);
|
|
187
|
+
const unsubs = [];
|
|
188
|
+
// create state query
|
|
189
|
+
let state$;
|
|
190
|
+
if (stateSchema === undefined) {
|
|
191
|
+
// TODO remove this query
|
|
192
|
+
state$ = store.queryJS(() => ({}), {
|
|
193
|
+
componentKey,
|
|
194
|
+
otelContext,
|
|
195
|
+
label: 'empty-component-state',
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
const componentTableName = tableNameForComponentKey(componentKey);
|
|
200
|
+
insertRowForComponentInstance({ store, componentKey, stateSchema });
|
|
201
|
+
const whereClause = componentKey._tag === 'singleton' ? '' : `where id = '${componentKey.id}'`;
|
|
202
|
+
state$ = store
|
|
203
|
+
.querySQL(() => sql `select * from ${componentTableName} ${whereClause} limit 1`, {
|
|
204
|
+
queriedTables: [componentTableName],
|
|
205
|
+
componentKey,
|
|
206
|
+
label: `localState:query:${componentKeyLabel}`,
|
|
207
|
+
otelContext,
|
|
208
|
+
})
|
|
209
|
+
// TODO consider to instead of just returning the default value, to write the default component state to the DB
|
|
210
|
+
.pipe((results) => results.length === 1 ? Schema.parseSync(componentStateEffectSchema)(results[0]) : defaultComponentState);
|
|
211
|
+
}
|
|
212
|
+
unsubs.push(store.subscribe(state$, (results) => {
|
|
213
|
+
if (isEqual(results, componentStateRef.current) === false) {
|
|
214
|
+
setComponentState_(results);
|
|
215
|
+
}
|
|
216
|
+
}, undefined, { label: `useLiveStoreComponent:localState:subscribe:${state$.label}` }));
|
|
217
|
+
const registerSubscription = (query$, callback, onUnsubscribe) => {
|
|
218
|
+
unsubs.push(store.subscribe(query$, (results) => {
|
|
219
|
+
callback(results);
|
|
220
|
+
}, onUnsubscribe, { label: `useLiveStoreComponent:query:manual-subscribe:${query$.label}` }));
|
|
221
|
+
};
|
|
222
|
+
const queries = generateQueries({ state$, otelContext, registerSubscription, isTemporaryQuery: false });
|
|
223
|
+
for (const [key, query] of Object.entries(queries)) {
|
|
224
|
+
// Use the field name given to this query in the useQueries hook as its label
|
|
225
|
+
query.label = key;
|
|
226
|
+
unsubs.push(store.subscribe(query, (results) => {
|
|
227
|
+
const newQueryResults = { ...queryResultsRef.current, [key]: results };
|
|
228
|
+
if (isEqual(newQueryResults, queryResultsRef.current) === false) {
|
|
229
|
+
setQueryResults_(newQueryResults);
|
|
230
|
+
}
|
|
231
|
+
}, undefined, { label: `useLiveStoreComponent:query:subscribe:${query.label}` }));
|
|
232
|
+
}
|
|
233
|
+
return () => {
|
|
234
|
+
for (const unsub of unsubs) {
|
|
235
|
+
unsub();
|
|
236
|
+
}
|
|
237
|
+
span.end();
|
|
238
|
+
};
|
|
239
|
+
});
|
|
240
|
+
// NOTE excluding `setComponentState_` and `setQueryResults_` from the deps array as it seems to cause an infinite loop
|
|
241
|
+
// This should probably be improved
|
|
242
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
243
|
+
}, [
|
|
244
|
+
store,
|
|
245
|
+
componentKey,
|
|
246
|
+
stateSchema,
|
|
247
|
+
defaultComponentState,
|
|
248
|
+
generateQueries,
|
|
249
|
+
otelContext,
|
|
250
|
+
componentStateRef,
|
|
251
|
+
// setComponentState_,
|
|
252
|
+
// setQueryResults_,
|
|
253
|
+
]);
|
|
254
|
+
// Very important: remove any queries / other resources associated w/ this component
|
|
255
|
+
React.useEffect(() => () => store.unmountComponent(componentKey), [store, componentKey]);
|
|
256
|
+
// performance.mark('useLiveStoreComponent:end')
|
|
257
|
+
// performance.measure(`useLiveStoreComponent:${componentKey.type}`, 'useLiveStoreComponent:start', 'useLiveStoreComponent:end')
|
|
258
|
+
const state = componentStateRef.current;
|
|
259
|
+
const useLiveStoreJsonState = (jsonStringKey, parse = (_) => _) => {
|
|
260
|
+
const value = React.useMemo(() => {
|
|
261
|
+
return parse(JSON.parse(state[jsonStringKey]));
|
|
262
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
263
|
+
}, [state[jsonStringKey], parse]);
|
|
264
|
+
const setValue = React.useCallback((newValOrFn) => {
|
|
265
|
+
const newVal = typeof newValOrFn === 'function'
|
|
266
|
+
? // NOTE we're using the ref instead of the value because we want to be sure
|
|
267
|
+
// we're using the latest value when the setter is called
|
|
268
|
+
newValOrFn(parse(JSON.parse(componentStateRef.current[jsonStringKey])))
|
|
269
|
+
: newValOrFn;
|
|
270
|
+
setState[jsonStringKey](JSON.stringify(newVal));
|
|
271
|
+
}, [parse, jsonStringKey]);
|
|
272
|
+
return [value, setValue];
|
|
273
|
+
};
|
|
274
|
+
return {
|
|
275
|
+
queryResults: queryResultsRef.current,
|
|
276
|
+
state,
|
|
277
|
+
setState,
|
|
278
|
+
useLiveStoreJsonState,
|
|
279
|
+
};
|
|
280
|
+
};
|
|
281
|
+
export const useComponentKey = ({ name, id }, deps = []) => React.useMemo(() => {
|
|
282
|
+
switch (id) {
|
|
283
|
+
case 'singleton': {
|
|
284
|
+
return { _tag: 'singleton', componentName: name, id: 'singleton' };
|
|
285
|
+
}
|
|
286
|
+
case '__ephemeral__': {
|
|
287
|
+
return { _tag: 'ephemeral', componentName: name, id: uuid() };
|
|
288
|
+
}
|
|
289
|
+
default: {
|
|
290
|
+
return { _tag: 'custom', componentName: name, id };
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
294
|
+
}, [...deps, id, name]);
|
|
295
|
+
/**
|
|
296
|
+
* Create a row storing the state for a component instance, if none exists yet.
|
|
297
|
+
* Initialized with default values, and keyed on the component key.
|
|
298
|
+
*/
|
|
299
|
+
const insertRowForComponentInstance = ({ store, componentKey, stateSchema, }) => {
|
|
300
|
+
const columnNames = ['id', ...Object.keys(stateSchema.columns)];
|
|
301
|
+
const columnValues = columnNames.map((name) => `$${name}`).join(', ');
|
|
302
|
+
const tableName = tableNameForComponentKey(componentKey);
|
|
303
|
+
const insertQuery = sql `insert into ${tableName} (${columnNames.join(', ')}) select ${columnValues} where not exists(select 1 from ${tableName} where id = '${componentKey.id}')`;
|
|
304
|
+
void store.execute(insertQuery, {
|
|
305
|
+
...mapValues(stateSchema.columns, (column) => prepareValueForSql(column.default ?? null)),
|
|
306
|
+
id: componentKey.id,
|
|
307
|
+
}, [tableName]);
|
|
308
|
+
};
|
|
309
|
+
const prepareValueForSql = (value) => {
|
|
310
|
+
if (typeof value === 'string' || typeof value === 'number' || value === null) {
|
|
311
|
+
return value;
|
|
312
|
+
}
|
|
313
|
+
else {
|
|
314
|
+
return value ? 1 : 0;
|
|
315
|
+
}
|
|
316
|
+
};
|
|
317
|
+
//# sourceMappingURL=useLiveStoreComponent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useLiveStoreComponent.js","sourceRoot":"","sources":["../../src/react/useLiveStoreComponent.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useQuery.d.ts","sourceRoot":"","sources":["../../src/react/useQuery.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AAMrE,eAAO,MAAM,QAAQ,+CAAgD,KAAK,yBA6CzE,CAAA"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { labelForKey } from '../componentKey.js';
|
|
3
|
+
import { useStore } from './LiveStoreContext.js';
|
|
4
|
+
// TODO get rid of the query cache in favour of the new side-effect-free query definition approach https://www.notion.so/schickling/New-query-definition-approach-1097a78ef0e9495bac25f90417374756?pvs=4
|
|
5
|
+
const queryCache = new Map();
|
|
6
|
+
export const useQuery = (queryDef) => {
|
|
7
|
+
const { store } = useStore();
|
|
8
|
+
const query = React.useMemo(() => {
|
|
9
|
+
if (queryCache.has(queryDef))
|
|
10
|
+
return queryCache.get(queryDef);
|
|
11
|
+
const query = queryDef(store);
|
|
12
|
+
queryCache.set(queryDef, query);
|
|
13
|
+
return query;
|
|
14
|
+
}, [store, queryDef]);
|
|
15
|
+
// We know the query has a result by the time we use it; so we can synchronously populate a default state
|
|
16
|
+
const [value, setValue] = React.useState(query.results$.result);
|
|
17
|
+
// Subscribe to future updates for this query
|
|
18
|
+
React.useEffect(() => {
|
|
19
|
+
return store.otel.tracer.startActiveSpan(`LiveStore:useQuery:${labelForKey(query.componentKey)}:${query.label}`, { attributes: { label: query.label } }, query.otelContext, (span) => {
|
|
20
|
+
const cancel = store.subscribe(query, (v) => {
|
|
21
|
+
// NOTE: we return a reference to the result object within LiveStore;
|
|
22
|
+
// this implies that app code must not mutate the results, or else
|
|
23
|
+
// there may be weird reactivity bugs.
|
|
24
|
+
return setValue(v);
|
|
25
|
+
}, undefined, { label: query.label });
|
|
26
|
+
return () => {
|
|
27
|
+
// // NOTE destroying the whole query will also unsubscribe it
|
|
28
|
+
// query.destroy()
|
|
29
|
+
// TODO for now we'll still `cancel` manually, but we should remove this once we have some kind of
|
|
30
|
+
// ARC-based system
|
|
31
|
+
cancel();
|
|
32
|
+
span.end();
|
|
33
|
+
};
|
|
34
|
+
});
|
|
35
|
+
}, [query, store]);
|
|
36
|
+
return value;
|
|
37
|
+
};
|
|
38
|
+
//# sourceMappingURL=useQuery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useQuery.js","sourceRoot":"","sources":["../../src/react/useQuery.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAGhD,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAA;AAEhD,wMAAwM;AACxM,MAAM,UAAU,GAAG,IAAI,GAAG,EAAmC,CAAA;AAE7D,MAAM,CAAC,MAAM,QAAQ,GAAG,CAA2B,QAA6B,EAAkB,EAAE;IAClG,MAAM,EAAE,KAAK,EAAE,GAAG,QAAQ,EAAE,CAAA;IAC5B,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE;QAC/B,IAAI,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC;YAAE,OAAO,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAM,CAAA;QAElE,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;QAC7B,UAAU,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAA;QAC/B,OAAO,KAAK,CAAA;IACd,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAA;IAErB,yGAAyG;IACzG,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAiB,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAA;IAE/E,6CAA6C;IAC7C,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE;QACnB,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,eAAe,CACtC,sBAAsB,WAAW,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,KAAK,CAAC,KAAK,EAAE,EACtE,EAAE,UAAU,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,EAAE,EACtC,KAAK,CAAC,WAAW,EACjB,CAAC,IAAI,EAAE,EAAE;YACP,MAAM,MAAM,GAAG,KAAK,CAAC,SAAS,CAC5B,KAAK,EACL,CAAC,CAAC,EAAE,EAAE;gBACJ,qEAAqE;gBACrE,kEAAkE;gBAClE,sCAAsC;gBACtC,OAAO,QAAQ,CAAC,CAAC,CAAC,CAAA;YACpB,CAAC,EACD,SAAS,EACT,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CACvB,CAAA;YACD,OAAO,GAAG,EAAE;gBACV,8DAA8D;gBAC9D,kBAAkB;gBAElB,kGAAkG;gBAClG,mBAAmB;gBACnB,MAAM,EAAE,CAAA;gBACR,IAAI,CAAC,GAAG,EAAE,CAAA;YACZ,CAAC,CAAA;QACH,CAAC,CACF,CAAA;IACH,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAA;IAElB,OAAO,KAAK,CAAA;AACd,CAAC,CAAA"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* A variant of `React.useState` which allows the `inputState` to change over time as well.
|
|
4
|
+
* Important: This hook is synchronous / single-render-pass (i.e. doesn't use `useEffect` or `setState` directly).
|
|
5
|
+
*
|
|
6
|
+
* Notes:
|
|
7
|
+
* - The output state is always reset to the input state in case the input state changes (i.e. the previous "external" `setStateAndRerender` call is forgotten)
|
|
8
|
+
* - This hook might not work properly with React Suspense
|
|
9
|
+
* - Also see this Tweet for more potential problems: https://twitter.com/schickling/status/1677317711104278528
|
|
10
|
+
*
|
|
11
|
+
*/
|
|
12
|
+
export declare const useStateRefWithReactiveInput: <T>(inputState: T) => [React.MutableRefObject<T>, (newState: T | ((prev: T) => T)) => void];
|
|
13
|
+
//# sourceMappingURL=useStateRefWithReactiveInput.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useStateRefWithReactiveInput.d.ts","sourceRoot":"","sources":["../../../src/react/utils/useStateRefWithReactiveInput.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB;;;;;;;;;GASG;AACH,eAAO,MAAM,4BAA4B,wFAE0B,IAAI,CAwBtE,CAAA"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* A variant of `React.useState` which allows the `inputState` to change over time as well.
|
|
4
|
+
* Important: This hook is synchronous / single-render-pass (i.e. doesn't use `useEffect` or `setState` directly).
|
|
5
|
+
*
|
|
6
|
+
* Notes:
|
|
7
|
+
* - The output state is always reset to the input state in case the input state changes (i.e. the previous "external" `setStateAndRerender` call is forgotten)
|
|
8
|
+
* - This hook might not work properly with React Suspense
|
|
9
|
+
* - Also see this Tweet for more potential problems: https://twitter.com/schickling/status/1677317711104278528
|
|
10
|
+
*
|
|
11
|
+
*/
|
|
12
|
+
export const useStateRefWithReactiveInput = (inputState) => {
|
|
13
|
+
const [_, rerender] = React.useState(0);
|
|
14
|
+
const lastKnownInputStateRef = React.useRef(inputState);
|
|
15
|
+
const stateRef = React.useRef(inputState);
|
|
16
|
+
if (lastKnownInputStateRef.current !== inputState) {
|
|
17
|
+
lastKnownInputStateRef.current = inputState;
|
|
18
|
+
// NOTE we don't need to re-render here, because the component is already re-rendering due to the `inputState` change
|
|
19
|
+
stateRef.current = inputState;
|
|
20
|
+
}
|
|
21
|
+
const setStateAndRerender = React.useCallback((newState) => {
|
|
22
|
+
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/37663
|
|
23
|
+
const val = typeof newState === 'function' ? newState(stateRef.current) : newState;
|
|
24
|
+
stateRef.current = val;
|
|
25
|
+
rerender((c) => c + 1);
|
|
26
|
+
}, [rerender]);
|
|
27
|
+
return [stateRef, setStateAndRerender];
|
|
28
|
+
};
|
|
29
|
+
// Down-side of this implementation: Double render pass due to `setState` call (which forces a re-render)
|
|
30
|
+
// Keeping around for now in case `useStateRefWithReactiveInput` doesn't work out
|
|
31
|
+
// const _useStateWithReactiveInput = <T>(inputState: T): [T, (newState: T | ((prev: T) => T)) => void] => {
|
|
32
|
+
// const [externalState, setExternalState] = React.useState(inputState)
|
|
33
|
+
// if (externalState !== inputState) {
|
|
34
|
+
// setExternalState(inputState)
|
|
35
|
+
// }
|
|
36
|
+
// return [externalState, setExternalState]
|
|
37
|
+
// }
|
|
38
|
+
//# sourceMappingURL=useStateRefWithReactiveInput.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useStateRefWithReactiveInput.js","sourceRoot":"","sources":["../../../src/react/utils/useStateRefWithReactiveInput.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAC1C,UAAa,EAC0D,EAAE;IACzE,MAAM,CAAC,CAAC,EAAE,QAAQ,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAA;IAEvC,MAAM,sBAAsB,GAAG,KAAK,CAAC,MAAM,CAAI,UAAU,CAAC,CAAA;IAC1D,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,CAAI,UAAU,CAAC,CAAA;IAE5C,IAAI,sBAAsB,CAAC,OAAO,KAAK,UAAU,EAAE;QACjD,sBAAsB,CAAC,OAAO,GAAG,UAAU,CAAA;QAE3C,qHAAqH;QACrH,QAAQ,CAAC,OAAO,GAAG,UAAU,CAAA;KAC9B;IAED,MAAM,mBAAmB,GAAG,KAAK,CAAC,WAAW,CAC3C,CAAC,QAA8B,EAAE,EAAE;QACjC,wEAAwE;QACxE,MAAM,GAAG,GAAG,OAAO,QAAQ,KAAK,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAA;QAClF,QAAQ,CAAC,OAAO,GAAG,GAAG,CAAA;QACtB,QAAQ,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAA;IACxB,CAAC,EACD,CAAC,QAAQ,CAAC,CACX,CAAA;IAED,OAAO,CAAC,QAAQ,EAAE,mBAAmB,CAAC,CAAA;AACxC,CAAC,CAAA;AAED,yGAAyG;AACzG,iFAAiF;AACjF,4GAA4G;AAC5G,yEAAyE;AAEzE,wCAAwC;AACxC,mCAAmC;AACnC,MAAM;AAEN,6CAA6C;AAC7C,IAAI"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import type { PrettifyFlat } from '@livestore/utils';
|
|
2
|
+
import type * as otel from '@opentelemetry/api';
|
|
3
|
+
import { BoundArray } from './bounded-collections.js';
|
|
4
|
+
export type GetAtom = <T>(atom: Atom<T>) => T;
|
|
5
|
+
export type Ref<T> = {
|
|
6
|
+
_tag: 'ref';
|
|
7
|
+
id: string;
|
|
8
|
+
result: T;
|
|
9
|
+
height: 0;
|
|
10
|
+
getResult: () => T;
|
|
11
|
+
sub: Set<Atom<any>>;
|
|
12
|
+
super: Set<Atom<any> | Effect>;
|
|
13
|
+
label?: string;
|
|
14
|
+
/** Container for meta information (e.g. the LiveStore Store) */
|
|
15
|
+
meta?: any;
|
|
16
|
+
equal: (a: T, b: T) => boolean;
|
|
17
|
+
};
|
|
18
|
+
type BaseThunk<T> = {
|
|
19
|
+
_tag: 'thunk';
|
|
20
|
+
id: string;
|
|
21
|
+
height: number;
|
|
22
|
+
getResult: (get: GetAtom, addDebugInfo: (debugInfo: any) => void) => T;
|
|
23
|
+
sub: Set<Atom<any>>;
|
|
24
|
+
super: Set<Atom<any> | Effect>;
|
|
25
|
+
label?: string;
|
|
26
|
+
/** Container for meta information (e.g. the LiveStore Store) */
|
|
27
|
+
meta?: any;
|
|
28
|
+
equal: (a: T, b: T) => boolean;
|
|
29
|
+
};
|
|
30
|
+
export type Thunk<T> = BaseThunk<T> & {
|
|
31
|
+
result: T;
|
|
32
|
+
};
|
|
33
|
+
export type Atom<T> = Ref<T> | Thunk<T>;
|
|
34
|
+
export type Effect = {
|
|
35
|
+
_tag: 'effect';
|
|
36
|
+
id: string;
|
|
37
|
+
doEffect: (get: GetAtom) => void;
|
|
38
|
+
sub: Set<Atom<any>>;
|
|
39
|
+
};
|
|
40
|
+
export type Taggable<T extends string = string> = {
|
|
41
|
+
_tag: T;
|
|
42
|
+
};
|
|
43
|
+
export type ReactiveGraphOptions = {
|
|
44
|
+
effectsWrapper?: (runEffects: () => void) => void;
|
|
45
|
+
otelTracer: otel.Tracer;
|
|
46
|
+
};
|
|
47
|
+
export type AtomDebugInfo<TDebugThunkInfo extends Taggable> = {
|
|
48
|
+
atom: SerializedAtom;
|
|
49
|
+
resultChanged: boolean;
|
|
50
|
+
durationMs: number;
|
|
51
|
+
debugInfo: TDebugThunkInfo;
|
|
52
|
+
};
|
|
53
|
+
export type RefreshDebugInfo<TDebugRefreshReason extends Taggable, TDebugThunkInfo extends Taggable> = {
|
|
54
|
+
/** Currently only used for easier handling in React (e.g. as key) */
|
|
55
|
+
id: string;
|
|
56
|
+
reason: TDebugRefreshReason;
|
|
57
|
+
refreshedAtoms: AtomDebugInfo<TDebugThunkInfo>[];
|
|
58
|
+
skippedRefresh: boolean;
|
|
59
|
+
durationMs: number;
|
|
60
|
+
/** Note we're using a regular `Date.now()` timestamp here as it's faster to produce and we don't need the fine accuracy */
|
|
61
|
+
completedTimestamp: number;
|
|
62
|
+
graphSnapshot: ReactiveGraphSnapshot;
|
|
63
|
+
};
|
|
64
|
+
export type RefreshReasonWithGenericReasons<T extends Taggable> = T | {
|
|
65
|
+
_tag: 'makeThunk';
|
|
66
|
+
label?: string;
|
|
67
|
+
} | {
|
|
68
|
+
_tag: 'makeEffect';
|
|
69
|
+
label?: string;
|
|
70
|
+
} | {
|
|
71
|
+
_tag: 'unknown';
|
|
72
|
+
};
|
|
73
|
+
export declare const unknownRefreshReason: () => {
|
|
74
|
+
_tag: "unknown";
|
|
75
|
+
};
|
|
76
|
+
export type SerializedAtom = Readonly<PrettifyFlat<Pick<Atom<unknown>, '_tag' | 'height' | 'id' | 'label' | 'meta' | 'result'> & {
|
|
77
|
+
sub: string[];
|
|
78
|
+
super: string[];
|
|
79
|
+
}>>;
|
|
80
|
+
export type SerializedEffect = Readonly<PrettifyFlat<Pick<Effect, '_tag' | 'id'>>>;
|
|
81
|
+
type ReactiveGraphSnapshot = {
|
|
82
|
+
readonly atoms: SerializedAtom[];
|
|
83
|
+
readonly effects: SerializedEffect[];
|
|
84
|
+
/** IDs of atoms and effects that are dirty */
|
|
85
|
+
readonly dirtyNodes: string[];
|
|
86
|
+
};
|
|
87
|
+
export declare class ReactiveGraph<TDebugRefreshReason extends Taggable, TDebugThunkInfo extends Taggable> {
|
|
88
|
+
private atoms;
|
|
89
|
+
private effects;
|
|
90
|
+
private otelTracer;
|
|
91
|
+
readonly dirtyNodes: Set<Atom<any> | Effect>;
|
|
92
|
+
effectsWrapper: (runEffects: () => void) => void;
|
|
93
|
+
debugRefreshInfos: BoundArray<RefreshDebugInfo<RefreshReasonWithGenericReasons<TDebugRefreshReason>, TDebugThunkInfo>>;
|
|
94
|
+
constructor(options: ReactiveGraphOptions);
|
|
95
|
+
makeRef<T>(val: T, options?: {
|
|
96
|
+
label?: string;
|
|
97
|
+
meta?: unknown;
|
|
98
|
+
equal?: (a: T, b: T) => boolean;
|
|
99
|
+
}): Ref<T>;
|
|
100
|
+
makeThunk<T>(getResult: (get: GetAtom, addDebugInfo: (debugInfo: TDebugThunkInfo) => void) => T, options: {
|
|
101
|
+
label?: string;
|
|
102
|
+
meta?: any;
|
|
103
|
+
equal?: (a: T, b: T) => boolean;
|
|
104
|
+
/** Debug info for initializing the thunk (i.e. running it the first time) */
|
|
105
|
+
debugRefreshReason?: RefreshReasonWithGenericReasons<TDebugRefreshReason>;
|
|
106
|
+
} | undefined, otelContext: otel.Context): Thunk<T>;
|
|
107
|
+
destroy(node: Atom<any> | Effect): void;
|
|
108
|
+
makeEffect(doEffect: (get: GetAtom) => void, options: {
|
|
109
|
+
label?: string;
|
|
110
|
+
} | undefined, otelContext: otel.Context): Effect;
|
|
111
|
+
setRef<T>(ref: Ref<T>, val: T, options: {
|
|
112
|
+
otelHint?: string;
|
|
113
|
+
skipRefresh?: boolean;
|
|
114
|
+
debugRefreshReason?: TDebugRefreshReason;
|
|
115
|
+
} | undefined, otelContext: otel.Context): void;
|
|
116
|
+
setRefs<T>(refs: [Ref<T>, T][], options: {
|
|
117
|
+
otelHint?: string;
|
|
118
|
+
skipRefresh?: boolean;
|
|
119
|
+
debugRefreshReason?: TDebugRefreshReason;
|
|
120
|
+
} | undefined, otelContext: otel.Context): void;
|
|
121
|
+
get<T>(atom: Atom<T>, context: Atom<any> | Effect): T;
|
|
122
|
+
/**
|
|
123
|
+
* Update the graph to be consistent with the current values of the root atoms.
|
|
124
|
+
* Generally we run this after a ref is updated.
|
|
125
|
+
* At the end of the refresh, we run any effects that were scheduled.
|
|
126
|
+
*
|
|
127
|
+
* @param roots Root atoms to start the refresh from
|
|
128
|
+
*/
|
|
129
|
+
refresh(options: {
|
|
130
|
+
otelHint?: string;
|
|
131
|
+
debugRefreshReason?: RefreshReasonWithGenericReasons<TDebugRefreshReason>;
|
|
132
|
+
} | undefined, otelContext: otel.Context): void;
|
|
133
|
+
label(atom: Atom<any> | Effect): string;
|
|
134
|
+
addEdge(superComp: Atom<any> | Effect, subComp: Atom<any>): void;
|
|
135
|
+
removeEdge(superComp: Atom<any> | Effect, subComp: Atom<any>): void;
|
|
136
|
+
updateAtomHeight(atom: Atom<any> | Effect): void;
|
|
137
|
+
private getSnapshot;
|
|
138
|
+
}
|
|
139
|
+
export {};
|
|
140
|
+
//# sourceMappingURL=reactive.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reactive.d.ts","sourceRoot":"","sources":["../src/reactive.ts"],"names":[],"mappings":"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"}
|