ic-mops 0.1.3 → 0.1.6

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.
package/cli.js CHANGED
@@ -11,7 +11,7 @@ import {install} from './commands/install.js';
11
11
  import {publish} from './commands/publish.js';
12
12
  import {importPem} from './commands/import-identity.js';
13
13
  import {sources} from './commands/sources.js';
14
- import {getLastVersion, getNetwork, setNetwork} from './mops.js';
14
+ import {checkApiCompatibility, getHighestVersion, getNetwork, setNetwork} from './mops.js';
15
15
  import {whoami} from './commands/whoami.js';
16
16
  import {installAll} from './commands/install-all.js';
17
17
  import logUpdate from 'log-update';
@@ -55,17 +55,22 @@ program
55
55
  config.dependencies = {};
56
56
  }
57
57
 
58
+ let compatible = await checkApiCompatibility();
59
+ if (!compatible) {
60
+ return;
61
+ }
62
+
58
63
  if (!pkg) {
59
64
  installAll(options);
60
65
  }
61
66
  else {
62
- let version = await getLastVersion(pkg);
63
- await install(pkg, version, options.verbose);
67
+ let version = await getHighestVersion(pkg);
68
+ await install(pkg, version, {verbose: options.verbose});
64
69
 
65
70
  config.dependencies[pkg] = version;
66
71
  wirteConfig(config);
67
72
  logUpdate.clear();
68
- console.log(chalk.green('Package installed ') + `${pkg} = '${version}'`);
73
+ console.log(chalk.green('Package installed ') + `${pkg} = "${version}"`);
69
74
  }
70
75
  });
71
76
 
@@ -74,7 +79,10 @@ program
74
79
  .command('publish')
75
80
  .description('Publish package to the mops registry')
76
81
  .action(async () => {
77
- await publish();
82
+ let compatible = await checkApiCompatibility();
83
+ if (compatible) {
84
+ await publish();
85
+ }
78
86
  });
79
87
 
80
88
  // set-network
@@ -1,10 +1,15 @@
1
1
  import fs from 'fs';
2
2
  import chalk from 'chalk';
3
+ import path from 'path';
4
+ import {globalCacheDir} from '../mops.js';
3
5
 
4
6
  export async function importPem(data) {
5
7
  try {
6
- let url = new URL('../identity.pem', import.meta.url);
7
- fs.writeFileSync(url, data);
8
+ if (!fs.existsSync(globalCacheDir)) {
9
+ fs.mkdirSync(globalCacheDir);
10
+ }
11
+ let identityPem = path.resolve(globalCacheDir, 'identity.pem');
12
+ fs.writeFileSync(identityPem, data);
8
13
  console.log(chalk.green('Success'));
9
14
  }
10
15
  catch (err) {
@@ -12,7 +12,7 @@ export async function installAll({verbose} = {}) {
12
12
  let deps = Object.entries(config.dependencies || {});
13
13
 
14
14
  for (let [pkg, ver] of deps) {
15
- await install(pkg, ver, verbose);
15
+ await install(pkg, ver, {verbose});
16
16
  }
17
17
 
18
18
  logUpdate.clear();
@@ -1,16 +1,17 @@
1
1
  import path from 'path';
2
2
  import fs from 'fs';
3
3
  import logUpdate from 'log-update';
4
- import {checkConfigFile, getLastVersion, mainActor, progressBar, readConfig} from '../mops.js';
4
+ import {checkConfigFile, getHighestVersion, mainActor, progressBar, readConfig, storageActor} from '../mops.js';
5
5
  import {parallel} from '../parallel.js';
6
+ import chalk from 'chalk';
6
7
 
7
- export async function install(pkg, version = '', verbose = false, dep = false) {
8
+ export async function install(pkg, version = '', {verbose, silent, dep} = {}) {
8
9
  if (!checkConfigFile()) {
9
10
  return;
10
11
  }
11
12
 
12
13
  if (!version) {
13
- version = await getLastVersion(pkg);
14
+ version = await getHighestVersion(pkg);
14
15
  }
15
16
 
16
17
  let dir = path.join(process.cwd(), '.mops', `${pkg}@${version}`);
@@ -18,44 +19,60 @@ export async function install(pkg, version = '', verbose = false, dep = false) {
18
19
 
19
20
  // cache
20
21
  if (fs.existsSync(dir)) {
21
- logUpdate(`${dep ? 'Dependency' : 'Installing'} ${pkg}@${version} (cache)`);
22
+ silent || logUpdate(`${dep ? 'Dependency' : 'Installing'} ${pkg}@${version} (cache)`);
22
23
  }
23
24
  // no cache
24
25
  else {
25
26
  fs.mkdirSync(dir, {recursive: true});
26
27
 
28
+ if (!dep) {
29
+ actor.notifyInstall(pkg, version);
30
+ }
31
+ let packageDetails = await actor.getPackageDetails(pkg, version);
27
32
  let filesIds = await actor.getFileIds(pkg, version);
33
+ let storage = await storageActor(packageDetails.publication.storage);
28
34
 
29
35
  // progress
30
36
  let total = filesIds.length + 1;
31
37
  let step = 0;
32
38
  let progress = () => {
33
39
  step++;
34
- logUpdate(`${dep ? 'Dependency' : 'Installing'} ${pkg}@${version} ${progressBar(step, total)}`);
40
+ silent || logUpdate(`${dep ? 'Dependency' : 'Installing'} ${pkg}@${version} ${progressBar(step, total)}`);
35
41
  };
36
42
 
37
43
  // download files
38
44
  progress();
39
45
  await parallel(8, filesIds, async (fileId) => {
40
- let file = await actor.getFile(fileId);
41
- fs.mkdirSync(path.join(dir, path.dirname(file.path)), {recursive: true});
42
- fs.writeFileSync(path.join(dir, file.path), Buffer.from(file.content));
46
+ let fileMetaRes = await storage.getFileMeta(fileId);
47
+ if (fileMetaRes.err) {
48
+ console.log(chalk.red('ERR: ') + fileMetaRes.err);
49
+ return;
50
+ }
51
+ let fileMeta = fileMetaRes.ok;
52
+
53
+ let chunks = Array(Number(fileMeta.chunkCount));
54
+ for (let i = 0; i < fileMeta.chunkCount; i++) {
55
+ let chunkRes = await storage.downloadChunk(fileId, i);
56
+ if (chunkRes.err) {
57
+ console.log(chalk.red('ERR: ') + chunkRes.err);
58
+ return;
59
+ }
60
+ chunks[i] = chunkRes.ok;
61
+ }
62
+
63
+ fs.mkdirSync(path.join(dir, path.dirname(fileMeta.path)), {recursive: true});
64
+ fs.writeFileSync(path.join(dir, fileMeta.path), Buffer.from(chunks.flat()));
43
65
  progress();
44
66
  });
45
-
46
- // TODO: slow (update call)
47
- // if (!dep) {
48
- // await actor.notifyInstall(pkg, version);
49
- // }
50
67
  }
51
68
 
52
69
  if (verbose) {
53
- logUpdate.done();
70
+ silent || logUpdate.done();
54
71
  }
55
72
 
56
73
  // install dependencies
57
74
  let config = readConfig(path.join(dir, 'mops.toml'));
58
75
  for (let [name, version] of Object.entries(config.dependencies || {})) {
59
- await install(name, version, verbose, true);
76
+ await install(name, version, {verbose, silent, dep: true});
60
77
  }
61
78
  }
@@ -17,7 +17,7 @@ export async function publish() {
17
17
 
18
18
  // validate
19
19
  for (let key of Object.keys(config)) {
20
- if (!['package', 'dependencies', 'permissions', 'scripts'].includes(key)) {
20
+ if (!['package', 'dependencies', 'scripts'].includes(key)) {
21
21
  console.log(chalk.red('Error: ') + `Unknown config section [${key}]`);
22
22
  return;
23
23
  }
@@ -59,7 +59,6 @@ export async function publish() {
59
59
  'homepage',
60
60
  'readme',
61
61
  'license',
62
- 'isPrivate',
63
62
  'files',
64
63
  'dfx',
65
64
  'moc',
@@ -101,11 +100,6 @@ export async function publish() {
101
100
  return;
102
101
  }
103
102
 
104
- if (config.package.permissions && Object.keys(config.package.permissions).length > 50) {
105
- console.log(chalk.red('Error: ') + 'max permissions is 50');
106
- return;
107
- }
108
-
109
103
  if (config.package.keywords) {
110
104
  for (let keyword of config.package.keywords) {
111
105
  if (keyword.length > 20) {
@@ -135,7 +129,6 @@ export async function publish() {
135
129
  documentation: config.package.documentation || '',
136
130
  readme: 'README.md',
137
131
  license: config.package.license || '',
138
- isPrivate: false,
139
132
  owner: getIdentity()?.getPrincipal() || Principal.anonymous(),
140
133
  dfx: config.package.dfx || '',
141
134
  moc: config.package.moc || '',
@@ -143,7 +136,6 @@ export async function publish() {
143
136
  dependencies: (Object.entries(config.dependencies || {})).map(([name, version]) => {
144
137
  return {name, version};
145
138
  }),
146
- permissions: [],
147
139
  scripts: [],
148
140
  };
149
141
 
@@ -196,8 +188,27 @@ export async function publish() {
196
188
  // upload files
197
189
  await parallel(8, files, async (file) => {
198
190
  progress();
191
+
192
+ let chunkSize = 1024 * 1024 + 512 * 1024; // 1.5mb
199
193
  let content = fs.readFileSync(file);
200
- await actor.uploadFile(puiblishingId, file, Array.from(content));
194
+ let chunkCount = Math.ceil(content.length / chunkSize);
195
+ let firstChunk = Array.from(content.slice(0, chunkSize));
196
+
197
+ let res = await actor.startFileUpload(puiblishingId, file, chunkCount, firstChunk);
198
+ if (res.err) {
199
+ console.log(chalk.red('Error: ') + res.err);
200
+ return;
201
+ }
202
+ let fileId = res.ok;
203
+
204
+ for (let i = 1, start = i * chunkSize; start < content.length; i++) {
205
+ let chunk = Array.from(content.slice(start, start + chunkSize));
206
+ let res = await actor.uploadFileChunk(puiblishingId, fileId, i, chunk);
207
+ if (res.err) {
208
+ console.log(chalk.red('Error: ') + res.err);
209
+ return;
210
+ }
211
+ }
201
212
  });
202
213
 
203
214
  // finish
@@ -2,7 +2,7 @@ import path from 'path';
2
2
  import fs from 'fs';
3
3
  import chalk from 'chalk';
4
4
  import {install} from './install.js';
5
- import {getLastVersion, readConfig} from '../mops.js';
5
+ import {getHighestVersion, readConfig} from '../mops.js';
6
6
 
7
7
  function rootDir(cwd = process.cwd()) {
8
8
  let configFile = path.join(cwd, 'mops.toml');
@@ -73,8 +73,8 @@ export async function sources({verbose} = {}) {
73
73
  packages['base-unofficial'] = downloadedPackages['base-unofficial'];
74
74
  }
75
75
  else {
76
- let version = await getLastVersion('base-unofficial');
77
- await install('base-unofficial', version, false, true);
76
+ let version = await getHighestVersion('base-unofficial');
77
+ await install('base-unofficial', version, {silent: true, dep: true});
78
78
  packages['base-unofficial'] = version;
79
79
  }
80
80
  }
@@ -13,20 +13,20 @@ export async function uninstall(pkg, version) {
13
13
  let pkgDir = path.join(process.cwd(), '.mops', `${pkg}@${version}`);
14
14
 
15
15
  if (!fs.existsSync(pkgDir)) {
16
- console.log(`No cache to remove ${pkg} = '${version}'`);
16
+ console.log(`No cache to remove ${pkg} = "${version}"`);
17
17
  return;
18
18
  }
19
19
 
20
20
  // don't remove if there are dependents
21
21
  // let dependents = getDependents(pkg, version);
22
22
  // if (dependents.length) {
23
- // console.log(`Cache left ${pkg} = '${version}' (dependents: ${dependents})`)
23
+ // console.log(`Cache left ${pkg} = "${version}" (dependents: ${dependents})`)
24
24
  // return;
25
25
  // }
26
26
 
27
27
  del.sync([`${pkgDir}/**`]);
28
28
 
29
- console.log(chalk.green('Package removed ') + `${pkg} = '${version}'`);
29
+ console.log(chalk.green('Package removed ') + `${pkg} = "${version}"`);
30
30
 
31
31
  // remove dependencies
32
32
  // let text = fs.readFileSync(path.join(pkgDir, 'mops.toml')).toString();
@@ -1,9 +1,10 @@
1
1
  import chalk from 'chalk';
2
2
  import fs from 'fs';
3
- import {getIdentity} from '../mops.js';
3
+ import path from 'path';
4
+ import {getIdentity, globalCacheDir} from '../mops.js';
4
5
 
5
6
  export function whoami() {
6
- let identityPem = new URL('../identity.pem', import.meta.url);
7
+ let identityPem = path.resolve(globalCacheDir, 'identity.pem');
7
8
  if (fs.existsSync(identityPem)) {
8
9
  let identity = getIdentity();
9
10
  console.log(identity.getPrincipal().toText());
@@ -1,11 +1,23 @@
1
1
  type Version = text;
2
2
  type Time = int;
3
3
  type Text = text;
4
+ type StorageStats =
5
+ record {
6
+ cyclesBalance: nat;
7
+ fileCount: nat;
8
+ memorySize: nat;
9
+ };
10
+ type StorageId = principal;
4
11
  type Script =
5
12
  record {
6
13
  name: text;
7
14
  value: text;
8
15
  };
16
+ type Result_2 =
17
+ variant {
18
+ err: Err;
19
+ ok: FileId;
20
+ };
9
21
  type Result_1 =
10
22
  variant {
11
23
  err: PublishingErr;
@@ -18,23 +30,39 @@ type Result =
18
30
  };
19
31
  type PublishingId = text;
20
32
  type PublishingErr = text;
21
- type Permission =
33
+ type PackagePublication =
22
34
  record {
23
- access: Access;
35
+ storage: principal;
36
+ time: Time;
24
37
  user: principal;
25
38
  };
26
- type PackageSummary =
39
+ type PackageName__1 = text;
40
+ type PackageName = text;
41
+ type PackageDetails =
27
42
  record {
28
- description: text;
43
+ config: PackageConfig__1;
29
44
  downloadsInLast30Days: nat;
30
45
  downloadsTotal: nat;
46
+ owner: principal;
47
+ publication: PackagePublication;
48
+ };
49
+ type PackageConfig__1 =
50
+ record {
51
+ dependencies: vec Dependency;
52
+ description: text;
53
+ dfx: text;
54
+ documentation: text;
55
+ donation: text;
56
+ homepage: text;
31
57
  keywords: vec text;
58
+ license: text;
59
+ moc: text;
32
60
  name: PackageName;
33
- updatedAt: Time;
61
+ readme: text;
62
+ repository: text;
63
+ scripts: vec Script;
34
64
  version: text;
35
65
  };
36
- type PackageName__1 = text;
37
- type PackageName = text;
38
66
  type PackageConfig =
39
67
  record {
40
68
  dependencies: vec Dependency;
@@ -43,48 +71,36 @@ type PackageConfig =
43
71
  documentation: text;
44
72
  donation: text;
45
73
  homepage: text;
46
- isPrivate: bool;
47
74
  keywords: vec text;
48
75
  license: text;
49
76
  moc: text;
50
77
  name: PackageName;
51
- owner: principal;
52
- permissions: vec Permission;
53
78
  readme: text;
54
79
  repository: text;
55
80
  scripts: vec Script;
56
81
  version: text;
57
82
  };
58
83
  type FileId = text;
59
- type File =
60
- record {
61
- content: blob;
62
- id: FileId;
63
- path: Text;
64
- };
65
84
  type Err = text;
66
85
  type Dependency =
67
86
  record {
68
87
  name: PackageName;
69
88
  version: text;
70
89
  };
71
- type Access =
72
- variant {
73
- readOnly;
74
- readWrite;
75
- };
76
90
  service : {
77
91
  finishPublish: (PublishingId) -> (Result);
78
- getConfig: (PackageName__1, Version) -> (PackageConfig) query;
79
- getFile: (FileId) -> (File) query;
92
+ getApiVersion: () -> (Text) query;
80
93
  getFileIds: (PackageName__1, Version) -> (vec FileId) query;
81
- getLastConfig: (PackageName__1) -> (PackageConfig) query;
82
- getLastVersion: (PackageName__1) -> (Version) query;
83
- getReadmeFile: (PackageName__1, Version) -> (File) query;
84
- getRecentlyUpdatedPackages: () -> (vec PackageSummary) query;
94
+ getHighestVersion: (PackageName__1) -> (Version) query;
95
+ getPackageDetails: (PackageName__1, Version) -> (PackageDetails) query;
96
+ getRecentlyUpdatedPackages: () -> (vec PackageDetails) query;
97
+ getStoragesStats: () -> (vec record {
98
+ StorageId;
99
+ StorageStats;
100
+ }) query;
85
101
  notifyInstall: (PackageName__1, Version) -> () oneway;
86
- search: (Text) -> (vec PackageSummary) query;
102
+ search: (Text) -> (vec PackageDetails) query;
103
+ startFileUpload: (PublishingId, Text, nat, blob) -> (Result_2);
87
104
  startPublish: (PackageConfig) -> (Result_1);
88
- uploadFile: (PublishingId, Text, blob) -> (Result);
89
- whoami: () -> (Text) query;
105
+ uploadFileChunk: (PublishingId, FileId, nat, blob) -> (Result);
90
106
  }
@@ -1,22 +1,13 @@
1
1
  import type { Principal } from '@dfinity/principal';
2
2
  import type { ActorMethod } from '@dfinity/agent';
3
3
 
4
- export type Access = { 'readOnly' : null } |
5
- { 'readWrite' : null };
6
4
  export interface Dependency { 'name' : PackageName, 'version' : string }
7
5
  export type Err = string;
8
- export interface File {
9
- 'id' : FileId,
10
- 'content' : Array<number>,
11
- 'path' : Text,
12
- }
13
6
  export type FileId = string;
14
7
  export interface PackageConfig {
15
8
  'dfx' : string,
16
9
  'moc' : string,
17
- 'permissions' : Array<Permission>,
18
10
  'scripts' : Array<Script>,
19
- 'owner' : Principal,
20
11
  'documentation' : string,
21
12
  'name' : PackageName,
22
13
  'homepage' : string,
@@ -24,46 +15,76 @@ export interface PackageConfig {
24
15
  'version' : string,
25
16
  'keywords' : Array<string>,
26
17
  'donation' : string,
27
- 'isPrivate' : boolean,
28
18
  'repository' : string,
29
19
  'dependencies' : Array<Dependency>,
30
20
  'license' : string,
31
21
  'readme' : string,
32
22
  }
33
- export type PackageName = string;
34
- export type PackageName__1 = string;
35
- export interface PackageSummary {
23
+ export interface PackageConfig__1 {
24
+ 'dfx' : string,
25
+ 'moc' : string,
26
+ 'scripts' : Array<Script>,
27
+ 'documentation' : string,
36
28
  'name' : PackageName,
37
- 'downloadsTotal' : bigint,
38
- 'downloadsInLast30Days' : bigint,
29
+ 'homepage' : string,
39
30
  'description' : string,
40
31
  'version' : string,
41
32
  'keywords' : Array<string>,
42
- 'updatedAt' : Time,
33
+ 'donation' : string,
34
+ 'repository' : string,
35
+ 'dependencies' : Array<Dependency>,
36
+ 'license' : string,
37
+ 'readme' : string,
38
+ }
39
+ export interface PackageDetails {
40
+ 'owner' : Principal,
41
+ 'downloadsTotal' : bigint,
42
+ 'downloadsInLast30Days' : bigint,
43
+ 'config' : PackageConfig__1,
44
+ 'publication' : PackagePublication,
45
+ }
46
+ export type PackageName = string;
47
+ export type PackageName__1 = string;
48
+ export interface PackagePublication {
49
+ 'storage' : Principal,
50
+ 'time' : Time,
51
+ 'user' : Principal,
43
52
  }
44
- export interface Permission { 'access' : Access, 'user' : Principal }
45
53
  export type PublishingErr = string;
46
54
  export type PublishingId = string;
47
55
  export type Result = { 'ok' : null } |
48
56
  { 'err' : Err };
49
57
  export type Result_1 = { 'ok' : PublishingId } |
50
58
  { 'err' : PublishingErr };
59
+ export type Result_2 = { 'ok' : FileId } |
60
+ { 'err' : Err };
51
61
  export interface Script { 'value' : string, 'name' : string }
62
+ export type StorageId = Principal;
63
+ export interface StorageStats {
64
+ 'fileCount' : bigint,
65
+ 'cyclesBalance' : bigint,
66
+ 'memorySize' : bigint,
67
+ }
52
68
  export type Text = string;
53
69
  export type Time = bigint;
54
70
  export type Version = string;
55
71
  export interface _SERVICE {
56
72
  'finishPublish' : ActorMethod<[PublishingId], Result>,
57
- 'getConfig' : ActorMethod<[PackageName__1, Version], PackageConfig>,
58
- 'getFile' : ActorMethod<[FileId], File>,
73
+ 'getApiVersion' : ActorMethod<[], Text>,
59
74
  'getFileIds' : ActorMethod<[PackageName__1, Version], Array<FileId>>,
60
- 'getLastConfig' : ActorMethod<[PackageName__1], PackageConfig>,
61
- 'getLastVersion' : ActorMethod<[PackageName__1], Version>,
62
- 'getReadmeFile' : ActorMethod<[PackageName__1, Version], File>,
63
- 'getRecentlyUpdatedPackages' : ActorMethod<[], Array<PackageSummary>>,
75
+ 'getHighestVersion' : ActorMethod<[PackageName__1], Version>,
76
+ 'getPackageDetails' : ActorMethod<[PackageName__1, Version], PackageDetails>,
77
+ 'getRecentlyUpdatedPackages' : ActorMethod<[], Array<PackageDetails>>,
78
+ 'getStoragesStats' : ActorMethod<[], Array<[StorageId, StorageStats]>>,
64
79
  'notifyInstall' : ActorMethod<[PackageName__1, Version], undefined>,
65
- 'search' : ActorMethod<[Text], Array<PackageSummary>>,
80
+ 'search' : ActorMethod<[Text], Array<PackageDetails>>,
81
+ 'startFileUpload' : ActorMethod<
82
+ [PublishingId, Text, bigint, Array<number>],
83
+ Result_2,
84
+ >,
66
85
  'startPublish' : ActorMethod<[PackageConfig], Result_1>,
67
- 'uploadFile' : ActorMethod<[PublishingId, Text, Array<number>], Result>,
68
- 'whoami' : ActorMethod<[], Text>,
86
+ 'uploadFileChunk' : ActorMethod<
87
+ [PublishingId, FileId, bigint, Array<number>],
88
+ Result,
89
+ >,
69
90
  }
@@ -2,19 +2,17 @@ export const idlFactory = ({ IDL }) => {
2
2
  const PublishingId = IDL.Text;
3
3
  const Err = IDL.Text;
4
4
  const Result = IDL.Variant({ 'ok' : IDL.Null, 'err' : Err });
5
+ const Text = IDL.Text;
5
6
  const PackageName__1 = IDL.Text;
6
7
  const Version = IDL.Text;
7
- const Access = IDL.Variant({ 'readOnly' : IDL.Null, 'readWrite' : IDL.Null });
8
- const Permission = IDL.Record({ 'access' : Access, 'user' : IDL.Principal });
8
+ const FileId = IDL.Text;
9
9
  const Script = IDL.Record({ 'value' : IDL.Text, 'name' : IDL.Text });
10
10
  const PackageName = IDL.Text;
11
11
  const Dependency = IDL.Record({ 'name' : PackageName, 'version' : IDL.Text });
12
- const PackageConfig = IDL.Record({
12
+ const PackageConfig__1 = IDL.Record({
13
13
  'dfx' : IDL.Text,
14
14
  'moc' : IDL.Text,
15
- 'permissions' : IDL.Vec(Permission),
16
15
  'scripts' : IDL.Vec(Script),
17
- 'owner' : IDL.Principal,
18
16
  'documentation' : IDL.Text,
19
17
  'name' : PackageName,
20
18
  'homepage' : IDL.Text,
@@ -22,61 +20,86 @@ export const idlFactory = ({ IDL }) => {
22
20
  'version' : IDL.Text,
23
21
  'keywords' : IDL.Vec(IDL.Text),
24
22
  'donation' : IDL.Text,
25
- 'isPrivate' : IDL.Bool,
26
23
  'repository' : IDL.Text,
27
24
  'dependencies' : IDL.Vec(Dependency),
28
25
  'license' : IDL.Text,
29
26
  'readme' : IDL.Text,
30
27
  });
31
- const FileId = IDL.Text;
32
- const Text = IDL.Text;
33
- const File = IDL.Record({
34
- 'id' : FileId,
35
- 'content' : IDL.Vec(IDL.Nat8),
36
- 'path' : Text,
37
- });
38
28
  const Time = IDL.Int;
39
- const PackageSummary = IDL.Record({
40
- 'name' : PackageName,
29
+ const PackagePublication = IDL.Record({
30
+ 'storage' : IDL.Principal,
31
+ 'time' : Time,
32
+ 'user' : IDL.Principal,
33
+ });
34
+ const PackageDetails = IDL.Record({
35
+ 'owner' : IDL.Principal,
41
36
  'downloadsTotal' : IDL.Nat,
42
37
  'downloadsInLast30Days' : IDL.Nat,
38
+ 'config' : PackageConfig__1,
39
+ 'publication' : PackagePublication,
40
+ });
41
+ const StorageId = IDL.Principal;
42
+ const StorageStats = IDL.Record({
43
+ 'fileCount' : IDL.Nat,
44
+ 'cyclesBalance' : IDL.Nat,
45
+ 'memorySize' : IDL.Nat,
46
+ });
47
+ const Result_2 = IDL.Variant({ 'ok' : FileId, 'err' : Err });
48
+ const PackageConfig = IDL.Record({
49
+ 'dfx' : IDL.Text,
50
+ 'moc' : IDL.Text,
51
+ 'scripts' : IDL.Vec(Script),
52
+ 'documentation' : IDL.Text,
53
+ 'name' : PackageName,
54
+ 'homepage' : IDL.Text,
43
55
  'description' : IDL.Text,
44
56
  'version' : IDL.Text,
45
57
  'keywords' : IDL.Vec(IDL.Text),
46
- 'updatedAt' : Time,
58
+ 'donation' : IDL.Text,
59
+ 'repository' : IDL.Text,
60
+ 'dependencies' : IDL.Vec(Dependency),
61
+ 'license' : IDL.Text,
62
+ 'readme' : IDL.Text,
47
63
  });
48
64
  const PublishingErr = IDL.Text;
49
65
  const Result_1 = IDL.Variant({ 'ok' : PublishingId, 'err' : PublishingErr });
50
66
  return IDL.Service({
51
67
  'finishPublish' : IDL.Func([PublishingId], [Result], []),
52
- 'getConfig' : IDL.Func(
68
+ 'getApiVersion' : IDL.Func([], [Text], ['query']),
69
+ 'getFileIds' : IDL.Func(
53
70
  [PackageName__1, Version],
54
- [PackageConfig],
71
+ [IDL.Vec(FileId)],
55
72
  ['query'],
56
73
  ),
57
- 'getFile' : IDL.Func([FileId], [File], ['query']),
58
- 'getFileIds' : IDL.Func(
74
+ 'getHighestVersion' : IDL.Func([PackageName__1], [Version], ['query']),
75
+ 'getPackageDetails' : IDL.Func(
59
76
  [PackageName__1, Version],
60
- [IDL.Vec(FileId)],
77
+ [PackageDetails],
61
78
  ['query'],
62
79
  ),
63
- 'getLastConfig' : IDL.Func([PackageName__1], [PackageConfig], ['query']),
64
- 'getLastVersion' : IDL.Func([PackageName__1], [Version], ['query']),
65
- 'getReadmeFile' : IDL.Func([PackageName__1, Version], [File], ['query']),
66
80
  'getRecentlyUpdatedPackages' : IDL.Func(
67
81
  [],
68
- [IDL.Vec(PackageSummary)],
82
+ [IDL.Vec(PackageDetails)],
83
+ ['query'],
84
+ ),
85
+ 'getStoragesStats' : IDL.Func(
86
+ [],
87
+ [IDL.Vec(IDL.Tuple(StorageId, StorageStats))],
69
88
  ['query'],
70
89
  ),
71
90
  'notifyInstall' : IDL.Func([PackageName__1, Version], [], ['oneway']),
72
- 'search' : IDL.Func([Text], [IDL.Vec(PackageSummary)], ['query']),
91
+ 'search' : IDL.Func([Text], [IDL.Vec(PackageDetails)], ['query']),
92
+ 'startFileUpload' : IDL.Func(
93
+ [PublishingId, Text, IDL.Nat, IDL.Vec(IDL.Nat8)],
94
+ [Result_2],
95
+ [],
96
+ ),
73
97
  'startPublish' : IDL.Func([PackageConfig], [Result_1], []),
74
- 'uploadFile' : IDL.Func(
75
- [PublishingId, Text, IDL.Vec(IDL.Nat8)],
98
+ 'uploadFileChunk' : IDL.Func(
99
+ [PublishingId, FileId, IDL.Nat, IDL.Vec(IDL.Nat8)],
76
100
  [Result],
77
101
  [],
78
102
  ),
79
- 'whoami' : IDL.Func([], [Text], ['query']),
80
103
  });
81
104
  };
82
105
  export const init = ({ IDL }) => { return []; };
@@ -0,0 +1,38 @@
1
+ import { Actor, HttpAgent } from "@dfinity/agent";
2
+
3
+ // Imports and re-exports candid interface
4
+ import { idlFactory } from './storage.did.js';
5
+ export { idlFactory } from './storage.did.js';
6
+ // CANISTER_ID is replaced by webpack based on node environment
7
+ export const canisterId = process.env.STORAGE_CANISTER_ID;
8
+
9
+ /**
10
+ *
11
+ * @param {string | import("@dfinity/principal").Principal} canisterId Canister ID of Agent
12
+ * @param {{agentOptions?: import("@dfinity/agent").HttpAgentOptions; actorOptions?: import("@dfinity/agent").ActorConfig}} [options]
13
+ * @return {import("@dfinity/agent").ActorSubclass<import("./storage.did.js")._SERVICE>}
14
+ */
15
+ export const createActor = (canisterId, options) => {
16
+ const agent = new HttpAgent({ ...options?.agentOptions });
17
+
18
+ // Fetch root key for certificate validation during development
19
+ if(process.env.NODE_ENV !== "production") {
20
+ agent.fetchRootKey().catch(err=>{
21
+ console.warn("Unable to fetch root key. Check to ensure that your local replica is running");
22
+ console.error(err);
23
+ });
24
+ }
25
+
26
+ // Creates an actor with using the candid interface and the HttpAgent
27
+ return Actor.createActor(idlFactory, {
28
+ agent,
29
+ canisterId,
30
+ ...options?.actorOptions,
31
+ });
32
+ };
33
+
34
+ /**
35
+ * A ready-to-use agent for the storage canister
36
+ * @type {import("@dfinity/agent").ActorSubclass<import("./storage.did.js")._SERVICE>}
37
+ */
38
+ export const storage = createActor(canisterId);
@@ -0,0 +1,46 @@
1
+ type StorageStats =
2
+ record {
3
+ cyclesBalance: nat;
4
+ fileCount: nat;
5
+ memorySize: nat;
6
+ };
7
+ type Storage =
8
+ service {
9
+ acceptCycles: () -> ();
10
+ deleteFile: (FileId) -> ();
11
+ downloadChunk: (FileId, nat) -> (Result_2) query;
12
+ finishUpload: (FileId) -> ();
13
+ getFileIdsRange: (nat, nat) -> (vec FileId) query;
14
+ getFileMeta: (FileId) -> (Result_1) query;
15
+ getStats: () -> (StorageStats) query;
16
+ startUpload: (FileMeta) -> (Result);
17
+ updateFileOwners: (FileId, vec principal) -> ();
18
+ uploadChunk: (FileId, nat, Chunk) -> ();
19
+ };
20
+ type Result_2 =
21
+ variant {
22
+ err: Err;
23
+ ok: Chunk;
24
+ };
25
+ type Result_1 =
26
+ variant {
27
+ err: Err;
28
+ ok: FileMeta;
29
+ };
30
+ type Result =
31
+ variant {
32
+ err: Err;
33
+ ok;
34
+ };
35
+ type FileMeta =
36
+ record {
37
+ chunkCount: nat;
38
+ id: FileId__1;
39
+ owners: vec principal;
40
+ path: text;
41
+ };
42
+ type FileId__1 = text;
43
+ type FileId = text;
44
+ type Err = text;
45
+ type Chunk = blob;
46
+ service : () -> Storage
@@ -0,0 +1,37 @@
1
+ import type { Principal } from '@dfinity/principal';
2
+ import type { ActorMethod } from '@dfinity/agent';
3
+
4
+ export type Chunk = Array<number>;
5
+ export type Err = string;
6
+ export type FileId = string;
7
+ export type FileId__1 = string;
8
+ export interface FileMeta {
9
+ 'id' : FileId__1,
10
+ 'owners' : Array<Principal>,
11
+ 'path' : string,
12
+ 'chunkCount' : bigint,
13
+ }
14
+ export type Result = { 'ok' : null } |
15
+ { 'err' : Err };
16
+ export type Result_1 = { 'ok' : FileMeta } |
17
+ { 'err' : Err };
18
+ export type Result_2 = { 'ok' : Chunk } |
19
+ { 'err' : Err };
20
+ export interface Storage {
21
+ 'acceptCycles' : ActorMethod<[], undefined>,
22
+ 'deleteFile' : ActorMethod<[FileId], undefined>,
23
+ 'downloadChunk' : ActorMethod<[FileId, bigint], Result_2>,
24
+ 'finishUpload' : ActorMethod<[FileId], undefined>,
25
+ 'getFileIdsRange' : ActorMethod<[bigint, bigint], Array<FileId>>,
26
+ 'getFileMeta' : ActorMethod<[FileId], Result_1>,
27
+ 'getStats' : ActorMethod<[], StorageStats>,
28
+ 'startUpload' : ActorMethod<[FileMeta], Result>,
29
+ 'updateFileOwners' : ActorMethod<[FileId, Array<Principal>], undefined>,
30
+ 'uploadChunk' : ActorMethod<[FileId, bigint, Chunk], undefined>,
31
+ }
32
+ export interface StorageStats {
33
+ 'fileCount' : bigint,
34
+ 'cyclesBalance' : bigint,
35
+ 'memorySize' : bigint,
36
+ }
37
+ export interface _SERVICE extends Storage {}
@@ -0,0 +1,38 @@
1
+ export const idlFactory = ({ IDL }) => {
2
+ const FileId = IDL.Text;
3
+ const Chunk = IDL.Vec(IDL.Nat8);
4
+ const Err = IDL.Text;
5
+ const Result_2 = IDL.Variant({ 'ok' : Chunk, 'err' : Err });
6
+ const FileId__1 = IDL.Text;
7
+ const FileMeta = IDL.Record({
8
+ 'id' : FileId__1,
9
+ 'owners' : IDL.Vec(IDL.Principal),
10
+ 'path' : IDL.Text,
11
+ 'chunkCount' : IDL.Nat,
12
+ });
13
+ const Result_1 = IDL.Variant({ 'ok' : FileMeta, 'err' : Err });
14
+ const StorageStats = IDL.Record({
15
+ 'fileCount' : IDL.Nat,
16
+ 'cyclesBalance' : IDL.Nat,
17
+ 'memorySize' : IDL.Nat,
18
+ });
19
+ const Result = IDL.Variant({ 'ok' : IDL.Null, 'err' : Err });
20
+ const Storage = IDL.Service({
21
+ 'acceptCycles' : IDL.Func([], [], []),
22
+ 'deleteFile' : IDL.Func([FileId], [], []),
23
+ 'downloadChunk' : IDL.Func([FileId, IDL.Nat], [Result_2], ['query']),
24
+ 'finishUpload' : IDL.Func([FileId], [], []),
25
+ 'getFileIdsRange' : IDL.Func(
26
+ [IDL.Nat, IDL.Nat],
27
+ [IDL.Vec(FileId)],
28
+ ['query'],
29
+ ),
30
+ 'getFileMeta' : IDL.Func([FileId], [Result_1], ['query']),
31
+ 'getStats' : IDL.Func([], [StorageStats], ['query']),
32
+ 'startUpload' : IDL.Func([FileMeta], [Result], []),
33
+ 'updateFileOwners' : IDL.Func([FileId, IDL.Vec(IDL.Principal)], [], []),
34
+ 'uploadChunk' : IDL.Func([FileId, IDL.Nat, Chunk], [], []),
35
+ });
36
+ return Storage;
37
+ };
38
+ export const init = ({ IDL }) => { return []; };
package/mops.js CHANGED
@@ -6,13 +6,19 @@ import path from 'path';
6
6
  import fs from 'fs';
7
7
 
8
8
  import {idlFactory} from './declarations/main/main.did.js';
9
+ import {idlFactory as storageIdlFactory} from './declarations/storage/storage.did.js';
9
10
  import {decodeFile} from './pem.js';
10
11
 
11
12
 
12
13
  global.fetch = fetch;
13
14
 
15
+ // (!) make changes in pair with backend
16
+ let apiVersion = '0.1';
17
+
14
18
  let networkFile = new URL('./network.txt', import.meta.url);
15
19
 
20
+ export let globalCacheDir = path.resolve(process.env.HOME || process.env.APPDATA, 'mops');
21
+
16
22
  export function setNetwork(network) {
17
23
  fs.writeFileSync(networkFile, network);
18
24
  }
@@ -41,7 +47,7 @@ export function getNetwork() {
41
47
  }
42
48
 
43
49
  export let getIdentity = () => {
44
- let identityPem = new URL('./identity.pem', import.meta.url);
50
+ let identityPem = path.resolve(globalCacheDir, 'identity.pem');
45
51
  if (fs.existsSync(identityPem)) {
46
52
  return decodeFile(identityPem);
47
53
  }
@@ -65,6 +71,23 @@ export let mainActor = async () => {
65
71
  });
66
72
  };
67
73
 
74
+ export let storageActor = async (storageId) => {
75
+ let network = getNetwork().network;
76
+ let host = getNetwork().host;
77
+
78
+ let identity = getIdentity();
79
+ let agent = new HttpAgent({host, identity});
80
+
81
+ if (network === 'local') {
82
+ await agent.fetchRootKey();
83
+ }
84
+
85
+ return Actor.createActor(storageIdlFactory, {
86
+ agent,
87
+ canisterId: storageId,
88
+ });
89
+ };
90
+
68
91
  export function checkConfigFile() {
69
92
  let configFile = path.join(process.cwd(), 'mops.toml');
70
93
  if (!fs.existsSync(configFile)) {
@@ -79,12 +102,31 @@ export function progressBar(step, total) {
79
102
  return `[${':'.repeat(done)}${' '.repeat(10 - done)}]`;
80
103
  }
81
104
 
82
- export async function getLastVersion(pkgName) {
105
+ export async function getHighestVersion(pkgName) {
83
106
  let actor = await mainActor();
84
- return actor.getLastVersion(pkgName);
107
+ return actor.getHighestVersion(pkgName);
85
108
  }
86
109
 
87
110
  export function readConfig(configFile = path.join(process.cwd(), 'mops.toml')) {
88
111
  let text = fs.readFileSync(configFile).toString();
89
112
  return TOML.parse(text);
113
+ }
114
+
115
+ // warn on minor mismatch
116
+ // err on major mismatch
117
+ export async function checkApiCompatibility() {
118
+ let actor = await mainActor();
119
+ let backendApiVer = await actor.getApiVersion();
120
+ if (backendApiVer.split('.')[0] !== apiVersion.split('.')[0]) {
121
+ console.log(chalk.red('ERR: ') + `CLI incompatible with backend. CLI v${apiVersion}, Backend v${backendApiVer}`);
122
+ console.log('Run ' + chalk.greenBright('npm i -g ic-mops') + ' to upgrade cli.');
123
+ return false;
124
+ }
125
+ else if (backendApiVer.split('.')[1] !== apiVersion.split('.')[1]) {
126
+ console.log('-'.repeat(50));
127
+ console.log(chalk.yellow('WARN: ') + `CLI probably incompatible with backend. CLI v${apiVersion}, Backend v${backendApiVer}`);
128
+ console.log('Recommended to run ' + chalk.greenBright('npm i -g ic-mops') + ' to upgrade cli.');
129
+ console.log('-'.repeat(50));
130
+ }
131
+ return true;
90
132
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ic-mops",
3
- "version": "0.1.3",
3
+ "version": "0.1.6",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "mops": "cli.js"