@livestore/livestore 0.0.54-dev.21 → 0.0.54-dev.24

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 (46) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/__tests__/react/fixture.d.ts +1 -9
  3. package/dist/__tests__/react/fixture.d.ts.map +1 -1
  4. package/dist/__tests__/react/fixture.js +1 -1
  5. package/dist/__tests__/react/fixture.js.map +1 -1
  6. package/dist/effect/LiveStore.d.ts +1 -0
  7. package/dist/effect/LiveStore.d.ts.map +1 -1
  8. package/dist/effect/LiveStore.js +1 -1
  9. package/dist/effect/LiveStore.js.map +1 -1
  10. package/dist/global-state.d.ts +0 -2
  11. package/dist/global-state.d.ts.map +1 -1
  12. package/dist/global-state.js +0 -1
  13. package/dist/global-state.js.map +1 -1
  14. package/dist/index.d.ts +2 -2
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +1 -1
  17. package/dist/index.js.map +1 -1
  18. package/dist/react/LiveStoreContext.d.ts.map +1 -1
  19. package/dist/react/LiveStoreContext.js +3 -0
  20. package/dist/react/LiveStoreContext.js.map +1 -1
  21. package/dist/react/LiveStoreProvider.d.ts +3 -3
  22. package/dist/react/LiveStoreProvider.d.ts.map +1 -1
  23. package/dist/react/LiveStoreProvider.js +16 -8
  24. package/dist/react/LiveStoreProvider.js.map +1 -1
  25. package/dist/react/LiveStoreProvider.test.js +6 -4
  26. package/dist/react/LiveStoreProvider.test.js.map +1 -1
  27. package/dist/reactive.js +1 -1
  28. package/dist/reactive.js.map +1 -1
  29. package/dist/row-query.d.ts.map +1 -1
  30. package/dist/row-query.js +3 -37
  31. package/dist/row-query.js.map +1 -1
  32. package/dist/store.d.ts +12 -6
  33. package/dist/store.d.ts.map +1 -1
  34. package/dist/store.js +312 -176
  35. package/dist/store.js.map +1 -1
  36. package/package.json +5 -5
  37. package/src/__tests__/react/fixture.tsx +1 -1
  38. package/src/effect/LiveStore.ts +2 -1
  39. package/src/global-state.ts +0 -4
  40. package/src/index.ts +2 -1
  41. package/src/react/LiveStoreContext.ts +4 -0
  42. package/src/react/LiveStoreProvider.test.tsx +9 -4
  43. package/src/react/LiveStoreProvider.tsx +17 -10
  44. package/src/reactive.ts +3 -1
  45. package/src/row-query.ts +4 -50
  46. package/src/store.ts +435 -224
package/dist/store.js CHANGED
@@ -1,17 +1,19 @@
1
- import { Devtools, getExecArgsFromMutation, prepareBindValues } from '@livestore/common';
2
- import { version as liveStoreVersion } from '@livestore/common/package.json';
1
+ import { Devtools, getExecArgsFromMutation, liveStoreVersion, prepareBindValues } from '@livestore/common';
3
2
  import { makeMutationEventSchemaMemo } from '@livestore/common/schema';
4
- import { assertNever, isPromise, makeNoopTracer, shouldNeverHappen } from '@livestore/utils';
5
- import { Effect, Schema, Stream } from '@livestore/utils/effect';
3
+ import { assertNever, isPromise, makeNoopTracer, shouldNeverHappen, throttle } from '@livestore/utils';
4
+ import { cuid } from '@livestore/utils/cuid';
5
+ import { Effect, Layer, Logger, LogLevel, OtelTracer, Queue, Schema, Stream } from '@livestore/utils/effect';
6
6
  import * as otel from '@opentelemetry/api';
7
7
  import { globalReactivityGraph } from './global-state.js';
8
8
  import { MainDatabaseWrapper } from './MainDatabaseWrapper.js';
9
+ import { NOT_REFRESHED_YET } from './reactive.js';
9
10
  import { downloadBlob } from './utils/dev.js';
10
11
  import { getDurationMsFromSpan } from './utils/otel.js';
11
12
  let storeCount = 0;
12
13
  const uniqueStoreId = () => `store-${++storeCount}`;
13
14
  export class Store {
14
15
  id = uniqueStoreId();
16
+ devtoolsConnectionId = cuid();
15
17
  reactivityGraph;
16
18
  mainDbWrapper;
17
19
  adapter;
@@ -25,17 +27,19 @@ export class Store {
25
27
  */
26
28
  tableRefs;
27
29
  // TODO remove this temporary solution and find a better way to avoid re-processing the same mutation
28
- __processedMutationIds = new Set();
30
+ __processedMutationIds;
29
31
  __processedMutationWithoutRefreshIds = new Set();
30
32
  /** RC-based set to see which queries are currently subscribed to */
31
33
  activeQueries;
32
34
  __mutationEventSchema;
33
- constructor({ adapter, schema, graphQLOptions, reactivityGraph, otelOptions, disableDevtools, }) {
35
+ constructor({ adapter, schema, graphQLOptions, reactivityGraph, otelOptions, disableDevtools, __processedMutationIds, }) {
34
36
  this.mainDbWrapper = new MainDatabaseWrapper({ otel: otelOptions, db: adapter.mainDb });
35
37
  this.adapter = adapter;
36
38
  this.schema = schema;
37
39
  // TODO refactor
38
40
  this.__mutationEventSchema = makeMutationEventSchemaMemo(schema);
41
+ // TODO remove this temporary solution and find a better way to avoid re-processing the same mutation
42
+ this.__processedMutationIds = __processedMutationIds;
39
43
  // TODO generalize the `tableRefs` concept to allow finer-grained refs
40
44
  this.tableRefs = {};
41
45
  this.activeQueries = new ReferenceCountedSet();
@@ -51,7 +55,7 @@ export class Store {
51
55
  };
52
56
  this.adapter.coordinator.syncMutations.pipe(Stream.tapSync((mutationEventDecoded) => {
53
57
  this.mutate({ wasSyncMessage: true }, mutationEventDecoded);
54
- }), Stream.runDrain, Effect.tapCauseLogPretty, Effect.runFork);
58
+ }), Stream.runDrain, runEffectFork);
55
59
  if (disableDevtools !== true) {
56
60
  this.bootDevtools();
57
61
  }
@@ -124,7 +128,7 @@ export class Store {
124
128
  }
125
129
  otel.trace.getSpan(this.otel.mutationsSpanContext).end();
126
130
  otel.trace.getSpan(this.otel.queriesSpanContext).end();
127
- await this.adapter.coordinator.shutdown();
131
+ await this.adapter.coordinator.shutdown.pipe(runEffectPromise);
128
132
  };
129
133
  mutate = (firstMutationOrTxnFnOrOptions, ...restMutations) => {
130
134
  let mutationsEvents;
@@ -278,10 +282,9 @@ export class Store {
278
282
  const mutationEventEncoded = Schema.encodeUnknownSync(this.__mutationEventSchema)(mutationEventDecoded);
279
283
  if (coordinatorMode !== 'skip-coordinator') {
280
284
  // Asynchronously apply mutation to a persistent storage (we're not awaiting this promise here)
281
- void this.adapter.coordinator.mutate(mutationEventEncoded, {
282
- span,
283
- persisted: coordinatorMode !== 'skip-persist',
284
- });
285
+ this.adapter.coordinator
286
+ .mutate(mutationEventEncoded, { persisted: coordinatorMode !== 'skip-persist' })
287
+ .pipe(runEffectFork);
285
288
  }
286
289
  // Uncomment to print a list of queries currently registered on the store
287
290
  // console.debug(JSON.parse(JSON.stringify([...this.queries].map((q) => `${labelForKey(q.componentKey)}/${q.label}`))))
@@ -296,8 +299,7 @@ export class Store {
296
299
  */
297
300
  execute = (query, params = {}, writeTables, otelContext) => {
298
301
  this.mainDbWrapper.execute(query, prepareBindValues(params, query), writeTables, { otelContext });
299
- const parentSpan = otel.trace.getSpan(otel.context.active());
300
- this.adapter.coordinator.execute(query, prepareBindValues(params, query), parentSpan);
302
+ this.adapter.coordinator.execute(query, prepareBindValues(params, query)).pipe(runEffectFork);
301
303
  };
302
304
  select = (query, params = {}) => {
303
305
  return this.mainDbWrapper.select(query, { bindValues: prepareBindValues(params, query) });
@@ -307,191 +309,323 @@ export class Store {
307
309
  label: `tableRef:${tableName}`,
308
310
  meta: { liveStoreRefType: 'table' },
309
311
  });
312
+ // TODO shutdown behaviour
310
313
  bootDevtools = () => {
311
- const devtoolsChannel = Devtools.makeBroadcastChannels();
312
- const reactivityGraphSubcriptions = new Map();
313
- const liveQueriesSubscriptions = new Map();
314
- devtoolsChannel.toAppHost.addEventListener('message', async (event) => {
315
- const decoded = Schema.decodeUnknownOption(Devtools.MessageToAppHost)(event.data);
316
- if (decoded._tag === 'None' ||
317
- decoded.value._tag === 'LSD.DevtoolsReady' ||
318
- decoded.value._tag === 'LSD.DevtoolsConnected' ||
319
- decoded.value.channelId !== this.adapter.coordinator.devtools.channelId) {
320
- // console.log(`Unknown message`, event)
314
+ const sendToDevtoolsContentscript = (message) => {
315
+ window.postMessage(Schema.encodeSync(Devtools.DevtoolsWindowMessage.MessageForContentscript)(message), '*');
316
+ };
317
+ const channelId = this.adapter.coordinator.devtools.channelId;
318
+ window.addEventListener('message', (event) => {
319
+ const decodedMessageRes = Schema.decodeOption(Devtools.DevtoolsWindowMessage.MessageForStore)(event.data);
320
+ if (decodedMessageRes._tag === 'None')
321
+ return;
322
+ const message = decodedMessageRes.value;
323
+ if (message._tag === 'LSD.WindowMessage.ContentscriptListening') {
324
+ sendToDevtoolsContentscript(Devtools.DevtoolsWindowMessage.StoreReady.make({ channelId }));
321
325
  return;
322
326
  }
323
- const requestId = decoded.value.requestId;
324
- const sendToDevtools = (message) => devtoolsChannel.fromAppHost.postMessage(Schema.encodeSync(Devtools.MessageFromAppHost)(message));
325
- switch (decoded.value._tag) {
326
- case 'LSD.ReactivityGraphSubscribe': {
327
- const includeResults = decoded.value.includeResults;
328
- const send = () => sendToDevtools(Devtools.ReactivityGraphRes.make({
329
- reactivityGraph: this.reactivityGraph.getSnapshot({ includeResults }),
330
- requestId,
331
- liveStoreVersion,
332
- }));
333
- send();
334
- reactivityGraphSubcriptions.set(requestId, this.reactivityGraph.subscribeToRefresh(() => send()));
335
- break;
336
- }
337
- case 'LSD.DebugInfoReq': {
338
- sendToDevtools(Devtools.DebugInfoRes.make({ debugInfo: this.mainDbWrapper.debugInfo, requestId, liveStoreVersion }));
339
- break;
340
- }
341
- case 'LSD.DebugInfoResetReq': {
342
- this.mainDbWrapper.debugInfo.slowQueries.clear();
343
- sendToDevtools(Devtools.DebugInfoResetRes.make({ requestId, liveStoreVersion }));
344
- break;
345
- }
346
- case 'LSD.DebugInfoRerunQueryReq': {
347
- const { queryStr, bindValues, queriedTables } = decoded.value;
348
- this.mainDbWrapper.select(queryStr, { bindValues, queriedTables, skipCache: true });
349
- sendToDevtools(Devtools.DebugInfoRerunQueryRes.make({ requestId, liveStoreVersion }));
350
- break;
351
- }
352
- case 'LSD.ReactivityGraphUnsubscribe': {
353
- reactivityGraphSubcriptions.get(requestId)();
354
- break;
355
- }
356
- case 'LSD.LiveQueriesSubscribe': {
357
- const send = () => sendToDevtools(Devtools.LiveQueriesRes.make({
358
- liveQueries: [...this.activeQueries].map((q) => ({
359
- _tag: q._tag,
360
- id: q.id,
361
- label: q.label,
362
- runs: q.runs,
363
- executionTimes: q.executionTimes.map((_) => Number(_.toString().slice(0, 5))),
364
- lastestResult: q.results$.previousResult,
365
- activeSubscriptions: Array.from(q.activeSubscriptions),
366
- })),
367
- requestId,
368
- liveStoreVersion,
369
- }));
370
- send();
371
- liveQueriesSubscriptions.set(requestId, this.reactivityGraph.subscribeToRefresh(() => send()));
372
- break;
373
- }
374
- case 'LSD.LiveQueriesUnsubscribe': {
375
- liveQueriesSubscriptions.get(requestId)();
376
- break;
377
- }
378
- case 'LSD.ResetAllDataReq': {
379
- await this.adapter.coordinator.dangerouslyReset(decoded.value.mode);
380
- sendToDevtools(Devtools.ResetAllDataRes.make({ requestId, liveStoreVersion }));
381
- break;
382
- }
383
- // No default
327
+ if (message.channelId !== channelId)
328
+ return;
329
+ if (message._tag === 'LSD.WindowMessage.MessagePortForStore') {
330
+ this.adapter.coordinator.devtools.connect({ port: message.port, connectionId: this.devtoolsConnectionId }).pipe(Effect.tapSync(({ storeMessagePort }) => {
331
+ // console.log('storeMessagePort', storeMessagePort)
332
+ storeMessagePort.addEventListener('message', (event) => {
333
+ const decodedMessage = Schema.decodeUnknownSync(Devtools.MessageToAppHostStore)(event.data);
334
+ // console.log('storeMessagePort message', decodedMessage)
335
+ if (decodedMessage.channelId !== this.adapter.coordinator.devtools.channelId) {
336
+ // console.log(`Unknown message`, event)
337
+ return;
338
+ }
339
+ const requestId = decodedMessage.requestId;
340
+ const sendToDevtools = (message) => storeMessagePort.postMessage(Schema.encodeSync(Devtools.MessageFromAppHostStore)(message));
341
+ const requestIdleCallback = window.requestIdleCallback ?? ((cb) => cb());
342
+ switch (decodedMessage._tag) {
343
+ case 'LSD.ReactivityGraphSubscribe': {
344
+ const includeResults = decodedMessage.includeResults;
345
+ const send = () =>
346
+ // In order to not add more work to the current tick, we use requestIdleCallback
347
+ // to send the reactivity graph updates to the devtools
348
+ requestIdleCallback(() => sendToDevtools(Devtools.ReactivityGraphRes.make({
349
+ reactivityGraph: this.reactivityGraph.getSnapshot({ includeResults }),
350
+ requestId,
351
+ liveStoreVersion,
352
+ })), { timeout: 500 });
353
+ send();
354
+ // In some cases, there can be A LOT of reactivity graph updates in a short period of time
355
+ // so we throttle the updates to avoid sending too much data
356
+ // This might need to be tweaked further and possibly be exposed to the user in some way.
357
+ const throttledSend = throttle(send, 20);
358
+ reactivityGraphSubcriptions.set(requestId, this.reactivityGraph.subscribeToRefresh(throttledSend));
359
+ break;
360
+ }
361
+ case 'LSD.DebugInfoReq': {
362
+ sendToDevtools(Devtools.DebugInfoRes.make({
363
+ debugInfo: this.mainDbWrapper.debugInfo,
364
+ requestId,
365
+ liveStoreVersion,
366
+ }));
367
+ break;
368
+ }
369
+ case 'LSD.DebugInfoResetReq': {
370
+ this.mainDbWrapper.debugInfo.slowQueries.clear();
371
+ sendToDevtools(Devtools.DebugInfoResetRes.make({ requestId, liveStoreVersion }));
372
+ break;
373
+ }
374
+ case 'LSD.DebugInfoRerunQueryReq': {
375
+ const { queryStr, bindValues, queriedTables } = decodedMessage;
376
+ this.mainDbWrapper.select(queryStr, { bindValues, queriedTables, skipCache: true });
377
+ sendToDevtools(Devtools.DebugInfoRerunQueryRes.make({ requestId, liveStoreVersion }));
378
+ break;
379
+ }
380
+ case 'LSD.ReactivityGraphUnsubscribe': {
381
+ reactivityGraphSubcriptions.get(requestId)();
382
+ break;
383
+ }
384
+ case 'LSD.LiveQueriesSubscribe': {
385
+ const send = () => requestIdleCallback(() => sendToDevtools(Devtools.LiveQueriesRes.make({
386
+ liveQueries: [...this.activeQueries].map((q) => ({
387
+ _tag: q._tag,
388
+ id: q.id,
389
+ label: q.label,
390
+ runs: q.runs,
391
+ executionTimes: q.executionTimes.map((_) => Number(_.toString().slice(0, 5))),
392
+ lastestResult: q.results$.previousResult === NOT_REFRESHED_YET
393
+ ? 'SYMBOL_NOT_REFRESHED_YET'
394
+ : q.results$.previousResult,
395
+ activeSubscriptions: Array.from(q.activeSubscriptions),
396
+ })),
397
+ requestId,
398
+ liveStoreVersion,
399
+ })), { timeout: 500 });
400
+ send();
401
+ // Same as in the reactivity graph subscription case above, we need to throttle the updates
402
+ const throttledSend = throttle(send, 20);
403
+ liveQueriesSubscriptions.set(requestId, this.reactivityGraph.subscribeToRefresh(throttledSend));
404
+ break;
405
+ }
406
+ case 'LSD.LiveQueriesUnsubscribe': {
407
+ liveQueriesSubscriptions.get(requestId)();
408
+ break;
409
+ }
410
+ // No default
411
+ }
412
+ });
413
+ storeMessagePort.start();
414
+ }), runEffectFork);
415
+ return;
384
416
  }
385
417
  });
418
+ sendToDevtoolsContentscript(Devtools.DevtoolsWindowMessage.StoreReady.make({ channelId }));
419
+ const reactivityGraphSubcriptions = new Map();
420
+ const liveQueriesSubscriptions = new Map();
421
+ // devtoolsChannel.toAppHost.addEventListener('message', async (event) => {
422
+ // const decoded = Schema.decodeUnknownOption(Devtools.MessageToAppHostStore)(event.data)
423
+ // if (decoded._tag === 'None' || decoded.value.channelId !== this.adapter.coordinator.devtools.channelId) {
424
+ // // console.log(`Unknown message`, event)
425
+ // return
426
+ // }
427
+ // const requestId = decoded.value.requestId
428
+ // const sendToDevtools = (message: Devtools.MessageFromAppHostStore) =>
429
+ // devtoolsChannel.fromAppHost.postMessage(Schema.encodeSync(Devtools.MessageFromAppHostStore)(message))
430
+ // const requestIdleCallback = window.requestIdleCallback ?? ((cb: Function) => cb())
431
+ // switch (decoded.value._tag) {
432
+ // case 'LSD.ReactivityGraphSubscribe': {
433
+ // const includeResults = decoded.value.includeResults
434
+ // const send = () =>
435
+ // // In order to not add more work to the current tick, we use requestIdleCallback
436
+ // // to send the reactivity graph updates to the devtools
437
+ // requestIdleCallback(
438
+ // () =>
439
+ // sendToDevtools(
440
+ // Devtools.ReactivityGraphRes.make({
441
+ // reactivityGraph: this.reactivityGraph.getSnapshot({ includeResults }),
442
+ // requestId,
443
+ // liveStoreVersion,
444
+ // }),
445
+ // ),
446
+ // { timeout: 500 },
447
+ // )
448
+ // send()
449
+ // // In some cases, there can be A LOT of reactivity graph updates in a short period of time
450
+ // // so we throttle the updates to avoid sending too much data
451
+ // // This might need to be tweaked further and possibly be exposed to the user in some way.
452
+ // const throttledSend = throttle(send, 20)
453
+ // reactivityGraphSubcriptions.set(requestId, this.reactivityGraph.subscribeToRefresh(throttledSend))
454
+ // break
455
+ // }
456
+ // case 'LSD.DebugInfoReq': {
457
+ // sendToDevtools(
458
+ // Devtools.DebugInfoRes.make({ debugInfo: this.mainDbWrapper.debugInfo, requestId, liveStoreVersion }),
459
+ // )
460
+ // break
461
+ // }
462
+ // case 'LSD.DebugInfoResetReq': {
463
+ // this.mainDbWrapper.debugInfo.slowQueries.clear()
464
+ // sendToDevtools(Devtools.DebugInfoResetRes.make({ requestId, liveStoreVersion }))
465
+ // break
466
+ // }
467
+ // case 'LSD.DebugInfoRerunQueryReq': {
468
+ // const { queryStr, bindValues, queriedTables } = decoded.value
469
+ // this.mainDbWrapper.select(queryStr, { bindValues, queriedTables, skipCache: true })
470
+ // sendToDevtools(Devtools.DebugInfoRerunQueryRes.make({ requestId, liveStoreVersion }))
471
+ // break
472
+ // }
473
+ // case 'LSD.ReactivityGraphUnsubscribe': {
474
+ // reactivityGraphSubcriptions.get(requestId)!()
475
+ // break
476
+ // }
477
+ // case 'LSD.LiveQueriesSubscribe': {
478
+ // const send = () =>
479
+ // requestIdleCallback(
480
+ // () =>
481
+ // sendToDevtools(
482
+ // Devtools.LiveQueriesRes.make({
483
+ // liveQueries: [...this.activeQueries].map((q) => ({
484
+ // _tag: q._tag,
485
+ // id: q.id,
486
+ // label: q.label,
487
+ // runs: q.runs,
488
+ // executionTimes: q.executionTimes.map((_) => Number(_.toString().slice(0, 5))),
489
+ // lastestResult:
490
+ // q.results$.previousResult === NOT_REFRESHED_YET
491
+ // ? 'SYMBOL_NOT_REFRESHED_YET'
492
+ // : q.results$.previousResult,
493
+ // activeSubscriptions: Array.from(q.activeSubscriptions),
494
+ // })),
495
+ // requestId,
496
+ // liveStoreVersion,
497
+ // }),
498
+ // ),
499
+ // { timeout: 500 },
500
+ // )
501
+ // send()
502
+ // // Same as in the reactivity graph subscription case above, we need to throttle the updates
503
+ // const throttledSend = throttle(send, 20)
504
+ // liveQueriesSubscriptions.set(requestId, this.reactivityGraph.subscribeToRefresh(throttledSend))
505
+ // break
506
+ // }
507
+ // case 'LSD.LiveQueriesUnsubscribe': {
508
+ // liveQueriesSubscriptions.get(requestId)!()
509
+ // break
510
+ // }
511
+ // // No default
512
+ // }
513
+ // })
386
514
  };
387
515
  __devDownloadDb = () => {
388
516
  const data = this.mainDbWrapper.export();
389
517
  downloadBlob(data, `livestore-${Date.now()}.db`);
390
518
  };
391
519
  __devDownloadMutationLogDb = async () => {
392
- const data = await this.adapter.coordinator.getMutationLogData();
520
+ const data = await this.adapter.coordinator.getMutationLogData.pipe(runEffectPromise);
393
521
  downloadBlob(data, `livestore-mutationlog-${Date.now()}.db`);
394
522
  };
395
523
  // TODO allow for graceful store reset without requiring a full page reload (which should also call .boot)
396
- dangerouslyResetStorage = (mode) => this.adapter.coordinator.dangerouslyReset(mode);
524
+ dangerouslyResetStorage = (mode) => this.adapter.coordinator.dangerouslyReset(mode).pipe(runEffectPromise);
397
525
  }
398
526
  /** Create a new LiveStore Store */
399
- export const createStore = async ({ schema, graphQLOptions, otelOptions, adapter: adapterFactory, boot, reactivityGraph = globalReactivityGraph, batchUpdates, disableDevtools, }) => {
527
+ export const createStore = async (options) => createStoreEff(options).pipe(runEffectPromise);
528
+ export const createStoreEff = ({ schema, graphQLOptions, otelOptions, adapter: adapterFactory, boot, reactivityGraph = globalReactivityGraph, batchUpdates, disableDevtools, onBootStatus, }) => {
400
529
  const otelTracer = otelOptions?.tracer ?? makeNoopTracer();
401
530
  const otelRootSpanContext = otelOptions?.rootSpanContext ?? otel.context.active();
402
- return otelTracer.startActiveSpan('createStore', {}, otelRootSpanContext, async (span) => {
403
- try {
404
- performance.mark('livestore:db-creating');
405
- const otelContext = otel.trace.setSpan(otel.context.active(), span);
406
- const adapterPromise = adapterFactory({ otelTracer, otelContext, schema });
407
- const adapter = adapterPromise instanceof Promise ? await adapterPromise : adapterPromise;
408
- performance.mark('livestore:db-created');
409
- performance.measure('livestore:db-create', 'livestore:db-creating', 'livestore:db-created');
410
- if (batchUpdates !== undefined) {
411
- reactivityGraph.effectsWrapper = batchUpdates;
412
- }
413
- const mutationEventSchema = makeMutationEventSchemaMemo(schema);
414
- // TODO consider moving booting into the storage backend
415
- if (boot !== undefined) {
416
- let isInTxn = false;
417
- let txnExecuteStmnts = [];
418
- const bootDbImpl = {
419
- _tag: 'BootDb',
420
- execute: (queryStr, bindValues) => {
421
- const stmt = adapter.mainDb.prepare(queryStr);
422
- stmt.execute(bindValues);
423
- if (isInTxn === true) {
424
- txnExecuteStmnts.push([queryStr, bindValues]);
425
- }
426
- else {
427
- void adapter.coordinator.execute(queryStr, bindValues, undefined);
428
- }
429
- },
430
- mutate: (...list) => {
431
- for (const mutationEventDecoded of list) {
432
- const mutationDef = schema.mutations.get(mutationEventDecoded.mutation) ??
433
- shouldNeverHappen(`Unknown mutation type: ${mutationEventDecoded.mutation}`);
434
- const execArgsArr = getExecArgsFromMutation({ mutationDef, mutationEventDecoded });
435
- // const { bindValues, statementSql } = getExecArgsFromMutation({ mutationDef, mutationEventDecoded })
436
- for (const { statementSql, bindValues } of execArgsArr) {
437
- adapter.mainDb.execute(statementSql, bindValues);
438
- }
439
- const mutationEventEncoded = Schema.encodeUnknownSync(mutationEventSchema)(mutationEventDecoded);
440
- void adapter.coordinator.mutate(mutationEventEncoded, {
441
- span,
442
- persisted: true,
443
- });
444
- }
445
- },
446
- select: (queryStr, bindValues) => {
447
- const stmt = adapter.mainDb.prepare(queryStr);
448
- return stmt.select(bindValues);
449
- },
450
- txn: (callback) => {
451
- try {
452
- isInTxn = true;
453
- adapter.mainDb.execute('BEGIN', undefined);
454
- callback();
455
- adapter.mainDb.execute('COMMIT', undefined);
456
- // adapter.coordinator.execute('BEGIN', undefined, undefined)
457
- for (const [queryStr, bindValues] of txnExecuteStmnts) {
458
- adapter.coordinator.execute(queryStr, bindValues, undefined);
459
- }
460
- // adapter.coordinator.execute('COMMIT', undefined, undefined)
461
- }
462
- catch (e) {
463
- adapter.mainDb.execute('ROLLBACK', undefined);
464
- throw e;
531
+ const TracingLive = Layer.unwrapEffect(Effect.map(OtelTracer.make, Layer.setTracer)).pipe(Layer.provide(Layer.sync(OtelTracer.Tracer, () => otelTracer)));
532
+ return Effect.gen(function* () {
533
+ const span = yield* OtelTracer.currentOtelSpan.pipe(Effect.orDie);
534
+ const bootStatusQueue = yield* Queue.unbounded();
535
+ yield* Queue.take(bootStatusQueue).pipe(Effect.tapSync((status) => onBootStatus?.(status)), Effect.forever, Effect.tapCauseLogPretty, Effect.forkScoped);
536
+ const adapter = yield* adapterFactory({
537
+ schema,
538
+ devtoolsEnabled: disableDevtools !== true,
539
+ bootStatusQueue,
540
+ }).pipe(Effect.withPerformanceMeasure('livestore:makeAdapter'), Effect.withSpan('createStore:makeAdapter'));
541
+ if (batchUpdates !== undefined) {
542
+ reactivityGraph.effectsWrapper = batchUpdates;
543
+ }
544
+ const mutationEventSchema = makeMutationEventSchemaMemo(schema);
545
+ const __processedMutationIds = new Set();
546
+ // TODO consider moving booting into the storage backend
547
+ if (boot !== undefined) {
548
+ let isInTxn = false;
549
+ let txnExecuteStmnts = [];
550
+ const bootDbImpl = {
551
+ _tag: 'BootDb',
552
+ execute: (queryStr, bindValues) => {
553
+ const stmt = adapter.mainDb.prepare(queryStr);
554
+ stmt.execute(bindValues);
555
+ if (isInTxn === true) {
556
+ txnExecuteStmnts.push([queryStr, bindValues]);
557
+ }
558
+ else {
559
+ adapter.coordinator.execute(queryStr, bindValues).pipe(runEffectFork);
560
+ }
561
+ },
562
+ mutate: (...list) => {
563
+ for (const mutationEventDecoded of list) {
564
+ const mutationDef = schema.mutations.get(mutationEventDecoded.mutation) ??
565
+ shouldNeverHappen(`Unknown mutation type: ${mutationEventDecoded.mutation}`);
566
+ __processedMutationIds.add(mutationEventDecoded.id);
567
+ const execArgsArr = getExecArgsFromMutation({ mutationDef, mutationEventDecoded });
568
+ // const { bindValues, statementSql } = getExecArgsFromMutation({ mutationDef, mutationEventDecoded })
569
+ for (const { statementSql, bindValues } of execArgsArr) {
570
+ adapter.mainDb.execute(statementSql, bindValues);
465
571
  }
466
- finally {
467
- isInTxn = false;
468
- txnExecuteStmnts = [];
572
+ const mutationEventEncoded = Schema.encodeUnknownSync(mutationEventSchema)(mutationEventDecoded);
573
+ adapter.coordinator
574
+ .mutate(mutationEventEncoded, { persisted: true })
575
+ .pipe(runEffectFork);
576
+ }
577
+ },
578
+ select: (queryStr, bindValues) => {
579
+ const stmt = adapter.mainDb.prepare(queryStr);
580
+ return stmt.select(bindValues);
581
+ },
582
+ txn: (callback) => {
583
+ try {
584
+ isInTxn = true;
585
+ adapter.mainDb.execute('BEGIN', undefined);
586
+ callback();
587
+ adapter.mainDb.execute('COMMIT', undefined);
588
+ // adapter.coordinator.execute('BEGIN', undefined, undefined)
589
+ for (const [queryStr, bindValues] of txnExecuteStmnts) {
590
+ adapter.coordinator.execute(queryStr, bindValues).pipe(runEffectFork);
469
591
  }
470
- },
471
- };
472
- const booting = boot(bootDbImpl, span);
473
- // NOTE only awaiting if it's actually a promise to avoid unnecessary async/await
474
- if (isPromise(booting)) {
475
- await booting;
476
- }
592
+ // adapter.coordinator.execute('COMMIT', undefined, undefined)
593
+ }
594
+ catch (e) {
595
+ adapter.mainDb.execute('ROLLBACK', undefined);
596
+ throw e;
597
+ }
598
+ finally {
599
+ isInTxn = false;
600
+ txnExecuteStmnts = [];
601
+ }
602
+ },
603
+ };
604
+ const booting = boot(bootDbImpl, span);
605
+ // NOTE only awaiting if it's actually a promise to avoid unnecessary async/await
606
+ if (isPromise(booting)) {
607
+ yield* Effect.promise(() => booting);
477
608
  }
478
- // TODO: we can't apply the schema at this point, we've already loaded persisted data!
479
- // Think about what to do about this case.
480
- // await applySchema(db, schema)
481
- return Store.createStore({
482
- adapter,
483
- schema,
484
- graphQLOptions,
485
- otelOptions: { tracer: otelTracer, rootSpanContext: otelRootSpanContext },
486
- reactivityGraph,
487
- disableDevtools,
488
- }, span);
489
609
  }
490
- finally {
491
- span.end();
492
- }
493
- });
610
+ // TODO: we can't apply the schema at this point, we've already loaded persisted data!
611
+ // Think about what to do about this case.
612
+ // await applySchema(db, schema)
613
+ return Store.createStore({
614
+ adapter,
615
+ schema,
616
+ graphQLOptions,
617
+ otelOptions: { tracer: otelTracer, rootSpanContext: otelRootSpanContext },
618
+ reactivityGraph,
619
+ disableDevtools,
620
+ __processedMutationIds,
621
+ }, span);
622
+ }).pipe(Effect.scoped, Effect.withSpan('createStore', {
623
+ parent: otelOptions?.rootSpanContext
624
+ ? OtelTracer.makeExternalSpan(otel.trace.getSpanContext(otelOptions.rootSpanContext))
625
+ : undefined,
626
+ }), Effect.provide(TracingLive));
494
627
  };
628
+ // TODO consider replacing with Effect's RC data structures
495
629
  class ReferenceCountedSet {
496
630
  map;
497
631
  constructor() {
@@ -522,4 +656,6 @@ class ReferenceCountedSet {
522
656
  }
523
657
  }
524
658
  }
659
+ const runEffectFork = (effect) => effect.pipe(Effect.tapCauseLogPretty, Effect.annotateLogs({ thread: 'window' }), Effect.provide(Logger.pretty), Logger.withMinimumLogLevel(LogLevel.Debug), Effect.runFork);
660
+ const runEffectPromise = (effect) => effect.pipe(Effect.tapCauseLogPretty, Effect.annotateLogs({ thread: 'window' }), Effect.provide(Logger.pretty), Logger.withMinimumLogLevel(LogLevel.Debug), Effect.runPromise);
525
661
  //# sourceMappingURL=store.js.map