@event-driven-io/emmett-expressjs 0.20.2-alpha.4 → 0.20.2-alpha.6

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/index.cjs CHANGED
@@ -1,494 +1,7 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2; var _class3;// src/index.ts
2
- require('express-async-errors');
3
-
4
- // src/application.ts
5
- var _express = require('express'); var _express2 = _interopRequireDefault(_express);
6
-
7
- var _http = require('http'); var _http2 = _interopRequireDefault(_http);
8
-
9
- // ../emmett/dist/chunk-3XBWME34.js
10
- var a = ((n) => (n.NOT_A_NONEMPTY_STRING = "NOT_A_NONEMPTY_STRING", n.NOT_A_POSITIVE_NUMBER = "NOT_A_POSITIVE_NUMBER", n.NOT_AN_UNSIGNED_BIGINT = "NOT_AN_UNSIGNED_BIGINT", n))(a || {});
11
- var s = (t) => typeof t == "number" && t === t;
12
-
13
- // ../emmett/dist/index.js
14
- var _uuid = require('uuid');
15
- var _webstreamspolyfill = require('web-streams-polyfill');
16
-
17
-
18
-
19
- var _asyncretry = require('async-retry'); var _asyncretry2 = _interopRequireDefault(_asyncretry);
20
-
21
-
22
-
23
-
24
-
25
-
26
-
27
-
28
-
29
-
30
-
31
- var O = (t, e = {}) => new S(t, e);
32
- var S = (_class = class extends _webstreamspolyfill.TransformStream {
33
- constructor(r2, n = {}) {
34
- super({ cancel: (o2) => {
35
- console.log("Stream was canceled. Reason:", o2), this.stopChecking();
36
- } });_class.prototype.__init.call(this);_class.prototype.__init2.call(this);;
37
- this.onNoActiveReaderCallback = r2;
38
- this.streamId = _nullishCoalesce(_optionalChain([n, 'optionalAccess', _ => _.streamId]), () => ( _uuid.v4.call(void 0, ))), this.onNoActiveReaderCallback = r2, this.startChecking(_nullishCoalesce(_optionalChain([n, 'optionalAccess', _2 => _2.intervalCheckInMs]), () => ( 20)));
39
- }
40
- __init() {this.checkInterval = null}
41
-
42
- __init2() {this._isStopped = false}
43
- get hasActiveSubscribers() {
44
- return !this._isStopped;
45
- }
46
- startChecking(r2) {
47
- this.checkInterval = setInterval(() => {
48
- this.checkNoActiveReader();
49
- }, r2);
50
- }
51
- stopChecking() {
52
- this.checkInterval && (clearInterval(this.checkInterval), this.checkInterval = null, this._isStopped = true, this.onNoActiveReaderCallback(this));
53
- }
54
- checkNoActiveReader() {
55
- !this.readable.locked && !this._isStopped && this.stopChecking();
56
- }
57
- }, _class);
58
- var w2 = async (t, e) => e === void 0 || e.retries === 0 ? t() : _asyncretry2.default.call(void 0, async (r2) => {
59
- try {
60
- return await t();
61
- } catch (n) {
62
- throw _optionalChain([e, 'optionalAccess', _3 => _3.shouldRetryError]) && !e.shouldRetryError(n) && r2(n), n;
63
- }
64
- }, _nullishCoalesce(e, () => ( { retries: 0 })));
65
- var W = class extends Error {
66
- constructor(e) {
67
- super(`Cannot parse! ${e}`);
68
- }
69
- };
70
- var c2 = { stringify: (t, e) => JSON.stringify(_optionalChain([e, 'optionalAccess', _4 => _4.map]) ? e.map(t) : t, (r2, n) => typeof n == "bigint" ? n.toString() : n), parse: (t, e) => {
71
- let r2 = JSON.parse(t, _optionalChain([e, 'optionalAccess', _5 => _5.reviver]));
72
- if (_optionalChain([e, 'optionalAccess', _6 => _6.typeCheck]) && !_optionalChain([e, 'optionalAccess', _7 => _7.typeCheck, 'call', _8 => _8(r2)])) throw new W(t);
73
- return _optionalChain([e, 'optionalAccess', _9 => _9.map]) ? e.map(r2) : r2;
74
- } };
75
- var ae = (t) => new (0, _webstreamspolyfill.TransformStream)({ transform(e, r2) {
76
- t(e) && r2.enqueue(e);
77
- } });
78
- var se = (t) => new (0, _webstreamspolyfill.TransformStream)({ transform(e, r2) {
79
- r2.enqueue(t(e));
80
- } });
81
- var ie = (t, e) => new g2(t, e);
82
- var g2 = class extends _webstreamspolyfill.TransformStream {
83
-
84
-
85
- constructor(e, r2) {
86
- super({ transform: (n) => {
87
- this.accumulator = this.reducer(this.accumulator, n);
88
- }, flush: (n) => {
89
- n.enqueue(this.accumulator), n.terminate();
90
- } }), this.accumulator = r2, this.reducer = e;
91
- }
92
- };
93
- var me = (t, e, r2 = { forever: true, minTimeout: 25 }) => new (0, _webstreamspolyfill.TransformStream)({ start(n) {
94
- w2(() => Ne(t, e, n), r2).catch((o2) => {
95
- n.error(o2);
96
- });
97
- } });
98
- var Ne = async (t, e, r2) => {
99
- let o2 = t().getReader();
100
- try {
101
- let a2;
102
- do {
103
- let i = await o2.read();
104
- a2 = i.done, await e(i, r2), a2 && r2.terminate();
105
- } while (!a2);
106
- } finally {
107
- o2.releaseLock();
108
- }
109
- };
110
- var de = (t) => new h(t);
111
- var h = (_class2 = class extends _webstreamspolyfill.TransformStream {
112
- __init3() {this.count = 0}
113
-
114
- constructor(e) {
115
- super({ transform: (r2, n) => {
116
- this.count++, this.count > this.skip && n.enqueue(r2);
117
- } });_class2.prototype.__init3.call(this);, this.skip = e;
118
- }
119
- }, _class2);
120
- var pe = (t) => new (0, _webstreamspolyfill.TransformStream)({ transform(e, r2) {
121
- r2.enqueue(e), t(e) && r2.terminate();
122
- } });
123
- var le = (t) => new (0, _webstreamspolyfill.TransformStream)({ async transform(e, r2) {
124
- if (!t(e)) {
125
- r2.enqueue(e);
126
- return;
127
- }
128
- await Promise.resolve(), r2.terminate();
129
- } });
130
- var ce = (t) => new C(t);
131
- var C = (_class3 = class extends _webstreamspolyfill.TransformStream {
132
- __init4() {this.count = 0}
133
-
134
- constructor(e) {
135
- super({ transform: (r2, n) => {
136
- this.count < this.limit ? (this.count++, n.enqueue(r2)) : n.terminate();
137
- } });_class3.prototype.__init4.call(this);, this.limit = e;
138
- }
139
- }, _class3);
140
- var ue = (t) => new (0, _webstreamspolyfill.TransformStream)({ start(e) {
141
- let r2 = setTimeout(() => {
142
- e.terminate();
143
- }, t), n = e.terminate.bind(e);
144
- e.terminate = () => {
145
- clearTimeout(r2), n();
146
- };
147
- }, transform(e, r2) {
148
- r2.enqueue(e);
149
- } });
150
- var fe = { filter: ae, take: ce, TakeTransformStream: C, skip: de, SkipTransformStream: h, map: se, notifyAboutNoActiveReadersStream: O, NotifyAboutNoActiveReadersStream: S, reduce: ie, ReduceTransformStream: g2, retry: me, stopAfter: pe, stopOn: le, waitAtMost: ue };
151
- var { retry: Be } = fe;
152
- var p2 = class extends Error {
153
- constructor(e) {
154
- super(e);
155
- }
156
- };
157
- var ye = (t, e) => {
158
- let r2 = t, n = e;
159
- return U(r2), U(n), Object.keys(n).every((o2) => typeof n[o2] == "object" ? ye(r2[o2], n[o2]) : n[o2] === r2[o2]);
160
- };
161
- var po = (t) => {
162
- throw new p2(_nullishCoalesce(t, () => ( "That should not ever happened, right?")));
163
- };
164
- var ve = (t, e, r2) => {
165
- if (!ye(t, e)) throw new p2(_nullishCoalesce(r2, () => ( `subObj:
166
- ${c2.stringify(e)}
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2; var _class3;require('express-async-errors');var _express = require('express'); var _express2 = _interopRequireDefault(_express);var _http = require('http'); var _http2 = _interopRequireDefault(_http);var w=(e=>(e.NOT_A_NONEMPTY_STRING="NOT_A_NONEMPTY_STRING",e.NOT_A_POSITIVE_NUMBER="NOT_A_POSITIVE_NUMBER",e.NOT_AN_UNSIGNED_BIGINT="NOT_AN_UNSIGNED_BIGINT",e))(w||{}),y=e=>typeof e=="number"&&e===e;var _uuid = require('uuid');var _webstreamspolyfill = require('web-streams-polyfill');var _asyncretry = require('async-retry'); var _asyncretry2 = _interopRequireDefault(_asyncretry);var F=(e,t={})=>new x(e,t),x= (_class =class extends _webstreamspolyfill.TransformStream{constructor(e,t={}){super({cancel:r=>{console.log("Stream was canceled. Reason:",r),this.stopChecking()}});_class.prototype.__init.call(this);_class.prototype.__init2.call(this);,this.onNoActiveReaderCallback=e,this.streamId=_nullishCoalesce(_optionalChain([t, 'optionalAccess', _2 => _2.streamId]), () => (_uuid.v4.call(void 0, ))),this.onNoActiveReaderCallback=e,this.startChecking(_nullishCoalesce(_optionalChain([t, 'optionalAccess', _3 => _3.intervalCheckInMs]), () => (20)))}__init() {this.checkInterval=null}__init2() {this._isStopped=!1}get hasActiveSubscribers(){return!this._isStopped}startChecking(e){this.checkInterval=setInterval(()=>{this.checkNoActiveReader()},e)}stopChecking(){this.checkInterval&&(clearInterval(this.checkInterval),this.checkInterval=null,this._isStopped=!0,this.onNoActiveReaderCallback(this))}checkNoActiveReader(){!this.readable.locked&&!this._isStopped&&this.stopChecking()}}, _class);var B=async(e,t)=>t===void 0||t.retries===0?e():_asyncretry2.default.call(void 0, async r=>{try{return await e()}catch(s){throw _optionalChain([t, 'optionalAccess', _4 => _4.shouldRetryError])&&!t.shouldRetryError(s)&&r(s),s}},_nullishCoalesce(t, () => ({retries:0})));var $=class extends Error{constructor(e){super(`Cannot parse! ${e}`)}},u={stringify:(e,t)=>JSON.stringify(_optionalChain([t, 'optionalAccess', _5 => _5.map])?t.map(e):e,(r,s)=>typeof s=="bigint"?s.toString():s),parse:(e,t)=>{let r=JSON.parse(e,_optionalChain([t, 'optionalAccess', _6 => _6.reviver]));if(_optionalChain([t, 'optionalAccess', _7 => _7.typeCheck])&&!_optionalChain([t, 'optionalAccess', _8 => _8.typeCheck, 'call', _9 => _9(r)]))throw new $(e);return _optionalChain([t, 'optionalAccess', _10 => _10.map])?t.map(r):r}};var G=e=>new (0, _webstreamspolyfill.TransformStream)({transform(t,r){e(t)&&r.enqueue(t)}}),Y=e=>new (0, _webstreamspolyfill.TransformStream)({transform(t,r){r.enqueue(e(t))}}),K=(e,t)=>new A(e,t),A=class extends _webstreamspolyfill.TransformStream{constructor(e,t){super({transform:r=>{this.accumulator=this.reducer(this.accumulator,r)},flush:r=>{r.enqueue(this.accumulator),r.terminate()}}),this.accumulator=t,this.reducer=e}},X=(e,t,r={forever:!0,minTimeout:25})=>new (0, _webstreamspolyfill.TransformStream)({start(s){B(()=>Z(e,t,s),r).catch(o=>{s.error(o)})}}),Z=async(e,t,r)=>{let s=e().getReader();try{let o;do{let n=await s.read();o=n.done,await t(n,r),o&&r.terminate()}while(!o)}finally{s.releaseLock()}},Q=e=>new O(e),O= (_class2 =class extends _webstreamspolyfill.TransformStream{__init3() {this.count=0}constructor(e){super({transform:(t,r)=>{this.count++,this.count>this.skip&&r.enqueue(t)}});_class2.prototype.__init3.call(this);,this.skip=e}}, _class2),te=e=>new (0, _webstreamspolyfill.TransformStream)({transform(t,r){r.enqueue(t),e(t)&&r.terminate()}}),se=e=>new (0, _webstreamspolyfill.TransformStream)({async transform(t,r){if(!e(t)){r.enqueue(t);return}await Promise.resolve(),r.terminate()}}),ne=e=>new C(e),C= (_class3 =class extends _webstreamspolyfill.TransformStream{__init4() {this.count=0}constructor(e){super({transform:(t,r)=>{this.count<this.limit?(this.count++,r.enqueue(t)):r.terminate()}});_class3.prototype.__init4.call(this);,this.limit=e}}, _class3),ie=e=>new (0, _webstreamspolyfill.TransformStream)({start(t){let r=setTimeout(()=>{t.terminate()},e),s=t.terminate.bind(t);t.terminate=()=>{clearTimeout(r),s()}},transform(t,r){r.enqueue(t)}}),pe={filter:G,take:ne,TakeTransformStream:C,skip:Q,SkipTransformStream:O,map:Y,notifyAboutNoActiveReadersStream:F,NotifyAboutNoActiveReadersStream:x,reduce:K,ReduceTransformStream:A,retry:X,stopAfter:te,stopOn:se,waitAtMost:ie},{retry:pt}=pe;var d=class extends Error{constructor(e){super(e)}},N=(e,t)=>{let r=e,s=t;return R(r),R(s),Object.keys(s).every(o=>typeof s[o]=="object"?N(r[o],s[o]):s[o]===r[o])},E=e=>{throw new d(_nullishCoalesce(e, () => ("That should not ever happened, right?")))};var f=(e,t,r)=>{if(!N(e,t))throw new d(_nullishCoalesce(r, () => (`subObj:
2
+ ${u.stringify(t)}
167
3
  is not subset of
168
- ${c2.stringify(t)}`)));
169
- };
170
- function U(t, e) {
171
- if (!t) throw new p2(_nullishCoalesce(e, () => ( "Condition is not truthy")));
172
- }
173
- function u(t, e, r2) {
174
- if (t !== e) throw new p2(`${_nullishCoalesce(r2, () => ( "Objects are not equal"))}:
175
- Expected: ${c2.stringify(t)}
176
- Actual:${c2.stringify(e)}`);
177
- }
178
- var wo = (t) => {
179
- let e = /* @__PURE__ */ new Map();
180
- return { async aggregateStream(r2, n) {
181
- return t.aggregateStream(r2, n);
182
- }, readStream(r2, n) {
183
- return t.readStream(r2, n);
184
- }, appendToStream: async (r2, n, o2) => {
185
- let a2 = await t.appendToStream(r2, n, o2), i = _nullishCoalesce(e.get(r2), () => ( [r2, []]));
186
- return e.set(r2, [r2, [...i[1], ...n]]), a2;
187
- }, appendedEvents: e, setup: async (r2, n) => t.appendToStream(r2, n) };
188
- };
189
-
190
- // src/middlewares/problemDetailsMiddleware.ts
191
- var _httpproblemdetails = require('http-problem-details');
192
- var problemDetailsMiddleware = (mapError) => (error, request, response, _next) => {
193
- let problemDetails;
194
- if (mapError) problemDetails = mapError(error, request);
195
- problemDetails = _nullishCoalesce(problemDetails, () => ( defaulErrorToProblemDetailsMapping(error)));
196
- sendProblem(response, problemDetails.status, { problem: problemDetails });
197
- };
198
- var defaulErrorToProblemDetailsMapping = (error) => {
199
- let statusCode = 500;
200
- if ("errorCode" in error && s(error.errorCode) && error.errorCode >= 100 && error.errorCode < 600) {
201
- statusCode = error.errorCode;
202
- }
203
- return new (0, _httpproblemdetails.ProblemDocument)({
204
- detail: error.message,
205
- status: statusCode
206
- });
207
- };
208
-
209
- // src/application.ts
210
- var getApplication = (options) => {
211
- const app = _express2.default.call(void 0, );
212
- const {
213
- apis,
214
- mapError,
215
- enableDefaultExpressEtag,
216
- disableJsonMiddleware,
217
- disableUrlEncodingMiddleware,
218
- disableProblemDetailsMiddleware
219
- } = options;
220
- const router = _express.Router.call(void 0, );
221
- app.set("etag", _nullishCoalesce(enableDefaultExpressEtag, () => ( false)));
222
- if (!disableJsonMiddleware) app.use(_express2.default.json());
223
- if (!disableUrlEncodingMiddleware)
224
- app.use(
225
- _express2.default.urlencoded({
226
- extended: true
227
- })
228
- );
229
- for (const api of apis) {
230
- api(router);
231
- }
232
- app.use(router);
233
- if (!disableProblemDetailsMiddleware)
234
- app.use(problemDetailsMiddleware(mapError));
235
- return app;
236
- };
237
- var startAPI = (app, options = { port: 3e3 }) => {
238
- const { port } = options;
239
- const server = _http2.default.createServer(app);
240
- server.on("listening", () => {
241
- console.info("server up listening");
242
- });
243
- return server.listen(port);
244
- };
245
-
246
- // src/etag.ts
247
- var HeaderNames = {
248
- IF_MATCH: "if-match",
249
- IF_NOT_MATCH: "if-not-match",
250
- ETag: "etag"
251
- };
252
- var WeakETagRegex = /W\/"(-?\d+.*)"/;
253
- var ETagErrors = /* @__PURE__ */ ((ETagErrors2) => {
254
- ETagErrors2["WRONG_WEAK_ETAG_FORMAT"] = "WRONG_WEAK_ETAG_FORMAT";
255
- ETagErrors2["MISSING_IF_MATCH_HEADER"] = "MISSING_IF_MATCH_HEADER";
256
- ETagErrors2["MISSING_IF_NOT_MATCH_HEADER"] = "MISSING_IF_NOT_MATCH_HEADER";
257
- return ETagErrors2;
258
- })(ETagErrors || {});
259
- var isWeakETag = (etag) => {
260
- return WeakETagRegex.test(etag);
261
- };
262
- var getWeakETagValue = (etag) => {
263
- const result = WeakETagRegex.exec(etag);
264
- if (result === null || result.length === 0) {
265
- throw new Error("WRONG_WEAK_ETAG_FORMAT" /* WRONG_WEAK_ETAG_FORMAT */);
266
- }
267
- return result[1];
268
- };
269
- var toWeakETag = (value) => {
270
- return `W/"${value}"`;
271
- };
272
- var getETagFromIfMatch = (request) => {
273
- const etag = request.headers[HeaderNames.IF_MATCH];
274
- if (etag === void 0) {
275
- throw new Error("MISSING_IF_MATCH_HEADER" /* MISSING_IF_MATCH_HEADER */);
276
- }
277
- return etag;
278
- };
279
- var getETagFromIfNotMatch = (request) => {
280
- const etag = request.headers[HeaderNames.IF_NOT_MATCH];
281
- if (etag === void 0) {
282
- throw new Error("MISSING_IF_MATCH_HEADER" /* MISSING_IF_MATCH_HEADER */);
283
- }
284
- return Array.isArray(etag) ? etag[0] : etag;
285
- };
286
- var setETag = (response, etag) => {
287
- response.setHeader(HeaderNames.ETag, etag);
288
- };
289
- var getETagValueFromIfMatch = (request) => {
290
- const eTagValue = getETagFromIfMatch(request);
291
- return isWeakETag(eTagValue) ? getWeakETagValue(eTagValue) : eTagValue;
292
- };
293
-
294
- // src/handler.ts
295
-
296
- var on = (handle) => async (request, response, _next) => {
297
- const setResponse = await Promise.resolve(handle(request));
298
- return setResponse(response);
299
- };
300
- var OK = (options) => (response) => {
301
- send(response, 200, options);
302
- };
303
- var Created = (options) => (response) => {
304
- sendCreated(response, options);
305
- };
306
- var Accepted = (options) => (response) => {
307
- sendAccepted(response, options);
308
- };
309
- var NoContent = (options) => HttpResponse(204, options);
310
- var HttpResponse = (statusCode, options) => (response) => {
311
- send(response, statusCode, options);
312
- };
313
- var BadRequest = (options) => HttpProblem(400, options);
314
- var Forbidden = (options) => HttpProblem(403, options);
315
- var NotFound = (options) => HttpProblem(404, options);
316
- var Conflict = (options) => HttpProblem(409, options);
317
- var PreconditionFailed = (options) => HttpProblem(412, options);
318
- var HttpProblem = (statusCode, options) => (response) => {
319
- sendProblem(response, statusCode, options);
320
- };
321
-
322
- // src/responses.ts
323
-
324
-
325
- var DefaultHttpResponseOptions = {};
326
- var DefaultHttpProblemResponseOptions = {
327
- problemDetails: "Error occured!"
328
- };
329
- var sendCreated = (response, { eTag, ...options }) => send(response, 201, {
330
- location: "url" in options ? options.url : `${response.req.url}/${options.createdId}`,
331
- body: "createdId" in options ? { id: options.createdId } : void 0,
332
- eTag
333
- });
334
- var sendAccepted = (response, options) => send(response, 202, options);
335
- var send = (response, statusCode, options) => {
336
- const { location, body, eTag } = _nullishCoalesce(options, () => ( DefaultHttpResponseOptions));
337
- if (eTag) setETag(response, eTag);
338
- if (location) response.setHeader("Location", location);
339
- if (body) {
340
- response.statusCode = statusCode;
341
- response.send(body);
342
- } else {
343
- response.sendStatus(statusCode);
344
- }
345
- };
346
- var sendProblem = (response, statusCode, options) => {
347
- options = _nullishCoalesce(options, () => ( DefaultHttpProblemResponseOptions));
348
- const { location, eTag } = options;
349
- const problemDetails = "problem" in options ? options.problem : new (0, _httpproblemdetails.ProblemDocument)({
350
- detail: options.problemDetails,
351
- status: statusCode
352
- });
353
- if (eTag) setETag(response, eTag);
354
- if (location) response.setHeader("Location", location);
355
- response.setHeader("Content-Type", "application/problem+json");
356
- response.statusCode = statusCode;
357
- response.json(problemDetails);
358
- };
359
-
360
- // src/testing/apiE2ESpecification.ts
361
- var _supertest = require('supertest'); var _supertest2 = _interopRequireDefault(_supertest);
362
- var _assert = require('assert'); var _assert2 = _interopRequireDefault(_assert);
363
- var ApiE2ESpecification = {
364
- for: (getEventStore, getApplication2) => {
365
- {
366
- return (...givenRequests) => {
367
- const eventStore = wo(getEventStore());
368
- const application = getApplication2(eventStore);
369
- return {
370
- when: (setupRequest) => {
371
- const handle = async () => {
372
- for (const requestFn of givenRequests) {
373
- await requestFn(_supertest2.default.call(void 0, application));
374
- }
375
- return setupRequest(_supertest2.default.call(void 0, application));
376
- };
377
- return {
378
- then: async (verify) => {
379
- const response = await handle();
380
- verify.forEach((assertion) => {
381
- const succeeded = assertion(response);
382
- if (succeeded === false) _assert2.default.fail();
383
- });
384
- }
385
- };
386
- }
387
- };
388
- };
389
- }
390
- }
391
- };
392
-
393
- // src/testing/apiSpecification.ts
394
-
395
-
396
- var existingStream = (streamId, events) => {
397
- return [streamId, events];
398
- };
399
- var expect = (streamId, events) => {
400
- return [streamId, events];
401
- };
402
- var expectNewEvents = (streamId, events) => {
403
- return [streamId, events];
404
- };
405
- var expectResponse = (statusCode, options) => (response) => {
406
- const { body, headers } = _nullishCoalesce(options, () => ( {}));
407
- u(statusCode, response.statusCode, "Response code doesn't match");
408
- if (body) ve(response.body, body);
409
- if (headers) ve(response.headers, headers);
410
- };
411
- var expectError = (errorCode, problemDetails) => expectResponse(
412
- errorCode,
413
- problemDetails ? { body: problemDetails } : void 0
414
- );
415
- var ApiSpecification = {
416
- for: (getEventStore, getApplication2) => {
417
- {
418
- return (...givenStreams) => {
419
- const eventStore = wo(getEventStore());
420
- const application = getApplication2(eventStore);
421
- return {
422
- when: (setupRequest) => {
423
- const handle = async () => {
424
- for (const [streamName, events] of givenStreams) {
425
- await eventStore.setup(streamName, events);
426
- }
427
- return setupRequest(_supertest2.default.call(void 0, application));
428
- };
429
- return {
430
- then: async (verify) => {
431
- const response = await handle();
432
- if (typeof verify === "function") {
433
- const succeeded = verify(response);
434
- if (succeeded === false) po();
435
- } else if (Array.isArray(verify)) {
436
- const [first, ...rest] = verify;
437
- if (typeof first === "function") {
438
- const succeeded = first(response);
439
- if (succeeded === false) po();
440
- }
441
- const events = typeof first === "function" ? rest : verify;
442
- ve(
443
- Array.from(eventStore.appendedEvents.values()),
444
- events
445
- );
446
- }
447
- }
448
- };
449
- }
450
- };
451
- };
452
- }
453
- }
454
- };
455
-
456
-
457
-
458
-
459
-
460
-
461
-
462
-
463
-
464
-
465
-
466
-
467
-
468
-
469
-
470
-
471
-
472
-
473
-
474
-
475
-
476
-
477
-
478
-
479
-
480
-
481
-
482
-
483
-
484
-
485
-
486
-
487
-
488
-
489
-
490
-
491
-
492
-
493
- exports.Accepted = Accepted; exports.ApiE2ESpecification = ApiE2ESpecification; exports.ApiSpecification = ApiSpecification; exports.BadRequest = BadRequest; exports.Conflict = Conflict; exports.Created = Created; exports.DefaultHttpProblemResponseOptions = DefaultHttpProblemResponseOptions; exports.DefaultHttpResponseOptions = DefaultHttpResponseOptions; exports.ETagErrors = ETagErrors; exports.Forbidden = Forbidden; exports.HeaderNames = HeaderNames; exports.HttpProblem = HttpProblem; exports.HttpResponse = HttpResponse; exports.NoContent = NoContent; exports.NotFound = NotFound; exports.OK = OK; exports.PreconditionFailed = PreconditionFailed; exports.WeakETagRegex = WeakETagRegex; exports.existingStream = existingStream; exports.expect = expect; exports.expectError = expectError; exports.expectNewEvents = expectNewEvents; exports.expectResponse = expectResponse; exports.getApplication = getApplication; exports.getETagFromIfMatch = getETagFromIfMatch; exports.getETagFromIfNotMatch = getETagFromIfNotMatch; exports.getETagValueFromIfMatch = getETagValueFromIfMatch; exports.getWeakETagValue = getWeakETagValue; exports.isWeakETag = isWeakETag; exports.on = on; exports.send = send; exports.sendAccepted = sendAccepted; exports.sendCreated = sendCreated; exports.sendProblem = sendProblem; exports.setETag = setETag; exports.startAPI = startAPI; exports.toWeakETag = toWeakETag;
4
+ ${u.stringify(e)}`)))};function R(e,t){if(!e)throw new d(_nullishCoalesce(t, () => ("Condition is not truthy")))}function I(e,t,r){if(e!==t)throw new d(`${_nullishCoalesce(r, () => ("Objects are not equal"))}:
5
+ Expected: ${u.stringify(e)}
6
+ Actual:${u.stringify(t)}`)}var g=e=>{let t=new Map;return{async aggregateStream(r,s){return e.aggregateStream(r,s)},readStream(r,s){return e.readStream(r,s)},appendToStream:async(r,s,o)=>{let n=await e.appendToStream(r,s,o),c=_nullishCoalesce(t.get(r), () => ([r,[]]));return t.set(r,[r,[...c[1],...s]]),n},appendedEvents:t,setup:async(r,s)=>e.appendToStream(r,s)}};var _httpproblemdetails = require('http-problem-details');var P=e=>(t,r,s,o)=>{let n;e&&(n=e(t,r)),n=_nullishCoalesce(n, () => (le(t))),h(s,n.status,{problem:n})},le=e=>{let t=500;return"errorCode"in e&&y(e.errorCode)&&e.errorCode>=100&&e.errorCode<600&&(t=e.errorCode),new (0, _httpproblemdetails.ProblemDocument)({detail:e.message,status:t})};var Rt=e=>{let t=_express2.default.call(void 0, ),{apis:r,mapError:s,enableDefaultExpressEtag:o,disableJsonMiddleware:n,disableUrlEncodingMiddleware:c,disableProblemDetailsMiddleware:a}=e,i=_express.Router.call(void 0, );t.set("etag",_nullishCoalesce(o, () => (!1))),n||t.use(_express2.default.json()),c||t.use(_express2.default.urlencoded({extended:!0}));for(let p of r)p(i);return t.use(i),a||t.use(P(s)),t},xt= exports.startAPI =(e,t={port:3e3})=>{let{port:r}=t,s=_http2.default.createServer(e);return s.on("listening",()=>{console.info("server up listening")}),s.listen(r)};var T={IF_MATCH:"if-match",IF_NOT_MATCH:"if-not-match",ETag:"etag"},_= exports.WeakETagRegex =/W\/"(-?\d+.*)"/,de= exports.ETagErrors =(s=>(s.WRONG_WEAK_ETAG_FORMAT="WRONG_WEAK_ETAG_FORMAT",s.MISSING_IF_MATCH_HEADER="MISSING_IF_MATCH_HEADER",s.MISSING_IF_NOT_MATCH_HEADER="MISSING_IF_NOT_MATCH_HEADER",s))(de||{}),fe= exports.isWeakETag =e=>_.test(e),ge= exports.getWeakETagValue =e=>{let t=_.exec(e);if(t===null||t.length===0)throw new Error("WRONG_WEAK_ETAG_FORMAT");return t[1]},Ct= exports.toWeakETag =e=>`W/"${e}"`,he= exports.getETagFromIfMatch =e=>{let t=e.headers[T.IF_MATCH];if(t===void 0)throw new Error("MISSING_IF_MATCH_HEADER");return t},Nt= exports.getETagFromIfNotMatch =e=>{let t=e.headers[T.IF_NOT_MATCH];if(t===void 0)throw new Error("MISSING_IF_MATCH_HEADER");return Array.isArray(t)?t[0]:t},S= exports.setETag =(e,t)=>{e.setHeader(T.ETag,t)},It= exports.getETagValueFromIfMatch =e=>{let t=he(e);return fe(t)?ge(t):t};var Ht=e=>async(t,r,s)=>(await Promise.resolve(e(t)))(r),kt= exports.OK =e=>t=>{m(t,200,e)},Dt= exports.Created =e=>t=>{M(t,e)},qt= exports.Accepted =e=>t=>{H(t,e)},Vt= exports.NoContent =e=>ye(204,e),ye= exports.HttpResponse =(e,t)=>r=>{m(r,e,t)},Ft= exports.BadRequest =e=>l(400,e),jt= exports.Forbidden =e=>l(403,e),Bt= exports.NotFound =e=>l(404,e),$t= exports.Conflict =e=>l(409,e),Wt= exports.PreconditionFailed =e=>l(412,e),l= exports.HttpProblem =(e,t)=>r=>{h(r,e,t)};var ve={},Te= exports.DefaultHttpProblemResponseOptions ={problemDetails:"Error occured!"},M= exports.sendCreated =(e,{eTag:t,...r})=>m(e,201,{location:"url"in r?r.url:`${e.req.url}/${r.createdId}`,body:"createdId"in r?{id:r.createdId}:void 0,eTag:t}),H= exports.sendAccepted =(e,t)=>m(e,202,t),m= exports.send =(e,t,r)=>{let{location:s,body:o,eTag:n}=_nullishCoalesce(r, () => (ve));n&&S(e,n),s&&e.setHeader("Location",s),o?(e.statusCode=t,e.send(o)):e.sendStatus(t)},h= exports.sendProblem =(e,t,r)=>{r=_nullishCoalesce(r, () => (Te));let{location:s,eTag:o}=r,n="problem"in r?r.problem:new (0, _httpproblemdetails.ProblemDocument)({detail:r.problemDetails,status:t});o&&S(e,o),s&&e.setHeader("Location",s),e.setHeader("Content-Type","application/problem+json"),e.statusCode=t,e.json(n)};var _supertest = require('supertest'); var _supertest2 = _interopRequireDefault(_supertest);var _assert = require('assert'); var _assert2 = _interopRequireDefault(_assert);var zt={for:(e,t)=>(...r)=>{let s=g(e()),o=t(s);return{when:n=>{let c=async()=>{for(let a of r)await a(_supertest2.default.call(void 0, o));return n(_supertest2.default.call(void 0, o))};return{then:async a=>{let i=await c();a.forEach(p=>{p(i)===!1&&_assert2.default.fail()})}}}}}};var sr=(e,t)=>[e,t],or= exports.expect =(e,t)=>[e,t],nr= exports.expectNewEvents =(e,t)=>[e,t],we= exports.expectResponse =(e,t)=>r=>{let{body:s,headers:o}=_nullishCoalesce(t, () => ({}));I(e,r.statusCode,"Response code doesn't match"),s&&f(r.body,s),o&&f(r.headers,o)},ar= exports.expectError =(e,t)=>we(e,t?{body:t}:void 0),ir= exports.ApiSpecification ={for:(e,t)=>(...r)=>{let s=g(e()),o=t(s);return{when:n=>{let c=async()=>{for(let[a,i]of r)await s.setup(a,i);return n(_supertest2.default.call(void 0, o))};return{then:async a=>{let i=await c();if(typeof a=="function")a(i)===!1&&E();else if(Array.isArray(a)){let[p,...b]=a;typeof p=="function"&&p(i)===!1&&E();let D=typeof p=="function"?b:a;f(Array.from(s.appendedEvents.values()),D)}}}}}}};exports.Accepted = qt; exports.ApiE2ESpecification = zt; exports.ApiSpecification = ir; exports.BadRequest = Ft; exports.Conflict = $t; exports.Created = Dt; exports.DefaultHttpProblemResponseOptions = Te; exports.DefaultHttpResponseOptions = ve; exports.ETagErrors = de; exports.Forbidden = jt; exports.HeaderNames = T; exports.HttpProblem = l; exports.HttpResponse = ye; exports.NoContent = Vt; exports.NotFound = Bt; exports.OK = kt; exports.PreconditionFailed = Wt; exports.WeakETagRegex = _; exports.existingStream = sr; exports.expect = or; exports.expectError = ar; exports.expectNewEvents = nr; exports.expectResponse = we; exports.getApplication = Rt; exports.getETagFromIfMatch = he; exports.getETagFromIfNotMatch = Nt; exports.getETagValueFromIfMatch = It; exports.getWeakETagValue = ge; exports.isWeakETag = fe; exports.on = Ht; exports.send = m; exports.sendAccepted = H; exports.sendCreated = M; exports.sendProblem = h; exports.setETag = S; exports.startAPI = xt; exports.toWeakETag = Ct;
494
7
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["/home/runner/work/emmett/emmett/src/packages/emmett-expressjs/dist/index.cjs","../src/index.ts","../src/application.ts","../../emmett/src/validation/index.ts","../../emmett/src/eventStore/inMemoryEventStore.ts","../../emmett/src/eventStore/subscriptions/caughtUpTransformStream.ts","../../emmett/src/eventStore/subscriptions/streamingCoordinator.ts","../../emmett/src/streaming/transformations/notifyAboutNoActiveReaders.ts","../../emmett/src/utils/retry.ts","../../emmett/src/streaming/generators/fromArray.ts","../../emmett/src/streaming/restream.ts","../../emmett/src/streaming/transformations/filter.ts","../../emmett/src/streaming/transformations/map.ts","../../emmett/src/streaming/transformations/reduce.ts","../../emmett/src/streaming/transformations/retry.ts","../../emmett/src/streaming/transformations/skip.ts","../../emmett/src/streaming/transformations/stopAfter.ts","../../emmett/src/streaming/transformations/stopOn.ts","../../emmett/src/streaming/transformations/take.ts","../../emmett/src/streaming/transformations/waitAtMost.ts","../../emmett/src/serialization/json/JSONParser.ts","../../emmett/src/streaming/transformations/index.ts","../../emmett/src/testing/assertions.ts","../../emmett/src/testing/wrapEventStore.ts","../src/middlewares/problemDetailsMiddleware.ts","../src/etag.ts","../src/handler.ts","../src/responses.ts","../src/testing/apiE2ESpecification.ts","../src/testing/apiSpecification.ts"],"names":["ValidationErrors","isNumber","val","notifyAboutNoActiveReadersStream","onNoActiveReaderCallback","options","NotifyAboutNoActiveReadersStream","TransformStream","reason","uuid","interval","asyncRetry","fn","opts","retry","bail","error","ParseError","text","initialValue","chunk","done","result","controller","skip","limit","take","streamTransformations","subObj","AssertionError","streamName","ETagErrors","ProblemDocument","getApplication"],"mappings":"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","file":"/home/runner/work/emmett/emmett/src/packages/emmett-expressjs/dist/index.cjs","sourcesContent":[null,"import 'express-async-errors';\n\nexport * from './application';\nexport * from './etag';\nexport * from './handler';\nexport * from './responses';\nexport * from './testing';\n","import express, { Router, type Application } from 'express';\nimport 'express-async-errors';\nimport http from 'http';\nimport { problemDetailsMiddleware } from './middlewares/problemDetailsMiddleware';\nimport type { ErrorToProblemDetailsMapping } from './responses';\n\n// #region web-api-setup\nexport type WebApiSetup = (router: Router) => void;\n// #endregion web-api-setup\n\nexport type ApplicationOptions = {\n apis: WebApiSetup[];\n mapError?: ErrorToProblemDetailsMapping;\n enableDefaultExpressEtag?: boolean;\n disableJsonMiddleware?: boolean;\n disableUrlEncodingMiddleware?: boolean;\n disableProblemDetailsMiddleware?: boolean;\n};\n\nexport const getApplication = (options: ApplicationOptions) => {\n const app: Application = express();\n\n const {\n apis,\n mapError,\n enableDefaultExpressEtag,\n disableJsonMiddleware,\n disableUrlEncodingMiddleware,\n disableProblemDetailsMiddleware,\n } = options;\n\n const router = Router();\n\n // disabling default etag behaviour\n // to use etags in if-match and if-not-match headers\n app.set('etag', enableDefaultExpressEtag ?? false);\n\n // add json middleware\n if (!disableJsonMiddleware) app.use(express.json());\n\n // enable url encoded urls and bodies\n if (!disableUrlEncodingMiddleware)\n app.use(\n express.urlencoded({\n extended: true,\n }),\n );\n\n for (const api of apis) {\n api(router);\n }\n app.use(router);\n\n // add problem details middleware\n if (!disableProblemDetailsMiddleware)\n app.use(problemDetailsMiddleware(mapError));\n\n return app;\n};\n\nexport type StartApiOptions = {\n port?: number;\n};\n\nexport const startAPI = (\n app: Application,\n options: StartApiOptions = { port: 3000 },\n) => {\n const { port } = options;\n const server = http.createServer(app);\n\n server.on('listening', () => {\n console.info('server up listening');\n });\n\n return server.listen(port);\n};\n","import { ValidationError } from '../errors';\n\nexport const enum ValidationErrors {\n NOT_A_NONEMPTY_STRING = 'NOT_A_NONEMPTY_STRING',\n NOT_A_POSITIVE_NUMBER = 'NOT_A_POSITIVE_NUMBER',\n NOT_AN_UNSIGNED_BIGINT = 'NOT_AN_UNSIGNED_BIGINT',\n}\n\nexport const isNumber = (val: unknown): val is number =>\n typeof val === 'number' && val === val;\n\nexport const isString = (val: unknown): val is string =>\n typeof val === 'string';\n\nexport const assertNotEmptyString = (value: unknown): string => {\n if (!isString(value) || value.length === 0) {\n throw new ValidationError(ValidationErrors.NOT_A_NONEMPTY_STRING);\n }\n return value;\n};\n\nexport const assertPositiveNumber = (value: unknown): number => {\n if (!isNumber(value) || value <= 0) {\n throw new ValidationError(ValidationErrors.NOT_A_POSITIVE_NUMBER);\n }\n return value;\n};\n\nexport const assertUnsignedBigInt = (value: string): bigint => {\n const number = BigInt(value);\n if (number < 0) {\n throw new ValidationError(ValidationErrors.NOT_AN_UNSIGNED_BIGINT);\n }\n return number;\n};\n\nexport * from './dates';\n","import { v4 as uuid } from 'uuid';\nimport type {\n Event,\n ReadEvent,\n ReadEventMetadataWithGlobalPosition,\n} from '../typing';\nimport {\n type AggregateStreamOptions,\n type AggregateStreamResult,\n type AppendToStreamOptions,\n type AppendToStreamResult,\n type DefaultStreamVersionType,\n type EventStore,\n type ReadStreamOptions,\n type ReadStreamResult,\n} from './eventStore';\nimport { assertExpectedVersionMatchesCurrent } from './expectedVersion';\nimport { StreamingCoordinator } from './subscriptions';\n\nexport type EventHandler<E extends Event = Event> = (\n eventEnvelope: ReadEvent<E>,\n) => void;\n\nexport const InMemoryEventStoreDefaultStreamVersion = 0n;\n\nexport const getInMemoryEventStore = (): EventStore<\n DefaultStreamVersionType,\n ReadEventMetadataWithGlobalPosition\n> => {\n const streams = new Map<\n string,\n ReadEvent<Event, ReadEventMetadataWithGlobalPosition>[]\n >();\n const streamingCoordinator = StreamingCoordinator();\n\n const getAllEventsCount = () => {\n return Array.from<ReadEvent[]>(streams.values())\n .map((s) => s.length)\n .reduce((p, c) => p + c, 0);\n };\n\n return {\n async aggregateStream<State, EventType extends Event>(\n streamName: string,\n options: AggregateStreamOptions<State, EventType>,\n ): Promise<AggregateStreamResult<State>> {\n const { evolve, initialState, read } = options;\n\n const result = await this.readStream<EventType>(streamName, read);\n\n const events = result?.events ?? [];\n\n return {\n currentStreamVersion: BigInt(events.length),\n state: events.reduce(evolve, initialState()),\n streamExists: result.streamExists,\n };\n },\n\n readStream: <EventType extends Event>(\n streamName: string,\n options?: ReadStreamOptions,\n ): Promise<\n ReadStreamResult<\n EventType,\n DefaultStreamVersionType,\n ReadEventMetadataWithGlobalPosition\n >\n > => {\n const events = streams.get(streamName);\n const currentStreamVersion = events\n ? BigInt(events.length)\n : InMemoryEventStoreDefaultStreamVersion;\n\n assertExpectedVersionMatchesCurrent(\n currentStreamVersion,\n options?.expectedStreamVersion,\n InMemoryEventStoreDefaultStreamVersion,\n );\n\n const from = Number(options && 'from' in options ? options.from : 0);\n const to = Number(\n options && 'to' in options\n ? options.to\n : options && 'maxCount' in options && options.maxCount\n ? options.from + options.maxCount\n : (events?.length ?? 1),\n );\n\n const resultEvents =\n events !== undefined && events.length > 0\n ? events\n .map(\n (e) =>\n e as ReadEvent<\n EventType,\n ReadEventMetadataWithGlobalPosition\n >,\n )\n .slice(from, to)\n : [];\n\n const result: ReadStreamResult<\n EventType,\n DefaultStreamVersionType,\n ReadEventMetadataWithGlobalPosition\n > = {\n currentStreamVersion,\n events: resultEvents,\n streamExists: events !== undefined && events.length > 0,\n };\n\n return Promise.resolve(result);\n },\n\n appendToStream: async <EventType extends Event>(\n streamName: string,\n events: EventType[],\n options?: AppendToStreamOptions,\n ): Promise<AppendToStreamResult> => {\n const currentEvents = streams.get(streamName) ?? [];\n const currentStreamVersion =\n currentEvents.length > 0\n ? BigInt(currentEvents.length)\n : InMemoryEventStoreDefaultStreamVersion;\n\n assertExpectedVersionMatchesCurrent(\n currentStreamVersion,\n options?.expectedStreamVersion,\n InMemoryEventStoreDefaultStreamVersion,\n );\n\n const newEvents: ReadEvent<\n EventType,\n ReadEventMetadataWithGlobalPosition\n >[] = events.map((event, index) => {\n return {\n ...event,\n metadata: {\n ...(event.metadata ?? {}),\n streamName,\n eventId: uuid(),\n streamPosition: BigInt(currentEvents.length + index + 1),\n globalPosition: BigInt(getAllEventsCount() + index + 1),\n },\n };\n });\n\n const positionOfLastEventInTheStream = BigInt(\n newEvents.slice(-1)[0]!.metadata.streamPosition,\n );\n\n streams.set(streamName, [...currentEvents, ...newEvents]);\n await streamingCoordinator.notify(newEvents);\n\n const result: AppendToStreamResult = {\n nextExpectedStreamVersion: positionOfLastEventInTheStream,\n createdNewStream:\n currentStreamVersion === InMemoryEventStoreDefaultStreamVersion,\n };\n\n return result;\n },\n\n //streamEvents: streamingCoordinator.stream,\n };\n};\n","import { TransformStream } from 'web-streams-polyfill';\nimport type {\n Event,\n ReadEvent,\n ReadEventMetadataWithGlobalPosition,\n} from '../../typing';\nimport { globalStreamCaughtUp, type GlobalSubscriptionEvent } from '../events';\n\nexport const streamTrackingGlobalPosition = (\n currentEvents: ReadEvent<Event, ReadEventMetadataWithGlobalPosition>[],\n) => new CaughtUpTransformStream(currentEvents);\n\nexport class CaughtUpTransformStream extends TransformStream<\n ReadEvent<Event, ReadEventMetadataWithGlobalPosition>,\n | ReadEvent<Event, ReadEventMetadataWithGlobalPosition>\n | GlobalSubscriptionEvent\n> {\n private _currentPosition: bigint;\n private _logPosition: bigint;\n\n constructor(events: ReadEvent<Event, ReadEventMetadataWithGlobalPosition>[]) {\n super({\n start: (controller) => {\n let globalPosition = 0n;\n for (const event of events) {\n controller.enqueue(event);\n globalPosition = event.metadata.globalPosition;\n }\n controller.enqueue(globalStreamCaughtUp({ globalPosition }));\n },\n transform: (event, controller) => {\n this._currentPosition = event.metadata.globalPosition;\n controller.enqueue(event);\n\n if (this._currentPosition < this._logPosition) return;\n\n controller.enqueue(\n globalStreamCaughtUp({ globalPosition: this._currentPosition }),\n );\n },\n });\n\n this._currentPosition = this._logPosition =\n events.length > 0\n ? events[events.length - 1]!.metadata.globalPosition\n : 0n;\n }\n\n public set logPosition(value: bigint) {\n this._logPosition = value;\n }\n}\n","import { v4 as uuid } from 'uuid';\nimport { notifyAboutNoActiveReadersStream } from '../../streaming/transformations/notifyAboutNoActiveReaders';\nimport { writeToStream } from '../../streaming/writers';\nimport type {\n Event,\n ReadEvent,\n ReadEventMetadataWithGlobalPosition,\n} from '../../typing';\nimport {\n CaughtUpTransformStream,\n streamTrackingGlobalPosition,\n} from './caughtUpTransformStream';\n\nexport const StreamingCoordinator = () => {\n const allEvents: ReadEvent<Event, ReadEventMetadataWithGlobalPosition>[] = [];\n const listeners = new Map<string, CaughtUpTransformStream>();\n\n return {\n notify: async (\n events: ReadEvent<Event, ReadEventMetadataWithGlobalPosition>[],\n ) => {\n if (events.length === 0) return;\n\n allEvents.push(...events);\n\n for (const listener of listeners.values()) {\n listener.logPosition =\n events[events.length - 1]!.metadata.globalPosition;\n\n await writeToStream(listener, events);\n }\n },\n\n stream: () => {\n const streamId = uuid();\n const transformStream = streamTrackingGlobalPosition(allEvents);\n\n listeners.set(streamId, transformStream);\n return transformStream.readable.pipeThrough(\n notifyAboutNoActiveReadersStream(\n (stream) => {\n if (listeners.has(stream.streamId))\n listeners.delete(stream.streamId);\n },\n { streamId },\n ),\n );\n },\n };\n};\n","import { v4 as uuid } from 'uuid';\nimport { TransformStream } from 'web-streams-polyfill';\n\nexport const notifyAboutNoActiveReadersStream = <Item>(\n onNoActiveReaderCallback: (\n stream: NotifyAboutNoActiveReadersStream<Item>,\n ) => void,\n options: { streamId?: string; intervalCheckInMs?: number } = {},\n) => new NotifyAboutNoActiveReadersStream(onNoActiveReaderCallback, options);\n\nexport class NotifyAboutNoActiveReadersStream<Item> extends TransformStream<\n Item,\n Item\n> {\n private checkInterval: NodeJS.Timeout | null = null;\n public readonly streamId: string;\n private _isStopped: boolean = false;\n public get hasActiveSubscribers() {\n return !this._isStopped;\n }\n\n constructor(\n private onNoActiveReaderCallback: (\n stream: NotifyAboutNoActiveReadersStream<Item>,\n ) => void,\n options: { streamId?: string; intervalCheckInMs?: number } = {},\n ) {\n super({\n cancel: (reason) => {\n console.log('Stream was canceled. Reason:', reason);\n this.stopChecking();\n },\n });\n this.streamId = options?.streamId ?? uuid();\n\n this.onNoActiveReaderCallback = onNoActiveReaderCallback;\n\n this.startChecking(options?.intervalCheckInMs ?? 20);\n }\n\n private startChecking(interval: number) {\n this.checkInterval = setInterval(() => {\n this.checkNoActiveReader();\n }, interval);\n }\n\n private stopChecking() {\n if (!this.checkInterval) return;\n\n clearInterval(this.checkInterval);\n this.checkInterval = null;\n this._isStopped = true;\n this.onNoActiveReaderCallback(this);\n }\n\n private checkNoActiveReader() {\n if (!this.readable.locked && !this._isStopped) {\n this.stopChecking();\n }\n }\n}\n","import retry from 'async-retry';\n\nexport type AsyncRetryOptions = retry.Options & {\n shouldRetryError?: (error: unknown) => boolean;\n};\n\nexport const NoRetries: AsyncRetryOptions = { retries: 0 };\n\nexport const asyncRetry = async <T>(\n fn: () => Promise<T>,\n opts?: AsyncRetryOptions,\n): Promise<T> => {\n if (opts === undefined || opts.retries === 0) return fn();\n\n return retry(\n async (bail) => {\n try {\n return await fn();\n } catch (error) {\n if (opts?.shouldRetryError && !opts.shouldRetryError(error)) {\n bail(error as Error);\n }\n throw error;\n }\n },\n opts ?? { retries: 0 },\n );\n};\n","import { ReadableStream } from 'web-streams-polyfill';\n\nexport const fromArray = <T>(chunks: T[]) =>\n new ReadableStream<T>({\n start(controller) {\n for (const chunk of chunks) controller.enqueue(chunk);\n controller.close();\n },\n });\n","import {\n type ReadableStream,\n type ReadableStreamDefaultReadResult,\n type TransformStreamDefaultController,\n} from 'web-streams-polyfill';\nimport type { AsyncRetryOptions } from '../utils';\nimport type { Decoder } from './decoders';\nimport { DefaultDecoder } from './decoders/composite';\nimport { streamTransformations } from './transformations';\n\nconst { retry } = streamTransformations;\n\nexport const restream = <\n Source = unknown,\n Transformed = Source,\n StreamType = Source,\n>(\n createSourceStream: () => ReadableStream<StreamType>,\n transform: (input: Source) => Transformed = (source) =>\n source as unknown as Transformed,\n retryOptions: AsyncRetryOptions = { forever: true, minTimeout: 25 },\n decoder: Decoder<StreamType, Source> = new DefaultDecoder<Source>(),\n): ReadableStream<Transformed> =>\n retry(createSourceStream, handleChunk(transform, decoder), retryOptions)\n .readable;\n\nconst handleChunk =\n <Source = unknown, Transformed = Source, StreamType = Source>(\n transform: (input: Source) => Transformed = (source) =>\n source as unknown as Transformed,\n decoder: Decoder<StreamType, Source> = new DefaultDecoder<Source>(),\n ) =>\n (\n readResult: ReadableStreamDefaultReadResult<StreamType>,\n controller: TransformStreamDefaultController<Transformed>,\n ): void => {\n const { done: isDone, value } = readResult;\n\n if (value) decoder.addToBuffer(value);\n\n if (!isDone && !decoder.hasCompleteMessage()) return;\n\n decodeAndTransform(decoder, transform, controller);\n };\n\nconst decodeAndTransform = <StreamType, Source, Transformed = Source>(\n decoder: Decoder<StreamType, Source>,\n transform: (input: Source) => Transformed,\n controller: TransformStreamDefaultController<Transformed>,\n) => {\n try {\n const decoded = decoder.decode();\n if (!decoded) return; // TODO: Add a proper handling of decode errors\n\n const transformed = transform(decoded);\n controller.enqueue(transformed);\n } catch (error) {\n controller.error(new Error(`Decoding error: ${error?.toString()}`));\n }\n};\n","import { TransformStream } from 'web-streams-polyfill';\n\nexport const filter = <Item>(filter: (item: Item) => boolean) =>\n new TransformStream<Item, Item>({\n transform(chunk, controller) {\n if (filter(chunk)) {\n controller.enqueue(chunk);\n }\n },\n });\n","import { TransformStream } from 'web-streams-polyfill';\n\nexport const map = <From, To>(map: (item: From) => To) =>\n new TransformStream<From, To>({\n transform(chunk, controller) {\n controller.enqueue(map(chunk));\n },\n });\n","import { TransformStream } from 'web-streams-polyfill';\n\nexport const reduce = <I, O>(\n reducer: (accumulator: O, chunk: I) => O,\n initialValue: O,\n) => new ReduceTransformStream<I, O>(reducer, initialValue);\n\nexport class ReduceTransformStream<I, O> extends TransformStream<I, O> {\n private accumulator: O;\n private reducer: (accumulator: O, chunk: I) => O;\n\n constructor(reducer: (accumulator: O, chunk: I) => O, initialValue: O) {\n super({\n transform: (chunk) => {\n this.accumulator = this.reducer(this.accumulator, chunk);\n },\n flush: (controller) => {\n controller.enqueue(this.accumulator);\n controller.terminate();\n },\n });\n\n this.accumulator = initialValue;\n this.reducer = reducer;\n }\n}\n","import {\n type ReadableStream,\n type ReadableStreamDefaultReadResult,\n TransformStream,\n type TransformStreamDefaultController,\n} from 'web-streams-polyfill';\nimport { type AsyncRetryOptions, asyncRetry } from '../../utils';\n\nexport const retryStream = <\n Source = unknown,\n Transformed = Source,\n StreamType = Source,\n>(\n createSourceStream: () => ReadableStream<StreamType>,\n handleChunk: (\n readResult: ReadableStreamDefaultReadResult<StreamType>,\n controller: TransformStreamDefaultController<Transformed>,\n ) => Promise<void> | void,\n retryOptions: AsyncRetryOptions = { forever: true, minTimeout: 25 },\n): TransformStream<Source, Transformed> =>\n new TransformStream<Source, Transformed>({\n start(controller) {\n asyncRetry(\n () => onRestream(createSourceStream, handleChunk, controller),\n retryOptions,\n ).catch((error) => {\n controller.error(error);\n });\n },\n });\n\nconst onRestream = async <StreamType, Source, Transformed = Source>(\n createSourceStream: () => ReadableStream<StreamType>,\n handleChunk: (\n readResult: ReadableStreamDefaultReadResult<StreamType>,\n controller: TransformStreamDefaultController<Transformed>,\n ) => Promise<void> | void,\n controller: TransformStreamDefaultController<Transformed>,\n): Promise<void> => {\n const sourceStream = createSourceStream();\n const reader = sourceStream.getReader();\n\n try {\n let done: boolean;\n\n do {\n const result = await reader.read();\n done = result.done;\n\n await handleChunk(result, controller);\n\n if (done) {\n controller.terminate();\n }\n } while (!done);\n } finally {\n reader.releaseLock();\n }\n};\n","import { TransformStream } from 'web-streams-polyfill';\n\nexport const skip = <T>(limit: number) => new SkipTransformStream<T>(limit);\n\nexport class SkipTransformStream<T> extends TransformStream<T, T> {\n private count = 0;\n private skip: number;\n\n constructor(skip: number) {\n super({\n transform: (chunk, controller) => {\n this.count++;\n if (this.count > this.skip) {\n controller.enqueue(chunk);\n }\n },\n });\n\n this.skip = skip;\n }\n}\n","import { TransformStream } from 'web-streams-polyfill';\n\nexport const stopAfter = <Item>(stopCondition: (item: Item) => boolean) =>\n new TransformStream<Item, Item>({\n transform(chunk, controller) {\n controller.enqueue(chunk);\n\n if (stopCondition(chunk)) {\n controller.terminate();\n }\n },\n });\n","import { TransformStream } from 'web-streams-polyfill';\n\nexport const stopOn = <Item>(stopCondition: (item: Item) => boolean) =>\n new TransformStream<Item, Item>({\n async transform(chunk, controller) {\n if (!stopCondition(chunk)) {\n controller.enqueue(chunk);\n return;\n }\n await Promise.resolve();\n controller.terminate();\n },\n });\n","import { TransformStream } from 'web-streams-polyfill';\n\nexport const take = <T>(limit: number) => new TakeTransformStream<T>(limit);\n\nexport class TakeTransformStream<T> extends TransformStream<T, T> {\n private count = 0;\n private limit: number;\n\n constructor(limit: number) {\n super({\n transform: (chunk, controller) => {\n if (this.count < this.limit) {\n this.count++;\n controller.enqueue(chunk);\n } else {\n controller.terminate();\n }\n },\n });\n\n this.limit = limit;\n }\n}\n","import { TransformStream } from 'web-streams-polyfill';\n\nexport const waitAtMost = <Item>(waitTimeInMs: number) =>\n new TransformStream<Item, Item>({\n start(controller) {\n const timeoutId = setTimeout(() => {\n controller.terminate();\n }, waitTimeInMs);\n\n const originalTerminate = controller.terminate.bind(controller);\n\n // Clear the timeout if the stream is terminated early\n controller.terminate = () => {\n clearTimeout(timeoutId);\n originalTerminate();\n };\n },\n transform(chunk, controller) {\n controller.enqueue(chunk);\n },\n });\n","export class ParseError extends Error {\n constructor(text: string) {\n super(`Cannot parse! ${text}`);\n }\n}\n\nexport type Mapper<From, To = From> =\n | ((value: unknown) => To)\n | ((value: Partial<From>) => To)\n | ((value: From) => To)\n | ((value: Partial<To>) => To)\n | ((value: To) => To)\n | ((value: Partial<To | From>) => To)\n | ((value: To | From) => To);\n\nexport type MapperArgs<From, To = From> = Partial<From> &\n From &\n Partial<To> &\n To;\n\nexport type ParseOptions<From, To = From> = {\n reviver?: (key: string, value: unknown) => unknown;\n map?: Mapper<From, To>;\n typeCheck?: <To>(value: unknown) => value is To;\n};\n\nexport type StringifyOptions<From, To = From> = {\n map?: Mapper<From, To>;\n};\n\nexport const JSONParser = {\n stringify: <From, To = From>(\n value: From,\n options?: StringifyOptions<From, To>,\n ) => {\n return JSON.stringify(\n options?.map ? options.map(value as MapperArgs<From, To>) : value,\n //TODO: Consider adding support to DateTime and adding specific format to mark that's a bigint\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n (_, v) => (typeof v === 'bigint' ? v.toString() : v),\n );\n },\n parse: <From, To = From>(\n text: string,\n options?: ParseOptions<From, To>,\n ): To | undefined => {\n const parsed: unknown = JSON.parse(text, options?.reviver);\n\n if (options?.typeCheck && !options?.typeCheck<To>(parsed))\n throw new ParseError(text);\n\n return options?.map\n ? options.map(parsed as MapperArgs<From, To>)\n : (parsed as To | undefined);\n },\n};\n","import { filter } from './filter';\nimport { map } from './map';\nimport {\n notifyAboutNoActiveReadersStream,\n NotifyAboutNoActiveReadersStream,\n} from './notifyAboutNoActiveReaders';\nimport { reduce, ReduceTransformStream } from './reduce';\nimport { retryStream } from './retry';\nimport { skip, SkipTransformStream } from './skip';\nimport { stopAfter } from './stopAfter';\nimport { stopOn } from './stopOn';\nimport { take, TakeTransformStream } from './take';\nimport { waitAtMost } from './waitAtMost';\n\nexport const streamTransformations = {\n filter,\n take,\n TakeTransformStream,\n skip,\n SkipTransformStream,\n map,\n notifyAboutNoActiveReadersStream,\n NotifyAboutNoActiveReadersStream,\n reduce,\n ReduceTransformStream,\n retry: retryStream,\n stopAfter,\n stopOn,\n waitAtMost,\n};\n","import { JSONParser } from '../serialization';\nimport type { DefaultRecord } from '../typing';\nimport { deepEquals } from '../utils';\n\nexport class AssertionError extends Error {\n constructor(message: string) {\n super(message);\n }\n}\n\nexport const isSubset = (superObj: unknown, subObj: unknown): boolean => {\n const sup = superObj as DefaultRecord;\n const sub = subObj as DefaultRecord;\n\n assertOk(sup);\n assertOk(sub);\n\n return Object.keys(sub).every((ele: string) => {\n if (typeof sub[ele] == 'object') {\n return isSubset(sup[ele], sub[ele]);\n }\n return sub[ele] === sup[ele];\n });\n};\n\nexport const assertFails = (message?: string) => {\n throw new AssertionError(message ?? 'That should not ever happened, right?');\n};\n\nexport const assertThrowsAsync = async <TError extends Error>(\n fun: () => Promise<void>,\n errorCheck?: (error: Error) => boolean,\n): Promise<TError> => {\n try {\n await fun();\n throw new AssertionError(\"Function didn't throw expected error\");\n } catch (error) {\n const typedError = error as TError;\n if (errorCheck) assertTrue(errorCheck(typedError));\n return typedError;\n }\n};\n\nexport const assertThrows = <TError extends Error>(\n fun: () => void,\n errorCheck?: (error: Error) => boolean,\n): TError => {\n try {\n fun();\n throw new AssertionError(\"Function didn't throw expected error\");\n } catch (error) {\n const typedError = error as TError;\n if (errorCheck) assertTrue(errorCheck(typedError));\n return typedError;\n }\n};\n\nexport const assertRejects = async <T, TError extends Error = Error>(\n promise: Promise<T>,\n errorCheck?: ((error: TError) => boolean) | TError,\n) => {\n try {\n await promise;\n throw new AssertionError(\"Function didn't throw expected error\");\n } catch (error) {\n if (!errorCheck) return;\n\n if (errorCheck instanceof Error) assertDeepEqual(error, errorCheck);\n else assertTrue(errorCheck(error as TError));\n }\n};\n\nexport const assertMatches = (\n actual: unknown,\n expected: unknown,\n message?: string,\n) => {\n if (!isSubset(actual, expected))\n throw new AssertionError(\n message ??\n `subObj:\\n${JSONParser.stringify(expected)}\\nis not subset of\\n${JSONParser.stringify(actual)}`,\n );\n};\n\nexport const assertDeepEqual = <T = unknown>(\n actual: T,\n expected: T,\n message?: string,\n) => {\n if (!deepEquals(actual, expected))\n throw new AssertionError(\n message ??\n `subObj:\\n${JSONParser.stringify(expected)}\\nis not equal to\\n${JSONParser.stringify(actual)}`,\n );\n};\n\nexport const assertNotDeepEqual = <T = unknown>(\n actual: T,\n expected: T,\n message?: string,\n) => {\n if (deepEquals(actual, expected))\n throw new AssertionError(\n message ??\n `subObj:\\n${JSONParser.stringify(expected)}\\nis equals to\\n${JSONParser.stringify(actual)}`,\n );\n};\n\nexport const assertThat = <T>(item: T) => {\n return {\n isEqualTo: (other: T) => assertTrue(deepEquals(item, other)),\n };\n};\n\nexport function assertFalse(\n condition: boolean,\n message?: string,\n): asserts condition is false {\n if (condition) throw new AssertionError(message ?? `Condition is false`);\n}\n\nexport function assertTrue(\n condition: boolean,\n message?: string,\n): asserts condition is true {\n if (!condition) throw new AssertionError(message ?? `Condition is false`);\n}\n\nexport function assertOk<T>(\n obj: T | null | undefined,\n message?: string,\n): asserts obj is T {\n if (!obj) throw new AssertionError(message ?? `Condition is not truthy`);\n}\n\nexport function assertEqual<T>(\n expected: T | null | undefined,\n actual: T | null | undefined,\n message?: string,\n): void {\n if (expected !== actual)\n throw new AssertionError(\n `${message ?? 'Objects are not equal'}:\\nExpected: ${JSONParser.stringify(expected)}\\nActual:${JSONParser.stringify(actual)}`,\n );\n}\n\nexport function assertNotEqual<T>(\n obj: T | null | undefined,\n other: T | null | undefined,\n message?: string,\n): void {\n if (obj === other)\n throw new AssertionError(\n message ?? `Objects are equal: ${JSONParser.stringify(obj)}`,\n );\n}\n\nexport function assertIsNotNull<T extends object | bigint>(\n result: T | null,\n): asserts result is T {\n assertNotEqual(result, null);\n assertOk(result);\n}\n\nexport function assertIsNull<T extends object>(\n result: T | null,\n): asserts result is null {\n assertEqual(result, null);\n}\n\ntype Call = {\n arguments: unknown[];\n result: unknown;\n target: unknown;\n this: unknown;\n};\n\nexport type ArgumentMatcher = (arg: unknown) => boolean;\n\nexport const argValue =\n <T>(value: T): ArgumentMatcher =>\n (arg) =>\n deepEquals(arg, value);\n\nexport const argMatches =\n <T>(matches: (arg: T) => boolean): ArgumentMatcher =>\n (arg) =>\n matches(arg as T);\n\n// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\nexport type MockedFunction = Function & { mock?: { calls: Call[] } };\n\nexport function verifyThat(fn: MockedFunction) {\n return {\n calledTimes: (times: number) => {\n assertEqual(fn.mock?.calls?.length, times);\n },\n notCalled: () => {\n assertEqual(fn?.mock?.calls?.length, 0);\n },\n called: () => {\n assertTrue(\n fn.mock?.calls.length !== undefined && fn.mock.calls.length > 0,\n );\n },\n calledWith: (...args: unknown[]) => {\n assertTrue(\n fn.mock?.calls.length !== undefined &&\n fn.mock.calls.length >= 1 &&\n fn.mock.calls.some((call) => deepEquals(call.arguments, args)),\n );\n },\n calledOnceWith: (...args: unknown[]) => {\n assertTrue(\n fn.mock?.calls.length !== undefined &&\n fn.mock.calls.length === 1 &&\n fn.mock.calls.some((call) => deepEquals(call.arguments, args)),\n );\n },\n calledWithArgumentMatching: (...matches: ArgumentMatcher[]) => {\n assertTrue(\n fn.mock?.calls.length !== undefined && fn.mock.calls.length >= 1,\n );\n assertTrue(\n fn.mock?.calls.length !== undefined &&\n fn.mock.calls.length >= 1 &&\n fn.mock.calls.some(\n (call) =>\n call.arguments &&\n call.arguments.length >= matches.length &&\n matches.every((match, index) => match(call.arguments[index])),\n ),\n );\n },\n notCalledWithArgumentMatching: (...matches: ArgumentMatcher[]) => {\n assertFalse(\n fn.mock?.calls.length !== undefined &&\n fn.mock.calls.length >= 1 &&\n fn.mock.calls[0]!.arguments &&\n fn.mock.calls[0]!.arguments.length >= matches.length &&\n matches.every((match, index) =>\n match(fn.mock!.calls[0]!.arguments[index]),\n ),\n );\n },\n };\n}\n\nexport const assertThatArray = <T>(array: T[]) => {\n return {\n isEmpty: () => assertEqual(array.length, 0),\n isNotEmpty: () => assertNotEqual(array.length, 0),\n hasSize: (length: number) => assertEqual(array.length, length),\n containsElements: (...other: T[]) => {\n assertTrue(other.every((ts) => other.some((o) => deepEquals(ts, o))));\n },\n containsExactlyInAnyOrder: (...other: T[]) => {\n assertEqual(array.length, other.length);\n assertTrue(array.every((ts) => other.some((o) => deepEquals(ts, o))));\n },\n containsExactlyInAnyOrderElementsOf: (other: T[]) => {\n assertEqual(array.length, other.length);\n assertTrue(array.every((ts) => other.some((o) => deepEquals(ts, o))));\n },\n containsExactlyElementsOf: (other: T[]) => {\n assertEqual(array.length, other.length);\n for (let i = 0; i < array.length; i++) {\n assertTrue(deepEquals(array[i], other[i]));\n }\n },\n containsExactly: (elem: T) => {\n assertEqual(array.length, 1);\n assertTrue(deepEquals(array[0], elem));\n },\n contains: (elem: T) => {\n assertTrue(array.some((a) => deepEquals(a, elem)));\n },\n containsOnlyOnceElementsOf: (other: T[]) => {\n assertTrue(\n other\n .map((o) => array.filter((a) => deepEquals(a, o)).length)\n .filter((a) => a === 1).length === other.length,\n );\n },\n containsAnyOf: (...other: T[]) => {\n assertTrue(array.some((a) => other.some((o) => deepEquals(a, o))));\n },\n allMatch: (matches: (item: T) => boolean) => {\n assertTrue(array.every(matches));\n },\n anyMatches: (matches: (item: T) => boolean) => {\n assertTrue(array.some(matches));\n },\n allMatchAsync: async (\n matches: (item: T) => Promise<boolean>,\n ): Promise<void> => {\n for (const item of array) {\n assertTrue(await matches(item));\n }\n },\n };\n};\n","import type {\n AggregateStreamOptions,\n AggregateStreamResult,\n AppendToStreamOptions,\n AppendToStreamResult,\n DefaultStreamVersionType,\n EventStore,\n ReadStreamOptions,\n ReadStreamResult,\n} from '../eventStore';\nimport { type Event, type ReadEventMetadata } from '../typing';\n\nexport type TestEventStream<EventType extends Event = Event> = [\n string,\n EventType[],\n];\n\nexport const WrapEventStore = <\n StreamVersion = DefaultStreamVersionType,\n ReadEventMetadataType extends ReadEventMetadata = ReadEventMetadata,\n>(\n eventStore: EventStore<StreamVersion, ReadEventMetadataType>,\n): EventStore<StreamVersion, ReadEventMetadataType> & {\n appendedEvents: Map<string, TestEventStream>;\n setup<EventType extends Event>(\n streamName: string,\n events: EventType[],\n ): Promise<AppendToStreamResult<StreamVersion>>;\n} => {\n const appendedEvents = new Map<string, TestEventStream>();\n\n return {\n async aggregateStream<State, EventType extends Event>(\n streamName: string,\n options: AggregateStreamOptions<State, EventType, StreamVersion>,\n ): Promise<AggregateStreamResult<State, StreamVersion>> {\n return eventStore.aggregateStream(streamName, options);\n },\n\n readStream<EventType extends Event>(\n streamName: string,\n options?: ReadStreamOptions<StreamVersion>,\n ): Promise<\n ReadStreamResult<EventType, StreamVersion, ReadEventMetadataType>\n > {\n return eventStore.readStream(streamName, options);\n },\n\n appendToStream: async <EventType extends Event>(\n streamName: string,\n events: EventType[],\n options?: AppendToStreamOptions<StreamVersion>,\n ): Promise<AppendToStreamResult<StreamVersion>> => {\n const result = await eventStore.appendToStream(\n streamName,\n events,\n options,\n );\n\n const currentStream = appendedEvents.get(streamName) ?? [streamName, []];\n\n appendedEvents.set(streamName, [\n streamName,\n [...currentStream[1], ...events],\n ]);\n\n return result;\n },\n\n appendedEvents,\n\n setup: async <EventType extends Event>(\n streamName: string,\n events: EventType[],\n ): Promise<AppendToStreamResult<StreamVersion>> => {\n return eventStore.appendToStream(streamName, events);\n },\n\n // streamEvents: (): ReadableStream<\n // // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents\n // ReadEvent<Event, ReadEventMetadataType> | GlobalSubscriptionEvent\n // > => {\n // return eventStore.streamEvents();\n // },\n };\n};\n","import { isNumber } from '@event-driven-io/emmett';\nimport type { NextFunction, Request, Response } from 'express';\nimport { ProblemDocument } from 'http-problem-details';\nimport { sendProblem, type ErrorToProblemDetailsMapping } from '..';\n\nexport const problemDetailsMiddleware =\n (mapError?: ErrorToProblemDetailsMapping) =>\n (\n error: Error,\n request: Request,\n response: Response,\n _next: NextFunction,\n ): void => {\n let problemDetails: ProblemDocument | undefined;\n\n if (mapError) problemDetails = mapError(error, request);\n\n problemDetails =\n problemDetails ?? defaulErrorToProblemDetailsMapping(error);\n\n sendProblem(response, problemDetails.status, { problem: problemDetails });\n };\n\nexport const defaulErrorToProblemDetailsMapping = (\n error: Error,\n): ProblemDocument => {\n let statusCode = 500;\n\n if (\n 'errorCode' in error &&\n isNumber(error.errorCode) &&\n error.errorCode >= 100 &&\n error.errorCode < 600\n ) {\n statusCode = error.errorCode;\n }\n\n return new ProblemDocument({\n detail: error.message,\n status: statusCode,\n });\n};\n","import { type Brand } from '@event-driven-io/emmett';\nimport type { Request, Response } from 'express';\n\n//////////////////////////////////////\n/// ETAG\n//////////////////////////////////////\n\nexport const HeaderNames = {\n IF_MATCH: 'if-match',\n IF_NOT_MATCH: 'if-not-match',\n ETag: 'etag',\n};\n\nexport type WeakETag = Brand<`W/${string}`, 'ETag'>;\nexport type ETag = Brand<string, 'ETag'>;\n\nexport const WeakETagRegex = /W\\/\"(-?\\d+.*)\"/;\n\nexport const enum ETagErrors {\n WRONG_WEAK_ETAG_FORMAT = 'WRONG_WEAK_ETAG_FORMAT',\n MISSING_IF_MATCH_HEADER = 'MISSING_IF_MATCH_HEADER',\n MISSING_IF_NOT_MATCH_HEADER = 'MISSING_IF_NOT_MATCH_HEADER',\n}\n\nexport const isWeakETag = (etag: ETag): etag is WeakETag => {\n return WeakETagRegex.test(etag as string);\n};\n\nexport const getWeakETagValue = (etag: ETag): string => {\n const result = WeakETagRegex.exec(etag as string);\n if (result === null || result.length === 0) {\n throw new Error(ETagErrors.WRONG_WEAK_ETAG_FORMAT);\n }\n return result[1]!;\n};\n\nexport const toWeakETag = (value: number | bigint | string): WeakETag => {\n return `W/\"${value}\"` as WeakETag;\n};\n\nexport const getETagFromIfMatch = (request: Request): ETag => {\n const etag = request.headers[HeaderNames.IF_MATCH];\n\n if (etag === undefined) {\n throw new Error(ETagErrors.MISSING_IF_MATCH_HEADER);\n }\n\n return etag as ETag;\n};\n\nexport const getETagFromIfNotMatch = (request: Request): ETag => {\n const etag = request.headers[HeaderNames.IF_NOT_MATCH];\n\n if (etag === undefined) {\n throw new Error(ETagErrors.MISSING_IF_MATCH_HEADER);\n }\n\n return (Array.isArray(etag) ? etag[0] : etag) as ETag;\n};\n\nexport const setETag = (response: Response, etag: ETag): void => {\n response.setHeader(HeaderNames.ETag, etag as string);\n};\n\nexport const getETagValueFromIfMatch = (request: Request): string => {\n const eTagValue: ETag = getETagFromIfMatch(request);\n\n return isWeakETag(eTagValue)\n ? getWeakETagValue(eTagValue)\n : (eTagValue as string);\n};\n","import { type NextFunction, type Request, type Response } from 'express';\nimport {\n send,\n sendAccepted,\n sendCreated,\n sendProblem,\n type AcceptedHttpResponseOptions,\n type CreatedHttpResponseOptions,\n type HttpProblemResponseOptions,\n type HttpResponseOptions,\n type NoContentHttpResponseOptions,\n} from '.';\n\n// #region httpresponse-on\nexport type HttpResponse = (response: Response) => void;\n\nexport type HttpHandler<RequestType extends Request> = (\n request: RequestType,\n) => Promise<HttpResponse> | HttpResponse;\n\nexport const on =\n <RequestType extends Request>(handle: HttpHandler<RequestType>) =>\n async (\n request: RequestType,\n response: Response,\n _next: NextFunction,\n ): Promise<void> => {\n const setResponse = await Promise.resolve(handle(request));\n\n return setResponse(response);\n };\n// #endregion httpresponse-on\n\nexport const OK =\n (options?: HttpResponseOptions): HttpResponse =>\n (response: Response) => {\n send(response, 200, options);\n };\n\nexport const Created =\n (options: CreatedHttpResponseOptions): HttpResponse =>\n (response: Response) => {\n sendCreated(response, options);\n };\n\nexport const Accepted =\n (options: AcceptedHttpResponseOptions): HttpResponse =>\n (response: Response) => {\n sendAccepted(response, options);\n };\n\nexport const NoContent = (\n options?: NoContentHttpResponseOptions,\n): HttpResponse => HttpResponse(204, options);\n\nexport const HttpResponse =\n (statusCode: number, options?: HttpResponseOptions): HttpResponse =>\n (response: Response) => {\n send(response, statusCode, options);\n };\n\n/////////////////////\n// ERRORS\n/////////////////////\n\nexport const BadRequest = (\n options?: HttpProblemResponseOptions,\n): HttpResponse => HttpProblem(400, options);\n\nexport const Forbidden = (options?: HttpProblemResponseOptions): HttpResponse =>\n HttpProblem(403, options);\n\nexport const NotFound = (options?: HttpProblemResponseOptions): HttpResponse =>\n HttpProblem(404, options);\n\nexport const Conflict = (options?: HttpProblemResponseOptions): HttpResponse =>\n HttpProblem(409, options);\n\nexport const PreconditionFailed = (\n options: HttpProblemResponseOptions,\n): HttpResponse => HttpProblem(412, options);\n\nexport const HttpProblem =\n (statusCode: number, options?: HttpProblemResponseOptions): HttpResponse =>\n (response: Response) => {\n sendProblem(response, statusCode, options);\n };\n","import { type Request, type Response } from 'express';\nimport { ProblemDocument } from 'http-problem-details';\nimport { setETag, type ETag } from './etag';\n\nexport type ErrorToProblemDetailsMapping = (\n error: Error,\n request: Request,\n) => ProblemDocument | undefined;\n\nexport type HttpResponseOptions = {\n body?: unknown;\n location?: string;\n eTag?: ETag;\n};\nexport const DefaultHttpResponseOptions: HttpResponseOptions = {};\n\nexport type HttpProblemResponseOptions = {\n location?: string;\n eTag?: ETag;\n} & Omit<HttpResponseOptions, 'body'> &\n (\n | {\n problem: ProblemDocument;\n }\n | { problemDetails: string }\n );\nexport const DefaultHttpProblemResponseOptions: HttpProblemResponseOptions = {\n problemDetails: 'Error occured!',\n};\n\nexport type CreatedHttpResponseOptions = (\n | {\n createdId: string;\n }\n | {\n createdId?: string;\n url: string;\n }\n) &\n HttpResponseOptions;\n\nexport const sendCreated = (\n response: Response,\n { eTag, ...options }: CreatedHttpResponseOptions,\n): void =>\n send(response, 201, {\n location:\n 'url' in options\n ? options.url\n : `${response.req.url}/${options.createdId}`,\n body: 'createdId' in options ? { id: options.createdId } : undefined,\n eTag,\n });\n\nexport type AcceptedHttpResponseOptions = {\n location: string;\n} & HttpResponseOptions;\n\nexport const sendAccepted = (\n response: Response,\n options: AcceptedHttpResponseOptions,\n): void => send(response, 202, options);\n\nexport type NoContentHttpResponseOptions = Omit<HttpResponseOptions, 'body'>;\n\nexport const send = (\n response: Response,\n statusCode: number,\n options?: HttpResponseOptions,\n): void => {\n const { location, body, eTag } = options ?? DefaultHttpResponseOptions;\n // HEADERS\n if (eTag) setETag(response, eTag);\n if (location) response.setHeader('Location', location);\n\n if (body) {\n response.statusCode = statusCode;\n response.send(body);\n } else {\n response.sendStatus(statusCode);\n }\n};\n\nexport const sendProblem = (\n response: Response,\n statusCode: number,\n options?: HttpProblemResponseOptions,\n): void => {\n options = options ?? DefaultHttpProblemResponseOptions;\n\n const { location, eTag } = options;\n\n const problemDetails =\n 'problem' in options\n ? options.problem\n : new ProblemDocument({\n detail: options.problemDetails,\n status: statusCode,\n });\n\n // HEADERS\n if (eTag) setETag(response, eTag);\n if (location) response.setHeader('Location', location);\n\n response.setHeader('Content-Type', 'application/problem+json');\n\n response.statusCode = statusCode;\n response.json(problemDetails);\n};\n","import supertest, { type Response } from 'supertest';\n\nimport type {\n DefaultStreamVersionType,\n EventStore,\n} from '@event-driven-io/emmett';\nimport { WrapEventStore } from '@event-driven-io/emmett';\nimport assert from 'assert';\nimport type { Application } from 'express';\nimport type { TestRequest } from './apiSpecification';\n\nexport type E2EResponseAssert = (response: Response) => boolean | void;\n\nexport type ApiE2ESpecificationAssert = [E2EResponseAssert];\n\nexport type ApiE2ESpecification = (...givenRequests: TestRequest[]) => {\n when: (setupRequest: TestRequest) => {\n then: (verify: ApiE2ESpecificationAssert) => Promise<void>;\n };\n};\n\nexport const ApiE2ESpecification = {\n for: <StreamVersion = DefaultStreamVersionType>(\n getEventStore: () => EventStore<StreamVersion>,\n getApplication: (eventStore: EventStore<StreamVersion>) => Application,\n ): ApiE2ESpecification => {\n {\n return (...givenRequests: TestRequest[]) => {\n const eventStore = WrapEventStore(getEventStore());\n const application = getApplication(eventStore);\n\n return {\n when: (setupRequest: TestRequest) => {\n const handle = async () => {\n for (const requestFn of givenRequests) {\n await requestFn(supertest(application));\n }\n\n return setupRequest(supertest(application));\n };\n\n return {\n then: async (\n verify: ApiE2ESpecificationAssert,\n ): Promise<void> => {\n const response = await handle();\n\n verify.forEach((assertion) => {\n const succeeded = assertion(response);\n\n if (succeeded === false) assert.fail();\n });\n },\n };\n },\n };\n };\n }\n },\n};\n","import {\n WrapEventStore,\n assertEqual,\n assertFails,\n assertMatches,\n type DefaultStreamVersionType,\n type Event,\n type EventStore,\n type TestEventStream,\n} from '@event-driven-io/emmett';\nimport { type Application } from 'express';\nimport type { ProblemDocument } from 'http-problem-details';\nimport type { Response, Test } from 'supertest';\nimport supertest from 'supertest';\nimport type TestAgent from 'supertest/lib/agent';\n\n////////////////////////////////\n/////////// Setup\n////////////////////////////////\n\nexport type TestRequest = (request: TestAgent<supertest.Test>) => Test;\n\nexport const existingStream = <EventType extends Event = Event>(\n streamId: string,\n events: EventType[],\n): TestEventStream<EventType> => {\n return [streamId, events];\n};\n\n////////////////////////////////\n/////////// Asserts\n////////////////////////////////\n\nexport type ResponseAssert = (response: Response) => boolean | void;\n\nexport type ApiSpecificationAssert<EventType extends Event = Event> =\n | TestEventStream<EventType>[]\n | ResponseAssert\n | [ResponseAssert, ...TestEventStream<EventType>[]];\n\nexport const expect = <EventType extends Event = Event>(\n streamId: string,\n events: EventType[],\n): TestEventStream<EventType> => {\n return [streamId, events];\n};\n\nexport const expectNewEvents = <EventType extends Event = Event>(\n streamId: string,\n events: EventType[],\n): TestEventStream<EventType> => {\n return [streamId, events];\n};\n\nexport const expectResponse =\n <Body = unknown>(\n statusCode: number,\n options?: { body?: Body; headers?: { [index: string]: string } },\n ) =>\n (response: Response): void => {\n const { body, headers } = options ?? {};\n assertEqual(statusCode, response.statusCode, \"Response code doesn't match\");\n if (body) assertMatches(response.body, body);\n if (headers) assertMatches(response.headers, headers);\n };\n\nexport const expectError = (\n errorCode: number,\n problemDetails?: Partial<ProblemDocument>,\n) =>\n expectResponse(\n errorCode,\n problemDetails ? { body: problemDetails } : undefined,\n );\n\n////////////////////////////////\n/////////// Api Specification\n////////////////////////////////\n\nexport type ApiSpecification<EventType extends Event = Event> = (\n ...givenStreams: TestEventStream<EventType>[]\n) => {\n when: (setupRequest: TestRequest) => {\n then: (verify: ApiSpecificationAssert<EventType>) => Promise<void>;\n };\n};\n\nexport const ApiSpecification = {\n for: <\n EventType extends Event = Event,\n StreamVersion = DefaultStreamVersionType,\n >(\n getEventStore: () => EventStore<StreamVersion>,\n getApplication: (eventStore: EventStore<StreamVersion>) => Application,\n ): ApiSpecification<EventType> => {\n {\n return (...givenStreams: TestEventStream<EventType>[]) => {\n const eventStore = WrapEventStore(getEventStore());\n const application = getApplication(eventStore);\n\n return {\n when: (setupRequest: TestRequest) => {\n const handle = async () => {\n for (const [streamName, events] of givenStreams) {\n await eventStore.setup(streamName, events);\n }\n\n return setupRequest(supertest(application));\n };\n\n return {\n then: async (\n verify: ApiSpecificationAssert<EventType>,\n ): Promise<void> => {\n const response = await handle();\n\n if (typeof verify === 'function') {\n const succeeded = verify(response);\n\n if (succeeded === false) assertFails();\n } else if (Array.isArray(verify)) {\n const [first, ...rest] = verify;\n\n if (typeof first === 'function') {\n const succeeded = first(response);\n\n if (succeeded === false) assertFails();\n }\n\n const events = typeof first === 'function' ? rest : verify;\n\n assertMatches(\n Array.from(eventStore.appendedEvents.values()),\n events,\n );\n }\n },\n };\n },\n };\n };\n }\n },\n};\n"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/application.ts","../../emmett/src/validation/index.ts","../../emmett/src/eventStore/inMemoryEventStore.ts","../../emmett/src/eventStore/subscriptions/caughtUpTransformStream.ts","../../emmett/src/utils/retry.ts","../../emmett/src/streaming/transformations/notifyAboutNoActiveReaders.ts","../../emmett/src/serialization/json/JSONParser.ts","../../emmett/src/testing/assertions.ts","/home/runner/work/emmett/emmett/src/packages/emmett-expressjs/dist/index.cjs","../src/responses.ts"],"names":["ValidationErrors","isNumber","val","notifyAboutNoActiveReadersStream","onNoActiveReaderCallback","options","NotifyAboutNoActiveReadersStream","TransformStream","reason","uuid","interval","asyncRetry","fn","opts","retry","bail","error","ParseError","text"],"mappings":"AAAA,u1BAAO,oFCA2C,wEAEjC,ICACA,CAAAA,CAAAA,CAAAA,CAAAA,EAAAA,CAChBA,CAAAA,CAAA,qBAAA,CAAwB,uBAAA,CACxBA,CAAAA,CAAA,qBAAA,CAAwB,uBAAA,CACxBA,CAAAA,CAAA,sBAAA,CAAyB,wBAAA,CAHTA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,EAAA,CAAA,CAAA,CAAA,CAMLC,CAAAA,CAAYC,CAAAA,EACvB,OAAOA,CAAAA,EAAQ,QAAA,EAAYA,CAAAA,GAAQA,CAAAA,CCTrC,4BAA2B,0DCAK,iGCAd,ICGLC,CAAAA,CAAmC,CAC9CC,CAAAA,CAGAC,CAAAA,CAA6D,CAAC,CAAA,CAAA,EAC3D,IAAIC,CAAAA,CAAiCF,CAAAA,CAA0BC,CAAO,CAAA,CAE9DC,CAAAA,WAAN,MAAA,QAAqDC,mCAG1D,CAQA,WAAA,CACUH,CAAAA,CAGRC,CAAAA,CAA6D,CAAC,CAAA,CAC9D,CACA,KAAA,CAAM,CACJ,MAAA,CAASG,CAAAA,EAAW,CAClB,OAAA,CAAQ,GAAA,CAAI,8BAAA,CAAgCA,CAAM,CAAA,CAClD,IAAA,CAAK,YAAA,CAAa,CACpB,CACF,CAAC,yEAAA,CAVO,IAAA,CAAA,wBAAA,CAAAJ,CAAAA,CAWR,IAAA,CAAK,QAAA,kCAAWC,CAAAA,6BAAS,UAAA,SAAYI,sBAAAA,GAAK,CAE1C,IAAA,CAAK,wBAAA,CAA2BL,CAAAA,CAEhC,IAAA,CAAK,aAAA,kCAAcC,CAAAA,6BAAS,mBAAA,SAAqB,IAAE,CACrD,gBAxBQ,aAAA,CAAuC,KAAA,gBAEvC,UAAA,CAAsB,CAAA,EAAA,IACnB,oBAAA,CAAA,CAAuB,CAChC,MAAO,CAAC,IAAA,CAAK,UACf,CAqBQ,aAAA,CAAcK,CAAAA,CAAkB,CACtC,IAAA,CAAK,aAAA,CAAgB,WAAA,CAAY,CAAA,CAAA,EAAM,CACrC,IAAA,CAAK,mBAAA,CAAoB,CAC3B,CAAA,CAAGA,CAAQ,CACb,CAEQ,YAAA,CAAA,CAAe,CAChB,IAAA,CAAK,aAAA,EAAA,CAEV,aAAA,CAAc,IAAA,CAAK,aAAa,CAAA,CAChC,IAAA,CAAK,aAAA,CAAgB,IAAA,CACrB,IAAA,CAAK,UAAA,CAAa,CAAA,CAAA,CAClB,IAAA,CAAK,wBAAA,CAAyB,IAAI,CAAA,CACpC,CAEQ,mBAAA,CAAA,CAAsB,CACxB,CAAC,IAAA,CAAK,QAAA,CAAS,MAAA,EAAU,CAAC,IAAA,CAAK,UAAA,EACjC,IAAA,CAAK,YAAA,CAAa,CAEtB,CACF,UAAA,CDtDO,IAEMC,CAAAA,CAAa,KAAA,CACxBC,CAAAA,CACAC,CAAAA,CAAAA,EAEIA,CAAAA,GAAS,KAAA,CAAA,EAAaA,CAAAA,CAAK,OAAA,GAAY,CAAA,CAAUD,CAAAA,CAAG,CAAA,CAEjDE,kCAAAA,MACEC,CAAAA,EAAS,CACd,GAAI,CACF,OAAO,MAAMH,CAAAA,CAAG,CAClB,CAAA,KAAA,CAASI,CAAAA,CAAO,CACd,sBAAIH,CAAAA,6BAAM,kBAAA,EAAoB,CAACA,CAAAA,CAAK,gBAAA,CAAiBG,CAAK,CAAA,EACxDD,CAAAA,CAAKC,CAAc,CAAA,CAEfA,CACR,CACF,CAAA,kBACAH,CAAAA,SAAQ,CAAE,OAAA,CAAS,CAAE,GACvB,CAAA,CE1BK,IAAMI,CAAAA,CAAN,MAAA,QAAyB,KAAM,CACpC,WAAA,CAAYC,CAAAA,CAAc,CACxB,KAAA,CAAM,CAAA,cAAA,EAAiBA,CAAI,CAAA,CAAA;AC8EmB;AC9Ei9D;AD4Ix9D;AAA8C,UAAA;AE7IzF,OAAA","file":"/home/runner/work/emmett/emmett/src/packages/emmett-expressjs/dist/index.cjs","sourcesContent":["import 'express-async-errors';\n\nexport * from './application';\nexport * from './etag';\nexport * from './handler';\nexport * from './responses';\nexport * from './testing';\n","import express, { Router, type Application } from 'express';\nimport 'express-async-errors';\nimport http from 'http';\nimport { problemDetailsMiddleware } from './middlewares/problemDetailsMiddleware';\nimport type { ErrorToProblemDetailsMapping } from './responses';\n\n// #region web-api-setup\nexport type WebApiSetup = (router: Router) => void;\n// #endregion web-api-setup\n\nexport type ApplicationOptions = {\n apis: WebApiSetup[];\n mapError?: ErrorToProblemDetailsMapping;\n enableDefaultExpressEtag?: boolean;\n disableJsonMiddleware?: boolean;\n disableUrlEncodingMiddleware?: boolean;\n disableProblemDetailsMiddleware?: boolean;\n};\n\nexport const getApplication = (options: ApplicationOptions) => {\n const app: Application = express();\n\n const {\n apis,\n mapError,\n enableDefaultExpressEtag,\n disableJsonMiddleware,\n disableUrlEncodingMiddleware,\n disableProblemDetailsMiddleware,\n } = options;\n\n const router = Router();\n\n // disabling default etag behaviour\n // to use etags in if-match and if-not-match headers\n app.set('etag', enableDefaultExpressEtag ?? false);\n\n // add json middleware\n if (!disableJsonMiddleware) app.use(express.json());\n\n // enable url encoded urls and bodies\n if (!disableUrlEncodingMiddleware)\n app.use(\n express.urlencoded({\n extended: true,\n }),\n );\n\n for (const api of apis) {\n api(router);\n }\n app.use(router);\n\n // add problem details middleware\n if (!disableProblemDetailsMiddleware)\n app.use(problemDetailsMiddleware(mapError));\n\n return app;\n};\n\nexport type StartApiOptions = {\n port?: number;\n};\n\nexport const startAPI = (\n app: Application,\n options: StartApiOptions = { port: 3000 },\n) => {\n const { port } = options;\n const server = http.createServer(app);\n\n server.on('listening', () => {\n console.info('server up listening');\n });\n\n return server.listen(port);\n};\n","import { ValidationError } from '../errors';\n\nexport const enum ValidationErrors {\n NOT_A_NONEMPTY_STRING = 'NOT_A_NONEMPTY_STRING',\n NOT_A_POSITIVE_NUMBER = 'NOT_A_POSITIVE_NUMBER',\n NOT_AN_UNSIGNED_BIGINT = 'NOT_AN_UNSIGNED_BIGINT',\n}\n\nexport const isNumber = (val: unknown): val is number =>\n typeof val === 'number' && val === val;\n\nexport const isString = (val: unknown): val is string =>\n typeof val === 'string';\n\nexport const assertNotEmptyString = (value: unknown): string => {\n if (!isString(value) || value.length === 0) {\n throw new ValidationError(ValidationErrors.NOT_A_NONEMPTY_STRING);\n }\n return value;\n};\n\nexport const assertPositiveNumber = (value: unknown): number => {\n if (!isNumber(value) || value <= 0) {\n throw new ValidationError(ValidationErrors.NOT_A_POSITIVE_NUMBER);\n }\n return value;\n};\n\nexport const assertUnsignedBigInt = (value: string): bigint => {\n const number = BigInt(value);\n if (number < 0) {\n throw new ValidationError(ValidationErrors.NOT_AN_UNSIGNED_BIGINT);\n }\n return number;\n};\n\nexport * from './dates';\n","import { v4 as uuid } from 'uuid';\nimport type {\n Event,\n ReadEvent,\n ReadEventMetadataWithGlobalPosition,\n} from '../typing';\nimport {\n type AggregateStreamOptions,\n type AggregateStreamResult,\n type AppendToStreamOptions,\n type AppendToStreamResult,\n type DefaultStreamVersionType,\n type EventStore,\n type ReadStreamOptions,\n type ReadStreamResult,\n} from './eventStore';\nimport { assertExpectedVersionMatchesCurrent } from './expectedVersion';\nimport { StreamingCoordinator } from './subscriptions';\n\nexport type EventHandler<E extends Event = Event> = (\n eventEnvelope: ReadEvent<E>,\n) => void;\n\nexport const InMemoryEventStoreDefaultStreamVersion = 0n;\n\nexport const getInMemoryEventStore = (): EventStore<\n DefaultStreamVersionType,\n ReadEventMetadataWithGlobalPosition\n> => {\n const streams = new Map<\n string,\n ReadEvent<Event, ReadEventMetadataWithGlobalPosition>[]\n >();\n const streamingCoordinator = StreamingCoordinator();\n\n const getAllEventsCount = () => {\n return Array.from<ReadEvent[]>(streams.values())\n .map((s) => s.length)\n .reduce((p, c) => p + c, 0);\n };\n\n return {\n async aggregateStream<State, EventType extends Event>(\n streamName: string,\n options: AggregateStreamOptions<State, EventType>,\n ): Promise<AggregateStreamResult<State>> {\n const { evolve, initialState, read } = options;\n\n const result = await this.readStream<EventType>(streamName, read);\n\n const events = result?.events ?? [];\n\n return {\n currentStreamVersion: BigInt(events.length),\n state: events.reduce(evolve, initialState()),\n streamExists: result.streamExists,\n };\n },\n\n readStream: <EventType extends Event>(\n streamName: string,\n options?: ReadStreamOptions,\n ): Promise<\n ReadStreamResult<\n EventType,\n DefaultStreamVersionType,\n ReadEventMetadataWithGlobalPosition\n >\n > => {\n const events = streams.get(streamName);\n const currentStreamVersion = events\n ? BigInt(events.length)\n : InMemoryEventStoreDefaultStreamVersion;\n\n assertExpectedVersionMatchesCurrent(\n currentStreamVersion,\n options?.expectedStreamVersion,\n InMemoryEventStoreDefaultStreamVersion,\n );\n\n const from = Number(options && 'from' in options ? options.from : 0);\n const to = Number(\n options && 'to' in options\n ? options.to\n : options && 'maxCount' in options && options.maxCount\n ? options.from + options.maxCount\n : (events?.length ?? 1),\n );\n\n const resultEvents =\n events !== undefined && events.length > 0\n ? events\n .map(\n (e) =>\n e as ReadEvent<\n EventType,\n ReadEventMetadataWithGlobalPosition\n >,\n )\n .slice(from, to)\n : [];\n\n const result: ReadStreamResult<\n EventType,\n DefaultStreamVersionType,\n ReadEventMetadataWithGlobalPosition\n > = {\n currentStreamVersion,\n events: resultEvents,\n streamExists: events !== undefined && events.length > 0,\n };\n\n return Promise.resolve(result);\n },\n\n appendToStream: async <EventType extends Event>(\n streamName: string,\n events: EventType[],\n options?: AppendToStreamOptions,\n ): Promise<AppendToStreamResult> => {\n const currentEvents = streams.get(streamName) ?? [];\n const currentStreamVersion =\n currentEvents.length > 0\n ? BigInt(currentEvents.length)\n : InMemoryEventStoreDefaultStreamVersion;\n\n assertExpectedVersionMatchesCurrent(\n currentStreamVersion,\n options?.expectedStreamVersion,\n InMemoryEventStoreDefaultStreamVersion,\n );\n\n const newEvents: ReadEvent<\n EventType,\n ReadEventMetadataWithGlobalPosition\n >[] = events.map((event, index) => {\n return {\n ...event,\n metadata: {\n ...(event.metadata ?? {}),\n streamName,\n eventId: uuid(),\n streamPosition: BigInt(currentEvents.length + index + 1),\n globalPosition: BigInt(getAllEventsCount() + index + 1),\n },\n };\n });\n\n const positionOfLastEventInTheStream = BigInt(\n newEvents.slice(-1)[0]!.metadata.streamPosition,\n );\n\n streams.set(streamName, [...currentEvents, ...newEvents]);\n await streamingCoordinator.notify(newEvents);\n\n const result: AppendToStreamResult = {\n nextExpectedStreamVersion: positionOfLastEventInTheStream,\n createdNewStream:\n currentStreamVersion === InMemoryEventStoreDefaultStreamVersion,\n };\n\n return result;\n },\n\n //streamEvents: streamingCoordinator.stream,\n };\n};\n","import { TransformStream } from 'web-streams-polyfill';\nimport type {\n Event,\n ReadEvent,\n ReadEventMetadataWithGlobalPosition,\n} from '../../typing';\nimport { globalStreamCaughtUp, type GlobalSubscriptionEvent } from '../events';\n\nexport const streamTrackingGlobalPosition = (\n currentEvents: ReadEvent<Event, ReadEventMetadataWithGlobalPosition>[],\n) => new CaughtUpTransformStream(currentEvents);\n\nexport class CaughtUpTransformStream extends TransformStream<\n ReadEvent<Event, ReadEventMetadataWithGlobalPosition>,\n | ReadEvent<Event, ReadEventMetadataWithGlobalPosition>\n | GlobalSubscriptionEvent\n> {\n private _currentPosition: bigint;\n private _logPosition: bigint;\n\n constructor(events: ReadEvent<Event, ReadEventMetadataWithGlobalPosition>[]) {\n super({\n start: (controller) => {\n let globalPosition = 0n;\n for (const event of events) {\n controller.enqueue(event);\n globalPosition = event.metadata.globalPosition;\n }\n controller.enqueue(globalStreamCaughtUp({ globalPosition }));\n },\n transform: (event, controller) => {\n this._currentPosition = event.metadata.globalPosition;\n controller.enqueue(event);\n\n if (this._currentPosition < this._logPosition) return;\n\n controller.enqueue(\n globalStreamCaughtUp({ globalPosition: this._currentPosition }),\n );\n },\n });\n\n this._currentPosition = this._logPosition =\n events.length > 0\n ? events[events.length - 1]!.metadata.globalPosition\n : 0n;\n }\n\n public set logPosition(value: bigint) {\n this._logPosition = value;\n }\n}\n","import retry from 'async-retry';\n\nexport type AsyncRetryOptions = retry.Options & {\n shouldRetryError?: (error: unknown) => boolean;\n};\n\nexport const NoRetries: AsyncRetryOptions = { retries: 0 };\n\nexport const asyncRetry = async <T>(\n fn: () => Promise<T>,\n opts?: AsyncRetryOptions,\n): Promise<T> => {\n if (opts === undefined || opts.retries === 0) return fn();\n\n return retry(\n async (bail) => {\n try {\n return await fn();\n } catch (error) {\n if (opts?.shouldRetryError && !opts.shouldRetryError(error)) {\n bail(error as Error);\n }\n throw error;\n }\n },\n opts ?? { retries: 0 },\n );\n};\n","import { v4 as uuid } from 'uuid';\nimport { TransformStream } from 'web-streams-polyfill';\n\nexport const notifyAboutNoActiveReadersStream = <Item>(\n onNoActiveReaderCallback: (\n stream: NotifyAboutNoActiveReadersStream<Item>,\n ) => void,\n options: { streamId?: string; intervalCheckInMs?: number } = {},\n) => new NotifyAboutNoActiveReadersStream(onNoActiveReaderCallback, options);\n\nexport class NotifyAboutNoActiveReadersStream<Item> extends TransformStream<\n Item,\n Item\n> {\n private checkInterval: NodeJS.Timeout | null = null;\n public readonly streamId: string;\n private _isStopped: boolean = false;\n public get hasActiveSubscribers() {\n return !this._isStopped;\n }\n\n constructor(\n private onNoActiveReaderCallback: (\n stream: NotifyAboutNoActiveReadersStream<Item>,\n ) => void,\n options: { streamId?: string; intervalCheckInMs?: number } = {},\n ) {\n super({\n cancel: (reason) => {\n console.log('Stream was canceled. Reason:', reason);\n this.stopChecking();\n },\n });\n this.streamId = options?.streamId ?? uuid();\n\n this.onNoActiveReaderCallback = onNoActiveReaderCallback;\n\n this.startChecking(options?.intervalCheckInMs ?? 20);\n }\n\n private startChecking(interval: number) {\n this.checkInterval = setInterval(() => {\n this.checkNoActiveReader();\n }, interval);\n }\n\n private stopChecking() {\n if (!this.checkInterval) return;\n\n clearInterval(this.checkInterval);\n this.checkInterval = null;\n this._isStopped = true;\n this.onNoActiveReaderCallback(this);\n }\n\n private checkNoActiveReader() {\n if (!this.readable.locked && !this._isStopped) {\n this.stopChecking();\n }\n }\n}\n","export class ParseError extends Error {\n constructor(text: string) {\n super(`Cannot parse! ${text}`);\n }\n}\n\nexport type Mapper<From, To = From> =\n | ((value: unknown) => To)\n | ((value: Partial<From>) => To)\n | ((value: From) => To)\n | ((value: Partial<To>) => To)\n | ((value: To) => To)\n | ((value: Partial<To | From>) => To)\n | ((value: To | From) => To);\n\nexport type MapperArgs<From, To = From> = Partial<From> &\n From &\n Partial<To> &\n To;\n\nexport type ParseOptions<From, To = From> = {\n reviver?: (key: string, value: unknown) => unknown;\n map?: Mapper<From, To>;\n typeCheck?: <To>(value: unknown) => value is To;\n};\n\nexport type StringifyOptions<From, To = From> = {\n map?: Mapper<From, To>;\n};\n\nexport const JSONParser = {\n stringify: <From, To = From>(\n value: From,\n options?: StringifyOptions<From, To>,\n ) => {\n return JSON.stringify(\n options?.map ? options.map(value as MapperArgs<From, To>) : value,\n //TODO: Consider adding support to DateTime and adding specific format to mark that's a bigint\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n (_, v) => (typeof v === 'bigint' ? v.toString() : v),\n );\n },\n parse: <From, To = From>(\n text: string,\n options?: ParseOptions<From, To>,\n ): To | undefined => {\n const parsed: unknown = JSON.parse(text, options?.reviver);\n\n if (options?.typeCheck && !options?.typeCheck<To>(parsed))\n throw new ParseError(text);\n\n return options?.map\n ? options.map(parsed as MapperArgs<From, To>)\n : (parsed as To | undefined);\n },\n};\n","import { JSONParser } from '../serialization';\nimport type { DefaultRecord } from '../typing';\nimport { deepEquals } from '../utils';\n\nexport class AssertionError extends Error {\n constructor(message: string) {\n super(message);\n }\n}\n\nexport const isSubset = (superObj: unknown, subObj: unknown): boolean => {\n const sup = superObj as DefaultRecord;\n const sub = subObj as DefaultRecord;\n\n assertOk(sup);\n assertOk(sub);\n\n return Object.keys(sub).every((ele: string) => {\n if (typeof sub[ele] == 'object') {\n return isSubset(sup[ele], sub[ele]);\n }\n return sub[ele] === sup[ele];\n });\n};\n\nexport const assertFails = (message?: string) => {\n throw new AssertionError(message ?? 'That should not ever happened, right?');\n};\n\nexport const assertThrowsAsync = async <TError extends Error>(\n fun: () => Promise<void>,\n errorCheck?: (error: Error) => boolean,\n): Promise<TError> => {\n try {\n await fun();\n throw new AssertionError(\"Function didn't throw expected error\");\n } catch (error) {\n const typedError = error as TError;\n if (errorCheck) assertTrue(errorCheck(typedError));\n return typedError;\n }\n};\n\nexport const assertThrows = <TError extends Error>(\n fun: () => void,\n errorCheck?: (error: Error) => boolean,\n): TError => {\n try {\n fun();\n throw new AssertionError(\"Function didn't throw expected error\");\n } catch (error) {\n const typedError = error as TError;\n if (errorCheck) assertTrue(errorCheck(typedError));\n return typedError;\n }\n};\n\nexport const assertRejects = async <T, TError extends Error = Error>(\n promise: Promise<T>,\n errorCheck?: ((error: TError) => boolean) | TError,\n) => {\n try {\n await promise;\n throw new AssertionError(\"Function didn't throw expected error\");\n } catch (error) {\n if (!errorCheck) return;\n\n if (errorCheck instanceof Error) assertDeepEqual(error, errorCheck);\n else assertTrue(errorCheck(error as TError));\n }\n};\n\nexport const assertMatches = (\n actual: unknown,\n expected: unknown,\n message?: string,\n) => {\n if (!isSubset(actual, expected))\n throw new AssertionError(\n message ??\n `subObj:\\n${JSONParser.stringify(expected)}\\nis not subset of\\n${JSONParser.stringify(actual)}`,\n );\n};\n\nexport const assertDeepEqual = <T = unknown>(\n actual: T,\n expected: T,\n message?: string,\n) => {\n if (!deepEquals(actual, expected))\n throw new AssertionError(\n message ??\n `subObj:\\n${JSONParser.stringify(expected)}\\nis not equal to\\n${JSONParser.stringify(actual)}`,\n );\n};\n\nexport const assertNotDeepEqual = <T = unknown>(\n actual: T,\n expected: T,\n message?: string,\n) => {\n if (deepEquals(actual, expected))\n throw new AssertionError(\n message ??\n `subObj:\\n${JSONParser.stringify(expected)}\\nis equals to\\n${JSONParser.stringify(actual)}`,\n );\n};\n\nexport const assertThat = <T>(item: T) => {\n return {\n isEqualTo: (other: T) => assertTrue(deepEquals(item, other)),\n };\n};\n\nexport function assertFalse(\n condition: boolean,\n message?: string,\n): asserts condition is false {\n if (condition) throw new AssertionError(message ?? `Condition is false`);\n}\n\nexport function assertTrue(\n condition: boolean,\n message?: string,\n): asserts condition is true {\n if (!condition) throw new AssertionError(message ?? `Condition is false`);\n}\n\nexport function assertOk<T>(\n obj: T | null | undefined,\n message?: string,\n): asserts obj is T {\n if (!obj) throw new AssertionError(message ?? `Condition is not truthy`);\n}\n\nexport function assertEqual<T>(\n expected: T | null | undefined,\n actual: T | null | undefined,\n message?: string,\n): void {\n if (expected !== actual)\n throw new AssertionError(\n `${message ?? 'Objects are not equal'}:\\nExpected: ${JSONParser.stringify(expected)}\\nActual:${JSONParser.stringify(actual)}`,\n );\n}\n\nexport function assertNotEqual<T>(\n obj: T | null | undefined,\n other: T | null | undefined,\n message?: string,\n): void {\n if (obj === other)\n throw new AssertionError(\n message ?? `Objects are equal: ${JSONParser.stringify(obj)}`,\n );\n}\n\nexport function assertIsNotNull<T extends object | bigint>(\n result: T | null,\n): asserts result is T {\n assertNotEqual(result, null);\n assertOk(result);\n}\n\nexport function assertIsNull<T extends object>(\n result: T | null,\n): asserts result is null {\n assertEqual(result, null);\n}\n\ntype Call = {\n arguments: unknown[];\n result: unknown;\n target: unknown;\n this: unknown;\n};\n\nexport type ArgumentMatcher = (arg: unknown) => boolean;\n\nexport const argValue =\n <T>(value: T): ArgumentMatcher =>\n (arg) =>\n deepEquals(arg, value);\n\nexport const argMatches =\n <T>(matches: (arg: T) => boolean): ArgumentMatcher =>\n (arg) =>\n matches(arg as T);\n\n// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\nexport type MockedFunction = Function & { mock?: { calls: Call[] } };\n\nexport function verifyThat(fn: MockedFunction) {\n return {\n calledTimes: (times: number) => {\n assertEqual(fn.mock?.calls?.length, times);\n },\n notCalled: () => {\n assertEqual(fn?.mock?.calls?.length, 0);\n },\n called: () => {\n assertTrue(\n fn.mock?.calls.length !== undefined && fn.mock.calls.length > 0,\n );\n },\n calledWith: (...args: unknown[]) => {\n assertTrue(\n fn.mock?.calls.length !== undefined &&\n fn.mock.calls.length >= 1 &&\n fn.mock.calls.some((call) => deepEquals(call.arguments, args)),\n );\n },\n calledOnceWith: (...args: unknown[]) => {\n assertTrue(\n fn.mock?.calls.length !== undefined &&\n fn.mock.calls.length === 1 &&\n fn.mock.calls.some((call) => deepEquals(call.arguments, args)),\n );\n },\n calledWithArgumentMatching: (...matches: ArgumentMatcher[]) => {\n assertTrue(\n fn.mock?.calls.length !== undefined && fn.mock.calls.length >= 1,\n );\n assertTrue(\n fn.mock?.calls.length !== undefined &&\n fn.mock.calls.length >= 1 &&\n fn.mock.calls.some(\n (call) =>\n call.arguments &&\n call.arguments.length >= matches.length &&\n matches.every((match, index) => match(call.arguments[index])),\n ),\n );\n },\n notCalledWithArgumentMatching: (...matches: ArgumentMatcher[]) => {\n assertFalse(\n fn.mock?.calls.length !== undefined &&\n fn.mock.calls.length >= 1 &&\n fn.mock.calls[0]!.arguments &&\n fn.mock.calls[0]!.arguments.length >= matches.length &&\n matches.every((match, index) =>\n match(fn.mock!.calls[0]!.arguments[index]),\n ),\n );\n },\n };\n}\n\nexport const assertThatArray = <T>(array: T[]) => {\n return {\n isEmpty: () => assertEqual(array.length, 0),\n isNotEmpty: () => assertNotEqual(array.length, 0),\n hasSize: (length: number) => assertEqual(array.length, length),\n containsElements: (...other: T[]) => {\n assertTrue(other.every((ts) => other.some((o) => deepEquals(ts, o))));\n },\n containsExactlyInAnyOrder: (...other: T[]) => {\n assertEqual(array.length, other.length);\n assertTrue(array.every((ts) => other.some((o) => deepEquals(ts, o))));\n },\n containsExactlyInAnyOrderElementsOf: (other: T[]) => {\n assertEqual(array.length, other.length);\n assertTrue(array.every((ts) => other.some((o) => deepEquals(ts, o))));\n },\n containsExactlyElementsOf: (other: T[]) => {\n assertEqual(array.length, other.length);\n for (let i = 0; i < array.length; i++) {\n assertTrue(deepEquals(array[i], other[i]));\n }\n },\n containsExactly: (elem: T) => {\n assertEqual(array.length, 1);\n assertTrue(deepEquals(array[0], elem));\n },\n contains: (elem: T) => {\n assertTrue(array.some((a) => deepEquals(a, elem)));\n },\n containsOnlyOnceElementsOf: (other: T[]) => {\n assertTrue(\n other\n .map((o) => array.filter((a) => deepEquals(a, o)).length)\n .filter((a) => a === 1).length === other.length,\n );\n },\n containsAnyOf: (...other: T[]) => {\n assertTrue(array.some((a) => other.some((o) => deepEquals(a, o))));\n },\n allMatch: (matches: (item: T) => boolean) => {\n assertTrue(array.every(matches));\n },\n anyMatches: (matches: (item: T) => boolean) => {\n assertTrue(array.some(matches));\n },\n allMatchAsync: async (\n matches: (item: T) => Promise<boolean>,\n ): Promise<void> => {\n for (const item of array) {\n assertTrue(await matches(item));\n }\n },\n };\n};\n",null,"import { type Request, type Response } from 'express';\nimport { ProblemDocument } from 'http-problem-details';\nimport { setETag, type ETag } from './etag';\n\nexport type ErrorToProblemDetailsMapping = (\n error: Error,\n request: Request,\n) => ProblemDocument | undefined;\n\nexport type HttpResponseOptions = {\n body?: unknown;\n location?: string;\n eTag?: ETag;\n};\nexport const DefaultHttpResponseOptions: HttpResponseOptions = {};\n\nexport type HttpProblemResponseOptions = {\n location?: string;\n eTag?: ETag;\n} & Omit<HttpResponseOptions, 'body'> &\n (\n | {\n problem: ProblemDocument;\n }\n | { problemDetails: string }\n );\nexport const DefaultHttpProblemResponseOptions: HttpProblemResponseOptions = {\n problemDetails: 'Error occured!',\n};\n\nexport type CreatedHttpResponseOptions = (\n | {\n createdId: string;\n }\n | {\n createdId?: string;\n url: string;\n }\n) &\n HttpResponseOptions;\n\nexport const sendCreated = (\n response: Response,\n { eTag, ...options }: CreatedHttpResponseOptions,\n): void =>\n send(response, 201, {\n location:\n 'url' in options\n ? options.url\n : `${response.req.url}/${options.createdId}`,\n body: 'createdId' in options ? { id: options.createdId } : undefined,\n eTag,\n });\n\nexport type AcceptedHttpResponseOptions = {\n location: string;\n} & HttpResponseOptions;\n\nexport const sendAccepted = (\n response: Response,\n options: AcceptedHttpResponseOptions,\n): void => send(response, 202, options);\n\nexport type NoContentHttpResponseOptions = Omit<HttpResponseOptions, 'body'>;\n\nexport const send = (\n response: Response,\n statusCode: number,\n options?: HttpResponseOptions,\n): void => {\n const { location, body, eTag } = options ?? DefaultHttpResponseOptions;\n // HEADERS\n if (eTag) setETag(response, eTag);\n if (location) response.setHeader('Location', location);\n\n if (body) {\n response.statusCode = statusCode;\n response.send(body);\n } else {\n response.sendStatus(statusCode);\n }\n};\n\nexport const sendProblem = (\n response: Response,\n statusCode: number,\n options?: HttpProblemResponseOptions,\n): void => {\n options = options ?? DefaultHttpProblemResponseOptions;\n\n const { location, eTag } = options;\n\n const problemDetails =\n 'problem' in options\n ? options.problem\n : new ProblemDocument({\n detail: options.problemDetails,\n status: statusCode,\n });\n\n // HEADERS\n if (eTag) setETag(response, eTag);\n if (location) response.setHeader('Location', location);\n\n response.setHeader('Content-Type', 'application/problem+json');\n\n response.statusCode = statusCode;\n response.json(problemDetails);\n};\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@event-driven-io/emmett-expressjs",
3
- "version": "0.20.2-alpha.4",
3
+ "version": "0.20.2-alpha.6",
4
4
  "type": "module",
5
5
  "description": "Emmett - Event Sourcing development made simple",
6
6
  "scripts": {
@@ -49,7 +49,7 @@
49
49
  "dependencies": {},
50
50
  "devDependencies": {},
51
51
  "peerDependencies": {
52
- "@event-driven-io/emmett": "0.20.2-alpha.4",
52
+ "@event-driven-io/emmett": "0.20.2-alpha.6",
53
53
  "@types/express": "^4.17.21",
54
54
  "@types/supertest": "^6.0.2",
55
55
  "express": "^4.19.2",