Skip to content

chore: bump dojo 1.5 #434

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 30, 2025
Merged

chore: bump dojo 1.5 #434

merged 1 commit into from
Apr 30, 2025

Conversation

MartianGreed
Copy link
Collaborator

@MartianGreed MartianGreed commented Apr 28, 2025

Closes #

Introduced changes

Checklist

  • Linked relevant issue
  • Updated relevant documentation
  • Added relevant tests
  • Add a dedicated CI job for new examples
  • Performed self-review of the code

Summary by CodeRabbit

  • New Features

    • Added utilities for deep merging complex Cairo data types and model retrieval functions.
    • Introduced a worker lifecycle management utility for graceful subscription cleanup.
    • Provided TypeScript modules for smart contract interactions and comprehensive data models in the example Node worker.
    • Enhanced SDK with modular offchain message support and refined entity update handling.
  • Improvements

    • Migrated SDK and state pagination from offset-based to cursor-based for efficient data retrieval.
    • Unified event and entity parsing logic across SDK web and node clients, standardizing data handling.
    • Improved error handling and result reporting in message sending workflows.
    • Refined React subscription hooks with better filtering of invalid entities/events and improved state updates.
    • Simplified client initialization by replacing asynchronous creation with direct instantiation.
    • Updated dependencies, scripts, and TypeScript configurations to enhance environment setup and type safety.
    • Updated query builder to use cursor-based pagination with enhanced filtering and ordering capabilities.
    • Streamlined SDK methods by removing deprecated parameters and unifying historical event handling.
    • Enhanced example Node worker with modular async worker setup, historical event processing, and offchain position count publishing.
  • Bug Fixes

    • Excluded entities and events with zero IDs in React hooks to prevent invalid data processing.
  • Chores

    • Documented minor version bumps for multiple packages.
    • Cleaned up internal code structure and updated dependencies for consistency and maintainability.

Copy link

coderabbitai bot commented Apr 28, 2025

Walkthrough

This update introduces significant refactoring and feature enhancements across the Dojo SDK and related packages. The core changes include a transition from offset-based to cursor-based pagination in queries, the addition of new utility modules for deep merging Cairo types, and the introduction of improved error handling using the neverthrow library. The SDK's message sending and typed data generation APIs have been revised for greater flexibility and robustness. Entity and event parsing functions now operate on arrays instead of keyed objects, and the Zustand state management logic leverages the new deep merge utilities. Several new TypeScript modules are added for model management and worker process orchestration. Example projects are updated to reflect these architectural changes, with new scripts, environment handling, and improved contract interaction code.

Changes

Files/Paths Change Summary
.changeset/thirty-cheetahs-design.md Added a changeset documenting a minor version bump for multiple @dojoengine packages.
examples/example-node-worker/env.ts, examples/example-node-worker/package.json Added environment variable validation with zod and dotenv; updated dependencies and scripts.
examples/example-node-worker/main.ts, examples/example-node-worker/dojoConfig.ts Refactored worker logic for entity updates, SDK initialization, and offchain messaging; clarified JSON import.
examples/example-node-worker/src/typescript/contracts.gen.ts, examples/example-node-worker/src/typescript/models.gen.ts Added generated TypeScript modules for contract actions and strongly-typed models.
examples/example-vite-react-app-recs/src/dojo/setup.ts, examples/example-vite-react-sdk/src/historical-events.tsx Updated imports and client instantiation patterns; switched to historical query builder.
packages/sdk/package.json Added new dependencies: neverthrow, @types/node.
packages/sdk/src/__tests__/zustand.perf.test.ts Reformatted property access for readability.
packages/sdk/src/internal/errors.ts New module exporting error message constants for SDK initialization and account state.
packages/sdk/src/internal/generateTypedData.ts Extended generateTypedData to accept additional type definitions for typed data.
packages/sdk/src/internal/merge.ts New module providing deep merge utilities for Cairo types and nested models.
packages/sdk/src/internal/models.ts New module for retrieving models by entity ID or model name from query results.
packages/sdk/src/internal/parseEntities.ts Updated entity/event parsing to operate on arrays instead of keyed objects; added debug logging and improved primitive parsing.
packages/sdk/src/internal/parseHistoricalEvents.ts Deleted module that parsed historical events and ordered keys.
packages/sdk/src/internal/toriiQueryBuilder.ts Refactored to use cursor-based pagination, added direction/cursor methods, introduced HistoricalToriiQueryBuilder.
packages/sdk/src/internal/types.ts Updated SDK interfaces: improved error handling, added optional signer/identity, deprecated historical flag.
packages/sdk/src/node/index.ts, packages/sdk/src/web/experimental/index.ts, packages/sdk/src/web/index.ts Refactored client instantiation, unified entity/event parsing, improved error handling, updated message sending and typed data APIs.
packages/sdk/src/node/worker.ts New module for creating worker processes with subscription cleanup on termination.
packages/sdk/src/web/react/hooks/entities.ts, packages/sdk/src/web/react/hooks/events.ts, packages/sdk/src/web/react/hooks/hooks.ts Refactored React hooks: updated data processing logic, improved filtering, and state updates.
packages/sdk/src/web/state/zustand.ts Replaced local deep merge logic with imported utilities; adjusted model merging structure.
packages/state/src/recs/index.ts Switched from offset- to cursor-based pagination, updated entity/event fetching and database insertion logic.
packages/torii-wasm/dojo.c Updated subproject commit reference.
examples/example-node-worker/tsconfig.json Added TypeScript configuration for example-node-worker project.
.github/workflows/ci.yaml Updated CI workflow to use a newer version of dojoup.
examples/example-nodejs-bot/package.json Removed test script from package.json.
package.json Added test and test:watch scripts using turbo.
packages/core/package.json Modified test script to allow passing with no tests.
packages/react/package.json Changed test script to run Vitest with --passWithNoTests; added Vitest dev dependency.
packages/sdk/src/__tests__/parseEntities.test.ts Updated test inputs from keyed objects to arrays for entity parsing tests.
packages/sdk/src/__tests__/parseHistoricalEvents.test.ts Deleted tests for parseHistoricalEvents and orderKeys.
packages/sdk/src/__tests__/toriiQueryBuilder.test.ts Updated tests to reflect new pagination and query builder API changes.
packages/torii-client/package.json Modified test script to allow passing with no tests; added Vitest dev dependency.
turbo.json Added test and test:watch tasks to Turbo configuration.

Sequence Diagram(s)

sequenceDiagram
    participant App
    participant SDK
    participant ToriiClient
    participant Worker
    participant State

    App->>SDK: Initialize with config (signer, identity, etc.)
    SDK->>ToriiClient: new ToriiClient(config)
    App->>SDK: Query or subscribe to entities/events (with cursor-based pagination)
    SDK->>ToriiClient: getEntities/getEventMessages({pagination: {cursor, limit, direction}})
    ToriiClient-->>SDK: { items: [entities/events], next_cursor }
    SDK->>State: parseEntities(items)
    App->>Worker: createWorker(callback)
    Worker->>SDK: Subscribe to updates
    SDK->>Worker: onEntityUpdated callback (array of entities)
    Worker->>SDK: publishOffchainPositionCount (if needed)
    SDK->>ToriiClient: sendMessage (with typed data, using signer/identity)
    ToriiClient-->>SDK: Result<Uint8Array, string>
    Worker->>State: update state with merged entities (deepMerge)
    Note over Worker: On SIGTERM/SIGINT, Worker frees subscriptions
Loading

Possibly related PRs

Poem

Hopping through code with a cursor in paw,
I merge all my models without a flaw.
Entities in arrays, events in a stream,
Deep merges and workers—what a dream!
With neverthrow errors and types that delight,
This bunny’s codebase is feeling just right.
🐇✨


🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Inline review comments failed to post. This is likely due to GitHub's limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 17

🔭 Outside diff range comments (4)
packages/sdk/src/internal/generateTypedData.ts (1)

4-12: 🛠️ Refactor suggestion

Update JSDoc to match current function signature

The JSDoc still references a [domain] optional parameter (which still exists in the code), but doesn't document the new additionnalTypes parameter. Update the documentation to accurately reflect all parameters.

 /**
  * Generates typed data for any user-defined message.
  *
  *
  * @template M - The message type defined by the schema models.
  * @param {string} nsModel - Model name prefixed with namespace joined by a hyphen.
  * @param {M} message - The user-defined message content, must be part of the schema models.
  * @param {StarknetDomain} [domain] - The domain object. If not provided, uses the default domain from options.
+ * @param {Array<{ name: string; type: string }>} [modelMapping] - Custom type mapping for message fields.
+ * @param {Record<string, Array<{ name: string; type: string }>>} [additionalTypes] - Additional types to include in the typed data.
  * @returns {TypedData} - The generated typed data.
  */
packages/state/src/recs/index.ts (1)

518-554: ⚠️ Potential issue

insertEntitiesInDB type & logic diverge
The function now accepts entities: ToriiEntity[] but still iterates as if it were a map:

for (const [entityId, data] of Object.entries(entities)) {  }

When entities is an array, entityId becomes "0" | "1" | …, not the real entity hash, resulting in incorrect primary keys and data corruption.

Either:

- for (const [entityId, data] of Object.entries(entities)) {
+ for (const entity of entities) {
+     const { id: entityId, ...data } = entity;

or revert the parameter back to an object.

packages/sdk/src/web/index.ts (1)

257-283: ⚠️ Potential issue

Return err() instead of throwing to stay consistent with Result API

sendMessage advertises a Result<Uint8Array, string> return type, yet the catch branch still throws, which will break callers that rely on isOk()/isErr() handling instead of a try/​catch.

-            } catch (error) {
-                console.error("Failed to send message:", error);
-                throw error;
+            } catch (error) {
+                const msg =
+                    error instanceof Error ? error.message : String(error);
+                console.error("Failed to send message:", msg);
+                return err(msg);
             }
packages/sdk/src/node/index.ts (1)

240-269: ⚠️ Potential issue

sendMessage should propagate failures through err() instead of exceptions

Same rationale as the web SDK: throwing breaks the Result contract.

-            } catch (error) {
-                console.error("Failed to send message:", error);
-                throw error;
+            } catch (error) {
+                const msg =
+                    error instanceof Error ? error.message : String(error);
+                console.error("Failed to send message:", msg);
+                return err(msg);
             }
🧹 Nitpick comments (25)
examples/example-vite-react-sdk/src/historical-events.tsx (3)

1-5: Update imports to use type imports where appropriate.

Some named imports are only used as types according to the static analysis hint.

-import {
-    KeysClause,
-    ParsedEntity,
-    HistoricalToriiQueryBuilder,
-} from "@dojoengine/sdk";
+import {
+    KeysClause,
+    HistoricalToriiQueryBuilder,
+} from "@dojoengine/sdk";
+import type { ParsedEntity } from "@dojoengine/sdk";
🧰 Tools
🪛 Biome (1.9.4)

[error] 1-5: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)


33-35: Consider addressing TypeScript errors properly.

The code contains a @ts-ignore comment that suppresses TypeScript errors. Consider addressing the underlying type issue properly instead of ignoring it.

Consider providing proper type definitions or using explicit type casting if appropriate:

-            {/* @ts-ignore */}
-            {events.map((e: ParsedEntity<SchemaType>, key) => {
+            {events.map((e: ParsedEntity<SchemaType>, key: number) => {
                return <Event event={e} key={key} />;
            })}

Or if necessary, add a more descriptive reason for the type suppression:

-            {/* @ts-ignore */}
+            {/* @ts-ignore - events is correctly typed but TypeScript has issues with the mapping */}
🧰 Tools
🪛 Biome (1.9.4)

[error] 34-34: Avoid using the index of an array as key property in an element.

This is the source of the key value.

The order of the items may change, and this also affects performances and component state.
Check the React documentation.

(lint/suspicious/noArrayIndexKey)


49-50: Consider addressing TypeScript errors properly.

Similarly, there's another TypeScript error suppression with @ts-expect-error. It would be better to fix the underlying type issue or provide a more descriptive comment explaining why the suppression is necessary.

-                {/* @ts-expect-error type is ok here */}
+                {/* @ts-expect-error - direction is correctly typed as an enum or string but TypeScript can't infer it */}
                <div>Direction: {direction}</div>
packages/sdk/src/__tests__/zustand.perf.test.ts (1)

131-133: Consider using optional chaining instead of non-null assertions

The formatting change improves readability of the deeply nested property access, but the code still contains non-null assertions that could be replaced with optional chaining for safer access.

-                        draft.entities[
-                            "entity500"
-                        ].models.world!.item!.durability = 75;
+                        draft.entities["entity500"].models.world?.item?.durability = 75;

Also, you could simplify the computed property access:

-                        draft.entities[
-                            "entity500"
-                        ].models.world?.item?.durability = 75;
+                        draft.entities.entity500.models.world?.item?.durability = 75;
🧰 Tools
🪛 Biome (1.9.4)

[error] 131-133: Forbidden non-null assertion.

(lint/style/noNonNullAssertion)


[error] 131-133: Forbidden non-null assertion.

Unsafe fix: Replace with optional chain operator ?. This operator includes runtime checks, so it is safer than the compile-only non-null assertion operator

(lint/style/noNonNullAssertion)


[error] 132-132: The computed expression can be simplified without the use of a string literal.

Unsafe fix: Use a literal key instead.

(lint/complexity/useLiteralKeys)

packages/sdk/src/internal/parseHistoricalEvents.ts (1)

40-42: Added debug logging

These additional logging statements will help with debugging but might clutter the console in production environments. Consider adding a more specific debug flag rather than using the general options?.logging condition.

+            if (options?.debugging) {
                console.log(model);
                console.log(entityId, modelName, modelData);
+            }
packages/sdk/src/node/worker.ts (1)

30-43: Refactor duplicated signal handling code

The signal handling code for SIGTERM and SIGINT is duplicated. Consider extracting this into a dedicated cleanup function to improve maintainability and reduce duplication.

 export async function createWorker(
     callback: DojoWorkerCallback
 ): Promise<void> {
     const sub = await callback();
 
-    process.on("SIGTERM", () => {
-        for (const s of sub) {
-            s.free();
-        }
-
-        process.exit(0);
-    });
-
-    process.on("SIGINT", () => {
-        for (const s of sub) {
-            s.free();
-        }
-        process.exit(0);
-    });
+    const cleanup = () => {
+        for (const s of sub) {
+            s.free();
+        }
+        process.exit(0);
+    };
+
+    process.on("SIGTERM", cleanup);
+    process.on("SIGINT", cleanup);
 }
packages/sdk/src/internal/generateTypedData.ts (1)

21-21: Fix typo in parameter name "additionnalTypes"

The parameter name contains a spelling error - it should be "additionalTypes" with a single 'n'.

-    additionnalTypes?: Record<string, Array<{ name: string; type: string }>>
+    additionalTypes?: Record<string, Array<{ name: string; type: string }>>

Also update the usage at line 31:

-            ...additionnalTypes,
+            ...additionalTypes,
packages/sdk/src/web/experimental/index.ts (2)

30-30: Remove inferrable type annotation

The type annotation on the historical parameter is trivially inferred from its initialization. You can safely remove it.

-        getEvents: async (query: torii.Query, historical: boolean = false) => {
+        getEvents: async (query: torii.Query, historical = false) => {
🧰 Tools
🪛 Biome (1.9.4)

[error] 30-30: This type annotation is trivially inferred from its initialization.

Safe fix: Remove the type annotation.

(lint/style/noInferrableTypes)


103-103: Avoid using any type for entity data

The entityData parameter is typed as any, which bypasses type checking. Consider using a more specific type like torii.Entity for consistency with the entity update handler.

-                    (_: string, entityData: any) => {
+                    (_: string, entityData: torii.Entity) => {
🧰 Tools
🪛 Biome (1.9.4)

[error] 103-103: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

packages/sdk/src/web/react/hooks/events.ts (1)

70-75: Consider adding error handling for setEvents

When updating the events state with new events, you should consider adding error handling in case the filtering or state update operations fail.

 processUpdateData: (data) => {
     if (data) {
-        const evts = data.filter(
-            (e) => Number.parseInt(e.entityId, 16) !== 0
-        );
-        setEvents((ev) => [...evts, ...ev]);
+        try {
+            const evts = data.filter(
+                (e) => Number.parseInt(e.entityId, 16) !== 0
+            );
+            setEvents((ev) => [...evts, ...ev]);
+        } catch (error) {
+            console.error("Error processing event update:", error);
+        }
     }
 },
packages/sdk/src/internal/parseEntities.ts (2)

76-77: Remove stray debug log

A hard-coded console.log(value); will spam production logs. Gate this behind options?.logging or delete it.

-            console.log(value);
+            if (process.env.NODE_ENV !== "production" && options?.logging) {
+                console.debug(value);
+            }

58-59: Redundant Object.values call

result is already an array; wrapping it in Object.values adds an unnecessary copy.

-    return Object.values(result);
+    return result;
examples/example-node-worker/src/typescript/contracts.gen.ts (1)

1-3: Import unused / type-only symbols with import type

BigNumberish, ByteArray, CairoOption, and several others are never used at runtime.
Switching to import type (and deleting unused names) removes dead code from bundles and appeases Biome.

-import { DojoProvider, DojoCall } from "@dojoengine/core";
-import { Account, AccountInterface, BigNumberish, CairoOption, CairoCustomEnum, ByteArray } from "starknet";
+import type { DojoProvider, DojoCall } from "@dojoengine/core";
+import type { Account, AccountInterface, CairoCustomEnum } from "starknet";

Also delete the now-unused BigNumberish, ByteArray, and CairoOption imports entirely.

🧰 Tools
🪛 Biome (1.9.4)

[error] 1-1: All these imports are only used as types.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Use import type.

(lint/style/useImportType)


[error] 2-2: Some named imports are only used as types.

This import is only used as a type.

This import is only used as a type.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

packages/sdk/src/web/state/zustand.ts (1)

14-15: Use import type for type-only symbol

MergedModels is only used in the type position; importing it as a value keeps unnecessary code.

-import { deepMerge, MergedModels } from "../../internal/merge";
+import { deepMerge } from "../../internal/merge";
+import type { MergedModels } from "../../internal/merge";
🧰 Tools
🪛 Biome (1.9.4)

[error] 14-14: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

packages/sdk/src/internal/types.ts (1)

361-366: Docs & overload drift – additionalTypes needs to be reflected in JSDoc and overloads
The signature gained an additionalTypes parameter but the surrounding comment block (lines 341-356) still documents the old domain parameter and doesn’t mention the new argument. Consumers relying on IntelliSense / typedoc will get stale guidance.

- * @param {Array<{ name: string; type: string }>} [modelMapping]
- * @param {Record<string, Array<{ name: string; type: string }>>} [additionalTypes]
+ * @param {Array<{ name: string; type: string }>} [modelMapping] - …
+ * @param {Record<string, Array<{ name: string; type: string }>>} [additionalTypes] - Extra EIP-712 type groups

Please update the docblock (and any overloads/implementations) for parity.

packages/sdk/src/internal/models.ts (1)

38-52: Stale JSDoc & “first entity wins” logic
The docstring repeats the “entityId” parameter even though this overload does not receive one. Additionally, for…of returns on the very first entity, regardless of whether that entity actually contains the requested model. Consider scanning until a hit is found or using Array.prototype.find.

- * @param entityId - The ID of the entity.

Also apply the same generic-type fix as above.

examples/example-node-worker/main.ts (1)

120-120: Use const (or mutate) – positionCount is never reassigned

Static analysis is correct: the variable is declared with let but never updated.
Either change it to const if it is meant to be immutable, or actually mutate it when events arrive (the commented-out block shows the intent).

-let positionCount = defaultPositionCount;
+const positionCount = defaultPositionCount;
🧰 Tools
🪛 Biome (1.9.4)

[error] 119-120: This let declares a variable that is only assigned once.

'positionCount' is never reassigned.

Safe fix: Use const instead.

(lint/style/useConst)

packages/sdk/src/internal/merge.ts (1)

74-109: Avoid any in new enum objects – keeps the strong typing you added elsewhere

newEnumContent is currently typed as Record<string, any> — this silences type-checking and can leak invalid values.

-const newEnumContent: Record<string, any> = {};
+const newEnumContent: { [K in keyof typeof target.variant]?: unknown } = {};

Repeat the same change in the target-branch block (lines 96-105) and in the generic deepMerge result map (line 133).

Using precise types improves IntelliSense and prevents accidental insertion of non-serialisable values.

🧰 Tools
🪛 Biome (1.9.4)

[error] 76-76: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)


[error] 96-96: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

examples/example-node-worker/src/typescript/models.gen.ts (2)

3-3: Mark StarkNet imports that are types with import type

BigNumberish is a type-only import, so using import type prevents unnecessary runtime dependency loading and keeps emitted code lean.

-import { CairoCustomEnum, CairoOption, CairoOptionVariant, BigNumberish } from 'starknet';
+import type { BigNumberish } from 'starknet';
+import { CairoCustomEnum, CairoOption, CairoOptionVariant } from 'starknet';
🧰 Tools
🪛 Biome (1.9.4)

[error] 3-3: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)


70-79: Consider exporting a union literal instead of a run-time constant for directions

The current pattern

export const direction = ['Left', 'Right', 'Up', 'Down'] as const;
export type Direction = { [key in typeof direction[number]]: string };

creates an object-map type rather than a string union ('Left' | 'Right' | ...).
If the intention was a simple enum of string literals, a cleaner approach is:

export type Direction = 'Left' | 'Right' | 'Up' | 'Down';

or leverage keyof typeof over a readonly object.

Doing so simplifies downstream discriminated-union checks and narrows allowable values at compile time.

packages/sdk/src/web/index.ts (1)

45-49: await is redundant when calling new ToriiClient

new is a synchronous constructor; the extra await just adds an unnecessary micro-task. If the constructor later becomes async, the signature can be revisited, but today this hurts perf slightly and confuses the reader.

-    return await new torii.ToriiClient(config);
+    return new torii.ToriiClient(config);
packages/sdk/src/internal/toriiQueryBuilder.ts (2)

56-63: Misleading JSDoc: comment says “Set the maximum number of results” but method sets direction

The copy-paste description for withDirection is incorrect and will confuse IDE tooltips & generated docs.


145-158: Duplicated defaultToriiOptions() merge in HistoricalToriiQueryBuilder

ToriiQueryBuilder already merges defaultToriiOptions(). Re-applying the spread produces an extra object allocation and may accidentally overwrite user-supplied options.

-        super({
-            ...(defaultToriiOptions() as Query),
-            ...options,
-            historical: true,
-        });
+        super({ ...options, historical: true });
packages/sdk/src/node/index.ts (2)

215-223: Inconsistent spelling: additionalTypes vs additionnalTypes

generateTypedData expects the 5-th positional argument to be additionnalTypes; inadvertent drift in spelling can confuse readers and tooling. Align the variable name with the callee (or better, rename the parameter in the helper to the correct English spelling).

-            additionalTypes?: Record<
+            additionnalTypes?: Record<

18-20: Remove unused Account import

The node variant now relies on options.signer and no longer uses an Account instance; the leftover import increases bundle size and may mislead maintainers.

-import type { Account, TypedData } from "starknet";
+import type { TypedData } from "starknet";
🛑 Comments failed to post (17)
packages/sdk/src/internal/parseHistoricalEvents.ts (1)

19-20: ⚠️ Potential issue

Mismatched parameter type with function implementation

The parameter type has been updated to accept an array (torii.Entity[]) but the function implementation on line 27 still uses a for...in loop which is designed for iterating over object properties, not array indices. This will cause incorrect behavior.

Apply this diff to fix the iteration logic:

export function parseHistoricalEvents<T extends SchemaType>(
    entities: torii.Entity[],
    options?: { logging?: boolean }
): StandardizedQueryResult<T> {
    if (options?.logging) {
        console.log("Raw historical events", entities);
    }
    // Events come from torii flagged as "dojo_starter-Moved-idx"
    let events: StandardizedQueryResult<T> = [];
-    for (const entityId in entities) {
+    for (const entity of entities) {
+        const entityId = entity.id; // Assuming entity has an id property
        console.log(entityId);
-        const entityData = entities[entityId];
+        const entityData = entity;

Also, update the JSDoc comment on line 10 to match the new parameter type:

- * @param {torii.Entities} entities - The collection of entities to parse.
+ * @param {torii.Entity[]} entities - The array of entities to parse.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

/**
 * Parses historical events.
 * @param {torii.Entity[]} entities - The array of entities to parse.
 * @param {{ logging?: boolean }} [options] - Optional settings.
 */
export function parseHistoricalEvents<T extends SchemaType>(
    entities: torii.Entity[],
    options?: { logging?: boolean }
): StandardizedQueryResult<T> {
    if (options?.logging) {
        console.log("Raw historical events", entities);
    }
    // Events come from torii flagged as "dojo_starter-Moved-idx"
    let events: StandardizedQueryResult<T> = [];
    for (const entity of entities) {
        const entityId = entity.id; // Assuming entity has an id property
        console.log(entityId);
        const entityData = entity;
        // …rest of your processing logic for each entityData…
    }
    return events;
}
packages/sdk/src/web/react/hooks/hooks.ts (1)

114-117: ⚠️ Potential issue

Resource cleanup is commented out

The subscription cleanup code has been commented out, which could lead to memory leaks or unwanted updates after component unmount. Subscriptions should be properly cleaned up when the component unmounts.

Uncomment and update the cleanup code:

return () => {
    if (subscriptionRef.current) {
-        // subscriptionRef.current?.free();
-        // subscriptionRef.current = null;
+        subscriptionRef.current?.free();
+        subscriptionRef.current = null;
    }
};
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

return () => {
    if (subscriptionRef.current) {
        subscriptionRef.current?.free();
        subscriptionRef.current = null;
    }
};
examples/example-node-worker/package.json (1)

6-9: 🛠️ Refactor suggestion

Security: Hardcoded credentials in scripts

The scripts include hardcoded credentials (IDENTITY and SECRET_KEY) which is a security risk, even for examples. These should be moved to environment variables loaded from a .env file.

"scripts": {
-    "bun": "IDENTITY=0x0127fd5f1fe78a71f8bcd1fec63e3fe2f0486b6ecd5c86a0466c3a21fa5cfcec SECRET_KEY=0xc5b2fcab997346f3ea1c00b002ecf6f382c5f9c9659a3894eb783c5320f912 bun run main.ts",
-    "node": "IDENTITY=0x0127fd5f1fe78a71f8bcd1fec63e3fe2f0486b6ecd5c86a0466c3a21fa5cfcec SECRET_KEY=0xc5b2fcab997346f3ea1c00b002ecf6f382c5f9c9659a3894eb783c5320f912 node main.ts"
+    "bun": "bun run main.ts",
+    "node": "node main.ts"
},

Then create a .env.example file (without real credentials) and document the need to create a .env file with proper credentials:

IDENTITY=your_identity_here
SECRET_KEY=your_secret_key_here
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    "scripts": {
        "bun": "bun run main.ts",
        "node": "node main.ts"
    },
packages/sdk/src/web/react/hooks/entities.ts (1)

26-35: ⚠️ Potential issue

Only first entity is processed in updates

The update logic now filters out entities with zero entityId (which is good) but then only processes the first entity from the filtered list. This might ignore valid entities if multiple are received in a single update.

Consider processing all valid entities:

if (data) {
    const entities = data.filter(
        (e) => Number.parseInt(e.entityId, 16) !== 0
    );

-    const entity = entities[0];
-    if (entity) {
-        state.updateEntity(entity);
-    }
+    // Process all valid entities
+    for (const entity of entities) {
+        state.updateEntity(entity);
+    }
}

Alternatively, if batch updates are more efficient:

if (data) {
    const entities = data.filter(
        (e) => Number.parseInt(e.entityId, 16) !== 0
    );

-    const entity = entities[0];
-    if (entity) {
-        state.updateEntity(entity);
-    }
+    if (entities.length > 0) {
+        state.updateEntities(entities);
+    }
}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

            if (data) {
                const entities = data.filter(
                    (e) => Number.parseInt(e.entityId, 16) !== 0
                );

                // Process all valid entities
                for (const entity of entities) {
                    state.updateEntity(entity);
                }
            }
packages/sdk/src/node/worker.ts (1)

2-2: 🛠️ Refactor suggestion

Remove commented-out import or explain why it's kept

There's a commented-out import for process from node:process, but your code uses the process object multiple times. This creates ambiguity about whether you're relying on the global Node.js process object or intending to use an imported one.

Either remove the commented code if you're intentionally using the global process object, or uncomment it if you meant to use the explicit import.

-// import process from "node:process";
+import process from "node:process";
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

import process from "node:process";
packages/sdk/src/web/experimental/index.ts (1)

41-41: 💡 Verification agent

🧩 Analysis chain

Renamed query flag from dont_include_hashed_keys to no_hashed_keys

The query property for excluding hashed keys has been renamed. Ensure this change is documented in the migration guide for users upgrading from previous versions.

Also applies to: 84-84


🏁 Script executed:

#!/bin/bash
# Check if both property names are still used in the codebase

echo "Checking for usage of old property name 'dont_include_hashed_keys':"
rg "dont_include_hashed_keys" --type ts

echo -e "\nChecking for usage of new property name 'no_hashed_keys':"
rg "no_hashed_keys" --type ts

Length of output: 1573


🏁 Script executed:

#!/bin/bash
# Locate migration guide markdown files
echo "Searching for migration guide files:"
rg -l "migration" -g "*.md"

# Check for references to old and new flags in markdown
echo -e "\nReferences to 'dont_include_hashed_keys' in markdown:"
rg "dont_include_hashed_keys" -g "*.md"

echo -e "\nReferences to 'no_hashed_keys' in markdown:"
rg "no_hashed_keys" -g "*.md"

Length of output: 451


Ensure Migration Guide & Tests Updated for Renamed Flag
The query option dont_include_hashed_keys has been replaced by no_hashed_keys, but:

• The migration guide (migration/v1-0-v1-1.md) doesn’t mention this rename.
• The tests in packages/sdk/src/__tests__/toriiQueryBuilder.test.ts still reference dont_include_hashed_keys.

Please:

  • Add a note to migration/v1-0-v1-1.md describing the rename and its impact on user code.
  • Update all occurrences of dont_include_hashed_keys in your tests and examples to no_hashed_keys.
packages/sdk/src/internal/parseEntities.ts (1)

129-135: ⚠️ Potential issue

u64 should be parsed as BigInt, not number

parsePrimitive converts every u64 hex string into a JS number (Number.parseInt).
Many on-chain u64 values overflow Number.MAX_SAFE_INTEGER, so precision will be silently lost.

-        case "u64":
-            return Number.parseInt(value.value as string, 16);
+        case "u64":
+            // Preserve 64-bit precision
+            return BigInt(value.value as string);
packages/sdk/src/web/state/zustand.ts (1)

71-98: ⚠️ Potential issue

Incorrect deep-merge granularity may overwrite sibling models

mergeEntities iterates over every model inside a namespace and calls

mergedModels[namespace] = deepMerge(mergedModels[namespace], namespaceModels);

where namespaceModels is a single model value, not the whole namespace object.
Each iteration therefore merges the previous namespace object with a scalar, so the final loop pass leaves only the last model, dropping its siblings.

Replace the nested loop with a single merge per namespace:

-for (const namespace of Object.keys(entity.models)) {
-  for (const model of Object.keys(entity.models[namespace])) {
-      const namespaceModels = entity.models[namespace][model];
-
-      mergedModels[typedNamespace] = deepMerge(
-          mergedModels[typedNamespace] as MergedModels<T>,
-          namespaceModels           // 🛑 only one model!
-      ) as MergedModels<T>;
-  }
-}
+for (const namespace of Object.keys(entity.models)) {
+    const typedNamespace = namespace as keyof ParsedEntity<T>["models"];
+    mergedModels[typedNamespace] = deepMerge(
+        mergedModels[typedNamespace] as MergedModels<T>,
+        entity.models[namespace]     // ✅ whole namespace object
+    ) as MergedModels<T>;
+}

Make the same change in updateEntity (lines 126-148) for consistency.

packages/sdk/src/internal/types.ts (2)

515-523: ⚠️ Potential issue

Contractual mismatch – field is documented as “required” but typed as optional
The comment states that signer/identity will be required when using off-chain messages, yet both properties are declared optional. Either:

  1. Make them non-optional and supply sensible defaults, or
  2. Keep them optional but clarify that runtime checks will throw if missing.

Current state risks silent undefined access at call-sites.


368-372: 🛠️ Refactor suggestion

Prefer rich errors over plain strings in Result
sendMessage now resolves to Result<Uint8Array, string>. Returning a plain string sacrifices stack traces and makes downstream error-handling brittle (hard to pattern-match reliably).

Recommend:

-) => Promise<Result<Uint8Array, string>>;
+) => Promise<Result<Uint8Array, Error>>;

If a custom error hierarchy exists, expose it here instead of Error.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    sendMessage: (
        data: TypedData,
        account?: Account
    ) => Promise<Result<Uint8Array, Error>>;
packages/sdk/src/internal/models.ts (1)

11-29: ⚠️ Potential issue

Generic type mismatch and unsafe entityId comparison

  1. The generic Schema parameter is declared but never used – return type still hard-codes SchemaType[N][M], breaking type-safety for consumers with custom schemas.
  2. entityId is BigNumberish while v.entityId is a string; strict comparison (!==) will always fail unless caller already stringifies the value.
-export function getModelByEntityId<
-    N extends keyof SchemaType,
-    M extends keyof SchemaType[N] & string,
-    Schema extends SchemaType
+export function getModelByEntityId<
+    Schema extends SchemaType,
+    N extends keyof Schema,
+    M extends keyof Schema[N] & string
>(
-    entityId: BigNumberish,
+    entityId: BigNumberish | string,
     model: `${N}-${M}`,
     value: StandardizedQueryResult<Schema>
-): SchemaType[N][M] | undefined {
+): Schema[N][M] | undefined {
     const [namespace, modelName] = model.split("-") as [N, M];
-    for (const v of value) {
-        if (v.entityId !== entityId) {
+    const idStr = entityId.toString();
+    for (const v of value) {
+        if (v.entityId !== idStr) {
             continue;
         }
-        return v.models?.[namespace]?.[modelName] as
-            | SchemaType[N][M]
+        return v.models?.[namespace]?.[modelName] as
+            | Schema[N][M]
             | undefined;
     }
}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

export function getModelByEntityId<
    Schema extends SchemaType,
    N extends keyof Schema,
    M extends keyof Schema[N] & string
>(
    entityId: BigNumberish | string,
    model: `${N}-${M}`,
    value: StandardizedQueryResult<Schema>
): Schema[N][M] | undefined {
    const [namespace, modelName] = model.split("-") as [N, M];
    const idStr = entityId.toString();
    for (const v of value) {
        if (v.entityId !== idStr) {
            continue;
        }
        return v.models?.[namespace]?.[modelName] as
            | Schema[N][M]
            | undefined;
    }
}
packages/state/src/recs/index.ts (2)

181-190: ⚠️ Potential issue

entities.items is now an array — update downstream consumers
The new SDK returns items: ToriiEntity[], yet:

setEntities(entities.items, components, logging);          // expects object
if (Object.keys(entities.items).length < limit) {  }      // treating as map

setEntities and the length check both assume an object keyed by entityId, causing:

• Incorrect entity IDs (array indices)
• Potential loss of data when two entities share the same index across pages
• Misleading size calculations

Refactor setEntities to accept an array or convert the array to the expected object shape before passing it along.

🧰 Tools
🪛 Biome (1.9.4)

[error] 185-185: Do not use template literals if interpolation and special-character handling are not needed.

Unsafe fix: Replace with string literal

(lint/style/noUnusedTemplateLiteral)


320-334: 🛠️ Refactor suggestion

Pagination loop never advances – constant fetchedEntities
Inside getEntitiesQuery you fetch once before the while, then keep re-using the same fetchedEntities object; cursor is updated but never supplied to a new request, so additional pages are never requested, risking an infinite loop when the first batch size ≥ limit.

Move the fetch inside the loop or convert to a do/while:

- const fetchedEntities = await client.getEntities({ … });
- while (continueFetching) {
+ while (continueFetching) {
+     const fetchedEntities = await client.getEntities({ …, cursor });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    while (continueFetching) {
        const fetchedEntities = await client.getEntities({ …, cursor });

        if (logging)
            console.log(
                `Fetched ${Object.keys(fetchedEntities.items).length} entities ${fetchedEntities.next_cursor}`
            );

        setEntities(fetchedEntities.items, components, logging);

        if (Object.keys(fetchedEntities.items).length < limit) {
            continueFetching = false;
        } else {
            cursor = fetchedEntities.next_cursor;
        }
    }
};
examples/example-node-worker/main.ts (2)

79-98: ⚠️ Potential issue

publishOffchainPositionCount always sends zeroes – the function uses a stale global instead of live data

Inside the payload you pass

position: intoPositionCount(positionCount)

but positionCount is the module-level default that is never mutated anywhere after its initial declaration.
Hence every message published from this worker will contain only zero counts, regardless of actual moves.

Two possible fixes:

-                position: intoPositionCount(positionCount),
+                // TODO: compute the real count from the current `moves`
+                position: intoPositionCount(calculateNewCount(moves)),

or, if you intend to keep the accumulator:

-                position: intoPositionCount(positionCount),
+                position: intoPositionCount(currentPositionCount),

where currentPositionCount is updated inside onEntityUpdated.

Failing to address this will make off-chain consumers rely on incorrect game state.


21-25: 💡 Verification agent

🧩 Analysis chain

global.Websocket is probably a typo – use global.WebSocket (capital “S”)

Most libraries (including isomorphic-ws, websocket, and the browser spec) expose the constructor as WebSocket, not Websocket.
Setting the wrong global symbol means downstream code that expects global.WebSocket will silently fall back to the Node TCP socket implementation or crash at runtime.

-// @ts-ignore
-global.Websocket = w3cwebsocket;
+// @ts-ignore – make Ws available globally for SDK internals
+// NOTE: the correct global symbol is “WebSocket” with a capital “S”.
+global.WebSocket = w3cwebsocket;

🏁 Script executed:

#!/bin/bash
set -e
echo "Searching for global.Websocket usage..."
rg "global\.Websocket" -n
echo "Searching for global.WebSocket usage..."
rg "global\.WebSocket" -n

Length of output: 315


Use the correct global.WebSocket (capital “S”)

The browser spec and most libraries (e.g. isomorphic-ws, websocket) expose the constructor as WebSocket, not Websocket. Downstream code that looks for global.WebSocket will otherwise break or fall back to a TCP socket at runtime.

Locations needing updates:

  • examples/example-node-worker/main.ts: replace the global.Websocket assignment
-// @ts-ignore
-global.Websocket = w3cwebsocket;
+// @ts-ignore – make WebSocket available globally for SDK internals
+// NOTE: the correct global symbol is “WebSocket” with a capital “S”.
+global.WebSocket = w3cwebsocket;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

// Those lines are require so that websocket works.
// @ts-ignore – make WebSocket available globally for SDK internals
// NOTE: the correct global symbol is “WebSocket” with a capital “S”.
global.WebSocket = w3cwebsocket;
// @ts-ignore
global.WorkerGlobalScope = global;
examples/example-node-worker/src/typescript/models.gen.ts (1)

38-46: ⚠️ Potential issue

Typo: posititionposition everywhere

The field is misspelled in the interface, value interface, and default schema:

-positition: Array<[BigNumberish, BigNumberish]>;
+position: Array<[BigNumberish, BigNumberish]>;

This typo propagates to runtime code (main.ts expects position) and will cause type errors as well as undefined fields in JSON serialisation.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

export interface PositionCount {
	identity: string;
	position: Array<[BigNumberish, BigNumberish]>;
}

// Type definition for `dojo_starter::models::PositionCountValue` struct
export interface PositionCountValue {
	position: Array<[BigNumberish, BigNumberish]>;
}
packages/sdk/src/internal/toriiQueryBuilder.ts (1)

130-142: 🛠️ Refactor suggestion

Tighten generics & allow undefined cursor in withPagination

  1. Record<string, Record<string, any>> triggers the noExplicitAny lint error.
  2. Cursor can legitimately be undefined (start-from-beginning scenario).
-static withPagination<T extends Record<string, Record<string, any>>>(
-    cursor: string,
+static withPagination<T extends SchemaType>(
+    cursor: string | undefined,
     limit: number,
     direction: PaginationDirection
 ): ToriiQueryBuilder<T> {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

     * Create a new builder instance with pagination settings
     *
     */
    static withPagination<T extends SchemaType>(
        cursor: string | undefined,
        limit: number,
        direction: PaginationDirection
    ): ToriiQueryBuilder<T> {
        return new ToriiQueryBuilder<T>()
            .withLimit(limit)
            .withCursor(cursor)
            .withDirection(direction);
    }
🧰 Tools
🪛 Biome (1.9.4)

[error] 133-133: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 11

🔭 Outside diff range comments (2)
packages/sdk/src/web/state/zustand.ts (1)

67-99: 🛠️ Refactor suggestion

Nested merge loop can be greatly simplified

The current triple-nested loops plus repeated Object.assign make the path easy to break and hard to read. Because you already have deepMerge, you can merge the entire models object in one shot:

-// Create new models object without spread
-const mergedModels: typeof existingEntity.models = Object.assign({}, existingEntity.models);
-// Iterate through each namespace …
-… deepMerge(…)
+const mergedModels = deepMerge(
+  existingEntity.models as MergedModels<T>,
+  entity.models as MergedModels<T>
+);

This removes ~25 lines, eliminates the @ts-expect-errors and prevents accidental shadowing of model/namespace.

packages/state/src/recs/index.ts (1)

516-554: ⚠️ Potential issue

insertEntitiesInDB iterates an array as an object

With cursor pagination every batch is ToriiEntity[].
Object.entries(entities) turns each entity into [index, value], so entityId is the numeric string index, not the actual ID.

-for (const [entityId, data] of Object.entries(entities)) {
-  const entityData = { id: entityId, ...data };
-  store.put(entityData);
-}
+for (const entity of entities) {
+  store.put(entity);
+}

Without this fix the IndexedDB store gets incorrect IDs and overwrites rows.

🧹 Nitpick comments (13)
examples/example-vite-react-sdk/src/historical-events.tsx (1)

1-5: Imports improved with specialized query builder

The import of HistoricalToriiQueryBuilder instead of ToriiQueryBuilder is a good change, making the code more semantically clear about its intention to query historical data.

Consider using explicit type imports as suggested by Biome:

import {
    KeysClause,
-   ParsedEntity,
+   type ParsedEntity,
    HistoricalToriiQueryBuilder,
} from "@dojoengine/sdk";
🧰 Tools
🪛 Biome (1.9.4)

[error] 1-5: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

packages/sdk/src/web/experimental/index.ts (1)

103-103: Consider replacing any type with a more specific type

Using any disables type checking, which could lead to potential runtime errors.

-                    (_: string, entityData: any) => {
+                    (_: string, entityData: torii.Entity) => {
🧰 Tools
🪛 Biome (1.9.4)

[error] 103-103: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

packages/sdk/src/internal/parseEntities.ts (2)

58-58: Simplify return statement

Since result is now an array, using Object.values() is redundant and could be simplified.

-    return Object.values(result);
+    return result;

76-76: Remove or conditionally execute debug logging

This console.log statement appears to be leftover debug code that should be removed or made conditional based on the options.

-            console.log(value);
+            if (options?.logging) {
+                console.log("Processing enum value:", value);
+            }
examples/example-node-worker/src/typescript/contracts.gen.ts (2)

1-3: Use import type and drop unused symbols

Account, AccountInterface, and CairoCustomEnum are referenced only in type positions, while BigNumberish, CairoOption, and ByteArray are not referenced at all. Importing them as runtime values keeps unnecessary code in the bundle.

-import { DojoProvider, DojoCall } from "@dojoengine/core";
-import { Account, AccountInterface, BigNumberish, CairoOption, CairoCustomEnum, ByteArray } from "starknet";
+import type { Account, AccountInterface, CairoCustomEnum } from "starknet";
+import { DojoProvider, DojoCall } from "@dojoengine/core";

Remove the truly-unused names altogether.

🧰 Tools
🪛 Biome (1.9.4)

[error] 1-1: All these imports are only used as types.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Use import type.

(lint/style/useImportType)


[error] 2-2: Some named imports are only used as types.

This import is only used as a type.

This import is only used as a type.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)


15-26: Return-type annotation improves DX

actions_move currently has no return type, so the inferred type is Promise<any>. Explicitly declaring the expected result from provider.execute surfaces errors earlier and helps autocomplete.

-const actions_move = async (snAccount: Account | AccountInterface, direction: CairoCustomEnum) => {
+const actions_move = async (
+  snAccount: Account | AccountInterface,
+  direction: CairoCustomEnum
+): Promise<Awaited<ReturnType<typeof provider.execute>>> => {

(Replace the placeholder with the concrete type if execute is generically typed.)

packages/sdk/src/web/state/zustand.ts (1)

14-14: Import MergedModels as a type-only symbol

deepMerge is used at runtime, MergedModels is not. Switching to import type for the latter avoids shipping dead code.

-import { deepMerge, MergedModels } from "../../internal/merge";
+import { deepMerge } from "../../internal/merge";
+import type { MergedModels } from "../../internal/merge";
🧰 Tools
🪛 Biome (1.9.4)

[error] 14-14: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

examples/example-node-worker/main.ts (1)

79-118: positionCount is never updated after publishing

publishOffchainPositionCount reads the global positionCount, but neither this function nor onEntityUpdated mutates it for new moves. The payload may therefore become stale and identical messages will be re-sent.

Consider incrementing the relevant field before calling intoPositionCount, e.g.:

-if (!m) {
+if (!m) {
+    positionCount[moves.direction] += 1; // or other logic

Alternatively compute the counts on-demand from historical + live data.

packages/state/src/recs/index.ts (1)

185-185: Unnecessary template literal

"Fetched entities" is a plain string; the template literal signs are redundant and flagged by Biome.

🧰 Tools
🪛 Biome (1.9.4)

[error] 185-185: Do not use template literals if interpolation and special-character handling are not needed.

Unsafe fix: Replace with string literal

(lint/style/noUnusedTemplateLiteral)

packages/sdk/src/internal/merge.ts (2)

63-113: Replace any with more specific types in CairoCustomEnum handling

The implementation logic for merging enum variants is solid, but the use of any reduces type safety.

Consider using more specific types instead of any:

-        const newEnumContent: Record<string, any> = {};
+        const newEnumContent: Record<string, unknown> = {};

This change would maintain the flexibility needed while being slightly more type-safe than any.

🧰 Tools
🪛 Biome (1.9.4)

[error] 76-76: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)


[error] 96-96: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)


123-160: Improve type safety in deepMerge function

The deepMerge function implementation is comprehensive, handling special Cairo types and regular JavaScript objects recursively. However, it uses any which could lead to type safety issues.

Consider using more specific types:

-    const result = { ...target } as Record<string, any>;
+    const result = { ...target } as Record<string, unknown>;

The function correctly prioritizes special Cairo types handling before general object merging, which is an appropriate approach.

🧰 Tools
🪛 Biome (1.9.4)

[error] 133-133: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

examples/example-node-worker/src/typescript/models.gen.ts (1)

1-3: Use import type for type-only imports

Since Cairo types are only used as types in this file, not as values, you should use the import type syntax.

import type { SchemaType as ISchemaType } from "@dojoengine/sdk";

-import { CairoCustomEnum, CairoOption, CairoOptionVariant, BigNumberish } from 'starknet';
+import type { CairoCustomEnum, CairoOption, CairoOptionVariant, BigNumberish } from 'starknet';

This helps TypeScript optimize imports that aren't needed at runtime.

🧰 Tools
🪛 Biome (1.9.4)

[error] 3-3: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

packages/sdk/src/internal/toriiQueryBuilder.ts (1)

133-142: Improve type safety in withPagination static method

The static method has been updated to support cursor-based pagination, but uses any in its type signature.

-    static withPagination<T extends Record<string, Record<string, any>>>(
+    static withPagination<T extends Record<string, Record<string, unknown>>>(

Using unknown instead of any would improve type safety while maintaining flexibility.

🧰 Tools
🪛 Biome (1.9.4)

[error] 133-133: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f4758c1 and eadecf7.

⛔ Files ignored due to path filters (5)
  • examples/example-vite-react-phaser-recs/src/dojo/generated/setup.ts is excluded by !**/generated/**
  • examples/example-vite-react-pwa-recs/src/dojo/generated/setup.ts is excluded by !**/generated/**
  • examples/example-vite-react-threejs-recs/src/dojo/generated/setup.ts is excluded by !**/generated/**
  • examples/example-vue-app-recs/src/dojo/generated/setup.ts is excluded by !**/generated/**
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (30)
  • .changeset/thirty-cheetahs-design.md (1 hunks)
  • examples/example-node-worker/dojoConfig.ts (1 hunks)
  • examples/example-node-worker/env.ts (1 hunks)
  • examples/example-node-worker/main.ts (1 hunks)
  • examples/example-node-worker/package.json (1 hunks)
  • examples/example-node-worker/src/typescript/contracts.gen.ts (1 hunks)
  • examples/example-node-worker/src/typescript/models.gen.ts (1 hunks)
  • examples/example-node-worker/tsconfig.json (1 hunks)
  • examples/example-vite-react-app-recs/src/dojo/setup.ts (2 hunks)
  • examples/example-vite-react-sdk/src/historical-events.tsx (2 hunks)
  • packages/sdk/package.json (2 hunks)
  • packages/sdk/src/__tests__/zustand.perf.test.ts (1 hunks)
  • packages/sdk/src/internal/errors.ts (1 hunks)
  • packages/sdk/src/internal/generateTypedData.ts (2 hunks)
  • packages/sdk/src/internal/merge.ts (1 hunks)
  • packages/sdk/src/internal/models.ts (1 hunks)
  • packages/sdk/src/internal/parseEntities.ts (5 hunks)
  • packages/sdk/src/internal/parseHistoricalEvents.ts (3 hunks)
  • packages/sdk/src/internal/toriiQueryBuilder.ts (6 hunks)
  • packages/sdk/src/internal/types.ts (5 hunks)
  • packages/sdk/src/node/index.ts (12 hunks)
  • packages/sdk/src/node/worker.ts (1 hunks)
  • packages/sdk/src/web/experimental/index.ts (4 hunks)
  • packages/sdk/src/web/index.ts (11 hunks)
  • packages/sdk/src/web/react/hooks/entities.ts (1 hunks)
  • packages/sdk/src/web/react/hooks/events.ts (3 hunks)
  • packages/sdk/src/web/react/hooks/hooks.ts (4 hunks)
  • packages/sdk/src/web/state/zustand.ts (2 hunks)
  • packages/state/src/recs/index.ts (6 hunks)
  • packages/torii-wasm/dojo.c (1 hunks)
✅ Files skipped from review due to trivial changes (4)
  • packages/sdk/src/internal/errors.ts
  • packages/sdk/package.json
  • .changeset/thirty-cheetahs-design.md
  • examples/example-node-worker/tsconfig.json
🚧 Files skipped from review as they are similar to previous changes (14)
  • examples/example-node-worker/dojoConfig.ts
  • packages/torii-wasm/dojo.c
  • examples/example-node-worker/env.ts
  • examples/example-vite-react-app-recs/src/dojo/setup.ts
  • packages/sdk/src/node/worker.ts
  • packages/sdk/src/internal/parseHistoricalEvents.ts
  • packages/sdk/src/internal/generateTypedData.ts
  • examples/example-node-worker/package.json
  • packages/sdk/src/web/react/hooks/entities.ts
  • packages/sdk/src/web/react/hooks/hooks.ts
  • packages/sdk/src/web/react/hooks/events.ts
  • packages/sdk/src/internal/models.ts
  • packages/sdk/src/internal/types.ts
  • packages/sdk/src/web/index.ts
🧰 Additional context used
🧬 Code Graph Analysis (8)
packages/sdk/src/internal/toriiQueryBuilder.ts (3)
packages/sdk/src/internal/types.ts (1)
  • SchemaType (50-70)
examples/example-node-worker/src/typescript/models.gen.ts (2)
  • direction (71-76)
  • SchemaType (80-94)
packages/core/src/provider/DojoProvider.ts (1)
  • model (131-140)
packages/sdk/src/node/index.ts (6)
packages/sdk/src/internal/convertClauseToEntityKeysClause.ts (1)
  • intoEntityKeysClause (4-36)
packages/sdk/src/internal/parseEntities.ts (1)
  • parseEntities (10-59)
packages/sdk/src/internal/types.ts (2)
  • ToriiResponse (248-253)
  • ToriiQueryBuilder (557-557)
packages/sdk/src/internal/generateTypedData.ts (1)
  • generateTypedData (13-48)
packages/sdk/src/internal/errors.ts (2)
  • NO_SIGNER (1-1)
  • NO_IDENTITY (2-2)
packages/sdk/src/internal/toriiQueryBuilder.ts (1)
  • ToriiQueryBuilder (24-143)
examples/example-node-worker/src/typescript/contracts.gen.ts (2)
examples/example-node-worker/src/typescript/models.gen.ts (1)
  • direction (71-76)
packages/core/src/types/index.ts (1)
  • DojoCall (130-134)
examples/example-node-worker/src/typescript/models.gen.ts (1)
packages/sdk/src/internal/types.ts (1)
  • SchemaType (50-70)
examples/example-vite-react-sdk/src/historical-events.tsx (1)
packages/sdk/src/internal/toriiQueryBuilder.ts (1)
  • HistoricalToriiQueryBuilder (149-159)
packages/sdk/src/internal/merge.ts (3)
packages/sdk/src/web/queryBuilder.ts (1)
  • value (191-193)
examples/example-node-worker/src/typescript/models.gen.ts (1)
  • SchemaType (80-94)
packages/sdk/src/internal/types.ts (2)
  • SchemaType (50-70)
  • ParsedEntity (228-237)
packages/sdk/src/web/experimental/index.ts (3)
packages/sdk/src/internal/parseEntities.ts (1)
  • parseEntities (10-59)
packages/sdk/src/internal/parseHistoricalEvents.ts (1)
  • parseHistoricalEvents (18-55)
packages/sdk/src/internal/convertClauseToEntityKeysClause.ts (1)
  • intoEntityKeysClause (4-36)
packages/state/src/recs/index.ts (1)
packages/core/src/provider/DojoProvider.ts (1)
  • entities (104-123)
🪛 Biome (1.9.4)
packages/sdk/src/web/state/zustand.ts

[error] 14-14: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

packages/sdk/src/internal/toriiQueryBuilder.ts

[error] 133-133: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

examples/example-node-worker/src/typescript/contracts.gen.ts

[error] 1-1: All these imports are only used as types.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Use import type.

(lint/style/useImportType)


[error] 2-2: Some named imports are only used as types.

This import is only used as a type.

This import is only used as a type.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

examples/example-node-worker/src/typescript/models.gen.ts

[error] 3-3: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

examples/example-vite-react-sdk/src/historical-events.tsx

[error] 1-5: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

packages/sdk/src/__tests__/zustand.perf.test.ts

[error] 131-133: Forbidden non-null assertion.

(lint/style/noNonNullAssertion)


[error] 131-133: Forbidden non-null assertion.

Unsafe fix: Replace with optional chain operator ?. This operator includes runtime checks, so it is safer than the compile-only non-null assertion operator

(lint/style/noNonNullAssertion)


[error] 132-132: The computed expression can be simplified without the use of a string literal.

Unsafe fix: Use a literal key instead.

(lint/complexity/useLiteralKeys)

packages/sdk/src/internal/merge.ts

[error] 76-76: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)


[error] 96-96: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)


[error] 133-133: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

packages/sdk/src/internal/parseEntities.ts

[error] 50-50: Do not use template literals if interpolation and special-character handling are not needed.

Unsafe fix: Replace with string literal

(lint/style/noUnusedTemplateLiteral)

packages/sdk/src/web/experimental/index.ts

[error] 30-30: This type annotation is trivially inferred from its initialization.

Safe fix: Remove the type annotation.

(lint/style/noInferrableTypes)


[error] 103-103: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

packages/state/src/recs/index.ts

[error] 185-185: Do not use template literals if interpolation and special-character handling are not needed.

Unsafe fix: Replace with string literal

(lint/style/noUnusedTemplateLiteral)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build
🔇 Additional comments (34)
examples/example-vite-react-sdk/src/historical-events.tsx (1)

14-14: LGTM - Uses specialized historical query builder

The code now uses HistoricalToriiQueryBuilder instead of manually configuring ToriiQueryBuilder with a historical flag, which simplifies the code and makes its intent clearer.

packages/sdk/src/__tests__/zustand.perf.test.ts (1)

131-133: Improved code formatting for readability

Breaking the property access into separate lines improves readability, especially for deeply nested property paths.

🧰 Tools
🪛 Biome (1.9.4)

[error] 131-133: Forbidden non-null assertion.

(lint/style/noNonNullAssertion)


[error] 131-133: Forbidden non-null assertion.

Unsafe fix: Replace with optional chain operator ?. This operator includes runtime checks, so it is safer than the compile-only non-null assertion operator

(lint/style/noNonNullAssertion)


[error] 132-132: The computed expression can be simplified without the use of a string literal.

Unsafe fix: Use a literal key instead.

(lint/complexity/useLiteralKeys)

packages/sdk/src/web/experimental/index.ts (5)

24-24: Standardized client instantiation approach

Changed from using a factory method to direct constructor instantiation, which aligns with other client initialization patterns in the SDK.


28-35: Improved handling of paginated results

The code now consistently extracts the .items array from paginated results before passing to parsing functions, unifying the approach to data handling across the SDK.

🧰 Tools
🪛 Biome (1.9.4)

[error] 30-30: This type annotation is trivially inferred from its initialization.

Safe fix: Remove the type annotation.

(lint/style/noInferrableTypes)


41-42: Updated query parameter naming for consistency

Renamed dont_include_hashed_keys to no_hashed_keys for consistent terminology across the SDK.

Also applies to: 84-85


56-62: Standardized entity parsing in callbacks

Updated to ignore the entity ID parameter and directly parse an array containing the entity data, aligning with the SDK-wide shift to array-based entity handling.


107-108: Consistent array-based parsing in event callbacks

Updated the event parsing to work with arrays instead of keyed objects, consistent with the entity subscription handling and overall SDK approach.

packages/sdk/src/internal/parseEntities.ts (5)

10-11: Updated parameter type to use arrays instead of keyed objects

Changed the function to accept an array of entities rather than a keyed object, which aligns with the SDK's shift toward array-based entity handling.


15-25: Refactored entity iteration logic for array-based input

Modified result container and iteration approach to work with an array of entities instead of a keyed object, maintaining the same parsing logic with updated data access.


47-52: Updated result building for array-based structure

Changed from building a keyed object to pushing entities into an array, consistent with the new function parameter structure.

🧰 Tools
🪛 Biome (1.9.4)

[error] 50-50: Do not use template literals if interpolation and special-character handling are not needed.

Unsafe fix: Replace with string literal

(lint/style/noUnusedTemplateLiteral)


91-93: Added support for 'tuple' type alongside 'array'

This enhancement provides better type handling for Cairo data structures, improving the SDK's compatibility with diverse data formats.


129-134: Enhanced primitive type parsing

Improved parsing for various integer types, including BigInt support for large integers and optimized parsing for u64, which will handle Cairo numeric types more accurately.

packages/sdk/src/internal/merge.ts (4)

1-11: Well-structured Cairo type guards

The implementation of the isCairoOption type guard is clean and well-documented. This approach of creating dedicated type guards improves code readability and supports TypeScript's type inference system.


19-46: Thorough implementation of Cairo option merging

The mergeCairoOption function handles the expected cases well:

  • When source is Some, it correctly prefers the source value
  • Otherwise it correctly preserves the target value
  • It handles None variants properly

The type casting to unknown as MergedModels<T> is necessary due to TypeScript's limitations with complex generics, but be aware this bypasses some type checking.


53-55: Clear implementation of CairoCustomEnum type guard

This type guard follows the same pattern as the CairoOption guard, maintaining consistency in the codebase.


120-121: Good type reuse

Defining MergedModels as a type alias that reuses ParsedEntity types is a clean approach that avoids duplication.

examples/example-node-worker/src/typescript/models.gen.ts (4)

70-79: Well-designed Direction enum type representation

The representation of the Direction enum using both a const tuple and CairoCustomEnum type is a good approach for TypeScript and StarkNet compatibility. This pattern allows for type checking while supporting the Cairo type system.


80-94: Clean schema type extension pattern

Extending the imported SchemaType interface is a good approach for maintaining type compatibility with the SDK while adding application-specific models.


95-157: Consistent schema initialization with proper Cairo type handling

The schema constant correctly initializes all model types with appropriate default values, including proper instantiation of CairoOption and CairoCustomEnum instances.

Note: The "positition" typo mentioned earlier is also present in the schema initialization and should be fixed consistently.


158-171: Clean model mapping enum implementation

The ModelsMapping enum provides a convenient way to reference model identifiers. This pattern makes it easy to maintain consistency when referring to models throughout the application.

packages/sdk/src/internal/toriiQueryBuilder.ts (7)

1-6: Updated imports for new pagination approach

The imports now include PaginationDirection type, which supports the new cursor-based pagination approach.


34-37: Updated withLimit implementation for nested pagination

The method now correctly sets the limit in the pagination object.


47-62: New cursor-based pagination methods

The introduction of withCursor and withDirection methods provides a clean API for the new pagination approach. The implementation is straightforward and matches the new structure.


77-80: Updated includeHashedKeys for renamed property

The method now correctly sets the renamed no_hashed_keys property.


90-103: Updated orderBy methods for nested pagination

The addOrderBy and withOrderBy methods now correctly set values in the nested pagination object.


110-120: Updated entity model methods for renamed property

The addEntityModel and withEntityModels methods now correctly use the renamed models property.


145-158: Good addition of HistoricalToriiQueryBuilder

The new subclass provides a convenient way to create queries with the historical flag set to true by default. This is a cleaner approach than requiring clients to remember to set this flag manually.

packages/sdk/src/node/index.ts (7)

19-36: Improved imports and additional exports

Good improvements:

  1. Changed Account to a type import
  2. Added Result type from neverthrow for better error handling
  3. Added error constants for consistent error messages
  4. Additional exports for worker.ts, internal types, and models

These changes enhance the module's API and error handling capabilities.


60-71: Updated property name in entity query checks

Updated to use the renamed property no_hashed_keys instead of dont_include_hashed_keys, aligning with changes in ToriiQueryBuilder.


118-130: Updated property name and event parsing

Similar changes to the entity query handling:

  1. Updated to use no_hashed_keys property
  2. Changed event parsing to handle arrays from .items property

These changes align with the ToriiQueryBuilder changes and simplify the data structure.


138-144: Consistent event data parsing

Updated the event callback to parse data consistently with the initial event query, using arrays from .items property.


187-190: Updated getEntities to use items array

Consistent change to use .items property in getEntities method.


201-204: Updated getEventMessages to use items array

Consistent change to use .items property in getEventMessages method.


240-270: Improved error handling in sendMessage

Excellent improvements to the sendMessage method:

  1. Now returns Result type from neverthrow instead of throwing errors
  2. Clear error constants for consistent messages
  3. Removed explicit Account parameter, using options.signer and options.identity

This is a breaking change for code that explicitly passed an account parameter or relied on thrown errors, but the new approach is much better for error handling.

The use of Result type allows for more predictable error handling patterns in client code. Make sure to document this change in migration guides.

Comment on lines +137 to +151
async function onEntityUpdated({ data, error }) {
if (error) {
console.error(error);
return;
}

console.log(entities);
const entity = data.pop();
if (entity && entity.entityId !== "0x0") {
// do whatever you need here
const model = entity.models.dojo_starter;
if (model?.Moves) {
await publishOffchainPositionCount(model.Moves);
}
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Potential data loss with data.pop()

data is already the argument supplied by the subscription.
Calling pop() both mutates the array and drops all but the last entity; if multiple entities arrive in the same batch they are silently ignored.

Iterate instead:

for (const entity of data) {
  // handle each entity
}

Comment on lines +125 to +133
const pc = defaultPositionCount;
for (const e of events) {
const moved = e.models.dojo_starter.Moved;
if (!moved) {
continue;
}
pc[moved.direction] += 1;
}
return pc;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

initPositionFromEvent mutates the shared constant

pc is an alias of defaultPositionCount, so every call mutates the constant and all future callers see cumulative side-effects.

-const pc = defaultPositionCount;
+const pc: PositionCount = { ...defaultPositionCount };

This also prevents accidental modification of the default object elsewhere.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const pc = defaultPositionCount;
for (const e of events) {
const moved = e.models.dojo_starter.Moved;
if (!moved) {
continue;
}
pc[moved.direction] += 1;
}
return pc;
const pc: PositionCount = { ...defaultPositionCount };
for (const e of events) {
const moved = e.models.dojo_starter.Moved;
if (!moved) {
continue;
}
pc[moved.direction] += 1;
}
return pc;

Comment on lines +21 to +25
// Those lines are require so that websocket works.
// @ts-ignore
global.Websocket = w3cwebsocket;
// @ts-ignore
global.WorkerGlobalScope = global;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Wrong global identifier: use WebSocket, not Websocket

Browsers (and most polyfills) expose global.WebSocket (uppercase S).
Assigning to Websocket leaves libraries that expect WebSocket undefined.

-// @ts-ignore
-global.Websocket = w3cwebsocket;
+// @ts-ignore
+global.WebSocket = w3cwebsocket;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Those lines are require so that websocket works.
// @ts-ignore
global.Websocket = w3cwebsocket;
// @ts-ignore
global.WorkerGlobalScope = global;
// Those lines are require so that websocket works.
// @ts-ignore
global.WebSocket = w3cwebsocket;
// @ts-ignore
global.WorkerGlobalScope = global;

Comment on lines 320 to 333
while (continueFetching) {
if (logging)
console.log(
`Fetched ${Object.keys(fetchedEntities).length} entities ${cursor}`
`Fetched ${Object.keys(fetchedEntities.items).length} entities ${fetchedEntities.next_cursor}`
);

setEntities(fetchedEntities, components, logging);
setEntities(fetchedEntities.items, components, logging);

if (Object.keys(fetchedEntities).length < limit) {
if (Object.keys(fetchedEntities.items).length < limit) {
continueFetching = false;
} else {
cursor += limit;
cursor = fetchedEntities.next_cursor;
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Infinite loop in getEntitiesQuery

Inside the while (continueFetching) loop you never issue a new client.getEntities call—fetchedEntities is fetched once outside the loop, so cursor advances but data doesn’t refresh.

Move the fetch inside the loop:

while (continueFetching) {
  const fetchedEntities = await client.getEntities({ …cursor… });
  
}

Otherwise the loop either spins forever or exits immediately depending on the first batch size.

Comment on lines +38 to +46
export interface PositionCount {
identity: string;
positition: Array<[BigNumberish, BigNumberish]>;
}

// Type definition for `dojo_starter::models::PositionCountValue` struct
export interface PositionCountValue {
positition: Array<[BigNumberish, BigNumberish]>;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix typo in "positition" field name

There appears to be a typo in the positition field name (should be "position").

export interface PositionCount {
	identity: string;
-	positition: Array<[BigNumberish, BigNumberish]>;
+	position: Array<[BigNumberish, BigNumberish]>;
}

export interface PositionCountValue {
-	positition: Array<[BigNumberish, BigNumberish]>;
+	position: Array<[BigNumberish, BigNumberish]>;
}

Since this affects the interface structure and is reflected in the schema initialization, this typo should be fixed in the generator if possible.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export interface PositionCount {
identity: string;
positition: Array<[BigNumberish, BigNumberish]>;
}
// Type definition for `dojo_starter::models::PositionCountValue` struct
export interface PositionCountValue {
positition: Array<[BigNumberish, BigNumberish]>;
}
export interface PositionCount {
identity: string;
position: Array<[BigNumberish, BigNumberish]>;
}
// Type definition for `dojo_starter::models::PositionCountValue` struct
export interface PositionCountValue {
position: Array<[BigNumberish, BigNumberish]>;
}

Comment on lines +50 to 51
const client = await new torii.ToriiClient(clientConfig);
return {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Client initialization change

Changed from torii.createClient to directly instantiating new torii.ToriiClient. This is a breaking change for anyone directly using the returned client.

Ensure this change is documented in migration guides and potentially consider providing a compatibility layer if feasible.

@MartianGreed MartianGreed force-pushed the chore/dojo1.5 branch 2 times, most recently from 503223b to 2cc16f7 Compare April 28, 2025 19:44
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Inline review comments failed to post. This is likely due to GitHub's limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 11

🔭 Outside diff range comments (5)
packages/react/package.json (1)

3-3: ⚠️ Potential issue

Version bump needed
The PR title suggests bumping to 1.5, but this package remains at 1.4.5. Please update the version field to 1.5.0.

Apply this diff:

-  "version": "1.4.5",
+  "version": "1.5.0",
packages/torii-client/package.json (1)

3-3: ⚠️ Potential issue

Version bump needed
This package is still at version 1.4.4. Please update it to 1.5.0 to match the PR objective.

Apply this diff:

-  "version": "1.4.4",
+  "version": "1.5.0",
packages/core/package.json (1)

3-3: ⚠️ Potential issue

Version bump needed
The PR title indicates a bump to 1.5, but this package remains at 1.4.4. Please update the version field to 1.5.0.

Apply this diff:

-  "version": "1.4.4",
+  "version": "1.5.0",
examples/example-vite-react-sdk/src/historical-events.tsx (1)

13-21: 🛠️ Refactor suggestion

Stabilise the HistoricalToriiQueryBuilder instance

new HistoricalToriiQueryBuilder() is created on every render, so the hook sees a different object in its deps each time, forcing an unnecessary re-query.

-const events = useHistoricalEventsQuery(
-    new HistoricalToriiQueryBuilder().withClause(
+const builder = React.useMemo(
+    () =>
+        new HistoricalToriiQueryBuilder().withClause(
             KeysClause(
                 [],
                 [addAddressPadding(account?.address ?? "0")],
                 "VariableLen"
             ).build()
-    ),
+        ),
+    [account?.address],
+);
+const events = useHistoricalEventsQuery(builder);

This memoisation avoids redundant network calls and improves performance.

packages/state/src/recs/index.ts (1)

518-553: ⚠️ Potential issue

insertEntitiesInDB iterates incorrectly over an array

entities is typed as ToriiEntity[], yet the code uses
for (const [entityId, data] of Object.entries(entities)), which yields the
array index ("0", "1" …) instead of the entity’s hashed key.

-for (const [entityId, data] of Object.entries(entities)) {
+for (const entity of entities) {
+    const entityId = entity.hashed_keys;
+    const data = entity;

This prevents corrupt keys and preserves original entity data.

♻️ Duplicate comments (13)
packages/sdk/src/internal/merge.ts (1)

96-103: Replicate the stricter typing for the source-precedence branch

The same any/for…in patterns appear here. Apply the stricter snippet (see previous comment) to keep both branches symmetrical and fully typed.

🧰 Tools
🪛 Biome (1.9.4)

[error] 96-96: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

examples/example-node-worker/main.ts (3)

22-25: ⚠️ Potential issue

Websocket should be WebSocket (duplicate comment)

Browsers and most polyfills expose the global with an uppercase S (WebSocket).
Re-introducing the lowercase alias re-creates the very issue flagged in earlier reviews.

-// @ts-ignore
-global.Websocket = w3cwebsocket;
+// @ts-ignore
+global.WebSocket = w3cwebsocket;

125-132: ⚠️ Potential issue

initPositionFromEvent still mutates the shared constant (duplicate comment)

pc is an alias of defaultPositionCount; each call accumulates counts into the
constant and leaks state across workers. Spread-clone the default instead:

-const pc = defaultPositionCount;
+const pc: PositionCount = { ...defaultPositionCount };

137-151: 🛠️ Refactor suggestion

data.pop() drops all but the last entity (duplicate comment)

data is already the supplied batch. Iterating is safer and avoids silent data
loss:

-const entity = data.pop();
-if (entity && entity.entityId !== "0x0") {
-
-}
+for (const entity of data) {
+    if (entity.entityId === "0x0") continue;
+
+}
packages/state/src/recs/index.ts (2)

189-193: 🛠️ Refactor suggestion

Inefficient & incorrect length check on array of entities

entities.items is an array, so Object.keys(…).length is both slower and
semantically misleading. It also breaks if the array contains sparse indexes.

-        if (Object.keys(entities.items).length < limit) {
+        if (entities.items.length < limit) {

307-333: ⚠️ Potential issue

getEntitiesQuery fetches once, then loops forever (duplicate comment)

fetchedEntities is obtained before the while loop and never refreshed;
cursor changes but data doesn’t, so the loop either spins indefinitely or
exits immediately.

Move the fetch inside the loop:

-const fetchedEntities = await client.getEntities({ … });
-while (continueFetching) {
-
-}
+while (continueFetching) {
+    const fetchedEntities = await client.getEntities({
+        pagination: { limit, cursor, direction: "Forward", order_by: orderBy },
+        clause: clause || undefined,
+        no_hashed_keys: false,
+        models: entityModels,
+        historical,
+    });
+
+}
examples/example-node-worker/src/typescript/models.gen.ts (3)

38-46: Fix typo in "positition" field name

There appears to be a typo in the positition field name (should be "position").

export interface PositionCount {
	identity: string;
-	positition: Array<[BigNumberish, BigNumberish]>;
+	position: Array<[BigNumberish, BigNumberish]>;
}

export interface PositionCountValue {
-	positition: Array<[BigNumberish, BigNumberish]>;
+	position: Array<[BigNumberish, BigNumberish]>;
}

Since this affects the interface structure and is reflected in the schema initialization, this typo should be fixed in the generator if possible.


129-129: Schema uses incorrect field name "positition"

The schema initialization at line 129 uses the typo "positition" which should be corrected to "position" to match the intended field name.

-			positition: [[0, 0]],
+			position: [[0, 0]],

132-132: Schema uses incorrect field name "positition"

The schema initialization at line 132 also uses the typo "positition" which should be corrected.

-			positition: [[0, 0]],
+			position: [[0, 0]],
packages/sdk/src/internal/toriiQueryBuilder.ts (1)

39-45: Deprecated withOffset method lacks backward compatibility

The withOffset method is now a no-op that returns this without setting any properties. While it's appropriately marked as deprecated, this implementation could silently break existing code that relies on offset-based pagination.

Consider maintaining backwards compatibility by mapping offset to an equivalent cursor-based approach, or provide more explicit migration guidance in the deprecation notice.

packages/sdk/src/node/index.ts (3)

50-51: Client initialization change

Changed from torii.createClient to directly instantiating new torii.ToriiClient. This is a breaking change for anyone directly using the returned client.

Ensure this change is documented in migration guides and potentially consider providing a compatibility layer if feasible.


215-230: Modified generateTypedData signature

The method signature has changed:

  • Removed optional domain parameter
  • Added optional additionalTypes parameter
  • Domain is now always taken from options.domain

This is a breaking change for code that explicitly passed a domain parameter different from options.domain.

Ensure this change is documented in migration guides and consider if there are cases where passing a different domain was useful functionality.


72-85: Updated entity parsing to use items array

The entity parsing has been updated to handle arrays from the .items property rather than keyed objects. This change simplifies the data structure but is a breaking change for existing code.

-                (await client.getEntities(q)).items
+                (await client.getEntities(q)).items

Ensure this change is documented in migration guides. Make sure that all examples and documentation are updated to reflect this new data structure.

🧹 Nitpick comments (20)
turbo.json (1)

39-42: Define dependencies for test tasks
Consider adding a dependsOn for the "test" task so that builds run before tests, improving cache consistency:

 "test": {
-},
+  "dependsOn": ["^build"],
+  "outputs": [],
+},

This ensures tests always run against fresh builds.

packages/sdk/src/internal/merge.ts (1)

133-155: deepMerge silently overwrites arrays & loses type-info

deepMerge:

  • Treats arrays like primitives (!Array.isArray(source[key])) → any array on source replaces the one on target.
    That’s rarely what callers expect from a “deep-merge”.
  • Falls back to Record<string, any> which erases all schema information.
  • Recurses without runtime cycle protection, which can blow the call-stack if circular refs sneak in.

Consider:

-if (
-    source[key] !== null &&
-    typeof source[key] === "object" &&
-    !Array.isArray(source[key])
-) {
+if (
+    source[key] !== null &&
+    typeof source[key] === "object"
+) {
     // handle Array vs Object separately
+    if (Array.isArray(source[key]) && Array.isArray(target[key])) {
+        result[key] = [...(target[key] as unknown[]), ...(source[key] as unknown[])] as typeof source[key];
+        continue;
+    }

…and replace the root Record<string, any> with a generic helper:

function asMutable<T>(v: T): Mutable<T> { return v as Mutable<T>; }

This maintains schema types throughout the recursion.

🧰 Tools
🪛 Biome (1.9.4)

[error] 133-133: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

examples/example-vite-react-sdk/src/historical-events.tsx (2)

1-5: Import ParsedEntity as a type-only import to trim bundle size

ParsedEntity is only used for type annotations.
Switching to import type lets TS / bundlers tree-shake the symbol:

-import {
-    KeysClause,
-    ParsedEntity,
-    HistoricalToriiQueryBuilder,
-} from "@dojoengine/sdk";
+import {
+    KeysClause,
+    HistoricalToriiQueryBuilder,
+} from "@dojoengine/sdk";
+import type { ParsedEntity } from "@dojoengine/sdk";
🧰 Tools
🪛 Biome (1.9.4)

[error] 1-5: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)


31-35: Remove @ts-ignore by refining the hook’s return type

Blindly silencing TypeScript defeats its value.
If useHistoricalEventsQuery returns { data?: ParsedEntity[] }, destructure it properly and default to an empty array:

const { data: events = [] } = useHistoricalEventsQuery(builder);
...
{events.map((e, key) => (
    <Event event={e} key={key} />
))}

This eliminates the ignore directive and the unsafe cast.

🧰 Tools
🪛 Biome (1.9.4)

[error] 34-34: Avoid using the index of an array as key property in an element.

This is the source of the key value.

The order of the items may change, and this also affects performances and component state.
Check the React documentation.

(lint/suspicious/noArrayIndexKey)

packages/sdk/src/__tests__/zustand.perf.test.ts (1)

131-133: Replace non-null assertions with optional chaining

!. bypasses runtime checks and the linter rightfully flags it.
Use ?. so the benchmark won’t crash if the path is unexpectedly undefined.

-        draft.entities[
-            "entity500"
-        ].models.world!.item!.durability = 75;
+        const item = draft.entities["entity500"]?.models.world?.item;
+        if (item) item.durability = 75;

This keeps the perf test realistic while remaining type-safe.

🧰 Tools
🪛 Biome (1.9.4)

[error] 131-133: Forbidden non-null assertion.

(lint/style/noNonNullAssertion)


[error] 131-133: Forbidden non-null assertion.

Unsafe fix: Replace with optional chain operator ?. This operator includes runtime checks, so it is safer than the compile-only non-null assertion operator

(lint/style/noNonNullAssertion)


[error] 132-132: The computed expression can be simplified without the use of a string literal.

Unsafe fix: Use a literal key instead.

(lint/complexity/useLiteralKeys)

packages/sdk/src/web/react/hooks/entities.ts (1)

26-35: Improved entity filtering and update logic.

The updated approach to filter zero-value entities and use the first valid entity is more robust. However, there's an opportunity for further optimization:

  1. If the filter might return an empty array, the direct access to entities[0] could be simplified
  2. The check for data being truthy could be combined with the filter operation

Consider this slightly more concise implementation:

-            if (data) {
-                const entities = data.filter(
-                    (e) => Number.parseInt(e.entityId, 16) !== 0
-                );
-
-                const entity = entities[0];
-                if (entity) {
-                    state.updateEntity(entity);
-                }
-            }
+            const validEntity = data?.filter(e => Number.parseInt(e.entityId, 16) !== 0)[0];
+            if (validEntity) {
+                state.updateEntity(validEntity);
+            }
examples/example-node-worker/src/typescript/contracts.gen.ts (3)

1-2: Import type optimizations needed

The imports are only used as types and should use the import type syntax to ensure they're removed by compilers, which improves bundle size and avoids loading unnecessary modules.

-import { DojoProvider, DojoCall } from "@dojoengine/core";
-import { Account, AccountInterface, BigNumberish, CairoOption, CairoCustomEnum, ByteArray } from "starknet";
+import type { DojoProvider, DojoCall } from "@dojoengine/core";
+import type { Account, AccountInterface, BigNumberish, CairoOption, CairoCustomEnum, ByteArray } from "starknet";
🧰 Tools
🪛 Biome (1.9.4)

[error] 1-1: All these imports are only used as types.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Use import type.

(lint/style/useImportType)


[error] 2-2: Some named imports are only used as types.

This import is only used as a type.

This import is only used as a type.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)


15-26: Consider refactoring error handling

The function logs the error and then rethrows it. Consider adding more context to the error message or implementing more specific error handling based on the contract interaction failure.

const actions_move = async (snAccount: Account | AccountInterface, direction: CairoCustomEnum) => {
    try {
        return await provider.execute(
            snAccount,
            build_actions_move_calldata(direction),
            "dojo_starter",
        );
    } catch (error) {
-        console.error(error);
-        throw error;
+        console.error(`Failed to execute move action: ${error}`);
+        throw new Error(`Move action failed: ${error instanceof Error ? error.message : String(error)}`);
    }
};

5-59: Exported contract wrapper lacks documentation

The exported function and its methods would benefit from JSDoc documentation explaining their purpose, parameters, and return values, especially since this is likely to be used by developers integrating with the Dojo ecosystem.

+/**
+ * Sets up the world contract interfaces for interacting with Dojo contracts
+ * 
+ * @param provider - The Dojo provider used for contract interactions
+ * @returns Object containing methods to interact with game actions
+ */
export function setupWorld(provider: DojoProvider) {

+    /**
+     * Builds calldata for the move action
+     * @param direction - A CairoCustomEnum representing the movement direction
+     * @returns The formatted DojoCall object ready for execution
+     */
    const build_actions_move_calldata = (direction: CairoCustomEnum): DojoCall => {
        // ...
    };

    // Add similar documentation for other methods
packages/sdk/src/web/state/zustand.ts (1)

14-14: Import optimization for types

The MergedModels import is only used as a type and should use the import type syntax to optimize bundle size.

-import { deepMerge, MergedModels } from "../../internal/merge";
+import { deepMerge } from "../../internal/merge";
+import type { MergedModels } from "../../internal/merge";
🧰 Tools
🪛 Biome (1.9.4)

[error] 14-14: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

packages/sdk/src/internal/parseEntities.ts (1)

58-58: Unnecessary Object.values call

The function returns Object.values(result), but result is already an array. This is unnecessary and could be simplified.

-    return Object.values(result);
+    return result;
examples/example-node-worker/main.ts (1)

79-98: Temporal-Dead-Zone risk for positionCount reference

positionCount is declared after this function definition. Although the
function isn’t executed until later, hoisting nuances can be confusing. Moving
the declaration above the function (or passing it as a parameter) improves
readability and avoids TDZ surprises.

packages/sdk/src/web/index.ts (1)

83-90: Guard condition is inverted

q.no_hashed_keys means hashed keys are not included.
The check should block the subscription when hashed keys are excluded
and no explicit Keys clause is present.

The current logic throws when no_hashed_keys === true, which seems correct,
but the variable naming is easy to misinterpret—consider an inline comment or a
helper predicate to avoid future confusion.

examples/example-node-worker/src/typescript/models.gen.ts (3)

100-103: Inconsistent indentation in schema initialization

The indentation in the CairoCustomEnum initialization is inconsistent, with the first option aligned differently than the others.

directions: [new CairoCustomEnum({ 
-					Left: "",
+				Left: "",
				Right: undefined,
				Up: undefined,
				Down: undefined, })],

1-3: Use import type for type-only imports

Use the import type syntax for imports that are only used as types to ensure they are removed during compilation.

-import type { SchemaType as ISchemaType } from "@dojoengine/sdk";
+import type { SchemaType as ISchemaType } from "@dojoengine/sdk";

-import { CairoCustomEnum, CairoOption, CairoOptionVariant, BigNumberish } from 'starknet';
+import { CairoCustomEnum, CairoOption, CairoOptionVariant } from 'starknet';
+import type { BigNumberish } from 'starknet';
🧰 Tools
🪛 Biome (1.9.4)

[error] 3-3: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)


144-147: Inconsistent indentation in Moved schema initialization

Similar to the earlier comment, there's inconsistent indentation in the CairoCustomEnum initialization for the Moved schema.

direction: new CairoCustomEnum({ 
-					Left: "",
+				Left: "",
				Right: undefined,
				Up: undefined,
				Down: undefined, }),
packages/sdk/src/internal/toriiQueryBuilder.ts (4)

47-55: Add parameter documentation for withCursor method

The withCursor method documentation explains what the parameter is for, but doesn't document the parameter itself. Consider adding a @param tag to match the style of other methods in this class.

/**
 * Set the cursor for pagination
 * undefined is default, fetch from starting point
 * `next_cursor` is return from queries
+ * @param {string} cursor - The cursor to paginate from
 */

57-62: Fix incorrect JSDoc for withDirection method

The JSDoc comment for withDirection is incorrect - it states "Set the maximum number of results to return" which is a copy of the withLimit method's documentation. Update it to correctly describe setting the pagination direction.

/**
- * Set the maximum number of results to return
+ * Set the direction for pagination
+ * @param {PaginationDirection} direction - The direction to paginate ("Forward" or "Backward")
 */

133-142: Type safety improvement needed in withPagination method

The static method withPagination uses Record<string, Record<string, any>> as its generic constraint. This could be improved to use the SchemaType type for better type safety and to avoid the any type flagged by the linter.

- static withPagination<T extends Record<string, Record<string, any>>>(
+ static withPagination<T extends SchemaType>(
🧰 Tools
🪛 Biome (1.9.4)

[error] 133-133: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)


145-158: Add type documentation for HistoricalToriiQueryBuilderOptions

The new HistoricalToriiQueryBuilderOptions type and HistoricalToriiQueryBuilder class lack documentation explaining their purpose and usage. Consider adding JSDoc comments to clarify that this is specifically for historical queries.

+/**
+ * Options type for HistoricalToriiQueryBuilder, excluding the 'historical' property
+ * which is always true for historical queries.
+ */
export type HistoricalToriiQueryBuilderOptions = Omit<
    Partial<ToriiQueryBuilderOptions>,
    "historical"
>;

+/**
+ * A specialized query builder for historical queries that sets historical:true by default.
+ * Use this when querying historical event data.
+ */
export class HistoricalToriiQueryBuilder<
    T extends SchemaType
> extends ToriiQueryBuilder<T> {
🛑 Comments failed to post (11)
packages/react/package.json (1)

38-39: 💡 Verification agent

🧩 Analysis chain

Ensure consistent tooling versions
You’ve added typescript@^5.6.2 and vitest@^3.1.2 here—great for React. However, @dojoengine/core still uses vitest@^1.6.0. To avoid version drift, please align all vitest versions across the monorepo.

Run this to locate mismatched versions:


🏁 Script executed:

#!/bin/bash
rg -A1 '"vitest":' -g 'packages/**/package.json'

Length of output: 1077


Align Vitest Versions Across the Monorepo

We currently have three different major Vitest versions in use:

• packages/react & packages/torii-client: ^3.1.2
• packages/utils-wasm & packages/create-dojo: ^2.1.1
• packages/utils, core, state, predeployed-connector, create-burner: ^1.6.x
• packages/sdk: ^1.6.1

To avoid confusing test behavior and dependency drift, please pick a single Vitest version (e.g. the latest ^3.1.2) and update all package.json files accordingly:

  • packages/utils-wasm/package.json
  • packages/utils/package.json
  • packages/sdk/package.json
  • packages/predeployed-connector/package.json
  • packages/state/package.json
  • packages/create-burner/package.json
  • packages/create-dojo/package.json
  • packages/core/package.json

Ensure that each entry under "devDependencies" (or "dependencies") reads "vitest": "^3.1.2".

packages/sdk/src/internal/merge.ts (1)

76-83: 🛠️ Refactor suggestion

Avoid any and tighten variant-loop typing

newEnumContent is declared with Record<string, any> and the subsequent for…in iterates over target.variant.
Both patterns reduce type-safety:

  • any disables all compile-time checks.
  • for…in also enumerates inherited keys; if the underlying CairoCustomEnum prototype ever gains enumerable props, they will leak in.
-const newEnumContent: Record<string, any> = {};
-for (const key in target.variant) {
-    newEnumContent[key] = undefined;
-}
+const newEnumContent = Object.fromEntries(
+    Object.keys(target.variant as Record<string, unknown>).map((k) => [k, undefined]),
+) as { [K in keyof typeof target.variant]?: never };

Using Object.keys + fromEntries:

  1. Filters out prototype keys.
  2. Preserves the exact variant names via a mapped type instead of string.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

        const newEnumContent = Object.fromEntries(
            Object.keys(target.variant as Record<string, unknown>).map((k) => [k, undefined]),
        ) as { [K in keyof typeof target.variant]?: never };

        // Set the active variant from source
🧰 Tools
🪛 Biome (1.9.4)

[error] 76-76: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

examples/example-node-worker/package.json (1)

6-10: ⚠️ Potential issue

Added helpful scripts for development.

The addition of scripts to run the example with both bun and node makes it easier for developers to get started with the example. The build script is also useful for TypeScript compilation.

Warning: Hardcoded credentials in scripts

The IDENTITY and SECRET_KEY values in the scripts appear to be private keys or identifiers. Although these might be example values, it's generally not recommended to hardcode credentials in scripts that will be committed to a repository.

Consider using environment variables from a local .env file that is not committed to the repository, like:

-        "bun": "IDENTITY=0x0127fd5f1fe78a71f8bcd1fec63e3fe2f0486b6ecd5c86a0466c3a21fa5cfcec SECRET_KEY=0xc5b2fcab997346f3ea1c00b002ecf6f382c5f9c9659a3894eb783c5320f912 bun run main.ts",
-        "node": "IDENTITY=0x0127fd5f1fe78a71f8bcd1fec63e3fe2f0486b6ecd5c86a0466c3a21fa5cfcec SECRET_KEY=0xc5b2fcab997346f3ea1c00b002ecf6f382c5f9c9659a3894eb783c5320f912 node main.ts",
+        "bun": "bun run main.ts",
+        "node": "node main.ts",

This would work with the dotenv package already included in the dependencies.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    "scripts": {
        "bun": "bun run main.ts",
        "node": "node main.ts",
        "build": "tsc -b"
    },
🧰 Tools
🪛 Gitleaks (8.21.2)

7-7: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


8-8: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

examples/example-node-worker/src/typescript/contracts.gen.ts (1)

36-47: 🛠️ Refactor suggestion

Duplicate error handling pattern

The same error handling pattern is repeated in actions_spawn. Consider extracting a common error handling utility to avoid code duplication.

+const handleContractError = (action: string, error: unknown) => {
+    console.error(`Failed to execute ${action} action: ${error}`);
+    throw new Error(`${action} action failed: ${error instanceof Error ? error.message : String(error)}`);
+};

const actions_move = async (snAccount: Account | AccountInterface, direction: CairoCustomEnum) => {
    try {
        return await provider.execute(
            snAccount,
            build_actions_move_calldata(direction),
            "dojo_starter",
        );
    } catch (error) {
-        console.error(error);
-        throw error;
+        handleContractError('move', error);
    }
};

const actions_spawn = async (snAccount: Account | AccountInterface) => {
    try {
        return await provider.execute(
            snAccount,
            build_actions_spawn_calldata(),
            "dojo_starter",
        );
    } catch (error) {
-        console.error(error);
-        throw error;
+        handleContractError('spawn', error);
    }
};
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

// Add this helper at the top of your file (after imports)
const handleContractError = (action: string, error: unknown) => {
    console.error(`Failed to execute ${action} action:`, error);
    throw new Error(
        `${action} action failed: ${
            error instanceof Error ? error.message : String(error)
        }`
    );
};

const actions_move = async (
    snAccount: Account | AccountInterface,
    direction: CairoCustomEnum
) => {
    try {
        return await provider.execute(
            snAccount,
            build_actions_move_calldata(direction),
            "dojo_starter",
        );
    } catch (error) {
        handleContractError("move", error);
    }
};

const actions_spawn = async (snAccount: Account | AccountInterface) => {
    try {
        return await provider.execute(
            snAccount,
            build_actions_spawn_calldata(),
            "dojo_starter",
        );
    } catch (error) {
        handleContractError("spawn", error);
    }
};
packages/sdk/src/web/state/zustand.ts (1)

92-97: 💡 Verification agent

🧩 Analysis chain

Type casting during deep merge

The code uses multiple type assertions during the merge process, which could be fragile if the structure of the data changes. Consider adding runtime type checks or defensive programming techniques to ensure type safety.


🏁 Script executed:

#!/bin/bash
# Check how the deepMerge function is implemented and if it handles type safety

# Find the implementation of deepMerge
rg -A 10 "export function deepMerge" --type ts

Length of output: 864


🏁 Script executed:

#!/bin/bash
# Fetch the full implementation of deepMerge to inspect runtime checks and type assertions
rg -A100 "export function deepMerge" --type ts packages/sdk/src/internal/merge.ts

Length of output: 1495


Ensure runtime type safety when merging models

In packages/sdk/src/web/state/zustand.ts (lines 92–97), we’re casting the result of deepMerge—which internally spreads into a Record<string, any>—back to MergedModels<T>:

] = deepMerge(
    mergedModels[typedNamespace as keyof typeof mergedModels] as MergedModels<T>,
    namespaceModels
) as MergedModels<T>;

While TypeScript trusts the cast, at runtime there’s no check that the merged object actually conforms to our schema. To prevent subtle bugs if the input shape changes, we should:

  • Add post-merge validation using either:
    • Custom type guards that verify required keys/types, or
    • A schema-based validator (e.g. Zod, io-ts) to assert the merged output matches MergedModels<T>.
  • Alternatively, enhance deepMerge to enforce expected fields and throw or default on unknown/mismatched properties.

This defensive step will ensure any divergence between compile-time types and runtime data is caught early.

examples/example-node-worker/main.ts (1)

175-176: ⚠️ Potential issue

Unreachable second return statement

The empty array returned on line 175 makes the subsequent return [sub];
dead-code (Biome warning). Return the subscription instead.

-    return [];
-    return [sub];
+    return [sub];
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    return [sub];
🧰 Tools
🪛 Biome (1.9.4)

[error] 176-176: This code is unreachable

... because this statement will return from the function beforehand

(lint/correctness/noUnreachable)

packages/sdk/src/web/index.ts (2)

45-49: ⚠️ Potential issue

createClient need not be async or await

new torii.ToriiClient is synchronous. Wrapping it in async/await adds an
unnecessary micro-task and misleads readers into thinking the constructor is
asynchronous.

-export async function createClient(
-    config: torii.ClientConfig
-): Promise<torii.ToriiClient> {
-    return await new torii.ToriiClient(config);
-}
+export function createClient(
+    config: torii.ClientConfig
+): torii.ToriiClient {
+    return new torii.ToriiClient(config);
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

export function createClient(
    config: torii.ClientConfig
): torii.ToriiClient {
    return new torii.ToriiClient(config);
}

261-279: 💡 Verification agent

❓ Verification inconclusive

sendMessage mixes Result and exceptions

The function returns Result<Uint8Array,string>, except when an internal
error occurs—then it throws, breaking the Result contract. Prefer returning
err(String(error)) to keep the API consistent.

-            } catch (error) {
-                console.error("Failed to send message:", error);
-                throw error;
+            } catch (error) {
+                console.error("Failed to send message:", error);
+                return err(
+                    error instanceof Error ? error.message : String(error)
+                );
             }

Ensure sendMessage consistently returns a Result on errors

The catch block currently throws an exception, breaking the Result<Uint8Array, string> contract. Convert the caught error into an err(...) so callers always receive a Result.

• packages/sdk/src/web/index.ts (around lines 261–279)

             } catch (error) {
                 console.error("Failed to send message:", error);
-                throw error;
+                return err(
+                    error instanceof Error ? error.message : String(error)
+                );
             }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

            if (!account) {
                return err(NO_ACCOUNT);
            }
            try {
                // Sign the typed data
                const signature: Signature = await account.signMessage(data);

                // Stringify typed data for publishing
                const dataString = JSON.stringify(data);
                // Publish the signed message
                return ok(
                    await client.publishMessage(
                        dataString,
                        Array.isArray(signature)
                            ? signature
                            : [signature.r.toString(), signature.s.toString()]
                    )
                );
            } catch (error) {
                console.error("Failed to send message:", error);
                return err(
                    error instanceof Error ? error.message : String(error)
                );
            }
packages/sdk/src/internal/toriiQueryBuilder.ts (1)

9-20: ⚠️ Potential issue

Breaking change: Nested pagination structure and renamed properties

The default options structure has significantly changed:

  1. Pagination parameters are now nested in a pagination object
  2. dont_include_hashed_keys renamed to no_hashed_keys
  3. entity_models renamed to models

These changes will require updates to any code that directly accesses these properties.

Ensure this breaking change is documented in migration guides and update all dependent code, including tests.

packages/sdk/src/node/index.ts (2)

240-265: 🛠️ Refactor suggestion

Enhanced error handling with Result type

The sendMessage method now returns a Result type from neverthrow instead of throwing errors directly. This is a good improvement for error handling but is a breaking change for existing code that expects exceptions.

- async (data: TypedData, _account?: Account): Promise<void>
+ async (data: TypedData, _account?: Account): Promise<Result<Uint8Array, string>>

Update callers to handle the Result type appropriately:

const result = await sdk.sendMessage(typedData);
if (result.isOk()) {
  const receipt = result.value;
  // Handle success
} else {
  const errorMessage = result.error;
  // Handle error
}

127-130: ⚠️ Potential issue

Historical events handling simplified

The handling of historical events has been simplified to always use parseEntities on the items array. This is consistent with the changes to entity parsing and provides a more unified approach.

Ensure that any code relying on the previous historical event parsing behavior is updated accordingly.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🔭 Outside diff range comments (1)
packages/state/src/recs/index.ts (1)

516-554: 🛠️ Refactor suggestion

insertEntitiesInDB assumes an object, but receives an array

entities is typed as ToriiEntity[]; iterating with Object.entries produces numeric indices rather than entity IDs. Iterate the array directly and pick a stable key (e.g., hashed_keys).

-        for (const [entityId, data] of Object.entries(entities)) {
-            const entityData = {
-                id: entityId,
-                ...data,
-            };
+        for (const entity of entities) {
+            const entityData = {
+                id: entity.hashed_keys,
+                ...entity,
+            };
             const request = store.put(entityData);
♻️ Duplicate comments (13)
examples/example-node-worker/src/typescript/models.gen.ts (4)

38-41: Fix typo in "positition" field name

There appears to be a typo in the positition field name (should be "position").

export interface PositionCount {
	identity: string;
-	positition: Array<[BigNumberish, BigNumberish]>;
+	position: Array<[BigNumberish, BigNumberish]>;
}

44-46: Fix typo in "positition" field name

The same typo exists in the PositionCountValue interface.

export interface PositionCountValue {
-	positition: Array<[BigNumberish, BigNumberish]>;
+	position: Array<[BigNumberish, BigNumberish]>;
}

128-130: Fix typo in "positition" field name in schema initialization

The same typo exists in the schema initialization for PositionCount.

		PositionCount: {
			identity: "",
-			positition: [[0, 0]],
+			position: [[0, 0]],
		},

132-133: Fix typo in "positition" field name in schema initialization

The same typo exists in the schema initialization for PositionCountValue.

		PositionCountValue: {
-			positition: [[0, 0]],
+			position: [[0, 0]],
		},
examples/example-node-worker/main.ts (3)

21-25: Global identifier typo: use WebSocket, not Websocket
Browsers (and most polyfills) expose global.WebSocket. Re-introducing the lowercase Websocket will break libraries expecting the correct casing.

-// @ts-ignore
-global.Websocket = w3cwebsocket;
+// @ts-ignore
+global.WebSocket = w3cwebsocket;

120-128: initPositionFromEvent still mutates the shared constant

pc is a direct alias of defaultPositionCount; each update mutates the default object and leaks state across workers/runs.

-    const pc = defaultPositionCount;
+    const pc: PositionCount = { ...defaultPositionCount };

136-151: data.pop() drops all but the last entity

If multiple entities arrive in one batch, every entity except the final one is silently ignored. Iterate over the array instead.

-        const entity = data.pop();
-        if (entity && entity.entityId !== "0x0") {
-
-        }
+        for (const entity of data) {
+            if (entity.entityId === "0x0") continue;
+            // do whatever you need here
+            const model = entity.models.dojo_starter;
+            if (model?.Moves) {
+                await publishOffchainPositionCount(model.Moves);
+            }
+        }
packages/state/src/recs/index.ts (2)

323-330: Infinite loop risk—fetchedEntities never refreshes

fetchedEntities is fetched once outside the loop; the while loop re-uses the same data endlessly. Move the fetch inside the loop (or drop the loop if a single page is intended).

-    const fetchedEntities = await client.getEntities({ … });
-    while (continueFetching) {
-
-        setEntities(fetchedEntities.items, …);
-
-        cursor = fetchedEntities.next_cursor;
+    while (continueFetching) {
+        const page = await client.getEntities({
+            pagination: { limit, cursor, direction: "Forward", order_by: orderBy },
+            clause: clause || undefined,
+            no_hashed_keys: false,
+            models: entityModels,
+            historical,
+        });
+
+        setEntities(page.items, components, logging);
+
+        if (page.items.length < limit) {
+            continueFetching = false;
+        } else {
+            cursor = page.next_cursor;
+        }
+    }

185-190: 🛠️ Refactor suggestion

entities.items is an array—use .length directly

Using Object.keys on an array is slower and semantically confusing.

-        if (Object.keys(entities.items).length < limit) {
+        if (entities.items.length < limit) {
🧰 Tools
🪛 Biome (1.9.4)

[error] 185-185: Do not use template literals if interpolation and special-character handling are not needed.

Unsafe fix: Replace with string literal

(lint/style/noUnusedTemplateLiteral)

packages/sdk/src/internal/toriiQueryBuilder.ts (1)

39-45: ⚠️ Potential issue

Deprecated withOffset method lacks backward compatibility.

The withOffset method is now a no-op that returns this without setting any properties. While it's appropriately marked as deprecated, this implementation could silently break existing code that relies on offset-based pagination.

Consider maintaining backwards compatibility by mapping offset to an equivalent cursor-based approach, or provide more explicit migration guidance in the deprecation notice.

packages/sdk/src/node/index.ts (3)

50-51: ⚠️ Potential issue

Client initialization change.

Changed from torii.createClient to directly instantiating new torii.ToriiClient. This is a breaking change for anyone directly using the returned client.

Ensure this change is documented in migration guides and potentially consider providing a compatibility layer if feasible.


72-85: ⚠️ Potential issue

Updated entity parsing to use items array.

The entity parsing has been updated to handle arrays from the .items property rather than keyed objects. This change simplifies the data structure but is a breaking change for existing code.

Ensure this change is documented in migration guides. Make sure that all examples and documentation are updated to reflect this new data structure.


215-230: ⚠️ Potential issue

Modified generateTypedData signature.

The method signature has changed:

  • Removed optional domain parameter
  • Added optional additionalTypes parameter
  • Domain is now always taken from options.domain

This is a breaking change for code that explicitly passed a domain parameter different from options.domain.

Ensure this change is documented in migration guides and consider if there are cases where passing a different domain was useful functionality.

🧹 Nitpick comments (12)
examples/example-node-worker/src/typescript/models.gen.ts (2)

1-3: Consider using import type for type-only imports

The SchemaType import is only used as a type, not as a value. Using import type would ensure it's removed during compilation.

-import type { SchemaType as ISchemaType } from "@dojoengine/sdk";
+import type { SchemaType as ISchemaType } from "@dojoengine/sdk";

-import { CairoCustomEnum, CairoOption, CairoOptionVariant, BigNumberish } from 'starknet';
+import { CairoCustomEnum, CairoOption, CairoOptionVariant } from 'starknet';
+import type { BigNumberish } from 'starknet';
🧰 Tools
🪛 Biome (1.9.4)

[error] 3-3: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)


100-103: Improve alignment in enum initialization

The indentation in the CairoCustomEnum initialization is inconsistent. Consider aligning the properties for better readability.

			directions: [new CairoCustomEnum({ 
-					Left: "",
-				Right: undefined,
-				Up: undefined,
-				Down: undefined, })],
+				Left: "",
+				Right: undefined,
+				Up: undefined,
+				Down: undefined, 
+			})],
examples/example-vite-react-sdk/src/historical-events.tsx (1)

1-5: Use import type for type-only imports

The ParsedEntity import is only used as a type, not a value. Using import type would ensure it's removed during compilation.

import {
    KeysClause,
-    ParsedEntity,
    HistoricalToriiQueryBuilder,
} from "@dojoengine/sdk";
+import type { ParsedEntity } from "@dojoengine/sdk";
🧰 Tools
🪛 Biome (1.9.4)

[error] 1-5: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

packages/sdk/src/__tests__/zustand.perf.test.ts (1)

131-133: Improve property access notation and avoid non-null assertions

The code was reformatted for better readability, but we can further improve it by using optional chaining instead of non-null assertions to make the code safer.

-                        draft.entities[
-                            "entity500"
-                        ].models.world!.item!.durability = 75;
+                        const entity = draft.entities["entity500"];
+                        if (entity?.models?.world?.item) {
+                            entity.models.world.item.durability = 75;
+                        }
🧰 Tools
🪛 Biome (1.9.4)

[error] 131-133: Forbidden non-null assertion.

(lint/style/noNonNullAssertion)


[error] 131-133: Forbidden non-null assertion.

Unsafe fix: Replace with optional chain operator ?. This operator includes runtime checks, so it is safer than the compile-only non-null assertion operator

(lint/style/noNonNullAssertion)


[error] 132-132: The computed expression can be simplified without the use of a string literal.

Unsafe fix: Use a literal key instead.

(lint/complexity/useLiteralKeys)

packages/sdk/src/web/experimental/index.ts (1)

30-30: Remove redundant type annotation

The historical parameter's boolean type is trivially inferred from its initialization.

-        getEvents: async (query: torii.Query, historical: boolean = false) => {
+        getEvents: async (query: torii.Query, historical = false) => {
🧰 Tools
🪛 Biome (1.9.4)

[error] 30-30: This type annotation is trivially inferred from its initialization.

Safe fix: Remove the type annotation.

(lint/style/noInferrableTypes)

examples/example-node-worker/src/typescript/contracts.gen.ts (2)

1-3: Update imports to use import type for type-only imports

Using import type ensures that type-only imports are removed by the compilers and avoids loading unnecessary modules at runtime.

-import { DojoProvider, DojoCall } from "@dojoengine/core";
-import { Account, AccountInterface, BigNumberish, CairoOption, CairoCustomEnum, ByteArray } from "starknet";
+import { DojoProvider } from "@dojoengine/core";
+import type { DojoCall } from "@dojoengine/core";
+import { Account } from "starknet";
+import type { AccountInterface, BigNumberish, CairoOption, CairoCustomEnum, ByteArray } from "starknet";
 import * as models from "./models.gen";
🧰 Tools
🪛 Biome (1.9.4)

[error] 1-1: All these imports are only used as types.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Use import type.

(lint/style/useImportType)


[error] 2-2: Some named imports are only used as types.

This import is only used as a type.

This import is only used as a type.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)


15-26: Consider adding retry logic for contract calls

Contract calls can fail due to network issues or congestion. Consider implementing retry logic with exponential backoff for more robust error handling.

 const actions_move = async (snAccount: Account | AccountInterface, direction: CairoCustomEnum) => {
+    const maxRetries = 3;
+    let retries = 0;
     try {
-        return await provider.execute(
-            snAccount,
-            build_actions_move_calldata(direction),
-            "dojo_starter",
-        );
+        while (true) {
+            try {
+                return await provider.execute(
+                    snAccount,
+                    build_actions_move_calldata(direction),
+                    "dojo_starter",
+                );
+            } catch (error) {
+                if (retries >= maxRetries || !isRetryableError(error)) {
+                    throw error;
+                }
+                retries++;
+                console.log(`Retrying execution (${retries}/${maxRetries})...`);
+                await new Promise(resolve => setTimeout(resolve, 1000 * Math.pow(2, retries)));
+            }
+        }
     } catch (error) {
         console.error(error);
         throw error;
     }
 };

+// Helper to determine if an error is retryable
+const isRetryableError = (error: any): boolean => {
+    // Customize based on the specific error types you want to retry
+    const errorMessage = String(error);
+    return errorMessage.includes("network") || 
+           errorMessage.includes("timeout") ||
+           errorMessage.includes("rate limit");
+};
packages/sdk/src/web/state/zustand.ts (1)

14-14: Centralized deep merge utility imported

Importing deepMerge and MergedModels from a dedicated module improves code organization and reusability. Use import type for MergedModels if it's only used as a type.

-import { deepMerge, MergedModels } from "../../internal/merge";
+import { deepMerge } from "../../internal/merge";
+import type { MergedModels } from "../../internal/merge";
🧰 Tools
🪛 Biome (1.9.4)

[error] 14-14: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

packages/sdk/src/internal/parseEntities.ts (2)

58-59: Return the array directly—Object.values is redundant

result is already an array. Wrapping it in Object.values creates an unnecessary shallow copy and slightly obscures intent.

-    return Object.values(result);
+    return result;

50-52: Use a plain string literal to silence Biome warning

There is no interpolation in this template literal; switch to a normal string to remove the linter error.

-            console.log(`Parsed entity:`, parsedEntity);
+            console.log("Parsed entity:", parsedEntity);
🧰 Tools
🪛 Biome (1.9.4)

[error] 50-50: Do not use template literals if interpolation and special-character handling are not needed.

Unsafe fix: Replace with string literal

(lint/style/noUnusedTemplateLiteral)

packages/sdk/src/internal/merge.ts (2)

57-113: Consider using more specific type than 'any' for enum content.

The implementation for merging Cairo custom enums creates new instances to maintain immutability, but uses any type for the enum content.

-        const newEnumContent: Record<string, any> = {};
+        const newEnumContent: Record<string, unknown> = {};
-        const newEnumContent: Record<string, any> = {};
+        const newEnumContent: Record<string, unknown> = {};
🧰 Tools
🪛 Biome (1.9.4)

[error] 76-76: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)


[error] 96-96: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)


123-160: Consider using a more specific type than 'any' in the deep merge function.

The deep merge function is well-implemented with appropriate special case handling for Cairo types, but the result type could be more specific than any.

-    const result = { ...target } as Record<string, any>;
+    const result = { ...target } as Record<string, unknown>;

Additionally, consider adding more explanatory comments about the merging strategy for nested objects to make the implementation more maintainable.

🧰 Tools
🪛 Biome (1.9.4)

[error] 133-133: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro (Legacy)

📥 Commits

Reviewing files that changed from the base of the PR and between 503223b and 2cc16f7.

⛔ Files ignored due to path filters (5)
  • examples/example-vite-react-phaser-recs/src/dojo/generated/setup.ts is excluded by !**/generated/**
  • examples/example-vite-react-pwa-recs/src/dojo/generated/setup.ts is excluded by !**/generated/**
  • examples/example-vite-react-threejs-recs/src/dojo/generated/setup.ts is excluded by !**/generated/**
  • examples/example-vue-app-recs/src/dojo/generated/setup.ts is excluded by !**/generated/**
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (40)
  • .changeset/thirty-cheetahs-design.md (1 hunks)
  • .github/workflows/ci.yaml (1 hunks)
  • examples/example-node-worker/dojoConfig.ts (1 hunks)
  • examples/example-node-worker/env.ts (1 hunks)
  • examples/example-node-worker/main.ts (1 hunks)
  • examples/example-node-worker/package.json (1 hunks)
  • examples/example-node-worker/src/typescript/contracts.gen.ts (1 hunks)
  • examples/example-node-worker/src/typescript/models.gen.ts (1 hunks)
  • examples/example-node-worker/tsconfig.json (1 hunks)
  • examples/example-nodejs-bot/package.json (1 hunks)
  • examples/example-vite-react-app-recs/src/dojo/setup.ts (2 hunks)
  • examples/example-vite-react-sdk/src/historical-events.tsx (2 hunks)
  • package.json (1 hunks)
  • packages/core/package.json (1 hunks)
  • packages/react/package.json (2 hunks)
  • packages/sdk/package.json (3 hunks)
  • packages/sdk/src/__tests__/parseEntities.test.ts (7 hunks)
  • packages/sdk/src/__tests__/parseHistoricalEvents.test.ts (0 hunks)
  • packages/sdk/src/__tests__/toriiQueryBuilder.test.ts (6 hunks)
  • packages/sdk/src/__tests__/zustand.perf.test.ts (1 hunks)
  • packages/sdk/src/internal/errors.ts (1 hunks)
  • packages/sdk/src/internal/generateTypedData.ts (2 hunks)
  • packages/sdk/src/internal/merge.ts (1 hunks)
  • packages/sdk/src/internal/models.ts (1 hunks)
  • packages/sdk/src/internal/parseEntities.ts (4 hunks)
  • packages/sdk/src/internal/parseHistoricalEvents.ts (0 hunks)
  • packages/sdk/src/internal/toriiQueryBuilder.ts (6 hunks)
  • packages/sdk/src/internal/types.ts (5 hunks)
  • packages/sdk/src/node/index.ts (12 hunks)
  • packages/sdk/src/node/worker.ts (1 hunks)
  • packages/sdk/src/web/experimental/index.ts (4 hunks)
  • packages/sdk/src/web/index.ts (11 hunks)
  • packages/sdk/src/web/react/hooks/entities.ts (1 hunks)
  • packages/sdk/src/web/react/hooks/events.ts (3 hunks)
  • packages/sdk/src/web/react/hooks/hooks.ts (4 hunks)
  • packages/sdk/src/web/state/zustand.ts (2 hunks)
  • packages/state/src/recs/index.ts (6 hunks)
  • packages/torii-client/package.json (2 hunks)
  • packages/torii-wasm/dojo.c (1 hunks)
  • turbo.json (1 hunks)
💤 Files with no reviewable changes (2)
  • packages/sdk/src/internal/parseHistoricalEvents.ts
  • packages/sdk/src/tests/parseHistoricalEvents.test.ts
✅ Files skipped from review due to trivial changes (2)
  • .changeset/thirty-cheetahs-design.md
  • .github/workflows/ci.yaml
🚧 Files skipped from review as they are similar to previous changes (22)
  • examples/example-nodejs-bot/package.json
  • packages/react/package.json
  • packages/torii-wasm/dojo.c
  • examples/example-node-worker/dojoConfig.ts
  • packages/core/package.json
  • examples/example-node-worker/env.ts
  • packages/torii-client/package.json
  • packages/sdk/src/internal/errors.ts
  • packages/sdk/package.json
  • examples/example-vite-react-app-recs/src/dojo/setup.ts
  • package.json
  • turbo.json
  • packages/sdk/src/tests/parseEntities.test.ts
  • packages/sdk/src/web/react/hooks/hooks.ts
  • packages/sdk/src/internal/generateTypedData.ts
  • packages/sdk/src/web/react/hooks/entities.ts
  • examples/example-node-worker/tsconfig.json
  • packages/sdk/src/node/worker.ts
  • packages/sdk/src/tests/toriiQueryBuilder.test.ts
  • packages/sdk/src/internal/types.ts
  • packages/sdk/src/internal/models.ts
  • packages/sdk/src/web/index.ts
🧰 Additional context used
🧬 Code Graph Analysis (7)
packages/sdk/src/node/index.ts (6)
packages/sdk/src/internal/convertClauseToEntityKeysClause.ts (1)
  • intoEntityKeysClause (4-36)
packages/sdk/src/internal/parseEntities.ts (1)
  • parseEntities (10-59)
packages/sdk/src/internal/types.ts (2)
  • ToriiResponse (248-253)
  • ToriiQueryBuilder (557-557)
packages/sdk/src/internal/generateTypedData.ts (1)
  • generateTypedData (13-48)
packages/sdk/src/internal/errors.ts (2)
  • NO_SIGNER (1-1)
  • NO_IDENTITY (2-2)
packages/sdk/src/internal/toriiQueryBuilder.ts (1)
  • ToriiQueryBuilder (24-143)
packages/sdk/src/internal/toriiQueryBuilder.ts (1)
packages/sdk/src/internal/types.ts (2)
  • ToriiQueryBuilder (557-557)
  • SchemaType (50-70)
examples/example-node-worker/src/typescript/contracts.gen.ts (2)
examples/example-node-worker/src/typescript/models.gen.ts (1)
  • direction (71-76)
packages/core/src/types/index.ts (1)
  • DojoCall (130-134)
examples/example-node-worker/src/typescript/models.gen.ts (1)
packages/sdk/src/internal/types.ts (1)
  • SchemaType (50-70)
examples/example-vite-react-sdk/src/historical-events.tsx (1)
packages/sdk/src/internal/toriiQueryBuilder.ts (1)
  • HistoricalToriiQueryBuilder (149-159)
packages/sdk/src/internal/merge.ts (3)
packages/sdk/src/web/queryBuilder.ts (1)
  • value (191-193)
examples/example-node-worker/src/typescript/models.gen.ts (1)
  • SchemaType (80-94)
packages/sdk/src/internal/types.ts (2)
  • SchemaType (50-70)
  • ParsedEntity (228-237)
packages/state/src/recs/index.ts (1)
packages/core/src/provider/DojoProvider.ts (1)
  • entities (104-123)
🪛 Biome (1.9.4)
examples/example-node-worker/main.ts

[error] 176-176: This code is unreachable

... because this statement will return from the function beforehand

(lint/correctness/noUnreachable)

packages/sdk/src/web/state/zustand.ts

[error] 14-14: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

packages/sdk/src/internal/toriiQueryBuilder.ts

[error] 133-133: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

examples/example-node-worker/src/typescript/contracts.gen.ts

[error] 1-1: All these imports are only used as types.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Use import type.

(lint/style/useImportType)


[error] 2-2: Some named imports are only used as types.

This import is only used as a type.

This import is only used as a type.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

examples/example-node-worker/src/typescript/models.gen.ts

[error] 3-3: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

examples/example-vite-react-sdk/src/historical-events.tsx

[error] 1-5: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

packages/sdk/src/__tests__/zustand.perf.test.ts

[error] 131-133: Forbidden non-null assertion.

(lint/style/noNonNullAssertion)


[error] 131-133: Forbidden non-null assertion.

Unsafe fix: Replace with optional chain operator ?. This operator includes runtime checks, so it is safer than the compile-only non-null assertion operator

(lint/style/noNonNullAssertion)


[error] 132-132: The computed expression can be simplified without the use of a string literal.

Unsafe fix: Use a literal key instead.

(lint/complexity/useLiteralKeys)

packages/sdk/src/internal/merge.ts

[error] 76-76: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)


[error] 96-96: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)


[error] 133-133: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

packages/sdk/src/internal/parseEntities.ts

[error] 50-50: Do not use template literals if interpolation and special-character handling are not needed.

Unsafe fix: Replace with string literal

(lint/style/noUnusedTemplateLiteral)

packages/sdk/src/web/experimental/index.ts

[error] 30-30: This type annotation is trivially inferred from its initialization.

Safe fix: Remove the type annotation.

(lint/style/noInferrableTypes)


[error] 101-101: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

packages/state/src/recs/index.ts

[error] 185-185: Do not use template literals if interpolation and special-character handling are not needed.

Unsafe fix: Replace with string literal

(lint/style/noUnusedTemplateLiteral)

🪛 Gitleaks (8.21.2)
examples/example-node-worker/package.json

7-7: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


8-8: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build
🔇 Additional comments (36)
examples/example-vite-react-sdk/src/historical-events.tsx (1)

14-14: Updated to use the specialized HistoricalToriiQueryBuilder

Good update to use HistoricalToriiQueryBuilder which automatically sets the historical flag to true and encapsulates pagination parameters.

examples/example-node-worker/package.json (1)

14-18: Well-structured dependencies for worker functionality

The added dependencies appropriately support the worker functionality with Torii WASM integration, environment variable handling, Starknet integration, and WebSocket support.

packages/sdk/src/web/experimental/index.ts (4)

24-24: Client initialization correctly changed to synchronous instantiation

The change from asynchronous to synchronous client creation aligns with similar changes in other SDK modules, providing a more consistent API.


28-28: Entity and event parsing now correctly extracts items array

The updated code properly extracts the .items array from query results before passing to parsing functions, aligning with the standardized data handling approach across the SDK.

Also applies to: 31-33


39-39: Query parameter rename for consistency

Parameter renamed from dont_include_hashed_keys to no_hashed_keys for consistency with updates in other SDK files.

Also applies to: 82-82


54-59: Entity update callback refactored to process arrays

The callback now correctly processes arrays of entity data rather than objects keyed by entity ID, matching the pattern adopted in other SDK components.

examples/example-node-worker/src/typescript/contracts.gen.ts (1)

5-59: Well-structured contract interaction helpers

The setupWorld function provides a clean API for contract interactions with proper error handling and type safety. The function follows Dojo conventions by providing both execution and calldata building functions.

packages/sdk/src/web/react/hooks/events.ts (5)

3-7: Improved type imports with ParsedEntity

The addition of the ParsedEntity type import improves type safety throughout the file.


27-29: State update logic simplified

Changed from a return statement to a direct state update call, which is cleaner and more explicit.


31-39: Event filtering logic enhanced

Now filters out events with zero entityId and updates state with the first valid event instead of the last. This is a better approach for most use cases.


58-58: Historical events state properly typed

The state is now explicitly typed as an array of ParsedEntity<Schema> and the processing function is simplified.

Also applies to: 66-68


70-75: Historical events update improved

Now prepends all filtered events rather than a single event, providing more complete historical data.

packages/sdk/src/web/state/zustand.ts (2)

71-99: Enhanced nested model merging

The entity merging logic is improved with a more granular nested loop approach that properly handles namespace and model hierarchies. The use of deepMerge ensures proper handling of Cairo-specific types.


140-149: Consistent use of deepMerge in updateEntity

The updateEntity method now uses the same deepMerge utility as mergeEntities, ensuring consistent merging behavior across the store.

packages/sdk/src/internal/merge.ts (4)

1-11: Well-structured type checking utility for CairoOption.

The implementation properly uses the type guard pattern to safely check if a value is a CairoOption instance, with good documentation.


13-46: Merge strategy for CairoOption implements proper immutability.

The function creates new instances of CairoOption rather than modifying existing ones, which maintains immutability. The merging logic prioritizes "Some" values from the source, falling back to the target when needed.


48-55: Appropriate type guard implementation for CairoCustomEnum.

The implementation is clean and follows the same pattern as the CairoOption type guard.


115-121: Clean utility type definition for merged models.

The type effectively extracts model types from the schema, which will be useful for type-checking throughout the codebase.

packages/sdk/src/internal/toriiQueryBuilder.ts (8)

10-20: Refactored structure to use nested pagination object.

The default options now use a nested pagination object that contains limit, cursor, direction, and order_by properties, aligning with a cursor-based pagination approach. The property names have been updated from dont_include_hashed_keys to no_hashed_keys and from entity_models to models for consistency.


35-36: Updated method to use nested pagination property.

The withLimit method has been updated to set the limit within the pagination object.


47-63: Added new cursor-based pagination methods.

The new withCursor and withDirection methods support cursor-based pagination, which is a more scalable approach for large datasets. These methods provide building blocks for the new pagination strategy.


78-79: Updated property name for consistency.

The includeHashedKeys method now modifies no_hashed_keys rather than dont_include_hashed_keys, reflecting the renamed property.


90-103: Updated order methods to use nested pagination structure.

The order-related methods now modify properties within the pagination object rather than directly on the query object.


110-119: Updated entity model methods to use renamed property.

The entity model related methods now use the models property instead of entity_models for consistency.


133-142: Updated pagination static method to use cursor-based approach.

The static withPagination method now uses cursor, limit, and direction parameters instead of page and pageSize, reflecting the transition to cursor-based pagination.

Consider using a more specific type than any for the Record generic:

-    static withPagination<T extends Record<string, Record<string, any>>>(
+    static withPagination<T extends Record<string, Record<string, unknown>>>(
🧰 Tools
🪛 Biome (1.9.4)

[error] 133-133: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)


145-159: Added HistoricalToriiQueryBuilder for specialized historical queries.

The new subclass extends ToriiQueryBuilder with the historical flag set to true by default, which provides a cleaner API for historical data queries.

packages/sdk/src/node/index.ts (10)

19-30: Improved error handling with neverthrow library.

The addition of the Result type from neverthrow and error constants enables more explicit and functional error handling, making the SDK more robust.


34-36: Added additional exports for worker, types, and models.

These exports make internal utilities more accessible to consumers of the SDK, improving the overall developer experience.


64-65: Updated property name for consistency.

The check now uses no_hashed_keys instead of dont_include_hashed_keys, reflecting the renamed property in the query builder.


119-120: Updated property name in event query checks.

The property check for event queries has been updated to use no_hashed_keys instead of dont_include_hashed_keys.


127-130: Updated event message parsing to use items array.

Similar to entity parsing, event message parsing has been updated to handle arrays from the .items property rather than keyed objects.


138-143: Updated callback handling for entity data arrays.

The event message subscription callback now handles entity data as an array, matching the changes in entity parsing.


189-190: Updated entity retrieval to use items array.

The getEntities method now parses items from the response, consistent with the changes in entity parsing.


202-204: Updated event message retrieval to use items array.

The getEventMessages method now parses items from the response, consistent with the changes in event message parsing.


390-391: Updated entity query method to use items array.

The toriiQueryIntoHashedKeys method now parses items from the response, consistent with the changes in entity parsing.


401-415: Updated event query method to use historical query builder.

The toriiEventMessagesQueryIntoHashedKeys method now accepts a ToriiQueryBuilder, making it consistent with the new query builder structure.

Comment on lines +6 to +10
"scripts": {
"bun": "IDENTITY=0x0127fd5f1fe78a71f8bcd1fec63e3fe2f0486b6ecd5c86a0466c3a21fa5cfcec SECRET_KEY=0xc5b2fcab997346f3ea1c00b002ecf6f382c5f9c9659a3894eb783c5320f912 bun run main.ts",
"node": "IDENTITY=0x0127fd5f1fe78a71f8bcd1fec63e3fe2f0486b6ecd5c86a0466c3a21fa5cfcec SECRET_KEY=0xc5b2fcab997346f3ea1c00b002ecf6f382c5f9c9659a3894eb783c5320f912 node main.ts",
"build": "tsc -b"
},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Remove hardcoded credentials from scripts

The scripts contain hardcoded private keys and identity values, which is a security risk. These should be moved to environment variables loaded from a .env file instead.

    "scripts": {
-        "bun": "IDENTITY=0x0127fd5f1fe78a71f8bcd1fec63e3fe2f0486b6ecd5c86a0466c3a21fa5cfcec SECRET_KEY=0xc5b2fcab997346f3ea1c00b002ecf6f382c5f9c9659a3894eb783c5320f912 bun run main.ts",
-        "node": "IDENTITY=0x0127fd5f1fe78a71f8bcd1fec63e3fe2f0486b6ecd5c86a0466c3a21fa5cfcec SECRET_KEY=0xc5b2fcab997346f3ea1c00b002ecf6f382c5f9c9659a3894eb783c5320f912 node main.ts",
+        "bun": "bun run main.ts",
+        "node": "node main.ts",
        "build": "tsc -b"
    },

Since you've already added dotenv as a dependency, you should use it to load these values from a .env file (which should be added to .gitignore).

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"scripts": {
"bun": "IDENTITY=0x0127fd5f1fe78a71f8bcd1fec63e3fe2f0486b6ecd5c86a0466c3a21fa5cfcec SECRET_KEY=0xc5b2fcab997346f3ea1c00b002ecf6f382c5f9c9659a3894eb783c5320f912 bun run main.ts",
"node": "IDENTITY=0x0127fd5f1fe78a71f8bcd1fec63e3fe2f0486b6ecd5c86a0466c3a21fa5cfcec SECRET_KEY=0xc5b2fcab997346f3ea1c00b002ecf6f382c5f9c9659a3894eb783c5320f912 node main.ts",
"build": "tsc -b"
},
"scripts": {
"bun": "bun run main.ts",
"node": "node main.ts",
"build": "tsc -b"
},
🧰 Tools
🪛 Gitleaks (8.21.2)

7-7: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


8-8: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

Comment on lines +101 to +100
(_: string, entityData: any) => {
try {
if (callback) {
const data = { [entityId]: entityData };
const parsedData = historical
? parseHistoricalEvents<T>(data)
: parseEntities<T>(data);
const parsedData = parseEntities<T>([
entityData,
]);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Event update callback refactored to process arrays

Callback modernized to handle arrays of entity data, but uses any type which should be replaced with a more specific type.

-                    (_: string, entityData: any) => {
+                    (_: string, entityData: torii.Entity) => {

🏁 Script executed:

#!/bin/bash
# Check what type is used in similar contexts to determine the correct type
rg -A 1 "onEventMessageUpdated\(" packages/

Length of output: 710


🏁 Script executed:

#!/bin/bash
# Inspect parseEntities signature to determine the expected input type
echo "---- parseEntities definition ----"
rg -n -A2 -B2 "export function parseEntities" -n packages/sdk

# Inspect the onEventMessageUpdated callback in experimental index file
echo "---- onEventMessageUpdated callback in experimental/index.ts ----"
rg -A10 -B5 "onEventMessageUpdated" -n packages/sdk/src/web/experimental/index.ts

Length of output: 1548


🏁 Script executed:

#!/bin/bash
# Inspect import statements to confirm how to reference torii.Entity
echo "---- Import statements in experimental/index.ts ----"
head -n 50 packages/sdk/src/web/experimental/index.ts

Length of output: 2004


Strengthen callback parameter types

For improved type safety, replace the any type on the entityData parameter with torii.Entity, which aligns with the parseEntities<T>(entities: torii.Entity[]) signature.

File: packages/sdk/src/web/experimental/index.ts
Lines: 101–106

-                    (_: string, entityData: any) => {
+                    (_: string, entityData: torii.Entity) => {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
(_: string, entityData: any) => {
try {
if (callback) {
const data = { [entityId]: entityData };
const parsedData = historical
? parseHistoricalEvents<T>(data)
: parseEntities<T>(data);
const parsedData = parseEntities<T>([
entityData,
]);
(_: string, entityData: torii.Entity) => {
try {
if (callback) {
const parsedData = parseEntities<T>([
entityData,
]);
🧰 Tools
🪛 Biome (1.9.4)

[error] 101-101: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

Comment on lines +175 to +176
return [];
return [sub];
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Unreachable code after the first return

The second return [sub]; can never execute—Biome rightfully flags it.

-    return [];
-    return [sub];
+    return [sub];
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return [];
return [sub];
return [sub];
🧰 Tools
🪛 Biome (1.9.4)

[error] 176-176: This code is unreachable

... because this statement will return from the function beforehand

(lint/correctness/noUnreachable)

Comment on lines +246 to 247
if (Object.keys(entities.items).length === 0) {
continueFetching = false;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Same length check issue in getEvents

-        if (Object.keys(entities.items).length === 0) {
+        if (entities.items.length === 0) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (Object.keys(entities.items).length === 0) {
continueFetching = false;
if (entities.items.length === 0) {
continueFetching = false;

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🔭 Outside diff range comments (3)
packages/state/src/recs/index.ts (1)

516-555: ⚠️ Potential issue

Entity database insertion logic doesn't match new array type

The function signature has been updated to accept ToriiEntity[] (an array), but the implementation still treats it as an object with Object.entries(entities). This will cause runtime errors.

Update the implementation to handle arrays:

async function insertEntitiesInDB(
    db: IDBDatabase,
    entities: ToriiEntity[]
): Promise<void> {
    return new Promise((resolve, reject) => {
        const transaction = db.transaction(["entities"], "readwrite");
        const store = transaction.objectStore("entities");

        let completed = 0;
        let error: Error | null = null;

        // Handle transaction completion
        transaction.oncomplete = () => {
            if (error) {
                reject(error);
            } else {
                resolve();
            }
        };

        transaction.onerror = () => {
            reject(transaction.error);
        };

        // Store each entity
-        for (const [entityId, data] of Object.entries(entities)) {
+        for (const entity of entities) {
            const entityData = {
-                id: entityId,
+                id: entity.hashed_keys,
-                ...data,
+                ...entity.models,
            };

            const request = store.put(entityData);
            completed++;

            request.onerror = () => {
                error = request.error;
            };
        }
    });
}
packages/sdk/src/web/index.ts (2)

257-283: 🛠️ Refactor suggestion

Inconsistent error handling in sendMessage

The function now returns a Result type for account-related errors, but still throws errors for other failures. This creates inconsistent error handling.

Refactor to consistently use the Result type for all error cases:

sendMessage: async (
    data: TypedData,
    account?: Account
): Promise<Result<Uint8Array, string>> => {
    if (!account) {
        return err(NO_ACCOUNT);
    }
    try {
        // Sign the typed data
        const signature: Signature = await account.signMessage(data);

        // Stringify typed data for publishing
        const dataString = JSON.stringify(data);
        // Publish the signed message
        return ok(
            await client.publishMessage(
                dataString,
                Array.isArray(signature)
                    ? signature
                    : [signature.r.toString(), signature.s.toString()]
            )
        );
    } catch (error) {
        console.error("Failed to send message:", error);
-        throw error;
+        return err(error instanceof Error ? error.message : String(error));
    }
},

401-424: ⚠️ Potential issue

Inconsistent array extraction in toriiEventMessagesQueryIntoHashedKeys

In one line you're extracting events.items but in another just passing events, which may cause type errors.

toriiEventMessagesQueryIntoHashedKeys: async <H extends boolean>(
    query: ToriiQueryBuilder<T>
): Promise<[ToriiResponse<T, H>, torii.EntityKeysClause[]]> => {
    const q = query.build();

    const events = await client.getEventMessages(q);
    return [
        parseEntities(events.items) as ToriiResponse<T, H>,
        // @ts-expect-error TODO: fix type here
-        intoEntityKeysClause<T>(q.clause, events.items),
+        intoEntityKeysClause<T>(q.clause, parseEntities(events.items)),
    ];
},
♻️ Duplicate comments (15)
packages/sdk/src/web/experimental/index.ts (1)

101-101: Strengthen callback parameter types

For improved type safety, replace the any type on the entityData parameter with torii.Entity, which aligns with the parseEntities<T>(entities: torii.Entity[]) signature.

-                    (_: string, entityData: any) => {
+                    (_: string, entityData: torii.Entity) => {
🧰 Tools
🪛 Biome (1.9.4)

[error] 101-101: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

packages/sdk/src/internal/parseEntities.ts (1)

126-133: u64 can overflow JS number; parse it as BigInt

A 64-bit unsigned integer can exceed Number.MAX_SAFE_INTEGER (2⁵³-1), leading to precision loss if parsed with Number.parseInt. Treat it the same way as the larger integer types.

-        case "u64":
-            return Number.parseInt(value.value as string, 16);
+        case "u64":
+        case "i256":
+        case "i128":
+        case "u256":
+        case "u128":
+            return BigInt(value.value as string);
-        case "i256":
-        case "i128":
-        case "u256":
-        case "u128":
-            return BigInt(value.value as string);
examples/example-node-worker/main.ts (4)

21-25: Wrong global identifier: use WebSocket, not Websocket

Browsers (and most polyfills) expose global.WebSocket (uppercase S).
Assigning to Websocket leaves libraries that expect WebSocket undefined.

-// @ts-ignore
-global.Websocket = w3cwebsocket;
+// @ts-ignore
+global.WebSocket = w3cwebsocket;

125-133: initPositionFromEvent mutates the shared constant

pc is an alias of defaultPositionCount, so every call mutates the constant and all future callers see cumulative side-effects.

-const pc = defaultPositionCount;
+const pc: PositionCount = { ...defaultPositionCount };

137-151: Potential data loss with data.pop()

data is already the argument supplied by the subscription.
Calling pop() both mutates the array and drops all but the last entity; if multiple entities arrive in the same batch they are silently ignored.

Iterate instead:

for (const entity of data) {
  // handle each entity
  if (entity && entity.entityId !== "0x0") {
    const model = entity.models.dojo_starter;
    if (model?.Moves) {
      await publishOffchainPositionCount(model.Moves);
    }
  }
}

175-176: Unreachable code after the first return

The second return [sub]; can never execute—Biome rightfully flags it.

-    return [];
-    return [sub];
+    return [sub];
🧰 Tools
🪛 Biome (1.9.4)

[error] 176-176: This code is unreachable

... because this statement will return from the function beforehand

(lint/correctness/noUnreachable)

packages/state/src/recs/index.ts (3)

307-333: ⚠️ Potential issue

Infinite loop in getEntitiesQuery

Inside the while (continueFetching) loop you never issue a new client.getEntities call—fetchedEntities is fetched once outside the loop, so cursor advances but data doesn't refresh.

Move the fetch inside the loop:

while (continueFetching) {
+    const fetchedEntities = await client.getEntities({
+        pagination: {
+            limit,
+            cursor,
+            direction: "Forward",
+            order_by: orderBy,
+        },
+        clause: clause || undefined,
+        no_hashed_keys: false,
+        models: entityModels,
+        historical,
+    });

    if (logging)
        console.log(
            `Fetched ${Object.keys(fetchedEntities.items).length} entities ${fetchedEntities.next_cursor}`
        );

    setEntities(fetchedEntities.items, components, logging);

-    if (Object.keys(fetchedEntities.items).length < limit) {
+    if (fetchedEntities.items.length < limit) {
        continueFetching = false;
    } else {
        cursor = fetchedEntities.next_cursor;
    }
}

162-193: 🛠️ Refactor suggestion

Fixed pagination logic needs array length check adjustment

The pagination implementation has been correctly updated from offset-based to cursor-based, but there's an inconsistency in how you're determining if there are more items to fetch.

Since entities.items is now an array, you should use array length directly:

-        if (Object.keys(entities.items).length < limit) {
+        if (entities.items.length < limit) {
            continueFetching = false;
        } else {
            cursor = entities.next_cursor;
        }
🧰 Tools
🪛 Biome (1.9.4)

[error] 185-185: Do not use template literals if interpolation and special-character handling are not needed.

Unsafe fix: Replace with string literal

(lint/style/noUnusedTemplateLiteral)


225-250: 🛠️ Refactor suggestion

Same array length check issue in getEvents

Similar to the issue in getEntities, the length check should use array length directly.

-        if (Object.keys(entities.items).length === 0) {
+        if (entities.items.length === 0) {
            continueFetching = false;
        } else {
            cursor = entities.next_cursor;
        }
examples/example-node-worker/src/typescript/models.gen.ts (1)

38-46: ⚠️ Potential issue

Fix typo in "positition" field name

There appears to be a typo in the positition field name (should be "position").

export interface PositionCount {
	identity: string;
-	positition: Array<[BigNumberish, BigNumberish]>;
+	position: Array<[BigNumberish, BigNumberish]>;
}

export interface PositionCountValue {
-	positition: Array<[BigNumberish, BigNumberish]>;
+	position: Array<[BigNumberish, BigNumberish]>;
}

Since this affects the interface structure and is reflected in the schema initialization, this typo should be fixed in the generator if possible.

packages/sdk/src/internal/toriiQueryBuilder.ts (1)

39-45: ⚠️ Potential issue

withOffset method is now a no-op without backward compatibility

The withOffset method has been deprecated and converted to a no-op that simply returns this. While it's appropriately marked as deprecated, this implementation breaks backward compatibility without offering a migration path.

Consider one of these approaches:

  1. Map offset to an equivalent cursor-based implementation (preferred)
  2. Provide more detailed guidance in the deprecation notice about how to migrate
  3. Throw a meaningful error with migration instructions instead of silently returning
/**
 * Set the offset for pagination
 * @deprecated Use `withCursor` instead
 */
withOffset(): ToriiQueryBuilder<T> {
-    return this;
+    console.warn("withOffset is deprecated. Use withCursor instead. This will be removed in a future release.");
+    // Return this for backward compatibility, but issue a warning
+    return this;
}
packages/sdk/src/node/index.ts (4)

50-50: ⚠️ Potential issue

Changed client initialization approach

Changed from asynchronous torii.createClient() to directly instantiating new torii.ToriiClient().

This is a breaking change for code that relies on the specific behavior of the returned client. Ensure this change is documented in migration guides and consider providing a compatibility layer if feasible.


72-84: ⚠️ Potential issue

Updated entity parsing to use items array

The entity parsing has been updated to handle arrays from the .items property rather than keyed objects.

This change simplifies the data structure but breaks backward compatibility. Ensure this change is documented in migration guides and all examples are updated to reflect this new data structure.


219-230: ⚠️ Potential issue

Modified generateTypedData signature

The method signature has changed by:

  • Removing the optional domain parameter
  • Adding optional additionalTypes parameter
  • Always using options.domain for the domain

This is a breaking change for code that explicitly passed a domain parameter different from options.domain. Ensure this change is documented in migration guides and consider if there are cases where passing a different domain was useful functionality.


251-265: 🛠️ Refactor suggestion

Inconsistent error handling in sendMessage

Despite returning a Result type for validation errors, the function still throws an error in the catch block rather than returning an err Result.

Make the error handling consistent by returning an err in the catch block:

try {
    const td = new torii.TypedData(JSON.stringify(data)).encode(
        options.identity
    );

    const sig = options.signer.sign(td);

    const dataString = JSON.stringify(data);

    return ok(
        await client.publishMessage(dataString, [
            sig.r.toString(),
            sig.s.toString(),
        ])
    );
} catch (error) {
    console.error("Failed to send message:", error);
-    throw error;
+    return err(`Failed to send message: ${error instanceof Error ? error.message : String(error)}`);
}
🧹 Nitpick comments (14)
examples/example-vite-react-sdk/src/historical-events.tsx (1)

1-5: Consider importing types with the 'type' keyword

Several imports are only used as types and could benefit from explicit type imports.

 import {
-    KeysClause,
-    ParsedEntity,
-    HistoricalToriiQueryBuilder,
+    KeysClause,
+    type ParsedEntity,
+    HistoricalToriiQueryBuilder,
 } from "@dojoengine/sdk";
🧰 Tools
🪛 Biome (1.9.4)

[error] 1-5: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

packages/sdk/src/__tests__/zustand.perf.test.ts (1)

131-133: Style improvement in property access

The reformatting of the property access expression improves readability by breaking complex nested property access across multiple lines.

Consider addressing the static analysis warnings by:

  1. Using optional chaining instead of non-null assertions
  2. Using dot notation for the entity key (if the key is a valid identifier)
-                        draft.entities[
-                            "entity500"
-                        ].models.world!.item!.durability = 75;
+                        draft.entities.entity500.models.world?.item?.durability = 75;
🧰 Tools
🪛 Biome (1.9.4)

[error] 131-133: Forbidden non-null assertion.

(lint/style/noNonNullAssertion)


[error] 131-133: Forbidden non-null assertion.

Unsafe fix: Replace with optional chain operator ?. This operator includes runtime checks, so it is safer than the compile-only non-null assertion operator

(lint/style/noNonNullAssertion)


[error] 132-132: The computed expression can be simplified without the use of a string literal.

Unsafe fix: Use a literal key instead.

(lint/complexity/useLiteralKeys)

packages/sdk/src/web/state/zustand.ts (1)

14-14: Centralized merge utilities

Good refactoring to import the deep merge utilities from a centralized internal module rather than using local implementations. This improves code maintainability and consistency across the SDK.

Consider importing MergedModels with the type keyword since it's only used as a type:

-import { deepMerge, MergedModels } from "../../internal/merge";
+import { deepMerge, type MergedModels } from "../../internal/merge";
🧰 Tools
🪛 Biome (1.9.4)

[error] 14-14: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

examples/example-node-worker/src/typescript/contracts.gen.ts (2)

1-2: Use import type for type-only imports

Importing types with import type ensures they're removed by the TypeScript compiler during transpilation and avoids loading unnecessary modules at runtime.

-import { DojoProvider, DojoCall } from "@dojoengine/core";
-import { Account, AccountInterface, BigNumberish, CairoOption, CairoCustomEnum, ByteArray } from "starknet";
+import type { DojoProvider, DojoCall } from "@dojoengine/core";
+import type { Account, AccountInterface, BigNumberish } from "starknet";
+import { CairoOption, CairoCustomEnum, ByteArray } from "starknet";
🧰 Tools
🪛 Biome (1.9.4)

[error] 1-1: All these imports are only used as types.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Use import type.

(lint/style/useImportType)


[error] 2-2: Some named imports are only used as types.

This import is only used as a type.

This import is only used as a type.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)


17-25: Extract hardcoded values into constants and reuse error handling logic

The code duplicates error handling and hardcodes values like "dojo_starter" which should be extracted for better maintainability.

+const WORLD_NAMESPACE = "dojo_starter";

+const handleExecuteError = (error: unknown) => {
+    console.error(error);
+    throw error;
+};

const actions_move = async (snAccount: Account | AccountInterface, direction: CairoCustomEnum) => {
    try {
        return await provider.execute(
            snAccount,
            build_actions_move_calldata(direction),
-            "dojo_starter",
+            WORLD_NAMESPACE,
        );
    } catch (error) {
-        console.error(error);
-        throw error;
+        return handleExecuteError(error);
    }
};

// Apply same pattern to actions_spawn function

Also applies to: 36-46

packages/sdk/src/internal/merge.ts (5)

9-46: Cairo options are properly handled, but type casting could be improved

The CairoOption merging logic is sound, but the repeated type casting with as unknown as MergedModels<T> suggests the types might be defined more precisely.

Consider using a more specific return type for the function rather than casting:

export function mergeCairoOption<T extends SchemaType, V>(
    target: CairoOption<V>,
    source: CairoOption<V>
-): MergedModels<T> {
+): CairoOption<V> {
    // If source is Some, prefer source's value
    if (source instanceof CairoOption && source.isSome()) {
        return new CairoOption(
            CairoOptionVariant.Some,
            source.unwrap()
-        ) as unknown as MergedModels<T>;
+        );
    }

    // If source is None or undefined, keep target
    if (target instanceof CairoOption) {
        if (target.isSome()) {
            return new CairoOption(
                CairoOptionVariant.Some,
                target.unwrap()
-            ) as unknown as MergedModels<T>;
+            );
        }
        return new CairoOption(
            CairoOptionVariant.None
-        ) as unknown as MergedModels<T>;
+        );
    }

    // This should not happen if both are CairoOption instances
-    return target as unknown as MergedModels<T>;
+    return target;
}

Then in deepMerge, you can cast the result as needed:

if (isCairoOption(target) && isCairoOption(source)) {
    return mergeCairoOption(target, source) as MergedModels<T>;
}

76-76: Specify a more precise type than any

Using any disables important type checks and can hide type errors.

Replace with a more specific type for enum content:

-        const newEnumContent: Record<string, any> = {};
+        const newEnumContent: Record<string, unknown> = {};
🧰 Tools
🪛 Biome (1.9.4)

[error] 76-76: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)


96-96: Specify a more precise type than any

Same issue with any in another location.

-        const newEnumContent: Record<string, any> = {};
+        const newEnumContent: Record<string, unknown> = {};
🧰 Tools
🪛 Biome (1.9.4)

[error] 96-96: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)


133-133: Specify a more precise type than any

Same issue with any in the deepMerge function.

-    const result = { ...target } as Record<string, any>;
+    const result = { ...target } as Record<string, unknown>;
🧰 Tools
🪛 Biome (1.9.4)

[error] 133-133: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)


123-160: Deep merge implementation doesn't handle array merging

The current implementation only recursively merges objects (non-arrays) and directly replaces arrays and primitives. Consider whether arrays should be merged (concatenated) or replaced.

For most Cairo type use cases, directly replacing arrays is likely the correct behavior, but it might be valuable to document this decision in a comment to avoid confusion for future developers. If array merging becomes necessary in the future, you could add a specific case for array handling.

🧰 Tools
🪛 Biome (1.9.4)

[error] 133-133: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

examples/example-node-worker/src/typescript/models.gen.ts (2)

3-3: Use import type for type-only imports

This import includes types used only for type declarations, which should use import type for better tree-shaking.

-import { CairoCustomEnum, CairoOption, CairoOptionVariant, BigNumberish } from 'starknet';
+import { CairoOption, CairoOptionVariant } from 'starknet';
+import type { CairoCustomEnum, BigNumberish } from 'starknet';
🧰 Tools
🪛 Biome (1.9.4)

[error] 3-3: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)


99-103: Improve Cairo enum initialization formatting

The initialization of Cairo custom enums has inconsistent formatting which makes it harder to read.

-			directions: [new CairoCustomEnum({ 
-					Left: "",
-				Right: undefined,
-				Up: undefined,
-				Down: undefined, })],
+			directions: [new CairoCustomEnum({ 
+				Left: "",
+				Right: undefined,
+				Up: undefined,
+				Down: undefined, 
+			})],

Consider applying this formatting improvement throughout the file for all enum initializations.

packages/sdk/src/web/index.ts (1)

48-48: Remove unnecessary await with constructor

The client creation has changed from an async factory function to a synchronous constructor, so the await is unnecessary.

-    return await new torii.ToriiClient(config);
+    return new torii.ToriiClient(config);
packages/sdk/src/internal/toriiQueryBuilder.ts (1)

28-28: Type casting in constructor could be improved

The constructor casts the default options to Query type, which might hide type errors.

Consider adding more type safety by ensuring the default options match the Query type structure:

-        this.query = { ...(defaultToriiOptions() as Query), ...options };
+        this.query = { ...defaultToriiOptions(), ...options } as Query;
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro (Legacy)

📥 Commits

Reviewing files that changed from the base of the PR and between 2cc16f7 and c7e6890.

⛔ Files ignored due to path filters (5)
  • examples/example-vite-react-phaser-recs/src/dojo/generated/setup.ts is excluded by !**/generated/**
  • examples/example-vite-react-pwa-recs/src/dojo/generated/setup.ts is excluded by !**/generated/**
  • examples/example-vite-react-threejs-recs/src/dojo/generated/setup.ts is excluded by !**/generated/**
  • examples/example-vue-app-recs/src/dojo/generated/setup.ts is excluded by !**/generated/**
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (41)
  • .changeset/thirty-cheetahs-design.md (1 hunks)
  • .github/workflows/ci.yaml (1 hunks)
  • examples/example-node-worker/dojoConfig.ts (1 hunks)
  • examples/example-node-worker/env.ts (1 hunks)
  • examples/example-node-worker/main.ts (1 hunks)
  • examples/example-node-worker/package.json (1 hunks)
  • examples/example-node-worker/src/typescript/contracts.gen.ts (1 hunks)
  • examples/example-node-worker/src/typescript/models.gen.ts (1 hunks)
  • examples/example-node-worker/tsconfig.json (1 hunks)
  • examples/example-nodejs-bot/package.json (1 hunks)
  • examples/example-vite-react-app-recs/src/dojo/setup.ts (2 hunks)
  • examples/example-vite-react-sdk/src/historical-events.tsx (2 hunks)
  • package.json (1 hunks)
  • packages/core/package.json (1 hunks)
  • packages/react/package.json (2 hunks)
  • packages/sdk/package.json (3 hunks)
  • packages/sdk/src/__tests__/parseEntities.test.ts (7 hunks)
  • packages/sdk/src/__tests__/parseHistoricalEvents.test.ts (0 hunks)
  • packages/sdk/src/__tests__/state.test.ts (3 hunks)
  • packages/sdk/src/__tests__/toriiQueryBuilder.test.ts (6 hunks)
  • packages/sdk/src/__tests__/zustand.perf.test.ts (1 hunks)
  • packages/sdk/src/internal/errors.ts (1 hunks)
  • packages/sdk/src/internal/generateTypedData.ts (2 hunks)
  • packages/sdk/src/internal/merge.ts (1 hunks)
  • packages/sdk/src/internal/models.ts (1 hunks)
  • packages/sdk/src/internal/parseEntities.ts (4 hunks)
  • packages/sdk/src/internal/parseHistoricalEvents.ts (0 hunks)
  • packages/sdk/src/internal/toriiQueryBuilder.ts (6 hunks)
  • packages/sdk/src/internal/types.ts (5 hunks)
  • packages/sdk/src/node/index.ts (12 hunks)
  • packages/sdk/src/node/worker.ts (1 hunks)
  • packages/sdk/src/web/experimental/index.ts (4 hunks)
  • packages/sdk/src/web/index.ts (11 hunks)
  • packages/sdk/src/web/react/hooks/entities.ts (1 hunks)
  • packages/sdk/src/web/react/hooks/events.ts (3 hunks)
  • packages/sdk/src/web/react/hooks/hooks.ts (4 hunks)
  • packages/sdk/src/web/state/zustand.ts (1 hunks)
  • packages/state/src/recs/index.ts (6 hunks)
  • packages/torii-client/package.json (2 hunks)
  • packages/torii-wasm/dojo.c (1 hunks)
  • turbo.json (1 hunks)
💤 Files with no reviewable changes (2)
  • packages/sdk/src/internal/parseHistoricalEvents.ts
  • packages/sdk/src/tests/parseHistoricalEvents.test.ts
✅ Files skipped from review due to trivial changes (2)
  • .github/workflows/ci.yaml
  • .changeset/thirty-cheetahs-design.md
🚧 Files skipped from review as they are similar to previous changes (23)
  • examples/example-nodejs-bot/package.json
  • packages/torii-client/package.json
  • packages/sdk/src/internal/errors.ts
  • packages/torii-wasm/dojo.c
  • package.json
  • packages/core/package.json
  • examples/example-node-worker/dojoConfig.ts
  • packages/sdk/package.json
  • examples/example-node-worker/tsconfig.json
  • examples/example-node-worker/env.ts
  • turbo.json
  • packages/sdk/src/tests/parseEntities.test.ts
  • examples/example-vite-react-app-recs/src/dojo/setup.ts
  • packages/react/package.json
  • packages/sdk/src/tests/toriiQueryBuilder.test.ts
  • packages/sdk/src/internal/generateTypedData.ts
  • packages/sdk/src/web/react/hooks/events.ts
  • examples/example-node-worker/package.json
  • packages/sdk/src/node/worker.ts
  • packages/sdk/src/web/react/hooks/hooks.ts
  • packages/sdk/src/web/react/hooks/entities.ts
  • packages/sdk/src/internal/models.ts
  • packages/sdk/src/internal/types.ts
🧰 Additional context used
🧬 Code Graph Analysis (9)
packages/sdk/src/internal/toriiQueryBuilder.ts (1)
packages/sdk/src/internal/types.ts (1)
  • SchemaType (50-70)
packages/sdk/src/web/index.ts (6)
packages/sdk/src/internal/parseEntities.ts (1)
  • parseEntities (10-59)
packages/sdk/src/internal/convertClauseToEntityKeysClause.ts (1)
  • intoEntityKeysClause (4-36)
packages/sdk/src/internal/types.ts (2)
  • ToriiResponse (248-253)
  • ToriiQueryBuilder (557-557)
packages/sdk/src/internal/generateTypedData.ts (1)
  • generateTypedData (13-48)
packages/sdk/src/internal/errors.ts (1)
  • NO_ACCOUNT (3-3)
packages/sdk/src/internal/toriiQueryBuilder.ts (1)
  • ToriiQueryBuilder (24-143)
packages/sdk/src/node/index.ts (6)
packages/sdk/src/internal/convertClauseToEntityKeysClause.ts (1)
  • intoEntityKeysClause (4-36)
packages/sdk/src/internal/parseEntities.ts (1)
  • parseEntities (10-59)
packages/sdk/src/internal/types.ts (2)
  • ToriiResponse (248-253)
  • ToriiQueryBuilder (557-557)
packages/sdk/src/internal/generateTypedData.ts (1)
  • generateTypedData (13-48)
packages/sdk/src/internal/errors.ts (2)
  • NO_SIGNER (1-1)
  • NO_IDENTITY (2-2)
packages/sdk/src/internal/toriiQueryBuilder.ts (1)
  • ToriiQueryBuilder (24-143)
examples/example-node-worker/src/typescript/contracts.gen.ts (2)
examples/example-node-worker/src/typescript/models.gen.ts (1)
  • direction (71-76)
packages/core/src/types/index.ts (1)
  • DojoCall (130-134)
examples/example-node-worker/src/typescript/models.gen.ts (1)
packages/sdk/src/internal/types.ts (1)
  • SchemaType (50-70)
examples/example-vite-react-sdk/src/historical-events.tsx (1)
packages/sdk/src/internal/toriiQueryBuilder.ts (1)
  • HistoricalToriiQueryBuilder (149-159)
packages/sdk/src/internal/merge.ts (3)
packages/sdk/src/web/queryBuilder.ts (1)
  • value (191-193)
examples/example-node-worker/src/typescript/models.gen.ts (1)
  • SchemaType (80-94)
packages/sdk/src/internal/types.ts (2)
  • SchemaType (50-70)
  • ParsedEntity (228-237)
packages/sdk/src/web/experimental/index.ts (3)
packages/sdk/src/internal/parseEntities.ts (1)
  • parseEntities (10-59)
packages/core/src/provider/DojoProvider.ts (1)
  • entities (104-123)
packages/sdk/src/internal/convertClauseToEntityKeysClause.ts (1)
  • intoEntityKeysClause (4-36)
packages/state/src/recs/index.ts (1)
packages/core/src/provider/DojoProvider.ts (1)
  • entities (104-123)
🪛 Biome (1.9.4)
examples/example-node-worker/main.ts

[error] 176-176: This code is unreachable

... because this statement will return from the function beforehand

(lint/correctness/noUnreachable)

packages/sdk/src/internal/toriiQueryBuilder.ts

[error] 133-133: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

examples/example-node-worker/src/typescript/contracts.gen.ts

[error] 1-1: All these imports are only used as types.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Use import type.

(lint/style/useImportType)


[error] 2-2: Some named imports are only used as types.

This import is only used as a type.

This import is only used as a type.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

examples/example-node-worker/src/typescript/models.gen.ts

[error] 3-3: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

examples/example-vite-react-sdk/src/historical-events.tsx

[error] 1-5: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

packages/sdk/src/__tests__/zustand.perf.test.ts

[error] 131-133: Forbidden non-null assertion.

(lint/style/noNonNullAssertion)


[error] 131-133: Forbidden non-null assertion.

Unsafe fix: Replace with optional chain operator ?. This operator includes runtime checks, so it is safer than the compile-only non-null assertion operator

(lint/style/noNonNullAssertion)


[error] 132-132: The computed expression can be simplified without the use of a string literal.

Unsafe fix: Use a literal key instead.

(lint/complexity/useLiteralKeys)

packages/sdk/src/internal/merge.ts

[error] 76-76: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)


[error] 96-96: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)


[error] 133-133: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

packages/sdk/src/internal/parseEntities.ts

[error] 50-50: Do not use template literals if interpolation and special-character handling are not needed.

Unsafe fix: Replace with string literal

(lint/style/noUnusedTemplateLiteral)

packages/sdk/src/web/experimental/index.ts

[error] 30-30: This type annotation is trivially inferred from its initialization.

Safe fix: Remove the type annotation.

(lint/style/noInferrableTypes)


[error] 101-101: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

packages/sdk/src/web/state/zustand.ts

[error] 14-14: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

packages/state/src/recs/index.ts

[error] 185-185: Do not use template literals if interpolation and special-character handling are not needed.

Unsafe fix: Replace with string literal

(lint/style/noUnusedTemplateLiteral)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build
🔇 Additional comments (25)
examples/example-vite-react-sdk/src/historical-events.tsx (2)

1-5: Updated imports to use the new HistoricalToriiQueryBuilder

The change correctly imports the specialized HistoricalToriiQueryBuilder class instead of the generic ToriiQueryBuilder. This aligns with the SDK's updated design for handling historical queries.

🧰 Tools
🪛 Biome (1.9.4)

[error] 1-5: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)


14-14: Correctly updated the component to use HistoricalToriiQueryBuilder

This change implements the new specialized query builder which has the historical flag set to true by default, making the code more semantically clear about its purpose.

packages/sdk/src/__tests__/state.test.ts (2)

108-111: Updated entity access syntax from bracket to dot notation

The change from bracket notation to dot notation for entity access is consistent with the broader SDK refactoring that unifies entity data handling formats. This improves code readability while maintaining the same functionality.


126-126: Consistent use of dot notation for entity access

This change follows the same pattern of using dot notation instead of bracket notation, maintaining consistency throughout the file.

packages/sdk/src/internal/toriiQueryBuilder.ts (10)

1-6: Updated imports reflect the pagination structure change

The imports now include PaginationDirection, which is part of the transition to cursor-based pagination.


10-15: Replaced flat pagination fields with nested pagination structure

The query options now use a nested pagination object with limit, cursor, direction, and order_by fields, which is a breaking change from the previous flat structure.

This change will impact any code that directly accesses the query structure. Ensure you've updated all places that interact with these properties.


17-18: Renamed query properties for clarity

Renamed dont_include_hashed_keys to no_hashed_keys and entity_models to models for more consistent and clearer naming.


47-62: Added cursor-based pagination methods

Added new methods withCursor and withDirection to support cursor-based pagination, which is a good addition that aligns with modern API pagination patterns.


78-79: Updated property name in includeHashedKeys method

The method now sets no_hashed_keys to false instead of the previous dont_include_hashed_keys, consistent with the property rename.


90-94: Updated addOrderBy to use nested pagination structure

The method now correctly updates pagination.order_by instead of the previous top-level property.


102-103: Updated withOrderBy to use nested pagination structure

The method now correctly sets pagination.order_by instead of the previous top-level property.


110-111: Updated addEntityModel to use renamed property

The method now correctly updates models instead of the previous entity_models.


118-119: Updated withEntityModels to use renamed property

The method now correctly sets models instead of the previous entity_models.


145-158: Added HistoricalToriiQueryBuilder for historical queries

Adding a specialized subclass for historical queries is a clean design approach that makes the intention clear.

packages/sdk/src/node/index.ts (11)

29-30: Added neverthrow for improved error handling

The addition of the neverthrow library and error constants improves error handling throughout the SDK.


34-36: Added new exports for additional functionality

Exporting ./worker.ts, ../internal/types.ts, and ../internal/models.ts provides access to new functionality and types.


64-65: Updated property name for consistency

Changed dont_include_hashed_keys to no_hashed_keys to match the property rename in the query builder.


119-120: Updated property name in event query subscription

Changed dont_include_hashed_keys to no_hashed_keys in the event query subscription check.


127-130: Updated event message retrieval and parsing

Simplified event message retrieval and parsing by directly accessing the .items array.


138-143: Updated event parsing in callback

Changed event parsing in the callback to handle arrays instead of keyed objects.


189-189: Simplified entity retrieval

Simplified entity retrieval by directly accessing the .items property and parsing the entities.


202-204: Simplified event retrieval

Simplified event retrieval and parsing to directly work with the .items array.


240-249: Improved error handling in sendMessage

The sendMessage method now:

  1. Checks for the presence of signer and identity before attempting to send
  2. Returns a Result type (from neverthrow) instead of throwing exceptions
  3. No longer requires an explicit Account parameter

This is a good improvement for error handling, but represents a breaking change.


390-391: Updated entity retrieval in toriiQueryIntoHashedKeys

Updated to directly access the .items property from the result of client.getEntities().


401-407: Updated event message retrieval in toriiEventMessagesQueryIntoHashedKeys

Updated to directly access the .items property from the result of client.getEventMessages() and parse the events.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🔭 Outside diff range comments (2)
packages/state/src/recs/index.ts (2)

287-333: ⚠️ Potential issue

Infinite loop risk – fetch happens only once

fetchedEntities is retrieved before the while loop and never refreshed, so:

  • The loop repeats forever when fetchedEntities.items.length ≥ limit.
  • Or it exits immediately if the first batch is < limit, ignoring pagination entirely.

Move the fetch inside the loop:

-    const fetchedEntities = await client.getEntities({ ... });
-    while (continueFetching) {
+    while (continueFetching) {
+        const fetchedEntities = await client.getEntities({
+            pagination: { limit, cursor, direction: "Forward", order_by: orderBy },
+            clause: clause || undefined,
+            no_hashed_keys: false,
+            models: entityModels,
+            historical,
+        });

Then apply the same array-handling fixes as above.


540-554: 🛠️ Refactor suggestion

insertEntitiesInDB iterates array as if it were an object

for (const [entityId, data] of Object.entries(entities)) gives numeric indices ("0", "1").
Instead iterate the array directly:

-        for (const [entityId, data] of Object.entries(entities)) {
-            const entityData = { id: entityId, ...data };
+        for (const entity of entities) {
+            const entityData = {
+                id: entity.hashed_keys.join("."), // or whatever unique id you use
+                ...entity.models,
+            };

Otherwise you’ll store meaningless IDs and lose data fidelity.

♻️ Duplicate comments (13)
packages/sdk/src/web/experimental/index.ts (1)

101-106: Replace any type with proper Entity type.

For improved type safety, replace the any type with the correct type from the torii library.

🧰 Tools
🪛 Biome (1.9.4)

[error] 101-101: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

packages/sdk/src/internal/parseEntities.ts (1)

126-133: u64 can overflow JS number; parse it as BigInt.

A 64-bit unsigned integer can exceed Number.MAX_SAFE_INTEGER (2⁵³-1), leading to precision loss if parsed with Number.parseInt.

examples/example-node-worker/main.ts (4)

21-25: Wrong global identifier: use WebSocket, not Websocket.

Browsers (and most polyfills) expose global.WebSocket (uppercase S). Assigning to Websocket leaves libraries that expect WebSocket undefined.


125-132: initPositionFromEvent mutates the shared constant.

pc is an alias of defaultPositionCount, so every call mutates the constant and all future callers see cumulative side-effects.


143-143: Potential data loss with data.pop().

data is already the argument supplied by the subscription. Calling pop() both mutates the array and drops all but the last entity; if multiple entities arrive in the same batch they are silently ignored.


175-176: Unreachable code after the first return.

The second return [sub]; can never execute—Biome rightfully flags it.

🧰 Tools
🪛 Biome (1.9.4)

[error] 176-176: This code is unreachable

... because this statement will return from the function beforehand

(lint/correctness/noUnreachable)

packages/state/src/recs/index.ts (1)

226-246: Same array/object issue in getEvents

The event path passes an array to setEntities and performs an object-length check. Apply the same normalisation or refactor as described for getEntities.

examples/example-node-worker/src/typescript/models.gen.ts (1)

38-46: Typo: posititionposition

The field name is misspelled in both PositionCount and PositionCountValue.
This propagates to generated schema and will break consumers relying on the correct field.

-    positition: Array<[BigNumberish, BigNumberish]>;
+    position: Array<[BigNumberish, BigNumberish]>;

Remember to regenerate any related code after correcting the source.

packages/sdk/src/internal/toriiQueryBuilder.ts (1)

133-142: ⚠️ Potential issue

Updated static withPagination method for cursor-based pagination

The method signature has changed to accept cursor, limit, and direction parameters instead of page-based parameters, reflecting the new pagination approach.

However, there's a type issue flagged by the static analyzer:

[error] 133-133: Unexpected any. Specify a different type.

Consider specifying a more specific type for the generic parameter:

-    static withPagination<T extends Record<string, Record<string, any>>>(
+    static withPagination<T extends SchemaType>(
🧰 Tools
🪛 Biome (1.9.4)

[error] 133-133: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

packages/sdk/src/node/index.ts (4)

50-50: ⚠️ Potential issue

Client initialization change

Changed from torii.createClient to directly instantiating new torii.ToriiClient. This is a breaking change for anyone directly using the returned client.

Ensure this change is documented in migration guides and potentially consider providing a compatibility layer if feasible.


72-84: ⚠️ Potential issue

Updated entity parsing to use items array

The entity parsing has been updated to handle arrays from the .items property rather than keyed objects. The parsing function also now processes an array of entities from the query response rather than directly accessing properties.

This change simplifies the data structure but is a breaking change for existing code.


215-230: ⚠️ Potential issue

Modified generateTypedData signature

The method signature has changed:

  • Removed optional domain parameter
  • Added optional additionalTypes parameter
  • Domain is now always taken from options.domain

This is a breaking change for code that explicitly passed a domain parameter different from options.domain.

Ensure this change is documented in migration guides and consider if there are cases where passing a different domain was useful functionality.


240-265: ⚠️ Potential issue

Improved error handling in sendMessage method

The sendMessage method now:

  1. Checks for the presence of signer and identity before attempting to send
  2. Returns a Result type (from neverthrow) with ok/err instead of throwing exceptions
  3. No longer requires an explicit Account parameter, relying on options.signer and options.identity

This is a breaking change for code that catches exceptions from this method, but improves error handling overall.

However, there's inconsistency in the error handling approach - the method returns Result for configuration errors but still throws in the catch block. Consider making the error handling consistent:

    try {
        const td = new torii.TypedData(JSON.stringify(data)).encode(
            options.identity
        );

        const sig = options.signer.sign(td);

        const dataString = JSON.stringify(data);

        return ok(
            await client.publishMessage(dataString, [
                sig.r.toString(),
                sig.s.toString(),
            ])
        );
    } catch (error) {
        console.error("Failed to send message:", error);
-        throw error;
+        return err(`Failed to send message: ${error instanceof Error ? error.message : String(error)}`);
    }
🧹 Nitpick comments (13)
packages/sdk/src/internal/merge.ts (4)

19-46: Review the excessive type assertions in mergeCairoOption

The function correctly handles merging of CairoOption instances with proper preference for source values when present. However, there are multiple type assertions to unknown and then to MergedModels<T> which could be simplified.

Consider refactoring to reduce the number of type assertions and make the types flow more naturally:

export function mergeCairoOption<T extends SchemaType>(
    target: MergedModels<T>,
    source: Partial<MergedModels<T>>
): MergedModels<T> {
    // If source is Some, prefer source's value
    if (source instanceof CairoOption && source.isSome()) {
-        return new CairoOption(
-            CairoOptionVariant.Some,
-            source.unwrap()
-        ) as unknown as MergedModels<T>;
+        return new CairoOption(
+            CairoOptionVariant.Some,
+            source.unwrap()
+        ) as MergedModels<T>;
    }

    // If source is None or undefined, keep target
    if (target instanceof CairoOption) {
        if (target.isSome()) {
-            return new CairoOption(
-                CairoOptionVariant.Some,
-                target.unwrap()
-            ) as unknown as MergedModels<T>;
+            return new CairoOption(
+                CairoOptionVariant.Some,
+                target.unwrap()
+            ) as MergedModels<T>;
        }
-        return new CairoOption(
-            CairoOptionVariant.None
-        ) as unknown as MergedModels<T>;
+        return new CairoOption(
+            CairoOptionVariant.None
+        ) as MergedModels<T>;
    }

    // This should not happen if both are CairoOption instances
    return target as unknown as MergedModels<T>;
}

76-76: Replace any with a more specific type

The use of any disables type checking for this variable. Consider using a more specific type.

- const newEnumContent: Record<string, any> = {};
+ const newEnumContent: Record<string, unknown> = {};
🧰 Tools
🪛 Biome (1.9.4)

[error] 76-76: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)


96-96: Replace any with a more specific type

Similar to the previous comment, the use of any should be avoided.

- const newEnumContent: Record<string, any> = {};
+ const newEnumContent: Record<string, unknown> = {};
🧰 Tools
🪛 Biome (1.9.4)

[error] 96-96: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)


123-133: Replace any with a more specific type in deepMerge

The deepMerge function correctly handles various cases but uses any in its implementation.

- const result = { ...target } as Record<string, any>;
+ const result = { ...target } as Record<string, unknown>;
🧰 Tools
🪛 Biome (1.9.4)

[error] 133-133: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

packages/sdk/src/web/state/zustand.ts (1)

14-14: Use import type for type-only imports

The MergedModels is only used as a type in this file, so it should be imported with import type to ensure it is removed by compilers and avoid loading unnecessary modules.

- import { deepMerge, MergedModels } from "../../internal/merge";
+ import { deepMerge } from "../../internal/merge";
+ import type { MergedModels } from "../../internal/merge";
🧰 Tools
🪛 Biome (1.9.4)

[error] 14-14: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

examples/example-vite-react-sdk/src/historical-events.tsx (2)

1-5: Use import type for type-only imports

ParsedEntity is only used as a type in this file. Consider using import type for better performance and to ensure they are removed by compilers.

import {
    KeysClause,
-   ParsedEntity,
    HistoricalToriiQueryBuilder,
} from "@dojoengine/sdk";
+import type { ParsedEntity } from "@dojoengine/sdk";
🧰 Tools
🪛 Biome (1.9.4)

[error] 1-5: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)


33-35: Remove unnecessary @ts-ignore comment

The code uses a @ts-ignore comment but doesn't appear to have any TypeScript errors. Consider removing this comment if it's no longer needed.

-            {/* @ts-ignore */}
            {events.map((e: ParsedEntity<SchemaType>, key) => {
                return <Event event={e} key={key} />;
            })}
🧰 Tools
🪛 Biome (1.9.4)

[error] 34-34: Avoid using the index of an array as key property in an element.

This is the source of the key value.

The order of the items may change, and this also affects performances and component state.
Check the React documentation.

(lint/suspicious/noArrayIndexKey)

packages/sdk/src/__tests__/zustand.perf.test.ts (1)

131-133: Avoid non-null assertions and use more readable code style

The code has been reformatted to use bracket notation on a separate line, but still uses non-null assertions which are discouraged. Consider using optional chaining for a safer approach.

-                       draft.entities[
-                           "entity500"
-                       ].models.world!.item!.durability = 75;
+                       draft.entities.entity500?.models.world?.item?.durability = 75;

Or if you're certain these properties exist:

-                       draft.entities[
-                           "entity500"
-                       ].models.world!.item!.durability = 75;
+                       const entity = draft.entities.entity500;
+                       if (entity?.models.world?.item) {
+                           entity.models.world.item.durability = 75;
+                       }
🧰 Tools
🪛 Biome (1.9.4)

[error] 131-133: Forbidden non-null assertion.

(lint/style/noNonNullAssertion)


[error] 131-133: Forbidden non-null assertion.

Unsafe fix: Replace with optional chain operator ?. This operator includes runtime checks, so it is safer than the compile-only non-null assertion operator

(lint/style/noNonNullAssertion)


[error] 132-132: The computed expression can be simplified without the use of a string literal.

Unsafe fix: Use a literal key instead.

(lint/complexity/useLiteralKeys)

examples/example-node-worker/src/typescript/contracts.gen.ts (2)

1-3: Use import type for type-only imports.

Since these imports are only used as types, using import type would ensure they're removed by the compiler and avoid loading unnecessary modules.

-import { DojoProvider, DojoCall } from "@dojoengine/core";
-import { Account, AccountInterface, BigNumberish, CairoOption, CairoCustomEnum, ByteArray } from "starknet";
+import type { DojoProvider, DojoCall } from "@dojoengine/core";
+import type { Account, AccountInterface, BigNumberish, CairoOption, CairoCustomEnum, ByteArray } from "starknet";
 import * as models from "./models.gen";
🧰 Tools
🪛 Biome (1.9.4)

[error] 1-1: All these imports are only used as types.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Use import type.

(lint/style/useImportType)


[error] 2-2: Some named imports are only used as types.

This import is only used as a type.

This import is only used as a type.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)


23-25: Consider enhancing error handling with structured errors.

The current implementation logs and rethrows raw errors. Consider enhancing the error handling to provide more context-specific information about the failure.

 		} catch (error) {
-			console.error(error);
-			throw error;
+			console.error(`Failed to execute 'move' action:`, error);
+			throw new Error(`Actions.move failed: ${error.message || String(error)}`);
 		}

Also applies to: 44-46

packages/state/src/recs/index.ts (1)

185-193: Length check & loop control use array as object

Object.keys(entities.items).length works but is O(n).
More importantly, once entities.items.length < limit is true we never set cursor = entities.next_cursor, so we might drop the final page when items.length === limit. Prefer:

-        if (Object.keys(entities.items).length < limit) {
+        if (entities.items.length < limit) {
             continueFetching = false;
         } else {
             cursor = entities.next_cursor;
         }

Also adjust the logging line to avoid unnecessary template literal:

-        if (logging) console.log(`Fetched entities`, entities.items);
+        if (logging) console.log("Fetched entities", entities.items);
🧰 Tools
🪛 Biome (1.9.4)

[error] 185-185: Do not use template literals if interpolation and special-character handling are not needed.

Unsafe fix: Replace with string literal

(lint/style/noUnusedTemplateLiteral)

packages/sdk/src/web/index.ts (1)

48-49: Redundant await on constructor

new torii.ToriiClient(config) is synchronous; awaiting it only wraps the object in a resolved promise.

-    return await new torii.ToriiClient(config);
+    return new torii.ToriiClient(config);
examples/example-node-worker/src/typescript/models.gen.ts (1)

3-3: Import unused at runtime – mark BigNumberish as type-only

BigNumberish is used purely as a type, but importing it as a value keeps it in the emitted JS bundle.

-import { CairoCustomEnum, CairoOption, CairoOptionVariant, BigNumberish } from 'starknet';
+import { CairoCustomEnum, CairoOption, CairoOptionVariant } from 'starknet';
+import type { BigNumberish } from 'starknet';
🧰 Tools
🪛 Biome (1.9.4)

[error] 3-3: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro (Legacy)

📥 Commits

Reviewing files that changed from the base of the PR and between c7e6890 and 4d32324.

⛔ Files ignored due to path filters (5)
  • examples/example-vite-react-phaser-recs/src/dojo/generated/setup.ts is excluded by !**/generated/**
  • examples/example-vite-react-pwa-recs/src/dojo/generated/setup.ts is excluded by !**/generated/**
  • examples/example-vite-react-threejs-recs/src/dojo/generated/setup.ts is excluded by !**/generated/**
  • examples/example-vue-app-recs/src/dojo/generated/setup.ts is excluded by !**/generated/**
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (42)
  • .changeset/thirty-cheetahs-design.md (1 hunks)
  • .github/workflows/ci.yaml (1 hunks)
  • .github/workflows/release.yaml (6 hunks)
  • examples/example-node-worker/dojoConfig.ts (1 hunks)
  • examples/example-node-worker/env.ts (1 hunks)
  • examples/example-node-worker/main.ts (1 hunks)
  • examples/example-node-worker/package.json (1 hunks)
  • examples/example-node-worker/src/typescript/contracts.gen.ts (1 hunks)
  • examples/example-node-worker/src/typescript/models.gen.ts (1 hunks)
  • examples/example-node-worker/tsconfig.json (1 hunks)
  • examples/example-nodejs-bot/package.json (1 hunks)
  • examples/example-vite-react-app-recs/src/dojo/setup.ts (2 hunks)
  • examples/example-vite-react-sdk/src/historical-events.tsx (2 hunks)
  • package.json (1 hunks)
  • packages/core/package.json (1 hunks)
  • packages/react/package.json (2 hunks)
  • packages/sdk/package.json (3 hunks)
  • packages/sdk/src/__tests__/parseEntities.test.ts (7 hunks)
  • packages/sdk/src/__tests__/parseHistoricalEvents.test.ts (0 hunks)
  • packages/sdk/src/__tests__/state.test.ts (3 hunks)
  • packages/sdk/src/__tests__/toriiQueryBuilder.test.ts (6 hunks)
  • packages/sdk/src/__tests__/zustand.perf.test.ts (1 hunks)
  • packages/sdk/src/internal/errors.ts (1 hunks)
  • packages/sdk/src/internal/generateTypedData.ts (2 hunks)
  • packages/sdk/src/internal/merge.ts (1 hunks)
  • packages/sdk/src/internal/models.ts (1 hunks)
  • packages/sdk/src/internal/parseEntities.ts (4 hunks)
  • packages/sdk/src/internal/parseHistoricalEvents.ts (0 hunks)
  • packages/sdk/src/internal/toriiQueryBuilder.ts (6 hunks)
  • packages/sdk/src/internal/types.ts (5 hunks)
  • packages/sdk/src/node/index.ts (12 hunks)
  • packages/sdk/src/node/worker.ts (1 hunks)
  • packages/sdk/src/web/experimental/index.ts (4 hunks)
  • packages/sdk/src/web/index.ts (11 hunks)
  • packages/sdk/src/web/react/hooks/entities.ts (1 hunks)
  • packages/sdk/src/web/react/hooks/events.ts (3 hunks)
  • packages/sdk/src/web/react/hooks/hooks.ts (4 hunks)
  • packages/sdk/src/web/state/zustand.ts (1 hunks)
  • packages/state/src/recs/index.ts (6 hunks)
  • packages/torii-client/package.json (2 hunks)
  • packages/torii-wasm/dojo.c (1 hunks)
  • turbo.json (1 hunks)
💤 Files with no reviewable changes (2)
  • packages/sdk/src/tests/parseHistoricalEvents.test.ts
  • packages/sdk/src/internal/parseHistoricalEvents.ts
✅ Files skipped from review due to trivial changes (4)
  • packages/sdk/src/tests/state.test.ts
  • .github/workflows/ci.yaml
  • .changeset/thirty-cheetahs-design.md
  • .github/workflows/release.yaml
🚧 Files skipped from review as they are similar to previous changes (23)
  • examples/example-nodejs-bot/package.json
  • packages/torii-wasm/dojo.c
  • package.json
  • packages/torii-client/package.json
  • examples/example-vite-react-app-recs/src/dojo/setup.ts
  • packages/react/package.json
  • packages/sdk/src/internal/errors.ts
  • examples/example-node-worker/env.ts
  • turbo.json
  • packages/sdk/package.json
  • examples/example-node-worker/tsconfig.json
  • packages/sdk/src/web/react/hooks/events.ts
  • examples/example-node-worker/package.json
  • packages/sdk/src/tests/parseEntities.test.ts
  • examples/example-node-worker/dojoConfig.ts
  • packages/core/package.json
  • packages/sdk/src/tests/toriiQueryBuilder.test.ts
  • packages/sdk/src/node/worker.ts
  • packages/sdk/src/internal/generateTypedData.ts
  • packages/sdk/src/web/react/hooks/hooks.ts
  • packages/sdk/src/web/react/hooks/entities.ts
  • packages/sdk/src/internal/types.ts
  • packages/sdk/src/internal/models.ts
🧰 Additional context used
🧬 Code Graph Analysis (9)
packages/sdk/src/web/index.ts (5)
packages/sdk/src/internal/parseEntities.ts (1)
  • parseEntities (10-59)
packages/sdk/src/internal/convertClauseToEntityKeysClause.ts (1)
  • intoEntityKeysClause (4-36)
packages/sdk/src/internal/generateTypedData.ts (1)
  • generateTypedData (13-48)
packages/sdk/src/internal/errors.ts (1)
  • NO_ACCOUNT (3-3)
packages/sdk/src/internal/toriiQueryBuilder.ts (1)
  • ToriiQueryBuilder (24-143)
examples/example-node-worker/main.ts (11)
packages/sdk/src/web/experimental/index.ts (1)
  • init (18-124)
packages/sdk/src/node/index.ts (1)
  • init (43-417)
packages/sdk/src/web/index.ts (1)
  • init (62-427)
examples/example-node-worker/dojoConfig.ts (1)
  • dojoConfig (7-9)
examples/example-node-worker/env.ts (1)
  • env (9-9)
examples/example-node-worker/src/typescript/models.gen.ts (3)
  • PositionCount (38-41)
  • Moves (17-22)
  • SchemaType (80-94)
packages/sdk/src/internal/toriiQueryBuilder.ts (2)
  • ToriiQueryBuilder (24-143)
  • HistoricalToriiQueryBuilder (149-159)
packages/sdk/src/internal/types.ts (3)
  • ToriiQueryBuilder (557-557)
  • ParsedEntity (228-237)
  • SchemaType (50-70)
packages/sdk/src/node/clauseBuilder.ts (1)
  • KeysClause (46-52)
packages/sdk/src/internal/models.ts (1)
  • getModel (38-52)
packages/sdk/src/node/worker.ts (1)
  • createWorker (25-44)
packages/sdk/src/internal/toriiQueryBuilder.ts (1)
packages/sdk/src/internal/types.ts (2)
  • ToriiQueryBuilder (557-557)
  • SchemaType (50-70)
packages/sdk/src/node/index.ts (6)
packages/sdk/src/internal/convertClauseToEntityKeysClause.ts (1)
  • intoEntityKeysClause (4-36)
packages/sdk/src/internal/parseEntities.ts (1)
  • parseEntities (10-59)
packages/sdk/src/internal/types.ts (2)
  • ToriiResponse (248-253)
  • ToriiQueryBuilder (557-557)
packages/sdk/src/internal/generateTypedData.ts (1)
  • generateTypedData (13-48)
packages/sdk/src/internal/errors.ts (2)
  • NO_SIGNER (1-1)
  • NO_IDENTITY (2-2)
packages/sdk/src/internal/toriiQueryBuilder.ts (1)
  • ToriiQueryBuilder (24-143)
examples/example-node-worker/src/typescript/contracts.gen.ts (2)
examples/example-node-worker/src/typescript/models.gen.ts (1)
  • direction (71-76)
packages/core/src/types/index.ts (1)
  • DojoCall (130-134)
examples/example-vite-react-sdk/src/historical-events.tsx (1)
packages/sdk/src/internal/toriiQueryBuilder.ts (1)
  • HistoricalToriiQueryBuilder (149-159)
packages/sdk/src/internal/merge.ts (3)
packages/sdk/src/web/queryBuilder.ts (1)
  • value (191-193)
examples/example-node-worker/src/typescript/models.gen.ts (1)
  • SchemaType (80-94)
packages/sdk/src/internal/types.ts (2)
  • SchemaType (50-70)
  • ParsedEntity (228-237)
packages/sdk/src/web/experimental/index.ts (3)
packages/sdk/src/internal/parseEntities.ts (1)
  • parseEntities (10-59)
packages/core/src/provider/DojoProvider.ts (1)
  • entities (104-123)
packages/sdk/src/internal/convertClauseToEntityKeysClause.ts (1)
  • intoEntityKeysClause (4-36)
packages/state/src/recs/index.ts (1)
packages/core/src/provider/DojoProvider.ts (1)
  • entities (104-123)
🪛 Biome (1.9.4)
examples/example-node-worker/main.ts

[error] 176-176: This code is unreachable

... because this statement will return from the function beforehand

(lint/correctness/noUnreachable)

packages/sdk/src/internal/toriiQueryBuilder.ts

[error] 133-133: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

examples/example-node-worker/src/typescript/contracts.gen.ts

[error] 1-1: All these imports are only used as types.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Use import type.

(lint/style/useImportType)


[error] 2-2: Some named imports are only used as types.

This import is only used as a type.

This import is only used as a type.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

examples/example-node-worker/src/typescript/models.gen.ts

[error] 3-3: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

examples/example-vite-react-sdk/src/historical-events.tsx

[error] 1-5: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

packages/sdk/src/__tests__/zustand.perf.test.ts

[error] 131-133: Forbidden non-null assertion.

(lint/style/noNonNullAssertion)


[error] 131-133: Forbidden non-null assertion.

Unsafe fix: Replace with optional chain operator ?. This operator includes runtime checks, so it is safer than the compile-only non-null assertion operator

(lint/style/noNonNullAssertion)


[error] 132-132: The computed expression can be simplified without the use of a string literal.

Unsafe fix: Use a literal key instead.

(lint/complexity/useLiteralKeys)

packages/sdk/src/internal/merge.ts

[error] 76-76: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)


[error] 96-96: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)


[error] 133-133: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

packages/sdk/src/internal/parseEntities.ts

[error] 50-50: Do not use template literals if interpolation and special-character handling are not needed.

Unsafe fix: Replace with string literal

(lint/style/noUnusedTemplateLiteral)

packages/sdk/src/web/experimental/index.ts

[error] 30-30: This type annotation is trivially inferred from its initialization.

Safe fix: Remove the type annotation.

(lint/style/noInferrableTypes)


[error] 101-101: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

packages/sdk/src/web/state/zustand.ts

[error] 14-14: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

packages/state/src/recs/index.ts

[error] 185-185: Do not use template literals if interpolation and special-character handling are not needed.

Unsafe fix: Replace with string literal

(lint/style/noUnusedTemplateLiteral)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build
🔇 Additional comments (39)
packages/sdk/src/internal/merge.ts (3)

1-11: Cairo type utilities imported correctly

The module properly imports the necessary Cairo types from the starknet package and provides a well-documented type guard for CairoOption.


120-121: Type alias is well defined

The MergedModels type correctly extracts the union of model types from a parsed schema entity, enabling type-safe merging operations.


134-160: The deepMerge function handles complex nested structures correctly

The function correctly implements deep merging with special handling for Cairo types. It properly recurses into nested objects and preserves arrays and primitives. The implementation correctly delegates to specialized merge functions for Cairo types.

packages/sdk/src/web/state/zustand.ts (2)

88-94: Good reuse of the centralized deepMerge utility

The implementation correctly uses the imported deepMerge function to handle merging of Cairo types and nested structures. This ensures consistent merging behavior across the codebase.


138-144: Good consistency in using the imported deepMerge function

The implementation correctly uses the same imported deepMerge function in the updateEntity method as well, ensuring consistent behavior across different operations.

examples/example-vite-react-sdk/src/historical-events.tsx (1)

14-14: Properly switched to specialized HistoricalToriiQueryBuilder

The change to use HistoricalToriiQueryBuilder instead of ToriiQueryBuilder is correct and aligns with the SDK's approach for handling historical queries. The HistoricalToriiQueryBuilder class is specifically designed for historical data queries with the historical flag set to true by default.

packages/sdk/src/web/experimental/index.ts (4)

24-24: Client instantiation now uses constructor instead of async factory method.

This is a significant change from awaiting torii.createClient(clientConfig) to synchronously instantiating with new torii.ToriiClient(clientConfig). This aligns with the unified approach across the SDK.


28-29: Data extraction from paginated responses.

The code now correctly extracts the .items array from API responses before passing to parsing functions, consistent with the SDK's transition to cursor-based pagination.

Also applies to: 31-33


39-40: Query parameter renamed for clarity.

Parameter renamed from dont_include_hashed_keys to no_hashed_keys, making the API more concise while maintaining the same functionality.

Also applies to: 82-83


54-60: Entity update subscription now processes arrays instead of keyed objects.

The callback has been updated to parse an array containing the single updated entity rather than an object keyed by entity ID. This aligns with the unified data parsing approach across the SDK.

examples/example-node-worker/src/typescript/contracts.gen.ts (1)

5-59: Well-structured contract interaction layer.

The contract interaction module provides a clean separation of concerns with:

  1. Calldata builders that structure the parameters
  2. Execution functions that handle errors consistently
  3. A well-organized export interface

This approach makes the contract interactions more maintainable and testable.

packages/sdk/src/internal/parseEntities.ts (2)

10-20: Function refactored to work with arrays instead of keyed objects.

The parseEntities function now accepts an array of torii.Entity objects instead of a keyed object, aligning with the SDK's transition to array-based entity handling throughout the codebase.


90-93: Support for tuple type added.

The parseValue function now correctly handles the tuple type, ensuring compatibility with Cairo tuple values.

examples/example-node-worker/main.ts (3)

36-42: Improved SDK initialization with domain and identity.

The SDK initialization now includes more comprehensive domain metadata and properly configured identity and signer from environment variables, enhancing security and message verification.


79-118: Well-structured offchain position count publishing.

The function correctly:

  1. Queries for existing position count data
  2. Generates typed data with proper typing information
  3. Handles message sending with proper error handling

This implementation demonstrates good practices for offchain data publication.


153-165: Effective use of HistoricalToriiQueryBuilder.

The code properly utilizes the new HistoricalToriiQueryBuilder to query both Moved and Moves models, including proper clause building and hashed key inclusion.

packages/sdk/src/web/index.ts (1)

83-90: Subscription safeguard mis-fires when no_hashed_keys is false

The guard currently triggers when q.no_hashed_keys is truthy, but we want to enforce the presence of explicit keys only when hashed keys are not included (no_hashed_keys === true). Double-check the logic; a simple inversion might suffice.

packages/sdk/src/internal/toriiQueryBuilder.ts (12)

1-6: Import updates for new pagination approach

The imports now include the PaginationDirection type from @dojoengine/torii-wasm/types, which is needed for the new cursor-based pagination implementation.


28-28: Improved type safety in constructor

The constructor now explicitly casts the default options to Query type, preventing potential type errors.


34-37: Updated withLimit method for new pagination structure

The method now sets the limit within the pagination object instead of directly on the query root.


47-55: New withCursor method for cursor-based pagination

This new method supports the cursor-based pagination approach, allowing users to specify a cursor for fetching the next set of results.


57-63: New withDirection method for pagination direction

This new method allows controlling the pagination direction (Forward/Backward), completing the cursor-based pagination implementation.


77-79: Updated includeHashedKeys for new property name

The method now toggles no_hashed_keys instead of dont_include_hashed_keys, aligning with the property renaming.


90-95: Updated addOrderBy for new pagination structure

The method now adds order-by clauses to the nested pagination.order_by array instead of at the root level.


102-103: Updated withOrderBy for new pagination structure

The method now sets the order-by clauses in the nested pagination.order_by property rather than at the root level.


110-111: Updated addEntityModel for renamed property

The method now adds entity models to the models array instead of the previously named entity_models.


118-119: Updated withEntityModels for renamed property

The method now sets entity models in the models property instead of the previously named entity_models.


145-158: New HistoricalToriiQueryBuilder for historical queries

This new subclass extends ToriiQueryBuilder with historical: true by default, providing a clear and type-safe way to create historical queries. This is a good addition that simplifies the API for common use cases.


9-18:

✅ Verification successful

Refactored pagination structure and renamed properties

The default options now use a nested pagination object to group related pagination parameters. Property names have been improved for clarity:

  • dont_include_hashed_keysno_hashed_keys
  • entity_modelsmodels

This structure is cleaner but represents a breaking change for any code directly accessing these properties.


🏁 Script executed:

#!/bin/bash
# Check for any direct access to the old property names in the codebase
rg "dont_include_hashed_keys|entity_models" --type=ts

Length of output: 55


🏁 Script executed:

#!/bin/bash
# Broadly search across all file types for any remaining references to the old property names
rg "dont_include_hashed_keys|entity_models"

Length of output: 358


Approve pagination refactor – internal verification complete

We’ve confirmed that there are no remaining references to the old property names in the repository (only occurrences are in CHANGELOG.md). This change is safe to merge. External consumers should update any usage of:

  • dont_include_hashed_keysno_hashed_keys
  • entity_modelsmodels
packages/sdk/src/node/index.ts (10)

19-30: Updated imports for improved error handling

The changes include:

  • Simplifying Starknet imports to only what's needed
  • Adding neverthrow library for more robust error handling
  • Importing error constant definitions

These changes support the new error handling approach throughout the file.


34-36: Added exports for worker and model types

The new exports make worker functionality and model types available to consumers of the library. This improves the API's discoverability and usability.


64-65: Updated property name reference

Updated to use no_hashed_keys instead of dont_include_hashed_keys, consistent with the property renaming in ToriiQueryBuilder.


119-120: Updated property name in event query subscription

Updated to use no_hashed_keys instead of dont_include_hashed_keys, maintaining consistency with other changes.


127-130: Updated event query processing

Event messages are now retrieved and parsed from the .items property of the response, and no longer require a historical parameter. This aligns with the entity parsing approach and simplifies the API.


138-143: Simplified event message callback processing

The event message parsing in the callback now uses the same approach as entity parsing, processing an array of entities rather than individual entities. This provides consistency across the API.


189-189: Updated getEntities to use items array

The entity retrieval now properly accesses the .items property from the response and parses the array of entities.


202-204: Simplified event message retrieval

Event messages retrieval now consistently uses the .items property, eliminating the need for separate handling of historical events.


390-391: Updated entity query in toriiQueryIntoHashedKeys

The entity retrieval now properly accesses the .items property from the response, consistent with other changes.


401-407: Simplified historical query type parameter

The toriiEventMessagesQueryIntoHashedKeys method now:

  1. No longer requires a historical parameter
  2. Accesses the .items property from the event messages response
  3. Uses the same parsing approach as other methods

This simplifies the API and provides consistency, but it's a breaking change for code that relied on the previous behavior.

Comment on lines +162 to 180
let cursor = undefined;
let continueFetching = true;

const time = dbConnection ? getCache(timestampCacheKey) : 0;
// const time = dbConnection ? getCache(timestampCacheKey) : 0;

while (continueFetching) {
const entities = await client.getEntities(
{
const entities = await client.getEntities({
pagination: {
limit,
offset,
clause,
cursor,
direction: "Forward",
order_by: orderBy,
entity_models: entityModels,
dont_include_hashed_keys: false,
entity_updated_after: time,
},
historical
);
clause: clause || undefined,
no_hashed_keys: false,
models: entityModels,
historical,
});

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

⚠️ Potential issue

Array vs object mismatch – setEntities will mis-process results

client.getEntities() now returns entities.items as an array, but we forward that array directly to setEntities which still expects an object keyed by hashed-entity-id.
Symptoms you’ll see at runtime:

  • Keys become "0" | "1" | … instead of the real entityId, so look-ups, updates and deletes silently fail.
  • Component extraction loops over unexpected properties (hashed_keys, models, etc.) and throws.

Either convert the array to the previous object shape or refactor setEntities (and the downstream code-path) to understand the new array shape. A minimal hot-fix:

-        setEntities(entities.items, components, logging);
+        // normalise to the old { [hashedKey]: models } shape until setEntities is refactored
+        const entityMap = Object.fromEntries(
+            entities.items.map((e) => [e.hashed_keys.join("."), e.models])
+        );
+        setEntities(entityMap, components, logging);

Committable suggestion skipped: line range outside the PR's diff.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🔭 Outside diff range comments (1)
packages/state/src/recs/index.ts (1)

540-545: ⚠️ Potential issue

Fix entity iteration in insertEntitiesInDB

The function parameter has been updated to accept an array of ToriiEntity, but the code still treats it as an object with entity IDs as keys.

Update to properly iterate over the array:

-for (const [entityId, data] of Object.entries(entities)) {
-    const entityData = {
-        id: entityId,
-        ...data,
-    };
+for (const entity of entities) {
+    const entityData = {
+        id: entity.hashed_keys.join("."),
+        ...entity.models,
+    };
♻️ Duplicate comments (20)
examples/example-node-worker/package.json (1)

6-10: ⚠️ Potential issue

Remove hardcoded credentials from scripts

The scripts contain hardcoded private keys and identity values, which is a security risk. These should be moved to environment variables loaded from a .env file instead.

"scripts": {
-    "bun": "IDENTITY=0x0127fd5f1fe78a71f8bcd1fec63e3fe2f0486b6ecd5c86a0466c3a21fa5cfcec SECRET_KEY=0xc5b2fcab997346f3ea1c00b002ecf6f382c5f9c9659a3894eb783c5320f912 bun run main.ts",
-    "node": "IDENTITY=0x0127fd5f1fe78a71f8bcd1fec63e3fe2f0486b6ecd5c86a0466c3a21fa5cfcec SECRET_KEY=0xc5b2fcab997346f3ea1c00b002ecf6f382c5f9c9659a3894eb783c5320f912 node main.ts",
+    "bun": "bun run main.ts",
+    "node": "node main.ts",
    "build": "tsc -b"
},

Since you've already added dotenv as a dependency, you should use it to load these values from a .env file (which should be added to .gitignore).

🧰 Tools
🪛 Gitleaks (8.21.2)

7-7: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


8-8: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

packages/sdk/src/web/experimental/index.ts (1)

101-101: Strengthen callback parameter types

For improved type safety, replace the any type on the entityData parameter with torii.Entity, which aligns with the parseEntities<T>(entities: torii.Entity[]) signature.

🧰 Tools
🪛 Biome (1.9.4)

[error] 101-101: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

packages/sdk/src/internal/parseEntities.ts (1)

126-134: u64 can overflow JS number; parse it as BigInt

A 64-bit unsigned integer can exceed Number.MAX_SAFE_INTEGER (2⁵³-1), leading to precision loss if parsed with Number.parseInt. Treat it the same way as the larger integer types.

examples/example-node-worker/main.ts (4)

23-23: Wrong global identifier: use WebSocket, not Websocket

Browsers (and most polyfills) expose global.WebSocket (uppercase S).
Assigning to Websocket leaves libraries that expect WebSocket undefined.

-// @ts-ignore
-global.Websocket = w3cwebsocket;
+// @ts-ignore
+global.WebSocket = w3cwebsocket;

125-125: initPositionFromEvent mutates the shared constant

pc is an alias of defaultPositionCount, so every call mutates the constant and all future callers see cumulative side-effects.

-const pc = defaultPositionCount;
+const pc: PositionCount = { ...defaultPositionCount };

143-143: Potential data loss with data.pop()

data is already the argument supplied by the subscription.
Calling pop() both mutates the array and drops all but the last entity; if multiple entities arrive in the same batch they are silently ignored.

Iterate instead:

for (const entity of data) {
  // handle each entity
}

175-176: Unreachable code after the first return

The second return [sub]; can never execute—Biome rightfully flags it.

-    return [];
-    return [sub];
+    return [sub];
🧰 Tools
🪛 Biome (1.9.4)

[error] 176-176: This code is unreachable

... because this statement will return from the function beforehand

(lint/correctness/noUnreachable)

packages/state/src/recs/index.ts (4)

189-189: ⚠️ Potential issue

Fix length check for array structure

entities.items is now an array, but this code incorrectly uses Object.keys(entities.items).length to check its size.

Replace with:

-if (Object.keys(entities.items).length < limit) {
+if (entities.items.length < limit) {

246-246: ⚠️ Potential issue

Fix empty check for array structure

Similar to the previous issue, entities.items is now an array but is being checked as an object.

Replace with:

-if (Object.keys(entities.items).length === 0) {
+if (entities.items.length === 0) {

307-332: ⚠️ Potential issue

Fix infinite loop in getEntitiesQuery

The fetchedEntities is retrieved once outside the loop, but the cursor is updated inside the loop without fetching new data, causing either an infinite loop or immediate exit.

Move the fetch inside the loop:

while (continueFetching) {
+    const fetchedEntities = await client.getEntities({
+        pagination: {
+            limit,
+            cursor,
+            direction: "Forward",
+            order_by: orderBy,
+        },
+        clause: clause || undefined,
+        no_hashed_keys: false,
+        models: entityModels,
+        historical,
+    });

    if (logging)
        console.log(
            `Fetched ${Object.keys(fetchedEntities.items).length} entities ${fetchedEntities.next_cursor}`
        );

    setEntities(fetchedEntities.items, components, logging);

    if (Object.keys(fetchedEntities.items).length < limit) {
        continueFetching = false;
    } else {
        cursor = fetchedEntities.next_cursor;
    }
}

Also fix the array length check in the log and if-condition.


187-187: ⚠️ Potential issue

Fix entities data format mismatch

client.getEntities() now returns entities.items as an array, but setEntities expects an object keyed by entity ID.

Convert the array to the expected object format:

-setEntities(entities.items, components, logging);
+// Convert array to the expected {[hashedKey]: models} format
+const entityMap = Object.fromEntries(
+    entities.items.map((e) => [e.hashed_keys.join("."), e.models])
+);
+setEntities(entityMap, components, logging);
examples/example-node-worker/src/typescript/models.gen.ts (3)

38-46: ⚠️ Potential issue

Fix typo in "positition" field name

There appears to be a typo in the positition field name (should be "position").

export interface PositionCount {
	identity: string;
-	positition: Array<[BigNumberish, BigNumberish]>;
+	position: Array<[BigNumberish, BigNumberish]>;
}

export interface PositionCountValue {
-	positition: Array<[BigNumberish, BigNumberish]>;
+	position: Array<[BigNumberish, BigNumberish]>;
}

Since this affects the interface structure and is reflected in the schema initialization, this typo should be fixed in the generator if possible.


132-132: ⚠️ Potential issue

Fix field name in schema to match interface

The schema initializes the misspelled positition field, which should be fixed along with the interface.

-			positition: [[0, 0]],
+			position: [[0, 0]],

129-129: ⚠️ Potential issue

Fix field name in schema to match interface

The schema initializes the misspelled positition field, which should be fixed along with the interface.

-			positition: [[0, 0]],
+			position: [[0, 0]],
packages/sdk/src/internal/toriiQueryBuilder.ts (2)

133-133: Replace any with more specific type

Using any reduces type safety.

-static withPagination<T extends Record<string, Record<string, any>>>(
+static withPagination<T extends SchemaType>(
🧰 Tools
🪛 Biome (1.9.4)

[error] 133-133: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)


39-45: 🛠️ Refactor suggestion

Enhance deprecation notice for withOffset

The withOffset method has been converted to a no-op but doesn't provide guidance on migrating to cursor-based pagination.

Add a more descriptive deprecation notice explaining the migration path:

/**
 * Set the offset for pagination
- * @deprecated Use `withCursor` instead
+ * @deprecated Use `withCursor` instead. To convert an offset to a cursor, 
+ * retrieve the cursor for the first page and then paginate using the
+ * next_cursor value returned from each query. For example:
+ * 
+ * // Old approach:
+ * .withOffset(100).withLimit(50)
+ * 
+ * // New approach:
+ * // First page: .withLimit(100) - no cursor needed
+ * // Second page: .withCursor(result.next_cursor).withLimit(50)
 */
withOffset(): ToriiQueryBuilder<T> {
    return this;
}
packages/sdk/src/node/index.ts (4)

50-51: ⚠️ Potential issue

Client initialization change

Changed from torii.createClient to directly instantiating new torii.ToriiClient. This is a breaking change for anyone directly using the returned client.

Ensure this change is documented in migration guides and potentially consider providing a compatibility layer if feasible.


72-74: ⚠️ Potential issue

Updated entity parsing to use items array

The entity parsing has been updated to handle arrays from the .items property rather than keyed objects. This is a breaking change for existing code.

Ensure this change is documented in migration guides. Make sure that all examples and documentation are updated to reflect this new data structure.

Also applies to: 79-85


219-230: ⚠️ Potential issue

Modified generateTypedData signature

The method signature has changed:

  • Removed optional domain parameter
  • Added optional additionalTypes parameter
  • Domain is now always taken from options.domain

This is a breaking change for code that explicitly passed a domain parameter different from options.domain.

Ensure this change is documented in migration guides and consider if there are cases where passing a different domain was useful functionality.


236-249: 🛠️ Refactor suggestion

Enhanced sendMessage method with improved error handling

The sendMessage method now:

  1. Marks the Account parameter as optional with a leading underscore (indicating it's deprecated)
  2. Returns a Result type from neverthrow for consistent error handling
  3. Validates the presence of signer and identity before proceeding

This is a significant improvement in error handling but represents a breaking change for code expecting exceptions.

To complete the error handling improvement, the catch block should return an error result instead of throwing:

    } catch (error) {
        console.error("Failed to send message:", error);
-       throw error;
+       return err(`Failed to send message: ${error instanceof Error ? error.message : String(error)}`);
    }
🧹 Nitpick comments (11)
examples/example-vite-react-sdk/src/historical-events.tsx (2)

1-5: Update type imports to use import type syntax.

Since ParsedEntity and KeysClause are only used as types in this file, they should be imported using the import type syntax to improve bundling efficiency.

import {
-    KeysClause,
-    ParsedEntity,
+    KeysClause,
+    type ParsedEntity,
    HistoricalToriiQueryBuilder,
} from "@dojoengine/sdk";
🧰 Tools
🪛 Biome (1.9.4)

[error] 1-5: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)


33-33: Replace @ts-ignore with more specific @ts-expect-error.

Using @ts-ignore suppresses all type checking for the line and makes it harder to track why the suppression was needed. Consider providing a more descriptive comment explaining why type checking needs to be suppressed here.

-            {/* @ts-ignore */}
+            {/* @ts-expect-error events is an array of ParsedEntity<SchemaType> */}
packages/sdk/src/__tests__/zustand.perf.test.ts (1)

131-133: Consider removing non-null assertions for better type safety.

The non-null assertions (!) on world and item properties could be replaced with optional chaining and nullish coalescing for better type safety.

draft.entities[
    "entity500"
-].models.world!.item!.durability = 75;
+].models.world?.item?.durability = 75;

If you're certain these properties can never be null, consider adding runtime validation or structuring your data to make this guarantee explicit in the type system.

🧰 Tools
🪛 Biome (1.9.4)

[error] 131-133: Forbidden non-null assertion.

(lint/style/noNonNullAssertion)


[error] 131-133: Forbidden non-null assertion.

Unsafe fix: Replace with optional chain operator ?. This operator includes runtime checks, so it is safer than the compile-only non-null assertion operator

(lint/style/noNonNullAssertion)


[error] 132-132: The computed expression can be simplified without the use of a string literal.

Unsafe fix: Use a literal key instead.

(lint/complexity/useLiteralKeys)

packages/sdk/src/web/state/zustand.ts (1)

14-14: Update type import to use import type syntax.

Since MergedModels is only used as a type, it should be imported using the import type syntax to improve bundling efficiency.

-import { deepMerge, MergedModels } from "../../internal/merge";
+import { deepMerge, type MergedModels } from "../../internal/merge";
🧰 Tools
🪛 Biome (1.9.4)

[error] 14-14: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

examples/example-node-worker/src/typescript/contracts.gen.ts (1)

1-2: Use import type for type-only imports

Several imports are only used as types and should use the import type syntax to ensure they're removed during compilation.

-import { DojoProvider, DojoCall } from "@dojoengine/core";
-import { Account, AccountInterface, BigNumberish, CairoOption, CairoCustomEnum, ByteArray } from "starknet";
+import { DojoProvider } from "@dojoengine/core";
+import type { DojoCall } from "@dojoengine/core";
+import { Account } from "starknet";
+import type { AccountInterface, BigNumberish, CairoOption, CairoCustomEnum, ByteArray } from "starknet";
🧰 Tools
🪛 Biome (1.9.4)

[error] 1-1: All these imports are only used as types.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Use import type.

(lint/style/useImportType)


[error] 2-2: Some named imports are only used as types.

This import is only used as a type.

This import is only used as a type.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

packages/state/src/recs/index.ts (1)

185-185: Use regular string instead of template literal

Template literals should only be used when interpolation is needed.

Replace with a regular string literal:

-if (logging) console.log(`Fetched entities`, entities.items);
+if (logging) console.log("Fetched entities", entities.items);
🧰 Tools
🪛 Biome (1.9.4)

[error] 185-185: Do not use template literals if interpolation and special-character handling are not needed.

Unsafe fix: Replace with string literal

(lint/style/noUnusedTemplateLiteral)

packages/sdk/src/internal/merge.ts (3)

76-76: Specify more precise type for enum content

Using any reduces type safety. Consider using a more specific type for the enum content.

-const newEnumContent: Record<string, any> = {};
+const newEnumContent: Record<string, unknown> = {};
🧰 Tools
🪛 Biome (1.9.4)

[error] 76-76: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)


96-96: Specify more precise type for enum content

Using any reduces type safety. Consider using a more specific type for the enum content.

-const newEnumContent: Record<string, any> = {};
+const newEnumContent: Record<string, unknown> = {};
🧰 Tools
🪛 Biome (1.9.4)

[error] 96-96: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)


133-133: Specify more precise type for result object

Using any reduces type safety. Consider using a more specific type for the result.

-const result = { ...target } as Record<string, any>;
+const result = { ...target } as Record<string, unknown>;
🧰 Tools
🪛 Biome (1.9.4)

[error] 133-133: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

examples/example-node-worker/src/typescript/models.gen.ts (2)

3-3: Use import type for type-only imports

When imports are only used in type positions, use the import type syntax to ensure they're removed during compilation.

-import { CairoCustomEnum, CairoOption, CairoOptionVariant, BigNumberish } from 'starknet';
+import { CairoCustomEnum, CairoOption, CairoOptionVariant } from 'starknet';
+import type { BigNumberish } from 'starknet';
🧰 Tools
🪛 Biome (1.9.4)

[error] 3-3: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)


99-103: Fix inconsistent indentation in CairoCustomEnum initializations

The indentation in the CairoCustomEnum initializations is inconsistent, making the code harder to read and maintain.

Fix the indentation to be consistent across all instances:

directions: [new CairoCustomEnum({ 
-					Left: "",
-				Right: undefined,
-				Up: undefined,
-				Down: undefined, })],
+	Left: "",
+	Right: undefined,
+	Up: undefined,
+	Down: undefined,
})],

Apply similar fixes to all other CairoCustomEnum instances in the file.

Also applies to: 107-110, 144-147, 150-154

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro (Legacy)

📥 Commits

Reviewing files that changed from the base of the PR and between 4d32324 and ae00a22.

⛔ Files ignored due to path filters (5)
  • examples/example-vite-react-phaser-recs/src/dojo/generated/setup.ts is excluded by !**/generated/**
  • examples/example-vite-react-pwa-recs/src/dojo/generated/setup.ts is excluded by !**/generated/**
  • examples/example-vite-react-threejs-recs/src/dojo/generated/setup.ts is excluded by !**/generated/**
  • examples/example-vue-app-recs/src/dojo/generated/setup.ts is excluded by !**/generated/**
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (42)
  • .changeset/thirty-cheetahs-design.md (1 hunks)
  • .github/workflows/ci.yaml (1 hunks)
  • .github/workflows/release.yaml (6 hunks)
  • examples/example-node-worker/dojoConfig.ts (1 hunks)
  • examples/example-node-worker/env.ts (1 hunks)
  • examples/example-node-worker/main.ts (1 hunks)
  • examples/example-node-worker/package.json (1 hunks)
  • examples/example-node-worker/src/typescript/contracts.gen.ts (1 hunks)
  • examples/example-node-worker/src/typescript/models.gen.ts (1 hunks)
  • examples/example-node-worker/tsconfig.json (1 hunks)
  • examples/example-nodejs-bot/package.json (1 hunks)
  • examples/example-vite-react-app-recs/src/dojo/setup.ts (2 hunks)
  • examples/example-vite-react-sdk/src/historical-events.tsx (2 hunks)
  • package.json (1 hunks)
  • packages/core/package.json (1 hunks)
  • packages/react/package.json (2 hunks)
  • packages/sdk/package.json (3 hunks)
  • packages/sdk/src/__tests__/parseEntities.test.ts (7 hunks)
  • packages/sdk/src/__tests__/parseHistoricalEvents.test.ts (0 hunks)
  • packages/sdk/src/__tests__/state.test.ts (3 hunks)
  • packages/sdk/src/__tests__/toriiQueryBuilder.test.ts (6 hunks)
  • packages/sdk/src/__tests__/zustand.perf.test.ts (1 hunks)
  • packages/sdk/src/internal/errors.ts (1 hunks)
  • packages/sdk/src/internal/generateTypedData.ts (2 hunks)
  • packages/sdk/src/internal/merge.ts (1 hunks)
  • packages/sdk/src/internal/models.ts (1 hunks)
  • packages/sdk/src/internal/parseEntities.ts (4 hunks)
  • packages/sdk/src/internal/parseHistoricalEvents.ts (0 hunks)
  • packages/sdk/src/internal/toriiQueryBuilder.ts (6 hunks)
  • packages/sdk/src/internal/types.ts (5 hunks)
  • packages/sdk/src/node/index.ts (12 hunks)
  • packages/sdk/src/node/worker.ts (1 hunks)
  • packages/sdk/src/web/experimental/index.ts (4 hunks)
  • packages/sdk/src/web/index.ts (11 hunks)
  • packages/sdk/src/web/react/hooks/entities.ts (1 hunks)
  • packages/sdk/src/web/react/hooks/events.ts (3 hunks)
  • packages/sdk/src/web/react/hooks/hooks.ts (4 hunks)
  • packages/sdk/src/web/state/zustand.ts (1 hunks)
  • packages/state/src/recs/index.ts (6 hunks)
  • packages/torii-client/package.json (2 hunks)
  • packages/torii-wasm/dojo.c (1 hunks)
  • turbo.json (1 hunks)
💤 Files with no reviewable changes (2)
  • packages/sdk/src/internal/parseHistoricalEvents.ts
  • packages/sdk/src/tests/parseHistoricalEvents.test.ts
✅ Files skipped from review due to trivial changes (4)
  • .changeset/thirty-cheetahs-design.md
  • .github/workflows/ci.yaml
  • packages/sdk/package.json
  • packages/sdk/src/tests/state.test.ts
🚧 Files skipped from review as they are similar to previous changes (23)
  • examples/example-nodejs-bot/package.json
  • package.json
  • packages/torii-wasm/dojo.c
  • packages/torii-client/package.json
  • examples/example-vite-react-app-recs/src/dojo/setup.ts
  • packages/core/package.json
  • examples/example-node-worker/dojoConfig.ts
  • examples/example-node-worker/tsconfig.json
  • packages/react/package.json
  • examples/example-node-worker/env.ts
  • packages/sdk/src/web/react/hooks/entities.ts
  • turbo.json
  • packages/sdk/src/tests/parseEntities.test.ts
  • packages/sdk/src/node/worker.ts
  • .github/workflows/release.yaml
  • packages/sdk/src/internal/errors.ts
  • packages/sdk/src/web/react/hooks/events.ts
  • packages/sdk/src/internal/types.ts
  • packages/sdk/src/tests/toriiQueryBuilder.test.ts
  • packages/sdk/src/internal/generateTypedData.ts
  • packages/sdk/src/web/react/hooks/hooks.ts
  • packages/sdk/src/internal/models.ts
  • packages/sdk/src/web/index.ts
🧰 Additional context used
🧬 Code Graph Analysis (5)
packages/sdk/src/internal/toriiQueryBuilder.ts (1)
packages/sdk/src/internal/types.ts (2)
  • ToriiQueryBuilder (557-557)
  • SchemaType (50-70)
examples/example-node-worker/src/typescript/contracts.gen.ts (2)
examples/example-node-worker/src/typescript/models.gen.ts (1)
  • direction (71-76)
packages/core/src/types/index.ts (1)
  • DojoCall (130-134)
examples/example-vite-react-sdk/src/historical-events.tsx (1)
packages/sdk/src/internal/toriiQueryBuilder.ts (1)
  • HistoricalToriiQueryBuilder (149-159)
packages/sdk/src/internal/merge.ts (3)
packages/sdk/src/web/queryBuilder.ts (1)
  • value (191-193)
examples/example-node-worker/src/typescript/models.gen.ts (1)
  • SchemaType (80-94)
packages/sdk/src/internal/types.ts (2)
  • SchemaType (50-70)
  • ParsedEntity (228-237)
packages/sdk/src/web/experimental/index.ts (2)
packages/sdk/src/internal/parseEntities.ts (1)
  • parseEntities (10-59)
packages/sdk/src/internal/convertClauseToEntityKeysClause.ts (1)
  • intoEntityKeysClause (4-36)
🪛 Biome (1.9.4)
examples/example-node-worker/main.ts

[error] 176-176: This code is unreachable

... because this statement will return from the function beforehand

(lint/correctness/noUnreachable)

packages/sdk/src/internal/toriiQueryBuilder.ts

[error] 133-133: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

examples/example-node-worker/src/typescript/contracts.gen.ts

[error] 1-1: All these imports are only used as types.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Use import type.

(lint/style/useImportType)


[error] 2-2: Some named imports are only used as types.

This import is only used as a type.

This import is only used as a type.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

examples/example-node-worker/src/typescript/models.gen.ts

[error] 3-3: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

examples/example-vite-react-sdk/src/historical-events.tsx

[error] 1-5: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

packages/sdk/src/__tests__/zustand.perf.test.ts

[error] 131-133: Forbidden non-null assertion.

(lint/style/noNonNullAssertion)


[error] 131-133: Forbidden non-null assertion.

Unsafe fix: Replace with optional chain operator ?. This operator includes runtime checks, so it is safer than the compile-only non-null assertion operator

(lint/style/noNonNullAssertion)


[error] 132-132: The computed expression can be simplified without the use of a string literal.

Unsafe fix: Use a literal key instead.

(lint/complexity/useLiteralKeys)

packages/sdk/src/internal/merge.ts

[error] 76-76: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)


[error] 96-96: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)


[error] 133-133: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

packages/sdk/src/internal/parseEntities.ts

[error] 50-50: Do not use template literals if interpolation and special-character handling are not needed.

Unsafe fix: Replace with string literal

(lint/style/noUnusedTemplateLiteral)

packages/sdk/src/web/experimental/index.ts

[error] 30-30: This type annotation is trivially inferred from its initialization.

Safe fix: Remove the type annotation.

(lint/style/noInferrableTypes)


[error] 101-101: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

packages/sdk/src/web/state/zustand.ts

[error] 14-14: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

packages/state/src/recs/index.ts

[error] 185-185: Do not use template literals if interpolation and special-character handling are not needed.

Unsafe fix: Replace with string literal

(lint/style/noUnusedTemplateLiteral)

🪛 Gitleaks (8.21.2)
examples/example-node-worker/package.json

7-7: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


8-8: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build
🔇 Additional comments (23)
examples/example-vite-react-sdk/src/historical-events.tsx (1)

14-14: LGTM! Proper implementation of HistoricalToriiQueryBuilder.

The change from ToriiQueryBuilder to HistoricalToriiQueryBuilder correctly implements the new API for historical queries.

examples/example-node-worker/package.json (1)

13-19: LGTM! Appropriate dependencies for supporting new features.

The new dependencies appropriately support the features described in the AI summary, including environment variable loading (dotenv), schema validation (zod), StarkNet integration, and WebSocket support.

packages/sdk/src/web/experimental/index.ts (4)

24-24: Client initialization updated to use synchronous constructor

The change from asynchronous await torii.createClient() to synchronous new torii.ToriiClient() aligns with similar changes in other SDK modules, providing a more consistent instantiation pattern.


28-29: Updated entity/event retrieval to extract array items

The code now correctly extracts the .items array from the paginated response before passing to the parsing functions, aligning with the cursor-based pagination changes in the Torii client.

Also applies to: 31-33


39-40: Query parameter renamed for consistency

The flag has been renamed from dont_include_hashed_keys to no_hashed_keys for better naming consistency across the SDK.

Also applies to: 82-83


54-59: Entity/event parsing updated to process arrays

The callback has been modernized to handle arrays of entity data instead of keyed objects, aligning with the new parseEntities approach. This simplifies data processing and provides a more consistent pattern across the SDK.

Also applies to: 104-106

examples/example-node-worker/src/typescript/contracts.gen.ts (1)

5-59: Well-structured contract interaction helpers

The implementation provides a clean interface for contract interactions with:

  • Separate build/execute functions that promote reusability
  • Proper error handling and logging
  • Clear return value structure
  • Type-safe parameters using CairoCustomEnum for direction

This pattern makes it easier to interact with the smart contract in a type-safe manner.

packages/sdk/src/internal/parseEntities.ts (2)

10-25: Entity parsing refactored to use arrays

The function now accepts an array of torii.Entity objects instead of a keyed object, simplifying the iteration and avoiding unnecessary transformations. This creates a more consistent pattern across the SDK.


90-92: Added support for tuple types

The parseValue function now handles tuples the same way as arrays, ensuring proper recursive parsing of nested tuple structures.

examples/example-node-worker/main.ts (3)

162-166: Initialize position counts from historical events

The code now properly initializes the position counts from historical events, providing a complete state for the worker to operate on. This is a good practice to ensure consistency with on-chain data.


167-173: Updated subscription to use new API

The subscription method now correctly uses the new entity query format with callbacks, allowing the worker to process entity updates efficiently.


79-118: Added offchain position count publishing

The new function provides a clean interface for publishing offchain data, including proper type generation and error handling for messages. This extends the example to showcase SDK's offchain capabilities.

packages/sdk/src/internal/merge.ts (3)

1-46: Well-structured Cairo option handling

The implementation of isCairoOption and mergeCairoOption functions properly handles different states of Cairo option types.


120-121: Good type definition for MergedModels

The type definition for MergedModels appropriately extracts the correct model type from the ParsedEntity to ensure type safety during merging.


123-160: Well-implemented deep merge function

The deepMerge function handles different types of data correctly:

  • Detects and delegates to specialized merge functions for Cairo types
  • Handles plain objects with nested properties
  • Preserves arrays and primitive values

This ensures that complex nested data structures are merged properly throughout the codebase.

🧰 Tools
🪛 Biome (1.9.4)

[error] 133-133: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

packages/sdk/src/internal/toriiQueryBuilder.ts (2)

145-158: Well-implemented HistoricalToriiQueryBuilder

The new HistoricalToriiQueryBuilder class provides a convenient way to create queries with historical data, properly extending the base class with clear options typing.


47-55: Good implementation of withCursor for cursor-based pagination

The new withCursor method properly implements cursor-based pagination, which is a more efficient approach than offset-based pagination for large datasets.

packages/sdk/src/node/index.ts (6)

19-19: Enhanced error handling with neverthrow

The addition of the neverthrow library and error constants improves error handling consistency across the SDK. The changes to imports reflect this shift towards more structured error management.

Also applies to: 29-30


34-36: Additional exports enhance SDK functionality

Exporting worker functionality, internal types, and models provides users with more utility and flexibility. This improves the developer experience by exposing previously internal functionality.


189-189: Simplified entity fetching with array-based parsing

The getEntities method now uses array-based parsing from the .items property, consistent with subscription methods. This improves API consistency.


202-204: Simplified event message fetching with array-based parsing

Event message fetching now uses array-based parsing from the .items property and removes historical event handling, consistent with the other methods.


251-265: Updated message signing process

The message signing process now uses options.signer and options.identity instead of the Account parameter, with improved error handling using Result types.


390-391: Aligned helper methods with array-based approach

The helper methods toriiQueryIntoHashedKeys and toriiEventMessagesQueryIntoHashedKeys now use array-based parsing from .items, consistent with the other methods.

Also applies to: 402-408

@MartianGreed MartianGreed force-pushed the chore/dojo1.5 branch 2 times, most recently from 5c35f5c to 6588556 Compare April 29, 2025 07:39
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🔭 Outside diff range comments (3)
packages/state/src/recs/index.ts (1)

516-555: ⚠️ Potential issue

Entity array vs object mismatch in insertEntitiesInDB

The function signature now accepts an array of ToriiEntity, but the implementation still processes it as an object with Object.entries(entities). This will cause runtime errors.

 async function insertEntitiesInDB(
     db: IDBDatabase,
     entities: ToriiEntity[]
 ): Promise<void> {
     return new Promise((resolve, reject) => {
         const transaction = db.transaction(["entities"], "readwrite");
         const store = transaction.objectStore("entities");

         let completed = 0;
         let error: Error | null = null;

         // Handle transaction completion
         transaction.oncomplete = () => {
             if (error) {
                 reject(error);
             } else {
                 resolve();
             }
         };

         transaction.onerror = () => {
             reject(transaction.error);
         };

         // Store each entity
-        for (const [entityId, data] of Object.entries(entities)) {
+        for (const entity of entities) {
             const entityData = {
-                id: entityId,
+                id: entity.hashed_keys.join("."),
-                ...data,
+                ...entity.models,
             };

             const request = store.put(entityData);
             completed++;

             request.onerror = () => {
                 error = request.error;
             };
         }
     });
 }
packages/sdk/src/web/index.ts (1)

279-282: ⚠️ Potential issue

Inconsistent error handling pattern

The function returns a Result type but still throws errors in the catch block, breaking the Result contract and forcing callers to juggle two error channels.

            } catch (error) {
                console.error("Failed to send message:", error);
-                throw error;
+                return err(String(error));
            }
packages/sdk/src/node/index.ts (1)

240-269: 🛠️ Refactor suggestion

Inconsistent error handling in sendMessage method

The sendMessage method has been improved to:

  1. Check for the presence of signer and identity before attempting to send
  2. Return a Result type (from neverthrow) with ok/err instead of throwing exceptions
  3. No longer require an explicit Account parameter, relying on options.signer and options.identity

However, there's inconsistent error handling - the function returns err for configuration issues but still throws in the catch block.

Make the error handling consistent by returning err in the catch block as well:

    try {
        const td = new torii.TypedData(JSON.stringify(data)).encode(
            options.identity
        );

        const sig = options.signer.sign(td);

        const dataString = JSON.stringify(data);

        return ok(
            await client.publishMessage(dataString, [
                sig.r.toString(),
                sig.s.toString(),
            ])
        );
    } catch (error) {
        console.error("Failed to send message:", error);
-        throw error;
+        return err(`Failed to send message: ${error instanceof Error ? error.message : String(error)}`);
    }
♻️ Duplicate comments (18)
packages/sdk/src/web/experimental/index.ts (1)

101-106: Strengthen callback parameter types

For improved type safety, replace the any type on the entityData parameter with torii.Entity, which aligns with the parseEntities<T>(entities: torii.Entity[]) signature.

-                    (_: string, entityData: any) => {
+                    (_: string, entityData: torii.Entity) => {
🧰 Tools
🪛 Biome (1.9.4)

[error] 101-101: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

packages/sdk/src/internal/parseEntities.ts (1)

127-133: ⚠️ Potential issue

u64 can still overflow JavaScript numbers

The u64 type is still being parsed with Number.parseInt instead of BigInt, which can lead to precision loss for large values exceeding JavaScript's safe integer range (2^53-1).

case "u64":
-    return Number.parseInt(value.value as string, 16);
+    return BigInt(value.value as string);
case "i256":
case "i128":
case "u256":
case "u128":
    return BigInt(value.value as string);
packages/state/src/recs/index.ts (3)

162-193: ⚠️ Potential issue

Update array length checking

Since entities.items is now an array, using Object.keys(entities.items).length is semantically confusing and less efficient.

-        if (Object.keys(entities.items).length < limit) {
+        if (entities.items.length < limit) {
             continueFetching = false;
         } else {
             cursor = entities.next_cursor;
         }
🧰 Tools
🪛 Biome (1.9.4)

[error] 185-185: Do not use template literals if interpolation and special-character handling are not needed.

Unsafe fix: Replace with string literal

(lint/style/noUnusedTemplateLiteral)


225-250: ⚠️ Potential issue

Same array length check issue in getEvents

Update the array length check here as well:

-        if (Object.keys(entities.items).length === 0) {
+        if (entities.items.length === 0) {
             continueFetching = false;
         } else {
             cursor = entities.next_cursor;
         }

307-333: ⚠️ Potential issue

Fix infinite loop in getEntitiesQuery

There's a critical issue here: the fetch happens once outside the loop, and inside the loop it only updates the cursor but never fetches new data. This will either cause an infinite loop or exit immediately depending on the first batch size.

-    const fetchedEntities = await client.getEntities({
-        pagination: {
-            limit,
-            cursor,
-            direction: "Forward",
-            order_by: orderBy,
-        },
-        clause: clause || undefined,
-        no_hashed_keys: false,
-        models: entityModels,
-        historical,
-    });

     while (continueFetching) {
+        const fetchedEntities = await client.getEntities({
+            pagination: {
+                limit,
+                cursor,
+                direction: "Forward",
+                order_by: orderBy,
+            },
+            clause: clause || undefined,
+            no_hashed_keys: false,
+            models: entityModels,
+            historical,
+        });
+
         if (logging)
             console.log(
                 `Fetched ${Object.keys(fetchedEntities.items).length} entities ${fetchedEntities.next_cursor}`
             );

         setEntities(fetchedEntities.items, components, logging);

-        if (Object.keys(fetchedEntities.items).length < limit) {
+        if (fetchedEntities.items.length < limit) {
             continueFetching = false;
         } else {
             cursor = fetchedEntities.next_cursor;
         }
     }
examples/example-node-worker/main.ts (4)

21-25: ⚠️ Potential issue

Fix WebSocket capitalization

Browsers and Node.js polyfills expect WebSocket with a capital 'S'. Using lowercase Websocket can cause compatibility issues with libraries that expect the standard global name.

// Those lines are require so that websocket works.
// @ts-ignore
-global.Websocket = w3cwebsocket;
+global.WebSocket = w3cwebsocket;
// @ts-ignore
global.WorkerGlobalScope = global;

122-134: ⚠️ Potential issue

Avoid mutating the shared constant

The function is modifying the defaultPositionCount constant, which will affect all future callers. Create a new object instead:

function initPositionFromEvent(
    events: ParsedEntity<SchemaType>[]
): PositionCount {
-    const pc = defaultPositionCount;
+    const pc: PositionCount = { ...defaultPositionCount };
    for (const e of events) {
        const moved = e.models.dojo_starter.Moved;
        if (!moved) {
            continue;
        }
        pc[moved.direction] += 1;
    }
    return pc;
}

137-151: ⚠️ Potential issue

Process all entities in the subscription data

Using data.pop() both mutates the array and drops all but the last entity. If multiple entities arrive in the same batch, they're silently ignored.

async function onEntityUpdated({ data, error }) {
    if (error) {
        console.error(error);
        return;
    }

-    const entity = data.pop();
-    if (entity && entity.entityId !== "0x0") {
-        // do whatever you need here
-        const model = entity.models.dojo_starter;
-        if (model?.Moves) {
-            await publishOffchainPositionCount(model.Moves);
-        }
-    }
+    for (const entity of data) {
+        if (entity && entity.entityId !== "0x0") {
+            // do whatever you need here
+            const model = entity.models.dojo_starter;
+            if (model?.Moves) {
+                await publishOffchainPositionCount(model.Moves);
+            }
+        }
+    }
}

175-176: ⚠️ Potential issue

Fix unreachable code

The second return statement is unreachable due to the preceding return []. This needs to be fixed:

-    return [];
-    return [sub];
+    return [sub];
🧰 Tools
🪛 Biome (1.9.4)

[error] 176-176: This code is unreachable

... because this statement will return from the function beforehand

(lint/correctness/noUnreachable)

examples/example-node-worker/src/typescript/models.gen.ts (2)

38-46: ⚠️ Potential issue

Fix typo in "positition" field name

There appears to be a typo in the positition field name (should be "position").

export interface PositionCount {
	identity: string;
-	positition: Array<[BigNumberish, BigNumberish]>;
+	position: Array<[BigNumberish, BigNumberish]>;
}

export interface PositionCountValue {
-	positition: Array<[BigNumberish, BigNumberish]>;
+	position: Array<[BigNumberish, BigNumberish]>;
}

Since this affects the interface structure and is reflected in the schema initialization, this typo should be fixed in the generator if possible.


128-130: ⚠️ Potential issue

Ensure field names in schema match interfaces

Fix the typo in the schema definition to match the corrected interface.

PositionCount: {
	identity: "",
-	positition: [[0, 0]],
+	position: [[0, 0]],
},
packages/sdk/src/web/index.ts (1)

271-277: 🛠️ Refactor suggestion

Ensure signature elements are properly converted to strings

When signature is an array of bigints, they should be explicitly converted to strings as publishMessage expects string array.

                return ok(
                    await client.publishMessage(
                        dataString,
-                        Array.isArray(signature)
-                            ? signature
-                            : [signature.r.toString(), signature.s.toString()]
+                        Array.isArray(signature)
+                            ? signature.map(s => s.toString())
+                            : [signature.r.toString(), signature.s.toString()]
                    )
                );
packages/sdk/src/internal/toriiQueryBuilder.ts (1)

39-45: 🛠️ Refactor suggestion

Enhance deprecation notice for withOffset method

The withOffset method is now a no-op, which could silently break existing code that relies on offset-based pagination. Consider adding a more descriptive notice explaining the migration path.

    /**
     * Set the offset for pagination
-     * @deprecated Use `withCursor` instead
+     * @deprecated Use `withCursor` instead. For migration, you can convert your offset to a cursor:
+     * - For initial pagination, use undefined cursor to start from the beginning
+     * - For subsequent pages, use the next_cursor value returned from the previous query
+     * - To roughly approximate old offset behavior: fetch with undefined cursor and skip offset records
     */
    withOffset(): ToriiQueryBuilder<T> {
        return this;
    }
packages/sdk/src/node/index.ts (5)

50-50: Client initialization change

Changed from torii.createClient to directly instantiating new torii.ToriiClient. This is a breaking change for anyone directly using the returned client.

Ensure this change is documented in migration guides and potentially consider providing a compatibility layer if feasible.


64-65: Renamed query parameter for improved clarity

Parameter renamed from dont_include_hashed_keys to no_hashed_keys in both entity and event query subscriptions. This is a breaking change but improves naming consistency.

Ensure this change is documented in migration guides and update all examples to use the new parameter name.


72-74: Updated entity parsing to use items array

The entity parsing has been updated to handle arrays from the .items property rather than keyed objects. This change simplifies the data structure but is a breaking change for existing code.

Ensure this change is documented in migration guides. Make sure that all examples and documentation are updated to reflect this new data structure.


127-130: Updated event parsing to use items array

Similar to the entity parsing changes, event parsing now uses arrays from the .items property. This aligns event and entity parsing approaches for better consistency.

Update all documentation and examples to reflect this structural change in event data handling.


215-230: Modified generateTypedData signature

The method signature has changed:

  • Removed optional domain parameter
  • Added optional additionalTypes parameter
  • Domain is now always taken from options.domain

This is a breaking change for code that explicitly passed a domain parameter different from options.domain.

Ensure this change is documented in migration guides and consider if there are cases where passing a different domain was useful functionality.

🧹 Nitpick comments (15)
examples/example-vite-react-sdk/src/historical-events.tsx (1)

1-5: Optimize imports to use import type for type-only imports

Some of the imported symbols are only used as types and should be imported using import type syntax for better build performance.

-import {
-    KeysClause,
-    ParsedEntity,
-    HistoricalToriiQueryBuilder,
-} from "@dojoengine/sdk";
+import {
+    KeysClause,
+    HistoricalToriiQueryBuilder,
+} from "@dojoengine/sdk";
+import type { ParsedEntity } from "@dojoengine/sdk";
🧰 Tools
🪛 Biome (1.9.4)

[error] 1-5: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

packages/sdk/src/__tests__/zustand.perf.test.ts (1)

131-133: Improved code readability with line breaks

Breaking the nested property access into multiple lines improves readability and makes the test code easier to understand and maintain.

Consider replacing the non-null assertions (!) with optional chaining (?.) for safer property access:

 draft.entities[
     "entity500"
-].models.world!.item!.durability = 75;
+].models.world?.item?.durability = 75;
🧰 Tools
🪛 Biome (1.9.4)

[error] 131-133: Forbidden non-null assertion.

(lint/style/noNonNullAssertion)


[error] 131-133: Forbidden non-null assertion.

Unsafe fix: Replace with optional chain operator ?. This operator includes runtime checks, so it is safer than the compile-only non-null assertion operator

(lint/style/noNonNullAssertion)


[error] 132-132: The computed expression can be simplified without the use of a string literal.

Unsafe fix: Use a literal key instead.

(lint/complexity/useLiteralKeys)

packages/sdk/src/web/state/zustand.ts (1)

14-14: Excellent refactoring: Centralizing merge utilities

Replacing local implementations with a centralized module improves maintainability and ensures consistent handling of Cairo-specific types across the codebase.

Consider using import type for the MergedModels type since it's only used as a type:

-import { deepMerge, MergedModels } from "../../internal/merge";
+import { deepMerge } from "../../internal/merge";
+import type { MergedModels } from "../../internal/merge";
🧰 Tools
🪛 Biome (1.9.4)

[error] 14-14: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

examples/example-node-worker/src/typescript/contracts.gen.ts (1)

1-4: Optimize imports with import type for type-only imports

Several of these imports are only used as types in the file. Consider using the import type syntax to ensure they're properly removed during compilation.

-import { DojoProvider, DojoCall } from "@dojoengine/core";
-import { Account, AccountInterface, BigNumberish, CairoOption, CairoCustomEnum, ByteArray } from "starknet";
+import { DojoProvider } from "@dojoengine/core";
+import type { DojoCall } from "@dojoengine/core";
+import { Account, AccountInterface } from "starknet";
+import type { BigNumberish, CairoOption, CairoCustomEnum, ByteArray } from "starknet";
import * as models from "./models.gen";
🧰 Tools
🪛 Biome (1.9.4)

[error] 1-1: All these imports are only used as types.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Use import type.

(lint/style/useImportType)


[error] 2-2: Some named imports are only used as types.

This import is only used as a type.

This import is only used as a type.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

packages/sdk/src/internal/merge.ts (4)

75-77: Avoid using any type for enhanced type safety

Replace the generic any type with a more specific type for newEnumContent.

-        const newEnumContent: Record<string, any> = {};
+        const newEnumContent: Record<string, unknown> = {};
🧰 Tools
🪛 Biome (1.9.4)

[error] 76-76: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)


95-97: Avoid using any type for enhanced type safety

Replace the generic any type with a more specific type for newEnumContent.

-        const newEnumContent: Record<string, any> = {};
+        const newEnumContent: Record<string, unknown> = {};
🧰 Tools
🪛 Biome (1.9.4)

[error] 96-96: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)


133-133: Avoid using any type for enhanced type safety

Replace the generic any type with a more specific type for result.

-    const result = { ...target } as Record<string, any>;
+    const result = { ...target } as Record<string, unknown>;
🧰 Tools
🪛 Biome (1.9.4)

[error] 133-133: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)


9-11: Consider enhancing the type guard for broader compatibility

The current implementation checks if the value is an instance of CairoOption, but it might not handle all possible Cairo option formats or serialized forms.

Consider enhancing the type guard to handle both instance checks and structural checks:

export function isCairoOption(value: unknown): value is CairoOption<unknown> {
-    return value instanceof CairoOption;
+    return (
+        value instanceof CairoOption || 
+        (typeof value === 'object' && 
+         value !== null && 
+         ('variant' in value || 'isSome' in value))
+    );
}
examples/example-node-worker/src/typescript/models.gen.ts (1)

3-3: Use import type for type-only imports

Since these imports are only used as types, it's better to use the import type syntax to ensure they're removed during compilation.

-import { CairoCustomEnum, CairoOption, CairoOptionVariant, BigNumberish } from 'starknet';
+import type { CairoCustomEnum, CairoOption, CairoOptionVariant, BigNumberish } from 'starknet';
🧰 Tools
🪛 Biome (1.9.4)

[error] 3-3: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

packages/sdk/src/web/index.ts (2)

236-239: Fix typo in parameter name

The parameter name "additionnalTypes" contains a spelling error (extra 'n').

-            additionnalTypes?: Record<
+            additionalTypes?: Record<
                string,
                Array<{ name: string; type: string }>
            >

245-246: Fix typo in argument name

Ensure the argument name matches the corrected parameter name.

                options.domain,
                modelMapping,
-                additionnalTypes
+                additionalTypes
packages/sdk/src/internal/toriiQueryBuilder.ts (2)

133-133: Replace any with more specific type constraint

Using any in type constraints disables many type checking rules. Consider using a more specific type.

-    static withPagination<T extends Record<string, Record<string, any>>>(
+    static withPagination<T extends SchemaType>(
🧰 Tools
🪛 Biome (1.9.4)

[error] 133-133: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)


152-158: Add documentation for the HistoricalToriiQueryBuilder class

This new class lacks documentation explaining its purpose and how it differs from the base class.

export class HistoricalToriiQueryBuilder<
    T extends SchemaType
> extends ToriiQueryBuilder<T> {
+    /**
+     * Creates a ToriiQueryBuilder instance with historical flag set to true
+     * Used for querying historical events in the system
+     * 
+     * @param options - Optional query builder configuration
+     */
    constructor(options?: ToriiQueryBuilderOptions) {
        super({
            ...(defaultToriiOptions() as Query),
            ...options,
            historical: true,
        });
    }
}
packages/sdk/src/node/index.ts (2)

34-36: New module exports added

Several new modules are now exported:

  • worker.ts: likely for worker thread management
  • types.ts: SDK type definitions
  • models.ts: model management utilities

Ensure these new modules are documented with examples showing how they should be used.


401-415: Removed historical parameter from event query conversion

The historical parameter has been removed from the toriiEventMessagesQueryIntoHashedKeys method, but the method still uses a generic type parameter H (presumably for Historical). This could lead to confusion.

Since historical events support has been removed, consider updating the method signature for clarity:

-    toriiEventMessagesQueryIntoHashedKeys: async <H extends boolean>(
+    toriiEventMessagesQueryIntoHashedKeys: async (
         query: ToriiQueryBuilder<T>
-    ): Promise<[ToriiResponse<T, H>, torii.EntityKeysClause[]]> => {
+    ): Promise<[ToriiResponse<T, false>, torii.EntityKeysClause[]]> => {
         const q = query.build();

         const events = await client.getEventMessages(q);
         const parsedEvents = parseEntities(events.items);
         return [
-            parsedEvents as ToriiResponse<T, H>,
+            parsedEvents as ToriiResponse<T, false>,
             intoEntityKeysClause<T>(
                 q.clause,
                 parsedEvents as StandardizedQueryResult<T>
             ),
         ];
     },
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro (Legacy)

📥 Commits

Reviewing files that changed from the base of the PR and between ae00a22 and 6588556.

⛔ Files ignored due to path filters (5)
  • examples/example-vite-react-phaser-recs/src/dojo/generated/setup.ts is excluded by !**/generated/**
  • examples/example-vite-react-pwa-recs/src/dojo/generated/setup.ts is excluded by !**/generated/**
  • examples/example-vite-react-threejs-recs/src/dojo/generated/setup.ts is excluded by !**/generated/**
  • examples/example-vue-app-recs/src/dojo/generated/setup.ts is excluded by !**/generated/**
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (42)
  • .changeset/thirty-cheetahs-design.md (1 hunks)
  • .github/workflows/ci.yaml (1 hunks)
  • .github/workflows/release.yaml (6 hunks)
  • examples/example-node-worker/dojoConfig.ts (1 hunks)
  • examples/example-node-worker/env.ts (1 hunks)
  • examples/example-node-worker/main.ts (1 hunks)
  • examples/example-node-worker/package.json (1 hunks)
  • examples/example-node-worker/src/typescript/contracts.gen.ts (1 hunks)
  • examples/example-node-worker/src/typescript/models.gen.ts (1 hunks)
  • examples/example-node-worker/tsconfig.json (1 hunks)
  • examples/example-nodejs-bot/package.json (1 hunks)
  • examples/example-vite-react-app-recs/src/dojo/setup.ts (2 hunks)
  • examples/example-vite-react-sdk/src/historical-events.tsx (2 hunks)
  • package.json (1 hunks)
  • packages/core/package.json (1 hunks)
  • packages/react/package.json (2 hunks)
  • packages/sdk/package.json (3 hunks)
  • packages/sdk/src/__tests__/parseEntities.test.ts (7 hunks)
  • packages/sdk/src/__tests__/parseHistoricalEvents.test.ts (0 hunks)
  • packages/sdk/src/__tests__/state.test.ts (3 hunks)
  • packages/sdk/src/__tests__/toriiQueryBuilder.test.ts (6 hunks)
  • packages/sdk/src/__tests__/zustand.perf.test.ts (1 hunks)
  • packages/sdk/src/internal/errors.ts (1 hunks)
  • packages/sdk/src/internal/generateTypedData.ts (2 hunks)
  • packages/sdk/src/internal/merge.ts (1 hunks)
  • packages/sdk/src/internal/models.ts (1 hunks)
  • packages/sdk/src/internal/parseEntities.ts (4 hunks)
  • packages/sdk/src/internal/parseHistoricalEvents.ts (0 hunks)
  • packages/sdk/src/internal/toriiQueryBuilder.ts (6 hunks)
  • packages/sdk/src/internal/types.ts (5 hunks)
  • packages/sdk/src/node/index.ts (12 hunks)
  • packages/sdk/src/node/worker.ts (1 hunks)
  • packages/sdk/src/web/experimental/index.ts (4 hunks)
  • packages/sdk/src/web/index.ts (11 hunks)
  • packages/sdk/src/web/react/hooks/entities.ts (1 hunks)
  • packages/sdk/src/web/react/hooks/events.ts (3 hunks)
  • packages/sdk/src/web/react/hooks/hooks.ts (4 hunks)
  • packages/sdk/src/web/state/zustand.ts (1 hunks)
  • packages/state/src/recs/index.ts (6 hunks)
  • packages/torii-client/package.json (2 hunks)
  • packages/torii-wasm/dojo.c (1 hunks)
  • turbo.json (1 hunks)
💤 Files with no reviewable changes (2)
  • packages/sdk/src/internal/parseHistoricalEvents.ts
  • packages/sdk/src/tests/parseHistoricalEvents.test.ts
✅ Files skipped from review due to trivial changes (2)
  • packages/sdk/src/tests/state.test.ts
  • .changeset/thirty-cheetahs-design.md
🚧 Files skipped from review as they are similar to previous changes (25)
  • examples/example-node-worker/dojoConfig.ts
  • examples/example-nodejs-bot/package.json
  • packages/react/package.json
  • .github/workflows/ci.yaml
  • packages/torii-wasm/dojo.c
  • packages/torii-client/package.json
  • examples/example-node-worker/env.ts
  • packages/core/package.json
  • package.json
  • .github/workflows/release.yaml
  • examples/example-vite-react-app-recs/src/dojo/setup.ts
  • packages/sdk/src/tests/parseEntities.test.ts
  • turbo.json
  • packages/sdk/src/node/worker.ts
  • packages/sdk/src/internal/generateTypedData.ts
  • packages/sdk/package.json
  • packages/sdk/src/tests/toriiQueryBuilder.test.ts
  • examples/example-node-worker/tsconfig.json
  • packages/sdk/src/web/react/hooks/events.ts
  • packages/sdk/src/web/react/hooks/hooks.ts
  • packages/sdk/src/internal/types.ts
  • packages/sdk/src/internal/errors.ts
  • packages/sdk/src/web/react/hooks/entities.ts
  • packages/sdk/src/internal/models.ts
  • examples/example-node-worker/package.json
🧰 Additional context used
🧬 Code Graph Analysis (7)
packages/sdk/src/web/index.ts (5)
packages/sdk/src/internal/parseEntities.ts (1)
  • parseEntities (10-59)
packages/sdk/src/internal/convertClauseToEntityKeysClause.ts (1)
  • intoEntityKeysClause (4-36)
packages/sdk/src/internal/generateTypedData.ts (1)
  • generateTypedData (13-48)
packages/sdk/src/internal/errors.ts (1)
  • NO_ACCOUNT (3-3)
packages/sdk/src/internal/toriiQueryBuilder.ts (1)
  • ToriiQueryBuilder (24-143)
packages/sdk/src/internal/toriiQueryBuilder.ts (1)
packages/sdk/src/internal/types.ts (2)
  • ToriiQueryBuilder (557-557)
  • SchemaType (50-70)
packages/sdk/src/node/index.ts (6)
packages/sdk/src/internal/convertClauseToEntityKeysClause.ts (1)
  • intoEntityKeysClause (4-36)
packages/sdk/src/internal/parseEntities.ts (1)
  • parseEntities (10-59)
packages/sdk/src/internal/types.ts (2)
  • ToriiResponse (248-253)
  • ToriiQueryBuilder (557-557)
packages/sdk/src/internal/generateTypedData.ts (1)
  • generateTypedData (13-48)
packages/sdk/src/internal/errors.ts (2)
  • NO_SIGNER (1-1)
  • NO_IDENTITY (2-2)
packages/sdk/src/internal/toriiQueryBuilder.ts (1)
  • ToriiQueryBuilder (24-143)
examples/example-node-worker/src/typescript/contracts.gen.ts (2)
examples/example-node-worker/src/typescript/models.gen.ts (1)
  • direction (71-76)
packages/core/src/types/index.ts (1)
  • DojoCall (130-134)
examples/example-node-worker/src/typescript/models.gen.ts (1)
packages/sdk/src/internal/types.ts (1)
  • SchemaType (50-70)
examples/example-vite-react-sdk/src/historical-events.tsx (1)
packages/sdk/src/internal/toriiQueryBuilder.ts (1)
  • HistoricalToriiQueryBuilder (149-159)
packages/sdk/src/internal/merge.ts (3)
packages/sdk/src/web/queryBuilder.ts (1)
  • value (191-193)
examples/example-node-worker/src/typescript/models.gen.ts (1)
  • SchemaType (80-94)
packages/sdk/src/internal/types.ts (2)
  • SchemaType (50-70)
  • ParsedEntity (228-237)
🪛 Biome (1.9.4)
examples/example-node-worker/main.ts

[error] 176-176: This code is unreachable

... because this statement will return from the function beforehand

(lint/correctness/noUnreachable)

packages/sdk/src/internal/toriiQueryBuilder.ts

[error] 133-133: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

examples/example-node-worker/src/typescript/contracts.gen.ts

[error] 1-1: All these imports are only used as types.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Use import type.

(lint/style/useImportType)


[error] 2-2: Some named imports are only used as types.

This import is only used as a type.

This import is only used as a type.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

examples/example-node-worker/src/typescript/models.gen.ts

[error] 3-3: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

examples/example-vite-react-sdk/src/historical-events.tsx

[error] 1-5: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

packages/sdk/src/__tests__/zustand.perf.test.ts

[error] 131-133: Forbidden non-null assertion.

(lint/style/noNonNullAssertion)


[error] 131-133: Forbidden non-null assertion.

Unsafe fix: Replace with optional chain operator ?. This operator includes runtime checks, so it is safer than the compile-only non-null assertion operator

(lint/style/noNonNullAssertion)


[error] 132-132: The computed expression can be simplified without the use of a string literal.

Unsafe fix: Use a literal key instead.

(lint/complexity/useLiteralKeys)

packages/sdk/src/internal/merge.ts

[error] 76-76: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)


[error] 96-96: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)


[error] 133-133: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

packages/sdk/src/internal/parseEntities.ts

[error] 50-50: Do not use template literals if interpolation and special-character handling are not needed.

Unsafe fix: Replace with string literal

(lint/style/noUnusedTemplateLiteral)

packages/sdk/src/web/experimental/index.ts

[error] 30-30: This type annotation is trivially inferred from its initialization.

Safe fix: Remove the type annotation.

(lint/style/noInferrableTypes)


[error] 101-101: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

packages/sdk/src/web/state/zustand.ts

[error] 14-14: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

packages/state/src/recs/index.ts

[error] 185-185: Do not use template literals if interpolation and special-character handling are not needed.

Unsafe fix: Replace with string literal

(lint/style/noUnusedTemplateLiteral)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build
🔇 Additional comments (13)
examples/example-vite-react-sdk/src/historical-events.tsx (1)

4-4: Great improvement: Using specialized query builder

The switch from ToriiQueryBuilder to HistoricalToriiQueryBuilder makes the code more semantically clear, as it communicates the intent to query historical data directly through the class name.

Also applies to: 14-14

packages/sdk/src/web/experimental/index.ts (2)

24-24: Good refactor: Consistent client initialization and data handling

The switch to synchronous client initialization and consistent extraction of .items from client responses improves code consistency across the SDK.

Also applies to: 28-28, 31-32, 48-48, 92-92, 95-95


39-39: Property rename: dont_include_hashed_keys to no_hashed_keys

The query property name has been updated for better clarity and consistency.

Also applies to: 82-82

examples/example-node-worker/src/typescript/contracts.gen.ts (5)

7-13: LGTM! Clear implementation for move calldata

The function correctly constructs calldata for the move action with the direction parameter.


15-26: Good implementation with proper error handling

The function implementation follows best practices by including error logging and propagation.


28-34: LGTM! Clear implementation for spawn calldata

The function correctly constructs calldata for the spawn action with empty parameters.


36-47: Good implementation with proper error handling

The function implementation follows best practices by including error logging and propagation.


51-58: Clean API interface design

The return object provides a well-structured API for contract interactions, exposing both the action functions and the calldata builders.

packages/sdk/src/internal/parseEntities.ts (3)

10-15: Good refactoring to work with entity arrays

The function signature has been updated to accept an array of entities rather than a keyed object, which aligns with changes in the broader codebase.


17-20: Clear adaptation of entity extraction

The logic has been updated to correctly extract entity ID and data from the array structure.


90-93: Support for tuple type added

Adding support for the "tuple" type is a good enhancement to handle more complex data structures.

packages/state/src/recs/index.ts (1)

15-15: Type import updated to reflect new entity structure

The type import has been changed from Entities to Entity as ToriiEntity, correctly aligning with the shift to array-based entity representation.

packages/sdk/src/node/index.ts (1)

29-30: Improved error handling with neverthrow library

The addition of the neverthrow library for Result-based error handling is a good practice and improves error handling throughout the SDK.

Make sure to document this pattern in the SDK documentation so consumers understand how to properly handle errors from the SDK functions.

Comment on lines +123 to +160
export function deepMerge<T extends SchemaType>(
target: MergedModels<T>,
source: Partial<MergedModels<T>>
): MergedModels<T> {
if (isCairoOption(target) && isCairoOption(source)) {
return mergeCairoOption(target, source);
}
if (isCairoCustomEnum(target) && isCairoCustomEnum(source)) {
return mergeCairoCustomEnum(target, source);
}
const result = { ...target } as Record<string, any>;
for (const key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
if (
source[key] !== null &&
typeof source[key] === "object" &&
!Array.isArray(source[key])
) {
// If the property is an object in both source and target, recursively merge
if (
key in target &&
typeof target[key] === "object" &&
!Array.isArray(target[key])
) {
result[key] = deepMerge(target[key], source[key]);
} else {
// If the key doesn't exist in target or isn't an object, just assign
result[key] = source[key];
}
} else {
// For non-objects (primitives, arrays, null), just assign
result[key] = source[key];
}
}
}

return result;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Add unit tests for the deepMerge function

This is a critical utility function that handles complex merging logic for Cairo types. Ensure it has comprehensive test coverage for various edge cases.

Let's verify if there are existing tests for this function:


🏁 Script executed:

#!/bin/bash
# Check for tests related to the deepMerge function
rg "deepMerge" --type=ts -A 5 -B 5 "packages/sdk/src/__tests__" || echo "No tests found"

Length of output: 100


Add unit tests for deepMerge in packages/sdk/src/internal/merge.ts

No tests currently cover this function. Please add a new test suite (e.g. packages/sdk/src/internal/tests/merge.test.ts) that verifies:

  • Merging of primitive values (overwrites correctly)
  • Recursive merging of nested objects
  • Handling of arrays (should be overwritten, not deep-merged)
  • Treatment of null and undefined properties
  • Behavior with custom Cairo types (CairoOption, CairoCustomEnum)
🧰 Tools
🪛 Biome (1.9.4)

[error] 133-133: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

Comment on lines +132 to +133
positition: [[0, 0]],
},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Ensure field names in schema match interfaces

Fix the typo in the schema definition to match the corrected interface.

PositionCountValue: {
-	positition: [[0, 0]],
+	position: [[0, 0]],
},
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
positition: [[0, 0]],
},
PositionCountValue: {
position: [[0, 0]],
},

@MartianGreed MartianGreed force-pushed the chore/dojo1.5 branch 4 times, most recently from 4f22735 to 8fef149 Compare April 29, 2025 08:01
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Inline review comments failed to post. This is likely due to GitHub's limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 2

🔭 Outside diff range comments (2)
packages/state/src/recs/index.ts (1)

516-554: ⚠️ Potential issue

Inconsistent parameter type and usage in insertEntitiesInDB

The parameter type is changed to ToriiEntity[] (array), but the function still treats it as an object with Object.entries(entities). This mismatch will cause runtime errors.

 async function insertEntitiesInDB(
     db: IDBDatabase,
     entities: ToriiEntity[]
 ): Promise<void> {
     return new Promise((resolve, reject) => {
         const transaction = db.transaction(["entities"], "readwrite");
         const store = transaction.objectStore("entities");

         let completed = 0;
         let error: Error | null = null;

         // Handle transaction completion
         transaction.oncomplete = () => {
             if (error) {
                 reject(error);
             } else {
                 resolve();
             }
         };

         transaction.onerror = () => {
             reject(transaction.error);
         };

         // Store each entity
-        for (const [entityId, data] of Object.entries(entities)) {
+        for (const entity of entities) {
             const entityData = {
-                id: entityId,
+                id: entity.hashed_keys,
-                ...data,
+                ...entity.models,
             };

             const request = store.put(entityData);
             completed++;

             request.onerror = () => {
                 error = request.error;
             };
         }
     });
 }
packages/sdk/src/web/index.ts (1)

257-282: ⚠️ Potential issue

sendMessage should return Result.err, not throw

The method has been updated to use the neverthrow Result type for error handling, but inconsistently throws in the catch block instead of returning an error result.

This breaks the Result contract and forces callers to handle both the Result pattern and try/catch, which defeats the purpose of using neverthrow.

try {
    // Sign the typed data
    const signature: Signature = await account.signMessage(data);

    // Stringify typed data for publishing
    const dataString = JSON.stringify(data);
    // Publish the signed message
    return ok(
        await client.publishMessage(
            dataString,
            Array.isArray(signature)
                ? signature
                : [signature.r.toString(), signature.s.toString()]
        )
    );
} catch (error) {
    console.error("Failed to send message:", error);
-    throw error;
+    return err(String(error));
}
♻️ Duplicate comments (18)
packages/sdk/src/internal/merge.ts (1)

123-160: Add unit tests for the deepMerge function.

This is a critical utility function that handles complex merging logic for Cairo types. Currently, there are no tests covering its functionality.

Please add a new test suite (e.g. packages/sdk/src/internal/tests/merge.test.ts) that verifies:

  • Merging of primitive values (overwrites correctly)
  • Recursive merging of nested objects
  • Handling of arrays (should be overwritten, not deep-merged)
  • Treatment of null and undefined properties
  • Behavior with custom Cairo types (CairoOption, CairoCustomEnum)
🧰 Tools
🪛 Biome (1.9.4)

[error] 133-133: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

packages/sdk/src/web/experimental/index.ts (2)

30-32: Remove unused historical parameter

The historical parameter is declared but never used in the implementation.

-    getEvents: async (query: torii.Query, historical: boolean = false) => {
+    getEvents: async (query: torii.Query) => {
         const events = await client.getEventMessages(query);
         return parseEntities(events.items);
     },
🧰 Tools
🪛 Biome (1.9.4)

[error] 30-30: This type annotation is trivially inferred from its initialization.

Safe fix: Remove the type annotation.

(lint/style/noInferrableTypes)


101-106: Strengthen callback parameter types

For improved type safety, replace the any type on the entityData parameter with torii.Entity, which aligns with the parseEntities<T>(entities: torii.Entity[]) signature.

-                    (_: string, entityData: any) => {
+                    (_: string, entityData: torii.Entity) => {
🧰 Tools
🪛 Biome (1.9.4)

[error] 101-101: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

packages/sdk/src/internal/parseEntities.ts (1)

126-133: 🛠️ Refactor suggestion

Risk of numeric precision loss for u64 values

A 64-bit unsigned integer can exceed JavaScript's Number.MAX_SAFE_INTEGER (2^53-1), leading to precision loss. Consider parsing u64 with BigInt like the other large integer types.

 function parsePrimitive(value: torii.Ty): any {
     switch (value.type_name) {
-        case "u64":
-            return Number.parseInt(value.value as string, 16);
         case "i256":
         case "i128":
         case "u256":
         case "u128":
+        case "u64":
             return BigInt(value.value as string);
         default:
             return value.value;
     }
 }
packages/state/src/recs/index.ts (3)

246-246: Same length check issue in getEvents

-        if (Object.keys(entities.items).length === 0) {
+        if (entities.items.length === 0) {

189-189: ⚠️ Potential issue

Array length check uses incorrect method

Since entities.items is now an array, use .length directly instead of Object.keys().length.

-        if (Object.keys(entities.items).length < limit) {
+        if (entities.items.length < limit) {
             continueFetching = false;
         } else {

320-333: ⚠️ Potential issue

Infinite loop in getEntitiesQuery

Inside the while (continueFetching) loop you never issue a new client.getEntities call—fetchedEntities is fetched once outside the loop, so cursor advances but data doesn't refresh.

-    const fetchedEntities = await client.getEntities({
-        pagination: {
-            limit,
-            cursor,
-            direction: "Forward",
-            order_by: orderBy,
-        },
-        clause: clause || undefined,
-        no_hashed_keys: false,
-        models: entityModels,
-        historical,
-    });

 while (continueFetching) {
+    const fetchedEntities = await client.getEntities({
+        pagination: {
+            limit,
+            cursor,
+            direction: "Forward",
+            order_by: orderBy,
+        },
+        clause: clause || undefined,
+        no_hashed_keys: false,
+        models: entityModels,
+        historical,
+    });
+
     if (logging)
         console.log(
             `Fetched ${Object.keys(fetchedEntities.items).length} entities ${fetchedEntities.next_cursor}`
         );

     setEntities(fetchedEntities.items, components, logging);

-    if (Object.keys(fetchedEntities.items).length < limit) {
+    if (fetchedEntities.items.length < limit) {
         continueFetching = false;
     } else {
         cursor = fetchedEntities.next_cursor;
     }
 }
examples/example-node-worker/main.ts (4)

21-25: Wrong global identifier: use WebSocket, not Websocket

Browsers and Node.js polyfills expect global.WebSocket (uppercase 'S'). Using lowercase Websocket will leave libraries that expect the standard WebSocket identifier undefined.

// Those lines are require so that websocket works.
// @ts-ignore
-global.Websocket = w3cwebsocket;
+global.WebSocket = w3cwebsocket;
// @ts-ignore
global.WorkerGlobalScope = global;

125-133: initPositionFromEvent mutates the shared constant

Using const pc = defaultPositionCount creates a reference to the shared constant. Each call to this function will mutate the same object, creating cumulative side-effects for future callers.

function initPositionFromEvent(
    events: ParsedEntity<SchemaType>[]
): PositionCount {
-    const pc = defaultPositionCount;
+    const pc: PositionCount = { ...defaultPositionCount };
    for (const e of events) {
        const moved = e.models.dojo_starter.Moved;
        if (!moved) {
            continue;
        }
        pc[moved.direction] += 1;
    }
    return pc;
}

143-151: Potential data loss with data.pop()

Using data.pop() both mutates the array and only processes the last entity. If multiple entities arrive in the same batch, all but the last will be silently ignored.

async function onEntityUpdated({ data, error }) {
    if (error) {
        console.error(error);
        return;
    }

-    const entity = data.pop();
-    if (entity && entity.entityId !== "0x0") {
-        // do whatever you need here
-        const model = entity.models.dojo_starter;
-        if (model?.Moves) {
-            await publishOffchainPositionCount(model.Moves);
+    for (const entity of data) {
+        if (entity && entity.entityId !== "0x0") {
+            // do whatever you need here
+            const model = entity.models.dojo_starter;
+            if (model?.Moves) {
+                await publishOffchainPositionCount(model.Moves);
+            }
        }
    }
}

175-176: Unreachable code after the first return

The second return statement can never execute because the function will always exit at the first return.

-    return [];
-    return [sub];
+    return [sub];
🧰 Tools
🪛 Biome (1.9.4)

[error] 176-176: This code is unreachable

... because this statement will return from the function beforehand

(lint/correctness/noUnreachable)

examples/example-node-worker/src/typescript/models.gen.ts (2)

38-46: ⚠️ Potential issue

Fix typo in "positition" field name

There appears to be a typo in the positition field name (should be "position").

export interface PositionCount {
	identity: string;
-	positition: Array<[BigNumberish, BigNumberish]>;
+	position: Array<[BigNumberish, BigNumberish]>;
}

export interface PositionCountValue {
-	positition: Array<[BigNumberish, BigNumberish]>;
+	position: Array<[BigNumberish, BigNumberish]>;
}

129-133: ⚠️ Potential issue

Ensure field names in schema match interfaces

Fix the typo in the schema definition to match the corrected interface.

PositionCount: {
	identity: "",
-	positition: [[0, 0]],
+	position: [[0, 0]],
},
PositionCountValue: {
-	positition: [[0, 0]],
+	position: [[0, 0]],
},
packages/sdk/src/node/index.ts (5)

50-50: Client initialization change

Changed from torii.createClient to directly instantiating new torii.ToriiClient. This is a breaking change for anyone directly using the returned client.


64-64: Renamed query parameter for improved clarity

Parameter renamed from dont_include_hashed_keys to no_hashed_keys in both entity and event query subscriptions. This is a breaking change but improves naming consistency.

Also applies to: 119-119


73-74: Updated entity and event parsing to use items array

The entity and event parsing has been updated to handle arrays from the .items property rather than keyed objects. This change simplifies the data structure but is a breaking change for existing code.

Also applies to: 82-84, 141-143, 189-189, 204-204, 390-391


215-230: Modified generateTypedData signature

The method signature has changed:

  • Removed optional domain parameter
  • Added optional additionalTypes parameter
  • Domain is now always taken from options.domain

This is a breaking change for code that explicitly passed a domain parameter different from options.domain.


240-265: 🛠️ Refactor suggestion

Error handling inconsistency in sendMessage method

While the method now uses Result type from neverthrow for early returns, it still throws an error in the catch block instead of returning an error result.

Apply this fix to make error handling consistent:

    try {
        const td = new torii.TypedData(JSON.stringify(data)).encode(
            options.identity
        );

        const sig = options.signer.sign(td);

        const dataString = JSON.stringify(data);

        return ok(
            await client.publishMessage(dataString, [
                sig.r.toString(),
                sig.s.toString(),
            ])
        );
    } catch (error) {
        console.error("Failed to send message:", error);
-        throw error;
+        return err(`Failed to send message: ${error instanceof Error ? error.message : String(error)}`);
    }
🧹 Nitpick comments (9)
packages/sdk/src/internal/merge.ts (3)

76-76: Consider using a more specific type instead of any.

Using any reduces type safety and can lead to runtime errors.

-        const newEnumContent: Record<string, any> = {};
+        const newEnumContent: Record<string, unknown> = {};
🧰 Tools
🪛 Biome (1.9.4)

[error] 76-76: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)


96-96: Replace any with a more specific type.

Similarly to the previous occurrence, using a more specific type would improve type safety.

-        const newEnumContent: Record<string, any> = {};
+        const newEnumContent: Record<string, unknown> = {};
🧰 Tools
🪛 Biome (1.9.4)

[error] 96-96: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)


133-133: Replace any with unknown for better type safety.

The any type bypasses TypeScript's type checking, while unknown forces proper type checking before use.

-    const result = { ...target } as Record<string, any>;
+    const result = { ...target } as Record<string, unknown>;
🧰 Tools
🪛 Biome (1.9.4)

[error] 133-133: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

packages/sdk/src/web/state/zustand.ts (1)

14-14: Use import type for type imports.

MergedModels is only used as a type, so it should be imported with import type to ensure they are removed by compilers.

-import { deepMerge, MergedModels } from "../../internal/merge";
+import { deepMerge } from "../../internal/merge";
+import type { MergedModels } from "../../internal/merge";
🧰 Tools
🪛 Biome (1.9.4)

[error] 14-14: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

examples/example-vite-react-sdk/src/historical-events.tsx (1)

1-5: Use import type for type-only imports.

ParsedEntity is only used as a type, so it should be imported with import type.

 import {
     KeysClause,
-    ParsedEntity,
     HistoricalToriiQueryBuilder,
 } from "@dojoengine/sdk";
+import type { ParsedEntity } from "@dojoengine/sdk";
🧰 Tools
🪛 Biome (1.9.4)

[error] 1-5: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

packages/sdk/src/__tests__/zustand.perf.test.ts (1)

131-133: Consider using optional chaining instead of non-null assertions.

The current implementation uses non-null assertions (!) which might cause runtime errors if the values are null or undefined.

-                        draft.entities[
-                            "entity500"
-                        ].models.world!.item!.durability = 75;
+                        draft.entities["entity500"].models.world?.item?.durability = 75;
🧰 Tools
🪛 Biome (1.9.4)

[error] 131-133: Forbidden non-null assertion.

(lint/style/noNonNullAssertion)


[error] 131-133: Forbidden non-null assertion.

Unsafe fix: Replace with optional chain operator ?. This operator includes runtime checks, so it is safer than the compile-only non-null assertion operator

(lint/style/noNonNullAssertion)


[error] 132-132: The computed expression can be simplified without the use of a string literal.

Unsafe fix: Use a literal key instead.

(lint/complexity/useLiteralKeys)

examples/example-node-worker/src/typescript/contracts.gen.ts (1)

1-2: Use import type for better bundle optimization

The imports are only used for type information, not at runtime. Using import type helps optimize bundle size and clarifies intent.

-import { DojoProvider, DojoCall } from "@dojoengine/core";
-import { Account, AccountInterface, BigNumberish, CairoOption, CairoCustomEnum, ByteArray } from "starknet";
+import type { DojoProvider, DojoCall } from "@dojoengine/core";
+import { Account, type AccountInterface, type BigNumberish, type CairoOption, type CairoCustomEnum, type ByteArray } from "starknet";
 import * as models from "./models.gen";
🧰 Tools
🪛 Biome (1.9.4)

[error] 1-1: All these imports are only used as types.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Use import type.

(lint/style/useImportType)


[error] 2-2: Some named imports are only used as types.

This import is only used as a type.

This import is only used as a type.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

examples/example-node-worker/src/typescript/models.gen.ts (1)

3-3: Use import type for type-only imports

Since CairoCustomEnum, CairoOption, CairoOptionVariant, and BigNumberish are only used as types in this file, you should use the import type syntax to ensure they're removed during compilation.

-import { CairoCustomEnum, CairoOption, CairoOptionVariant, BigNumberish } from 'starknet';
+import type { CairoCustomEnum, CairoOption, CairoOptionVariant, BigNumberish } from 'starknet';
🧰 Tools
🪛 Biome (1.9.4)

[error] 3-3: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

packages/sdk/src/internal/toriiQueryBuilder.ts (1)

133-133: Remove any type from static withPagination method

The static method uses Record<string, Record<string, any>> which includes any. This violates type safety principles and can lead to runtime errors.

-static withPagination<T extends Record<string, Record<string, any>>>(
+static withPagination<T extends SchemaType>(
    cursor: string,
    limit: number,
    direction: PaginationDirection
): ToriiQueryBuilder<T> {
🧰 Tools
🪛 Biome (1.9.4)

[error] 133-133: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

🛑 Comments failed to post (2)
packages/sdk/src/internal/toriiQueryBuilder.ts (1)

39-45: 💡 Verification agent

🧩 Analysis chain

Enhance deprecation notice for withOffset

The withOffset method has been converted to a no-op that returns this without setting any properties. While it's marked as deprecated, this silent change could break existing code that relies on offset-based pagination.

Provide a more descriptive deprecation notice in the JSDoc explaining the migration path to cursor-based pagination:

/**
 * Set the offset for pagination
- * @deprecated Use `withCursor` instead
+ * @deprecated Offset-based pagination is no longer supported. Use `withCursor` instead.
+ * To migrate from offset to cursor pagination:
+ * 1. For initial page, use `withCursor(undefined)` instead of `withOffset(0)`
+ * 2. For subsequent pages, use the `next_cursor` value returned from the previous query
 */
withOffset(): ToriiQueryBuilder<T> {
    return this;
}

Also consider adding a compatibility helper method to assist with migration:

/**
 * Compatibility helper for migrating from offset-based pagination
 * @deprecated Use `withCursor` directly when possible
 */
withOffsetCompat(offset: number): ToriiQueryBuilder<T> {
    // If offset is 0, leave cursor undefined to start from the beginning
    if (offset > 0) {
        console.warn('Offset-based pagination is deprecated. Using approximation for cursor.');
        // Apply some cursor approximation logic here if possible
    }
    return this;
}

Let me search for usages of withOffset to assess impact:


🏁 Script executed:

#!/bin/bash
# Check for usage of withOffset in the codebase
rg "\.withOffset\(" --type=ts

Length of output: 31


🏁 Script executed:

#!/bin/bash
# Search for all usages of withOffset across the repo
rg '\.withOffset\(' -n .

# Inspect the withOffset declaration in the SDK internals
rg 'withOffset' -n packages/sdk/src/internal/toriiQueryBuilder.ts

Length of output: 136


Improve deprecation notice for withOffset

No internal usages of withOffset were detected, so this change won’t break existing SDK calls—but it’s still part of the public API, and we should help consumers migrate.

Please update the JSDoc in packages/sdk/src/internal/toriiQueryBuilder.ts to something like:

    /**
     * Set the offset for pagination
-    * @deprecated Use `withCursor` instead
+    * @deprecated Offset-based pagination is no longer supported. Use `withCursor` instead.
+    * To migrate:
+    * 1. For the first page, replace `withOffset(0)` with `withCursor(undefined)`.
+    * 2. For subsequent pages, pass the `next_cursor` from the previous response.
     */
    withOffset(): ToriiQueryBuilder<T> {
        return this;
    }

Optionally, add a helper for smoother migration:

/**
 * Compatibility helper for migrating from offset-based pagination.
 * @deprecated Use `withCursor` directly when possible.
 */
withOffsetCompat(offset: number): ToriiQueryBuilder<T> {
  if (offset > 0) {
    console.warn(
      'Offset-based pagination is deprecated. Using approximate cursor fallback.'
    );
    // TODO: implement fallback or document limitations
  }
  return this;
}
  • File: packages/sdk/src/internal/toriiQueryBuilder.ts
  • Lines: 39–45
packages/sdk/src/node/index.ts (1)

401-407: 💡 Verification agent

🧩 Analysis chain

Simplified historical event handling

The toriiEventMessagesQueryIntoHashedKeys method has been simplified to work with the updated event parsing structure. The historical parameter is still in the type signature but no longer affects implementation behavior.

Check if any documentation or migration guides need to be updated to reflect this change:


🏁 Script executed:

#!/bin/bash
# Search for uses of the historical parameter in code or docs
rg -A 3 -B 3 "historical\s*[:=]" --type tsx --type ts

Length of output: 83


🏁 Script executed:

#!/bin/bash
# Search for 'historical' references in code and documentation
rg -n "historical" --glob "*.{ts,tsx,md}"

Length of output: 6473


🏁 Script executed:

#!/bin/bash
# Display lines 330-430 in packages/sdk/src/node/index.ts for context
sed -n '330,430p' packages/sdk/src/node/index.ts

Length of output: 3280


Update documentation for deprecated historical flag

The SDK helper no longer acts on the historical parameter even though it remains in the type signature. We should align all docs and JSDoc with this simplification:

• packages/sdk/src/node/index.ts
– Remove the obsolete @param {boolean} historical from updateEventMessageSubscription’s JSDoc.
– In toriiEventMessagesQueryIntoHashedKeys, correct the @returns annotation to use the generic H (not hard-coded false) and drop any description implying historical has behavioral effect.

• README.md
– Update or remove the “If you want to index historical events” section so users don’t expect the helper to change behavior based on a historical flag.

• CHANGELOG.md (and any Migration guide)
– Add a note that historical is now a no-op in event‐query helpers and point to query.historical for clarity.

@MartianGreed MartianGreed force-pushed the chore/dojo1.5 branch 8 times, most recently from 8347ca5 to a0ed096 Compare April 29, 2025 08:28
@MartianGreed MartianGreed force-pushed the chore/dojo1.5 branch 3 times, most recently from dd6f140 to b104036 Compare April 29, 2025 09:47
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🔭 Outside diff range comments (2)
packages/sdk/src/web/index.ts (1)

279-282: 🛠️ Refactor suggestion

sendMessage should return Result.err, not throw

Inside the catch we throw, breaking the Result contract and forcing callers to juggle two error channels.

catch (error) {
    console.error("Failed to send message:", error);
-    throw error;
+    return err(String(error));
}

This ensures consistent error handling using the Result type pattern throughout the codebase.

packages/sdk/src/node/index.ts (1)

266-269: ⚠️ Potential issue

Inconsistent error handling pattern.

While Result types from neverthrow are used to handle errors in the initial checks, the function still throws errors in the catch block instead of returning an err Result. This inconsistency could confuse developers and make error handling more difficult.

Apply this diff to make error handling consistent:

    catch (error) {
        console.error("Failed to send message:", error);
-        throw error;
+        return err(`Failed to send message: ${error instanceof Error ? error.message : String(error)}`);
    }
♻️ Duplicate comments (20)
packages/sdk/src/internal/merge.ts (1)

123-160: Add unit tests for the deepMerge function.

This function handles complex merging logic but lacks test coverage. Comprehensive tests should be added to verify its behavior with different Cairo types and nested objects.

Also, consider improving type safety by replacing any with a more specific type:

-    const result = { ...target } as Record<string, any>;
+    const result = { ...target } as Record<string, unknown>;
🧰 Tools
🪛 Biome (1.9.4)

[error] 133-133: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

packages/sdk/src/web/experimental/index.ts (1)

95-95: 🛠️ Refactor suggestion

Still using any type for entity data

A previous review comment suggested replacing the any type with torii.Entity for improved type safety, but this hasn't been addressed.

-                    (_: string, entityData: any) => {
+                    (_: string, entityData: torii.Entity) => {
🧰 Tools
🪛 Biome (1.9.4)

[error] 95-95: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

packages/sdk/src/internal/parseEntities.ts (1)

127-133: ⚠️ Potential issue

Inconsistent handling of integer types

The code treats u64 as a regular number (using Number.parseInt), while handling larger integer types (i256, i128, u256, u128) as BigInt. Since a u64 can also exceed JavaScript's safe integer range, it should also use BigInt for consistency and to prevent precision loss.

 function parsePrimitive(value: torii.Ty): any {
   switch (value.type_name) {
     case "u64":
-      return Number.parseInt(value.value as string, 16);
+      return BigInt(value.value as string);
     case "i256":
     case "i128":
     case "u256":
     case "u128":
       return BigInt(value.value as string);
     default:
       return value.value;
   }
 }
packages/state/src/recs/index.ts (3)

242-246: ⚠️ Potential issue

Event handling inconsistency with array vs object

Similar to the entity handling, the code expects entities.items to be an array but uses Object.keys(entities.items).length to check the length.

-if (logging) console.log("entities", entities.items);
+if (logging) console.log("events", entities.items);

 setEntities(entities.items, components, logging);

-if (Object.keys(entities.items).length === 0) {
+if (entities.items.length === 0) {

516-519: ⚠️ Potential issue

Updated parameter type but function implementation not updated

The insertEntitiesInDB function now expects an array of ToriiEntity objects but still processes them as a keyed object with Object.entries(entities) in line 541. This will lead to unexpected behavior.

 async function insertEntitiesInDB(
     db: IDBDatabase,
     entities: ToriiEntity[]
 ): Promise<void> {
     return new Promise((resolve, reject) => {
         const transaction = db.transaction(["entities"], "readwrite");
         const store = transaction.objectStore("entities");

         let completed = 0;
         let error: Error | null = null;

         // Handle transaction completion
         transaction.oncomplete = () => {
             if (error) {
                 reject(error);
             } else {
                 resolve();
             }
         };

         transaction.onerror = () => {
             reject(transaction.error);
         };

         // Store each entity
-        for (const [entityId, data] of Object.entries(entities)) {
+        for (const entity of entities) {
             const entityData = {
-                id: entityId,
+                id: entity.hashed_keys.join('.'),
-                ...data,
+                ...entity.models,
             };

             const request = store.put(entityData);
             completed++;

             request.onerror = () => {
                 error = request.error;
             };
         }
     });
 }

307-318: ⚠️ Potential issue

Updated entity query parameters in getEntitiesQuery

The parameters for getEntitiesQuery have been updated to use cursor-based pagination, but there's a critical issue: the fetchedEntities variable is defined outside the loop but never updated inside it.

This is likely to cause an infinite loop or incorrect behavior, as the cursor updates but the entity data doesn't refresh. Move the fetch inside the loop:

-const fetchedEntities = await client.getEntities({
-    pagination: {
-        limit,
-        cursor,
-        direction: "Forward",
-        order_by: orderBy,
-    },
-    clause: clause || undefined,
-    no_hashed_keys: false,
-    models: entityModels,
-    historical,
-});

 while (continueFetching) {
+    const fetchedEntities = await client.getEntities({
+        pagination: {
+            limit,
+            cursor,
+            direction: "Forward",
+            order_by: orderBy,
+        },
+        clause: clause || undefined,
+        no_hashed_keys: false,
+        models: entityModels,
+        historical,
+    });
+    
     if (logging)
         console.log(
             `Fetched ${Object.keys(fetchedEntities.items).length} entities ${fetchedEntities.next_cursor}`
         );

     setEntities(fetchedEntities.items, components, logging);

     if (Object.keys(fetchedEntities.items).length < limit) {
         continueFetching = false;
     } else {
         cursor = fetchedEntities.next_cursor;
     }
 }
examples/example-node-worker/main.ts (4)

21-25: Wrong global identifier: use WebSocket, not Websocket

Browsers (and most polyfills) expose global.WebSocket (uppercase S).
Assigning to Websocket leaves libraries that expect WebSocket undefined.

// @ts-ignore
-global.Websocket = w3cwebsocket;
+global.WebSocket = w3cwebsocket;

125-133: initPositionFromEvent mutates the shared constant

pc is an alias of defaultPositionCount, so every call mutates the constant and all future callers see cumulative side-effects.

-const pc = defaultPositionCount;
+const pc: PositionCount = { ...defaultPositionCount };

This also prevents accidental modification of the default object elsewhere.


137-151: Potential data loss with data.pop()

data is already the argument supplied by the subscription.
Calling pop() both mutates the array and drops all but the last entity; if multiple entities arrive in the same batch they are silently ignored.

Iterate instead:

-        const entity = data.pop();
-        if (entity && entity.entityId !== "0x0") {
-            // do whatever you need here
-            const model = entity.models.dojo_starter;
-            if (model?.Moves) {
-                await publishOffchainPositionCount(model.Moves);
-            }
-        }
+        for (const entity of data) {
+            if (entity && entity.entityId !== "0x0") {
+                // do whatever you need here
+                const model = entity.models.dojo_starter;
+                if (model?.Moves) {
+                    await publishOffchainPositionCount(model.Moves);
+                }
+            }
+        }

175-176: Unreachable code after the first return

The second return [sub]; can never execute—Biome rightfully flags it.

-    return [];
-    return [sub];
+    return [sub];
🧰 Tools
🪛 Biome (1.9.4)

[error] 176-176: This code is unreachable

... because this statement will return from the function beforehand

(lint/correctness/noUnreachable)

examples/example-node-worker/src/typescript/models.gen.ts (2)

38-46: Fix typo in "positition" field name

There appears to be a typo in the positition field name (should be "position").

export interface PositionCount {
	identity: string;
-	positition: Array<[BigNumberish, BigNumberish]>;
+	position: Array<[BigNumberish, BigNumberish]>;
}

export interface PositionCountValue {
-	positition: Array<[BigNumberish, BigNumberish]>;
+	position: Array<[BigNumberish, BigNumberish]>;
}

Since this affects the interface structure and is reflected in the schema initialization, this typo should be fixed in the generator if possible.


129-133: Ensure field names in schema match interfaces

Fix the typo in the schema definition to match the corrected interface.

PositionCount: {
	identity: "",
-	positition: [[0, 0]],
+	position: [[0, 0]],
},
PositionCountValue: {
-	positition: [[0, 0]],
+	position: [[0, 0]],
},
packages/sdk/src/node/index.ts (8)

50-50: Client initialization change.

Changed from torii.createClient to directly instantiating new torii.ToriiClient. This is a breaking change for anyone directly using the returned client.

Ensure this change is documented in migration guides and potentially consider providing a compatibility layer if feasible.


64-65: Renamed query parameter for improved clarity.

Parameter renamed from dont_include_hashed_keys to no_hashed_keys in entity query subscriptions. This is a breaking change but improves naming consistency.

Ensure this change is documented in migration guides and update all examples to use the new parameter name.


72-74: Updated entity parsing to use items array.

The entity parsing has been updated to handle arrays from the .items property rather than keyed objects. This change simplifies the data structure but is a breaking change for existing code.

Ensure this change is documented in migration guides. Make sure that all examples and documentation are updated to reflect this new data structure.


119-120: Renamed query parameter for improved clarity.

Parameter renamed from dont_include_hashed_keys to no_hashed_keys in event query subscriptions. This is a breaking change but improves naming consistency.

Ensure this change is documented in migration guides and update all examples to use the new parameter name.


127-130: Updated event parsing to use items array.

Similar to the entity parsing changes, event parsing now uses arrays from the .items property. This aligns event and entity parsing approaches for better consistency.

Update all documentation and examples to reflect this structural change in event data handling.


138-143: Updated event parsing to use items array.

Similar to the entity parsing changes, event parsing now uses arrays from the .items property. This aligns event and entity parsing approaches for better consistency.

Update all documentation and examples to reflect this structural change in event data handling.


219-230: Modified generateTypedData signature.

The method signature has changed:

  • Removed optional domain parameter
  • Added optional additionalTypes parameter
  • Domain is now always taken from options.domain

This is a breaking change for code that explicitly passed a domain parameter different from options.domain.

Ensure this change is documented in migration guides and consider if there are cases where passing a different domain was useful functionality.


242-265: 🛠️ Refactor suggestion

Improved error handling in sendMessage method.

The sendMessage method now:

  1. Checks for the presence of signer and identity before attempting to send
  2. Returns a Result type (from neverthrow) with ok/err instead of throwing exceptions
  3. No longer requires an explicit Account parameter, relying on options.signer and options.identity

This is a breaking change for code that catches exceptions from this method, but improves error handling overall.

However, there's still inconsistency in error handling. The function uses Result for initial checks but throws an error in the catch block:

    try {
        const td = new torii.TypedData(JSON.stringify(data)).encode(
            options.identity
        );

        const sig = options.signer.sign(td);

        const dataString = JSON.stringify(data);

        return ok(
            await client.publishMessage(dataString, [
                sig.r.toString(),
                sig.s.toString(),
            ])
        );
    } catch (error) {
        console.error("Failed to send message:", error);
-        throw error;
+        return err(`Failed to send message: ${error instanceof Error ? error.message : String(error)}`);
    }
🧹 Nitpick comments (13)
packages/sdk/src/internal/merge.ts (2)

19-46: Improve type safety in mergeCairoOption function.

The function correctly handles the merging logic for CairoOption instances, but it contains multiple as unknown as MergedModels<T> type assertions which are unsafe and could be improved.

Consider refactoring to make better use of generics to avoid these type assertions:

-export function mergeCairoOption<T extends SchemaType>(
-    target: MergedModels<T>,
-    source: Partial<MergedModels<T>>
-): MergedModels<T> {
+export function mergeCairoOption<T extends SchemaType, V>(
+    target: CairoOption<V>,
+    source: CairoOption<V>
+): CairoOption<V> {
    // If source is Some, prefer source's value
-    if (source instanceof CairoOption && source.isSome()) {
+    if (source.isSome()) {
        return new CairoOption(
            CairoOptionVariant.Some,
            source.unwrap()
-        ) as unknown as MergedModels<T>;
+        );
    }

    // If source is None or undefined, keep target
-    if (target instanceof CairoOption) {
-        if (target.isSome()) {
-            return new CairoOption(
-                CairoOptionVariant.Some,
-                target.unwrap()
-            ) as unknown as MergedModels<T>;
-        }
-        return new CairoOption(
-            CairoOptionVariant.None
-        ) as unknown as MergedModels<T>;
+    if (target.isSome()) {
+        return new CairoOption(
+            CairoOptionVariant.Some,
+            target.unwrap()
+        );
    }
+    return new CairoOption(CairoOptionVariant.None);

    // This should not happen if both are CairoOption instances
-    return target as unknown as MergedModels<T>;
}

Then update the calling code to handle the type conversion.


63-113: Improve type safety and remove usage of any in mergeCairoCustomEnum.

While the function correctly handles merging CairoCustomEnum instances, it uses any type in the Record definitions which could lead to type safety issues.

Consider using a more specific type instead of any:

-        const newEnumContent: Record<string, any> = {};
+        const newEnumContent: Record<string, unknown> = {};

Make the same change at line 96.

🧰 Tools
🪛 Biome (1.9.4)

[error] 76-76: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)


[error] 96-96: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

packages/sdk/src/web/state/zustand.ts (1)

14-14: Good refactoring: Import utilities from centralized module.

Importing deepMerge and MergedModels from the centralized module improves code maintainability and reduces duplication. Consider using import type for MergedModels since it's only used as a type.

-import { deepMerge, MergedModels } from "../../internal/merge";
+import { deepMerge } from "../../internal/merge";
+import type { MergedModels } from "../../internal/merge";
🧰 Tools
🪛 Biome (1.9.4)

[error] 14-14: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

examples/example-vite-react-sdk/src/historical-events.tsx (1)

1-5: Update import with type annotations.

Since ParsedEntity is only used as a type, consider using import type to improve build optimization.

 import {
     KeysClause,
-    ParsedEntity,
     HistoricalToriiQueryBuilder,
 } from "@dojoengine/sdk";
+import type { ParsedEntity } from "@dojoengine/sdk";
🧰 Tools
🪛 Biome (1.9.4)

[error] 1-5: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

examples/example-node-worker/src/typescript/contracts.gen.ts (1)

1-2: Use import type for type-only imports

The imports are only used as type annotations. Using import type ensures they're removed during compilation, improving bundle size and avoiding unnecessary module loading.

-import { DojoProvider, DojoCall } from "@dojoengine/core";
-import { Account, AccountInterface, BigNumberish, CairoOption, CairoCustomEnum, ByteArray } from "starknet";
+import type { DojoProvider, DojoCall } from "@dojoengine/core";
+import type { Account, AccountInterface, CairoCustomEnum } from "starknet";
import * as models from "./models.gen";
🧰 Tools
🪛 Biome (1.9.4)

[error] 1-1: All these imports are only used as types.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Use import type.

(lint/style/useImportType)


[error] 2-2: Some named imports are only used as types.

This import is only used as a type.

This import is only used as a type.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

packages/state/src/recs/index.ts (4)

165-165: Commented out timestamp caching code

The timestamp caching functionality appears to be disabled but not completely removed. Consider removing it entirely if it's no longer needed or documenting why it's being kept for future reference.


323-323: Array length check uses inefficient method

The code is using Object.keys(fetchedEntities.items).length instead of fetchedEntities.items.length to get the array length.

-                `Fetched ${Object.keys(fetchedEntities.items).length} entities ${fetchedEntities.next_cursor}`
+                `Fetched ${fetchedEntities.items.length} entities ${fetchedEntities.next_cursor}`

326-328: Array length check uses inefficient method

Similar to previous instances, the code uses Object.keys(...).length instead of .length for an array.

-if (Object.keys(fetchedEntities.items).length < limit) {
+if (fetchedEntities.items.length < limit) {

512-514: Commented out code should be removed

The getCache function is commented out but not removed. If it's no longer needed, remove it completely to improve code clarity.

-// const getCache = (timestampCacheKey: string) => {
-//     return Number(localStorage.getItem(timestampCacheKey) || 0);
-// };
examples/example-node-worker/src/typescript/models.gen.ts (1)

3-3: Use import type for type-only imports

-import { CairoCustomEnum, CairoOption, CairoOptionVariant, BigNumberish } from 'starknet';
+import type { CairoCustomEnum, CairoOption, CairoOptionVariant, BigNumberish } from 'starknet';

This helps the compiler optimize the code by excluding imports that aren't needed at runtime.

🧰 Tools
🪛 Biome (1.9.4)

[error] 3-3: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

packages/sdk/src/web/index.ts (1)

274-277: Convert bigint signatures to strings

When signature is an array of bigints they're forwarded as-is; publishMessage expects string[].

return ok(
    await client.publishMessage(
        dataString,
-        Array.isArray(signature)
-            ? signature
-            : [signature.r.toString(), signature.s.toString()]
+        Array.isArray(signature)
+            ? signature.map((x) => x.toString())
+            : [signature.r.toString(), signature.s.toString()]
    )
);

This ensures the signature is properly converted to strings regardless of the input format.

packages/sdk/src/internal/toriiQueryBuilder.ts (1)

133-133: Avoid any in generic type parameter

Using any in type parameters disables important type checking rules.

-static withPagination<T extends Record<string, Record<string, any>>>(
+static withPagination<T extends SchemaType>(

This makes the method type-safe and consistent with the rest of the class implementation.

🧰 Tools
🪛 Biome (1.9.4)

[error] 133-133: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

packages/sdk/src/__tests__/zustand.perf.test.ts (1)

131-133: Consider optional chaining instead of non-null assertions for safer access.

The code uses non-null assertions (!) twice, which can lead to runtime errors if the properties are unexpectedly null or undefined. Consider using optional chaining for safer property access.

-draft.entities[
-    "entity500"
-].models.world!.item!.durability = 75;
+draft.entities["entity500"].models.world?.item?.durability = 75;

Additionally, note that the static analyzer suggests using a literal key instead of a string literal for "entity500", but this is primarily a style preference and both approaches are valid.

🧰 Tools
🪛 Biome (1.9.4)

[error] 131-133: Forbidden non-null assertion.

(lint/style/noNonNullAssertion)


[error] 131-133: Forbidden non-null assertion.

Unsafe fix: Replace with optional chain operator ?. This operator includes runtime checks, so it is safer than the compile-only non-null assertion operator

(lint/style/noNonNullAssertion)


[error] 132-132: The computed expression can be simplified without the use of a string literal.

Unsafe fix: Use a literal key instead.

(lint/complexity/useLiteralKeys)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro (Legacy)

📥 Commits

Reviewing files that changed from the base of the PR and between 49f5039 and b104036.

⛔ Files ignored due to path filters (5)
  • examples/example-vite-react-phaser-recs/src/dojo/generated/setup.ts is excluded by !**/generated/**
  • examples/example-vite-react-pwa-recs/src/dojo/generated/setup.ts is excluded by !**/generated/**
  • examples/example-vite-react-threejs-recs/src/dojo/generated/setup.ts is excluded by !**/generated/**
  • examples/example-vue-app-recs/src/dojo/generated/setup.ts is excluded by !**/generated/**
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (42)
  • .changeset/thirty-cheetahs-design.md (1 hunks)
  • .github/workflows/ci.yaml (1 hunks)
  • .github/workflows/release.yaml (6 hunks)
  • examples/example-node-worker/dojoConfig.ts (1 hunks)
  • examples/example-node-worker/env.ts (1 hunks)
  • examples/example-node-worker/main.ts (1 hunks)
  • examples/example-node-worker/package.json (1 hunks)
  • examples/example-node-worker/src/typescript/contracts.gen.ts (1 hunks)
  • examples/example-node-worker/src/typescript/models.gen.ts (1 hunks)
  • examples/example-node-worker/tsconfig.json (1 hunks)
  • examples/example-nodejs-bot/package.json (1 hunks)
  • examples/example-vite-react-app-recs/src/dojo/setup.ts (2 hunks)
  • examples/example-vite-react-sdk/src/historical-events.tsx (2 hunks)
  • package.json (1 hunks)
  • packages/core/package.json (1 hunks)
  • packages/react/package.json (2 hunks)
  • packages/sdk/package.json (3 hunks)
  • packages/sdk/src/__tests__/parseEntities.test.ts (7 hunks)
  • packages/sdk/src/__tests__/parseHistoricalEvents.test.ts (0 hunks)
  • packages/sdk/src/__tests__/state.test.ts (3 hunks)
  • packages/sdk/src/__tests__/toriiQueryBuilder.test.ts (6 hunks)
  • packages/sdk/src/__tests__/zustand.perf.test.ts (1 hunks)
  • packages/sdk/src/internal/errors.ts (1 hunks)
  • packages/sdk/src/internal/generateTypedData.ts (2 hunks)
  • packages/sdk/src/internal/merge.ts (1 hunks)
  • packages/sdk/src/internal/models.ts (1 hunks)
  • packages/sdk/src/internal/parseEntities.ts (4 hunks)
  • packages/sdk/src/internal/parseHistoricalEvents.ts (0 hunks)
  • packages/sdk/src/internal/toriiQueryBuilder.ts (6 hunks)
  • packages/sdk/src/internal/types.ts (5 hunks)
  • packages/sdk/src/node/index.ts (12 hunks)
  • packages/sdk/src/node/worker.ts (1 hunks)
  • packages/sdk/src/web/experimental/index.ts (3 hunks)
  • packages/sdk/src/web/index.ts (11 hunks)
  • packages/sdk/src/web/react/hooks/entities.ts (1 hunks)
  • packages/sdk/src/web/react/hooks/events.ts (3 hunks)
  • packages/sdk/src/web/react/hooks/hooks.ts (4 hunks)
  • packages/sdk/src/web/state/zustand.ts (1 hunks)
  • packages/state/src/recs/index.ts (6 hunks)
  • packages/torii-client/package.json (2 hunks)
  • packages/torii-wasm/dojo.c (1 hunks)
  • turbo.json (1 hunks)
💤 Files with no reviewable changes (2)
  • packages/sdk/src/tests/parseHistoricalEvents.test.ts
  • packages/sdk/src/internal/parseHistoricalEvents.ts
✅ Files skipped from review due to trivial changes (1)
  • .changeset/thirty-cheetahs-design.md
🚧 Files skipped from review as they are similar to previous changes (26)
  • packages/sdk/src/internal/errors.ts
  • examples/example-nodejs-bot/package.json
  • examples/example-node-worker/dojoConfig.ts
  • examples/example-node-worker/env.ts
  • examples/example-vite-react-app-recs/src/dojo/setup.ts
  • packages/react/package.json
  • packages/torii-wasm/dojo.c
  • packages/core/package.json
  • packages/sdk/src/web/react/hooks/hooks.ts
  • packages/sdk/src/tests/state.test.ts
  • .github/workflows/ci.yaml
  • examples/example-node-worker/tsconfig.json
  • packages/torii-client/package.json
  • package.json
  • packages/sdk/package.json
  • .github/workflows/release.yaml
  • packages/sdk/src/web/react/hooks/entities.ts
  • packages/sdk/src/tests/parseEntities.test.ts
  • examples/example-node-worker/package.json
  • packages/sdk/src/internal/generateTypedData.ts
  • packages/sdk/src/web/react/hooks/events.ts
  • turbo.json
  • packages/sdk/src/tests/toriiQueryBuilder.test.ts
  • packages/sdk/src/internal/types.ts
  • packages/sdk/src/node/worker.ts
  • packages/sdk/src/internal/models.ts
🧰 Additional context used
🧬 Code Graph Analysis (7)
packages/sdk/src/web/index.ts (5)
packages/sdk/src/internal/parseEntities.ts (1)
  • parseEntities (10-59)
packages/sdk/src/internal/convertClauseToEntityKeysClause.ts (1)
  • intoEntityKeysClause (4-36)
packages/sdk/src/internal/generateTypedData.ts (1)
  • generateTypedData (13-48)
packages/sdk/src/internal/errors.ts (1)
  • NO_ACCOUNT (3-3)
packages/sdk/src/internal/toriiQueryBuilder.ts (1)
  • ToriiQueryBuilder (24-143)
packages/sdk/src/internal/toriiQueryBuilder.ts (1)
packages/sdk/src/internal/types.ts (2)
  • ToriiQueryBuilder (557-557)
  • SchemaType (50-70)
packages/sdk/src/node/index.ts (6)
packages/sdk/src/internal/convertClauseToEntityKeysClause.ts (1)
  • intoEntityKeysClause (4-36)
packages/sdk/src/internal/parseEntities.ts (1)
  • parseEntities (10-59)
packages/sdk/src/internal/types.ts (2)
  • ToriiResponse (248-253)
  • ToriiQueryBuilder (557-557)
packages/sdk/src/internal/generateTypedData.ts (1)
  • generateTypedData (13-48)
packages/sdk/src/internal/errors.ts (2)
  • NO_SIGNER (1-1)
  • NO_IDENTITY (2-2)
packages/sdk/src/internal/toriiQueryBuilder.ts (1)
  • ToriiQueryBuilder (24-143)
examples/example-node-worker/src/typescript/contracts.gen.ts (2)
examples/example-node-worker/src/typescript/models.gen.ts (1)
  • direction (71-76)
packages/core/src/types/index.ts (1)
  • DojoCall (130-134)
examples/example-vite-react-sdk/src/historical-events.tsx (1)
packages/sdk/src/internal/toriiQueryBuilder.ts (1)
  • HistoricalToriiQueryBuilder (149-159)
packages/sdk/src/internal/merge.ts (3)
packages/sdk/src/web/queryBuilder.ts (1)
  • value (191-193)
examples/example-node-worker/src/typescript/models.gen.ts (1)
  • SchemaType (80-94)
packages/sdk/src/internal/types.ts (2)
  • SchemaType (50-70)
  • ParsedEntity (228-237)
packages/state/src/recs/index.ts (1)
packages/core/src/provider/DojoProvider.ts (1)
  • entities (104-123)
🪛 Biome (1.9.4)
examples/example-node-worker/main.ts

[error] 176-176: This code is unreachable

... because this statement will return from the function beforehand

(lint/correctness/noUnreachable)

packages/sdk/src/internal/toriiQueryBuilder.ts

[error] 133-133: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

examples/example-node-worker/src/typescript/contracts.gen.ts

[error] 1-1: All these imports are only used as types.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Use import type.

(lint/style/useImportType)


[error] 2-2: Some named imports are only used as types.

This import is only used as a type.

This import is only used as a type.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

examples/example-node-worker/src/typescript/models.gen.ts

[error] 3-3: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

examples/example-vite-react-sdk/src/historical-events.tsx

[error] 1-5: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

packages/sdk/src/__tests__/zustand.perf.test.ts

[error] 131-133: Forbidden non-null assertion.

(lint/style/noNonNullAssertion)


[error] 131-133: Forbidden non-null assertion.

Unsafe fix: Replace with optional chain operator ?. This operator includes runtime checks, so it is safer than the compile-only non-null assertion operator

(lint/style/noNonNullAssertion)


[error] 132-132: The computed expression can be simplified without the use of a string literal.

Unsafe fix: Use a literal key instead.

(lint/complexity/useLiteralKeys)

packages/sdk/src/internal/merge.ts

[error] 76-76: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)


[error] 96-96: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)


[error] 133-133: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

packages/sdk/src/internal/parseEntities.ts

[error] 50-50: Do not use template literals if interpolation and special-character handling are not needed.

Unsafe fix: Replace with string literal

(lint/style/noUnusedTemplateLiteral)

packages/sdk/src/web/experimental/index.ts

[error] 95-95: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

packages/sdk/src/web/state/zustand.ts

[error] 14-14: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

packages/state/src/recs/index.ts

[error] 185-185: Do not use template literals if interpolation and special-character handling are not needed.

Unsafe fix: Replace with string literal

(lint/style/noUnusedTemplateLiteral)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build
🔇 Additional comments (29)
packages/sdk/src/internal/merge.ts (3)

9-11: Looks good! Well-documented type guard function.

The type guard for CairoOption is correctly implemented and includes clear JSDoc comments explaining its purpose.


53-55: Looks good! Well-documented type guard function.

The type guard for CairoCustomEnum is correctly implemented and includes clear JSDoc comments explaining its purpose.


120-122: Good type definition with export.

The MergedModels type alias is properly defined and exported, making it available for use throughout the codebase.

packages/sdk/src/web/state/zustand.ts (2)

84-94: Good implementation: Using the imported deepMerge utility.

The imported deepMerge utility is used correctly to merge models within namespaces.


134-144: Good implementation: Consistent usage of deepMerge across methods.

The imported deepMerge utility is consistently used in the updateEntity method, matching the implementation in mergeEntities.

examples/example-vite-react-sdk/src/historical-events.tsx (1)

14-14: Good migration to HistoricalToriiQueryBuilder.

Switching to the HistoricalToriiQueryBuilder aligns with the SDK's updated approach to handling historical events with cursor-based pagination.

packages/sdk/src/web/experimental/index.ts (6)

23-23: Client initialization changed from async to sync

The initialization of the Torii client has been changed from an asynchronous factory method to a synchronous constructor. This is a significant change in usage pattern that should be documented in release notes.


27-27: Improved entity extraction and parsing

The function now explicitly extracts the items array from the client response before parsing, which provides a more consistent data handling approach across the codebase.


29-31: Simplified event retrieval without historical parameter

The getEvents function has been simplified by removing the unused historical parameter, addressing a previous review comment. The implementation now consistently uses the items array from the response.


38-38: Query flag renamed for clarity

The query flag was renamed from dont_include_hashed_keys to no_hashed_keys, which is clearer and more concise.


53-58: Updated entity data parsing in subscription callback

The callback now correctly parses entity data as an array instead of a keyed object, aligning with the changes in the parseEntities function.


98-100: Updated event data parsing in subscription callback

The callback now correctly parses the event data as an array, which aligns with the changes in the parseEntities function.

packages/sdk/src/internal/parseEntities.ts (5)

10-15: Updated parseEntities to accept an array instead of a keyed object

The function now accepts an array of torii.Entity objects instead of a keyed object. This is a significant API change that should be documented for users.


17-20: Refactored entity processing for array input

The entity processing loop now iterates over an array of entities rather than object keys, extracting hashed_keys and models directly from each entity.


25-25: Updated model loop to reference entity's models property

The inner loop now correctly references entity.models to match the array-based entity structure.


47-47: Changed result collection method for array-based approach

Instead of setting values in an object by key, the result is now built using push() to maintain the array structure.


90-90: Added support for "tuple" type in parseValue

The parseValue function now handles the "tuple" type, treating it the same way as "array" by mapping over the values and parsing each one.

packages/state/src/recs/index.ts (4)

15-15: Updated import to use the renamed Entity type

The import has been updated to use Entity as ToriiEntity from @dojoengine/torii-client, aligning with the new entity handling approach.


162-162: Switched from offset to cursor-based pagination

The code now uses cursor-based pagination instead of offset-based pagination, which is a significant change in how pagination works.


168-179: Updated query parameters for cursor-based pagination

The query parameters now use a nested pagination object with cursor and direction properties instead of the previous flat structure. This is a breaking change that should be documented.


229-240: Updated event query parameters for cursor-based pagination

Similar to the entity query, the event query parameters now use a nested pagination object and have updated field names.

packages/sdk/src/internal/toriiQueryBuilder.ts (1)

145-159: Good implementation of HistoricalToriiQueryBuilder

The new HistoricalToriiQueryBuilder class is well-designed, extending the base query builder while fixing the historical flag to true. This ensures consistent behavior for historical queries.

packages/sdk/src/node/index.ts (7)

19-19: Import refinement improved type safety.

The import statement has been refined to only import the necessary types, removing unused imports like StarknetDomain. This helps prevent potential confusion and bloat in the codebase.


29-30: Added robust error handling with neverthrow.

Integration of the neverthrow library and standardized error constants provides better error handling patterns throughout the SDK.


34-36: New module exports enhance SDK capabilities.

The additional exports from worker.ts, types.ts, and models.ts modules expand the SDK's functionality and provide more tools for developers.


189-189: Entity query results access updated.

Entity query results now consistently access the .items property before parsing.


202-204: Event query results access updated.

Event query results now consistently access the .items property before parsing.


390-391: Entity query results access updated.

Query results now consistently access the .items property before parsing.


406-407: Event query results access updated.

Event query results now consistently access the .items property before parsing.

Comment on lines +7 to +13
const build_actions_move_calldata = (direction: CairoCustomEnum): DojoCall => {
return {
contractName: "actions",
entrypoint: "move",
calldata: [direction],
};
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Function uses inconsistent naming style

The function uses snake_case (build_actions_move_calldata) instead of camelCase which is the TypeScript convention. This is inconsistent with the exported function names in line 52-57 which use camelCase (buildMoveCalldata).

-const build_actions_move_calldata = (direction: CairoCustomEnum): DojoCall => {
+const buildActionsMoveCalldata = (direction: CairoCustomEnum): DojoCall => {
   return {
     contractName: "actions",
     entrypoint: "move",
     calldata: [direction],
   };
 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const build_actions_move_calldata = (direction: CairoCustomEnum): DojoCall => {
return {
contractName: "actions",
entrypoint: "move",
calldata: [direction],
};
};
const buildActionsMoveCalldata = (direction: CairoCustomEnum): DojoCall => {
return {
contractName: "actions",
entrypoint: "move",
calldata: [direction],
};
};

Comment on lines +28 to +34
const build_actions_spawn_calldata = (): DojoCall => {
return {
contractName: "actions",
entrypoint: "spawn",
calldata: [],
};
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Function uses inconsistent naming style

Similar to the previous function, this uses snake_case instead of the conventional camelCase in TypeScript.

-const build_actions_spawn_calldata = (): DojoCall => {
+const buildActionsSpawnCalldata = (): DojoCall => {
   return {
     contractName: "actions",
     entrypoint: "spawn",
     calldata: [],
   };
 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const build_actions_spawn_calldata = (): DojoCall => {
return {
contractName: "actions",
entrypoint: "spawn",
calldata: [],
};
};
const buildActionsSpawnCalldata = (): DojoCall => {
return {
contractName: "actions",
entrypoint: "spawn",
calldata: [],
};
};

Comment on lines +15 to +26
const actions_move = async (snAccount: Account | AccountInterface, direction: CairoCustomEnum) => {
try {
return await provider.execute(
snAccount,
build_actions_move_calldata(direction),
"dojo_starter",
);
} catch (error) {
console.error(error);
throw error;
}
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Function follows inconsistent naming and could use better error handling

The function uses snake_case which is inconsistent with TypeScript conventions. Additionally, the error handling could benefit from more specific error messages to aid debugging.

-const actions_move = async (snAccount: Account | AccountInterface, direction: CairoCustomEnum) => {
+const actionsMove = async (snAccount: Account | AccountInterface, direction: CairoCustomEnum) => {
   try {
     return await provider.execute(
       snAccount,
-      build_actions_move_calldata(direction),
+      buildActionsMoveCalldata(direction),
       "dojo_starter",
     );
   } catch (error) {
-    console.error(error);
+    console.error(`Error executing move action: ${error}`);
     throw error;
   }
 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const actions_move = async (snAccount: Account | AccountInterface, direction: CairoCustomEnum) => {
try {
return await provider.execute(
snAccount,
build_actions_move_calldata(direction),
"dojo_starter",
);
} catch (error) {
console.error(error);
throw error;
}
};
const actionsMove = async (snAccount: Account | AccountInterface, direction: CairoCustomEnum) => {
try {
return await provider.execute(
snAccount,
buildActionsMoveCalldata(direction),
"dojo_starter",
);
} catch (error) {
console.error(`Error executing move action: ${error}`);
throw error;
}
};

Comment on lines +52 to +57
actions: {
move: actions_move,
buildMoveCalldata: build_actions_move_calldata,
spawn: actions_spawn,
buildSpawnCalldata: build_actions_spawn_calldata,
},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Inconsistency between function definitions and exports

The exported function names use camelCase but reference functions defined with snake_case. This inconsistency should be resolved.

 return {
   actions: {
-    move: actions_move,
-    buildMoveCalldata: build_actions_move_calldata,
-    spawn: actions_spawn,
-    buildSpawnCalldata: build_actions_spawn_calldata,
+    move: actionsMove,
+    buildMoveCalldata: buildActionsMoveCalldata,
+    spawn: actionsSpawn,
+    buildSpawnCalldata: buildActionsSpawnCalldata,
   },
 };

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +36 to +47
const actions_spawn = async (snAccount: Account | AccountInterface) => {
try {
return await provider.execute(
snAccount,
build_actions_spawn_calldata(),
"dojo_starter",
);
} catch (error) {
console.error(error);
throw error;
}
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Function follows inconsistent naming and could use better error handling

Similar to actions_move, this function uses snake_case and has minimal error information in the error handling.

-const actions_spawn = async (snAccount: Account | AccountInterface) => {
+const actionsSpawn = async (snAccount: Account | AccountInterface) => {
   try {
     return await provider.execute(
       snAccount,
-      build_actions_spawn_calldata(),
+      buildActionsSpawnCalldata(),
       "dojo_starter",
     );
   } catch (error) {
-    console.error(error);
+    console.error(`Error executing spawn action: ${error}`);
     throw error;
   }
 };

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +185 to +187
if (logging) console.log(`Fetched entities`, entities.items);

setEntities(entities, components, logging);
setEntities(entities.items, components, logging);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Entity handling inconsistency with array vs object

The code now expects entities.items to be an array, but then uses Object.keys(entities.items).length in line 189 to check the length, which is inefficient for an array. Additionally, setEntities might expect a different shape than what's being passed.

-if (logging) console.log(`Fetched entities`, entities.items);
+if (logging) console.log("Fetched entities", entities.items);

 setEntities(entities.items, components, logging);

-if (Object.keys(entities.items).length < limit) {
+if (entities.items.length < limit) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (logging) console.log(`Fetched entities`, entities.items);
setEntities(entities, components, logging);
setEntities(entities.items, components, logging);
if (logging) console.log("Fetched entities", entities.items);
setEntities(entities.items, components, logging);
if (entities.items.length < limit) {
🧰 Tools
🪛 Biome (1.9.4)

[error] 185-185: Do not use template literals if interpolation and special-character handling are not needed.

Unsafe fix: Replace with string literal

(lint/style/noUnusedTemplateLiteral)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🔭 Outside diff range comments (3)
packages/state/src/recs/index.ts (1)

516-554: ⚠️ Potential issue

Type mismatch in insertEntitiesInDB function

The function parameter type has been updated to ToriiEntity[], but the implementation still treats entities as an object with entries. This will cause runtime errors.

 async function insertEntitiesInDB(
     db: IDBDatabase,
     entities: ToriiEntity[]
 ): Promise<void> {
     return new Promise((resolve, reject) => {
         const transaction = db.transaction(["entities"], "readwrite");
         const store = transaction.objectStore("entities");

         let completed = 0;
         let error: Error | null = null;

         // Handle transaction completion
         transaction.oncomplete = () => {
             if (error) {
                 reject(error);
             } else {
                 resolve();
             }
         };

         transaction.onerror = () => {
             reject(transaction.error);
         };

         // Store each entity
-        for (const [entityId, data] of Object.entries(entities)) {
-            const entityData = {
-                id: entityId,
-                ...data,
-            };
+        for (const entity of entities) {
+            const entityData = {
+                id: entity.hashed_keys,
+                ...entity,
+            };

             const request = store.put(entityData);
             completed++;

             request.onerror = () => {
                 error = request.error;
             };
         }
     });
 }
packages/sdk/src/web/index.ts (1)

257-283: ⚠️ Potential issue

Improve error handling in sendMessage method

The method returns a Result type but still throws an error in the catch block rather than returning an error result. This defeats the purpose of using the Result type for proper error handling.

sendMessage: async (
    data: TypedData,
    account?: Account
): Promise<Result<Uint8Array, string>> => {
    if (!account) {
        return err(NO_ACCOUNT);
    }
    try {
        // Sign the typed data
        const signature: Signature = await account.signMessage(data);

        // Stringify typed data for publishing
        const dataString = JSON.stringify(data);
        // Publish the signed message
        return ok(
            await client.publishMessage(
                dataString,
                Array.isArray(signature)
                    ? signature
                    : [signature.r.toString(), signature.s.toString()]
            )
        );
    } catch (error) {
        console.error("Failed to send message:", error);
-       throw error;
+       return err(String(error));
    }
},
packages/sdk/src/internal/toriiQueryBuilder.ts (1)

90-103: ⚠️ Potential issue

Updated OrderBy methods to use nested pagination structure

Both addOrderBy and withOrderBy now operate on the nested pagination.order_by property instead of a root-level property. This is a breaking change that aligns with the new pagination structure.

♻️ Duplicate comments (22)
examples/example-node-worker/src/typescript/contracts.gen.ts (5)

7-13: Function uses inconsistent naming style

The function uses snake_case (build_actions_move_calldata) instead of camelCase which is the TypeScript convention. This is inconsistent with the exported function names in line 52-57 which use camelCase (buildMoveCalldata).

-const build_actions_move_calldata = (direction: CairoCustomEnum): DojoCall => {
+const buildActionsMoveCalldata = (direction: CairoCustomEnum): DojoCall => {
  return {
    contractName: "actions",
    entrypoint: "move",
    calldata: [direction],
  };
};

15-26: Function follows inconsistent naming and could use better error handling

The function uses snake_case which is inconsistent with TypeScript conventions. Additionally, the error handling could benefit from more specific error messages to aid debugging.

-const actions_move = async (snAccount: Account | AccountInterface, direction: CairoCustomEnum) => {
+const actionsMove = async (snAccount: Account | AccountInterface, direction: CairoCustomEnum) => {
  try {
    return await provider.execute(
      snAccount,
-      build_actions_move_calldata(direction),
+      buildActionsMoveCalldata(direction),
      "dojo_starter",
    );
  } catch (error) {
-    console.error(error);
+    console.error(`Error executing move action: ${error}`);
    throw error;
  }
};

28-34: Function uses inconsistent naming style

Similar to the previous function, this uses snake_case instead of the conventional camelCase in TypeScript.

-const build_actions_spawn_calldata = (): DojoCall => {
+const buildActionsSpawnCalldata = (): DojoCall => {
  return {
    contractName: "actions",
    entrypoint: "spawn",
    calldata: [],
  };
};

36-47: Function follows inconsistent naming and could use better error handling

Similar to actions_move, this function uses snake_case and has minimal error information in the error handling.

-const actions_spawn = async (snAccount: Account | AccountInterface) => {
+const actionsSpawn = async (snAccount: Account | AccountInterface) => {
  try {
    return await provider.execute(
      snAccount,
-      build_actions_spawn_calldata(),
+      buildActionsSpawnCalldata(),
      "dojo_starter",
    );
  } catch (error) {
-    console.error(error);
+    console.error(`Error executing spawn action: ${error}`);
    throw error;
  }
};

52-57: Inconsistency between function definitions and exports

The exported function names use camelCase but reference functions defined with snake_case. This inconsistency should be resolved.

return {
  actions: {
-    move: actions_move,
-    buildMoveCalldata: build_actions_move_calldata,
-    spawn: actions_spawn,
-    buildSpawnCalldata: build_actions_spawn_calldata,
+    move: actionsMove,
+    buildMoveCalldata: buildActionsMoveCalldata,
+    spawn: actionsSpawn,
+    buildSpawnCalldata: buildActionsSpawnCalldata,
  },
};
packages/sdk/src/web/experimental/index.ts (1)

95-101: Strengthen callback parameter types

For improved type safety, replace the any type on the entityData parameter with torii.Entity, which aligns with the parseEntities<T>(entities: torii.Entity[]) signature.

-                    (_: string, entityData: any) => {
+                    (_: string, entityData: torii.Entity) => {
🧰 Tools
🪛 Biome (1.9.4)

[error] 95-95: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

packages/sdk/src/internal/parseEntities.ts (1)

126-133: 🛠️ Refactor suggestion

Inconsistent handling of numeric types

u64 is parsed as Number.parseInt, but it can exceed JavaScript's safe integer range (2^53-1). For consistency with other large integer types, consider converting it to BigInt.

function parsePrimitive(value: torii.Ty): any {
    switch (value.type_name) {
-        case "u64":
-            return Number.parseInt(value.value as string, 16);
        case "i256":
        case "i128":
        case "u256":
        case "u128":
+        case "u64":
            return BigInt(value.value as string);
        default:
            return value.value;
    }
}
packages/state/src/recs/index.ts (1)

307-333: ⚠️ Potential issue

Infinite loop in getEntitiesQuery

The fetchedEntities is only retrieved once outside the loop, but the cursor is updated inside the loop. This will lead to an infinite loop or immediate exit because you're not fetching new data on each iteration.

-    const fetchedEntities = await client.getEntities({
-        pagination: {
-            limit,
-            cursor,
-            direction: "Forward",
-            order_by: orderBy,
-        },
-        clause: clause || undefined,
-        no_hashed_keys: false,
-        models: entityModels,
-        historical,
-    });
-
     while (continueFetching) {
+        const fetchedEntities = await client.getEntities({
+            pagination: {
+                limit,
+                cursor,
+                direction: "Forward",
+                order_by: orderBy,
+            },
+            clause: clause || undefined,
+            no_hashed_keys: false,
+            models: entityModels,
+            historical,
+        });
+
         if (logging)
             console.log(
                 `Fetched ${Object.keys(fetchedEntities.items).length} entities ${fetchedEntities.next_cursor}`
             );

         setEntities(fetchedEntities.items, components, logging);

         if (Object.keys(fetchedEntities.items).length < limit) {
             continueFetching = false;
         } else {
             cursor = fetchedEntities.next_cursor;
         }
     }
examples/example-node-worker/main.ts (4)

21-23: ⚠️ Potential issue

Fix the case in WebSocket global assignment

The code is using Websocket (lowercase 'S') when it should be WebSocket (capital 'S'). This can cause compatibility issues with libraries that expect the standard global name.

-// @ts-ignore
-global.Websocket = w3cwebsocket;
+// @ts-ignore
+global.WebSocket = w3cwebsocket;

125-132: ⚠️ Potential issue

Avoid mutating the shared constant

The function initPositionFromEvent modifies defaultPositionCount via the pc reference, introducing side effects that affect all future callers.

-const pc = defaultPositionCount;
+const pc: PositionCount = { ...defaultPositionCount };

136-151: 🛠️ Refactor suggestion

Use iteration instead of data.pop() to process multiple entities

Using data.pop() both mutates the array and processes only the last entity, silently ignoring any others that might have arrived in the same batch.

async function onEntityUpdated({ data, error }) {
    if (error) {
        console.error(error);
        return;
    }

-   const entity = data.pop();
-   if (entity && entity.entityId !== "0x0") {
-       // do whatever you need here
-       const model = entity.models.dojo_starter;
-       if (model?.Moves) {
-           await publishOffchainPositionCount(model.Moves);
-       }
-   }
+   for (const entity of data) {
+       if (entity && entity.entityId !== "0x0") {
+           // do whatever you need here
+           const model = entity.models.dojo_starter;
+           if (model?.Moves) {
+               await publishOffchainPositionCount(model.Moves);
+           }
+       }
+   }
}

175-176: ⚠️ Potential issue

Remove unreachable code

The second return [sub]; statement will never execute because of the preceding return []; statement.

-    return [];
-    return [sub];
+    return [sub];
🧰 Tools
🪛 Biome (1.9.4)

[error] 176-176: This code is unreachable

... because this statement will return from the function beforehand

(lint/correctness/noUnreachable)

packages/sdk/src/internal/merge.ts (1)

123-160: Add unit tests for the deepMerge function

This is a critical utility function that handles complex merging logic for Cairo types, but it appears to lack test coverage.

Let's verify if there are tests for this function:

#!/bin/bash
# Check for tests related to the deepMerge function
rg "deepMerge" --type=ts -A 5 -B 5 "packages/sdk/src/__tests__" || echo "No tests found"

A comprehensive test suite should include:

  • Merging primitive values (overwrites correctly)
  • Recursive merging of nested objects
  • Handling of arrays (should be overwritten, not merged)
  • Treatment of null and undefined properties
  • Behavior with CairoOption and CairoCustomEnum types
🧰 Tools
🪛 Biome (1.9.4)

[error] 133-133: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

examples/example-node-worker/src/typescript/models.gen.ts (2)

38-46: ⚠️ Potential issue

Fix typo in "positition" field name

There is a typo in the positition field name (should be "position"). Since this affects the interface structure, it should be fixed in all occurrences.

export interface PositionCount {
	identity: string;
-	positition: Array<[BigNumberish, BigNumberish]>;
+	position: Array<[BigNumberish, BigNumberish]>;
}

export interface PositionCountValue {
-	positition: Array<[BigNumberish, BigNumberish]>;
+	position: Array<[BigNumberish, BigNumberish]>;
}

128-133: ⚠️ Potential issue

Ensure field names in schema match interfaces

The schema definition needs to be updated to match the corrected interface.

PositionCount: {
    identity: "",
-   positition: [[0, 0]],
+   position: [[0, 0]],
},
PositionCountValue: {
-   positition: [[0, 0]],
+   position: [[0, 0]],
},
packages/sdk/src/web/index.ts (1)

271-278: 🛠️ Refactor suggestion

Ensure signature array elements are strings

When signature is an array of bigints, they're forwarded as-is, but publishMessage expects string[]. Convert numeric values to strings.

return ok(
    await client.publishMessage(
        dataString,
-       Array.isArray(signature)
-           ? signature
-           : [signature.r.toString(), signature.s.toString()]
+       Array.isArray(signature)
+           ? signature.map(s => s.toString())
+           : [signature.r.toString(), signature.s.toString()]
    )
);
packages/sdk/src/internal/toriiQueryBuilder.ts (1)

133-142: ⚠️ Potential issue

Updated static pagination helper method signature

The withPagination static method now accepts cursor, limit, and direction parameters instead of page-based parameters, reflecting the new pagination approach.

There's a type issue flagged by the static analyzer:

[error] 133-133: Unexpected any. Specify a different type.

Consider specifying a more specific type for the generic parameter:

-    static withPagination<T extends Record<string, Record<string, any>>>(
+    static withPagination<T extends SchemaType>(
🧰 Tools
🪛 Biome (1.9.4)

[error] 133-133: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

packages/sdk/src/node/index.ts (5)

50-51: ⚠️ Potential issue

Client initialization change

Changed from torii.createClient to directly instantiating new torii.ToriiClient. This is a breaking change for anyone directly using the returned client.

Ensure this change is documented in migration guides and potentially consider providing a compatibility layer if feasible.


64-65: ⚠️ Potential issue

Updated entity parsing to use items array

The entity parsing has been updated to handle arrays from the .items property rather than keyed objects. This change simplifies the data structure but is a breaking change for existing code.

Renamed parameter from dont_include_hashed_keys to no_hashed_keys for improved naming consistency.

Ensure this change is documented in migration guides. Make sure that all examples and documentation are updated to reflect this new data structure.

Also applies to: 72-85


119-130: ⚠️ Potential issue

Updated event parsing to use items array

Similar to the entity parsing changes, event parsing now uses arrays from the .items property. This aligns event and entity parsing approaches for better consistency.

Update all documentation and examples to reflect this structural change in event data handling.


215-230: ⚠️ Potential issue

Modified generateTypedData signature

The method signature has changed:

  • Removed optional domain parameter
  • Added optional additionalTypes parameter
  • Domain is now always taken from options.domain

This is a breaking change for code that explicitly passed a domain parameter different from options.domain.

Ensure this change is documented in migration guides and consider if there are cases where passing a different domain was useful functionality.


240-265: 🛠️ Refactor suggestion

Improved error handling in sendMessage method

The method now:

  1. Checks for the presence of signer and identity before attempting to send
  2. Returns a Result type (from neverthrow) with ok/err instead of throwing exceptions
  3. No longer requires an explicit Account parameter, relying on options.signer and options.identity

This is a breaking change for code that catches exceptions from this method, but improves error handling overall.

However, there's an inconsistency - the function still throws an error in the catch block despite using Result for other error cases. Consider making the error handling consistent:

    try {
        const td = new torii.TypedData(JSON.stringify(data)).encode(
            options.identity
        );

        const sig = options.signer.sign(td);

        const dataString = JSON.stringify(data);

        return ok(
            await client.publishMessage(dataString, [
                sig.r.toString(),
                sig.s.toString(),
            ])
        );
    } catch (error) {
        console.error("Failed to send message:", error);
-        throw error;
+        return err(`Failed to send message: ${error instanceof Error ? error.message : String(error)}`);
    }
🧹 Nitpick comments (8)
examples/example-node-worker/src/typescript/contracts.gen.ts (1)

1-3: Update imports to use import type for type-only imports

The imports are only used as types in this file, not as values. Using import type ensures they're removed during compilation, resulting in a smaller bundle size.

-import { DojoProvider, DojoCall } from "@dojoengine/core";
-import { Account, AccountInterface, BigNumberish, CairoOption, CairoCustomEnum, ByteArray } from "starknet";
+import type { DojoProvider, DojoCall } from "@dojoengine/core";
+import type { Account, AccountInterface, BigNumberish, CairoOption, CairoCustomEnum, ByteArray } from "starknet";
 import * as models from "./models.gen";
🧰 Tools
🪛 Biome (1.9.4)

[error] 1-1: All these imports are only used as types.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Use import type.

(lint/style/useImportType)


[error] 2-2: Some named imports are only used as types.

This import is only used as a type.

This import is only used as a type.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

packages/state/src/recs/index.ts (2)

189-189: Inefficient array length check

When checking the length of an array, entities.items.length is more appropriate than Object.keys(entities.items).length.

-        if (Object.keys(entities.items).length < limit) {
+        if (entities.items.length < limit) {

246-246: Inefficient array length check in getEvents

Same as the previous comment, use direct array length property instead of Object.keys().length.

-        if (Object.keys(entities.items).length === 0) {
+        if (entities.items.length === 0) {
examples/example-node-worker/main.ts (1)

110-114: Use Result handling pattern for error management

The function uses a try-catch that logs the error and then rethrows it, but the SDK now supports the Result pattern from neverthrow. Consider handling the error without throwing.

try {
-   await sdk.sendMessage(data);
+   const result = await sdk.sendMessage(data);
+   if (result.isErr()) {
+       console.error(result.error);
+   }
} catch (err) {
    console.error(err);
}
packages/sdk/src/internal/merge.ts (2)

76-76: Avoid the use of any type

The code uses any types in multiple places, which disables TypeScript's type checking. This can lead to runtime errors that could have been caught at compile time.

-const newEnumContent: Record<string, any> = {};
+const newEnumContent: Record<string, unknown> = {};

-const newEnumContent: Record<string, any> = {};
+const newEnumContent: Record<string, unknown> = {};

-const result = { ...target } as Record<string, any>;
+const result = { ...target } as Record<string, unknown>;

Also applies to: 96-96, 133-133

🧰 Tools
🪛 Biome (1.9.4)

[error] 76-76: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)


127-156: Consider adding specific handling for null in comparison logic

The current comparison checks for source[key] !== null before checking if it's an object. This might cause issues if you intend to treat null as a special case differently from other primitives.

if (
-   source[key] !== null &&
    typeof source[key] === "object" &&
    !Array.isArray(source[key])
) {

Or if you want to preserve the current behavior but make it more explicit:

if (
    source[key] !== null &&
    typeof source[key] === "object" &&
    !Array.isArray(source[key])
) {
    // If the property is an object in both source and target, recursively merge
    if (
        key in target &&
+       target[key] !== null &&
        typeof target[key] === "object" &&
        !Array.isArray(target[key])
    ) {
🧰 Tools
🪛 Biome (1.9.4)

[error] 133-133: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

examples/example-node-worker/src/typescript/models.gen.ts (1)

1-3: Use import type for type-only imports

Use import type syntax for imports that are only used as types to help optimize the compiled output.

import type { SchemaType as ISchemaType } from "@dojoengine/sdk";

-import { CairoCustomEnum, CairoOption, CairoOptionVariant, BigNumberish } from 'starknet';
+import { CairoCustomEnum, CairoOption, CairoOptionVariant } from 'starknet';
+import type { BigNumberish } from 'starknet';
🧰 Tools
🪛 Biome (1.9.4)

[error] 3-3: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

packages/sdk/src/internal/toriiQueryBuilder.ts (1)

39-45: Deprecated withOffset is now a no-op

The withOffset method has been deprecated and converted to a no-op that returns the builder instance without making any changes. This supports the transition from offset-based to cursor-based pagination.

Consider adding more details to the deprecation notice to guide users toward using the new cursor-based pagination methods.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro (Legacy)

📥 Commits

Reviewing files that changed from the base of the PR and between b104036 and ff82d6f.

⛔ Files ignored due to path filters (5)
  • examples/example-vite-react-phaser-recs/src/dojo/generated/setup.ts is excluded by !**/generated/**
  • examples/example-vite-react-pwa-recs/src/dojo/generated/setup.ts is excluded by !**/generated/**
  • examples/example-vite-react-threejs-recs/src/dojo/generated/setup.ts is excluded by !**/generated/**
  • examples/example-vue-app-recs/src/dojo/generated/setup.ts is excluded by !**/generated/**
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (43)
  • .changeset/thirty-cheetahs-design.md (1 hunks)
  • .github/workflows/ci.yaml (1 hunks)
  • .github/workflows/release.yaml (6 hunks)
  • examples/example-node-worker/dojoConfig.ts (1 hunks)
  • examples/example-node-worker/env.ts (1 hunks)
  • examples/example-node-worker/main.ts (1 hunks)
  • examples/example-node-worker/package.json (1 hunks)
  • examples/example-node-worker/src/typescript/contracts.gen.ts (1 hunks)
  • examples/example-node-worker/src/typescript/models.gen.ts (1 hunks)
  • examples/example-node-worker/tsconfig.json (1 hunks)
  • examples/example-nodejs-bot/package.json (1 hunks)
  • examples/example-vite-react-app-recs/src/dojo/setup.ts (2 hunks)
  • examples/example-vite-react-sdk/src/historical-events.tsx (2 hunks)
  • package.json (1 hunks)
  • packages/core/package.json (1 hunks)
  • packages/react/package.json (2 hunks)
  • packages/sdk/package.json (3 hunks)
  • packages/sdk/src/__tests__/parseEntities.test.ts (7 hunks)
  • packages/sdk/src/__tests__/parseHistoricalEvents.test.ts (0 hunks)
  • packages/sdk/src/__tests__/state.test.ts (3 hunks)
  • packages/sdk/src/__tests__/toriiQueryBuilder.test.ts (6 hunks)
  • packages/sdk/src/__tests__/zustand.perf.test.ts (1 hunks)
  • packages/sdk/src/internal/errors.ts (1 hunks)
  • packages/sdk/src/internal/generateTypedData.ts (2 hunks)
  • packages/sdk/src/internal/merge.ts (1 hunks)
  • packages/sdk/src/internal/models.ts (1 hunks)
  • packages/sdk/src/internal/parseEntities.ts (4 hunks)
  • packages/sdk/src/internal/parseHistoricalEvents.ts (0 hunks)
  • packages/sdk/src/internal/toriiQueryBuilder.ts (6 hunks)
  • packages/sdk/src/internal/types.ts (5 hunks)
  • packages/sdk/src/node/index.ts (12 hunks)
  • packages/sdk/src/node/worker.ts (1 hunks)
  • packages/sdk/src/web/experimental/index.ts (3 hunks)
  • packages/sdk/src/web/index.ts (11 hunks)
  • packages/sdk/src/web/react/hooks/entities.ts (1 hunks)
  • packages/sdk/src/web/react/hooks/events.ts (3 hunks)
  • packages/sdk/src/web/react/hooks/hooks.ts (4 hunks)
  • packages/sdk/src/web/state/zustand.ts (1 hunks)
  • packages/state/src/recs/index.ts (6 hunks)
  • packages/torii-client/package.json (2 hunks)
  • packages/torii-wasm/dojo.c (1 hunks)
  • turbo.json (1 hunks)
  • worlds/onchain-dash (1 hunks)
💤 Files with no reviewable changes (2)
  • packages/sdk/src/tests/parseHistoricalEvents.test.ts
  • packages/sdk/src/internal/parseHistoricalEvents.ts
✅ Files skipped from review due to trivial changes (3)
  • worlds/onchain-dash
  • packages/sdk/src/tests/state.test.ts
  • .changeset/thirty-cheetahs-design.md
🚧 Files skipped from review as they are similar to previous changes (24)
  • examples/example-nodejs-bot/package.json
  • packages/torii-client/package.json
  • packages/torii-wasm/dojo.c
  • packages/sdk/package.json
  • packages/core/package.json
  • examples/example-node-worker/env.ts
  • turbo.json
  • packages/sdk/src/internal/errors.ts
  • packages/sdk/src/node/worker.ts
  • packages/react/package.json
  • packages/sdk/src/web/react/hooks/hooks.ts
  • .github/workflows/release.yaml
  • examples/example-node-worker/dojoConfig.ts
  • .github/workflows/ci.yaml
  • package.json
  • examples/example-vite-react-app-recs/src/dojo/setup.ts
  • packages/sdk/src/tests/parseEntities.test.ts
  • packages/sdk/src/web/react/hooks/events.ts
  • examples/example-node-worker/package.json
  • examples/example-node-worker/tsconfig.json
  • packages/sdk/src/tests/toriiQueryBuilder.test.ts
  • packages/sdk/src/internal/types.ts
  • packages/sdk/src/internal/generateTypedData.ts
  • packages/sdk/src/internal/models.ts
🧰 Additional context used
🧬 Code Graph Analysis (8)
packages/sdk/src/web/react/hooks/entities.ts (2)
packages/core/src/provider/DojoProvider.ts (1)
  • entities (104-123)
packages/sdk/src/web/queryBuilder.ts (2)
  • entity (72-80)
  • entity (100-105)
packages/sdk/src/web/index.ts (5)
packages/sdk/src/internal/parseEntities.ts (1)
  • parseEntities (10-59)
packages/sdk/src/internal/convertClauseToEntityKeysClause.ts (1)
  • intoEntityKeysClause (4-36)
packages/sdk/src/internal/generateTypedData.ts (1)
  • generateTypedData (13-48)
packages/sdk/src/internal/errors.ts (1)
  • NO_ACCOUNT (3-3)
packages/sdk/src/internal/toriiQueryBuilder.ts (1)
  • ToriiQueryBuilder (24-143)
packages/sdk/src/node/index.ts (6)
packages/sdk/src/internal/convertClauseToEntityKeysClause.ts (1)
  • intoEntityKeysClause (4-36)
packages/sdk/src/internal/parseEntities.ts (1)
  • parseEntities (10-59)
packages/sdk/src/internal/types.ts (2)
  • ToriiResponse (248-253)
  • ToriiQueryBuilder (557-557)
packages/sdk/src/internal/generateTypedData.ts (1)
  • generateTypedData (13-48)
packages/sdk/src/internal/errors.ts (2)
  • NO_SIGNER (1-1)
  • NO_IDENTITY (2-2)
packages/sdk/src/internal/toriiQueryBuilder.ts (1)
  • ToriiQueryBuilder (24-143)
examples/example-node-worker/src/typescript/contracts.gen.ts (2)
examples/example-node-worker/src/typescript/models.gen.ts (1)
  • direction (71-76)
packages/core/src/types/index.ts (1)
  • DojoCall (130-134)
examples/example-node-worker/src/typescript/models.gen.ts (1)
packages/sdk/src/internal/types.ts (1)
  • SchemaType (50-70)
examples/example-vite-react-sdk/src/historical-events.tsx (1)
packages/sdk/src/internal/toriiQueryBuilder.ts (1)
  • HistoricalToriiQueryBuilder (149-159)
packages/sdk/src/internal/merge.ts (3)
packages/sdk/src/web/queryBuilder.ts (1)
  • value (191-193)
examples/example-node-worker/src/typescript/models.gen.ts (1)
  • SchemaType (80-94)
packages/sdk/src/internal/types.ts (2)
  • SchemaType (50-70)
  • ParsedEntity (228-237)
packages/sdk/src/web/experimental/index.ts (2)
packages/sdk/src/internal/parseEntities.ts (1)
  • parseEntities (10-59)
packages/sdk/src/internal/convertClauseToEntityKeysClause.ts (1)
  • intoEntityKeysClause (4-36)
🪛 Biome (1.9.4)
examples/example-node-worker/main.ts

[error] 176-176: This code is unreachable

... because this statement will return from the function beforehand

(lint/correctness/noUnreachable)

packages/sdk/src/internal/toriiQueryBuilder.ts

[error] 133-133: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

examples/example-node-worker/src/typescript/contracts.gen.ts

[error] 1-1: All these imports are only used as types.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Use import type.

(lint/style/useImportType)


[error] 2-2: Some named imports are only used as types.

This import is only used as a type.

This import is only used as a type.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

examples/example-node-worker/src/typescript/models.gen.ts

[error] 3-3: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

examples/example-vite-react-sdk/src/historical-events.tsx

[error] 1-5: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

packages/sdk/src/__tests__/zustand.perf.test.ts

[error] 131-133: Forbidden non-null assertion.

(lint/style/noNonNullAssertion)


[error] 131-133: Forbidden non-null assertion.

Unsafe fix: Replace with optional chain operator ?. This operator includes runtime checks, so it is safer than the compile-only non-null assertion operator

(lint/style/noNonNullAssertion)


[error] 132-132: The computed expression can be simplified without the use of a string literal.

Unsafe fix: Use a literal key instead.

(lint/complexity/useLiteralKeys)

packages/sdk/src/internal/merge.ts

[error] 76-76: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)


[error] 96-96: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)


[error] 133-133: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

packages/sdk/src/internal/parseEntities.ts

[error] 50-50: Do not use template literals if interpolation and special-character handling are not needed.

Unsafe fix: Replace with string literal

(lint/style/noUnusedTemplateLiteral)

packages/sdk/src/web/experimental/index.ts

[error] 95-95: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

packages/sdk/src/web/state/zustand.ts

[error] 14-14: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

packages/state/src/recs/index.ts

[error] 185-185: Do not use template literals if interpolation and special-character handling are not needed.

Unsafe fix: Replace with string literal

(lint/style/noUnusedTemplateLiteral)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build
🔇 Additional comments (21)
packages/sdk/src/__tests__/zustand.perf.test.ts (1)

131-133: Style change for better code formatting

The code change maintains the same functionality while breaking the property access across multiple lines for better readability in complex nested expressions.

Note: There are two static analysis warnings about non-null assertions on line 133. While not introduced by this change, you might consider using optional chaining (?.) instead of non-null assertions (!) in the future for better type safety.

🧰 Tools
🪛 Biome (1.9.4)

[error] 131-133: Forbidden non-null assertion.

(lint/style/noNonNullAssertion)


[error] 131-133: Forbidden non-null assertion.

Unsafe fix: Replace with optional chain operator ?. This operator includes runtime checks, so it is safer than the compile-only non-null assertion operator

(lint/style/noNonNullAssertion)


[error] 132-132: The computed expression can be simplified without the use of a string literal.

Unsafe fix: Use a literal key instead.

(lint/complexity/useLiteralKeys)

examples/example-vite-react-sdk/src/historical-events.tsx (2)

4-5: Updated import to use specialized HistoricalToriiQueryBuilder

The specialized HistoricalToriiQueryBuilder is now imported instead of the generic ToriiQueryBuilder, which aligns with the SDK's architectural changes.

Consider using import type { ParsedEntity } for type-only imports to optimize bundling:

import {
    KeysClause,
-   ParsedEntity,
    HistoricalToriiQueryBuilder,
} from "@dojoengine/sdk";
+import type { ParsedEntity } from "@dojoengine/sdk";
🧰 Tools
🪛 Biome (1.9.4)

[error] 1-5: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)


14-14: Updated to use HistoricalToriiQueryBuilder

The implementation now uses the specialized HistoricalToriiQueryBuilder which automatically sets the historical flag to true, improving code clarity and intent.

packages/sdk/src/web/state/zustand.ts (1)

14-14: Improved code organization by centralizing merge utilities

The previous local implementation of Cairo type merging functions has been replaced with imports from a centralized utility module. This improves maintainability by keeping all merging logic in one place.

Consider using import type for the MergedModels type to optimize bundling:

-import { deepMerge, MergedModels } from "../../internal/merge";
+import { deepMerge } from "../../internal/merge";
+import type { MergedModels } from "../../internal/merge";
🧰 Tools
🪛 Biome (1.9.4)

[error] 14-14: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

packages/sdk/src/web/react/hooks/entities.ts (2)

22-24: Improved function body clarity

Changed from implicit return to explicit block statement for the processInitialData callback, which better represents that this function performs an action without returning a meaningful value.


26-35:

✅ Verification successful

Enhanced entity filtering logic and update behavior

The updated implementation improves how entities with zero IDs are filtered:

  1. Uses Number.parseInt(e.entityId, 16) !== 0 which is more robust than string comparison
  2. Uses the first entity (entities[0]) instead of the last one (entities.pop())

This change aligns with the SDK's unified approach to entity and event data processing.

Verify that using the first entity instead of the last one in the array doesn't cause any issues with your application logic:


🏁 Script executed:

#!/bin/bash
# Look for patterns where multiple entities are expected in update data
rg -A 5 "updateEntity" --no-ignore-vcs

Length of output: 12707


No issues with first-entity selection

I’ve verified that across the SDK and example code, updateEntity is always called with the first filtered element. There are no patterns where downstream logic expects the last element:

• examples/example-vite-phaser-sdk/src/scenes/Game.ts uses data[0]
• No occurrences of multiple-entity handling rely on .pop() or the last item

This change is consistent with the unified entity/event processing and can be approved.

packages/sdk/src/web/experimental/index.ts (4)

27-32: Updated entity and event fetching to handle paginated results

The code now correctly extracts the .items array from client responses, aligning with the new cursor-based pagination approach.


53-59: Update entity data handling in callback

The callback now correctly parses a single entity from an array, matching the changes in the parseEntities function that now expects an array of entities.


98-100: Update event data handling in callback

Similar to the entity callback, this correctly adapts to the new array-based entity parsing.


23-23:

✅ Verification successful

Changed client creation from async to sync

The client initialization has been updated from using an asynchronous factory method to a synchronous constructor. Make sure to verify this doesn't affect callers expecting a Promise.


🏁 Script executed:

#!/bin/bash
# Check for any usages of createClient that might need updating
echo "Searching for usages of createClient that might need updating"
rg -A 2 "createClient" --type ts

Length of output: 13516


🏁 Script executed:

#!/bin/bash
# Search for any imports or references to the experimental module
rg -n "experimental" --type ts

Length of output: 427


🏁 Script executed:

#!/bin/bash
# Display the contents of the experimental index file to inspect exports
sed -n '1,200p' packages/sdk/src/web/experimental/index.ts

Length of output: 4431


Client instantiation remains async to consumers
We still export init as an async function returning a Promise of the SDK methods. Although we now call new ToriiClient(...) synchronously under the hood, callers continue to await init(...) with no change to their code.

No further updates required.

packages/sdk/src/internal/parseEntities.ts (3)

10-13: Updated function signature for array-based entity handling

The function now accepts an array of entities rather than an object, aligning with the new cursor-based pagination approach that returns items as arrays.


17-25: Refactored entity parsing for array inputs

The entity parsing logic has been updated to iterate over an array of entities rather than object keys, which is more efficient and aligns with the changes in the API response format.


90-93: Added support for tuple type parsing

Tuple type support has been added to the parseValue function, ensuring that tuple types in Cairo are correctly handled. This is handled similarly to arrays by mapping over the value elements.

packages/state/src/recs/index.ts (1)

168-179: Updated pagination to use cursor-based approach

The pagination logic has been updated to use cursor-based pagination (with cursor and direction: "Forward") instead of offset-based pagination. The query parameter dont_include_hashed_keys has been renamed to no_hashed_keys for clarity.

packages/sdk/src/internal/toriiQueryBuilder.ts (3)

47-55: Added new withCursor method for cursor-based pagination

Good addition! This method allows setting the cursor for pagination, which is essential for the new cursor-based pagination approach.


57-63: Added new withDirection method for pagination direction

This method properly implements the ability to set pagination direction, completing the cursor-based pagination API.


145-158: Added HistoricalToriiQueryBuilder for historical queries

Good addition! This subclass extends ToriiQueryBuilder with the historical flag set to true by default, making it easier to create historical queries without having to set this flag manually.

packages/sdk/src/node/index.ts (4)

19-37: Improved imports and exports for better error handling and type safety

The changes:

  1. Removed unnecessary imports from "starknet"
  2. Added Result, err, ok from "neverthrow" for better error handling
  3. Added NO_IDENTITY and NO_SIGNER constants for consistent error messages
  4. Added exports for worker.ts, internal types, and models

These changes improve error handling and support new worker-based usage patterns.


138-143: Consistent event callback data parsing

The event callback now parses a single entity into an array using parseEntities([entityData]), consistent with the entity callback approach. This ensures uniformity in how entity data is parsed throughout the SDK.


189-204: Simplified entity and event querying

The entity and event querying methods now access .items before parsing, consistent with the updated data structure. This matches the subscription callback approach and ensures data is processed uniformly throughout the SDK.


385-392: Updated query conversion methods to use consistent item parsing

The methods toriiQueryIntoHashedKeys and toriiEventMessagesQueryIntoHashedKeys have been updated to:

  1. Use .items before parsing, consistent with other methods
  2. Use the same parsing approach for entities and events

The historical parameter has been removed from toriiEventMessagesQueryIntoHashedKeys, simplifying the API.

These changes ensure consistency across the SDK and align with the cursor-based pagination model.

Also applies to: 401-414

Comment on lines 257 to +263
sendMessage: async (
data: TypedData,
account: Account
): Promise<void> => {
account?: Account
): Promise<Result<Uint8Array, string>> => {
if (!account) {
return err(NO_ACCOUNT);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Use configured signer and identity when account is not provided

The method checks if an account is provided and returns an error if not, but it should utilize the configured signer and identity from the SDK initialization.

sendMessage: async (
    data: TypedData,
    account?: Account
): Promise<Result<Uint8Array, string>> => {
-   if (!account) {
-       return err(NO_ACCOUNT);
-   }
+   // Try to use provided account, fall back to configured signer and identity
+   if (!account && options.signer && options.identity) {
+       // Use the configured signer and identity
+       try {
+           // Implementation details depend on how your signer works,
+           // but you would need to create or use a signing mechanism
+           // with the configured signer and identity
+           // e.g., something like:
+           // const signature = options.signer.sign(data, options.identity);
+           // ...
+       } catch (error) {
+           return err(`Failed to sign with configured signer: ${String(error)}`);
+       }
+   } else if (!account) {
+       return err(NO_ACCOUNT);
+   }

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines 77 to 79
includeHashedKeys(): ToriiQueryBuilder<T> {
this.query.dont_include_hashed_keys = false;
this.query.no_hashed_keys = false;
return this;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Renamed flag for hashed keys inclusion

The property was renamed from dont_include_hashed_keys to no_hashed_keys for improved naming consistency.

This is a breaking change that should be documented in the migration guide.

Comment on lines +110 to 119
this.query.models.push(model);
return this;
}

/**
* Set multiple entity models at once
*/
withEntityModels(models: (keyof T & string)[]): ToriiQueryBuilder<T> {
this.query.entity_models = models;
return this;
}

/**
* Set the minimum timestamp for entity updates
*/
updatedAfter(timestamp: number): ToriiQueryBuilder<T> {
this.query.entity_updated_after = timestamp;
this.query.models = models;
return this;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Renamed entity models property

The property was renamed from entity_models to models for consistency and clarity.

This is a breaking change that should be documented in the migration guide.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🔭 Outside diff range comments (2)
packages/sdk/src/web/index.ts (1)

279-282: ⚠️ Potential issue

Error handling inconsistency

The method returns a Result type to handle errors in a structured way, but still throws errors in the catch block instead of returning err().

Fix the inconsistency by returning an error result instead of throwing:

            } catch (error) {
                console.error("Failed to send message:", error);
-                throw error;
+                return err(String(error));
            }
packages/sdk/src/node/index.ts (1)

267-269: ⚠️ Potential issue

Error handling inconsistency

The method returns a Result type but still throws errors in the catch block instead of returning err().

Fix the inconsistency by returning an error result instead of throwing:

            } catch (error) {
                console.error("Failed to send message:", error);
-                throw error;
+                return err(String(error));
            }
♻️ Duplicate comments (17)
examples/example-node-worker/src/typescript/contracts.gen.ts (5)

7-13: Function uses inconsistent naming style

The function uses snake_case (build_actions_move_calldata) instead of camelCase which is the TypeScript convention. This is inconsistent with the exported function names in line 52-57 which use camelCase (buildMoveCalldata).

-const build_actions_move_calldata = (direction: CairoCustomEnum): DojoCall => {
+const buildActionsMoveCalldata = (direction: CairoCustomEnum): DojoCall => {
  return {
    contractName: "actions",
    entrypoint: "move",
    calldata: [direction],
  };
};

15-26: Function follows inconsistent naming and could use better error handling

The function uses snake_case which is inconsistent with TypeScript conventions. Additionally, the error handling could benefit from more specific error messages to aid debugging.

-const actions_move = async (snAccount: Account | AccountInterface, direction: CairoCustomEnum) => {
+const actionsMove = async (snAccount: Account | AccountInterface, direction: CairoCustomEnum) => {
  try {
    return await provider.execute(
      snAccount,
-      build_actions_move_calldata(direction),
+      buildActionsMoveCalldata(direction),
      "dojo_starter",
    );
  } catch (error) {
-    console.error(error);
+    console.error(`Error executing move action: ${error}`);
    throw error;
  }
};

28-34: Function uses inconsistent naming style

Similar to the previous function, this uses snake_case instead of the conventional camelCase in TypeScript.

-const build_actions_spawn_calldata = (): DojoCall => {
+const buildActionsSpawnCalldata = (): DojoCall => {
  return {
    contractName: "actions",
    entrypoint: "spawn",
    calldata: [],
  };
};

36-47: Function follows inconsistent naming and could use better error handling

Similar to actions_move, this function uses snake_case and has minimal error information in the error handling.

-const actions_spawn = async (snAccount: Account | AccountInterface) => {
+const actionsSpawn = async (snAccount: Account | AccountInterface) => {
  try {
    return await provider.execute(
      snAccount,
-      build_actions_spawn_calldata(),
+      buildActionsSpawnCalldata(),
      "dojo_starter",
    );
  } catch (error) {
-    console.error(error);
+    console.error(`Error executing spawn action: ${error}`);
    throw error;
  }
};

51-58: Inconsistency between function definitions and exports

The exported function names use camelCase but reference functions defined with snake_case. This inconsistency should be resolved.

return {
  actions: {
-    move: actions_move,
-    buildMoveCalldata: build_actions_move_calldata,
-    spawn: actions_spawn,
-    buildSpawnCalldata: build_actions_spawn_calldata,
+    move: actionsMove,
+    buildMoveCalldata: buildActionsMoveCalldata,
+    spawn: actionsSpawn,
+    buildSpawnCalldata: buildActionsSpawnCalldata,
  },
};
packages/sdk/src/web/experimental/index.ts (1)

95-95: Strengthen callback parameter types

For improved type safety, replace the any type on the entityData parameter with torii.Entity, which aligns with the parseEntities<T>(entities: torii.Entity[]) signature.

-                    (_: string, entityData: any) => {
+                    (_: string, entityData: torii.Entity) => {
🧰 Tools
🪛 Biome (1.9.4)

[error] 95-95: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

packages/sdk/src/internal/parseEntities.ts (1)

127-133: 🛠️ Refactor suggestion

Potential precision loss with u64 values

The function parses "u64" values as Number.parseInt which may cause precision loss for large values. JavaScript's Number type can safely represent integers up to 2^53-1, but u64 can go up to 2^64-1.

 switch (value.type_name) {
     case "u64":
-        return Number.parseInt(value.value as string, 16);
+        return BigInt(value.value as string);
     case "i256":
     case "i128":
     case "u256":
     case "u128":
         return BigInt(value.value as string);
examples/example-node-worker/main.ts (4)

21-25: Wrong global identifier: use WebSocket, not Websocket

Browsers (and most polyfills) expose global.WebSocket (uppercase S).
Assigning to Websocket leaves libraries that expect WebSocket undefined.

-// @ts-ignore
-global.Websocket = w3cwebsocket;
+// @ts-ignore
+global.WebSocket = w3cwebsocket;

125-134: initPositionFromEvent mutates the shared constant

pc is an alias of defaultPositionCount, so every call mutates the constant and all future callers see cumulative side-effects.

-const pc = defaultPositionCount;
+const pc: PositionCount = { ...defaultPositionCount };

175-176: Unreachable code after the first return

The second return [sub]; can never execute—Biome rightfully flags it.

-    return [];
-    return [sub];
+    return [sub];
🧰 Tools
🪛 Biome (1.9.4)

[error] 176-176: This code is unreachable

... because this statement will return from the function beforehand

(lint/correctness/noUnreachable)


137-151: 🛠️ Refactor suggestion

Potential data loss with data.pop()

data is already the argument supplied by the subscription.
Calling pop() both mutates the array and drops all but the last entity; if multiple entities arrive in the same batch they are silently ignored.

-        const entity = data.pop();
-        if (entity && entity.entityId !== "0x0") {
-            // do whatever you need here
-            const model = entity.models.dojo_starter;
-            if (model?.Moves) {
-                await publishOffchainPositionCount(model.Moves);
-            }
-        }
+        for (const entity of data) {
+            if (entity && entity.entityId !== "0x0") {
+                // do whatever you need here
+                const model = entity.models.dojo_starter;
+                if (model?.Moves) {
+                    await publishOffchainPositionCount(model.Moves);
+                }
+            }
+        }
packages/state/src/recs/index.ts (3)

307-333: ⚠️ Potential issue

Infinite pagination loop – fetch happens outside the while

fetchedEntities is requested once before the loop; inside the loop no new page is fetched, so cursor advances without fresh data, causing either an immediate exit or an infinite loop.

Move the client.getEntities call inside the while block (mirroring the pattern used in getEntities and getEvents).


246-247: ⚠️ Potential issue

Same array-length issue in getEvents

Replicate the fix applied above:

-        if (Object.keys(entities.items).length === 0) {
+        if (entities.items.length === 0) {

189-190: ⚠️ Potential issue

Incorrect length check for an array

entities.items is an array; using Object.keys is slower and semantically wrong.
Switch to .length directly.

-        if (Object.keys(entities.items).length < limit) {
+        if (entities.items.length < limit) {
examples/example-node-worker/src/typescript/models.gen.ts (1)

40-46: ⚠️ Potential issue

Fix typo posititionposition

The misspelled field propagates to interfaces, schema defaults and any generated clients, breaking type-safe access.

-	positition: Array<[BigNumberish, BigNumberish]>;
+	position: Array<[BigNumberish, BigNumberish]>;

Apply the same correction in PositionCountValue and the schema object.

Also applies to: 128-133

packages/sdk/src/web/index.ts (1)

271-278: 🛠️ Refactor suggestion

Potential type mismatch in signature handling

The code handles both array and non-array signature formats, but doesn't convert array elements to strings when signature is an array.

Ensure consistent string conversion for all signature formats:

                return ok(
                    await client.publishMessage(
                        dataString,
                        Array.isArray(signature)
-                            ? signature
+                            ? signature.map((x) => x.toString())
                            : [signature.r.toString(), signature.s.toString()]
                    )
                );
packages/sdk/src/internal/toriiQueryBuilder.ts (1)

133-142: ⚠️ Potential issue

Updated static pagination helper method signature

The withPagination static method now accepts cursor, limit, and direction parameters instead of page-based parameters, reflecting the new pagination approach.

There's a type issue flagged by the static analyzer:

[error] 133-133: Unexpected any. Specify a different type.

Consider specifying a more specific type for the generic parameter:

-    static withPagination<T extends Record<string, Record<string, any>>>(
+    static withPagination<T extends SchemaType>(
🧰 Tools
🪛 Biome (1.9.4)

[error] 133-133: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

🧹 Nitpick comments (10)
packages/sdk/src/__tests__/zustand.perf.test.ts (1)

131-133: Code readability improvement with multi-line property access

The refactoring improves readability by breaking the long property access chain into multiple lines, making it easier to understand the nested structure.

However, there are some issues that should be addressed:

  1. The code uses non-null assertions (!), which can lead to runtime errors if the properties are unexpectedly null.
  2. The string literal in bracket notation could be simplified if "entity500" is a valid identifier.

Consider replacing the non-null assertions with optional chaining and potentially simplify the bracket notation:

-                        draft.entities[
-                            "entity500"
-                        ].models.world!.item!.durability = 75;
+                        draft.entities["entity500"]?.models.world?.item?.durability = 75;

Or even better, if you want to handle the potential nullish values safely:

-                        draft.entities[
-                            "entity500"
-                        ].models.world!.item!.durability = 75;
+                        const entity = draft.entities["entity500"];
+                        if (entity?.models.world?.item) {
+                            entity.models.world.item.durability = 75;
+                        }
🧰 Tools
🪛 Biome (1.9.4)

[error] 131-133: Forbidden non-null assertion.

(lint/style/noNonNullAssertion)


[error] 131-133: Forbidden non-null assertion.

Unsafe fix: Replace with optional chain operator ?. This operator includes runtime checks, so it is safer than the compile-only non-null assertion operator

(lint/style/noNonNullAssertion)


[error] 132-132: The computed expression can be simplified without the use of a string literal.

Unsafe fix: Use a literal key instead.

(lint/complexity/useLiteralKeys)

examples/example-vite-react-sdk/src/historical-events.tsx (1)

1-5: Updated import to use specialized HistoricalToriiQueryBuilder

Good update to use the specialized HistoricalToriiQueryBuilder which better reflects the historical querying intent of this component.

Consider using import type for type-only imports to improve compilation efficiency:

-import {
-    KeysClause,
-    ParsedEntity,
-    HistoricalToriiQueryBuilder,
-} from "@dojoengine/sdk";
+import {
+    KeysClause,
+    HistoricalToriiQueryBuilder,
+} from "@dojoengine/sdk";
+import type { ParsedEntity } from "@dojoengine/sdk";
🧰 Tools
🪛 Biome (1.9.4)

[error] 1-5: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

packages/sdk/src/web/state/zustand.ts (1)

14-14: Centralized merge utilities for improved maintainability

Good refactoring to import centralized merge utilities instead of using local implementations. This promotes code reuse and maintainability.

Since MergedModels is only used as a type, consider using the import type syntax:

-import { deepMerge, MergedModels } from "../../internal/merge";
+import { deepMerge } from "../../internal/merge";
+import type { MergedModels } from "../../internal/merge";
🧰 Tools
🪛 Biome (1.9.4)

[error] 14-14: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

examples/example-node-worker/src/typescript/contracts.gen.ts (1)

1-3: Consider using import type for type-only imports

For better tree-shaking and to ensure imported types are removed during compilation, consider using the import type syntax for type imports.

-import { DojoProvider, DojoCall } from "@dojoengine/core";
-import { Account, AccountInterface, BigNumberish, CairoOption, CairoCustomEnum, ByteArray } from "starknet";
+import type { DojoProvider, DojoCall } from "@dojoengine/core";
+import type { Account, AccountInterface, BigNumberish, CairoOption, CairoCustomEnum, ByteArray } from "starknet";
 import * as models from "./models.gen";
🧰 Tools
🪛 Biome (1.9.4)

[error] 1-1: All these imports are only used as types.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Use import type.

(lint/style/useImportType)


[error] 2-2: Some named imports are only used as types.

This import is only used as a type.

This import is only used as a type.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

packages/state/src/recs/index.ts (1)

185-185: Avoid unnecessary template literal

console.log does not interpolate any variables here, so a simple string literal is clearer and avoids the Biome warning.

-        if (logging) console.log(`Fetched entities`, entities.items);
+        if (logging) console.log("Fetched entities", entities.items);
🧰 Tools
🪛 Biome (1.9.4)

[error] 185-185: Do not use template literals if interpolation and special-character handling are not needed.

Unsafe fix: Replace with string literal

(lint/style/noUnusedTemplateLiteral)

packages/sdk/src/internal/merge.ts (2)

76-83: Avoid any in new enum construction

Using Record<string, any> loses type safety and is flagged by Biome.
Since variant keys come from CairoCustomEnum["variant"], you can preserve types:

-const newEnumContent: Record<string, any> = {};
+const newEnumContent: { [K in keyof typeof target.variant]?: typeof sourceValue } = {};

Also applies to: 96-103

🧰 Tools
🪛 Biome (1.9.4)

[error] 76-76: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)


133-155: Recursive merge mutates nested object type

result[key] = deepMerge(target[key], source[key]);
The recursive call returns MergedModels<T> but result is typed as Record<string, any>, losing compile-time safety and raising any warnings downstream.

Introduce a generic helper and tighten the result type:

-const result = { ...target } as Record<string, any>;
+const result: typeof target = { ...target };
🧰 Tools
🪛 Biome (1.9.4)

[error] 133-133: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

examples/example-node-worker/src/typescript/models.gen.ts (1)

3-3: Import only types where possible

BigNumberish is a type-only import; switch to import type to reduce bundle size.

-import { CairoCustomEnum, CairoOption, CairoOptionVariant, BigNumberish } from 'starknet';
+import { CairoCustomEnum, CairoOption, CairoOptionVariant } from 'starknet';
+import type { BigNumberish } from 'starknet';
🧰 Tools
🪛 Biome (1.9.4)

[error] 3-3: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

packages/sdk/src/web/index.ts (1)

236-247: Updated generateTypedData signature

The method signature has changed to:

  • Remove the optional domain parameter
  • Add an optional additionnalTypes parameter
  • Always use options.domain as the domain value

This is more consistent but could be a breaking change for code that explicitly passed a different domain.

Consider fixing the typo in the parameter name:

-            additionnalTypes?: Record<
+            additionalTypes?: Record<
packages/sdk/src/node/index.ts (1)

242-242: Unused account parameter

The _account parameter is accepted but unused (indicated by the underscore prefix), as the method now relies on options.signer and options.identity.

Consider removing the parameter entirely or clarifying its purpose through better documentation, since it's confusing to accept a parameter that's not used.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro (Legacy)

📥 Commits

Reviewing files that changed from the base of the PR and between ff82d6f and 34c97ec.

⛔ Files ignored due to path filters (5)
  • examples/example-vite-react-phaser-recs/src/dojo/generated/setup.ts is excluded by !**/generated/**
  • examples/example-vite-react-pwa-recs/src/dojo/generated/setup.ts is excluded by !**/generated/**
  • examples/example-vite-react-threejs-recs/src/dojo/generated/setup.ts is excluded by !**/generated/**
  • examples/example-vue-app-recs/src/dojo/generated/setup.ts is excluded by !**/generated/**
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (43)
  • .changeset/thirty-cheetahs-design.md (1 hunks)
  • .github/workflows/ci.yaml (1 hunks)
  • .github/workflows/release.yaml (6 hunks)
  • examples/example-node-worker/dojoConfig.ts (1 hunks)
  • examples/example-node-worker/env.ts (1 hunks)
  • examples/example-node-worker/main.ts (1 hunks)
  • examples/example-node-worker/package.json (1 hunks)
  • examples/example-node-worker/src/typescript/contracts.gen.ts (1 hunks)
  • examples/example-node-worker/src/typescript/models.gen.ts (1 hunks)
  • examples/example-node-worker/tsconfig.json (1 hunks)
  • examples/example-nodejs-bot/package.json (1 hunks)
  • examples/example-vite-react-app-recs/src/dojo/setup.ts (2 hunks)
  • examples/example-vite-react-sdk/src/historical-events.tsx (2 hunks)
  • package.json (1 hunks)
  • packages/core/package.json (1 hunks)
  • packages/react/package.json (2 hunks)
  • packages/sdk/package.json (3 hunks)
  • packages/sdk/src/__tests__/parseEntities.test.ts (7 hunks)
  • packages/sdk/src/__tests__/parseHistoricalEvents.test.ts (0 hunks)
  • packages/sdk/src/__tests__/state.test.ts (3 hunks)
  • packages/sdk/src/__tests__/toriiQueryBuilder.test.ts (6 hunks)
  • packages/sdk/src/__tests__/zustand.perf.test.ts (1 hunks)
  • packages/sdk/src/internal/errors.ts (1 hunks)
  • packages/sdk/src/internal/generateTypedData.ts (2 hunks)
  • packages/sdk/src/internal/merge.ts (1 hunks)
  • packages/sdk/src/internal/models.ts (1 hunks)
  • packages/sdk/src/internal/parseEntities.ts (4 hunks)
  • packages/sdk/src/internal/parseHistoricalEvents.ts (0 hunks)
  • packages/sdk/src/internal/toriiQueryBuilder.ts (6 hunks)
  • packages/sdk/src/internal/types.ts (5 hunks)
  • packages/sdk/src/node/index.ts (12 hunks)
  • packages/sdk/src/node/worker.ts (1 hunks)
  • packages/sdk/src/web/experimental/index.ts (3 hunks)
  • packages/sdk/src/web/index.ts (11 hunks)
  • packages/sdk/src/web/react/hooks/entities.ts (1 hunks)
  • packages/sdk/src/web/react/hooks/events.ts (3 hunks)
  • packages/sdk/src/web/react/hooks/hooks.ts (6 hunks)
  • packages/sdk/src/web/state/zustand.ts (1 hunks)
  • packages/state/src/recs/index.ts (6 hunks)
  • packages/torii-client/package.json (2 hunks)
  • packages/torii-wasm/dojo.c (1 hunks)
  • turbo.json (1 hunks)
  • worlds/onchain-dash (1 hunks)
💤 Files with no reviewable changes (2)
  • packages/sdk/src/tests/parseHistoricalEvents.test.ts
  • packages/sdk/src/internal/parseHistoricalEvents.ts
✅ Files skipped from review due to trivial changes (1)
  • .changeset/thirty-cheetahs-design.md
🚧 Files skipped from review as they are similar to previous changes (26)
  • package.json
  • worlds/onchain-dash
  • packages/torii-wasm/dojo.c
  • packages/sdk/package.json
  • examples/example-node-worker/dojoConfig.ts
  • packages/torii-client/package.json
  • examples/example-node-worker/env.ts
  • examples/example-vite-react-app-recs/src/dojo/setup.ts
  • .github/workflows/ci.yaml
  • packages/react/package.json
  • turbo.json
  • packages/core/package.json
  • packages/sdk/src/node/worker.ts
  • packages/sdk/src/tests/parseEntities.test.ts
  • packages/sdk/src/internal/errors.ts
  • packages/sdk/src/web/react/hooks/entities.ts
  • packages/sdk/src/tests/state.test.ts
  • .github/workflows/release.yaml
  • packages/sdk/src/internal/generateTypedData.ts
  • examples/example-node-worker/tsconfig.json
  • packages/sdk/src/tests/toriiQueryBuilder.test.ts
  • examples/example-nodejs-bot/package.json
  • packages/sdk/src/web/react/hooks/events.ts
  • examples/example-node-worker/package.json
  • packages/sdk/src/internal/types.ts
  • packages/sdk/src/internal/models.ts
🧰 Additional context used
🧬 Code Graph Analysis (8)
examples/example-node-worker/main.ts (9)
packages/sdk/src/node/index.ts (1)
  • init (43-417)
examples/example-node-worker/dojoConfig.ts (1)
  • dojoConfig (7-9)
examples/example-node-worker/env.ts (1)
  • env (9-9)
examples/example-node-worker/src/typescript/models.gen.ts (3)
  • PositionCount (38-41)
  • Moves (17-22)
  • SchemaType (80-94)
packages/sdk/src/internal/toriiQueryBuilder.ts (2)
  • ToriiQueryBuilder (24-143)
  • HistoricalToriiQueryBuilder (149-159)
packages/sdk/src/internal/types.ts (3)
  • ToriiQueryBuilder (557-557)
  • ParsedEntity (228-237)
  • SchemaType (50-70)
packages/sdk/src/node/clauseBuilder.ts (1)
  • KeysClause (46-52)
packages/sdk/src/internal/models.ts (1)
  • getModel (38-52)
packages/sdk/src/node/worker.ts (1)
  • createWorker (25-44)
packages/sdk/src/node/index.ts (6)
packages/sdk/src/internal/convertClauseToEntityKeysClause.ts (1)
  • intoEntityKeysClause (4-36)
packages/sdk/src/internal/parseEntities.ts (1)
  • parseEntities (10-59)
packages/sdk/src/internal/types.ts (2)
  • ToriiResponse (248-253)
  • ToriiQueryBuilder (557-557)
packages/sdk/src/internal/generateTypedData.ts (1)
  • generateTypedData (13-48)
packages/sdk/src/internal/errors.ts (2)
  • NO_SIGNER (1-1)
  • NO_IDENTITY (2-2)
packages/sdk/src/internal/toriiQueryBuilder.ts (1)
  • ToriiQueryBuilder (24-143)
packages/sdk/src/web/index.ts (5)
packages/sdk/src/internal/parseEntities.ts (1)
  • parseEntities (10-59)
packages/sdk/src/internal/convertClauseToEntityKeysClause.ts (1)
  • intoEntityKeysClause (4-36)
packages/sdk/src/internal/generateTypedData.ts (1)
  • generateTypedData (13-48)
packages/sdk/src/internal/errors.ts (1)
  • NO_ACCOUNT (3-3)
packages/sdk/src/internal/toriiQueryBuilder.ts (1)
  • ToriiQueryBuilder (24-143)
examples/example-node-worker/src/typescript/contracts.gen.ts (2)
examples/example-node-worker/src/typescript/models.gen.ts (1)
  • direction (71-76)
packages/core/src/types/index.ts (1)
  • DojoCall (130-134)
examples/example-node-worker/src/typescript/models.gen.ts (1)
packages/sdk/src/internal/types.ts (1)
  • SchemaType (50-70)
examples/example-vite-react-sdk/src/historical-events.tsx (1)
packages/sdk/src/internal/toriiQueryBuilder.ts (1)
  • HistoricalToriiQueryBuilder (149-159)
packages/sdk/src/internal/merge.ts (3)
packages/sdk/src/web/queryBuilder.ts (1)
  • value (191-193)
examples/example-node-worker/src/typescript/models.gen.ts (1)
  • SchemaType (80-94)
packages/sdk/src/internal/types.ts (2)
  • SchemaType (50-70)
  • ParsedEntity (228-237)
packages/state/src/recs/index.ts (1)
packages/core/src/provider/DojoProvider.ts (1)
  • entities (104-123)
🪛 Biome (1.9.4)
packages/sdk/src/web/experimental/index.ts

[error] 95-95: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

examples/example-node-worker/main.ts

[error] 176-176: This code is unreachable

... because this statement will return from the function beforehand

(lint/correctness/noUnreachable)

packages/sdk/src/internal/toriiQueryBuilder.ts

[error] 133-133: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

examples/example-node-worker/src/typescript/contracts.gen.ts

[error] 1-1: All these imports are only used as types.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Use import type.

(lint/style/useImportType)


[error] 2-2: Some named imports are only used as types.

This import is only used as a type.

This import is only used as a type.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

examples/example-node-worker/src/typescript/models.gen.ts

[error] 3-3: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

examples/example-vite-react-sdk/src/historical-events.tsx

[error] 1-5: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

packages/sdk/src/__tests__/zustand.perf.test.ts

[error] 131-133: Forbidden non-null assertion.

(lint/style/noNonNullAssertion)


[error] 131-133: Forbidden non-null assertion.

Unsafe fix: Replace with optional chain operator ?. This operator includes runtime checks, so it is safer than the compile-only non-null assertion operator

(lint/style/noNonNullAssertion)


[error] 132-132: The computed expression can be simplified without the use of a string literal.

Unsafe fix: Use a literal key instead.

(lint/complexity/useLiteralKeys)

packages/sdk/src/internal/merge.ts

[error] 76-76: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)


[error] 96-96: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)


[error] 133-133: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

packages/sdk/src/internal/parseEntities.ts

[error] 50-50: Do not use template literals if interpolation and special-character handling are not needed.

Unsafe fix: Replace with string literal

(lint/style/noUnusedTemplateLiteral)

packages/sdk/src/web/react/hooks/hooks.ts

[error] 72-72: Forbidden non-null assertion.

(lint/style/noNonNullAssertion)

packages/sdk/src/web/state/zustand.ts

[error] 14-14: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

packages/state/src/recs/index.ts

[error] 185-185: Do not use template literals if interpolation and special-character handling are not needed.

Unsafe fix: Replace with string literal

(lint/style/noUnusedTemplateLiteral)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build
🔇 Additional comments (30)
examples/example-vite-react-sdk/src/historical-events.tsx (1)

14-14: Using HistoricalToriiQueryBuilder instead of ToriiQueryBuilder

The component now correctly uses the specialized HistoricalToriiQueryBuilder which has the historical flag set to true by default, simplifying the query building process.

packages/sdk/src/web/react/hooks/hooks.ts (4)

7-7: Added ParsedEntity to imports

Good addition of the ParsedEntity type to the imports, necessary for the type changes in the component.


18-18: Added trailing comma after type parameter

Minor stylistic change that helps with cleaner git diffs for future changes to type parameters.


32-33: Updated callback types to handle arrays of ParsedEntity

This is a significant change that aligns with the SDK's shift towards handling arrays of parsed entities rather than objects keyed by entity IDs.


65-67: Updated data processing to cast to ParsedEntity arrays

The changes ensure that data is properly cast to the expected ParsedEntity<Schema>[] type in the callbacks.

Also applies to: 75-77, 90-92

packages/sdk/src/web/experimental/index.ts (4)

27-27: Client response structure change

The code now extracts .items from the response before parsing. This indicates a change in the return structure of client.getEntities. Ensure all client usage is updated consistently.


38-38: Changed query property name

The property name has changed from dont_include_hashed_keys to no_hashed_keys. Ensure all client code is updated to use the new property name.


53-59: Changed entity update callback to handle arrays

The callback now processes arrays of entities instead of objects keyed by entity ID. This is a breaking change that affects how developers handle entity updates in their code.


23-23:

❓ Verification inconclusive

Client initialization changed from async to sync

The client initialization was changed from asynchronous to synchronous, which is a significant change. Make sure this is intentional and that no initialization logic that requires awaiting is lost.


🏁 Script executed:

#!/bin/bash
# Check for any potential async initialization in ToriiClient that might be bypassed
rg -B5 -A2 "ToriiClient" --multiline packages/

Length of output: 10696


Confirm synchronous ToriiClient initialization

The experimental Web SDK now instantiates the Torii client without await:

// packages/sdk/src/web/experimental/index.ts:23
const client = new torii.ToriiClient(clientConfig);

However, the existing Web (packages/sdk/src/web/index.ts) and Node (packages/sdk/src/node/index.ts) entrypoints use:

const client = await new torii.ToriiClient(config);

Please verify that the ToriiClient constructor does not perform any asynchronous setup (i.e., it doesn’t return a Promise requiring await). If it does, restore the await to ensure all initialization logic completes before use.

packages/sdk/src/internal/parseEntities.ts (3)

17-20: Updated iteration logic for entity arrays

The function now iterates over an array of entities instead of a keyed object, extracting entityId from hashed_keys and entityData from models. This change reflects a fundamental shift in how entity data is structured.


90-93: Added support for tuple type

The function now handles the "tuple" type similar to "array" when parsing values. This enhances compatibility with Cairo types.


10-13:

❌ Incorrect review comment

Function signature change impacts all callers

The function now accepts an array of torii.Entity objects instead of a keyed object. This is a major API change that affects all callers of this function.


🏁 Script executed:

#!/bin/bash
# Check how widespread the usage of parseEntities is
echo "=== Searching for parseEntities calls ==="
rg "parseEntities\(" --no-ignore packages/ examples/ -B2 -A2

Length of output: 4182


No breaking change detected for parseEntities
All callers already pass arrays of torii.Entity to parseEntities, so the updated signature aligns with existing usage. You can disregard the previous API-impact concern.

• Usage locations confirmed:
– packages/sdk/src/node/index.ts
– packages/sdk/src/web/index.ts
– packages/sdk/src/web/experimental/index.ts
– packages/sdk/src/tests/parseEntities.test.ts

Likely an incorrect or invalid review comment.

examples/example-node-worker/main.ts (1)

27-42: Comprehensive SDK initialization with identity and signer

The SDK initialization now includes domain metadata, identity, and signer configuration. This is a good practice for proper authentication and message signing.

packages/state/src/recs/index.ts (1)

168-179:

❓ Verification inconclusive

Verify order_by nesting

The Torii client previously accepted order_by at the root level, not inside pagination. Placing it under pagination may be ignored by the API.

Run the following quick check against the client types:

Also applies to: 230-240


🏁 Script executed:

#!/bin/bash
# Inspect ToriiClient method signature to confirm pagination shape
grep -R --line-number "interface Pagination" $(npm root)/@dojoengine/torii-client | head

Length of output: 212


Verify placement of order_by in getEntities call

The Torii client’s getEntities API may expect the order_by parameter at the top level of the request rather than nested inside pagination. If it’s nested, it could be silently ignored. Please confirm the request shape in the Torii client type definitions and adjust accordingly:

- await client.getEntities({
-   pagination: {
-     limit,
-     cursor,
-     direction: "Forward",
-     order_by: orderBy,
-   },
-   clause: clause || undefined,
-   no_hashed_keys: false,
-   models: entityModels,
-   historical,
- });
+ await client.getEntities({
+   pagination: {
+     limit,
+     cursor,
+     direction: "Forward",
+   },
+   order_by: orderBy,
+   clause: clause || undefined,
+   no_hashed_keys: false,
+   models: entityModels,
+   historical,
+ });

Also applies to lines 230–240.

packages/sdk/src/internal/merge.ts (1)

127-132: Type guard ordering in deepMerge

The function first checks for Cairo-specific types, but when only one side is a CairoOption/CairoCustomEnum the merge falls through to generic object logic, returning an object instead of the expected wrapped type, potentially violating invariants.

Consider early-returning the target when the wrappers differ:

if (isCairoOption(target) !== isCairoOption(source) ||
    isCairoCustomEnum(target) !== isCairoCustomEnum(source)) {
  return target;
}
packages/sdk/src/web/index.ts (5)

30-31: Good addition of error handling utilities

Added error handling using the neverthrow library for functional error handling with Result types, and imported error constants from a centralized location.


257-263: Improved error handling in sendMessage

The method now accepts an optional account parameter and returns a Result type instead of throwing errors directly when no account is provided. This is a good improvement for error handling consistency.


92-94: Updated entity and event parsing to use items array

Entity and event parsing now consistently access the .items property of the response before parsing. This aligns with changes in response data structure from the Torii client.

Also applies to: 147-149, 206-206, 219-221


99-104: Updated subscription callbacks to parse single entities

Subscription callbacks now parse arrays containing a single entity instead of objects keyed by ID, reflecting the updated data shape returned by the client.

Also applies to: 158-160


48-48:

✅ Verification successful

Client initialization now uses direct instantiation

Changed from torii.createClient(config) to direct instantiation with new torii.ToriiClient(config). This aligns with similar changes in the node SDK and experimental web SDK.


🏁 Script executed:

#!/bin/bash
# Check if all client initializations are consistent across the codebase
rg -A 2 -B 2 "torii\.ToriiClient" --glob "*.ts"

Length of output: 5797


Client instantiation consistency verified

All client initializations across the SDKs and example apps now use direct instantiation via new torii.ToriiClient(config), matching the node SDK and experimental web SDK. No further changes required.

packages/sdk/src/internal/toriiQueryBuilder.ts (3)

43-55: Transition from offset to cursor-based pagination

The withOffset method is now deprecated in favor of the new withCursor method, reflecting a shift to cursor-based pagination which is more efficient for large datasets.


57-62: Added direction support for pagination

New withDirection method supports specifying the traversal direction in cursor-based pagination, which is useful for implementing infinite scrolling in both directions.


145-159: Added HistoricalToriiQueryBuilder class

New specialized class that extends ToriiQueryBuilder with the historical flag set to true by default, making it more convenient to work with historical queries.

packages/sdk/src/node/index.ts (7)

50-50: Client initialization now uses direct instantiation

Changed from torii.createClient(config) to direct instantiation with new torii.ToriiClient(config). This aligns with similar changes in the web SDK.


34-36: Added exports for worker module and other utilities

New exports for worker, types, and models modules enhance the SDK's functionality and make it easier to use these modules from external code.


244-249: Improved error handling in sendMessage

Now checks for the presence of options.signer and options.identity and returns a Result type instead of throwing errors. This is a significant improvement for error handling.


251-265: Updated message signing approach

Now uses torii.TypedData encoding with the configured identity and signer, which provides a more streamlined approach to message signing.


72-74: Updated entity and event parsing to use items array

Entity and event parsing now consistently access the .items property of the response before parsing, reflecting changes in the Torii client response structure.

Also applies to: 127-130, 189-189, 202-204


79-84: Updated subscription callbacks to parse single entities

Subscription callbacks now parse arrays containing a single entity instead of objects keyed by ID, reflecting the updated data shape returned by the client.

Also applies to: 138-143


219-230: Updated generateTypedData signature

The method signature has changed to:

  • Remove the optional domain parameter
  • Add an optional additionalTypes parameter
  • Always use options.domain as the domain value

This is more consistent with the web SDK implementation.

Comment on lines +121 to 123
// subscriptionRef.current?.free();
// subscriptionRef.current = null;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Cleanup function is commented out

The cleanup function that should free the subscription reference is commented out, which could lead to memory leaks.

Uncomment and fix the cleanup function to properly free resources:

-                    // subscriptionRef.current?.free();
-                    // subscriptionRef.current = null;
+                    subscriptionRef.current.free();
+                    subscriptionRef.current = null;

Comment on lines +29 to 32
getEvents: async (query: torii.Query) => {
const events = await client.getEventMessages(query);
return parseEntities(events.items);
},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Removed historical parameter in getEvents

The historical parameter has been removed from the getEvents function. Make sure this change is intentional and documented, as it may affect backward compatibility.


🏁 Script executed:

#!/bin/bash
# Check for usages of getEvents with historical parameter that might break
rg "getEvents.*historical.*true" -B1 -A1 --exclude="*.ts.old" examples/ | grep -v "^binary file"

Length of output: 510


🏁 Script executed:

#!/bin/bash
# Search for getEvents calls with multiple parameters (potential historical flag usage)
echo "=== Multi-argument getEvents calls ==="
rg -n "getEvents\(" . | grep "," || echo "None found"

# Search specifically for 'historical' coupled with getEvents
echo -e "\n=== 'historical' in getEvents contexts ==="
rg -n "historical" . | grep "getEvents" || echo "None found"

Length of output: 326


Document removal of historical parameter in getEvents

We’ve confirmed there are no internal calls passing a historical flag to getEvents, but its removal is a breaking change for consumers. Please update your public-facing documentation and release notes to reflect this.

• File: packages/sdk/src/web/experimental/index.ts (lines 29–32)

  • getEvents(query: torii.Query) no longer accepts a historical argument.
    • Action: Add a note in CHANGELOG.md and the SDK migration guide about the removed historical parameter.

Comment on lines 181 to +189
if (dbConnection) {
await insertEntitiesInDB(dbConnection, entities);
await insertEntitiesInDB(dbConnection, entities.items);
}

if (logging) console.log(`Fetched entities`, entities);
if (logging) console.log(`Fetched entities`, entities.items);

setEntities(entities, components, logging);
setEntities(entities.items, components, logging);

if (Object.keys(entities).length < limit) {
if (Object.keys(entities.items).length < limit) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Database insert treats entity array as object

insertEntitiesInDB receives an array but iterates with Object.entries, producing numeric indices instead of real entity IDs.
Store the actual hashed_keys (or another stable ID) and iterate directly:

-for (const [entityId, data] of Object.entries(entities)) {
-    const entityData = { id: entityId, ...data };
+for (const entity of entities) {
+    const entityId = entity.hashed_keys?.join(".") ?? crypto.randomUUID();
+    const entityData = { id: entityId, ...entity };
     const request = store.put(entityData);

Also applies to: 541-553

🧰 Tools
🪛 Biome (1.9.4)

[error] 185-185: Do not use template literals if interpolation and special-character handling are not needed.

Unsafe fix: Replace with string literal

(lint/style/noUnusedTemplateLiteral)

@@ -49,7 +75,7 @@
* HashedKeys represent internal torii entity id.
*/
includeHashedKeys(): ToriiQueryBuilder<T> {
this.query.dont_include_hashed_keys = false;
this.query.no_hashed_keys = false;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Renamed properties for improved clarity

Properties were renamed for consistency and clarity:

  • dont_include_hashed_keysno_hashed_keys
  • entity_modelsmodels

These changes make the API more intuitive but are breaking changes.

Ensure these breaking changes are documented in the migration guide.

Also applies to: 110-110, 118-118

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🔭 Outside diff range comments (3)
packages/state/src/recs/index.ts (1)

541-553: ⚠️ Potential issue

Database insert treats entity array as object

insertEntitiesInDB receives an array but iterates with Object.entries, producing numeric indices instead of real entity IDs.
Store the actual hashed_keys (or another stable ID) and iterate directly:

-for (const [entityId, data] of Object.entries(entities)) {
-    const entityData = {
-        id: entityId,
-        ...data,
-    };
+for (const entity of entities) {
+    const entityId = entity.hashed_keys?.join(".") ?? crypto.randomUUID();
+    const entityData = {
+        id: entityId,
+        ...entity,
+    };
     const request = store.put(entityData);
packages/sdk/src/web/index.ts (1)

257-282: ⚠️ Potential issue

Handle errors consistently with Result in sendMessage

The function returns a Result type but still throws in the catch block, which defeats the purpose of using Result for error handling. Ensure consistency by always returning err instead of throwing.

         } catch (error) {
             console.error("Failed to send message:", error);
-            throw error;
+            return err(String(error));
         }
packages/sdk/src/node/index.ts (1)

240-270: 🛠️ Refactor suggestion

Improved error handling in sendMessage method

The sendMessage method now:

  1. Checks for the presence of signer and identity before attempting to send
  2. Returns a Result type (from neverthrow) with ok/err instead of throwing exceptions
  3. No longer requires an explicit Account parameter, relying on options.signer and options.identity

The function still throws an error in the catch block despite using Result for other error cases. Consider making the error handling consistent by returning err in the catch block as well:

try {
    const td = new torii.TypedData(JSON.stringify(data)).encode(
        options.identity
    );

    const sig = options.signer.sign(td);

    const dataString = JSON.stringify(data);

    return ok(
        await client.publishMessage(dataString, [
            sig.r.toString(),
            sig.s.toString(),
        ])
    );
} catch (error) {
    console.error("Failed to send message:", error);
-    throw error;
+    return err(`Failed to send message: ${error instanceof Error ? error.message : String(error)}`);
}
♻️ Duplicate comments (23)
examples/example-node-worker/package.json (1)

6-10: ⚠️ Potential issue

Remove hardcoded credentials from scripts

The scripts contain hardcoded private keys and identity values, which is a security risk. These should be moved to environment variables loaded from a .env file instead.

    "scripts": {
-        "bun": "IDENTITY=0x0127fd5f1fe78a71f8bcd1fec63e3fe2f0486b6ecd5c86a0466c3a21fa5cfcec SECRET_KEY=0xc5b2fcab997346f3ea1c00b002ecf6f382c5f9c9659a3894eb783c5320f912 bun run main.ts",
-        "node": "IDENTITY=0x0127fd5f1fe78a71f8bcd1fec63e3fe2f0486b6ecd5c86a0466c3a21fa5cfcec SECRET_KEY=0xc5b2fcab997346f3ea1c00b002ecf6f382c5f9c9659a3894eb783c5320f912 node main.ts",
+        "bun": "bun run main.ts",
+        "node": "node main.ts",
        "build": "tsc -b"
    },

Since dotenv is already added as a dependency (line 15), use it to load these values from a .env file instead, which should be added to .gitignore.

🧰 Tools
🪛 Gitleaks (8.21.2)

7-7: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


8-8: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

packages/sdk/src/web/react/hooks/hooks.ts (1)

121-123: ⚠️ Potential issue

Cleanup function is still commented out

The cleanup function that should free the subscription reference remains commented out, which could lead to memory leaks.

Uncomment and fix the cleanup function to properly free resources:

-                    // subscriptionRef.current?.free();
-                    // subscriptionRef.current = null;
+                    subscriptionRef.current?.free();
+                    subscriptionRef.current = null;
examples/example-node-worker/src/typescript/contracts.gen.ts (5)

7-13: Function uses inconsistent naming style

The function uses snake_case (build_actions_move_calldata) instead of camelCase, which is the TypeScript convention. This is inconsistent with the exported function names in line 52-57 which use camelCase (buildMoveCalldata).

-const build_actions_move_calldata = (direction: CairoCustomEnum): DojoCall => {
+const buildActionsMoveCalldata = (direction: CairoCustomEnum): DojoCall => {
  return {
    contractName: "actions",
    entrypoint: "move",
    calldata: [direction],
  };
};

15-26: Function follows inconsistent naming and could use better error handling

The function uses snake_case which is inconsistent with TypeScript conventions. Additionally, the error handling could benefit from more specific error messages to aid debugging.

-const actions_move = async (snAccount: Account | AccountInterface, direction: CairoCustomEnum) => {
+const actionsMove = async (snAccount: Account | AccountInterface, direction: CairoCustomEnum) => {
  try {
    return await provider.execute(
      snAccount,
-      build_actions_move_calldata(direction),
+      buildActionsMoveCalldata(direction),
      "dojo_starter",
    );
  } catch (error) {
-    console.error(error);
+    console.error(`Error executing move action: ${error}`);
    throw error;
  }
};

28-34: Function uses inconsistent naming style

Similar to the previous function, this uses snake_case instead of the conventional camelCase in TypeScript.

-const build_actions_spawn_calldata = (): DojoCall => {
+const buildActionsSpawnCalldata = (): DojoCall => {
  return {
    contractName: "actions",
    entrypoint: "spawn",
    calldata: [],
  };
};

36-47: Function follows inconsistent naming and could use better error handling

Similar to actions_move, this function uses snake_case and has minimal error information in the error handling.

-const actions_spawn = async (snAccount: Account | AccountInterface) => {
+const actionsSpawn = async (snAccount: Account | AccountInterface) => {
  try {
    return await provider.execute(
      snAccount,
-      build_actions_spawn_calldata(),
+      buildActionsSpawnCalldata(),
      "dojo_starter",
    );
  } catch (error) {
-    console.error(error);
+    console.error(`Error executing spawn action: ${error}`);
    throw error;
  }
};

52-57: Inconsistency between function definitions and exports

The exported function names use camelCase but reference functions defined with snake_case. This inconsistency should be resolved to improve maintainability.

return {
  actions: {
-    move: actions_move,
-    buildMoveCalldata: build_actions_move_calldata,
-    spawn: actions_spawn,
-    buildSpawnCalldata: build_actions_spawn_calldata,
+    move: actionsMove,
+    buildMoveCalldata: buildActionsMoveCalldata,
+    spawn: actionsSpawn,
+    buildSpawnCalldata: buildActionsSpawnCalldata,
  },
};
packages/sdk/src/web/experimental/index.ts (2)

29-32: Removed historical parameter from getEvents

The historical parameter has been removed from the getEvents function, which is a breaking change for API consumers.

This change should be documented in the CHANGELOG.md and migration guide. Please verify if there are any existing calls that might be affected:

#!/bin/bash
# Check for usages of getEvents with historical parameter
grep -r "getEvents.*historical" --include="*.ts" --include="*.tsx" examples/

95-95: Strengthen callback parameter types

For improved type safety, replace the any type on the entityData parameter with torii.Entity, which aligns with the parseEntities<T>(entities: torii.Entity[]) signature.

-                    (_: string, entityData: any) => {
+                    (_: string, entityData: torii.Entity) => {
🧰 Tools
🪛 Biome (1.9.4)

[error] 95-95: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

packages/sdk/src/internal/parseEntities.ts (1)

126-133: ⚠️ Potential issue

u64 can overflow JS number; parse it as BigInt

A 64-bit unsigned integer can exceed Number.MAX_SAFE_INTEGER (2⁵³-1), leading to precision loss if parsed with Number.parseInt. It should be treated the same way as the larger integer types.

-        case "u64":
-            return Number.parseInt(value.value as string, 16);
-        case "i256":
-        case "i128":
-        case "u256":
-        case "u128":
-            return BigInt(value.value as string);
+        case "u64":
+        case "i256":
+        case "i128":
+        case "u256":
+        case "u128":
+            return BigInt(value.value as string);
examples/example-node-worker/main.ts (4)

21-25: Wrong global identifier: use WebSocket, not Websocket

Browsers (and most polyfills) expose global.WebSocket (uppercase S).
Assigning to Websocket leaves libraries that expect WebSocket undefined.

-// @ts-ignore
-global.Websocket = w3cwebsocket;
+// @ts-ignore
+global.WebSocket = w3cwebsocket;

125-133: initPositionFromEvent mutates the shared constant

pc is an alias of defaultPositionCount, so every call mutates the constant and all future callers see cumulative side-effects.

-const pc = defaultPositionCount;
+const pc: PositionCount = { ...defaultPositionCount };

This also prevents accidental modification of the default object elsewhere.


143-151: Potential data loss with data.pop()

data is already the argument supplied by the subscription.
Calling pop() both mutates the array and drops all but the last entity; if multiple entities arrive in the same batch they are silently ignored.

-        const entity = data.pop();
-        if (entity && entity.entityId !== "0x0") {
-            // do whatever you need here
-            const model = entity.models.dojo_starter;
-            if (model?.Moves) {
-                await publishOffchainPositionCount(model.Moves);
-            }
-        }
+        for (const entity of data) {
+            if (entity.entityId !== "0x0") {
+                // do whatever you need here
+                const model = entity.models.dojo_starter;
+                if (model?.Moves) {
+                    await publishOffchainPositionCount(model.Moves);
+                }
+            }
+        }

175-176: Unreachable code after the first return

The second return [sub]; can never execute—Biome rightfully flags it.

-    return [];
-    return [sub];
+    return [sub];
🧰 Tools
🪛 Biome (1.9.4)

[error] 176-176: This code is unreachable

... because this statement will return from the function beforehand

(lint/correctness/noUnreachable)

packages/state/src/recs/index.ts (1)

307-333: ⚠️ Potential issue

Infinite loop in getEntitiesQuery

Inside the while (continueFetching) loop you never issue a new client.getEntities call—fetchedEntities is fetched once outside the loop, so cursor advances but data doesn't refresh.

-    const fetchedEntities = await client.getEntities({
-        pagination: {
-            limit,
-            cursor,
-            direction: "Forward",
-            order_by: orderBy,
-        },
-        clause: clause || undefined,
-        no_hashed_keys: false,
-        models: entityModels,
-        historical,
-    });
-
-    while (continueFetching) {
+    while (continueFetching) {
+        const fetchedEntities = await client.getEntities({
+            pagination: {
+                limit,
+                cursor,
+                direction: "Forward",
+                order_by: orderBy,
+            },
+            clause: clause || undefined,
+            no_hashed_keys: false,
+            models: entityModels,
+            historical,
+        });
packages/sdk/src/internal/merge.ts (1)

123-160: 🛠️ Refactor suggestion

Add unit tests for deepMerge in packages/sdk/src/internal/merge.ts

No tests currently cover this function. Please add a new test suite (e.g. packages/sdk/src/internal/tests/merge.test.ts) that verifies:

  • Merging of primitive values (overwrites correctly)
  • Recursive merging of nested objects
  • Handling of arrays (should be overwritten, not deep-merged)
  • Treatment of null and undefined properties
  • Behavior with custom Cairo types (CairoOption, CairoCustomEnum)
🧰 Tools
🪛 Biome (1.9.4)

[error] 133-133: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

examples/example-node-worker/src/typescript/models.gen.ts (2)

38-46: Fix typo in "positition" field name

There appears to be a typo in the positition field name (should be "position").

export interface PositionCount {
	identity: string;
-	positition: Array<[BigNumberish, BigNumberish]>;
+	position: Array<[BigNumberish, BigNumberish]>;
}

export interface PositionCountValue {
-	positition: Array<[BigNumberish, BigNumberish]>;
+	position: Array<[BigNumberish, BigNumberish]>;
}

Since this affects the interface structure and is reflected in the schema initialization, this typo should be fixed in the generator if possible.


129-133: Fix typo in schema definition to match interfaces

The schema definition contains the same typo as found in the interfaces.

PositionCount: {
	identity: "",
-	positition: [[0, 0]],
},
PositionCountValue: {
-	positition: [[0, 0]],
+	position: [[0, 0]],
},
packages/sdk/src/internal/toriiQueryBuilder.ts (1)

133-137: 🛠️ Refactor suggestion

Specify a more specific type for the generic parameter

The any type in the generic parameter of withPagination disables many type checking rules and should be replaced with a more specific type.

-static withPagination<T extends Record<string, Record<string, any>>>(
+static withPagination<T extends SchemaType>(
    cursor: string,
    limit: number,
    direction: PaginationDirection
): ToriiQueryBuilder<T> {
🧰 Tools
🪛 Biome (1.9.4)

[error] 133-133: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

packages/sdk/src/node/index.ts (4)

50-51: Breaking change: Client initialization changed

Changed from torii.createClient to directly instantiating new torii.ToriiClient.

This is a breaking change for anyone directly using the returned client. Ensure this change is documented in migration guides and potentially consider providing a compatibility layer if feasible.


64-65: Renamed query parameter for improved clarity

Parameter renamed from dont_include_hashed_keys to no_hashed_keys in both entity and event query subscriptions.

This is a breaking change but improves naming consistency. Ensure this change is documented in migration guides and update all examples to use the new parameter name.


72-85: Updated entity parsing to use items array

The entity parsing has been updated to handle arrays from the .items property rather than keyed objects.

This change simplifies the data structure but is a breaking change for existing code. Ensure this change is documented in migration guides and make sure that all examples and documentation are updated to reflect this new data structure.


215-230: Modified generateTypedData signature

The method signature has changed:

  • Removed optional domain parameter
  • Added optional additionalTypes parameter
  • Domain is now always taken from options.domain

This is a breaking change for code that explicitly passed a domain parameter different from options.domain. Ensure this change is documented in migration guides and consider if there are cases where passing a different domain was useful functionality.

🧹 Nitpick comments (12)
packages/sdk/src/__tests__/zustand.perf.test.ts (1)

131-133: Consider replacing non-null assertions and string literal key

The code uses non-null assertions (!) which could be unsafe if world or item is null or undefined. Additionally, using a string literal for the entity key could be simplified.

-                        draft.entities[
-                            "entity500"
-                        ].models.world!.item!.durability = 75;
+                        const entity = draft.entities["entity500"];
+                        if (entity?.models?.world?.item) {
+                            entity.models.world.item.durability = 75;
+                        }

This change adds null-checking to prevent potential runtime errors and improves readability.

🧰 Tools
🪛 Biome (1.9.4)

[error] 131-133: Forbidden non-null assertion.

(lint/style/noNonNullAssertion)


[error] 131-133: Forbidden non-null assertion.

Unsafe fix: Replace with optional chain operator ?. This operator includes runtime checks, so it is safer than the compile-only non-null assertion operator

(lint/style/noNonNullAssertion)


[error] 132-132: The computed expression can be simplified without the use of a string literal.

Unsafe fix: Use a literal key instead.

(lint/complexity/useLiteralKeys)

examples/example-vite-react-sdk/src/historical-events.tsx (1)

1-5: Consider using import type for type-only imports

Some imports are only used as types. Using import type would ensure they're removed by compilers, potentially optimizing the bundle size.

 import {
-    KeysClause,
-    ParsedEntity,
+    KeysClause,
     HistoricalToriiQueryBuilder,
+    type ParsedEntity,
 } from "@dojoengine/sdk";
🧰 Tools
🪛 Biome (1.9.4)

[error] 1-5: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

packages/sdk/src/web/state/zustand.ts (1)

14-14: Consider using import type for MergedModels

The MergedModels import is only used as a type. Using import type would ensure it's removed by compilers and potentially optimize bundle size.

-import { deepMerge, MergedModels } from "../../internal/merge";
+import { deepMerge } from "../../internal/merge";
+import type { MergedModels } from "../../internal/merge";
🧰 Tools
🪛 Biome (1.9.4)

[error] 14-14: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

examples/example-node-worker/src/typescript/contracts.gen.ts (1)

1-2: Use import type for type-only imports

The imports from @dojoengine/core and starknet include types that are only used as types and not as values.

-import { DojoProvider, DojoCall } from "@dojoengine/core";
-import { Account, AccountInterface, BigNumberish, CairoOption, CairoCustomEnum, ByteArray } from "starknet";
+import { DojoProvider } from "@dojoengine/core";
+import type { DojoCall } from "@dojoengine/core";
+import type { Account, AccountInterface, BigNumberish, CairoOption, CairoCustomEnum, ByteArray } from "starknet";
🧰 Tools
🪛 Biome (1.9.4)

[error] 1-1: All these imports are only used as types.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Use import type.

(lint/style/useImportType)


[error] 2-2: Some named imports are only used as types.

This import is only used as a type.

This import is only used as a type.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

packages/state/src/recs/index.ts (2)

189-189: Use array length property instead of Object.keys().length

Since entities.items is now an array, use the direct .length property instead of converting to object keys first.

-        if (Object.keys(entities.items).length < limit) {
+        if (entities.items.length < limit) {

246-247: Use array length check for empty array

-        if (Object.keys(entities.items).length === 0) {
+        if (entities.items.length === 0) {
packages/sdk/src/internal/merge.ts (2)

76-76: Improve type safety by avoiding any type

The codebase uses any in several Record type declarations, which weakens type safety. Consider using more specific types where possible:

-        const newEnumContent: Record<string, any> = {};
+        const newEnumContent: Record<string, unknown> = {};

And similarly for other occurrences.

Also applies to: 96-96, 133-133

🧰 Tools
🪛 Biome (1.9.4)

[error] 76-76: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)


127-156: Handle circular references in deepMerge

The current implementation doesn't check for circular references, which could lead to stack overflow errors. Consider adding a WeakMap to track objects being merged and detect cycles.

 export function deepMerge<T extends SchemaType>(
     target: MergedModels<T>,
     source: Partial<MergedModels<T>>
 ): MergedModels<T> {
+    // Use WeakMap to detect circular references
+    const seen = new WeakMap();
+    
+    function merge(target: any, source: any): any {
+        // Check for circular references
+        if (typeof source === 'object' && source !== null) {
+            if (seen.has(source)) {
+                return seen.get(source);
+            }
+            seen.set(source, target);
+        }
+        
+        if (isCairoOption(target) && isCairoOption(source)) {
+            return mergeCairoOption(target, source);
+        }
+        if (isCairoCustomEnum(target) && isCairoCustomEnum(source)) {
+            return mergeCairoCustomEnum(target, source);
+        }
+        // Rest of the implementation...
+    }
+    
+    return merge(target, source);
🧰 Tools
🪛 Biome (1.9.4)

[error] 133-133: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

packages/sdk/src/web/index.ts (2)

259-264: Consider using configured signer and identity when account is not provided

The SDK has configured identity and signer at initialization, but sendMessage doesn't use them when an account isn't provided directly.

        sendMessage: async (
            data: TypedData,
            account?: Account
        ): Promise<Result<Uint8Array, string>> => {
            if (!account) {
+               // Try to use configured signer and identity if available
+               if (options.signer && options.identity) {
+                   try {
+                       const signature = await options.signer.signMessage(data);
+                       const dataString = JSON.stringify(data);
+                       const sigArray = Array.isArray(signature)
+                           ? signature.map(s => s.toString())
+                           : [signature.r.toString(), signature.s.toString()];
+                           
+                       return ok(await client.publishMessage(dataString, sigArray));
+                   } catch (error) {
+                       console.error("Failed to sign with configured signer:", error);
+                       return err(`Failed to sign with configured signer: ${String(error)}`);
+                   }
+               }
                return err(NO_ACCOUNT);
            }

272-277: Convert bigint signature elements to strings

When signature is an array of bigints they're forwarded as-is, but publishMessage likely expects string[]. Ensure all signature elements are properly converted to strings.

                return ok(
                    await client.publishMessage(
                        dataString,
-                        Array.isArray(signature)
-                            ? signature
-                            : [signature.r.toString(), signature.s.toString()]
+                        Array.isArray(signature)
+                            ? signature.map(s => s.toString())
+                            : [signature.r.toString(), signature.s.toString()]
                    )
                );
examples/example-node-worker/src/typescript/models.gen.ts (2)

3-3: Add a type import to improve type safety.

The import statement includes types that are only used as types and not as runtime values.

-import { CairoCustomEnum, CairoOption, CairoOptionVariant, BigNumberish } from 'starknet';
+import type { CairoCustomEnum, CairoOption, CairoOptionVariant, BigNumberish } from 'starknet';
🧰 Tools
🪛 Biome (1.9.4)

[error] 3-3: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)


99-103: Inconsistent indentation in CairoCustomEnum initialization

The indentation is inconsistent in the CairoCustomEnum initialization, making the code harder to read.

directions: [new CairoCustomEnum({ 
-					Left: "",
-				Right: undefined,
-				Up: undefined,
-				Down: undefined, })],
+	Left: "",
+	Right: undefined,
+	Up: undefined,
+	Down: undefined, 
})],

Apply the same fix to all similar occurrences in the file (lines 106-111, 143-148, and 150-155).

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro (Legacy)

📥 Commits

Reviewing files that changed from the base of the PR and between 34c97ec and b159d72.

⛔ Files ignored due to path filters (5)
  • examples/example-vite-react-phaser-recs/src/dojo/generated/setup.ts is excluded by !**/generated/**
  • examples/example-vite-react-pwa-recs/src/dojo/generated/setup.ts is excluded by !**/generated/**
  • examples/example-vite-react-threejs-recs/src/dojo/generated/setup.ts is excluded by !**/generated/**
  • examples/example-vue-app-recs/src/dojo/generated/setup.ts is excluded by !**/generated/**
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (44)
  • .changeset/thirty-cheetahs-design.md (1 hunks)
  • .github/workflows/ci.yaml (1 hunks)
  • .github/workflows/release.yaml (6 hunks)
  • examples/example-node-worker/dojoConfig.ts (1 hunks)
  • examples/example-node-worker/env.ts (1 hunks)
  • examples/example-node-worker/main.ts (1 hunks)
  • examples/example-node-worker/package.json (1 hunks)
  • examples/example-node-worker/src/typescript/contracts.gen.ts (1 hunks)
  • examples/example-node-worker/src/typescript/models.gen.ts (1 hunks)
  • examples/example-node-worker/tsconfig.json (1 hunks)
  • examples/example-nodejs-bot/package.json (1 hunks)
  • examples/example-vite-react-app-recs/src/dojo/setup.ts (2 hunks)
  • examples/example-vite-react-sdk/src/historical-events.tsx (2 hunks)
  • package.json (1 hunks)
  • packages/core/package.json (1 hunks)
  • packages/react/package.json (2 hunks)
  • packages/sdk/package.json (3 hunks)
  • packages/sdk/src/__tests__/parseEntities.test.ts (7 hunks)
  • packages/sdk/src/__tests__/parseHistoricalEvents.test.ts (0 hunks)
  • packages/sdk/src/__tests__/state.test.ts (3 hunks)
  • packages/sdk/src/__tests__/toriiQueryBuilder.test.ts (6 hunks)
  • packages/sdk/src/__tests__/zustand.perf.test.ts (1 hunks)
  • packages/sdk/src/internal/errors.ts (1 hunks)
  • packages/sdk/src/internal/generateTypedData.ts (2 hunks)
  • packages/sdk/src/internal/merge.ts (1 hunks)
  • packages/sdk/src/internal/models.ts (1 hunks)
  • packages/sdk/src/internal/parseEntities.ts (4 hunks)
  • packages/sdk/src/internal/parseHistoricalEvents.ts (0 hunks)
  • packages/sdk/src/internal/toriiQueryBuilder.ts (6 hunks)
  • packages/sdk/src/internal/types.ts (5 hunks)
  • packages/sdk/src/node/index.ts (12 hunks)
  • packages/sdk/src/node/worker.ts (1 hunks)
  • packages/sdk/src/web/experimental/index.ts (3 hunks)
  • packages/sdk/src/web/index.ts (11 hunks)
  • packages/sdk/src/web/react/hooks/entities.ts (1 hunks)
  • packages/sdk/src/web/react/hooks/events.ts (3 hunks)
  • packages/sdk/src/web/react/hooks/hooks.ts (6 hunks)
  • packages/sdk/src/web/state/zustand.ts (1 hunks)
  • packages/state/src/recs/index.ts (6 hunks)
  • packages/torii-client/package.json (2 hunks)
  • packages/torii-wasm/dojo.c (1 hunks)
  • turbo.json (1 hunks)
  • worlds/dojo-starter (1 hunks)
  • worlds/onchain-dash (1 hunks)
💤 Files with no reviewable changes (2)
  • packages/sdk/src/tests/parseHistoricalEvents.test.ts
  • packages/sdk/src/internal/parseHistoricalEvents.ts
✅ Files skipped from review due to trivial changes (4)
  • packages/sdk/src/tests/state.test.ts
  • .changeset/thirty-cheetahs-design.md
  • worlds/dojo-starter
  • packages/sdk/src/node/worker.ts
🚧 Files skipped from review as they are similar to previous changes (23)
  • packages/torii-wasm/dojo.c
  • worlds/onchain-dash
  • examples/example-node-worker/dojoConfig.ts
  • packages/react/package.json
  • packages/core/package.json
  • examples/example-node-worker/env.ts
  • packages/sdk/package.json
  • packages/sdk/src/tests/parseEntities.test.ts
  • examples/example-vite-react-app-recs/src/dojo/setup.ts
  • examples/example-node-worker/tsconfig.json
  • turbo.json
  • examples/example-nodejs-bot/package.json
  • packages/sdk/src/internal/errors.ts
  • package.json
  • packages/sdk/src/web/react/hooks/entities.ts
  • packages/torii-client/package.json
  • packages/sdk/src/tests/toriiQueryBuilder.test.ts
  • packages/sdk/src/internal/generateTypedData.ts
  • .github/workflows/release.yaml
  • packages/sdk/src/web/react/hooks/events.ts
  • packages/sdk/src/internal/models.ts
  • .github/workflows/ci.yaml
  • packages/sdk/src/internal/types.ts
🧰 Additional context used
🧬 Code Graph Analysis (9)
packages/sdk/src/node/index.ts (6)
packages/sdk/src/internal/convertClauseToEntityKeysClause.ts (1)
  • intoEntityKeysClause (4-36)
packages/sdk/src/internal/parseEntities.ts (1)
  • parseEntities (10-59)
packages/sdk/src/internal/types.ts (2)
  • ToriiResponse (248-253)
  • ToriiQueryBuilder (557-557)
packages/sdk/src/internal/generateTypedData.ts (1)
  • generateTypedData (13-48)
packages/sdk/src/internal/errors.ts (2)
  • NO_SIGNER (1-1)
  • NO_IDENTITY (2-2)
packages/sdk/src/internal/toriiQueryBuilder.ts (1)
  • ToriiQueryBuilder (24-143)
packages/sdk/src/web/index.ts (6)
packages/sdk/src/internal/parseEntities.ts (1)
  • parseEntities (10-59)
packages/sdk/src/internal/convertClauseToEntityKeysClause.ts (1)
  • intoEntityKeysClause (4-36)
packages/sdk/src/internal/types.ts (2)
  • ToriiResponse (248-253)
  • ToriiQueryBuilder (557-557)
packages/sdk/src/internal/generateTypedData.ts (1)
  • generateTypedData (13-48)
packages/sdk/src/internal/errors.ts (1)
  • NO_ACCOUNT (3-3)
packages/sdk/src/internal/toriiQueryBuilder.ts (1)
  • ToriiQueryBuilder (24-143)
examples/example-node-worker/src/typescript/contracts.gen.ts (2)
examples/example-node-worker/src/typescript/models.gen.ts (1)
  • direction (71-76)
packages/core/src/types/index.ts (1)
  • DojoCall (130-134)
examples/example-node-worker/src/typescript/models.gen.ts (1)
packages/sdk/src/internal/types.ts (1)
  • SchemaType (50-70)
examples/example-vite-react-sdk/src/historical-events.tsx (1)
packages/sdk/src/internal/toriiQueryBuilder.ts (1)
  • HistoricalToriiQueryBuilder (149-159)
packages/sdk/src/internal/merge.ts (3)
packages/sdk/src/web/queryBuilder.ts (1)
  • value (191-193)
examples/example-node-worker/src/typescript/models.gen.ts (1)
  • SchemaType (80-94)
packages/sdk/src/internal/types.ts (2)
  • SchemaType (50-70)
  • ParsedEntity (228-237)
packages/sdk/src/internal/parseEntities.ts (2)
packages/core/src/provider/DojoProvider.ts (1)
  • entities (104-123)
packages/sdk/src/internal/types.ts (2)
  • StandardizedQueryResult (193-195)
  • ParsedEntity (228-237)
packages/sdk/src/web/experimental/index.ts (2)
packages/sdk/src/internal/parseEntities.ts (1)
  • parseEntities (10-59)
packages/sdk/src/internal/convertClauseToEntityKeysClause.ts (1)
  • intoEntityKeysClause (4-36)
packages/sdk/src/web/react/hooks/hooks.ts (2)
packages/sdk/src/internal/types.ts (1)
  • ParsedEntity (228-237)
examples/example-vite-react-sql/src/components/playground/schema.tsx (1)
  • Schema (48-66)
🪛 Biome (1.9.4)
packages/sdk/src/internal/toriiQueryBuilder.ts

[error] 133-133: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

examples/example-node-worker/main.ts

[error] 176-176: This code is unreachable

... because this statement will return from the function beforehand

(lint/correctness/noUnreachable)

examples/example-node-worker/src/typescript/contracts.gen.ts

[error] 1-1: All these imports are only used as types.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Use import type.

(lint/style/useImportType)


[error] 2-2: Some named imports are only used as types.

This import is only used as a type.

This import is only used as a type.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

examples/example-node-worker/src/typescript/models.gen.ts

[error] 3-3: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

examples/example-vite-react-sdk/src/historical-events.tsx

[error] 1-5: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

packages/sdk/src/__tests__/zustand.perf.test.ts

[error] 131-133: Forbidden non-null assertion.

(lint/style/noNonNullAssertion)


[error] 131-133: Forbidden non-null assertion.

Unsafe fix: Replace with optional chain operator ?. This operator includes runtime checks, so it is safer than the compile-only non-null assertion operator

(lint/style/noNonNullAssertion)


[error] 132-132: The computed expression can be simplified without the use of a string literal.

Unsafe fix: Use a literal key instead.

(lint/complexity/useLiteralKeys)

packages/sdk/src/internal/merge.ts

[error] 76-76: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)


[error] 96-96: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)


[error] 133-133: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

packages/sdk/src/internal/parseEntities.ts

[error] 50-50: Do not use template literals if interpolation and special-character handling are not needed.

Unsafe fix: Replace with string literal

(lint/style/noUnusedTemplateLiteral)

packages/sdk/src/web/experimental/index.ts

[error] 95-95: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

packages/sdk/src/web/react/hooks/hooks.ts

[error] 72-72: Forbidden non-null assertion.

(lint/style/noNonNullAssertion)

packages/sdk/src/web/state/zustand.ts

[error] 14-14: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

packages/state/src/recs/index.ts

[error] 185-185: Do not use template literals if interpolation and special-character handling are not needed.

Unsafe fix: Replace with string literal

(lint/style/noUnusedTemplateLiteral)

🪛 Gitleaks (8.21.2)
examples/example-node-worker/package.json

7-7: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


8-8: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: check
  • GitHub Check: build
🔇 Additional comments (16)
examples/example-vite-react-sdk/src/historical-events.tsx (1)

14-14: Great upgrade to the specialized HistoricalToriiQueryBuilder

The change to HistoricalToriiQueryBuilder aligns with the SDK's internal refactor where query builders were enhanced to support cursor-based pagination and historical querying.

examples/example-node-worker/package.json (2)

13-19: Good dependency updates for the enhanced SDK implementation

The additions of Torii WASM, dotenv, starknet, websocket, and zod dependencies align well with the new environment configuration and enhanced SDK usage in this example.


21-22: Appropriate type definitions for development

Adding the type definitions for Node.js and WebSocket is appropriate for TypeScript development.

packages/sdk/src/web/react/hooks/hooks.ts (2)

32-33: Type signature updated to use array-based entities

The parameter types for processInitialData and processUpdateData have been updated from handling the general ToriiResponse to specifically working with arrays of ParsedEntity<Schema>. This aligns with the broader SDK changes moving from keyed objects to arrays of entities.


65-67: Updated data casting to match new type signatures

The function calls have been updated to cast the data to ParsedEntity<Schema>[], ensuring type compatibility with the updated callback signatures. This maintains type safety throughout the data flow.

Also applies to: 75-77, 90-92

packages/sdk/src/web/experimental/index.ts (2)

53-59: Updated entity parsing to handle arrays instead of keyed objects

The callback now processes entity data as an array, matching the updated parseEntities function signature. This approach is more consistent with the rest of the SDK.


98-100: Updated event parsing to handle arrays instead of keyed objects

The callback now processes event data as an array, matching the updated parseEntities function signature. This approach is more consistent with the rest of the SDK.

packages/sdk/src/internal/parseEntities.ts (2)

10-23: Function refactored to work with entity arrays instead of keyed objects

The parseEntities function has been refactored to accept an array of torii.Entity objects instead of a keyed object. This aligns with the broader SDK changes to handle entities as arrays.


90-90: Added support for tuple type in parseValue

The function now handles the "tuple" case, which was previously missing. This improves the robustness of the parser.

packages/sdk/src/internal/toriiQueryBuilder.ts (4)

9-20: Breaking change: Pagination structure refactored

The pagination parameters are now nested within a pagination object instead of being at the root level. This structural change aligns with the transition from offset-based to cursor-based pagination.

This is a breaking change that should be documented in the migration guide for users upgrading from previous versions.


39-45: Deprecated method now returns a no-op

The withOffset method is now deprecated and returns this without performing any action, which is appropriate for a smooth transition to the new pagination model.


47-63: New cursor-based pagination methods added

The new withCursor and withDirection methods provide better support for cursor-based pagination, allowing for more flexible and efficient data retrieval.


145-159: New HistoricalToriiQueryBuilder class for historical queries

The new HistoricalToriiQueryBuilder class provides a convenient way to create queries for historical data by extending ToriiQueryBuilder with the historical flag set to true by default.

packages/sdk/src/node/index.ts (3)

29-30: Added neverthrow for improved error handling

The addition of neverthrow for Result types provides a more robust error handling approach.


34-36: New exports for worker, types, and models

The additional exports make worker functionality, internal types, and models available to consumers of the SDK.


401-414: Simplified toriiEventMessagesQueryIntoHashedKeys method

The toriiEventMessagesQueryIntoHashedKeys method signature has been simplified by removing the historical parameter and always parsing events uniformly on .items.

},
subscribeEntities: async (
query: torii.Query,
callback: ToriiSubscriptionCallback<T>
) => {
if (
query.dont_include_hashed_keys &&
query.no_hashed_keys &&
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Changed property name from dont_include_hashed_keys to no_hashed_keys

The property name used in conditions has changed from dont_include_hashed_keys to no_hashed_keys, which is a breaking change for API consumers.

Let's check if this change is consistent across the codebase:

Also applies to: 80-80


🏁 Script executed:

#!/bin/bash
# Check for usages of both property names
grep -r "dont_include_hashed_keys" --include="*.ts" packages/
grep -r "no_hashed_keys" --include="*.ts" packages/

Length of output: 1289


Action Required: Address Breaking Change for dont_include_hashed_keysno_hashed_keys

We’ve confirmed that the old dont_include_hashed_keys key has been fully removed and replaced by no_hashed_keys across the SDK (no references to the old name were found). This will break any consumers still passing dont_include_hashed_keys.

Affected SDK surfaces:

  • packages/sdk/src/internal/toriiQueryBuilder.ts
  • packages/sdk/src/web/index.ts
  • packages/sdk/src/web/experimental/index.ts
  • packages/sdk/src/node/index.ts
  • packages/state/src/recs/index.ts

Please choose one of the following:

  • Add a deprecated alias: if query.dont_include_hashed_keys is set, map it internally to query.no_hashed_keys.
  • Treat this as a true breaking change: document it in the CHANGELOG, bump to a new major version, and call out the renamed property in upgrade notes.

@@ -21,24 +20,22 @@
...options.client,
} as torii.ClientConfig;

const client = await torii.createClient(clientConfig);
const client = new torii.ToriiClient(clientConfig);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Client instantiation changed from async to sync

The client creation has been changed from using await torii.createClient(clientConfig) to a synchronous instantiation with new torii.ToriiClient(clientConfig). This is a breaking change if consumers are awaiting the client creation.

Is this change consistent across the codebase? Let's check other client initializations:


🏁 Script executed:

#!/bin/bash
# Search for client initialization patterns in the SDK
grep -r "createClient" --include="*.ts" packages/sdk/
grep -r "new torii.ToriiClient" --include="*.ts" packages/sdk/

Length of output: 611


🏁 Script executed:

#!/bin/bash
# Show the surrounding code for context in experimental/index.ts
sed -n '1,50p' packages/sdk/src/web/experimental/index.ts

Length of output: 1810


🏁 Script executed:

#!/bin/bash
sed -n '1,50p' packages/sdk/src/web/index.ts

Length of output: 1607


🏁 Script executed:

#!/bin/bash
# Show the surrounding code in node/index.ts for client instantiation
sed -n '1,50p' packages/sdk/src/node/index.ts

Length of output: 1630


Inconsistent ToriiClient instantiation across entrypoints

We’re now instantiating the ToriiClient synchronously in web/experimental but still using await new ToriiClient() in other async entrypoints. While this isn’t a breaking change for consumers (all public APIs remain async functions that return a promise), it can be confusing and looks inconsistent.

Locations to harmonize:

  • packages/sdk/src/web/experimental/index.ts
    const client = new torii.ToriiClient(clientConfig);
  • packages/sdk/src/web/index.ts
    return await new torii.ToriiClient(config);
  • packages/sdk/src/node/index.ts
    const client = await new torii.ToriiClient(clientConfig);

Recommendation: pick one pattern—either drop all redundant await or add await everywhere—and apply it across all init/createClient functions for clarity.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🔭 Outside diff range comments (2)
packages/sdk/src/web/index.ts (1)

257-282: ⚠️ Potential issue

Fix inconsistent error handling in sendMessage

While the function now returns a Result type, the catch block still throws errors instead of returning them as Err.

try {
    // Sign the typed data
    const signature: Signature = await account.signMessage(data);

    // Stringify typed data for publishing
    const dataString = JSON.stringify(data);
    // Publish the signed message
    return ok(
        await client.publishMessage(
            dataString,
            Array.isArray(signature)
                ? signature
                : [signature.r.toString(), signature.s.toString()]
        )
    );
} catch (error) {
    console.error("Failed to send message:", error);
-   throw error;
+   return err(String(error));
}

Additionally, when signature is an array of bigints, they should be converted to strings:

return ok(
    await client.publishMessage(
        dataString,
        Array.isArray(signature)
-           ? signature
+           ? signature.map(s => s.toString())
            : [signature.r.toString(), signature.s.toString()]
    )
);
packages/sdk/src/node/index.ts (1)

267-269: 🛠️ Refactor suggestion

Inconsistent error handling in catch block

While the function has been refactored to use Result for normal error cases, it still throws an exception in the catch block, which is inconsistent with the new pattern.

Make the error handling consistent by returning err in the catch block:

catch (error) {
    console.error("Failed to send message:", error);
-   throw error;
+   return err(`Failed to send message: ${error instanceof Error ? error.message : String(error)}`);
}
♻️ Duplicate comments (28)
examples/example-node-worker/package.json (1)

6-10: ⚠️ Potential issue

Remove hardcoded credentials from scripts

The scripts contain hardcoded private keys and identity values, which is a security risk. These should be moved to environment variables loaded from a .env file instead.

    "scripts": {
-        "bun": "IDENTITY=0x0127fd5f1fe78a71f8bcd1fec63e3fe2f0486b6ecd5c86a0466c3a21fa5cfcec SECRET_KEY=0xc5b2fcab997346f3ea1c00b002ecf6f382c5f9c9659a3894eb783c5320f912 bun run main.ts",
-        "node": "IDENTITY=0x0127fd5f1fe78a71f8bcd1fec63e3fe2f0486b6ecd5c86a0466c3a21fa5cfcec SECRET_KEY=0xc5b2fcab997346f3ea1c00b002ecf6f382c5f9c9659a3894eb783c5320f912 node main.ts",
+        "bun": "bun run main.ts",
+        "node": "node main.ts",
        "build": "tsc -b"
    },

Since you've already added dotenv as a dependency, you should use it to load these values from a .env file (which should be added to .gitignore).

🧰 Tools
🪛 Gitleaks (8.21.2)

7-7: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


8-8: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

packages/sdk/src/web/react/hooks/hooks.ts (1)

121-123: ⚠️ Potential issue

Uncomment and fix the cleanup function

The cleanup function that should free the subscription reference is commented out, which could lead to memory leaks.

    return () => {
        if (subscriptionRef.current) {
-            // subscriptionRef.current?.free();
-            // subscriptionRef.current = null;
+            subscriptionRef.current.free();
+            subscriptionRef.current = null;
        }
    };
examples/example-node-worker/src/typescript/contracts.gen.ts (5)

7-13: Function uses inconsistent naming style

The function uses snake_case (build_actions_move_calldata) instead of camelCase which is the TypeScript convention. This is inconsistent with the exported function names in line 52-57 which use camelCase.

-const build_actions_move_calldata = (direction: CairoCustomEnum): DojoCall => {
+const buildActionsMoveCalldata = (direction: CairoCustomEnum): DojoCall => {
  return {
    contractName: "actions",
    entrypoint: "move",
    calldata: [direction],
  };
};

15-26: Function follows inconsistent naming and could use better error handling

The function uses snake_case which is inconsistent with TypeScript conventions. Additionally, the error handling could benefit from more specific error messages to aid debugging.

-const actions_move = async (snAccount: Account | AccountInterface, direction: CairoCustomEnum) => {
+const actionsMove = async (snAccount: Account | AccountInterface, direction: CairoCustomEnum) => {
  try {
    return await provider.execute(
      snAccount,
-      build_actions_move_calldata(direction),
+      buildActionsMoveCalldata(direction),
      "dojo_starter",
    );
  } catch (error) {
-    console.error(error);
+    console.error(`Error executing move action: ${error}`);
    throw error;
  }
};

28-34: Function uses inconsistent naming style

Similar to the previous function, this uses snake_case instead of the conventional camelCase in TypeScript.

-const build_actions_spawn_calldata = (): DojoCall => {
+const buildActionsSpawnCalldata = (): DojoCall => {
  return {
    contractName: "actions",
    entrypoint: "spawn",
    calldata: [],
  };
};

36-47: Function follows inconsistent naming and could use better error handling

Similar to actions_move, this function uses snake_case and has minimal error information in the error handling.

-const actions_spawn = async (snAccount: Account | AccountInterface) => {
+const actionsSpawn = async (snAccount: Account | AccountInterface) => {
  try {
    return await provider.execute(
      snAccount,
-      build_actions_spawn_calldata(),
+      buildActionsSpawnCalldata(),
      "dojo_starter",
    );
  } catch (error) {
-    console.error(error);
+    console.error(`Error executing spawn action: ${error}`);
    throw error;
  }
};

52-57: Inconsistency between function definitions and exports

The exported function names use camelCase but reference functions defined with snake_case. This inconsistency should be resolved.

return {
  actions: {
-    move: actions_move,
-    buildMoveCalldata: build_actions_move_calldata,
-    spawn: actions_spawn,
-    buildSpawnCalldata: build_actions_spawn_calldata,
+    move: actionsMove,
+    buildMoveCalldata: buildActionsMoveCalldata,
+    spawn: actionsSpawn,
+    buildSpawnCalldata: buildActionsSpawnCalldata,
  },
};
packages/sdk/src/web/experimental/index.ts (4)

95-95: Strengthen callback parameter types

For improved type safety, replace the any type on the entityData parameter with torii.Entity, which aligns with the parseEntities<T>(entities: torii.Entity[]) signature.

-                    (_: string, entityData: any) => {
+                    (_: string, entityData: torii.Entity) => {
🧰 Tools
🪛 Biome (1.9.4)

[error] 95-95: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)


29-32: Document removal of historical parameter in getEvents

We've confirmed there are no internal calls passing a historical flag to getEvents, but its removal is a breaking change for consumers. Please update your public-facing documentation and release notes to reflect this.

• File: packages/sdk/src/web/experimental/index.ts (lines 29–32)

  • getEvents(query: torii.Query) no longer accepts a historical argument.
    • Action: Add a note in CHANGELOG.md and the SDK migration guide about the removed historical parameter.

38-38: Action Required: Address Breaking Change for dont_include_hashed_keysno_hashed_keys

We've confirmed that the old dont_include_hashed_keys key has been fully removed and replaced by no_hashed_keys across the SDK. This will break any consumers still passing dont_include_hashed_keys.

Please choose one of the following:

  • Add a deprecated alias: if query.dont_include_hashed_keys is set, map it internally to query.no_hashed_keys.
  • Treat this as a true breaking change: document it in the CHANGELOG, bump to a new major version, and call out the renamed property in upgrade notes.

23-23: Inconsistent ToriiClient instantiation across entrypoints

We're now instantiating the ToriiClient synchronously in web/experimental but still using await new ToriiClient() in other async entrypoints. While this isn't a breaking change for consumers (all public APIs remain async functions that return a promise), it can be confusing and looks inconsistent.

Recommendation: pick one pattern—either drop all redundant await or add await everywhere—and apply it across all init/createClient functions for clarity.

packages/sdk/src/internal/parseEntities.ts (1)

126-134: ⚠️ Potential issue

Parse u64 (and larger) as BigInt to avoid precision loss

Number.parseInt cannot safely represent all u64 values – anything above 2**53-1 will silently lose precision.
Please parse all 64-bit and wider integers as BigInt (and prepend 0x when the string is hexadecimal).

-        case "u64":
-            return Number.parseInt(value.value as string, 16);
-        case "i256":
-        case "i128":
-        case "u256":
-        case "u128":
-            return BigInt(value.value as string);
+        case "u64":
+        case "i256":
+        case "i128":
+        case "u256":
+        case "u128":
+            // Use BigInt to keep full precision. The `0x` prefix lets BigInt
+            // parse the hex string correctly.
+            return BigInt(`0x${value.value as string}`);
examples/example-node-worker/main.ts (4)

21-25: Still assigning the wrong global – use WebSocket, not Websocket
Same issue was raised previously; the browser (and most polyfills) expose WebSocket with a capital S.

-// @ts-ignore
-global.Websocket = w3cwebsocket;
+// @ts-ignore
+global.WebSocket = w3cwebsocket;

123-131: initPositionFromEvent mutates the shared default object

pc is just an alias of defaultPositionCount, so every call permanently mutates the constant.

-const pc = defaultPositionCount;
+const pc: PositionCount = { ...defaultPositionCount };

137-150: data.pop() drops entities & mutates the batch

If multiple entities arrive in one notification all but the last are ignored.
Iterate instead:

-        const entity = data.pop();
-        if (entity && entity.entityId !== "0x0") {
-
-        }
+        for (const entity of data) {
+            if (entity.entityId === "0x0") continue;
+
+        }

175-176: Unreachable code after the first return

The second return [sub]; can never execute.

-    return [];
-    return [sub];
+    return [sub];
🧰 Tools
🪛 Biome (1.9.4)

[error] 176-176: This code is unreachable

... because this statement will return from the function beforehand

(lint/correctness/noUnreachable)

packages/state/src/recs/index.ts (3)

185-190: Array length check uses Object.keys – inefficient & misleading

entities.items is an array; use .length directly.

-        if (Object.keys(entities.items).length < limit) {
+        if (entities.items.length < limit) {
🧰 Tools
🪛 Biome (1.9.4)

[error] 185-185: Do not use template literals if interpolation and special-character handling are not needed.

Unsafe fix: Replace with string literal

(lint/style/noUnusedTemplateLiteral)


246-247: setEntities still receives an array although it expects an object

Passing the raw array causes keys to become "0" | "1" | …, breaking look-ups.
Convert the array into an object keyed by the real entityId, or refactor setEntities to understand arrays.

Also applies to: 328-329


541-553: Iterating over an array with Object.entries loses entity IDs

entities is an array; Object.entries returns numeric indices, so the stored id is "0", "1", … instead of the hashed key. Iterate directly:

-for (const [entityId, data] of Object.entries(entities)) {
-    const entityData = { id: entityId, ...data };
+for (const entity of entities) {
+    const entityId = entity.hashed_keys?.join(".") ?? crypto.randomUUID();
+    const entityData = { id: entityId, ...entity };
packages/sdk/src/internal/merge.ts (1)

123-160: 🛠️ Refactor suggestion

Add unit tests for deepMerge and fix any usage

This is a critical utility function that handles complex merging logic for Cairo types. Ensure it has comprehensive test coverage for various edge cases.

- const result = { ...target } as Record<string, any>;
+ const result = { ...target } as Record<string, unknown>;

Add tests that verify:

  1. Merging of primitive values (overwrites correctly)
  2. Recursive merging of nested objects
  3. Handling of arrays (should be overwritten, not deep-merged)
  4. Treatment of null and undefined properties
  5. Behavior with custom Cairo types (CairoOption, CairoCustomEnum)
🧰 Tools
🪛 Biome (1.9.4)

[error] 133-133: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

examples/example-node-worker/src/typescript/models.gen.ts (2)

38-46: Fix typo in "positition" field name

There appears to be a typo in the positition field name (should be "position").

export interface PositionCount {
	identity: string;
-	positition: Array<[BigNumberish, BigNumberish]>;
+	position: Array<[BigNumberish, BigNumberish]>;
}

export interface PositionCountValue {
-	positition: Array<[BigNumberish, BigNumberish]>;
+	position: Array<[BigNumberish, BigNumberish]>;
}

Since this affects the interface structure and is reflected in the schema initialization, this typo should be fixed in the generator if possible.


128-133: Fix typo in schema field name to match interfaces

Fix the typo in the schema definition to match the corrected interface.

PositionCount: {
	identity: "",
-	positition: [[0, 0]],
+	position: [[0, 0]],
},
PositionCountValue: {
-	positition: [[0, 0]],
+	position: [[0, 0]],
},
packages/sdk/src/node/index.ts (6)

50-50: Client initialization change

Changed from torii.createClient to directly instantiating new torii.ToriiClient. This is a breaking change for anyone directly using the returned client.

Ensure this change is documented in migration guides and potentially consider providing a compatibility layer if feasible.


64-65: Renamed query parameter for improved clarity

Parameter renamed from dont_include_hashed_keys to no_hashed_keys in entity query subscriptions.

Ensure this change is documented in migration guides and update all examples to use the new parameter name.


72-74: Updated entity parsing to use items array

The entity parsing has been updated to handle arrays from the .items property rather than keyed objects.

Ensure this change is documented in migration guides. Make sure that all examples and documentation are updated to reflect this new data structure.


119-120: Renamed query parameter for improved clarity

Parameter renamed from dont_include_hashed_keys to no_hashed_keys in event query subscriptions.

Ensure this change is documented in migration guides and update all examples to use the new parameter name.


127-130: Updated event parsing to use items array

Similar to the entity parsing changes, event parsing now uses arrays from the .items property.

Update all documentation and examples to reflect this structural change in event data handling.


219-230: Modified generateTypedData signature

The method signature has changed:

  • Removed optional domain parameter
  • Added optional additionalTypes parameter
  • Domain is now always taken from options.domain

This is a breaking change for code that explicitly passed a domain parameter different from options.domain. Ensure this change is documented in migration guides.

🧹 Nitpick comments (10)
packages/sdk/src/__tests__/zustand.perf.test.ts (2)

131-133: Consider addressing non-null assertions

While the code formatting change is fine, the static analysis tools flag two concerns:

  1. The use of non-null assertions (!) is discouraged as they bypass TypeScript's type checking
  2. The string literal key could be simplified
-                        draft.entities[
-                            "entity500"
-                        ].models.world!.item!.durability = 75;
+                        draft.entities.entity500?.models.world?.item?.durability = 75;

This change replaces non-null assertions with optional chaining operators for safer access, and simplifies the property access syntax.

🧰 Tools
🪛 Biome (1.9.4)

[error] 131-133: Forbidden non-null assertion.

(lint/style/noNonNullAssertion)


[error] 131-133: Forbidden non-null assertion.

Unsafe fix: Replace with optional chain operator ?. This operator includes runtime checks, so it is safer than the compile-only non-null assertion operator

(lint/style/noNonNullAssertion)


[error] 132-132: The computed expression can be simplified without the use of a string literal.

Unsafe fix: Use a literal key instead.

(lint/complexity/useLiteralKeys)


151-151: Consider addressing non-null assertions here too

For consistency with the above change, this line should also be updated to use optional chaining.

-            draft.entities["entity500"].models.world!.item!.durability = 75;
+            draft.entities.entity500?.models.world?.item?.durability = 75;
🧰 Tools
🪛 Biome (1.9.4)

[error] 151-151: Forbidden non-null assertion.

(lint/style/noNonNullAssertion)


[error] 151-151: Forbidden non-null assertion.

Unsafe fix: Replace with optional chain operator ?. This operator includes runtime checks, so it is safer than the compile-only non-null assertion operator

(lint/style/noNonNullAssertion)


[error] 151-151: The computed expression can be simplified without the use of a string literal.

Unsafe fix: Use a literal key instead.

(lint/complexity/useLiteralKeys)

examples/example-vite-react-sdk/src/historical-events.tsx (1)

1-5: Consider using import type for type-only imports

The static analysis tools suggest that some imports are only used as types and should be marked as such with import type.

 import {
     KeysClause,
-    ParsedEntity,
+    type ParsedEntity,
     HistoricalToriiQueryBuilder,
 } from "@dojoengine/sdk";
🧰 Tools
🪛 Biome (1.9.4)

[error] 1-5: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

packages/sdk/src/web/state/zustand.ts (1)

14-14: Good refactor: Using centralized merge utilities

Importing the deepMerge and MergedModels from a central utility module helps reduce code duplication and ensures consistent handling of Cairo types across the SDK.

Consider using import type for type-only imports:

-import { deepMerge, MergedModels } from "../../internal/merge";
+import { deepMerge, type MergedModels } from "../../internal/merge";
🧰 Tools
🪛 Biome (1.9.4)

[error] 14-14: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

examples/example-node-worker/src/typescript/contracts.gen.ts (1)

1-2: Use import type for type-only imports

Importing the types with import type ensures they are removed by the compilers and avoids loading unnecessary modules.

-import { DojoProvider, DojoCall } from "@dojoengine/core";
-import { Account, AccountInterface, BigNumberish, CairoOption, CairoCustomEnum, ByteArray } from "starknet";
+import { DojoProvider } from "@dojoengine/core";
+import type { DojoCall } from "@dojoengine/core";
+import type { Account, AccountInterface, BigNumberish, CairoOption, CairoCustomEnum, ByteArray } from "starknet";
🧰 Tools
🪛 Biome (1.9.4)

[error] 1-1: All these imports are only used as types.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Use import type.

(lint/style/useImportType)


[error] 2-2: Some named imports are only used as types.

This import is only used as a type.

This import is only used as a type.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

packages/sdk/src/internal/parseEntities.ts (1)

55-59: Redundant Object.values on an array

result is already an array (ParsedEntity<T>[]). Wrapping it with Object.values just allocates a shallow copy for no benefit.

-    return Object.values(result);
+    return result;
packages/sdk/src/internal/merge.ts (1)

63-113: Replace any with more specific types

The merging logic is correct, properly handling active variants. However, there are untyped any usages in the implementation.

Replace the untyped Records with more specific types:

- const newEnumContent: Record<string, any> = {};
+ const newEnumContent: Record<string, unknown> = {};

Same for line 96.

🧰 Tools
🪛 Biome (1.9.4)

[error] 76-76: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)


[error] 96-96: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

examples/example-node-worker/src/typescript/models.gen.ts (2)

3-3: Use import type for type-only imports

- import { CairoCustomEnum, CairoOption, CairoOptionVariant, BigNumberish } from 'starknet';
+ import { CairoCustomEnum, CairoOption, CairoOptionVariant } from 'starknet';
+ import type { BigNumberish } from 'starknet';

Since BigNumberish is only used as a type, not as a value, it's better to use import type for it.

🧰 Tools
🪛 Biome (1.9.4)

[error] 3-3: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)


99-104: Improve code indentation for enum initialization

The indentation is inconsistent in the CairoCustomEnum initialization, making it harder to read.

directions: [new CairoCustomEnum({ 
-	Left: "",
+	Left: "",
-	Right: undefined,
+	Right: undefined,
-	Up: undefined,
+	Up: undefined,
-	Down: undefined, })],
+	Down: undefined 
+})],

Apply similar formatting improvements to other CairoCustomEnum initializations in the file.

packages/sdk/src/internal/toriiQueryBuilder.ts (1)

133-142: Update the generic type parameter in withPagination

Replace the generic Record type with the more specific SchemaType:

- static withPagination<T extends Record<string, Record<string, any>>>(
+ static withPagination<T extends SchemaType>(
    cursor: string,
    limit: number,
    direction: PaginationDirection
): ToriiQueryBuilder<T> {

This aligns with how the class is used elsewhere and eliminates the any type warning.

🧰 Tools
🪛 Biome (1.9.4)

[error] 133-133: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro (Legacy)

📥 Commits

Reviewing files that changed from the base of the PR and between b159d72 and cb2f3ee.

⛔ Files ignored due to path filters (5)
  • examples/example-vite-react-phaser-recs/src/dojo/generated/setup.ts is excluded by !**/generated/**
  • examples/example-vite-react-pwa-recs/src/dojo/generated/setup.ts is excluded by !**/generated/**
  • examples/example-vite-react-threejs-recs/src/dojo/generated/setup.ts is excluded by !**/generated/**
  • examples/example-vue-app-recs/src/dojo/generated/setup.ts is excluded by !**/generated/**
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (45)
  • .changeset/thirty-cheetahs-design.md (1 hunks)
  • .github/workflows/ci.yaml (1 hunks)
  • .github/workflows/release.yaml (6 hunks)
  • examples/example-node-worker/dojoConfig.ts (1 hunks)
  • examples/example-node-worker/env.ts (1 hunks)
  • examples/example-node-worker/main.ts (1 hunks)
  • examples/example-node-worker/package.json (1 hunks)
  • examples/example-node-worker/src/typescript/contracts.gen.ts (1 hunks)
  • examples/example-node-worker/src/typescript/models.gen.ts (1 hunks)
  • examples/example-node-worker/tsconfig.json (1 hunks)
  • examples/example-nodejs-bot/package.json (1 hunks)
  • examples/example-vite-experimental-sdk/src/main.ts (1 hunks)
  • examples/example-vite-react-app-recs/src/dojo/setup.ts (2 hunks)
  • examples/example-vite-react-sdk/src/historical-events.tsx (2 hunks)
  • package.json (1 hunks)
  • packages/core/package.json (1 hunks)
  • packages/react/package.json (2 hunks)
  • packages/sdk/package.json (3 hunks)
  • packages/sdk/src/__tests__/parseEntities.test.ts (7 hunks)
  • packages/sdk/src/__tests__/parseHistoricalEvents.test.ts (0 hunks)
  • packages/sdk/src/__tests__/state.test.ts (3 hunks)
  • packages/sdk/src/__tests__/toriiQueryBuilder.test.ts (6 hunks)
  • packages/sdk/src/__tests__/zustand.perf.test.ts (1 hunks)
  • packages/sdk/src/internal/errors.ts (1 hunks)
  • packages/sdk/src/internal/generateTypedData.ts (2 hunks)
  • packages/sdk/src/internal/merge.ts (1 hunks)
  • packages/sdk/src/internal/models.ts (1 hunks)
  • packages/sdk/src/internal/parseEntities.ts (4 hunks)
  • packages/sdk/src/internal/parseHistoricalEvents.ts (0 hunks)
  • packages/sdk/src/internal/toriiQueryBuilder.ts (6 hunks)
  • packages/sdk/src/internal/types.ts (5 hunks)
  • packages/sdk/src/node/index.ts (12 hunks)
  • packages/sdk/src/node/worker.ts (1 hunks)
  • packages/sdk/src/web/experimental/index.ts (3 hunks)
  • packages/sdk/src/web/index.ts (11 hunks)
  • packages/sdk/src/web/react/hooks/entities.ts (1 hunks)
  • packages/sdk/src/web/react/hooks/events.ts (3 hunks)
  • packages/sdk/src/web/react/hooks/hooks.ts (6 hunks)
  • packages/sdk/src/web/state/zustand.ts (1 hunks)
  • packages/state/src/recs/index.ts (6 hunks)
  • packages/torii-client/package.json (2 hunks)
  • packages/torii-wasm/dojo.c (1 hunks)
  • turbo.json (1 hunks)
  • worlds/dojo-starter (1 hunks)
  • worlds/onchain-dash (1 hunks)
💤 Files with no reviewable changes (2)
  • packages/sdk/src/internal/parseHistoricalEvents.ts
  • packages/sdk/src/tests/parseHistoricalEvents.test.ts
✅ Files skipped from review due to trivial changes (4)
  • packages/react/package.json
  • .changeset/thirty-cheetahs-design.md
  • packages/sdk/src/tests/state.test.ts
  • turbo.json
🚧 Files skipped from review as they are similar to previous changes (23)
  • examples/example-node-worker/dojoConfig.ts
  • examples/example-nodejs-bot/package.json
  • worlds/dojo-starter
  • packages/torii-client/package.json
  • examples/example-vite-react-app-recs/src/dojo/setup.ts
  • package.json
  • packages/torii-wasm/dojo.c
  • packages/sdk/src/internal/errors.ts
  • examples/example-node-worker/env.ts
  • packages/sdk/package.json
  • packages/core/package.json
  • packages/sdk/src/tests/parseEntities.test.ts
  • packages/sdk/src/web/react/hooks/entities.ts
  • examples/example-node-worker/tsconfig.json
  • packages/sdk/src/tests/toriiQueryBuilder.test.ts
  • packages/sdk/src/node/worker.ts
  • .github/workflows/ci.yaml
  • .github/workflows/release.yaml
  • packages/sdk/src/web/react/hooks/events.ts
  • worlds/onchain-dash
  • packages/sdk/src/internal/types.ts
  • packages/sdk/src/internal/models.ts
  • packages/sdk/src/internal/generateTypedData.ts
🧰 Additional context used
🧬 Code Graph Analysis (7)
packages/sdk/src/web/index.ts (5)
packages/sdk/src/internal/parseEntities.ts (1)
  • parseEntities (10-59)
packages/sdk/src/internal/convertClauseToEntityKeysClause.ts (1)
  • intoEntityKeysClause (4-36)
packages/sdk/src/internal/generateTypedData.ts (1)
  • generateTypedData (13-48)
packages/sdk/src/internal/errors.ts (1)
  • NO_ACCOUNT (3-3)
packages/sdk/src/internal/toriiQueryBuilder.ts (1)
  • ToriiQueryBuilder (24-143)
packages/sdk/src/node/index.ts (6)
packages/sdk/src/internal/convertClauseToEntityKeysClause.ts (1)
  • intoEntityKeysClause (4-36)
packages/sdk/src/internal/parseEntities.ts (1)
  • parseEntities (10-59)
packages/sdk/src/internal/types.ts (2)
  • ToriiResponse (248-253)
  • ToriiQueryBuilder (557-557)
packages/sdk/src/internal/generateTypedData.ts (1)
  • generateTypedData (13-48)
packages/sdk/src/internal/errors.ts (2)
  • NO_SIGNER (1-1)
  • NO_IDENTITY (2-2)
packages/sdk/src/internal/toriiQueryBuilder.ts (1)
  • ToriiQueryBuilder (24-143)
examples/example-node-worker/src/typescript/contracts.gen.ts (2)
examples/example-node-worker/src/typescript/models.gen.ts (1)
  • direction (71-76)
packages/core/src/types/index.ts (1)
  • DojoCall (130-134)
examples/example-node-worker/src/typescript/models.gen.ts (1)
packages/sdk/src/internal/types.ts (1)
  • SchemaType (50-70)
examples/example-vite-react-sdk/src/historical-events.tsx (1)
packages/sdk/src/internal/toriiQueryBuilder.ts (1)
  • HistoricalToriiQueryBuilder (149-159)
packages/sdk/src/internal/merge.ts (3)
packages/sdk/src/web/queryBuilder.ts (1)
  • value (191-193)
examples/example-node-worker/src/typescript/models.gen.ts (1)
  • SchemaType (80-94)
packages/sdk/src/internal/types.ts (2)
  • SchemaType (50-70)
  • ParsedEntity (228-237)
packages/state/src/recs/index.ts (1)
packages/core/src/provider/DojoProvider.ts (1)
  • entities (104-123)
🪛 Biome (1.9.4)
examples/example-node-worker/main.ts

[error] 176-176: This code is unreachable

... because this statement will return from the function beforehand

(lint/correctness/noUnreachable)

packages/sdk/src/web/experimental/index.ts

[error] 95-95: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

packages/sdk/src/internal/toriiQueryBuilder.ts

[error] 133-133: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

examples/example-node-worker/src/typescript/contracts.gen.ts

[error] 1-1: All these imports are only used as types.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Use import type.

(lint/style/useImportType)


[error] 2-2: Some named imports are only used as types.

This import is only used as a type.

This import is only used as a type.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

examples/example-node-worker/src/typescript/models.gen.ts

[error] 3-3: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

examples/example-vite-react-sdk/src/historical-events.tsx

[error] 1-5: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

packages/sdk/src/__tests__/zustand.perf.test.ts

[error] 131-133: Forbidden non-null assertion.

(lint/style/noNonNullAssertion)


[error] 131-133: Forbidden non-null assertion.

Unsafe fix: Replace with optional chain operator ?. This operator includes runtime checks, so it is safer than the compile-only non-null assertion operator

(lint/style/noNonNullAssertion)


[error] 132-132: The computed expression can be simplified without the use of a string literal.

Unsafe fix: Use a literal key instead.

(lint/complexity/useLiteralKeys)

packages/sdk/src/internal/merge.ts

[error] 76-76: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)


[error] 96-96: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)


[error] 133-133: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

packages/sdk/src/internal/parseEntities.ts

[error] 50-50: Do not use template literals if interpolation and special-character handling are not needed.

Unsafe fix: Replace with string literal

(lint/style/noUnusedTemplateLiteral)

packages/sdk/src/web/react/hooks/hooks.ts

[error] 72-72: Forbidden non-null assertion.

(lint/style/noNonNullAssertion)

packages/sdk/src/web/state/zustand.ts

[error] 14-14: Some named imports are only used as types.

This import is only used as a type.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Add inline type keywords.

(lint/style/useImportType)

packages/state/src/recs/index.ts

[error] 185-185: Do not use template literals if interpolation and special-character handling are not needed.

Unsafe fix: Replace with string literal

(lint/style/noUnusedTemplateLiteral)

🪛 Gitleaks (8.21.2)
examples/example-node-worker/package.json

7-7: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


8-8: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: build
  • GitHub Check: check
🔇 Additional comments (24)
examples/example-vite-experimental-sdk/src/main.ts (1)

34-38: Update looks good: Removed unnecessary "historical" parameter

The removal of the second parameter to sdk.getEvents() aligns with SDK-wide changes where the historical event handling was unified, removing the distinction between historical and non-historical events.

examples/example-vite-react-sdk/src/historical-events.tsx (1)

14-20: Good update: Using specialized query builder for historical events

The change from ToriiQueryBuilder to HistoricalToriiQueryBuilder is appropriate as it leverages the specialized builder that sets historical: true by default. This makes the code more explicit about its intent and leverages the dedicated API.

packages/sdk/src/internal/merge.ts (2)

9-11: Looks good: Type guard for CairoOption

Clean and straightforward implementation of the type guard for Cairo's Option type.


53-55: Looks good: Type guard for CairoCustomEnum

Clean implementation of the type guard for Cairo's custom enum type.

packages/sdk/src/web/index.ts (5)

30-31: LGTM: Added Result type from neverthrow for error handling

Good addition of error handling using the Result type from neverthrow library and centralizing error messages.


48-48: API change: direct instantiation of ToriiClient

Changed from using a factory function to direct class instantiation. This appears to be an intentional API change in the underlying library.


92-94: Updated to access items property from pagination response

The SDK now expects responses to include an items array property, consistent with a move to cursor-based pagination.


232-247: Updated typed data generation interface

The signature was updated to use the domain from options and add support for additional types. This provides more flexibility for custom type definitions.


414-424: Fixed array handling in toriiEventMessagesQueryIntoHashedKeys

The function now properly handles the items array from the query result and passes it to intoEntityKeysClause.

Though there's still a TODO comment about fixing the type. You might want to address this in a future update.

packages/sdk/src/internal/toriiQueryBuilder.ts (3)

39-45: Deprecated offset-based pagination in favor of cursor-based

Good approach to maintain backward compatibility by keeping the deprecated method as a no-op while directing users to the new method through JSDoc.


47-62: Added new cursor-based pagination methods

The new withCursor and withDirection methods support the transition to cursor-based pagination, which is more efficient for large datasets.


145-158: LGTM: New HistoricalToriiQueryBuilder class

Good addition of a specialized class for historical queries, extending the base query builder with the historical flag set to true by default.

packages/sdk/src/node/index.ts (12)

19-19: Import cleanup for Starknet types

The import has been simplified to only include the actually used types from the Starknet package.


29-30: Added Result type and error constants

Added imports from neverthrow for error handling and imported constants for common error messages. This is a good improvement for standardized error handling.


34-36: Additional exports for worker and models

New exports for worker, types, and models will enable more flexible SDK usage patterns.


79-85: Updated subscription callback to use array-based parsing

The entity update callback now parses a single entity into an array structure, consistent with the new array-based approach throughout the codebase.


138-143: Updated event callback to use array-based parsing

The event update callback now parses a single entity into an array structure, consistent with the new array-based approach throughout the codebase.


189-189: Unified entity retrieval and parsing

The entity retrieval has been simplified to access the items array directly and parse it.


202-204: Simplified event retrieval and parsing

Event retrieval and parsing have been simplified by removing special handling for historical events and directly accessing the items array.


242-249: Improved error handling with Result type

The sendMessage method now returns a Result type with proper error handling when signer or identity is missing, which is more robust than throwing exceptions.


251-265: Refactored message sending for better reliability

Message sending now:

  1. Uses options.signer and options.identity instead of explicit account parameter
  2. Returns a Result type for successful operations
  3. Uses proper error handling for missing configuration

390-390: Updated entity array access in toriiQueryIntoHashedKeys

Entity access pattern has been updated to use the items array property, consistent with other changes throughout the file.


402-402: Simplified toriiEventMessagesQueryIntoHashedKeys signature

Removed the historical parameter from the method signature, simplifying the API.


406-407: Updated event parsing in toriiEventMessagesQueryIntoHashedKeys

Event parsing has been updated to use the items array property, consistent with other changes throughout the file.

Comment on lines +307 to 333
const fetchedEntities = await client.getEntities({
pagination: {
limit,
offset: cursor,
clause: clause || undefined,
cursor,
direction: "Forward",
order_by: orderBy,
entity_models: entityModels,
dont_include_hashed_keys: false,
entity_updated_after: 0,
},
historical
);
clause: clause || undefined,
no_hashed_keys: false,
models: entityModels,
historical,
});

while (continueFetching) {
if (logging)
console.log(
`Fetched ${Object.keys(fetchedEntities).length} entities ${cursor}`
`Fetched ${Object.keys(fetchedEntities.items).length} entities ${fetchedEntities.next_cursor}`
);

setEntities(fetchedEntities, components, logging);
setEntities(fetchedEntities.items, components, logging);

if (Object.keys(fetchedEntities).length < limit) {
if (Object.keys(fetchedEntities.items).length < limit) {
continueFetching = false;
} else {
cursor += limit;
cursor = fetchedEntities.next_cursor;
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Potential infinite/incorrect loop – fetch performed only once

fetchedEntities is retrieved before entering the while loop and never refreshed, so:

  • the loop may spin forever if entities.items.length ≥ limit, or
  • it processes only the first page and exits.

Move the client.getEntities call inside the loop or convert the loop to do … while.

Comment on lines +19 to +46
export function mergeCairoOption<T extends SchemaType>(
target: MergedModels<T>,
source: Partial<MergedModels<T>>
): MergedModels<T> {
// If source is Some, prefer source's value
if (source instanceof CairoOption && source.isSome()) {
return new CairoOption(
CairoOptionVariant.Some,
source.unwrap()
) as unknown as MergedModels<T>;
}

// If source is None or undefined, keep target
if (target instanceof CairoOption) {
if (target.isSome()) {
return new CairoOption(
CairoOptionVariant.Some,
target.unwrap()
) as unknown as MergedModels<T>;
}
return new CairoOption(
CairoOptionVariant.None
) as unknown as MergedModels<T>;
}

// This should not happen if both are CairoOption instances
return target as unknown as MergedModels<T>;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider adding unit tests for this critical merge functionality

The implementation looks correct, handling the different cases (Some vs None) for CairoOption merging. The function properly preserves the source value when present or falls back to the target.

This is a core function that will impact entity state management throughout the application. Please add test coverage for various merge scenarios, including:

  • When source is Some and target is Some
  • When source is None and target is Some
  • When source is Some and target is None
  • When source is None and target is None

Comment on lines 9 to 20
const defaultToriiOptions = () => ({
limit: 100, // default limit
offset: 0,
pagination: {
limit: 100,
cursor: undefined,
direction: "Forward",
order_by: [],
},
clause: undefined,
dont_include_hashed_keys: true,
order_by: [],
entity_models: [],
entity_updated_after: 0,
no_hashed_keys: true,
models: [],
historical: false,
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

❓ Verification inconclusive

Breaking change: Restructured pagination options

The pagination parameters are now nested within a pagination object instead of being at the root level, and no_hashed_keys replaces dont_include_hashed_keys.

Please ensure these breaking changes are documented in the migration guide for users upgrading from previous versions.


🏁 Script executed:

#!/bin/bash
# Check if there's a migration guide or documentation for these breaking changes
fd -t f "migration|upgrade|changelog|BREAKING" --exec grep -l "pagination" {} \;

Length of output: 83


Add missing migration guide entry for pagination restructure and key rename

It looks like the breaking changes to defaultToriiOptions haven’t been documented yet. Please add/update your migration guide or changelog to cover:

  • Moving pagination fields (limit, cursor, direction, order_by) under a top-level pagination object
  • Renaming dont_include_hashed_keysno_hashed_keys
  • Any examples or code snippets showing the before/after usage

@MartianGreed MartianGreed merged commit bcec562 into main Apr 30, 2025
5 checks passed
@MartianGreed MartianGreed deleted the chore/dojo1.5 branch April 30, 2025 06:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant