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

refactor(store-sync): use config namespaces for tables #2963

Merged
merged 27 commits into from
Jul 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .changeset/khaki-wolves-perform.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
"@latticexyz/store-sync": patch
---

Refactored `syncToRecs` and `syncToZustand` to use tables from config namespaces output. This is a precursor for supporting multiple namespaces.

Note for library authors: If you were using `createStorageAdapter` from `@latticexyz/store-sync/recs`, this helper no longer appends MUD's built-in tables from Store and World packages. This behavior was moved into `syncToRecs` for consistency with `syncToZustand` and makes `createStorageAdapter` less opinionated.

You can achieve the previous behavior with:

```diff
import { createStorageAdapter } from "@latticexyz/store-sync/recs";
+import { mudTables } from "@latticexyz/store-sync";

createStorageAdapter({
- tables,
+ tables: { ...tables, ...mudTables },
...
});
```
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
"package.json": "pnpm sort-package-json"
},
"devDependencies": {
"@ark/attest": "0.9.2",
"@arktype/attest": "0.7.5",
"@changesets/cli": "^2.26.1",
"@types/node": "^18.15.11",
"@typescript-eslint/eslint-plugin": "7.1.1",
Expand Down
1 change: 1 addition & 0 deletions packages/store-sync/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
"test:ci": "vitest --run"
},
"dependencies": {
"@arktype/util": "0.1.0",
"@latticexyz/block-logs-stream": "workspace:*",
"@latticexyz/common": "workspace:*",
"@latticexyz/config": "workspace:*",
Expand Down
12 changes: 12 additions & 0 deletions packages/store-sync/src/common.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { describe, it } from "vitest";
import { attest } from "@arktype/attest";
import { isDisjoint } from "@arktype/util";
import storeConfig from "@latticexyz/store/mud.config";
import worldConfig from "@latticexyz/world/mud.config";
import { configToTables } from "./configToTables";

describe("mudTables", () => {
it("has no overlapping table labels", () => {
attest<true, isDisjoint<keyof configToTables<typeof storeConfig>, keyof configToTables<typeof worldConfig>>>();
});
});
21 changes: 10 additions & 11 deletions packages/store-sync/src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,15 @@ import storeConfig from "@latticexyz/store/mud.config";
import worldConfig from "@latticexyz/world/mud.config";
import { Store as StoreConfig } from "@latticexyz/store";
import { Table as ConfigTable, Schema } from "@latticexyz/config";
import { configToTables } from "./configToTables";

export const storeTables = storeConfig.tables;
export type storeTables = typeof storeTables;
export const mudTables = {
...configToTables(storeConfig),
...configToTables(worldConfig),
} as const;
export type mudTables = typeof mudTables;

export const worldTables = worldConfig.tables;
export type worldTables = typeof worldTables;

export const internalTableIds = [...Object.values(storeTables), ...Object.values(worldTables)].map(
(table) => table.tableId,
);
export const internalTableIds = Object.values(mudTables).map((table) => table.tableId);

export type ChainId = number;
export type WorldId = `${ChainId}:${Address}`;
Expand Down Expand Up @@ -137,7 +136,7 @@ export type StorageAdapterBlock = { blockNumber: BlockLogs["blockNumber"]; logs:
export type StorageAdapter = (block: StorageAdapterBlock) => Promise<void>;

export const schemasTable = {
...storeTables.store__Tables,
keySchema: getSchemaTypes(getKeySchema(storeTables.store__Tables)),
valueSchema: getSchemaTypes(getValueSchema(storeTables.store__Tables)),
...mudTables.Tables,
keySchema: getSchemaTypes(getKeySchema(mudTables.Tables)),
valueSchema: getSchemaTypes(getValueSchema(mudTables.Tables)),
};
44 changes: 44 additions & 0 deletions packages/store-sync/src/configToTables.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { describe, it } from "vitest";
import { configToTables } from "./configToTables";
import { defineWorld } from "@latticexyz/world";
import { resourceToHex } from "@latticexyz/common";
import { attest } from "@arktype/attest";

describe("configToTables", () => {
it("flattens tables from single namespace", async () => {
const config = defineWorld({
namespace: "app",
tables: {
ExceedsResourceNameSizeLimit: "bytes32",
Table2: "address",
},
});

const tables = configToTables(config);

attest<"ExceedsResourceNameSizeLimit" | "Table2", keyof typeof tables>();

attest(tables.ExceedsResourceNameSizeLimit.tableId).equals(
resourceToHex({
type: "table",
namespace: "app",
name: "ExceedsResourceN",
}),
);
attest(tables.ExceedsResourceNameSizeLimit.label).equals("ExceedsResourceNameSizeLimit");
attest(tables.ExceedsResourceNameSizeLimit.name).equals("ExceedsResourceN");

attest(tables.Table2.tableId).equals(
resourceToHex({
type: "table",
namespace: "app",
name: "Table2",
}),
);
attest(tables.Table2.label).equals("Table2");
attest(tables.Table2.name).equals("Table2");
});

// TODO: add test with multiple namespaces
// TODO: add test where the label is the same for two tables in different namespaces to make sure TS + runtime agree on which takes precedence
});
25 changes: 25 additions & 0 deletions packages/store-sync/src/configToTables.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { show } from "@arktype/util";
import { Store } from "@latticexyz/store";

type flattenedTableKeys<config extends Store> = config extends { readonly namespaces: infer namespaces }
? {
[namespaceLabel in keyof namespaces]: namespaces[namespaceLabel] extends { readonly tables: infer tables }
? `${namespaceLabel & string}__${keyof tables & string}`
: never;
}[keyof namespaces]
: never;

// TODO: figure out how TS handles overlapping table labels so we can make runtime match

export type configToTables<config extends Store> = {
readonly [key in flattenedTableKeys<config> as key extends `${string}__${infer tableLabel}`
? tableLabel
: never]: key extends `${infer namespaceLabel}__${infer tableLabel}`
? config["namespaces"][namespaceLabel]["tables"][tableLabel]
: never;
};

export function configToTables<config extends Store>(config: config): show<configToTables<config>> {
const tables = Object.values(config.namespaces).flatMap((namespace) => Object.values(namespace.tables));
return Object.fromEntries(tables.map((table) => [table.label, table])) as never;
}
1 change: 1 addition & 0 deletions packages/store-sync/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from "./common";
export * from "./configToTables";
export * from "./createStoreSync";
export * from "./SyncStep";
export * from "./isTableRegistrationLog";
Expand Down
4 changes: 2 additions & 2 deletions packages/store-sync/src/postgres-decoded/syncToPostgres.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { SyncOptions, SyncResult } from "../common";
import { createStorageAdapter } from "./createStorageAdapter";
import { createStoreSync } from "../createStoreSync";

type SyncToPostgresOptions<config extends StoreConfig = StoreConfig> = SyncOptions<config> & {
export type SyncToPostgresOptions<config extends StoreConfig = StoreConfig> = SyncOptions<config> & {
/**
* [Postgres database object from Drizzle][0].
*
Expand All @@ -15,7 +15,7 @@ type SyncToPostgresOptions<config extends StoreConfig = StoreConfig> = SyncOptio
startSync?: boolean;
};

type SyncToPostgresResult = SyncResult & {
export type SyncToPostgresResult = SyncResult & {
stopSync: () => void;
};

Expand Down
4 changes: 2 additions & 2 deletions packages/store-sync/src/postgres/syncToPostgres.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { SyncOptions, SyncResult } from "../common";
import { createStorageAdapter } from "./createStorageAdapter";
import { createStoreSync } from "../createStoreSync";

type SyncToPostgresOptions<config extends StoreConfig = StoreConfig> = SyncOptions<config> & {
export type SyncToPostgresOptions<config extends StoreConfig = StoreConfig> = SyncOptions<config> & {
/**
* [Postgres database object from Drizzle][0].
*
Expand All @@ -15,7 +15,7 @@ type SyncToPostgresOptions<config extends StoreConfig = StoreConfig> = SyncOptio
startSync?: boolean;
};

type SyncToPostgresResult = SyncResult & {
export type SyncToPostgresResult = SyncResult & {
stopSync: () => void;
};

Expand Down
21 changes: 6 additions & 15 deletions packages/store-sync/src/recs/createStorageAdapter.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Table } from "@latticexyz/config";
import { Tables } from "@latticexyz/config";
import { debug } from "./debug";
import { World as RecsWorld, getComponentValue, hasComponent, removeComponent, setComponent } from "@latticexyz/recs";
import { defineInternalComponents } from "./defineInternalComponents";
Expand All @@ -11,28 +11,21 @@ import { logToTable } from "../logToTable";
import { hexKeyTupleToEntity } from "./hexKeyTupleToEntity";
import { StorageAdapter, StorageAdapterBlock } from "../common";
import { singletonEntity } from "./singletonEntity";
import storeConfig from "@latticexyz/store/mud.config";
import worldConfig from "@latticexyz/world/mud.config";
import { tablesToComponents } from "./tablesToComponents";
import { merge } from "@arktype/util";

const storeTables = storeConfig.tables;
const worldTables = worldConfig.tables;

export type CreateStorageAdapterOptions<tables extends Record<string, Table>> = {
export type CreateStorageAdapterOptions<tables extends Tables> = {
world: RecsWorld;
tables: tables;
shouldSkipUpdateStream?: () => boolean;
};

export type CreateStorageAdapterResult<tables extends Record<string, Table>> = {
export type CreateStorageAdapterResult<tables extends Tables> = {
storageAdapter: StorageAdapter;
components: tablesToComponents<tables> &
tablesToComponents<typeof storeTables> &
tablesToComponents<typeof worldTables> &
ReturnType<typeof defineInternalComponents>;
components: merge<tablesToComponents<tables>, ReturnType<typeof defineInternalComponents>>;
};

export function createStorageAdapter<tables extends Record<string, Table>>({
export function createStorageAdapter<tables extends Tables>({
world,
tables,
shouldSkipUpdateStream,
Expand All @@ -41,8 +34,6 @@ export function createStorageAdapter<tables extends Record<string, Table>>({

const components = {
...tablesToComponents(world, tables),
...tablesToComponents(world, storeTables),
...tablesToComponents(world, worldTables),
...defineInternalComponents(world),
};

Expand Down
23 changes: 13 additions & 10 deletions packages/store-sync/src/recs/syncToRecs.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,38 @@
import { Table } from "@latticexyz/config";
import { Tables } from "@latticexyz/config";
import { Store as StoreConfig } from "@latticexyz/store";
import { Component as RecsComponent, World as RecsWorld, getComponentValue, setComponent } from "@latticexyz/recs";
import { SyncOptions, SyncResult } from "../common";
import { SyncOptions, SyncResult, mudTables } from "../common";
import { CreateStorageAdapterResult, createStorageAdapter } from "./createStorageAdapter";
import { createStoreSync } from "../createStoreSync";
import { singletonEntity } from "./singletonEntity";
import { SyncStep } from "../SyncStep";
import { configToTables } from "../configToTables";
import { merge } from "@arktype/util";

type SyncToRecsOptions<config extends StoreConfig, extraTables extends Record<string, Table>> = SyncOptions & {
export type SyncToRecsOptions<config extends StoreConfig, extraTables extends Tables> = Omit<SyncOptions, "config"> & {
world: RecsWorld;
config: config;
tables?: extraTables;
startSync?: boolean;
};

type SyncToRecsResult<config extends StoreConfig, extraTables extends Record<string, Table>> = SyncResult & {
components: CreateStorageAdapterResult<config["tables"] & extraTables>["components"];
export type SyncToRecsResult<config extends StoreConfig, extraTables extends Tables> = SyncResult & {
components: CreateStorageAdapterResult<merge<merge<configToTables<config>, extraTables>, mudTables>>["components"];
stopSync: () => void;
};

export async function syncToRecs<config extends StoreConfig, extraTables extends Record<string, Table>>({
export async function syncToRecs<config extends StoreConfig, extraTables extends Tables = {}>({
world,
config,
tables: extraTables,
tables: extraTables = {} as extraTables,
startSync = true,
...syncOptions
}: SyncToRecsOptions<config, extraTables>): Promise<SyncToRecsResult<config, extraTables>> {
const tables = {
...config.tables,
...configToTables(config),
...extraTables,
} as config["tables"] & extraTables;
...mudTables,
};

const { storageAdapter, components } = createStorageAdapter({
world,
Expand Down Expand Up @@ -79,5 +82,5 @@ export async function syncToRecs<config extends StoreConfig, extraTables extends
...storeSync,
components,
stopSync,
};
} as never;
}
9 changes: 5 additions & 4 deletions packages/store-sync/src/recs/tablesToComponents.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import { Table } from "@latticexyz/config";
import { Tables } from "@latticexyz/config";
import { tableToComponent } from "./tableToComponent";
import { World } from "@latticexyz/recs";
import { show } from "@arktype/util";

export type tablesToComponents<tables extends Record<string, Table>> = {
export type tablesToComponents<tables extends Tables> = {
[label in keyof tables as tables[label]["label"]]: tableToComponent<tables[label]>;
};

export function tablesToComponents<tables extends Record<string, Table>>(
export function tablesToComponents<tables extends Tables>(
world: World,
tables: tables,
): tablesToComponents<tables> {
): show<tablesToComponents<tables>> {
return Object.fromEntries(
Object.entries(tables).map(([, table]) => [table.label, tableToComponent(world, table)]),
) as never;
Expand Down
4 changes: 2 additions & 2 deletions packages/store-sync/src/sqlite/syncToSqlite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { SyncOptions, SyncResult } from "../common";
import { sqliteStorage } from "./sqliteStorage";
import { createStoreSync } from "../createStoreSync";

type SyncToSqliteOptions<config extends StoreConfig = StoreConfig> = SyncOptions<config> & {
export type SyncToSqliteOptions<config extends StoreConfig = StoreConfig> = SyncOptions<config> & {
/**
* [SQLite database object from Drizzle][0].
*
Expand All @@ -15,7 +15,7 @@ type SyncToSqliteOptions<config extends StoreConfig = StoreConfig> = SyncOptions
startSync?: boolean;
};

type SyncToSqliteResult = SyncResult & {
export type SyncToSqliteResult = SyncResult & {
stopSync: () => void;
};

Expand Down
28 changes: 0 additions & 28 deletions packages/store-sync/src/zustand/getAllTables.ts

This file was deleted.

7 changes: 0 additions & 7 deletions packages/store-sync/src/zustand/mergeRight.ts

This file was deleted.

Loading
Loading