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/README.md +42 -3
- package/dist/bin/cj0.js +14243 -47
- package/dist/{config-BOzAyP7m.d.ts → config-B32NDXlr.d.ts} +1 -1
- package/dist/core.d.ts +15 -7
- package/dist/core.js +319 -157
- package/dist/index.d.ts +5 -5
- package/dist/index.js +184 -22
- package/dist/{providers-BX7e4pNW.d.ts → providers-BwJKzGkd.d.ts} +17 -0
- package/dist/scaffold.d.ts +67 -3
- package/dist/scaffold.js +293 -32
- package/dist/serve.d.ts +1 -1
- package/dist/serve.js +325 -163
- package/dist/ui/assets/index-CNHWg0_t.js +4404 -0
- package/dist/ui/assets/{main-BeABKxzY.js → main-DTcEV4JE.js} +16 -16
- package/dist/ui/index.html +1 -1
- package/package.json +4 -4
- package/dist/ui/assets/index-CaAHje8X.js +0 -4404
package/dist/index.js
CHANGED
|
@@ -11,7 +11,7 @@ import 'unstorage/drivers/memory';
|
|
|
11
11
|
|
|
12
12
|
// src/version.ts
|
|
13
13
|
var NAME = "cajonzero";
|
|
14
|
-
var VERSION = "0.1.
|
|
14
|
+
var VERSION = "0.1.3";
|
|
15
15
|
|
|
16
16
|
// src/core/config.ts
|
|
17
17
|
var ConfigError = class extends Error {
|
|
@@ -213,6 +213,7 @@ function parseEntry(value, index) {
|
|
|
213
213
|
const name = optionalString2(record.name, `maps[${index}].name`);
|
|
214
214
|
const author = optionalString2(record.author, `maps[${index}].author`);
|
|
215
215
|
const version = optionalString2(record.version, `maps[${index}].version`);
|
|
216
|
+
const etag = optionalString2(record.etag, `maps[${index}].etag`);
|
|
216
217
|
const cover = optionalString2(record.cover, `maps[${index}].cover`);
|
|
217
218
|
return {
|
|
218
219
|
slug,
|
|
@@ -221,6 +222,7 @@ function parseEntry(value, index) {
|
|
|
221
222
|
...name !== void 0 ? { name } : {},
|
|
222
223
|
...author !== void 0 ? { author } : {},
|
|
223
224
|
...version !== void 0 ? { version } : {},
|
|
225
|
+
...etag !== void 0 ? { etag } : {},
|
|
224
226
|
...cover !== void 0 ? { cover } : {},
|
|
225
227
|
...assetRoot !== void 0 ? { assetRoot: normalizeRootUrl(assetRoot) } : {}
|
|
226
228
|
};
|
|
@@ -16155,16 +16157,125 @@ function decodeBlockInfo(bytes) {
|
|
|
16155
16157
|
return null;
|
|
16156
16158
|
}
|
|
16157
16159
|
}
|
|
16158
|
-
async function
|
|
16160
|
+
async function readBlockInfoBytes(reader, worldId, hash = BLOCK_INFO_HASH) {
|
|
16159
16161
|
try {
|
|
16160
16162
|
const bytes = await reader.read(`worlds/${worldId}/data/blobs/${hash}`);
|
|
16161
16163
|
if (bytes === null || bytes.length === 0) return null;
|
|
16162
|
-
return
|
|
16164
|
+
return bytes;
|
|
16163
16165
|
} catch {
|
|
16164
16166
|
return null;
|
|
16165
16167
|
}
|
|
16166
16168
|
}
|
|
16167
16169
|
|
|
16170
|
+
// src/core/maps/seed-pack.ts
|
|
16171
|
+
var SEED_PACK_VERSION = 1;
|
|
16172
|
+
var SEED_PACK_FORMAT = "cajonzero-seed";
|
|
16173
|
+
var SEED_PACK_FILES = ["seed.json.gz", "seed.json"];
|
|
16174
|
+
function isGzip(bytes) {
|
|
16175
|
+
return bytes.length >= 2 && bytes[0] === 31 && bytes[1] === 139;
|
|
16176
|
+
}
|
|
16177
|
+
async function gunzipBytes(bytes) {
|
|
16178
|
+
if (typeof DecompressionStream === "undefined") {
|
|
16179
|
+
throw new Error("this runtime does not support gzip seed packs");
|
|
16180
|
+
}
|
|
16181
|
+
const buffer = bytes.buffer.slice(
|
|
16182
|
+
bytes.byteOffset,
|
|
16183
|
+
bytes.byteOffset + bytes.byteLength
|
|
16184
|
+
);
|
|
16185
|
+
const stream = new Blob([buffer]).stream().pipeThrough(new DecompressionStream("gzip"));
|
|
16186
|
+
return new Uint8Array(await new Response(stream).arrayBuffer());
|
|
16187
|
+
}
|
|
16188
|
+
async function decodeSeedPackAuto(bytes) {
|
|
16189
|
+
return decodeSeedPack(isGzip(bytes) ? await gunzipBytes(bytes) : bytes);
|
|
16190
|
+
}
|
|
16191
|
+
async function loadSeedPackFromAssets(assets, slug, worldId) {
|
|
16192
|
+
if (assets === void 0) return null;
|
|
16193
|
+
for (const file of SEED_PACK_FILES) {
|
|
16194
|
+
const suffix = file.slice("seed".length);
|
|
16195
|
+
const path = `/seed/${encodeURIComponent(slug)}${suffix}`;
|
|
16196
|
+
try {
|
|
16197
|
+
const response = await assets.fetch(new Request(`https://cajonzero.internal${path}`));
|
|
16198
|
+
if (!response.ok) continue;
|
|
16199
|
+
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
16200
|
+
const seed = await decodeSeedPackAuto(bytes);
|
|
16201
|
+
if (worldId !== void 0 && worldId.length > 0 && seed.mapId !== worldId) continue;
|
|
16202
|
+
return seed;
|
|
16203
|
+
} catch {
|
|
16204
|
+
}
|
|
16205
|
+
}
|
|
16206
|
+
return null;
|
|
16207
|
+
}
|
|
16208
|
+
function base64ToBytes(text) {
|
|
16209
|
+
const binary = atob(text);
|
|
16210
|
+
const out = new Uint8Array(binary.length);
|
|
16211
|
+
for (let i = 0; i < binary.length; i += 1) out[i] = binary.charCodeAt(i);
|
|
16212
|
+
return out;
|
|
16213
|
+
}
|
|
16214
|
+
function isRecord2(value) {
|
|
16215
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
16216
|
+
}
|
|
16217
|
+
function decodeSeedPack(bytes) {
|
|
16218
|
+
let parsed;
|
|
16219
|
+
try {
|
|
16220
|
+
parsed = JSON.parse(new TextDecoder().decode(bytes));
|
|
16221
|
+
} catch {
|
|
16222
|
+
throw new Error("seed pack is not valid JSON");
|
|
16223
|
+
}
|
|
16224
|
+
if (!isRecord2(parsed) || parsed["format"] !== SEED_PACK_FORMAT) {
|
|
16225
|
+
throw new Error("not a cajonzero seed pack");
|
|
16226
|
+
}
|
|
16227
|
+
if (parsed["version"] !== SEED_PACK_VERSION) {
|
|
16228
|
+
throw new Error(
|
|
16229
|
+
`unsupported seed pack version ${String(parsed["version"])} (expected ${SEED_PACK_VERSION})`
|
|
16230
|
+
);
|
|
16231
|
+
}
|
|
16232
|
+
if (typeof parsed["mapId"] !== "string" || parsed["mapId"].length === 0) {
|
|
16233
|
+
throw new Error("seed pack is missing mapId");
|
|
16234
|
+
}
|
|
16235
|
+
if (!isRecord2(parsed["seed"])) {
|
|
16236
|
+
throw new Error("seed pack is missing the seed payload");
|
|
16237
|
+
}
|
|
16238
|
+
const rawSeed = parsed["seed"];
|
|
16239
|
+
if (rawSeed["version"] !== WORLD_SEED_VERSION) {
|
|
16240
|
+
throw new Error(
|
|
16241
|
+
`seed pack was built for world-seed v${String(rawSeed["version"])} (this runtime expects v${WORLD_SEED_VERSION}); re-run cj0 maps pack`
|
|
16242
|
+
);
|
|
16243
|
+
}
|
|
16244
|
+
const rawVoxels = rawSeed["voxels"];
|
|
16245
|
+
let voxels = null;
|
|
16246
|
+
if (rawVoxels !== null) {
|
|
16247
|
+
if (!isRecord2(rawVoxels)) throw new Error("seed pack voxels are malformed");
|
|
16248
|
+
const blobs = rawVoxels["chunkBlobs"];
|
|
16249
|
+
if (!Array.isArray(blobs)) throw new Error("seed pack voxels are malformed");
|
|
16250
|
+
const rawShape = rawVoxels["shape"];
|
|
16251
|
+
const shape = isRecord2(rawShape) ? {
|
|
16252
|
+
x: Number(rawShape["x"]) || 0,
|
|
16253
|
+
y: Number(rawShape["y"]) || 0,
|
|
16254
|
+
z: Number(rawShape["z"]) || 0
|
|
16255
|
+
} : { x: 0, y: 0, z: 0 };
|
|
16256
|
+
const hashes = rawVoxels["chunkHashes"];
|
|
16257
|
+
voxels = {
|
|
16258
|
+
shape,
|
|
16259
|
+
chunkCount: Number(rawVoxels["chunkCount"]) || blobs.length,
|
|
16260
|
+
chunkBlobs: blobs.map(
|
|
16261
|
+
(blob) => typeof blob === "string" ? base64ToBytes(blob) : new Uint8Array(0)
|
|
16262
|
+
),
|
|
16263
|
+
chunkHashes: Array.isArray(hashes) ? hashes.map((hash) => typeof hash === "string" ? hash : "") : []
|
|
16264
|
+
};
|
|
16265
|
+
}
|
|
16266
|
+
const { voxels: _ignored, ...rest } = rawSeed;
|
|
16267
|
+
const seed = {
|
|
16268
|
+
...rest,
|
|
16269
|
+
mapId: parsed["mapId"],
|
|
16270
|
+
voxels
|
|
16271
|
+
};
|
|
16272
|
+
if (typeof parsed["blockInfo"] === "string" && parsed["blockInfo"].length > 0) {
|
|
16273
|
+
const blockInfo = decodeBlockInfo(base64ToBytes(parsed["blockInfo"]));
|
|
16274
|
+
if (blockInfo !== null) seed.blockInfo = blockInfo;
|
|
16275
|
+
}
|
|
16276
|
+
return seed;
|
|
16277
|
+
}
|
|
16278
|
+
|
|
16168
16279
|
// src/core/maps/http.ts
|
|
16169
16280
|
var MapSourceError = class extends Error {
|
|
16170
16281
|
code;
|
|
@@ -16267,14 +16378,14 @@ var HttpMapSource = class {
|
|
|
16267
16378
|
return new Uint8Array(buffer);
|
|
16268
16379
|
}
|
|
16269
16380
|
};
|
|
16270
|
-
function
|
|
16381
|
+
function isRecord3(value) {
|
|
16271
16382
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
16272
16383
|
}
|
|
16273
16384
|
async function resolveWorldId(source) {
|
|
16274
16385
|
const bundle = await source.readJson("bundle.json");
|
|
16275
16386
|
if (bundle === null) fail3("MAP_NO_WORLD", "bundle.json is missing or not JSON");
|
|
16276
16387
|
const members = Array.isArray(bundle.members) ? bundle.members : [];
|
|
16277
|
-
const records = members.filter(
|
|
16388
|
+
const records = members.filter(isRecord3);
|
|
16278
16389
|
const main = records.find((member) => member.mapRole === "main") ?? records[0];
|
|
16279
16390
|
const candidate = (main !== void 0 && typeof main.id === "string" ? main.id : void 0) ?? (typeof bundle.mainProjectId === "string" ? bundle.mainProjectId : void 0);
|
|
16280
16391
|
if (candidate === void 0) {
|
|
@@ -16283,6 +16394,13 @@ async function resolveWorldId(source) {
|
|
|
16283
16394
|
return assertWorldId(candidate);
|
|
16284
16395
|
}
|
|
16285
16396
|
async function loadWorldSeedFromHttp(entry, options = {}) {
|
|
16397
|
+
if (options.preferSeedPack !== false) {
|
|
16398
|
+
const packed = await loadSeedPackFromHttp(entry, options);
|
|
16399
|
+
if (packed !== null) return packed;
|
|
16400
|
+
}
|
|
16401
|
+
return (await loadWorldSeedGraphFromHttp(entry, options)).seed;
|
|
16402
|
+
}
|
|
16403
|
+
async function loadWorldSeedGraphFromHttp(entry, options = {}) {
|
|
16286
16404
|
const source = new HttpMapSource(entry.root, options);
|
|
16287
16405
|
const worldId = entry.worldId ?? await resolveWorldId(source);
|
|
16288
16406
|
const manifest = await source.readJson(
|
|
@@ -16291,13 +16409,35 @@ async function loadWorldSeedFromHttp(entry, options = {}) {
|
|
|
16291
16409
|
if (manifest === null) {
|
|
16292
16410
|
fail3("MAP_NO_WORLD", `worlds/${worldId}/manifest.json is missing or not JSON`);
|
|
16293
16411
|
}
|
|
16294
|
-
|
|
16412
|
+
const seed = await resolveWorldSeed(
|
|
16295
16413
|
{ worldId, manifest, reader: source },
|
|
16296
16414
|
{ concurrency: options.concurrency ?? DEFAULT_CONCURRENCY }
|
|
16297
|
-
)
|
|
16298
|
-
|
|
16299
|
-
|
|
16300
|
-
|
|
16415
|
+
);
|
|
16416
|
+
const blockInfoBytes = await readBlockInfoBytes(source, worldId) ?? void 0;
|
|
16417
|
+
const blockInfo = blockInfoBytes === void 0 ? null : decodeBlockInfo(blockInfoBytes);
|
|
16418
|
+
return {
|
|
16419
|
+
seed: blockInfo === null ? seed : { ...seed, blockInfo },
|
|
16420
|
+
...blockInfoBytes !== void 0 ? { blockInfoBytes } : {}
|
|
16421
|
+
};
|
|
16422
|
+
}
|
|
16423
|
+
async function loadSeedPackFromHttp(entry, options) {
|
|
16424
|
+
const source = new HttpMapSource(entry.root, options);
|
|
16425
|
+
for (const file of SEED_PACK_FILES) {
|
|
16426
|
+
let bytes;
|
|
16427
|
+
try {
|
|
16428
|
+
bytes = await source.read(file);
|
|
16429
|
+
} catch {
|
|
16430
|
+
continue;
|
|
16431
|
+
}
|
|
16432
|
+
if (bytes === null) continue;
|
|
16433
|
+
try {
|
|
16434
|
+
const seed = await decodeSeedPackAuto(bytes);
|
|
16435
|
+
if (entry.worldId !== void 0 && seed.mapId !== entry.worldId) continue;
|
|
16436
|
+
return seed;
|
|
16437
|
+
} catch {
|
|
16438
|
+
}
|
|
16439
|
+
}
|
|
16440
|
+
return null;
|
|
16301
16441
|
}
|
|
16302
16442
|
|
|
16303
16443
|
// src/core/net/framing.ts
|
|
@@ -24521,6 +24661,8 @@ var Room = class {
|
|
|
24521
24661
|
createPair;
|
|
24522
24662
|
logger;
|
|
24523
24663
|
secret;
|
|
24664
|
+
/** Worker static-assets binding, used to read seed packs bundled at deploy. */
|
|
24665
|
+
assets;
|
|
24524
24666
|
engine;
|
|
24525
24667
|
script;
|
|
24526
24668
|
explicitScriptHost;
|
|
@@ -24583,6 +24725,10 @@ var Room = class {
|
|
|
24583
24725
|
pendingEngineMap;
|
|
24584
24726
|
/** Identity of the map the current engine was seeded from. */
|
|
24585
24727
|
seededMap;
|
|
24728
|
+
/** Non-fatal seed warnings (missing blobs, skipped entities, …). */
|
|
24729
|
+
seedWarnings = [];
|
|
24730
|
+
/** Last map-load failure; cleared on a successful seed. */
|
|
24731
|
+
mapLoadError;
|
|
24586
24732
|
heartbeatBeats = 0;
|
|
24587
24733
|
ticksRun = 0;
|
|
24588
24734
|
lastTickError;
|
|
@@ -24606,6 +24752,7 @@ var Room = class {
|
|
|
24606
24752
|
this.createPair = options.createPair ?? defaultCreatePair;
|
|
24607
24753
|
this.logger = options.logger ?? defaultLogger;
|
|
24608
24754
|
this.secret = options.secret ?? resolveSessionSecret(env);
|
|
24755
|
+
this.assets = env.ASSETS;
|
|
24609
24756
|
this.engine = options.engine ?? getRoomEngineFactory()?.() ?? createNullEngine();
|
|
24610
24757
|
this.explicitScriptHost = options.scriptHost !== void 0;
|
|
24611
24758
|
this.script = options.scriptHost ?? this.engine;
|
|
@@ -24700,7 +24847,9 @@ var Room = class {
|
|
|
24700
24847
|
ok: true,
|
|
24701
24848
|
slug: this.mapBootstrap?.slug ?? null,
|
|
24702
24849
|
seeded: this.worldReady,
|
|
24703
|
-
entities: this.engine.getReplica().entities.length
|
|
24850
|
+
entities: this.engine.getReplica().entities.length,
|
|
24851
|
+
mapLoadError: this.mapLoadError ?? null,
|
|
24852
|
+
warnings: this.seedWarnings
|
|
24704
24853
|
}),
|
|
24705
24854
|
{ headers: { "content-type": "application/json", "cache-control": "no-store" } }
|
|
24706
24855
|
);
|
|
@@ -24731,6 +24880,8 @@ var Room = class {
|
|
|
24731
24880
|
secretPackets: this.secretPackets,
|
|
24732
24881
|
clockPings: this.clockPings,
|
|
24733
24882
|
inboundIds: Object.fromEntries(this.inboundIds),
|
|
24883
|
+
mapLoadError: this.mapLoadError ?? null,
|
|
24884
|
+
mapWarnings: this.seedWarnings,
|
|
24734
24885
|
voxel: this.engine.voxelGeometry?.() ?? null,
|
|
24735
24886
|
sampleEntity: (() => {
|
|
24736
24887
|
const entity = this.engine.getReplica().entities[0];
|
|
@@ -24828,21 +24979,32 @@ var Room = class {
|
|
|
24828
24979
|
this.engine = engine;
|
|
24829
24980
|
if (!this.explicitScriptHost) this.script = engine;
|
|
24830
24981
|
this.seededMap = { slug: bootstrap.slug, root: bootstrap.root, worldId: seed.mapId };
|
|
24982
|
+
this.seedWarnings = seed.warnings;
|
|
24983
|
+
this.mapLoadError = void 0;
|
|
24984
|
+
for (const warning of seed.warnings) {
|
|
24985
|
+
this.logger.log(`cajonzero: map "${bootstrap.slug}" warning: ${warning}`);
|
|
24986
|
+
}
|
|
24831
24987
|
this.applyPendingEngineSnapshot();
|
|
24832
24988
|
this.worldReady = true;
|
|
24833
24989
|
this.logger.log(`cajonzero: seeded room "${bootstrap.slug}" from "${seed.mapId}"`);
|
|
24834
24990
|
} catch (error) {
|
|
24991
|
+
this.mapLoadError = describeError2(error);
|
|
24835
24992
|
this.logger.error(
|
|
24836
|
-
`cajonzero: map load failed for "${bootstrap.slug}": ${
|
|
24993
|
+
`cajonzero: map load failed for "${bootstrap.slug}": ${this.mapLoadError}`
|
|
24837
24994
|
);
|
|
24838
24995
|
}
|
|
24839
24996
|
}
|
|
24840
24997
|
loadSeed(bootstrap) {
|
|
24841
|
-
return
|
|
24842
|
-
|
|
24843
|
-
|
|
24844
|
-
|
|
24845
|
-
|
|
24998
|
+
return loadSeedPackFromAssets(this.assets, bootstrap.slug, bootstrap.worldId).then(
|
|
24999
|
+
(packed) => {
|
|
25000
|
+
if (packed !== null) return packed;
|
|
25001
|
+
return loadWorldSeedFromHttp(bootstrap, {
|
|
25002
|
+
...bootstrap.maxDownloadBytes !== void 0 ? { maxDownloadBytes: bootstrap.maxDownloadBytes } : {},
|
|
25003
|
+
...bootstrap.concurrency !== void 0 ? { concurrency: bootstrap.concurrency } : {},
|
|
25004
|
+
...bootstrap.requestTimeoutMs !== void 0 ? { requestTimeoutMs: bootstrap.requestTimeoutMs } : {}
|
|
25005
|
+
});
|
|
25006
|
+
}
|
|
25007
|
+
);
|
|
24846
25008
|
}
|
|
24847
25009
|
/**
|
|
24848
25010
|
* Run the full connect path and return a structured outcome (no `Response`,
|
|
@@ -26658,7 +26820,7 @@ function createPlayEngine(options = {}) {
|
|
|
26658
26820
|
const spawnOnClientInput = options.spawnOnClientInput ?? true;
|
|
26659
26821
|
const scriptHost = options.scriptHost;
|
|
26660
26822
|
const physicsOverride = options.physics ? buildPhysicsOverride(options.physics) : null;
|
|
26661
|
-
const playerConfig =
|
|
26823
|
+
const playerConfig = isRecord4(options.worldSeed?.player) ? options.worldSeed.player : null;
|
|
26662
26824
|
let world = options.world ? NetStateSchema.clone(options.world) : options.worldSeed ? buildInitialWorld(options.worldSeed) : null;
|
|
26663
26825
|
let currentTick = 0;
|
|
26664
26826
|
let suspended = false;
|
|
@@ -26977,7 +27139,7 @@ function buildPhysicsOverride(override) {
|
|
|
26977
27139
|
}
|
|
26978
27140
|
return params;
|
|
26979
27141
|
}
|
|
26980
|
-
function
|
|
27142
|
+
function isRecord4(value) {
|
|
26981
27143
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
26982
27144
|
}
|
|
26983
27145
|
function finite(value) {
|
|
@@ -27035,7 +27197,7 @@ function spawnPlayerBody(id, config) {
|
|
|
27035
27197
|
body.friction = PLAYER_FRICTION;
|
|
27036
27198
|
body.restitution = PLAYER_RESTITUTION;
|
|
27037
27199
|
body.mass = PLAYER_MASS;
|
|
27038
|
-
const spawn =
|
|
27200
|
+
const spawn = isRecord4(config?.initialPosition) ? config.initialPosition : null;
|
|
27039
27201
|
body.px = finite(spawn?.x) ?? 0;
|
|
27040
27202
|
body.py = finite(spawn?.y) ?? 0;
|
|
27041
27203
|
body.pz = finite(spawn?.z) ?? 0;
|
|
@@ -27054,12 +27216,12 @@ function projectEnvironment(raw) {
|
|
|
27054
27216
|
return environment;
|
|
27055
27217
|
}
|
|
27056
27218
|
function copyEnvironmentFields(target, source) {
|
|
27057
|
-
if (!
|
|
27219
|
+
if (!isRecord4(target)) return;
|
|
27058
27220
|
for (const key of Object.keys(target)) {
|
|
27059
27221
|
const value = source[key];
|
|
27060
27222
|
if (value === void 0) continue;
|
|
27061
27223
|
const current = target[key];
|
|
27062
|
-
if (
|
|
27224
|
+
if (isRecord4(current) && isRecord4(value)) {
|
|
27063
27225
|
copyEnvironmentFields(current, value);
|
|
27064
27226
|
continue;
|
|
27065
27227
|
}
|
|
@@ -131,6 +131,10 @@ interface R2Bucket {
|
|
|
131
131
|
delete(key: string): Promise<void>;
|
|
132
132
|
list(options?: unknown): Promise<unknown>;
|
|
133
133
|
}
|
|
134
|
+
/** Structural stand-in for workerd's static-assets `Fetcher` binding. */
|
|
135
|
+
interface AssetsBinding {
|
|
136
|
+
fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response>;
|
|
137
|
+
}
|
|
134
138
|
/**
|
|
135
139
|
* Runtime bindings + secrets injected by the Cloudflare Workers runtime.
|
|
136
140
|
*
|
|
@@ -142,6 +146,11 @@ interface Env {
|
|
|
142
146
|
readonly CAJONZERO_KV: KVNamespace;
|
|
143
147
|
/** Durable Object namespace for per-room actors (`Room`). */
|
|
144
148
|
readonly ROOMS: DurableObjectNamespace;
|
|
149
|
+
/**
|
|
150
|
+
* Static-assets binding (`assets.binding`). Optional: older deployments
|
|
151
|
+
* without it still work, they just cannot read the worker-bundled seed packs.
|
|
152
|
+
*/
|
|
153
|
+
readonly ASSETS?: AssetsBinding;
|
|
145
154
|
/** Optional R2 bucket for large map assets; absent in minimal deployments. */
|
|
146
155
|
readonly CAJONZERO_R2?: R2Bucket;
|
|
147
156
|
/** Admin bearer token guarding the management/ban API. Secret. */
|
|
@@ -533,6 +542,8 @@ interface MapEntry {
|
|
|
533
542
|
readonly name?: string;
|
|
534
543
|
readonly author?: string;
|
|
535
544
|
readonly version?: string;
|
|
545
|
+
/** Optional content revision (e.g. `v1.0.4`); invalidates precomputed seeds. */
|
|
546
|
+
readonly etag?: string;
|
|
536
547
|
/** Cover image URL for the lobby. */
|
|
537
548
|
readonly cover?: string;
|
|
538
549
|
/** Optional CDN root for browser assets; defaults to {@link root}. */
|
|
@@ -5032,6 +5043,8 @@ declare class Room {
|
|
|
5032
5043
|
private readonly createPair;
|
|
5033
5044
|
private readonly logger;
|
|
5034
5045
|
private readonly secret;
|
|
5046
|
+
/** Worker static-assets binding, used to read seed packs bundled at deploy. */
|
|
5047
|
+
private readonly assets;
|
|
5035
5048
|
private engine;
|
|
5036
5049
|
private script;
|
|
5037
5050
|
private readonly explicitScriptHost;
|
|
@@ -5094,6 +5107,10 @@ declare class Room {
|
|
|
5094
5107
|
private pendingEngineMap;
|
|
5095
5108
|
/** Identity of the map the current engine was seeded from. */
|
|
5096
5109
|
private seededMap;
|
|
5110
|
+
/** Non-fatal seed warnings (missing blobs, skipped entities, …). */
|
|
5111
|
+
private seedWarnings;
|
|
5112
|
+
/** Last map-load failure; cleared on a successful seed. */
|
|
5113
|
+
private mapLoadError;
|
|
5097
5114
|
private heartbeatBeats;
|
|
5098
5115
|
private ticksRun;
|
|
5099
5116
|
private lastTickError;
|
package/dist/scaffold.d.ts
CHANGED
|
@@ -147,7 +147,7 @@ declare function toPackageName(folder: string): string;
|
|
|
147
147
|
declare function createProject(folder: string, options?: CreateProjectOptions): ScaffoldResult;
|
|
148
148
|
|
|
149
149
|
/** Where one setup value is written. */
|
|
150
|
-
type SetupKind = "var" | "secret";
|
|
150
|
+
type SetupKind = "var" | "secret" | "kv";
|
|
151
151
|
/** One configurable value. */
|
|
152
152
|
interface SetupField {
|
|
153
153
|
readonly name: string;
|
|
@@ -171,6 +171,8 @@ interface SetupResult {
|
|
|
171
171
|
readonly vars: Record<string, string>;
|
|
172
172
|
/** Secrets written to `.dev.vars`. */
|
|
173
173
|
readonly secrets: Record<string, string>;
|
|
174
|
+
/** KV namespace id written to `wrangler.jsonc` (when provided). */
|
|
175
|
+
readonly kvId?: string;
|
|
174
176
|
}
|
|
175
177
|
interface SetupOptions {
|
|
176
178
|
readonly cwd?: string;
|
|
@@ -189,6 +191,19 @@ declare function suggestedValue(field: SetupField): string | undefined;
|
|
|
189
191
|
declare function parseDevVars(text: string): Record<string, string>;
|
|
190
192
|
/** Serialize `.dev.vars` (stable order, trailing newline). */
|
|
191
193
|
declare function serializeDevVars(values: Record<string, string>): string;
|
|
194
|
+
/** Strip `//` / block comments and trailing commas so a JSONC object can be parsed. */
|
|
195
|
+
declare function parseJsoncObject(text: string): Record<string, unknown>;
|
|
196
|
+
/**
|
|
197
|
+
* Read the KV namespace id bound to `binding` from a wrangler config.
|
|
198
|
+
* Returns `undefined` when the binding is absent or the id is the placeholder.
|
|
199
|
+
*/
|
|
200
|
+
declare function readWranglerKvNamespaceId(raw: string, binding?: string): string | undefined;
|
|
201
|
+
/**
|
|
202
|
+
* Set the `id` of the `kv_namespaces` entry bound to `binding`, preserving
|
|
203
|
+
* comments and unrelated JSONC formatting. Adds the entry (or the whole
|
|
204
|
+
* `kv_namespaces` array) when missing.
|
|
205
|
+
*/
|
|
206
|
+
declare function setWranglerKvNamespaceId(raw: string, binding: string, id: string): string;
|
|
192
207
|
/**
|
|
193
208
|
* Merge `vars` into the `wrangler.jsonc` `"vars"` object.
|
|
194
209
|
*
|
|
@@ -204,5 +219,54 @@ declare function readSetupValues(cwd: string, configPath: string, devVarsPath: s
|
|
|
204
219
|
*/
|
|
205
220
|
declare function applySetup(options: SetupOptions): SetupResult;
|
|
206
221
|
|
|
207
|
-
|
|
208
|
-
|
|
222
|
+
/** Marker written inside the target dir; excluded from the copy and the hash. */
|
|
223
|
+
declare const ASSETS_STAMP_FILE = ".cajonzero-assets.json";
|
|
224
|
+
/**
|
|
225
|
+
* Subdirectory of `assets.directory` that holds `cj0 worker build-seed` packs.
|
|
226
|
+
* `worker prepare` preserves it when re-syncing the packaged UI.
|
|
227
|
+
*/
|
|
228
|
+
declare const ASSETS_SEED_DIR = "seed";
|
|
229
|
+
/** Options for {@link prepareWorkerAssets}. */
|
|
230
|
+
interface PrepareWorkerAssetsOptions {
|
|
231
|
+
/** Project directory (default: `process.cwd()`). */
|
|
232
|
+
readonly cwd?: string;
|
|
233
|
+
/** Wrangler config, relative to `cwd` (default: `wrangler.jsonc`). */
|
|
234
|
+
readonly configPath?: string;
|
|
235
|
+
/**
|
|
236
|
+
* Target directory, relative to the config's directory. Overrides
|
|
237
|
+
* `assets.directory` from the config.
|
|
238
|
+
*/
|
|
239
|
+
readonly assetsDir?: string;
|
|
240
|
+
/** Source tree override (tests). Defaults to the installed package's `dist/ui`. */
|
|
241
|
+
readonly sourceDir?: string;
|
|
242
|
+
/** Copy even when the fingerprint already matches. */
|
|
243
|
+
readonly force?: boolean;
|
|
244
|
+
}
|
|
245
|
+
/** Result of a successful preparation. */
|
|
246
|
+
interface PrepareWorkerAssetsResult {
|
|
247
|
+
/** Absolute source directory (the packaged UI). */
|
|
248
|
+
readonly sourceDir: string;
|
|
249
|
+
/** Absolute target directory (the project's `assets.directory`). */
|
|
250
|
+
readonly targetDir: string;
|
|
251
|
+
/** Content fingerprint of the source tree. */
|
|
252
|
+
readonly fingerprint: string;
|
|
253
|
+
/** `false` when the target was already up to date. */
|
|
254
|
+
readonly copied: boolean;
|
|
255
|
+
/** Number of files in the source tree. */
|
|
256
|
+
readonly files: number;
|
|
257
|
+
/** Total uncompressed bytes in the source tree. */
|
|
258
|
+
readonly bytes: number;
|
|
259
|
+
}
|
|
260
|
+
/** Throw when `configPath` is missing or has no `assets.directory`. */
|
|
261
|
+
declare function readWranglerAssetsDirectory(configPath: string): string | undefined;
|
|
262
|
+
/**
|
|
263
|
+
* Materialise cajonzero's packaged UI into the project's static-assets
|
|
264
|
+
* directory. Idempotent: a matching fingerprint is a no-op.
|
|
265
|
+
*
|
|
266
|
+
* @throws when the packaged UI is missing, the config has no `assets.directory`
|
|
267
|
+
* and no override was given, or the target would overlap the project/source.
|
|
268
|
+
*/
|
|
269
|
+
declare function prepareWorkerAssets(options?: PrepareWorkerAssetsOptions): Promise<PrepareWorkerAssetsResult>;
|
|
270
|
+
|
|
271
|
+
export { ASSETS_SEED_DIR, ASSETS_STAMP_FILE, SCAFFOLD_COMPATIBILITY_DATE, SCAFFOLD_CONFIG_CONTENT, SCAFFOLD_ENTRY_CONTENT, SCAFFOLD_ENTRY_PATH, SCAFFOLD_KV_PLACEHOLDER_ID, SCAFFOLD_MAPS_CONTENT, SETUP_FIELDS, ScaffoldError, applySetup, buildScaffoldFiles, createProject, generateAdminToken, generateSecret, parseDevVars, parseJsoncObject, prepareWorkerAssets, readCajonzeroPackage, readSetupValues, readWranglerAssetsDirectory, readWranglerKvNamespaceId, serializeDevVars, setWranglerKvNamespaceId, setWranglerVars, suggestedValue, toPackageName };
|
|
272
|
+
export type { CajonzeroPackageInfo, CreateProjectOptions, PrepareWorkerAssetsOptions, PrepareWorkerAssetsResult, ScaffoldErrorCode, ScaffoldFile, ScaffoldResult, ScaffoldTemplateOptions, SetupField, SetupKind, SetupOptions, SetupResult };
|