ic-mops 2.4.0 → 2.5.1

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 (48) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/api/network.ts +12 -4
  3. package/bun.lock +1082 -78
  4. package/bundle/cli.tgz +0 -0
  5. package/commands/add.ts +4 -1
  6. package/commands/build.ts +7 -12
  7. package/commands/install/install-dep.ts +5 -3
  8. package/commands/publish.ts +8 -8
  9. package/commands/remove.ts +5 -2
  10. package/commands/sources.ts +3 -2
  11. package/commands/sync.ts +13 -16
  12. package/commands/test/test.ts +3 -3
  13. package/commands/update.ts +13 -7
  14. package/commands/watch/error-checker.ts +3 -8
  15. package/commands/watch/warning-checker.ts +3 -8
  16. package/dist/api/network.js +11 -4
  17. package/dist/commands/add.js +4 -1
  18. package/dist/commands/build.js +5 -10
  19. package/dist/commands/install/install-dep.js +3 -3
  20. package/dist/commands/publish.js +8 -8
  21. package/dist/commands/remove.js +5 -2
  22. package/dist/commands/sources.js +3 -2
  23. package/dist/commands/sync.js +9 -14
  24. package/dist/commands/test/test.js +3 -3
  25. package/dist/commands/update.js +9 -4
  26. package/dist/commands/watch/error-checker.js +3 -8
  27. package/dist/commands/watch/warning-checker.js +3 -8
  28. package/dist/environments/web/cli.js +9 -0
  29. package/dist/helpers/find-changelog-entry.js +1 -1
  30. package/dist/integrity.js +9 -3
  31. package/dist/mops.js +3 -0
  32. package/dist/package.json +3 -5
  33. package/dist/parallel.js +9 -1
  34. package/dist/resolve-packages.js +4 -4
  35. package/dist/tests/build.test.js +3 -1
  36. package/dist/tests/helpers.js +8 -1
  37. package/dist/vessel.d.ts +1 -1
  38. package/dist/vessel.js +3 -2
  39. package/environments/web/cli.ts +12 -0
  40. package/helpers/find-changelog-entry.ts +3 -1
  41. package/integrity.ts +12 -3
  42. package/mops.ts +7 -0
  43. package/package.json +5 -7
  44. package/parallel.ts +16 -5
  45. package/resolve-packages.ts +6 -4
  46. package/tests/build.test.ts +4 -1
  47. package/tests/helpers.ts +9 -1
  48. package/vessel.ts +5 -3
@@ -4,7 +4,7 @@ import os from "node:os";
4
4
  import chalk from "chalk";
5
5
  import { getMocPath } from "../../helpers/get-moc-path.js";
6
6
  import { getGlobalMocArgs, getRootDir, readConfig } from "../../mops.js";
7
- import { sources } from "../sources.js";
7
+ import { sourcesArgs } from "../sources.js";
8
8
  import { parallel } from "../../parallel.js";
9
9
  import { globMoFiles } from "./globMoFiles.js";
10
10
  export class ErrorChecker {
@@ -30,19 +30,14 @@ export class ErrorChecker {
30
30
  onProgress();
31
31
  let rootDir = getRootDir();
32
32
  let mocPath = getMocPath();
33
- let deps = await sources({ cwd: rootDir });
33
+ let deps = (await sourcesArgs({ cwd: rootDir })).flat();
34
34
  let globalMocArgs = getGlobalMocArgs(readConfig());
35
35
  let paths = globMoFiles(rootDir);
36
36
  this.totalFiles = paths.length;
37
37
  this.processedFiles = 0;
38
38
  await parallel(os.cpus().length, paths, async (file) => {
39
39
  try {
40
- await promisify(execFile)(mocPath, [
41
- "--check",
42
- ...deps.flatMap((x) => x.split(" ")),
43
- ...globalMocArgs,
44
- file,
45
- ], { cwd: rootDir });
40
+ await promisify(execFile)(mocPath, ["--check", ...deps, ...globalMocArgs, file], { cwd: rootDir });
46
41
  }
47
42
  catch (error) {
48
43
  error.message.split("\n").forEach((line) => {
@@ -4,7 +4,7 @@ import os from "node:os";
4
4
  import chalk from "chalk";
5
5
  import { getMocPath } from "../../helpers/get-moc-path.js";
6
6
  import { getGlobalMocArgs, getRootDir, readConfig } from "../../mops.js";
7
- import { sources } from "../sources.js";
7
+ import { sourcesArgs } from "../sources.js";
8
8
  import { parallel } from "../../parallel.js";
9
9
  import { globMoFiles } from "./globMoFiles.js";
10
10
  export class WarningChecker {
@@ -50,7 +50,7 @@ export class WarningChecker {
50
50
  onProgress();
51
51
  let rootDir = getRootDir();
52
52
  let mocPath = getMocPath();
53
- let deps = await sources({ cwd: rootDir });
53
+ let deps = (await sourcesArgs({ cwd: rootDir })).flat();
54
54
  let globalMocArgs = getGlobalMocArgs(readConfig());
55
55
  let paths = globMoFiles(rootDir);
56
56
  this.totalFiles = paths.length;
@@ -59,12 +59,7 @@ export class WarningChecker {
59
59
  let controller = new AbortController();
60
60
  let { signal } = controller;
61
61
  this.controllers.set(file, controller);
62
- let { stderr } = await promisify(execFile)(mocPath, [
63
- "--check",
64
- ...deps.flatMap((x) => x.split(" ")),
65
- ...globalMocArgs,
66
- file,
67
- ], { cwd: rootDir, signal }).catch((error) => {
62
+ let { stderr } = await promisify(execFile)(mocPath, ["--check", ...deps, ...globalMocArgs, file], { cwd: rootDir, signal }).catch((error) => {
68
63
  if (error.code === "ABORT_ERR") {
69
64
  return { stderr: "" };
70
65
  }
@@ -1,4 +1,13 @@
1
1
  import * as wasm from "../../wasm/pkg/web/wasm.js";
2
2
  import { setWasmBindings } from "../../wasm.js";
3
+ import { readFileSync } from "node:fs";
4
+ import { resolve } from "node:path";
5
+ // Web wasm-pack target requires explicit initialization (unlike the nodejs
6
+ // target which auto-inits on require). Load the CLI's own WASM binary and
7
+ // call initSync before exposing the bindings.
8
+ // In the bundle __dirname is defined as import.meta.dirname and the binary
9
+ // sits next to cli.js; bundle:fix rewrites the path accordingly.
10
+ const wasmBytes = readFileSync(resolve(__dirname, "../../wasm/pkg/web/wasm_bg.wasm"));
11
+ wasm.initSync({ module: wasmBytes });
3
12
  setWasmBindings(wasm);
4
13
  export * from "../../cli.js";
@@ -15,7 +15,7 @@ export function findChangelogEntry(changelog, version) {
15
15
  }
16
16
  }
17
17
  else if (node.type === "heading" &&
18
- toMarkdown(node).match(new RegExp(`\\b${version}\\b`))) {
18
+ toMarkdown(node).match(new RegExp(`\\b${version.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`))) {
19
19
  depth = node.depth;
20
20
  found = true;
21
21
  }
package/dist/integrity.js CHANGED
@@ -50,7 +50,7 @@ export function getLocalFileHash(fileId) {
50
50
  return bytesToHex(sha256(fileData));
51
51
  }
52
52
  function getMopsTomlHash() {
53
- return bytesToHex(sha256(fs.readFileSync(getRootDir() + "/mops.toml")));
53
+ return bytesToHex(sha256(fs.readFileSync(path.join(getRootDir(), "mops.toml"))));
54
54
  }
55
55
  function getMopsTomlDepsHash() {
56
56
  let config = readConfig();
@@ -90,7 +90,13 @@ export function readLockFile() {
90
90
  let rootDir = getRootDir();
91
91
  let lockFile = path.join(rootDir, "mops.lock");
92
92
  if (fs.existsSync(lockFile)) {
93
- return JSON.parse(fs.readFileSync(lockFile).toString());
93
+ try {
94
+ return JSON.parse(fs.readFileSync(lockFile).toString());
95
+ }
96
+ catch {
97
+ console.error("mops.lock is corrupted. Delete it and run `mops install` to regenerate.");
98
+ process.exit(1);
99
+ }
94
100
  }
95
101
  return null;
96
102
  }
@@ -208,7 +214,7 @@ export async function checkLockFile(force = false) {
208
214
  }
209
215
  for (let [fileId, lockedHash] of Object.entries(hashes)) {
210
216
  // check if file belongs to package
211
- if (!fileId.startsWith(packageId)) {
217
+ if (!fileId.startsWith(packageId + "/")) {
212
218
  console.error("Integrity check failed");
213
219
  console.error(`File ${fileId} in lock file does not belong to package ${packageId}`);
214
220
  process.exit(1);
package/dist/mops.js CHANGED
@@ -131,6 +131,9 @@ export async function getGithubCommit(repo, ref) {
131
131
  res = await fetch(`https://api.github.com/repos/${repo}/commits/main`);
132
132
  json = await res.json();
133
133
  }
134
+ if (!res.ok || !json.sha) {
135
+ throw new Error(`Failed to fetch commit for ${repo}#${ref}: ${json.message || `HTTP ${res.status}`}`);
136
+ }
134
137
  return json;
135
138
  }
136
139
  export function getDependencyType(version) {
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ic-mops",
3
- "version": "2.4.0",
3
+ "version": "2.5.1",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "mops": "bin/mops.js",
@@ -34,7 +34,6 @@
34
34
  "@noble/hashes": "1.8.0",
35
35
  "as-table": "1.0.55",
36
36
  "buffer": "6.0.3",
37
- "cacheable-request": "12.0.1",
38
37
  "chalk": "5.4.1",
39
38
  "change-case": "5.4.4",
40
39
  "chokidar": "3.6.0",
@@ -56,7 +55,7 @@
56
55
  "markdown-table": "3.0.4",
57
56
  "mdast-util-from-markdown": "2.0.2",
58
57
  "mdast-util-to-markdown": "2.1.2",
59
- "minimatch": "10.0.1",
58
+ "minimatch": "10.2.4",
60
59
  "ncp": "2.0.0",
61
60
  "octokit": "3.1.2",
62
61
  "pem-file": "1.0.1",
@@ -69,7 +68,7 @@
69
68
  "semver": "7.7.1",
70
69
  "stream-to-promise": "3.0.0",
71
70
  "string-width": "7.2.0",
72
- "tar": "7.5.9",
71
+ "tar": "7.5.11",
73
72
  "terminal-size": "4.0.0",
74
73
  "vscode-languageserver-textdocument": "1.0.12"
75
74
  },
@@ -78,7 +77,6 @@
78
77
  "@types/debounce": "1.2.4",
79
78
  "@types/decompress": "4.2.7",
80
79
  "@types/fs-extra": "11.0.4",
81
- "@types/glob": "8.1.0",
82
80
  "@types/jsdom": "28.0.0",
83
81
  "@types/ncp": "2.0.8",
84
82
  "@types/node": "24.0.3",
package/dist/parallel.js CHANGED
@@ -1,8 +1,12 @@
1
1
  export async function parallel(threads, items, fn) {
2
- return new Promise((resolve) => {
2
+ return new Promise((resolve, reject) => {
3
3
  let busyThreads = 0;
4
+ let failed = false;
4
5
  items = items.slice();
5
6
  let loop = () => {
7
+ if (failed) {
8
+ return;
9
+ }
6
10
  if (!items.length) {
7
11
  if (busyThreads === 0) {
8
12
  resolve();
@@ -16,6 +20,10 @@ export async function parallel(threads, items, fn) {
16
20
  fn(items.shift()).then(() => {
17
21
  busyThreads--;
18
22
  loop();
23
+ }, (err) => {
24
+ busyThreads--;
25
+ failed = true;
26
+ reject(err);
19
27
  });
20
28
  loop();
21
29
  };
@@ -21,8 +21,8 @@ export async function resolvePackages({ conflicts = "ignore", } = {}) {
21
21
  let packages = {};
22
22
  let versions = {};
23
23
  let compareVersions = (a = "0.0.0", b = "0.0.0") => {
24
- let ap = a.split(".").map((x) => parseInt(x));
25
- let bp = b.split(".").map((x) => parseInt(x));
24
+ let ap = a.split(".").map((x) => parseInt(x) || 0);
25
+ let bp = b.split(".").map((x) => parseInt(x) || 0);
26
26
  if (ap[0] - bp[0]) {
27
27
  return Math.sign(ap[0] - bp[0]);
28
28
  }
@@ -97,7 +97,7 @@ export async function resolvePackages({ conflicts = "ignore", } = {}) {
97
97
  }
98
98
  else if (version) {
99
99
  let cacheDir = getDepCacheName(name, version);
100
- nestedConfig = readConfig(getDepCacheDir(cacheDir) + "/mops.toml");
100
+ nestedConfig = readConfig(path.join(getDepCacheDir(cacheDir), "mops.toml"));
101
101
  }
102
102
  // collect nested deps
103
103
  if (nestedConfig) {
@@ -137,7 +137,7 @@ export async function resolvePackages({ conflicts = "ignore", } = {}) {
137
137
  if (majors.size > 1) {
138
138
  console.error(chalk.reset("") +
139
139
  chalk.redBright(conflicts === "error" ? "Error!" : "Warning!"), `Conflicting versions of dependency "${dep}"`);
140
- for (let { version, dependencyOf } of vers.reverse()) {
140
+ for (let { version, dependencyOf } of [...vers].reverse()) {
141
141
  console.error(chalk.reset(" ") +
142
142
  `${dep} ${chalk.bold.red(version.split(".")[0])}.${version.split(".").slice(1).join(".")} is dependency of ${chalk.bold(dependencyOf)}`);
143
143
  }
@@ -1,4 +1,4 @@
1
- import { describe, expect, test } from "@jest/globals";
1
+ import { describe, expect, jest, test } from "@jest/globals";
2
2
  import { execa } from "execa";
3
3
  import { existsSync, rmSync } from "node:fs";
4
4
  import path from "path";
@@ -11,6 +11,8 @@ function cleanFixture(cwd, ...extras) {
11
11
  }
12
12
  }
13
13
  describe("build", () => {
14
+ // Several dfx/pocket-ic builds per test; slow CI can exceed 60s default.
15
+ jest.setTimeout(120_000);
14
16
  test("ok", async () => {
15
17
  const cwd = path.join(import.meta.dirname, "build/success");
16
18
  try {
@@ -2,8 +2,15 @@ import { expect } from "@jest/globals";
2
2
  import { execa } from "execa";
3
3
  import { dirname } from "path";
4
4
  import { fileURLToPath } from "url";
5
+ // When MOPS_TEST_GLOBAL is set, invoke the globally-installed `mops` binary
6
+ // directly rather than the npm script. This exercises the real global-install
7
+ // code path where the binary lives outside the project tree.
8
+ const useGlobalBinary = Boolean(process.env.MOPS_TEST_GLOBAL);
5
9
  export const cli = async (args, { cwd } = {}) => {
6
- return await execa("npm", ["run", "--silent", "mops", "--", ...args], {
10
+ const [cmd, cmdArgs] = useGlobalBinary
11
+ ? ["mops", args]
12
+ : ["npm", ["run", "--silent", "mops", "--", ...args]];
13
+ return await execa(cmd, cmdArgs, {
7
14
  env: { ...process.env, ...(cwd != null && { MOPS_CWD: cwd }) },
8
15
  ...(cwd != null && { cwd }),
9
16
  stdio: "pipe",
package/dist/vessel.d.ts CHANGED
@@ -18,4 +18,4 @@ export declare const installFromGithub: (name: string, repo: string, { verbose,
18
18
  dep?: boolean | undefined;
19
19
  silent?: boolean | undefined;
20
20
  ignoreTransitive?: boolean | undefined;
21
- }) => Promise<void>;
21
+ }) => Promise<boolean>;
package/dist/vessel.js CHANGED
@@ -141,7 +141,7 @@ export const installFromGithub = async (name, repo, { verbose = false, dep = fal
141
141
  }
142
142
  catch (err) {
143
143
  deleteSync([cacheDir], { force: true });
144
- process.exit(1);
144
+ return false;
145
145
  }
146
146
  }
147
147
  if (verbose) {
@@ -151,7 +151,7 @@ export const installFromGithub = async (name, repo, { verbose = false, dep = fal
151
151
  logUpdate.clear();
152
152
  }
153
153
  if (ignoreTransitive) {
154
- return;
154
+ return true;
155
155
  }
156
156
  const config = await readVesselConfig(cacheDir, { silent });
157
157
  if (config) {
@@ -161,4 +161,5 @@ export const installFromGithub = async (name, repo, { verbose = false, dep = fal
161
161
  }
162
162
  }
163
163
  }
164
+ return true;
164
165
  };
@@ -1,5 +1,17 @@
1
1
  import * as wasm from "../../wasm/pkg/web/wasm.js";
2
2
  import { setWasmBindings } from "../../wasm.js";
3
+ import { readFileSync } from "node:fs";
4
+ import { resolve } from "node:path";
5
+
6
+ // Web wasm-pack target requires explicit initialization (unlike the nodejs
7
+ // target which auto-inits on require). Load the CLI's own WASM binary and
8
+ // call initSync before exposing the bindings.
9
+ // In the bundle __dirname is defined as import.meta.dirname and the binary
10
+ // sits next to cli.js; bundle:fix rewrites the path accordingly.
11
+ const wasmBytes = readFileSync(
12
+ resolve(__dirname, "../../wasm/pkg/web/wasm_bg.wasm"),
13
+ );
14
+ wasm.initSync({ module: wasmBytes });
3
15
 
4
16
  setWasmBindings(wasm);
5
17
 
@@ -16,7 +16,9 @@ export function findChangelogEntry(changelog: string, version: string): string {
16
16
  }
17
17
  } else if (
18
18
  node.type === "heading" &&
19
- toMarkdown(node).match(new RegExp(`\\b${version}\\b`))
19
+ toMarkdown(node).match(
20
+ new RegExp(`\\b${version.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`),
21
+ )
20
22
  ) {
21
23
  depth = node.depth;
22
24
  found = true;
package/integrity.ts CHANGED
@@ -87,7 +87,9 @@ export function getLocalFileHash(fileId: string): string {
87
87
  }
88
88
 
89
89
  function getMopsTomlHash(): string {
90
- return bytesToHex(sha256(fs.readFileSync(getRootDir() + "/mops.toml")));
90
+ return bytesToHex(
91
+ sha256(fs.readFileSync(path.join(getRootDir(), "mops.toml"))),
92
+ );
91
93
  }
92
94
 
93
95
  function getMopsTomlDepsHash(): string {
@@ -137,7 +139,14 @@ export function readLockFile(): LockFile | null {
137
139
  let rootDir = getRootDir();
138
140
  let lockFile = path.join(rootDir, "mops.lock");
139
141
  if (fs.existsSync(lockFile)) {
140
- return JSON.parse(fs.readFileSync(lockFile).toString()) as LockFile;
142
+ try {
143
+ return JSON.parse(fs.readFileSync(lockFile).toString()) as LockFile;
144
+ } catch {
145
+ console.error(
146
+ "mops.lock is corrupted. Delete it and run `mops install` to regenerate.",
147
+ );
148
+ process.exit(1);
149
+ }
141
150
  }
142
151
  return null;
143
152
  }
@@ -290,7 +299,7 @@ export async function checkLockFile(force = false) {
290
299
 
291
300
  for (let [fileId, lockedHash] of Object.entries(hashes)) {
292
301
  // check if file belongs to package
293
- if (!fileId.startsWith(packageId)) {
302
+ if (!fileId.startsWith(packageId + "/")) {
294
303
  console.error("Integrity check failed");
295
304
  console.error(
296
305
  `File ${fileId} in lock file does not belong to package ${packageId}`,
package/mops.ts CHANGED
@@ -152,6 +152,13 @@ export async function getGithubCommit(repo: string, ref: string): Promise<any> {
152
152
  res = await fetch(`https://api.github.com/repos/${repo}/commits/main`);
153
153
  json = await res.json();
154
154
  }
155
+
156
+ if (!res.ok || !json.sha) {
157
+ throw new Error(
158
+ `Failed to fetch commit for ${repo}#${ref}: ${json.message || `HTTP ${res.status}`}`,
159
+ );
160
+ }
161
+
155
162
  return json;
156
163
  }
157
164
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ic-mops",
3
- "version": "2.4.0",
3
+ "version": "2.5.1",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "mops": "dist/bin/mops.js",
@@ -36,8 +36,8 @@
36
36
  "build:wasm:web": "wasm-pack build wasm --target web --out-dir pkg/web && rm wasm/pkg/web/.gitignore",
37
37
  "dist": "npm run build:wasm && tsc && mkdir -p dist/wasm && cp -r wasm/pkg dist/wasm",
38
38
  "bundle": "rm -rf ./bundle && bun build ./environments/web/cli.ts --outdir ./bundle --target node --minify --external @napi-rs/lzma --external fsevents --format esm --define '__dirname=import.meta.dirname' && run-s bundle:fix bundle:copy bundle:package-json bundle:tar",
39
- "bundle:fix": "rexreplace 'new URL\\(\"\\.\\./templates' 'new URL(\"./templates' bundle/cli.js && rexreplace 'resolve\\(\".*?/xhr-sync-worker\\.js\"\\)' 'resolve(\"./xhr-sync-worker.js\")' bundle/cli.js && rexreplace '\"import.meta.dirname\",\"wasm_bg.wasm\"' 'import.meta.dirname || new URL(\".\", import.meta.url).pathname,\"wasm_bg.wasm\"' bundle/cli.js && rexreplace 'resolve\\(\"import.meta.dirname\",\".*?/default-stylesheet\\.css\"\\)' 'resolve(import.meta.dirname,\"./default-stylesheet.css\")' bundle/cli.js",
40
- "bundle:copy": "cp -r commands/bench bundle && cp -r bin declarations templates package.json bundle && cp -r node_modules/prettier-plugin-motoko/wasm/pkg/nodejs/wasm_bg.wasm node_modules/jsdom/lib/jsdom/living/xhr/xhr-sync-worker.js node_modules/jsdom/lib/jsdom/browser/default-stylesheet.css bundle",
39
+ "bundle:fix": "rexreplace 'new URL\\(\"\\.\\./templates' 'new URL(\"./templates' bundle/cli.js && rexreplace 'resolve\\(\".*?/xhr-sync-worker\\.js\"\\)' 'resolve(\"./xhr-sync-worker.js\")' bundle/cli.js && rexreplace '\"import.meta.dirname\",\"wasm_bg.wasm\"' 'import.meta.dirname || new URL(\".\", import.meta.url).pathname,\"wasm_bg.wasm\"' bundle/cli.js && rexreplace 'resolve\\(\"import.meta.dirname\",\".*?/default-stylesheet\\.css\"\\)' 'resolve(import.meta.dirname,\"./default-stylesheet.css\")' bundle/cli.js && rexreplace '\"import.meta.dirname\",\"../../wasm/pkg/web/wasm_bg.wasm\"' 'import.meta.dirname || new URL(\".\", import.meta.url).pathname,\"mops_wasm_bg.wasm\"' bundle/cli.js",
40
+ "bundle:copy": "cp -r commands/bench bundle && cp -r bin declarations templates package.json bundle && cp wasm/pkg/web/wasm_bg.wasm bundle/mops_wasm_bg.wasm && cp -r node_modules/prettier-plugin-motoko/wasm/pkg/nodejs/wasm_bg.wasm node_modules/jsdom/lib/jsdom/living/xhr/xhr-sync-worker.js node_modules/jsdom/lib/jsdom/browser/default-stylesheet.css bundle",
41
41
  "bundle:package-json": "tsx bundle-package-json.ts",
42
42
  "bundle:tar": "rm -f bundle/cli.tgz && touch -t 200101010101 bundle/cli.tgz && find bundle -exec touch -d '1970-01-01 00:00:00' {} + && tar --sort name --exclude bundle/cli.tgz -cvf - bundle | gzip -n > bundle/cli.tgz",
43
43
  "copy": "cp -r commands/bench dist/commands && cp -r declarations templates package.json bin dist | true",
@@ -55,7 +55,6 @@
55
55
  "@noble/hashes": "1.8.0",
56
56
  "as-table": "1.0.55",
57
57
  "buffer": "6.0.3",
58
- "cacheable-request": "12.0.1",
59
58
  "chalk": "5.4.1",
60
59
  "change-case": "5.4.4",
61
60
  "chokidar": "3.6.0",
@@ -77,7 +76,7 @@
77
76
  "markdown-table": "3.0.4",
78
77
  "mdast-util-from-markdown": "2.0.2",
79
78
  "mdast-util-to-markdown": "2.1.2",
80
- "minimatch": "10.0.1",
79
+ "minimatch": "10.2.4",
81
80
  "ncp": "2.0.0",
82
81
  "octokit": "3.1.2",
83
82
  "pem-file": "1.0.1",
@@ -90,7 +89,7 @@
90
89
  "semver": "7.7.1",
91
90
  "stream-to-promise": "3.0.0",
92
91
  "string-width": "7.2.0",
93
- "tar": "7.5.9",
92
+ "tar": "7.5.11",
94
93
  "terminal-size": "4.0.0",
95
94
  "vscode-languageserver-textdocument": "1.0.12"
96
95
  },
@@ -99,7 +98,6 @@
99
98
  "@types/debounce": "1.2.4",
100
99
  "@types/decompress": "4.2.7",
101
100
  "@types/fs-extra": "11.0.4",
102
- "@types/glob": "8.1.0",
103
101
  "@types/jsdom": "28.0.0",
104
102
  "@types/ncp": "2.0.8",
105
103
  "@types/node": "24.0.3",
package/parallel.ts CHANGED
@@ -3,11 +3,15 @@ export async function parallel<T>(
3
3
  items: T[],
4
4
  fn: (item: T) => Promise<void>,
5
5
  ) {
6
- return new Promise<void>((resolve) => {
6
+ return new Promise<void>((resolve, reject) => {
7
7
  let busyThreads = 0;
8
+ let failed = false;
8
9
  items = items.slice();
9
10
 
10
11
  let loop = () => {
12
+ if (failed) {
13
+ return;
14
+ }
11
15
  if (!items.length) {
12
16
  if (busyThreads === 0) {
13
17
  resolve();
@@ -18,10 +22,17 @@ export async function parallel<T>(
18
22
  return;
19
23
  }
20
24
  busyThreads++;
21
- fn(items.shift() as T).then(() => {
22
- busyThreads--;
23
- loop();
24
- });
25
+ fn(items.shift() as T).then(
26
+ () => {
27
+ busyThreads--;
28
+ loop();
29
+ },
30
+ (err) => {
31
+ busyThreads--;
32
+ failed = true;
33
+ reject(err);
34
+ },
35
+ );
25
36
  loop();
26
37
  };
27
38
  loop();
@@ -41,12 +41,12 @@ export async function resolvePackages({
41
41
  > = {};
42
42
 
43
43
  let compareVersions = (a: string = "0.0.0", b: string = "0.0.0") => {
44
- let ap = a.split(".").map((x: string) => parseInt(x)) as [
44
+ let ap = a.split(".").map((x: string) => parseInt(x) || 0) as [
45
45
  number,
46
46
  number,
47
47
  number,
48
48
  ];
49
- let bp = b.split(".").map((x: string) => parseInt(x)) as [
49
+ let bp = b.split(".").map((x: string) => parseInt(x) || 0) as [
50
50
  number,
51
51
  number,
52
52
  number,
@@ -138,7 +138,9 @@ export async function resolvePackages({
138
138
  }
139
139
  } else if (version) {
140
140
  let cacheDir = getDepCacheName(name, version);
141
- nestedConfig = readConfig(getDepCacheDir(cacheDir) + "/mops.toml");
141
+ nestedConfig = readConfig(
142
+ path.join(getDepCacheDir(cacheDir), "mops.toml"),
143
+ );
142
144
  }
143
145
 
144
146
  // collect nested deps
@@ -193,7 +195,7 @@ export async function resolvePackages({
193
195
  `Conflicting versions of dependency "${dep}"`,
194
196
  );
195
197
 
196
- for (let { version, dependencyOf } of vers.reverse()) {
198
+ for (let { version, dependencyOf } of [...vers].reverse()) {
197
199
  console.error(
198
200
  chalk.reset(" ") +
199
201
  `${dep} ${chalk.bold.red(version.split(".")[0])}.${version.split(".").slice(1).join(".")} is dependency of ${chalk.bold(dependencyOf)}`,
@@ -1,4 +1,4 @@
1
- import { describe, expect, test } from "@jest/globals";
1
+ import { describe, expect, jest, test } from "@jest/globals";
2
2
  import { execa } from "execa";
3
3
  import { existsSync, rmSync } from "node:fs";
4
4
  import path from "path";
@@ -14,6 +14,9 @@ function cleanFixture(cwd: string, ...extras: string[]) {
14
14
  }
15
15
 
16
16
  describe("build", () => {
17
+ // Several dfx/pocket-ic builds per test; slow CI can exceed 60s default.
18
+ jest.setTimeout(120_000);
19
+
17
20
  test("ok", async () => {
18
21
  const cwd = path.join(import.meta.dirname, "build/success");
19
22
  try {
package/tests/helpers.ts CHANGED
@@ -7,8 +7,16 @@ export interface CliOptions {
7
7
  cwd?: string;
8
8
  }
9
9
 
10
+ // When MOPS_TEST_GLOBAL is set, invoke the globally-installed `mops` binary
11
+ // directly rather than the npm script. This exercises the real global-install
12
+ // code path where the binary lives outside the project tree.
13
+ const useGlobalBinary = Boolean(process.env.MOPS_TEST_GLOBAL);
14
+
10
15
  export const cli = async (args: string[], { cwd }: CliOptions = {}) => {
11
- return await execa("npm", ["run", "--silent", "mops", "--", ...args], {
16
+ const [cmd, cmdArgs] = useGlobalBinary
17
+ ? ["mops", args]
18
+ : ["npm", ["run", "--silent", "mops", "--", ...args]];
19
+ return await execa(cmd, cmdArgs, {
12
20
  env: { ...process.env, ...(cwd != null && { MOPS_CWD: cwd }) },
13
21
  ...(cwd != null && { cwd }),
14
22
  stdio: "pipe",
package/vessel.ts CHANGED
@@ -181,7 +181,7 @@ export const installFromGithub = async (
181
181
  silent = false,
182
182
  ignoreTransitive = false,
183
183
  } = {},
184
- ) => {
184
+ ): Promise<boolean> => {
185
185
  let cacheName = getGithubDepCacheName(name, repo);
186
186
  let cacheDir = getDepCacheDir(cacheName);
187
187
 
@@ -205,7 +205,7 @@ export const installFromGithub = async (
205
205
  await downloadFromGithub(repo, cacheDir, progress);
206
206
  } catch (err) {
207
207
  deleteSync([cacheDir], { force: true });
208
- process.exit(1);
208
+ return false;
209
209
  }
210
210
  }
211
211
 
@@ -216,7 +216,7 @@ export const installFromGithub = async (
216
216
  }
217
217
 
218
218
  if (ignoreTransitive) {
219
- return;
219
+ return true;
220
220
  }
221
221
 
222
222
  const config = await readVesselConfig(cacheDir, { silent });
@@ -228,4 +228,6 @@ export const installFromGithub = async (
228
228
  }
229
229
  }
230
230
  }
231
+
232
+ return true;
231
233
  };