@crowdedkingdoms/crowdyjs 18.1.0-dev.1 → 18.2.0-dev.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/MIGRATION.md CHANGED
@@ -120,6 +120,25 @@ super-admin's session). There is no SDK replacement.
120
120
  `dev`'s SDL after #417 merged (`npm run schema:sync:paths -- --schema <that schema.gql>`, then
121
121
  `npm run codegen`).
122
122
 
123
+ ## 18.2.0: `ChunkStore` keeps the voxel edits it hydrated
124
+
125
+ No API change. Every voxel write but a chunk write-back lands only in the chunk's edit log: a
126
+ hub's or mod's `world.set_voxels`, `updateVoxel`, and realtime voxel updates. Since ck-api
127
+ `dev/v2.33.0` (cks-game-api #445) `getChunk` returns each recorded edit as a `voxelStates` entry
128
+ with its type, over a stored `voxels` that holds none of them. `ChunkStore` applies them when it
129
+ hydrates a loaded chunk: set `hydrateVoxelStates: true` unless you configure a
130
+ `voxelStateCodec` (the-construct does since construct 0.3.6), or a reload shows none of them.
131
+ (OI-2026-10-02-006)
132
+
133
+ - `ensureAround` no longer applies a chunk it has already loaded when the bulk load returns it
134
+ again: the cube around a new center overlaps the old one, and the stored `voxels` put back
135
+ over the cache wiped the hydrated edits (a hub's block vanished the moment the player crossed
136
+ into a new chunk) and every realtime merge, and the chunk was never hydrated again. To load a
137
+ chunk afresh, `pruneBeyond` it first, or call `hydrate(coord)`.
138
+ - `hydrate` puts the entries on a chunk stored with `voxels: null` (a zero grid under them); it
139
+ used to drop their types. An entry without a state clears the state cached at its voxel, as a
140
+ realtime merge without one does.
141
+
123
142
  ## 18.1.0: open grids, and where a connect token may go
124
143
 
125
144
  Additive, except that `client.exec.connect` refuses a gateway it would once have dialed (below).
package/README.md CHANGED
@@ -12,19 +12,23 @@ gameplay, minted per app through `client.portal`.
12
12
 
13
13
  Start here:
14
14
 
15
- - [SDK guide](https://docs.crowdedkingdoms.com/crowdyjs/readme) — the canonical
15
+ - [SDK guide](https://docs.dev.crowdedkingdoms.com/crowdyjs/readme) — the canonical
16
16
  walkthrough of everything in this README.
17
- - [Build a game](https://docs.crowdedkingdoms.com/build-a-game/intro) — a
17
+ - [Build a game](https://docs.dev.crowdedkingdoms.com/build-a-game/intro) — a
18
18
  hands-on tutorial from sign-in to a playable voxel game.
19
- - [Platform overview](https://docs.crowdedkingdoms.com/overview/client-workflow)
19
+ - [Platform overview](https://docs.dev.crowdedkingdoms.com/overview/client-workflow)
20
20
  — how a client session flows through the platform.
21
21
 
22
22
  ## Install
23
23
 
24
24
  ```bash
25
- npm install @crowdedkingdoms/crowdyjs
25
+ npm install @crowdedkingdoms/crowdyjs@dev
26
26
  ```
27
27
 
28
+ This README is the `dev` branch's: it documents the release on the `dev` dist-tag
29
+ (`npm view @crowdedkingdoms/crowdyjs dist-tags`), and its links go to the dev docs site. A
30
+ plain `npm install` resolves `latest`, the production release, which has no `client.exec`.
31
+
28
32
  CrowdyJS targets browsers by default and uses native `fetch`, `WebSocket`,
29
33
  `crypto`, `btoa`, and `atob`. Node tools can still use the SDK, but must
30
34
  provide browser-compatible globals when opening realtime connections (e.g.
@@ -171,8 +175,8 @@ Notes:
171
175
  - Use `client.auth.setToken(token)` to seed a token externally (e.g. when
172
176
  restoring auth from a non-default storage).
173
177
 
174
- Deeper reading: [Portals & app-scoped tokens](https://docs.crowdedkingdoms.com/management-api/portals-and-app-tokens)
175
- and [Sign in](https://docs.crowdedkingdoms.com/management-api/authentication).
178
+ Deeper reading: [Portals & app-scoped tokens](https://docs.dev.crowdedkingdoms.com/management-api/portals-and-app-tokens)
179
+ and [Sign in](https://docs.dev.crowdedkingdoms.com/management-api/authentication).
176
180
 
177
181
  ### Token refresh during gameplay
178
182
 
@@ -295,7 +299,7 @@ query AppForRouting($appId: BigInt!) {
295
299
  `CrowdyClient` with `httpUrl: gameApiUrl` (and the matching `wsUrl`) holding
296
300
  that app's app-scoped token, then drive gameplay through that client. Apps
297
301
  with no `gameApiUrl` keep working against the default `httpUrl` you
298
- configured. See [Loading an app's Game API](https://docs.crowdedkingdoms.com/crowdyjs/shared-environment-routing).
302
+ configured. See [Loading an app's Game API](https://docs.dev.crowdedkingdoms.com/crowdyjs/shared-environment-routing).
299
303
 
300
304
  ## Realtime notifications
301
305
 
@@ -587,6 +591,11 @@ write-back), `channelInbox` / `actorInbox` (message inboxes), `events` (typed
587
591
  event router), `host` (host tracking), and `save` / `avatar` (typed durable
588
592
  state).
589
593
 
594
+ A voxel edit that is not a chunk write-back (a hub's or mod's `world.set_voxels`, `updateVoxel`,
595
+ another player's realtime voxel update) reaches a reload only through hydration: `getChunk`
596
+ returns it in `voxelStates`, and the bulk load's `voxels` never hold it. The store hydrates when
597
+ it has a `voxelStateCodec`; without one, set `chunks: { hydrateVoxelStates: true }`.
598
+
590
599
  A chunk write-back the server refuses (a visitor on someone else's plot, a safe zone, a closed
591
600
  wilderness) is dropped, not retried; one that fails for a reason that can clear is tried five
592
601
  times. Listen with `session.chunks.onWriteBackFailed(...)` to undo or flag the edit (18.0.4).
@@ -599,7 +608,7 @@ reach your bundle. Reads are synchronous snapshots and writes happen on
599
608
  WebSocket events (not `requestAnimationFrame`), so render loops read freely
600
609
  and a backgrounded tab keeps ingesting updates; pass `workerTicker()` to also
601
610
  keep timer-driven sends at full rate while hidden. See the
602
- [World Stores guide](https://docs.crowdedkingdoms.com/crowdyjs/stores).
611
+ [World Stores guide](https://docs.dev.crowdedkingdoms.com/crowdyjs/stores).
603
612
 
604
613
  ## Game Kit
605
614
 
@@ -626,7 +635,8 @@ model in 18.0.0; see [MIGRATION.md](MIGRATION.md).
626
635
  ## ck-exec (dev-tier preview)
627
636
 
628
637
  ck-exec runs an app's server code as **hubs** (stateful, one instance per key) and **spokes**
629
- (stateless, replicated) on execution hosts, on the dev environment only for now. A player
638
+ (stateless, replicated) on execution hosts, on the dev environment only for now, so it needs
639
+ the `@dev` release (`npm install @crowdedkingdoms/crowdyjs@dev`). A player
630
640
  connects to one host and calls any node of the app through it:
631
641
 
632
642
  ```ts
@@ -873,7 +883,7 @@ CLIENT project needs ck-api `v2.25.1` or later. A CLIENT crate still on the lega
873
883
  on legacy player compute since 18.0.0.
874
884
 
875
885
  See [CLIENT halves](https://docs.dev.crowdedkingdoms.com/exec/client-halves), [mods](https://docs.dev.crowdedkingdoms.com/exec/mods)
876
- and [Embed Crowdy Studio in your game](https://docs.crowdedkingdoms.com/crowdyjs/crowdy-studio-embed).
886
+ and [Embed Crowdy Studio in your game](https://docs.dev.crowdedkingdoms.com/crowdyjs/crowdy-studio-embed).
877
887
 
878
888
  ### The Studio agent (DeepSeek Harness in the browser)
879
889
 
@@ -1051,7 +1061,7 @@ no other repositories and no network access required:
1051
1061
  - `src/generated/graphql.ts` — generated TypeScript operation types.
1052
1062
 
1053
1063
  Schema refresh is explicit, from the published
1054
- [SDL](https://docs.crowdedkingdoms.com/schema/game-api.graphql):
1064
+ [SDL](https://docs.dev.crowdedkingdoms.com/schema/game-api.graphql):
1055
1065
 
1056
1066
  ```bash
1057
1067
  npm run schema:sync:prod
@@ -17,8 +17,8 @@ import type { GameAppsAPI } from './gameApps.js';
17
17
  * instances also reachable at the top level (e.g. `client.billing`), grouped
18
18
  * here for discoverability.
19
19
  *
20
- * Operator-only platform operations live separately under `client.operator`
21
- * (requires `is_operator`).
20
+ * Platform-operator operations are not in the SDK; it wraps only what a studio
21
+ * or a player may call.
22
22
  *
23
23
  * @example
24
24
  * ```ts
@@ -8,8 +8,8 @@
8
8
  * instances also reachable at the top level (e.g. `client.billing`), grouped
9
9
  * here for discoverability.
10
10
  *
11
- * Operator-only platform operations live separately under `client.operator`
12
- * (requires `is_operator`).
11
+ * Platform-operator operations are not in the SDK; it wraps only what a studio
12
+ * or a player may call.
13
13
  *
14
14
  * @example
15
15
  * ```ts
@@ -1096,7 +1096,7 @@ export type AppMarketplaceFilterInput = {
1096
1096
  /** Per-player usage row for studio diagnostics (top spenders / quota utilization): one (player, app) aggregate over a window. */
1097
1097
  export type AppPlayerUsageRow = {
1098
1098
  __typename?: 'AppPlayerUsageRow';
1099
- /** Player automation units used. */
1099
+ /** Legacy: the removed engines' player automation units. Always 0 for usage since the legacy engines were removed; kept for the API shape until the migration. */
1100
1100
  automationUnits: Scalars['BigInt']['output'];
1101
1101
  /**
1102
1102
  * DEPRECATED. chargedMicrousd / 10,000 truncated toward zero.
@@ -1105,7 +1105,7 @@ export type AppPlayerUsageRow = {
1105
1105
  chargedCents: Scalars['BigInt']['output'];
1106
1106
  /** Micro-USD charged to the player for this app in the window (exact). */
1107
1107
  chargedMicrousd: Scalars['BigInt']['output'];
1108
- /** Compile submissions. */
1108
+ /** Legacy: compile submissions of the removed player-compute engine (mod builds are not counted). Always 0 for usage since the legacy engines were removed; kept for the API shape until the migration. */
1109
1109
  compileCount: Scalars['Int']['output'];
1110
1110
  /** Player compute units used. */
1111
1111
  computeUnits: Scalars['BigInt']['output'];
@@ -1219,11 +1219,11 @@ export type AppUsageSummary = {
1219
1219
  __typename?: 'AppUsageSummary';
1220
1220
  /** App id (as a string). */
1221
1221
  appId: Scalars['String']['output'];
1222
- /** Billed autonomous-process compute units over the window (string counter). */
1222
+ /** Legacy: billed compute units of the removed automation engine over the window (string counter). Always 0 for usage since the legacy engines were removed; kept for the API shape until the migration. */
1223
1223
  automationComputeUnits: Scalars['String']['output'];
1224
- /** Autonomous-process function invocations over the window (string counter). */
1224
+ /** Legacy: function invocations of the removed automation engine over the window (string counter). Always 0 for usage since the legacy engines were removed; kept for the API shape until the migration. */
1225
1225
  automationInvocations: Scalars['String']['output'];
1226
- /** Autonomous-process (NPC) runs over the window (string counter). */
1226
+ /** Legacy: runs of the removed automation engine over the window (string counter). Always 0 for usage since the legacy engines were removed; kept for the API shape until the migration. */
1227
1227
  automationRuns: Scalars['String']['output'];
1228
1228
  /** Total GraphQL bytes received (string counter). */
1229
1229
  graphqlRecvBytes: Scalars['String']['output'];
@@ -1611,7 +1611,7 @@ export declare enum CheckoutPurpose {
1611
1611
  Donation = "DONATION",
1612
1612
  /** Add funds to an organization wallet. Requires orgId and amountCents, and the caller must hold the org "manage_billing" permission. Credits the org wallet on completion. */
1613
1613
  OrgWalletTopup = "ORG_WALLET_TOPUP",
1614
- /** Add funds to the caller's own player wallet (player compute P2). Requires amountCents only — any authenticated user may fund their own wallet; no org permission is involved. Credits the player wallet idempotently on completion. */
1614
+ /** Add funds to the caller's own player wallet, which pays for their mods past the monthly trial (see PlayerWallet). Requires amountCents only — any authenticated user may fund their own wallet; no org permission is involved. Credits the player wallet idempotently on completion. */
1615
1615
  PlayerWalletTopup = "PLAYER_WALLET_TOPUP",
1616
1616
  /**
1617
1617
  * Deprecated. Historically a purchase of in-world property tokens. No longer purchasable and rejected at runtime by createCheckout.
@@ -3241,7 +3241,7 @@ export type DefineAppFeatureInput = {
3241
3241
  appId: Scalars['BigInt']['input'];
3242
3242
  /** Optional description of the feature. */
3243
3243
  description?: InputMaybe<Scalars['String']['input']>;
3244
- /** The feature key (referenced by tier_feature authority rules). */
3244
+ /** The feature key, as ck-exec code reads it among a player's features. */
3245
3245
  featureKey: Scalars['String']['input'];
3246
3246
  };
3247
3247
  /** Identifies a studio-created grid to delete. The default open-by-default world grid and grids that still contain nested child grids cannot be removed. */
@@ -3833,14 +3833,14 @@ export type GetVoxelListInput = {
3833
3833
  /** Address of the chunk whose voxel edits to list. */
3834
3834
  coordinates: ChunkCoordinatesInput;
3835
3835
  };
3836
- /** An app feature key that functions can gate on and tiers can grant. */
3836
+ /** An app feature key that access tiers can grant. ck-exec code reads the keys a player's tier grants with `ctx.players().features(player)`. */
3837
3837
  export type GmAppFeature = {
3838
3838
  __typename?: 'GmAppFeature';
3839
3839
  /** The app (tenant) that defines the feature. */
3840
3840
  appId: Scalars['BigInt']['output'];
3841
3841
  /** Optional description of the feature. */
3842
3842
  description: Maybe<Scalars['String']['output']>;
3843
- /** The feature key (referenced by tier_feature authority rules). */
3843
+ /** The feature key, as ck-exec code reads it among a player's features. */
3844
3844
  featureKey: Scalars['String']['output'];
3845
3845
  };
3846
3846
  /** A grant of a feature key to an access tier. */
@@ -4513,9 +4513,9 @@ export type Mutation = {
4513
4513
  forceLogoutUser: Scalars['Boolean']['output'];
4514
4514
  /** Operator only (is_operator). Deletes the stored deliverability rows for one address (email_status and email_events) and returns how many rows went. Exists so a verification run is not reading the previous run's events, and so an address suppressed by a bounce that has since been fixed can be given another chance. Returns 0 when there was nothing stored. */
4515
4515
  forgetEmailDeliverability: Scalars['Int']['output'];
4516
- /** Define an app feature key that functions can gate on (via a tier_feature authority rule) and that access tiers can be granted. Idempotent on (app, featureKey). Requires app-admin ('manage_apps'). */
4516
+ /** Define an app feature key that access tiers can be granted. ck-exec code reads the keys a player's tier grants with `ctx.players().features(player)` (scope `players.read`) and gates on them itself. The `gameModel` prefix is a legacy name. Idempotent on (app, featureKey). Requires app-admin ('manage_apps'). */
4517
4517
  gameModelDefineFeature: GmAppFeature;
4518
- /** Grant a feature key to an access tier, so users on that tier satisfy tier_feature authority checks for it. Requires app-admin ('manage_apps'). */
4518
+ /** Grant a feature key to an access tier, so it is among the features ck-exec code reads with `ctx.players().features(player)` for every player with active access on that tier. The `gameModel` prefix is a legacy name. Requires app-admin ('manage_apps'). */
4519
4519
  gameModelGrantTierFeature: GmTierFeature;
4520
4520
  /** Revoke a feature key from an access tier. Requires app-admin ('manage_apps'). Returns true if a grant was removed. */
4521
4521
  gameModelRevokeTierFeature: Scalars['Boolean']['output'];
@@ -6103,7 +6103,7 @@ export type PlayerUsageCharge = {
6103
6103
  /** The owning player user id. */
6104
6104
  userId: Scalars['BigInt']['output'];
6105
6105
  };
6106
- /** The caller's player wallet (player compute P2, DN-5): a platform-scoped balance funding that player's grid compute across every org/app they play in. Out-of-band from org wallets — player usage never touches an org's money. */
6106
+ /** The caller's player wallet: one platform-scoped balance, across every org and app they play in, that pays for the player's mods past their monthly trial (250,000 compute units in each app) and for any Agentic Studio model requests charged to them. Out-of-band from org wallets — player usage never touches an org's money. */
6107
6107
  export type PlayerWallet = {
6108
6108
  __typename?: 'PlayerWallet';
6109
6109
  /**
@@ -6300,7 +6300,7 @@ export type Query = {
6300
6300
  appPlayerMarkupAccrued: Scalars['BigInt']['output'];
6301
6301
  /** Total player rate-card markup credited to this app's org wallet (`markup_payout` rows, mirrored as CREDIT events on the ledger), in micro-USD, exact. Cents-era accruals from before the ledger are included. Requires 'view_billing'. */
6302
6302
  appPlayerMarkupAccruedMicrousd: Scalars['BigInt']['output'];
6303
- /** Per-player usage aggregate for an app over a trailing window (top spenders / quota utilization, 06 §5): compute units, automation units, compiles, and cents charged per player. Requires 'view_compute_diagnostics'. */
6303
+ /** Per-player usage aggregate for an app over a trailing window (top spenders / quota utilization): the compute units each player's mods used, and what the player was charged for this app. automationUnits and compileCount are legacy and always 0 for usage since the legacy engines were removed. Requires 'view_compute_diagnostics'. */
6304
6304
  appPlayerUsage: Array<AppPlayerUsageRow>;
6305
6305
  /** Shared-environment runtime gate decision plus current hour/day billing-window usage for an app. Read this to learn why an app is not running (runtimeDenialReason). Caller must be a member of the app's org. */
6306
6306
  appRuntimeState: AppRuntimeState;
@@ -6562,7 +6562,7 @@ export type Query = {
6562
6562
  playerSpendCaps: Array<PlayerSpendCap>;
6563
6563
  /** The caller's posted hourly player-usage charges, newest first, optionally filtered by app. Each charge splits platformCents (base rate card) from markupCents (the studio's configured markup) with a per-metric snapshot — players always see what the platform charges and what the studio adds. */
6564
6564
  playerUsageCharges: Array<PlayerUsageCharge>;
6565
- /** The caller's player wallet, created empty on first access (never null). Platform-scoped: one wallet funds the player's grid compute across every org/app (DN-5); org billing never touches it. Fund it via createCheckout purpose PLAYER_WALLET_TOPUP. */
6565
+ /** The caller's player wallet, created empty on first access (never null). Platform-scoped: one wallet, across every org and app, pays for the player's mods past their monthly trial and any Agentic Studio model requests charged to them; org billing never touches it. Fund it via createCheckout purpose PLAYER_WALLET_TOPUP. */
6566
6566
  playerWalletBalance: PlayerWallet;
6567
6567
  /** The caller's player-wallet ledger, newest first: top-ups, hourly usage debits (per app), auto-recharges, refunds, and adjustments. Usage-debit hours are broken down by playerUsageCharges, whose snapshot splits the platform and studio-markup components. */
6568
6568
  playerWalletTransactions: Array<PlayerWalletTransaction>;
@@ -8375,13 +8375,13 @@ export type VoxelCoordinates = {
8375
8375
  /** Local voxel Z within the chunk (0-15 for in-bounds voxels). */
8376
8376
  z: Scalars['Int']['output'];
8377
8377
  };
8378
- /** Input form of a voxel position LOCAL to its chunk (see VoxelCoordinates). Signed 16-bit integers; in-bounds positions are 0-15 on each axis for a 16x16x16 chunk. */
8378
+ /** Input form of a voxel position LOCAL to its chunk (see VoxelCoordinates). Each coordinate is 0-15 on a 16x16x16 chunk, and the voxel writes (updateVoxel, sendVoxelUpdate, updateChunk's voxelStates) refuse any other value. Teleport's voxelAddress, which is not a write, takes any signed 16-bit value. */
8379
8379
  export type VoxelCoordinatesInput = {
8380
- /** Local voxel X within the chunk (0-15 for in-bounds voxels). */
8380
+ /** Local voxel X within the chunk, 0-15; voxel writes refuse other values. */
8381
8381
  x: Scalars['Int']['input'];
8382
- /** Local voxel Y within the chunk (0-15 for in-bounds voxels). */
8382
+ /** Local voxel Y within the chunk, 0-15; voxel writes refuse other values. */
8383
8383
  y: Scalars['Int']['input'];
8384
- /** Local voxel Z within the chunk (0-15 for in-bounds voxels). */
8384
+ /** Local voxel Z within the chunk, 0-15; voxel writes refuse other values. */
8385
8385
  z: Scalars['Int']['input'];
8386
8386
  };
8387
8387
  /** A single voxel's state override stored on a chunk: its local position, its voxel type, and an opaque base64-encoded state blob. */