cajonzero 0.1.2 → 0.1.3

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/dist/serve.js CHANGED
@@ -5534,147 +5534,6 @@ async function persistSession(sessions, input, now = Date.now()) {
5534
5534
  return record;
5535
5535
  }
5536
5536
 
5537
- // src/core/maps/catalog.ts
5538
- var MAP_CATALOG_VERSION = 1;
5539
- var SLUG_RE = /^[a-z0-9][a-z0-9-_]*$/;
5540
- var MAX_SLUG_LENGTH = 63;
5541
- var WORLD_ID_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
5542
- var EMPTY_MAP_CATALOG = { version: MAP_CATALOG_VERSION, maps: [] };
5543
- var MapCatalogError = class extends Error {
5544
- code;
5545
- constructor(code, message) {
5546
- super(message);
5547
- this.name = "MapCatalogError";
5548
- this.code = code;
5549
- }
5550
- };
5551
- function fail(code, message) {
5552
- throw new MapCatalogError(code, message);
5553
- }
5554
- function isMapSlug(value) {
5555
- return value.length > 0 && value.length <= MAX_SLUG_LENGTH && SLUG_RE.test(value);
5556
- }
5557
- function assertMapSlug(slug) {
5558
- if (!isMapSlug(slug)) fail("INVALID_SLUG", `invalid map slug "${slug}"`);
5559
- return slug;
5560
- }
5561
- function assertWorldId(worldId) {
5562
- if (!WORLD_ID_RE.test(worldId) || worldId === "." || worldId === "..") {
5563
- fail("INVALID_PATH", `invalid world id "${worldId}"`);
5564
- }
5565
- return worldId;
5566
- }
5567
- function normalizeRootUrl(raw) {
5568
- if (typeof raw !== "string" || raw.trim().length === 0) {
5569
- fail("INVALID_ROOT", "map root must be a non-empty URL");
5570
- }
5571
- let url;
5572
- try {
5573
- url = new URL(raw.trim());
5574
- } catch {
5575
- fail("INVALID_ROOT", `map root is not a valid URL: ${raw}`);
5576
- }
5577
- if (url.protocol !== "http:" && url.protocol !== "https:") {
5578
- fail("INVALID_ROOT", `map root must be http(s): ${raw}`);
5579
- }
5580
- url.hash = "";
5581
- if (!url.pathname.endsWith("/")) {
5582
- const lastSegment = url.pathname.split("/").pop() ?? "";
5583
- if (/\.[A-Za-z0-9]+$/.test(lastSegment)) {
5584
- fail(
5585
- "INVALID_ROOT",
5586
- `map root must be the unpacked map directory, not a file ("${lastSegment}"); use its parent directory ending in "/"`
5587
- );
5588
- }
5589
- url.pathname += "/";
5590
- }
5591
- return url.toString();
5592
- }
5593
- function assertRelativeMapPath(relPath) {
5594
- if (relPath.length === 0 || relPath.includes("\0") || relPath.includes("\\")) {
5595
- fail("INVALID_PATH", `invalid map path "${relPath}"`);
5596
- }
5597
- if (relPath.startsWith("/")) fail("INVALID_PATH", `absolute map path "${relPath}"`);
5598
- const parts2 = [];
5599
- for (const part of relPath.split("/")) {
5600
- if (part === "" || part === ".") continue;
5601
- if (part === "..") fail("INVALID_PATH", `map path escapes the root: "${relPath}"`);
5602
- parts2.push(part);
5603
- }
5604
- if (parts2.length === 0) fail("INVALID_PATH", "empty map path");
5605
- return parts2.join("/");
5606
- }
5607
- function mapFilePath(root, relPath) {
5608
- const base = new URL(normalizeRootUrl(root));
5609
- const relative = assertRelativeMapPath(relPath);
5610
- const url = new URL(relative, base);
5611
- if (url.origin !== base.origin || !url.pathname.startsWith(base.pathname)) {
5612
- fail("INVALID_PATH", `map path escapes the root: "${relPath}"`);
5613
- }
5614
- if (base.search.length > 0) url.search = base.search;
5615
- url.hash = "";
5616
- return url.toString();
5617
- }
5618
- function optionalString(value, label, code = "INVALID_MAP") {
5619
- if (value === void 0) return void 0;
5620
- if (typeof value !== "string" || value.trim().length === 0) {
5621
- fail(code, `${label} must be a non-empty string`);
5622
- }
5623
- return value.trim();
5624
- }
5625
- function parseEntry(value, index) {
5626
- if (typeof value !== "object" || value === null || Array.isArray(value)) {
5627
- fail("INVALID_MAP", `maps[${index}] must be an object`);
5628
- }
5629
- const record = value;
5630
- const slug = optionalString(record.slug, `maps[${index}].slug`, "INVALID_SLUG");
5631
- if (slug === void 0) fail("INVALID_SLUG", `maps[${index}].slug is required`);
5632
- assertMapSlug(slug);
5633
- const rootRaw = optionalString(record.root, `maps[${index}].root`, "INVALID_ROOT");
5634
- if (rootRaw === void 0) fail("INVALID_ROOT", `maps[${slug}].root is required`);
5635
- const root = normalizeRootUrl(rootRaw);
5636
- const worldId = optionalString(record.worldId, `maps[${index}].worldId`);
5637
- const assetRoot = optionalString(record.assetRoot, `maps[${index}].assetRoot`, "INVALID_ROOT");
5638
- const name = optionalString(record.name, `maps[${index}].name`);
5639
- const author = optionalString(record.author, `maps[${index}].author`);
5640
- const version = optionalString(record.version, `maps[${index}].version`);
5641
- const cover = optionalString(record.cover, `maps[${index}].cover`);
5642
- return {
5643
- slug,
5644
- root,
5645
- ...worldId !== void 0 ? { worldId: assertWorldId(worldId) } : {},
5646
- ...name !== void 0 ? { name } : {},
5647
- ...author !== void 0 ? { author } : {},
5648
- ...version !== void 0 ? { version } : {},
5649
- ...cover !== void 0 ? { cover } : {},
5650
- ...assetRoot !== void 0 ? { assetRoot: normalizeRootUrl(assetRoot) } : {}
5651
- };
5652
- }
5653
- function parseMapCatalog(input) {
5654
- if (input === void 0 || input === null) return EMPTY_MAP_CATALOG;
5655
- if (typeof input !== "object" || Array.isArray(input)) {
5656
- fail("INVALID_CATALOG", "maps.json must be an object");
5657
- }
5658
- const record = input;
5659
- const version = record.version ?? MAP_CATALOG_VERSION;
5660
- if (version !== MAP_CATALOG_VERSION) {
5661
- fail("INVALID_CATALOG", `unsupported maps.json version ${String(version)}`);
5662
- }
5663
- if (!Array.isArray(record.maps)) {
5664
- fail("INVALID_CATALOG", "maps.json `maps` must be an array");
5665
- }
5666
- const maps = record.maps.map((entry, index) => parseEntry(entry, index));
5667
- const seen = /* @__PURE__ */ new Set();
5668
- for (const map of maps) {
5669
- if (seen.has(map.slug)) fail("DUPLICATE_SLUG", `duplicate map slug "${map.slug}"`);
5670
- seen.add(map.slug);
5671
- }
5672
- return { version: MAP_CATALOG_VERSION, maps };
5673
- }
5674
- function findMap(catalog, slug) {
5675
- return catalog.maps.find((entry) => entry.slug === slug);
5676
- }
5677
-
5678
5537
  // src/vendor/mudb/box-schema/backend/database-type.ts
5679
5538
  var DB;
5680
5539
  ((DB2) => {
@@ -14279,16 +14138,268 @@ function decodeBlockInfo(bytes) {
14279
14138
  return null;
14280
14139
  }
14281
14140
  }
14282
- async function loadBlockInfo(reader, worldId, hash = BLOCK_INFO_HASH) {
14141
+ async function readBlockInfoBytes(reader, worldId, hash = BLOCK_INFO_HASH) {
14283
14142
  try {
14284
14143
  const bytes = await reader.read(`worlds/${worldId}/data/blobs/${hash}`);
14285
14144
  if (bytes === null || bytes.length === 0) return null;
14286
- return decodeBlockInfo(bytes);
14145
+ return bytes;
14287
14146
  } catch {
14288
14147
  return null;
14289
14148
  }
14290
14149
  }
14291
14150
 
14151
+ // src/core/maps/seed-pack.ts
14152
+ var SEED_PACK_VERSION = 1;
14153
+ var SEED_PACK_FORMAT = "cajonzero-seed";
14154
+ var SEED_PACK_FILES = ["seed.json.gz", "seed.json"];
14155
+ function isGzip(bytes) {
14156
+ return bytes.length >= 2 && bytes[0] === 31 && bytes[1] === 139;
14157
+ }
14158
+ async function gunzipBytes(bytes) {
14159
+ if (typeof DecompressionStream === "undefined") {
14160
+ throw new Error("this runtime does not support gzip seed packs");
14161
+ }
14162
+ const buffer = bytes.buffer.slice(
14163
+ bytes.byteOffset,
14164
+ bytes.byteOffset + bytes.byteLength
14165
+ );
14166
+ const stream = new Blob([buffer]).stream().pipeThrough(new DecompressionStream("gzip"));
14167
+ return new Uint8Array(await new Response(stream).arrayBuffer());
14168
+ }
14169
+ async function decodeSeedPackAuto(bytes) {
14170
+ return decodeSeedPack(isGzip(bytes) ? await gunzipBytes(bytes) : bytes);
14171
+ }
14172
+ async function loadSeedPackFromAssets(assets, slug, worldId) {
14173
+ if (assets === void 0) return null;
14174
+ for (const file of SEED_PACK_FILES) {
14175
+ const suffix = file.slice("seed".length);
14176
+ const path = `/seed/${encodeURIComponent(slug)}${suffix}`;
14177
+ try {
14178
+ const response = await assets.fetch(new Request(`https://cajonzero.internal${path}`));
14179
+ if (!response.ok) continue;
14180
+ const bytes = new Uint8Array(await response.arrayBuffer());
14181
+ const seed = await decodeSeedPackAuto(bytes);
14182
+ if (worldId !== void 0 && worldId.length > 0 && seed.mapId !== worldId) continue;
14183
+ return seed;
14184
+ } catch {
14185
+ }
14186
+ }
14187
+ return null;
14188
+ }
14189
+ function base64ToBytes(text) {
14190
+ const binary = atob(text);
14191
+ const out = new Uint8Array(binary.length);
14192
+ for (let i = 0; i < binary.length; i += 1) out[i] = binary.charCodeAt(i);
14193
+ return out;
14194
+ }
14195
+ function isRecord2(value) {
14196
+ return typeof value === "object" && value !== null && !Array.isArray(value);
14197
+ }
14198
+ function decodeSeedPack(bytes) {
14199
+ let parsed;
14200
+ try {
14201
+ parsed = JSON.parse(new TextDecoder().decode(bytes));
14202
+ } catch {
14203
+ throw new Error("seed pack is not valid JSON");
14204
+ }
14205
+ if (!isRecord2(parsed) || parsed["format"] !== SEED_PACK_FORMAT) {
14206
+ throw new Error("not a cajonzero seed pack");
14207
+ }
14208
+ if (parsed["version"] !== SEED_PACK_VERSION) {
14209
+ throw new Error(
14210
+ `unsupported seed pack version ${String(parsed["version"])} (expected ${SEED_PACK_VERSION})`
14211
+ );
14212
+ }
14213
+ if (typeof parsed["mapId"] !== "string" || parsed["mapId"].length === 0) {
14214
+ throw new Error("seed pack is missing mapId");
14215
+ }
14216
+ if (!isRecord2(parsed["seed"])) {
14217
+ throw new Error("seed pack is missing the seed payload");
14218
+ }
14219
+ const rawSeed = parsed["seed"];
14220
+ if (rawSeed["version"] !== WORLD_SEED_VERSION) {
14221
+ throw new Error(
14222
+ `seed pack was built for world-seed v${String(rawSeed["version"])} (this runtime expects v${WORLD_SEED_VERSION}); re-run cj0 maps pack`
14223
+ );
14224
+ }
14225
+ const rawVoxels = rawSeed["voxels"];
14226
+ let voxels = null;
14227
+ if (rawVoxels !== null) {
14228
+ if (!isRecord2(rawVoxels)) throw new Error("seed pack voxels are malformed");
14229
+ const blobs = rawVoxels["chunkBlobs"];
14230
+ if (!Array.isArray(blobs)) throw new Error("seed pack voxels are malformed");
14231
+ const rawShape = rawVoxels["shape"];
14232
+ const shape = isRecord2(rawShape) ? {
14233
+ x: Number(rawShape["x"]) || 0,
14234
+ y: Number(rawShape["y"]) || 0,
14235
+ z: Number(rawShape["z"]) || 0
14236
+ } : { x: 0, y: 0, z: 0 };
14237
+ const hashes = rawVoxels["chunkHashes"];
14238
+ voxels = {
14239
+ shape,
14240
+ chunkCount: Number(rawVoxels["chunkCount"]) || blobs.length,
14241
+ chunkBlobs: blobs.map(
14242
+ (blob) => typeof blob === "string" ? base64ToBytes(blob) : new Uint8Array(0)
14243
+ ),
14244
+ chunkHashes: Array.isArray(hashes) ? hashes.map((hash) => typeof hash === "string" ? hash : "") : []
14245
+ };
14246
+ }
14247
+ const { voxels: _ignored, ...rest } = rawSeed;
14248
+ const seed = {
14249
+ ...rest,
14250
+ mapId: parsed["mapId"],
14251
+ voxels
14252
+ };
14253
+ if (typeof parsed["blockInfo"] === "string" && parsed["blockInfo"].length > 0) {
14254
+ const blockInfo = decodeBlockInfo(base64ToBytes(parsed["blockInfo"]));
14255
+ if (blockInfo !== null) seed.blockInfo = blockInfo;
14256
+ }
14257
+ return seed;
14258
+ }
14259
+
14260
+ // src/core/maps/catalog.ts
14261
+ var MAP_CATALOG_VERSION = 1;
14262
+ var SLUG_RE = /^[a-z0-9][a-z0-9-_]*$/;
14263
+ var MAX_SLUG_LENGTH = 63;
14264
+ var WORLD_ID_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
14265
+ var EMPTY_MAP_CATALOG = { version: MAP_CATALOG_VERSION, maps: [] };
14266
+ var MapCatalogError = class extends Error {
14267
+ code;
14268
+ constructor(code, message) {
14269
+ super(message);
14270
+ this.name = "MapCatalogError";
14271
+ this.code = code;
14272
+ }
14273
+ };
14274
+ function fail(code, message) {
14275
+ throw new MapCatalogError(code, message);
14276
+ }
14277
+ function isMapSlug(value) {
14278
+ return value.length > 0 && value.length <= MAX_SLUG_LENGTH && SLUG_RE.test(value);
14279
+ }
14280
+ function assertMapSlug(slug) {
14281
+ if (!isMapSlug(slug)) fail("INVALID_SLUG", `invalid map slug "${slug}"`);
14282
+ return slug;
14283
+ }
14284
+ function assertWorldId(worldId) {
14285
+ if (!WORLD_ID_RE.test(worldId) || worldId === "." || worldId === "..") {
14286
+ fail("INVALID_PATH", `invalid world id "${worldId}"`);
14287
+ }
14288
+ return worldId;
14289
+ }
14290
+ function normalizeRootUrl(raw) {
14291
+ if (typeof raw !== "string" || raw.trim().length === 0) {
14292
+ fail("INVALID_ROOT", "map root must be a non-empty URL");
14293
+ }
14294
+ let url;
14295
+ try {
14296
+ url = new URL(raw.trim());
14297
+ } catch {
14298
+ fail("INVALID_ROOT", `map root is not a valid URL: ${raw}`);
14299
+ }
14300
+ if (url.protocol !== "http:" && url.protocol !== "https:") {
14301
+ fail("INVALID_ROOT", `map root must be http(s): ${raw}`);
14302
+ }
14303
+ url.hash = "";
14304
+ if (!url.pathname.endsWith("/")) {
14305
+ const lastSegment = url.pathname.split("/").pop() ?? "";
14306
+ if (/\.[A-Za-z0-9]+$/.test(lastSegment)) {
14307
+ fail(
14308
+ "INVALID_ROOT",
14309
+ `map root must be the unpacked map directory, not a file ("${lastSegment}"); use its parent directory ending in "/"`
14310
+ );
14311
+ }
14312
+ url.pathname += "/";
14313
+ }
14314
+ return url.toString();
14315
+ }
14316
+ function assertRelativeMapPath(relPath) {
14317
+ if (relPath.length === 0 || relPath.includes("\0") || relPath.includes("\\")) {
14318
+ fail("INVALID_PATH", `invalid map path "${relPath}"`);
14319
+ }
14320
+ if (relPath.startsWith("/")) fail("INVALID_PATH", `absolute map path "${relPath}"`);
14321
+ const parts2 = [];
14322
+ for (const part of relPath.split("/")) {
14323
+ if (part === "" || part === ".") continue;
14324
+ if (part === "..") fail("INVALID_PATH", `map path escapes the root: "${relPath}"`);
14325
+ parts2.push(part);
14326
+ }
14327
+ if (parts2.length === 0) fail("INVALID_PATH", "empty map path");
14328
+ return parts2.join("/");
14329
+ }
14330
+ function mapFilePath(root, relPath) {
14331
+ const base = new URL(normalizeRootUrl(root));
14332
+ const relative = assertRelativeMapPath(relPath);
14333
+ const url = new URL(relative, base);
14334
+ if (url.origin !== base.origin || !url.pathname.startsWith(base.pathname)) {
14335
+ fail("INVALID_PATH", `map path escapes the root: "${relPath}"`);
14336
+ }
14337
+ if (base.search.length > 0) url.search = base.search;
14338
+ url.hash = "";
14339
+ return url.toString();
14340
+ }
14341
+ function optionalString(value, label, code = "INVALID_MAP") {
14342
+ if (value === void 0) return void 0;
14343
+ if (typeof value !== "string" || value.trim().length === 0) {
14344
+ fail(code, `${label} must be a non-empty string`);
14345
+ }
14346
+ return value.trim();
14347
+ }
14348
+ function parseEntry(value, index) {
14349
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
14350
+ fail("INVALID_MAP", `maps[${index}] must be an object`);
14351
+ }
14352
+ const record = value;
14353
+ const slug = optionalString(record.slug, `maps[${index}].slug`, "INVALID_SLUG");
14354
+ if (slug === void 0) fail("INVALID_SLUG", `maps[${index}].slug is required`);
14355
+ assertMapSlug(slug);
14356
+ const rootRaw = optionalString(record.root, `maps[${index}].root`, "INVALID_ROOT");
14357
+ if (rootRaw === void 0) fail("INVALID_ROOT", `maps[${slug}].root is required`);
14358
+ const root = normalizeRootUrl(rootRaw);
14359
+ const worldId = optionalString(record.worldId, `maps[${index}].worldId`);
14360
+ const assetRoot = optionalString(record.assetRoot, `maps[${index}].assetRoot`, "INVALID_ROOT");
14361
+ const name = optionalString(record.name, `maps[${index}].name`);
14362
+ const author = optionalString(record.author, `maps[${index}].author`);
14363
+ const version = optionalString(record.version, `maps[${index}].version`);
14364
+ const etag = optionalString(record.etag, `maps[${index}].etag`);
14365
+ const cover = optionalString(record.cover, `maps[${index}].cover`);
14366
+ return {
14367
+ slug,
14368
+ root,
14369
+ ...worldId !== void 0 ? { worldId: assertWorldId(worldId) } : {},
14370
+ ...name !== void 0 ? { name } : {},
14371
+ ...author !== void 0 ? { author } : {},
14372
+ ...version !== void 0 ? { version } : {},
14373
+ ...etag !== void 0 ? { etag } : {},
14374
+ ...cover !== void 0 ? { cover } : {},
14375
+ ...assetRoot !== void 0 ? { assetRoot: normalizeRootUrl(assetRoot) } : {}
14376
+ };
14377
+ }
14378
+ function parseMapCatalog(input) {
14379
+ if (input === void 0 || input === null) return EMPTY_MAP_CATALOG;
14380
+ if (typeof input !== "object" || Array.isArray(input)) {
14381
+ fail("INVALID_CATALOG", "maps.json must be an object");
14382
+ }
14383
+ const record = input;
14384
+ const version = record.version ?? MAP_CATALOG_VERSION;
14385
+ if (version !== MAP_CATALOG_VERSION) {
14386
+ fail("INVALID_CATALOG", `unsupported maps.json version ${String(version)}`);
14387
+ }
14388
+ if (!Array.isArray(record.maps)) {
14389
+ fail("INVALID_CATALOG", "maps.json `maps` must be an array");
14390
+ }
14391
+ const maps = record.maps.map((entry, index) => parseEntry(entry, index));
14392
+ const seen = /* @__PURE__ */ new Set();
14393
+ for (const map of maps) {
14394
+ if (seen.has(map.slug)) fail("DUPLICATE_SLUG", `duplicate map slug "${map.slug}"`);
14395
+ seen.add(map.slug);
14396
+ }
14397
+ return { version: MAP_CATALOG_VERSION, maps };
14398
+ }
14399
+ function findMap(catalog, slug) {
14400
+ return catalog.maps.find((entry) => entry.slug === slug);
14401
+ }
14402
+
14292
14403
  // src/core/maps/http.ts
14293
14404
  var MapSourceError = class extends Error {
14294
14405
  code;
@@ -14391,14 +14502,14 @@ var HttpMapSource = class {
14391
14502
  return new Uint8Array(buffer);
14392
14503
  }
14393
14504
  };
14394
- function isRecord2(value) {
14505
+ function isRecord3(value) {
14395
14506
  return typeof value === "object" && value !== null && !Array.isArray(value);
14396
14507
  }
14397
14508
  async function resolveWorldId(source) {
14398
14509
  const bundle = await source.readJson("bundle.json");
14399
14510
  if (bundle === null) fail2("MAP_NO_WORLD", "bundle.json is missing or not JSON");
14400
14511
  const members = Array.isArray(bundle.members) ? bundle.members : [];
14401
- const records = members.filter(isRecord2);
14512
+ const records = members.filter(isRecord3);
14402
14513
  const main = records.find((member) => member.mapRole === "main") ?? records[0];
14403
14514
  const candidate = (main !== void 0 && typeof main.id === "string" ? main.id : void 0) ?? (typeof bundle.mainProjectId === "string" ? bundle.mainProjectId : void 0);
14404
14515
  if (candidate === void 0) {
@@ -14407,6 +14518,13 @@ async function resolveWorldId(source) {
14407
14518
  return assertWorldId(candidate);
14408
14519
  }
14409
14520
  async function loadWorldSeedFromHttp(entry, options = {}) {
14521
+ if (options.preferSeedPack !== false) {
14522
+ const packed = await loadSeedPackFromHttp(entry, options);
14523
+ if (packed !== null) return packed;
14524
+ }
14525
+ return (await loadWorldSeedGraphFromHttp(entry, options)).seed;
14526
+ }
14527
+ async function loadWorldSeedGraphFromHttp(entry, options = {}) {
14410
14528
  const source = new HttpMapSource(entry.root, options);
14411
14529
  const worldId = entry.worldId ?? await resolveWorldId(source);
14412
14530
  const manifest = await source.readJson(
@@ -14415,13 +14533,35 @@ async function loadWorldSeedFromHttp(entry, options = {}) {
14415
14533
  if (manifest === null) {
14416
14534
  fail2("MAP_NO_WORLD", `worlds/${worldId}/manifest.json is missing or not JSON`);
14417
14535
  }
14418
- return resolveWorldSeed(
14536
+ const seed = await resolveWorldSeed(
14419
14537
  { worldId, manifest, reader: source },
14420
14538
  { concurrency: options.concurrency ?? DEFAULT_CONCURRENCY }
14421
- ).then(async (seed) => {
14422
- const blockInfo = await loadBlockInfo(source, worldId);
14423
- return blockInfo === null ? seed : { ...seed, blockInfo };
14424
- });
14539
+ );
14540
+ const blockInfoBytes = await readBlockInfoBytes(source, worldId) ?? void 0;
14541
+ const blockInfo = blockInfoBytes === void 0 ? null : decodeBlockInfo(blockInfoBytes);
14542
+ return {
14543
+ seed: blockInfo === null ? seed : { ...seed, blockInfo },
14544
+ ...blockInfoBytes !== void 0 ? { blockInfoBytes } : {}
14545
+ };
14546
+ }
14547
+ async function loadSeedPackFromHttp(entry, options) {
14548
+ const source = new HttpMapSource(entry.root, options);
14549
+ for (const file of SEED_PACK_FILES) {
14550
+ let bytes;
14551
+ try {
14552
+ bytes = await source.read(file);
14553
+ } catch {
14554
+ continue;
14555
+ }
14556
+ if (bytes === null) continue;
14557
+ try {
14558
+ const seed = await decodeSeedPackAuto(bytes);
14559
+ if (entry.worldId !== void 0 && seed.mapId !== entry.worldId) continue;
14560
+ return seed;
14561
+ } catch {
14562
+ }
14563
+ }
14564
+ return null;
14425
14565
  }
14426
14566
  var MEBIBYTE = 1024 * 1024;
14427
14567
  var ZERO_PLAYER_TIMEOUT_DEFAULT_MS = 48e4;
@@ -23576,6 +23716,8 @@ var Room = class {
23576
23716
  createPair;
23577
23717
  logger;
23578
23718
  secret;
23719
+ /** Worker static-assets binding, used to read seed packs bundled at deploy. */
23720
+ assets;
23579
23721
  engine;
23580
23722
  script;
23581
23723
  explicitScriptHost;
@@ -23638,6 +23780,10 @@ var Room = class {
23638
23780
  pendingEngineMap;
23639
23781
  /** Identity of the map the current engine was seeded from. */
23640
23782
  seededMap;
23783
+ /** Non-fatal seed warnings (missing blobs, skipped entities, …). */
23784
+ seedWarnings = [];
23785
+ /** Last map-load failure; cleared on a successful seed. */
23786
+ mapLoadError;
23641
23787
  heartbeatBeats = 0;
23642
23788
  ticksRun = 0;
23643
23789
  lastTickError;
@@ -23661,6 +23807,7 @@ var Room = class {
23661
23807
  this.createPair = options.createPair ?? defaultCreatePair;
23662
23808
  this.logger = options.logger ?? defaultLogger;
23663
23809
  this.secret = options.secret ?? resolveSessionSecret(env);
23810
+ this.assets = env.ASSETS;
23664
23811
  this.engine = options.engine ?? getRoomEngineFactory()?.() ?? createNullEngine();
23665
23812
  this.explicitScriptHost = options.scriptHost !== void 0;
23666
23813
  this.script = options.scriptHost ?? this.engine;
@@ -23755,7 +23902,9 @@ var Room = class {
23755
23902
  ok: true,
23756
23903
  slug: this.mapBootstrap?.slug ?? null,
23757
23904
  seeded: this.worldReady,
23758
- entities: this.engine.getReplica().entities.length
23905
+ entities: this.engine.getReplica().entities.length,
23906
+ mapLoadError: this.mapLoadError ?? null,
23907
+ warnings: this.seedWarnings
23759
23908
  }),
23760
23909
  { headers: { "content-type": "application/json", "cache-control": "no-store" } }
23761
23910
  );
@@ -23786,6 +23935,8 @@ var Room = class {
23786
23935
  secretPackets: this.secretPackets,
23787
23936
  clockPings: this.clockPings,
23788
23937
  inboundIds: Object.fromEntries(this.inboundIds),
23938
+ mapLoadError: this.mapLoadError ?? null,
23939
+ mapWarnings: this.seedWarnings,
23789
23940
  voxel: this.engine.voxelGeometry?.() ?? null,
23790
23941
  sampleEntity: (() => {
23791
23942
  const entity = this.engine.getReplica().entities[0];
@@ -23883,21 +24034,32 @@ var Room = class {
23883
24034
  this.engine = engine;
23884
24035
  if (!this.explicitScriptHost) this.script = engine;
23885
24036
  this.seededMap = { slug: bootstrap.slug, root: bootstrap.root, worldId: seed.mapId };
24037
+ this.seedWarnings = seed.warnings;
24038
+ this.mapLoadError = void 0;
24039
+ for (const warning of seed.warnings) {
24040
+ this.logger.log(`cajonzero: map "${bootstrap.slug}" warning: ${warning}`);
24041
+ }
23886
24042
  this.applyPendingEngineSnapshot();
23887
24043
  this.worldReady = true;
23888
24044
  this.logger.log(`cajonzero: seeded room "${bootstrap.slug}" from "${seed.mapId}"`);
23889
24045
  } catch (error) {
24046
+ this.mapLoadError = describeError2(error);
23890
24047
  this.logger.error(
23891
- `cajonzero: map load failed for "${bootstrap.slug}": ${describeError2(error)}`
24048
+ `cajonzero: map load failed for "${bootstrap.slug}": ${this.mapLoadError}`
23892
24049
  );
23893
24050
  }
23894
24051
  }
23895
24052
  loadSeed(bootstrap) {
23896
- return loadWorldSeedFromHttp(bootstrap, {
23897
- ...bootstrap.maxDownloadBytes !== void 0 ? { maxDownloadBytes: bootstrap.maxDownloadBytes } : {},
23898
- ...bootstrap.concurrency !== void 0 ? { concurrency: bootstrap.concurrency } : {},
23899
- ...bootstrap.requestTimeoutMs !== void 0 ? { requestTimeoutMs: bootstrap.requestTimeoutMs } : {}
23900
- });
24053
+ return loadSeedPackFromAssets(this.assets, bootstrap.slug, bootstrap.worldId).then(
24054
+ (packed) => {
24055
+ if (packed !== null) return packed;
24056
+ return loadWorldSeedFromHttp(bootstrap, {
24057
+ ...bootstrap.maxDownloadBytes !== void 0 ? { maxDownloadBytes: bootstrap.maxDownloadBytes } : {},
24058
+ ...bootstrap.concurrency !== void 0 ? { concurrency: bootstrap.concurrency } : {},
24059
+ ...bootstrap.requestTimeoutMs !== void 0 ? { requestTimeoutMs: bootstrap.requestTimeoutMs } : {}
24060
+ });
24061
+ }
24062
+ );
23901
24063
  }
23902
24064
  /**
23903
24065
  * Run the full connect path and return a structured outcome (no `Response`,
@@ -25713,7 +25875,7 @@ function createPlayEngine(options = {}) {
25713
25875
  const spawnOnClientInput = options.spawnOnClientInput ?? true;
25714
25876
  const scriptHost = options.scriptHost;
25715
25877
  const physicsOverride = options.physics ? buildPhysicsOverride(options.physics) : null;
25716
- const playerConfig = isRecord3(options.worldSeed?.player) ? options.worldSeed.player : null;
25878
+ const playerConfig = isRecord4(options.worldSeed?.player) ? options.worldSeed.player : null;
25717
25879
  let world = options.world ? NetStateSchema.clone(options.world) : options.worldSeed ? buildInitialWorld(options.worldSeed) : null;
25718
25880
  let currentTick = 0;
25719
25881
  let suspended = false;
@@ -26032,7 +26194,7 @@ function buildPhysicsOverride(override) {
26032
26194
  }
26033
26195
  return params;
26034
26196
  }
26035
- function isRecord3(value) {
26197
+ function isRecord4(value) {
26036
26198
  return typeof value === "object" && value !== null && !Array.isArray(value);
26037
26199
  }
26038
26200
  function finite(value) {
@@ -26090,7 +26252,7 @@ function spawnPlayerBody(id, config) {
26090
26252
  body.friction = PLAYER_FRICTION;
26091
26253
  body.restitution = PLAYER_RESTITUTION;
26092
26254
  body.mass = PLAYER_MASS;
26093
- const spawn = isRecord3(config?.initialPosition) ? config.initialPosition : null;
26255
+ const spawn = isRecord4(config?.initialPosition) ? config.initialPosition : null;
26094
26256
  body.px = finite(spawn?.x) ?? 0;
26095
26257
  body.py = finite(spawn?.y) ?? 0;
26096
26258
  body.pz = finite(spawn?.z) ?? 0;
@@ -26109,12 +26271,12 @@ function projectEnvironment(raw) {
26109
26271
  return environment;
26110
26272
  }
26111
26273
  function copyEnvironmentFields(target, source) {
26112
- if (!isRecord3(target)) return;
26274
+ if (!isRecord4(target)) return;
26113
26275
  for (const key of Object.keys(target)) {
26114
26276
  const value = source[key];
26115
26277
  if (value === void 0) continue;
26116
26278
  const current = target[key];
26117
- if (isRecord3(current) && isRecord3(value)) {
26279
+ if (isRecord4(current) && isRecord4(value)) {
26118
26280
  copyEnvironmentFields(current, value);
26119
26281
  continue;
26120
26282
  }
@@ -26843,7 +27005,7 @@ function mountMapRoutes(app, catalog) {
26843
27005
  }
26844
27006
 
26845
27007
  // src/version.ts
26846
- var VERSION = "0.1.1";
27008
+ var VERSION = "0.1.3";
26847
27009
 
26848
27010
  // src/core/server/room-gateway.ts
26849
27011
  var CONTAINER_PATTERN3 = /^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$/;