ic-mops 0.45.3 → 1.0.0-pre.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 (151) hide show
  1. package/.eslintrc.json +0 -0
  2. package/.gitignore +0 -0
  3. package/CHANGELOG.md +15 -0
  4. package/README.md +1 -1
  5. package/api/actors.ts +25 -17
  6. package/api/downloadPackageFiles.ts +0 -0
  7. package/api/getHighestVersion.ts +0 -0
  8. package/api/index.ts +0 -0
  9. package/api/network.ts +0 -0
  10. package/api/resolveVersion.ts +0 -0
  11. package/cache.ts +20 -8
  12. package/cli.ts +28 -27
  13. package/commands/add.ts +4 -0
  14. package/commands/bench/bench-canister.mo +34 -8
  15. package/commands/bench/user-bench.mo +0 -0
  16. package/commands/bench-replica.ts +11 -6
  17. package/commands/bench.ts +31 -5
  18. package/commands/bump.ts +0 -0
  19. package/commands/docs.ts +0 -0
  20. package/commands/outdated.ts +0 -0
  21. package/commands/publish.ts +1 -1
  22. package/commands/replica.ts +237 -0
  23. package/commands/search.ts +0 -0
  24. package/commands/sources.ts +2 -3
  25. package/commands/sync.ts +0 -0
  26. package/commands/template.ts +0 -0
  27. package/commands/test/mmf1.ts +5 -5
  28. package/commands/test/reporters/compact-reporter.ts +2 -1
  29. package/commands/test/reporters/files-reporter.ts +3 -2
  30. package/commands/test/reporters/reporter.ts +2 -1
  31. package/commands/test/reporters/silent-reporter.ts +2 -1
  32. package/commands/test/reporters/verbose-reporter.ts +14 -4
  33. package/commands/test/test.ts +213 -74
  34. package/commands/test/utils.ts +0 -0
  35. package/commands/toolchain/moc.ts +0 -0
  36. package/commands/toolchain/pocket-ic.ts +0 -0
  37. package/commands/toolchain/toolchain-utils.ts +0 -0
  38. package/commands/toolchain/wasmtime.ts +0 -0
  39. package/commands/transfer-ownership.ts +0 -0
  40. package/commands/update.ts +0 -0
  41. package/commands/user.ts +71 -1
  42. package/declarations/bench/bench.did +8 -4
  43. package/declarations/bench/bench.did.d.ts +4 -0
  44. package/declarations/bench/bench.did.js +4 -0
  45. package/declarations/storage/index.d.ts +0 -0
  46. package/declarations/storage/index.js +0 -0
  47. package/declarations/storage/storage.did +0 -0
  48. package/declarations/storage/storage.did.d.ts +0 -0
  49. package/declarations/storage/storage.did.js +0 -0
  50. package/dist/api/actors.js +19 -13
  51. package/dist/cache.js +17 -8
  52. package/dist/cli.d.ts +1 -0
  53. package/dist/cli.js +24 -27
  54. package/dist/commands/add.js +3 -0
  55. package/dist/commands/bench/bench-canister.mo +34 -8
  56. package/dist/commands/bench/user-bench.mo +0 -0
  57. package/dist/commands/bench-replica.d.ts +2 -1
  58. package/dist/commands/bench-replica.js +10 -6
  59. package/dist/commands/bench.js +29 -5
  60. package/dist/commands/publish.js +1 -1
  61. package/dist/commands/replica.d.ts +59 -0
  62. package/dist/commands/replica.js +193 -0
  63. package/dist/commands/sources.d.ts +2 -2
  64. package/dist/commands/sources.js +2 -3
  65. package/dist/commands/test/mmf1.d.ts +2 -2
  66. package/dist/commands/test/mmf1.js +4 -4
  67. package/dist/commands/test/reporters/compact-reporter.d.ts +2 -1
  68. package/dist/commands/test/reporters/compact-reporter.js +1 -1
  69. package/dist/commands/test/reporters/files-reporter.d.ts +2 -1
  70. package/dist/commands/test/reporters/files-reporter.js +2 -2
  71. package/dist/commands/test/reporters/reporter.d.ts +2 -1
  72. package/dist/commands/test/reporters/silent-reporter.d.ts +2 -1
  73. package/dist/commands/test/reporters/silent-reporter.js +1 -1
  74. package/dist/commands/test/reporters/verbose-reporter.d.ts +3 -1
  75. package/dist/commands/test/reporters/verbose-reporter.js +12 -4
  76. package/dist/commands/test/test.d.ts +10 -8
  77. package/dist/commands/test/test.js +170 -71
  78. package/dist/commands/user.d.ts +6 -0
  79. package/dist/commands/user.js +59 -1
  80. package/dist/declarations/bench/bench.did +8 -4
  81. package/dist/declarations/bench/bench.did.d.ts +4 -0
  82. package/dist/declarations/bench/bench.did.js +4 -0
  83. package/dist/declarations/storage/index.d.ts +0 -0
  84. package/dist/declarations/storage/index.js +0 -0
  85. package/dist/declarations/storage/storage.did +0 -0
  86. package/dist/declarations/storage/storage.did.d.ts +0 -0
  87. package/dist/declarations/storage/storage.did.js +0 -0
  88. package/dist/mops.d.ts +1 -1
  89. package/dist/mops.js +4 -28
  90. package/dist/package.json +4 -3
  91. package/dist/resolve-packages.d.ts +2 -2
  92. package/dist/resolve-packages.js +29 -7
  93. package/dist/templates/README.md +0 -0
  94. package/dist/templates/licenses/Apache-2.0 +0 -0
  95. package/dist/templates/licenses/Apache-2.0-NOTICE +0 -0
  96. package/dist/templates/licenses/MIT +0 -0
  97. package/dist/templates/mops-publish.yml +0 -0
  98. package/dist/templates/mops-test.yml +0 -0
  99. package/dist/templates/src/lib.mo +0 -0
  100. package/dist/templates/test/lib.test.mo +0 -0
  101. package/dist/types.d.ts +1 -0
  102. package/global.d.ts +0 -0
  103. package/helpers/get-dfx-version.ts +0 -0
  104. package/helpers/get-moc-path.ts +0 -0
  105. package/helpers/get-moc-version.ts +0 -0
  106. package/mops.ts +4 -31
  107. package/package.json +4 -3
  108. package/parallel.ts +0 -0
  109. package/resolve-packages.ts +39 -8
  110. package/templates/README.md +0 -0
  111. package/templates/licenses/Apache-2.0 +0 -0
  112. package/templates/licenses/Apache-2.0-NOTICE +0 -0
  113. package/templates/licenses/MIT +0 -0
  114. package/templates/mops-publish.yml +0 -0
  115. package/templates/mops-test.yml +0 -0
  116. package/templates/src/lib.mo +0 -0
  117. package/templates/test/lib.test.mo +0 -0
  118. package/types.ts +3 -1
  119. package/.DS_Store +0 -0
  120. package/bundle/bench/bench-canister.mo +0 -87
  121. package/bundle/bench/user-bench.mo +0 -14
  122. package/bundle/bin/moc-wrapper.sh +0 -40
  123. package/bundle/bin/mops.js +0 -3
  124. package/bundle/cli.js +0 -163
  125. package/bundle/cli.tgz +0 -0
  126. package/bundle/declarations/bench/bench.did +0 -26
  127. package/bundle/declarations/bench/bench.did.d.ts +0 -29
  128. package/bundle/declarations/bench/bench.did.js +0 -26
  129. package/bundle/declarations/bench/index.d.ts +0 -50
  130. package/bundle/declarations/bench/index.js +0 -40
  131. package/bundle/declarations/main/index.d.ts +0 -50
  132. package/bundle/declarations/main/index.js +0 -40
  133. package/bundle/declarations/main/main.did +0 -438
  134. package/bundle/declarations/main/main.did.d.ts +0 -360
  135. package/bundle/declarations/main/main.did.js +0 -412
  136. package/bundle/declarations/storage/index.d.ts +0 -50
  137. package/bundle/declarations/storage/index.js +0 -30
  138. package/bundle/declarations/storage/storage.did +0 -46
  139. package/bundle/declarations/storage/storage.did.d.ts +0 -40
  140. package/bundle/declarations/storage/storage.did.js +0 -38
  141. package/bundle/package.json +0 -31
  142. package/bundle/templates/README.md +0 -13
  143. package/bundle/templates/licenses/Apache-2.0 +0 -202
  144. package/bundle/templates/licenses/Apache-2.0-NOTICE +0 -13
  145. package/bundle/templates/licenses/MIT +0 -21
  146. package/bundle/templates/mops-publish.yml +0 -17
  147. package/bundle/templates/mops-test.yml +0 -22
  148. package/bundle/templates/src/lib.mo +0 -15
  149. package/bundle/templates/test/lib.test.mo +0 -4
  150. package/commands/import-identity.ts +0 -62
  151. package/commands/whoami.ts +0 -12
@@ -3,6 +3,7 @@ import {spawn, ChildProcessWithoutNullStreams} from 'node:child_process';
3
3
  import path from 'node:path';
4
4
  import fs from 'node:fs';
5
5
  import os from 'node:os';
6
+
6
7
  import chalk from 'chalk';
7
8
  import {globSync} from 'glob';
8
9
  import chokidar from 'chokidar';
@@ -20,6 +21,10 @@ import {FilesReporter} from './reporters/files-reporter.js';
20
21
  import {CompactReporter} from './reporters/compact-reporter.js';
21
22
  import {SilentReporter} from './reporters/silent-reporter.js';
22
23
  import {toolchain} from '../toolchain/index.js';
24
+ import {Replica} from '../replica.js';
25
+ import {ActorMethod} from '@dfinity/agent';
26
+ import {PassThrough, Readable} from 'node:stream';
27
+ import {TestMode} from '../../types.js';
23
28
 
24
29
  let ignore = [
25
30
  '**/node_modules/**',
@@ -34,18 +39,60 @@ let globConfig = {
34
39
  };
35
40
 
36
41
  type ReporterName = 'verbose' | 'files' | 'compact' | 'silent';
37
- type TestMode = 'interpreter' | 'wasi';
42
+ type ReplicaName = 'dfx' | 'pocket-ic';
43
+
44
+ type TestOptions = {
45
+ watch : boolean;
46
+ reporter : ReporterName;
47
+ mode : TestMode;
48
+ replica : ReplicaName;
49
+ };
50
+
51
+
52
+ let replica = new Replica();
53
+ let replicaStartPromise : Promise<void> | undefined;
54
+
55
+ async function startReplicaOnce(replica : Replica, type : ReplicaName) {
56
+ if (!replicaStartPromise) {
57
+ replicaStartPromise = new Promise((resolve) => {
58
+ replica.start({type, silent: true}).then(resolve);
59
+ });
60
+ }
61
+ return replicaStartPromise;
62
+ }
38
63
 
39
- export async function test(filter = '', {watch = false, reporter = 'verbose' as ReporterName, mode = 'interpreter' as TestMode} = {}) {
64
+ export async function test(filter = '', options : Partial<TestOptions> = {}) {
65
+ let config = readConfig();
40
66
  let rootDir = getRootDir();
41
67
 
42
- if (watch) {
68
+ let replicaType = options.replica ?? (config.toolchain?.['pocket-ic'] ? 'pocket-ic' : 'dfx' as ReplicaName);
69
+ replica.type = replicaType;
70
+
71
+ if (options.watch) {
72
+ replica.ttl = 60 * 15; // 15 minutes
73
+
74
+ let sigint = false;
75
+ process.on('SIGINT', () => {
76
+ if (sigint) {
77
+ console.log('Force exit');
78
+ process.exit(0);
79
+ }
80
+ sigint = true;
81
+
82
+ if (replicaStartPromise) {
83
+ console.log('Stopping replica...');
84
+ replica.stop(true).then(() => {
85
+ process.exit(0);
86
+ });
87
+ }
88
+ });
89
+
43
90
  // todo: run only changed for *.test.mo?
44
91
  // todo: run all for *.mo?
45
92
  let run = debounce(async () => {
46
93
  console.clear();
47
94
  process.stdout.write('\x1Bc');
48
- await runAll(reporter, filter, mode);
95
+ await runAll(options.reporter, filter, options.mode, replicaType, true);
49
96
  console.log('-'.repeat(50));
50
97
  console.log('Waiting for file changes...');
51
98
  console.log(chalk.gray((`Press ${chalk.gray('Ctrl+C')} to exit.`)));
@@ -65,7 +112,7 @@ export async function test(filter = '', {watch = false, reporter = 'verbose' as
65
112
  run();
66
113
  }
67
114
  else {
68
- let passed = await runAll(reporter, filter, mode);
115
+ let passed = await runAll(options.reporter, filter, options.mode, replicaType);
69
116
  if (!passed) {
70
117
  process.exit(1);
71
118
  }
@@ -75,25 +122,12 @@ export async function test(filter = '', {watch = false, reporter = 'verbose' as
75
122
  let mocPath = '';
76
123
  let wasmtimePath = '';
77
124
 
78
- export async function runAll(reporterName : ReporterName = 'verbose', filter = '', mode : TestMode = 'interpreter') : Promise<boolean> {
79
- let reporter : Reporter;
80
- if (reporterName == 'compact') {
81
- reporter = new CompactReporter;
82
- }
83
- else if (reporterName == 'files') {
84
- reporter = new FilesReporter;
85
- }
86
- else if (reporterName == 'silent') {
87
- reporter = new SilentReporter;
88
- }
89
- else {
90
- reporter = new VerboseReporter;
91
- }
92
- let done = await testWithReporter(reporter, filter, mode);
125
+ async function runAll(reporterName : ReporterName | undefined, filter = '', mode : TestMode = 'interpreter', replicaType : ReplicaName, watch = false) : Promise<boolean> {
126
+ let done = await testWithReporter(reporterName, filter, mode, replicaType, watch);
93
127
  return done;
94
128
  }
95
129
 
96
- export async function testWithReporter(reporter : Reporter, filter = '', mode : TestMode = 'interpreter') : Promise<boolean> {
130
+ export async function testWithReporter(reporterName : ReporterName | Reporter | undefined, filter = '', defaultMode : TestMode = 'interpreter', replicaType : ReplicaName, watch = false) : Promise<boolean> {
97
131
  let rootDir = getRootDir();
98
132
  let files : string[] = [];
99
133
  let libFiles = globSync('**/test?(s)/lib.mo', globConfig);
@@ -117,6 +151,31 @@ export async function testWithReporter(reporter : Reporter, filter = '', mode :
117
151
  return false;
118
152
  }
119
153
 
154
+
155
+ let reporter : Reporter;
156
+
157
+ if (!reporterName || typeof reporterName === 'string') {
158
+ if (!reporterName) {
159
+ reporterName = files.length > 1 ? 'files' : 'verbose';
160
+ }
161
+
162
+ if (reporterName == 'compact') {
163
+ reporter = new CompactReporter;
164
+ }
165
+ else if (reporterName == 'files') {
166
+ reporter = new FilesReporter;
167
+ }
168
+ else if (reporterName == 'silent') {
169
+ reporter = new SilentReporter;
170
+ }
171
+ else {
172
+ reporter = new VerboseReporter;
173
+ }
174
+ }
175
+ else {
176
+ reporter = reporterName;
177
+ }
178
+
120
179
  reporter.addFiles(files);
121
180
 
122
181
  let config = readConfig();
@@ -126,14 +185,25 @@ export async function testWithReporter(reporter : Reporter, filter = '', mode :
126
185
  mocPath = await toolchain.bin('moc', {fallback: true});
127
186
  }
128
187
 
129
- let wasmDir = `${getRootDir()}/.mops/.test/`;
130
- fs.mkdirSync(wasmDir, {recursive: true});
188
+ let testTempDir = path.join(getRootDir(), '.mops/.test/');
189
+ replica.dir = testTempDir;
190
+
191
+ fs.mkdirSync(testTempDir, {recursive: true});
131
192
 
132
193
  await parallel(os.cpus().length, files, async (file : string) => {
133
194
  let mmf = new MMF1('store', absToRel(file));
134
- let wasiMode = mode === 'wasi' || fs.readFileSync(file, 'utf8').startsWith('// @testmode wasi');
135
195
 
136
- if (wasiMode && !wasmtimePath) {
196
+ // mode overrides
197
+ let lines = fs.readFileSync(file, 'utf8').split('\n');
198
+ let mode = defaultMode;
199
+ if (lines.includes('// @testmode wasi')) {
200
+ mode = 'wasi';
201
+ }
202
+ else if (lines.includes('actor {') || lines.includes('// @testmode replica')) {
203
+ mode = 'replica';
204
+ }
205
+
206
+ if (mode === 'wasi' && !wasmtimePath) {
137
207
  // ensure wasmtime is installed or specified in config
138
208
  if (config.toolchain?.wasmtime) {
139
209
  wasmtimePath = await toolchain.bin('wasmtime');
@@ -149,9 +219,14 @@ export async function testWithReporter(reporter : Reporter, filter = '', mode :
149
219
  let promise = new Promise<void>((resolve) => {
150
220
  let mocArgs = ['--hide-warnings', '--error-detail=2', ...sourcesArr.join(' ').split(' '), file].filter(x => x);
151
221
 
222
+ // interpret
223
+ if (mode === 'interpreter') {
224
+ let proc = spawn(mocPath, ['-r', '-ref-system-api', ...mocArgs]);
225
+ pipeMMF(proc, mmf).then(resolve);
226
+ }
152
227
  // build and run wasm
153
- if (wasiMode) {
154
- let wasmFile = `${path.join(wasmDir, path.parse(file).name)}.wasm`;
228
+ else if (mode === 'wasi') {
229
+ let wasmFile = `${path.join(testTempDir, path.parse(file).name)}.wasm`;
155
230
 
156
231
  // build
157
232
  let buildProc = spawn(mocPath, [`-o=${wasmFile}`, '-wasi-system-api', ...mocArgs]);
@@ -172,6 +247,7 @@ export async function testWithReporter(reporter : Reporter, filter = '', mode :
172
247
  wasmFile,
173
248
  ];
174
249
  }
250
+ // backcompat
175
251
  else {
176
252
  wasmtimeArgs = [
177
253
  '--max-wasm-stack=4000000',
@@ -188,72 +264,135 @@ export async function testWithReporter(reporter : Reporter, filter = '', mode :
188
264
  fs.rmSync(wasmFile, {force: true});
189
265
  }).then(resolve);
190
266
  }
191
- // interpret
192
- else {
193
- let proc = spawn(mocPath, ['-r', '-ref-system-api', ...mocArgs]);
194
- pipeMMF(proc, mmf).then(resolve);
267
+ // build and execute in replica
268
+ else if (mode === 'replica') {
269
+ // mmf.strategy = 'print'; // because we run replica tests one-by-one
270
+
271
+ let wasmFile = `${path.join(testTempDir, path.parse(file).name)}.wasm`;
272
+
273
+ // build
274
+ let buildProc = spawn(mocPath, [`-o=${wasmFile}`, ...mocArgs]);
275
+
276
+ pipeMMF(buildProc, mmf).then(async () => {
277
+ if (mmf.failed > 0) {
278
+ return;
279
+ }
280
+
281
+ await startReplicaOnce(replica, replicaType);
282
+
283
+ let canisterName = path.parse(file).name;
284
+ let idlFactory = ({IDL} : any) => {
285
+ return IDL.Service({'runTests': IDL.Func([], [], [])});
286
+ };
287
+ interface _SERVICE {'runTests' : ActorMethod<[], undefined>;}
288
+
289
+ let {stream} = await replica.deploy(canisterName, wasmFile, idlFactory);
290
+
291
+ pipeStdoutToMMF(stream, mmf);
292
+
293
+ let actor = await replica.getActor(canisterName) as _SERVICE;
294
+
295
+ try {
296
+ if (globalThis.mopsReplicaTestRunning) {
297
+ await new Promise<void>((resolve) => {
298
+ let timerId = setInterval(() => {
299
+ if (!globalThis.mopsReplicaTestRunning) {
300
+ resolve();
301
+ clearInterval(timerId);
302
+ }
303
+ }, Math.random() * 1000 |0);
304
+ });
305
+ }
306
+
307
+ globalThis.mopsReplicaTestRunning = true;
308
+ await actor.runTests();
309
+ globalThis.mopsReplicaTestRunning = false;
310
+
311
+ mmf.pass();
312
+ }
313
+ catch (e : any) {
314
+ let stderrStream = new PassThrough();
315
+ pipeStderrToMMF(stderrStream, mmf, path.dirname(file));
316
+ stderrStream.write(e.message);
317
+ }
318
+ }).finally(async () => {
319
+ fs.rmSync(wasmFile, {force: true});
320
+ }).then(resolve);
195
321
  }
196
322
  });
197
323
 
198
- reporter.addRun(file, mmf, promise, wasiMode);
324
+ reporter.addRun(file, mmf, promise, mode);
199
325
 
200
326
  await promise;
201
327
  });
202
328
 
203
- fs.rmSync(wasmDir, {recursive: true, force: true});
329
+ if (replicaStartPromise && !watch) {
330
+ await replica.stop();
331
+ fs.rmSync(testTempDir, {recursive: true, force: true});
332
+ }
333
+
204
334
  return reporter.done();
205
335
  }
206
336
 
207
- function pipeMMF(proc : ChildProcessWithoutNullStreams, mmf : MMF1) {
208
- return new Promise<void>((resolve) => {
209
- // stdout
210
- proc.stdout.on('data', (data) => {
211
- for (let line of data.toString().split('\n')) {
212
- line = line.trim();
213
- if (line) {
214
- mmf.parseLine(line);
215
- }
337
+ function pipeStdoutToMMF(stdout : Readable, mmf : MMF1) {
338
+ stdout.on('data', (data) => {
339
+ for (let line of data.toString().split('\n')) {
340
+ line = line.trim();
341
+ if (line) {
342
+ mmf.parseLine(line);
216
343
  }
217
- });
344
+ }
345
+ });
346
+ }
347
+
348
+ function pipeStderrToMMF(stderr : Readable, mmf : MMF1, dir = '') {
349
+ stderr.on('data', (data) => {
350
+ let text : string = data.toString().trim();
351
+ let failedLine = '';
352
+
353
+ text = text.replace(/([\w+._/-]+):(\d+).(\d+)(-\d+.\d+)/g, (_m0, m1 : string, m2 : string, m3 : string) => {
354
+ // change absolute file path to relative
355
+ // change :line:col-line:col to :line:col to work in vscode
356
+ let res = `${absToRel(m1)}:${m2}:${m3}`;
357
+ let file = path.join(dir, m1);
218
358
 
219
- // stderr
220
- proc.stderr.on('data', (data) => {
221
- let text : string = data.toString().trim();
222
- let failedLine = '';
223
- text = text.replace(/([\w+._/-]+):(\d+).(\d+)(-\d+.\d+)/g, (_m0, m1 : string, m2 : string, m3 : string) => {
224
- // change absolute file path to relative
225
- // change :line:col-line:col to :line:col to work in vscode
226
- let res = `${absToRel(m1)}:${m2}:${m3}`;
227
-
228
- if (!fs.existsSync(m1)) {
229
- return res;
230
- }
231
-
232
- // show failed line
233
- let content = fs.readFileSync(m1);
234
- let lines = content.toString().split('\n') || [];
235
- failedLine += chalk.dim('\n ...');
236
- let lineBefore = lines[+m2 - 2];
237
- if (lineBefore) {
238
- failedLine += chalk.dim(`\n ${+m2 - 1}\t| ${lineBefore.replaceAll('\t', ' ')}`);
239
- }
240
- failedLine += `\n${chalk.redBright`->`} ${m2}\t| ${lines[+m2 - 1]?.replaceAll('\t', ' ')}`;
241
- if (lines.length > +m2) {
242
- failedLine += chalk.dim(`\n ${+m2 + 1}\t| ${lines[+m2]?.replaceAll('\t', ' ')}`);
243
- }
244
- failedLine += chalk.dim('\n ...');
359
+ if (!fs.existsSync(file)) {
245
360
  return res;
246
- });
247
- if (failedLine) {
248
- text += failedLine;
249
361
  }
250
- mmf.fail(text);
362
+
363
+ // show failed line
364
+ let content = fs.readFileSync(file);
365
+ let lines = content.toString().split('\n') || [];
366
+
367
+ failedLine += chalk.dim('\n ...');
368
+
369
+ let lineBefore = lines[+m2 - 2];
370
+ if (lineBefore) {
371
+ failedLine += chalk.dim(`\n ${+m2 - 1}\t| ${lineBefore.replaceAll('\t', ' ')}`);
372
+ }
373
+ failedLine += `\n${chalk.redBright`->`} ${m2}\t| ${lines[+m2 - 1]?.replaceAll('\t', ' ')}`;
374
+ if (lines.length > +m2) {
375
+ failedLine += chalk.dim(`\n ${+m2 + 1}\t| ${lines[+m2]?.replaceAll('\t', ' ')}`);
376
+ }
377
+ failedLine += chalk.dim('\n ...');
378
+ return res;
251
379
  });
380
+ if (failedLine) {
381
+ text += failedLine;
382
+ }
383
+ mmf.fail(text);
384
+ });
385
+ }
386
+
387
+ function pipeMMF(proc : ChildProcessWithoutNullStreams, mmf : MMF1) {
388
+ return new Promise<void>((resolve) => {
389
+ pipeStdoutToMMF(proc.stdout, mmf);
390
+ pipeStderrToMMF(proc.stderr, mmf);
252
391
 
253
392
  // exit
254
393
  proc.on('close', (code) => {
255
394
  if (code === 0) {
256
- mmf.pass();
395
+ mmf.strategy !== 'print' && mmf.pass();
257
396
  }
258
397
  else if (code !== 1) {
259
398
  mmf.fail(`unknown exit code: ${code}`);
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
package/commands/user.ts CHANGED
@@ -1,7 +1,13 @@
1
1
  import process from 'node:process';
2
2
  import chalk from 'chalk';
3
- import {getIdentity} from '../mops.js';
3
+ import fs from 'node:fs';
4
+ import path from 'node:path';
5
+ import {Buffer} from 'node:buffer';
6
+ import prompts from 'prompts';
7
+ import {deleteSync} from 'del';
4
8
  import {mainActor} from '../api/actors.js';
9
+ import {getIdentity, globalConfigDir} from '../mops.js';
10
+ import {encrypt} from '../pem.js';
5
11
 
6
12
  export async function getUserProp(prop : string) {
7
13
  let actor = await mainActor();
@@ -26,4 +32,68 @@ export async function setUserProp(prop : string, value : string) {
26
32
  else {
27
33
  console.log(chalk.red('Error: ') + res.err);
28
34
  }
35
+ }
36
+
37
+ export async function getPrincipal() {
38
+ let identity = await getIdentity();
39
+ if (identity) {
40
+ console.log(identity.getPrincipal().toText());
41
+ }
42
+ else {
43
+ console.log(chalk.red('Error: ') + 'identity not found. Run ' + chalk.greenBright('mops user import') + ' command.');
44
+ }
45
+ }
46
+
47
+ type ImportIdentityOptions = {
48
+ encrypt : boolean;
49
+ };
50
+
51
+ export async function importPem(data : string, options : ImportIdentityOptions = {encrypt: true}) {
52
+ try {
53
+ if (!fs.existsSync(globalConfigDir)) {
54
+ fs.mkdirSync(globalConfigDir);
55
+ }
56
+
57
+ let password = '';
58
+
59
+ if (options.encrypt) {
60
+ let res = await prompts({
61
+ type: 'invisible',
62
+ name: 'password',
63
+ message: 'Enter password to encrypt identity.pem',
64
+ });
65
+ password = res.password;
66
+
67
+ if (!password) {
68
+ let res = await prompts({
69
+ type: 'confirm',
70
+ name: 'ok',
71
+ message: 'Are you sure you don\'t want to protect your identity.pem with a password?',
72
+ });
73
+ if (!res.ok) {
74
+ console.log('aborted');
75
+ return;
76
+ }
77
+ }
78
+ }
79
+
80
+ let identityPem = path.resolve(globalConfigDir, 'identity.pem');
81
+ let identityPemEncrypted = path.resolve(globalConfigDir, 'identity.pem.encrypted');
82
+
83
+ deleteSync([identityPem, identityPemEncrypted], {force: true});
84
+
85
+ // encrypted
86
+ if (password) {
87
+ let encrypted = await encrypt(Buffer.from(data), password);
88
+ fs.writeFileSync(identityPemEncrypted, encrypted);
89
+ }
90
+ // unencrypted
91
+ else {
92
+ fs.writeFileSync(identityPem, data);
93
+ }
94
+ console.log(chalk.green('Success'));
95
+ }
96
+ catch (err) {
97
+ console.log(chalk.red('Error: ') + err);
98
+ }
29
99
  }
@@ -1,4 +1,4 @@
1
- type _anon_class_10_1 =
1
+ type _anon_class_10_1 =
2
2
  service {
3
3
  getSchema: () -> (BenchSchema) query;
4
4
  getStats: () -> (BenchResult) query;
@@ -7,20 +7,24 @@ type _anon_class_10_1 =
7
7
  runCellUpdate: (nat, nat) -> (BenchResult);
8
8
  runCellUpdateAwait: (nat, nat) -> (BenchResult);
9
9
  };
10
- type BenchSchema =
10
+ type BenchSchema =
11
11
  record {
12
12
  cols: vec text;
13
13
  description: text;
14
14
  name: text;
15
15
  rows: vec text;
16
16
  };
17
- type BenchResult =
17
+ type BenchResult =
18
18
  record {
19
19
  instructions: int;
20
- rts_collector_instructions: int;
21
20
  rts_heap_size: int;
21
+ stable_memory_size: int;
22
+ rts_stable_memory_size: int;
23
+ rts_collector_instructions: int;
22
24
  rts_memory_size: int;
23
25
  rts_mutator_instructions: int;
24
26
  rts_total_allocation: int;
27
+ rts_logical_stable_memory_size: int;
28
+ rts_reclaimed: int;
25
29
  };
26
30
  service : () -> _anon_class_10_1
@@ -5,10 +5,14 @@ import type { IDL } from '@dfinity/candid';
5
5
  export interface BenchResult {
6
6
  'instructions' : bigint,
7
7
  'rts_memory_size' : bigint,
8
+ 'stable_memory_size' : bigint,
9
+ 'rts_stable_memory_size' : bigint,
10
+ 'rts_logical_stable_memory_size' : bigint,
8
11
  'rts_total_allocation' : bigint,
9
12
  'rts_collector_instructions' : bigint,
10
13
  'rts_mutator_instructions' : bigint,
11
14
  'rts_heap_size' : bigint,
15
+ 'rts_reclaimed' : bigint,
12
16
  }
13
17
  export interface BenchSchema {
14
18
  'cols' : Array<string>,
@@ -7,11 +7,15 @@ export const idlFactory = ({ IDL }) => {
7
7
  });
8
8
  const BenchResult = IDL.Record({
9
9
  'instructions' : IDL.Int,
10
+ 'stable_memory_size' : IDL.Int,
11
+ 'rts_stable_memory_size' : IDL.Int,
12
+ 'rts_logical_stable_memory_size' : IDL.Int,
10
13
  'rts_memory_size' : IDL.Int,
11
14
  'rts_total_allocation' : IDL.Int,
12
15
  'rts_collector_instructions' : IDL.Int,
13
16
  'rts_mutator_instructions' : IDL.Int,
14
17
  'rts_heap_size' : IDL.Int,
18
+ 'rts_reclaimed' : IDL.Int,
15
19
  });
16
20
  const _anon_class_10_1 = IDL.Service({
17
21
  'getSchema' : IDL.Func([], [BenchSchema], ['query']),
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -3,28 +3,34 @@ import { idlFactory } from '../declarations/main/main.did.js';
3
3
  import { idlFactory as storageIdlFactory } from '../declarations/storage/storage.did.js';
4
4
  import { getEndpoint } from './network.js';
5
5
  import { getNetwork } from './network.js';
6
+ let agentPromiseByPrincipal = new Map();
7
+ let getAgent = async (identity) => {
8
+ let principal = identity ? identity?.getPrincipal().toText() : '';
9
+ let agentPromise = agentPromiseByPrincipal.get(principal);
10
+ if (!agentPromise) {
11
+ let network = getNetwork();
12
+ let host = getEndpoint(network).host;
13
+ agentPromise = HttpAgent.create({
14
+ host,
15
+ identity,
16
+ shouldFetchRootKey: network === 'local',
17
+ verifyQuerySignatures: process.env.MOPS_VERIFY_QUERY_SIGNATURES !== 'false',
18
+ });
19
+ agentPromiseByPrincipal.set(principal, agentPromise);
20
+ }
21
+ return agentPromise;
22
+ };
6
23
  export let mainActor = async (identity) => {
24
+ let agent = await getAgent(identity);
7
25
  let network = getNetwork();
8
- let host = getEndpoint(network).host;
9
26
  let canisterId = getEndpoint(network).canisterId;
10
- // @ts-ignore exactOptionalPropertyTypes
11
- let agent = new HttpAgent({ host, identity });
12
- if (network === 'local') {
13
- await agent.fetchRootKey();
14
- }
15
27
  return Actor.createActor(idlFactory, {
16
28
  agent,
17
29
  canisterId,
18
30
  });
19
31
  };
20
32
  export let storageActor = async (storageId, identity) => {
21
- let network = getNetwork();
22
- let host = getEndpoint(network).host;
23
- // @ts-ignore exactOptionalPropertyTypes
24
- let agent = new HttpAgent({ host, identity });
25
- if (network === 'local') {
26
- await agent.fetchRootKey();
27
- }
33
+ let agent = await getAgent(identity);
28
34
  return Actor.createActor(storageIdlFactory, {
29
35
  agent,
30
36
  canisterId: storageId,
package/dist/cache.js CHANGED
@@ -2,13 +2,17 @@ import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import ncp from 'ncp';
4
4
  import getFolderSize from 'get-folder-size';
5
- import { getDependencyType, globalCacheDir, parseGithubURL } from './mops.js';
5
+ import { getDependencyType, getNetwork, getRootDir, globalCacheDir, parseGithubURL } from './mops.js';
6
6
  import { getPackageId } from './helpers/get-package-id.js';
7
+ let getGlobalCacheDir = () => {
8
+ let network = getNetwork();
9
+ return path.join(globalCacheDir, network === 'ic' ? '' : network);
10
+ };
7
11
  export let show = () => {
8
- return globalCacheDir;
12
+ return getGlobalCacheDir();
9
13
  };
10
14
  export let getDepCacheDir = (cacheName) => {
11
- return path.join(globalCacheDir, 'packages', cacheName);
15
+ return path.join(getGlobalCacheDir(), 'packages', cacheName);
12
16
  };
13
17
  export let isDepCached = (cacheName) => {
14
18
  let dir = getDepCacheDir(cacheName);
@@ -26,7 +30,7 @@ export function getGithubDepCacheName(name, repo) {
26
30
  return `_github/${name}#${branch}` + (commitHash ? `@${commitHash}` : '');
27
31
  }
28
32
  export let addCache = (cacheName, source) => {
29
- let dest = path.join(globalCacheDir, 'packages', cacheName);
33
+ let dest = path.join(getGlobalCacheDir(), 'packages', cacheName);
30
34
  fs.mkdirSync(dest, { recursive: true });
31
35
  return new Promise((resolve, reject) => {
32
36
  ncp.ncp(source, dest, { stopOnErr: true }, (err) => {
@@ -38,7 +42,7 @@ export let addCache = (cacheName, source) => {
38
42
  });
39
43
  };
40
44
  export let copyCache = (cacheName, dest) => {
41
- let source = path.join(globalCacheDir, 'packages', cacheName);
45
+ let source = path.join(getGlobalCacheDir(), 'packages', cacheName);
42
46
  fs.mkdirSync(dest, { recursive: true });
43
47
  return new Promise((resolve, reject) => {
44
48
  ncp.ncp(source, dest, { stopOnErr: true }, (err) => {
@@ -50,7 +54,7 @@ export let copyCache = (cacheName, dest) => {
50
54
  });
51
55
  };
52
56
  export let cacheSize = async () => {
53
- let dir = path.join(globalCacheDir);
57
+ let dir = path.join(getGlobalCacheDir());
54
58
  fs.mkdirSync(dir, { recursive: true });
55
59
  let size = await getFolderSize.strict(dir);
56
60
  if (size < 1024 * 1024) {
@@ -59,6 +63,11 @@ export let cacheSize = async () => {
59
63
  return (size / 1024 / 1024).toFixed(2) + ' MB';
60
64
  };
61
65
  export let cleanCache = async () => {
62
- let dir = path.join(globalCacheDir);
63
- fs.rmSync(dir, { recursive: true, force: true });
66
+ if (!getGlobalCacheDir().endsWith('mops/cache') && !getGlobalCacheDir().endsWith('/mops') && !getGlobalCacheDir().endsWith('/mops/' + getNetwork())) {
67
+ throw new Error('Invalid cache directory: ' + getGlobalCacheDir());
68
+ }
69
+ // local cache
70
+ fs.rmSync(path.join(getRootDir(), '.mops'), { recursive: true, force: true });
71
+ // global cache
72
+ fs.rmSync(getGlobalCacheDir(), { recursive: true, force: true });
64
73
  };
package/dist/cli.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  declare global {
2
2
  var MOPS_NETWORK: string;
3
+ var mopsReplicaTestRunning: boolean;
3
4
  }
4
5
  export {};