Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
a883280
Update README.md
sebastiankb Aug 6, 2025
c90bd5c
refactor: activate rule no-unused-private-class-members
danielpeintner Oct 7, 2025
48ea315
refactor: activate eslint n/no-unpublished-import
danielpeintner Oct 7, 2025
ab2e33c
refactor: activate eslint typescript-eslint/prefer-nullish-coalescing
danielpeintner Oct 7, 2025
8089283
refactor: activate eslint typescript-eslint/no-floating-promises
danielpeintner Oct 7, 2025
c656b00
fix: issues in core
danielpeintner Oct 9, 2025
c454306
refactor: revert changing function signature
danielpeintner Oct 9, 2025
6560614
Merge pull request #1438 from danielpeintner/issue-1430-no-unpublishe…
relu91 Oct 24, 2025
28af07c
Merge pull request #1440 from danielpeintner/issue-1430-typescript-es…
relu91 Oct 24, 2025
5ef9dea
docs: revise README
mkovatsc Oct 31, 2025
04d17fe
refactor: remove unused private members
danielpeintner Dec 1, 2025
d734187
Merge pull request #1434 from danielpeintner/issue-1430-no-unused-pri…
relu91 Dec 1, 2025
e02fb15
chore(deps): bump js-yaml
dependabot[bot] Dec 5, 2025
21fd9cd
Merge pull request #1457 from eclipse-thingweb/dependabot/npm_and_yar…
relu91 Dec 19, 2025
9fbd0e0
Merge pull request #1410 from eclipse-thingweb/sebastiankb-modbus-rea…
relu91 Dec 19, 2025
61228d6
chore(deps): bump vm2 from 3.9.18 to 3.10.0
dependabot[bot] Jan 10, 2026
a213417
chore: revisit opcua binding examples
erossignon Oct 18, 2025
6bb2c24
Merge pull request #1452 from node-opcua/ImprovedOPCUAExample
relu91 Jan 14, 2026
3bcd2e9
docs(README): fix link to cli README
relu91 Jan 22, 2026
5f9a49e
Merge pull request #1455 from eclipse-thingweb/mkovatsc-readme-revision
relu91 Jan 22, 2026
5e5f9c7
Support ID-based HTTP paths for exposed Things (#1464)
relu91 Jan 22, 2026
97ea52c
Merge pull request #1460 from eclipse-thingweb/dependabot/npm_and_yar…
relu91 Jan 22, 2026
2e8f3b1
chore(deps): bump lodash from 4.17.21 to 4.17.23
dependabot[bot] Jan 22, 2026
f05617a
Merge pull request #1465 from eclipse-thingweb/dependabot/npm_and_yar…
relu91 Jan 22, 2026
388a07e
Merge branch 'issue-1430-typescript-eslint-no-floating-promises' of h…
relu91 Jan 22, 2026
a27587b
test(core): revert promise catches
relu91 Jan 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 35 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,9 @@ For further information please refer to the official [W3C Web of Things](https:/

## Installation

The framework can be used in two ways: as a library or as a CLI tool. In this section we will explain how to install the framework in both ways.
The framework is composed by different packages that users can use as they please. The core package is @node-wot/core and it is the only mandatory package to install. The other packages are bindings that allow the framework to communicate with different protocols.

### As a library

The framework is composed by different packages that users can use as they please. The core package is `@node-wot/core` and it is the only mandatory package to install. The other packages are bindings that allow the framework to communicate with different protocols.

#### Node.js
### Prerequisite: Node.js with build tools

> [!WARNING]
> We no longer actively support Node.js version 18 and lower.
Expand All @@ -81,27 +77,52 @@ Platforms specific prerequisites:
- Mac OS: Meet the [node-gyp](https://github.com/nodejs/node-gyp#installation) requirements:
- `xcode-select --install`

If you want to use node-wot as a library in your Node.js application, you can use npm to install the node-wot packages that you need. To do so, `cd` inside your application folder, and run:
### As a library

If you want to use node-wot as a library in your Node.js application, you can use npm to install the node-wot packages that you need.
Todo so, `cd` inside your application folder and install at least the mandatory core package:

```
npm i @node-wot/core @node-wot/binding-http --save
npm i @node-wot/core
```

#### Browser
Usually, your application needs at least one protocol binding to commmunicate, e.g.,:

To use node-wot as a browser-side JavaScript Library, the browser needs to support ECMAScript 2015.
```
npm i @node-wot/binding-http
```

Using a browser with only ES5 support (e.g., IE 11) might be possible if you add polyfills. If you want to use node-wot as a library in your browser application, you can install the `@node-wot/browser-bundle` as following:
In case the application shall consume Thing Descriptions that are stored locally, you would also need the `file` binding:

```
npm i @node-wot/browser-bundle --save
npm i @node-wot/binding-file
```

you can find more installation options in the specific [package README](./packages/browser-bundle/README.md).
You see other available bindings in the [packages folder](./packages), which you can install via `npm i @node-wot/<package-name>`.

### In the browser

To use node-wot as JavaScript library insde the Web browser, it needs to support ECMAScript 2015+. Using a browser with only ES5 support (e.g., IE 11) might be possible if you add polyfills.

If you want to use node-wot as a library in your browser application,`cd` inside your application folder and install the browser bundle:

```
npm i @node-wot/browser-bundle
```

You can find more (non-)installation options in the specific [package README](./packages/browser-bundle/README.md).

### As a CLI tool

You can alternatively use node-wot via its command line interface (CLI). Please visit the [CLI tool's Readme](<[url](https://github.com/eclipse-thingweb/node-wot/tree/master/packages/cli)>) to find out more.
You can alternatively use node-wot via its command line interface (CLI). Please visit the [CLI tool README](https://github.com/eclipse-thingweb/node-wot/tree/master/packages/cli) to find out more.

#### As global tool

To make the `wot-servient` command available on your machine, install the CLI tool in the global scope:

```
npm i @node-wot/cli -g
```

#### As a docker image

Expand Down
8 changes: 4 additions & 4 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ export default defineConfig([
"n/no-unsupported-features/node-builtins": "off", // https://github.com/eclipse-thingweb/node-wot/issues/1430
"n/no-extraneous-import": "off", // https://github.com/eclipse-thingweb/node-wot/issues/1430
"n/no-deprecated-api": "off", // https://github.com/eclipse-thingweb/node-wot/issues/1430
"n/no-unpublished-import": "off", // https://github.com/eclipse-thingweb/node-wot/issues/1430
"n/no-unpublished-import": "error",
"n/no-process-exit": "off", // https://github.com/eclipse-thingweb/node-wot/issues/1430
"n/hashbang": "warn",

Expand Down Expand Up @@ -122,9 +122,9 @@ export default defineConfig([
"@typescript-eslint/no-unused-vars": "off",
"@typescript-eslint/no-unused-expressions": "off",
"@typescript-eslint/no-require-imports": "off", // https://github.com/eclipse-thingweb/node-wot/issues/1430
"@typescript-eslint/prefer-nullish-coalescing": "off", // https://github.com/eclipse-thingweb/node-wot/issues/1430
"@typescript-eslint/prefer-nullish-coalescing": "error",
"@typescript-eslint/no-empty-object-type": "off", // https://github.com/eclipse-thingweb/node-wot/issues/1430
"@typescript-eslint/no-floating-promises": "off", // https://github.com/eclipse-thingweb/node-wot/issues/1430
"@typescript-eslint/no-floating-promises": "error",

// **************** Enforce usage of `const` over `let` wherever possible, to prevent accidental reassignments
"prefer-const": "error",
Expand All @@ -135,7 +135,7 @@ export default defineConfig([

"no-use-before-define": "error",

"no-unused-private-class-members": "off", // https://github.com/eclipse-thingweb/node-wot/issues/1430
"no-unused-private-class-members": "error",
"no-prototype-builtins": "off",
"no-case-declarations": "off",

Expand Down
50 changes: 38 additions & 12 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 1 addition & 3 deletions packages/binding-coap/src/coap-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@
private readonly server: Server = createServer(
{ reuseAddr: false },
(req: IncomingMessage, res: OutgoingMessage) => {
this.handleRequest(req, res);

Check failure on line 76 in packages/binding-coap/src/coap-server.ts

View workflow job for this annotation

GitHub Actions / eslint

Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the `void` operator
}
);

Expand Down Expand Up @@ -201,9 +201,7 @@
return;
}

if (thing.forms == null) {
thing.forms = [];
}
thing.forms ??= [];

const form = this.createAffordanceForm(base, this.PROPERTY_DIR, offeredMediaType, opValues, thing.uriVariables);

Expand Down Expand Up @@ -514,13 +512,13 @@

switch (affordanceType) {
case this.PROPERTY_DIR:
this.handlePropertyRequest(thing, affordanceKey, req, res, contentType);

Check failure on line 515 in packages/binding-coap/src/coap-server.ts

View workflow job for this annotation

GitHub Actions / eslint

Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the `void` operator
break;
case this.ACTION_DIR:
this.handleActionRequest(thing, affordanceKey, req, res, contentType);

Check failure on line 518 in packages/binding-coap/src/coap-server.ts

View workflow job for this annotation

GitHub Actions / eslint

Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the `void` operator
break;
case this.EVENT_DIR:
this.handleEventRequest(thing, affordanceKey, req, res, contentType);

Check failure on line 521 in packages/binding-coap/src/coap-server.ts

View workflow job for this annotation

GitHub Actions / eslint

Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the `void` operator
break;
default:
this.sendNotFoundResponse(res);
Expand Down Expand Up @@ -557,16 +555,16 @@
const property = thing.properties[affordanceKey];

if (property == null) {
this.handlePropertiesRequest(req, contentType, thing, res);

Check failure on line 558 in packages/binding-coap/src/coap-server.ts

View workflow job for this annotation

GitHub Actions / eslint

Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the `void` operator
return;
}

switch (req.method) {
case "GET":
if (req.headers.Observe == null) {
this.handleReadProperty(property, req, contentType, thing, res, affordanceKey);

Check failure on line 565 in packages/binding-coap/src/coap-server.ts

View workflow job for this annotation

GitHub Actions / eslint

Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the `void` operator
} else {
this.handleObserveProperty(property, req, contentType, thing, res, affordanceKey);

Check failure on line 567 in packages/binding-coap/src/coap-server.ts

View workflow job for this annotation

GitHub Actions / eslint

Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the `void` operator
}
break;
case "PUT":
Expand All @@ -575,7 +573,7 @@
return;
}

this.handleWriteProperty(property, req, contentType, thing, res, affordanceKey);

Check failure on line 576 in packages/binding-coap/src/coap-server.ts

View workflow job for this annotation

GitHub Actions / eslint

Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the `void` operator
break;
default:
this.sendMethodNotAllowedResponse(res);
Expand All @@ -597,7 +595,7 @@

switch (req.method) {
case "GET":
this.handleReadMultipleProperties(forms, req, contentType, thing, res);

Check failure on line 598 in packages/binding-coap/src/coap-server.ts

View workflow job for this annotation

GitHub Actions / eslint

Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the `void` operator
break;
default:
this.sendMethodNotAllowedResponse(res);
Expand Down
2 changes: 1 addition & 1 deletion packages/binding-http/src/http-client-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,7 @@ export default class HttpClient implements ProtocolClient {

const url = HttpClient.fixLocalhostName(form.href);

requestInit.method = form["htv:methodName"] ? form["htv:methodName"] : defaultMethod;
requestInit.method = form["htv:methodName"] ?? defaultMethod;

requestInit.headers = requestInit.headers ?? [];
requestInit.headers = requestInit.headers as string[][];
Expand Down
64 changes: 40 additions & 24 deletions packages/binding-http/src/http-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ export default class HttpServer implements ProtocolServer {
private readonly address?: string;
private readonly baseUri?: string;
private readonly urlRewrite?: Record<string, string>;
private readonly devFriendlyUri: boolean;
private readonly supportedSecuritySchemes: string[] = ["nosec"];
private readonly validOAuthClients: RegExp = /.*/g;
private readonly server: http.Server | https.Server;
Expand All @@ -83,6 +84,7 @@ export default class HttpServer implements ProtocolServer {
this.baseUri = config.baseUri;
this.urlRewrite = config.urlRewrite;
this.middleware = config.middleware;
this.devFriendlyUri = config.devFriendlyUri ?? true;

const router = Router({
ignoreTrailingSlash: true,
Expand Down Expand Up @@ -267,21 +269,32 @@ export default class HttpServer implements ProtocolServer {
}

public async expose(thing: ExposedThing, tdTemplate: WoT.ExposedThingInit = {}): Promise<void> {
let urlPath = slugify(thing.title, { lower: true });

// avoid URL clashes
if (this.things.has(urlPath)) {
let uniqueUrlPath;
let nameClashCnt = 2;
do {
uniqueUrlPath = urlPath + "_" + nameClashCnt++;
} while (this.things.has(uniqueUrlPath));
urlPath = uniqueUrlPath;
}

if (this.getPort() !== -1) {
debug(`HttpServer on port ${this.getPort()} exposes '${thing.title}' as unique '/${urlPath}'`);
this.things.set(urlPath, thing);
const paths: string[] = [];
// If not id is given we create the path using the title even if devFriendlyUri is false.
// in Thing Description 1.1 id is optional
if (this.devFriendlyUri || thing.id == null) {
let urlPath = slugify(thing.title, { lower: true });

// avoid URL clashes
if (this.things.has(urlPath)) {
let uniqueUrlPath;
let nameClashCnt = 2;
do {
uniqueUrlPath = urlPath + "_" + nameClashCnt++;
} while (this.things.has(uniqueUrlPath));
urlPath = uniqueUrlPath;
}
this.things.set(urlPath, thing);
paths.push(urlPath);
debug("HttpServer on port %d exposes %s as unique '/%s'", this.getPort(), thing.name, urlPath);
}

if (thing.id != null) {
this.things.set(thing.id, thing);
paths.push(thing.id);
debug("HttpServer on port %d exposes %s as unique '/%s'", this.getPort(), thing.name, thing.id);
}

if (this.scheme === "http" && Object.keys(thing.securityDefinitions).length !== 0) {
warn(`HTTP Server will attempt to use your security schemes even if you are not using HTTPS.`);
Expand All @@ -290,16 +303,20 @@ export default class HttpServer implements ProtocolServer {
this.fillSecurityScheme(thing);

if (this.baseUri !== undefined) {
const base: string = this.baseUri.concat("/", encodeURIComponent(urlPath));
info("HttpServer TD hrefs using baseUri " + this.baseUri);
this.addEndpoint(thing, tdTemplate, base);
for (const path of paths) {
info("HttpServer TD hrefs using baseUri %s and path %s", this.baseUri, path);
const base: string = this.baseUri.concat("/", encodeURIComponent(path));
this.addEndpoint(thing, tdTemplate, base);
}
} else {
// fill in binding data
for (const address of Helpers.getAddresses()) {
const base: string =
this.scheme + "://" + address + ":" + this.getPort() + "/" + encodeURIComponent(urlPath);

this.addEndpoint(thing, tdTemplate, base);
for (const path of paths) {
const base: string =
this.scheme + "://" + address + ":" + this.getPort() + "/" + encodeURIComponent(path);
info("HttpServer TD hrefs using address %s and path %s", address, path);
this.addEndpoint(thing, tdTemplate, base);
}
}
}
}
Expand All @@ -311,6 +328,7 @@ export default class HttpServer implements ProtocolServer {
for (const [name, thing] of this.things.entries()) {
if (thing.id === thingId) {
this.things.delete(name);
this.things.delete(thingId);
info(`HttpServer successfully destroyed '${thing.title}'`);

return true;
Expand Down Expand Up @@ -369,9 +387,7 @@ export default class HttpServer implements ProtocolServer {
"writemultipleproperties",
];
}
if (thing.forms == null) {
thing.forms = [];
}
thing.forms ??= [];
thing.forms.push(form);
this.addUrlRewriteEndpoints(form, thing.forms);
}
Expand Down
1 change: 1 addition & 0 deletions packages/binding-http/src/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export interface HttpConfig {
serverKey?: string;
serverCert?: string;
security?: SecurityScheme[];
devFriendlyUri?: boolean;
middleware?: MiddlewareRequestHandler;
}

Expand Down
Loading
Loading