Skip to content
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

feat: create reusable hooks and add sql hook to sdk #376

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

MartianGreed
Copy link
Collaborator

@MartianGreed MartianGreed commented Jan 15, 2025

Summary by CodeRabbit

Release Notes

  • New Features

    • Added new SDK modules: @dojoengine/sdk/state, @dojoengine/sdk/react, and @dojoengine/sdk/sql
    • Introduced DojoSdkProvider for centralized SDK configuration
    • Added useDojoSDK hook for simplified SDK access
  • Breaking Changes

    • Replaced useDojoDb with useDojoSDK
    • Removed DojoContext and useDojo hook
    • Restructured SDK initialization and context management
  • Improvements

    • Enhanced type safety for React components
    • Streamlined state management across examples
    • Updated query handling with useToriiSQLQuery
  • Dependency Updates

    • Updated React and related dependencies
    • Added TypeScript type definitions for React

Copy link

coderabbitai bot commented Jan 15, 2025

Walkthrough

The pull request introduces significant architectural changes to the Dojo SDK, focusing on restructuring the state management, SDK initialization, and React integration. The modifications span multiple packages and example projects, with a primary emphasis on consolidating context management, introducing new SDK modules, and streamlining the way components interact with the Dojo ecosystem. Key changes include replacing context-based providers with a new DojoSdkProvider, introducing new SDK submodules like @dojoengine/sdk/state, @dojoengine/sdk/react, and @dojoengine/sdk/sql, and updating various example projects to leverage the new SDK structure.

Changes

File/Path Change Summary
packages/sdk/package.json Added new module exports for state, react, and sql submodules; expanded peer dependencies
packages/sdk/src/react/hooks.ts Added useDojoSDK hook; updated useModel function
packages/sdk/src/react/provider.tsx Introduced DojoSdkProvider and DojoContext
packages/sdk/src/sql/index.ts Added queryTorii and useToriiSQLQuery functions
examples/*/src/main.tsx Updated to use DojoSdkProvider with new configuration
examples/*/src/components/* Replaced useDojoDb with useDojoSDK; updated data fetching methods
Various example projects Removed useDojo, useDojoStore, and useQuery hooks

Sequence Diagram

sequenceDiagram
    participant App as React Application
    participant Provider as DojoSdkProvider
    participant SDK as Dojo SDK
    participant Client as World Client
    participant Store as Zustand Store

    App->>Provider: Render with SDK configuration
    Provider->>SDK: Initialize SDK
    SDK->>Client: Create World Client
    SDK->>Store: Create Zustand Store
    Provider-->>App: Provide Context with SDK, Client, Store
    App->>Provider: Access SDK via useDojoSDK hook
Loading

Poem

🐰 Hop, hop, hooray! SDK's new way!
Context dancing, modules bright
React components take their flight
From db to sdk, we leap with glee
A rabbit's code, now swift and free! 🚀

Finishing Touches

  • 📝 Generate Docstrings (Beta)

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 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 docstrings to generate docstrings for this PR. (Beta)
  • @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.

Actionable comments posted: 7

🔭 Outside diff range comments (8)
packages/sdk/tsconfig.json (1)

Line range hint 18-18: Include .tsx files in the compilation.

The current include pattern "include": ["src/**/*.ts"] will miss TypeScript React files. Consider updating it to include .tsx files:

-    "include": ["src/**/*.ts"],
+    "include": ["src/**/*.{ts,tsx}"],
examples/example-vite-react-sdk/src/historical-events.tsx (1)

Line range hint 23-46: Include sdk in dependency array and add cleanup function in useEffect.

  • Dependency Array Issue: The sdk variable is used within the subscribeHistoricalEvent function inside the useEffect, but it's not included in the dependency array. If sdk changes, the effect won't re-run, potentially causing the component to use a stale sdk instance.

  • Missing Cleanup Function: To prevent memory leaks, it's important to unsubscribe from the event subscription when the component unmounts or dependencies change. Currently, there's no cleanup function to handle this.

Apply this diff to include sdk in the dependency array and add a cleanup function:

 useEffect(() => {
     async function subscribeHistoricalEvent(account: AccountInterface) {
         try {
             const s = await sdk.subscribeEventQuery({
                 // ...
             });
             setSubscription(s);
         } catch (error) {
             setEvents([]);
             if (subscription) {
                 subscription.free();
             }
             console.error(error);
         }
     }

     if (account) {
         subscribeHistoricalEvent(account);
     }
+    return () => {
+        if (subscription) {
+            subscription.free();
+        }
+    };
-}, [account, setEvents]);
+}, [account, setEvents, sdk]);
examples/example-vite-react-sql/src/hooks/useSystemCalls.ts (1)

Line range hint 18-45: Consider adding timeout to waitForEntityChange.

The waitForEntityChange call could potentially hang indefinitely if the entity update never matches the condition.

-await state.waitForEntityChange(entityId.toString(), (entity) => {
+await state.waitForEntityChange(entityId.toString(), (entity) => {
     return (
         entity?.models?.dojo_starter?.Moves?.remaining ===
         remainingMoves
     );
-});
+}, { timeout: 30000 }); // Add 30-second timeout
examples/example-vite-kitchen-sink/src/components/global-counter.tsx (1)

Line range hint 32-49: Consider handling error in the callback.

The callback in getEntity ignores both data and error parameters, which could lead to missed error conditions.

-callback: ({ data, error }) => {},
+callback: ({ data, error }) => {
+    if (error) {
+        console.error('Error fetching entity:', error);
+    }
+},
examples/example-vite-kitchen-sink/src/components/caller-counter.tsx (1)

Line range hint 105-115: Improve subscription cleanup logic.

The subscription cleanup could be more robust by checking the subscription state before freeing.

-if (sub) {
+if (sub?.free && typeof sub.free === 'function') {
   sub.free();
 }
examples/example-vite-kitchen-sink/src/components/chat.tsx (1)

Line range hint 89-91: Remove ts-expect-error comments.

The code contains multiple ts-expect-error comments that should be addressed properly with correct typing.

Consider adding proper type definitions or using type assertions instead of suppressing TypeScript errors.

examples/example-vite-kitchen-sink/src/components/theme-switch.tsx (2)

Line range hint 102-104: Remove ts-expect-error comments and fix typing issues.

The code contains multiple ts-expect-error comments that should be addressed with proper typing.

Consider creating proper type definitions for the theme-related data structures to avoid type assertions and error suppressions.


Line range hint 142-146: Add error handling for theme subscription failures.

The subscription error handling could be more robust with proper user feedback.

 if (error) {
-    throw error;
+    console.error('Theme subscription error:', error);
+    setTheme({
+        current: AvailableThemeClassMap[AvailableTheme.Light],
+        next: AvailableThemeClassMap[AvailableTheme.Light]
+    });
+    // Consider showing a user-friendly error message
 }
🧹 Nitpick comments (18)
packages/sdk/tsup.config.ts (1)

7-12: Document the new module structure in README.

Consider adding documentation that explains:

  • The purpose of each module
  • How to import from specific modules
  • Example usage for each module

Example documentation structure:

## SDK Modules

The SDK is organized into several modules:

- `@dojoengine/sdk`: Core SDK functionality
- `@dojoengine/sdk/state`: State management utilities
- `@dojoengine/sdk/react`: React hooks and components
- `@dojoengine/sdk/sql`: SQL integration utilities

### Usage

```typescript
// Core SDK
import { DojoProvider } from '@dojoengine/sdk';

// State management
import { useDojoState } from '@dojoengine/sdk/state';

// React hooks
import { useDojoSDK } from '@dojoengine/sdk/react';

// SQL utilities
import { useDojoSQL } from '@dojoengine/sdk/sql';

</blockquote></details>
<details>
<summary>packages/sdk/src/sql/index.ts (2)</summary><blockquote>

`41-43`: **Add comprehensive JSDoc documentation.**

The hook is missing proper documentation explaining its purpose, parameters, and return values.

Add detailed JSDoc:

```typescript
/**
 * React hook for querying Torii SQL endpoint with TanStack Query integration.
 * 
 * @template Output - The type of formatted data returned by the query
 * @template Input - The type of raw data returned from SQL
 * @param {string} query - SQL query to execute
 * @param {(rows: Input) => Output} formatFn - Function to format the query results
 * @param {UseQueryOptions<Output>["placeholderData"]} [defaultValue] - Default value while loading
 * @param {string} [toriiUrl] - Optional Torii endpoint URL (defaults to localhost:8080)
 * @returns {Object} Query result object
 * @returns {Output} .data - The formatted query results
 * @returns {Error} .error - Any error that occurred
 * @returns {boolean} .isPending - Whether the query is loading
 * @returns {boolean} .isRefetching - Whether the query is refetching
 */

44-69: Improve type safety and configuration.

Several improvements could make this hook more robust:

  1. The @ts-expect-error needs explanation or proper typing
  2. The default Torii URL should be configurable
  3. Query key could be more specific for better cache management

Consider these improvements:

+const DEFAULT_TORII_URL = "http://127.0.0.1:8080";
+
 export function useToriiSQLQuery<Output, Input>(
     query: string,
     formatFn: (rows: Input) => Output,
     defaultValue?: UseQueryOptions<Output>["placeholderData"],
     toriiUrl?: string
 ) {
     const { data, error, isPending, isRefetching } = useTanStackQuery({
-        queryKey: [query],
+        queryKey: ["torii-sql", toriiUrl ?? DEFAULT_TORII_URL, query],
         queryFn: async () => {
             return await queryTorii(
-                toriiUrl ?? "http://127.0.0.1:8080",
+                toriiUrl ?? DEFAULT_TORII_URL,
                 query,
                 formatFn
             );
         },
-        // @ts-expect-error trust me
-        placeholderData: defaultValue ?? [],
+        placeholderData: defaultValue ?? ([] as unknown as Output),
     });

     return {
         data,
         error,
         isPending,
         isRefetching,
     };
 }
packages/sdk/package.json (1)

63-63: LGTM! Consider documenting the architectural changes.

The addition of @dojoengine/core as a workspace dependency aligns with the SDK restructuring.

Consider adding a CHANGELOG.md entry or updating the README.md to document these architectural changes, especially:

  • The new modular structure with state, react, and sql submodules
  • The relationship between @dojoengine/core and @dojoengine/sdk
packages/sdk/src/state/zustand.ts (5)

Line range hint 23-45: Consider strengthening type constraints for better type safety.

The interface is well-structured, but some parameters could benefit from additional type constraints:

  • entityId parameters could be constrained to match your entity ID format
  • timeout parameter could use a more specific type than number

Consider adding these type improvements:

export interface GameState<T extends SchemaType> {
-   subscribeToEntity: (entityId: string, listener: (entity: ParsedEntity<T> | undefined) => void) => () => void;
+   subscribeToEntity: (entityId: `${string}:${string}`, listener: (entity: ParsedEntity<T> | undefined) => void) => () => void;
-   waitForEntityChange: (entityId: string, predicate: (entity: ParsedEntity<T> | undefined) => boolean, timeout?: number) => Promise<ParsedEntity<T> | undefined>;
+   waitForEntityChange: (entityId: `${string}:${string}`, predicate: (entity: ParsedEntity<T> | undefined) => boolean, timeout?: TimeoutMS) => Promise<ParsedEntity<T> | undefined>;
}

+type TimeoutMS = number & { readonly brand: unique symbol };
+const asTimeout = (ms: number): TimeoutMS => ms as TimeoutMS;

Line range hint 47-56: Enhance factory function documentation.

While the function is documented, it could benefit from more detailed JSDoc comments including:

  • Parameter constraints
  • Return value details
  • Usage examples

Consider expanding the documentation:

 /**
  * Factory function to create a Zustand store based on a given SchemaType.
  *
+ * @description
+ * Creates a type-safe store with entity management, optimistic updates, and query capabilities.
+ * The store is configured with Immer and selector subscription middleware.
  *
  * @template T - The schema type.
+ * @throws {Error} When schema validation fails
+ * @example
+ * ```typescript
+ * type MySchema = {
+ *   namespace1: {
+ *     model1: { field1: string }
+ *   }
+ * };
+ * const useStore = createDojoStore<MySchema>();
+ * ```
  * @returns A Zustand hook tailored to the provided schema.
  */

Line range hint 57-107: Optimize entity update logic for better performance and type safety.

The current implementation of updateEntity uses multiple type assertions and complex object merging. Consider these improvements:

  • Use TypeScript's type inference more effectively
  • Optimize performance for large entity updates
  • Reduce the use of type assertions

Consider this optimization:

 updateEntity: (entity: Partial<ParsedEntity<T>>) => {
     set((state: Draft<GameState<T>>) => {
         if (entity.entityId && entity.models) {
             const existingEntity = state.entities[entity.entityId];
             if (existingEntity) {
-                // Create new models object without spread
-                const mergedModels: typeof existingEntity.models = Object.assign({}, existingEntity.models);
-                // Iterate through each namespace in the new models
-                Object.entries(entity.models).forEach(([namespace, namespaceModels]) => {
-                    const typedNamespace = namespace as keyof ParsedEntity<T>["models"];
-                    if (!(typedNamespace in mergedModels)) {
-                        mergedModels[typedNamespace as keyof typeof mergedModels] = {} as any;
-                    }
-                    mergedModels[typedNamespace as keyof typeof mergedModels] = Object.assign(
-                        {},
-                        mergedModels[typedNamespace as keyof typeof mergedModels],
-                        namespaceModels
-                    );
-                });
+                // Type-safe model merging with better performance
+                const mergedModels = Object.fromEntries(
+                    Object.entries(entity.models).map(([namespace, models]) => [
+                        namespace,
+                        {
+                            ...(existingEntity.models[namespace as keyof T] || {}),
+                            ...models
+                        }
+                    ])
+                ) as ParsedEntity<T>["models"];
                 state.entities[entity.entityId] = {
                     ...existingEntity,
                     ...entity,
                     models: mergedModels,
                 };
             } else {
                 state.entities[entity.entityId] = entity as WritableDraft<ParsedEntity<T>>;
             }
         }
     });
 }

Line range hint 108-143: Add transaction cleanup and timeout handling.

The transaction handling is robust but could benefit from:

  • Automatic cleanup of stale transactions
  • Timeout handling for pending transactions
  • Transaction status tracking

Consider adding transaction timeouts and cleanup:

+interface PendingTransaction {
+    transactionId: string;
+    patches: Patch[];
+    inversePatches: Patch[];
+    timestamp: number;
+    status: 'pending' | 'confirmed' | 'reverted';
+}

 applyOptimisticUpdate: (transactionId, updateFn) => {
     const currentState = get();
     const [nextState, patches, inversePatches] = produceWithPatches(currentState, (draftState: Draft<GameState<T>>) => {
         updateFn(draftState);
     });

     set(() => nextState);
     set((state: Draft<GameState<T>>) => {
         state.pendingTransactions[transactionId] = {
             transactionId,
             patches,
             inversePatches,
+            timestamp: Date.now(),
+            status: 'pending'
         };
     });

+    // Auto-cleanup after timeout
+    setTimeout(() => {
+        const transaction = get().pendingTransactions[transactionId];
+        if (transaction?.status === 'pending') {
+            get().revertOptimisticUpdate(transactionId);
+        }
+    }, 30000); // 30 second timeout
 }

Line range hint 144-199: Add batch subscription support and prevent memory leaks.

The subscription and query methods could be enhanced with:

  • Batch subscription capabilities for better performance
  • Memory leak prevention through subscription cleanup
  • Enhanced filtering capabilities

Consider these improvements:

+interface SubscriptionOptions {
+    batchSize?: number;
+    debounceMs?: number;
+}

-subscribeToEntity: (entityId, listener) => {
+subscribeToEntity: (entityId, listener, options?: SubscriptionOptions) => {
+    let batchTimeout: NodeJS.Timeout | null = null;
+    let batch: ParsedEntity<T>[] = [];
+
+    const cleanup = () => {
+        if (batchTimeout) {
+            clearTimeout(batchTimeout);
+            batchTimeout = null;
+        }
+    };

     const unsubscribe = useStore.subscribe(
         (state) => state.entities[entityId],
-        (entity) => {
+        (entity) => {
+            if (options?.batchSize) {
+                batch.push(entity);
+                if (batch.length >= options.batchSize) {
+                    listener(entity);
+                    batch = [];
+                } else if (options?.debounceMs && !batchTimeout) {
+                    batchTimeout = setTimeout(() => {
+                        listener(entity);
+                        batch = [];
+                        batchTimeout = null;
+                    }, options.debounceMs);
+                }
+            } else {
                 listener(entity);
+            }
         }
     );

-    return unsubscribe;
+    return () => {
+        cleanup();
+        unsubscribe();
+    };
 }
packages/sdk/src/react/provider.tsx (2)

28-29: Fix the broken word in the inline comment.

The word "cannot" is split across two lines, which affects readability.

Apply this diff to combine the lines:

-// @ts-expect-error Since we c
-// annot dynamically set context at runtime, we will get a type error.
+// @ts-expect-error Since we cannot dynamically set context at runtime, we will get a type error.

52-52: Update the error message to match the component name.

The error message incorrectly references "DojoProvider" instead of "DojoSdkProvider".

Apply this diff to correct the error message:

     if (currentValue) {
-        throw new Error("DojoProvider can only be used once");
+        throw new Error("DojoSdkProvider can only be used once");
     }
examples/example-vite-react-sql/src/components/playground/total-entities.tsx (1)

15-18: Handle loading and error states when using useToriiSQLQuery

Currently, the component assumes that totalEntities is readily available. To enhance user experience and prevent potential runtime errors, consider handling loading and error states returned by useToriiSQLQuery.

Here's how you might update the component:

const { data: totalEntities, error, isLoading } = useToriiSQLQuery(
    TOTAL_ENTITIES_QUERY,
    formatFn
);

if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
packages/sdk/src/react/hooks.ts (1)

36-41: Consider adding JSDoc documentation.

The new useDojoSDK hook would benefit from JSDoc documentation explaining its purpose, generic parameters, and return value, similar to the documentation provided for useModel.

+/**
+ * Custom hook to access the Dojo SDK context.
+ * 
+ * @typeParam Client - The type of the client function
+ * @typeParam Schema - The type of the schema extending SchemaType
+ * @returns The Dojo context containing SDK functionality
+ */
 export function useDojoSDK<
     Client extends (...args: any) => any,
     Schema extends SchemaType,
 >(): DojoContextType<Client, Schema> {
     return useContext(DojoContext);
 }
examples/example-vite-react-sql/src/components/playground/direction-count.tsx (1)

66-72: Consider extracting default rendering to a constant.

The default values are duplicated between the conditional rendering and the defaultDirectionObject. Consider reusing the object.

+const DefaultDirectionDisplay = ({ directions = defaultDirectionObject }) => (
+    <div>
+        Player went :<br />
+        Left <b>{directions.Left}</b> times<br />
+        Up <b>{directions.Up}</b> times<br />
+        Down <b>{directions.Down}</b> times<br />
+        Right <b>{directions.Right}</b> times<br />
+    </div>
+);

-if (!directions) {
-    return (
-        <div>
-            Player went :<br />
-            Left <b>0</b> times<br />
-            Up <b>0</b> times<br />
-            Down <b>0</b> times<br />
-            Right <b>0</b> times<br />
-        </div>
-    );
-}
+if (!directions) {
+    return <DefaultDirectionDisplay />;
+}
examples/example-vite-kitchen-sink/src/components/global-counter.tsx (1)

Line range hint 89-96: Consider using AbortController for cleanup.

The subscription cleanup could be enhanced using AbortController to handle race conditions during component unmount.

 useEffect(() => {
+    const abortController = new AbortController();
     async function subscribeToEntityUpdates(db: SDK<SchemaType>) {
         const sub = await db.subscribeEntityQuery({
             query: new QueryBuilder<SchemaType>()
                 .namespace("onchain_dash", (n) =>
                     n.entity("GlobalCounter", (e) =>
                         e.eq("global_counter_key", 9999999)
                     )
                 )
                 .build(),
-            callback: ({ data, error }) => {
+            callback: ({ data, error }) => {
+                if (abortController.signal.aborted) return;
                 // ... rest of the callback
             },
         });
         setSub(sub);
     }
     if (db && sub === null) {
         subscribeToEntityUpdates(db)
             .then(() => {})
             .catch(console.error);
     }
     return () => {
+        abortController.abort();
         if (sub) {
             sub.free();
         }
     };
 }, [db, sub, setIsLoading]);
examples/example-vite-kitchen-sink/src/components/caller-counter.tsx (1)

34-34: Consider using type inference for better maintainability.

The explicit type parameters might be unnecessary if the SDK can infer types from the setupWorld.

-const { sdk } = useDojoSDK<typeof setupWorld, SchemaType>();
+const { sdk } = useDojoSDK();
examples/example-vite-kitchen-sink/src/components/chat.tsx (1)

34-34: Improve type safety with SDK alias.

The SDK alias might cause confusion. Consider using a more descriptive name.

-const { sdk: db } = useDojoSDK<typeof setupWorld, SchemaType>();
+const { sdk: messageSDK } = useDojoSDK<typeof setupWorld, SchemaType>();
examples/example-vite-kitchen-sink/src/components/theme-switch.tsx (1)

51-54: Improve SDK destructuring with proper typing.

The SDK destructuring could be more concise and type-safe.

-const { sdk: db, client: actions } = useDojoSDK<
-    typeof setupWorld,
-    SchemaType
->();
+const { sdk: themeSDK, client: themeActions } = useDojoSDK<typeof setupWorld, SchemaType>();
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 395e561 and 044df1f.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (34)
  • .changeset/great-kiwis-peel.md (1 hunks)
  • examples/example-vite-kitchen-sink/src/components/caller-counter.tsx (5 hunks)
  • examples/example-vite-kitchen-sink/src/components/chat.tsx (2 hunks)
  • examples/example-vite-kitchen-sink/src/components/global-counter.tsx (2 hunks)
  • examples/example-vite-kitchen-sink/src/components/theme-switch.tsx (2 hunks)
  • examples/example-vite-kitchen-sink/src/dojo/provider.tsx (0 hunks)
  • examples/example-vite-kitchen-sink/src/main.tsx (2 hunks)
  • examples/example-vite-react-sdk/src/App.tsx (2 hunks)
  • examples/example-vite-react-sdk/src/historical-events.tsx (1 hunks)
  • examples/example-vite-react-sdk/src/main.tsx (2 hunks)
  • examples/example-vite-react-sdk/src/useDojo.tsx (0 hunks)
  • examples/example-vite-react-sdk/src/useModel.tsx (0 hunks)
  • examples/example-vite-react-sql/package.json (2 hunks)
  • examples/example-vite-react-sql/src/components/playground/action.tsx (1 hunks)
  • examples/example-vite-react-sql/src/components/playground/direction-count.tsx (4 hunks)
  • examples/example-vite-react-sql/src/components/playground/schema.tsx (2 hunks)
  • examples/example-vite-react-sql/src/components/playground/total-entities.tsx (2 hunks)
  • examples/example-vite-react-sql/src/hooks/useDojoStore.ts (0 hunks)
  • examples/example-vite-react-sql/src/hooks/usePlayerActions.ts (1 hunks)
  • examples/example-vite-react-sql/src/hooks/useQuery.ts (0 hunks)
  • examples/example-vite-react-sql/src/hooks/useSystemCalls.ts (2 hunks)
  • examples/example-vite-react-sql/src/main.tsx (2 hunks)
  • examples/example-vite-react-sql/tsconfig.app.tsbuildinfo (1 hunks)
  • examples/example-vite-react-sql/tsconfig.node.tsbuildinfo (1 hunks)
  • packages/sdk/package.json (2 hunks)
  • packages/sdk/src/index.ts (0 hunks)
  • packages/sdk/src/react/hooks.ts (3 hunks)
  • packages/sdk/src/react/index.ts (1 hunks)
  • packages/sdk/src/react/provider.tsx (1 hunks)
  • packages/sdk/src/sql/index.ts (1 hunks)
  • packages/sdk/src/state/zustand.ts (1 hunks)
  • packages/sdk/tsconfig.json (1 hunks)
  • packages/sdk/tsup.config.ts (1 hunks)
  • pnpm-workspace.yaml (1 hunks)
💤 Files with no reviewable changes (6)
  • examples/example-vite-react-sdk/src/useDojo.tsx
  • examples/example-vite-react-sql/src/hooks/useDojoStore.ts
  • packages/sdk/src/index.ts
  • examples/example-vite-kitchen-sink/src/dojo/provider.tsx
  • examples/example-vite-react-sdk/src/useModel.tsx
  • examples/example-vite-react-sql/src/hooks/useQuery.ts
✅ Files skipped from review due to trivial changes (2)
  • examples/example-vite-react-sql/tsconfig.node.tsbuildinfo
  • packages/sdk/src/react/index.ts
🧰 Additional context used
🪛 Biome (1.9.4)
packages/sdk/src/react/provider.tsx

[error] 47-47: Don't use 'Function' as a type.

Prefer explicitly define the function shape. This type accepts any function-like value, which can be a common source of bugs.

(lint/complexity/noBannedTypes)

⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: build
  • GitHub Check: check
🔇 Additional comments (31)
packages/sdk/tsconfig.json (1)

3-3: LGTM! JSX transform configuration looks good.

The addition of "jsx": "react-jsx" is appropriate for the new React integration in the SDK.

examples/example-vite-react-sql/tsconfig.app.tsbuildinfo (1)

1-1: Skip detailed review of auto-generated build info file.

This is an auto-generated TypeScript build info file that doesn't require direct review.

However, let's verify the consistency of hook usage across the codebase since the PR involves significant changes to hooks:

#!/bin/bash
# Description: Verify the transition from useDojoStore to useDojoSDK
# and ensure no legacy hook usage remains

# Check for any remaining useDojoStore references
echo "Checking for remaining useDojoStore references..."
rg -l "useDojoStore"

# Check for new useDojoSDK usage
echo "Checking new useDojoSDK usage..."
rg -l "useDojoSDK"

# Look for potential hook-related imports that might need updates
echo "Checking hook-related imports..."
rg -l "import.*from.*@dojoengine/sdk/(hooks|react)"
packages/sdk/tsup.config.ts (1)

7-12: LGTM! Verify the entry point paths exist.

The modular structure with separate entry points for different SDK functionalities is a good architectural decision. This allows for better tree-shaking and more flexible imports.

Let's verify that all the entry point files exist:

✅ Verification successful

✓ Entry point paths verified

All specified entry points exist and contain exports, confirming the configuration is valid.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify that all entry point files exist
# Expected: All files should exist and contain exports

for file in "src/index.ts" "src/state/index.ts" "src/react/index.ts" "src/sql/index.ts"; do
  echo "Checking $file..."
  if [ -f "packages/sdk/$file" ]; then
    # Check if file contains any exports
    rg -l "export" "packages/sdk/$file"
  else
    echo "File $file does not exist"
  fi
done

Length of output: 1099

pnpm-workspace.yaml (1)

10-13: ⚠️ Potential issue

Fix invalid React package versions.

The specified version ^18.3.1 for React packages is invalid as it doesn't exist. React's latest stable version is 18.2.0.

Apply this diff to use the correct versions:

-  "@types/react": ^18.3.1
-  "@types/react-dom": ^18.3.1
-  react: ^18.3.1
-  react-dom: ^18.3.1
+  "@types/react": ^18.2.0
+  "@types/react-dom": ^18.2.0
+  react: ^18.2.0
+  react-dom: ^18.2.0

Let's verify the latest available versions:

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

1-4: LGTM! Clean and focused imports.

Good practice renaming useQuery to useTanStackQuery to avoid potential naming conflicts.

packages/sdk/package.json (1)

8-30: LGTM! Verify build outputs exist for new modules.

The exports configuration is well-structured and follows Node.js best practices for dual CJS/ESM support.

Let's verify the build outputs exist:

✅ Verification successful

✅ Source files and configuration verified

All exported modules have corresponding source files in the correct locations, and TypeScript is properly configured to generate the necessary build outputs. The package structure aligns perfectly with the exports configuration.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify that all exported module paths exist after build

# Check if the build outputs exist for all exported modules
fd -t f "^(index|state|react|sql)\.(d\.ts|js|cjs)$" dist/

Length of output: 152


Script:

#!/bin/bash
# Check source files and configuration
echo "=== Checking source files ==="
fd -e ts -e tsx "^(index|state|react|sql)\.(ts|tsx)$" src/

echo -e "\n=== Checking TypeScript config ==="
cat tsconfig.json 2>/dev/null || echo "tsconfig.json not found"

echo -e "\n=== Checking package structure ==="
ls -la src/ 2>/dev/null || echo "src/ directory not found"

Length of output: 1982


Script:

#!/bin/bash
echo "=== Checking SDK package structure ==="
ls -la packages/sdk/src/ 2>/dev/null || echo "SDK src/ directory not found"

echo -e "\n=== Checking source files ==="
fd -e ts -e tsx "^(index|state|react|sql)\.(ts|tsx)$" packages/sdk/src/

echo -e "\n=== Checking SDK package config ==="
cat packages/sdk/tsconfig.json 2>/dev/null || echo "SDK tsconfig.json not found"

Length of output: 2414

examples/example-vite-react-sql/package.json (2)

17-18: LGTM! Synchronized TanStack Router updates.

The TanStack Router ecosystem (@tanstack/router-devtools, @tanstack/router-plugin, @tanstack/react-router) has been consistently updated to version ^1.97.0, maintaining version alignment across related packages.

Also applies to: 48-48


28-28: Verify TypeScript compatibility with the new SDK.

The TypeScript update to ^5.7.3 might introduce stricter type checking. Ensure this version is compatible with the new SDK hooks and SQL integration.

Run this script to check for any TypeScript-related issues:

✅ Verification successful

TypeScript 5.7.3 is compatible with the SDK implementation.

The codebase shows proper type integration between the example project and SDK, with well-structured type definitions and no compatibility issues detected. The type system is robust with proper schema types, model generation, and type safety mechanisms in place.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for TypeScript configuration and potential type issues

# Check if tsconfig exists and its content
if [ -f "tsconfig.json" ]; then
    echo "TypeScript Config:"
    cat tsconfig.json

    # Look for any TypeScript-specific TODO or FIXME comments
    echo -e "\nChecking for TypeScript-related TODOs:"
    rg -i "todo|fixme|hack" . -g "*.ts" -g "*.tsx"
fi

Length of output: 2542


Script:

#!/bin/bash
# Check SDK and example TypeScript integration

# Find TypeScript files in SDK and example directories
echo "TypeScript files in SDK and example:"
fd -e ts -e tsx . packages/sdk examples/example-vite-react-sql

# Look for type definitions
echo -e "\nType definitions:"
fd -g "*.d.ts" . packages/sdk examples/example-vite-react-sql

# Check for type imports between example and SDK
echo -e "\nType imports:"
rg "import.*from.*@coderabbit" examples/example-vite-react-sql -g "*.ts" -g "*.tsx" -A 1

Length of output: 3820


Script:

#!/bin/bash
# Check for type usage and potential issues

# Look for type imports and usage in example project
echo "Type imports and usage in example project:"
rg "import.*type" examples/example-vite-react-sql/src -g "*.ts" -g "*.tsx" -A 1

# Check SDK type exports
echo -e "\nSDK type exports:"
rg "export.*type" packages/sdk/src/types.ts -A 1

# Look for any error handling related to types
echo -e "\nType-related error handling:"
rg "TypeError|type.*error" packages/sdk/src -g "*.ts" -g "*.tsx"

Length of output: 3725

examples/example-vite-react-sdk/src/App.tsx (5)

1-1: Imports updated appropriately.

The necessary React hooks are correctly imported.


12-12: Importing useDojoSDK from the correct package.

The useDojoSDK hook is correctly imported from @dojoengine/sdk/react.


20-20: Removed sdk parameter from App function.

The App component no longer requires the sdk prop since it's now accessed via the useDojoSDK hook internally, simplifying the component interface.


21-21: Using useDojoSDK to access SDK and store.

Destructured useDojoStore, client, and sdk from useDojoSDK(), which is an appropriate use of the hook to access the necessary SDK components.


286-286: Removed sdk prop from HistoricalEvents component.

The HistoricalEvents component now accesses sdk via the useDojoSDK hook internally, eliminating the need to pass sdk as a prop and enhancing component encapsulation.

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

7-7: Importing useDojoSDK from the correct package.

The useDojoSDK hook is correctly imported from @dojoengine/sdk/react.


9-9: Removed sdk parameter from HistoricalEvents function.

The HistoricalEvents component no longer requires the sdk prop since it's now accessed via the useDojoSDK hook internally, simplifying the component interface.


11-11: Using useDojoSDK to access sdk.

Destructured sdk from useDojoSDK(), which is appropriate for accessing the SDK within the component.

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

43-51: Verify provider nesting for correct context propagation

Please ensure that wrapping StarknetProvider inside DojoSdkProvider provides the required contexts to all child components. Confirm that the order of the providers does not affect the availability of necessary contexts in App and its descendants.


44-46: Confirm compatibility of setupWorld with clientFn

Verify that the setupWorld function passed as clientFn to DojoSdkProvider matches the expected interface and functionality required by the provider. Ensure that setupWorld properly initializes the client as intended.

examples/example-vite-kitchen-sink/src/main.tsx (2)

38-46: Verify initialization of DojoSdkProvider with correct props

Ensure that the props passed to DojoSdkProvider (sdk, dojoConfig, clientFn) are accurate and compatible with the provider's expected interface. Confirm that setupWorld is the appropriate function for clientFn and that dojoConfig contains all necessary configurations.


18-18: Ensure all references to db are updated to sdk

Since the variable db has been renamed to sdk, please verify that all references to db throughout the codebase have been updated accordingly to prevent any undefined references or runtime errors.

You can run the following script to check for any remaining references:

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

15-24: LGTM! Enhanced type safety with generic parameters.

The addition of Client and Schema generic parameters to useModel improves type safety and provides better type inference when accessing the store through context.

examples/example-vite-react-sql/src/main.tsx (1)

53-57: LGTM! Clean integration with the new SDK provider.

The transition to DojoSdkProvider with explicit configuration through dojoConfig and clientFn props provides a more structured and maintainable way to initialize the SDK.

examples/example-vite-react-sql/src/components/playground/action.tsx (1)

1-1: LGTM! Aligned with SDK restructuring.

The import path change from local hooks to @dojoengine/sdk/react aligns with the architectural changes to centralize hooks in the SDK.

examples/example-vite-react-sql/src/components/playground/schema.tsx (1)

1-1: LGTM! Migrated to SDK's SQL module.

The transition from local useQuery to useToriiSQLQuery from the SDK's SQL module maintains the same functionality while leveraging the centralized SDK implementation.

Also applies to: 49-49

examples/example-vite-react-sql/src/components/playground/direction-count.tsx (2)

1-3: LGTM! Clean import restructuring.

The imports have been properly updated to use the new SDK structure, improving modularity by separating React-specific functionality into @dojoengine/sdk/react and SQL functionality into @dojoengine/sdk/sql.


57-60: LGTM! SQL query implementation.

The transition to useToriiSQLQuery aligns with the new SDK structure while maintaining the same functionality.

examples/example-vite-react-sql/src/hooks/usePlayerActions.ts (2)

1-6: LGTM! Clean import structure.

The imports have been properly updated to use the new SDK structure, with clear separation of concerns.


10-12: Consider adding error handling for SDK initialization.

The destructuring of SDK hooks could fail if the SDK is not properly initialized.

examples/example-vite-react-sql/src/hooks/useSystemCalls.ts (1)

14-14: LGTM! Clean SDK hook usage.

The transition to useDojoSDK is properly implemented, maintaining the necessary functionality while improving the architecture.

examples/example-vite-kitchen-sink/src/components/global-counter.tsx (1)

29-29: LGTM! Proper SDK type parameters.

The SDK initialization with proper type parameters (setupWorld and SchemaType) ensures type safety throughout the component.

.changeset/great-kiwis-peel.md (1)

1-15: Verify the migration impact across the codebase.

The architectural change of moving hooks to the SDK and introducing new modules (@dojoengine/sdk/state, @dojoengine/sdk/react, @dojoengine/sdk/sql) is significant. Let's verify the migration's completeness.

Comment on lines +20 to +39
export async function queryTorii<Input, Output>(
toriiUrl: string,
query: string,
formatFn: (rows: Input) => Output
): Promise<Output> {
try {
const response = await fetch(`${toriiUrl}/sql`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: query,
});
if (!response.ok) throw new Error("Failed to fetch data");
return formatFn(await response.json());
} catch (error) {
console.error("Error fetching data:", 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

Add input validation and improve error handling.

The current implementation has several areas that could be improved for robustness:

  1. The query is sent as raw string in the body instead of a JSON structure
  2. No timeout handling for the fetch operation
  3. Basic error handling could be more informative

Consider this improved implementation:

 export async function queryTorii<Input, Output>(
     toriiUrl: string,
     query: string,
     formatFn: (rows: Input) => Output
 ): Promise<Output> {
+    if (!toriiUrl?.trim()) {
+        throw new Error("Invalid Torii URL");
+    }
+    if (!query?.trim()) {
+        throw new Error("Empty SQL query");
+    }
     try {
-        const response = await fetch(`${toriiUrl}/sql`, {
+        const controller = new AbortController();
+        const timeout = setTimeout(() => controller.abort(), 5000);
+        const response = await fetch(`${toriiUrl}/sql`, {
             method: "POST",
             headers: {
                 "Content-Type": "application/json",
             },
-            body: query,
+            body: JSON.stringify({ query }),
+            signal: controller.signal,
         });
+        clearTimeout(timeout);
-        if (!response.ok) throw new Error("Failed to fetch data");
+        if (!response.ok) {
+            throw new Error(`Failed to fetch data: ${response.status} ${response.statusText}`);
+        }
         return formatFn(await response.json());
     } catch (error) {
-        console.error("Error fetching data:", error);
+        if (error.name === 'AbortError') {
+            throw new Error('Request timeout');
+        }
+        console.error("[queryTorii] Error:", 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
export async function queryTorii<Input, Output>(
toriiUrl: string,
query: string,
formatFn: (rows: Input) => Output
): Promise<Output> {
try {
const response = await fetch(`${toriiUrl}/sql`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: query,
});
if (!response.ok) throw new Error("Failed to fetch data");
return formatFn(await response.json());
} catch (error) {
console.error("Error fetching data:", error);
throw error;
}
}
export async function queryTorii<Input, Output>(
toriiUrl: string,
query: string,
formatFn: (rows: Input) => Output
): Promise<Output> {
if (!toriiUrl?.trim()) {
throw new Error("Invalid Torii URL");
}
if (!query?.trim()) {
throw new Error("Empty SQL query");
}
try {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 5000);
const response = await fetch(`${toriiUrl}/sql`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ query }),
signal: controller.signal,
});
clearTimeout(timeout);
if (!response.ok) {
throw new Error(`Failed to fetch data: ${response.status} ${response.statusText}`);
}
return formatFn(await response.json());
} catch (error) {
if (error.name === 'AbortError') {
throw new Error('Request timeout');
}
console.error("[queryTorii] Error:", error);
throw error;
}
}

Comment on lines +55 to +60
"@types/react": "catalog:",
"@types/react-dom": "catalog:",
"@tanstack/react-query": "^5.62.16",
"starknet": "catalog:",
"react": "catalog:",
"react-dom": "catalog:"
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 invalid version specifiers in peer dependencies.

The "catalog:" version specifier is invalid. Additionally, using a fixed version for react-query in peer dependencies could cause version conflicts.

Apply this diff to fix the version specifiers:

-        "@types/react": "catalog:",
-        "@types/react-dom": "catalog:",
+        "@types/react": "^18.0.0",
+        "@types/react-dom": "^18.0.0",
         "@tanstack/react-query": "^5.62.16",
-        "starknet": "catalog:",
-        "react": "catalog:",
-        "react-dom": "catalog:"
+        "starknet": "^5.14.1",
+        "react": "^18.0.0",
+        "react-dom": "^18.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
"@types/react": "catalog:",
"@types/react-dom": "catalog:",
"@tanstack/react-query": "^5.62.16",
"starknet": "catalog:",
"react": "catalog:",
"react-dom": "catalog:"
"@types/react": "^18.0.0",
"@types/react-dom": "^18.0.0",
"@tanstack/react-query": "^5.62.16",
"starknet": "^5.14.1",
"react": "^18.0.0",
"react-dom": "^18.0.0"

*
* @param props.children - Child components that will have access to the Dojo context
* @param props.burnerManager - Instance of BurnerManager for handling burner accounts
* @throws {Error} If DojoProvider is used more than once in the component tree
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Remove the outdated parameter description in the comment.

The burnerManager prop is mentioned in the comment but is not part of the function's parameters.

Apply this diff to remove the unnecessary comment:

  * @param props.children - Child components that will have access to the Dojo context
- * @param props.burnerManager - Instance of BurnerManager for handling burner accounts
  * @throws {Error} If DojoProvider is used more than once in the component tree
📝 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
* @throws {Error} If DojoProvider is used more than once in the component tree
* @param props.children - Child components that will have access to the Dojo context
* @throws {Error} If DojoProvider is used more than once in the component tree

Schema extends SchemaType,
> {
/** The Dojo client instance */
config: DojoConfig;
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Correct the comment for the 'config' property.

The comment for the config property incorrectly states "The Dojo client instance". It should describe the actual purpose of the property.

Apply this diff to fix the comment:

-    /** The Dojo client instance */
+    /** The Dojo configuration */

Committable suggestion skipped: line range outside the PR's diff.

}: {
dojoConfig: DojoConfig;
sdk: SDK<Schema>;
clientFn: Function;
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Avoid using 'Function' as a type annotation.

Using Function as a type is discouraged because it is too generic and can lead to type safety issues. Prefer specifying the exact function signature.

Modify the type annotation to reflect the expected function shape. For example:

-    clientFn: Function;
+    clientFn: (provider: DojoProvider) => ReturnType<Client>;

Replace (provider: DojoProvider) => ReturnType<Client> with the actual function signature expected by clientFn.

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 Biome (1.9.4)

[error] 47-47: Don't use 'Function' as a type.

Prefer explicitly define the function shape. This type accepts any function-like value, which can be a common source of bugs.

(lint/complexity/noBannedTypes)

@@ -39,9 +41,10 @@ function formatFn(rows: QueryResponse): DirectionCount {

export function DirectionCount() {
// use queryClient to invalidateQuery when state is changing.
const { useDojoStore } = useContext(DojoContext);
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 using useDojoSDK hook instead of useContext.

The component is still using context directly while the PR's objective is to transition to the new SDK hooks structure.

-const { useDojoStore } = useContext(DojoContext);
+const { useDojoStore } = useDojoSDK();

Also applies to: 47-47

Comment on lines +58 to +61
if (address && sdk) {
getEntity(sdk, address).then(setCount).catch(console.error);
}
}, [address, db]);
}, [address, sdk]);
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add error boundary for SDK initialization.

The SDK usage should be wrapped in proper error handling to gracefully handle initialization failures.

-if (address && sdk) {
+try {
+  if (address && sdk) {
     getEntity(sdk, address).then(setCount).catch(console.error);
+  }
+} catch (error) {
+  console.error('Failed to initialize SDK:', error);
+  // Consider showing a user-friendly error message
}
📝 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 (address && sdk) {
getEntity(sdk, address).then(setCount).catch(console.error);
}
}, [address, db]);
}, [address, sdk]);
try {
if (address && sdk) {
getEntity(sdk, address).then(setCount).catch(console.error);
}
} catch (error) {
console.error('Failed to initialize SDK:', error);
// Consider showing a user-friendly error message
}
}, [address, sdk]);

@MartianGreed MartianGreed force-pushed the feat/add-sdk-packages branch from 044df1f to 125c510 Compare January 15, 2025 17:38
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 (4)
examples/example-vite-kitchen-sink/src/components/theme-switch.tsx (2)

Line range hint 89-89: Address type safety issues and remove @ts-expect-error comments.

There are several type safety issues that should be addressed:

  1. Enum value resolution needs proper type checking
  2. EntityId query type needs to be properly typed
  3. Theme mapping needs proper type guards

Here's how to fix these issues:

// 1. Create a type guard for theme values
function isValidTheme(theme: string): theme is keyof typeof AvailableTheme {
    return theme in AvailableTheme;
}

// 2. Type the entity query properly
interface ThemeEntity {
    entityId: string;
    // Add other required properties
}

// 3. Use type guards for theme mapping
const themeValue = entity.models?.onchain_dash?.Theme?.value.unwrap();
if (themeValue && isValidTheme(themeValue)) {
    setTheme({
        current: AvailableThemeClassMap[AvailableTheme[themeValue]],
        next: AvailableThemeClassMap[AvailableTheme[themeValue]]
    });
}

Also applies to: 115-115, 134-135


Line range hint 71-146: Improve component lifecycle management and error handling.

Several improvements could enhance the component's reliability:

  1. Add subscription cleanup
  2. Implement proper error handling
  3. Simplify useEffect logic

Here's how to improve the code:

useEffect(() => {
    if (!db || !entityId) return;

    let isSubscribed = true;

    async function subscribeToEntityUpdates() {
        try {
            const subscription = await db.subscribeEntityQuery({
                query: [entityId],
                callback: ({ data, error }) => {
                    if (!isSubscribed) return;
                    if (error) {
                        // Handle error properly, possibly with an error state
                        onError?.(error);
                        return;
                    }
                    
                    const entity = data?.pop() as ParsedEntity<SchemaType>;
                    if (!entity?.models?.onchain_dash?.Theme?.value) {
                        setTheme({
                            current: AvailableThemeClassMap[AvailableTheme.Light],
                            next: AvailableThemeClassMap[AvailableTheme.Light]
                        });
                        return;
                    }
                    
                    // Process theme update...
                }
            });
            setSub(subscription);
        } catch (error) {
            onError?.(error);
        }
    }

    subscribeToEntityUpdates();

    return () => {
        isSubscribed = false;
        sub?.unsubscribe();
    };
}, [db, entityId]);

Consider extracting the theme subscription logic into a custom hook for better reusability and testing.

examples/example-vite-kitchen-sink/src/main.tsx (1)

Line range hint 18-35: Add error handling for SDK initialization.

The SDK initialization should be wrapped in proper error handling to gracefully handle failures.

 async function main() {
+    try {
         const sdk = await init<SchemaType>(
             {
                 client: {
                     rpcUrl: getRpcUrl(),
                     toriiUrl: env.VITE_TORII_URL,
                     relayUrl: env.VITE_RELAY_URL,
                     worldAddress: dojoConfig.manifest.world.address,
                 },
                 domain: {
                     name: "OnChainDash",
                     revision: "1",
                     chainId: "1",
                     version: "1",
                 },
             },
             schema
         );
+    } catch (error) {
+        console.error("Failed to initialize SDK:", error);
+        // Consider showing a user-friendly error message or fallback UI
+        return;
+    }
examples/example-vite-react-sdk/src/historical-events.tsx (1)

Line range hint 44-65: Improve subscription error handling.

The subscription error handling could be improved to provide better cleanup and error recovery.

 } catch (error) {
-    setEvents([]);
     if (subscription) {
         subscription.free();
     }
-    console.error(error);
+    console.error('Failed to subscribe to historical events:', error);
+    // Consider implementing retry logic
+    const retryTimeout = setTimeout(() => {
+        if (account) {
+            subscribeHistoricalEvent(account);
+        }
+    }, 5000);
+    return () => clearTimeout(retryTimeout);
 }
♻️ Duplicate comments (2)
packages/sdk/package.json (1)

55-60: ⚠️ Potential issue

Fix invalid version specifiers in peer dependencies.

The "catalog:" version specifier is invalid and using fixed versions for peer dependencies could cause version conflicts.

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

20-39: ⚠️ Potential issue

Enhance error handling and request formatting.

The current implementation needs improvements in several areas:

  1. The query is sent as a raw string instead of JSON
  2. Missing input validation for toriiUrl and query
  3. No timeout handling for fetch operations
  4. Basic error handling could be more informative

The previous review comment's suggestions for improvement are still valid and should be implemented.

🧹 Nitpick comments (15)
examples/example-vite-react-sdk/src/useSystemCalls.ts (3)

4-4: Great architectural improvement!

The transition from context-based to SDK-based initialization provides better encapsulation and centralized state management. This change aligns well with modern React patterns and makes the codebase more maintainable.

Also applies to: 14-14


Line range hint 30-67: Consider enhancing error handling specificity.

While the error handling is good, it could be more specific to help with debugging and user feedback. Consider categorizing different types of errors (e.g., network issues, contract errors) and handling them separately.

Here's a suggested improvement:

         try {
             await client.actions.spawn(account!);
 
             await state.waitForEntityChange(entityId, (entity) => {
                 return (
                     entity?.models?.dojo_starter?.Moves?.remaining ===
                     remainingMoves
                 );
             });
         } catch (error) {
             state.revertOptimisticUpdate(transactionId);
-            console.error("Error executing spawn:", error);
-            throw error;
+            if (error instanceof Error) {
+                console.error(`Spawn failed: ${error.message}`);
+                if (error.message.includes("timeout")) {
+                    throw new Error("Spawn timed out. Please try again.");
+                } else if (error.message.includes("rejected")) {
+                    throw new Error("Transaction was rejected. Please check your account balance.");
+                }
+            }
+            throw new Error("Failed to spawn entity. Please try again later.");
         } finally {
             state.confirmTransaction(transactionId);
         }

13-15: Add explicit type annotations for better type safety.

While TypeScript can infer types, explicit annotations improve code maintainability and documentation.

Consider adding these type annotations:

-export const useSystemCalls = () => {
-    const { useDojoStore, client } = useDojoSDK();
-    const state = useDojoStore((state) => state);
+export const useSystemCalls = (): { spawn: () => Promise<void> } => {
+    const { useDojoStore, client } = useDojoSDK();
+    const state = useDojoStore((state: DojoState) => state);
examples/example-vite-kitchen-sink/src/components/theme-switch.tsx (1)

19-19: LGTM! Consider improving variable naming clarity.

The transition to useDojoSDK with proper type parameters enhances type safety. However, the destructuring aliases (sdk: db and client: actions) might be confusing. Consider using the original property names for better clarity:

-    const { sdk: db, client: actions } = useDojoSDK<
+    const { sdk, client } = useDojoSDK<
         typeof setupWorld,
         SchemaType
     >();

Also applies to: 51-54

packages/sdk/src/react/provider.tsx (1)

28-29: Merge the split comment.

The comment explaining the ts-expect-error is split across two lines, making it harder to read.

Apply this diff to merge the comment:

-// @ts-expect-error Since we c
-// annot dynamically set context at runtime, we will get a type error.
+// @ts-expect-error Since we cannot dynamically set context at runtime, we will get a type error.
packages/sdk/src/sql/index.ts (2)

41-43: Add missing documentation for useToriiSQLQuery function.

The function lacks proper documentation. Consider adding JSDoc comments describing:

  • Purpose and usage of the function
  • Template parameters Output and Input
  • Function parameters including their types and purposes
  • Return value structure
 /**
+ * React hook to query Torii instance over SQL endpoint with TanStack Query integration.
+ *
+ * @template Output - The type of formatted data after processing
+ * @template Input - The type of raw data from SQL query
+ * @param {string} query - Raw SQL query to execute
+ * @param {(rows: Input) => Output} formatFn - Function to format the query results
+ * @param {UseQueryOptions<Output>["placeholderData"]} [defaultValue] - Optional default value while loading
+ * @param {string} [toriiUrl] - Optional Torii URL (defaults to http://127.0.0.1:8080)
+ * @returns {Object} Query result object containing:
+ *   - data: The formatted query results
+ *   - error: Any error that occurred
+ *   - isPending: Loading state indicator
+ *   - isRefetching: Refetch state indicator
  */

54-54: Move default Torii URL to a configuration constant.

The hardcoded URL "http://127.0.0.1:8080" should be moved to a configuration constant or environment variable for better maintainability and flexibility.

+const DEFAULT_TORII_URL = process.env.TORII_URL || "http://127.0.0.1:8080";
+
 export function useToriiSQLQuery<Output, Input>(
     query: string,
     formatFn: (rows: Input) => Output,
     defaultValue?: UseQueryOptions<Output>["placeholderData"],
     toriiUrl?: string
 ) {
     // ...
-                toriiUrl ?? "http://127.0.0.1:8080",
+                toriiUrl ?? DEFAULT_TORII_URL,
examples/example-vite-kitchen-sink/src/main.tsx (1)

38-46: Consider adding error boundaries.

The DojoSdkProvider should be wrapped in an error boundary to handle runtime errors gracefully.

+import { ErrorBoundary } from 'react-error-boundary';

 createRoot(document.getElementById("root")!).render(
     <StrictMode>
+        <ErrorBoundary fallback={<div>Something went wrong</div>}>
             <DojoSdkProvider
                 sdk={sdk}
                 dojoConfig={dojoConfig}
                 clientFn={setupWorld}
             >
                 <RootLayout>
                     <Home />
                 </RootLayout>
             </DojoSdkProvider>
+        </ErrorBoundary>
     </StrictMode>
 );
examples/example-vite-react-sdk/src/main.tsx (1)

Line range hint 17-23: Update JSDoc to reflect new provider structure.

The JSDoc comment is outdated as it references initialization and burner manager setup that's no longer relevant.

 /**
  * Initializes and bootstraps the Dojo application.
- * Sets up the SDK, burner manager, and renders the root component.
+ * Sets up the SDK with DojoSdkProvider and renders the root component.
  *
- * @throws {Error} If initialization fails
+ * @throws {Error} If SDK initialization fails
  */
examples/example-vite-react-sdk/src/historical-events.tsx (1)

Line range hint 18-31: Consider uncommenting and using the structured query.

The commented query provides better type safety and filtering capabilities compared to the current entityIds approach.

-                query: { entityIds: [addAddressPadding(account.address)] },
+                query: {
+                  event_messages_historical: {
+                    Moved: {
+                      $: { where: { player: { $eq: addAddressPadding(account.address) } } }
+                    }
+                  }
+                },
examples/example-vite-kitchen-sink/src/components/caller-counter.tsx (1)

Line range hint 105-115: Improve subscription cleanup logic.

The subscription cleanup could be more robust by handling potential cleanup errors.

 return () => {
     if (sub) {
+        try {
             sub.free();
+        } catch (error) {
+            console.error('Failed to cleanup subscription:', error);
+        }
     }
 };
examples/example-vite-react-sdk/src/App.tsx (2)

284-284: Remove outdated comment.

The comment about passing SDK as props is no longer relevant since the component now uses context.

-                {/* // Here sdk is passed as props but this can be done via contexts */}
                 <HistoricalEvents />

Line range hint 19-24: Consider extracting store initialization.

The store initialization logic could be extracted to a custom hook for better reusability and testing.

// useGameStore.ts
export function useGameStore() {
    const { useDojoStore, client, sdk } = useDojoSDK();
    const state = useDojoStore((state) => state);
    const entities = useDojoStore((state) => state.entities);
    return { state, entities, client, sdk };
}
packages/sdk/src/react/hooks.ts (2)

Line range hint 6-34: Update JSDoc to document new generic parameters.

The function documentation should be updated to include descriptions for the new generic parameters Client and Schema.

 /**
  * Custom hook to retrieve a specific model for a given entityId within a specified namespace.
  *
+ * @typeParam Client - The client function type
+ * @typeParam Schema - The schema type extending SchemaType
  * @param entityId - The ID of the entity.
  * @param model - The model to retrieve, specified as a string in the format "namespace-modelName".
  * @returns The model structure if found, otherwise undefined.
  */

36-41: Add documentation and error handling for useDojoSDK hook.

The new hook would benefit from:

  1. JSDoc documentation explaining its purpose and usage
  2. Error handling for when used outside the DojoContext provider
+/**
+ * Custom hook to access the Dojo SDK context.
+ * 
+ * @typeParam Client - The client function type
+ * @typeParam Schema - The schema type extending SchemaType
+ * @returns The Dojo context value
+ * @throws {Error} When used outside of DojoContext.Provider
+ */
 export function useDojoSDK<
     Client extends (...args: any) => any,
     Schema extends SchemaType,
 >(): DojoContextType<Client, Schema> {
-    return useContext(DojoContext);
+    const context = useContext(DojoContext);
+    if (!context) {
+        throw new Error('useDojoSDK must be used within a DojoContext.Provider');
+    }
+    return context;
 }
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 044df1f and 125c510.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (36)
  • .changeset/great-kiwis-peel.md (1 hunks)
  • examples/example-vite-kitchen-sink/src/components/caller-counter.tsx (5 hunks)
  • examples/example-vite-kitchen-sink/src/components/chat.tsx (2 hunks)
  • examples/example-vite-kitchen-sink/src/components/global-counter.tsx (2 hunks)
  • examples/example-vite-kitchen-sink/src/components/theme-switch.tsx (2 hunks)
  • examples/example-vite-kitchen-sink/src/dojo/provider.tsx (0 hunks)
  • examples/example-vite-kitchen-sink/src/main.tsx (2 hunks)
  • examples/example-vite-react-sdk/src/App.tsx (2 hunks)
  • examples/example-vite-react-sdk/src/historical-events.tsx (1 hunks)
  • examples/example-vite-react-sdk/src/main.tsx (2 hunks)
  • examples/example-vite-react-sdk/src/useDojo.tsx (0 hunks)
  • examples/example-vite-react-sdk/src/useModel.tsx (0 hunks)
  • examples/example-vite-react-sdk/src/useSystemCalls.ts (2 hunks)
  • examples/example-vite-react-sdk/tsconfig.app.tsbuildinfo (1 hunks)
  • examples/example-vite-react-sql/package.json (2 hunks)
  • examples/example-vite-react-sql/src/components/playground/action.tsx (1 hunks)
  • examples/example-vite-react-sql/src/components/playground/direction-count.tsx (4 hunks)
  • examples/example-vite-react-sql/src/components/playground/schema.tsx (2 hunks)
  • examples/example-vite-react-sql/src/components/playground/total-entities.tsx (2 hunks)
  • examples/example-vite-react-sql/src/hooks/useDojoStore.ts (0 hunks)
  • examples/example-vite-react-sql/src/hooks/usePlayerActions.ts (1 hunks)
  • examples/example-vite-react-sql/src/hooks/useQuery.ts (0 hunks)
  • examples/example-vite-react-sql/src/hooks/useSystemCalls.ts (2 hunks)
  • examples/example-vite-react-sql/src/main.tsx (2 hunks)
  • examples/example-vite-react-sql/tsconfig.app.tsbuildinfo (1 hunks)
  • examples/example-vite-react-sql/tsconfig.node.tsbuildinfo (1 hunks)
  • packages/sdk/package.json (2 hunks)
  • packages/sdk/src/index.ts (0 hunks)
  • packages/sdk/src/react/hooks.ts (3 hunks)
  • packages/sdk/src/react/index.ts (1 hunks)
  • packages/sdk/src/react/provider.tsx (1 hunks)
  • packages/sdk/src/sql/index.ts (1 hunks)
  • packages/sdk/src/state/zustand.ts (1 hunks)
  • packages/sdk/tsconfig.json (1 hunks)
  • packages/sdk/tsup.config.ts (1 hunks)
  • pnpm-workspace.yaml (1 hunks)
💤 Files with no reviewable changes (6)
  • examples/example-vite-react-sdk/src/useDojo.tsx
  • packages/sdk/src/index.ts
  • examples/example-vite-kitchen-sink/src/dojo/provider.tsx
  • examples/example-vite-react-sdk/src/useModel.tsx
  • examples/example-vite-react-sql/src/hooks/useQuery.ts
  • examples/example-vite-react-sql/src/hooks/useDojoStore.ts
✅ Files skipped from review due to trivial changes (1)
  • examples/example-vite-react-sdk/tsconfig.app.tsbuildinfo
🚧 Files skipped from review as they are similar to previous changes (18)
  • examples/example-vite-react-sql/tsconfig.node.tsbuildinfo
  • packages/sdk/tsconfig.json
  • packages/sdk/src/state/zustand.ts
  • packages/sdk/src/react/index.ts
  • .changeset/great-kiwis-peel.md
  • examples/example-vite-react-sql/tsconfig.app.tsbuildinfo
  • examples/example-vite-react-sql/src/components/playground/action.tsx
  • packages/sdk/tsup.config.ts
  • examples/example-vite-react-sql/src/main.tsx
  • examples/example-vite-react-sql/src/components/playground/schema.tsx
  • examples/example-vite-react-sql/src/hooks/usePlayerActions.ts
  • examples/example-vite-react-sql/src/components/playground/total-entities.tsx
  • pnpm-workspace.yaml
  • examples/example-vite-react-sql/src/hooks/useSystemCalls.ts
  • examples/example-vite-kitchen-sink/src/components/chat.tsx
  • examples/example-vite-kitchen-sink/src/components/global-counter.tsx
  • examples/example-vite-react-sql/src/components/playground/direction-count.tsx
  • examples/example-vite-react-sql/package.json
🧰 Additional context used
🪛 Biome (1.9.4)
packages/sdk/src/react/provider.tsx

[error] 47-47: Don't use 'Function' as a type.

Prefer explicitly define the function shape. This type accepts any function-like value, which can be a common source of bugs.

(lint/complexity/noBannedTypes)

⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: build
  • GitHub Check: check
🔇 Additional comments (10)
packages/sdk/package.json (2)

8-30: LGTM! Well-structured exports configuration.

The exports field is well-organized with proper submodule separation and follows Node.js dual package hazard patterns. The TypeScript types are correctly configured.


63-63: LGTM! Correct workspace dependency configuration.

The addition of @dojoengine/core as a workspace dependency is properly configured using the workspace:* syntax.

packages/sdk/src/react/provider.tsx (4)

36-37: Remove the outdated parameter description in the comment.

The burnerManager prop is mentioned in the comment but is not part of the function's parameters.


47-47: Avoid using 'Function' as a type annotation.

Using Function as a type is too generic and can lead to type safety issues. Prefer specifying the exact function signature.

🧰 Tools
🪛 Biome (1.9.4)

[error] 47-47: Don't use 'Function' as a type.

Prefer explicitly define the function shape. This type accepts any function-like value, which can be a common source of bugs.

(lint/complexity/noBannedTypes)


39-73: Well-structured provider implementation!

The DojoSdkProvider implementation is clean and follows best practices:

  • Prevents multiple provider instances
  • Properly initializes the provider with config
  • Provides all necessary context values to children
🧰 Tools
🪛 Biome (1.9.4)

[error] 47-47: Don't use 'Function' as a type.

Prefer explicitly define the function shape. This type accepts any function-like value, which can be a common source of bugs.

(lint/complexity/noBannedTypes)


14-16: 🛠️ Refactor suggestion

Fix duplicate JSDoc comment.

The comment "The Dojo client instance" is used for both config and client properties. The comment for config should be updated to reflect its actual purpose.

Apply this diff to fix the comments:

-    /** The Dojo client instance */
+    /** The Dojo configuration object */
     config: DojoConfig;
     /** The Dojo client instance */
     client: ReturnType<Client>;

Likely invalid or redundant comment.

examples/example-vite-react-sdk/src/main.tsx (1)

43-51: LGTM! Well-structured provider hierarchy.

The nesting of providers (DojoSdkProvider -> StarknetProvider -> App) is logically organized.

examples/example-vite-kitchen-sink/src/components/caller-counter.tsx (1)

58-61: Add error boundary for SDK initialization.

The SDK usage should be wrapped in proper error handling to gracefully handle initialization failures.

packages/sdk/src/react/hooks.ts (2)

1-4: LGTM! Clean import organization.

The imports are well-organized and follow a good separation of concerns by moving types to a dedicated file.


Line range hint 1-41: Verify migration path for existing consumers.

Since this change relocates hooks to the SDK and introduces breaking changes (useDojoDb → useDojoSDK), please ensure:

  1. All example projects have been updated
  2. Migration guide is provided
  3. Version bump follows semver
✅ Verification successful

Migration verification completed successfully

The codebase shows complete adoption of the new hooks:

  • All example projects have been updated to use useDojoSDK/useModel
  • No legacy useDojoDb usage found
  • Multiple reference implementations available in examples/

While explicit migration documentation could be beneficial, the example projects (particularly example-vite-react-sdk) serve as comprehensive implementation references.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for any remaining useDojoDb usage that needs migration
rg "useDojoDb"

# Check for package.json to verify version bump
cat package.json | jq .version

Length of output: 52


Script:

#!/bin/bash
# Find and check SDK package.json
cat packages/sdk/package.json | jq .version

# Look for example projects
fd -t d "example"

# Check for migration guide
fd -t f "MIGRATION|UPGRADING|CHANGELOG" -i

Length of output: 917


Script:

#!/bin/bash
# Check SDK changelog for migration notes
cat packages/sdk/CHANGELOG.md

# Check hook usage in example projects
rg -A 2 "useDojoSDK|useModel" examples/

Length of output: 10425

Comment on lines +59 to +60
// @ts-expect-error trust me
placeholderData: defaultValue ?? [],
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 type issue instead of suppressing TypeScript error.

The @ts-expect-error comment suggests a type compatibility issue that should be fixed rather than suppressed. The default value of an empty array might not be compatible with all possible Output types.

Consider implementing proper type checking:

-        // @ts-expect-error trust me
-        placeholderData: defaultValue ?? [],
+        placeholderData: defaultValue ?? ([] as unknown as Output),

Additionally, consider making the default value type-safe by:

  1. Making it required when Output is not array-like
  2. Using a type guard to ensure compatibility
📝 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
// @ts-expect-error trust me
placeholderData: defaultValue ?? [],
placeholderData: defaultValue ?? ([] as unknown as Output),

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