cajonzero 0.0.1 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  # cajonzero
2
2
 
3
- Serverless(Cloudflare)的 BOX3 **仅 Play 端**引擎:内置账号系统,房间跑在
4
- Durable Object 上,地图放在 OSS/CDN。
3
+ 运行在 Cloudflare 上的 BOX3 游玩端引擎:内置账号系统,每个房间是一个
4
+ Durable Object,地图存放在 OSS/CDN。
5
5
 
6
6
  ## 安装
7
7
 
@@ -14,12 +14,12 @@ npx cj0 --help
14
14
 
15
15
  ```bash
16
16
  npm install
17
- npm run build # 构建大厅/后台
18
- npm run demo # http://127.0.0.1:8787
17
+ npm run build # 构建大厅与管理后台
18
+ npm run demo # 启动在 http://127.0.0.1:8787
19
19
  ```
20
20
 
21
- 打开页面后注册账号、选一张地图即可进入房间。
22
- 管理后台在 `/admin/login`,演示令牌为 `cajonzero-demo-admin-token`。
21
+ 打开页面,注册账号后选择地图即可进入房间。
22
+ 管理后台地址是 `/admin/login`,演示环境的令牌为 `cajonzero-demo-admin-token`。
23
23
 
24
24
  ## 创建自己的服务
25
25
 
@@ -30,19 +30,19 @@ npm install
30
30
  npm run dev # 本地预览
31
31
  ```
32
32
 
33
- 部署前配置三项:
33
+ 部署前需要完成以下配置:
34
34
 
35
35
  ```bash
36
36
  npx wrangler kv namespace create CAJONZERO_KV # 把返回的 id 填进 wrangler.jsonc
37
37
  npx wrangler secret put CAJONZERO_SESSION_SECRET # 会话签名密钥
38
- npx wrangler secret put BOX_PLAY_ADMIN_TOKEN # 管理后台令牌
38
+ npx wrangler secret put BOX_PLAY_ADMIN_TOKEN # 管理后台令牌
39
39
  npm run deploy
40
40
  ```
41
41
 
42
42
  ## 配置地图
43
43
 
44
- 地图不随 worker 上传。把解包后的 `.boxplay` 目录传到 OSS/S3/CDN,
45
- 再在 `cajonzero/maps.json` 中登记:
44
+ 地图文件需要单独上传,不随 worker 打包。把解包后的 `.boxplay` 目录上传到
45
+ OSS/S3/CDN,然后在 `cajonzero/maps.json` 中登记:
46
46
 
47
47
  ```json
48
48
  {
@@ -52,23 +52,23 @@ npm run deploy
52
52
  "slug": "race",
53
53
  "root": "https://<你的 CDN>/box3/race/",
54
54
  "worldId": "<世界 id>",
55
- "name": "显示名",
56
- "etag": "v1"
55
+ "name": "显示名"
57
56
  }
58
57
  ]
59
58
  }
60
59
  ```
61
60
 
62
- 地图内容更新时记得提升 `etag`(否则会命中 KV 里的旧缓存)。
63
- 房间参数、地图拉取策略等在 `cajonzero/config.json` 中调整。
61
+ 地图文件由客户端和房间直接向 CDN 拉取,服务端不做缓存。
62
+ 更新地图后请刷新 CDN 缓存,或改用带版本的路径。
63
+ 房间参数、地图拉取策略等配置位于 `cajonzero/config.json`。
64
64
 
65
65
  ## 常用命令
66
66
 
67
67
  ```bash
68
68
  cj0 serve [--port <port>] [--host <host>] # 本地启动
69
69
  cj0 create <folder> # 生成部署项目
70
- npm test # 运行测试
71
- npm run ci:gates # 完整检查(类型/测试/构建)
70
+ npm test # 运行测试
71
+ npm run ci:gates # 完整检查(类型/测试/构建)
72
72
  ```
73
73
 
74
- 更多细节见 [`docs/`](./docs/) 与 [`demo/`](./demo/README.md)。
74
+ 详细说明见 [`docs/`](./docs/)。
package/dist/bin/cj0.js CHANGED
@@ -12,8 +12,45 @@ var SCAFFOLD_ENTRY_CONTENT = [
12
12
  'import maps from "../cajonzero/maps.json";',
13
13
  "",
14
14
  'import { serve } from "cajonzero/serve";',
15
+ 'import type { Env } from "cajonzero/serve";',
16
+ 'import { createStorage } from "unstorage";',
17
+ 'import cloudflareKvDriver from "unstorage/drivers/cloudflare-kv-binding";',
15
18
  "",
16
- "export default serve({ config, maps });",
19
+ "/** Every env value the runtime needs is wired here \u2014 nothing is implicit. */",
20
+ "const vars = (env: Env): Record<string, string | undefined> =>",
21
+ " env as unknown as Record<string, string | undefined>;",
22
+ "const num = (value: string | undefined): number | undefined => {",
23
+ ' const parsed = value === undefined || value === "" ? NaN : Number(value);',
24
+ " return Number.isFinite(parsed) ? parsed : undefined;",
25
+ "};",
26
+ "const bool = (value: string | undefined): boolean | undefined =>",
27
+ ' value === undefined ? undefined : value === "1" || value.toLowerCase() === "true";',
28
+ "",
29
+ "export default serve({",
30
+ " config,",
31
+ " maps,",
32
+ " storage: (env) =>",
33
+ " createStorage({ driver: cloudflareKvDriver({ binding: env.CAJONZERO_KV }) }),",
34
+ " sessionSecret: (env) => env.CAJONZERO_SESSION_SECRET,",
35
+ " adminToken: (env) => env.BOX_PLAY_ADMIN_TOKEN,",
36
+ " environment: (env) => env.CAJONZERO_ENV,",
37
+ " rooms: (env) => env.ROOMS,",
38
+ " settings: (env) => {",
39
+ " const e = vars(env);",
40
+ " return {",
41
+ " playerLimit: num(e.CAJONZERO_PLAYER_LIMIT),",
42
+ " zeroPlayerTimeout: num(e.CAJONZERO_ZERO_PLAYER_TIMEOUT_MS),",
43
+ " autoSaveInterval: num(e.CAJONZERO_AUTOSAVE_INTERVAL_MS),",
44
+ " debug: bool(e.CAJONZERO_DEBUG),",
45
+ " teleportAccess: bool(e.CAJONZERO_TELEPORT_ACCESS),",
46
+ " paidAccess: bool(e.CAJONZERO_PAID_ACCESS),",
47
+ " scriptHeapSizeMB: num(e.CAJONZERO_SCRIPT_HEAP_MB),",
48
+ " scriptTimeout: num(e.CAJONZERO_SCRIPT_TIMEOUT_MS),",
49
+ " dbSizeLimit: num(e.CAJONZERO_DB_SIZE_LIMIT),",
50
+ " env: e.CAJONZERO_ENV,",
51
+ " };",
52
+ " },",
53
+ "});",
17
54
  'export { Room } from "cajonzero";',
18
55
  ""
19
56
  ].join("\n");
@@ -23,7 +60,6 @@ var SCAFFOLD_CONFIG_CONTENT = `${JSON.stringify(
23
60
  admin: { path: "/admin" },
24
61
  room: { tickRate: 64, emptyGraceMs: 48e4 },
25
62
  maps: {
26
- cacheTtlSeconds: 86400,
27
63
  maxDownloadBytes: 33554432,
28
64
  concurrency: 8,
29
65
  requestTimeoutMs: 15e3
@@ -88,7 +124,7 @@ function wranglerConfig(options) {
88
124
  " },",
89
125
  ' "kv_namespaces": [',
90
126
  " {",
91
- " // Player/account data + map-script KV + resolved map-seed cache.",
127
+ " // Player/account data + map-script KV.",
92
128
  ' "binding": "CAJONZERO_KV",',
93
129
  " // Replace with `wrangler kv namespace create CAJONZERO_KV` output.",
94
130
  ` "id": ${JSON.stringify(SCAFFOLD_KV_PLACEHOLDER_ID)}`,
@@ -132,7 +168,13 @@ function readme(options) {
132
168
  'import maps from "../cajonzero/maps.json";',
133
169
  'import { serve } from "cajonzero/serve";',
134
170
  "",
135
- "export default serve({ config, maps });",
171
+ "export default serve({",
172
+ " config,",
173
+ " maps,",
174
+ " storage: (env) => /* any unstorage driver over env bindings */,",
175
+ " sessionSecret: (env) => env.CAJONZERO_SESSION_SECRET,",
176
+ " // adminToken / environment / rooms / settings \u2026",
177
+ "});",
136
178
  'export { Room } from "cajonzero";',
137
179
  "```",
138
180
  "",
@@ -146,13 +188,34 @@ function readme(options) {
146
188
  "`npm run build` validates the deploy by bundling the worker without",
147
189
  "publishing (`wrangler deploy --dry-run`).",
148
190
  "",
149
- "## Required secrets",
191
+ "## Environment",
192
+ "",
193
+ "Every value the runtime consumes is passed explicitly in `src/index.ts`;",
194
+ "the package never guesses env var names.",
195
+ "",
196
+ "| Name | Kind | Required | Purpose |",
197
+ "|---|---|---|---|",
198
+ "| `CAJONZERO_KV` | KV binding | yes | player/account data + map-script KV |",
199
+ "| `ROOMS` | Durable Object binding | yes | room actors |",
200
+ "| `CAJONZERO_SESSION_SECRET` | secret | yes | signs session proofs and login cookies |",
201
+ "| `BOX_PLAY_ADMIN_TOKEN` | secret | yes | owner console and `/api/admin/*` |",
202
+ "| `CAJONZERO_ENV` | var | no | deployment label (`development` relaxes cookie `Secure`) |",
203
+ "| `CAJONZERO_PLAYER_LIMIT` | var | no | maximum players per room |",
204
+ "| `CAJONZERO_ZERO_PLAYER_TIMEOUT_MS` | var | no | empty-room grace window |",
205
+ "| `CAJONZERO_AUTOSAVE_INTERVAL_MS` | var | no | snapshot cadence |",
206
+ "| `CAJONZERO_DEBUG` | var | no | verbose logging |",
207
+ "| `CAJONZERO_TELEPORT_ACCESS` | var | no | allow non-team containers |",
208
+ "| `CAJONZERO_PAID_ACCESS` | var | no | restrict joins to paid containers |",
209
+ "| `CAJONZERO_SCRIPT_HEAP_MB` | var | no | QuickJS heap per script host |",
210
+ "| `CAJONZERO_SCRIPT_TIMEOUT_MS` | var | no | script invocation timeout |",
211
+ "| `CAJONZERO_DB_SIZE_LIMIT` | var | no | map-script storage budget |",
150
212
  "",
151
- "Set both secrets with Wrangler (never commit them):",
213
+ "Create the namespace and set the secrets before deploying:",
152
214
  "",
153
215
  "```bash",
154
- "npx wrangler secret put CAJONZERO_SESSION_SECRET # signs player session proofs",
155
- "npx wrangler secret put BOX_PLAY_ADMIN_TOKEN # owner token for the admin API",
216
+ "npx wrangler kv namespace create CAJONZERO_KV # paste the id into wrangler.jsonc",
217
+ "npx wrangler secret put CAJONZERO_SESSION_SECRET",
218
+ "npx wrangler secret put BOX_PLAY_ADMIN_TOKEN",
156
219
  "```",
157
220
  "",
158
221
  "## Maps (object storage)",
@@ -179,18 +242,16 @@ function readme(options) {
179
242
  ' "root": "https://cdn.example.com/cajonzero/omega/",',
180
243
  ' "worldId": "project-omega",',
181
244
  ' "name": "Omega City Reloaded",',
182
- ' "version": "1.0.4",',
183
- ' "etag": "v1.0.4"',
245
+ ' "version": "1.0.4"',
184
246
  " }",
185
247
  " ]",
186
248
  "}",
187
249
  "```",
188
250
  "",
189
251
  "- `root` must be the unpacked map directory and end with `/`.",
190
- "- `etag` (or `version`) is the seed-cache revision: bump it whenever you",
191
- " re-upload the map, otherwise the cached world stays stale.",
192
- "- The bucket may be public-read; the server fetches it, so no CORS is needed",
193
- " for gameplay. Enable CORS only if the browser loads assets from it.",
252
+ "- The browser loads the map files directly, so the bucket must allow CORS",
253
+ " (`Access-Control-Allow-Origin`) for this deployment's origin.",
254
+ "- Re-uploaded maps need a CDN cache refresh (or a versioned path).",
194
255
  "",
195
256
  "## Where things live",
196
257
  "",
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/scaffold/templates.ts","../../src/scaffold/create.ts","../../src/bin/cj0.ts"],"names":["path","mkdirSync"],"mappings":"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","file":"cj0.js","sourcesContent":["/**\r\n * cajonzero `create` templates — the single source of truth for the files a\r\n * created project contains.\r\n *\r\n * ## Source-free by design\r\n *\r\n * A project emitted by `cj0 create` ships **no engine source code**: it is\r\n * configuration plus a short worker entry that re-exports the cajonzero runtime.\r\n *\r\n * ## Where configuration lives\r\n *\r\n * - `cajonzero/config.json` — cajonzero runtime config (room policy, map fetch policy).\r\n * - `cajonzero/maps.json` — the map catalog; entries point at unpacked map roots\r\n * hosted on OSS/CDN. Maps are never stored in the project.\r\n * - `wrangler.jsonc` — Cloudflare bindings (KV, Durable Object, assets).\r\n *\r\n * `buildScaffoldFiles()` is imported by BOTH the scaffolder (`createProject`)\r\n * and the scaffold test, so the emitted bytes and the test assertions can never\r\n * drift apart.\r\n *\r\n * This module is Node-only (the CLI/tooling path). It is never imported from\r\n * the Cloudflare worker entry (`src/serve.ts` / `src/index.ts`).\r\n */\r\n\r\n/** One generated file: a project-relative POSIX path plus its exact bytes. */\r\nexport interface ScaffoldFile {\r\n /** POSIX-style path relative to the created project root. */\r\n readonly path: string;\r\n /** Exact file contents (deterministic; written verbatim). */\r\n readonly content: string;\r\n}\r\n\r\n/** Inputs used to render the templates. */\r\nexport interface ScaffoldTemplateOptions {\r\n /** npm package name for the created project. */\r\n readonly packageName: string;\r\n /** `cajonzero` dependency range (e.g. `^0.0.0`), read from cajonzero itself. */\r\n readonly cajonzeroRange: string;\r\n /**\r\n * `unstorage` range, declared directly in the created project so it can\r\n * import any driver (`unstorage/drivers/redis`, `.../s3`, ...) without\r\n * relying on cajonzero's own copy.\r\n */\r\n readonly unstorageRange: string;\r\n /** `wrangler` devDependency range (e.g. `^4.151.1`), read from cajonzero. */\r\n readonly wranglerRange: string;\r\n /** `compatibility_date` for the emitted `wrangler.jsonc`. */\r\n readonly compatibilityDate: string;\r\n}\r\n\r\n/** Relative path of the worker entry inside a created project. */\r\nexport const SCAFFOLD_ENTRY_PATH = \"src/index.ts\";\r\n\r\n/**\r\n * `compatibility_date` pinned to the value T18 verified boots. Kept identical\r\n * to cajonzero's own `wrangler.jsonc` so the created project boots the same\r\n * workerd runtime (and deliberately carries NO `nodejs_compat`).\r\n */\r\nexport const SCAFFOLD_COMPATIBILITY_DATE = \"2026-09-01\";\r\n\r\n/** Placeholder Cloudflare KV namespace id (replaced by the deployer). */\r\nexport const SCAFFOLD_KV_PLACEHOLDER_ID = \"PLACEHOLDER_KV_ID\";\r\n\r\n/**\r\n * The entire created project's source code: import the two JSON documents, hand\r\n * them to `serve()`, and expose the Durable Object class.\r\n */\r\nexport const SCAFFOLD_ENTRY_CONTENT = [\r\n 'import config from \"../cajonzero/config.json\";',\r\n 'import maps from \"../cajonzero/maps.json\";',\r\n \"\",\r\n 'import { serve } from \"cajonzero/serve\";',\r\n \"\",\r\n \"export default serve({ config, maps });\",\r\n 'export { Room } from \"cajonzero\";',\r\n \"\",\r\n].join(\"\\n\");\r\n\r\n/** `cajonzero/config.json` — runtime policy; secrets stay in `wrangler secret`. */\r\nexport const SCAFFOLD_CONFIG_CONTENT = `${JSON.stringify(\r\n {\r\n name: \"<project-name>\",\r\n admin: { path: \"/admin\" },\r\n room: { tickRate: 64, emptyGraceMs: 480_000 },\r\n maps: {\r\n cacheTtlSeconds: 86_400,\r\n maxDownloadBytes: 33_554_432,\r\n concurrency: 8,\r\n requestTimeoutMs: 15_000,\r\n },\r\n },\r\n null,\r\n 2,\r\n)}\\n`;\r\n\r\n/** `cajonzero/maps.json` — empty by default; see the README for an entry. */\r\nexport const SCAFFOLD_MAPS_CONTENT = `${JSON.stringify({ version: 1, maps: [] }, null, 2)}\\n`;\r\n\r\n/**\r\n * Sync cajonzero's prebuilt lobby/console into the project's `dist/ui`.\r\n *\r\n * A created project carries no UI source: the assets belong to the installed\r\n * `cajonzero` package. Copying them before every wrangler command keeps `dev`,\r\n * `build` and `deploy` working even when the install lifecycle was skipped\r\n * (e.g. `npm install <tarball>` does not run `prepare`).\r\n */\r\nconst SHIP_UI_SCRIPT =\r\n 'node -e \"const fs=require(\\'node:fs\\'),p=require(\\'node:path\\');' +\r\n \"fs.cpSync(p.join(p.dirname(require.resolve('cajonzero/package.json')),'dist','ui'),\" +\r\n \"p.join('dist','ui'),{recursive:true,force:true})\\\"\";\r\n\r\nfunction packageJson(options: ScaffoldTemplateOptions): string {\r\n const manifest = {\r\n name: options.packageName,\r\n version: \"0.0.0\",\r\n private: true,\r\n type: \"module\",\r\n scripts: {\r\n dev: `${SHIP_UI_SCRIPT} && wrangler dev`,\r\n build: `${SHIP_UI_SCRIPT} && wrangler deploy --dry-run`,\r\n deploy: `${SHIP_UI_SCRIPT} && wrangler deploy`,\r\n // `prepare` also runs after `npm install`, so a plain install leaves a\r\n // ready-to-deploy project.\r\n prepare: SHIP_UI_SCRIPT,\r\n },\r\n dependencies: {\r\n cajonzero: options.cajonzeroRange,\r\n unstorage: options.unstorageRange,\r\n },\r\n devDependencies: {\r\n wrangler: options.wranglerRange,\r\n },\r\n };\r\n return `${JSON.stringify(manifest, null, 2)}\\n`;\r\n}\r\n\r\nfunction wranglerConfig(options: ScaffoldTemplateOptions): string {\r\n return [\r\n \"{\",\r\n \" // cajonzero-created project — Cloudflare Workers config.\",\r\n \" // `main` is the thin worker entry; wrangler bundles it (and cajonzero) on\",\r\n \" // the fly, so there is no separate worker build step.\",\r\n ' \"$schema\": \"./node_modules/wrangler/config-schema.json\",',\r\n ` \"name\": ${JSON.stringify(options.packageName)},`,\r\n ` \"main\": ${JSON.stringify(SCAFFOLD_ENTRY_PATH)},`,\r\n ` \"compatibility_date\": ${JSON.stringify(options.compatibilityDate)},`,\r\n \" // Deliberately NO `nodejs_compat`: cajonzero's worker path is Node-free.\",\r\n ' \"compatibility_flags\": [],',\r\n \" // Static UI (play lobby + owner admin console) served by the worker.\",\r\n \" // cajonzero ships the built assets into this directory.\",\r\n ' \"assets\": {',\r\n ' \"directory\": \"dist/ui\",',\r\n \" // History routing: deep links (/play/x, /admin/bans) boot index.html.\",\r\n ' \"not_found_handling\": \"single-page-application\",',\r\n ' \"run_worker_first\": [\"/api/*\", \"/runtime/*\"]',\r\n \" },\",\r\n \" // Non-secret deployment label. Secrets are set with `wrangler secret put`:\",\r\n \" // CAJONZERO_SESSION_SECRET, BOX_PLAY_ADMIN_TOKEN\",\r\n ' \"vars\": {',\r\n ' \"CAJONZERO_ENV\": \"development\"',\r\n \" },\",\r\n ' \"kv_namespaces\": [',\r\n \" {\",\r\n \" // Player/account data + map-script KV + resolved map-seed cache.\",\r\n ' \"binding\": \"CAJONZERO_KV\",',\r\n \" // Replace with `wrangler kv namespace create CAJONZERO_KV` output.\",\r\n ` \"id\": ${JSON.stringify(SCAFFOLD_KV_PLACEHOLDER_ID)}`,\r\n \" }\",\r\n \" ],\",\r\n ' \"durable_objects\": {',\r\n ' \"bindings\": [',\r\n \" {\",\r\n \" // Per-room actor namespace. `Room` is re-exported by the entry.\",\r\n ' \"name\": \"ROOMS\",',\r\n ' \"class_name\": \"Room\"',\r\n \" }\",\r\n \" ]\",\r\n \" },\",\r\n ' \"migrations\": [',\r\n \" {\",\r\n ' \"tag\": \"v1\",',\r\n ' \"new_sqlite_classes\": [\"Room\"]',\r\n \" }\",\r\n \" ]\",\r\n \"}\",\r\n \"\",\r\n ].join(\"\\n\");\r\n}\r\n\r\nfunction gitignore(): string {\r\n return [\"node_modules\", \"dist\", \".wrangler\", \".dev.vars\", \"\"].join(\"\\n\");\r\n}\r\n\r\nfunction readme(options: ScaffoldTemplateOptions): string {\r\n return [\r\n `# ${options.packageName}`,\r\n \"\",\r\n \"A serverless (Cloudflare Workers) BOX3 **play** server, scaffolded by\",\r\n \"[`cajonzero`](https://www.npmjs.com/package/cajonzero).\",\r\n \"\",\r\n \"This project contains **no engine source code** — configuration plus a short\",\r\n \"worker entry that hands `cajonzero/config.json` and `cajonzero/maps.json` to the\",\r\n \"runtime:\",\r\n \"\",\r\n \"```ts\",\r\n 'import config from \"../cajonzero/config.json\";',\r\n 'import maps from \"../cajonzero/maps.json\";',\r\n 'import { serve } from \"cajonzero/serve\";',\r\n \"\",\r\n \"export default serve({ config, maps });\",\r\n 'export { Room } from \"cajonzero\";',\r\n \"```\",\r\n \"\",\r\n \"## Quick start\",\r\n \"\",\r\n \"```bash\",\r\n \"npm install # also syncs cajonzero's prebuilt lobby/console into dist/ui\",\r\n \"npm run dev # wrangler dev → http://localhost:8787\",\r\n \"```\",\r\n \"\",\r\n \"`npm run build` validates the deploy by bundling the worker without\",\r\n \"publishing (`wrangler deploy --dry-run`).\",\r\n \"\",\r\n \"## Required secrets\",\r\n \"\",\r\n \"Set both secrets with Wrangler (never commit them):\",\r\n \"\",\r\n \"```bash\",\r\n \"npx wrangler secret put CAJONZERO_SESSION_SECRET # signs player session proofs\",\r\n \"npx wrangler secret put BOX_PLAY_ADMIN_TOKEN # owner token for the admin API\",\r\n \"```\",\r\n \"\",\r\n \"## Maps (object storage)\",\r\n \"\",\r\n \"Maps are **not** uploaded with the worker and are never unpacked at runtime.\",\r\n \"Unpack the `.boxplay` yourself and upload the resulting directory to OSS/S3/CDN\",\r\n \"with the archive layout preserved:\",\r\n \"\",\r\n \"```text\",\r\n \"<root>/bundle.json\",\r\n \"<root>/worlds/<worldId>/manifest.json\",\r\n \"<root>/worlds/<worldId>/data/project-state.json\",\r\n \"<root>/worlds/<worldId>/data/blobs/<cid>\",\r\n \"```\",\r\n \"\",\r\n \"Then add an entry to `cajonzero/maps.json`:\",\r\n \"\",\r\n \"```json\",\r\n \"{\",\r\n ' \"version\": 1,',\r\n ' \"maps\": [',\r\n \" {\",\r\n ' \"slug\": \"omega\",',\r\n ' \"root\": \"https://cdn.example.com/cajonzero/omega/\",',\r\n ' \"worldId\": \"project-omega\",',\r\n ' \"name\": \"Omega City Reloaded\",',\r\n ' \"version\": \"1.0.4\",',\r\n ' \"etag\": \"v1.0.4\"',\r\n \" }\",\r\n \" ]\",\r\n \"}\",\r\n \"```\",\r\n \"\",\r\n \"- `root` must be the unpacked map directory and end with `/`.\",\r\n \"- `etag` (or `version`) is the seed-cache revision: bump it whenever you\",\r\n \" re-upload the map, otherwise the cached world stays stale.\",\r\n \"- The bucket may be public-read; the server fetches it, so no CORS is needed\",\r\n \" for gameplay. Enable CORS only if the browser loads assets from it.\",\r\n \"\",\r\n \"## Where things live\",\r\n \"\",\r\n \"- **Play lobby** — the worker serves the lobby at `/`.\",\r\n \"- **Admin console** — at `/admin` (sign in with `BOX_PLAY_ADMIN_TOKEN`) for\",\r\n \" player bans and runtime settings; its JSON API is under `/api/admin/*`.\",\r\n \"- **Map catalog** — `GET /api/maps` returns the entries from `maps.json`.\",\r\n \"\",\r\n \"## Deploy\",\r\n \"\",\r\n \"```bash\",\r\n \"npm run deploy # wrangler deploy\",\r\n \"```\",\r\n \"\",\r\n ].join(\"\\n\");\r\n}\r\n\r\n/**\r\n * Render the complete, deterministically ordered set of files for a created\r\n * project. The first entry is `package.json`; every file is emitted exactly\r\n * once with a trailing newline.\r\n */\r\nexport function buildScaffoldFiles(options: ScaffoldTemplateOptions): ScaffoldFile[] {\r\n return [\r\n { path: \"package.json\", content: packageJson(options) },\r\n { path: \"wrangler.jsonc\", content: wranglerConfig(options) },\r\n { path: SCAFFOLD_ENTRY_PATH, content: SCAFFOLD_ENTRY_CONTENT },\r\n {\r\n path: \"cajonzero/config.json\",\r\n content: SCAFFOLD_CONFIG_CONTENT.replace(\"<project-name>\", options.packageName),\r\n },\r\n { path: \"cajonzero/maps.json\", content: SCAFFOLD_MAPS_CONTENT },\r\n { path: \".gitignore\", content: gitignore() },\r\n { path: \"README.md\", content: readme(options) },\r\n ];\r\n}\r\n","/**\r\n * cajonzero `create` scaffolder (T17).\r\n *\r\n * Writes a complete, runnable Cloudflare Workers project that contains **no\r\n * source code of its own** — configuration plus a two-line worker entry that\r\n * re-exports the cajonzero runtime (see `templates.ts` for the exact bytes).\r\n *\r\n * This module is the \"functions exposed in a ts file for other modules\" that\r\n * AGENTS.md asks for: the CLI calls {@link createProject}, and deployment\r\n * tooling can import the same functions directly. It is Node-only (filesystem\r\n * writes) and is never imported from the Cloudflare worker entry.\r\n *\r\n * Scaffolding is deterministic (stable file order + formatting) and refuses to\r\n * clobber: an existing non-empty target directory fails with\r\n * {@link ScaffoldError} `TARGET_NOT_EMPTY` instead of overwriting files.\r\n */\r\n\r\nimport { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from \"node:fs\";\r\nimport path from \"node:path\";\r\nimport { fileURLToPath } from \"node:url\";\r\n\r\nimport {\r\n buildScaffoldFiles,\r\n SCAFFOLD_COMPATIBILITY_DATE,\r\n type ScaffoldFile,\r\n} from \"./templates\";\r\n\r\n/** Caret prefix used for the `cajonzero` dependency range. */\r\nconst CAJONZERO_RANGE_PREFIX = \"^\";\r\n\r\n/** Fallback wrangler range, mirroring cajonzero's pinned devDependency. */\r\nconst FALLBACK_WRANGLER_RANGE = \"^4.151.1\";\r\n\r\n/** Fallback unstorage range, mirroring cajonzero's own dependency. */\r\nconst FALLBACK_UNSTORAGE_RANGE = \"^1.17.6\";\r\n\r\n/**\r\n * Absolute path to cajonzero's own `package.json`.\r\n *\r\n * This module lives two levels below the package root in BOTH layouts — the\r\n * source tree (`src/scaffold/`) and the bundled CLI (`dist/bin/`) — so\r\n * `../../package.json` resolves to the real manifest in dev and in production.\r\n */\r\nconst PACKAGE_JSON_URL = new URL(\"../../package.json\", import.meta.url);\r\n\r\n/** Stable error codes so callers/tests never string-match messages. */\r\nexport type ScaffoldErrorCode = \"INVALID_TARGET\" | \"TARGET_NOT_EMPTY\";\r\n\r\n/** Typed failure thrown by {@link createProject}. */\r\nexport class ScaffoldError extends Error {\r\n readonly code: ScaffoldErrorCode;\r\n\r\n constructor(code: ScaffoldErrorCode, message: string) {\r\n super(message);\r\n this.name = \"ScaffoldError\";\r\n this.code = code;\r\n }\r\n}\r\n\r\n/** The subset of cajonzero's manifest the scaffolder needs. */\r\nexport interface CajonzeroPackageInfo {\r\n /** cajonzero's own `version`. */\r\n readonly version: string;\r\n /** cajonzero's `wrangler` devDependency range. */\r\n readonly wranglerRange: string;\r\n /** cajonzero's `unstorage` dependency range (declared by created projects). */\r\n readonly unstorageRange: string;\r\n}\r\n\r\n/** Options accepted by {@link createProject}. */\r\nexport interface CreateProjectOptions {\r\n /** Base directory the target `folder` is resolved against (default: cwd). */\r\n readonly cwd?: string;\r\n /** Override for cajonzero's version (deterministic tests). */\r\n readonly version?: string;\r\n /** Override for the wrangler devDependency range. */\r\n readonly wranglerRange?: string;\r\n /** Override for the emitted `unstorage` dependency range. */\r\n readonly unstorageRange?: string;\r\n /** Override for `compatibility_date` (defaults to the pinned value). */\r\n readonly compatibilityDate?: string;\r\n}\r\n\r\n/** Result of a successful scaffold. */\r\nexport interface ScaffoldResult {\r\n /** Absolute path of the created project root. */\r\n readonly target: string;\r\n /** Written files, as project-relative POSIX paths, in write order. */\r\n readonly files: string[];\r\n}\r\n\r\n/**\r\n * Read cajonzero's own `package.json` to source the dependency version/range and\r\n * the wrangler devDependency, so a created project tracks the installed\r\n * cajonzero automatically.\r\n *\r\n * @param manifestPath Optional explicit path (tests). Defaults to the package\r\n * root resolved relative to this module.\r\n */\r\nexport function readCajonzeroPackage(manifestPath?: string): CajonzeroPackageInfo {\r\n const filePath = manifestPath ?? fileURLToPath(PACKAGE_JSON_URL);\r\n const raw = readFileSync(filePath, \"utf8\");\r\n const parsed = JSON.parse(raw) as {\r\n version?: unknown;\r\n dependencies?: Record<string, unknown>;\r\n devDependencies?: Record<string, unknown>;\r\n };\r\n const version =\r\n typeof parsed.version === \"string\" && parsed.version.length > 0 ? parsed.version : \"0.0.0\";\r\n const wrangler = parsed.devDependencies?.[\"wrangler\"];\r\n const wranglerRange =\r\n typeof wrangler === \"string\" && wrangler.length > 0 ? wrangler : FALLBACK_WRANGLER_RANGE;\r\n const unstorage = parsed.dependencies?.[\"unstorage\"];\r\n const unstorageRange =\r\n typeof unstorage === \"string\" && unstorage.length > 0\r\n ? unstorage\r\n : FALLBACK_UNSTORAGE_RANGE;\r\n return { version, wranglerRange, unstorageRange };\r\n}\r\n\r\n/** Derive a valid npm package name from the target directory's basename. */\r\nexport function toPackageName(folder: string): string {\r\n const base = path.basename(folder);\r\n const slug = base\r\n .toLowerCase()\r\n .replace(/[^a-z0-9._-]+/g, \"-\")\r\n .replace(/^[-._]+/, \"\")\r\n .replace(/[-._]+$/, \"\");\r\n return slug.length > 0 ? slug : \"cajonzero-app\";\r\n}\r\n\r\n/** True when `dir` exists and is a directory with at least one entry. */\r\nfunction isNonEmptyDirectory(dir: string): boolean {\r\n if (!existsSync(dir)) return false;\r\n if (!statSync(dir).isDirectory()) return false;\r\n return readdirSync(dir).length > 0;\r\n}\r\n\r\n/**\r\n * Scaffold a source-free, deployable project into `folder`.\r\n *\r\n * The target is resolved relative to `options.cwd` (default `process.cwd()`).\r\n * A missing target (or an existing **empty** directory) is filled; an existing\r\n * non-empty directory, or a target that exists as a file, is rejected.\r\n *\r\n * @throws {ScaffoldError} `INVALID_TARGET` (missing folder / not a directory)\r\n * or `TARGET_NOT_EMPTY`.\r\n */\r\nexport function createProject(folder: string, options: CreateProjectOptions = {}): ScaffoldResult {\r\n const name = typeof folder === \"string\" ? folder.trim() : \"\";\r\n if (name.length === 0) {\r\n throw new ScaffoldError(\"INVALID_TARGET\", \"create requires a target folder\");\r\n }\r\n\r\n const cwd = options.cwd ?? process.cwd();\r\n const target = path.resolve(cwd, name);\r\n\r\n if (existsSync(target) && !statSync(target).isDirectory()) {\r\n throw new ScaffoldError(\"INVALID_TARGET\", `target exists and is not a directory: ${target}`);\r\n }\r\n if (isNonEmptyDirectory(target)) {\r\n throw new ScaffoldError(\r\n \"TARGET_NOT_EMPTY\",\r\n `target directory already exists and is not empty: ${target} (refusing to overwrite)`,\r\n );\r\n }\r\n\r\n const manifest = options.version !== undefined ? undefined : readCajonzeroPackage();\r\n const version = options.version ?? manifest?.version ?? \"0.0.0\";\r\n const wranglerRange = options.wranglerRange ?? manifest?.wranglerRange ?? FALLBACK_WRANGLER_RANGE;\r\n const unstorageRange =\r\n options.unstorageRange ?? manifest?.unstorageRange ?? FALLBACK_UNSTORAGE_RANGE;\r\n\r\n const files: ScaffoldFile[] = buildScaffoldFiles({\r\n packageName: toPackageName(target),\r\n cajonzeroRange: `${CAJONZERO_RANGE_PREFIX}${version}`,\r\n unstorageRange,\r\n wranglerRange,\r\n compatibilityDate: options.compatibilityDate ?? SCAFFOLD_COMPATIBILITY_DATE,\r\n });\r\n\r\n mkdirSync(target, { recursive: true });\r\n for (const file of files) {\r\n const fullPath = path.join(target, file.path);\r\n mkdirSync(path.dirname(fullPath), { recursive: true });\r\n writeFileSync(fullPath, file.content, \"utf8\");\r\n }\r\n\r\n return { target, files: files.map((file) => file.path) };\r\n}\r\n","#!/usr/bin/env node\n/**\n * cajonzero CLI (`bin/cj0`).\n *\n * Commands:\n * create <folder> Scaffold a deployable project\n * serve [--port <port>] [--host <host>] Run the cajonzero server locally\n *\n * Maps are not managed by the CLI: unpack the `.boxplay` yourself, upload the\n * unpacked directory to object storage, and point `cajonzero/maps.json` at it.\n *\n * `serve` boots a real workerd runtime through wrangler's programmatic dev\n * server (`unstable_dev` → Miniflare). It is **not** a Node HTTP server: the\n * production/serve path is always Cloudflare Workers. This is the only place\n * Node APIs are permitted (see `scripts/lint-guards.mjs`).\n *\n * Exit codes: 0 on success, 1 on any failure (unknown command, bad options) with\n * a clear message on stderr.\n */\nimport { mkdirSync } from \"node:fs\";\nimport path from \"node:path\";\n\nimport { createProject, ScaffoldError } from \"../scaffold\";\n\nconst USAGE = `cj0 — serverless (Cloudflare) play-only BOX3 engine\n\nUsage:\n cj0 <command> [options]\n\nCommands:\n create <folder> Scaffold a deployable project\n serve [--port <port>] [--host <host>] Run the cajonzero server locally (workerd)\n\nServe options:\n --port <port> Listener port (default: 8787)\n --host <host> Listener host (default: 127.0.0.1)\n --config <file> Wrangler config (default: wrangler.jsonc)\n -h, --help Show this help\n\nMaps: unpack the .boxplay, upload the directory to OSS/CDN, and list its root\nin cajonzero/maps.json. The CLI does not import map archives.\n`;\n\nconst DEFAULT_SERVE_PORT = 8787;\nconst DEFAULT_SERVE_HOST = \"127.0.0.1\";\nconst DEFAULT_CONFIG = \"wrangler.jsonc\";\n\n/**\n * Secret bindings accepted from the process environment and injected as\n * plain-text vars for local dev. Secrets never live in `wrangler.jsonc`;\n * supplying them here lets `cj0 serve` run a fully functional local room\n * without `wrangler secret put`.\n */\nconst LOCAL_DEV_SECRETS = [\"CAJONZERO_SESSION_SECRET\", \"BOX_PLAY_ADMIN_TOKEN\"] as const;\n\n/** Collect any {@link LOCAL_DEV_SECRETS} present in the environment. */\nfunction localDevVars(): Record<string, string> | undefined {\n const vars: Record<string, string> = {};\n for (const name of LOCAL_DEV_SECRETS) {\n const value = process.env[name];\n if (value !== undefined && value.length > 0) {\n vars[name] = value;\n }\n }\n return Object.keys(vars).length > 0 ? vars : undefined;\n}\n\nfunction printUsage(): void {\n process.stdout.write(USAGE);\n}\n\nfunction fail(message: string): void {\n process.stderr.write(`cajonzero: ${message}\\n`);\n process.exitCode = 1;\n}\n\nfunction errorLabel(error: unknown): string {\n if (error instanceof ScaffoldError) {\n return `${error.code}: ${error.message}`;\n }\n return error instanceof Error ? error.message : String(error);\n}\n\nfunction runCreate(args: string[]): void {\n const [folder] = args;\n if (folder === undefined) {\n fail(\"create requires: cj0 create <folder>\");\n return;\n }\n const result = createProject(folder);\n process.stdout.write(\n `created project in ${result.target} (${result.files.length} files)\\n ` +\n `${result.files.join(\", \")}\\n next: cd ${folder} && npm install && npm run dev\\n`,\n );\n}\n\n/** Parsed `cj0 serve` flags. */\ninterface ServeFlags {\n readonly port: number;\n readonly host: string;\n readonly config: string;\n}\n\nfunction readFlagValue(args: string[], index: number, flag: string): string {\n const inline = args[index];\n if (inline !== undefined && inline.startsWith(`${flag}=`)) {\n return inline.slice(flag.length + 1);\n }\n const value = args[index + 1];\n if (value === undefined || value.startsWith(\"-\")) {\n throw new Error(`${flag} requires a value`);\n }\n return value;\n}\n\nfunction parseServeFlags(args: string[]): ServeFlags {\n let port = DEFAULT_SERVE_PORT;\n let host = DEFAULT_SERVE_HOST;\n let config = DEFAULT_CONFIG;\n\n for (let i = 0; i < args.length; i += 1) {\n const arg = args[i];\n if (arg === undefined) continue;\n if (arg === \"--port\" || arg.startsWith(\"--port=\")) {\n const raw = readFlagValue(args, i, \"--port\");\n const parsed = Number(raw);\n if (!Number.isInteger(parsed) || parsed < 0 || parsed > 65535) {\n throw new Error(`--port must be an integer in [0, 65535] (got \"${raw}\")`);\n }\n port = parsed;\n if (!arg.includes(\"=\")) i += 1;\n } else if (arg === \"--host\" || arg.startsWith(\"--host=\")) {\n host = readFlagValue(args, i, \"--host\");\n if (!arg.includes(\"=\")) i += 1;\n } else if (arg === \"--config\" || arg.startsWith(\"--config=\")) {\n config = readFlagValue(args, i, \"--config\");\n if (!arg.includes(\"=\")) i += 1;\n } else {\n throw new Error(`unexpected serve option \"${arg}\"`);\n }\n }\n\n return { port, host, config };\n}\n\n/**\n * Boot a real workerd instance through wrangler's programmatic dev server.\n *\n * `unstable_dev` bundles the configured entry (TypeScript + JSON imports\n * included), resolves `wrangler.jsonc` (DO + KV bindings, migrations, compat\n * date, assets) and starts Miniflare/workerd — the same runtime used in\n * production. It never starts a Node HTTP server.\n */\nasync function runServe(args: string[]): Promise<void> {\n const flags = parseServeFlags(args);\n\n // Dynamic import keeps wrangler (a large Node-only dep) out of the other\n // commands and off the worker path entirely.\n const { unstable_dev, unstable_readConfig } = await import(\"wrangler\");\n\n // Resolve the entry + assets from the project's wrangler config so a created\n // project's thin entry is served without any extra wiring.\n const config = unstable_readConfig({ config: flags.config });\n const entry = config.main;\n if (entry === undefined || entry.length === 0) {\n throw new Error(`no worker entry (\"main\") found in ${flags.config}`);\n }\n\n // wrangler refuses to start when `assets.directory` is missing. The UI build\n // emits it; create it eagerly so `serve` works before then. The path is\n // resolved relative to the wrangler config (not the cwd), exactly like\n // wrangler itself.\n const assetsDir = config.assets?.directory;\n if (typeof assetsDir === \"string\" && assetsDir.length > 0) {\n const configDir = path.dirname(path.resolve(process.cwd(), flags.config));\n mkdirSync(path.resolve(configDir, assetsDir), { recursive: true });\n }\n\n const secrets = localDevVars();\n\n const worker = await unstable_dev(entry, {\n config: flags.config,\n ip: flags.host,\n port: flags.port,\n local: true,\n logLevel: \"info\",\n // Inject local-dev secrets (from the environment) on top of the config\n // `vars` so auth/session work locally without `wrangler secret put`.\n ...(secrets !== undefined\n ? { vars: { ...(config.vars ?? {}), ...secrets } as Record<string, string> }\n : {}),\n experimental: {\n disableExperimentalWarning: true,\n disableDevRegistry: true,\n showInteractiveDevSession: false,\n },\n });\n\n process.stdout.write(\n `cj0 serve listening on http://${worker.address}:${worker.port}\\n` +\n ` (workerd via wrangler dev · config ${flags.config} · DO/KV/assets bound)\\n` +\n (secrets !== undefined\n ? ` (local dev secrets injected: ${Object.keys(secrets).join(\", \")})\\n`\n : \"\"),\n );\n\n let stopping = false;\n const shutdown = async (signal: string): Promise<void> => {\n if (stopping) return;\n stopping = true;\n process.stdout.write(`\\ncajonzero: received ${signal}, shutting down\\n`);\n try {\n await worker.stop();\n } finally {\n process.exit(0);\n }\n };\n\n process.on(\"SIGINT\", () => {\n void shutdown(\"SIGINT\");\n });\n process.on(\"SIGTERM\", () => {\n void shutdown(\"SIGTERM\");\n });\n\n await worker.waitUntilExit();\n}\n\nasync function main(argv: string[]): Promise<void> {\n const [command, ...rest] = argv;\n\n if (command === undefined || command === \"--help\" || command === \"-h\" || command === \"help\") {\n printUsage();\n return;\n }\n\n try {\n switch (command) {\n case \"serve\":\n await runServe(rest);\n return;\n case \"create\":\n runCreate(rest);\n return;\n default:\n process.stderr.write(`cajonzero: unknown command \"${command}\"\\n\\n`);\n printUsage();\n process.exitCode = 1;\n }\n } catch (error) {\n fail(errorLabel(error));\n }\n}\n\nmain(process.argv.slice(2)).catch((error: unknown) => {\n fail(errorLabel(error));\n});\n"]}
1
+ {"version":3,"sources":["../../src/scaffold/templates.ts","../../src/scaffold/create.ts","../../src/bin/cj0.ts"],"names":["path","mkdirSync"],"mappings":"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","file":"cj0.js","sourcesContent":["/**\r\n * cajonzero `create` templates — the single source of truth for the files a\r\n * created project contains.\r\n *\r\n * ## Source-free by design\r\n *\r\n * A project emitted by `cj0 create` ships **no engine source code**: it is\r\n * configuration plus a short worker entry that re-exports the cajonzero runtime.\r\n *\r\n * ## Where configuration lives\r\n *\r\n * - `cajonzero/config.json` — cajonzero runtime config (room policy, map fetch policy).\r\n * - `cajonzero/maps.json` — the map catalog; entries point at unpacked map roots\r\n * hosted on OSS/CDN. Maps are never stored in the project.\r\n * - `wrangler.jsonc` — Cloudflare bindings (KV, Durable Object, assets).\r\n *\r\n * `buildScaffoldFiles()` is imported by BOTH the scaffolder (`createProject`)\r\n * and the scaffold test, so the emitted bytes and the test assertions can never\r\n * drift apart.\r\n *\r\n * This module is Node-only (the CLI/tooling path). It is never imported from\r\n * the Cloudflare worker entry (`src/serve.ts` / `src/index.ts`).\r\n */\r\n\r\n/** One generated file: a project-relative POSIX path plus its exact bytes. */\r\nexport interface ScaffoldFile {\r\n /** POSIX-style path relative to the created project root. */\r\n readonly path: string;\r\n /** Exact file contents (deterministic; written verbatim). */\r\n readonly content: string;\r\n}\r\n\r\n/** Inputs used to render the templates. */\r\nexport interface ScaffoldTemplateOptions {\r\n /** npm package name for the created project. */\r\n readonly packageName: string;\r\n /** `cajonzero` dependency range (e.g. `^0.0.0`), read from cajonzero itself. */\r\n readonly cajonzeroRange: string;\r\n /**\r\n * `unstorage` range, declared directly in the created project so it can\r\n * import any driver (`unstorage/drivers/redis`, `.../s3`, ...) without\r\n * relying on cajonzero's own copy.\r\n */\r\n readonly unstorageRange: string;\r\n /** `wrangler` devDependency range (e.g. `^4.151.1`), read from cajonzero. */\r\n readonly wranglerRange: string;\r\n /** `compatibility_date` for the emitted `wrangler.jsonc`. */\r\n readonly compatibilityDate: string;\r\n}\r\n\r\n/** Relative path of the worker entry inside a created project. */\r\nexport const SCAFFOLD_ENTRY_PATH = \"src/index.ts\";\r\n\r\n/**\r\n * `compatibility_date` pinned to the value T18 verified boots. Kept identical\r\n * to cajonzero's own `wrangler.jsonc` so the created project boots the same\r\n * workerd runtime (and deliberately carries NO `nodejs_compat`).\r\n */\r\nexport const SCAFFOLD_COMPATIBILITY_DATE = \"2026-09-01\";\r\n\r\n/** Placeholder Cloudflare KV namespace id (replaced by the deployer). */\r\nexport const SCAFFOLD_KV_PLACEHOLDER_ID = \"PLACEHOLDER_KV_ID\";\r\n\r\n/**\r\n * The entire created project's source code: import the two JSON documents, hand\r\n * them to `serve()`, and expose the Durable Object class.\r\n */\r\nexport const SCAFFOLD_ENTRY_CONTENT = [\r\n 'import config from \"../cajonzero/config.json\";',\r\n 'import maps from \"../cajonzero/maps.json\";',\r\n \"\",\r\n 'import { serve } from \"cajonzero/serve\";',\r\n 'import type { Env } from \"cajonzero/serve\";',\r\n 'import { createStorage } from \"unstorage\";',\r\n 'import cloudflareKvDriver from \"unstorage/drivers/cloudflare-kv-binding\";',\r\n \"\",\r\n \"/** Every env value the runtime needs is wired here — nothing is implicit. */\",\r\n \"const vars = (env: Env): Record<string, string | undefined> =>\",\r\n \" env as unknown as Record<string, string | undefined>;\",\r\n \"const num = (value: string | undefined): number | undefined => {\",\r\n ' const parsed = value === undefined || value === \"\" ? NaN : Number(value);',\r\n \" return Number.isFinite(parsed) ? parsed : undefined;\",\r\n \"};\",\r\n \"const bool = (value: string | undefined): boolean | undefined =>\",\r\n \" value === undefined ? undefined : value === \\\"1\\\" || value.toLowerCase() === \\\"true\\\";\",\r\n \"\",\r\n \"export default serve({\",\r\n \" config,\",\r\n \" maps,\",\r\n \" storage: (env) =>\",\r\n \" createStorage({ driver: cloudflareKvDriver({ binding: env.CAJONZERO_KV }) }),\",\r\n \" sessionSecret: (env) => env.CAJONZERO_SESSION_SECRET,\",\r\n \" adminToken: (env) => env.BOX_PLAY_ADMIN_TOKEN,\",\r\n \" environment: (env) => env.CAJONZERO_ENV,\",\r\n \" rooms: (env) => env.ROOMS,\",\r\n \" settings: (env) => {\",\r\n \" const e = vars(env);\",\r\n \" return {\",\r\n \" playerLimit: num(e.CAJONZERO_PLAYER_LIMIT),\",\r\n \" zeroPlayerTimeout: num(e.CAJONZERO_ZERO_PLAYER_TIMEOUT_MS),\",\r\n \" autoSaveInterval: num(e.CAJONZERO_AUTOSAVE_INTERVAL_MS),\",\r\n \" debug: bool(e.CAJONZERO_DEBUG),\",\r\n \" teleportAccess: bool(e.CAJONZERO_TELEPORT_ACCESS),\",\r\n \" paidAccess: bool(e.CAJONZERO_PAID_ACCESS),\",\r\n \" scriptHeapSizeMB: num(e.CAJONZERO_SCRIPT_HEAP_MB),\",\r\n \" scriptTimeout: num(e.CAJONZERO_SCRIPT_TIMEOUT_MS),\",\r\n \" dbSizeLimit: num(e.CAJONZERO_DB_SIZE_LIMIT),\",\r\n \" env: e.CAJONZERO_ENV,\",\r\n \" };\",\r\n \" },\",\r\n \"});\",\r\n 'export { Room } from \"cajonzero\";',\r\n \"\",\r\n].join(\"\\n\");\r\n\r\n/** `cajonzero/config.json` — runtime policy; secrets stay in `wrangler secret`. */\r\nexport const SCAFFOLD_CONFIG_CONTENT = `${JSON.stringify(\r\n {\r\n name: \"<project-name>\",\r\n admin: { path: \"/admin\" },\r\n room: { tickRate: 64, emptyGraceMs: 480_000 },\r\n maps: {\r\n maxDownloadBytes: 33_554_432,\r\n concurrency: 8,\r\n requestTimeoutMs: 15_000,\r\n },\r\n },\r\n null,\r\n 2,\r\n)}\\n`;\r\n\r\n/** `cajonzero/maps.json` — empty by default; see the README for an entry. */\r\nexport const SCAFFOLD_MAPS_CONTENT = `${JSON.stringify({ version: 1, maps: [] }, null, 2)}\\n`;\r\n\r\n/**\r\n * Sync cajonzero's prebuilt lobby/console into the project's `dist/ui`.\r\n *\r\n * A created project carries no UI source: the assets belong to the installed\r\n * `cajonzero` package. Copying them before every wrangler command keeps `dev`,\r\n * `build` and `deploy` working even when the install lifecycle was skipped\r\n * (e.g. `npm install <tarball>` does not run `prepare`).\r\n */\r\nconst SHIP_UI_SCRIPT =\r\n 'node -e \"const fs=require(\\'node:fs\\'),p=require(\\'node:path\\');' +\r\n \"fs.cpSync(p.join(p.dirname(require.resolve('cajonzero/package.json')),'dist','ui'),\" +\r\n \"p.join('dist','ui'),{recursive:true,force:true})\\\"\";\r\n\r\nfunction packageJson(options: ScaffoldTemplateOptions): string {\r\n const manifest = {\r\n name: options.packageName,\r\n version: \"0.0.0\",\r\n private: true,\r\n type: \"module\",\r\n scripts: {\r\n dev: `${SHIP_UI_SCRIPT} && wrangler dev`,\r\n build: `${SHIP_UI_SCRIPT} && wrangler deploy --dry-run`,\r\n deploy: `${SHIP_UI_SCRIPT} && wrangler deploy`,\r\n // `prepare` also runs after `npm install`, so a plain install leaves a\r\n // ready-to-deploy project.\r\n prepare: SHIP_UI_SCRIPT,\r\n },\r\n dependencies: {\r\n cajonzero: options.cajonzeroRange,\r\n unstorage: options.unstorageRange,\r\n },\r\n devDependencies: {\r\n wrangler: options.wranglerRange,\r\n },\r\n };\r\n return `${JSON.stringify(manifest, null, 2)}\\n`;\r\n}\r\n\r\nfunction wranglerConfig(options: ScaffoldTemplateOptions): string {\r\n return [\r\n \"{\",\r\n \" // cajonzero-created project — Cloudflare Workers config.\",\r\n \" // `main` is the thin worker entry; wrangler bundles it (and cajonzero) on\",\r\n \" // the fly, so there is no separate worker build step.\",\r\n ' \"$schema\": \"./node_modules/wrangler/config-schema.json\",',\r\n ` \"name\": ${JSON.stringify(options.packageName)},`,\r\n ` \"main\": ${JSON.stringify(SCAFFOLD_ENTRY_PATH)},`,\r\n ` \"compatibility_date\": ${JSON.stringify(options.compatibilityDate)},`,\r\n \" // Deliberately NO `nodejs_compat`: cajonzero's worker path is Node-free.\",\r\n ' \"compatibility_flags\": [],',\r\n \" // Static UI (play lobby + owner admin console) served by the worker.\",\r\n \" // cajonzero ships the built assets into this directory.\",\r\n ' \"assets\": {',\r\n ' \"directory\": \"dist/ui\",',\r\n \" // History routing: deep links (/play/x, /admin/bans) boot index.html.\",\r\n ' \"not_found_handling\": \"single-page-application\",',\r\n ' \"run_worker_first\": [\"/api/*\", \"/runtime/*\"]',\r\n \" },\",\r\n \" // Non-secret deployment label. Secrets are set with `wrangler secret put`:\",\r\n \" // CAJONZERO_SESSION_SECRET, BOX_PLAY_ADMIN_TOKEN\",\r\n ' \"vars\": {',\r\n ' \"CAJONZERO_ENV\": \"development\"',\r\n \" },\",\r\n ' \"kv_namespaces\": [',\r\n \" {\",\r\n \" // Player/account data + map-script KV.\",\r\n ' \"binding\": \"CAJONZERO_KV\",',\r\n \" // Replace with `wrangler kv namespace create CAJONZERO_KV` output.\",\r\n ` \"id\": ${JSON.stringify(SCAFFOLD_KV_PLACEHOLDER_ID)}`,\r\n \" }\",\r\n \" ],\",\r\n ' \"durable_objects\": {',\r\n ' \"bindings\": [',\r\n \" {\",\r\n \" // Per-room actor namespace. `Room` is re-exported by the entry.\",\r\n ' \"name\": \"ROOMS\",',\r\n ' \"class_name\": \"Room\"',\r\n \" }\",\r\n \" ]\",\r\n \" },\",\r\n ' \"migrations\": [',\r\n \" {\",\r\n ' \"tag\": \"v1\",',\r\n ' \"new_sqlite_classes\": [\"Room\"]',\r\n \" }\",\r\n \" ]\",\r\n \"}\",\r\n \"\",\r\n ].join(\"\\n\");\r\n}\r\n\r\nfunction gitignore(): string {\r\n return [\"node_modules\", \"dist\", \".wrangler\", \".dev.vars\", \"\"].join(\"\\n\");\r\n}\r\n\r\nfunction readme(options: ScaffoldTemplateOptions): string {\r\n return [\r\n `# ${options.packageName}`,\r\n \"\",\r\n \"A serverless (Cloudflare Workers) BOX3 **play** server, scaffolded by\",\r\n \"[`cajonzero`](https://www.npmjs.com/package/cajonzero).\",\r\n \"\",\r\n \"This project contains **no engine source code** — configuration plus a short\",\r\n \"worker entry that hands `cajonzero/config.json` and `cajonzero/maps.json` to the\",\r\n \"runtime:\",\r\n \"\",\r\n \"```ts\",\r\n 'import config from \"../cajonzero/config.json\";',\r\n 'import maps from \"../cajonzero/maps.json\";',\r\n 'import { serve } from \"cajonzero/serve\";',\r\n \"\",\r\n \"export default serve({\",\r\n \" config,\",\r\n \" maps,\",\r\n \" storage: (env) => /* any unstorage driver over env bindings */,\",\r\n \" sessionSecret: (env) => env.CAJONZERO_SESSION_SECRET,\",\r\n \" // adminToken / environment / rooms / settings …\",\r\n \"});\",\r\n 'export { Room } from \"cajonzero\";',\r\n \"```\",\r\n \"\",\r\n \"## Quick start\",\r\n \"\",\r\n \"```bash\",\r\n \"npm install # also syncs cajonzero's prebuilt lobby/console into dist/ui\",\r\n \"npm run dev # wrangler dev → http://localhost:8787\",\r\n \"```\",\r\n \"\",\r\n \"`npm run build` validates the deploy by bundling the worker without\",\r\n \"publishing (`wrangler deploy --dry-run`).\",\r\n \"\",\r\n \"## Environment\",\r\n \"\",\r\n \"Every value the runtime consumes is passed explicitly in `src/index.ts`;\",\r\n \"the package never guesses env var names.\",\r\n \"\",\r\n \"| Name | Kind | Required | Purpose |\",\r\n \"|---|---|---|---|\",\r\n \"| `CAJONZERO_KV` | KV binding | yes | player/account data + map-script KV |\",\r\n \"| `ROOMS` | Durable Object binding | yes | room actors |\",\r\n \"| `CAJONZERO_SESSION_SECRET` | secret | yes | signs session proofs and login cookies |\",\r\n \"| `BOX_PLAY_ADMIN_TOKEN` | secret | yes | owner console and `/api/admin/*` |\",\r\n \"| `CAJONZERO_ENV` | var | no | deployment label (`development` relaxes cookie `Secure`) |\",\r\n \"| `CAJONZERO_PLAYER_LIMIT` | var | no | maximum players per room |\",\r\n \"| `CAJONZERO_ZERO_PLAYER_TIMEOUT_MS` | var | no | empty-room grace window |\",\r\n \"| `CAJONZERO_AUTOSAVE_INTERVAL_MS` | var | no | snapshot cadence |\",\r\n \"| `CAJONZERO_DEBUG` | var | no | verbose logging |\",\r\n \"| `CAJONZERO_TELEPORT_ACCESS` | var | no | allow non-team containers |\",\r\n \"| `CAJONZERO_PAID_ACCESS` | var | no | restrict joins to paid containers |\",\r\n \"| `CAJONZERO_SCRIPT_HEAP_MB` | var | no | QuickJS heap per script host |\",\r\n \"| `CAJONZERO_SCRIPT_TIMEOUT_MS` | var | no | script invocation timeout |\",\r\n \"| `CAJONZERO_DB_SIZE_LIMIT` | var | no | map-script storage budget |\",\r\n \"\",\r\n \"Create the namespace and set the secrets before deploying:\",\r\n \"\",\r\n \"```bash\",\r\n \"npx wrangler kv namespace create CAJONZERO_KV # paste the id into wrangler.jsonc\",\r\n \"npx wrangler secret put CAJONZERO_SESSION_SECRET\",\r\n \"npx wrangler secret put BOX_PLAY_ADMIN_TOKEN\",\r\n \"```\",\r\n \"\",\r\n \"## Maps (object storage)\",\r\n \"\",\r\n \"Maps are **not** uploaded with the worker and are never unpacked at runtime.\",\r\n \"Unpack the `.boxplay` yourself and upload the resulting directory to OSS/S3/CDN\",\r\n \"with the archive layout preserved:\",\r\n \"\",\r\n \"```text\",\r\n \"<root>/bundle.json\",\r\n \"<root>/worlds/<worldId>/manifest.json\",\r\n \"<root>/worlds/<worldId>/data/project-state.json\",\r\n \"<root>/worlds/<worldId>/data/blobs/<cid>\",\r\n \"```\",\r\n \"\",\r\n \"Then add an entry to `cajonzero/maps.json`:\",\r\n \"\",\r\n \"```json\",\r\n \"{\",\r\n ' \"version\": 1,',\r\n ' \"maps\": [',\r\n \" {\",\r\n ' \"slug\": \"omega\",',\r\n ' \"root\": \"https://cdn.example.com/cajonzero/omega/\",',\r\n ' \"worldId\": \"project-omega\",',\r\n ' \"name\": \"Omega City Reloaded\",',\r\n ' \"version\": \"1.0.4\"',\r\n \" }\",\r\n \" ]\",\r\n \"}\",\r\n \"```\",\r\n \"\",\r\n \"- `root` must be the unpacked map directory and end with `/`.\",\r\n \"- The browser loads the map files directly, so the bucket must allow CORS\",\r\n \" (`Access-Control-Allow-Origin`) for this deployment's origin.\",\r\n \"- Re-uploaded maps need a CDN cache refresh (or a versioned path).\",\r\n \"\",\r\n \"## Where things live\",\r\n \"\",\r\n \"- **Play lobby** — the worker serves the lobby at `/`.\",\r\n \"- **Admin console** — at `/admin` (sign in with `BOX_PLAY_ADMIN_TOKEN`) for\",\r\n \" player bans and runtime settings; its JSON API is under `/api/admin/*`.\",\r\n \"- **Map catalog** — `GET /api/maps` returns the entries from `maps.json`.\",\r\n \"\",\r\n \"## Deploy\",\r\n \"\",\r\n \"```bash\",\r\n \"npm run deploy # wrangler deploy\",\r\n \"```\",\r\n \"\",\r\n ].join(\"\\n\");\r\n}\r\n\r\n/**\r\n * Render the complete, deterministically ordered set of files for a created\r\n * project. The first entry is `package.json`; every file is emitted exactly\r\n * once with a trailing newline.\r\n */\r\nexport function buildScaffoldFiles(options: ScaffoldTemplateOptions): ScaffoldFile[] {\r\n return [\r\n { path: \"package.json\", content: packageJson(options) },\r\n { path: \"wrangler.jsonc\", content: wranglerConfig(options) },\r\n { path: SCAFFOLD_ENTRY_PATH, content: SCAFFOLD_ENTRY_CONTENT },\r\n {\r\n path: \"cajonzero/config.json\",\r\n content: SCAFFOLD_CONFIG_CONTENT.replace(\"<project-name>\", options.packageName),\r\n },\r\n { path: \"cajonzero/maps.json\", content: SCAFFOLD_MAPS_CONTENT },\r\n { path: \".gitignore\", content: gitignore() },\r\n { path: \"README.md\", content: readme(options) },\r\n ];\r\n}\r\n","/**\r\n * cajonzero `create` scaffolder (T17).\r\n *\r\n * Writes a complete, runnable Cloudflare Workers project that contains **no\r\n * source code of its own** — configuration plus a two-line worker entry that\r\n * re-exports the cajonzero runtime (see `templates.ts` for the exact bytes).\r\n *\r\n * This module is the \"functions exposed in a ts file for other modules\" that\r\n * AGENTS.md asks for: the CLI calls {@link createProject}, and deployment\r\n * tooling can import the same functions directly. It is Node-only (filesystem\r\n * writes) and is never imported from the Cloudflare worker entry.\r\n *\r\n * Scaffolding is deterministic (stable file order + formatting) and refuses to\r\n * clobber: an existing non-empty target directory fails with\r\n * {@link ScaffoldError} `TARGET_NOT_EMPTY` instead of overwriting files.\r\n */\r\n\r\nimport { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from \"node:fs\";\r\nimport path from \"node:path\";\r\nimport { fileURLToPath } from \"node:url\";\r\n\r\nimport {\r\n buildScaffoldFiles,\r\n SCAFFOLD_COMPATIBILITY_DATE,\r\n type ScaffoldFile,\r\n} from \"./templates\";\r\n\r\n/** Caret prefix used for the `cajonzero` dependency range. */\r\nconst CAJONZERO_RANGE_PREFIX = \"^\";\r\n\r\n/** Fallback wrangler range, mirroring cajonzero's pinned devDependency. */\r\nconst FALLBACK_WRANGLER_RANGE = \"^4.151.1\";\r\n\r\n/** Fallback unstorage range, mirroring cajonzero's own dependency. */\r\nconst FALLBACK_UNSTORAGE_RANGE = \"^1.17.6\";\r\n\r\n/**\r\n * Absolute path to cajonzero's own `package.json`.\r\n *\r\n * This module lives two levels below the package root in BOTH layouts — the\r\n * source tree (`src/scaffold/`) and the bundled CLI (`dist/bin/`) — so\r\n * `../../package.json` resolves to the real manifest in dev and in production.\r\n */\r\nconst PACKAGE_JSON_URL = new URL(\"../../package.json\", import.meta.url);\r\n\r\n/** Stable error codes so callers/tests never string-match messages. */\r\nexport type ScaffoldErrorCode = \"INVALID_TARGET\" | \"TARGET_NOT_EMPTY\";\r\n\r\n/** Typed failure thrown by {@link createProject}. */\r\nexport class ScaffoldError extends Error {\r\n readonly code: ScaffoldErrorCode;\r\n\r\n constructor(code: ScaffoldErrorCode, message: string) {\r\n super(message);\r\n this.name = \"ScaffoldError\";\r\n this.code = code;\r\n }\r\n}\r\n\r\n/** The subset of cajonzero's manifest the scaffolder needs. */\r\nexport interface CajonzeroPackageInfo {\r\n /** cajonzero's own `version`. */\r\n readonly version: string;\r\n /** cajonzero's `wrangler` devDependency range. */\r\n readonly wranglerRange: string;\r\n /** cajonzero's `unstorage` dependency range (declared by created projects). */\r\n readonly unstorageRange: string;\r\n}\r\n\r\n/** Options accepted by {@link createProject}. */\r\nexport interface CreateProjectOptions {\r\n /** Base directory the target `folder` is resolved against (default: cwd). */\r\n readonly cwd?: string;\r\n /** Override for cajonzero's version (deterministic tests). */\r\n readonly version?: string;\r\n /** Override for the wrangler devDependency range. */\r\n readonly wranglerRange?: string;\r\n /** Override for the emitted `unstorage` dependency range. */\r\n readonly unstorageRange?: string;\r\n /** Override for `compatibility_date` (defaults to the pinned value). */\r\n readonly compatibilityDate?: string;\r\n}\r\n\r\n/** Result of a successful scaffold. */\r\nexport interface ScaffoldResult {\r\n /** Absolute path of the created project root. */\r\n readonly target: string;\r\n /** Written files, as project-relative POSIX paths, in write order. */\r\n readonly files: string[];\r\n}\r\n\r\n/**\r\n * Read cajonzero's own `package.json` to source the dependency version/range and\r\n * the wrangler devDependency, so a created project tracks the installed\r\n * cajonzero automatically.\r\n *\r\n * @param manifestPath Optional explicit path (tests). Defaults to the package\r\n * root resolved relative to this module.\r\n */\r\nexport function readCajonzeroPackage(manifestPath?: string): CajonzeroPackageInfo {\r\n const filePath = manifestPath ?? fileURLToPath(PACKAGE_JSON_URL);\r\n const raw = readFileSync(filePath, \"utf8\");\r\n const parsed = JSON.parse(raw) as {\r\n version?: unknown;\r\n dependencies?: Record<string, unknown>;\r\n devDependencies?: Record<string, unknown>;\r\n };\r\n const version =\r\n typeof parsed.version === \"string\" && parsed.version.length > 0 ? parsed.version : \"0.0.0\";\r\n const wrangler = parsed.devDependencies?.[\"wrangler\"];\r\n const wranglerRange =\r\n typeof wrangler === \"string\" && wrangler.length > 0 ? wrangler : FALLBACK_WRANGLER_RANGE;\r\n const unstorage = parsed.dependencies?.[\"unstorage\"];\r\n const unstorageRange =\r\n typeof unstorage === \"string\" && unstorage.length > 0\r\n ? unstorage\r\n : FALLBACK_UNSTORAGE_RANGE;\r\n return { version, wranglerRange, unstorageRange };\r\n}\r\n\r\n/** Derive a valid npm package name from the target directory's basename. */\r\nexport function toPackageName(folder: string): string {\r\n const base = path.basename(folder);\r\n const slug = base\r\n .toLowerCase()\r\n .replace(/[^a-z0-9._-]+/g, \"-\")\r\n .replace(/^[-._]+/, \"\")\r\n .replace(/[-._]+$/, \"\");\r\n return slug.length > 0 ? slug : \"cajonzero-app\";\r\n}\r\n\r\n/** True when `dir` exists and is a directory with at least one entry. */\r\nfunction isNonEmptyDirectory(dir: string): boolean {\r\n if (!existsSync(dir)) return false;\r\n if (!statSync(dir).isDirectory()) return false;\r\n return readdirSync(dir).length > 0;\r\n}\r\n\r\n/**\r\n * Scaffold a source-free, deployable project into `folder`.\r\n *\r\n * The target is resolved relative to `options.cwd` (default `process.cwd()`).\r\n * A missing target (or an existing **empty** directory) is filled; an existing\r\n * non-empty directory, or a target that exists as a file, is rejected.\r\n *\r\n * @throws {ScaffoldError} `INVALID_TARGET` (missing folder / not a directory)\r\n * or `TARGET_NOT_EMPTY`.\r\n */\r\nexport function createProject(folder: string, options: CreateProjectOptions = {}): ScaffoldResult {\r\n const name = typeof folder === \"string\" ? folder.trim() : \"\";\r\n if (name.length === 0) {\r\n throw new ScaffoldError(\"INVALID_TARGET\", \"create requires a target folder\");\r\n }\r\n\r\n const cwd = options.cwd ?? process.cwd();\r\n const target = path.resolve(cwd, name);\r\n\r\n if (existsSync(target) && !statSync(target).isDirectory()) {\r\n throw new ScaffoldError(\"INVALID_TARGET\", `target exists and is not a directory: ${target}`);\r\n }\r\n if (isNonEmptyDirectory(target)) {\r\n throw new ScaffoldError(\r\n \"TARGET_NOT_EMPTY\",\r\n `target directory already exists and is not empty: ${target} (refusing to overwrite)`,\r\n );\r\n }\r\n\r\n const manifest = options.version !== undefined ? undefined : readCajonzeroPackage();\r\n const version = options.version ?? manifest?.version ?? \"0.0.0\";\r\n const wranglerRange = options.wranglerRange ?? manifest?.wranglerRange ?? FALLBACK_WRANGLER_RANGE;\r\n const unstorageRange =\r\n options.unstorageRange ?? manifest?.unstorageRange ?? FALLBACK_UNSTORAGE_RANGE;\r\n\r\n const files: ScaffoldFile[] = buildScaffoldFiles({\r\n packageName: toPackageName(target),\r\n cajonzeroRange: `${CAJONZERO_RANGE_PREFIX}${version}`,\r\n unstorageRange,\r\n wranglerRange,\r\n compatibilityDate: options.compatibilityDate ?? SCAFFOLD_COMPATIBILITY_DATE,\r\n });\r\n\r\n mkdirSync(target, { recursive: true });\r\n for (const file of files) {\r\n const fullPath = path.join(target, file.path);\r\n mkdirSync(path.dirname(fullPath), { recursive: true });\r\n writeFileSync(fullPath, file.content, \"utf8\");\r\n }\r\n\r\n return { target, files: files.map((file) => file.path) };\r\n}\r\n","#!/usr/bin/env node\n/**\n * cajonzero CLI (`bin/cj0`).\n *\n * Commands:\n * create <folder> Scaffold a deployable project\n * serve [--port <port>] [--host <host>] Run the cajonzero server locally\n *\n * Maps are not managed by the CLI: unpack the `.boxplay` yourself, upload the\n * unpacked directory to object storage, and point `cajonzero/maps.json` at it.\n *\n * `serve` boots a real workerd runtime through wrangler's programmatic dev\n * server (`unstable_dev` → Miniflare). It is **not** a Node HTTP server: the\n * production/serve path is always Cloudflare Workers. This is the only place\n * Node APIs are permitted (see `scripts/lint-guards.mjs`).\n *\n * Exit codes: 0 on success, 1 on any failure (unknown command, bad options) with\n * a clear message on stderr.\n */\nimport { mkdirSync } from \"node:fs\";\nimport path from \"node:path\";\n\nimport { createProject, ScaffoldError } from \"../scaffold\";\n\nconst USAGE = `cj0 — serverless (Cloudflare) play-only BOX3 engine\n\nUsage:\n cj0 <command> [options]\n\nCommands:\n create <folder> Scaffold a deployable project\n serve [--port <port>] [--host <host>] Run the cajonzero server locally (workerd)\n\nServe options:\n --port <port> Listener port (default: 8787)\n --host <host> Listener host (default: 127.0.0.1)\n --config <file> Wrangler config (default: wrangler.jsonc)\n -h, --help Show this help\n\nMaps: unpack the .boxplay, upload the directory to OSS/CDN, and list its root\nin cajonzero/maps.json. The CLI does not import map archives.\n`;\n\nconst DEFAULT_SERVE_PORT = 8787;\nconst DEFAULT_SERVE_HOST = \"127.0.0.1\";\nconst DEFAULT_CONFIG = \"wrangler.jsonc\";\n\n/**\n * Secret bindings accepted from the process environment and injected as\n * plain-text vars for local dev. Secrets never live in `wrangler.jsonc`;\n * supplying them here lets `cj0 serve` run a fully functional local room\n * without `wrangler secret put`.\n */\nconst LOCAL_DEV_SECRETS = [\"CAJONZERO_SESSION_SECRET\", \"BOX_PLAY_ADMIN_TOKEN\"] as const;\n\n/** Collect any {@link LOCAL_DEV_SECRETS} present in the environment. */\nfunction localDevVars(): Record<string, string> | undefined {\n const vars: Record<string, string> = {};\n for (const name of LOCAL_DEV_SECRETS) {\n const value = process.env[name];\n if (value !== undefined && value.length > 0) {\n vars[name] = value;\n }\n }\n return Object.keys(vars).length > 0 ? vars : undefined;\n}\n\nfunction printUsage(): void {\n process.stdout.write(USAGE);\n}\n\nfunction fail(message: string): void {\n process.stderr.write(`cajonzero: ${message}\\n`);\n process.exitCode = 1;\n}\n\nfunction errorLabel(error: unknown): string {\n if (error instanceof ScaffoldError) {\n return `${error.code}: ${error.message}`;\n }\n return error instanceof Error ? error.message : String(error);\n}\n\nfunction runCreate(args: string[]): void {\n const [folder] = args;\n if (folder === undefined) {\n fail(\"create requires: cj0 create <folder>\");\n return;\n }\n const result = createProject(folder);\n process.stdout.write(\n `created project in ${result.target} (${result.files.length} files)\\n ` +\n `${result.files.join(\", \")}\\n next: cd ${folder} && npm install && npm run dev\\n`,\n );\n}\n\n/** Parsed `cj0 serve` flags. */\ninterface ServeFlags {\n readonly port: number;\n readonly host: string;\n readonly config: string;\n}\n\nfunction readFlagValue(args: string[], index: number, flag: string): string {\n const inline = args[index];\n if (inline !== undefined && inline.startsWith(`${flag}=`)) {\n return inline.slice(flag.length + 1);\n }\n const value = args[index + 1];\n if (value === undefined || value.startsWith(\"-\")) {\n throw new Error(`${flag} requires a value`);\n }\n return value;\n}\n\nfunction parseServeFlags(args: string[]): ServeFlags {\n let port = DEFAULT_SERVE_PORT;\n let host = DEFAULT_SERVE_HOST;\n let config = DEFAULT_CONFIG;\n\n for (let i = 0; i < args.length; i += 1) {\n const arg = args[i];\n if (arg === undefined) continue;\n if (arg === \"--port\" || arg.startsWith(\"--port=\")) {\n const raw = readFlagValue(args, i, \"--port\");\n const parsed = Number(raw);\n if (!Number.isInteger(parsed) || parsed < 0 || parsed > 65535) {\n throw new Error(`--port must be an integer in [0, 65535] (got \"${raw}\")`);\n }\n port = parsed;\n if (!arg.includes(\"=\")) i += 1;\n } else if (arg === \"--host\" || arg.startsWith(\"--host=\")) {\n host = readFlagValue(args, i, \"--host\");\n if (!arg.includes(\"=\")) i += 1;\n } else if (arg === \"--config\" || arg.startsWith(\"--config=\")) {\n config = readFlagValue(args, i, \"--config\");\n if (!arg.includes(\"=\")) i += 1;\n } else {\n throw new Error(`unexpected serve option \"${arg}\"`);\n }\n }\n\n return { port, host, config };\n}\n\n/**\n * Boot a real workerd instance through wrangler's programmatic dev server.\n *\n * `unstable_dev` bundles the configured entry (TypeScript + JSON imports\n * included), resolves `wrangler.jsonc` (DO + KV bindings, migrations, compat\n * date, assets) and starts Miniflare/workerd — the same runtime used in\n * production. It never starts a Node HTTP server.\n */\nasync function runServe(args: string[]): Promise<void> {\n const flags = parseServeFlags(args);\n\n // Dynamic import keeps wrangler (a large Node-only dep) out of the other\n // commands and off the worker path entirely.\n const { unstable_dev, unstable_readConfig } = await import(\"wrangler\");\n\n // Resolve the entry + assets from the project's wrangler config so a created\n // project's thin entry is served without any extra wiring.\n const config = unstable_readConfig({ config: flags.config });\n const entry = config.main;\n if (entry === undefined || entry.length === 0) {\n throw new Error(`no worker entry (\"main\") found in ${flags.config}`);\n }\n\n // wrangler refuses to start when `assets.directory` is missing. The UI build\n // emits it; create it eagerly so `serve` works before then. The path is\n // resolved relative to the wrangler config (not the cwd), exactly like\n // wrangler itself.\n const assetsDir = config.assets?.directory;\n if (typeof assetsDir === \"string\" && assetsDir.length > 0) {\n const configDir = path.dirname(path.resolve(process.cwd(), flags.config));\n mkdirSync(path.resolve(configDir, assetsDir), { recursive: true });\n }\n\n const secrets = localDevVars();\n\n const worker = await unstable_dev(entry, {\n config: flags.config,\n ip: flags.host,\n port: flags.port,\n local: true,\n logLevel: \"info\",\n // Inject local-dev secrets (from the environment) on top of the config\n // `vars` so auth/session work locally without `wrangler secret put`.\n ...(secrets !== undefined\n ? { vars: { ...(config.vars ?? {}), ...secrets } as Record<string, string> }\n : {}),\n experimental: {\n disableExperimentalWarning: true,\n disableDevRegistry: true,\n showInteractiveDevSession: false,\n },\n });\n\n process.stdout.write(\n `cj0 serve listening on http://${worker.address}:${worker.port}\\n` +\n ` (workerd via wrangler dev · config ${flags.config} · DO/KV/assets bound)\\n` +\n (secrets !== undefined\n ? ` (local dev secrets injected: ${Object.keys(secrets).join(\", \")})\\n`\n : \"\"),\n );\n\n let stopping = false;\n const shutdown = async (signal: string): Promise<void> => {\n if (stopping) return;\n stopping = true;\n process.stdout.write(`\\ncajonzero: received ${signal}, shutting down\\n`);\n try {\n await worker.stop();\n } finally {\n process.exit(0);\n }\n };\n\n process.on(\"SIGINT\", () => {\n void shutdown(\"SIGINT\");\n });\n process.on(\"SIGTERM\", () => {\n void shutdown(\"SIGTERM\");\n });\n\n await worker.waitUntilExit();\n}\n\nasync function main(argv: string[]): Promise<void> {\n const [command, ...rest] = argv;\n\n if (command === undefined || command === \"--help\" || command === \"-h\" || command === \"help\") {\n printUsage();\n return;\n }\n\n try {\n switch (command) {\n case \"serve\":\n await runServe(rest);\n return;\n case \"create\":\n runCreate(rest);\n return;\n default:\n process.stderr.write(`cajonzero: unknown command \"${command}\"\\n\\n`);\n printUsage();\n process.exitCode = 1;\n }\n } catch (error) {\n fail(errorLabel(error));\n }\n}\n\nmain(process.argv.slice(2)).catch((error: unknown) => {\n fail(errorLabel(error));\n});\n"]}
@@ -1,4 +1,4 @@
1
- import { T as TimestampedClientInput, N as NetPublic, A as NetSecret } from './room-BVwLGGkm.js';
1
+ import { T as TimestampedClientInput, N as NetPublic, O as NetSecret } from './providers-Dh-umPQ6.js';
2
2
 
3
3
  /**
4
4
  * cajonzero — mudb protocol envelope (message-id tables + `raw` channel).
@@ -235,8 +235,6 @@ declare class NetCodec {
235
235
  */
236
236
  /** Map delivery + seed loading policy. */
237
237
  interface MapsConfig {
238
- /** How long a resolved world seed stays in KV. `0` disables the cache. */
239
- readonly cacheTtlSeconds: number;
240
238
  /** Hard cap for a single map file fetched from the CDN (bytes). */
241
239
  readonly maxDownloadBytes: number;
242
240
  /** Parallel blob fetches while resolving a seed. */
package/dist/core.d.ts CHANGED
@@ -1,30 +1,9 @@
1
- import { M as MapEntry, W as WorldSeed } from './room-BVwLGGkm.js';
2
- export { a as EMPTY_MAP_CATALOG, b as MapCatalog, c as Repos, R as Room, d as RoomOptions, e as SETTINGS, f as SETTING_KEYS, g as SettingDefinition, h as Settings, i as SettingsError, j as SettingsErrorCode, k as SettingsService, l as StorageFactory, S as StorageSource, m as Store, n as WorldSeedReader, o as configureDefaultKV, p as createMemoryStore, q as createRepos, r as createSettingsService, s as createStore, t as findMap, u as isSettingKey, v as memoryDriver, w as parseMapCatalog, x as resolveStore, y as setStorageFactory } from './room-BVwLGGkm.js';
3
- export { C as CajonzeroConfig, D as DEFAULT_CONFIG, G as GAME_NET_INBOUND_BASE, a as GAME_NET_OUTBOUND_BASE, I as INBOUND_LAYOUT, M as MAX_INBOUND_FRAME_BYTES, b as MapsConfig, N as NetCodec, c as NetCodecError, d as NetCodecErrorCode, e as NetCodecHandlers, f as NetCodecOptions, O as OFFICIAL_PREFIX, g as OUTBOUND_LAYOUT, P as ProtocolDirection, h as ProtocolLayout, i as ProtocolSchemaLike, R as RoomConfig, j as chainLayouts, l as layoutMessages, k as layoutProtocol, m as matchesLayout, n as messageIds, o as messageNameAt, r as readNetFrameHeader, p as readNetMessageType, q as resolveConfig } from './config-BiQK9O_0.js';
1
+ import { M as MapEntry, W as WorldSeed } from './providers-Dh-umPQ6.js';
2
+ export { c as EMPTY_MAP_CATALOG, E as EnvProvider, d as MapCatalog, e as Repos, R as Room, f as RoomOptions, g as RuntimeProviders, h as SETTINGS, i as SETTING_KEYS, a as SettingDefaults, j as SettingDefinition, k as Settings, l as SettingsError, m as SettingsErrorCode, n as SettingsService, o as StorageFactory, S as StorageSource, p as Store, q as WorldSeedReader, r as configureDefaultKV, s as createMemoryStore, t as createRepos, u as createSettingsService, v as createStore, w as findMap, x as getRuntimeProviders, y as isSettingKey, z as memoryDriver, A as parseMapCatalog, B as resolveAdminToken, C as resolveEnvironment, F as resolveRooms, G as resolveSessionSecret, H as resolveSettingDefaults, I as resolveStore, J as setRuntimeProviders, K as setStorageFactory } from './providers-Dh-umPQ6.js';
3
+ export { C as CajonzeroConfig, D as DEFAULT_CONFIG, G as GAME_NET_INBOUND_BASE, a as GAME_NET_OUTBOUND_BASE, I as INBOUND_LAYOUT, M as MAX_INBOUND_FRAME_BYTES, b as MapsConfig, N as NetCodec, c as NetCodecError, d as NetCodecErrorCode, e as NetCodecHandlers, f as NetCodecOptions, O as OFFICIAL_PREFIX, g as OUTBOUND_LAYOUT, P as ProtocolDirection, h as ProtocolLayout, i as ProtocolSchemaLike, R as RoomConfig, j as chainLayouts, l as layoutMessages, k as layoutProtocol, m as matchesLayout, n as messageIds, o as messageNameAt, r as readNetFrameHeader, p as readNetMessageType, q as resolveConfig } from './config-YtU8BYQM.js';
4
4
  import 'unstorage';
5
- import 'zod';
6
5
  import 'gl-matrix';
7
-
8
- /**
9
- * cajonzero — resolved-seed cache.
10
- *
11
- * Reading a map straight from object storage costs one request per graph node
12
- * plus one per voxel chunk (dozens of subrequests). The resolved
13
- * {@link WorldSeed} is tiny by comparison (a few hundred KB), so it is cached in
14
- * the deployment KV the first time a map revision is loaded and read back for
15
- * every later cold start.
16
- *
17
- * The cache is keyed by **slug + revision (etag)**. Without a revision we do not
18
- * cache, because a re-uploaded map would otherwise be served stale forever.
19
- *
20
- * Worker path: `btoa`/`atob` only (no Node `Buffer`).
21
- */
22
-
23
- /** The subset of a KV namespace the cache needs (structural). */
24
- interface SeedCacheKv {
25
- get(key: string): Promise<string | null>;
26
- put(key: string, value: string, options?: unknown): Promise<void>;
27
- }
6
+ import 'zod';
28
7
 
29
8
  /**
30
9
  * cajonzero — HTTP map source.
@@ -35,8 +14,8 @@ interface SeedCacheKv {
35
14
  * voxel chunk blobs. Meshes/audio are never downloaded by the server — the
36
15
  * browser loads those straight from the same root.
37
16
  *
38
- * The resolved seed is cached in KV (see `seed-cache.ts`) so the dozens of
39
- * subrequests this costs happen once per map revision, not once per cold start.
17
+ * The seed is resolved on demand from object storage: map bytes stay on the
18
+ * CDN, never in the deployment KV.
40
19
  *
41
20
  * Worker path: `fetch`, `AbortController`, `TextDecoder` only.
42
21
  */
@@ -62,10 +41,6 @@ interface HttpMapOptions {
62
41
  maxDownloadBytes?: number;
63
42
  /** Parallel voxel-chunk reads. Default 8. */
64
43
  concurrency?: number;
65
- /** KV handle used for the resolved-seed cache. */
66
- kv?: SeedCacheKv;
67
- /** Seed cache TTL (seconds); `0` disables it. */
68
- cacheTtlSeconds?: number;
69
44
  }
70
45
  /**
71
46
  * A {@link WorldSeedReader} over one unpacked map root.
@@ -91,8 +66,9 @@ declare class HttpMapSource {
91
66
  /**
92
67
  * Load one map entry into a resolved {@link WorldSeed}.
93
68
  *
94
- * Order: KV cache (when the entry has a revision) → `bundle.json`/manifest →
95
- * graph + voxel blob fetches → write the seed back to KV.
69
+ * Reads `bundle.json`/manifest and the project graph straight from object
70
+ * storage. Nothing is cached server-side: map bytes belong on the CDN, not in
71
+ * the deployment KV.
96
72
  */
97
73
  declare function loadWorldSeedFromHttp(entry: MapEntry, options?: HttpMapOptions): Promise<WorldSeed>;
98
74