@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.
- package/LICENSE +661 -0
- package/README.md +3 -0
- package/dist/applog/applog-helpers.d.ts +47 -0
- package/dist/applog/applog-helpers.d.ts.map +1 -0
- package/dist/applog/applog-utils.d.ts +57 -0
- package/dist/applog/applog-utils.d.ts.map +1 -0
- package/dist/applog/datom-types.d.ts +128 -0
- package/dist/applog/datom-types.d.ts.map +1 -0
- package/dist/applog.d.ts +4 -0
- package/dist/applog.d.ts.map +1 -0
- package/dist/applog.js +101 -0
- package/dist/applog.js.map +1 -0
- package/dist/blockstore/index.d.ts +21 -0
- package/dist/blockstore/index.d.ts.map +1 -0
- package/dist/blockstore.d.ts +2 -0
- package/dist/blockstore.d.ts.map +1 -0
- package/dist/blockstore.js +24 -0
- package/dist/blockstore.js.map +1 -0
- package/dist/chunk-6MQKRL6W.js +86 -0
- package/dist/chunk-6MQKRL6W.js.map +1 -0
- package/dist/chunk-7MW34UEO.js +40 -0
- package/dist/chunk-7MW34UEO.js.map +1 -0
- package/dist/chunk-7Z5YDQKK.js +1 -0
- package/dist/chunk-7Z5YDQKK.js.map +1 -0
- package/dist/chunk-CY4NLISM.js +144 -0
- package/dist/chunk-CY4NLISM.js.map +1 -0
- package/dist/chunk-E46VTKTZ.js +1 -0
- package/dist/chunk-E46VTKTZ.js.map +1 -0
- package/dist/chunk-O43W7UW6.js +434 -0
- package/dist/chunk-O43W7UW6.js.map +1 -0
- package/dist/chunk-XIQSYEV3.js +1604 -0
- package/dist/chunk-XIQSYEV3.js.map +1 -0
- package/dist/chunk-XVGW4QC3.js +55 -0
- package/dist/chunk-XVGW4QC3.js.map +1 -0
- package/dist/chunk-YDAKBU6Q.js +9 -0
- package/dist/chunk-YDAKBU6Q.js.map +1 -0
- package/dist/chunk-ZAADLBSB.js +36 -0
- package/dist/chunk-ZAADLBSB.js.map +1 -0
- package/dist/chunk-ZXCJRYD7.js +883 -0
- package/dist/chunk-ZXCJRYD7.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +354 -0
- package/dist/index.js.map +1 -0
- package/dist/ipfs/car.d.ts +59 -0
- package/dist/ipfs/car.d.ts.map +1 -0
- package/dist/ipfs/fetch-snapshot-chain.d.ts +32 -0
- package/dist/ipfs/fetch-snapshot-chain.d.ts.map +1 -0
- package/dist/ipfs/ipfs-utils.d.ts +35 -0
- package/dist/ipfs/ipfs-utils.d.ts.map +1 -0
- package/dist/ipfs.d.ts +4 -0
- package/dist/ipfs.d.ts.map +1 -0
- package/dist/ipfs.js +60 -0
- package/dist/ipfs.js.map +1 -0
- package/dist/ipns/ipns-record.d.ts +34 -0
- package/dist/ipns/ipns-record.d.ts.map +1 -0
- package/dist/ipns.d.ts +2 -0
- package/dist/ipns.d.ts.map +1 -0
- package/dist/ipns.js +64 -0
- package/dist/ipns.js.map +1 -0
- package/dist/pubsub/connector.d.ts +9 -0
- package/dist/pubsub/connector.d.ts.map +1 -0
- package/dist/pubsub/pub-pull.d.ts +14 -0
- package/dist/pubsub/pub-pull.d.ts.map +1 -0
- package/dist/pubsub/pubsub-types.d.ts +72 -0
- package/dist/pubsub/pubsub-types.d.ts.map +1 -0
- package/dist/pubsub/snap-push.d.ts +41 -0
- package/dist/pubsub/snap-push.d.ts.map +1 -0
- package/dist/pubsub/ucan-example.d.ts +3 -0
- package/dist/pubsub/ucan-example.d.ts.map +1 -0
- package/dist/pubsub/ucan.d.ts +16 -0
- package/dist/pubsub/ucan.d.ts.map +1 -0
- package/dist/pubsub.d.ts +5 -0
- package/dist/pubsub.d.ts.map +1 -0
- package/dist/pubsub.js +31 -0
- package/dist/pubsub.js.map +1 -0
- package/dist/query/basic.d.ts +105 -0
- package/dist/query/basic.d.ts.map +1 -0
- package/dist/query/divergences.d.ts +12 -0
- package/dist/query/divergences.d.ts.map +1 -0
- package/dist/query/matchers.d.ts +4 -0
- package/dist/query/matchers.d.ts.map +1 -0
- package/dist/query/memoized.d.ts +66 -0
- package/dist/query/memoized.d.ts.map +1 -0
- package/dist/query/query-steps.d.ts +4 -0
- package/dist/query/query-steps.d.ts.map +1 -0
- package/dist/query/situations.d.ts +80 -0
- package/dist/query/situations.d.ts.map +1 -0
- package/dist/query/subscribable.d.ts +102 -0
- package/dist/query/subscribable.d.ts.map +1 -0
- package/dist/query/types.d.ts +70 -0
- package/dist/query/types.d.ts.map +1 -0
- package/dist/query.d.ts +8 -0
- package/dist/query.d.ts.map +1 -0
- package/dist/query.js +108 -0
- package/dist/query.js.map +1 -0
- package/dist/retrieve/index.d.ts +2 -0
- package/dist/retrieve/index.d.ts.map +1 -0
- package/dist/retrieve/update-thread.d.ts +64 -0
- package/dist/retrieve/update-thread.d.ts.map +1 -0
- package/dist/retrieve.d.ts +2 -0
- package/dist/retrieve.d.ts.map +1 -0
- package/dist/retrieve.js +14 -0
- package/dist/retrieve.js.map +1 -0
- package/dist/thread/basic.d.ts +60 -0
- package/dist/thread/basic.d.ts.map +1 -0
- package/dist/thread/filters.d.ts +47 -0
- package/dist/thread/filters.d.ts.map +1 -0
- package/dist/thread/mapped.d.ts +31 -0
- package/dist/thread/mapped.d.ts.map +1 -0
- package/dist/thread/utils.d.ts +23 -0
- package/dist/thread/utils.d.ts.map +1 -0
- package/dist/thread/writeable.d.ts +41 -0
- package/dist/thread/writeable.d.ts.map +1 -0
- package/dist/thread.d.ts +6 -0
- package/dist/thread.d.ts.map +1 -0
- package/dist/thread.js +54 -0
- package/dist/thread.js.map +1 -0
- package/dist/types/typescript-utils.d.ts +34 -0
- package/dist/types/typescript-utils.d.ts.map +1 -0
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +26 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/debug-name.d.ts +13 -0
- package/dist/utils/debug-name.d.ts.map +1 -0
- package/dist/utils.d.ts +4 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +9 -0
- package/dist/utils.js.map +1 -0
- package/package.json +110 -0
- package/src/applog/applog-helpers.ts +150 -0
- package/src/applog/applog-utils.ts +398 -0
- package/src/applog/datom-types.ts +148 -0
- package/src/applog.ts +3 -0
- package/src/blockstore/index.ts +36 -0
- package/src/blockstore.ts +1 -0
- package/src/index.ts +8 -0
- package/src/ipfs/car.ts +291 -0
- package/src/ipfs/fetch-snapshot-chain.ts +135 -0
- package/src/ipfs/ipfs-utils.ts +132 -0
- package/src/ipfs.ts +3 -0
- package/src/ipns/ipns-record.ts +115 -0
- package/src/ipns.ts +1 -0
- package/src/pubsub/UCAN Specs Overview.md +217 -0
- package/src/pubsub/connector.ts +9 -0
- package/src/pubsub/pub-pull.ts +31 -0
- package/src/pubsub/pubsub-types.ts +90 -0
- package/src/pubsub/snap-push.ts +277 -0
- package/src/pubsub/ucan-example.ts +61 -0
- package/src/pubsub/ucan.ts +56 -0
- package/src/pubsub.ts +4 -0
- package/src/query/basic.ts +1061 -0
- package/src/query/divergences.ts +50 -0
- package/src/query/matchers.ts +8 -0
- package/src/query/memoized.test.ts +151 -0
- package/src/query/memoized.ts +180 -0
- package/src/query/query-steps.ts +4 -0
- package/src/query/query.test.ts +536 -0
- package/src/query/situations.ts +261 -0
- package/src/query/subscribable.test.ts +245 -0
- package/src/query/subscribable.ts +225 -0
- package/src/query/types.ts +155 -0
- package/src/query.ts +7 -0
- package/src/retrieve/index.ts +1 -0
- package/src/retrieve/update-thread.ts +248 -0
- package/src/retrieve.ts +1 -0
- package/src/test/perf/query.1m.perf.test.ts +94 -0
- package/src/test/perf/query.perf.test.ts +389 -0
- package/src/test/perf/query.realdata.perf.test.ts +175 -0
- package/src/thread/basic.ts +209 -0
- package/src/thread/filters.ts +234 -0
- package/src/thread/mapped.ts +166 -0
- package/src/thread/utils.ts +146 -0
- package/src/thread/writeable.ts +163 -0
- package/src/thread.ts +5 -0
- package/src/types/typescript-utils.ts +64 -0
- package/src/types.ts +1 -0
- package/src/utils/debug-name.ts +54 -0
- 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,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