@hotmeshio/long-tail 0.25.3 → 0.26.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.
Files changed (145) hide show
  1. package/README.md +1 -0
  2. package/build/index.d.ts +1 -0
  3. package/build/index.js +8 -1
  4. package/build/lib/machines/index.d.ts +5 -0
  5. package/build/lib/machines/index.js +21 -0
  6. package/build/lib/machines/line.d.ts +44 -0
  7. package/build/lib/machines/line.js +114 -0
  8. package/build/lib/machines/protocol/codec.d.ts +92 -0
  9. package/build/lib/machines/protocol/codec.js +79 -0
  10. package/build/lib/machines/protocol/constants.d.ts +120 -0
  11. package/build/lib/machines/protocol/constants.js +97 -0
  12. package/build/lib/machines/protocol/contract.d.ts +19 -0
  13. package/build/lib/machines/protocol/contract.js +132 -0
  14. package/build/lib/machines/protocol/envelope.d.ts +14 -0
  15. package/build/lib/machines/protocol/envelope.js +43 -0
  16. package/build/lib/machines/protocol/index.d.ts +5 -0
  17. package/build/lib/machines/protocol/index.js +21 -0
  18. package/build/lib/machines/protocol/topics.d.ts +33 -0
  19. package/build/lib/machines/protocol/topics.js +79 -0
  20. package/build/lib/machines/site/mosquitto-conf.d.ts +27 -0
  21. package/build/lib/machines/site/mosquitto-conf.js +81 -0
  22. package/build/lib/machines/site/site-conf.d.ts +20 -0
  23. package/build/lib/machines/site/site-conf.js +55 -0
  24. package/build/lib/machines/transport/callback-feed.d.ts +18 -0
  25. package/build/lib/machines/transport/callback-feed.js +31 -0
  26. package/build/lib/machines/transport/delivery.d.ts +21 -0
  27. package/build/lib/machines/transport/delivery.js +42 -0
  28. package/build/lib/machines/transport/index.d.ts +11 -0
  29. package/build/lib/machines/transport/index.js +27 -0
  30. package/build/lib/machines/transport/memory-broker.d.ts +35 -0
  31. package/build/lib/machines/transport/memory-broker.js +145 -0
  32. package/build/lib/machines/transport/memory.d.ts +40 -0
  33. package/build/lib/machines/transport/memory.js +79 -0
  34. package/build/lib/machines/transport/mqtt-bus.d.ts +24 -0
  35. package/build/lib/machines/transport/mqtt-bus.js +43 -0
  36. package/build/lib/machines/transport/mqtt-link.d.ts +16 -0
  37. package/build/lib/machines/transport/mqtt-link.js +37 -0
  38. package/build/lib/machines/transport/mqtt-session.d.ts +34 -0
  39. package/build/lib/machines/transport/mqtt-session.js +127 -0
  40. package/build/lib/machines/transport/nats-bus.d.ts +42 -0
  41. package/build/lib/machines/transport/nats-bus.js +67 -0
  42. package/build/lib/machines/transport/nats-durable.d.ts +11 -0
  43. package/build/lib/machines/transport/nats-durable.js +28 -0
  44. package/build/lib/machines/transport/nats-feed.d.ts +26 -0
  45. package/build/lib/machines/transport/nats-feed.js +49 -0
  46. package/build/lib/machines/transport/serial.d.ts +21 -0
  47. package/build/lib/machines/transport/serial.js +54 -0
  48. package/build/lib/machines/transport/streams.d.ts +28 -0
  49. package/build/lib/machines/transport/streams.js +92 -0
  50. package/build/routes/oauth-server/authorize.js +4 -4
  51. package/build/scripts/machine-line.d.ts +33 -0
  52. package/build/scripts/machine-line.js +301 -0
  53. package/build/services/auth/oauth-server/authorization.d.ts +5 -0
  54. package/build/services/auth/oauth-server/authorization.js +8 -0
  55. package/build/services/auth/oauth-server/metadata.d.ts +1 -1
  56. package/build/services/auth/oauth-server/metadata.js +2 -1
  57. package/build/services/machines/commands.d.ts +29 -0
  58. package/build/services/machines/commands.js +37 -0
  59. package/build/services/machines/config.d.ts +43 -0
  60. package/build/services/machines/config.js +84 -0
  61. package/build/services/machines/contracts.d.ts +193 -0
  62. package/build/services/machines/contracts.js +115 -0
  63. package/build/services/machines/forms.d.ts +55 -0
  64. package/build/services/machines/forms.js +37 -0
  65. package/build/services/machines/gateway/actions.d.ts +111 -0
  66. package/build/services/machines/gateway/actions.js +8 -0
  67. package/build/services/machines/gateway/address-book.d.ts +16 -0
  68. package/build/services/machines/gateway/address-book.js +24 -0
  69. package/build/services/machines/gateway/admission.d.ts +35 -0
  70. package/build/services/machines/gateway/admission.js +66 -0
  71. package/build/services/machines/gateway/api-escalations.d.ts +23 -0
  72. package/build/services/machines/gateway/api-escalations.js +101 -0
  73. package/build/services/machines/gateway/dispatcher.d.ts +63 -0
  74. package/build/services/machines/gateway/dispatcher.js +157 -0
  75. package/build/services/machines/gateway/escalations.d.ts +51 -0
  76. package/build/services/machines/gateway/escalations.js +40 -0
  77. package/build/services/machines/gateway/executor.d.ts +55 -0
  78. package/build/services/machines/gateway/executor.js +195 -0
  79. package/build/services/machines/gateway/gateway.d.ts +82 -0
  80. package/build/services/machines/gateway/gateway.js +247 -0
  81. package/build/services/machines/gateway/index.d.ts +10 -0
  82. package/build/services/machines/gateway/index.js +26 -0
  83. package/build/services/machines/gateway/jobs.d.ts +16 -0
  84. package/build/services/machines/gateway/jobs.js +24 -0
  85. package/build/services/machines/gateway/log.d.ts +41 -0
  86. package/build/services/machines/gateway/log.js +42 -0
  87. package/build/services/machines/gateway/lt-http.d.ts +51 -0
  88. package/build/services/machines/gateway/lt-http.js +120 -0
  89. package/build/services/machines/gateway/pacing.d.ts +29 -0
  90. package/build/services/machines/gateway/pacing.js +134 -0
  91. package/build/services/machines/gateway/plan-escalation.d.ts +19 -0
  92. package/build/services/machines/gateway/plan-escalation.js +201 -0
  93. package/build/services/machines/gateway/plan-machine.d.ts +16 -0
  94. package/build/services/machines/gateway/plan-machine.js +242 -0
  95. package/build/services/machines/gateway/plan-shared.d.ts +18 -0
  96. package/build/services/machines/gateway/plan-shared.js +36 -0
  97. package/build/services/machines/gateway/provision.d.ts +22 -0
  98. package/build/services/machines/gateway/provision.js +79 -0
  99. package/build/services/machines/gateway/receipts.d.ts +33 -0
  100. package/build/services/machines/gateway/receipts.js +65 -0
  101. package/build/services/machines/gateway/reconcile.d.ts +33 -0
  102. package/build/services/machines/gateway/reconcile.js +86 -0
  103. package/build/services/machines/gateway/registration.d.ts +38 -0
  104. package/build/services/machines/gateway/registration.js +121 -0
  105. package/build/services/machines/gateway/reply-waiter.d.ts +24 -0
  106. package/build/services/machines/gateway/reply-waiter.js +65 -0
  107. package/build/services/machines/gateway/state.d.ts +125 -0
  108. package/build/services/machines/gateway/state.js +228 -0
  109. package/build/services/machines/gateway/trace.d.ts +16 -0
  110. package/build/services/machines/gateway/trace.js +43 -0
  111. package/build/services/machines/identities.d.ts +25 -0
  112. package/build/services/machines/identities.js +77 -0
  113. package/build/services/machines/index.d.ts +11 -0
  114. package/build/services/machines/index.js +27 -0
  115. package/build/services/machines/leadership.d.ts +22 -0
  116. package/build/services/machines/leadership.js +75 -0
  117. package/build/services/machines/roles.d.ts +13 -0
  118. package/build/services/machines/roles.js +62 -0
  119. package/build/services/machines/rows.d.ts +30 -0
  120. package/build/services/machines/rows.js +56 -0
  121. package/build/services/machines/select.d.ts +18 -0
  122. package/build/services/machines/select.js +63 -0
  123. package/build/services/machines/surface.d.ts +33 -0
  124. package/build/services/machines/surface.js +98 -0
  125. package/build/services/machines/testing/fake-long-tail.d.ts +78 -0
  126. package/build/services/machines/testing/fake-long-tail.js +155 -0
  127. package/build/services/machines/testing/index.d.ts +3 -0
  128. package/build/services/machines/testing/index.js +19 -0
  129. package/build/services/machines/testing/recording-log.d.ts +14 -0
  130. package/build/services/machines/testing/recording-log.js +22 -0
  131. package/build/services/machines/testing/wait.d.ts +3 -0
  132. package/build/services/machines/testing/wait.js +17 -0
  133. package/build/services/mcp/db-server/schemas.d.ts +2 -2
  134. package/build/start/index.js +13 -0
  135. package/build/system/mcp-servers/admin/schemas.d.ts +221 -221
  136. package/build/system/mcp-servers/db-query/schemas.d.ts +2 -2
  137. package/build/system/mcp-servers/human-queue-schemas.d.ts +6 -6
  138. package/build/tsconfig.tsbuildinfo +1 -1
  139. package/build/types/index.d.ts +2 -1
  140. package/build/types/machines.d.ts +166 -0
  141. package/build/types/machines.js +7 -0
  142. package/build/types/startup.d.ts +23 -0
  143. package/docs/machines.md +293 -0
  144. package/docs/mcp.md +6 -1
  145. package/package.json +11 -1
package/README.md CHANGED
@@ -246,6 +246,7 @@ All modes share PostgreSQL and scale independently. See [Cloud Deployment](https
246
246
  | [Schema Enforcement](https://github.com/hotmeshio/long-tail/blob/main/docs/schema-enforcement.md) | form_schema as an enforced API contract on every resolve surface |
247
247
  | [Code-Owned Configuration](https://github.com/hotmeshio/long-tail/blob/main/docs/code-owned-configuration.md) | `configSource` — declared config compared and applied at every boot |
248
248
  | [Faceted Routing](https://github.com/hotmeshio/long-tail/blob/main/docs/faceted-routing.md) | Query and atomically claim the queue by facets; dispatcher pattern |
249
+ | [Machines](https://github.com/hotmeshio/long-tail/blob/main/docs/machines.md) | `start({ machines })`: machines resolve escalation rows over MQTT, as people do with forms |
249
250
  | [SDK](https://github.com/hotmeshio/long-tail/blob/main/docs/sdk.md) | Embedded usage, `createClient`, event subscriptions |
250
251
  | [Architecture](https://github.com/hotmeshio/long-tail/blob/main/docs/architecture.md) | Project structure, conventions, discovery |
251
252
  | [Cloud](https://github.com/hotmeshio/long-tail/blob/main/docs/cloud.md) | AWS ECS, GCP Cloud Run, Docker |
package/build/index.d.ts CHANGED
@@ -50,4 +50,5 @@ export { resolveCredential } from './services/iam/credentials';
50
50
  export * as KnowledgeApi from './api/knowledge';
51
51
  export type { LTClient, LTClientOptions } from './sdk';
52
52
  export * as api from './api';
53
+ export * as Machines from './services/machines';
53
54
  export { LTExpressAdapter } from './adapters/express';
package/build/index.js CHANGED
@@ -37,11 +37,12 @@ var __importStar = (this && this.__importStar) || (function () {
37
37
  })();
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.McpClient = exports.McpService = exports.BuiltInMcpAdapter = exports.mcpRegistry = exports.defaultMaintenanceConfig = exports.maintenanceRegistry = exports.PinoLoggerAdapter = exports.loggerRegistry = exports.HoneycombTelemetryAdapter = exports.telemetryRegistry = exports.publishKnowledgeEvent = exports.publishAgentEvent = exports.publishWorkflowEvent = exports.publishActivityEvent = exports.publishEscalationEvent = exports.publishTaskEvent = exports.publishMilestoneEvent = exports.SocketIOEventAdapter = exports.InMemoryEventAdapter = exports.NatsEventAdapter = exports.eventRegistry = exports.ltConfig = exports.seedConfigTopics = exports.seedSystemTopics = exports.TopicService = exports.displayGtin = exports.normalizeGtin = exports.isValidGtin = exports.assertValidScanIdentityRule = exports.assertValidScanSteps = exports.assertValidScanScheme = exports.UserService = exports.ConfigService = exports.EscalationService = exports.TaskService = exports.setAuthAdapter = exports.signToken = exports.requireAdmin = exports.requireAuth = exports.createAuthMiddleware = exports.JwtAuthAdapter = exports.conditionalAccumulator = exports.conditionLT = exports.conditional = exports.executeLT = exports.createLTActivityInterceptor = exports.createLTInterceptor = exports.registerLT = exports.midnightTheme = exports.start = void 0;
40
- exports.LTExpressAdapter = exports.api = exports.KnowledgeApi = exports.resolveCredential = exports.mimeFromPath = exports.getStorageBackend = exports.listOAuthConnections = exports.getFreshAccessToken = exports.createClient = exports.CallbackEventAdapter = exports.builtinMcpServerFactories = exports.getSystemAgents = exports.getSystemWorkers = exports.registerMcpTool = exports.getToolContext = exports.getActivityIdentity = exports.McpEscalationStrategy = exports.DefaultEscalationStrategy = exports.escalationStrategyRegistry = exports.McpVisionServer = exports.McpTranslationServer = exports.McpServer = void 0;
40
+ exports.LTExpressAdapter = exports.Machines = exports.api = exports.KnowledgeApi = exports.resolveCredential = exports.mimeFromPath = exports.getStorageBackend = exports.listOAuthConnections = exports.getFreshAccessToken = exports.createClient = exports.CallbackEventAdapter = exports.builtinMcpServerFactories = exports.getSystemAgents = exports.getSystemWorkers = exports.registerMcpTool = exports.getToolContext = exports.getActivityIdentity = exports.McpEscalationStrategy = exports.DefaultEscalationStrategy = exports.escalationStrategyRegistry = exports.McpVisionServer = exports.McpTranslationServer = exports.McpServer = void 0;
41
41
  const config_1 = require("./modules/config");
42
42
  const logger_1 = require("./lib/logger");
43
43
  const midnight_1 = require("./themes/midnight");
44
44
  const start_1 = require("./start");
45
+ const line_1 = require("./lib/machines/line");
45
46
  // ─── Package Exports ─────────────────────────────────────────────────────────
46
47
  var start_2 = require("./start");
47
48
  Object.defineProperty(exports, "start", { enumerable: true, get: function () { return start_2.start; } });
@@ -151,6 +152,7 @@ var credentials_1 = require("./services/iam/credentials");
151
152
  Object.defineProperty(exports, "resolveCredential", { enumerable: true, get: function () { return credentials_1.resolveCredential; } });
152
153
  exports.KnowledgeApi = __importStar(require("./api/knowledge"));
153
154
  exports.api = __importStar(require("./api"));
155
+ exports.Machines = __importStar(require("./services/machines"));
154
156
  var express_1 = require("./adapters/express");
155
157
  Object.defineProperty(exports, "LTExpressAdapter", { enumerable: true, get: function () { return express_1.LTExpressAdapter; } });
156
158
  // ─── Server ──────────────────────────────────────────────────────────────────
@@ -252,6 +254,11 @@ async function main() {
252
254
  escalation: {
253
255
  strategy: 'mcp',
254
256
  },
257
+ // Machines resolve rows over MQTT when a line and a transport are configured
258
+ // (docker-compose.machines.yml sets both).
259
+ machines: process.env.MACHINE_TRANSPORT && process.env.MACHINE_LINE_CONFIG
260
+ ? { lines: [(0, line_1.loadLine)(process.env.MACHINE_LINE_CONFIG)] }
261
+ : undefined,
255
262
  branding: {
256
263
  ...(process.env.WHITE_LABEL ? { appName: process.env.WHITE_LABEL } : {}),
257
264
  // Midnight ships as a registered theme — the working proof that a
@@ -0,0 +1,5 @@
1
+ export * from './protocol';
2
+ export * from './transport';
3
+ export * from './line';
4
+ export * from './site/site-conf';
5
+ export * from './site/mosquitto-conf';
@@ -0,0 +1,21 @@
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("./protocol"), exports);
18
+ __exportStar(require("./transport"), exports);
19
+ __exportStar(require("./line"), exports);
20
+ __exportStar(require("./site/site-conf"), exports);
21
+ __exportStar(require("./site/mosquitto-conf"), exports);
@@ -0,0 +1,44 @@
1
+ /**
2
+ * The line: site, line and the machines on it, in route order, read from a
3
+ * JSON file (MACHINE_LINE_CONFIG) or passed in by the host.
4
+ */
5
+ import type { LineDef, MachineDef, MachineIdent, MaterialDef, MeasurementDef } from '../../types/machines';
6
+ export type { LineDef, MachineDef, MaterialDef, MeasurementDef };
7
+ /** How a line knows its machines' addresses (`LineDef.addressing`). */
8
+ export declare const ADDRESSING: {
9
+ readonly DECLARED: "declared";
10
+ readonly LEARNED: "learned";
11
+ };
12
+ /** Whether the line learns this station's address from the machine that claims it. */
13
+ export declare function learnsAddress(line: LineDef, machineId: string): boolean;
14
+ /** Which machine puts a unit on its carrier plate and which takes it off. */
15
+ export declare const CARRIER_ROLE: {
16
+ readonly BIND: "bind";
17
+ readonly RELEASE: "release";
18
+ readonly NONE: "none";
19
+ };
20
+ export type CarrierRole = typeof CARRIER_ROLE[keyof typeof CARRIER_ROLE];
21
+ /** Reject a line whose identifiers would not survive the topic mapping. */
22
+ export declare function validateLine(line: LineDef): LineDef;
23
+ /** The line at `path` (JSON), or `fallback` when no path is given. */
24
+ export declare function loadLine(path: string | undefined, fallback?: LineDef): LineDef;
25
+ export declare function identOf(line: LineDef, machineId: string): MachineIdent;
26
+ export declare function machineById(line: LineDef, machineId: string): MachineDef;
27
+ /** The broker identity of each machine whose address the line declares, {site}-{line}-{address}. */
28
+ export declare function clientIds(line: LineDef): string[];
29
+ /** The broker and Long Tail identity of the line's gateway. */
30
+ export declare function controllerId(line: LineDef): string;
31
+ /** The default role name for a process. */
32
+ export declare function stationRole(process: string): string;
33
+ export declare const DEFAULT_EXIT_ROLE = "line-exit";
34
+ export declare const DEFAULT_OPERATOR_ROLE = "line-operator";
35
+ /** The Long Tail role a machine resolves handoffs under. */
36
+ export declare function machineRole(def: Pick<MachineDef, 'role' | 'process'>): string;
37
+ /** Every station role on the line, once each, in route order. */
38
+ export declare function stationRoles(line: LineDef): string[];
39
+ /** The role of the person who receives units at line exit. */
40
+ export declare function exitRole(line: LineDef): string;
41
+ /** Roles of people who take units straight from a machine: the exit role and any receivers. */
42
+ export declare function receiverRoles(line: LineDef): string[];
43
+ /** The role of the person who decides failed handoffs and closes alarm and machine rows. */
44
+ export declare function operatorRole(line: LineDef): string;
@@ -0,0 +1,114 @@
1
+ "use strict";
2
+ /**
3
+ * The line: site, line and the machines on it, in route order, read from a
4
+ * JSON file (MACHINE_LINE_CONFIG) or passed in by the host.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.DEFAULT_OPERATOR_ROLE = exports.DEFAULT_EXIT_ROLE = exports.CARRIER_ROLE = exports.ADDRESSING = void 0;
8
+ exports.learnsAddress = learnsAddress;
9
+ exports.validateLine = validateLine;
10
+ exports.loadLine = loadLine;
11
+ exports.identOf = identOf;
12
+ exports.machineById = machineById;
13
+ exports.clientIds = clientIds;
14
+ exports.controllerId = controllerId;
15
+ exports.stationRole = stationRole;
16
+ exports.machineRole = machineRole;
17
+ exports.stationRoles = stationRoles;
18
+ exports.exitRole = exitRole;
19
+ exports.receiverRoles = receiverRoles;
20
+ exports.operatorRole = operatorRole;
21
+ const fs_1 = require("fs");
22
+ const protocol_1 = require("./protocol");
23
+ /** How a line knows its machines' addresses (`LineDef.addressing`). */
24
+ exports.ADDRESSING = { DECLARED: 'declared', LEARNED: 'learned' };
25
+ /** Whether the line learns this station's address from the machine that claims it. */
26
+ function learnsAddress(line, machineId) {
27
+ return line.addressing === exports.ADDRESSING.LEARNED && !line.machines.find((m) => m.machineId === machineId)?.address;
28
+ }
29
+ /** Which machine puts a unit on its carrier plate and which takes it off. */
30
+ exports.CARRIER_ROLE = { BIND: 'bind', RELEASE: 'release', NONE: 'none' };
31
+ /** Reject a line whose identifiers would not survive the topic mapping. */
32
+ function validateLine(line) {
33
+ (0, protocol_1.assertTopicLevel)('siteId', line.siteId);
34
+ (0, protocol_1.assertTopicLevel)('lineId', line.lineId);
35
+ if (!line.machines?.length)
36
+ throw new Error('line has no machines');
37
+ if (line.addressing && !Object.values(exports.ADDRESSING).includes(line.addressing)) {
38
+ throw new Error(`addressing must be one of ${Object.values(exports.ADDRESSING).join(', ')}`);
39
+ }
40
+ const seen = new Set();
41
+ const addresses = new Set();
42
+ for (const m of line.machines) {
43
+ (0, protocol_1.assertTopicLevel)('machineId', m.machineId);
44
+ if (seen.has(m.machineId))
45
+ throw new Error(`duplicate machineId "${m.machineId}"`);
46
+ seen.add(m.machineId);
47
+ if (!learnsAddress(line, m.machineId)) {
48
+ const address = m.address ?? m.machineId;
49
+ (0, protocol_1.assertTopicLevel)('address', address);
50
+ if (addresses.has(address))
51
+ throw new Error(`duplicate address "${address}"`);
52
+ addresses.add(address);
53
+ }
54
+ if (!(m.cycleSec > 0))
55
+ throw new Error(`${m.machineId}: cycleSec must be positive`);
56
+ if (m.queueDepth !== undefined && !(Number.isInteger(m.queueDepth) && m.queueDepth >= 1)) {
57
+ throw new Error(`${m.machineId}: queueDepth must be a whole number of at least 1`);
58
+ }
59
+ }
60
+ return line;
61
+ }
62
+ /** The line at `path` (JSON), or `fallback` when no path is given. */
63
+ function loadLine(path, fallback) {
64
+ if (!path) {
65
+ if (!fallback)
66
+ throw new Error('no line definition: set MACHINE_LINE_CONFIG or pass one');
67
+ return validateLine(fallback);
68
+ }
69
+ return validateLine(JSON.parse((0, fs_1.readFileSync)(path, 'utf8')));
70
+ }
71
+ function identOf(line, machineId) {
72
+ const address = line.machines.find((m) => m.machineId === machineId)?.address;
73
+ return { siteId: line.siteId, lineId: line.lineId, machineId, ...(address ? { address } : {}) };
74
+ }
75
+ function machineById(line, machineId) {
76
+ const def = line.machines.find((m) => m.machineId === machineId);
77
+ if (!def)
78
+ throw new Error(`machine "${machineId}" is not on ${line.siteId}/${line.lineId}`);
79
+ return def;
80
+ }
81
+ /** The broker identity of each machine whose address the line declares, {site}-{line}-{address}. */
82
+ function clientIds(line) {
83
+ return line.machines.filter((m) => !learnsAddress(line, m.machineId)).map((m) => (0, protocol_1.clientIdOf)(identOf(line, m.machineId)));
84
+ }
85
+ /** The broker and Long Tail identity of the line's gateway. */
86
+ function controllerId(line) {
87
+ return `line-controller-${line.siteId}-${line.lineId}`;
88
+ }
89
+ /** The default role name for a process. */
90
+ function stationRole(process) {
91
+ return `line-${process.replace(/_/g, '-')}`;
92
+ }
93
+ exports.DEFAULT_EXIT_ROLE = 'line-exit';
94
+ exports.DEFAULT_OPERATOR_ROLE = 'line-operator';
95
+ /** The Long Tail role a machine resolves handoffs under. */
96
+ function machineRole(def) {
97
+ return def.role ?? stationRole(def.process);
98
+ }
99
+ /** Every station role on the line, once each, in route order. */
100
+ function stationRoles(line) {
101
+ return [...new Set(line.machines.map(machineRole))];
102
+ }
103
+ /** The role of the person who receives units at line exit. */
104
+ function exitRole(line) {
105
+ return line.roles?.exit ?? exports.DEFAULT_EXIT_ROLE;
106
+ }
107
+ /** Roles of people who take units straight from a machine: the exit role and any receivers. */
108
+ function receiverRoles(line) {
109
+ return [...new Set([exitRole(line), ...(line.roles?.receivers ?? [])])];
110
+ }
111
+ /** The role of the person who decides failed handoffs and closes alarm and machine rows. */
112
+ function operatorRole(line) {
113
+ return line.roles?.operator ?? exports.DEFAULT_OPERATOR_ROLE;
114
+ }
@@ -0,0 +1,92 @@
1
+ /**
2
+ * The wire, behind one seam. A codec turns a machine's MQTT messages into
3
+ * the line protocol's events (topics.ts, contract.ts) and the gateway's
4
+ * commands back into MQTT messages. The gateway, the rows and the workflow
5
+ * only ever see the line protocol. The generic codec speaks it natively; a
6
+ * host whose machines speak another protocol supplies a codec for it.
7
+ */
8
+ import type { LineDef } from '../../../types/machines';
9
+ import { type Inspection } from './contract';
10
+ import { type MachineIdent } from './envelope';
11
+ export interface WireMessage {
12
+ topic: string;
13
+ payload: string;
14
+ }
15
+ export interface MachineCommand {
16
+ cmdId: string;
17
+ op: string;
18
+ args: Record<string, unknown>;
19
+ }
20
+ /** MQTT filters under each machine's topic, e.g. `job` or `alarm/+`. */
21
+ export interface CodecChannels {
22
+ /** Messages the gateway acts on; kept in the durable event stream. */
23
+ events: string[];
24
+ /** Readings that only prove a machine is alive; read live. */
25
+ telemetry: string[];
26
+ /** Where a machine reads its commands; only the line controller writes them. */
27
+ commands: string[];
28
+ }
29
+ /** Which topics a filter covers: a level left out matches any (`+`). */
30
+ export interface TopicScope {
31
+ siteId?: string;
32
+ lineId?: string;
33
+ /** The machine's wire address (`MachineDef.address`, else its id). */
34
+ address?: string;
35
+ }
36
+ export interface LineCodec {
37
+ readonly root: string;
38
+ readonly channels: CodecChannels;
39
+ /**
40
+ * The topic of one machine channel. Defaults to `{root}/{site}/{line}/{address}/{channel}`.
41
+ * A layout must begin with `{root}/{site}/{line}/`, the prefix the line's subscription and
42
+ * retained reads use; the ACL and stream subjects are built from this.
43
+ */
44
+ topicFor?(scope: TopicScope, channel: string): string;
45
+ /** Most units one dispatch may carry; the gateway sends one dispatch per unit when 1. Null: any. */
46
+ readonly unitsPerDispatch: number | null;
47
+ /**
48
+ * When a handoff's job goes to its receiver: as soon as the row exists (the default), or once every
49
+ * unit has arrived there, for machines that take work only for units they have scanned in. Each
50
+ * dispatched unit then carries the arrival event its receiver reported.
51
+ */
52
+ readonly dispatchOn?: DispatchOn;
53
+ /**
54
+ * Strict: a message with contract violations is confirmed but never acted on, and a row shows it
55
+ * to a person. Otherwise (the default) known fields are acted on and the violations are logged.
56
+ */
57
+ readonly strict?: boolean;
58
+ /** One wire message as zero or more line-protocol messages; empty when the topic is not a machine's. */
59
+ decode(topic: string, payload: string, retained?: boolean): Inspection[];
60
+ /** The command as wire messages; none when the machines have no such command, and the gateway then sends nothing. */
61
+ encode(ident: MachineIdent, command: MachineCommand): WireMessage[];
62
+ /**
63
+ * The receipt confirming messages the gateway is done with (each decoded with a `receiptId`), for
64
+ * machines that keep an outbox until confirmed. Absent: no receipts are sent.
65
+ */
66
+ receipt?(ident: MachineIdent, messageIds: string[]): WireMessage[];
67
+ }
68
+ export declare const DISPATCH_ON: {
69
+ readonly CREATED: "created";
70
+ readonly ARRIVAL: "arrival";
71
+ };
72
+ export type DispatchOn = typeof DISPATCH_ON[keyof typeof DISPATCH_ON];
73
+ /** Which job event counts as the receiver's receipt. */
74
+ export declare const RECEIPT_FROM: {
75
+ readonly ARRIVED: "arrived";
76
+ readonly BOUND: "bound";
77
+ readonly STARTED: "started";
78
+ };
79
+ export type ReceiptFrom = typeof RECEIPT_FROM[keyof typeof RECEIPT_FROM];
80
+ export interface GenericCodecOptions {
81
+ root: string;
82
+ /** Maps topic levels back to machine ids for machines whose `address` differs from their id. */
83
+ line?: Pick<LineDef, 'machines'>;
84
+ /** For machines that report no arrival: treat the receiver's carrier bind or job start as the receipt. */
85
+ receiptFrom?: ReceiptFrom;
86
+ unitsPerDispatch?: number | null;
87
+ dispatchOn?: DispatchOn;
88
+ }
89
+ export declare const GENERIC_CHANNELS: CodecChannels;
90
+ /** The topic of one machine channel under the codec's layout. */
91
+ export declare function topicOf(codec: Pick<LineCodec, 'root' | 'topicFor'>, scope: TopicScope, channel: string): string;
92
+ export declare function genericCodec(opts: GenericCodecOptions): LineCodec;
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ /**
3
+ * The wire, behind one seam. A codec turns a machine's MQTT messages into
4
+ * the line protocol's events (topics.ts, contract.ts) and the gateway's
5
+ * commands back into MQTT messages. The gateway, the rows and the workflow
6
+ * only ever see the line protocol. The generic codec speaks it natively; a
7
+ * host whose machines speak another protocol supplies a codec for it.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.GENERIC_CHANNELS = exports.RECEIPT_FROM = exports.DISPATCH_ON = void 0;
11
+ exports.topicOf = topicOf;
12
+ exports.genericCodec = genericCodec;
13
+ const constants_1 = require("./constants");
14
+ const contract_1 = require("./contract");
15
+ const envelope_1 = require("./envelope");
16
+ const topics_1 = require("./topics");
17
+ exports.DISPATCH_ON = { CREATED: 'created', ARRIVAL: 'arrival' };
18
+ /** Which job event counts as the receiver's receipt. */
19
+ exports.RECEIPT_FROM = { ARRIVED: 'arrived', BOUND: 'bound', STARTED: 'started' };
20
+ exports.GENERIC_CHANNELS = {
21
+ events: [constants_1.CHANNEL.PRESENCE, constants_1.CHANNEL.SNAPSHOT, constants_1.CHANNEL.JOB, `${constants_1.CHANNEL.ALARM}/+`, constants_1.CHANNEL.REPLY, `${constants_1.CHANNEL.REPORT}/#`],
22
+ telemetry: [constants_1.CHANNEL.TELEMETRY],
23
+ commands: [constants_1.CHANNEL.CMD],
24
+ };
25
+ /** The topic of one machine channel under the codec's layout. */
26
+ function topicOf(codec, scope, channel) {
27
+ if (codec.topicFor)
28
+ return codec.topicFor(scope, channel);
29
+ const level = (v) => v ?? '+';
30
+ return `${codec.root}/${level(scope.siteId)}/${level(scope.lineId)}/${level(scope.address)}/${channel}`;
31
+ }
32
+ /** Topic level to machine id, for the machines whose address is not their id. */
33
+ function addressMap(line) {
34
+ return new Map((line?.machines ?? []).filter((m) => m.address && m.address !== m.machineId).map((m) => [m.address, m.machineId]));
35
+ }
36
+ /** The inspection with the topic's address read back as the machine id; an unknown address keeps the station its message claims. */
37
+ function atMachine(seen, ids) {
38
+ const machineId = seen.parsed && ids.get(seen.parsed.ident.machineId);
39
+ if (!machineId) {
40
+ const station = seen.body?.source?.station;
41
+ return typeof station === 'string' && station ? { ...seen, claim: station } : seen;
42
+ }
43
+ const ident = { ...seen.parsed.ident, machineId, address: seen.parsed.ident.machineId };
44
+ return { ...seen, parsed: { ...seen.parsed, ident } };
45
+ }
46
+ function withEvent(seen, event) {
47
+ const body = seen.body;
48
+ return { ...seen, body: { ...body, body: { ...body.body, event } } };
49
+ }
50
+ /** Another event as the receipt: the arrival first, then that event; reported arrivals are dropped. */
51
+ function inferReceipt(seen, from) {
52
+ if (seen.parsed?.kind !== topics_1.MESSAGE_KIND.JOB || !seen.body)
53
+ return [seen];
54
+ const event = seen.body.body?.event;
55
+ if (event === constants_1.JOB_EVENT.ARRIVED)
56
+ return [];
57
+ if (event === from)
58
+ return [withEvent(seen, constants_1.JOB_EVENT.ARRIVED), seen];
59
+ return [seen];
60
+ }
61
+ function genericCodec(opts) {
62
+ const receiptFrom = opts.receiptFrom ?? exports.RECEIPT_FROM.ARRIVED;
63
+ const ids = addressMap(opts.line);
64
+ return {
65
+ root: opts.root,
66
+ channels: exports.GENERIC_CHANNELS,
67
+ unitsPerDispatch: opts.unitsPerDispatch ?? null,
68
+ dispatchOn: opts.dispatchOn ?? exports.DISPATCH_ON.CREATED,
69
+ decode(topic, payload, retained) {
70
+ const seen = atMachine((0, contract_1.inspectMessage)(opts.root, topic, payload, retained), ids);
71
+ if (!seen.parsed)
72
+ return [];
73
+ return receiptFrom === exports.RECEIPT_FROM.ARRIVED ? [seen] : inferReceipt(seen, receiptFrom);
74
+ },
75
+ encode(ident, command) {
76
+ return [{ topic: (0, topics_1.machineTopic)(opts.root, ident, constants_1.CHANNEL.CMD), payload: JSON.stringify((0, envelope_1.buildEnvelope)(ident, command)) }];
77
+ },
78
+ };
79
+ }
@@ -0,0 +1,120 @@
1
+ /**
2
+ * The line protocol: how a machine on an automated line talks to the line
3
+ * controller over MQTT. Each machine publishes under its own subtree and
4
+ * hears only its own commands. Every message is a small JSON envelope.
5
+ *
6
+ * {root}/{site}/{line}/{machine}/presence retained; birth and will, with the software version
7
+ * {root}/{site}/{line}/{machine}/snapshot retained; full machine state and its boot id
8
+ * {root}/{site}/{line}/{machine}/job job lifecycle and unit handoff
9
+ * {root}/{site}/{line}/{machine}/alarm/{id} retained while active
10
+ * {root}/{site}/{line}/{machine}/telemetry best effort readings
11
+ * {root}/{site}/{line}/{machine}/cmd controller to machine
12
+ * {root}/{site}/{line}/{machine}/cmd/reply machine to controller
13
+ */
14
+ export declare const PROTOCOL_VERSION = "1";
15
+ export declare const QOS: {
16
+ readonly BEST_EFFORT: 0;
17
+ readonly RELIABLE: 1;
18
+ };
19
+ /** A command with no reply inside this window counts as refused. */
20
+ export declare const REPLY_DEADLINE_MS = 5000;
21
+ export declare const MAX_PAYLOAD_BYTES: number;
22
+ export declare const PRESENCE_HEARTBEAT_SEC = 30;
23
+ export declare const SNAPSHOT_HEARTBEAT_SEC = 60;
24
+ export declare const CHANNEL: {
25
+ readonly PRESENCE: "presence";
26
+ readonly SNAPSHOT: "snapshot";
27
+ readonly JOB: "job";
28
+ readonly ALARM: "alarm";
29
+ readonly TELEMETRY: "telemetry";
30
+ readonly CMD: "cmd";
31
+ readonly REPLY: "cmd/reply";
32
+ /** report/<name>: production, availability, inventory and other reporting, kept in the event stream. */
33
+ readonly REPORT: "report";
34
+ };
35
+ export declare const MODE: {
36
+ readonly IDLE: "idle";
37
+ readonly RUNNING: "running";
38
+ readonly CHANGEOVER: "changeover";
39
+ readonly FAULTED: "faulted";
40
+ readonly EMERGENCY_STOP: "emergency-stop";
41
+ readonly MAINTENANCE: "maintenance";
42
+ };
43
+ export type Mode = typeof MODE[keyof typeof MODE];
44
+ export declare const PRODUCTIVE_MODES: readonly Mode[];
45
+ export declare const LEVEL: {
46
+ readonly INFO: "info";
47
+ readonly WARNING: "warning";
48
+ readonly CRITICAL: "critical";
49
+ readonly EMERGENCY: "emergency";
50
+ };
51
+ export type Level = typeof LEVEL[keyof typeof LEVEL];
52
+ /** Levels that stop work and need a person. */
53
+ export declare const STOPPING_LEVELS: readonly Level[];
54
+ /**
55
+ * Job events, one per unit: a job carries one or more units through a
56
+ * machine together. A unit moves between machines in a handshake: the
57
+ * receiver says READY, the sender says RELEASED, the receiver confirms
58
+ * ARRIVED. ARRIVED is the receipt that ends a handoff. WITHDRAWN means the
59
+ * job left the queue, by a cancel or by the machine giving it up.
60
+ */
61
+ export declare const JOB_EVENT: {
62
+ readonly QUEUED: "queued";
63
+ readonly READY: "ready";
64
+ readonly ARRIVED: "arrived";
65
+ /** The receiver put the unit on its carrier; some machines report this instead of an arrival. */
66
+ readonly BOUND: "bound";
67
+ readonly STARTED: "started";
68
+ readonly PROGRESS: "progress";
69
+ readonly FINISHED: "finished";
70
+ readonly RELEASED: "released";
71
+ readonly TRANSFER_FAILED: "transfer-failed";
72
+ readonly WITHDRAWN: "withdrawn";
73
+ };
74
+ export type JobEvent = typeof JOB_EVENT[keyof typeof JOB_EVENT];
75
+ export declare const JOB_RESULT: {
76
+ readonly OK: "ok";
77
+ readonly REJECT: "reject";
78
+ readonly HOLD: "hold";
79
+ readonly SCRAP: "scrap";
80
+ readonly REWORK: "rework";
81
+ };
82
+ export type JobResult = typeof JOB_RESULT[keyof typeof JOB_RESULT];
83
+ export declare const TRANSFER_FAILURE: {
84
+ readonly JAM: "jam";
85
+ readonly INCOMPLETE_HANDSHAKE: "incomplete-handshake";
86
+ readonly CARRIER_MISMATCH: "carrier-mismatch";
87
+ readonly UNREADABLE_ID: "unreadable-id";
88
+ };
89
+ export type TransferFailure = typeof TRANSFER_FAILURE[keyof typeof TRANSFER_FAILURE];
90
+ /** Commands. Every command carries a cmdId; a repeated cmdId is answered, never run twice. */
91
+ export declare const OP: {
92
+ readonly DISPATCH: "dispatch";
93
+ readonly CANCEL: "cancel";
94
+ /** Take work: begin or continue the queue. */
95
+ readonly START: "start";
96
+ /** Finish the current unit, then take no more; the queue is kept. */
97
+ readonly STOP: "stop";
98
+ /** Halt at the next safe point, holding the current unit; nothing new starts. */
99
+ readonly PAUSE: "pause";
100
+ /** Continue after a pause. */
101
+ readonly RESUME: "resume";
102
+ /** Load a recipe version the machine holds (`args.recipe`); refused when it holds none. */
103
+ readonly CHANGEOVER: "changeover";
104
+ readonly ACKNOWLEDGE_ALARM: "ack-alarm";
105
+ readonly SNAPSHOT: "snapshot";
106
+ };
107
+ export type Op = typeof OP[keyof typeof OP];
108
+ /** Commands a host may ask of a machine through a machine-command row. */
109
+ export declare const CONTROL_OPS: readonly Op[];
110
+ /**
111
+ * A command's reply. `accepted` means recorded; a machine that prepares the work first (fetch, verify)
112
+ * may later send `completed` (ready) or `failed` under the same cmdId.
113
+ */
114
+ export declare const REPLY_STATUS: {
115
+ readonly ACCEPTED: "accepted";
116
+ readonly COMPLETED: "completed";
117
+ readonly REFUSED: "refused";
118
+ readonly FAILED: "failed";
119
+ };
120
+ export type ReplyStatus = typeof REPLY_STATUS[keyof typeof REPLY_STATUS];
@@ -0,0 +1,97 @@
1
+ "use strict";
2
+ /**
3
+ * The line protocol: how a machine on an automated line talks to the line
4
+ * controller over MQTT. Each machine publishes under its own subtree and
5
+ * hears only its own commands. Every message is a small JSON envelope.
6
+ *
7
+ * {root}/{site}/{line}/{machine}/presence retained; birth and will, with the software version
8
+ * {root}/{site}/{line}/{machine}/snapshot retained; full machine state and its boot id
9
+ * {root}/{site}/{line}/{machine}/job job lifecycle and unit handoff
10
+ * {root}/{site}/{line}/{machine}/alarm/{id} retained while active
11
+ * {root}/{site}/{line}/{machine}/telemetry best effort readings
12
+ * {root}/{site}/{line}/{machine}/cmd controller to machine
13
+ * {root}/{site}/{line}/{machine}/cmd/reply machine to controller
14
+ */
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.REPLY_STATUS = exports.CONTROL_OPS = exports.OP = exports.TRANSFER_FAILURE = exports.JOB_RESULT = exports.JOB_EVENT = exports.STOPPING_LEVELS = exports.LEVEL = exports.PRODUCTIVE_MODES = exports.MODE = exports.CHANNEL = exports.SNAPSHOT_HEARTBEAT_SEC = exports.PRESENCE_HEARTBEAT_SEC = exports.MAX_PAYLOAD_BYTES = exports.REPLY_DEADLINE_MS = exports.QOS = exports.PROTOCOL_VERSION = void 0;
17
+ exports.PROTOCOL_VERSION = '1';
18
+ exports.QOS = { BEST_EFFORT: 0, RELIABLE: 1 };
19
+ /** A command with no reply inside this window counts as refused. */
20
+ exports.REPLY_DEADLINE_MS = 5_000;
21
+ exports.MAX_PAYLOAD_BYTES = 256 * 1024;
22
+ exports.PRESENCE_HEARTBEAT_SEC = 30;
23
+ exports.SNAPSHOT_HEARTBEAT_SEC = 60;
24
+ exports.CHANNEL = {
25
+ PRESENCE: 'presence',
26
+ SNAPSHOT: 'snapshot',
27
+ JOB: 'job',
28
+ ALARM: 'alarm',
29
+ TELEMETRY: 'telemetry',
30
+ CMD: 'cmd',
31
+ REPLY: 'cmd/reply',
32
+ /** report/<name>: production, availability, inventory and other reporting, kept in the event stream. */
33
+ REPORT: 'report',
34
+ };
35
+ exports.MODE = {
36
+ IDLE: 'idle',
37
+ RUNNING: 'running',
38
+ CHANGEOVER: 'changeover',
39
+ FAULTED: 'faulted',
40
+ EMERGENCY_STOP: 'emergency-stop',
41
+ MAINTENANCE: 'maintenance',
42
+ };
43
+ exports.PRODUCTIVE_MODES = [exports.MODE.IDLE, exports.MODE.RUNNING];
44
+ exports.LEVEL = { INFO: 'info', WARNING: 'warning', CRITICAL: 'critical', EMERGENCY: 'emergency' };
45
+ /** Levels that stop work and need a person. */
46
+ exports.STOPPING_LEVELS = [exports.LEVEL.CRITICAL, exports.LEVEL.EMERGENCY];
47
+ /**
48
+ * Job events, one per unit: a job carries one or more units through a
49
+ * machine together. A unit moves between machines in a handshake: the
50
+ * receiver says READY, the sender says RELEASED, the receiver confirms
51
+ * ARRIVED. ARRIVED is the receipt that ends a handoff. WITHDRAWN means the
52
+ * job left the queue, by a cancel or by the machine giving it up.
53
+ */
54
+ exports.JOB_EVENT = {
55
+ QUEUED: 'queued',
56
+ READY: 'ready',
57
+ ARRIVED: 'arrived',
58
+ /** The receiver put the unit on its carrier; some machines report this instead of an arrival. */
59
+ BOUND: 'bound',
60
+ STARTED: 'started',
61
+ PROGRESS: 'progress',
62
+ FINISHED: 'finished',
63
+ RELEASED: 'released',
64
+ TRANSFER_FAILED: 'transfer-failed',
65
+ WITHDRAWN: 'withdrawn',
66
+ };
67
+ exports.JOB_RESULT = { OK: 'ok', REJECT: 'reject', HOLD: 'hold', SCRAP: 'scrap', REWORK: 'rework' };
68
+ exports.TRANSFER_FAILURE = {
69
+ JAM: 'jam',
70
+ INCOMPLETE_HANDSHAKE: 'incomplete-handshake',
71
+ CARRIER_MISMATCH: 'carrier-mismatch',
72
+ UNREADABLE_ID: 'unreadable-id',
73
+ };
74
+ /** Commands. Every command carries a cmdId; a repeated cmdId is answered, never run twice. */
75
+ exports.OP = {
76
+ DISPATCH: 'dispatch',
77
+ CANCEL: 'cancel',
78
+ /** Take work: begin or continue the queue. */
79
+ START: 'start',
80
+ /** Finish the current unit, then take no more; the queue is kept. */
81
+ STOP: 'stop',
82
+ /** Halt at the next safe point, holding the current unit; nothing new starts. */
83
+ PAUSE: 'pause',
84
+ /** Continue after a pause. */
85
+ RESUME: 'resume',
86
+ /** Load a recipe version the machine holds (`args.recipe`); refused when it holds none. */
87
+ CHANGEOVER: 'changeover',
88
+ ACKNOWLEDGE_ALARM: 'ack-alarm',
89
+ SNAPSHOT: 'snapshot',
90
+ };
91
+ /** Commands a host may ask of a machine through a machine-command row. */
92
+ exports.CONTROL_OPS = [exports.OP.START, exports.OP.STOP, exports.OP.PAUSE, exports.OP.RESUME, exports.OP.CHANGEOVER, exports.OP.CANCEL];
93
+ /**
94
+ * A command's reply. `accepted` means recorded; a machine that prepares the work first (fetch, verify)
95
+ * may later send `completed` (ready) or `failed` under the same cmdId.
96
+ */
97
+ exports.REPLY_STATUS = { ACCEPTED: 'accepted', COMPLETED: 'completed', REFUSED: 'refused', FAILED: 'failed' };
@@ -0,0 +1,19 @@
1
+ /**
2
+ * The line protocol as JSON Schema plus the rules a schema cannot express.
3
+ * Known fields are checked; unknown fields are allowed, so a machine on a
4
+ * newer minor version can add fields without breaking older gateways. The
5
+ * gateway checks every inbound message; the tests check everything the mock sends.
6
+ */
7
+ import { type Envelope } from './envelope';
8
+ import { type ParsedTopic } from './topics';
9
+ export interface Inspection {
10
+ parsed: ParsedTopic | null;
11
+ body: Envelope | null;
12
+ violations: string[];
13
+ /** Set by a codec whose machines await a receipt: the id to confirm once the gateway is done with the message. */
14
+ receiptId?: string;
15
+ /** The station the message says it comes from, when the wire names one apart from the address (a line that learns addresses). */
16
+ claim?: string;
17
+ }
18
+ /** Validate one message. `retained` is the publisher's flag when the transport reports it. */
19
+ export declare function inspectMessage(root: string, topic: string, raw: string | Buffer, retained?: boolean): Inspection;