@totemsdk/edge-coap 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -21
- package/NOTICE +60 -0
- package/README.md +201 -1
- package/dist/cbor.d.ts +22 -0
- package/dist/cbor.js +266 -0
- package/dist/cjs/cbor.d.ts +22 -0
- package/dist/cjs/cbor.js +271 -0
- package/dist/cjs/codec.d.ts +30 -0
- package/dist/cjs/codec.js +349 -0
- package/dist/cjs/gateway.d.ts +76 -0
- package/dist/cjs/gateway.js +511 -0
- package/dist/cjs/index.d.ts +13 -0
- package/dist/cjs/index.js +17 -0
- package/dist/cjs/native-transport.d.ts +33 -0
- package/dist/cjs/native-transport.js +183 -0
- package/dist/cjs/oscore.d.ts +32 -0
- package/dist/cjs/oscore.js +562 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/security.d.ts +29 -0
- package/dist/cjs/security.js +19 -0
- package/dist/cjs/sensor-bridge.d.ts +31 -0
- package/dist/cjs/sensor-bridge.js +99 -0
- package/dist/cjs/transport.d.ts +77 -0
- package/dist/cjs/transport.js +9 -0
- package/dist/codec.d.ts +30 -0
- package/dist/codec.js +339 -0
- package/dist/gateway.d.ts +61 -7
- package/dist/gateway.js +470 -67
- package/dist/index.d.ts +11 -3
- package/dist/index.js +6 -7
- package/dist/native-transport.d.ts +33 -0
- package/dist/native-transport.js +146 -0
- package/dist/oscore.d.ts +32 -0
- package/dist/oscore.js +526 -0
- package/dist/security.d.ts +29 -0
- package/dist/security.js +16 -0
- package/dist/sensor-bridge.d.ts +6 -1
- package/dist/sensor-bridge.js +73 -24
- package/dist/transport.d.ts +31 -1
- package/dist/transport.js +1 -2
- package/package.json +55 -26
- package/sidecar.json +34 -0
package/dist/sensor-bridge.js
CHANGED
|
@@ -1,46 +1,95 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
|
-
* CoAP Sensor Bridge — maps CoAP
|
|
2
|
+
* CoAP Sensor Bridge — maps CoAP resources and RFC 7641 observations to Edge
|
|
3
|
+
* proof inputs. CBOR payloads (content-format 60) are decoded into structured
|
|
4
|
+
* claims; malformed CBOR is not signed as sensor data.
|
|
4
5
|
*/
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
function createCoapSensorBridge(config) {
|
|
6
|
+
import { decodeCbor } from './cbor.js';
|
|
7
|
+
export const APPLICATION_CBOR = 60;
|
|
8
|
+
export function createCoapSensorBridge(config) {
|
|
8
9
|
const timers = [];
|
|
10
|
+
const observations = [];
|
|
9
11
|
let running = false;
|
|
12
|
+
function claimsFor(binding, data, receivedAt = Date.now()) {
|
|
13
|
+
const claim = {
|
|
14
|
+
sensorId: binding.sensorId,
|
|
15
|
+
path: binding.path.join('/'),
|
|
16
|
+
dataType: binding.dataType ?? 'coap-observation',
|
|
17
|
+
payload: data.payload,
|
|
18
|
+
timestamp: receivedAt,
|
|
19
|
+
};
|
|
20
|
+
if (data.contentFormat !== undefined)
|
|
21
|
+
claim.contentFormat = data.contentFormat;
|
|
22
|
+
if (data.observe !== undefined)
|
|
23
|
+
claim.observe = data.observe;
|
|
24
|
+
if (data.contentFormat === APPLICATION_CBOR) {
|
|
25
|
+
try {
|
|
26
|
+
claim.cbor = decodeCbor(data.payload);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
// Do not sign a payload that claims to be CBOR but does not decode.
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return claim;
|
|
34
|
+
}
|
|
35
|
+
async function emitProof(binding, data, receivedAt) {
|
|
36
|
+
const proof = config.runtime.ports.proof;
|
|
37
|
+
if (!proof)
|
|
38
|
+
return;
|
|
39
|
+
const claim = claimsFor(binding, data, receivedAt);
|
|
40
|
+
if (!claim)
|
|
41
|
+
return;
|
|
42
|
+
await proof.createProof({ subject: `sensor:${binding.sensorId}`, claims: [claim] });
|
|
43
|
+
}
|
|
10
44
|
async function pollBinding(binding) {
|
|
11
45
|
try {
|
|
12
46
|
const gw = config.gateway;
|
|
13
47
|
if (!gw)
|
|
14
48
|
return;
|
|
15
49
|
const result = await gw.get(binding.path, binding.host, binding.port);
|
|
16
|
-
if (result.ok &&
|
|
17
|
-
await
|
|
18
|
-
subject: `sensor:${binding.sensorId}`,
|
|
19
|
-
claims: [{
|
|
20
|
-
sensorId: binding.sensorId,
|
|
21
|
-
path: binding.path.join('/'),
|
|
22
|
-
dataType: binding.dataType ?? 'coap-observation',
|
|
23
|
-
payload: result.data?.payload,
|
|
24
|
-
timestamp: Date.now(),
|
|
25
|
-
}],
|
|
26
|
-
});
|
|
27
|
-
}
|
|
50
|
+
if (result.ok && result.data)
|
|
51
|
+
await emitProof(binding, result.data);
|
|
28
52
|
}
|
|
29
53
|
catch { /* non-fatal */ }
|
|
30
54
|
}
|
|
55
|
+
async function observeBinding(binding) {
|
|
56
|
+
const gw = config.gateway;
|
|
57
|
+
if (!gw)
|
|
58
|
+
return false;
|
|
59
|
+
const result = await gw.observe(binding.path, binding.host, binding.port, (notification) => {
|
|
60
|
+
void emitProof(binding, notification, notification.receivedAt).catch(() => { });
|
|
61
|
+
});
|
|
62
|
+
if (!result.ok || !result.data)
|
|
63
|
+
return false;
|
|
64
|
+
observations.push(result.data);
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
31
67
|
return {
|
|
32
68
|
async start() {
|
|
33
69
|
if (running)
|
|
34
70
|
return;
|
|
35
|
-
for (const
|
|
36
|
-
|
|
37
|
-
|
|
71
|
+
for (const binding of config.bindings) {
|
|
72
|
+
if (binding.observe) {
|
|
73
|
+
const registered = await observeBinding(binding).catch(() => false);
|
|
74
|
+
if (registered)
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
await pollBinding(binding);
|
|
78
|
+
timers.push(setInterval(() => pollBinding(binding), binding.intervalMs));
|
|
38
79
|
}
|
|
39
80
|
running = true;
|
|
40
81
|
},
|
|
41
|
-
async stop() {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
82
|
+
async stop() {
|
|
83
|
+
for (const timer of timers)
|
|
84
|
+
clearInterval(timer);
|
|
85
|
+
timers.length = 0;
|
|
86
|
+
const active = observations.splice(0);
|
|
87
|
+
await Promise.all(active.map((observation) => observation.cancel().catch(() => { })));
|
|
88
|
+
running = false;
|
|
89
|
+
},
|
|
90
|
+
async poll() {
|
|
91
|
+
for (const binding of config.bindings)
|
|
92
|
+
await pollBinding(binding);
|
|
93
|
+
},
|
|
45
94
|
};
|
|
46
95
|
}
|
package/dist/transport.d.ts
CHANGED
|
@@ -21,7 +21,15 @@ export interface CoapTransportPort {
|
|
|
21
21
|
onError(handler: (err: Error) => void): () => void;
|
|
22
22
|
}
|
|
23
23
|
export type CoapMessageType = 'CON' | 'NON' | 'ACK' | 'RST';
|
|
24
|
-
export type CoapMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
|
|
24
|
+
export type CoapMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'FETCH';
|
|
25
|
+
export interface CoapBlockOption {
|
|
26
|
+
/** Block number. */
|
|
27
|
+
num: number;
|
|
28
|
+
/** More blocks follow. */
|
|
29
|
+
more: boolean;
|
|
30
|
+
/** Size exponent: block size is 2^(sizeExponent + 4), 16..1024 bytes. */
|
|
31
|
+
sizeExponent: number;
|
|
32
|
+
}
|
|
25
33
|
export interface CoapMessage {
|
|
26
34
|
/** Message type. */
|
|
27
35
|
type: CoapMessageType;
|
|
@@ -35,8 +43,30 @@ export interface CoapMessage {
|
|
|
35
43
|
token: Uint8Array;
|
|
36
44
|
/** URI path (e.g. ["sensors", "temperature"]). */
|
|
37
45
|
path: string[];
|
|
46
|
+
/** Uri-Host option (3). Defaults to the destination address when absent. */
|
|
47
|
+
uriHost?: string;
|
|
48
|
+
/** Uri-Port option (7). Defaults to the destination port when absent. */
|
|
49
|
+
uriPort?: number;
|
|
50
|
+
/** Uri-Query options (15), in order. */
|
|
51
|
+
uriQuery?: string[];
|
|
38
52
|
/** Payload bytes. */
|
|
39
53
|
payload: Uint8Array;
|
|
54
|
+
/** Content-Format option (12), e.g. 0 text/plain, 60 application/cbor. */
|
|
55
|
+
contentFormat?: number;
|
|
56
|
+
/** Accept option (17). */
|
|
57
|
+
accept?: number;
|
|
58
|
+
/** Observe option (6): 0 register, 1 deregister, otherwise notification sequence. */
|
|
59
|
+
observe?: number;
|
|
60
|
+
/** OSCORE option (9) bytes, passed through without interpretation. */
|
|
61
|
+
oscore?: Uint8Array;
|
|
62
|
+
/** Block1 option (27) for blockwise requests. */
|
|
63
|
+
block1?: CoapBlockOption;
|
|
64
|
+
/** Block2 option (23) for blockwise responses. */
|
|
65
|
+
block2?: CoapBlockOption;
|
|
66
|
+
/** Size2 option (28): total response body size hint. */
|
|
67
|
+
size2?: number;
|
|
68
|
+
/** Size1 option (60): total request body size hint. */
|
|
69
|
+
size1?: number;
|
|
40
70
|
/** Remote endpoint. */
|
|
41
71
|
remote: {
|
|
42
72
|
host: string;
|
package/dist/transport.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* CoAP transport port — injected by the caller.
|
|
4
3
|
*
|
|
@@ -6,4 +5,4 @@
|
|
|
6
5
|
* Messages are confirmable (CON), non-confirmable (NON),
|
|
7
6
|
* acknowledgement (ACK), or reset (RST).
|
|
8
7
|
*/
|
|
9
|
-
|
|
8
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,43 +1,72 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@totemsdk/edge-coap",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Edge runtime adapter for CoAP — constrained devices, RFC 7252, UDP transport",
|
|
5
|
-
"main": "dist/index.js",
|
|
6
|
-
"types": "dist/index.d.ts",
|
|
5
|
+
"main": "dist/cjs/index.js",
|
|
6
|
+
"types": "dist/cjs/index.d.ts",
|
|
7
7
|
"exports": {
|
|
8
8
|
".": {
|
|
9
|
-
"
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
"import": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"default": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"require": {
|
|
14
|
+
"types": "./dist/cjs/index.d.ts",
|
|
15
|
+
"default": "./dist/cjs/index.js"
|
|
16
|
+
}
|
|
12
17
|
}
|
|
13
18
|
},
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
"files": [
|
|
20
|
+
"dist",
|
|
21
|
+
"sidecar.json",
|
|
22
|
+
"README.md",
|
|
23
|
+
"LICENSE",
|
|
24
|
+
"NOTICE"
|
|
25
|
+
],
|
|
21
26
|
"dependencies": {
|
|
22
|
-
"@totemsdk/edge": "^
|
|
27
|
+
"@totemsdk/edge": "^1.5.1"
|
|
23
28
|
},
|
|
24
29
|
"devDependencies": {
|
|
25
|
-
"@types/jest": "^30.0.0",
|
|
26
30
|
"@types/node": "^20.0.0",
|
|
27
|
-
"jest": "^30.4.2",
|
|
28
|
-
"ts-jest": "^29.0.0",
|
|
29
31
|
"typescript": "^7.0.2"
|
|
30
32
|
},
|
|
31
|
-
"engines": {
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=18.0.0"
|
|
35
|
+
},
|
|
36
|
+
"publishConfig": {
|
|
37
|
+
"access": "public"
|
|
38
|
+
},
|
|
39
|
+
"license": "Apache-2.0",
|
|
34
40
|
"author": "Totem SDK",
|
|
35
|
-
"homepage": "https://totem.ing",
|
|
41
|
+
"homepage": "https://docs.totem.ing",
|
|
36
42
|
"repository": {
|
|
37
43
|
"type": "git",
|
|
38
|
-
"url": "git+https://github.com/
|
|
39
|
-
"directory": "packages/
|
|
44
|
+
"url": "git+https://github.com/Totem-Edge/totem-sdk.git",
|
|
45
|
+
"directory": "packages/edge-coap"
|
|
46
|
+
},
|
|
47
|
+
"keywords": [
|
|
48
|
+
"totem",
|
|
49
|
+
"totemsdk",
|
|
50
|
+
"minima",
|
|
51
|
+
"blockchain",
|
|
52
|
+
"edge",
|
|
53
|
+
"coap",
|
|
54
|
+
"transport"
|
|
55
|
+
],
|
|
56
|
+
"bugs": {
|
|
57
|
+
"url": "https://github.com/Totem-Edge/totem-sdk/issues"
|
|
40
58
|
},
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
}
|
|
59
|
+
"type": "module",
|
|
60
|
+
"scripts": {
|
|
61
|
+
"build": "tsc && tsc -p tsconfig.cjs.json && node -e \"require('fs').writeFileSync('dist/cjs/package.json', JSON.stringify({type:'commonjs'})+'\\n')\"",
|
|
62
|
+
"clean": "rm -rf dist bin",
|
|
63
|
+
"test": "jest --config ../../tests/jest/edge-coap.cjs",
|
|
64
|
+
"build:go": "cd go && go build -o ../bin/edge-coap .",
|
|
65
|
+
"test:go": "cd go && go test -race ./... && go vet ./...",
|
|
66
|
+
"test:conformance": "node --test ../../tests/qualification/edge-coap/coap-conformance.test.mjs",
|
|
67
|
+
"test:integration": "node --test ../../tests/qualification/edge-coap/integration.test.mjs",
|
|
68
|
+
"test:recovery": "node --test ../../tests/qualification/edge-coap/recovery.test.mjs",
|
|
69
|
+
"test:consumer": "node --test ../../tests/qualification/edge-coap/packed-consumer.test.mjs",
|
|
70
|
+
"test:oscore": "node --test ../../tests/qualification/edge-coap/oscore.test.mjs"
|
|
71
|
+
}
|
|
72
|
+
}
|
package/sidecar.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "edge-coap-sidecar",
|
|
3
|
+
"protocolVersion": 1,
|
|
4
|
+
"distribution": {
|
|
5
|
+
"mode": "source-build",
|
|
6
|
+
"bundledInNpmPackage": false,
|
|
7
|
+
"binary": "bin/edge-coap",
|
|
8
|
+
"build": "pnpm --filter @totemsdk/edge-coap build:go",
|
|
9
|
+
"autoDownload": false
|
|
10
|
+
},
|
|
11
|
+
"compatibility": {
|
|
12
|
+
"protocolMajor": 1,
|
|
13
|
+
"healthCheck": "edge-coap --version",
|
|
14
|
+
"rule": "Rebuild the sidecar from the matching package source. The npm package does not bundle platform binaries."
|
|
15
|
+
},
|
|
16
|
+
"configuration": {
|
|
17
|
+
"listen": "COAP_LISTEN_ADDR=127.0.0.1:15003",
|
|
18
|
+
"bindPort": "COAP_BIND_PORT=5683"
|
|
19
|
+
},
|
|
20
|
+
"qualified": [
|
|
21
|
+
"source build on the target platform",
|
|
22
|
+
"plaintext unicast CoAP request/response over UDP",
|
|
23
|
+
"CBOR payload pass-through with TypeScript content-format decoding",
|
|
24
|
+
"RFC 7641 observation registration, notifications and deregistration",
|
|
25
|
+
"OSCORE client and server contexts in the TypeScript layer (RFC 8613 Appendix C vectors)",
|
|
26
|
+
"RFC 7959 blockwise Block1 upload, Block1 size renegotiation and Block2 response assembly"
|
|
27
|
+
],
|
|
28
|
+
"unqualified": [
|
|
29
|
+
"DTLS security (transport-owned)",
|
|
30
|
+
"blockwise observation notifications and multicast",
|
|
31
|
+
"OSCORE Class I options (none currently defined)",
|
|
32
|
+
"Windows prebuilt binaries"
|
|
33
|
+
]
|
|
34
|
+
}
|