autotel-subscribers 4.0.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 (87) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +669 -0
  3. package/dist/amplitude.cjs +2486 -0
  4. package/dist/amplitude.cjs.map +1 -0
  5. package/dist/amplitude.d.cts +49 -0
  6. package/dist/amplitude.d.ts +49 -0
  7. package/dist/amplitude.js +2463 -0
  8. package/dist/amplitude.js.map +1 -0
  9. package/dist/event-subscriber-base-CnF3V56W.d.cts +182 -0
  10. package/dist/event-subscriber-base-CnF3V56W.d.ts +182 -0
  11. package/dist/factories.cjs +16660 -0
  12. package/dist/factories.cjs.map +1 -0
  13. package/dist/factories.d.cts +304 -0
  14. package/dist/factories.d.ts +304 -0
  15. package/dist/factories.js +16624 -0
  16. package/dist/factories.js.map +1 -0
  17. package/dist/index.cjs +16575 -0
  18. package/dist/index.cjs.map +1 -0
  19. package/dist/index.d.cts +179 -0
  20. package/dist/index.d.ts +179 -0
  21. package/dist/index.js +16539 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/middleware.cjs +220 -0
  24. package/dist/middleware.cjs.map +1 -0
  25. package/dist/middleware.d.cts +227 -0
  26. package/dist/middleware.d.ts +227 -0
  27. package/dist/middleware.js +208 -0
  28. package/dist/middleware.js.map +1 -0
  29. package/dist/mixpanel.cjs +2940 -0
  30. package/dist/mixpanel.cjs.map +1 -0
  31. package/dist/mixpanel.d.cts +47 -0
  32. package/dist/mixpanel.d.ts +47 -0
  33. package/dist/mixpanel.js +2932 -0
  34. package/dist/mixpanel.js.map +1 -0
  35. package/dist/posthog.cjs +4115 -0
  36. package/dist/posthog.cjs.map +1 -0
  37. package/dist/posthog.d.cts +299 -0
  38. package/dist/posthog.d.ts +299 -0
  39. package/dist/posthog.js +4113 -0
  40. package/dist/posthog.js.map +1 -0
  41. package/dist/segment.cjs +6822 -0
  42. package/dist/segment.cjs.map +1 -0
  43. package/dist/segment.d.cts +49 -0
  44. package/dist/segment.d.ts +49 -0
  45. package/dist/segment.js +6794 -0
  46. package/dist/segment.js.map +1 -0
  47. package/dist/slack.cjs +368 -0
  48. package/dist/slack.cjs.map +1 -0
  49. package/dist/slack.d.cts +126 -0
  50. package/dist/slack.d.ts +126 -0
  51. package/dist/slack.js +366 -0
  52. package/dist/slack.js.map +1 -0
  53. package/dist/webhook.cjs +100 -0
  54. package/dist/webhook.cjs.map +1 -0
  55. package/dist/webhook.d.cts +53 -0
  56. package/dist/webhook.d.ts +53 -0
  57. package/dist/webhook.js +98 -0
  58. package/dist/webhook.js.map +1 -0
  59. package/examples/quickstart-custom-subscriber.ts +144 -0
  60. package/examples/subscriber-bigquery.ts +219 -0
  61. package/examples/subscriber-databricks.ts +280 -0
  62. package/examples/subscriber-kafka.ts +326 -0
  63. package/examples/subscriber-kinesis.ts +307 -0
  64. package/examples/subscriber-posthog.ts +421 -0
  65. package/examples/subscriber-pubsub.ts +336 -0
  66. package/examples/subscriber-snowflake.ts +232 -0
  67. package/package.json +141 -0
  68. package/src/amplitude.test.ts +231 -0
  69. package/src/amplitude.ts +148 -0
  70. package/src/event-subscriber-base.ts +325 -0
  71. package/src/factories.ts +197 -0
  72. package/src/index.ts +50 -0
  73. package/src/middleware.ts +489 -0
  74. package/src/mixpanel.test.ts +194 -0
  75. package/src/mixpanel.ts +134 -0
  76. package/src/mock-event-subscriber.ts +333 -0
  77. package/src/posthog.test.ts +629 -0
  78. package/src/posthog.ts +530 -0
  79. package/src/segment.test.ts +228 -0
  80. package/src/segment.ts +148 -0
  81. package/src/slack.ts +383 -0
  82. package/src/streaming-event-subscriber.ts +323 -0
  83. package/src/testing/index.ts +37 -0
  84. package/src/testing/mock-webhook-server.ts +242 -0
  85. package/src/testing/subscriber-test-harness.ts +365 -0
  86. package/src/webhook.test.ts +264 -0
  87. package/src/webhook.ts +158 -0
@@ -0,0 +1,220 @@
1
+ 'use strict';
2
+
3
+ // src/middleware.ts
4
+ function applyMiddleware(subscriber, middlewares) {
5
+ const trackEvent = async (event) => {
6
+ switch (event.type) {
7
+ case "event": {
8
+ await subscriber.trackEvent(event.name, event.attributes);
9
+ break;
10
+ }
11
+ case "funnel": {
12
+ await subscriber.trackFunnelStep(event.funnel, event.step, event.attributes);
13
+ break;
14
+ }
15
+ case "outcome": {
16
+ await subscriber.trackOutcome(event.operation, event.outcome, event.attributes);
17
+ break;
18
+ }
19
+ case "value": {
20
+ await subscriber.trackValue(event.name, event.value, event.attributes);
21
+ break;
22
+ }
23
+ }
24
+ };
25
+ const reversedMiddlewares = middlewares.toReversed();
26
+ let chain = trackEvent;
27
+ for (const middleware of reversedMiddlewares) {
28
+ const next = chain;
29
+ chain = (event) => middleware(event, next);
30
+ }
31
+ return {
32
+ name: `${subscriber.name}(middleware)`,
33
+ version: subscriber.version,
34
+ async trackEvent(name, attributes) {
35
+ await chain({ type: "event", name, attributes });
36
+ },
37
+ async trackFunnelStep(funnel, step, attributes) {
38
+ await chain({ type: "funnel", funnel, step, attributes });
39
+ },
40
+ async trackOutcome(operation, outcome, attributes) {
41
+ await chain({ type: "outcome", operation, outcome, attributes });
42
+ },
43
+ async trackValue(name, value, attributes) {
44
+ await chain({ type: "value", name, value, attributes });
45
+ },
46
+ async shutdown() {
47
+ await subscriber.shutdown?.();
48
+ }
49
+ };
50
+ }
51
+ function retryMiddleware(options) {
52
+ const { maxRetries = 3, delayMs = 1e3, onRetry } = options;
53
+ return async (event, next) => {
54
+ let lastError;
55
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
56
+ try {
57
+ await next(event);
58
+ return;
59
+ } catch (error) {
60
+ lastError = error;
61
+ if (attempt < maxRetries) {
62
+ onRetry?.(attempt, lastError);
63
+ await new Promise((resolve) => setTimeout(resolve, delayMs * Math.pow(2, attempt - 1)));
64
+ }
65
+ }
66
+ }
67
+ throw lastError;
68
+ };
69
+ }
70
+ function samplingMiddleware(rate) {
71
+ if (rate < 0 || rate > 1) {
72
+ throw new Error("Sample rate must be between 0 and 1");
73
+ }
74
+ return async (event, next) => {
75
+ if (Math.random() < rate) {
76
+ await next(event);
77
+ }
78
+ };
79
+ }
80
+ function enrichmentMiddleware(enricher) {
81
+ return async (event, next) => {
82
+ const enriched = enricher(event);
83
+ await next(enriched);
84
+ };
85
+ }
86
+ function loggingMiddleware(options = {}) {
87
+ const { prefix = "[Events]", logAttributes = false } = options;
88
+ return async (event, next) => {
89
+ if (logAttributes) {
90
+ console.log(prefix, event.type, event);
91
+ } else {
92
+ const eventName = "name" in event ? event.name : `${event.funnel || event.operation}`;
93
+ console.log(prefix, event.type, eventName);
94
+ }
95
+ await next(event);
96
+ };
97
+ }
98
+ function filterMiddleware(predicate) {
99
+ return async (event, next) => {
100
+ if (predicate(event)) {
101
+ await next(event);
102
+ }
103
+ };
104
+ }
105
+ function transformMiddleware(transformer) {
106
+ return async (event, next) => {
107
+ const transformed = transformer(event);
108
+ await next(transformed);
109
+ };
110
+ }
111
+ function batchingMiddleware(options) {
112
+ const { batchSize = 100, flushInterval = 5e3 } = options;
113
+ const buffer = [];
114
+ let flushTimer = null;
115
+ const flush = async () => {
116
+ const batch = [...buffer];
117
+ buffer.length = 0;
118
+ await Promise.all(batch.map(({ event, next }) => next(event)));
119
+ };
120
+ const scheduleFlush = () => {
121
+ if (flushTimer) return;
122
+ flushTimer = setTimeout(() => {
123
+ flush().catch(console.error);
124
+ flushTimer = null;
125
+ }, flushInterval);
126
+ };
127
+ return async (event, next) => {
128
+ buffer.push({ event, next });
129
+ if (buffer.length >= batchSize) {
130
+ await flush();
131
+ } else {
132
+ scheduleFlush();
133
+ }
134
+ };
135
+ }
136
+ function rateLimitMiddleware(options) {
137
+ const { requestsPerSecond } = options;
138
+ const intervalMs = 1e3 / requestsPerSecond;
139
+ let lastCallTime = 0;
140
+ const queue = [];
141
+ let processing = false;
142
+ const processQueue = async () => {
143
+ if (processing) return;
144
+ processing = true;
145
+ while (queue.length > 0) {
146
+ const now = Date.now();
147
+ const timeSinceLastCall = now - lastCallTime;
148
+ if (timeSinceLastCall < intervalMs) {
149
+ await new Promise((resolve) => setTimeout(resolve, intervalMs - timeSinceLastCall));
150
+ }
151
+ const fn = queue.shift();
152
+ if (fn) {
153
+ lastCallTime = Date.now();
154
+ fn();
155
+ }
156
+ }
157
+ processing = false;
158
+ };
159
+ return async (event, next) => {
160
+ return new Promise((resolve, reject) => {
161
+ queue.push(() => {
162
+ next(event).then(resolve).catch(reject);
163
+ });
164
+ processQueue().catch(reject);
165
+ });
166
+ };
167
+ }
168
+ function circuitBreakerMiddleware(options) {
169
+ const { failureThreshold = 5, timeout = 6e4, onOpen, onClose } = options;
170
+ let failureCount = 0;
171
+ let lastFailureTime = 0;
172
+ let circuitOpen = false;
173
+ return async (event, next) => {
174
+ if (circuitOpen) {
175
+ const now = Date.now();
176
+ if (now - lastFailureTime > timeout) {
177
+ circuitOpen = false;
178
+ failureCount = 0;
179
+ onClose?.();
180
+ } else {
181
+ throw new Error("Circuit breaker is open");
182
+ }
183
+ }
184
+ try {
185
+ await next(event);
186
+ failureCount = 0;
187
+ } catch (error) {
188
+ failureCount++;
189
+ lastFailureTime = Date.now();
190
+ if (failureCount >= failureThreshold) {
191
+ circuitOpen = true;
192
+ onOpen?.();
193
+ }
194
+ throw error;
195
+ }
196
+ };
197
+ }
198
+ function timeoutMiddleware(options) {
199
+ const { timeoutMs } = options;
200
+ return async (event, next) => {
201
+ const timeoutPromise = new Promise((_, reject) => {
202
+ setTimeout(() => reject(new Error(`Timeout after ${timeoutMs}ms`)), timeoutMs);
203
+ });
204
+ await Promise.race([next(event), timeoutPromise]);
205
+ };
206
+ }
207
+
208
+ exports.applyMiddleware = applyMiddleware;
209
+ exports.batchingMiddleware = batchingMiddleware;
210
+ exports.circuitBreakerMiddleware = circuitBreakerMiddleware;
211
+ exports.enrichmentMiddleware = enrichmentMiddleware;
212
+ exports.filterMiddleware = filterMiddleware;
213
+ exports.loggingMiddleware = loggingMiddleware;
214
+ exports.rateLimitMiddleware = rateLimitMiddleware;
215
+ exports.retryMiddleware = retryMiddleware;
216
+ exports.samplingMiddleware = samplingMiddleware;
217
+ exports.timeoutMiddleware = timeoutMiddleware;
218
+ exports.transformMiddleware = transformMiddleware;
219
+ //# sourceMappingURL=middleware.cjs.map
220
+ //# sourceMappingURL=middleware.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/middleware.ts"],"names":[],"mappings":";;;AAiFO,SAAS,eAAA,CACd,YACA,WAAA,EACiB;AAEjB,EAAA,MAAM,UAAA,GAAa,OAAO,KAAA,KAAsC;AAC9D,IAAA,QAAQ,MAAM,IAAA;AAAM,MAClB,KAAK,OAAA,EAAS;AACZ,QAAA,MAAM,UAAA,CAAW,UAAA,CAAW,KAAA,CAAM,IAAA,EAAM,MAAM,UAAU,CAAA;AACxD,QAAA;AAAA,MACF;AAAA,MACA,KAAK,QAAA,EAAU;AACb,QAAA,MAAM,WAAW,eAAA,CAAgB,KAAA,CAAM,QAAQ,KAAA,CAAM,IAAA,EAAM,MAAM,UAAU,CAAA;AAC3E,QAAA;AAAA,MACF;AAAA,MACA,KAAK,SAAA,EAAW;AACd,QAAA,MAAM,WAAW,YAAA,CAAa,KAAA,CAAM,WAAW,KAAA,CAAM,OAAA,EAAS,MAAM,UAAU,CAAA;AAC9E,QAAA;AAAA,MACF;AAAA,MACA,KAAK,OAAA,EAAS;AACZ,QAAA,MAAM,WAAW,UAAA,CAAW,KAAA,CAAM,MAAM,KAAA,CAAM,KAAA,EAAO,MAAM,UAAU,CAAA;AACrE,QAAA;AAAA,MACF;AAAA;AACF,EACF,CAAA;AAIA,EAAA,MAAM,mBAAA,GAAsB,YAAY,UAAA,EAAW;AACnD,EAAA,IAAI,KAAA,GAAuB,UAAA;AAC3B,EAAA,KAAA,MAAW,cAAc,mBAAA,EAAqB;AAC5C,IAAA,MAAM,IAAA,GAAO,KAAA;AACb,IAAA,KAAA,GAAQ,CAAC,KAAA,KAAuB,UAAA,CAAW,KAAA,EAAO,IAAI,CAAA;AAAA,EACxD;AAGA,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,CAAA,EAAG,UAAA,CAAW,IAAI,CAAA,YAAA,CAAA;AAAA,IACxB,SAAS,UAAA,CAAW,OAAA;AAAA,IAEpB,MAAM,UAAA,CAAW,IAAA,EAAc,UAAA,EAA6C;AAC1E,MAAA,MAAM,MAAM,EAAE,IAAA,EAAM,OAAA,EAAS,IAAA,EAAM,YAAY,CAAA;AAAA,IACjD,CAAA;AAAA,IAEA,MAAM,eAAA,CAAgB,MAAA,EAAgB,IAAA,EAAoB,UAAA,EAA6C;AACrG,MAAA,MAAM,MAAM,EAAE,IAAA,EAAM,UAAU,MAAA,EAAQ,IAAA,EAAM,YAAY,CAAA;AAAA,IAC1D,CAAA;AAAA,IAEA,MAAM,YAAA,CAAa,SAAA,EAAmB,OAAA,EAAwB,UAAA,EAA6C;AACzG,MAAA,MAAM,MAAM,EAAE,IAAA,EAAM,WAAW,SAAA,EAAW,OAAA,EAAS,YAAY,CAAA;AAAA,IACjE,CAAA;AAAA,IAEA,MAAM,UAAA,CAAW,IAAA,EAAc,KAAA,EAAe,UAAA,EAA6C;AACzF,MAAA,MAAM,MAAM,EAAE,IAAA,EAAM,SAAS,IAAA,EAAM,KAAA,EAAO,YAAY,CAAA;AAAA,IACxD,CAAA;AAAA,IAEA,MAAM,QAAA,GAA0B;AAC9B,MAAA,MAAM,WAAW,QAAA,IAAW;AAAA,IAC9B;AAAA,GACF;AACF;AAgBO,SAAS,gBAAgB,OAAA,EAIP;AACvB,EAAA,MAAM,EAAE,UAAA,GAAa,CAAA,EAAG,OAAA,GAAU,GAAA,EAAM,SAAQ,GAAI,OAAA;AAEpD,EAAA,OAAO,OAAO,OAAO,IAAA,KAAS;AAC5B,IAAA,IAAI,SAAA;AAEJ,IAAA,KAAA,IAAS,OAAA,GAAU,CAAA,EAAG,OAAA,IAAW,UAAA,EAAY,OAAA,EAAA,EAAW;AACtD,MAAA,IAAI;AACF,QAAA,MAAM,KAAK,KAAK,CAAA;AAChB,QAAA;AAAA,MACF,SAAS,KAAA,EAAO;AACd,QAAA,SAAA,GAAY,KAAA;AAEZ,QAAA,IAAI,UAAU,UAAA,EAAY;AACxB,UAAA,OAAA,GAAU,SAAS,SAAS,CAAA;AAE5B,UAAA,MAAM,IAAI,OAAA,CAAQ,CAAC,OAAA,KAAY,UAAA,CAAW,OAAA,EAAS,OAAA,GAAU,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,OAAA,GAAU,CAAC,CAAC,CAAC,CAAA;AAAA,QACxF;AAAA,MACF;AAAA,IACF;AAEA,IAAA,MAAM,SAAA;AAAA,EACR,CAAA;AACF;AAaO,SAAS,mBAAmB,IAAA,EAAoC;AACrE,EAAA,IAAI,IAAA,GAAO,CAAA,IAAK,IAAA,GAAO,CAAA,EAAG;AACxB,IAAA,MAAM,IAAI,MAAM,qCAAqC,CAAA;AAAA,EACvD;AAEA,EAAA,OAAO,OAAO,OAAO,IAAA,KAAS;AAC5B,IAAA,IAAI,IAAA,CAAK,MAAA,EAAO,GAAI,IAAA,EAAM;AACxB,MAAA,MAAM,KAAK,KAAK,CAAA;AAAA,IAClB;AAAA,EAEF,CAAA;AACF;AAmBO,SAAS,qBACd,QAAA,EACsB;AACtB,EAAA,OAAO,OAAO,OAAO,IAAA,KAAS;AAC5B,IAAA,MAAM,QAAA,GAAW,SAAS,KAAK,CAAA;AAC/B,IAAA,MAAM,KAAK,QAAQ,CAAA;AAAA,EACrB,CAAA;AACF;AAYO,SAAS,iBAAA,CAAkB,OAAA,GAG9B,EAAC,EAAyB;AAC5B,EAAA,MAAM,EAAE,MAAA,GAAS,UAAA,EAAY,aAAA,GAAgB,OAAM,GAAI,OAAA;AAEvD,EAAA,OAAO,OAAO,OAAO,IAAA,KAAS;AAC5B,IAAA,IAAI,aAAA,EAAe;AACjB,MAAA,OAAA,CAAQ,GAAA,CAAI,MAAA,EAAQ,KAAA,CAAM,IAAA,EAAM,KAAK,CAAA;AAAA,IACvC,CAAA,MAAO;AAEL,MAAA,MAAM,SAAA,GAAY,UAAU,KAAA,GAAQ,KAAA,CAAM,OAAO,CAAA,EAAI,KAAA,CAAc,MAAA,IAAW,KAAA,CAAc,SAAS,CAAA,CAAA;AACrG,MAAA,OAAA,CAAQ,GAAA,CAAI,MAAA,EAAQ,KAAA,CAAM,IAAA,EAAM,SAAS,CAAA;AAAA,IAC3C;AAEA,IAAA,MAAM,KAAK,KAAK,CAAA;AAAA,EAClB,CAAA;AACF;AAeO,SAAS,iBACd,SAAA,EACsB;AACtB,EAAA,OAAO,OAAO,OAAO,IAAA,KAAS;AAC5B,IAAA,IAAI,SAAA,CAAU,KAAK,CAAA,EAAG;AACpB,MAAA,MAAM,KAAK,KAAK,CAAA;AAAA,IAClB;AAAA,EACF,CAAA;AACF;AAkBO,SAAS,oBACd,WAAA,EACsB;AACtB,EAAA,OAAO,OAAO,OAAO,IAAA,KAAS;AAC5B,IAAA,MAAM,WAAA,GAAc,YAAY,KAAK,CAAA;AACrC,IAAA,MAAM,KAAK,WAAW,CAAA;AAAA,EACxB,CAAA;AACF;AAYO,SAAS,mBAAmB,OAAA,EAGV;AACvB,EAAA,MAAM,EAAE,SAAA,GAAY,GAAA,EAAK,aAAA,GAAgB,KAAK,GAAI,OAAA;AAClD,EAAA,MAAM,SAAqF,EAAC;AAC5F,EAAA,IAAI,UAAA,GAAoC,IAAA;AAExC,EAAA,MAAM,QAAQ,YAAY;AACxB,IAAA,MAAM,KAAA,GAAQ,CAAC,GAAG,MAAM,CAAA;AACxB,IAAA,MAAA,CAAO,MAAA,GAAS,CAAA;AAEhB,IAAA,MAAM,OAAA,CAAQ,GAAA,CAAI,KAAA,CAAM,GAAA,CAAI,CAAC,EAAE,KAAA,EAAO,IAAA,EAAK,KAAM,IAAA,CAAK,KAAK,CAAC,CAAC,CAAA;AAAA,EAC/D,CAAA;AAEA,EAAA,MAAM,gBAAgB,MAAM;AAC1B,IAAA,IAAI,UAAA,EAAY;AAChB,IAAA,UAAA,GAAa,WAAW,MAAM;AAC5B,MAAA,KAAA,EAAM,CAAE,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA;AAC3B,MAAA,UAAA,GAAa,IAAA;AAAA,IACf,GAAG,aAAa,CAAA;AAAA,EAClB,CAAA;AAEA,EAAA,OAAO,OAAO,OAAO,IAAA,KAAS;AAC5B,IAAA,MAAA,CAAO,IAAA,CAAK,EAAE,KAAA,EAAO,IAAA,EAAM,CAAA;AAE3B,IAAA,IAAI,MAAA,CAAO,UAAU,SAAA,EAAW;AAC9B,MAAA,MAAM,KAAA,EAAM;AAAA,IACd,CAAA,MAAO;AACL,MAAA,aAAA,EAAc;AAAA,IAChB;AAAA,EACF,CAAA;AACF;AAaO,SAAS,oBAAoB,OAAA,EAEX;AACvB,EAAA,MAAM,EAAE,mBAAkB,GAAI,OAAA;AAC9B,EAAA,MAAM,aAAa,GAAA,GAAO,iBAAA;AAC1B,EAAA,IAAI,YAAA,GAAe,CAAA;AACnB,EAAA,MAAM,QAA2B,EAAC;AAClC,EAAA,IAAI,UAAA,GAAa,KAAA;AAEjB,EAAA,MAAM,eAAe,YAAY;AAC/B,IAAA,IAAI,UAAA,EAAY;AAChB,IAAA,UAAA,GAAa,IAAA;AAEb,IAAA,OAAO,KAAA,CAAM,SAAS,CAAA,EAAG;AACvB,MAAA,MAAM,GAAA,GAAM,KAAK,GAAA,EAAI;AACrB,MAAA,MAAM,oBAAoB,GAAA,GAAM,YAAA;AAEhC,MAAA,IAAI,oBAAoB,UAAA,EAAY;AAClC,QAAA,MAAM,IAAI,QAAQ,CAAC,OAAA,KAAY,WAAW,OAAA,EAAS,UAAA,GAAa,iBAAiB,CAAC,CAAA;AAAA,MACpF;AAEA,MAAA,MAAM,EAAA,GAAK,MAAM,KAAA,EAAM;AACvB,MAAA,IAAI,EAAA,EAAI;AACN,QAAA,YAAA,GAAe,KAAK,GAAA,EAAI;AACxB,QAAA,EAAA,EAAG;AAAA,MACL;AAAA,IACF;AAEA,IAAA,UAAA,GAAa,KAAA;AAAA,EACf,CAAA;AAEA,EAAA,OAAO,OAAO,OAAO,IAAA,KAAS;AAC5B,IAAA,OAAO,IAAI,OAAA,CAAc,CAAC,OAAA,EAAS,MAAA,KAAW;AAC5C,MAAA,KAAA,CAAM,KAAK,MAAM;AACf,QAAA,IAAA,CAAK,KAAK,CAAA,CAAE,IAAA,CAAK,OAAO,CAAA,CAAE,MAAM,MAAM,CAAA;AAAA,MACxC,CAAC,CAAA;AACD,MAAA,YAAA,EAAa,CAAE,MAAM,MAAM,CAAA;AAAA,IAC7B,CAAC,CAAA;AAAA,EACH,CAAA;AACF;AAiBO,SAAS,yBAAyB,OAAA,EAKhB;AACvB,EAAA,MAAM,EAAE,gBAAA,GAAmB,CAAA,EAAG,UAAU,GAAA,EAAQ,MAAA,EAAQ,SAAQ,GAAI,OAAA;AACpE,EAAA,IAAI,YAAA,GAAe,CAAA;AACnB,EAAA,IAAI,eAAA,GAAkB,CAAA;AACtB,EAAA,IAAI,WAAA,GAAc,KAAA;AAElB,EAAA,OAAO,OAAO,OAAO,IAAA,KAAS;AAE5B,IAAA,IAAI,WAAA,EAAa;AACf,MAAA,MAAM,GAAA,GAAM,KAAK,GAAA,EAAI;AACrB,MAAA,IAAI,GAAA,GAAM,kBAAkB,OAAA,EAAS;AACnC,QAAA,WAAA,GAAc,KAAA;AACd,QAAA,YAAA,GAAe,CAAA;AACf,QAAA,OAAA,IAAU;AAAA,MACZ,CAAA,MAAO;AACL,QAAA,MAAM,IAAI,MAAM,yBAAyB,CAAA;AAAA,MAC3C;AAAA,IACF;AAEA,IAAA,IAAI;AACF,MAAA,MAAM,KAAK,KAAK,CAAA;AAEhB,MAAA,YAAA,GAAe,CAAA;AAAA,IACjB,SAAS,KAAA,EAAO;AACd,MAAA,YAAA,EAAA;AACA,MAAA,eAAA,GAAkB,KAAK,GAAA,EAAI;AAE3B,MAAA,IAAI,gBAAgB,gBAAA,EAAkB;AACpC,QAAA,WAAA,GAAc,IAAA;AACd,QAAA,MAAA,IAAS;AAAA,MACX;AAEA,MAAA,MAAM,KAAA;AAAA,IACR;AAAA,EACF,CAAA;AACF;AAYO,SAAS,kBAAkB,OAAA,EAET;AACvB,EAAA,MAAM,EAAE,WAAU,GAAI,OAAA;AAEtB,EAAA,OAAO,OAAO,OAAO,IAAA,KAAS;AAC5B,IAAA,MAAM,cAAA,GAAiB,IAAI,OAAA,CAAe,CAAC,GAAG,MAAA,KAAW;AACvD,MAAA,UAAA,CAAW,MAAM,OAAO,IAAI,KAAA,CAAM,iBAAiB,SAAS,CAAA,EAAA,CAAI,CAAC,CAAA,EAAG,SAAS,CAAA;AAAA,IAC/E,CAAC,CAAA;AAED,IAAA,MAAM,QAAQ,IAAA,CAAK,CAAC,KAAK,KAAK,CAAA,EAAG,cAAc,CAAC,CAAA;AAAA,EAClD,CAAA;AACF","file":"middleware.cjs","sourcesContent":["/**\n * Middleware System for Events Subscribers\n *\n * Compose subscriber behaviors using middleware (like Redux/Express).\n * Add retry logic, sampling, enrichment, logging, and more without modifying subscriber code.\n *\n * @example\n * ```typescript\n * import { applyMiddleware, retryMiddleware, loggingMiddleware } from 'autotel-subscribers/middleware';\n *\n * const subscriber = applyMiddleware(\n * new PostHogSubscriber({ apiKey: '...' }),\n * [\n * retryMiddleware({ maxRetries: 3 }),\n * loggingMiddleware()\n * ]\n * );\n * ```\n */\n\nimport type { EventSubscriber, EventAttributes, FunnelStatus, OutcomeStatus } from 'autotel/event-subscriber';\n\n/**\n * Unified event type for middleware\n */\nexport type EventsEvent =\n | {\n type: 'event';\n name: string;\n attributes?: EventAttributes;\n }\n | {\n type: 'funnel';\n funnel: string;\n step: FunnelStatus;\n attributes?: EventAttributes;\n }\n | {\n type: 'outcome';\n operation: string;\n outcome: OutcomeStatus;\n attributes?: EventAttributes;\n }\n | {\n type: 'value';\n name: string;\n value: number;\n attributes?: EventAttributes;\n };\n\n/**\n * Middleware function signature.\n *\n * Like Express middleware: `(event, next) => Promise<void>`\n */\nexport type SubscriberMiddleware = (\n event: EventsEvent,\n next: (event: EventsEvent) => Promise<void>\n) => Promise<void>;\n\n/**\n * Apply middleware to an subscriber.\n *\n * Middleware is executed in order. Each middleware can:\n * - Transform the event before passing to next()\n * - Add side effects (logging, metrics)\n * - Skip calling next() (filtering)\n * - Handle errors\n *\n * @example\n * ```typescript\n * const subscriber = applyMiddleware(\n * new WebhookSubscriber({ url: '...' }),\n * [\n * loggingMiddleware(),\n * retryMiddleware({ maxRetries: 3 }),\n * samplingMiddleware(0.1) // Only 10% of events\n * ]\n * );\n * ```\n */\nexport function applyMiddleware(\n subscriber: EventSubscriber,\n middlewares: SubscriberMiddleware[]\n): EventSubscriber {\n // Convert subscriber methods to event format\n const trackEvent = async (event: EventsEvent): Promise<void> => {\n switch (event.type) {\n case 'event': {\n await subscriber.trackEvent(event.name, event.attributes);\n break;\n }\n case 'funnel': {\n await subscriber.trackFunnelStep(event.funnel, event.step, event.attributes);\n break;\n }\n case 'outcome': {\n await subscriber.trackOutcome(event.operation, event.outcome, event.attributes);\n break;\n }\n case 'value': {\n await subscriber.trackValue(event.name, event.value, event.attributes);\n break;\n }\n }\n };\n\n // Build middleware chain\n type ChainFunction = (event: EventsEvent) => Promise<void>;\n const reversedMiddlewares = middlewares.toReversed();\n let chain: ChainFunction = trackEvent;\n for (const middleware of reversedMiddlewares) {\n const next = chain;\n chain = (event: EventsEvent) => middleware(event, next);\n }\n\n // Wrap subscriber with middleware chain\n return {\n name: `${subscriber.name}(middleware)`,\n version: subscriber.version,\n\n async trackEvent(name: string, attributes?: EventAttributes): Promise<void> {\n await chain({ type: 'event', name, attributes });\n },\n\n async trackFunnelStep(funnel: string, step: FunnelStatus, attributes?: EventAttributes): Promise<void> {\n await chain({ type: 'funnel', funnel, step, attributes });\n },\n\n async trackOutcome(operation: string, outcome: OutcomeStatus, attributes?: EventAttributes): Promise<void> {\n await chain({ type: 'outcome', operation, outcome, attributes });\n },\n\n async trackValue(name: string, value: number, attributes?: EventAttributes): Promise<void> {\n await chain({ type: 'value', name, value, attributes });\n },\n\n async shutdown(): Promise<void> {\n await subscriber.shutdown?.();\n },\n };\n}\n\n// ============================================================================\n// Built-in Middleware\n// ============================================================================\n\n/**\n * Retry failed requests with exponential backoff.\n *\n * @example\n * ```typescript\n * const subscriber = applyMiddleware(adapter, [\n * retryMiddleware({ maxRetries: 3, delayMs: 1000 })\n * ]);\n * ```\n */\nexport function retryMiddleware(options: {\n maxRetries?: number;\n delayMs?: number;\n onRetry?: (attempt: number, error: Error) => void;\n}): SubscriberMiddleware {\n const { maxRetries = 3, delayMs = 1000, onRetry } = options;\n\n return async (event, next) => {\n let lastError: Error | undefined;\n\n for (let attempt = 1; attempt <= maxRetries; attempt++) {\n try {\n await next(event);\n return; // Success!\n } catch (error) {\n lastError = error as Error;\n\n if (attempt < maxRetries) {\n onRetry?.(attempt, lastError);\n // Exponential backoff: 1s, 2s, 4s, 8s...\n await new Promise((resolve) => setTimeout(resolve, delayMs * Math.pow(2, attempt - 1)));\n }\n }\n }\n\n throw lastError;\n };\n}\n\n/**\n * Sample events (only send a percentage).\n *\n * @example\n * ```typescript\n * // Only send 10% of events (reduce costs)\n * const subscriber = applyMiddleware(adapter, [\n * samplingMiddleware(0.1)\n * ]);\n * ```\n */\nexport function samplingMiddleware(rate: number): SubscriberMiddleware {\n if (rate < 0 || rate > 1) {\n throw new Error('Sample rate must be between 0 and 1');\n }\n\n return async (event, next) => {\n if (Math.random() < rate) {\n await next(event);\n }\n // Else: skip this event\n };\n}\n\n/**\n * Enrich events with additional attributes.\n *\n * @example\n * ```typescript\n * const subscriber = applyMiddleware(adapter, [\n * enrichmentMiddleware((event) => ({\n * ...event,\n * attributes: {\n * ...event.attributes,\n * environment: process.env.NODE_ENV,\n * timestamp: Date.now()\n * }\n * }))\n * ]);\n * ```\n */\nexport function enrichmentMiddleware(\n enricher: (event: EventsEvent) => EventsEvent\n): SubscriberMiddleware {\n return async (event, next) => {\n const enriched = enricher(event);\n await next(enriched);\n };\n}\n\n/**\n * Log events to console.\n *\n * @example\n * ```typescript\n * const subscriber = applyMiddleware(adapter, [\n * loggingMiddleware({ prefix: '[Events]', logAttributes: true })\n * ]);\n * ```\n */\nexport function loggingMiddleware(options: {\n prefix?: string;\n logAttributes?: boolean;\n} = {}): SubscriberMiddleware {\n const { prefix = '[Events]', logAttributes = false } = options;\n\n return async (event, next) => {\n if (logAttributes) {\n console.log(prefix, event.type, event);\n } else {\n // Just log event type and name\n const eventName = 'name' in event ? event.name : `${(event as any).funnel || (event as any).operation}`;\n console.log(prefix, event.type, eventName);\n }\n\n await next(event);\n };\n}\n\n/**\n * Filter events based on a predicate.\n *\n * @example\n * ```typescript\n * // Only send 'order' events\n * const subscriber = applyMiddleware(adapter, [\n * filterMiddleware((event) =>\n * event.type === 'event' && event.name.startsWith('order.')\n * )\n * ]);\n * ```\n */\nexport function filterMiddleware(\n predicate: (event: EventsEvent) => boolean\n): SubscriberMiddleware {\n return async (event, next) => {\n if (predicate(event)) {\n await next(event);\n }\n };\n}\n\n/**\n * Transform events.\n *\n * @example\n * ```typescript\n * // Lowercase all event names\n * const subscriber = applyMiddleware(adapter, [\n * transformMiddleware((event) => {\n * if (event.type === 'event') {\n * return { ...event, name: event.name.toLowerCase() };\n * }\n * return event;\n * })\n * ]);\n * ```\n */\nexport function transformMiddleware(\n transformer: (event: EventsEvent) => EventsEvent\n): SubscriberMiddleware {\n return async (event, next) => {\n const transformed = transformer(event);\n await next(transformed);\n };\n}\n\n/**\n * Batch events and flush periodically.\n *\n * @example\n * ```typescript\n * const subscriber = applyMiddleware(adapter, [\n * batchingMiddleware({ batchSize: 100, flushInterval: 5000 })\n * ]);\n * ```\n */\nexport function batchingMiddleware(options: {\n batchSize?: number;\n flushInterval?: number;\n}): SubscriberMiddleware {\n const { batchSize = 100, flushInterval = 5000 } = options;\n const buffer: Array<{ event: EventsEvent; next: (event: EventsEvent) => Promise<void> }> = [];\n let flushTimer: NodeJS.Timeout | null = null;\n\n const flush = async () => {\n const batch = [...buffer];\n buffer.length = 0;\n\n await Promise.all(batch.map(({ event, next }) => next(event)));\n };\n\n const scheduleFlush = () => {\n if (flushTimer) return;\n flushTimer = setTimeout(() => {\n flush().catch(console.error);\n flushTimer = null;\n }, flushInterval);\n };\n\n return async (event, next) => {\n buffer.push({ event, next });\n\n if (buffer.length >= batchSize) {\n await flush();\n } else {\n scheduleFlush();\n }\n };\n}\n\n/**\n * Rate limit events (throttle).\n *\n * @example\n * ```typescript\n * // Max 100 events per second\n * const subscriber = applyMiddleware(adapter, [\n * rateLimitMiddleware({ requestsPerSecond: 100 })\n * ]);\n * ```\n */\nexport function rateLimitMiddleware(options: {\n requestsPerSecond: number;\n}): SubscriberMiddleware {\n const { requestsPerSecond } = options;\n const intervalMs = 1000 / requestsPerSecond;\n let lastCallTime = 0;\n const queue: Array<() => void> = [];\n let processing = false;\n\n const processQueue = async () => {\n if (processing) return;\n processing = true;\n\n while (queue.length > 0) {\n const now = Date.now();\n const timeSinceLastCall = now - lastCallTime;\n\n if (timeSinceLastCall < intervalMs) {\n await new Promise((resolve) => setTimeout(resolve, intervalMs - timeSinceLastCall));\n }\n\n const fn = queue.shift();\n if (fn) {\n lastCallTime = Date.now();\n fn();\n }\n }\n\n processing = false;\n };\n\n return async (event, next) => {\n return new Promise<void>((resolve, reject) => {\n queue.push(() => {\n next(event).then(resolve).catch(reject);\n });\n processQueue().catch(reject);\n });\n };\n}\n\n/**\n * Circuit breaker pattern.\n *\n * Opens circuit after N failures, prevents further requests for a timeout period.\n *\n * @example\n * ```typescript\n * const subscriber = applyMiddleware(adapter, [\n * circuitBreakerMiddleware({\n * failureThreshold: 5,\n * timeout: 60000 // 1 minute\n * })\n * ]);\n * ```\n */\nexport function circuitBreakerMiddleware(options: {\n failureThreshold?: number;\n timeout?: number;\n onOpen?: () => void;\n onClose?: () => void;\n}): SubscriberMiddleware {\n const { failureThreshold = 5, timeout = 60_000, onOpen, onClose } = options;\n let failureCount = 0;\n let lastFailureTime = 0;\n let circuitOpen = false;\n\n return async (event, next) => {\n // Check if circuit should close\n if (circuitOpen) {\n const now = Date.now();\n if (now - lastFailureTime > timeout) {\n circuitOpen = false;\n failureCount = 0;\n onClose?.();\n } else {\n throw new Error('Circuit breaker is open');\n }\n }\n\n try {\n await next(event);\n // Success resets failure count\n failureCount = 0;\n } catch (error) {\n failureCount++;\n lastFailureTime = Date.now();\n\n if (failureCount >= failureThreshold) {\n circuitOpen = true;\n onOpen?.();\n }\n\n throw error;\n }\n };\n}\n\n/**\n * Add timeout to events.\n *\n * @example\n * ```typescript\n * const subscriber = applyMiddleware(adapter, [\n * timeoutMiddleware({ timeoutMs: 5000 })\n * ]);\n * ```\n */\nexport function timeoutMiddleware(options: {\n timeoutMs: number;\n}): SubscriberMiddleware {\n const { timeoutMs } = options;\n\n return async (event, next) => {\n const timeoutPromise = new Promise<never>((_, reject) => {\n setTimeout(() => reject(new Error(`Timeout after ${timeoutMs}ms`)), timeoutMs);\n });\n\n await Promise.race([next(event), timeoutPromise]);\n };\n}\n"]}
@@ -0,0 +1,227 @@
1
+ import { EventAttributes, FunnelStatus, OutcomeStatus, EventSubscriber } from 'autotel/event-subscriber';
2
+
3
+ /**
4
+ * Middleware System for Events Subscribers
5
+ *
6
+ * Compose subscriber behaviors using middleware (like Redux/Express).
7
+ * Add retry logic, sampling, enrichment, logging, and more without modifying subscriber code.
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * import { applyMiddleware, retryMiddleware, loggingMiddleware } from 'autotel-subscribers/middleware';
12
+ *
13
+ * const subscriber = applyMiddleware(
14
+ * new PostHogSubscriber({ apiKey: '...' }),
15
+ * [
16
+ * retryMiddleware({ maxRetries: 3 }),
17
+ * loggingMiddleware()
18
+ * ]
19
+ * );
20
+ * ```
21
+ */
22
+
23
+ /**
24
+ * Unified event type for middleware
25
+ */
26
+ type EventsEvent = {
27
+ type: 'event';
28
+ name: string;
29
+ attributes?: EventAttributes;
30
+ } | {
31
+ type: 'funnel';
32
+ funnel: string;
33
+ step: FunnelStatus;
34
+ attributes?: EventAttributes;
35
+ } | {
36
+ type: 'outcome';
37
+ operation: string;
38
+ outcome: OutcomeStatus;
39
+ attributes?: EventAttributes;
40
+ } | {
41
+ type: 'value';
42
+ name: string;
43
+ value: number;
44
+ attributes?: EventAttributes;
45
+ };
46
+ /**
47
+ * Middleware function signature.
48
+ *
49
+ * Like Express middleware: `(event, next) => Promise<void>`
50
+ */
51
+ type SubscriberMiddleware = (event: EventsEvent, next: (event: EventsEvent) => Promise<void>) => Promise<void>;
52
+ /**
53
+ * Apply middleware to an subscriber.
54
+ *
55
+ * Middleware is executed in order. Each middleware can:
56
+ * - Transform the event before passing to next()
57
+ * - Add side effects (logging, metrics)
58
+ * - Skip calling next() (filtering)
59
+ * - Handle errors
60
+ *
61
+ * @example
62
+ * ```typescript
63
+ * const subscriber = applyMiddleware(
64
+ * new WebhookSubscriber({ url: '...' }),
65
+ * [
66
+ * loggingMiddleware(),
67
+ * retryMiddleware({ maxRetries: 3 }),
68
+ * samplingMiddleware(0.1) // Only 10% of events
69
+ * ]
70
+ * );
71
+ * ```
72
+ */
73
+ declare function applyMiddleware(subscriber: EventSubscriber, middlewares: SubscriberMiddleware[]): EventSubscriber;
74
+ /**
75
+ * Retry failed requests with exponential backoff.
76
+ *
77
+ * @example
78
+ * ```typescript
79
+ * const subscriber = applyMiddleware(adapter, [
80
+ * retryMiddleware({ maxRetries: 3, delayMs: 1000 })
81
+ * ]);
82
+ * ```
83
+ */
84
+ declare function retryMiddleware(options: {
85
+ maxRetries?: number;
86
+ delayMs?: number;
87
+ onRetry?: (attempt: number, error: Error) => void;
88
+ }): SubscriberMiddleware;
89
+ /**
90
+ * Sample events (only send a percentage).
91
+ *
92
+ * @example
93
+ * ```typescript
94
+ * // Only send 10% of events (reduce costs)
95
+ * const subscriber = applyMiddleware(adapter, [
96
+ * samplingMiddleware(0.1)
97
+ * ]);
98
+ * ```
99
+ */
100
+ declare function samplingMiddleware(rate: number): SubscriberMiddleware;
101
+ /**
102
+ * Enrich events with additional attributes.
103
+ *
104
+ * @example
105
+ * ```typescript
106
+ * const subscriber = applyMiddleware(adapter, [
107
+ * enrichmentMiddleware((event) => ({
108
+ * ...event,
109
+ * attributes: {
110
+ * ...event.attributes,
111
+ * environment: process.env.NODE_ENV,
112
+ * timestamp: Date.now()
113
+ * }
114
+ * }))
115
+ * ]);
116
+ * ```
117
+ */
118
+ declare function enrichmentMiddleware(enricher: (event: EventsEvent) => EventsEvent): SubscriberMiddleware;
119
+ /**
120
+ * Log events to console.
121
+ *
122
+ * @example
123
+ * ```typescript
124
+ * const subscriber = applyMiddleware(adapter, [
125
+ * loggingMiddleware({ prefix: '[Events]', logAttributes: true })
126
+ * ]);
127
+ * ```
128
+ */
129
+ declare function loggingMiddleware(options?: {
130
+ prefix?: string;
131
+ logAttributes?: boolean;
132
+ }): SubscriberMiddleware;
133
+ /**
134
+ * Filter events based on a predicate.
135
+ *
136
+ * @example
137
+ * ```typescript
138
+ * // Only send 'order' events
139
+ * const subscriber = applyMiddleware(adapter, [
140
+ * filterMiddleware((event) =>
141
+ * event.type === 'event' && event.name.startsWith('order.')
142
+ * )
143
+ * ]);
144
+ * ```
145
+ */
146
+ declare function filterMiddleware(predicate: (event: EventsEvent) => boolean): SubscriberMiddleware;
147
+ /**
148
+ * Transform events.
149
+ *
150
+ * @example
151
+ * ```typescript
152
+ * // Lowercase all event names
153
+ * const subscriber = applyMiddleware(adapter, [
154
+ * transformMiddleware((event) => {
155
+ * if (event.type === 'event') {
156
+ * return { ...event, name: event.name.toLowerCase() };
157
+ * }
158
+ * return event;
159
+ * })
160
+ * ]);
161
+ * ```
162
+ */
163
+ declare function transformMiddleware(transformer: (event: EventsEvent) => EventsEvent): SubscriberMiddleware;
164
+ /**
165
+ * Batch events and flush periodically.
166
+ *
167
+ * @example
168
+ * ```typescript
169
+ * const subscriber = applyMiddleware(adapter, [
170
+ * batchingMiddleware({ batchSize: 100, flushInterval: 5000 })
171
+ * ]);
172
+ * ```
173
+ */
174
+ declare function batchingMiddleware(options: {
175
+ batchSize?: number;
176
+ flushInterval?: number;
177
+ }): SubscriberMiddleware;
178
+ /**
179
+ * Rate limit events (throttle).
180
+ *
181
+ * @example
182
+ * ```typescript
183
+ * // Max 100 events per second
184
+ * const subscriber = applyMiddleware(adapter, [
185
+ * rateLimitMiddleware({ requestsPerSecond: 100 })
186
+ * ]);
187
+ * ```
188
+ */
189
+ declare function rateLimitMiddleware(options: {
190
+ requestsPerSecond: number;
191
+ }): SubscriberMiddleware;
192
+ /**
193
+ * Circuit breaker pattern.
194
+ *
195
+ * Opens circuit after N failures, prevents further requests for a timeout period.
196
+ *
197
+ * @example
198
+ * ```typescript
199
+ * const subscriber = applyMiddleware(adapter, [
200
+ * circuitBreakerMiddleware({
201
+ * failureThreshold: 5,
202
+ * timeout: 60000 // 1 minute
203
+ * })
204
+ * ]);
205
+ * ```
206
+ */
207
+ declare function circuitBreakerMiddleware(options: {
208
+ failureThreshold?: number;
209
+ timeout?: number;
210
+ onOpen?: () => void;
211
+ onClose?: () => void;
212
+ }): SubscriberMiddleware;
213
+ /**
214
+ * Add timeout to events.
215
+ *
216
+ * @example
217
+ * ```typescript
218
+ * const subscriber = applyMiddleware(adapter, [
219
+ * timeoutMiddleware({ timeoutMs: 5000 })
220
+ * ]);
221
+ * ```
222
+ */
223
+ declare function timeoutMiddleware(options: {
224
+ timeoutMs: number;
225
+ }): SubscriberMiddleware;
226
+
227
+ export { type EventsEvent, type SubscriberMiddleware, applyMiddleware, batchingMiddleware, circuitBreakerMiddleware, enrichmentMiddleware, filterMiddleware, loggingMiddleware, rateLimitMiddleware, retryMiddleware, samplingMiddleware, timeoutMiddleware, transformMiddleware };