Skip to content

Commit

Permalink
fix(cli,world): resolve table by just name (#2850)
Browse files Browse the repository at this point in the history
  • Loading branch information
holic authored May 22, 2024
1 parent d752660 commit 9be2bb8
Show file tree
Hide file tree
Showing 17 changed files with 52 additions and 128 deletions.
13 changes: 13 additions & 0 deletions .changeset/heavy-seas-learn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
"@latticexyz/cli": patch
"@latticexyz/world": patch
---

Fixed `resolveTableId` usage within config's module `args` to allow referencing both namespaced tables (e.g. `resolveTableId("app_Tasks")`) as well as tables by just their name (e.g. `resolveTableId("Tasks")`). Note that using just the table name requires it to be unique among all tables within the config.

This helper is now exported from `@latticexyz/world` package as intended. The previous, deprecated export has been removed.

```diff
-import { resolveTableId } from "@latticexyz/config/library";
+import { resolveTableId } from "@latticexyz/world/internal";
```
2 changes: 1 addition & 1 deletion examples/minimal/packages/contracts/mud.config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { defineWorld } from "@latticexyz/world";
import { resolveTableId } from "@latticexyz/config/library";
import { resolveTableId } from "@latticexyz/world/internal";

export default defineWorld({
systems: {
Expand Down
1 change: 0 additions & 1 deletion examples/minimal/packages/contracts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
},
"devDependencies": {
"@latticexyz/cli": "link:../../../../packages/cli",
"@latticexyz/config": "link:../../../../packages/config",
"@latticexyz/faucet": "link:../../../../packages/faucet",
"@latticexyz/schema-type": "link:../../../../packages/schema-type",
"@latticexyz/store": "link:../../../../packages/store",
Expand Down
3 changes: 0 additions & 3 deletions examples/minimal/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 3 additions & 10 deletions packages/cli/src/deploy/configToModules.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,19 @@
import path from "node:path";
import { Module } from "./common";
import { resolveWithContext } from "@latticexyz/config/library";
import { encodeField } from "@latticexyz/protocol-parser/internal";
import { SchemaAbiType, SchemaAbiTypeToPrimitiveType } from "@latticexyz/schema-type/internal";
import { bytesToHex, hexToBytes } from "viem";
import { bytesToHex } from "viem";
import { createPrepareDeploy } from "./createPrepareDeploy";
import { World } from "@latticexyz/world";
import { getContractArtifact } from "../utils/getContractArtifact";
import { knownModuleArtifacts } from "../utils/knownModuleArtifacts";
import { resolveWithContext } from "@latticexyz/world/internal";

export async function configToModules<config extends World>(
config: config,
// TODO: remove/replace `forgeOutDir`
forgeOutDir: string,
): Promise<readonly Module[]> {
// this expects a namespaced table name when used with `resolveTableId`
const resolveContext = {
tableIds: Object.fromEntries(
Object.entries(config.tables).map(([tableName, table]) => [tableName, hexToBytes(table.tableId)]),
),
};

const modules = await Promise.all(
config.modules.map(async (mod): Promise<Module> => {
let artifactPath = mod.artifactPath;
Expand Down Expand Up @@ -57,7 +50,7 @@ export async function configToModules<config extends World>(

// TODO: replace args with something more strongly typed
const installArgs = mod.args
.map((arg) => resolveWithContext(arg, resolveContext))
.map((arg) => resolveWithContext(arg, { config }))
.map((arg) => {
const value = arg.value instanceof Uint8Array ? bytesToHex(arg.value) : arg.value;
return encodeField(arg.type as SchemaAbiType, value as SchemaAbiTypeToPrimitiveType<SchemaAbiType>);
Expand Down
55 changes: 0 additions & 55 deletions packages/config/src/deprecated/library/dynamicResolution.ts

This file was deleted.

1 change: 0 additions & 1 deletion packages/config/src/deprecated/library/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,5 @@
export * from "./commonSchemas";
export * from "./context";
export * from "./core";
export * from "./dynamicResolution";
export * from "./errors";
export * from "./validation";
4 changes: 2 additions & 2 deletions packages/config/src/deprecated/register/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { mudCoreConfig, resolveTableId } from "../library";
import { mudCoreConfig } from "../library";
import { MUDCoreContext } from "../library/context";

export { mudCoreConfig, resolveTableId };
export { mudCoreConfig };

// Importing this file has side-effects, and it should always be imported before MUD plugins.
// Use this import for defining a MUD config.
Expand Down
2 changes: 1 addition & 1 deletion packages/store/ts/register/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

// For convenience register and reexport config, to reduce the number of needed imports for users
import "@latticexyz/config/register";
export { mudCoreConfig, resolveTableId } from "@latticexyz/config/register";
export { mudCoreConfig } from "@latticexyz/config/register";
// Extend core config and types
import "./configExtensions";
import "./typeExtensions";
Expand Down
1 change: 0 additions & 1 deletion packages/world/ts/config/defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ export const WORLD_DEFAULTS = {
worldsFile: "./worlds.json",
worldgenDirectory: "world",
worldImportPath: "@latticexyz/world/src/",
modules: [],
} as const;

export type WORLD_DEFAULTS = typeof WORLD_DEFAULTS;
5 changes: 4 additions & 1 deletion packages/world/ts/config/resolveWorldConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ export type ResolvedWorldConfig = ReturnType<typeof resolveWorldConfig>;
* filtering out excluded systems, validate system names refer to existing contracts, and
* splitting the access list into addresses and system names.
*/
export function resolveWorldConfig(config: StoreConfig & WorldConfig, existingContracts?: string[]) {
export function resolveWorldConfig(
config: Pick<StoreConfig & WorldConfig, "systems" | "excludeSystems">,
existingContracts?: string[],
) {
// Include contract names ending in "System", but not the base "System" contract, and not Interfaces
const defaultSystemNames =
existingContracts?.filter((name) => name.endsWith("System") && name !== "System" && !name.match(/^I[A-Z]/)) ?? [];
Expand Down
4 changes: 1 addition & 3 deletions packages/world/ts/config/types.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { z } from "zod";
import { DynamicResolution, ValueWithType } from "@latticexyz/config/library";
import { OrDefaults } from "@latticexyz/common/type-utils";
import { zWorldConfig } from "./worldConfig";
import { SYSTEM_DEFAULTS } from "./defaults";
import { DynamicResolution, ValueWithType } from "./v2/dynamicResolution";

// zod doesn't preserve doc comments
export type SystemUserConfig = {
Expand Down Expand Up @@ -85,8 +85,6 @@ export interface WorldUserConfig {
worldgenDirectory?: string;
/** Path for world package imports. Default is "@latticexyz/world/src/" */
worldImportPath?: string;
/** Modules to in the World */
modules?: readonly ModuleConfig[];
}

export type WorldConfig = z.output<typeof zWorldConfig>;
Expand Down
16 changes: 1 addition & 15 deletions packages/world/ts/config/v2/compat.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,8 @@
import { conform } from "@arktype/util";
import { Module, World, Systems } from "./output";
import { World, Systems } from "./output";
import { Store } from "@latticexyz/store";
import { storeToV1 } from "@latticexyz/store/config/v2";

type modulesToV1<modules extends readonly Module[]> = {
[key in keyof modules]: Omit<modules[key], "artifactPath">;
};

function modulesToV1<modules extends readonly Module[]>(modules: modules): modulesToV1<modules> {
return modules.map((module) => ({
...module,
root: module.root ?? false,
args: module.args ?? [],
})) as never;
}

type systemsToV1<systems extends Systems> = {
[key in keyof systems]: {
name?: systems[key]["name"];
Expand All @@ -30,7 +18,6 @@ export type worldToV1<world> = world extends World
? Omit<storeToV1<world>, "v2"> & {
systems: systemsToV1<world["systems"]>;
excludeSystems: world["excludeSystems"];
modules: modulesToV1<world["modules"]>;
worldContractName: world["deploy"]["customWorldContract"];
postDeployScript: world["deploy"]["postDeployScript"];
deploysDirectory: world["deploy"]["deploysDirectory"];
Expand All @@ -46,7 +33,6 @@ export function worldToV1<world>(world: conform<world, World>): worldToV1<world>
const v1WorldConfig = {
systems: systemsToV1(world.systems),
excludeSystems: world.excludeSystems,
modules: modulesToV1(world.modules),
worldContractName: world.deploy.customWorldContract,
postDeployScript: world.deploy.postDeployScript,
deploysDirectory: world.deploy.deploysDirectory,
Expand Down
37 changes: 22 additions & 15 deletions packages/world/ts/config/v2/dynamicResolution.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
export enum DynamicResolutionType {
TABLE_ID,
SYSTEM_ADDRESS,
}
import { World } from "./output";

export type DynamicResolution = {
type: DynamicResolutionType;
// TODO: add systemAddress support
type: "tableId";
input: string;
};

Expand All @@ -18,9 +16,9 @@ export type ValueWithType = {
*/
export function resolveTableId(tableName: string) {
return {
type: DynamicResolutionType.TABLE_ID,
type: "tableId",
input: tableName,
};
} as const;
}

/** Type guard for DynamicResolution */
Expand All @@ -38,20 +36,29 @@ export function isValueWithType(value: unknown): value is ValueWithType {
*/
export function resolveWithContext(
input: unknown,
context: { systemAddresses?: Record<string, Promise<string>>; tableIds?: Record<string, Uint8Array> },
context: { config: World; systemAddresses?: Record<string, Promise<string>> },
): ValueWithType {
if (isValueWithType(input)) return input;

if (isDynamicResolution(input)) {
let resolved: ValueWithType | undefined = undefined;
if (input.type === "tableId") {
const tableEntries = Object.entries(context.config.tables).filter(
([tableName, table]) => tableName === input.input || table.name === input.input,
);

if (input.type === DynamicResolutionType.TABLE_ID) {
const tableId = context.tableIds?.[input.input];
resolved = tableId && { value: tableId, type: "bytes32" };
}
if (tableEntries.length > 1) {
throw new Error(
`Found more than one table with name "${input.input}". Try using one of the following table names instead: ${tableEntries.map(([tableName]) => tableName).join(", ")}`,
);
}

if (resolved) return resolved;
if (tableEntries.length === 1) {
const [entry] = tableEntries;
const [, table] = entry;
return { type: "bytes32", value: table.tableId };
}
}
}

throw new Error(`Could not resolve dynamic resolution: \n${JSON.stringify(input, null, 2)}`);
throw new Error(`Could not resolve dynamic resolution:\n${JSON.stringify(input, null, 2)}`);
}
19 changes: 1 addition & 18 deletions packages/world/ts/config/worldConfig.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { z } from "zod";
import { DynamicResolutionType, zEthereumAddress, zName, zObjectName } from "@latticexyz/config/library";
import { zEthereumAddress, zName, zObjectName } from "@latticexyz/config/library";
import { SYSTEM_DEFAULTS, WORLD_DEFAULTS } from "./defaults";

const zSystemName = zObjectName;
const zModuleName = zObjectName;
const zSystemAccessList = z.array(zSystemName.or(zEthereumAddress)).readonly().default(SYSTEM_DEFAULTS.accessList);

// The system config is a combination of a name config and access config
Expand All @@ -23,21 +22,6 @@ const zSystemConfig = z.intersection(
]),
);

const zValueWithType = z.object({
value: z.union([z.string(), z.number(), z.instanceof(Uint8Array)]),
type: z.string(),
});
const zDynamicResolution = z.object({ type: z.nativeEnum(DynamicResolutionType), input: z.string() });

const zModuleConfig = z.object({
name: zModuleName,
root: z.boolean().default(false),
args: z
.array(z.union([zValueWithType, zDynamicResolution]))
.readonly()
.default([]),
});

// The parsed world config is the result of parsing the user config
export const zWorldConfig = z.object({
worldContractName: z.string().optional(),
Expand All @@ -49,7 +33,6 @@ export const zWorldConfig = z.object({
worldsFile: z.string().default(WORLD_DEFAULTS.worldsFile),
worldgenDirectory: z.string().default(WORLD_DEFAULTS.worldgenDirectory),
worldImportPath: z.string().default(WORLD_DEFAULTS.worldImportPath),
modules: z.array(zModuleConfig).readonly().default(WORLD_DEFAULTS.modules),
});

// Catchall preserves other plugins' options
Expand Down
2 changes: 2 additions & 0 deletions packages/world/ts/exports/internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,5 @@ export * from "../encodeSystemCallsFrom";
export * from "../actions/callFrom";

export * from "../callWithSignatureTypes";

export { resolveTableId, resolveWithContext } from "../config/v2/dynamicResolution";
2 changes: 1 addition & 1 deletion packages/world/ts/register/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
// For convenience register and reexport store (which does the same for core config),
// to reduce the number of needed imports for users
import "@latticexyz/store/register";
export { mudConfig, mudCoreConfig, resolveTableId } from "@latticexyz/store/register";
export { mudConfig, mudCoreConfig } from "@latticexyz/store/register";
// Extend core config and types
import "./configExtensions";
import "./typeExtensions";

0 comments on commit 9be2bb8

Please sign in to comment.