@livestore/livestore 0.0.24 → 0.0.27

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 (243) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/QueryCache.d.ts +3 -3
  3. package/dist/QueryCache.d.ts.map +1 -1
  4. package/dist/QueryCache.js +50 -60
  5. package/dist/QueryCache.js.map +1 -1
  6. package/dist/__tests__/react/fixture.d.ts +22 -7
  7. package/dist/__tests__/react/fixture.d.ts.map +1 -1
  8. package/dist/__tests__/react/fixture.js +14 -15
  9. package/dist/__tests__/react/fixture.js.map +1 -1
  10. package/dist/__tests__/react/useQuery.test.js +37 -12
  11. package/dist/__tests__/react/useQuery.test.js.map +1 -1
  12. package/dist/__tests__/react/useRow.test.d.ts +2 -0
  13. package/dist/__tests__/react/useRow.test.d.ts.map +1 -0
  14. package/dist/__tests__/react/useRow.test.js +131 -0
  15. package/dist/__tests__/react/useRow.test.js.map +1 -0
  16. package/dist/__tests__/react/utils/stack-info.test.js +32 -0
  17. package/dist/__tests__/react/utils/stack-info.test.js.map +1 -1
  18. package/dist/__tests__/reactive.test.js +51 -0
  19. package/dist/__tests__/reactive.test.js.map +1 -1
  20. package/dist/__tests__/reactiveQueries/sql.test.js +6 -13
  21. package/dist/__tests__/reactiveQueries/sql.test.js.map +1 -1
  22. package/dist/effect/LiveStore.d.ts +3 -3
  23. package/dist/effect/LiveStore.d.ts.map +1 -1
  24. package/dist/effect/LiveStore.js +2 -2
  25. package/dist/effect/LiveStore.js.map +1 -1
  26. package/dist/global-state.d.ts +19 -0
  27. package/dist/global-state.d.ts.map +1 -0
  28. package/dist/global-state.js +20 -0
  29. package/dist/global-state.js.map +1 -0
  30. package/dist/inMemoryDatabase.d.ts +6 -6
  31. package/dist/inMemoryDatabase.d.ts.map +1 -1
  32. package/dist/inMemoryDatabase.js +16 -10
  33. package/dist/inMemoryDatabase.js.map +1 -1
  34. package/dist/index.d.ts +9 -13
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +7 -8
  37. package/dist/index.js.map +1 -1
  38. package/dist/migrations.d.ts +4 -4
  39. package/dist/migrations.d.ts.map +1 -1
  40. package/dist/migrations.js +34 -28
  41. package/dist/migrations.js.map +1 -1
  42. package/dist/react/LiveStoreContext.js.map +1 -1
  43. package/dist/react/LiveStoreProvider.d.ts +2 -2
  44. package/dist/react/LiveStoreProvider.d.ts.map +1 -1
  45. package/dist/react/LiveStoreProvider.js.map +1 -1
  46. package/dist/react/index.d.ts +1 -2
  47. package/dist/react/index.d.ts.map +1 -1
  48. package/dist/react/index.js +1 -1
  49. package/dist/react/index.js.map +1 -1
  50. package/dist/react/useQuery.d.ts +3 -0
  51. package/dist/react/useQuery.d.ts.map +1 -1
  52. package/dist/react/useQuery.js +7 -6
  53. package/dist/react/useQuery.js.map +1 -1
  54. package/dist/react/useRow.d.ts +33 -0
  55. package/dist/react/useRow.d.ts.map +1 -0
  56. package/dist/react/useRow.js +136 -0
  57. package/dist/react/useRow.js.map +1 -0
  58. package/dist/react/useTemporaryQuery.d.ts +2 -0
  59. package/dist/react/useTemporaryQuery.d.ts.map +1 -1
  60. package/dist/react/useTemporaryQuery.js +28 -11
  61. package/dist/react/useTemporaryQuery.js.map +1 -1
  62. package/dist/react/utils/stack-info.d.ts.map +1 -1
  63. package/dist/react/utils/stack-info.js +3 -3
  64. package/dist/react/utils/stack-info.js.map +1 -1
  65. package/dist/react/utils/useStateRefWithReactiveInput.js.map +1 -1
  66. package/dist/reactive.d.ts +38 -29
  67. package/dist/reactive.d.ts.map +1 -1
  68. package/dist/reactive.js +73 -45
  69. package/dist/reactive.js.map +1 -1
  70. package/dist/reactiveQueries/base-class.d.ts +10 -6
  71. package/dist/reactiveQueries/base-class.d.ts.map +1 -1
  72. package/dist/reactiveQueries/base-class.js +11 -12
  73. package/dist/reactiveQueries/base-class.js.map +1 -1
  74. package/dist/reactiveQueries/graphql.d.ts +2 -2
  75. package/dist/reactiveQueries/graphql.d.ts.map +1 -1
  76. package/dist/reactiveQueries/graphql.js +56 -50
  77. package/dist/reactiveQueries/graphql.js.map +1 -1
  78. package/dist/reactiveQueries/js.d.ts +7 -3
  79. package/dist/reactiveQueries/js.d.ts.map +1 -1
  80. package/dist/reactiveQueries/js.js +25 -15
  81. package/dist/reactiveQueries/js.js.map +1 -1
  82. package/dist/reactiveQueries/sql.d.ts +5 -5
  83. package/dist/reactiveQueries/sql.d.ts.map +1 -1
  84. package/dist/reactiveQueries/sql.js +39 -34
  85. package/dist/reactiveQueries/sql.js.map +1 -1
  86. package/dist/row-query.d.ts +21 -0
  87. package/dist/row-query.d.ts.map +1 -0
  88. package/dist/row-query.js +77 -0
  89. package/dist/row-query.js.map +1 -0
  90. package/dist/schema/action.d.ts +30 -0
  91. package/dist/schema/action.d.ts.map +1 -0
  92. package/dist/schema/action.js +3 -0
  93. package/dist/schema/action.js.map +1 -0
  94. package/dist/schema/index.d.ts +28 -0
  95. package/dist/schema/index.d.ts.map +1 -0
  96. package/dist/schema/index.js +26 -0
  97. package/dist/schema/index.js.map +1 -0
  98. package/dist/schema/system-tables.d.ts +24 -0
  99. package/dist/schema/system-tables.d.ts.map +1 -0
  100. package/dist/schema/system-tables.js +11 -0
  101. package/dist/schema/system-tables.js.map +1 -0
  102. package/dist/schema/table-def.d.ts +161 -0
  103. package/dist/schema/table-def.d.ts.map +1 -0
  104. package/dist/schema/table-def.js +53 -0
  105. package/dist/schema/table-def.js.map +1 -0
  106. package/dist/storage/in-memory/index.d.ts +1 -1
  107. package/dist/storage/in-memory/index.d.ts.map +1 -1
  108. package/dist/storage/in-memory/index.js +6 -7
  109. package/dist/storage/in-memory/index.js.map +1 -1
  110. package/dist/storage/index.d.ts +1 -1
  111. package/dist/storage/index.d.ts.map +1 -1
  112. package/dist/storage/tauri/index.d.ts +1 -1
  113. package/dist/storage/tauri/index.d.ts.map +1 -1
  114. package/dist/storage/tauri/index.js +25 -23
  115. package/dist/storage/tauri/index.js.map +1 -1
  116. package/dist/storage/utils/idb.js +3 -1
  117. package/dist/storage/utils/idb.js.map +1 -1
  118. package/dist/storage/web-worker/index.d.ts +1 -1
  119. package/dist/storage/web-worker/index.d.ts.map +1 -1
  120. package/dist/storage/web-worker/index.js +38 -34
  121. package/dist/storage/web-worker/index.js.map +1 -1
  122. package/dist/storage/web-worker/worker.d.ts +1 -1
  123. package/dist/storage/web-worker/worker.d.ts.map +1 -1
  124. package/dist/storage/web-worker/worker.js +1 -1
  125. package/dist/storage/web-worker/worker.js.map +1 -1
  126. package/dist/store.d.ts +11 -21
  127. package/dist/store.d.ts.map +1 -1
  128. package/dist/store.js +284 -272
  129. package/dist/store.js.map +1 -1
  130. package/dist/utils/bounded-collections.d.ts.map +1 -0
  131. package/dist/utils/bounded-collections.js +90 -0
  132. package/dist/utils/bounded-collections.js.map +1 -0
  133. package/dist/utils/otel.d.ts.map +1 -0
  134. package/dist/{otel.js → utils/otel.js} +1 -1
  135. package/dist/utils/otel.js.map +1 -0
  136. package/dist/utils/util.d.ts.map +1 -0
  137. package/dist/utils/util.js.map +1 -0
  138. package/package.json +21 -18
  139. package/src/QueryCache.ts +4 -4
  140. package/src/__tests__/react/fixture.tsx +17 -17
  141. package/src/__tests__/react/useQuery.test.tsx +56 -14
  142. package/src/__tests__/react/useRow.test.tsx +205 -0
  143. package/src/__tests__/react/utils/stack-info.test.ts +34 -0
  144. package/src/__tests__/reactive.test.ts +71 -0
  145. package/src/__tests__/reactiveQueries/sql.test.ts +6 -13
  146. package/src/effect/LiveStore.ts +7 -7
  147. package/src/global-state.ts +26 -0
  148. package/src/inMemoryDatabase.ts +14 -12
  149. package/src/index.ts +22 -29
  150. package/src/migrations.ts +41 -35
  151. package/src/react/LiveStoreProvider.tsx +2 -2
  152. package/src/react/index.ts +7 -9
  153. package/src/react/useQuery.ts +12 -6
  154. package/src/react/useRow.ts +221 -0
  155. package/src/react/useTemporaryQuery.ts +43 -11
  156. package/src/react/utils/stack-info.ts +4 -3
  157. package/src/reactive.ts +81 -65
  158. package/src/reactiveQueries/base-class.ts +14 -10
  159. package/src/reactiveQueries/graphql.ts +4 -3
  160. package/src/reactiveQueries/js.ts +9 -5
  161. package/src/reactiveQueries/sql.ts +9 -9
  162. package/src/row-query.ts +142 -0
  163. package/src/schema/action.ts +41 -0
  164. package/src/schema/index.ts +63 -0
  165. package/src/schema/system-tables.ts +21 -0
  166. package/src/schema/table-def.ts +199 -0
  167. package/src/storage/in-memory/index.ts +1 -1
  168. package/src/storage/index.ts +2 -1
  169. package/src/storage/tauri/index.ts +2 -2
  170. package/src/storage/web-worker/index.ts +1 -1
  171. package/src/storage/web-worker/worker.ts +2 -2
  172. package/src/store.ts +51 -51
  173. package/dist/__tests__/react/useComponentState.test.d.ts +0 -2
  174. package/dist/__tests__/react/useComponentState.test.d.ts.map +0 -1
  175. package/dist/__tests__/react/useComponentState.test.js +0 -68
  176. package/dist/__tests__/react/useComponentState.test.js.map +0 -1
  177. package/dist/__tests__/react/useLQuery.test.d.ts +0 -2
  178. package/dist/__tests__/react/useLQuery.test.d.ts.map +0 -1
  179. package/dist/__tests__/react/useLQuery.test.js +0 -38
  180. package/dist/__tests__/react/useLQuery.test.js.map +0 -1
  181. package/dist/__tests__/react/useLiveStoreComponent.test.d.ts +0 -2
  182. package/dist/__tests__/react/useLiveStoreComponent.test.d.ts.map +0 -1
  183. package/dist/__tests__/react/useLiveStoreComponent.test.js +0 -73
  184. package/dist/__tests__/react/useLiveStoreComponent.test.js.map +0 -1
  185. package/dist/__tests__/react/utils/extractStackInfoFromStackTrace.test.d.ts +0 -2
  186. package/dist/__tests__/react/utils/extractStackInfoFromStackTrace.test.d.ts.map +0 -1
  187. package/dist/__tests__/react/utils/extractStackInfoFromStackTrace.test.js +0 -38
  188. package/dist/__tests__/react/utils/extractStackInfoFromStackTrace.test.js.map +0 -1
  189. package/dist/bounded-collections.d.ts.map +0 -1
  190. package/dist/bounded-collections.js +0 -103
  191. package/dist/bounded-collections.js.map +0 -1
  192. package/dist/componentKey.d.ts +0 -20
  193. package/dist/componentKey.d.ts.map +0 -1
  194. package/dist/componentKey.js +0 -3
  195. package/dist/componentKey.js.map +0 -1
  196. package/dist/otel.d.ts.map +0 -1
  197. package/dist/otel.js.map +0 -1
  198. package/dist/react/useComponentState.d.ts +0 -50
  199. package/dist/react/useComponentState.d.ts.map +0 -1
  200. package/dist/react/useComponentState.js +0 -240
  201. package/dist/react/useComponentState.js.map +0 -1
  202. package/dist/react/useGlobalQuery.d.ts +0 -3
  203. package/dist/react/useGlobalQuery.d.ts.map +0 -1
  204. package/dist/react/useGlobalQuery.js +0 -26
  205. package/dist/react/useGlobalQuery.js.map +0 -1
  206. package/dist/react/useGraphQL.d.ts +0 -13
  207. package/dist/react/useGraphQL.d.ts.map +0 -1
  208. package/dist/react/useGraphQL.js +0 -87
  209. package/dist/react/useGraphQL.js.map +0 -1
  210. package/dist/react/useLiveStoreComponent.d.ts +0 -75
  211. package/dist/react/useLiveStoreComponent.d.ts.map +0 -1
  212. package/dist/react/useLiveStoreComponent.js +0 -361
  213. package/dist/react/useLiveStoreComponent.js.map +0 -1
  214. package/dist/react/utils/extractNamesFromStackTrace.d.ts +0 -3
  215. package/dist/react/utils/extractNamesFromStackTrace.d.ts.map +0 -1
  216. package/dist/react/utils/extractNamesFromStackTrace.js +0 -40
  217. package/dist/react/utils/extractNamesFromStackTrace.js.map +0 -1
  218. package/dist/react/utils/extractStackInfoFromStackTrace.d.ts +0 -7
  219. package/dist/react/utils/extractStackInfoFromStackTrace.d.ts.map +0 -1
  220. package/dist/react/utils/extractStackInfoFromStackTrace.js +0 -40
  221. package/dist/react/utils/extractStackInfoFromStackTrace.js.map +0 -1
  222. package/dist/reactiveQueries/graph.d.ts +0 -10
  223. package/dist/reactiveQueries/graph.d.ts.map +0 -1
  224. package/dist/reactiveQueries/graph.js +0 -6
  225. package/dist/reactiveQueries/graph.js.map +0 -1
  226. package/dist/schema.d.ts +0 -81
  227. package/dist/schema.d.ts.map +0 -1
  228. package/dist/schema.js +0 -46
  229. package/dist/schema.js.map +0 -1
  230. package/dist/util.d.ts.map +0 -1
  231. package/dist/util.js.map +0 -1
  232. package/src/__tests__/react/useComponentState.test.tsx +0 -100
  233. package/src/componentKey.ts +0 -9
  234. package/src/react/useComponentState.ts +0 -404
  235. package/src/reactiveQueries/graph.ts +0 -15
  236. package/src/schema.ts +0 -143
  237. /package/dist/{bounded-collections.d.ts → utils/bounded-collections.d.ts} +0 -0
  238. /package/dist/{otel.d.ts → utils/otel.d.ts} +0 -0
  239. /package/dist/{util.d.ts → utils/util.d.ts} +0 -0
  240. /package/dist/{util.js → utils/util.js} +0 -0
  241. /package/src/{bounded-collections.ts → utils/bounded-collections.ts} +0 -0
  242. /package/src/{otel.ts → utils/otel.ts} +0 -0
  243. /package/src/{util.ts → utils/util.ts} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bounded-collections.d.ts","sourceRoot":"","sources":["../../src/utils/bounded-collections.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,CAAC,OAAO,OAAO,QAAQ,CAAC,CAAC,EAAE,CAAC;;gBAIpB,SAAS,EAAE,MAAM;IAI7B,OAAO,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,KAAK,IAAI,CAAC,GAAG,SAAS,CAAA;IAEvC,GAAG,QAAS,CAAC,SAAS,CAAC,UAUtB;IAED,GAAG,QAAS,CAAC,KAAG,CAAC,GAAG,SAAS,CAE5B;IAED,MAAM,QAAS,CAAC,UAEf;IAED,IAAI,4BAEH;CACF;AAED,qBAAa,QAAQ,CAAC,CAAC;;gBAGT,SAAS,EAAE,MAAM;IAW7B,OAAO,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,KAAK,IAAI,CAAC,GAAG,SAAS,CAAA;IAEvC,GAAG,MAAO,CAAC,UAET;IAEF,CAAC,MAAM,CAAC,QAAQ,CAAC,4BAEhB;CACF;AAED,qBAAa,UAAU,CAAC,CAAC;;gBAIX,SAAS,EAAE,MAAM;IAI7B,OAAO,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,KAAK,IAAI,CAAC,GAAG,SAAS,CAAA;IAEvC,IAAI,MAAO,CAAC,UAQX;IAED,GAAG,UAAW,MAAM,KAAG,CAAC,GAAG,SAAS,CAEnC;IAED,MAAM,UAAW,MAAM,UAEtB;IAED,IAAI,MAAM,WAET;IAED,CAAC,MAAM,CAAC,QAAQ,CAAC,4BAEhB;IAED,GAAG,cAAe,CAAC,eAElB;IAED,KAAK,aAEJ;IAED,IAAI,aAAa,CAAC,KAAK,CAAC,KAAK,MAAM,sBAElC;CACF"}
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Creates a map that has a fixed number of entries.
3
+ * Once hitting the bound, earliest insertions are removed
4
+ */
5
+ export default class BoundMap {
6
+ #map = new Map();
7
+ #sizeLimit;
8
+ constructor(sizeLimit) {
9
+ this.#sizeLimit = sizeLimit;
10
+ }
11
+ onEvict;
12
+ set = (key, value) => {
13
+ this.#map.set(key, value);
14
+ // console.log(this.#map.size, this.#sizeLimit);
15
+ if (this.#map.size > this.#sizeLimit) {
16
+ const firstKey = this.#map.keys().next().value;
17
+ this.#map.delete(firstKey);
18
+ if (this.onEvict) {
19
+ this.onEvict(firstKey);
20
+ }
21
+ }
22
+ };
23
+ get = (key) => {
24
+ return this.#map.get(key);
25
+ };
26
+ delete = (key) => {
27
+ this.#map.delete(key);
28
+ };
29
+ keys = () => {
30
+ return this.#map.keys();
31
+ };
32
+ }
33
+ export class BoundSet {
34
+ #map;
35
+ constructor(sizeLimit) {
36
+ this.#map = new BoundMap(sizeLimit);
37
+ this.#map.onEvict = this.#onEvict;
38
+ }
39
+ #onEvict = (v) => {
40
+ if (this.onEvict) {
41
+ this.onEvict(v);
42
+ }
43
+ };
44
+ onEvict;
45
+ add = (v) => {
46
+ this.#map.set(v, v);
47
+ };
48
+ [Symbol.iterator] = () => {
49
+ return this.#map.keys();
50
+ };
51
+ }
52
+ export class BoundArray {
53
+ #array = [];
54
+ #sizeLimit;
55
+ constructor(sizeLimit) {
56
+ this.#sizeLimit = sizeLimit;
57
+ }
58
+ onEvict;
59
+ push = (v) => {
60
+ this.#array.push(v);
61
+ if (this.#array.length > this.#sizeLimit) {
62
+ const first = this.#array.shift();
63
+ if (first && this.onEvict) {
64
+ this.onEvict(first);
65
+ }
66
+ }
67
+ };
68
+ get = (index) => {
69
+ return this.#array[index];
70
+ };
71
+ delete = (index) => {
72
+ this.#array.splice(index, 1);
73
+ };
74
+ get length() {
75
+ return this.#array.length;
76
+ }
77
+ [Symbol.iterator] = () => {
78
+ return this.#array[Symbol.iterator]();
79
+ };
80
+ map = (fn) => {
81
+ return this.#array.map(fn);
82
+ };
83
+ clear = () => {
84
+ this.#array = [];
85
+ };
86
+ sort = (fn) => {
87
+ return this.#array.sort(fn);
88
+ };
89
+ }
90
+ //# sourceMappingURL=bounded-collections.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bounded-collections.js","sourceRoot":"","sources":["../../src/utils/bounded-collections.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"otel.d.ts","sourceRoot":"","sources":["../../src/utils/otel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,IAAI,MAAM,oBAAoB,CAAA;AAE/C,eAAO,MAAM,qBAAqB,SAAU,KAAK,IAAI,KAAG,MAGvD,CAAA;AAED,eAAO,MAAM,2BAA2B,SAAU,KAAK,IAAI,KAAG,mBACF,CAAA"}
@@ -1,6 +1,6 @@
1
1
  export const getDurationMsFromSpan = (span) => {
2
2
  const durationHr = span._duration;
3
- return durationHr[0] * 1000 + durationHr[1] / 1000000;
3
+ return durationHr[0] * 1000 + durationHr[1] / 1_000_000;
4
4
  };
5
5
  export const getStartTimeHighResFromSpan = (span) => span._performanceStartTime;
6
6
  //# sourceMappingURL=otel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"otel.js","sourceRoot":"","sources":["../../src/utils/otel.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,IAAe,EAAU,EAAE;IAC/D,MAAM,UAAU,GAAsC,IAAY,CAAC,SAAS,CAAA;IAC5E,OAAO,UAAU,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,UAAU,CAAC,CAAC,CAAC,GAAG,SAAS,CAAA;AACzD,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,IAAe,EAAuB,EAAE,CACjF,IAAY,CAAC,qBAA4C,CAAA"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../../src/utils/util.ts"],"names":[],"mappings":";AAEA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAA;AAEpD,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;AACnD,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,UAAU,GAAG,IAAI,CAAA;AAE1D,MAAM,MAAM,QAAQ,GAAG,QAAQ,EAAE,GAAG,YAAY,CAAA;AAEhD,MAAM,MAAM,kBAAkB,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,oBAAoB,CAAC,CAAA;AAE9E;;;;;GAKG;AACH,eAAO,MAAM,GAAG,aAAc,oBAAoB,WAAW,OAAO,EAAE,KAAG,MAMxE,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,WAAY,QAAQ,aAAa,MAAM,KAAG,kBAWvE,CAAA;AAED;;;GAGG;AAEH,wBAAgB,YAAY,CAAC,CAAC,EAAE,KAAK,GAAG,KAAK,CAE5C;AAED,eAAO,MAAM,cAAc,UAAW,GAAG,KAAG,MAa3C,CAAA;AAED,eAAO,MAAM,SAAS,UAAW,GAAG,8BAAiE,CAAA"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"util.js","sourceRoot":"","sources":["../../src/utils/util.ts"],"names":[],"mappings":"AAAA,8BAA8B;AAW9B;;;;;GAKG;AACH,MAAM,CAAC,MAAM,GAAG,GAAG,CAAC,QAA8B,EAAE,GAAG,IAAe,EAAU,EAAE;IAChF,IAAI,GAAG,GAAG,EAAE,CAAA;IACZ,KAAK,MAAM,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC;QACtC,GAAG,IAAI,QAAQ,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAA;IAClC,CAAC;IACD,OAAO,GAAG,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAA;AAC9B,CAAC,CAAA;AAED;;;;;GAKG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,MAAgB,EAAE,SAAiB,EAAsB,EAAE;IAC3F,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;QAAE,OAAO,MAA4B,CAAA;IAE9D,MAAM,MAAM,GAAiB,EAAE,CAAA;IAC/B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAClD,IAAI,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YAC5B,MAAM,CAAC,IAAI,GAAG,EAAE,CAAC,GAAG,KAAK,CAAA;QAC3B,CAAC;IACH,CAAC;IAED,OAAO,MAA4B,CAAA;AACrC,CAAC,CAAA;AAED;;;GAGG;AACH,kEAAkE;AAClE,MAAM,UAAU,YAAY,CAAC,CAAQ;IACnC,MAAM,IAAI,KAAK,CAAC,qCAAqC,cAAc,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;AAC3E,CAAC;AAED,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,KAAU,EAAU,EAAE;IACnD,MAAM,KAAK,GAAG,OAAO,OAAO,KAAK,WAAW,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAA;IAC9F,MAAM,GAAG,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAA;IAC5B,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,KAAK,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;IAC1C,IAAI,GAAG,KAAK,iBAAiB;QAAE,OAAO,GAAG,GAAG,QAAQ,CAAA;IAEpD,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,KAAK,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;IACrD,CAAC;IAAC,OAAO,CAAM,EAAE,CAAC;QAChB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;QAElB,OAAO,8BAA8B,GAAG,CAAC,CAAA;IAC3C,CAAC;AACH,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,KAAU,EAA6B,EAAE,CAAC,OAAO,KAAK,EAAE,IAAI,KAAK,UAAU,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@livestore/livestore",
3
- "version": "0.0.24",
3
+ "version": "0.0.27",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -28,8 +28,8 @@
28
28
  "default": "./dist/effect/index.js"
29
29
  },
30
30
  "./util": {
31
- "types": "./dist/util.d.ts",
32
- "default": "./dist/util.js"
31
+ "types": "./dist/utils/util.d.ts",
32
+ "default": "./dist/utils/util.js"
33
33
  }
34
34
  },
35
35
  "types": "./dist/index.d.ts",
@@ -42,44 +42,47 @@
42
42
  "./dist/effect/index.d.ts"
43
43
  ],
44
44
  "util": [
45
- "./dist/util.d.ts"
45
+ "./dist/utils/util.d.ts"
46
46
  ]
47
47
  }
48
48
  },
49
49
  "dependencies": {
50
50
  "@graphql-typed-document-node/core": "^3.2.0",
51
- "@opentelemetry/api": "^1.6.0",
51
+ "@opentelemetry/api": "^1.7.0",
52
52
  "comlink": "^4.4.1",
53
- "graphql": "^16.8.1",
54
53
  "lodash-es": "^4.17.21",
55
54
  "sqlite-esm": "3.42.0-build6",
56
55
  "uuid": "^9.0.1",
57
- "@livestore/utils": "0.0.24",
58
- "effect-db-schema": "0.0.24"
56
+ "@livestore/utils": "0.0.27",
57
+ "effect-db-schema": "0.0.27"
59
58
  },
60
59
  "devDependencies": {
61
- "@tauri-apps/api": "^1.5.0",
62
- "@opentelemetry/sdk-trace-base": "1.17.1",
63
- "@testing-library/react": "^14.0.0",
64
- "@types/lodash-es": "^4.17.9",
65
- "@types/react": "^18.2.28",
66
- "@types/react-dom": "^18.2.13",
67
- "@types/uuid": "^9.0.5",
68
- "jsdom": "^22.1.0",
60
+ "@opentelemetry/sdk-trace-base": "1.18.1",
61
+ "@tauri-apps/api": "^1.5.1",
62
+ "@testing-library/react": "^14.1.2",
63
+ "@types/lodash-es": "^4.17.12",
64
+ "@types/react": "^18.2.39",
65
+ "@types/react-dom": "^18.2.17",
66
+ "@types/uuid": "^9.0.7",
67
+ "jsdom": "^23.0.1",
69
68
  "react": "^18.2.0",
70
69
  "react-dom": "^18.2.0",
71
- "typescript": "5.2.2",
72
- "vite": "4.4.11",
70
+ "typescript": "5.3.2",
71
+ "vite": "5.0.4",
73
72
  "vitest": "^0.34.6"
74
73
  },
75
74
  "peerDependencies": {
76
75
  "@tauri-apps/api": "^1.4.0",
76
+ "graphql": "^16.8.1",
77
77
  "react": "^18",
78
78
  "react-dom": "^18"
79
79
  },
80
80
  "peerDependenciesMeta": {
81
81
  "@tauri-apps/api": {
82
82
  "optional": true
83
+ },
84
+ "graphql": {
85
+ "optional": true
83
86
  }
84
87
  },
85
88
  "publishConfig": {
package/src/QueryCache.ts CHANGED
@@ -1,5 +1,5 @@
1
- import BoundMap, { BoundSet } from './bounded-collections.js'
2
- import type { Bindable } from './util.js'
1
+ import BoundMap, { BoundSet } from './utils/bounded-collections.js'
2
+ import type { Bindable } from './utils/util.js'
3
3
 
4
4
  type Opaque<BaseType, BrandType = unknown> = BaseType & {
5
5
  readonly [Symbols.base]: BaseType
@@ -38,7 +38,7 @@ export default class QueryCache {
38
38
  return this.#entries.get(key)
39
39
  }
40
40
 
41
- set = (queriedTables: ReadonlyArray<string>, key: CacheKey, results: any) => {
41
+ set = (queriedTables: Iterable<string>, key: CacheKey, results: any) => {
42
42
  this.#entries.set(key, results)
43
43
  for (const table of queriedTables) {
44
44
  let keys = this.#dependencies.get(table)
@@ -67,7 +67,7 @@ export default class QueryCache {
67
67
  // b. incrementally updating the view on insert by the EventImporter
68
68
  //
69
69
  // We'll not try to tackle any generalized approach until we have a proof of concept working.
70
- invalidate = (queriedTables: string[]) => {
70
+ invalidate = (queriedTables: Iterable<string>) => {
71
71
  for (const table of queriedTables) {
72
72
  const keys = this.#dependencies.get(table)
73
73
  if (keys == null) {
@@ -2,14 +2,14 @@ import type * as otel from '@opentelemetry/api'
2
2
  import React from 'react'
3
3
  import initSqlite3Wasm from 'sqlite-esm'
4
4
 
5
- import * as LiveStore from '../../index.js'
6
- import { sql } from '../../index.js'
5
+ import type { LiveStoreContext } from '../../index.js'
6
+ import { createStore, DbSchema, makeSchema, sql } from '../../index.js'
7
7
  import * as LiveStoreReact from '../../react/index.js'
8
8
  import { InMemoryStorage } from '../../storage/in-memory/index.js'
9
9
 
10
10
  export type Todo = {
11
11
  id: string
12
- text: string | null
12
+ text: string
13
13
  completed: boolean
14
14
  }
15
15
 
@@ -20,17 +20,17 @@ export type AppState = {
20
20
  filter: Filter
21
21
  }
22
22
 
23
- export const schema = LiveStore.makeSchema({
23
+ export const schema = makeSchema({
24
24
  tables: {
25
- todos: LiveStore.DbSchema.table('todos', {
26
- id: LiveStore.DbSchema.text({ primaryKey: true }),
27
- text: LiveStore.DbSchema.text({ default: '', nullable: false }),
28
- completed: LiveStore.DbSchema.boolean({ default: false, nullable: false }),
25
+ todos: DbSchema.table('todos', {
26
+ id: DbSchema.text({ primaryKey: true }),
27
+ text: DbSchema.text({ default: '', nullable: false }),
28
+ completed: DbSchema.boolean({ default: false, nullable: false }),
29
29
  }),
30
- app: LiveStore.DbSchema.table('app', {
31
- id: LiveStore.DbSchema.text({ primaryKey: true }),
32
- newTodoText: LiveStore.DbSchema.text({ default: '', nullable: true }),
33
- filter: LiveStore.DbSchema.text({ default: 'all', nullable: false }),
30
+ app: DbSchema.table('app', {
31
+ id: DbSchema.text({ primaryKey: true }),
32
+ newTodoText: DbSchema.text({ default: '', nullable: true }),
33
+ filter: DbSchema.text({ default: 'all', nullable: false }),
34
34
  }),
35
35
  },
36
36
  actions: {
@@ -59,8 +59,8 @@ export const makeTodoMvc = async ({
59
59
  otelTracer?: otel.Tracer
60
60
  otelContext?: otel.Context
61
61
  } = {}) => {
62
- const AppSchema = LiveStore.defineComponentStateSchema('UserInfo', {
63
- username: LiveStore.DbSchema.text({ default: '' }),
62
+ const AppComponentSchema = DbSchema.table('UserInfo', {
63
+ username: DbSchema.text({ default: '' }),
64
64
  })
65
65
 
66
66
  const sqlite3 = await initSqlite3Wasm({
@@ -68,7 +68,7 @@ export const makeTodoMvc = async ({
68
68
  printErr: (message) => console.error(`[livestore sqlite] ${message}`),
69
69
  })
70
70
 
71
- const store = await LiveStore.createStore({
71
+ const store = await createStore({
72
72
  schema,
73
73
  loadStorage: () => InMemoryStorage.load(),
74
74
  boot: (db) => db.execute(sql`INSERT OR IGNORE INTO app (id, newTodoText, filter) VALUES ('static', '', 'all');`),
@@ -77,11 +77,11 @@ export const makeTodoMvc = async ({
77
77
  otelRootSpanContext: otelContext,
78
78
  })
79
79
 
80
- const storeContext: LiveStore.LiveStoreContext = { store }
80
+ const storeContext: LiveStoreContext = { store }
81
81
 
82
82
  const wrapper = ({ children }: any) => (
83
83
  <LiveStoreReact.LiveStoreContext.Provider value={storeContext}>{children}</LiveStoreReact.LiveStoreContext.Provider>
84
84
  )
85
85
 
86
- return { wrapper, AppSchema, store }
86
+ return { wrapper, AppComponentSchema, store }
87
87
  }
@@ -1,29 +1,26 @@
1
1
  import { act, renderHook } from '@testing-library/react'
2
+ import React from 'react'
2
3
  import { describe, expect, it } from 'vitest'
3
4
 
4
5
  import * as LiveStoreReact from '../../react/index.js'
5
- import { LiveStoreSQLQuery } from '../../reactiveQueries/sql.js'
6
- import { sql } from '../../util.js'
6
+ import { querySQL } from '../../reactiveQueries/sql.js'
7
+ import { sql } from '../../utils/util.js'
7
8
  import type { Todo } from './fixture.js'
8
9
  import { makeTodoMvc } from './fixture.js'
9
10
 
10
- const query = new LiveStoreSQLQuery<Todo>({
11
- label: 'todo',
12
- genQueryString: `select * from todos`,
13
- // queriedTables: ['todos'],
14
- })
15
-
16
11
  describe('useQuery', () => {
17
12
  it('simple', async () => {
18
13
  let renderCount = 0
19
14
 
20
15
  const { wrapper, store } = await makeTodoMvc()
21
16
 
17
+ const allTodos$ = querySQL<Todo>(`select * from todos`)
18
+
22
19
  const { result } = renderHook(
23
20
  () => {
24
21
  renderCount++
25
22
 
26
- return LiveStoreReact.useQuery(query)
23
+ return LiveStoreReact.useQuery(allTodos$)
27
24
  },
28
25
  { wrapper },
29
26
  )
@@ -32,9 +29,8 @@ describe('useQuery', () => {
32
29
  expect(renderCount).toBe(1)
33
30
 
34
31
  act(() =>
35
- store.applyEvent('RawSql', {
36
- sql: sql`INSERT INTO todos (id, text, completed) VALUES ('t1', 'buy milk', 0);`,
37
- bindValues: {},
32
+ store.applyEvent('livestore.RawSql', {
33
+ sql: sql`INSERT INTO todos (id, text, completed) VALUES ('t1', 'buy milk', 0)`,
38
34
  writeTables: ['todos'],
39
35
  }),
40
36
  )
@@ -43,6 +39,52 @@ describe('useQuery', () => {
43
39
  expect(result.current[0]!.text).toBe('buy milk')
44
40
  expect(renderCount).toBe(2)
45
41
  })
46
- })
47
42
 
48
- // TODO write tests that use the same query in multiple components at the same time with different bind values
43
+ it('same `useQuery` hook invoked with different queries', async () => {
44
+ let renderCount = 0
45
+
46
+ const { wrapper, store } = await makeTodoMvc()
47
+
48
+ const todo1$ = querySQL<Todo>(`select * from todos where id = 't1'`, { label: 'libraryTracksView1' })
49
+ const todo2$ = querySQL<Todo>(`select * from todos where id = 't2'`, { label: 'libraryTracksView2' })
50
+
51
+ store.applyEvent('livestore.RawSql', {
52
+ sql: sql`INSERT INTO todos (id, text, completed) VALUES ('t1', 'buy milk', 0)`,
53
+ writeTables: ['todos'],
54
+ })
55
+
56
+ store.applyEvent('livestore.RawSql', {
57
+ sql: sql`INSERT INTO todos (id, text, completed) VALUES ('t2', 'buy eggs', 0)`,
58
+ writeTables: ['todos'],
59
+ })
60
+
61
+ const { result, rerender } = renderHook(
62
+ (todoId: string) => {
63
+ renderCount++
64
+
65
+ const query$ = React.useMemo(() => (todoId === 't1' ? todo1$ : todo2$), [todoId])
66
+
67
+ return LiveStoreReact.useQuery(query$)[0]!.text
68
+ },
69
+ { wrapper, initialProps: 't1' },
70
+ )
71
+
72
+ expect(result.current).toBe('buy milk')
73
+ expect(renderCount).toBe(1)
74
+
75
+ act(() =>
76
+ store.applyEvent('livestore.RawSql', {
77
+ sql: sql`UPDATE todos SET text = 'buy soy milk' WHERE id = 't1'`,
78
+ writeTables: ['todos'],
79
+ }),
80
+ )
81
+
82
+ expect(result.current).toBe('buy soy milk')
83
+ expect(renderCount).toBe(2)
84
+
85
+ rerender('t2')
86
+
87
+ expect(result.current).toBe('buy eggs')
88
+ expect(renderCount).toBe(3)
89
+ })
90
+ })
@@ -0,0 +1,205 @@
1
+ import { act, render, renderHook } from '@testing-library/react'
2
+ import React from 'react'
3
+ import { describe, expect, it } from 'vitest'
4
+
5
+ import * as LiveStore from '../../index.js'
6
+ import * as LiveStoreReact from '../../react/index.js'
7
+ import type { Todo } from './fixture.js'
8
+ import { makeTodoMvc } from './fixture.js'
9
+
10
+ describe('useRow', () => {
11
+ it('should update the data based on component key', async () => {
12
+ let renderCount = 0
13
+
14
+ const { wrapper, AppComponentSchema, store } = await makeTodoMvc()
15
+
16
+ const { result, rerender } = renderHook(
17
+ (userId: string) => {
18
+ renderCount++
19
+
20
+ const [state, setState] = LiveStoreReact.useRow(AppComponentSchema, userId)
21
+ return { state, setState }
22
+ },
23
+ { wrapper, initialProps: 'u1' },
24
+ )
25
+
26
+ expect(result.current.state.id).toBe('u1')
27
+ expect(result.current.state.username).toBe('')
28
+ expect(renderCount).toBe(1)
29
+
30
+ act(() => {
31
+ void store.execute(LiveStore.sql`INSERT INTO UserInfo (id, username) VALUES ('u2', 'username_u2');`)
32
+ })
33
+
34
+ rerender('u2')
35
+
36
+ expect(result.current.state.id).toBe('u2')
37
+ expect(result.current.state.username).toBe('username_u2')
38
+ expect(renderCount).toBe(2)
39
+ })
40
+
41
+ it('should update the data reactively - via setState', async () => {
42
+ let renderCount = 0
43
+
44
+ const { wrapper, AppComponentSchema } = await makeTodoMvc()
45
+
46
+ const { result } = renderHook(
47
+ (userId: string) => {
48
+ renderCount++
49
+
50
+ const [state, setState] = LiveStoreReact.useRow(AppComponentSchema, userId)
51
+ return { state, setState }
52
+ },
53
+ { wrapper, initialProps: 'u1' },
54
+ )
55
+
56
+ expect(result.current.state.id).toBe('u1')
57
+ expect(result.current.state.username).toBe('')
58
+ expect(renderCount).toBe(1)
59
+
60
+ act(() => result.current.setState.username('username_u1_hello'))
61
+
62
+ expect(result.current.state.id).toBe('u1')
63
+ expect(result.current.state.username).toBe('username_u1_hello')
64
+ expect(renderCount).toBe(2)
65
+ })
66
+
67
+ it('should update the data reactively - via raw store update', async () => {
68
+ let renderCount = 0
69
+
70
+ const { wrapper, AppComponentSchema, store } = await makeTodoMvc()
71
+
72
+ const { result } = renderHook(
73
+ (userId: string) => {
74
+ renderCount++
75
+
76
+ const [state, setState] = LiveStoreReact.useRow(AppComponentSchema, userId)
77
+ return { state, setState }
78
+ },
79
+ { wrapper, initialProps: 'u1' },
80
+ )
81
+
82
+ expect(result.current.state.id).toBe('u1')
83
+ expect(result.current.state.username).toBe('')
84
+ expect(renderCount).toBe(1)
85
+
86
+ act(() => result.current.setState.username('username_u1_hello'))
87
+
88
+ act(() => {
89
+ void store.execute(LiveStore.sql`UPDATE UserInfo SET username = 'username_u1_hello' WHERE id = 'u1';`)
90
+ })
91
+
92
+ expect(result.current.state.id).toBe('u1')
93
+ expect(result.current.state.username).toBe('username_u1_hello')
94
+ expect(renderCount).toBe(2)
95
+ })
96
+
97
+ it('should work for a larger app', async () => {
98
+ const allTodos$ = LiveStore.querySQL<Todo>(`select * from todos`, { label: 'allTodos' })
99
+
100
+ const { wrapper, store } = await makeTodoMvc()
101
+
102
+ const AppRouterSchema = LiveStore.DbSchema.table(
103
+ 'AppRouter',
104
+ {
105
+ currentTaskId: LiveStore.DbSchema.text({ default: null, nullable: true }),
106
+ },
107
+ { isSingleton: true },
108
+ )
109
+
110
+ let appRouterRenderCount = 0
111
+ let globalSetState: LiveStoreReact.StateSetters<typeof AppRouterSchema> | undefined
112
+ const AppRouter: React.FC = () => {
113
+ appRouterRenderCount++
114
+
115
+ const [state, setState] = LiveStoreReact.useRow(AppRouterSchema)
116
+
117
+ globalSetState = setState
118
+
119
+ return (
120
+ <div>
121
+ <TasksList setTaskId={setState.currentTaskId} />
122
+ <div role="current-id">Current Task Id: {state.currentTaskId ?? '-'}</div>
123
+ {state.currentTaskId ? <TaskDetails id={state.currentTaskId} /> : <div>Click on a task to see details</div>}
124
+ </div>
125
+ )
126
+ }
127
+
128
+ const TasksList: React.FC<{ setTaskId: (_: string) => void }> = ({ setTaskId }) => {
129
+ const allTodos = LiveStoreReact.useQuery(allTodos$)
130
+
131
+ return (
132
+ <div>
133
+ {allTodos.map((_) => (
134
+ <div key={_.id} onClick={() => setTaskId(_.id)}>
135
+ {_.id}
136
+ </div>
137
+ ))}
138
+ </div>
139
+ )
140
+ }
141
+
142
+ const TaskDetails: React.FC<{ id: string }> = ({ id }) => {
143
+ const todo = LiveStoreReact.useTemporaryQuery(() =>
144
+ LiveStore.querySQL<Todo>(`select * from todos where id = '${id}' limit 1`).getFirstRow(),
145
+ )
146
+ return <div role="content">{JSON.stringify(todo)}</div>
147
+ }
148
+
149
+ const renderResult = render(<AppRouter />, { wrapper })
150
+
151
+ expect(appRouterRenderCount).toBe(1)
152
+
153
+ act(() =>
154
+ store.applyEvent('livestore.RawSql', {
155
+ sql: LiveStore.sql`INSERT INTO todos (id, text, completed) VALUES ('t1', 'buy milk', 0);`,
156
+ writeTables: ['todos'],
157
+ }),
158
+ )
159
+
160
+ expect(appRouterRenderCount).toBe(1)
161
+ expect(renderResult.getByRole('current-id').innerHTML).toMatchInlineSnapshot('"Current Task Id: -"')
162
+
163
+ act(() => globalSetState!.currentTaskId('t1'))
164
+
165
+ expect(appRouterRenderCount).toBe(2)
166
+ expect(renderResult.getByRole('content').innerHTML).toMatchInlineSnapshot(
167
+ '"{\\"id\\":\\"t1\\",\\"text\\":\\"buy milk\\",\\"completed\\":0}"',
168
+ )
169
+
170
+ expect(renderResult.getByRole('current-id').innerHTML).toMatchInlineSnapshot('"Current Task Id: t1"')
171
+
172
+ act(() =>
173
+ store.applyEvents([
174
+ {
175
+ eventType: 'livestore.RawSql',
176
+ args: {
177
+ sql: LiveStore.sql`INSERT INTO todos (id, text, completed) VALUES ('t2', 'buy eggs', 0);`,
178
+ writeTables: ['todos'],
179
+ },
180
+ },
181
+ {
182
+ eventType: 'livestore.UpdateComponentState',
183
+ args: {
184
+ id: 'singleton',
185
+ columnNames: ['currentTaskId'],
186
+ tableName: AppRouterSchema.schema.name,
187
+ bindValues: { currentTaskId: 't2' },
188
+ },
189
+ },
190
+ {
191
+ eventType: 'livestore.RawSql',
192
+ args: {
193
+ sql: LiveStore.sql`INSERT INTO todos (id, text, completed) VALUES ('t3', 'buy bread', 0);`,
194
+ writeTables: ['todos'],
195
+ },
196
+ },
197
+ ]),
198
+ )
199
+
200
+ expect(appRouterRenderCount).toBe(3)
201
+ expect(renderResult.getByRole('current-id').innerHTML).toMatchInlineSnapshot('"Current Task Id: t2"')
202
+ })
203
+ })
204
+
205
+ // TODO add otel tests
@@ -43,3 +43,37 @@ Error
43
43
  }
44
44
  `)
45
45
  })
46
+
47
+ it('Tracklist_ stacktrace', async () => {
48
+ const stackTrace = `\
49
+ stack Error
50
+ at https://localhost:8081/@fs/Users/schickling/Code/overtone/submodules/livestore/packages/@livestore/livestore/dist/react/useQuery.js?t=1701368568351:19:23
51
+ at mountMemo (https://localhost:8081/node_modules/.vite-web/deps/chunk-YKTDXTVC.js?v=86daed82:12817:27)
52
+ at Object.useMemo (https://localhost:8081/node_modules/.vite-web/deps/chunk-YKTDXTVC.js?v=86daed82:13141:24)
53
+ at Object.useMemo (https://localhost:8081/node_modules/.vite-web/deps/chunk-7P4K3U7O.js?v=86daed82:1094:29)
54
+ at useQueryRef (https://localhost:8081/@fs/Users/schickling/Code/overtone/submodules/livestore/packages/@livestore/livestore/dist/react/useQuery.js?t=1701368568351:16:29)
55
+ at Module.useQuery (https://localhost:8081/@fs/Users/schickling/Code/overtone/submodules/livestore/packages/@livestore/livestore/dist/react/useQuery.js?t=1701368568351:13:36)
56
+ at Tracklist_ (https://localhost:8081/src/components/Tracklist/Tracklist.tsx?t=1701368568351:148:44)
57
+ at renderWithHooks (https://localhost:8081/node_modules/.vite-web/deps/chunk-YKTDXTVC.js?v=86daed82:12171:26)
58
+ at mountIndeterminateComponent (https://localhost:8081/node_modules/.vite-web/deps/chunk-YKTDXTVC.js?v=86daed82:14921:21)
59
+ at beginWork (https://localhost:8081/node_modules/.vite-web/deps/chunk-YKTDXTVC.js?v=86daed82:15902:22)
60
+ `
61
+
62
+ const stackInfo = extractStackInfoFromStackTrace(stackTrace)
63
+ // Replacing file paths for snapshot testing as they are not stable
64
+ stackInfo.frames.forEach((_) => (_.filePath = '__REPLACED_FOR_SNAPSHOT__'))
65
+ expect(stackInfo).toMatchInlineSnapshot(`
66
+ {
67
+ "frames": [
68
+ {
69
+ "filePath": "__REPLACED_FOR_SNAPSHOT__",
70
+ "name": "Tracklist_",
71
+ },
72
+ {
73
+ "filePath": "__REPLACED_FOR_SNAPSHOT__",
74
+ "name": "useQuery",
75
+ },
76
+ ],
77
+ }
78
+ `)
79
+ })