@event-driven-io/emmett-expressjs 0.20.1 → 0.20.2-alpha.1
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 +5 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +5 -5
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function
|
|
2
|
-
${
|
|
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 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 V=(e,t={})=>new R(e,t),R= (_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 j=class extends Error{constructor(e){super(`Cannot parse! ${e}`)}},l={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 j(e);return _optionalChain([t, 'optionalAccess', _10 => _10.map])?t.map(r):r}};var $=e=>new (0, _webstreamspolyfill.TransformStream)({transform(t,r){e(t)&&r.enqueue(t)}}),W=e=>new (0, _webstreamspolyfill.TransformStream)({transform(t,r){r.enqueue(e(t))}}),J=(e,t)=>new x(e,t),x=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}},L=(e,t,r={forever:!0,minTimeout:25})=>new (0, _webstreamspolyfill.TransformStream)({start(s){B(()=>X(e,t,s),r).catch(n=>{s.error(n)})}}),X=async(e,t,r)=>{let n=e().getReader();try{let o;do{let i=await n.read();o=i.done,await t(i,r),o&&r.terminate()}while(!o)}finally{n.releaseLock()}},z=e=>new A(e),A= (_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),ee=e=>new (0, _webstreamspolyfill.TransformStream)({transform(t,r){r.enqueue(t),e(t)&&r.terminate()}}),re=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),ae=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)}}),ie={filter:$,take:ne,TakeTransformStream:C,skip:z,SkipTransformStream:A,map:W,notifyAboutNoActiveReadersStream:V,NotifyAboutNoActiveReadersStream:R,reduce:J,ReduceTransformStream:x,retry:L,stopAfter:ee,stopOn:re,waitAtMost:ae},{retry:ct}=ie;var d=class extends Error{constructor(e){super(e)}},O=(e,t)=>{let r=e,s=t;return w(r),w(s),Object.keys(s).every(n=>typeof s[n]=="object"?O(r[n],s[n]):s[n]===r[n])},E=e=>{throw new d(_nullishCoalesce(e, () => ("That should not ever happened, right?")))};var f=(e,t,r)=>{if(!O(e,t))throw new d(_nullishCoalesce(r, () => (`subObj:
|
|
2
|
+
${l.stringify(t)}
|
|
3
3
|
is not subset of
|
|
4
|
-
${
|
|
5
|
-
Expected: ${
|
|
6
|
-
Actual:${
|
|
4
|
+
${l.stringify(e)}`)))};function w(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: ${l.stringify(e)}
|
|
6
|
+
Actual:${l.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,n)=>{let o=await e.appendToStream(r,s,n),i=_nullishCoalesce(t.get(r), () => ([r,[]]));return t.set(r,[r,[...i[1],...s]]),o},appendedEvents:t,setup:async(r,s)=>e.appendToStream(r,s)}};var _httpproblemdetails = require('http-problem-details');var N=e=>(t,r,s,n)=>{let o;e&&(o=e(t,r)),o=_nullishCoalesce(o, () => (pe(t))),h(s,o.status,{problem:o})},pe=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:n,disableJsonMiddleware:o,disableUrlEncodingMiddleware:i,disableProblemDetailsMiddleware:a}=e,c=_express.Router.call(void 0, );t.set("etag",_nullishCoalesce(n, () => (!1))),o||t.use(_express2.default.json()),i||t.use(_express2.default.urlencoded({extended:!0}));for(let p of r)p(c);return t.use(c),a||t.use(N(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+.*)"/,le= 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))(le||{}),de= exports.isWeakETag =e=>_.test(e),fe= exports.getWeakETagValue =e=>{let t=_.exec(e);if(t===null||t.length===0)throw new Error("WRONG_WEAK_ETAG_FORMAT");return t[1]},Ot= exports.toWeakETag =e=>`W/"${e}"`,ge= exports.getETagFromIfMatch =e=>{let t=e.headers[T.IF_MATCH];if(t===void 0)throw new Error("MISSING_IF_MATCH_HEADER");return t},It= 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)},Nt= exports.getETagValueFromIfMatch =e=>{let t=ge(e);return de(t)?fe(t):t};var Ht=e=>async(t,r,s)=>(await Promise.resolve(e(t)))(r),Dt= exports.OK =e=>t=>{m(t,200,e)},kt= exports.Created =e=>t=>{P(t,e)},qt= exports.Accepted =e=>t=>{M(t,e)},Vt= exports.NoContent =e=>he(204,e),he= exports.HttpResponse =(e,t)=>r=>{m(r,e,t)},Ft= exports.BadRequest =e=>u(400,e),Bt= exports.Forbidden =e=>u(403,e),jt= exports.NotFound =e=>u(404,e),Yt= exports.Conflict =e=>u(409,e),$t= exports.PreconditionFailed =e=>u(412,e),u= exports.HttpProblem =(e,t)=>r=>{h(r,e,t)};var Ee={},ve= exports.DefaultHttpProblemResponseOptions ={problemDetails:"Error occured!"},P= 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}),M= exports.sendAccepted =(e,t)=>m(e,202,t),m= exports.send =(e,t,r)=>{let{location:s,body:n,eTag:o}=_nullishCoalesce(r, () => (Ee));o&&S(e,o),s&&e.setHeader("Location",s),n?(e.statusCode=t,e.send(n)):e.sendStatus(t)},h= exports.sendProblem =(e,t,r)=>{r=_nullishCoalesce(r, () => (ve));let{location:s,eTag:n}=r,o="problem"in r?r.problem:new (0, _httpproblemdetails.ProblemDocument)({detail:r.problemDetails,status:t});n&&S(e,n),s&&e.setHeader("Location",s),e.setHeader("Content-Type","application/problem+json"),e.statusCode=t,e.json(o)};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()),n=t(s);return{when:o=>{let i=async()=>{for(let a of r)await a(_supertest2.default.call(void 0, n));return o(_supertest2.default.call(void 0, n))};return{then:async a=>{let c=await i();a.forEach(p=>{p(c)===!1&&_assert2.default.fail()})}}}}}};var sr=(e,t)=>[e,t],nr= exports.expect =(e,t)=>[e,t],or= exports.expectNewEvents =(e,t)=>[e,t],be= exports.expectResponse =(e,t)=>r=>{let{body:s,headers:n}=_nullishCoalesce(t, () => ({}));I(e,r.statusCode,"Response code doesn't match"),s&&f(r.body,s),n&&f(r.headers,n)},ar= exports.expectError =(e,t)=>be(e,t?{body:t}:void 0),ir= exports.ApiSpecification ={for:(e,t)=>(...r)=>{let s=g(e()),n=t(s);return{when:o=>{let i=async()=>{for(let[a,c]of r)await s.setup(a,c);return o(_supertest2.default.call(void 0, n))};return{then:async a=>{let c=await i();if(typeof a=="function")a(c)===!1&&E();else if(Array.isArray(a)){let[p,...b]=a;typeof p=="function"&&p(c)===!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 = Yt; exports.Created = kt; exports.DefaultHttpProblemResponseOptions = ve; exports.DefaultHttpResponseOptions = Ee; exports.ETagErrors = le; exports.Forbidden = Bt; exports.HeaderNames = T; exports.HttpProblem = u; exports.HttpResponse = he; exports.NoContent = Vt; exports.NotFound = jt; exports.OK = Dt; exports.PreconditionFailed = $t; exports.WeakETagRegex = _; exports.existingStream = sr; exports.expect = nr; exports.expectError = ar; exports.expectNewEvents = or; exports.expectResponse = be; exports.getApplication = Rt; exports.getETagFromIfMatch = ge; exports.getETagFromIfNotMatch = It; exports.getETagValueFromIfMatch = Nt; exports.getWeakETagValue = fe; exports.isWeakETag = de; exports.on = Ht; exports.send = m; exports.sendAccepted = M; exports.sendCreated = P; exports.sendProblem = h; exports.setETag = S; exports.startAPI = xt; exports.toWeakETag = Ot;
|
|
7
7
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/application.ts","../../emmett/src/config/plugins/index.ts","../../emmett/src/validation/index.ts","../../emmett/src/eventStore/inMemoryEventStore.ts","../../emmett-shims/src/index.ts","../../emmett/src/utils/retry.ts","../../emmett/src/typing/event.ts","../../emmett/src/eventStore/events/index.ts","../../emmett/src/eventStore/subscriptions/caughtUpTransformStream.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","event","type","data","metadata","GlobalStreamCaughtUpType","globalStreamCaughtUp","streamsPolyfill","ReadableStream","WritableStream","TransformStream","isBun","streams","src_default","CaughtUpTransformStream","m","events","controller","globalPosition","value","notifyAboutNoActiveReadersStream","onNoActiveReaderCallback","options","NotifyAboutNoActiveReadersStream","reason","uuid","interval","asyncRetry","fn","opts","retry","bail","error","ParseError","text"],"mappings":"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","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 { Command as CliCommand } from 'commander';\n\nexport type EmmettPluginConfig =\n | {\n name: string;\n register: EmmettPluginRegistration[];\n }\n | string;\n\nexport type EmmettPluginType = 'cli';\n\nexport type EmmettCliPluginRegistration = { pluginType: 'cli'; path?: string };\n\nexport type EmmettPluginRegistration = EmmettCliPluginRegistration;\n\nexport type EmmettCliPlugin = {\n pluginType: 'cli';\n name: string;\n registerCommands: (program: CliCommand) => Promise<void> | void;\n};\n\nexport type EmmettPlugin = EmmettCliPlugin;\n\nexport const isPluginConfig = (\n plugin: Partial<EmmettPluginConfig> | string | undefined,\n): plugin is EmmettPluginConfig =>\n plugin !== undefined &&\n (typeof plugin === 'string' ||\n ('name' in plugin &&\n plugin.name !== undefined &&\n typeof plugin.name === 'string'));\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 {\n ReadableStream,\n TransformStream,\n WritableStream,\n} from 'web-streams-polyfill';\n\nconst streamsPolyfill = { ReadableStream, WritableStream, TransformStream };\n\n// https://github.com/jsdom/jsdom/issues/1537#issuecomment-229405327\n\nconst isJsDom = (): boolean => {\n // @ts-expect-error global object check\n const globalWindow = window as { name: string };\n // @ts-expect-error global object check\n const globalNavigator = navigator as { userAgent: string };\n\n return (\n (typeof globalWindow !== 'undefined' && globalWindow.name === 'nodejs') ||\n (typeof globalNavigator !== 'undefined' &&\n 'userAgent' in globalNavigator &&\n typeof globalNavigator.userAgent === 'string' &&\n (globalNavigator.userAgent.includes('Node.js') ||\n globalNavigator.userAgent.includes('jsdom')))\n );\n};\n\nconst isDeno = (): boolean => {\n // @ts-expect-error global object check\n const globalDeno = Deno as { version: { deno: unknown } };\n\n return (\n typeof globalDeno !== 'undefined' &&\n typeof globalDeno.version !== 'undefined' &&\n typeof globalDeno.version.deno !== 'undefined'\n );\n};\n\nconst isBun =\n typeof process !== 'undefined' &&\n process.versions != null &&\n process.versions.bun != null;\n\nexport { isBrowser, isBun, isDeno, isJsDom, isNode, isWebWorker };\n\nlet streams: typeof streamsPolyfill;\n\nif (\n globalThis &&\n globalThis.WritableStream &&\n globalThis.ReadableStream &&\n globalThis.TransformStream\n) {\n streams = globalThis as typeof streamsPolyfill;\n} else {\n try {\n streams = (await import('node:stream/web')) as typeof streamsPolyfill;\n } catch {\n // Just falling back to the default polyfill\n streams = streamsPolyfill;\n }\n}\n\nexport default streams;\n\n// Use a type-only import/export to re-export all types\nexport type * from 'web-streams-polyfill';\n\nconst isBrowser = (): boolean => {\n // @ts-expect-error global object check\n const globalWindow = window as { document: unknown };\n\n return (\n typeof globalWindow !== 'undefined' &&\n typeof globalWindow.document !== 'undefined'\n );\n};\n\nconst isNode = (): boolean =>\n typeof process !== 'undefined' &&\n process.versions != null &&\n process.versions.node != null;\n\nconst isWebWorker = (): boolean => {\n // @ts-expect-error global object check\n const globalSelf = self as { constructor: { name: unknown } | undefined };\n\n return (\n typeof globalSelf === 'object' &&\n !!globalSelf.constructor &&\n globalSelf.constructor.name === 'DedicatedWorkerGlobalScope'\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 type { DefaultRecord, Flavour } from './';\n\nexport type Event<\n EventType extends string = string,\n EventData extends DefaultRecord = DefaultRecord,\n EventMetaData extends DefaultRecord = DefaultRecord,\n> = Flavour<\n Readonly<{\n type: EventType;\n data: EventData;\n metadata?: EventMetaData;\n }>,\n 'Event'\n>;\n\nexport type EventTypeOf<T extends Event> = T['type'];\nexport type EventDataOf<T extends Event> = T['data'];\nexport type EventMetaDataOf<T extends Event> = T['metadata'];\n\nexport type CanHandle<T extends Event> = EventTypeOf<T>[];\n\nexport type CreateEventType<\n EventType extends string,\n EventData extends DefaultRecord,\n EventMetaData extends DefaultRecord | undefined,\n> = Readonly<{\n type: EventType;\n data: EventData;\n metadata?: EventMetaData;\n}>;\n\nexport const event = <EventType extends Event>(\n type: EventTypeOf<EventType>,\n data: EventDataOf<EventType>,\n metadata?: EventMetaDataOf<EventType>,\n): CreateEventType<\n EventTypeOf<EventType>,\n EventDataOf<EventType>,\n EventMetaDataOf<EventType>\n> => {\n return {\n type,\n data,\n metadata,\n };\n};\n\nexport type ReadEvent<\n EventType extends Event = Event,\n EventMetaDataType extends EventMetaDataOf<EventType> &\n ReadEventMetadata = EventMetaDataOf<EventType> & ReadEventMetadata,\n> = CreateEventType<\n EventTypeOf<EventType>,\n EventDataOf<EventType>,\n EventMetaDataType\n> &\n EventType & { metadata: EventMetaDataType };\n\nexport type ReadEventMetadata = Readonly<{\n eventId: string;\n streamPosition: bigint;\n streamName: string;\n}>;\n\nexport type ReadEventMetadataWithGlobalPosition = ReadEventMetadata & {\n globalPosition: bigint;\n};\n","import {\n event,\n type Event,\n type EventDataOf,\n type ReadEvent,\n type ReadEventMetadataWithGlobalPosition,\n} from '../../typing';\n\nexport const GlobalStreamCaughtUpType = '__emt:GlobalStreamCaughtUp';\n\nexport type GlobalStreamCaughtUp = Event<\n '__emt:GlobalStreamCaughtUp',\n { globalPosition: bigint },\n { globalPosition: bigint }\n>;\n\nexport const isGlobalStreamCaughtUp = (\n event: Event,\n): event is GlobalStreamCaughtUp => event.type === GlobalStreamCaughtUpType;\n\nexport const caughtUpEventFrom =\n (position: bigint) =>\n (\n event: ReadEvent<Event, ReadEventMetadataWithGlobalPosition>,\n ): event is ReadEvent<\n GlobalStreamCaughtUp,\n ReadEventMetadataWithGlobalPosition\n > =>\n event.type === GlobalStreamCaughtUpType &&\n event.metadata?.globalPosition >= position;\n\nexport const globalStreamCaughtUp = (\n data: EventDataOf<GlobalStreamCaughtUp>,\n): GlobalStreamCaughtUp =>\n event<GlobalStreamCaughtUp>(GlobalStreamCaughtUpType, data, {\n globalPosition: data.globalPosition,\n });\n\nexport const isSubscriptionEvent = (\n event: Event,\n): event is GlobalSubscriptionEvent => isGlobalStreamCaughtUp(event);\n\nexport const isNotInternalEvent = (event: Event): boolean =>\n !isGlobalStreamCaughtUp(event);\n\nexport type GlobalSubscriptionEvent = GlobalStreamCaughtUp;\n","import streams from '@event-driven-io/emmett-shims';\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 streams.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 streams from '@event-driven-io/emmett-shims';\nimport { v4 as uuid } from 'uuid';\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<\n Item,\n> extends streams.TransformStream<Item, Item> {\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"]}
|
|
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/handler.ts"],"names":["isNumber","val","notifyAboutNoActiveReadersStream","onNoActiveReaderCallback","options","NotifyAboutNoActiveReadersStream","TransformStream","reason","uuid","interval","asyncRetry","fn","opts","retry","bail","error","ParseError","text","sendAccepted"],"mappings":"AAAA,u1BAAO,oFCA2C,wEAEjC,ICMJA,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,CDpDO,IAAMC,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;AC9E4yD;AD4InzD;AAA8C,UAAA;AE9FrFC,OAAAA","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 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"]}
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import"express-async-errors";import
|
|
2
|
-
${
|
|
1
|
+
import"express-async-errors";import v,{Router as ue}from"express";import"express-async-errors";import me from"http";var y=e=>typeof e=="number"&&e===e;import{v4 as Ye}from"uuid";import{TransformStream as Ue}from"web-streams-polyfill";import{v4 as Ge}from"uuid";import{v4 as k}from"uuid";import{TransformStream as q}from"web-streams-polyfill";import F from"async-retry";import{ReadableStream as Ze}from"web-streams-polyfill";import"web-streams-polyfill";import{TransformStream as Y}from"web-streams-polyfill";import{TransformStream as U}from"web-streams-polyfill";import{TransformStream as G}from"web-streams-polyfill";import{TransformStream as K}from"web-streams-polyfill";import{TransformStream as Z}from"web-streams-polyfill";import{TransformStream as Q}from"web-streams-polyfill";import{TransformStream as te}from"web-streams-polyfill";import{TransformStream as se}from"web-streams-polyfill";import{TransformStream as oe}from"web-streams-polyfill";var V=(e,t={})=>new R(e,t),R=class extends q{constructor(e,t={}){super({cancel:r=>{console.log("Stream was canceled. Reason:",r),this.stopChecking()}}),this.onNoActiveReaderCallback=e,this.streamId=t?.streamId??k(),this.onNoActiveReaderCallback=e,this.startChecking(t?.intervalCheckInMs??20)}checkInterval=null;streamId;_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()}};var B=async(e,t)=>t===void 0||t.retries===0?e():F(async r=>{try{return await e()}catch(s){throw t?.shouldRetryError&&!t.shouldRetryError(s)&&r(s),s}},t??{retries:0});var j=class extends Error{constructor(e){super(`Cannot parse! ${e}`)}},l={stringify:(e,t)=>JSON.stringify(t?.map?t.map(e):e,(r,s)=>typeof s=="bigint"?s.toString():s),parse:(e,t)=>{let r=JSON.parse(e,t?.reviver);if(t?.typeCheck&&!t?.typeCheck(r))throw new j(e);return t?.map?t.map(r):r}};var $=e=>new Y({transform(t,r){e(t)&&r.enqueue(t)}}),W=e=>new U({transform(t,r){r.enqueue(e(t))}}),J=(e,t)=>new x(e,t),x=class extends G{accumulator;reducer;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}},L=(e,t,r={forever:!0,minTimeout:25})=>new K({start(s){B(()=>X(e,t,s),r).catch(n=>{s.error(n)})}}),X=async(e,t,r)=>{let n=e().getReader();try{let o;do{let i=await n.read();o=i.done,await t(i,r),o&&r.terminate()}while(!o)}finally{n.releaseLock()}},z=e=>new A(e),A=class extends Z{count=0;skip;constructor(e){super({transform:(t,r)=>{this.count++,this.count>this.skip&&r.enqueue(t)}}),this.skip=e}},ee=e=>new Q({transform(t,r){r.enqueue(t),e(t)&&r.terminate()}}),re=e=>new te({async transform(t,r){if(!e(t)){r.enqueue(t);return}await Promise.resolve(),r.terminate()}}),ne=e=>new C(e),C=class extends se{count=0;limit;constructor(e){super({transform:(t,r)=>{this.count<this.limit?(this.count++,r.enqueue(t)):r.terminate()}}),this.limit=e}},ae=e=>new oe({start(t){let r=setTimeout(()=>{t.terminate()},e),s=t.terminate.bind(t);t.terminate=()=>{clearTimeout(r),s()}},transform(t,r){r.enqueue(t)}}),ie={filter:$,take:ne,TakeTransformStream:C,skip:z,SkipTransformStream:A,map:W,notifyAboutNoActiveReadersStream:V,NotifyAboutNoActiveReadersStream:R,reduce:J,ReduceTransformStream:x,retry:L,stopAfter:ee,stopOn:re,waitAtMost:ae},{retry:ct}=ie;var d=class extends Error{constructor(e){super(e)}},O=(e,t)=>{let r=e,s=t;return w(r),w(s),Object.keys(s).every(n=>typeof s[n]=="object"?O(r[n],s[n]):s[n]===r[n])},E=e=>{throw new d(e??"That should not ever happened, right?")};var f=(e,t,r)=>{if(!O(e,t))throw new d(r??`subObj:
|
|
2
|
+
${l.stringify(t)}
|
|
3
3
|
is not subset of
|
|
4
|
-
${
|
|
5
|
-
Expected: ${
|
|
6
|
-
Actual:${
|
|
4
|
+
${l.stringify(e)}`)};function w(e,t){if(!e)throw new d(t??"Condition is not truthy")}function I(e,t,r){if(e!==t)throw new d(`${r??"Objects are not equal"}:
|
|
5
|
+
Expected: ${l.stringify(e)}
|
|
6
|
+
Actual:${l.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,n)=>{let o=await e.appendToStream(r,s,n),i=t.get(r)??[r,[]];return t.set(r,[r,[...i[1],...s]]),o},appendedEvents:t,setup:async(r,s)=>e.appendToStream(r,s)}};import{ProblemDocument as ce}from"http-problem-details";var N=e=>(t,r,s,n)=>{let o;e&&(o=e(t,r)),o=o??pe(t),h(s,o.status,{problem:o})},pe=e=>{let t=500;return"errorCode"in e&&y(e.errorCode)&&e.errorCode>=100&&e.errorCode<600&&(t=e.errorCode),new ce({detail:e.message,status:t})};var Rt=e=>{let t=v(),{apis:r,mapError:s,enableDefaultExpressEtag:n,disableJsonMiddleware:o,disableUrlEncodingMiddleware:i,disableProblemDetailsMiddleware:a}=e,c=ue();t.set("etag",n??!1),o||t.use(v.json()),i||t.use(v.urlencoded({extended:!0}));for(let p of r)p(c);return t.use(c),a||t.use(N(s)),t},xt=(e,t={port:3e3})=>{let{port:r}=t,s=me.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"},_=/W\/"(-?\d+.*)"/,le=(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))(le||{}),de=e=>_.test(e),fe=e=>{let t=_.exec(e);if(t===null||t.length===0)throw new Error("WRONG_WEAK_ETAG_FORMAT");return t[1]},Ot=e=>`W/"${e}"`,ge=e=>{let t=e.headers[T.IF_MATCH];if(t===void 0)throw new Error("MISSING_IF_MATCH_HEADER");return t},It=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=(e,t)=>{e.setHeader(T.ETag,t)},Nt=e=>{let t=ge(e);return de(t)?fe(t):t};import"express";var Ht=e=>async(t,r,s)=>(await Promise.resolve(e(t)))(r),Dt=e=>t=>{m(t,200,e)},kt=e=>t=>{P(t,e)},qt=e=>t=>{M(t,e)},Vt=e=>he(204,e),he=(e,t)=>r=>{m(r,e,t)},Ft=e=>u(400,e),Bt=e=>u(403,e),jt=e=>u(404,e),Yt=e=>u(409,e),$t=e=>u(412,e),u=(e,t)=>r=>{h(r,e,t)};import"express";import{ProblemDocument as ye}from"http-problem-details";var Ee={},ve={problemDetails:"Error occured!"},P=(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}),M=(e,t)=>m(e,202,t),m=(e,t,r)=>{let{location:s,body:n,eTag:o}=r??Ee;o&&S(e,o),s&&e.setHeader("Location",s),n?(e.statusCode=t,e.send(n)):e.sendStatus(t)},h=(e,t,r)=>{r=r??ve;let{location:s,eTag:n}=r,o="problem"in r?r.problem:new ye({detail:r.problemDetails,status:t});n&&S(e,n),s&&e.setHeader("Location",s),e.setHeader("Content-Type","application/problem+json"),e.statusCode=t,e.json(o)};import H from"supertest";import Te from"assert";var zt={for:(e,t)=>(...r)=>{let s=g(e()),n=t(s);return{when:o=>{let i=async()=>{for(let a of r)await a(H(n));return o(H(n))};return{then:async a=>{let c=await i();a.forEach(p=>{p(c)===!1&&Te.fail()})}}}}}};import"express";import Se from"supertest";var sr=(e,t)=>[e,t],nr=(e,t)=>[e,t],or=(e,t)=>[e,t],be=(e,t)=>r=>{let{body:s,headers:n}=t??{};I(e,r.statusCode,"Response code doesn't match"),s&&f(r.body,s),n&&f(r.headers,n)},ar=(e,t)=>be(e,t?{body:t}:void 0),ir={for:(e,t)=>(...r)=>{let s=g(e()),n=t(s);return{when:o=>{let i=async()=>{for(let[a,c]of r)await s.setup(a,c);return o(Se(n))};return{then:async a=>{let c=await i();if(typeof a=="function")a(c)===!1&&E();else if(Array.isArray(a)){let[p,...b]=a;typeof p=="function"&&p(c)===!1&&E();let D=typeof p=="function"?b:a;f(Array.from(s.appendedEvents.values()),D)}}}}}}};export{qt as Accepted,zt as ApiE2ESpecification,ir as ApiSpecification,Ft as BadRequest,Yt as Conflict,kt as Created,ve as DefaultHttpProblemResponseOptions,Ee as DefaultHttpResponseOptions,le as ETagErrors,Bt as Forbidden,T as HeaderNames,u as HttpProblem,he as HttpResponse,Vt as NoContent,jt as NotFound,Dt as OK,$t as PreconditionFailed,_ as WeakETagRegex,sr as existingStream,nr as expect,ar as expectError,or as expectNewEvents,be as expectResponse,Rt as getApplication,ge as getETagFromIfMatch,It as getETagFromIfNotMatch,Nt as getETagValueFromIfMatch,fe as getWeakETagValue,de as isWeakETag,Ht as on,m as send,M as sendAccepted,P as sendCreated,h as sendProblem,S as setETag,xt as startAPI,Ot as toWeakETag};
|
|
7
7
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/application.ts","../../emmett/src/validation/index.ts","../../emmett/src/errors/index.ts","../../emmett/src/validation/dates.ts","../../emmett/src/config/plugins/index.ts","../../emmett/src/eventStore/eventStore.ts","../../emmett/src/typing/command.ts","../../emmett/src/typing/event.ts","../../emmett/src/typing/workflow.ts","../../emmett/src/eventStore/events/index.ts","../../emmett/src/eventStore/expectedVersion.ts","../../emmett/src/eventStore/inMemoryEventStore.ts","../../emmett-shims/src/index.ts","../../emmett/src/eventStore/subscriptions/caughtUpTransformStream.ts","../../emmett/src/eventStore/subscriptions/streamingCoordinator.ts","../../emmett/src/streaming/transformations/notifyAboutNoActiveReaders.ts","../../emmett/src/streaming/writers/writeToStream.ts","../../emmett/src/utils/deepEquals.ts","../../emmett/src/utils/iterators.ts","../../emmett/src/utils/merge.ts","../../emmett/src/utils/retry.ts","../../emmett/src/commandHandling/handleCommand.ts","../../emmett/src/commandHandling/handleCommandWithDecider.ts","../../emmett/src/messageBus/index.ts","../../emmett/src/projections/index.ts","../../emmett/src/serialization/json/JSONParser.ts","../../emmett/src/streaming/binaryArrays.ts","../../emmett/src/streaming/collectors/collect.ts","../../emmett/src/streaming/decoders/binary.ts","../../emmett/src/streaming/decoders/string.ts","../../emmett/src/streaming/decoders/json.ts","../../emmett/src/streaming/decoders/object.ts","../../emmett/src/streaming/decoders/composite.ts","../../emmett/src/streaming/generators/fromArray.ts","../../emmett/src/streaming/generators/index.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/streaming/transformations/index.ts","../../emmett/src/streaming/restream.ts","../../emmett/src/testing/assertions.ts","../../emmett/src/testing/deciderSpecification.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"],"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 { isNumber, isString } from '../validation';\n\nexport type ErrorConstructor<ErrorType extends Error> = new (\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ...args: any[]\n) => ErrorType;\n\nexport const isErrorConstructor = <ErrorType extends Error>(\n // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\n expect: Function,\n): expect is ErrorConstructor<ErrorType> => {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return (\n typeof expect === 'function' &&\n expect.prototype &&\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n expect.prototype.constructor === expect\n );\n};\n\nexport class EmmettError extends Error {\n public errorCode: number;\n\n constructor(\n options?: { errorCode: number; message?: string } | string | number,\n ) {\n const errorCode =\n options && typeof options === 'object' && 'errorCode' in options\n ? options.errorCode\n : isNumber(options)\n ? options\n : 500;\n const message =\n options && typeof options === 'object' && 'message' in options\n ? options.message\n : isString(options)\n ? options\n : `Error with status code '${errorCode}' ocurred during Emmett processing`;\n\n super(message);\n this.errorCode = errorCode;\n\n // 👇️ because we are extending a built-in class\n Object.setPrototypeOf(this, EmmettError.prototype);\n }\n}\n\nexport class ConcurrencyError extends EmmettError {\n constructor(\n public current: string | undefined,\n public expected: string,\n message?: string,\n ) {\n super({\n errorCode: 412,\n message:\n message ??\n `Expected version ${expected.toString()} does not match current ${current?.toString()}`,\n });\n\n // 👇️ because we are extending a built-in class\n Object.setPrototypeOf(this, ConcurrencyError.prototype);\n }\n}\n\nexport class ValidationError extends EmmettError {\n constructor(message?: string) {\n super({\n errorCode: 400,\n message: message ?? `Validation Error ocurred during Emmett processing`,\n });\n\n // 👇️ because we are extending a built-in class\n Object.setPrototypeOf(this, ValidationError.prototype);\n }\n}\n\nexport class IllegalStateError extends EmmettError {\n constructor(message?: string) {\n super({\n errorCode: 403,\n message: message ?? `Illegal State ocurred during Emmett processing`,\n });\n\n // 👇️ because we are extending a built-in class\n Object.setPrototypeOf(this, IllegalStateError.prototype);\n }\n}\n\nexport class NotFoundError extends EmmettError {\n constructor(options?: { id: string; type: string; message?: string }) {\n super({\n errorCode: 404,\n message:\n options?.message ??\n (options?.id\n ? options.type\n ? `${options.type} with ${options.id} was not found during Emmett processing`\n : `State with ${options.id} was not found during Emmett processing`\n : options?.type\n ? `${options.type} was not found during Emmett processing`\n : 'State was not found during Emmett processing'),\n });\n\n // 👇️ because we are extending a built-in class\n Object.setPrototypeOf(this, NotFoundError.prototype);\n }\n}\n","import { ValidationError } from '../errors';\n\nexport const formatDateToUtcYYYYMMDD = (date: Date) => {\n // Use the 'en-CA' locale which formats as 'yyyy-mm-dd'\n const formatter = new Intl.DateTimeFormat('en-CA', {\n timeZone: 'UTC',\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n });\n\n // Format the date\n return formatter.format(date);\n};\n\n// Function to validate 'yyyy-mm-dd' format\nexport const isValidYYYYMMDD = (dateString: string) => {\n const regex = /^\\d{4}-\\d{2}-\\d{2}$/;\n return regex.test(dateString);\n};\n\nexport const parseDateFromUtcYYYYMMDD = (dateString: string) => {\n const date = new Date(dateString + 'T00:00:00Z');\n\n if (!isValidYYYYMMDD(dateString)) {\n throw new ValidationError('Invalid date format, must be yyyy-mm-dd');\n }\n\n if (isNaN(date.getTime())) {\n throw new ValidationError('Invalid date format');\n }\n\n return date;\n};\n","import { Command as CliCommand } from 'commander';\n\nexport type EmmettPluginConfig =\n | {\n name: string;\n register: EmmettPluginRegistration[];\n }\n | string;\n\nexport type EmmettPluginType = 'cli';\n\nexport type EmmettCliPluginRegistration = { pluginType: 'cli'; path?: string };\n\nexport type EmmettPluginRegistration = EmmettCliPluginRegistration;\n\nexport type EmmettCliPlugin = {\n pluginType: 'cli';\n name: string;\n registerCommands: (program: CliCommand) => Promise<void> | void;\n};\n\nexport type EmmettPlugin = EmmettCliPlugin;\n\nexport const isPluginConfig = (\n plugin: Partial<EmmettPluginConfig> | string | undefined,\n): plugin is EmmettPluginConfig =>\n plugin !== undefined &&\n (typeof plugin === 'string' ||\n ('name' in plugin &&\n plugin.name !== undefined &&\n typeof plugin.name === 'string'));\n","//import type { ReadableStream } from '@event-driven-io/emmett-shims';\nimport type { Event, ReadEvent, ReadEventMetadata } from '../typing';\n//import type { GlobalSubscriptionEvent } from './events';\nimport type { ExpectedStreamVersion } from './expectedVersion';\n\n// #region event-store\nexport interface EventStore<\n StreamVersion = DefaultStreamVersionType,\n ReadEventMetadataType extends ReadEventMetadata = ReadEventMetadata,\n> {\n aggregateStream<State, EventType extends Event>(\n streamName: string,\n options: AggregateStreamOptions<\n State,\n EventType,\n StreamVersion,\n ReadEventMetadataType\n >,\n ): Promise<AggregateStreamResult<State, StreamVersion>>;\n\n readStream<EventType extends Event>(\n streamName: string,\n options?: ReadStreamOptions<StreamVersion>,\n ): Promise<ReadStreamResult<EventType, StreamVersion, ReadEventMetadataType>>;\n\n appendToStream<EventType extends Event>(\n streamName: string,\n events: EventType[],\n options?: AppendToStreamOptions<StreamVersion>,\n ): Promise<AppendToStreamResult<StreamVersion>>;\n\n // streamEvents(): ReadableStream<\n // ReadEvent<Event, ReadEventMetadataType> | GlobalSubscriptionEvent\n // >;\n}\n\nexport type EventStoreSession<\n EventStoreType extends EventStore<StreamVersion>,\n StreamVersion = DefaultStreamVersionType,\n> = {\n eventStore: EventStoreType;\n close: () => Promise<void>;\n};\n\nexport interface EventStoreSessionFactory<\n EventStoreType extends EventStore<StreamVersion>,\n StreamVersion = DefaultStreamVersionType,\n> {\n withSession<T = unknown>(\n callback: (\n session: EventStoreSession<EventStoreType, StreamVersion>,\n ) => Promise<T>,\n ): Promise<T>;\n}\n\nexport type DefaultStreamVersionType = bigint;\n// #endregion event-store\n\nexport const canCreateEventStoreSession = <\n Store extends EventStore<StreamVersion>,\n StreamVersion = DefaultStreamVersionType,\n>(\n eventStore: Store | EventStoreSessionFactory<Store, StreamVersion>,\n): eventStore is EventStoreSessionFactory<Store, StreamVersion> =>\n 'withSession' in eventStore;\n\nexport const nulloSessionFactory = <\n EventStoreType extends EventStore<StreamVersion>,\n StreamVersion = DefaultStreamVersionType,\n>(\n eventStore: EventStoreType,\n): EventStoreSessionFactory<EventStoreType, StreamVersion> => ({\n withSession: (callback) => {\n const nulloSession: EventStoreSession<EventStoreType, StreamVersion> = {\n eventStore,\n close: () => Promise.resolve(),\n };\n\n return callback(nulloSession);\n },\n});\n\n////////////////////////////////////////////////////////////////////\n/// ReadStream types\n////////////////////////////////////////////////////////////////////\n\nexport type ReadStreamOptions<StreamVersion = DefaultStreamVersionType> = (\n | {\n from: StreamVersion;\n }\n | { to: StreamVersion }\n | { from: StreamVersion; maxCount?: bigint }\n | {\n expectedStreamVersion: ExpectedStreamVersion<StreamVersion>;\n }\n) & {\n expectedStreamVersion?: ExpectedStreamVersion<StreamVersion>;\n};\n\nexport type ReadStreamResult<\n EventType extends Event,\n StreamVersion = DefaultStreamVersionType,\n ReadEventMetadataType extends ReadEventMetadata = ReadEventMetadata,\n> = {\n currentStreamVersion: StreamVersion;\n events: ReadEvent<EventType, ReadEventMetadataType>[];\n streamExists: boolean;\n};\n\n////////////////////////////////////////////////////////////////////\n/// AggregateStream types\n////////////////////////////////////////////////////////////////////\n\ntype Evolve<\n State,\n EventType extends Event,\n ReadEventMetadataType extends ReadEventMetadata = ReadEventMetadata,\n> =\n | ((currentState: State, event: EventType) => State)\n | ((\n currentState: State,\n event: ReadEvent<EventType, ReadEventMetadataType>,\n ) => State)\n | ((currentState: State, event: ReadEvent<EventType>) => State);\n\nexport type AggregateStreamOptions<\n State,\n EventType extends Event,\n StreamVersion = DefaultStreamVersionType,\n ReadEventMetadataType extends ReadEventMetadata = ReadEventMetadata,\n> = {\n evolve: Evolve<State, EventType, ReadEventMetadataType>;\n initialState: () => State;\n read?: ReadStreamOptions<StreamVersion>;\n};\n\nexport type AggregateStreamResult<\n State,\n StreamVersion = DefaultStreamVersionType,\n> = {\n currentStreamVersion: StreamVersion;\n state: State;\n streamExists: boolean;\n};\n\n////////////////////////////////////////////////////////////////////\n/// AppendToStream types\n////////////////////////////////////////////////////////////////////\n\nexport type AppendToStreamOptions<StreamVersion = DefaultStreamVersionType> = {\n expectedStreamVersion?: ExpectedStreamVersion<StreamVersion>;\n};\n\nexport type AppendToStreamResult<StreamVersion = DefaultStreamVersionType> = {\n nextExpectedStreamVersion: StreamVersion;\n createdNewStream: boolean;\n};\n","import type { DefaultRecord, Flavour } from './';\nexport type Command<\n CommandType extends string = string,\n CommandData extends DefaultRecord = DefaultRecord,\n CommandMetaData extends DefaultRecord = DefaultCommandMetadata,\n> = Flavour<\n Readonly<{\n type: CommandType;\n data: Readonly<CommandData>;\n metadata?: CommandMetaData | undefined;\n }>,\n 'Command'\n>;\n\nexport type CommandTypeOf<T extends Command> = T['type'];\nexport type CommandDataOf<T extends Command> = T['data'];\nexport type CommandMetaDataOf<T extends Command> = T['metadata'];\n\nexport type CreateCommandType<\n CommandType extends string,\n CommandData extends DefaultRecord,\n CommandMetaData extends DefaultRecord | undefined,\n> = Readonly<{\n type: CommandType;\n data: CommandData;\n metadata?: CommandMetaData;\n}>;\n\nexport const command = <CommandType extends Command>(\n type: CommandTypeOf<CommandType>,\n data: CommandDataOf<CommandType>,\n metadata?: CommandMetaDataOf<CommandType>,\n): CreateCommandType<\n CommandTypeOf<CommandType>,\n CommandDataOf<CommandType>,\n CommandMetaDataOf<CommandType>\n> => {\n return {\n type,\n data,\n metadata,\n };\n};\n\nexport type DefaultCommandMetadata = { now: Date };\n","import type { DefaultRecord, Flavour } from './';\n\nexport type Event<\n EventType extends string = string,\n EventData extends DefaultRecord = DefaultRecord,\n EventMetaData extends DefaultRecord = DefaultRecord,\n> = Flavour<\n Readonly<{\n type: EventType;\n data: EventData;\n metadata?: EventMetaData;\n }>,\n 'Event'\n>;\n\nexport type EventTypeOf<T extends Event> = T['type'];\nexport type EventDataOf<T extends Event> = T['data'];\nexport type EventMetaDataOf<T extends Event> = T['metadata'];\n\nexport type CanHandle<T extends Event> = EventTypeOf<T>[];\n\nexport type CreateEventType<\n EventType extends string,\n EventData extends DefaultRecord,\n EventMetaData extends DefaultRecord | undefined,\n> = Readonly<{\n type: EventType;\n data: EventData;\n metadata?: EventMetaData;\n}>;\n\nexport const event = <EventType extends Event>(\n type: EventTypeOf<EventType>,\n data: EventDataOf<EventType>,\n metadata?: EventMetaDataOf<EventType>,\n): CreateEventType<\n EventTypeOf<EventType>,\n EventDataOf<EventType>,\n EventMetaDataOf<EventType>\n> => {\n return {\n type,\n data,\n metadata,\n };\n};\n\nexport type ReadEvent<\n EventType extends Event = Event,\n EventMetaDataType extends EventMetaDataOf<EventType> &\n ReadEventMetadata = EventMetaDataOf<EventType> & ReadEventMetadata,\n> = CreateEventType<\n EventTypeOf<EventType>,\n EventDataOf<EventType>,\n EventMetaDataType\n> &\n EventType & { metadata: EventMetaDataType };\n\nexport type ReadEventMetadata = Readonly<{\n eventId: string;\n streamPosition: bigint;\n streamName: string;\n}>;\n\nexport type ReadEventMetadataWithGlobalPosition = ReadEventMetadata & {\n globalPosition: bigint;\n};\n","import type { Command } from './command';\nimport type { Event } from './event';\n\n/// Inspired by https://blog.bittacklr.be/the-workflow-pattern.html\n\nexport type Workflow<\n Input extends Event | Command,\n State,\n Output extends Event | Command,\n> = {\n decide: (command: Input, state: State) => WorkflowOutput<Output>[];\n evolve: (currentState: State, event: WorkflowEvent<Output>) => State;\n initialState: () => State;\n};\n\nexport type WorkflowEvent<Output extends Command | Event> = Extract<\n Output,\n { __brand?: 'Event' }\n>;\n\nexport type WorkflowCommand<Output extends Command | Event> = Extract<\n Output,\n { __brand?: 'Command' }\n>;\n\nexport type WorkflowOutput<TOutput extends Command | Event> =\n | { kind: 'Reply'; message: TOutput }\n | { kind: 'Send'; message: WorkflowCommand<TOutput> }\n | { kind: 'Publish'; message: WorkflowEvent<TOutput> }\n | {\n kind: 'Schedule';\n message: TOutput;\n when: { afterInMs: number } | { at: Date };\n }\n | { kind: 'Complete' }\n | { kind: 'Accept' }\n | { kind: 'Ignore'; reason: string }\n | { kind: 'Error'; reason: string };\n\nexport const reply = <TOutput extends Command | Event>(\n message: TOutput,\n): WorkflowOutput<TOutput> => {\n return {\n kind: 'Reply',\n message,\n };\n};\n\nexport const send = <TOutput extends Command | Event>(\n message: WorkflowCommand<TOutput>,\n): WorkflowOutput<TOutput> => {\n return {\n kind: 'Send',\n message,\n };\n};\n\nexport const publish = <TOutput extends Command | Event>(\n message: WorkflowEvent<TOutput>,\n): WorkflowOutput<TOutput> => {\n return {\n kind: 'Publish',\n message,\n };\n};\n\nexport const schedule = <TOutput extends Command | Event>(\n message: TOutput,\n when: { afterInMs: number } | { at: Date },\n): WorkflowOutput<TOutput> => {\n return {\n kind: 'Schedule',\n message,\n when,\n };\n};\n\nexport const complete = <\n TOutput extends Command | Event,\n>(): WorkflowOutput<TOutput> => {\n return {\n kind: 'Complete',\n };\n};\n\nexport const ignore = <TOutput extends Command | Event>(\n reason: string,\n): WorkflowOutput<TOutput> => {\n return {\n kind: 'Ignore',\n reason,\n };\n};\n\nexport const error = <TOutput extends Command | Event>(\n reason: string,\n): WorkflowOutput<TOutput> => {\n return {\n kind: 'Error',\n reason,\n };\n};\n\nexport const accept = <\n TOutput extends Command | Event,\n>(): WorkflowOutput<TOutput> => {\n return { kind: 'Accept' };\n};\n","import {\n event,\n type Event,\n type EventDataOf,\n type ReadEvent,\n type ReadEventMetadataWithGlobalPosition,\n} from '../../typing';\n\nexport const GlobalStreamCaughtUpType = '__emt:GlobalStreamCaughtUp';\n\nexport type GlobalStreamCaughtUp = Event<\n '__emt:GlobalStreamCaughtUp',\n { globalPosition: bigint },\n { globalPosition: bigint }\n>;\n\nexport const isGlobalStreamCaughtUp = (\n event: Event,\n): event is GlobalStreamCaughtUp => event.type === GlobalStreamCaughtUpType;\n\nexport const caughtUpEventFrom =\n (position: bigint) =>\n (\n event: ReadEvent<Event, ReadEventMetadataWithGlobalPosition>,\n ): event is ReadEvent<\n GlobalStreamCaughtUp,\n ReadEventMetadataWithGlobalPosition\n > =>\n event.type === GlobalStreamCaughtUpType &&\n event.metadata?.globalPosition >= position;\n\nexport const globalStreamCaughtUp = (\n data: EventDataOf<GlobalStreamCaughtUp>,\n): GlobalStreamCaughtUp =>\n event<GlobalStreamCaughtUp>(GlobalStreamCaughtUpType, data, {\n globalPosition: data.globalPosition,\n });\n\nexport const isSubscriptionEvent = (\n event: Event,\n): event is GlobalSubscriptionEvent => isGlobalStreamCaughtUp(event);\n\nexport const isNotInternalEvent = (event: Event): boolean =>\n !isGlobalStreamCaughtUp(event);\n\nexport type GlobalSubscriptionEvent = GlobalStreamCaughtUp;\n","import { ConcurrencyError } from '../errors';\nimport type { Flavour } from '../typing';\nimport type { DefaultStreamVersionType } from './eventStore';\n\nexport type ExpectedStreamVersion<VersionType = DefaultStreamVersionType> =\n | ExpectedStreamVersionWithValue<VersionType>\n | ExpectedStreamVersionGeneral;\n\nexport type ExpectedStreamVersionWithValue<\n VersionType = DefaultStreamVersionType,\n> = Flavour<VersionType, 'StreamVersion'>;\n\nexport type ExpectedStreamVersionGeneral = Flavour<\n 'STREAM_EXISTS' | 'STREAM_DOES_NOT_EXIST' | 'NO_CONCURRENCY_CHECK',\n 'StreamVersion'\n>;\n\nexport const STREAM_EXISTS = 'STREAM_EXISTS' as ExpectedStreamVersionGeneral;\nexport const STREAM_DOES_NOT_EXIST =\n 'STREAM_DOES_NOT_EXIST' as ExpectedStreamVersionGeneral;\nexport const NO_CONCURRENCY_CHECK =\n 'NO_CONCURRENCY_CHECK' as ExpectedStreamVersionGeneral;\n\nexport const matchesExpectedVersion = <\n StreamVersion = DefaultStreamVersionType,\n>(\n current: StreamVersion | undefined,\n expected: ExpectedStreamVersion<StreamVersion>,\n defaultVersion: StreamVersion,\n): boolean => {\n if (expected === NO_CONCURRENCY_CHECK) return true;\n\n if (expected == STREAM_DOES_NOT_EXIST) return current === defaultVersion;\n\n if (expected == STREAM_EXISTS) return current !== defaultVersion;\n\n return current === expected;\n};\n\nexport const assertExpectedVersionMatchesCurrent = <\n StreamVersion = DefaultStreamVersionType,\n>(\n current: StreamVersion,\n expected: ExpectedStreamVersion<StreamVersion> | undefined,\n defaultVersion: StreamVersion,\n): void => {\n expected ??= NO_CONCURRENCY_CHECK;\n\n if (!matchesExpectedVersion(current, expected, defaultVersion))\n throw new ExpectedVersionConflictError(current, expected);\n};\n\nexport class ExpectedVersionConflictError<\n VersionType = DefaultStreamVersionType,\n> extends ConcurrencyError {\n constructor(\n current: VersionType,\n expected: ExpectedStreamVersion<VersionType>,\n ) {\n super(current?.toString(), expected?.toString());\n\n // 👇️ because we are extending a built-in class\n Object.setPrototypeOf(this, ExpectedVersionConflictError.prototype);\n }\n}\n\nexport const isExpectedVersionConflictError = (\n error: unknown,\n): error is ExpectedVersionConflictError =>\n error instanceof ExpectedVersionConflictError;\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 {\n ReadableStream,\n TransformStream,\n WritableStream,\n} from 'web-streams-polyfill';\n\nconst streamsPolyfill = { ReadableStream, WritableStream, TransformStream };\n\n// https://github.com/jsdom/jsdom/issues/1537#issuecomment-229405327\n\nconst isJsDom = (): boolean => {\n // @ts-expect-error global object check\n const globalWindow = window as { name: string };\n // @ts-expect-error global object check\n const globalNavigator = navigator as { userAgent: string };\n\n return (\n (typeof globalWindow !== 'undefined' && globalWindow.name === 'nodejs') ||\n (typeof globalNavigator !== 'undefined' &&\n 'userAgent' in globalNavigator &&\n typeof globalNavigator.userAgent === 'string' &&\n (globalNavigator.userAgent.includes('Node.js') ||\n globalNavigator.userAgent.includes('jsdom')))\n );\n};\n\nconst isDeno = (): boolean => {\n // @ts-expect-error global object check\n const globalDeno = Deno as { version: { deno: unknown } };\n\n return (\n typeof globalDeno !== 'undefined' &&\n typeof globalDeno.version !== 'undefined' &&\n typeof globalDeno.version.deno !== 'undefined'\n );\n};\n\nconst isBun =\n typeof process !== 'undefined' &&\n process.versions != null &&\n process.versions.bun != null;\n\nexport { isBrowser, isBun, isDeno, isJsDom, isNode, isWebWorker };\n\nlet streams: typeof streamsPolyfill;\n\nif (\n globalThis &&\n globalThis.WritableStream &&\n globalThis.ReadableStream &&\n globalThis.TransformStream\n) {\n streams = globalThis as typeof streamsPolyfill;\n} else {\n try {\n streams = (await import('node:stream/web')) as typeof streamsPolyfill;\n } catch {\n // Just falling back to the default polyfill\n streams = streamsPolyfill;\n }\n}\n\nexport default streams;\n\n// Use a type-only import/export to re-export all types\nexport type * from 'web-streams-polyfill';\n\nconst isBrowser = (): boolean => {\n // @ts-expect-error global object check\n const globalWindow = window as { document: unknown };\n\n return (\n typeof globalWindow !== 'undefined' &&\n typeof globalWindow.document !== 'undefined'\n );\n};\n\nconst isNode = (): boolean =>\n typeof process !== 'undefined' &&\n process.versions != null &&\n process.versions.node != null;\n\nconst isWebWorker = (): boolean => {\n // @ts-expect-error global object check\n const globalSelf = self as { constructor: { name: unknown } | undefined };\n\n return (\n typeof globalSelf === 'object' &&\n !!globalSelf.constructor &&\n globalSelf.constructor.name === 'DedicatedWorkerGlobalScope'\n );\n};\n","import streams from '@event-driven-io/emmett-shims';\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 streams.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 streams from '@event-driven-io/emmett-shims';\nimport { v4 as uuid } from 'uuid';\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<\n Item,\n> extends streams.TransformStream<Item, Item> {\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 type {\n ReadableStream,\n TransformStream,\n} from '@event-driven-io/emmett-shims';\nimport { noMoreWritingOn } from './noMoreWritingOn';\n\nexport const writeToStream = async <In, Out = In>(\n stream: TransformStream<In, Out>,\n items: In[],\n): Promise<boolean> => {\n if (stream.writable.locked) return false;\n\n const writer = stream.writable.getWriter();\n await writer.ready;\n\n if (!stream.readable.locked) return false;\n\n try {\n for (const item of items) {\n await writer.write(item);\n }\n } catch (error) {\n console.log(error);\n } finally {\n await writer.close();\n }\n return true;\n};\n\nexport const writeToStreamAndStop = async <In, Out>(\n stream: TransformStream<In, Out>,\n items: In[],\n): Promise<ReadableStream<Out>> => {\n await writeToStream(stream, items);\n return await noMoreWritingOn(stream);\n};\n","export const deepEquals = <T>(left: T, right: T): boolean => {\n if (isEquatable(left)) {\n return left.equals(right);\n }\n\n if (Array.isArray(left)) {\n return (\n Array.isArray(right) &&\n left.length === right.length &&\n left.every((val, index) => deepEquals(val, right[index]))\n );\n }\n\n if (\n typeof left !== 'object' ||\n typeof right !== 'object' ||\n left === null ||\n right === null\n ) {\n return left === right;\n }\n\n if (Array.isArray(right)) return false;\n\n const keys1 = Object.keys(left);\n const keys2 = Object.keys(right);\n\n if (\n keys1.length !== keys2.length ||\n !keys1.every((key) => keys2.includes(key))\n )\n return false;\n\n for (const key in left) {\n if (left[key] instanceof Function && right[key] instanceof Function)\n continue;\n\n const isEqual = deepEquals(left[key], right[key]);\n if (!isEqual) {\n return false;\n }\n }\n\n return true;\n};\n\nexport type Equatable<T> = { equals: (right: T) => boolean } & T;\n\nexport const isEquatable = <T>(left: T): left is Equatable<T> => {\n return (\n left &&\n typeof left === 'object' &&\n 'equals' in left &&\n typeof left['equals'] === 'function'\n );\n};\n","export const sum = (\n iterator: Iterator<number, number, number> | Iterator<number>,\n) => {\n let value,\n done: boolean | undefined,\n sum = 0;\n do {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n ({ value, done } = iterator.next());\n sum += value || 0;\n } while (!done);\n return sum;\n};\n","export const merge = <T>(\n array: T[],\n item: T,\n where: (current: T) => boolean,\n onExisting: (current: T) => T,\n onNotFound: () => T | undefined = () => undefined,\n) => {\n let wasFound = false;\n\n const result = array\n // merge the existing item if matches condition\n .map((p: T) => {\n if (!where(p)) return p;\n\n wasFound = true;\n return onExisting(p);\n })\n // filter out item if undefined was returned\n // for cases of removal\n .filter((p) => p !== undefined)\n // make TypeScript happy\n .map((p) => {\n if (!p) throw Error('That should not happen');\n\n return p;\n });\n\n // if item was not found and onNotFound action is defined\n // try to generate new item\n if (!wasFound) {\n const result = onNotFound();\n\n if (result !== undefined) return [...array, item];\n }\n\n return result;\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 {\n canCreateEventStoreSession,\n isExpectedVersionConflictError,\n NO_CONCURRENCY_CHECK,\n nulloSessionFactory,\n STREAM_DOES_NOT_EXIST,\n type AppendToStreamResult,\n type DefaultStreamVersionType,\n type EventStore,\n type EventStoreSession,\n type ExpectedStreamVersion,\n} from '../eventStore';\nimport type { Event } from '../typing';\nimport { asyncRetry, NoRetries, type AsyncRetryOptions } from '../utils';\n\nexport const CommandHandlerStreamVersionConflictRetryOptions: AsyncRetryOptions =\n {\n retries: 3,\n minTimeout: 100,\n factor: 1.5,\n shouldRetryError: isExpectedVersionConflictError,\n };\n\nexport type CommandHandlerRetryOptions =\n | AsyncRetryOptions\n | { onVersionConflict: true | number | AsyncRetryOptions };\n\nconst fromCommandHandlerRetryOptions = (\n retryOptions: CommandHandlerRetryOptions | undefined,\n): AsyncRetryOptions => {\n if (retryOptions === undefined) return NoRetries;\n\n if ('onVersionConflict' in retryOptions) {\n if (typeof retryOptions.onVersionConflict === 'boolean')\n return CommandHandlerStreamVersionConflictRetryOptions;\n else if (typeof retryOptions.onVersionConflict === 'number')\n return {\n ...CommandHandlerStreamVersionConflictRetryOptions,\n retries: retryOptions.onVersionConflict,\n };\n else return retryOptions.onVersionConflict;\n }\n\n return retryOptions;\n};\n\n// #region command-handler\nexport type CommandHandlerResult<\n State,\n StreamEvent extends Event,\n StreamVersion = DefaultStreamVersionType,\n> = AppendToStreamResult<StreamVersion> & {\n newState: State;\n newEvents: StreamEvent[];\n};\n\nexport type CommandHandlerOptions<State, StreamEvent extends Event> = {\n evolve: (state: State, event: StreamEvent) => State;\n initialState: () => State;\n mapToStreamId?: (id: string) => string;\n retry?: CommandHandlerRetryOptions;\n};\n\nexport type HandleOptions<\n StreamVersion,\n Store extends EventStore<StreamVersion>,\n> = Parameters<Store['appendToStream']>[2] &\n (\n | {\n expectedStreamVersion?: ExpectedStreamVersion<StreamVersion>;\n }\n | {\n retry?: CommandHandlerRetryOptions;\n }\n );\n\nexport const CommandHandler =\n <State, StreamEvent extends Event, StreamVersion = DefaultStreamVersionType>(\n options: CommandHandlerOptions<State, StreamEvent>,\n ) =>\n async <Store extends EventStore<StreamVersion>>(\n store: Store,\n id: string,\n handle: (\n state: State,\n ) =>\n | StreamEvent\n | StreamEvent[]\n | Promise<StreamEvent>\n | Promise<StreamEvent[]>,\n handleOptions?: HandleOptions<StreamVersion, Store>,\n ): Promise<CommandHandlerResult<State, StreamEvent, StreamVersion>> =>\n asyncRetry(\n async () => {\n const result = await withSession<\n Store,\n StreamVersion,\n CommandHandlerResult<State, StreamEvent, StreamVersion>\n >(store, async ({ eventStore }) => {\n const { evolve, initialState } = options;\n const mapToStreamId = options.mapToStreamId ?? ((id) => id);\n\n const streamName = mapToStreamId(id);\n\n // 1. Aggregate the stream\n const aggregationResult = await eventStore.aggregateStream<\n State,\n StreamEvent\n >(streamName, {\n evolve,\n initialState,\n read: {\n // expected stream version is passed to fail fast\n // if stream is in the wrong state\n expectedStreamVersion:\n handleOptions?.expectedStreamVersion ?? NO_CONCURRENCY_CHECK,\n },\n });\n\n // 2. Use the aggregate state\n const state = aggregationResult.state;\n const currentStreamVersion = aggregationResult.currentStreamVersion;\n\n // 3. Run business logic\n const result = await handle(state);\n\n const newEvents = Array.isArray(result) ? result : [result];\n\n if (newEvents.length === 0) {\n return {\n newEvents: [],\n newState: state,\n nextExpectedStreamVersion: currentStreamVersion,\n createdNewStream: false,\n };\n }\n\n // Either use:\n // - provided expected stream version,\n // - current stream version got from stream aggregation,\n // - or expect stream not to exists otherwise.\n const expectedStreamVersion: ExpectedStreamVersion<StreamVersion> =\n handleOptions?.expectedStreamVersion ??\n (aggregationResult.streamExists\n ? (currentStreamVersion as ExpectedStreamVersion<StreamVersion>)\n : STREAM_DOES_NOT_EXIST);\n\n // 4. Append result to the stream\n const appendResult = await eventStore.appendToStream(\n streamName,\n newEvents,\n {\n ...handleOptions,\n expectedStreamVersion,\n },\n );\n\n // 5. Return result with updated state\n return {\n ...appendResult,\n newEvents,\n newState: newEvents.reduce(evolve, state),\n };\n });\n\n return result;\n },\n fromCommandHandlerRetryOptions(\n handleOptions && 'retry' in handleOptions\n ? handleOptions.retry\n : options.retry,\n ),\n );\n// #endregion command-handler\n\nconst withSession = <\n EventStoreType extends EventStore<StreamVersion>,\n StreamVersion = DefaultStreamVersionType,\n T = unknown,\n>(\n eventStore: EventStoreType,\n callback: (\n session: EventStoreSession<EventStoreType, StreamVersion>,\n ) => Promise<T>,\n) => {\n const sessionFactory = canCreateEventStoreSession<\n EventStoreType,\n StreamVersion\n >(eventStore)\n ? eventStore\n : nulloSessionFactory<EventStoreType, StreamVersion>(eventStore);\n\n return sessionFactory.withSession(callback);\n};\n","import type { DefaultStreamVersionType, EventStore } from '../eventStore';\nimport type { Command, Event } from '../typing';\nimport type { Decider } from '../typing/decider';\nimport {\n CommandHandler,\n type CommandHandlerOptions,\n type HandleOptions,\n} from './handleCommand';\n\n// #region command-handler\n\nexport type DeciderCommandHandlerOptions<\n State,\n CommandType extends Command,\n StreamEvent extends Event,\n> = CommandHandlerOptions<State, StreamEvent> &\n Decider<State, CommandType, StreamEvent>;\n\nexport const DeciderCommandHandler =\n <\n State,\n CommandType extends Command,\n StreamEvent extends Event,\n StreamVersion = DefaultStreamVersionType,\n >(\n options: DeciderCommandHandlerOptions<State, CommandType, StreamEvent>,\n ) =>\n async (\n eventStore: EventStore<StreamVersion>,\n id: string,\n command: CommandType,\n handleOptions?: HandleOptions<StreamVersion, EventStore<StreamVersion>>,\n ) => {\n const { decide, ...rest } = options;\n\n return CommandHandler<State, StreamEvent, StreamVersion>(rest)(\n eventStore,\n id,\n (state) => decide(command, state),\n handleOptions,\n );\n };\n// #endregion command-handler\n","import { EmmettError } from '../errors';\nimport {\n type Command,\n type CommandTypeOf,\n type Event,\n type EventTypeOf,\n} from '../typing';\n\nexport interface CommandSender {\n send<CommandType extends Command = Command>(\n command: CommandType,\n ): Promise<void>;\n}\n\nexport interface EventsPublisher {\n publish<EventType extends Event = Event>(event: EventType): Promise<void>;\n}\n\nexport type ScheduleOptions = { afterInMs: number } | { at: Date };\n\nexport interface MessageScheduler<CommandOrEvent extends Command | Event> {\n schedule<MessageType extends CommandOrEvent>(\n message: MessageType,\n when?: ScheduleOptions,\n ): void;\n}\n\nexport interface CommandBus extends CommandSender, MessageScheduler<Command> {}\n\nexport interface EventBus extends EventsPublisher, MessageScheduler<Event> {}\n\nexport interface MessageBus extends CommandBus, EventBus {\n schedule<MessageType extends Command | Event>(\n message: MessageType,\n when?: ScheduleOptions,\n ): void;\n}\n\ntype CommandHandler<CommandType extends Command = Command> = (\n command: CommandType,\n) => Promise<void> | void;\n\nexport interface CommandProcessor {\n handle<CommandType extends Command>(\n commandHandler: CommandHandler<CommandType>,\n ...commandTypes: CommandTypeOf<CommandType>[]\n ): void;\n}\n\ntype EventHandler<EventType extends Event = Event> = (\n event: EventType,\n) => Promise<void> | void;\n\nexport interface EventProcessor {\n subscribe<EventType extends Event>(\n eventHandler: EventHandler<EventType>,\n ...eventTypes: EventTypeOf<EventType>[]\n ): void;\n}\n\nexport type ScheduledMessage = {\n message: Event | Command;\n options?: ScheduleOptions;\n};\n\nexport interface ScheduledMessageProcessor {\n dequeue(): ScheduledMessage[];\n}\n\nexport type MessageHandler = CommandHandler | EventHandler;\n\nexport type MessageProcessor = EventProcessor | CommandProcessor;\n\nexport const getInMemoryMessageBus = (): MessageBus &\n EventProcessor &\n CommandProcessor &\n ScheduledMessageProcessor => {\n const allHandlers = new Map<string, MessageHandler[]>();\n let pendingMessages: ScheduledMessage[] = [];\n\n return {\n send: async <CommandType extends Command = Command>(\n command: CommandType,\n ): Promise<void> => {\n const handlers = allHandlers.get(command.type);\n\n if (handlers === undefined || handlers.length === 0)\n throw new EmmettError(\n `No handler registered for command ${command.type}!`,\n );\n\n const commandHandler = handlers[0] as CommandHandler<CommandType>;\n\n await commandHandler(command);\n },\n\n publish: async <EventType extends Event = Event>(\n event: EventType,\n ): Promise<void> => {\n const handlers = allHandlers.get(event.type) ?? [];\n\n for (const handler of handlers) {\n const eventHandler = handler as EventHandler<EventType>;\n\n await eventHandler(event);\n }\n },\n\n schedule: <MessageType extends Command | Event>(\n message: MessageType,\n when?: ScheduleOptions,\n ): void => {\n pendingMessages = [...pendingMessages, { message, options: when }];\n },\n\n handle: <CommandType extends Command>(\n commandHandler: CommandHandler<CommandType>,\n ...commandTypes: CommandTypeOf<CommandType>[]\n ): void => {\n const alreadyRegistered = [...allHandlers.keys()].filter((registered) =>\n commandTypes.includes(registered),\n );\n\n if (alreadyRegistered.length > 0)\n throw new EmmettError(\n `Cannot register handler for commands ${alreadyRegistered.join(', ')} as they're already registered!`,\n );\n for (const commandType of commandTypes) {\n allHandlers.set(commandType, [commandHandler as MessageHandler]);\n }\n },\n\n subscribe<EventType extends Event>(\n eventHandler: EventHandler<EventType>,\n ...eventTypes: EventTypeOf<EventType>[]\n ): void {\n for (const eventType of eventTypes) {\n if (!allHandlers.has(eventType)) allHandlers.set(eventType, []);\n\n allHandlers.set(eventType, [\n ...(allHandlers.get(eventType) ?? []),\n eventHandler as MessageHandler,\n ]);\n }\n },\n\n dequeue: (): ScheduledMessage[] => {\n const pending = pendingMessages;\n pendingMessages = [];\n return pending;\n },\n };\n};\n","import type {\n CanHandle,\n DefaultRecord,\n Event,\n EventMetaDataOf,\n ReadEvent,\n ReadEventMetadata,\n} from '../typing';\n\nexport type ProjectionHandlingType = 'inline' | 'async';\n\nexport type ProjectionHandler<\n EventType extends Event = Event,\n EventMetaDataType extends EventMetaDataOf<EventType> &\n ReadEventMetadata = EventMetaDataOf<EventType> & ReadEventMetadata,\n ProjectionHandlerContext extends DefaultRecord = DefaultRecord,\n> = (\n events: ReadEvent<EventType, EventMetaDataType>[],\n context: ProjectionHandlerContext,\n) => Promise<void> | void;\n\nexport interface ProjectionDefinition<\n ProjectionHandlerContext extends DefaultRecord = DefaultRecord,\n> {\n name?: string;\n canHandle: CanHandle<Event>;\n handle: ProjectionHandler<Event, ReadEventMetadata, ProjectionHandlerContext>;\n}\n\nexport interface TypedProjectionDefinition<\n EventType extends Event = Event,\n EventMetaDataType extends EventMetaDataOf<EventType> &\n ReadEventMetadata = EventMetaDataOf<EventType> & ReadEventMetadata,\n ProjectionHandlerContext extends DefaultRecord = DefaultRecord,\n> {\n name?: string;\n canHandle: CanHandle<EventType>;\n handle: ProjectionHandler<\n EventType,\n EventMetaDataType,\n ProjectionHandlerContext\n >;\n}\n\nexport type ProjectionRegistration<\n HandlingType extends ProjectionHandlingType,\n ProjectionHandlerContext extends DefaultRecord = DefaultRecord,\n> = {\n type: HandlingType;\n projection: ProjectionDefinition<ProjectionHandlerContext>;\n};\n\nexport const projection = <\n EventType extends Event = Event,\n EventMetaDataType extends EventMetaDataOf<EventType> &\n ReadEventMetadata = EventMetaDataOf<EventType> & ReadEventMetadata,\n ProjectionHandlerContext extends DefaultRecord = DefaultRecord,\n ProjectionDefintionType extends TypedProjectionDefinition<\n EventType,\n EventMetaDataType,\n ProjectionHandlerContext\n > = ProjectionDefinition<ProjectionHandlerContext>,\n>(\n definition: ProjectionDefintionType,\n): ProjectionDefintionType => definition;\n\nexport const inlineProjections = <\n ProjectionHandlerContext extends DefaultRecord = DefaultRecord,\n ProjectionDefintionType extends\n ProjectionDefinition<ProjectionHandlerContext> = ProjectionDefinition<ProjectionHandlerContext>,\n>(\n definitions: ProjectionDefintionType[],\n): ProjectionRegistration<'inline', ProjectionHandlerContext>[] =>\n definitions.map((projection) => ({ type: 'inline', projection }));\n\nexport const asyncProjections = <\n ProjectionHandlerContext extends DefaultRecord = DefaultRecord,\n ProjectionDefintionType extends\n ProjectionDefinition<ProjectionHandlerContext> = ProjectionDefinition<ProjectionHandlerContext>,\n>(\n definitions: ProjectionDefintionType[],\n): ProjectionRegistration<'async', ProjectionHandlerContext>[] =>\n definitions.map((projection) => ({ type: 'async', projection }));\n\nexport const projections = {\n inline: inlineProjections,\n async: asyncProjections,\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","export const concatUint8Arrays = (chunks: Uint8Array[]): Uint8Array => {\n const totalLength = chunks.reduce((acc, chunk) => acc + chunk.length, 0);\n const result = new Uint8Array(totalLength);\n let offset = 0;\n for (const chunk of chunks) {\n result.set(chunk, offset);\n offset += chunk.length;\n }\n return result;\n};\n","import type { ReadableStream } from '@event-driven-io/emmett-shims';\n\nexport const collect = async <T>(stream: ReadableStream<T>): Promise<T[]> => {\n const results: T[] = [];\n\n for await (const value of stream) {\n results.push(value as T);\n }\n\n return results;\n};\n","import type { Decoder } from '.';\nimport { concatUint8Arrays } from '../binaryArrays';\n\nexport class BinaryJsonDecoder<Decoded>\n implements Decoder<Uint8Array, Decoded>\n{\n private buffer: Uint8Array[] = [];\n\n addToBuffer(data: Uint8Array): void {\n this.buffer.push(data);\n }\n\n clearBuffer(): void {\n this.buffer = [];\n }\n\n hasCompleteMessage(): boolean {\n const combined = concatUint8Arrays(this.buffer);\n const text = new TextDecoder().decode(combined);\n return text.includes('\\n');\n }\n\n decode(): Decoded | null {\n if (!this.hasCompleteMessage()) {\n return null;\n }\n\n const combined = concatUint8Arrays(this.buffer);\n const text = new TextDecoder().decode(combined);\n const delimiterIndex = text.indexOf('\\n');\n\n if (delimiterIndex === -1) {\n return null;\n }\n\n const jsonString = text.slice(0, delimiterIndex);\n const remaining = new Uint8Array(combined.buffer, delimiterIndex + 1);\n this.buffer = remaining.byteLength > 0 ? [remaining] : [];\n\n return JSON.parse(jsonString) as Decoded;\n }\n}\n","import type { Decoder } from '.';\n\nexport class StringDecoder<Decoded> implements Decoder<string, Decoded> {\n protected buffer: string[] = [];\n\n constructor(private transform: (input: string) => Decoded) {\n this.transform = transform;\n }\n\n addToBuffer(data: string): void {\n this.buffer.push(data);\n }\n\n clearBuffer(): void {\n this.buffer = [];\n }\n\n hasCompleteMessage(): boolean {\n return this.buffer.some((chunk) => chunk.includes('\\n'));\n }\n\n decode(): Decoded | null {\n const completeString = this.buffer.join('');\n\n if (!this.hasCompleteMessage()) {\n if (completeString.trim().length > 0) {\n throw new Error('Unterminated string in JSON at position');\n }\n return null;\n }\n\n const delimiterIndex = completeString.indexOf('\\n');\n const message = completeString.slice(0, delimiterIndex).trim();\n this.buffer = [completeString.slice(delimiterIndex + 1)];\n\n return this.transform(message);\n }\n}\n","import { StringDecoder } from './string';\n\nexport class JsonDecoder<Decoded> extends StringDecoder<Decoded> {\n constructor() {\n super((jsonString) => JSON.parse(jsonString) as Decoded);\n }\n}\n","import type { Decoder } from '.';\n\nexport class ObjectDecoder<Decoded> implements Decoder<Decoded, Decoded> {\n private buffer: Decoded | null = null;\n\n addToBuffer(data: Decoded): void {\n this.buffer = data;\n }\n\n clearBuffer(): void {\n this.buffer = null;\n }\n\n hasCompleteMessage(): boolean {\n return this.buffer !== null;\n }\n\n decode(): Decoded | null {\n if (!this.hasCompleteMessage() || !this.buffer) {\n return null;\n }\n\n const data = this.buffer;\n this.clearBuffer();\n return data as Decoded;\n }\n}\n","import type { Decoder } from '.';\nimport { BinaryJsonDecoder } from './binary';\nimport { JsonDecoder } from './json';\nimport { ObjectDecoder } from './object';\n\nexport class CompositeDecoder<Decoded> implements Decoder<unknown, Decoded> {\n constructor(\n private decoders: [(data: unknown) => boolean, Decoder<unknown, Decoded>][],\n ) {}\n\n private decoderFor(data: unknown): Decoder<unknown, unknown> | null {\n const decoder = this.decoders.find((d) => d[0](data));\n\n if (!decoder) return null;\n\n return decoder[1];\n }\n\n addToBuffer(data: unknown): void {\n this.decoderFor(data)?.addToBuffer(data);\n }\n\n clearBuffer(): void {\n for (const decoder of this.decoders.map((d) => d[1])) {\n decoder.clearBuffer();\n }\n }\n\n hasCompleteMessage(): boolean {\n return this.decoders.some((d) => d[1].hasCompleteMessage());\n }\n\n decode(): Decoded | null {\n const decoder = this.decoders\n .map((d) => d[1])\n .find((d) => d.hasCompleteMessage());\n\n return decoder?.decode() ?? null;\n }\n}\n\nexport class DefaultDecoder<Decoded> extends CompositeDecoder<Decoded> {\n constructor() {\n super([\n [(data) => typeof data === 'string', new JsonDecoder<Decoded>()],\n [(data) => data instanceof Uint8Array, new BinaryJsonDecoder<Decoded>()],\n [(data) => typeof data === 'object', new ObjectDecoder<Decoded>()],\n ]);\n }\n}\n","import streams from '@event-driven-io/emmett-shims';\n\nexport const fromArray = <T>(chunks: T[]) =>\n new streams.ReadableStream<T>({\n start(controller) {\n for (const chunk of chunks) controller.enqueue(chunk);\n controller.close();\n },\n });\n","import { fromArray } from './fromArray';\nexport const streamGenerators = { fromArray };\n","import streams from '@event-driven-io/emmett-shims';\n\nexport const filter = <Item>(filter: (item: Item) => boolean) =>\n new streams.TransformStream<Item, Item>({\n transform(chunk, controller) {\n if (filter(chunk)) {\n controller.enqueue(chunk);\n }\n },\n });\n","import streams from '@event-driven-io/emmett-shims';\n\nexport const map = <From, To>(map: (item: From) => To) =>\n new streams.TransformStream<From, To>({\n transform(chunk, controller) {\n controller.enqueue(map(chunk));\n },\n });\n","import streams from '@event-driven-io/emmett-shims';\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 streams.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 streams, {\n type ReadableStream,\n type ReadableStreamDefaultReadResult,\n type TransformStream,\n type TransformStreamDefaultController,\n} from '@event-driven-io/emmett-shims';\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 streams.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 streams from '@event-driven-io/emmett-shims';\n\nexport const skip = <T>(limit: number) => new SkipTransformStream<T>(limit);\n\nexport class SkipTransformStream<T> extends streams.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 streams from '@event-driven-io/emmett-shims';\n\nexport const stopAfter = <Item>(stopCondition: (item: Item) => boolean) =>\n new streams.TransformStream<Item, Item>({\n transform(chunk, controller) {\n controller.enqueue(chunk);\n\n if (stopCondition(chunk)) {\n controller.terminate();\n }\n },\n });\n","import streams from '@event-driven-io/emmett-shims';\n\nexport const stopOn = <Item>(stopCondition: (item: Item) => boolean) =>\n new streams.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 streams from '@event-driven-io/emmett-shims';\n\nexport const take = <T>(limit: number) => new TakeTransformStream<T>(limit);\n\nexport class TakeTransformStream<T> extends streams.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 streams from '@event-driven-io/emmett-shims';\n\nexport const waitAtMost = <Item>(waitTimeInMs: number) =>\n new streams.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","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 {\n type ReadableStream,\n type ReadableStreamDefaultReadResult,\n type TransformStreamDefaultController,\n} from '@event-driven-io/emmett-shims';\nimport type { Decoder } from './decoders';\nimport { DefaultDecoder } from './decoders/composite';\nimport { streamTransformations } from './transformations';\nimport type { AsyncRetryOptions } from '../utils';\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 { 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 { isErrorConstructor, type ErrorConstructor } from '../errors';\nimport { AssertionError, assertMatches, assertTrue } from './assertions';\n\ntype ErrorCheck<ErrorType> = (error: ErrorType) => boolean;\n\nexport type ThenThrows<ErrorType extends Error> =\n | (() => void)\n | ((errorConstructor: ErrorConstructor<ErrorType>) => void)\n | ((errorCheck: ErrorCheck<ErrorType>) => void)\n | ((\n errorConstructor: ErrorConstructor<ErrorType>,\n errorCheck?: ErrorCheck<ErrorType>,\n ) => void);\n\nexport type DeciderSpecfication<Command, Event> = (\n givenEvents: Event | Event[],\n) => {\n when: (command: Command) => {\n then: (expectedEvents: Event | Event[]) => void;\n thenThrows: <ErrorType extends Error = Error>(\n ...args: Parameters<ThenThrows<ErrorType>>\n ) => void;\n };\n};\n\nexport const DeciderSpecification = {\n for: <Command, Event, State>(decider: {\n decide: (command: Command, state: State) => Event | Event[];\n evolve: (state: State, event: Event) => State;\n initialState: () => State;\n }): DeciderSpecfication<Command, Event> => {\n {\n return (givenEvents: Event | Event[]) => {\n return {\n when: (command: Command) => {\n const handle = () => {\n const existingEvents = Array.isArray(givenEvents)\n ? givenEvents\n : [givenEvents];\n\n const currentState = existingEvents.reduce<State>(\n decider.evolve,\n decider.initialState(),\n );\n\n return decider.decide(command, currentState);\n };\n\n return {\n then: (expectedEvents: Event | Event[]): void => {\n const resultEvents = handle();\n\n const resultEventsArray = Array.isArray(resultEvents)\n ? resultEvents\n : [resultEvents];\n\n const expectedEventsArray = Array.isArray(expectedEvents)\n ? expectedEvents\n : [expectedEvents];\n\n assertMatches(resultEventsArray, expectedEventsArray);\n },\n thenThrows: <ErrorType extends Error>(\n ...args: Parameters<ThenThrows<ErrorType>>\n ): void => {\n try {\n handle();\n throw new AssertionError('Handler did not fail as expected');\n } catch (error) {\n if (error instanceof AssertionError) throw error;\n\n if (args.length === 0) return;\n\n if (!isErrorConstructor(args[0])) {\n assertTrue(\n args[0](error as ErrorType),\n `Error didn't match the error condition: ${error?.toString()}`,\n );\n return;\n }\n\n assertTrue(\n error instanceof args[0],\n `Caught error is not an instance of the expected type: ${error?.toString()}`,\n );\n\n if (args[1]) {\n assertTrue(\n args[1](error as ErrorType),\n `Error didn't match the error condition: ${error?.toString()}`,\n );\n }\n }\n },\n };\n },\n };\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"],"mappings":"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","names":["express","Router","http","ValidationErrors","isNumber","val","uuid","ReadableStream","TransformStream","WritableStream","retry","event","type","data","metadata","GlobalStreamCaughtUpType","globalStreamCaughtUp","data","event","GlobalStreamCaughtUpType","streamsPolyfill","ReadableStream","WritableStream","TransformStream","isBun","streams","src_default","CaughtUpTransformStream","m","events","controller","globalPosition","event","globalStreamCaughtUp","value","notifyAboutNoActiveReadersStream","onNoActiveReaderCallback","options","NotifyAboutNoActiveReadersStream","reason","uuid","interval","asyncRetry","fn","opts","retry","bail","error","ParseError","text","JSONParser","value","options","_","v","parsed","filter","m","chunk","controller","map","reduce","reducer","initialValue","ReduceTransformStream","retryStream","createSourceStream","handleChunk","retryOptions","asyncRetry","onRestream","error","reader","done","result","skip","limit","SkipTransformStream","stopAfter","stopCondition","stopOn","take","TakeTransformStream","waitAtMost","waitTimeInMs","timeoutId","originalTerminate","streamTransformations","notifyAboutNoActiveReadersStream","NotifyAboutNoActiveReadersStream","retry","AssertionError","message","isSubset","superObj","subObj","sup","sub","assertOk","ele","assertFails","assertMatches","actual","expected","message","isSubset","AssertionError","JSONParser","assertOk","obj","message","AssertionError","assertEqual","expected","actual","JSONParser","WrapEventStore","eventStore","appendedEvents","streamName","options","events","result","currentStream","ProblemDocument","problemDetailsMiddleware","mapError","error","request","response","_next","problemDetails","defaulErrorToProblemDetailsMapping","sendProblem","statusCode","i","ProblemDocument","getApplication","options","app","express","apis","mapError","enableDefaultExpressEtag","disableJsonMiddleware","disableUrlEncodingMiddleware","disableProblemDetailsMiddleware","router","Router","api","problemDetailsMiddleware","startAPI","port","server","http","HeaderNames","WeakETagRegex","ETagErrors","isWeakETag","etag","getWeakETagValue","result","toWeakETag","value","getETagFromIfMatch","request","getETagFromIfNotMatch","setETag","response","getETagValueFromIfMatch","eTagValue","on","handle","request","response","_next","OK","options","send","Created","sendCreated","Accepted","sendAccepted","NoContent","HttpResponse","statusCode","BadRequest","HttpProblem","Forbidden","NotFound","Conflict","PreconditionFailed","sendProblem","ProblemDocument","DefaultHttpResponseOptions","DefaultHttpProblemResponseOptions","sendCreated","response","eTag","options","send","sendAccepted","statusCode","location","body","setETag","sendProblem","problemDetails","ProblemDocument","supertest","assert","ApiE2ESpecification","getEventStore","getApplication","givenRequests","eventStore","bo","application","setupRequest","handle","requestFn","supertest","verify","response","assertion","supertest","existingStream","streamId","events","expect","expectNewEvents","expectResponse","statusCode","options","response","body","headers","f","Te","expectError","errorCode","problemDetails","ApiSpecification","getEventStore","getApplication","givenStreams","eventStore","bo","application","setupRequest","handle","streamName","verify","so","first","rest"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/application.ts","../../emmett/src/validation/index.ts","../../emmett/src/errors/index.ts","../../emmett/src/validation/dates.ts","../../emmett/src/config/plugins/index.ts","../../emmett/src/eventStore/eventStore.ts","../../emmett/src/typing/command.ts","../../emmett/src/typing/event.ts","../../emmett/src/typing/workflow.ts","../../emmett/src/eventStore/events/index.ts","../../emmett/src/eventStore/expectedVersion.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/streaming/writers/writeToStream.ts","../../emmett/src/utils/deepEquals.ts","../../emmett/src/utils/iterators.ts","../../emmett/src/utils/merge.ts","../../emmett/src/utils/retry.ts","../../emmett/src/commandHandling/handleCommand.ts","../../emmett/src/commandHandling/handleCommandWithDecider.ts","../../emmett/src/messageBus/index.ts","../../emmett/src/projections/index.ts","../../emmett/src/serialization/json/JSONParser.ts","../../emmett/src/streaming/binaryArrays.ts","../../emmett/src/streaming/collectors/collect.ts","../../emmett/src/streaming/decoders/binary.ts","../../emmett/src/streaming/decoders/string.ts","../../emmett/src/streaming/decoders/json.ts","../../emmett/src/streaming/decoders/object.ts","../../emmett/src/streaming/decoders/composite.ts","../../emmett/src/streaming/generators/fromArray.ts","../../emmett/src/streaming/generators/index.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/streaming/transformations/index.ts","../../emmett/src/testing/assertions.ts","../../emmett/src/testing/deciderSpecification.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"],"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 { isNumber, isString } from '../validation';\n\nexport type ErrorConstructor<ErrorType extends Error> = new (\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ...args: any[]\n) => ErrorType;\n\nexport const isErrorConstructor = <ErrorType extends Error>(\n // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\n expect: Function,\n): expect is ErrorConstructor<ErrorType> => {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return (\n typeof expect === 'function' &&\n expect.prototype &&\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n expect.prototype.constructor === expect\n );\n};\n\nexport class EmmettError extends Error {\n public errorCode: number;\n\n constructor(\n options?: { errorCode: number; message?: string } | string | number,\n ) {\n const errorCode =\n options && typeof options === 'object' && 'errorCode' in options\n ? options.errorCode\n : isNumber(options)\n ? options\n : 500;\n const message =\n options && typeof options === 'object' && 'message' in options\n ? options.message\n : isString(options)\n ? options\n : `Error with status code '${errorCode}' ocurred during Emmett processing`;\n\n super(message);\n this.errorCode = errorCode;\n\n // 👇️ because we are extending a built-in class\n Object.setPrototypeOf(this, EmmettError.prototype);\n }\n}\n\nexport class ConcurrencyError extends EmmettError {\n constructor(\n public current: string | undefined,\n public expected: string,\n message?: string,\n ) {\n super({\n errorCode: 412,\n message:\n message ??\n `Expected version ${expected.toString()} does not match current ${current?.toString()}`,\n });\n\n // 👇️ because we are extending a built-in class\n Object.setPrototypeOf(this, ConcurrencyError.prototype);\n }\n}\n\nexport class ValidationError extends EmmettError {\n constructor(message?: string) {\n super({\n errorCode: 400,\n message: message ?? `Validation Error ocurred during Emmett processing`,\n });\n\n // 👇️ because we are extending a built-in class\n Object.setPrototypeOf(this, ValidationError.prototype);\n }\n}\n\nexport class IllegalStateError extends EmmettError {\n constructor(message?: string) {\n super({\n errorCode: 403,\n message: message ?? `Illegal State ocurred during Emmett processing`,\n });\n\n // 👇️ because we are extending a built-in class\n Object.setPrototypeOf(this, IllegalStateError.prototype);\n }\n}\n\nexport class NotFoundError extends EmmettError {\n constructor(options?: { id: string; type: string; message?: string }) {\n super({\n errorCode: 404,\n message:\n options?.message ??\n (options?.id\n ? options.type\n ? `${options.type} with ${options.id} was not found during Emmett processing`\n : `State with ${options.id} was not found during Emmett processing`\n : options?.type\n ? `${options.type} was not found during Emmett processing`\n : 'State was not found during Emmett processing'),\n });\n\n // 👇️ because we are extending a built-in class\n Object.setPrototypeOf(this, NotFoundError.prototype);\n }\n}\n","import { ValidationError } from '../errors';\n\nexport const formatDateToUtcYYYYMMDD = (date: Date) => {\n // Use the 'en-CA' locale which formats as 'yyyy-mm-dd'\n const formatter = new Intl.DateTimeFormat('en-CA', {\n timeZone: 'UTC',\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n });\n\n // Format the date\n return formatter.format(date);\n};\n\n// Function to validate 'yyyy-mm-dd' format\nexport const isValidYYYYMMDD = (dateString: string) => {\n const regex = /^\\d{4}-\\d{2}-\\d{2}$/;\n return regex.test(dateString);\n};\n\nexport const parseDateFromUtcYYYYMMDD = (dateString: string) => {\n const date = new Date(dateString + 'T00:00:00Z');\n\n if (!isValidYYYYMMDD(dateString)) {\n throw new ValidationError('Invalid date format, must be yyyy-mm-dd');\n }\n\n if (isNaN(date.getTime())) {\n throw new ValidationError('Invalid date format');\n }\n\n return date;\n};\n","export type EmmettPluginConfig =\n | {\n name: string;\n register: EmmettPluginRegistration[];\n }\n | string;\n\nexport type EmmettPluginType = 'cli';\n\nexport type EmmettCliPluginRegistration = { pluginType: 'cli'; path?: string };\n\nexport type EmmettPluginRegistration = EmmettCliPluginRegistration;\n\nexport type EmmettCliCommand = {\n addCommand<CliCommand>(command: CliCommand): CliCommand;\n};\n\nexport type EmmettCliPlugin = {\n pluginType: 'cli';\n name: string;\n registerCommands: (program: EmmettCliCommand) => Promise<void> | void;\n};\n\nexport type EmmettPlugin = EmmettCliPlugin;\n\nexport const isPluginConfig = (\n plugin: Partial<EmmettPluginConfig> | string | undefined,\n): plugin is EmmettPluginConfig =>\n plugin !== undefined &&\n (typeof plugin === 'string' ||\n ('name' in plugin &&\n plugin.name !== undefined &&\n typeof plugin.name === 'string'));\n","//import type { ReadableStream } from 'web-streams-polyfill';\nimport type { Event, ReadEvent, ReadEventMetadata } from '../typing';\n//import type { GlobalSubscriptionEvent } from './events';\nimport type { ExpectedStreamVersion } from './expectedVersion';\n\n// #region event-store\nexport interface EventStore<\n StreamVersion = DefaultStreamVersionType,\n ReadEventMetadataType extends ReadEventMetadata = ReadEventMetadata,\n> {\n aggregateStream<State, EventType extends Event>(\n streamName: string,\n options: AggregateStreamOptions<\n State,\n EventType,\n StreamVersion,\n ReadEventMetadataType\n >,\n ): Promise<AggregateStreamResult<State, StreamVersion>>;\n\n readStream<EventType extends Event>(\n streamName: string,\n options?: ReadStreamOptions<StreamVersion>,\n ): Promise<ReadStreamResult<EventType, StreamVersion, ReadEventMetadataType>>;\n\n appendToStream<EventType extends Event>(\n streamName: string,\n events: EventType[],\n options?: AppendToStreamOptions<StreamVersion>,\n ): Promise<AppendToStreamResult<StreamVersion>>;\n\n // streamEvents(): ReadableStream<\n // ReadEvent<Event, ReadEventMetadataType> | GlobalSubscriptionEvent\n // >;\n}\n\nexport type EventStoreSession<\n EventStoreType extends EventStore<StreamVersion>,\n StreamVersion = DefaultStreamVersionType,\n> = {\n eventStore: EventStoreType;\n close: () => Promise<void>;\n};\n\nexport interface EventStoreSessionFactory<\n EventStoreType extends EventStore<StreamVersion>,\n StreamVersion = DefaultStreamVersionType,\n> {\n withSession<T = unknown>(\n callback: (\n session: EventStoreSession<EventStoreType, StreamVersion>,\n ) => Promise<T>,\n ): Promise<T>;\n}\n\nexport type DefaultStreamVersionType = bigint;\n// #endregion event-store\n\nexport const canCreateEventStoreSession = <\n Store extends EventStore<StreamVersion>,\n StreamVersion = DefaultStreamVersionType,\n>(\n eventStore: Store | EventStoreSessionFactory<Store, StreamVersion>,\n): eventStore is EventStoreSessionFactory<Store, StreamVersion> =>\n 'withSession' in eventStore;\n\nexport const nulloSessionFactory = <\n EventStoreType extends EventStore<StreamVersion>,\n StreamVersion = DefaultStreamVersionType,\n>(\n eventStore: EventStoreType,\n): EventStoreSessionFactory<EventStoreType, StreamVersion> => ({\n withSession: (callback) => {\n const nulloSession: EventStoreSession<EventStoreType, StreamVersion> = {\n eventStore,\n close: () => Promise.resolve(),\n };\n\n return callback(nulloSession);\n },\n});\n\n////////////////////////////////////////////////////////////////////\n/// ReadStream types\n////////////////////////////////////////////////////////////////////\n\nexport type ReadStreamOptions<StreamVersion = DefaultStreamVersionType> = (\n | {\n from: StreamVersion;\n }\n | { to: StreamVersion }\n | { from: StreamVersion; maxCount?: bigint }\n | {\n expectedStreamVersion: ExpectedStreamVersion<StreamVersion>;\n }\n) & {\n expectedStreamVersion?: ExpectedStreamVersion<StreamVersion>;\n};\n\nexport type ReadStreamResult<\n EventType extends Event,\n StreamVersion = DefaultStreamVersionType,\n ReadEventMetadataType extends ReadEventMetadata = ReadEventMetadata,\n> = {\n currentStreamVersion: StreamVersion;\n events: ReadEvent<EventType, ReadEventMetadataType>[];\n streamExists: boolean;\n};\n\n////////////////////////////////////////////////////////////////////\n/// AggregateStream types\n////////////////////////////////////////////////////////////////////\n\ntype Evolve<\n State,\n EventType extends Event,\n ReadEventMetadataType extends ReadEventMetadata = ReadEventMetadata,\n> =\n | ((currentState: State, event: EventType) => State)\n | ((\n currentState: State,\n event: ReadEvent<EventType, ReadEventMetadataType>,\n ) => State)\n | ((currentState: State, event: ReadEvent<EventType>) => State);\n\nexport type AggregateStreamOptions<\n State,\n EventType extends Event,\n StreamVersion = DefaultStreamVersionType,\n ReadEventMetadataType extends ReadEventMetadata = ReadEventMetadata,\n> = {\n evolve: Evolve<State, EventType, ReadEventMetadataType>;\n initialState: () => State;\n read?: ReadStreamOptions<StreamVersion>;\n};\n\nexport type AggregateStreamResult<\n State,\n StreamVersion = DefaultStreamVersionType,\n> = {\n currentStreamVersion: StreamVersion;\n state: State;\n streamExists: boolean;\n};\n\n////////////////////////////////////////////////////////////////////\n/// AppendToStream types\n////////////////////////////////////////////////////////////////////\n\nexport type AppendToStreamOptions<StreamVersion = DefaultStreamVersionType> = {\n expectedStreamVersion?: ExpectedStreamVersion<StreamVersion>;\n};\n\nexport type AppendToStreamResult<StreamVersion = DefaultStreamVersionType> = {\n nextExpectedStreamVersion: StreamVersion;\n createdNewStream: boolean;\n};\n","import type { DefaultRecord, Flavour } from './';\nexport type Command<\n CommandType extends string = string,\n CommandData extends DefaultRecord = DefaultRecord,\n CommandMetaData extends DefaultRecord = DefaultCommandMetadata,\n> = Flavour<\n Readonly<{\n type: CommandType;\n data: Readonly<CommandData>;\n metadata?: CommandMetaData | undefined;\n }>,\n 'Command'\n>;\n\nexport type CommandTypeOf<T extends Command> = T['type'];\nexport type CommandDataOf<T extends Command> = T['data'];\nexport type CommandMetaDataOf<T extends Command> = T['metadata'];\n\nexport type CreateCommandType<\n CommandType extends string,\n CommandData extends DefaultRecord,\n CommandMetaData extends DefaultRecord | undefined,\n> = Readonly<{\n type: CommandType;\n data: CommandData;\n metadata?: CommandMetaData;\n}>;\n\nexport const command = <CommandType extends Command>(\n type: CommandTypeOf<CommandType>,\n data: CommandDataOf<CommandType>,\n metadata?: CommandMetaDataOf<CommandType>,\n): CreateCommandType<\n CommandTypeOf<CommandType>,\n CommandDataOf<CommandType>,\n CommandMetaDataOf<CommandType>\n> => {\n return {\n type,\n data,\n metadata,\n };\n};\n\nexport type DefaultCommandMetadata = { now: Date };\n","import type { DefaultRecord, Flavour } from './';\n\nexport type Event<\n EventType extends string = string,\n EventData extends DefaultRecord = DefaultRecord,\n EventMetaData extends DefaultRecord = DefaultRecord,\n> = Flavour<\n Readonly<{\n type: EventType;\n data: EventData;\n metadata?: EventMetaData;\n }>,\n 'Event'\n>;\n\nexport type EventTypeOf<T extends Event> = T['type'];\nexport type EventDataOf<T extends Event> = T['data'];\nexport type EventMetaDataOf<T extends Event> = T['metadata'];\n\nexport type CanHandle<T extends Event> = EventTypeOf<T>[];\n\nexport type CreateEventType<\n EventType extends string,\n EventData extends DefaultRecord,\n EventMetaData extends DefaultRecord | undefined,\n> = Readonly<{\n type: EventType;\n data: EventData;\n metadata?: EventMetaData;\n}>;\n\nexport const event = <EventType extends Event>(\n type: EventTypeOf<EventType>,\n data: EventDataOf<EventType>,\n metadata?: EventMetaDataOf<EventType>,\n): CreateEventType<\n EventTypeOf<EventType>,\n EventDataOf<EventType>,\n EventMetaDataOf<EventType>\n> => {\n return {\n type,\n data,\n metadata,\n };\n};\n\nexport type ReadEvent<\n EventType extends Event = Event,\n EventMetaDataType extends EventMetaDataOf<EventType> &\n ReadEventMetadata = EventMetaDataOf<EventType> & ReadEventMetadata,\n> = CreateEventType<\n EventTypeOf<EventType>,\n EventDataOf<EventType>,\n EventMetaDataType\n> &\n EventType & { metadata: EventMetaDataType };\n\nexport type ReadEventMetadata = Readonly<{\n eventId: string;\n streamPosition: bigint;\n streamName: string;\n}>;\n\nexport type ReadEventMetadataWithGlobalPosition = ReadEventMetadata & {\n globalPosition: bigint;\n};\n","import type { Command } from './command';\nimport type { Event } from './event';\n\n/// Inspired by https://blog.bittacklr.be/the-workflow-pattern.html\n\nexport type Workflow<\n Input extends Event | Command,\n State,\n Output extends Event | Command,\n> = {\n decide: (command: Input, state: State) => WorkflowOutput<Output>[];\n evolve: (currentState: State, event: WorkflowEvent<Output>) => State;\n initialState: () => State;\n};\n\nexport type WorkflowEvent<Output extends Command | Event> = Extract<\n Output,\n { __brand?: 'Event' }\n>;\n\nexport type WorkflowCommand<Output extends Command | Event> = Extract<\n Output,\n { __brand?: 'Command' }\n>;\n\nexport type WorkflowOutput<TOutput extends Command | Event> =\n | { kind: 'Reply'; message: TOutput }\n | { kind: 'Send'; message: WorkflowCommand<TOutput> }\n | { kind: 'Publish'; message: WorkflowEvent<TOutput> }\n | {\n kind: 'Schedule';\n message: TOutput;\n when: { afterInMs: number } | { at: Date };\n }\n | { kind: 'Complete' }\n | { kind: 'Accept' }\n | { kind: 'Ignore'; reason: string }\n | { kind: 'Error'; reason: string };\n\nexport const reply = <TOutput extends Command | Event>(\n message: TOutput,\n): WorkflowOutput<TOutput> => {\n return {\n kind: 'Reply',\n message,\n };\n};\n\nexport const send = <TOutput extends Command | Event>(\n message: WorkflowCommand<TOutput>,\n): WorkflowOutput<TOutput> => {\n return {\n kind: 'Send',\n message,\n };\n};\n\nexport const publish = <TOutput extends Command | Event>(\n message: WorkflowEvent<TOutput>,\n): WorkflowOutput<TOutput> => {\n return {\n kind: 'Publish',\n message,\n };\n};\n\nexport const schedule = <TOutput extends Command | Event>(\n message: TOutput,\n when: { afterInMs: number } | { at: Date },\n): WorkflowOutput<TOutput> => {\n return {\n kind: 'Schedule',\n message,\n when,\n };\n};\n\nexport const complete = <\n TOutput extends Command | Event,\n>(): WorkflowOutput<TOutput> => {\n return {\n kind: 'Complete',\n };\n};\n\nexport const ignore = <TOutput extends Command | Event>(\n reason: string,\n): WorkflowOutput<TOutput> => {\n return {\n kind: 'Ignore',\n reason,\n };\n};\n\nexport const error = <TOutput extends Command | Event>(\n reason: string,\n): WorkflowOutput<TOutput> => {\n return {\n kind: 'Error',\n reason,\n };\n};\n\nexport const accept = <\n TOutput extends Command | Event,\n>(): WorkflowOutput<TOutput> => {\n return { kind: 'Accept' };\n};\n","import {\n event,\n type Event,\n type EventDataOf,\n type ReadEvent,\n type ReadEventMetadataWithGlobalPosition,\n} from '../../typing';\n\nexport const GlobalStreamCaughtUpType = '__emt:GlobalStreamCaughtUp';\n\nexport type GlobalStreamCaughtUp = Event<\n '__emt:GlobalStreamCaughtUp',\n { globalPosition: bigint },\n { globalPosition: bigint }\n>;\n\nexport const isGlobalStreamCaughtUp = (\n event: Event,\n): event is GlobalStreamCaughtUp => event.type === GlobalStreamCaughtUpType;\n\nexport const caughtUpEventFrom =\n (position: bigint) =>\n (\n event: ReadEvent<Event, ReadEventMetadataWithGlobalPosition>,\n ): event is ReadEvent<\n GlobalStreamCaughtUp,\n ReadEventMetadataWithGlobalPosition\n > =>\n event.type === GlobalStreamCaughtUpType &&\n event.metadata?.globalPosition >= position;\n\nexport const globalStreamCaughtUp = (\n data: EventDataOf<GlobalStreamCaughtUp>,\n): GlobalStreamCaughtUp =>\n event<GlobalStreamCaughtUp>(GlobalStreamCaughtUpType, data, {\n globalPosition: data.globalPosition,\n });\n\nexport const isSubscriptionEvent = (\n event: Event,\n): event is GlobalSubscriptionEvent => isGlobalStreamCaughtUp(event);\n\nexport const isNotInternalEvent = (event: Event): boolean =>\n !isGlobalStreamCaughtUp(event);\n\nexport type GlobalSubscriptionEvent = GlobalStreamCaughtUp;\n","import { ConcurrencyError } from '../errors';\nimport type { Flavour } from '../typing';\nimport type { DefaultStreamVersionType } from './eventStore';\n\nexport type ExpectedStreamVersion<VersionType = DefaultStreamVersionType> =\n | ExpectedStreamVersionWithValue<VersionType>\n | ExpectedStreamVersionGeneral;\n\nexport type ExpectedStreamVersionWithValue<\n VersionType = DefaultStreamVersionType,\n> = Flavour<VersionType, 'StreamVersion'>;\n\nexport type ExpectedStreamVersionGeneral = Flavour<\n 'STREAM_EXISTS' | 'STREAM_DOES_NOT_EXIST' | 'NO_CONCURRENCY_CHECK',\n 'StreamVersion'\n>;\n\nexport const STREAM_EXISTS = 'STREAM_EXISTS' as ExpectedStreamVersionGeneral;\nexport const STREAM_DOES_NOT_EXIST =\n 'STREAM_DOES_NOT_EXIST' as ExpectedStreamVersionGeneral;\nexport const NO_CONCURRENCY_CHECK =\n 'NO_CONCURRENCY_CHECK' as ExpectedStreamVersionGeneral;\n\nexport const matchesExpectedVersion = <\n StreamVersion = DefaultStreamVersionType,\n>(\n current: StreamVersion | undefined,\n expected: ExpectedStreamVersion<StreamVersion>,\n defaultVersion: StreamVersion,\n): boolean => {\n if (expected === NO_CONCURRENCY_CHECK) return true;\n\n if (expected == STREAM_DOES_NOT_EXIST) return current === defaultVersion;\n\n if (expected == STREAM_EXISTS) return current !== defaultVersion;\n\n return current === expected;\n};\n\nexport const assertExpectedVersionMatchesCurrent = <\n StreamVersion = DefaultStreamVersionType,\n>(\n current: StreamVersion,\n expected: ExpectedStreamVersion<StreamVersion> | undefined,\n defaultVersion: StreamVersion,\n): void => {\n expected ??= NO_CONCURRENCY_CHECK;\n\n if (!matchesExpectedVersion(current, expected, defaultVersion))\n throw new ExpectedVersionConflictError(current, expected);\n};\n\nexport class ExpectedVersionConflictError<\n VersionType = DefaultStreamVersionType,\n> extends ConcurrencyError {\n constructor(\n current: VersionType,\n expected: ExpectedStreamVersion<VersionType>,\n ) {\n super(current?.toString(), expected?.toString());\n\n // 👇️ because we are extending a built-in class\n Object.setPrototypeOf(this, ExpectedVersionConflictError.prototype);\n }\n}\n\nexport const isExpectedVersionConflictError = (\n error: unknown,\n): error is ExpectedVersionConflictError =>\n error instanceof ExpectedVersionConflictError;\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 type { ReadableStream, TransformStream } from 'web-streams-polyfill';\nimport { noMoreWritingOn } from './noMoreWritingOn';\n\nexport const writeToStream = async <In, Out = In>(\n stream: TransformStream<In, Out>,\n items: In[],\n): Promise<boolean> => {\n if (stream.writable.locked) return false;\n\n const writer = stream.writable.getWriter();\n await writer.ready;\n\n if (!stream.readable.locked) return false;\n\n try {\n for (const item of items) {\n await writer.write(item);\n }\n } catch (error) {\n console.log(error);\n } finally {\n await writer.close();\n }\n return true;\n};\n\nexport const writeToStreamAndStop = async <In, Out>(\n stream: TransformStream<In, Out>,\n items: In[],\n): Promise<ReadableStream<Out>> => {\n await writeToStream(stream, items);\n return await noMoreWritingOn(stream);\n};\n","export const deepEquals = <T>(left: T, right: T): boolean => {\n if (isEquatable(left)) {\n return left.equals(right);\n }\n\n if (Array.isArray(left)) {\n return (\n Array.isArray(right) &&\n left.length === right.length &&\n left.every((val, index) => deepEquals(val, right[index]))\n );\n }\n\n if (\n typeof left !== 'object' ||\n typeof right !== 'object' ||\n left === null ||\n right === null\n ) {\n return left === right;\n }\n\n if (Array.isArray(right)) return false;\n\n const keys1 = Object.keys(left);\n const keys2 = Object.keys(right);\n\n if (\n keys1.length !== keys2.length ||\n !keys1.every((key) => keys2.includes(key))\n )\n return false;\n\n for (const key in left) {\n if (left[key] instanceof Function && right[key] instanceof Function)\n continue;\n\n const isEqual = deepEquals(left[key], right[key]);\n if (!isEqual) {\n return false;\n }\n }\n\n return true;\n};\n\nexport type Equatable<T> = { equals: (right: T) => boolean } & T;\n\nexport const isEquatable = <T>(left: T): left is Equatable<T> => {\n return (\n left &&\n typeof left === 'object' &&\n 'equals' in left &&\n typeof left['equals'] === 'function'\n );\n};\n","export const sum = (\n iterator: Iterator<number, number, number> | Iterator<number>,\n) => {\n let value,\n done: boolean | undefined,\n sum = 0;\n do {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n ({ value, done } = iterator.next());\n sum += value || 0;\n } while (!done);\n return sum;\n};\n","export const merge = <T>(\n array: T[],\n item: T,\n where: (current: T) => boolean,\n onExisting: (current: T) => T,\n onNotFound: () => T | undefined = () => undefined,\n) => {\n let wasFound = false;\n\n const result = array\n // merge the existing item if matches condition\n .map((p: T) => {\n if (!where(p)) return p;\n\n wasFound = true;\n return onExisting(p);\n })\n // filter out item if undefined was returned\n // for cases of removal\n .filter((p) => p !== undefined)\n // make TypeScript happy\n .map((p) => {\n if (!p) throw Error('That should not happen');\n\n return p;\n });\n\n // if item was not found and onNotFound action is defined\n // try to generate new item\n if (!wasFound) {\n const result = onNotFound();\n\n if (result !== undefined) return [...array, item];\n }\n\n return result;\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 {\n canCreateEventStoreSession,\n isExpectedVersionConflictError,\n NO_CONCURRENCY_CHECK,\n nulloSessionFactory,\n STREAM_DOES_NOT_EXIST,\n type AppendToStreamResult,\n type DefaultStreamVersionType,\n type EventStore,\n type EventStoreSession,\n type ExpectedStreamVersion,\n} from '../eventStore';\nimport type { Event } from '../typing';\nimport { asyncRetry, NoRetries, type AsyncRetryOptions } from '../utils';\n\nexport const CommandHandlerStreamVersionConflictRetryOptions: AsyncRetryOptions =\n {\n retries: 3,\n minTimeout: 100,\n factor: 1.5,\n shouldRetryError: isExpectedVersionConflictError,\n };\n\nexport type CommandHandlerRetryOptions =\n | AsyncRetryOptions\n | { onVersionConflict: true | number | AsyncRetryOptions };\n\nconst fromCommandHandlerRetryOptions = (\n retryOptions: CommandHandlerRetryOptions | undefined,\n): AsyncRetryOptions => {\n if (retryOptions === undefined) return NoRetries;\n\n if ('onVersionConflict' in retryOptions) {\n if (typeof retryOptions.onVersionConflict === 'boolean')\n return CommandHandlerStreamVersionConflictRetryOptions;\n else if (typeof retryOptions.onVersionConflict === 'number')\n return {\n ...CommandHandlerStreamVersionConflictRetryOptions,\n retries: retryOptions.onVersionConflict,\n };\n else return retryOptions.onVersionConflict;\n }\n\n return retryOptions;\n};\n\n// #region command-handler\nexport type CommandHandlerResult<\n State,\n StreamEvent extends Event,\n StreamVersion = DefaultStreamVersionType,\n> = AppendToStreamResult<StreamVersion> & {\n newState: State;\n newEvents: StreamEvent[];\n};\n\nexport type CommandHandlerOptions<State, StreamEvent extends Event> = {\n evolve: (state: State, event: StreamEvent) => State;\n initialState: () => State;\n mapToStreamId?: (id: string) => string;\n retry?: CommandHandlerRetryOptions;\n};\n\nexport type HandleOptions<\n StreamVersion,\n Store extends EventStore<StreamVersion>,\n> = Parameters<Store['appendToStream']>[2] &\n (\n | {\n expectedStreamVersion?: ExpectedStreamVersion<StreamVersion>;\n }\n | {\n retry?: CommandHandlerRetryOptions;\n }\n );\n\nexport const CommandHandler =\n <State, StreamEvent extends Event, StreamVersion = DefaultStreamVersionType>(\n options: CommandHandlerOptions<State, StreamEvent>,\n ) =>\n async <Store extends EventStore<StreamVersion>>(\n store: Store,\n id: string,\n handle: (\n state: State,\n ) =>\n | StreamEvent\n | StreamEvent[]\n | Promise<StreamEvent>\n | Promise<StreamEvent[]>,\n handleOptions?: HandleOptions<StreamVersion, Store>,\n ): Promise<CommandHandlerResult<State, StreamEvent, StreamVersion>> =>\n asyncRetry(\n async () => {\n const result = await withSession<\n Store,\n StreamVersion,\n CommandHandlerResult<State, StreamEvent, StreamVersion>\n >(store, async ({ eventStore }) => {\n const { evolve, initialState } = options;\n const mapToStreamId = options.mapToStreamId ?? ((id) => id);\n\n const streamName = mapToStreamId(id);\n\n // 1. Aggregate the stream\n const aggregationResult = await eventStore.aggregateStream<\n State,\n StreamEvent\n >(streamName, {\n evolve,\n initialState,\n read: {\n // expected stream version is passed to fail fast\n // if stream is in the wrong state\n expectedStreamVersion:\n handleOptions?.expectedStreamVersion ?? NO_CONCURRENCY_CHECK,\n },\n });\n\n // 2. Use the aggregate state\n const state = aggregationResult.state;\n const currentStreamVersion = aggregationResult.currentStreamVersion;\n\n // 3. Run business logic\n const result = await handle(state);\n\n const newEvents = Array.isArray(result) ? result : [result];\n\n if (newEvents.length === 0) {\n return {\n newEvents: [],\n newState: state,\n nextExpectedStreamVersion: currentStreamVersion,\n createdNewStream: false,\n };\n }\n\n // Either use:\n // - provided expected stream version,\n // - current stream version got from stream aggregation,\n // - or expect stream not to exists otherwise.\n const expectedStreamVersion: ExpectedStreamVersion<StreamVersion> =\n handleOptions?.expectedStreamVersion ??\n (aggregationResult.streamExists\n ? (currentStreamVersion as ExpectedStreamVersion<StreamVersion>)\n : STREAM_DOES_NOT_EXIST);\n\n // 4. Append result to the stream\n const appendResult = await eventStore.appendToStream(\n streamName,\n newEvents,\n {\n ...handleOptions,\n expectedStreamVersion,\n },\n );\n\n // 5. Return result with updated state\n return {\n ...appendResult,\n newEvents,\n newState: newEvents.reduce(evolve, state),\n };\n });\n\n return result;\n },\n fromCommandHandlerRetryOptions(\n handleOptions && 'retry' in handleOptions\n ? handleOptions.retry\n : options.retry,\n ),\n );\n// #endregion command-handler\n\nconst withSession = <\n EventStoreType extends EventStore<StreamVersion>,\n StreamVersion = DefaultStreamVersionType,\n T = unknown,\n>(\n eventStore: EventStoreType,\n callback: (\n session: EventStoreSession<EventStoreType, StreamVersion>,\n ) => Promise<T>,\n) => {\n const sessionFactory = canCreateEventStoreSession<\n EventStoreType,\n StreamVersion\n >(eventStore)\n ? eventStore\n : nulloSessionFactory<EventStoreType, StreamVersion>(eventStore);\n\n return sessionFactory.withSession(callback);\n};\n","import type { DefaultStreamVersionType, EventStore } from '../eventStore';\nimport type { Command, Event } from '../typing';\nimport type { Decider } from '../typing/decider';\nimport {\n CommandHandler,\n type CommandHandlerOptions,\n type HandleOptions,\n} from './handleCommand';\n\n// #region command-handler\n\nexport type DeciderCommandHandlerOptions<\n State,\n CommandType extends Command,\n StreamEvent extends Event,\n> = CommandHandlerOptions<State, StreamEvent> &\n Decider<State, CommandType, StreamEvent>;\n\nexport const DeciderCommandHandler =\n <\n State,\n CommandType extends Command,\n StreamEvent extends Event,\n StreamVersion = DefaultStreamVersionType,\n >(\n options: DeciderCommandHandlerOptions<State, CommandType, StreamEvent>,\n ) =>\n async (\n eventStore: EventStore<StreamVersion>,\n id: string,\n command: CommandType,\n handleOptions?: HandleOptions<StreamVersion, EventStore<StreamVersion>>,\n ) => {\n const { decide, ...rest } = options;\n\n return CommandHandler<State, StreamEvent, StreamVersion>(rest)(\n eventStore,\n id,\n (state) => decide(command, state),\n handleOptions,\n );\n };\n// #endregion command-handler\n","import { EmmettError } from '../errors';\nimport {\n type Command,\n type CommandTypeOf,\n type Event,\n type EventTypeOf,\n} from '../typing';\n\nexport interface CommandSender {\n send<CommandType extends Command = Command>(\n command: CommandType,\n ): Promise<void>;\n}\n\nexport interface EventsPublisher {\n publish<EventType extends Event = Event>(event: EventType): Promise<void>;\n}\n\nexport type ScheduleOptions = { afterInMs: number } | { at: Date };\n\nexport interface MessageScheduler<CommandOrEvent extends Command | Event> {\n schedule<MessageType extends CommandOrEvent>(\n message: MessageType,\n when?: ScheduleOptions,\n ): void;\n}\n\nexport interface CommandBus extends CommandSender, MessageScheduler<Command> {}\n\nexport interface EventBus extends EventsPublisher, MessageScheduler<Event> {}\n\nexport interface MessageBus extends CommandBus, EventBus {\n schedule<MessageType extends Command | Event>(\n message: MessageType,\n when?: ScheduleOptions,\n ): void;\n}\n\ntype CommandHandler<CommandType extends Command = Command> = (\n command: CommandType,\n) => Promise<void> | void;\n\nexport interface CommandProcessor {\n handle<CommandType extends Command>(\n commandHandler: CommandHandler<CommandType>,\n ...commandTypes: CommandTypeOf<CommandType>[]\n ): void;\n}\n\ntype EventHandler<EventType extends Event = Event> = (\n event: EventType,\n) => Promise<void> | void;\n\nexport interface EventProcessor {\n subscribe<EventType extends Event>(\n eventHandler: EventHandler<EventType>,\n ...eventTypes: EventTypeOf<EventType>[]\n ): void;\n}\n\nexport type ScheduledMessage = {\n message: Event | Command;\n options?: ScheduleOptions;\n};\n\nexport interface ScheduledMessageProcessor {\n dequeue(): ScheduledMessage[];\n}\n\nexport type MessageHandler = CommandHandler | EventHandler;\n\nexport type MessageProcessor = EventProcessor | CommandProcessor;\n\nexport const getInMemoryMessageBus = (): MessageBus &\n EventProcessor &\n CommandProcessor &\n ScheduledMessageProcessor => {\n const allHandlers = new Map<string, MessageHandler[]>();\n let pendingMessages: ScheduledMessage[] = [];\n\n return {\n send: async <CommandType extends Command = Command>(\n command: CommandType,\n ): Promise<void> => {\n const handlers = allHandlers.get(command.type);\n\n if (handlers === undefined || handlers.length === 0)\n throw new EmmettError(\n `No handler registered for command ${command.type}!`,\n );\n\n const commandHandler = handlers[0] as CommandHandler<CommandType>;\n\n await commandHandler(command);\n },\n\n publish: async <EventType extends Event = Event>(\n event: EventType,\n ): Promise<void> => {\n const handlers = allHandlers.get(event.type) ?? [];\n\n for (const handler of handlers) {\n const eventHandler = handler as EventHandler<EventType>;\n\n await eventHandler(event);\n }\n },\n\n schedule: <MessageType extends Command | Event>(\n message: MessageType,\n when?: ScheduleOptions,\n ): void => {\n pendingMessages = [...pendingMessages, { message, options: when }];\n },\n\n handle: <CommandType extends Command>(\n commandHandler: CommandHandler<CommandType>,\n ...commandTypes: CommandTypeOf<CommandType>[]\n ): void => {\n const alreadyRegistered = [...allHandlers.keys()].filter((registered) =>\n commandTypes.includes(registered),\n );\n\n if (alreadyRegistered.length > 0)\n throw new EmmettError(\n `Cannot register handler for commands ${alreadyRegistered.join(', ')} as they're already registered!`,\n );\n for (const commandType of commandTypes) {\n allHandlers.set(commandType, [commandHandler as MessageHandler]);\n }\n },\n\n subscribe<EventType extends Event>(\n eventHandler: EventHandler<EventType>,\n ...eventTypes: EventTypeOf<EventType>[]\n ): void {\n for (const eventType of eventTypes) {\n if (!allHandlers.has(eventType)) allHandlers.set(eventType, []);\n\n allHandlers.set(eventType, [\n ...(allHandlers.get(eventType) ?? []),\n eventHandler as MessageHandler,\n ]);\n }\n },\n\n dequeue: (): ScheduledMessage[] => {\n const pending = pendingMessages;\n pendingMessages = [];\n return pending;\n },\n };\n};\n","import type {\n CanHandle,\n DefaultRecord,\n Event,\n EventMetaDataOf,\n ReadEvent,\n ReadEventMetadata,\n} from '../typing';\n\nexport type ProjectionHandlingType = 'inline' | 'async';\n\nexport type ProjectionHandler<\n EventType extends Event = Event,\n EventMetaDataType extends EventMetaDataOf<EventType> &\n ReadEventMetadata = EventMetaDataOf<EventType> & ReadEventMetadata,\n ProjectionHandlerContext extends DefaultRecord = DefaultRecord,\n> = (\n events: ReadEvent<EventType, EventMetaDataType>[],\n context: ProjectionHandlerContext,\n) => Promise<void> | void;\n\nexport interface ProjectionDefinition<\n ProjectionHandlerContext extends DefaultRecord = DefaultRecord,\n> {\n name?: string;\n canHandle: CanHandle<Event>;\n handle: ProjectionHandler<Event, ReadEventMetadata, ProjectionHandlerContext>;\n}\n\nexport interface TypedProjectionDefinition<\n EventType extends Event = Event,\n EventMetaDataType extends EventMetaDataOf<EventType> &\n ReadEventMetadata = EventMetaDataOf<EventType> & ReadEventMetadata,\n ProjectionHandlerContext extends DefaultRecord = DefaultRecord,\n> {\n name?: string;\n canHandle: CanHandle<EventType>;\n handle: ProjectionHandler<\n EventType,\n EventMetaDataType,\n ProjectionHandlerContext\n >;\n}\n\nexport type ProjectionRegistration<\n HandlingType extends ProjectionHandlingType,\n ProjectionHandlerContext extends DefaultRecord = DefaultRecord,\n> = {\n type: HandlingType;\n projection: ProjectionDefinition<ProjectionHandlerContext>;\n};\n\nexport const projection = <\n EventType extends Event = Event,\n EventMetaDataType extends EventMetaDataOf<EventType> &\n ReadEventMetadata = EventMetaDataOf<EventType> & ReadEventMetadata,\n ProjectionHandlerContext extends DefaultRecord = DefaultRecord,\n ProjectionDefintionType extends TypedProjectionDefinition<\n EventType,\n EventMetaDataType,\n ProjectionHandlerContext\n > = ProjectionDefinition<ProjectionHandlerContext>,\n>(\n definition: ProjectionDefintionType,\n): ProjectionDefintionType => definition;\n\nexport const inlineProjections = <\n ProjectionHandlerContext extends DefaultRecord = DefaultRecord,\n ProjectionDefintionType extends\n ProjectionDefinition<ProjectionHandlerContext> = ProjectionDefinition<ProjectionHandlerContext>,\n>(\n definitions: ProjectionDefintionType[],\n): ProjectionRegistration<'inline', ProjectionHandlerContext>[] =>\n definitions.map((projection) => ({ type: 'inline', projection }));\n\nexport const asyncProjections = <\n ProjectionHandlerContext extends DefaultRecord = DefaultRecord,\n ProjectionDefintionType extends\n ProjectionDefinition<ProjectionHandlerContext> = ProjectionDefinition<ProjectionHandlerContext>,\n>(\n definitions: ProjectionDefintionType[],\n): ProjectionRegistration<'async', ProjectionHandlerContext>[] =>\n definitions.map((projection) => ({ type: 'async', projection }));\n\nexport const projections = {\n inline: inlineProjections,\n async: asyncProjections,\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","export const concatUint8Arrays = (chunks: Uint8Array[]): Uint8Array => {\n const totalLength = chunks.reduce((acc, chunk) => acc + chunk.length, 0);\n const result = new Uint8Array(totalLength);\n let offset = 0;\n for (const chunk of chunks) {\n result.set(chunk, offset);\n offset += chunk.length;\n }\n return result;\n};\n","import type { ReadableStream } from 'web-streams-polyfill';\n\nexport const collect = async <T>(stream: ReadableStream<T>): Promise<T[]> => {\n const results: T[] = [];\n\n for await (const value of stream) {\n results.push(value as T);\n }\n\n return results;\n};\n","import type { Decoder } from '.';\nimport { concatUint8Arrays } from '../binaryArrays';\n\nexport class BinaryJsonDecoder<Decoded>\n implements Decoder<Uint8Array, Decoded>\n{\n private buffer: Uint8Array[] = [];\n\n addToBuffer(data: Uint8Array): void {\n this.buffer.push(data);\n }\n\n clearBuffer(): void {\n this.buffer = [];\n }\n\n hasCompleteMessage(): boolean {\n const combined = concatUint8Arrays(this.buffer);\n const text = new TextDecoder().decode(combined);\n return text.includes('\\n');\n }\n\n decode(): Decoded | null {\n if (!this.hasCompleteMessage()) {\n return null;\n }\n\n const combined = concatUint8Arrays(this.buffer);\n const text = new TextDecoder().decode(combined);\n const delimiterIndex = text.indexOf('\\n');\n\n if (delimiterIndex === -1) {\n return null;\n }\n\n const jsonString = text.slice(0, delimiterIndex);\n const remaining = new Uint8Array(combined.buffer, delimiterIndex + 1);\n this.buffer = remaining.byteLength > 0 ? [remaining] : [];\n\n return JSON.parse(jsonString) as Decoded;\n }\n}\n","import type { Decoder } from '.';\n\nexport class StringDecoder<Decoded> implements Decoder<string, Decoded> {\n protected buffer: string[] = [];\n\n constructor(private transform: (input: string) => Decoded) {\n this.transform = transform;\n }\n\n addToBuffer(data: string): void {\n this.buffer.push(data);\n }\n\n clearBuffer(): void {\n this.buffer = [];\n }\n\n hasCompleteMessage(): boolean {\n return this.buffer.some((chunk) => chunk.includes('\\n'));\n }\n\n decode(): Decoded | null {\n const completeString = this.buffer.join('');\n\n if (!this.hasCompleteMessage()) {\n if (completeString.trim().length > 0) {\n throw new Error('Unterminated string in JSON at position');\n }\n return null;\n }\n\n const delimiterIndex = completeString.indexOf('\\n');\n const message = completeString.slice(0, delimiterIndex).trim();\n this.buffer = [completeString.slice(delimiterIndex + 1)];\n\n return this.transform(message);\n }\n}\n","import { StringDecoder } from './string';\n\nexport class JsonDecoder<Decoded> extends StringDecoder<Decoded> {\n constructor() {\n super((jsonString) => JSON.parse(jsonString) as Decoded);\n }\n}\n","import type { Decoder } from '.';\n\nexport class ObjectDecoder<Decoded> implements Decoder<Decoded, Decoded> {\n private buffer: Decoded | null = null;\n\n addToBuffer(data: Decoded): void {\n this.buffer = data;\n }\n\n clearBuffer(): void {\n this.buffer = null;\n }\n\n hasCompleteMessage(): boolean {\n return this.buffer !== null;\n }\n\n decode(): Decoded | null {\n if (!this.hasCompleteMessage() || !this.buffer) {\n return null;\n }\n\n const data = this.buffer;\n this.clearBuffer();\n return data as Decoded;\n }\n}\n","import type { Decoder } from '.';\nimport { BinaryJsonDecoder } from './binary';\nimport { JsonDecoder } from './json';\nimport { ObjectDecoder } from './object';\n\nexport class CompositeDecoder<Decoded> implements Decoder<unknown, Decoded> {\n constructor(\n private decoders: [(data: unknown) => boolean, Decoder<unknown, Decoded>][],\n ) {}\n\n private decoderFor(data: unknown): Decoder<unknown, unknown> | null {\n const decoder = this.decoders.find((d) => d[0](data));\n\n if (!decoder) return null;\n\n return decoder[1];\n }\n\n addToBuffer(data: unknown): void {\n this.decoderFor(data)?.addToBuffer(data);\n }\n\n clearBuffer(): void {\n for (const decoder of this.decoders.map((d) => d[1])) {\n decoder.clearBuffer();\n }\n }\n\n hasCompleteMessage(): boolean {\n return this.decoders.some((d) => d[1].hasCompleteMessage());\n }\n\n decode(): Decoded | null {\n const decoder = this.decoders\n .map((d) => d[1])\n .find((d) => d.hasCompleteMessage());\n\n return decoder?.decode() ?? null;\n }\n}\n\nexport class DefaultDecoder<Decoded> extends CompositeDecoder<Decoded> {\n constructor() {\n super([\n [(data) => typeof data === 'string', new JsonDecoder<Decoded>()],\n [(data) => data instanceof Uint8Array, new BinaryJsonDecoder<Decoded>()],\n [(data) => typeof data === 'object', new ObjectDecoder<Decoded>()],\n ]);\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 { fromArray } from './fromArray';\nexport const streamGenerators = { fromArray };\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","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 { isErrorConstructor, type ErrorConstructor } from '../errors';\nimport { AssertionError, assertMatches, assertTrue } from './assertions';\n\ntype ErrorCheck<ErrorType> = (error: ErrorType) => boolean;\n\nexport type ThenThrows<ErrorType extends Error> =\n | (() => void)\n | ((errorConstructor: ErrorConstructor<ErrorType>) => void)\n | ((errorCheck: ErrorCheck<ErrorType>) => void)\n | ((\n errorConstructor: ErrorConstructor<ErrorType>,\n errorCheck?: ErrorCheck<ErrorType>,\n ) => void);\n\nexport type DeciderSpecfication<Command, Event> = (\n givenEvents: Event | Event[],\n) => {\n when: (command: Command) => {\n then: (expectedEvents: Event | Event[]) => void;\n thenThrows: <ErrorType extends Error = Error>(\n ...args: Parameters<ThenThrows<ErrorType>>\n ) => void;\n };\n};\n\nexport const DeciderSpecification = {\n for: <Command, Event, State>(decider: {\n decide: (command: Command, state: State) => Event | Event[];\n evolve: (state: State, event: Event) => State;\n initialState: () => State;\n }): DeciderSpecfication<Command, Event> => {\n {\n return (givenEvents: Event | Event[]) => {\n return {\n when: (command: Command) => {\n const handle = () => {\n const existingEvents = Array.isArray(givenEvents)\n ? givenEvents\n : [givenEvents];\n\n const currentState = existingEvents.reduce<State>(\n decider.evolve,\n decider.initialState(),\n );\n\n return decider.decide(command, currentState);\n };\n\n return {\n then: (expectedEvents: Event | Event[]): void => {\n const resultEvents = handle();\n\n const resultEventsArray = Array.isArray(resultEvents)\n ? resultEvents\n : [resultEvents];\n\n const expectedEventsArray = Array.isArray(expectedEvents)\n ? expectedEvents\n : [expectedEvents];\n\n assertMatches(resultEventsArray, expectedEventsArray);\n },\n thenThrows: <ErrorType extends Error>(\n ...args: Parameters<ThenThrows<ErrorType>>\n ): void => {\n try {\n handle();\n throw new AssertionError('Handler did not fail as expected');\n } catch (error) {\n if (error instanceof AssertionError) throw error;\n\n if (args.length === 0) return;\n\n if (!isErrorConstructor(args[0])) {\n assertTrue(\n args[0](error as ErrorType),\n `Error didn't match the error condition: ${error?.toString()}`,\n );\n return;\n }\n\n assertTrue(\n error instanceof args[0],\n `Caught error is not an instance of the expected type: ${error?.toString()}`,\n );\n\n if (args[1]) {\n assertTrue(\n args[1](error as ErrorType),\n `Error didn't match the error condition: ${error?.toString()}`,\n );\n }\n }\n },\n };\n },\n };\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"],"mappings":"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","names":["express","Router","http","isNumber","val","uuid","TransformStream","retry","ReadableStream","notifyAboutNoActiveReadersStream","onNoActiveReaderCallback","options","NotifyAboutNoActiveReadersStream","TransformStream","reason","uuid","interval","asyncRetry","fn","opts","retry","bail","error","ParseError","text","JSONParser","value","options","_","v","parsed","filter","TransformStream","chunk","controller","map","reduce","reducer","initialValue","ReduceTransformStream","retryStream","createSourceStream","handleChunk","retryOptions","asyncRetry","onRestream","error","reader","done","result","skip","limit","SkipTransformStream","stopAfter","stopCondition","stopOn","take","TakeTransformStream","waitAtMost","waitTimeInMs","timeoutId","originalTerminate","streamTransformations","notifyAboutNoActiveReadersStream","NotifyAboutNoActiveReadersStream","retry","AssertionError","message","isSubset","superObj","subObj","sup","sub","assertOk","ele","assertFails","assertMatches","actual","expected","message","isSubset","AssertionError","JSONParser","assertOk","obj","message","AssertionError","assertEqual","expected","actual","JSONParser","WrapEventStore","eventStore","appendedEvents","streamName","options","events","result","currentStream","ProblemDocument","problemDetailsMiddleware","mapError","error","request","response","_next","problemDetails","defaulErrorToProblemDetailsMapping","sendProblem","statusCode","isNumber","ProblemDocument","getApplication","options","app","express","apis","mapError","enableDefaultExpressEtag","disableJsonMiddleware","disableUrlEncodingMiddleware","disableProblemDetailsMiddleware","router","Router","api","problemDetailsMiddleware","startAPI","port","server","http","HeaderNames","WeakETagRegex","ETagErrors","isWeakETag","etag","getWeakETagValue","result","toWeakETag","value","getETagFromIfMatch","request","getETagFromIfNotMatch","setETag","response","getETagValueFromIfMatch","eTagValue","on","handle","request","response","_next","OK","options","send","Created","sendCreated","Accepted","sendAccepted","NoContent","HttpResponse","statusCode","BadRequest","HttpProblem","Forbidden","NotFound","Conflict","PreconditionFailed","sendProblem","ProblemDocument","DefaultHttpResponseOptions","DefaultHttpProblemResponseOptions","sendCreated","response","eTag","options","send","sendAccepted","statusCode","location","body","setETag","sendProblem","problemDetails","ProblemDocument","supertest","assert","ApiE2ESpecification","getEventStore","getApplication","givenRequests","eventStore","WrapEventStore","application","setupRequest","handle","requestFn","supertest","verify","response","assertion","supertest","existingStream","streamId","events","expect","expectNewEvents","expectResponse","statusCode","options","response","body","headers","assertEqual","assertMatches","expectError","errorCode","problemDetails","ApiSpecification","getEventStore","getApplication","givenStreams","eventStore","WrapEventStore","application","setupRequest","handle","streamName","verify","assertFails","first","rest"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@event-driven-io/emmett-expressjs",
|
|
3
|
-
"version": "0.20.1",
|
|
3
|
+
"version": "0.20.2-alpha.1",
|
|
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.1",
|
|
52
|
+
"@event-driven-io/emmett": "0.20.2-alpha.1",
|
|
53
53
|
"@types/express": "^4.17.21",
|
|
54
54
|
"@types/supertest": "^6.0.2",
|
|
55
55
|
"express": "^4.19.2",
|