;
+ };
+}
+export const schema: SchemaType = {
+ dojo_starter: {
+ DirectionsAvailable: {
+ fieldOrder: ["player", "directions"],
+ player: "",
+ directions: [
+ new CairoCustomEnum({
+ Left: "",
+ Right: undefined,
+ Up: undefined,
+ Down: undefined,
+ }),
+ ],
+ },
+ DirectionsAvailableValue: {
+ fieldOrder: ["directions"],
+ directions: [
+ new CairoCustomEnum({
+ Left: "",
+ Right: undefined,
+ Up: undefined,
+ Down: undefined,
+ }),
+ ],
+ },
+ Moves: {
+ fieldOrder: ["player", "remaining", "last_direction", "can_move"],
+ player: "",
+ remaining: 0,
+ last_direction: new CairoOption(CairoOptionVariant.None),
+ can_move: false,
+ },
+ MovesValue: {
+ fieldOrder: ["remaining", "last_direction", "can_move"],
+ remaining: 0,
+ last_direction: new CairoOption(CairoOptionVariant.None),
+ can_move: false,
+ },
+ Position: {
+ fieldOrder: ["player", "vec"],
+ player: "",
+ vec: { x: 0, y: 0 },
+ },
+ PositionValue: {
+ fieldOrder: ["vec"],
+ vec: { x: 0, y: 0 },
+ },
+ Vec2: {
+ fieldOrder: ["x", "y"],
+ x: 0,
+ y: 0,
+ },
+ Moved: {
+ fieldOrder: ["player", "direction"],
+ player: "",
+ direction: new CairoCustomEnum({
+ Left: "",
+ Right: undefined,
+ Up: undefined,
+ Down: undefined,
+ }),
+ },
+ MovedValue: {
+ fieldOrder: ["direction"],
+ direction: new CairoCustomEnum({
+ Left: "",
+ Right: undefined,
+ Up: undefined,
+ Down: undefined,
+ }),
+ },
+ },
+};
+export enum ModelsMapping {
+ Direction = "dojo_starter-Direction",
+ DirectionsAvailable = "dojo_starter-DirectionsAvailable",
+ DirectionsAvailableValue = "dojo_starter-DirectionsAvailableValue",
+ Moves = "dojo_starter-Moves",
+ MovesValue = "dojo_starter-MovesValue",
+ Position = "dojo_starter-Position",
+ PositionValue = "dojo_starter-PositionValue",
+ Vec2 = "dojo_starter-Vec2",
+ Moved = "dojo_starter-Moved",
+ MovedValue = "dojo_starter-MovedValue",
+}
diff --git a/examples/example-vite-experimental-sdk/src/update-manager.ts b/examples/example-vite-experimental-sdk/src/update-manager.ts
new file mode 100644
index 00000000..55011d07
--- /dev/null
+++ b/examples/example-vite-experimental-sdk/src/update-manager.ts
@@ -0,0 +1,86 @@
+import hljs from "highlight.js";
+
+/**
+ * Manages the display and interaction of updates in the playground.
+ */
+export class UpdateManager {
+ container: HTMLDivElement;
+ constructor() {
+ // Create the main container for updates
+ this.container = document.createElement("div");
+ this.container.id = "updates";
+ this.container.style.cssText = `
+ height: 80vh;
+ overflow-y: auto;
+ margin-top: 5vh;
+ padding: 10px;
+ `;
+ document.body.appendChild(this.container);
+ }
+
+ /**
+ * Displays an update in the updates div.
+ *
+ * The `updates` div must be defined in the HTML file.
+ *
+ * @param {Object} update - The update to display as a JSON object.
+ */
+ displayUpdate(title: string, update: any) {
+ const updatesDiv = document.getElementById("updates")!;
+ const updateContainer = document.createElement("div");
+ updateContainer.style.position = "relative";
+
+ const titleElement = document.createElement("div");
+ titleElement.textContent = title;
+ titleElement.style.cssText = `
+ padding: 8px 12px;
+ border-top: 1px solid #ddd;
+ color: #666;
+ font-family: monospace;
+ font-size: 0.9em;
+ `;
+
+ const updateElement = document.createElement("pre");
+ updateElement.style.margin = "8px";
+ updateElement.style.padding = "12px";
+ updateElement.style.backgroundColor = "#f5f5f5";
+ updateElement.style.borderRadius = "4px";
+ updateElement.style.fontFamily = "monospace";
+ updateElement.style.fontSize = "10px";
+ updateElement.innerHTML = `${JSON.stringify(update, null, 2)}
`;
+ hljs.highlightElement(updateElement.firstChild! as HTMLElement);
+
+ const copyButton = document.createElement("button");
+ copyButton.textContent = "copy";
+ copyButton.style.cssText = `
+ position: absolute;
+ top: 5px;
+ right: 5px;
+ padding: 2px 4px;
+ border: none;
+ border-radius: 4px;
+ background: #e0e0e0;
+ cursor: pointer;
+ `;
+
+ copyButton.onclick = async () => {
+ try {
+ await navigator.clipboard.writeText(
+ JSON.stringify(update, null, 2)
+ );
+ copyButton.textContent = "✓";
+ setTimeout(() => (copyButton.textContent = "copy"), 1000);
+ } catch (err) {
+ console.error("Failed to copy:", err);
+ copyButton.textContent = "❌";
+ setTimeout(() => (copyButton.textContent = "copy"), 1000);
+ }
+ };
+
+ updateContainer.appendChild(titleElement);
+ updateContainer.appendChild(updateElement);
+ updateContainer.appendChild(copyButton);
+ updatesDiv.appendChild(updateContainer);
+ updatesDiv.scrollTop = updatesDiv.scrollHeight;
+ }
+}
diff --git a/examples/example-vite-experimental-sdk/src/vite-env.d.ts b/examples/example-vite-experimental-sdk/src/vite-env.d.ts
new file mode 100644
index 00000000..11f02fe2
--- /dev/null
+++ b/examples/example-vite-experimental-sdk/src/vite-env.d.ts
@@ -0,0 +1 @@
+///
diff --git a/examples/example-vite-experimental-sdk/tsconfig.json b/examples/example-vite-experimental-sdk/tsconfig.json
new file mode 100644
index 00000000..1a33e88c
--- /dev/null
+++ b/examples/example-vite-experimental-sdk/tsconfig.json
@@ -0,0 +1,22 @@
+{
+ "compilerOptions": {
+ "target": "ES2022",
+ "useDefineForClassFields": true,
+ "module": "ESNext",
+ "lib": ["ES2023"],
+ "skipLibCheck": true,
+ /* Bundler mode */
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "isolatedModules": true,
+ "moduleDetection": "force",
+ "noEmit": true,
+ /* Linting */
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "noFallthroughCasesInSwitch": true,
+ "noUncheckedSideEffectImports": true
+ },
+ "include": ["src", "vite.config.ts"]
+}
diff --git a/examples/example-vite-experimental-sdk/vite.config.ts b/examples/example-vite-experimental-sdk/vite.config.ts
new file mode 100644
index 00000000..9ec33a86
--- /dev/null
+++ b/examples/example-vite-experimental-sdk/vite.config.ts
@@ -0,0 +1,17 @@
+import path from "path";
+import { defineConfig } from "vite";
+import wasm from "vite-plugin-wasm";
+import topLevelAwait from "vite-plugin-top-level-await";
+
+// https://vitejs.dev/config/
+export default defineConfig({
+ plugins: [wasm(), topLevelAwait()],
+ resolve: {
+ alias: {
+ "@": path.resolve(__dirname, "./src"),
+ },
+ },
+ build: {
+ target: "ES2022",
+ },
+});
diff --git a/examples/example-vite-react-sdk/README.md b/examples/example-vite-react-sdk/README.md
index 44f2acc0..544eeee4 100644
--- a/examples/example-vite-react-sdk/README.md
+++ b/examples/example-vite-react-sdk/README.md
@@ -6,45 +6,3 @@ Currently, two official plugins are available:
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
-
-## Expanding the ESLint configuration
-
-If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:
-
-- Configure the top-level `parserOptions` property like this:
-
-```js
-export default tseslint.config({
- languageOptions: {
- // other options...
- parserOptions: {
- project: ["./tsconfig.node.json", "./tsconfig.app.json"],
- tsconfigRootDir: import.meta.dirname,
- },
- },
-});
-```
-
-- Replace `tseslint.configs.recommended` to `tseslint.configs.recommendedTypeChecked` or `tseslint.configs.strictTypeChecked`
-- Optionally add `...tseslint.configs.stylisticTypeChecked`
-- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and update the config:
-
-```js
-// eslint.config.js
-import react from "eslint-plugin-react";
-
-export default tseslint.config({
- // Set the react version
- settings: { react: { version: "18.3" } },
- plugins: {
- // Add the react plugin
- react,
- },
- rules: {
- // other rules...
- // Enable its recommended rules
- ...react.configs.recommended.rules,
- ...react.configs["jsx-runtime"].rules,
- },
-});
-```
diff --git a/examples/example-vite-react-sdk/src/App.tsx b/examples/example-vite-react-sdk/src/App.tsx
index 80487a4f..a3c284c7 100644
--- a/examples/example-vite-react-sdk/src/App.tsx
+++ b/examples/example-vite-react-sdk/src/App.tsx
@@ -31,6 +31,26 @@ function App() {
return BigInt(0);
}, [account]);
+ // This is experimental feature.
+ // Use those queries if you want to be closer to how you should query your ecs system with torii
+ // useEffect(() => {
+ // async function fetchToriiClause() {
+ // const res = await sdk.client.getEntities(
+ // new ToriiQueryBuilder()
+ // .withClause(
+ // new ClauseBuilder()
+ // .keys([], [undefined], "VariableLen")
+ // .build()
+ // )
+ // .withLimit(2)
+ // .addOrderBy(ModelsMapping.Moves, "remaining", "Desc")
+ // .build()
+ // );
+ // return res;
+ // }
+ // fetchToriiClause().then(console.log);
+ // });
+
useEffect(() => {
let unsubscribe: (() => void) | undefined;
@@ -248,7 +268,7 @@ function App() {
className="text-gray-300"
>
- {entityId}
+ {addAddressPadding(entityId)}
|
{position?.player ?? "N/A"}
diff --git a/packages/sdk/package.json b/packages/sdk/package.json
index 26d2b4b5..445a242a 100644
--- a/packages/sdk/package.json
+++ b/packages/sdk/package.json
@@ -26,6 +26,11 @@
"types": "./dist/src/sql.d.ts",
"import": "./dist/src/sql.js",
"require": "./dist/src/sql.cjs"
+ },
+ "./experimental": {
+ "types": "./dist/src/experimental.d.ts",
+ "import": "./dist/src/experimental.js",
+ "require": "./dist/src/experimental.cjs"
}
},
"type": "module",
diff --git a/packages/sdk/src/__tests__/clauseBuilder.test.ts b/packages/sdk/src/__tests__/clauseBuilder.test.ts
new file mode 100644
index 00000000..5c3afb16
--- /dev/null
+++ b/packages/sdk/src/__tests__/clauseBuilder.test.ts
@@ -0,0 +1,211 @@
+import { describe, expect, it } from "vitest";
+import { ClauseBuilder } from "../clauseBuilder";
+import {
+ ComparisonOperator,
+ LogicalOperator,
+ PatternMatching,
+} from "@dojoengine/torii-client";
+import { SchemaType } from "../types";
+
+// Test models interface
+interface TestModels extends SchemaType {
+ dojo_starter: {
+ Moves: {
+ fieldOrder: string[];
+ remaining: number;
+ player: string;
+ };
+ Position: {
+ fieldOrder: string[];
+ x: number;
+ y: number;
+ };
+ GameState: {
+ fieldOrder: string[];
+ active: boolean;
+ score: number;
+ gameId: string;
+ };
+ };
+}
+
+describe("ClauseBuilder", () => {
+ describe("whereKeys", () => {
+ it("should create a Keys clause with default pattern matching", () => {
+ const builder = new ClauseBuilder();
+ const clause = builder
+ .keys(["dojo_starter-Moves"], ["player1"])
+ .build();
+
+ expect(clause).toEqual({
+ Keys: {
+ keys: ["player1"],
+ pattern_matching: "VariableLen" as PatternMatching,
+ models: ["dojo_starter-Moves"],
+ },
+ });
+ });
+
+ it("should create a Keys clause with custom pattern matching", () => {
+ const builder = new ClauseBuilder();
+ const clause = builder
+ .keys(["dojo_starter-Moves"], ["player1"], "VariableLen")
+ .build();
+
+ expect(clause).toEqual({
+ Keys: {
+ keys: ["player1"],
+ pattern_matching: "VariableLen" as PatternMatching,
+ models: ["dojo_starter-Moves"],
+ },
+ });
+ });
+ });
+
+ describe("where", () => {
+ it("should create a Member clause with number value", () => {
+ const builder = new ClauseBuilder();
+ const clause = builder
+ .where("dojo_starter-Moves", "remaining", "Gt", 10)
+ .build();
+
+ expect(clause).toEqual({
+ Member: {
+ model: "dojo_starter-Moves",
+ member: "remaining",
+ operator: "Gt" as ComparisonOperator,
+ value: { Primitive: { U32: 10 } },
+ },
+ });
+ });
+
+ it("should create a Member clause with string value", () => {
+ const builder = new ClauseBuilder();
+ const clause = builder
+ .where("dojo_starter-Moves", "player", "Eq", "player1")
+ .build();
+
+ expect(clause).toEqual({
+ Member: {
+ model: "dojo_starter-Moves",
+ member: "player",
+ operator: "Eq" as ComparisonOperator,
+ value: { String: "player1" },
+ },
+ });
+ });
+ });
+
+ describe("compose", () => {
+ it("should create a composite OR then AND clause", () => {
+ const clause = new ClauseBuilder()
+ .compose()
+ .or([
+ new ClauseBuilder().where(
+ "dojo_starter-Position",
+ "x",
+ "Gt",
+ 0
+ ),
+ new ClauseBuilder().where(
+ "dojo_starter-Position",
+ "y",
+ "Gt",
+ 0
+ ),
+ ])
+ .and([
+ new ClauseBuilder().where(
+ "dojo_starter-GameState",
+ "active",
+ "Eq",
+ true
+ ),
+ ])
+ .build();
+
+ expect(clause).toEqual({
+ Composite: {
+ operator: "And",
+ clauses: [
+ {
+ Member: {
+ model: "dojo_starter-GameState",
+ member: "active",
+ operator: "Eq" as ComparisonOperator,
+ value: { Primitive: { Bool: true } },
+ },
+ },
+ {
+ Composite: {
+ operator: "Or",
+ clauses: [
+ {
+ Member: {
+ model: "dojo_starter-Position",
+ member: "x",
+ operator:
+ "Gt" as ComparisonOperator,
+ value: { Primitive: { U32: 0 } },
+ },
+ },
+ {
+ Member: {
+ model: "dojo_starter-Position",
+ member: "y",
+ operator:
+ "Gt" as ComparisonOperator,
+ value: { Primitive: { U32: 0 } },
+ },
+ },
+ ],
+ },
+ },
+ ],
+ },
+ });
+ });
+
+ it("should handle single composite operation", () => {
+ const builder = new ClauseBuilder();
+ const clauseA = new ClauseBuilder().where(
+ "dojo_starter-Position",
+ "x",
+ "Gt",
+ 0
+ );
+ const clauseB = new ClauseBuilder().where(
+ "dojo_starter-Position",
+ "y",
+ "Gt",
+ 0
+ );
+
+ const clause = builder.compose().or([clauseA, clauseB]).build();
+
+ expect(clause).toEqual({
+ Composite: {
+ operator: "Or",
+ clauses: [
+ {
+ Member: {
+ model: "dojo_starter-Position",
+ member: "x",
+ operator: "Gt" as ComparisonOperator,
+ value: { Primitive: { U32: 0 } },
+ },
+ },
+ {
+ Member: {
+ model: "dojo_starter-Position",
+ member: "y",
+ operator: "Gt" as ComparisonOperator,
+ value: { Primitive: { U32: 0 } },
+ },
+ },
+ ],
+ },
+ });
+ });
+ });
+});
diff --git a/packages/sdk/src/__tests__/parseEntities.test.ts b/packages/sdk/src/__tests__/parseEntities.test.ts
index 72333a36..2b003a58 100644
--- a/packages/sdk/src/__tests__/parseEntities.test.ts
+++ b/packages/sdk/src/__tests__/parseEntities.test.ts
@@ -140,50 +140,52 @@ describe("parseEntities", () => {
const result = parseEntities(mockEntities);
- expect(result).toEqual([
- {
- entityId:
- "0x14c362c17947ef1d40152d6e3bedd859c98bebfad41f75ef3f26798556a4c85",
- models: {
- dojo_starter: {
- Position: {
- player: "0x7f7e355d3ae20c34de26c21b46612622f4e4012e7debc10f0300cf193a46366",
- vec: {
- x: 6,
- y: 10,
+ expect(result).toEqual({
+ "0x14c362c17947ef1d40152d6e3bedd859c98bebfad41f75ef3f26798556a4c85":
+ {
+ entityId:
+ "0x14c362c17947ef1d40152d6e3bedd859c98bebfad41f75ef3f26798556a4c85",
+ models: {
+ dojo_starter: {
+ Position: {
+ player: "0x7f7e355d3ae20c34de26c21b46612622f4e4012e7debc10f0300cf193a46366",
+ vec: {
+ x: 6,
+ y: 10,
+ },
+ },
+ Moves: {
+ last_direction: "Left",
+ remaining: 98,
+ can_move: true,
+ player: "0x7f7e355d3ae20c34de26c21b46612622f4e4012e7debc10f0300cf193a46366",
},
- },
- Moves: {
- last_direction: "Left",
- remaining: 98,
- can_move: true,
- player: "0x7f7e355d3ae20c34de26c21b46612622f4e4012e7debc10f0300cf193a46366",
},
},
},
- },
- {
- entityId:
- "0x144c128b8ead7d0da39c6a150abbfdd38f572ba9418d3e36929eb6107b4ce4d",
- models: {
- dojo_starter: {
- Moves: {
- last_direction: "Left",
- remaining: 99,
- can_move: true,
- player: "0x70c774f8d061323ada4e4924c12c894f39b5874b71147af254b3efae07e68c0",
- },
- Position: {
- player: "0x70c774f8d061323ada4e4924c12c894f39b5874b71147af254b3efae07e68c0",
- vec: {
- x: 6,
- y: 10,
+ "0x144c128b8ead7d0da39c6a150abbfdd38f572ba9418d3e36929eb6107b4ce4d":
+ {
+ entityId:
+ "0x144c128b8ead7d0da39c6a150abbfdd38f572ba9418d3e36929eb6107b4ce4d",
+ models: {
+ dojo_starter: {
+ Moves: {
+ last_direction: "Left",
+ remaining: 99,
+ can_move: true,
+ player: "0x70c774f8d061323ada4e4924c12c894f39b5874b71147af254b3efae07e68c0",
+ },
+ Position: {
+ player: "0x70c774f8d061323ada4e4924c12c894f39b5874b71147af254b3efae07e68c0",
+ vec: {
+ x: 6,
+ y: 10,
+ },
},
},
},
},
- },
- ]);
+ });
});
it("should parse Options", () => {
@@ -223,9 +225,11 @@ describe("parseEntities", () => {
const res = parseEntities(toriiResult);
const expected = new CairoOption(CairoOptionVariant.Some, 1734537235);
// @ts-ignore can be undefined
- expect(res[0].models.onchain_dash.CallerCounter.timestamp).toEqual(
- expected
- );
+ expect(
+ res[
+ "0x43ebbfee0476dcc36cae36dfa9b47935cc20c36cb4dc7d014076e5f875cf164"
+ ].models.onchain_dash.CallerCounter.timestamp
+ ).toEqual(expected);
});
it("should parse complex enums", () => {
const toriiResult: torii.Entities = {
@@ -278,7 +282,11 @@ describe("parseEntities", () => {
const res = parseEntities(toriiResult);
const expected = new CairoCustomEnum({ Predefined: "Dojo" });
// @ts-ignore can be undefined
- expect(res[0].models.onchain_dash.Theme.value).toEqual(expected);
+ expect(
+ res[
+ "0x5248d30cafd7af5e7f9255ed9bef2bd7aa0f191669a4c1e3a03b8c64ea5a9d8"
+ ].models.onchain_dash.Theme.value
+ ).toEqual(expected);
});
it("should parse enum with nested struct", () => {
@@ -336,6 +344,10 @@ describe("parseEntities", () => {
},
});
// @ts-ignore can be undefined
- expect(res[0].models.onchain_dash.Theme.value).toEqual(expected);
+ expect(
+ res[
+ "0x5248d30cafd7af5e7f9255ed9bef2bd7aa0f191669a4c1e3a03b8c64ea5a9d8"
+ ].models.onchain_dash.Theme.value
+ ).toEqual(expected);
});
});
diff --git a/packages/sdk/src/__tests__/toriiQueryBuilder.test.ts b/packages/sdk/src/__tests__/toriiQueryBuilder.test.ts
new file mode 100644
index 00000000..c8d43f7c
--- /dev/null
+++ b/packages/sdk/src/__tests__/toriiQueryBuilder.test.ts
@@ -0,0 +1,185 @@
+import { describe, expect, it } from "vitest";
+import { ToriiQueryBuilder } from "../toriiQueryBuilder";
+import { Clause, OrderBy } from "@dojoengine/torii-client";
+import { SchemaType } from "../types";
+import { ClauseBuilder } from "../clauseBuilder";
+
+interface TestModels extends SchemaType {
+ dojo_starter: {
+ Moves: {
+ fieldOrder: string[];
+ remaining: number;
+ player: string;
+ };
+ Position: {
+ fieldOrder: string[];
+ x: number;
+ y: number;
+ };
+ };
+}
+
+describe("ToriiQueryBuilder", () => {
+ describe("basic query building", () => {
+ it("override default options", () => {
+ const builder = new ToriiQueryBuilder({ limit: 25 });
+ const query = builder.build();
+
+ expect(query).toEqual({
+ limit: 25,
+ offset: 0,
+ clause: undefined,
+ dont_include_hashed_keys: true,
+ order_by: [],
+ entity_models: [],
+ entity_updated_after: 0,
+ });
+ });
+
+ it("should create a query with default values", () => {
+ const builder = new ToriiQueryBuilder();
+ const query = builder.build();
+
+ expect(query).toEqual({
+ limit: 100,
+ offset: 0,
+ clause: undefined,
+ dont_include_hashed_keys: true,
+ order_by: [],
+ entity_models: [],
+ entity_updated_after: 0,
+ });
+ });
+
+ it("should set limit and offset", () => {
+ const builder = new ToriiQueryBuilder();
+ const query = builder.withLimit(10).withOffset(20).build();
+
+ expect(query.limit).toBe(10);
+ expect(query.offset).toBe(20);
+ });
+
+ it("should set clause", () => {
+ const mockClause: Clause = {
+ Member: {
+ model: "dojo_starter-Position",
+ member: "x",
+ operator: "Gt",
+ value: { Primitive: { U32: 10 } },
+ },
+ };
+ const clause = new ClauseBuilder().where(
+ "dojo_starter-Position",
+ "x",
+ "Gt",
+ 10
+ );
+
+ const builder = new ToriiQueryBuilder();
+ const query = builder.withClause(clause.build()).build();
+
+ expect(query.clause).toEqual(mockClause);
+ });
+ });
+
+ describe("order by handling", () => {
+ it("should add a single order by", () => {
+ const builder = new ToriiQueryBuilder();
+ const query = builder
+ .addOrderBy("dojo_starter", "x", "Asc")
+ .build();
+
+ expect(query.order_by).toEqual([
+ { model: "dojo_starter", member: "x", direction: "Asc" },
+ ]);
+ });
+
+ it("should set multiple order by clauses", () => {
+ const orderBy: OrderBy[] = [
+ { model: "dojo_starter", member: "x", direction: "Asc" },
+ { model: "dojo_starter", member: "y", direction: "Desc" },
+ ];
+
+ const builder = new ToriiQueryBuilder();
+ const query = builder.withOrderBy(orderBy).build();
+
+ expect(query.order_by).toEqual(orderBy);
+ });
+ });
+
+ describe("entity models handling", () => {
+ it("should add a single entity model", () => {
+ const builder = new ToriiQueryBuilder();
+ const query = builder.addEntityModel("dojo_starter").build();
+
+ expect(query.entity_models).toEqual(["dojo_starter"]);
+ });
+
+ it("should set multiple entity models", () => {
+ const builder = new ToriiQueryBuilder();
+ const query = builder.withEntityModels(["dojo_starter"]).build();
+
+ expect(query.entity_models).toEqual(["dojo_starter"]);
+ });
+ });
+
+ describe("timestamp and hashed keys handling", () => {
+ it("should set updated after timestamp", () => {
+ const timestamp = Date.now();
+ const builder = new ToriiQueryBuilder();
+ const query = builder.updatedAfter(timestamp).build();
+
+ expect(query.entity_updated_after).toBe(timestamp);
+ });
+
+ it("should handle hashed keys inclusion", () => {
+ const builder = new ToriiQueryBuilder();
+ const query = builder.includeHashedKeys().build();
+
+ expect(query.dont_include_hashed_keys).toBe(false);
+ });
+ });
+
+ describe("static methods", () => {
+ it("should create a paginated query", () => {
+ const query = ToriiQueryBuilder.withPagination(
+ 2,
+ 25
+ ).build();
+
+ expect(query.limit).toBe(25);
+ expect(query.offset).toBe(50);
+ });
+ });
+
+ describe("chaining", () => {
+ it("should support method chaining", () => {
+ const timestamp = Date.now();
+ const builder = new ToriiQueryBuilder();
+ const query = builder
+ .withLimit(10)
+ .withOffset(20)
+ .addEntityModel("dojo_starter-Position")
+ .addOrderBy("dojo_starter-Position", "x", "Asc")
+ .includeHashedKeys()
+ .updatedAfter(timestamp)
+ .build();
+
+ expect(query).toEqual({
+ limit: 10,
+ offset: 20,
+ clause: undefined,
+ dont_include_hashed_keys: false,
+ order_by: [
+ {
+ model: "dojo_starter-Position",
+ member: "x",
+ direction: "Asc",
+ },
+ ],
+ entity_models: ["dojo_starter-Position"],
+ entity_updated_after: timestamp,
+ });
+ });
+ });
+});
diff --git a/packages/sdk/src/clauseBuilder.ts b/packages/sdk/src/clauseBuilder.ts
new file mode 100644
index 00000000..e882da78
--- /dev/null
+++ b/packages/sdk/src/clauseBuilder.ts
@@ -0,0 +1,163 @@
+import {
+ Clause,
+ ComparisonOperator,
+ MemberValue,
+ PatternMatching,
+} from "@dojoengine/torii-client";
+
+import { convertToPrimitive } from "./convertToMemberValue";
+import { SchemaType } from "./types";
+
+// Helper types for nested model structure
+type ModelPath = K extends string
+ ? T[K] extends Record
+ ? {
+ [SubK in keyof T[K]]: `${K}-${SubK & string}`;
+ }[keyof T[K]]
+ : never
+ : never;
+
+type GetModelType<
+ T,
+ Path extends string,
+> = Path extends `${infer Namespace}-${infer Model}`
+ ? Namespace extends keyof T
+ ? Model extends keyof T[Namespace]
+ ? T[Namespace][Model]
+ : never
+ : never
+ : never;
+
+export class ClauseBuilder {
+ private clause: Clause;
+
+ constructor() {
+ // @ts-expect-error It's ok if it's not assignable here.
+ this.clause = {};
+ }
+
+ /**
+ * Create a clause based on entity keys
+ */
+ keys(
+ models: ModelPath[],
+ keys: (string | undefined)[],
+ pattern: PatternMatching = "VariableLen"
+ ): ClauseBuilder {
+ this.clause = {
+ Keys: {
+ keys: keys.length === 0 ? [undefined] : keys,
+ pattern_matching: pattern,
+ models,
+ },
+ };
+ return this;
+ }
+
+ /**
+ * Create a member clause for comparing values
+ */
+ where<
+ Path extends ModelPath,
+ M extends keyof GetModelType,
+ >(
+ model: Path,
+ member: M & string,
+ operator: ComparisonOperator,
+ value: GetModelType[M] | GetModelType[M][]
+ ): ClauseBuilder {
+ const memberValue: MemberValue = Array.isArray(value)
+ ? { List: value.map(convertToPrimitive) }
+ : convertToPrimitive(value);
+
+ this.clause = {
+ Member: {
+ model,
+ member,
+ operator,
+ value: memberValue,
+ },
+ };
+ return this;
+ }
+
+ /**
+ * Start a composite clause chain
+ */
+ compose(): CompositeBuilder {
+ return new CompositeBuilder();
+ }
+ /**
+ * Build the final clause
+ */
+ build(): Clause {
+ if (Object.keys(this.clause).length === 0) {
+ throw new Error("You cannot build an empty Clause");
+ }
+
+ return this.clause;
+ }
+}
+
+class CompositeBuilder>> {
+ private orClauses: Clause[] = [];
+ private andClauses: Clause[] = [];
+
+ or(clauses: ClauseBuilder[]): CompositeBuilder {
+ this.orClauses = clauses.map((c) => c.build());
+ return this;
+ }
+
+ and(clauses: ClauseBuilder[]): CompositeBuilder {
+ this.andClauses = clauses.map((c) => c.build());
+ return this;
+ }
+
+ build(): Clause {
+ if (this.orClauses.length === 0 && this.andClauses.length === 0) {
+ throw new Error(
+ "ComposeClause is empty. Add .or([clause]) or .and([clause])"
+ );
+ }
+
+ // If we only have OR clauses
+ if (this.orClauses && this.andClauses.length === 0) {
+ return {
+ Composite: {
+ operator: "Or",
+ clauses: this.orClauses,
+ },
+ };
+ }
+
+ // If we only have AND clauses
+ if (this.andClauses && this.orClauses.length === 0) {
+ return {
+ Composite: {
+ operator: "And",
+ clauses: this.andClauses,
+ },
+ };
+ }
+
+ // If we have both OR and AND clauses
+ if (this.andClauses && this.orClauses) {
+ return {
+ Composite: {
+ operator: "And",
+ clauses: [
+ ...this.andClauses,
+ {
+ Composite: {
+ operator: "Or",
+ clauses: this.orClauses,
+ },
+ },
+ ],
+ },
+ };
+ }
+
+ throw new Error("CompositeClause is not properly build");
+ }
+}
diff --git a/packages/sdk/src/convertQuerytoClause.ts b/packages/sdk/src/convertQuerytoClause.ts
index 649901c5..211e7492 100644
--- a/packages/sdk/src/convertQuerytoClause.ts
+++ b/packages/sdk/src/convertQuerytoClause.ts
@@ -3,6 +3,7 @@
import * as torii from "@dojoengine/torii-client";
import { QueryType, SchemaType, SubscriptionQueryType } from "./types";
import { convertQueryToEntityKeyClauses } from "./convertQueryToEntityKeyClauses";
+import { convertToPrimitive } from "./convertToMemberValue";
/**
* Converts a query object into a Torii clause.
@@ -275,34 +276,6 @@ function buildWhereClause(
return undefined;
}
-/**
- * Converts a value to a Torii primitive type.
- *
- * @param {any} value - The value to convert.
- * @returns {torii.MemberValue} - The converted primitive value.
- * @throws {Error} - If the value type is unsupported.
- */
-function convertToPrimitive(value: any): torii.MemberValue {
- if (typeof value === "number") {
- return { Primitive: { U32: value } };
- } else if (typeof value === "boolean") {
- return { Primitive: { Bool: value } };
- } else if (typeof value === "bigint") {
- return {
- Primitive: {
- Felt252: torii.cairoShortStringToFelt(value.toString()),
- },
- };
- } else if (typeof value === "string") {
- return { String: value };
- } else if (Array.isArray(value)) {
- return { List: value.map((item) => convertToPrimitive(item)) };
- }
-
- // Add more type conversions as needed
- throw new Error(`Unsupported primitive type: ${typeof value}`);
-}
-
/**
* Converts a query operator to a Torii comparison operator.
*
diff --git a/packages/sdk/src/convertToMemberValue.ts b/packages/sdk/src/convertToMemberValue.ts
new file mode 100644
index 00000000..6fefe342
--- /dev/null
+++ b/packages/sdk/src/convertToMemberValue.ts
@@ -0,0 +1,29 @@
+import * as torii from "@dojoengine/torii-client";
+
+/**
+ * Converts a value to a Torii primitive type.
+ *
+ * @param {any} value - The value to convert.
+ * @returns {torii.MemberValue} - The converted primitive value.
+ * @throws {Error} - If the value type is unsupported.
+ */
+export function convertToPrimitive(value: any): torii.MemberValue {
+ if (typeof value === "number") {
+ return { Primitive: { U32: value } };
+ } else if (typeof value === "boolean") {
+ return { Primitive: { Bool: value } };
+ } else if (typeof value === "bigint") {
+ return {
+ Primitive: {
+ Felt252: torii.cairoShortStringToFelt(value.toString()),
+ },
+ };
+ } else if (typeof value === "string") {
+ return { String: value };
+ } else if (Array.isArray(value)) {
+ return { List: value.map((item) => convertToPrimitive(item)) };
+ }
+
+ // Add more type conversions as needed
+ throw new Error(`Unsupported primitive type: ${typeof value}`);
+}
diff --git a/packages/sdk/src/experimental/__tests__/convertClauseToEntityKeysClause.test.ts b/packages/sdk/src/experimental/__tests__/convertClauseToEntityKeysClause.test.ts
new file mode 100644
index 00000000..353fc1f1
--- /dev/null
+++ b/packages/sdk/src/experimental/__tests__/convertClauseToEntityKeysClause.test.ts
@@ -0,0 +1,64 @@
+import { describe, expect, it } from "vitest";
+import { intoEntityKeysClause } from "../convertClauseToEntityKeysClause";
+import { ClauseBuilder } from "../../clauseBuilder";
+
+describe("convertClauseToEntityKeysClause", () => {
+ it("return empty array", () => {
+ expect(intoEntityKeysClause(undefined)).toEqual([]);
+ });
+
+ it("returns Keys if KeysClause", () => {
+ const clause = new ClauseBuilder().keys([], []).build();
+ expect(intoEntityKeysClause(clause)).toEqual([
+ {
+ Keys: {
+ keys: [undefined],
+ pattern_matching: "VariableLen",
+ models: [],
+ },
+ },
+ ]);
+ });
+
+ it("HashedKeys has priority over keys", () => {
+ const clause = new ClauseBuilder()
+ .keys(["dojo_starter-Position"], ["0x123"])
+ .build();
+ expect(
+ intoEntityKeysClause(clause, [
+ {
+ entityId: "0xentityHashedKey",
+ models: { dojo_starter: { Position: { some: "data" } } },
+ },
+ ])
+ ).toEqual([
+ {
+ HashedKeys: ["0xentityHashedKey"],
+ },
+ ]);
+ });
+
+ it("CompositeClauses cannot be converted to EntityKeysClause", () => {
+ const clause = new ClauseBuilder()
+ .compose()
+ .or([
+ new ClauseBuilder().keys(["dojo_starter-Position"], ["0x123"]),
+ new ClauseBuilder().keys(["dojo_starter-Position"], ["0x456"]),
+ ])
+ .build();
+
+ expect(() => intoEntityKeysClause(clause, [])).toThrowError(
+ /cannot use CompositeClause \| MemberClause/
+ );
+ });
+
+ it("MemberClause cannot be converted to EntityKeysClause", () => {
+ const clause = new ClauseBuilder()
+ .where("dojo_starter-Position", "x", "Gt", 5)
+ .build();
+
+ expect(() => intoEntityKeysClause(clause, [])).toThrowError(
+ /cannot use CompositeClause \| MemberClause/
+ );
+ });
+});
diff --git a/packages/sdk/src/experimental/convertClauseToEntityKeysClause.ts b/packages/sdk/src/experimental/convertClauseToEntityKeysClause.ts
new file mode 100644
index 00000000..43d54722
--- /dev/null
+++ b/packages/sdk/src/experimental/convertClauseToEntityKeysClause.ts
@@ -0,0 +1,34 @@
+import { EntityKeysClause, Clause } from "@dojoengine/torii-client";
+import { SchemaType, StandardizedQueryResult } from "../types";
+
+export function intoEntityKeysClause(
+ clause: Clause | undefined,
+ initialData: StandardizedQueryResult = []
+): EntityKeysClause[] {
+ // We want to send over placeholder but this case is very unlikely to happen
+ if (!clause) {
+ return [];
+ }
+ // if we have initial wih query.dont_include_hash_keys = false
+ // we can move forward to query those hashed keys directly
+ if (initialData && initialData.length > 0) {
+ return [{ HashedKeys: initialData.map((e) => e.entityId) }];
+ }
+
+ if (Object.hasOwn(clause, "Keys")) {
+ return [clause as unknown as EntityKeysClause];
+ }
+
+ // We want to avoid those kind of weird cases where we are guessing what data should be retrieved
+ if (
+ (Object.hasOwn(clause, "Member") ||
+ Object.hasOwn(clause, "Composite")) &&
+ initialData.length === 0
+ ) {
+ throw new Error(
+ "You cannot use CompositeClause | MemberClause to subscribe to entity updates, include initial data with hashed keys"
+ );
+ }
+
+ return [];
+}
diff --git a/packages/sdk/src/experimental/index.ts b/packages/sdk/src/experimental/index.ts
new file mode 100644
index 00000000..3082ee15
--- /dev/null
+++ b/packages/sdk/src/experimental/index.ts
@@ -0,0 +1,64 @@
+import * as torii from "@dojoengine/torii-client";
+import { SchemaType, SDKConfig } from "../types";
+import { parseEntities } from "../parseEntities";
+import { parseHistoricalEvents } from "../parseHistoricalEvents";
+import { intoEntityKeysClause } from "./convertClauseToEntityKeysClause";
+
+export async function init(options: SDKConfig) {
+ const client = await torii.createClient(options.client);
+
+ return {
+ getEntities: async (query: torii.Query) => {
+ return parseEntities(await client.getEntities(query));
+ },
+ getEvents: async (query: torii.Query, historical: boolean = false) => {
+ const events = await client.getEventMessages(query, historical);
+ return historical
+ ? parseHistoricalEvents(events)
+ : parseEntities(events);
+ },
+ subscribeEntities: async (query: torii.Query, callback: Function) => {
+ if (
+ query.dont_include_hashed_keys &&
+ query.clause &&
+ !Object.hasOwn(query.clause, "Keys")
+ ) {
+ throw new Error(
+ "For subscription, you need to include entity ids"
+ );
+ }
+ const entities = parseEntities(await client.getEntities(query));
+ return [
+ entities,
+ client.onEntityUpdated(
+ intoEntityKeysClause(query.clause, entities),
+ callback
+ ),
+ ];
+ },
+ subscribeEvents: async (
+ query: torii.Query,
+ callback: Function,
+ historical: boolean = false
+ ) => {
+ if (
+ query.dont_include_hashed_keys &&
+ query.clause &&
+ !Object.hasOwn(query.clause, "Keys")
+ ) {
+ throw new Error(
+ "For subscription, you need to include entity ids"
+ );
+ }
+ const events = parseEntities(await client.getEntities(query));
+ return [
+ events,
+ client.onEventMessageUpdated(
+ intoEntityKeysClause(query.clause, events),
+ historical,
+ callback
+ ),
+ ];
+ },
+ };
+}
diff --git a/packages/sdk/src/index.ts b/packages/sdk/src/index.ts
index ee09d120..804b7e53 100644
--- a/packages/sdk/src/index.ts
+++ b/packages/sdk/src/index.ts
@@ -9,6 +9,8 @@ import { SchemaType, SDK, SDKConfig, UnionOfModelData } from "./types";
export * from "./types";
export * from "./queryBuilder";
+export * from "./clauseBuilder";
+export * from "./toriiQueryBuilder";
/**
* Creates a new Torii client instance.
diff --git a/packages/sdk/src/parseEntities.ts b/packages/sdk/src/parseEntities.ts
index afeb78b5..9ae4e745 100644
--- a/packages/sdk/src/parseEntities.ts
+++ b/packages/sdk/src/parseEntities.ts
@@ -3,25 +3,15 @@ import * as torii from "@dojoengine/torii-client";
import { ParsedEntity, SchemaType, StandardizedQueryResult } from "./types";
import { CairoCustomEnum, CairoOption, CairoOptionVariant } from "starknet";
-/**
- * Parses a collection of entities into a standardized query result format.
- *
- * @template T - The schema type.
- * @param {torii.Entities} entities - The collection of entities to parse.
- * @param {{ logging?: boolean }} [options] - Optional settings for logging.
- * @returns {StandardizedQueryResult} - The parsed entities in a standardized query result format.
- *
- * @example
- * const parsedResult = parseEntities(entities, { logging: true });
- * console.log(parsedResult);
- */
export function parseEntities(
entities: torii.Entities,
options?: { logging?: boolean }
): StandardizedQueryResult {
- const result: StandardizedQueryResult = [];
+ // @ts-ignore
+ const result: StandardizedQueryResult = entities;
+ const entityIds = Object.keys(entities);
- for (const entityId in entities) {
+ entityIds.forEach((entityId) => {
const entityData = entities[entityId];
const parsedEntity: ParsedEntity = {
entityId,
@@ -50,12 +40,13 @@ export function parseEntities(
);
}
- result.push(parsedEntity);
+ result[entityId] = parsedEntity;
if (options?.logging) {
console.log(`Parsed entity:`, parsedEntity);
}
- }
+ return parsedEntity;
+ });
if (options?.logging) {
console.log("Parsed result:", result);
diff --git a/packages/sdk/src/toriiQueryBuilder.ts b/packages/sdk/src/toriiQueryBuilder.ts
new file mode 100644
index 00000000..45356edd
--- /dev/null
+++ b/packages/sdk/src/toriiQueryBuilder.ts
@@ -0,0 +1,122 @@
+import { Clause, OrderBy, Query } from "@dojoengine/torii-client";
+import { SchemaType } from "./types";
+
+const defaultToriiOptions = () => ({
+ limit: 100, // default limit
+ offset: 0,
+ clause: undefined,
+ dont_include_hashed_keys: true,
+ order_by: [],
+ entity_models: [],
+ entity_updated_after: 0,
+});
+
+type ToriiQueryBuilderOptions = Omit, "clause">;
+
+export class ToriiQueryBuilder {
+ private query: Query;
+
+ constructor(options?: ToriiQueryBuilderOptions) {
+ this.query = { ...defaultToriiOptions(), ...options };
+ }
+
+ /**
+ * Set the maximum number of results to return
+ */
+ withLimit(limit: number): ToriiQueryBuilder {
+ this.query.limit = limit;
+ return this;
+ }
+
+ /**
+ * Set the offset for pagination
+ */
+ withOffset(offset: number): ToriiQueryBuilder {
+ this.query.offset = offset;
+ return this;
+ }
+
+ /**
+ * Add the clause to filter results
+ */
+ withClause(clause: Clause): ToriiQueryBuilder {
+ this.query.clause = clause;
+ return this;
+ }
+
+ /**
+ * Set whether to include hashed keys in the response
+ * HashedKeys represent internal torii entity id.
+ */
+ includeHashedKeys(): ToriiQueryBuilder {
+ this.query.dont_include_hashed_keys = false;
+ return this;
+ }
+
+ /**
+ * Add a single order by clause
+ */
+ addOrderBy(
+ model: keyof T & string,
+ member: string,
+ direction: "Asc" | "Desc"
+ ): ToriiQueryBuilder {
+ this.query.order_by.push({
+ model,
+ member,
+ direction,
+ });
+ return this;
+ }
+
+ /**
+ * Add multiple order by clauses at once
+ */
+ withOrderBy(orderBy: OrderBy[]): ToriiQueryBuilder {
+ this.query.order_by = orderBy;
+ return this;
+ }
+
+ /**
+ * Add a single entity model to filter
+ */
+ addEntityModel(model: keyof T & string): ToriiQueryBuilder {
+ this.query.entity_models.push(model);
+ return this;
+ }
+
+ /**
+ * Set multiple entity models at once
+ */
+ withEntityModels(models: (keyof T & string)[]): ToriiQueryBuilder {
+ this.query.entity_models = models;
+ return this;
+ }
+
+ /**
+ * Set the minimum timestamp for entity updates
+ */
+ updatedAfter(timestamp: number): ToriiQueryBuilder {
+ this.query.entity_updated_after = timestamp;
+ return this;
+ }
+
+ /**
+ * Build the final query
+ */
+ build(): Query {
+ return this.query;
+ }
+
+ /**
+ * Create a new builder instance with pagination settings
+ */
+ static withPagination>>(
+ page: number,
+ pageSize: number
+ ): ToriiQueryBuilder {
+ return new ToriiQueryBuilder()
+ .withLimit(pageSize)
+ .withOffset(page * pageSize);
+ }
+}
diff --git a/packages/sdk/tsup.config.ts b/packages/sdk/tsup.config.ts
index 05edf348..fb6b3bb6 100644
--- a/packages/sdk/tsup.config.ts
+++ b/packages/sdk/tsup.config.ts
@@ -9,5 +9,6 @@ export default defineConfig({
"src/state": "src/state/index.ts",
"src/react": "src/react/index.ts",
"src/sql": "src/sql/index.ts",
+ "src/experimental": "src/experimental/index.ts",
},
});
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index c4da2105..65a89c9e 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -132,7 +132,7 @@ importers:
version: 3.0.11
bun-types:
specifier: latest
- version: 1.1.43
+ version: 1.1.45
graphql:
specifier: ^16.9.0
version: 16.10.0
@@ -186,6 +186,37 @@ importers:
specifier: ^3.3.0
version: 3.4.1(vite@5.4.11(@types/node@22.10.6)(terser@5.37.0))
+ examples/example-vite-experimental-sdk:
+ dependencies:
+ '@dojoengine/core':
+ specifier: workspace:*
+ version: link:../../packages/core
+ '@dojoengine/sdk':
+ specifier: workspace:*
+ version: link:../../packages/sdk
+ highlight.js:
+ specifier: ^11.11.1
+ version: 11.11.1
+ starknet:
+ specifier: 'catalog:'
+ version: 6.21.0(encoding@0.1.13)
+ devDependencies:
+ '@types/highlight.js':
+ specifier: ^10.1.0
+ version: 10.1.0
+ typescript:
+ specifier: ~5.6.3
+ version: 5.6.3
+ vite:
+ specifier: ^6.0.7
+ version: 6.0.7(@types/node@22.10.6)(jiti@2.4.2)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0)
+ vite-plugin-top-level-await:
+ specifier: ^1.4.4
+ version: 1.4.4(@swc/helpers@0.5.5)(rollup@4.30.1)(vite@6.0.7(@types/node@22.10.6)(jiti@2.4.2)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0))
+ vite-plugin-wasm:
+ specifier: ^3.4.1
+ version: 3.4.1(vite@6.0.7(@types/node@22.10.6)(jiti@2.4.2)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0))
+
examples/example-vite-kitchen-sink:
dependencies:
'@cartridge/connector':
@@ -843,7 +874,7 @@ importers:
version: 2.1.1
drizzle-orm:
specifier: ^0.38.3
- version: 0.38.3(@libsql/client@0.14.0)(@types/react@18.3.18)(bun-types@1.1.43)(react@18.3.1)
+ version: 0.38.3(@libsql/client@0.14.0)(@types/react@18.3.18)(bun-types@1.1.45)(react@18.3.1)
lucide-react:
specifier: ^0.469.0
version: 0.469.0(react@18.3.1)
@@ -6294,6 +6325,10 @@ packages:
'@types/hast@3.0.4':
resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==}
+ '@types/highlight.js@10.1.0':
+ resolution: {integrity: sha512-77hF2dGBsOgnvZll1vymYiNUtqJ8cJfXPD6GG/2M0aLRc29PkvB7Au6sIDjIEFcSICBhCh2+Pyq6WSRS7LUm6A==}
+ deprecated: This is a stub types definition. highlight.js provides its own type definitions, so you do not need this installed.
+
'@types/http-errors@2.0.4':
resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==}
@@ -7292,8 +7327,8 @@ packages:
buffer@6.0.3:
resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==}
- bun-types@1.1.43:
- resolution: {integrity: sha512-W0wCtVH+bwFp7p3Zgs03CqxEDmXxEvmmUM/FBKgWIv9T8gyeotvIjIbHzuDScc2DphhRNtr7hJLCR5PspYL5qw==}
+ bun-types@1.1.45:
+ resolution: {integrity: sha512-8NT3BYwkyO8nzTG1k+q86VEvucw7s5W1fjRIGs0Y6/XNbTZn+mHEU39LFnuDLj4UmGCMpWCQtXUhLd6cko49Ww==}
bundle-require@5.1.0:
resolution: {integrity: sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==}
@@ -9249,6 +9284,10 @@ packages:
header-case@2.0.4:
resolution: {integrity: sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q==}
+ highlight.js@11.11.1:
+ resolution: {integrity: sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==}
+ engines: {node: '>=12.0.0'}
+
hls.js@1.5.18:
resolution: {integrity: sha512-znxR+2jecWluu/0KOBqUcvVyAB5tLff10vjMGrpAlz1eFY+ZhF1bY3r82V+Bk7WJdk03iTjtja9KFFz5BrqjSA==}
@@ -13009,6 +13048,11 @@ packages:
eslint: ^8.57.0 || ^9.0.0
typescript: '>=4.8.4 <5.8.0'
+ typescript@5.6.3:
+ resolution: {integrity: sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==}
+ engines: {node: '>=14.17'}
+ hasBin: true
+
typescript@5.7.2:
resolution: {integrity: sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==}
engines: {node: '>=14.17'}
@@ -19741,6 +19785,10 @@ snapshots:
dependencies:
'@types/unist': 3.0.3
+ '@types/highlight.js@10.1.0':
+ dependencies:
+ highlight.js: 11.11.1
+
'@types/http-errors@2.0.4': {}
'@types/istanbul-lib-coverage@2.0.6': {}
@@ -21028,7 +21076,7 @@ snapshots:
base64-js: 1.5.1
ieee754: 1.2.1
- bun-types@1.1.43:
+ bun-types@1.1.45:
dependencies:
'@types/node': 20.12.14
'@types/ws': 8.5.13
@@ -21904,11 +21952,11 @@ snapshots:
transitivePeerDependencies:
- supports-color
- drizzle-orm@0.38.3(@libsql/client@0.14.0)(@types/react@18.3.18)(bun-types@1.1.43)(react@18.3.1):
+ drizzle-orm@0.38.3(@libsql/client@0.14.0)(@types/react@18.3.18)(bun-types@1.1.45)(react@18.3.1):
optionalDependencies:
'@libsql/client': 0.14.0
'@types/react': 18.3.18
- bun-types: 1.1.43
+ bun-types: 1.1.45
react: 18.3.1
dset@3.1.4: {}
@@ -23420,6 +23468,8 @@ snapshots:
capital-case: 1.0.4
tslib: 2.8.1
+ highlight.js@11.11.1: {}
+
hls.js@1.5.18: {}
hmac-drbg@1.0.1:
@@ -26808,8 +26858,8 @@ snapshots:
picocolors: 1.1.1
sade: 1.8.1
svelte: 4.2.19
- svelte-preprocess: 5.1.4(@babel/core@7.26.0)(postcss-load-config@4.0.2(postcss@8.5.1))(postcss@8.5.1)(svelte@4.2.19)(typescript@5.7.2)
- typescript: 5.7.2
+ svelte-preprocess: 5.1.4(@babel/core@7.26.0)(postcss-load-config@4.0.2(postcss@8.5.1))(postcss@8.5.1)(svelte@4.2.19)(typescript@5.7.3)
+ typescript: 5.7.3
transitivePeerDependencies:
- '@babel/core'
- coffeescript
@@ -26825,7 +26875,7 @@ snapshots:
dependencies:
svelte: 4.2.19
- svelte-preprocess@5.1.4(@babel/core@7.26.0)(postcss-load-config@4.0.2(postcss@8.5.1))(postcss@8.5.1)(svelte@4.2.19)(typescript@5.7.2):
+ svelte-preprocess@5.1.4(@babel/core@7.26.0)(postcss-load-config@4.0.2(postcss@8.5.1))(postcss@8.5.1)(svelte@4.2.19)(typescript@5.7.3):
dependencies:
'@types/pug': 2.0.10
detect-indent: 6.1.0
@@ -26837,7 +26887,7 @@ snapshots:
'@babel/core': 7.26.0
postcss: 8.5.1
postcss-load-config: 4.0.2(postcss@8.5.1)
- typescript: 5.7.2
+ typescript: 5.7.3
svelte@4.2.19:
dependencies:
@@ -27332,6 +27382,8 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ typescript@5.6.3: {}
+
typescript@5.7.2: {}
typescript@5.7.3: {}
diff --git a/scripts/build-examples.sh b/scripts/build-examples.sh
index 497a8ec2..db4b7019 100755
--- a/scripts/build-examples.sh
+++ b/scripts/build-examples.sh
@@ -15,6 +15,7 @@ examples=(
"examples/example-vue-app-recs"
"examples/example-vite-svelte-recs"
"examples/example-vite-react-sql"
+ "examples/example-vite-experimental-sdk"
)
# Iterate over each example directory and run the build command
@@ -24,4 +25,3 @@ for example in "${examples[@]}"; do
pnpm run build
cd ../../
done
-
|