@nestarc/webhook 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 (104) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +281 -0
  3. package/dist/adapters/fetch-http-client.d.ts +6 -0
  4. package/dist/adapters/fetch-http-client.d.ts.map +1 -0
  5. package/dist/adapters/fetch-http-client.js +56 -0
  6. package/dist/adapters/fetch-http-client.js.map +1 -0
  7. package/dist/adapters/prisma-delivery.repository.d.ts +18 -0
  8. package/dist/adapters/prisma-delivery.repository.d.ts.map +1 -0
  9. package/dist/adapters/prisma-delivery.repository.js +162 -0
  10. package/dist/adapters/prisma-delivery.repository.js.map +1 -0
  11. package/dist/adapters/prisma-endpoint.repository.d.ts +18 -0
  12. package/dist/adapters/prisma-endpoint.repository.d.ts.map +1 -0
  13. package/dist/adapters/prisma-endpoint.repository.js +157 -0
  14. package/dist/adapters/prisma-endpoint.repository.js.map +1 -0
  15. package/dist/adapters/prisma-event.repository.d.ts +8 -0
  16. package/dist/adapters/prisma-event.repository.d.ts.map +1 -0
  17. package/dist/adapters/prisma-event.repository.js +39 -0
  18. package/dist/adapters/prisma-event.repository.js.map +1 -0
  19. package/dist/index.d.ts +26 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +57 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/interfaces/webhook-delivery.interface.d.ts +39 -0
  24. package/dist/interfaces/webhook-delivery.interface.d.ts.map +1 -0
  25. package/dist/interfaces/webhook-delivery.interface.js +3 -0
  26. package/dist/interfaces/webhook-delivery.interface.js.map +1 -0
  27. package/dist/interfaces/webhook-endpoint.interface.d.ts +34 -0
  28. package/dist/interfaces/webhook-endpoint.interface.d.ts.map +1 -0
  29. package/dist/interfaces/webhook-endpoint.interface.js +3 -0
  30. package/dist/interfaces/webhook-endpoint.interface.js.map +1 -0
  31. package/dist/interfaces/webhook-options.interface.d.ts +45 -0
  32. package/dist/interfaces/webhook-options.interface.d.ts.map +1 -0
  33. package/dist/interfaces/webhook-options.interface.js +3 -0
  34. package/dist/interfaces/webhook-options.interface.js.map +1 -0
  35. package/dist/ports/webhook-delivery.repository.d.ts +26 -0
  36. package/dist/ports/webhook-delivery.repository.d.ts.map +1 -0
  37. package/dist/ports/webhook-delivery.repository.js +3 -0
  38. package/dist/ports/webhook-delivery.repository.js.map +1 -0
  39. package/dist/ports/webhook-endpoint.repository.d.ts +15 -0
  40. package/dist/ports/webhook-endpoint.repository.d.ts.map +1 -0
  41. package/dist/ports/webhook-endpoint.repository.js +3 -0
  42. package/dist/ports/webhook-endpoint.repository.js.map +1 -0
  43. package/dist/ports/webhook-event.repository.d.ts +5 -0
  44. package/dist/ports/webhook-event.repository.d.ts.map +1 -0
  45. package/dist/ports/webhook-event.repository.js +3 -0
  46. package/dist/ports/webhook-event.repository.js.map +1 -0
  47. package/dist/ports/webhook-http-client.d.ts +5 -0
  48. package/dist/ports/webhook-http-client.d.ts.map +1 -0
  49. package/dist/ports/webhook-http-client.js +3 -0
  50. package/dist/ports/webhook-http-client.js.map +1 -0
  51. package/dist/webhook.admin.service.d.ts +22 -0
  52. package/dist/webhook.admin.service.d.ts.map +1 -0
  53. package/dist/webhook.admin.service.js +58 -0
  54. package/dist/webhook.admin.service.js.map +1 -0
  55. package/dist/webhook.circuit-breaker.d.ts +12 -0
  56. package/dist/webhook.circuit-breaker.d.ts.map +1 -0
  57. package/dist/webhook.circuit-breaker.js +58 -0
  58. package/dist/webhook.circuit-breaker.js.map +1 -0
  59. package/dist/webhook.constants.d.ts +17 -0
  60. package/dist/webhook.constants.d.ts.map +1 -0
  61. package/dist/webhook.constants.js +26 -0
  62. package/dist/webhook.constants.js.map +1 -0
  63. package/dist/webhook.delivery-admin.service.d.ts +9 -0
  64. package/dist/webhook.delivery-admin.service.d.ts.map +1 -0
  65. package/dist/webhook.delivery-admin.service.js +36 -0
  66. package/dist/webhook.delivery-admin.service.js.map +1 -0
  67. package/dist/webhook.delivery-worker.d.ts +23 -0
  68. package/dist/webhook.delivery-worker.d.ts.map +1 -0
  69. package/dist/webhook.delivery-worker.js +141 -0
  70. package/dist/webhook.delivery-worker.js.map +1 -0
  71. package/dist/webhook.dispatcher.d.ts +14 -0
  72. package/dist/webhook.dispatcher.d.ts.map +1 -0
  73. package/dist/webhook.dispatcher.js +54 -0
  74. package/dist/webhook.dispatcher.js.map +1 -0
  75. package/dist/webhook.endpoint-admin.service.d.ts +23 -0
  76. package/dist/webhook.endpoint-admin.service.d.ts.map +1 -0
  77. package/dist/webhook.endpoint-admin.service.js +97 -0
  78. package/dist/webhook.endpoint-admin.service.js.map +1 -0
  79. package/dist/webhook.event.d.ts +6 -0
  80. package/dist/webhook.event.d.ts.map +1 -0
  81. package/dist/webhook.event.js +22 -0
  82. package/dist/webhook.event.js.map +1 -0
  83. package/dist/webhook.module.d.ts +15 -0
  84. package/dist/webhook.module.d.ts.map +1 -0
  85. package/dist/webhook.module.js +176 -0
  86. package/dist/webhook.module.js.map +1 -0
  87. package/dist/webhook.retry-policy.d.ts +7 -0
  88. package/dist/webhook.retry-policy.d.ts.map +1 -0
  89. package/dist/webhook.retry-policy.js +38 -0
  90. package/dist/webhook.retry-policy.js.map +1 -0
  91. package/dist/webhook.service.d.ts +17 -0
  92. package/dist/webhook.service.d.ts.map +1 -0
  93. package/dist/webhook.service.js +63 -0
  94. package/dist/webhook.service.js.map +1 -0
  95. package/dist/webhook.signer.d.ts +12 -0
  96. package/dist/webhook.signer.d.ts.map +1 -0
  97. package/dist/webhook.signer.js +75 -0
  98. package/dist/webhook.signer.js.map +1 -0
  99. package/dist/webhook.url-validator.d.ts +3 -0
  100. package/dist/webhook.url-validator.d.ts.map +1 -0
  101. package/dist/webhook.url-validator.js +156 -0
  102. package/dist/webhook.url-validator.js.map +1 -0
  103. package/package.json +61 -0
  104. package/src/sql/create-webhook-tables.sql +72 -0
@@ -0,0 +1,156 @@
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 __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.validateWebhookUrl = validateWebhookUrl;
37
+ exports.resolveAndValidateHost = resolveAndValidateHost;
38
+ const net = __importStar(require("net"));
39
+ const dns = __importStar(require("dns"));
40
+ const BLOCKED_HOSTNAMES = new Set([
41
+ 'localhost',
42
+ 'localhost.localdomain',
43
+ 'ip6-localhost',
44
+ 'ip6-loopback',
45
+ ]);
46
+ async function validateWebhookUrl(url) {
47
+ let parsed;
48
+ try {
49
+ parsed = new URL(url);
50
+ }
51
+ catch {
52
+ throw new Error(`Invalid webhook URL: unable to parse "${url}"`);
53
+ }
54
+ if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
55
+ throw new Error(`Invalid webhook URL: scheme must be http or https, got "${parsed.protocol}"`);
56
+ }
57
+ const hostname = parsed.hostname.toLowerCase();
58
+ if (BLOCKED_HOSTNAMES.has(hostname)) {
59
+ throw new Error(`Invalid webhook URL: "${hostname}" is not allowed (loopback address)`);
60
+ }
61
+ if (net.isIPv4(hostname)) {
62
+ validateIPv4(hostname);
63
+ }
64
+ else if (net.isIPv6(hostname) || hostname.startsWith('[')) {
65
+ const cleanIp = hostname.replace(/^\[|\]$/g, '');
66
+ validateIPv6(cleanIp);
67
+ }
68
+ else {
69
+ // Hostname — resolve DNS and validate all resolved IPs
70
+ await resolveAndValidateHost(hostname);
71
+ }
72
+ }
73
+ function validateIPv4(ip) {
74
+ const parts = ip.split('.').map(Number);
75
+ if (parts[0] === 127) {
76
+ throw new Error(`Invalid webhook URL: "${ip}" is a loopback address`);
77
+ }
78
+ if (parts[0] === 10) {
79
+ throw new Error(`Invalid webhook URL: "${ip}" is a private address`);
80
+ }
81
+ if (parts[0] === 172 && parts[1] >= 16 && parts[1] <= 31) {
82
+ throw new Error(`Invalid webhook URL: "${ip}" is a private address`);
83
+ }
84
+ if (parts[0] === 192 && parts[1] === 168) {
85
+ throw new Error(`Invalid webhook URL: "${ip}" is a private address`);
86
+ }
87
+ if (parts[0] === 169 && parts[1] === 254) {
88
+ throw new Error(`Invalid webhook URL: "${ip}" is a link-local/metadata address`);
89
+ }
90
+ if (parts[0] === 0) {
91
+ throw new Error(`Invalid webhook URL: "${ip}" is not a valid target`);
92
+ }
93
+ }
94
+ function validateIPv6(ip) {
95
+ if (ip === '::1') {
96
+ throw new Error(`Invalid webhook URL: "${ip}" is a loopback address`);
97
+ }
98
+ const lowerIp = ip.toLowerCase();
99
+ // IPv4-mapped IPv6 — ::ffff:x.x.x.x or ::ffff:HHHH:HHHH (hex form)
100
+ if (lowerIp.startsWith('::ffff:') || lowerIp.startsWith('0:0:0:0:0:ffff:')) {
101
+ const suffix = lowerIp.replace(/^(::ffff:|0:0:0:0:0:ffff:)/, '');
102
+ let ipv4;
103
+ if (net.isIPv4(suffix)) {
104
+ // ::ffff:10.0.0.1 form
105
+ ipv4 = suffix;
106
+ }
107
+ else {
108
+ // ::ffff:a00:1 form (hex) — convert to IPv4
109
+ const hexParts = suffix.split(':');
110
+ if (hexParts.length === 2) {
111
+ const hi = parseInt(hexParts[0], 16);
112
+ const lo = parseInt(hexParts[1], 16);
113
+ ipv4 = `${(hi >> 8) & 0xff}.${hi & 0xff}.${(lo >> 8) & 0xff}.${lo & 0xff}`;
114
+ }
115
+ else {
116
+ return; // Unknown format, let it through (conservative)
117
+ }
118
+ }
119
+ validateIPv4(ipv4);
120
+ return;
121
+ }
122
+ const firstSegment = ip.split(':')[0].toLowerCase();
123
+ if (firstSegment.startsWith('fc') || firstSegment.startsWith('fd')) {
124
+ throw new Error(`Invalid webhook URL: "${ip}" is a private address`);
125
+ }
126
+ if (firstSegment.startsWith('fe8') || firstSegment.startsWith('fe9') ||
127
+ firstSegment.startsWith('fea') || firstSegment.startsWith('feb')) {
128
+ throw new Error(`Invalid webhook URL: "${ip}" is a link-local address`);
129
+ }
130
+ }
131
+ async function resolveAndValidateHost(hostname) {
132
+ let addresses = [];
133
+ try {
134
+ const ipv4 = await dns.promises.resolve4(hostname);
135
+ addresses = addresses.concat(ipv4);
136
+ }
137
+ catch {
138
+ // No A record — not an error yet
139
+ }
140
+ try {
141
+ const ipv6 = await dns.promises.resolve6(hostname);
142
+ addresses = addresses.concat(ipv6);
143
+ }
144
+ catch {
145
+ // No AAAA record — not an error yet
146
+ }
147
+ for (const ip of addresses) {
148
+ if (net.isIPv4(ip)) {
149
+ validateIPv4(ip); // throws if private
150
+ }
151
+ else if (net.isIPv6(ip)) {
152
+ validateIPv6(ip); // throws if private
153
+ }
154
+ }
155
+ }
156
+ //# sourceMappingURL=webhook.url-validator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhook.url-validator.js","sourceRoot":"","sources":["../src/webhook.url-validator.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "@nestarc/webhook",
3
+ "version": "0.2.0",
4
+ "description": "Outbound webhook delivery for NestJS — HMAC signing, exponential retry, circuit breaker, delivery logs, fan-out, Standard Webhooks compatible",
5
+ "author": "nestarc",
6
+ "license": "MIT",
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "files": [
10
+ "dist",
11
+ "src/sql"
12
+ ],
13
+ "scripts": {
14
+ "clean": "rm -rf dist",
15
+ "build": "tsc -p tsconfig.build.json",
16
+ "test": "jest",
17
+ "test:e2e": "jest --config jest.e2e.config.js --runInBand",
18
+ "test:cov": "jest --coverage --runInBand",
19
+ "lint": "tsc --noEmit",
20
+ "prisma:generate": "prisma generate --schema=test/e2e/schema.prisma",
21
+ "prepublishOnly": "npm run clean && npm run lint && npm test && npm run build"
22
+ },
23
+ "peerDependencies": {
24
+ "@nestjs/common": "^10.0.0 || ^11.0.0",
25
+ "@nestjs/core": "^10.0.0 || ^11.0.0",
26
+ "@nestjs/schedule": "^4.0.0 || ^5.0.0",
27
+ "@prisma/client": "^5.0.0 || ^6.0.0"
28
+ },
29
+ "devDependencies": {
30
+ "@nestjs/common": "^11.0.0",
31
+ "@nestjs/core": "^11.0.0",
32
+ "@nestjs/platform-express": "^11.0.0",
33
+ "@nestjs/schedule": "^5.0.0",
34
+ "@nestjs/testing": "^11.0.0",
35
+ "@prisma/client": "^6.0.0",
36
+ "@types/jest": "^29.5.0",
37
+ "@types/node": "^20.0.0",
38
+ "jest": "^29.7.0",
39
+ "prisma": "^6.19.3",
40
+ "reflect-metadata": "^0.2.0",
41
+ "rxjs": "^7.8.0",
42
+ "ts-jest": "^29.2.0",
43
+ "typescript": "^5.4.0"
44
+ },
45
+ "engines": {
46
+ "node": ">=20.0.0"
47
+ },
48
+ "repository": {
49
+ "type": "git",
50
+ "url": "https://github.com/nestarc/webhook.git"
51
+ },
52
+ "keywords": [
53
+ "nestjs",
54
+ "webhook",
55
+ "outbound",
56
+ "hmac",
57
+ "retry",
58
+ "circuit-breaker",
59
+ "standard-webhooks"
60
+ ]
61
+ }
@@ -0,0 +1,72 @@
1
+ -- @nestarc/webhook — PostgreSQL migration
2
+ -- Creates the three core tables for outbound webhook delivery.
3
+ -- Run this migration against your PostgreSQL database before using the module.
4
+
5
+ -- Required for gen_random_uuid() on PostgreSQL < 13
6
+ CREATE EXTENSION IF NOT EXISTS pgcrypto;
7
+
8
+ CREATE TABLE IF NOT EXISTS webhook_endpoints (
9
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
10
+ url VARCHAR(2048) NOT NULL,
11
+ secret VARCHAR(255) NOT NULL,
12
+ events VARCHAR(255)[] NOT NULL DEFAULT '{}',
13
+ active BOOLEAN NOT NULL DEFAULT TRUE,
14
+ description VARCHAR(500),
15
+ metadata JSONB,
16
+ tenant_id VARCHAR(255),
17
+
18
+ consecutive_failures INT NOT NULL DEFAULT 0,
19
+ disabled_at TIMESTAMPTZ,
20
+ disabled_reason VARCHAR(255),
21
+
22
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
23
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
24
+ );
25
+
26
+ CREATE INDEX IF NOT EXISTS idx_webhook_endpoints_tenant_active
27
+ ON webhook_endpoints (tenant_id, active);
28
+ CREATE INDEX IF NOT EXISTS idx_webhook_endpoints_active_events
29
+ ON webhook_endpoints (active, events);
30
+
31
+ ---
32
+
33
+ CREATE TABLE IF NOT EXISTS webhook_events (
34
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
35
+ event_type VARCHAR(255) NOT NULL,
36
+ payload JSONB NOT NULL,
37
+ tenant_id VARCHAR(255),
38
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
39
+ );
40
+
41
+ CREATE INDEX IF NOT EXISTS idx_webhook_events_type_created
42
+ ON webhook_events (event_type, created_at);
43
+
44
+ ---
45
+
46
+ CREATE TABLE IF NOT EXISTS webhook_deliveries (
47
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
48
+ event_id UUID NOT NULL REFERENCES webhook_events(id),
49
+ endpoint_id UUID NOT NULL REFERENCES webhook_endpoints(id),
50
+ status VARCHAR(20) NOT NULL DEFAULT 'PENDING',
51
+ attempts INT NOT NULL DEFAULT 0,
52
+ max_attempts INT NOT NULL DEFAULT 5,
53
+ next_attempt_at TIMESTAMPTZ,
54
+ claimed_at TIMESTAMPTZ,
55
+ last_attempt_at TIMESTAMPTZ,
56
+ completed_at TIMESTAMPTZ,
57
+
58
+ response_status INT,
59
+ response_body TEXT,
60
+ latency_ms INT,
61
+ last_error TEXT,
62
+
63
+ CONSTRAINT fk_delivery_event FOREIGN KEY (event_id) REFERENCES webhook_events(id),
64
+ CONSTRAINT fk_delivery_endpoint FOREIGN KEY (endpoint_id) REFERENCES webhook_endpoints(id)
65
+ );
66
+
67
+ CREATE INDEX IF NOT EXISTS idx_webhook_deliveries_status_next
68
+ ON webhook_deliveries (status, next_attempt_at);
69
+ CREATE INDEX IF NOT EXISTS idx_webhook_deliveries_endpoint_status
70
+ ON webhook_deliveries (endpoint_id, status);
71
+ CREATE INDEX IF NOT EXISTS idx_webhook_deliveries_event
72
+ ON webhook_deliveries (event_id);