@event-driven-io/emmett-expressjs 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/handler.d.mts +3 -0
- package/dist/handler.d.ts +3 -0
- package/dist/index.d.mts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -0
- package/dist/index.mjs.map +1 -1
- package/dist/middlewares/problemDetailsMiddleware.d.mts +3 -0
- package/dist/middlewares/problemDetailsMiddleware.d.ts +3 -0
- package/dist/testing/apiSpecification.d.mts +32 -0
- package/dist/testing/apiSpecification.d.ts +32 -0
- package/dist/testing/apiSpecification.js +106 -0
- package/dist/testing/apiSpecification.js.map +1 -0
- package/dist/testing/apiSpecification.mjs +106 -0
- package/dist/testing/apiSpecification.mjs.map +1 -0
- package/dist/testing/index.d.mts +6 -0
- package/dist/testing/index.d.ts +6 -0
- package/dist/testing/index.js +2 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/index.mjs +2 -0
- package/dist/testing/index.mjs.map +1 -0
- package/package.json +2 -2
- package/dist/testing.d.mts +0 -2
- package/dist/testing.d.ts +0 -2
- package/dist/testing.js +0 -1
- package/dist/testing.js.map +0 -1
- package/dist/testing.mjs +0 -1
- package/dist/testing.mjs.map +0 -1
package/dist/handler.d.mts
CHANGED
package/dist/handler.d.ts
CHANGED
package/dist/index.d.mts
CHANGED
|
@@ -5,7 +5,10 @@ import http from 'http';
|
|
|
5
5
|
import { ProblemDocument } from 'http-problem-details';
|
|
6
6
|
import { ETag } from './etag.mjs';
|
|
7
7
|
export { ETagErrors, HeaderNames, WeakETag, WeakETagRegex, getETagFromIfMatch, getETagFromIfNotMatch, getETagValueFromIfMatch, getWeakETagValue, isWeakETag, setETag, toWeakETag } from './etag.mjs';
|
|
8
|
+
export { ApiSpecification, ApiSpecificationAssert, ResponseAssert, TestEventStream, existingStream, expect, expectError, expectNewEvents, expectResponse } from './testing/apiSpecification.mjs';
|
|
8
9
|
import '@event-driven-io/emmett';
|
|
10
|
+
import 'supertest';
|
|
11
|
+
import 'supertest/lib/agent';
|
|
9
12
|
|
|
10
13
|
type ErrorToProblemDetailsMapping = (error: Error, request: Request) => ProblemDocument | undefined;
|
|
11
14
|
type WebApiSetup = (router: Router) => void;
|
package/dist/index.d.ts
CHANGED
|
@@ -5,7 +5,10 @@ import http from 'http';
|
|
|
5
5
|
import { ProblemDocument } from 'http-problem-details';
|
|
6
6
|
import { ETag } from './etag.js';
|
|
7
7
|
export { ETagErrors, HeaderNames, WeakETag, WeakETagRegex, getETagFromIfMatch, getETagFromIfNotMatch, getETagValueFromIfMatch, getWeakETagValue, isWeakETag, setETag, toWeakETag } from './etag.js';
|
|
8
|
+
export { ApiSpecification, ApiSpecificationAssert, ResponseAssert, TestEventStream, existingStream, expect, expectError, expectNewEvents, expectResponse } from './testing/apiSpecification.js';
|
|
8
9
|
import '@event-driven-io/emmett';
|
|
10
|
+
import 'supertest';
|
|
11
|
+
import 'supertest/lib/agent';
|
|
9
12
|
|
|
10
13
|
type ErrorToProblemDetailsMapping = (error: Error, request: Request) => ProblemDocument | undefined;
|
|
11
14
|
type WebApiSetup = (router: Router) => void;
|
package/dist/index.js
CHANGED
|
@@ -8,6 +8,7 @@ var _etag = require('./etag'); _createStarExport(_etag);
|
|
|
8
8
|
var _problemDetailsMiddleware = require('./middlewares/problemDetailsMiddleware');
|
|
9
9
|
|
|
10
10
|
var _handler = require('./handler'); _createStarExport(_handler);
|
|
11
|
+
var _testing = require('./testing'); _createStarExport(_testing);
|
|
11
12
|
const getApplication = (options) => {
|
|
12
13
|
const app = _express2.default.call(void 0, );
|
|
13
14
|
const {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO;AAAA,EACL;AAAA,OAIK;AACP,OAAO;AACP,OAAO,UAAU;AACjB,SAAS,uBAAuB;AAChC,SAAS,eAA0B;AACnC,SAAS,gCAAgC;AAEzC,cAAc;AACd,cAAc;AAoBP,MAAM,iBAAiB,CAAC,YAAgC;AAC7D,QAAM,MAAmB,QAAQ;AAEjC,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI;AAEJ,QAAM,SAAS,OAAO;AAItB,MAAI,IAAI,QAAQ,4BAA4B,KAAK;AAGjD,MAAI,CAAC;AAAuB,QAAI,IAAI,QAAQ,KAAK,CAAC;AAGlD,MAAI,CAAC;AACH,QAAI;AAAA,MACF,QAAQ,WAAW;AAAA,QACjB,UAAU;AAAA,MACZ,CAAC;AAAA,IACH;AAEF,aAAW,OAAO,MAAM;AACtB,QAAI,MAAM;AAAA,EACZ;AACA,MAAI,IAAI,MAAM;AAGd,MAAI,CAAC;AACH,QAAI,IAAI,yBAAyB,QAAQ,CAAC;AAE5C,SAAO;AACT;AAMO,MAAM,WAAW,CACtB,KACA,UAA2B,EAAE,MAAM,IAAK,MACrC;AACH,QAAM,EAAE,KAAK,IAAI;AACjB,QAAM,SAAS,KAAK,aAAa,GAAG;AAEpC,SAAO,GAAG,aAAa,MAAM;AAC3B,YAAQ,KAAK,qBAAqB;AAAA,EACpC,CAAC;AAED,SAAO,OAAO,OAAO,IAAI;AAC3B;AAOO,MAAM,6BAAkD,CAAC;AAYzD,MAAM,oCAAgE;AAAA,EAC3E,gBAAgB;AAClB;AAOO,MAAM,cAAc,CACzB,UACA,EAAE,WAAW,WAAW,KAAK,MAE7B,KAAK,UAAU,KAAK;AAAA,EAClB,UAAU,GAAG,aAAa,SAAS,IAAI,GAAG,IAAI,SAAS;AAAA,EACvD,MAAM,EAAE,IAAI,UAAU;AAAA;AAAA;AAAA,EAGtB;AACF,CAAC;AAMI,MAAM,eAAe,CAC1B,UACA,YACS,KAAK,UAAU,KAAK,OAAO;AAI/B,MAAM,OAAO,CAClB,UACA,YACA,YACS;AAGT,QAAM,EAAE,UAAU,MAAM,KAAK,IAAI,WAAW;AAE5C,MAAI;AAAM,YAAQ,UAAU,IAAI;AAChC,MAAI;AAAU,aAAS,UAAU,YAAY,QAAQ;AAErD,MAAI,MAAM;AACR,aAAS,aAAa;AACtB,aAAS,KAAK,IAAI;AAAA,EACpB,OAAO;AACL,aAAS,WAAW,UAAU;AAAA,EAChC;AACF;AAEO,MAAM,cAAc,CACzB,UACA,YACA,YACS;AACT,YAAU,WAAW;AAIrB,QAAM,EAAE,UAAU,KAAK,IAAI;AAE3B,QAAM,iBACJ,aAAa,UACT,QAAQ,UACR,IAAI,gBAAgB;AAAA,IAClB,QAAQ,QAAQ;AAAA,IAChB,QAAQ;AAAA,EACV,CAAC;AAGP,MAAI;AAAM,YAAQ,UAAU,IAAI;AAChC,MAAI;AAAU,aAAS,UAAU,YAAY,QAAQ;AAErD,WAAS,UAAU,gBAAgB,0BAA0B;AAE7D,WAAS,aAAa;AACtB,WAAS,KAAK,cAAc;AAC9B","sourcesContent":["import express, {\n Router,\n type Application,\n type Request,\n type Response,\n} from 'express';\nimport 'express-async-errors';\nimport http from 'http';\nimport { ProblemDocument } from 'http-problem-details';\nimport { setETag, type ETag } from './etag';\nimport { problemDetailsMiddleware } from './middlewares/problemDetailsMiddleware';\n\nexport * from './etag';\nexport * from './handler';\n\nexport type ErrorToProblemDetailsMapping = (\n error: Error,\n request: Request,\n) => ProblemDocument | undefined;\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\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 createdId: string;\n urlPrefix?: string;\n} & HttpResponseOptions;\n\nexport const sendCreated = (\n response: Response,\n { createdId, urlPrefix, eTag }: CreatedHttpResponseOptions,\n): void =>\n send(response, 201, {\n location: `${urlPrefix ?? response.req.url}/${createdId}`,\n body: { id: createdId },\n // TODO: https://github.com/event-driven-io/emmett/issues/18\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\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 // TODO: https://github.com/event-driven-io/emmett/issues/18\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\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 // TODO: https://github.com/event-driven-io/emmett/issues/18\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\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"],"names":[],"mappings":"AAAA,OAAO;AAAA,EACL;AAAA,OAIK;AACP,OAAO;AACP,OAAO,UAAU;AACjB,SAAS,uBAAuB;AAChC,SAAS,eAA0B;AACnC,SAAS,gCAAgC;AAEzC,cAAc;AACd,cAAc;AACd,cAAc;AAoBP,MAAM,iBAAiB,CAAC,YAAgC;AAC7D,QAAM,MAAmB,QAAQ;AAEjC,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI;AAEJ,QAAM,SAAS,OAAO;AAItB,MAAI,IAAI,QAAQ,4BAA4B,KAAK;AAGjD,MAAI,CAAC;AAAuB,QAAI,IAAI,QAAQ,KAAK,CAAC;AAGlD,MAAI,CAAC;AACH,QAAI;AAAA,MACF,QAAQ,WAAW;AAAA,QACjB,UAAU;AAAA,MACZ,CAAC;AAAA,IACH;AAEF,aAAW,OAAO,MAAM;AACtB,QAAI,MAAM;AAAA,EACZ;AACA,MAAI,IAAI,MAAM;AAGd,MAAI,CAAC;AACH,QAAI,IAAI,yBAAyB,QAAQ,CAAC;AAE5C,SAAO;AACT;AAMO,MAAM,WAAW,CACtB,KACA,UAA2B,EAAE,MAAM,IAAK,MACrC;AACH,QAAM,EAAE,KAAK,IAAI;AACjB,QAAM,SAAS,KAAK,aAAa,GAAG;AAEpC,SAAO,GAAG,aAAa,MAAM;AAC3B,YAAQ,KAAK,qBAAqB;AAAA,EACpC,CAAC;AAED,SAAO,OAAO,OAAO,IAAI;AAC3B;AAOO,MAAM,6BAAkD,CAAC;AAYzD,MAAM,oCAAgE;AAAA,EAC3E,gBAAgB;AAClB;AAOO,MAAM,cAAc,CACzB,UACA,EAAE,WAAW,WAAW,KAAK,MAE7B,KAAK,UAAU,KAAK;AAAA,EAClB,UAAU,GAAG,aAAa,SAAS,IAAI,GAAG,IAAI,SAAS;AAAA,EACvD,MAAM,EAAE,IAAI,UAAU;AAAA;AAAA;AAAA,EAGtB;AACF,CAAC;AAMI,MAAM,eAAe,CAC1B,UACA,YACS,KAAK,UAAU,KAAK,OAAO;AAI/B,MAAM,OAAO,CAClB,UACA,YACA,YACS;AAGT,QAAM,EAAE,UAAU,MAAM,KAAK,IAAI,WAAW;AAE5C,MAAI;AAAM,YAAQ,UAAU,IAAI;AAChC,MAAI;AAAU,aAAS,UAAU,YAAY,QAAQ;AAErD,MAAI,MAAM;AACR,aAAS,aAAa;AACtB,aAAS,KAAK,IAAI;AAAA,EACpB,OAAO;AACL,aAAS,WAAW,UAAU;AAAA,EAChC;AACF;AAEO,MAAM,cAAc,CACzB,UACA,YACA,YACS;AACT,YAAU,WAAW;AAIrB,QAAM,EAAE,UAAU,KAAK,IAAI;AAE3B,QAAM,iBACJ,aAAa,UACT,QAAQ,UACR,IAAI,gBAAgB;AAAA,IAClB,QAAQ,QAAQ;AAAA,IAChB,QAAQ;AAAA,EACV,CAAC;AAGP,MAAI;AAAM,YAAQ,UAAU,IAAI;AAChC,MAAI;AAAU,aAAS,UAAU,YAAY,QAAQ;AAErD,WAAS,UAAU,gBAAgB,0BAA0B;AAE7D,WAAS,aAAa;AACtB,WAAS,KAAK,cAAc;AAC9B","sourcesContent":["import express, {\n Router,\n type Application,\n type Request,\n type Response,\n} from 'express';\nimport 'express-async-errors';\nimport http from 'http';\nimport { ProblemDocument } from 'http-problem-details';\nimport { setETag, type ETag } from './etag';\nimport { problemDetailsMiddleware } from './middlewares/problemDetailsMiddleware';\n\nexport * from './etag';\nexport * from './handler';\nexport * from './testing';\n\nexport type ErrorToProblemDetailsMapping = (\n error: Error,\n request: Request,\n) => ProblemDocument | undefined;\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\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 createdId: string;\n urlPrefix?: string;\n} & HttpResponseOptions;\n\nexport const sendCreated = (\n response: Response,\n { createdId, urlPrefix, eTag }: CreatedHttpResponseOptions,\n): void =>\n send(response, 201, {\n location: `${urlPrefix ?? response.req.url}/${createdId}`,\n body: { id: createdId },\n // TODO: https://github.com/event-driven-io/emmett/issues/18\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\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 // TODO: https://github.com/event-driven-io/emmett/issues/18\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\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 // TODO: https://github.com/event-driven-io/emmett/issues/18\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n const { location, eTag } = options;\n\n const problemDetails =\n 'problem' in options\n ? options.problem\n : new ProblemDocument({\n detail: options.problemDetails,\n status: statusCode,\n });\n\n // HEADERS\n if (eTag) setETag(response, eTag);\n if (location) response.setHeader('Location', location);\n\n response.setHeader('Content-Type', 'application/problem+json');\n\n response.statusCode = statusCode;\n response.json(problemDetails);\n};\n"]}
|
package/dist/index.mjs
CHANGED
|
@@ -8,6 +8,7 @@ import { setETag } from "./etag";
|
|
|
8
8
|
import { problemDetailsMiddleware } from "./middlewares/problemDetailsMiddleware";
|
|
9
9
|
export * from "./etag";
|
|
10
10
|
export * from "./handler";
|
|
11
|
+
export * from "./testing";
|
|
11
12
|
const getApplication = (options) => {
|
|
12
13
|
const app = express();
|
|
13
14
|
const {
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import express, {\n Router,\n type Application,\n type Request,\n type Response,\n} from 'express';\nimport 'express-async-errors';\nimport http from 'http';\nimport { ProblemDocument } from 'http-problem-details';\nimport { setETag, type ETag } from './etag';\nimport { problemDetailsMiddleware } from './middlewares/problemDetailsMiddleware';\n\nexport * from './etag';\nexport * from './handler';\n\nexport type ErrorToProblemDetailsMapping = (\n error: Error,\n request: Request,\n) => ProblemDocument | undefined;\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\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 createdId: string;\n urlPrefix?: string;\n} & HttpResponseOptions;\n\nexport const sendCreated = (\n response: Response,\n { createdId, urlPrefix, eTag }: CreatedHttpResponseOptions,\n): void =>\n send(response, 201, {\n location: `${urlPrefix ?? response.req.url}/${createdId}`,\n body: { id: createdId },\n // TODO: https://github.com/event-driven-io/emmett/issues/18\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\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 // TODO: https://github.com/event-driven-io/emmett/issues/18\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\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 // TODO: https://github.com/event-driven-io/emmett/issues/18\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\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"],"mappings":"AAAA,OAAO;AAAA,EACL;AAAA,OAIK;AACP,OAAO;AACP,OAAO,UAAU;AACjB,SAAS,uBAAuB;AAChC,SAAS,eAA0B;AACnC,SAAS,gCAAgC;AAEzC,cAAc;AACd,cAAc;AAoBP,MAAM,iBAAiB,CAAC,YAAgC;AAC7D,QAAM,MAAmB,QAAQ;AAEjC,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI;AAEJ,QAAM,SAAS,OAAO;AAItB,MAAI,IAAI,QAAQ,4BAA4B,KAAK;AAGjD,MAAI,CAAC;AAAuB,QAAI,IAAI,QAAQ,KAAK,CAAC;AAGlD,MAAI,CAAC;AACH,QAAI;AAAA,MACF,QAAQ,WAAW;AAAA,QACjB,UAAU;AAAA,MACZ,CAAC;AAAA,IACH;AAEF,aAAW,OAAO,MAAM;AACtB,QAAI,MAAM;AAAA,EACZ;AACA,MAAI,IAAI,MAAM;AAGd,MAAI,CAAC;AACH,QAAI,IAAI,yBAAyB,QAAQ,CAAC;AAE5C,SAAO;AACT;AAMO,MAAM,WAAW,CACtB,KACA,UAA2B,EAAE,MAAM,IAAK,MACrC;AACH,QAAM,EAAE,KAAK,IAAI;AACjB,QAAM,SAAS,KAAK,aAAa,GAAG;AAEpC,SAAO,GAAG,aAAa,MAAM;AAC3B,YAAQ,KAAK,qBAAqB;AAAA,EACpC,CAAC;AAED,SAAO,OAAO,OAAO,IAAI;AAC3B;AAOO,MAAM,6BAAkD,CAAC;AAYzD,MAAM,oCAAgE;AAAA,EAC3E,gBAAgB;AAClB;AAOO,MAAM,cAAc,CACzB,UACA,EAAE,WAAW,WAAW,KAAK,MAE7B,KAAK,UAAU,KAAK;AAAA,EAClB,UAAU,GAAG,aAAa,SAAS,IAAI,GAAG,IAAI,SAAS;AAAA,EACvD,MAAM,EAAE,IAAI,UAAU;AAAA;AAAA;AAAA,EAGtB;AACF,CAAC;AAMI,MAAM,eAAe,CAC1B,UACA,YACS,KAAK,UAAU,KAAK,OAAO;AAI/B,MAAM,OAAO,CAClB,UACA,YACA,YACS;AAGT,QAAM,EAAE,UAAU,MAAM,KAAK,IAAI,WAAW;AAE5C,MAAI;AAAM,YAAQ,UAAU,IAAI;AAChC,MAAI;AAAU,aAAS,UAAU,YAAY,QAAQ;AAErD,MAAI,MAAM;AACR,aAAS,aAAa;AACtB,aAAS,KAAK,IAAI;AAAA,EACpB,OAAO;AACL,aAAS,WAAW,UAAU;AAAA,EAChC;AACF;AAEO,MAAM,cAAc,CACzB,UACA,YACA,YACS;AACT,YAAU,WAAW;AAIrB,QAAM,EAAE,UAAU,KAAK,IAAI;AAE3B,QAAM,iBACJ,aAAa,UACT,QAAQ,UACR,IAAI,gBAAgB;AAAA,IAClB,QAAQ,QAAQ;AAAA,IAChB,QAAQ;AAAA,EACV,CAAC;AAGP,MAAI;AAAM,YAAQ,UAAU,IAAI;AAChC,MAAI;AAAU,aAAS,UAAU,YAAY,QAAQ;AAErD,WAAS,UAAU,gBAAgB,0BAA0B;AAE7D,WAAS,aAAa;AACtB,WAAS,KAAK,cAAc;AAC9B;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import express, {\n Router,\n type Application,\n type Request,\n type Response,\n} from 'express';\nimport 'express-async-errors';\nimport http from 'http';\nimport { ProblemDocument } from 'http-problem-details';\nimport { setETag, type ETag } from './etag';\nimport { problemDetailsMiddleware } from './middlewares/problemDetailsMiddleware';\n\nexport * from './etag';\nexport * from './handler';\nexport * from './testing';\n\nexport type ErrorToProblemDetailsMapping = (\n error: Error,\n request: Request,\n) => ProblemDocument | undefined;\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\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 createdId: string;\n urlPrefix?: string;\n} & HttpResponseOptions;\n\nexport const sendCreated = (\n response: Response,\n { createdId, urlPrefix, eTag }: CreatedHttpResponseOptions,\n): void =>\n send(response, 201, {\n location: `${urlPrefix ?? response.req.url}/${createdId}`,\n body: { id: createdId },\n // TODO: https://github.com/event-driven-io/emmett/issues/18\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\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 // TODO: https://github.com/event-driven-io/emmett/issues/18\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\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 // TODO: https://github.com/event-driven-io/emmett/issues/18\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\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"],"mappings":"AAAA,OAAO;AAAA,EACL;AAAA,OAIK;AACP,OAAO;AACP,OAAO,UAAU;AACjB,SAAS,uBAAuB;AAChC,SAAS,eAA0B;AACnC,SAAS,gCAAgC;AAEzC,cAAc;AACd,cAAc;AACd,cAAc;AAoBP,MAAM,iBAAiB,CAAC,YAAgC;AAC7D,QAAM,MAAmB,QAAQ;AAEjC,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI;AAEJ,QAAM,SAAS,OAAO;AAItB,MAAI,IAAI,QAAQ,4BAA4B,KAAK;AAGjD,MAAI,CAAC;AAAuB,QAAI,IAAI,QAAQ,KAAK,CAAC;AAGlD,MAAI,CAAC;AACH,QAAI;AAAA,MACF,QAAQ,WAAW;AAAA,QACjB,UAAU;AAAA,MACZ,CAAC;AAAA,IACH;AAEF,aAAW,OAAO,MAAM;AACtB,QAAI,MAAM;AAAA,EACZ;AACA,MAAI,IAAI,MAAM;AAGd,MAAI,CAAC;AACH,QAAI,IAAI,yBAAyB,QAAQ,CAAC;AAE5C,SAAO;AACT;AAMO,MAAM,WAAW,CACtB,KACA,UAA2B,EAAE,MAAM,IAAK,MACrC;AACH,QAAM,EAAE,KAAK,IAAI;AACjB,QAAM,SAAS,KAAK,aAAa,GAAG;AAEpC,SAAO,GAAG,aAAa,MAAM;AAC3B,YAAQ,KAAK,qBAAqB;AAAA,EACpC,CAAC;AAED,SAAO,OAAO,OAAO,IAAI;AAC3B;AAOO,MAAM,6BAAkD,CAAC;AAYzD,MAAM,oCAAgE;AAAA,EAC3E,gBAAgB;AAClB;AAOO,MAAM,cAAc,CACzB,UACA,EAAE,WAAW,WAAW,KAAK,MAE7B,KAAK,UAAU,KAAK;AAAA,EAClB,UAAU,GAAG,aAAa,SAAS,IAAI,GAAG,IAAI,SAAS;AAAA,EACvD,MAAM,EAAE,IAAI,UAAU;AAAA;AAAA;AAAA,EAGtB;AACF,CAAC;AAMI,MAAM,eAAe,CAC1B,UACA,YACS,KAAK,UAAU,KAAK,OAAO;AAI/B,MAAM,OAAO,CAClB,UACA,YACA,YACS;AAGT,QAAM,EAAE,UAAU,MAAM,KAAK,IAAI,WAAW;AAE5C,MAAI;AAAM,YAAQ,UAAU,IAAI;AAChC,MAAI;AAAU,aAAS,UAAU,YAAY,QAAQ;AAErD,MAAI,MAAM;AACR,aAAS,aAAa;AACtB,aAAS,KAAK,IAAI;AAAA,EACpB,OAAO;AACL,aAAS,WAAW,UAAU;AAAA,EAChC;AACF;AAEO,MAAM,cAAc,CACzB,UACA,YACA,YACS;AACT,YAAU,WAAW;AAIrB,QAAM,EAAE,UAAU,KAAK,IAAI;AAE3B,QAAM,iBACJ,aAAa,UACT,QAAQ,UACR,IAAI,gBAAgB;AAAA,IAClB,QAAQ,QAAQ;AAAA,IAChB,QAAQ;AAAA,EACV,CAAC;AAGP,MAAI;AAAM,YAAQ,UAAU,IAAI;AAChC,MAAI;AAAU,aAAS,UAAU,YAAY,QAAQ;AAErD,WAAS,UAAU,gBAAgB,0BAA0B;AAE7D,WAAS,aAAa;AACtB,WAAS,KAAK,cAAc;AAC9B;","names":[]}
|
|
@@ -6,6 +6,9 @@ import 'express-serve-static-core';
|
|
|
6
6
|
import 'http';
|
|
7
7
|
import '../etag.mjs';
|
|
8
8
|
import '@event-driven-io/emmett';
|
|
9
|
+
import '../testing/apiSpecification.mjs';
|
|
10
|
+
import 'supertest';
|
|
11
|
+
import 'supertest/lib/agent';
|
|
9
12
|
|
|
10
13
|
declare const problemDetailsMiddleware: (mapError?: ErrorToProblemDetailsMapping) => (error: Error, request: Request, response: Response, _next: NextFunction) => void;
|
|
11
14
|
declare const defaulErrorToProblemDetailsMapping: (error: Error) => ProblemDocument;
|
|
@@ -6,6 +6,9 @@ import 'express-serve-static-core';
|
|
|
6
6
|
import 'http';
|
|
7
7
|
import '../etag.js';
|
|
8
8
|
import '@event-driven-io/emmett';
|
|
9
|
+
import '../testing/apiSpecification.js';
|
|
10
|
+
import 'supertest';
|
|
11
|
+
import 'supertest/lib/agent';
|
|
9
12
|
|
|
10
13
|
declare const problemDetailsMiddleware: (mapError?: ErrorToProblemDetailsMapping) => (error: Error, request: Request, response: Response, _next: NextFunction) => void;
|
|
11
14
|
declare const defaulErrorToProblemDetailsMapping: (error: Error) => ProblemDocument;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Event, EventStore } from '@event-driven-io/emmett';
|
|
2
|
+
import { Application } from 'express';
|
|
3
|
+
import { ProblemDocument } from 'http-problem-details';
|
|
4
|
+
import supertest, { Response, Test } from 'supertest';
|
|
5
|
+
import TestAgent from 'supertest/lib/agent';
|
|
6
|
+
|
|
7
|
+
type TestEventStream<EventType extends Event = Event> = [
|
|
8
|
+
string,
|
|
9
|
+
EventType[]
|
|
10
|
+
];
|
|
11
|
+
declare const existingStream: <EventType extends Event = Event>(streamId: string, events: EventType[]) => TestEventStream<EventType>;
|
|
12
|
+
type ResponseAssert = (response: Response) => boolean | void;
|
|
13
|
+
type ApiSpecificationAssert<EventType extends Event = Event> = TestEventStream<EventType>[] | ResponseAssert | [ResponseAssert, ...TestEventStream<EventType>[]];
|
|
14
|
+
declare const expect: <EventType extends Event = Event>(streamId: string, events: EventType[]) => TestEventStream<EventType>;
|
|
15
|
+
declare const expectNewEvents: <EventType extends Event = Event>(streamId: string, events: EventType[]) => TestEventStream<EventType>;
|
|
16
|
+
declare const expectResponse: (statusCode: number, options?: {
|
|
17
|
+
body?: unknown;
|
|
18
|
+
headers?: {
|
|
19
|
+
[index: string]: string;
|
|
20
|
+
} | undefined;
|
|
21
|
+
} | undefined) => (response: Response) => void;
|
|
22
|
+
declare const expectError: (errorCode: number, problemDetails?: ProblemDocument) => (response: Response) => void;
|
|
23
|
+
type ApiSpecification<EventType extends Event = Event> = (...givenStreams: TestEventStream<EventType>[]) => {
|
|
24
|
+
when: (setupRequest: (request: TestAgent<supertest.Test>) => Test) => {
|
|
25
|
+
then: (verify: ApiSpecificationAssert<EventType>) => Promise<void>;
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
declare const ApiSpecification: {
|
|
29
|
+
for: <EventType extends Event = Event, StreamVersion = bigint>(getEventStore: () => EventStore<StreamVersion>, getApplication: (eventStore: EventStore<StreamVersion>) => Application) => ApiSpecification<EventType>;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export { ApiSpecification, type ApiSpecificationAssert, type ResponseAssert, type TestEventStream, existingStream, expect, expectError, expectNewEvents, expectResponse };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Event, EventStore } from '@event-driven-io/emmett';
|
|
2
|
+
import { Application } from 'express';
|
|
3
|
+
import { ProblemDocument } from 'http-problem-details';
|
|
4
|
+
import supertest, { Response, Test } from 'supertest';
|
|
5
|
+
import TestAgent from 'supertest/lib/agent';
|
|
6
|
+
|
|
7
|
+
type TestEventStream<EventType extends Event = Event> = [
|
|
8
|
+
string,
|
|
9
|
+
EventType[]
|
|
10
|
+
];
|
|
11
|
+
declare const existingStream: <EventType extends Event = Event>(streamId: string, events: EventType[]) => TestEventStream<EventType>;
|
|
12
|
+
type ResponseAssert = (response: Response) => boolean | void;
|
|
13
|
+
type ApiSpecificationAssert<EventType extends Event = Event> = TestEventStream<EventType>[] | ResponseAssert | [ResponseAssert, ...TestEventStream<EventType>[]];
|
|
14
|
+
declare const expect: <EventType extends Event = Event>(streamId: string, events: EventType[]) => TestEventStream<EventType>;
|
|
15
|
+
declare const expectNewEvents: <EventType extends Event = Event>(streamId: string, events: EventType[]) => TestEventStream<EventType>;
|
|
16
|
+
declare const expectResponse: (statusCode: number, options?: {
|
|
17
|
+
body?: unknown;
|
|
18
|
+
headers?: {
|
|
19
|
+
[index: string]: string;
|
|
20
|
+
} | undefined;
|
|
21
|
+
} | undefined) => (response: Response) => void;
|
|
22
|
+
declare const expectError: (errorCode: number, problemDetails?: ProblemDocument) => (response: Response) => void;
|
|
23
|
+
type ApiSpecification<EventType extends Event = Event> = (...givenStreams: TestEventStream<EventType>[]) => {
|
|
24
|
+
when: (setupRequest: (request: TestAgent<supertest.Test>) => Test) => {
|
|
25
|
+
then: (verify: ApiSpecificationAssert<EventType>) => Promise<void>;
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
declare const ApiSpecification: {
|
|
29
|
+
for: <EventType extends Event = Event, StreamVersion = bigint>(getEventStore: () => EventStore<StreamVersion>, getApplication: (eventStore: EventStore<StreamVersion>) => Application) => ApiSpecification<EventType>;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export { ApiSpecification, type ApiSpecificationAssert, type ResponseAssert, type TestEventStream, existingStream, expect, expectError, expectNewEvents, expectResponse };
|
|
@@ -0,0 +1,106 @@
|
|
|
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(); } }
|
|
2
|
+
|
|
3
|
+
var _emmett = require('@event-driven-io/emmett');
|
|
4
|
+
require('express');
|
|
5
|
+
var _strict = require('node:assert/strict'); var _strict2 = _interopRequireDefault(_strict);
|
|
6
|
+
var _supertest = require('supertest'); var _supertest2 = _interopRequireDefault(_supertest);
|
|
7
|
+
const existingStream = (streamId, events) => {
|
|
8
|
+
return [streamId, events];
|
|
9
|
+
};
|
|
10
|
+
const expect = (streamId, events) => {
|
|
11
|
+
return [streamId, events];
|
|
12
|
+
};
|
|
13
|
+
const expectNewEvents = (streamId, events) => {
|
|
14
|
+
return [streamId, events];
|
|
15
|
+
};
|
|
16
|
+
const expectResponse = (statusCode, options) => (response) => {
|
|
17
|
+
const { body, headers } = _nullishCoalesce(options, () => ( {}));
|
|
18
|
+
_strict2.default.equal(response.statusCode, statusCode);
|
|
19
|
+
if (body)
|
|
20
|
+
_emmett.assertMatches.call(void 0, response.body, body);
|
|
21
|
+
if (headers)
|
|
22
|
+
_emmett.assertMatches.call(void 0, response.headers, headers);
|
|
23
|
+
};
|
|
24
|
+
const expectError = (errorCode, problemDetails) => expectResponse(
|
|
25
|
+
errorCode,
|
|
26
|
+
problemDetails ? { body: problemDetails } : void 0
|
|
27
|
+
);
|
|
28
|
+
const ApiSpecification = {
|
|
29
|
+
for: (getEventStore, getApplication) => {
|
|
30
|
+
{
|
|
31
|
+
return (...givenStreams) => {
|
|
32
|
+
const eventStore = WrapEventStore(getEventStore());
|
|
33
|
+
const application = getApplication(eventStore);
|
|
34
|
+
return {
|
|
35
|
+
when: (setupRequest) => {
|
|
36
|
+
const handle = async () => {
|
|
37
|
+
for (const [streamName, events] of givenStreams) {
|
|
38
|
+
await eventStore.setup(streamName, events);
|
|
39
|
+
}
|
|
40
|
+
return setupRequest(_supertest2.default.call(void 0, application));
|
|
41
|
+
};
|
|
42
|
+
return {
|
|
43
|
+
then: async (verify) => {
|
|
44
|
+
const response = await handle();
|
|
45
|
+
if (typeof verify === "function") {
|
|
46
|
+
const succeded = verify(response);
|
|
47
|
+
if (succeded === false)
|
|
48
|
+
_strict2.default.fail();
|
|
49
|
+
} else if (Array.isArray(verify)) {
|
|
50
|
+
const [first, ...rest] = verify;
|
|
51
|
+
if (typeof first === "function") {
|
|
52
|
+
const succeded = first(response);
|
|
53
|
+
if (succeded === false)
|
|
54
|
+
_strict2.default.fail();
|
|
55
|
+
}
|
|
56
|
+
const events = typeof first === "function" ? rest : verify;
|
|
57
|
+
_emmett.assertMatches.call(void 0,
|
|
58
|
+
Array.from(eventStore.appendedEvents.values()),
|
|
59
|
+
events
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
const WrapEventStore = (eventStore) => {
|
|
71
|
+
const appendedEvents = /* @__PURE__ */ new Map();
|
|
72
|
+
return {
|
|
73
|
+
async aggregateStream(streamName, options) {
|
|
74
|
+
return eventStore.aggregateStream(streamName, options);
|
|
75
|
+
},
|
|
76
|
+
readStream(streamName, options) {
|
|
77
|
+
return eventStore.readStream(streamName, options);
|
|
78
|
+
},
|
|
79
|
+
appendToStream: async (streamName, events, options) => {
|
|
80
|
+
const result = await eventStore.appendToStream(
|
|
81
|
+
streamName,
|
|
82
|
+
events,
|
|
83
|
+
options
|
|
84
|
+
);
|
|
85
|
+
const currentStream = _nullishCoalesce(appendedEvents.get(streamName), () => ( [streamName, []]));
|
|
86
|
+
appendedEvents.set(streamName, [
|
|
87
|
+
streamName,
|
|
88
|
+
[...currentStream[1], ...events]
|
|
89
|
+
]);
|
|
90
|
+
return result;
|
|
91
|
+
},
|
|
92
|
+
appendedEvents,
|
|
93
|
+
setup: async (streamName, events) => {
|
|
94
|
+
return eventStore.appendToStream(streamName, events);
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
exports.ApiSpecification = ApiSpecification; exports.existingStream = existingStream; exports.expect = expect; exports.expectError = expectError; exports.expectNewEvents = expectNewEvents; exports.expectResponse = expectResponse;
|
|
106
|
+
//# sourceMappingURL=apiSpecification.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/testing/apiSpecification.ts"],"names":[],"mappings":"AAAA;AAAA,EACE;AAAA,OAUK;AACP,eAAiC;AAEjC,OAAO,YAAY;AAEnB,OAAO,eAAe;AAYf,MAAM,iBAAiB,CAC5B,UACA,WAC+B;AAC/B,SAAO,CAAC,UAAU,MAAM;AAC1B;AAaO,MAAM,SAAS,CACpB,UACA,WAC+B;AAC/B,SAAO,CAAC,UAAU,MAAM;AAC1B;AAEO,MAAM,kBAAkB,CAC7B,UACA,WAC+B;AAC/B,SAAO,CAAC,UAAU,MAAM;AAC1B;AAEO,MAAM,iBACX,CACE,YACA,YAEF,CAAC,aAA6B;AAC5B,QAAM,EAAE,MAAM,QAAQ,IAAI,WAAW,CAAC;AACtC,SAAO,MAAM,SAAS,YAAY,UAAU;AAC5C,MAAI;AAAM,kBAAc,SAAS,MAAM,IAAI;AAC3C,MAAI;AAAS,kBAAc,SAAS,SAAS,OAAO;AACtD;AAEK,MAAM,cAAc,CACzB,WACA,mBAEA;AAAA,EACE;AAAA,EACA,iBAAiB,EAAE,MAAM,eAAe,IAAI;AAC9C;AAcK,MAAM,mBAAmB;AAAA,EAC9B,KAAK,CAIH,eACA,mBACgC;AAChC;AACE,aAAO,IAAI,iBAA+C;AACxD,cAAM,aAAa,eAAe,cAAc,CAAC;AACjD,cAAM,cAAc,eAAe,UAAU;AAE7C,eAAO;AAAA,UACL,MAAM,CACJ,iBACG;AACH,kBAAM,SAAS,YAAY;AACzB,yBAAW,CAAC,YAAY,MAAM,KAAK,cAAc;AAC/C,sBAAM,WAAW,MAAM,YAAY,MAAM;AAAA,cAC3C;AAEA,qBAAO,aAAa,UAAU,WAAW,CAAC;AAAA,YAC5C;AAEA,mBAAO;AAAA,cACL,MAAM,OACJ,WACkB;AAClB,sBAAM,WAAW,MAAM,OAAO;AAE9B,oBAAI,OAAO,WAAW,YAAY;AAChC,wBAAM,WAAW,OAAO,QAAQ;AAEhC,sBAAI,aAAa;AAAO,2BAAO,KAAK;AAAA,gBACtC,WAAW,MAAM,QAAQ,MAAM,GAAG;AAChC,wBAAM,CAAC,OAAO,GAAG,IAAI,IAAI;AAEzB,sBAAI,OAAO,UAAU,YAAY;AAC/B,0BAAM,WAAW,MAAM,QAAQ;AAE/B,wBAAI,aAAa;AAAO,6BAAO,KAAK;AAAA,kBACtC;AAEA,wBAAM,SAAS,OAAO,UAAU,aAAa,OAAO;AAEpD;AAAA,oBACE,MAAM,KAAK,WAAW,eAAe,OAAO,CAAC;AAAA,oBAC7C;AAAA,kBACF;AAAA,gBACF;AAAA,cACF;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAEA,MAAM,iBAAiB,CACrB,eAOG;AACH,QAAM,iBAAiB,oBAAI,IAA6B;AAExD,SAAO;AAAA,IACL,MAAM,gBACJ,YACA,SAC6D;AAC7D,aAAO,WAAW,gBAAgB,YAAY,OAAO;AAAA,IACvD;AAAA,IAEA,WACE,YACA,SACqD;AACrD,aAAO,WAAW,WAAW,YAAY,OAAO;AAAA,IAClD;AAAA,IAEA,gBAAgB,OACd,YACA,QACA,YACiD;AACjD,YAAM,SAAS,MAAM,WAAW;AAAA,QAC9B;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAEA,YAAM,gBAAgB,eAAe,IAAI,UAAU,KAAK,CAAC,YAAY,CAAC,CAAC;AAEvE,qBAAe,IAAI,YAAY;AAAA,QAC7B;AAAA,QACA,CAAC,GAAG,cAAc,CAAC,GAAG,GAAG,MAAM;AAAA,MACjC,CAAC;AAED,aAAO;AAAA,IACT;AAAA,IAEA;AAAA,IAEA,OAAO,OACL,YACA,WACiD;AACjD,aAAO,WAAW,eAAe,YAAY,MAAM;AAAA,IACrD;AAAA,EACF;AACF","sourcesContent":["import {\n assertMatches,\n type AggregateStreamOptions,\n type AggregateStreamResult,\n type AppendToStreamOptions,\n type AppendToStreamResult,\n type DefaultStreamVersionType,\n type Event,\n type EventStore,\n type ReadStreamOptions,\n type ReadStreamResult,\n} from '@event-driven-io/emmett';\nimport { type Application } from 'express';\nimport type { ProblemDocument } from 'http-problem-details';\nimport assert from 'node:assert/strict';\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 TestEventStream<EventType extends Event = Event> = [\n string,\n EventType[],\n];\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 (\n statusCode: number,\n options?: { body?: unknown; headers?: { [index: string]: string } },\n ) =>\n (response: Response): void => {\n const { body, headers } = options ?? {};\n assert.equal(response.statusCode, statusCode);\n if (body) assertMatches(response.body, body);\n if (headers) assertMatches(response.headers, headers);\n };\n\nexport const expectError = (\n errorCode: number,\n problemDetails?: 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: (request: TestAgent<supertest.Test>) => Test) => {\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: (\n setupRequest: (request: TestAgent<supertest.Test>) => Test,\n ) => {\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 succeded = verify(response);\n\n if (succeded === false) assert.fail();\n } else if (Array.isArray(verify)) {\n const [first, ...rest] = verify;\n\n if (typeof first === 'function') {\n const succeded = first(response);\n\n if (succeded === false) assert.fail();\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\nconst WrapEventStore = <StreamVersion = DefaultStreamVersionType>(\n eventStore: EventStore<StreamVersion>,\n): EventStore<StreamVersion> & {\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> | null> {\n return eventStore.aggregateStream(streamName, options);\n },\n\n readStream<EventType extends Event>(\n streamName: string,\n options?: ReadStreamOptions<StreamVersion>,\n ): Promise<ReadStreamResult<EventType, StreamVersion>> {\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};\n"]}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import {
|
|
2
|
+
assertMatches
|
|
3
|
+
} from "@event-driven-io/emmett";
|
|
4
|
+
import {} from "express";
|
|
5
|
+
import assert from "node:assert/strict";
|
|
6
|
+
import supertest from "supertest";
|
|
7
|
+
const existingStream = (streamId, events) => {
|
|
8
|
+
return [streamId, events];
|
|
9
|
+
};
|
|
10
|
+
const expect = (streamId, events) => {
|
|
11
|
+
return [streamId, events];
|
|
12
|
+
};
|
|
13
|
+
const expectNewEvents = (streamId, events) => {
|
|
14
|
+
return [streamId, events];
|
|
15
|
+
};
|
|
16
|
+
const expectResponse = (statusCode, options) => (response) => {
|
|
17
|
+
const { body, headers } = options ?? {};
|
|
18
|
+
assert.equal(response.statusCode, statusCode);
|
|
19
|
+
if (body)
|
|
20
|
+
assertMatches(response.body, body);
|
|
21
|
+
if (headers)
|
|
22
|
+
assertMatches(response.headers, headers);
|
|
23
|
+
};
|
|
24
|
+
const expectError = (errorCode, problemDetails) => expectResponse(
|
|
25
|
+
errorCode,
|
|
26
|
+
problemDetails ? { body: problemDetails } : void 0
|
|
27
|
+
);
|
|
28
|
+
const ApiSpecification = {
|
|
29
|
+
for: (getEventStore, getApplication) => {
|
|
30
|
+
{
|
|
31
|
+
return (...givenStreams) => {
|
|
32
|
+
const eventStore = WrapEventStore(getEventStore());
|
|
33
|
+
const application = getApplication(eventStore);
|
|
34
|
+
return {
|
|
35
|
+
when: (setupRequest) => {
|
|
36
|
+
const handle = async () => {
|
|
37
|
+
for (const [streamName, events] of givenStreams) {
|
|
38
|
+
await eventStore.setup(streamName, events);
|
|
39
|
+
}
|
|
40
|
+
return setupRequest(supertest(application));
|
|
41
|
+
};
|
|
42
|
+
return {
|
|
43
|
+
then: async (verify) => {
|
|
44
|
+
const response = await handle();
|
|
45
|
+
if (typeof verify === "function") {
|
|
46
|
+
const succeded = verify(response);
|
|
47
|
+
if (succeded === false)
|
|
48
|
+
assert.fail();
|
|
49
|
+
} else if (Array.isArray(verify)) {
|
|
50
|
+
const [first, ...rest] = verify;
|
|
51
|
+
if (typeof first === "function") {
|
|
52
|
+
const succeded = first(response);
|
|
53
|
+
if (succeded === false)
|
|
54
|
+
assert.fail();
|
|
55
|
+
}
|
|
56
|
+
const events = typeof first === "function" ? rest : verify;
|
|
57
|
+
assertMatches(
|
|
58
|
+
Array.from(eventStore.appendedEvents.values()),
|
|
59
|
+
events
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
const WrapEventStore = (eventStore) => {
|
|
71
|
+
const appendedEvents = /* @__PURE__ */ new Map();
|
|
72
|
+
return {
|
|
73
|
+
async aggregateStream(streamName, options) {
|
|
74
|
+
return eventStore.aggregateStream(streamName, options);
|
|
75
|
+
},
|
|
76
|
+
readStream(streamName, options) {
|
|
77
|
+
return eventStore.readStream(streamName, options);
|
|
78
|
+
},
|
|
79
|
+
appendToStream: async (streamName, events, options) => {
|
|
80
|
+
const result = await eventStore.appendToStream(
|
|
81
|
+
streamName,
|
|
82
|
+
events,
|
|
83
|
+
options
|
|
84
|
+
);
|
|
85
|
+
const currentStream = appendedEvents.get(streamName) ?? [streamName, []];
|
|
86
|
+
appendedEvents.set(streamName, [
|
|
87
|
+
streamName,
|
|
88
|
+
[...currentStream[1], ...events]
|
|
89
|
+
]);
|
|
90
|
+
return result;
|
|
91
|
+
},
|
|
92
|
+
appendedEvents,
|
|
93
|
+
setup: async (streamName, events) => {
|
|
94
|
+
return eventStore.appendToStream(streamName, events);
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
};
|
|
98
|
+
export {
|
|
99
|
+
ApiSpecification,
|
|
100
|
+
existingStream,
|
|
101
|
+
expect,
|
|
102
|
+
expectError,
|
|
103
|
+
expectNewEvents,
|
|
104
|
+
expectResponse
|
|
105
|
+
};
|
|
106
|
+
//# sourceMappingURL=apiSpecification.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/testing/apiSpecification.ts"],"sourcesContent":["import {\n assertMatches,\n type AggregateStreamOptions,\n type AggregateStreamResult,\n type AppendToStreamOptions,\n type AppendToStreamResult,\n type DefaultStreamVersionType,\n type Event,\n type EventStore,\n type ReadStreamOptions,\n type ReadStreamResult,\n} from '@event-driven-io/emmett';\nimport { type Application } from 'express';\nimport type { ProblemDocument } from 'http-problem-details';\nimport assert from 'node:assert/strict';\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 TestEventStream<EventType extends Event = Event> = [\n string,\n EventType[],\n];\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 (\n statusCode: number,\n options?: { body?: unknown; headers?: { [index: string]: string } },\n ) =>\n (response: Response): void => {\n const { body, headers } = options ?? {};\n assert.equal(response.statusCode, statusCode);\n if (body) assertMatches(response.body, body);\n if (headers) assertMatches(response.headers, headers);\n };\n\nexport const expectError = (\n errorCode: number,\n problemDetails?: 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: (request: TestAgent<supertest.Test>) => Test) => {\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: (\n setupRequest: (request: TestAgent<supertest.Test>) => Test,\n ) => {\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 succeded = verify(response);\n\n if (succeded === false) assert.fail();\n } else if (Array.isArray(verify)) {\n const [first, ...rest] = verify;\n\n if (typeof first === 'function') {\n const succeded = first(response);\n\n if (succeded === false) assert.fail();\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\nconst WrapEventStore = <StreamVersion = DefaultStreamVersionType>(\n eventStore: EventStore<StreamVersion>,\n): EventStore<StreamVersion> & {\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> | null> {\n return eventStore.aggregateStream(streamName, options);\n },\n\n readStream<EventType extends Event>(\n streamName: string,\n options?: ReadStreamOptions<StreamVersion>,\n ): Promise<ReadStreamResult<EventType, StreamVersion>> {\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};\n"],"mappings":"AAAA;AAAA,EACE;AAAA,OAUK;AACP,eAAiC;AAEjC,OAAO,YAAY;AAEnB,OAAO,eAAe;AAYf,MAAM,iBAAiB,CAC5B,UACA,WAC+B;AAC/B,SAAO,CAAC,UAAU,MAAM;AAC1B;AAaO,MAAM,SAAS,CACpB,UACA,WAC+B;AAC/B,SAAO,CAAC,UAAU,MAAM;AAC1B;AAEO,MAAM,kBAAkB,CAC7B,UACA,WAC+B;AAC/B,SAAO,CAAC,UAAU,MAAM;AAC1B;AAEO,MAAM,iBACX,CACE,YACA,YAEF,CAAC,aAA6B;AAC5B,QAAM,EAAE,MAAM,QAAQ,IAAI,WAAW,CAAC;AACtC,SAAO,MAAM,SAAS,YAAY,UAAU;AAC5C,MAAI;AAAM,kBAAc,SAAS,MAAM,IAAI;AAC3C,MAAI;AAAS,kBAAc,SAAS,SAAS,OAAO;AACtD;AAEK,MAAM,cAAc,CACzB,WACA,mBAEA;AAAA,EACE;AAAA,EACA,iBAAiB,EAAE,MAAM,eAAe,IAAI;AAC9C;AAcK,MAAM,mBAAmB;AAAA,EAC9B,KAAK,CAIH,eACA,mBACgC;AAChC;AACE,aAAO,IAAI,iBAA+C;AACxD,cAAM,aAAa,eAAe,cAAc,CAAC;AACjD,cAAM,cAAc,eAAe,UAAU;AAE7C,eAAO;AAAA,UACL,MAAM,CACJ,iBACG;AACH,kBAAM,SAAS,YAAY;AACzB,yBAAW,CAAC,YAAY,MAAM,KAAK,cAAc;AAC/C,sBAAM,WAAW,MAAM,YAAY,MAAM;AAAA,cAC3C;AAEA,qBAAO,aAAa,UAAU,WAAW,CAAC;AAAA,YAC5C;AAEA,mBAAO;AAAA,cACL,MAAM,OACJ,WACkB;AAClB,sBAAM,WAAW,MAAM,OAAO;AAE9B,oBAAI,OAAO,WAAW,YAAY;AAChC,wBAAM,WAAW,OAAO,QAAQ;AAEhC,sBAAI,aAAa;AAAO,2BAAO,KAAK;AAAA,gBACtC,WAAW,MAAM,QAAQ,MAAM,GAAG;AAChC,wBAAM,CAAC,OAAO,GAAG,IAAI,IAAI;AAEzB,sBAAI,OAAO,UAAU,YAAY;AAC/B,0BAAM,WAAW,MAAM,QAAQ;AAE/B,wBAAI,aAAa;AAAO,6BAAO,KAAK;AAAA,kBACtC;AAEA,wBAAM,SAAS,OAAO,UAAU,aAAa,OAAO;AAEpD;AAAA,oBACE,MAAM,KAAK,WAAW,eAAe,OAAO,CAAC;AAAA,oBAC7C;AAAA,kBACF;AAAA,gBACF;AAAA,cACF;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAEA,MAAM,iBAAiB,CACrB,eAOG;AACH,QAAM,iBAAiB,oBAAI,IAA6B;AAExD,SAAO;AAAA,IACL,MAAM,gBACJ,YACA,SAC6D;AAC7D,aAAO,WAAW,gBAAgB,YAAY,OAAO;AAAA,IACvD;AAAA,IAEA,WACE,YACA,SACqD;AACrD,aAAO,WAAW,WAAW,YAAY,OAAO;AAAA,IAClD;AAAA,IAEA,gBAAgB,OACd,YACA,QACA,YACiD;AACjD,YAAM,SAAS,MAAM,WAAW;AAAA,QAC9B;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAEA,YAAM,gBAAgB,eAAe,IAAI,UAAU,KAAK,CAAC,YAAY,CAAC,CAAC;AAEvE,qBAAe,IAAI,YAAY;AAAA,QAC7B;AAAA,QACA,CAAC,GAAG,cAAc,CAAC,GAAG,GAAG,MAAM;AAAA,MACjC,CAAC;AAED,aAAO;AAAA,IACT;AAAA,IAEA;AAAA,IAEA,OAAO,OACL,YACA,WACiD;AACjD,aAAO,WAAW,eAAe,YAAY,MAAM;AAAA,IACrD;AAAA,EACF;AACF;","names":[]}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { ApiSpecification, ApiSpecificationAssert, ResponseAssert, TestEventStream, existingStream, expect, expectError, expectNewEvents, expectResponse } from './apiSpecification.mjs';
|
|
2
|
+
import '@event-driven-io/emmett';
|
|
3
|
+
import 'express';
|
|
4
|
+
import 'http-problem-details';
|
|
5
|
+
import 'supertest';
|
|
6
|
+
import 'supertest/lib/agent';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { ApiSpecification, ApiSpecificationAssert, ResponseAssert, TestEventStream, existingStream, expect, expectError, expectNewEvents, expectResponse } from './apiSpecification.js';
|
|
2
|
+
import '@event-driven-io/emmett';
|
|
3
|
+
import 'express';
|
|
4
|
+
import 'http-problem-details';
|
|
5
|
+
import 'supertest';
|
|
6
|
+
import 'supertest/lib/agent';
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _createStarExport(obj) { Object.keys(obj) .filter((key) => key !== "default" && key !== "__esModule") .forEach((key) => { if (exports.hasOwnProperty(key)) { return; } Object.defineProperty(exports, key, {enumerable: true, configurable: true, get: () => obj[key]}); }); }var _apiSpecification = require('./apiSpecification'); _createStarExport(_apiSpecification);
|
|
2
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA,cAAc","sourcesContent":["export * from './apiSpecification';\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/testing/index.ts"],"sourcesContent":["export * from './apiSpecification';\n"],"mappings":"AAAA,cAAc;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@event-driven-io/emmett-expressjs",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Emmett - Event Sourcing development made simple",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"build": "tsup",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"dist"
|
|
41
41
|
],
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@event-driven-io/emmett": "
|
|
43
|
+
"@event-driven-io/emmett": "0.3.0",
|
|
44
44
|
"express": "4.18.2",
|
|
45
45
|
"express-async-errors": "3.1.1",
|
|
46
46
|
"http-problem-details": "0.1.5"
|
package/dist/testing.d.mts
DELETED
package/dist/testing.d.ts
DELETED
package/dist/testing.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"use strict";//# sourceMappingURL=testing.js.map
|
package/dist/testing.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":[],"names":[],"mappings":""}
|
package/dist/testing.mjs
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
//# sourceMappingURL=testing.mjs.map
|
package/dist/testing.mjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|