@hotmeshio/long-tail 0.25.4 → 0.27.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/README.md +1 -0
- package/build/index.d.ts +5 -0
- package/build/index.js +36 -7
- package/build/lib/events/nats.d.ts +11 -0
- package/build/lib/events/nats.js +33 -2
- package/build/lib/machines/index.d.ts +6 -0
- package/build/lib/machines/index.js +22 -0
- package/build/lib/machines/line.d.ts +44 -0
- package/build/lib/machines/line.js +114 -0
- package/build/lib/machines/protocol/codec.d.ts +108 -0
- package/build/lib/machines/protocol/codec.js +80 -0
- package/build/lib/machines/protocol/constants.d.ts +120 -0
- package/build/lib/machines/protocol/constants.js +97 -0
- package/build/lib/machines/protocol/contract.d.ts +19 -0
- package/build/lib/machines/protocol/contract.js +141 -0
- package/build/lib/machines/protocol/envelope.d.ts +14 -0
- package/build/lib/machines/protocol/envelope.js +43 -0
- package/build/lib/machines/protocol/index.d.ts +5 -0
- package/build/lib/machines/protocol/index.js +21 -0
- package/build/lib/machines/protocol/topics.d.ts +33 -0
- package/build/lib/machines/protocol/topics.js +79 -0
- package/build/lib/machines/site/edge-account.d.ts +11 -0
- package/build/lib/machines/site/edge-account.js +24 -0
- package/build/lib/machines/site/mosquitto-conf.d.ts +27 -0
- package/build/lib/machines/site/mosquitto-conf.js +81 -0
- package/build/lib/machines/site/site-conf.d.ts +23 -0
- package/build/lib/machines/site/site-conf.js +62 -0
- package/build/lib/machines/transport/callback-feed.d.ts +18 -0
- package/build/lib/machines/transport/callback-feed.js +31 -0
- package/build/lib/machines/transport/delivery.d.ts +20 -0
- package/build/lib/machines/transport/delivery.js +37 -0
- package/build/lib/machines/transport/index.d.ts +13 -0
- package/build/lib/machines/transport/index.js +29 -0
- package/build/lib/machines/transport/memory-broker.d.ts +35 -0
- package/build/lib/machines/transport/memory-broker.js +145 -0
- package/build/lib/machines/transport/memory.d.ts +40 -0
- package/build/lib/machines/transport/memory.js +79 -0
- package/build/lib/machines/transport/mqtt-bus.d.ts +24 -0
- package/build/lib/machines/transport/mqtt-bus.js +43 -0
- package/build/lib/machines/transport/mqtt-link.d.ts +16 -0
- package/build/lib/machines/transport/mqtt-link.js +37 -0
- package/build/lib/machines/transport/mqtt-session.d.ts +34 -0
- package/build/lib/machines/transport/mqtt-session.js +127 -0
- package/build/lib/machines/transport/nats-bus.d.ts +36 -0
- package/build/lib/machines/transport/nats-bus.js +47 -0
- package/build/lib/machines/transport/nats-feed.d.ts +26 -0
- package/build/lib/machines/transport/nats-feed.js +49 -0
- package/build/lib/machines/transport/pubsub-feed.d.ts +17 -0
- package/build/lib/machines/transport/pubsub-feed.js +39 -0
- package/build/lib/machines/transport/serial.d.ts +21 -0
- package/build/lib/machines/transport/serial.js +54 -0
- package/build/lib/machines/transport/stream-bus.d.ts +40 -0
- package/build/lib/machines/transport/stream-bus.js +57 -0
- package/build/lib/machines/transport/streams.d.ts +24 -0
- package/build/lib/machines/transport/streams.js +49 -0
- package/build/lib/messaging/config.d.ts +25 -0
- package/build/lib/messaging/config.js +55 -0
- package/build/lib/messaging/constants.d.ts +26 -0
- package/build/lib/messaging/constants.js +26 -0
- package/build/lib/messaging/index.d.ts +24 -0
- package/build/lib/messaging/index.js +65 -0
- package/build/lib/messaging/memory.d.ts +16 -0
- package/build/lib/messaging/memory.js +147 -0
- package/build/lib/messaging/nats/permissions.d.ts +13 -0
- package/build/lib/messaging/nats/permissions.js +22 -0
- package/build/lib/messaging/nats/provider.d.ts +30 -0
- package/build/lib/messaging/nats/provider.js +77 -0
- package/build/lib/messaging/nats/render-conf.d.ts +68 -0
- package/build/lib/messaging/nats/render-conf.js +58 -0
- package/build/lib/messaging/nats/streams.d.ts +26 -0
- package/build/lib/messaging/nats/streams.js +97 -0
- package/build/lib/messaging/subjects.d.ts +8 -0
- package/build/lib/messaging/subjects.js +25 -0
- package/build/scripts/bambu-boat.d.ts +17 -0
- package/build/scripts/bambu-boat.js +134 -0
- package/build/scripts/bambu-print.d.ts +18 -0
- package/build/scripts/bambu-print.js +114 -0
- package/build/scripts/machine-line.d.ts +33 -0
- package/build/scripts/machine-line.js +301 -0
- package/build/services/machines/commands.d.ts +29 -0
- package/build/services/machines/commands.js +37 -0
- package/build/services/machines/config.d.ts +49 -0
- package/build/services/machines/config.js +94 -0
- package/build/services/machines/contracts.d.ts +210 -0
- package/build/services/machines/contracts.js +124 -0
- package/build/services/machines/forms.d.ts +55 -0
- package/build/services/machines/forms.js +37 -0
- package/build/services/machines/gateway/actions.d.ts +111 -0
- package/build/services/machines/gateway/actions.js +8 -0
- package/build/services/machines/gateway/address-book.d.ts +16 -0
- package/build/services/machines/gateway/address-book.js +24 -0
- package/build/services/machines/gateway/admission.d.ts +35 -0
- package/build/services/machines/gateway/admission.js +66 -0
- package/build/services/machines/gateway/api-escalations.d.ts +23 -0
- package/build/services/machines/gateway/api-escalations.js +101 -0
- package/build/services/machines/gateway/dispatcher.d.ts +76 -0
- package/build/services/machines/gateway/dispatcher.js +197 -0
- package/build/services/machines/gateway/escalations.d.ts +51 -0
- package/build/services/machines/gateway/escalations.js +40 -0
- package/build/services/machines/gateway/executor.d.ts +60 -0
- package/build/services/machines/gateway/executor.js +221 -0
- package/build/services/machines/gateway/gateway.d.ts +88 -0
- package/build/services/machines/gateway/gateway.js +270 -0
- package/build/services/machines/gateway/index.d.ts +10 -0
- package/build/services/machines/gateway/index.js +26 -0
- package/build/services/machines/gateway/jobs.d.ts +16 -0
- package/build/services/machines/gateway/jobs.js +24 -0
- package/build/services/machines/gateway/lifecycle.d.ts +21 -0
- package/build/services/machines/gateway/lifecycle.js +89 -0
- package/build/services/machines/gateway/log.d.ts +41 -0
- package/build/services/machines/gateway/log.js +42 -0
- package/build/services/machines/gateway/lt-http.d.ts +51 -0
- package/build/services/machines/gateway/lt-http.js +120 -0
- package/build/services/machines/gateway/pacing.d.ts +43 -0
- package/build/services/machines/gateway/pacing.js +158 -0
- package/build/services/machines/gateway/plan-escalation.d.ts +19 -0
- package/build/services/machines/gateway/plan-escalation.js +210 -0
- package/build/services/machines/gateway/plan-machine.d.ts +17 -0
- package/build/services/machines/gateway/plan-machine.js +283 -0
- package/build/services/machines/gateway/plan-shared.d.ts +18 -0
- package/build/services/machines/gateway/plan-shared.js +36 -0
- package/build/services/machines/gateway/provision.d.ts +22 -0
- package/build/services/machines/gateway/provision.js +79 -0
- package/build/services/machines/gateway/receipts.d.ts +33 -0
- package/build/services/machines/gateway/receipts.js +65 -0
- package/build/services/machines/gateway/reconcile.d.ts +35 -0
- package/build/services/machines/gateway/reconcile.js +99 -0
- package/build/services/machines/gateway/registration.d.ts +38 -0
- package/build/services/machines/gateway/registration.js +121 -0
- package/build/services/machines/gateway/reply-waiter.d.ts +24 -0
- package/build/services/machines/gateway/reply-waiter.js +65 -0
- package/build/services/machines/gateway/state.d.ts +145 -0
- package/build/services/machines/gateway/state.js +241 -0
- package/build/services/machines/gateway/trace.d.ts +16 -0
- package/build/services/machines/gateway/trace.js +43 -0
- package/build/services/machines/identities.d.ts +25 -0
- package/build/services/machines/identities.js +77 -0
- package/build/services/machines/index.d.ts +11 -0
- package/build/services/machines/index.js +27 -0
- package/build/services/machines/leadership.d.ts +35 -0
- package/build/services/machines/leadership.js +100 -0
- package/build/services/machines/roles.d.ts +13 -0
- package/build/services/machines/roles.js +62 -0
- package/build/services/machines/rows.d.ts +30 -0
- package/build/services/machines/rows.js +71 -0
- package/build/services/machines/select.d.ts +25 -0
- package/build/services/machines/select.js +76 -0
- package/build/services/machines/surface.d.ts +33 -0
- package/build/services/machines/surface.js +104 -0
- package/build/services/machines/testing/fake-long-tail.d.ts +79 -0
- package/build/services/machines/testing/fake-long-tail.js +158 -0
- package/build/services/machines/testing/index.d.ts +3 -0
- package/build/services/machines/testing/index.js +19 -0
- package/build/services/machines/testing/recording-log.d.ts +14 -0
- package/build/services/machines/testing/recording-log.js +22 -0
- package/build/services/machines/testing/wait.d.ts +3 -0
- package/build/services/machines/testing/wait.js +17 -0
- package/build/services/mcp/db-server/schemas.d.ts +2 -2
- package/build/start/adapters.js +11 -2
- package/build/start/index.js +20 -1
- package/build/system/mcp-servers/admin/schemas.d.ts +221 -221
- package/build/system/mcp-servers/db-query/schemas.d.ts +2 -2
- package/build/system/mcp-servers/human-queue-schemas.d.ts +6 -6
- package/build/tsconfig.tsbuildinfo +1 -1
- package/build/types/index.d.ts +3 -1
- package/build/types/machines.d.ts +167 -0
- package/build/types/machines.js +7 -0
- package/build/types/messaging.d.ts +72 -0
- package/build/types/messaging.js +11 -0
- package/build/types/startup.d.ts +49 -1
- package/docs/events.md +50 -4
- package/docs/machines.md +322 -0
- package/package.json +13 -1
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* The machine broker (Mosquitto), rendered from the line. Machines speak
|
|
4
|
+
* MQTT 5 to it over TLS, each with its own client certificate (the CN is
|
|
5
|
+
* its username). The ACL lets a machine publish only its own channels and
|
|
6
|
+
* read only its own commands, and lets the line controller command every
|
|
7
|
+
* machine on the line. A bridge carries the line's topics out to the site
|
|
8
|
+
* NATS, where JetStream keeps them.
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.renderMosquittoConf = renderMosquittoConf;
|
|
12
|
+
exports.renderMosquittoAcl = renderMosquittoAcl;
|
|
13
|
+
const envelope_1 = require("../protocol/envelope");
|
|
14
|
+
const codec_1 = require("../protocol/codec");
|
|
15
|
+
const line_1 = require("../line");
|
|
16
|
+
const site_conf_1 = require("./site-conf");
|
|
17
|
+
function renderMosquittoConf(line, opts) {
|
|
18
|
+
return `# Rendered from the line definition by site/setup.ts. Do not edit by hand.
|
|
19
|
+
persistence true
|
|
20
|
+
persistence_location /mosquitto/data/
|
|
21
|
+
log_dest stdout
|
|
22
|
+
log_type error
|
|
23
|
+
log_type warning
|
|
24
|
+
log_type notice
|
|
25
|
+
|
|
26
|
+
listener 8883
|
|
27
|
+
cafile ${opts.certDir}/ca.pem
|
|
28
|
+
certfile ${opts.certDir}/broker.pem
|
|
29
|
+
keyfile ${opts.certDir}/broker.key
|
|
30
|
+
require_certificate true
|
|
31
|
+
use_identity_as_username true
|
|
32
|
+
acl_file ${opts.aclFile}
|
|
33
|
+
max_packet_size ${opts.maxPacketBytes ?? 262_144}
|
|
34
|
+
|
|
35
|
+
connection ${line.siteId}-${line.lineId}-site-nats
|
|
36
|
+
address ${opts.siteNats}
|
|
37
|
+
bridge_protocol_version mqttv311
|
|
38
|
+
bridge_cafile ${opts.certDir}/ca.pem
|
|
39
|
+
bridge_certfile ${opts.certDir}/${(0, site_conf_1.bridgeId)(line)}.pem
|
|
40
|
+
bridge_keyfile ${opts.certDir}/${(0, site_conf_1.bridgeId)(line)}.key
|
|
41
|
+
remote_clientid ${(0, site_conf_1.bridgeId)(line)}
|
|
42
|
+
cleansession false
|
|
43
|
+
try_private false
|
|
44
|
+
notifications false
|
|
45
|
+
topic ${opts.root}/${line.siteId}/${line.lineId}/# out 1
|
|
46
|
+
`;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* The codec's channels decide the topics: a machine writes its events and telemetry and reads its commands.
|
|
50
|
+
* A station whose address the line learns has no user of its own: `pattern` rules give every certificate
|
|
51
|
+
* the channels under its own name (%u), so its certificate's common name must be its address. On a line
|
|
52
|
+
* that also declares addresses, a certificate named for a declared address is denied that station's channels.
|
|
53
|
+
*/
|
|
54
|
+
function renderMosquittoAcl(line, codec) {
|
|
55
|
+
const { siteId, lineId } = line;
|
|
56
|
+
const { events, telemetry, commands } = codec.channels;
|
|
57
|
+
const own = (address, channels) => channels.map((c) => (0, codec_1.topicOf)(codec, { siteId, lineId, address }, c));
|
|
58
|
+
const rules = (prefix, address) => [
|
|
59
|
+
...own(address, [...events, ...telemetry]).map((t) => `${prefix}write ${t}`),
|
|
60
|
+
...own(address, commands).map((t) => `${prefix}read ${t}`),
|
|
61
|
+
];
|
|
62
|
+
const declared = line.machines.filter((m) => !(0, line_1.learnsAddress)(line, m.machineId)).map((m) => (0, line_1.identOf)(line, m.machineId));
|
|
63
|
+
const machines = declared.map((ident) => [`user ${(0, envelope_1.clientIdOf)(ident)}`, ...rules('topic ', (0, envelope_1.addressOf)(ident))].join('\n'));
|
|
64
|
+
const learns = line.machines.some((m) => (0, line_1.learnsAddress)(line, m.machineId));
|
|
65
|
+
const learned = learns ? [['# Stations that learn their address: each certificate, under its own common name.', ...rules('pattern ', '%u')].join('\n')] : [];
|
|
66
|
+
const every = [...events, ...telemetry, ...commands];
|
|
67
|
+
const shadows = learns ? declared.map((ident) => shadowRules(line, (0, envelope_1.addressOf)(ident), own((0, envelope_1.addressOf)(ident), every))) : [];
|
|
68
|
+
const controller = [
|
|
69
|
+
`user ${(0, line_1.controllerId)(line)}`,
|
|
70
|
+
...commands.map((c) => `topic write ${(0, codec_1.topicOf)(codec, { siteId, lineId }, c)}`),
|
|
71
|
+
`topic read ${codec.root}/${siteId}/${lineId}/#`,
|
|
72
|
+
].join('\n');
|
|
73
|
+
return `# Rendered from the line definition by site/setup.ts. Do not edit by hand.\n\n${[...learned, ...shadows, ...machines, controller].join('\n\n')}\n`;
|
|
74
|
+
}
|
|
75
|
+
/** Deny a declared station's channels to a certificate named for its address, which the patterns would grant. */
|
|
76
|
+
function shadowRules(line, address, topics) {
|
|
77
|
+
const users = [(0, line_1.controllerId)(line), ...line.machines.filter((m) => !(0, line_1.learnsAddress)(line, m.machineId)).map((m) => (0, envelope_1.clientIdOf)((0, line_1.identOf)(line, m.machineId)))];
|
|
78
|
+
if (users.includes(address))
|
|
79
|
+
throw new Error(`address "${address}" is also a broker user name; a declared address on a learning line must differ from every user`);
|
|
80
|
+
return [`user ${address}`, ...topics.map((t) => `topic deny ${t}`)].join('\n');
|
|
81
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The site's NATS server, rendered from the line. It runs JetStream and an
|
|
3
|
+
* MQTT listener that only the machine broker's bridge logs in to (by client
|
|
4
|
+
* certificate; the CN is its user, and it may publish only under the line's
|
|
5
|
+
* topics). The gateway logs in by password and may only keep and read the
|
|
6
|
+
* line's streams through its own consumer (it can neither publish a machine
|
|
7
|
+
* subject nor delete a stream), and subscribe to its inbox and the line. The
|
|
8
|
+
* site NATS stands alone; Long Tail's escalation events stay inside Long Tail.
|
|
9
|
+
*/
|
|
10
|
+
import { type LineDef } from '../line';
|
|
11
|
+
export interface SiteConfOptions {
|
|
12
|
+
root: string;
|
|
13
|
+
gatewayUser: string;
|
|
14
|
+
gatewayPassword: string;
|
|
15
|
+
certDir: string;
|
|
16
|
+
storeDir: string;
|
|
17
|
+
mqttPort?: number;
|
|
18
|
+
}
|
|
19
|
+
/** The machine broker's bridge identity for a line. */
|
|
20
|
+
export declare function bridgeId(line: LineDef): string;
|
|
21
|
+
/** What the gateway may publish: its streams' and its consumer's JetStream requests and acks, never a machine subject. */
|
|
22
|
+
export declare function gatewayPublish(line: LineDef): string[];
|
|
23
|
+
export declare function renderSiteConf(line: LineDef, opts: SiteConfOptions): string;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* The site's NATS server, rendered from the line. It runs JetStream and an
|
|
4
|
+
* MQTT listener that only the machine broker's bridge logs in to (by client
|
|
5
|
+
* certificate; the CN is its user, and it may publish only under the line's
|
|
6
|
+
* topics). The gateway logs in by password and may only keep and read the
|
|
7
|
+
* line's streams through its own consumer (it can neither publish a machine
|
|
8
|
+
* subject nor delete a stream), and subscribe to its inbox and the line. The
|
|
9
|
+
* site NATS stands alone; Long Tail's escalation events stay inside Long Tail.
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.bridgeId = bridgeId;
|
|
13
|
+
exports.gatewayPublish = gatewayPublish;
|
|
14
|
+
exports.renderSiteConf = renderSiteConf;
|
|
15
|
+
const line_1 = require("../line");
|
|
16
|
+
const protocol_1 = require("../protocol");
|
|
17
|
+
const permissions_1 = require("../../messaging/nats/permissions");
|
|
18
|
+
const streams_1 = require("../transport/streams");
|
|
19
|
+
const q = JSON.stringify;
|
|
20
|
+
/** The machine broker's bridge identity for a line. */
|
|
21
|
+
function bridgeId(line) {
|
|
22
|
+
return `${line.siteId}-${line.lineId}-bridge`;
|
|
23
|
+
}
|
|
24
|
+
/** What the gateway may publish: its streams' and its consumer's JetStream requests and acks, never a machine subject. */
|
|
25
|
+
function gatewayPublish(line) {
|
|
26
|
+
return (0, permissions_1.jsPermissions)({ streams: Object.values(streams_1.LINE_STREAM), durable: `${(0, line_1.controllerId)(line)}-events` }).publish;
|
|
27
|
+
}
|
|
28
|
+
function renderSiteConf(line, opts) {
|
|
29
|
+
const base = (0, protocol_1.toSubject)(`${opts.root}/${line.siteId}/${line.lineId}`);
|
|
30
|
+
const bridge = [
|
|
31
|
+
` { user: ${q(`CN=${bridgeId(line)}`)}, allowed_connection_types: ["MQTT"],`,
|
|
32
|
+
` permissions: { publish: [${q(`${base}.>`)}], subscribe: { deny: [">"] } } }`,
|
|
33
|
+
].join('\n');
|
|
34
|
+
const gateway = [
|
|
35
|
+
` { user: ${q(opts.gatewayUser)}, password: ${q(opts.gatewayPassword)}, allowed_connection_types: ["STANDARD"],`,
|
|
36
|
+
` permissions: { publish: ${q(gatewayPublish(line))}, subscribe: ${q(['_INBOX.>', `${base}.>`])} } }`,
|
|
37
|
+
].join('\n');
|
|
38
|
+
return `# Rendered from the line definition by site/setup.ts. Do not edit by hand.
|
|
39
|
+
server_name: ${q(`${line.siteId}-${line.lineId}`)}
|
|
40
|
+
port: 4222
|
|
41
|
+
http: 8222
|
|
42
|
+
jetstream { store_dir: ${q(opts.storeDir)} }
|
|
43
|
+
|
|
44
|
+
authorization {
|
|
45
|
+
users: [
|
|
46
|
+
${bridge}
|
|
47
|
+
${gateway}
|
|
48
|
+
]
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
mqtt {
|
|
52
|
+
port: ${opts.mqttPort ?? 8883}
|
|
53
|
+
tls {
|
|
54
|
+
cert_file: ${q(`${opts.certDir}/server.pem`)}
|
|
55
|
+
key_file: ${q(`${opts.certDir}/server.key`)}
|
|
56
|
+
ca_file: ${q(`${opts.certDir}/ca.pem`)}
|
|
57
|
+
verify_and_map: true
|
|
58
|
+
timeout: 5
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
`;
|
|
62
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Escalation events from Long Tail's in-process event adapter. With a
|
|
3
|
+
* cross-container bus configured, the adapter also receives events other
|
|
4
|
+
* containers publish. Nothing is replayed: the gateway reconciles open rows
|
|
5
|
+
* when it starts leading a line.
|
|
6
|
+
*/
|
|
7
|
+
import type { CallbackEventAdapter } from '../../events/callback';
|
|
8
|
+
import { type EscalationEvent, type EscalationFeed } from './delivery';
|
|
9
|
+
import type { MachineDisposition } from '../../../types/machines';
|
|
10
|
+
export declare class CallbackEscalationFeed implements EscalationFeed {
|
|
11
|
+
private readonly adapter;
|
|
12
|
+
private off?;
|
|
13
|
+
private readonly redelivery;
|
|
14
|
+
constructor(adapter: CallbackEventAdapter, nakDelayMs?: number);
|
|
15
|
+
connect(): Promise<void>;
|
|
16
|
+
consume(handler: (event: EscalationEvent) => Promise<MachineDisposition>): Promise<void>;
|
|
17
|
+
close(): Promise<void>;
|
|
18
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Escalation events from Long Tail's in-process event adapter. With a
|
|
4
|
+
* cross-container bus configured, the adapter also receives events other
|
|
5
|
+
* containers publish. Nothing is replayed: the gateway reconciles open rows
|
|
6
|
+
* when it starts leading a line.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.CallbackEscalationFeed = void 0;
|
|
10
|
+
const delivery_1 = require("./delivery");
|
|
11
|
+
const serial_1 = require("./serial");
|
|
12
|
+
const ESCALATION_EVENTS = 'system.escalation.>';
|
|
13
|
+
class CallbackEscalationFeed {
|
|
14
|
+
constructor(adapter, nakDelayMs = 1_000) {
|
|
15
|
+
this.adapter = adapter;
|
|
16
|
+
this.redelivery = new serial_1.LocalRedelivery(new serial_1.SerialQueue(), nakDelayMs);
|
|
17
|
+
}
|
|
18
|
+
async connect() { }
|
|
19
|
+
async consume(handler) {
|
|
20
|
+
this.off = this.adapter.on(ESCALATION_EVENTS, (raw) => {
|
|
21
|
+
const event = (0, delivery_1.toEscalationEvent)(raw);
|
|
22
|
+
if (event)
|
|
23
|
+
void this.redelivery.deliver(() => handler(event));
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
async close() {
|
|
27
|
+
this.off?.();
|
|
28
|
+
await this.redelivery.stop();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
exports.CallbackEscalationFeed = CallbackEscalationFeed;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Two sides of the machine fabric. A machine holds a MachineLink: one MQTT
|
|
3
|
+
* client with a will, publishing under its own subtree and hearing only its
|
|
4
|
+
* own commands. The gateway holds a MachineBus: durable, ordered delivery of
|
|
5
|
+
* every machine message on the line, QoS 1 command egress, and retained reads.
|
|
6
|
+
*/
|
|
7
|
+
import type { Delivery } from '../../../types/messaging';
|
|
8
|
+
import type { EscalationEvent, EscalationFeed, InboundMessage, LinkConnectOptions, LinkWill, MachineBus, MachineLink, PublishOptions } from '../../../types/machines';
|
|
9
|
+
export type { EscalationEvent, EscalationFeed, InboundMessage, LinkConnectOptions, LinkWill, MachineBus, MachineLink, PublishOptions };
|
|
10
|
+
/** What the gateway does with a machine message: the messaging capability's deliveries (nak: its row does not exist yet). */
|
|
11
|
+
export declare const DISPOSITION: {
|
|
12
|
+
readonly ACK: "ack";
|
|
13
|
+
readonly NAK: "nak";
|
|
14
|
+
readonly TERM: "term";
|
|
15
|
+
};
|
|
16
|
+
export type Disposition = Delivery;
|
|
17
|
+
/** MQTT filter match with `+` (one level) and `#` (the rest). */
|
|
18
|
+
export declare function topicMatches(filter: string, topic: string): boolean;
|
|
19
|
+
/** Read an LTEvent body into an EscalationEvent; null for other families. */
|
|
20
|
+
export declare function toEscalationEvent(body: Record<string, any>): EscalationEvent | null;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Two sides of the machine fabric. A machine holds a MachineLink: one MQTT
|
|
4
|
+
* client with a will, publishing under its own subtree and hearing only its
|
|
5
|
+
* own commands. The gateway holds a MachineBus: durable, ordered delivery of
|
|
6
|
+
* every machine message on the line, QoS 1 command egress, and retained reads.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.DISPOSITION = void 0;
|
|
10
|
+
exports.topicMatches = topicMatches;
|
|
11
|
+
exports.toEscalationEvent = toEscalationEvent;
|
|
12
|
+
const constants_1 = require("../../messaging/constants");
|
|
13
|
+
/** What the gateway does with a machine message: the messaging capability's deliveries (nak: its row does not exist yet). */
|
|
14
|
+
exports.DISPOSITION = constants_1.DELIVERY;
|
|
15
|
+
/** MQTT filter match with `+` (one level) and `#` (the rest). */
|
|
16
|
+
function topicMatches(filter, topic) {
|
|
17
|
+
const f = filter.split('/');
|
|
18
|
+
const t = topic.split('/');
|
|
19
|
+
for (let i = 0; i < f.length; i++) {
|
|
20
|
+
if (f[i] === '#')
|
|
21
|
+
return true;
|
|
22
|
+
if (i >= t.length)
|
|
23
|
+
return false;
|
|
24
|
+
if (f[i] !== '+' && f[i] !== t[i])
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
return f.length === t.length;
|
|
28
|
+
}
|
|
29
|
+
const ESCALATION_SUBJECT = /^(?:.*\.)?system\.escalation\.([^.]+)\.([^.]+)\.([^.]+)$/;
|
|
30
|
+
/** Read an LTEvent body into an EscalationEvent; null for other families. */
|
|
31
|
+
function toEscalationEvent(body) {
|
|
32
|
+
const match = typeof body?.type === 'string' ? ESCALATION_SUBJECT.exec(body.type) : null;
|
|
33
|
+
if (!match)
|
|
34
|
+
return null;
|
|
35
|
+
const [, role, escalationId, verb] = match;
|
|
36
|
+
return { type: body.type, escalationId: body.escalationId ?? escalationId, role: body.role ?? role, verb, data: body.data ?? {} };
|
|
37
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export * from './delivery';
|
|
2
|
+
export * from './memory-broker';
|
|
3
|
+
export * from './memory';
|
|
4
|
+
export * from './serial';
|
|
5
|
+
export * from './streams';
|
|
6
|
+
export * from './mqtt-session';
|
|
7
|
+
export * from './mqtt-link';
|
|
8
|
+
export * from './mqtt-bus';
|
|
9
|
+
export * from './stream-bus';
|
|
10
|
+
export * from './nats-bus';
|
|
11
|
+
export * from './nats-feed';
|
|
12
|
+
export * from './pubsub-feed';
|
|
13
|
+
export * from './callback-feed';
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./delivery"), exports);
|
|
18
|
+
__exportStar(require("./memory-broker"), exports);
|
|
19
|
+
__exportStar(require("./memory"), exports);
|
|
20
|
+
__exportStar(require("./serial"), exports);
|
|
21
|
+
__exportStar(require("./streams"), exports);
|
|
22
|
+
__exportStar(require("./mqtt-session"), exports);
|
|
23
|
+
__exportStar(require("./mqtt-link"), exports);
|
|
24
|
+
__exportStar(require("./mqtt-bus"), exports);
|
|
25
|
+
__exportStar(require("./stream-bus"), exports);
|
|
26
|
+
__exportStar(require("./nats-bus"), exports);
|
|
27
|
+
__exportStar(require("./nats-feed"), exports);
|
|
28
|
+
__exportStar(require("./pubsub-feed"), exports);
|
|
29
|
+
__exportStar(require("./callback-feed"), exports);
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An in-process broker with the semantics the line relies on: retained
|
|
3
|
+
* messages, wills, persistent sessions that queue QoS 1 messages while a
|
|
4
|
+
* client is away, and a durable consumer log with ack, nak and replay (the
|
|
5
|
+
* JetStream stand-in). Used by tests and single-process runs.
|
|
6
|
+
*/
|
|
7
|
+
import { type Disposition, type InboundMessage, type LinkWill } from './delivery';
|
|
8
|
+
export interface MemoryBrokerOptions {
|
|
9
|
+
/** Delay before a nak'd message is offered again. */
|
|
10
|
+
redeliverMs?: number;
|
|
11
|
+
maxDeliver?: number;
|
|
12
|
+
}
|
|
13
|
+
export declare class MemoryBroker {
|
|
14
|
+
private sessions;
|
|
15
|
+
private retainedStore;
|
|
16
|
+
private log;
|
|
17
|
+
private durables;
|
|
18
|
+
private readonly redeliverMs;
|
|
19
|
+
private readonly maxDeliver;
|
|
20
|
+
constructor(opts?: MemoryBrokerOptions);
|
|
21
|
+
connect(clientId: string, will?: LinkWill): void;
|
|
22
|
+
disconnect(clientId: string, graceful: boolean): void;
|
|
23
|
+
isConnected(clientId: string): boolean;
|
|
24
|
+
subscribe(clientId: string, filter: string, handler: (msg: InboundMessage) => void): void;
|
|
25
|
+
publish(topic: string, payload: string, retain?: boolean): void;
|
|
26
|
+
retained(filter: string): InboundMessage[];
|
|
27
|
+
/** Attach a handler to a named durable consumer; it resumes from its cursor. */
|
|
28
|
+
consume(name: string, filter: string, handler: (msg: InboundMessage) => Promise<Disposition>): void;
|
|
29
|
+
/** Detach the handler; unacked and unread messages wait for the next consume. */
|
|
30
|
+
detach(name: string): void;
|
|
31
|
+
shutdown(): void;
|
|
32
|
+
private deliverToSession;
|
|
33
|
+
private pump;
|
|
34
|
+
private offer;
|
|
35
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* An in-process broker with the semantics the line relies on: retained
|
|
4
|
+
* messages, wills, persistent sessions that queue QoS 1 messages while a
|
|
5
|
+
* client is away, and a durable consumer log with ack, nak and replay (the
|
|
6
|
+
* JetStream stand-in). Used by tests and single-process runs.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.MemoryBroker = void 0;
|
|
10
|
+
const delivery_1 = require("./delivery");
|
|
11
|
+
class MemoryBroker {
|
|
12
|
+
constructor(opts = {}) {
|
|
13
|
+
this.sessions = new Map();
|
|
14
|
+
this.retainedStore = new Map();
|
|
15
|
+
this.log = [];
|
|
16
|
+
this.durables = new Map();
|
|
17
|
+
this.redeliverMs = opts.redeliverMs ?? 20;
|
|
18
|
+
this.maxDeliver = opts.maxDeliver ?? 50;
|
|
19
|
+
}
|
|
20
|
+
connect(clientId, will) {
|
|
21
|
+
const session = this.sessions.get(clientId) ?? { connected: false, subs: new Map(), queued: [] };
|
|
22
|
+
session.connected = true;
|
|
23
|
+
session.will = will;
|
|
24
|
+
this.sessions.set(clientId, session);
|
|
25
|
+
const queued = session.queued.splice(0);
|
|
26
|
+
for (const msg of queued)
|
|
27
|
+
this.deliverToSession(session, msg);
|
|
28
|
+
}
|
|
29
|
+
disconnect(clientId, graceful) {
|
|
30
|
+
const session = this.sessions.get(clientId);
|
|
31
|
+
if (!session?.connected)
|
|
32
|
+
return;
|
|
33
|
+
session.connected = false;
|
|
34
|
+
const will = session.will;
|
|
35
|
+
session.will = undefined;
|
|
36
|
+
if (!graceful && will)
|
|
37
|
+
this.publish(will.topic, will.payload, will.retain);
|
|
38
|
+
}
|
|
39
|
+
isConnected(clientId) {
|
|
40
|
+
return this.sessions.get(clientId)?.connected ?? false;
|
|
41
|
+
}
|
|
42
|
+
subscribe(clientId, filter, handler) {
|
|
43
|
+
const session = this.sessions.get(clientId);
|
|
44
|
+
if (!session)
|
|
45
|
+
throw new Error(`subscribe before connect: ${clientId}`);
|
|
46
|
+
session.subs.set(filter, handler);
|
|
47
|
+
for (const [topic, payload] of this.retainedStore) {
|
|
48
|
+
if ((0, delivery_1.topicMatches)(filter, topic))
|
|
49
|
+
queueMicrotask(() => handler({ topic, payload, retained: true, deliveries: 1 }));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
publish(topic, payload, retain = false) {
|
|
53
|
+
if (retain) {
|
|
54
|
+
if (payload === '')
|
|
55
|
+
this.retainedStore.delete(topic);
|
|
56
|
+
else
|
|
57
|
+
this.retainedStore.set(topic, payload);
|
|
58
|
+
}
|
|
59
|
+
for (const session of this.sessions.values()) {
|
|
60
|
+
this.deliverToSession(session, { topic, payload, retained: false, deliveries: 1 });
|
|
61
|
+
}
|
|
62
|
+
this.log.push({ seq: this.log.length, topic, payload, retained: retain });
|
|
63
|
+
for (const durable of this.durables.values())
|
|
64
|
+
void this.pump(durable);
|
|
65
|
+
}
|
|
66
|
+
retained(filter) {
|
|
67
|
+
return [...this.retainedStore]
|
|
68
|
+
.filter(([topic]) => (0, delivery_1.topicMatches)(filter, topic))
|
|
69
|
+
.map(([topic, payload]) => ({ topic, payload, retained: true, deliveries: 1 }));
|
|
70
|
+
}
|
|
71
|
+
/** Attach a handler to a named durable consumer; it resumes from its cursor. */
|
|
72
|
+
consume(name, filter, handler) {
|
|
73
|
+
const durable = this.durables.get(name) ?? { filter, cursor: 0, pumping: false, retries: new Map(), timers: new Set() };
|
|
74
|
+
durable.handler = handler;
|
|
75
|
+
this.durables.set(name, durable);
|
|
76
|
+
void this.pump(durable);
|
|
77
|
+
}
|
|
78
|
+
/** Detach the handler; unacked and unread messages wait for the next consume. */
|
|
79
|
+
detach(name) {
|
|
80
|
+
const durable = this.durables.get(name);
|
|
81
|
+
if (!durable)
|
|
82
|
+
return;
|
|
83
|
+
durable.handler = undefined;
|
|
84
|
+
for (const t of durable.timers)
|
|
85
|
+
clearTimeout(t);
|
|
86
|
+
durable.timers.clear();
|
|
87
|
+
if (durable.retries.size)
|
|
88
|
+
durable.cursor = Math.min(durable.cursor, ...durable.retries.keys());
|
|
89
|
+
durable.retries.clear();
|
|
90
|
+
}
|
|
91
|
+
shutdown() {
|
|
92
|
+
for (const name of this.durables.keys())
|
|
93
|
+
this.detach(name);
|
|
94
|
+
}
|
|
95
|
+
deliverToSession(session, msg) {
|
|
96
|
+
for (const [filter, handler] of session.subs) {
|
|
97
|
+
if (!(0, delivery_1.topicMatches)(filter, msg.topic))
|
|
98
|
+
continue;
|
|
99
|
+
if (session.connected)
|
|
100
|
+
queueMicrotask(() => handler(msg));
|
|
101
|
+
else
|
|
102
|
+
session.queued.push(msg);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
async pump(durable) {
|
|
106
|
+
if (durable.pumping)
|
|
107
|
+
return;
|
|
108
|
+
durable.pumping = true;
|
|
109
|
+
try {
|
|
110
|
+
while (durable.handler && durable.cursor < this.log.length) {
|
|
111
|
+
const entry = this.log[durable.cursor++];
|
|
112
|
+
if ((0, delivery_1.topicMatches)(durable.filter, entry.topic))
|
|
113
|
+
await this.offer(durable, entry.seq);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
finally {
|
|
117
|
+
durable.pumping = false;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
async offer(durable, seq) {
|
|
121
|
+
const handler = durable.handler;
|
|
122
|
+
if (!handler)
|
|
123
|
+
return;
|
|
124
|
+
const entry = this.log[seq];
|
|
125
|
+
const deliveries = (durable.retries.get(seq) ?? 0) + 1;
|
|
126
|
+
durable.retries.set(seq, deliveries);
|
|
127
|
+
let disposition;
|
|
128
|
+
try {
|
|
129
|
+
disposition = await handler({ topic: entry.topic, payload: entry.payload, retained: entry.retained, deliveries });
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
disposition = delivery_1.DISPOSITION.NAK;
|
|
133
|
+
}
|
|
134
|
+
if (disposition !== delivery_1.DISPOSITION.NAK || deliveries >= this.maxDeliver) {
|
|
135
|
+
durable.retries.delete(seq);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
const timer = setTimeout(() => {
|
|
139
|
+
durable.timers.delete(timer);
|
|
140
|
+
void this.offer(durable, seq);
|
|
141
|
+
}, this.redeliverMs);
|
|
142
|
+
durable.timers.add(timer);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
exports.MemoryBroker = MemoryBroker;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memory adapters over one MemoryBroker: a MachineLink per mock machine, the
|
|
3
|
+
* gateway's MachineBus, and an EscalationFeed that tests publish into the
|
|
4
|
+
* way Long Tail publishes escalation events.
|
|
5
|
+
*/
|
|
6
|
+
import { MemoryBroker } from './memory-broker';
|
|
7
|
+
import { type Disposition, type EscalationEvent, type EscalationFeed, type InboundMessage, type LinkConnectOptions, type MachineBus, type MachineLink, type PublishOptions } from './delivery';
|
|
8
|
+
export declare class MemoryMachineLink implements MachineLink {
|
|
9
|
+
private readonly broker;
|
|
10
|
+
private clientId?;
|
|
11
|
+
constructor(broker: MemoryBroker);
|
|
12
|
+
get connected(): boolean;
|
|
13
|
+
connect(opts: LinkConnectOptions): Promise<void>;
|
|
14
|
+
publish(topic: string, payload: string, opts: PublishOptions): Promise<void>;
|
|
15
|
+
subscribe(filter: string, handler: (msg: InboundMessage) => void): Promise<void>;
|
|
16
|
+
disconnect(graceful: boolean): Promise<void>;
|
|
17
|
+
}
|
|
18
|
+
export declare class MemoryMachineBus implements MachineBus {
|
|
19
|
+
private readonly broker;
|
|
20
|
+
private readonly filter;
|
|
21
|
+
private readonly durableName;
|
|
22
|
+
constructor(broker: MemoryBroker, filter: string, durableName?: string);
|
|
23
|
+
connect(): Promise<void>;
|
|
24
|
+
consume(handler: (msg: InboundMessage) => Promise<Disposition>): Promise<void>;
|
|
25
|
+
publish(topic: string, payload: string, opts?: {
|
|
26
|
+
retain?: boolean;
|
|
27
|
+
}): Promise<void>;
|
|
28
|
+
retained(filter: string): Promise<InboundMessage[]>;
|
|
29
|
+
close(): Promise<void>;
|
|
30
|
+
}
|
|
31
|
+
export declare class MemoryEscalationFeed implements EscalationFeed {
|
|
32
|
+
private readonly broker;
|
|
33
|
+
private readonly durableName;
|
|
34
|
+
constructor(broker: MemoryBroker, durableName?: string);
|
|
35
|
+
/** Publish an LTEvent body as Long Tail would. */
|
|
36
|
+
emit(body: Record<string, any>): void;
|
|
37
|
+
connect(): Promise<void>;
|
|
38
|
+
consume(handler: (event: EscalationEvent) => Promise<Disposition>): Promise<void>;
|
|
39
|
+
close(): Promise<void>;
|
|
40
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Memory adapters over one MemoryBroker: a MachineLink per mock machine, the
|
|
4
|
+
* gateway's MachineBus, and an EscalationFeed that tests publish into the
|
|
5
|
+
* way Long Tail publishes escalation events.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.MemoryEscalationFeed = exports.MemoryMachineBus = exports.MemoryMachineLink = void 0;
|
|
9
|
+
const delivery_1 = require("./delivery");
|
|
10
|
+
class MemoryMachineLink {
|
|
11
|
+
constructor(broker) {
|
|
12
|
+
this.broker = broker;
|
|
13
|
+
}
|
|
14
|
+
get connected() {
|
|
15
|
+
return !!this.clientId && this.broker.isConnected(this.clientId);
|
|
16
|
+
}
|
|
17
|
+
async connect(opts) {
|
|
18
|
+
this.clientId = opts.clientId;
|
|
19
|
+
this.broker.connect(opts.clientId, opts.will);
|
|
20
|
+
}
|
|
21
|
+
async publish(topic, payload, opts) {
|
|
22
|
+
if (!this.connected)
|
|
23
|
+
throw new Error(`publish while disconnected: ${topic}`);
|
|
24
|
+
this.broker.publish(topic, payload, opts.retain ?? false);
|
|
25
|
+
}
|
|
26
|
+
async subscribe(filter, handler) {
|
|
27
|
+
if (!this.clientId)
|
|
28
|
+
throw new Error('subscribe before connect');
|
|
29
|
+
this.broker.subscribe(this.clientId, filter, handler);
|
|
30
|
+
}
|
|
31
|
+
async disconnect(graceful) {
|
|
32
|
+
if (this.clientId)
|
|
33
|
+
this.broker.disconnect(this.clientId, graceful);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
exports.MemoryMachineLink = MemoryMachineLink;
|
|
37
|
+
class MemoryMachineBus {
|
|
38
|
+
constructor(broker, filter, durableName = 'line-gateway') {
|
|
39
|
+
this.broker = broker;
|
|
40
|
+
this.filter = filter;
|
|
41
|
+
this.durableName = durableName;
|
|
42
|
+
}
|
|
43
|
+
async connect() { }
|
|
44
|
+
async consume(handler) {
|
|
45
|
+
this.broker.consume(this.durableName, this.filter, handler);
|
|
46
|
+
}
|
|
47
|
+
async publish(topic, payload, opts = {}) {
|
|
48
|
+
this.broker.publish(topic, payload, opts.retain ?? false);
|
|
49
|
+
}
|
|
50
|
+
async retained(filter) {
|
|
51
|
+
return this.broker.retained(filter);
|
|
52
|
+
}
|
|
53
|
+
async close() {
|
|
54
|
+
this.broker.detach(this.durableName);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
exports.MemoryMachineBus = MemoryMachineBus;
|
|
58
|
+
const FEED_TOPIC = 'lt/events';
|
|
59
|
+
class MemoryEscalationFeed {
|
|
60
|
+
constructor(broker, durableName = 'line-gateway-escalations') {
|
|
61
|
+
this.broker = broker;
|
|
62
|
+
this.durableName = durableName;
|
|
63
|
+
}
|
|
64
|
+
/** Publish an LTEvent body as Long Tail would. */
|
|
65
|
+
emit(body) {
|
|
66
|
+
this.broker.publish(FEED_TOPIC, JSON.stringify(body));
|
|
67
|
+
}
|
|
68
|
+
async connect() { }
|
|
69
|
+
async consume(handler) {
|
|
70
|
+
this.broker.consume(this.durableName, FEED_TOPIC, async (msg) => {
|
|
71
|
+
const event = (0, delivery_1.toEscalationEvent)(JSON.parse(msg.payload));
|
|
72
|
+
return event ? handler(event) : delivery_1.DISPOSITION.TERM;
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
async close() {
|
|
76
|
+
this.broker.detach(this.durableName);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
exports.MemoryEscalationFeed = MemoryEscalationFeed;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The gateway over plain MQTT, for any broker. The persistent session keeps
|
|
3
|
+
* QoS 1 messages while the gateway is away; there is no stream to replay
|
|
4
|
+
* beyond that, and a message that is not yet actionable is retried in this
|
|
5
|
+
* process.
|
|
6
|
+
*/
|
|
7
|
+
import type { MqttEndpoint } from '../../../services/machines/config';
|
|
8
|
+
import type { Disposition, InboundMessage, MachineBus } from './delivery';
|
|
9
|
+
export declare class MqttMachineBus implements MachineBus {
|
|
10
|
+
private readonly endpoint;
|
|
11
|
+
private readonly clientId;
|
|
12
|
+
private readonly filter;
|
|
13
|
+
private session?;
|
|
14
|
+
private readonly redelivery;
|
|
15
|
+
constructor(endpoint: MqttEndpoint, clientId: string, filter: string, nakDelayMs?: number);
|
|
16
|
+
connect(): Promise<void>;
|
|
17
|
+
consume(handler: (msg: InboundMessage) => Promise<Disposition>): Promise<void>;
|
|
18
|
+
publish(topic: string, payload: string, opts?: {
|
|
19
|
+
retain?: boolean;
|
|
20
|
+
}): Promise<void>;
|
|
21
|
+
retained(filter: string): Promise<InboundMessage[]>;
|
|
22
|
+
close(): Promise<void>;
|
|
23
|
+
private connected;
|
|
24
|
+
}
|