From ef6dd3d08757dc43b1e8b92f2f634fd00edac460 Mon Sep 17 00:00:00 2001 From: Ed Ball Date: Mon, 10 Oct 2022 15:26:52 -0700 Subject: [PATCH 1/7] Fix include path. --- example/ts/tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/ts/tsconfig.json b/example/ts/tsconfig.json index 46433e6..57266dd 100644 --- a/example/ts/tsconfig.json +++ b/example/ts/tsconfig.json @@ -9,7 +9,7 @@ "lib": [ "ES2019" ] }, "include": [ - "*.ts", + "src/*.ts", "test/*.ts" ] } From 8611142c989468375952f2e850583209a4179077 Mon Sep 17 00:00:00 2001 From: Ed Ball Date: Tue, 1 Jun 2021 08:55:38 -0700 Subject: [PATCH 2/7] Generate code for conformance API. --- conformance/ConformanceApi.fsd | 407 +++++++++++ conformance/ConformanceTests.json | 787 ++++++++++++++++++++++ conformance/ts/src/conformanceApi.ts | 555 +++++++++++++++ conformance/ts/src/conformanceApiTypes.ts | 461 +++++++++++++ dotnet-tools.json | 6 + tools/Build/Build.cs | 4 + 6 files changed, 2220 insertions(+) create mode 100644 conformance/ConformanceApi.fsd create mode 100644 conformance/ConformanceTests.json create mode 100644 conformance/ts/src/conformanceApi.ts create mode 100644 conformance/ts/src/conformanceApiTypes.ts diff --git a/conformance/ConformanceApi.fsd b/conformance/ConformanceApi.fsd new file mode 100644 index 0000000..cc2832d --- /dev/null +++ b/conformance/ConformanceApi.fsd @@ -0,0 +1,407 @@ +/// API for a Facility test server. +[csharp(namespace: Facility.ConformanceApi)] +service ConformanceApi +{ + /// Gets API information. + [http(method: get, path: "/")] + method getApiInfo + { + }: + { + /// The name of the service. + service: string; + + /// The version of the service. + version: string; + } + + /// Gets widgets. + [http(method: GET, path: "/widgets")] + [tag(name: widgets)] + method getWidgets + { + /// The query. + [http(name: "q")] + query: string; + }: + { + /// The widgets. + widgets: Widget[]; + } + + /// Creates a new widget. + [http(method: POST, path: "/widgets")] + [tag(name: widgets)] + method createWidget + { + /// The widget to create. + [http(from: body)] + widget: Widget; + }: + { + /// The created widget. + [http(from: body, code: 201)] + widget: Widget; + + /// The URL of the created widget. + [http(from: header, name: Location)] + url: string; + + /// The ETag of the created widget. + [http(from: header)] + eTag: string; + } + + /// Gets the specified widget. + [http(method: GET, path: "/widgets/{id}")] + [tag(name: widgets)] + method getWidget + { + /// The widget ID. + [required, validate(value: 0..)] + id: int32; + + /// Don't get the widget if it has this ETag. + [http(from: header, name: If-None-Match)] + ifNotETag: string; + }: + { + /// The requested widget. + [http(from: body)] + widget: Widget; + + /// The ETag of the widget. + [http(from: header)] + eTag: string; + + /// The widget still has the specified ETag. + [http(from: body, code: 304)] + notModified: boolean; + } + + /// Deletes the specified widget. + [http(method: DELETE, path: "/widgets/{id}", code: 204)] + [tag(name: widgets)] + method deleteWidget + { + /// The widget ID. + [validate(value: 0..)] + id: int32; + + /// Don't delete the widget unless it has this ETag. + [http(from: header, name: If-Match)] + ifETag: string; + }: + { + /// The widget was not found. + [http(from: body, code: 404)] + notFound: boolean; + + /// The widget no longer has the specified ETag. + [http(from: body, code: 409)] + conflict: boolean; + } + + /// Gets the specified widgets. + [http(method: POST, path: "/widgets/get")] + [tag(name: widgets)] + method getWidgetBatch + { + /// The IDs of the widgets to return. + [http(from: body), required, validate(count: ..10)] + ids: int32[]; + }: + { + /// The widget results. + [http(from: body)] + results: result[]; + } + + /// A widget. + [tag(name: widgets)] + data Widget + { + /// A unique identifier for the widget. + [validate(value: 0..)] + id: int32; + + /// The name of the widget. + [required, validate(regex: "^[_a-zA-Z0-9]+$", length: 1..10)] + name: string; + } + + method mirrorFields + { + field: Any; + matrix: double[][][]; + }: + { + field: Any; + matrix: double[][][]; + } + + [http(method: GET)] + method checkQuery + { + string: string; + boolean: boolean; + double: double; + int32: int32; + int64: int64; + decimal: decimal; + + [validate] + enum: Answer; + }: + { + } + + [http(method: GET, path: "/checkPath/{string}/{boolean}/{double}/{int32}/{int64}/{decimal}/{enum}")] + method checkPath + { + string: string; + boolean: boolean; + double: double; + int32: int32; + int64: int64; + decimal: decimal; + + [validate] + enum: Answer; + }: + { + } + + [http(method: GET)] + method mirrorHeaders + { + [http(from: header)] string: string; + [http(from: header)] boolean: boolean; + [http(from: header)] double: double; + [http(from: header)] int32: int32; + [http(from: header)] int64: int64; + [http(from: header)] decimal: decimal; + [http(from: header)] enum: Answer; + }: + { + [http(from: header)] string: string; + [http(from: header)] boolean: boolean; + [http(from: header)] double: double; + [http(from: header)] int32: int32; + [http(from: header)] int64: int64; + [http(from: header)] decimal: decimal; + [http(from: header)] enum: Answer; + } + + [http(path: "/mixed/{path}")] + method mixed + { + [http(from: path)] path: string; + [http(from: query)] query: string; + [http(from: header)] header: string; + [http(from: normal)] normal: string; + }: + { + [http(from: header)] header: string; + [http(from: normal)] normal: string; + [http(from: body, code: 202)] body: object; + [http(from: body, code: 204)] empty: boolean; + } + + method required + { + [http(from: query), required] query: string; + [required] normal: string; + widget: Widget; + widgets: Widget[]; + widgetMatrix: Widget[][]; + widgetResult: result; + widgetResults: result[]; + widgetMap: map; + hasWidget: HasWidget; + [validate(count: 2)] point: double[]; + }: + { + [required] normal: string; + } + + method mirrorBytes + { + [http(from: body)] content: bytes; + [http(from: header, name: Content-Type)] type: string; + }: + { + [http(from: body)] content: bytes; + [http(from: header, name: Content-Type)] type: string; + } + + method mirrorText + { + [http(from: body)] content: string; + [http(from: header, name: Content-Type)] type: string; + }: + { + [http(from: body)] content: string; + [http(from: header, name: Content-Type)] type: string; + } + + method bodyTypes + { + [http(from: body, type: "text/x-input")] content: string; + }: + { + [http(from: body, type: "application/x-output")] content: bytes; + } + + data Any + { + string: string; + boolean: boolean; + double: double; + int32: int32; + int64: int64; + decimal: decimal; + bytes: bytes; + object: object; + error: error; + data: Any; + enum: Answer; + array: AnyArray; + map: AnyMap; + result: AnyResult; + } + + data AnyArray + { + string: string[]; + boolean: boolean[]; + double: double[]; + int32: int32[]; + int64: int64[]; + decimal: decimal[]; + bytes: bytes[]; + object: object[]; + error: error[]; + data: Any[]; + enum: Answer[]; + array: int32[][]; + map: map[]; + result: result[]; + } + + data AnyMap + { + string: map; + boolean: map; + double: map; + int32: map; + int64: map; + decimal: map; + bytes: map; + object: map; + error: map; + data: map; + enum: map; + array: map; + map: map>; + result: map>; + } + + data AnyResult + { + string: result; + boolean: result; + double: result; + int32: result; + int64: result; + decimal: result; + bytes: result; + object: result; + error: result; + data: result; + enum: result; + array: result; + map: result>; + result: result>; + } + + data HasWidget + { + widget: Widget; + } + + /// One of three answers. + enum Answer + { + /// Affirmative. + yes, + + /// Negative. + no, + + /// Unknown. + maybe, + } + + /// Custom errors. + errors ApiErrors + { + /// The user is not an administrator. + [http(code: 403)] + NotAdmin, + + /// I'm "too" 😄! + TooHappy, + } +} + +# ConformanceApi + +The Facility Conformance API allows clients and servers in various languages and platforms +to test one another for conformance. + +## Conformance Web Server + +A conformance web server should leverage a Facility server code generator and/or library. +On each request, the web server should determine the conformance test being executed by +looking at the `FacilityTest` HTTP header. The web server should find the test data for the +specified test, ensure that the request matches, and return the specified response or error. + +The web server should return `InvalidRequest` if: + +* The `FacilityTest` HTTP header is missing. +* The API method called does not match the method specified by the test data. +* The actual request is not an exact match of the request specified by the test data. +* The server fails to create a response or error that is an exact match of the specified + response or error. + +When checking for an exact match, the only thing that can be ignored is the order of the +properties of an object. There should be no missing properties, no extra properties, and +all property names and other strings should use the same letter case. + +## Conformance Tester + +A conformance tester should leverage a Facility client generator and/or library. The tester +should enumerate through all of the conformance tests. For each test, it should create a +client that adds the `FacilityTest` HTTP header to any request. It should use that client to +call the method specified by the test data with the request specified by the test data. The +tester should verify that the result matches the response or error specified by the test data. + +The tester should report a test failure if: + +* The method specified by the test data is missing. +* The client fails to create a request that is an exact match of the specified request. +* The client receives a response or error that is not an exact match of the specified + response or error. + +# getApiInfo + +The name of the service should be `ConformanceApi`. + +# Widget + +Widgets are useful for conformance tests. + +# Answer + +Use `maybe` if you aren't sure. diff --git a/conformance/ConformanceTests.json b/conformance/ConformanceTests.json new file mode 100644 index 0000000..6cd60a6 --- /dev/null +++ b/conformance/ConformanceTests.json @@ -0,0 +1,787 @@ +{ + "tests": [ + { + "test": "checkPath", + "method": "checkPath", + "request": { + "string": "string", + "boolean": true, + "double": 0.375, + "int32": 42, + "int64": 12345678910, + "decimal": 1.5, + "enum": "yes" + }, + "response": {} + }, + { + "test": "checkQuery", + "method": "checkQuery", + "request": { + "string": "string", + "boolean": true, + "double": 0.375, + "int32": 42, + "int64": 12345678910, + "decimal": 1.5, + "enum": "yes" + }, + "response": {} + }, + { + "test": "createWidget", + "method": "createWidget", + "request": { + "widget": { + "name": "shiny" + } + }, + "response": { + "widget": { + "id": 1337, + "name": "shiny" + }, + "url": "/widgets/1337", + "eTag": "\"initial\"" + } + }, + { + "test": "createWidgetNoName", + "method": "createWidget", + "request": { + "widget": {} + }, + "error": { + "code": "InvalidRequest", + "message": "'widget' is invalid: 'name' is required." + } + }, + { + "test": "createWidgetNoWidget", + "method": "createWidget", + "request": {}, + "error": { + "code": "InvalidRequest", + "message": "'widget' is required." + } + }, + { + "test": "deleteWidget", + "method": "deleteWidget", + "request": { + "id": 1337 + }, + "response": {} + }, + { + "test": "deleteWidgetConflict", + "method": "deleteWidget", + "request": { + "id": 1337, + "ifETag": "\"not-initial\"" + }, + "response": { + "conflict": true + } + }, + { + "test": "deleteWidgetNotFound", + "method": "deleteWidget", + "request": { + "id": 1338 + }, + "response": { + "notFound": true + } + }, + { + "test": "getApiInfo", + "method": "getApiInfo", + "request": {}, + "response": { + "service": "ConformanceApi", + "version": "0.1.0" + } + }, + { + "test": "getWidget", + "method": "getWidget", + "request": { + "id": 1337 + }, + "response": { + "widget": { + "id": 1337, + "name": "shiny" + }, + "eTag": "\"initial\"" + } + }, + { + "test": "getWidgetBatch", + "method": "getWidgetBatch", + "request": { + "ids": [ + 1337, + 42 + ] + }, + "response": { + "results": [ + { + "value": { + "id": 1337, + "name": "shiny" + } + }, + { + "error": { + "code": "NotFound" + } + } + ] + } + }, + { + "test": "getWidgetNotFound", + "method": "getWidget", + "request": { + "id": 1338 + }, + "error": { + "code": "NotFound", + "message": "The specified widget was not found." + } + }, + { + "test": "getWidgetNotModified", + "method": "getWidget", + "request": { + "id": 1337, + "ifNotETag": "\"initial\"" + }, + "response": { + "notModified": true + } + }, + { + "test": "getWidgets", + "method": "getWidgets", + "request": { + "query": "shiny" + }, + "response": { + "widgets": [ + { + "id": 1337, + "name": "shiny" + }, + { + "id": 7331, + "name": "shinier" + } + ] + } + }, + { + "test": "getWidgetsNone", + "method": "getWidgets", + "request": { + "query": "none" + }, + "response": { + "widgets": [] + } + }, + { + "test": "mirrorArrayFields", + "method": "mirrorFields", + "request": { + "field": { + "array": { + "string": ["string", "\n"], + "boolean": [true, false], + "double": [0.375, -1.5], + "int32": [42, 24], + "int64": [12345678910, -987654321], + "decimal": [1.5, 6.875], + "bytes": ["aGk=", ""], + "object": [{ "": null, " ": [null] }, {}], + "error": [{ "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } }], + "data": [{ "data": { "data": {} } }, {}], + "enum": ["yes", "no"], + "array": [[42, 24], [24, 42]], + "map": [{ "": 42 }, { " ": 24 }], + "result": [{ "value": 42 }, { "error": { "code": "code" } }] + } + }, + "matrix": [[[1.0,0.0,0.0],[0.0,1.0,0.0],[0.0,0.0,1.0]]] + }, + "response": { + "field": { + "array": { + "string": ["string", "\n"], + "boolean": [true, false], + "double": [0.375, -1.5], + "int32": [42, 24], + "int64": [12345678910, -987654321], + "decimal": [1.5, 6.875], + "bytes": ["aGk=", ""], + "object": [{ "": null, " ": [null] }, {}], + "error": [{ "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } }], + "data": [{ "data": { "data": {} } }, {}], + "enum": ["yes", "no"], + "array": [[42, 24], [24, 42]], + "map": [{ "": 42 }, { " ": 24 }], + "result": [{ "value": 42 }, { "error": { "code": "code" } }] + } + }, + "matrix": [[[1.0,0.0,0.0],[0.0,1.0,0.0],[0.0,0.0,1.0]]] + } + }, + { + "test": "mirrorFields", + "method": "mirrorFields", + "request": { + "field": { + "string": "string", + "boolean": true, + "double": 0.375, + "int32": 42, + "int64": 12345678910, + "decimal": 1.5, + "bytes": "aGk=", + "object": { "": null, " ": [null] }, + "error": { "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } }, + "data": { "data": { "data": {} } }, + "enum": "yes" + } + }, + "response": { + "field": { + "string": "string", + "boolean": true, + "double": 0.375, + "int32": 42, + "int64": 12345678910, + "decimal": 1.5, + "bytes": "aGk=", + "object": { "": null, " ": [null] }, + "error": { "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } }, + "data": { "data": { "data": {} } }, + "enum": "yes" + } + } + }, + { + "test": "mirrorHeaders", + "method": "mirrorHeaders", + "request": { + "string": "string", + "boolean": true, + "double": 0.375, + "int32": 42, + "int64": 12345678910, + "decimal": 1.5, + "enum": "yes" + }, + "response": { + "string": "string", + "boolean": true, + "double": 0.375, + "int32": 42, + "int64": 12345678910, + "decimal": 1.5, + "enum": "yes" + } + }, + { + "test": "mirrorHeadersMissing", + "method": "mirrorHeaders", + "request": {}, + "response": {} + }, + { + "test": "mirrorMapFields", + "method": "mirrorFields", + "request": { + "field": { + "map": { + "string": { "": "string", " ": "\n" }, + "boolean": { "": true, " ": false }, + "double": { "": 0.375, " ": -1.5 }, + "int32": { "": 42, " ": 24 }, + "int64": { "": 12345678910, " ": -987654321 }, + "decimal": { "": 1.5, " ": 6.875 }, + "bytes": { "": "aGk=", " ": "" }, + "object": { "": { "": null, " ": [null] }, " ": {} }, + "error": { "": { "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } }, " ": {} }, + "data": { "": { "data": { "data": {} } }, " ": {} }, + "enum": { "": "yes", " ": "no" }, + "array": { "": [42, 24], " ": [24, 42] }, + "map": { "": { "": 42 }, " ": { " ": 24 } }, + "result": { "": { "value": 42 }, " ": { "error": { "code": "24" } } } + } + } + }, + "response": { + "field": { + "map": { + "string": { "": "string", " ": "\n" }, + "boolean": { "": true, " ": false }, + "double": { "": 0.375, " ": -1.5 }, + "int32": { "": 42, " ": 24 }, + "int64": { "": 12345678910, " ": -987654321 }, + "decimal": { "": 1.5, " ": 6.875 }, + "bytes": { "": "aGk=", " ": "" }, + "object": { "": { "": null, " ": [null] }, " ": {} }, + "error": { "": { "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } }, " ": {} }, + "data": { "": { "data": { "data": {} } }, " ": {} }, + "enum": { "": "yes", " ": "no" }, + "array": { "": [42, 24], " ": [24, 42] }, + "map": { "": { "": 42 }, " ": { " ": 24 } }, + "result": { "": { "value": 42 }, " ": { "error": { "code": "24" } } } + } + } + } + }, + { + "test": "mirrorResultErrorFields", + "method": "mirrorFields", + "request": { + "field": { + "result": { + "string": { "error": { "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } } }, + "boolean": { "error": { "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } } }, + "double": { "error": { "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } } }, + "int32": { "error": { "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } } }, + "int64": { "error": { "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } } }, + "decimal": { "error": { "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } } }, + "bytes": { "error": { "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } } }, + "object": { "error": { "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } } }, + "error": { "error": { "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } } }, + "data": { "error": { "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } } }, + "enum": { "error": { "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } } }, + "array": { "error": { "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } } }, + "map": { "error": { "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } } }, + "result": { "error": { "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } } } + } + } + }, + "response": { + "field": { + "result": { + "string": { "error": { "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } } }, + "boolean": { "error": { "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } } }, + "double": { "error": { "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } } }, + "int32": { "error": { "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } } }, + "int64": { "error": { "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } } }, + "decimal": { "error": { "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } } }, + "bytes": { "error": { "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } } }, + "object": { "error": { "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } } }, + "error": { "error": { "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } } }, + "data": { "error": { "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } } }, + "enum": { "error": { "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } } }, + "array": { "error": { "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } } }, + "map": { "error": { "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } } }, + "result": { "error": { "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } } } + } + } + } + }, + { + "test": "mirrorResultValueFields", + "method": "mirrorFields", + "request": { + "field": { + "result": { + "string": { "value": "string" }, + "boolean": { "value": true }, + "double": { "value": 0.375 }, + "int32": { "value": 42 }, + "int64": { "value": 12345678910 }, + "decimal": { "value": 1.5 }, + "bytes": { "value": "aGk=" }, + "object": { "value": { "": null, " ": [null] } }, + "error": { "value": { "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } } }, + "data": { "value": { "data": { "data": {} } } }, + "enum": { "value": "yes" }, + "array": { "value": [42] }, + "map": { "value": { "": 42 } }, + "result": { "value": { "value": 42 } } + } + } + }, + "response": { + "field": { + "result": { + "string": { "value": "string" }, + "boolean": { "value": true }, + "double": { "value": 0.375 }, + "int32": { "value": 42 }, + "int64": { "value": 12345678910 }, + "decimal": { "value": 1.5 }, + "bytes": { "value": "aGk=" }, + "object": { "value": { "": null, " ": [null] } }, + "error": { "value": { "code": "code", "message": "message", "innerError": { "code": "code" }, "details": { "": null, " ": [null] } } }, + "data": { "value": { "data": { "data": {} } } }, + "enum": { "value": "yes" }, + "array": { "value": [42] }, + "map": { "value": { "": 42 } }, + "result": { "value": { "value": 42 } } + } + } + } + }, + { + "test": "mixedOk", + "method": "mixed", + "request": { + "path": "string", + "query": "string", + "header": "string", + "normal": "ok" + }, + "response": { + "header": "string", + "normal": "string" + } + }, + { + "test": "mixedAccepted", + "method": "mixed", + "request": { + "path": "string", + "query": "string", + "header": "string", + "normal": "accepted" + }, + "response": { + "header": "string", + "body": {} + } + }, + { + "test": "mixedNoContent", + "method": "mixed", + "request": { + "path": "string", + "query": "string", + "header": "string", + "normal": "noContent" + }, + "response": { + "header": "string", + "empty": true + } + }, + { + "test": "requiredSuccess", + "method": "required", + "request": { + "query": "string", + "normal": "string" + }, + "response": { + "normal": "string" + } + }, + { + "test": "requiredNoQuery", + "method": "required", + "request": { + "normal": "string" + }, + "error": { + "code": "InvalidRequest", + "message": "'query' is required." + } + }, + { + "test": "requiredNoNormal", + "method": "required", + "request": { + "query": "string" + }, + "error": { + "code": "InvalidRequest", + "message": "'normal' is required." + } + }, + { + "test": "requiredSuccessWidgets", + "method": "required", + "request": { + "query": "string", + "normal": "string", + "widget": { "name": "string" }, + "widgets": [{ "name": "string" }], + "widgetMatrix": [[{ "name": "string" }]], + "widgetResult": { "value": { "name": "string" } }, + "widgetResults": [{ "value": { "name": "string" } }], + "widgetMap": { "name": { "name": "string" } }, + "hasWidget": { "widget": { "name": "string" } }, + "point": [ 0.0, 1.0 ] + }, + "response": { + "normal": "string" + } + }, + { + "test": "requiredNoNameWidget", + "method": "required", + "request": { + "query": "string", + "normal": "string", + "widget": {} + }, + "error": { + "code": "InvalidRequest", + "message": "'widget' is invalid: 'name' is required." + } + }, + { + "test": "requiredNoNameWidgets", + "method": "required", + "request": { + "query": "string", + "normal": "string", + "widgets": [{ "name": "string" }, {}] + }, + "error": { + "code": "InvalidRequest", + "message": "'widgets[1]' is invalid: 'name' is required." + } + }, + { + "test": "requiredNoNameWidgetMatrix", + "method": "required", + "request": { + "query": "string", + "normal": "string", + "widgetMatrix": [[{}]] + }, + "error": { + "code": "InvalidRequest", + "message": "'widgetMatrix[0][0]' is invalid: 'name' is required." + } + }, + { + "test": "requiredNoNameWidgetResult", + "method": "required", + "request": { + "query": "string", + "normal": "string", + "widgetResult": { "value": {} } + }, + "error": { + "code": "InvalidRequest", + "message": "'widgetResult' is invalid: 'name' is required." + } + }, + { + "test": "requiredNoNameWidgetResults", + "method": "required", + "request": { + "query": "string", + "normal": "string", + "widgetResults": [{ "value": {} }] + }, + "error": { + "code": "InvalidRequest", + "message": "'widgetResults[0]' is invalid: 'name' is required." + } + }, + { + "test": "requiredNoNameWidgetMap", + "method": "required", + "request": { + "query": "string", + "normal": "string", + "widgetMap": { "key": {} } + }, + "error": { + "code": "InvalidRequest", + "message": "'widgetMap.key' is invalid: 'name' is required." + } + }, + { + "test": "requiredNoNameHasWidget", + "method": "required", + "request": { + "query": "string", + "normal": "string", + "hasWidget": { "widget": {} } + }, + "error": { + "code": "InvalidRequest", + "message": "'hasWidget' is invalid: 'widget' is invalid: 'name' is required." + } + }, + { + "test": "mirrorBytes", + "method": "mirrorBytes", + "request": { + "content": "NDI=", + "type": "application/binary" + }, + "response": { + "content": "NDI=", + "type": "application/binary" + } + }, + { + "test": "mirrorText", + "method": "mirrorText", + "request": { + "content": "forty-two", + "type": "text/number" + }, + "response": { + "content": "forty-two", + "type": "text/number" + } + }, + { + "test": "bodyTypes", + "method": "bodyTypes", + "request": { + "content": "forty-two" + }, + "response": { + "content": "NDI=" + } + }, + { + "test": "checkPathHttpPathCase", + "method": "checkPath", + "httpRequest": { + "method": "GET", + "path": "/CHECKPATH/pathCase/true/1.5/3/4/0.625/yes" + }, + "request": { + "string": "pathCase", + "boolean": true, + "double": 1.5, + "int32": 3, + "int64": 4, + "decimal": 0.625, + "enum": "yes" + }, + "response": {} + }, + { + "test": "checkPathHttpBooleanCase", + "method": "checkPath", + "httpRequest": { + "method": "GET", + "path": "/checkPath/booleanCase/TRUE/1.5/3/4/0.625/yes" + }, + "request": { + "string": "booleanCase", + "boolean": true, + "double": 1.5, + "int32": 3, + "int64": 4, + "decimal": 0.625, + "enum": "yes" + }, + "response": {} + }, + { + "test": "checkQueryHttpPathCase", + "method": "checkQuery", + "httpRequest": { + "method": "GET", + "path": "/CHECKQUERY?string=pathCase&boolean=true&double=1.5&int32=3&int64=4&decimal=0.625&enum=yes" + }, + "request": { + "string": "pathCase", + "boolean": true, + "double": 1.5, + "int32": 3, + "int64": 4, + "decimal": 0.625, + "enum": "yes" + }, + "response": {} + }, + { + "test": "checkQueryHttpQueryCase", + "method": "checkQuery", + "httpRequest": { + "method": "GET", + "path": "/checkQuery?STRING=queryCase&boolean=true&double=1.5&int32=3&int64=4&decimal=0.625&enum=yes" + }, + "request": { + "string": "queryCase", + "boolean": true, + "double": 1.5, + "int32": 3, + "int64": 4, + "decimal": 0.625, + "enum": "yes" + }, + "response": {} + }, + { + "test": "getWidgetInvalidId", + "method": "getWidget", + "request": { + "id": -1 + }, + "error": { + "code": "InvalidRequest", + "message": "'id' is invalid: Must be at least 0." + } + }, + { + "test": "createWidgetInvalidName", + "method": "createWidget", + "request": { + "widget": { + "name": "*order" + } + }, + "error": { + "code": "InvalidRequest", + "message": "'widget' is invalid: 'name' is invalid: Must match regular expression: ^[_a-zA-Z0-9]+$" + } + }, + { + "test": "getWidgetBatchTooLarge", + "method": "getWidgetBatch", + "request": { + "ids": [0, 1, 2, 31, 231, 10011, 10213, 20014, 120001, 120003, 120032, 1000000001] + }, + "error": { + "code": "InvalidRequest", + "message": "'ids' is invalid: Count must be at most 10." + } + }, + { + "test": "createWidgetNameTooLong", + "method": "createWidget", + "request": { + "widget": { + "name": "FacilityServiceWidget" + } + }, + "error": { + "code": "InvalidRequest", + "message": "'widget' is invalid: 'name' is invalid: Length must be at most 10." + } + }, + { + "test": "checkQueryInvalidAnswer", + "method": "checkQuery", + "request": { + "enum": "negative" + }, + "error": { + "code": "InvalidRequest", + "message": "'enum' is invalid: Must be an expected enum value." + } + } + ] +} diff --git a/conformance/ts/src/conformanceApi.ts b/conformance/ts/src/conformanceApi.ts new file mode 100644 index 0000000..392a7d8 --- /dev/null +++ b/conformance/ts/src/conformanceApi.ts @@ -0,0 +1,555 @@ +// DO NOT EDIT: generated by fsdgenjs +/* eslint-disable */ + +import { HttpClientUtility, IServiceResult, IHttpClientOptions } from 'facility-core'; +import { IConformanceApi, IGetApiInfoRequest, IGetApiInfoResponse, IGetWidgetsRequest, IGetWidgetsResponse, ICreateWidgetRequest, ICreateWidgetResponse, IGetWidgetRequest, IGetWidgetResponse, IDeleteWidgetRequest, IDeleteWidgetResponse, IGetWidgetBatchRequest, IGetWidgetBatchResponse, IMirrorFieldsRequest, IMirrorFieldsResponse, ICheckQueryRequest, ICheckQueryResponse, ICheckPathRequest, ICheckPathResponse, IMirrorHeadersRequest, IMirrorHeadersResponse, IMixedRequest, IMixedResponse, IRequiredRequest, IRequiredResponse, IMirrorBytesRequest, IMirrorBytesResponse, IMirrorTextRequest, IMirrorTextResponse, IBodyTypesRequest, IBodyTypesResponse, IWidget, IAny, IAnyArray, IAnyMap, IAnyResult, IHasWidget, Answer } from './conformanceApiTypes'; +export * from './conformanceApiTypes'; + +/** Provides access to ConformanceApi over HTTP via fetch. */ +export function createHttpClient({ fetch, baseUri }: IHttpClientOptions): IConformanceApi { + return new ConformanceApiHttpClient(fetch, baseUri); +} + +const { fetchResponse, createResponseError, createRequiredRequestFieldError } = HttpClientUtility; +type IFetch = HttpClientUtility.IFetch; +type IFetchRequest = HttpClientUtility.IFetchRequest; + +class ConformanceApiHttpClient implements IConformanceApi { + constructor(fetch: IFetch, baseUri?: string) { + if (typeof fetch !== 'function') { + throw new TypeError('fetch must be a function.'); + } + if (typeof baseUri === 'undefined') { + baseUri = ''; + } + if (/[^\/]$/.test(baseUri)) { + baseUri += '/'; + } + this._fetch = fetch; + this._baseUri = baseUri; + } + + /** Gets API information. */ + public getApiInfo(request: IGetApiInfoRequest, context?: unknown): Promise> { + const uri = ''; + const fetchRequest: IFetchRequest = { + method: 'GET', + }; + return fetchResponse(this._fetch, this._baseUri + uri, fetchRequest, context) + .then(result => { + const status = result.response.status; + let value: IGetApiInfoResponse | null = null; + if (status === 200) { + if (result.json) { + value = result.json as IGetApiInfoResponse | null; + } + } + if (!value) { + return createResponseError(status, result.json) as IServiceResult; + } + return { value: value }; + }); + } + + /** Gets widgets. */ + public getWidgets(request: IGetWidgetsRequest, context?: unknown): Promise> { + let uri = 'widgets'; + const query: string[] = []; + request.query == null || query.push('q=' + encodeURIComponent(request.query)); + if (query.length) { + uri = uri + '?' + query.join('&'); + } + const fetchRequest: IFetchRequest = { + method: 'GET', + }; + return fetchResponse(this._fetch, this._baseUri + uri, fetchRequest, context) + .then(result => { + const status = result.response.status; + let value: IGetWidgetsResponse | null = null; + if (status === 200) { + if (result.json) { + value = result.json as IGetWidgetsResponse | null; + } + } + if (!value) { + return createResponseError(status, result.json) as IServiceResult; + } + return { value: value }; + }); + } + + /** Creates a new widget. */ + public createWidget(request: ICreateWidgetRequest, context?: unknown): Promise> { + const uri = 'widgets'; + const fetchRequest: IFetchRequest = { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(request.widget) + }; + return fetchResponse(this._fetch, this._baseUri + uri, fetchRequest, context) + .then(result => { + const status = result.response.status; + let value: ICreateWidgetResponse | null = null; + if (status === 201) { + if (result.json) { + value = { widget: result.json } as ICreateWidgetResponse; + } + } + if (!value) { + return createResponseError(status, result.json) as IServiceResult; + } + let headerValue: string | null | undefined; + headerValue = result.response.headers.get('Location'); + if (headerValue != null) { + value.url = headerValue; + } + headerValue = result.response.headers.get('eTag'); + if (headerValue != null) { + value.eTag = headerValue; + } + return { value: value }; + }); + } + + /** Gets the specified widget. */ + public getWidget(request: IGetWidgetRequest, context?: unknown): Promise> { + const uriPartId = request.id != null && request.id.toString(); + if (!uriPartId) { + return Promise.resolve(createRequiredRequestFieldError('id')); + } + const uri = `widgets/${uriPartId}`; + const fetchRequest: IFetchRequest = { + method: 'GET', + headers: {}, + }; + if (request.ifNotETag != null) { + fetchRequest.headers!['If-None-Match'] = request.ifNotETag; + } + return fetchResponse(this._fetch, this._baseUri + uri, fetchRequest, context) + .then(result => { + const status = result.response.status; + let value: IGetWidgetResponse | null = null; + if (status === 200) { + if (result.json) { + value = { widget: result.json } as IGetWidgetResponse; + } + } + else if (status === 304) { + value = { notModified: true }; + } + if (!value) { + return createResponseError(status, result.json) as IServiceResult; + } + let headerValue: string | null | undefined; + headerValue = result.response.headers.get('eTag'); + if (headerValue != null) { + value.eTag = headerValue; + } + return { value: value }; + }); + } + + /** Deletes the specified widget. */ + public deleteWidget(request: IDeleteWidgetRequest, context?: unknown): Promise> { + const uriPartId = request.id != null && request.id.toString(); + if (!uriPartId) { + return Promise.resolve(createRequiredRequestFieldError('id')); + } + const uri = `widgets/${uriPartId}`; + const fetchRequest: IFetchRequest = { + method: 'DELETE', + headers: {}, + }; + if (request.ifETag != null) { + fetchRequest.headers!['If-Match'] = request.ifETag; + } + return fetchResponse(this._fetch, this._baseUri + uri, fetchRequest, context) + .then(result => { + const status = result.response.status; + let value: IDeleteWidgetResponse | null = null; + if (status === 204) { + value = {}; + } + else if (status === 404) { + value = { notFound: true }; + } + else if (status === 409) { + value = { conflict: true }; + } + if (!value) { + return createResponseError(status, result.json) as IServiceResult; + } + return { value: value }; + }); + } + + /** Gets the specified widgets. */ + public getWidgetBatch(request: IGetWidgetBatchRequest, context?: unknown): Promise> { + const uri = 'widgets/get'; + const fetchRequest: IFetchRequest = { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(request.ids) + }; + return fetchResponse(this._fetch, this._baseUri + uri, fetchRequest, context) + .then(result => { + const status = result.response.status; + let value: IGetWidgetBatchResponse | null = null; + if (status === 200) { + if (result.json) { + value = { results: result.json } as IGetWidgetBatchResponse; + } + } + if (!value) { + return createResponseError(status, result.json) as IServiceResult; + } + return { value: value }; + }); + } + + public mirrorFields(request: IMirrorFieldsRequest, context?: unknown): Promise> { + const uri = 'mirrorFields'; + const fetchRequest: IFetchRequest = { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(request) + }; + return fetchResponse(this._fetch, this._baseUri + uri, fetchRequest, context) + .then(result => { + const status = result.response.status; + let value: IMirrorFieldsResponse | null = null; + if (status === 200) { + if (result.json) { + value = result.json as IMirrorFieldsResponse | null; + } + } + if (!value) { + return createResponseError(status, result.json) as IServiceResult; + } + return { value: value }; + }); + } + + public checkQuery(request: ICheckQueryRequest, context?: unknown): Promise> { + let uri = 'checkQuery'; + const query: string[] = []; + request.string == null || query.push('string=' + encodeURIComponent(request.string)); + request.boolean == null || query.push('boolean=' + request.boolean.toString()); + request.double == null || query.push('double=' + encodeURIComponent(request.double.toString())); + request.int32 == null || query.push('int32=' + request.int32.toString()); + request.int64 == null || query.push('int64=' + request.int64.toString()); + request.decimal == null || query.push('decimal=' + request.decimal.toString()); + request.enum == null || query.push('enum=' + request.enum); + if (query.length) { + uri = uri + '?' + query.join('&'); + } + const fetchRequest: IFetchRequest = { + method: 'GET', + }; + return fetchResponse(this._fetch, this._baseUri + uri, fetchRequest, context) + .then(result => { + const status = result.response.status; + let value: ICheckQueryResponse | null = null; + if (status === 200) { + value = {}; + } + if (!value) { + return createResponseError(status, result.json) as IServiceResult; + } + return { value: value }; + }); + } + + public checkPath(request: ICheckPathRequest, context?: unknown): Promise> { + const uriPartString = request.string != null && encodeURIComponent(request.string); + if (!uriPartString) { + return Promise.resolve(createRequiredRequestFieldError('string')); + } + const uriPartBoolean = request.boolean != null && request.boolean.toString(); + if (!uriPartBoolean) { + return Promise.resolve(createRequiredRequestFieldError('boolean')); + } + const uriPartDouble = request.double != null && encodeURIComponent(request.double.toString()); + if (!uriPartDouble) { + return Promise.resolve(createRequiredRequestFieldError('double')); + } + const uriPartInt32 = request.int32 != null && request.int32.toString(); + if (!uriPartInt32) { + return Promise.resolve(createRequiredRequestFieldError('int32')); + } + const uriPartInt64 = request.int64 != null && request.int64.toString(); + if (!uriPartInt64) { + return Promise.resolve(createRequiredRequestFieldError('int64')); + } + const uriPartDecimal = request.decimal != null && request.decimal.toString(); + if (!uriPartDecimal) { + return Promise.resolve(createRequiredRequestFieldError('decimal')); + } + const uriPartEnum = request.enum != null && request.enum; + if (!uriPartEnum) { + return Promise.resolve(createRequiredRequestFieldError('enum')); + } + const uri = `checkPath/${uriPartString}/${uriPartBoolean}/${uriPartDouble}/${uriPartInt32}/${uriPartInt64}/${uriPartDecimal}/${uriPartEnum}`; + const fetchRequest: IFetchRequest = { + method: 'GET', + }; + return fetchResponse(this._fetch, this._baseUri + uri, fetchRequest, context) + .then(result => { + const status = result.response.status; + let value: ICheckPathResponse | null = null; + if (status === 200) { + value = {}; + } + if (!value) { + return createResponseError(status, result.json) as IServiceResult; + } + return { value: value }; + }); + } + + public mirrorHeaders(request: IMirrorHeadersRequest, context?: unknown): Promise> { + const uri = 'mirrorHeaders'; + const fetchRequest: IFetchRequest = { + method: 'GET', + headers: {}, + }; + if (request.string != null) { + fetchRequest.headers!['string'] = request.string; + } + if (request.boolean != null) { + fetchRequest.headers!['boolean'] = request.boolean; + } + if (request.double != null) { + fetchRequest.headers!['double'] = request.double; + } + if (request.int32 != null) { + fetchRequest.headers!['int32'] = request.int32; + } + if (request.int64 != null) { + fetchRequest.headers!['int64'] = request.int64; + } + if (request.decimal != null) { + fetchRequest.headers!['decimal'] = request.decimal; + } + if (request.enum != null) { + fetchRequest.headers!['enum'] = request.enum; + } + return fetchResponse(this._fetch, this._baseUri + uri, fetchRequest, context) + .then(result => { + const status = result.response.status; + let value: IMirrorHeadersResponse | null = null; + if (status === 200) { + value = {}; + } + if (!value) { + return createResponseError(status, result.json) as IServiceResult; + } + let headerValue: string | null | undefined; + headerValue = result.response.headers.get('string'); + if (headerValue != null) { + value.string = headerValue; + } + headerValue = result.response.headers.get('boolean'); + if (headerValue != null) { + value.boolean = headerValue; + } + headerValue = result.response.headers.get('double'); + if (headerValue != null) { + value.double = headerValue; + } + headerValue = result.response.headers.get('int32'); + if (headerValue != null) { + value.int32 = headerValue; + } + headerValue = result.response.headers.get('int64'); + if (headerValue != null) { + value.int64 = headerValue; + } + headerValue = result.response.headers.get('decimal'); + if (headerValue != null) { + value.decimal = headerValue; + } + headerValue = result.response.headers.get('enum'); + if (headerValue != null) { + value.enum = headerValue; + } + return { value: value }; + }); + } + + public mixed(request: IMixedRequest, context?: unknown): Promise> { + const uriPartPath = request.path != null && encodeURIComponent(request.path); + if (!uriPartPath) { + return Promise.resolve(createRequiredRequestFieldError('path')); + } + let uri = `mixed/${uriPartPath}`; + const query: string[] = []; + request.query == null || query.push('query=' + encodeURIComponent(request.query)); + if (query.length) { + uri = uri + '?' + query.join('&'); + } + const fetchRequest: IFetchRequest = { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + normal: request.normal + }) + }; + if (request.header != null) { + fetchRequest.headers!['header'] = request.header; + } + return fetchResponse(this._fetch, this._baseUri + uri, fetchRequest, context) + .then(result => { + const status = result.response.status; + let value: IMixedResponse | null = null; + if (status === 200) { + if (result.json) { + value = result.json as IMixedResponse | null; + } + } + else if (status === 202) { + if (result.json) { + value = { body: result.json } as IMixedResponse; + } + } + else if (status === 204) { + value = { empty: true }; + } + if (!value) { + return createResponseError(status, result.json) as IServiceResult; + } + let headerValue: string | null | undefined; + headerValue = result.response.headers.get('header'); + if (headerValue != null) { + value.header = headerValue; + } + return { value: value }; + }); + } + + public required(request: IRequiredRequest, context?: unknown): Promise> { + let uri = 'required'; + const query: string[] = []; + request.query == null || query.push('query=' + encodeURIComponent(request.query)); + if (query.length) { + uri = uri + '?' + query.join('&'); + } + const fetchRequest: IFetchRequest = { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + normal: request.normal, + widget: request.widget, + widgets: request.widgets, + widgetMatrix: request.widgetMatrix, + widgetResult: request.widgetResult, + widgetResults: request.widgetResults, + widgetMap: request.widgetMap, + hasWidget: request.hasWidget, + point: request.point + }) + }; + return fetchResponse(this._fetch, this._baseUri + uri, fetchRequest, context) + .then(result => { + const status = result.response.status; + let value: IRequiredResponse | null = null; + if (status === 200) { + if (result.json) { + value = result.json as IRequiredResponse | null; + } + } + if (!value) { + return createResponseError(status, result.json) as IServiceResult; + } + return { value: value }; + }); + } + + public mirrorBytes(request: IMirrorBytesRequest, context?: unknown): Promise> { + const uri = 'mirrorBytes'; + const fetchRequest: IFetchRequest = { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(request.content) + }; + if (request.type != null) { + fetchRequest.headers!['Content-Type'] = request.type; + } + return fetchResponse(this._fetch, this._baseUri + uri, fetchRequest, context) + .then(result => { + const status = result.response.status; + let value: IMirrorBytesResponse | null = null; + if (status === 200) { + if (result.json) { + value = { content: result.json } as IMirrorBytesResponse; + } + } + if (!value) { + return createResponseError(status, result.json) as IServiceResult; + } + let headerValue: string | null | undefined; + headerValue = result.response.headers.get('Content-Type'); + if (headerValue != null) { + value.type = headerValue; + } + return { value: value }; + }); + } + + public mirrorText(request: IMirrorTextRequest, context?: unknown): Promise> { + const uri = 'mirrorText'; + const fetchRequest: IFetchRequest = { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(request.content) + }; + if (request.type != null) { + fetchRequest.headers!['Content-Type'] = request.type; + } + return fetchResponse(this._fetch, this._baseUri + uri, fetchRequest, context) + .then(result => { + const status = result.response.status; + let value: IMirrorTextResponse | null = null; + if (status === 200) { + if (result.json) { + value = { content: result.json } as IMirrorTextResponse; + } + } + if (!value) { + return createResponseError(status, result.json) as IServiceResult; + } + let headerValue: string | null | undefined; + headerValue = result.response.headers.get('Content-Type'); + if (headerValue != null) { + value.type = headerValue; + } + return { value: value }; + }); + } + + public bodyTypes(request: IBodyTypesRequest, context?: unknown): Promise> { + const uri = 'bodyTypes'; + const fetchRequest: IFetchRequest = { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(request.content) + }; + return fetchResponse(this._fetch, this._baseUri + uri, fetchRequest, context) + .then(result => { + const status = result.response.status; + let value: IBodyTypesResponse | null = null; + if (status === 200) { + if (result.json) { + value = { content: result.json } as IBodyTypesResponse; + } + } + if (!value) { + return createResponseError(status, result.json) as IServiceResult; + } + return { value: value }; + }); + } + + private _fetch: IFetch; + private _baseUri: string; +} diff --git a/conformance/ts/src/conformanceApiTypes.ts b/conformance/ts/src/conformanceApiTypes.ts new file mode 100644 index 0000000..96e1de8 --- /dev/null +++ b/conformance/ts/src/conformanceApiTypes.ts @@ -0,0 +1,461 @@ +// DO NOT EDIT: generated by fsdgenjs +/* eslint-disable */ + +import { IServiceResult, IServiceError } from 'facility-core'; + +/** API for a Facility test server. */ +export interface IConformanceApi { + /** Gets API information. */ + getApiInfo(request: IGetApiInfoRequest, context?: unknown): Promise>; + + /** Gets widgets. */ + getWidgets(request: IGetWidgetsRequest, context?: unknown): Promise>; + + /** Creates a new widget. */ + createWidget(request: ICreateWidgetRequest, context?: unknown): Promise>; + + /** Gets the specified widget. */ + getWidget(request: IGetWidgetRequest, context?: unknown): Promise>; + + /** Deletes the specified widget. */ + deleteWidget(request: IDeleteWidgetRequest, context?: unknown): Promise>; + + /** Gets the specified widgets. */ + getWidgetBatch(request: IGetWidgetBatchRequest, context?: unknown): Promise>; + + mirrorFields(request: IMirrorFieldsRequest, context?: unknown): Promise>; + + checkQuery(request: ICheckQueryRequest, context?: unknown): Promise>; + + checkPath(request: ICheckPathRequest, context?: unknown): Promise>; + + mirrorHeaders(request: IMirrorHeadersRequest, context?: unknown): Promise>; + + mixed(request: IMixedRequest, context?: unknown): Promise>; + + required(request: IRequiredRequest, context?: unknown): Promise>; + + mirrorBytes(request: IMirrorBytesRequest, context?: unknown): Promise>; + + mirrorText(request: IMirrorTextRequest, context?: unknown): Promise>; + + bodyTypes(request: IBodyTypesRequest, context?: unknown): Promise>; +} + +/** Request for GetApiInfo. */ +export interface IGetApiInfoRequest { +} + +/** Response for GetApiInfo. */ +export interface IGetApiInfoResponse { + /** The name of the service. */ + service?: string; + + /** The version of the service. */ + version?: string; +} + +/** Request for GetWidgets. */ +export interface IGetWidgetsRequest { + /** The query. */ + query?: string; +} + +/** Response for GetWidgets. */ +export interface IGetWidgetsResponse { + /** The widgets. */ + widgets?: IWidget[]; +} + +/** Request for CreateWidget. */ +export interface ICreateWidgetRequest { + /** The widget to create. */ + widget?: IWidget; +} + +/** Response for CreateWidget. */ +export interface ICreateWidgetResponse { + /** The created widget. */ + widget?: IWidget; + + /** The URL of the created widget. */ + url?: string; + + /** The ETag of the created widget. */ + eTag?: string; +} + +/** Request for GetWidget. */ +export interface IGetWidgetRequest { + /** The widget ID. */ + id?: number; + + /** Don't get the widget if it has this ETag. */ + ifNotETag?: string; +} + +/** Response for GetWidget. */ +export interface IGetWidgetResponse { + /** The requested widget. */ + widget?: IWidget; + + /** The ETag of the widget. */ + eTag?: string; + + /** The widget still has the specified ETag. */ + notModified?: boolean; +} + +/** Request for DeleteWidget. */ +export interface IDeleteWidgetRequest { + /** The widget ID. */ + id?: number; + + /** Don't delete the widget unless it has this ETag. */ + ifETag?: string; +} + +/** Response for DeleteWidget. */ +export interface IDeleteWidgetResponse { + /** The widget was not found. */ + notFound?: boolean; + + /** The widget no longer has the specified ETag. */ + conflict?: boolean; +} + +/** Request for GetWidgetBatch. */ +export interface IGetWidgetBatchRequest { + /** The IDs of the widgets to return. */ + ids?: number[]; +} + +/** Response for GetWidgetBatch. */ +export interface IGetWidgetBatchResponse { + /** The widget results. */ + results?: IServiceResult[]; +} + +/** Request for MirrorFields. */ +export interface IMirrorFieldsRequest { + field?: IAny; + + matrix?: number[][][]; +} + +/** Response for MirrorFields. */ +export interface IMirrorFieldsResponse { + field?: IAny; + + matrix?: number[][][]; +} + +/** Request for CheckQuery. */ +export interface ICheckQueryRequest { + string?: string; + + boolean?: boolean; + + double?: number; + + int32?: number; + + int64?: number; + + decimal?: number; + + enum?: Answer; +} + +/** Response for CheckQuery. */ +export interface ICheckQueryResponse { +} + +/** Request for CheckPath. */ +export interface ICheckPathRequest { + string?: string; + + boolean?: boolean; + + double?: number; + + int32?: number; + + int64?: number; + + decimal?: number; + + enum?: Answer; +} + +/** Response for CheckPath. */ +export interface ICheckPathResponse { +} + +/** Request for MirrorHeaders. */ +export interface IMirrorHeadersRequest { + string?: string; + + boolean?: boolean; + + double?: number; + + int32?: number; + + int64?: number; + + decimal?: number; + + enum?: Answer; +} + +/** Response for MirrorHeaders. */ +export interface IMirrorHeadersResponse { + string?: string; + + boolean?: boolean; + + double?: number; + + int32?: number; + + int64?: number; + + decimal?: number; + + enum?: Answer; +} + +/** Request for Mixed. */ +export interface IMixedRequest { + path?: string; + + query?: string; + + header?: string; + + normal?: string; +} + +/** Response for Mixed. */ +export interface IMixedResponse { + header?: string; + + normal?: string; + + body?: { [name: string]: any }; + + empty?: boolean; +} + +/** Request for Required. */ +export interface IRequiredRequest { + query?: string; + + normal?: string; + + widget?: IWidget; + + widgets?: IWidget[]; + + widgetMatrix?: IWidget[][]; + + widgetResult?: IServiceResult; + + widgetResults?: IServiceResult[]; + + widgetMap?: { [name: string]: IWidget }; + + hasWidget?: IHasWidget; + + point?: number[]; +} + +/** Response for Required. */ +export interface IRequiredResponse { + normal?: string; +} + +/** Request for MirrorBytes. */ +export interface IMirrorBytesRequest { + content?: string; + + type?: string; +} + +/** Response for MirrorBytes. */ +export interface IMirrorBytesResponse { + content?: string; + + type?: string; +} + +/** Request for MirrorText. */ +export interface IMirrorTextRequest { + content?: string; + + type?: string; +} + +/** Response for MirrorText. */ +export interface IMirrorTextResponse { + content?: string; + + type?: string; +} + +/** Request for BodyTypes. */ +export interface IBodyTypesRequest { + content?: string; +} + +/** Response for BodyTypes. */ +export interface IBodyTypesResponse { + content?: string; +} + +/** A widget. */ +export interface IWidget { + /** A unique identifier for the widget. */ + id?: number; + + /** The name of the widget. */ + name?: string; +} + +export interface IAny { + string?: string; + + boolean?: boolean; + + double?: number; + + int32?: number; + + int64?: number; + + decimal?: number; + + bytes?: string; + + object?: { [name: string]: any }; + + error?: IServiceError; + + data?: IAny; + + enum?: Answer; + + array?: IAnyArray; + + map?: IAnyMap; + + result?: IAnyResult; +} + +export interface IAnyArray { + string?: string[]; + + boolean?: boolean[]; + + double?: number[]; + + int32?: number[]; + + int64?: number[]; + + decimal?: number[]; + + bytes?: string[]; + + object?: { [name: string]: any }[]; + + error?: IServiceError[]; + + data?: IAny[]; + + enum?: Answer[]; + + array?: number[][]; + + map?: { [name: string]: number }[]; + + result?: IServiceResult[]; +} + +export interface IAnyMap { + string?: { [name: string]: string }; + + boolean?: { [name: string]: boolean }; + + double?: { [name: string]: number }; + + int32?: { [name: string]: number }; + + int64?: { [name: string]: number }; + + decimal?: { [name: string]: number }; + + bytes?: { [name: string]: string }; + + object?: { [name: string]: { [name: string]: any } }; + + error?: { [name: string]: IServiceError }; + + data?: { [name: string]: IAny }; + + enum?: { [name: string]: Answer }; + + array?: { [name: string]: number[] }; + + map?: { [name: string]: { [name: string]: number } }; + + result?: { [name: string]: IServiceResult }; +} + +export interface IAnyResult { + string?: IServiceResult; + + boolean?: IServiceResult; + + double?: IServiceResult; + + int32?: IServiceResult; + + int64?: IServiceResult; + + decimal?: IServiceResult; + + bytes?: IServiceResult; + + object?: IServiceResult<{ [name: string]: any }>; + + error?: IServiceResult; + + data?: IServiceResult; + + enum?: IServiceResult; + + array?: IServiceResult; + + map?: IServiceResult<{ [name: string]: number }>; + + result?: IServiceResult>; +} + +export interface IHasWidget { + widget?: IWidget; +} + +/** One of three answers. */ +export enum Answer { + /** Affirmative. */ + yes = 'yes', + + /** Negative. */ + no = 'no', + + /** Unknown. */ + maybe = 'maybe', +} + diff --git a/dotnet-tools.json b/dotnet-tools.json index d9ecc08..c0850ea 100644 --- a/dotnet-tools.json +++ b/dotnet-tools.json @@ -13,6 +13,12 @@ "commands": [ "jb" ] + }, + "facilityconformance": { + "version": "2.15.2", + "commands": [ + "FacilityConformance" + ] } } } \ No newline at end of file diff --git a/tools/Build/Build.cs b/tools/Build/Build.cs index c350e92..9c931c3 100644 --- a/tools/Build/Build.cs +++ b/tools/Build/Build.cs @@ -44,6 +44,10 @@ void CodeGen(bool verify) RunCodeGen("example/ExampleApi.fsd", "example/js/", "--indent", "2", "--express", "--disable-eslint"); RunCodeGen("example/ExampleApi.fsd", "example/ts/src/", "--typescript", "--express", "--disable-eslint"); + RunDotNet("FacilityConformance", "fsd", "--output", "conformance/ConformanceApi.fsd", verifyOption); + RunDotNet("FacilityConformance", "json", "--output", "conformance/ConformanceTests.json", verifyOption); + RunCodeGen("conformance/ConformanceApi.fsd", "conformance/ts/src/", "--typescript", "--indent", "2", "--disable-eslint"); + void RunCodeGen(params string?[] args) => RunDotNet(new[] { "run", "--no-build", "--project", $"src/{codegen}", "-f", "net6.0", "-c", configuration, "--", "--newline", "lf", verifyOption }.Concat(args)); } From 8a393962b82b8c1124bed0bf447f6bb783269177 Mon Sep 17 00:00:00 2001 From: Ed Ball Date: Mon, 10 Oct 2022 16:19:36 -0700 Subject: [PATCH 3/7] Run tests from conformanceTests.json. Temporarily delete broken generated code. --- conformance/ts/.gitignore | 4 + conformance/ts/package-lock.json | 2025 ++++++++++++++++++++ conformance/ts/package.json | 28 + conformance/ts/src/conformanceApi.ts | 39 - conformance/ts/test/conformanceApiTests.ts | 30 + conformance/ts/tsconfig.json | 15 + 6 files changed, 2102 insertions(+), 39 deletions(-) create mode 100644 conformance/ts/.gitignore create mode 100644 conformance/ts/package-lock.json create mode 100644 conformance/ts/package.json create mode 100644 conformance/ts/test/conformanceApiTests.ts create mode 100644 conformance/ts/tsconfig.json diff --git a/conformance/ts/.gitignore b/conformance/ts/.gitignore new file mode 100644 index 0000000..b196ce0 --- /dev/null +++ b/conformance/ts/.gitignore @@ -0,0 +1,4 @@ +node_modules/ +*.d.ts +*.js +*.js.map diff --git a/conformance/ts/package-lock.json b/conformance/ts/package-lock.json new file mode 100644 index 0000000..da656ef --- /dev/null +++ b/conformance/ts/package-lock.json @@ -0,0 +1,2025 @@ +{ + "name": "conformance-tester", + "version": "0.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "conformance-tester", + "version": "0.0.0", + "license": "UNLICENSED", + "dependencies": { + "facility-core": "file:../../ts" + }, + "devDependencies": { + "@types/chai": "^4.2.18", + "@types/mocha": "^8.2.2", + "@types/node": "^12.20.15", + "@types/node-fetch": "^2.5.10", + "chai": "^4.3.4", + "mocha": "^10.0.0", + "node-fetch": "^2.6.7", + "typescript": "~3.9.9" + } + }, + "../../ts": { + "name": "facility-core", + "version": "2.2.0", + "license": "MIT", + "devDependencies": { + "@types/chai": "^4.2.18", + "@types/mocha": "^8.2.2", + "@typescript-eslint/eslint-plugin": "^4.26.1", + "@typescript-eslint/parser": "^4.26.1", + "chai": "^4.3.4", + "eslint": "^7.28.0", + "eslint-config-prettier": "^8.3.0", + "eslint-plugin-prettier": "^3.4.0", + "mocha": "^10.0.0", + "prettier": "^2.3.1", + "typescript": "~3.9.9" + } + }, + "node_modules/@types/chai": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.3.tgz", + "integrity": "sha512-hC7OMnszpxhZPduX+m+nrx+uFoLkWOMiR4oa/AZF3MuSETYTZmFfJAHqZEM8MVlvfG7BEUcgvtwoCTxBp6hm3g==", + "dev": true + }, + "node_modules/@types/mocha": { + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.3.tgz", + "integrity": "sha512-ekGvFhFgrc2zYQoX4JeZPmVzZxw6Dtllga7iGHzfbYIYkAMUx/sAFP2GdFpLff+vdHXu5fl7WX9AT+TtqYcsyw==", + "dev": true + }, + "node_modules/@types/node": { + "version": "12.20.55", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", + "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==", + "dev": true + }, + "node_modules/@types/node-fetch": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.2.tgz", + "integrity": "sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==", + "dev": true, + "dependencies": { + "@types/node": "*", + "form-data": "^3.0.0" + } + }, + "node_modules/@ungap/promise-all-settled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", + "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", + "dev": true + }, + "node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chai": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz", + "integrity": "sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==", + "dev": true, + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^3.0.1", + "get-func-name": "^2.0.0", + "loupe": "^2.3.1", + "pathval": "^1.1.1", + "type-detect": "^4.0.5" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-eql": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", + "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "dev": true, + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/facility-core": { + "resolved": "../../ts", + "link": true + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/loupe": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.4.tgz", + "integrity": "sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.0" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.0.0.tgz", + "integrity": "sha512-0Wl+elVUD43Y0BqPZBzZt8Tnkw9CMUdNYnUsTfOM1vuhJVZL+kiesFYsqwBkEEuEixaiPe5ZQdqDgX2jddhmoA==", + "dev": true, + "dependencies": { + "@ungap/promise-all-settled": "1.1.2", + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.3", + "debug": "4.3.4", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.2.0", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "5.0.1", + "ms": "2.1.3", + "nanoid": "3.3.3", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "workerpool": "6.2.1", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": ">= 14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mochajs" + } + }, + "node_modules/mocha/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/mocha/node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/mocha/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/nanoid": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", + "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", + "dev": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dev": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/typescript": { + "version": "3.9.10", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz", + "integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/workerpool": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", + "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + }, + "dependencies": { + "@types/chai": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.3.tgz", + "integrity": "sha512-hC7OMnszpxhZPduX+m+nrx+uFoLkWOMiR4oa/AZF3MuSETYTZmFfJAHqZEM8MVlvfG7BEUcgvtwoCTxBp6hm3g==", + "dev": true + }, + "@types/mocha": { + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.3.tgz", + "integrity": "sha512-ekGvFhFgrc2zYQoX4JeZPmVzZxw6Dtllga7iGHzfbYIYkAMUx/sAFP2GdFpLff+vdHXu5fl7WX9AT+TtqYcsyw==", + "dev": true + }, + "@types/node": { + "version": "12.20.55", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", + "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==", + "dev": true + }, + "@types/node-fetch": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.2.tgz", + "integrity": "sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==", + "dev": true, + "requires": { + "@types/node": "*", + "form-data": "^3.0.0" + } + }, + "@ungap/promise-all-settled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", + "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", + "dev": true + }, + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true + }, + "chai": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz", + "integrity": "sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==", + "dev": true, + "requires": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^3.0.1", + "get-func-name": "^2.0.0", + "loupe": "^2.3.1", + "pathval": "^1.1.1", + "type-detect": "^4.0.5" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==", + "dev": true + }, + "chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + } + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true + }, + "deep-eql": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", + "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "dev": true, + "requires": { + "type-detect": "^4.0.0" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true + }, + "diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "facility-core": { + "version": "file:../../ts", + "requires": { + "@types/chai": "^4.2.18", + "@types/mocha": "^8.2.2", + "@typescript-eslint/eslint-plugin": "^4.26.1", + "@typescript-eslint/parser": "^4.26.1", + "chai": "^4.3.4", + "eslint": "^7.28.0", + "eslint-config-prettier": "^8.3.0", + "eslint-plugin-prettier": "^3.4.0", + "mocha": "^10.0.0", + "prettier": "^2.3.1", + "typescript": "~3.9.9" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true + }, + "form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", + "dev": true + }, + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "dependencies": { + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true + }, + "is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, + "log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + } + }, + "loupe": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.4.tgz", + "integrity": "sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==", + "dev": true, + "requires": { + "get-func-name": "^2.0.0" + } + }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "requires": { + "mime-db": "1.52.0" + } + }, + "minimatch": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "mocha": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.0.0.tgz", + "integrity": "sha512-0Wl+elVUD43Y0BqPZBzZt8Tnkw9CMUdNYnUsTfOM1vuhJVZL+kiesFYsqwBkEEuEixaiPe5ZQdqDgX2jddhmoA==", + "dev": true, + "requires": { + "@ungap/promise-all-settled": "1.1.2", + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.3", + "debug": "4.3.4", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.2.0", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "5.0.1", + "ms": "2.1.3", + "nanoid": "3.3.3", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "workerpool": "6.2.1", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + } + } + }, + "nanoid": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", + "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", + "dev": true + }, + "node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dev": true, + "requires": { + "whatwg-url": "^5.0.0" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true + }, + "pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + }, + "serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true + }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, + "typescript": { + "version": "3.9.10", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz", + "integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==", + "dev": true + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "workerpool": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", + "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", + "dev": true + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true + }, + "yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "requires": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + } + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true + } + } +} diff --git a/conformance/ts/package.json b/conformance/ts/package.json new file mode 100644 index 0000000..d011dbc --- /dev/null +++ b/conformance/ts/package.json @@ -0,0 +1,28 @@ +{ + "name": "conformance-tester", + "version": "0.0.0", + "description": "Tests the ConformanceApi client.", + "scripts": { + "build": "tsc", + "test": "npm run --silent build && mocha" + }, + "repository": { + "type": "git", + "url": "https://github.com/FacilityApi/FacilityJavaScript.git" + }, + "author": "Ed Ball", + "license": "UNLICENSED", + "devDependencies": { + "@types/chai": "^4.2.18", + "@types/mocha": "^8.2.2", + "@types/node": "^12.20.15", + "@types/node-fetch": "^2.5.10", + "chai": "^4.3.4", + "mocha": "^10.0.0", + "node-fetch": "^2.6.7", + "typescript": "~3.9.9" + }, + "dependencies": { + "facility-core": "file:../../ts" + } +} diff --git a/conformance/ts/src/conformanceApi.ts b/conformance/ts/src/conformanceApi.ts index 392a7d8..cbd4427 100644 --- a/conformance/ts/src/conformanceApi.ts +++ b/conformance/ts/src/conformanceApi.ts @@ -316,21 +316,6 @@ class ConformanceApiHttpClient implements IConformanceApi { if (request.string != null) { fetchRequest.headers!['string'] = request.string; } - if (request.boolean != null) { - fetchRequest.headers!['boolean'] = request.boolean; - } - if (request.double != null) { - fetchRequest.headers!['double'] = request.double; - } - if (request.int32 != null) { - fetchRequest.headers!['int32'] = request.int32; - } - if (request.int64 != null) { - fetchRequest.headers!['int64'] = request.int64; - } - if (request.decimal != null) { - fetchRequest.headers!['decimal'] = request.decimal; - } if (request.enum != null) { fetchRequest.headers!['enum'] = request.enum; } @@ -349,30 +334,6 @@ class ConformanceApiHttpClient implements IConformanceApi { if (headerValue != null) { value.string = headerValue; } - headerValue = result.response.headers.get('boolean'); - if (headerValue != null) { - value.boolean = headerValue; - } - headerValue = result.response.headers.get('double'); - if (headerValue != null) { - value.double = headerValue; - } - headerValue = result.response.headers.get('int32'); - if (headerValue != null) { - value.int32 = headerValue; - } - headerValue = result.response.headers.get('int64'); - if (headerValue != null) { - value.int64 = headerValue; - } - headerValue = result.response.headers.get('decimal'); - if (headerValue != null) { - value.decimal = headerValue; - } - headerValue = result.response.headers.get('enum'); - if (headerValue != null) { - value.enum = headerValue; - } return { value: value }; }); } diff --git a/conformance/ts/test/conformanceApiTests.ts b/conformance/ts/test/conformanceApiTests.ts new file mode 100644 index 0000000..07fdef2 --- /dev/null +++ b/conformance/ts/test/conformanceApiTests.ts @@ -0,0 +1,30 @@ +import { createHttpClient } from '../src/conformanceApi'; +import { expect, should } from 'chai'; +import fetch from 'node-fetch'; +import * as fs from 'fs'; +import * as path from 'path'; + +should(); + +const httpClient = createHttpClient({ + fetch: (uri, request) => { + return fetch('http://localhost:4117/' + uri, request); + } +}); + +const testData = JSON.parse( + fs.readFileSync(path.resolve(__dirname, '../../conformanceTests.json'), 'utf8')); + +describe('tests', () => { + + testData.tests.forEach((data: any) => { + it(data.test, () => { + return ((httpClient as any)[data.method](data.request)) + .then((result: any) => { + expect(result.value).to.deep.equal(data.response); + expect(result.error).to.deep.equal(data.error); + }); + }); + }); + +}); diff --git a/conformance/ts/tsconfig.json b/conformance/ts/tsconfig.json new file mode 100644 index 0000000..57266dd --- /dev/null +++ b/conformance/ts/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "ES2019", + "strict": true, + "newLine": "LF", + "declaration": true, + "sourceMap": true, + "lib": [ "ES2019" ] + }, + "include": [ + "src/*.ts", + "test/*.ts" + ] +} From 7dcbd8902f5ef8fc3a105c555a714afc4e136de5 Mon Sep 17 00:00:00 2001 From: Ed Ball Date: Mon, 10 Oct 2022 20:48:49 -0700 Subject: [PATCH 4/7] Use same error message as C#. --- ts/src/facilityCore.ts | 2 +- ts/test/createRequiredRequestFieldError.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ts/src/facilityCore.ts b/ts/src/facilityCore.ts index 22ed707..36052ef 100644 --- a/ts/src/facilityCore.ts +++ b/ts/src/facilityCore.ts @@ -127,7 +127,7 @@ export namespace HttpClientUtility { return { error: { code: 'InvalidRequest', - message: `The request field '${name}' is required.`, + message: `'${name}' is required.`, }, }; } diff --git a/ts/test/createRequiredRequestFieldError.ts b/ts/test/createRequiredRequestFieldError.ts index 89b4467..fb384a0 100644 --- a/ts/test/createRequiredRequestFieldError.ts +++ b/ts/test/createRequiredRequestFieldError.ts @@ -9,7 +9,7 @@ describe('createRequiredRequestFieldError', () => { it('should return error result', () => { createRequiredRequestFieldError('id') - .should.deep.equal({ error: { code: 'InvalidRequest', message: 'The request field \'id\' is required.' } }); + .should.deep.equal({ error: { code: 'InvalidRequest', message: '\'id\' is required.' } }); }); }); From 1211b8c9a6f6cf71117fc1c7e06e62d014bb8844 Mon Sep 17 00:00:00 2001 From: Ed Ball Date: Mon, 10 Oct 2022 20:52:52 -0700 Subject: [PATCH 5/7] Check for missing body field. --- conformance/ts/src/conformanceApi.ts | 15 +++++++++++++++ example/js/exampleApi.js | 9 +++++++++ example/ts/src/exampleApi.ts | 9 +++++++++ .../JavaScriptGenerator.cs | 9 ++++++--- 4 files changed, 39 insertions(+), 3 deletions(-) diff --git a/conformance/ts/src/conformanceApi.ts b/conformance/ts/src/conformanceApi.ts index cbd4427..c301dda 100644 --- a/conformance/ts/src/conformanceApi.ts +++ b/conformance/ts/src/conformanceApi.ts @@ -81,6 +81,9 @@ class ConformanceApiHttpClient implements IConformanceApi { /** Creates a new widget. */ public createWidget(request: ICreateWidgetRequest, context?: unknown): Promise> { const uri = 'widgets'; + if (!request.widget) { + return Promise.resolve(createRequiredRequestFieldError('widget')); + } const fetchRequest: IFetchRequest = { method: 'POST', headers: { 'Content-Type': 'application/json' }, @@ -186,6 +189,9 @@ class ConformanceApiHttpClient implements IConformanceApi { /** Gets the specified widgets. */ public getWidgetBatch(request: IGetWidgetBatchRequest, context?: unknown): Promise> { const uri = 'widgets/get'; + if (!request.ids) { + return Promise.resolve(createRequiredRequestFieldError('ids')); + } const fetchRequest: IFetchRequest = { method: 'POST', headers: { 'Content-Type': 'application/json' }, @@ -428,6 +434,9 @@ class ConformanceApiHttpClient implements IConformanceApi { public mirrorBytes(request: IMirrorBytesRequest, context?: unknown): Promise> { const uri = 'mirrorBytes'; + if (!request.content) { + return Promise.resolve(createRequiredRequestFieldError('content')); + } const fetchRequest: IFetchRequest = { method: 'POST', headers: { 'Content-Type': 'application/json' }, @@ -459,6 +468,9 @@ class ConformanceApiHttpClient implements IConformanceApi { public mirrorText(request: IMirrorTextRequest, context?: unknown): Promise> { const uri = 'mirrorText'; + if (!request.content) { + return Promise.resolve(createRequiredRequestFieldError('content')); + } const fetchRequest: IFetchRequest = { method: 'POST', headers: { 'Content-Type': 'application/json' }, @@ -490,6 +502,9 @@ class ConformanceApiHttpClient implements IConformanceApi { public bodyTypes(request: IBodyTypesRequest, context?: unknown): Promise> { const uri = 'bodyTypes'; + if (!request.content) { + return Promise.resolve(createRequiredRequestFieldError('content')); + } const fetchRequest: IFetchRequest = { method: 'POST', headers: { 'Content-Type': 'application/json' }, diff --git a/example/js/exampleApi.js b/example/js/exampleApi.js index 2c2b515..4b68e95 100644 --- a/example/js/exampleApi.js +++ b/example/js/exampleApi.js @@ -66,6 +66,9 @@ class ExampleApiHttpClient { /** Creates a new widget. */ createWidget(request, context) { const uri = 'widgets'; + if (!request.widget) { + return Promise.resolve(createRequiredRequestFieldError('widget')); + } const fetchRequest = { method: 'POST', headers: { 'Content-Type': 'application/json' }, @@ -192,6 +195,9 @@ class ExampleApiHttpClient { /** Gets the specified widgets. */ getWidgetBatch(request, context) { const uri = 'widgets/get'; + if (!request.ids) { + return Promise.resolve(createRequiredRequestFieldError('ids')); + } const fetchRequest = { method: 'POST', headers: { 'Content-Type': 'application/json' }, @@ -275,6 +281,9 @@ class ExampleApiHttpClient { return Promise.resolve(createRequiredRequestFieldError('key')); } const uri = `prefs/${uriPartKey}`; + if (!request.value) { + return Promise.resolve(createRequiredRequestFieldError('value')); + } const fetchRequest = { method: 'PUT', headers: { 'Content-Type': 'application/json' }, diff --git a/example/ts/src/exampleApi.ts b/example/ts/src/exampleApi.ts index 9cc3707..de5424a 100644 --- a/example/ts/src/exampleApi.ts +++ b/example/ts/src/exampleApi.ts @@ -69,6 +69,9 @@ class ExampleApiHttpClient implements IExampleApi { /** Creates a new widget. */ public createWidget(request: ICreateWidgetRequest, context?: unknown): Promise> { const uri = 'widgets'; + if (!request.widget) { + return Promise.resolve(createRequiredRequestFieldError('widget')); + } const fetchRequest: IFetchRequest = { method: 'POST', headers: { 'Content-Type': 'application/json' }, @@ -195,6 +198,9 @@ class ExampleApiHttpClient implements IExampleApi { /** Gets the specified widgets. */ public getWidgetBatch(request: IGetWidgetBatchRequest, context?: unknown): Promise> { const uri = 'widgets/get'; + if (!request.ids) { + return Promise.resolve(createRequiredRequestFieldError('ids')); + } const fetchRequest: IFetchRequest = { method: 'POST', headers: { 'Content-Type': 'application/json' }, @@ -278,6 +284,9 @@ class ExampleApiHttpClient implements IExampleApi { return Promise.resolve(createRequiredRequestFieldError('key')); } const uri = `prefs/${uriPartKey}`; + if (!request.value) { + return Promise.resolve(createRequiredRequestFieldError('value')); + } const fetchRequest: IFetchRequest = { method: 'PUT', headers: { 'Content-Type': 'application/json' }, diff --git a/src/Facility.CodeGen.JavaScript/JavaScriptGenerator.cs b/src/Facility.CodeGen.JavaScript/JavaScriptGenerator.cs index b338449..f41101f 100644 --- a/src/Facility.CodeGen.JavaScript/JavaScriptGenerator.cs +++ b/src/Facility.CodeGen.JavaScript/JavaScriptGenerator.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; using System.Globalization; -using System.Linq; using Facility.Definition; using Facility.Definition.CodeGen; using Facility.Definition.Http; @@ -222,6 +219,12 @@ public override CodeGenOutput GenerateOutput(ServiceInfo service) code.WriteLine("uri = uri + '?' + query.join('&');"); } + if (httpMethodInfo.RequestBodyField != null) + { + using (code.Block($"if (!request.{httpMethodInfo.RequestBodyField.ServiceField.Name}) {{", "}")) + code.WriteLine($"return Promise.resolve(createRequiredRequestFieldError('{httpMethodInfo.RequestBodyField.ServiceField.Name}'));"); + } + using (code.Block("const fetchRequest" + IfTypeScript(": IFetchRequest") + " = {", "};")) { if (httpMethodInfo.RequestBodyField == null && httpMethodInfo.RequestNormalFields.Count == 0) From 45d39bd4ee7ecac31ab5b7569dd222d1d4a2ff46 Mon Sep 17 00:00:00 2001 From: Ed Ball Date: Tue, 11 Oct 2022 09:14:02 -0700 Subject: [PATCH 6/7] Fix client codegen for non-string headers. --- conformance/ts/src/conformanceApi.ts | 52 ++++++++++++++++++ .../JavaScriptGenerator.cs | 54 +++++++++++++++++-- 2 files changed, 101 insertions(+), 5 deletions(-) diff --git a/conformance/ts/src/conformanceApi.ts b/conformance/ts/src/conformanceApi.ts index c301dda..4394c63 100644 --- a/conformance/ts/src/conformanceApi.ts +++ b/conformance/ts/src/conformanceApi.ts @@ -14,6 +14,19 @@ const { fetchResponse, createResponseError, createRequiredRequestFieldError } = type IFetch = HttpClientUtility.IFetch; type IFetchRequest = HttpClientUtility.IFetchRequest; +function parseBoolean(value: string | undefined) { + if (typeof value === 'string') { + const lowerValue = value.toLowerCase(); + if (lowerValue === 'true') { + return true; + } + if (lowerValue === 'false') { + return false; + } + } + return undefined; +} + class ConformanceApiHttpClient implements IConformanceApi { constructor(fetch: IFetch, baseUri?: string) { if (typeof fetch !== 'function') { @@ -322,6 +335,21 @@ class ConformanceApiHttpClient implements IConformanceApi { if (request.string != null) { fetchRequest.headers!['string'] = request.string; } + if (request.boolean != null) { + fetchRequest.headers!['boolean'] = request.boolean.toString(); + } + if (request.double != null) { + fetchRequest.headers!['double'] = request.double.toString(); + } + if (request.int32 != null) { + fetchRequest.headers!['int32'] = request.int32.toString(); + } + if (request.int64 != null) { + fetchRequest.headers!['int64'] = request.int64.toString(); + } + if (request.decimal != null) { + fetchRequest.headers!['decimal'] = request.decimal.toString(); + } if (request.enum != null) { fetchRequest.headers!['enum'] = request.enum; } @@ -340,6 +368,30 @@ class ConformanceApiHttpClient implements IConformanceApi { if (headerValue != null) { value.string = headerValue; } + headerValue = result.response.headers.get('boolean'); + if (headerValue != null) { + value.boolean = parseBoolean(headerValue); + } + headerValue = result.response.headers.get('double'); + if (headerValue != null) { + value.double = parseFloat(headerValue); + } + headerValue = result.response.headers.get('int32'); + if (headerValue != null) { + value.int32 = parseInt(headerValue, 10); + } + headerValue = result.response.headers.get('int64'); + if (headerValue != null) { + value.int64 = parseInt(headerValue, 10); + } + headerValue = result.response.headers.get('decimal'); + if (headerValue != null) { + value.decimal = parseFloat(headerValue); + } + headerValue = result.response.headers.get('enum'); + if (headerValue != null) { + value.enum = headerValue as Answer; + } return { value: value }; }); } diff --git a/src/Facility.CodeGen.JavaScript/JavaScriptGenerator.cs b/src/Facility.CodeGen.JavaScript/JavaScriptGenerator.cs index f41101f..4b0b6dc 100644 --- a/src/Facility.CodeGen.JavaScript/JavaScriptGenerator.cs +++ b/src/Facility.CodeGen.JavaScript/JavaScriptGenerator.cs @@ -164,6 +164,24 @@ public override CodeGenOutput GenerateOutput(ServiceInfo service) code.WriteLine("type IFetchRequest = HttpClientUtility.IFetchRequest;"); } + // TODO: export this from facility-core? + if (httpServiceInfo.Methods.Any(x => x.RequestHeaderFields.Any(y => service.GetFieldType(y.ServiceField)!.Kind == ServiceTypeKind.Boolean))) + { + code.WriteLine(); + using (code.Block("function parseBoolean(value" + IfTypeScript(": string | undefined") + ") {", "}")) + { + using (code.Block("if (typeof value === 'string') {", "}")) + { + code.WriteLine("const lowerValue = value.toLowerCase();"); + using (code.Block("if (lowerValue === 'true') {", "}")) + code.WriteLine("return true;"); + using (code.Block("if (lowerValue === 'false') {", "}")) + code.WriteLine("return false;"); + } + code.WriteLine("return undefined;"); + } + } + code.WriteLine(); using (code.Block($"class {capModuleName}HttpClient" + IfTypeScript($" implements I{capModuleName}") + " {", "}")) { @@ -267,7 +285,7 @@ public override CodeGenOutput GenerateOutput(ServiceInfo service) foreach (var httpHeaderField in httpMethodInfo.RequestHeaderFields) { using (code.Block($"if (request.{httpHeaderField.ServiceField.Name} != null) {{", "}")) - code.WriteLine("fetchRequest.headers" + IfTypeScript("!") + $"['{httpHeaderField.Name}'] = request.{httpHeaderField.ServiceField.Name};"); + code.WriteLine("fetchRequest.headers" + IfTypeScript("!") + $"['{httpHeaderField.Name}'] = {RenderFieldValue(httpHeaderField.ServiceField, service, $"request.{httpHeaderField.ServiceField.Name}")};"); } } @@ -334,7 +352,7 @@ public override CodeGenOutput GenerateOutput(ServiceInfo service) { code.WriteLine($"headerValue = result.response.headers.get('{httpHeaderField.Name}');"); using (code.Block("if (headerValue != null) {", "}")) - code.WriteLine($"value.{httpHeaderField.ServiceField.Name} = headerValue;"); + code.WriteLine($"value.{httpHeaderField.ServiceField.Name} = {ParseFieldValue(httpHeaderField.ServiceField, service, "headerValue")};"); } } @@ -390,6 +408,7 @@ public override CodeGenOutput GenerateOutput(ServiceInfo service) code.WriteLine("'ServiceUnavailable': 503,"); } + // TODO: export this from facility-core? code.WriteLine(); using (code.Block("function parseBoolean(value" + IfTypeScript(": string | undefined") + ") {", "}")) { @@ -427,12 +446,12 @@ public override CodeGenOutput GenerateOutput(ServiceInfo service) code.WriteLine("const request" + IfTypeScript($": I{capMethodName}Request") + " = {};"); foreach (var httpPathField in httpMethodInfo.PathFields) - code.WriteLine($"request.{httpPathField.ServiceField.Name} = {RenderJsConversion(httpPathField.ServiceField, service, $"req.params.{httpPathField.Name}")};"); + code.WriteLine($"request.{httpPathField.ServiceField.Name} = {ParseFieldValue(httpPathField.ServiceField, service, $"req.params.{httpPathField.Name}")};"); foreach (var httpQueryField in httpMethodInfo.QueryFields) { using (code.Block($"if (typeof req.query['{httpQueryField.Name}'] === 'string') {{", "}")) - code.WriteLine($"request.{httpQueryField.ServiceField.Name} = {RenderJsConversion(httpQueryField.ServiceField, service, $"req.query['{httpQueryField.Name}']")};"); + code.WriteLine($"request.{httpQueryField.ServiceField.Name} = {ParseFieldValue(httpQueryField.ServiceField, service, $"req.query['{httpQueryField.Name}']")};"); } if (httpMethodInfo.RequestBodyField != null) @@ -634,7 +653,7 @@ private static string RenderUriComponent(ServiceFieldInfo field, ServiceInfo ser } } - private static string RenderJsConversion(ServiceFieldInfo field, ServiceInfo service, string value) + private static string ParseFieldValue(ServiceFieldInfo field, ServiceInfo service, string value) { var fieldTypeKind = service.GetFieldType(field)!.Kind; @@ -655,6 +674,31 @@ private static string RenderJsConversion(ServiceFieldInfo field, ServiceInfo ser return $"parseFloat({value})"; case ServiceTypeKind.Dto: case ServiceTypeKind.Error: + case ServiceTypeKind.Object: + throw new NotSupportedException("Field type not supported on path/query/header: " + fieldTypeKind); + default: + throw new NotSupportedException("Unknown field type " + fieldTypeKind); + } + } + + private static string RenderFieldValue(ServiceFieldInfo field, ServiceInfo service, string value) + { + var fieldTypeKind = service.GetFieldType(field)!.Kind; + + switch (fieldTypeKind) + { + case ServiceTypeKind.Enum: + case ServiceTypeKind.String: + case ServiceTypeKind.Bytes: + return value; + case ServiceTypeKind.Boolean: + case ServiceTypeKind.Int32: + case ServiceTypeKind.Int64: + case ServiceTypeKind.Decimal: + case ServiceTypeKind.Double: + return $"{value}.toString()"; + case ServiceTypeKind.Dto: + case ServiceTypeKind.Error: case ServiceTypeKind.Object: throw new NotSupportedException("Field type not supported on path/query: " + fieldTypeKind); default: From a33bfd77e39ce66c04cf8df2607cd62b60109cac Mon Sep 17 00:00:00 2001 From: Ed Ball Date: Tue, 11 Oct 2022 09:40:36 -0700 Subject: [PATCH 7/7] Fix example tests. --- example/ts/test/exampleApiTests.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/example/ts/test/exampleApiTests.ts b/example/ts/test/exampleApiTests.ts index d4ccdd8..112bbbb 100644 --- a/example/ts/test/exampleApiTests.ts +++ b/example/ts/test/exampleApiTests.ts @@ -52,7 +52,7 @@ describe('createHttpClient', () => { }).getWidget({}).then(result => { expect(result.error).to.deep.equal({ code: 'InvalidRequest', - message: 'The request field \'id\' is required.' + message: '\'id\' is required.' }) }); }); @@ -67,7 +67,7 @@ describe('createHttpClient', () => { }).then(result => { expect(result.error).to.deep.equal({ code: 'InvalidRequest', - message: 'The request field \'id\' is required.' + message: '\'id\' is required.' }) }); });