@uniforge/core 0.1.0-alpha.2
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.
- package/dist/auth/index.d.cts +165 -0
- package/dist/auth/index.d.ts +165 -0
- package/dist/auth/index.js +443 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/index.mjs +406 -0
- package/dist/auth/index.mjs.map +1 -0
- package/dist/billing/index.d.cts +34 -0
- package/dist/billing/index.d.ts +34 -0
- package/dist/billing/index.js +254 -0
- package/dist/billing/index.js.map +1 -0
- package/dist/billing/index.mjs +225 -0
- package/dist/billing/index.mjs.map +1 -0
- package/dist/config/index.d.cts +12 -0
- package/dist/config/index.d.ts +12 -0
- package/dist/config/index.js +186 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/index.mjs +156 -0
- package/dist/config/index.mjs.map +1 -0
- package/dist/database/index.d.cts +33 -0
- package/dist/database/index.d.ts +33 -0
- package/dist/database/index.js +127 -0
- package/dist/database/index.js.map +1 -0
- package/dist/database/index.mjs +95 -0
- package/dist/database/index.mjs.map +1 -0
- package/dist/graphql/index.d.cts +36 -0
- package/dist/graphql/index.d.ts +36 -0
- package/dist/graphql/index.js +209 -0
- package/dist/graphql/index.js.map +1 -0
- package/dist/graphql/index.mjs +179 -0
- package/dist/graphql/index.mjs.map +1 -0
- package/dist/index.d.cts +16 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +36 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +10 -0
- package/dist/index.mjs.map +1 -0
- package/dist/multi-store/index.d.cts +11 -0
- package/dist/multi-store/index.d.ts +11 -0
- package/dist/multi-store/index.js +473 -0
- package/dist/multi-store/index.js.map +1 -0
- package/dist/multi-store/index.mjs +447 -0
- package/dist/multi-store/index.mjs.map +1 -0
- package/dist/multi-tenant/index.d.cts +23 -0
- package/dist/multi-tenant/index.d.ts +23 -0
- package/dist/multi-tenant/index.js +69 -0
- package/dist/multi-tenant/index.js.map +1 -0
- package/dist/multi-tenant/index.mjs +41 -0
- package/dist/multi-tenant/index.mjs.map +1 -0
- package/dist/performance/index.d.cts +34 -0
- package/dist/performance/index.d.ts +34 -0
- package/dist/performance/index.js +319 -0
- package/dist/performance/index.js.map +1 -0
- package/dist/performance/index.mjs +290 -0
- package/dist/performance/index.mjs.map +1 -0
- package/dist/platform/index.d.cts +25 -0
- package/dist/platform/index.d.ts +25 -0
- package/dist/platform/index.js +91 -0
- package/dist/platform/index.js.map +1 -0
- package/dist/platform/index.mjs +62 -0
- package/dist/platform/index.mjs.map +1 -0
- package/dist/rbac/index.d.cts +24 -0
- package/dist/rbac/index.d.ts +24 -0
- package/dist/rbac/index.js +267 -0
- package/dist/rbac/index.js.map +1 -0
- package/dist/rbac/index.mjs +236 -0
- package/dist/rbac/index.mjs.map +1 -0
- package/dist/schema-CM7mHj_H.d.cts +53 -0
- package/dist/schema-CM7mHj_H.d.ts +53 -0
- package/dist/security/index.d.cts +47 -0
- package/dist/security/index.d.ts +47 -0
- package/dist/security/index.js +505 -0
- package/dist/security/index.js.map +1 -0
- package/dist/security/index.mjs +474 -0
- package/dist/security/index.mjs.map +1 -0
- package/dist/session-storage/index.d.cts +70 -0
- package/dist/session-storage/index.d.ts +70 -0
- package/dist/session-storage/index.js +271 -0
- package/dist/session-storage/index.js.map +1 -0
- package/dist/session-storage/index.mjs +242 -0
- package/dist/session-storage/index.mjs.map +1 -0
- package/dist/webhooks/index.d.cts +89 -0
- package/dist/webhooks/index.d.ts +89 -0
- package/dist/webhooks/index.js +380 -0
- package/dist/webhooks/index.js.map +1 -0
- package/dist/webhooks/index.mjs +348 -0
- package/dist/webhooks/index.mjs.map +1 -0
- package/package.json +119 -0
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
// src/webhooks/validator.ts
|
|
2
|
+
import { createHmac, timingSafeEqual } from "crypto";
|
|
3
|
+
function createWebhookValidator() {
|
|
4
|
+
return {
|
|
5
|
+
validate(request) {
|
|
6
|
+
const computed = createHmac("sha256", request.secret).update(request.rawBody).digest("base64");
|
|
7
|
+
const computedBuf = Buffer.from(computed, "utf-8");
|
|
8
|
+
const receivedBuf = Buffer.from(request.hmac, "utf-8");
|
|
9
|
+
if (computedBuf.length !== receivedBuf.length) {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
return timingSafeEqual(computedBuf, receivedBuf);
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// src/webhooks/queue.ts
|
|
18
|
+
import { randomUUID } from "crypto";
|
|
19
|
+
var DEFAULT_MAX_ATTEMPTS = 5;
|
|
20
|
+
var DEFAULT_BASE_RETRY_DELAY_MS = 1e3;
|
|
21
|
+
var DEFAULT_MAX_RETRY_DELAY_MS = 3e5;
|
|
22
|
+
var DEFAULT_KEY_PREFIX = "uniforge:webhook:";
|
|
23
|
+
function createWebhookQueue(redis, config) {
|
|
24
|
+
const maxAttempts = config?.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;
|
|
25
|
+
const baseRetryDelayMs = config?.baseRetryDelayMs ?? DEFAULT_BASE_RETRY_DELAY_MS;
|
|
26
|
+
const maxRetryDelayMs = config?.maxRetryDelayMs ?? DEFAULT_MAX_RETRY_DELAY_MS;
|
|
27
|
+
const prefix = config?.keyPrefix ?? DEFAULT_KEY_PREFIX;
|
|
28
|
+
const jobKey = (id) => `${prefix}job:${id}`;
|
|
29
|
+
const pendingKey = `${prefix}pending`;
|
|
30
|
+
const deadKey = `${prefix}dead`;
|
|
31
|
+
function serializeJob(job) {
|
|
32
|
+
return JSON.stringify({
|
|
33
|
+
...job,
|
|
34
|
+
payload: {
|
|
35
|
+
...job.payload,
|
|
36
|
+
timestamp: job.payload.timestamp.toISOString()
|
|
37
|
+
},
|
|
38
|
+
createdAt: job.createdAt.toISOString(),
|
|
39
|
+
updatedAt: job.updatedAt.toISOString(),
|
|
40
|
+
...job.nextRetryAt ? { nextRetryAt: job.nextRetryAt.toISOString() } : {}
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
function deserializeJob(raw) {
|
|
44
|
+
const data = JSON.parse(raw);
|
|
45
|
+
const payload = data["payload"];
|
|
46
|
+
const result = {
|
|
47
|
+
id: data["id"],
|
|
48
|
+
payload: {
|
|
49
|
+
topic: payload["topic"],
|
|
50
|
+
shopDomain: payload["shopDomain"],
|
|
51
|
+
apiVersion: payload["apiVersion"],
|
|
52
|
+
payload: payload["payload"],
|
|
53
|
+
webhookId: payload["webhookId"],
|
|
54
|
+
timestamp: new Date(payload["timestamp"])
|
|
55
|
+
},
|
|
56
|
+
status: data["status"],
|
|
57
|
+
attempts: data["attempts"],
|
|
58
|
+
maxAttempts: data["maxAttempts"],
|
|
59
|
+
createdAt: new Date(data["createdAt"]),
|
|
60
|
+
updatedAt: new Date(data["updatedAt"])
|
|
61
|
+
};
|
|
62
|
+
if (data["lastError"]) {
|
|
63
|
+
result.lastError = data["lastError"];
|
|
64
|
+
}
|
|
65
|
+
if (data["nextRetryAt"]) {
|
|
66
|
+
result.nextRetryAt = new Date(data["nextRetryAt"]);
|
|
67
|
+
}
|
|
68
|
+
return result;
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
async enqueue(payload) {
|
|
72
|
+
const id = randomUUID();
|
|
73
|
+
const now = /* @__PURE__ */ new Date();
|
|
74
|
+
const job = {
|
|
75
|
+
id,
|
|
76
|
+
payload,
|
|
77
|
+
status: "pending",
|
|
78
|
+
attempts: 0,
|
|
79
|
+
maxAttempts,
|
|
80
|
+
createdAt: now,
|
|
81
|
+
updatedAt: now
|
|
82
|
+
};
|
|
83
|
+
await redis.set(jobKey(id), serializeJob(job));
|
|
84
|
+
await redis.zadd(pendingKey, Date.now(), id);
|
|
85
|
+
return id;
|
|
86
|
+
},
|
|
87
|
+
async dequeueNext() {
|
|
88
|
+
const now = Date.now();
|
|
89
|
+
const results = await redis.zrangebyscore(pendingKey, "-inf", now, "LIMIT", 0, 1);
|
|
90
|
+
if (results.length === 0) {
|
|
91
|
+
return void 0;
|
|
92
|
+
}
|
|
93
|
+
const id = results[0];
|
|
94
|
+
const removed = await redis.zrem(pendingKey, id);
|
|
95
|
+
if (removed === 0) {
|
|
96
|
+
return void 0;
|
|
97
|
+
}
|
|
98
|
+
const raw = await redis.get(jobKey(id));
|
|
99
|
+
if (!raw) {
|
|
100
|
+
return void 0;
|
|
101
|
+
}
|
|
102
|
+
const job = deserializeJob(raw);
|
|
103
|
+
job.status = "processing";
|
|
104
|
+
job.updatedAt = /* @__PURE__ */ new Date();
|
|
105
|
+
await redis.set(jobKey(id), serializeJob(job));
|
|
106
|
+
return job;
|
|
107
|
+
},
|
|
108
|
+
async getJob(jobId) {
|
|
109
|
+
const raw = await redis.get(jobKey(jobId));
|
|
110
|
+
if (!raw) {
|
|
111
|
+
return void 0;
|
|
112
|
+
}
|
|
113
|
+
return deserializeJob(raw);
|
|
114
|
+
},
|
|
115
|
+
async acknowledgeJob(jobId, result) {
|
|
116
|
+
const raw = await redis.get(jobKey(jobId));
|
|
117
|
+
if (!raw) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const job = deserializeJob(raw);
|
|
121
|
+
job.status = result.success ? "completed" : "failed";
|
|
122
|
+
job.updatedAt = /* @__PURE__ */ new Date();
|
|
123
|
+
if (result.error) {
|
|
124
|
+
job.lastError = result.error;
|
|
125
|
+
}
|
|
126
|
+
await redis.set(jobKey(jobId), serializeJob(job));
|
|
127
|
+
await redis.zrem(pendingKey, jobId);
|
|
128
|
+
},
|
|
129
|
+
async requeueJob(jobId, error) {
|
|
130
|
+
const raw = await redis.get(jobKey(jobId));
|
|
131
|
+
if (!raw) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
const job = deserializeJob(raw);
|
|
135
|
+
job.attempts += 1;
|
|
136
|
+
job.lastError = error;
|
|
137
|
+
job.updatedAt = /* @__PURE__ */ new Date();
|
|
138
|
+
if (job.attempts >= job.maxAttempts) {
|
|
139
|
+
job.status = "dead";
|
|
140
|
+
await redis.set(jobKey(jobId), serializeJob(job));
|
|
141
|
+
await redis.zrem(pendingKey, jobId);
|
|
142
|
+
await redis.zadd(deadKey, Date.now(), jobId);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
const delay = Math.min(
|
|
146
|
+
baseRetryDelayMs * Math.pow(2, job.attempts - 1),
|
|
147
|
+
maxRetryDelayMs
|
|
148
|
+
);
|
|
149
|
+
const nextRetryAt = Date.now() + delay;
|
|
150
|
+
job.status = "pending";
|
|
151
|
+
job.nextRetryAt = new Date(nextRetryAt);
|
|
152
|
+
await redis.set(jobKey(jobId), serializeJob(job));
|
|
153
|
+
await redis.zadd(pendingKey, nextRetryAt, jobId);
|
|
154
|
+
},
|
|
155
|
+
async getDeadLetterJobs(limit = 100) {
|
|
156
|
+
const ids = await redis.zrange(deadKey, 0, limit - 1);
|
|
157
|
+
const jobs = [];
|
|
158
|
+
for (const id of ids) {
|
|
159
|
+
const raw = await redis.get(jobKey(id));
|
|
160
|
+
if (raw) {
|
|
161
|
+
jobs.push(deserializeJob(raw));
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return jobs;
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// src/webhooks/registry.ts
|
|
170
|
+
function createWebhookRegistry() {
|
|
171
|
+
const handlers = /* @__PURE__ */ new Map();
|
|
172
|
+
return {
|
|
173
|
+
register(topic, handler) {
|
|
174
|
+
handlers.set(topic, handler);
|
|
175
|
+
},
|
|
176
|
+
unregister(topic) {
|
|
177
|
+
handlers.delete(topic);
|
|
178
|
+
},
|
|
179
|
+
getHandler(topic) {
|
|
180
|
+
return handlers.get(topic);
|
|
181
|
+
},
|
|
182
|
+
listTopics() {
|
|
183
|
+
return [...handlers.keys()];
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// src/webhooks/processor.ts
|
|
189
|
+
var DEFAULT_POLL_INTERVAL_MS = 1e3;
|
|
190
|
+
function createWebhookProcessor(queue, handlers, config) {
|
|
191
|
+
const pollIntervalMs = config?.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
192
|
+
let intervalId;
|
|
193
|
+
function getHandler(topic) {
|
|
194
|
+
if (handlers instanceof Map) {
|
|
195
|
+
return handlers.get(topic);
|
|
196
|
+
}
|
|
197
|
+
return handlers.getHandler(topic);
|
|
198
|
+
}
|
|
199
|
+
return {
|
|
200
|
+
async processNext() {
|
|
201
|
+
const job = await queue.dequeueNext();
|
|
202
|
+
if (!job) {
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
const handler = getHandler(job.payload.topic);
|
|
206
|
+
if (!handler) {
|
|
207
|
+
await queue.acknowledgeJob(job.id, {
|
|
208
|
+
success: false,
|
|
209
|
+
error: `No handler registered for topic: ${job.payload.topic}`
|
|
210
|
+
});
|
|
211
|
+
return true;
|
|
212
|
+
}
|
|
213
|
+
try {
|
|
214
|
+
const result = await handler.handle(job.payload);
|
|
215
|
+
if (result.success) {
|
|
216
|
+
await queue.acknowledgeJob(job.id, result);
|
|
217
|
+
} else {
|
|
218
|
+
await queue.requeueJob(job.id, result.error ?? "Handler returned failure");
|
|
219
|
+
}
|
|
220
|
+
} catch (err) {
|
|
221
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
222
|
+
await queue.requeueJob(job.id, message);
|
|
223
|
+
}
|
|
224
|
+
return true;
|
|
225
|
+
},
|
|
226
|
+
start() {
|
|
227
|
+
if (intervalId) {
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
intervalId = setInterval(async () => {
|
|
231
|
+
try {
|
|
232
|
+
await this.processNext();
|
|
233
|
+
} catch {
|
|
234
|
+
}
|
|
235
|
+
}, pollIntervalMs);
|
|
236
|
+
},
|
|
237
|
+
stop() {
|
|
238
|
+
if (intervalId) {
|
|
239
|
+
clearInterval(intervalId);
|
|
240
|
+
intervalId = void 0;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// src/webhooks/gdpr.ts
|
|
247
|
+
function createGdprHandlers(handler) {
|
|
248
|
+
const handlers = /* @__PURE__ */ new Map();
|
|
249
|
+
handlers.set("customers/data_request", {
|
|
250
|
+
async handle(webhookPayload) {
|
|
251
|
+
try {
|
|
252
|
+
await handler.handleDataRequest(webhookPayload.payload);
|
|
253
|
+
return { success: true };
|
|
254
|
+
} catch (err) {
|
|
255
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
256
|
+
return { success: false, error: message };
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
handlers.set("customers/redact", {
|
|
261
|
+
async handle(webhookPayload) {
|
|
262
|
+
try {
|
|
263
|
+
await handler.handleCustomerRedact(webhookPayload.payload);
|
|
264
|
+
return { success: true };
|
|
265
|
+
} catch (err) {
|
|
266
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
267
|
+
return { success: false, error: message };
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
handlers.set("shop/redact", {
|
|
272
|
+
async handle(webhookPayload) {
|
|
273
|
+
try {
|
|
274
|
+
await handler.handleShopRedact(webhookPayload.payload);
|
|
275
|
+
return { success: true };
|
|
276
|
+
} catch (err) {
|
|
277
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
278
|
+
return { success: false, error: message };
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
return handlers;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// src/webhooks/middleware.ts
|
|
286
|
+
function createWebhookMiddleware(config) {
|
|
287
|
+
const { validator, secret, registry, queue } = config;
|
|
288
|
+
return {
|
|
289
|
+
async processWebhook(request) {
|
|
290
|
+
const hmac = request.headers["x-shopify-hmac-sha256"];
|
|
291
|
+
const topic = request.headers["x-shopify-topic"];
|
|
292
|
+
const shopDomain = request.headers["x-shopify-shop-domain"];
|
|
293
|
+
const apiVersion = request.headers["x-shopify-api-version"];
|
|
294
|
+
const webhookId = request.headers["x-shopify-webhook-id"];
|
|
295
|
+
if (!hmac || !topic || !shopDomain) {
|
|
296
|
+
return { statusCode: 401, body: "Missing required webhook headers" };
|
|
297
|
+
}
|
|
298
|
+
const isValid = validator.validate({
|
|
299
|
+
rawBody: request.rawBody,
|
|
300
|
+
hmac,
|
|
301
|
+
secret
|
|
302
|
+
});
|
|
303
|
+
if (!isValid) {
|
|
304
|
+
return { statusCode: 401, body: "Invalid HMAC signature" };
|
|
305
|
+
}
|
|
306
|
+
const payload = {
|
|
307
|
+
topic,
|
|
308
|
+
shopDomain,
|
|
309
|
+
apiVersion: apiVersion ?? "",
|
|
310
|
+
payload: JSON.parse(request.rawBody.toString("utf-8")),
|
|
311
|
+
webhookId: webhookId ?? "",
|
|
312
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
313
|
+
};
|
|
314
|
+
if (queue) {
|
|
315
|
+
try {
|
|
316
|
+
await queue.enqueue(payload);
|
|
317
|
+
return { statusCode: 200 };
|
|
318
|
+
} catch (err) {
|
|
319
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
320
|
+
return { statusCode: 500, body: `Failed to enqueue webhook: ${message}` };
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
const handler = registry.getHandler(topic);
|
|
324
|
+
if (!handler) {
|
|
325
|
+
return { statusCode: 404, body: `No handler for topic: ${topic}` };
|
|
326
|
+
}
|
|
327
|
+
try {
|
|
328
|
+
const result = await handler.handle(payload);
|
|
329
|
+
if (result.success) {
|
|
330
|
+
return { statusCode: 200 };
|
|
331
|
+
}
|
|
332
|
+
return { statusCode: 500, body: result.error ?? "Handler failed" };
|
|
333
|
+
} catch (err) {
|
|
334
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
335
|
+
return { statusCode: 500, body: `Webhook handler error: ${message}` };
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
export {
|
|
341
|
+
createGdprHandlers,
|
|
342
|
+
createWebhookMiddleware,
|
|
343
|
+
createWebhookProcessor,
|
|
344
|
+
createWebhookQueue,
|
|
345
|
+
createWebhookRegistry,
|
|
346
|
+
createWebhookValidator
|
|
347
|
+
};
|
|
348
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/webhooks/validator.ts","../../src/webhooks/queue.ts","../../src/webhooks/registry.ts","../../src/webhooks/processor.ts","../../src/webhooks/gdpr.ts","../../src/webhooks/middleware.ts"],"sourcesContent":["/**\n * Webhook HMAC validation.\n *\n * Validates incoming Shopify webhook requests by computing\n * HMAC-SHA256 of the raw body and comparing with the header value.\n * Uses base64 encoding (Shopify webhooks use base64, not hex).\n */\n\nimport { createHmac, timingSafeEqual } from 'node:crypto';\nimport type { WebhookValidator, WebhookValidationRequest } from '@uniforge/platform-core/webhooks';\n\nexport function createWebhookValidator(): WebhookValidator {\n return {\n validate(request: WebhookValidationRequest): boolean {\n const computed = createHmac('sha256', request.secret)\n .update(request.rawBody)\n .digest('base64');\n\n const computedBuf = Buffer.from(computed, 'utf-8');\n const receivedBuf = Buffer.from(request.hmac, 'utf-8');\n\n // Buffers must be the same length for timingSafeEqual\n if (computedBuf.length !== receivedBuf.length) {\n return false;\n }\n\n return timingSafeEqual(computedBuf, receivedBuf);\n },\n };\n}\n","/**\n * Redis-backed webhook queue.\n *\n * Stores webhook jobs in Redis with sorted sets for ordered processing.\n * Supports exponential backoff retries and dead letter queue.\n */\n\nimport { randomUUID } from 'node:crypto';\nimport type Redis from 'ioredis';\nimport type {\n WebhookQueue,\n WebhookJob,\n WebhookJobStatus,\n WebhookQueueConfig,\n WebhookHandlerResult,\n} from '@uniforge/platform-core/webhooks';\nimport type { WebhookPayload } from '@uniforge/platform-core/webhooks';\n\nconst DEFAULT_MAX_ATTEMPTS = 5;\nconst DEFAULT_BASE_RETRY_DELAY_MS = 1000;\nconst DEFAULT_MAX_RETRY_DELAY_MS = 300_000;\nconst DEFAULT_KEY_PREFIX = 'uniforge:webhook:';\n\nexport function createWebhookQueue(\n redis: Redis,\n config?: WebhookQueueConfig,\n): WebhookQueue {\n const maxAttempts = config?.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;\n const baseRetryDelayMs = config?.baseRetryDelayMs ?? DEFAULT_BASE_RETRY_DELAY_MS;\n const maxRetryDelayMs = config?.maxRetryDelayMs ?? DEFAULT_MAX_RETRY_DELAY_MS;\n const prefix = config?.keyPrefix ?? DEFAULT_KEY_PREFIX;\n\n const jobKey = (id: string) => `${prefix}job:${id}`;\n const pendingKey = `${prefix}pending`;\n const deadKey = `${prefix}dead`;\n\n function serializeJob(job: WebhookJob): string {\n return JSON.stringify({\n ...job,\n payload: {\n ...job.payload,\n timestamp: job.payload.timestamp.toISOString(),\n },\n createdAt: job.createdAt.toISOString(),\n updatedAt: job.updatedAt.toISOString(),\n ...(job.nextRetryAt ? { nextRetryAt: job.nextRetryAt.toISOString() } : {}),\n });\n }\n\n function deserializeJob(raw: string): WebhookJob {\n const data = JSON.parse(raw) as Record<string, unknown>;\n const payload = data['payload'] as Record<string, unknown>;\n const result: WebhookJob = {\n id: data['id'] as string,\n payload: {\n topic: payload['topic'] as string,\n shopDomain: payload['shopDomain'] as string,\n apiVersion: payload['apiVersion'] as string,\n payload: payload['payload'],\n webhookId: payload['webhookId'] as string,\n timestamp: new Date(payload['timestamp'] as string),\n },\n status: data['status'] as WebhookJobStatus,\n attempts: data['attempts'] as number,\n maxAttempts: data['maxAttempts'] as number,\n createdAt: new Date(data['createdAt'] as string),\n updatedAt: new Date(data['updatedAt'] as string),\n };\n if (data['lastError']) {\n result.lastError = data['lastError'] as string;\n }\n if (data['nextRetryAt']) {\n result.nextRetryAt = new Date(data['nextRetryAt'] as string);\n }\n return result;\n }\n\n return {\n async enqueue(payload: WebhookPayload): Promise<string> {\n const id = randomUUID();\n const now = new Date();\n const job: WebhookJob = {\n id,\n payload,\n status: 'pending',\n attempts: 0,\n maxAttempts,\n createdAt: now,\n updatedAt: now,\n };\n\n await redis.set(jobKey(id), serializeJob(job));\n await redis.zadd(pendingKey, Date.now(), id);\n\n return id;\n },\n\n async dequeueNext(): Promise<WebhookJob | undefined> {\n const now = Date.now();\n // Get jobs that are ready to process (score <= now)\n const results = await redis.zrangebyscore(pendingKey, '-inf', now, 'LIMIT', 0, 1);\n if (results.length === 0) {\n return undefined;\n }\n\n const id = results[0]!;\n // Remove from pending set atomically\n const removed = await redis.zrem(pendingKey, id);\n if (removed === 0) {\n // Another consumer took it\n return undefined;\n }\n\n const raw = await redis.get(jobKey(id));\n if (!raw) {\n return undefined;\n }\n\n const job = deserializeJob(raw);\n job.status = 'processing';\n job.updatedAt = new Date();\n await redis.set(jobKey(id), serializeJob(job));\n\n return job;\n },\n\n async getJob(jobId: string): Promise<WebhookJob | undefined> {\n const raw = await redis.get(jobKey(jobId));\n if (!raw) {\n return undefined;\n }\n return deserializeJob(raw);\n },\n\n async acknowledgeJob(jobId: string, result: WebhookHandlerResult): Promise<void> {\n const raw = await redis.get(jobKey(jobId));\n if (!raw) {\n return;\n }\n\n const job = deserializeJob(raw);\n job.status = result.success ? 'completed' : 'failed';\n job.updatedAt = new Date();\n if (result.error) {\n job.lastError = result.error;\n }\n\n await redis.set(jobKey(jobId), serializeJob(job));\n await redis.zrem(pendingKey, jobId);\n },\n\n async requeueJob(jobId: string, error: string): Promise<void> {\n const raw = await redis.get(jobKey(jobId));\n if (!raw) {\n return;\n }\n\n const job = deserializeJob(raw);\n job.attempts += 1;\n job.lastError = error;\n job.updatedAt = new Date();\n\n if (job.attempts >= job.maxAttempts) {\n job.status = 'dead';\n await redis.set(jobKey(jobId), serializeJob(job));\n await redis.zrem(pendingKey, jobId);\n await redis.zadd(deadKey, Date.now(), jobId);\n return;\n }\n\n // Exponential backoff: baseDelay * 2^(attempts - 1), capped at maxDelay\n const delay = Math.min(\n baseRetryDelayMs * Math.pow(2, job.attempts - 1),\n maxRetryDelayMs,\n );\n const nextRetryAt = Date.now() + delay;\n job.status = 'pending';\n job.nextRetryAt = new Date(nextRetryAt);\n\n await redis.set(jobKey(jobId), serializeJob(job));\n await redis.zadd(pendingKey, nextRetryAt, jobId);\n },\n\n async getDeadLetterJobs(limit = 100): Promise<WebhookJob[]> {\n const ids = await redis.zrange(deadKey, 0, limit - 1);\n const jobs: WebhookJob[] = [];\n\n for (const id of ids) {\n const raw = await redis.get(jobKey(id));\n if (raw) {\n jobs.push(deserializeJob(raw));\n }\n }\n\n return jobs;\n },\n };\n}\n","/**\n * Webhook handler registry.\n *\n * A simple in-memory registry mapping webhook topics to their handlers.\n */\n\nimport type { WebhookHandler } from '@uniforge/platform-core/webhooks';\n\nexport interface WebhookRegistry {\n register(topic: string, handler: WebhookHandler): void;\n unregister(topic: string): void;\n getHandler(topic: string): WebhookHandler | undefined;\n listTopics(): string[];\n}\n\nexport function createWebhookRegistry(): WebhookRegistry {\n const handlers = new Map<string, WebhookHandler>();\n\n return {\n register(topic: string, handler: WebhookHandler): void {\n handlers.set(topic, handler);\n },\n\n unregister(topic: string): void {\n handlers.delete(topic);\n },\n\n getHandler(topic: string): WebhookHandler | undefined {\n return handlers.get(topic);\n },\n\n listTopics(): string[] {\n return [...handlers.keys()];\n },\n };\n}\n","/**\n * Webhook queue processor.\n *\n * Polls the webhook queue for pending jobs and dispatches them\n * to the appropriate handler. Handles success/failure and requeue.\n */\n\nimport type { WebhookQueue, WebhookHandler } from '@uniforge/platform-core/webhooks';\nimport type { WebhookRegistry } from './registry';\n\nexport interface WebhookProcessorConfig {\n pollIntervalMs?: number;\n}\n\nexport interface WebhookProcessor {\n processNext(): Promise<boolean>;\n start(): void;\n stop(): void;\n}\n\nconst DEFAULT_POLL_INTERVAL_MS = 1000;\n\nexport function createWebhookProcessor(\n queue: WebhookQueue,\n handlers: Map<string, WebhookHandler> | WebhookRegistry,\n config?: WebhookProcessorConfig,\n): WebhookProcessor {\n const pollIntervalMs = config?.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;\n let intervalId: ReturnType<typeof setInterval> | undefined;\n\n function getHandler(topic: string): WebhookHandler | undefined {\n if (handlers instanceof Map) {\n return handlers.get(topic);\n }\n return handlers.getHandler(topic);\n }\n\n return {\n async processNext(): Promise<boolean> {\n const job = await queue.dequeueNext();\n if (!job) {\n return false;\n }\n\n const handler = getHandler(job.payload.topic);\n if (!handler) {\n await queue.acknowledgeJob(job.id, {\n success: false,\n error: `No handler registered for topic: ${job.payload.topic}`,\n });\n return true;\n }\n\n try {\n const result = await handler.handle(job.payload);\n if (result.success) {\n await queue.acknowledgeJob(job.id, result);\n } else {\n await queue.requeueJob(job.id, result.error ?? 'Handler returned failure');\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n await queue.requeueJob(job.id, message);\n }\n\n return true;\n },\n\n start(): void {\n if (intervalId) {\n return;\n }\n intervalId = setInterval(async () => {\n try {\n await this.processNext();\n } catch {\n // Swallow polling errors to avoid crashing the processor\n }\n }, pollIntervalMs);\n },\n\n stop(): void {\n if (intervalId) {\n clearInterval(intervalId);\n intervalId = undefined;\n }\n },\n };\n}\n","/**\n * GDPR webhook helpers.\n *\n * Wraps a GdprHandler implementation into standard WebhookHandler\n * entries that can be registered with the webhook registry.\n */\n\nimport type {\n GdprHandler,\n CustomersDataRequest,\n CustomersRedact,\n ShopRedact,\n WebhookHandler,\n} from '@uniforge/platform-core/webhooks';\n\nexport function createGdprHandlers(handler: GdprHandler): Map<string, WebhookHandler> {\n const handlers = new Map<string, WebhookHandler>();\n\n handlers.set('customers/data_request', {\n async handle(webhookPayload) {\n try {\n await handler.handleDataRequest(webhookPayload.payload as CustomersDataRequest);\n return { success: true };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return { success: false, error: message };\n }\n },\n });\n\n handlers.set('customers/redact', {\n async handle(webhookPayload) {\n try {\n await handler.handleCustomerRedact(webhookPayload.payload as CustomersRedact);\n return { success: true };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return { success: false, error: message };\n }\n },\n });\n\n handlers.set('shop/redact', {\n async handle(webhookPayload) {\n try {\n await handler.handleShopRedact(webhookPayload.payload as ShopRedact);\n return { success: true };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return { success: false, error: message };\n }\n },\n });\n\n return handlers;\n}\n","/**\n * Webhook middleware.\n *\n * Processes incoming webhook HTTP requests: validates HMAC,\n * extracts headers, and either enqueues for async processing\n * or dispatches synchronously to the handler.\n */\n\nimport type {\n WebhookValidator,\n WebhookQueue,\n WebhookPayload,\n} from '@uniforge/platform-core/webhooks';\nimport type { WebhookRegistry } from './registry';\n\nexport interface WebhookRequest {\n rawBody: Buffer;\n headers: Record<string, string>;\n}\n\nexport interface WebhookMiddlewareResult {\n statusCode: 200 | 401 | 404 | 500;\n body?: string;\n}\n\nexport interface WebhookMiddlewareConfig {\n validator: WebhookValidator;\n secret: string;\n registry: WebhookRegistry;\n queue?: WebhookQueue;\n}\n\nexport function createWebhookMiddleware(config: WebhookMiddlewareConfig) {\n const { validator, secret, registry, queue } = config;\n\n return {\n async processWebhook(request: WebhookRequest): Promise<WebhookMiddlewareResult> {\n const hmac = request.headers['x-shopify-hmac-sha256'];\n const topic = request.headers['x-shopify-topic'];\n const shopDomain = request.headers['x-shopify-shop-domain'];\n const apiVersion = request.headers['x-shopify-api-version'];\n const webhookId = request.headers['x-shopify-webhook-id'];\n\n if (!hmac || !topic || !shopDomain) {\n return { statusCode: 401, body: 'Missing required webhook headers' };\n }\n\n const isValid = validator.validate({\n rawBody: request.rawBody,\n hmac,\n secret,\n });\n\n if (!isValid) {\n return { statusCode: 401, body: 'Invalid HMAC signature' };\n }\n\n const payload: WebhookPayload = {\n topic,\n shopDomain,\n apiVersion: apiVersion ?? '',\n payload: JSON.parse(request.rawBody.toString('utf-8')),\n webhookId: webhookId ?? '',\n timestamp: new Date(),\n };\n\n // Async mode: enqueue and return immediately\n if (queue) {\n try {\n await queue.enqueue(payload);\n return { statusCode: 200 };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return { statusCode: 500, body: `Failed to enqueue webhook: ${message}` };\n }\n }\n\n // Sync mode: process immediately\n const handler = registry.getHandler(topic);\n if (!handler) {\n return { statusCode: 404, body: `No handler for topic: ${topic}` };\n }\n\n try {\n const result = await handler.handle(payload);\n if (result.success) {\n return { statusCode: 200 };\n }\n return { statusCode: 500, body: result.error ?? 'Handler failed' };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return { statusCode: 500, body: `Webhook handler error: ${message}` };\n }\n },\n };\n}\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@uniforge/core",
|
|
3
|
+
"version": "0.1.0-alpha.2",
|
|
4
|
+
"description": "Core framework functionality for UniForge applications",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.mjs",
|
|
12
|
+
"require": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./auth": {
|
|
15
|
+
"types": "./dist/auth/index.d.ts",
|
|
16
|
+
"import": "./dist/auth/index.mjs",
|
|
17
|
+
"require": "./dist/auth/index.js"
|
|
18
|
+
},
|
|
19
|
+
"./multi-tenant": {
|
|
20
|
+
"types": "./dist/multi-tenant/index.d.ts",
|
|
21
|
+
"import": "./dist/multi-tenant/index.mjs",
|
|
22
|
+
"require": "./dist/multi-tenant/index.js"
|
|
23
|
+
},
|
|
24
|
+
"./session-storage": {
|
|
25
|
+
"types": "./dist/session-storage/index.d.ts",
|
|
26
|
+
"import": "./dist/session-storage/index.mjs",
|
|
27
|
+
"require": "./dist/session-storage/index.js"
|
|
28
|
+
},
|
|
29
|
+
"./database": {
|
|
30
|
+
"types": "./dist/database/index.d.ts",
|
|
31
|
+
"import": "./dist/database/index.mjs",
|
|
32
|
+
"require": "./dist/database/index.js"
|
|
33
|
+
},
|
|
34
|
+
"./config": {
|
|
35
|
+
"types": "./dist/config/index.d.ts",
|
|
36
|
+
"import": "./dist/config/index.mjs",
|
|
37
|
+
"require": "./dist/config/index.js"
|
|
38
|
+
},
|
|
39
|
+
"./graphql": {
|
|
40
|
+
"types": "./dist/graphql/index.d.ts",
|
|
41
|
+
"import": "./dist/graphql/index.mjs",
|
|
42
|
+
"require": "./dist/graphql/index.js"
|
|
43
|
+
},
|
|
44
|
+
"./webhooks": {
|
|
45
|
+
"types": "./dist/webhooks/index.d.ts",
|
|
46
|
+
"import": "./dist/webhooks/index.mjs",
|
|
47
|
+
"require": "./dist/webhooks/index.js"
|
|
48
|
+
},
|
|
49
|
+
"./multi-store": {
|
|
50
|
+
"types": "./dist/multi-store/index.d.ts",
|
|
51
|
+
"import": "./dist/multi-store/index.mjs",
|
|
52
|
+
"require": "./dist/multi-store/index.js"
|
|
53
|
+
},
|
|
54
|
+
"./rbac": {
|
|
55
|
+
"types": "./dist/rbac/index.d.ts",
|
|
56
|
+
"import": "./dist/rbac/index.mjs",
|
|
57
|
+
"require": "./dist/rbac/index.js"
|
|
58
|
+
},
|
|
59
|
+
"./billing": {
|
|
60
|
+
"types": "./dist/billing/index.d.ts",
|
|
61
|
+
"import": "./dist/billing/index.mjs",
|
|
62
|
+
"require": "./dist/billing/index.js"
|
|
63
|
+
},
|
|
64
|
+
"./platform": {
|
|
65
|
+
"types": "./dist/platform/index.d.ts",
|
|
66
|
+
"import": "./dist/platform/index.mjs",
|
|
67
|
+
"require": "./dist/platform/index.js"
|
|
68
|
+
},
|
|
69
|
+
"./security": {
|
|
70
|
+
"types": "./dist/security/index.d.ts",
|
|
71
|
+
"import": "./dist/security/index.mjs",
|
|
72
|
+
"require": "./dist/security/index.js"
|
|
73
|
+
},
|
|
74
|
+
"./performance": {
|
|
75
|
+
"types": "./dist/performance/index.d.ts",
|
|
76
|
+
"import": "./dist/performance/index.mjs",
|
|
77
|
+
"require": "./dist/performance/index.js"
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
"files": [
|
|
81
|
+
"dist"
|
|
82
|
+
],
|
|
83
|
+
"dependencies": {
|
|
84
|
+
"@prisma/client": "^5",
|
|
85
|
+
"ioredis": "^5",
|
|
86
|
+
"@uniforge/platform-core": "^0.1.0-alpha.2"
|
|
87
|
+
},
|
|
88
|
+
"devDependencies": {
|
|
89
|
+
"prisma": "^5",
|
|
90
|
+
"tsup": "^8.3.0",
|
|
91
|
+
"vitest": "^2.1.0",
|
|
92
|
+
"@uniforge/testing": "^0.1.0-alpha.2"
|
|
93
|
+
},
|
|
94
|
+
"keywords": [
|
|
95
|
+
"uniforge",
|
|
96
|
+
"core",
|
|
97
|
+
"e-commerce",
|
|
98
|
+
"shopify"
|
|
99
|
+
],
|
|
100
|
+
"license": "MIT",
|
|
101
|
+
"publishConfig": {
|
|
102
|
+
"access": "public"
|
|
103
|
+
},
|
|
104
|
+
"repository": {
|
|
105
|
+
"type": "git",
|
|
106
|
+
"url": "https://github.com/uniprocessing/uniforge.git",
|
|
107
|
+
"directory": "packages/core"
|
|
108
|
+
},
|
|
109
|
+
"scripts": {
|
|
110
|
+
"build": "prisma generate && tsup",
|
|
111
|
+
"db:generate": "prisma generate",
|
|
112
|
+
"db:migrate": "prisma migrate dev",
|
|
113
|
+
"db:push": "prisma db push",
|
|
114
|
+
"dev": "tsup --watch",
|
|
115
|
+
"test": "vitest",
|
|
116
|
+
"lint": "eslint src/",
|
|
117
|
+
"typecheck": "tsc --noEmit"
|
|
118
|
+
}
|
|
119
|
+
}
|