@cequrebackends/cequre-ts 0.14.0 → 0.15.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.
@@ -15,7 +15,7 @@ import {
15
15
  } from "../../index-mfqj7cwr.js";
16
16
  import {
17
17
  CequreQueueManager
18
- } from "../../index-7xxm1add.js";
18
+ } from "../../index-z2xccrdm.js";
19
19
  import {
20
20
  CequreError
21
21
  } from "../../index-qzzg2p5r.js";
@@ -24,7 +24,7 @@ import {
24
24
  init_bun_redis_cache,
25
25
  init_logger,
26
26
  logger
27
- } from "../../index-8bhn0nb4.js";
27
+ } from "../../index-6vf0r9nh.js";
28
28
  import {
29
29
  ulid
30
30
  } from "../../index-cpw1bjf1.js";
@@ -1,7 +1,7 @@
1
1
  // @bun
2
2
  import {
3
3
  CequreQueueManager
4
- } from "../../index-7xxm1add.js";
4
+ } from "../../index-z2xccrdm.js";
5
5
  import {
6
6
  CequreError
7
7
  } from "../../index-qzzg2p5r.js";
@@ -9,7 +9,7 @@ import {
9
9
  exports_logger,
10
10
  init_logger,
11
11
  logger
12
- } from "../../index-8bhn0nb4.js";
12
+ } from "../../index-6vf0r9nh.js";
13
13
  import {
14
14
  __toCommonJS,
15
15
  ulid
@@ -4,7 +4,7 @@ import {
4
4
  init_logger,
5
5
  init_upstash_kv,
6
6
  logger
7
- } from "../../index-8bhn0nb4.js";
7
+ } from "../../index-6vf0r9nh.js";
8
8
  import {
9
9
  ulid
10
10
  } from "../../index-cpw1bjf1.js";
@@ -41,7 +41,20 @@ class VercelCronProvider {
41
41
  this.jobs.set(name, { name, schedule, handler });
42
42
  logger.info(`[Cequre Cron (Vercel)] Registered "${name}" with schedule "${schedule}". ` + `Add to vercel.json crons: { "path": "/_cron/${name}", "schedule": "${schedule}" }`);
43
43
  }
44
- async execute(name) {
44
+ verifyCronSecret(request) {
45
+ const cronSecret = process.env.CRON_SECRET;
46
+ if (!cronSecret) {
47
+ return true;
48
+ }
49
+ const authHeader = request.headers.get("authorization");
50
+ const customHeader = request.headers.get("x-cron-secret");
51
+ const bearerSecret = authHeader?.startsWith("Bearer ") ? authHeader.substring(7) : null;
52
+ return bearerSecret === cronSecret || customHeader === cronSecret;
53
+ }
54
+ async execute(name, request) {
55
+ if (request && !this.verifyCronSecret(request)) {
56
+ throw new Error(`[Cequre Cron (Vercel)] Unauthorized cron invocation for "${name}".`);
57
+ }
45
58
  const job = this.jobs.get(name);
46
59
  if (!job) {
47
60
  throw new Error(`[Cequre Cron (Vercel)] Job "${name}" not found.`);
@@ -136,19 +149,21 @@ class VercelStorageProvider {
136
149
  }
137
150
  }
138
151
  async saveFile(file, options) {
152
+ if (!this.token) {
153
+ throw new Error("[Cequre Storage (Vercel)] BLOB_READ_WRITE_TOKEN is not configured.");
154
+ }
139
155
  const ext = file.name ? file.name.substring(file.name.lastIndexOf(".")) : "";
140
156
  const safeName = options?.filename ? options.filename : `${ulid()}${ext}`;
141
157
  const buffer = await file.arrayBuffer();
142
- const res = await fetch("https://blob.vercel.com/", {
143
- method: "POST",
158
+ const res = await fetch(`https://blob.vercel.com/${encodeURIComponent(safeName)}`, {
159
+ method: "PUT",
144
160
  headers: {
145
161
  Authorization: `Bearer ${this.token}`,
146
- "Content-Type": "application/json"
162
+ "x-api-version": "7",
163
+ "x-add-random-suffix": "0",
164
+ "content-type": file.type || "application/octet-stream"
147
165
  },
148
- body: JSON.stringify({
149
- pathname: safeName,
150
- payload: Array.from(new Uint8Array(buffer))
151
- })
166
+ body: buffer
152
167
  });
153
168
  if (!res.ok) {
154
169
  throw new Error(`Vercel Blob upload failed: ${res.status} ${await res.text()}`);
@@ -162,17 +177,23 @@ class VercelStorageProvider {
162
177
  url: blob.url
163
178
  };
164
179
  }
165
- async deleteFile(filename) {
166
- const res = await fetch("https://blob.vercel.com/", {
167
- method: "DELETE",
180
+ async deleteFile(filenameOrUrl) {
181
+ if (!this.token) {
182
+ console.warn("[Cequre Storage (Vercel)] BLOB_READ_WRITE_TOKEN not configured.");
183
+ return;
184
+ }
185
+ const url = filenameOrUrl.startsWith("http") ? filenameOrUrl : `https://blob.vercel.com/${filenameOrUrl}`;
186
+ const res = await fetch("https://blob.vercel.com/delete", {
187
+ method: "POST",
168
188
  headers: {
169
189
  Authorization: `Bearer ${this.token}`,
190
+ "x-api-version": "7",
170
191
  "Content-Type": "application/json"
171
192
  },
172
- body: JSON.stringify({ pathname: filename })
193
+ body: JSON.stringify({ urls: [url] })
173
194
  });
174
195
  if (!res.ok && res.status !== 404) {
175
- console.error(`[Cequre Storage (Vercel)] Failed to delete ${filename}:`, await res.text());
196
+ console.error(`[Cequre Storage (Vercel)] Failed to delete ${filenameOrUrl}:`, await res.text());
176
197
  }
177
198
  }
178
199
  }
@@ -221,7 +242,22 @@ class VercelQueueProvider {
221
242
  this.workers.set(queueName, handler);
222
243
  logger.info(`[Cequre Queue (Vercel)] Worker registered for queue "${queueName}". Route: /_queue/${queueName}`);
223
244
  }
224
- async processDelivery(queueName, body) {
245
+ async verifySignature(request) {
246
+ const signature = request.headers.get("upstash-signature");
247
+ const currentKey = process.env.QSTASH_CURRENT_SIGNING_KEY;
248
+ const nextKey = process.env.QSTASH_NEXT_SIGNING_KEY;
249
+ if (!currentKey && !nextKey) {
250
+ return true;
251
+ }
252
+ if (!signature) {
253
+ return false;
254
+ }
255
+ return true;
256
+ }
257
+ async processDelivery(queueName, body, request) {
258
+ if (request && !await this.verifySignature(request)) {
259
+ throw new Error(`[Cequre Queue (Vercel)] Invalid QStash signature for queue "${queueName}".`);
260
+ }
225
261
  const handler = this.workers.get(queueName);
226
262
  if (!handler) {
227
263
  throw new Error(`[Cequre Queue (Vercel)] No worker for queue "${queueName}".`);
@@ -16,10 +16,15 @@ import type { CronProvider } from "../../shared/core/types";
16
16
  export declare class VercelCronProvider implements CronProvider {
17
17
  private jobs;
18
18
  registerJob(name: string, schedule: string, handler: () => void | Promise<void>): void;
19
+ /**
20
+ * Verifies the Vercel Cron request secret if CRON_SECRET environment variable is set.
21
+ * Vercel sends `Authorization: Bearer <CRON_SECRET>` or `x-cron-secret` header.
22
+ */
23
+ verifyCronSecret(request: Request): boolean;
19
24
  /**
20
25
  * Execute a cron job by name. Called by the /_cron/:name route handler.
21
26
  */
22
- execute(name: string): Promise<void>;
27
+ execute(name: string, request?: Request): Promise<void>;
23
28
  /**
24
29
  * Returns all registered cron jobs for vercel.json generation.
25
30
  */
@@ -22,10 +22,15 @@ export declare class VercelQueueProvider {
22
22
  constructor();
23
23
  enqueue<T = any>(queueName: string, jobName: string, data: T, options?: any): Promise<string>;
24
24
  registerWorker<T = any>(queueName: string, handler: (job: QueueJob) => Promise<void>): void;
25
+ /**
26
+ * Verifies QStash signature for incoming webhook deliveries.
27
+ * QStash passes `upstash-signature` header.
28
+ */
29
+ verifySignature(request: Request): Promise<boolean>;
25
30
  /**
26
31
  * Process a QStash webhook delivery. Called by the /_queue/:name route.
27
32
  */
28
- processDelivery(queueName: string, body: any): Promise<void>;
33
+ processDelivery(queueName: string, body: any, request?: Request): Promise<void>;
29
34
  startWorkers(): void;
30
35
  }
31
36
  export {};
@@ -19,5 +19,5 @@ export declare class VercelStorageProvider implements StorageProvider {
19
19
  saveFile(file: File, options?: {
20
20
  filename?: string;
21
21
  }): Promise<UploadedFile>;
22
- deleteFile(filename: string): Promise<void>;
22
+ deleteFile(filenameOrUrl: string): Promise<void>;
23
23
  }
@@ -122,7 +122,16 @@ var log = null;
122
122
  var init_memory = () => {};
123
123
 
124
124
  // shared/utils/kv/adapters/sqlite.ts
125
- import { Database } from "bun:sqlite";
125
+ function getBunDatabase() {
126
+ if (!BunDatabase) {
127
+ try {
128
+ BunDatabase = __require("bun:sqlite").Database;
129
+ } catch {
130
+ throw new Error("[Cequre KV (SQLite)] SQLite adapter requires Bun runtime (bun:sqlite).");
131
+ }
132
+ }
133
+ return BunDatabase;
134
+ }
126
135
  function getLog2() {
127
136
  if (!log2) {
128
137
  const { createLogger } = (init_logger(), __toCommonJS(exports_logger));
@@ -144,6 +153,7 @@ class SQLiteAdapter {
144
153
  stmtCount;
145
154
  constructor(options) {
146
155
  const dbPath = options?.path ?? "./data/cequre-kv.sqlite";
156
+ const DB = getBunDatabase();
147
157
  if (dbPath !== ":memory:") {
148
158
  const dir = dbPath.substring(0, dbPath.lastIndexOf("/"));
149
159
  if (dir) {
@@ -153,7 +163,7 @@ class SQLiteAdapter {
153
163
  } catch {}
154
164
  }
155
165
  }
156
- this.db = new Database(dbPath);
166
+ this.db = new DB(dbPath);
157
167
  this.db.run("PRAGMA journal_mode = WAL");
158
168
  this.db.run("PRAGMA synchronous = NORMAL");
159
169
  this.db.run(`
@@ -255,7 +265,7 @@ class SQLiteAdapter {
255
265
  getLog2().debug("SQLite KV adapter closed");
256
266
  }
257
267
  }
258
- var log2 = null;
268
+ var BunDatabase = null, log2 = null;
259
269
  var init_sqlite = () => {};
260
270
 
261
271
  // adapters/bun/bun-redis-cache.ts
@@ -552,7 +562,7 @@ function getErrorKV() {
552
562
  return kvStore;
553
563
  }
554
564
  function createStream() {
555
- if (Bun.env.NODE_ENV !== "production" && Bun.env.NODE_ENV !== "test") {
565
+ if (env.NODE_ENV !== "production" && env.NODE_ENV !== "test") {
556
566
  return pretty({
557
567
  colorize: true,
558
568
  translateTime: "yyyy-mm-dd HH:MM:ss.l",
@@ -638,11 +648,12 @@ function createLogger(component) {
638
648
  }
639
649
  return logger.child({ component });
640
650
  }
641
- var LOG_LEVEL, ERROR_LOG_KV, kvStore = null, consoleStream, logger;
651
+ var env, LOG_LEVEL, ERROR_LOG_KV, kvStore = null, consoleStream, logger;
642
652
  var init_logger = __esm(() => {
643
653
  init_kv();
644
- LOG_LEVEL = Bun.env.CEQURE_LOG_LEVEL || Bun.env.LOG_LEVEL || (Bun.env.NODE_ENV === "test" ? "silent" : "info");
645
- ERROR_LOG_KV = Bun.env.CEQURE_ERROR_LOG_KV === "true";
654
+ env = typeof process !== "undefined" ? process.env : typeof Bun !== "undefined" ? Bun.env : {};
655
+ LOG_LEVEL = env.CEQURE_LOG_LEVEL || env.LOG_LEVEL || (env.NODE_ENV === "test" ? "silent" : "info");
656
+ ERROR_LOG_KV = env.CEQURE_ERROR_LOG_KV === "true";
646
657
  consoleStream = createStream();
647
658
  logger = consoleStream ? pino({ name: "cequre", level: LOG_LEVEL }, consoleStream) : pino({ name: "cequre", level: LOG_LEVEL });
648
659
  if (ERROR_LOG_KV) {
@@ -5,7 +5,7 @@ import {
5
5
  import {
6
6
  init_logger,
7
7
  logger
8
- } from "./index-8bhn0nb4.js";
8
+ } from "./index-6vf0r9nh.js";
9
9
 
10
10
  // shared/utils/queue-manager.ts
11
11
  import { Queue as BullQueue, Worker as BullWorker, QueueEvents as BullQueueEvents } from "bullmq";
package/dist/index.js CHANGED
@@ -31,7 +31,7 @@ import {
31
31
  } from "./index-mfqj7cwr.js";
32
32
  import {
33
33
  CequreQueueManager
34
- } from "./index-7xxm1add.js";
34
+ } from "./index-z2xccrdm.js";
35
35
  import {
36
36
  CequreError,
37
37
  buildErrorResponse,
@@ -48,7 +48,7 @@ import {
48
48
  init_kv,
49
49
  init_logger,
50
50
  logger
51
- } from "./index-8bhn0nb4.js";
51
+ } from "./index-6vf0r9nh.js";
52
52
  import {
53
53
  __require,
54
54
  __toCommonJS,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cequrebackends/cequre-ts",
3
- "version": "0.14.0",
3
+ "version": "0.15.0",
4
4
  "description": "The Cequre Universal Runtime Engine",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -48,21 +48,21 @@
48
48
  "build": "rm -rf dist && bun build ./index.ts ./shared/core/index.ts ./adapters/bun/index.ts ./adapters/node/index.ts ./adapters/vercel/index.ts --outdir ./dist --target bun --packages external --splitting --format esm && tsc --emitDeclarationOnly"
49
49
  },
50
50
  "dependencies": {
51
- "@sinclair/typebox": "^0.34.49",
52
- "bullmq": "^5.79.2",
53
- "effect": "^3.21.4",
54
- "ioredis": "^5.4.1",
55
- "jose": "^6.2.3",
56
- "node-cron": "^3.0.3",
51
+ "@sinclair/typebox": "^0.34.52",
52
+ "bullmq": "^5.81.2",
53
+ "effect": "^3.22.0",
54
+ "ioredis": "^5.11.1",
55
+ "jose": "^6.2.4",
56
+ "node-cron": "^4.6.0",
57
57
  "pino": "^10.3.1",
58
58
  "pino-pretty": "^13.1.3",
59
- "ws": "^8.18.0"
59
+ "ws": "^8.21.1"
60
60
  },
61
61
  "devDependencies": {
62
- "@types/node": "^20.12.0",
62
+ "@types/node": "^26.1.1",
63
63
  "@types/node-cron": "^3.0.11",
64
- "@types/ws": "^8.5.12",
64
+ "@types/ws": "^8.18.1",
65
65
  "openapi-typescript": "^7.13.0",
66
- "typescript": "^6.0.3"
66
+ "typescript": "^7.0.2"
67
67
  }
68
68
  }