ic-mops 0.32.2 → 0.33.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.
@@ -4,7 +4,8 @@ import { checkConfigFile, readConfig } from '../mops.js';
4
4
  import { install } from './install.js';
5
5
  import { installFromGithub } from '../vessel.js';
6
6
  import { notifyInstalls } from '../notify-installs.js';
7
- export async function installAll({ verbose = false, silent = false } = {}) {
7
+ import { checkIntegrity } from '../integrity.js';
8
+ export async function installAll({ verbose = false, silent = false, lockfile } = {}) {
8
9
  if (!checkConfigFile()) {
9
10
  return;
10
11
  }
@@ -25,7 +26,13 @@ export async function installAll({ verbose = false, silent = false } = {}) {
25
26
  installedPackages = { ...installedPackages, ...res };
26
27
  }
27
28
  }
28
- await notifyInstalls(Object.keys(installedPackages));
29
+ if (!silent && lockfile !== 'ignore') {
30
+ logUpdate('Checking integrity...');
31
+ }
32
+ await Promise.all([
33
+ notifyInstalls(Object.keys(installedPackages)),
34
+ checkIntegrity(lockfile),
35
+ ]);
29
36
  if (!silent) {
30
37
  logUpdate.clear();
31
38
  console.log(chalk.green('All packages installed'));
@@ -118,8 +118,8 @@ export async function install(pkg, version = '', { verbose = false, silent = fal
118
118
  if (!alreadyInstalled) {
119
119
  installedDeps = { ...installedDeps, [pkg]: version };
120
120
  }
121
- if (ok) {
122
- return installedDeps;
121
+ if (!ok) {
122
+ return false;
123
123
  }
124
- return false;
124
+ return installedDeps;
125
125
  }
@@ -1,5 +1,8 @@
1
- export declare function remove(name: string, { dev, verbose, dryRun }?: {
2
- dev?: boolean | undefined;
3
- verbose?: boolean | undefined;
4
- dryRun?: boolean | undefined;
5
- }): Promise<void>;
1
+ type RemoveOptions = {
2
+ verbose?: boolean;
3
+ dev?: boolean;
4
+ dryRun?: boolean;
5
+ lockfile?: 'save' | 'ignore';
6
+ };
7
+ export declare function remove(name: string, { dev, verbose, dryRun, lockfile }?: RemoveOptions): Promise<void>;
8
+ export {};
@@ -2,7 +2,8 @@ import fs from 'node:fs';
2
2
  import { deleteSync } from 'del';
3
3
  import chalk from 'chalk';
4
4
  import { formatDir, formatGithubDir, checkConfigFile, readConfig, writeConfig } from '../mops.js';
5
- export async function remove(name, { dev = false, verbose = false, dryRun = false } = {}) {
5
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
6
+ export async function remove(name, { dev = false, verbose = false, dryRun = false, lockfile } = {}) {
6
7
  if (!checkConfigFile()) {
7
8
  return;
8
9
  }
@@ -79,5 +80,6 @@ export async function remove(name, { dev = false, verbose = false, dryRun = fals
79
80
  delete config['dev-dependencies'][name];
80
81
  }
81
82
  dryRun || writeConfig(config);
83
+ // await checkIntegrity(lockfile);
82
84
  console.log(chalk.green('Package removed ') + `${name} = "${version}"`);
83
85
  }
@@ -1 +1,5 @@
1
- export declare function sync(): Promise<void>;
1
+ type SyncOptions = {
2
+ lockfile?: 'save' | 'ignore';
3
+ };
4
+ export declare function sync({ lockfile }?: SyncOptions): Promise<void>;
5
+ export {};
@@ -5,6 +5,29 @@ import chalk from 'chalk';
5
5
  import { checkConfigFile, getRootDir, readConfig } from '../mops.js';
6
6
  import { add } from './add.js';
7
7
  import { remove } from './remove.js';
8
+ import { checkIntegrity } from '../integrity.js';
9
+ export async function sync({ lockfile } = {}) {
10
+ if (!checkConfigFile()) {
11
+ return;
12
+ }
13
+ let missing = await getMissingPackages();
14
+ let unused = await getUnusedPackages();
15
+ missing.length && console.log(`${chalk.yellow('Missing packages:')} ${missing.join(', ')}`);
16
+ unused.length && console.log(`${chalk.yellow('Unused packages:')} ${unused.join(', ')}`);
17
+ let config = readConfig();
18
+ let deps = new Set(Object.keys(config.dependencies || {}));
19
+ let devDeps = new Set(Object.keys(config['dev-dependencies'] || {}));
20
+ // add missing packages
21
+ for (let pkg of missing) {
22
+ await add(pkg, { lockfile: 'ignore' });
23
+ }
24
+ // remove unused packages
25
+ for (let pkg of unused) {
26
+ let dev = devDeps.has(pkg) && !deps.has(pkg);
27
+ await remove(pkg, { dev, lockfile: 'ignore' });
28
+ }
29
+ await checkIntegrity(lockfile);
30
+ }
8
31
  let ignore = [
9
32
  '**/node_modules/**',
10
33
  '**/.vessel/**',
@@ -63,24 +86,3 @@ async function getUnusedPackages() {
63
86
  }
64
87
  return [...allDeps];
65
88
  }
66
- export async function sync() {
67
- if (!checkConfigFile()) {
68
- return;
69
- }
70
- let missing = await getMissingPackages();
71
- let unused = await getUnusedPackages();
72
- missing.length && console.log(`${chalk.yellow('Missing packages:')} ${missing.join(', ')}`);
73
- unused.length && console.log(`${chalk.yellow('Unused packages:')} ${unused.join(', ')}`);
74
- let config = readConfig();
75
- let deps = new Set(Object.keys(config.dependencies || {}));
76
- let devDeps = new Set(Object.keys(config['dev-dependencies'] || {}));
77
- // add missing packages
78
- for (let pkg of missing) {
79
- await add(pkg);
80
- }
81
- // remove unused packages
82
- for (let pkg of unused) {
83
- let dev = devDeps.has(pkg) && !deps.has(pkg);
84
- await remove(pkg, { dev });
85
- }
86
- }
@@ -123,7 +123,13 @@ export async function testWithReporter(reporter, filter = '', mode = 'interprete
123
123
  return;
124
124
  }
125
125
  // run
126
- let proc = spawn('wasmtime', ['--max-wasm-stack=2000000', wasmFile]);
126
+ let proc = spawn('wasmtime', [
127
+ '--max-wasm-stack=2000000',
128
+ '--enable-cranelift-nan-canonicalization',
129
+ '--wasm-features',
130
+ 'multi-memory,bulk-memory',
131
+ wasmFile,
132
+ ]);
127
133
  await pipeMMF(proc, mmf);
128
134
  }).finally(() => {
129
135
  fs.rmSync(wasmFile, { force: true });
@@ -1 +1,7 @@
1
- export declare function update(pkg?: string): Promise<void>;
1
+ type UpdateOptions = {
2
+ verbose?: boolean;
3
+ dev?: boolean;
4
+ lockfile?: 'save' | 'ignore';
5
+ };
6
+ export declare function update(pkg?: string, { lockfile }?: UpdateOptions): Promise<void>;
7
+ export {};
@@ -2,7 +2,8 @@ import chalk from 'chalk';
2
2
  import { checkConfigFile, getGithubCommit, parseGithubURL, readConfig } from '../mops.js';
3
3
  import { add } from './add.js';
4
4
  import { getAvailableUpdates } from './available-updates.js';
5
- export async function update(pkg) {
5
+ import { checkIntegrity } from '../integrity.js';
6
+ export async function update(pkg, { lockfile } = {}) {
6
7
  if (!checkConfigFile()) {
7
8
  return;
8
9
  }
@@ -42,4 +43,5 @@ export async function update(pkg) {
42
43
  await add(`${dep[0]}@${dep[2]}`, { dev });
43
44
  }
44
45
  }
46
+ await checkIntegrity(lockfile);
45
47
  }
@@ -73,6 +73,14 @@ type Script =
73
73
  name: text;
74
74
  value: text;
75
75
  };
76
+ type Result_8 =
77
+ variant {
78
+ err: Err;
79
+ ok: vec record {
80
+ FileId;
81
+ blob;
82
+ };
83
+ };
76
84
  type Result_7 =
77
85
  variant {
78
86
  err: Err;
@@ -145,6 +153,7 @@ type PackageSummary__1 =
145
153
  owner: principal;
146
154
  ownerInfo: User;
147
155
  publication: PackagePublication;
156
+ quality: PackageQuality;
148
157
  };
149
158
  type PackageSummaryWithChanges__1 =
150
159
  record {
@@ -156,6 +165,7 @@ type PackageSummaryWithChanges__1 =
156
165
  owner: principal;
157
166
  ownerInfo: User;
158
167
  publication: PackagePublication;
168
+ quality: PackageQuality;
159
169
  };
160
170
  type PackageSummaryWithChanges =
161
171
  record {
@@ -167,6 +177,7 @@ type PackageSummaryWithChanges =
167
177
  owner: principal;
168
178
  ownerInfo: User;
169
179
  publication: PackagePublication;
180
+ quality: PackageQuality;
170
181
  };
171
182
  type PackageSummary =
172
183
  record {
@@ -177,6 +188,18 @@ type PackageSummary =
177
188
  owner: principal;
178
189
  ownerInfo: User;
179
190
  publication: PackagePublication;
191
+ quality: PackageQuality;
192
+ };
193
+ type PackageQuality =
194
+ record {
195
+ depsStatus: DepsStatus;
196
+ hasDescription: bool;
197
+ hasDocumentation: bool;
198
+ hasKeywords: bool;
199
+ hasLicense: bool;
200
+ hasReleaseNotes: bool;
201
+ hasRepository: bool;
202
+ hasTests: bool;
180
203
  };
181
204
  type PackagePublication =
182
205
  record {
@@ -207,6 +230,7 @@ type PackageDetails =
207
230
  owner: principal;
208
231
  ownerInfo: User;
209
232
  publication: PackagePublication;
233
+ quality: PackageQuality;
210
234
  testStats: TestStats__1;
211
235
  versionHistory: vec PackageSummaryWithChanges__1;
212
236
  };
@@ -281,6 +305,12 @@ type DownloadsSnapshot =
281
305
  endTime: Time;
282
306
  startTime: Time;
283
307
  };
308
+ type DepsStatus =
309
+ variant {
310
+ allLatest;
311
+ tooOld;
312
+ updatesAvailable;
313
+ };
284
314
  type DependencyV2 =
285
315
  record {
286
316
  name: PackageName__1;
@@ -296,6 +326,7 @@ type DepChange =
296
326
  service : {
297
327
  backup: () -> ();
298
328
  claimAirdrop: (principal) -> (text);
329
+ computeHashesForExistingFiles: () -> ();
299
330
  diff: (text, text) -> (PackageChanges__1) query;
300
331
  finishPublish: (PublishingId) -> (Result);
301
332
  getAirdropAmount: () -> (nat) query;
@@ -311,6 +342,15 @@ service : {
311
342
  (vec DownloadsSnapshot__1) query;
312
343
  getDownloadTrendByPackageName: (PackageName) ->
313
344
  (vec DownloadsSnapshot__1) query;
345
+ getFileHashes: (PackageName, PackageVersion) -> (Result_8);
346
+ getFileHashesByPackageIds: (vec PackageId) ->
347
+ (vec record {
348
+ PackageId;
349
+ vec record {
350
+ FileId;
351
+ blob;
352
+ };
353
+ });
314
354
  getFileIds: (PackageName, PackageVersion) -> (Result_7) query;
315
355
  getHighestSemverBatch:
316
356
  (vec record {
@@ -11,6 +11,9 @@ export interface DependencyV2 {
11
11
  'repo' : string,
12
12
  'version' : string,
13
13
  }
14
+ export type DepsStatus = { 'allLatest' : null } |
15
+ { 'tooOld' : null } |
16
+ { 'updatesAvailable' : null };
14
17
  export interface DownloadsSnapshot {
15
18
  'startTime' : Time,
16
19
  'endTime' : Time,
@@ -76,6 +79,7 @@ export interface PackageDetails {
76
79
  'ownerInfo' : User,
77
80
  'owner' : Principal,
78
81
  'deps' : Array<PackageSummary__1>,
82
+ 'quality' : PackageQuality,
79
83
  'testStats' : TestStats__1,
80
84
  'downloadsTotal' : bigint,
81
85
  'downloadsInLast30Days' : bigint,
@@ -101,9 +105,20 @@ export interface PackagePublication {
101
105
  'time' : Time,
102
106
  'user' : Principal,
103
107
  }
108
+ export interface PackageQuality {
109
+ 'depsStatus' : DepsStatus,
110
+ 'hasDescription' : boolean,
111
+ 'hasKeywords' : boolean,
112
+ 'hasLicense' : boolean,
113
+ 'hasDocumentation' : boolean,
114
+ 'hasTests' : boolean,
115
+ 'hasRepository' : boolean,
116
+ 'hasReleaseNotes' : boolean,
117
+ }
104
118
  export interface PackageSummary {
105
119
  'ownerInfo' : User,
106
120
  'owner' : Principal,
121
+ 'quality' : PackageQuality,
107
122
  'downloadsTotal' : bigint,
108
123
  'downloadsInLast30Days' : bigint,
109
124
  'downloadsInLast7Days' : bigint,
@@ -113,6 +128,7 @@ export interface PackageSummary {
113
128
  export interface PackageSummaryWithChanges {
114
129
  'ownerInfo' : User,
115
130
  'owner' : Principal,
131
+ 'quality' : PackageQuality,
116
132
  'downloadsTotal' : bigint,
117
133
  'downloadsInLast30Days' : bigint,
118
134
  'downloadsInLast7Days' : bigint,
@@ -123,6 +139,7 @@ export interface PackageSummaryWithChanges {
123
139
  export interface PackageSummaryWithChanges__1 {
124
140
  'ownerInfo' : User,
125
141
  'owner' : Principal,
142
+ 'quality' : PackageQuality,
126
143
  'downloadsTotal' : bigint,
127
144
  'downloadsInLast30Days' : bigint,
128
145
  'downloadsInLast7Days' : bigint,
@@ -133,6 +150,7 @@ export interface PackageSummaryWithChanges__1 {
133
150
  export interface PackageSummary__1 {
134
151
  'ownerInfo' : User,
135
152
  'owner' : Principal,
153
+ 'quality' : PackageQuality,
136
154
  'downloadsTotal' : bigint,
137
155
  'downloadsInLast30Days' : bigint,
138
156
  'downloadsInLast7Days' : bigint,
@@ -173,6 +191,8 @@ export type Result_6 = { 'ok' : Array<[PackageName, PackageVersion]> } |
173
191
  { 'err' : Err };
174
192
  export type Result_7 = { 'ok' : Array<FileId> } |
175
193
  { 'err' : Err };
194
+ export type Result_8 = { 'ok' : Array<[FileId, Uint8Array | number[]]> } |
195
+ { 'err' : Err };
176
196
  export interface Script { 'value' : string, 'name' : string }
177
197
  export type SemverPart = { 'major' : null } |
178
198
  { 'minor' : null } |
@@ -233,6 +253,7 @@ export interface User__1 {
233
253
  export interface _SERVICE {
234
254
  'backup' : ActorMethod<[], undefined>,
235
255
  'claimAirdrop' : ActorMethod<[Principal], string>,
256
+ 'computeHashesForExistingFiles' : ActorMethod<[], undefined>,
236
257
  'diff' : ActorMethod<[string, string], PackageChanges__1>,
237
258
  'finishPublish' : ActorMethod<[PublishingId], Result>,
238
259
  'getAirdropAmount' : ActorMethod<[], bigint>,
@@ -251,6 +272,11 @@ export interface _SERVICE {
251
272
  [PackageName],
252
273
  Array<DownloadsSnapshot__1>
253
274
  >,
275
+ 'getFileHashes' : ActorMethod<[PackageName, PackageVersion], Result_8>,
276
+ 'getFileHashesByPackageIds' : ActorMethod<
277
+ [Array<PackageId>],
278
+ Array<[PackageId, Array<[FileId, Uint8Array | number[]]>]>
279
+ >,
254
280
  'getFileIds' : ActorMethod<[PackageName, PackageVersion], Result_7>,
255
281
  'getHighestSemverBatch' : ActorMethod<
256
282
  [Array<[PackageName, PackageVersion, SemverPart]>],
@@ -28,6 +28,10 @@ export const idlFactory = ({ IDL }) => {
28
28
  'downloads' : IDL.Nat,
29
29
  });
30
30
  const FileId = IDL.Text;
31
+ const Result_8 = IDL.Variant({
32
+ 'ok' : IDL.Vec(IDL.Tuple(FileId, IDL.Vec(IDL.Nat8))),
33
+ 'err' : Err,
34
+ });
31
35
  const Result_7 = IDL.Variant({ 'ok' : IDL.Vec(FileId), 'err' : Err });
32
36
  const SemverPart = IDL.Variant({
33
37
  'major' : IDL.Null,
@@ -51,6 +55,21 @@ export const idlFactory = ({ IDL }) => {
51
55
  'githubVerified' : IDL.Bool,
52
56
  'github' : IDL.Text,
53
57
  });
58
+ const DepsStatus = IDL.Variant({
59
+ 'allLatest' : IDL.Null,
60
+ 'tooOld' : IDL.Null,
61
+ 'updatesAvailable' : IDL.Null,
62
+ });
63
+ const PackageQuality = IDL.Record({
64
+ 'depsStatus' : DepsStatus,
65
+ 'hasDescription' : IDL.Bool,
66
+ 'hasKeywords' : IDL.Bool,
67
+ 'hasLicense' : IDL.Bool,
68
+ 'hasDocumentation' : IDL.Bool,
69
+ 'hasTests' : IDL.Bool,
70
+ 'hasRepository' : IDL.Bool,
71
+ 'hasReleaseNotes' : IDL.Bool,
72
+ });
54
73
  const Script = IDL.Record({ 'value' : IDL.Text, 'name' : IDL.Text });
55
74
  const PackageName__1 = IDL.Text;
56
75
  const DependencyV2 = IDL.Record({
@@ -84,6 +103,7 @@ export const idlFactory = ({ IDL }) => {
84
103
  const PackageSummary = IDL.Record({
85
104
  'ownerInfo' : User,
86
105
  'owner' : IDL.Principal,
106
+ 'quality' : PackageQuality,
87
107
  'downloadsTotal' : IDL.Nat,
88
108
  'downloadsInLast30Days' : IDL.Nat,
89
109
  'downloadsInLast7Days' : IDL.Nat,
@@ -93,6 +113,7 @@ export const idlFactory = ({ IDL }) => {
93
113
  const PackageSummary__1 = IDL.Record({
94
114
  'ownerInfo' : User,
95
115
  'owner' : IDL.Principal,
116
+ 'quality' : PackageQuality,
96
117
  'downloadsTotal' : IDL.Nat,
97
118
  'downloadsInLast30Days' : IDL.Nat,
98
119
  'downloadsInLast7Days' : IDL.Nat,
@@ -121,6 +142,7 @@ export const idlFactory = ({ IDL }) => {
121
142
  const PackageSummaryWithChanges__1 = IDL.Record({
122
143
  'ownerInfo' : User,
123
144
  'owner' : IDL.Principal,
145
+ 'quality' : PackageQuality,
124
146
  'downloadsTotal' : IDL.Nat,
125
147
  'downloadsInLast30Days' : IDL.Nat,
126
148
  'downloadsInLast7Days' : IDL.Nat,
@@ -132,6 +154,7 @@ export const idlFactory = ({ IDL }) => {
132
154
  'ownerInfo' : User,
133
155
  'owner' : IDL.Principal,
134
156
  'deps' : IDL.Vec(PackageSummary__1),
157
+ 'quality' : PackageQuality,
135
158
  'testStats' : TestStats__1,
136
159
  'downloadsTotal' : IDL.Nat,
137
160
  'downloadsInLast30Days' : IDL.Nat,
@@ -149,6 +172,7 @@ export const idlFactory = ({ IDL }) => {
149
172
  const PackageSummaryWithChanges = IDL.Record({
150
173
  'ownerInfo' : User,
151
174
  'owner' : IDL.Principal,
175
+ 'quality' : PackageQuality,
152
176
  'downloadsTotal' : IDL.Nat,
153
177
  'downloadsInLast30Days' : IDL.Nat,
154
178
  'downloadsInLast7Days' : IDL.Nat,
@@ -235,6 +259,7 @@ export const idlFactory = ({ IDL }) => {
235
259
  return IDL.Service({
236
260
  'backup' : IDL.Func([], [], []),
237
261
  'claimAirdrop' : IDL.Func([IDL.Principal], [IDL.Text], []),
262
+ 'computeHashesForExistingFiles' : IDL.Func([], [], []),
238
263
  'diff' : IDL.Func([IDL.Text, IDL.Text], [PackageChanges__1], ['query']),
239
264
  'finishPublish' : IDL.Func([PublishingId], [Result], []),
240
265
  'getAirdropAmount' : IDL.Func([], [IDL.Nat], ['query']),
@@ -256,6 +281,16 @@ export const idlFactory = ({ IDL }) => {
256
281
  [IDL.Vec(DownloadsSnapshot__1)],
257
282
  ['query'],
258
283
  ),
284
+ 'getFileHashes' : IDL.Func([PackageName, PackageVersion], [Result_8], []),
285
+ 'getFileHashesByPackageIds' : IDL.Func(
286
+ [IDL.Vec(PackageId)],
287
+ [
288
+ IDL.Vec(
289
+ IDL.Tuple(PackageId, IDL.Vec(IDL.Tuple(FileId, IDL.Vec(IDL.Nat8))))
290
+ ),
291
+ ],
292
+ [],
293
+ ),
259
294
  'getFileIds' : IDL.Func(
260
295
  [PackageName, PackageVersion],
261
296
  [Result_7],
@@ -0,0 +1,5 @@
1
+ export declare function checkIntegrity(lock?: 'save' | 'check' | 'ignore'): Promise<void>;
2
+ export declare function getLocalFileHash(fileId: string): string;
3
+ export declare function checkRemote(): Promise<void>;
4
+ export declare function saveLockFile(): Promise<void>;
5
+ export declare function checkLockFile(force?: boolean): Promise<void>;
@@ -0,0 +1,155 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { sha256 } from '@noble/hashes/sha256';
4
+ import { bytesToHex } from '@noble/hashes/utils';
5
+ import { getDependencyType, getRootDir, mainActor } from './mops.js';
6
+ import { resolvePackages } from './resolve-packages.js';
7
+ export async function checkIntegrity(lock) {
8
+ let force = !!lock;
9
+ if (!lock) {
10
+ lock = process.env['CI'] ? 'check' : 'ignore';
11
+ }
12
+ if (lock === 'save') {
13
+ await saveLockFile();
14
+ await checkLockFile(force);
15
+ }
16
+ else if (lock === 'check') {
17
+ await checkLockFile(force);
18
+ }
19
+ }
20
+ async function getFileHashesFromRegistry() {
21
+ let packageIds = await getResolvedMopsPackageIds();
22
+ let actor = await mainActor();
23
+ let fileHashesByPackageIds = await actor.getFileHashesByPackageIds(packageIds);
24
+ return fileHashesByPackageIds;
25
+ }
26
+ async function getResolvedMopsPackageIds() {
27
+ let resolvedPackages = await resolvePackages();
28
+ let packageIds = Object.entries(resolvedPackages)
29
+ .filter(([_, version]) => getDependencyType(version) === 'mops')
30
+ .map(([name, version]) => `${name}@${version}`);
31
+ return packageIds;
32
+ }
33
+ // get hash of local file from '.mops' dir by fileId
34
+ export function getLocalFileHash(fileId) {
35
+ let rootDir = getRootDir();
36
+ let file = path.join(rootDir, '.mops', fileId);
37
+ if (!fs.existsSync(file)) {
38
+ console.error(`Missing file ${fileId} in .mops dir`);
39
+ process.exit(1);
40
+ }
41
+ let fileData = fs.readFileSync(file);
42
+ return bytesToHex(sha256(fileData));
43
+ }
44
+ function getMopsTomlHash() {
45
+ return bytesToHex(sha256(fs.readFileSync(getRootDir() + '/mops.toml')));
46
+ }
47
+ // compare hashes of local files with hashes from the registry
48
+ export async function checkRemote() {
49
+ let fileHashesFromRegistry = await getFileHashesFromRegistry();
50
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
51
+ for (let [_packageId, fileHashes] of fileHashesFromRegistry) {
52
+ for (let [fileId, hash] of fileHashes) {
53
+ let remoteHash = new Uint8Array(hash);
54
+ let localHash = getLocalFileHash(fileId);
55
+ if (localHash !== bytesToHex(remoteHash)) {
56
+ console.error('Integrity check failed.');
57
+ console.error(`Mismatched hash for ${fileId}: ${localHash} vs ${bytesToHex(remoteHash)}`);
58
+ process.exit(1);
59
+ }
60
+ }
61
+ }
62
+ }
63
+ export async function saveLockFile() {
64
+ let rootDir = getRootDir();
65
+ let lockFile = path.join(rootDir, 'mops.lock');
66
+ // if lock file exists and mops.toml hasn't changed, don't update it
67
+ if (fs.existsSync(lockFile)) {
68
+ let lockFileJson = JSON.parse(fs.readFileSync(lockFile).toString());
69
+ let mopsTomlHash = getMopsTomlHash();
70
+ if (mopsTomlHash === lockFileJson.mopsTomlHash) {
71
+ return;
72
+ }
73
+ }
74
+ let fileHashes = await getFileHashesFromRegistry();
75
+ let lockFileJson = {
76
+ version: 1,
77
+ mopsTomlHash: getMopsTomlHash(),
78
+ hashes: fileHashes.reduce((acc, [packageId, fileHashes]) => {
79
+ acc[packageId] = fileHashes.reduce((acc, [fileId, hash]) => {
80
+ acc[fileId] = bytesToHex(new Uint8Array(hash));
81
+ return acc;
82
+ }, {});
83
+ return acc;
84
+ }, {}),
85
+ };
86
+ fs.writeFileSync(lockFile, JSON.stringify(lockFileJson, null, 2));
87
+ }
88
+ // compare hashes of local files with hashes from the lock file
89
+ export async function checkLockFile(force = false) {
90
+ let rootDir = getRootDir();
91
+ let lockFile = path.join(rootDir, 'mops.lock');
92
+ // check if lock file exists
93
+ if (!fs.existsSync(lockFile)) {
94
+ if (force) {
95
+ console.error('Missing lock file. Run `mops install` to generate it.');
96
+ process.exit(1);
97
+ }
98
+ return;
99
+ }
100
+ let lockFileJson = JSON.parse(fs.readFileSync(lockFile).toString());
101
+ let packageIds = await getResolvedMopsPackageIds();
102
+ // check lock file version
103
+ if (lockFileJson.version !== 1) {
104
+ console.error('Integrity check failed');
105
+ console.error(`Invalid lock file version: ${lockFileJson.version}. Supported versions: 1`);
106
+ process.exit(1);
107
+ }
108
+ // check mops.toml hash
109
+ if (lockFileJson.mopsTomlHash !== getMopsTomlHash()) {
110
+ console.error('Integrity check failed');
111
+ console.error('Mismatched mops.toml hash');
112
+ console.error(`Locked hash: ${lockFileJson.mopsTomlHash}`);
113
+ console.error(`Actual hash: ${getMopsTomlHash()}`);
114
+ process.exit(1);
115
+ }
116
+ // check number of packages
117
+ if (Object.keys(lockFileJson.hashes).length !== packageIds.length) {
118
+ console.error('Integrity check failed');
119
+ console.error(`Mismatched number of resolved packages: ${JSON.stringify(Object.keys(lockFileJson.hashes).length)} vs ${JSON.stringify(packageIds.length)}`);
120
+ process.exit(1);
121
+ }
122
+ // check if resolved packages are in the lock file
123
+ for (let packageId of packageIds) {
124
+ if (!(packageId in lockFileJson.hashes)) {
125
+ console.error('Integrity check failed');
126
+ console.error(`Missing package ${packageId} in lock file`);
127
+ process.exit(1);
128
+ }
129
+ }
130
+ for (let [packageId, hashes] of Object.entries(lockFileJson.hashes)) {
131
+ // check if package is in resolved packages
132
+ if (!packageIds.includes(packageId)) {
133
+ console.error('Integrity check failed');
134
+ console.error(`Package ${packageId} in lock file but not in resolved packages`);
135
+ process.exit(1);
136
+ }
137
+ for (let [fileId, lockedHash] of Object.entries(hashes)) {
138
+ // check if file belongs to package
139
+ if (!fileId.startsWith(packageId)) {
140
+ console.error('Integrity check failed');
141
+ console.error(`File ${fileId} in lock file does not belong to package ${packageId}`);
142
+ process.exit(1);
143
+ }
144
+ // local file hash vs hash from lock file
145
+ let localHash = getLocalFileHash(fileId);
146
+ if (lockedHash !== localHash) {
147
+ console.error('Integrity check failed');
148
+ console.error(`Mismatched hash for ${fileId}`);
149
+ console.error(`Locked hash: ${lockedHash}`);
150
+ console.error(`Actual hash: ${localHash}`);
151
+ process.exit(1);
152
+ }
153
+ }
154
+ }
155
+ }
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ic-mops",
3
- "version": "0.32.2",
3
+ "version": "0.33.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "mops": "dist/cli.js"
@@ -38,6 +38,7 @@
38
38
  "@dfinity/identity-secp256k1": "^0.18.1",
39
39
  "@dfinity/principal": "^0.18.1",
40
40
  "@iarna/toml": "^2.2.5",
41
+ "@noble/hashes": "1.3.2",
41
42
  "as-table": "^1.0.55",
42
43
  "cacheable-request": "10.2.12",
43
44
  "camelcase": "^7.0.1",
package/dist/vessel.js CHANGED
@@ -120,11 +120,11 @@ export const installFromGithub = async (name, repo, { verbose = false, dep = fal
120
120
  let dir = formatGithubDir(name, repo);
121
121
  let cacheName = `_github/${name}#${branch}` + (commitHash ? `@${commitHash}` : '');
122
122
  if (existsSync(dir)) {
123
- silent || logUpdate(`${dep ? 'Dependency' : 'Installing'} ${repo} (local cache) from Github`);
123
+ silent || logUpdate(`${dep ? 'Dependency' : 'Installing'} ${repo} (local cache)`);
124
124
  }
125
125
  else if (isCached(cacheName)) {
126
126
  await copyCache(cacheName, dir);
127
- silent || logUpdate(`${dep ? 'Dependency' : 'Installing'} ${repo} (global cache) from Github`);
127
+ silent || logUpdate(`${dep ? 'Dependency' : 'Installing'} ${repo} (global cache)`);
128
128
  }
129
129
  else {
130
130
  mkdirSync(dir, { recursive: true });