hybridq 0.1.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.
- package/LICENSE +674 -0
- package/README.md +165 -0
- package/dist/client/index.cjs +92 -0
- package/dist/client/index.cjs.map +1 -0
- package/dist/client/index.d.cts +44 -0
- package/dist/client/index.d.ts +44 -0
- package/dist/client/index.js +90 -0
- package/dist/client/index.js.map +1 -0
- package/dist/server/index.cjs +604 -0
- package/dist/server/index.cjs.map +1 -0
- package/dist/server/index.d.cts +463 -0
- package/dist/server/index.d.ts +463 -0
- package/dist/server/index.js +584 -0
- package/dist/server/index.js.map +1 -0
- package/package.json +83 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/storage/memory.ts","../../src/storage/redis.ts","../../src/engine/processor.ts","../../src/util/id.ts","../../src/engine/lock.ts","../../src/server/queue.ts","../../src/security/crypto.ts","../../src/server/middleware.ts"],"names":["_enc","job","hit"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * In-memory storage adapter.\n *\n * For local dev and tests only — state lives in the process, so it does NOT\n * survive across serverless invocations. The locking/lease semantics mirror\n * the Redis adapter so handler code behaves identically in both.\n */\nimport type { Job, StorageAdapter } from \"../types.js\";\nimport type { PayloadCipher } from \"../security/crypto.js\";\n\ninterface StoredJob extends Job {\n /** Encrypted-at-rest payload envelope, when a cipher is configured. */\n _enc?: string;\n}\n\nexport interface MemoryAdapterOptions {\n /** Optional cipher to encrypt payloads at rest, matching prod behaviour. */\n cipher?: PayloadCipher | null;\n}\n\nexport class MemoryAdapter implements StorageAdapter {\n private readonly queues = new Map<string, StoredJob[]>();\n private readonly cipher: PayloadCipher | null;\n\n constructor(opts: MemoryAdapterOptions = {}) {\n this.cipher = opts.cipher ?? null;\n }\n\n private bucket(queue: string): StoredJob[] {\n let b = this.queues.get(queue);\n if (!b) this.queues.set(queue, (b = []));\n return b;\n }\n\n private seal(job: Job): StoredJob {\n if (!this.cipher) return { ...job };\n const enc = this.cipher.encrypt(JSON.stringify(job.payload));\n return { ...job, payload: null, _enc: enc };\n }\n\n private open(stored: StoredJob): Job {\n if (!this.cipher || !stored._enc) {\n const { _enc, ...job } = stored;\n void _enc;\n return job;\n }\n const payload = JSON.parse(this.cipher.decrypt(stored._enc));\n const { _enc, ...job } = stored;\n void _enc;\n return { ...job, payload };\n }\n\n async push(job: Job): Promise<void> {\n this.bucket(job.queue).push(this.seal(job));\n }\n\n async shiftBatch(\n queue: string,\n count: number,\n leaseMs: number,\n ): Promise<Job[]> {\n const now = Date.now();\n const bucket = this.bucket(queue);\n const claimed: Job[] = [];\n\n for (const stored of bucket) {\n if (claimed.length >= count) break;\n const eligible =\n (stored.status === \"pending\" || stored.status === \"delayed\") &&\n stored.runAt <= now;\n // Reclaim jobs whose lease lapsed (the prior runner died mid-flight).\n const lapsed =\n stored.status === \"active\" &&\n stored.leaseUntil !== undefined &&\n stored.leaseUntil <= now;\n if (!eligible && !lapsed) continue;\n\n stored.status = \"active\";\n stored.attempts += 1;\n stored.leaseUntil = now + leaseMs;\n claimed.push(this.open(stored));\n }\n return claimed;\n }\n\n async complete(queue: string, jobId: string): Promise<void> {\n const bucket = this.bucket(queue);\n const i = bucket.findIndex((j) => j.id === jobId);\n if (i !== -1) bucket.splice(i, 1);\n }\n\n async fail(\n queue: string,\n jobId: string,\n error: string,\n retry: boolean,\n backoffMs: number,\n ): Promise<void> {\n const job = this.bucket(queue).find((j) => j.id === jobId);\n if (!job) return;\n job.lastError = error;\n job.leaseUntil = undefined;\n if (retry && job.attempts < job.maxAttempts) {\n job.status = \"pending\";\n job.runAt = Date.now() + backoffMs;\n } else {\n job.status = \"failed\";\n }\n }\n\n async release(queue: string, jobId: string): Promise<void> {\n const job = this.bucket(queue).find((j) => j.id === jobId);\n if (!job || job.status !== \"active\") return;\n job.status = \"pending\";\n job.leaseUntil = undefined;\n // Releasing for budget reasons must not burn a retry attempt.\n job.attempts = Math.max(0, job.attempts - 1);\n }\n\n async size(queue: string): Promise<number> {\n const now = Date.now();\n return this.bucket(queue).filter(\n (j) =>\n (j.status === \"pending\" || j.status === \"delayed\") && j.runAt <= now,\n ).length;\n }\n}\n","/**\n * Serverless-optimized Redis storage adapter.\n *\n * Works with either:\n * - `@upstash/redis` (HTTP/fetch — ideal for edge & connectionless runtimes)\n * - `ioredis` (TCP — uses a module-level singleton so we don't open a\n * new socket on every cold-ish invocation)\n *\n * Both clients differ in their `eval` signature, so we normalize them behind a\n * tiny `RedisDriver` and do all the claim logic in a single atomic Lua script.\n *\n * Data model (per queue `q`):\n * hq:{q}:pending LIST — ids waiting to be claimed, FIFO\n * hq:{q}:delayed ZSET — id -> runAt (promoted to pending when due)\n * hq:{q}:active ZSET — id -> leaseUntil (reclaimed when the lease lapses)\n * hq:{q}:jobs HASH — id -> envelope JSON (see JobEnvelope)\n *\n * The user payload is stored as an opaque string field (`payloadJson`) so the\n * server-side Lua `cjson` round-trip can never mangle nested structures.\n */\nimport type { Job, StorageAdapter } from \"../types.js\";\nimport type { PayloadCipher } from \"../security/crypto.js\";\n\n/** Minimal normalized Redis surface the adapter needs. */\nexport interface RedisDriver {\n eval(\n script: string,\n keys: string[],\n args: (string | number)[],\n ): Promise<unknown>;\n hget(key: string, field: string): Promise<string | null>;\n hset(key: string, field: string, value: string): Promise<unknown>;\n hdel(key: string, field: string): Promise<unknown>;\n rpush(key: string, value: string): Promise<unknown>;\n zadd(key: string, score: number, member: string): Promise<unknown>;\n zrem(key: string, member: string): Promise<unknown>;\n llen(key: string): Promise<number>;\n}\n\n/** On-disk envelope. `payloadJson` is opaque: plaintext JSON or cipher text. */\ninterface JobEnvelope {\n id: string;\n queue: string;\n status: Job[\"status\"];\n attempts: number;\n maxAttempts: number;\n runAt: number;\n createdAt: number;\n leaseUntil?: number;\n lastError?: string;\n payloadJson: string;\n enc: boolean;\n}\n\nexport interface RedisAdapterOptions {\n /** Optional namespace prefix (default \"hq\"). Lets you isolate apps/envs. */\n namespace?: string;\n /** Optional cipher for payload-at-rest encryption. */\n cipher?: PayloadCipher | null;\n}\n\n/**\n * Atomic claim: promote due delayed jobs, reclaim lapsed leases, then pop up to\n * `count` ids, mark them active with a fresh lease, and return their envelopes.\n * Doing this in one round trip is what gives us cross-request idempotency — two\n * concurrent triggers can never claim the same id.\n */\nconst CLAIM_LUA = `\nlocal pending = KEYS[1]\nlocal delayed = KEYS[2]\nlocal active = KEYS[3]\nlocal jobs = KEYS[4]\nlocal now = tonumber(ARGV[1])\nlocal count = tonumber(ARGV[2])\nlocal lease = tonumber(ARGV[3])\n\nlocal due = redis.call('ZRANGEBYSCORE', delayed, '-inf', now)\nfor _, id in ipairs(due) do\n redis.call('RPUSH', pending, id)\n redis.call('ZREM', delayed, id)\nend\n\nlocal lapsed = redis.call('ZRANGEBYSCORE', active, '-inf', now)\nfor _, id in ipairs(lapsed) do\n redis.call('RPUSH', pending, id)\n redis.call('ZREM', active, id)\nend\n\nlocal out = {}\nfor i = 1, count do\n local id = redis.call('LPOP', pending)\n if not id then break end\n local raw = redis.call('HGET', jobs, id)\n if raw then\n local env = cjson.decode(raw)\n env.status = 'active'\n env.attempts = (env.attempts or 0) + 1\n env.leaseUntil = now + lease\n local enc = cjson.encode(env)\n redis.call('HSET', jobs, id, enc)\n redis.call('ZADD', active, env.leaseUntil, id)\n table.insert(out, enc)\n end\nend\nreturn out\n`;\n\nexport class RedisAdapter implements StorageAdapter {\n private readonly ns: string;\n private readonly cipher: PayloadCipher | null;\n\n constructor(\n private readonly redis: RedisDriver,\n opts: RedisAdapterOptions = {},\n ) {\n this.ns = opts.namespace ?? \"hq\";\n this.cipher = opts.cipher ?? null;\n }\n\n private k(queue: string, suffix: string): string {\n return `${this.ns}:${queue}:${suffix}`;\n }\n\n private toEnvelope(job: Job): JobEnvelope {\n const plain = JSON.stringify(job.payload ?? null);\n const enc = !!this.cipher;\n return {\n id: job.id,\n queue: job.queue,\n status: job.status,\n attempts: job.attempts,\n maxAttempts: job.maxAttempts,\n runAt: job.runAt,\n createdAt: job.createdAt,\n leaseUntil: job.leaseUntil,\n lastError: job.lastError,\n payloadJson: enc ? this.cipher!.encrypt(plain) : plain,\n enc,\n };\n }\n\n private fromEnvelope(env: JobEnvelope): Job {\n const plain = env.enc\n ? this.cipher\n ? this.cipher.decrypt(env.payloadJson)\n : (() => {\n throw new Error(\n \"hybridq: job is encrypted but no cipher is configured\",\n );\n })()\n : env.payloadJson;\n return {\n id: env.id,\n queue: env.queue,\n payload: JSON.parse(plain),\n status: env.status,\n attempts: env.attempts,\n maxAttempts: env.maxAttempts,\n runAt: env.runAt,\n createdAt: env.createdAt,\n leaseUntil: env.leaseUntil,\n lastError: env.lastError,\n };\n }\n\n async push(job: Job): Promise<void> {\n const env = this.toEnvelope(job);\n const raw = JSON.stringify(env);\n await this.redis.hset(this.k(job.queue, \"jobs\"), job.id, raw);\n if (job.runAt > Date.now()) {\n await this.redis.zadd(this.k(job.queue, \"delayed\"), job.runAt, job.id);\n } else {\n await this.redis.rpush(this.k(job.queue, \"pending\"), job.id);\n }\n }\n\n async shiftBatch(\n queue: string,\n count: number,\n leaseMs: number,\n ): Promise<Job[]> {\n const res = (await this.redis.eval(\n CLAIM_LUA,\n [\n this.k(queue, \"pending\"),\n this.k(queue, \"delayed\"),\n this.k(queue, \"active\"),\n this.k(queue, \"jobs\"),\n ],\n [Date.now(), count, leaseMs],\n )) as string[] | null;\n if (!res || res.length === 0) return [];\n return res.map((raw) => this.fromEnvelope(JSON.parse(raw) as JobEnvelope));\n }\n\n async complete(queue: string, jobId: string): Promise<void> {\n await this.redis.zrem(this.k(queue, \"active\"), jobId);\n await this.redis.hdel(this.k(queue, \"jobs\"), jobId);\n }\n\n async fail(\n queue: string,\n jobId: string,\n error: string,\n retry: boolean,\n backoffMs: number,\n ): Promise<void> {\n const raw = await this.redis.hget(this.k(queue, \"jobs\"), jobId);\n if (!raw) return;\n const env = JSON.parse(raw) as JobEnvelope;\n env.lastError = error;\n env.leaseUntil = undefined;\n await this.redis.zrem(this.k(queue, \"active\"), jobId);\n\n if (retry && env.attempts < env.maxAttempts) {\n env.status = \"pending\";\n env.runAt = Date.now() + backoffMs;\n await this.redis.hset(this.k(queue, \"jobs\"), jobId, JSON.stringify(env));\n if (backoffMs > 0) {\n await this.redis.zadd(this.k(queue, \"delayed\"), env.runAt, jobId);\n } else {\n await this.redis.rpush(this.k(queue, \"pending\"), jobId);\n }\n } else {\n env.status = \"failed\";\n // Keep failed envelopes for inspection; drop from working sets.\n await this.redis.hset(this.k(queue, \"jobs\"), jobId, JSON.stringify(env));\n }\n }\n\n async release(queue: string, jobId: string): Promise<void> {\n const raw = await this.redis.hget(this.k(queue, \"jobs\"), jobId);\n if (!raw) return;\n const env = JSON.parse(raw) as JobEnvelope;\n if (env.status !== \"active\") return;\n env.status = \"pending\";\n env.leaseUntil = undefined;\n // Budget releases must not consume a retry attempt.\n env.attempts = Math.max(0, env.attempts - 1);\n await this.redis.zrem(this.k(queue, \"active\"), jobId);\n await this.redis.hset(this.k(queue, \"jobs\"), jobId, JSON.stringify(env));\n await this.redis.rpush(this.k(queue, \"pending\"), jobId);\n }\n\n async size(queue: string): Promise<number> {\n return this.redis.llen(this.k(queue, \"pending\"));\n }\n}\n\n/* ----------------------------- Driver factories ---------------------------- */\n\n/**\n * Wrap an `@upstash/redis` client. Connectionless (HTTP), so it's safe to\n * construct per request — no pooling needed.\n */\nexport function fromUpstash(client: {\n eval: (script: string, keys: string[], args: unknown[]) => Promise<unknown>;\n hget: (k: string, f: string) => Promise<string | null>;\n hset: (k: string, v: Record<string, string>) => Promise<unknown>;\n hdel: (k: string, f: string) => Promise<unknown>;\n rpush: (k: string, ...v: string[]) => Promise<unknown>;\n zadd: (k: string, m: { score: number; member: string }) => Promise<unknown>;\n zrem: (k: string, m: string) => Promise<unknown>;\n llen: (k: string) => Promise<number>;\n}): RedisDriver {\n return {\n eval: (script, keys, args) => client.eval(script, keys, args),\n hget: (k, f) => client.hget(k, f),\n hset: (k, f, v) => client.hset(k, { [f]: v }),\n hdel: (k, f) => client.hdel(k, f),\n rpush: (k, v) => client.rpush(k, v),\n zadd: (k, score, member) => client.zadd(k, { score, member }),\n zrem: (k, m) => client.zrem(k, m),\n llen: (k) => client.llen(k),\n };\n}\n\n/**\n * Wrap an `ioredis` client. ioredis opens a real TCP socket, so callers should\n * reuse ONE client across invocations — see `getSharedIORedis` below.\n */\nexport function fromIORedis(client: {\n eval: (\n script: string,\n numKeys: number,\n ...rest: (string | number)[]\n ) => Promise<unknown>;\n hget: (k: string, f: string) => Promise<string | null>;\n hset: (k: string, f: string, v: string) => Promise<unknown>;\n hdel: (k: string, f: string) => Promise<unknown>;\n rpush: (k: string, v: string) => Promise<unknown>;\n zadd: (k: string, score: number, member: string) => Promise<unknown>;\n zrem: (k: string, m: string) => Promise<unknown>;\n llen: (k: string) => Promise<number>;\n}): RedisDriver {\n return {\n eval: (script, keys, args) =>\n client.eval(script, keys.length, ...keys, ...args),\n hget: (k, f) => client.hget(k, f),\n hset: (k, f, v) => client.hset(k, f, v),\n hdel: (k, f) => client.hdel(k, f),\n rpush: (k, v) => client.rpush(k, v),\n zadd: (k, score, member) => client.zadd(k, score, member),\n zrem: (k, m) => client.zrem(k, m),\n llen: (k) => client.llen(k),\n };\n}\n\n/**\n * Connection-pool-safe ioredis singleton for serverless. Stashes the client on\n * `globalThis` so warm invocations reuse the same socket instead of exhausting\n * Redis connections under load.\n */\nexport function getSharedIORedis(\n factory: () => RedisDriver,\n key = \"__hybridq_ioredis__\",\n): RedisDriver {\n const g = globalThis as Record<string, unknown>;\n if (!g[key]) g[key] = factory();\n return g[key] as RedisDriver;\n}\n","/**\n * The processing engine.\n *\n * A trigger calls `drain()`. It claims jobs in batches and runs the handler,\n * checking the budget *between every job*. The moment the time or job-count\n * (or optional CPU) budget is exhausted it stops claiming, releases any jobs it\n * grabbed-but-didn't-run back to the queue, and returns — guaranteeing the\n * serverless function never blows past its wall-clock limit.\n *\n * Crash-safety comes from the lease model in the adapter: anything claimed but\n * not completed/failed/released simply becomes reclaimable once its lease\n * lapses, so a killed function loses no work.\n */\nimport type {\n BudgetConfig,\n DrainReport,\n Job,\n JobHandler,\n StorageAdapter,\n} from \"../types.js\";\nimport type { DistributedLock } from \"./lock.js\";\n\nexport interface DrainOptions<TPayload = unknown> {\n queue: string;\n adapter: StorageAdapter;\n handler: JobHandler<TPayload>;\n budget: BudgetConfig;\n /** Optional single-flight lock so concurrent triggers don't double-drain. */\n lock?: DistributedLock;\n /** Jobs claimed per round trip to the store. Default 10. */\n batchSize?: number;\n /**\n * Lease length for claimed jobs. Should exceed how long one job can take.\n * Defaults to `maxExecutionTimeMs` so a whole run is covered by one lease.\n */\n leaseMs?: number;\n /** Retry backoff in ms given the attempt number (1-based). */\n backoff?: (attempt: number) => number;\n /** Observability hook for handler errors. Never throws into the loop. */\n onError?: (err: unknown, job: Job<TPayload>) => void;\n}\n\n/** Default exponential backoff: 1s, 2s, 4s, … capped at 30s. */\nconst defaultBackoff = (attempt: number): number =>\n Math.min(30_000, 1000 * 2 ** Math.max(0, attempt - 1));\n\n/** Best-effort CPU utilisation sampler (Node only). */\nfunction cpuSampler(): () => number {\n const proc = (\n globalThis as { process?: { cpuUsage?: (p?: unknown) => unknown } }\n ).process;\n if (!proc?.cpuUsage) return () => 0;\n const startCpu = proc.cpuUsage() as { user: number; system: number };\n const startWall = Date.now();\n return () => {\n const d = proc.cpuUsage!(startCpu) as { user: number; system: number };\n const wallUs = Math.max(1, (Date.now() - startWall) * 1000);\n return ((d.user + d.system) / wallUs) * 100;\n };\n}\n\nexport async function drain<TPayload = unknown>(\n opts: DrainOptions<TPayload>,\n): Promise<DrainReport> {\n const {\n queue,\n adapter,\n handler,\n budget,\n lock,\n batchSize = 10,\n leaseMs = budget.maxExecutionTimeMs,\n backoff = defaultBackoff,\n onError,\n } = opts;\n\n const startedAt = Date.now();\n const elapsed = () => Date.now() - startedAt;\n const sampleCpu = cpuSampler();\n\n let processed = 0;\n let failed = 0;\n let released = 0;\n let stoppedBy: DrainReport[\"stoppedBy\"] = \"drained\";\n\n // ---- Reentrancy guard: only one drain per queue at a time. -------------\n const lockKey = `${queue}:drain-lock`;\n let lockToken: string | null = null;\n if (lock) {\n // TTL covers the run plus a margin so a slow run keeps its lock.\n lockToken = await lock.acquire(lockKey, budget.maxExecutionTimeMs + 2000);\n if (!lockToken) {\n return { processed, failed, released, stoppedBy: \"lockBusy\", elapsedMs: 0 };\n }\n }\n\n const done = () => processed + failed; // jobs we've fully accounted for\n\n /** True when any budget says \"stop starting new work\". */\n const budgetExhausted = (): DrainReport[\"stoppedBy\"] | null => {\n if (done() >= budget.maxJobsPerTrigger) return \"jobCap\";\n if (elapsed() >= budget.maxExecutionTimeMs) return \"timeBudget\";\n if (\n budget.maxCpuBudgetPct !== undefined &&\n sampleCpu() >= budget.maxCpuBudgetPct\n ) {\n return \"cpuBudget\";\n }\n return null;\n };\n\n try {\n outer: while (true) {\n const hit = budgetExhausted();\n if (hit) {\n stoppedBy = hit;\n break;\n }\n\n const remainingByCount = budget.maxJobsPerTrigger - done();\n const claimCount = Math.max(1, Math.min(batchSize, remainingByCount));\n const batch = await adapter.shiftBatch(queue, claimCount, leaseMs);\n\n if (batch.length === 0) {\n stoppedBy = \"drained\";\n break;\n }\n\n for (let i = 0; i < batch.length; i++) {\n const job = batch[i] as Job<TPayload>;\n\n // Re-check the budget before *each* job. If exhausted, hand back this\n // job and everything after it untouched (no attempt consumed).\n const hit = budgetExhausted();\n if (hit) {\n for (let j = i; j < batch.length; j++) {\n await adapter.release(queue, batch[j]!.id);\n released++;\n }\n stoppedBy = hit;\n break outer;\n }\n\n try {\n await handler(job);\n await adapter.complete(queue, job.id);\n processed++;\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n onError?.(err, job);\n await adapter.fail(\n queue,\n job.id,\n message,\n /* retry */ true,\n backoff(job.attempts),\n );\n failed++;\n }\n }\n }\n } finally {\n if (lock && lockToken) await lock.release(lockKey, lockToken);\n }\n\n return { processed, failed, released, stoppedBy, elapsedMs: elapsed() };\n}\n","/**\n * Small id helper. Prefers crypto.randomUUID where present (Node 18+ and all\n * serverless edge runtimes), with a cheap fallback for exotic environments.\n */\nexport function newId(prefix = \"job\"): string {\n const g = globalThis as { crypto?: { randomUUID?: () => string } };\n const uuid =\n g.crypto?.randomUUID?.() ??\n `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;\n return `${prefix}_${uuid}`;\n}\n","/**\n * Distributed lock used to guarantee single-flight processing.\n *\n * When several inbound requests fire a trigger at the same instant we don't\n * want N concurrent drain loops fighting over the same jobs. Each run first\n * tries to grab a short-TTL lock; losers exit immediately (the winner is\n * already draining). The TTL is short and auto-expires, so a crashed run never\n * wedges the queue.\n */\nimport type { RedisDriver } from \"../storage/redis.js\";\nimport { newId } from \"../util/id.js\";\n\nexport interface DistributedLock {\n /** Try to acquire `key` for `ttlMs`. Returns a release token, or null. */\n acquire(key: string, ttlMs: number): Promise<string | null>;\n /** Release `key` only if we still own it (compare-and-delete). */\n release(key: string, token: string): Promise<void>;\n}\n\nconst ACQUIRE_LUA = `return redis.call('SET', KEYS[1], ARGV[1], 'NX', 'PX', ARGV[2])`;\nconst RELEASE_LUA = `\nif redis.call('GET', KEYS[1]) == ARGV[1] then\n return redis.call('DEL', KEYS[1])\nend\nreturn 0`;\n\n/** Redis-backed lock (SET NX PX + compare-and-delete). */\nexport class RedisLock implements DistributedLock {\n constructor(private readonly redis: RedisDriver) {}\n\n async acquire(key: string, ttlMs: number): Promise<string | null> {\n const token = newId(\"lock\");\n const res = await this.redis.eval(ACQUIRE_LUA, [key], [token, ttlMs]);\n return res === \"OK\" || res === true ? token : null;\n }\n\n async release(key: string, token: string): Promise<void> {\n await this.redis.eval(RELEASE_LUA, [key], [token]);\n }\n}\n\n/** In-process lock for the MemoryAdapter / single-instance dev. */\nexport class MemoryLock implements DistributedLock {\n private readonly held = new Map<string, { token: string; until: number }>();\n\n async acquire(key: string, ttlMs: number): Promise<string | null> {\n const now = Date.now();\n const cur = this.held.get(key);\n if (cur && cur.until > now) return null;\n const token = newId(\"lock\");\n this.held.set(key, { token, until: now + ttlMs });\n return token;\n }\n\n async release(key: string, token: string): Promise<void> {\n const cur = this.held.get(key);\n if (cur && cur.token === token) this.held.delete(key);\n }\n}\n","/**\n * High-level Queue facade.\n *\n * Wires a storage adapter, optional lock, and the engine into an ergonomic\n * producer/consumer surface:\n * - `enqueue()` from any API route to defer work.\n * - `process()` from a trigger to drain a budgeted batch.\n */\nimport type {\n BudgetConfig,\n DrainReport,\n Job,\n JobHandler,\n StorageAdapter,\n} from \"../types.js\";\nimport { drain } from \"../engine/processor.js\";\nimport type { DistributedLock } from \"../engine/lock.js\";\nimport { newId } from \"../util/id.js\";\n\nconst DEFAULT_BUDGET: BudgetConfig = {\n maxJobsPerTrigger: 25,\n maxExecutionTimeMs: 8000, // safe under a 10s serverless limit\n};\n\nexport interface QueueConfig {\n /** Logical queue name (a single store can host many). */\n name: string;\n /** Where jobs live. */\n adapter: StorageAdapter;\n /** Default execution budget; overridable per `process()` call. */\n budget?: Partial<BudgetConfig>;\n /** Single-flight lock to prevent concurrent drains double-processing. */\n lock?: DistributedLock;\n /** Default retry ceiling for enqueued jobs. Default 3. */\n defaultMaxAttempts?: number;\n /** Jobs claimed per round trip while draining. Default 10. */\n batchSize?: number;\n /** Retry backoff in ms for attempt N (1-based). */\n backoff?: (attempt: number) => number;\n}\n\nexport interface EnqueueOptions {\n /** Delay before the job becomes eligible to run. */\n delayMs?: number;\n /** Override retry ceiling for this job. */\n maxAttempts?: number;\n /**\n * Stable id → idempotency key. Re-enqueuing with the same id is a no-op at\n * the application layer (callers should treat enqueue as best-effort unique).\n */\n id?: string;\n}\n\nexport class Queue<TPayload = unknown> {\n private readonly budget: BudgetConfig;\n\n constructor(private readonly config: QueueConfig) {\n this.budget = { ...DEFAULT_BUDGET, ...config.budget };\n }\n\n get name(): string {\n return this.config.name;\n }\n\n /** Producer side: push a unit of work. Returns the created job. */\n async enqueue(\n payload: TPayload,\n opts: EnqueueOptions = {},\n ): Promise<Job<TPayload>> {\n const now = Date.now();\n const job: Job<TPayload> = {\n id: opts.id ?? newId(\"job\"),\n queue: this.config.name,\n payload,\n status: opts.delayMs && opts.delayMs > 0 ? \"delayed\" : \"pending\",\n attempts: 0,\n maxAttempts: opts.maxAttempts ?? this.config.defaultMaxAttempts ?? 3,\n runAt: now + (opts.delayMs ?? 0),\n createdAt: now,\n };\n await this.config.adapter.push(job as Job);\n return job;\n }\n\n /** How many jobs are currently claimable. Handy for trigger gating. */\n size(): Promise<number> {\n return this.config.adapter.size(this.config.name);\n }\n\n /**\n * Consumer side: drain a budgeted batch with the given handler. Safe to call\n * from many concurrent requests — the lock ensures only one actually runs.\n */\n process(\n handler: JobHandler<TPayload>,\n budgetOverride?: Partial<BudgetConfig>,\n ): Promise<DrainReport> {\n return drain<TPayload>({\n queue: this.config.name,\n adapter: this.config.adapter,\n handler,\n budget: { ...this.budget, ...budgetOverride },\n lock: this.config.lock,\n batchSize: this.config.batchSize,\n backoff: this.config.backoff,\n });\n }\n}\n\n/** Convenience factory. */\nexport function defineQueue<TPayload = unknown>(\n config: QueueConfig,\n): Queue<TPayload> {\n return new Queue<TPayload>(config);\n}\n","/**\n * Server-side cryptography helpers.\n *\n * Uses the Node `crypto` module (available on Vercel/Lambda Node runtimes and\n * on the Web Crypto-compatible Node API). Kept out of the client bundle.\n */\nimport {\n createCipheriv,\n createDecipheriv,\n createHmac,\n randomBytes,\n timingSafeEqual,\n} from \"node:crypto\";\n\nconst ALGO = \"aes-256-gcm\";\nconst IV_BYTES = 12;\nconst ENVELOPE_PREFIX = \"hqv1\"; // versioned so the format can evolve\n\n/**\n * Optional payload-at-rest encryption. When a secret is configured, payloads\n * are sealed with AES-256-GCM before they ever touch the store, so sensitive\n * data (emails, tokens) is never plaintext in Redis.\n */\nexport interface PayloadCipher {\n encrypt(plaintext: string): string;\n decrypt(envelope: string): string;\n}\n\n/** Derive a stable 32-byte key from an arbitrary-length secret. */\nfunction deriveKey(secret: string): Buffer {\n // HKDF-lite: a single HMAC-SHA256 round gives us a fixed 32-byte key.\n return createHmac(\"sha256\", \"hybridq:payload-key\").update(secret).digest();\n}\n\n/**\n * Build a cipher from a secret. Returns `null` when no secret is supplied so\n * callers can transparently no-op (store plaintext) in dev.\n */\nexport function createPayloadCipher(secret?: string): PayloadCipher | null {\n if (!secret) return null;\n const key = deriveKey(secret);\n\n return {\n encrypt(plaintext) {\n const iv = randomBytes(IV_BYTES);\n const cipher = createCipheriv(ALGO, key, iv);\n const ct = Buffer.concat([\n cipher.update(plaintext, \"utf8\"),\n cipher.final(),\n ]);\n const tag = cipher.getAuthTag();\n // envelope: prefix.iv.tag.ciphertext (all base64url)\n return [\n ENVELOPE_PREFIX,\n iv.toString(\"base64url\"),\n tag.toString(\"base64url\"),\n ct.toString(\"base64url\"),\n ].join(\".\");\n },\n\n decrypt(envelope) {\n const parts = envelope.split(\".\");\n if (parts.length !== 4 || parts[0] !== ENVELOPE_PREFIX) {\n throw new Error(\"hybridq: malformed encrypted payload envelope\");\n }\n const [, ivB64, tagB64, ctB64] = parts;\n const iv = Buffer.from(ivB64!, \"base64url\");\n const tag = Buffer.from(tagB64!, \"base64url\");\n const ct = Buffer.from(ctB64!, \"base64url\");\n const decipher = createDecipheriv(ALGO, key, iv);\n decipher.setAuthTag(tag);\n return Buffer.concat([decipher.update(ct), decipher.final()]).toString(\n \"utf8\",\n );\n },\n };\n}\n\n/** Constant-time string comparison that won't throw on length mismatch. */\nexport function safeEqual(a: string, b: string): boolean {\n const ab = Buffer.from(a);\n const bb = Buffer.from(b);\n if (ab.length !== bb.length) return false;\n return timingSafeEqual(ab, bb);\n}\n\n/**\n * HMAC trigger tokens. A trigger presents `t=<ts>,v=<sig>` where\n * sig = HMAC-SHA256(secret, `${ts}.${path}`). Tokens expire to blunt replay.\n */\nexport function signTrigger(\n secret: string,\n path: string,\n ts: number = Date.now(),\n): string {\n const sig = createHmac(\"sha256\", secret)\n .update(`${ts}.${path}`)\n .digest(\"base64url\");\n return `t=${ts},v=${sig}`;\n}\n\nexport function verifyTrigger(\n secret: string,\n path: string,\n token: string,\n maxSkewMs = 5 * 60_000,\n): boolean {\n const m = /^t=(\\d+),v=([A-Za-z0-9_-]+)$/.exec(token.trim());\n if (!m) return false;\n const ts = Number(m[1]);\n const sig = m[2]!;\n if (!Number.isFinite(ts) || Math.abs(Date.now() - ts) > maxSkewMs) {\n return false;\n }\n const expected = createHmac(\"sha256\", secret)\n .update(`${ts}.${path}`)\n .digest(\"base64url\");\n return safeEqual(sig, expected);\n}\n","/**\n * Hybrid trigger middleware.\n *\n * Two ways to kick off a drain without a dedicated worker daemon:\n *\n * 1. `withTrigger(handler)` — wraps a normal API route / webhook. It runs your\n * real handler, returns the response immediately, and drains the queue\n * *after* the response is flushed using the platform's \"keep the function\n * alive a bit longer\" primitive (`waitUntil`). Inbound traffic = free CPU.\n *\n * 2. `createTriggerEndpoint(...)` — a dedicated, authenticated endpoint that\n * client pings (see ../client/trigger) or a cron hits to force a drain.\n *\n * Both verify a shared secret (X-Worker-Trigger-Secret) or an HMAC token so a\n * stranger can't pound the endpoint and turn your worker into a money pit.\n */\nimport type { BudgetConfig, DrainReport, JobHandler } from \"../types.js\";\nimport type { Queue } from \"./queue.js\";\nimport { safeEqual, verifyTrigger } from \"../security/crypto.js\";\n\n/** Anything with `waitUntil` — Vercel/Cloudflare give you one per request. */\nexport interface ExecutionContextLike {\n waitUntil(promise: Promise<unknown>): void;\n}\n\nexport const TRIGGER_SECRET_HEADER = \"x-worker-trigger-secret\";\nexport const TRIGGER_TOKEN_HEADER = \"x-worker-trigger-token\";\n\nexport interface TriggerAuthConfig {\n /** Pre-shared secret compared in constant time against the header. */\n secret?: string;\n /** HMAC secret enabling short-lived signed tokens (replay-resistant). */\n hmacSecret?: string;\n /** Path bound into the HMAC signature. Default the request pathname. */\n path?: string;\n}\n\n/**\n * Verify a trigger request. Accepts EITHER a valid pre-shared secret header OR\n * a valid HMAC token. Returns true only when at least one configured method\n * passes — so an endpoint with no auth configured always rejects.\n */\nexport function authorizeTrigger(\n req: Request,\n auth: TriggerAuthConfig,\n): boolean {\n let configured = false;\n\n if (auth.secret) {\n configured = true;\n const provided = req.headers.get(TRIGGER_SECRET_HEADER);\n if (provided && safeEqual(provided, auth.secret)) return true;\n }\n\n if (auth.hmacSecret) {\n configured = true;\n const token = req.headers.get(TRIGGER_TOKEN_HEADER);\n const path = auth.path ?? new URL(req.url).pathname;\n if (token && verifyTrigger(auth.hmacSecret, path, token)) return true;\n }\n\n // Fail closed: never allow when nothing is configured or nothing matched.\n return configured ? false : false;\n}\n\n/**\n * Schedule a drain to run after the response is sent. Uses `waitUntil` when a\n * context is supplied (recommended); otherwise fires a detached promise as a\n * best-effort fallback (works on long-lived Node servers, not edge).\n */\nfunction deferDrain(\n run: () => Promise<DrainReport>,\n ctx?: ExecutionContextLike,\n): void {\n const p = run().catch(() => {\n /* swallow — draining must never break the main response */\n });\n if (ctx?.waitUntil) ctx.waitUntil(p);\n else void p; // detached; fine for persistent servers\n}\n\nexport interface WithTriggerOptions<TPayload> {\n queue: Queue<TPayload>;\n handler: JobHandler<TPayload>;\n /** Platform execution context exposing `waitUntil`. */\n ctx?: ExecutionContextLike;\n /** Optional per-trigger budget override. */\n budget?: Partial<BudgetConfig>;\n /** Skip draining when the queue is empty to avoid pointless work. */\n skipIfEmpty?: boolean;\n}\n\n/**\n * Wrap a normal request handler so each inbound request opportunistically\n * drains the queue *after* responding. This is the core \"hybrid trigger\".\n *\n * @example\n * export const POST = withTrigger(\n * async (req) => Response.json({ ok: true }),\n * { queue, handler: sendEmail, ctx },\n * );\n */\nexport function withTrigger<TPayload>(\n route: (req: Request) => Promise<Response> | Response,\n opts: WithTriggerOptions<TPayload>,\n): (req: Request) => Promise<Response> {\n return async (req: Request): Promise<Response> => {\n const res = await route(req); // produce the real response first\n deferDrain(async () => {\n if (opts.skipIfEmpty && (await opts.queue.size()) === 0) {\n return {\n processed: 0,\n failed: 0,\n released: 0,\n stoppedBy: \"drained\" as const,\n elapsedMs: 0,\n };\n }\n return opts.queue.process(opts.handler, opts.budget);\n }, opts.ctx);\n return res;\n };\n}\n\nexport interface TriggerEndpointOptions<TPayload> {\n queue: Queue<TPayload>;\n handler: JobHandler<TPayload>;\n auth: TriggerAuthConfig;\n ctx?: ExecutionContextLike;\n budget?: Partial<BudgetConfig>;\n /**\n * When true (default) the drain runs in the background via waitUntil and the\n * endpoint returns 202 immediately. Set false to await the drain and return\n * the full report (useful for cron jobs that want the result).\n */\n background?: boolean;\n}\n\n/**\n * Build a standalone authenticated trigger endpoint (Fetch-API style). Mount it\n * at e.g. `app/api/_worker/route.ts` and point client pings / cron at it.\n */\nexport function createTriggerEndpoint<TPayload>(\n opts: TriggerEndpointOptions<TPayload>,\n): (req: Request) => Promise<Response> {\n const background = opts.background ?? true;\n\n return async (req: Request): Promise<Response> => {\n if (!authorizeTrigger(req, opts.auth)) {\n return new Response(\"Forbidden\", { status: 403 });\n }\n\n if (background) {\n deferDrain(() => opts.queue.process(opts.handler, opts.budget), opts.ctx);\n return new Response(JSON.stringify({ accepted: true }), {\n status: 202,\n headers: { \"content-type\": \"application/json\" },\n });\n }\n\n const report = await opts.queue.process(opts.handler, opts.budget);\n return new Response(JSON.stringify(report), {\n status: 200,\n headers: { \"content-type\": \"application/json\" },\n });\n };\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "hybridq",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Lightweight embedded hybrid background worker for serverless. Drain batched, deferred tasks using incoming traffic — no persistent worker server required.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Samsonchim",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"sideEffects": false,
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"README.md",
|
|
12
|
+
"LICENSE"
|
|
13
|
+
],
|
|
14
|
+
"publishConfig": {
|
|
15
|
+
"access": "public"
|
|
16
|
+
},
|
|
17
|
+
"exports": {
|
|
18
|
+
"./server": {
|
|
19
|
+
"types": "./dist/server/index.d.ts",
|
|
20
|
+
"import": "./dist/server/index.js",
|
|
21
|
+
"require": "./dist/server/index.cjs"
|
|
22
|
+
},
|
|
23
|
+
"./client": {
|
|
24
|
+
"types": "./dist/client/index.d.ts",
|
|
25
|
+
"import": "./dist/client/index.js",
|
|
26
|
+
"require": "./dist/client/index.cjs"
|
|
27
|
+
},
|
|
28
|
+
"./package.json": "./package.json"
|
|
29
|
+
},
|
|
30
|
+
"typesVersions": {
|
|
31
|
+
"*": {
|
|
32
|
+
"server": [
|
|
33
|
+
"./dist/server/index.d.ts"
|
|
34
|
+
],
|
|
35
|
+
"client": [
|
|
36
|
+
"./dist/client/index.d.ts"
|
|
37
|
+
]
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"peerDependencies": {
|
|
41
|
+
"@upstash/redis": ">=1.28.0",
|
|
42
|
+
"ioredis": ">=5.0.0"
|
|
43
|
+
},
|
|
44
|
+
"peerDependenciesMeta": {
|
|
45
|
+
"@upstash/redis": {
|
|
46
|
+
"optional": true
|
|
47
|
+
},
|
|
48
|
+
"ioredis": {
|
|
49
|
+
"optional": true
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@types/node": "^22.10.0",
|
|
54
|
+
"@upstash/redis": "^1.34.0",
|
|
55
|
+
"ioredis": "^5.4.1",
|
|
56
|
+
"tsup": "^8.3.5",
|
|
57
|
+
"typescript": "^5.7.2",
|
|
58
|
+
"vitest": "^2.1.8"
|
|
59
|
+
},
|
|
60
|
+
"engines": {
|
|
61
|
+
"node": ">=18"
|
|
62
|
+
},
|
|
63
|
+
"keywords": [
|
|
64
|
+
"queue",
|
|
65
|
+
"background-jobs",
|
|
66
|
+
"serverless",
|
|
67
|
+
"vercel",
|
|
68
|
+
"cloudflare-workers",
|
|
69
|
+
"lambda",
|
|
70
|
+
"redis",
|
|
71
|
+
"upstash",
|
|
72
|
+
"deferred",
|
|
73
|
+
"batch",
|
|
74
|
+
"worker"
|
|
75
|
+
],
|
|
76
|
+
"scripts": {
|
|
77
|
+
"build": "tsup",
|
|
78
|
+
"dev": "tsup --watch",
|
|
79
|
+
"typecheck": "tsc --noEmit",
|
|
80
|
+
"test": "vitest run",
|
|
81
|
+
"lint": "tsc --noEmit"
|
|
82
|
+
}
|
|
83
|
+
}
|