@uns-kit/core 3.0.32 → 3.0.33

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 (35) hide show
  1. package/MIGRATIONS.md +16 -0
  2. package/dist/config/app-config.d.ts +1 -0
  3. package/dist/config/app-config.d.ts.map +1 -1
  4. package/dist/config/app-config.js.map +1 -1
  5. package/dist/runtime/mqtt-drain.d.ts +29 -0
  6. package/dist/runtime/mqtt-drain.d.ts.map +1 -0
  7. package/dist/runtime/mqtt-drain.js +70 -0
  8. package/dist/runtime/mqtt-drain.js.map +1 -0
  9. package/dist/runtime/mqtt-output-state-contract.d.ts.map +1 -1
  10. package/dist/runtime/mqtt-output-state-contract.js +2 -0
  11. package/dist/runtime/mqtt-output-state-contract.js.map +1 -1
  12. package/dist/uns/uns-proxy-process.d.ts +8 -4
  13. package/dist/uns/uns-proxy-process.d.ts.map +1 -1
  14. package/dist/uns/uns-proxy-process.js +41 -5
  15. package/dist/uns/uns-proxy-process.js.map +1 -1
  16. package/dist/uns-config/config-schema.d.ts +1 -0
  17. package/dist/uns-config/config-schema.d.ts.map +1 -1
  18. package/dist/uns-config/uns-core-schema.d.ts +2 -0
  19. package/dist/uns-config/uns-core-schema.d.ts.map +1 -1
  20. package/dist/uns-mqtt/disabled-publish-queue.d.ts +6 -0
  21. package/dist/uns-mqtt/disabled-publish-queue.d.ts.map +1 -0
  22. package/dist/uns-mqtt/disabled-publish-queue.js +64 -0
  23. package/dist/uns-mqtt/disabled-publish-queue.js.map +1 -0
  24. package/dist/uns-mqtt/publish-queue-contract.d.ts +2 -0
  25. package/dist/uns-mqtt/publish-queue-contract.d.ts.map +1 -1
  26. package/dist/uns-mqtt/publish-queue-contract.js +4 -0
  27. package/dist/uns-mqtt/publish-queue-contract.js.map +1 -1
  28. package/dist/uns-mqtt/publish-queue.d.ts.map +1 -1
  29. package/dist/uns-mqtt/publish-queue.js +2 -0
  30. package/dist/uns-mqtt/publish-queue.js.map +1 -1
  31. package/dist/uns-mqtt/uns-mqtt-proxy.d.ts +3 -0
  32. package/dist/uns-mqtt/uns-mqtt-proxy.d.ts.map +1 -1
  33. package/dist/uns-mqtt/uns-mqtt-proxy.js +17 -1
  34. package/dist/uns-mqtt/uns-mqtt-proxy.js.map +1 -1
  35. package/package.json +1 -1
@@ -0,0 +1,64 @@
1
+ import { createHash } from "node:crypto";
2
+ import { createRequire } from "node:module";
3
+ import { lstatSync, readdirSync } from "node:fs";
4
+ import path from "node:path";
5
+ /** Disabling persistence is not permission to hide retained output or a live owner. */
6
+ export function assertDisabledPublishQueueEmpty(config, identity) {
7
+ if (config.enabled !== false)
8
+ return;
9
+ assertRetainedPublishQueueEmpty(config, identity);
10
+ }
11
+ /** Host preflight may prove emptiness; the launching SDK still verifies its exact identity. */
12
+ export function assertRetainedPublishQueueEmpty(config, identity) {
13
+ if (!path.isAbsolute(config.directory) || path.parse(config.directory).root === config.directory)
14
+ throw Error("Disabled MQTT queue still requires a dedicated absolute directory");
15
+ for (let p = config.directory; p !== path.dirname(p); p = path.dirname(p)) {
16
+ try {
17
+ if (lstatSync(p).isSymbolicLink())
18
+ throw Error("Unsafe disabled MQTT queue path");
19
+ }
20
+ catch (e) {
21
+ if (e.code !== "ENOENT")
22
+ throw e;
23
+ }
24
+ }
25
+ let entries;
26
+ try {
27
+ const s = lstatSync(config.directory);
28
+ if (!s.isDirectory() || s.mode & 0o077 || (process.getuid && s.uid !== process.getuid()))
29
+ throw Error("Retained disabled MQTT queue directory is unsafe");
30
+ entries = readdirSync(config.directory);
31
+ }
32
+ catch (e) {
33
+ if (e.code === "ENOENT")
34
+ return;
35
+ throw e;
36
+ }
37
+ if (!entries.length)
38
+ return;
39
+ const allowed = new Set(["mqtt-outbox.sqlite", "mqtt-outbox.sqlite-wal", "mqtt-outbox.sqlite-shm", "mqtt-outbox-owner.sqlite", "mqtt-outbox-owner.sqlite-journal"]);
40
+ if (entries.some(name => !allowed.has(name)) || !entries.includes("mqtt-outbox.sqlite") || !entries.includes("mqtt-outbox-owner.sqlite"))
41
+ throw Error("Unknown retained MQTT state; disabling is blocked");
42
+ for (const name of entries) {
43
+ const s = lstatSync(path.join(config.directory, name));
44
+ if (!s.isFile() || s.isSymbolicLink() || s.nlink !== 1 || s.mode & 0o077 || (process.getuid && s.uid !== process.getuid()))
45
+ throw Error("Unsafe retained MQTT output file");
46
+ }
47
+ const { DatabaseSync: Sqlite } = createRequire(import.meta.url)("node:sqlite");
48
+ let lease, db;
49
+ try {
50
+ lease = new Sqlite(path.join(config.directory, "mqtt-outbox-owner.sqlite"), { readOnly: true });
51
+ lease.exec("PRAGMA busy_timeout=0; BEGIN EXCLUSIVE");
52
+ db = new Sqlite(path.join(config.directory, "mqtt-outbox.sqlite"), { readOnly: true });
53
+ const metadata = db.prepare("SELECT value FROM metadata WHERE key=?");
54
+ if (!["1", "2"].includes(String(metadata.get("schema")?.value)) || (identity !== undefined ? metadata.get("identity")?.value !== createHash("sha256").update(identity).digest("hex") : !/^[a-f0-9]{64}$/.test(String(metadata.get("identity")?.value))))
55
+ throw Error("Retained MQTT output identity differs; disabling is blocked");
56
+ if (Number(db.prepare("SELECT COUNT(*) AS n FROM pending").get()?.n) !== 0)
57
+ throw Error("Retained MQTT output is not empty; drain it with its original owner before disabling");
58
+ }
59
+ finally {
60
+ db?.close();
61
+ lease?.close();
62
+ }
63
+ }
64
+ //# sourceMappingURL=disabled-publish-queue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"disabled-publish-queue.js","sourceRoot":"","sources":["../../src/uns-mqtt/disabled-publish-queue.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from \"node:crypto\";\nimport { createRequire } from \"node:module\";\nimport { lstatSync, readdirSync } from \"node:fs\";\nimport path from \"node:path\";\nimport type { DatabaseSync } from \"node:sqlite\";\nimport type { MqttPublishQueueOptions } from \"./publish-queue-contract.js\";\n\n/** Disabling persistence is not permission to hide retained output or a live owner. */\nexport function assertDisabledPublishQueueEmpty(config: MqttPublishQueueOptions, identity: string): void {\n if (config.enabled !== false) return;\n assertRetainedPublishQueueEmpty(config, identity);\n}\n\n/** Host preflight may prove emptiness; the launching SDK still verifies its exact identity. */\nexport function assertRetainedPublishQueueEmpty(config: MqttPublishQueueOptions, identity?: string): void {\n if (!path.isAbsolute(config.directory) || path.parse(config.directory).root === config.directory)\n throw Error(\"Disabled MQTT queue still requires a dedicated absolute directory\");\n for (let p = config.directory; p !== path.dirname(p); p = path.dirname(p)) {\n try { if (lstatSync(p).isSymbolicLink()) throw Error(\"Unsafe disabled MQTT queue path\"); }\n catch (e) { if ((e as NodeJS.ErrnoException).code !== \"ENOENT\") throw e; }\n }\n let entries: string[];\n try {\n const s = lstatSync(config.directory);\n if (!s.isDirectory() || s.mode & 0o077 || (process.getuid && s.uid !== process.getuid()))\n throw Error(\"Retained disabled MQTT queue directory is unsafe\");\n entries = readdirSync(config.directory);\n } catch (e) { if ((e as NodeJS.ErrnoException).code === \"ENOENT\") return; throw e; }\n if (!entries.length) return;\n const allowed = new Set([\"mqtt-outbox.sqlite\", \"mqtt-outbox.sqlite-wal\", \"mqtt-outbox.sqlite-shm\", \"mqtt-outbox-owner.sqlite\", \"mqtt-outbox-owner.sqlite-journal\"]);\n if (entries.some(name => !allowed.has(name)) || !entries.includes(\"mqtt-outbox.sqlite\") || !entries.includes(\"mqtt-outbox-owner.sqlite\"))\n throw Error(\"Unknown retained MQTT state; disabling is blocked\");\n for (const name of entries) {\n const s = lstatSync(path.join(config.directory, name));\n if (!s.isFile() || s.isSymbolicLink() || s.nlink !== 1 || s.mode & 0o077 || (process.getuid && s.uid !== process.getuid()))\n throw Error(\"Unsafe retained MQTT output file\");\n }\n const { DatabaseSync: Sqlite } = createRequire(import.meta.url)(\"node:sqlite\") as typeof import(\"node:sqlite\");\n let lease: DatabaseSync | undefined, db: DatabaseSync | undefined;\n try {\n lease = new Sqlite(path.join(config.directory, \"mqtt-outbox-owner.sqlite\"), { readOnly: true });\n lease.exec(\"PRAGMA busy_timeout=0; BEGIN EXCLUSIVE\");\n db = new Sqlite(path.join(config.directory, \"mqtt-outbox.sqlite\"), { readOnly: true });\n const metadata = db.prepare(\"SELECT value FROM metadata WHERE key=?\");\n if (![\"1\", \"2\"].includes(String(metadata.get(\"schema\")?.value)) || (identity !== undefined ? metadata.get(\"identity\")?.value !== createHash(\"sha256\").update(identity).digest(\"hex\") : !/^[a-f0-9]{64}$/.test(String(metadata.get(\"identity\")?.value))))\n throw Error(\"Retained MQTT output identity differs; disabling is blocked\");\n if (Number(db.prepare(\"SELECT COUNT(*) AS n FROM pending\").get()?.n) !== 0)\n throw Error(\"Retained MQTT output is not empty; drain it with its original owner before disabling\");\n } finally { db?.close(); lease?.close(); }\n}\n"]}
@@ -1,6 +1,7 @@
1
1
  import { z } from "zod";
2
2
  /** Opt-in output buffering; absence preserves the issued MQTT publisher. */
3
3
  export declare const mqttPublishQueueSchema: z.ZodObject<{
4
+ enabled: z.ZodDefault<z.ZodBoolean>;
4
5
  mode: z.ZodEnum<{
5
6
  buffered: "buffered";
6
7
  durable: "durable";
@@ -34,6 +35,7 @@ export type MqttPublishQueueStatus = {
34
35
  retries: number;
35
36
  lastError: string | null;
36
37
  };
38
+ export declare function isMqttPublishQueueEnabled(value: MqttPublishQueueOptions | undefined): boolean;
37
39
  /** Stable queue owner/destination. Keep byte-compatible with issued schema-2 outboxes. */
38
40
  export declare function mqttPublishQueueIdentity(packageName: string, owner: string, channel: {
39
41
  host: string;
@@ -1 +1 @@
1
- {"version":3,"file":"publish-queue-contract.d.ts","sourceRoot":"","sources":["../../src/uns-mqtt/publish-queue-contract.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,4EAA4E;AAC5E,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;kBAexB,CAAC;AACZ,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAC7E,MAAM,MAAM,+BAA+B,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,sBAAsB,CAAC,CAAC;AACtF,MAAM,MAAM,sBAAsB,GAAG;IACnC,IAAI,EAAE,UAAU,GAAG,SAAS,CAAC;IAC7B,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,GAAG,QAAQ,CAAC;IAC9D,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B,CAAC;AAEF,0FAA0F;AAC1F,wBAAgB,wBAAwB,CACtC,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE;IACP,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,GACA,MAAM,CAUR"}
1
+ {"version":3,"file":"publish-queue-contract.d.ts","sourceRoot":"","sources":["../../src/uns-mqtt/publish-queue-contract.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,4EAA4E;AAC5E,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;kBAgBxB,CAAC;AACZ,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAC7E,MAAM,MAAM,+BAA+B,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,sBAAsB,CAAC,CAAC;AACtF,MAAM,MAAM,sBAAsB,GAAG;IACnC,IAAI,EAAE,UAAU,GAAG,SAAS,CAAC;IAC7B,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,GAAG,QAAQ,CAAC;IAC9D,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B,CAAC;AAEF,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,uBAAuB,GAAG,SAAS,GAAG,OAAO,CAE7F;AAED,0FAA0F;AAC1F,wBAAgB,wBAAwB,CACtC,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE;IACP,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,GACA,MAAM,CAUR"}
@@ -2,6 +2,7 @@ import { z } from "zod";
2
2
  /** Opt-in output buffering; absence preserves the issued MQTT publisher. */
3
3
  export const mqttPublishQueueSchema = z
4
4
  .object({
5
+ enabled: z.boolean().default(true),
5
6
  mode: z.enum(["buffered", "durable"]),
6
7
  storage: z.enum(["local", "runtime"]).default("local"),
7
8
  directory: z.string().min(1),
@@ -16,6 +17,9 @@ export const mqttPublishQueueSchema = z
16
17
  publishTimeoutMs: z.number().int().min(10).max(60000).default(10000),
17
18
  })
18
19
  .strict();
20
+ export function isMqttPublishQueueEnabled(value) {
21
+ return value !== undefined && value.enabled !== false;
22
+ }
19
23
  /** Stable queue owner/destination. Keep byte-compatible with issued schema-2 outboxes. */
20
24
  export function mqttPublishQueueIdentity(packageName, owner, channel) {
21
25
  return JSON.stringify({
@@ -1 +1 @@
1
- {"version":3,"file":"publish-queue-contract.js","sourceRoot":"","sources":["../../src/uns-mqtt/publish-queue-contract.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,4EAA4E;AAC5E,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC;KACpC,MAAM,CAAC;IACN,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;IACrC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC;IACtD,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC5B,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC;IAC9D,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC;IAClE,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC;IAC9D,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC;IACnE,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC;IACrE,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC;IAC9E,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC;IACvE,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC;IAClE,gBAAgB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC;CACrE,CAAC;KACD,MAAM,EAAE,CAAC;AAgBZ,0FAA0F;AAC1F,MAAM,UAAU,wBAAwB,CACtC,WAAmB,EACnB,KAAa,EACb,OAOC;IAED,OAAO,IAAI,CAAC,SAAS,CAAC;QACpB,OAAO,EAAE,WAAW;QACpB,KAAK;QACL,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;QACrD,QAAQ,EAAE,OAAO,CAAC,QAAQ,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC;QAClE,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,IAAI;QAC5B,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,IAAI;KACjC,CAAC,CAAC;AACL,CAAC","sourcesContent":["import { z } from \"zod\";\n\n/** Opt-in output buffering; absence preserves the issued MQTT publisher. */\nexport const mqttPublishQueueSchema = z\n .object({\n mode: z.enum([\"buffered\", \"durable\"]),\n storage: z.enum([\"local\", \"runtime\"]).default(\"local\"),\n directory: z.string().min(1),\n maxRecords: z.number().int().min(1).max(100000).default(10000),\n maxBytes: z.number().int().min(1).max(536870912).default(67108864),\n memoryRecords: z.number().int().min(0).max(10000).default(256),\n memoryBytes: z.number().int().min(0).max(67108864).default(4194304),\n maxMessageBytes: z.number().int().min(1).max(1048576).default(524288),\n maxDiskBytes: z.number().int().min(1048576).max(1073741824).default(268435456),\n minFreeBytes: z.number().int().min(0).max(1073741824).default(16777216),\n retryIntervalMs: z.number().int().min(10).max(60000).default(1000),\n publishTimeoutMs: z.number().int().min(10).max(60000).default(10000),\n })\n .strict();\nexport type MqttPublishQueueOptions = z.input<typeof mqttPublishQueueSchema>;\nexport type ResolvedMqttPublishQueueOptions = z.output<typeof mqttPublishQueueSchema>;\nexport type MqttPublishQueueStatus = {\n mode: \"buffered\" | \"durable\";\n state: \"idle\" | \"pending\" | \"retrying\" | \"blocked\" | \"closed\";\n pendingRecords: number;\n pendingBytes: number;\n memoryRecords: number;\n diskRecords: number;\n inFlight: boolean;\n delivered: number;\n retries: number;\n lastError: string | null;\n};\n\n/** Stable queue owner/destination. Keep byte-compatible with issued schema-2 outboxes. */\nexport function mqttPublishQueueIdentity(\n packageName: string,\n owner: string,\n channel: {\n host: string;\n port?: number | undefined;\n protocol?: string | undefined;\n mqttSSL?: boolean | undefined;\n hosts?: unknown;\n servers?: unknown;\n },\n): string {\n return JSON.stringify({\n package: packageName,\n owner,\n host: channel.host,\n port: channel.port ?? (channel.mqttSSL ? 8883 : 1883),\n protocol: channel.protocol ?? (channel.mqttSSL ? \"mqtts\" : \"mqtt\"),\n hosts: channel.hosts ?? null,\n servers: channel.servers ?? null,\n });\n}\n"]}
1
+ {"version":3,"file":"publish-queue-contract.js","sourceRoot":"","sources":["../../src/uns-mqtt/publish-queue-contract.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from \"zod\";\n\n/** Opt-in output buffering; absence preserves the issued MQTT publisher. */\nexport const mqttPublishQueueSchema = z\n .object({\n enabled: z.boolean().default(true),\n mode: z.enum([\"buffered\", \"durable\"]),\n storage: z.enum([\"local\", \"runtime\"]).default(\"local\"),\n directory: z.string().min(1),\n maxRecords: z.number().int().min(1).max(100000).default(10000),\n maxBytes: z.number().int().min(1).max(536870912).default(67108864),\n memoryRecords: z.number().int().min(0).max(10000).default(256),\n memoryBytes: z.number().int().min(0).max(67108864).default(4194304),\n maxMessageBytes: z.number().int().min(1).max(1048576).default(524288),\n maxDiskBytes: z.number().int().min(1048576).max(1073741824).default(268435456),\n minFreeBytes: z.number().int().min(0).max(1073741824).default(16777216),\n retryIntervalMs: z.number().int().min(10).max(60000).default(1000),\n publishTimeoutMs: z.number().int().min(10).max(60000).default(10000),\n })\n .strict();\nexport type MqttPublishQueueOptions = z.input<typeof mqttPublishQueueSchema>;\nexport type ResolvedMqttPublishQueueOptions = z.output<typeof mqttPublishQueueSchema>;\nexport type MqttPublishQueueStatus = {\n mode: \"buffered\" | \"durable\";\n state: \"idle\" | \"pending\" | \"retrying\" | \"blocked\" | \"closed\";\n pendingRecords: number;\n pendingBytes: number;\n memoryRecords: number;\n diskRecords: number;\n inFlight: boolean;\n delivered: number;\n retries: number;\n lastError: string | null;\n};\n\nexport function isMqttPublishQueueEnabled(value: MqttPublishQueueOptions | undefined): boolean {\n return value !== undefined && value.enabled !== false;\n}\n\n/** Stable queue owner/destination. Keep byte-compatible with issued schema-2 outboxes. */\nexport function mqttPublishQueueIdentity(\n packageName: string,\n owner: string,\n channel: {\n host: string;\n port?: number | undefined;\n protocol?: string | undefined;\n mqttSSL?: boolean | undefined;\n hosts?: unknown;\n servers?: unknown;\n },\n): string {\n return JSON.stringify({\n package: packageName,\n owner,\n host: channel.host,\n port: channel.port ?? (channel.mqttSSL ? 8883 : 1883),\n protocol: channel.protocol ?? (channel.mqttSSL ? \"mqtts\" : \"mqtt\"),\n hosts: channel.hosts ?? null,\n servers: channel.servers ?? null,\n });\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"publish-queue.d.ts","sourceRoot":"","sources":["../../src/uns-mqtt/publish-queue.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,MAAM,CAAC;AAElD,OAAO,EAAE,KAAK,uBAAuB,EAAE,KAAK,sBAAsB,EAA0B,MAAM,6BAA6B,CAAC;AAChI,OAAO,EAAyB,KAAK,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAErF,iGAAiG;AACjG,qBAAa,gBAAgB;IAmBzB,OAAO,CAAC,IAAI;IAEZ,OAAO,CAAC,OAAO;IApBjB,OAAO,CAAC,MAAM,CAAC;IACf,OAAO,CAAC,KAAK,CAAwB;IACrC,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,WAAW,CAA8E;IACjG,OAAO,CAAC,OAAO,CAA4G;IAC3H,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,MAAM,CAAU;IACxB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,UAAU,CAA4B;IAC9C,OAAO,CAAC,UAAU,CAA4C;IAC9D,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,OAAO,CAAoB;gBAGjC,OAAO,EAAE,uBAAuB,EAChC,QAAQ,EAAE,MAAM,EACR,IAAI,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,OAAO,CAAC,IAAI,CAAC,EACtD,MAAM,UAAO,EACL,OAAO,GAAE,CAAC,MAAM,EAAE,sBAAsB,KAAK,IAAe;IActE,sFAAsF;IACtF,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC;IAmC1G,QAAQ,IAAI,sBAAsB;IAuBlC,QAAQ,IAAI,OAAO;IAGnB,YAAY,CAAC,QAAQ,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAKvE,aAAa,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAE,CAAC;IAUvF,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAgB7C,kFAAkF;IAC5E,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAyB5B,OAAO,CAAC,QAAQ;YAcF,GAAG;IAiCjB,OAAO,CAAC,IAAI;IAUZ,OAAO,CAAC,IAAI;CAUb"}
1
+ {"version":3,"file":"publish-queue.d.ts","sourceRoot":"","sources":["../../src/uns-mqtt/publish-queue.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,MAAM,CAAC;AAElD,OAAO,EAAE,KAAK,uBAAuB,EAAE,KAAK,sBAAsB,EAA0B,MAAM,6BAA6B,CAAC;AAChI,OAAO,EAAyB,KAAK,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAErF,iGAAiG;AACjG,qBAAa,gBAAgB;IAmBzB,OAAO,CAAC,IAAI;IAEZ,OAAO,CAAC,OAAO;IApBjB,OAAO,CAAC,MAAM,CAAC;IACf,OAAO,CAAC,KAAK,CAAwB;IACrC,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,WAAW,CAA8E;IACjG,OAAO,CAAC,OAAO,CAA4G;IAC3H,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,MAAM,CAAU;IACxB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,UAAU,CAA4B;IAC9C,OAAO,CAAC,UAAU,CAA4C;IAC9D,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,OAAO,CAAoB;gBAGjC,OAAO,EAAE,uBAAuB,EAChC,QAAQ,EAAE,MAAM,EACR,IAAI,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,OAAO,CAAC,IAAI,CAAC,EACtD,MAAM,UAAO,EACL,OAAO,GAAE,CAAC,MAAM,EAAE,sBAAsB,KAAK,IAAe;IAetE,sFAAsF;IACtF,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC;IAmC1G,QAAQ,IAAI,sBAAsB;IAuBlC,QAAQ,IAAI,OAAO;IAGnB,YAAY,CAAC,QAAQ,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAKvE,aAAa,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAE,CAAC;IAUvF,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAgB7C,kFAAkF;IAC5E,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAyB5B,OAAO,CAAC,QAAQ;YAcF,GAAG;IAiCjB,OAAO,CAAC,IAAI;IAUZ,OAAO,CAAC,IAAI;CAUb"}
@@ -22,6 +22,8 @@ export class MqttPublishQueue {
22
22
  this.send = send;
23
23
  this.changed = changed;
24
24
  this.config = mqttPublishQueueSchema.parse(options);
25
+ if (!this.config.enabled)
26
+ throw Error("Disabled MQTT persistence must be resolved before constructing an outbox");
25
27
  this.store = new MqttPublishQueueStore(this.config, identity);
26
28
  const saved = this.store.summary();
27
29
  if (saved.records > this.config.maxRecords || saved.bytes > this.config.maxBytes) {
@@ -1 +1 @@
1
- {"version":3,"file":"publish-queue.js","sourceRoot":"","sources":["../../src/uns-mqtt/publish-queue.ts"],"names":[],"mappings":"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","sourcesContent":["import type { IClientPublishOptions } from \"mqtt\";\n\nimport { type MqttPublishQueueOptions, type MqttPublishQueueStatus, mqttPublishQueueSchema } from \"./publish-queue-contract.js\";\nimport { MqttPublishQueueStore, type PublishRecord } from \"./publish-queue-store.js\";\n\n/** MQTT output queue only. Input acknowledgement and business transforms remain caller-owned. */\nexport class MqttPublishQueue {\n private config;\n private store: MqttPublishQueueStore;\n private memory: PublishRecord[] = [];\n private completions = new Map<string, { resolve: () => void; reject: (error: Error) => void }>();\n private waiters = new Set<{ resolve: () => void; reject: (error: Error) => void; timer: ReturnType<typeof setTimeout> }>();\n private seq: number;\n private active: boolean;\n private closed = false;\n private processing: Promise<void> | undefined;\n private retryTimer: ReturnType<typeof setTimeout> | undefined;\n private delivered = 0;\n private retries = 0;\n private lastError: string | null = null;\n private blocked: Error | undefined;\n\n constructor(\n options: MqttPublishQueueOptions,\n identity: string,\n private send: (record: PublishRecord) => Promise<void>,\n active = true,\n private changed: (status: MqttPublishQueueStatus) => void = () => {},\n ) {\n this.config = mqttPublishQueueSchema.parse(options);\n this.store = new MqttPublishQueueStore(this.config, identity);\n const saved = this.store.summary();\n if (saved.records > this.config.maxRecords || saved.bytes > this.config.maxBytes) {\n this.store.close();\n throw Error(\"Retained MQTT outbox exceeds configured limits; preserve and review it\");\n }\n this.seq = saved.lastSeq;\n this.active = active;\n this.schedule();\n }\n\n /** Returns only after RAM admission, or (durable mode) after a FULL SQLite commit. */\n enqueueOrThrow(topic: string, message: string, id: string, options?: IClientPublishOptions): Promise<void> {\n if (this.closed || this.blocked) throw this.blocked ?? Error(\"MQTT publish queue is closed\");\n if (this.completions.has(id)) throw Error(\"Duplicate MQTT queue admission id\");\n if (\n !topic ||\n topic.includes(\"\\0\") ||\n /[+#]/.test(topic) ||\n Buffer.byteLength(topic) > 65535 ||\n typeof message !== \"string\" ||\n !id ||\n id.length > 200\n )\n throw Error(\"Invalid MQTT publish envelope\");\n if (options && Object.keys(options).some((key) => ![\"qos\", \"retain\"].includes(key)))\n throw Error(\"Persistent MQTT output currently supports only qos and retain publish options\");\n const qos = options?.qos ?? 1;\n if (qos !== 1 && qos !== 2) throw Error(\"Persistent MQTT output requires QoS 1 or 2 broker acknowledgement\");\n const bytes = Buffer.byteLength(topic) + Buffer.byteLength(message) + 256;\n if (bytes > this.config.maxMessageBytes) throw Error(\"MQTT publish exceeds maxMessageBytes\");\n const total = this.snapshot();\n if (total.pendingRecords >= this.config.maxRecords || total.pendingBytes + bytes > this.config.maxBytes)\n throw Error(\"MQTT publish queue capacity reached; admission rejected\");\n const record: PublishRecord = { seq: ++this.seq, id, topic, message, options: { qos, retain: options?.retain ?? false }, bytes };\n if (\n this.config.mode === \"durable\" ||\n this.memory.length >= this.config.memoryRecords ||\n this.memory.reduce((sum, r) => sum + r.bytes, 0) + bytes > this.config.memoryBytes\n )\n this.store.put(record);\n else this.memory.push(record);\n const delivered = new Promise<void>((resolve, reject) => this.completions.set(id, { resolve, reject }));\n this.emit();\n this.schedule();\n return delivered;\n }\n snapshot(): MqttPublishQueueStatus {\n const disk = this.store.summary();\n return {\n mode: this.config.mode,\n state: this.closed\n ? \"closed\"\n : this.blocked\n ? \"blocked\"\n : this.retryTimer\n ? \"retrying\"\n : disk.records + this.memory.length\n ? \"pending\"\n : \"idle\",\n pendingRecords: disk.records + this.memory.length,\n pendingBytes: disk.bytes + this.memory.reduce((sum, r) => sum + r.bytes, 0),\n memoryRecords: this.memory.length,\n diskRecords: disk.records,\n inFlight: !!this.processing,\n delivered: this.delivered,\n retries: this.retries,\n lastError: this.lastError,\n };\n }\n getState(): boolean {\n return this.active;\n }\n becomeActive(snapshot?: { batchSize?: number; referenceHash?: string }): void {\n if (snapshot?.batchSize) throw Error(\"Persistent MQTT output cannot discard work using legacy handover hashes\");\n this.active = true;\n this.schedule();\n }\n async becomePassive(_n?: number): Promise<{ batchSize: number; referenceHash: string }> {\n this.active = false;\n if (this.retryTimer) {\n clearTimeout(this.retryTimer);\n this.retryTimer = undefined;\n }\n await this.processing;\n this.emit();\n return { batchSize: 0, referenceHash: \"\" };\n }\n waitForIdle(timeoutMs: number): Promise<void> {\n if (this.blocked) return Promise.reject(this.blocked);\n if (!this.snapshot().pendingRecords && !this.processing) return Promise.resolve();\n if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) return Promise.reject(Error(\"MQTT drain timeout must be positive and finite\"));\n return new Promise((resolve, reject) => {\n const waiter = {\n resolve,\n reject,\n timer: setTimeout(() => {\n this.waiters.delete(waiter);\n reject(Error(\"MQTT publish queue drain timed out; pending work retained\"));\n }, timeoutMs),\n };\n this.waiters.add(waiter);\n });\n }\n /** Caller must stop its MQTT transport first to release any in-flight publish. */\n async close(): Promise<void> {\n if (this.closed) return;\n this.active = false;\n if (this.retryTimer) {\n clearTimeout(this.retryTimer);\n this.retryTimer = undefined;\n }\n await this.processing;\n // Persist all buffered work before releasing the storage lease. Failure leaves it open.\n while (this.memory.length) {\n this.store.put(this.memory[0]);\n this.memory.shift();\n }\n this.closed = true;\n const stopped = Error(\"MQTT output stopped; undelivered work retained on disk\");\n for (const completion of this.completions.values()) completion.reject(stopped);\n this.completions.clear();\n for (const waiter of this.waiters) {\n clearTimeout(waiter.timer);\n waiter.reject(stopped);\n }\n this.waiters.clear();\n this.emit();\n this.store.close();\n }\n private schedule(): void {\n if (!this.active || this.closed || this.blocked || this.processing || this.retryTimer) return;\n // Defer so enqueue acceptance is visible before the delivery callback.\n this.processing = Promise.resolve()\n .then(() => this.run())\n .catch((error) => this.fail(error))\n .finally(() => {\n this.processing = undefined;\n if (!this.closed) {\n this.emit();\n if (this.snapshot().pendingRecords) this.schedule();\n }\n });\n }\n private async run(): Promise<void> {\n while (this.active && !this.closed && !this.blocked) {\n const disk = this.store.head();\n const memory = this.memory[0];\n const record = !memory || (disk && disk.seq < memory.seq) ? disk : memory;\n if (!record) return;\n try {\n await this.send(record);\n } catch {\n // A lost acknowledgement remains uncertain; persist and replay, never mark success.\n // After an observed outage, move all already accepted RAM work to disk.\n while (this.memory.length) {\n this.store.put(this.memory[0]);\n this.memory.shift();\n }\n this.retries++;\n this.lastError = \"MQTT delivery unconfirmed; retained for retry\";\n if (this.active)\n this.retryTimer = setTimeout(() => {\n this.retryTimer = undefined;\n this.schedule();\n }, this.config.retryIntervalMs);\n return;\n }\n if (record === memory) this.memory.shift();\n else this.store.complete(record);\n this.delivered++;\n this.lastError = null;\n this.completions.get(record.id)?.resolve();\n this.completions.delete(record.id);\n this.emit();\n }\n }\n private fail(reason: unknown): void {\n this.blocked = reason instanceof Error ? reason : Error(\"MQTT outbox storage failed\");\n this.lastError = \"MQTT outbox blocked; preserve storage and inspect local error\";\n for (const waiter of this.waiters) {\n clearTimeout(waiter.timer);\n waiter.reject(this.blocked);\n }\n this.waiters.clear();\n this.emit();\n }\n private emit(): void {\n this.changed(this.snapshot());\n if (!this.snapshot().pendingRecords && !this.processing) {\n for (const waiter of this.waiters) {\n clearTimeout(waiter.timer);\n waiter.resolve();\n }\n this.waiters.clear();\n }\n }\n}\n"]}
1
+ {"version":3,"file":"publish-queue.js","sourceRoot":"","sources":["../../src/uns-mqtt/publish-queue.ts"],"names":[],"mappings":"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","sourcesContent":["import type { IClientPublishOptions } from \"mqtt\";\n\nimport { type MqttPublishQueueOptions, type MqttPublishQueueStatus, mqttPublishQueueSchema } from \"./publish-queue-contract.js\";\nimport { MqttPublishQueueStore, type PublishRecord } from \"./publish-queue-store.js\";\n\n/** MQTT output queue only. Input acknowledgement and business transforms remain caller-owned. */\nexport class MqttPublishQueue {\n private config;\n private store: MqttPublishQueueStore;\n private memory: PublishRecord[] = [];\n private completions = new Map<string, { resolve: () => void; reject: (error: Error) => void }>();\n private waiters = new Set<{ resolve: () => void; reject: (error: Error) => void; timer: ReturnType<typeof setTimeout> }>();\n private seq: number;\n private active: boolean;\n private closed = false;\n private processing: Promise<void> | undefined;\n private retryTimer: ReturnType<typeof setTimeout> | undefined;\n private delivered = 0;\n private retries = 0;\n private lastError: string | null = null;\n private blocked: Error | undefined;\n\n constructor(\n options: MqttPublishQueueOptions,\n identity: string,\n private send: (record: PublishRecord) => Promise<void>,\n active = true,\n private changed: (status: MqttPublishQueueStatus) => void = () => {},\n ) {\n this.config = mqttPublishQueueSchema.parse(options);\n if (!this.config.enabled) throw Error(\"Disabled MQTT persistence must be resolved before constructing an outbox\");\n this.store = new MqttPublishQueueStore(this.config, identity);\n const saved = this.store.summary();\n if (saved.records > this.config.maxRecords || saved.bytes > this.config.maxBytes) {\n this.store.close();\n throw Error(\"Retained MQTT outbox exceeds configured limits; preserve and review it\");\n }\n this.seq = saved.lastSeq;\n this.active = active;\n this.schedule();\n }\n\n /** Returns only after RAM admission, or (durable mode) after a FULL SQLite commit. */\n enqueueOrThrow(topic: string, message: string, id: string, options?: IClientPublishOptions): Promise<void> {\n if (this.closed || this.blocked) throw this.blocked ?? Error(\"MQTT publish queue is closed\");\n if (this.completions.has(id)) throw Error(\"Duplicate MQTT queue admission id\");\n if (\n !topic ||\n topic.includes(\"\\0\") ||\n /[+#]/.test(topic) ||\n Buffer.byteLength(topic) > 65535 ||\n typeof message !== \"string\" ||\n !id ||\n id.length > 200\n )\n throw Error(\"Invalid MQTT publish envelope\");\n if (options && Object.keys(options).some((key) => ![\"qos\", \"retain\"].includes(key)))\n throw Error(\"Persistent MQTT output currently supports only qos and retain publish options\");\n const qos = options?.qos ?? 1;\n if (qos !== 1 && qos !== 2) throw Error(\"Persistent MQTT output requires QoS 1 or 2 broker acknowledgement\");\n const bytes = Buffer.byteLength(topic) + Buffer.byteLength(message) + 256;\n if (bytes > this.config.maxMessageBytes) throw Error(\"MQTT publish exceeds maxMessageBytes\");\n const total = this.snapshot();\n if (total.pendingRecords >= this.config.maxRecords || total.pendingBytes + bytes > this.config.maxBytes)\n throw Error(\"MQTT publish queue capacity reached; admission rejected\");\n const record: PublishRecord = { seq: ++this.seq, id, topic, message, options: { qos, retain: options?.retain ?? false }, bytes };\n if (\n this.config.mode === \"durable\" ||\n this.memory.length >= this.config.memoryRecords ||\n this.memory.reduce((sum, r) => sum + r.bytes, 0) + bytes > this.config.memoryBytes\n )\n this.store.put(record);\n else this.memory.push(record);\n const delivered = new Promise<void>((resolve, reject) => this.completions.set(id, { resolve, reject }));\n this.emit();\n this.schedule();\n return delivered;\n }\n snapshot(): MqttPublishQueueStatus {\n const disk = this.store.summary();\n return {\n mode: this.config.mode,\n state: this.closed\n ? \"closed\"\n : this.blocked\n ? \"blocked\"\n : this.retryTimer\n ? \"retrying\"\n : disk.records + this.memory.length\n ? \"pending\"\n : \"idle\",\n pendingRecords: disk.records + this.memory.length,\n pendingBytes: disk.bytes + this.memory.reduce((sum, r) => sum + r.bytes, 0),\n memoryRecords: this.memory.length,\n diskRecords: disk.records,\n inFlight: !!this.processing,\n delivered: this.delivered,\n retries: this.retries,\n lastError: this.lastError,\n };\n }\n getState(): boolean {\n return this.active;\n }\n becomeActive(snapshot?: { batchSize?: number; referenceHash?: string }): void {\n if (snapshot?.batchSize) throw Error(\"Persistent MQTT output cannot discard work using legacy handover hashes\");\n this.active = true;\n this.schedule();\n }\n async becomePassive(_n?: number): Promise<{ batchSize: number; referenceHash: string }> {\n this.active = false;\n if (this.retryTimer) {\n clearTimeout(this.retryTimer);\n this.retryTimer = undefined;\n }\n await this.processing;\n this.emit();\n return { batchSize: 0, referenceHash: \"\" };\n }\n waitForIdle(timeoutMs: number): Promise<void> {\n if (this.blocked) return Promise.reject(this.blocked);\n if (!this.snapshot().pendingRecords && !this.processing) return Promise.resolve();\n if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) return Promise.reject(Error(\"MQTT drain timeout must be positive and finite\"));\n return new Promise((resolve, reject) => {\n const waiter = {\n resolve,\n reject,\n timer: setTimeout(() => {\n this.waiters.delete(waiter);\n reject(Error(\"MQTT publish queue drain timed out; pending work retained\"));\n }, timeoutMs),\n };\n this.waiters.add(waiter);\n });\n }\n /** Caller must stop its MQTT transport first to release any in-flight publish. */\n async close(): Promise<void> {\n if (this.closed) return;\n this.active = false;\n if (this.retryTimer) {\n clearTimeout(this.retryTimer);\n this.retryTimer = undefined;\n }\n await this.processing;\n // Persist all buffered work before releasing the storage lease. Failure leaves it open.\n while (this.memory.length) {\n this.store.put(this.memory[0]);\n this.memory.shift();\n }\n this.closed = true;\n const stopped = Error(\"MQTT output stopped; undelivered work retained on disk\");\n for (const completion of this.completions.values()) completion.reject(stopped);\n this.completions.clear();\n for (const waiter of this.waiters) {\n clearTimeout(waiter.timer);\n waiter.reject(stopped);\n }\n this.waiters.clear();\n this.emit();\n this.store.close();\n }\n private schedule(): void {\n if (!this.active || this.closed || this.blocked || this.processing || this.retryTimer) return;\n // Defer so enqueue acceptance is visible before the delivery callback.\n this.processing = Promise.resolve()\n .then(() => this.run())\n .catch((error) => this.fail(error))\n .finally(() => {\n this.processing = undefined;\n if (!this.closed) {\n this.emit();\n if (this.snapshot().pendingRecords) this.schedule();\n }\n });\n }\n private async run(): Promise<void> {\n while (this.active && !this.closed && !this.blocked) {\n const disk = this.store.head();\n const memory = this.memory[0];\n const record = !memory || (disk && disk.seq < memory.seq) ? disk : memory;\n if (!record) return;\n try {\n await this.send(record);\n } catch {\n // A lost acknowledgement remains uncertain; persist and replay, never mark success.\n // After an observed outage, move all already accepted RAM work to disk.\n while (this.memory.length) {\n this.store.put(this.memory[0]);\n this.memory.shift();\n }\n this.retries++;\n this.lastError = \"MQTT delivery unconfirmed; retained for retry\";\n if (this.active)\n this.retryTimer = setTimeout(() => {\n this.retryTimer = undefined;\n this.schedule();\n }, this.config.retryIntervalMs);\n return;\n }\n if (record === memory) this.memory.shift();\n else this.store.complete(record);\n this.delivered++;\n this.lastError = null;\n this.completions.get(record.id)?.resolve();\n this.completions.delete(record.id);\n this.emit();\n }\n }\n private fail(reason: unknown): void {\n this.blocked = reason instanceof Error ? reason : Error(\"MQTT outbox storage failed\");\n this.lastError = \"MQTT outbox blocked; preserve storage and inspect local error\";\n for (const waiter of this.waiters) {\n clearTimeout(waiter.timer);\n waiter.reject(this.blocked);\n }\n this.waiters.clear();\n this.emit();\n }\n private emit(): void {\n this.changed(this.snapshot());\n if (!this.snapshot().pendingRecords && !this.processing) {\n for (const waiter of this.waiters) {\n clearTimeout(waiter.timer);\n waiter.resolve();\n }\n this.waiters.clear();\n }\n }\n}\n"]}
@@ -37,6 +37,7 @@ export default class UnsMqttProxy extends UnsProxy {
37
37
  private stopPromise;
38
38
  private expectedWorkerExit;
39
39
  private publishQueueStatus?;
40
+ private publishesSealed;
40
41
  private admissionBytes;
41
42
  private pendingAdmissionBytes;
42
43
  private queueRequests;
@@ -145,6 +146,8 @@ export default class UnsMqttProxy extends UnsProxy {
145
146
  */
146
147
  stop(options?: UnsMqttProxyStopOptions): Promise<void>;
147
148
  private performStop;
149
+ /** A completed migration owns this fence until the original process exits. */
150
+ sealPublishes(): void;
148
151
  getPublishQueueStatus(): MqttPublishQueueStatus | undefined;
149
152
  private requestPublishQueue;
150
153
  private failPendingPublishes;
@@ -1 +1 @@
1
- {"version":3,"file":"uns-mqtt-proxy.d.ts","sourceRoot":"","sources":["../../src/uns-mqtt/uns-mqtt-proxy.ts"],"names":[],"mappings":"AACA,OAAO,EAAoD,KAAK,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAE5H,OAAO,EAAE,qBAAqB,EAAE,MAAM,MAAM,CAAC;AAM7C,OAAO,EAAE,mBAAmB,EAAsC,UAAU,EAAE,cAAc,EAAmG,SAAS,EAAa,MAAM,0BAA0B,CAAC;AAOtP,OAAO,QAAQ,MAAM,qBAAqB,CAAC;AAa3C,oBAAY,WAAW;IACrB,GAAG,QAAQ,CAAM,iCAAiC;IAClD;;;OAGG;IACH,KAAK,UAAU,CAAE,8BAA8B;IAC/C;;;OAGG;IACH,IAAI,SAAS;CACd;AAED,MAAM,WAAW,uBAAuB;IACtC,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAwCD,MAAM,CAAC,OAAO,OAAO,YAAa,SAAQ,QAAQ;IAChD,gBAAuB,wBAAwB,SAAU;IACzD,OAAO,CAAC,UAAU,CAA8E;IAChG,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,eAAe,CAAmF;IAC1G,OAAO,CAAC,yBAAyB,CAAqB;IACtD,OAAO,CAAC,aAAa,CAAiB;IACtC,SAAS,CAAC,kBAAkB,EAAE,MAAM,CAAC;IAC9B,YAAY,EAAE,MAAM,CAAC;IAC5B,OAAO,CAAC,iBAAiB,CAAkC;IAC3D,OAAO,CAAC,YAAY,CAAmB;IACvC,OAAO,CAAC,0BAA0B,CAAS;IAC3C,OAAO,CAAC,YAAY,CAA8F;IAClH,OAAO,CAAC,aAAa,CAAsB;IAC3C,OAAO,CAAC,cAAc,CAAyC;IAC/D,OAAO,CAAC,WAAW,CAA8B;IACjD,OAAO,CAAC,kBAAkB,CAAS;IACnC,OAAO,CAAC,kBAAkB,CAAC,CAAyB;IACpD,OAAO,CAAC,cAAc,CAA6B;IACnD,OAAO,CAAC,qBAAqB,CAAK;IAClC,OAAO,CAAC,aAAa,CAAoH;gBAIvI,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,EACpB,aAAa,CAAC,EAAE,cAAc,EAC9B,eAAe,GAAE,OAAe,EAChC,gBAAgB,GAAE,OAAe;IAgDnC;;;OAGG;IACH,OAAO,CAAC,qBAAqB;IAkC7B;;OAEG;IACH,OAAO,CAAC,wBAAwB;IAIhC;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAqFxB;;;;;;;OAOG;YACW,2BAA2B;IAmCzC;;;;;OAKG;IACI,kBAAkB,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI;IAI3E;;;OAGG;IACI,mBAAmB,IAAI,OAAO,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;IAc9D;;;;;OAKG;IACI,mBAAmB,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI;IAI5E;;;OAGG;IACI,oBAAoB,IAAI,OAAO,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;IAe/D;;;OAGG;IACU,iCAAiC,IAAI,OAAO,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;IAOrE,cAAc,CAAC,SAAS,GAAE,MAA8C,GAAG,OAAO,CAAC,IAAI,CAAC;IAwBxF,KAAK,CAAC,SAAS,GAAE,MAA8C,GAAG,OAAO,CAAC,IAAI,CAAC;IAI5F;;;;;;OAMG;IACU,kBAAkB,CAAC,WAAW,EAAE,mBAAmB,GAAG,IAAI,EAAE,IAAI,GAAE,WAA6B;IA2I5G,OAAO,CAAC,0BAA0B;IAelC,OAAO,CAAC,uBAAuB;IAQ/B;;;;;;OAMG;IACI,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC;IAIrG;;;;;OAKG;IACI,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI;IAI7D;;;;OAIG;IACI,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI;IAItD;;;;OAIG;IACI,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI;IAI/C;;;;;;;OAOG;YACW,wBAAwB;IAkHtC;;OAEG;IACI,IAAI,CAAC,OAAO,GAAE,uBAA4B,GAAG,OAAO,CAAC,IAAI,CAAC;YAanD,WAAW;IAuClB,qBAAqB,IAAI,sBAAsB,GAAG,SAAS;IAIlE,OAAO,CAAC,mBAAmB;IAc3B,OAAO,CAAC,oBAAoB;IAO5B,OAAO,CAAC,0BAA0B;IAiBlC,OAAO,CAAC,kBAAkB;IAuB1B,OAAO,CAAC,iBAAiB;CAI1B"}
1
+ {"version":3,"file":"uns-mqtt-proxy.d.ts","sourceRoot":"","sources":["../../src/uns-mqtt/uns-mqtt-proxy.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import { assertDisabledPublishQueueEmpty } from "./disabled-publish-queue.js";
1
2
  import { randomUUID } from "node:crypto";
2
3
  import { mqttPublishQueueIdentity, mqttPublishQueueSchema } from "./publish-queue-contract.js";
3
4
  import { readFileSync } from "fs";
@@ -49,6 +50,7 @@ export default class UnsMqttProxy extends UnsProxy {
49
50
  stopPromise = null;
50
51
  expectedWorkerExit = false;
51
52
  publishQueueStatus;
53
+ publishesSealed = false;
52
54
  admissionBytes = new Map();
53
55
  pendingAdmissionBytes = 0;
54
56
  queueRequests = new Map();
@@ -90,7 +92,13 @@ export default class UnsMqttProxy extends UnsProxy {
90
92
  };
91
93
  this.unsParameters = { ...(unsParameters ?? {}) };
92
94
  if (unsParameters?.publishQueue) {
93
- this.unsParameters.publishQueue = mqttPublishQueueSchema.parse(unsParameters.publishQueue);
95
+ const queue = mqttPublishQueueSchema.parse(unsParameters.publishQueue);
96
+ if (queue.enabled === false) {
97
+ assertDisabledPublishQueueEmpty(queue, mqttPublishQueueIdentity(packageJson.name, this.instanceNameWithSuffix, { host: mqttHost, ...mqttParameters }));
98
+ delete this.unsParameters.publishQueue;
99
+ }
100
+ else
101
+ this.unsParameters.publishQueue = queue;
94
102
  if ((unsParameters.publishConcurrency ?? 1) !== 1)
95
103
  throw Error("Persistent MQTT output requires publishConcurrency=1");
96
104
  }
@@ -234,6 +242,10 @@ export default class UnsMqttProxy extends UnsProxy {
234
242
  */
235
243
  async enqueueMessageToWorkerQueue(topic, message, options) {
236
244
  return new Promise((resolve, reject) => {
245
+ if (this.publishesSealed) {
246
+ reject(Error("MQTT output admission is sealed after clean migration drain"));
247
+ return;
248
+ }
237
249
  if (this.lifecycleState !== "running") {
238
250
  reject(new Error(`${this.instanceNameWithSuffix} - MQTT proxy is ${this.lifecycleState}; publish rejected.`));
239
251
  return;
@@ -368,6 +380,8 @@ export default class UnsMqttProxy extends UnsProxy {
368
380
  * producer-side delta calculation loses state across restarts.
369
381
  */
370
382
  async publishMqttMessage(mqttMessage, mode = MessageMode.Raw) {
383
+ if (this.publishesSealed)
384
+ throw Error("MQTT output admission is sealed after clean migration drain");
371
385
  if (!mqttMessage) {
372
386
  logger.error(`${this.instanceNameWithSuffix} - Error publishing mqtt message: mqttMessage must be defined.`);
373
387
  return;
@@ -705,6 +719,8 @@ export default class UnsMqttProxy extends UnsProxy {
705
719
  throw stopError;
706
720
  }
707
721
  }
722
+ /** A completed migration owns this fence until the original process exits. */
723
+ sealPublishes() { this.publishesSealed = true; }
708
724
  getPublishQueueStatus() {
709
725
  return this.publishQueueStatus ? { ...this.publishQueueStatus } : undefined;
710
726
  }