cooper-stack 0.1.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.
- package/dist/ai.d.ts +60 -0
- package/dist/ai.d.ts.map +1 -0
- package/dist/ai.js +66 -0
- package/dist/ai.js.map +1 -0
- package/dist/api.d.ts +31 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +40 -0
- package/dist/api.js.map +1 -0
- package/dist/auth.d.ts +13 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +16 -0
- package/dist/auth.js.map +1 -0
- package/dist/bridge.d.ts +15 -0
- package/dist/bridge.d.ts.map +1 -0
- package/dist/bridge.js +217 -0
- package/dist/bridge.js.map +1 -0
- package/dist/cache.d.ts +27 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +69 -0
- package/dist/cache.js.map +1 -0
- package/dist/cron.d.ts +22 -0
- package/dist/cron.d.ts.map +1 -0
- package/dist/cron.js +26 -0
- package/dist/cron.js.map +1 -0
- package/dist/db.d.ts +46 -0
- package/dist/db.d.ts.map +1 -0
- package/dist/db.js +158 -0
- package/dist/db.js.map +1 -0
- package/dist/error.d.ts +18 -0
- package/dist/error.d.ts.map +1 -0
- package/dist/error.js +30 -0
- package/dist/error.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/islands.d.ts +16 -0
- package/dist/islands.d.ts.map +1 -0
- package/dist/islands.js +23 -0
- package/dist/islands.js.map +1 -0
- package/dist/middleware.d.ts +20 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/middleware.js +26 -0
- package/dist/middleware.js.map +1 -0
- package/dist/nats.d.ts +46 -0
- package/dist/nats.d.ts.map +1 -0
- package/dist/nats.js +157 -0
- package/dist/nats.js.map +1 -0
- package/dist/pubsub.d.ts +27 -0
- package/dist/pubsub.d.ts.map +1 -0
- package/dist/pubsub.js +152 -0
- package/dist/pubsub.js.map +1 -0
- package/dist/queue.d.ts +39 -0
- package/dist/queue.d.ts.map +1 -0
- package/dist/queue.js +298 -0
- package/dist/queue.js.map +1 -0
- package/dist/rateLimit.d.ts +29 -0
- package/dist/rateLimit.d.ts.map +1 -0
- package/dist/rateLimit.js +70 -0
- package/dist/rateLimit.js.map +1 -0
- package/dist/registry.d.ts +75 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +41 -0
- package/dist/registry.js.map +1 -0
- package/dist/secrets.d.ts +10 -0
- package/dist/secrets.d.ts.map +1 -0
- package/dist/secrets.js +35 -0
- package/dist/secrets.js.map +1 -0
- package/dist/ssr.d.ts +53 -0
- package/dist/ssr.d.ts.map +1 -0
- package/dist/ssr.js +39 -0
- package/dist/ssr.js.map +1 -0
- package/dist/storage.d.ts +28 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +61 -0
- package/dist/storage.js.map +1 -0
- package/package.json +40 -0
- package/src/ai.ts +99 -0
- package/src/api.ts +56 -0
- package/src/auth.ts +16 -0
- package/src/bridge.ts +267 -0
- package/src/cache.ts +86 -0
- package/src/cron.ts +32 -0
- package/src/db.ts +211 -0
- package/src/error.ts +44 -0
- package/src/index.ts +17 -0
- package/src/islands.ts +28 -0
- package/src/middleware.ts +27 -0
- package/src/nats.ts +186 -0
- package/src/pubsub.ts +208 -0
- package/src/queue.ts +414 -0
- package/src/rateLimit.ts +89 -0
- package/src/registry.ts +98 -0
- package/src/secrets.ts +40 -0
- package/src/ssr.ts +58 -0
- package/src/storage.ts +79 -0
- package/tsconfig.json +17 -0
package/dist/queue.js
ADDED
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
import { registry } from "./registry.js";
|
|
2
|
+
import { ensureConnected, getJetStream, getJetStreamManager, jsonCodec, consumerName, ensureQueueStream, ensureDLQStream, } from "./nats.js";
|
|
3
|
+
import { headers as natsHeaders, AckPolicy } from "nats";
|
|
4
|
+
const parseDuration = (dur) => {
|
|
5
|
+
const m = dur.match(/^(\d+)(s|m|h|d)$/);
|
|
6
|
+
if (!m)
|
|
7
|
+
return 0;
|
|
8
|
+
const mult = {
|
|
9
|
+
s: 1000,
|
|
10
|
+
m: 60000,
|
|
11
|
+
h: 3600000,
|
|
12
|
+
d: 86400000,
|
|
13
|
+
};
|
|
14
|
+
return parseInt(m[1]) * (mult[m[2]] ?? 1000);
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Declare a job queue.
|
|
18
|
+
*
|
|
19
|
+
* ```ts
|
|
20
|
+
* export const EmailQueue = queue<{ to: string; subject: string; body: string }>(
|
|
21
|
+
* "email-queue",
|
|
22
|
+
* { concurrency: 10, retries: 3, retryDelay: "exponential", timeout: "30s", deadLetter: "email-dlq" }
|
|
23
|
+
* );
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* Local dev uses NATS JetStream for persistent, durable queues.
|
|
27
|
+
* Falls back to in-memory if NATS is unavailable.
|
|
28
|
+
*/
|
|
29
|
+
export function queue(name, config) {
|
|
30
|
+
const subject = `cooper.queue.${name}`;
|
|
31
|
+
const concurrency = config?.concurrency ?? 1;
|
|
32
|
+
const maxRetries = config?.retries ?? 0;
|
|
33
|
+
const timeoutMs = config?.timeout ? parseDuration(config.timeout) : 0;
|
|
34
|
+
const dlqName = config?.deadLetter;
|
|
35
|
+
// In-memory fallback state
|
|
36
|
+
const memJobs = [];
|
|
37
|
+
let memProcessing = false;
|
|
38
|
+
let workerHandler = null;
|
|
39
|
+
let failureHandler = null;
|
|
40
|
+
let consumerStarted = false;
|
|
41
|
+
// Track retry attempts per message (NATS redelivers original message on NAK)
|
|
42
|
+
const attemptTracker = new Map();
|
|
43
|
+
// In-memory fallback processor
|
|
44
|
+
const processMemQueue = async () => {
|
|
45
|
+
if (memProcessing || !workerHandler)
|
|
46
|
+
return;
|
|
47
|
+
memProcessing = true;
|
|
48
|
+
while (memJobs.length > 0) {
|
|
49
|
+
const batch = memJobs.splice(0, concurrency);
|
|
50
|
+
await Promise.allSettled(batch.map(async (job) => {
|
|
51
|
+
if (job.scheduledAt > Date.now()) {
|
|
52
|
+
memJobs.push(job);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
await executeWithTimeout(workerHandler, job.data);
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
job.attempts++;
|
|
60
|
+
if (job.attempts <= maxRetries) {
|
|
61
|
+
const delay = config?.retryDelay === "exponential"
|
|
62
|
+
? Math.pow(2, job.attempts - 1) * 1000
|
|
63
|
+
: 1000;
|
|
64
|
+
job.scheduledAt = Date.now() + delay;
|
|
65
|
+
memJobs.push(job);
|
|
66
|
+
}
|
|
67
|
+
else if (failureHandler) {
|
|
68
|
+
await failureHandler(job.data, err);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}));
|
|
72
|
+
}
|
|
73
|
+
memProcessing = false;
|
|
74
|
+
};
|
|
75
|
+
async function executeWithTimeout(handler, data) {
|
|
76
|
+
if (!timeoutMs) {
|
|
77
|
+
return handler(data);
|
|
78
|
+
}
|
|
79
|
+
const result = await Promise.race([
|
|
80
|
+
handler(data),
|
|
81
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error(`Job timed out after ${config.timeout}`)), timeoutMs)),
|
|
82
|
+
]);
|
|
83
|
+
return result;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Start a JetStream consumer to process queue jobs.
|
|
87
|
+
*/
|
|
88
|
+
async function startConsumer(workerName, handler, onFailure) {
|
|
89
|
+
if (consumerStarted)
|
|
90
|
+
return;
|
|
91
|
+
consumerStarted = true;
|
|
92
|
+
const js = getJetStream();
|
|
93
|
+
const jsm = getJetStreamManager();
|
|
94
|
+
if (!js || !jsm)
|
|
95
|
+
return;
|
|
96
|
+
const streamName = "QUEUE_" + name.replace(/[^a-zA-Z0-9_-]/g, "_").toUpperCase();
|
|
97
|
+
const durable = consumerName(workerName);
|
|
98
|
+
// Ensure consumer exists
|
|
99
|
+
try {
|
|
100
|
+
await jsm.consumers.info(streamName, durable);
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
await jsm.consumers.add(streamName, {
|
|
104
|
+
durable_name: durable,
|
|
105
|
+
ack_policy: AckPolicy.Explicit,
|
|
106
|
+
max_ack_pending: concurrency,
|
|
107
|
+
filter_subject: subject,
|
|
108
|
+
// Redeliver unacked messages after ack_wait
|
|
109
|
+
ack_wait: 30 * 1_000_000_000, // 30 seconds in nanos
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
const consumer = await js.consumers.get(streamName, durable);
|
|
113
|
+
// Process jobs in the background
|
|
114
|
+
(async () => {
|
|
115
|
+
try {
|
|
116
|
+
const messages = await consumer.consume();
|
|
117
|
+
for await (const msg of messages) {
|
|
118
|
+
let jobData;
|
|
119
|
+
try {
|
|
120
|
+
jobData = jsonCodec.decode(msg.data);
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
// Malformed message — ack to remove from queue
|
|
124
|
+
msg.ack();
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
// Track retries using sequence number — msg.seq is the stream seq
|
|
128
|
+
const seq = msg.seq;
|
|
129
|
+
const attempts = attemptTracker.get(seq) ?? 0;
|
|
130
|
+
try {
|
|
131
|
+
await executeWithTimeout(handler, jobData);
|
|
132
|
+
msg.ack();
|
|
133
|
+
attemptTracker.delete(seq);
|
|
134
|
+
}
|
|
135
|
+
catch (err) {
|
|
136
|
+
const nextAttempt = attempts + 1;
|
|
137
|
+
attemptTracker.set(seq, nextAttempt);
|
|
138
|
+
if (nextAttempt <= maxRetries) {
|
|
139
|
+
// NAK with backoff delay for retry
|
|
140
|
+
const delay = config?.retryDelay === "exponential"
|
|
141
|
+
? Math.pow(2, nextAttempt - 1) * 1000
|
|
142
|
+
: 1000;
|
|
143
|
+
msg.nak(delay);
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
// Max retries exceeded — move to DLQ if configured
|
|
147
|
+
if (dlqName) {
|
|
148
|
+
try {
|
|
149
|
+
await moveToDLQ(dlqName, name, jobData, err);
|
|
150
|
+
}
|
|
151
|
+
catch (dlqErr) {
|
|
152
|
+
console.error(`[cooper] Failed to move job to DLQ "${dlqName}":`, dlqErr);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
if (onFailure) {
|
|
156
|
+
try {
|
|
157
|
+
await onFailure(jobData, err);
|
|
158
|
+
}
|
|
159
|
+
catch (failErr) {
|
|
160
|
+
console.error(`[cooper] onFailure handler for queue "${name}" threw:`, failErr);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
// Ack to remove from main queue (it's in DLQ now)
|
|
164
|
+
msg.ack();
|
|
165
|
+
attemptTracker.delete(seq);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
catch (err) {
|
|
171
|
+
consumerStarted = false;
|
|
172
|
+
if (!err.message?.includes("closed")) {
|
|
173
|
+
console.error(`[cooper] Queue consumer "${name}" stopped:`, err);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
})();
|
|
177
|
+
}
|
|
178
|
+
const client = {
|
|
179
|
+
async enqueue(data, opts) {
|
|
180
|
+
const connected = await ensureConnected();
|
|
181
|
+
if (connected) {
|
|
182
|
+
const js = getJetStream();
|
|
183
|
+
if (js) {
|
|
184
|
+
await ensureQueueStream(name, { dedup: !!opts?.dedupeKey });
|
|
185
|
+
const h = natsHeaders();
|
|
186
|
+
// Dedup key
|
|
187
|
+
if (opts?.dedupeKey) {
|
|
188
|
+
h.set("Nats-Msg-Id", opts.dedupeKey);
|
|
189
|
+
}
|
|
190
|
+
// Priority as header (for visibility — JetStream doesn't natively prioritize)
|
|
191
|
+
if (opts?.priority && opts.priority !== "normal") {
|
|
192
|
+
h.set("Cooper-Priority", opts.priority);
|
|
193
|
+
}
|
|
194
|
+
// Initial attempt count
|
|
195
|
+
h.set("Cooper-Attempts", "0");
|
|
196
|
+
if (opts?.delay) {
|
|
197
|
+
// For delayed jobs: JetStream doesn't have native delay.
|
|
198
|
+
// We publish immediately but set a header; consumer-side can
|
|
199
|
+
// NAK with delay or we use a timer.
|
|
200
|
+
const delayMs = parseDuration(opts.delay);
|
|
201
|
+
h.set("Cooper-Delay-Until", String(Date.now() + delayMs));
|
|
202
|
+
}
|
|
203
|
+
await js.publish(subject, jsonCodec.encode(data), { headers: h });
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
// Fallback: in-memory
|
|
208
|
+
const delay = opts?.delay ? parseDuration(opts.delay) : 0;
|
|
209
|
+
memJobs.push({
|
|
210
|
+
id: crypto.randomUUID(),
|
|
211
|
+
data,
|
|
212
|
+
attempts: 0,
|
|
213
|
+
priority: opts?.priority ?? "normal",
|
|
214
|
+
scheduledAt: Date.now() + delay,
|
|
215
|
+
});
|
|
216
|
+
const priorityOrder = {
|
|
217
|
+
high: 0,
|
|
218
|
+
normal: 1,
|
|
219
|
+
low: 2,
|
|
220
|
+
};
|
|
221
|
+
memJobs.sort((a, b) => (priorityOrder[a.priority] ?? 1) - (priorityOrder[b.priority] ?? 1));
|
|
222
|
+
setImmediate(() => processMemQueue());
|
|
223
|
+
},
|
|
224
|
+
worker(workerName, workerConfig) {
|
|
225
|
+
workerHandler = workerConfig.handler;
|
|
226
|
+
failureHandler = workerConfig.onFailure ?? null;
|
|
227
|
+
registry.registerQueue(name, {
|
|
228
|
+
name,
|
|
229
|
+
options: config ?? {},
|
|
230
|
+
worker: {
|
|
231
|
+
name: workerName,
|
|
232
|
+
handler: workerConfig.handler,
|
|
233
|
+
onFailure: workerConfig.onFailure,
|
|
234
|
+
},
|
|
235
|
+
});
|
|
236
|
+
// Start JetStream consumer
|
|
237
|
+
ensureConnected().then(async (connected) => {
|
|
238
|
+
if (connected) {
|
|
239
|
+
await ensureQueueStream(name);
|
|
240
|
+
if (dlqName)
|
|
241
|
+
await ensureDLQStream(dlqName);
|
|
242
|
+
startConsumer(workerName, workerConfig.handler, workerConfig.onFailure).catch((err) => {
|
|
243
|
+
console.error(`[cooper] Failed to start queue consumer "${name}":`, err);
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
// Kick in-memory fallback processing
|
|
248
|
+
setImmediate(() => processMemQueue());
|
|
249
|
+
return { _cooper_type: "queue_worker", queue: name, name: workerName };
|
|
250
|
+
},
|
|
251
|
+
async list() {
|
|
252
|
+
const connected = await ensureConnected();
|
|
253
|
+
if (connected) {
|
|
254
|
+
const jsm = getJetStreamManager();
|
|
255
|
+
if (jsm) {
|
|
256
|
+
const streamName = "QUEUE_" + name.replace(/[^a-zA-Z0-9_-]/g, "_").toUpperCase();
|
|
257
|
+
try {
|
|
258
|
+
const info = await jsm.streams.info(streamName);
|
|
259
|
+
// Return stream message count as approximation
|
|
260
|
+
// JetStream doesn't support listing individual messages directly
|
|
261
|
+
return Array.from({ length: Number(info.state.messages) }, (_, i) => ({
|
|
262
|
+
id: `js-${i}`,
|
|
263
|
+
data: {},
|
|
264
|
+
}));
|
|
265
|
+
}
|
|
266
|
+
catch {
|
|
267
|
+
return [];
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
return memJobs.map((j) => ({ id: j.id, data: j.data }));
|
|
272
|
+
},
|
|
273
|
+
async delete(id) {
|
|
274
|
+
// In-memory fallback
|
|
275
|
+
const idx = memJobs.findIndex((j) => j.id === id);
|
|
276
|
+
if (idx >= 0)
|
|
277
|
+
memJobs.splice(idx, 1);
|
|
278
|
+
},
|
|
279
|
+
};
|
|
280
|
+
registry.registerQueue(name, { name, options: config ?? {} });
|
|
281
|
+
return client;
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Move a failed job to the dead-letter queue.
|
|
285
|
+
*/
|
|
286
|
+
async function moveToDLQ(dlqName, sourceQueue, data, error) {
|
|
287
|
+
const js = getJetStream();
|
|
288
|
+
if (!js)
|
|
289
|
+
return;
|
|
290
|
+
await ensureDLQStream(dlqName);
|
|
291
|
+
const dlqSubject = `cooper.dlq.${dlqName}`;
|
|
292
|
+
const h = natsHeaders();
|
|
293
|
+
h.set("Cooper-Source-Queue", sourceQueue);
|
|
294
|
+
h.set("Cooper-Error", error.message.slice(0, 256));
|
|
295
|
+
h.set("Cooper-Failed-At", new Date().toISOString());
|
|
296
|
+
await js.publish(dlqSubject, jsonCodec.encode(data), { headers: h });
|
|
297
|
+
}
|
|
298
|
+
//# sourceMappingURL=queue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queue.js","sourceRoot":"","sources":["../src/queue.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EACL,eAAe,EACf,YAAY,EACZ,mBAAmB,EACnB,SAAS,EACT,YAAY,EACZ,iBAAiB,EACjB,eAAe,GAChB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;AA6BzD,MAAM,aAAa,GAAG,CAAC,GAAW,EAAU,EAAE;IAC5C,MAAM,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;IACxC,IAAI,CAAC,CAAC;QAAE,OAAO,CAAC,CAAC;IACjB,MAAM,IAAI,GAA2B;QACnC,CAAC,EAAE,IAAI;QACP,CAAC,EAAE,KAAK;QACR,CAAC,EAAE,OAAO;QACV,CAAC,EAAE,QAAQ;KACZ,CAAC;IACF,OAAO,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC;AAC/C,CAAC,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,KAAK,CACnB,IAAY,EACZ,MAAoB;IAEpB,MAAM,OAAO,GAAG,gBAAgB,IAAI,EAAE,CAAC;IACvC,MAAM,WAAW,GAAG,MAAM,EAAE,WAAW,IAAI,CAAC,CAAC;IAC7C,MAAM,UAAU,GAAG,MAAM,EAAE,OAAO,IAAI,CAAC,CAAC;IACxC,MAAM,SAAS,GAAG,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACtE,MAAM,OAAO,GAAG,MAAM,EAAE,UAAU,CAAC;IAEnC,2BAA2B;IAC3B,MAAM,OAAO,GAMP,EAAE,CAAC;IACT,IAAI,aAAa,GAAG,KAAK,CAAC;IAC1B,IAAI,aAAa,GAAwC,IAAI,CAAC;IAC9D,IAAI,cAAc,GAAsD,IAAI,CAAC;IAC7E,IAAI,eAAe,GAAG,KAAK,CAAC;IAE5B,6EAA6E;IAC7E,MAAM,cAAc,GAAG,IAAI,GAAG,EAAkB,CAAC;IAEjD,+BAA+B;IAC/B,MAAM,eAAe,GAAG,KAAK,IAAI,EAAE;QACjC,IAAI,aAAa,IAAI,CAAC,aAAa;YAAE,OAAO;QAC5C,aAAa,GAAG,IAAI,CAAC;QAErB,OAAO,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC1B,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC;YAC7C,MAAM,OAAO,CAAC,UAAU,CACtB,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;gBACtB,IAAI,GAAG,CAAC,WAAW,GAAG,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;oBACjC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;oBAClB,OAAO;gBACT,CAAC;gBACD,IAAI,CAAC;oBACH,MAAM,kBAAkB,CAAC,aAAc,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;gBACrD,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,GAAG,CAAC,QAAQ,EAAE,CAAC;oBACf,IAAI,GAAG,CAAC,QAAQ,IAAI,UAAU,EAAE,CAAC;wBAC/B,MAAM,KAAK,GACT,MAAM,EAAE,UAAU,KAAK,aAAa;4BAClC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,QAAQ,GAAG,CAAC,CAAC,GAAG,IAAI;4BACtC,CAAC,CAAC,IAAI,CAAC;wBACX,GAAG,CAAC,WAAW,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC;wBACrC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;oBACpB,CAAC;yBAAM,IAAI,cAAc,EAAE,CAAC;wBAC1B,MAAM,cAAc,CAAC,GAAG,CAAC,IAAI,EAAE,GAAY,CAAC,CAAC;oBAC/C,CAAC;gBACH,CAAC;YACH,CAAC,CAAC,CACH,CAAC;QACJ,CAAC;QAED,aAAa,GAAG,KAAK,CAAC;IACxB,CAAC,CAAC;IAEF,KAAK,UAAU,kBAAkB,CAC/B,OAAmC,EACnC,IAAO;QAEP,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC;QACvB,CAAC;QACD,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC;YAChC,OAAO,CAAC,IAAI,CAAC;YACb,IAAI,OAAO,CAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,CAC/B,UAAU,CACR,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,uBAAuB,MAAO,CAAC,OAAO,EAAE,CAAC,CAAC,EACjE,SAAS,CACV,CACF;SACF,CAAC,CAAC;QACH,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;OAEG;IACH,KAAK,UAAU,aAAa,CAC1B,UAAkB,EAClB,OAAmC,EACnC,SAAoD;QAEpD,IAAI,eAAe;YAAE,OAAO;QAC5B,eAAe,GAAG,IAAI,CAAC;QAEvB,MAAM,EAAE,GAAG,YAAY,EAAE,CAAC;QAC1B,MAAM,GAAG,GAAG,mBAAmB,EAAE,CAAC;QAClC,IAAI,CAAC,EAAE,IAAI,CAAC,GAAG;YAAE,OAAO;QAExB,MAAM,UAAU,GACd,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,iBAAiB,EAAE,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC;QAChE,MAAM,OAAO,GAAG,YAAY,CAAC,UAAU,CAAC,CAAC;QAEzC,yBAAyB;QACzB,IAAI,CAAC;YACH,MAAM,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QAChD,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,UAAU,EAAE;gBAClC,YAAY,EAAE,OAAO;gBACrB,UAAU,EAAE,SAAS,CAAC,QAAQ;gBAC9B,eAAe,EAAE,WAAW;gBAC5B,cAAc,EAAE,OAAO;gBACvB,4CAA4C;gBAC5C,QAAQ,EAAE,EAAE,GAAG,aAAa,EAAE,sBAAsB;aACrD,CAAC,CAAC;QACL,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QAE7D,iCAAiC;QACjC,CAAC,KAAK,IAAI,EAAE;YACV,IAAI,CAAC;gBACH,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,OAAO,EAAE,CAAC;gBAC1C,IAAI,KAAK,EAAE,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;oBACjC,IAAI,OAAU,CAAC;oBACf,IAAI,CAAC;wBACH,OAAO,GAAG,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAM,CAAC;oBAC5C,CAAC;oBAAC,MAAM,CAAC;wBACP,+CAA+C;wBAC/C,GAAG,CAAC,GAAG,EAAE,CAAC;wBACV,SAAS;oBACX,CAAC;oBAED,kEAAkE;oBAClE,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;oBACpB,MAAM,QAAQ,GAAG,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;oBAE9C,IAAI,CAAC;wBACH,MAAM,kBAAkB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;wBAC3C,GAAG,CAAC,GAAG,EAAE,CAAC;wBACV,cAAc,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;oBAC7B,CAAC;oBAAC,OAAO,GAAG,EAAE,CAAC;wBACb,MAAM,WAAW,GAAG,QAAQ,GAAG,CAAC,CAAC;wBACjC,cAAc,CAAC,GAAG,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;wBAErC,IAAI,WAAW,IAAI,UAAU,EAAE,CAAC;4BAC9B,mCAAmC;4BACnC,MAAM,KAAK,GACT,MAAM,EAAE,UAAU,KAAK,aAAa;gCAClC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,WAAW,GAAG,CAAC,CAAC,GAAG,IAAI;gCACrC,CAAC,CAAC,IAAI,CAAC;4BACX,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;wBACjB,CAAC;6BAAM,CAAC;4BACN,mDAAmD;4BACnD,IAAI,OAAO,EAAE,CAAC;gCACZ,IAAI,CAAC;oCACH,MAAM,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,GAAY,CAAC,CAAC;gCACxD,CAAC;gCAAC,OAAO,MAAM,EAAE,CAAC;oCAChB,OAAO,CAAC,KAAK,CACX,uCAAuC,OAAO,IAAI,EAClD,MAAM,CACP,CAAC;gCACJ,CAAC;4BACH,CAAC;4BAED,IAAI,SAAS,EAAE,CAAC;gCACd,IAAI,CAAC;oCACH,MAAM,SAAS,CAAC,OAAO,EAAE,GAAY,CAAC,CAAC;gCACzC,CAAC;gCAAC,OAAO,OAAO,EAAE,CAAC;oCACjB,OAAO,CAAC,KAAK,CACX,yCAAyC,IAAI,UAAU,EACvD,OAAO,CACR,CAAC;gCACJ,CAAC;4BACH,CAAC;4BAED,kDAAkD;4BAClD,GAAG,CAAC,GAAG,EAAE,CAAC;4BACV,cAAc,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;wBAC7B,CAAC;oBACH,CAAC;gBACH,CAAC;YACH,CAAC;YAAC,OAAO,GAAQ,EAAE,CAAC;gBAClB,eAAe,GAAG,KAAK,CAAC;gBACxB,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;oBACrC,OAAO,CAAC,KAAK,CACX,4BAA4B,IAAI,YAAY,EAC5C,GAAG,CACJ,CAAC;gBACJ,CAAC;YACH,CAAC;QACH,CAAC,CAAC,EAAE,CAAC;IACP,CAAC;IAED,MAAM,MAAM,GAAmB;QAC7B,KAAK,CAAC,OAAO,CAAC,IAAO,EAAE,IAAqB;YAC1C,MAAM,SAAS,GAAG,MAAM,eAAe,EAAE,CAAC;YAE1C,IAAI,SAAS,EAAE,CAAC;gBACd,MAAM,EAAE,GAAG,YAAY,EAAE,CAAC;gBAC1B,IAAI,EAAE,EAAE,CAAC;oBACP,MAAM,iBAAiB,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC;oBAE5D,MAAM,CAAC,GAAG,WAAW,EAAE,CAAC;oBAExB,YAAY;oBACZ,IAAI,IAAI,EAAE,SAAS,EAAE,CAAC;wBACpB,CAAC,CAAC,GAAG,CAAC,aAAa,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;oBACvC,CAAC;oBAED,8EAA8E;oBAC9E,IAAI,IAAI,EAAE,QAAQ,IAAI,IAAI,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;wBACjD,CAAC,CAAC,GAAG,CAAC,iBAAiB,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;oBAC1C,CAAC;oBAED,wBAAwB;oBACxB,CAAC,CAAC,GAAG,CAAC,iBAAiB,EAAE,GAAG,CAAC,CAAC;oBAE9B,IAAI,IAAI,EAAE,KAAK,EAAE,CAAC;wBAChB,yDAAyD;wBACzD,6DAA6D;wBAC7D,oCAAoC;wBACpC,MAAM,OAAO,GAAG,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;wBAC1C,CAAC,CAAC,GAAG,CAAC,oBAAoB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC;oBAC5D,CAAC;oBAED,MAAM,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;oBAClE,OAAO;gBACT,CAAC;YACH,CAAC;YAED,sBAAsB;YACtB,MAAM,KAAK,GAAG,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAC1D,OAAO,CAAC,IAAI,CAAC;gBACX,EAAE,EAAE,MAAM,CAAC,UAAU,EAAE;gBACvB,IAAI;gBACJ,QAAQ,EAAE,CAAC;gBACX,QAAQ,EAAE,IAAI,EAAE,QAAQ,IAAI,QAAQ;gBACpC,WAAW,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK;aAChC,CAAC,CAAC;YAEH,MAAM,aAAa,GAA2B;gBAC5C,IAAI,EAAE,CAAC;gBACP,MAAM,EAAE,CAAC;gBACT,GAAG,EAAE,CAAC;aACP,CAAC;YACF,OAAO,CAAC,IAAI,CACV,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CACP,CAAC,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CACtE,CAAC;YAEF,YAAY,CAAC,GAAG,EAAE,CAAC,eAAe,EAAE,CAAC,CAAC;QACxC,CAAC;QAED,MAAM,CAAC,UAAkB,EAAE,YAAY;YACrC,aAAa,GAAG,YAAY,CAAC,OAAO,CAAC;YACrC,cAAc,GAAG,YAAY,CAAC,SAAS,IAAI,IAAI,CAAC;YAEhD,QAAQ,CAAC,aAAa,CAAC,IAAI,EAAE;gBAC3B,IAAI;gBACJ,OAAO,EAAE,MAAM,IAAI,EAAE;gBACrB,MAAM,EAAE;oBACN,IAAI,EAAE,UAAU;oBAChB,OAAO,EAAE,YAAY,CAAC,OAAO;oBAC7B,SAAS,EAAE,YAAY,CAAC,SAAS;iBAClC;aACF,CAAC,CAAC;YAEH,2BAA2B;YAC3B,eAAe,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,SAAS,EAAE,EAAE;gBACzC,IAAI,SAAS,EAAE,CAAC;oBACd,MAAM,iBAAiB,CAAC,IAAI,CAAC,CAAC;oBAC9B,IAAI,OAAO;wBAAE,MAAM,eAAe,CAAC,OAAO,CAAC,CAAC;oBAC5C,aAAa,CACX,UAAU,EACV,YAAY,CAAC,OAAO,EACpB,YAAY,CAAC,SAAS,CACvB,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;wBACd,OAAO,CAAC,KAAK,CACX,4CAA4C,IAAI,IAAI,EACpD,GAAG,CACJ,CAAC;oBACJ,CAAC,CAAC,CAAC;gBACL,CAAC;YACH,CAAC,CAAC,CAAC;YAEH,qCAAqC;YACrC,YAAY,CAAC,GAAG,EAAE,CAAC,eAAe,EAAE,CAAC,CAAC;YAEtC,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;QACzE,CAAC;QAED,KAAK,CAAC,IAAI;YACR,MAAM,SAAS,GAAG,MAAM,eAAe,EAAE,CAAC;YAE1C,IAAI,SAAS,EAAE,CAAC;gBACd,MAAM,GAAG,GAAG,mBAAmB,EAAE,CAAC;gBAClC,IAAI,GAAG,EAAE,CAAC;oBACR,MAAM,UAAU,GACd,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,iBAAiB,EAAE,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC;oBAChE,IAAI,CAAC;wBACH,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;wBAChD,+CAA+C;wBAC/C,iEAAiE;wBACjE,OAAO,KAAK,CAAC,IAAI,CACf,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,EACvC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;4BACT,EAAE,EAAE,MAAM,CAAC,EAAE;4BACb,IAAI,EAAE,EAAO;yBACd,CAAC,CACH,CAAC;oBACJ,CAAC;oBAAC,MAAM,CAAC;wBACP,OAAO,EAAE,CAAC;oBACZ,CAAC;gBACH,CAAC;YACH,CAAC;YAED,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QAC1D,CAAC;QAED,KAAK,CAAC,MAAM,CAAC,EAAU;YACrB,qBAAqB;YACrB,MAAM,GAAG,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;YAClD,IAAI,GAAG,IAAI,CAAC;gBAAE,OAAO,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QACvC,CAAC;KACF,CAAC;IAEF,QAAQ,CAAC,aAAa,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,IAAI,EAAE,EAAE,CAAC,CAAC;IAE9D,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,SAAS,CACtB,OAAe,EACf,WAAmB,EACnB,IAAO,EACP,KAAY;IAEZ,MAAM,EAAE,GAAG,YAAY,EAAE,CAAC;IAC1B,IAAI,CAAC,EAAE;QAAE,OAAO;IAEhB,MAAM,eAAe,CAAC,OAAO,CAAC,CAAC;IAE/B,MAAM,UAAU,GAAG,cAAc,OAAO,EAAE,CAAC;IAC3C,MAAM,CAAC,GAAG,WAAW,EAAE,CAAC;IACxB,CAAC,CAAC,GAAG,CAAC,qBAAqB,EAAE,WAAW,CAAC,CAAC;IAC1C,CAAC,CAAC,GAAG,CAAC,cAAc,EAAE,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;IACnD,CAAC,CAAC,GAAG,CAAC,kBAAkB,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC;IAEpD,MAAM,EAAE,CAAC,OAAO,CAAC,UAAU,EAAE,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;AACvE,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { MiddlewareFn } from "./registry.js";
|
|
2
|
+
export interface RateLimitConfig {
|
|
3
|
+
/** Time window — e.g. "1m", "10s", "1h" */
|
|
4
|
+
window: string;
|
|
5
|
+
/** Max requests allowed within the window */
|
|
6
|
+
max: number;
|
|
7
|
+
/** Function to derive the rate limit key from the request. Defaults to IP address. */
|
|
8
|
+
key?: (req: any) => string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Create a rate limiting middleware.
|
|
12
|
+
*
|
|
13
|
+
* Can be used globally:
|
|
14
|
+
* ```ts
|
|
15
|
+
* cooper.use(rateLimit({ window: "1m", max: 100 }));
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* Or per-route via middleware config:
|
|
19
|
+
* ```ts
|
|
20
|
+
* export default api({
|
|
21
|
+
* method: "POST",
|
|
22
|
+
* path: "/api/login",
|
|
23
|
+
* middleware: [rateLimit({ window: "1m", max: 5 })],
|
|
24
|
+
* handler: async (input) => { ... }
|
|
25
|
+
* });
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export declare function rateLimit(config: RateLimitConfig): MiddlewareFn;
|
|
29
|
+
//# sourceMappingURL=rateLimit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rateLimit.d.ts","sourceRoot":"","sources":["../src/rateLimit.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD,MAAM,WAAW,eAAe;IAC9B,2CAA2C;IAC3C,MAAM,EAAE,MAAM,CAAC;IACf,6CAA6C;IAC7C,GAAG,EAAE,MAAM,CAAC;IACZ,sFAAsF;IACtF,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,MAAM,CAAC;CAC5B;AAwBD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,eAAe,GAAG,YAAY,CAoC/D"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { CooperError } from "./error.js";
|
|
2
|
+
function parseTTL(ttl) {
|
|
3
|
+
const match = ttl.match(/^(\d+)(s|m|h|d)$/);
|
|
4
|
+
if (!match)
|
|
5
|
+
return 60;
|
|
6
|
+
const [, num, unit] = match;
|
|
7
|
+
const multipliers = { s: 1, m: 60, h: 3600, d: 86400 };
|
|
8
|
+
return parseInt(num) * (multipliers[unit] ?? 60);
|
|
9
|
+
}
|
|
10
|
+
function parseWindowMs(window) {
|
|
11
|
+
return parseTTL(window) * 1000;
|
|
12
|
+
}
|
|
13
|
+
let redis = null;
|
|
14
|
+
async function ensureRedis() {
|
|
15
|
+
if (redis)
|
|
16
|
+
return redis;
|
|
17
|
+
const Redis = (await import("ioredis")).default;
|
|
18
|
+
const url = process.env.COOPER_VALKEY_URL ?? "redis://localhost:6379";
|
|
19
|
+
redis = new Redis(url);
|
|
20
|
+
return redis;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Create a rate limiting middleware.
|
|
24
|
+
*
|
|
25
|
+
* Can be used globally:
|
|
26
|
+
* ```ts
|
|
27
|
+
* cooper.use(rateLimit({ window: "1m", max: 100 }));
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* Or per-route via middleware config:
|
|
31
|
+
* ```ts
|
|
32
|
+
* export default api({
|
|
33
|
+
* method: "POST",
|
|
34
|
+
* path: "/api/login",
|
|
35
|
+
* middleware: [rateLimit({ window: "1m", max: 5 })],
|
|
36
|
+
* handler: async (input) => { ... }
|
|
37
|
+
* });
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export function rateLimit(config) {
|
|
41
|
+
const { window, max, key: keyFn } = config;
|
|
42
|
+
const ttlSec = parseTTL(window);
|
|
43
|
+
const windowMs = parseWindowMs(window);
|
|
44
|
+
return async (req, next) => {
|
|
45
|
+
const identifier = keyFn ? keyFn(req) : (req.ip ?? "unknown");
|
|
46
|
+
const redisKey = `cooper:rl:${identifier}:${Math.floor(Date.now() / windowMs)}`;
|
|
47
|
+
const r = await ensureRedis();
|
|
48
|
+
const count = await r.incr(redisKey);
|
|
49
|
+
// Set TTL on first increment
|
|
50
|
+
if (count === 1) {
|
|
51
|
+
await r.expire(redisKey, ttlSec);
|
|
52
|
+
}
|
|
53
|
+
if (count > max) {
|
|
54
|
+
// Calculate retry-after in seconds
|
|
55
|
+
const ttl = await r.ttl(redisKey);
|
|
56
|
+
const retryAfter = ttl > 0 ? ttl : ttlSec;
|
|
57
|
+
const error = new CooperError("RATE_LIMITED", `Rate limit exceeded. Try again in ${retryAfter}s.`);
|
|
58
|
+
error.retryAfter = retryAfter;
|
|
59
|
+
throw error;
|
|
60
|
+
}
|
|
61
|
+
// Attach rate limit headers info to request for downstream use
|
|
62
|
+
req._rateLimit = {
|
|
63
|
+
limit: max,
|
|
64
|
+
remaining: max - count,
|
|
65
|
+
reset: Math.ceil(Date.now() / 1000) + ttlSec,
|
|
66
|
+
};
|
|
67
|
+
return next(req);
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=rateLimit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rateLimit.js","sourceRoot":"","sources":["../src/rateLimit.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal registry — Cooper's Rust runtime communicates with JS workers
|
|
3
|
+
* by referencing registered handlers by name. This module tracks all
|
|
4
|
+
* declarations (routes, topics, crons, queues, etc.) so the bridge
|
|
5
|
+
* can look them up at runtime.
|
|
6
|
+
*/
|
|
7
|
+
export interface RegisteredRoute {
|
|
8
|
+
method: string;
|
|
9
|
+
path: string;
|
|
10
|
+
auth: boolean;
|
|
11
|
+
stream?: "sse" | "websocket";
|
|
12
|
+
validate?: any;
|
|
13
|
+
middleware: MiddlewareFn[];
|
|
14
|
+
handler: Function;
|
|
15
|
+
exportName: string;
|
|
16
|
+
sourceFile: string;
|
|
17
|
+
}
|
|
18
|
+
export interface RegisteredTopic {
|
|
19
|
+
name: string;
|
|
20
|
+
subscribers: Map<string, {
|
|
21
|
+
handler: Function;
|
|
22
|
+
options: any;
|
|
23
|
+
}>;
|
|
24
|
+
}
|
|
25
|
+
export interface RegisteredCron {
|
|
26
|
+
name: string;
|
|
27
|
+
schedule: string;
|
|
28
|
+
handler: Function;
|
|
29
|
+
}
|
|
30
|
+
export interface RegisteredQueue {
|
|
31
|
+
name: string;
|
|
32
|
+
options: any;
|
|
33
|
+
worker?: {
|
|
34
|
+
name: string;
|
|
35
|
+
handler: Function;
|
|
36
|
+
onFailure?: Function;
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
export interface RegisteredDatabase {
|
|
40
|
+
name: string;
|
|
41
|
+
engine: string;
|
|
42
|
+
pool: any;
|
|
43
|
+
}
|
|
44
|
+
export interface RegisteredCache {
|
|
45
|
+
name: string;
|
|
46
|
+
options: any;
|
|
47
|
+
}
|
|
48
|
+
export interface RegisteredBucket {
|
|
49
|
+
name: string;
|
|
50
|
+
options: any;
|
|
51
|
+
}
|
|
52
|
+
export type MiddlewareFn = (req: any, next: (req: any) => Promise<any>) => Promise<any>;
|
|
53
|
+
export type AuthHandlerFn = (token: string) => Promise<Record<string, any>>;
|
|
54
|
+
declare class Registry {
|
|
55
|
+
routes: Map<string, RegisteredRoute>;
|
|
56
|
+
topics: Map<string, RegisteredTopic>;
|
|
57
|
+
crons: Map<string, RegisteredCron>;
|
|
58
|
+
queues: Map<string, RegisteredQueue>;
|
|
59
|
+
databases: Map<string, RegisteredDatabase>;
|
|
60
|
+
caches: Map<string, RegisteredCache>;
|
|
61
|
+
buckets: Map<string, RegisteredBucket>;
|
|
62
|
+
secrets: Map<string, () => Promise<string>>;
|
|
63
|
+
globalMiddleware: MiddlewareFn[];
|
|
64
|
+
authHandler: AuthHandlerFn | null;
|
|
65
|
+
registerRoute(key: string, route: RegisteredRoute): void;
|
|
66
|
+
registerTopic(name: string, topic: RegisteredTopic): void;
|
|
67
|
+
registerCron(name: string, cron: RegisteredCron): void;
|
|
68
|
+
registerQueue(name: string, queue: RegisteredQueue): void;
|
|
69
|
+
registerDatabase(name: string, db: RegisteredDatabase): void;
|
|
70
|
+
setAuthHandler(handler: AuthHandlerFn): void;
|
|
71
|
+
addGlobalMiddleware(mw: MiddlewareFn): void;
|
|
72
|
+
}
|
|
73
|
+
export declare const registry: Registry;
|
|
74
|
+
export {};
|
|
75
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"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"}
|
package/dist/registry.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal registry — Cooper's Rust runtime communicates with JS workers
|
|
3
|
+
* by referencing registered handlers by name. This module tracks all
|
|
4
|
+
* declarations (routes, topics, crons, queues, etc.) so the bridge
|
|
5
|
+
* can look them up at runtime.
|
|
6
|
+
*/
|
|
7
|
+
class Registry {
|
|
8
|
+
routes = new Map();
|
|
9
|
+
topics = new Map();
|
|
10
|
+
crons = new Map();
|
|
11
|
+
queues = new Map();
|
|
12
|
+
databases = new Map();
|
|
13
|
+
caches = new Map();
|
|
14
|
+
buckets = new Map();
|
|
15
|
+
secrets = new Map();
|
|
16
|
+
globalMiddleware = [];
|
|
17
|
+
authHandler = null;
|
|
18
|
+
registerRoute(key, route) {
|
|
19
|
+
this.routes.set(key, route);
|
|
20
|
+
}
|
|
21
|
+
registerTopic(name, topic) {
|
|
22
|
+
this.topics.set(name, topic);
|
|
23
|
+
}
|
|
24
|
+
registerCron(name, cron) {
|
|
25
|
+
this.crons.set(name, cron);
|
|
26
|
+
}
|
|
27
|
+
registerQueue(name, queue) {
|
|
28
|
+
this.queues.set(name, queue);
|
|
29
|
+
}
|
|
30
|
+
registerDatabase(name, db) {
|
|
31
|
+
this.databases.set(name, db);
|
|
32
|
+
}
|
|
33
|
+
setAuthHandler(handler) {
|
|
34
|
+
this.authHandler = handler;
|
|
35
|
+
}
|
|
36
|
+
addGlobalMiddleware(mw) {
|
|
37
|
+
this.globalMiddleware.push(mw);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export const registry = new Registry();
|
|
41
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAmDH,MAAM,QAAQ;IACZ,MAAM,GAAiC,IAAI,GAAG,EAAE,CAAC;IACjD,MAAM,GAAiC,IAAI,GAAG,EAAE,CAAC;IACjD,KAAK,GAAgC,IAAI,GAAG,EAAE,CAAC;IAC/C,MAAM,GAAiC,IAAI,GAAG,EAAE,CAAC;IACjD,SAAS,GAAoC,IAAI,GAAG,EAAE,CAAC;IACvD,MAAM,GAAiC,IAAI,GAAG,EAAE,CAAC;IACjD,OAAO,GAAkC,IAAI,GAAG,EAAE,CAAC;IACnD,OAAO,GAAuC,IAAI,GAAG,EAAE,CAAC;IACxD,gBAAgB,GAAmB,EAAE,CAAC;IACtC,WAAW,GAAyB,IAAI,CAAC;IAEzC,aAAa,CAAC,GAAW,EAAE,KAAsB;QAC/C,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IAC9B,CAAC;IAED,aAAa,CAAC,IAAY,EAAE,KAAsB;QAChD,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC/B,CAAC;IAED,YAAY,CAAC,IAAY,EAAE,IAAoB;QAC7C,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAC7B,CAAC;IAED,aAAa,CAAC,IAAY,EAAE,KAAsB;QAChD,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC/B,CAAC;IAED,gBAAgB,CAAC,IAAY,EAAE,EAAsB;QACnD,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IAC/B,CAAC;IAED,cAAc,CAAC,OAAsB;QACnC,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC;IAC7B,CAAC;IAED,mBAAmB,CAAC,EAAgB;QAClC,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjC,CAAC;CACF;AAED,MAAM,CAAC,MAAM,QAAQ,GAAG,IAAI,QAAQ,EAAE,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Declare a secret — fetched at runtime, never in .env or source code.
|
|
3
|
+
*
|
|
4
|
+
* ```ts
|
|
5
|
+
* const stripeKey = secret("stripe-api-key");
|
|
6
|
+
* const client = new Stripe(await stripeKey());
|
|
7
|
+
* ```
|
|
8
|
+
*/
|
|
9
|
+
export declare function secret(name: string): () => Promise<string>;
|
|
10
|
+
//# sourceMappingURL=secrets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secrets.d.ts","sourceRoot":"","sources":["../src/secrets.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,CA+B1D"}
|
package/dist/secrets.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Declare a secret — fetched at runtime, never in .env or source code.
|
|
3
|
+
*
|
|
4
|
+
* ```ts
|
|
5
|
+
* const stripeKey = secret("stripe-api-key");
|
|
6
|
+
* const client = new Stripe(await stripeKey());
|
|
7
|
+
* ```
|
|
8
|
+
*/
|
|
9
|
+
export function secret(name) {
|
|
10
|
+
let cached = null;
|
|
11
|
+
const resolve = async () => {
|
|
12
|
+
if (cached !== null)
|
|
13
|
+
return cached;
|
|
14
|
+
// 1. Check env var (set by `cooper secrets set`)
|
|
15
|
+
const envKey = `COOPER_SECRET_${name.toUpperCase().replace(/-/g, "_")}`;
|
|
16
|
+
const envVal = process.env[envKey];
|
|
17
|
+
if (envVal) {
|
|
18
|
+
cached = envVal;
|
|
19
|
+
return envVal;
|
|
20
|
+
}
|
|
21
|
+
// 2. Check .cooper/secrets/<env>/<name>
|
|
22
|
+
const fs = require("node:fs");
|
|
23
|
+
const path = require("node:path");
|
|
24
|
+
const env = process.env.COOPER_ENV ?? "local";
|
|
25
|
+
const secretPath = path.join(".cooper", "secrets", env, name);
|
|
26
|
+
if (fs.existsSync(secretPath)) {
|
|
27
|
+
const val = fs.readFileSync(secretPath, "utf-8").trim();
|
|
28
|
+
cached = val;
|
|
29
|
+
return val;
|
|
30
|
+
}
|
|
31
|
+
throw new Error(`Secret "${name}" not found. Set it with: cooper secrets set ${name} --env ${env}`);
|
|
32
|
+
};
|
|
33
|
+
return resolve;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=secrets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secrets.js","sourceRoot":"","sources":["../src/secrets.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,UAAU,MAAM,CAAC,IAAY;IACjC,IAAI,MAAM,GAAkB,IAAI,CAAC;IAEjC,MAAM,OAAO,GAAG,KAAK,IAAqB,EAAE;QAC1C,IAAI,MAAM,KAAK,IAAI;YAAE,OAAO,MAAM,CAAC;QAEnC,iDAAiD;QACjD,MAAM,MAAM,GAAG,iBAAiB,IAAI,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC;QACxE,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACnC,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,GAAG,MAAM,CAAC;YAChB,OAAO,MAAM,CAAC;QAChB,CAAC;QAED,wCAAwC;QACxC,MAAM,EAAE,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;QAC9B,MAAM,IAAI,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;QAClC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,OAAO,CAAC;QAC9C,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,SAAS,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;QAC9D,IAAI,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YAC9B,MAAM,GAAG,GAAW,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;YAChE,MAAM,GAAG,GAAG,CAAC;YACb,OAAO,GAAG,CAAC;QACb,CAAC;QAED,MAAM,IAAI,KAAK,CACb,WAAW,IAAI,gDAAgD,IAAI,UAAU,GAAG,EAAE,CACnF,CAAC;IACJ,CAAC,CAAC;IAEF,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
package/dist/ssr.d.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export interface PageConfig {
|
|
2
|
+
cache?: string;
|
|
3
|
+
auth?: boolean;
|
|
4
|
+
}
|
|
5
|
+
export interface PageContext {
|
|
6
|
+
params: Record<string, string>;
|
|
7
|
+
req?: any;
|
|
8
|
+
data?: any;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Define a server-rendered page.
|
|
12
|
+
*
|
|
13
|
+
* ```ts
|
|
14
|
+
* export default page(async ({ params }) => {
|
|
15
|
+
* const { user } = await UsersService.getUser({ id: params.id });
|
|
16
|
+
* return <div><h1>{user.name}</h1></div>;
|
|
17
|
+
* });
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export declare function page<T = any>(render: (ctx: PageContext) => Promise<T>): {
|
|
21
|
+
_cooper_type: "page";
|
|
22
|
+
render: typeof render;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Define a layout that wraps pages.
|
|
26
|
+
*
|
|
27
|
+
* ```ts
|
|
28
|
+
* export default layout(({ children }) => (
|
|
29
|
+
* <div><Nav /><main>{children}</main><Footer /></div>
|
|
30
|
+
* ));
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export declare function layout<T = any>(render: (props: {
|
|
34
|
+
children: any;
|
|
35
|
+
}) => T): {
|
|
36
|
+
_cooper_type: "layout";
|
|
37
|
+
render: typeof render;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Define a page data loader — runs on the server, data passed to the page.
|
|
41
|
+
*/
|
|
42
|
+
export declare function pageLoader<T>(loader: (ctx: PageContext) => Promise<T>): {
|
|
43
|
+
_cooper_type: "loader";
|
|
44
|
+
loader: typeof loader;
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Suspense boundary for streaming SSR.
|
|
48
|
+
*/
|
|
49
|
+
export declare function Suspense(_props: {
|
|
50
|
+
fallback: any;
|
|
51
|
+
children: any;
|
|
52
|
+
}): null;
|
|
53
|
+
//# sourceMappingURL=ssr.d.ts.map
|