create-daloy 1.4.0 → 1.4.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/bin/create-daloy.mjs +61 -6
- package/package.json +1 -1
- package/sbom.cdx.json +9 -9
- package/sbom.spdx.json +5 -5
- package/templates/_ci/deno/_github/workflows/container-scan.yml +10 -0
- package/templates/_ci/deno/_github/workflows/deploy.yml +9 -0
- package/templates/_ci/node/_github/workflows/ci.yml +1 -5
- package/templates/_ci/node/_github/workflows/container-scan.yml +10 -0
- package/templates/bun-basic/AGENTS.md +1 -1
- package/templates/bun-basic/README.md +6 -1
- package/templates/bun-basic/_Dockerfile +6 -3
- package/templates/bun-basic/_agents/skills/daloyjs-best-practices/SKILL.md +6 -6
- package/templates/bun-basic/_dockerignore +4 -1
- package/templates/bun-basic/_env.example +12 -0
- package/templates/bun-basic/_gitignore +5 -0
- package/templates/bun-basic/_npmrc +5 -3
- package/templates/bun-basic/package.json +3 -3
- package/templates/bun-basic/pnpm-workspace.yaml +19 -5
- package/templates/bun-basic/src/index.ts +3 -1
- package/templates/bun-basic/tsconfig.json +1 -1
- package/templates/cloudflare-worker/AGENTS.md +1 -1
- package/templates/cloudflare-worker/README.md +6 -1
- package/templates/cloudflare-worker/_Dockerfile +1 -1
- package/templates/cloudflare-worker/_agents/skills/daloyjs-best-practices/SKILL.md +56 -45
- package/templates/cloudflare-worker/_dockerignore +6 -1
- package/templates/cloudflare-worker/_gitignore +5 -0
- package/templates/cloudflare-worker/_npmrc +3 -0
- package/templates/cloudflare-worker/package.json +3 -3
- package/templates/cloudflare-worker/pnpm-workspace.yaml +16 -0
- package/templates/cloudflare-worker/src/index.ts +8 -7
- package/templates/deno-basic/AGENTS.md +1 -1
- package/templates/deno-basic/README.md +1 -1
- package/templates/deno-basic/_Dockerfile +10 -6
- package/templates/deno-basic/_agents/skills/daloyjs-best-practices/SKILL.md +4 -4
- package/templates/deno-basic/_dockerignore +4 -1
- package/templates/deno-basic/_gitignore +5 -0
- package/templates/deno-basic/deno.json +3 -3
- package/templates/deno-basic/deno.lock +4 -4
- package/templates/node-basic/README.md +5 -0
- package/templates/node-basic/_Dockerfile +6 -3
- package/templates/node-basic/_agents/skills/daloyjs-best-practices/SKILL.md +8 -8
- package/templates/node-basic/_dockerignore +4 -1
- package/templates/node-basic/_env.example +11 -0
- package/templates/node-basic/_gitignore +5 -0
- package/templates/node-basic/_npmrc +3 -0
- package/templates/node-basic/package.json +2 -2
- package/templates/node-basic/pnpm-workspace.yaml +16 -0
- package/templates/vercel/AGENTS.md +1 -1
- package/templates/vercel/README.md +19 -2
- package/templates/vercel/_Dockerfile +10 -9
- package/templates/vercel/_agents/skills/daloyjs-best-practices/SKILL.md +2 -2
- package/templates/vercel/_dockerignore +4 -1
- package/templates/vercel/_env.example +9 -1
- package/templates/vercel/_gitignore +5 -0
- package/templates/vercel/_npmrc +3 -0
- package/templates/vercel/_vercelignore +10 -0
- package/templates/vercel/api/index.ts +4 -3
- package/templates/vercel/package.json +3 -3
- package/templates/vercel/pnpm-workspace.yaml +16 -0
package/bin/create-daloy.mjs
CHANGED
|
@@ -95,6 +95,9 @@ const RENAME_ON_COPY = new Map([
|
|
|
95
95
|
["_github", ".github"],
|
|
96
96
|
["_Dockerfile", "Dockerfile"],
|
|
97
97
|
["_dockerignore", ".dockerignore"],
|
|
98
|
+
// Keeps local secrets out of `vercel deploy` uploads: the Vercel CLI does
|
|
99
|
+
// not apply .gitignore to what it uploads.
|
|
100
|
+
["_vercelignore", ".vercelignore"],
|
|
98
101
|
// Directory: holds skill files for AI coding agents under
|
|
99
102
|
// `.agents/skills/<skill-name>/SKILL.md`. Templates author this as
|
|
100
103
|
// `_agents/` so npm pack does not drop the dotfolder during publish.
|
|
@@ -548,6 +551,10 @@ const TOOL_INSTALL_GUIDES = {
|
|
|
548
551
|
const MIN_NODE_MAJOR = 24;
|
|
549
552
|
const MIN_NPM_MAJOR = 12;
|
|
550
553
|
const MIN_PNPM_MAJOR = 11;
|
|
554
|
+
// Exact pnpm used by the generated CI (pnpm/action-setup) and, for templates
|
|
555
|
+
// whose platform needs it, by package.json#packageManager. One constant so the
|
|
556
|
+
// two can never drift.
|
|
557
|
+
const PINNED_PNPM_VERSION = "11.1.3";
|
|
551
558
|
|
|
552
559
|
/**
|
|
553
560
|
* Parse the leading major-version integer out of a version string such as
|
|
@@ -719,7 +726,7 @@ async function copyTemplate(src, dest) {
|
|
|
719
726
|
}
|
|
720
727
|
}
|
|
721
728
|
|
|
722
|
-
async function patchPackageJson(dir, projectName, packageManager) {
|
|
729
|
+
async function patchPackageJson(dir, projectName, packageManager, template) {
|
|
723
730
|
const file = path.join(dir, "package.json");
|
|
724
731
|
if (!existsSync(file)) return;
|
|
725
732
|
const raw = await readFile(file, "utf8");
|
|
@@ -742,6 +749,11 @@ async function patchPackageJson(dir, projectName, packageManager) {
|
|
|
742
749
|
}
|
|
743
750
|
if (packageManager === "pnpm") {
|
|
744
751
|
json.engines = { ...json.engines, pnpm: `>=${MIN_PNPM_MAJOR}.0.0` };
|
|
752
|
+
// Vercel otherwise picks pnpm from the lockfile format (9.x/10.x), which
|
|
753
|
+
// fails the engines.pnpm floor above and aborts every deploy. With this
|
|
754
|
+
// field plus the ENABLE_EXPERIMENTAL_COREPACK=1 project variable (see the
|
|
755
|
+
// template README), Vercel builds with the same pnpm as CI.
|
|
756
|
+
if (template === "vercel") json.packageManager = `pnpm@${PINNED_PNPM_VERSION}`;
|
|
745
757
|
}
|
|
746
758
|
await writeFile(file, JSON.stringify(json, null, 2) + "\n", "utf8");
|
|
747
759
|
}
|
|
@@ -767,16 +779,27 @@ async function patchTemplateTextFiles(dir, packageManager) {
|
|
|
767
779
|
}
|
|
768
780
|
|
|
769
781
|
function rewritePackageManagerText(raw, packageManager) {
|
|
770
|
-
|
|
782
|
+
// The release-day cooldown note names pnpm's settings file. Bun scaffolds
|
|
783
|
+
// get the same cooldown from bunfig.toml; npm and yarn scaffolds have no
|
|
784
|
+
// cooldown configured, so the note would be wrong there and is dropped.
|
|
785
|
+
const cooldownNote =
|
|
786
|
+
/> \*\*Install refused right after a DaloyJS release\?\*\*[\s\S]*?safeguard off\.\r?\n\r?\n/;
|
|
787
|
+
const withCooldownNote =
|
|
788
|
+
packageManager === "bun"
|
|
789
|
+
? raw.replace("`pnpm-workspace.yaml`), a supply-chain", "`bunfig.toml`), a supply-chain")
|
|
790
|
+
: raw.replace(cooldownNote, "");
|
|
791
|
+
return withCooldownNote
|
|
771
792
|
.replace(
|
|
772
793
|
"Package manager: pnpm (use `pnpm` unless the project's `package.json` was rewritten for npm/yarn/bun).",
|
|
773
794
|
`Package manager: ${packageManager}.`
|
|
774
795
|
)
|
|
796
|
+
.replace("Package manager: pnpm.", `Package manager: ${packageManager}.`)
|
|
775
797
|
.replace(/\bpnpm install\b/g, `${packageManager} install`)
|
|
776
798
|
.replace(/\bpnpm gen:openapi\b/g, `${packageManager} run gen:openapi`)
|
|
777
799
|
.replace(/\bpnpm gen:client\b/g, `${packageManager} run gen:client`)
|
|
778
800
|
.replace(/\bpnpm typecheck\b/g, `${packageManager} run typecheck`)
|
|
779
801
|
.replace(/\bpnpm build\b/g, `${packageManager} run build`)
|
|
802
|
+
.replace(/\bpnpm run deploy\b/g, `${packageManager} run deploy`)
|
|
780
803
|
.replace(/\bpnpm deploy\b/g, `${packageManager} run deploy`)
|
|
781
804
|
.replace(/\bpnpm dev\b/g, `${packageManager} run dev`)
|
|
782
805
|
.replace(/\bpnpm gen\b/g, `${packageManager} run gen`)
|
|
@@ -843,6 +866,20 @@ const NPM_STRICT_NPMRC = `# DaloyJS install-time guardrails for the npm CLI.
|
|
|
843
866
|
engine-strict=true
|
|
844
867
|
`;
|
|
845
868
|
|
|
869
|
+
// Bun reads its install settings from bunfig.toml, not from pnpm-workspace.yaml
|
|
870
|
+
// or .npmrc. Without this file a Bun scaffold would have no release-age
|
|
871
|
+
// cooldown at all, silently dropping the 24h supply-chain floor the pnpm path
|
|
872
|
+
// enforces. `minimumReleaseAge` is in seconds and needs Bun >= 1.3; older Bun
|
|
873
|
+
// ignores the key, which is why bun-basic's engines floor is >= 1.3.0.
|
|
874
|
+
const BUN_BUNFIG = `# DaloyJS install-time guardrails for Bun.
|
|
875
|
+
#
|
|
876
|
+
# Wait 24h (86400 seconds) before resolving a freshly published version.
|
|
877
|
+
# Most npm worm campaigns are detected and unpublished within hours. Set to 0
|
|
878
|
+
# only for a real hotfix. Requires Bun >= 1.3.
|
|
879
|
+
[install]
|
|
880
|
+
minimumReleaseAge = 86400
|
|
881
|
+
`;
|
|
882
|
+
|
|
846
883
|
async function normalizePackageManagerFiles(dir, packageManager) {
|
|
847
884
|
if (packageManager === "pnpm") return;
|
|
848
885
|
// pnpm-workspace.yaml is pnpm-only; drop it for every other manager.
|
|
@@ -860,6 +897,9 @@ async function normalizePackageManagerFiles(dir, packageManager) {
|
|
|
860
897
|
} else if (existsSync(npmrc)) {
|
|
861
898
|
await rm(npmrc, { force: true });
|
|
862
899
|
}
|
|
900
|
+
if (packageManager === "bun") {
|
|
901
|
+
await writeFile(path.join(dir, "bunfig.toml"), BUN_BUNFIG, "utf8");
|
|
902
|
+
}
|
|
863
903
|
}
|
|
864
904
|
|
|
865
905
|
function dockerInstallSnippet(packageManager) {
|
|
@@ -902,7 +942,7 @@ async function patchDockerfileForPackageManager(dir, packageManager) {
|
|
|
902
942
|
// an `\n`-only pattern would silently fail to match there, leaving the
|
|
903
943
|
// pnpm-specific COPY/RUN lines in place for npm/yarn/bun scaffolds.
|
|
904
944
|
let next = raw.replace(
|
|
905
|
-
/COPY package\.json pnpm-lock\.yaml\* \.\/\r?\nRUN corepack enable && corepack prepare pnpm@
|
|
945
|
+
/COPY package\.json pnpm-lock\.yaml\* \.\/\r?\nRUN corepack enable && corepack prepare pnpm@[\w.-]+ --activate && \\\r?\n\s+pnpm install --frozen-lockfile --ignore-scripts/,
|
|
906
946
|
`${install.copy}\n${install.run}`
|
|
907
947
|
);
|
|
908
948
|
|
|
@@ -1031,7 +1071,7 @@ function setupPackageManagerStep(packageManager) {
|
|
|
1031
1071
|
return ` - name: Set up pnpm
|
|
1032
1072
|
uses: pnpm/action-setup@ac6db6d3c1f721f886538a378a2d73e85697340a # v6
|
|
1033
1073
|
with:
|
|
1034
|
-
version:
|
|
1074
|
+
version: ${PINNED_PNPM_VERSION}
|
|
1035
1075
|
run_install: false`;
|
|
1036
1076
|
}
|
|
1037
1077
|
if (packageManager === "yarn") {
|
|
@@ -1329,9 +1369,23 @@ async function addDenoSecurityTasks(dir) {
|
|
|
1329
1369
|
*/
|
|
1330
1370
|
function nodeVersionStrategy(template) {
|
|
1331
1371
|
if (template === "bun-basic") {
|
|
1332
|
-
return {
|
|
1372
|
+
return {
|
|
1373
|
+
strategy: "",
|
|
1374
|
+
version: "24",
|
|
1375
|
+
checksNote: [
|
|
1376
|
+
"# The verify job runs once (Bun is the runtime, so there is no Node",
|
|
1377
|
+
"# version matrix). Protect `main` with the single `Verify` check.",
|
|
1378
|
+
].join("\n"),
|
|
1379
|
+
};
|
|
1333
1380
|
}
|
|
1334
1381
|
return {
|
|
1382
|
+
checksNote: [
|
|
1383
|
+
"# The verify job runs as a matrix across both ends of the `engines.node`",
|
|
1384
|
+
"# range declared in package.json, so a change that only works on one of",
|
|
1385
|
+
"# them fails here instead of in production. That means the required status",
|
|
1386
|
+
"# checks to protect `main` with are the per-version jobs (`Verify (24)`,",
|
|
1387
|
+
"# `Verify (26)`), not a single `Verify`.",
|
|
1388
|
+
].join("\n"),
|
|
1335
1389
|
strategy: [
|
|
1336
1390
|
" strategy:",
|
|
1337
1391
|
" fail-fast: false",
|
|
@@ -1445,6 +1499,7 @@ function renderCiReplacements({
|
|
|
1445
1499
|
["__BUILD_STEP__", buildStep],
|
|
1446
1500
|
["__CONTRACT_STEP__", contractStep],
|
|
1447
1501
|
["__CI_NODE_STRATEGY__", nodeVersions.strategy],
|
|
1502
|
+
["__CI_REQUIRED_CHECKS_NOTE__", nodeVersions.checksNote],
|
|
1448
1503
|
["__CI_NODE_VERSION__", nodeVersions.version],
|
|
1449
1504
|
["__DAST_LAUNCH_COMMAND__", dast.command],
|
|
1450
1505
|
["__DAST_EXTRA_ENV__", dast.extraEnv],
|
|
@@ -2339,7 +2394,7 @@ async function main() {
|
|
|
2339
2394
|
logStep("Minimal mode applied", `${count} file${count === 1 ? "" : "s"} trimmed`);
|
|
2340
2395
|
}
|
|
2341
2396
|
if (!skipPackageManager) {
|
|
2342
|
-
await patchPackageJson(targetDir, packageName, packageManager);
|
|
2397
|
+
await patchPackageJson(targetDir, packageName, packageManager, template);
|
|
2343
2398
|
logStep("Package metadata written", packageName);
|
|
2344
2399
|
await patchTemplateTextFiles(targetDir, packageManager);
|
|
2345
2400
|
await patchDockerignoreForPackageManager(targetDir, packageManager);
|
package/package.json
CHANGED
package/sbom.cdx.json
CHANGED
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
{
|
|
2
2
|
"bomFormat": "CycloneDX",
|
|
3
3
|
"specVersion": "1.5",
|
|
4
|
-
"serialNumber": "urn:uuid:
|
|
4
|
+
"serialNumber": "urn:uuid:8cdfc2e0-10ba-5bf1-adfd-7b858b186eeb",
|
|
5
5
|
"version": 1,
|
|
6
6
|
"metadata": {
|
|
7
|
-
"timestamp": "2026-09-
|
|
7
|
+
"timestamp": "2026-09-29T19:00:55.389Z",
|
|
8
8
|
"tools": [
|
|
9
9
|
{
|
|
10
10
|
"vendor": "DaloyJS",
|
|
11
11
|
"name": "daloy-generate-sbom",
|
|
12
|
-
"version": "1.4.
|
|
12
|
+
"version": "1.4.1"
|
|
13
13
|
}
|
|
14
14
|
],
|
|
15
15
|
"authors": [],
|
|
16
16
|
"component": {
|
|
17
17
|
"type": "library",
|
|
18
|
-
"bom-ref": "pkg:npm/create-daloy@1.4.
|
|
18
|
+
"bom-ref": "pkg:npm/create-daloy@1.4.1",
|
|
19
19
|
"name": "create-daloy",
|
|
20
|
-
"version": "1.4.
|
|
20
|
+
"version": "1.4.1",
|
|
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@1.4.
|
|
22
|
+
"purl": "pkg:npm/create-daloy@1.4.1",
|
|
23
23
|
"licenses": [
|
|
24
24
|
{
|
|
25
25
|
"license": {
|
|
@@ -42,9 +42,9 @@
|
|
|
42
42
|
}
|
|
43
43
|
],
|
|
44
44
|
"swid": {
|
|
45
|
-
"tagId": "swidtag-create-daloy-1.4.
|
|
45
|
+
"tagId": "swidtag-create-daloy-1.4.1",
|
|
46
46
|
"name": "create-daloy",
|
|
47
|
-
"version": "1.4.
|
|
47
|
+
"version": "1.4.1",
|
|
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@1.4.
|
|
56
|
+
"ref": "pkg:npm/create-daloy@1.4.1",
|
|
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-1.4.
|
|
6
|
-
"documentNamespace": "https://github.com/daloyjs/daloy/sbom/create-daloy-1.4.
|
|
5
|
+
"name": "create-daloy-1.4.1",
|
|
6
|
+
"documentNamespace": "https://github.com/daloyjs/daloy/sbom/create-daloy-1.4.1-8cdfc2e0-10ba-5bf1-adfd-7b858b186eeb",
|
|
7
7
|
"creationInfo": {
|
|
8
|
-
"created": "2026-09-
|
|
8
|
+
"created": "2026-09-29T19:00:55.389Z",
|
|
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": "1.4.
|
|
19
|
+
"versionInfo": "1.4.1",
|
|
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@1.4.
|
|
30
|
+
"referenceLocator": "pkg:npm/create-daloy@1.4.1"
|
|
31
31
|
}
|
|
32
32
|
]
|
|
33
33
|
}
|
|
@@ -116,6 +116,16 @@ jobs:
|
|
|
116
116
|
ref="${ref%% as *}"
|
|
117
117
|
ref="${ref## }"
|
|
118
118
|
ref="${ref%% }"
|
|
119
|
+
# Resolve `${NAME}` against this Dockerfile's `ARG NAME=default`
|
|
120
|
+
# line, so ARG-driven base images are checked instead of skipped.
|
|
121
|
+
case "$ref" in
|
|
122
|
+
'${'*'}')
|
|
123
|
+
name="${ref#??}"
|
|
124
|
+
name="${name%?}"
|
|
125
|
+
def="$(grep -E "^ARG ${name}=" Dockerfile | head -n 1 | cut -d= -f2-)"
|
|
126
|
+
[ -n "$def" ] && ref="$def"
|
|
127
|
+
;;
|
|
128
|
+
esac
|
|
119
129
|
case "$ref" in
|
|
120
130
|
scratch|"\${"*|*"@sha256:"*) ;;
|
|
121
131
|
*)
|
|
@@ -59,9 +59,18 @@ jobs:
|
|
|
59
59
|
with:
|
|
60
60
|
deno-version: v2.x
|
|
61
61
|
|
|
62
|
+
- name: Install dependencies (frozen lockfile)
|
|
63
|
+
# Same integrity gate as ci.yml: fail when any dependency does not
|
|
64
|
+
# match the committed deno.lock, so a deploy never ships something
|
|
65
|
+
# the OSV scan did not see.
|
|
66
|
+
run: deno install --frozen=true
|
|
67
|
+
|
|
62
68
|
- name: Typecheck
|
|
63
69
|
run: deno task typecheck
|
|
64
70
|
|
|
71
|
+
- name: Contract check
|
|
72
|
+
run: deno task contract
|
|
73
|
+
|
|
65
74
|
- name: Test
|
|
66
75
|
run: deno task test
|
|
67
76
|
|
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
# Hardened CI generated by create-daloy --with-ci.
|
|
2
2
|
#
|
|
3
|
-
|
|
4
|
-
# range declared in package.json, so a change that only works on one of
|
|
5
|
-
# them fails here instead of in production. That means the required status
|
|
6
|
-
# checks to protect `main` with are the per-version jobs (`Verify (24)`,
|
|
7
|
-
# `Verify (26)`), not a single `Verify`.
|
|
3
|
+
__CI_REQUIRED_CHECKS_NOTE__
|
|
8
4
|
name: CI
|
|
9
5
|
|
|
10
6
|
on:
|
|
@@ -134,6 +134,16 @@ jobs:
|
|
|
134
134
|
ref="${ref%% as *}"
|
|
135
135
|
ref="${ref## }"
|
|
136
136
|
ref="${ref%% }"
|
|
137
|
+
# Resolve `${NAME}` against this Dockerfile's `ARG NAME=default`
|
|
138
|
+
# line, so ARG-driven base images are checked instead of skipped.
|
|
139
|
+
case "$ref" in
|
|
140
|
+
'${'*'}')
|
|
141
|
+
name="${ref#??}"
|
|
142
|
+
name="${name%?}"
|
|
143
|
+
def="$(grep -E "^ARG ${name}=" Dockerfile | head -n 1 | cut -d= -f2-)"
|
|
144
|
+
[ -n "$def" ] && ref="$def"
|
|
145
|
+
;;
|
|
146
|
+
esac
|
|
137
147
|
case "$ref" in
|
|
138
148
|
scratch|"\${"*|*"@sha256:"*) ;;
|
|
139
149
|
*)
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
A [DaloyJS](https://daloyjs.dev) REST API for the [Bun](https://bun.sh) runtime. **Contract-first**: routes are defined with validation schemas (Zod in this template; DaloyJS also supports Standard Schema-compatible validators) and OpenAPI 3.1 is generated from them. With `docs: "auto"` in `new App({...})`, three routes are auto-mounted outside production: `GET /openapi.json`, `GET /openapi.yaml`, and `GET /docs` (Scalar UI).
|
|
4
4
|
|
|
5
|
-
- Package manager
|
|
5
|
+
- Runtime: Bun. Package manager: pnpm.
|
|
6
6
|
|
|
7
7
|
## Agent guidance
|
|
8
8
|
|
|
@@ -5,7 +5,7 @@ A [DaloyJS](https://daloyjs.dev) starter for the [Bun](https://bun.sh) runtime.
|
|
|
5
5
|
## Develop
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
|
|
8
|
+
pnpm install
|
|
9
9
|
bun run dev # http://localhost:3000
|
|
10
10
|
```
|
|
11
11
|
|
|
@@ -18,6 +18,11 @@ curl http://localhost:3000/books/1
|
|
|
18
18
|
<!-- daloy-minimal:strip-end books -->
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
+
> **Install refused right after a DaloyJS release?** New installs wait 24 hours
|
|
22
|
+
> before using a freshly published version (`minimumReleaseAge` in
|
|
23
|
+
> `pnpm-workspace.yaml`), a supply-chain safeguard. If `@daloyjs/core` was just
|
|
24
|
+
> released, retry a few hours later rather than turning the safeguard off.
|
|
25
|
+
|
|
21
26
|
<!-- daloy-minimal:strip-start docs -->
|
|
22
27
|
|
|
23
28
|
## API documentation
|
|
@@ -22,8 +22,11 @@
|
|
|
22
22
|
# docker build --build-arg \
|
|
23
23
|
# NODE_IMAGE=node:24-alpine@sha256:<digest> \
|
|
24
24
|
# BUN_IMAGE=oven/bun:1-alpine@sha256:<digest> .
|
|
25
|
-
# Dependabot's `docker` ecosystem
|
|
26
|
-
#
|
|
25
|
+
# The default is a floating tag, and Dependabot's `docker` ecosystem
|
|
26
|
+
# cannot update a `FROM ${ARG}` line. For automatic digest updates,
|
|
27
|
+
# write the digest into the ARG default below (for example
|
|
28
|
+
# `ARG NODE_IMAGE=node:24-alpine@sha256:<digest>`).
|
|
29
|
+
# The companion `container-scan.yml`
|
|
27
30
|
# workflow lints this file with hadolint and scans the built image
|
|
28
31
|
# with Trivy on every PR.
|
|
29
32
|
|
|
@@ -35,7 +38,7 @@ FROM ${NODE_IMAGE} AS builder
|
|
|
35
38
|
WORKDIR /app
|
|
36
39
|
# Install deps in a layer that only invalidates when manifests change.
|
|
37
40
|
COPY package.json pnpm-lock.yaml* ./
|
|
38
|
-
RUN corepack enable && corepack prepare pnpm@
|
|
41
|
+
RUN corepack enable && corepack prepare pnpm@11.1.3 --activate && \
|
|
39
42
|
pnpm install --frozen-lockfile --ignore-scripts
|
|
40
43
|
COPY . .
|
|
41
44
|
|
|
@@ -201,7 +201,7 @@ app.get(
|
|
|
201
201
|
- Throw typed errors from `@daloyjs/core` — they serialize to RFC 9457
|
|
202
202
|
problem responses.
|
|
203
203
|
- Add a `responses[code]` entry for every error you throw.
|
|
204
|
-
- Do not swallow errors. Log via `ctx.log.error(err, "context")` and
|
|
204
|
+
- Do not swallow errors. Log via `ctx.state.log.error({ err }, "context")` and
|
|
205
205
|
rethrow if recovery is impossible.
|
|
206
206
|
|
|
207
207
|
## Middleware
|
|
@@ -267,15 +267,15 @@ Tests run with `bun test`. Use **in-process** requests through
|
|
|
267
267
|
|
|
268
268
|
```ts
|
|
269
269
|
import { test, expect } from "bun:test";
|
|
270
|
-
import { buildApp } from "../src/build-app";
|
|
270
|
+
import { buildApp } from "../src/build-app.ts";
|
|
271
271
|
|
|
272
272
|
test("GET /healthz returns ok", async () => {
|
|
273
273
|
const app = buildApp();
|
|
274
274
|
const res = await app.request("/healthz");
|
|
275
275
|
expect(res.status).toBe(200);
|
|
276
|
-
const body = await res.json();
|
|
276
|
+
const body = (await res.json()) as { ok: boolean; runtime: string };
|
|
277
277
|
expect(body.ok).toBe(true);
|
|
278
|
-
expect(
|
|
278
|
+
expect(body.runtime).toBe("bun");
|
|
279
279
|
});
|
|
280
280
|
```
|
|
281
281
|
|
|
@@ -324,7 +324,7 @@ Aim for complete happy- and unhappy-path test coverage of the routes you add.
|
|
|
324
324
|
of it when URLs can be influenced by users or tenants. SSRF protections
|
|
325
325
|
should fail closed for private ranges and cloud metadata endpoints.
|
|
326
326
|
- Bun ships its own audit story; check `bun pm audit` periodically and
|
|
327
|
-
pin versions in
|
|
327
|
+
pin versions in the committed lockfile.
|
|
328
328
|
|
|
329
329
|
## CI and workflows (`--with-ci` scaffolds)
|
|
330
330
|
|
|
@@ -334,7 +334,7 @@ reference. Skip that file for ordinary route work.
|
|
|
334
334
|
|
|
335
335
|
## Logging & observability
|
|
336
336
|
|
|
337
|
-
- Use the framework logger via `ctx.log` — it carries the request id
|
|
337
|
+
- Use the framework logger via `ctx.state.log` — it carries the request id
|
|
338
338
|
automatically.
|
|
339
339
|
- Avoid `console.log` in production code paths; the structured logger
|
|
340
340
|
emits JSON for log aggregators.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Copy to .env for local use (Bun loads .env automatically). Real secrets
|
|
2
|
+
# belong in your platform's secret store, never in git (.env is git-ignored).
|
|
3
|
+
PORT=3000
|
|
4
|
+
|
|
5
|
+
# Number of reverse-proxy hops in front of the app (load balancer, PaaS edge,
|
|
6
|
+
# CDN). Required in production behind any proxy: without it DaloyJS refuses
|
|
7
|
+
# forwarded headers and answers 500. rateLimit() also uses it to key each
|
|
8
|
+
# client instead of the proxy. Leave unset when clients connect directly.
|
|
9
|
+
# TRUST_PROXY_HOPS=1
|
|
10
|
+
|
|
11
|
+
# Public base URL, used as the OpenAPI `servers` entry (no localhost default).
|
|
12
|
+
# PUBLIC_URL=https://api.example.com
|
|
@@ -13,3 +13,8 @@ bun.lockb
|
|
|
13
13
|
# consumer's lockfile and can pin transitives to unsigned tarballs.
|
|
14
14
|
# See https://socket.dev/blog/understanding-the-security-concerns-of-npm-shrinkwrap
|
|
15
15
|
npm-shrinkwrap.json
|
|
16
|
+
|
|
17
|
+
# Private keys and certificates (keep them in a secret manager, not git).
|
|
18
|
+
*.pem
|
|
19
|
+
*.key
|
|
20
|
+
*.p12
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
# DaloyJS supply-chain hardening defaults.
|
|
2
2
|
#
|
|
3
|
-
#
|
|
4
|
-
# starter back to pnpm inherit the same secure-by-default install posture.
|
|
3
|
+
# pnpm is this template's default package manager (Bun is the runtime).
|
|
5
4
|
# When you scaffold with `--package-manager bun` the CLI removes this file
|
|
6
|
-
#
|
|
5
|
+
# and writes bunfig.toml with the same 24h cooldown instead.
|
|
6
|
+
#
|
|
7
|
+
# pnpm 11+ reads its settings from pnpm-workspace.yaml, not from this file,
|
|
8
|
+
# so the same values are set there. They stay here for npm and older tooling.
|
|
7
9
|
|
|
8
10
|
auto-install-peers=true
|
|
9
11
|
strict-peer-dependencies=true
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"private": true,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
7
|
-
"bun": ">=1.
|
|
7
|
+
"bun": ">=1.3.0"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
10
|
"dev": "daloy dev --runtime bun",
|
|
@@ -19,12 +19,12 @@
|
|
|
19
19
|
"hooks:install": "git config core.hooksPath .githooks"
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@daloyjs/core": "^1.4.
|
|
22
|
+
"@daloyjs/core": "^1.4.1",
|
|
23
23
|
"zod": "^4.4.3"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
26
|
"@hey-api/openapi-ts": "0.0.0-next-20260711024907",
|
|
27
|
-
"@types/bun": "^1.
|
|
27
|
+
"@types/bun": "^1.3.0",
|
|
28
28
|
"typescript": "^7.0.2"
|
|
29
29
|
}
|
|
30
30
|
}
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
# pnpm workspace-level supply-chain settings.
|
|
2
2
|
#
|
|
3
|
-
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
# scaffolder keeps this file so pnpm 11+ picks up the workspace-level
|
|
7
|
-
# supply-chain controls below. If you keep using Bun, you can delete it.
|
|
3
|
+
# pnpm is this template's default package manager (Bun is the runtime).
|
|
4
|
+
# Scaffolding with `--package-manager bun` removes this file and writes a
|
|
5
|
+
# bunfig.toml with the same 24h release-age cooldown instead.
|
|
8
6
|
#
|
|
9
7
|
# pnpm 11+ reads `pnpm-workspace.yaml` as a settings file even when no
|
|
10
8
|
# `packages:` list is declared. It is the only file format that accepts
|
|
@@ -23,3 +21,19 @@ minimumReleaseAge: 1440
|
|
|
23
21
|
# Transitive deps must resolve from the configured registry, which makes
|
|
24
22
|
# typosquatted-tarball and compromised-fork attacks much harder to land.
|
|
25
23
|
blockExoticSubdeps: true
|
|
24
|
+
|
|
25
|
+
# pnpm 11+ ignores these keys in `.npmrc`, so they live here. `.npmrc` keeps
|
|
26
|
+
# the same values for older tooling.
|
|
27
|
+
#
|
|
28
|
+
# Never run dependency lifecycle scripts (postinstall / preinstall / prepare):
|
|
29
|
+
# the main execution channel of the chalk/debug, node-ipc and Shai-Hulud
|
|
30
|
+
# malware. If you later need a build step, allowlist that one package with
|
|
31
|
+
# `allowBuilds` instead of turning this off.
|
|
32
|
+
ignoreScripts: true
|
|
33
|
+
# Fail on unmet or incompatible peer dependencies instead of warning.
|
|
34
|
+
strictPeerDependencies: true
|
|
35
|
+
# Re-verify package integrity hashes against the lockfile on install.
|
|
36
|
+
verifyStoreIntegrity: true
|
|
37
|
+
# Use the lockfile as-is whenever it satisfies package.json.
|
|
38
|
+
preferFrozenLockfile: true
|
|
39
|
+
autoInstallPeers: true
|
|
@@ -11,7 +11,9 @@ const handle = serve(app, {
|
|
|
11
11
|
idleTimeout: 30,
|
|
12
12
|
});
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
// `handle.url` is a URL whose string form ends in "/"; use the origin so the
|
|
15
|
+
// links below do not become "//docs" (which 404s).
|
|
16
|
+
const url = handle.url ? handle.url.origin : `http://localhost:${port}`;
|
|
15
17
|
const links: StartupBannerLink[] = [
|
|
16
18
|
// daloy-minimal:strip-start docs
|
|
17
19
|
{ label: "API docs", url: `${url}/docs` },
|
|
@@ -19,7 +19,7 @@ A [DaloyJS](https://daloyjs.dev) REST API deployed to **Cloudflare Workers**. **
|
|
|
19
19
|
- `pnpm test` — run test suite
|
|
20
20
|
- `pnpm contract` — run `daloy inspect --check src/index.ts`
|
|
21
21
|
- `pnpm hooks:install` — enable the optional pre-push contract gate
|
|
22
|
-
- `pnpm deploy` — `wrangler deploy`
|
|
22
|
+
- `pnpm run deploy` — `wrangler deploy`
|
|
23
23
|
- `pnpm audit` — supply-chain audit
|
|
24
24
|
|
|
25
25
|
## Project shape
|
|
@@ -9,10 +9,15 @@ pnpm install
|
|
|
9
9
|
pnpm dev # http://localhost:8787
|
|
10
10
|
```
|
|
11
11
|
|
|
12
|
+
> **Install refused right after a DaloyJS release?** New installs wait 24 hours
|
|
13
|
+
> before using a freshly published version (`minimumReleaseAge` in
|
|
14
|
+
> `pnpm-workspace.yaml`), a supply-chain safeguard. If `@daloyjs/core` was just
|
|
15
|
+
> released, retry a few hours later rather than turning the safeguard off.
|
|
16
|
+
|
|
12
17
|
## Deploy
|
|
13
18
|
|
|
14
19
|
```bash
|
|
15
|
-
pnpm deploy
|
|
20
|
+
pnpm run deploy
|
|
16
21
|
```
|
|
17
22
|
|
|
18
23
|
`@daloyjs/core/cloudflare` exposes `toFetchHandler(app)`, so the same `App` you would use on Node also runs on Workers.
|
|
@@ -34,7 +34,7 @@ ARG NODE_IMAGE=node:24-alpine
|
|
|
34
34
|
FROM ${NODE_IMAGE} AS builder
|
|
35
35
|
WORKDIR /app
|
|
36
36
|
COPY package.json pnpm-lock.yaml* ./
|
|
37
|
-
RUN corepack enable && corepack prepare pnpm@
|
|
37
|
+
RUN corepack enable && corepack prepare pnpm@11.1.3 --activate && \
|
|
38
38
|
pnpm install --frozen-lockfile --ignore-scripts
|
|
39
39
|
COPY . .
|
|
40
40
|
|