cajonzero 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -71,4 +71,4 @@ npm test # 运行测试
71
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,46 @@ 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
+ " mapSeedKv: (env) => env.CAJONZERO_KV,",
38
+ " rooms: (env) => env.ROOMS,",
39
+ " settings: (env) => {",
40
+ " const e = vars(env);",
41
+ " return {",
42
+ " playerLimit: num(e.CAJONZERO_PLAYER_LIMIT),",
43
+ " zeroPlayerTimeout: num(e.CAJONZERO_ZERO_PLAYER_TIMEOUT_MS),",
44
+ " autoSaveInterval: num(e.CAJONZERO_AUTOSAVE_INTERVAL_MS),",
45
+ " debug: bool(e.CAJONZERO_DEBUG),",
46
+ " teleportAccess: bool(e.CAJONZERO_TELEPORT_ACCESS),",
47
+ " paidAccess: bool(e.CAJONZERO_PAID_ACCESS),",
48
+ " scriptHeapSizeMB: num(e.CAJONZERO_SCRIPT_HEAP_MB),",
49
+ " scriptTimeout: num(e.CAJONZERO_SCRIPT_TIMEOUT_MS),",
50
+ " dbSizeLimit: num(e.CAJONZERO_DB_SIZE_LIMIT),",
51
+ " env: e.CAJONZERO_ENV,",
52
+ " };",
53
+ " },",
54
+ "});",
17
55
  'export { Room } from "cajonzero";',
18
56
  ""
19
57
  ].join("\n");
@@ -132,7 +170,13 @@ function readme(options) {
132
170
  'import maps from "../cajonzero/maps.json";',
133
171
  'import { serve } from "cajonzero/serve";',
134
172
  "",
135
- "export default serve({ config, maps });",
173
+ "export default serve({",
174
+ " config,",
175
+ " maps,",
176
+ " storage: (env) => /* any unstorage driver over env bindings */,",
177
+ " sessionSecret: (env) => env.CAJONZERO_SESSION_SECRET,",
178
+ " // adminToken / environment / mapSeedKv / rooms / settings \u2026",
179
+ "});",
136
180
  'export { Room } from "cajonzero";',
137
181
  "```",
138
182
  "",
@@ -146,13 +190,34 @@ function readme(options) {
146
190
  "`npm run build` validates the deploy by bundling the worker without",
147
191
  "publishing (`wrangler deploy --dry-run`).",
148
192
  "",
149
- "## Required secrets",
193
+ "## Environment",
194
+ "",
195
+ "Every value the runtime consumes is passed explicitly in `src/index.ts`;",
196
+ "the package never guesses env var names.",
197
+ "",
198
+ "| Name | Kind | Required | Purpose |",
199
+ "|---|---|---|---|",
200
+ "| `CAJONZERO_KV` | KV binding | yes | player/account data + map-seed cache |",
201
+ "| `ROOMS` | Durable Object binding | yes | room actors |",
202
+ "| `CAJONZERO_SESSION_SECRET` | secret | yes | signs session proofs and login cookies |",
203
+ "| `BOX_PLAY_ADMIN_TOKEN` | secret | yes | owner console and `/api/admin/*` |",
204
+ "| `CAJONZERO_ENV` | var | no | deployment label (`development` relaxes cookie `Secure`) |",
205
+ "| `CAJONZERO_PLAYER_LIMIT` | var | no | maximum players per room |",
206
+ "| `CAJONZERO_ZERO_PLAYER_TIMEOUT_MS` | var | no | empty-room grace window |",
207
+ "| `CAJONZERO_AUTOSAVE_INTERVAL_MS` | var | no | snapshot cadence |",
208
+ "| `CAJONZERO_DEBUG` | var | no | verbose logging |",
209
+ "| `CAJONZERO_TELEPORT_ACCESS` | var | no | allow non-team containers |",
210
+ "| `CAJONZERO_PAID_ACCESS` | var | no | restrict joins to paid containers |",
211
+ "| `CAJONZERO_SCRIPT_HEAP_MB` | var | no | QuickJS heap per script host |",
212
+ "| `CAJONZERO_SCRIPT_TIMEOUT_MS` | var | no | script invocation timeout |",
213
+ "| `CAJONZERO_DB_SIZE_LIMIT` | var | no | map-script storage budget |",
150
214
  "",
151
- "Set both secrets with Wrangler (never commit them):",
215
+ "Create the namespace and set the secrets before deploying:",
152
216
  "",
153
217
  "```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",
218
+ "npx wrangler kv namespace create CAJONZERO_KV # paste the id into wrangler.jsonc",
219
+ "npx wrangler secret put CAJONZERO_SESSION_SECRET",
220
+ "npx wrangler secret put BOX_PLAY_ADMIN_TOKEN",
156
221
  "```",
157
222
  "",
158
223
  "## Maps (object storage)",
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/scaffold/templates.ts","../../src/scaffold/create.ts","../../src/bin/cj0.ts"],"names":["path","mkdirSync"],"mappings":";;;;;;AAmDO,IAAM,mBAAA,GAAsB,cAAA;AAO5B,IAAM,2BAAA,GAA8B,YAAA;AAGpC,IAAM,0BAAA,GAA6B,mBAAA;AAMnC,IAAM,sBAAA,GAAyB;AAAA,EACpC,gDAAA;AAAA,EACA,4CAAA;AAAA,EACA,EAAA;AAAA,EACA,0CAAA;AAAA,EACA,EAAA;AAAA,EACA,yCAAA;AAAA,EACA,mCAAA;AAAA,EACA;AACF,CAAA,CAAE,KAAK,IAAI,CAAA;AAGJ,IAAM,uBAAA,GAA0B,GAAG,IAAA,CAAK,SAAA;AAAA,EAC7C;AAAA,IACE,IAAA,EAAM,gBAAA;AAAA,IACN,KAAA,EAAO,EAAE,IAAA,EAAM,QAAA,EAAS;AAAA,IACxB,IAAA,EAAM,EAAE,QAAA,EAAU,EAAA,EAAI,cAAc,IAAA,EAAQ;AAAA,IAC5C,IAAA,EAAM;AAAA,MACJ,eAAA,EAAiB,KAAA;AAAA,MACjB,gBAAA,EAAkB,QAAA;AAAA,MAClB,WAAA,EAAa,CAAA;AAAA,MACb,gBAAA,EAAkB;AAAA;AACpB,GACF;AAAA,EACA,IAAA;AAAA,EACA;AACF,CAAC;AAAA,CAAA;AAGM,IAAM,qBAAA,GAAwB,CAAA,EAAG,IAAA,CAAK,SAAA,CAAU,EAAE,OAAA,EAAS,CAAA,EAAG,IAAA,EAAM,EAAC,EAAE,EAAG,IAAA,EAAM,CAAC,CAAC;AAAA,CAAA;AAUzF,IAAM,cAAA,GACJ,CAAA,gMAAA,CAAA;AAIF,SAAS,YAAY,OAAA,EAA0C;AAC7D,EAAA,MAAM,QAAA,GAAW;AAAA,IACf,MAAM,OAAA,CAAQ,WAAA;AAAA,IACd,OAAA,EAAS,OAAA;AAAA,IACT,OAAA,EAAS,IAAA;AAAA,IACT,IAAA,EAAM,QAAA;AAAA,IACN,OAAA,EAAS;AAAA,MACP,GAAA,EAAK,GAAG,cAAc,CAAA,gBAAA,CAAA;AAAA,MACtB,KAAA,EAAO,GAAG,cAAc,CAAA,6BAAA,CAAA;AAAA,MACxB,MAAA,EAAQ,GAAG,cAAc,CAAA,mBAAA,CAAA;AAAA;AAAA;AAAA,MAGzB,OAAA,EAAS;AAAA,KACX;AAAA,IACA,YAAA,EAAc;AAAA,MACZ,WAAW,OAAA,CAAQ,cAAA;AAAA,MACnB,WAAW,OAAA,CAAQ;AAAA,KACrB;AAAA,IACA,eAAA,EAAiB;AAAA,MACf,UAAU,OAAA,CAAQ;AAAA;AACpB,GACF;AACA,EAAA,OAAO,GAAG,IAAA,CAAK,SAAA,CAAU,QAAA,EAAU,IAAA,EAAM,CAAC,CAAC;AAAA,CAAA;AAC7C;AAEA,SAAS,eAAe,OAAA,EAA0C;AAChE,EAAA,OAAO;AAAA,IACL,GAAA;AAAA,IACA,kEAAA;AAAA,IACA,8EAAA;AAAA,IACA,0DAAA;AAAA,IACA,4DAAA;AAAA,IACA,CAAA,UAAA,EAAa,IAAA,CAAK,SAAA,CAAU,OAAA,CAAQ,WAAW,CAAC,CAAA,CAAA,CAAA;AAAA,IAChD,CAAA,UAAA,EAAa,IAAA,CAAK,SAAA,CAAU,mBAAmB,CAAC,CAAA,CAAA,CAAA;AAAA,IAChD,CAAA,wBAAA,EAA2B,IAAA,CAAK,SAAA,CAAU,OAAA,CAAQ,iBAAiB,CAAC,CAAA,CAAA,CAAA;AAAA,IACpE,6EAAA;AAAA,IACA,8BAAA;AAAA,IACA,yEAAA;AAAA,IACA,4DAAA;AAAA,IACA,eAAA;AAAA,IACA,6BAAA;AAAA,IACA,4EAAA;AAAA,IACA,sDAAA;AAAA,IACA,kDAAA;AAAA,IACA,MAAA;AAAA,IACA,+EAAA;AAAA,IACA,uDAAA;AAAA,IACA,aAAA;AAAA,IACA,oCAAA;AAAA,IACA,MAAA;AAAA,IACA,sBAAA;AAAA,IACA,OAAA;AAAA,IACA,yEAAA;AAAA,IACA,kCAAA;AAAA,IACA,2EAAA;AAAA,IACA,CAAA,YAAA,EAAe,IAAA,CAAK,SAAA,CAAU,0BAA0B,CAAC,CAAA,CAAA;AAAA,IACzD,OAAA;AAAA,IACA,MAAA;AAAA,IACA,wBAAA;AAAA,IACA,mBAAA;AAAA,IACA,SAAA;AAAA,IACA,0EAAA;AAAA,IACA,0BAAA;AAAA,IACA,8BAAA;AAAA,IACA,SAAA;AAAA,IACA,OAAA;AAAA,IACA,MAAA;AAAA,IACA,mBAAA;AAAA,IACA,OAAA;AAAA,IACA,oBAAA;AAAA,IACA,sCAAA;AAAA,IACA,OAAA;AAAA,IACA,KAAA;AAAA,IACA,GAAA;AAAA,IACA;AAAA,GACF,CAAE,KAAK,IAAI,CAAA;AACb;AAEA,SAAS,SAAA,GAAoB;AAC3B,EAAA,OAAO,CAAC,gBAAgB,MAAA,EAAQ,WAAA,EAAa,aAAa,EAAE,CAAA,CAAE,KAAK,IAAI,CAAA;AACzE;AAEA,SAAS,OAAO,OAAA,EAA0C;AACxD,EAAA,OAAO;AAAA,IACL,CAAA,EAAA,EAAK,QAAQ,WAAW,CAAA,CAAA;AAAA,IACxB,EAAA;AAAA,IACA,uEAAA;AAAA,IACA,yDAAA;AAAA,IACA,EAAA;AAAA,IACA,mFAAA;AAAA,IACA,kFAAA;AAAA,IACA,UAAA;AAAA,IACA,EAAA;AAAA,IACA,OAAA;AAAA,IACA,gDAAA;AAAA,IACA,4CAAA;AAAA,IACA,0CAAA;AAAA,IACA,EAAA;AAAA,IACA,yCAAA;AAAA,IACA,mCAAA;AAAA,IACA,KAAA;AAAA,IACA,EAAA;AAAA,IACA,gBAAA;AAAA,IACA,EAAA;AAAA,IACA,SAAA;AAAA,IACA,8EAAA;AAAA,IACA,6DAAA;AAAA,IACA,KAAA;AAAA,IACA,EAAA;AAAA,IACA,qEAAA;AAAA,IACA,2CAAA;AAAA,IACA,EAAA;AAAA,IACA,qBAAA;AAAA,IACA,EAAA;AAAA,IACA,qDAAA;AAAA,IACA,EAAA;AAAA,IACA,SAAA;AAAA,IACA,kFAAA;AAAA,IACA,kFAAA;AAAA,IACA,KAAA;AAAA,IACA,EAAA;AAAA,IACA,0BAAA;AAAA,IACA,EAAA;AAAA,IACA,8EAAA;AAAA,IACA,iFAAA;AAAA,IACA,oCAAA;AAAA,IACA,EAAA;AAAA,IACA,SAAA;AAAA,IACA,oBAAA;AAAA,IACA,uCAAA;AAAA,IACA,iDAAA;AAAA,IACA,0CAAA;AAAA,IACA,KAAA;AAAA,IACA,EAAA;AAAA,IACA,6CAAA;AAAA,IACA,EAAA;AAAA,IACA,SAAA;AAAA,IACA,GAAA;AAAA,IACA,iBAAA;AAAA,IACA,aAAA;AAAA,IACA,OAAA;AAAA,IACA,wBAAA;AAAA,IACA,2DAAA;AAAA,IACA,mCAAA;AAAA,IACA,sCAAA;AAAA,IACA,2BAAA;AAAA,IACA,wBAAA;AAAA,IACA,OAAA;AAAA,IACA,KAAA;AAAA,IACA,GAAA;AAAA,IACA,KAAA;AAAA,IACA,EAAA;AAAA,IACA,+DAAA;AAAA,IACA,0EAAA;AAAA,IACA,8DAAA;AAAA,IACA,8EAAA;AAAA,IACA,uEAAA;AAAA,IACA,EAAA;AAAA,IACA,sBAAA;AAAA,IACA,EAAA;AAAA,IACA,6DAAA;AAAA,IACA,kFAAA;AAAA,IACA,2EAAA;AAAA,IACA,gFAAA;AAAA,IACA,EAAA;AAAA,IACA,WAAA;AAAA,IACA,EAAA;AAAA,IACA,SAAA;AAAA,IACA,mCAAA;AAAA,IACA,KAAA;AAAA,IACA;AAAA,GACF,CAAE,KAAK,IAAI,CAAA;AACb;AAOO,SAAS,mBAAmB,OAAA,EAAkD;AACnF,EAAA,OAAO;AAAA,IACL,EAAE,IAAA,EAAM,cAAA,EAAgB,OAAA,EAAS,WAAA,CAAY,OAAO,CAAA,EAAE;AAAA,IACtD,EAAE,IAAA,EAAM,gBAAA,EAAkB,OAAA,EAAS,cAAA,CAAe,OAAO,CAAA,EAAE;AAAA,IAC3D,EAAE,IAAA,EAAM,mBAAA,EAAqB,OAAA,EAAS,sBAAA,EAAuB;AAAA,IAC7D;AAAA,MACE,IAAA,EAAM,uBAAA;AAAA,MACN,OAAA,EAAS,uBAAA,CAAwB,OAAA,CAAQ,gBAAA,EAAkB,QAAQ,WAAW;AAAA,KAChF;AAAA,IACA,EAAE,IAAA,EAAM,qBAAA,EAAuB,OAAA,EAAS,qBAAA,EAAsB;AAAA,IAC9D,EAAE,IAAA,EAAM,YAAA,EAAc,OAAA,EAAS,WAAU,EAAE;AAAA,IAC3C,EAAE,IAAA,EAAM,WAAA,EAAa,OAAA,EAAS,MAAA,CAAO,OAAO,CAAA;AAAE,GAChD;AACF;ACnRA,IAAM,sBAAA,GAAyB,GAAA;AAG/B,IAAM,uBAAA,GAA0B,UAAA;AAGhC,IAAM,wBAAA,GAA2B,SAAA;AASjC,IAAM,gBAAA,GAAmB,IAAI,GAAA,CAAI,oBAAA,EAAsB,YAAY,GAAG,CAAA;AAM/D,IAAM,aAAA,GAAN,cAA4B,KAAA,CAAM;AAAA,EAC9B,IAAA;AAAA,EAET,WAAA,CAAY,MAAyB,OAAA,EAAiB;AACpD,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,IAAA,GAAO,eAAA;AACZ,IAAA,IAAA,CAAK,IAAA,GAAO,IAAA;AAAA,EACd;AACF,CAAA;AA0CO,SAAS,qBAAqB,YAAA,EAA6C;AAChF,EAAA,MAAM,QAAA,GAA2B,aAAA,CAAc,gBAAgB,CAAA;AAC/D,EAAA,MAAM,GAAA,GAAM,YAAA,CAAa,QAAA,EAAU,MAAM,CAAA;AACzC,EAAA,MAAM,MAAA,GAAS,IAAA,CAAK,KAAA,CAAM,GAAG,CAAA;AAK7B,EAAA,MAAM,OAAA,GACJ,OAAO,MAAA,CAAO,OAAA,KAAY,QAAA,IAAY,OAAO,OAAA,CAAQ,MAAA,GAAS,CAAA,GAAI,MAAA,CAAO,OAAA,GAAU,OAAA;AACrF,EAAA,MAAM,QAAA,GAAW,MAAA,CAAO,eAAA,GAAkB,UAAU,CAAA;AACpD,EAAA,MAAM,gBACJ,OAAO,QAAA,KAAa,YAAY,QAAA,CAAS,MAAA,GAAS,IAAI,QAAA,GAAW,uBAAA;AACnE,EAAA,MAAM,SAAA,GAAY,MAAA,CAAO,YAAA,GAAe,WAAW,CAAA;AACnD,EAAA,MAAM,iBACJ,OAAO,SAAA,KAAc,YAAY,SAAA,CAAU,MAAA,GAAS,IAChD,SAAA,GACA,wBAAA;AACN,EAAA,OAAO,EAAE,OAAA,EAAS,aAAA,EAAe,cAAA,EAAe;AAClD;AAGO,SAAS,cAAc,MAAA,EAAwB;AACpD,EAAA,MAAM,IAAA,GAAO,IAAA,CAAK,QAAA,CAAS,MAAM,CAAA;AACjC,EAAA,MAAM,IAAA,GAAO,IAAA,CACV,WAAA,EAAY,CACZ,QAAQ,gBAAA,EAAkB,GAAG,CAAA,CAC7B,OAAA,CAAQ,SAAA,EAAW,EAAE,CAAA,CACrB,OAAA,CAAQ,WAAW,EAAE,CAAA;AACxB,EAAA,OAAO,IAAA,CAAK,MAAA,GAAS,CAAA,GAAI,IAAA,GAAO,eAAA;AAClC;AAGA,SAAS,oBAAoB,GAAA,EAAsB;AACjD,EAAA,IAAI,CAAC,UAAA,CAAW,GAAG,CAAA,EAAG,OAAO,KAAA;AAC7B,EAAA,IAAI,CAAC,QAAA,CAAS,GAAG,CAAA,CAAE,WAAA,IAAe,OAAO,KAAA;AACzC,EAAA,OAAO,WAAA,CAAY,GAAG,CAAA,CAAE,MAAA,GAAS,CAAA;AACnC;AAYO,SAAS,aAAA,CAAc,MAAA,EAAgB,OAAA,GAAgC,EAAC,EAAmB;AAChG,EAAA,MAAM,OAAO,OAAO,MAAA,KAAW,QAAA,GAAW,MAAA,CAAO,MAAK,GAAI,EAAA;AAC1D,EAAA,IAAI,IAAA,CAAK,WAAW,CAAA,EAAG;AACrB,IAAA,MAAM,IAAI,aAAA,CAAc,gBAAA,EAAkB,iCAAiC,CAAA;AAAA,EAC7E;AAEA,EAAA,MAAM,GAAA,GAAM,OAAA,CAAQ,GAAA,IAAO,OAAA,CAAQ,GAAA,EAAI;AACvC,EAAA,MAAM,MAAA,GAAS,IAAA,CAAK,OAAA,CAAQ,GAAA,EAAK,IAAI,CAAA;AAErC,EAAA,IAAI,UAAA,CAAW,MAAM,CAAA,IAAK,CAAC,SAAS,MAAM,CAAA,CAAE,aAAY,EAAG;AACzD,IAAA,MAAM,IAAI,aAAA,CAAc,gBAAA,EAAkB,CAAA,sCAAA,EAAyC,MAAM,CAAA,CAAE,CAAA;AAAA,EAC7F;AACA,EAAA,IAAI,mBAAA,CAAoB,MAAM,CAAA,EAAG;AAC/B,IAAA,MAAM,IAAI,aAAA;AAAA,MACR,kBAAA;AAAA,MACA,qDAAqD,MAAM,CAAA,wBAAA;AAAA,KAC7D;AAAA,EACF;AAEA,EAAA,MAAM,QAAA,GAAW,OAAA,CAAQ,OAAA,KAAY,MAAA,GAAY,SAAY,oBAAA,EAAqB;AAClF,EAAA,MAAM,OAAA,GAAU,OAAA,CAAQ,OAAA,IAAW,QAAA,EAAU,OAAA,IAAW,OAAA;AACxD,EAAA,MAAM,aAAA,GAAgB,OAAA,CAAQ,aAAA,IAAiB,QAAA,EAAU,aAAA,IAAiB,uBAAA;AAC1E,EAAA,MAAM,cAAA,GACJ,OAAA,CAAQ,cAAA,IAAkB,QAAA,EAAU,cAAA,IAAkB,wBAAA;AAExD,EAAA,MAAM,QAAwB,kBAAA,CAAmB;AAAA,IAC/C,WAAA,EAAa,cAAc,MAAM,CAAA;AAAA,IACjC,cAAA,EAAgB,CAAA,EAAG,sBAAsB,CAAA,EAAG,OAAO,CAAA,CAAA;AAAA,IACnD,cAAA;AAAA,IACA,aAAA;AAAA,IACA,iBAAA,EAAmB,QAAQ,iBAAA,IAAqB;AAAA,GACjD,CAAA;AAED,EAAA,SAAA,CAAU,MAAA,EAAQ,EAAE,SAAA,EAAW,IAAA,EAAM,CAAA;AACrC,EAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,IAAA,MAAM,QAAA,GAAW,IAAA,CAAK,IAAA,CAAK,MAAA,EAAQ,KAAK,IAAI,CAAA;AAC5C,IAAA,SAAA,CAAU,KAAK,OAAA,CAAQ,QAAQ,GAAG,EAAE,SAAA,EAAW,MAAM,CAAA;AACrD,IAAA,aAAA,CAAc,QAAA,EAAU,IAAA,CAAK,OAAA,EAAS,MAAM,CAAA;AAAA,EAC9C;AAEA,EAAA,OAAO,EAAE,QAAQ,KAAA,EAAO,KAAA,CAAM,IAAI,CAAC,IAAA,KAAS,IAAA,CAAK,IAAI,CAAA,EAAE;AACzD;;;ACrKA,IAAM,KAAA,GAAQ,CAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA,CAAA;AAmBd,IAAM,kBAAA,GAAqB,IAAA;AAC3B,IAAM,kBAAA,GAAqB,WAAA;AAC3B,IAAM,cAAA,GAAiB,gBAAA;AAQvB,IAAM,iBAAA,GAAoB,CAAC,0BAAA,EAA4B,sBAAsB,CAAA;AAG7E,SAAS,YAAA,GAAmD;AAC1D,EAAA,MAAM,OAA+B,EAAC;AACtC,EAAA,KAAA,MAAW,QAAQ,iBAAA,EAAmB;AACpC,IAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,GAAA,CAAI,IAAI,CAAA;AAC9B,IAAA,IAAI,KAAA,KAAU,MAAA,IAAa,KAAA,CAAM,MAAA,GAAS,CAAA,EAAG;AAC3C,MAAA,IAAA,CAAK,IAAI,CAAA,GAAI,KAAA;AAAA,IACf;AAAA,EACF;AACA,EAAA,OAAO,OAAO,IAAA,CAAK,IAAI,CAAA,CAAE,MAAA,GAAS,IAAI,IAAA,GAAO,MAAA;AAC/C;AAEA,SAAS,UAAA,GAAmB;AAC1B,EAAA,OAAA,CAAQ,MAAA,CAAO,MAAM,KAAK,CAAA;AAC5B;AAEA,SAAS,KAAK,OAAA,EAAuB;AACnC,EAAA,OAAA,CAAQ,MAAA,CAAO,KAAA,CAAM,CAAA,WAAA,EAAc,OAAO;AAAA,CAAI,CAAA;AAC9C,EAAA,OAAA,CAAQ,QAAA,GAAW,CAAA;AACrB;AAEA,SAAS,WAAW,KAAA,EAAwB;AAC1C,EAAA,IAAI,iBAAiB,aAAA,EAAe;AAClC,IAAA,OAAO,CAAA,EAAG,KAAA,CAAM,IAAI,CAAA,EAAA,EAAK,MAAM,OAAO,CAAA,CAAA;AAAA,EACxC;AACA,EAAA,OAAO,KAAA,YAAiB,KAAA,GAAQ,KAAA,CAAM,OAAA,GAAU,OAAO,KAAK,CAAA;AAC9D;AAEA,SAAS,UAAU,IAAA,EAAsB;AACvC,EAAA,MAAM,CAAC,MAAM,CAAA,GAAI,IAAA;AACjB,EAAA,IAAI,WAAW,MAAA,EAAW;AACxB,IAAA,IAAA,CAAK,sCAAsC,CAAA;AAC3C,IAAA;AAAA,EACF;AACA,EAAA,MAAM,MAAA,GAAS,cAAc,MAAM,CAAA;AACnC,EAAA,OAAA,CAAQ,MAAA,CAAO,KAAA;AAAA,IACb,sBAAsB,MAAA,CAAO,MAAM,CAAA,EAAA,EAAK,MAAA,CAAO,MAAM,MAAM,CAAA;AAAA,EAAA,EACtD,MAAA,CAAO,KAAA,CAAM,IAAA,CAAK,IAAI,CAAC;AAAA,WAAA,EAAgB,MAAM,CAAA;AAAA;AAAA,GACpD;AACF;AASA,SAAS,aAAA,CAAc,IAAA,EAAgB,KAAA,EAAe,IAAA,EAAsB;AAC1E,EAAA,MAAM,MAAA,GAAS,KAAK,KAAK,CAAA;AACzB,EAAA,IAAI,WAAW,MAAA,IAAa,MAAA,CAAO,WAAW,CAAA,EAAG,IAAI,GAAG,CAAA,EAAG;AACzD,IAAA,OAAO,MAAA,CAAO,KAAA,CAAM,IAAA,CAAK,MAAA,GAAS,CAAC,CAAA;AAAA,EACrC;AACA,EAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,KAAA,GAAQ,CAAC,CAAA;AAC5B,EAAA,IAAI,KAAA,KAAU,MAAA,IAAa,KAAA,CAAM,UAAA,CAAW,GAAG,CAAA,EAAG;AAChD,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,EAAG,IAAI,CAAA,iBAAA,CAAmB,CAAA;AAAA,EAC5C;AACA,EAAA,OAAO,KAAA;AACT;AAEA,SAAS,gBAAgB,IAAA,EAA4B;AACnD,EAAA,IAAI,IAAA,GAAO,kBAAA;AACX,EAAA,IAAI,IAAA,GAAO,kBAAA;AACX,EAAA,IAAI,MAAA,GAAS,cAAA;AAEb,EAAA,KAAA,IAAS,IAAI,CAAA,EAAG,CAAA,GAAI,IAAA,CAAK,MAAA,EAAQ,KAAK,CAAA,EAAG;AACvC,IAAA,MAAM,GAAA,GAAM,KAAK,CAAC,CAAA;AAClB,IAAA,IAAI,QAAQ,MAAA,EAAW;AACvB,IAAA,IAAI,GAAA,KAAQ,QAAA,IAAY,GAAA,CAAI,UAAA,CAAW,SAAS,CAAA,EAAG;AACjD,MAAA,MAAM,GAAA,GAAM,aAAA,CAAc,IAAA,EAAM,CAAA,EAAG,QAAQ,CAAA;AAC3C,MAAA,MAAM,MAAA,GAAS,OAAO,GAAG,CAAA;AACzB,MAAA,IAAI,CAAC,OAAO,SAAA,CAAU,MAAM,KAAK,MAAA,GAAS,CAAA,IAAK,SAAS,KAAA,EAAO;AAC7D,QAAA,MAAM,IAAI,KAAA,CAAM,CAAA,8CAAA,EAAiD,GAAG,CAAA,EAAA,CAAI,CAAA;AAAA,MAC1E;AACA,MAAA,IAAA,GAAO,MAAA;AACP,MAAA,IAAI,CAAC,GAAA,CAAI,QAAA,CAAS,GAAG,GAAG,CAAA,IAAK,CAAA;AAAA,IAC/B,WAAW,GAAA,KAAQ,QAAA,IAAY,GAAA,CAAI,UAAA,CAAW,SAAS,CAAA,EAAG;AACxD,MAAA,IAAA,GAAO,aAAA,CAAc,IAAA,EAAM,CAAA,EAAG,QAAQ,CAAA;AACtC,MAAA,IAAI,CAAC,GAAA,CAAI,QAAA,CAAS,GAAG,GAAG,CAAA,IAAK,CAAA;AAAA,IAC/B,WAAW,GAAA,KAAQ,UAAA,IAAc,GAAA,CAAI,UAAA,CAAW,WAAW,CAAA,EAAG;AAC5D,MAAA,MAAA,GAAS,aAAA,CAAc,IAAA,EAAM,CAAA,EAAG,UAAU,CAAA;AAC1C,MAAA,IAAI,CAAC,GAAA,CAAI,QAAA,CAAS,GAAG,GAAG,CAAA,IAAK,CAAA;AAAA,IAC/B,CAAA,MAAO;AACL,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,yBAAA,EAA4B,GAAG,CAAA,CAAA,CAAG,CAAA;AAAA,IACpD;AAAA,EACF;AAEA,EAAA,OAAO,EAAE,IAAA,EAAM,IAAA,EAAM,MAAA,EAAO;AAC9B;AAUA,eAAe,SAAS,IAAA,EAA+B;AACrD,EAAA,MAAM,KAAA,GAAQ,gBAAgB,IAAI,CAAA;AAIlC,EAAA,MAAM,EAAE,YAAA,EAAc,mBAAA,EAAoB,GAAI,MAAM,OAAO,UAAU,CAAA;AAIrE,EAAA,MAAM,SAAS,mBAAA,CAAoB,EAAE,MAAA,EAAQ,KAAA,CAAM,QAAQ,CAAA;AAC3D,EAAA,MAAM,QAAQ,MAAA,CAAO,IAAA;AACrB,EAAA,IAAI,KAAA,KAAU,MAAA,IAAa,KAAA,CAAM,MAAA,KAAW,CAAA,EAAG;AAC7C,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,kCAAA,EAAqC,KAAA,CAAM,MAAM,CAAA,CAAE,CAAA;AAAA,EACrE;AAMA,EAAA,MAAM,SAAA,GAAY,OAAO,MAAA,EAAQ,SAAA;AACjC,EAAA,IAAI,OAAO,SAAA,KAAc,QAAA,IAAY,SAAA,CAAU,SAAS,CAAA,EAAG;AACzD,IAAA,MAAM,SAAA,GAAYA,IAAAA,CAAK,OAAA,CAAQA,IAAAA,CAAK,OAAA,CAAQ,QAAQ,GAAA,EAAI,EAAG,KAAA,CAAM,MAAM,CAAC,CAAA;AACxE,IAAAC,SAAAA,CAAUD,KAAK,OAAA,CAAQ,SAAA,EAAW,SAAS,CAAA,EAAG,EAAE,SAAA,EAAW,IAAA,EAAM,CAAA;AAAA,EACnE;AAEA,EAAA,MAAM,UAAU,YAAA,EAAa;AAE7B,EAAA,MAAM,MAAA,GAAS,MAAM,YAAA,CAAa,KAAA,EAAO;AAAA,IACvC,QAAQ,KAAA,CAAM,MAAA;AAAA,IACd,IAAI,KAAA,CAAM,IAAA;AAAA,IACV,MAAM,KAAA,CAAM,IAAA;AAAA,IACZ,KAAA,EAAO,IAAA;AAAA,IACP,QAAA,EAAU,MAAA;AAAA;AAAA;AAAA,IAGV,GAAI,OAAA,KAAY,MAAA,GACZ,EAAE,MAAM,EAAE,GAAI,MAAA,CAAO,IAAA,IAAQ,EAAC,EAAI,GAAG,OAAA,EAAQ,KAC7C,EAAC;AAAA,IACL,YAAA,EAAc;AAAA,MACZ,0BAAA,EAA4B,IAAA;AAAA,MAC5B,kBAAA,EAAoB,IAAA;AAAA,MACpB,yBAAA,EAA2B;AAAA;AAC7B,GACD,CAAA;AAED,EAAA,OAAA,CAAQ,MAAA,CAAO,KAAA;AAAA,IACb,CAAA,8BAAA,EAAiC,MAAA,CAAO,OAAO,CAAA,CAAA,EAAI,OAAO,IAAI;AAAA,wCAAA,EACpB,MAAM,MAAM,CAAA;AAAA,CAAA,IACnD,OAAA,KAAY,SACT,CAAA,+BAAA,EAAkC,MAAA,CAAO,KAAK,OAAO,CAAA,CAAE,IAAA,CAAK,IAAI,CAAC,CAAA;AAAA,CAAA,GACjE,EAAA;AAAA,GACR;AAEA,EAAA,IAAI,QAAA,GAAW,KAAA;AACf,EAAA,MAAM,QAAA,GAAW,OAAO,MAAA,KAAkC;AACxD,IAAA,IAAI,QAAA,EAAU;AACd,IAAA,QAAA,GAAW,IAAA;AACX,IAAA,OAAA,CAAQ,OAAO,KAAA,CAAM;AAAA,oBAAA,EAAyB,MAAM,CAAA;AAAA,CAAmB,CAAA;AACvE,IAAA,IAAI;AACF,MAAA,MAAM,OAAO,IAAA,EAAK;AAAA,IACpB,CAAA,SAAE;AACA,MAAA,OAAA,CAAQ,KAAK,CAAC,CAAA;AAAA,IAChB;AAAA,EACF,CAAA;AAEA,EAAA,OAAA,CAAQ,EAAA,CAAG,UAAU,MAAM;AACzB,IAAA,KAAK,SAAS,QAAQ,CAAA;AAAA,EACxB,CAAC,CAAA;AACD,EAAA,OAAA,CAAQ,EAAA,CAAG,WAAW,MAAM;AAC1B,IAAA,KAAK,SAAS,SAAS,CAAA;AAAA,EACzB,CAAC,CAAA;AAED,EAAA,MAAM,OAAO,aAAA,EAAc;AAC7B;AAEA,eAAe,KAAK,IAAA,EAA+B;AACjD,EAAA,MAAM,CAAC,OAAA,EAAS,GAAG,IAAI,CAAA,GAAI,IAAA;AAE3B,EAAA,IAAI,YAAY,MAAA,IAAa,OAAA,KAAY,YAAY,OAAA,KAAY,IAAA,IAAQ,YAAY,MAAA,EAAQ;AAC3F,IAAA,UAAA,EAAW;AACX,IAAA;AAAA,EACF;AAEA,EAAA,IAAI;AACF,IAAA,QAAQ,OAAA;AAAS,MACf,KAAK,OAAA;AACH,QAAA,MAAM,SAAS,IAAI,CAAA;AACnB,QAAA;AAAA,MACF,KAAK,QAAA;AACH,QAAA,SAAA,CAAU,IAAI,CAAA;AACd,QAAA;AAAA,MACF;AACE,QAAA,OAAA,CAAQ,MAAA,CAAO,KAAA,CAAM,CAAA,4BAAA,EAA+B,OAAO,CAAA;;AAAA,CAAO,CAAA;AAClE,QAAA,UAAA,EAAW;AACX,QAAA,OAAA,CAAQ,QAAA,GAAW,CAAA;AAAA;AACvB,EACF,SAAS,KAAA,EAAO;AACd,IAAA,IAAA,CAAK,UAAA,CAAW,KAAK,CAAC,CAAA;AAAA,EACxB;AACF;AAEA,IAAA,CAAK,OAAA,CAAQ,KAAK,KAAA,CAAM,CAAC,CAAC,CAAA,CAAE,KAAA,CAAM,CAAC,KAAA,KAAmB;AACpD,EAAA,IAAA,CAAK,UAAA,CAAW,KAAK,CAAC,CAAA;AACxB,CAAC,CAAA","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 \" mapSeedKv: (env) => env.CAJONZERO_KV,\",\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 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({\",\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 / mapSeedKv / 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-seed cache |\",\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 ' \"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,4 +1,4 @@
1
- import { T as TimestampedClientInput, N as NetPublic, A as NetSecret } from './room-BVwLGGkm.js';
1
+ import { T as TimestampedClientInput, P as NetPublic, Q as NetSecret } from './providers-CJgLgWh4.js';
2
2
 
3
3
  /**
4
4
  * cajonzero — mudb protocol envelope (message-id tables + `raw` channel).
package/dist/core.d.ts CHANGED
@@ -1,9 +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-CJgLgWh4.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 resolveMapSeedKv, G as resolveRooms, H as resolveSessionSecret, I as resolveSettingDefaults, J as resolveStore, L as setRuntimeProviders, N as setStorageFactory } from './providers-CJgLgWh4.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-Che_6xEE.js';
4
4
  import 'unstorage';
5
- import 'zod';
6
5
  import 'gl-matrix';
6
+ import 'zod';
7
7
 
8
8
  /**
9
9
  * cajonzero — resolved-seed cache.
package/dist/core.js CHANGED
@@ -14068,92 +14068,66 @@ async function loadWorldSeedFromHttp(entry, options = {}) {
14068
14068
  }
14069
14069
  return seed;
14070
14070
  }
14071
- function rawEnv(env, name) {
14072
- return env[name];
14073
- }
14074
- function envString(env, name) {
14075
- const value = rawEnv(env, name);
14076
- return typeof value === "string" && value.length > 0 ? value : void 0;
14077
- }
14078
- function envNumber(env, name) {
14079
- const value = rawEnv(env, name);
14080
- if (typeof value === "number" && Number.isFinite(value)) return value;
14081
- if (typeof value === "string" && value.trim().length > 0) {
14082
- const parsed = Number(value);
14083
- if (Number.isFinite(parsed)) return parsed;
14084
- }
14085
- return void 0;
14086
- }
14087
- function envBoolean(env, name) {
14088
- const value = rawEnv(env, name);
14089
- if (typeof value === "boolean") return value;
14090
- if (typeof value === "string") {
14091
- const normalized = value.trim().toLowerCase();
14092
- if (normalized === "1" || normalized === "true") return true;
14093
- if (normalized === "0" || normalized === "false") return false;
14094
- }
14095
- return void 0;
14096
- }
14097
14071
  var MEBIBYTE = 1024 * 1024;
14098
14072
  var ZERO_PLAYER_TIMEOUT_DEFAULT_MS = 48e4;
14099
14073
  var SETTINGS = {
14100
14074
  playerLimit: {
14101
14075
  schema: z.number().int().min(1).max(1e4),
14102
- default: (env) => envNumber(env, "CAJONZERO_PLAYER_LIMIT") ?? 100,
14076
+ default: (defaults) => defaults.playerLimit ?? 100,
14103
14077
  liveUpdatable: true,
14104
14078
  description: "Maximum simultaneous players admitted to one room (read live at admission)."
14105
14079
  },
14106
14080
  zeroPlayerTimeout: {
14107
14081
  schema: z.number().int().min(0).max(864e5),
14108
- default: (env) => envNumber(env, "CAJONZERO_ZERO_PLAYER_TIMEOUT_MS") ?? ZERO_PLAYER_TIMEOUT_DEFAULT_MS,
14082
+ default: (defaults) => defaults.zeroPlayerTimeout ?? ZERO_PLAYER_TIMEOUT_DEFAULT_MS,
14109
14083
  liveUpdatable: true,
14110
14084
  description: "Empty-room grace window before eviction is allowed, ms (read live at the drain transition)."
14111
14085
  },
14112
14086
  autoSaveInterval: {
14113
14087
  schema: z.number().int().min(1e3).max(864e5),
14114
- default: (env) => envNumber(env, "CAJONZERO_AUTOSAVE_INTERVAL_MS") ?? 3e5,
14088
+ default: (defaults) => defaults.autoSaveInterval ?? 3e5,
14115
14089
  liveUpdatable: true,
14116
14090
  description: "Snapshot cadence hint, ms (read live by the persistence loop)."
14117
14091
  },
14118
14092
  debug: {
14119
14093
  schema: z.boolean(),
14120
- default: (env) => envBoolean(env, "CAJONZERO_DEBUG") ?? false,
14094
+ default: (defaults) => defaults.debug ?? false,
14121
14095
  liveUpdatable: true,
14122
14096
  description: "Verbose logging toggle (read live at each log site)."
14123
14097
  },
14124
14098
  teleportAccess: {
14125
14099
  schema: z.boolean(),
14126
- default: (env) => envBoolean(env, "CAJONZERO_TELEPORT_ACCESS") ?? false,
14100
+ default: (defaults) => defaults.teleportAccess ?? false,
14127
14101
  liveUpdatable: true,
14128
14102
  description: "Allow non-team ('teleport') containers (read live at container admission)."
14129
14103
  },
14130
14104
  paidAccess: {
14131
14105
  schema: z.boolean(),
14132
- default: (env) => envBoolean(env, "CAJONZERO_PAID_ACCESS") ?? false,
14106
+ default: (defaults) => defaults.paidAccess ?? false,
14133
14107
  liveUpdatable: true,
14134
14108
  description: "Restrict joins to paid containers (read live at container admission)."
14135
14109
  },
14136
14110
  scriptHeapSizeMB: {
14137
14111
  schema: z.number().int().min(1).max(4096),
14138
- default: (env) => envNumber(env, "CAJONZERO_SCRIPT_HEAP_MB") ?? 256,
14112
+ default: (defaults) => defaults.scriptHeapSizeMB ?? 256,
14139
14113
  liveUpdatable: false,
14140
14114
  description: "QuickJS heap ceiling per script host, MB \u2014 boot-pinned: the host is sized once."
14141
14115
  },
14142
14116
  scriptTimeout: {
14143
14117
  schema: z.number().int().min(1).max(6e5),
14144
- default: (env) => envNumber(env, "CAJONZERO_SCRIPT_TIMEOUT_MS") ?? 1e4,
14118
+ default: (defaults) => defaults.scriptTimeout ?? 1e4,
14145
14119
  liveUpdatable: false,
14146
14120
  description: "Per-invocation script timeout, ms \u2014 boot-pinned: baked into the host config."
14147
14121
  },
14148
14122
  dbSizeLimit: {
14149
14123
  schema: z.number().int().min(0).max(Number.MAX_SAFE_INTEGER),
14150
- default: (env) => envNumber(env, "CAJONZERO_DB_SIZE_LIMIT") ?? 128 * MEBIBYTE,
14124
+ default: (defaults) => defaults.dbSizeLimit ?? 128 * MEBIBYTE,
14151
14125
  liveUpdatable: false,
14152
14126
  description: "Map-script storage byte budget \u2014 boot-pinned: quota accounting is captured at boot."
14153
14127
  },
14154
14128
  env: {
14155
14129
  schema: z.string().min(1).max(64),
14156
- default: (env) => envString(env, "CAJONZERO_ENV") ?? "development",
14130
+ default: (defaults) => defaults.env ?? "development",
14157
14131
  liveUpdatable: false,
14158
14132
  description: "Deployment label \u2014 boot-pinned: identifies the running deployment, never changes live."
14159
14133
  }
@@ -14174,15 +14148,15 @@ var SettingsError = class extends Error {
14174
14148
  this.key = key;
14175
14149
  }
14176
14150
  };
14177
- function effective(key, raw, env) {
14151
+ function effective(key, raw, defaults) {
14178
14152
  const definition = SETTINGS[key];
14179
- if (raw === void 0) return definition.default(env);
14153
+ if (raw === void 0) return definition.default(defaults);
14180
14154
  const parsed = definition.schema.safeParse(raw);
14181
- return parsed.success ? parsed.data : definition.default(env);
14155
+ return parsed.success ? parsed.data : definition.default(defaults);
14182
14156
  }
14183
14157
  function createSettingsService(options) {
14184
14158
  const repo = options.repo;
14185
- const env = options.env ?? {};
14159
+ const defaults = options.defaults ?? {};
14186
14160
  const service = {
14187
14161
  keys: SETTING_KEYS,
14188
14162
  isLiveUpdatable(key) {
@@ -14191,21 +14165,21 @@ function createSettingsService(options) {
14191
14165
  async getSettings() {
14192
14166
  const persisted = await repo.list();
14193
14167
  return {
14194
- playerLimit: effective("playerLimit", persisted.playerLimit, env),
14195
- zeroPlayerTimeout: effective("zeroPlayerTimeout", persisted.zeroPlayerTimeout, env),
14196
- autoSaveInterval: effective("autoSaveInterval", persisted.autoSaveInterval, env),
14197
- debug: effective("debug", persisted.debug, env),
14198
- teleportAccess: effective("teleportAccess", persisted.teleportAccess, env),
14199
- paidAccess: effective("paidAccess", persisted.paidAccess, env),
14200
- scriptHeapSizeMB: effective("scriptHeapSizeMB", persisted.scriptHeapSizeMB, env),
14201
- scriptTimeout: effective("scriptTimeout", persisted.scriptTimeout, env),
14202
- dbSizeLimit: effective("dbSizeLimit", persisted.dbSizeLimit, env),
14203
- env: effective("env", persisted.env, env)
14168
+ playerLimit: effective("playerLimit", persisted.playerLimit, defaults),
14169
+ zeroPlayerTimeout: effective("zeroPlayerTimeout", persisted.zeroPlayerTimeout, defaults),
14170
+ autoSaveInterval: effective("autoSaveInterval", persisted.autoSaveInterval, defaults),
14171
+ debug: effective("debug", persisted.debug, defaults),
14172
+ teleportAccess: effective("teleportAccess", persisted.teleportAccess, defaults),
14173
+ paidAccess: effective("paidAccess", persisted.paidAccess, defaults),
14174
+ scriptHeapSizeMB: effective("scriptHeapSizeMB", persisted.scriptHeapSizeMB, defaults),
14175
+ scriptTimeout: effective("scriptTimeout", persisted.scriptTimeout, defaults),
14176
+ dbSizeLimit: effective("dbSizeLimit", persisted.dbSizeLimit, defaults),
14177
+ env: effective("env", persisted.env, defaults)
14204
14178
  };
14205
14179
  },
14206
14180
  async getSetting(key) {
14207
14181
  const raw = await repo.get(String(key));
14208
- return effective(key, raw, env);
14182
+ return effective(key, raw, defaults);
14209
14183
  },
14210
14184
  async updateSettings(patch) {
14211
14185
  const entries = Object.entries(patch);
@@ -15619,15 +15593,10 @@ function isStore(value) {
15619
15593
  return typeof value.mount === "function";
15620
15594
  }
15621
15595
  function resolveStore(env) {
15622
- if (storageFactory !== void 0) {
15623
- const source = storageFactory(env);
15624
- if (source === void 0) return void 0;
15625
- return isStore(source) ? source : createStore(source);
15626
- }
15627
- const kv = env.CAJONZERO_KV;
15628
- if (kv === void 0) return void 0;
15629
- configureDefaultKV(kv);
15630
- return createStore();
15596
+ if (storageFactory === void 0) return void 0;
15597
+ const source = storageFactory(env);
15598
+ if (source === void 0) return void 0;
15599
+ return isStore(source) ? source : createStore(source);
15631
15600
  }
15632
15601
  function memoryDriver() {
15633
15602
  return memoryDriverFactory();
@@ -15684,6 +15653,33 @@ function lazyCloudflareKvDriver() {
15684
15653
  };
15685
15654
  }
15686
15655
 
15656
+ // src/core/runtime/providers.ts
15657
+ var providers = {};
15658
+ function setRuntimeProviders(next) {
15659
+ providers = next;
15660
+ }
15661
+ function getRuntimeProviders() {
15662
+ return providers;
15663
+ }
15664
+ function resolveSessionSecret(env) {
15665
+ return providers.sessionSecret?.(env);
15666
+ }
15667
+ function resolveAdminToken(env) {
15668
+ return providers.adminToken?.(env);
15669
+ }
15670
+ function resolveEnvironment(env) {
15671
+ return providers.environment?.(env);
15672
+ }
15673
+ function resolveMapSeedKv(env) {
15674
+ return providers.mapSeedKv?.(env);
15675
+ }
15676
+ function resolveRooms(env) {
15677
+ return providers.rooms?.(env);
15678
+ }
15679
+ function resolveSettingDefaults(env) {
15680
+ return providers.settings?.(env);
15681
+ }
15682
+
15687
15683
  // src/core/storage/write-queue.ts
15688
15684
  function isQuotaError(error) {
15689
15685
  if (error === null || typeof error !== "object") return false;
@@ -16758,12 +16754,12 @@ var Room = class {
16758
16754
  this.manualClock = options.manualClock ?? false;
16759
16755
  this.createPair = options.createPair ?? defaultCreatePair;
16760
16756
  this.logger = options.logger ?? defaultLogger;
16761
- this.secret = options.secret ?? env.CAJONZERO_SESSION_SECRET;
16757
+ this.secret = options.secret ?? resolveSessionSecret(env);
16762
16758
  this.engine = options.engine ?? getRoomEngineFactory()?.() ?? createNullEngine();
16763
16759
  this.explicitScriptHost = options.scriptHost !== void 0;
16764
16760
  this.script = options.scriptHost ?? this.engine;
16765
16761
  this.mapBootstrap = options.map;
16766
- this.seedCacheKv = env.CAJONZERO_KV;
16762
+ this.seedCacheKv = resolveMapSeedKv(env);
16767
16763
  this.mapLoader = options.loadMapSeed ?? ((bootstrap) => this.loadSeed(bootstrap));
16768
16764
  this.registryTtlSeconds = options.registryTtlSeconds ?? DEFAULT_SESSION_SLOT_TTL_SECONDS;
16769
16765
  const banCacheTtlMs = options.banCacheTtlMs ?? DEFAULT_BAN_CACHE_TTL_MS2;
@@ -16773,7 +16769,10 @@ var Room = class {
16773
16769
  cacheTtlMs: banCacheTtlMs
16774
16770
  }) : void 0);
16775
16771
  this.registry = options.registry ?? (store !== void 0 ? createSessionRegistry(store, { now: this.now, ttlSeconds: this.registryTtlSeconds }) : void 0);
16776
- this.settings = options.settings ?? (store !== void 0 ? createSettingsService({ repo: createRepos(store, { now: this.now }).settings, env }) : void 0);
16772
+ this.settings = options.settings ?? (store !== void 0 ? createSettingsService({
16773
+ repo: createRepos(store, { now: this.now }).settings,
16774
+ defaults: resolveSettingDefaults(env)
16775
+ }) : void 0);
16777
16776
  this.net = createNetServerState(0);
16778
16777
  this.codec = new NetCodec({
16779
16778
  logger: { error: (message) => this.logger.error(message) }
@@ -17913,6 +17912,6 @@ function resolveConfig(input) {
17913
17912
  // src/core.ts
17914
17913
  var CORE_ENTRYPOINT = "cajonzero/core";
17915
17914
 
17916
- export { CORE_ENTRYPOINT, DEFAULT_CONFIG, EMPTY_MAP_CATALOG, GAME_NET_INBOUND_BASE, GAME_NET_OUTBOUND_BASE, HttpMapSource, INBOUND_LAYOUT, MAX_INBOUND_FRAME_BYTES, MapSourceError, NetCodec, NetCodecError, OFFICIAL_PREFIX, OUTBOUND_LAYOUT, Room, SETTINGS, SETTING_KEYS, SettingsError, chainLayouts, configureDefaultKV, createMemoryStore, createRepos, createSettingsService, createStore, findMap, isSettingKey, layoutMessages, layoutProtocol, loadWorldSeedFromHttp, matchesLayout, memoryDriver, messageIds, messageNameAt, parseMapCatalog, readNetFrameHeader, readNetMessageType, resolveConfig, resolveStore, setStorageFactory };
17915
+ export { CORE_ENTRYPOINT, DEFAULT_CONFIG, EMPTY_MAP_CATALOG, GAME_NET_INBOUND_BASE, GAME_NET_OUTBOUND_BASE, HttpMapSource, INBOUND_LAYOUT, MAX_INBOUND_FRAME_BYTES, MapSourceError, NetCodec, NetCodecError, OFFICIAL_PREFIX, OUTBOUND_LAYOUT, Room, SETTINGS, SETTING_KEYS, SettingsError, chainLayouts, configureDefaultKV, createMemoryStore, createRepos, createSettingsService, createStore, findMap, getRuntimeProviders, isSettingKey, layoutMessages, layoutProtocol, loadWorldSeedFromHttp, matchesLayout, memoryDriver, messageIds, messageNameAt, parseMapCatalog, readNetFrameHeader, readNetMessageType, resolveAdminToken, resolveConfig, resolveEnvironment, resolveMapSeedKv, resolveRooms, resolveSessionSecret, resolveSettingDefaults, resolveStore, setRuntimeProviders, setStorageFactory };
17917
17916
  //# sourceMappingURL=core.js.map
17918
17917
  //# sourceMappingURL=core.js.map