@rlabs-inc/fsdb 1.0.0
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/README.md +358 -0
- package/dist/core/collection.d.ts +66 -0
- package/dist/core/collection.d.ts.map +1 -0
- package/dist/core/columns.d.ts +47 -0
- package/dist/core/columns.d.ts.map +1 -0
- package/dist/core/constants.d.ts +22 -0
- package/dist/core/constants.d.ts.map +1 -0
- package/dist/core/database.d.ts +55 -0
- package/dist/core/database.d.ts.map +1 -0
- package/dist/core/registry.d.ts +52 -0
- package/dist/core/registry.d.ts.map +1 -0
- package/dist/core/types.d.ts +107 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2682 -0
- package/dist/index.mjs +2656 -0
- package/dist/persistence/markdown.d.ts +57 -0
- package/dist/persistence/markdown.d.ts.map +1 -0
- package/dist/persistence/watcher.d.ts +38 -0
- package/dist/persistence/watcher.d.ts.map +1 -0
- package/dist/query/filters.d.ts +65 -0
- package/dist/query/filters.d.ts.map +1 -0
- package/dist/query/reactive.d.ts +58 -0
- package/dist/query/reactive.d.ts.map +1 -0
- package/dist/vector/search.d.ts +59 -0
- package/dist/vector/search.d.ts.map +1 -0
- package/package.json +53 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,2656 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __toESM = (mod, isNodeMode, target) => {
|
|
8
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
9
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
10
|
+
for (let key of __getOwnPropNames(mod))
|
|
11
|
+
if (!__hasOwnProp.call(to, key))
|
|
12
|
+
__defProp(to, key, {
|
|
13
|
+
get: () => mod[key],
|
|
14
|
+
enumerable: true
|
|
15
|
+
});
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
19
|
+
|
|
20
|
+
// node_modules/@rlabs-inc/signals/dist/index.mjs
|
|
21
|
+
var equals = (oldValue, newValue) => Object.is(oldValue, newValue);
|
|
22
|
+
var DERIVED = 1 << 1;
|
|
23
|
+
var EFFECT = 1 << 2;
|
|
24
|
+
var RENDER_EFFECT = 1 << 3;
|
|
25
|
+
var ROOT_EFFECT = 1 << 4;
|
|
26
|
+
var BRANCH_EFFECT = 1 << 5;
|
|
27
|
+
var USER_EFFECT = 1 << 6;
|
|
28
|
+
var BLOCK_EFFECT = 1 << 7;
|
|
29
|
+
var CLEAN = 1 << 10;
|
|
30
|
+
var DIRTY = 1 << 11;
|
|
31
|
+
var MAYBE_DIRTY = 1 << 12;
|
|
32
|
+
var REACTION_IS_UPDATING = 1 << 13;
|
|
33
|
+
var DESTROYED = 1 << 14;
|
|
34
|
+
var INERT = 1 << 15;
|
|
35
|
+
var EFFECT_RAN = 1 << 16;
|
|
36
|
+
var EFFECT_PRESERVED = 1 << 17;
|
|
37
|
+
var UNOWNED = 1 << 8;
|
|
38
|
+
var DISCONNECTED = 1 << 9;
|
|
39
|
+
var INSPECT_EFFECT = 1 << 18;
|
|
40
|
+
var UNINITIALIZED = Symbol.for("rlabs.signals.uninitialized");
|
|
41
|
+
var STALE_REACTION = Symbol.for("rlabs.signals.stale_reaction");
|
|
42
|
+
var STATE_SYMBOL = Symbol.for("rlabs.signals.state");
|
|
43
|
+
var REACTIVE_MARKER = Symbol.for("rlabs.signals.reactive");
|
|
44
|
+
var STATUS_MASK = ~(DIRTY | MAYBE_DIRTY | CLEAN);
|
|
45
|
+
var activeReaction = null;
|
|
46
|
+
var activeEffect = null;
|
|
47
|
+
var untracking = false;
|
|
48
|
+
var writeVersion = 1;
|
|
49
|
+
var readVersion = 0;
|
|
50
|
+
var newDeps = null;
|
|
51
|
+
var skippedDeps = 0;
|
|
52
|
+
var untrackedWrites = null;
|
|
53
|
+
var batchDepth = 0;
|
|
54
|
+
var pendingReactions = new Set;
|
|
55
|
+
var queuedRootEffects = [];
|
|
56
|
+
var isFlushingSync = false;
|
|
57
|
+
function setActiveReaction(reaction) {
|
|
58
|
+
const prev = activeReaction;
|
|
59
|
+
activeReaction = reaction;
|
|
60
|
+
return prev;
|
|
61
|
+
}
|
|
62
|
+
function setActiveEffect(effect) {
|
|
63
|
+
const prev = activeEffect;
|
|
64
|
+
activeEffect = effect;
|
|
65
|
+
return prev;
|
|
66
|
+
}
|
|
67
|
+
function incrementWriteVersion() {
|
|
68
|
+
return ++writeVersion;
|
|
69
|
+
}
|
|
70
|
+
function incrementReadVersion() {
|
|
71
|
+
return ++readVersion;
|
|
72
|
+
}
|
|
73
|
+
function setNewDeps(deps) {
|
|
74
|
+
const prev = newDeps;
|
|
75
|
+
newDeps = deps;
|
|
76
|
+
return prev;
|
|
77
|
+
}
|
|
78
|
+
function setSkippedDeps(count) {
|
|
79
|
+
const prev = skippedDeps;
|
|
80
|
+
skippedDeps = count;
|
|
81
|
+
return prev;
|
|
82
|
+
}
|
|
83
|
+
function setUntrackedWrites(writes) {
|
|
84
|
+
const prev = untrackedWrites;
|
|
85
|
+
untrackedWrites = writes;
|
|
86
|
+
return prev;
|
|
87
|
+
}
|
|
88
|
+
function addUntrackedWrite(signal) {
|
|
89
|
+
if (untrackedWrites === null) {
|
|
90
|
+
untrackedWrites = [signal];
|
|
91
|
+
} else {
|
|
92
|
+
untrackedWrites.push(signal);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function addPendingReaction(reaction) {
|
|
96
|
+
pendingReactions.add(reaction);
|
|
97
|
+
}
|
|
98
|
+
function clearQueuedRootEffects() {
|
|
99
|
+
const prev = queuedRootEffects;
|
|
100
|
+
queuedRootEffects = [];
|
|
101
|
+
return prev;
|
|
102
|
+
}
|
|
103
|
+
function addQueuedRootEffect(effect) {
|
|
104
|
+
queuedRootEffects.push(effect);
|
|
105
|
+
}
|
|
106
|
+
function scheduleEffect(reaction) {
|
|
107
|
+
addPendingReaction(reaction);
|
|
108
|
+
if (batchDepth > 0) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
let effect = reaction;
|
|
112
|
+
while (effect.parent !== null) {
|
|
113
|
+
effect = effect.parent;
|
|
114
|
+
const flags = effect.f;
|
|
115
|
+
if ((flags & (ROOT_EFFECT | BRANCH_EFFECT)) !== 0) {
|
|
116
|
+
if ((flags & CLEAN) === 0) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
effect.f ^= CLEAN;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
addQueuedRootEffect(effect);
|
|
123
|
+
if (!isFlushingSync) {
|
|
124
|
+
queueMicrotask(flushEffects);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
var updateEffectImpl = () => {
|
|
128
|
+
throw new Error("updateEffect not initialized - import effect.ts first");
|
|
129
|
+
};
|
|
130
|
+
function setUpdateEffectImpl(impl) {
|
|
131
|
+
updateEffectImpl = impl;
|
|
132
|
+
}
|
|
133
|
+
function flushEffects() {
|
|
134
|
+
const roots = clearQueuedRootEffects();
|
|
135
|
+
for (const root of roots) {
|
|
136
|
+
if (isDirty(root)) {
|
|
137
|
+
updateEffectImpl(root);
|
|
138
|
+
}
|
|
139
|
+
processEffectTree(root);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
function processEffectTree(effect) {
|
|
143
|
+
let child = effect.first;
|
|
144
|
+
while (child !== null) {
|
|
145
|
+
const next = child.next;
|
|
146
|
+
if (isDirty(child)) {
|
|
147
|
+
updateEffectImpl(child);
|
|
148
|
+
}
|
|
149
|
+
if (child.first !== null) {
|
|
150
|
+
processEffectTree(child);
|
|
151
|
+
}
|
|
152
|
+
child = next;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
function get(signal) {
|
|
156
|
+
if (activeReaction !== null && !untracking) {
|
|
157
|
+
if ((activeReaction.f & REACTION_IS_UPDATING) !== 0) {
|
|
158
|
+
if (signal.rv < readVersion) {
|
|
159
|
+
signal.rv = readVersion;
|
|
160
|
+
const deps = activeReaction.deps;
|
|
161
|
+
if (newDeps === null && deps !== null && deps[skippedDeps] === signal) {
|
|
162
|
+
setSkippedDeps(skippedDeps + 1);
|
|
163
|
+
} else {
|
|
164
|
+
if (newDeps === null) {
|
|
165
|
+
setNewDeps([signal]);
|
|
166
|
+
} else {
|
|
167
|
+
newDeps.push(signal);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
} else {
|
|
172
|
+
if (activeReaction.deps === null) {
|
|
173
|
+
activeReaction.deps = [signal];
|
|
174
|
+
} else if (!activeReaction.deps.includes(signal)) {
|
|
175
|
+
activeReaction.deps.push(signal);
|
|
176
|
+
}
|
|
177
|
+
if (signal.reactions === null) {
|
|
178
|
+
signal.reactions = [activeReaction];
|
|
179
|
+
} else if (!signal.reactions.includes(activeReaction)) {
|
|
180
|
+
signal.reactions.push(activeReaction);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
if ((signal.f & DERIVED) !== 0) {
|
|
185
|
+
const derived = signal;
|
|
186
|
+
if (isDirty(derived)) {
|
|
187
|
+
updateDerived(derived);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return signal.v;
|
|
191
|
+
}
|
|
192
|
+
function set(signal, value) {
|
|
193
|
+
if (activeReaction !== null && (activeReaction.f & DERIVED) !== 0) {
|
|
194
|
+
throw new Error("Cannot write to signals inside a derived. " + "Deriveds should be pure computations with no side effects.");
|
|
195
|
+
}
|
|
196
|
+
if (!signal.equals(signal.v, value)) {
|
|
197
|
+
signal.v = value;
|
|
198
|
+
signal.wv = incrementWriteVersion();
|
|
199
|
+
markReactions(signal, DIRTY);
|
|
200
|
+
if (activeEffect !== null && (activeEffect.f & CLEAN) !== 0 && (activeEffect.f & (ROOT_EFFECT | BRANCH_EFFECT)) === 0) {
|
|
201
|
+
addUntrackedWrite(signal);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return value;
|
|
205
|
+
}
|
|
206
|
+
function markReactions(signal, status) {
|
|
207
|
+
const reactions = signal.reactions;
|
|
208
|
+
if (reactions === null)
|
|
209
|
+
return;
|
|
210
|
+
for (let i = 0;i < reactions.length; i++) {
|
|
211
|
+
const reaction = reactions[i];
|
|
212
|
+
const flags = reaction.f;
|
|
213
|
+
const notDirty = (flags & DIRTY) === 0;
|
|
214
|
+
if (notDirty) {
|
|
215
|
+
setSignalStatus(reaction, status);
|
|
216
|
+
}
|
|
217
|
+
if ((flags & DERIVED) !== 0) {
|
|
218
|
+
markReactions(reaction, MAYBE_DIRTY);
|
|
219
|
+
} else if (notDirty) {
|
|
220
|
+
scheduleEffect(reaction);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
function setSignalStatus(signal, status) {
|
|
225
|
+
signal.f = signal.f & STATUS_MASK | status;
|
|
226
|
+
}
|
|
227
|
+
function isDirty(reaction) {
|
|
228
|
+
if ((reaction.f & DIRTY) !== 0) {
|
|
229
|
+
return true;
|
|
230
|
+
}
|
|
231
|
+
if ((reaction.f & MAYBE_DIRTY) !== 0) {
|
|
232
|
+
const deps = reaction.deps;
|
|
233
|
+
if (deps !== null) {
|
|
234
|
+
for (let i = 0;i < deps.length; i++) {
|
|
235
|
+
const dep = deps[i];
|
|
236
|
+
if ((dep.f & DERIVED) !== 0) {
|
|
237
|
+
if (isDirty(dep)) {
|
|
238
|
+
updateDerived(dep);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
if (dep.wv > reaction.wv) {
|
|
242
|
+
return true;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
setSignalStatus(reaction, CLEAN);
|
|
247
|
+
}
|
|
248
|
+
return false;
|
|
249
|
+
}
|
|
250
|
+
var updateDerivedImpl = () => {
|
|
251
|
+
throw new Error("updateDerived not initialized - import derived.ts first");
|
|
252
|
+
};
|
|
253
|
+
function updateDerived(derived) {
|
|
254
|
+
updateDerivedImpl(derived);
|
|
255
|
+
}
|
|
256
|
+
function setUpdateDerivedImpl(impl) {
|
|
257
|
+
updateDerivedImpl = impl;
|
|
258
|
+
}
|
|
259
|
+
function removeReactions(reaction, start) {
|
|
260
|
+
const deps = reaction.deps;
|
|
261
|
+
if (deps === null)
|
|
262
|
+
return;
|
|
263
|
+
for (let i = start;i < deps.length; i++) {
|
|
264
|
+
removeReaction(reaction, deps[i]);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
function removeReaction(reaction, dep) {
|
|
268
|
+
const reactions = dep.reactions;
|
|
269
|
+
if (reactions === null)
|
|
270
|
+
return;
|
|
271
|
+
const index = reactions.indexOf(reaction);
|
|
272
|
+
if (index !== -1) {
|
|
273
|
+
const last = reactions.length - 1;
|
|
274
|
+
if (index !== last) {
|
|
275
|
+
reactions[index] = reactions[last];
|
|
276
|
+
}
|
|
277
|
+
reactions.pop();
|
|
278
|
+
if (reactions.length === 0) {
|
|
279
|
+
dep.reactions = null;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
function updateReaction(reaction) {
|
|
284
|
+
const prevNewDeps = newDeps;
|
|
285
|
+
const prevSkippedDeps = skippedDeps;
|
|
286
|
+
const prevReaction = activeReaction;
|
|
287
|
+
const prevUntrackedWrites = untrackedWrites;
|
|
288
|
+
setNewDeps(null);
|
|
289
|
+
setSkippedDeps(0);
|
|
290
|
+
setActiveReaction(reaction);
|
|
291
|
+
setUntrackedWrites(null);
|
|
292
|
+
const prevReadVersion = readVersion;
|
|
293
|
+
incrementReadVersion();
|
|
294
|
+
reaction.f |= REACTION_IS_UPDATING;
|
|
295
|
+
try {
|
|
296
|
+
const result = reaction.fn();
|
|
297
|
+
const deps = reaction.deps;
|
|
298
|
+
if (newDeps !== null) {
|
|
299
|
+
removeReactions(reaction, skippedDeps);
|
|
300
|
+
if (deps !== null && skippedDeps > 0) {
|
|
301
|
+
deps.length = skippedDeps + newDeps.length;
|
|
302
|
+
for (let i = 0;i < newDeps.length; i++) {
|
|
303
|
+
deps[skippedDeps + i] = newDeps[i];
|
|
304
|
+
}
|
|
305
|
+
} else {
|
|
306
|
+
reaction.deps = newDeps;
|
|
307
|
+
}
|
|
308
|
+
const finalDeps = reaction.deps;
|
|
309
|
+
for (let i = skippedDeps;i < finalDeps.length; i++) {
|
|
310
|
+
const dep = finalDeps[i];
|
|
311
|
+
if (dep.reactions === null) {
|
|
312
|
+
dep.reactions = [reaction];
|
|
313
|
+
} else {
|
|
314
|
+
dep.reactions.push(reaction);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
} else if (deps !== null && skippedDeps < deps.length) {
|
|
318
|
+
removeReactions(reaction, skippedDeps);
|
|
319
|
+
deps.length = skippedDeps;
|
|
320
|
+
}
|
|
321
|
+
if (untrackedWrites !== null && reaction.deps !== null && (reaction.f & EFFECT) !== 0 && (reaction.f & DERIVED) === 0) {
|
|
322
|
+
for (const signal of untrackedWrites) {
|
|
323
|
+
if (reaction.deps.includes(signal)) {
|
|
324
|
+
setSignalStatus(reaction, DIRTY);
|
|
325
|
+
scheduleEffect(reaction);
|
|
326
|
+
break;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
return result;
|
|
331
|
+
} finally {
|
|
332
|
+
reaction.f &= ~REACTION_IS_UPDATING;
|
|
333
|
+
setNewDeps(prevNewDeps);
|
|
334
|
+
setSkippedDeps(prevSkippedDeps);
|
|
335
|
+
setActiveReaction(prevReaction);
|
|
336
|
+
if (untrackedWrites !== null) {
|
|
337
|
+
if (prevUntrackedWrites === null) {
|
|
338
|
+
setUntrackedWrites(untrackedWrites);
|
|
339
|
+
} else {
|
|
340
|
+
prevUntrackedWrites.push(...untrackedWrites);
|
|
341
|
+
setUntrackedWrites(prevUntrackedWrites);
|
|
342
|
+
}
|
|
343
|
+
} else {
|
|
344
|
+
setUntrackedWrites(prevUntrackedWrites);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
function source(initialValue, options) {
|
|
349
|
+
return {
|
|
350
|
+
f: 0,
|
|
351
|
+
v: initialValue,
|
|
352
|
+
equals: options?.equals ?? equals,
|
|
353
|
+
reactions: null,
|
|
354
|
+
rv: 0,
|
|
355
|
+
wv: 0
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
function signal(initialValue, options) {
|
|
359
|
+
const src = source(initialValue, options);
|
|
360
|
+
return {
|
|
361
|
+
get value() {
|
|
362
|
+
return get(src);
|
|
363
|
+
},
|
|
364
|
+
set value(newValue) {
|
|
365
|
+
set(src, newValue);
|
|
366
|
+
}
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
var proxyFn = null;
|
|
370
|
+
function setProxyFn(fn) {
|
|
371
|
+
proxyFn = fn;
|
|
372
|
+
}
|
|
373
|
+
function shouldProxy(value) {
|
|
374
|
+
if (value === null || typeof value !== "object") {
|
|
375
|
+
return false;
|
|
376
|
+
}
|
|
377
|
+
const proto = Object.getPrototypeOf(value);
|
|
378
|
+
return proto === Object.prototype || proto === Array.prototype || proto === null;
|
|
379
|
+
}
|
|
380
|
+
function isProxy(value) {
|
|
381
|
+
return value !== null && typeof value === "object" && STATE_SYMBOL in value;
|
|
382
|
+
}
|
|
383
|
+
function isWritable(target, prop) {
|
|
384
|
+
const descriptor = Object.getOwnPropertyDescriptor(target, prop);
|
|
385
|
+
return descriptor === undefined || descriptor.writable === true;
|
|
386
|
+
}
|
|
387
|
+
function proxy(value) {
|
|
388
|
+
if (!shouldProxy(value) || isProxy(value)) {
|
|
389
|
+
return value;
|
|
390
|
+
}
|
|
391
|
+
const sources = new Map;
|
|
392
|
+
const version = source(0);
|
|
393
|
+
const isArray = Array.isArray(value);
|
|
394
|
+
if (isArray) {
|
|
395
|
+
sources.set("length", source(value.length));
|
|
396
|
+
}
|
|
397
|
+
const parentReadVersion = readVersion;
|
|
398
|
+
const withParent = (fn) => {
|
|
399
|
+
if (readVersion === parentReadVersion) {
|
|
400
|
+
return fn();
|
|
401
|
+
}
|
|
402
|
+
const prevReaction = activeReaction;
|
|
403
|
+
setActiveReaction(null);
|
|
404
|
+
try {
|
|
405
|
+
return fn();
|
|
406
|
+
} finally {
|
|
407
|
+
setActiveReaction(prevReaction);
|
|
408
|
+
}
|
|
409
|
+
};
|
|
410
|
+
const getSource = (prop, initialValue) => {
|
|
411
|
+
let s = sources.get(prop);
|
|
412
|
+
if (s === undefined) {
|
|
413
|
+
s = withParent(() => {
|
|
414
|
+
const proxied = shouldProxy(initialValue) ? proxy(initialValue) : initialValue;
|
|
415
|
+
return source(proxied);
|
|
416
|
+
});
|
|
417
|
+
sources.set(prop, s);
|
|
418
|
+
}
|
|
419
|
+
return s;
|
|
420
|
+
};
|
|
421
|
+
const proxyObj = new Proxy(value, {
|
|
422
|
+
get(target, prop, receiver) {
|
|
423
|
+
if (prop === STATE_SYMBOL) {
|
|
424
|
+
return value;
|
|
425
|
+
}
|
|
426
|
+
const exists = prop in target;
|
|
427
|
+
const currentValue = Reflect.get(target, prop, receiver);
|
|
428
|
+
if (isArray && typeof currentValue === "function") {
|
|
429
|
+
get(version);
|
|
430
|
+
return currentValue.bind(proxyObj);
|
|
431
|
+
}
|
|
432
|
+
if (exists || isWritable(target, prop)) {
|
|
433
|
+
const s = getSource(prop, currentValue);
|
|
434
|
+
const val = get(s);
|
|
435
|
+
if (val === UNINITIALIZED) {
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
return val;
|
|
439
|
+
}
|
|
440
|
+
return currentValue;
|
|
441
|
+
},
|
|
442
|
+
set(target, prop, newValue, receiver) {
|
|
443
|
+
const exists = prop in target;
|
|
444
|
+
let s = sources.get(prop);
|
|
445
|
+
if (s === undefined) {
|
|
446
|
+
if (!exists && !isWritable(target, prop)) {
|
|
447
|
+
return false;
|
|
448
|
+
}
|
|
449
|
+
s = withParent(() => source(undefined));
|
|
450
|
+
sources.set(prop, s);
|
|
451
|
+
}
|
|
452
|
+
const proxied = withParent(() => shouldProxy(newValue) ? proxy(newValue) : newValue);
|
|
453
|
+
set(s, proxied);
|
|
454
|
+
Reflect.set(target, prop, newValue, receiver);
|
|
455
|
+
if (isArray && prop === "length") {
|
|
456
|
+
const oldLength = s.v;
|
|
457
|
+
const newLength = newValue;
|
|
458
|
+
for (let i = newLength;i < oldLength; i++) {
|
|
459
|
+
const indexKey = String(i);
|
|
460
|
+
const indexSource = sources.get(indexKey);
|
|
461
|
+
if (indexSource !== undefined) {
|
|
462
|
+
set(indexSource, UNINITIALIZED);
|
|
463
|
+
} else if (i in target) {
|
|
464
|
+
const deletedSource = withParent(() => source(UNINITIALIZED));
|
|
465
|
+
sources.set(indexKey, deletedSource);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
if (isArray && typeof prop === "string") {
|
|
470
|
+
const index = Number(prop);
|
|
471
|
+
if (Number.isInteger(index) && index >= 0) {
|
|
472
|
+
const lengthSource = sources.get("length");
|
|
473
|
+
if (lengthSource !== undefined && index >= lengthSource.v) {
|
|
474
|
+
set(lengthSource, index + 1);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
if (!exists) {
|
|
479
|
+
set(version, get(version) + 1);
|
|
480
|
+
}
|
|
481
|
+
return true;
|
|
482
|
+
},
|
|
483
|
+
deleteProperty(target, prop) {
|
|
484
|
+
const exists = prop in target;
|
|
485
|
+
if (exists) {
|
|
486
|
+
let s = sources.get(prop);
|
|
487
|
+
if (s === undefined) {
|
|
488
|
+
s = withParent(() => source(UNINITIALIZED));
|
|
489
|
+
sources.set(prop, s);
|
|
490
|
+
} else {
|
|
491
|
+
set(s, UNINITIALIZED);
|
|
492
|
+
}
|
|
493
|
+
set(version, get(version) + 1);
|
|
494
|
+
}
|
|
495
|
+
return Reflect.deleteProperty(target, prop);
|
|
496
|
+
},
|
|
497
|
+
has(target, prop) {
|
|
498
|
+
if (prop === STATE_SYMBOL) {
|
|
499
|
+
return true;
|
|
500
|
+
}
|
|
501
|
+
get(version);
|
|
502
|
+
const s = sources.get(prop);
|
|
503
|
+
if (s !== undefined) {
|
|
504
|
+
const val = get(s);
|
|
505
|
+
if (val === UNINITIALIZED) {
|
|
506
|
+
return false;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
return Reflect.has(target, prop);
|
|
510
|
+
},
|
|
511
|
+
ownKeys(target) {
|
|
512
|
+
get(version);
|
|
513
|
+
const keys = Reflect.ownKeys(target).filter((key) => {
|
|
514
|
+
const s = sources.get(key);
|
|
515
|
+
return s === undefined || s.v !== UNINITIALIZED;
|
|
516
|
+
});
|
|
517
|
+
for (const [key, s] of sources) {
|
|
518
|
+
const k = key;
|
|
519
|
+
if (s.v !== UNINITIALIZED && !(key in target) && !keys.includes(k)) {
|
|
520
|
+
keys.push(k);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
return keys;
|
|
524
|
+
},
|
|
525
|
+
getOwnPropertyDescriptor(target, prop) {
|
|
526
|
+
const s = sources.get(prop);
|
|
527
|
+
if (s !== undefined && s.v === UNINITIALIZED) {
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
530
|
+
return Reflect.getOwnPropertyDescriptor(target, prop);
|
|
531
|
+
}
|
|
532
|
+
});
|
|
533
|
+
return proxyObj;
|
|
534
|
+
}
|
|
535
|
+
function createDerived(fn, options) {
|
|
536
|
+
let flags = DERIVED | DIRTY;
|
|
537
|
+
const parentDerived = activeReaction !== null && (activeReaction.f & DERIVED) !== 0 ? activeReaction : null;
|
|
538
|
+
if (activeEffect === null || parentDerived !== null && (parentDerived.f & UNOWNED) !== 0) {
|
|
539
|
+
flags |= UNOWNED;
|
|
540
|
+
}
|
|
541
|
+
const derived = {
|
|
542
|
+
f: flags,
|
|
543
|
+
fn,
|
|
544
|
+
v: UNINITIALIZED,
|
|
545
|
+
equals: options?.equals ?? equals,
|
|
546
|
+
reactions: null,
|
|
547
|
+
deps: null,
|
|
548
|
+
effects: null,
|
|
549
|
+
parent: parentDerived ?? activeEffect,
|
|
550
|
+
rv: 0,
|
|
551
|
+
wv: 0
|
|
552
|
+
};
|
|
553
|
+
return derived;
|
|
554
|
+
}
|
|
555
|
+
function executeDerived(derived) {
|
|
556
|
+
destroyDerivedEffects(derived);
|
|
557
|
+
const value = updateReaction(derived);
|
|
558
|
+
return value;
|
|
559
|
+
}
|
|
560
|
+
function updateDerived2(derived) {
|
|
561
|
+
const value = executeDerived(derived);
|
|
562
|
+
if (!derived.equals(derived.v, value)) {
|
|
563
|
+
derived.v = value;
|
|
564
|
+
derived.wv = incrementWriteVersion();
|
|
565
|
+
}
|
|
566
|
+
const status = (derived.f & UNOWNED) !== 0 && derived.deps !== null ? MAYBE_DIRTY : CLEAN;
|
|
567
|
+
setSignalStatus(derived, status);
|
|
568
|
+
}
|
|
569
|
+
setUpdateDerivedImpl(updateDerived2);
|
|
570
|
+
var destroyEffectImpl = () => {};
|
|
571
|
+
function setDestroyEffectImpl(impl) {
|
|
572
|
+
destroyEffectImpl = impl;
|
|
573
|
+
}
|
|
574
|
+
function destroyDerivedEffects(derived) {
|
|
575
|
+
const effects = derived.effects;
|
|
576
|
+
if (effects !== null) {
|
|
577
|
+
derived.effects = null;
|
|
578
|
+
for (let i = 0;i < effects.length; i++) {
|
|
579
|
+
destroyEffectImpl(effects[i]);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
function derived(fn, options) {
|
|
584
|
+
const d = createDerived(fn, options);
|
|
585
|
+
return {
|
|
586
|
+
get value() {
|
|
587
|
+
return get(d);
|
|
588
|
+
}
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
derived.by = derived;
|
|
592
|
+
function createEffect(type, fn, sync, push = true) {
|
|
593
|
+
const parent = activeEffect;
|
|
594
|
+
const effect = {
|
|
595
|
+
f: type | DIRTY,
|
|
596
|
+
fn,
|
|
597
|
+
deps: null,
|
|
598
|
+
teardown: null,
|
|
599
|
+
parent,
|
|
600
|
+
first: null,
|
|
601
|
+
last: null,
|
|
602
|
+
prev: null,
|
|
603
|
+
next: null,
|
|
604
|
+
wv: 0
|
|
605
|
+
};
|
|
606
|
+
if (sync) {
|
|
607
|
+
updateEffect(effect);
|
|
608
|
+
effect.f |= EFFECT_RAN;
|
|
609
|
+
} else if (fn !== null) {
|
|
610
|
+
scheduleEffect(effect);
|
|
611
|
+
}
|
|
612
|
+
if (push && parent !== null) {
|
|
613
|
+
pushEffect(effect, parent);
|
|
614
|
+
}
|
|
615
|
+
return effect;
|
|
616
|
+
}
|
|
617
|
+
function pushEffect(effect, parent) {
|
|
618
|
+
const parentLast = parent.last;
|
|
619
|
+
if (parentLast === null) {
|
|
620
|
+
parent.first = parent.last = effect;
|
|
621
|
+
} else {
|
|
622
|
+
parentLast.next = effect;
|
|
623
|
+
effect.prev = parentLast;
|
|
624
|
+
parent.last = effect;
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
function updateEffect(effect) {
|
|
628
|
+
if ((effect.f & DESTROYED) !== 0)
|
|
629
|
+
return;
|
|
630
|
+
setSignalStatus(effect, CLEAN);
|
|
631
|
+
const prevEffect = activeEffect;
|
|
632
|
+
setActiveEffect(effect);
|
|
633
|
+
try {
|
|
634
|
+
destroyEffectChildren(effect);
|
|
635
|
+
executeTeardown(effect);
|
|
636
|
+
const teardown = updateReaction(effect);
|
|
637
|
+
effect.teardown = typeof teardown === "function" ? teardown : null;
|
|
638
|
+
effect.wv = incrementWriteVersion();
|
|
639
|
+
} finally {
|
|
640
|
+
setActiveEffect(prevEffect);
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
setUpdateEffectImpl(updateEffect);
|
|
644
|
+
function destroyEffect(effect, removeFromParent = true) {
|
|
645
|
+
destroyEffectChildren(effect);
|
|
646
|
+
removeReactions(effect, 0);
|
|
647
|
+
setSignalStatus(effect, DESTROYED);
|
|
648
|
+
executeTeardown(effect);
|
|
649
|
+
if (removeFromParent && effect.parent !== null) {
|
|
650
|
+
unlinkEffect(effect);
|
|
651
|
+
}
|
|
652
|
+
effect.fn = null;
|
|
653
|
+
effect.teardown = null;
|
|
654
|
+
effect.deps = null;
|
|
655
|
+
effect.first = null;
|
|
656
|
+
effect.last = null;
|
|
657
|
+
effect.prev = null;
|
|
658
|
+
effect.next = null;
|
|
659
|
+
}
|
|
660
|
+
setDestroyEffectImpl(destroyEffect);
|
|
661
|
+
function destroyEffectChildren(effect) {
|
|
662
|
+
let child = effect.first;
|
|
663
|
+
effect.first = null;
|
|
664
|
+
effect.last = null;
|
|
665
|
+
while (child !== null) {
|
|
666
|
+
const next = child.next;
|
|
667
|
+
if ((child.f & (EFFECT_PRESERVED | ROOT_EFFECT)) === 0) {
|
|
668
|
+
destroyEffect(child, false);
|
|
669
|
+
}
|
|
670
|
+
child = next;
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
function executeTeardown(effect) {
|
|
674
|
+
const teardown = effect.teardown;
|
|
675
|
+
if (teardown !== null) {
|
|
676
|
+
effect.teardown = null;
|
|
677
|
+
teardown();
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
function unlinkEffect(effect) {
|
|
681
|
+
const { parent, prev, next } = effect;
|
|
682
|
+
if (prev !== null) {
|
|
683
|
+
prev.next = next;
|
|
684
|
+
}
|
|
685
|
+
if (next !== null) {
|
|
686
|
+
next.prev = prev;
|
|
687
|
+
}
|
|
688
|
+
if (parent !== null) {
|
|
689
|
+
if (parent.first === effect) {
|
|
690
|
+
parent.first = next;
|
|
691
|
+
}
|
|
692
|
+
if (parent.last === effect) {
|
|
693
|
+
parent.last = prev;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
effect.prev = null;
|
|
697
|
+
effect.next = null;
|
|
698
|
+
}
|
|
699
|
+
function effect(fn) {
|
|
700
|
+
const eff = createEffect(EFFECT | USER_EFFECT, fn, false);
|
|
701
|
+
return () => destroyEffect(eff);
|
|
702
|
+
}
|
|
703
|
+
effect.pre = function effectPre(fn) {
|
|
704
|
+
const eff = createEffect(RENDER_EFFECT | USER_EFFECT, fn, true);
|
|
705
|
+
return () => destroyEffect(eff);
|
|
706
|
+
};
|
|
707
|
+
effect.root = function effectRoot(fn) {
|
|
708
|
+
const eff = createEffect(ROOT_EFFECT | EFFECT_PRESERVED, fn, true);
|
|
709
|
+
return () => destroyEffect(eff);
|
|
710
|
+
};
|
|
711
|
+
effect.tracking = function effectTracking() {
|
|
712
|
+
return activeEffect !== null;
|
|
713
|
+
};
|
|
714
|
+
class ReactiveMap extends Map {
|
|
715
|
+
#keySignals = new Map;
|
|
716
|
+
#version = source(0);
|
|
717
|
+
#size;
|
|
718
|
+
constructor(entries) {
|
|
719
|
+
super(entries);
|
|
720
|
+
this.#size = source(super.size);
|
|
721
|
+
}
|
|
722
|
+
#getKeySignal(key) {
|
|
723
|
+
let sig = this.#keySignals.get(key);
|
|
724
|
+
if (sig === undefined) {
|
|
725
|
+
sig = source(0);
|
|
726
|
+
this.#keySignals.set(key, sig);
|
|
727
|
+
}
|
|
728
|
+
return sig;
|
|
729
|
+
}
|
|
730
|
+
#increment(sig) {
|
|
731
|
+
set(sig, sig.v + 1);
|
|
732
|
+
}
|
|
733
|
+
get size() {
|
|
734
|
+
get(this.#size);
|
|
735
|
+
return super.size;
|
|
736
|
+
}
|
|
737
|
+
has(key) {
|
|
738
|
+
const sig = this.#keySignals.get(key);
|
|
739
|
+
if (sig === undefined) {
|
|
740
|
+
if (!super.has(key)) {
|
|
741
|
+
get(this.#version);
|
|
742
|
+
return false;
|
|
743
|
+
}
|
|
744
|
+
const newSig = this.#getKeySignal(key);
|
|
745
|
+
get(newSig);
|
|
746
|
+
return true;
|
|
747
|
+
}
|
|
748
|
+
get(sig);
|
|
749
|
+
return super.has(key);
|
|
750
|
+
}
|
|
751
|
+
get(key) {
|
|
752
|
+
const sig = this.#keySignals.get(key);
|
|
753
|
+
if (sig === undefined) {
|
|
754
|
+
const val = super.get(key);
|
|
755
|
+
if (val !== undefined) {
|
|
756
|
+
const newSig = this.#getKeySignal(key);
|
|
757
|
+
get(newSig);
|
|
758
|
+
return val;
|
|
759
|
+
}
|
|
760
|
+
get(this.#version);
|
|
761
|
+
return;
|
|
762
|
+
}
|
|
763
|
+
get(sig);
|
|
764
|
+
return super.get(key);
|
|
765
|
+
}
|
|
766
|
+
set(key, value) {
|
|
767
|
+
const isNew = !super.has(key);
|
|
768
|
+
const oldValue = super.get(key);
|
|
769
|
+
super.set(key, value);
|
|
770
|
+
const sig = this.#getKeySignal(key);
|
|
771
|
+
if (isNew) {
|
|
772
|
+
set(this.#size, super.size);
|
|
773
|
+
this.#increment(this.#version);
|
|
774
|
+
this.#increment(sig);
|
|
775
|
+
} else if (!Object.is(oldValue, value)) {
|
|
776
|
+
this.#increment(sig);
|
|
777
|
+
const versionReactions = this.#version.reactions;
|
|
778
|
+
const keyReactions = sig.reactions;
|
|
779
|
+
if (keyReactions !== null && (versionReactions === null || !keyReactions.every((r) => versionReactions.includes(r)))) {
|
|
780
|
+
this.#increment(this.#version);
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
return this;
|
|
784
|
+
}
|
|
785
|
+
delete(key) {
|
|
786
|
+
const existed = super.has(key);
|
|
787
|
+
if (existed) {
|
|
788
|
+
super.delete(key);
|
|
789
|
+
const sig = this.#keySignals.get(key);
|
|
790
|
+
if (sig !== undefined) {
|
|
791
|
+
set(sig, -1);
|
|
792
|
+
this.#keySignals.delete(key);
|
|
793
|
+
}
|
|
794
|
+
set(this.#size, super.size);
|
|
795
|
+
this.#increment(this.#version);
|
|
796
|
+
}
|
|
797
|
+
return existed;
|
|
798
|
+
}
|
|
799
|
+
clear() {
|
|
800
|
+
if (super.size > 0) {
|
|
801
|
+
for (const [key, sig] of this.#keySignals) {
|
|
802
|
+
set(sig, -1);
|
|
803
|
+
}
|
|
804
|
+
this.#keySignals.clear();
|
|
805
|
+
super.clear();
|
|
806
|
+
set(this.#size, 0);
|
|
807
|
+
this.#increment(this.#version);
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
keys() {
|
|
811
|
+
get(this.#version);
|
|
812
|
+
return super.keys();
|
|
813
|
+
}
|
|
814
|
+
values() {
|
|
815
|
+
get(this.#version);
|
|
816
|
+
return super.values();
|
|
817
|
+
}
|
|
818
|
+
entries() {
|
|
819
|
+
get(this.#version);
|
|
820
|
+
return super.entries();
|
|
821
|
+
}
|
|
822
|
+
forEach(callbackfn, thisArg) {
|
|
823
|
+
get(this.#version);
|
|
824
|
+
super.forEach(callbackfn, thisArg);
|
|
825
|
+
}
|
|
826
|
+
[Symbol.iterator]() {
|
|
827
|
+
return this.entries();
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
class ReactiveSet extends Set {
|
|
832
|
+
#itemSignals = new Map;
|
|
833
|
+
#version = source(0);
|
|
834
|
+
#size;
|
|
835
|
+
constructor(values) {
|
|
836
|
+
super(values);
|
|
837
|
+
this.#size = source(super.size);
|
|
838
|
+
}
|
|
839
|
+
#getItemSignal(item) {
|
|
840
|
+
let sig = this.#itemSignals.get(item);
|
|
841
|
+
if (sig === undefined) {
|
|
842
|
+
sig = source(super.has(item));
|
|
843
|
+
this.#itemSignals.set(item, sig);
|
|
844
|
+
}
|
|
845
|
+
return sig;
|
|
846
|
+
}
|
|
847
|
+
#incrementVersion() {
|
|
848
|
+
set(this.#version, this.#version.v + 1);
|
|
849
|
+
}
|
|
850
|
+
get size() {
|
|
851
|
+
get(this.#size);
|
|
852
|
+
return super.size;
|
|
853
|
+
}
|
|
854
|
+
has(item) {
|
|
855
|
+
const sig = this.#itemSignals.get(item);
|
|
856
|
+
if (sig === undefined) {
|
|
857
|
+
const exists = super.has(item);
|
|
858
|
+
if (exists) {
|
|
859
|
+
const newSig = this.#getItemSignal(item);
|
|
860
|
+
get(newSig);
|
|
861
|
+
return true;
|
|
862
|
+
}
|
|
863
|
+
get(this.#version);
|
|
864
|
+
return false;
|
|
865
|
+
}
|
|
866
|
+
get(sig);
|
|
867
|
+
return super.has(item);
|
|
868
|
+
}
|
|
869
|
+
add(item) {
|
|
870
|
+
const isNew = !super.has(item);
|
|
871
|
+
super.add(item);
|
|
872
|
+
if (isNew) {
|
|
873
|
+
const sig = this.#getItemSignal(item);
|
|
874
|
+
set(sig, true);
|
|
875
|
+
set(this.#size, super.size);
|
|
876
|
+
this.#incrementVersion();
|
|
877
|
+
}
|
|
878
|
+
return this;
|
|
879
|
+
}
|
|
880
|
+
delete(item) {
|
|
881
|
+
const existed = super.has(item);
|
|
882
|
+
if (existed) {
|
|
883
|
+
super.delete(item);
|
|
884
|
+
const sig = this.#itemSignals.get(item);
|
|
885
|
+
if (sig !== undefined) {
|
|
886
|
+
set(sig, false);
|
|
887
|
+
this.#itemSignals.delete(item);
|
|
888
|
+
}
|
|
889
|
+
set(this.#size, super.size);
|
|
890
|
+
this.#incrementVersion();
|
|
891
|
+
}
|
|
892
|
+
return existed;
|
|
893
|
+
}
|
|
894
|
+
clear() {
|
|
895
|
+
if (super.size > 0) {
|
|
896
|
+
for (const [item, sig] of this.#itemSignals) {
|
|
897
|
+
set(sig, false);
|
|
898
|
+
}
|
|
899
|
+
this.#itemSignals.clear();
|
|
900
|
+
super.clear();
|
|
901
|
+
set(this.#size, 0);
|
|
902
|
+
this.#incrementVersion();
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
keys() {
|
|
906
|
+
get(this.#version);
|
|
907
|
+
return super.keys();
|
|
908
|
+
}
|
|
909
|
+
values() {
|
|
910
|
+
get(this.#version);
|
|
911
|
+
return super.values();
|
|
912
|
+
}
|
|
913
|
+
entries() {
|
|
914
|
+
get(this.#version);
|
|
915
|
+
return super.entries();
|
|
916
|
+
}
|
|
917
|
+
forEach(callbackfn, thisArg) {
|
|
918
|
+
get(this.#version);
|
|
919
|
+
super.forEach(callbackfn, thisArg);
|
|
920
|
+
}
|
|
921
|
+
[Symbol.iterator]() {
|
|
922
|
+
return this.values();
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
class ReactiveDate extends Date {
|
|
927
|
+
#time;
|
|
928
|
+
constructor(...args) {
|
|
929
|
+
super(...args);
|
|
930
|
+
this.#time = source(super.getTime());
|
|
931
|
+
}
|
|
932
|
+
#update() {
|
|
933
|
+
const time = super.getTime();
|
|
934
|
+
set(this.#time, time);
|
|
935
|
+
return time;
|
|
936
|
+
}
|
|
937
|
+
getTime() {
|
|
938
|
+
get(this.#time);
|
|
939
|
+
return super.getTime();
|
|
940
|
+
}
|
|
941
|
+
getFullYear() {
|
|
942
|
+
get(this.#time);
|
|
943
|
+
return super.getFullYear();
|
|
944
|
+
}
|
|
945
|
+
getMonth() {
|
|
946
|
+
get(this.#time);
|
|
947
|
+
return super.getMonth();
|
|
948
|
+
}
|
|
949
|
+
getDate() {
|
|
950
|
+
get(this.#time);
|
|
951
|
+
return super.getDate();
|
|
952
|
+
}
|
|
953
|
+
getDay() {
|
|
954
|
+
get(this.#time);
|
|
955
|
+
return super.getDay();
|
|
956
|
+
}
|
|
957
|
+
getHours() {
|
|
958
|
+
get(this.#time);
|
|
959
|
+
return super.getHours();
|
|
960
|
+
}
|
|
961
|
+
getMinutes() {
|
|
962
|
+
get(this.#time);
|
|
963
|
+
return super.getMinutes();
|
|
964
|
+
}
|
|
965
|
+
getSeconds() {
|
|
966
|
+
get(this.#time);
|
|
967
|
+
return super.getSeconds();
|
|
968
|
+
}
|
|
969
|
+
getMilliseconds() {
|
|
970
|
+
get(this.#time);
|
|
971
|
+
return super.getMilliseconds();
|
|
972
|
+
}
|
|
973
|
+
getUTCFullYear() {
|
|
974
|
+
get(this.#time);
|
|
975
|
+
return super.getUTCFullYear();
|
|
976
|
+
}
|
|
977
|
+
getUTCMonth() {
|
|
978
|
+
get(this.#time);
|
|
979
|
+
return super.getUTCMonth();
|
|
980
|
+
}
|
|
981
|
+
getUTCDate() {
|
|
982
|
+
get(this.#time);
|
|
983
|
+
return super.getUTCDate();
|
|
984
|
+
}
|
|
985
|
+
getUTCDay() {
|
|
986
|
+
get(this.#time);
|
|
987
|
+
return super.getUTCDay();
|
|
988
|
+
}
|
|
989
|
+
getUTCHours() {
|
|
990
|
+
get(this.#time);
|
|
991
|
+
return super.getUTCHours();
|
|
992
|
+
}
|
|
993
|
+
getUTCMinutes() {
|
|
994
|
+
get(this.#time);
|
|
995
|
+
return super.getUTCMinutes();
|
|
996
|
+
}
|
|
997
|
+
getUTCSeconds() {
|
|
998
|
+
get(this.#time);
|
|
999
|
+
return super.getUTCSeconds();
|
|
1000
|
+
}
|
|
1001
|
+
getUTCMilliseconds() {
|
|
1002
|
+
get(this.#time);
|
|
1003
|
+
return super.getUTCMilliseconds();
|
|
1004
|
+
}
|
|
1005
|
+
getTimezoneOffset() {
|
|
1006
|
+
get(this.#time);
|
|
1007
|
+
return super.getTimezoneOffset();
|
|
1008
|
+
}
|
|
1009
|
+
setTime(time) {
|
|
1010
|
+
super.setTime(time);
|
|
1011
|
+
return this.#update();
|
|
1012
|
+
}
|
|
1013
|
+
setFullYear(year, month, date) {
|
|
1014
|
+
if (date !== undefined) {
|
|
1015
|
+
super.setFullYear(year, month, date);
|
|
1016
|
+
} else if (month !== undefined) {
|
|
1017
|
+
super.setFullYear(year, month);
|
|
1018
|
+
} else {
|
|
1019
|
+
super.setFullYear(year);
|
|
1020
|
+
}
|
|
1021
|
+
return this.#update();
|
|
1022
|
+
}
|
|
1023
|
+
setMonth(month, date) {
|
|
1024
|
+
if (date !== undefined) {
|
|
1025
|
+
super.setMonth(month, date);
|
|
1026
|
+
} else {
|
|
1027
|
+
super.setMonth(month);
|
|
1028
|
+
}
|
|
1029
|
+
return this.#update();
|
|
1030
|
+
}
|
|
1031
|
+
setDate(date) {
|
|
1032
|
+
super.setDate(date);
|
|
1033
|
+
return this.#update();
|
|
1034
|
+
}
|
|
1035
|
+
setHours(hours, min, sec, ms) {
|
|
1036
|
+
if (ms !== undefined) {
|
|
1037
|
+
super.setHours(hours, min, sec, ms);
|
|
1038
|
+
} else if (sec !== undefined) {
|
|
1039
|
+
super.setHours(hours, min, sec);
|
|
1040
|
+
} else if (min !== undefined) {
|
|
1041
|
+
super.setHours(hours, min);
|
|
1042
|
+
} else {
|
|
1043
|
+
super.setHours(hours);
|
|
1044
|
+
}
|
|
1045
|
+
return this.#update();
|
|
1046
|
+
}
|
|
1047
|
+
setMinutes(min, sec, ms) {
|
|
1048
|
+
if (ms !== undefined) {
|
|
1049
|
+
super.setMinutes(min, sec, ms);
|
|
1050
|
+
} else if (sec !== undefined) {
|
|
1051
|
+
super.setMinutes(min, sec);
|
|
1052
|
+
} else {
|
|
1053
|
+
super.setMinutes(min);
|
|
1054
|
+
}
|
|
1055
|
+
return this.#update();
|
|
1056
|
+
}
|
|
1057
|
+
setSeconds(sec, ms) {
|
|
1058
|
+
if (ms !== undefined) {
|
|
1059
|
+
super.setSeconds(sec, ms);
|
|
1060
|
+
} else {
|
|
1061
|
+
super.setSeconds(sec);
|
|
1062
|
+
}
|
|
1063
|
+
return this.#update();
|
|
1064
|
+
}
|
|
1065
|
+
setMilliseconds(ms) {
|
|
1066
|
+
super.setMilliseconds(ms);
|
|
1067
|
+
return this.#update();
|
|
1068
|
+
}
|
|
1069
|
+
setUTCFullYear(year, month, date) {
|
|
1070
|
+
if (date !== undefined) {
|
|
1071
|
+
super.setUTCFullYear(year, month, date);
|
|
1072
|
+
} else if (month !== undefined) {
|
|
1073
|
+
super.setUTCFullYear(year, month);
|
|
1074
|
+
} else {
|
|
1075
|
+
super.setUTCFullYear(year);
|
|
1076
|
+
}
|
|
1077
|
+
return this.#update();
|
|
1078
|
+
}
|
|
1079
|
+
setUTCMonth(month, date) {
|
|
1080
|
+
if (date !== undefined) {
|
|
1081
|
+
super.setUTCMonth(month, date);
|
|
1082
|
+
} else {
|
|
1083
|
+
super.setUTCMonth(month);
|
|
1084
|
+
}
|
|
1085
|
+
return this.#update();
|
|
1086
|
+
}
|
|
1087
|
+
setUTCDate(date) {
|
|
1088
|
+
super.setUTCDate(date);
|
|
1089
|
+
return this.#update();
|
|
1090
|
+
}
|
|
1091
|
+
setUTCHours(hours, min, sec, ms) {
|
|
1092
|
+
if (ms !== undefined) {
|
|
1093
|
+
super.setUTCHours(hours, min, sec, ms);
|
|
1094
|
+
} else if (sec !== undefined) {
|
|
1095
|
+
super.setUTCHours(hours, min, sec);
|
|
1096
|
+
} else if (min !== undefined) {
|
|
1097
|
+
super.setUTCHours(hours, min);
|
|
1098
|
+
} else {
|
|
1099
|
+
super.setUTCHours(hours);
|
|
1100
|
+
}
|
|
1101
|
+
return this.#update();
|
|
1102
|
+
}
|
|
1103
|
+
setUTCMinutes(min, sec, ms) {
|
|
1104
|
+
if (ms !== undefined) {
|
|
1105
|
+
super.setUTCMinutes(min, sec, ms);
|
|
1106
|
+
} else if (sec !== undefined) {
|
|
1107
|
+
super.setUTCMinutes(min, sec);
|
|
1108
|
+
} else {
|
|
1109
|
+
super.setUTCMinutes(min);
|
|
1110
|
+
}
|
|
1111
|
+
return this.#update();
|
|
1112
|
+
}
|
|
1113
|
+
setUTCSeconds(sec, ms) {
|
|
1114
|
+
if (ms !== undefined) {
|
|
1115
|
+
super.setUTCSeconds(sec, ms);
|
|
1116
|
+
} else {
|
|
1117
|
+
super.setUTCSeconds(sec);
|
|
1118
|
+
}
|
|
1119
|
+
return this.#update();
|
|
1120
|
+
}
|
|
1121
|
+
setUTCMilliseconds(ms) {
|
|
1122
|
+
super.setUTCMilliseconds(ms);
|
|
1123
|
+
return this.#update();
|
|
1124
|
+
}
|
|
1125
|
+
toString() {
|
|
1126
|
+
get(this.#time);
|
|
1127
|
+
return super.toString();
|
|
1128
|
+
}
|
|
1129
|
+
toDateString() {
|
|
1130
|
+
get(this.#time);
|
|
1131
|
+
return super.toDateString();
|
|
1132
|
+
}
|
|
1133
|
+
toTimeString() {
|
|
1134
|
+
get(this.#time);
|
|
1135
|
+
return super.toTimeString();
|
|
1136
|
+
}
|
|
1137
|
+
toISOString() {
|
|
1138
|
+
get(this.#time);
|
|
1139
|
+
return super.toISOString();
|
|
1140
|
+
}
|
|
1141
|
+
toUTCString() {
|
|
1142
|
+
get(this.#time);
|
|
1143
|
+
return super.toUTCString();
|
|
1144
|
+
}
|
|
1145
|
+
toLocaleString(locales, options) {
|
|
1146
|
+
get(this.#time);
|
|
1147
|
+
return super.toLocaleString(locales, options);
|
|
1148
|
+
}
|
|
1149
|
+
toLocaleDateString(locales, options) {
|
|
1150
|
+
get(this.#time);
|
|
1151
|
+
return super.toLocaleDateString(locales, options);
|
|
1152
|
+
}
|
|
1153
|
+
toLocaleTimeString(locales, options) {
|
|
1154
|
+
get(this.#time);
|
|
1155
|
+
return super.toLocaleTimeString(locales, options);
|
|
1156
|
+
}
|
|
1157
|
+
toJSON() {
|
|
1158
|
+
get(this.#time);
|
|
1159
|
+
return super.toJSON();
|
|
1160
|
+
}
|
|
1161
|
+
valueOf() {
|
|
1162
|
+
get(this.#time);
|
|
1163
|
+
return super.valueOf();
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
setProxyFn(proxy);
|
|
1167
|
+
|
|
1168
|
+
// src/core/registry.ts
|
|
1169
|
+
function createRegistry() {
|
|
1170
|
+
const idToIndex = new ReactiveMap;
|
|
1171
|
+
const indexToId = new ReactiveMap;
|
|
1172
|
+
const allocatedIndices = new ReactiveSet;
|
|
1173
|
+
const freeIndices = [];
|
|
1174
|
+
const _nextIndex = signal(0);
|
|
1175
|
+
const registry = {
|
|
1176
|
+
idToIndex,
|
|
1177
|
+
indexToId,
|
|
1178
|
+
allocatedIndices,
|
|
1179
|
+
freeIndices,
|
|
1180
|
+
get nextIndex() {
|
|
1181
|
+
return _nextIndex.value;
|
|
1182
|
+
},
|
|
1183
|
+
set nextIndex(value) {
|
|
1184
|
+
_nextIndex.value = value;
|
|
1185
|
+
},
|
|
1186
|
+
allocate(id) {
|
|
1187
|
+
const existingIndex = idToIndex.get(id);
|
|
1188
|
+
if (existingIndex !== undefined) {
|
|
1189
|
+
return existingIndex;
|
|
1190
|
+
}
|
|
1191
|
+
let index;
|
|
1192
|
+
if (freeIndices.length > 0) {
|
|
1193
|
+
index = freeIndices.pop();
|
|
1194
|
+
} else {
|
|
1195
|
+
index = _nextIndex.value;
|
|
1196
|
+
_nextIndex.value++;
|
|
1197
|
+
}
|
|
1198
|
+
idToIndex.set(id, index);
|
|
1199
|
+
indexToId.set(index, id);
|
|
1200
|
+
allocatedIndices.add(index);
|
|
1201
|
+
return index;
|
|
1202
|
+
},
|
|
1203
|
+
release(id) {
|
|
1204
|
+
const index = idToIndex.get(id);
|
|
1205
|
+
if (index === undefined) {
|
|
1206
|
+
return false;
|
|
1207
|
+
}
|
|
1208
|
+
idToIndex.delete(id);
|
|
1209
|
+
indexToId.delete(index);
|
|
1210
|
+
allocatedIndices.delete(index);
|
|
1211
|
+
freeIndices.push(index);
|
|
1212
|
+
return true;
|
|
1213
|
+
},
|
|
1214
|
+
getIndex(id) {
|
|
1215
|
+
return idToIndex.get(id) ?? -1;
|
|
1216
|
+
},
|
|
1217
|
+
getId(index) {
|
|
1218
|
+
return indexToId.get(index);
|
|
1219
|
+
},
|
|
1220
|
+
has(id) {
|
|
1221
|
+
return idToIndex.has(id);
|
|
1222
|
+
},
|
|
1223
|
+
getAllIds() {
|
|
1224
|
+
return Array.from(idToIndex.keys());
|
|
1225
|
+
},
|
|
1226
|
+
getAllIndices() {
|
|
1227
|
+
return Array.from(allocatedIndices);
|
|
1228
|
+
},
|
|
1229
|
+
get count() {
|
|
1230
|
+
return idToIndex.size;
|
|
1231
|
+
},
|
|
1232
|
+
reset() {
|
|
1233
|
+
idToIndex.clear();
|
|
1234
|
+
indexToId.clear();
|
|
1235
|
+
allocatedIndices.clear();
|
|
1236
|
+
freeIndices.length = 0;
|
|
1237
|
+
_nextIndex.value = 0;
|
|
1238
|
+
}
|
|
1239
|
+
};
|
|
1240
|
+
return registry;
|
|
1241
|
+
}
|
|
1242
|
+
function generateId() {
|
|
1243
|
+
const timestamp = Date.now();
|
|
1244
|
+
const random = Math.random().toString(36).substring(2, 8);
|
|
1245
|
+
return `${timestamp}-${random}`;
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
// src/core/constants.ts
|
|
1249
|
+
var DEFAULT_VALUES = {
|
|
1250
|
+
string: "",
|
|
1251
|
+
number: 0,
|
|
1252
|
+
boolean: false,
|
|
1253
|
+
timestamp: 0,
|
|
1254
|
+
"string[]": [],
|
|
1255
|
+
"number[]": [],
|
|
1256
|
+
vector: null
|
|
1257
|
+
};
|
|
1258
|
+
var WATCHER_DEBOUNCE_MS = 100;
|
|
1259
|
+
var SAVE_GRACE_PERIOD_MS = 200;
|
|
1260
|
+
var DEFAULT_PAGE_SIZE = 20;
|
|
1261
|
+
|
|
1262
|
+
// src/core/columns.ts
|
|
1263
|
+
function parseColumnType(type) {
|
|
1264
|
+
if (type === "string")
|
|
1265
|
+
return { baseType: "string" };
|
|
1266
|
+
if (type === "number")
|
|
1267
|
+
return { baseType: "number" };
|
|
1268
|
+
if (type === "boolean")
|
|
1269
|
+
return { baseType: "boolean" };
|
|
1270
|
+
if (type === "timestamp")
|
|
1271
|
+
return { baseType: "timestamp" };
|
|
1272
|
+
if (type === "string[]")
|
|
1273
|
+
return { baseType: "array", arrayType: "string" };
|
|
1274
|
+
if (type === "number[]")
|
|
1275
|
+
return { baseType: "array", arrayType: "number" };
|
|
1276
|
+
const vectorMatch = type.match(/^vector:(\d+)$/);
|
|
1277
|
+
if (vectorMatch) {
|
|
1278
|
+
return {
|
|
1279
|
+
baseType: "vector",
|
|
1280
|
+
vectorDimensions: parseInt(vectorMatch[1], 10)
|
|
1281
|
+
};
|
|
1282
|
+
}
|
|
1283
|
+
throw new Error(`Unknown column type: ${type}`);
|
|
1284
|
+
}
|
|
1285
|
+
function getDefaultValue(type) {
|
|
1286
|
+
const parsed = parseColumnType(type);
|
|
1287
|
+
switch (parsed.baseType) {
|
|
1288
|
+
case "string":
|
|
1289
|
+
return DEFAULT_VALUES.string;
|
|
1290
|
+
case "number":
|
|
1291
|
+
case "timestamp":
|
|
1292
|
+
return DEFAULT_VALUES.number;
|
|
1293
|
+
case "boolean":
|
|
1294
|
+
return DEFAULT_VALUES.boolean;
|
|
1295
|
+
case "array":
|
|
1296
|
+
return parsed.arrayType === "string" ? [...DEFAULT_VALUES["string[]"]] : [...DEFAULT_VALUES["number[]"]];
|
|
1297
|
+
case "vector":
|
|
1298
|
+
return DEFAULT_VALUES.vector;
|
|
1299
|
+
default:
|
|
1300
|
+
return null;
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
function parseSchema(definition) {
|
|
1304
|
+
const columns = Object.keys(definition);
|
|
1305
|
+
const vectorColumns = [];
|
|
1306
|
+
const parsedTypes = new Map;
|
|
1307
|
+
for (const col of columns) {
|
|
1308
|
+
const parsed = parseColumnType(definition[col]);
|
|
1309
|
+
parsedTypes.set(col, parsed);
|
|
1310
|
+
if (parsed.baseType === "vector") {
|
|
1311
|
+
vectorColumns.push(col);
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
return {
|
|
1315
|
+
definition,
|
|
1316
|
+
columns,
|
|
1317
|
+
vectorColumns,
|
|
1318
|
+
parsedTypes
|
|
1319
|
+
};
|
|
1320
|
+
}
|
|
1321
|
+
function createColumns(definition) {
|
|
1322
|
+
const schema = parseSchema(definition);
|
|
1323
|
+
const columns = new Map;
|
|
1324
|
+
for (const col of schema.columns) {
|
|
1325
|
+
columns.set(col, signal([]));
|
|
1326
|
+
}
|
|
1327
|
+
const manager = {
|
|
1328
|
+
schema,
|
|
1329
|
+
getColumn(name) {
|
|
1330
|
+
const col = columns.get(name);
|
|
1331
|
+
if (!col) {
|
|
1332
|
+
throw new Error(`Unknown column: ${String(name)}`);
|
|
1333
|
+
}
|
|
1334
|
+
return col;
|
|
1335
|
+
},
|
|
1336
|
+
get(column, index) {
|
|
1337
|
+
const col = columns.get(column);
|
|
1338
|
+
if (!col) {
|
|
1339
|
+
throw new Error(`Unknown column: ${String(column)}`);
|
|
1340
|
+
}
|
|
1341
|
+
return col.value[index];
|
|
1342
|
+
},
|
|
1343
|
+
set(column, index, value) {
|
|
1344
|
+
const col = columns.get(column);
|
|
1345
|
+
if (!col) {
|
|
1346
|
+
throw new Error(`Unknown column: ${String(column)}`);
|
|
1347
|
+
}
|
|
1348
|
+
const parsed = schema.parsedTypes.get(column);
|
|
1349
|
+
if (parsed?.baseType === "vector" && Array.isArray(value)) {
|
|
1350
|
+
value = new Float32Array(value);
|
|
1351
|
+
}
|
|
1352
|
+
const arr = col.value;
|
|
1353
|
+
while (arr.length <= index) {
|
|
1354
|
+
arr.push(getDefaultValue(definition[column]));
|
|
1355
|
+
}
|
|
1356
|
+
arr[index] = value;
|
|
1357
|
+
col.value = arr;
|
|
1358
|
+
},
|
|
1359
|
+
getRecord(index) {
|
|
1360
|
+
const record = {};
|
|
1361
|
+
for (const col of schema.columns) {
|
|
1362
|
+
record[col] = manager.get(col, index);
|
|
1363
|
+
}
|
|
1364
|
+
return record;
|
|
1365
|
+
},
|
|
1366
|
+
setRecord(index, record) {
|
|
1367
|
+
for (const col of schema.columns) {
|
|
1368
|
+
if (col in record) {
|
|
1369
|
+
manager.set(col, index, record[col]);
|
|
1370
|
+
} else {
|
|
1371
|
+
const defaultValue = getDefaultValue(definition[col]);
|
|
1372
|
+
manager.set(col, index, defaultValue);
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
},
|
|
1376
|
+
clearAt(index) {
|
|
1377
|
+
for (const col of schema.columns) {
|
|
1378
|
+
const defaultValue = getDefaultValue(definition[col]);
|
|
1379
|
+
manager.set(col, index, defaultValue);
|
|
1380
|
+
}
|
|
1381
|
+
},
|
|
1382
|
+
reset() {
|
|
1383
|
+
for (const col of schema.columns) {
|
|
1384
|
+
const colSignal = columns.get(col);
|
|
1385
|
+
if (colSignal) {
|
|
1386
|
+
colSignal.value = [];
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
};
|
|
1391
|
+
return manager;
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1394
|
+
// src/core/collection.ts
|
|
1395
|
+
function createMetadataArrays() {
|
|
1396
|
+
return {
|
|
1397
|
+
created: signal([]),
|
|
1398
|
+
updated: signal([]),
|
|
1399
|
+
stale: signal([])
|
|
1400
|
+
};
|
|
1401
|
+
}
|
|
1402
|
+
function createCollection(name, options) {
|
|
1403
|
+
const { schema, contentColumn } = options;
|
|
1404
|
+
const registry = createRegistry();
|
|
1405
|
+
const columns = createColumns(schema);
|
|
1406
|
+
const metadata = createMetadataArrays();
|
|
1407
|
+
function buildRecord(index) {
|
|
1408
|
+
const id = registry.getId(index);
|
|
1409
|
+
if (!id)
|
|
1410
|
+
throw new Error(`No ID for index ${index}`);
|
|
1411
|
+
const data = columns.getRecord(index);
|
|
1412
|
+
return {
|
|
1413
|
+
...data,
|
|
1414
|
+
id,
|
|
1415
|
+
created: metadata.created.value[index] ?? 0,
|
|
1416
|
+
updated: metadata.updated.value[index] ?? 0,
|
|
1417
|
+
stale: metadata.stale.value[index] ?? false
|
|
1418
|
+
};
|
|
1419
|
+
}
|
|
1420
|
+
function setMetadataAt(index, created, updated, stale) {
|
|
1421
|
+
const createdArr = metadata.created.value;
|
|
1422
|
+
const updatedArr = metadata.updated.value;
|
|
1423
|
+
const staleArr = metadata.stale.value;
|
|
1424
|
+
while (createdArr.length <= index)
|
|
1425
|
+
createdArr.push(0);
|
|
1426
|
+
while (updatedArr.length <= index)
|
|
1427
|
+
updatedArr.push(0);
|
|
1428
|
+
while (staleArr.length <= index)
|
|
1429
|
+
staleArr.push(false);
|
|
1430
|
+
createdArr[index] = created;
|
|
1431
|
+
updatedArr[index] = updated;
|
|
1432
|
+
staleArr[index] = stale;
|
|
1433
|
+
metadata.created.value = createdArr;
|
|
1434
|
+
metadata.updated.value = updatedArr;
|
|
1435
|
+
metadata.stale.value = staleArr;
|
|
1436
|
+
}
|
|
1437
|
+
const collection = {
|
|
1438
|
+
name,
|
|
1439
|
+
schema,
|
|
1440
|
+
contentColumn,
|
|
1441
|
+
registry,
|
|
1442
|
+
columns,
|
|
1443
|
+
insert(data) {
|
|
1444
|
+
const id = data.id ?? generateId();
|
|
1445
|
+
const index = registry.allocate(id);
|
|
1446
|
+
const now = Date.now();
|
|
1447
|
+
columns.setRecord(index, data);
|
|
1448
|
+
setMetadataAt(index, now, now, false);
|
|
1449
|
+
return id;
|
|
1450
|
+
},
|
|
1451
|
+
insertMany(records) {
|
|
1452
|
+
return records.map((record) => collection.insert(record));
|
|
1453
|
+
},
|
|
1454
|
+
get(id) {
|
|
1455
|
+
const index = registry.getIndex(id);
|
|
1456
|
+
if (index === -1)
|
|
1457
|
+
return;
|
|
1458
|
+
return buildRecord(index);
|
|
1459
|
+
},
|
|
1460
|
+
all() {
|
|
1461
|
+
const indices = registry.getAllIndices();
|
|
1462
|
+
return indices.map((index) => buildRecord(index));
|
|
1463
|
+
},
|
|
1464
|
+
find(filter) {
|
|
1465
|
+
const results = [];
|
|
1466
|
+
const indices = registry.getAllIndices();
|
|
1467
|
+
for (const index of indices) {
|
|
1468
|
+
const data = columns.getRecord(index);
|
|
1469
|
+
if (filter(data, index)) {
|
|
1470
|
+
results.push(buildRecord(index));
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
return results;
|
|
1474
|
+
},
|
|
1475
|
+
findOne(filter) {
|
|
1476
|
+
const indices = registry.getAllIndices();
|
|
1477
|
+
for (const index of indices) {
|
|
1478
|
+
const data = columns.getRecord(index);
|
|
1479
|
+
if (filter(data, index)) {
|
|
1480
|
+
return buildRecord(index);
|
|
1481
|
+
}
|
|
1482
|
+
}
|
|
1483
|
+
return;
|
|
1484
|
+
},
|
|
1485
|
+
update(id, data) {
|
|
1486
|
+
const index = registry.getIndex(id);
|
|
1487
|
+
if (index === -1)
|
|
1488
|
+
return false;
|
|
1489
|
+
for (const key of Object.keys(data)) {
|
|
1490
|
+
columns.set(key, index, data[key]);
|
|
1491
|
+
}
|
|
1492
|
+
const updatedArr = metadata.updated.value;
|
|
1493
|
+
updatedArr[index] = Date.now();
|
|
1494
|
+
metadata.updated.value = updatedArr;
|
|
1495
|
+
return true;
|
|
1496
|
+
},
|
|
1497
|
+
updateField(id, field, value) {
|
|
1498
|
+
const index = registry.getIndex(id);
|
|
1499
|
+
if (index === -1)
|
|
1500
|
+
return false;
|
|
1501
|
+
columns.set(field, index, value);
|
|
1502
|
+
const updatedArr = metadata.updated.value;
|
|
1503
|
+
updatedArr[index] = Date.now();
|
|
1504
|
+
metadata.updated.value = updatedArr;
|
|
1505
|
+
return true;
|
|
1506
|
+
},
|
|
1507
|
+
updateMany(filter, data) {
|
|
1508
|
+
let count = 0;
|
|
1509
|
+
const indices = registry.getAllIndices();
|
|
1510
|
+
const now = Date.now();
|
|
1511
|
+
for (const index of indices) {
|
|
1512
|
+
const record = columns.getRecord(index);
|
|
1513
|
+
if (filter(record, index)) {
|
|
1514
|
+
for (const key of Object.keys(data)) {
|
|
1515
|
+
columns.set(key, index, data[key]);
|
|
1516
|
+
}
|
|
1517
|
+
const updatedArr = metadata.updated.value;
|
|
1518
|
+
updatedArr[index] = now;
|
|
1519
|
+
metadata.updated.value = updatedArr;
|
|
1520
|
+
count++;
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1523
|
+
return count;
|
|
1524
|
+
},
|
|
1525
|
+
delete(id) {
|
|
1526
|
+
const index = registry.getIndex(id);
|
|
1527
|
+
if (index === -1)
|
|
1528
|
+
return false;
|
|
1529
|
+
columns.clearAt(index);
|
|
1530
|
+
const createdArr = metadata.created.value;
|
|
1531
|
+
const updatedArr = metadata.updated.value;
|
|
1532
|
+
const staleArr = metadata.stale.value;
|
|
1533
|
+
if (index < createdArr.length)
|
|
1534
|
+
createdArr[index] = 0;
|
|
1535
|
+
if (index < updatedArr.length)
|
|
1536
|
+
updatedArr[index] = 0;
|
|
1537
|
+
if (index < staleArr.length)
|
|
1538
|
+
staleArr[index] = false;
|
|
1539
|
+
metadata.created.value = createdArr;
|
|
1540
|
+
metadata.updated.value = updatedArr;
|
|
1541
|
+
metadata.stale.value = staleArr;
|
|
1542
|
+
registry.release(id);
|
|
1543
|
+
return true;
|
|
1544
|
+
},
|
|
1545
|
+
deleteMany(filter) {
|
|
1546
|
+
const toDelete = [];
|
|
1547
|
+
const indices = registry.getAllIndices();
|
|
1548
|
+
for (const index of indices) {
|
|
1549
|
+
const data = columns.getRecord(index);
|
|
1550
|
+
if (filter(data, index)) {
|
|
1551
|
+
const id = registry.getId(index);
|
|
1552
|
+
if (id)
|
|
1553
|
+
toDelete.push(id);
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
for (const id of toDelete) {
|
|
1557
|
+
collection.delete(id);
|
|
1558
|
+
}
|
|
1559
|
+
return toDelete.length;
|
|
1560
|
+
},
|
|
1561
|
+
count(filter) {
|
|
1562
|
+
if (!filter) {
|
|
1563
|
+
return registry.count;
|
|
1564
|
+
}
|
|
1565
|
+
let count = 0;
|
|
1566
|
+
const indices = registry.getAllIndices();
|
|
1567
|
+
for (const index of indices) {
|
|
1568
|
+
const data = columns.getRecord(index);
|
|
1569
|
+
if (filter(data, index))
|
|
1570
|
+
count++;
|
|
1571
|
+
}
|
|
1572
|
+
return count;
|
|
1573
|
+
},
|
|
1574
|
+
reactiveCount: derived(() => registry.count),
|
|
1575
|
+
getByIndex(index) {
|
|
1576
|
+
if (!registry.allocatedIndices.has(index))
|
|
1577
|
+
return;
|
|
1578
|
+
return buildRecord(index);
|
|
1579
|
+
},
|
|
1580
|
+
getIndices() {
|
|
1581
|
+
return registry.getAllIndices();
|
|
1582
|
+
},
|
|
1583
|
+
has(id) {
|
|
1584
|
+
return registry.has(id);
|
|
1585
|
+
},
|
|
1586
|
+
isStale(id) {
|
|
1587
|
+
const index = registry.getIndex(id);
|
|
1588
|
+
if (index === -1)
|
|
1589
|
+
return false;
|
|
1590
|
+
return metadata.stale.value[index] ?? false;
|
|
1591
|
+
},
|
|
1592
|
+
getStaleIds() {
|
|
1593
|
+
const ids = [];
|
|
1594
|
+
const staleArr = metadata.stale.value;
|
|
1595
|
+
for (const index of registry.getAllIndices()) {
|
|
1596
|
+
if (staleArr[index]) {
|
|
1597
|
+
const id = registry.getId(index);
|
|
1598
|
+
if (id)
|
|
1599
|
+
ids.push(id);
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
return ids;
|
|
1603
|
+
},
|
|
1604
|
+
setStale(id, stale) {
|
|
1605
|
+
const index = registry.getIndex(id);
|
|
1606
|
+
if (index === -1)
|
|
1607
|
+
return;
|
|
1608
|
+
const staleArr = metadata.stale.value;
|
|
1609
|
+
while (staleArr.length <= index)
|
|
1610
|
+
staleArr.push(false);
|
|
1611
|
+
staleArr[index] = stale;
|
|
1612
|
+
metadata.stale.value = staleArr;
|
|
1613
|
+
},
|
|
1614
|
+
clear() {
|
|
1615
|
+
registry.reset();
|
|
1616
|
+
columns.reset();
|
|
1617
|
+
metadata.created.value = [];
|
|
1618
|
+
metadata.updated.value = [];
|
|
1619
|
+
metadata.stale.value = [];
|
|
1620
|
+
}
|
|
1621
|
+
};
|
|
1622
|
+
return collection;
|
|
1623
|
+
}
|
|
1624
|
+
|
|
1625
|
+
// src/persistence/markdown.ts
|
|
1626
|
+
function idToFilename(id) {
|
|
1627
|
+
return id.replace(/[<>:"/\\|?*\x00-\x1f]/g, "_") + ".md";
|
|
1628
|
+
}
|
|
1629
|
+
function filenameToId(filename) {
|
|
1630
|
+
return filename.replace(/\.md$/, "");
|
|
1631
|
+
}
|
|
1632
|
+
function parseMarkdown(text) {
|
|
1633
|
+
const frontmatter = {};
|
|
1634
|
+
let content = "";
|
|
1635
|
+
if (!text.startsWith("---")) {
|
|
1636
|
+
return { frontmatter, content: text.trim() };
|
|
1637
|
+
}
|
|
1638
|
+
const endIndex = text.indexOf(`
|
|
1639
|
+
---`, 3);
|
|
1640
|
+
if (endIndex === -1) {
|
|
1641
|
+
return { frontmatter, content: text.trim() };
|
|
1642
|
+
}
|
|
1643
|
+
const yamlText = text.slice(4, endIndex);
|
|
1644
|
+
const lines = yamlText.split(`
|
|
1645
|
+
`);
|
|
1646
|
+
for (const line of lines) {
|
|
1647
|
+
const trimmed = line.trim();
|
|
1648
|
+
if (!trimmed || trimmed.startsWith("#"))
|
|
1649
|
+
continue;
|
|
1650
|
+
const colonIndex = trimmed.indexOf(":");
|
|
1651
|
+
if (colonIndex === -1)
|
|
1652
|
+
continue;
|
|
1653
|
+
const key = trimmed.slice(0, colonIndex).trim();
|
|
1654
|
+
let value = trimmed.slice(colonIndex + 1).trim();
|
|
1655
|
+
frontmatter[key] = parseYamlValue(value);
|
|
1656
|
+
}
|
|
1657
|
+
content = text.slice(endIndex + 4).trim();
|
|
1658
|
+
return { frontmatter, content };
|
|
1659
|
+
}
|
|
1660
|
+
function parseYamlValue(value) {
|
|
1661
|
+
if (value === "null" || value === "~" || value === "") {
|
|
1662
|
+
return null;
|
|
1663
|
+
}
|
|
1664
|
+
if (value === "true")
|
|
1665
|
+
return true;
|
|
1666
|
+
if (value === "false")
|
|
1667
|
+
return false;
|
|
1668
|
+
if (/^-?\d+$/.test(value)) {
|
|
1669
|
+
return parseInt(value, 10);
|
|
1670
|
+
}
|
|
1671
|
+
if (/^-?\d+\.\d+$/.test(value)) {
|
|
1672
|
+
return parseFloat(value);
|
|
1673
|
+
}
|
|
1674
|
+
if (value.startsWith("[") && value.endsWith("]")) {
|
|
1675
|
+
try {
|
|
1676
|
+
return JSON.parse(value);
|
|
1677
|
+
} catch {
|
|
1678
|
+
return value;
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1681
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
1682
|
+
return value.slice(1, -1);
|
|
1683
|
+
}
|
|
1684
|
+
return value;
|
|
1685
|
+
}
|
|
1686
|
+
function toYamlValue(value) {
|
|
1687
|
+
if (value === null || value === undefined) {
|
|
1688
|
+
return "null";
|
|
1689
|
+
}
|
|
1690
|
+
if (typeof value === "boolean") {
|
|
1691
|
+
return value ? "true" : "false";
|
|
1692
|
+
}
|
|
1693
|
+
if (typeof value === "number") {
|
|
1694
|
+
return String(value);
|
|
1695
|
+
}
|
|
1696
|
+
if (Array.isArray(value)) {
|
|
1697
|
+
return JSON.stringify(value);
|
|
1698
|
+
}
|
|
1699
|
+
if (value instanceof Float32Array) {
|
|
1700
|
+
return JSON.stringify(Array.from(value));
|
|
1701
|
+
}
|
|
1702
|
+
if (typeof value === "string") {
|
|
1703
|
+
if (value.includes(":") || value.includes("#") || value.includes(`
|
|
1704
|
+
`) || value.startsWith('"') || value.startsWith("'") || value === "true" || value === "false" || value === "null") {
|
|
1705
|
+
return JSON.stringify(value);
|
|
1706
|
+
}
|
|
1707
|
+
return value;
|
|
1708
|
+
}
|
|
1709
|
+
return JSON.stringify(value);
|
|
1710
|
+
}
|
|
1711
|
+
function generateMarkdown(record, schema, contentColumn) {
|
|
1712
|
+
const lines = ["---"];
|
|
1713
|
+
lines.push(`id: ${toYamlValue(record.id)}`);
|
|
1714
|
+
lines.push(`created: ${record.created}`);
|
|
1715
|
+
lines.push(`updated: ${record.updated}`);
|
|
1716
|
+
for (const key of Object.keys(schema)) {
|
|
1717
|
+
if (key === contentColumn)
|
|
1718
|
+
continue;
|
|
1719
|
+
const value = record[key];
|
|
1720
|
+
lines.push(`${String(key)}: ${toYamlValue(value)}`);
|
|
1721
|
+
}
|
|
1722
|
+
lines.push("---");
|
|
1723
|
+
lines.push("");
|
|
1724
|
+
if (contentColumn) {
|
|
1725
|
+
const content = record[contentColumn];
|
|
1726
|
+
if (typeof content === "string") {
|
|
1727
|
+
lines.push(content);
|
|
1728
|
+
}
|
|
1729
|
+
}
|
|
1730
|
+
return lines.join(`
|
|
1731
|
+
`);
|
|
1732
|
+
}
|
|
1733
|
+
async function loadFromMarkdown(filepath, schema, contentColumn) {
|
|
1734
|
+
try {
|
|
1735
|
+
const file = Bun.file(filepath);
|
|
1736
|
+
if (!await file.exists()) {
|
|
1737
|
+
return null;
|
|
1738
|
+
}
|
|
1739
|
+
const text = await file.text();
|
|
1740
|
+
const { frontmatter, content } = parseMarkdown(text);
|
|
1741
|
+
const id = frontmatter.id;
|
|
1742
|
+
if (!id) {
|
|
1743
|
+
const filename = filepath.split("/").pop() || "";
|
|
1744
|
+
return null;
|
|
1745
|
+
}
|
|
1746
|
+
const record = {
|
|
1747
|
+
id,
|
|
1748
|
+
created: frontmatter.created || Date.now(),
|
|
1749
|
+
updated: frontmatter.updated || Date.now(),
|
|
1750
|
+
stale: false
|
|
1751
|
+
};
|
|
1752
|
+
for (const key of Object.keys(schema)) {
|
|
1753
|
+
if (key === contentColumn) {
|
|
1754
|
+
record[key] = content;
|
|
1755
|
+
} else if (key in frontmatter) {
|
|
1756
|
+
let value = frontmatter[key];
|
|
1757
|
+
const parsed = parseColumnType(schema[key]);
|
|
1758
|
+
if (parsed.baseType === "vector" && Array.isArray(value)) {
|
|
1759
|
+
value = new Float32Array(value);
|
|
1760
|
+
}
|
|
1761
|
+
record[key] = value;
|
|
1762
|
+
}
|
|
1763
|
+
}
|
|
1764
|
+
return { id, record };
|
|
1765
|
+
} catch {
|
|
1766
|
+
return null;
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
async function saveToMarkdown(filepath, record, schema, contentColumn) {
|
|
1770
|
+
try {
|
|
1771
|
+
const markdown = generateMarkdown(record, schema, contentColumn);
|
|
1772
|
+
await Bun.write(filepath, markdown);
|
|
1773
|
+
return true;
|
|
1774
|
+
} catch {
|
|
1775
|
+
return false;
|
|
1776
|
+
}
|
|
1777
|
+
}
|
|
1778
|
+
async function loadFromDirectory(dirpath, schema, contentColumn) {
|
|
1779
|
+
const results = [];
|
|
1780
|
+
try {
|
|
1781
|
+
const glob = new Bun.Glob("*.md");
|
|
1782
|
+
const files = glob.scanSync({ cwd: dirpath });
|
|
1783
|
+
for (const filename of files) {
|
|
1784
|
+
const filepath = `${dirpath}/${filename}`;
|
|
1785
|
+
const result = await loadFromMarkdown(filepath, schema, contentColumn);
|
|
1786
|
+
if (result) {
|
|
1787
|
+
results.push(result);
|
|
1788
|
+
}
|
|
1789
|
+
}
|
|
1790
|
+
} catch {}
|
|
1791
|
+
return results;
|
|
1792
|
+
}
|
|
1793
|
+
async function deleteMarkdownFile(filepath) {
|
|
1794
|
+
try {
|
|
1795
|
+
const fs = await import("fs/promises");
|
|
1796
|
+
await fs.unlink(filepath);
|
|
1797
|
+
return true;
|
|
1798
|
+
} catch {
|
|
1799
|
+
return false;
|
|
1800
|
+
}
|
|
1801
|
+
}
|
|
1802
|
+
async function ensureDirectory(dirpath) {
|
|
1803
|
+
const fs = await import("fs/promises");
|
|
1804
|
+
await fs.mkdir(dirpath, { recursive: true });
|
|
1805
|
+
}
|
|
1806
|
+
|
|
1807
|
+
// src/persistence/watcher.ts
|
|
1808
|
+
import { watch } from "fs";
|
|
1809
|
+
function createFileWatcher(options) {
|
|
1810
|
+
const {
|
|
1811
|
+
dirpath,
|
|
1812
|
+
schema,
|
|
1813
|
+
contentColumn,
|
|
1814
|
+
debounceMs = WATCHER_DEBOUNCE_MS
|
|
1815
|
+
} = options;
|
|
1816
|
+
const _isWatching = signal(false);
|
|
1817
|
+
const _callbacks = new Set;
|
|
1818
|
+
const _savingIds = new Set;
|
|
1819
|
+
const _knownFiles = new Set;
|
|
1820
|
+
const _pendingChanges = new Map;
|
|
1821
|
+
let _watcher = null;
|
|
1822
|
+
async function processChange(filename) {
|
|
1823
|
+
if (!filename.endsWith(".md"))
|
|
1824
|
+
return;
|
|
1825
|
+
const id = filenameToId(filename);
|
|
1826
|
+
const filepath = `${dirpath}/${filename}`;
|
|
1827
|
+
if (_savingIds.has(id)) {
|
|
1828
|
+
return;
|
|
1829
|
+
}
|
|
1830
|
+
const file = Bun.file(filepath);
|
|
1831
|
+
const exists = await file.exists();
|
|
1832
|
+
let event;
|
|
1833
|
+
if (!exists) {
|
|
1834
|
+
if (!_knownFiles.has(filename))
|
|
1835
|
+
return;
|
|
1836
|
+
_knownFiles.delete(filename);
|
|
1837
|
+
event = {
|
|
1838
|
+
type: "delete",
|
|
1839
|
+
id,
|
|
1840
|
+
filename,
|
|
1841
|
+
filepath,
|
|
1842
|
+
stale: false
|
|
1843
|
+
};
|
|
1844
|
+
} else {
|
|
1845
|
+
const isNew = !_knownFiles.has(filename);
|
|
1846
|
+
_knownFiles.add(filename);
|
|
1847
|
+
const result = await loadFromMarkdown(filepath, schema, contentColumn);
|
|
1848
|
+
if (!result)
|
|
1849
|
+
return;
|
|
1850
|
+
let stale = false;
|
|
1851
|
+
if (options.isStaleCallback && contentColumn) {
|
|
1852
|
+
const content = result.record[contentColumn];
|
|
1853
|
+
if (content) {
|
|
1854
|
+
stale = options.isStaleCallback(id, content);
|
|
1855
|
+
}
|
|
1856
|
+
}
|
|
1857
|
+
event = {
|
|
1858
|
+
type: isNew ? "create" : "update",
|
|
1859
|
+
id,
|
|
1860
|
+
filename,
|
|
1861
|
+
filepath,
|
|
1862
|
+
record: result.record,
|
|
1863
|
+
stale
|
|
1864
|
+
};
|
|
1865
|
+
}
|
|
1866
|
+
for (const callback of _callbacks) {
|
|
1867
|
+
try {
|
|
1868
|
+
await callback(event);
|
|
1869
|
+
} catch (err) {
|
|
1870
|
+
console.error("File watcher callback error:", err);
|
|
1871
|
+
}
|
|
1872
|
+
}
|
|
1873
|
+
}
|
|
1874
|
+
function handleChange(filename) {
|
|
1875
|
+
const existing = _pendingChanges.get(filename);
|
|
1876
|
+
if (existing) {
|
|
1877
|
+
clearTimeout(existing);
|
|
1878
|
+
}
|
|
1879
|
+
const timeout = setTimeout(() => {
|
|
1880
|
+
_pendingChanges.delete(filename);
|
|
1881
|
+
processChange(filename);
|
|
1882
|
+
}, debounceMs);
|
|
1883
|
+
_pendingChanges.set(filename, timeout);
|
|
1884
|
+
}
|
|
1885
|
+
const watcher = {
|
|
1886
|
+
start() {
|
|
1887
|
+
if (_isWatching.value)
|
|
1888
|
+
return;
|
|
1889
|
+
try {
|
|
1890
|
+
const glob = new Bun.Glob("*.md");
|
|
1891
|
+
for (const filename of glob.scanSync({ cwd: dirpath })) {
|
|
1892
|
+
_knownFiles.add(filename);
|
|
1893
|
+
}
|
|
1894
|
+
_watcher = watch(dirpath, { recursive: false }, (eventType, filename) => {
|
|
1895
|
+
if (filename && filename.endsWith(".md")) {
|
|
1896
|
+
handleChange(filename);
|
|
1897
|
+
}
|
|
1898
|
+
});
|
|
1899
|
+
_isWatching.value = true;
|
|
1900
|
+
} catch (err) {
|
|
1901
|
+
console.error("Failed to start file watcher:", err);
|
|
1902
|
+
}
|
|
1903
|
+
},
|
|
1904
|
+
stop() {
|
|
1905
|
+
if (!_isWatching.value)
|
|
1906
|
+
return;
|
|
1907
|
+
for (const timeout of _pendingChanges.values()) {
|
|
1908
|
+
clearTimeout(timeout);
|
|
1909
|
+
}
|
|
1910
|
+
_pendingChanges.clear();
|
|
1911
|
+
if (_watcher) {
|
|
1912
|
+
_watcher.close();
|
|
1913
|
+
_watcher = null;
|
|
1914
|
+
}
|
|
1915
|
+
_isWatching.value = false;
|
|
1916
|
+
},
|
|
1917
|
+
get isWatching() {
|
|
1918
|
+
return _isWatching.value;
|
|
1919
|
+
},
|
|
1920
|
+
onChange(callback) {
|
|
1921
|
+
_callbacks.add(callback);
|
|
1922
|
+
return () => _callbacks.delete(callback);
|
|
1923
|
+
},
|
|
1924
|
+
markSaving(id) {
|
|
1925
|
+
_savingIds.add(id);
|
|
1926
|
+
},
|
|
1927
|
+
clearSaving(id) {
|
|
1928
|
+
setTimeout(() => {
|
|
1929
|
+
_savingIds.delete(id);
|
|
1930
|
+
}, SAVE_GRACE_PERIOD_MS);
|
|
1931
|
+
}
|
|
1932
|
+
};
|
|
1933
|
+
return watcher;
|
|
1934
|
+
}
|
|
1935
|
+
|
|
1936
|
+
// src/vector/search.ts
|
|
1937
|
+
function toFloat32Array(arr) {
|
|
1938
|
+
if (arr instanceof Float32Array)
|
|
1939
|
+
return arr;
|
|
1940
|
+
return new Float32Array(arr);
|
|
1941
|
+
}
|
|
1942
|
+
function normalizeVector(vec) {
|
|
1943
|
+
let sum = 0;
|
|
1944
|
+
for (let i = 0;i < vec.length; i++) {
|
|
1945
|
+
sum += vec[i] * vec[i];
|
|
1946
|
+
}
|
|
1947
|
+
const norm = Math.sqrt(sum);
|
|
1948
|
+
if (norm > 0) {
|
|
1949
|
+
for (let i = 0;i < vec.length; i++) {
|
|
1950
|
+
vec[i] /= norm;
|
|
1951
|
+
}
|
|
1952
|
+
}
|
|
1953
|
+
return vec;
|
|
1954
|
+
}
|
|
1955
|
+
function cosineSimilarity(a, b) {
|
|
1956
|
+
const vecA = a instanceof Float32Array ? a : new Float32Array(a);
|
|
1957
|
+
const vecB = b instanceof Float32Array ? b : new Float32Array(b);
|
|
1958
|
+
if (vecA.length !== vecB.length) {
|
|
1959
|
+
throw new Error(`Vector dimension mismatch: ${vecA.length} vs ${vecB.length}`);
|
|
1960
|
+
}
|
|
1961
|
+
let dot = 0;
|
|
1962
|
+
let normA = 0;
|
|
1963
|
+
let normB = 0;
|
|
1964
|
+
for (let i = 0;i < vecA.length; i++) {
|
|
1965
|
+
dot += vecA[i] * vecB[i];
|
|
1966
|
+
normA += vecA[i] * vecA[i];
|
|
1967
|
+
normB += vecB[i] * vecB[i];
|
|
1968
|
+
}
|
|
1969
|
+
const denom = Math.sqrt(normA) * Math.sqrt(normB);
|
|
1970
|
+
if (denom === 0)
|
|
1971
|
+
return 0;
|
|
1972
|
+
return dot / denom;
|
|
1973
|
+
}
|
|
1974
|
+
function batchCosineSimilarity(query, vectors, indices, topK) {
|
|
1975
|
+
let queryNorm = 0;
|
|
1976
|
+
for (let i = 0;i < query.length; i++) {
|
|
1977
|
+
queryNorm += query[i] * query[i];
|
|
1978
|
+
}
|
|
1979
|
+
queryNorm = Math.sqrt(queryNorm);
|
|
1980
|
+
if (queryNorm === 0)
|
|
1981
|
+
return [];
|
|
1982
|
+
const results = [];
|
|
1983
|
+
for (let i = 0;i < vectors.length; i++) {
|
|
1984
|
+
const vec = vectors[i];
|
|
1985
|
+
if (!vec)
|
|
1986
|
+
continue;
|
|
1987
|
+
let dot = 0;
|
|
1988
|
+
let vecNorm = 0;
|
|
1989
|
+
for (let j = 0;j < query.length; j++) {
|
|
1990
|
+
dot += query[j] * vec[j];
|
|
1991
|
+
vecNorm += vec[j] * vec[j];
|
|
1992
|
+
}
|
|
1993
|
+
vecNorm = Math.sqrt(vecNorm);
|
|
1994
|
+
if (vecNorm === 0)
|
|
1995
|
+
continue;
|
|
1996
|
+
const similarity = dot / (queryNorm * vecNorm);
|
|
1997
|
+
results.push({ index: indices[i], similarity });
|
|
1998
|
+
}
|
|
1999
|
+
results.sort((a, b) => b.similarity - a.similarity);
|
|
2000
|
+
return results.slice(0, topK);
|
|
2001
|
+
}
|
|
2002
|
+
function createEmbeddingManager() {
|
|
2003
|
+
const hashes = new ReactiveMap;
|
|
2004
|
+
return {
|
|
2005
|
+
setEmbedding(id, column, content) {
|
|
2006
|
+
const hash = BigInt(Bun.hash(content));
|
|
2007
|
+
let columnHashes = hashes.get(id);
|
|
2008
|
+
if (!columnHashes) {
|
|
2009
|
+
columnHashes = new Map;
|
|
2010
|
+
hashes.set(id, columnHashes);
|
|
2011
|
+
}
|
|
2012
|
+
columnHashes.set(column, hash);
|
|
2013
|
+
},
|
|
2014
|
+
isStale(id, column, currentContent) {
|
|
2015
|
+
const columnHashes = hashes.get(id);
|
|
2016
|
+
if (!columnHashes)
|
|
2017
|
+
return false;
|
|
2018
|
+
const storedHash = columnHashes.get(column);
|
|
2019
|
+
if (storedHash === undefined)
|
|
2020
|
+
return false;
|
|
2021
|
+
const currentHash = BigInt(Bun.hash(currentContent));
|
|
2022
|
+
return storedHash !== currentHash;
|
|
2023
|
+
},
|
|
2024
|
+
getHash(id, column) {
|
|
2025
|
+
return hashes.get(id)?.get(column);
|
|
2026
|
+
},
|
|
2027
|
+
clearHash(id, column) {
|
|
2028
|
+
const columnHashes = hashes.get(id);
|
|
2029
|
+
if (columnHashes) {
|
|
2030
|
+
columnHashes.delete(column);
|
|
2031
|
+
if (columnHashes.size === 0) {
|
|
2032
|
+
hashes.delete(id);
|
|
2033
|
+
}
|
|
2034
|
+
}
|
|
2035
|
+
},
|
|
2036
|
+
reset() {
|
|
2037
|
+
hashes.clear();
|
|
2038
|
+
}
|
|
2039
|
+
};
|
|
2040
|
+
}
|
|
2041
|
+
function vectorSearch(collection, vectorColumn, queryVector, options = {}) {
|
|
2042
|
+
const { topK = 10, minSimilarity = 0, filter } = options;
|
|
2043
|
+
const query = toFloat32Array(queryVector);
|
|
2044
|
+
const vectors = [];
|
|
2045
|
+
const vectorIndices = [];
|
|
2046
|
+
for (const index of collection.getIndices()) {
|
|
2047
|
+
if (filter) {
|
|
2048
|
+
const data = collection.columns.getRecord(index);
|
|
2049
|
+
if (!filter(data, index))
|
|
2050
|
+
continue;
|
|
2051
|
+
}
|
|
2052
|
+
const vec = collection.columns.get(vectorColumn, index);
|
|
2053
|
+
if (vec) {
|
|
2054
|
+
vectors.push(vec);
|
|
2055
|
+
vectorIndices.push(index);
|
|
2056
|
+
}
|
|
2057
|
+
}
|
|
2058
|
+
const topResults = batchCosineSimilarity(query, vectors, vectorIndices, topK);
|
|
2059
|
+
const results = [];
|
|
2060
|
+
for (const { index, similarity } of topResults) {
|
|
2061
|
+
if (similarity < minSimilarity)
|
|
2062
|
+
continue;
|
|
2063
|
+
const record = collection.getByIndex(index);
|
|
2064
|
+
if (!record)
|
|
2065
|
+
continue;
|
|
2066
|
+
results.push({
|
|
2067
|
+
record,
|
|
2068
|
+
similarity,
|
|
2069
|
+
stale: collection.isStale(record.id)
|
|
2070
|
+
});
|
|
2071
|
+
}
|
|
2072
|
+
return results;
|
|
2073
|
+
}
|
|
2074
|
+
|
|
2075
|
+
// src/core/database.ts
|
|
2076
|
+
function createPersistentCollection(name, options) {
|
|
2077
|
+
const baseCollection = createCollection(name, options);
|
|
2078
|
+
const { path, schema, contentColumn, autoSave = false, watchFiles = false, onExternalChange } = options;
|
|
2079
|
+
const embeddingManager = createEmbeddingManager();
|
|
2080
|
+
let fileWatcher = null;
|
|
2081
|
+
const savingIds = new Set;
|
|
2082
|
+
function getFilepath(id) {
|
|
2083
|
+
return `${path}/${idToFilename(id)}`;
|
|
2084
|
+
}
|
|
2085
|
+
async function saveRecord(id) {
|
|
2086
|
+
const record = baseCollection.get(id);
|
|
2087
|
+
if (!record)
|
|
2088
|
+
return false;
|
|
2089
|
+
savingIds.add(id);
|
|
2090
|
+
if (fileWatcher)
|
|
2091
|
+
fileWatcher.markSaving(id);
|
|
2092
|
+
const success = await saveToMarkdown(getFilepath(id), record, schema, contentColumn);
|
|
2093
|
+
setTimeout(() => {
|
|
2094
|
+
savingIds.delete(id);
|
|
2095
|
+
if (fileWatcher)
|
|
2096
|
+
fileWatcher.clearSaving(id);
|
|
2097
|
+
}, 200);
|
|
2098
|
+
return success;
|
|
2099
|
+
}
|
|
2100
|
+
async function deleteFile(id) {
|
|
2101
|
+
return await deleteMarkdownFile(getFilepath(id));
|
|
2102
|
+
}
|
|
2103
|
+
function isContentStale(id, content) {
|
|
2104
|
+
for (const col of baseCollection.columns.schema.vectorColumns) {
|
|
2105
|
+
if (embeddingManager.isStale(id, String(col), content)) {
|
|
2106
|
+
return true;
|
|
2107
|
+
}
|
|
2108
|
+
}
|
|
2109
|
+
return false;
|
|
2110
|
+
}
|
|
2111
|
+
const persistentCollection = {
|
|
2112
|
+
name: baseCollection.name,
|
|
2113
|
+
schema: baseCollection.schema,
|
|
2114
|
+
contentColumn: baseCollection.contentColumn,
|
|
2115
|
+
registry: baseCollection.registry,
|
|
2116
|
+
columns: baseCollection.columns,
|
|
2117
|
+
reactiveCount: baseCollection.reactiveCount,
|
|
2118
|
+
insert(data) {
|
|
2119
|
+
const id = baseCollection.insert(data);
|
|
2120
|
+
if (autoSave)
|
|
2121
|
+
saveRecord(id);
|
|
2122
|
+
return id;
|
|
2123
|
+
},
|
|
2124
|
+
insertMany(records) {
|
|
2125
|
+
const ids = baseCollection.insertMany(records);
|
|
2126
|
+
if (autoSave) {
|
|
2127
|
+
for (const id of ids)
|
|
2128
|
+
saveRecord(id);
|
|
2129
|
+
}
|
|
2130
|
+
return ids;
|
|
2131
|
+
},
|
|
2132
|
+
get: baseCollection.get.bind(baseCollection),
|
|
2133
|
+
all: baseCollection.all.bind(baseCollection),
|
|
2134
|
+
find: baseCollection.find.bind(baseCollection),
|
|
2135
|
+
findOne: baseCollection.findOne.bind(baseCollection),
|
|
2136
|
+
update(id, data) {
|
|
2137
|
+
const success = baseCollection.update(id, data);
|
|
2138
|
+
if (success && autoSave)
|
|
2139
|
+
saveRecord(id);
|
|
2140
|
+
return success;
|
|
2141
|
+
},
|
|
2142
|
+
updateField(id, field, value) {
|
|
2143
|
+
const success = baseCollection.updateField(id, field, value);
|
|
2144
|
+
if (success && autoSave)
|
|
2145
|
+
saveRecord(id);
|
|
2146
|
+
return success;
|
|
2147
|
+
},
|
|
2148
|
+
updateMany(filter, data) {
|
|
2149
|
+
const count = baseCollection.updateMany(filter, data);
|
|
2150
|
+
if (count > 0 && autoSave) {
|
|
2151
|
+
const updated = baseCollection.find(filter);
|
|
2152
|
+
for (const record of updated)
|
|
2153
|
+
saveRecord(record.id);
|
|
2154
|
+
}
|
|
2155
|
+
return count;
|
|
2156
|
+
},
|
|
2157
|
+
delete(id) {
|
|
2158
|
+
const success = baseCollection.delete(id);
|
|
2159
|
+
if (success && autoSave)
|
|
2160
|
+
deleteFile(id);
|
|
2161
|
+
embeddingManager.clearHash(id, "*");
|
|
2162
|
+
return success;
|
|
2163
|
+
},
|
|
2164
|
+
deleteMany(filter) {
|
|
2165
|
+
const toDelete = baseCollection.find(filter).map((r) => r.id);
|
|
2166
|
+
const count = baseCollection.deleteMany(filter);
|
|
2167
|
+
if (autoSave) {
|
|
2168
|
+
for (const id of toDelete)
|
|
2169
|
+
deleteFile(id);
|
|
2170
|
+
}
|
|
2171
|
+
for (const id of toDelete) {
|
|
2172
|
+
embeddingManager.clearHash(id, "*");
|
|
2173
|
+
}
|
|
2174
|
+
return count;
|
|
2175
|
+
},
|
|
2176
|
+
count: baseCollection.count.bind(baseCollection),
|
|
2177
|
+
getByIndex: baseCollection.getByIndex.bind(baseCollection),
|
|
2178
|
+
getIndices: baseCollection.getIndices.bind(baseCollection),
|
|
2179
|
+
has: baseCollection.has.bind(baseCollection),
|
|
2180
|
+
isStale: baseCollection.isStale.bind(baseCollection),
|
|
2181
|
+
getStaleIds: baseCollection.getStaleIds.bind(baseCollection),
|
|
2182
|
+
setStale: baseCollection.setStale.bind(baseCollection),
|
|
2183
|
+
clear() {
|
|
2184
|
+
baseCollection.clear();
|
|
2185
|
+
embeddingManager.reset();
|
|
2186
|
+
},
|
|
2187
|
+
async load() {
|
|
2188
|
+
await ensureDirectory(path);
|
|
2189
|
+
const loaded = await loadFromDirectory(path, schema, contentColumn);
|
|
2190
|
+
for (const { id, record } of loaded) {
|
|
2191
|
+
const index = baseCollection.registry.allocate(id);
|
|
2192
|
+
baseCollection.columns.setRecord(index, record);
|
|
2193
|
+
if ("created" in record) {}
|
|
2194
|
+
if (contentColumn) {
|
|
2195
|
+
const content = record[contentColumn];
|
|
2196
|
+
if (content && isContentStale(id, content)) {
|
|
2197
|
+
baseCollection.setStale(id, true);
|
|
2198
|
+
}
|
|
2199
|
+
}
|
|
2200
|
+
}
|
|
2201
|
+
return loaded.length;
|
|
2202
|
+
},
|
|
2203
|
+
async save() {
|
|
2204
|
+
await ensureDirectory(path);
|
|
2205
|
+
const records = baseCollection.all();
|
|
2206
|
+
for (const record of records) {
|
|
2207
|
+
await saveRecord(record.id);
|
|
2208
|
+
}
|
|
2209
|
+
return records.length;
|
|
2210
|
+
},
|
|
2211
|
+
startWatching() {
|
|
2212
|
+
if (fileWatcher)
|
|
2213
|
+
return;
|
|
2214
|
+
fileWatcher = createFileWatcher({
|
|
2215
|
+
dirpath: path,
|
|
2216
|
+
schema,
|
|
2217
|
+
contentColumn,
|
|
2218
|
+
isStaleCallback: contentColumn ? (id, content) => isContentStale(id, content) : undefined
|
|
2219
|
+
});
|
|
2220
|
+
fileWatcher.onChange(async (event) => {
|
|
2221
|
+
if (savingIds.has(event.id))
|
|
2222
|
+
return;
|
|
2223
|
+
if (event.type === "delete") {
|
|
2224
|
+
baseCollection.delete(event.id);
|
|
2225
|
+
} else if (event.record) {
|
|
2226
|
+
const exists = baseCollection.has(event.id);
|
|
2227
|
+
if (exists) {
|
|
2228
|
+
baseCollection.update(event.id, event.record);
|
|
2229
|
+
} else {
|
|
2230
|
+
baseCollection.insert({ ...event.record, id: event.id });
|
|
2231
|
+
}
|
|
2232
|
+
if (event.stale) {
|
|
2233
|
+
baseCollection.setStale(event.id, true);
|
|
2234
|
+
}
|
|
2235
|
+
}
|
|
2236
|
+
if (onExternalChange) {
|
|
2237
|
+
await onExternalChange(event);
|
|
2238
|
+
}
|
|
2239
|
+
});
|
|
2240
|
+
fileWatcher.start();
|
|
2241
|
+
},
|
|
2242
|
+
stopWatching() {
|
|
2243
|
+
if (fileWatcher) {
|
|
2244
|
+
fileWatcher.stop();
|
|
2245
|
+
fileWatcher = null;
|
|
2246
|
+
}
|
|
2247
|
+
},
|
|
2248
|
+
get isWatching() {
|
|
2249
|
+
return fileWatcher?.isWatching ?? false;
|
|
2250
|
+
},
|
|
2251
|
+
onFileChange(callback) {
|
|
2252
|
+
if (!fileWatcher) {
|
|
2253
|
+
persistentCollection.startWatching();
|
|
2254
|
+
}
|
|
2255
|
+
return fileWatcher.onChange(callback);
|
|
2256
|
+
},
|
|
2257
|
+
setEmbedding(id, column, embedding, sourceContent) {
|
|
2258
|
+
const vec = embedding instanceof Float32Array ? embedding : new Float32Array(embedding);
|
|
2259
|
+
baseCollection.updateField(id, column, vec);
|
|
2260
|
+
embeddingManager.setEmbedding(id, String(column), sourceContent);
|
|
2261
|
+
baseCollection.setStale(id, false);
|
|
2262
|
+
if (autoSave)
|
|
2263
|
+
saveRecord(id);
|
|
2264
|
+
},
|
|
2265
|
+
search(column, queryVector, options2) {
|
|
2266
|
+
return vectorSearch(baseCollection, column, queryVector, options2);
|
|
2267
|
+
},
|
|
2268
|
+
close() {
|
|
2269
|
+
persistentCollection.stopWatching();
|
|
2270
|
+
baseCollection.clear();
|
|
2271
|
+
embeddingManager.reset();
|
|
2272
|
+
}
|
|
2273
|
+
};
|
|
2274
|
+
if (watchFiles) {
|
|
2275
|
+
persistentCollection.startWatching();
|
|
2276
|
+
}
|
|
2277
|
+
return persistentCollection;
|
|
2278
|
+
}
|
|
2279
|
+
function createDatabase(options = {}) {
|
|
2280
|
+
const name = options.name ?? "default";
|
|
2281
|
+
let basePath;
|
|
2282
|
+
if (options.basePath) {
|
|
2283
|
+
basePath = options.basePath;
|
|
2284
|
+
} else if (options.local) {
|
|
2285
|
+
basePath = `${process.cwd()}/.fsdb/${name}`;
|
|
2286
|
+
} else {
|
|
2287
|
+
basePath = `${Bun.env.HOME}/.fsdb/${name}`;
|
|
2288
|
+
}
|
|
2289
|
+
const collections = new ReactiveMap;
|
|
2290
|
+
return {
|
|
2291
|
+
name,
|
|
2292
|
+
basePath,
|
|
2293
|
+
collection(collectionName, collectionOptions) {
|
|
2294
|
+
const existing = collections.get(collectionName);
|
|
2295
|
+
if (existing) {
|
|
2296
|
+
return existing;
|
|
2297
|
+
}
|
|
2298
|
+
const path = `${basePath}/${collectionName}`;
|
|
2299
|
+
const collection = createPersistentCollection(collectionName, {
|
|
2300
|
+
...collectionOptions,
|
|
2301
|
+
path
|
|
2302
|
+
});
|
|
2303
|
+
collections.set(collectionName, collection);
|
|
2304
|
+
return collection;
|
|
2305
|
+
},
|
|
2306
|
+
getCollection(collectionName) {
|
|
2307
|
+
return collections.get(collectionName);
|
|
2308
|
+
},
|
|
2309
|
+
listCollections() {
|
|
2310
|
+
return Array.from(collections.keys());
|
|
2311
|
+
},
|
|
2312
|
+
close() {
|
|
2313
|
+
for (const collection of collections.values()) {
|
|
2314
|
+
collection.close();
|
|
2315
|
+
}
|
|
2316
|
+
collections.clear();
|
|
2317
|
+
}
|
|
2318
|
+
};
|
|
2319
|
+
}
|
|
2320
|
+
// src/query/reactive.ts
|
|
2321
|
+
function query(collection, filter) {
|
|
2322
|
+
return derived(() => {
|
|
2323
|
+
const indices = [];
|
|
2324
|
+
const records = [];
|
|
2325
|
+
for (const index of collection.getIndices()) {
|
|
2326
|
+
const data = collection.columns.getRecord(index);
|
|
2327
|
+
if (filter(data, index)) {
|
|
2328
|
+
indices.push(index);
|
|
2329
|
+
const record = collection.getByIndex(index);
|
|
2330
|
+
if (record)
|
|
2331
|
+
records.push(record);
|
|
2332
|
+
}
|
|
2333
|
+
}
|
|
2334
|
+
return { indices, records, count: records.length };
|
|
2335
|
+
});
|
|
2336
|
+
}
|
|
2337
|
+
function querySorted(collection, filter, sortBy, descending = false) {
|
|
2338
|
+
return derived(() => {
|
|
2339
|
+
const result = query(collection, filter).value;
|
|
2340
|
+
const sortOptions = Array.isArray(sortBy) ? sortBy : [{ column: sortBy, direction: descending ? "desc" : "asc" }];
|
|
2341
|
+
const sorted = [...result.records].sort((a, b) => {
|
|
2342
|
+
for (const opt of sortOptions) {
|
|
2343
|
+
const aVal = a[opt.column];
|
|
2344
|
+
const bVal = b[opt.column];
|
|
2345
|
+
if (aVal === bVal)
|
|
2346
|
+
continue;
|
|
2347
|
+
if (aVal == null)
|
|
2348
|
+
return 1;
|
|
2349
|
+
if (bVal == null)
|
|
2350
|
+
return -1;
|
|
2351
|
+
const cmp = aVal < bVal ? -1 : 1;
|
|
2352
|
+
return opt.direction === "desc" ? -cmp : cmp;
|
|
2353
|
+
}
|
|
2354
|
+
return 0;
|
|
2355
|
+
});
|
|
2356
|
+
const sortedIndices = sorted.map((r) => collection.registry.getIndex(r.id));
|
|
2357
|
+
return { indices: sortedIndices, records: sorted, count: sorted.length };
|
|
2358
|
+
});
|
|
2359
|
+
}
|
|
2360
|
+
function queryFirst(collection, filter) {
|
|
2361
|
+
return derived(() => {
|
|
2362
|
+
for (const index of collection.getIndices()) {
|
|
2363
|
+
const data = collection.columns.getRecord(index);
|
|
2364
|
+
if (filter(data, index)) {
|
|
2365
|
+
return collection.getByIndex(index);
|
|
2366
|
+
}
|
|
2367
|
+
}
|
|
2368
|
+
return;
|
|
2369
|
+
});
|
|
2370
|
+
}
|
|
2371
|
+
function queryCount(collection, filter) {
|
|
2372
|
+
return derived(() => {
|
|
2373
|
+
let count = 0;
|
|
2374
|
+
for (const index of collection.getIndices()) {
|
|
2375
|
+
const data = collection.columns.getRecord(index);
|
|
2376
|
+
if (filter(data, index))
|
|
2377
|
+
count++;
|
|
2378
|
+
}
|
|
2379
|
+
return count;
|
|
2380
|
+
});
|
|
2381
|
+
}
|
|
2382
|
+
function queryAggregate(collection, aggregator, filter) {
|
|
2383
|
+
return derived(() => {
|
|
2384
|
+
const records = [];
|
|
2385
|
+
for (const index of collection.getIndices()) {
|
|
2386
|
+
const data = collection.columns.getRecord(index);
|
|
2387
|
+
if (!filter || filter(data, index)) {
|
|
2388
|
+
records.push(data);
|
|
2389
|
+
}
|
|
2390
|
+
}
|
|
2391
|
+
return aggregator(records);
|
|
2392
|
+
});
|
|
2393
|
+
}
|
|
2394
|
+
function queryGroupBy(collection, field, filter) {
|
|
2395
|
+
return derived(() => {
|
|
2396
|
+
const groups = new Map;
|
|
2397
|
+
for (const index of collection.getIndices()) {
|
|
2398
|
+
const data = collection.columns.getRecord(index);
|
|
2399
|
+
if (filter && !filter(data, index))
|
|
2400
|
+
continue;
|
|
2401
|
+
const key = data[field];
|
|
2402
|
+
const record = collection.getByIndex(index);
|
|
2403
|
+
if (!record)
|
|
2404
|
+
continue;
|
|
2405
|
+
const group = groups.get(key);
|
|
2406
|
+
if (group) {
|
|
2407
|
+
group.push(record);
|
|
2408
|
+
} else {
|
|
2409
|
+
groups.set(key, [record]);
|
|
2410
|
+
}
|
|
2411
|
+
}
|
|
2412
|
+
return groups;
|
|
2413
|
+
});
|
|
2414
|
+
}
|
|
2415
|
+
// src/query/filters.ts
|
|
2416
|
+
function eq(column, value) {
|
|
2417
|
+
return (record) => record[column] === value;
|
|
2418
|
+
}
|
|
2419
|
+
function neq(column, value) {
|
|
2420
|
+
return (record) => record[column] !== value;
|
|
2421
|
+
}
|
|
2422
|
+
function gt(column, value) {
|
|
2423
|
+
return (record) => record[column] > value;
|
|
2424
|
+
}
|
|
2425
|
+
function gte(column, value) {
|
|
2426
|
+
return (record) => record[column] >= value;
|
|
2427
|
+
}
|
|
2428
|
+
function lt(column, value) {
|
|
2429
|
+
return (record) => record[column] < value;
|
|
2430
|
+
}
|
|
2431
|
+
function lte(column, value) {
|
|
2432
|
+
return (record) => record[column] <= value;
|
|
2433
|
+
}
|
|
2434
|
+
function between(column, min, max) {
|
|
2435
|
+
return (record) => {
|
|
2436
|
+
const val = record[column];
|
|
2437
|
+
return val >= min && val <= max;
|
|
2438
|
+
};
|
|
2439
|
+
}
|
|
2440
|
+
function oneOf(column, values) {
|
|
2441
|
+
const set2 = new Set(values);
|
|
2442
|
+
return (record) => set2.has(record[column]);
|
|
2443
|
+
}
|
|
2444
|
+
function fullText(query2, options) {
|
|
2445
|
+
const searchTerm = options?.caseSensitive ? query2 : query2.toLowerCase();
|
|
2446
|
+
return (record) => {
|
|
2447
|
+
const columns = options?.columns ?? Object.keys(record);
|
|
2448
|
+
for (const col of columns) {
|
|
2449
|
+
const value = record[col];
|
|
2450
|
+
if (typeof value === "string") {
|
|
2451
|
+
const text = options?.caseSensitive ? value : value.toLowerCase();
|
|
2452
|
+
if (text.includes(searchTerm))
|
|
2453
|
+
return true;
|
|
2454
|
+
}
|
|
2455
|
+
}
|
|
2456
|
+
return false;
|
|
2457
|
+
};
|
|
2458
|
+
}
|
|
2459
|
+
function matches(column, pattern) {
|
|
2460
|
+
const regex = typeof pattern === "string" ? new RegExp(pattern) : pattern;
|
|
2461
|
+
return (record) => {
|
|
2462
|
+
const value = record[column];
|
|
2463
|
+
if (typeof value !== "string")
|
|
2464
|
+
return false;
|
|
2465
|
+
return regex.test(value);
|
|
2466
|
+
};
|
|
2467
|
+
}
|
|
2468
|
+
function startsWith(column, prefix, caseSensitive = false) {
|
|
2469
|
+
const search = caseSensitive ? prefix : prefix.toLowerCase();
|
|
2470
|
+
return (record) => {
|
|
2471
|
+
const value = record[column];
|
|
2472
|
+
if (typeof value !== "string")
|
|
2473
|
+
return false;
|
|
2474
|
+
const text = caseSensitive ? value : value.toLowerCase();
|
|
2475
|
+
return text.startsWith(search);
|
|
2476
|
+
};
|
|
2477
|
+
}
|
|
2478
|
+
function endsWith(column, suffix, caseSensitive = false) {
|
|
2479
|
+
const search = caseSensitive ? suffix : suffix.toLowerCase();
|
|
2480
|
+
return (record) => {
|
|
2481
|
+
const value = record[column];
|
|
2482
|
+
if (typeof value !== "string")
|
|
2483
|
+
return false;
|
|
2484
|
+
const text = caseSensitive ? value : value.toLowerCase();
|
|
2485
|
+
return text.endsWith(search);
|
|
2486
|
+
};
|
|
2487
|
+
}
|
|
2488
|
+
function contains(column, value) {
|
|
2489
|
+
return (record) => {
|
|
2490
|
+
const arr = record[column];
|
|
2491
|
+
if (!Array.isArray(arr))
|
|
2492
|
+
return false;
|
|
2493
|
+
return arr.includes(value);
|
|
2494
|
+
};
|
|
2495
|
+
}
|
|
2496
|
+
function containsAny(column, values) {
|
|
2497
|
+
const set2 = new Set(values);
|
|
2498
|
+
return (record) => {
|
|
2499
|
+
const arr = record[column];
|
|
2500
|
+
if (!Array.isArray(arr))
|
|
2501
|
+
return false;
|
|
2502
|
+
return arr.some((v) => set2.has(v));
|
|
2503
|
+
};
|
|
2504
|
+
}
|
|
2505
|
+
function containsAll(column, values) {
|
|
2506
|
+
return (record) => {
|
|
2507
|
+
const arr = record[column];
|
|
2508
|
+
if (!Array.isArray(arr))
|
|
2509
|
+
return false;
|
|
2510
|
+
return values.every((v) => arr.includes(v));
|
|
2511
|
+
};
|
|
2512
|
+
}
|
|
2513
|
+
function isEmpty(column) {
|
|
2514
|
+
return (record) => {
|
|
2515
|
+
const arr = record[column];
|
|
2516
|
+
if (!Array.isArray(arr))
|
|
2517
|
+
return true;
|
|
2518
|
+
return arr.length === 0;
|
|
2519
|
+
};
|
|
2520
|
+
}
|
|
2521
|
+
function isNotEmpty(column) {
|
|
2522
|
+
return (record) => {
|
|
2523
|
+
const arr = record[column];
|
|
2524
|
+
if (!Array.isArray(arr))
|
|
2525
|
+
return false;
|
|
2526
|
+
return arr.length > 0;
|
|
2527
|
+
};
|
|
2528
|
+
}
|
|
2529
|
+
function exists(column) {
|
|
2530
|
+
return (record) => record[column] != null;
|
|
2531
|
+
}
|
|
2532
|
+
function isNull(column) {
|
|
2533
|
+
return (record) => record[column] == null;
|
|
2534
|
+
}
|
|
2535
|
+
function after(column, date) {
|
|
2536
|
+
const ts = typeof date === "number" ? date : date.getTime();
|
|
2537
|
+
return (record) => record[column] > ts;
|
|
2538
|
+
}
|
|
2539
|
+
function before(column, date) {
|
|
2540
|
+
const ts = typeof date === "number" ? date : date.getTime();
|
|
2541
|
+
return (record) => record[column] < ts;
|
|
2542
|
+
}
|
|
2543
|
+
function withinLast(column, milliseconds) {
|
|
2544
|
+
return (record) => {
|
|
2545
|
+
const now = Date.now();
|
|
2546
|
+
const ts = record[column];
|
|
2547
|
+
return now - ts <= milliseconds;
|
|
2548
|
+
};
|
|
2549
|
+
}
|
|
2550
|
+
function and(...filters) {
|
|
2551
|
+
return (record, index) => filters.every((f) => f(record, index));
|
|
2552
|
+
}
|
|
2553
|
+
function or(...filters) {
|
|
2554
|
+
return (record, index) => filters.some((f) => f(record, index));
|
|
2555
|
+
}
|
|
2556
|
+
function not(filter) {
|
|
2557
|
+
return (record, index) => !filter(record, index);
|
|
2558
|
+
}
|
|
2559
|
+
function sortBy(records, ...sorts) {
|
|
2560
|
+
const sortOptions = sorts.map((s) => typeof s === "object" ? s : { column: s, direction: "asc" });
|
|
2561
|
+
return [...records].sort((a, b) => {
|
|
2562
|
+
for (const opt of sortOptions) {
|
|
2563
|
+
const aVal = a[opt.column];
|
|
2564
|
+
const bVal = b[opt.column];
|
|
2565
|
+
if (aVal === bVal)
|
|
2566
|
+
continue;
|
|
2567
|
+
if (aVal == null)
|
|
2568
|
+
return 1;
|
|
2569
|
+
if (bVal == null)
|
|
2570
|
+
return -1;
|
|
2571
|
+
const cmp = aVal < bVal ? -1 : 1;
|
|
2572
|
+
return opt.direction === "desc" ? -cmp : cmp;
|
|
2573
|
+
}
|
|
2574
|
+
return 0;
|
|
2575
|
+
});
|
|
2576
|
+
}
|
|
2577
|
+
function paginate(records, page = 1, pageSize = DEFAULT_PAGE_SIZE) {
|
|
2578
|
+
const total = records.length;
|
|
2579
|
+
const totalPages = Math.ceil(total / pageSize);
|
|
2580
|
+
const start = (page - 1) * pageSize;
|
|
2581
|
+
const end = start + pageSize;
|
|
2582
|
+
return {
|
|
2583
|
+
records: records.slice(start, end),
|
|
2584
|
+
total,
|
|
2585
|
+
page,
|
|
2586
|
+
pageSize,
|
|
2587
|
+
totalPages,
|
|
2588
|
+
hasNext: page < totalPages,
|
|
2589
|
+
hasPrev: page > 1
|
|
2590
|
+
};
|
|
2591
|
+
}
|
|
2592
|
+
export {
|
|
2593
|
+
withinLast,
|
|
2594
|
+
vectorSearch,
|
|
2595
|
+
toFloat32Array,
|
|
2596
|
+
startsWith,
|
|
2597
|
+
sortBy,
|
|
2598
|
+
saveToMarkdown,
|
|
2599
|
+
querySorted,
|
|
2600
|
+
queryGroupBy,
|
|
2601
|
+
queryFirst,
|
|
2602
|
+
queryCount,
|
|
2603
|
+
queryAggregate,
|
|
2604
|
+
query,
|
|
2605
|
+
parseSchema,
|
|
2606
|
+
parseMarkdown,
|
|
2607
|
+
parseColumnType,
|
|
2608
|
+
paginate,
|
|
2609
|
+
or,
|
|
2610
|
+
oneOf,
|
|
2611
|
+
not,
|
|
2612
|
+
normalizeVector,
|
|
2613
|
+
neq,
|
|
2614
|
+
matches,
|
|
2615
|
+
lte,
|
|
2616
|
+
lt,
|
|
2617
|
+
loadFromMarkdown,
|
|
2618
|
+
loadFromDirectory,
|
|
2619
|
+
isNull,
|
|
2620
|
+
isNotEmpty,
|
|
2621
|
+
isEmpty,
|
|
2622
|
+
idToFilename,
|
|
2623
|
+
gte,
|
|
2624
|
+
gt,
|
|
2625
|
+
getDefaultValue,
|
|
2626
|
+
generateMarkdown,
|
|
2627
|
+
generateId,
|
|
2628
|
+
fullText,
|
|
2629
|
+
createDatabase as fsDB,
|
|
2630
|
+
filenameToId,
|
|
2631
|
+
exists,
|
|
2632
|
+
eq,
|
|
2633
|
+
ensureDirectory,
|
|
2634
|
+
endsWith,
|
|
2635
|
+
deleteMarkdownFile,
|
|
2636
|
+
createRegistry,
|
|
2637
|
+
createPersistentCollection,
|
|
2638
|
+
createFileWatcher,
|
|
2639
|
+
createEmbeddingManager,
|
|
2640
|
+
createDatabase,
|
|
2641
|
+
createColumns,
|
|
2642
|
+
createCollection,
|
|
2643
|
+
cosineSimilarity,
|
|
2644
|
+
containsAny,
|
|
2645
|
+
containsAll,
|
|
2646
|
+
contains,
|
|
2647
|
+
between,
|
|
2648
|
+
before,
|
|
2649
|
+
batchCosineSimilarity,
|
|
2650
|
+
and,
|
|
2651
|
+
after,
|
|
2652
|
+
WATCHER_DEBOUNCE_MS,
|
|
2653
|
+
SAVE_GRACE_PERIOD_MS,
|
|
2654
|
+
DEFAULT_VALUES,
|
|
2655
|
+
DEFAULT_PAGE_SIZE
|
|
2656
|
+
};
|