@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,1604 @@
|
|
|
1
|
+
import {
|
|
2
|
+
arrayIfSingle
|
|
3
|
+
} from "./chunk-ZAADLBSB.js";
|
|
4
|
+
|
|
5
|
+
// src/thread/basic.ts
|
|
6
|
+
import { Logger } from "besonders-logger";
|
|
7
|
+
|
|
8
|
+
// src/query/subscribable.ts
|
|
9
|
+
var SubscribableImpl = class {
|
|
10
|
+
_value;
|
|
11
|
+
_derivedSubscribers = [];
|
|
12
|
+
_subscribers = [];
|
|
13
|
+
_upstreamActive = false;
|
|
14
|
+
_activateUpstream;
|
|
15
|
+
_deactivateUpstream = null;
|
|
16
|
+
_equals;
|
|
17
|
+
constructor(initialValue, activateUpstream, opts) {
|
|
18
|
+
this._value = initialValue;
|
|
19
|
+
this._activateUpstream = activateUpstream ?? null;
|
|
20
|
+
this._equals = opts?.equals === false ? () => false : opts?.equals ?? ((a, b) => a === b);
|
|
21
|
+
}
|
|
22
|
+
get value() {
|
|
23
|
+
return this._value;
|
|
24
|
+
}
|
|
25
|
+
subscribe(cb, type) {
|
|
26
|
+
if (!this._upstreamActive && this._activateUpstream) {
|
|
27
|
+
this._deactivateUpstream = this._activateUpstream();
|
|
28
|
+
this._upstreamActive = true;
|
|
29
|
+
}
|
|
30
|
+
const list = type === "derived" ? this._derivedSubscribers : this._subscribers;
|
|
31
|
+
list.push(cb);
|
|
32
|
+
return () => {
|
|
33
|
+
const idx = list.indexOf(cb);
|
|
34
|
+
if (idx >= 0) list.splice(idx, 1);
|
|
35
|
+
if (this._derivedSubscribers.length === 0 && this._subscribers.length === 0 && this._upstreamActive) {
|
|
36
|
+
this._deactivateUpstream?.();
|
|
37
|
+
this._deactivateUpstream = null;
|
|
38
|
+
this._upstreamActive = false;
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/** Update value and notify subscribers (skips if equals check passes) */
|
|
43
|
+
_set(value) {
|
|
44
|
+
if (this._equals(value, this._value)) return;
|
|
45
|
+
this._value = value;
|
|
46
|
+
this._notify();
|
|
47
|
+
}
|
|
48
|
+
_notify() {
|
|
49
|
+
const derived = [...this._derivedSubscribers];
|
|
50
|
+
for (const sub of derived) sub();
|
|
51
|
+
const subs = [...this._subscribers];
|
|
52
|
+
for (const sub of subs) sub();
|
|
53
|
+
}
|
|
54
|
+
dispose() {
|
|
55
|
+
this._deactivateUpstream?.();
|
|
56
|
+
this._deactivateUpstream = null;
|
|
57
|
+
this._derivedSubscribers.length = 0;
|
|
58
|
+
this._subscribers.length = 0;
|
|
59
|
+
this._upstreamActive = false;
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
function isArrayInitEvent(event) {
|
|
63
|
+
return event.init !== void 0;
|
|
64
|
+
}
|
|
65
|
+
var SubscribableArrayImpl = class {
|
|
66
|
+
_items;
|
|
67
|
+
_derivedSubscribers = [];
|
|
68
|
+
_subscribers = [];
|
|
69
|
+
_upstreamActive = false;
|
|
70
|
+
_activateUpstream;
|
|
71
|
+
_deactivateUpstream = null;
|
|
72
|
+
constructor(initialItems, activateUpstream) {
|
|
73
|
+
this._items = initialItems;
|
|
74
|
+
this._activateUpstream = activateUpstream ?? null;
|
|
75
|
+
}
|
|
76
|
+
get items() {
|
|
77
|
+
return this._items;
|
|
78
|
+
}
|
|
79
|
+
get length() {
|
|
80
|
+
return this._items.length;
|
|
81
|
+
}
|
|
82
|
+
subscribe(cb, type) {
|
|
83
|
+
if (!this._upstreamActive && this._activateUpstream) {
|
|
84
|
+
this._deactivateUpstream = this._activateUpstream();
|
|
85
|
+
this._upstreamActive = true;
|
|
86
|
+
}
|
|
87
|
+
const list = type === "derived" ? this._derivedSubscribers : this._subscribers;
|
|
88
|
+
list.push(cb);
|
|
89
|
+
return () => {
|
|
90
|
+
const idx = list.indexOf(cb);
|
|
91
|
+
if (idx >= 0) list.splice(idx, 1);
|
|
92
|
+
if (this._derivedSubscribers.length === 0 && this._subscribers.length === 0 && this._upstreamActive) {
|
|
93
|
+
this._deactivateUpstream?.();
|
|
94
|
+
this._deactivateUpstream = null;
|
|
95
|
+
this._upstreamActive = false;
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
/** Push items and notify subscribers */
|
|
100
|
+
_push(...items) {
|
|
101
|
+
this._items.push(...items);
|
|
102
|
+
this._notify({ added: items, removed: null });
|
|
103
|
+
}
|
|
104
|
+
/** Remove items and notify subscribers */
|
|
105
|
+
_remove(items) {
|
|
106
|
+
for (const item of items) {
|
|
107
|
+
const idx = this._items.indexOf(item);
|
|
108
|
+
if (idx >= 0) this._items.splice(idx, 1);
|
|
109
|
+
}
|
|
110
|
+
this._notify({ added: [], removed: items });
|
|
111
|
+
}
|
|
112
|
+
/** Full reset — replace all items */
|
|
113
|
+
_reset(items) {
|
|
114
|
+
this._items = items;
|
|
115
|
+
this._notify({ init: [...this._items] });
|
|
116
|
+
}
|
|
117
|
+
_notify(event) {
|
|
118
|
+
const derived = [...this._derivedSubscribers];
|
|
119
|
+
for (const sub of derived) sub(event);
|
|
120
|
+
const subs = [...this._subscribers];
|
|
121
|
+
for (const sub of subs) sub(event);
|
|
122
|
+
}
|
|
123
|
+
dispose() {
|
|
124
|
+
this._deactivateUpstream?.();
|
|
125
|
+
this._deactivateUpstream = null;
|
|
126
|
+
this._derivedSubscribers.length = 0;
|
|
127
|
+
this._subscribers.length = 0;
|
|
128
|
+
this._upstreamActive = false;
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
// src/utils/debug-name.ts
|
|
133
|
+
import stringify from "safe-stable-stringify";
|
|
134
|
+
var createDebugName = ({ caller, thread, pattern, args }) => {
|
|
135
|
+
args = args || pattern;
|
|
136
|
+
const str = `${!Array.isArray(thread) && thread?.name ? thread.name + " | " : ""}${caller ?? "caller?"}${args ? `{${typeof args === "string" ? args : stringify(args)}}` : ""}`;
|
|
137
|
+
return str;
|
|
138
|
+
};
|
|
139
|
+
var createDebugNameObj = (args) => {
|
|
140
|
+
return { name: createDebugName(args) };
|
|
141
|
+
};
|
|
142
|
+
function prettifyThreadName(input) {
|
|
143
|
+
let depth = 0;
|
|
144
|
+
let result = "";
|
|
145
|
+
let insideCurlyBraces = 0;
|
|
146
|
+
for (let i = 0; i < input.length; i++) {
|
|
147
|
+
const char = input[i];
|
|
148
|
+
if (char === "(") {
|
|
149
|
+
result += char + "\n" + " ".repeat(++depth);
|
|
150
|
+
} else if (char === ")") {
|
|
151
|
+
result += "\n" + " ".repeat(--depth) + char;
|
|
152
|
+
} else if (char === "," && insideCurlyBraces === 0) {
|
|
153
|
+
result += char + "\n" + " ".repeat(depth);
|
|
154
|
+
} else if (char === "{" && insideCurlyBraces === 0) {
|
|
155
|
+
insideCurlyBraces++;
|
|
156
|
+
result += char + "\n" + " ".repeat(depth + 1);
|
|
157
|
+
} else if (char === "}" && insideCurlyBraces === 1) {
|
|
158
|
+
insideCurlyBraces--;
|
|
159
|
+
result += "\n" + " ".repeat(depth) + char;
|
|
160
|
+
} else if (char === "{" && insideCurlyBraces > 0) {
|
|
161
|
+
insideCurlyBraces++;
|
|
162
|
+
result += char;
|
|
163
|
+
} else if (char === "}" && insideCurlyBraces > 1) {
|
|
164
|
+
insideCurlyBraces--;
|
|
165
|
+
result += char;
|
|
166
|
+
} else {
|
|
167
|
+
result += char;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return result;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// src/thread/basic.ts
|
|
174
|
+
var { WARN, LOG, DEBUG, VERBOSE, ERROR } = Logger.setup(Logger.INFO, { prefix: "[thread]" });
|
|
175
|
+
var isInitEvent = isArrayInitEvent;
|
|
176
|
+
var Thread = class {
|
|
177
|
+
constructor(name, parents, filters, _applogs = []) {
|
|
178
|
+
this.name = name;
|
|
179
|
+
this._applogs = _applogs;
|
|
180
|
+
this.parents = parents === null ? null : arrayIfSingle(parents);
|
|
181
|
+
this.filters = filters;
|
|
182
|
+
if (this.parents?.length === 0) {
|
|
183
|
+
WARN(`[Thread] empty parents array`, name);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
filters;
|
|
187
|
+
parents;
|
|
188
|
+
_derivedSubscribers = [];
|
|
189
|
+
_subscribers = [];
|
|
190
|
+
/** Monotonic counter incremented on every mutation. Used by memoizedFn to invalidate caches. */
|
|
191
|
+
_version = 0;
|
|
192
|
+
get readOnly() {
|
|
193
|
+
if (this.parents.length !== 1) return true;
|
|
194
|
+
return this.parents[0].readOnly;
|
|
195
|
+
}
|
|
196
|
+
insert(appLogsToInsert) {
|
|
197
|
+
if (this.readOnly) throw ERROR(`[Thread] insert() called on read-only thread:`, this.nameAndSizeUntracked);
|
|
198
|
+
if (!this.parents) throw ERROR(`[Thread] insert() called on non-writable thread without parents:`, this.nameAndSizeUntracked);
|
|
199
|
+
if (this.parents?.length !== 1) throw ERROR(`[Thread] insert() called on thread with multiple parents:`, this.nameAndSizeUntracked);
|
|
200
|
+
return this.parents[0].insert(appLogsToInsert);
|
|
201
|
+
}
|
|
202
|
+
insertRaw(appLogsToInsert) {
|
|
203
|
+
if (this.readOnly) throw ERROR(`[Thread] insertRaw() called on read-only thread:`, this.nameAndSizeUntracked);
|
|
204
|
+
if (!this.parents) throw ERROR(`[Thread] insertRaw() called on non-writable thread without parents:`, this.nameAndSizeUntracked);
|
|
205
|
+
if (this.parents?.length !== 1) throw ERROR(`[Thread] insertRaw() called on thread with multiple parents:`, this.nameAndSizeUntracked);
|
|
206
|
+
return this.parents[0].insertRaw(appLogsToInsert);
|
|
207
|
+
}
|
|
208
|
+
subscribe(callback, type) {
|
|
209
|
+
const list = type === "derived" ? this._derivedSubscribers : this._subscribers;
|
|
210
|
+
list.push(callback);
|
|
211
|
+
return () => {
|
|
212
|
+
const idx = list.indexOf(callback);
|
|
213
|
+
if (idx >= 0) list.splice(idx, 1);
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
notifySubscribers(event) {
|
|
217
|
+
this._version++;
|
|
218
|
+
DEBUG(`[thread: ${this.name}] notifying`, this._derivedSubscribers.length, "derived +", this._subscribers.length, "subscribers of", { ...event, subs: this._subscribers });
|
|
219
|
+
const derived = [...this._derivedSubscribers];
|
|
220
|
+
for (const subscriber of derived) {
|
|
221
|
+
subscriber(event);
|
|
222
|
+
}
|
|
223
|
+
const subs = [...this._subscribers];
|
|
224
|
+
for (const subscriber of subs) {
|
|
225
|
+
subscriber(event);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
// ── SubscribableArray<Applog> ──
|
|
229
|
+
get items() {
|
|
230
|
+
return this._applogs;
|
|
231
|
+
}
|
|
232
|
+
dispose() {
|
|
233
|
+
this._derivedSubscribers.length = 0;
|
|
234
|
+
this._subscribers.length = 0;
|
|
235
|
+
}
|
|
236
|
+
get applogs() {
|
|
237
|
+
return this._applogs;
|
|
238
|
+
}
|
|
239
|
+
get applogsCids() {
|
|
240
|
+
return this._applogs.map((l) => l.cid);
|
|
241
|
+
}
|
|
242
|
+
get applogsCidSet() {
|
|
243
|
+
return new Set(this._applogs.map((l) => l.cid));
|
|
244
|
+
}
|
|
245
|
+
map(fn) {
|
|
246
|
+
return this.applogs.map(fn);
|
|
247
|
+
}
|
|
248
|
+
get findLast() {
|
|
249
|
+
return this.applogs.findLast.bind(this.applogs);
|
|
250
|
+
}
|
|
251
|
+
get findFirst() {
|
|
252
|
+
return this.applogs.find.bind(this.applogs);
|
|
253
|
+
}
|
|
254
|
+
get firstLog() {
|
|
255
|
+
return this.applogs[0];
|
|
256
|
+
}
|
|
257
|
+
get latestLog() {
|
|
258
|
+
return this.applogs[this.applogs.length - 1];
|
|
259
|
+
}
|
|
260
|
+
hasApplog(applog, byRef) {
|
|
261
|
+
if (byRef) {
|
|
262
|
+
return this.applogs.includes(applog);
|
|
263
|
+
} else {
|
|
264
|
+
if (!applog.cid) throw ERROR(`[hasApplogs] applog without CID:`, applog);
|
|
265
|
+
return this.hasApplogCid(applog.cid);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
hasApplogCid(cid) {
|
|
269
|
+
return this.applogsCidSet.has(cid.toString());
|
|
270
|
+
}
|
|
271
|
+
get applogsByCid() {
|
|
272
|
+
return new Map(this.applogs.map((log) => [log.cid, log]));
|
|
273
|
+
}
|
|
274
|
+
getApplog(cid) {
|
|
275
|
+
return this.applogsByCid.get(cid.toString());
|
|
276
|
+
}
|
|
277
|
+
hasApplogWithDiffTs(applog) {
|
|
278
|
+
return this.applogs.find((existing) => existing.en === applog.en && existing.at === applog.at && existing.vl === applog.vl && existing.ag === applog.ag);
|
|
279
|
+
}
|
|
280
|
+
// get stateHash() {
|
|
281
|
+
// blakeHasher.init()
|
|
282
|
+
// for (const log of this.applogs) {
|
|
283
|
+
// blakeHasher.update(log.cid)
|
|
284
|
+
// }
|
|
285
|
+
// return blakeHasher.digest()
|
|
286
|
+
// }
|
|
287
|
+
get isEmpty() {
|
|
288
|
+
return this.size === 0;
|
|
289
|
+
}
|
|
290
|
+
get size() {
|
|
291
|
+
return this.applogs.length;
|
|
292
|
+
}
|
|
293
|
+
get length() {
|
|
294
|
+
return this.applogs.length;
|
|
295
|
+
}
|
|
296
|
+
get untrackedSize() {
|
|
297
|
+
return this.size;
|
|
298
|
+
}
|
|
299
|
+
get nameAndSizeUntracked() {
|
|
300
|
+
return `${this.name} (${this.size})`;
|
|
301
|
+
}
|
|
302
|
+
get prettyName() {
|
|
303
|
+
return prettifyThreadName(this.name);
|
|
304
|
+
}
|
|
305
|
+
get hasParents() {
|
|
306
|
+
return !!this.parents?.length;
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
var getLogsFromThread = (logsOrThread) => logsOrThread instanceof Thread ? logsOrThread.applogs : logsOrThread;
|
|
310
|
+
var StaticThread = class _StaticThread extends Thread {
|
|
311
|
+
static fromArray(applogs, name) {
|
|
312
|
+
return new _StaticThread(name || "static", null, [], applogs);
|
|
313
|
+
}
|
|
314
|
+
constructor(name, parents, filters, _applogs) {
|
|
315
|
+
super(name, parents, filters, _applogs);
|
|
316
|
+
}
|
|
317
|
+
get readOnly() {
|
|
318
|
+
return true;
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
// src/thread/filters.ts
|
|
323
|
+
import { Logger as Logger6 } from "besonders-logger";
|
|
324
|
+
import stringify3 from "safe-stable-stringify";
|
|
325
|
+
|
|
326
|
+
// src/applog/applog-helpers.ts
|
|
327
|
+
import { Logger as Logger4 } from "besonders-logger";
|
|
328
|
+
|
|
329
|
+
// src/ipfs/ipfs-utils.ts
|
|
330
|
+
import * as dagJson from "@ipld/dag-json";
|
|
331
|
+
import { sha256 } from "@noble/hashes/sha2.js";
|
|
332
|
+
import { Logger as Logger2 } from "besonders-logger";
|
|
333
|
+
import { CID, digest as Digest } from "multiformats";
|
|
334
|
+
import { encode as multiformatsEncode } from "multiformats/block";
|
|
335
|
+
|
|
336
|
+
// src/applog/datom-types.ts
|
|
337
|
+
import { FormatRegistry, Type } from "@sinclair/typebox";
|
|
338
|
+
import { TypeCompiler } from "@sinclair/typebox/compiler";
|
|
339
|
+
var Nullable = (schema) => Type.Union([schema, Type.Null()]);
|
|
340
|
+
var EntityID_LENGTH = 7;
|
|
341
|
+
var isCID = /^(k51qz|baguq)[0-9a-z]{56,57}$/;
|
|
342
|
+
var isShortHash = /^[0-9A-Fa-f]{7,8}$/g;
|
|
343
|
+
FormatRegistry.Set("EntityID", (value) => !!value.match(isShortHash) || !!value.match(isCID));
|
|
344
|
+
var EntityID = Type.String();
|
|
345
|
+
var isEncryptedApplog = (l) => l?.enc instanceof Uint8Array;
|
|
346
|
+
FormatRegistry.Set("CID", (value) => !!value.match(isCID));
|
|
347
|
+
var CIDTB = Type.String({ format: "CID" });
|
|
348
|
+
var isURL = /^http([s]?):\/\/.*\..*/;
|
|
349
|
+
FormatRegistry.Set("URL", (value) => !!value.match(isURL));
|
|
350
|
+
var URL = Type.String({ format: "URL" });
|
|
351
|
+
var AppLogNoCidTB = Type.Object({
|
|
352
|
+
en: EntityID,
|
|
353
|
+
// EntityID
|
|
354
|
+
at: Type.String(),
|
|
355
|
+
// Attribute
|
|
356
|
+
vl: Nullable(Type.Union([Type.String(), Type.Boolean(), Type.Number()])),
|
|
357
|
+
// TODO refactor to semantic typesafe ApplogValue
|
|
358
|
+
ts: Type.String(),
|
|
359
|
+
// Timestamp
|
|
360
|
+
ag: Type.String(),
|
|
361
|
+
// AgentHash
|
|
362
|
+
pv: Nullable(CIDTB)
|
|
363
|
+
// CidString
|
|
364
|
+
});
|
|
365
|
+
var AppLogNoCidTBC = TypeCompiler.Compile(AppLogNoCidTB);
|
|
366
|
+
var getApplogNoCidTypeErrors = (obj) => Array.from(AppLogNoCidTBC.Errors(obj));
|
|
367
|
+
var isValidApplogNoCid = AppLogNoCidTBC.Check.bind(AppLogNoCidTBC);
|
|
368
|
+
var AppLogTB = Type.Composite([
|
|
369
|
+
Type.Object({
|
|
370
|
+
cid: CIDTB
|
|
371
|
+
}),
|
|
372
|
+
AppLogNoCidTB
|
|
373
|
+
]);
|
|
374
|
+
var AppLogTBC = TypeCompiler.Compile(AppLogTB);
|
|
375
|
+
var getApplogTypeErrors = (obj) => Array.from(AppLogTBC.Errors(obj));
|
|
376
|
+
var isValidApplog = AppLogTBC.Check.bind(AppLogTBC);
|
|
377
|
+
|
|
378
|
+
// src/ipfs/ipfs-utils.ts
|
|
379
|
+
import { base36 } from "multiformats/bases/base36";
|
|
380
|
+
import { sha256 as sha265Hasher } from "multiformats/hashes/sha2";
|
|
381
|
+
var { WARN: WARN2, LOG: LOG2, DEBUG: DEBUG2, VERBOSE: VERBOSE2, ERROR: ERROR2 } = Logger2.setup(Logger2.INFO);
|
|
382
|
+
var MULTICODEC_IPNS_KEY = 114;
|
|
383
|
+
function prepareForPub(log, without = ["cid"]) {
|
|
384
|
+
if (!log) throw ERROR2("falsy log", log);
|
|
385
|
+
let cid = log.cid;
|
|
386
|
+
if (isEncryptedApplog(log)) {
|
|
387
|
+
if (!cid) cid = getCidSync(encodeBlock(log).bytes).toString();
|
|
388
|
+
WARN2("preparing an encrypted applog - really?");
|
|
389
|
+
return { log, cid };
|
|
390
|
+
}
|
|
391
|
+
const logWithout = {};
|
|
392
|
+
for (let [key, val] of Object.entries(log)) {
|
|
393
|
+
if (val === void 0) {
|
|
394
|
+
WARN2(`log.${key} is undefined, which is not allowed - encoding as null`, log);
|
|
395
|
+
val = null;
|
|
396
|
+
}
|
|
397
|
+
if (!without.includes(key)) {
|
|
398
|
+
logWithout[key] = val;
|
|
399
|
+
} else {
|
|
400
|
+
VERBOSE2("excluding app log", { key, val });
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
return { log: logWithout, cid };
|
|
404
|
+
}
|
|
405
|
+
function encodeApplogAndGetCid(log) {
|
|
406
|
+
return getCidSync(encodeApplog(log).bytes);
|
|
407
|
+
}
|
|
408
|
+
function encodeApplog(log) {
|
|
409
|
+
return encodeBlock(prepareForPub(log)?.log);
|
|
410
|
+
}
|
|
411
|
+
function getCidSync(bytes) {
|
|
412
|
+
const hash = sha256(bytes);
|
|
413
|
+
const digest = Digest.create(sha265Hasher.code, hash);
|
|
414
|
+
const cid = CID.create(1, dagJson.code, digest);
|
|
415
|
+
VERBOSE2(`[getCidSync]`, { bytes, hash, digest, cid });
|
|
416
|
+
return cid;
|
|
417
|
+
}
|
|
418
|
+
function encodeBlock(jsonObject) {
|
|
419
|
+
DEBUG2("[encodeBlock]", jsonObject);
|
|
420
|
+
try {
|
|
421
|
+
const byteView = dagJson.encode(jsonObject);
|
|
422
|
+
return { bytes: byteView, cid: getCidSync(byteView) };
|
|
423
|
+
} catch (err) {
|
|
424
|
+
throw ERROR2("[encodeBlock] failed to encode:", jsonObject, err);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
async function encodeBlockOriginal(jsonObject) {
|
|
428
|
+
const encoded = await multiformatsEncode({ value: jsonObject, codec: dagJson, hasher: sha265Hasher });
|
|
429
|
+
const syncVariant = encodeBlock(jsonObject);
|
|
430
|
+
if (syncVariant.cid.toString() !== encoded.cid.toString()) {
|
|
431
|
+
ERROR2(`[encodeBlockOriginal] sync cid mismatch`, { jsonObject, encoded, syncVariant });
|
|
432
|
+
}
|
|
433
|
+
return encoded;
|
|
434
|
+
}
|
|
435
|
+
function tryParseCID(cidString) {
|
|
436
|
+
let cid = null;
|
|
437
|
+
let errors = [];
|
|
438
|
+
try {
|
|
439
|
+
cid = CID.parse(cidString);
|
|
440
|
+
} catch (err) {
|
|
441
|
+
VERBOSE2(`[retrieveThread] couldn't parse pubID with default base`);
|
|
442
|
+
errors.push(err);
|
|
443
|
+
}
|
|
444
|
+
if (!cid) {
|
|
445
|
+
try {
|
|
446
|
+
cid = CID.parse(cidString, base36);
|
|
447
|
+
} catch (err) {
|
|
448
|
+
VERBOSE2(`[retrieveThread] couldn't parse pubID with base36`);
|
|
449
|
+
errors.push(err);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
return {
|
|
453
|
+
cid,
|
|
454
|
+
errors: cid ? null : errors,
|
|
455
|
+
// we only care about errors if we failed to parse
|
|
456
|
+
isIpns: cid && isIpnsKeyCid(cid)
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
function isIpnsKeyCid(cid) {
|
|
460
|
+
return cid.code === MULTICODEC_IPNS_KEY;
|
|
461
|
+
}
|
|
462
|
+
function cidToString(cid) {
|
|
463
|
+
if (cid.code == MULTICODEC_IPNS_KEY) {
|
|
464
|
+
return toIpnsString(cid);
|
|
465
|
+
} else {
|
|
466
|
+
return cid.toString();
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
function toIpnsString(cid) {
|
|
470
|
+
if (cid.code !== MULTICODEC_IPNS_KEY) throw ERROR2(`Not an IPNS cid (${cid.code}):`, cid.toString());
|
|
471
|
+
return cid.toString(base36);
|
|
472
|
+
}
|
|
473
|
+
function ensureValidCIDinstance(cidOrStringA) {
|
|
474
|
+
return typeof cidOrStringA === "string" ? CID.parse(cidOrStringA) : typeof cidOrStringA.toV1 != "function" ? CID.decode(cidOrStringA.bytes) : cidOrStringA;
|
|
475
|
+
}
|
|
476
|
+
function areCidsEqual(cidOrStringA, cidOrStringB) {
|
|
477
|
+
if (!cidOrStringA || !cidOrStringB) throw new Error(`[areCidsEqual] invalid params: ${cidOrStringA}, ${cidOrStringB}`);
|
|
478
|
+
if (cidOrStringA === cidOrStringB) return true;
|
|
479
|
+
const cidA = ensureValidCIDinstance(cidOrStringA);
|
|
480
|
+
const cidB = ensureValidCIDinstance(cidOrStringB);
|
|
481
|
+
return cidA.toV1().toString() === cidB.toV1().toString();
|
|
482
|
+
}
|
|
483
|
+
function containsCid(list, needle) {
|
|
484
|
+
if (list instanceof Set) return list.has(typeof needle === "string" ? needle : needle.toV1().toString());
|
|
485
|
+
return list.some((cidOrString) => areCidsEqual(cidOrString, needle));
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
// src/applog/applog-utils.ts
|
|
489
|
+
import { Logger as Logger3 } from "besonders-logger";
|
|
490
|
+
import { isBefore } from "date-fns";
|
|
491
|
+
import { partial, pick } from "lodash-es";
|
|
492
|
+
import { isEqual } from "lodash-es";
|
|
493
|
+
import stringify2 from "safe-stable-stringify";
|
|
494
|
+
var { WARN: WARN3, LOG: LOG3, DEBUG: DEBUG3, VERBOSE: VERBOSE3, ERROR: ERROR3 } = Logger3.setup(Logger3.INFO);
|
|
495
|
+
var isoDateStrCompare = (strA, strB, dir = "asc") => dir === "asc" ? strA.localeCompare(strB, "en-US") : strB.localeCompare(strA, "en-US");
|
|
496
|
+
var objEqualByKeys = (keys, objA, objB) => {
|
|
497
|
+
return isEqual(pick(objA, keys), pick(objB, keys));
|
|
498
|
+
};
|
|
499
|
+
var compareApplogsByTs = (logA, logB, dir = "asc") => isoDateStrCompare(logA.ts, logB.ts, dir);
|
|
500
|
+
var compareApplogsByEnAt = partial(objEqualByKeys, ["en", "at"]);
|
|
501
|
+
function sortApplogsByTs(appLogArray, dir = "asc") {
|
|
502
|
+
return appLogArray.sort((a, b) => compareApplogsByTs(a, b, dir));
|
|
503
|
+
}
|
|
504
|
+
var isTsBefore = (log, logToCompare) => isBefore(new Date(log.ts), new Date(logToCompare.ts));
|
|
505
|
+
var uniqueEnFromAppLogs = (appLogArray) => [...new Set(appLogArray.map((eachLog) => eachLog.en))];
|
|
506
|
+
var areApplogsEqual = (logA, logB) => isEqual(logA, logB);
|
|
507
|
+
var warnMissingRemoveDuplicateMode = () => {
|
|
508
|
+
WARN3(`[removeDuplicateAppLogs] mode not set; pass 'safety' or 'cleanup' for optimal behavior`);
|
|
509
|
+
};
|
|
510
|
+
var removeDuplicateAppLogsCleanup = (appLogArray) => {
|
|
511
|
+
const logMap = /* @__PURE__ */ new Map();
|
|
512
|
+
const verboseEnabled = VERBOSE3.isEnabled;
|
|
513
|
+
for (const eachLog of appLogArray) {
|
|
514
|
+
if (!eachLog) {
|
|
515
|
+
ERROR3(`falsy entry in applogs`, appLogArray);
|
|
516
|
+
throw new Error(`falsy entry in applogs`);
|
|
517
|
+
}
|
|
518
|
+
if (!eachLog.cid) {
|
|
519
|
+
ERROR3(`applog with missing CID`, eachLog);
|
|
520
|
+
throw new Error(`applog with missing CID`);
|
|
521
|
+
}
|
|
522
|
+
const key = eachLog.cid;
|
|
523
|
+
const existing = logMap.get(key);
|
|
524
|
+
if (existing) {
|
|
525
|
+
if (verboseEnabled) VERBOSE3(`Skipping duplicate applog:`, [existing, eachLog]);
|
|
526
|
+
} else {
|
|
527
|
+
logMap.set(key, eachLog);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
return Array.from(logMap.values());
|
|
531
|
+
};
|
|
532
|
+
var removeDuplicateAppLogsSafety = (appLogArray) => {
|
|
533
|
+
const seen = /* @__PURE__ */ new Set();
|
|
534
|
+
const verboseEnabled = VERBOSE3.isEnabled;
|
|
535
|
+
const existingByCid = verboseEnabled ? /* @__PURE__ */ new Map() : null;
|
|
536
|
+
let result = null;
|
|
537
|
+
let index = 0;
|
|
538
|
+
for (const eachLog of appLogArray) {
|
|
539
|
+
if (!eachLog) {
|
|
540
|
+
ERROR3(`falsy entry in applogs`, appLogArray);
|
|
541
|
+
throw new Error(`falsy entry in applogs`);
|
|
542
|
+
}
|
|
543
|
+
if (!eachLog.cid) {
|
|
544
|
+
ERROR3(`applog with missing CID`, eachLog);
|
|
545
|
+
throw new Error(`applog with missing CID`);
|
|
546
|
+
}
|
|
547
|
+
const key = eachLog.cid;
|
|
548
|
+
if (seen.has(key)) {
|
|
549
|
+
if (!result) {
|
|
550
|
+
result = appLogArray.slice(0, index);
|
|
551
|
+
}
|
|
552
|
+
if (verboseEnabled) VERBOSE3(`Skipping duplicate applog:`, [existingByCid?.get(key), eachLog]);
|
|
553
|
+
} else {
|
|
554
|
+
seen.add(key);
|
|
555
|
+
if (existingByCid) existingByCid.set(key, eachLog);
|
|
556
|
+
if (result) result.push(eachLog);
|
|
557
|
+
}
|
|
558
|
+
index++;
|
|
559
|
+
}
|
|
560
|
+
return result ?? appLogArray;
|
|
561
|
+
};
|
|
562
|
+
var removeDuplicateAppLogs = (appLogArray, mode) => {
|
|
563
|
+
if (!mode) {
|
|
564
|
+
warnMissingRemoveDuplicateMode();
|
|
565
|
+
return removeDuplicateAppLogsCleanup(appLogArray);
|
|
566
|
+
}
|
|
567
|
+
return mode === "safety" ? removeDuplicateAppLogsSafety(appLogArray) : removeDuplicateAppLogsCleanup(appLogArray);
|
|
568
|
+
};
|
|
569
|
+
var getHashID = (stringifiable, lngth = 8) => cyrb53hash(stringify2(stringifiable), 31, lngth);
|
|
570
|
+
function isVariable(x) {
|
|
571
|
+
return typeof x === "string" && x.startsWith("?");
|
|
572
|
+
}
|
|
573
|
+
function variableNameWithoutQuestionmark(str) {
|
|
574
|
+
return str.slice(1);
|
|
575
|
+
}
|
|
576
|
+
function isStaticPattern(x) {
|
|
577
|
+
if (!["string", "boolean", "number", "function"].includes(typeof x)) WARN3(`Unhandled pattern value type:`, typeof x, x);
|
|
578
|
+
return !isVariable(x) && ["string", "boolean", "number"].includes(typeof x);
|
|
579
|
+
}
|
|
580
|
+
function resolveOrRemoveVariables(pattern, candidate) {
|
|
581
|
+
let variablesToFill = {};
|
|
582
|
+
const newPattern = {};
|
|
583
|
+
for (const [patternKey, patternValue] of Object.entries(pattern)) {
|
|
584
|
+
if (isVariable(patternValue)) {
|
|
585
|
+
const varName = variableNameWithoutQuestionmark(patternValue);
|
|
586
|
+
const candidateValue = candidate[varName];
|
|
587
|
+
if (candidateValue) {
|
|
588
|
+
newPattern[patternKey] = candidateValue;
|
|
589
|
+
} else {
|
|
590
|
+
variablesToFill[patternKey] = varName;
|
|
591
|
+
}
|
|
592
|
+
} else {
|
|
593
|
+
newPattern[patternKey] = patternValue;
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
return [newPattern, variablesToFill];
|
|
597
|
+
}
|
|
598
|
+
function matchVariable(variable, triplePart, context) {
|
|
599
|
+
if (context.hasOwnProperty(variable)) {
|
|
600
|
+
const bound = context[variable];
|
|
601
|
+
const match = matchPart(bound, triplePart, context);
|
|
602
|
+
return match;
|
|
603
|
+
}
|
|
604
|
+
return { ...context, [variable]: triplePart };
|
|
605
|
+
}
|
|
606
|
+
function matchPartStatic(field, patternPart, atomPart) {
|
|
607
|
+
let result;
|
|
608
|
+
if (patternPart) {
|
|
609
|
+
const typ = typeof patternPart;
|
|
610
|
+
if (typ === "string") {
|
|
611
|
+
result = patternPart === atomPart;
|
|
612
|
+
} else if (typ === "function") {
|
|
613
|
+
result = patternPart(atomPart);
|
|
614
|
+
} else if (typeof patternPart.has === "function") {
|
|
615
|
+
result = patternPart.has(atomPart);
|
|
616
|
+
} else if (Array.isArray(patternPart) && !Array.isArray(atomPart)) {
|
|
617
|
+
result = patternPart.includes(atomPart);
|
|
618
|
+
} else {
|
|
619
|
+
result = patternPart === atomPart;
|
|
620
|
+
}
|
|
621
|
+
} else {
|
|
622
|
+
result = patternPart === atomPart;
|
|
623
|
+
}
|
|
624
|
+
if (field.charAt(0) === "!") {
|
|
625
|
+
return !result;
|
|
626
|
+
} else {
|
|
627
|
+
return result;
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
function matchPart(patternPart, atomPart, context) {
|
|
631
|
+
if (!context) {
|
|
632
|
+
return null;
|
|
633
|
+
}
|
|
634
|
+
if (typeof patternPart === "string") {
|
|
635
|
+
if (isVariable(patternPart)) {
|
|
636
|
+
return matchVariable(patternPart, atomPart, context);
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
if (typeof patternPart === "function") {
|
|
640
|
+
return patternPart(atomPart) ? context : null;
|
|
641
|
+
}
|
|
642
|
+
return patternPart === atomPart ? context : null;
|
|
643
|
+
}
|
|
644
|
+
function matchPattern(pattern, applog, context) {
|
|
645
|
+
return Object.entries(pattern).reduce((context2, [field, patternValue]) => {
|
|
646
|
+
const applogValue = applog[field];
|
|
647
|
+
const patternValT = patternValue;
|
|
648
|
+
return matchPart(patternValT, applogValue, context2);
|
|
649
|
+
}, context);
|
|
650
|
+
}
|
|
651
|
+
function actualize(context, find) {
|
|
652
|
+
return Object.fromEntries(find.map((findField) => {
|
|
653
|
+
if (context === null) {
|
|
654
|
+
throw new Error(`actualize context is null ${find}`);
|
|
655
|
+
}
|
|
656
|
+
return [
|
|
657
|
+
isVariable(findField) ? findField.replace(/^\?/, "") : findField,
|
|
658
|
+
isVariable(findField) ? context[findField] : findField
|
|
659
|
+
];
|
|
660
|
+
}));
|
|
661
|
+
}
|
|
662
|
+
var sum = function sum2(array) {
|
|
663
|
+
var num = 0;
|
|
664
|
+
for (var i = 0, l = array.length; i < l; i++) num += array[i];
|
|
665
|
+
return num;
|
|
666
|
+
};
|
|
667
|
+
var mean = function mean2(array) {
|
|
668
|
+
return sum(array) / array.length;
|
|
669
|
+
};
|
|
670
|
+
var arrStats = {
|
|
671
|
+
max: function(array) {
|
|
672
|
+
return Math.max.apply(null, array);
|
|
673
|
+
},
|
|
674
|
+
min: function(array) {
|
|
675
|
+
return Math.min.apply(null, array);
|
|
676
|
+
},
|
|
677
|
+
range: function(array) {
|
|
678
|
+
return arrStats.max(array) - arrStats.min(array);
|
|
679
|
+
},
|
|
680
|
+
midrange: function(array) {
|
|
681
|
+
return arrStats.range(array) / 2;
|
|
682
|
+
},
|
|
683
|
+
sum,
|
|
684
|
+
mean,
|
|
685
|
+
average: mean,
|
|
686
|
+
median: function(array) {
|
|
687
|
+
array.sort(function(a, b) {
|
|
688
|
+
return a - b;
|
|
689
|
+
});
|
|
690
|
+
var mid = array.length / 2;
|
|
691
|
+
return mid % 1 ? array[mid - 0.5] : (array[mid - 1] + array[mid]) / 2;
|
|
692
|
+
},
|
|
693
|
+
modes: function(array) {
|
|
694
|
+
if (!array.length) return [];
|
|
695
|
+
var modeMap = {}, maxCount = 0, modes = [];
|
|
696
|
+
array.forEach(function(val) {
|
|
697
|
+
if (!modeMap[val]) modeMap[val] = 1;
|
|
698
|
+
else modeMap[val]++;
|
|
699
|
+
if (modeMap[val] > maxCount) {
|
|
700
|
+
modes = [val];
|
|
701
|
+
maxCount = modeMap[val];
|
|
702
|
+
} else if (modeMap[val] === maxCount) {
|
|
703
|
+
modes.push(val);
|
|
704
|
+
maxCount = modeMap[val];
|
|
705
|
+
}
|
|
706
|
+
});
|
|
707
|
+
return modes;
|
|
708
|
+
},
|
|
709
|
+
variance: function(array) {
|
|
710
|
+
var mean3 = arrStats.mean(array);
|
|
711
|
+
return arrStats.mean(array.map(function(num) {
|
|
712
|
+
return Math.pow(num - mean3, 2);
|
|
713
|
+
}));
|
|
714
|
+
},
|
|
715
|
+
standardDeviation: function(array) {
|
|
716
|
+
return Math.sqrt(arrStats.variance(array));
|
|
717
|
+
},
|
|
718
|
+
meanAbsoluteDeviation: function(array) {
|
|
719
|
+
var mean3 = arrStats.mean(array);
|
|
720
|
+
return arrStats.mean(array.map(function(num) {
|
|
721
|
+
return Math.abs(num - mean3);
|
|
722
|
+
}));
|
|
723
|
+
},
|
|
724
|
+
zScores: function(array) {
|
|
725
|
+
var mean3 = arrStats.mean(array);
|
|
726
|
+
var standardDeviation = arrStats.standardDeviation(array);
|
|
727
|
+
return array.map(function(num) {
|
|
728
|
+
return (num - mean3) / standardDeviation;
|
|
729
|
+
});
|
|
730
|
+
}
|
|
731
|
+
};
|
|
732
|
+
arrStats.average = arrStats.mean;
|
|
733
|
+
var tsNearlySame = (timeA, timeB) => timeB.startsWith(timeA.slice(0, timeA.length - 4));
|
|
734
|
+
var cyrb53hash = function(str, seed = 13, strLength) {
|
|
735
|
+
if (!str?.length) {
|
|
736
|
+
throw new Error(`Empty string: ${str}`);
|
|
737
|
+
}
|
|
738
|
+
let h1 = 3735928559 ^ seed;
|
|
739
|
+
let h2 = 1103547991 ^ seed;
|
|
740
|
+
for (let i = 0, ch; i < str.length; i++) {
|
|
741
|
+
ch = str.charCodeAt(i);
|
|
742
|
+
h1 = Math.imul(h1 ^ ch, 2654435761);
|
|
743
|
+
h2 = Math.imul(h2 ^ ch, 1597334677);
|
|
744
|
+
}
|
|
745
|
+
h1 = Math.imul(h1 ^ h1 >>> 16, 2246822507) ^ Math.imul(h2 ^ h2 >>> 13, 3266489909);
|
|
746
|
+
h2 = Math.imul(h2 ^ h2 >>> 16, 2246822507) ^ Math.imul(h1 ^ h1 >>> 13, 3266489909);
|
|
747
|
+
const asHex = (4294967296 * (2097151 & h2) + (h1 >>> 0)).toString(16);
|
|
748
|
+
return asHex.slice(-strLength).padStart(strLength, "0");
|
|
749
|
+
};
|
|
750
|
+
function arraysContainSameElements(arr1, arr2) {
|
|
751
|
+
if (arr1.length !== arr2.length) {
|
|
752
|
+
return false;
|
|
753
|
+
}
|
|
754
|
+
const sortedArr1 = [...arr1].sort();
|
|
755
|
+
const sortedArr2 = [...arr2].sort();
|
|
756
|
+
for (let i = 0; i < sortedArr1.length; i++) {
|
|
757
|
+
if (sortedArr1[i] !== sortedArr2[i]) {
|
|
758
|
+
return false;
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
return true;
|
|
762
|
+
}
|
|
763
|
+
function dateNowIso() {
|
|
764
|
+
const now = /* @__PURE__ */ new Date();
|
|
765
|
+
return now.toISOString();
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
// src/applog/applog-helpers.ts
|
|
769
|
+
var { WARN: WARN4, LOG: LOG4, DEBUG: DEBUG4, VERBOSE: VERBOSE4, ERROR: ERROR4 } = Logger4.setup(Logger4.INFO);
|
|
770
|
+
function ensureTsPvAndFinalizeApplogs(appLogsToInsert, threadForPv) {
|
|
771
|
+
DEBUG4(`[ensureTsPvAndFinalizeApplogs] ENTER - ${appLogsToInsert.length} applogs, thread size=${threadForPv.size}`);
|
|
772
|
+
const ts = dateNowIso();
|
|
773
|
+
const currentThread = threadForPv;
|
|
774
|
+
DEBUG4(`[ensureTsPvAndFinalizeApplogs] About to map over applogs`);
|
|
775
|
+
const mapped = appLogsToInsert.map((log, idx) => {
|
|
776
|
+
DEBUG4(`[ensureTsPvAndFinalizeApplogs] Processing applog ${idx + 1}/${appLogsToInsert.length}`);
|
|
777
|
+
const result = finalizeApplogForInsert(log, { ts, threadForPv: currentThread });
|
|
778
|
+
DEBUG4(`[ensureTsPvAndFinalizeApplogs] Finalized applog ${idx + 1}/${appLogsToInsert.length}`);
|
|
779
|
+
return result;
|
|
780
|
+
});
|
|
781
|
+
DEBUG4(`[ensureTsPvAndFinalizeApplogs] EXIT - mapped ${mapped.length} applogs`);
|
|
782
|
+
return mapped;
|
|
783
|
+
}
|
|
784
|
+
function ensureTsPvAndFinalizeApplog(applogToInsert, threadForPv) {
|
|
785
|
+
return ensureTsPvAndFinalizeApplogs([applogToInsert], threadForPv)[0];
|
|
786
|
+
}
|
|
787
|
+
function finalizeApplogForInsert(log, { ts, threadForPv } = {}) {
|
|
788
|
+
DEBUG4(`[finalizeApplogForInsert] ENTER - en=${log.en}, at=${log.at}`);
|
|
789
|
+
DEBUG4(`[finalizeApplogForInsert] About to call withTs`);
|
|
790
|
+
const logWithTs = withTs(log, ts ?? dateNowIso());
|
|
791
|
+
DEBUG4(`[finalizeApplogForInsert] About to call withPvFrom (thread size=${threadForPv?.size ?? "null"})`);
|
|
792
|
+
const logWithPv = withPvFrom(logWithTs, threadForPv);
|
|
793
|
+
DEBUG4(`[finalizeApplogForInsert] About to call encodeApplogAndGetCid`);
|
|
794
|
+
const cid = encodeApplogAndGetCid(logWithPv).toString();
|
|
795
|
+
DEBUG4(`[finalizeApplogForInsert] CID created: ${cid}`);
|
|
796
|
+
if (log.cid && log.cid !== cid) WARN4(`[finalizeApplogForInsert] overwriting wrong CID`, { log, cid, logWithPv });
|
|
797
|
+
const logWithCid = { ...logWithPv, cid };
|
|
798
|
+
DEBUG4(`[finalizeApplogForInsert] About to validate applog`);
|
|
799
|
+
if (!isValidApplog(logWithCid)) {
|
|
800
|
+
throw ERROR4(`Bogus Applog ${JSON.stringify(logWithCid)}`, getApplogTypeErrors(logWithCid));
|
|
801
|
+
}
|
|
802
|
+
DEBUG4(`[finalizeApplogForInsert] EXIT - CID=${cid}`);
|
|
803
|
+
return Object.freeze(logWithCid);
|
|
804
|
+
}
|
|
805
|
+
function hasAg(log) {
|
|
806
|
+
return !!log.ag;
|
|
807
|
+
}
|
|
808
|
+
function hasTs(log) {
|
|
809
|
+
return !!log.ts;
|
|
810
|
+
}
|
|
811
|
+
function hasPv(log) {
|
|
812
|
+
return !!log.pv;
|
|
813
|
+
}
|
|
814
|
+
function withTs(log, ts) {
|
|
815
|
+
return hasTs(log) ? log : { ...log, ts };
|
|
816
|
+
}
|
|
817
|
+
function withAg(log, ag) {
|
|
818
|
+
return hasAg(log) ? log : { ...log, ag };
|
|
819
|
+
}
|
|
820
|
+
function withPvFrom(log, thread) {
|
|
821
|
+
DEBUG4(`[withPvFrom] ENTER - en=${log.en}, at=${log.at}, hasPv=${log.pv !== void 0}`);
|
|
822
|
+
if (log.pv !== void 0) {
|
|
823
|
+
DEBUG4(`[withPvFrom] EXIT early - pv already set`);
|
|
824
|
+
return log;
|
|
825
|
+
}
|
|
826
|
+
if (!thread) {
|
|
827
|
+
if (!hasPv(log)) throw ERROR4(`[withPvFrom] no thread and no pv:`, log);
|
|
828
|
+
DEBUG4(`[withPvFrom] EXIT - no thread, returning log with existing pv`);
|
|
829
|
+
return log;
|
|
830
|
+
} else {
|
|
831
|
+
const { en, at } = log;
|
|
832
|
+
DEBUG4(`[withPvFrom] About to call thread.findLast for en=${en}, at=${at}, thread.size=${thread.size}`);
|
|
833
|
+
const prevLog = thread.findLast((l) => l.en == en && l.at == at);
|
|
834
|
+
DEBUG4(`[withPvFrom] findLast completed, found=${!!prevLog}`);
|
|
835
|
+
DEBUG4(`[withPvFrom] About to check equality`);
|
|
836
|
+
if (objEqualByKeys(["en", "at", "vl", "ts", "ag"], log, prevLog)) {
|
|
837
|
+
throw ERROR4(`[withPvFrom] Same as previous:`, { log, pv: prevLog, thread });
|
|
838
|
+
}
|
|
839
|
+
const prevLogCid = (log.pv !== void 0 ? log.pv : prevLog?.cid) ?? null;
|
|
840
|
+
DEBUG4(`[withPvFrom] EXIT - prevLogCid=${prevLogCid}`);
|
|
841
|
+
return { ...log, pv: prevLogCid };
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
function joinThreads(threads) {
|
|
845
|
+
const fullJoin = () => {
|
|
846
|
+
if (threads.length < 2) DEBUG4(`joinThreads with count=${threads.length}`);
|
|
847
|
+
return sortApplogsByTs(
|
|
848
|
+
removeDuplicateAppLogs(threads.flatMap((s) => {
|
|
849
|
+
const logs = s.applogs;
|
|
850
|
+
if (!logs) {
|
|
851
|
+
ERROR4(`falsy applogs of thread`, s);
|
|
852
|
+
throw new Error(`falsy applogs of thread`);
|
|
853
|
+
}
|
|
854
|
+
return logs;
|
|
855
|
+
}), "cleanup")
|
|
856
|
+
);
|
|
857
|
+
};
|
|
858
|
+
let initialMergeResult = fullJoin();
|
|
859
|
+
const eventMapper = function(event, sourceThread) {
|
|
860
|
+
if (isInitEvent(event)) {
|
|
861
|
+
return { init: fullJoin() };
|
|
862
|
+
} else {
|
|
863
|
+
return {
|
|
864
|
+
// TODO: test this stuff
|
|
865
|
+
added: event.added.filter((addedLog) => !this.hasApplog(addedLog, true)),
|
|
866
|
+
removed: event.removed?.filter((removedLog) => {
|
|
867
|
+
const otherParentHasLog = !this.parents.some((parent) => {
|
|
868
|
+
if (parent === sourceThread) return false;
|
|
869
|
+
return parent.hasApplog(removedLog, true);
|
|
870
|
+
});
|
|
871
|
+
return !otherParentHasLog;
|
|
872
|
+
})
|
|
873
|
+
};
|
|
874
|
+
}
|
|
875
|
+
};
|
|
876
|
+
return new MappedThread(
|
|
877
|
+
`join(~ ${threads.map((s) => s.name).join(", ")})`,
|
|
878
|
+
threads,
|
|
879
|
+
["?"],
|
|
880
|
+
// HACK this basically says "we're not sure what filters are applied"
|
|
881
|
+
initialMergeResult,
|
|
882
|
+
eventMapper
|
|
883
|
+
);
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
// src/query/memoized.ts
|
|
887
|
+
import { isEqual as isEqual2 } from "lodash-es";
|
|
888
|
+
function compareStructural(argsA, argsB, versionsA) {
|
|
889
|
+
if (argsA.length !== argsB.length) return false;
|
|
890
|
+
for (let i = 0; i < argsA.length; i++) {
|
|
891
|
+
if (argsA[i] instanceof Thread) {
|
|
892
|
+
if (argsB[i] !== argsA[i]) return false;
|
|
893
|
+
if (versionsA && versionsA[i] !== void 0 && argsB[i]._version !== versionsA[i]) return false;
|
|
894
|
+
} else {
|
|
895
|
+
if (!isEqual2(argsA[i], argsB[i])) return false;
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
return true;
|
|
899
|
+
}
|
|
900
|
+
function snapshotVersions(args) {
|
|
901
|
+
return args.map((a) => a instanceof Thread ? a._version : void 0);
|
|
902
|
+
}
|
|
903
|
+
function memoizedFn(name, fn, opts) {
|
|
904
|
+
const cache = [];
|
|
905
|
+
const argsEqual = opts?.argsEqual ?? compareStructural;
|
|
906
|
+
const maxSize = opts?.maxSize ?? Infinity;
|
|
907
|
+
return function(...args) {
|
|
908
|
+
const existing = cache.find((entry) => argsEqual(entry.args, args, entry.versions));
|
|
909
|
+
if (existing) return existing.result;
|
|
910
|
+
const result = fn.apply(this, args);
|
|
911
|
+
if (cache.length >= maxSize) {
|
|
912
|
+
cache.shift();
|
|
913
|
+
}
|
|
914
|
+
cache.push({ args, versions: snapshotVersions(args), result });
|
|
915
|
+
return result;
|
|
916
|
+
};
|
|
917
|
+
}
|
|
918
|
+
function refCountedMemoizedFn(name, fn, opts) {
|
|
919
|
+
const cache = [];
|
|
920
|
+
const argsEqual = opts?.argsEqual ?? compareStructural;
|
|
921
|
+
const gracePeriodMs = opts?.gracePeriodMs ?? 0;
|
|
922
|
+
function evict(entry, args) {
|
|
923
|
+
const idx = cache.indexOf(entry);
|
|
924
|
+
if (idx >= 0) cache.splice(idx, 1);
|
|
925
|
+
opts?.onCleanup?.(entry.result, ...args);
|
|
926
|
+
}
|
|
927
|
+
return function(...args) {
|
|
928
|
+
let entry = cache.find((e) => argsEqual(e.args, args));
|
|
929
|
+
if (entry) {
|
|
930
|
+
if (entry.graceTimer !== null) {
|
|
931
|
+
clearTimeout(entry.graceTimer);
|
|
932
|
+
entry.graceTimer = null;
|
|
933
|
+
}
|
|
934
|
+
entry.refCount++;
|
|
935
|
+
} else {
|
|
936
|
+
const result = fn.apply(this, args);
|
|
937
|
+
entry = { args, result, refCount: 1, graceTimer: null };
|
|
938
|
+
cache.push(entry);
|
|
939
|
+
}
|
|
940
|
+
const capturedEntry = entry;
|
|
941
|
+
let released = false;
|
|
942
|
+
return {
|
|
943
|
+
get value() {
|
|
944
|
+
return capturedEntry.result;
|
|
945
|
+
},
|
|
946
|
+
release() {
|
|
947
|
+
if (released) return;
|
|
948
|
+
released = true;
|
|
949
|
+
capturedEntry.refCount--;
|
|
950
|
+
if (capturedEntry.refCount <= 0) {
|
|
951
|
+
if (gracePeriodMs > 0) {
|
|
952
|
+
capturedEntry.graceTimer = setTimeout(() => {
|
|
953
|
+
if (capturedEntry.refCount <= 0) {
|
|
954
|
+
evict(capturedEntry, args);
|
|
955
|
+
}
|
|
956
|
+
}, gracePeriodMs);
|
|
957
|
+
} else {
|
|
958
|
+
evict(capturedEntry, args);
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
};
|
|
963
|
+
};
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
// src/thread/mapped.ts
|
|
967
|
+
import { Logger as Logger5 } from "besonders-logger";
|
|
968
|
+
import { sortedIndexBy } from "lodash-es";
|
|
969
|
+
var { WARN: WARN5, LOG: LOG5, DEBUG: DEBUG5, VERBOSE: VERBOSE5, ERROR: ERROR5 } = Logger5.setup(Logger5.INFO);
|
|
970
|
+
var MappedThread = class _MappedThread extends Thread {
|
|
971
|
+
// mapped to unsubscribe function
|
|
972
|
+
constructor(name, parents, filters, _initialLogs, _eventMapper, _writeMapper = null, _readOnly) {
|
|
973
|
+
super(
|
|
974
|
+
name,
|
|
975
|
+
parents,
|
|
976
|
+
filters,
|
|
977
|
+
_eventMapper ? [..._initialLogs] : _initialLogs
|
|
978
|
+
// if we don't map events, we just re-use the array and then don't need to subscribe
|
|
979
|
+
);
|
|
980
|
+
this.name = name;
|
|
981
|
+
this._initialLogs = _initialLogs;
|
|
982
|
+
this._eventMapper = _eventMapper;
|
|
983
|
+
this._writeMapper = _writeMapper;
|
|
984
|
+
this._readOnly = _readOnly;
|
|
985
|
+
if (_eventMapper) {
|
|
986
|
+
this.subscribeToParents();
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
static mapWrites(parent, name, mapper) {
|
|
990
|
+
return new _MappedThread(
|
|
991
|
+
`${name}<${parent.nameAndSizeUntracked}>`,
|
|
992
|
+
parent,
|
|
993
|
+
parent.filters,
|
|
994
|
+
// @ts-expect-error ? what's the proper way
|
|
995
|
+
parent._applogs,
|
|
996
|
+
null,
|
|
997
|
+
mapper
|
|
998
|
+
);
|
|
999
|
+
}
|
|
1000
|
+
static asReadOnly(parent) {
|
|
1001
|
+
if (parent.readOnly) return parent;
|
|
1002
|
+
return new _MappedThread(
|
|
1003
|
+
`readOnly(${parent.name})`,
|
|
1004
|
+
parent,
|
|
1005
|
+
parent.filters,
|
|
1006
|
+
// @ts-expect-error ? what's the proper way
|
|
1007
|
+
parent._applogs,
|
|
1008
|
+
null,
|
|
1009
|
+
null,
|
|
1010
|
+
true
|
|
1011
|
+
// readOnly
|
|
1012
|
+
);
|
|
1013
|
+
}
|
|
1014
|
+
_parentSubscriptions = null;
|
|
1015
|
+
insert(appLogsToInsert) {
|
|
1016
|
+
if (this.readOnly) throw ERROR5(`[MappedThread] insert() called on read-only thread:`, this.nameAndSizeUntracked);
|
|
1017
|
+
const mapped = this._writeMapper ? this._writeMapper(appLogsToInsert) : appLogsToInsert;
|
|
1018
|
+
if (this._writeMapper && !mapped) return;
|
|
1019
|
+
return this.parents.forEach((parent) => parent.insert(mapped));
|
|
1020
|
+
}
|
|
1021
|
+
insertRaw(appLogsToInsert) {
|
|
1022
|
+
if (this.readOnly) throw ERROR5(`[MappedThread] insertRaw() called on read-only thread:`, this.nameAndSizeUntracked);
|
|
1023
|
+
const mapped = this._writeMapper ? this._writeMapper(appLogsToInsert) : appLogsToInsert;
|
|
1024
|
+
if (this._writeMapper && !mapped) return;
|
|
1025
|
+
return this.parents.forEach((parent) => parent.insertRaw(mapped));
|
|
1026
|
+
}
|
|
1027
|
+
subscribeToParents() {
|
|
1028
|
+
this._parentSubscriptions = /* @__PURE__ */ new Map();
|
|
1029
|
+
if (!this.parents.length) {
|
|
1030
|
+
WARN5(`MappedThread has no parents`, this);
|
|
1031
|
+
}
|
|
1032
|
+
VERBOSE5(`[MappedThread: ${this.name}] subscribing to parents:`, this.parents.map((p) => p.name));
|
|
1033
|
+
for (const p of this.parents) {
|
|
1034
|
+
VERBOSE5(`[MappedThread: ${this.name}] sub to parent`, p.nameAndSizeUntracked);
|
|
1035
|
+
const sub = this.onParentUpdate.bind(this, p);
|
|
1036
|
+
const unsubscribe = p.subscribe(sub, "derived");
|
|
1037
|
+
this._parentSubscriptions.set(p, unsubscribe);
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
/** Tear down parent subscriptions and clear internal state */
|
|
1041
|
+
dispose() {
|
|
1042
|
+
if (this._parentSubscriptions) {
|
|
1043
|
+
for (const [, unsubscribe] of this._parentSubscriptions) {
|
|
1044
|
+
unsubscribe();
|
|
1045
|
+
}
|
|
1046
|
+
this._parentSubscriptions = null;
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
/** Swap parents at runtime — re-subscribes and recomputes applogs, notifying downstream */
|
|
1050
|
+
setParents(newParents) {
|
|
1051
|
+
this.dispose();
|
|
1052
|
+
this.parents = newParents;
|
|
1053
|
+
this.subscribeToParents();
|
|
1054
|
+
this.triggerRemap();
|
|
1055
|
+
}
|
|
1056
|
+
subscribe(callback, type) {
|
|
1057
|
+
if (!this._parentSubscriptions) {
|
|
1058
|
+
this.subscribeToParents();
|
|
1059
|
+
}
|
|
1060
|
+
return super.subscribe(callback, type);
|
|
1061
|
+
}
|
|
1062
|
+
/**
|
|
1063
|
+
* // HACK to trigger remap on pattern change in rollingFilter
|
|
1064
|
+
* should not be used lightly
|
|
1065
|
+
*/
|
|
1066
|
+
triggerRemap() {
|
|
1067
|
+
if (!this._eventMapper) throw ERROR5(`triggerRemap on a thread that is not actually mapping?!`, this.nameAndSizeUntracked);
|
|
1068
|
+
DEBUG5(`MappedThread{${this.nameAndSizeUntracked}} triggerRemap`);
|
|
1069
|
+
if (this.parents.length !== 1) {
|
|
1070
|
+
WARN5(`MappedThread{${this.nameAndSizeUntracked}} triggerRemap with parentCount=${this.parents.length} - not meant for this`);
|
|
1071
|
+
}
|
|
1072
|
+
this.parents.forEach((p) => {
|
|
1073
|
+
this.onParentUpdate(p, { init: [...p.applogs] });
|
|
1074
|
+
});
|
|
1075
|
+
}
|
|
1076
|
+
onParentUpdate(thread, event) {
|
|
1077
|
+
VERBOSE5(`MappedThread{${this.nameAndSizeUntracked}} parentUpdate`, event);
|
|
1078
|
+
const mapResult = this._eventMapper ? this._eventMapper(event, thread) : event;
|
|
1079
|
+
if (this._eventMapper) {
|
|
1080
|
+
VERBOSE5(`MappedThread{${this.nameAndSizeUntracked}} parentUpdate => mapped`, mapResult);
|
|
1081
|
+
if (isInitEvent(mapResult)) {
|
|
1082
|
+
this._applogs.length = 0;
|
|
1083
|
+
this._applogs.push(...mapResult.init);
|
|
1084
|
+
} else {
|
|
1085
|
+
for (const log of mapResult.added) {
|
|
1086
|
+
this._applogs.splice(sortedIndexBy(this._applogs, log, "ts"), 0, log);
|
|
1087
|
+
}
|
|
1088
|
+
if (mapResult.removed) {
|
|
1089
|
+
for (const toRemove of mapResult.removed) {
|
|
1090
|
+
const idx = this._applogs.indexOf(toRemove);
|
|
1091
|
+
if (idx >= 0) {
|
|
1092
|
+
this._applogs.splice(idx, 1);
|
|
1093
|
+
} else if (!isInitEvent(event) && event.removed?.includes(toRemove)) {
|
|
1094
|
+
DEBUG5(`Ignoring remove event for non-existent because it was part of parent event's removed`, toRemove, event);
|
|
1095
|
+
} else {
|
|
1096
|
+
throw ERROR5(`MappedThread{${this.name}} toRemove contained log that doesn't exist`, toRemove, {
|
|
1097
|
+
thread: this,
|
|
1098
|
+
event,
|
|
1099
|
+
mapResult
|
|
1100
|
+
});
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
this.notifySubscribers(mapResult);
|
|
1107
|
+
}
|
|
1108
|
+
get readOnly() {
|
|
1109
|
+
return this._readOnly ?? false;
|
|
1110
|
+
}
|
|
1111
|
+
};
|
|
1112
|
+
|
|
1113
|
+
// src/thread/filters.ts
|
|
1114
|
+
var { WARN: WARN6, LOG: LOG6, DEBUG: DEBUG6, VERBOSE: VERBOSE6, ERROR: ERROR6 } = Logger6.setup(Logger6.INFO);
|
|
1115
|
+
var rollingFilter2 = memoizedFn("rollingFilter", function rollingFilter3(thread, pattern, opts = {}) {
|
|
1116
|
+
const filter = makeFilter(pattern);
|
|
1117
|
+
const initialLogs = filter(thread.applogs);
|
|
1118
|
+
const handleUpdateEvent = (event) => {
|
|
1119
|
+
let mappedEvent;
|
|
1120
|
+
if (isInitEvent(event)) {
|
|
1121
|
+
mappedEvent = { init: filter(event.init) };
|
|
1122
|
+
} else {
|
|
1123
|
+
mappedEvent = {
|
|
1124
|
+
added: filter(event.added),
|
|
1125
|
+
removed: event.removed
|
|
1126
|
+
// whatever's removed shall be removed
|
|
1127
|
+
};
|
|
1128
|
+
}
|
|
1129
|
+
VERBOSE6(
|
|
1130
|
+
`rollingFilter{${thread.nameAndSizeUntracked} | ${opts.name ? ` '${opts.name}'}` : ""} parentUpdate`,
|
|
1131
|
+
pattern,
|
|
1132
|
+
event,
|
|
1133
|
+
"=>",
|
|
1134
|
+
mappedEvent
|
|
1135
|
+
);
|
|
1136
|
+
return mappedEvent;
|
|
1137
|
+
};
|
|
1138
|
+
const mappedThread = new MappedThread(
|
|
1139
|
+
`${thread.name} | ${opts.name || `rollingFilter{${stringify3(pattern)}}`}`,
|
|
1140
|
+
thread,
|
|
1141
|
+
[...thread.filters, ...opts.extraFilterName ? [opts.extraFilterName] : []],
|
|
1142
|
+
initialLogs,
|
|
1143
|
+
handleUpdateEvent
|
|
1144
|
+
);
|
|
1145
|
+
return mappedThread;
|
|
1146
|
+
}, {
|
|
1147
|
+
argsDebugName: (thread, pattern, opts) => createDebugName({ caller: "rollingFilter", thread, pattern, args: opts })
|
|
1148
|
+
});
|
|
1149
|
+
var rollingMapper = memoizedFn("rollingMapper", function rollingMapper2(thread, eventMapper, opts = {}) {
|
|
1150
|
+
const initialMapResult = eventMapper.call(null, { init: thread.applogs }, thread);
|
|
1151
|
+
if (!isInitEvent(initialMapResult)) {
|
|
1152
|
+
throw ERROR6("Initial run must return init event");
|
|
1153
|
+
}
|
|
1154
|
+
const initialLogs = initialMapResult.init;
|
|
1155
|
+
return new MappedThread(
|
|
1156
|
+
`${thread.name} | ${opts.name || `rollingMapper`}`,
|
|
1157
|
+
thread,
|
|
1158
|
+
[...thread.filters, ...opts.extraFilterName ? [opts.extraFilterName] : []],
|
|
1159
|
+
// @ts-expect-error readonly.... FIXME
|
|
1160
|
+
initialLogs,
|
|
1161
|
+
eventMapper
|
|
1162
|
+
);
|
|
1163
|
+
}, {
|
|
1164
|
+
argsDebugName: (thread, _mapper, opts) => createDebugName({ caller: "rollingMapper", thread, args: opts })
|
|
1165
|
+
});
|
|
1166
|
+
var rollingAcc = memoizedFn(
|
|
1167
|
+
"rollingAcc",
|
|
1168
|
+
function rollingAcc2(thread, acc, eventMapper, opts = {}) {
|
|
1169
|
+
eventMapper({ init: thread.applogs }, acc);
|
|
1170
|
+
const result = new SubscribableImpl(
|
|
1171
|
+
acc,
|
|
1172
|
+
() => thread.subscribe((event) => {
|
|
1173
|
+
eventMapper(event, acc);
|
|
1174
|
+
result._set(acc);
|
|
1175
|
+
}, "derived"),
|
|
1176
|
+
{ equals: false }
|
|
1177
|
+
// accumulator is mutated in-place
|
|
1178
|
+
);
|
|
1179
|
+
return result;
|
|
1180
|
+
},
|
|
1181
|
+
{ argsDebugName: (thread, _acc, _mapper, opts) => `rollingAcc{${thread.nameAndSizeUntracked}${opts?.name ? ` | ${opts?.name}` : ""}}` }
|
|
1182
|
+
);
|
|
1183
|
+
var getUntrackedPattern = function getUntrackedPattern2(pattern) {
|
|
1184
|
+
if (!Object.entries(pattern).length) {
|
|
1185
|
+
throw new Error(`Pattern is empty`);
|
|
1186
|
+
}
|
|
1187
|
+
return pattern;
|
|
1188
|
+
};
|
|
1189
|
+
function makeFilter(pattern) {
|
|
1190
|
+
return function madeFilter(logs) {
|
|
1191
|
+
return logs.filter(function madeFilterSingleLog(applog) {
|
|
1192
|
+
for (const field of Object.keys(pattern)) {
|
|
1193
|
+
let patternValue = pattern[field];
|
|
1194
|
+
if (patternValue === void 0) continue;
|
|
1195
|
+
const applogValue = applog[field.startsWith("!") ? field.slice(1) : field];
|
|
1196
|
+
const patternValT = patternValue;
|
|
1197
|
+
if (!matchPartStatic(field, patternValT, applogValue)) {
|
|
1198
|
+
return false;
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
return true;
|
|
1202
|
+
});
|
|
1203
|
+
};
|
|
1204
|
+
}
|
|
1205
|
+
var getUntrackedFilterResults = function getUntrackedFilterResults2(thread, pattern, opts = {}) {
|
|
1206
|
+
const untrackedPattern = getUntrackedPattern(pattern);
|
|
1207
|
+
const filter = makeFilter(untrackedPattern);
|
|
1208
|
+
return filter(thread.applogs);
|
|
1209
|
+
};
|
|
1210
|
+
function hasFilter(thread, filter) {
|
|
1211
|
+
return thread.filters.includes(filter);
|
|
1212
|
+
}
|
|
1213
|
+
function assertRaw(thread) {
|
|
1214
|
+
if (thread.filters.length) {
|
|
1215
|
+
WARN6(`[assertRaw] but '${thread.nameAndSizeUntracked}' has filters:`, thread.filters);
|
|
1216
|
+
}
|
|
1217
|
+
return thread;
|
|
1218
|
+
}
|
|
1219
|
+
function assertOnlyCurrent(thread) {
|
|
1220
|
+
if (!hasFilter(thread, "lastWriteWins") || !hasFilter(thread, "withoutDeleted")) throw ERROR6(`should be filtered thread, but is:`, thread.filters);
|
|
1221
|
+
return thread;
|
|
1222
|
+
}
|
|
1223
|
+
var asReadOnly = MappedThread.asReadOnly;
|
|
1224
|
+
var simpleApplogMapper = function simpleApplogMapper2(thread, logMapper, opts = {}) {
|
|
1225
|
+
const mappedTo = /* @__PURE__ */ new Map();
|
|
1226
|
+
const mapLogs = (applogs, thread2) => {
|
|
1227
|
+
const ts = dateNowIso();
|
|
1228
|
+
return applogs.map((log) => {
|
|
1229
|
+
const mapped = logMapper(log, thread2);
|
|
1230
|
+
let mapTo;
|
|
1231
|
+
if (mapped === log) {
|
|
1232
|
+
mapTo = log;
|
|
1233
|
+
} else {
|
|
1234
|
+
if (mapped.cid === log.cid) {
|
|
1235
|
+
delete mapped.cid;
|
|
1236
|
+
}
|
|
1237
|
+
mapTo = finalizeApplogForInsert(mapped, {
|
|
1238
|
+
ts,
|
|
1239
|
+
threadForPv: null
|
|
1240
|
+
// ? should not be inferred, right?
|
|
1241
|
+
});
|
|
1242
|
+
}
|
|
1243
|
+
mappedTo.set(log.cid, mapTo);
|
|
1244
|
+
return mapTo;
|
|
1245
|
+
});
|
|
1246
|
+
};
|
|
1247
|
+
const handleUpdateEvent = (event) => {
|
|
1248
|
+
let mappedEvent;
|
|
1249
|
+
if (isInitEvent(event)) {
|
|
1250
|
+
mappedEvent = { init: mapLogs(event.init, thread) };
|
|
1251
|
+
} else {
|
|
1252
|
+
mappedEvent = {
|
|
1253
|
+
added: mapLogs(event.added, thread),
|
|
1254
|
+
removed: event.removed.map((removedSourceLog) => {
|
|
1255
|
+
const mappedLog = mappedTo.get(removedSourceLog.cid);
|
|
1256
|
+
if (!mappedLog) {
|
|
1257
|
+
throw ERROR6(`[simpleApplogMapper] Parent remove event for Applog that we don't know about`, { removedSourceLog });
|
|
1258
|
+
}
|
|
1259
|
+
mappedTo.delete(removedSourceLog.cid);
|
|
1260
|
+
return mappedLog;
|
|
1261
|
+
})
|
|
1262
|
+
};
|
|
1263
|
+
}
|
|
1264
|
+
VERBOSE6(
|
|
1265
|
+
`simpleApplogMapper{${thread.nameAndSizeUntracked} | ${opts?.name ? ` '${opts?.name}'}` : ""} parentUpdate`,
|
|
1266
|
+
event,
|
|
1267
|
+
"=>",
|
|
1268
|
+
mappedEvent
|
|
1269
|
+
);
|
|
1270
|
+
return mappedEvent;
|
|
1271
|
+
};
|
|
1272
|
+
const mappedThread = rollingMapper(thread, handleUpdateEvent, opts);
|
|
1273
|
+
VERBOSE6.isDisabled || VERBOSE6(`simpleApplogMapper<${thread.nameAndSizeUntracked}> initial mapped to`, mappedThread.applogs);
|
|
1274
|
+
return mappedThread;
|
|
1275
|
+
};
|
|
1276
|
+
|
|
1277
|
+
// src/thread/writeable.ts
|
|
1278
|
+
import { Logger as Logger7 } from "besonders-logger";
|
|
1279
|
+
var { WARN: WARN7, LOG: LOG7, DEBUG: DEBUG7, VERBOSE: VERBOSE7, ERROR: ERROR7 } = Logger7.setup(Logger7.INFO);
|
|
1280
|
+
var WriteableThread = class extends Thread {
|
|
1281
|
+
constructor(name, applogs = [], filters) {
|
|
1282
|
+
super(name, null, filters, applogs);
|
|
1283
|
+
}
|
|
1284
|
+
purge(cidsToPurge) {
|
|
1285
|
+
const beforeCount = this.applogs.length;
|
|
1286
|
+
this._applogs = this.applogs.filter((log) => !cidsToPurge.includes(log.cid));
|
|
1287
|
+
return beforeCount - this.applogs.length;
|
|
1288
|
+
}
|
|
1289
|
+
insert(appLogsToInsert) {
|
|
1290
|
+
DEBUG7(`[WriteableThread.insert] ENTER - ${appLogsToInsert.length} applogs for thread "${this.name}"`);
|
|
1291
|
+
DEBUG7(`[WriteableThread.insert] About to call ensureTsPvAndFinalizeApplogs`);
|
|
1292
|
+
const mapped = ensureTsPvAndFinalizeApplogs(appLogsToInsert, this);
|
|
1293
|
+
DEBUG7(`[WriteableThread.insert] ensureTsPvAndFinalizeApplogs completed, mapped=${mapped.length} applogs`);
|
|
1294
|
+
DEBUG7(`[WriteableThread.insert] About to call insertRaw`);
|
|
1295
|
+
const result = this.insertRaw(mapped);
|
|
1296
|
+
DEBUG7(`[WriteableThread.insert] insertRaw completed`);
|
|
1297
|
+
return result;
|
|
1298
|
+
}
|
|
1299
|
+
/**
|
|
1300
|
+
* Insert only applogs not already in this thread.
|
|
1301
|
+
* @param byRef If true, compares by reference; if false, compares by CID (default)
|
|
1302
|
+
* @returns The applogs that were actually inserted
|
|
1303
|
+
*/
|
|
1304
|
+
insertMissing(appLogsToInsert, byRef = false) {
|
|
1305
|
+
const missing = appLogsToInsert.filter((log) => !this.hasApplog(log, byRef));
|
|
1306
|
+
if (missing.length === 0) {
|
|
1307
|
+
VERBOSE7(`[insertMissing] no missing applogs`);
|
|
1308
|
+
return [];
|
|
1309
|
+
}
|
|
1310
|
+
return this.insertRaw(missing) ?? [];
|
|
1311
|
+
}
|
|
1312
|
+
/**
|
|
1313
|
+
* Insert raw applogs directly into the thread.
|
|
1314
|
+
*
|
|
1315
|
+
* STRICT VALIDATION: This method throws errors for:
|
|
1316
|
+
* - Duplicate applogs in input array (programming error)
|
|
1317
|
+
* - Invalid applogs (missing required fields)
|
|
1318
|
+
* - Applogs already in thread (programming error)
|
|
1319
|
+
*
|
|
1320
|
+
* For external imports where duplicates are expected, use removeDuplicateAppLogs(..., 'cleanup')
|
|
1321
|
+
* before calling this method.
|
|
1322
|
+
*
|
|
1323
|
+
* @param appLogsToInsert Must be deduplicated and validated. Needs to be mutable because it will be sorted
|
|
1324
|
+
* (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
|
|
1325
|
+
* @throws Error if validation fails
|
|
1326
|
+
*/
|
|
1327
|
+
insertRaw(appLogsToInsert) {
|
|
1328
|
+
DEBUG7(`[WriteableThread.insertRaw] ENTER - ${appLogsToInsert.length} applogs for thread "${this.name}"`);
|
|
1329
|
+
DEBUG7(`[WriteableThread.insertRaw] About to deduplicate`);
|
|
1330
|
+
const deduplicated = removeDuplicateAppLogs(appLogsToInsert, "safety");
|
|
1331
|
+
if (deduplicated.length !== appLogsToInsert.length) {
|
|
1332
|
+
throw ERROR7(`[insertRaw] duplicate applogs passed: ${appLogsToInsert.length - deduplicated.length}`, {
|
|
1333
|
+
appLogsToInsert,
|
|
1334
|
+
deduplicated
|
|
1335
|
+
});
|
|
1336
|
+
}
|
|
1337
|
+
DEBUG7(`[WriteableThread.insertRaw] Deduplication done`);
|
|
1338
|
+
DEBUG7(`[WriteableThread.insertRaw] About to validate`);
|
|
1339
|
+
const bogus = appLogsToInsert.filter((log) => !isValidApplog(log));
|
|
1340
|
+
if (bogus.length) {
|
|
1341
|
+
throw ERROR7(`[insertRaw] bogus applogs passed: ${bogus.length}`, { bogus });
|
|
1342
|
+
}
|
|
1343
|
+
DEBUG7(`[WriteableThread.insertRaw] Validation done`);
|
|
1344
|
+
DEBUG7(`[WriteableThread.insertRaw] About to check for existing`);
|
|
1345
|
+
const existing = appLogsToInsert.filter((log) => this.hasApplog(log, false));
|
|
1346
|
+
if (existing.length) {
|
|
1347
|
+
throw ERROR7(`[insertRaw] already existing applogs passed: ${existing.length}`, { existing });
|
|
1348
|
+
}
|
|
1349
|
+
DEBUG7(`[WriteableThread.insertRaw] Existing check done`);
|
|
1350
|
+
if (!appLogsToInsert.length) {
|
|
1351
|
+
WARN7("[insertRaw] skipping empty insert empty logs array");
|
|
1352
|
+
return;
|
|
1353
|
+
}
|
|
1354
|
+
;
|
|
1355
|
+
(!this.hasParents && !(this instanceof ThreadInMemory) ? LOG7 : DEBUG7)(
|
|
1356
|
+
"Inserting:",
|
|
1357
|
+
appLogsToInsert.length === 1 ? appLogsToInsert[0] : appLogsToInsert,
|
|
1358
|
+
{ ds: this }
|
|
1359
|
+
);
|
|
1360
|
+
DEBUG7(`[WriteableThread.insertRaw] About to sort applogs`);
|
|
1361
|
+
sortApplogsByTs(appLogsToInsert);
|
|
1362
|
+
const sortNeeded = this._applogs.length && isTsBefore(appLogsToInsert[0], this._applogs[this._applogs.length - 1]);
|
|
1363
|
+
DEBUG7(`[WriteableThread.insertRaw] About to push to _applogs array`);
|
|
1364
|
+
const CHUNK_SIZE = 5e4;
|
|
1365
|
+
for (let i = 0; i < appLogsToInsert.length; i += CHUNK_SIZE) {
|
|
1366
|
+
this._applogs.push(...appLogsToInsert.slice(i, i + CHUNK_SIZE));
|
|
1367
|
+
}
|
|
1368
|
+
if (sortNeeded) {
|
|
1369
|
+
DEBUG7(`[WriteableThread.insertRaw] About to sort _applogs (sortNeeded=true)`);
|
|
1370
|
+
sortApplogsByTs(this._applogs);
|
|
1371
|
+
}
|
|
1372
|
+
DEBUG7(`[WriteableThread.insertRaw] About to notify subscribers`);
|
|
1373
|
+
this.notifySubscribers({ added: appLogsToInsert, removed: null });
|
|
1374
|
+
DEBUG7(`[WriteableThread.insertRaw] Subscribers notified`);
|
|
1375
|
+
DEBUG7(`[WriteableThread.insertRaw] About to call persist (void - not awaited)`);
|
|
1376
|
+
void this.persist(appLogsToInsert);
|
|
1377
|
+
DEBUG7(`[WriteableThread.insertRaw] EXIT - returning ${appLogsToInsert.length} applogs`);
|
|
1378
|
+
return appLogsToInsert;
|
|
1379
|
+
}
|
|
1380
|
+
get readOnly() {
|
|
1381
|
+
return false;
|
|
1382
|
+
}
|
|
1383
|
+
};
|
|
1384
|
+
var ThreadInMemory = class _ThreadInMemory extends WriteableThread {
|
|
1385
|
+
constructor(name, applogs, filters, _readOnly) {
|
|
1386
|
+
super(name, applogs, filters);
|
|
1387
|
+
this._readOnly = _readOnly;
|
|
1388
|
+
}
|
|
1389
|
+
static empty(name) {
|
|
1390
|
+
return _ThreadInMemory.fromArray([], name ?? "empty in-memory", false);
|
|
1391
|
+
}
|
|
1392
|
+
static fromArray(applogs, name, readOnly = false) {
|
|
1393
|
+
return new _ThreadInMemory(name ?? "in-memory", applogs, [], readOnly);
|
|
1394
|
+
}
|
|
1395
|
+
static fromReadOnlyArray(applogs, name) {
|
|
1396
|
+
return new _ThreadInMemory(name ?? "in-memory", applogs, [], true);
|
|
1397
|
+
}
|
|
1398
|
+
get readOnly() {
|
|
1399
|
+
return this._readOnly;
|
|
1400
|
+
}
|
|
1401
|
+
async persist(logs) {
|
|
1402
|
+
VERBOSE7(`[InMem.persist] no persist for`, logs);
|
|
1403
|
+
if (this.readOnly) {
|
|
1404
|
+
throw ERROR7(`[persist] called for readOnly thread`);
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
};
|
|
1408
|
+
|
|
1409
|
+
// src/thread/utils.ts
|
|
1410
|
+
import { Logger as Logger8 } from "besonders-logger";
|
|
1411
|
+
import { debounce, uniq, uniqWith } from "lodash-es";
|
|
1412
|
+
var { WARN: WARN8, LOG: LOG8, DEBUG: DEBUG8, VERBOSE: VERBOSE8, ERROR: ERROR8 } = Logger8.setup(Logger8.INFO);
|
|
1413
|
+
function entityCount(thread) {
|
|
1414
|
+
return allEntityIDs(thread).size;
|
|
1415
|
+
}
|
|
1416
|
+
function allEntityIDs(thread) {
|
|
1417
|
+
return accumulateLogsToSet(thread, (log) => log.en);
|
|
1418
|
+
}
|
|
1419
|
+
function accumulateLogsToSet(threadOrLogs, callback) {
|
|
1420
|
+
const logs = threadOrLogs instanceof Thread ? threadOrLogs.applogs : threadOrLogs;
|
|
1421
|
+
const set = /* @__PURE__ */ new Set();
|
|
1422
|
+
for (const log of logs) {
|
|
1423
|
+
set.add(callback(log, set));
|
|
1424
|
+
}
|
|
1425
|
+
return set;
|
|
1426
|
+
}
|
|
1427
|
+
function debounceWrites(thread, wait = 700, removeDuplicatesWith = compareApplogsByEnAt) {
|
|
1428
|
+
if (thread.readOnly) throw ERROR8(`[debounceWrites] but readOnly thread`, thread.name);
|
|
1429
|
+
let insertQueue = [];
|
|
1430
|
+
const debouncedCommit = debounce(() => {
|
|
1431
|
+
WARN8(`Debounce tail`, { thread, mappedThread, insertQueue });
|
|
1432
|
+
const toInsert = ensureTsPvAndFinalizeApplogs(
|
|
1433
|
+
// ? uniq, sure - but which one is used? (update: seems the first one, so reverse)
|
|
1434
|
+
uniqWith(insertQueue.reverse(), removeDuplicatesWith),
|
|
1435
|
+
thread
|
|
1436
|
+
);
|
|
1437
|
+
thread.insertRaw(toInsert);
|
|
1438
|
+
insertQueue.splice(0, insertQueue.length);
|
|
1439
|
+
}, wait);
|
|
1440
|
+
const handleInsert = (applogs) => {
|
|
1441
|
+
DEBUG8(`Debounce input:`, applogs, { thread, mappedThread, insertQueue });
|
|
1442
|
+
insertQueue.push(...applogs);
|
|
1443
|
+
debouncedCommit();
|
|
1444
|
+
return null;
|
|
1445
|
+
};
|
|
1446
|
+
const mappedThread = MappedThread.mapWrites(
|
|
1447
|
+
thread,
|
|
1448
|
+
`Debounce(${wait})`,
|
|
1449
|
+
handleInsert
|
|
1450
|
+
);
|
|
1451
|
+
return mappedThread;
|
|
1452
|
+
}
|
|
1453
|
+
function holdTillFirstWrite(thread, applogsToHold, opts) {
|
|
1454
|
+
DEBUG8(`[holdTillFirstWrite] holding logs:`, { applogsToHold });
|
|
1455
|
+
if (thread.readOnly) throw ERROR8(`[holdTillFirstWrite] but readOnly thread`, thread.nameAndSizeUntracked);
|
|
1456
|
+
const heldLogs = ensureTsPvAndFinalizeApplogs([...applogsToHold], thread);
|
|
1457
|
+
const heldThread = ThreadInMemory.fromArray(heldLogs);
|
|
1458
|
+
let hasInserted = false;
|
|
1459
|
+
const handleInsert = (realApplogs) => {
|
|
1460
|
+
if (hasInserted) return realApplogs;
|
|
1461
|
+
hasInserted = true;
|
|
1462
|
+
let toInsert = [...heldLogs];
|
|
1463
|
+
if (opts.deduplicateHoldItemsWith) {
|
|
1464
|
+
toInsert = toInsert.filter(
|
|
1465
|
+
(heldLog) => (
|
|
1466
|
+
// some duplicate? so don't insert
|
|
1467
|
+
!realApplogs.some((realLog) => opts.deduplicateHoldItemsWith(heldLog, realLog))
|
|
1468
|
+
)
|
|
1469
|
+
);
|
|
1470
|
+
}
|
|
1471
|
+
if (opts.onFirstWrite) {
|
|
1472
|
+
const callbackResult = opts.onFirstWrite(toInsert);
|
|
1473
|
+
if (callbackResult !== void 0) {
|
|
1474
|
+
toInsert = callbackResult;
|
|
1475
|
+
}
|
|
1476
|
+
}
|
|
1477
|
+
DEBUG8(`[holdTillFirstWrite] adding hold logs:`, { applogsToHold, heldLogs, toInsert, realApplogs });
|
|
1478
|
+
return [...toInsert, ...realApplogs];
|
|
1479
|
+
};
|
|
1480
|
+
const joinedThread = joinThreads([thread, heldThread]);
|
|
1481
|
+
return new MappedThread(
|
|
1482
|
+
`HoldTillFirstWrite[${applogsToHold.length}]<${thread.nameAndSizeUntracked}>`,
|
|
1483
|
+
joinedThread,
|
|
1484
|
+
thread.filters,
|
|
1485
|
+
// @ts-expect-error ? what's the proper way
|
|
1486
|
+
joinedThread.applogs,
|
|
1487
|
+
null,
|
|
1488
|
+
(applogs) => {
|
|
1489
|
+
const logsToInsert = handleInsert(applogs);
|
|
1490
|
+
thread.insert(logsToInsert);
|
|
1491
|
+
return null;
|
|
1492
|
+
}
|
|
1493
|
+
);
|
|
1494
|
+
}
|
|
1495
|
+
function getAgents(thread) {
|
|
1496
|
+
return uniq(thread.map((l) => l.ag));
|
|
1497
|
+
}
|
|
1498
|
+
var excludeApplogsContainedIn = (applogs, exclude) => {
|
|
1499
|
+
const excludeCids = exclude instanceof Set ? new Set([...exclude].map((c) => c.toString())) : exclude instanceof Thread ? new Set(exclude.applogsCids) : new Set(exclude.map((a) => a.cid));
|
|
1500
|
+
return applogs.filter((applog) => {
|
|
1501
|
+
if (!applog.cid) {
|
|
1502
|
+
ERROR8(`applog with missing CID`, applog);
|
|
1503
|
+
throw new Error(`applog with missing CID`);
|
|
1504
|
+
}
|
|
1505
|
+
return !excludeCids.has(applog.cid);
|
|
1506
|
+
});
|
|
1507
|
+
};
|
|
1508
|
+
|
|
1509
|
+
export {
|
|
1510
|
+
Nullable,
|
|
1511
|
+
EntityID_LENGTH,
|
|
1512
|
+
EntityID,
|
|
1513
|
+
isEncryptedApplog,
|
|
1514
|
+
CIDTB,
|
|
1515
|
+
URL,
|
|
1516
|
+
AppLogNoCidTB,
|
|
1517
|
+
AppLogNoCidTBC,
|
|
1518
|
+
getApplogNoCidTypeErrors,
|
|
1519
|
+
isValidApplogNoCid,
|
|
1520
|
+
AppLogTB,
|
|
1521
|
+
AppLogTBC,
|
|
1522
|
+
getApplogTypeErrors,
|
|
1523
|
+
isValidApplog,
|
|
1524
|
+
MULTICODEC_IPNS_KEY,
|
|
1525
|
+
prepareForPub,
|
|
1526
|
+
encodeApplogAndGetCid,
|
|
1527
|
+
encodeApplog,
|
|
1528
|
+
getCidSync,
|
|
1529
|
+
encodeBlock,
|
|
1530
|
+
encodeBlockOriginal,
|
|
1531
|
+
tryParseCID,
|
|
1532
|
+
isIpnsKeyCid,
|
|
1533
|
+
cidToString,
|
|
1534
|
+
toIpnsString,
|
|
1535
|
+
ensureValidCIDinstance,
|
|
1536
|
+
areCidsEqual,
|
|
1537
|
+
containsCid,
|
|
1538
|
+
SubscribableImpl,
|
|
1539
|
+
isArrayInitEvent,
|
|
1540
|
+
SubscribableArrayImpl,
|
|
1541
|
+
createDebugName,
|
|
1542
|
+
createDebugNameObj,
|
|
1543
|
+
prettifyThreadName,
|
|
1544
|
+
isInitEvent,
|
|
1545
|
+
Thread,
|
|
1546
|
+
getLogsFromThread,
|
|
1547
|
+
StaticThread,
|
|
1548
|
+
isoDateStrCompare,
|
|
1549
|
+
objEqualByKeys,
|
|
1550
|
+
compareApplogsByTs,
|
|
1551
|
+
compareApplogsByEnAt,
|
|
1552
|
+
sortApplogsByTs,
|
|
1553
|
+
isTsBefore,
|
|
1554
|
+
uniqueEnFromAppLogs,
|
|
1555
|
+
areApplogsEqual,
|
|
1556
|
+
removeDuplicateAppLogs,
|
|
1557
|
+
getHashID,
|
|
1558
|
+
isVariable,
|
|
1559
|
+
variableNameWithoutQuestionmark,
|
|
1560
|
+
isStaticPattern,
|
|
1561
|
+
resolveOrRemoveVariables,
|
|
1562
|
+
matchPartStatic,
|
|
1563
|
+
matchPart,
|
|
1564
|
+
matchPattern,
|
|
1565
|
+
actualize,
|
|
1566
|
+
arrStats,
|
|
1567
|
+
tsNearlySame,
|
|
1568
|
+
cyrb53hash,
|
|
1569
|
+
arraysContainSameElements,
|
|
1570
|
+
dateNowIso,
|
|
1571
|
+
memoizedFn,
|
|
1572
|
+
refCountedMemoizedFn,
|
|
1573
|
+
MappedThread,
|
|
1574
|
+
rollingFilter2 as rollingFilter,
|
|
1575
|
+
rollingMapper,
|
|
1576
|
+
rollingAcc,
|
|
1577
|
+
getUntrackedPattern,
|
|
1578
|
+
makeFilter,
|
|
1579
|
+
getUntrackedFilterResults,
|
|
1580
|
+
hasFilter,
|
|
1581
|
+
assertRaw,
|
|
1582
|
+
assertOnlyCurrent,
|
|
1583
|
+
asReadOnly,
|
|
1584
|
+
simpleApplogMapper,
|
|
1585
|
+
WriteableThread,
|
|
1586
|
+
ThreadInMemory,
|
|
1587
|
+
entityCount,
|
|
1588
|
+
allEntityIDs,
|
|
1589
|
+
debounceWrites,
|
|
1590
|
+
holdTillFirstWrite,
|
|
1591
|
+
getAgents,
|
|
1592
|
+
excludeApplogsContainedIn,
|
|
1593
|
+
ensureTsPvAndFinalizeApplogs,
|
|
1594
|
+
ensureTsPvAndFinalizeApplog,
|
|
1595
|
+
finalizeApplogForInsert,
|
|
1596
|
+
hasAg,
|
|
1597
|
+
hasTs,
|
|
1598
|
+
hasPv,
|
|
1599
|
+
withTs,
|
|
1600
|
+
withAg,
|
|
1601
|
+
withPvFrom,
|
|
1602
|
+
joinThreads
|
|
1603
|
+
};
|
|
1604
|
+
//# sourceMappingURL=chunk-XIQSYEV3.js.map
|