From 0031e7efe18533f2bdad8980f0847b0e1690ff3f Mon Sep 17 00:00:00 2001 From: Ege Korkan Date: Mon, 10 Nov 2025 18:17:41 +0900 Subject: [PATCH 01/17] rename examples folder --- .../test/expansionExamples/cborcoap.json | 29 +++++++++++++++++++ .../httpAndMqtt.json | 0 .../noProtocol.json | 0 .../onlyHttp.json | 0 .../onlyMqtt.json | 0 .../secureProtocols.json | 0 6 files changed, 29 insertions(+) create mode 100644 node/td-utils/test/expansionExamples/cborcoap.json rename node/td-utils/test/{examples => protocolDetectionExamples}/httpAndMqtt.json (100%) rename node/td-utils/test/{examples => protocolDetectionExamples}/noProtocol.json (100%) rename node/td-utils/test/{examples => protocolDetectionExamples}/onlyHttp.json (100%) rename node/td-utils/test/{examples => protocolDetectionExamples}/onlyMqtt.json (100%) rename node/td-utils/test/{examples => protocolDetectionExamples}/secureProtocols.json (100%) diff --git a/node/td-utils/test/expansionExamples/cborcoap.json b/node/td-utils/test/expansionExamples/cborcoap.json new file mode 100644 index 0000000..6283aa1 --- /dev/null +++ b/node/td-utils/test/expansionExamples/cborcoap.json @@ -0,0 +1,29 @@ +{ + "@context": "https://www.w3.org/ns/wot-next/td", + "title": "recommended-test-cbor-default", + "form": { + "contentType": "application/cbor", + "base": "coap://[2001:DB8::1]/mything/", + "security": { + "scheme": "nosec" + } + }, + "properties": { + "prop1": { + "type": "string", + "forms": [ + { + "href": "props/prop1" + } + ] + }, + "prop2": { + "type": "string", + "forms": [ + { + "href": "props/prop2" + } + ] + } + } +} \ No newline at end of file diff --git a/node/td-utils/test/examples/httpAndMqtt.json b/node/td-utils/test/protocolDetectionExamples/httpAndMqtt.json similarity index 100% rename from node/td-utils/test/examples/httpAndMqtt.json rename to node/td-utils/test/protocolDetectionExamples/httpAndMqtt.json diff --git a/node/td-utils/test/examples/noProtocol.json b/node/td-utils/test/protocolDetectionExamples/noProtocol.json similarity index 100% rename from node/td-utils/test/examples/noProtocol.json rename to node/td-utils/test/protocolDetectionExamples/noProtocol.json diff --git a/node/td-utils/test/examples/onlyHttp.json b/node/td-utils/test/protocolDetectionExamples/onlyHttp.json similarity index 100% rename from node/td-utils/test/examples/onlyHttp.json rename to node/td-utils/test/protocolDetectionExamples/onlyHttp.json diff --git a/node/td-utils/test/examples/onlyMqtt.json b/node/td-utils/test/protocolDetectionExamples/onlyMqtt.json similarity index 100% rename from node/td-utils/test/examples/onlyMqtt.json rename to node/td-utils/test/protocolDetectionExamples/onlyMqtt.json diff --git a/node/td-utils/test/examples/secureProtocols.json b/node/td-utils/test/protocolDetectionExamples/secureProtocols.json similarity index 100% rename from node/td-utils/test/examples/secureProtocols.json rename to node/td-utils/test/protocolDetectionExamples/secureProtocols.json From b1c2e6303a2ddccc4f1475ee6ab86a20f54b7795 Mon Sep 17 00:00:00 2001 From: Ege Korkan Date: Mon, 10 Nov 2025 18:18:58 +0900 Subject: [PATCH 02/17] feat: add td 2.0 expansion code --- node/td-utils/expandingTest.js | 69 ++++++++++++ node/td-utils/index.ts | 1 + node/td-utils/package-lock.json | 29 +++-- node/td-utils/package.json | 2 + node/td-utils/src/expand.ts | 100 ++++++++++++++++++ node/td-utils/test/expansion.test.suite.ts | 53 ++++++++++ node/td-utils/test/expansion.test.ts | 31 ++++++ .../test/protocol-detection.test.suite.ts | 10 +- 8 files changed, 282 insertions(+), 13 deletions(-) create mode 100644 node/td-utils/expandingTest.js create mode 100644 node/td-utils/src/expand.ts create mode 100644 node/td-utils/test/expansion.test.suite.ts create mode 100644 node/td-utils/test/expansion.test.ts diff --git a/node/td-utils/expandingTest.js b/node/td-utils/expandingTest.js new file mode 100644 index 0000000..02c67e5 --- /dev/null +++ b/node/td-utils/expandingTest.js @@ -0,0 +1,69 @@ +const mod = require("./build/src/expand.js"); +const expandTD = mod.expandTD || mod.default || mod; + +const exampleTD = { + "@context": "https://www.w3.org/ns/wot-next/td", + "title": "recommended-test-cbor-default", + "form": { + "contentType": "application/cbor", + "base": "coap://[2001:db8::1]/mything/", + "security": { + "scheme": "nosec" + } + }, + "properties": { + "prop1": { + "type": "string", + "forms": [ + { + "href": "props/prop1" + } + ] + }, + "prop2": { + "type": "string", + "forms": [ + { + "href": "props/prop2" + } + ] + } + } + }; + +/* +const exampleTDExpanded = { + "@context": "https://www.w3.org/ns/wot-next/td", + "title": "recommended-test-cbor-default", + "properties": { + "prop1": { + "type": "string", + "forms": [ + { + "href": "coap://[2001:DB8::1]/mything/props/prop1", + "contentType": "application/cbor", + "security": { + "scheme": "nosec" + } + } + ] + }, + "prop2": { + "type": "string", + "forms": [ + { + "href": "coap://[2001:DB8::1]/mything/props/prop2", + "contentType": "application/cbor", + "security": { + "scheme": "nosec" + } + } + ] + } + } + }; + + */ + +const outputTD = expandTD(exampleTD); +console.log(JSON.stringify(outputTD, null, 2)); \ No newline at end of file diff --git a/node/td-utils/index.ts b/node/td-utils/index.ts index 4e31132..548c16a 100644 --- a/node/td-utils/index.ts +++ b/node/td-utils/index.ts @@ -1 +1,2 @@ export { detectProtocolSchemes } from "./src/detectProtocolSchemes"; +export { expandTD } from "./src/expand"; diff --git a/node/td-utils/package-lock.json b/node/td-utils/package-lock.json index 60f9575..8e0a76c 100644 --- a/node/td-utils/package-lock.json +++ b/node/td-utils/package-lock.json @@ -13,10 +13,12 @@ "@babel/preset-env": "^7.24.7", "@babel/preset-typescript": "^7.24.7", "@types/jest": "^29.5.12", + "@types/node": "^24.10.0", "babel-jest": "^29.7.0", "browserify": "^17.0.0", "jest": "^28.1.3", "terser": "^5.14.2", + "tslib": "^2.8.1", "wot-thing-description-types": "^1.1.0-09-November-2023" } }, @@ -60,6 +62,7 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.7.tgz", "integrity": "sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==", "dev": true, + "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.24.7", @@ -2525,12 +2528,13 @@ } }, "node_modules/@types/node": { - "version": "20.14.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.9.tgz", - "integrity": "sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg==", + "version": "24.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.0.tgz", + "integrity": "sha512-qzQZRBqkFsYyaSWXuEHc2WR9c0a0CXwiE5FWUvn7ZM+vdy1uZLfCunD38UzhuB7YN/J11ndbDBcTmOdxJo9Q7A==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~7.16.0" } }, "node_modules/@types/prettier": { @@ -3232,6 +3236,7 @@ "url": "https://github.com/sponsors/ai" } ], + "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001629", "electron-to-chromium": "^1.4.796", @@ -6721,6 +6726,13 @@ "node": ">=8.0" } }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, "node_modules/tty-browserify": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.1.tgz", @@ -6780,10 +6792,11 @@ } }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.0", diff --git a/node/td-utils/package.json b/node/td-utils/package.json index 2e53cf2..733868f 100644 --- a/node/td-utils/package.json +++ b/node/td-utils/package.json @@ -21,10 +21,12 @@ "@babel/preset-env": "^7.24.7", "@babel/preset-typescript": "^7.24.7", "@types/jest": "^29.5.12", + "@types/node": "^24.10.0", "babel-jest": "^29.7.0", "browserify": "^17.0.0", "jest": "^28.1.3", "terser": "^5.14.2", + "tslib": "^2.8.1", "wot-thing-description-types": "^1.1.0-09-November-2023" }, "author": "Eclipse Thingweb (https://thingweb.io/)", diff --git a/node/td-utils/src/expand.ts b/node/td-utils/src/expand.ts new file mode 100644 index 0000000..6ee2ab7 --- /dev/null +++ b/node/td-utils/src/expand.ts @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2025 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the W3C Software Notice and + * Document License (2015-05-13) which is available at + * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document. + * + * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 + */ + +import { + AnyUri, + Form, + ThingDescription, + PropertyElement, + ActionElement, + EventElement, +} from "wot-thing-description-types"; + +const topLevelFormKey = "form"; +const topLevelFormDefinitions = "formDefinitions"; +const topLevelConnectionKey = "connection"; +const topLevelConnectionDefinitions = "connectionDefinitions"; +const topLevelSecurityKey = "security"; +const topLevelSecurityDefinitions = "securityDefinitions"; + +export function expandTD(inputTD: ThingDescription): ThingDescription|undefined { + + // finding default form(s) based on the top level "form" and "formDefinitions" keys + if (topLevelFormKey in inputTD) { + const topLevelForm = (inputTD as any)[topLevelFormKey]; + let defaultForm: any = {}; + if (Array.isArray(topLevelForm)) { + if (topLevelForm.length > 1) { + // TODO: multiple forms: choose how to handle; for now, keep first as default + } else if (topLevelForm.length === 1) { + defaultForm = (inputTD as any)[topLevelFormDefinitions]?.[topLevelForm[0]]; + } else if (topLevelForm.length === 0) { + throw new Error("Empty form array is not allowed"); + } else { + // should not be possible. throw error + throw new Error("Badly formatted form array"); + } + } else if (typeof topLevelForm === "object" && topLevelForm !== null) { + defaultForm = topLevelForm; + } else { + // only object or array is allowed. return error + throw new Error("Only object or array is allowed for the form key in the top level"); + } + // Helper function to expand forms for an interaction affordance + function expandForms(element: PropertyElement | ActionElement | EventElement, defaultForm: any) { + for (const formElement of element.forms) { + // if base is present in defaultForm and the href in the formElement is relative, expand it + if ("base" in defaultForm) { + formElement.href = new URL(formElement.href as string, defaultForm.base as string).toString(); + } + // go through each key in defaultForm and add to formElement if not present + for (const key in defaultForm) { + if (key !== "base" && !(key in formElement)) { + (formElement as any)[key] = (defaultForm as any)[key]; + } + } + } + } + + // Expand forms for properties + if ("properties" in inputTD) { + const properties = inputTD.properties; + for (const propertyKey in properties) { + expandForms(properties[propertyKey] as PropertyElement, defaultForm); + } + } + + // Expand forms for actions + if ("actions" in inputTD) { + const actions = inputTD.actions; + for (const actionKey in actions) { + expandForms(actions[actionKey] as ActionElement, defaultForm); + } + } + + // Expand forms for events + if ("events" in inputTD) { + const events = inputTD.events; + for (const eventKey in events) { + expandForms(events[eventKey] as EventElement, defaultForm); + } + } + return inputTD; + } else { + // no top level form to expand. There can be connection etc. in the top level. For now, we return input + // TODO: handle this properly + return inputTD; + } +} diff --git a/node/td-utils/test/expansion.test.suite.ts b/node/td-utils/test/expansion.test.suite.ts new file mode 100644 index 0000000..4c0ff69 --- /dev/null +++ b/node/td-utils/test/expansion.test.suite.ts @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2025 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the W3C Software Notice and + * Document License (2015-05-13) which is available at + * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document. + * + * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 + */ + +import cborcoap from "./expansionExamples/cborcoap.json"; + +export const testSuite = [ + { + name: "cborcoap", + input: cborcoap, + expected: { + "@context": "https://www.w3.org/ns/wot-next/td", + "title": "recommended-test-cbor-default", + "properties": { + "prop1": { + "type": "string", + "forms": [ + { + "href": "coap://[2001:db8::1]/mything/props/prop1", + "contentType": "application/cbor", + "security": { + "scheme": "nosec" + } + } + ] + }, + "prop2": { + "type": "string", + "forms": [ + { + "href": "coap://[2001:db8::1]/mything/props/prop2", + "contentType": "application/cbor", + "security": { + "scheme": "nosec" + } + } + ] + } + } + } +}, +]; diff --git a/node/td-utils/test/expansion.test.ts b/node/td-utils/test/expansion.test.ts new file mode 100644 index 0000000..f3a27ea --- /dev/null +++ b/node/td-utils/test/expansion.test.ts @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2025 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the W3C Software Notice and + * Document License (2015-05-13) which is available at + * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document. + * + * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 + */ +import { expandTD } from "../src/expand"; +import { ThingDescription } from "wot-thing-description-types"; +import { testSuite } from "./expansion.test.suite"; + +describe("test examples", () => { + testSuite.forEach((t: typeof testSuite[number]) => { + it(`should test ${t.name}`, () => { + testTD(t.input, t.expected); + }); + }); +}); + +const testTD = (tdCompact: any, tdExpanded: any) => { + const generatedTD = expandTD(tdCompact); + + expect(generatedTD).toMatchObject(tdExpanded); +}; diff --git a/node/td-utils/test/protocol-detection.test.suite.ts b/node/td-utils/test/protocol-detection.test.suite.ts index 75a0252..0a841ee 100644 --- a/node/td-utils/test/protocol-detection.test.suite.ts +++ b/node/td-utils/test/protocol-detection.test.suite.ts @@ -1,8 +1,8 @@ -import httpAndMqtt from "./examples/httpAndMqtt.json"; -import noProtocol from "./examples/noProtocol.json"; -import onlyHttp from "./examples/onlyHttp.json"; -import onlyMqtt from "./examples/onlyMqtt.json"; -import secureProtocols from "./examples/secureProtocols.json"; +import httpAndMqtt from "./protocolDetectionExamples/httpAndMqtt.json"; +import noProtocol from "./protocolDetectionExamples/noProtocol.json"; +import onlyHttp from "./protocolDetectionExamples/onlyHttp.json"; +import onlyMqtt from "./protocolDetectionExamples/onlyMqtt.json"; +import secureProtocols from "./protocolDetectionExamples/secureProtocols.json"; export const testSuite = [ { From 9df6a8144eb9ff967c819514e8a961a32880b0c1 Mon Sep 17 00:00:00 2001 From: Ege Korkan Date: Tue, 11 Nov 2025 09:27:36 +0900 Subject: [PATCH 03/17] chore: run formatting --- node/td-utils/expandingTest.js | 56 ++++++++--------- node/td-utils/src/expand.ts | 9 ++- node/td-utils/test/expansion.test.suite.ts | 60 +++++++++---------- node/td-utils/test/expansion.test.ts | 2 +- .../test/expansionExamples/cborcoap.json | 2 +- 5 files changed, 64 insertions(+), 65 deletions(-) diff --git a/node/td-utils/expandingTest.js b/node/td-utils/expandingTest.js index 02c67e5..b092e27 100644 --- a/node/td-utils/expandingTest.js +++ b/node/td-utils/expandingTest.js @@ -1,35 +1,35 @@ const mod = require("./build/src/expand.js"); const expandTD = mod.expandTD || mod.default || mod; -const exampleTD = { +const exampleTD = { "@context": "https://www.w3.org/ns/wot-next/td", - "title": "recommended-test-cbor-default", - "form": { - "contentType": "application/cbor", - "base": "coap://[2001:db8::1]/mything/", - "security": { - "scheme": "nosec" - } + title: "recommended-test-cbor-default", + form: { + contentType: "application/cbor", + base: "coap://[2001:db8::1]/mything/", + security: { + scheme: "nosec", + }, }, - "properties": { - "prop1": { - "type": "string", - "forms": [ - { - "href": "props/prop1" - } - ] - }, - "prop2": { - "type": "string", - "forms": [ - { - "href": "props/prop2" - } - ] - } - } - }; + properties: { + prop1: { + type: "string", + forms: [ + { + href: "props/prop1", + }, + ], + }, + prop2: { + type: "string", + forms: [ + { + href: "props/prop2", + }, + ], + }, + }, +}; /* const exampleTDExpanded = { @@ -66,4 +66,4 @@ const exampleTDExpanded = { */ const outputTD = expandTD(exampleTD); -console.log(JSON.stringify(outputTD, null, 2)); \ No newline at end of file +console.log(JSON.stringify(outputTD, null, 2)); diff --git a/node/td-utils/src/expand.ts b/node/td-utils/src/expand.ts index 6ee2ab7..8fa8e6f 100644 --- a/node/td-utils/src/expand.ts +++ b/node/td-utils/src/expand.ts @@ -29,8 +29,7 @@ const topLevelConnectionDefinitions = "connectionDefinitions"; const topLevelSecurityKey = "security"; const topLevelSecurityDefinitions = "securityDefinitions"; -export function expandTD(inputTD: ThingDescription): ThingDescription|undefined { - +export function expandTD(inputTD: ThingDescription): ThingDescription | undefined { // finding default form(s) based on the top level "form" and "formDefinitions" keys if (topLevelFormKey in inputTD) { const topLevelForm = (inputTD as any)[topLevelFormKey]; @@ -93,8 +92,8 @@ export function expandTD(inputTD: ThingDescription): ThingDescription|undefined } return inputTD; } else { - // no top level form to expand. There can be connection etc. in the top level. For now, we return input - // TODO: handle this properly - return inputTD; + // no top level form to expand. There can be connection etc. in the top level. For now, we return input + // TODO: handle this properly + return inputTD; } } diff --git a/node/td-utils/test/expansion.test.suite.ts b/node/td-utils/test/expansion.test.suite.ts index 4c0ff69..2e2bd29 100644 --- a/node/td-utils/test/expansion.test.suite.ts +++ b/node/td-utils/test/expansion.test.suite.ts @@ -20,34 +20,34 @@ export const testSuite = [ name: "cborcoap", input: cborcoap, expected: { - "@context": "https://www.w3.org/ns/wot-next/td", - "title": "recommended-test-cbor-default", - "properties": { - "prop1": { - "type": "string", - "forms": [ - { - "href": "coap://[2001:db8::1]/mything/props/prop1", - "contentType": "application/cbor", - "security": { - "scheme": "nosec" - } - } - ] - }, - "prop2": { - "type": "string", - "forms": [ - { - "href": "coap://[2001:db8::1]/mything/props/prop2", - "contentType": "application/cbor", - "security": { - "scheme": "nosec" - } - } - ] - } - } - } -}, + "@context": "https://www.w3.org/ns/wot-next/td", + title: "recommended-test-cbor-default", + properties: { + prop1: { + type: "string", + forms: [ + { + href: "coap://[2001:db8::1]/mything/props/prop1", + contentType: "application/cbor", + security: { + scheme: "nosec", + }, + }, + ], + }, + prop2: { + type: "string", + forms: [ + { + href: "coap://[2001:db8::1]/mything/props/prop2", + contentType: "application/cbor", + security: { + scheme: "nosec", + }, + }, + ], + }, + }, + }, + }, ]; diff --git a/node/td-utils/test/expansion.test.ts b/node/td-utils/test/expansion.test.ts index f3a27ea..51233db 100644 --- a/node/td-utils/test/expansion.test.ts +++ b/node/td-utils/test/expansion.test.ts @@ -17,7 +17,7 @@ import { ThingDescription } from "wot-thing-description-types"; import { testSuite } from "./expansion.test.suite"; describe("test examples", () => { - testSuite.forEach((t: typeof testSuite[number]) => { + testSuite.forEach((t: (typeof testSuite)[number]) => { it(`should test ${t.name}`, () => { testTD(t.input, t.expected); }); diff --git a/node/td-utils/test/expansionExamples/cborcoap.json b/node/td-utils/test/expansionExamples/cborcoap.json index 6283aa1..8aa362d 100644 --- a/node/td-utils/test/expansionExamples/cborcoap.json +++ b/node/td-utils/test/expansionExamples/cborcoap.json @@ -26,4 +26,4 @@ ] } } -} \ No newline at end of file +} From 0cfb7f6164025381e8a112add9e32f2fdf9de3ac Mon Sep 17 00:00:00 2001 From: Ege Korkan Date: Tue, 11 Nov 2025 09:27:43 +0900 Subject: [PATCH 04/17] chore: update lock file --- package-lock.json | 28 +++++----------------------- 1 file changed, 5 insertions(+), 23 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6c9166a..f59e4e2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1857,6 +1857,7 @@ "resolved": "https://registry.npmjs.org/@octokit/core/-/core-4.2.4.tgz", "integrity": "sha512-rYKilwgzQ7/imScn3M9/pFfUf4I1AZEH3KhyJmtPdE2zfaXAn2mFfUy4FbKewzc2We5y/LlKLj36fWJLKC2SIQ==", "dev": true, + "peer": true, "dependencies": { "@octokit/auth-token": "^3.0.0", "@octokit/graphql": "^5.0.0", @@ -2141,6 +2142,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", "dev": true, + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -3243,29 +3245,6 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, - "node_modules/encoding": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", - "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", - "dev": true, - "optional": true, - "dependencies": { - "iconv-lite": "^0.6.2" - } - }, - "node_modules/encoding/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, - "optional": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -3349,6 +3328,7 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", "dev": true, + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -6060,6 +6040,7 @@ "integrity": "sha512-1qlEeDjX9OKZEryC8i4bA+twNg+lB5RKrozlNwWx/lLJHqWPUfvUTvxh+uxlPYL9KzVReQjUuxMLFMsHNqWUrA==", "dev": true, "hasInstallScript": true, + "peer": true, "dependencies": { "@nrwl/cli": "15.9.7", "@nrwl/tao": "15.9.7", @@ -6759,6 +6740,7 @@ "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", "dev": true, + "peer": true, "bin": { "prettier": "bin-prettier.js" }, From fcce20f5b0c12301d46fe5f0c6ed110955625a52 Mon Sep 17 00:00:00 2001 From: Ege Korkan Date: Tue, 11 Nov 2025 09:43:21 +0900 Subject: [PATCH 05/17] chore: fix prettier to 3.0.0 --- package-lock.json | 13 +++++++------ package.json | 2 +- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index f59e4e2..7c28206 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "eslint-config-prettier": "^8.8.0", "husky": "^7.0.4", "lerna": "^5.4.3", - "prettier": "^2.3.2", + "prettier": "3.0.0", "pretty-quick": "^3.1.1" } }, @@ -6736,16 +6736,17 @@ } }, "node_modules/prettier": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", - "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.0.tgz", + "integrity": "sha512-zBf5eHpwHOGPC47h0zrPyNn+eAEIdEzfywMoYn2XPi0P44Zp0tSq64rq0xAREh4auw2cJZHo9QUob+NqCQky4g==", "dev": true, + "license": "MIT", "peer": true, "bin": { - "prettier": "bin-prettier.js" + "prettier": "bin/prettier.cjs" }, "engines": { - "node": ">=10.13.0" + "node": ">=14" }, "funding": { "url": "https://github.com/prettier/prettier?sponsor=1" diff --git a/package.json b/package.json index 242aa1e..0050960 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "eslint-config-prettier": "^8.8.0", "husky": "^7.0.4", "lerna": "^5.4.3", - "prettier": "^2.3.2", + "prettier": "3.0.0", "pretty-quick": "^3.1.1" }, "scripts": { From 441b05308d8f1c233f6575dbe53b720596ae73f3 Mon Sep 17 00:00:00 2001 From: Ege Korkan Date: Tue, 11 Nov 2025 10:32:45 +0900 Subject: [PATCH 06/17] chore: fix eslint errors --- .eslintignore | 11 + .eslintrc.cjs | 33 +++ .eslintrc.js | 13 + eslint.config.cjs | 52 ++++ package-lock.json | 692 ++++++++++++++++++++++++---------------------- package.json | 13 +- 6 files changed, 469 insertions(+), 345 deletions(-) create mode 100644 .eslintignore create mode 100644 .eslintrc.cjs create mode 100644 .eslintrc.js create mode 100644 eslint.config.cjs diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..d76d47d --- /dev/null +++ b/.eslintignore @@ -0,0 +1,11 @@ +# Ignore built distributions and generated bundles +node/*/dist/** +node/**/dist/** +**/web-bundle*.js +**/web-bundle*.min.js +**/bundle*.js +**/*.min.js +# Keep node_modules excluded +node_modules/** +# Ignore other generated directories +**/dist/** diff --git a/.eslintrc.cjs b/.eslintrc.cjs new file mode 100644 index 0000000..e12c35a --- /dev/null +++ b/.eslintrc.cjs @@ -0,0 +1,33 @@ +// Flat ESLint config compatible with ESLint v9 +module.exports = [ + { + ignores: ["dist", "node_modules", "bin"], + languageOptions: { + parserOptions: { + ecmaVersion: 8, + }, + globals: { + // browser globals + window: "readonly", + document: "readonly", + navigator: "readonly", + // node globals + global: "readonly", + process: "readonly", + module: "readonly", + require: "readonly", + __dirname: "readonly", + __filename: "readonly", + // jest globals + describe: "readonly", + it: "readonly", + test: "readonly", + expect: "readonly", + beforeEach: "readonly", + afterEach: "readonly", + jest: "readonly", + }, + }, + extends: ["eslint:recommended", "prettier"], + }, +]; diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..493f284 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,13 @@ +module.exports = { + env: { + browser: true, + es6: true, + node: true, + jest: true, + }, + parserOptions: { + ecmaVersion: 8, + }, + extends: ["eslint:recommended", "prettier"], + ignorePatterns: ["dist", "node_modules", "bin"], +}; diff --git a/eslint.config.cjs b/eslint.config.cjs new file mode 100644 index 0000000..b1534d2 --- /dev/null +++ b/eslint.config.cjs @@ -0,0 +1,52 @@ +// Minimal flat ESLint config (eslint v9+) +module.exports = [ + { + // Apply to JS/TS source files + files: ["**/*.{js,ts,jsx,tsx}"], + // Ignore top-level/build artifacts and package bundles. Keep linting focused on source files. + ignores: [ + "dist", + "dist/**", + "node_modules", + "node_modules/**", + "bin", + "bin/**", + // ignore built distributions inside node/* packages + "node/*/dist", + "node/*/dist/**", + "node/**/dist", + "node/**/dist/**", + // ignore generated web bundles and minified bundles + "node/**/web-bundle*.js", + "node/**/web-bundle*.min.js", + "node/**/bundle*.js", + "**/*.min.js", + ], + languageOptions: { + parserOptions: { + ecmaVersion: 8, + }, + globals: { + // browser globals + window: "readonly", + document: "readonly", + navigator: "readonly", + // node globals + global: "readonly", + process: "readonly", + module: "readonly", + require: "readonly", + __dirname: "readonly", + __filename: "readonly", + // jest globals + describe: "readonly", + it: "readonly", + test: "readonly", + expect: "readonly", + beforeEach: "readonly", + afterEach: "readonly", + jest: "readonly", + }, + }, + }, +]; diff --git a/package-lock.json b/package-lock.json index 7c28206..1171a5d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,22 +7,12 @@ "name": "Thingweb TD Tools", "license": "EPL-2.0 OR W3C-20150513", "devDependencies": { - "@microsoft/eslint-formatter-sarif": "^2.1.7", - "eslint": "^8.22.0", - "eslint-config-prettier": "^8.8.0", + "eslint": "^9.39.1", + "eslint-config-prettier": "^10.1.8", "husky": "^7.0.4", "lerna": "^5.4.3", "prettier": "3.0.0", - "pretty-quick": "^3.1.1" - } - }, - "node_modules/@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", - "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", - "dev": true, - "engines": { - "node": ">=0.10.0" + "pretty-quick": "^4.2.2" } }, "node_modules/@babel/code-frame": { @@ -204,39 +194,99 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", + "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", "dev": true, + "license": "MIT", "dependencies": { - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.3" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, + "funding": { + "url": "https://opencollective.com/eslint" + }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/@eslint-community/regexpp": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", - "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", "dev": true, + "license": "MIT", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, + "node_modules/@eslint/config-array": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", + "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", "dev": true, + "license": "MIT", "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", + "espree": "^10.0.1", + "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", @@ -244,19 +294,47 @@ "strip-json-comments": "^3.1.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/@eslint/js": { - "version": "8.56.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", - "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", + "version": "9.39.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.1.tgz", + "integrity": "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==", "dev": true, + "license": "MIT", "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@gar/promisify": { @@ -265,18 +343,28 @@ "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", "dev": true }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.14", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", - "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@humanwhocodes/object-schema": "^2.0.2", - "debug": "^4.3.1", - "minimatch": "^3.0.5" + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" }, "engines": { - "node": ">=10.10.0" + "node": ">=18.18.0" } }, "node_modules/@humanwhocodes/module-importer": { @@ -284,6 +372,7 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">=12.22" }, @@ -292,11 +381,19 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", - "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", - "dev": true + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } }, "node_modules/@hutson/parse-repository-url": { "version": "3.0.2", @@ -1371,18 +1468,6 @@ "node": "^14.15.0 || >=16.0.0" } }, - "node_modules/@microsoft/eslint-formatter-sarif": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/@microsoft/eslint-formatter-sarif/-/eslint-formatter-sarif-2.1.7.tgz", - "integrity": "sha512-gDNc2elHjX0eqk34HxxRxEwEL49SrvXImOoK1bZHq7IDYfuY1xY/CUx8/gOWgvwf6Qv2Yy3HirzjIvKXKH82vQ==", - "dev": true, - "dependencies": { - "eslint": "^8.9.0", - "jschardet": "latest", - "lodash": "^4.17.14", - "utf8": "^3.0.0" - } - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -2039,6 +2124,19 @@ "url": "https://opencollective.com/parcel" } }, + "node_modules/@pkgr/core": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz", + "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/pkgr" + } + }, "node_modules/@tootallnate/once": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", @@ -2048,6 +2146,20 @@ "node": ">= 10" } }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/minimatch": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz", @@ -2072,12 +2184,6 @@ "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", "dev": true }, - "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", - "dev": true - }, "node_modules/@yarnpkg/lockfile": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", @@ -2138,10 +2244,11 @@ "dev": true }, "node_modules/acorn": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", - "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, + "license": "MIT", "peer": true, "bin": { "acorn": "bin/acorn" @@ -2155,6 +2262,7 @@ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, + "license": "MIT", "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } @@ -2207,6 +2315,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -3005,10 +3114,11 @@ } }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, + "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -3107,7 +3217,8 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/defaults": { "version": "1.0.4", @@ -3182,18 +3293,6 @@ "node": ">=8" } }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/dot-prop": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz", @@ -3316,6 +3415,7 @@ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -3324,113 +3424,125 @@ } }, "node_modules/eslint": { - "version": "8.56.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", - "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", + "version": "9.39.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.1.tgz", + "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.56.0", - "@humanwhocodes/config-array": "^0.11.13", + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.1", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.39.1", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", "ajv": "^6.12.4", "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", + "cross-spawn": "^7.0.6", "debug": "^4.3.2", - "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", + "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" + "optionator": "^0.9.3" }, "bin": { "eslint": "bin/eslint.js" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } } }, "node_modules/eslint-config-prettier": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.10.0.tgz", - "integrity": "sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==", + "version": "10.1.8", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, + "license": "MIT", "bin": { "eslint-config-prettier": "bin/cli.js" }, + "funding": { + "url": "https://opencollective.com/eslint-config-prettier" + }, "peerDependencies": { "eslint": ">=7.0.0" } }, "node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, + "license": "Apache-2.0", "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "acorn": "^8.9.0", + "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" + "eslint-visitor-keys": "^4.2.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" @@ -3450,10 +3562,11 @@ } }, "node_modules/esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "estraverse": "^5.1.0" }, @@ -3466,6 +3579,7 @@ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "estraverse": "^5.2.0" }, @@ -3478,6 +3592,7 @@ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, + "license": "BSD-2-Clause", "engines": { "node": ">=4.0" } @@ -3487,6 +3602,7 @@ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true, + "license": "BSD-2-Clause", "engines": { "node": ">=0.10.0" } @@ -3556,7 +3672,8 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/fast-glob": { "version": "3.3.2", @@ -3590,13 +3707,15 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/fastq": { "version": "1.17.0", @@ -3632,15 +3751,16 @@ } }, "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, + "license": "MIT", "dependencies": { - "flat-cache": "^3.0.4" + "flat-cache": "^4.0.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=16.0.0" } }, "node_modules/filelist": { @@ -3690,6 +3810,7 @@ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, + "license": "MIT", "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -3711,24 +3832,25 @@ } }, "node_modules/flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, + "license": "MIT", "dependencies": { "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" + "keyv": "^4.5.4" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=16" } }, "node_modules/flatted": { - "version": "3.2.9", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", - "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", - "dev": true + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" }, "node_modules/follow-redirects": { "version": "1.15.5", @@ -4040,6 +4162,7 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, + "license": "ISC", "dependencies": { "is-glob": "^4.0.3" }, @@ -4069,15 +4192,13 @@ } }, "node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -4109,12 +4230,6 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true - }, "node_modules/handlebars": { "version": "4.7.8", "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", @@ -4602,15 +4717,6 @@ "node": ">=8" } }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/is-plain-obj": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", @@ -4749,20 +4855,12 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/jschardet": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/jschardet/-/jschardet-3.0.0.tgz", - "integrity": "sha512-lJH6tJ77V8Nzd5QWRkFYCLc13a3vADkh3r/Fi8HupZGWk2OVVDfnZP8V/VgQgZ+lzW0kG2UGb5hFgt3V3ndotQ==", - "dev": true, - "engines": { - "node": ">=0.1.90" - } - }, "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json-parse-better-errors": { "version": "1.0.2", @@ -4780,13 +4878,15 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json-stringify-nice": { "version": "1.1.4", @@ -4875,6 +4975,7 @@ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, + "license": "MIT", "dependencies": { "json-buffer": "3.0.1" } @@ -4930,6 +5031,7 @@ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, + "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" @@ -5076,6 +5178,7 @@ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, + "license": "MIT", "dependencies": { "p-locate": "^5.0.0" }, @@ -5102,7 +5205,8 @@ "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/log-symbols": { "version": "4.1.0", @@ -5600,6 +5704,7 @@ "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } @@ -5648,7 +5753,8 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/negotiator": { "version": "0.6.3", @@ -6276,17 +6382,18 @@ } }, "node_modules/optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, + "license": "MIT", "dependencies": { - "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" }, "engines": { "node": ">= 0.8.0" @@ -6338,6 +6445,7 @@ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, + "license": "MIT", "dependencies": { "yocto-queue": "^0.1.0" }, @@ -6353,6 +6461,7 @@ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, + "license": "MIT", "dependencies": { "p-limit": "^3.0.2" }, @@ -6633,10 +6742,11 @@ } }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" }, "node_modules/picomatch": { "version": "2.3.1", @@ -6731,6 +6841,7 @@ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.8.0" } @@ -6753,135 +6864,51 @@ } }, "node_modules/pretty-quick": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/pretty-quick/-/pretty-quick-3.3.1.tgz", - "integrity": "sha512-3b36UXfYQ+IXXqex6mCca89jC8u0mYLqFAN5eTQKoXO6oCQYcIVYZEB/5AlBHI7JPYygReM2Vv6Vom/Gln7fBg==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/pretty-quick/-/pretty-quick-4.2.2.tgz", + "integrity": "sha512-uAh96tBW1SsD34VhhDmWuEmqbpfYc/B3j++5MC/6b3Cb8Ow7NJsvKFhg0eoGu2xXX+o9RkahkTK6sUdd8E7g5w==", "dev": true, + "license": "MIT", "dependencies": { - "execa": "^4.1.0", - "find-up": "^4.1.0", - "ignore": "^5.3.0", + "@pkgr/core": "^0.2.7", + "ignore": "^7.0.5", "mri": "^1.2.0", - "picocolors": "^1.0.0", - "picomatch": "^3.0.1", - "tslib": "^2.6.2" + "picocolors": "^1.1.1", + "picomatch": "^4.0.2", + "tinyexec": "^0.3.2", + "tslib": "^2.8.1" }, "bin": { - "pretty-quick": "dist/cli.js" - }, - "engines": { - "node": ">=10.13" - }, - "peerDependencies": { - "prettier": "^2.0.0" - } - }, - "node_modules/pretty-quick/node_modules/execa": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", - "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.0", - "get-stream": "^5.0.0", - "human-signals": "^1.1.1", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.0", - "onetime": "^5.1.0", - "signal-exit": "^3.0.2", - "strip-final-newline": "^2.0.0" + "pretty-quick": "lib/cli.mjs" }, "engines": { - "node": ">=10" + "node": ">=14" }, "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/pretty-quick/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" + "url": "https://opencollective.com/pretty-quick" }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pretty-quick/node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pretty-quick/node_modules/human-signals": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", - "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", - "dev": true, - "engines": { - "node": ">=8.12.0" - } - }, - "node_modules/pretty-quick/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pretty-quick/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "prettier": "^3.0.0" } }, - "node_modules/pretty-quick/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "node_modules/pretty-quick/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">= 4" } }, "node_modules/pretty-quick/node_modules/picomatch": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-3.0.1.tgz", - "integrity": "sha512-I3EurrIQMlRc9IaAZnqRR044Phh2DXY+55o7uJ0V+hYZAcQYSuFWsc9q5PvyDHUSCe1Qxn/iBz+78s86zWnGag==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" @@ -6966,21 +6993,12 @@ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", "dev": true }, - "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -7811,6 +7829,7 @@ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" }, @@ -7919,12 +7938,6 @@ "node": ">=0.10" } }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true - }, "node_modules/through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -7940,6 +7953,13 @@ "readable-stream": "3" } }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, "node_modules/tmp": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", @@ -8012,16 +8032,18 @@ } }, "node_modules/tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, + "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1" }, @@ -8029,18 +8051,6 @@ "node": ">= 0.8.0" } }, - "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", @@ -8136,16 +8146,11 @@ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.0" } }, - "node_modules/utf8": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz", - "integrity": "sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==", - "dev": true - }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -8244,6 +8249,16 @@ "string-width": "^1.0.2 || 2 || 3 || 4" } }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", @@ -8492,6 +8507,7 @@ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, diff --git a/package.json b/package.json index 0050960..b72d622 100644 --- a/package.json +++ b/package.json @@ -9,18 +9,17 @@ "url": "https://github.com/eclipse-thingweb/td-tools.git" }, "devDependencies": { - "@microsoft/eslint-formatter-sarif": "^2.1.7", - "eslint": "^8.22.0", - "eslint-config-prettier": "^8.8.0", + "eslint": "^9.39.1", + "eslint-config-prettier": "^10.1.8", "husky": "^7.0.4", "lerna": "^5.4.3", "prettier": "3.0.0", - "pretty-quick": "^3.1.1" + "pretty-quick": "^4.2.2" }, "scripts": { - "lint": "npx eslint . --config .eslintrc.json", - "lint:sarif": "npx eslint . --config .eslintrc.json --format @microsoft/eslint-formatter-sarif --output-file eslint-results.sarif", - "lint:fix": "npx eslint . --config .eslintrc.json --fix", + "lint": "npx eslint node/*/src node/*/test *.js", + "lint:sarif": "npx eslint node/*/src node/*/test *.js --format @microsoft/eslint-formatter-sarif --output-file eslint-results.sarif", + "lint:fix": "npx eslint node/*/src node/*/test *.js --fix", "format": "prettier --write . && npm run format --silent --workspaces --if-present", "format:check": "prettier --check .", "format:quick": "pretty-quick" From 96496f81cd73b74238448470ab9b619cd00f8cdd Mon Sep 17 00:00:00 2001 From: Ege Korkan Date: Tue, 11 Nov 2025 11:09:36 +0900 Subject: [PATCH 07/17] Delete node/td-utils/expandingTest.js --- node/td-utils/expandingTest.js | 69 ---------------------------------- 1 file changed, 69 deletions(-) delete mode 100644 node/td-utils/expandingTest.js diff --git a/node/td-utils/expandingTest.js b/node/td-utils/expandingTest.js deleted file mode 100644 index b092e27..0000000 --- a/node/td-utils/expandingTest.js +++ /dev/null @@ -1,69 +0,0 @@ -const mod = require("./build/src/expand.js"); -const expandTD = mod.expandTD || mod.default || mod; - -const exampleTD = { - "@context": "https://www.w3.org/ns/wot-next/td", - title: "recommended-test-cbor-default", - form: { - contentType: "application/cbor", - base: "coap://[2001:db8::1]/mything/", - security: { - scheme: "nosec", - }, - }, - properties: { - prop1: { - type: "string", - forms: [ - { - href: "props/prop1", - }, - ], - }, - prop2: { - type: "string", - forms: [ - { - href: "props/prop2", - }, - ], - }, - }, -}; - -/* -const exampleTDExpanded = { - "@context": "https://www.w3.org/ns/wot-next/td", - "title": "recommended-test-cbor-default", - "properties": { - "prop1": { - "type": "string", - "forms": [ - { - "href": "coap://[2001:DB8::1]/mything/props/prop1", - "contentType": "application/cbor", - "security": { - "scheme": "nosec" - } - } - ] - }, - "prop2": { - "type": "string", - "forms": [ - { - "href": "coap://[2001:DB8::1]/mything/props/prop2", - "contentType": "application/cbor", - "security": { - "scheme": "nosec" - } - } - ] - } - } - }; - - */ - -const outputTD = expandTD(exampleTD); -console.log(JSON.stringify(outputTD, null, 2)); From 89063d9e0daaaebca26453fd99472a683975c2a1 Mon Sep 17 00:00:00 2001 From: Ege Korkan Date: Tue, 11 Nov 2025 11:11:12 +0900 Subject: [PATCH 08/17] chore: preserve quotes for copy pasting --- .prettierrc.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.prettierrc.json b/.prettierrc.json index 57717ef..0457bf8 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -11,5 +11,6 @@ "tabWidth": 2 } } - ] + ], + "quoteProps": "preserve" } From 27f137e4e5e8a30bcd1d72ecf6a758700b223686 Mon Sep 17 00:00:00 2001 From: Ege Korkan Date: Tue, 11 Nov 2025 11:12:09 +0900 Subject: [PATCH 09/17] test: move test input to ts file --- node/td-utils/test/expansion.test.suite.ts | 242 ++++++++++++++++-- .../test/expansionExamples/cborcoap.json | 29 --- 2 files changed, 222 insertions(+), 49 deletions(-) delete mode 100644 node/td-utils/test/expansionExamples/cborcoap.json diff --git a/node/td-utils/test/expansion.test.suite.ts b/node/td-utils/test/expansion.test.suite.ts index 2e2bd29..b8e5f55 100644 --- a/node/td-utils/test/expansion.test.suite.ts +++ b/node/td-utils/test/expansion.test.suite.ts @@ -13,36 +13,155 @@ * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 */ -import cborcoap from "./expansionExamples/cborcoap.json"; - export const testSuite = [ { name: "cborcoap", - input: cborcoap, + input: { + "@context": "https://www.w3.org/ns/wot-next/td", + "title": "recommended-test-cbor-default", + "form": { + "contentType": "application/cbor", + "base": "coap://[2001:DB8::1]/mything/", + "security": { + "scheme": "nosec", + }, + }, + "properties": { + "prop1": { + "type": "string", + "forms": [ + { + "href": "props/prop1", + }, + ], + }, + "prop2": { + "type": "string", + "forms": [ + { + "href": "props/prop2", + }, + ], + }, + }, + "actions": { + "act1": { + "input": { "type": "string" }, + "forms": [ + { + "href": "actions/act1", + }, + ], + }, + }, + "events": { + "evt1": { + "data": { "type": "string" }, + "forms": [ + { + "href": "events/evt1", + }, + ], + }, + }, + }, expected: { "@context": "https://www.w3.org/ns/wot-next/td", - title: "recommended-test-cbor-default", - properties: { - prop1: { - type: "string", - forms: [ - { - href: "coap://[2001:db8::1]/mything/props/prop1", - contentType: "application/cbor", - security: { - scheme: "nosec", + "title": "recommended-test-cbor-default", + "properties": { + "prop1": { + "type": "string", + "forms": [ + { + "href": "coap://[2001:db8::1]/mything/props/prop1", + "contentType": "application/cbor", + "security": { + "scheme": "nosec", }, }, ], }, - prop2: { - type: "string", - forms: [ + "prop2": { + "type": "string", + "forms": [ + { + "href": "coap://[2001:db8::1]/mything/props/prop2", + "contentType": "application/cbor", + "security": { + "scheme": "nosec", + }, + }, + ], + }, + }, + "actions": { + "act1": { + "input": { "type": "string" }, + "forms": [ + { + "href": "coap://[2001:db8::1]/mything/actions/act1", + "contentType": "application/cbor", + "security": { + "scheme": "nosec", + }, + }, + ], + }, + }, + "events": { + "evt1": { + "data": { "type": "string" }, + "forms": [ + { + "href": "coap://[2001:db8::1]/mything/events/evt1", + "contentType": "application/cbor", + "security": { + "scheme": "nosec", + }, + }, + ], + }, + }, + }, + }, + { + name: "cborcoap-alternate-input", + input: { + "@context": "https://www.w3.org/ns/wot-next/td", + "title": "recommended-test-cbor-default", + "formDefinitions": { + "f1": { + "contentType": "application/cbor", + "base": "coap://[2001:DB8::1]/mything/", + "security": { + "scheme": "nosec", + }, + }, + }, + "form": ["f1"], + "properties": { + "prop1": { + "type": "string", + "forms": [ + { + "href": "props/prop1", + }, + ], + }, + }, + }, + expected: { + "@context": "https://www.w3.org/ns/wot-next/td", + "title": "recommended-test-cbor-default", + "properties": { + "prop1": { + "type": "string", + "forms": [ { - href: "coap://[2001:db8::1]/mything/props/prop2", - contentType: "application/cbor", - security: { - scheme: "nosec", + "href": "coap://[2001:db8::1]/mything/props/prop1", + "contentType": "application/cbor", + "security": { + "scheme": "nosec", }, }, ], @@ -50,4 +169,87 @@ export const testSuite = [ }, }, }, + // { + // name: "modbus-separate-form-connection", + // input: { + // "@context": "https://www.w3.org/ns/wot-next/td", + // "title": "recommended-test-modbus-params", + // "connection": { + // "base": "modbus+tcp://192.168.178.32:502/1/", + // "modv:timeout": 1000, + // "security": { + // "scheme": "nosec", + // }, + // }, + // "form": { + // "modv:pollingInterval": 5000, + // "modv:zeroBasedAddressing": true, + // "modv:mostSignificantByte": true, + // "modv:mostSignificantWord": true, + // "contentType": "application/octet-stream", + // }, + // "properties": { + // "prop1": { + // "type": "boolean", + // "forms": [ + // { + // "href": "1", + // "modv:entity": "Coil", + // }, + // ], + // }, + // "prop2": { + // "type": "string", + // "forms": [ + // { + // "href": "2?quantity=8", + // "modv:entity": "HoldingRegister", + // }, + // ], + // }, + // }, + // }, + // expected: { + // "@context": "https://www.w3.org/ns/wot-next/td", + // "title": "recommended-test-modbus-params", + // "properties": { + // "prop1": { + // "type": "boolean", + // "forms": [ + // { + // "href": "modbus+tcp://192.168.178.32:502/1/1", + // "modv:entity": "Coil", + // "modv:timeout": 1000, + // "security": { + // "scheme": "nosec", + // }, + // "modv:pollingInterval": 5000, + // "modv:zeroBasedAddressing": true, + // "modv:mostSignificantByte": true, + // "modv:mostSignificantWord": true, + // "contentType": "application/octet-stream", + // }, + // ], + // }, + // "prop2": { + // "type": "string", + // "forms": [ + // { + // "href": "modbus+tcp://192.168.178.32:502/1/2?quantity=8", + // "modv:entity": "HoldingRegister", + // "modv:timeout": 1000, + // "security": { + // "scheme": "nosec", + // }, + // "modv:pollingInterval": 5000, + // "modv:zeroBasedAddressing": true, + // "modv:mostSignificantByte": true, + // "modv:mostSignificantWord": true, + // "contentType": "application/octet-stream", + // }, + // ], + // }, + // }, + // }, + // }, ]; diff --git a/node/td-utils/test/expansionExamples/cborcoap.json b/node/td-utils/test/expansionExamples/cborcoap.json deleted file mode 100644 index 8aa362d..0000000 --- a/node/td-utils/test/expansionExamples/cborcoap.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "@context": "https://www.w3.org/ns/wot-next/td", - "title": "recommended-test-cbor-default", - "form": { - "contentType": "application/cbor", - "base": "coap://[2001:DB8::1]/mything/", - "security": { - "scheme": "nosec" - } - }, - "properties": { - "prop1": { - "type": "string", - "forms": [ - { - "href": "props/prop1" - } - ] - }, - "prop2": { - "type": "string", - "forms": [ - { - "href": "props/prop2" - } - ] - } - } -} From 83fe72087708fbf920d9d541e2e335f3ec545b3a Mon Sep 17 00:00:00 2001 From: Ege Korkan Date: Tue, 11 Nov 2025 11:15:34 +0900 Subject: [PATCH 10/17] ci: update prettier version --- .github/workflows/prettier.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/prettier.yml b/.github/workflows/prettier.yml index 001cf30..1ddf966 100644 --- a/.github/workflows/prettier.yml +++ b/.github/workflows/prettier.yml @@ -18,7 +18,7 @@ jobs: uses: actions/checkout@v4 - name: Install prettier - run: npm install prettier@2.3.2 + run: npm install prettier@3.0.0 - name: Run prettier run: npm run format:check From 997e4e1dde8880313102cf9d5e7e33fd49b1e4db Mon Sep 17 00:00:00 2001 From: Ege Korkan Date: Tue, 11 Nov 2025 11:17:27 +0900 Subject: [PATCH 11/17] ci: remove install versions in ci scripts --- .github/workflows/eslint.yml | 7 ++----- .github/workflows/prettier.yml | 4 ++-- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/.github/workflows/eslint.yml b/.github/workflows/eslint.yml index 9322a9d..c64a3ec 100644 --- a/.github/workflows/eslint.yml +++ b/.github/workflows/eslint.yml @@ -28,11 +28,8 @@ jobs: - name: Checkout code uses: actions/checkout@v4 - - name: Install ESLint - run: | - npm install eslint@8.10.0 - npm install @microsoft/eslint-formatter-sarif@2.1.7 - npm install eslint-config-prettier@8.8.0 + - name: Install Dependencies + run: npm install - name: Run ESLint run: "npm run lint:sarif" diff --git a/.github/workflows/prettier.yml b/.github/workflows/prettier.yml index 1ddf966..4dfb856 100644 --- a/.github/workflows/prettier.yml +++ b/.github/workflows/prettier.yml @@ -17,8 +17,8 @@ jobs: - name: Checkout code uses: actions/checkout@v4 - - name: Install prettier - run: npm install prettier@3.0.0 + - name: Install Dependencies + run: npm install - name: Run prettier run: npm run format:check From 440529942b747f9eb280a3b952a688a7c5b254ac Mon Sep 17 00:00:00 2001 From: Ege Korkan Date: Tue, 11 Nov 2025 12:30:29 +0900 Subject: [PATCH 12/17] add negative test cases --- node/td-utils/src/expand.ts | 110 +++++++---- node/td-utils/test/expansion.test.suite.ts | 211 +++++++++++++-------- node/td-utils/test/expansion.test.ts | 17 +- 3 files changed, 215 insertions(+), 123 deletions(-) diff --git a/node/td-utils/src/expand.ts b/node/td-utils/src/expand.ts index 8fa8e6f..56bb1c9 100644 --- a/node/td-utils/src/expand.ts +++ b/node/td-utils/src/expand.ts @@ -30,15 +30,41 @@ const topLevelSecurityKey = "security"; const topLevelSecurityDefinitions = "securityDefinitions"; export function expandTD(inputTD: ThingDescription): ThingDescription | undefined { + let defaultForm: any = {}; + let defaultConnection: any = {}; + // finding default connection(s) based on the top level "connection" and "connectionDefinitions" keys + if (topLevelConnectionKey in inputTD) { + const topLevelConnection = (inputTD as any)[topLevelConnectionKey]; + + if (Array.isArray(topLevelConnection)) { + if (topLevelConnection.length > 1) { + // TODO: multiple connection: choose how to handle; for now, keep first as default + } else if (topLevelConnection.length === 1) { + defaultConnection = (inputTD as any)[topLevelConnectionDefinitions]?.[topLevelConnection[0]]; + delete inputTD.connection; + } else if (topLevelConnection.length === 0) { + throw new Error("Empty connection array is not allowed"); + } else { + // should not be possible. throw error + throw new Error("Badly formatted connection array"); + } + } else if (typeof topLevelConnection === "object" && topLevelConnection !== null) { + defaultConnection = topLevelConnection; + delete inputTD.connection; + } else { + // only object or array is allowed. return error + throw new Error("Only object or array is allowed for the connection key in the top level"); + } + } // finding default form(s) based on the top level "form" and "formDefinitions" keys if (topLevelFormKey in inputTD) { const topLevelForm = (inputTD as any)[topLevelFormKey]; - let defaultForm: any = {}; if (Array.isArray(topLevelForm)) { if (topLevelForm.length > 1) { // TODO: multiple forms: choose how to handle; for now, keep first as default } else if (topLevelForm.length === 1) { defaultForm = (inputTD as any)[topLevelFormDefinitions]?.[topLevelForm[0]]; + delete inputTD.form; } else if (topLevelForm.length === 0) { throw new Error("Empty form array is not allowed"); } else { @@ -47,53 +73,63 @@ export function expandTD(inputTD: ThingDescription): ThingDescription | undefine } } else if (typeof topLevelForm === "object" && topLevelForm !== null) { defaultForm = topLevelForm; + delete inputTD.form; } else { // only object or array is allowed. return error - throw new Error("Only object or array is allowed for the form key in the top level"); + throw new Error("Only non-empty object or array is allowed for the form key in the top level"); } - // Helper function to expand forms for an interaction affordance - function expandForms(element: PropertyElement | ActionElement | EventElement, defaultForm: any) { - for (const formElement of element.forms) { - // if base is present in defaultForm and the href in the formElement is relative, expand it - if ("base" in defaultForm) { - formElement.href = new URL(formElement.href as string, defaultForm.base as string).toString(); - } - // go through each key in defaultForm and add to formElement if not present - for (const key in defaultForm) { - if (key !== "base" && !(key in formElement)) { - (formElement as any)[key] = (defaultForm as any)[key]; - } + // adding default connection to default form if there is only one of each + } else { + // no top level form to expand. There can be connection etc. in the top level. For now, we return input + // TODO: handle this properly + return inputTD; + } + + // if defaultForm and defaultConnection are both present, merge them. defaultForm takes precedence + if (Object.keys(defaultForm).length > 0 && Object.keys(defaultConnection).length > 0) { + defaultForm = { ...defaultConnection, ...defaultForm }; + } else if (Object.keys(defaultConnection).length > 0) { + defaultForm = defaultConnection; + } + + // Helper function to expand forms for an interaction affordance + function expandForms(element: PropertyElement | ActionElement | EventElement, defaultForm: any) { + for (const formElement of element.forms) { + // if base is present in defaultForm and the href in the formElement is relative, expand it + if ("base" in defaultForm) { + formElement.href = new URL(formElement.href as string, defaultForm.base as string).toString(); + } + // go through each key in defaultForm and add to formElement if not present + for (const key in defaultForm) { + if (key !== "base" && !(key in formElement)) { + (formElement as any)[key] = (defaultForm as any)[key]; } } } + } - // Expand forms for properties - if ("properties" in inputTD) { - const properties = inputTD.properties; - for (const propertyKey in properties) { - expandForms(properties[propertyKey] as PropertyElement, defaultForm); - } + // Expand forms for properties + if ("properties" in inputTD) { + const properties = inputTD.properties; + for (const propertyKey in properties) { + expandForms(properties[propertyKey] as PropertyElement, defaultForm); } + } - // Expand forms for actions - if ("actions" in inputTD) { - const actions = inputTD.actions; - for (const actionKey in actions) { - expandForms(actions[actionKey] as ActionElement, defaultForm); - } + // Expand forms for actions + if ("actions" in inputTD) { + const actions = inputTD.actions; + for (const actionKey in actions) { + expandForms(actions[actionKey] as ActionElement, defaultForm); } + } - // Expand forms for events - if ("events" in inputTD) { - const events = inputTD.events; - for (const eventKey in events) { - expandForms(events[eventKey] as EventElement, defaultForm); - } + // Expand forms for events + if ("events" in inputTD) { + const events = inputTD.events; + for (const eventKey in events) { + expandForms(events[eventKey] as EventElement, defaultForm); } - return inputTD; - } else { - // no top level form to expand. There can be connection etc. in the top level. For now, we return input - // TODO: handle this properly - return inputTD; } + return inputTD; } diff --git a/node/td-utils/test/expansion.test.suite.ts b/node/td-utils/test/expansion.test.suite.ts index b8e5f55..9210ef4 100644 --- a/node/td-utils/test/expansion.test.suite.ts +++ b/node/td-utils/test/expansion.test.suite.ts @@ -13,7 +13,7 @@ * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 */ -export const testSuite = [ +export const positiveTestSuite = [ { name: "cborcoap", input: { @@ -169,87 +169,130 @@ export const testSuite = [ }, }, }, - // { - // name: "modbus-separate-form-connection", - // input: { - // "@context": "https://www.w3.org/ns/wot-next/td", - // "title": "recommended-test-modbus-params", - // "connection": { - // "base": "modbus+tcp://192.168.178.32:502/1/", - // "modv:timeout": 1000, - // "security": { - // "scheme": "nosec", - // }, - // }, - // "form": { - // "modv:pollingInterval": 5000, - // "modv:zeroBasedAddressing": true, - // "modv:mostSignificantByte": true, - // "modv:mostSignificantWord": true, - // "contentType": "application/octet-stream", - // }, - // "properties": { - // "prop1": { - // "type": "boolean", - // "forms": [ - // { - // "href": "1", - // "modv:entity": "Coil", - // }, - // ], - // }, - // "prop2": { - // "type": "string", - // "forms": [ - // { - // "href": "2?quantity=8", - // "modv:entity": "HoldingRegister", - // }, - // ], - // }, - // }, - // }, - // expected: { - // "@context": "https://www.w3.org/ns/wot-next/td", - // "title": "recommended-test-modbus-params", - // "properties": { - // "prop1": { - // "type": "boolean", - // "forms": [ - // { - // "href": "modbus+tcp://192.168.178.32:502/1/1", - // "modv:entity": "Coil", - // "modv:timeout": 1000, - // "security": { - // "scheme": "nosec", - // }, - // "modv:pollingInterval": 5000, - // "modv:zeroBasedAddressing": true, - // "modv:mostSignificantByte": true, - // "modv:mostSignificantWord": true, - // "contentType": "application/octet-stream", - // }, - // ], - // }, - // "prop2": { - // "type": "string", - // "forms": [ - // { - // "href": "modbus+tcp://192.168.178.32:502/1/2?quantity=8", - // "modv:entity": "HoldingRegister", - // "modv:timeout": 1000, - // "security": { - // "scheme": "nosec", - // }, - // "modv:pollingInterval": 5000, - // "modv:zeroBasedAddressing": true, - // "modv:mostSignificantByte": true, - // "modv:mostSignificantWord": true, - // "contentType": "application/octet-stream", - // }, - // ], - // }, - // }, - // }, - // }, + { + name: "modbus-separate-form-connection", + input: { + "@context": "https://www.w3.org/ns/wot-next/td", + "title": "recommended-test-modbus-params", + "connection": { + "base": "modbus+tcp://192.168.178.32:502/1/", + "modv:timeout": 1000, + "security": { + "scheme": "nosec", + }, + }, + "form": { + "modv:pollingInterval": 5000, + "modv:zeroBasedAddressing": true, + "modv:mostSignificantByte": true, + "modv:mostSignificantWord": true, + "contentType": "application/octet-stream", + }, + "properties": { + "prop1": { + "type": "boolean", + "forms": [ + { + "href": "1", + "modv:entity": "Coil", + }, + ], + }, + "prop2": { + "type": "string", + "forms": [ + { + "href": "2?quantity=8", + "modv:entity": "HoldingRegister", + }, + ], + }, + }, + }, + expected: { + "@context": "https://www.w3.org/ns/wot-next/td", + "title": "recommended-test-modbus-params", + "properties": { + "prop1": { + "type": "boolean", + "forms": [ + { + "href": "modbus+tcp://192.168.178.32:502/1/1", + "modv:entity": "Coil", + "modv:timeout": 1000, + "security": { + "scheme": "nosec", + }, + "modv:pollingInterval": 5000, + "modv:zeroBasedAddressing": true, + "modv:mostSignificantByte": true, + "modv:mostSignificantWord": true, + "contentType": "application/octet-stream", + }, + ], + }, + "prop2": { + "type": "string", + "forms": [ + { + "href": "modbus+tcp://192.168.178.32:502/1/2?quantity=8", + "modv:entity": "HoldingRegister", + "modv:timeout": 1000, + "security": { + "scheme": "nosec", + }, + "modv:pollingInterval": 5000, + "modv:zeroBasedAddressing": true, + "modv:mostSignificantByte": true, + "modv:mostSignificantWord": true, + "contentType": "application/octet-stream", + }, + ], + }, + }, + }, + }, +]; + +export const negativeTestSuite = [ + { + name: "empty-connection-object", + input: { + "@context": "https://www.w3.org/ns/wot-next/td", + "title": "recommended-test-modbus-params", + "connection": {}, + "properties": { + "prop1": { + "type": "boolean", + "forms": [ + { + "href": "1", + "modv:entity": "Coil", + }, + ], + }, + }, + }, + expected: "Only non-empty object or array is allowed for the form key in the top level", + }, + { + name: "empty-connection-object", + input: { + "@context": "https://www.w3.org/ns/wot-next/td", + "title": "recommended-test-modbus-params", + "form": {}, + "properties": { + "prop1": { + "type": "boolean", + "forms": [ + { + "href": "1", + "modv:entity": "Coil", + }, + ], + }, + }, + }, + expected: "Only non-empty object or array is allowed for the form key in the top level", + }, ]; diff --git a/node/td-utils/test/expansion.test.ts b/node/td-utils/test/expansion.test.ts index 51233db..1f2d44e 100644 --- a/node/td-utils/test/expansion.test.ts +++ b/node/td-utils/test/expansion.test.ts @@ -14,16 +14,29 @@ */ import { expandTD } from "../src/expand"; import { ThingDescription } from "wot-thing-description-types"; -import { testSuite } from "./expansion.test.suite"; +import { positiveTestSuite } from "./expansion.test.suite"; +import { negativeTestSuite } from "./expansion.test.suite"; describe("test examples", () => { - testSuite.forEach((t: (typeof testSuite)[number]) => { + positiveTestSuite.forEach((t: (typeof positiveTestSuite)[number]) => { it(`should test ${t.name}`, () => { testTD(t.input, t.expected); }); }); }); +describe("test negative examples", () => { + negativeTestSuite.forEach((t: (typeof negativeTestSuite)[number]) => { + it(`should test ${t.name}`, () => { + try { + const generatedTD = expandTD(t.input as any); + } catch (error) { + expect((error as Error).message).toBe(t.expected); + } + }); + }); +}); + const testTD = (tdCompact: any, tdExpanded: any) => { const generatedTD = expandTD(tdCompact); From 858f50680ecde7010a47d4371141c6fc45be7665 Mon Sep 17 00:00:00 2001 From: Ege Korkan Date: Tue, 11 Nov 2025 12:43:38 +0900 Subject: [PATCH 13/17] handle negative test cases --- node/td-utils/src/expand.ts | 8 ++++++++ node/td-utils/test/expansion.test.suite.ts | 6 +++--- node/td-utils/test/expansion.test.ts | 1 + 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/node/td-utils/src/expand.ts b/node/td-utils/src/expand.ts index 56bb1c9..4bd96b0 100644 --- a/node/td-utils/src/expand.ts +++ b/node/td-utils/src/expand.ts @@ -49,6 +49,10 @@ export function expandTD(inputTD: ThingDescription): ThingDescription | undefine throw new Error("Badly formatted connection array"); } } else if (typeof topLevelConnection === "object" && topLevelConnection !== null) { + // Check if object is empty + if (Object.keys(topLevelConnection).length === 0) { + throw new Error("Empty connection object is not allowed"); + } defaultConnection = topLevelConnection; delete inputTD.connection; } else { @@ -72,6 +76,10 @@ export function expandTD(inputTD: ThingDescription): ThingDescription | undefine throw new Error("Badly formatted form array"); } } else if (typeof topLevelForm === "object" && topLevelForm !== null) { + // Check if object is empty + if (Object.keys(topLevelForm).length === 0) { + throw new Error("Empty form object is not allowed"); + } defaultForm = topLevelForm; delete inputTD.form; } else { diff --git a/node/td-utils/test/expansion.test.suite.ts b/node/td-utils/test/expansion.test.suite.ts index 9210ef4..9c77051 100644 --- a/node/td-utils/test/expansion.test.suite.ts +++ b/node/td-utils/test/expansion.test.suite.ts @@ -273,10 +273,10 @@ export const negativeTestSuite = [ }, }, }, - expected: "Only non-empty object or array is allowed for the form key in the top level", + expected: "Empty connection object is not allowed", }, { - name: "empty-connection-object", + name: "empty-form-object", input: { "@context": "https://www.w3.org/ns/wot-next/td", "title": "recommended-test-modbus-params", @@ -293,6 +293,6 @@ export const negativeTestSuite = [ }, }, }, - expected: "Only non-empty object or array is allowed for the form key in the top level", + expected: "Empty form object is not allowed", }, ]; diff --git a/node/td-utils/test/expansion.test.ts b/node/td-utils/test/expansion.test.ts index 1f2d44e..3b07215 100644 --- a/node/td-utils/test/expansion.test.ts +++ b/node/td-utils/test/expansion.test.ts @@ -31,6 +31,7 @@ describe("test negative examples", () => { try { const generatedTD = expandTD(t.input as any); } catch (error) { + console.error("❌ Caught error:", (error as Error).message); expect((error as Error).message).toBe(t.expected); } }); From b994a5fdb5b6b1a73df1d175d82077d3b36c5e0f Mon Sep 17 00:00:00 2001 From: Ege Korkan Date: Tue, 11 Nov 2025 16:42:22 +0900 Subject: [PATCH 14/17] feat: handle multi connection and forms --- node/td-utils/src/expand.ts | 129 ++++++++++++++++++++++++++---------- 1 file changed, 95 insertions(+), 34 deletions(-) diff --git a/node/td-utils/src/expand.ts b/node/td-utils/src/expand.ts index 4bd96b0..5ca1825 100644 --- a/node/td-utils/src/expand.ts +++ b/node/td-utils/src/expand.ts @@ -30,15 +30,25 @@ const topLevelSecurityKey = "security"; const topLevelSecurityDefinitions = "securityDefinitions"; export function expandTD(inputTD: ThingDescription): ThingDescription | undefined { + // in case of single form or connectin let defaultForm: any = {}; let defaultConnection: any = {}; + + // in case of multiple form or connections + let defaultFormArray: any = []; + let defaultConnectionArray: any = []; + // finding default connection(s) based on the top level "connection" and "connectionDefinitions" keys if (topLevelConnectionKey in inputTD) { const topLevelConnection = (inputTD as any)[topLevelConnectionKey]; if (Array.isArray(topLevelConnection)) { if (topLevelConnection.length > 1) { - // TODO: multiple connection: choose how to handle; for now, keep first as default + defaultConnectionArray = topLevelConnection.map((connKey: string) => { + return (inputTD as any)[topLevelConnectionDefinitions]?.[connKey]; + }); + + delete inputTD.connection; } else if (topLevelConnection.length === 1) { defaultConnection = (inputTD as any)[topLevelConnectionDefinitions]?.[topLevelConnection[0]]; delete inputTD.connection; @@ -59,13 +69,20 @@ export function expandTD(inputTD: ThingDescription): ThingDescription | undefine // only object or array is allowed. return error throw new Error("Only object or array is allowed for the connection key in the top level"); } + } else { + // no top level connection to expand. There can be form etc. in the top level. } + // finding default form(s) based on the top level "form" and "formDefinitions" keys if (topLevelFormKey in inputTD) { const topLevelForm = (inputTD as any)[topLevelFormKey]; if (Array.isArray(topLevelForm)) { if (topLevelForm.length > 1) { - // TODO: multiple forms: choose how to handle; for now, keep first as default + defaultFormArray = topLevelForm.map((formKey: string) => { + return (inputTD as any)[topLevelFormDefinitions]?.[formKey]; + }); + + delete inputTD.form; } else if (topLevelForm.length === 1) { defaultForm = (inputTD as any)[topLevelFormDefinitions]?.[topLevelForm[0]]; delete inputTD.form; @@ -86,58 +103,102 @@ export function expandTD(inputTD: ThingDescription): ThingDescription | undefine // only object or array is allowed. return error throw new Error("Only non-empty object or array is allowed for the form key in the top level"); } - // adding default connection to default form if there is only one of each } else { - // no top level form to expand. There can be connection etc. in the top level. For now, we return input - // TODO: handle this properly - return inputTD; + // no top level form to expand. There can be connection etc. in the top level. } - // if defaultForm and defaultConnection are both present, merge them. defaultForm takes precedence + // if defaultForm and defaultConnection are filled with items, merge them. defaultForm takes precedence if (Object.keys(defaultForm).length > 0 && Object.keys(defaultConnection).length > 0) { defaultForm = { ...defaultConnection, ...defaultForm }; } else if (Object.keys(defaultConnection).length > 0) { defaultForm = defaultConnection; } + defaultFormArray[0]=defaultForm; + + // // like above but for the array case. However, each array needs to be merged like a matrix multiplication. form array of length 2 and connection array of length 3 results in 6 forms + if (defaultFormArray.length > 0 && defaultConnectionArray.length > 0) { + const mergedFormArray: any[] = []; + for (const formItem of defaultFormArray) { + for (const connItem of defaultConnectionArray) { + mergedFormArray.push({ ...connItem, ...formItem }); + } + } + defaultFormArray = mergedFormArray; + delete inputTD[topLevelConnectionDefinitions]; + delete inputTD[topLevelFormDefinitions]; + } + // Helper function to expand forms for an interaction affordance - function expandForms(element: PropertyElement | ActionElement | EventElement, defaultForm: any) { - for (const formElement of element.forms) { - // if base is present in defaultForm and the href in the formElement is relative, expand it - if ("base" in defaultForm) { - formElement.href = new URL(formElement.href as string, defaultForm.base as string).toString(); + function expandForms(element: PropertyElement | ActionElement | EventElement, defaultFormArray: any) { + // if single default form and multiple affordance forms, it is fine + // if multiple default forms and single affordance form, it is fine. Need to expand that form array with each default form as potentials + // if multiple default forms and multiple affordance forms, throw error + + // helper to merge a form with a default object (handles base/href expansion and missing keys) + const mergeFormWithDefaults = (original: Form, def: any): Form => { + const newForm: Form = { ...original }; + if ("base" in def && newForm.href) { + try { + newForm.href = new URL(newForm.href as string, def.base as string).toString(); + } catch { + // ignore URL errors and leave href as-is + } } - // go through each key in defaultForm and add to formElement if not present - for (const key in defaultForm) { - if (key !== "base" && !(key in formElement)) { - (formElement as any)[key] = (defaultForm as any)[key]; + for (const key in def) { + if (key === "base") continue; + if (!(key in newForm)) { + (newForm as any)[key] = def[key]; } } + return newForm; + }; + + if (defaultFormArray.length > 1 && element.forms.length > 1) { + throw new Error("Multiple default forms and multiple affordance forms are not allowed together"); + } else if (defaultFormArray.length > 1 && element.forms.length === 1) { + // expand the single form with each default form + const originalForm = element.forms[0]; + element.forms = defaultFormArray.map((defForm: any) => mergeFormWithDefaults(originalForm, defForm)); + return; + } else if (defaultFormArray.length === 1 && element.forms.length >= 1) { + // case for single default form + const def = defaultFormArray[0]; + element.forms = element.forms.map((formElement: Form) => mergeFormWithDefaults(formElement, def)) as [Form, ...Form[]]; + return; } } - // Expand forms for properties - if ("properties" in inputTD) { - const properties = inputTD.properties; - for (const propertyKey in properties) { - expandForms(properties[propertyKey] as PropertyElement, defaultForm); + if (defaultFormArray.length > 0) { + // case for single default form + if ("properties" in inputTD) { + const properties = inputTD.properties; + for (const propertyKey in properties) { + expandForms(properties[propertyKey] as PropertyElement, defaultFormArray); + } } - } - // Expand forms for actions - if ("actions" in inputTD) { - const actions = inputTD.actions; - for (const actionKey in actions) { - expandForms(actions[actionKey] as ActionElement, defaultForm); + if ("actions" in inputTD) { + const actions = inputTD.actions; + for (const actionKey in actions) { + expandForms(actions[actionKey] as ActionElement, defaultFormArray); + } } - } - // Expand forms for events - if ("events" in inputTD) { - const events = inputTD.events; - for (const eventKey in events) { - expandForms(events[eventKey] as EventElement, defaultForm); + if ("events" in inputTD) { + const events = inputTD.events; + for (const eventKey in events) { + expandForms(events[eventKey] as EventElement, defaultFormArray); + } } + return inputTD; + // case for multiple default forms, i.e. multi ip address, multi protocol, multi content type + // [ + // { base: 'http://192.168.1.10:8080/mything' }, + // { base: 'coap://[2001:DB8::1]/mything' } + // ] + + } else { + // no defaults at the top level. Investigate individual forms } - return inputTD; } From 8649802a826202859248dca545a6a49b6e8204c4 Mon Sep 17 00:00:00 2001 From: Ege Korkan Date: Tue, 11 Nov 2025 16:42:42 +0900 Subject: [PATCH 15/17] chore: run format --- node/td-utils/src/expand.ts | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/node/td-utils/src/expand.ts b/node/td-utils/src/expand.ts index 5ca1825..da6f2b9 100644 --- a/node/td-utils/src/expand.ts +++ b/node/td-utils/src/expand.ts @@ -113,7 +113,7 @@ export function expandTD(inputTD: ThingDescription): ThingDescription | undefine } else if (Object.keys(defaultConnection).length > 0) { defaultForm = defaultConnection; } - defaultFormArray[0]=defaultForm; + defaultFormArray[0] = defaultForm; // // like above but for the array case. However, each array needs to be merged like a matrix multiplication. form array of length 2 and connection array of length 3 results in 6 forms if (defaultFormArray.length > 0 && defaultConnectionArray.length > 0) { @@ -128,7 +128,6 @@ export function expandTD(inputTD: ThingDescription): ThingDescription | undefine delete inputTD[topLevelFormDefinitions]; } - // Helper function to expand forms for an interaction affordance function expandForms(element: PropertyElement | ActionElement | EventElement, defaultFormArray: any) { // if single default form and multiple affordance forms, it is fine @@ -139,11 +138,11 @@ export function expandTD(inputTD: ThingDescription): ThingDescription | undefine const mergeFormWithDefaults = (original: Form, def: any): Form => { const newForm: Form = { ...original }; if ("base" in def && newForm.href) { - try { - newForm.href = new URL(newForm.href as string, def.base as string).toString(); - } catch { - // ignore URL errors and leave href as-is - } + try { + newForm.href = new URL(newForm.href as string, def.base as string).toString(); + } catch { + // ignore URL errors and leave href as-is + } } for (const key in def) { if (key === "base") continue; @@ -164,7 +163,10 @@ export function expandTD(inputTD: ThingDescription): ThingDescription | undefine } else if (defaultFormArray.length === 1 && element.forms.length >= 1) { // case for single default form const def = defaultFormArray[0]; - element.forms = element.forms.map((formElement: Form) => mergeFormWithDefaults(formElement, def)) as [Form, ...Form[]]; + element.forms = element.forms.map((formElement: Form) => mergeFormWithDefaults(formElement, def)) as [ + Form, + ...Form[], + ]; return; } } @@ -197,7 +199,6 @@ export function expandTD(inputTD: ThingDescription): ThingDescription | undefine // { base: 'http://192.168.1.10:8080/mything' }, // { base: 'coap://[2001:DB8::1]/mything' } // ] - } else { // no defaults at the top level. Investigate individual forms } From 6fbf9d2c3df166d2fa281ea1350043eabc584148 Mon Sep 17 00:00:00 2001 From: Ege Korkan Date: Tue, 11 Nov 2025 19:33:26 +0900 Subject: [PATCH 16/17] feat: start work on security --- node/td-utils/src/expand.ts | 83 ++++++++-- node/td-utils/test/expansion.test.suite.ts | 168 ++++++++++++++++++++- node/td-utils/test/expansion.test.ts | 2 +- 3 files changed, 236 insertions(+), 17 deletions(-) diff --git a/node/td-utils/src/expand.ts b/node/td-utils/src/expand.ts index da6f2b9..d1bab3c 100644 --- a/node/td-utils/src/expand.ts +++ b/node/td-utils/src/expand.ts @@ -29,14 +29,52 @@ const topLevelConnectionDefinitions = "connectionDefinitions"; const topLevelSecurityKey = "security"; const topLevelSecurityDefinitions = "securityDefinitions"; -export function expandTD(inputTD: ThingDescription): ThingDescription | undefined { - // in case of single form or connectin +// Expand a TD with form/connection/security definitions at the top level into each interaction affordance +export function expandTD(inputTD: any): ThingDescription | undefined { + // in case of single form or connection let defaultForm: any = {}; let defaultConnection: any = {}; + let defaultSecurity: any = {}; // in case of multiple form or connections let defaultFormArray: any = []; let defaultConnectionArray: any = []; + let defaultSecurityArray: any = []; + + // finding default security(s) based on the top level "security" and "securityDefinitions" keys + if (topLevelSecurityKey in inputTD) { + const topLevelSecurity = (inputTD as any)[topLevelSecurityKey]; + + if (Array.isArray(topLevelSecurity)) { + if (topLevelSecurity.length > 1) { + defaultSecurityArray = topLevelSecurity.map((secKey: string) => { + return (inputTD as any)[topLevelSecurityDefinitions]?.[secKey]; + }); + + delete inputTD[topLevelSecurityKey]; + } else if (topLevelSecurity.length === 1) { + defaultSecurity = {security:(inputTD as any)[topLevelSecurityDefinitions]?.[topLevelSecurity[0]]}; + delete inputTD[topLevelSecurityKey]; + } else if (topLevelSecurity.length === 0) { + throw new Error("Empty security array is not allowed"); + } else { + // should not be possible. throw error + throw new Error("Badly formatted security array"); + } + } else if (typeof topLevelSecurity === "object" && topLevelSecurity !== null) { + // Check if object is empty + if (Object.keys(topLevelSecurity).length === 0) { + throw new Error("Empty security object is not allowed"); + } + defaultSecurity = {security:topLevelSecurity}; + delete inputTD[topLevelSecurityKey]; + } else { + // only object or array is allowed. return error + throw new Error("Only object or array is allowed for the security key in the top level"); + } + } else { + // no top level security to expand. There can be in a form or connection in the top level. + } // finding default connection(s) based on the top level "connection" and "connectionDefinitions" keys if (topLevelConnectionKey in inputTD) { @@ -48,10 +86,10 @@ export function expandTD(inputTD: ThingDescription): ThingDescription | undefine return (inputTD as any)[topLevelConnectionDefinitions]?.[connKey]; }); - delete inputTD.connection; + delete inputTD[topLevelConnectionKey]; } else if (topLevelConnection.length === 1) { defaultConnection = (inputTD as any)[topLevelConnectionDefinitions]?.[topLevelConnection[0]]; - delete inputTD.connection; + delete inputTD[topLevelConnectionKey]; } else if (topLevelConnection.length === 0) { throw new Error("Empty connection array is not allowed"); } else { @@ -64,7 +102,7 @@ export function expandTD(inputTD: ThingDescription): ThingDescription | undefine throw new Error("Empty connection object is not allowed"); } defaultConnection = topLevelConnection; - delete inputTD.connection; + delete inputTD[topLevelConnectionKey]; } else { // only object or array is allowed. return error throw new Error("Only object or array is allowed for the connection key in the top level"); @@ -82,10 +120,10 @@ export function expandTD(inputTD: ThingDescription): ThingDescription | undefine return (inputTD as any)[topLevelFormDefinitions]?.[formKey]; }); - delete inputTD.form; + delete inputTD[topLevelFormKey]; } else if (topLevelForm.length === 1) { defaultForm = (inputTD as any)[topLevelFormDefinitions]?.[topLevelForm[0]]; - delete inputTD.form; + delete inputTD[topLevelFormKey]; } else if (topLevelForm.length === 0) { throw new Error("Empty form array is not allowed"); } else { @@ -98,7 +136,7 @@ export function expandTD(inputTD: ThingDescription): ThingDescription | undefine throw new Error("Empty form object is not allowed"); } defaultForm = topLevelForm; - delete inputTD.form; + delete inputTD[topLevelFormKey]; } else { // only object or array is allowed. return error throw new Error("Only non-empty object or array is allowed for the form key in the top level"); @@ -107,13 +145,29 @@ export function expandTD(inputTD: ThingDescription): ThingDescription | undefine // no top level form to expand. There can be connection etc. in the top level. } - // if defaultForm and defaultConnection are filled with items, merge them. defaultForm takes precedence - if (Object.keys(defaultForm).length > 0 && Object.keys(defaultConnection).length > 0) { - defaultForm = { ...defaultConnection, ...defaultForm }; - } else if (Object.keys(defaultConnection).length > 0) { - defaultForm = defaultConnection; + console.log("Default form before merging:", defaultForm); + console.log("Default connection before merging:", defaultConnection); + console.log("Default security before merging:", defaultSecurity); + + // if defaultForm, defaultConnection and defaultSecurity are filled with items, merge them. defaultForm > defaultConnection > defaultSecurity + // The merging happens in groups of two but only if both are objects and not empty due to initialization as {} + if (typeof defaultConnection === "object" && Object.keys(defaultConnection).length > 0 && typeof defaultSecurity === "object" && Object.keys(defaultSecurity).length > 0) { + if (Object.keys(defaultConnection).length > 0 && Object.keys(defaultSecurity).length > 0) { + defaultConnection = { defaultSecurity, ...defaultConnection }; + } else if (Object.keys(defaultSecurity).length > 0) { + defaultConnection = defaultSecurity; + } + } + + console.log("Default connection after merging security:", defaultConnection); + if (typeof defaultConnection === "object" && Object.keys(defaultConnection).length > 0 && typeof defaultForm === "object" && Object.keys(defaultForm).length > 0) { + if (Object.keys(defaultForm).length > 0 && Object.keys(defaultConnection).length > 0) { + defaultForm = { ...defaultConnection, ...defaultForm }; + } else if (Object.keys(defaultConnection).length > 0) { + defaultForm = defaultConnection; + } + defaultFormArray[0] = defaultForm; } - defaultFormArray[0] = defaultForm; // // like above but for the array case. However, each array needs to be merged like a matrix multiplication. form array of length 2 and connection array of length 3 results in 6 forms if (defaultFormArray.length > 0 && defaultConnectionArray.length > 0) { @@ -124,6 +178,7 @@ export function expandTD(inputTD: ThingDescription): ThingDescription | undefine } } defaultFormArray = mergedFormArray; + console.log("Merged default form array:", defaultFormArray); delete inputTD[topLevelConnectionDefinitions]; delete inputTD[topLevelFormDefinitions]; } diff --git a/node/td-utils/test/expansion.test.suite.ts b/node/td-utils/test/expansion.test.suite.ts index 9c77051..3355251 100644 --- a/node/td-utils/test/expansion.test.suite.ts +++ b/node/td-utils/test/expansion.test.suite.ts @@ -40,6 +40,7 @@ export const positiveTestSuite = [ "forms": [ { "href": "props/prop2", + "contentType": "application/json", }, ], }, @@ -86,7 +87,7 @@ export const positiveTestSuite = [ "forms": [ { "href": "coap://[2001:db8::1]/mything/props/prop2", - "contentType": "application/cbor", + "contentType": "application/json", "security": { "scheme": "nosec", }, @@ -125,7 +126,7 @@ export const positiveTestSuite = [ }, }, { - name: "cborcoap-alternate-input", + name: "cborcoap-alternate-input-1", input: { "@context": "https://www.w3.org/ns/wot-next/td", "title": "recommended-test-cbor-default", @@ -169,6 +170,50 @@ export const positiveTestSuite = [ }, }, }, + { + name: "cborcoap-alternate-input-2", + input: { + "@context": "https://www.w3.org/ns/wot-next/td", + "title": "recommended-test-cbor-default", + "form": { + "contentType": "application/cbor", + }, + "connection":{ + "base": "coap://[2001:DB8::1]/mything/", + }, + "security": { + "scheme": "nosec" + }, + "properties": { + "prop1": { + "type": "string", + "forms": [ + { + "href": "props/prop1", + }, + ], + }, + }, + }, + expected: { + "@context": "https://www.w3.org/ns/wot-next/td", + "title": "recommended-test-cbor-default", + "properties": { + "prop1": { + "type": "string", + "forms": [ + { + "href": "coap://[2001:db8::1]/mything/props/prop1", + "contentType": "application/cbor", + "security": { + "scheme": "nosec", + }, + }, + ], + }, + }, + }, + }, { name: "modbus-separate-form-connection", input: { @@ -252,6 +297,125 @@ export const positiveTestSuite = [ }, }, }, + { + name: "multi-protocol-multi-contenttype", + input: { + "@context": "https://www.w3.org/ns/wot-next/td", + "title": "recommended-test-multi-protocol", + "connectionDefinitions": { + "http": { + "base": "http://192.168.1.10:8080/mything", + }, + "coap": { + "base": "coap://[2001:DB8::1]/mything", + }, + }, + "formDefinitions": { + "json": { + "contentType": "application/json", + }, + "cbor": { + "contentType": "application/cbor", + }, + }, + "form": ["json", "cbor"], + "connection": ["http", "coap"], + "security": { + "scheme": "nosec", + }, + "properties": { + "prop1": { + "type": "string", + "forms": [ + { + "href": "props/prop1", + }, + ], + }, + "prop2": { + "type": "string", + "forms": [ + { + "href": "props/prop2", + }, + ], + }, + }, + }, + expected: { + "@context": "https://www.w3.org/ns/wot-next/td", + "title": "recommended-test-multi-protocol", + + "properties": { + "prop1": { + "type": "string", + "forms": [ + { + "href": "http://192.168.1.10:8080/props/prop1", + "contentType": "application/json", + "security": { + "scheme": "nosec", + }, + }, + { + "href": "coap://[2001:db8::1]/props/prop1", + "contentType": "application/json", + "security": { + "scheme": "nosec", + }, + }, + { + "href": "http://192.168.1.10:8080/props/prop1", + "contentType": "application/cbor", + "security": { + "scheme": "nosec", + }, + }, + { + "href": "coap://[2001:db8::1]/props/prop1", + "contentType": "application/cbor", + "security": { + "scheme": "nosec", + }, + }, + ], + }, + "prop2": { + "type": "string", + "forms": [ + { + "href": "http://192.168.1.10:8080/props/prop2", + "contentType": "application/json", + "security": { + "scheme": "nosec", + }, + }, + { + "href": "coap://[2001:db8::1]/props/prop2", + "contentType": "application/json", + "security": { + "scheme": "nosec", + }, + }, + { + "href": "http://192.168.1.10:8080/props/prop2", + "contentType": "application/cbor", + "security": { + "scheme": "nosec", + }, + }, + { + "href": "coap://[2001:db8::1]/props/prop2", + "contentType": "application/cbor", + "security": { + "scheme": "nosec", + }, + }, + ], + }, + }, + }, + }, ]; export const negativeTestSuite = [ diff --git a/node/td-utils/test/expansion.test.ts b/node/td-utils/test/expansion.test.ts index 3b07215..c5387c0 100644 --- a/node/td-utils/test/expansion.test.ts +++ b/node/td-utils/test/expansion.test.ts @@ -31,7 +31,7 @@ describe("test negative examples", () => { try { const generatedTD = expandTD(t.input as any); } catch (error) { - console.error("❌ Caught error:", (error as Error).message); + console.error(`❌ ${t.name} Caught error:`, (error as Error).message); expect((error as Error).message).toBe(t.expected); } }); From f576495ea4704ab0ed5e99d87608d2a745281006 Mon Sep 17 00:00:00 2001 From: Ege Korkan Date: Wed, 12 Nov 2025 09:56:05 +0900 Subject: [PATCH 17/17] feat: fix form array filling --- node/td-utils/src/expand.ts | 9 ++++++++- node/td-utils/test/expansion.test.ts | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/node/td-utils/src/expand.ts b/node/td-utils/src/expand.ts index d1bab3c..ec60072 100644 --- a/node/td-utils/src/expand.ts +++ b/node/td-utils/src/expand.ts @@ -166,8 +166,15 @@ export function expandTD(inputTD: any): ThingDescription | undefined { } else if (Object.keys(defaultConnection).length > 0) { defaultForm = defaultConnection; } - defaultFormArray[0] = defaultForm; + } + defaultFormArray[0] = defaultForm; + + console.log("Default form after merging:", defaultForm); + console.log("Default form after merging:", defaultFormArray); + + // TODO: if one is object and the other is array, doesn't work. Way out?: convert the object to array of one and proceed + // TODO: no references in the root // // like above but for the array case. However, each array needs to be merged like a matrix multiplication. form array of length 2 and connection array of length 3 results in 6 forms if (defaultFormArray.length > 0 && defaultConnectionArray.length > 0) { diff --git a/node/td-utils/test/expansion.test.ts b/node/td-utils/test/expansion.test.ts index c5387c0..8003ad7 100644 --- a/node/td-utils/test/expansion.test.ts +++ b/node/td-utils/test/expansion.test.ts @@ -31,7 +31,7 @@ describe("test negative examples", () => { try { const generatedTD = expandTD(t.input as any); } catch (error) { - console.error(`❌ ${t.name} Caught error:`, (error as Error).message); + // console.error(`❌ ${t.name} Caught error:`, (error as Error).message); expect((error as Error).message).toBe(t.expected); } });