ic-mops 0.1.12 → 0.2.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.
package/.eslintrc.json CHANGED
File without changes
package/README.md CHANGED
File without changes
package/cli.js CHANGED
@@ -4,25 +4,21 @@ import fs from 'fs';
4
4
  import path from 'path';
5
5
  import {program} from 'commander';
6
6
  import chalk from 'chalk';
7
- import TOML from '@iarna/toml';
8
7
 
9
8
  import {init} from './commands/init.js';
10
9
  import {install} from './commands/install.js';
11
10
  import {publish} from './commands/publish.js';
12
11
  import {importPem} from './commands/import-identity.js';
13
12
  import {sources} from './commands/sources.js';
14
- import {checkApiCompatibility, getHighestVersion, getNetwork, setNetwork} from './mops.js';
13
+ import {checkApiCompatibility, getHighestVersion, getNetwork, parseGithubURL, readConfig, setNetwork, writeConfig} from './mops.js';
15
14
  import {whoami} from './commands/whoami.js';
16
15
  import {installAll} from './commands/install-all.js';
17
16
  import logUpdate from 'log-update';
17
+ import { installFromGithub } from './vessel.js';
18
18
 
19
19
  let cwd = process.cwd();
20
20
  let configFile = path.join(cwd, 'mops.toml');
21
21
 
22
- function wirteConfig(config) {
23
- fs.writeFileSync(configFile, TOML.stringify(config).trim());
24
- }
25
-
26
22
  program.name('mops');
27
23
 
28
24
  // init
@@ -44,8 +40,7 @@ program
44
40
  let config = {};
45
41
  let exists = fs.existsSync(configFile);
46
42
  if (exists) {
47
- let text = fs.readFileSync(configFile).toString();
48
- config = TOML.parse(text);
43
+ config = readConfig(configFile);
49
44
  }
50
45
  else {
51
46
  console.log(chalk.red('Error: ') + `mops.toml not found. Please run ${chalk.green('mops init')} first`);
@@ -62,22 +57,63 @@ program
62
57
 
63
58
  if (!pkg) {
64
59
  installAll(options);
60
+ return;
65
61
  }
66
- else {
62
+
63
+ let pkgDetails;
64
+ let existingPkg = config.dependencies[pkg];
65
+
66
+ if (pkg.startsWith('https://github.com') || pkg.split('/') > 1){
67
+ const {org, gitName, branch} = parseGithubURL(pkg);
68
+
69
+ pkgDetails = {
70
+ name: parseGithubURL(pkg).gitName,
71
+ repo: `https://github.com/${org}/${gitName}#${branch}`,
72
+ version: ''
73
+ };
74
+
75
+ existingPkg = config.dependencies[pkgDetails.name];
76
+
77
+ }else if (!existingPkg || !existingPkg.repo){
67
78
  let versionRes = await getHighestVersion(pkg);
68
79
  if (versionRes.err) {
69
80
  console.log(chalk.red('Error: ') + versionRes.err);
70
81
  return;
71
82
  }
72
- let version = versionRes.ok;
73
83
 
74
- await install(pkg, version, {verbose: options.verbose});
84
+ pkgDetails = {
85
+ name: pkg,
86
+ repo: '',
87
+ version: versionRes.ok
88
+ };
75
89
 
76
- config.dependencies[pkg] = version;
77
- wirteConfig(config);
78
- logUpdate.clear();
79
- console.log(chalk.green('Package installed ') + `${pkg} = "${version}"`);
90
+ }else{
91
+ options.silent || logUpdate(`Installing ${existingPkg.name}@${existingPkg.version} (cache) from Github`);
92
+ return;
80
93
  }
94
+
95
+ const {name, repo, version} = pkgDetails;
96
+
97
+ if (repo){
98
+ // pkg name conflict with an installed mops pkg
99
+ if (existingPkg && !existingPkg.repo){
100
+ console.log(chalk.red('Error: ') + `Conflicting Package Name '${name}`);
101
+ console.log('Consider entering the repo url and assigning a new name in the \'mops.toml\' file');
102
+ return;
103
+ }
104
+
105
+ await installFromGithub(name, repo, {verbose: options.verbose});
106
+ }else{
107
+ await install(name, version, {verbose: options.verbose});
108
+ }
109
+
110
+ config.dependencies[name] = pkgDetails;
111
+ writeConfig(config);
112
+
113
+ logUpdate.clear();
114
+ console.log(
115
+ chalk.green('Package installed ') + `${name} = "${repo || version}"`
116
+ );
81
117
  });
82
118
 
83
119
  // publish
File without changes
package/commands/init.js CHANGED
@@ -1,7 +1,9 @@
1
- import TOML from '@iarna/toml';
2
1
  import chalk from 'chalk';
3
2
  import path from 'path';
4
3
  import fs from 'fs';
4
+ import {checkApiCompatibility, mainActor, readDfxJson, writeConfig} from '../mops.js';
5
+ import {installAll} from './install-all.js';
6
+ import { readVesselConfig } from '../vessel.js';
5
7
 
6
8
  export async function init(name = '') {
7
9
  let configFile = path.join(process.cwd(), 'mops.toml');
@@ -10,23 +12,67 @@ export async function init(name = '') {
10
12
  console.log(chalk.yellow('mops.toml already exists'));
11
13
  return;
12
14
  }
15
+
16
+ console.log('Initializing...');
17
+
13
18
  let config = {};
19
+ let vesselConfig = {};
20
+
21
+ const vesselFile = path.join(process.cwd(), 'vessel.dhall');
22
+
23
+ if (fs.existsSync(vesselFile)){
24
+ console.log('Reading vessel.dhall file');
25
+ const res = await readVesselConfig(process.cwd(), { cache: false });
26
+ vesselConfig = {...res};
27
+ }
28
+
29
+ if (vesselConfig.dependencies){
30
+ config.dependencies = {};
31
+
32
+ for (const dep of (vesselConfig.dependencies || [])){
33
+ config.dependencies[dep.name] = dep;
34
+ }
35
+ }
36
+
37
+ // lib mode
14
38
  if (name) {
15
- config = {
16
- package: {
17
- name: name,
18
- version: '0.1.0',
19
- description: '',
20
- repository: '',
21
- }
39
+ config.package = {
40
+ name,
41
+ version: '0.1.0',
42
+ description: '',
43
+ repository: '',
22
44
  };
45
+
46
+ writeConfig(config);
47
+
48
+ if (Object.keys(config.dependencies || {}).length)
49
+ await installAll({verbose: true});
23
50
  }
24
- // TODO: add last version of 'base' package?
25
- // else {
26
- // config.dependencies = {};
27
- // }
28
51
 
29
- fs.writeFileSync(configFile, TOML.stringify(config).trim());
52
+ // project mode
53
+ if (!name) {
54
+ let compatible = await checkApiCompatibility();
55
+ if (!compatible) {
56
+ return;
57
+ }
58
+
59
+ let dfxJson = readDfxJson();
60
+ let dfxVersion = dfxJson?.dfx || '';
61
+
62
+ let actor = await mainActor();
63
+ let defaultPackages = await actor.getDefaultPackages(dfxVersion);
64
+
65
+ if (!config.dependencies)
66
+ config.dependencies = {};
67
+
68
+ defaultPackages.forEach(([name, version]) => {
69
+ config.dependencies[name] = version;
70
+ });
71
+
72
+ writeConfig(config);
73
+
74
+ await installAll({verbose: true});
75
+ }
30
76
 
31
77
  console.log(chalk.green('mops.toml has been created'));
32
78
  }
@@ -2,6 +2,7 @@ import chalk from 'chalk';
2
2
  import logUpdate from 'log-update';
3
3
  import {checkConfigFile, readConfig} from '../mops.js';
4
4
  import {install} from './install.js';
5
+ import {installFromGithub} from '../vessel.js';
5
6
 
6
7
  export async function installAll({verbose} = {}) {
7
8
  if (!checkConfigFile()) {
@@ -9,10 +10,14 @@ export async function installAll({verbose} = {}) {
9
10
  }
10
11
 
11
12
  let config = readConfig();
12
- let deps = Object.entries(config.dependencies || {});
13
+ const deps = Object.values(config.dependencies || {});
13
14
 
14
- for (let [pkg, ver] of deps) {
15
- await install(pkg, ver, {verbose});
15
+ for (let {name, repo, version} of deps) {
16
+ if (repo){
17
+ await installFromGithub(name, repo, {verbose});
18
+ }else{
19
+ await install(name, version, {verbose});
20
+ }
16
21
  }
17
22
 
18
23
  logUpdate.clear();
@@ -1,9 +1,10 @@
1
1
  import path from 'path';
2
2
  import fs from 'fs';
3
3
  import logUpdate from 'log-update';
4
- import {checkConfigFile, getHighestVersion, mainActor, progressBar, readConfig, storageActor} from '../mops.js';
4
+ import {checkConfigFile, formatDir, getHighestVersion, mainActor, progressBar, readConfig, storageActor} from '../mops.js';
5
5
  import {parallel} from '../parallel.js';
6
6
  import chalk from 'chalk';
7
+ import { installFromGithub } from '../vessel.js';
7
8
 
8
9
  export async function install(pkg, version = '', {verbose, silent, dep} = {}) {
9
10
  if (!checkConfigFile()) {
@@ -19,7 +20,7 @@ export async function install(pkg, version = '', {verbose, silent, dep} = {}) {
19
20
  version = versionRes.ok;
20
21
  }
21
22
 
22
- let dir = path.join(process.cwd(), '.mops', `${pkg}@${version}`);
23
+ let dir = formatDir(pkg, version);
23
24
  let actor = await mainActor();
24
25
 
25
26
  // cache
@@ -30,9 +31,7 @@ export async function install(pkg, version = '', {verbose, silent, dep} = {}) {
30
31
  else {
31
32
  fs.mkdirSync(dir, {recursive: true});
32
33
 
33
- if (!dep) {
34
- actor.notifyInstall(pkg, version);
35
- }
34
+ actor.notifyInstall(pkg, version);
36
35
 
37
36
  let packageDetailsRes = await actor.getPackageDetails(pkg, version);
38
37
  if (packageDetailsRes.err) {
@@ -91,7 +90,11 @@ export async function install(pkg, version = '', {verbose, silent, dep} = {}) {
91
90
 
92
91
  // install dependencies
93
92
  let config = readConfig(path.join(dir, 'mops.toml'));
94
- for (let [name, version] of Object.entries(config.dependencies || {})) {
95
- await install(name, version, {verbose, silent, dep: true});
93
+ for (const {name, repo, version} of Object.values(config.dependencies || {})) {
94
+ if (repo){
95
+ await installFromGithub(name, repo, {verbose});
96
+ }else{
97
+ await install(name, version, {verbose});
98
+ }
96
99
  }
97
100
  }
@@ -136,9 +136,7 @@ export async function publish() {
136
136
  dfx: config.package.dfx || '',
137
137
  moc: config.package.moc || '',
138
138
  donation: config.package.donation || '',
139
- dependencies: (Object.entries(config.dependencies || {})).map(([name, version]) => {
140
- return {name, version};
141
- }),
139
+ dependencies: Object.values(config.dependencies || {}),
142
140
  devDependencies: [],
143
141
  scripts: [],
144
142
  };
@@ -2,7 +2,8 @@ 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 {getHighestVersion, readConfig} from '../mops.js';
5
+ import {formatDir, formatGithubDir, getHighestVersion, parseGithubURL, readConfig} from '../mops.js';
6
+ import { readVesselConfig } from '../vessel.js';
6
7
 
7
8
  function rootDir(cwd = process.cwd()) {
8
9
  let configFile = path.join(cwd, 'mops.toml');
@@ -16,8 +17,7 @@ function rootDir(cwd = process.cwd()) {
16
17
  return rootDir(path.join(cwd, '..'));
17
18
  }
18
19
 
19
- // TODO: resolve deps for a specific file to avoid conflicts
20
- // TODO: remove base-unofficial
20
+ // TODO: resolve conflicts
21
21
  export async function sources({verbose} = {}) {
22
22
  let root = rootDir();
23
23
  if (!root) {
@@ -42,48 +42,63 @@ export async function sources({verbose} = {}) {
42
42
  return 0;
43
43
  };
44
44
 
45
- let collectDeps = (config, isRoot = false) => {
46
- for (let [dep, ver] of Object.entries(config.dependencies || {})) {
47
- // take root dep ver or bigger one
48
- if (isRoot || !packages[dep] || compareVersions(packages[dep], ver) === -1) {
49
- packages[dep] = ver;
50
- }
45
+ const gitVerRegex = new RegExp(/v(\d{1,2}\.\d{1,2}\.\d{1,2})(-.*)?$/);
51
46
 
52
- let config = readConfig(path.join(root, `.mops/${dep}@${ver}/mops.toml`));
53
- collectDeps(config);
47
+ const compareGitVersions = (repoA, repoB) => {
48
+ const {branch: a} = parseGithubURL(repoA);
49
+ const {branch: b} = parseGithubURL(repoB);
54
50
 
55
- if (!versions[dep]) {
56
- versions[dep] = [];
57
- }
58
- versions[dep].push(ver);
51
+ if (gitVerRegex.test(a) && gitVerRegex.test(b)){
52
+ return compareVersions(a.substring(1) , b.substring(1));
53
+ }else if (!gitVerRegex.test(a)){
54
+ return -1;
55
+ }else{
56
+ return 1;
59
57
  }
60
58
  };
61
59
 
62
- let config = readConfig(path.join(root, 'mops.toml'));
63
- collectDeps(config, true);
60
+ let collectDeps = async (config, isRoot = false) => {
61
+ for (const pkgDetails of Object.values(config.dependencies || {})) {
62
+ const {name, repo, version} = pkgDetails;
64
63
 
65
- // install base-unofficial
66
- if (!packages['base-unofficial']) {
67
- let dirs = fs.readdirSync(path.join(root, '.mops'));
68
- let downloadedPackages = Object.fromEntries(dirs.map((dir) => {
69
- return dir.split('@');
70
- }));
64
+ // take root dep version or bigger one
65
+ if (
66
+ isRoot ||
67
+ !packages[name] ||
68
+ repo && compareGitVersions(packages[name].repo, repo) === -1 ||
69
+ compareVersions(packages[name].version, version) === -1
70
+ ) {
71
+ packages[name] = pkgDetails;
72
+ }
71
73
 
72
- if (downloadedPackages['base-unofficial']) {
73
- packages['base-unofficial'] = downloadedPackages['base-unofficial'];
74
- }
75
- else {
76
- let versionRes = await getHighestVersion('base-unofficial');
77
- if (versionRes.err) {
78
- console.log(chalk.red('Error: ') + versionRes.err);
79
- return;
74
+ let nestedConfig;
75
+
76
+ if (repo){
77
+ const dir = formatGithubDir(name, repo);
78
+ nestedConfig = await readVesselConfig(dir) || {};
79
+ }
80
+ else{
81
+ const dir = formatDir(name, version) + '/mops.toml';
82
+ nestedConfig = readConfig(dir);
83
+ }
84
+
85
+ await collectDeps(nestedConfig);
86
+
87
+ if (!versions[name]) {
88
+ versions[name] = [];
80
89
  }
81
- let version = versionRes.ok;
82
90
 
83
- await install('base-unofficial', version, {silent: true, dep: true});
84
- packages['base-unofficial'] = version;
91
+ if (repo){
92
+ const {branch} = parseGithubURL(repo);
93
+ versions[name].push(branch);
94
+ }else{
95
+ versions[name].push(version);
96
+ }
85
97
  }
86
- }
98
+ };
99
+
100
+ let config = readConfig(path.join(root, 'mops.toml'));
101
+ await collectDeps(config, true);
87
102
 
88
103
  // show conflicts
89
104
  if (verbose) {
@@ -95,13 +110,14 @@ export async function sources({verbose} = {}) {
95
110
  }
96
111
 
97
112
  // sources
98
- for (let [name, ver] of Object.entries(packages)) {
99
- let pkgDir = path.relative(process.cwd(), path.join(root, `.mops/${name}@${ver}/src`));
100
- console.log(`--package ${name} ${pkgDir}`);
101
-
102
- // fallback base to base-unofficial
103
- if (name == 'base-unofficial' && !packages.base) {
104
- console.log(`--package base ${pkgDir}`);
113
+ for (let [name, {repo, version}] of Object.entries(packages)) {
114
+ let pkgDir;
115
+ if (repo){
116
+ pkgDir = path.relative(process.cwd(), formatGithubDir(name, repo)) + '/src';
117
+ }else{
118
+ pkgDir = path.relative(process.cwd(), formatDir(name, version)) + '/src';
105
119
  }
120
+
121
+ console.log(`--package ${name} ${pkgDir}`);
106
122
  }
107
123
  }
@@ -1,8 +1,8 @@
1
- import {checkConfigFile} from '../mops.js';
2
- import path from 'path';
1
+ import {checkConfigFile, readConfig} from '../mops.js';
3
2
  import fs from 'fs';
4
3
  import del from 'del';
5
4
  import chalk from 'chalk';
5
+ import { formatDir, formatGithubDir } from '../mops.js';
6
6
 
7
7
  export async function uninstall(pkg, version) {
8
8
  if (!checkConfigFile()) {
@@ -10,7 +10,23 @@ export async function uninstall(pkg, version) {
10
10
  }
11
11
 
12
12
  // TODO: check if deps relate on this package
13
- let pkgDir = path.join(process.cwd(), '.mops', `${pkg}@${version}`);
13
+ const config = readConfig();
14
+
15
+ const pkgDetails = config.dependencies[pkg];
16
+
17
+ if (!pkgDetails){
18
+ console.log(`No dependency to remove ${pkg} = "${version}"`);
19
+ return;
20
+ }
21
+
22
+ const {repo} = pkgDetails;
23
+ let pkgDir;
24
+
25
+ if (repo){
26
+ pkgDir = formatGithubDir(pkg, repo);
27
+ }else{
28
+ pkgDir = formatDir(pkg, version);
29
+ }
14
30
 
15
31
  if (!fs.existsSync(pkgDir)) {
16
32
  console.log(`No cache to remove ${pkg} = "${version}"`);
File without changes
@@ -0,0 +1,16 @@
1
+ import { ActorSubclass, HttpAgentOptions, ActorConfig } from '@dfinity/agent';
2
+ import { Principal } from '@dfinity/principal';
3
+
4
+ import { _SERVICE } from './main.did';
5
+
6
+ export declare interface CreateActorOptions {
7
+ agentOptions?: HttpAgentOptions;
8
+ actorOptions?: ActorConfig;
9
+ }
10
+
11
+ export declare const createActor: (
12
+ canisterId: string | Principal,
13
+ options: CreateActorOptions
14
+ ) => ActorSubclass<_SERVICE>;
15
+
16
+ export declare const main: ActorSubclass<_SERVICE>;
@@ -7,17 +7,21 @@ export { idlFactory } from './main.did.js';
7
7
  export const canisterId = process.env.MAIN_CANISTER_ID;
8
8
 
9
9
  /**
10
- *
10
+ * @deprecated since dfx 0.11.1
11
+ * Do not import from `.dfx`, instead switch to using `dfx generate` to generate your JS interface.
11
12
  * @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
+ * @param {{agentOptions?: import("@dfinity/agent").HttpAgentOptions; actorOptions?: import("@dfinity/agent").ActorConfig} | { agent?: import("@dfinity/agent").Agent; actorOptions?: import("@dfinity/agent").ActorConfig }} [options]
13
14
  * @return {import("@dfinity/agent").ActorSubclass<import("./main.did.js")._SERVICE>}
14
15
  */
15
- export const createActor = (canisterId, options) => {
16
- const agent = new HttpAgent({ ...options?.agentOptions });
16
+ export const createActor = (canisterId, options = {}) => {
17
+ console.warn(`Deprecation warning: you are currently importing code from .dfx. Going forward, refactor to use the dfx generate command for JavaScript bindings.
18
+
19
+ See https://internetcomputer.org/docs/current/developer-docs/updates/release-notes/ for migration instructions`);
20
+ const agent = options.agent || new HttpAgent({ ...options.agentOptions });
17
21
 
18
22
  // Fetch root key for certificate validation during development
19
- if(process.env.NODE_ENV !== "production") {
20
- agent.fetchRootKey().catch(err=>{
23
+ if (process.env.DFX_NETWORK !== "ic") {
24
+ agent.fetchRootKey().catch(err => {
21
25
  console.warn("Unable to fetch root key. Check to ensure that your local replica is running");
22
26
  console.error(err);
23
27
  });
@@ -27,7 +31,7 @@ export const canisterId = process.env.MAIN_CANISTER_ID;
27
31
  return Actor.createActor(idlFactory, {
28
32
  agent,
29
33
  canisterId,
30
- ...options?.actorOptions,
34
+ ...(options ? options.actorOptions : {}),
31
35
  });
32
36
  };
33
37
 
@@ -35,4 +39,4 @@ export const canisterId = process.env.MAIN_CANISTER_ID;
35
39
  * A ready-to-use agent for the main canister
36
40
  * @type {import("@dfinity/agent").ActorSubclass<import("./main.did.js")._SERVICE>}
37
41
  */
38
- export const main = createActor(canisterId);
42
+ export const main = createActor(canisterId);
@@ -1,3 +1,4 @@
1
+ type Version = text;
1
2
  type Ver = text;
2
3
  type Time = int;
3
4
  type Text = text;
@@ -55,18 +56,18 @@ type PackageName__1 = text;
55
56
  type PackageName = text;
56
57
  type PackageDetails =
57
58
  record {
58
- config: PackageConfig__1;
59
+ config: PackageConfigV2__1;
59
60
  downloadsInLast30Days: nat;
60
61
  downloadsTotal: nat;
61
62
  owner: principal;
62
63
  publication: PackagePublication;
63
64
  };
64
- type PackageConfig__1 =
65
+ type PackageConfigV2__1 =
65
66
  record {
66
67
  baseDir: text;
67
- dependencies: vec Dependency;
68
+ dependencies: vec DependencyV2;
68
69
  description: text;
69
- devDependencies: vec Dependency;
70
+ devDependencies: vec DependencyV2;
70
71
  dfx: text;
71
72
  documentation: text;
72
73
  donation: text;
@@ -80,12 +81,12 @@ type PackageConfig__1 =
80
81
  scripts: vec Script;
81
82
  version: text;
82
83
  };
83
- type PackageConfig =
84
+ type PackageConfigV2 =
84
85
  record {
85
86
  baseDir: text;
86
- dependencies: vec Dependency;
87
+ dependencies: vec DependencyV2;
87
88
  description: text;
88
- devDependencies: vec Dependency;
89
+ devDependencies: vec DependencyV2;
89
90
  dfx: text;
90
91
  documentation: text;
91
92
  donation: text;
@@ -101,25 +102,33 @@ type PackageConfig =
101
102
  };
102
103
  type FileId = text;
103
104
  type Err = text;
104
- type Dependency =
105
+ type DependencyV2 =
105
106
  record {
106
107
  name: PackageName;
108
+ repo: text;
107
109
  version: text;
108
110
  };
109
111
  service : {
110
112
  finishPublish: (PublishingId) -> (Result);
111
113
  getApiVersion: () -> (Text) query;
114
+ getDefaultPackages: (text) -> (vec record {
115
+ PackageName__1;
116
+ Version;
117
+ }) query;
112
118
  getFileIds: (PackageName__1, Ver) -> (Result_5) query;
113
119
  getHighestVersion: (PackageName__1) -> (Result_4) query;
120
+ getMostDownloadedPackages: () -> (vec PackageDetails) query;
114
121
  getPackageDetails: (PackageName__1, Ver) -> (Result_3) query;
115
122
  getRecentlyUpdatedPackages: () -> (vec PackageDetails) query;
116
123
  getStoragesStats: () -> (vec record {
117
124
  StorageId;
118
125
  StorageStats;
119
126
  }) query;
127
+ getTotalDownloads: () -> (nat);
128
+ getTotalPackages: () -> (nat);
120
129
  notifyInstall: (PackageName__1, Ver) -> () oneway;
121
130
  search: (Text) -> (vec PackageDetails) query;
122
131
  startFileUpload: (PublishingId, Text, nat, blob) -> (Result_2);
123
- startPublish: (PackageConfig) -> (Result_1);
132
+ startPublish: (PackageConfigV2) -> (Result_1);
124
133
  uploadFileChunk: (PublishingId, FileId, nat, blob) -> (Result);
125
134
  }
@@ -1,10 +1,14 @@
1
1
  import type { Principal } from '@dfinity/principal';
2
2
  import type { ActorMethod } from '@dfinity/agent';
3
3
 
4
- export interface Dependency { 'name' : PackageName, 'version' : string }
4
+ export interface DependencyV2 {
5
+ 'name' : PackageName,
6
+ 'repo' : string,
7
+ 'version' : string,
8
+ }
5
9
  export type Err = string;
6
10
  export type FileId = string;
7
- export interface PackageConfig {
11
+ export interface PackageConfigV2 {
8
12
  'dfx' : string,
9
13
  'moc' : string,
10
14
  'scripts' : Array<Script>,
@@ -16,13 +20,13 @@ export interface PackageConfig {
16
20
  'version' : string,
17
21
  'keywords' : Array<string>,
18
22
  'donation' : string,
19
- 'devDependencies' : Array<Dependency>,
23
+ 'devDependencies' : Array<DependencyV2>,
20
24
  'repository' : string,
21
- 'dependencies' : Array<Dependency>,
25
+ 'dependencies' : Array<DependencyV2>,
22
26
  'license' : string,
23
27
  'readme' : string,
24
28
  }
25
- export interface PackageConfig__1 {
29
+ export interface PackageConfigV2__1 {
26
30
  'dfx' : string,
27
31
  'moc' : string,
28
32
  'scripts' : Array<Script>,
@@ -34,9 +38,9 @@ export interface PackageConfig__1 {
34
38
  'version' : string,
35
39
  'keywords' : Array<string>,
36
40
  'donation' : string,
37
- 'devDependencies' : Array<Dependency>,
41
+ 'devDependencies' : Array<DependencyV2>,
38
42
  'repository' : string,
39
- 'dependencies' : Array<Dependency>,
43
+ 'dependencies' : Array<DependencyV2>,
40
44
  'license' : string,
41
45
  'readme' : string,
42
46
  }
@@ -44,7 +48,7 @@ export interface PackageDetails {
44
48
  'owner' : Principal,
45
49
  'downloadsTotal' : bigint,
46
50
  'downloadsInLast30Days' : bigint,
47
- 'config' : PackageConfig__1,
51
+ 'config' : PackageConfigV2__1,
48
52
  'publication' : PackagePublication,
49
53
  }
50
54
  export type PackageName = string;
@@ -78,23 +82,31 @@ export interface StorageStats {
78
82
  export type Text = string;
79
83
  export type Time = bigint;
80
84
  export type Ver = string;
85
+ export type Version = string;
81
86
  export interface _SERVICE {
82
87
  'finishPublish' : ActorMethod<[PublishingId], Result>,
83
88
  'getApiVersion' : ActorMethod<[], Text>,
89
+ 'getDefaultPackages' : ActorMethod<
90
+ [string],
91
+ Array<[PackageName__1, Version]>,
92
+ >,
84
93
  'getFileIds' : ActorMethod<[PackageName__1, Ver], Result_5>,
85
94
  'getHighestVersion' : ActorMethod<[PackageName__1], Result_4>,
95
+ 'getMostDownloadedPackages' : ActorMethod<[], Array<PackageDetails>>,
86
96
  'getPackageDetails' : ActorMethod<[PackageName__1, Ver], Result_3>,
87
97
  'getRecentlyUpdatedPackages' : ActorMethod<[], Array<PackageDetails>>,
88
98
  'getStoragesStats' : ActorMethod<[], Array<[StorageId, StorageStats]>>,
99
+ 'getTotalDownloads' : ActorMethod<[], bigint>,
100
+ 'getTotalPackages' : ActorMethod<[], bigint>,
89
101
  'notifyInstall' : ActorMethod<[PackageName__1, Ver], undefined>,
90
102
  'search' : ActorMethod<[Text], Array<PackageDetails>>,
91
103
  'startFileUpload' : ActorMethod<
92
- [PublishingId, Text, bigint, Array<number>],
104
+ [PublishingId, Text, bigint, Uint8Array],
93
105
  Result_2,
94
106
  >,
95
- 'startPublish' : ActorMethod<[PackageConfig], Result_1>,
107
+ 'startPublish' : ActorMethod<[PackageConfigV2], Result_1>,
96
108
  'uploadFileChunk' : ActorMethod<
97
- [PublishingId, FileId, bigint, Array<number>],
109
+ [PublishingId, FileId, bigint, Uint8Array],
98
110
  Result,
99
111
  >,
100
112
  }
@@ -4,14 +4,19 @@ export const idlFactory = ({ IDL }) => {
4
4
  const Result = IDL.Variant({ 'ok' : IDL.Null, 'err' : Err });
5
5
  const Text = IDL.Text;
6
6
  const PackageName__1 = IDL.Text;
7
+ const Version = IDL.Text;
7
8
  const Ver = IDL.Text;
8
9
  const FileId = IDL.Text;
9
10
  const Result_5 = IDL.Variant({ 'ok' : IDL.Vec(FileId), 'err' : Err });
10
11
  const Result_4 = IDL.Variant({ 'ok' : Ver, 'err' : Err });
11
12
  const Script = IDL.Record({ 'value' : IDL.Text, 'name' : IDL.Text });
12
13
  const PackageName = IDL.Text;
13
- const Dependency = IDL.Record({ 'name' : PackageName, 'version' : IDL.Text });
14
- const PackageConfig__1 = IDL.Record({
14
+ const DependencyV2 = IDL.Record({
15
+ 'name' : PackageName,
16
+ 'repo' : IDL.Text,
17
+ 'version' : IDL.Text,
18
+ });
19
+ const PackageConfigV2__1 = IDL.Record({
15
20
  'dfx' : IDL.Text,
16
21
  'moc' : IDL.Text,
17
22
  'scripts' : IDL.Vec(Script),
@@ -23,9 +28,9 @@ export const idlFactory = ({ IDL }) => {
23
28
  'version' : IDL.Text,
24
29
  'keywords' : IDL.Vec(IDL.Text),
25
30
  'donation' : IDL.Text,
26
- 'devDependencies' : IDL.Vec(Dependency),
31
+ 'devDependencies' : IDL.Vec(DependencyV2),
27
32
  'repository' : IDL.Text,
28
- 'dependencies' : IDL.Vec(Dependency),
33
+ 'dependencies' : IDL.Vec(DependencyV2),
29
34
  'license' : IDL.Text,
30
35
  'readme' : IDL.Text,
31
36
  });
@@ -39,7 +44,7 @@ export const idlFactory = ({ IDL }) => {
39
44
  'owner' : IDL.Principal,
40
45
  'downloadsTotal' : IDL.Nat,
41
46
  'downloadsInLast30Days' : IDL.Nat,
42
- 'config' : PackageConfig__1,
47
+ 'config' : PackageConfigV2__1,
43
48
  'publication' : PackagePublication,
44
49
  });
45
50
  const Result_3 = IDL.Variant({ 'ok' : PackageDetails, 'err' : Err });
@@ -50,7 +55,7 @@ export const idlFactory = ({ IDL }) => {
50
55
  'memorySize' : IDL.Nat,
51
56
  });
52
57
  const Result_2 = IDL.Variant({ 'ok' : FileId, 'err' : Err });
53
- const PackageConfig = IDL.Record({
58
+ const PackageConfigV2 = IDL.Record({
54
59
  'dfx' : IDL.Text,
55
60
  'moc' : IDL.Text,
56
61
  'scripts' : IDL.Vec(Script),
@@ -62,9 +67,9 @@ export const idlFactory = ({ IDL }) => {
62
67
  'version' : IDL.Text,
63
68
  'keywords' : IDL.Vec(IDL.Text),
64
69
  'donation' : IDL.Text,
65
- 'devDependencies' : IDL.Vec(Dependency),
70
+ 'devDependencies' : IDL.Vec(DependencyV2),
66
71
  'repository' : IDL.Text,
67
- 'dependencies' : IDL.Vec(Dependency),
72
+ 'dependencies' : IDL.Vec(DependencyV2),
68
73
  'license' : IDL.Text,
69
74
  'readme' : IDL.Text,
70
75
  });
@@ -73,8 +78,18 @@ export const idlFactory = ({ IDL }) => {
73
78
  return IDL.Service({
74
79
  'finishPublish' : IDL.Func([PublishingId], [Result], []),
75
80
  'getApiVersion' : IDL.Func([], [Text], ['query']),
81
+ 'getDefaultPackages' : IDL.Func(
82
+ [IDL.Text],
83
+ [IDL.Vec(IDL.Tuple(PackageName__1, Version))],
84
+ ['query'],
85
+ ),
76
86
  'getFileIds' : IDL.Func([PackageName__1, Ver], [Result_5], ['query']),
77
87
  'getHighestVersion' : IDL.Func([PackageName__1], [Result_4], ['query']),
88
+ 'getMostDownloadedPackages' : IDL.Func(
89
+ [],
90
+ [IDL.Vec(PackageDetails)],
91
+ ['query'],
92
+ ),
78
93
  'getPackageDetails' : IDL.Func(
79
94
  [PackageName__1, Ver],
80
95
  [Result_3],
@@ -90,6 +105,8 @@ export const idlFactory = ({ IDL }) => {
90
105
  [IDL.Vec(IDL.Tuple(StorageId, StorageStats))],
91
106
  ['query'],
92
107
  ),
108
+ 'getTotalDownloads' : IDL.Func([], [IDL.Nat], []),
109
+ 'getTotalPackages' : IDL.Func([], [IDL.Nat], []),
93
110
  'notifyInstall' : IDL.Func([PackageName__1, Ver], [], ['oneway']),
94
111
  'search' : IDL.Func([Text], [IDL.Vec(PackageDetails)], ['query']),
95
112
  'startFileUpload' : IDL.Func(
@@ -97,7 +114,7 @@ export const idlFactory = ({ IDL }) => {
97
114
  [Result_2],
98
115
  [],
99
116
  ),
100
- 'startPublish' : IDL.Func([PackageConfig], [Result_1], []),
117
+ 'startPublish' : IDL.Func([PackageConfigV2], [Result_1], []),
101
118
  'uploadFileChunk' : IDL.Func(
102
119
  [PublishingId, FileId, IDL.Nat, IDL.Vec(IDL.Nat8)],
103
120
  [Result],
File without changes
File without changes
File without changes
File without changes
package/mops.js CHANGED
@@ -13,7 +13,7 @@ import {decodeFile} from './pem.js';
13
13
  global.fetch = fetch;
14
14
 
15
15
  // (!) make changes in pair with backend
16
- let apiVersion = '0.1';
16
+ let apiVersion = '1.2';
17
17
 
18
18
  let networkFile = new URL('./network.txt', import.meta.url);
19
19
 
@@ -107,9 +107,71 @@ export async function getHighestVersion(pkgName) {
107
107
  return actor.getHighestVersion(pkgName);
108
108
  }
109
109
 
110
+ export function parseGithubURL(href){
111
+ const url = new URL(href);
112
+ const branch = url.hash?.substring(1) || 'master';
113
+
114
+ let [org, gitName] = url.pathname.split('/').filter(path => !!path);
115
+
116
+ if (gitName.endsWith('.git')){
117
+ gitName = gitName.substring(0, gitName.length - 4);
118
+ }
119
+
120
+ return { org, gitName, branch };
121
+ }
122
+
110
123
  export function readConfig(configFile = path.join(process.cwd(), 'mops.toml')) {
111
124
  let text = fs.readFileSync(configFile).toString();
112
- return TOML.parse(text);
125
+ let toml = TOML.parse(text);
126
+
127
+ const deps = toml.dependencies || {};
128
+
129
+ Object.entries(deps).forEach(([name, data])=>{
130
+ if (data.startsWith('https://github.com/')){
131
+ deps[name] = {name, repo: data, version: ''};
132
+ }else{
133
+ deps[name] = {name, repo: '', version: data};
134
+ }
135
+ });
136
+
137
+ return toml;
138
+ }
139
+
140
+ export function writeConfig(config, configFile = path.join(process.cwd(), 'mops.toml')) {
141
+ const deps = config.dependencies || {};
142
+
143
+ Object.entries(deps).forEach(([name, {repo, version}])=>{
144
+ if (repo){
145
+ deps[name] = repo;
146
+ }else{
147
+ deps[name] = version;
148
+ }
149
+ });
150
+
151
+ fs.writeFileSync(configFile, TOML.stringify(config).trim());
152
+ }
153
+
154
+ export function formatDir(name, version){
155
+ return path.join(process.cwd(), '.mops', `${name}@${version}`);
156
+ }
157
+
158
+ export function formatGithubDir(name, repo){
159
+ const { branch } = parseGithubURL(repo);
160
+ return path.join(process.cwd(), '.mops/_github', `${name}@${branch}`);
161
+ }
162
+
163
+ export function readDfxJson() {
164
+ let dir = process.cwd();
165
+ let dfxJson = null;
166
+ for (let i = 0; i < 5; i++) {
167
+ let file = path.resolve(dir, 'dfx.json');
168
+ if (fs.existsSync(file)) {
169
+ dfxJson = JSON.parse(fs.readFileSync(file).toString());
170
+ break;
171
+ }
172
+ dir = path.resolve(dir, '..');
173
+ }
174
+ return dfxJson;
113
175
  }
114
176
 
115
177
  // warn on minor mismatch
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ic-mops",
3
- "version": "0.1.12",
3
+ "version": "0.2.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "mops": "cli.js"
@@ -13,9 +13,13 @@
13
13
  "@iarna/toml": "^2.2.5",
14
14
  "chalk": "^4.1.2",
15
15
  "commander": "^9.2.0",
16
+ "decompress": "^4.2.1",
16
17
  "del": "^6.0.0",
18
+ "dhall-to-json-cli": "^1.7.6",
17
19
  "eslint": "^8.15.0",
20
+ "execa": "^6.1.0",
18
21
  "globby": "^13.1.1",
22
+ "got": "^12.5.3",
19
23
  "log-update": "^5.0.1",
20
24
  "minimatch": "^5.0.1",
21
25
  "node-fetch": "^2.6.7",
package/parallel.js CHANGED
File without changes
package/pem.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import fs from 'fs';
2
- import {Ed25519KeyIdentity} from '@dfinity/identity';
2
+ import {Ed25519KeyIdentity, Secp256k1KeyIdentity} from '@dfinity/identity';
3
3
  import pemfile from 'pem-file';
4
4
 
5
5
  export function decodeFile(file) {
@@ -9,6 +9,12 @@ export function decodeFile(file) {
9
9
 
10
10
  export function decode(rawKey) {
11
11
  var buf = pemfile.decode(rawKey);
12
+ if (rawKey.includes('EC PRIVATE KEY')) {
13
+ if (buf.length != 118) {
14
+ throw 'expecting byte length 118 but got ' + buf.length;
15
+ }
16
+ return Secp256k1KeyIdentity.fromSecretKey(buf.slice(7, 39));
17
+ }
12
18
  if (buf.length != 85) {
13
19
  throw 'expecting byte length 85 but got ' + buf.length;
14
20
  }
package/vessel.js ADDED
@@ -0,0 +1,162 @@
1
+ import {
2
+ existsSync, mkdirSync, createWriteStream, readFileSync, writeFileSync
3
+ } from 'fs';
4
+ import del from 'del';
5
+ import { execaCommand} from 'execa';
6
+ import chalk from 'chalk';
7
+ import logUpdate from 'log-update';
8
+ import { formatGithubDir, parseGithubURL, progressBar } from './mops.js';
9
+ import path from 'path';
10
+ import got from 'got';
11
+ import decompress from 'decompress';
12
+ import {pipeline} from 'stream/promises';
13
+
14
+ const dhallFileToJson = async (filePath) => {
15
+ if (existsSync(filePath)) {
16
+ let cwd = new URL(path.dirname(import.meta.url)).pathname;
17
+ const res = await execaCommand(`dhall-to-json --file ${filePath}`, {preferLocal:true, cwd});
18
+
19
+ if (res.exitCode === 0){
20
+ return JSON.parse(res.stdout);
21
+ }
22
+ else {
23
+ return res;
24
+ }
25
+ }
26
+
27
+ return null;
28
+ };
29
+
30
+ export const readVesselConfig = async (
31
+ configFile,
32
+ { cache = true } = { cache: true }
33
+ ) => {
34
+ const cachedFile = (configFile || process.cwd()) + '/vessel.json';
35
+
36
+ if (existsSync(cachedFile)) {
37
+ let cachedConfig = readFileSync(cachedFile);
38
+ return JSON.parse(cachedConfig);
39
+ }
40
+
41
+ const [vessel, packageSetArray] = await Promise.all([
42
+ dhallFileToJson((configFile || process.cwd()) + '/vessel.dhall'),
43
+ dhallFileToJson((configFile || process.cwd()) + '/package-set.dhall')
44
+ ]);
45
+
46
+ if (!vessel || !packageSetArray) return null;
47
+
48
+ let repos = {};
49
+ for (const { name, repo, version } of packageSetArray) {
50
+ const { org, gitName } = parseGithubURL(repo);
51
+ repos[name] = `https://github.com/${org}/${gitName}#${version}`;
52
+ }
53
+
54
+ let config = {
55
+ compiler: vessel.compiler,
56
+ dependencies: vessel.dependencies.map((name) => {
57
+ return { name, repo: repos[name], version: '' };
58
+ }),
59
+ };
60
+
61
+ if (cache === true) {
62
+ writeFileSync(cachedFile, JSON.stringify(config), 'utf-8');
63
+ }
64
+
65
+ return config;
66
+ };
67
+
68
+ export const downloadFromGithub = async (repo, dest, onProgress = null) => {
69
+ const {branch, org, gitName} = parseGithubURL(repo);
70
+
71
+ const zipFile = `https://github.com/${org}/${gitName}/archive/${branch}.zip`;
72
+ const readStream = got.stream(zipFile);
73
+
74
+ const promise = new Promise((resolve, reject) => {
75
+
76
+ readStream.on('downloadProgress', ({ transferred, total}) => {
77
+ onProgress?.(transferred, total || 2 * (1024 ** 2) );
78
+ });
79
+
80
+ readStream.on('response', (response) => {
81
+ if (response.headers.age > 3600) {
82
+ console.log(chalk.red('Error: ') + 'Failure - response too old');
83
+ readStream.destroy(); // Destroy the stream to prevent hanging resources.
84
+ reject();
85
+ return;
86
+ }
87
+
88
+ // Prevent `onError` being called twice.
89
+ readStream.off('error', reject);
90
+ const tmpDir = process.cwd() + '/.mops/_tmp/';
91
+ const tmpFile = tmpDir + `/${gitName}@${branch}.zip`;
92
+
93
+ try {
94
+ mkdirSync(tmpDir, {recursive: true});
95
+
96
+ pipeline(readStream, createWriteStream(tmpFile))
97
+ .then(() => {
98
+ let options = {
99
+ extract: true,
100
+ strip: 1,
101
+ headers: {
102
+ accept: 'application/zip'
103
+ }
104
+ };
105
+
106
+ return decompress(tmpFile, dest, options);
107
+
108
+ }).then((unzippedFiles) => {
109
+ del.sync([tmpDir]);
110
+ resolve(unzippedFiles);
111
+
112
+ }).catch(err => {
113
+ del.sync([tmpDir]);
114
+ reject(err);
115
+ });
116
+
117
+ } catch (err) {
118
+ del.sync([tmpDir]);
119
+ reject(err);
120
+ }
121
+ });
122
+ });
123
+
124
+ return promise;
125
+ };
126
+
127
+ export const installFromGithub = async (name, repo, options = {})=>{
128
+
129
+ const {verbose, dep, silent} = options;
130
+
131
+ const {branch} = parseGithubURL(repo);
132
+ const dir = formatGithubDir(name, repo);
133
+
134
+ if (existsSync(dir)){
135
+ silent || logUpdate(`${dep ? 'Dependency' : 'Installing'} ${name}@${branch} (cache) from Github`);
136
+ }
137
+ else {
138
+ mkdirSync(dir, {recursive: true});
139
+
140
+ let progress = (step, total) => {
141
+ silent || logUpdate(`${dep ? 'Dependency' : 'Installing'} ${name}@${branch} ${progressBar(step, total)}`);
142
+ };
143
+
144
+ progress(0, 2 * (1024 ** 2));
145
+ await downloadFromGithub(repo, dir, progress).catch((err)=> {
146
+ del.sync([dir]);
147
+ console.log(chalk.red('Error: ') + err);
148
+ });
149
+ }
150
+
151
+ if (verbose) {
152
+ silent || logUpdate.done();
153
+ }
154
+
155
+ const config = await readVesselConfig(dir);
156
+
157
+ if (config){
158
+ for (const {name, repo} of config.dependencies){
159
+ await installFromGithub(name, repo, {verbose, silent, dep: true });
160
+ }
161
+ }
162
+ };