@agilesyndrome/cf-genai-base 0.1.2 → 0.1.4
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/CONTRACT.md +1 -2
- package/README.md +6 -5
- package/package.json +9 -3
- package/src/index.js +19 -10
package/CONTRACT.md
CHANGED
|
@@ -4,8 +4,7 @@ Every site built from this foundation follows the same edge contract.
|
|
|
4
4
|
|
|
5
5
|
## Worker entrypoint
|
|
6
6
|
|
|
7
|
-
`createWorker({ fetch, auth?, scheduled?, security? })` owns the Worker lifecycle.
|
|
8
|
-
The site router owns pages, APIs, D1 queries, and R2 object keys. `scheduled`
|
|
7
|
+
`createWorker({ fetch, features?, middleware?, auth?, scheduled?, security? })` owns the Worker lifecycle. Features run in declaration order and may call `next()` or return a response. The site router owns pages, APIs, D1 queries, and R2 object keys. `scheduled`
|
|
9
8
|
is optional and must use `ctx.waitUntil` for background work.
|
|
10
9
|
|
|
11
10
|
## Routes
|
package/README.md
CHANGED
|
@@ -1,18 +1,17 @@
|
|
|
1
|
-
# `@
|
|
1
|
+
# `@agilesyndrome/cf-genai-base`
|
|
2
2
|
|
|
3
3
|
Opinionated startup boilerplate for small Cloudflare Workers.
|
|
4
4
|
|
|
5
5
|
The base is deliberately small: a site still owns its router, HTML, D1
|
|
6
|
-
queries, R2 keys, and scheduled jobs. `createWorker` composes
|
|
7
|
-
handler, normalizes uncaught failures, and applies baseline response headers.
|
|
6
|
+
queries, R2 keys, and scheduled jobs. `createWorker` composes ordered feature middleware, normalizes uncaught failures, and applies baseline response headers.
|
|
8
7
|
Use D1 bindings for durable application data and R2 bindings for binary assets;
|
|
9
8
|
do not put either into module-level state.
|
|
10
9
|
|
|
11
10
|
```js
|
|
12
|
-
import { createWorker, healthResponse } from "@
|
|
11
|
+
import { createWorker, healthResponse } from "@agilesyndrome/cf-genai-base";
|
|
13
12
|
|
|
14
13
|
export default createWorker({
|
|
15
|
-
|
|
14
|
+
features: [auth],
|
|
16
15
|
fetch: async (request, env) => {
|
|
17
16
|
if (new URL(request.url).pathname === "/health") return healthResponse(env);
|
|
18
17
|
return router(request, env);
|
|
@@ -20,6 +19,8 @@ export default createWorker({
|
|
|
20
19
|
});
|
|
21
20
|
```
|
|
22
21
|
|
|
22
|
+
Features expose `middleware(request, env, ctx, next, state)` and may short-circuit reserved routes, attach request state, or call `next()`.
|
|
23
|
+
|
|
23
24
|
## Shared platform helpers
|
|
24
25
|
|
|
25
26
|
`createWorker` can own `/health` and `/api/health`, run a boot validator before
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agilesyndrome/cf-genai-base",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./src/index.js"
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
"description": "Lean Worker lifecycle and security helpers for Cloudflare sites.",
|
|
9
9
|
"license": "MIT",
|
|
10
10
|
"publishConfig": {
|
|
11
|
-
"access": "public"
|
|
11
|
+
"access": "public",
|
|
12
|
+
"provenance": true
|
|
12
13
|
},
|
|
13
14
|
"files": [
|
|
14
15
|
"src",
|
|
@@ -20,5 +21,10 @@
|
|
|
20
21
|
"type": "git",
|
|
21
22
|
"url": "git+https://github.com/agilesyndrome/cf-genai-base.git"
|
|
22
23
|
},
|
|
23
|
-
"homepage": "https://github.com/agilesyndrome/cf-genai-base#readme"
|
|
24
|
+
"homepage": "https://github.com/agilesyndrome/cf-genai-base#readme",
|
|
25
|
+
"scripts": {
|
|
26
|
+
"check": "node --check src/index.js",
|
|
27
|
+
"test": "node --check src/index.js",
|
|
28
|
+
"build": "npm run check && npm test && npm pack --dry-run"
|
|
29
|
+
}
|
|
24
30
|
}
|
package/src/index.js
CHANGED
|
@@ -2,23 +2,32 @@
|
|
|
2
2
|
* Lean, opinionated Worker composition for Cloudflare sites.
|
|
3
3
|
* Site code owns domain routes and data; this owns lifecycle and edge concerns.
|
|
4
4
|
*/
|
|
5
|
-
export function createWorker({ fetch, scheduled, auth, health, boot, metrics, security = true }) {
|
|
5
|
+
export function createWorker({ fetch, scheduled, auth, middleware = [], features = [], health, boot, metrics, security = true }) {
|
|
6
6
|
if (typeof fetch !== "function") throw new TypeError("createWorker requires a fetch handler");
|
|
7
|
+
const chain = [
|
|
8
|
+
...features.flatMap((feature) => feature?.middleware ? [feature.middleware.bind(feature)] : []),
|
|
9
|
+
...middleware,
|
|
10
|
+
...(auth ? [(request, env, ctx, next) => auth(request, env, ctx, next)] : []),
|
|
11
|
+
].filter(Boolean);
|
|
7
12
|
return {
|
|
8
13
|
async fetch(request, env, ctx) {
|
|
9
14
|
try {
|
|
10
15
|
if (boot) await boot(env, { request, ctx });
|
|
11
16
|
const url = new URL(request.url);
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
if (
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
const state = Object.create(null);
|
|
18
|
+
const dispatch = async (index, currentRequest = request) => {
|
|
19
|
+
const layer = chain[index];
|
|
20
|
+
if (!layer) {
|
|
21
|
+
if (url.pathname === "/health" || url.pathname === "/api/health") {
|
|
22
|
+
const details = health ? await health(env, { request: currentRequest, ctx, state }) : {};
|
|
23
|
+
return healthResponse(env, details);
|
|
24
|
+
}
|
|
25
|
+
return fetch(currentRequest, env, ctx, state);
|
|
20
26
|
}
|
|
21
|
-
|
|
27
|
+
if (typeof layer !== "function") throw new TypeError("Worker middleware must be a function");
|
|
28
|
+
return layer(currentRequest, env, ctx, (nextRequest = currentRequest) => dispatch(index + 1, nextRequest), state);
|
|
29
|
+
};
|
|
30
|
+
const response = await dispatch(0);
|
|
22
31
|
if (metrics) metrics.request(request, response, env, ctx);
|
|
23
32
|
return security ? secureResponse(response) : response;
|
|
24
33
|
} catch (error) {
|