@event-driven-io/emmett-expressjs 0.17.0 → 0.18.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/index.d.mts +123 -14
- package/dist/index.d.ts +123 -14
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +6 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +8 -8
- package/dist/application.d.mts +0 -23
- package/dist/application.d.ts +0 -23
- package/dist/application.js +0 -2
- package/dist/application.js.map +0 -1
- package/dist/application.mjs +0 -2
- package/dist/application.mjs.map +0 -1
- package/dist/chunk-4NF6RLJ3.js +0 -2
- package/dist/chunk-4NF6RLJ3.js.map +0 -1
- package/dist/chunk-5C7LP2V7.mjs +0 -2
- package/dist/chunk-5C7LP2V7.mjs.map +0 -1
- package/dist/chunk-64OM6ELM.js +0 -7
- package/dist/chunk-64OM6ELM.js.map +0 -1
- package/dist/chunk-65PYZ4M4.js +0 -2
- package/dist/chunk-65PYZ4M4.js.map +0 -1
- package/dist/chunk-7H3QW2YD.js +0 -1
- package/dist/chunk-7H3QW2YD.js.map +0 -1
- package/dist/chunk-E4DZPUJH.js +0 -2
- package/dist/chunk-E4DZPUJH.js.map +0 -1
- package/dist/chunk-GEU24YU2.mjs +0 -2
- package/dist/chunk-GEU24YU2.mjs.map +0 -1
- package/dist/chunk-GVXJFMJL.mjs +0 -2
- package/dist/chunk-GVXJFMJL.mjs.map +0 -1
- package/dist/chunk-HKEQAT6F.mjs +0 -1
- package/dist/chunk-HKEQAT6F.mjs.map +0 -1
- package/dist/chunk-NESJKQ5K.js +0 -2
- package/dist/chunk-NESJKQ5K.js.map +0 -1
- package/dist/chunk-NZI5RAYP.js +0 -2
- package/dist/chunk-NZI5RAYP.js.map +0 -1
- package/dist/chunk-PB6KQGXQ.js +0 -2
- package/dist/chunk-PB6KQGXQ.js.map +0 -1
- package/dist/chunk-PFHWOLRV.mjs +0 -2
- package/dist/chunk-PFHWOLRV.mjs.map +0 -1
- package/dist/chunk-QAUBB7VR.mjs +0 -2
- package/dist/chunk-QAUBB7VR.mjs.map +0 -1
- package/dist/chunk-SFZHI2EY.js +0 -2
- package/dist/chunk-SFZHI2EY.js.map +0 -1
- package/dist/chunk-WOJ2OW3O.mjs +0 -2
- package/dist/chunk-WOJ2OW3O.mjs.map +0 -1
- package/dist/chunk-YHVJ37C3.mjs +0 -7
- package/dist/chunk-YHVJ37C3.mjs.map +0 -1
- package/dist/chunk-Z3SRHAET.mjs +0 -2
- package/dist/chunk-Z3SRHAET.mjs.map +0 -1
- package/dist/e2e/decider/api.d.mts +0 -16
- package/dist/e2e/decider/api.d.ts +0 -16
- package/dist/e2e/decider/api.js +0 -2
- package/dist/e2e/decider/api.js.map +0 -1
- package/dist/e2e/decider/api.mjs +0 -2
- package/dist/e2e/decider/api.mjs.map +0 -1
- package/dist/e2e/decider/businessLogic.d.mts +0 -53
- package/dist/e2e/decider/businessLogic.d.ts +0 -53
- package/dist/e2e/decider/businessLogic.js +0 -2
- package/dist/e2e/decider/businessLogic.js.map +0 -1
- package/dist/e2e/decider/businessLogic.mjs +0 -2
- package/dist/e2e/decider/businessLogic.mjs.map +0 -1
- package/dist/e2e/decider/shoppingCart.d.mts +0 -82
- package/dist/e2e/decider/shoppingCart.d.ts +0 -82
- package/dist/e2e/decider/shoppingCart.js +0 -2
- package/dist/e2e/decider/shoppingCart.js.map +0 -1
- package/dist/e2e/decider/shoppingCart.mjs +0 -2
- package/dist/e2e/decider/shoppingCart.mjs.map +0 -1
- package/dist/e2e/testing.d.mts +0 -19
- package/dist/e2e/testing.d.ts +0 -19
- package/dist/e2e/testing.js +0 -2
- package/dist/e2e/testing.js.map +0 -1
- package/dist/e2e/testing.mjs +0 -2
- package/dist/e2e/testing.mjs.map +0 -1
- package/dist/etag.d.mts +0 -25
- package/dist/etag.d.ts +0 -25
- package/dist/etag.js +0 -2
- package/dist/etag.js.map +0 -1
- package/dist/etag.mjs +0 -2
- package/dist/etag.mjs.map +0 -1
- package/dist/handler.d.mts +0 -24
- package/dist/handler.d.ts +0 -24
- package/dist/handler.js +0 -2
- package/dist/handler.js.map +0 -1
- package/dist/handler.mjs +0 -2
- package/dist/handler.mjs.map +0 -1
- package/dist/middlewares/index.d.mts +0 -2
- package/dist/middlewares/index.d.ts +0 -2
- package/dist/middlewares/index.js +0 -1
- package/dist/middlewares/index.js.map +0 -1
- package/dist/middlewares/index.mjs +0 -1
- package/dist/middlewares/index.mjs.map +0 -1
- package/dist/middlewares/problemDetailsMiddleware.d.mts +0 -10
- package/dist/middlewares/problemDetailsMiddleware.d.ts +0 -10
- package/dist/middlewares/problemDetailsMiddleware.js +0 -2
- package/dist/middlewares/problemDetailsMiddleware.js.map +0 -1
- package/dist/middlewares/problemDetailsMiddleware.mjs +0 -2
- package/dist/middlewares/problemDetailsMiddleware.mjs.map +0 -1
- package/dist/responses.d.mts +0 -37
- package/dist/responses.d.ts +0 -37
- package/dist/responses.js +0 -2
- package/dist/responses.js.map +0 -1
- package/dist/responses.mjs +0 -2
- package/dist/responses.mjs.map +0 -1
- package/dist/testing/apiE2ESpecification.d.mts +0 -19
- package/dist/testing/apiE2ESpecification.d.ts +0 -19
- package/dist/testing/apiE2ESpecification.js +0 -2
- package/dist/testing/apiE2ESpecification.js.map +0 -1
- package/dist/testing/apiE2ESpecification.mjs +0 -2
- package/dist/testing/apiE2ESpecification.mjs.map +0 -1
- package/dist/testing/apiSpecification.d.mts +0 -29
- package/dist/testing/apiSpecification.d.ts +0 -29
- package/dist/testing/apiSpecification.js +0 -2
- package/dist/testing/apiSpecification.js.map +0 -1
- package/dist/testing/apiSpecification.mjs +0 -2
- package/dist/testing/apiSpecification.mjs.map +0 -1
- package/dist/testing/index.d.mts +0 -7
- package/dist/testing/index.d.ts +0 -7
- package/dist/testing/index.js +0 -2
- package/dist/testing/index.js.map +0 -1
- package/dist/testing/index.mjs +0 -2
- package/dist/testing/index.mjs.map +0 -1
package/dist/index.d.mts
CHANGED
|
@@ -1,14 +1,123 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
1
|
+
import express, { Request, Response, Router, Application, NextFunction } from 'express';
|
|
2
|
+
import http from 'http';
|
|
3
|
+
import { ProblemDocument } from 'http-problem-details';
|
|
4
|
+
import { Brand, Event, TestEventStream, EventStore } from '@event-driven-io/emmett';
|
|
5
|
+
import supertest, { Test, Response as Response$1 } from 'supertest';
|
|
6
|
+
import TestAgent from 'supertest/lib/agent';
|
|
7
|
+
|
|
8
|
+
declare const HeaderNames: {
|
|
9
|
+
IF_MATCH: string;
|
|
10
|
+
IF_NOT_MATCH: string;
|
|
11
|
+
ETag: string;
|
|
12
|
+
};
|
|
13
|
+
type WeakETag = Brand<`W/${string}`, 'ETag'>;
|
|
14
|
+
type ETag = Brand<string, 'ETag'>;
|
|
15
|
+
declare const WeakETagRegex: RegExp;
|
|
16
|
+
declare const enum ETagErrors {
|
|
17
|
+
WRONG_WEAK_ETAG_FORMAT = "WRONG_WEAK_ETAG_FORMAT",
|
|
18
|
+
MISSING_IF_MATCH_HEADER = "MISSING_IF_MATCH_HEADER",
|
|
19
|
+
MISSING_IF_NOT_MATCH_HEADER = "MISSING_IF_NOT_MATCH_HEADER"
|
|
20
|
+
}
|
|
21
|
+
declare const isWeakETag: (etag: ETag) => etag is WeakETag;
|
|
22
|
+
declare const getWeakETagValue: (etag: ETag) => string;
|
|
23
|
+
declare const toWeakETag: (value: number | bigint | string) => WeakETag;
|
|
24
|
+
declare const getETagFromIfMatch: (request: Request) => ETag;
|
|
25
|
+
declare const getETagFromIfNotMatch: (request: Request) => ETag;
|
|
26
|
+
declare const setETag: (response: Response, etag: ETag) => void;
|
|
27
|
+
declare const getETagValueFromIfMatch: (request: Request) => string;
|
|
28
|
+
|
|
29
|
+
type ErrorToProblemDetailsMapping = (error: Error, request: Request) => ProblemDocument | undefined;
|
|
30
|
+
type HttpResponseOptions = {
|
|
31
|
+
body?: unknown;
|
|
32
|
+
location?: string;
|
|
33
|
+
eTag?: ETag;
|
|
34
|
+
};
|
|
35
|
+
declare const DefaultHttpResponseOptions: HttpResponseOptions;
|
|
36
|
+
type HttpProblemResponseOptions = {
|
|
37
|
+
location?: string;
|
|
38
|
+
eTag?: ETag;
|
|
39
|
+
} & Omit<HttpResponseOptions, 'body'> & ({
|
|
40
|
+
problem: ProblemDocument;
|
|
41
|
+
} | {
|
|
42
|
+
problemDetails: string;
|
|
43
|
+
});
|
|
44
|
+
declare const DefaultHttpProblemResponseOptions: HttpProblemResponseOptions;
|
|
45
|
+
type CreatedHttpResponseOptions = ({
|
|
46
|
+
createdId: string;
|
|
47
|
+
} | {
|
|
48
|
+
createdId?: string;
|
|
49
|
+
url: string;
|
|
50
|
+
}) & HttpResponseOptions;
|
|
51
|
+
declare const sendCreated: (response: Response, { eTag, ...options }: CreatedHttpResponseOptions) => void;
|
|
52
|
+
type AcceptedHttpResponseOptions = {
|
|
53
|
+
location: string;
|
|
54
|
+
} & HttpResponseOptions;
|
|
55
|
+
declare const sendAccepted: (response: Response, options: AcceptedHttpResponseOptions) => void;
|
|
56
|
+
type NoContentHttpResponseOptions = Omit<HttpResponseOptions, 'body'>;
|
|
57
|
+
declare const send: (response: Response, statusCode: number, options?: HttpResponseOptions) => void;
|
|
58
|
+
declare const sendProblem: (response: Response, statusCode: number, options?: HttpProblemResponseOptions) => void;
|
|
59
|
+
|
|
60
|
+
type WebApiSetup = (router: Router) => void;
|
|
61
|
+
type ApplicationOptions = {
|
|
62
|
+
apis: WebApiSetup[];
|
|
63
|
+
mapError?: ErrorToProblemDetailsMapping;
|
|
64
|
+
enableDefaultExpressEtag?: boolean;
|
|
65
|
+
disableJsonMiddleware?: boolean;
|
|
66
|
+
disableUrlEncodingMiddleware?: boolean;
|
|
67
|
+
disableProblemDetailsMiddleware?: boolean;
|
|
68
|
+
};
|
|
69
|
+
declare const getApplication: (options: ApplicationOptions) => express.Application;
|
|
70
|
+
type StartApiOptions = {
|
|
71
|
+
port?: number;
|
|
72
|
+
};
|
|
73
|
+
declare const startAPI: (app: Application, options?: StartApiOptions) => http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>;
|
|
74
|
+
|
|
75
|
+
type HttpHandler<RequestType extends Request> = (request: RequestType) => Promise<HttpResponse> | HttpResponse;
|
|
76
|
+
declare const on: <RequestType extends Request>(handle: HttpHandler<RequestType>) => (request: RequestType, response: Response, _next: NextFunction) => Promise<void>;
|
|
77
|
+
declare const OK: (options?: HttpResponseOptions) => HttpResponse;
|
|
78
|
+
declare const Created: (options: CreatedHttpResponseOptions) => HttpResponse;
|
|
79
|
+
declare const Accepted: (options: AcceptedHttpResponseOptions) => HttpResponse;
|
|
80
|
+
declare const NoContent: (options?: NoContentHttpResponseOptions) => HttpResponse;
|
|
81
|
+
type HttpResponse = (response: Response) => void;
|
|
82
|
+
declare const HttpResponse: (statusCode: number, options?: HttpResponseOptions) => HttpResponse;
|
|
83
|
+
declare const BadRequest: (options?: HttpProblemResponseOptions) => HttpResponse;
|
|
84
|
+
declare const Forbidden: (options?: HttpProblemResponseOptions) => HttpResponse;
|
|
85
|
+
declare const NotFound: (options?: HttpProblemResponseOptions) => HttpResponse;
|
|
86
|
+
declare const Conflict: (options?: HttpProblemResponseOptions) => HttpResponse;
|
|
87
|
+
declare const PreconditionFailed: (options: HttpProblemResponseOptions) => HttpResponse;
|
|
88
|
+
declare const HttpProblem: (statusCode: number, options?: HttpProblemResponseOptions) => HttpResponse;
|
|
89
|
+
|
|
90
|
+
type TestRequest = (request: TestAgent<supertest.Test>) => Test;
|
|
91
|
+
declare const existingStream: <EventType extends Event = Event>(streamId: string, events: EventType[]) => TestEventStream<EventType>;
|
|
92
|
+
type ResponseAssert = (response: Response$1) => boolean | void;
|
|
93
|
+
type ApiSpecificationAssert<EventType extends Event = Event> = TestEventStream<EventType>[] | ResponseAssert | [ResponseAssert, ...TestEventStream<EventType>[]];
|
|
94
|
+
declare const expect: <EventType extends Event = Event>(streamId: string, events: EventType[]) => TestEventStream<EventType>;
|
|
95
|
+
declare const expectNewEvents: <EventType extends Event = Event>(streamId: string, events: EventType[]) => TestEventStream<EventType>;
|
|
96
|
+
declare const expectResponse: <Body = unknown>(statusCode: number, options?: {
|
|
97
|
+
body?: Body;
|
|
98
|
+
headers?: {
|
|
99
|
+
[index: string]: string;
|
|
100
|
+
};
|
|
101
|
+
}) => (response: Response$1) => void;
|
|
102
|
+
declare const expectError: (errorCode: number, problemDetails?: Partial<ProblemDocument>) => (response: Response$1) => void;
|
|
103
|
+
type ApiSpecification<EventType extends Event = Event> = (...givenStreams: TestEventStream<EventType>[]) => {
|
|
104
|
+
when: (setupRequest: TestRequest) => {
|
|
105
|
+
then: (verify: ApiSpecificationAssert<EventType>) => Promise<void>;
|
|
106
|
+
};
|
|
107
|
+
};
|
|
108
|
+
declare const ApiSpecification: {
|
|
109
|
+
for: <EventType extends Event = Event, StreamVersion = bigint>(getEventStore: () => EventStore<StreamVersion>, getApplication: (eventStore: EventStore<StreamVersion>) => Application) => ApiSpecification<EventType>;
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
type E2EResponseAssert = (response: Response$1) => boolean | void;
|
|
113
|
+
type ApiE2ESpecificationAssert = [E2EResponseAssert];
|
|
114
|
+
type ApiE2ESpecification = (...givenRequests: TestRequest[]) => {
|
|
115
|
+
when: (setupRequest: TestRequest) => {
|
|
116
|
+
then: (verify: ApiE2ESpecificationAssert) => Promise<void>;
|
|
117
|
+
};
|
|
118
|
+
};
|
|
119
|
+
declare const ApiE2ESpecification: {
|
|
120
|
+
for: <StreamVersion = bigint>(getEventStore: () => EventStore<StreamVersion>, getApplication: (eventStore: EventStore<StreamVersion>) => Application) => ApiE2ESpecification;
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
export { Accepted, type AcceptedHttpResponseOptions, ApiE2ESpecification, type ApiE2ESpecificationAssert, ApiSpecification, type ApiSpecificationAssert, type ApplicationOptions, BadRequest, Conflict, Created, type CreatedHttpResponseOptions, DefaultHttpProblemResponseOptions, DefaultHttpResponseOptions, type E2EResponseAssert, type ETag, ETagErrors, type ErrorToProblemDetailsMapping, Forbidden, HeaderNames, type HttpHandler, HttpProblem, type HttpProblemResponseOptions, HttpResponse, type HttpResponseOptions, NoContent, type NoContentHttpResponseOptions, NotFound, OK, PreconditionFailed, type ResponseAssert, type StartApiOptions, type TestRequest, type WeakETag, WeakETagRegex, type WebApiSetup, existingStream, expect, expectError, expectNewEvents, expectResponse, getApplication, getETagFromIfMatch, getETagFromIfNotMatch, getETagValueFromIfMatch, getWeakETagValue, isWeakETag, on, send, sendAccepted, sendCreated, sendProblem, setETag, startAPI, toWeakETag };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,14 +1,123 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
1
|
+
import express, { Request, Response, Router, Application, NextFunction } from 'express';
|
|
2
|
+
import http from 'http';
|
|
3
|
+
import { ProblemDocument } from 'http-problem-details';
|
|
4
|
+
import { Brand, Event, TestEventStream, EventStore } from '@event-driven-io/emmett';
|
|
5
|
+
import supertest, { Test, Response as Response$1 } from 'supertest';
|
|
6
|
+
import TestAgent from 'supertest/lib/agent';
|
|
7
|
+
|
|
8
|
+
declare const HeaderNames: {
|
|
9
|
+
IF_MATCH: string;
|
|
10
|
+
IF_NOT_MATCH: string;
|
|
11
|
+
ETag: string;
|
|
12
|
+
};
|
|
13
|
+
type WeakETag = Brand<`W/${string}`, 'ETag'>;
|
|
14
|
+
type ETag = Brand<string, 'ETag'>;
|
|
15
|
+
declare const WeakETagRegex: RegExp;
|
|
16
|
+
declare const enum ETagErrors {
|
|
17
|
+
WRONG_WEAK_ETAG_FORMAT = "WRONG_WEAK_ETAG_FORMAT",
|
|
18
|
+
MISSING_IF_MATCH_HEADER = "MISSING_IF_MATCH_HEADER",
|
|
19
|
+
MISSING_IF_NOT_MATCH_HEADER = "MISSING_IF_NOT_MATCH_HEADER"
|
|
20
|
+
}
|
|
21
|
+
declare const isWeakETag: (etag: ETag) => etag is WeakETag;
|
|
22
|
+
declare const getWeakETagValue: (etag: ETag) => string;
|
|
23
|
+
declare const toWeakETag: (value: number | bigint | string) => WeakETag;
|
|
24
|
+
declare const getETagFromIfMatch: (request: Request) => ETag;
|
|
25
|
+
declare const getETagFromIfNotMatch: (request: Request) => ETag;
|
|
26
|
+
declare const setETag: (response: Response, etag: ETag) => void;
|
|
27
|
+
declare const getETagValueFromIfMatch: (request: Request) => string;
|
|
28
|
+
|
|
29
|
+
type ErrorToProblemDetailsMapping = (error: Error, request: Request) => ProblemDocument | undefined;
|
|
30
|
+
type HttpResponseOptions = {
|
|
31
|
+
body?: unknown;
|
|
32
|
+
location?: string;
|
|
33
|
+
eTag?: ETag;
|
|
34
|
+
};
|
|
35
|
+
declare const DefaultHttpResponseOptions: HttpResponseOptions;
|
|
36
|
+
type HttpProblemResponseOptions = {
|
|
37
|
+
location?: string;
|
|
38
|
+
eTag?: ETag;
|
|
39
|
+
} & Omit<HttpResponseOptions, 'body'> & ({
|
|
40
|
+
problem: ProblemDocument;
|
|
41
|
+
} | {
|
|
42
|
+
problemDetails: string;
|
|
43
|
+
});
|
|
44
|
+
declare const DefaultHttpProblemResponseOptions: HttpProblemResponseOptions;
|
|
45
|
+
type CreatedHttpResponseOptions = ({
|
|
46
|
+
createdId: string;
|
|
47
|
+
} | {
|
|
48
|
+
createdId?: string;
|
|
49
|
+
url: string;
|
|
50
|
+
}) & HttpResponseOptions;
|
|
51
|
+
declare const sendCreated: (response: Response, { eTag, ...options }: CreatedHttpResponseOptions) => void;
|
|
52
|
+
type AcceptedHttpResponseOptions = {
|
|
53
|
+
location: string;
|
|
54
|
+
} & HttpResponseOptions;
|
|
55
|
+
declare const sendAccepted: (response: Response, options: AcceptedHttpResponseOptions) => void;
|
|
56
|
+
type NoContentHttpResponseOptions = Omit<HttpResponseOptions, 'body'>;
|
|
57
|
+
declare const send: (response: Response, statusCode: number, options?: HttpResponseOptions) => void;
|
|
58
|
+
declare const sendProblem: (response: Response, statusCode: number, options?: HttpProblemResponseOptions) => void;
|
|
59
|
+
|
|
60
|
+
type WebApiSetup = (router: Router) => void;
|
|
61
|
+
type ApplicationOptions = {
|
|
62
|
+
apis: WebApiSetup[];
|
|
63
|
+
mapError?: ErrorToProblemDetailsMapping;
|
|
64
|
+
enableDefaultExpressEtag?: boolean;
|
|
65
|
+
disableJsonMiddleware?: boolean;
|
|
66
|
+
disableUrlEncodingMiddleware?: boolean;
|
|
67
|
+
disableProblemDetailsMiddleware?: boolean;
|
|
68
|
+
};
|
|
69
|
+
declare const getApplication: (options: ApplicationOptions) => express.Application;
|
|
70
|
+
type StartApiOptions = {
|
|
71
|
+
port?: number;
|
|
72
|
+
};
|
|
73
|
+
declare const startAPI: (app: Application, options?: StartApiOptions) => http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>;
|
|
74
|
+
|
|
75
|
+
type HttpHandler<RequestType extends Request> = (request: RequestType) => Promise<HttpResponse> | HttpResponse;
|
|
76
|
+
declare const on: <RequestType extends Request>(handle: HttpHandler<RequestType>) => (request: RequestType, response: Response, _next: NextFunction) => Promise<void>;
|
|
77
|
+
declare const OK: (options?: HttpResponseOptions) => HttpResponse;
|
|
78
|
+
declare const Created: (options: CreatedHttpResponseOptions) => HttpResponse;
|
|
79
|
+
declare const Accepted: (options: AcceptedHttpResponseOptions) => HttpResponse;
|
|
80
|
+
declare const NoContent: (options?: NoContentHttpResponseOptions) => HttpResponse;
|
|
81
|
+
type HttpResponse = (response: Response) => void;
|
|
82
|
+
declare const HttpResponse: (statusCode: number, options?: HttpResponseOptions) => HttpResponse;
|
|
83
|
+
declare const BadRequest: (options?: HttpProblemResponseOptions) => HttpResponse;
|
|
84
|
+
declare const Forbidden: (options?: HttpProblemResponseOptions) => HttpResponse;
|
|
85
|
+
declare const NotFound: (options?: HttpProblemResponseOptions) => HttpResponse;
|
|
86
|
+
declare const Conflict: (options?: HttpProblemResponseOptions) => HttpResponse;
|
|
87
|
+
declare const PreconditionFailed: (options: HttpProblemResponseOptions) => HttpResponse;
|
|
88
|
+
declare const HttpProblem: (statusCode: number, options?: HttpProblemResponseOptions) => HttpResponse;
|
|
89
|
+
|
|
90
|
+
type TestRequest = (request: TestAgent<supertest.Test>) => Test;
|
|
91
|
+
declare const existingStream: <EventType extends Event = Event>(streamId: string, events: EventType[]) => TestEventStream<EventType>;
|
|
92
|
+
type ResponseAssert = (response: Response$1) => boolean | void;
|
|
93
|
+
type ApiSpecificationAssert<EventType extends Event = Event> = TestEventStream<EventType>[] | ResponseAssert | [ResponseAssert, ...TestEventStream<EventType>[]];
|
|
94
|
+
declare const expect: <EventType extends Event = Event>(streamId: string, events: EventType[]) => TestEventStream<EventType>;
|
|
95
|
+
declare const expectNewEvents: <EventType extends Event = Event>(streamId: string, events: EventType[]) => TestEventStream<EventType>;
|
|
96
|
+
declare const expectResponse: <Body = unknown>(statusCode: number, options?: {
|
|
97
|
+
body?: Body;
|
|
98
|
+
headers?: {
|
|
99
|
+
[index: string]: string;
|
|
100
|
+
};
|
|
101
|
+
}) => (response: Response$1) => void;
|
|
102
|
+
declare const expectError: (errorCode: number, problemDetails?: Partial<ProblemDocument>) => (response: Response$1) => void;
|
|
103
|
+
type ApiSpecification<EventType extends Event = Event> = (...givenStreams: TestEventStream<EventType>[]) => {
|
|
104
|
+
when: (setupRequest: TestRequest) => {
|
|
105
|
+
then: (verify: ApiSpecificationAssert<EventType>) => Promise<void>;
|
|
106
|
+
};
|
|
107
|
+
};
|
|
108
|
+
declare const ApiSpecification: {
|
|
109
|
+
for: <EventType extends Event = Event, StreamVersion = bigint>(getEventStore: () => EventStore<StreamVersion>, getApplication: (eventStore: EventStore<StreamVersion>) => Application) => ApiSpecification<EventType>;
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
type E2EResponseAssert = (response: Response$1) => boolean | void;
|
|
113
|
+
type ApiE2ESpecificationAssert = [E2EResponseAssert];
|
|
114
|
+
type ApiE2ESpecification = (...givenRequests: TestRequest[]) => {
|
|
115
|
+
when: (setupRequest: TestRequest) => {
|
|
116
|
+
then: (verify: ApiE2ESpecificationAssert) => Promise<void>;
|
|
117
|
+
};
|
|
118
|
+
};
|
|
119
|
+
declare const ApiE2ESpecification: {
|
|
120
|
+
for: <StreamVersion = bigint>(getEventStore: () => EventStore<StreamVersion>, getApplication: (eventStore: EventStore<StreamVersion>) => Application) => ApiE2ESpecification;
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
export { Accepted, type AcceptedHttpResponseOptions, ApiE2ESpecification, type ApiE2ESpecificationAssert, ApiSpecification, type ApiSpecificationAssert, type ApplicationOptions, BadRequest, Conflict, Created, type CreatedHttpResponseOptions, DefaultHttpProblemResponseOptions, DefaultHttpResponseOptions, type E2EResponseAssert, type ETag, ETagErrors, type ErrorToProblemDetailsMapping, Forbidden, HeaderNames, type HttpHandler, HttpProblem, type HttpProblemResponseOptions, HttpResponse, type HttpResponseOptions, NoContent, type NoContentHttpResponseOptions, NotFound, OK, PreconditionFailed, type ResponseAssert, type StartApiOptions, type TestRequest, type WeakETag, WeakETagRegex, type WebApiSetup, existingStream, expect, expectError, expectNewEvents, expectResponse, getApplication, getETagFromIfMatch, getETagFromIfNotMatch, getETagValueFromIfMatch, getWeakETagValue, isWeakETag, on, send, sendAccepted, sendCreated, sendProblem, setETag, startAPI, toWeakETag };
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,7 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports, "__esModule", {value: true});
|
|
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; }require('express-async-errors');var _express = require('express'); var _express2 = _interopRequireDefault(_express);var _http = require('http'); var _http2 = _interopRequireDefault(_http);var _uuid = require('uuid');var _asyncretry = require('async-retry'); var _asyncretry2 = _interopRequireDefault(_asyncretry);var _webstreamspolyfill = require('web-streams-polyfill');var I=(e=>(e.NOT_A_NONEMPTY_STRING="NOT_A_NONEMPTY_STRING",e.NOT_A_POSITIVE_NUMBER="NOT_A_POSITIVE_NUMBER",e.NOT_AN_UNSIGNED_BIGINT="NOT_AN_UNSIGNED_BIGINT",e))(I||{}),R=e=>typeof e=="number"&&e===e;var _=class extends Error{constructor(e){super(`Cannot parse! ${e}`)}},u={stringify:(e,t)=>JSON.stringify(_optionalChain([t, 'optionalAccess', _2 => _2.map])?t.map(e):e,(r,s)=>typeof s=="bigint"?s.toString():s),parse:(e,t)=>{let r=JSON.parse(e,_optionalChain([t, 'optionalAccess', _3 => _3.reviver]));if(_optionalChain([t, 'optionalAccess', _4 => _4.typeCheck])&&!_optionalChain([t, 'optionalAccess', _5 => _5.typeCheck, 'call', _6 => _6(r)]))throw new _(e);return _optionalChain([t, 'optionalAccess', _7 => _7.map])?t.map(r):r}};var m=class extends Error{constructor(e){super(e)}},x=(e,t)=>{let r=e,s=t;return b(r),b(s),Object.keys(s).every(o=>typeof s[o]=="object"?x(r[o],s[o]):s[o]===r[o])},h=e=>{throw new m(_nullishCoalesce(e, () => ("That should not ever happened, right?")))};var f=(e,t,r)=>{if(!x(e,t))throw new m(_nullishCoalesce(r, () => (`subObj:
|
|
2
|
+
${u.stringify(t)}
|
|
3
|
+
is not subset of
|
|
4
|
+
${u.stringify(e)}`)))};function b(e,t){if(!e)throw new m(_nullishCoalesce(t, () => ("Condition is not truthy")))}function w(e,t,r){if(e!==t)throw new m(`${_nullishCoalesce(r, () => ("Objects are not equal"))}:
|
|
5
|
+
Expected: ${u.stringify(e)}
|
|
6
|
+
Actual:${u.stringify(t)}`)}var g=e=>{let t=new Map;return{async aggregateStream(r,s){return e.aggregateStream(r,s)},readStream(r,s){return e.readStream(r,s)},appendToStream:async(r,s,o)=>{let n=await e.appendToStream(r,s,o),c=_nullishCoalesce(t.get(r), () => ([r,[]]));return t.set(r,[r,[...c[1],...s]]),n},appendedEvents:t,setup:async(r,s)=>e.appendToStream(r,s)}};var _httpproblemdetails = require('http-problem-details');var A=e=>(t,r,s,o)=>{let n;e&&(n=e(t,r)),n=_nullishCoalesce(n, () => (P(t))),y(s,n.status,{problem:n})},P=e=>{let t=500;return"errorCode"in e&&R(e.errorCode)&&e.errorCode>=100&&e.errorCode<600&&(t=e.errorCode),new (0, _httpproblemdetails.ProblemDocument)({detail:e.message,status:t})};var me=e=>{let t=_express2.default.call(void 0, ),{apis:r,mapError:s,enableDefaultExpressEtag:o,disableJsonMiddleware:n,disableUrlEncodingMiddleware:c,disableProblemDetailsMiddleware:a}=e,i=_express.Router.call(void 0, );t.set("etag",_nullishCoalesce(o, () => (!1))),n||t.use(_express2.default.json()),c||t.use(_express2.default.urlencoded({extended:!0}));for(let p of r)p(i);return t.use(i),a||t.use(A(s)),t},fe= 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"},O= exports.WeakETagRegex =/W\/"(-?\d+.*)"/,F= 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))(F||{}),B= exports.isWeakETag =e=>O.test(e),V= exports.getWeakETagValue =e=>{let t=O.exec(e);if(t===null||t.length===0)throw new Error("WRONG_WEAK_ETAG_FORMAT");return t[1]},he= exports.toWeakETag =e=>`W/"${e}"`,j= exports.getETagFromIfMatch =e=>{let t=e.headers[T.IF_MATCH];if(t===void 0)throw new Error("MISSING_IF_MATCH_HEADER");return t},Ee= 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},v= exports.setETag =(e,t)=>{e.setHeader(T.ETag,t)},Te= exports.getETagValueFromIfMatch =e=>{let t=j(e);return B(t)?V(t):t};var Re=e=>async(t,r,s)=>(await Promise.resolve(e(t)))(r),xe= exports.OK =e=>t=>{d(t,200,e)},we= exports.Created =e=>t=>{N(t,e)},Ae= exports.Accepted =e=>t=>{C(t,e)},Oe= exports.NoContent =e=>$(204,e),$= exports.HttpResponse =(e,t)=>r=>{d(r,e,t)},Ne= exports.BadRequest =e=>l(400,e),Ce= exports.Forbidden =e=>l(403,e),He= exports.NotFound =e=>l(404,e),Me= exports.Conflict =e=>l(409,e),Ie= exports.PreconditionFailed =e=>l(412,e),l= exports.HttpProblem =(e,t)=>r=>{y(r,e,t)};var G={},U= exports.DefaultHttpProblemResponseOptions ={problemDetails:"Error occured!"},N= exports.sendCreated =(e,{eTag:t,...r})=>d(e,201,{location:"url"in r?r.url:`${e.req.url}/${r.createdId}`,body:"createdId"in r?{id:r.createdId}:void 0,eTag:t}),C= exports.sendAccepted =(e,t)=>d(e,202,t),d= exports.send =(e,t,r)=>{let{location:s,body:o,eTag:n}=_nullishCoalesce(r, () => (G));n&&v(e,n),s&&e.setHeader("Location",s),o?(e.statusCode=t,e.send(o)):e.sendStatus(t)},y= exports.sendProblem =(e,t,r)=>{r=_nullishCoalesce(r, () => (U));let{location:s,eTag:o}=r,n="problem"in r?r.problem:new (0, _httpproblemdetails.ProblemDocument)({detail:r.problemDetails,status:t});o&&v(e,o),s&&e.setHeader("Location",s),e.setHeader("Content-Type","application/problem+json"),e.statusCode=t,e.json(n)};var _supertest = require('supertest'); var _supertest2 = _interopRequireDefault(_supertest);var _assert = require('assert'); var _assert2 = _interopRequireDefault(_assert);var je={for:(e,t)=>(...r)=>{let s=g(e()),o=t(s);return{when:n=>{let c=async()=>{for(let a of r)await a(_supertest2.default.call(void 0, o));return n(_supertest2.default.call(void 0, o))};return{then:async a=>{let i=await c();a.forEach(p=>{p(i)===!1&&_assert2.default.fail()})}}}}}};var Ye=(e,t)=>[e,t],Je= exports.expect =(e,t)=>[e,t],Ke= exports.expectNewEvents =(e,t)=>[e,t],K= exports.expectResponse =(e,t)=>r=>{let{body:s,headers:o}=_nullishCoalesce(t, () => ({}));w(e,r.statusCode,"Response code doesn't match"),s&&f(r.body,s),o&&f(r.headers,o)},Le= exports.expectError =(e,t)=>K(e,t?{body:t}:void 0),Xe= exports.ApiSpecification ={for:(e,t)=>(...r)=>{let s=g(e()),o=t(s);return{when:n=>{let c=async()=>{for(let[a,i]of r)await s.setup(a,i);return n(_supertest2.default.call(void 0, o))};return{then:async a=>{let i=await c();if(typeof a=="function")a(i)===!1&&h();else if(Array.isArray(a)){let[p,...S]=a;typeof p=="function"&&p(i)===!1&&h();let M=typeof p=="function"?S:a;f(Array.from(s.appendedEvents.values()),M)}}}}}}};exports.Accepted = Ae; exports.ApiE2ESpecification = je; exports.ApiSpecification = Xe; exports.BadRequest = Ne; exports.Conflict = Me; exports.Created = we; exports.DefaultHttpProblemResponseOptions = U; exports.DefaultHttpResponseOptions = G; exports.ETagErrors = F; exports.Forbidden = Ce; exports.HeaderNames = T; exports.HttpProblem = l; exports.HttpResponse = $; exports.NoContent = Oe; exports.NotFound = He; exports.OK = xe; exports.PreconditionFailed = Ie; exports.WeakETagRegex = O; exports.existingStream = Ye; exports.expect = Je; exports.expectError = Le; exports.expectNewEvents = Ke; exports.expectResponse = K; exports.getApplication = me; exports.getETagFromIfMatch = j; exports.getETagFromIfNotMatch = Ee; exports.getETagValueFromIfMatch = Te; exports.getWeakETagValue = V; exports.isWeakETag = B; exports.on = Re; exports.send = d; exports.sendAccepted = C; exports.sendCreated = N; exports.sendProblem = y; exports.setETag = v; exports.startAPI = fe; exports.toWeakETag = he;
|
|
2
7
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"names":[],"mappings":""}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/application.ts","../../emmett/src/eventStore/inMemoryEventStore.ts","../../emmett/src/streaming/restream.ts","../../emmett/src/validation/index.ts","../../emmett/src/serialization/json/JSONParser.ts","../../emmett/src/testing/assertions.ts","/home/runner/work/emmett/emmett/src/packages/emmett-expressjs/dist/index.js","../src/middlewares/problemDetailsMiddleware.ts"],"names":["ValidationErrors","isNumber","val","ParseError","text"],"mappings":"AAAA,izBAAO,oFCA2C,wEAEjC,4BCFgB,iGCAf,0DAMX,ICJWA,CAAAA,CAAAA,CAAAA,CAAAA,EAAAA,CAChBA,CAAAA,CAAA,qBAAA,CAAwB,uBAAA,CACxBA,CAAAA,CAAA,qBAAA,CAAwB,uBAAA,CACxBA,CAAAA,CAAA,sBAAA,CAAyB,wBAAA,CAHTA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,EAAA,CAAA,CAAA,CAAA,CAMLC,CAAAA,CAAYC,CAAAA,EACvB,OAAOA,CAAAA,EAAQ,QAAA,EAAYA,CAAAA,GAAQA,CAAAA,CCT9B,IAAMC,CAAAA,CAAN,MAAA,QAAyB,KAAM,CACpC,WAAA,CAAYC,CAAAA,CAAc,CACxB,KAAA,CAAM,CAAA,cAAA,EAAiBA,CAAI,CAAA,CAAA;AC6DmB;AC7Dsb;AD2H7b;AAA8C,UAAA;AEnGtE,OAAA","file":"/home/runner/work/emmett/emmett/src/packages/emmett-expressjs/dist/index.js","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 { v4 as randomUUID } 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';\n\nexport type EventHandler<E extends Event = Event> = (\n eventEnvelope: ReadEvent<E>,\n) => void;\n\nexport const getInMemoryEventStore = (): EventStore<\n DefaultStreamVersionType,\n ReadEventMetadataWithGlobalPosition\n> => {\n const streams = new Map<string, ReadEvent[]>();\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> | null> {\n const { evolve, initialState, read } = options;\n\n const result = await this.readStream<EventType>(streamName, read);\n\n if (!result) return null;\n\n const events = result?.events ?? [];\n\n return {\n currentStreamVersion: BigInt(events.length),\n state: events.reduce(evolve, initialState()),\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 ? BigInt(events.length) : undefined;\n\n assertExpectedVersionMatchesCurrent(\n currentStreamVersion,\n options?.expectedStreamVersion,\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 && 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 events && events.length > 0\n ? {\n currentStreamVersion: currentStreamVersion!,\n events: resultEvents,\n }\n : null;\n\n return Promise.resolve(result);\n },\n\n appendToStream: <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 ? BigInt(currentEvents.length) : undefined;\n\n assertExpectedVersionMatchesCurrent(\n currentStreamVersion,\n options?.expectedStreamVersion,\n );\n\n const eventEnvelopes: ReadEvent<\n EventType,\n ReadEventMetadataWithGlobalPosition\n >[] = events.map((event, index) => {\n return {\n ...event,\n metadata: {\n ...(event.metadata ?? {}),\n streamName,\n eventId: randomUUID(),\n streamPosition: BigInt(currentEvents.length + index + 1),\n globalPosition: BigInt(getAllEventsCount() + index + 1),\n },\n };\n });\n\n const positionOfLastEventInTheStream = BigInt(\n eventEnvelopes.slice(-1)[0]!.metadata.streamPosition,\n );\n\n streams.set(streamName, [...currentEvents, ...eventEnvelopes]);\n\n const result: AppendToStreamResult = {\n nextExpectedStreamVersion: positionOfLastEventInTheStream,\n };\n\n return Promise.resolve(result);\n },\n };\n};\n","import retry from 'async-retry';\nimport {\n ReadableStream,\n ReadableStreamDefaultReader,\n TransformStream,\n TransformStreamDefaultController,\n} from 'web-streams-polyfill';\nimport type { Decoder } from './decoders';\nimport { DefaultDecoder } from './decoders/composite';\n\nexport const restream = <\n Source = unknown,\n Transformed = Source,\n StreamType = object,\n>(\n createSourceStream: () => ReadableStream<StreamType>,\n transform: (input: Source) => Transformed = (source) =>\n source as unknown as Transformed,\n retryOptions: retry.Options = { forever: true, minTimeout: 25 },\n decoder: Decoder<StreamType, Source> = new DefaultDecoder<Source>(),\n): ReadableStream<Transformed> =>\n new TransformStream<Source, Transformed>({\n start(controller) {\n retry(\n () => onRestream(createSourceStream, controller, transform, decoder),\n retryOptions,\n ).catch((error) => {\n controller.error(error);\n });\n },\n }).readable;\n\nconst onRestream = async <StreamType, Source, Transformed = Source>(\n createSourceStream: () => ReadableStream<StreamType>,\n controller: TransformStreamDefaultController<Transformed>,\n transform: (input: Source) => Transformed,\n decoder: Decoder<StreamType, Source>,\n): Promise<void> => {\n const sourceStream = createSourceStream();\n const reader = sourceStream.getReader();\n try {\n let done: boolean;\n\n do {\n done = await restreamChunk(reader, controller, transform, decoder);\n } while (!done);\n } finally {\n reader.releaseLock();\n }\n};\n\nconst restreamChunk = async <StreamType, Source, Transformed = Source>(\n reader: ReadableStreamDefaultReader<StreamType>,\n controller: TransformStreamDefaultController<Transformed>,\n transform: (input: Source) => Transformed,\n decoder: Decoder<StreamType, Source>,\n): Promise<boolean> => {\n const { done: isDone, value } = await reader.read();\n\n if (value) decoder.addToBuffer(value);\n\n if (!isDone && !decoder.hasCompleteMessage()) return false;\n\n decodeAndTransform(decoder, transform, controller);\n\n if (isDone) {\n controller.terminate();\n }\n\n return isDone;\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;\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 { 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","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}\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};\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 { 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"]}
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,7 @@
|
|
|
1
|
-
import
|
|
1
|
+
import"express-async-errors";import E,{Router as q}from"express";import"express-async-errors";import k from"http";import{v4 as z}from"uuid";import Q from"async-retry";import{TransformStream as te}from"web-streams-polyfill";var I=(e=>(e.NOT_A_NONEMPTY_STRING="NOT_A_NONEMPTY_STRING",e.NOT_A_POSITIVE_NUMBER="NOT_A_POSITIVE_NUMBER",e.NOT_AN_UNSIGNED_BIGINT="NOT_AN_UNSIGNED_BIGINT",e))(I||{}),R=e=>typeof e=="number"&&e===e;var _=class extends Error{constructor(e){super(`Cannot parse! ${e}`)}},u={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 _(e);return t?.map?t.map(r):r}};var m=class extends Error{constructor(e){super(e)}},x=(e,t)=>{let r=e,s=t;return b(r),b(s),Object.keys(s).every(o=>typeof s[o]=="object"?x(r[o],s[o]):s[o]===r[o])},h=e=>{throw new m(e??"That should not ever happened, right?")};var f=(e,t,r)=>{if(!x(e,t))throw new m(r??`subObj:
|
|
2
|
+
${u.stringify(t)}
|
|
3
|
+
is not subset of
|
|
4
|
+
${u.stringify(e)}`)};function b(e,t){if(!e)throw new m(t??"Condition is not truthy")}function w(e,t,r){if(e!==t)throw new m(`${r??"Objects are not equal"}:
|
|
5
|
+
Expected: ${u.stringify(e)}
|
|
6
|
+
Actual:${u.stringify(t)}`)}var g=e=>{let t=new Map;return{async aggregateStream(r,s){return e.aggregateStream(r,s)},readStream(r,s){return e.readStream(r,s)},appendToStream:async(r,s,o)=>{let n=await e.appendToStream(r,s,o),c=t.get(r)??[r,[]];return t.set(r,[r,[...c[1],...s]]),n},appendedEvents:t,setup:async(r,s)=>e.appendToStream(r,s)}};import{ProblemDocument as D}from"http-problem-details";var A=e=>(t,r,s,o)=>{let n;e&&(n=e(t,r)),n=n??P(t),y(s,n.status,{problem:n})},P=e=>{let t=500;return"errorCode"in e&&R(e.errorCode)&&e.errorCode>=100&&e.errorCode<600&&(t=e.errorCode),new D({detail:e.message,status:t})};var me=e=>{let t=E(),{apis:r,mapError:s,enableDefaultExpressEtag:o,disableJsonMiddleware:n,disableUrlEncodingMiddleware:c,disableProblemDetailsMiddleware:a}=e,i=q();t.set("etag",o??!1),n||t.use(E.json()),c||t.use(E.urlencoded({extended:!0}));for(let p of r)p(i);return t.use(i),a||t.use(A(s)),t},fe=(e,t={port:3e3})=>{let{port:r}=t,s=k.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"},O=/W\/"(-?\d+.*)"/,F=(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))(F||{}),B=e=>O.test(e),V=e=>{let t=O.exec(e);if(t===null||t.length===0)throw new Error("WRONG_WEAK_ETAG_FORMAT");return t[1]},he=e=>`W/"${e}"`,j=e=>{let t=e.headers[T.IF_MATCH];if(t===void 0)throw new Error("MISSING_IF_MATCH_HEADER");return t},Ee=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},v=(e,t)=>{e.setHeader(T.ETag,t)},Te=e=>{let t=j(e);return B(t)?V(t):t};import"express";var Re=e=>async(t,r,s)=>(await Promise.resolve(e(t)))(r),xe=e=>t=>{d(t,200,e)},we=e=>t=>{N(t,e)},Ae=e=>t=>{C(t,e)},Oe=e=>$(204,e),$=(e,t)=>r=>{d(r,e,t)},Ne=e=>l(400,e),Ce=e=>l(403,e),He=e=>l(404,e),Me=e=>l(409,e),Ie=e=>l(412,e),l=(e,t)=>r=>{y(r,e,t)};import"express";import{ProblemDocument as W}from"http-problem-details";var G={},U={problemDetails:"Error occured!"},N=(e,{eTag:t,...r})=>d(e,201,{location:"url"in r?r.url:`${e.req.url}/${r.createdId}`,body:"createdId"in r?{id:r.createdId}:void 0,eTag:t}),C=(e,t)=>d(e,202,t),d=(e,t,r)=>{let{location:s,body:o,eTag:n}=r??G;n&&v(e,n),s&&e.setHeader("Location",s),o?(e.statusCode=t,e.send(o)):e.sendStatus(t)},y=(e,t,r)=>{r=r??U;let{location:s,eTag:o}=r,n="problem"in r?r.problem:new W({detail:r.problemDetails,status:t});o&&v(e,o),s&&e.setHeader("Location",s),e.setHeader("Content-Type","application/problem+json"),e.statusCode=t,e.json(n)};import H from"supertest";import Y from"assert";var je={for:(e,t)=>(...r)=>{let s=g(e()),o=t(s);return{when:n=>{let c=async()=>{for(let a of r)await a(H(o));return n(H(o))};return{then:async a=>{let i=await c();a.forEach(p=>{p(i)===!1&&Y.fail()})}}}}}};import"express";import J from"supertest";var Ye=(e,t)=>[e,t],Je=(e,t)=>[e,t],Ke=(e,t)=>[e,t],K=(e,t)=>r=>{let{body:s,headers:o}=t??{};w(e,r.statusCode,"Response code doesn't match"),s&&f(r.body,s),o&&f(r.headers,o)},Le=(e,t)=>K(e,t?{body:t}:void 0),Xe={for:(e,t)=>(...r)=>{let s=g(e()),o=t(s);return{when:n=>{let c=async()=>{for(let[a,i]of r)await s.setup(a,i);return n(J(o))};return{then:async a=>{let i=await c();if(typeof a=="function")a(i)===!1&&h();else if(Array.isArray(a)){let[p,...S]=a;typeof p=="function"&&p(i)===!1&&h();let M=typeof p=="function"?S:a;f(Array.from(s.appendedEvents.values()),M)}}}}}}};export{Ae as Accepted,je as ApiE2ESpecification,Xe as ApiSpecification,Ne as BadRequest,Me as Conflict,we as Created,U as DefaultHttpProblemResponseOptions,G as DefaultHttpResponseOptions,F as ETagErrors,Ce as Forbidden,T as HeaderNames,l as HttpProblem,$ as HttpResponse,Oe as NoContent,He as NotFound,xe as OK,Ie as PreconditionFailed,O as WeakETagRegex,Ye as existingStream,Je as expect,Le as expectError,Ke as expectNewEvents,K as expectResponse,me as getApplication,j as getETagFromIfMatch,Ee as getETagFromIfNotMatch,Te as getETagValueFromIfMatch,V as getWeakETagValue,B as isWeakETag,Re as on,d as send,C as sendAccepted,N as sendCreated,y as sendProblem,v as setETag,fe as startAPI,he as toWeakETag};
|
|
2
7
|
//# sourceMappingURL=index.mjs.map
|