@nestarc/inbox 0.0.0-stage → 0.2.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 (98) hide show
  1. package/CHANGELOG.md +72 -0
  2. package/LICENSE +21 -0
  3. package/README.md +309 -2
  4. package/dist/contracts/errors.d.ts +34 -0
  5. package/dist/contracts/errors.js +73 -0
  6. package/dist/contracts/errors.js.map +1 -0
  7. package/dist/contracts/types.d.ts +174 -0
  8. package/dist/contracts/types.js +3 -0
  9. package/dist/contracts/types.js.map +1 -0
  10. package/dist/inbox-admin.service.d.ts +44 -0
  11. package/dist/inbox-admin.service.js +102 -0
  12. package/dist/inbox-admin.service.js.map +1 -0
  13. package/dist/inbox.module.d.ts +13 -0
  14. package/dist/inbox.module.js +59 -0
  15. package/dist/inbox.module.js.map +1 -0
  16. package/dist/inbox.service.d.ts +22 -0
  17. package/dist/inbox.service.js +74 -0
  18. package/dist/inbox.service.js.map +1 -0
  19. package/dist/index.d.ts +9 -0
  20. package/dist/index.js +28 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/integrations/tenancy.d.ts +25 -0
  23. package/dist/integrations/tenancy.js +56 -0
  24. package/dist/integrations/tenancy.js.map +1 -0
  25. package/dist/internal/handler-context.d.ts +3 -0
  26. package/dist/internal/handler-context.js +39 -0
  27. package/dist/internal/handler-context.js.map +1 -0
  28. package/dist/internal/options.d.ts +5 -0
  29. package/dist/internal/options.js +86 -0
  30. package/dist/internal/options.js.map +1 -0
  31. package/dist/internal/reliability-core/abort.d.ts +2 -0
  32. package/dist/internal/reliability-core/abort.js +21 -0
  33. package/dist/internal/reliability-core/abort.js.map +1 -0
  34. package/dist/internal/reliability-core/index.d.ts +3 -0
  35. package/dist/internal/reliability-core/index.js +14 -0
  36. package/dist/internal/reliability-core/index.js.map +1 -0
  37. package/dist/internal/reliability-core/json.d.ts +13 -0
  38. package/dist/internal/reliability-core/json.js +125 -0
  39. package/dist/internal/reliability-core/json.js.map +1 -0
  40. package/dist/internal/reliability-core/retry.d.ts +8 -0
  41. package/dist/internal/reliability-core/retry.js +26 -0
  42. package/dist/internal/reliability-core/retry.js.map +1 -0
  43. package/dist/internal/validation.d.ts +6 -0
  44. package/dist/internal/validation.js +123 -0
  45. package/dist/internal/validation.js.map +1 -0
  46. package/dist/storage/prisma/prisma-inbox.storage.d.ts +30 -0
  47. package/dist/storage/prisma/prisma-inbox.storage.js +369 -0
  48. package/dist/storage/prisma/prisma-inbox.storage.js.map +1 -0
  49. package/dist/testing/index.d.ts +40 -0
  50. package/dist/testing/index.js +120 -0
  51. package/dist/testing/index.js.map +1 -0
  52. package/dist/worker/inbox-worker.d.ts +31 -0
  53. package/dist/worker/inbox-worker.js +343 -0
  54. package/dist/worker/inbox-worker.js.map +1 -0
  55. package/docs/adr/0001-durable-inbox.md +40 -0
  56. package/docs/benchmarks/2026-10-27-local.md +31 -0
  57. package/docs/benchmarks/2026-10-28-preview1-local.md +87 -0
  58. package/docs/benchmarks/2026-10-28-preview1-run1.json +120 -0
  59. package/docs/benchmarks/2026-10-28-preview1-run2.json +120 -0
  60. package/docs/benchmarks/2026-10-28-preview1-run3.json +120 -0
  61. package/docs/benchmarks/2026-10-28-sustained-local.md +104 -0
  62. package/docs/benchmarks/2026-10-28-sustained-run1.json +2553 -0
  63. package/docs/benchmarks/2026-10-28-sustained-run2.json +2553 -0
  64. package/docs/broker-recovery-results.json +52 -0
  65. package/docs/broker-recovery.md +30 -0
  66. package/docs/db-operations.md +41 -0
  67. package/docs/implementation-roadmap.md +129 -0
  68. package/docs/operations/2026-10-28-database.json +34 -0
  69. package/docs/package-plan.md +250 -0
  70. package/docs/releasing.md +91 -0
  71. package/docs/testing.md +86 -0
  72. package/docs/usage.md +266 -0
  73. package/docs/validation.md +174 -0
  74. package/examples/README.md +124 -0
  75. package/examples/compose.yaml +26 -0
  76. package/examples/package.json +42 -0
  77. package/examples/prisma/schema.prisma +29 -0
  78. package/examples/prisma.config.ts +10 -0
  79. package/examples/scripts/prepare-db.cjs +45 -0
  80. package/examples/scripts/smoke.cjs +37 -0
  81. package/examples/scripts/with-brokers.cjs +249 -0
  82. package/examples/scripts/with-rabbitmq.cjs +131 -0
  83. package/examples/sql/application.sql +14 -0
  84. package/examples/sql/tenancy.sql +26 -0
  85. package/examples/src/broker-common.ts +79 -0
  86. package/examples/src/bullmq-ingress/main.ts +165 -0
  87. package/examples/src/common.ts +112 -0
  88. package/examples/src/idempotency-ingress/main.ts +211 -0
  89. package/examples/src/kafka-ingress/main.ts +209 -0
  90. package/examples/src/outbox-to-inbox/main.ts +198 -0
  91. package/examples/src/rabbitmq-ingress/main.ts +203 -0
  92. package/examples/src/sqs-ingress/main.ts +273 -0
  93. package/examples/src/tenancy/main.ts +210 -0
  94. package/examples/src/webhook-ingress/main.ts +172 -0
  95. package/examples/tsconfig.json +16 -0
  96. package/package.json +99 -4
  97. package/src/sql/create-inbox-table.sql +100 -0
  98. package/src/sql/migrate-v1-to-v2.sql +53 -0
@@ -0,0 +1,3 @@
1
+ import type { InboxContext, InboxHandlerContextRunner } from "../contracts/types";
2
+ /** A context wrapper cannot skip the handler, swallow its failure, or invoke it twice. */
3
+ export declare function runInHandlerContext(context: InboxContext, handle: () => Promise<void>, runner?: InboxHandlerContextRunner): Promise<void>;
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runInHandlerContext = runInHandlerContext;
4
+ const errors_1 = require("../contracts/errors");
5
+ /** A context wrapper cannot skip the handler, swallow its failure, or invoke it twice. */
6
+ async function runInHandlerContext(context, handle, runner) {
7
+ context.signal.throwIfAborted();
8
+ if (!runner)
9
+ return handle();
10
+ let invocation;
11
+ let invalid = false;
12
+ let open = true;
13
+ const next = () => {
14
+ context.signal.throwIfAborted();
15
+ if (!open || invocation) {
16
+ invalid = true;
17
+ throw new errors_1.InboxConfigurationError("handlerContext must call next exactly once.");
18
+ }
19
+ invocation = Promise.resolve().then(() => {
20
+ context.signal.throwIfAborted();
21
+ return handle();
22
+ });
23
+ // Also observe immediately when a wrapper forgets to await or swallows rejection.
24
+ void invocation.catch(() => undefined);
25
+ return invocation;
26
+ };
27
+ try {
28
+ await runner(context, next);
29
+ if (!invocation)
30
+ throw new errors_1.InboxConfigurationError("handlerContext must call next exactly once.");
31
+ await invocation;
32
+ if (invalid)
33
+ throw new errors_1.InboxConfigurationError("handlerContext must call next exactly once.");
34
+ }
35
+ finally {
36
+ open = false;
37
+ }
38
+ }
39
+ //# sourceMappingURL=handler-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler-context.js","sourceRoot":"","sources":["../../src/internal/handler-context.ts"],"names":[],"mappings":";;AAOA,kDAwCC;AA/CD,gDAA8D;AAM9D,0FAA0F;AACnF,KAAK,UAAU,mBAAmB,CACvC,OAAqB,EACrB,MAA2B,EAC3B,MAAkC;IAElC,OAAO,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC;IAChC,IAAI,CAAC,MAAM;QAAE,OAAO,MAAM,EAAE,CAAC;IAC7B,IAAI,UAAqC,CAAC;IAC1C,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,IAAI,IAAI,GAAG,IAAI,CAAC;IAChB,MAAM,IAAI,GAAG,GAAkB,EAAE;QAC/B,OAAO,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC;QAChC,IAAI,CAAC,IAAI,IAAI,UAAU,EAAE,CAAC;YACxB,OAAO,GAAG,IAAI,CAAC;YACf,MAAM,IAAI,gCAAuB,CAC/B,6CAA6C,CAC9C,CAAC;QACJ,CAAC;QACD,UAAU,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE;YACvC,OAAO,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC;YAChC,OAAO,MAAM,EAAE,CAAC;QAClB,CAAC,CAAC,CAAC;QACH,kFAAkF;QAClF,KAAK,UAAU,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QACvC,OAAO,UAAU,CAAC;IACpB,CAAC,CAAC;IACF,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QAC5B,IAAI,CAAC,UAAU;YACb,MAAM,IAAI,gCAAuB,CAC/B,6CAA6C,CAC9C,CAAC;QACJ,MAAM,UAAU,CAAC;QACjB,IAAI,OAAO;YACT,MAAM,IAAI,gCAAuB,CAC/B,6CAA6C,CAC9C,CAAC;IACN,CAAC;YAAS,CAAC;QACT,IAAI,GAAG,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
@@ -0,0 +1,5 @@
1
+ import type { InboxOptions, ResolvedWorkerOptions } from "../contracts/types";
2
+ export declare function resolveOptions(options: InboxOptions): {
3
+ consumers: string[];
4
+ worker: ResolvedWorkerOptions;
5
+ };
@@ -0,0 +1,86 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveOptions = resolveOptions;
4
+ const errors_1 = require("../contracts/errors");
5
+ const validation_1 = require("./validation");
6
+ function resolveOptions(options) {
7
+ if (!options ||
8
+ !options.prisma ||
9
+ typeof options.prisma.$transaction !== "function") {
10
+ throw new errors_1.InboxConfigurationError("An application-owned Prisma client with interactive transactions is required.");
11
+ }
12
+ const handlers = options.handlers ?? [];
13
+ if (options.handlerContext !== undefined &&
14
+ typeof options.handlerContext !== "function") {
15
+ throw new errors_1.InboxConfigurationError("handlerContext must be a function.");
16
+ }
17
+ const registered = handlers.map((handler) => {
18
+ (0, validation_1.boundedString)(handler.consumer, "handler.consumer", 128);
19
+ (0, validation_1.boundedString)(handler.eventType, "handler.eventType", 128);
20
+ if (!Number.isInteger(handler.schemaVersion) ||
21
+ handler.schemaVersion < 1 ||
22
+ handler.schemaVersion > 2147483647 ||
23
+ typeof handler.handle !== "function") {
24
+ throw new errors_1.InboxConfigurationError("Each handler needs an eventType, positive schemaVersion, and handle method.");
25
+ }
26
+ return handler.consumer;
27
+ });
28
+ if (new Set(registered).size !== registered.length)
29
+ throw new errors_1.InboxConfigurationError("Consumer handlers must be unique.");
30
+ const consumers = [...(options.consumers ?? registered)];
31
+ if (!consumers.length || new Set(consumers).size !== consumers.length)
32
+ throw new errors_1.InboxConfigurationError("Provide a non-empty unique consumer allowlist.");
33
+ consumers.forEach((name) => (0, validation_1.boundedString)(name, "consumer", 128));
34
+ const w = options.worker ?? {};
35
+ const worker = {
36
+ enabled: w.enabled ?? false,
37
+ consumers: [...(w.consumers ?? registered)],
38
+ concurrency: w.concurrency ?? 4,
39
+ pollIntervalMs: w.pollIntervalMs ?? 1000,
40
+ leaseMs: w.leaseMs ?? 30000,
41
+ heartbeatIntervalMs: w.heartbeatIntervalMs ?? 5000,
42
+ transactionTimeoutMs: w.transactionTimeoutMs ?? 10000,
43
+ transactionMaxWaitMs: w.transactionMaxWaitMs ?? 5000,
44
+ lockTimeoutMs: w.lockTimeoutMs ?? 1000,
45
+ shutdownTimeoutMs: w.shutdownTimeoutMs ?? 15000,
46
+ maxAttempts: w.maxAttempts ?? 8,
47
+ retryBaseMs: w.retryBaseMs ?? 1000,
48
+ retryMaxMs: w.retryMaxMs ?? 300000,
49
+ };
50
+ for (const [key, value] of Object.entries(worker)) {
51
+ if (key === "enabled" || key === "consumers")
52
+ continue;
53
+ if (!Number.isSafeInteger(value) ||
54
+ value < 1 ||
55
+ value > 2147483647) {
56
+ throw new errors_1.InboxConfigurationError(`${key} must be a positive 32-bit integer.`);
57
+ }
58
+ }
59
+ if (typeof worker.enabled !== "boolean" ||
60
+ worker.concurrency > 100 ||
61
+ worker.maxAttempts > 10000 ||
62
+ worker.heartbeatIntervalMs * 2 >= worker.leaseMs ||
63
+ worker.retryBaseMs > worker.retryMaxMs ||
64
+ worker.transactionTimeoutMs + worker.transactionMaxWaitMs > 2147483647) {
65
+ throw new errors_1.InboxConfigurationError("Invalid concurrency, heartbeat, retry, or attempt bounds.");
66
+ }
67
+ if (new Set(worker.consumers).size !== worker.consumers.length ||
68
+ worker.consumers.some((name) => !registered.includes(name)) ||
69
+ (worker.enabled && !worker.consumers.length)) {
70
+ throw new errors_1.InboxConfigurationError("Every owned consumer must have exactly one registered handler.");
71
+ }
72
+ if (options.maxPayloadBytes !== undefined &&
73
+ (!Number.isSafeInteger(options.maxPayloadBytes) ||
74
+ options.maxPayloadBytes < 1 ||
75
+ options.maxPayloadBytes > 16777216)) {
76
+ throw new errors_1.InboxConfigurationError("maxPayloadBytes must be between 1 and 16777216.");
77
+ }
78
+ if (options.maxPendingMessages !== undefined &&
79
+ (!Number.isSafeInteger(options.maxPendingMessages) ||
80
+ options.maxPendingMessages < 1 ||
81
+ options.maxPendingMessages > 2147483647)) {
82
+ throw new errors_1.InboxConfigurationError("maxPendingMessages must be a positive 32-bit integer.");
83
+ }
84
+ return { consumers, worker };
85
+ }
86
+ //# sourceMappingURL=options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"options.js","sourceRoot":"","sources":["../../src/internal/options.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ /** Cancellation rejects the owning operation; it cannot undo external side effects. */
2
+ export declare function withAbort<T>(signal: AbortSignal, run: () => Promise<T>): Promise<T>;
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.withAbort = withAbort;
4
+ /** Cancellation rejects the owning operation; it cannot undo external side effects. */
5
+ function withAbort(signal, run) {
6
+ if (signal.aborted)
7
+ return Promise.reject(signal.reason);
8
+ return new Promise((resolve, reject) => {
9
+ const aborted = () => reject(signal.reason);
10
+ signal.addEventListener("abort", aborted, { once: true });
11
+ void Promise.resolve()
12
+ .then(() => {
13
+ if (signal.aborted)
14
+ throw signal.reason;
15
+ return run();
16
+ })
17
+ .then(resolve, reject)
18
+ .finally(() => signal.removeEventListener("abort", aborted));
19
+ });
20
+ }
21
+ //# sourceMappingURL=abort.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"abort.js","sourceRoot":"","sources":["../../../src/internal/reliability-core/abort.ts"],"names":[],"mappings":";;AACA,8BAgBC;AAjBD,uFAAuF;AACvF,SAAgB,SAAS,CACvB,MAAmB,EACnB,GAAqB;IAErB,IAAI,MAAM,CAAC,OAAO;QAAE,OAAO,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACzD,OAAO,IAAI,OAAO,CAAI,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACxC,MAAM,OAAO,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC5C,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1D,KAAK,OAAO,CAAC,OAAO,EAAE;aACnB,IAAI,CAAC,GAAG,EAAE;YACT,IAAI,MAAM,CAAC,OAAO;gBAAE,MAAM,MAAM,CAAC,MAAM,CAAC;YACxC,OAAO,GAAG,EAAE,CAAC;QACf,CAAC,CAAC;aACD,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC;aACrB,OAAO,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;IACjE,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { CanonicalJsonError, assertPlainJsonObject, canonicalJson, isValidUnicode, sha256Json, } from "./json";
2
+ export { retryDelayMs, type RetryPolicy } from "./retry";
3
+ export { withAbort } from "./abort";
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.withAbort = exports.retryDelayMs = exports.sha256Json = exports.isValidUnicode = exports.canonicalJson = exports.assertPlainJsonObject = exports.CanonicalJsonError = void 0;
4
+ var json_1 = require("./json");
5
+ Object.defineProperty(exports, "CanonicalJsonError", { enumerable: true, get: function () { return json_1.CanonicalJsonError; } });
6
+ Object.defineProperty(exports, "assertPlainJsonObject", { enumerable: true, get: function () { return json_1.assertPlainJsonObject; } });
7
+ Object.defineProperty(exports, "canonicalJson", { enumerable: true, get: function () { return json_1.canonicalJson; } });
8
+ Object.defineProperty(exports, "isValidUnicode", { enumerable: true, get: function () { return json_1.isValidUnicode; } });
9
+ Object.defineProperty(exports, "sha256Json", { enumerable: true, get: function () { return json_1.sha256Json; } });
10
+ var retry_1 = require("./retry");
11
+ Object.defineProperty(exports, "retryDelayMs", { enumerable: true, get: function () { return retry_1.retryDelayMs; } });
12
+ var abort_1 = require("./abort");
13
+ Object.defineProperty(exports, "withAbort", { enumerable: true, get: function () { return abort_1.withAbort; } });
14
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/internal/reliability-core/index.ts"],"names":[],"mappings":";;;AAAA,+BAMgB;AALd,0GAAA,kBAAkB,OAAA;AAClB,6GAAA,qBAAqB,OAAA;AACrB,qGAAA,aAAa,OAAA;AACb,sGAAA,cAAc,OAAA;AACd,kGAAA,UAAU,OAAA;AAEZ,iCAAyD;AAAhD,qGAAA,YAAY,OAAA;AACrB,iCAAoC;AAA3B,kGAAA,SAAS,OAAA"}
@@ -0,0 +1,13 @@
1
+ /** Strict JSON data shared by durable message and request boundaries. */
2
+ export declare class CanonicalJsonError extends TypeError {
3
+ }
4
+ export declare function assertPlainJsonObject(value: unknown, label: string): asserts value is Record<string, unknown>;
5
+ /** Reject NUL and unpaired surrogates, which PostgreSQL JSON cannot store. */
6
+ export declare function isValidUnicode(value: string): boolean;
7
+ /** v1: sorted UTF-16 object keys, JSON number/string encoding, original array order. */
8
+ export declare function canonicalJson(value: unknown, maxBytes?: number): string;
9
+ /** Optional domain separation keeps message fingerprints distinct from HTTP fingerprints. */
10
+ export declare function sha256Json(value: unknown, options?: {
11
+ domain?: string;
12
+ maxBytes?: number;
13
+ }): string;
@@ -0,0 +1,125 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CanonicalJsonError = void 0;
4
+ exports.assertPlainJsonObject = assertPlainJsonObject;
5
+ exports.isValidUnicode = isValidUnicode;
6
+ exports.canonicalJson = canonicalJson;
7
+ exports.sha256Json = sha256Json;
8
+ const node_crypto_1 = require("node:crypto");
9
+ /** Strict JSON data shared by durable message and request boundaries. */
10
+ class CanonicalJsonError extends TypeError {
11
+ }
12
+ exports.CanonicalJsonError = CanonicalJsonError;
13
+ function assertPlainJsonObject(value, label) {
14
+ if (value === null ||
15
+ typeof value !== "object" ||
16
+ Array.isArray(value) ||
17
+ ![Object.prototype, null].includes(Object.getPrototypeOf(value))) {
18
+ throw new CanonicalJsonError(`${label} must be a plain object.`);
19
+ }
20
+ for (const key of Reflect.ownKeys(value)) {
21
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
22
+ if (typeof key !== "string" ||
23
+ !descriptor.enumerable ||
24
+ !("value" in descriptor)) {
25
+ throw new CanonicalJsonError(`${label} must contain only JSON data properties.`);
26
+ }
27
+ }
28
+ }
29
+ /** Reject NUL and unpaired surrogates, which PostgreSQL JSON cannot store. */
30
+ function isValidUnicode(value) {
31
+ for (let i = 0; i < value.length; i++) {
32
+ const code = value.charCodeAt(i);
33
+ if (code === 0)
34
+ return false;
35
+ if (code >= 0xd800 && code <= 0xdbff) {
36
+ const next = value.charCodeAt(++i);
37
+ if (!(next >= 0xdc00 && next <= 0xdfff))
38
+ return false;
39
+ }
40
+ else if (code >= 0xdc00 && code <= 0xdfff)
41
+ return false;
42
+ }
43
+ return true;
44
+ }
45
+ /** v1: sorted UTF-16 object keys, JSON number/string encoding, original array order. */
46
+ function canonicalJson(value, maxBytes = 262144) {
47
+ if (!Number.isSafeInteger(maxBytes) || maxBytes < 1)
48
+ throw new CanonicalJsonError("maxBytes must be a positive safe integer.");
49
+ const ancestors = new Set();
50
+ let nodes = 0;
51
+ let bytes = 0;
52
+ const chunks = [];
53
+ const emit = (fragment) => {
54
+ bytes += Buffer.byteLength(fragment);
55
+ if (bytes > maxBytes)
56
+ throw new CanonicalJsonError("JSON exceeds maxBytes.");
57
+ chunks.push(fragment);
58
+ };
59
+ const visit = (item, depth) => {
60
+ if (++nodes > 100000 || depth > 64)
61
+ throw new CanonicalJsonError("JSON exceeds its structural limit.");
62
+ if (item === null)
63
+ return emit("null");
64
+ if (typeof item === "boolean")
65
+ return emit(String(item));
66
+ if (typeof item === "number" && Number.isFinite(item))
67
+ return emit(JSON.stringify(item));
68
+ if (typeof item === "string") {
69
+ if (!isValidUnicode(item) || Buffer.byteLength(item) > maxBytes)
70
+ throw new CanonicalJsonError("Invalid or oversized JSON string.");
71
+ return emit(JSON.stringify(item));
72
+ }
73
+ if (!item || typeof item !== "object")
74
+ throw new CanonicalJsonError("Payload must contain only JSON values.");
75
+ if (ancestors.has(item))
76
+ throw new CanonicalJsonError("Cyclic payloads are not supported.");
77
+ ancestors.add(item);
78
+ if (Array.isArray(item)) {
79
+ if (item.length > 100000 ||
80
+ Reflect.ownKeys(item).length !== item.length + 1) {
81
+ throw new CanonicalJsonError("Sparse arrays and extra array properties are not supported.");
82
+ }
83
+ emit("[");
84
+ for (let index = 0; index < item.length; index++) {
85
+ if (index > 0)
86
+ emit(",");
87
+ const descriptor = Object.getOwnPropertyDescriptor(item, String(index));
88
+ if (!descriptor || !("value" in descriptor))
89
+ throw new CanonicalJsonError("Array accessors are not supported.");
90
+ visit(descriptor.value, depth + 1);
91
+ }
92
+ emit("]");
93
+ }
94
+ else {
95
+ assertPlainJsonObject(item, "payload");
96
+ emit("{");
97
+ let first = true;
98
+ for (const key of Object.keys(item).sort()) {
99
+ if (!first)
100
+ emit(",");
101
+ first = false;
102
+ if (!isValidUnicode(key) || Buffer.byteLength(key) > maxBytes)
103
+ throw new CanonicalJsonError("Invalid JSON key.");
104
+ emit(JSON.stringify(key));
105
+ emit(":");
106
+ visit(item[key], depth + 1);
107
+ }
108
+ emit("}");
109
+ }
110
+ ancestors.delete(item);
111
+ };
112
+ visit(value, 0);
113
+ return chunks.join("");
114
+ }
115
+ /** Optional domain separation keeps message fingerprints distinct from HTTP fingerprints. */
116
+ function sha256Json(value, options = {}) {
117
+ const hash = (0, node_crypto_1.createHash)("sha256");
118
+ if (options.domain !== undefined) {
119
+ if (!options.domain || !isValidUnicode(options.domain))
120
+ throw new CanonicalJsonError("Fingerprint domain must be nonempty UTF-8 without NUL.");
121
+ hash.update(options.domain).update("\0");
122
+ }
123
+ return hash.update(canonicalJson(value, options.maxBytes)).digest("hex");
124
+ }
125
+ //# sourceMappingURL=json.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json.js","sourceRoot":"","sources":["../../../src/internal/reliability-core/json.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ export interface RetryPolicy {
2
+ baseMs: number;
3
+ maxMs: number;
4
+ /** Inbox uses full jitter; existing outbox consumers can keep deterministic delays. */
5
+ jitter?: "none" | "full";
6
+ }
7
+ /** attempt is one-based and includes the initial claim. A crash still consumes an attempt. */
8
+ export declare function retryDelayMs(attempt: number, policy: RetryPolicy, random?: () => number): number;
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.retryDelayMs = retryDelayMs;
4
+ /** attempt is one-based and includes the initial claim. A crash still consumes an attempt. */
5
+ function retryDelayMs(attempt, policy, random = Math.random) {
6
+ if (!Number.isSafeInteger(attempt) ||
7
+ attempt < 1 ||
8
+ !Number.isSafeInteger(policy.baseMs) ||
9
+ !Number.isSafeInteger(policy.maxMs) ||
10
+ policy.baseMs < 0 ||
11
+ policy.maxMs < policy.baseMs ||
12
+ policy.maxMs > 2147483647 ||
13
+ (policy.jitter !== undefined && !["none", "full"].includes(policy.jitter))) {
14
+ throw new RangeError("Invalid retry attempt or bounded delay policy.");
15
+ }
16
+ const cap = policy.baseMs === 0
17
+ ? 0
18
+ : Math.min(policy.maxMs, policy.baseMs * 2 ** Math.min(attempt - 1, 31));
19
+ if (policy.jitter !== "full" || cap === 0)
20
+ return cap;
21
+ const sample = random();
22
+ if (!Number.isFinite(sample) || sample < 0 || sample >= 1)
23
+ throw new RangeError("Retry random source must return a value in [0, 1).");
24
+ return Math.floor(sample * (cap + 1));
25
+ }
26
+ //# sourceMappingURL=retry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retry.js","sourceRoot":"","sources":["../../../src/internal/reliability-core/retry.ts"],"names":[],"mappings":";;AAQA,oCA0BC;AA3BD,8FAA8F;AAC9F,SAAgB,YAAY,CAC1B,OAAe,EACf,MAAmB,EACnB,SAAuB,IAAI,CAAC,MAAM;IAElC,IACE,CAAC,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC;QAC9B,OAAO,GAAG,CAAC;QACX,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,MAAM,CAAC;QACpC,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,KAAK,CAAC;QACnC,MAAM,CAAC,MAAM,GAAG,CAAC;QACjB,MAAM,CAAC,KAAK,GAAG,MAAM,CAAC,MAAM;QAC5B,MAAM,CAAC,KAAK,GAAG,UAAU;QACzB,CAAC,MAAM,CAAC,MAAM,KAAK,SAAS,IAAI,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,EAC1E,CAAC;QACD,MAAM,IAAI,UAAU,CAAC,gDAAgD,CAAC,CAAC;IACzE,CAAC;IACD,MAAM,GAAG,GACP,MAAM,CAAC,MAAM,KAAK,CAAC;QACjB,CAAC,CAAC,CAAC;QACH,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;IAC7E,IAAI,MAAM,CAAC,MAAM,KAAK,MAAM,IAAI,GAAG,KAAK,CAAC;QAAE,OAAO,GAAG,CAAC;IACtD,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC;IACxB,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,MAAM,GAAG,CAAC,IAAI,MAAM,IAAI,CAAC;QACvD,MAAM,IAAI,UAAU,CAAC,oDAAoD,CAAC,CAAC;IAC7E,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;AACxC,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { InboxEnvelope, InboxScope, NormalizedEnvelope } from "../contracts/types";
2
+ export declare function boundedString(value: unknown, label: string, maxBytes: number): string;
3
+ export declare function validateScope(value: unknown): InboxScope;
4
+ /** Preserve the public Inbox error contract around the shared strict JSON encoder. */
5
+ export declare function canonicalJson(value: unknown, maxBytes?: number): string;
6
+ export declare function normalizeEnvelope(input: InboxEnvelope, consumers: string[], maxPayloadBytes?: number): NormalizedEnvelope;
@@ -0,0 +1,123 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.boundedString = boundedString;
4
+ exports.validateScope = validateScope;
5
+ exports.canonicalJson = canonicalJson;
6
+ exports.normalizeEnvelope = normalizeEnvelope;
7
+ const reliability_core_1 = require("./reliability-core");
8
+ const errors_1 = require("../contracts/errors");
9
+ function plain(value, label) {
10
+ try {
11
+ (0, reliability_core_1.assertPlainJsonObject)(value, label);
12
+ }
13
+ catch (error) {
14
+ if (error instanceof reliability_core_1.CanonicalJsonError)
15
+ throw new errors_1.InboxValidationError(error.message);
16
+ throw error;
17
+ }
18
+ }
19
+ function allowed(value, keys, label) {
20
+ if (Object.keys(value).some((key) => !keys.includes(key))) {
21
+ throw new errors_1.InboxValidationError(`${label} contains an unsupported field.`);
22
+ }
23
+ }
24
+ function boundedString(value, label, maxBytes) {
25
+ if (typeof value !== "string" ||
26
+ !value.trim() ||
27
+ !(0, reliability_core_1.isValidUnicode)(value) ||
28
+ Buffer.byteLength(value) > maxBytes) {
29
+ throw new errors_1.InboxValidationError(`${label} must be a non-empty UTF-8 string of at most ${maxBytes} bytes without NUL.`);
30
+ }
31
+ return value;
32
+ }
33
+ function validateScope(value) {
34
+ plain(value, "scope");
35
+ if (value.kind === "global") {
36
+ allowed(value, ["kind"], "scope");
37
+ return { kind: "global" };
38
+ }
39
+ if (value.kind === "tenant") {
40
+ allowed(value, ["kind", "id"], "scope");
41
+ return { kind: "tenant", id: boundedString(value.id, "scope.id", 128) };
42
+ }
43
+ throw new errors_1.InboxValidationError("scope.kind must be tenant or global.");
44
+ }
45
+ /** Preserve the public Inbox error contract around the shared strict JSON encoder. */
46
+ function canonicalJson(value, maxBytes = 262144) {
47
+ try {
48
+ return (0, reliability_core_1.canonicalJson)(value, maxBytes);
49
+ }
50
+ catch (error) {
51
+ if (error instanceof reliability_core_1.CanonicalJsonError)
52
+ throw new errors_1.InboxValidationError(error.message.replace("maxBytes", "maxPayloadBytes"));
53
+ throw error;
54
+ }
55
+ }
56
+ function normalizeEnvelope(input, consumers, maxPayloadBytes = 262144) {
57
+ plain(input, "envelope");
58
+ allowed(input, [
59
+ "scope",
60
+ "source",
61
+ "consumer",
62
+ "messageId",
63
+ "eventType",
64
+ "schemaVersion",
65
+ "payload",
66
+ "correlationId",
67
+ "causationId",
68
+ "headers",
69
+ ], "envelope");
70
+ const scope = validateScope(input.scope);
71
+ const source = boundedString(input.source, "source", 128);
72
+ const consumer = boundedString(input.consumer, "consumer", 128);
73
+ if (!consumers.includes(consumer))
74
+ throw new errors_1.InboxValidationError("The consumer is not allowed by this ingress.");
75
+ const messageId = boundedString(input.messageId, "messageId", 256);
76
+ const eventType = boundedString(input.eventType, "eventType", 128);
77
+ if (!Number.isSafeInteger(input.schemaVersion) ||
78
+ input.schemaVersion < 1 ||
79
+ input.schemaVersion > 2147483647) {
80
+ throw new errors_1.InboxValidationError("schemaVersion must be a positive 32-bit integer.");
81
+ }
82
+ const payloadJson = canonicalJson(input.payload, maxPayloadBytes);
83
+ const payload = JSON.parse(payloadJson);
84
+ let fingerprint;
85
+ try {
86
+ fingerprint = (0, reliability_core_1.sha256Json)({ eventType, schemaVersion: input.schemaVersion, payload }, {
87
+ domain: "nestarc.inbox.fingerprint.v1",
88
+ maxBytes: maxPayloadBytes + 1024,
89
+ });
90
+ }
91
+ catch (error) {
92
+ if (error instanceof reliability_core_1.CanonicalJsonError)
93
+ throw new errors_1.InboxValidationError(error.message.replace("maxBytes", "maxPayloadBytes"));
94
+ throw error;
95
+ }
96
+ const normalized = {
97
+ scope,
98
+ source,
99
+ consumer,
100
+ messageId,
101
+ eventType,
102
+ schemaVersion: input.schemaVersion,
103
+ payload,
104
+ payloadJson,
105
+ fingerprint,
106
+ fingerprintVersion: 1,
107
+ };
108
+ for (const key of ["correlationId", "causationId"]) {
109
+ if (input[key] !== undefined)
110
+ normalized[key] = boundedString(input[key], key, 256);
111
+ }
112
+ if (input.headers !== undefined) {
113
+ plain(input.headers, "headers");
114
+ allowed(input.headers, ["traceparent", "tracestate"], "headers");
115
+ normalized.headers = {};
116
+ for (const key of ["traceparent", "tracestate"]) {
117
+ if (input.headers[key] !== undefined)
118
+ normalized.headers[key] = boundedString(input.headers[key], key, 512);
119
+ }
120
+ }
121
+ return normalized;
122
+ }
123
+ //# sourceMappingURL=validation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation.js","sourceRoot":"","sources":["../../src/internal/validation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ import type { InboxReplayOptions, InboxReplayResult } from "../../inbox-admin.service";
2
+ import type { InboxListOptions, InboxPage, InboxPrismaClient, InboxReceipt, InboxRecord, InboxScope, InboxStats, InboxTransaction, NormalizedEnvelope } from "../../contracts/types";
3
+ /** Internal PostgreSQL implementation. Undefined scope is reserved for privileged administration. */
4
+ export declare class PrismaInboxStorage {
5
+ private readonly prisma;
6
+ private readonly options;
7
+ constructor(prisma: InboxPrismaClient, options?: {
8
+ maxPendingMessages?: number;
9
+ });
10
+ assertSchema(): Promise<void>;
11
+ accept(envelope: NormalizedEnvelope): Promise<InboxReceipt>;
12
+ private duplicate;
13
+ claim(consumers: string[], limit: number, workerId: string, leaseMs: number, maxAttempts: number): Promise<InboxRecord[]>;
14
+ run(claimed: InboxRecord, fn: (tx: InboxTransaction) => Promise<void>, options: {
15
+ timeoutMs: number;
16
+ maxWaitMs: number;
17
+ lockTimeoutMs: number;
18
+ }): Promise<void>;
19
+ heartbeat(id: string, token: string, leaseMs: number): Promise<boolean>;
20
+ fail(id: string, token: string, error: {
21
+ code: string;
22
+ message: string;
23
+ }, permanent: boolean, delayMs: number, maxAttempts: number): Promise<boolean>;
24
+ get(id: string, scope?: InboxScope): Promise<InboxRecord | null>;
25
+ list(options?: InboxListOptions, scope?: InboxScope): Promise<InboxPage>;
26
+ requeue(id: string, scope: InboxScope | undefined, expectedVersion: string, reason: string, actor: string): Promise<InboxRecord>;
27
+ replay(options: InboxReplayOptions, scope?: InboxScope): Promise<InboxReplayResult>;
28
+ stats(scope?: InboxScope): Promise<InboxStats>;
29
+ purgeCompletedPayload(before: Date, limit: number, scope?: InboxScope): Promise<number>;
30
+ }