@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.
@@ -0,0 +1,183 @@
1
+ "use strict";
2
+ /**
3
+ * NativeCoapTransport — connects to the edge-coap Go binary over TCP.
4
+ *
5
+ * The Go binary listens on a local TCP socket (default 127.0.0.1:15003) and
6
+ * accepts newline-delimited JSON send requests. Inbound datagrams are pushed
7
+ * back as `message` events; token correlation stays in the gateway.
8
+ */
9
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ var desc = Object.getOwnPropertyDescriptor(m, k);
12
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
13
+ desc = { enumerable: true, get: function() { return m[k]; } };
14
+ }
15
+ Object.defineProperty(o, k2, desc);
16
+ }) : (function(o, m, k, k2) {
17
+ if (k2 === undefined) k2 = k;
18
+ o[k2] = m[k];
19
+ }));
20
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
21
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
22
+ }) : function(o, v) {
23
+ o["default"] = v;
24
+ });
25
+ var __importStar = (this && this.__importStar) || (function () {
26
+ var ownKeys = function(o) {
27
+ ownKeys = Object.getOwnPropertyNames || function (o) {
28
+ var ar = [];
29
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
30
+ return ar;
31
+ };
32
+ return ownKeys(o);
33
+ };
34
+ return function (mod) {
35
+ if (mod && mod.__esModule) return mod;
36
+ var result = {};
37
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
38
+ __setModuleDefault(result, mod);
39
+ return result;
40
+ };
41
+ })();
42
+ Object.defineProperty(exports, "__esModule", { value: true });
43
+ exports.NativeCoapTransport = void 0;
44
+ let requestCounter = 0;
45
+ class NativeCoapTransport {
46
+ constructor(config = {}) {
47
+ this.socket = null;
48
+ this.buffer = '';
49
+ this.pending = new Map();
50
+ this.messageHandlers = [];
51
+ this.errorHandlers = [];
52
+ this.closedByUser = false;
53
+ this.config = {
54
+ host: config.host ?? '127.0.0.1',
55
+ port: config.port ?? 15003,
56
+ connectTimeoutMs: config.connectTimeoutMs ?? 5000,
57
+ requestTimeoutMs: config.requestTimeoutMs ?? 10000,
58
+ };
59
+ }
60
+ emitError(error) {
61
+ for (const handler of this.errorHandlers) {
62
+ try {
63
+ handler(error);
64
+ }
65
+ catch { }
66
+ }
67
+ }
68
+ async bind(_port) {
69
+ const net = await Promise.resolve().then(() => __importStar(require('net')));
70
+ this.closedByUser = false;
71
+ this.buffer = '';
72
+ return new Promise((resolve, reject) => {
73
+ let connected = false;
74
+ const timer = setTimeout(() => {
75
+ this.socket?.destroy();
76
+ reject(new Error('NativeCoapTransport: connect timeout'));
77
+ }, this.config.connectTimeoutMs);
78
+ this.socket = net.createConnection({ host: this.config.host, port: this.config.port }, () => {
79
+ clearTimeout(timer);
80
+ connected = true;
81
+ resolve();
82
+ });
83
+ this.socket.on('data', (chunk) => {
84
+ this.buffer += chunk.toString('utf-8');
85
+ const lines = this.buffer.split('\n');
86
+ this.buffer = lines.pop() ?? '';
87
+ for (const line of lines) {
88
+ if (!line.trim())
89
+ continue;
90
+ try {
91
+ const msg = JSON.parse(line);
92
+ if (msg.type === 'message') {
93
+ if (typeof msg.host !== 'string' || typeof msg.port !== 'number' || typeof msg.data !== 'string')
94
+ continue;
95
+ const data = hexToBytes(msg.data);
96
+ for (const handler of this.messageHandlers) {
97
+ try {
98
+ handler(data, { host: msg.host, port: msg.port });
99
+ }
100
+ catch { }
101
+ }
102
+ }
103
+ else if (msg.type === 'error') {
104
+ this.emitError(new Error(`NativeCoapTransport: ${msg.code ?? 'error'}: ${msg.error ?? 'unknown error'}`));
105
+ }
106
+ else if (msg.id && this.pending.has(msg.id)) {
107
+ const { resolve: res, reject: rej } = this.pending.get(msg.id);
108
+ this.pending.delete(msg.id);
109
+ if (msg.ok)
110
+ res();
111
+ else
112
+ rej(new Error(msg.error ?? 'unknown error'));
113
+ }
114
+ }
115
+ catch { }
116
+ }
117
+ });
118
+ this.socket.on('error', (err) => {
119
+ if (!connected) {
120
+ clearTimeout(timer);
121
+ this.socket?.destroy();
122
+ reject(err);
123
+ }
124
+ this.emitError(err);
125
+ });
126
+ this.socket.on('close', () => {
127
+ for (const [, p] of this.pending)
128
+ p.reject(new Error('connection closed'));
129
+ this.pending.clear();
130
+ if (!this.closedByUser)
131
+ this.emitError(new Error('NativeCoapTransport: connection closed'));
132
+ });
133
+ });
134
+ }
135
+ async close() {
136
+ this.closedByUser = true;
137
+ if (this.socket) {
138
+ this.socket.destroy();
139
+ this.socket = null;
140
+ }
141
+ for (const [, p] of this.pending)
142
+ p.reject(new Error('connection closed'));
143
+ this.pending.clear();
144
+ }
145
+ async send(host, port, message) {
146
+ if (!this.socket || this.socket.destroyed)
147
+ throw new Error('NativeCoapTransport: not connected');
148
+ const id = `coap-${++requestCounter}`;
149
+ const req = { id, host, port, data: bytesToHex(message) };
150
+ return new Promise((resolve, reject) => {
151
+ const timer = setTimeout(() => {
152
+ this.pending.delete(id);
153
+ reject(new Error(`NativeCoapTransport: request ${id} timed out`));
154
+ }, this.config.requestTimeoutMs);
155
+ this.pending.set(id, {
156
+ resolve: () => { clearTimeout(timer); resolve(); },
157
+ reject: (err) => { clearTimeout(timer); reject(err); },
158
+ });
159
+ this.socket.write(JSON.stringify(req) + '\n');
160
+ });
161
+ }
162
+ onMessage(handler) {
163
+ this.messageHandlers.push(handler);
164
+ return () => { this.messageHandlers = this.messageHandlers.filter(h => h !== handler); };
165
+ }
166
+ onError(handler) {
167
+ this.errorHandlers.push(handler);
168
+ return () => { this.errorHandlers = this.errorHandlers.filter(h => h !== handler); };
169
+ }
170
+ }
171
+ exports.NativeCoapTransport = NativeCoapTransport;
172
+ function bytesToHex(bytes) {
173
+ return Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('');
174
+ }
175
+ function hexToBytes(hex) {
176
+ if (hex.length % 2 !== 0)
177
+ throw new Error('Invalid hex string');
178
+ const arr = new Uint8Array(hex.length / 2);
179
+ for (let i = 0; i < arr.length; i++) {
180
+ arr[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
181
+ }
182
+ return arr;
183
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * OSCORE (RFC 8613) security context for Node.js.
3
+ *
4
+ * The connector stays crypto-free; this adapter implements the OSCORE client
5
+ * and server roles behind `CoapSecurityPort` using Node's `aes-128-ccm` and
6
+ * HKDF-SHA-256. It is validated against the RFC 8613 Appendix C vectors.
7
+ *
8
+ * Supported profile: AES-CCM-16-64-128 (alg 10), HKDF-SHA-256, no Class I
9
+ * options (none are currently defined), and the CoAP fields modeled by
10
+ * `CoapMessage`. Uri-Host/Uri-Port remain outer (Class U); Uri-Path and
11
+ * Uri-Query are inner (Class E). Block options are inner. Blockwise
12
+ * observations and multicast are outside this adapter's scope.
13
+ */
14
+ import type { CoapSecurityPort } from './security.js';
15
+ export interface OscoreContextConfig {
16
+ masterSecret: Uint8Array;
17
+ /** Optional master salt; defaults to the empty byte string. */
18
+ masterSalt?: Uint8Array;
19
+ senderId: Uint8Array;
20
+ recipientId: Uint8Array;
21
+ idContext?: Uint8Array;
22
+ /** Initial sender sequence number (Partial IV). Default 0. */
23
+ senderSequenceNumber?: number;
24
+ /** Anti-replay window for inbound Partial IVs. Default 32. */
25
+ replayWindowSize?: number;
26
+ /** Restrict protection to these endpoints. Default: all endpoints. */
27
+ endpoints?: Array<{
28
+ host: string;
29
+ port: number;
30
+ }>;
31
+ }
32
+ export declare function createOscoreContext(config: OscoreContextConfig): Promise<CoapSecurityPort>;