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/LICENSE.md +1 -1
- package/README.md +348 -4
- package/exports.js +0 -2
- package/index.d.ts +292 -237
- package/package.json +11 -10
- package/src/Codec.js +20 -7
- package/src/Connection.js +94 -40
- package/src/Crypto.js +103 -0
- package/src/Gateway.js +146 -70
- package/src/Logger.js +90 -9
- package/src/Registry.js +127 -15
- package/src/Remote.js +15 -6
- package/src/Request.js +117 -80
- package/src/RequestOptions.js +11 -8
- package/src/Service.js +128 -92
- package/src/Stream.js +69 -15
- package/src/Util.js +192 -158
- package/src/WebServer.js +18 -10
- package/.claude/settings.local.json +0 -10
- package/PROJECT.md +0 -164
- package/REVIEW.md +0 -372
package/src/Registry.js
CHANGED
|
@@ -1,38 +1,105 @@
|
|
|
1
1
|
import { connection } from "./Connection.js";
|
|
2
|
-
import { sleep } from "./Util.js";
|
|
2
|
+
import { fetchWithTimeout, sleep, withTimeout } from "./Util.js";
|
|
3
3
|
import semver from "semver";
|
|
4
4
|
import EventEmitter from "events";
|
|
5
5
|
import { directRequest } from "./Request.js";
|
|
6
6
|
import { decode } from "./Codec.js";
|
|
7
|
+
import { logger } from "./Logger.js";
|
|
7
8
|
|
|
8
9
|
const REGISTRY_ENTRY_TIMEOUT = 5000;
|
|
10
|
+
const GARBAGE_COLLECTOR_INTERVAL = 500;
|
|
9
11
|
|
|
10
12
|
/**
|
|
11
|
-
*
|
|
13
|
+
* Maintains a local, in-process view of all services currently present on the NATS bus.
|
|
14
|
+
* Entries are populated from beacon messages and expire after 5 seconds of silence.
|
|
15
|
+
* Emits `"added"` when a new entry is registered and `"removed"` when one expires.
|
|
16
|
+
*
|
|
17
|
+
* @extends EventEmitter
|
|
12
18
|
*/
|
|
13
19
|
class Registry extends EventEmitter {
|
|
14
20
|
|
|
15
21
|
#isActive = false;
|
|
16
22
|
#registry = {};
|
|
23
|
+
#byIdentifier = new Map();
|
|
17
24
|
|
|
18
25
|
constructor() {
|
|
19
26
|
super();
|
|
20
27
|
|
|
21
|
-
this.#start();
|
|
28
|
+
this.#start().catch(e => logger.error("registry.start:", e));
|
|
22
29
|
}
|
|
23
30
|
|
|
24
31
|
async #start() {
|
|
25
32
|
this.#isActive = true;
|
|
26
33
|
await connection.waitUntilReady();
|
|
27
34
|
|
|
28
|
-
this.#beaconListener();
|
|
29
|
-
this.#garbageCollector();
|
|
35
|
+
this.#beaconListener().catch(e => logger.error("registry.beaconListener:", e));
|
|
36
|
+
this.#garbageCollector().catch(e => logger.error("registry.garbageCollector:", e));
|
|
30
37
|
}
|
|
31
38
|
|
|
32
|
-
|
|
39
|
+
/**
|
|
40
|
+
* Stops the beacon listener and garbage collector.
|
|
41
|
+
* @returns {void}
|
|
42
|
+
*/
|
|
43
|
+
stop() {
|
|
33
44
|
this.#isActive = false;
|
|
34
45
|
}
|
|
35
46
|
|
|
47
|
+
async #checkHealth(address, addresses, onFirstHealthy, timeout = 500) {
|
|
48
|
+
try {
|
|
49
|
+
const result = await (await fetchWithTimeout(`http://${address}/!!_gx/health`, {}, timeout)).json();
|
|
50
|
+
|
|
51
|
+
if (result.status === "healthy") {
|
|
52
|
+
addresses.push(address);
|
|
53
|
+
onFirstHealthy();
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
} catch {
|
|
57
|
+
// ignore errors
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async #registerService(data) {
|
|
64
|
+
if (data.a?.length > 0) {
|
|
65
|
+
const allAddresses = data.a;
|
|
66
|
+
data.a = [];
|
|
67
|
+
|
|
68
|
+
let { promise: promiseFirst, resolve: resolveFirst } = Promise.withResolvers();
|
|
69
|
+
let firstFound = false;
|
|
70
|
+
const onFirstHealthy = () => {
|
|
71
|
+
if (!firstFound) {
|
|
72
|
+
firstFound = true; resolveFirst();
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// all checks run in parallel; background ones push into data.a,
|
|
77
|
+
// which is the same array reference spread into the registry entry below
|
|
78
|
+
// resolve promiseFirst when all checks are done so we don't wait on the timeout
|
|
79
|
+
Promise.all(allAddresses.map(a => this.#checkHealth(a, data.a, onFirstHealthy))).then(() => resolveFirst());
|
|
80
|
+
|
|
81
|
+
try {
|
|
82
|
+
await withTimeout(promiseFirst, 5000);
|
|
83
|
+
} catch {
|
|
84
|
+
// safety net only — should not normally be reached
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
this.#registry[data.i] = {
|
|
89
|
+
...data,
|
|
90
|
+
timeout: Date.now() + REGISTRY_ENTRY_TIMEOUT
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const nameVersion = `${data.n}@${data.v}`;
|
|
94
|
+
if (!this.#byIdentifier.has(nameVersion)) {
|
|
95
|
+
this.#byIdentifier.set(nameVersion, new Set());
|
|
96
|
+
}
|
|
97
|
+
this.#byIdentifier.get(nameVersion).add(data.i);
|
|
98
|
+
this.#byIdentifier.set(data.i, new Set([data.i]));
|
|
99
|
+
|
|
100
|
+
this.emit("added", this.#registry[data.i]);
|
|
101
|
+
}
|
|
102
|
+
|
|
36
103
|
async #beaconListener() {
|
|
37
104
|
const subscription = await connection.subscribe("gx2.beacon");
|
|
38
105
|
|
|
@@ -41,19 +108,23 @@ class Registry extends EventEmitter {
|
|
|
41
108
|
|
|
42
109
|
const exists = this.#registry[data.i] !== undefined;
|
|
43
110
|
|
|
44
|
-
// if this is a short beacon, request full service info
|
|
111
|
+
// if this is a short beacon, request full service info on first discovery only
|
|
45
112
|
if (!data.n) {
|
|
113
|
+
if (exists) {
|
|
114
|
+
this.#registry[data.i].timeout = Date.now() + REGISTRY_ENTRY_TIMEOUT;
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
46
117
|
data = await directRequest(data.i, "$getServiceInfo");
|
|
47
118
|
}
|
|
48
119
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
timeout
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
if (!exists) {
|
|
55
|
-
this.emit("added", this.#registry[data.i]);
|
|
120
|
+
// known service — just refresh timeout, no re-probing
|
|
121
|
+
if (exists) {
|
|
122
|
+
this.#registry[data.i].timeout = Date.now() + REGISTRY_ENTRY_TIMEOUT;
|
|
123
|
+
continue;
|
|
56
124
|
}
|
|
125
|
+
|
|
126
|
+
// new service — dispatch registration without awaiting so the beacon loop stays unblocked
|
|
127
|
+
this.#registerService(data).catch(e => logger.error("registry.registerService:", e));
|
|
57
128
|
}
|
|
58
129
|
}
|
|
59
130
|
|
|
@@ -69,18 +140,54 @@ class Registry extends EventEmitter {
|
|
|
69
140
|
if (now > entry.timeout) {
|
|
70
141
|
delete this.#registry[identifier];
|
|
71
142
|
|
|
143
|
+
const nameVersion = `${entry.n}@${entry.v}`;
|
|
144
|
+
const set = this.#byIdentifier.get(nameVersion);
|
|
145
|
+
if (set) {
|
|
146
|
+
set.delete(entry.i);
|
|
147
|
+
if (set.size === 0) { this.#byIdentifier.delete(nameVersion); }
|
|
148
|
+
}
|
|
149
|
+
this.#byIdentifier.delete(entry.i);
|
|
150
|
+
|
|
72
151
|
this.emit("removed", entry);
|
|
73
152
|
}
|
|
74
153
|
}
|
|
75
154
|
|
|
76
|
-
await sleep(
|
|
155
|
+
await sleep(GARBAGE_COLLECTOR_INTERVAL);
|
|
77
156
|
}
|
|
78
157
|
}
|
|
79
158
|
|
|
159
|
+
/**
|
|
160
|
+
* Returns the raw registry map keyed by instance ID.
|
|
161
|
+
*
|
|
162
|
+
* @returns {Object.<string, object>} All currently live registry entries.
|
|
163
|
+
*/
|
|
80
164
|
getEntries() {
|
|
81
165
|
return this.#registry;
|
|
82
166
|
}
|
|
83
167
|
|
|
168
|
+
/**
|
|
169
|
+
* Returns all live registry entries that match a given identifier string
|
|
170
|
+
* (e.g. `"MyService@1.0.0"` or a bare instance ID).
|
|
171
|
+
*
|
|
172
|
+
* @param {string} identifier - Identifier in the form `"Name@version"` or an instance ID.
|
|
173
|
+
* @returns {object[]} Array of matching registry entry objects.
|
|
174
|
+
*/
|
|
175
|
+
getEntriesForIdentifier(identifier) {
|
|
176
|
+
const ids = this.#byIdentifier.get(identifier);
|
|
177
|
+
if (!ids) { return []; }
|
|
178
|
+
return [...ids].map(i => this.#registry[i]).filter(Boolean);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Looks up the best-matching service identifier for the given name, optional semver range,
|
|
183
|
+
* and optional static ID. Returns the highest-version match as a `"Name@version"` string,
|
|
184
|
+
* or the instance ID when an exact `id` is requested.
|
|
185
|
+
*
|
|
186
|
+
* @param {string} service - Service name to look up.
|
|
187
|
+
* @param {string} [version] - Optional semver range (e.g. `"^1.2"`).
|
|
188
|
+
* @param {string} [id] - Optional static service ID for targeting a specific instance.
|
|
189
|
+
* @returns {string|undefined} Resolved identifier, or `undefined` if not found.
|
|
190
|
+
*/
|
|
84
191
|
getIdentifier(service, version, id) {
|
|
85
192
|
let matches = [];
|
|
86
193
|
|
|
@@ -111,4 +218,9 @@ class Registry extends EventEmitter {
|
|
|
111
218
|
|
|
112
219
|
}
|
|
113
220
|
|
|
221
|
+
/**
|
|
222
|
+
* Singleton {@link Registry} instance shared across the process.
|
|
223
|
+
*
|
|
224
|
+
* @type {Registry}
|
|
225
|
+
*/
|
|
114
226
|
export const registry = new Registry();
|
package/src/Remote.js
CHANGED
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
import { Request } from "./Request.js";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* @
|
|
4
|
+
* Creates a proxy object that forwards property-access calls to a named remote service.
|
|
5
|
+
* Each property access returns an async function that invokes the corresponding method on
|
|
6
|
+
* the remote service via {@link Request}.
|
|
7
|
+
*
|
|
8
|
+
* @param {string} service - Service name, optionally with a version range or instance ID
|
|
9
|
+
* (e.g. `"MyService"`, `"MyService@^1.2"`, `"MyService#<instanceId>"`).
|
|
10
|
+
* @param {...any} context - Optional context values forwarded to the remote method.
|
|
11
|
+
* @returns {Proxy} A proxy whose properties are async functions that call the remote service.
|
|
9
12
|
*/
|
|
10
13
|
export const Remote = (service, ...context) => new Proxy({}, {
|
|
11
|
-
get: (_target, method
|
|
14
|
+
get: (_target, method) => {
|
|
15
|
+
if (typeof method !== "string" || method === "then") {
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return async (...args) => Request(service, method, args, context);
|
|
20
|
+
}
|
|
12
21
|
});
|
package/src/Request.js
CHANGED
|
@@ -1,105 +1,140 @@
|
|
|
1
1
|
import { connection } from "./Connection.js";
|
|
2
2
|
import { registry } from "./Registry.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
3
|
+
import { fetchWithTimeout, hash } from "./Util.js";
|
|
4
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
5
|
+
import { _requestOptionsSymbol } from "./RequestOptions.js";
|
|
6
6
|
import { isStream, streamToString } from "./Stream.js";
|
|
7
7
|
import { inspect } from "node:util";
|
|
8
8
|
import { logger } from "./Logger.js";
|
|
9
|
-
|
|
10
|
-
const REGISTRY_TIMEOUT = 300000;
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Get full v8 stack trace
|
|
14
|
-
* @returns
|
|
15
|
-
*/
|
|
16
|
-
function getStack() {
|
|
17
|
-
const oldLimit = Error.stackTraceLimit;
|
|
18
|
-
const oldHandler = Error.prepareStackTrace;
|
|
19
|
-
const holder = {};
|
|
20
|
-
|
|
21
|
-
// set new values
|
|
22
|
-
Error.stackTraceLimit = Infinity;
|
|
23
|
-
Error.prepareStackTrace = (holder, stackTrace) => stackTrace;
|
|
24
|
-
|
|
25
|
-
// capture stack trace
|
|
26
|
-
Error.captureStackTrace(holder, getStack);
|
|
27
|
-
|
|
28
|
-
const stack = holder.stack;
|
|
29
|
-
|
|
30
|
-
// restore values
|
|
31
|
-
Error.prepareStackTrace = oldHandler;
|
|
32
|
-
Error.stackTraceLimit = oldLimit;
|
|
33
|
-
|
|
34
|
-
return stack;
|
|
35
|
-
}
|
|
9
|
+
import { _payloadKey, encryptPayload, decryptPayload } from "./Crypto.js";
|
|
36
10
|
|
|
37
11
|
/**
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
12
|
+
* AsyncLocalStorage instance used to propagate the RPC call originator across async boundaries
|
|
13
|
+
* so that nested service calls can include accurate tracing information.
|
|
14
|
+
*
|
|
15
|
+
* @type {import('node:async_hooks').AsyncLocalStorage<{ originator: string }>}
|
|
41
16
|
*/
|
|
42
|
-
|
|
43
|
-
|
|
17
|
+
export const rpcContext = new AsyncLocalStorage();
|
|
18
|
+
const REGISTRY_TIMEOUT = 300000;
|
|
44
19
|
|
|
45
|
-
|
|
46
|
-
|
|
20
|
+
function waitForIdentifier(name, version, id, timeout) {
|
|
21
|
+
const found = registry.getIdentifier(name, version, id);
|
|
22
|
+
if (found) {
|
|
23
|
+
return Promise.resolve(found);
|
|
24
|
+
}
|
|
47
25
|
|
|
48
|
-
|
|
49
|
-
|
|
26
|
+
return new Promise(resolve => {
|
|
27
|
+
const timer = setTimeout(() => {
|
|
28
|
+
registry.removeListener("added", onAdded);
|
|
29
|
+
resolve(null);
|
|
30
|
+
}, timeout);
|
|
31
|
+
|
|
32
|
+
function onAdded() {
|
|
33
|
+
const identifier = registry.getIdentifier(name, version, id);
|
|
34
|
+
if (identifier) {
|
|
35
|
+
clearTimeout(timer);
|
|
36
|
+
registry.removeListener("added", onAdded);
|
|
37
|
+
resolve(identifier);
|
|
38
|
+
}
|
|
50
39
|
}
|
|
51
|
-
|
|
40
|
+
|
|
41
|
+
registry.on("added", onAdded);
|
|
42
|
+
});
|
|
52
43
|
}
|
|
53
44
|
|
|
54
45
|
/**
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
* @param {string}
|
|
59
|
-
* @param {
|
|
60
|
-
* @param {any[]}
|
|
61
|
-
* @param {
|
|
62
|
-
* @
|
|
46
|
+
* Sends a request to a named service, waiting in the registry for up to `timeout` ms if the
|
|
47
|
+
* service is not yet available. Tries HTTP RPC first, falls back to NATS.
|
|
48
|
+
*
|
|
49
|
+
* @param {string} service - Service name, optionally with version (`@^1.2`) or instance ID (`#<id>`).
|
|
50
|
+
* @param {string} method - Method name to invoke on the service.
|
|
51
|
+
* @param {any[]} args - Positional arguments to pass. The first argument may be a {@link RequestOptions} object.
|
|
52
|
+
* @param {any[]} [context] - Optional context values forwarded to the remote method.
|
|
53
|
+
* @param {object} [options] - Request options (e.g. `timeout`).
|
|
54
|
+
* @returns {Promise<any>} The value returned by the remote method.
|
|
55
|
+
* @throws {Error} If the service is not found within the timeout or the remote method throws.
|
|
63
56
|
*/
|
|
64
57
|
export async function Request(service, method, args, context, options) {
|
|
65
58
|
const match = /(?<name>[^@#]*)(@(?<version>[^@#]*))?(#(?<id>[^@#]*))?/.exec(service);
|
|
66
59
|
const { name, version, id } = match.groups;
|
|
67
60
|
|
|
68
61
|
// allow passing RequestOptions as first arg
|
|
69
|
-
if (args?.length > 0 && args[0]
|
|
70
|
-
options =
|
|
62
|
+
if (args?.length > 0 && args[0]?.[_requestOptionsSymbol] !== undefined) {
|
|
63
|
+
options = args.shift()[_requestOptionsSymbol];
|
|
71
64
|
}
|
|
72
65
|
|
|
73
|
-
let identifier = null;
|
|
74
|
-
|
|
75
|
-
// get service instance identifier and wait for it if it's not available
|
|
76
66
|
const registryTimeout = options?.timeout ?? REGISTRY_TIMEOUT;
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
if (!identifier) {
|
|
82
|
-
await sleep(delay);
|
|
83
|
-
}
|
|
67
|
+
const identifier = await waitForIdentifier(name, version, id, registryTimeout);
|
|
68
|
+
|
|
69
|
+
if (!identifier) {
|
|
70
|
+
throw Error(`Request: service "${service}" not found within ${registryTimeout}ms`);
|
|
84
71
|
}
|
|
85
72
|
|
|
86
73
|
return directRequest(identifier, method, args, context, options, service);
|
|
87
74
|
}
|
|
88
75
|
|
|
76
|
+
let _httpRpcRoundRobin = 0;
|
|
77
|
+
|
|
89
78
|
/**
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
* @param {string}
|
|
94
|
-
* @param {
|
|
95
|
-
* @param {any[]}
|
|
96
|
-
* @param {
|
|
97
|
-
* @
|
|
79
|
+
* Sends a request directly to a resolved registry identifier without performing a registry
|
|
80
|
+
* lookup. Prefers HTTP RPC for single-instance services; falls back to NATS otherwise.
|
|
81
|
+
*
|
|
82
|
+
* @param {string} identifier - Registry identifier in the form `"Name@version"` or an instance ID.
|
|
83
|
+
* @param {string} method - Method name to invoke on the service.
|
|
84
|
+
* @param {any[]} [args] - Positional arguments forwarded to the remote method.
|
|
85
|
+
* @param {any[]} [context] - Optional context values forwarded to the remote method.
|
|
86
|
+
* @param {object} [options] - Request options (e.g. `timeout`, `httpTimeout`).
|
|
87
|
+
* @param {string} [service] - Original service string used only for error logging.
|
|
88
|
+
* @returns {Promise<any>} The value returned by the remote method.
|
|
89
|
+
* @throws {Error} If the transport returns an error or the response is invalid.
|
|
98
90
|
*/
|
|
99
91
|
export async function directRequest(identifier, method, args, context, options, service) {
|
|
92
|
+
const originator = rpcContext.getStore()?.originator;
|
|
93
|
+
const requestBegin = Date.now();
|
|
94
|
+
|
|
95
|
+
// try HTTP RPC first if the service has a single known instance with addresses
|
|
96
|
+
// (multiple instances use NATS so queue-group distribution is preserved)
|
|
97
|
+
const entries = registry.getEntriesForIdentifier(identifier);
|
|
98
|
+
if (entries.length === 1) {
|
|
99
|
+
const addresses = entries[0].a || [];
|
|
100
|
+
if (addresses.length > 0) {
|
|
101
|
+
const address = addresses[_httpRpcRoundRobin++ % addresses.length];
|
|
102
|
+
|
|
103
|
+
const url = `http://${address}/!!_gx/rpc/${hash(entries[0].i)}`;
|
|
104
|
+
const rpcPayload = { m: method, a: args, c: context, o: originator };
|
|
105
|
+
const fetchBody = _payloadKey
|
|
106
|
+
? encryptPayload(Buffer.from(JSON.stringify(rpcPayload)))
|
|
107
|
+
: JSON.stringify(rpcPayload);
|
|
108
|
+
const contentType = _payloadKey ? "application/octet-stream" : "application/json";
|
|
109
|
+
|
|
110
|
+
let httpResponse;
|
|
111
|
+
try {
|
|
112
|
+
const res = await fetchWithTimeout(url, {
|
|
113
|
+
method: "POST",
|
|
114
|
+
headers: { "content-type": contentType },
|
|
115
|
+
body: fetchBody,
|
|
116
|
+
}, options?.httpTimeout ?? 5000);
|
|
117
|
+
if (res.ok) {
|
|
118
|
+
httpResponse = _payloadKey
|
|
119
|
+
? JSON.parse(decryptPayload(Buffer.from(await res.arrayBuffer())))
|
|
120
|
+
: await res.json();
|
|
121
|
+
}
|
|
122
|
+
} catch (e) {
|
|
123
|
+
logger.debug("directRequest: HTTP RPC failed, falling back to NATS", address, e.message);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (httpResponse) {
|
|
127
|
+
if (httpResponse.e) { throw Error(`Request: remote error: ${httpResponse.e}`); }
|
|
128
|
+
if (isStream(httpResponse.r)) {
|
|
129
|
+
return JSON.parse(await streamToString(httpResponse.r));
|
|
130
|
+
}
|
|
131
|
+
return httpResponse.r;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// NATS fallback
|
|
100
137
|
let response;
|
|
101
|
-
const originator = getOriginator();
|
|
102
|
-
let requestBegin = Date.now();
|
|
103
138
|
try {
|
|
104
139
|
response = await connection.request(
|
|
105
140
|
`gx2.service.${hash(identifier)}`,
|
|
@@ -137,21 +172,23 @@ export async function directRequest(identifier, method, args, context, options,
|
|
|
137
172
|
}
|
|
138
173
|
|
|
139
174
|
/**
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
* @param {string} subject
|
|
143
|
-
* @param {string|
|
|
175
|
+
* Publishes a raw payload to a NATS subject. Fire-and-forget; no reply is expected.
|
|
176
|
+
*
|
|
177
|
+
* @param {string} subject - NATS subject to publish to.
|
|
178
|
+
* @param {string|Buffer} payload - Raw bytes or string to send.
|
|
179
|
+
* @returns {Promise<void>}
|
|
144
180
|
*/
|
|
145
181
|
export async function Publish(subject, payload) {
|
|
146
|
-
connection.publishRaw(subject, Buffer.from(payload));
|
|
182
|
+
await connection.publishRaw(subject, Buffer.from(payload));
|
|
147
183
|
}
|
|
148
184
|
|
|
149
185
|
/**
|
|
150
|
-
*
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
* @param {
|
|
154
|
-
* @
|
|
186
|
+
* Subscribes to a NATS subject and invokes `callback` with the raw message data for each
|
|
187
|
+
* incoming message. Returns once the subscription is drained.
|
|
188
|
+
*
|
|
189
|
+
* @param {string} subject - NATS subject to subscribe to.
|
|
190
|
+
* @param {function(Buffer): void} callback - Function called with the raw message data.
|
|
191
|
+
* @returns {Promise<void>}
|
|
155
192
|
*/
|
|
156
193
|
export async function Subscribe(subject, callback) {
|
|
157
194
|
if (typeof callback !== "function") {
|
package/src/RequestOptions.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
options;
|
|
1
|
+
const OPTS = Symbol("RequestOptions");
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Wraps a plain options object in a sentinel that {@link Request} can detect and extract
|
|
5
|
+
* when it appears as the first argument of a service method call.
|
|
6
|
+
*
|
|
7
|
+
* @param {{ timeout?: number, httpTimeout?: number }} options - Request options to attach.
|
|
8
|
+
* @returns {{ [symbol]: object }} Sentinel object consumed by {@link Request}.
|
|
9
|
+
*/
|
|
10
|
+
export function RequestOptions(options) {
|
|
11
|
+
return { [OPTS]: options };
|
|
7
12
|
}
|
|
8
13
|
|
|
9
|
-
export
|
|
10
|
-
return new RequestOptionsClass(options);
|
|
11
|
-
};
|
|
14
|
+
export { OPTS as _requestOptionsSymbol };
|