geonix 1.23.8 → 1.30.2

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/index.d.ts CHANGED
@@ -1,266 +1,321 @@
1
- export function encode(data: any): Uint8Array<ArrayBuffer>;
2
- export function decode(data: any): unknown;
3
- export const codec: import("nats").Codec<unknown>;
4
- export const connection: Connection;
5
- export function stopConnection(): void;
1
+ /// <reference types="node" />
2
+ import type { Readable } from "stream";
3
+ import type { IncomingMessage } from "http";
4
+ import type { EventEmitter } from "events";
5
+
6
+ // ---------------------------------------------------------------------------
7
+ // Service
8
+ // ---------------------------------------------------------------------------
9
+
10
+ export interface ServiceOptions {
11
+ /** Override the class name used to identify the service on the bus. */
12
+ name?: string;
13
+ /** Override the service version. Falls back to GX_VERSION env var, then a dev default. */
14
+ version?: string;
15
+ /** Optional static ID for targeting a specific instance via Remote("#<id>"). */
16
+ id?: string;
17
+ /** Toggle built-in Express body parsers. All default to true. */
18
+ middleware?: {
19
+ json?: boolean;
20
+ raw?: boolean;
21
+ cookies?: boolean;
22
+ };
23
+ /**
24
+ * When false, only the instance ID is sent in beacon messages (saves bandwidth).
25
+ * Default: true.
26
+ */
27
+ fullBeacon?: boolean;
28
+ /** Express route handlers injected before / after each service endpoint handler. */
29
+ handlers?: {
30
+ before?: ((...args: any[]) => any)[];
31
+ after?: ((...args: any[]) => any)[];
32
+ };
33
+ }
34
+
6
35
  /**
7
- * Connection class is responsible for starting a connection to a NATS server and provides shortcut
8
- * methods for publish, subscribe and request methods of the NATS client.
36
+ * Base class for all Geonix services. Extend this and call `Service.start()` to register on
37
+ * the NATS bus, expose HTTP endpoints, and begin sending presence beacons.
9
38
  *
10
- * It is implemented as an singleton so that multiple services can be implemented in the same
11
- * process while using just a single connection to the NATS server.
39
+ * @example
40
+ * class GreetingService extends Service {
41
+ * async greet(name: string) {
42
+ * return `Hello, ${name}!`;
43
+ * }
44
+ * }
45
+ * GreetingService.start();
12
46
  */
13
- declare class Connection {
47
+ export declare class Service {
14
48
  /**
15
- * Initiates connection to transport
16
- * @param {string} transport
49
+ * Override to hard-code the service version. Ignored when GX_VERSION / VERSION env vars
50
+ * are set, or when `options.version` is passed to `start()`.
17
51
  */
18
- start(transport?: string): Promise<void>;
19
- monitorStatus(): Promise<void>;
20
- /**
21
- * Wait for the connection to be safely closed
22
- */
23
- waitUntilClosed(): Promise<void>;
52
+ version?: string;
53
+ /** Called once after the service has connected and registered on the bus. */
54
+ onStart?(): void | Promise<void>;
55
+ /** Creates an instance of the subclass and starts it on the NATS bus. */
56
+ static start(options?: ServiceOptions): void;
57
+ }
58
+
59
+ // ---------------------------------------------------------------------------
60
+ // Remote
61
+ // ---------------------------------------------------------------------------
62
+
63
+ /**
64
+ * Creates a proxy that forwards property-access calls to a named remote service.
65
+ * Without a type parameter the proxy is fully dynamic (`any`). Pass a service
66
+ * interface as the type parameter for IDE autocompletion and type checking.
67
+ *
68
+ * @param service - Service name, optionally with version (`@^1.2`) or static ID (`#<id>`).
69
+ * @param context - Optional context values forwarded to every remote method call.
70
+ *
71
+ * @example
72
+ * // Fully dynamic — no types required, `as` casts work freely
73
+ * const svc = Remote("GreetingService");
74
+ * const result = await svc.greet("Alice");
75
+ *
76
+ * @example
77
+ * // Typed — opt-in
78
+ * interface GreetingService { greet(name: string): Promise<string> }
79
+ * const svc = Remote<GreetingService>("GreetingService");
80
+ */
81
+ export declare function Remote<T = any>(
82
+ service: string,
83
+ ...context: any[]
84
+ ): T;
85
+
86
+ // ---------------------------------------------------------------------------
87
+ // Stream
88
+ // ---------------------------------------------------------------------------
89
+
90
+ /** A Geonix stream descriptor — the wire representation of a streaming value. */
91
+ export interface StreamDescriptor {
92
+ $: "stream";
93
+ id: string;
94
+ /** HTTP addresses (host:port) where the stream can be fetched. */
95
+ a?: string[];
96
+ }
97
+
98
+ /** Map of in-process stream IDs to their active Readable instances. */
99
+ export declare const activeStreams: Record<string, Readable>;
100
+
101
+ /**
102
+ * Wraps data in a Geonix stream descriptor for transport over NATS or HTTP.
103
+ * Buffers, strings, and Readables are accepted. Already-wrapped descriptors
104
+ * are returned unchanged.
105
+ */
106
+ export declare function Stream(data: Buffer | string | Readable | object): StreamDescriptor;
107
+
108
+ /** Returns `true` if `object` is a Geonix stream descriptor. */
109
+ export declare function isStream(object: unknown): object is StreamDescriptor;
110
+
111
+ /**
112
+ * Resolves a stream descriptor to a Node.js Readable.
113
+ * Prefers HTTP; falls back to NATS. Non-descriptor values are returned as-is.
114
+ */
115
+ export declare function getReadable(object: StreamDescriptor | any): Promise<Readable>;
116
+
117
+ /** Reads a stream or descriptor fully into memory and returns a Buffer. */
118
+ export declare function streamToBuffer(object: Readable | StreamDescriptor): Promise<Buffer>;
119
+
120
+ /** Reads a stream or descriptor fully into memory and returns a string. */
121
+ export declare function streamToString(object: Readable | StreamDescriptor, encoding?: BufferEncoding): Promise<string>;
122
+
123
+ /** Reads a stream or descriptor fully into memory and parses the result as JSON. */
124
+ export declare function streamToJSON(object: Readable | StreamDescriptor): Promise<any>;
125
+
126
+ // ---------------------------------------------------------------------------
127
+ // Gateway
128
+ // ---------------------------------------------------------------------------
129
+
130
+ export interface GatewayOptions {
24
131
  /**
25
- * Wait for the connection to be fully established
132
+ * CORS mode:
133
+ * - omitted / `false` — no CORS headers set
134
+ * - `"*"` — wildcard, all origins, no credentials
135
+ * - `true` — reflect any origin with credentials
136
+ * - `string[]` — allow-list; credentials only for listed origins
26
137
  */
138
+ cors?: boolean | "*" | string[];
139
+ beforeRequest?: (req: any, res: any) => void;
140
+ afterRequest?: (req: any, res: any) => void;
141
+ }
142
+
143
+ /**
144
+ * HTTP / WebSocket gateway that auto-discovers services via the registry and reverse-proxies
145
+ * incoming requests to the appropriate instance.
146
+ */
147
+ export declare class Gateway {
148
+ static start(opts?: GatewayOptions): Gateway;
149
+ stop(): void;
150
+ }
151
+
152
+ // ---------------------------------------------------------------------------
153
+ // Connection
154
+ // ---------------------------------------------------------------------------
155
+
156
+ interface Connection {
157
+ start(transport?: string): Promise<void>;
158
+ monitorStatus(): void;
27
159
  waitUntilReady(): Promise<void>;
28
- /**
29
- * Publish JSON
30
- *
31
- * @param {string} subject
32
- * @param {object} json
33
- * @returns void
34
- */
160
+ waitUntilClosed(): Promise<void>;
35
161
  publish(subject: string, json: object): Promise<void>;
36
- /**
37
- * Publish RAW
38
- *
39
- * @param {string} subject
40
- * @param {string | Buffer} data
41
- * @returns void
42
- */
43
- publishRaw(subject: string, data: string | Buffer): Promise<void>;
44
- /**
45
- * Request/Reply pattern on top of pub/sub
46
- *
47
- * @param {string} subject
48
- * @param {object} json
49
- * @param {object} options
50
- * @returns any
51
- */
52
- request(subject: string, json: object, opts?: {}): Promise<unknown>;
53
- subscribe(subject: any, options: any): Promise<any>;
54
- unsubscribe(subscription: any): Promise<void>;
55
- /**
56
- *
57
- * @returns {number}
58
- */
162
+ publishRaw(subject: string, data?: string | Buffer): Promise<void>;
163
+ subscribe(subject: string, options?: object): Promise<AsyncIterable<any>>;
164
+ unsubscribe(subscription: AsyncIterable<any>): void;
165
+ request(subject: string, json: object, opts?: object): Promise<any>;
59
166
  getMaxPayloadSize(): number;
60
167
  isClosed(): boolean;
61
168
  drain(): Promise<void>;
62
- #private;
63
169
  }
64
- export {};
65
- export class Gateway {
66
- static start(opts: any): Gateway;
67
- constructor(opts: any);
68
- #private;
170
+
171
+ /** Singleton NATS connection pool shared across the process. */
172
+ export declare const connection: Connection;
173
+
174
+ /** Initiates a graceful shutdown by draining all NATS connections. */
175
+ export declare function stopConnection(): void;
176
+
177
+ // ---------------------------------------------------------------------------
178
+ // Registry
179
+ // ---------------------------------------------------------------------------
180
+
181
+ /** A live service entry as seen by the local registry. */
182
+ export interface RegistryEntry {
183
+ /** Unique instance ID (re-generated on each start) */
184
+ i: string;
185
+ /** Service name */
186
+ n: string;
187
+ /** Service version */
188
+ v: string;
189
+ /** Exposed method names */
190
+ m: string[];
191
+ /** Network addresses in `host:port` form */
192
+ a: string[];
193
+ /** Geonix version the service is running */
194
+ gx: string;
195
+ /** Optional static service ID */
196
+ id?: string;
69
197
  }
70
- export class Logger {
71
- constructor(options: any);
72
- info(...args: any[]): void;
73
- error(...args: any[]): void;
74
- debug(...args: any[]): void;
75
- #private;
76
- }
77
- export const logger: Logger;
78
- export const registry: Registry;
79
- /**
80
- * Registry maintains a local list of available services and their versions.
81
- */
82
- declare class Registry {
83
- getEntries(): {};
84
- getIdentifier(service: any, version: any, id: any): any;
85
- #private;
198
+
199
+ interface Registry extends EventEmitter {
200
+ stop(): void;
201
+ getEntries(): Record<string, RegistryEntry>;
202
+ getEntriesForIdentifier(identifier: string): RegistryEntry[];
203
+ getIdentifier(service: string, version?: string, id?: string): string | undefined;
204
+ on(event: "added" | "removed", listener: (entry: RegistryEntry) => void): this;
205
+ once(event: "added" | "removed", listener: (entry: RegistryEntry) => void): this;
206
+ off(event: "added" | "removed", listener: (entry: RegistryEntry) => void): this;
86
207
  }
87
- export {};
88
- export function Remote(service: string, ...context: (string | Stream | Object)[]): string | Stream | Object;
89
- /**
90
- * Send a request to a service
91
- *
92
- * @param {string} service
93
- * @param {string} method
94
- * @param {any[]} args
95
- * @param {any[]} context
96
- * @param {object} options
97
- * @returns
98
- */
99
- export function Request(service: string, method: string, args: any[], context: any[], options: object): Promise<any>;
100
- /**
101
- * Send a request to a service
102
- *
103
- * @param {string} identifier
104
- * @param {string} method
105
- * @param {any[]} args
106
- * @param {any[]} context
107
- * @param {object} options
108
- * @returns
109
- */
110
- export function directRequest(identifier: string, method: string, args: any[], context: any[], options: object, service: any): Promise<any>;
208
+
209
+ /** Singleton Registry instance local view of all live services on the bus. */
210
+ export declare const registry: Registry;
211
+
212
+ // ---------------------------------------------------------------------------
213
+ // Request / Subscribe / Publish
214
+ // ---------------------------------------------------------------------------
215
+
111
216
  /**
112
- * Publish payload to a subject
217
+ * Sends a request to a named service, waiting in the registry if the service is not yet up.
113
218
  *
114
- * @param {string} subject
115
- * @param {string|number} payload
219
+ * @param service - Service name, optionally with version (`@^1.2`) or instance ID (`#<id>`).
220
+ * @param method - Method name to invoke on the remote service.
221
+ * @param args - Arguments. The first element may be a `RequestOptions` sentinel.
222
+ * @param context - Optional context forwarded to the remote method.
116
223
  */
117
- export function Publish(subject: string, payload: string | number): Promise<void>;
224
+ export declare function Request(
225
+ service: string,
226
+ method: string,
227
+ args: any[],
228
+ context?: any[]
229
+ ): Promise<any>;
230
+
231
+ /** Subscribes to a NATS subject and calls `callback` with raw message data for each message. */
232
+ export declare function Subscribe(subject: string, callback: (data: Buffer) => void): Promise<void>;
233
+
234
+ /** Publishes a raw payload to a NATS subject. Fire-and-forget. */
235
+ export declare function Publish(subject: string, payload: string | Buffer): Promise<void>;
236
+
237
+ // ---------------------------------------------------------------------------
238
+ // RequestOptions
239
+ // ---------------------------------------------------------------------------
240
+
241
+ export interface RequestOptionsInput {
242
+ /** Max time in ms to wait for the service to appear in the registry. Default: 300 000. */
243
+ timeout?: number;
244
+ /** Max time in ms to wait for the HTTP RPC response. Default: 5 000. */
245
+ httpTimeout?: number;
246
+ }
247
+
118
248
  /**
119
- * Subscribe to subject
249
+ * Wraps per-call options in a sentinel that `Request` (and `Remote`) detects and extracts
250
+ * when passed as the first argument of a service method call.
120
251
  *
121
- * @param {string} subject
122
- * @param {function} callback
123
- * @returns
252
+ * @example
253
+ * const result = await myService.compute(RequestOptions({ timeout: 60_000 }), payload);
124
254
  */
125
- export function Subscribe(subject: string, callback: Function): Promise<void>;
126
- export class RequestOptionsClass {
127
- constructor(options: any);
128
- options: any;
255
+ export declare function RequestOptions(options: RequestOptionsInput): object;
256
+
257
+ // ---------------------------------------------------------------------------
258
+ // Utilities
259
+ // ---------------------------------------------------------------------------
260
+
261
+ /** The installed Geonix package version string. Equals `"N/A"` if metadata is unavailable. */
262
+ export declare const GeonixVersion: string;
263
+
264
+ /** Generates a cryptographically random Base62-encoded ID string. */
265
+ export declare function randomID(size?: number): string;
266
+
267
+ // ---------------------------------------------------------------------------
268
+ // parseMultipart
269
+ // ---------------------------------------------------------------------------
270
+
271
+ export interface ParseMultipartOptions {
272
+ /** Buffer parts in memory instead of writing to temp files. Default: false. */
273
+ useMemory?: boolean;
274
+ /** Maximum allowed size in bytes for a single part. */
275
+ maxFileSize?: number;
276
+ /** Maximum number of parts allowed. */
277
+ maxFiles?: number;
129
278
  }
130
- export function RequestOptions(options: any): RequestOptionsClass;
131
- export class Service {
132
- static serviceClasses: any[];
133
- /**
134
- *
135
- * @param {ServiceOptions} options
136
- */
137
- static start(options?: ServiceOptions): void;
138
- connections: Map<any, any>;
139
- $createConnection(streamId: any): Promise<boolean>;
140
- $getEnv(): {
141
- geonix: string;
142
- node: {
143
- version: any;
144
- platform: any;
145
- arch: any;
146
- };
147
- env: any;
148
- mem: any;
149
- rss: any;
150
- cpu: any;
151
- };
152
- $getServiceInfo(): {};
153
- #private;
279
+
280
+ export interface MultipartPart {
281
+ /** Field name from Content-Disposition, or `null` if absent. */
282
+ name: string | null;
283
+ /** Filename from Content-Disposition, or `null` if absent. */
284
+ filename: string | null;
285
+ /** Parsed part headers (lowercase keys). */
286
+ headers: Record<string, string>;
287
+ /** Readable stream of the part body. */
288
+ body: Readable;
289
+ /** Part body size in bytes. */
290
+ size: number;
154
291
  }
292
+
155
293
  /**
156
- * Converts data to stream
294
+ * Parses a `multipart/form-data` request body into an array of part objects.
157
295
  *
158
- * @param {*} data
159
- * @param {*} automated
160
- * @returns
296
+ * @throws If the content-type is not `multipart/form-data` or a size/count limit is exceeded.
161
297
  */
162
- export function Stream(data: any, tag?: string): any;
163
- export function isStream(object: any): any;
164
- export function getReadable(object: any): Promise<any>;
165
- export function streamToBuffer(object: any): Promise<any>;
166
- export function streamToString(object: any, encoding: any): Promise<any>;
167
- export function streamToJSON(object: any): Promise<any>;
168
- export const stats: {};
169
- export const activeStreams: {};
170
- type parseMultipartOptions = {
171
- maxFileSize: number;
172
- maxFiles: number;
173
- useMemory: boolean;
174
- };
175
- type ParsedMultiPart = {
176
- /**
177
- * Field name of the part (extracted from Content-Disposition)
178
- */
179
- "name-": string;
180
- /**
181
- * - Filename of the part (extracted from Content-Disposition)
182
- */
183
- filename: string;
184
- /**
185
- * - Headers of the part
186
- */
187
- headers: Object;
188
- /**
189
- * - Path to the file when useMemory is false
190
- */
191
- bodyFile: string;
192
- /**
193
- * - Readable stream of the part
194
- */
195
- body: Readable;
196
- };
197
- type ServiceOptions = {
198
- middleware?: {
199
- /**
200
- * Enable JSON middleware
201
- */
202
- json?: boolean | undefined;
203
- /**
204
- * Enable RAW middleware
205
- */
206
- raw?: boolean | undefined;
207
- /**
208
- * Enable cookies middleware
209
- */
210
- cookies?: boolean | undefined;
211
- } | undefined;
212
- /**
213
- * Enable full beacon
214
- */
215
- fullBeacon?: boolean | undefined;
216
- };
217
- /**
218
- * Parse nats:// URL
219
- * @param {string} url
220
- * @returns
221
- */
222
- export function parseURL(url: string): any;
223
- export function getFirstItemFromAsyncIterable(asyncIterable: any): Promise<any>;
224
- export function getNetworkAddresses(): any[];
225
- export function isIterable(obj: any): any;
298
+ export declare function parseMultipart(
299
+ req: IncomingMessage,
300
+ options?: ParseMultipartOptions
301
+ ): Promise<MultipartPart[]>;
302
+
303
+ // ---------------------------------------------------------------------------
304
+ // ServeStatic
305
+ // ---------------------------------------------------------------------------
306
+
307
+ export interface ServeStaticOptions {
308
+ /** URL prefix to strip before resolving the file path. */
309
+ root?: string;
310
+ /** If `true`, respond with `index.html` for all 404s — useful for SPAs. */
311
+ indexOn404?: boolean;
312
+ [key: string]: any;
313
+ }
314
+
226
315
  /**
316
+ * Creates an Express router that serves static files from `root`.
227
317
  *
228
- *
229
- * @param {Request} req
230
- * @param {parseMultipartOptions} options
231
- * @returns ParsedMultiPart[]
318
+ * @param root - Filesystem path to the directory containing static assets.
319
+ * @param options - Options forwarded to `express.static`, plus `root` prefix strip and `indexOn404`.
232
320
  */
233
- export function parseMultipart(req: Request, _options: any): Promise<{
234
- headers: {};
235
- bodyFile: any;
236
- body: any;
237
- }[]>;
238
- export function tempFilename(): any;
239
- export function randomSafeId(size?: number): string;
240
- export function deepMerge(target: any, ...source: any[]): any;
241
- export function sleep(delay: number): Promise<any>;
242
- export function nextTick(): Promise<any>;
243
- export function picoid(size?: number): any;
244
- export function hash(data: string | Buffer): any;
245
- export function createServerAtPort(port: number, pkg: Object, handler: Function): Promise<any>;
246
- export function createServerAtFreePort(pkg: Object, handler: Function, start?: number, poolSize?: number): Promise<any>;
247
- export function createTCPServer(handler: Function, start?: number, poolSize?: number): Promise<any>;
248
- export function createHTTPServer(handler: Function, start?: number, poolSize?: number): Promise<any>;
249
- export function getSecondsSinceMidnight(): number;
250
- export function getFunctionParams(fn: any): any;
251
- export function proxyHttp(target: any, req: any, res: any): Promise<any>;
252
- export function OverlayObject(object: any, overlay: any): any;
253
- export const GeonixVersion: string;
254
- export function StreamChunker(chunkSize?: number): any;
255
- export const HEALTH_CHECK_ENDPOINT: "/pA4vY7fT9oG5aI8cA4yV3qW5fP9qR1vI";
256
- export function ServeStatic(root: any, options?: {}): any;
257
- export const webserver: WebServer;
258
- declare class WebServer {
259
- start(): Promise<void>;
260
- getPort(): any;
261
- router(): any;
262
- waitUntilReady(): Promise<void>;
263
- stop(): void;
264
- #private;
265
- }
266
- export {};
321
+ export declare function ServeStatic(root: string, options?: ServeStaticOptions): any;
package/package.json CHANGED
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "geonix",
3
- "version": "1.23.8",
3
+ "version": "1.30.2",
4
4
  "type": "module",
5
5
  "description": "",
6
6
  "bin": {
7
7
  "gateway": "./.bin/gateway.js"
8
8
  },
9
9
  "main": "exports.js",
10
+ "types": "index.d.ts",
10
11
  "scripts": {
11
- "test": "node --test --test-reporter=spec 'tests/unit/*.test.js' && node --test-concurrency=1 --test-reporter=spec 'tests/integration/*.test.js'",
12
- "test:unit": "node --test --test-reporter=spec 'tests/unit/*.test.js'",
13
- "test:integration": "node --test-concurrency=1 --test-reporter=spec 'tests/integration/*.test.js'",
14
- "build": "npx tsc && cat build/* > index.d.ts && rm -rf build",
12
+ "test": "npm run test:unit && npm run test:integration",
13
+ "test:unit": "GX_LOG_LEVEL=none node --test --test-reporter=spec tests/unit/*.test.js",
14
+ "test:integration": "GX_LOG_LEVEL=none node --test --test-concurrency=1 --test-reporter=spec tests/integration/*.test.js",
15
15
  "lint": "npx eslint src",
16
16
  "deploy": "npm run build && npm publish"
17
17
  },
@@ -19,18 +19,19 @@
19
19
  "license": "MIT",
20
20
  "dependencies": {
21
21
  "cookie-parser": "1.4.7",
22
- "express": "^4.21.2",
22
+ "express": "^4.22.1",
23
23
  "express-async-errors": "3.1.1",
24
24
  "express-ws": "5.0.2",
25
- "nats": "2.28.2",
26
- "semver": "7.6.3",
25
+ "nats": "^2.29.3",
26
+ "semver": "^7.7.4",
27
27
  "ws": "8.18.0"
28
28
  },
29
29
  "publishConfig": {
30
30
  "registry": "https://registry.npmjs.org/"
31
31
  },
32
32
  "devDependencies": {
33
- "eslint": "^9.36.0",
34
- "typescript": "^5.5.4"
33
+ "@eslint/js": "^10.0.1",
34
+ "eslint": "^10.1.0",
35
+ "globals": "^17.4.0"
35
36
  }
36
37
  }
package/src/Codec.js CHANGED
@@ -1,20 +1,33 @@
1
1
  import { JSONCodec } from "nats";
2
2
 
3
+ /**
4
+ * Shared NATS JSON codec instance used to serialise and deserialise message payloads.
5
+ *
6
+ * @type {import('nats').Codec<any>}
7
+ */
3
8
  export const codec = JSONCodec();
4
9
 
10
+ /**
11
+ * Serialises `data` to a NATS-compatible `Uint8Array` using the JSON codec.
12
+ *
13
+ * @param {any} data - Value to encode.
14
+ * @returns {Uint8Array}
15
+ */
5
16
  export function encode(data) {
6
17
  return codec.encode(data);
7
18
  }
8
19
 
20
+ /**
21
+ * Deserialises a `Uint8Array` NATS payload back to a JavaScript value using the JSON codec.
22
+ *
23
+ * @param {Uint8Array|Buffer} data - Raw bytes to decode.
24
+ * @returns {any}
25
+ * @throws {Error} If the bytes cannot be decoded as JSON.
26
+ */
9
27
  export function decode(data) {
10
- // check if data is json
11
- if (Buffer.isBuffer(data) && data.readUInt8(0) === "{".charCodeAt(0)) {
12
- return codec.decode(data);
13
- }
14
-
15
28
  try {
16
29
  return codec.decode(data);
17
- } catch {
18
- throw new Error("Codec.decode: unknown data type");
30
+ } catch (e) {
31
+ throw new Error(`Codec.decode: ${e.message}`, { cause: e });
19
32
  }
20
33
  }