@kensio/simnaril 0.0.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/api.d.ts +25 -3
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +35 -31
- package/dist/api.js.map +1 -1
- package/dist/http/decode-form.d.ts +38 -0
- package/dist/http/decode-form.d.ts.map +1 -0
- package/dist/http/decode-form.js +119 -0
- package/dist/http/decode-form.js.map +1 -0
- package/dist/http/error-formatter.d.ts +13 -0
- package/dist/http/error-formatter.d.ts.map +1 -0
- package/dist/http/error-formatter.js +2 -0
- package/dist/http/error-formatter.js.map +1 -0
- package/dist/http/operation-router.d.ts +2 -0
- package/dist/http/operation-router.d.ts.map +1 -1
- package/dist/http/operation-router.js +16 -1
- package/dist/http/operation-router.js.map +1 -1
- package/dist/http/operation.d.ts +2 -1
- package/dist/http/operation.d.ts.map +1 -1
- package/dist/http/operation.js.map +1 -1
- package/dist/http/path-parameter.d.ts +3 -0
- package/dist/http/path-parameter.d.ts.map +1 -0
- package/dist/http/path-parameter.js +12 -0
- package/dist/http/path-parameter.js.map +1 -0
- package/dist/http/raw-operation.d.ts +16 -0
- package/dist/http/raw-operation.d.ts.map +1 -0
- package/dist/http/raw-operation.js +27 -0
- package/dist/http/raw-operation.js.map +1 -0
- package/dist/http/request-decoder.d.ts +30 -0
- package/dist/http/request-decoder.d.ts.map +1 -0
- package/dist/http/request-decoder.js +18 -0
- package/dist/http/request-decoder.js.map +1 -0
- package/dist/http/resource-operation-registry.d.ts +1 -1
- package/dist/http/resource-operation-registry.d.ts.map +1 -1
- package/dist/http/resource-operation-registry.js +6 -3
- package/dist/http/resource-operation-registry.js.map +1 -1
- package/dist/http/resource-operation.d.ts +2 -1
- package/dist/http/resource-operation.d.ts.map +1 -1
- package/dist/http/resource-operation.js +3 -3
- package/dist/http/resource-operation.js.map +1 -1
- package/dist/http/resource-path.d.ts +13 -0
- package/dist/http/resource-path.d.ts.map +1 -0
- package/dist/http/resource-path.js +23 -0
- package/dist/http/resource-path.js.map +1 -0
- package/dist/http/rest-resource-operation-defaults.d.ts +10 -0
- package/dist/http/rest-resource-operation-defaults.d.ts.map +1 -0
- package/dist/http/rest-resource-operation-defaults.js +8 -0
- package/dist/http/rest-resource-operation-defaults.js.map +1 -0
- package/dist/http/rest-resource-operations.d.ts +3 -3
- package/dist/http/rest-resource-operations.d.ts.map +1 -1
- package/dist/http/rest-resource-operations.js +37 -51
- package/dist/http/rest-resource-operations.js.map +1 -1
- package/dist/http/semantic-http-operation.d.ts +2 -1
- package/dist/http/semantic-http-operation.d.ts.map +1 -1
- package/dist/http/semantic-http-operation.js.map +1 -1
- package/dist/http/supplied-rest-resource-operation.d.ts +22 -0
- package/dist/http/supplied-rest-resource-operation.d.ts.map +1 -0
- package/dist/http/supplied-rest-resource-operation.js +27 -0
- package/dist/http/supplied-rest-resource-operation.js.map +1 -0
- package/dist/index.d.ts +10 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -3
- package/dist/index.js.map +1 -1
- package/dist/resource.d.ts +1 -1
- package/dist/resource.d.ts.map +1 -1
- package/dist/resource.js +1 -1
- package/dist/resource.js.map +1 -1
- package/dist/rest-resource-operation.d.ts +20 -2
- package/dist/rest-resource-operation.d.ts.map +1 -1
- package/dist/rest-resource.d.ts +2 -0
- package/dist/rest-resource.d.ts.map +1 -1
- package/dist/rest-resource.js +9 -1
- package/dist/rest-resource.js.map +1 -1
- package/dist/unimplemented-route-error.d.ts +2 -1
- package/dist/unimplemented-route-error.d.ts.map +1 -1
- package/dist/unimplemented-route-error.js +4 -2
- package/dist/unimplemented-route-error.js.map +1 -1
- package/dist/webhooks.d.ts +98 -0
- package/dist/webhooks.d.ts.map +1 -0
- package/dist/webhooks.js +129 -0
- package/dist/webhooks.js.map +1 -0
- package/package.json +2 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rest-resource-operation.d.ts","sourceRoot":"","sources":["../src/rest-resource-operation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EACjB,wBAAwB,EACzB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD,
|
|
1
|
+
{"version":3,"file":"rest-resource-operation.d.ts","sourceRoot":"","sources":["../src/rest-resource-operation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EACjB,wBAAwB,EACzB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD,uEAAuE;AACvE,MAAM,MAAM,eAAe,GAAG,CAC5B,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,KACrC,MAAM,CAAC;AAEZ,iFAAiF;AACjF,MAAM,WAAW,kCAAkC;IACjD,+DAA+D;IAC/D,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,kEAAkE;IAClE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kEAAkE;IAClE,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,2EAA2E;AAC3E,MAAM,MAAM,4BAA4B,GACpC,kCAAkC,GAClC,KAAK,CAAC;AAEV,iFAAiF;AACjF,MAAM,WAAW,iBAAiB;IAChC,qEAAqE;IACrE,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,uEAAuE;IACvE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,4EAA4E;IAC5E,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,+DAA+D;IAC/D,UAAU,CAAC,EAAE,OAAO,CAClB,MAAM,CAAC,yBAAyB,EAAE,4BAA4B,CAAC,CAChE,CAAC;IACF,qEAAqE;IACrE,IAAI,EAAE,MAAM,CAAC;CACd;AAED,8CAA8C;AAC9C,MAAM,MAAM,yBAAyB,GACjC,MAAM,GACN,QAAQ,GACR,KAAK,GACL,QAAQ,GACR,QAAQ,CAAC;AAEb,0DAA0D;AAC1D,MAAM,WAAW,sBAAsB,CAAC,CAAC,SAAS,MAAM;IACtD,MAAM,EAAE,iBAAiB,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1D,MAAM,EAAE,iBAAiB,CAAC,OAAO,EAAE,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;IACvD,GAAG,EAAE,iBAAiB,CAAC,OAAO,EAAE,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;IACpD,IAAI,EAAE,iBAAiB,CAAC,OAAO,EAAE,CAAC,EAAE,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;IACvD,MAAM,EAAE,iBAAiB,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;CAC3D;AAED,wDAAwD;AACxD,MAAM,WAAW,sBAAsB,CAAC,CAAC,SAAS,MAAM,EAAE,MAAM,EAAE,OAAO;IACvE,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,MAAM,EAAE,CACN,OAAO,EAAE,wBAAwB,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,KACvD,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd"}
|
package/dist/rest-resource.d.ts
CHANGED
|
@@ -10,6 +10,8 @@ export declare class RestResource<T extends object> {
|
|
|
10
10
|
readonly path: string;
|
|
11
11
|
readonly state: SimResource<T>;
|
|
12
12
|
constructor(state: SimResource<T>, props: RestResourceProps);
|
|
13
|
+
/** Returns the state identity named by matched route parameters. */
|
|
14
|
+
locate(params: Readonly<Record<string, string>>): string;
|
|
13
15
|
/** Adds middleware around every operation owned by this resource. */
|
|
14
16
|
use(middleware: HttpMiddleware): this;
|
|
15
17
|
/** Adds a named domain action under this resource's collection path. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rest-resource.d.ts","sourceRoot":"","sources":["../src/rest-resource.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAC7E,OAAO,EAAE,yBAAyB,EAAE,MAAM,uCAAuC,CAAC;AAClF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"rest-resource.d.ts","sourceRoot":"","sources":["../src/rest-resource.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAC7E,OAAO,EAAE,yBAAyB,EAAE,MAAM,uCAAuC,CAAC;AAClF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,KAAK,EAEV,sBAAsB,EACtB,sBAAsB,EACtB,iBAAiB,EAClB,MAAM,8BAA8B,CAAC;AAGtC,QAAA,MAAM,cAAc,eAAiC,CAAC;AAEtD,mEAAmE;AACnE,qBAAa,YAAY,CAAC,CAAC,SAAS,MAAM;;IACxC,QAAQ,CAAC,UAAU,EAAE,sBAAsB,CAAC,CAAC,CAAC,CAAC;IAC/C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC;IAI/B,YAAY,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,iBAAiB,EAO1D;IAED,oEAAoE;IACpE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,MAAM,CAEvD;IAED,qEAAqE;IACrE,GAAG,CAAC,UAAU,EAAE,cAAc,GAAG,IAAI,CAGpC;IAED,wEAAwE;IACxE,SAAS,CAAC,MAAM,GAAG,OAAO,EAAE,OAAO,GAAG,OAAO,EAC3C,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,sBAAsB,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,GAChD,iBAAiB,CAAC,MAAM,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,CAErD;IAED,CAAC,cAAc,CAAC,CACd,iBAAiB,EAAE,UAAU,CAAC,yBAAyB,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,GACvE,IAAI,CAEN;IAED,yDAAyD;IACzD,IAAI,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAEjB;IAED,oEAAoE;IACpE,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAE3B;IAED,+DAA+D;IAC/D,GAAG,CAAC,QAAQ,EAAE,MAAM,GAAG,CAAC,CAEvB;IAED,kDAAkD;IAClD,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS,CAEpC;IAED,+CAA+C;IAC/C,IAAI,IAAI,CAAC,EAAE,CAEV;IAED,8CAA8C;IAC9C,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAE/C;IAED,iDAAiD;IACjD,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,CAAC,CAE1B;IAED,+CAA+C;IAC/C,KAAK,IAAI,IAAI,CAEZ;CACF;AAED,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,MAAM,EACjD,QAAQ,EAAE,YAAY,CAAC,CAAC,CAAC,EACzB,iBAAiB,EAAE,UAAU,CAAC,yBAAyB,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,GACvE,IAAI,CAEN"}
|
package/dist/rest-resource.js
CHANGED
|
@@ -1,17 +1,25 @@
|
|
|
1
1
|
import { ResourceOperationRegistry } from "./http/resource-operation-registry.js";
|
|
2
|
+
import { requirePathParameter } from "./http/path-parameter.js";
|
|
2
3
|
const attachResource = Symbol("attach REST resource");
|
|
3
4
|
/** Adds conventional HTTP exposure to simulated resource state. */
|
|
4
5
|
export class RestResource {
|
|
5
6
|
operations;
|
|
6
7
|
path;
|
|
7
8
|
state;
|
|
9
|
+
#locate;
|
|
8
10
|
#operationRegistry;
|
|
9
11
|
constructor(state, props) {
|
|
10
12
|
this.state = state;
|
|
11
13
|
this.path = props.path;
|
|
12
|
-
this.#
|
|
14
|
+
this.#locate =
|
|
15
|
+
props.locate ?? ((params) => requirePathParameter(params, "id"));
|
|
16
|
+
this.#operationRegistry = new ResourceOperationRegistry(this, props);
|
|
13
17
|
this.operations = this.#operationRegistry.operations;
|
|
14
18
|
}
|
|
19
|
+
/** Returns the state identity named by matched route parameters. */
|
|
20
|
+
locate(params) {
|
|
21
|
+
return this.#locate(params);
|
|
22
|
+
}
|
|
15
23
|
/** Adds middleware around every operation owned by this resource. */
|
|
16
24
|
use(middleware) {
|
|
17
25
|
this.#operationRegistry.use(middleware);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rest-resource.js","sourceRoot":"","sources":["../src/rest-resource.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,yBAAyB,EAAE,MAAM,uCAAuC,CAAC;AAQlF,MAAM,cAAc,GAAG,MAAM,CAAC,sBAAsB,CAAC,CAAC;AAEtD,mEAAmE;AACnE,MAAM,OAAO,YAAY;IACd,UAAU,CAA4B;IACtC,IAAI,CAAS;IACb,KAAK,CAAiB;IACtB,kBAAkB,CAA+B;IAE1D,YAAY,KAAqB,EAAE,KAAwB;QACzD,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;QACvB,IAAI,CAAC,kBAAkB,GAAG,IAAI,yBAAyB,
|
|
1
|
+
{"version":3,"file":"rest-resource.js","sourceRoot":"","sources":["../src/rest-resource.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,yBAAyB,EAAE,MAAM,uCAAuC,CAAC;AAQlF,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAEhE,MAAM,cAAc,GAAG,MAAM,CAAC,sBAAsB,CAAC,CAAC;AAEtD,mEAAmE;AACnE,MAAM,OAAO,YAAY;IACd,UAAU,CAA4B;IACtC,IAAI,CAAS;IACb,KAAK,CAAiB;IACtB,OAAO,CAAkB;IACzB,kBAAkB,CAA+B;IAE1D,YAAY,KAAqB,EAAE,KAAwB;QACzD,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;QACvB,IAAI,CAAC,OAAO;YACV,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC,MAAM,EAAU,EAAE,CAAC,oBAAoB,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC;QAC3E,IAAI,CAAC,kBAAkB,GAAG,IAAI,yBAAyB,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACrE,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,kBAAkB,CAAC,UAAU,CAAC;IACvD,CAAC;IAED,oEAAoE;IACpE,MAAM,CAAC,MAAwC;QAC7C,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9B,CAAC;IAED,qEAAqE;IACrE,GAAG,CAAC,UAA0B;QAC5B,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACxC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,wEAAwE;IACxE,SAAS,CACP,IAAY,EACZ,KAAiD;QAEjD,OAAO,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAClD,CAAC;IAED,CAAC,cAAc,CAAC,CACd,iBAAwE;QAExE,IAAI,CAAC,kBAAkB,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC;IACpD,CAAC;IAED,yDAAyD;IACzD,IAAI,CAAC,MAAS;QACZ,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACjC,CAAC;IAED,oEAAoE;IACpE,MAAM,CAAC,KAAiB;QACtB,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAClC,CAAC;IAED,+DAA+D;IAC/D,GAAG,CAAC,QAAgB;QAClB,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAClC,CAAC;IAED,kDAAkD;IAClD,IAAI,CAAC,QAAgB;QACnB,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACnC,CAAC;IAED,+CAA+C;IAC/C,IAAI;QACF,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;IAC3B,CAAC;IAED,8CAA8C;IAC9C,MAAM,CAAC,QAAgB,EAAE,OAAmB;QAC1C,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC9C,CAAC;IAED,iDAAiD;IACjD,MAAM,CAAC,QAAgB;QACrB,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IACrC,CAAC;IAED,+CAA+C;IAC/C,KAAK;QACH,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;CACF;AAED,MAAM,UAAU,kBAAkB,CAChC,QAAyB,EACzB,iBAAwE;IAExE,QAAQ,CAAC,cAAc,CAAC,CAAC,iBAAiB,CAAC,CAAC;AAC9C,CAAC"}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/** Reports a request for which a simulated API has no operation. */
|
|
2
2
|
export declare class UnimplementedRouteError extends Error {
|
|
3
|
+
readonly apiName: string;
|
|
3
4
|
readonly method: string;
|
|
4
5
|
readonly pathname: string;
|
|
5
6
|
readonly url: string;
|
|
6
|
-
constructor(request: Request);
|
|
7
|
+
constructor(request: Request, apiName?: string);
|
|
7
8
|
}
|
|
8
9
|
//# sourceMappingURL=unimplemented-route-error.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"unimplemented-route-error.d.ts","sourceRoot":"","sources":["../src/unimplemented-route-error.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,qBAAa,uBAAwB,SAAQ,KAAK;IAChD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAErB,YAAY,OAAO,EAAE,OAAO,
|
|
1
|
+
{"version":3,"file":"unimplemented-route-error.d.ts","sourceRoot":"","sources":["../src/unimplemented-route-error.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,qBAAa,uBAAwB,SAAQ,KAAK;IAChD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAErB,YAAY,OAAO,EAAE,OAAO,EAAE,OAAO,SAAW,EAY/C;CACF"}
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
/** Reports a request for which a simulated API has no operation. */
|
|
2
2
|
export class UnimplementedRouteError extends Error {
|
|
3
|
+
apiName;
|
|
3
4
|
method;
|
|
4
5
|
pathname;
|
|
5
6
|
url;
|
|
6
|
-
constructor(request) {
|
|
7
|
+
constructor(request, apiName = "SimApi") {
|
|
7
8
|
const url = new URL(request.url);
|
|
8
9
|
const method = request.method.toUpperCase();
|
|
9
|
-
super(`${method} ${request.url} reached
|
|
10
|
+
super(`${method} ${request.url} reached ${apiName}, but ${apiName} has no handler for ${method} ${url.pathname}.`);
|
|
10
11
|
this.name = "UnimplementedRouteError";
|
|
12
|
+
this.apiName = apiName;
|
|
11
13
|
this.method = method;
|
|
12
14
|
this.pathname = url.pathname;
|
|
13
15
|
this.url = request.url;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"unimplemented-route-error.js","sourceRoot":"","sources":["../src/unimplemented-route-error.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,MAAM,OAAO,uBAAwB,SAAQ,KAAK;IACvC,MAAM,CAAS;IACf,QAAQ,CAAS;IACjB,GAAG,CAAS;IAErB,YAAY,OAAgB;
|
|
1
|
+
{"version":3,"file":"unimplemented-route-error.js","sourceRoot":"","sources":["../src/unimplemented-route-error.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,MAAM,OAAO,uBAAwB,SAAQ,KAAK;IACvC,OAAO,CAAS;IAChB,MAAM,CAAS;IACf,QAAQ,CAAS;IACjB,GAAG,CAAS;IAErB,YAAY,OAAgB,EAAE,OAAO,GAAG,QAAQ;QAC9C,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACjC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;QAE5C,KAAK,CACH,GAAG,MAAM,IAAI,OAAO,CAAC,GAAG,YAAY,OAAO,SAAS,OAAO,uBAAuB,MAAM,IAAI,GAAG,CAAC,QAAQ,GAAG,CAC5G,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,yBAAyB,CAAC;QACtC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC;QAC7B,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC;IACzB,CAAC;CACF"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* When a queue sends what it is given.
|
|
3
|
+
*
|
|
4
|
+
* `manual` holds every delivery until `flush` asks for it. The test decides
|
|
5
|
+
* when the hook goes out, and that is where a reader looks for the moment it
|
|
6
|
+
* did.
|
|
7
|
+
*
|
|
8
|
+
* `background` starts each delivery as it is enqueued, the way a real provider
|
|
9
|
+
* posts one without being asked. `flush` then waits for the last one to land.
|
|
10
|
+
*/
|
|
11
|
+
export type WebhookDeliveryTiming = "manual" | "background";
|
|
12
|
+
/** Configures one outbound queue. */
|
|
13
|
+
export interface SimWebhooksProps {
|
|
14
|
+
/** When deliveries go out. `manual` when none is given. */
|
|
15
|
+
readonly deliver?: WebhookDeliveryTiming;
|
|
16
|
+
}
|
|
17
|
+
/** One request a simulated service is holding to send. */
|
|
18
|
+
export interface WebhookDelivery {
|
|
19
|
+
readonly body: string;
|
|
20
|
+
readonly headers?: Record<string, string>;
|
|
21
|
+
/** `POST` when none is given. */
|
|
22
|
+
readonly method?: string;
|
|
23
|
+
readonly url: string;
|
|
24
|
+
}
|
|
25
|
+
/** What one endpoint did with one delivery. */
|
|
26
|
+
export type WebhookDeliveryResult = {
|
|
27
|
+
readonly delivered: true;
|
|
28
|
+
readonly delivery: WebhookDelivery;
|
|
29
|
+
readonly error: undefined;
|
|
30
|
+
/** The endpoint's answer, whatever its status. */
|
|
31
|
+
readonly response: Response;
|
|
32
|
+
} | {
|
|
33
|
+
readonly delivered: false;
|
|
34
|
+
readonly delivery: WebhookDelivery;
|
|
35
|
+
/** What stopped the request from arriving. */
|
|
36
|
+
readonly error: unknown;
|
|
37
|
+
readonly response: undefined;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* A queue of requests a simulated service sends outwards.
|
|
41
|
+
*
|
|
42
|
+
* The direction every other part of Simnaril lacks. A service that calls back
|
|
43
|
+
* into the application under test, the way Stripe posts an event or GitHub
|
|
44
|
+
* posts a push, holds the call here until a test asks for it to go.
|
|
45
|
+
*
|
|
46
|
+
* ```ts
|
|
47
|
+
* stripe.pay(sessionId);
|
|
48
|
+
* const [result] = await sim.webhooks.flush();
|
|
49
|
+
* ```
|
|
50
|
+
*
|
|
51
|
+
* Delivery is an ordinary `fetch`, so a live `SimEnvironment` routes it by
|
|
52
|
+
* origin like any other request. Registering the application's own origin as a
|
|
53
|
+
* `SimService` keeps the round trip inside the process. An origin no simulation
|
|
54
|
+
* claims never arrives, and comes back as a result whose `error` is
|
|
55
|
+
* `UnclaimedOriginError`.
|
|
56
|
+
*
|
|
57
|
+
* Holding the queue rather than sending on the spot is what makes a test
|
|
58
|
+
* readable. The arrangement, the delivery and the assertion are three lines the
|
|
59
|
+
* reader can see in order. A test that wants the same hook delivered twice
|
|
60
|
+
* enqueues it again, because `flush` empties the queue as it goes.
|
|
61
|
+
*
|
|
62
|
+
* Signing belongs to the service. Stripe's own SDK has
|
|
63
|
+
* `webhooks.generateTestHeaderString`, and a signature built anywhere else
|
|
64
|
+
* would be a second implementation to keep in step with the first.
|
|
65
|
+
*/
|
|
66
|
+
export declare class SimWebhooks {
|
|
67
|
+
#private;
|
|
68
|
+
constructor(props?: SimWebhooksProps);
|
|
69
|
+
/**
|
|
70
|
+
* Holds one request, or starts it, depending on when this queue delivers.
|
|
71
|
+
*
|
|
72
|
+
* A background delivery goes out behind the one before it. Deliveries are in
|
|
73
|
+
* order in both timings, and the timing decides when rather than what.
|
|
74
|
+
*/
|
|
75
|
+
enqueue(delivery: WebhookDelivery): void;
|
|
76
|
+
/**
|
|
77
|
+
* What is waiting for the next flush, in the order it was enqueued.
|
|
78
|
+
*
|
|
79
|
+
* Always empty on a background queue, where nothing waits.
|
|
80
|
+
*/
|
|
81
|
+
get pending(): readonly WebhookDelivery[];
|
|
82
|
+
/**
|
|
83
|
+
* Answers once everything enqueued so far has been delivered, with what each
|
|
84
|
+
* endpoint did.
|
|
85
|
+
*
|
|
86
|
+
* On the default queue this is what sends them. On a background queue they
|
|
87
|
+
* are already going, and this waits for the last one to land, the way
|
|
88
|
+
* Yulin's `backgroundTasksComplete()` does for a simulated AWS account.
|
|
89
|
+
*
|
|
90
|
+
* A result carries a `response` or an `error`, and the two mean different
|
|
91
|
+
* things. An endpoint that answered, including one answering 400 or 500, is
|
|
92
|
+
* a `response` to read the status off. `error` is for a delivery that never
|
|
93
|
+
* arrived at all, such as an unreachable host or an origin no simulation
|
|
94
|
+
* claims. A simulation exists to reproduce both, so neither throws.
|
|
95
|
+
*/
|
|
96
|
+
flush(): Promise<WebhookDeliveryResult[]>;
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=webhooks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhooks.d.ts","sourceRoot":"","sources":["../src/webhooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,MAAM,MAAM,qBAAqB,GAAG,QAAQ,GAAG,YAAY,CAAC;AAE5D,qCAAqC;AACrC,MAAM,WAAW,gBAAgB;IAC/B,2DAA2D;IAC3D,QAAQ,CAAC,OAAO,CAAC,EAAE,qBAAqB,CAAC;CAC1C;AAED,0DAA0D;AAC1D,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,iCAAiC;IACjC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACtB;AAED,+CAA+C;AAC/C,MAAM,MAAM,qBAAqB,GAC7B;IACE,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC;IACnC,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;IAC1B,kDAAkD;IAClD,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;CAC7B,GACD;IACE,QAAQ,CAAC,SAAS,EAAE,KAAK,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC;IACnC,8CAA8C;IAC9C,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC;CAC9B,CAAC;AAEN;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,qBAAa,WAAW;;IAOtB,YAAY,KAAK,GAAE,gBAAqB,EAEvC;IAED;;;;;OAKG;IACH,OAAO,CAAC,QAAQ,EAAE,eAAe,GAAG,IAAI,CAWvC;IAED;;;;OAIG;IACH,IAAI,OAAO,IAAI,SAAS,eAAe,EAAE,CAExC;IAED;;;;;;;;;;;;;OAaG;IACG,KAAK,IAAI,OAAO,CAAC,qBAAqB,EAAE,CAAC,CAE9C;CA6CF"}
|
package/dist/webhooks.js
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A queue of requests a simulated service sends outwards.
|
|
3
|
+
*
|
|
4
|
+
* The direction every other part of Simnaril lacks. A service that calls back
|
|
5
|
+
* into the application under test, the way Stripe posts an event or GitHub
|
|
6
|
+
* posts a push, holds the call here until a test asks for it to go.
|
|
7
|
+
*
|
|
8
|
+
* ```ts
|
|
9
|
+
* stripe.pay(sessionId);
|
|
10
|
+
* const [result] = await sim.webhooks.flush();
|
|
11
|
+
* ```
|
|
12
|
+
*
|
|
13
|
+
* Delivery is an ordinary `fetch`, so a live `SimEnvironment` routes it by
|
|
14
|
+
* origin like any other request. Registering the application's own origin as a
|
|
15
|
+
* `SimService` keeps the round trip inside the process. An origin no simulation
|
|
16
|
+
* claims never arrives, and comes back as a result whose `error` is
|
|
17
|
+
* `UnclaimedOriginError`.
|
|
18
|
+
*
|
|
19
|
+
* Holding the queue rather than sending on the spot is what makes a test
|
|
20
|
+
* readable. The arrangement, the delivery and the assertion are three lines the
|
|
21
|
+
* reader can see in order. A test that wants the same hook delivered twice
|
|
22
|
+
* enqueues it again, because `flush` empties the queue as it goes.
|
|
23
|
+
*
|
|
24
|
+
* Signing belongs to the service. Stripe's own SDK has
|
|
25
|
+
* `webhooks.generateTestHeaderString`, and a signature built anywhere else
|
|
26
|
+
* would be a second implementation to keep in step with the first.
|
|
27
|
+
*/
|
|
28
|
+
export class SimWebhooks {
|
|
29
|
+
#queued = [];
|
|
30
|
+
#sending = [];
|
|
31
|
+
#background;
|
|
32
|
+
/** The delivery each new background one queues up behind. */
|
|
33
|
+
#last = Promise.resolve();
|
|
34
|
+
constructor(props = {}) {
|
|
35
|
+
this.#background = props.deliver === "background";
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Holds one request, or starts it, depending on when this queue delivers.
|
|
39
|
+
*
|
|
40
|
+
* A background delivery goes out behind the one before it. Deliveries are in
|
|
41
|
+
* order in both timings, and the timing decides when rather than what.
|
|
42
|
+
*/
|
|
43
|
+
enqueue(delivery) {
|
|
44
|
+
if (!this.#background) {
|
|
45
|
+
this.#queued.push(delivery);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const sending = this.#last.then(() => send(delivery));
|
|
49
|
+
this.#last = sending;
|
|
50
|
+
this.#sending.push(sending);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* What is waiting for the next flush, in the order it was enqueued.
|
|
54
|
+
*
|
|
55
|
+
* Always empty on a background queue, where nothing waits.
|
|
56
|
+
*/
|
|
57
|
+
get pending() {
|
|
58
|
+
return [...this.#queued];
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Answers once everything enqueued so far has been delivered, with what each
|
|
62
|
+
* endpoint did.
|
|
63
|
+
*
|
|
64
|
+
* On the default queue this is what sends them. On a background queue they
|
|
65
|
+
* are already going, and this waits for the last one to land, the way
|
|
66
|
+
* Yulin's `backgroundTasksComplete()` does for a simulated AWS account.
|
|
67
|
+
*
|
|
68
|
+
* A result carries a `response` or an `error`, and the two mean different
|
|
69
|
+
* things. An endpoint that answered, including one answering 400 or 500, is
|
|
70
|
+
* a `response` to read the status off. `error` is for a delivery that never
|
|
71
|
+
* arrived at all, such as an unreachable host or an origin no simulation
|
|
72
|
+
* claims. A simulation exists to reproduce both, so neither throws.
|
|
73
|
+
*/
|
|
74
|
+
async flush() {
|
|
75
|
+
return this.#background ? this.#settled() : this.#sendQueued();
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Sends what is waiting.
|
|
79
|
+
*
|
|
80
|
+
* The queue is emptied before the first request goes out, so a receiver that
|
|
81
|
+
* enqueues more work leaves it for the next flush in place of extending this
|
|
82
|
+
* one. Holding a queue is for deciding when deliveries go, and a flush that
|
|
83
|
+
* chased its own tail would take that decision back.
|
|
84
|
+
*/
|
|
85
|
+
async #sendQueued() {
|
|
86
|
+
const sending = this.#queued.splice(0);
|
|
87
|
+
const results = [];
|
|
88
|
+
/*
|
|
89
|
+
* One at a time on purpose. A receiver's state changes in the order the
|
|
90
|
+
* deliveries were enqueued, and `Promise.all` would hand that order to the
|
|
91
|
+
* event loop.
|
|
92
|
+
*/
|
|
93
|
+
for (const delivery of sending) {
|
|
94
|
+
// oxlint-disable-next-line no-await-in-loop -- see above
|
|
95
|
+
results.push(await send(delivery));
|
|
96
|
+
}
|
|
97
|
+
return results;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Waits for the background to go quiet.
|
|
101
|
+
*
|
|
102
|
+
* A delivery a receiver enqueues while this is waiting has already started,
|
|
103
|
+
* so it is waited for too. That is the difference from a flush, and it is
|
|
104
|
+
* the point of a background queue: the test asks once, and everything the
|
|
105
|
+
* hooks set off has finished by the time it answers.
|
|
106
|
+
*/
|
|
107
|
+
async #settled() {
|
|
108
|
+
const results = [];
|
|
109
|
+
while (this.#sending.length > 0) {
|
|
110
|
+
// oxlint-disable-next-line no-await-in-loop -- each round may add more
|
|
111
|
+
results.push(...(await Promise.all(this.#sending.splice(0))));
|
|
112
|
+
}
|
|
113
|
+
return results;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
async function send(delivery) {
|
|
117
|
+
try {
|
|
118
|
+
const response = await fetch(delivery.url, {
|
|
119
|
+
body: delivery.body,
|
|
120
|
+
headers: delivery.headers ?? {},
|
|
121
|
+
method: delivery.method ?? "POST",
|
|
122
|
+
});
|
|
123
|
+
return { delivered: true, delivery, error: undefined, response };
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
return { delivered: false, delivery, error, response: undefined };
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=webhooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhooks.js","sourceRoot":"","sources":["../src/webhooks.ts"],"names":[],"mappings":"AA4CA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,OAAO,WAAW;IACb,OAAO,GAAsB,EAAE,CAAC;IAChC,QAAQ,GAAqC,EAAE,CAAC;IAChD,WAAW,CAAU;IAC9B,6DAA6D;IAC7D,KAAK,GAAqB,OAAO,CAAC,OAAO,EAAE,CAAC;IAE5C,YAAY,KAAK,GAAqB,EAAE;QACtC,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC,OAAO,KAAK,YAAY,CAAC;IACpD,CAAC;IAED;;;;;OAKG;IACH,OAAO,CAAC,QAAyB;QAC/B,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YACtB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAE5B,OAAO;QACT,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;QAEtD,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC;QACrB,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC9B,CAAC;IAED;;;;OAIG;IACH,IAAI,OAAO;QACT,OAAO,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC;IAC3B,CAAC;IAED;;;;;;;;;;;;;OAaG;IACH,KAAK,CAAC,KAAK;QACT,OAAO,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;IACjE,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,WAAW;QACf,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACvC,MAAM,OAAO,GAA4B,EAAE,CAAC;QAE5C;;;;WAIG;QACH,KAAK,MAAM,QAAQ,IAAI,OAAO,EAAE,CAAC;YAC/B,yDAAyD;YACzD,OAAO,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;QACrC,CAAC;QAED,OAAO,OAAO,CAAC;IACjB,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,QAAQ;QACZ,MAAM,OAAO,GAA4B,EAAE,CAAC;QAE5C,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAChC,uEAAuE;YACvE,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAChE,CAAC;QAED,OAAO,OAAO,CAAC;IACjB,CAAC;CACF;AAED,KAAK,UAAU,IAAI,CAAC,QAAyB;IAC3C,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,QAAQ,CAAC,GAAG,EAAE;YACzC,IAAI,EAAE,QAAQ,CAAC,IAAI;YACnB,OAAO,EAAE,QAAQ,CAAC,OAAO,IAAI,EAAE;YAC/B,MAAM,EAAE,QAAQ,CAAC,MAAM,IAAI,MAAM;SAClC,CAAC,CAAC;QAEH,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC;IACnE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;IACpE,CAAC;AACH,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kensio/simnaril",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "In-process simulation of third-party HTTP services",
|
|
5
5
|
"repository": "https://github.com/KensioSoftware/simnaril",
|
|
6
6
|
"bugs": "https://github.com/KensioSoftware/simnaril/issues",
|
|
7
|
+
"homepage": "https://simnaril.dev",
|
|
7
8
|
"main": "./dist/index.js",
|
|
8
9
|
"types": "./dist/index.d.ts",
|
|
9
10
|
"exports": {
|