create-daloy 0.1.0 → 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/README.md +11 -2
- package/bin/create-daloy.mjs +2 -2
- package/package.json +1 -1
- package/templates/vercel-edge/README.md +32 -0
- package/templates/vercel-edge/_env.example +1 -0
- package/templates/vercel-edge/_gitignore +9 -0
- package/templates/vercel-edge/_npmrc +4 -0
- package/templates/vercel-edge/api/[...path].ts +55 -0
- package/templates/vercel-edge/package.json +22 -0
- package/templates/vercel-edge/tests/app.test.ts +9 -0
- package/templates/vercel-edge/tsconfig.json +18 -0
- package/templates/vercel-edge/vercel.json +4 -0
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ bun create daloy my-api
|
|
|
13
13
|
The CLI is interactive when arguments are missing. It will ask you for:
|
|
14
14
|
|
|
15
15
|
- A project directory name (defaults to `my-daloy-app`)
|
|
16
|
-
- A template (`node-basic` or `cloudflare-worker`)
|
|
16
|
+
- A template (`node-basic`, `vercel-edge`, or `cloudflare-worker`)
|
|
17
17
|
- Whether to install dependencies
|
|
18
18
|
- Whether to initialize a git repository
|
|
19
19
|
|
|
@@ -31,7 +31,7 @@ pnpm create daloy@latest my-api \
|
|
|
31
31
|
|
|
32
32
|
| Flag | Description |
|
|
33
33
|
| --- | --- |
|
|
34
|
-
| `--template <name>` | `node-basic` (default) or `cloudflare-worker`. |
|
|
34
|
+
| `--template <name>` | `node-basic` (default), `vercel-edge`, or `cloudflare-worker`. |
|
|
35
35
|
| `--package-manager <pm>` | `pnpm` (default), `npm`, `yarn`, or `bun`. |
|
|
36
36
|
| `--install` / `--no-install` | Install dependencies after scaffolding. Defaults to interactive. |
|
|
37
37
|
| `--git` / `--no-git` | Initialize a git repository. Defaults to interactive. |
|
|
@@ -60,6 +60,15 @@ A minimal Cloudflare Worker bootstrap using `@daloyjs/core/cloudflare` with:
|
|
|
60
60
|
- Zod-validated route exposed as `fetch`.
|
|
61
61
|
- A sample test that exercises `app.request(...)`.
|
|
62
62
|
|
|
63
|
+
### `vercel-edge`
|
|
64
|
+
|
|
65
|
+
A Vercel Edge API bootstrap using `@daloyjs/core/vercel` with:
|
|
66
|
+
|
|
67
|
+
- `api/[...path].ts` catch-all routing so DaloyJS owns the API surface.
|
|
68
|
+
- `export const config = { runtime: "edge" }` ready for Vercel Edge.
|
|
69
|
+
- `vercel dev` / `vercel deploy` scripts.
|
|
70
|
+
- A health route and bookstore route mirroring the Node starter.
|
|
71
|
+
|
|
63
72
|
## What the CLI guarantees
|
|
64
73
|
|
|
65
74
|
- Zero runtime dependencies (uses only Node built-ins) for a clean supply-chain footprint.
|
package/bin/create-daloy.mjs
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import { spawn } from "node:child_process";
|
|
6
6
|
import { existsSync } from "node:fs";
|
|
7
|
-
import { mkdir, readdir, readFile,
|
|
7
|
+
import { mkdir, readdir, readFile, writeFile, copyFile } from "node:fs/promises";
|
|
8
8
|
import { createInterface } from "node:readline/promises";
|
|
9
9
|
import { stdin as input, stdout as output } from "node:process";
|
|
10
10
|
import path from "node:path";
|
|
@@ -14,7 +14,7 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
|
14
14
|
const PKG_ROOT = path.resolve(__dirname, "..");
|
|
15
15
|
const TEMPLATES_DIR = path.join(PKG_ROOT, "templates");
|
|
16
16
|
|
|
17
|
-
const TEMPLATES = ["node-basic", "cloudflare-worker"];
|
|
17
|
+
const TEMPLATES = ["node-basic", "vercel-edge", "cloudflare-worker"];
|
|
18
18
|
const PACKAGE_MANAGERS = ["pnpm", "npm", "yarn", "bun"];
|
|
19
19
|
|
|
20
20
|
const RENAME_ON_COPY = new Map([
|
package/package.json
CHANGED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# my-daloy-vercel-api
|
|
2
|
+
|
|
3
|
+
A [DaloyJS](https://daloyjs.dev) Vercel Edge API starter.
|
|
4
|
+
|
|
5
|
+
## Develop
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm install
|
|
9
|
+
pnpm dev # http://localhost:3000
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Try it:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
curl http://localhost:3000/healthz
|
|
16
|
+
curl http://localhost:3000/books/1
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Deploy
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pnpm deploy
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The API entry lives at `api/[...path].ts` and uses `@daloyjs/core/vercel`:
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
export const config = { runtime: "edge" };
|
|
29
|
+
export default toEdgeHandler(app);
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
That catch-all API route lets DaloyJS own routing while Vercel handles the Edge runtime.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Add Vercel project env vars here.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { App, NotFoundError, requestId, secureHeaders } from "@daloyjs/core";
|
|
3
|
+
import { toEdgeHandler } from "@daloyjs/core/vercel";
|
|
4
|
+
|
|
5
|
+
export const config = { runtime: "edge" };
|
|
6
|
+
|
|
7
|
+
const app = new App({
|
|
8
|
+
bodyLimitBytes: 256 * 1024,
|
|
9
|
+
requestTimeoutMs: 5_000,
|
|
10
|
+
production: process.env.NODE_ENV === "production",
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
app.use(requestId());
|
|
14
|
+
app.use(secureHeaders());
|
|
15
|
+
|
|
16
|
+
app.route({
|
|
17
|
+
method: "GET",
|
|
18
|
+
path: "/healthz",
|
|
19
|
+
operationId: "healthz",
|
|
20
|
+
tags: ["Ops"],
|
|
21
|
+
responses: {
|
|
22
|
+
200: {
|
|
23
|
+
description: "Service is healthy",
|
|
24
|
+
body: z.object({ ok: z.literal(true), runtime: z.literal("vercel-edge") }),
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
handler: async () => ({
|
|
28
|
+
status: 200,
|
|
29
|
+
body: { ok: true, runtime: "vercel-edge" as const },
|
|
30
|
+
}),
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const Book = z.object({ id: z.string(), title: z.string() });
|
|
34
|
+
const books = new Map<string, z.infer<typeof Book>>([
|
|
35
|
+
["1", { id: "1", title: "Noli Me Tangere" }],
|
|
36
|
+
]);
|
|
37
|
+
|
|
38
|
+
app.route({
|
|
39
|
+
method: "GET",
|
|
40
|
+
path: "/books/:id",
|
|
41
|
+
operationId: "getBookById",
|
|
42
|
+
tags: ["Books"],
|
|
43
|
+
request: { params: z.object({ id: z.string() }) },
|
|
44
|
+
responses: {
|
|
45
|
+
200: { description: "Found", body: Book },
|
|
46
|
+
404: { description: "Not found" },
|
|
47
|
+
},
|
|
48
|
+
handler: async ({ params }) => {
|
|
49
|
+
const book = books.get(params.id);
|
|
50
|
+
if (!book) throw new NotFoundError(`Book ${params.id} not found`);
|
|
51
|
+
return { status: 200, body: book };
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
export default toEdgeHandler(app);
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "my-daloy-vercel-api",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "vercel dev",
|
|
8
|
+
"deploy": "vercel deploy",
|
|
9
|
+
"typecheck": "tsc --noEmit",
|
|
10
|
+
"test": "node --import tsx --test tests/**/*.test.ts"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@daloyjs/core": "^0.1.1",
|
|
14
|
+
"zod": "^4.4.3"
|
|
15
|
+
},
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"@types/node": "^25.7.0",
|
|
18
|
+
"tsx": "^4.22.0",
|
|
19
|
+
"typescript": "^6.0.3",
|
|
20
|
+
"vercel": "^48.0.0"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
import handler from "../api/[...path].js";
|
|
4
|
+
|
|
5
|
+
test("Vercel Edge handler responds through DaloyJS", async () => {
|
|
6
|
+
const response = await handler(new Request("https://example.test/healthz"));
|
|
7
|
+
assert.equal(response.status, 200);
|
|
8
|
+
assert.equal((await response.json()).runtime, "vercel-edge");
|
|
9
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"lib": ["ES2022", "DOM"],
|
|
7
|
+
"types": ["node"],
|
|
8
|
+
"strict": true,
|
|
9
|
+
"noUncheckedIndexedAccess": true,
|
|
10
|
+
"esModuleInterop": true,
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"forceConsistentCasingInFileNames": true,
|
|
13
|
+
"resolveJsonModule": true,
|
|
14
|
+
"isolatedModules": true,
|
|
15
|
+
"noEmit": true
|
|
16
|
+
},
|
|
17
|
+
"include": ["api/**/*.ts", "tests/**/*.ts"]
|
|
18
|
+
}
|