create-daloy 0.36.0 → 0.38.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.
Files changed (31) hide show
  1. package/bin/create-daloy.mjs +59 -27
  2. package/package.json +1 -1
  3. package/sbom.cdx.json +9 -9
  4. package/sbom.spdx.json +5 -5
  5. package/templates/bun-basic/CLAUDE.md +1 -0
  6. package/templates/bun-basic/_agents/skills/daloyjs-best-practices/SKILL.md +11 -0
  7. package/templates/bun-basic/package.json +1 -1
  8. package/templates/cloudflare-worker/CLAUDE.md +1 -0
  9. package/templates/cloudflare-worker/_agents/skills/daloyjs-best-practices/SKILL.md +11 -0
  10. package/templates/cloudflare-worker/package.json +1 -1
  11. package/templates/deno-basic/AGENTS.md +2 -2
  12. package/templates/deno-basic/CLAUDE.md +1 -0
  13. package/templates/deno-basic/README.md +1 -1
  14. package/templates/deno-basic/_agents/skills/daloyjs-best-practices/SKILL.md +12 -1
  15. package/templates/deno-basic/deno.json +4 -2
  16. package/templates/deno-basic/deno.lock +35 -0
  17. package/templates/node-basic/AGENTS.md +3 -3
  18. package/templates/node-basic/CLAUDE.md +1 -0
  19. package/templates/node-basic/README.md +3 -3
  20. package/templates/node-basic/_agents/skills/daloyjs-best-practices/SKILL.md +10 -0
  21. package/templates/node-basic/package.json +1 -1
  22. package/templates/node-basic/scripts/dump-openapi.ts +1 -1
  23. package/templates/node-basic/src/index.ts +1 -1
  24. package/templates/node-basic/tsconfig.json +3 -1
  25. package/templates/vercel-edge/AGENTS.md +3 -3
  26. package/templates/vercel-edge/CLAUDE.md +1 -0
  27. package/templates/vercel-edge/README.md +3 -3
  28. package/templates/vercel-edge/_agents/skills/daloyjs-best-practices/SKILL.md +12 -0
  29. package/templates/vercel-edge/package.json +1 -1
  30. package/templates/vercel-edge/tests/app.test.ts +1 -1
  31. package/templates/vercel-edge/tsconfig.json +2 -1
@@ -216,7 +216,8 @@ const SYMBOLS = SUPPORTS_UNICODE
216
216
  const BAR = color(COLORS.gray, SYMBOLS.bar);
217
217
 
218
218
  function printIntro(title) {
219
- console.log(`${color(COLORS.cyan, SYMBOLS.cornerTL + SYMBOLS.lineH)} ${color(COLORS.bold, title)}`);
219
+ console.log(`${color(COLORS.cyan, SYMBOLS.cornerTL + SYMBOLS.lineH)} ${color(COLORS.bold + COLORS.white, title)}`);
220
+ console.log(`${BAR} ${color(COLORS.dim, "Answer a few prompts \u2014 arrow keys to move, Enter to confirm.")}`);
220
221
  console.log(BAR);
221
222
  }
222
223
 
@@ -259,17 +260,18 @@ function renderBox(lines, options = {}) {
259
260
  // requests, and responses moving cleanly between client and server.
260
261
  const LOGO_WAVE_LINES = SUPPORTS_UNICODE
261
262
  ? [
262
- "\u223F\u223F\u223F\u223F\u223F\u223F\u223F\u223F\u223F\u223F\u223F\u223F\u223F\u223F\u223F\u223F\u223F\u223F\u223F\u223F\u223F\u223F\u223F\u223F\u223F",
263
- "\u223F\u223F\u223F\u223F\u223F\u223F\u223F\u223F\u223F\u223F\u223F\u223F\u223F\u223F\u223F\u223F\u223F\u223F\u223F\u223F\u223F\u223F\u223F\u223F\u223F",
264
- "\u223F\u223F\u223F\u223F\u223F\u223F\u223F\u223F\u223F\u223F\u223F\u223F\u223F\u223F\u223F\u223F\u223F\u223F\u223F\u223F\u223F\u223F\u223F\u223F\u223F",
263
+ "\u2248\u2248\u2248\u2248\u2248\u2248", // ≈≈≈≈≈≈
264
+ "\u2248\u2248\u2248\u2248\u2248\u2248",
265
+ "\u2248\u2248\u2248\u2248\u2248\u2248",
265
266
  ]
266
- : ["~".repeat(25), "~".repeat(25), "~".repeat(25)];
267
+ : ["~~~~~~", "~~~~~~", "~~~~~~"];
267
268
 
268
- // Sky-blue palette tuned to match `tailwindcss` sky-200/400/700 — the same
269
- // colors used in the wordmark on https://daloyjs.dev.
269
+ // Sky-blue palette anchored to the brand mark (website/public/assets/source/
270
+ // mark.svg): the three stacked waves are sky-200, sky-400, and sky-600. Each
271
+ // row fades slightly along its length so the cascade reads as flowing water.
270
272
  const LOGO_WAVE_GRADIENTS = [
271
273
  { start: [186, 230, 253], end: [125, 211, 252] }, // sky-200 -> sky-300
272
- { start: [56, 189, 248], end: [14, 165, 233] }, // sky-400 -> sky-500
274
+ { start: [56, 189, 248], end: [2, 132, 199] }, // sky-400 -> sky-600
273
275
  { start: [2, 132, 199], end: [3, 105, 161] }, // sky-600 -> sky-700
274
276
  ];
275
277
 
@@ -291,25 +293,45 @@ function gradientLine(line, startRgb, endRgb) {
291
293
  function printBanner(version) {
292
294
  if (!SUPPORTS_UNICODE) {
293
295
  console.log(`\n${color(COLORS.bold + COLORS.cyan, "create-daloy")} ${color(COLORS.dim, `v${version}`)}`);
294
- console.log(color(COLORS.dim, "Contract-first REST APIs for Node, Bun, Deno, Vercel Edge, and Workers"));
296
+ console.log(color(COLORS.dim, "The runtime-portable framework with supply-chain-aware defaults"));
297
+ console.log(color(COLORS.dim, "Secure-by-default runtime | Blocked install scripts | Source-verified lockfiles | Typed end-to-end"));
295
298
  console.log(color(COLORS.dim, "https://daloyjs.dev\n"));
296
299
  return;
297
300
  }
298
- for (let i = 0; i < LOGO_WAVE_LINES.length; i += 1) {
299
- const { start, end } = LOGO_WAVE_GRADIENTS[i];
300
- console.log(` ${gradientLine(LOGO_WAVE_LINES[i], start, end)}`);
301
- }
302
- // Centered wordmark beneath the waves: "Daloy" in neutral text, "JS" in
303
- // brand sky-blue. Mirrors the SVG lockup used on the website.
301
+
302
+ // Brand lockup: the three-wave mark on the left sits beside the wordmark and
303
+ // tagline on the right — mirroring the social banner on https://daloyjs.dev.
304
304
  const wordmark = `${color(COLORS.bold + COLORS.white, "Daloy")}${color(COLORS.bold + COLORS.cyan, "JS")}`;
305
- const wordmarkPadding = " ".repeat(Math.max(0, Math.floor((stringWidth(LOGO_WAVE_LINES[0]) - 7) / 2)));
306
- console.log(` ${wordmarkPadding}${wordmark}`);
307
- // Build the welcome content lines (each contains its own ANSI color codes).
308
- const headline = `${color(COLORS.bold + COLORS.cyan, "Welcome to DaloyJS")} ${color(COLORS.gray, `\u2014 v${version}`)}`;
309
- const subline = color(COLORS.dim, "Contract-first REST APIs for Node, Bun, Deno, Vercel Edge, and Workers.");
310
- const docs = `${color(COLORS.gray, "docs:")} ${color(COLORS.cyan, "https://daloyjs.dev/docs")}`;
305
+ const versionTag = color(COLORS.gray, `v${version}`);
306
+ const tagline = color(COLORS.gray, "The runtime-portable framework with supply-chain-aware defaults");
307
+
308
+ const waves = LOGO_WAVE_LINES.map((line, i) =>
309
+ gradientLine(line, LOGO_WAVE_GRADIENTS[i].start, LOGO_WAVE_GRADIENTS[i].end),
310
+ );
311
+ const gap = " ";
312
+
313
+ console.log("");
314
+ console.log(` ${waves[0]}`);
315
+ console.log(` ${waves[1]}${gap}${wordmark} ${versionTag}`);
316
+ console.log(` ${waves[2]}${gap}${tagline}`);
311
317
  console.log("");
312
- console.log(renderBox([headline, subline, "", docs], { accent: COLORS.cyan }));
318
+
319
+ // Feature chips, brand-styled with cyan middots between dim labels.
320
+ const dot = color(COLORS.cyan, " \u00B7 ");
321
+ const chips = [
322
+ "Secure-by-default",
323
+ "Blocked install scripts",
324
+ "Source-verified lockfiles",
325
+ "Typed end-to-end",
326
+ ]
327
+ .map((chip) => color(COLORS.dim, chip))
328
+ .join(dot);
329
+ console.log(` ${chips}`);
330
+
331
+ // Inverse-video "daloyjs.dev" badge, echoing the rounded pill in the brand
332
+ // banner. Inverse degrades to plain text when color is unavailable.
333
+ const pill = color(COLORS.cyan + COLORS.inverse, " daloyjs.dev ");
334
+ console.log(` ${pill} ${color(COLORS.gray, "docs:")} ${color(COLORS.cyan + COLORS.underline, "https://daloyjs.dev/docs")}`);
313
335
  console.log("");
314
336
  }
315
337
 
@@ -608,8 +630,12 @@ async function patchDockerfileForPackageManager(dir, packageManager) {
608
630
 
609
631
  const raw = await readFile(file, "utf8");
610
632
  const install = dockerInstallSnippet(packageManager);
633
+ // Match the pnpm install block tolerant of both LF and CRLF line endings.
634
+ // Windows checkouts (and packages published from Windows) can carry CRLF;
635
+ // an `\n`-only pattern would silently fail to match there, leaving the
636
+ // pnpm-specific COPY/RUN lines in place for npm/yarn/bun scaffolds.
611
637
  let next = raw.replace(
612
- /COPY package\.json pnpm-lock\.yaml\* \.\/\nRUN corepack enable && corepack prepare pnpm@latest --activate && \\\n+\s+pnpm install --frozen-lockfile --ignore-scripts/,
638
+ /COPY package\.json pnpm-lock\.yaml\* \.\/\r?\nRUN corepack enable && corepack prepare pnpm@latest --activate && \\\r?\n\s+pnpm install --frozen-lockfile --ignore-scripts/,
613
639
  `${install.copy}\n${install.run}`,
614
640
  );
615
641
 
@@ -623,8 +649,11 @@ async function patchDockerfileForPackageManager(dir, packageManager) {
623
649
 
624
650
  if (packageManager === "bun" && next.includes("FROM ${NODE_IMAGE} AS builder")) {
625
651
  if (!next.includes("ARG BUN_IMAGE=")) {
652
+ // CRLF-tolerant: the working tree may carry `\r\n`, so a plain
653
+ // `"...alpine\n"` substring replace would miss and the BUN_IMAGE ARG
654
+ // would never be inserted.
626
655
  next = next.replace(
627
- "ARG NODE_IMAGE=node:24-alpine\n",
656
+ /ARG NODE_IMAGE=node:24-alpine\r?\n/,
628
657
  "ARG NODE_IMAGE=node:24-alpine\nARG BUN_IMAGE=oven/bun:1-alpine\n",
629
658
  );
630
659
  }
@@ -1312,7 +1341,9 @@ async function askChoice(rl, question, choices, defaultChoice) {
1312
1341
  const rawInput = rawInputHandle.stream;
1313
1342
 
1314
1343
  printPromptHeader(question);
1315
- printRailLine(color(COLORS.dim, `Use \u2191 \u2193 to navigate, Enter to confirm, type a number to jump.`));
1344
+ printRailLine(
1345
+ `${color(COLORS.dim, "\u2191/\u2193")} ${color(COLORS.gray, "navigate")} ${color(COLORS.dim, "1\u20139")} ${color(COLORS.gray, "jump")} ${color(COLORS.dim, "enter")} ${color(COLORS.gray, "confirm")}`,
1346
+ );
1316
1347
 
1317
1348
  let index = Math.max(
1318
1349
  0,
@@ -1326,15 +1357,16 @@ async function askChoice(rl, question, choices, defaultChoice) {
1326
1357
  .map((choice, i) => {
1327
1358
  const isActive = i === active;
1328
1359
  const isDefault = optionValue(choice) === defaultChoice;
1360
+ const pointer = isActive ? color(COLORS.cyan, SYMBOLS.pointer) : " ";
1329
1361
  const marker = isActive ? color(COLORS.cyan, SYMBOLS.radioOn) : color(COLORS.gray, SYMBOLS.radioOff);
1330
1362
  const titleRaw = optionTitle(choice).padEnd(titleWidth);
1331
1363
  const valueRaw = optionValue(choice).padEnd(valueWidth);
1332
1364
  const title = isActive ? color(COLORS.bold + COLORS.cyan, titleRaw) : color(COLORS.white, titleRaw);
1333
- const value = color(COLORS.dim, `(${valueRaw})`);
1365
+ const value = color(COLORS.dim, `${valueRaw}`);
1334
1366
  const description = optionDescription(choice);
1335
1367
  const descColored = isActive ? color(COLORS.cyan, description) : color(COLORS.dim, description);
1336
1368
  const recommended = isDefault ? color(COLORS.green, ` ${SYMBOLS.star} recommended`) : "";
1337
- return `${BAR} ${marker} ${title} ${value} ${descColored}${recommended}`;
1369
+ return `${BAR} ${pointer} ${marker} ${title} ${value} ${descColored}${recommended}`;
1338
1370
  })
1339
1371
  .join("\n");
1340
1372
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-daloy",
3
- "version": "0.36.0",
3
+ "version": "0.38.0",
4
4
  "description": "Scaffold a new DaloyJS project. Run with `pnpm create daloy`, `npm create daloy@latest`, `yarn create daloy`, or `bun create daloy`.",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/sbom.cdx.json CHANGED
@@ -1,25 +1,25 @@
1
1
  {
2
2
  "bomFormat": "CycloneDX",
3
3
  "specVersion": "1.5",
4
- "serialNumber": "urn:uuid:f93cd589-45c8-513e-a365-e0e8bd5ad309",
4
+ "serialNumber": "urn:uuid:b1a2eb69-121b-5cae-87a9-5d036a3e1a7a",
5
5
  "version": 1,
6
6
  "metadata": {
7
- "timestamp": "2026-05-28T20:50:57.300Z",
7
+ "timestamp": "2026-06-10T11:58:38.391Z",
8
8
  "tools": [
9
9
  {
10
10
  "vendor": "DaloyJS",
11
11
  "name": "daloy-generate-sbom",
12
- "version": "0.36.0"
12
+ "version": "0.38.0"
13
13
  }
14
14
  ],
15
15
  "authors": [],
16
16
  "component": {
17
17
  "type": "library",
18
- "bom-ref": "pkg:npm/create-daloy@0.36.0",
18
+ "bom-ref": "pkg:npm/create-daloy@0.38.0",
19
19
  "name": "create-daloy",
20
- "version": "0.36.0",
20
+ "version": "0.38.0",
21
21
  "description": "Scaffold a new DaloyJS project. Run with `pnpm create daloy`, `npm create daloy@latest`, `yarn create daloy`, or `bun create daloy`.",
22
- "purl": "pkg:npm/create-daloy@0.36.0",
22
+ "purl": "pkg:npm/create-daloy@0.38.0",
23
23
  "licenses": [
24
24
  {
25
25
  "license": {
@@ -42,9 +42,9 @@
42
42
  }
43
43
  ],
44
44
  "swid": {
45
- "tagId": "swidtag-create-daloy-0.36.0",
45
+ "tagId": "swidtag-create-daloy-0.38.0",
46
46
  "name": "create-daloy",
47
- "version": "0.36.0",
47
+ "version": "0.38.0",
48
48
  "tagVersion": 0,
49
49
  "patch": false
50
50
  }
@@ -53,7 +53,7 @@
53
53
  "components": [],
54
54
  "dependencies": [
55
55
  {
56
- "ref": "pkg:npm/create-daloy@0.36.0",
56
+ "ref": "pkg:npm/create-daloy@0.38.0",
57
57
  "dependsOn": []
58
58
  }
59
59
  ]
package/sbom.spdx.json CHANGED
@@ -2,10 +2,10 @@
2
2
  "spdxVersion": "SPDX-2.3",
3
3
  "dataLicense": "CC0-1.0",
4
4
  "SPDXID": "SPDXRef-DOCUMENT",
5
- "name": "create-daloy-0.36.0",
6
- "documentNamespace": "https://github.com/daloyjs/daloy/sbom/create-daloy-0.36.0-f93cd589-45c8-513e-a365-e0e8bd5ad309",
5
+ "name": "create-daloy-0.38.0",
6
+ "documentNamespace": "https://github.com/daloyjs/daloy/sbom/create-daloy-0.38.0-b1a2eb69-121b-5cae-87a9-5d036a3e1a7a",
7
7
  "creationInfo": {
8
- "created": "2026-05-28T20:50:57.300Z",
8
+ "created": "2026-06-10T11:58:38.391Z",
9
9
  "creators": [
10
10
  "Tool: daloy-generate-sbom",
11
11
  "Organization: DaloyJS"
@@ -16,7 +16,7 @@
16
16
  {
17
17
  "SPDXID": "SPDXRef-Package-create-daloy",
18
18
  "name": "create-daloy",
19
- "versionInfo": "0.36.0",
19
+ "versionInfo": "0.38.0",
20
20
  "downloadLocation": "https://github.com/daloyjs/daloy",
21
21
  "filesAnalyzed": false,
22
22
  "licenseConcluded": "MIT",
@@ -27,7 +27,7 @@
27
27
  {
28
28
  "referenceCategory": "PACKAGE-MANAGER",
29
29
  "referenceType": "purl",
30
- "referenceLocator": "pkg:npm/create-daloy@0.36.0"
30
+ "referenceLocator": "pkg:npm/create-daloy@0.38.0"
31
31
  }
32
32
  ]
33
33
  }
@@ -0,0 +1 @@
1
+ @AGENTS.md
@@ -1,3 +1,14 @@
1
+ ---
2
+ name: daloyjs-best-practices
3
+ description: >-
4
+ Best practices for building, testing, and hardening this DaloyJS REST API on
5
+ the Bun runtime. Use when adding or changing HTTP routes, Zod schemas,
6
+ middleware, or error handling; regenerating the OpenAPI spec or the typed
7
+ Hey API client; or working on auth, rate limits, secrets, and the project's
8
+ quality gates.
9
+ license: MIT
10
+ ---
11
+
1
12
  # SKILL.md — DaloyJS best practices (Bun)
2
13
 
3
14
  Operational guidance and best practices for AI coding agents working in this
@@ -17,7 +17,7 @@
17
17
  "audit": "pnpm audit --prod"
18
18
  },
19
19
  "dependencies": {
20
- "@daloyjs/core": "^0.36.0",
20
+ "@daloyjs/core": "^0.38.0",
21
21
  "zod": "^4.4.3"
22
22
  },
23
23
  "devDependencies": {
@@ -0,0 +1 @@
1
+ @AGENTS.md
@@ -1,3 +1,14 @@
1
+ ---
2
+ name: daloyjs-best-practices
3
+ description: >-
4
+ Best practices for building, testing, and hardening this DaloyJS REST API on
5
+ Cloudflare Workers. Use when adding or changing HTTP routes, Zod schemas,
6
+ middleware, or error handling; wiring Worker bindings (KV, D1, R2, Queues,
7
+ env, secrets); or working on auth, rate limits, and the project's quality
8
+ gates.
9
+ license: MIT
10
+ ---
11
+
1
12
  # SKILL.md — DaloyJS best practices (Cloudflare Workers)
2
13
 
3
14
  Operational guidance and best practices for AI coding agents working in this
@@ -11,7 +11,7 @@
11
11
  "audit": "pnpm audit --prod"
12
12
  },
13
13
  "dependencies": {
14
- "@daloyjs/core": "^0.36.0",
14
+ "@daloyjs/core": "^0.38.0",
15
15
  "zod": "^4.4.3"
16
16
  },
17
17
  "devDependencies": {
@@ -2,7 +2,7 @@
2
2
 
3
3
  A [DaloyJS](https://daloyjs.dev) REST API for the [Deno](https://deno.com) runtime. **Contract-first**: routes are defined with Zod schemas and OpenAPI 3.1 is generated from them. When `docs: true` is set in `new App({...})`, three routes are auto-mounted: `GET /openapi.json`, `GET /openapi.yaml`, and `GET /docs` (Scalar UI).
4
4
 
5
- - Runtime: Deno (no Node package manager). Dependencies are loaded via `npm:` and `jsr:` specifiers in `deno.json`.
5
+ - Runtime: Deno (no Node package manager). DaloyJS loads from `jsr:` and third-party packages such as Zod load from `npm:` in `deno.json`.
6
6
 
7
7
  ## Commands
8
8
 
@@ -18,7 +18,7 @@ The typed Hey API SDK is generated outside Deno (Hey API has no Deno entrypoint
18
18
  - `src/build-app.ts` — `buildApp()` factory. Routes, schemas, and middleware live here. **Pure, no side effects.**
19
19
  - `src/main.ts` — calls `buildApp()` and starts the listener via `@daloyjs/core/deno`. The only file that opens a port.
20
20
  - `scripts/dump-openapi.ts` — imports `buildApp()` and writes `generated/openapi.json`. Codegen reads from `buildApp()` only — never import `src/main.ts` from scripts.
21
- - `deno.json` — tasks, import map, and `npm:` specifiers. There is no `package.json` in this project.
21
+ - `deno.json` — tasks, import map, and JSR-first dependency specifiers. There is no `package.json` in this project.
22
22
  - `generated/` — machine-written. Do not edit by hand.
23
23
  - `tests/` — Deno test files.
24
24
 
@@ -0,0 +1 @@
1
+ @AGENTS.md
@@ -49,7 +49,7 @@ deno task test
49
49
 
50
50
  ## What's included
51
51
 
52
- - `@daloyjs/core` (loaded via `npm:` specifiers in `deno.json`).
52
+ - `@daloyjs/core` (loaded via `jsr:` specifiers in `deno.json`).
53
53
  - Starter security middleware: `secureHeaders`, `requestId`, and `rateLimit`.
54
54
  <!-- daloy-minimal:strip-start books -->
55
55
  - A health route and contract-first `/books/:id` route with Zod validation.
@@ -1,3 +1,14 @@
1
+ ---
2
+ name: daloyjs-best-practices
3
+ description: >-
4
+ Best practices for building, testing, and hardening this DaloyJS REST API on
5
+ the Deno runtime. Use when adding or changing HTTP routes, Zod schemas,
6
+ middleware, or error handling; regenerating the OpenAPI spec; managing Deno
7
+ permissions and tasks; or working on auth, rate limits, secrets, and the
8
+ project's quality gates.
9
+ license: MIT
10
+ ---
11
+
1
12
  # SKILL.md — DaloyJS best practices (Deno)
2
13
 
3
14
  Operational guidance and best practices for AI coding agents working in this
@@ -43,7 +54,7 @@ DaloyJS is a **contract-first** framework. Internalize these rules:
43
54
  `@daloyjs/core/deno`. The only file allowed to open a port.
44
55
  - `scripts/dump-openapi.ts` — imports `buildApp()` and writes
45
56
  `generated/openapi.json`.
46
- - `deno.json` — tasks, import map, and `npm:` specifiers. **There is no
57
+ - `deno.json` — tasks, import map, and JSR-first dependency specifiers. **There is no
47
58
  `package.json`** in this project — do not add one.
48
59
  - `tests/` — Deno test files (`*.test.ts`).
49
60
  - `generated/` — **machine-written**. Never edit by hand.
@@ -8,8 +8,10 @@
8
8
  "gen:openapi": "deno run --allow-net --allow-env --allow-read --allow-write scripts/dump-openapi.ts"
9
9
  },
10
10
  "imports": {
11
- "@daloyjs/core": "npm:@daloyjs/core@^0.36.0",
12
- "@daloyjs/core/": "npm:@daloyjs/core@^0.36.0/",
11
+ "@daloyjs/core": "jsr:@daloyjs/daloy@^0.38.0",
12
+ "@daloyjs/core/banner": "jsr:@daloyjs/daloy@^0.38.0/banner",
13
+ "@daloyjs/core/deno": "jsr:@daloyjs/daloy@^0.38.0/deno",
14
+ "@daloyjs/core/openapi": "jsr:@daloyjs/daloy@^0.38.0/openapi",
13
15
  "zod": "npm:zod@^4.4.3"
14
16
  },
15
17
  "compilerOptions": {
@@ -0,0 +1,35 @@
1
+ {
2
+ "version": "5",
3
+ "specifiers": {
4
+ "jsr:@std/assert@1": "1.0.19",
5
+ "jsr:@std/internal@^1.0.12": "1.0.14",
6
+ "npm:@daloyjs/core@0.37": "0.37.0",
7
+ "npm:zod@^4.4.3": "4.4.3"
8
+ },
9
+ "jsr": {
10
+ "@std/assert@1.0.19": {
11
+ "integrity": "eaada96ee120cb980bc47e040f82814d786fe8162ecc53c91d8df60b8755991e",
12
+ "dependencies": [
13
+ "jsr:@std/internal"
14
+ ]
15
+ },
16
+ "@std/internal@1.0.14": {
17
+ "integrity": "291516b3d4c35024d6ffbc0a9df5bf4c64116e05b50012cf846710152d2ffdf7"
18
+ }
19
+ },
20
+ "npm": {
21
+ "@daloyjs/core@0.37.0": {
22
+ "integrity": "sha512-z7XRaoue2TqDIyQIdsg8ZKlBPmvYU8WX2ieYk3sGBMyt181bkb9NBgnxD/m6W54ojpiz4ArFRxyJ9BQTfjLvnQ==",
23
+ "bin": true
24
+ },
25
+ "zod@4.4.3": {
26
+ "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ=="
27
+ }
28
+ },
29
+ "workspace": {
30
+ "dependencies": [
31
+ "npm:@daloyjs/core@0.37",
32
+ "npm:zod@^4.4.3"
33
+ ]
34
+ }
35
+ }
@@ -27,13 +27,13 @@ When `docs: true` is set in `new App({...})`, three routes are auto-mounted:
27
27
 
28
28
  ## Imports
29
29
 
30
- This project uses TypeScript with `"module": "NodeNext"` (ESM). Relative imports **must include a `.js` extension**, even when the source file is `.ts`:
30
+ This project uses TypeScript with `"module": "NodeNext"` plus `"rewriteRelativeImportExtensions"`, so relative imports use the **`.ts` extension** the actual file you see on disk:
31
31
 
32
32
  ```ts
33
- import { buildApp } from "./build-app.js"; // resolves to build-app.ts at typecheck, build-app.js at runtime
33
+ import { buildApp } from "./build-app.ts";
34
34
  ```
35
35
 
36
- This is the official Node.js ESM convention TypeScript rewrites the specifier during typecheck, and the compiled output really is `.js`. Bare-specifier imports from packages (`@daloyjs/core`, `zod`, …) do not need an extension.
36
+ You import the file you see. On `pnpm build`, TypeScript rewrites the `.ts` specifier to `.js` in the compiled `dist/` output, so the deployed code is still valid Node ESM. (Node ESM has no extensionless relative imports — `.ts` is the most natural form available.) Bare-specifier imports from packages (`@daloyjs/core`, `zod`, …) do not need an extension.
37
37
 
38
38
  ## Core rules
39
39
 
@@ -0,0 +1 @@
1
+ @AGENTS.md
@@ -48,13 +48,13 @@ node dist/index.js
48
48
 
49
49
  ## Imports
50
50
 
51
- This project uses Node.js **ESM** with `"module": "NodeNext"`. Relative imports must include a `.js` extension, even when the source file is `.ts`:
51
+ This project uses Node.js **ESM** with `"module": "NodeNext"` and `"rewriteRelativeImportExtensions"`. Relative imports use the `.ts` extension the actual file on disk:
52
52
 
53
53
  ```ts
54
- import { buildApp } from "./build-app.js"; // ./build-app.ts on disk
54
+ import { buildApp } from "./build-app.ts";
55
55
  ```
56
56
 
57
- TypeScript resolves the `.js` specifier to the matching `.ts` file at typecheck, and the compiled output really is `.js`. This is the official Node ESM conventionnot a typo.
57
+ On `pnpm build`, TypeScript rewrites the `.ts` specifier to `.js` in the compiled `dist/` output, so the deployed code is valid Node ESM. (Node ESM has no extensionless relative imports `.ts` is the most natural form available.)
58
58
 
59
59
  ## What's included
60
60
 
@@ -1,3 +1,13 @@
1
+ ---
2
+ name: daloyjs-best-practices
3
+ description: >-
4
+ Best practices for building, testing, and hardening this DaloyJS REST API on
5
+ Node.js. Use when adding or changing HTTP routes, Zod schemas, middleware, or
6
+ error handling; regenerating the OpenAPI spec or the typed Hey API client; or
7
+ working on auth, rate limits, secrets, and the project's quality gates.
8
+ license: MIT
9
+ ---
10
+
1
11
  # SKILL.md — DaloyJS best practices (Node)
2
12
 
3
13
  Operational guidance and best practices for AI coding agents working in this
@@ -18,7 +18,7 @@
18
18
  "audit": "pnpm audit --prod"
19
19
  },
20
20
  "dependencies": {
21
- "@daloyjs/core": "^0.36.0",
21
+ "@daloyjs/core": "^0.38.0",
22
22
  "zod": "^4.4.3"
23
23
  },
24
24
  "devDependencies": {
@@ -1,6 +1,6 @@
1
1
  import { writeFile, mkdir } from "node:fs/promises";
2
2
  import { generateOpenAPI } from "@daloyjs/core/openapi";
3
- import { buildApp } from "../src/build-app.js";
3
+ import { buildApp } from "../src/build-app.ts";
4
4
 
5
5
  // Build a fresh app from the factory so the spec dump never starts the HTTP
6
6
  // listener as a side effect. Keep this script deterministic so codegen output
@@ -1,6 +1,6 @@
1
1
  import { serve } from "@daloyjs/core/node";
2
2
  import { printStartupBanner, type StartupBannerLink } from "@daloyjs/core/banner";
3
- import { buildApp } from "./build-app.js";
3
+ import { buildApp } from "./build-app.ts";
4
4
 
5
5
  const app = buildApp();
6
6
  const port = Number(process.env.PORT ?? 3000);
@@ -11,7 +11,9 @@
11
11
  "esModuleInterop": true,
12
12
  "skipLibCheck": true,
13
13
  "forceConsistentCasingInFileNames": true,
14
- "resolveJsonModule": true
14
+ "resolveJsonModule": true,
15
+ "allowImportingTsExtensions": true,
16
+ "rewriteRelativeImportExtensions": true
15
17
  },
16
18
  "include": ["src/**/*", "scripts/**/*", "tests/**/*"],
17
19
  "exclude": ["node_modules", "dist"]
@@ -21,13 +21,13 @@ A [DaloyJS](https://daloyjs.dev) REST API deployed to **Vercel Edge**. **Contrac
21
21
 
22
22
  ## Imports
23
23
 
24
- This project uses TypeScript with `"module": "NodeNext"` (ESM). Relative imports **must include a `.js` extension**, even when the source file is `.ts`:
24
+ This project uses TypeScript with `"allowImportingTsExtensions"`, so relative imports use the **`.ts` extension** the actual file you see on disk:
25
25
 
26
26
  ```ts
27
- import handler from "../api/[...path].js"; // resolves to the .ts source at typecheck, .js at runtime
27
+ import handler from "../api/[...path].ts";
28
28
  ```
29
29
 
30
- This is the official Node.js ESM convention TypeScript rewrites the specifier during typecheck, and the deployed output really is `.js`. Bare-specifier imports from packages (`@daloyjs/core`, `zod`, …) do not need an extension.
30
+ You import the file you see. Vercel bundles the `api/` functions at deploy time and resolves `.ts` directly, and the test runner (tsx) does too. Bare-specifier imports from packages (`@daloyjs/core`, `zod`, …) do not need an extension.
31
31
 
32
32
  ## Core rules
33
33
 
@@ -0,0 +1 @@
1
+ @AGENTS.md
@@ -59,13 +59,13 @@ That catch-all API route lets DaloyJS own routing while Vercel handles the runti
59
59
 
60
60
  ## Imports
61
61
 
62
- This project uses TypeScript with `"module": "NodeNext"` (ESM). Relative imports must include a `.js` extension, even when the source file is `.ts`:
62
+ This project uses TypeScript with `"allowImportingTsExtensions"`. Relative imports use the `.ts` extension the actual file on disk:
63
63
 
64
64
  ```ts
65
- import handler from "../api/[...path].js"; // ../api/[...path].ts on disk
65
+ import handler from "../api/[...path].ts";
66
66
  ```
67
67
 
68
- TypeScript resolves the `.js` specifier to the matching `.ts` file at typecheck, and the deployed output really is `.js`. This is the official Node ESM convention — not a typo.
68
+ Vercel bundles the `api/` functions at deploy time and resolves `.ts` directly, and the test runner (tsx) does too.
69
69
 
70
70
  ## What's included
71
71
 
@@ -1,3 +1,15 @@
1
+ ---
2
+ name: daloyjs-best-practices
3
+ description: >-
4
+ Best practices for building, testing, and hardening this DaloyJS REST API on
5
+ Vercel Edge. Use when adding or changing HTTP routes, Zod schemas,
6
+ middleware, or error handling; regenerating the OpenAPI spec or the typed
7
+ Hey API client; keeping the catch-all Edge entrypoint and Web-Standard
8
+ runtime constraints; or working on auth, rate limits, secrets, and the
9
+ project's quality gates.
10
+ license: MIT
11
+ ---
12
+
1
13
  # SKILL.md — DaloyJS best practices (Vercel Edge)
2
14
 
3
15
  Operational guidance and best practices for AI coding agents working in this
@@ -11,7 +11,7 @@
11
11
  "audit": "pnpm audit --prod"
12
12
  },
13
13
  "dependencies": {
14
- "@daloyjs/core": "^0.36.0",
14
+ "@daloyjs/core": "^0.38.0",
15
15
  "zod": "^4.4.3"
16
16
  },
17
17
  "devDependencies": {
@@ -1,6 +1,6 @@
1
1
  import assert from "node:assert/strict";
2
2
  import test from "node:test";
3
- import handler from "../api/[...path].js";
3
+ import handler from "../api/[...path].ts";
4
4
 
5
5
  test("Vercel Edge handler responds through DaloyJS", async () => {
6
6
  const response = await handler(new Request("https://example.test/healthz"));
@@ -12,7 +12,8 @@
12
12
  "forceConsistentCasingInFileNames": true,
13
13
  "resolveJsonModule": true,
14
14
  "isolatedModules": true,
15
- "noEmit": true
15
+ "noEmit": true,
16
+ "allowImportingTsExtensions": true
16
17
  },
17
18
  "include": ["api/**/*.ts", "tests/**/*.ts"]
18
19
  }