@hookflo/tern 3.0.9-beta → 3.0.13-beta

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.
@@ -39,17 +39,79 @@ exports.handleProcess = handleProcess;
39
39
  exports.handleQueuedRequest = handleQueuedRequest;
40
40
  const QStash = __importStar(require("@upstash/qstash"));
41
41
  const index_1 = require("../index");
42
- function createQStashReceiver(queueConfig) {
43
- const receiverExport = QStash.Receiver
44
- ?? QStash.default?.Receiver;
42
+ async function dynamicImport(modulePath) {
43
+ return new Function('modulePath', 'return import(modulePath);')(modulePath);
44
+ }
45
+ async function loadQStashModules() {
46
+ const optionalImports = await Promise.allSettled([
47
+ dynamicImport('@upstash/qstash'),
48
+ dynamicImport('@upstash/qstash/nextjs'),
49
+ dynamicImport('@upstash/qstash/nuxt'),
50
+ dynamicImport('@upstash/qstash/sveltekit'),
51
+ dynamicImport('@upstash/qstash/cloudflare'),
52
+ ]);
53
+ return [
54
+ QStash,
55
+ ...optionalImports.flatMap((result) => (result.status === 'fulfilled' ? [result.value] : [])),
56
+ ];
57
+ }
58
+ function resolveModuleExport(modules, key) {
59
+ for (const moduleRef of modules) {
60
+ const directExport = moduleRef[key];
61
+ if (typeof directExport === 'function') {
62
+ return directExport;
63
+ }
64
+ const defaultExport = moduleRef.default?.[key];
65
+ if (typeof defaultExport === 'function') {
66
+ return defaultExport;
67
+ }
68
+ }
69
+ return undefined;
70
+ }
71
+ function resolveNestedConstructor(modules, key) {
72
+ const seen = new Set();
73
+ const queue = [...modules];
74
+ while (queue.length) {
75
+ const current = queue.shift();
76
+ if (!current || typeof current !== 'object' || seen.has(current)) {
77
+ continue;
78
+ }
79
+ seen.add(current);
80
+ const record = current;
81
+ const candidate = record[key];
82
+ if (typeof candidate === 'function') {
83
+ return candidate;
84
+ }
85
+ for (const value of Object.values(record)) {
86
+ if (value && typeof value === 'object') {
87
+ queue.push(value);
88
+ }
89
+ }
90
+ }
91
+ return undefined;
92
+ }
93
+ async function createQStashReceiver(queueConfig) {
94
+ const modules = await loadQStashModules();
95
+ const receiverExport = resolveModuleExport(modules, 'Receiver')
96
+ ?? resolveNestedConstructor(modules, 'Receiver');
45
97
  if (typeof receiverExport !== 'function') {
46
- throw new Error('[tern] Incompatible @upstash/qstash version: Receiver export not found. Please upgrade to a version that exports Receiver.');
98
+ throw new Error('[tern] Incompatible @upstash/qstash version: Receiver export not found. Ensure @upstash/qstash is installed and up-to-date.');
47
99
  }
48
100
  return new receiverExport({
49
101
  currentSigningKey: queueConfig.signingKey,
50
102
  nextSigningKey: queueConfig.nextSigningKey,
51
103
  });
52
104
  }
105
+ async function createQStashClient(queueConfig) {
106
+ const modules = await loadQStashModules();
107
+ const clientExport = resolveModuleExport(modules, 'Client');
108
+ const resolvedClientExport = clientExport
109
+ ?? resolveNestedConstructor(modules, 'Client');
110
+ if (typeof resolvedClientExport !== 'function') {
111
+ throw new Error('[tern] Incompatible @upstash/qstash version: Client export not found. Ensure @upstash/qstash is installed and up-to-date.');
112
+ }
113
+ return new resolvedClientExport({ token: queueConfig.token });
114
+ }
53
115
  function nonRetryableResponse(message, status = 489) {
54
116
  return new Response(JSON.stringify({ error: message }), {
55
117
  status,
@@ -76,7 +138,7 @@ async function handleReceive(request, platform, secret, queueConfig, toleranceIn
76
138
  if (!verificationResult.isValid) {
77
139
  return nonRetryableResponse(verificationResult.error || 'Webhook signature verification failed');
78
140
  }
79
- const client = new QStash.Client({ token: queueConfig.token });
141
+ const client = await createQStashClient(queueConfig);
80
142
  const queuedMessage = {
81
143
  platform,
82
144
  payload: verificationResult.payload,
@@ -105,7 +167,7 @@ async function handleProcess(request, handler, queueConfig) {
105
167
  return new Response(JSON.stringify({ error: 'Missing Upstash-Signature header' }), { status: 401, headers: { 'Content-Type': 'application/json' } });
106
168
  }
107
169
  const rawBody = await request.text();
108
- const receiver = createQStashReceiver(queueConfig);
170
+ const receiver = await createQStashReceiver(queueConfig);
109
171
  try {
110
172
  const verification = await receiver.verify({
111
173
  signature,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hookflo/tern",
3
- "version": "3.0.9-beta",
3
+ "version": "3.0.13-beta",
4
4
  "description": "A robust, scalable webhook verification framework supporting multiple platforms and signature algorithms",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -50,7 +50,6 @@
50
50
  "@types/node": "20.0.0",
51
51
  "@typescript-eslint/eslint-plugin": "^8.39.0",
52
52
  "@typescript-eslint/parser": "^8.39.0",
53
- "@upstash/qstash": "^2.9.0",
54
53
  "eslint": "^8.57.1",
55
54
  "eslint-config-airbnb-base": "^15.0.0",
56
55
  "eslint-plugin-import": "^2.32.0",
@@ -124,12 +123,7 @@
124
123
  ]
125
124
  }
126
125
  },
127
- "peerDependencies": {
128
- "@upstash/qstash": ">=2.0.0"
129
- },
130
- "peerDependenciesMeta": {
131
- "@upstash/qstash": {
132
- "optional": true
133
- }
126
+ "dependencies": {
127
+ "@upstash/qstash": "^2.9.0"
134
128
  }
135
129
  }