@enbox/dwn-sdk-js 0.3.0 → 0.3.2
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/browser.mjs +6 -6
- package/dist/browser.mjs.map +3 -3
- package/dist/esm/generated/precompiled-validators.js +704 -342
- package/dist/esm/generated/precompiled-validators.js.map +1 -1
- package/dist/esm/src/core/dwn-error.js +1 -0
- package/dist/esm/src/core/dwn-error.js.map +1 -1
- package/dist/esm/src/event-stream/event-emitter-event-log.js +152 -22
- package/dist/esm/src/event-stream/event-emitter-event-log.js.map +1 -1
- package/dist/esm/src/handlers/messages-subscribe.js +7 -0
- package/dist/esm/src/handlers/messages-subscribe.js.map +1 -1
- package/dist/esm/src/handlers/protocols-configure.js +1 -1
- package/dist/esm/src/handlers/protocols-configure.js.map +1 -1
- package/dist/esm/src/handlers/records-subscribe.js +7 -0
- package/dist/esm/src/handlers/records-subscribe.js.map +1 -1
- package/dist/esm/src/handlers/records-write.js +3 -2
- package/dist/esm/src/handlers/records-write.js.map +1 -1
- package/dist/esm/src/interfaces/messages-subscribe.js.map +1 -1
- package/dist/esm/src/interfaces/records-subscribe.js.map +1 -1
- package/dist/esm/src/store/storage-controller.js +1 -1
- package/dist/esm/src/store/storage-controller.js.map +1 -1
- package/dist/esm/src/utils/messages.js +41 -1
- package/dist/esm/src/utils/messages.js.map +1 -1
- package/dist/esm/tests/event-emitter-event-log.spec.js +278 -84
- package/dist/esm/tests/event-emitter-event-log.spec.js.map +1 -1
- package/dist/esm/tests/handlers/messages-subscribe.spec.js +288 -0
- package/dist/esm/tests/handlers/messages-subscribe.spec.js.map +1 -1
- package/dist/esm/tests/utils/test-data-generator.js.map +1 -1
- package/dist/types/generated/precompiled-validators.d.ts +50 -34
- package/dist/types/generated/precompiled-validators.d.ts.map +1 -1
- package/dist/types/src/core/dwn-error.d.ts +1 -0
- package/dist/types/src/core/dwn-error.d.ts.map +1 -1
- package/dist/types/src/event-stream/event-emitter-event-log.d.ts +34 -4
- package/dist/types/src/event-stream/event-emitter-event-log.d.ts.map +1 -1
- package/dist/types/src/handlers/messages-subscribe.d.ts +1 -1
- package/dist/types/src/handlers/messages-subscribe.d.ts.map +1 -1
- package/dist/types/src/handlers/records-subscribe.d.ts +1 -1
- package/dist/types/src/handlers/records-subscribe.d.ts.map +1 -1
- package/dist/types/src/handlers/records-write.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +1 -1
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/interfaces/messages-subscribe.d.ts +4 -3
- package/dist/types/src/interfaces/messages-subscribe.d.ts.map +1 -1
- package/dist/types/src/interfaces/records-subscribe.d.ts +4 -3
- package/dist/types/src/interfaces/records-subscribe.d.ts.map +1 -1
- package/dist/types/src/types/messages-types.d.ts +14 -4
- package/dist/types/src/types/messages-types.d.ts.map +1 -1
- package/dist/types/src/types/records-types.d.ts +8 -4
- package/dist/types/src/types/records-types.d.ts.map +1 -1
- package/dist/types/src/types/subscriptions.d.ts +80 -20
- package/dist/types/src/types/subscriptions.d.ts.map +1 -1
- package/dist/types/src/utils/messages.d.ts.map +1 -1
- package/dist/types/tests/handlers/messages-subscribe.spec.d.ts.map +1 -1
- package/dist/types/tests/utils/test-data-generator.d.ts +5 -4
- package/dist/types/tests/utils/test-data-generator.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/core/dwn-error.ts +1 -0
- package/src/event-stream/event-emitter-event-log.ts +174 -27
- package/src/handlers/messages-subscribe.ts +8 -1
- package/src/handlers/protocols-configure.ts +1 -1
- package/src/handlers/records-subscribe.ts +8 -1
- package/src/handlers/records-write.ts +3 -2
- package/src/index.ts +1 -1
- package/src/interfaces/messages-subscribe.ts +4 -3
- package/src/interfaces/records-subscribe.ts +4 -3
- package/src/store/storage-controller.ts +1 -1
- package/src/types/messages-types.ts +12 -4
- package/src/types/records-types.ts +6 -4
- package/src/types/subscriptions.ts +86 -20
- package/src/utils/messages.ts +47 -1
|
@@ -2,6 +2,54 @@ import type { RecordsWriteMessage } from './records-types.js';
|
|
|
2
2
|
import type { Filter, KeyValues } from './query-types.js';
|
|
3
3
|
import type { GenericMessage, GenericMessageReply, MessageSubscription } from './message-types.js';
|
|
4
4
|
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
// ProgressToken — structured cursor for EventLog replay/resume
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Structured cursor for EventLog replay and resume. Replaces the previous
|
|
11
|
+
* opaque `string` cursor to provide explicit ordering semantics required
|
|
12
|
+
* by causal frontier progression in multi-master sync.
|
|
13
|
+
*
|
|
14
|
+
* Comparisons are valid only when `streamId` and `epoch` are equal.
|
|
15
|
+
* `position` is compared numerically (BigInt-safe), never lexicographically.
|
|
16
|
+
* Progress tokens are source-local: they MUST NOT be reused across different
|
|
17
|
+
* remote providers or EventLog instances.
|
|
18
|
+
*/
|
|
19
|
+
export type ProgressToken = {
|
|
20
|
+
/** Stable identity of the source event stream domain. */
|
|
21
|
+
streamId : string;
|
|
22
|
+
/** Stream generation/version; changes on non-compatible reset. */
|
|
23
|
+
epoch : string;
|
|
24
|
+
/** Monotonic decimal string within `(streamId, epoch)`. Compared numerically. */
|
|
25
|
+
position : string;
|
|
26
|
+
/** The CID of the message associated with this event. */
|
|
27
|
+
messageCid : string;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Reason code for a {@link ProgressGapInfo} — explains why the cursor
|
|
32
|
+
* cannot be resumed.
|
|
33
|
+
*/
|
|
34
|
+
export type ProgressGapReason = 'token_too_old' | 'epoch_mismatch' | 'stream_mismatch';
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Metadata attached to a `DwnError(DwnErrorCode.EventLogProgressGap, ...)`
|
|
38
|
+
* when an EventLog implementation cannot resume from a given cursor.
|
|
39
|
+
*
|
|
40
|
+
* Subscribe handlers translate this into a 410 response.
|
|
41
|
+
*/
|
|
42
|
+
export type ProgressGapInfo = {
|
|
43
|
+
/** The cursor the consumer requested. */
|
|
44
|
+
requested : ProgressToken;
|
|
45
|
+
/** The oldest token still available for replay. */
|
|
46
|
+
oldestAvailable : ProgressToken;
|
|
47
|
+
/** The latest token available. */
|
|
48
|
+
latestAvailable : ProgressToken;
|
|
49
|
+
/** Why the cursor is no longer valid. */
|
|
50
|
+
reason : ProgressGapReason;
|
|
51
|
+
};
|
|
52
|
+
|
|
5
53
|
/**
|
|
6
54
|
* Internal listener type used by {@link EventLog.emit} to notify in-process
|
|
7
55
|
* subscribers. Not intended for direct consumer use — consumers should use
|
|
@@ -32,17 +80,16 @@ export type SubscriptionReply = GenericMessageReply & {
|
|
|
32
80
|
// ---------------------------------------------------------------------------
|
|
33
81
|
|
|
34
82
|
/**
|
|
35
|
-
* A regular subscription event carrying a message and its EventLog
|
|
83
|
+
* A regular subscription event carrying a message and its EventLog progress token.
|
|
36
84
|
*/
|
|
37
85
|
export type SubscriptionEvent = {
|
|
38
86
|
type : 'event';
|
|
39
87
|
/**
|
|
40
|
-
*
|
|
41
|
-
* persist this value and pass it back to `subscribe()` or `read()` to
|
|
42
|
-
* from this point.
|
|
43
|
-
* for in-memory, Redis stream ID, NATS stream sequence, etc.).
|
|
88
|
+
* Structured progress token assigned by the EventLog implementation. Clients
|
|
89
|
+
* should persist this value and pass it back to `subscribe()` or `read()` to
|
|
90
|
+
* resume from this point.
|
|
44
91
|
*/
|
|
45
|
-
cursor :
|
|
92
|
+
cursor : ProgressToken;
|
|
46
93
|
/** The event payload (message + optional initialWrite). */
|
|
47
94
|
event : MessageEvent;
|
|
48
95
|
};
|
|
@@ -57,10 +104,10 @@ export type SubscriptionEvent = {
|
|
|
57
104
|
export type SubscriptionEose = {
|
|
58
105
|
type : 'eose';
|
|
59
106
|
/**
|
|
60
|
-
*
|
|
107
|
+
* Progress token of the last stored event that was replayed.
|
|
61
108
|
* Echoes the input cursor when no stored events matched (i.e. already caught up).
|
|
62
109
|
*/
|
|
63
|
-
cursor :
|
|
110
|
+
cursor : ProgressToken;
|
|
64
111
|
};
|
|
65
112
|
|
|
66
113
|
/**
|
|
@@ -80,15 +127,15 @@ export type SubscriptionListener = (message: SubscriptionMessage) => void;
|
|
|
80
127
|
*/
|
|
81
128
|
export type EventLogSubscribeOptions = {
|
|
82
129
|
/**
|
|
83
|
-
*
|
|
130
|
+
* Progress token to resume from (exclusive — events after this position
|
|
84
131
|
* are replayed). When provided, stored events are replayed first, followed by
|
|
85
132
|
* an EOSE marker, then live events. When omitted, only live events are delivered.
|
|
86
133
|
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
134
|
+
* Tokens must be obtained from a prior interaction with the same EventLog
|
|
135
|
+
* instance (e.g. `SubscriptionEvent.cursor`, `EventLogReadResult.cursor`,
|
|
136
|
+
* or the return value of `emit()`).
|
|
90
137
|
*/
|
|
91
|
-
cursor? :
|
|
138
|
+
cursor? : ProgressToken;
|
|
92
139
|
|
|
93
140
|
/**
|
|
94
141
|
* Filters evaluated against event indexes. Events must match at least one
|
|
@@ -113,14 +160,22 @@ export type EventLogEntry = {
|
|
|
113
160
|
|
|
114
161
|
/** Indexes associated with the event (used for filter matching). */
|
|
115
162
|
indexes: KeyValues;
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* The CID of the message that produced this event. Populated by
|
|
166
|
+
* implementations that track it (e.g. {@link EventEmitterEventLog}).
|
|
167
|
+
* Consumers should fall back to computing the CID from the message
|
|
168
|
+
* if this is absent.
|
|
169
|
+
*/
|
|
170
|
+
messageCid?: string;
|
|
116
171
|
};
|
|
117
172
|
|
|
118
173
|
/**
|
|
119
174
|
* Options accepted by {@link EventLog.read}.
|
|
120
175
|
*/
|
|
121
176
|
export type EventLogReadOptions = {
|
|
122
|
-
/**
|
|
123
|
-
cursor? :
|
|
177
|
+
/** Progress token to resume from (exclusive — returns events after this position). */
|
|
178
|
+
cursor? : ProgressToken;
|
|
124
179
|
|
|
125
180
|
/** Maximum number of events to return. */
|
|
126
181
|
limit? : number;
|
|
@@ -137,14 +192,14 @@ export type EventLogReadResult = {
|
|
|
137
192
|
events : EventLogEntry[];
|
|
138
193
|
|
|
139
194
|
/**
|
|
140
|
-
*
|
|
195
|
+
* Progress token for resuming subsequent reads or subscriptions.
|
|
141
196
|
*
|
|
142
|
-
* - When events are returned:
|
|
197
|
+
* - When events are returned: token of the last event.
|
|
143
198
|
* - When no events are returned but a cursor was provided: the input cursor
|
|
144
199
|
* (meaning "you are caught up, nothing new since this point").
|
|
145
200
|
* - When no events exist and no cursor was provided: `undefined`.
|
|
146
201
|
*/
|
|
147
|
-
cursor? :
|
|
202
|
+
cursor? : ProgressToken;
|
|
148
203
|
};
|
|
149
204
|
|
|
150
205
|
/**
|
|
@@ -162,9 +217,13 @@ export type EventLogReadResult = {
|
|
|
162
217
|
export interface EventLog {
|
|
163
218
|
/**
|
|
164
219
|
* Persist an event and notify in-process subscribers.
|
|
165
|
-
* @
|
|
220
|
+
* @param tenant The tenant DID.
|
|
221
|
+
* @param event The event payload.
|
|
222
|
+
* @param indexes Index values for the event.
|
|
223
|
+
* @param messageCid The CID of the message being emitted — embedded in the returned token.
|
|
224
|
+
* @returns A {@link ProgressToken} assigned to the event, or `undefined` on failure.
|
|
166
225
|
*/
|
|
167
|
-
emit(tenant: string, event: MessageEvent, indexes: KeyValues): Promise<
|
|
226
|
+
emit(tenant: string, event: MessageEvent, indexes: KeyValues, messageCid: string): Promise<ProgressToken | undefined>;
|
|
168
227
|
|
|
169
228
|
/**
|
|
170
229
|
* Read events from the log starting after `cursor`, optionally filtered.
|
|
@@ -188,6 +247,13 @@ export interface EventLog {
|
|
|
188
247
|
*/
|
|
189
248
|
subscribe(tenant: string, id: string, listener: SubscriptionListener, options?: EventLogSubscribeOptions): Promise<EventSubscription>;
|
|
190
249
|
|
|
250
|
+
/**
|
|
251
|
+
* Returns the oldest and latest available progress tokens for a tenant,
|
|
252
|
+
* or `undefined` if the tenant has no events. Used to construct
|
|
253
|
+
* `ProgressGap` metadata when a consumer's cursor is no longer replayable.
|
|
254
|
+
*/
|
|
255
|
+
getReplayBounds(tenant: string): Promise<{ oldest: ProgressToken; latest: ProgressToken } | undefined>;
|
|
256
|
+
|
|
191
257
|
/**
|
|
192
258
|
* Delete events older than the given sequence number or ISO-8601 timestamp.
|
|
193
259
|
*/
|
package/src/utils/messages.ts
CHANGED
|
@@ -67,6 +67,29 @@ export class Messages {
|
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
messagesQueryFilters.push(this.convertFilter(filter));
|
|
70
|
+
|
|
71
|
+
// When protocolPathPrefix is used with a protocol, inject a shadow filter
|
|
72
|
+
// for ProtocolsConfigure events. Without this, protocol metadata updates
|
|
73
|
+
// would be excluded (ProtocolsConfigure indexes have no protocolPath).
|
|
74
|
+
// This mirrors the existing core-protocol additional-filter pattern above.
|
|
75
|
+
// The messageTimestamp constraint is carried over so time-bounded queries
|
|
76
|
+
// (including cursor-based subscriptions) also apply to the shadow filter.
|
|
77
|
+
if ((filter.protocolPathPrefix !== undefined || filter.contextIdPrefix !== undefined) && filter.protocol !== undefined) {
|
|
78
|
+
const metadataFilter: Filter = {
|
|
79
|
+
interface : 'Protocols',
|
|
80
|
+
method : 'Configure',
|
|
81
|
+
protocol : filter.protocol,
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
if (filter.messageTimestamp !== undefined) {
|
|
85
|
+
const timestampFilter = FilterUtility.convertRangeCriterion(filter.messageTimestamp);
|
|
86
|
+
if (timestampFilter) {
|
|
87
|
+
metadataFilter.messageTimestamp = timestampFilter;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
messagesQueryFilters.push(metadataFilter);
|
|
92
|
+
}
|
|
70
93
|
}
|
|
71
94
|
|
|
72
95
|
return messagesQueryFilters;
|
|
@@ -78,12 +101,35 @@ export class Messages {
|
|
|
78
101
|
private static convertFilter(filter: MessagesFilter): Filter {
|
|
79
102
|
const filterCopy = { ...filter } as Filter;
|
|
80
103
|
|
|
81
|
-
const { messageTimestamp } = filter;
|
|
104
|
+
const { messageTimestamp, protocolPathPrefix, contextIdPrefix } = filter;
|
|
82
105
|
const messageTimestampFilter = messageTimestamp ? FilterUtility.convertRangeCriterion(messageTimestamp) : undefined;
|
|
83
106
|
if (messageTimestampFilter) {
|
|
84
107
|
filterCopy.messageTimestamp = messageTimestampFilter;
|
|
85
108
|
delete filterCopy.dateUpdated;
|
|
86
109
|
}
|
|
110
|
+
|
|
111
|
+
// Convert protocolPathPrefix into a protocolPath range filter.
|
|
112
|
+
// The range gte: prefix, lt: prefix + '/\uffff' matches:
|
|
113
|
+
// - exact: 'post' (prefix itself)
|
|
114
|
+
// - children: 'post/attachment', 'post/comment', etc.
|
|
115
|
+
// - NOT siblings: 'poster', 'postfix' (excluded because '/' < any alphanumeric)
|
|
116
|
+
if (protocolPathPrefix !== undefined) {
|
|
117
|
+
delete (filterCopy as any).protocolPathPrefix;
|
|
118
|
+
filterCopy.protocolPath = {
|
|
119
|
+
gte : protocolPathPrefix,
|
|
120
|
+
lt : protocolPathPrefix + '/\uffff',
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Convert contextIdPrefix into a contextId range filter (same pattern).
|
|
125
|
+
if (contextIdPrefix !== undefined) {
|
|
126
|
+
delete (filterCopy as any).contextIdPrefix;
|
|
127
|
+
filterCopy.contextId = {
|
|
128
|
+
gte : contextIdPrefix,
|
|
129
|
+
lt : contextIdPrefix + '/\uffff',
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
87
133
|
return filterCopy as Filter;
|
|
88
134
|
}
|
|
89
135
|
}
|