ic-mops 2.10.0 → 2.12.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 (51) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/bundle/cli.tgz +0 -0
  3. package/cli.ts +24 -0
  4. package/commands/build.ts +10 -1
  5. package/commands/check-stable.ts +52 -21
  6. package/commands/check.ts +65 -52
  7. package/commands/init.ts +17 -4
  8. package/commands/migrate.ts +165 -0
  9. package/declarations/main/main.did +38 -0
  10. package/declarations/main/main.did.d.ts +36 -0
  11. package/declarations/main/main.did.js +36 -0
  12. package/dist/cli.js +18 -0
  13. package/dist/commands/build.js +5 -1
  14. package/dist/commands/check-stable.d.ts +1 -0
  15. package/dist/commands/check-stable.js +39 -21
  16. package/dist/commands/check.js +51 -42
  17. package/dist/commands/init.js +11 -4
  18. package/dist/commands/migrate.d.ts +2 -0
  19. package/dist/commands/migrate.js +104 -0
  20. package/dist/declarations/main/main.did +38 -0
  21. package/dist/declarations/main/main.did.d.ts +36 -0
  22. package/dist/declarations/main/main.did.js +36 -0
  23. package/dist/helpers/migrations.d.ts +10 -0
  24. package/dist/helpers/migrations.js +125 -0
  25. package/dist/helpers/resolve-canisters.d.ts +1 -1
  26. package/dist/helpers/resolve-canisters.js +15 -1
  27. package/dist/package.json +1 -1
  28. package/dist/tests/migrate.test.d.ts +1 -0
  29. package/dist/tests/migrate.test.js +181 -0
  30. package/dist/types.d.ts +7 -0
  31. package/helpers/migrations.ts +190 -0
  32. package/helpers/resolve-canisters.ts +17 -0
  33. package/package.json +1 -1
  34. package/tests/__snapshots__/migrate.test.ts.snap +119 -0
  35. package/tests/migrate/basic/deployed.most +12 -0
  36. package/tests/migrate/basic/migrations/20250101_000000_Init.mo +5 -0
  37. package/tests/migrate/basic/migrations/20250201_000000_AddName.mo +5 -0
  38. package/tests/migrate/basic/migrations/20250301_000000_AddEmail.mo +9 -0
  39. package/tests/migrate/basic/mops.toml +15 -0
  40. package/tests/migrate/basic/next-migration/.gitkeep +0 -0
  41. package/tests/migrate/basic/src/main.mo +11 -0
  42. package/tests/migrate/with-next/deployed.most +12 -0
  43. package/tests/migrate/with-next/migrations/20250101_000000_Init.mo +7 -0
  44. package/tests/migrate/with-next/migrations/20250201_000000_AddName.mo +7 -0
  45. package/tests/migrate/with-next/migrations/20250301_000000_AddEmail.mo +7 -0
  46. package/tests/migrate/with-next/mops.toml +15 -0
  47. package/tests/migrate/with-next/next-migration/20250401_000000_RenameId.mo +7 -0
  48. package/tests/migrate/with-next/src/main.mo +11 -0
  49. package/tests/migrate/with-next/types/State.mo +7 -0
  50. package/tests/migrate.test.ts +255 -0
  51. package/types.ts +8 -0
@@ -0,0 +1,119 @@
1
+ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
2
+
3
+ exports[`migrate build build produces .most with full migration chain 1`] = `
4
+ "// Version: 4.0.0
5
+ {
6
+ "20250101_000000_Init" : {} -> {a : Nat};
7
+ "20250201_000000_AddName" : (old : {a : Nat}) -> {a : Nat; name : Text};
8
+ "20250301_000000_AddEmail" :
9
+ (old : {a : Nat; name : Text}) -> {a : Nat; email : Text; name : Text}
10
+ }
11
+ actor {
12
+ stable a : Nat;
13
+ stable email : Text;
14
+ stable name : Text
15
+ };
16
+ "
17
+ `;
18
+
19
+ exports[`migrate build build with build-limit produces trimmed .most 1`] = `
20
+ "// Version: 4.0.0
21
+ {
22
+ "20250201_000000_AddName" : (old : {a : Nat}) -> {a : Nat; name : Text};
23
+ "20250301_000000_AddEmail" :
24
+ (old : {a : Nat; name : Text}) -> {a : Nat; email : Text; name : Text}
25
+ }
26
+ actor {
27
+ stable a : Nat;
28
+ stable email : Text;
29
+ stable name : Text
30
+ };
31
+ "
32
+ `;
33
+
34
+ exports[`migrate build build-limit counts next migration as part of the chain 1`] = `
35
+ "// Version: 4.0.0
36
+ type V2__933402648 = {a : Nat; name : Text};
37
+ type V3__40989327 = {a : Nat; email : Text; name : Text};
38
+ type V4__364034734 = {email : Text; id : Nat; name : Text};
39
+ {
40
+ "20250301_000000_AddEmail" : (old : V2__933402648) -> V3__40989327;
41
+ "20250401_000000_RenameId" : (old : V3__40989327) -> V4__364034734
42
+ }
43
+ actor {
44
+ stable email : Text;
45
+ stable id : Nat;
46
+ stable name : Text
47
+ };
48
+ "
49
+ `;
50
+
51
+ exports[`migrate check check fails without next migration, passes with it 1`] = `
52
+ {
53
+ "exitCode": 1,
54
+ "stderr": "src/main.mo:3.1-11.2: Compatibility error [M0169], the stable variable \`a\` of version \`20250301_000000_AddEmail\` cannot be implicitly discarded. The variable can only be dropped by an explicit migration function, please see https://internetcomputer.org/docs/motoko/fundamentals/actors/compatibility#explicit-migration-using-a-migration-function
55
+ migrations/20250101_000000_Init.mo:0.1: warning [M0254], initial actor requires field \`id\` of type
56
+ Nat
57
+ ✗ Check failed for canister backend (exit code: 1)",
58
+ "stdout": "",
59
+ }
60
+ `;
61
+
62
+ exports[`migrate check check fails without next migration, passes with it 2`] = `
63
+ {
64
+ "exitCode": 0,
65
+ "stderr": "",
66
+ "stdout": "✓ backend
67
+ ✓ Stable compatibility check passed for canister 'backend'",
68
+ }
69
+ `;
70
+
71
+ exports[`migrate check check with trimming shows reduced chain 1`] = `
72
+ {
73
+ "exitCode": 0,
74
+ "stderr": "",
75
+ "stdout": "check Using --all-libs for richer diagnostics
76
+ migrations Prepared 2 migration(s) for backend (trimmed from 3)
77
+ check Checking canister backend:
78
+ <CACHE>moc-wrapper ["src/main.mo","--check","--all-libs","--default-persistent-actors","--enhanced-migration=.migrations-backend","-A=M0254"]
79
+ ✓ backend
80
+ check-stable Generating stable types for src/main.mo
81
+ <CACHE>moc-wrapper ["--stable-types","-o",".mops/.check-stable/new.wasm","src/main.mo","--default-persistent-actors","--enhanced-migration=.migrations-backend","-A=M0254"]
82
+ check-stable Comparing deployed.most ↔ .mops/.check-stable/new.most
83
+ <CACHE>moc-wrapper ["--stable-compatible","deployed.most",".mops/.check-stable/new.most"]
84
+ ✓ Stable compatibility check passed for canister 'backend'",
85
+ }
86
+ `;
87
+
88
+ exports[`migrate migrate freeze moves the file from next to chain 1`] = `
89
+ {
90
+ "exitCode": 0,
91
+ "stderr": "",
92
+ "stdout": "✓ Frozen migration: 20250401_000000_RenameId.mo → migrations/",
93
+ }
94
+ `;
95
+
96
+ exports[`migrate migrate new creates a migration file with timestamp and template 1`] = `
97
+ {
98
+ "exitCode": 0,
99
+ "stderr": "",
100
+ "stdout": "✓ Created migration: next-migration/<TIMESTAMP>_AddPhone.mo",
101
+ "template": "module {
102
+ public func migration(old : {}) : {} {
103
+ {}
104
+ }
105
+ }
106
+ ",
107
+ }
108
+ `;
109
+
110
+ exports[`migrate stable check hint stable check fails with hint when deployed.most is incompatible 1`] = `
111
+ {
112
+ "exitCode": 1,
113
+ "stderr": "(unknown location): Compatibility error [M0169], the stable variable \`a\` of the previous version cannot be implicitly discarded. The variable can only be dropped by an explicit migration function, please see https://internetcomputer.org/docs/motoko/fundamentals/actors/compatibility#explicit-migration-using-a-migration-function
114
+ (unknown location): Compatibility error [M0169], the stable variable \`name\` of the previous version cannot be implicitly discarded. The variable can only be dropped by an explicit migration function, please see https://internetcomputer.org/docs/motoko/fundamentals/actors/compatibility#explicit-migration-using-a-migration-function
115
+ Hint: You may need a migration. Run \`mops migrate new <Name>\` to create one.
116
+ ✗ Stable compatibility check failed for canister 'backend'",
117
+ "stdout": "✓ backend",
118
+ }
119
+ `;
@@ -0,0 +1,12 @@
1
+ // Version: 4.0.0
2
+ {
3
+ "20250101_000000_Init" : {} -> {a : Nat};
4
+ "20250201_000000_AddName" : (old : {a : Nat}) -> {a : Nat; name : Text};
5
+ "20250301_000000_AddEmail" :
6
+ (old : {a : Nat; name : Text}) -> {a : Nat; email : Text; name : Text}
7
+ }
8
+ actor {
9
+ stable a : Nat;
10
+ stable email : Text;
11
+ stable name : Text
12
+ };
@@ -0,0 +1,5 @@
1
+ module {
2
+ public func migration(_ : {}) : { a : Nat } {
3
+ { a = 0 };
4
+ };
5
+ };
@@ -0,0 +1,5 @@
1
+ module {
2
+ public func migration(old : { a : Nat }) : { a : Nat; name : Text } {
3
+ { old with name = "" };
4
+ };
5
+ };
@@ -0,0 +1,9 @@
1
+ module {
2
+ public func migration(old : { a : Nat; name : Text }) : {
3
+ a : Nat;
4
+ name : Text;
5
+ email : Text;
6
+ } {
7
+ { old with email = "" };
8
+ };
9
+ };
@@ -0,0 +1,15 @@
1
+ [toolchain]
2
+ moc = "1.5.0"
3
+
4
+ [moc]
5
+ args = ["--default-persistent-actors"]
6
+
7
+ [canisters.backend]
8
+ main = "src/main.mo"
9
+
10
+ [canisters.backend.migrations]
11
+ chain = "migrations"
12
+ next = "next-migration"
13
+
14
+ [canisters.backend.check-stable]
15
+ path = "deployed.most"
File without changes
@@ -0,0 +1,11 @@
1
+ import Prim "mo:prim";
2
+
3
+ actor {
4
+ let a : Nat;
5
+ let name : Text;
6
+ let email : Text;
7
+
8
+ public func check() : async () {
9
+ Prim.debugPrint(debug_show { a; name; email });
10
+ };
11
+ };
@@ -0,0 +1,12 @@
1
+ // Version: 4.0.0
2
+ {
3
+ "20250101_000000_Init" : {} -> {a : Nat};
4
+ "20250201_000000_AddName" : (old : {a : Nat}) -> {a : Nat; name : Text};
5
+ "20250301_000000_AddEmail" :
6
+ (old : {a : Nat; name : Text}) -> {a : Nat; email : Text; name : Text}
7
+ }
8
+ actor {
9
+ stable a : Nat;
10
+ stable email : Text;
11
+ stable name : Text
12
+ };
@@ -0,0 +1,7 @@
1
+ import State "../types/State";
2
+
3
+ module {
4
+ public func migration(_ : State.V0) : State.V1 {
5
+ { a = 0 };
6
+ };
7
+ };
@@ -0,0 +1,7 @@
1
+ import State "../types/State";
2
+
3
+ module {
4
+ public func migration(old : State.V1) : State.V2 {
5
+ { old with name = "" };
6
+ };
7
+ };
@@ -0,0 +1,7 @@
1
+ import State "../types/State";
2
+
3
+ module {
4
+ public func migration(old : State.V2) : State.V3 {
5
+ { old with email = "" };
6
+ };
7
+ };
@@ -0,0 +1,15 @@
1
+ [toolchain]
2
+ moc = "1.5.0"
3
+
4
+ [moc]
5
+ args = ["--default-persistent-actors"]
6
+
7
+ [canisters.backend]
8
+ main = "src/main.mo"
9
+
10
+ [canisters.backend.migrations]
11
+ chain = "migrations"
12
+ next = "next-migration"
13
+
14
+ [canisters.backend.check-stable]
15
+ path = "deployed.most"
@@ -0,0 +1,7 @@
1
+ import State "../types/State";
2
+
3
+ module {
4
+ public func migration(old : State.V3) : State.V4 {
5
+ { id = old.a; name = old.name; email = old.email };
6
+ };
7
+ };
@@ -0,0 +1,11 @@
1
+ import Prim "mo:prim";
2
+
3
+ actor {
4
+ let id : Nat;
5
+ let name : Text;
6
+ let email : Text;
7
+
8
+ public func check() : async () {
9
+ Prim.debugPrint(debug_show { id; name; email });
10
+ };
11
+ };
@@ -0,0 +1,7 @@
1
+ module {
2
+ public type V0 = {};
3
+ public type V1 = { a : Nat };
4
+ public type V2 = { a : Nat; name : Text };
5
+ public type V3 = { a : Nat; name : Text; email : Text };
6
+ public type V4 = { id : Nat; name : Text; email : Text };
7
+ };
@@ -0,0 +1,255 @@
1
+ import { describe, expect, test, afterEach } from "@jest/globals";
2
+ import { readdirSync, readFileSync } from "node:fs";
3
+ import { cp, rm, writeFile } from "node:fs/promises";
4
+ import path from "path";
5
+ import { cli, cliSnapshot, normalizePaths } from "./helpers";
6
+
7
+ const normalizeTimestamp = (text: string) =>
8
+ text.replace(/\d{8}_\d{6}/g, "<TIMESTAMP>");
9
+
10
+ const fixturesDir = path.join(import.meta.dirname, "migrate");
11
+
12
+ describe("migrate", () => {
13
+ const tempDirs: string[] = [];
14
+
15
+ async function makeTempFixture(fixture: string): Promise<string> {
16
+ const src = path.join(fixturesDir, fixture);
17
+ const dest = path.join(fixturesDir, `_tmp_${fixture}_${Date.now()}`);
18
+ await cp(src, dest, { recursive: true });
19
+ tempDirs.push(dest);
20
+ return dest;
21
+ }
22
+
23
+ async function patchMigrations(cwd: string, extra: string): Promise<void> {
24
+ const tomlPath = path.join(cwd, "mops.toml");
25
+ const toml = readFileSync(tomlPath, "utf-8");
26
+ await writeFile(
27
+ tomlPath,
28
+ toml.replace(
29
+ 'next = "next-migration"',
30
+ `next = "next-migration"\n${extra}`,
31
+ ),
32
+ );
33
+ }
34
+
35
+ afterEach(async () => {
36
+ for (const dir of tempDirs) {
37
+ await rm(dir, { recursive: true, force: true });
38
+ }
39
+ tempDirs.length = 0;
40
+ });
41
+
42
+ describe("migrate new", () => {
43
+ test("creates a migration file with timestamp and template", async () => {
44
+ const cwd = await makeTempFixture("basic");
45
+ const result = await cli(["migrate", "new", "AddPhone"], { cwd });
46
+ expect(result.exitCode).toBe(0);
47
+
48
+ const nextDir = path.join(cwd, "next-migration");
49
+ const files = readdirSync(nextDir).filter((f) => f.endsWith(".mo"));
50
+ expect(files).toHaveLength(1);
51
+ expect(files[0]).toMatch(/^\d{8}_\d{6}_AddPhone\.mo$/);
52
+
53
+ const content = readFileSync(path.join(nextDir, files[0]!), "utf-8");
54
+
55
+ expect({
56
+ exitCode: result.exitCode,
57
+ stdout: normalizeTimestamp(normalizePaths(result.stdout)),
58
+ stderr: normalizePaths(result.stderr),
59
+ template: content,
60
+ }).toMatchSnapshot();
61
+ });
62
+
63
+ test("errors when next already has a file", async () => {
64
+ const cwd = await makeTempFixture("basic");
65
+ await cli(["migrate", "new", "First"], { cwd });
66
+ const result = await cli(["migrate", "new", "Second"], { cwd });
67
+ expect(result.exitCode).toBe(1);
68
+ expect(result.stderr).toMatch(/next migration already exists/i);
69
+ });
70
+
71
+ test("errors on invalid migration name", async () => {
72
+ const cwd = await makeTempFixture("basic");
73
+ for (const name of ["../evil", "has space", "123start", "foo/bar"]) {
74
+ const result = await cli(["migrate", "new", name], { cwd });
75
+ expect(result.exitCode).toBe(1);
76
+ expect(result.stderr).toMatch(/invalid migration name/i);
77
+ }
78
+ });
79
+
80
+ test("errors when [migrations] not configured", async () => {
81
+ const cwd = await makeTempFixture("basic");
82
+ const tomlPath = path.join(cwd, "mops.toml");
83
+ const toml = readFileSync(tomlPath, "utf-8");
84
+ await writeFile(
85
+ tomlPath,
86
+ toml.replace(/\[canisters\.backend\.migrations\][\s\S]*$/, ""),
87
+ );
88
+ const result = await cli(["migrate", "new", "Test"], { cwd });
89
+ expect(result.exitCode).toBe(1);
90
+ expect(result.stderr).toMatch(/\[migrations\]/i);
91
+ });
92
+ });
93
+
94
+ describe("migrate freeze", () => {
95
+ test("moves the file from next to chain", async () => {
96
+ const cwd = await makeTempFixture("with-next");
97
+ const result = await cli(["migrate", "freeze"], { cwd });
98
+ expect(result.exitCode).toBe(0);
99
+
100
+ const nextFiles = readdirSync(path.join(cwd, "next-migration")).filter(
101
+ (f) => f.endsWith(".mo"),
102
+ );
103
+ expect(nextFiles).toHaveLength(0);
104
+
105
+ const chainFiles = readdirSync(path.join(cwd, "migrations")).filter((f) =>
106
+ f.endsWith(".mo"),
107
+ );
108
+ expect(chainFiles).toContain("20250401_000000_RenameId.mo");
109
+
110
+ expect({
111
+ exitCode: result.exitCode,
112
+ stdout: normalizePaths(result.stdout),
113
+ stderr: normalizePaths(result.stderr),
114
+ }).toMatchSnapshot();
115
+ });
116
+
117
+ test("errors when next is empty", async () => {
118
+ const cwd = await makeTempFixture("basic");
119
+ const result = await cli(["migrate", "freeze"], { cwd });
120
+ expect(result.exitCode).toBe(1);
121
+ expect(result.stderr).toMatch(/no next migration/i);
122
+ });
123
+
124
+ test("errors when next-migration does not sort last", async () => {
125
+ const cwd = await makeTempFixture("basic");
126
+ await writeFile(
127
+ path.join(cwd, "next-migration", "00000000_000000_Early.mo"),
128
+ "module {\n public func migration(_ : {}) : {} {\n {}\n }\n}\n",
129
+ );
130
+ const result = await cli(["migrate", "freeze"], { cwd });
131
+ expect(result.exitCode).toBe(1);
132
+ expect(result.stderr).toMatch(/must sort after/i);
133
+ });
134
+ });
135
+
136
+ describe("check", () => {
137
+ test("check fails without next migration, passes with it", async () => {
138
+ const cwd = await makeTempFixture("with-next");
139
+ const nextFile = readdirSync(path.join(cwd, "next-migration")).find((f) =>
140
+ f.endsWith(".mo"),
141
+ )!;
142
+ const nextPath = path.join(cwd, "next-migration", nextFile);
143
+ const nextContent = readFileSync(nextPath, "utf-8");
144
+ await rm(nextPath);
145
+
146
+ await cliSnapshot(["check"], { cwd }, 1);
147
+
148
+ await writeFile(nextPath, nextContent);
149
+ await cliSnapshot(["check"], { cwd }, 0);
150
+ });
151
+
152
+ test("check with trimming shows reduced chain", async () => {
153
+ const cwd = await makeTempFixture("basic");
154
+ await patchMigrations(cwd, "check-limit = 2");
155
+ await cliSnapshot(["check", "--verbose"], { cwd }, 0);
156
+ });
157
+ });
158
+
159
+ describe("build", () => {
160
+ test("build produces .most with full migration chain", async () => {
161
+ const cwd = await makeTempFixture("basic");
162
+ const result = await cli(["build"], { cwd });
163
+ expect(result.exitCode).toBe(0);
164
+
165
+ const most = readFileSync(
166
+ path.join(cwd, ".mops", ".build", "backend.most"),
167
+ "utf-8",
168
+ );
169
+ expect(most).toMatchSnapshot();
170
+ });
171
+
172
+ test("build with build-limit produces trimmed .most", async () => {
173
+ const cwd = await makeTempFixture("basic");
174
+ await patchMigrations(cwd, "build-limit = 2");
175
+ const result = await cli(["build"], { cwd });
176
+ expect(result.exitCode).toBe(0);
177
+
178
+ const most = readFileSync(
179
+ path.join(cwd, ".mops", ".build", "backend.most"),
180
+ "utf-8",
181
+ );
182
+ expect(most).toMatchSnapshot();
183
+ });
184
+
185
+ test("build-limit counts next migration as part of the chain", async () => {
186
+ const cwd = await makeTempFixture("with-next");
187
+ await patchMigrations(cwd, "build-limit = 2");
188
+ const result = await cli(["build"], { cwd });
189
+ expect(result.exitCode).toBe(0);
190
+
191
+ const most = readFileSync(
192
+ path.join(cwd, ".mops", ".build", "backend.most"),
193
+ "utf-8",
194
+ );
195
+ expect(most).toMatchSnapshot();
196
+ });
197
+ });
198
+
199
+ describe("stable check hint", () => {
200
+ test("stable check fails with hint when deployed.most is incompatible", async () => {
201
+ const cwd = await makeTempFixture("basic");
202
+ await writeFile(
203
+ path.join(cwd, "deployed.most"),
204
+ "// Version: 1.0.0\nactor {\n stable var a : Nat;\n stable var name : Int\n};\n",
205
+ );
206
+ await cliSnapshot(["check"], { cwd }, 1);
207
+ });
208
+ });
209
+
210
+ describe("sibling validation", () => {
211
+ async function patchNextDir(cwd: string, nextValue: string): Promise<void> {
212
+ const tomlPath = path.join(cwd, "mops.toml");
213
+ const toml = readFileSync(tomlPath, "utf-8");
214
+ await writeFile(
215
+ tomlPath,
216
+ toml.replace('next = "next-migration"', `next = "${nextValue}"`),
217
+ );
218
+ }
219
+
220
+ test("errors from `mops check` when chain and next have different parents", async () => {
221
+ const cwd = await makeTempFixture("with-next");
222
+ await patchNextDir(cwd, "other/next-migration");
223
+ const result = await cli(["check"], { cwd });
224
+ expect(result.exitCode).toBe(1);
225
+ expect(result.stderr).toMatch(/same parent directory/i);
226
+ });
227
+
228
+ test("errors from `mops migrate new` too — validation runs in every entry point", async () => {
229
+ const cwd = await makeTempFixture("basic");
230
+ await patchNextDir(cwd, "other/next-migration");
231
+ const result = await cli(["migrate", "new", "Test"], { cwd });
232
+ expect(result.exitCode).toBe(1);
233
+ expect(result.stderr).toMatch(/same parent directory/i);
234
+ });
235
+ });
236
+
237
+ describe("conflict detection", () => {
238
+ test("errors when both [migrations] and --enhanced-migration in args", async () => {
239
+ const cwd = await makeTempFixture("basic");
240
+ const tomlPath = path.join(cwd, "mops.toml");
241
+ const toml = readFileSync(tomlPath, "utf-8");
242
+ await writeFile(
243
+ tomlPath,
244
+ toml.replace(
245
+ "[canisters.backend.migrations]",
246
+ 'args = ["--enhanced-migration=migrations"]\n\n[canisters.backend.migrations]',
247
+ ),
248
+ );
249
+ const result = await cli(["check"], { cwd });
250
+ expect(result.exitCode).toBe(1);
251
+ expect(result.stderr).toMatch(/--enhanced-migration/);
252
+ expect(result.stderr).toMatch(/managed automatically/i);
253
+ });
254
+ });
255
+ });
package/types.ts CHANGED
@@ -35,6 +35,13 @@ export type Config = {
35
35
  };
36
36
  };
37
37
 
38
+ export type MigrationsConfig = {
39
+ chain: string;
40
+ next?: string;
41
+ "check-limit"?: number;
42
+ "build-limit"?: number;
43
+ };
44
+
38
45
  export type CanisterConfig = {
39
46
  main?: string;
40
47
  args?: string[];
@@ -44,6 +51,7 @@ export type CanisterConfig = {
44
51
  path: string;
45
52
  skipIfMissing?: boolean;
46
53
  };
54
+ migrations?: MigrationsConfig;
47
55
  };
48
56
 
49
57
  export type Dependencies = Record<string, Dependency>;