@wovin/core 0.0.0-ciao-mobx-955482e8

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 (180) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +3 -0
  3. package/dist/applog/applog-helpers.d.ts +47 -0
  4. package/dist/applog/applog-helpers.d.ts.map +1 -0
  5. package/dist/applog/applog-utils.d.ts +57 -0
  6. package/dist/applog/applog-utils.d.ts.map +1 -0
  7. package/dist/applog/datom-types.d.ts +128 -0
  8. package/dist/applog/datom-types.d.ts.map +1 -0
  9. package/dist/applog.d.ts +4 -0
  10. package/dist/applog.d.ts.map +1 -0
  11. package/dist/applog.js +101 -0
  12. package/dist/applog.js.map +1 -0
  13. package/dist/blockstore/index.d.ts +21 -0
  14. package/dist/blockstore/index.d.ts.map +1 -0
  15. package/dist/blockstore.d.ts +2 -0
  16. package/dist/blockstore.d.ts.map +1 -0
  17. package/dist/blockstore.js +24 -0
  18. package/dist/blockstore.js.map +1 -0
  19. package/dist/chunk-6MQKRL6W.js +86 -0
  20. package/dist/chunk-6MQKRL6W.js.map +1 -0
  21. package/dist/chunk-7MW34UEO.js +40 -0
  22. package/dist/chunk-7MW34UEO.js.map +1 -0
  23. package/dist/chunk-7Z5YDQKK.js +1 -0
  24. package/dist/chunk-7Z5YDQKK.js.map +1 -0
  25. package/dist/chunk-CY4NLISM.js +144 -0
  26. package/dist/chunk-CY4NLISM.js.map +1 -0
  27. package/dist/chunk-E46VTKTZ.js +1 -0
  28. package/dist/chunk-E46VTKTZ.js.map +1 -0
  29. package/dist/chunk-O43W7UW6.js +434 -0
  30. package/dist/chunk-O43W7UW6.js.map +1 -0
  31. package/dist/chunk-XIQSYEV3.js +1604 -0
  32. package/dist/chunk-XIQSYEV3.js.map +1 -0
  33. package/dist/chunk-XVGW4QC3.js +55 -0
  34. package/dist/chunk-XVGW4QC3.js.map +1 -0
  35. package/dist/chunk-YDAKBU6Q.js +9 -0
  36. package/dist/chunk-YDAKBU6Q.js.map +1 -0
  37. package/dist/chunk-ZAADLBSB.js +36 -0
  38. package/dist/chunk-ZAADLBSB.js.map +1 -0
  39. package/dist/chunk-ZXCJRYD7.js +883 -0
  40. package/dist/chunk-ZXCJRYD7.js.map +1 -0
  41. package/dist/index.d.ts +8 -0
  42. package/dist/index.d.ts.map +1 -0
  43. package/dist/index.js +354 -0
  44. package/dist/index.js.map +1 -0
  45. package/dist/ipfs/car.d.ts +59 -0
  46. package/dist/ipfs/car.d.ts.map +1 -0
  47. package/dist/ipfs/fetch-snapshot-chain.d.ts +32 -0
  48. package/dist/ipfs/fetch-snapshot-chain.d.ts.map +1 -0
  49. package/dist/ipfs/ipfs-utils.d.ts +35 -0
  50. package/dist/ipfs/ipfs-utils.d.ts.map +1 -0
  51. package/dist/ipfs.d.ts +4 -0
  52. package/dist/ipfs.d.ts.map +1 -0
  53. package/dist/ipfs.js +60 -0
  54. package/dist/ipfs.js.map +1 -0
  55. package/dist/ipns/ipns-record.d.ts +34 -0
  56. package/dist/ipns/ipns-record.d.ts.map +1 -0
  57. package/dist/ipns.d.ts +2 -0
  58. package/dist/ipns.d.ts.map +1 -0
  59. package/dist/ipns.js +64 -0
  60. package/dist/ipns.js.map +1 -0
  61. package/dist/pubsub/connector.d.ts +9 -0
  62. package/dist/pubsub/connector.d.ts.map +1 -0
  63. package/dist/pubsub/pub-pull.d.ts +14 -0
  64. package/dist/pubsub/pub-pull.d.ts.map +1 -0
  65. package/dist/pubsub/pubsub-types.d.ts +72 -0
  66. package/dist/pubsub/pubsub-types.d.ts.map +1 -0
  67. package/dist/pubsub/snap-push.d.ts +41 -0
  68. package/dist/pubsub/snap-push.d.ts.map +1 -0
  69. package/dist/pubsub/ucan-example.d.ts +3 -0
  70. package/dist/pubsub/ucan-example.d.ts.map +1 -0
  71. package/dist/pubsub/ucan.d.ts +16 -0
  72. package/dist/pubsub/ucan.d.ts.map +1 -0
  73. package/dist/pubsub.d.ts +5 -0
  74. package/dist/pubsub.d.ts.map +1 -0
  75. package/dist/pubsub.js +31 -0
  76. package/dist/pubsub.js.map +1 -0
  77. package/dist/query/basic.d.ts +105 -0
  78. package/dist/query/basic.d.ts.map +1 -0
  79. package/dist/query/divergences.d.ts +12 -0
  80. package/dist/query/divergences.d.ts.map +1 -0
  81. package/dist/query/matchers.d.ts +4 -0
  82. package/dist/query/matchers.d.ts.map +1 -0
  83. package/dist/query/memoized.d.ts +66 -0
  84. package/dist/query/memoized.d.ts.map +1 -0
  85. package/dist/query/query-steps.d.ts +4 -0
  86. package/dist/query/query-steps.d.ts.map +1 -0
  87. package/dist/query/situations.d.ts +80 -0
  88. package/dist/query/situations.d.ts.map +1 -0
  89. package/dist/query/subscribable.d.ts +102 -0
  90. package/dist/query/subscribable.d.ts.map +1 -0
  91. package/dist/query/types.d.ts +70 -0
  92. package/dist/query/types.d.ts.map +1 -0
  93. package/dist/query.d.ts +8 -0
  94. package/dist/query.d.ts.map +1 -0
  95. package/dist/query.js +108 -0
  96. package/dist/query.js.map +1 -0
  97. package/dist/retrieve/index.d.ts +2 -0
  98. package/dist/retrieve/index.d.ts.map +1 -0
  99. package/dist/retrieve/update-thread.d.ts +64 -0
  100. package/dist/retrieve/update-thread.d.ts.map +1 -0
  101. package/dist/retrieve.d.ts +2 -0
  102. package/dist/retrieve.d.ts.map +1 -0
  103. package/dist/retrieve.js +14 -0
  104. package/dist/retrieve.js.map +1 -0
  105. package/dist/thread/basic.d.ts +60 -0
  106. package/dist/thread/basic.d.ts.map +1 -0
  107. package/dist/thread/filters.d.ts +47 -0
  108. package/dist/thread/filters.d.ts.map +1 -0
  109. package/dist/thread/mapped.d.ts +31 -0
  110. package/dist/thread/mapped.d.ts.map +1 -0
  111. package/dist/thread/utils.d.ts +23 -0
  112. package/dist/thread/utils.d.ts.map +1 -0
  113. package/dist/thread/writeable.d.ts +41 -0
  114. package/dist/thread/writeable.d.ts.map +1 -0
  115. package/dist/thread.d.ts +6 -0
  116. package/dist/thread.d.ts.map +1 -0
  117. package/dist/thread.js +54 -0
  118. package/dist/thread.js.map +1 -0
  119. package/dist/types/typescript-utils.d.ts +34 -0
  120. package/dist/types/typescript-utils.d.ts.map +1 -0
  121. package/dist/types.d.ts +2 -0
  122. package/dist/types.d.ts.map +1 -0
  123. package/dist/types.js +26 -0
  124. package/dist/types.js.map +1 -0
  125. package/dist/utils/debug-name.d.ts +13 -0
  126. package/dist/utils/debug-name.d.ts.map +1 -0
  127. package/dist/utils.d.ts +4 -0
  128. package/dist/utils.d.ts.map +1 -0
  129. package/dist/utils.js +9 -0
  130. package/dist/utils.js.map +1 -0
  131. package/package.json +110 -0
  132. package/src/applog/applog-helpers.ts +150 -0
  133. package/src/applog/applog-utils.ts +398 -0
  134. package/src/applog/datom-types.ts +148 -0
  135. package/src/applog.ts +3 -0
  136. package/src/blockstore/index.ts +36 -0
  137. package/src/blockstore.ts +1 -0
  138. package/src/index.ts +8 -0
  139. package/src/ipfs/car.ts +291 -0
  140. package/src/ipfs/fetch-snapshot-chain.ts +135 -0
  141. package/src/ipfs/ipfs-utils.ts +132 -0
  142. package/src/ipfs.ts +3 -0
  143. package/src/ipns/ipns-record.ts +115 -0
  144. package/src/ipns.ts +1 -0
  145. package/src/pubsub/UCAN Specs Overview.md +217 -0
  146. package/src/pubsub/connector.ts +9 -0
  147. package/src/pubsub/pub-pull.ts +31 -0
  148. package/src/pubsub/pubsub-types.ts +90 -0
  149. package/src/pubsub/snap-push.ts +277 -0
  150. package/src/pubsub/ucan-example.ts +61 -0
  151. package/src/pubsub/ucan.ts +56 -0
  152. package/src/pubsub.ts +4 -0
  153. package/src/query/basic.ts +1061 -0
  154. package/src/query/divergences.ts +50 -0
  155. package/src/query/matchers.ts +8 -0
  156. package/src/query/memoized.test.ts +151 -0
  157. package/src/query/memoized.ts +180 -0
  158. package/src/query/query-steps.ts +4 -0
  159. package/src/query/query.test.ts +536 -0
  160. package/src/query/situations.ts +261 -0
  161. package/src/query/subscribable.test.ts +245 -0
  162. package/src/query/subscribable.ts +225 -0
  163. package/src/query/types.ts +155 -0
  164. package/src/query.ts +7 -0
  165. package/src/retrieve/index.ts +1 -0
  166. package/src/retrieve/update-thread.ts +248 -0
  167. package/src/retrieve.ts +1 -0
  168. package/src/test/perf/query.1m.perf.test.ts +94 -0
  169. package/src/test/perf/query.perf.test.ts +389 -0
  170. package/src/test/perf/query.realdata.perf.test.ts +175 -0
  171. package/src/thread/basic.ts +209 -0
  172. package/src/thread/filters.ts +234 -0
  173. package/src/thread/mapped.ts +166 -0
  174. package/src/thread/utils.ts +146 -0
  175. package/src/thread/writeable.ts +163 -0
  176. package/src/thread.ts +5 -0
  177. package/src/types/typescript-utils.ts +64 -0
  178. package/src/types.ts +1 -0
  179. package/src/utils/debug-name.ts +54 -0
  180. package/src/utils.ts +4 -0
@@ -0,0 +1,146 @@
1
+ import { Logger } from 'besonders-logger'
2
+ import { curry, debounce, partial, pull, uniq, uniqBy, uniqWith } from 'lodash-es'
3
+
4
+ import { CID } from 'multiformats'
5
+ import { ensureTsPvAndFinalizeApplogs, joinThreads } from '../applog/applog-helpers.ts'
6
+ import { compareApplogsByEnAt, compareApplogsByTs, objEqualByKeys } from '../applog/applog-utils.ts'
7
+ import { Applog, ApplogForInsert, CidString, EntityID } from '../applog/datom-types.ts'
8
+ import { Thread } from './basic.ts'
9
+ import { MappedThread } from './mapped.ts'
10
+ import { ThreadInMemory, WriteableThread } from './writeable.ts'
11
+
12
+ const { WARN, LOG, DEBUG, VERBOSE, ERROR } = Logger.setup(Logger.INFO) // eslint-disable-line no-unused-vars
13
+
14
+ export function entityCount(thread: Thread) {
15
+ return allEntityIDs(thread).size // at some point: index size?
16
+ }
17
+ export function allEntityIDs(thread: Thread) {
18
+ return accumulateLogsToSet(thread, log => log.en)
19
+ }
20
+
21
+ function accumulateLogsToSet<ACC>(
22
+ threadOrLogs: Thread | Applog[],
23
+ callback: (log: Applog, acc: Set<ACC>) => ACC | undefined,
24
+ ) {
25
+ const logs = threadOrLogs instanceof Thread ? threadOrLogs.applogs : threadOrLogs
26
+ const set = new Set<ACC>()
27
+ for (const log of logs) {
28
+ set.add(callback(log, set))
29
+ }
30
+ return set
31
+ }
32
+
33
+ export function debounceWrites(
34
+ thread: Thread,
35
+ wait = 700,
36
+ removeDuplicatesWith = compareApplogsByEnAt,
37
+ ) {
38
+ if (thread.readOnly) throw ERROR(`[debounceWrites] but readOnly thread`, thread.name)
39
+ let insertQueue: Array<Applog | ApplogForInsert> = []
40
+
41
+ const debouncedCommit = debounce(() => {
42
+ WARN(`Debounce tail`, { thread, mappedThread, insertQueue })
43
+ const toInsert = ensureTsPvAndFinalizeApplogs(
44
+ // ? uniq, sure - but which one is used? (update: seems the first one, so reverse)
45
+ uniqWith(insertQueue.reverse(), removeDuplicatesWith),
46
+ thread,
47
+ )
48
+ thread.insertRaw(toInsert)
49
+ insertQueue.splice(0, insertQueue.length) // clear queue
50
+ }, wait)
51
+ const handleInsert = (applogs: Applog[] | ApplogForInsert[]) => {
52
+ DEBUG(`Debounce input:`, applogs, { thread, mappedThread, insertQueue })
53
+ insertQueue.push(...applogs)
54
+ debouncedCommit()
55
+ return null // don't insert anything
56
+ }
57
+
58
+ const mappedThread = MappedThread.mapWrites(
59
+ thread,
60
+ `Debounce(${wait})`,
61
+ handleInsert,
62
+ )
63
+ return mappedThread
64
+ }
65
+
66
+ /**
67
+ * @param deduplicateHoldItemsWith called with (heldLog, realLog), if it returns true, the held log is skipped
68
+ * @param onFirstWrite called with held logs about to be inserted, if it returns an array, those logs will be inserted instead
69
+ */
70
+ export function holdTillFirstWrite(
71
+ thread: Thread,
72
+ applogsToHold: ApplogForInsert[],
73
+ opts: {
74
+ deduplicateHoldItemsWith?: typeof compareApplogsByEnAt
75
+ onFirstWrite: (heldLogsToInsert: Applog[]) => Applog[] | undefined
76
+ },
77
+ ) {
78
+ DEBUG(`[holdTillFirstWrite] holding logs:`, { applogsToHold })
79
+ if (thread.readOnly) throw ERROR(`[holdTillFirstWrite] but readOnly thread`, thread.nameAndSizeUntracked)
80
+ const heldLogs = ensureTsPvAndFinalizeApplogs([...applogsToHold], thread)
81
+ const heldThread = ThreadInMemory.fromArray(heldLogs)
82
+
83
+ let hasInserted = false
84
+ const handleInsert = (realApplogs: Applog[] | ApplogForInsert[]) => {
85
+ if (hasInserted) return realApplogs
86
+ hasInserted = true
87
+
88
+ let toInsert = [...heldLogs]
89
+ // heldLogs.splice(0, heldLogs.length) // ? joinThread could take care of this
90
+ if (opts.deduplicateHoldItemsWith) {
91
+ toInsert = toInsert.filter(heldLog =>
92
+ // some duplicate? so don't insert
93
+ !realApplogs.some(realLog => opts.deduplicateHoldItemsWith(heldLog, realLog))
94
+ )
95
+ }
96
+ if (opts.onFirstWrite) {
97
+ const callbackResult = opts.onFirstWrite(toInsert)
98
+ if (callbackResult !== undefined) {
99
+ toInsert = callbackResult
100
+ }
101
+ }
102
+ DEBUG(`[holdTillFirstWrite] adding hold logs:`, { applogsToHold, heldLogs, toInsert, realApplogs })
103
+ return [...toInsert, ...realApplogs]
104
+ }
105
+ const joinedThread = joinThreads([thread, heldThread])
106
+ return new MappedThread(
107
+ `HoldTillFirstWrite[${applogsToHold.length}]<${thread.nameAndSizeUntracked}>`,
108
+ joinedThread,
109
+ thread.filters,
110
+ // @ts-expect-error ? what's the proper way
111
+ joinedThread.applogs,
112
+ null,
113
+ (applogs) => {
114
+ const logsToInsert = handleInsert(applogs)
115
+ thread.insert(logsToInsert)
116
+ return null
117
+ },
118
+ )
119
+ }
120
+
121
+ export function getAgents(thread: Thread) {
122
+ return uniq(thread.map(l => l.ag))
123
+ }
124
+
125
+ /**
126
+ * Returns applogs NOT contained in the exclusion set.
127
+ * Uses CID-based comparison (consistent with removeDuplicateAppLogs).
128
+ */
129
+ export const excludeApplogsContainedIn = (
130
+ applogs: readonly Applog[],
131
+ exclude: Thread | readonly Applog[] | Set<CID | CidString>,
132
+ ): Applog[] => {
133
+ const excludeCids: Set<string> = exclude instanceof Set
134
+ ? new Set([...exclude].map(c => c.toString()))
135
+ : exclude instanceof Thread
136
+ ? new Set(exclude.applogsCids)
137
+ : new Set(exclude.map(a => a.cid))
138
+
139
+ return applogs.filter(applog => {
140
+ if (!applog.cid) {
141
+ ERROR(`applog with missing CID`, applog)
142
+ throw new Error(`applog with missing CID`)
143
+ }
144
+ return !excludeCids.has(applog.cid)
145
+ })
146
+ }
@@ -0,0 +1,163 @@
1
+ import { Logger } from 'besonders-logger'
2
+ import { ensureTsPvAndFinalizeApplogs } from '../applog/applog-helpers.ts'
3
+ import { isTsBefore, removeDuplicateAppLogs, sortApplogsByTs } from '../applog/applog-utils.ts'
4
+ import { type Applog, ApplogForInsert, CidString, isValidApplog } from '../applog/datom-types.ts'
5
+ import { Thread } from './basic.ts'
6
+
7
+ const { WARN, LOG, DEBUG, VERBOSE, ERROR } = Logger.setup(Logger.INFO) // eslint-disable-line no-unused-vars
8
+
9
+ export abstract class WriteableThread extends Thread {
10
+ constructor(
11
+ name: string,
12
+ // parents: Thread | readonly Thread[] | null, // ? would this ever be a thing
13
+ applogs: Applog[] = [],
14
+ filters: readonly string[],
15
+ ) {
16
+ super(name, null, filters, applogs)
17
+ }
18
+
19
+ public purge(cidsToPurge: CidString[]) {
20
+ const beforeCount = this.applogs.length
21
+ // HACK setting readonly
22
+ this._applogs = this.applogs.filter(log => !(cidsToPurge.includes(log.cid)))
23
+ return beforeCount - this.applogs.length
24
+ }
25
+ public insert(appLogsToInsert: ApplogForInsert[]) {
26
+ DEBUG(`[WriteableThread.insert] ENTER - ${appLogsToInsert.length} applogs for thread "${this.name}"`)
27
+ DEBUG(`[WriteableThread.insert] About to call ensureTsPvAndFinalizeApplogs`)
28
+ const mapped = ensureTsPvAndFinalizeApplogs(appLogsToInsert, this)
29
+ DEBUG(`[WriteableThread.insert] ensureTsPvAndFinalizeApplogs completed, mapped=${mapped.length} applogs`)
30
+ DEBUG(`[WriteableThread.insert] About to call insertRaw`)
31
+ const result = this.insertRaw(mapped)
32
+ DEBUG(`[WriteableThread.insert] insertRaw completed`)
33
+ return result
34
+ }
35
+
36
+ /**
37
+ * Insert only applogs not already in this thread.
38
+ * @param byRef If true, compares by reference; if false, compares by CID (default)
39
+ * @returns The applogs that were actually inserted
40
+ */
41
+ public insertMissing(appLogsToInsert: readonly Applog[], byRef = false): Applog[] {
42
+ const missing = appLogsToInsert.filter(log => !this.hasApplog(log, byRef))
43
+ if (missing.length === 0) {
44
+ VERBOSE(`[insertMissing] no missing applogs`)
45
+ return []
46
+ }
47
+ return this.insertRaw(missing) ?? []
48
+ }
49
+
50
+ /**
51
+ * Insert raw applogs directly into the thread.
52
+ *
53
+ * STRICT VALIDATION: This method throws errors for:
54
+ * - Duplicate applogs in input array (programming error)
55
+ * - Invalid applogs (missing required fields)
56
+ * - Applogs already in thread (programming error)
57
+ *
58
+ * For external imports where duplicates are expected, use removeDuplicateAppLogs(..., 'cleanup')
59
+ * before calling this method.
60
+ *
61
+ * @param appLogsToInsert Must be deduplicated and validated. Needs to be mutable because it will be sorted
62
+ * (and if you need to clone it, so do it when you need to) - this is weird as TS is slathering type safety onto ducks
63
+ * @throws Error if validation fails
64
+ */
65
+ public insertRaw(appLogsToInsert: Applog[]) {
66
+ DEBUG(`[WriteableThread.insertRaw] ENTER - ${appLogsToInsert.length} applogs for thread "${this.name}"`)
67
+ DEBUG(`[WriteableThread.insertRaw] About to deduplicate`)
68
+ const deduplicated = removeDuplicateAppLogs(appLogsToInsert, 'safety')
69
+ if (deduplicated.length !== appLogsToInsert.length) {
70
+ throw ERROR(`[insertRaw] duplicate applogs passed: ${appLogsToInsert.length - deduplicated.length}`, {
71
+ appLogsToInsert,
72
+ deduplicated,
73
+ })
74
+ }
75
+ DEBUG(`[WriteableThread.insertRaw] Deduplication done`)
76
+
77
+ DEBUG(`[WriteableThread.insertRaw] About to validate`)
78
+ const bogus = appLogsToInsert.filter(log => !isValidApplog(log))
79
+ if (bogus.length) {
80
+ throw ERROR(`[insertRaw] bogus applogs passed: ${bogus.length}`, { bogus })
81
+ }
82
+ DEBUG(`[WriteableThread.insertRaw] Validation done`)
83
+
84
+ DEBUG(`[WriteableThread.insertRaw] About to check for existing`)
85
+ const existing = appLogsToInsert.filter(log => this.hasApplog(log, false))
86
+ if (existing.length) {
87
+ throw ERROR(`[insertRaw] already existing applogs passed: ${existing.length}`, { existing })
88
+ }
89
+ DEBUG(`[WriteableThread.insertRaw] Existing check done`)
90
+
91
+ if (!appLogsToInsert.length) {
92
+ WARN('[insertRaw] skipping empty insert empty logs array')
93
+ return
94
+ }
95
+
96
+ ;(!this.hasParents && !(this instanceof ThreadInMemory) ? LOG : DEBUG)(
97
+ 'Inserting:',
98
+ appLogsToInsert.length === 1 ? appLogsToInsert[0] : appLogsToInsert,
99
+ { ds: this },
100
+ )
101
+ DEBUG(`[WriteableThread.insertRaw] About to sort applogs`)
102
+ sortApplogsByTs(appLogsToInsert)
103
+ const sortNeeded = this._applogs.length && isTsBefore(appLogsToInsert[0], this._applogs[this._applogs.length - 1])
104
+ DEBUG(`[WriteableThread.insertRaw] About to push to _applogs array`)
105
+ // Chunked push: spread is fast but causes stack overflow with 100k+ items
106
+ // Chunking keeps us under the limit while using optimized push()
107
+ const CHUNK_SIZE = 50_000
108
+ for (let i = 0; i < appLogsToInsert.length; i += CHUNK_SIZE) {
109
+ this._applogs.push(...appLogsToInsert.slice(i, i + CHUNK_SIZE))
110
+ }
111
+ if (sortNeeded) {
112
+ DEBUG(`[WriteableThread.insertRaw] About to sort _applogs (sortNeeded=true)`)
113
+ sortApplogsByTs(this._applogs)
114
+ }
115
+ DEBUG(`[WriteableThread.insertRaw] About to notify subscribers`)
116
+ this.notifySubscribers({ added: appLogsToInsert, removed: null })
117
+ DEBUG(`[WriteableThread.insertRaw] Subscribers notified`)
118
+
119
+ // ? persist sync
120
+ DEBUG(`[WriteableThread.insertRaw] About to call persist (void - not awaited)`)
121
+ void this.persist(appLogsToInsert)
122
+ DEBUG(`[WriteableThread.insertRaw] EXIT - returning ${appLogsToInsert.length} applogs`)
123
+ return appLogsToInsert
124
+ }
125
+
126
+ get readOnly() {
127
+ return false
128
+ }
129
+
130
+ protected abstract persist(logs: Applog[]): Promise<void>
131
+ }
132
+ export class ThreadInMemory extends WriteableThread {
133
+ static empty(name?: string) {
134
+ return ThreadInMemory.fromArray([], name ?? 'empty in-memory', false)
135
+ }
136
+ static fromArray(applogs: Applog[], name?: string, readOnly = false) {
137
+ return new ThreadInMemory(name ?? 'in-memory', applogs, [], readOnly)
138
+ }
139
+ static fromReadOnlyArray(applogs: readonly Applog[], name?: string) {
140
+ // @ts-expect-error readonly conditional drama
141
+ return new ThreadInMemory(name ?? 'in-memory', applogs, [], true)
142
+ }
143
+
144
+ constructor(
145
+ name: string,
146
+ applogs: Applog[],
147
+ filters: readonly string[],
148
+ readonly _readOnly: boolean,
149
+ ) {
150
+ super(name, applogs, filters)
151
+ }
152
+
153
+ get readOnly() {
154
+ return this._readOnly
155
+ }
156
+
157
+ protected async persist(logs: Applog[]) {
158
+ VERBOSE(`[InMem.persist] no persist for`, logs)
159
+ if (this.readOnly) {
160
+ throw ERROR(`[persist] called for readOnly thread`)
161
+ }
162
+ }
163
+ }
package/src/thread.ts ADDED
@@ -0,0 +1,5 @@
1
+ export * from './thread/basic.ts'
2
+ export * from './thread/filters.ts'
3
+ export * from './thread/mapped.ts'
4
+ export * from './thread/writeable.ts'
5
+ export * from './thread/utils.ts'
@@ -0,0 +1,64 @@
1
+ import { Type as T } from '@sinclair/typebox'
2
+ // import { Thread } from '../thread/basic.ts'
3
+
4
+ // ! circular import
5
+ // export const ThreadTB = TypeSystem.Type<Thread>('Thread', (options, value) => {
6
+ // return value instanceof Thread
7
+ // })()
8
+ const { String: StringTB, Optional: OptionalTB, Boolean: BooleanTB, Object: ObjectTB, Number: NumberTB } = T
9
+ export const Str: typeof StringTB = StringTB.bind(T)
10
+ export const Num: typeof NumberTB = NumberTB.bind(T)
11
+ export const Obj: typeof ObjectTB = ObjectTB.bind(T)
12
+ export const Opt: typeof OptionalTB = OptionalTB.bind(T)
13
+ export const Bool: typeof BooleanTB = BooleanTB.bind(T)
14
+
15
+ export const STR: ReturnType<typeof T.String> = Str()
16
+ export const NUM: ReturnType<typeof T.Number> = Num()
17
+ export const BOOL: ReturnType<typeof T.Boolean> = Bool()
18
+
19
+ export type GenericObject = Record<string, any>
20
+ export type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>
21
+ export type PartialBy<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>
22
+ export type PromiseType<T extends Promise<any>> = T extends Promise<infer U> ? U : never
23
+
24
+ export type CoerceToString<T> = T extends string ? T : never
25
+
26
+ /**
27
+ * Define nominal type of U based on type of T. Similar to Opaque types in Flow
28
+ */
29
+ export type Tagged<T, Tag> = T & { tag?: Tag }
30
+
31
+ // export interface Service {
32
+ // endpoint?: URL
33
+ // token: string
34
+ // rateLimiter?: RateLimiter
35
+ // fetch?: typeof _fetch
36
+ // }
37
+
38
+ // https://stackoverflow.com/a/76276541
39
+ export type LastElementOf<T extends readonly unknown[]> = T extends readonly [...unknown[], infer Last] ? Last : never
40
+
41
+ export function checkParityTB() {
42
+ /* Most examples are constantly calling Type.*() - needed to check if its really needed
43
+ https://github.com/sinclairzx81/typebox/issues/587#issuecomment-1712457623
44
+ */
45
+ const s1 = Str()
46
+ const s2 = Str()
47
+ const n1 = Num()
48
+ const n2 = Num()
49
+ console.log({ s1, s2, n1, n2 })
50
+ }
51
+
52
+ /** solidjs Setter requires returning something, which I often don't */
53
+ export type GenericSetter<T> = (newValue: T) => void
54
+
55
+ export type ArrayOrSingle<T> = T | T[] | readonly T[]
56
+ export function arrayIfSingle<T>(arrOrSingle: ArrayOrSingle<T>): readonly T[] {
57
+ if (Array.isArray(arrOrSingle)) return arrOrSingle
58
+ else return [arrOrSingle] as readonly T[]
59
+ }
60
+ export type ArrayType<T> = T extends (infer U)[] ? U : T
61
+ export type ArrayElementType<T> = T extends readonly (infer U)[] ? Extract<U, string> : never
62
+ export type ConstructorType<T, S = {}> = {
63
+ new(...args: any[]): T
64
+ } & S
package/src/types.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './types/typescript-utils.ts'
@@ -0,0 +1,54 @@
1
+ import stringify from 'safe-stable-stringify'
2
+ import type { Applog, DatalogQueryPattern } from '../applog/datom-types.ts'
3
+ import type { Thread } from '../thread/basic.ts'
4
+
5
+ export const createDebugName = ({ caller, thread, pattern, args }: {
6
+ caller?: string
7
+ thread?: Thread | Applog[]
8
+ pattern?: DatalogQueryPattern | DatalogQueryPattern[] | string
9
+ args?: any
10
+ }) => {
11
+ args = args || pattern
12
+ const str = `${(!Array.isArray(thread) && thread?.name) ? thread.name + ' | ' : ''}`
13
+ + `${caller ?? 'caller?'}`
14
+ + `${args ? `{${typeof args === 'string' ? args : stringify(args)}}` : ''}`
15
+ return str
16
+ }
17
+
18
+ export const createDebugNameObj = (args: Parameters<typeof createDebugName>[0]) => {
19
+ return { name: createDebugName(args) } as const
20
+ }
21
+
22
+ export function prettifyThreadName(input: string): string {
23
+ let depth = 0
24
+ let result = ''
25
+ let insideCurlyBraces = 0
26
+
27
+ for (let i = 0; i < input.length; i++) {
28
+ const char = input[i]
29
+
30
+ if (char === '(') {
31
+ result += char + '\n' + '\t'.repeat(++depth)
32
+ } else if (char === ')') {
33
+ result += '\n' + '\t'.repeat(--depth) + char
34
+ } else if (char === ',' && insideCurlyBraces === 0) {
35
+ result += char + '\n' + '\t'.repeat(depth)
36
+ } else if (char === '{' && insideCurlyBraces === 0) {
37
+ insideCurlyBraces++
38
+ result += char + '\n' + '\t'.repeat(depth + 1)
39
+ } else if (char === '}' && insideCurlyBraces === 1) {
40
+ insideCurlyBraces--
41
+ result += '\n' + '\t'.repeat(depth) + char
42
+ } else if (char === '{' && insideCurlyBraces > 0) {
43
+ insideCurlyBraces++
44
+ result += char
45
+ } else if (char === '}' && insideCurlyBraces > 1) {
46
+ insideCurlyBraces--
47
+ result += char
48
+ } else {
49
+ result += char
50
+ }
51
+ }
52
+
53
+ return result
54
+ }
package/src/utils.ts ADDED
@@ -0,0 +1,4 @@
1
+ import { Applog, ApplogEncNoCid } from './applog/datom-types.ts'
2
+
3
+ export const isTruthy = (l: Applog | ApplogEncNoCid): boolean => !!l
4
+ export const keepTruthy = (arr: readonly any[]): any[] => arr.filter(isTruthy)