nvent 0.4.5 → 0.5.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/dist/module.d.mts +1 -1
- package/dist/module.mjs +433 -175
- package/dist/runtime/adapters/base/index.d.ts +6 -0
- package/dist/runtime/adapters/base/index.js +1 -0
- package/dist/runtime/adapters/base/store-validator.d.ts +48 -0
- package/dist/runtime/adapters/base/store-validator.js +147 -0
- package/dist/runtime/adapters/builtin/file-queue.d.ts +15 -1
- package/dist/runtime/adapters/builtin/file-queue.js +70 -6
- package/dist/runtime/adapters/builtin/file-store.d.ts +4 -18
- package/dist/runtime/adapters/builtin/file-store.js +90 -109
- package/dist/runtime/adapters/builtin/memory-queue.js +4 -0
- package/dist/runtime/adapters/builtin/memory-store.d.ts +42 -31
- package/dist/runtime/adapters/builtin/memory-store.js +253 -183
- package/dist/runtime/adapters/factory.d.ts +2 -2
- package/dist/runtime/adapters/factory.js +54 -20
- package/dist/runtime/adapters/interfaces/store.d.ts +177 -113
- package/dist/runtime/config/index.d.ts +2 -2
- package/dist/runtime/config/index.js +14 -6
- package/dist/runtime/config/types.d.ts +32 -2
- package/dist/runtime/events/eventBus.d.ts +1 -1
- package/dist/runtime/events/types.d.ts +31 -2
- package/dist/runtime/events/utils/scheduleTrigger.d.ts +8 -0
- package/dist/runtime/events/utils/scheduleTrigger.js +69 -0
- package/dist/runtime/events/utils/stallDetector.d.ts +44 -3
- package/dist/runtime/events/utils/stallDetector.js +288 -89
- package/dist/runtime/events/utils/triggerRuntime.d.ts +58 -0
- package/dist/runtime/events/utils/triggerRuntime.js +212 -0
- package/dist/runtime/events/wiring/flowWiring.d.ts +11 -5
- package/dist/runtime/events/wiring/flowWiring.js +620 -92
- package/dist/runtime/events/wiring/registry.d.ts +2 -2
- package/dist/runtime/events/wiring/registry.js +8 -6
- package/dist/runtime/events/wiring/streamWiring.d.ts +15 -11
- package/dist/runtime/events/wiring/streamWiring.js +88 -11
- package/dist/runtime/events/wiring/triggerWiring.d.ts +21 -0
- package/dist/runtime/events/wiring/triggerWiring.js +412 -0
- package/dist/runtime/{server → nitro}/plugins/00.adapters.js +8 -4
- package/dist/runtime/{server → nitro}/plugins/02.workers.js +21 -3
- package/dist/runtime/nitro/plugins/03.triggers.d.ts +12 -0
- package/dist/runtime/nitro/plugins/03.triggers.js +55 -0
- package/dist/runtime/nitro/routes/webhook.await.d.ts +23 -0
- package/dist/runtime/nitro/routes/webhook.await.js +90 -0
- package/dist/runtime/nitro/routes/webhook.trigger.d.ts +69 -0
- package/dist/runtime/nitro/routes/webhook.trigger.js +64 -0
- package/dist/runtime/{utils → nitro/utils}/adapters.d.ts +6 -6
- package/dist/runtime/nitro/utils/awaitPatterns/event.d.ts +15 -0
- package/dist/runtime/nitro/utils/awaitPatterns/event.js +120 -0
- package/dist/runtime/nitro/utils/awaitPatterns/index.d.ts +28 -0
- package/dist/runtime/nitro/utils/awaitPatterns/index.js +55 -0
- package/dist/runtime/nitro/utils/awaitPatterns/schedule.d.ts +16 -0
- package/dist/runtime/nitro/utils/awaitPatterns/schedule.js +78 -0
- package/dist/runtime/nitro/utils/awaitPatterns/time.d.ts +15 -0
- package/dist/runtime/nitro/utils/awaitPatterns/time.js +67 -0
- package/dist/runtime/nitro/utils/awaitPatterns/webhook.d.ts +15 -0
- package/dist/runtime/nitro/utils/awaitPatterns/webhook.js +120 -0
- package/dist/runtime/{utils → nitro/utils}/defineFunction.d.ts +2 -2
- package/dist/runtime/{utils → nitro/utils}/defineFunction.js +3 -3
- package/dist/runtime/{utils → nitro/utils}/defineFunctionConfig.d.ts +156 -0
- package/dist/runtime/{utils → nitro/utils}/defineFunctionConfig.js +1 -0
- package/dist/runtime/nitro/utils/defineHooks.d.ts +41 -0
- package/dist/runtime/nitro/utils/defineHooks.js +6 -0
- package/dist/runtime/{utils → nitro/utils}/registerAdapter.d.ts +3 -3
- package/dist/runtime/{utils → nitro/utils}/registerAdapter.js +1 -1
- package/dist/runtime/nitro/utils/useAwait.d.ts +71 -0
- package/dist/runtime/nitro/utils/useAwait.js +139 -0
- package/dist/runtime/{utils → nitro/utils}/useEventManager.d.ts +2 -2
- package/dist/runtime/{utils → nitro/utils}/useEventManager.js +1 -1
- package/dist/runtime/nitro/utils/useFlow.d.ts +68 -0
- package/dist/runtime/nitro/utils/useFlow.js +226 -0
- package/dist/runtime/nitro/utils/useHookRegistry.d.ts +34 -0
- package/dist/runtime/nitro/utils/useHookRegistry.js +25 -0
- package/dist/runtime/nitro/utils/useRunContext.d.ts +6 -0
- package/dist/runtime/nitro/utils/useRunContext.js +102 -0
- package/dist/runtime/nitro/utils/useStreamTopics.d.ts +83 -0
- package/dist/runtime/nitro/utils/useStreamTopics.js +94 -0
- package/dist/runtime/nitro/utils/useTrigger.d.ts +150 -0
- package/dist/runtime/nitro/utils/useTrigger.js +320 -0
- package/dist/runtime/scheduler/index.d.ts +33 -0
- package/dist/runtime/scheduler/index.js +38 -0
- package/dist/runtime/scheduler/scheduler.d.ts +113 -0
- package/dist/runtime/scheduler/scheduler.js +623 -0
- package/dist/runtime/scheduler/types.d.ts +116 -0
- package/dist/runtime/scheduler/types.js +0 -0
- package/dist/runtime/worker/node/runner.d.ts +12 -2
- package/dist/runtime/worker/node/runner.js +141 -37
- package/package.json +6 -6
- package/dist/runtime/server/api/_flows/[name]/clear-history.delete.d.ts +0 -10
- package/dist/runtime/server/api/_flows/[name]/clear-history.delete.js +0 -55
- package/dist/runtime/server/api/_flows/[name]/runs/[runId]/cancel.post.d.ts +0 -2
- package/dist/runtime/server/api/_flows/[name]/runs/[runId]/cancel.post.js +0 -21
- package/dist/runtime/server/api/_flows/[name]/runs.get.d.ts +0 -17
- package/dist/runtime/server/api/_flows/[name]/runs.get.js +0 -64
- package/dist/runtime/server/api/_flows/[name]/schedule.post.d.ts +0 -2
- package/dist/runtime/server/api/_flows/[name]/schedule.post.js +0 -66
- package/dist/runtime/server/api/_flows/[name]/schedules/[id].delete.d.ts +0 -2
- package/dist/runtime/server/api/_flows/[name]/schedules/[id].delete.js +0 -47
- package/dist/runtime/server/api/_flows/[name]/schedules.get.d.ts +0 -2
- package/dist/runtime/server/api/_flows/[name]/schedules.get.js +0 -50
- package/dist/runtime/server/api/_flows/[name]/start.post.d.ts +0 -2
- package/dist/runtime/server/api/_flows/[name]/start.post.js +0 -9
- package/dist/runtime/server/api/_flows/index.get.d.ts +0 -6
- package/dist/runtime/server/api/_flows/index.get.js +0 -5
- package/dist/runtime/server/api/_flows/ws.d.ts +0 -60
- package/dist/runtime/server/api/_flows/ws.js +0 -209
- package/dist/runtime/server/api/_queues/[name]/job/[id].get.d.ts +0 -2
- package/dist/runtime/server/api/_queues/[name]/job/[id].get.js +0 -14
- package/dist/runtime/server/api/_queues/[name]/job/index.get.d.ts +0 -2
- package/dist/runtime/server/api/_queues/[name]/job/index.get.js +0 -27
- package/dist/runtime/server/api/_queues/index.get.d.ts +0 -2
- package/dist/runtime/server/api/_queues/index.get.js +0 -106
- package/dist/runtime/server/api/_queues/ws.d.ts +0 -48
- package/dist/runtime/server/api/_queues/ws.js +0 -215
- package/dist/runtime/utils/useFlowEngine.d.ts +0 -19
- package/dist/runtime/utils/useFlowEngine.js +0 -108
- package/dist/runtime/utils/useStreamTopics.d.ts +0 -72
- package/dist/runtime/utils/useStreamTopics.js +0 -47
- /package/dist/runtime/{server → nitro}/plugins/00.adapters.d.ts +0 -0
- /package/dist/runtime/{server → nitro}/plugins/01.ws-lifecycle.d.ts +0 -0
- /package/dist/runtime/{server → nitro}/plugins/01.ws-lifecycle.js +0 -0
- /package/dist/runtime/{server → nitro}/plugins/02.workers.d.ts +0 -0
- /package/dist/runtime/{utils → nitro/utils}/adapters.js +0 -0
- /package/dist/runtime/{utils → nitro/utils}/useNventLogger.d.ts +0 -0
- /package/dist/runtime/{utils → nitro/utils}/useNventLogger.js +0 -0
- /package/dist/runtime/{utils → nitro/utils}/wsPeerManager.d.ts +0 -0
- /package/dist/runtime/{utils → nitro/utils}/wsPeerManager.js +0 -0
|
@@ -4,30 +4,28 @@
|
|
|
4
4
|
* In-memory storage implementation for development
|
|
5
5
|
* Three-tier storage:
|
|
6
6
|
* 1. Event Stream - Append-only event log
|
|
7
|
-
* 2.
|
|
7
|
+
* 2. Sorted Index - Time-ordered metadata storage
|
|
8
8
|
* 3. Key-Value Store - Fast lookups
|
|
9
9
|
*
|
|
10
10
|
* All data is lost on restart (ephemeral)
|
|
11
11
|
*/
|
|
12
|
-
import type { StoreAdapter, EventRecord, EventReadOptions, EventSubscription
|
|
12
|
+
import type { StoreAdapter, EventRecord, EventReadOptions, EventSubscription } from '../interfaces/store.js';
|
|
13
13
|
export declare class MemoryStoreAdapter implements StoreAdapter {
|
|
14
14
|
private eventStreams;
|
|
15
15
|
private eventSubscriptions;
|
|
16
16
|
private subscriptionCounter;
|
|
17
|
-
private documents;
|
|
18
17
|
private kvStore;
|
|
18
|
+
private sortedIndices;
|
|
19
|
+
protected validator: import("../base/index.js").StoreValidator;
|
|
20
|
+
private indexLocks;
|
|
19
21
|
close(): Promise<void>;
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
stream: {
|
|
23
|
+
append: (subject: string, event: Omit<EventRecord, "id" | "ts">) => Promise<EventRecord>;
|
|
24
|
+
read: (subject: string, opts?: EventReadOptions) => Promise<EventRecord[]>;
|
|
25
|
+
subscribe: (subject: string, onEvent: (event: EventRecord) => void) => Promise<EventSubscription>;
|
|
26
|
+
delete: (subject: string) => Promise<boolean>;
|
|
27
|
+
};
|
|
23
28
|
private notifySubscribers;
|
|
24
|
-
save(collection: string, id: string, doc: Record<string, any>): Promise<void>;
|
|
25
|
-
get(collection: string, id: string): Promise<Record<string, any> | null>;
|
|
26
|
-
list(collection: string, opts?: ListOptions): Promise<Array<{
|
|
27
|
-
id: string;
|
|
28
|
-
doc: any;
|
|
29
|
-
}>>;
|
|
30
|
-
delete(collection: string, id: string): Promise<void>;
|
|
31
29
|
kv: {
|
|
32
30
|
get: <T = any>(key: string) => Promise<T | null>;
|
|
33
31
|
set: <T = any>(key: string, value: T, _ttl?: number) => Promise<void>;
|
|
@@ -35,23 +33,36 @@ export declare class MemoryStoreAdapter implements StoreAdapter {
|
|
|
35
33
|
clear: (pattern: string) => Promise<number>;
|
|
36
34
|
increment: (key: string, by?: number) => Promise<number>;
|
|
37
35
|
};
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
36
|
+
index: {
|
|
37
|
+
add: (key: string, id: string, score: number, metadata?: Record<string, any>) => Promise<void>;
|
|
38
|
+
get: (key: string, id: string) => Promise<{
|
|
39
|
+
id: string;
|
|
40
|
+
score: number;
|
|
41
|
+
metadata?: any;
|
|
42
|
+
} | null>;
|
|
43
|
+
read: (key: string, opts?: {
|
|
44
|
+
offset?: number;
|
|
45
|
+
limit?: number;
|
|
46
|
+
}) => Promise<Array<{
|
|
47
|
+
id: string;
|
|
48
|
+
score: number;
|
|
49
|
+
metadata?: any;
|
|
50
|
+
}>>;
|
|
51
|
+
update: (key: string, id: string, metadata: Record<string, any>) => Promise<boolean>;
|
|
52
|
+
updateWithRetry: (key: string, id: string, metadata: Record<string, any>, maxRetries?: number) => Promise<void>;
|
|
53
|
+
increment: (key: string, id: string, field: string, increment?: number) => Promise<number>;
|
|
54
|
+
delete: (key: string, id: string) => Promise<boolean>;
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* Acquire a lock for atomic index operations
|
|
58
|
+
* Protected to allow access from FileStoreAdapter
|
|
59
|
+
*/
|
|
60
|
+
protected acquireIndexLock(key: string): Promise<() => void>;
|
|
61
|
+
/**
|
|
62
|
+
* Convert dot notation keys to nested objects
|
|
63
|
+
* e.g., { 'stats.totalFires': 5 } -> { stats: { totalFires: 5 } }
|
|
64
|
+
* null values are preserved for deletion
|
|
65
|
+
*/
|
|
66
|
+
private expandDotNotation;
|
|
56
67
|
private generateId;
|
|
57
68
|
}
|
|
@@ -1,88 +1,104 @@
|
|
|
1
|
+
import { defu } from "defu";
|
|
2
|
+
import { createStoreValidator } from "../base/store-validator.js";
|
|
1
3
|
export class MemoryStoreAdapter {
|
|
2
4
|
// Event Stream storage: subject -> events
|
|
3
5
|
eventStreams = /* @__PURE__ */ new Map();
|
|
4
6
|
eventSubscriptions = /* @__PURE__ */ new Map();
|
|
5
7
|
subscriptionCounter = 0;
|
|
6
|
-
// Document Store storage: collection -> id -> document
|
|
7
|
-
documents = /* @__PURE__ */ new Map();
|
|
8
8
|
// Key-Value Store storage: key -> value
|
|
9
9
|
kvStore = /* @__PURE__ */ new Map();
|
|
10
|
+
// Sorted index storage: key -> sorted array of {id, score, metadata}
|
|
11
|
+
sortedIndices = /* @__PURE__ */ new Map();
|
|
12
|
+
// Validator for update operations
|
|
13
|
+
validator = createStoreValidator("MemoryStoreAdapter");
|
|
14
|
+
// Lock mechanism for atomic index operations
|
|
15
|
+
indexLocks = /* @__PURE__ */ new Map();
|
|
10
16
|
async close() {
|
|
11
17
|
this.eventStreams.clear();
|
|
12
18
|
this.eventSubscriptions.clear();
|
|
13
|
-
this.documents.clear();
|
|
14
19
|
this.kvStore.clear();
|
|
20
|
+
this.sortedIndices.clear();
|
|
15
21
|
}
|
|
16
22
|
// ============================================================
|
|
17
23
|
// Event Stream
|
|
18
24
|
// ============================================================
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
this.eventStreams.
|
|
27
|
-
|
|
28
|
-
const stream = this.eventStreams.get(subject);
|
|
29
|
-
stream.push(eventRecord);
|
|
30
|
-
this.notifySubscribers(subject, eventRecord);
|
|
31
|
-
return eventRecord;
|
|
32
|
-
}
|
|
33
|
-
async read(subject, opts) {
|
|
34
|
-
const stream = this.eventStreams.get(subject) || [];
|
|
35
|
-
let events = [...stream];
|
|
36
|
-
if (opts?.types && opts.types.length > 0) {
|
|
37
|
-
events = events.filter((e) => opts.types.includes(e.type));
|
|
38
|
-
}
|
|
39
|
-
if (opts?.after) {
|
|
40
|
-
const afterIndex = events.findIndex((e) => e.id === opts.after);
|
|
41
|
-
if (afterIndex >= 0) {
|
|
42
|
-
events = events.slice(afterIndex + 1);
|
|
25
|
+
stream = {
|
|
26
|
+
append: async (subject, event) => {
|
|
27
|
+
const eventRecord = {
|
|
28
|
+
id: this.generateId(),
|
|
29
|
+
ts: Date.now(),
|
|
30
|
+
...event
|
|
31
|
+
};
|
|
32
|
+
if (!this.eventStreams.has(subject)) {
|
|
33
|
+
this.eventStreams.set(subject, []);
|
|
43
34
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
35
|
+
const stream = this.eventStreams.get(subject);
|
|
36
|
+
stream.push(eventRecord);
|
|
37
|
+
this.notifySubscribers(subject, eventRecord);
|
|
38
|
+
return eventRecord;
|
|
39
|
+
},
|
|
40
|
+
read: async (subject, opts) => {
|
|
41
|
+
const stream = this.eventStreams.get(subject) || [];
|
|
42
|
+
let events = [...stream];
|
|
43
|
+
if (opts?.types && opts.types.length > 0) {
|
|
44
|
+
events = events.filter((e) => opts.types.includes(e.type));
|
|
49
45
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
46
|
+
if (opts?.after) {
|
|
47
|
+
const afterIndex = events.findIndex((e) => e.id === opts.after);
|
|
48
|
+
if (afterIndex >= 0) {
|
|
49
|
+
events = events.slice(afterIndex + 1);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (opts?.before) {
|
|
53
|
+
const beforeIndex = events.findIndex((e) => e.id === opts.before);
|
|
54
|
+
if (beforeIndex >= 0) {
|
|
55
|
+
events = events.slice(0, beforeIndex);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (opts?.from) {
|
|
59
|
+
events = events.filter((e) => e.ts >= opts.from);
|
|
60
|
+
}
|
|
61
|
+
if (opts?.to) {
|
|
62
|
+
events = events.filter((e) => e.ts <= opts.to);
|
|
63
|
+
}
|
|
64
|
+
if (opts?.order === "desc") {
|
|
65
|
+
events.reverse();
|
|
66
|
+
}
|
|
67
|
+
if (opts?.limit) {
|
|
68
|
+
events = events.slice(0, opts.limit);
|
|
69
|
+
}
|
|
70
|
+
return events;
|
|
71
|
+
},
|
|
72
|
+
subscribe: async (subject, onEvent) => {
|
|
73
|
+
const subscriptionId = `sub-${++this.subscriptionCounter}`;
|
|
74
|
+
if (!this.eventSubscriptions.has(subject)) {
|
|
75
|
+
this.eventSubscriptions.set(subject, /* @__PURE__ */ new Map());
|
|
76
|
+
}
|
|
77
|
+
const subjectSubs = this.eventSubscriptions.get(subject);
|
|
78
|
+
subjectSubs.set(subscriptionId, onEvent);
|
|
79
|
+
return {
|
|
80
|
+
id: subscriptionId,
|
|
81
|
+
subject,
|
|
82
|
+
unsubscribe: async () => {
|
|
83
|
+
const subs = this.eventSubscriptions.get(subject);
|
|
84
|
+
if (subs) {
|
|
85
|
+
subs.delete(subscriptionId);
|
|
86
|
+
if (subs.size === 0) {
|
|
87
|
+
this.eventSubscriptions.delete(subject);
|
|
88
|
+
}
|
|
81
89
|
}
|
|
82
90
|
}
|
|
91
|
+
};
|
|
92
|
+
},
|
|
93
|
+
delete: async (subject) => {
|
|
94
|
+
const existed = this.eventStreams.has(subject);
|
|
95
|
+
if (existed) {
|
|
96
|
+
this.eventStreams.delete(subject);
|
|
97
|
+
this.eventSubscriptions.delete(subject);
|
|
83
98
|
}
|
|
84
|
-
|
|
85
|
-
|
|
99
|
+
return existed;
|
|
100
|
+
}
|
|
101
|
+
};
|
|
86
102
|
notifySubscribers(subject, event) {
|
|
87
103
|
const subs = this.eventSubscriptions.get(subject);
|
|
88
104
|
if (subs) {
|
|
@@ -96,62 +112,6 @@ export class MemoryStoreAdapter {
|
|
|
96
112
|
}
|
|
97
113
|
}
|
|
98
114
|
// ============================================================
|
|
99
|
-
// Document Store
|
|
100
|
-
// ============================================================
|
|
101
|
-
async save(collection, id, doc) {
|
|
102
|
-
if (!this.documents.has(collection)) {
|
|
103
|
-
this.documents.set(collection, /* @__PURE__ */ new Map());
|
|
104
|
-
}
|
|
105
|
-
const collectionDocs = this.documents.get(collection);
|
|
106
|
-
collectionDocs.set(id, doc);
|
|
107
|
-
}
|
|
108
|
-
async get(collection, id) {
|
|
109
|
-
const collectionDocs = this.documents.get(collection);
|
|
110
|
-
if (!collectionDocs) {
|
|
111
|
-
return null;
|
|
112
|
-
}
|
|
113
|
-
return collectionDocs.get(id) || null;
|
|
114
|
-
}
|
|
115
|
-
async list(collection, opts) {
|
|
116
|
-
const collectionDocs = this.documents.get(collection);
|
|
117
|
-
if (!collectionDocs) {
|
|
118
|
-
return [];
|
|
119
|
-
}
|
|
120
|
-
let results = Array.from(collectionDocs.entries()).map(([id, doc]) => ({ id, doc }));
|
|
121
|
-
if (opts?.filter) {
|
|
122
|
-
results = results.filter((item) => {
|
|
123
|
-
return Object.entries(opts.filter).every(([key, value]) => {
|
|
124
|
-
return item.doc[key] === value;
|
|
125
|
-
});
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
|
-
if (opts?.sortBy) {
|
|
129
|
-
results.sort((a, b) => {
|
|
130
|
-
const aVal = a.doc[opts.sortBy];
|
|
131
|
-
const bVal = b.doc[opts.sortBy];
|
|
132
|
-
if (aVal < bVal) return opts.order === "desc" ? 1 : -1;
|
|
133
|
-
if (aVal > bVal) return opts.order === "desc" ? -1 : 1;
|
|
134
|
-
return 0;
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
if (opts?.offset) {
|
|
138
|
-
results = results.slice(opts.offset);
|
|
139
|
-
}
|
|
140
|
-
if (opts?.limit) {
|
|
141
|
-
results = results.slice(0, opts.limit);
|
|
142
|
-
}
|
|
143
|
-
return results;
|
|
144
|
-
}
|
|
145
|
-
async delete(collection, id) {
|
|
146
|
-
const collectionDocs = this.documents.get(collection);
|
|
147
|
-
if (collectionDocs) {
|
|
148
|
-
collectionDocs.delete(id);
|
|
149
|
-
if (collectionDocs.size === 0) {
|
|
150
|
-
this.documents.delete(collection);
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
// ============================================================
|
|
155
115
|
// Key-Value Store
|
|
156
116
|
// ============================================================
|
|
157
117
|
kv = {
|
|
@@ -184,75 +144,185 @@ export class MemoryStoreAdapter {
|
|
|
184
144
|
}
|
|
185
145
|
};
|
|
186
146
|
// ============================================================
|
|
187
|
-
// Sorted Index (
|
|
147
|
+
// Sorted Index (for time-ordered listings)
|
|
188
148
|
// ============================================================
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
149
|
+
index = {
|
|
150
|
+
add: async (key, id, score, metadata) => {
|
|
151
|
+
if (!this.sortedIndices.has(key)) {
|
|
152
|
+
this.sortedIndices.set(key, []);
|
|
153
|
+
}
|
|
154
|
+
const index = this.sortedIndices.get(key);
|
|
155
|
+
const existingIndex = index.findIndex((entry2) => entry2.id === id);
|
|
156
|
+
const expandedMetadata = metadata ? this.expandDotNotation(metadata) : void 0;
|
|
157
|
+
const entry = {
|
|
158
|
+
id,
|
|
159
|
+
score,
|
|
160
|
+
metadata: expandedMetadata ? { version: 0, ...expandedMetadata } : void 0
|
|
161
|
+
};
|
|
162
|
+
if (existingIndex >= 0) {
|
|
163
|
+
index[existingIndex] = entry;
|
|
164
|
+
} else {
|
|
165
|
+
index.push(entry);
|
|
166
|
+
}
|
|
167
|
+
index.sort((a, b) => b.score - a.score);
|
|
168
|
+
},
|
|
169
|
+
get: async (key, id) => {
|
|
170
|
+
const index = this.sortedIndices.get(key);
|
|
171
|
+
if (!index) return null;
|
|
172
|
+
const entry = index.find((e) => e.id === id);
|
|
173
|
+
return entry ? { ...entry } : null;
|
|
174
|
+
},
|
|
175
|
+
read: async (key, opts) => {
|
|
176
|
+
const index = this.sortedIndices.get(key) || [];
|
|
177
|
+
const offset = opts?.offset || 0;
|
|
178
|
+
const limit = opts?.limit || 50;
|
|
179
|
+
return index.slice(offset, offset + limit).map((e) => ({ ...e }));
|
|
180
|
+
},
|
|
181
|
+
update: async (key, id, metadata) => {
|
|
182
|
+
this.validator.validateUpdatePayload(metadata, "index.update");
|
|
183
|
+
const lockKey = `${key}:${id}`;
|
|
184
|
+
const release = await this.acquireIndexLock(lockKey);
|
|
185
|
+
try {
|
|
186
|
+
const index = this.sortedIndices.get(key);
|
|
187
|
+
if (!index) return false;
|
|
188
|
+
const entry = index.find((e) => e.id === id);
|
|
189
|
+
if (!entry || !entry.metadata) return false;
|
|
190
|
+
const currentVersion = entry.metadata.version || 0;
|
|
191
|
+
const updates = this.expandDotNotation(metadata);
|
|
192
|
+
const deleteMarkers = updates.__deleteMarkers;
|
|
193
|
+
delete updates.__deleteMarkers;
|
|
194
|
+
entry.metadata = defu(updates, entry.metadata);
|
|
195
|
+
if (deleteMarkers) {
|
|
196
|
+
for (const { path } of deleteMarkers) {
|
|
197
|
+
let current = entry.metadata;
|
|
198
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
199
|
+
if (!current[path[i]]) break;
|
|
200
|
+
current = current[path[i]];
|
|
201
|
+
}
|
|
202
|
+
if (current) {
|
|
203
|
+
const lastKey = path[path.length - 1];
|
|
204
|
+
Reflect.deleteProperty(current, lastKey);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
entry.metadata.version = currentVersion + 1;
|
|
209
|
+
return true;
|
|
210
|
+
} finally {
|
|
211
|
+
release();
|
|
212
|
+
}
|
|
213
|
+
},
|
|
214
|
+
updateWithRetry: async (key, id, metadata, maxRetries = 3) => {
|
|
215
|
+
this.validator.validateUpdatePayload(metadata, "index.updateWithRetry");
|
|
216
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
217
|
+
const success = await this.index.update(key, id, metadata);
|
|
218
|
+
if (success) return;
|
|
219
|
+
await new Promise((resolve) => setTimeout(resolve, 10 * Math.pow(2, attempt)));
|
|
220
|
+
}
|
|
221
|
+
throw new Error(`Failed to update index after ${maxRetries} retries`);
|
|
222
|
+
},
|
|
223
|
+
increment: async (key, id, field, increment = 1) => {
|
|
224
|
+
const lockKey = `${key}:${id}`;
|
|
225
|
+
const release = await this.acquireIndexLock(lockKey);
|
|
226
|
+
try {
|
|
227
|
+
const index = this.sortedIndices.get(key);
|
|
228
|
+
if (!index) throw new Error(`Index not found: ${key}`);
|
|
229
|
+
const entry = index.find((e) => e.id === id);
|
|
230
|
+
if (!entry) throw new Error(`Entry not found: ${id} in index ${key}`);
|
|
231
|
+
if (!entry.metadata) {
|
|
232
|
+
entry.metadata = { version: 0 };
|
|
233
|
+
}
|
|
234
|
+
let currentValue;
|
|
235
|
+
let newValue;
|
|
236
|
+
if (field.includes(".")) {
|
|
237
|
+
const keys = field.split(".");
|
|
238
|
+
let current = entry.metadata;
|
|
239
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
240
|
+
const k = keys[i];
|
|
241
|
+
if (!current[k] || typeof current[k] !== "object") {
|
|
242
|
+
current[k] = {};
|
|
243
|
+
}
|
|
244
|
+
current = current[k];
|
|
245
|
+
}
|
|
246
|
+
const lastKey = keys[keys.length - 1];
|
|
247
|
+
currentValue = current[lastKey] || 0;
|
|
248
|
+
newValue = (typeof currentValue === "number" ? currentValue : 0) + increment;
|
|
249
|
+
current[lastKey] = newValue;
|
|
250
|
+
} else {
|
|
251
|
+
currentValue = entry.metadata[field] || 0;
|
|
252
|
+
newValue = (typeof currentValue === "number" ? currentValue : 0) + increment;
|
|
253
|
+
entry.metadata[field] = newValue;
|
|
254
|
+
}
|
|
255
|
+
entry.metadata.version = (entry.metadata.version || 0) + 1;
|
|
256
|
+
return newValue;
|
|
257
|
+
} finally {
|
|
258
|
+
release();
|
|
259
|
+
}
|
|
260
|
+
},
|
|
261
|
+
delete: async (key, id) => {
|
|
262
|
+
const index = this.sortedIndices.get(key);
|
|
263
|
+
if (!index) return false;
|
|
264
|
+
const initialLength = index.length;
|
|
265
|
+
const filtered = index.filter((e) => e.id !== id);
|
|
266
|
+
if (filtered.length === initialLength) {
|
|
267
|
+
return false;
|
|
268
|
+
}
|
|
269
|
+
this.sortedIndices.set(key, filtered);
|
|
270
|
+
return true;
|
|
194
271
|
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
index[existingIndex] = entry;
|
|
204
|
-
} else {
|
|
205
|
-
index.push(entry);
|
|
272
|
+
};
|
|
273
|
+
/**
|
|
274
|
+
* Acquire a lock for atomic index operations
|
|
275
|
+
* Protected to allow access from FileStoreAdapter
|
|
276
|
+
*/
|
|
277
|
+
async acquireIndexLock(key) {
|
|
278
|
+
while (this.indexLocks.has(key)) {
|
|
279
|
+
await this.indexLocks.get(key);
|
|
206
280
|
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
async indexRead(key, opts) {
|
|
216
|
-
const index = this.sortedIndices.get(key) || [];
|
|
217
|
-
const offset = opts?.offset || 0;
|
|
218
|
-
const limit = opts?.limit || 50;
|
|
219
|
-
return index.slice(offset, offset + limit).map((e) => ({ ...e }));
|
|
220
|
-
}
|
|
221
|
-
async indexUpdate(key, id, metadata) {
|
|
222
|
-
const index = this.sortedIndices.get(key);
|
|
223
|
-
if (!index) return false;
|
|
224
|
-
const entry = index.find((e) => e.id === id);
|
|
225
|
-
if (!entry || !entry.metadata) return false;
|
|
226
|
-
const currentVersion = entry.metadata.version || 0;
|
|
227
|
-
const expectedVersion = currentVersion;
|
|
228
|
-
entry.metadata = {
|
|
229
|
-
...entry.metadata,
|
|
230
|
-
...metadata,
|
|
231
|
-
version: expectedVersion + 1
|
|
281
|
+
let releaseLock;
|
|
282
|
+
const lockPromise = new Promise((resolve) => {
|
|
283
|
+
releaseLock = resolve;
|
|
284
|
+
});
|
|
285
|
+
this.indexLocks.set(key, lockPromise);
|
|
286
|
+
return () => {
|
|
287
|
+
this.indexLocks.delete(key);
|
|
288
|
+
releaseLock();
|
|
232
289
|
};
|
|
233
|
-
return true;
|
|
234
290
|
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
291
|
+
/**
|
|
292
|
+
* Convert dot notation keys to nested objects
|
|
293
|
+
* e.g., { 'stats.totalFires': 5 } -> { stats: { totalFires: 5 } }
|
|
294
|
+
* null values are preserved for deletion
|
|
295
|
+
*/
|
|
296
|
+
expandDotNotation(obj) {
|
|
297
|
+
const result = {};
|
|
298
|
+
const deleteMarkers = [];
|
|
299
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
300
|
+
if (key === "version") {
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
if (key.includes(".")) {
|
|
304
|
+
const keys = key.split(".");
|
|
305
|
+
if (value === null || value === void 0) {
|
|
306
|
+
deleteMarkers.push({ path: keys, delete: true });
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
let current = result;
|
|
310
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
311
|
+
const k = keys[i];
|
|
312
|
+
if (!current[k]) {
|
|
313
|
+
current[k] = {};
|
|
314
|
+
}
|
|
315
|
+
current = current[k];
|
|
316
|
+
}
|
|
317
|
+
current[keys[keys.length - 1]] = value;
|
|
318
|
+
} else {
|
|
319
|
+
result[key] = value;
|
|
320
|
+
}
|
|
240
321
|
}
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
async indexIncrement(key, id, field, increment = 1) {
|
|
244
|
-
const index = this.sortedIndices.get(key);
|
|
245
|
-
if (!index) throw new Error(`Index not found: ${key}`);
|
|
246
|
-
const entry = index.find((e) => e.id === id);
|
|
247
|
-
if (!entry) throw new Error(`Entry not found: ${id} in index ${key}`);
|
|
248
|
-
if (!entry.metadata) {
|
|
249
|
-
entry.metadata = { version: 0 };
|
|
322
|
+
if (deleteMarkers.length > 0) {
|
|
323
|
+
result.__deleteMarkers = deleteMarkers;
|
|
250
324
|
}
|
|
251
|
-
|
|
252
|
-
const newValue = (typeof currentValue === "number" ? currentValue : 0) + increment;
|
|
253
|
-
entry.metadata[field] = newValue;
|
|
254
|
-
entry.metadata.version = (entry.metadata.version || 0) + 1;
|
|
255
|
-
return newValue;
|
|
325
|
+
return result;
|
|
256
326
|
}
|
|
257
327
|
// ============================================================
|
|
258
328
|
// Helpers
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Adapter Factory
|
|
2
|
+
* Adapter Factory
|
|
3
3
|
*
|
|
4
4
|
* Creates adapters independently without dependencies
|
|
5
5
|
* StoreAdapter is pure storage - streaming handled by wiring layer
|
|
@@ -17,7 +17,7 @@ export interface AdapterSet {
|
|
|
17
17
|
store: StoreAdapter;
|
|
18
18
|
}
|
|
19
19
|
/**
|
|
20
|
-
* Create a complete set of adapters
|
|
20
|
+
* Create a complete set of adapters
|
|
21
21
|
* All adapters are independent - wiring layer handles coordination
|
|
22
22
|
*/
|
|
23
23
|
export declare function createAdapters(config: {
|