dsh-realtime 0.2.4 → 0.2.5
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/lib/journal.d.ts +16 -0
- package/lib/journal.d.ts.map +1 -1
- package/lib/journal.js +31 -0
- package/lib/journal.js.map +1 -1
- package/package.json +1 -1
- package/src/journal.ts +30 -0
package/lib/journal.d.ts
CHANGED
|
@@ -107,6 +107,7 @@ export declare class Journal {
|
|
|
107
107
|
private readonly capacity;
|
|
108
108
|
private readonly secrets;
|
|
109
109
|
private readonly entries;
|
|
110
|
+
private readonly sinks;
|
|
110
111
|
private nextSeq;
|
|
111
112
|
/**
|
|
112
113
|
* @param options - capacity and the values that must never be retained.
|
|
@@ -125,6 +126,21 @@ export declare class Journal {
|
|
|
125
126
|
* @param values - secrets to add; empty and duplicate values are ignored.
|
|
126
127
|
*/
|
|
127
128
|
addSecrets(values: readonly string[]): void;
|
|
129
|
+
/**
|
|
130
|
+
* Send every entry, as it is recorded, to somewhere else as well.
|
|
131
|
+
*
|
|
132
|
+
* The journal itself stays I/O-free on purpose — it is a plain object a test can construct and a route can
|
|
133
|
+
* serve — so persistence is a *sink* the caller supplies rather than something this class does. Additive,
|
|
134
|
+
* for the same reason `addSecrets` is: sinks arrive with the plugins that want them, and plugins load in no
|
|
135
|
+
* guaranteed order.
|
|
136
|
+
*
|
|
137
|
+
* **A sink cannot break the record.** It is called inside a guard, so a sink that throws — a full disk, a
|
|
138
|
+
* path that does not exist, a bug in the caller's own writer — costs the copy and never the entry. A
|
|
139
|
+
* diagnostic mechanism that can lose the diagnostic is worse than none, because it fails exactly when it is
|
|
140
|
+
* needed.
|
|
141
|
+
* @param sink - called with each entry, oldest to newest, in the order they were recorded.
|
|
142
|
+
*/
|
|
143
|
+
onEntry(sink: (entry: JournalEntry) => void): void;
|
|
128
144
|
/**
|
|
129
145
|
* Redact and retain one entry, evicting the oldest when the buffer is full.
|
|
130
146
|
* @param kind - what happened.
|
package/lib/journal.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"journal.d.ts","sourceRoot":"","sources":["../src/journal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAIH,6DAA6D;AAC7D,eAAO,MAAM,wBAAwB,MAAM,CAAA;AAE3C;;;;;GAKG;AACH,MAAM,MAAM,WAAW;AACrB,6DAA6D;AAC3D,gBAAgB,GAAG,gBAAgB,GAAG,gBAAgB;AACxD;;;;;;GAMG;GACD,iBAAiB,GAAG,iBAAiB,GAAG,eAAe;AACzD,0EAA0E;GACxE,iBAAiB;AACnB,6FAA6F;GAC3F,iBAAiB,GAAG,gBAAgB,GAAG,iBAAiB,GAAG,gBAAgB;AAC7E,iCAAiC;GAC/B,iBAAiB;AACnB,qFAAqF;GACnF,qBAAqB;AACvB,kGAAkG;GAChG,kBAAkB;AACpB,+EAA+E;GAC7E,aAAa;AACf,6FAA6F;GAC3F,iBAAiB;AACnB;;;;;;;GAOG;GACD,gBAAgB,CAAA;AAEpB,0BAA0B;AAC1B,MAAM,WAAW,YAAY;IAC3B;;;;;OAKG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,oEAAoE;IACpE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,qBAAqB;IACrB,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAA;IAC1B,uFAAuF;IACvF,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;CAClD;AAED,oCAAoC;AACpC,MAAM,WAAW,cAAc;IAC7B,qDAAqD;IACrD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAC5B;AAED;;;;;;GAMG;AACH,qBAAa,OAAO;IAClB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAQ;IACjC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAU;IAClC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;IAC7C,OAAO,CAAC,OAAO,CAAI;IAEnB;;;;OAIG;IACH,YAAY,OAAO,GAAE,cAAmB,EAOvC;IAED;;;;;;;;;OASG;IACH,UAAU,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAK1C;IAED;;;;;;OAMG;IACH,MAAM,CAAC,IAAI,EAAE,WAAW,EAAE,MAAM,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAM,EAAE,EAAE,GAAE,MAAmB,GAAG,YAAY,
|
|
1
|
+
{"version":3,"file":"journal.d.ts","sourceRoot":"","sources":["../src/journal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAIH,6DAA6D;AAC7D,eAAO,MAAM,wBAAwB,MAAM,CAAA;AAE3C;;;;;GAKG;AACH,MAAM,MAAM,WAAW;AACrB,6DAA6D;AAC3D,gBAAgB,GAAG,gBAAgB,GAAG,gBAAgB;AACxD;;;;;;GAMG;GACD,iBAAiB,GAAG,iBAAiB,GAAG,eAAe;AACzD,0EAA0E;GACxE,iBAAiB;AACnB,6FAA6F;GAC3F,iBAAiB,GAAG,gBAAgB,GAAG,iBAAiB,GAAG,gBAAgB;AAC7E,iCAAiC;GAC/B,iBAAiB;AACnB,qFAAqF;GACnF,qBAAqB;AACvB,kGAAkG;GAChG,kBAAkB;AACpB,+EAA+E;GAC7E,aAAa;AACf,6FAA6F;GAC3F,iBAAiB;AACnB;;;;;;;GAOG;GACD,gBAAgB,CAAA;AAEpB,0BAA0B;AAC1B,MAAM,WAAW,YAAY;IAC3B;;;;;OAKG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,oEAAoE;IACpE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,qBAAqB;IACrB,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAA;IAC1B,uFAAuF;IACvF,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;CAClD;AAED,oCAAoC;AACpC,MAAM,WAAW,cAAc;IAC7B,qDAAqD;IACrD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAC5B;AAED;;;;;;GAMG;AACH,qBAAa,OAAO;IAClB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAQ;IACjC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAU;IAClC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;IAC7C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAwC;IAC9D,OAAO,CAAC,OAAO,CAAI;IAEnB;;;;OAIG;IACH,YAAY,OAAO,GAAE,cAAmB,EAOvC;IAED;;;;;;;;;OASG;IACH,UAAU,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAK1C;IAED;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,GAAG,IAAI,CAGjD;IAED;;;;;;OAMG;IACH,MAAM,CAAC,IAAI,EAAE,WAAW,EAAE,MAAM,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAM,EAAE,EAAE,GAAE,MAAmB,GAAG,YAAY,CA0B9G;IAED;;;;;;OAMG;IACH,QAAQ,IAAI,YAAY,EAAE,CAEzB;IAED,kCAAkC;IAClC,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,+EAA+E;IAC/E,IAAI,SAAS,IAAI,MAAM,GAAG,SAAS,CAElC;CACF"}
|
package/lib/journal.js
CHANGED
|
@@ -38,6 +38,7 @@ export class Journal {
|
|
|
38
38
|
capacity;
|
|
39
39
|
secrets;
|
|
40
40
|
entries = [];
|
|
41
|
+
sinks = [];
|
|
41
42
|
nextSeq = 1;
|
|
42
43
|
/**
|
|
43
44
|
* @param options - capacity and the values that must never be retained.
|
|
@@ -70,6 +71,25 @@ export class Journal {
|
|
|
70
71
|
this.secrets.push(value);
|
|
71
72
|
}
|
|
72
73
|
}
|
|
74
|
+
/**
|
|
75
|
+
* Send every entry, as it is recorded, to somewhere else as well.
|
|
76
|
+
*
|
|
77
|
+
* The journal itself stays I/O-free on purpose — it is a plain object a test can construct and a route can
|
|
78
|
+
* serve — so persistence is a *sink* the caller supplies rather than something this class does. Additive,
|
|
79
|
+
* for the same reason `addSecrets` is: sinks arrive with the plugins that want them, and plugins load in no
|
|
80
|
+
* guaranteed order.
|
|
81
|
+
*
|
|
82
|
+
* **A sink cannot break the record.** It is called inside a guard, so a sink that throws — a full disk, a
|
|
83
|
+
* path that does not exist, a bug in the caller's own writer — costs the copy and never the entry. A
|
|
84
|
+
* diagnostic mechanism that can lose the diagnostic is worse than none, because it fails exactly when it is
|
|
85
|
+
* needed.
|
|
86
|
+
* @param sink - called with each entry, oldest to newest, in the order they were recorded.
|
|
87
|
+
*/
|
|
88
|
+
onEntry(sink) {
|
|
89
|
+
if (typeof sink !== 'function')
|
|
90
|
+
return;
|
|
91
|
+
this.sinks.push(sink);
|
|
92
|
+
}
|
|
73
93
|
/**
|
|
74
94
|
* Redact and retain one entry, evicting the oldest when the buffer is full.
|
|
75
95
|
* @param kind - what happened.
|
|
@@ -93,6 +113,17 @@ export class Journal {
|
|
|
93
113
|
// the memory ceiling is a property of the type rather than of the caller's discipline.
|
|
94
114
|
while (this.entries.length > this.capacity)
|
|
95
115
|
this.entries.shift();
|
|
116
|
+
for (const sink of this.sinks) {
|
|
117
|
+
try {
|
|
118
|
+
sink(entry);
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
// Deliberately swallowed: see `onEntry`. The entry is already retained, so a broken sink costs the
|
|
122
|
+
// copy and not the record — and re-throwing here would let a diagnostics sink take down the path it
|
|
123
|
+
// was added to illuminate. This guard is proven able to fail: remove it and `journal-sink.spec.ts`
|
|
124
|
+
// reports a lost entry.
|
|
125
|
+
}
|
|
126
|
+
}
|
|
96
127
|
return entry;
|
|
97
128
|
}
|
|
98
129
|
/**
|
package/lib/journal.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"journal.js","sourceRoot":"","sources":["../src/journal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEpC,6DAA6D;AAC7D,MAAM,CAAC,MAAM,wBAAwB,GAAG,GAAG,CAAA;AA2E3C;;;;;;GAMG;AACH,MAAM,OAAO,OAAO;IACD,QAAQ,CAAQ;IAChB,OAAO,CAAU;IACjB,OAAO,GAAmB,EAAE,CAAA;
|
|
1
|
+
{"version":3,"file":"journal.js","sourceRoot":"","sources":["../src/journal.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/journal.ts
CHANGED
|
@@ -114,6 +114,7 @@ export class Journal {
|
|
|
114
114
|
private readonly capacity: number
|
|
115
115
|
private readonly secrets: string[]
|
|
116
116
|
private readonly entries: JournalEntry[] = []
|
|
117
|
+
private readonly sinks: ((entry: JournalEntry) => void)[] = []
|
|
117
118
|
private nextSeq = 1
|
|
118
119
|
|
|
119
120
|
/**
|
|
@@ -147,6 +148,25 @@ export class Journal {
|
|
|
147
148
|
}
|
|
148
149
|
}
|
|
149
150
|
|
|
151
|
+
/**
|
|
152
|
+
* Send every entry, as it is recorded, to somewhere else as well.
|
|
153
|
+
*
|
|
154
|
+
* The journal itself stays I/O-free on purpose — it is a plain object a test can construct and a route can
|
|
155
|
+
* serve — so persistence is a *sink* the caller supplies rather than something this class does. Additive,
|
|
156
|
+
* for the same reason `addSecrets` is: sinks arrive with the plugins that want them, and plugins load in no
|
|
157
|
+
* guaranteed order.
|
|
158
|
+
*
|
|
159
|
+
* **A sink cannot break the record.** It is called inside a guard, so a sink that throws — a full disk, a
|
|
160
|
+
* path that does not exist, a bug in the caller's own writer — costs the copy and never the entry. A
|
|
161
|
+
* diagnostic mechanism that can lose the diagnostic is worse than none, because it fails exactly when it is
|
|
162
|
+
* needed.
|
|
163
|
+
* @param sink - called with each entry, oldest to newest, in the order they were recorded.
|
|
164
|
+
*/
|
|
165
|
+
onEntry(sink: (entry: JournalEntry) => void): void {
|
|
166
|
+
if (typeof sink !== 'function') return
|
|
167
|
+
this.sinks.push(sink)
|
|
168
|
+
}
|
|
169
|
+
|
|
150
170
|
/**
|
|
151
171
|
* Redact and retain one entry, evicting the oldest when the buffer is full.
|
|
152
172
|
* @param kind - what happened.
|
|
@@ -169,6 +189,16 @@ export class Journal {
|
|
|
169
189
|
// `shift()` on a bounded array: the buffer is capped, so this stays O(capacity) at worst and
|
|
170
190
|
// the memory ceiling is a property of the type rather than of the caller's discipline.
|
|
171
191
|
while (this.entries.length > this.capacity) this.entries.shift()
|
|
192
|
+
for (const sink of this.sinks) {
|
|
193
|
+
try {
|
|
194
|
+
sink(entry)
|
|
195
|
+
} catch {
|
|
196
|
+
// Deliberately swallowed: see `onEntry`. The entry is already retained, so a broken sink costs the
|
|
197
|
+
// copy and not the record — and re-throwing here would let a diagnostics sink take down the path it
|
|
198
|
+
// was added to illuminate. This guard is proven able to fail: remove it and `journal-sink.spec.ts`
|
|
199
|
+
// reports a lost entry.
|
|
200
|
+
}
|
|
201
|
+
}
|
|
172
202
|
return entry
|
|
173
203
|
}
|
|
174
204
|
|