@nestarc/webhook 0.12.1 → 0.13.1

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 (61) hide show
  1. package/README.md +154 -0
  2. package/dist/adapters/fetch-http-client.d.ts +2 -2
  3. package/dist/adapters/fetch-http-client.d.ts.map +1 -1
  4. package/dist/adapters/fetch-http-client.js +165 -29
  5. package/dist/adapters/fetch-http-client.js.map +1 -1
  6. package/dist/adapters/prisma-delivery.repository.d.ts +9 -3
  7. package/dist/adapters/prisma-delivery.repository.d.ts.map +1 -1
  8. package/dist/adapters/prisma-delivery.repository.js +228 -19
  9. package/dist/adapters/prisma-delivery.repository.js.map +1 -1
  10. package/dist/adapters/prisma-endpoint.repository.js +1 -1
  11. package/dist/adapters/prisma-event.repository.d.ts +3 -1
  12. package/dist/adapters/prisma-event.repository.d.ts.map +1 -1
  13. package/dist/adapters/prisma-event.repository.js +36 -2
  14. package/dist/adapters/prisma-event.repository.js.map +1 -1
  15. package/dist/index.d.ts +5 -4
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +3 -1
  18. package/dist/index.js.map +1 -1
  19. package/dist/interfaces/webhook-delivery.interface.d.ts +26 -0
  20. package/dist/interfaces/webhook-delivery.interface.d.ts.map +1 -1
  21. package/dist/interfaces/webhook-options.interface.d.ts +27 -0
  22. package/dist/interfaces/webhook-options.interface.d.ts.map +1 -1
  23. package/dist/ports/webhook-delivery.repository.d.ts +6 -2
  24. package/dist/ports/webhook-delivery.repository.d.ts.map +1 -1
  25. package/dist/ports/webhook-event.repository.d.ts +6 -0
  26. package/dist/ports/webhook-event.repository.d.ts.map +1 -1
  27. package/dist/ports/webhook-http-client.d.ts +8 -1
  28. package/dist/ports/webhook-http-client.d.ts.map +1 -1
  29. package/dist/webhook.admin.service.d.ts +4 -2
  30. package/dist/webhook.admin.service.d.ts.map +1 -1
  31. package/dist/webhook.admin.service.js +8 -2
  32. package/dist/webhook.admin.service.js.map +1 -1
  33. package/dist/webhook.delivery-admin.service.d.ts +4 -2
  34. package/dist/webhook.delivery-admin.service.d.ts.map +1 -1
  35. package/dist/webhook.delivery-admin.service.js +14 -2
  36. package/dist/webhook.delivery-admin.service.js.map +1 -1
  37. package/dist/webhook.dispatcher.d.ts.map +1 -1
  38. package/dist/webhook.dispatcher.js +5 -2
  39. package/dist/webhook.dispatcher.js.map +1 -1
  40. package/dist/webhook.module.d.ts.map +1 -1
  41. package/dist/webhook.module.js +6 -2
  42. package/dist/webhook.module.js.map +1 -1
  43. package/dist/webhook.retention-admin.service.d.ts +10 -0
  44. package/dist/webhook.retention-admin.service.d.ts.map +1 -0
  45. package/dist/webhook.retention-admin.service.js +50 -0
  46. package/dist/webhook.retention-admin.service.js.map +1 -0
  47. package/dist/webhook.service.d.ts +7 -4
  48. package/dist/webhook.service.d.ts.map +1 -1
  49. package/dist/webhook.service.js +40 -10
  50. package/dist/webhook.service.js.map +1 -1
  51. package/dist/webhook.signer.d.ts +5 -0
  52. package/dist/webhook.signer.d.ts.map +1 -1
  53. package/dist/webhook.signer.js +14 -0
  54. package/dist/webhook.signer.js.map +1 -1
  55. package/dist/webhook.url-validator.d.ts +1 -1
  56. package/dist/webhook.url-validator.d.ts.map +1 -1
  57. package/dist/webhook.url-validator.js +145 -51
  58. package/dist/webhook.url-validator.js.map +1 -1
  59. package/package.json +23 -12
  60. package/src/sql/create-webhook-tables.sql +11 -5
  61. package/src/sql/migrations/v0.13.0.sql +11 -0
@@ -1 +1 @@
1
- {"version":3,"file":"webhook.signer.js","sourceRoot":"","sources":["../src/webhook.signer.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,+CAAiC;AACjC,2CAA4C;AAUrC,IAAM,aAAa,GAAnB,MAAM,aAAa;IACxB,OAAO,CACL,OAAe,EACf,SAAiB,EACjB,IAAY,EACZ,OAAiB;QAEjB,MAAM,UAAU,GAAG,OAAO;aACvB,MAAM,CAAC,CAAC,MAAM,EAAoB,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;aACrD,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;QAEtE,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC5B,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;QAC7D,CAAC;QAED,OAAO;YACL,YAAY,EAAE,OAAO;YACrB,mBAAmB,EAAE,MAAM,CAAC,SAAS,CAAC;YACtC,mBAAmB,EAAE,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC;SAC1C,CAAC;IACJ,CAAC;IAED,IAAI,CACF,OAAe,EACf,SAAiB,EACjB,IAAY,EACZ,MAAc;QAEd,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;IAC1D,CAAC;IAED,MAAM,CACJ,OAAe,EACf,SAAiB,EACjB,IAAY,EACZ,MAAc,EACd,SAAiB;QAEjB,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;QACnE,MAAM,kBAAkB,GAAG,SAAS,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAEzE,OAAO,kBAAkB,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE;YAC3C,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAChC,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAEjC,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM;gBAAE,OAAO,KAAK,CAAC;YAExC,OAAO,MAAM,CAAC,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACtC,CAAC,CAAC,CAAC;IACL,CAAC;IAED,cAAc;QACZ,OAAO,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IACnD,CAAC;IAEO,UAAU,CAChB,OAAe,EACf,SAAiB,EACjB,IAAY,EACZ,MAAc;QAEd,MAAM,MAAM,GAAG,GAAG,OAAO,IAAI,SAAS,IAAI,IAAI,EAAE,CAAC;QACjD,MAAM,IAAI,GAAG,MAAM;aAChB,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;aACnD,MAAM,CAAC,MAAM,CAAC;aACd,MAAM,CAAC,QAAQ,CAAC,CAAC;QAEpB,OAAO,MAAM,IAAI,EAAE,CAAC;IACtB,CAAC;CACF,CAAA;AArEY,sCAAa;wBAAb,aAAa;IADzB,IAAA,mBAAU,GAAE;GACA,aAAa,CAqEzB"}
1
+ {"version":3,"file":"webhook.signer.js","sourceRoot":"","sources":["../src/webhook.signer.ts"],"names":[],"mappings":"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"}
@@ -7,5 +7,5 @@ export declare class WebhookUrlValidationError extends Error {
7
7
  constructor(message: string, reason: WebhookUrlValidationReason, url?: string, resolvedIp?: string);
8
8
  }
9
9
  export declare function validateWebhookUrl(url: string): Promise<void>;
10
- export declare function resolveAndValidateHost(hostname: string, url?: string): Promise<void>;
10
+ export declare function resolveAndValidateHost(hostname: string, url?: string): Promise<string[]>;
11
11
  //# sourceMappingURL=webhook.url-validator.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"webhook.url-validator.d.ts","sourceRoot":"","sources":["../src/webhook.url-validator.ts"],"names":[],"mappings":"AAUA,MAAM,MAAM,0BAA0B,GAClC,OAAO,GACP,QAAQ,GACR,kBAAkB,GAClB,UAAU,GACV,SAAS,GACT,YAAY,GACZ,gBAAgB,CAAC;AAErB,qBAAa,yBAA0B,SAAQ,KAAK;IAClD,QAAQ,CAAC,IAAI,+BAA+B;IAC5C,QAAQ,CAAC,MAAM,EAAE,0BAA0B,CAAC;IAC5C,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;gBAG3B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,0BAA0B,EAClC,GAAG,CAAC,EAAE,MAAM,EACZ,UAAU,CAAC,EAAE,MAAM;CAQtB;AAED,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAuCnE;AA+GD,wBAAsB,sBAAsB,CAC1C,QAAQ,EAAE,MAAM,EAChB,GAAG,CAAC,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC,CAwBf"}
1
+ {"version":3,"file":"webhook.url-validator.d.ts","sourceRoot":"","sources":["../src/webhook.url-validator.ts"],"names":[],"mappings":"AAiBA,MAAM,MAAM,0BAA0B,GAClC,OAAO,GACP,QAAQ,GACR,kBAAkB,GAClB,UAAU,GACV,SAAS,GACT,YAAY,GACZ,gBAAgB,CAAC;AAkCrB,qBAAa,yBAA0B,SAAQ,KAAK;IAClD,QAAQ,CAAC,IAAI,+BAA+B;IAC5C,QAAQ,CAAC,MAAM,EAAE,0BAA0B,CAAC;IAC5C,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;gBAG3B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,0BAA0B,EAClC,GAAG,CAAC,EAAE,MAAM,EACZ,UAAU,CAAC,EAAE,MAAM;CAQtB;AAED,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAuCnE;AAgDD,wBAAsB,sBAAsB,CAC1C,QAAQ,EAAE,MAAM,EAChB,GAAG,CAAC,EAAE,MAAM,GACX,OAAO,CAAC,MAAM,EAAE,CAAC,CAsDnB"}
@@ -44,6 +44,36 @@ const BLOCKED_HOSTNAMES = new Set([
44
44
  'ip6-localhost',
45
45
  'ip6-loopback',
46
46
  ]);
47
+ const BLOCKED_IPV4_RANGES = [
48
+ ipv4Range('0.0.0.0', 8, 'invalid_target', 'not a valid public target'),
49
+ ipv4Range('10.0.0.0', 8, 'private', 'a private address'),
50
+ ipv4Range('100.64.0.0', 10, 'private', 'a shared/private address'),
51
+ ipv4Range('127.0.0.0', 8, 'loopback', 'a loopback address'),
52
+ ipv4Range('169.254.0.0', 16, 'link_local', 'a link-local/metadata address'),
53
+ ipv4Range('172.16.0.0', 12, 'private', 'a private address'),
54
+ ipv4Range('192.0.0.0', 24, 'invalid_target', 'a special-use address'),
55
+ ipv4Range('192.0.2.0', 24, 'invalid_target', 'a documentation address'),
56
+ ipv4Range('192.88.99.0', 24, 'invalid_target', 'a special-use address'),
57
+ ipv4Range('192.168.0.0', 16, 'private', 'a private address'),
58
+ ipv4Range('198.18.0.0', 15, 'invalid_target', 'a benchmarking address'),
59
+ ipv4Range('198.51.100.0', 24, 'invalid_target', 'a documentation address'),
60
+ ipv4Range('203.0.113.0', 24, 'invalid_target', 'a documentation address'),
61
+ ipv4Range('224.0.0.0', 4, 'invalid_target', 'a multicast address'),
62
+ ipv4Range('240.0.0.0', 4, 'invalid_target', 'a reserved address'),
63
+ ];
64
+ const BLOCKED_IPV6_RANGES = [
65
+ ipv6Range('::', 128, 'invalid_target', 'not a valid public target'),
66
+ ipv6Range('::1', 128, 'loopback', 'a loopback address'),
67
+ ipv6Range('::', 96, 'invalid_target', 'a special-use address'),
68
+ ipv6Range('100::', 64, 'invalid_target', 'a discard-only address'),
69
+ ipv6Range('2001::', 23, 'invalid_target', 'a special-use address'),
70
+ ipv6Range('2001:db8::', 32, 'invalid_target', 'a documentation address'),
71
+ ipv6Range('2002::', 16, 'invalid_target', 'a special-use address'),
72
+ ipv6Range('3fff::', 20, 'invalid_target', 'a documentation address'),
73
+ ipv6Range('fc00::', 7, 'private', 'a private address'),
74
+ ipv6Range('fe80::', 10, 'link_local', 'a link-local address'),
75
+ ipv6Range('ff00::', 8, 'invalid_target', 'a multicast address'),
76
+ ];
47
77
  class WebhookUrlValidationError extends Error {
48
78
  name = 'WebhookUrlValidationError';
49
79
  reason;
@@ -86,79 +116,58 @@ async function validateWebhookUrl(url) {
86
116
  }
87
117
  }
88
118
  function validateIPv4(ip, url) {
89
- const parts = ip.split('.').map(Number);
90
- if (parts[0] === 127) {
91
- throw new WebhookUrlValidationError(`Invalid webhook URL: "${ip}" is a loopback address`, 'loopback', url, ip);
92
- }
93
- if (parts[0] === 10) {
94
- throw new WebhookUrlValidationError(`Invalid webhook URL: "${ip}" is a private address`, 'private', url, ip);
95
- }
96
- if (parts[0] === 172 && parts[1] >= 16 && parts[1] <= 31) {
97
- throw new WebhookUrlValidationError(`Invalid webhook URL: "${ip}" is a private address`, 'private', url, ip);
98
- }
99
- if (parts[0] === 192 && parts[1] === 168) {
100
- throw new WebhookUrlValidationError(`Invalid webhook URL: "${ip}" is a private address`, 'private', url, ip);
101
- }
102
- if (parts[0] === 169 && parts[1] === 254) {
103
- throw new WebhookUrlValidationError(`Invalid webhook URL: "${ip}" is a link-local/metadata address`, 'link_local', url, ip);
104
- }
105
- if (parts[0] === 0) {
106
- throw new WebhookUrlValidationError(`Invalid webhook URL: "${ip}" is not a valid target`, 'invalid_target', url, ip);
119
+ const value = ipv4ToNumber(ip);
120
+ const blocked = BLOCKED_IPV4_RANGES.find((range) => ipv4InRange(value, range));
121
+ if (blocked) {
122
+ throw new WebhookUrlValidationError(`Invalid webhook URL: "${ip}" is ${blocked.description}`, blocked.reason, url, ip);
107
123
  }
108
124
  }
109
125
  function validateIPv6(ip, url) {
110
- if (ip === '::1') {
111
- throw new WebhookUrlValidationError(`Invalid webhook URL: "${ip}" is a loopback address`, 'loopback', url, ip);
112
- }
113
126
  const lowerIp = ip.toLowerCase();
114
- // IPv4-mapped IPv6 — ::ffff:x.x.x.x or ::ffff:HHHH:HHHH (hex form)
115
- if (lowerIp.startsWith('::ffff:') || lowerIp.startsWith('0:0:0:0:0:ffff:')) {
116
- const suffix = lowerIp.replace(/^(::ffff:|0:0:0:0:0:ffff:)/, '');
117
- let ipv4;
118
- if (net.isIPv4(suffix)) {
119
- // ::ffff:10.0.0.1 form
120
- ipv4 = suffix;
121
- }
122
- else {
123
- // ::ffff:a00:1 form (hex) — convert to IPv4
124
- const hexParts = suffix.split(':');
125
- if (hexParts.length === 2) {
126
- const hi = parseInt(hexParts[0], 16);
127
- const lo = parseInt(hexParts[1], 16);
128
- ipv4 = `${(hi >> 8) & 0xff}.${hi & 0xff}.${(lo >> 8) & 0xff}.${lo & 0xff}`;
129
- }
130
- else {
131
- return; // Unknown format, let it through (conservative)
132
- }
133
- }
134
- validateIPv4(ipv4, url);
135
- return;
127
+ const parsed = parseIPv6ToBigInt(lowerIp);
128
+ if (parsed === null) {
129
+ throw new WebhookUrlValidationError(`Invalid webhook URL: "${ip}" is not a valid target`, 'invalid_target', url, ip);
136
130
  }
137
- const firstSegment = ip.split(':')[0].toLowerCase();
138
- if (firstSegment.startsWith('fc') || firstSegment.startsWith('fd')) {
139
- throw new WebhookUrlValidationError(`Invalid webhook URL: "${ip}" is a private address`, 'private', url, ip);
131
+ if (isIPv4MappedIPv6(parsed)) {
132
+ validateIPv4(numberToIPv4(Number(parsed & 0xffffffffn)), url);
133
+ return;
140
134
  }
141
- if (firstSegment.startsWith('fe8') || firstSegment.startsWith('fe9') ||
142
- firstSegment.startsWith('fea') || firstSegment.startsWith('feb')) {
143
- throw new WebhookUrlValidationError(`Invalid webhook URL: "${ip}" is a link-local address`, 'link_local', url, ip);
135
+ const blocked = BLOCKED_IPV6_RANGES.find((range) => ipv6InRange(parsed, range));
136
+ if (blocked) {
137
+ throw new WebhookUrlValidationError(`Invalid webhook URL: "${ip}" is ${blocked.description}`, blocked.reason, url, ip);
144
138
  }
145
139
  }
146
140
  async function resolveAndValidateHost(hostname, url) {
141
+ const cleanHostname = hostname.toLowerCase().replace(/^\[|\]$/g, '');
142
+ if (BLOCKED_HOSTNAMES.has(cleanHostname)) {
143
+ throw new WebhookUrlValidationError(`Invalid webhook URL: "${cleanHostname}" is not allowed (loopback address)`, 'blocked_hostname', url);
144
+ }
145
+ if (net.isIPv4(cleanHostname)) {
146
+ validateIPv4(cleanHostname, url);
147
+ return [cleanHostname];
148
+ }
149
+ if (net.isIPv6(cleanHostname)) {
150
+ validateIPv6(cleanHostname, url);
151
+ return [cleanHostname];
152
+ }
147
153
  let addresses = [];
148
154
  try {
149
- const ipv4 = await dns.promises.resolve4(hostname);
155
+ const ipv4 = await dns.promises.resolve4(cleanHostname);
150
156
  addresses = addresses.concat(ipv4);
151
157
  }
152
158
  catch {
153
159
  // No A record — not an error yet
154
160
  }
155
161
  try {
156
- const ipv6 = await dns.promises.resolve6(hostname);
162
+ const ipv6 = await dns.promises.resolve6(cleanHostname);
157
163
  addresses = addresses.concat(ipv6);
158
164
  }
159
165
  catch {
160
166
  // No AAAA record — not an error yet
161
167
  }
168
+ if (addresses.length === 0) {
169
+ throw new WebhookUrlValidationError(`Invalid webhook URL: hostname "${cleanHostname}" did not resolve to any address`, 'invalid_target', url);
170
+ }
162
171
  for (const ip of addresses) {
163
172
  if (net.isIPv4(ip)) {
164
173
  validateIPv4(ip, url); // throws if private
@@ -167,5 +176,90 @@ async function resolveAndValidateHost(hostname, url) {
167
176
  validateIPv6(ip, url); // throws if private
168
177
  }
169
178
  }
179
+ return addresses;
180
+ }
181
+ function ipv4Range(base, prefix, reason, description) {
182
+ return { base: ipv4ToNumber(base), prefix, reason, description };
183
+ }
184
+ function ipv6Range(base, prefix, reason, description) {
185
+ const parsed = parseIPv6ToBigInt(base);
186
+ if (parsed === null) {
187
+ throw new Error(`Invalid IPv6 range base: ${base}`);
188
+ }
189
+ return { base: parsed, prefix, reason, description };
190
+ }
191
+ function ipv4ToNumber(ip) {
192
+ return ip
193
+ .split('.')
194
+ .map(Number)
195
+ .reduce((value, part) => ((value << 8) + part) >>> 0, 0);
196
+ }
197
+ function numberToIPv4(value) {
198
+ return [
199
+ (value >>> 24) & 0xff,
200
+ (value >>> 16) & 0xff,
201
+ (value >>> 8) & 0xff,
202
+ value & 0xff,
203
+ ].join('.');
204
+ }
205
+ function ipv4InRange(value, range) {
206
+ const mask = range.prefix === 0
207
+ ? 0
208
+ : (0xffffffff << (32 - range.prefix)) >>> 0;
209
+ return (value & mask) >>> 0 === (range.base & mask) >>> 0;
210
+ }
211
+ function ipv6InRange(value, range) {
212
+ const shift = BigInt(128 - range.prefix);
213
+ return (value >> shift) === (range.base >> shift);
214
+ }
215
+ function parseIPv6ToBigInt(ip) {
216
+ let normalized = ip.replace(/^\[|\]$/g, '').toLowerCase();
217
+ const zoneIndex = normalized.indexOf('%');
218
+ if (zoneIndex !== -1) {
219
+ normalized = normalized.slice(0, zoneIndex);
220
+ }
221
+ if (normalized.includes('.')) {
222
+ const lastColon = normalized.lastIndexOf(':');
223
+ const ipv4 = normalized.slice(lastColon + 1);
224
+ if (!net.isIPv4(ipv4)) {
225
+ return null;
226
+ }
227
+ const value = ipv4ToNumber(ipv4);
228
+ const high = ((value >>> 16) & 0xffff).toString(16);
229
+ const low = (value & 0xffff).toString(16);
230
+ normalized = `${normalized.slice(0, lastColon)}:${high}:${low}`;
231
+ }
232
+ const compressed = normalized.includes('::');
233
+ const compressionParts = normalized.split('::');
234
+ if (compressionParts.length > 2) {
235
+ return null;
236
+ }
237
+ const left = compressionParts[0]
238
+ ? compressionParts[0].split(':').filter((part) => part.length > 0)
239
+ : [];
240
+ const right = compressionParts[1]
241
+ ? compressionParts[1].split(':').filter((part) => part.length > 0)
242
+ : [];
243
+ const missing = compressed ? 8 - left.length - right.length : 0;
244
+ if (missing < 0) {
245
+ return null;
246
+ }
247
+ const parts = compressed
248
+ ? [...left, ...Array(missing).fill('0'), ...right]
249
+ : left;
250
+ if (parts.length !== 8) {
251
+ return null;
252
+ }
253
+ let value = 0n;
254
+ for (const part of parts) {
255
+ if (!/^[0-9a-f]{1,4}$/.test(part)) {
256
+ return null;
257
+ }
258
+ value = (value << 16n) + BigInt(parseInt(part, 16));
259
+ }
260
+ return value;
261
+ }
262
+ function isIPv4MappedIPv6(value) {
263
+ return (value >> 32n) === 0xffffn;
170
264
  }
171
265
  //# sourceMappingURL=webhook.url-validator.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"webhook.url-validator.js","sourceRoot":"","sources":["../src/webhook.url-validator.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"webhook.url-validator.js","sourceRoot":"","sources":["../src/webhook.url-validator.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nestarc/webhook",
3
- "version": "0.12.1",
3
+ "version": "0.13.1",
4
4
  "description": "Outbound webhook delivery for NestJS — HMAC signing, exponential retry, circuit breaker, delivery logs, fan-out, Standard Webhooks compatible",
5
5
  "author": "nestarc",
6
6
  "license": "MIT",
@@ -16,32 +16,43 @@
16
16
  "test": "jest",
17
17
  "test:e2e": "jest --config jest.e2e.config.js --runInBand",
18
18
  "test:cov": "jest --coverage --runInBand",
19
+ "test:consumer:modern": "npm run build && node scripts/verify-modern-consumer.mjs",
19
20
  "lint": "tsc --noEmit",
20
- "prisma:generate": "prisma generate --schema=test/e2e/schema.prisma",
21
+ "prisma:generate": "prisma generate",
22
+ "prisma:generate:legacy": "prisma generate --schema=test/e2e/schema-v6.prisma",
21
23
  "prepublishOnly": "npm run clean && npm run lint && npm test && npm run build"
22
24
  },
23
25
  "peerDependencies": {
24
26
  "@nestjs/common": "^10.0.0 || ^11.0.0",
25
27
  "@nestjs/core": "^10.0.0 || ^11.0.0",
26
28
  "@nestjs/schedule": "^4.0.0 || ^5.0.0",
27
- "@prisma/client": "^5.0.0 || ^6.0.0"
29
+ "@prisma/client": "^5.0.0 || ^6.0.0 || ^7.0.0"
28
30
  },
29
31
  "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",
32
+ "@nestjs/common": "11.2.1",
33
+ "@nestjs/core": "11.2.1",
34
+ "@nestjs/platform-express": "11.2.1",
35
+ "@nestjs/schedule": "5.0.1",
36
+ "@nestjs/testing": "11.2.1",
37
+ "@prisma/adapter-pg": "7.10.0",
38
+ "@prisma/client": "7.10.0",
36
39
  "@types/jest": "^30.0.0",
37
40
  "@types/node": "^25.6.0",
38
- "jest": "^30.3.0",
39
- "prisma": "^6.19.3",
41
+ "@types/pg": "8.23.1",
42
+ "jest": "^30.4.2",
43
+ "pg": "8.23.0",
44
+ "prisma": "7.10.0",
40
45
  "reflect-metadata": "^0.2.0",
41
46
  "rxjs": "^7.8.0",
42
- "ts-jest": "^29.2.0",
47
+ "ts-jest": "^29.4.11",
43
48
  "typescript": "^5.4.0"
44
49
  },
50
+ "overrides": {
51
+ "@babel/core": "^7.29.7",
52
+ "js-yaml": "^4.2.0",
53
+ "multer": "^2.2.0",
54
+ "qs": "^6.15.2"
55
+ },
45
56
  "engines": {
46
57
  "node": ">=20.0.0"
47
58
  },
@@ -33,15 +33,21 @@ CREATE INDEX IF NOT EXISTS idx_webhook_endpoints_active_events
33
33
  ---
34
34
 
35
35
  CREATE TABLE IF NOT EXISTS webhook_events (
36
- id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
37
- event_type VARCHAR(255) NOT NULL,
38
- payload JSONB NOT NULL,
39
- tenant_id VARCHAR(255),
40
- created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
36
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
37
+ event_type VARCHAR(255) NOT NULL,
38
+ payload JSONB NOT NULL,
39
+ tenant_id VARCHAR(255),
40
+ idempotency_key VARCHAR(255),
41
+ correlation_id VARCHAR(255),
42
+ payload_purged_at TIMESTAMPTZ,
43
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
41
44
  );
42
45
 
43
46
  CREATE INDEX IF NOT EXISTS idx_webhook_events_type_created
44
47
  ON webhook_events (event_type, created_at);
48
+ CREATE UNIQUE INDEX IF NOT EXISTS webhook_events_idempotency_key_idx
49
+ ON webhook_events (COALESCE(tenant_id, ''), event_type, idempotency_key)
50
+ WHERE idempotency_key IS NOT NULL;
45
51
 
46
52
  ---
47
53
 
@@ -0,0 +1,11 @@
1
+ -- @nestarc/webhook v0.13.0 - idempotent publish and retention metadata
2
+ -- Adds producer idempotency fields, correlation diagnostics, and payload purge metadata.
3
+
4
+ ALTER TABLE webhook_events
5
+ ADD COLUMN IF NOT EXISTS idempotency_key VARCHAR(255),
6
+ ADD COLUMN IF NOT EXISTS correlation_id VARCHAR(255),
7
+ ADD COLUMN IF NOT EXISTS payload_purged_at TIMESTAMPTZ;
8
+
9
+ CREATE UNIQUE INDEX IF NOT EXISTS webhook_events_idempotency_key_idx
10
+ ON webhook_events (COALESCE(tenant_id, ''), event_type, idempotency_key)
11
+ WHERE idempotency_key IS NOT NULL;