@latticexyz/cli 1.41.1-alpha.41 → 1.42.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/dist/chunk-4STWSICF.js +26139 -0
  2. package/dist/chunk-6V563IAZ.js +283 -0
  3. package/dist/chunk-7KQJTK2K.js +3842 -0
  4. package/dist/{chunk-ATAWDHWC.js → chunk-FPG73MVN.js} +5 -1
  5. package/dist/{chunk-O6HOO6WA.js → chunk-L4YLJHLJ.js} +1 -9
  6. package/dist/{chunk-J4DJQNIC.js → chunk-SKNB74MT.js} +129 -568
  7. package/dist/chunk-VQTZJIFF.js +353 -0
  8. package/dist/chunk-WZFXLDPK.js +761 -0
  9. package/dist/index.d.ts +2 -3
  10. package/dist/index.js +0 -21
  11. package/dist/mud.d.ts +1 -1
  12. package/dist/mud.js +326 -4452
  13. package/dist/mud2.d.ts +1 -0
  14. package/dist/mud2.js +25 -0
  15. package/dist/render-solidity/index.d.ts +171 -0
  16. package/dist/render-solidity/index.js +49 -0
  17. package/dist/render-ts/index.d.ts +26 -0
  18. package/dist/render-ts/index.js +14 -0
  19. package/dist/utils/deprecated/index.js +3 -3
  20. package/dist/utils/index.d.ts +13 -18
  21. package/dist/utils/index.js +14 -16
  22. package/package.json +21 -21
  23. package/src/commands/deploy-v2.ts +80 -64
  24. package/src/commands/deprecated/index.ts +22 -0
  25. package/src/commands/deprecated/test.ts +1 -1
  26. package/src/commands/gas-report.ts +54 -55
  27. package/src/commands/index.ts +6 -17
  28. package/src/commands/set-version.ts +172 -0
  29. package/src/commands/tablegen.ts +5 -5
  30. package/src/commands/test-v2.ts +71 -0
  31. package/src/commands/tsgen.ts +33 -0
  32. package/src/commands/worldgen.ts +5 -4
  33. package/src/contracts/BulkUpload.sol +13 -20
  34. package/src/contracts/Deploy.sol +3 -3
  35. package/src/contracts/LibDeploy.sol +1 -1
  36. package/src/contracts/LibDeployStub.sol +1 -1
  37. package/src/index.ts +1 -15
  38. package/src/mud.ts +4 -3
  39. package/src/mud2.ts +29 -0
  40. package/src/render-solidity/common.ts +4 -4
  41. package/src/render-solidity/field.ts +25 -2
  42. package/src/render-solidity/record.ts +14 -10
  43. package/src/render-solidity/renderSystemInterface.ts +4 -4
  44. package/src/render-solidity/renderTableIndex.ts +15 -0
  45. package/src/render-solidity/renderTypesFromConfig.ts +1 -1
  46. package/src/render-solidity/tableOptions.ts +2 -2
  47. package/src/render-solidity/tablegen.ts +15 -13
  48. package/src/render-solidity/types.ts +2 -1
  49. package/src/render-solidity/userType.ts +1 -2
  50. package/src/render-solidity/worldgen.ts +8 -9
  51. package/src/render-ts/index.ts +5 -0
  52. package/src/render-ts/recsV1TableOptions.ts +39 -0
  53. package/src/render-ts/renderRecsV1Tables.ts +31 -0
  54. package/src/render-ts/schemaTypesToRecsTypeStrings.ts +202 -0
  55. package/src/render-ts/tsgen.ts +12 -0
  56. package/src/render-ts/types.ts +13 -0
  57. package/src/utils/contractToInterface.ts +5 -3
  58. package/src/utils/deploy-v2.ts +90 -84
  59. package/src/utils/errors.ts +3 -34
  60. package/src/utils/format.ts +6 -0
  61. package/src/utils/formatAndWrite.ts +11 -2
  62. package/src/utils/foundry.ts +9 -0
  63. package/src/utils/index.ts +1 -0
  64. package/dist/chunk-O57QENJ6.js +0 -23039
  65. package/dist/chunk-SLIMIO4Z.js +0 -14358
  66. package/dist/config/index.d.ts +0 -763
  67. package/dist/config/index.js +0 -83
  68. package/src/config/commonSchemas.ts +0 -34
  69. package/src/config/dynamicResolution.ts +0 -49
  70. package/src/config/index.ts +0 -24
  71. package/src/config/loadConfig.ts +0 -39
  72. package/src/config/loadStoreConfig.ts +0 -18
  73. package/src/config/parseStoreConfig.test-d.ts +0 -40
  74. package/src/config/parseStoreConfig.ts +0 -314
  75. package/src/config/validation.ts +0 -163
  76. package/src/config/world/index.ts +0 -4
  77. package/src/config/world/loadWorldConfig.test-d.ts +0 -11
  78. package/src/config/world/loadWorldConfig.ts +0 -26
  79. package/src/config/world/parseWorldConfig.ts +0 -55
  80. package/src/config/world/resolveWorldConfig.ts +0 -80
  81. package/src/config/world/userTypes.ts +0 -72
  82. package/src/utils/typeUtils.ts +0 -17
@@ -1,163 +0,0 @@
1
- import { ethers } from "ethers";
2
- import { ZodIssueCode, RefinementCtx } from "zod";
3
-
4
- export function validateName(name: string, ctx: RefinementCtx) {
5
- if (!/^\w+$/.test(name)) {
6
- ctx.addIssue({
7
- code: ZodIssueCode.custom,
8
- message: `Name must contain only alphanumeric & underscore characters`,
9
- });
10
- }
11
- }
12
-
13
- export function validateCapitalizedName(name: string, ctx: RefinementCtx) {
14
- validateName(name, ctx);
15
-
16
- if (!/^[A-Z]/.test(name)) {
17
- ctx.addIssue({
18
- code: ZodIssueCode.custom,
19
- message: `Name must start with a capital letter`,
20
- });
21
- }
22
- }
23
-
24
- export function validateUncapitalizedName(name: string, ctx: RefinementCtx) {
25
- validateName(name, ctx);
26
-
27
- if (!/^[a-z]/.test(name)) {
28
- ctx.addIssue({
29
- code: ZodIssueCode.custom,
30
- message: `Name must start with a lowercase letter`,
31
- });
32
- }
33
- }
34
-
35
- // validates only the enum array, not the names of enum members
36
- export function validateEnum(members: string[], ctx: RefinementCtx) {
37
- if (members.length === 0) {
38
- ctx.addIssue({
39
- code: ZodIssueCode.custom,
40
- message: `Enum must not be empty`,
41
- });
42
- }
43
- if (members.length >= 256) {
44
- ctx.addIssue({
45
- code: ZodIssueCode.custom,
46
- message: `Length of enum must be < 256`,
47
- });
48
- }
49
-
50
- const duplicates = getDuplicates(members);
51
- if (duplicates.length > 0) {
52
- ctx.addIssue({
53
- code: ZodIssueCode.custom,
54
- message: `Enum must not have duplicate names for: ${duplicates.join(", ")}`,
55
- });
56
- }
57
- }
58
-
59
- function _factoryForValidateRoute(requireNonEmpty: boolean, requireSingleLevel: boolean) {
60
- return (route: string, ctx: RefinementCtx) => {
61
- if (route === "") {
62
- if (requireNonEmpty) {
63
- ctx.addIssue({
64
- code: ZodIssueCode.custom,
65
- message: `Route must not be empty`,
66
- });
67
- }
68
- // we can skip further validation for empty routes
69
- return;
70
- }
71
-
72
- if (route[0] !== "/") {
73
- ctx.addIssue({
74
- code: ZodIssueCode.custom,
75
- message: `Route must start with "/"`,
76
- });
77
- }
78
-
79
- if (route[route.length - 1] === "/") {
80
- ctx.addIssue({
81
- code: ZodIssueCode.custom,
82
- message: `Route must not end with "/"`,
83
- });
84
- }
85
-
86
- const parts = route.split("/");
87
- if (requireSingleLevel && parts.length > 2) {
88
- ctx.addIssue({
89
- code: ZodIssueCode.custom,
90
- message: `Route must only have one level (e.g. "/foo")`,
91
- });
92
- }
93
-
94
- // start at 1 to skip the first empty part
95
- for (let i = 1; i < parts.length; i++) {
96
- if (parts[i] === "") {
97
- ctx.addIssue({
98
- code: ZodIssueCode.custom,
99
- message: `Route must not contain empty route fragments (e.g. "//")`,
100
- });
101
- }
102
-
103
- if (!/^\w+$/.test(parts[i])) {
104
- ctx.addIssue({
105
- code: ZodIssueCode.custom,
106
- message: `Route must contain only alphanumeric & underscore characters`,
107
- });
108
- }
109
- }
110
- };
111
- }
112
-
113
- export const validateRoute = _factoryForValidateRoute(true, false);
114
-
115
- export const validateBaseRoute = _factoryForValidateRoute(false, false);
116
-
117
- export const validateSingleLevelRoute = _factoryForValidateRoute(true, true);
118
-
119
- export function validateEthereumAddress(address: string, ctx: RefinementCtx) {
120
- if (!ethers.utils.isAddress(address)) {
121
- ctx.addIssue({
122
- code: ZodIssueCode.custom,
123
- message: `Address must be a valid Ethereum address`,
124
- });
125
- }
126
- }
127
-
128
- export function getDuplicates<T>(array: T[]) {
129
- const checked = new Set<T>();
130
- const duplicates = new Set<T>();
131
- for (const element of array) {
132
- if (checked.has(element)) {
133
- duplicates.add(element);
134
- }
135
- checked.add(element);
136
- }
137
- return [...duplicates];
138
- }
139
-
140
- export function validateSelector(name: string, ctx: RefinementCtx) {
141
- if (name.length > 16) {
142
- ctx.addIssue({
143
- code: ZodIssueCode.custom,
144
- message: `Selector must be <= 16 characters`,
145
- });
146
- }
147
- if (!/^\w*$/.test(name)) {
148
- ctx.addIssue({
149
- code: ZodIssueCode.custom,
150
- message: `Selector must contain only alphanumeric & underscore characters`,
151
- });
152
- }
153
- }
154
-
155
- /** Returns null if the type does not look like a static array, otherwise element and length data */
156
- export function parseStaticArray(abiType: string) {
157
- const matches = abiType.match(/^(\w+)\[(\d+)\]$/);
158
- if (!matches) return null;
159
- return {
160
- elementType: matches[1],
161
- staticLength: Number.parseInt(matches[2]),
162
- };
163
- }
@@ -1,4 +0,0 @@
1
- export * from "./loadWorldConfig.js";
2
- export * from "./parseWorldConfig.js";
3
- export * from "./resolveWorldConfig.js";
4
- export * from "./userTypes.js";
@@ -1,11 +0,0 @@
1
- import { describe, expectTypeOf } from "vitest";
2
- import { z } from "zod";
3
- import { zWorldConfig, WorldUserConfig } from "./index.js";
4
-
5
- describe("loadWorldConfig", () => {
6
- // Typecheck manual interfaces against zod
7
- expectTypeOf<WorldUserConfig>().toEqualTypeOf<z.input<typeof zWorldConfig>>();
8
- // type equality isn't deep for optionals
9
- expectTypeOf<WorldUserConfig["overrideSystems"]>().toEqualTypeOf<z.input<typeof zWorldConfig>["overrideSystems"]>();
10
- // TODO If more nested schemas are added, provide separate tests for them
11
- });
@@ -1,26 +0,0 @@
1
- import { ZodError } from "zod";
2
- import { fromZodErrorCustom } from "../../utils/errors.js";
3
- import { loadConfig } from "../loadConfig.js";
4
- import { zWorldConfig } from "./parseWorldConfig.js";
5
- import { resolveWorldConfig } from "./resolveWorldConfig.js";
6
-
7
- /**
8
- * Loads and resolves the world config.
9
- * @param configPath Path to load the config from. Defaults to "mud.config.mts" or "mud.config.ts"
10
- * @param existingContracts Optional list of existing contract names to validate system names against. If not provided, no validation is performed. Contract names ending in `System` will be added to the config with default values.
11
- * @returns Promise of ResolvedWorldConfig object
12
- */
13
- export async function loadWorldConfig(configPath?: string, existingContracts?: string[]) {
14
- const config = await loadConfig(configPath);
15
-
16
- try {
17
- const parsedConfig = zWorldConfig.parse(config);
18
- return resolveWorldConfig(parsedConfig, existingContracts);
19
- } catch (error) {
20
- if (error instanceof ZodError) {
21
- throw fromZodErrorCustom(error, "WorldConfig Validation Error");
22
- } else {
23
- throw error;
24
- }
25
- }
26
- }
@@ -1,55 +0,0 @@
1
- import { z } from "zod";
2
- import { zEthereumAddress, zObjectName, zSelector } from "../commonSchemas.js";
3
- import { DynamicResolutionType } from "../dynamicResolution.js";
4
-
5
- const zSystemName = zObjectName;
6
- const zModuleName = zObjectName;
7
- const zSystemAccessList = z.array(zSystemName.or(zEthereumAddress)).default([]);
8
-
9
- // The system config is a combination of a fileSelector config and access config
10
- const zSystemConfig = z.intersection(
11
- z.object({
12
- fileSelector: zSelector,
13
- registerFunctionSelectors: z.boolean().default(true),
14
- }),
15
- z.discriminatedUnion("openAccess", [
16
- z.object({
17
- openAccess: z.literal(true),
18
- }),
19
- z.object({
20
- openAccess: z.literal(false),
21
- accessList: zSystemAccessList,
22
- }),
23
- ])
24
- );
25
-
26
- const zValueWithType = z.object({
27
- value: z.union([z.string(), z.number(), z.instanceof(Uint8Array)]),
28
- type: z.string(),
29
- });
30
- const zDynamicResolution = z.object({ type: z.nativeEnum(DynamicResolutionType), input: z.string() });
31
-
32
- const zModuleConfig = z.object({
33
- name: zModuleName,
34
- root: z.boolean().default(false),
35
- args: z.array(z.union([zValueWithType, zDynamicResolution])).default([]),
36
- });
37
-
38
- // The parsed world config is the result of parsing the user config
39
- export const zWorldConfig = z.object({
40
- namespace: zSelector.default(""),
41
- worldContractName: z.string().optional(),
42
- overrideSystems: z.record(zSystemName, zSystemConfig).default({}),
43
- excludeSystems: z.array(zSystemName).default([]),
44
- postDeployScript: z.string().default("PostDeploy"),
45
- deploysDirectory: z.string().default("./deploys"),
46
- worldgenDirectory: z.string().default("world"),
47
- worldImportPath: z.string().default("@latticexyz/world/src/"),
48
- modules: z.array(zModuleConfig).default([]),
49
- });
50
-
51
- export async function parseWorldConfig(config: unknown) {
52
- return zWorldConfig.parse(config);
53
- }
54
-
55
- export type ParsedWorldConfig = z.output<typeof zWorldConfig>;
@@ -1,80 +0,0 @@
1
- import { UnrecognizedSystemErrorFactory } from "../../utils/errors.js";
2
- import { ParsedWorldConfig } from "./parseWorldConfig.js";
3
- import { SystemUserConfig } from "./userTypes.js";
4
-
5
- export type ResolvedSystemConfig = ReturnType<typeof resolveSystemConfig>;
6
-
7
- export type ResolvedWorldConfig = ReturnType<typeof resolveWorldConfig>;
8
-
9
- /**
10
- * Resolves the world config by combining the default and overridden system configs,
11
- * filtering out excluded systems, validate system names refer to existing contracts, and
12
- * splitting the access list into addresses and system names.
13
- */
14
- export function resolveWorldConfig(config: ParsedWorldConfig, existingContracts?: string[]) {
15
- // Include contract names ending in "System", but not the base "System" contract, and not Interfaces
16
- const defaultSystemNames =
17
- existingContracts?.filter((name) => name.endsWith("System") && name !== "System" && !name.match(/^I[A-Z]/)) ?? [];
18
- const overriddenSystemNames = Object.keys(config.overrideSystems);
19
-
20
- // Validate every key in overrideSystems refers to an existing system contract (and is not called "World")
21
- if (existingContracts) {
22
- for (const systemName of overriddenSystemNames) {
23
- if (!existingContracts.includes(systemName) || systemName === "World") {
24
- throw UnrecognizedSystemErrorFactory(["overrideSystems", systemName], systemName);
25
- }
26
- }
27
- }
28
-
29
- // Combine the default and overridden system names and filter out excluded systems
30
- const systemNames = [...new Set([...defaultSystemNames, ...overriddenSystemNames])].filter(
31
- (name) => !config.excludeSystems.includes(name)
32
- );
33
-
34
- // Resolve the config
35
- const resolvedSystems: Record<string, ResolvedSystemConfig> = systemNames.reduce((acc, systemName) => {
36
- return {
37
- ...acc,
38
- [systemName]: resolveSystemConfig(systemName, config.overrideSystems[systemName], existingContracts),
39
- };
40
- }, {});
41
-
42
- const { overrideSystems, excludeSystems, ...otherConfig } = config;
43
- return { ...otherConfig, systems: resolvedSystems };
44
- }
45
-
46
- /**
47
- * Resolves the system config by combining the default and overridden system configs,
48
- * @param systemName name of the system
49
- * @param config optional SystemConfig object, if none is provided the default config is used
50
- * @param existingContracts optional list of existing contract names, used to validate system names in the access list. If not provided, no validation is performed.
51
- * @returns ResolvedSystemConfig object
52
- * Default value for fileSelector is `systemName`
53
- * Default value for registerFunctionSelectors is true
54
- * Default value for openAccess is true
55
- * Default value for accessListAddresses is []
56
- * Default value for accessListSystems is []
57
- */
58
- export function resolveSystemConfig(systemName: string, config?: SystemUserConfig, existingContracts?: string[]) {
59
- const fileSelector = config?.fileSelector ?? systemName;
60
- const registerFunctionSelectors = config?.registerFunctionSelectors ?? true;
61
- const openAccess = config?.openAccess ?? true;
62
- const accessListAddresses: string[] = [];
63
- const accessListSystems: string[] = [];
64
- const accessList = config && !config.openAccess ? config.accessList : [];
65
-
66
- // Split the access list into addresses and system names
67
- for (const accessListItem of accessList) {
68
- if (accessListItem.startsWith("0x")) {
69
- accessListAddresses.push(accessListItem);
70
- } else {
71
- // Validate every system refers to an existing system contract
72
- if (existingContracts && !existingContracts.includes(accessListItem)) {
73
- throw UnrecognizedSystemErrorFactory(["overrideSystems", systemName, "accessList"], accessListItem);
74
- }
75
- accessListSystems.push(accessListItem);
76
- }
77
- }
78
-
79
- return { fileSelector, registerFunctionSelectors, openAccess, accessListAddresses, accessListSystems };
80
- }
@@ -1,72 +0,0 @@
1
- import { DynamicResolution } from "../dynamicResolution.js";
2
-
3
- // zod doesn't preserve doc comments
4
- export type SystemUserConfig =
5
- | {
6
- /** The full resource selector consists of namespace and fileSelector */
7
- fileSelector?: string;
8
- /**
9
- * Register function selectors for the system in the World.
10
- * Defaults to true.
11
- * Note:
12
- * - For root systems all World function selectors will correspond to the system's function selectors.
13
- * - For non-root systems, the World function selectors will be <namespace>_<system>_<function>.
14
- */
15
- registerFunctionSelectors?: boolean;
16
- } & (
17
- | {
18
- /** If openAccess is true, any address can call the system */
19
- openAccess: true;
20
- }
21
- | {
22
- /** If openAccess is false, only the addresses or systems in `access` can call the system */
23
- openAccess: false;
24
- /** An array of addresses or system names that can access the system */
25
- accessList: string[];
26
- }
27
- );
28
-
29
- export type ValueWithType = {
30
- value: string | number | Uint8Array;
31
- type: string;
32
- };
33
-
34
- export type ModuleConfig = {
35
- /** The name of the module */
36
- name: string;
37
- /** Should this module be installed as a root module? */
38
- root?: boolean;
39
- /** Arguments to be passed to the module's install method */
40
- args?: (ValueWithType | DynamicResolution)[];
41
- };
42
-
43
- // zod doesn't preserve doc comments
44
- export interface WorldUserConfig {
45
- /** The namespace to register tables and systems at. Defaults to the root namespace (empty string) */
46
- namespace?: string;
47
- /** The name of the World contract to deploy. If no name is provided, a vanilla World is deployed */
48
- worldContractName?: string;
49
- /**
50
- * Contracts named *System will be deployed by default
51
- * as public systems at `namespace/ContractName`, unless overridden
52
- *
53
- * The key is the system name (capitalized).
54
- * The value is a SystemConfig object.
55
- */
56
- overrideSystems?: Record<string, SystemUserConfig>;
57
- /** Systems to exclude from automatic deployment */
58
- excludeSystems?: string[];
59
- /**
60
- * Script to execute after the deployment is complete (Default "PostDeploy").
61
- * Script must be placed in the forge scripts directory (see foundry.toml) and have a ".s.sol" extension.
62
- */
63
- postDeployScript?: string;
64
- /** Directory to write the deployment info to (Default "./deploys") */
65
- deploysDirectory?: string;
66
- /** Directory to output system and world interfaces of `worldgen` (Default "world") */
67
- worldgenDirectory?: string;
68
- /** Path for world package imports. Default is "@latticexyz/world/src/" */
69
- worldImportPath?: string;
70
- /** Modules to in the World */
71
- modules?: ModuleConfig[];
72
- }
@@ -1,17 +0,0 @@
1
- import { AbiType, StaticAbiType } from "@latticexyz/schema-type";
2
-
3
- export type RequireKeys<T extends Record<string, unknown>, P extends string> = T & Required<Pick<T, P>>;
4
-
5
- // This allows unions between string literals and `string` without sacrificing autocompletion.
6
- // Workaround for https://github.com/Microsoft/TypeScript/issues/29729
7
- export type StringForUnion = string & Record<never, never>;
8
-
9
- export type StaticArray = `${StaticAbiType}[${number}]`;
10
- // static arrays and inferred enum names get mixed together - this helper separates them
11
- export type ExtractUserTypes<UnknownTypes extends StringForUnion> = Exclude<UnknownTypes, AbiType | StaticArray>;
12
-
13
- // When type inference sees multiple uses of 1 generic, it can only guess
14
- // which of those are supposed to define the generic (and it will be wrong in complex situations).
15
- // This helper explicitly makes a type that's dependent on some generic,
16
- // and will not be inferred as the generic's definition.
17
- export type AsDependent<T> = T extends infer P ? P : never;