diff --git a/.changeset/flat-swans-turn.md b/.changeset/flat-swans-turn.md
new file mode 100644
index 0000000000..b84f3e8e32
--- /dev/null
+++ b/.changeset/flat-swans-turn.md
@@ -0,0 +1,5 @@
+---
+"@latticexyz/store-sync": patch
+---
+
+Refactored `syncToZustand` to use new Store config under the hood, removing compatibility layers and improving performance.
diff --git a/.changeset/orange-beans-allow.md b/.changeset/orange-beans-allow.md
new file mode 100644
index 0000000000..a886edba0c
--- /dev/null
+++ b/.changeset/orange-beans-allow.md
@@ -0,0 +1,5 @@
+---
+"@latticexyz/dev-tools": patch
+---
+
+Updated Zustand components after changes to `syncToZustand`.
diff --git a/packages/config/src/common.ts b/packages/config/src/common.ts
index e08c34634a..f8807d3bde 100644
--- a/packages/config/src/common.ts
+++ b/packages/config/src/common.ts
@@ -31,3 +31,7 @@ export type Table = {
readonly schema: Schema;
readonly key: readonly string[];
};
+
+export type Tables = {
+ readonly [label: string]: Table;
+};
diff --git a/packages/config/src/exports/index.ts b/packages/config/src/exports/index.ts
index d6df7e297e..8805ded492 100644
--- a/packages/config/src/exports/index.ts
+++ b/packages/config/src/exports/index.ts
@@ -4,4 +4,4 @@
* Be sure we're ready to commit to these being supported and changes made backward compatible!
*/
-export type { AbiType, StaticAbiType, DynamicAbiType, Schema, Table } from "../common";
+export type { AbiType, StaticAbiType, DynamicAbiType, Schema, Table, Tables } from "../common";
diff --git a/packages/dev-tools/src/zustand/TableDataTable.tsx b/packages/dev-tools/src/zustand/TableDataTable.tsx
index 36d29922e3..064d60ea2f 100644
--- a/packages/dev-tools/src/zustand/TableDataTable.tsx
+++ b/packages/dev-tools/src/zustand/TableDataTable.tsx
@@ -1,6 +1,6 @@
-import { Table } from "@latticexyz/store/internal";
import { useRecords } from "./useRecords";
import { FieldValue } from "./FieldValue";
+import { Table } from "@latticexyz/store-sync/zustand";
// TODO: use react-table or similar for better perf with lots of logs
@@ -15,12 +15,7 @@ export function TableDataTable({ table }: Props) {
- {Object.keys(table.keySchema).map((name) => (
-
- {name}
- |
- ))}
- {Object.keys(table.valueSchema).map((name) => (
+ {Object.keys(table.schema).map((name) => (
{name}
|
@@ -31,14 +26,9 @@ export function TableDataTable({ table }: Props) {
{records.map((record) => {
return (
- {Object.keys(table.keySchema).map((name) => (
-
-
- |
- ))}
- {Object.keys(table.valueSchema).map((name) => (
+ {Object.keys(table.schema).map((name) => (
-
+
|
))}
diff --git a/packages/dev-tools/src/zustand/useRecords.ts b/packages/dev-tools/src/zustand/useRecords.ts
index 27d4b7f9fc..3d2941ed4e 100644
--- a/packages/dev-tools/src/zustand/useRecords.ts
+++ b/packages/dev-tools/src/zustand/useRecords.ts
@@ -1,7 +1,6 @@
-import { Table } from "@latticexyz/store/internal";
import { useDevToolsContext } from "../DevToolsContext";
import { useEffect, useState } from "react";
-import { TableRecord } from "@latticexyz/store-sync/zustand";
+import { Table, TableRecord } from "@latticexyz/store-sync/zustand";
export function useRecords(table: table): TableRecord[] {
const { useStore } = useDevToolsContext();
diff --git a/packages/dev-tools/src/zustand/useTables.ts b/packages/dev-tools/src/zustand/useTables.ts
index d9fbd55ea3..0732383788 100644
--- a/packages/dev-tools/src/zustand/useTables.ts
+++ b/packages/dev-tools/src/zustand/useTables.ts
@@ -1,6 +1,6 @@
-import { Table } from "@latticexyz/store/internal";
import { useDevToolsContext } from "../DevToolsContext";
import { useEffect, useState } from "react";
+import { Table } from "@latticexyz/store-sync/zustand";
export function useTables(): Table[] {
const { useStore } = useDevToolsContext();
diff --git a/packages/protocol-parser/src/common.ts b/packages/protocol-parser/src/common.ts
index 8922bbac9c..904a646ef1 100644
--- a/packages/protocol-parser/src/common.ts
+++ b/packages/protocol-parser/src/common.ts
@@ -30,6 +30,7 @@ export type KeySchema = Rec
string,
userTypes extends UserTypes ? StaticAbiType | keyof userTypes : StaticAbiType
>;
+
export type ValueSchema = Record<
string,
userTypes extends UserTypes ? SchemaAbiType | keyof userTypes : SchemaAbiType
diff --git a/packages/store-sync/src/zustand/common.ts b/packages/store-sync/src/zustand/common.ts
index d86b1ad654..5f5a64edc4 100644
--- a/packages/store-sync/src/zustand/common.ts
+++ b/packages/store-sync/src/zustand/common.ts
@@ -1,4 +1,5 @@
-import { Table, SchemaToPrimitives } from "@latticexyz/store/internal";
+import { Table } from "@latticexyz/config";
+import { SchemaToPrimitives, getKeySchema, getSchemaTypes, getValueSchema } from "@latticexyz/protocol-parser/internal";
import { Hex } from "viem";
export type RawRecord = {
@@ -16,6 +17,9 @@ export type TableRecord = {
readonly id: string;
readonly table: table;
readonly keyTuple: readonly Hex[];
- readonly key: SchemaToPrimitives;
- readonly value: SchemaToPrimitives;
+ readonly key: SchemaToPrimitives>>;
+ readonly value: SchemaToPrimitives>>;
+ readonly fields: SchemaToPrimitives>;
};
+
+export type { Table };
diff --git a/packages/store-sync/src/zustand/createStorageAdapter.test.ts b/packages/store-sync/src/zustand/createStorageAdapter.test.ts
index c26b7df3f1..63cf544712 100644
--- a/packages/store-sync/src/zustand/createStorageAdapter.test.ts
+++ b/packages/store-sync/src/zustand/createStorageAdapter.test.ts
@@ -2,7 +2,7 @@ import { beforeAll, describe, expect, it } from "vitest";
import { storeEventsAbi } from "@latticexyz/store";
import { createStorageAdapter } from "./createStorageAdapter";
import { createStore } from "./createStore";
-import { config, deployMockGame } from "../../test/mockGame";
+import { configV2 as config, deployMockGame } from "../../test/mockGame";
import { fetchAndStoreLogs } from "../fetchAndStoreLogs";
import { testClient } from "../../test/common";
import { getBlockNumber } from "viem/actions";
@@ -34,6 +34,11 @@ describe("createStorageAdapter", async () => {
expect(useStore.getState().getRecords(config.tables.Position)).toMatchInlineSnapshot(`
{
"0x74620000000000000000000000000000506f736974696f6e0000000000000000:0x000000000000000000000000078cf0753dd50f7c56f20b3ae02719ea199be2eb": {
+ "fields": {
+ "player": "0x078cf0753dd50f7C56F20B3Ae02719EA199BE2eb",
+ "x": 3,
+ "y": 5,
+ },
"id": "0x74620000000000000000000000000000506f736974696f6e0000000000000000:0x000000000000000000000000078cf0753dd50f7c56f20b3ae02719ea199be2eb",
"key": {
"player": "0x078cf0753dd50f7C56F20B3Ae02719EA199BE2eb",
@@ -42,16 +47,25 @@ describe("createStorageAdapter", async () => {
"0x000000000000000000000000078cf0753dd50f7c56f20b3ae02719ea199be2eb",
],
"table": {
- "keySchema": {
+ "codegen": {
+ "dataStruct": true,
+ "outputDirectory": "tables",
+ "storeArgument": false,
+ "tableIdArgument": false,
+ },
+ "deploy": {
+ "disabled": false,
+ },
+ "key": [
+ "player",
+ ],
+ "name": "Position",
+ "namespace": "",
+ "schema": {
"player": {
"internalType": "address",
"type": "address",
},
- },
- "name": "Position",
- "namespace": "",
- "tableId": "0x74620000000000000000000000000000506f736974696f6e0000000000000000",
- "valueSchema": {
"x": {
"internalType": "int32",
"type": "int32",
@@ -61,6 +75,8 @@ describe("createStorageAdapter", async () => {
"type": "int32",
},
},
+ "tableId": "0x74620000000000000000000000000000506f736974696f6e0000000000000000",
+ "type": "table",
},
"value": {
"x": 3,
@@ -68,6 +84,11 @@ describe("createStorageAdapter", async () => {
},
},
"0x74620000000000000000000000000000506f736974696f6e0000000000000000:0x0000000000000000000000001d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e": {
+ "fields": {
+ "player": "0x1D96F2f6BeF1202E4Ce1Ff6Dad0c2CB002861d3e",
+ "x": 1,
+ "y": -1,
+ },
"id": "0x74620000000000000000000000000000506f736974696f6e0000000000000000:0x0000000000000000000000001d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e",
"key": {
"player": "0x1D96F2f6BeF1202E4Ce1Ff6Dad0c2CB002861d3e",
@@ -76,16 +97,25 @@ describe("createStorageAdapter", async () => {
"0x0000000000000000000000001d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e",
],
"table": {
- "keySchema": {
+ "codegen": {
+ "dataStruct": true,
+ "outputDirectory": "tables",
+ "storeArgument": false,
+ "tableIdArgument": false,
+ },
+ "deploy": {
+ "disabled": false,
+ },
+ "key": [
+ "player",
+ ],
+ "name": "Position",
+ "namespace": "",
+ "schema": {
"player": {
"internalType": "address",
"type": "address",
},
- },
- "name": "Position",
- "namespace": "",
- "tableId": "0x74620000000000000000000000000000506f736974696f6e0000000000000000",
- "valueSchema": {
"x": {
"internalType": "int32",
"type": "int32",
@@ -95,6 +125,8 @@ describe("createStorageAdapter", async () => {
"type": "int32",
},
},
+ "tableId": "0x74620000000000000000000000000000506f736974696f6e0000000000000000",
+ "type": "table",
},
"value": {
"x": 1,
@@ -102,6 +134,11 @@ describe("createStorageAdapter", async () => {
},
},
"0x74620000000000000000000000000000506f736974696f6e0000000000000000:0x000000000000000000000000328809bc894f92807417d2dad6b7c998c1afdac6": {
+ "fields": {
+ "player": "0x328809Bc894f92807417D2dAD6b7C998c1aFdac6",
+ "x": 3,
+ "y": 5,
+ },
"id": "0x74620000000000000000000000000000506f736974696f6e0000000000000000:0x000000000000000000000000328809bc894f92807417d2dad6b7c998c1afdac6",
"key": {
"player": "0x328809Bc894f92807417D2dAD6b7C998c1aFdac6",
@@ -110,16 +147,25 @@ describe("createStorageAdapter", async () => {
"0x000000000000000000000000328809bc894f92807417d2dad6b7c998c1afdac6",
],
"table": {
- "keySchema": {
+ "codegen": {
+ "dataStruct": true,
+ "outputDirectory": "tables",
+ "storeArgument": false,
+ "tableIdArgument": false,
+ },
+ "deploy": {
+ "disabled": false,
+ },
+ "key": [
+ "player",
+ ],
+ "name": "Position",
+ "namespace": "",
+ "schema": {
"player": {
"internalType": "address",
"type": "address",
},
- },
- "name": "Position",
- "namespace": "",
- "tableId": "0x74620000000000000000000000000000506f736974696f6e0000000000000000",
- "valueSchema": {
"x": {
"internalType": "int32",
"type": "int32",
@@ -129,6 +175,8 @@ describe("createStorageAdapter", async () => {
"type": "int32",
},
},
+ "tableId": "0x74620000000000000000000000000000506f736974696f6e0000000000000000",
+ "type": "table",
},
"value": {
"x": 3,
@@ -136,6 +184,11 @@ describe("createStorageAdapter", async () => {
},
},
"0x74620000000000000000000000000000506f736974696f6e0000000000000000:0x000000000000000000000000dba86119a787422c593cef119e40887f396024e2": {
+ "fields": {
+ "player": "0xdBa86119a787422C593ceF119E40887f396024E2",
+ "x": 100,
+ "y": 100,
+ },
"id": "0x74620000000000000000000000000000506f736974696f6e0000000000000000:0x000000000000000000000000dba86119a787422c593cef119e40887f396024e2",
"key": {
"player": "0xdBa86119a787422C593ceF119E40887f396024E2",
@@ -144,16 +197,25 @@ describe("createStorageAdapter", async () => {
"0x000000000000000000000000dba86119a787422c593cef119e40887f396024e2",
],
"table": {
- "keySchema": {
+ "codegen": {
+ "dataStruct": true,
+ "outputDirectory": "tables",
+ "storeArgument": false,
+ "tableIdArgument": false,
+ },
+ "deploy": {
+ "disabled": false,
+ },
+ "key": [
+ "player",
+ ],
+ "name": "Position",
+ "namespace": "",
+ "schema": {
"player": {
"internalType": "address",
"type": "address",
},
- },
- "name": "Position",
- "namespace": "",
- "tableId": "0x74620000000000000000000000000000506f736974696f6e0000000000000000",
- "valueSchema": {
"x": {
"internalType": "int32",
"type": "int32",
@@ -163,6 +225,8 @@ describe("createStorageAdapter", async () => {
"type": "int32",
},
},
+ "tableId": "0x74620000000000000000000000000000506f736974696f6e0000000000000000",
+ "type": "table",
},
"value": {
"x": 100,
diff --git a/packages/store-sync/src/zustand/createStorageAdapter.ts b/packages/store-sync/src/zustand/createStorageAdapter.ts
index aefa93bae7..f55c1424c0 100644
--- a/packages/store-sync/src/zustand/createStorageAdapter.ts
+++ b/packages/store-sync/src/zustand/createStorageAdapter.ts
@@ -1,4 +1,3 @@
-import { Tables } from "@latticexyz/store/internal";
import { StorageAdapter } from "../common";
import { RawRecord, TableRecord } from "./common";
import { ZustandStore } from "./createStore";
@@ -6,9 +5,16 @@ import { hexToResource, resourceToLabel, spliceHex } from "@latticexyz/common";
import { debug } from "./debug";
import { getId } from "./getId";
import { size } from "viem";
-import { decodeKey, decodeValueArgs } from "@latticexyz/protocol-parser/internal";
-import { flattenSchema } from "../flattenSchema";
+import {
+ KeySchema,
+ decodeKey,
+ decodeValueArgs,
+ getKeySchema,
+ getSchemaTypes,
+ getValueSchema,
+} from "@latticexyz/protocol-parser/internal";
import { isDefined } from "@latticexyz/common/utils";
+import { Tables } from "@latticexyz/config";
export type CreateStorageAdapterOptions = {
store: ZustandStore;
@@ -131,14 +137,21 @@ export function createStorageAdapter({
return;
}
// TODO: warn if no table
+
+ // TODO: update decodeKey to use more recent types
+ const key = decodeKey(getSchemaTypes(getKeySchema(table)) as KeySchema, rawRecord.keyTuple);
+ // TODO: update decodeValueArgs to use more recent types
+ const value = decodeValueArgs(getSchemaTypes(getValueSchema(table)), rawRecord);
+
return [
id,
{
id,
table: store.getState().tables[rawRecord.tableId],
keyTuple: rawRecord.keyTuple,
- key: decodeKey(flattenSchema(table.keySchema), rawRecord.keyTuple),
- value: decodeValueArgs(flattenSchema(table.valueSchema), rawRecord),
+ key,
+ value,
+ fields: { ...key, ...value },
} satisfies TableRecord,
];
})
diff --git a/packages/store-sync/src/zustand/createStore.ts b/packages/store-sync/src/zustand/createStore.ts
index 67810bb8f3..e515b4faab 100644
--- a/packages/store-sync/src/zustand/createStore.ts
+++ b/packages/store-sync/src/zustand/createStore.ts
@@ -1,11 +1,10 @@
-import { SchemaToPrimitives, Table, Tables } from "@latticexyz/store/internal";
import { StoreApi, UseBoundStore, create } from "zustand";
import { RawRecord, TableRecord } from "./common";
import { Hex } from "viem";
-import { encodeKey } from "@latticexyz/protocol-parser/internal";
-import { flattenSchema } from "../flattenSchema";
+import { encodeKey, getKeySchema, getSchemaTypes } from "@latticexyz/protocol-parser/internal";
import { getId } from "./getId";
import { SyncStep } from "../SyncStep";
+import { Table, Tables } from "@latticexyz/config";
type TableRecords = {
readonly [id: string]: TableRecord;
@@ -36,11 +35,11 @@ export type ZustandState = {
readonly getRecords: (table: table) => TableRecords;
readonly getRecord: (
table: table,
- key: SchemaToPrimitives,
+ key: TableRecord["key"],
) => TableRecord | undefined;
readonly getValue: (
table: table,
- key: SchemaToPrimitives,
+ key: TableRecord["key"],
) => TableRecord["value"] | undefined;
};
@@ -69,17 +68,15 @@ export function createStore(opts: CreateStoreOptions record.table.tableId === table.tableId),
) as unknown as TableRecords;
},
- getRecord: (
- table: table,
- key: SchemaToPrimitives,
- ): TableRecord | undefined => {
- const keyTuple = encodeKey(flattenSchema(table.keySchema), key);
+ getRecord: (table: table, key: TableRecord["key"]): TableRecord | undefined => {
+ // TODO: update encodeKey to use more recent types
+ const keyTuple = encodeKey(getSchemaTypes(getKeySchema(table)) as never, key as never);
const id = getId({ tableId: table.tableId, keyTuple });
return get().records[id] as unknown as TableRecord | undefined;
},
getValue: (
table: table,
- key: SchemaToPrimitives,
+ key: TableRecord["key"],
): TableRecord["value"] | undefined => {
return get().getRecord(table, key)?.value;
},
diff --git a/packages/store-sync/src/zustand/getAllTables.ts b/packages/store-sync/src/zustand/getAllTables.ts
new file mode 100644
index 0000000000..d050127f3b
--- /dev/null
+++ b/packages/store-sync/src/zustand/getAllTables.ts
@@ -0,0 +1,28 @@
+import { Store as StoreConfig } from "@latticexyz/store";
+import { Tables } from "@latticexyz/config";
+import { tablesByLabel } from "./tablesByLabel";
+import { mergeRight } from "./mergeRight";
+import storeConfig from "@latticexyz/store/mud.config";
+import worldConfig from "@latticexyz/world/mud.config";
+
+const storeTables = storeConfig.tables;
+type storeTables = typeof storeTables;
+
+const worldTables = worldConfig.tables;
+type worldTables = typeof worldTables;
+
+export type getAllTables = tablesByLabel<
+ mergeRight>>
+>;
+
+export function getAllTables(
+ config: config,
+ extraTables: extraTables,
+): getAllTables {
+ return tablesByLabel({
+ ...config.tables,
+ ...extraTables,
+ ...storeTables,
+ ...worldTables,
+ }) as never;
+}
diff --git a/packages/store-sync/src/zustand/mergeRight.ts b/packages/store-sync/src/zustand/mergeRight.ts
new file mode 100644
index 0000000000..b45ef5e54a
--- /dev/null
+++ b/packages/store-sync/src/zustand/mergeRight.ts
@@ -0,0 +1,7 @@
+export type mergeRight = {
+ readonly [key in keyof left | keyof right]: key extends keyof right
+ ? right[key]
+ : key extends keyof left
+ ? left[key]
+ : never;
+};
diff --git a/packages/store-sync/src/zustand/syncToZustand.ts b/packages/store-sync/src/zustand/syncToZustand.ts
index 38c4caaa19..61e75fd2ed 100644
--- a/packages/store-sync/src/zustand/syncToZustand.ts
+++ b/packages/store-sync/src/zustand/syncToZustand.ts
@@ -1,5 +1,4 @@
-import { ResolvedStoreConfig, Tables, resolveConfig } from "@latticexyz/store/internal";
-import { SyncOptions, SyncResult, storeTables, worldTables } from "../common";
+import { SyncOptions, SyncResult } from "../common";
import { createStoreSync } from "../createStoreSync";
import { ZustandStore } from "./createStore";
import { createStore } from "./createStore";
@@ -7,47 +6,35 @@ import { createStorageAdapter } from "./createStorageAdapter";
import { Address } from "viem";
import { SyncStep } from "../SyncStep";
import { Store as StoreConfig } from "@latticexyz/store";
-import { storeToV1 } from "@latticexyz/store/config/v2";
+import { Tables } from "@latticexyz/config";
+import { getAllTables } from "./getAllTables";
-type AllTables = ResolvedStoreConfig<
- storeToV1
->["tables"] &
- (extraTables extends Tables ? extraTables : Record) &
- typeof storeTables &
- typeof worldTables;
-
-type SyncToZustandOptions = SyncOptions & {
+type SyncToZustandOptions = Omit<
+ SyncOptions,
+ "address" | "config"
+> & {
// require address for now to keep the data model + retrieval simpler
address: Address;
config: config;
tables?: extraTables;
- store?: ZustandStore>;
+ store?: ZustandStore>;
startSync?: boolean;
};
-type SyncToZustandResult = SyncResult & {
- tables: AllTables;
- useStore: ZustandStore>;
+type SyncToZustandResult = SyncResult & {
+ tables: getAllTables;
+ useStore: ZustandStore>;
stopSync: () => void;
};
-export async function syncToZustand({
+export async function syncToZustand({
config,
- tables: extraTables,
+ tables: extraTables = {} as extraTables,
store,
startSync = true,
...syncOptions
}: SyncToZustandOptions): Promise> {
- // TODO: migrate this once we redo config to return fully resolved tables (https://github.com/latticexyz/mud/issues/1668)
- // TODO: move store/world tables into `resolveConfig`
- const resolvedConfig = resolveConfig(storeToV1(config as StoreConfig));
- const tables = {
- ...resolvedConfig.tables,
- ...extraTables,
- ...storeTables,
- ...worldTables,
- } as unknown as AllTables;
-
+ const tables = getAllTables(config, extraTables);
const useStore = store ?? createStore({ tables });
const storageAdapter = createStorageAdapter({ store: useStore });
diff --git a/packages/store-sync/src/zustand/tablesByLabel.ts b/packages/store-sync/src/zustand/tablesByLabel.ts
new file mode 100644
index 0000000000..2f18dc9376
--- /dev/null
+++ b/packages/store-sync/src/zustand/tablesByLabel.ts
@@ -0,0 +1,15 @@
+import { Tables } from "@latticexyz/config";
+
+export type tablesByLabel = {
+ // TODO: switch from name to label once its available
+ readonly [key in string & keyof tables as tables[key]["name"]]: tables[key];
+};
+
+export function tablesByLabel(tables: tables): tablesByLabel {
+ return Object.fromEntries(
+ Object.entries(tables).map(([, table]) =>
+ // TODO: switch from name to label once its available
+ [table.name, table],
+ ),
+ ) as never;
+}