@wovin/core 0.1.36 → 0.2.2

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 (213) hide show
  1. package/README.md +0 -12
  2. package/dist/applog/applog-helpers.d.ts +12 -12
  3. package/dist/applog/applog-helpers.d.ts.map +1 -1
  4. package/dist/applog/applog-utils.d.ts +40 -6
  5. package/dist/applog/applog-utils.d.ts.map +1 -1
  6. package/dist/applog/datom-types.d.ts +67 -12
  7. package/dist/applog/datom-types.d.ts.map +1 -1
  8. package/dist/applog.d.ts +3 -3
  9. package/dist/applog.d.ts.map +1 -1
  10. package/dist/{applog.min.js → applog.js} +12 -7
  11. package/dist/blockstore.d.ts +1 -1
  12. package/dist/blockstore.d.ts.map +1 -1
  13. package/dist/{blockstore.min.js → blockstore.js} +1 -3
  14. package/dist/{blockstore.min.js.map → blockstore.js.map} +1 -1
  15. package/dist/chunk-22WDFLXO.js +138 -0
  16. package/dist/chunk-22WDFLXO.js.map +1 -0
  17. package/dist/chunk-3SUFNJEZ.js +1026 -0
  18. package/dist/chunk-3SUFNJEZ.js.map +1 -0
  19. package/dist/chunk-6ALNRM3J.js +435 -0
  20. package/dist/chunk-6ALNRM3J.js.map +1 -0
  21. package/dist/chunk-7Z5YDQKK.js +1 -0
  22. package/dist/{chunk-KXMTKPF4.min.js → chunk-BLF5MAWU.js} +8 -8
  23. package/dist/chunk-BLF5MAWU.js.map +1 -0
  24. package/dist/chunk-E46VTKTZ.js +1 -0
  25. package/dist/{chunk-H3VQJP56.min.js → chunk-HUIQ54TT.js} +9 -9
  26. package/dist/chunk-HUIQ54TT.js.map +1 -0
  27. package/dist/{chunk-BRC7LSM6.min.js → chunk-OC6Z6CQW.js} +5 -5
  28. package/dist/chunk-OC6Z6CQW.js.map +1 -0
  29. package/dist/chunk-SHUHRHOT.js +1923 -0
  30. package/dist/chunk-SHUHRHOT.js.map +1 -0
  31. package/dist/{chunk-QPGEBDMJ.min.js → chunk-YDAKBU6Q.js} +1 -1
  32. package/dist/chunk-YDAKBU6Q.js.map +1 -0
  33. package/dist/chunk-ZAADLBSB.js +36 -0
  34. package/dist/chunk-ZAADLBSB.js.map +1 -0
  35. package/dist/index.d.ts +7 -7
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/{index.min.js → index.js} +81 -46
  38. package/dist/ipfs/car.d.ts +11 -11
  39. package/dist/ipfs/car.d.ts.map +1 -1
  40. package/dist/ipfs/ipfs-utils.d.ts +2 -2
  41. package/dist/ipfs/ipfs-utils.d.ts.map +1 -1
  42. package/dist/ipfs.d.ts +3 -3
  43. package/dist/ipfs.d.ts.map +1 -1
  44. package/dist/{ipfs.min.js → ipfs.js} +7 -10
  45. package/dist/ipns.d.ts +1 -1
  46. package/dist/ipns.d.ts.map +1 -1
  47. package/dist/ipns.js +64 -0
  48. package/dist/ipns.js.map +1 -0
  49. package/dist/pubsub/pub-pull.d.ts +3 -3
  50. package/dist/pubsub/pub-pull.d.ts.map +1 -1
  51. package/dist/pubsub/pubsub-types.d.ts +3 -3
  52. package/dist/pubsub/pubsub-types.d.ts.map +1 -1
  53. package/dist/pubsub/snap-push.d.ts +4 -4
  54. package/dist/pubsub/snap-push.d.ts.map +1 -1
  55. package/dist/pubsub/ucan.d.ts +1 -1
  56. package/dist/pubsub/ucan.d.ts.map +1 -1
  57. package/dist/pubsub.d.ts +4 -4
  58. package/dist/pubsub.d.ts.map +1 -1
  59. package/dist/{pubsub.min.js → pubsub.js} +7 -10
  60. package/dist/query/attr-helpers.d.ts +5 -0
  61. package/dist/query/attr-helpers.d.ts.map +1 -0
  62. package/dist/query/basic.d.ts +87 -23
  63. package/dist/query/basic.d.ts.map +1 -1
  64. package/dist/query/divergences.d.ts +5 -5
  65. package/dist/query/divergences.d.ts.map +1 -1
  66. package/dist/query/entity-collection.d.ts +19 -0
  67. package/dist/query/entity-collection.d.ts.map +1 -0
  68. package/dist/query/matchers.d.ts +12 -1
  69. package/dist/query/matchers.d.ts.map +1 -1
  70. package/dist/query/memoized.d.ts +66 -0
  71. package/dist/query/memoized.d.ts.map +1 -0
  72. package/dist/query/situations.d.ts +2 -1
  73. package/dist/query/situations.d.ts.map +1 -1
  74. package/dist/query/subscribable.d.ts +111 -0
  75. package/dist/query/subscribable.d.ts.map +1 -0
  76. package/dist/query/types.d.ts +54 -14
  77. package/dist/query/types.d.ts.map +1 -1
  78. package/dist/query.d.ts +9 -5
  79. package/dist/query.d.ts.map +1 -1
  80. package/dist/{query.min.js → query.js} +55 -34
  81. package/dist/retrieve/index.d.ts +1 -1
  82. package/dist/retrieve/index.d.ts.map +1 -1
  83. package/dist/retrieve/update-thread.d.ts +3 -3
  84. package/dist/retrieve/update-thread.d.ts.map +1 -1
  85. package/dist/retrieve.d.ts +1 -1
  86. package/dist/retrieve.d.ts.map +1 -1
  87. package/dist/retrieve.js +14 -0
  88. package/dist/thread/basic.d.ts +15 -19
  89. package/dist/thread/basic.d.ts.map +1 -1
  90. package/dist/thread/filters.d.ts +8 -10
  91. package/dist/thread/filters.d.ts.map +1 -1
  92. package/dist/thread/indexes.d.ts +57 -0
  93. package/dist/thread/indexes.d.ts.map +1 -0
  94. package/dist/thread/mapped.d.ts +40 -11
  95. package/dist/thread/mapped.d.ts.map +1 -1
  96. package/dist/thread/utils.d.ts +5 -5
  97. package/dist/thread/utils.d.ts.map +1 -1
  98. package/dist/thread/writeable.d.ts +2 -2
  99. package/dist/thread/writeable.d.ts.map +1 -1
  100. package/dist/thread.d.ts +6 -5
  101. package/dist/thread.d.ts.map +1 -1
  102. package/dist/{thread.min.js → thread.js} +9 -6
  103. package/dist/types/typescript-utils.d.ts +6 -5
  104. package/dist/types/typescript-utils.d.ts.map +1 -1
  105. package/dist/types.d.ts +1 -1
  106. package/dist/types.d.ts.map +1 -1
  107. package/dist/{types.min.js → types.js} +3 -4
  108. package/dist/utils/debug-name.d.ts +13 -0
  109. package/dist/utils/debug-name.d.ts.map +1 -0
  110. package/dist/utils.d.ts +1 -1
  111. package/dist/utils.d.ts.map +1 -1
  112. package/dist/utils.js +9 -0
  113. package/package.json +32 -23
  114. package/src/applog/applog-helpers.ts +155 -0
  115. package/src/applog/applog-utils.test.ts +108 -0
  116. package/src/applog/applog-utils.ts +551 -0
  117. package/src/applog/datom-types.ts +167 -0
  118. package/src/applog/object-values.test.ts +106 -0
  119. package/src/applog.ts +3 -0
  120. package/src/blockstore/index.ts +36 -0
  121. package/src/blockstore.ts +1 -0
  122. package/src/index.ts +8 -0
  123. package/src/ipfs/car.ts +291 -0
  124. package/src/ipfs/fetch-snapshot-chain.ts +135 -0
  125. package/src/ipfs/ipfs-utils.ts +132 -0
  126. package/src/ipfs.ts +3 -0
  127. package/src/ipns/ipns-record.ts +115 -0
  128. package/src/ipns.ts +1 -0
  129. package/src/pubsub/UCAN Specs Overview.md +217 -0
  130. package/src/pubsub/connector.ts +9 -0
  131. package/src/pubsub/pub-pull.ts +31 -0
  132. package/src/pubsub/pubsub-types.ts +90 -0
  133. package/src/pubsub/snap-push.ts +278 -0
  134. package/src/pubsub/ucan-example.ts +61 -0
  135. package/src/pubsub/ucan.ts +56 -0
  136. package/src/pubsub.ts +4 -0
  137. package/src/query/attr-helpers.ts +5 -0
  138. package/src/query/basic.ts +1245 -0
  139. package/src/query/divergences.ts +50 -0
  140. package/src/query/entity-collection.ts +132 -0
  141. package/src/query/liveFilterAndMap.test.ts +102 -0
  142. package/src/query/matchers.ts +30 -0
  143. package/src/query/memoized.test.ts +151 -0
  144. package/src/query/memoized.ts +180 -0
  145. package/src/query/query-steps.ts +4 -0
  146. package/src/query/query.test.ts +538 -0
  147. package/src/query/situations.ts +261 -0
  148. package/src/query/subscribable.test.ts +245 -0
  149. package/src/query/subscribable.ts +234 -0
  150. package/src/query/types.ts +155 -0
  151. package/src/query/withoutDeleted.test.ts +204 -0
  152. package/src/query.ts +9 -0
  153. package/src/retrieve/index.ts +1 -0
  154. package/src/retrieve/update-thread.ts +248 -0
  155. package/src/retrieve.ts +1 -0
  156. package/src/test/perf/query.1m.perf.test.ts +94 -0
  157. package/src/test/perf/query.perf.test.ts +389 -0
  158. package/src/test/perf/query.realdata.perf.test.ts +182 -0
  159. package/src/thread/basic.ts +209 -0
  160. package/src/thread/filters.ts +227 -0
  161. package/src/thread/indexes.ts +256 -0
  162. package/src/thread/joinThreads.test.ts +304 -0
  163. package/src/thread/mapped.ts +226 -0
  164. package/src/thread/utils.ts +144 -0
  165. package/src/thread/writeable.ts +163 -0
  166. package/src/thread.ts +6 -0
  167. package/src/types/typescript-utils.ts +64 -0
  168. package/src/types.ts +1 -0
  169. package/src/utils/debug-name.ts +54 -0
  170. package/src/utils.ts +4 -0
  171. package/dist/chunk-2Y2PYHGR.min.js +0 -65
  172. package/dist/chunk-2Y2PYHGR.min.js.map +0 -1
  173. package/dist/chunk-5MMGBK2U.min.js +0 -1
  174. package/dist/chunk-7IDQIMQO.min.js +0 -1
  175. package/dist/chunk-BRC7LSM6.min.js.map +0 -1
  176. package/dist/chunk-COXXILXC.min.js +0 -512
  177. package/dist/chunk-COXXILXC.min.js.map +0 -1
  178. package/dist/chunk-GDX2OO7L.min.js +0 -9080
  179. package/dist/chunk-GDX2OO7L.min.js.map +0 -1
  180. package/dist/chunk-H3VQJP56.min.js.map +0 -1
  181. package/dist/chunk-HYMC7W6S.min.js +0 -1549
  182. package/dist/chunk-HYMC7W6S.min.js.map +0 -1
  183. package/dist/chunk-KEHU7HGZ.min.js +0 -5216
  184. package/dist/chunk-KEHU7HGZ.min.js.map +0 -1
  185. package/dist/chunk-KXMTKPF4.min.js.map +0 -1
  186. package/dist/chunk-PHITDXZT.min.js +0 -36
  187. package/dist/chunk-QO2KMGDN.min.js +0 -3771
  188. package/dist/chunk-QO2KMGDN.min.js.map +0 -1
  189. package/dist/chunk-QPGEBDMJ.min.js.map +0 -1
  190. package/dist/chunk-WXLCBTHX.min.js +0 -1606
  191. package/dist/chunk-WXLCBTHX.min.js.map +0 -1
  192. package/dist/ipns.min.js +0 -6419
  193. package/dist/ipns.min.js.map +0 -1
  194. package/dist/mobx/mobx-utils.d.ts +0 -82
  195. package/dist/mobx/mobx-utils.d.ts.map +0 -1
  196. package/dist/mobx.d.ts +0 -2
  197. package/dist/mobx.d.ts.map +0 -1
  198. package/dist/mobx.min.js +0 -141
  199. package/dist/retrieve.min.js +0 -17
  200. package/dist/types.min.js.map +0 -1
  201. package/dist/utils.min.js +0 -10
  202. package/dist/utils.min.js.map +0 -1
  203. /package/dist/{applog.min.js.map → applog.js.map} +0 -0
  204. /package/dist/{chunk-5MMGBK2U.min.js.map → chunk-7Z5YDQKK.js.map} +0 -0
  205. /package/dist/{chunk-7IDQIMQO.min.js.map → chunk-E46VTKTZ.js.map} +0 -0
  206. /package/dist/{chunk-PHITDXZT.min.js.map → index.js.map} +0 -0
  207. /package/dist/{index.min.js.map → ipfs.js.map} +0 -0
  208. /package/dist/{ipfs.min.js.map → pubsub.js.map} +0 -0
  209. /package/dist/{mobx.min.js.map → query.js.map} +0 -0
  210. /package/dist/{pubsub.min.js.map → retrieve.js.map} +0 -0
  211. /package/dist/{query.min.js.map → thread.js.map} +0 -0
  212. /package/dist/{retrieve.min.js.map → types.js.map} +0 -0
  213. /package/dist/{thread.min.js.map → utils.js.map} +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAA"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,cAAc,6BAA6B,CAAA"}
@@ -1,4 +1,4 @@
1
- import "./chunk-5MMGBK2U.min.js";
1
+ import "./chunk-E46VTKTZ.js";
2
2
  import {
3
3
  BOOL,
4
4
  Bool,
@@ -10,8 +10,7 @@ import {
10
10
  Str,
11
11
  arrayIfSingle,
12
12
  checkParityTB
13
- } from "./chunk-HYMC7W6S.min.js";
14
- import "./chunk-PHITDXZT.min.js";
13
+ } from "./chunk-ZAADLBSB.js";
15
14
  export {
16
15
  BOOL,
17
16
  Bool,
@@ -24,4 +23,4 @@ export {
24
23
  arrayIfSingle,
25
24
  checkParityTB
26
25
  };
27
- //# sourceMappingURL=types.min.js.map
26
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1,13 @@
1
+ import type { Applog, DatalogQueryPattern } from '../applog/datom-types.ts';
2
+ import type { Thread } from '../thread/basic.ts';
3
+ export declare const createDebugName: ({ caller, thread, pattern, args }: {
4
+ caller?: string;
5
+ thread?: Thread | Applog[];
6
+ pattern?: DatalogQueryPattern | DatalogQueryPattern[] | string;
7
+ args?: any;
8
+ }) => string;
9
+ export declare const createDebugNameObj: (args: Parameters<typeof createDebugName>[0]) => {
10
+ readonly name: string;
11
+ };
12
+ export declare function prettifyThreadName(input: string): string;
13
+ //# sourceMappingURL=debug-name.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"debug-name.d.ts","sourceRoot":"","sources":["../../src/utils/debug-name.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA;AAC3E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAA;AAEhD,eAAO,MAAM,eAAe,GAAI,mCAAmC;IAClE,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;IAC1B,OAAO,CAAC,EAAE,mBAAmB,GAAG,mBAAmB,EAAE,GAAG,MAAM,CAAA;IAC9D,IAAI,CAAC,EAAE,GAAG,CAAA;CACV,WAMA,CAAA;AAED,eAAO,MAAM,kBAAkB,GAAI,MAAM,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC,CAAC,CAAC;;CAE7E,CAAA;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAgCxD"}
package/dist/utils.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Applog, ApplogEncNoCid } from './applog/datom-types';
1
+ import { Applog, ApplogEncNoCid } from './applog/datom-types.ts';
2
2
  export declare const isTruthy: (l: Applog | ApplogEncNoCid) => boolean;
3
3
  export declare const keepTruthy: (arr: readonly any[]) => any[];
4
4
  //# sourceMappingURL=utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAE7D,eAAO,MAAM,QAAQ,GAAI,GAAG,MAAM,GAAG,cAAc,KAAG,OAAc,CAAA;AACpE,eAAO,MAAM,UAAU,GAAI,KAAK,SAAS,GAAG,EAAE,KAAG,GAAG,EAA0B,CAAA"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAA;AAEhE,eAAO,MAAM,QAAQ,GAAI,GAAG,MAAM,GAAG,cAAc,KAAG,OAAc,CAAA;AACpE,eAAO,MAAM,UAAU,GAAI,KAAK,SAAS,GAAG,EAAE,KAAG,GAAG,EAA0B,CAAA"}
package/dist/utils.js ADDED
@@ -0,0 +1,9 @@
1
+ import {
2
+ isTruthy,
3
+ keepTruthy
4
+ } from "./chunk-YDAKBU6Q.js";
5
+ export {
6
+ isTruthy,
7
+ keepTruthy
8
+ };
9
+ //# sourceMappingURL=utils.js.map
package/package.json CHANGED
@@ -1,70 +1,78 @@
1
1
  {
2
2
  "name": "@wovin/core",
3
- "version": "0.1.36",
3
+ "version": "0.2.2",
4
4
  "type": "module",
5
- "main": "./dist/index.min.js",
6
- "module": "./dist/index.min.js",
7
- "browser": "./dist/index.min.js",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.js",
7
+ "browser": "./dist/index.js",
8
8
  "types": "./dist/index.d.ts",
9
9
  "exports": {
10
10
  ".": {
11
- "import": "./dist/index.min.js",
11
+ "source": "./src/index.ts",
12
+ "import": "./dist/index.js",
12
13
  "types": "./dist/index.d.ts"
13
14
  },
14
15
  "./applog": {
15
- "import": "./dist/applog.min.js",
16
+ "source": "./src/applog.ts",
17
+ "import": "./dist/applog.js",
16
18
  "types": "./dist/applog.d.ts"
17
19
  },
18
20
  "./blockstore": {
19
- "import": "./dist/blockstore.min.js",
21
+ "source": "./src/blockstore.ts",
22
+ "import": "./dist/blockstore.js",
20
23
  "types": "./dist/blockstore.d.ts"
21
24
  },
22
25
  "./ipfs": {
23
- "import": "./dist/ipfs.min.js",
26
+ "source": "./src/ipfs.ts",
27
+ "import": "./dist/ipfs.js",
24
28
  "types": "./dist/ipfs.d.ts"
25
29
  },
26
30
  "./pubsub": {
27
- "import": "./dist/pubsub.min.js",
31
+ "source": "./src/pubsub.ts",
32
+ "import": "./dist/pubsub.js",
28
33
  "types": "./dist/pubsub.d.ts"
29
34
  },
30
35
  "./query": {
31
- "import": "./dist/query.min.js",
36
+ "source": "./src/query.ts",
37
+ "import": "./dist/query.js",
32
38
  "types": "./dist/query.d.ts"
33
39
  },
34
40
  "./retrieve": {
35
- "import": "./dist/retrieve.min.js",
41
+ "source": "./src/retrieve.ts",
42
+ "import": "./dist/retrieve.js",
36
43
  "types": "./dist/retrieve.d.ts"
37
44
  },
38
45
  "./thread": {
39
- "import": "./dist/thread.min.js",
46
+ "source": "./src/thread.ts",
47
+ "import": "./dist/thread.js",
40
48
  "types": "./dist/thread.d.ts"
41
49
  },
42
50
  "./types": {
43
- "import": "./dist/types.min.js",
51
+ "source": "./src/types.ts",
52
+ "import": "./dist/types.js",
44
53
  "types": "./dist/types.d.ts"
45
54
  },
46
55
  "./utils": {
47
- "import": "./dist/utils.min.js",
56
+ "source": "./src/utils.ts",
57
+ "import": "./dist/utils.js",
48
58
  "types": "./dist/utils.d.ts"
49
59
  },
50
- "./mobx": {
51
- "import": "./dist/mobx.min.js",
52
- "types": "./dist/mobx.d.ts"
53
- },
54
60
  "./ipns": {
55
- "import": "./dist/ipns.min.js",
61
+ "source": "./src/ipns.ts",
62
+ "import": "./dist/ipns.js",
56
63
  "types": "./dist/ipns.d.ts"
57
64
  }
58
65
  },
59
66
  "files": [
60
- "./dist/"
67
+ "./dist/",
68
+ "./src/"
61
69
  ],
62
70
  "packageManager": "pnpm",
63
71
  "esm.sh": {
64
72
  "bundle": false
65
73
  },
66
74
  "peerDependencies": {
67
- "@sinclair/typebox": "^0.31.28"
75
+ "@sinclair/typebox": ">=0.31.28 <1"
68
76
  },
69
77
  "dependencies": {
70
78
  "@ipld/car": "^5.4.2",
@@ -78,8 +86,6 @@
78
86
  "iso-signatures": "^0.5.1",
79
87
  "iso-ucan": "^0.4.2",
80
88
  "lodash-es": "^4.17.21",
81
- "mobx": "^6.13.7",
82
- "mobx-utils": "^6.1.1",
83
89
  "@libp2p/crypto": "^5.0.0",
84
90
  "ipns": "^10.1.2",
85
91
  "multiformats": "^13.4.1",
@@ -93,6 +99,7 @@
93
99
  "tsup": "^8.5.0",
94
100
  "typescript": "^5.9.2",
95
101
  "vite": "^7.1.7",
102
+ "vitest": "^3.1.3",
96
103
  "tsupconfig": "^0.0.0"
97
104
  },
98
105
  "repository": {
@@ -109,6 +116,8 @@
109
116
  "dev:code": "tsup --watch",
110
117
  "dev:types": "tsc --emitDeclarationOnly --declaration --watch",
111
118
  "lint": "eslint .",
119
+ "test": "vitest run",
120
+ "test:watch": "vitest",
112
121
  "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist",
113
122
  "pub": "npm publish --tag latest --access=public"
114
123
  }
@@ -0,0 +1,155 @@
1
+ import { Logger } from 'besonders-logger'
2
+ import { encodeApplogAndGetCid } from '../ipfs/ipfs-utils.ts'
3
+ import { lastWriteWins } from '../query/basic.ts'
4
+ import { MappedThread, rollingFilter, Thread, ThreadOnlyCurrent, type ThreadDerivation } from '../thread.ts'
5
+ import { PartialBy } from '../types/typescript-utils.ts'
6
+ import { dateNowIso, objEqualByKeys, removeDuplicateAppLogs, sortApplogsByTs } from './applog-utils.ts'
7
+ import {
8
+ Applog,
9
+ ApplogForInsert,
10
+ ApplogForInsertOptionalAgent,
11
+ ApplogNoCid,
12
+ AgentHash,
13
+ CidString,
14
+ getApplogTypeErrors,
15
+ isValidApplog,
16
+ Timestamp,
17
+ } from './datom-types.ts'
18
+
19
+ const { WARN, LOG, DEBUG, VERBOSE, ERROR } = Logger.setup(Logger.INFO) // eslint-disable-line no-unused-vars
20
+
21
+ export function ensureTsPvAndFinalizeApplogs(appLogsToInsert: ApplogForInsert[], threadForPv: Thread) {
22
+ DEBUG(`[ensureTsPvAndFinalizeApplogs] ENTER - ${appLogsToInsert.length} applogs, thread size=${threadForPv.size}`)
23
+ const ts = dateNowIso()
24
+ // const currentThread = lastWriteWins(threadForPv, { tolerateAlreadyFiltered: true }) // HACK to get `pv` from last write
25
+ const currentThread = threadForPv // HACK to not do un-cached lastWriteWins
26
+
27
+ // Within-batch (en, at) duplicates produce same-ts logs whose `pv` all point to the
28
+ // pre-batch tail (not to each other) — chain order is then ambiguous and the second
29
+ // insert is at the mercy of cid lex tie-break. Caller bug; warn so it's findable.
30
+ const seenInBatch = new Set<string>()
31
+ for (const log of appLogsToInsert) {
32
+ const key = log.en + '|' + log.at
33
+ if (seenInBatch.has(key)) {
34
+ WARN(
35
+ `[ensureTsPvAndFinalizeApplogs] within-batch duplicate (en, at)=(${log.en}, ${log.at}) — chain-pv won't link these; consider separate insert calls`,
36
+ { log },
37
+ )
38
+ break // one warning per batch is plenty
39
+ }
40
+ seenInBatch.add(key)
41
+ }
42
+
43
+ DEBUG(`[ensureTsPvAndFinalizeApplogs] About to map over applogs`)
44
+ const mapped = appLogsToInsert.map((log, idx) => {
45
+ DEBUG(`[ensureTsPvAndFinalizeApplogs] Processing applog ${idx + 1}/${appLogsToInsert.length}`)
46
+ const result = finalizeApplogForInsert(log, { ts, threadForPv: currentThread })
47
+ DEBUG(`[ensureTsPvAndFinalizeApplogs] Finalized applog ${idx + 1}/${appLogsToInsert.length}`)
48
+ return result
49
+ })
50
+ DEBUG(`[ensureTsPvAndFinalizeApplogs] EXIT - mapped ${mapped.length} applogs`)
51
+ return mapped
52
+ }
53
+ export function ensureTsPvAndFinalizeApplog(applogToInsert: ApplogForInsert, threadForPv: Thread) {
54
+ return ensureTsPvAndFinalizeApplogs([applogToInsert], threadForPv)[0]
55
+ }
56
+
57
+ export function finalizeApplogForInsert(
58
+ log: ApplogForInsert,
59
+ { ts, threadForPv }: { ts?: string; threadForPv?: Thread /*OnlyCurrent*/ } = {},
60
+ ) {
61
+ DEBUG(`[finalizeApplogForInsert] ENTER - en=${log.en}, at=${log.at}`)
62
+ DEBUG(`[finalizeApplogForInsert] About to call withTs`)
63
+ const logWithTs = withTs(log, ts ?? dateNowIso())
64
+ DEBUG(`[finalizeApplogForInsert] About to call withPvFrom (thread size=${threadForPv?.size ?? 'null'})`)
65
+ const logWithPv = withPvFrom(logWithTs, threadForPv)
66
+ DEBUG(`[finalizeApplogForInsert] About to call encodeApplogAndGetCid`)
67
+ const cid = encodeApplogAndGetCid(logWithPv).toString() as CidString
68
+ DEBUG(`[finalizeApplogForInsert] CID created: ${cid}`)
69
+ if ((log as Applog).cid && (log as Applog).cid !== cid) WARN(`[finalizeApplogForInsert] overwriting wrong CID`, { log, cid, logWithPv })
70
+ const logWithCid = { ...logWithPv, cid } satisfies Applog
71
+ DEBUG(`[finalizeApplogForInsert] About to validate applog`)
72
+ if (!isValidApplog(logWithCid)) {
73
+ throw ERROR(`Bogus Applog ${JSON.stringify(logWithCid)}`, getApplogTypeErrors(logWithCid))
74
+ }
75
+ DEBUG(`[finalizeApplogForInsert] EXIT - CID=${cid}`)
76
+ return Object.freeze(logWithCid)
77
+ }
78
+
79
+ export function hasAg(log: ApplogForInsertOptionalAgent): log is ApplogForInsert {
80
+ return !!log.ag
81
+ }
82
+ export function hasTs(log: ApplogForInsert): log is Omit<ApplogForInsert, 'ts'> & { ts: Timestamp } {
83
+ return !!log.ts
84
+ }
85
+ export function hasPv(log: ApplogForInsert): log is ApplogForInsert & { pv: string } {
86
+ return !!log.pv
87
+ }
88
+
89
+ export function withTs(log: ApplogForInsert, ts: Timestamp) {
90
+ return hasTs(log) ? log : { ...log, ts }
91
+ }
92
+ export function withAg(log: ApplogForInsertOptionalAgent, ag: AgentHash) {
93
+ return hasAg(log) ? log : { ...log, ag }
94
+ }
95
+ export function withPvFrom(log: PartialBy<ApplogNoCid, 'pv'>, thread: Thread /*OnlyCurrent*/ | null) {
96
+ DEBUG(`[withPvFrom] ENTER - en=${log.en}, at=${log.at}, hasPv=${log.pv !== undefined}`)
97
+ if (log.pv !== undefined) {
98
+ DEBUG(`[withPvFrom] EXIT early - pv already set`)
99
+ return log as ApplogNoCid // TODO: ? devMode WARN if it's different for catching bugs)
100
+ }
101
+ if (!thread) {
102
+ if (!hasPv(log)) throw ERROR(`[withPvFrom] no thread and no pv:`, log)
103
+ DEBUG(`[withPvFrom] EXIT - no thread, returning log with existing pv`)
104
+ return log // satisfies Pick<Applog, 'pv'>
105
+ } else {
106
+ const { en, at } = log
107
+ DEBUG(`[withPvFrom] About to call thread.findLast for en=${en}, at=${at}, thread.size=${thread.size}`)
108
+ const prevLog = thread.findLast(l => l.en == en && l.at == at) // HACK to not do lastWriteWins calc
109
+ DEBUG(`[withPvFrom] findLast completed, found=${!!prevLog}`)
110
+ // const prevLogs = rollingFilter(thread, { en, at }) // ? use some non-reactive filter here?
111
+ // if (prevLogs.size > 1) throw ERROR(`[withPvFrom] unexpected previous count:`, prevLogs.size, { log, prevLogs, thread }) // `thread` arg must be only current
112
+ // let prevLog = prevLogs.isEmpty ? null : prevLogs.applogs[0]
113
+ // const isMatchingPv = prevLog?.cid === log.pv
114
+ DEBUG(`[withPvFrom] About to check equality`)
115
+ if (objEqualByKeys(['en', 'at', 'vl', 'ts', 'ag'], log, prevLog)) {
116
+ throw ERROR(`[withPvFrom] Same as previous:`, { log, pv: prevLog, thread }) // bug catcher
117
+ }
118
+ // if (log.pv && !isMatchingPv) { // ineffective bc. shortcut in the beginning of this func
119
+ // WARN(`[withPvFrom] different than pre-set pv:`, { queriedPv: prevLog, logPv: log.pv })
120
+ // }
121
+ const prevLogCid = (log.pv !== undefined ? log.pv : prevLog?.cid) ?? null
122
+ DEBUG(`[withPvFrom] EXIT - prevLogCid=${prevLogCid}`)
123
+ return { ...log, pv: prevLogCid }
124
+ }
125
+ }
126
+ export function joinThreads(threads: ReadonlyArray<Thread>) {
127
+ const derivation: ThreadDerivation = {
128
+ compute(parents) {
129
+ if (parents.length < 2) DEBUG(`joinThreads with count=${parents.length}`) // ? EmptyThread
130
+ return sortApplogsByTs(
131
+ removeDuplicateAppLogs(parents.flatMap(s => {
132
+ const logs = s.applogs
133
+ if (!logs) {
134
+ ERROR(`falsy applogs of thread`, s)
135
+ throw new Error(`falsy applogs of thread`)
136
+ }
137
+ return logs
138
+ }), 'cleanup'),
139
+ )
140
+ },
141
+ mapDelta: (delta, { source, parents, state }) => ({
142
+ added: delta.added.filter(log => !state.includes(log)),
143
+ removed: delta.removed?.filter(log =>
144
+ state.includes(log) &&
145
+ !parents.some(p => p !== source && p.hasApplog(log, true))
146
+ ),
147
+ }),
148
+ }
149
+ return new MappedThread(
150
+ `join(~ ${threads.map(s => s.name).join(', ')})`,
151
+ threads,
152
+ ['?'], // HACK this basically says "we're not sure what filters are applied"
153
+ derivation,
154
+ )
155
+ }
@@ -0,0 +1,108 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import type { Applog } from './datom-types.ts'
3
+ import { compareApplogsByTs, isLaterByTsAndPv, sortApplogsByTs } from './applog-utils.ts'
4
+
5
+ const mkLog = (overrides: Partial<Applog>): Applog => ({
6
+ cid: 'cid-default' as any,
7
+ en: 'e1' as any,
8
+ at: 'name',
9
+ vl: 'value',
10
+ ts: '2026-04-25T00:00:00.000Z',
11
+ pv: null,
12
+ ag: 'agent' as any,
13
+ ...overrides,
14
+ })
15
+
16
+ describe('compareApplogsByTs', () => {
17
+ it('orders by ts asc then by cid for tiebreak (transitive)', () => {
18
+ const a = mkLog({ cid: 'cid-a' as any, ts: '2026-04-25T00:00:00.000Z' })
19
+ const b = mkLog({ cid: 'cid-b' as any, ts: '2026-04-25T00:00:00.000Z' })
20
+ const c = mkLog({ cid: 'cid-c' as any, ts: '2026-04-25T00:00:00.001Z' })
21
+ const sorted = [c, b, a].slice().sort((x, y) => compareApplogsByTs(x, y))
22
+ expect(sorted.map(l => l.cid)).toEqual(['cid-a', 'cid-b', 'cid-c'])
23
+ })
24
+ })
25
+
26
+ describe('sortApplogsByTs — chain stabilization', () => {
27
+ const sameTs = '2026-04-25T00:00:00.000Z'
28
+
29
+ it('topologically orders 3 same-ts chain logs regardless of input order', () => {
30
+ // Chain: A → B → C (same en, at, ts)
31
+ const a = mkLog({ cid: 'aaa' as any, pv: null, ts: sameTs })
32
+ const b = mkLog({ cid: 'bbb' as any, pv: 'aaa' as any, ts: sameTs })
33
+ const c = mkLog({ cid: 'ccc' as any, pv: 'bbb' as any, ts: sameTs })
34
+
35
+ // Note: cid lex order is a, b, c — so for asc, the cid-only sort would coincidentally
36
+ // produce the right order. To prove chain awareness, use cids whose lex order differs.
37
+ const z = mkLog({ cid: 'zzz' as any, pv: null, ts: sameTs })
38
+ const y = mkLog({ cid: 'yyy' as any, pv: 'zzz' as any, ts: sameTs })
39
+ const x = mkLog({ cid: 'xxx' as any, pv: 'yyy' as any, ts: sameTs })
40
+
41
+ // asc: chain root first → z, y, x
42
+ const ascOrder = sortApplogsByTs([x, y, z].slice())
43
+ expect(ascOrder.map(l => l.cid)).toEqual(['zzz', 'yyy', 'xxx'])
44
+
45
+ // scrambled
46
+ const scrambled = sortApplogsByTs([y, z, x].slice())
47
+ expect(scrambled.map(l => l.cid)).toEqual(['zzz', 'yyy', 'xxx'])
48
+
49
+ // desc: chain tail first → x, y, z
50
+ const descOrder = sortApplogsByTs([z, x, y].slice(), 'desc')
51
+ expect(descOrder.map(l => l.cid)).toEqual(['xxx', 'yyy', 'zzz'])
52
+ })
53
+
54
+ it('does not reshuffle different (en, at) sub-groups within a same-ts cluster', () => {
55
+ // Same ts, two sub-groups, each with a chain
56
+ const a1 = mkLog({ cid: 'a1' as any, en: 'e1' as any, at: 'name', pv: null, ts: sameTs })
57
+ const a2 = mkLog({ cid: 'a2' as any, en: 'e1' as any, at: 'name', pv: 'a1' as any, ts: sameTs })
58
+ const b1 = mkLog({ cid: 'b1' as any, en: 'e2' as any, at: 'name', pv: null, ts: sameTs })
59
+ const b2 = mkLog({ cid: 'b2' as any, en: 'e2' as any, at: 'name', pv: 'b1' as any, ts: sameTs })
60
+
61
+ const sorted = sortApplogsByTs([a2, b2, a1, b1].slice())
62
+ // Each (en, at) sub-group must be in chain order. Cross-group order = cid-lex (preserved).
63
+ const e1Logs = sorted.filter(l => l.en === 'e1')
64
+ const e2Logs = sorted.filter(l => l.en === 'e2')
65
+ expect(e1Logs.map(l => l.cid)).toEqual(['a1', 'a2'])
66
+ expect(e2Logs.map(l => l.cid)).toEqual(['b1', 'b2'])
67
+ })
68
+
69
+ it('falls back to cid-lex order for non-chain-related same-ts logs', () => {
70
+ const x = mkLog({ cid: 'xxx' as any, pv: null, ts: sameTs })
71
+ const y = mkLog({ cid: 'yyy' as any, pv: null, ts: sameTs })
72
+ const sorted = sortApplogsByTs([y, x].slice())
73
+ expect(sorted.map(l => l.cid)).toEqual(['xxx', 'yyy'])
74
+ })
75
+ })
76
+
77
+ describe('isLaterByTsAndPv', () => {
78
+ const sameTs = '2026-04-25T00:00:00.000Z'
79
+
80
+ it('uses ts when ts differs', () => {
81
+ const earlier = mkLog({ cid: 'a' as any, ts: '2026-04-25T00:00:00.000Z' })
82
+ const later = mkLog({ cid: 'b' as any, ts: '2026-04-25T00:00:00.001Z' })
83
+ expect(isLaterByTsAndPv(later, earlier)).toBe(true)
84
+ expect(isLaterByTsAndPv(earlier, later)).toBe(false)
85
+ })
86
+
87
+ it('uses pv chain for same-ts same (en, at) logs', () => {
88
+ const a = mkLog({ cid: 'a' as any, pv: null, ts: sameTs })
89
+ const b = mkLog({ cid: 'b' as any, pv: 'a' as any, ts: sameTs })
90
+ expect(isLaterByTsAndPv(b, a)).toBe(true) // b chains after a
91
+ expect(isLaterByTsAndPv(a, b)).toBe(false)
92
+ })
93
+
94
+ it('falls back to cid-lex for same-ts unrelated logs', () => {
95
+ const x = mkLog({ cid: 'x' as any, pv: null, ts: sameTs })
96
+ const y = mkLog({ cid: 'y' as any, pv: null, ts: sameTs })
97
+ expect(isLaterByTsAndPv(y, x)).toBe(true)
98
+ expect(isLaterByTsAndPv(x, y)).toBe(false)
99
+ })
100
+
101
+ it('does not use pv when (en, at) differs', () => {
102
+ // Edge case: cid coincidence across keys must not be interpreted as a chain link
103
+ const a = mkLog({ cid: 'shared' as any, en: 'e1' as any, at: 'name', pv: null, ts: sameTs })
104
+ const b = mkLog({ cid: 'other' as any, en: 'e2' as any, at: 'name', pv: 'shared' as any, ts: sameTs })
105
+ // Same-ts, different (en, at) — should fall through to cid-lex
106
+ expect(isLaterByTsAndPv(b, a)).toBe('other'.localeCompare('shared') > 0)
107
+ })
108
+ })