Skip to content

Commit

Permalink
fix(world): config uses readonly arrays (#2805)
Browse files Browse the repository at this point in the history
  • Loading branch information
holic authored May 8, 2024
1 parent 32c1cda commit 3dbf3bf
Show file tree
Hide file tree
Showing 9 changed files with 57 additions and 30 deletions.
5 changes: 5 additions & 0 deletions .changeset/seven-windows-tickle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@latticexyz/world": patch
---

Updated World config types to use readonly arrays.
8 changes: 4 additions & 4 deletions packages/world/ts/config/defaults.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
export const SYSTEM_DEFAULTS = {
registerFunctionSelector: true,
openAccess: true,
accessList: [] as string[],
accessList: [],
} as const;

export type SYSTEM_DEFAULTS = typeof SYSTEM_DEFAULTS;

export const WORLD_DEFAULTS = {
worldContractName: undefined,
worldInterfaceName: "IWorld",
systems: {} as Record<string, never>,
excludeSystems: [] as string[],
systems: {},
excludeSystems: [],
postDeployScript: "PostDeploy",
deploysDirectory: "./deploys",
worldsFile: "./worlds.json",
worldgenDirectory: "world",
worldImportPath: "@latticexyz/world/src/",
modules: [] as [],
modules: [],
} as const;

export type WORLD_DEFAULTS = typeof WORLD_DEFAULTS;
10 changes: 5 additions & 5 deletions packages/world/ts/config/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export type SystemUserConfig = {
/** If openAccess is false, only the addresses or systems in `access` can call the system */
openAccess: false;
/** An array of addresses or system names that can access the system */
accessList: string[];
accessList: readonly string[];
}
);

Expand All @@ -38,7 +38,7 @@ export interface ExpandSystemConfig<T extends SystemUserConfig, SystemName exten
openAccess: SYSTEM_DEFAULTS["openAccess"];
}
> {
accessList: T extends { accessList: string[] } ? T["accessList"] : SYSTEM_DEFAULTS["accessList"];
accessList: T extends { accessList: readonly string[] } ? T["accessList"] : SYSTEM_DEFAULTS["accessList"];
}

export type SystemsUserConfig = Record<string, SystemUserConfig>;
Expand All @@ -53,7 +53,7 @@ export type ModuleConfig = {
/** Should this module be installed as a root module? */
root?: boolean;
/** Arguments to be passed to the module's install method */
args?: (ValueWithType | DynamicResolution)[];
args?: readonly (ValueWithType | DynamicResolution)[];
};

// zod doesn't preserve doc comments
Expand All @@ -71,7 +71,7 @@ export interface WorldUserConfig {
*/
systems?: SystemsUserConfig;
/** Systems to exclude from automatic deployment */
excludeSystems?: string[];
excludeSystems?: readonly string[];
/**
* Script to execute after the deployment is complete (Default "PostDeploy").
* Script must be placed in the forge scripts directory (see foundry.toml) and have a ".s.sol" extension.
Expand All @@ -86,7 +86,7 @@ export interface WorldUserConfig {
/** Path for world package imports. Default is "@latticexyz/world/src/" */
worldImportPath?: string;
/** Modules to in the World */
modules?: ModuleConfig[];
modules?: readonly ModuleConfig[];
}

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

type modulesToV1<modules extends readonly Module[]> = mutable<{
[key in keyof modules]: Required<modules[key]>;
}>;
type modulesToV1<modules extends readonly Module[]> = {
[key in keyof modules]: modules[key];
};

function modulesToV1<modules extends readonly Module[]>(modules: modules): modulesToV1<modules> {
return modules.map((module) => ({
name: module.name,
...module,
root: module.root ?? false,
args: module.args ?? [],
})) as never;
Expand All @@ -29,7 +29,7 @@ function systemsToV1<systems extends Systems>(systems: systems): systemsToV1<sys
export type worldToV1<world> = world extends World
? Omit<storeToV1<world>, "v2"> & {
systems: systemsToV1<world["systems"]>;
excludeSystems: mutable<world["excludeSystems"]>;
excludeSystems: world["excludeSystems"];
modules: modulesToV1<world["modules"]>;
worldContractName: world["deploy"]["customWorldContract"];
postDeployScript: world["deploy"]["postDeployScript"];
Expand Down
11 changes: 9 additions & 2 deletions packages/world/ts/config/v2/defaults.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
export const SYSTEM_DEFAULTS = {
registerFunctionSelectors: true,
openAccess: true,
accessList: [] as string[],
accessList: [],
} as const;

export type SYSTEM_DEFAULTS = typeof SYSTEM_DEFAULTS;

export const MODULE_DEFAULTS = {
root: false,
args: [],
} as const;

export type MODULE_DEFAULTS = typeof MODULE_DEFAULTS;

export const CODEGEN_DEFAULTS = {
worldInterfaceName: "IWorld",
worldgenDirectory: "world",
Expand All @@ -27,7 +34,7 @@ export type DEPLOY_DEFAULTS = typeof DEPLOY_DEFAULTS;
export const CONFIG_DEFAULTS = {
systems: {},
tables: {},
excludeSystems: [] as string[],
excludeSystems: [],
modules: [],
codegen: CODEGEN_DEFAULTS,
deploy: DEPLOY_DEFAULTS,
Expand Down
18 changes: 14 additions & 4 deletions packages/world/ts/config/v2/input.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { evaluate } from "@arktype/util";
import { StoreInput, StoreWithShorthandsInput } from "@latticexyz/store/config/v2";
import { Module } from "./output";
import { DynamicResolution, ValueWithType } from "./dynamicResolution";

export type SystemInput = {
/** The full resource selector consists of namespace and name */
Expand All @@ -16,11 +16,21 @@ export type SystemInput = {
/** If openAccess is true, any address can call the system */
openAccess?: boolean;
/** An array of addresses or system names that can access the system */
accessList?: string[];
accessList?: readonly string[];
};

export type SystemsInput = { [key: string]: SystemInput };

export type ModuleInput = {
/** The name of the module */
readonly name: string;
/** Should this module be installed as a root module? */
readonly root?: boolean;
/** Arguments to be passed to the module's install method */
// TODO: make more strongly typed by taking in tables input
readonly args?: readonly (ValueWithType | DynamicResolution)[];
};

export type DeployInput = {
/**
* Script to execute after the deployment is complete (Default "PostDeploy").
Expand Down Expand Up @@ -56,9 +66,9 @@ export type WorldInput = evaluate<
*/
systems?: SystemsInput;
/** System names to exclude from automatic deployment */
excludeSystems?: string[];
excludeSystems?: readonly string[];
/** Modules to in the World */
modules?: Module[];
modules?: readonly ModuleInput[];
/** Deploy config */
deploy?: DeployInput;
/** Codegen config */
Expand Down
6 changes: 3 additions & 3 deletions packages/world/ts/config/v2/output.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ export type Module = {
/** The name of the module */
readonly name: string;
/** Should this module be installed as a root module? */
readonly root?: boolean;
readonly root: boolean;
/** Arguments to be passed to the module's install method */
readonly args?: (ValueWithType | DynamicResolution)[];
readonly args: readonly (ValueWithType | DynamicResolution)[];
};

export type System = {
Expand All @@ -24,7 +24,7 @@ export type System = {
/** If openAccess is true, any address can call the system */
readonly openAccess: boolean;
/** An array of addresses or system names that can access the system */
readonly accessList: string[];
readonly accessList: readonly string[];
};

export type Systems = { readonly [key: string]: System };
Expand Down
6 changes: 4 additions & 2 deletions packages/world/ts/config/v2/world.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
isObject,
} from "@latticexyz/store/config/v2";
import { SystemsInput, WorldInput } from "./input";
import { CONFIG_DEFAULTS } from "./defaults";
import { CONFIG_DEFAULTS, MODULE_DEFAULTS } from "./defaults";
import { Tables } from "@latticexyz/store/internal";
import { resolveSystems } from "./systems";
import { resolveNamespacedTables } from "./namespaces";
Expand Down Expand Up @@ -91,6 +91,8 @@ export function resolveWorld<const world extends WorldInput>(world: world): reso

const resolvedStore = resolveStore(world);

const modules = (world.modules ?? CONFIG_DEFAULTS.modules).map((mod) => mergeIfUndefined(mod, MODULE_DEFAULTS));

return mergeIfUndefined(
{
...resolvedStore,
Expand All @@ -99,7 +101,7 @@ export function resolveWorld<const world extends WorldInput>(world: world): reso
deploy: resolveDeploy(world.deploy),
systems: resolveSystems(world.systems ?? CONFIG_DEFAULTS.systems),
excludeSystems: get(world, "excludeSystems"),
modules: world.modules,
modules,
},
CONFIG_DEFAULTS,
) as never;
Expand Down
11 changes: 7 additions & 4 deletions packages/world/ts/config/worldConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { SYSTEM_DEFAULTS, WORLD_DEFAULTS } from "./defaults";

const zSystemName = zObjectName;
const zModuleName = zObjectName;
const zSystemAccessList = z.array(zSystemName.or(zEthereumAddress)).default(SYSTEM_DEFAULTS.accessList);
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
const zSystemConfig = z.intersection(
Expand Down Expand Up @@ -32,21 +32,24 @@ const zDynamicResolution = z.object({ type: z.nativeEnum(DynamicResolutionType),
const zModuleConfig = z.object({
name: zModuleName,
root: z.boolean().default(false),
args: z.array(z.union([zValueWithType, zDynamicResolution])).default([]),
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(),
worldInterfaceName: z.string().default(WORLD_DEFAULTS.worldInterfaceName),
systems: z.record(zSystemName, zSystemConfig).default(WORLD_DEFAULTS.systems),
excludeSystems: z.array(zSystemName).default(WORLD_DEFAULTS.excludeSystems),
excludeSystems: z.array(zSystemName).readonly().default(WORLD_DEFAULTS.excludeSystems),
postDeployScript: z.string().default(WORLD_DEFAULTS.postDeployScript),
deploysDirectory: z.string().default(WORLD_DEFAULTS.deploysDirectory),
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).default(WORLD_DEFAULTS.modules),
modules: z.array(zModuleConfig).readonly().default(WORLD_DEFAULTS.modules),
});

// Catchall preserves other plugins' options
Expand Down

0 comments on commit 3dbf3bf

Please sign in to comment.