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

fix(cli,world): resolve table by just name #2850

Merged
merged 8 commits into from
May 22, 2024
Merged
Show file tree
Hide file tree
Changes from 6 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
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;
Copy link
Member Author

Choose a reason for hiding this comment

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

this is an internal thing and changing this to strings instead of enums leads to better errors in the resolveWithContext function

};

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(", ")}`,
);
Copy link
Member

Choose a reason for hiding this comment

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

nice!

}

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";
Copy link
Member Author

Choose a reason for hiding this comment

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

I am intentionally exporting resolveTableId in the internal bundle for now as I want to reconsider the API before we make it concrete.

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";
Loading