@blocklet/cli 1.16.33 → 1.16.34-beta-20241120-080738-bbbe036c
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/README.md +32 -25
- package/bin/blocklet.js +292 -1
- package/config.example.yml +33 -0
- package/lib/arcblock.js +53 -0
- package/lib/commands/blocklet/add.js +124 -0
- package/lib/commands/blocklet/assets/git-ignore +28 -0
- package/lib/commands/blocklet/assets/index.html +9 -0
- package/lib/commands/blocklet/assets/index.js +14 -0
- package/lib/commands/blocklet/assets/logo.png +0 -0
- package/lib/commands/blocklet/bundle/bundle.js +184 -0
- package/lib/commands/blocklet/bundle/bundlers/blocklet.js +138 -0
- package/lib/commands/blocklet/bundle/bundlers/changelog.js +100 -0
- package/lib/commands/blocklet/bundle/bundlers/logo.js +56 -0
- package/lib/commands/blocklet/bundle/bundlers/markdown.js +241 -0
- package/lib/commands/blocklet/bundle/bundlers/preference.js +50 -0
- package/lib/commands/blocklet/bundle/bundlers/readme.js +43 -0
- package/lib/commands/blocklet/bundle/bundlers/screenshots.js +94 -0
- package/lib/commands/blocklet/bundle/bundlers/simple.js +70 -0
- package/lib/commands/blocklet/bundle/compact/bundle-compact-file.js +48 -0
- package/lib/commands/blocklet/bundle/compact/bundle-merge-extra.js +66 -0
- package/lib/commands/blocklet/bundle/compact/default-external.js +5 -0
- package/lib/commands/blocklet/bundle/compact/index.js +88 -0
- package/lib/commands/blocklet/bundle/index.js +139 -0
- package/lib/commands/blocklet/bundle/pack.js +8 -0
- package/lib/commands/blocklet/bundle/parse-external-dependencies.js +97 -0
- package/lib/commands/blocklet/bundle/simple/index.js +62 -0
- package/lib/commands/blocklet/bundle/zip/archive.js +35 -0
- package/lib/commands/blocklet/bundle/zip/dependencies.js +333 -0
- package/lib/commands/blocklet/bundle/zip/index.js +165 -0
- package/lib/commands/blocklet/bundle/zip/main.js +124 -0
- package/lib/commands/blocklet/bundle/zip/node.js +59 -0
- package/lib/commands/blocklet/bundle/zip/resolve.js +93 -0
- package/lib/commands/blocklet/cleanup.js +52 -0
- package/lib/commands/blocklet/config.js +108 -0
- package/lib/commands/blocklet/connect.js +87 -0
- package/lib/commands/blocklet/create.js +38 -0
- package/lib/commands/blocklet/deploy.js +435 -0
- package/lib/commands/blocklet/dev.js +1000 -0
- package/lib/commands/blocklet/document.js +39 -0
- package/lib/commands/blocklet/exec.js +106 -0
- package/lib/commands/blocklet/init.js +300 -0
- package/lib/commands/blocklet/meta.js +22 -0
- package/lib/commands/blocklet/remove.js +35 -0
- package/lib/commands/blocklet/test.js +201 -0
- package/lib/commands/blocklet/upload.js +105 -0
- package/lib/commands/blocklet/version.js +81 -0
- package/lib/commands/server/cleanup.js +32 -0
- package/lib/commands/server/command.js +131 -0
- package/lib/commands/server/info.js +92 -0
- package/lib/commands/server/init.js +433 -0
- package/lib/commands/server/logs.js +99 -0
- package/lib/commands/server/rescue.js +71 -0
- package/lib/commands/server/start.js +821 -0
- package/lib/commands/server/status.js +107 -0
- package/lib/commands/server/stop.js +163 -0
- package/lib/commands/server/upgrade.js +123 -0
- package/lib/constant.js +21 -2
- package/lib/debug.js +20 -0
- package/lib/manager/config.js +122 -0
- package/lib/manager/deploy.js +75 -0
- package/lib/manager/index.js +23 -0
- package/lib/manager/process.js +47 -0
- package/lib/node.js +214 -0
- package/lib/port.js +19 -0
- package/lib/postinstall.js +3 -0
- package/lib/process/daemon.js +196 -0
- package/lib/process/service.js +86 -0
- package/lib/ui.js +137 -0
- package/lib/util/blocklet/config.js +78 -0
- package/lib/util/blocklet/env.js +172 -0
- package/lib/util/blocklet/meta.js +36 -0
- package/lib/util/blocklet/payment.js +88 -0
- package/lib/util/blocklet/sign.js +21 -0
- package/lib/util/blocklet/tar.js +119 -0
- package/lib/util/convert-to-nosources-sourcemap.js +37 -0
- package/lib/util/docker-status-log.js +17 -0
- package/lib/util/exit-when-server-stopped.js +44 -0
- package/lib/util/get-cli-binary-name.js +8 -0
- package/lib/util/get-download-bundle-step.js +36 -0
- package/lib/util/get-service-instance-number.js +12 -0
- package/lib/util/index.js +626 -0
- package/lib/util/print-error.js +11 -0
- package/lib/util/print.js +9 -0
- package/lib/util/what-uri.js +40 -0
- package/package.json +123 -27
- package/lib/run.d.ts +0 -2
- package/lib/run.js +0 -73
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
const { toAddress } = require('@ocap/util');
|
|
2
|
+
const { updateDidDocument } = require('@abtnode/core/lib/util/blocklet');
|
|
3
|
+
|
|
4
|
+
const { getNode } = require('../../node');
|
|
5
|
+
const debug = require('../../debug')('document');
|
|
6
|
+
const { printSuccess, printError } = require('../../util');
|
|
7
|
+
const { wrapSpinner } = require('../../ui');
|
|
8
|
+
|
|
9
|
+
const update = async (appId) => {
|
|
10
|
+
try {
|
|
11
|
+
const { node } = await getNode();
|
|
12
|
+
const nodeInfo = await node.getNodeInfo();
|
|
13
|
+
|
|
14
|
+
const did = toAddress(appId);
|
|
15
|
+
|
|
16
|
+
const blocklet = await node.getBlocklet({ did });
|
|
17
|
+
if (!blocklet) {
|
|
18
|
+
printError(`Blocklet ${appId} not found`);
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
await wrapSpinner('Updating...', () => updateDidDocument({ blocklet, nodeInfo }), {
|
|
23
|
+
throwOnError: true,
|
|
24
|
+
printErrorFn: printError,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
printSuccess(`Update did document for ${appId} successfully!`);
|
|
28
|
+
process.exit(0);
|
|
29
|
+
} catch (error) {
|
|
30
|
+
debug(error);
|
|
31
|
+
printError(`Failed to update did document for blocklet ${appId}, error: ${error.message}`);
|
|
32
|
+
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
module.exports = {
|
|
38
|
+
update,
|
|
39
|
+
};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
const runScript = require('@abtnode/util/lib/run-script');
|
|
5
|
+
const getBlockletMeta = require('@blocklet/meta/lib/parse');
|
|
6
|
+
const { getSafeEnv } = require('@abtnode/core/lib/util');
|
|
7
|
+
const { isValid: isValidDid, toAddress } = require('@arcblock/did');
|
|
8
|
+
const { getRuntimeEnvironments } = require('@abtnode/core/lib/util/blocklet');
|
|
9
|
+
|
|
10
|
+
const { printError, printInfo, printSuccess, getCLIBinaryName } = require('../../util');
|
|
11
|
+
const { getNode } = require('../../node');
|
|
12
|
+
const { checkRunning } = require('../../manager');
|
|
13
|
+
const ensureBlockletEnv = require('../../util/blocklet/env');
|
|
14
|
+
|
|
15
|
+
const checkNodeRunning = async () => {
|
|
16
|
+
const isRunning = await checkRunning();
|
|
17
|
+
if (!isRunning) {
|
|
18
|
+
const startCommand = chalk.cyan(`${getCLIBinaryName()} server start`);
|
|
19
|
+
printError('Blocklet Server is not running, can not execute anything!');
|
|
20
|
+
printInfo(`To start Blocklet Server, use ${startCommand}`);
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const run = async (script, { appId, timeout } = {}) => {
|
|
26
|
+
try {
|
|
27
|
+
await checkNodeRunning();
|
|
28
|
+
const { node } = await getNode();
|
|
29
|
+
|
|
30
|
+
const timeoutMs = (+timeout || 30 * 60) * 1000;
|
|
31
|
+
const appDir = process.cwd();
|
|
32
|
+
const meta = getBlockletMeta(appDir, { fix: false });
|
|
33
|
+
|
|
34
|
+
printInfo(
|
|
35
|
+
`Try to run script for ${appId ? 'component' : 'blocklet'} ${chalk.cyan(meta.name)} (timeout: ${timeoutMs}ms)`
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
const scriptPath = path.join(appDir, script);
|
|
39
|
+
if (fs.existsSync(scriptPath) === false) {
|
|
40
|
+
printError(`Script ${chalk.cyan(scriptPath)} does not exist`);
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (appId) {
|
|
45
|
+
if (isValidDid(appId) === false) {
|
|
46
|
+
printError(`appId is not valid: ${appId}`);
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// eslint-disable-next-line
|
|
51
|
+
appId = toAddress(appId);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
node.onReady(async () => {
|
|
55
|
+
const rootDid = appId || meta.did;
|
|
56
|
+
const componentId = appId ? meta.did : null;
|
|
57
|
+
|
|
58
|
+
let blocklet = await node.getBlocklet({ did: rootDid });
|
|
59
|
+
let component;
|
|
60
|
+
|
|
61
|
+
if (!blocklet) {
|
|
62
|
+
printError(`Blocklet ${appId || meta.name} not found, you should install the blocklet first.`);
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (componentId) {
|
|
67
|
+
component = blocklet.children.find((x) => x.meta.did === componentId);
|
|
68
|
+
if (!component) {
|
|
69
|
+
printError(
|
|
70
|
+
`Component ${meta.name} not found in ${blocklet.meta.name}, you should add the component to the blocklet first`
|
|
71
|
+
);
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// ensure environments
|
|
77
|
+
await ensureBlockletEnv(node, blocklet, appDir);
|
|
78
|
+
|
|
79
|
+
blocklet = await node.getBlocklet({ did: rootDid });
|
|
80
|
+
const nodeEnvironments = await node.states.node.getEnvironments();
|
|
81
|
+
if (componentId) {
|
|
82
|
+
component = blocklet.children.find((x) => x.meta.did === componentId);
|
|
83
|
+
await runScript(`node ${scriptPath}`, component.env.processId, {
|
|
84
|
+
cwd: appDir,
|
|
85
|
+
env: getSafeEnv(getRuntimeEnvironments(component, nodeEnvironments, [blocklet])),
|
|
86
|
+
silent: false,
|
|
87
|
+
timeout: timeoutMs,
|
|
88
|
+
});
|
|
89
|
+
} else {
|
|
90
|
+
await runScript(`node ${scriptPath}`, blocklet.env.processId, {
|
|
91
|
+
cwd: appDir,
|
|
92
|
+
env: getSafeEnv(getRuntimeEnvironments(blocklet, nodeEnvironments)),
|
|
93
|
+
silent: false,
|
|
94
|
+
timeout: timeoutMs,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
printSuccess('Done!');
|
|
98
|
+
process.exit(0);
|
|
99
|
+
});
|
|
100
|
+
} catch (err) {
|
|
101
|
+
printError(err.message);
|
|
102
|
+
process.exit(1);
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
exports.run = run;
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const fs = require('fs-extra');
|
|
3
|
+
const fuzzy = require('fuzzy');
|
|
4
|
+
const inquirer = require('inquirer');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const open = require('open');
|
|
7
|
+
const pickBy = require('lodash/pickBy');
|
|
8
|
+
const {
|
|
9
|
+
BLOCKLET_GROUPS,
|
|
10
|
+
BLOCKLET_META_FILE,
|
|
11
|
+
BLOCKLET_DEFAULT_VERSION,
|
|
12
|
+
BlockletGroup,
|
|
13
|
+
BLOCKLET_LATEST_SPEC_VERSION,
|
|
14
|
+
} = require('@blocklet/constant');
|
|
15
|
+
const { select: getMetaFile, update: updateMetaFile, read: readMetaFile } = require('@blocklet/meta/lib/file');
|
|
16
|
+
const { fixInterfaces, parsePerson } = require('@blocklet/meta/lib/fix');
|
|
17
|
+
const { validateMeta } = require('@blocklet/meta/lib/validate');
|
|
18
|
+
const { fixRepository } = require('@blocklet/meta/lib/fix');
|
|
19
|
+
const { titleSchema } = require('@blocklet/meta/lib/schema');
|
|
20
|
+
const { isValid } = require('@arcblock/did');
|
|
21
|
+
|
|
22
|
+
const { createConnect } = require('@blocklet/store');
|
|
23
|
+
const {
|
|
24
|
+
getNPMConfig,
|
|
25
|
+
print,
|
|
26
|
+
printError,
|
|
27
|
+
printInfo,
|
|
28
|
+
printSuccess,
|
|
29
|
+
getUserName,
|
|
30
|
+
fixFiles,
|
|
31
|
+
printWarning,
|
|
32
|
+
} = require('../../util');
|
|
33
|
+
const { wrapSpinner } = require('../../ui');
|
|
34
|
+
|
|
35
|
+
const COMMON_KEYS = ['name', 'description', 'version', 'author', 'keywords', 'repository'];
|
|
36
|
+
|
|
37
|
+
inquirer.registerPrompt('autocomplete', require('inquirer-autocomplete-prompt'));
|
|
38
|
+
|
|
39
|
+
const pickCommonFieldsInBlockletAndPackage = (meta) =>
|
|
40
|
+
pickBy(meta, (value, key) => value !== undefined && COMMON_KEYS.includes(key));
|
|
41
|
+
|
|
42
|
+
const doInit = ({ metaFilePath, blockletMeta }) => {
|
|
43
|
+
updateMetaFile(metaFilePath, blockletMeta);
|
|
44
|
+
printSuccess(`Meta file ${chalk.cyan(metaFilePath)} was created`);
|
|
45
|
+
|
|
46
|
+
if (!fs.existsSync('blocklet.md')) {
|
|
47
|
+
fs.writeFileSync('blocklet.md', `# ${blockletMeta.name}`);
|
|
48
|
+
printSuccess(`Doc file ${chalk.cyan('blocklet.md')} was created`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (!fs.existsSync('screenshots')) {
|
|
52
|
+
fs.mkdirSync('screenshots');
|
|
53
|
+
printSuccess(`Screenshots dir ${chalk.cyan('screenshots/')} was created`);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (!fs.existsSync('logo.png')) {
|
|
57
|
+
fs.copyFileSync(path.join(__dirname, 'assets', 'logo.png'), 'logo.png');
|
|
58
|
+
printSuccess(`${chalk.cyan('logo.png')} was created`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (blockletMeta.group === BlockletGroup.dapp) {
|
|
62
|
+
if (!fs.existsSync('index.js')) {
|
|
63
|
+
fs.copyFileSync(path.join(__dirname, 'assets', 'index.js'), 'index.js');
|
|
64
|
+
printSuccess(`${chalk.cyan('index.js')} was created`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (blockletMeta.group === BlockletGroup.static) {
|
|
69
|
+
if (!fs.existsSync('index.html')) {
|
|
70
|
+
fs.copyFileSync(path.join(__dirname, 'assets', 'index.html'), 'index.html');
|
|
71
|
+
printSuccess(`${chalk.cyan('index.html')} was created`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// avoid bundle bug
|
|
76
|
+
if (!fs.existsSync('.gitignore')) {
|
|
77
|
+
fs.copyFileSync(path.join(__dirname, 'assets', 'git-ignore'), '.gitignore');
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const getPromptQuestions = (defaults) => {
|
|
82
|
+
const questions = [
|
|
83
|
+
{
|
|
84
|
+
type: 'text',
|
|
85
|
+
name: 'title',
|
|
86
|
+
message: 'blocklet title',
|
|
87
|
+
default: defaults.name,
|
|
88
|
+
transformer: (name) => name.trim(),
|
|
89
|
+
validate: async (input) => {
|
|
90
|
+
try {
|
|
91
|
+
if (!input || !input.trim()) {
|
|
92
|
+
return 'title can not be empty';
|
|
93
|
+
}
|
|
94
|
+
await titleSchema.validateAsync(input);
|
|
95
|
+
} catch (error) {
|
|
96
|
+
return error.message;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return true;
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
type: 'text',
|
|
104
|
+
name: 'description',
|
|
105
|
+
message: 'Please write concise description:',
|
|
106
|
+
default: defaults.description || '',
|
|
107
|
+
validate: (description) => (description.trim() ? true : 'Description can not be empty'),
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
type: 'autocomplete',
|
|
111
|
+
name: 'group',
|
|
112
|
+
message: 'What\'s the group of the blocklet?', // prettier-ignore
|
|
113
|
+
default: defaults.group,
|
|
114
|
+
source: (_, inp) => {
|
|
115
|
+
const input = inp || '';
|
|
116
|
+
return new Promise((resolve) => {
|
|
117
|
+
const result = fuzzy.filter(input, BLOCKLET_GROUPS);
|
|
118
|
+
resolve(result.map((item) => item.original));
|
|
119
|
+
});
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
type: 'text',
|
|
124
|
+
name: 'main',
|
|
125
|
+
message: 'What\'s the entry point of the blocklet?', // prettier-ignore
|
|
126
|
+
default: (answers) => (answers.group === BlockletGroup.dapp ? 'index.js' : defaults.main || '.'),
|
|
127
|
+
validate: (main) => (main.trim() ? true : 'The entry point can not be empty'),
|
|
128
|
+
when: (answers) => answers.group !== BlockletGroup.gateway,
|
|
129
|
+
},
|
|
130
|
+
];
|
|
131
|
+
|
|
132
|
+
return questions;
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
function getNpmAuthor() {
|
|
136
|
+
const npmAuthor = getNPMConfig('init.author.name');
|
|
137
|
+
const npmEmail = getNPMConfig('init.author.email');
|
|
138
|
+
let author = '';
|
|
139
|
+
if (npmAuthor) {
|
|
140
|
+
author = npmEmail ? `${npmAuthor} <${npmEmail}>` : npmAuthor;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return author;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function ensureMainDirPathWhenStatic(answers) {
|
|
147
|
+
if (answers.group !== BlockletGroup.static || !answers.main) {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
answers.main = path.dirname(answers.main) || '.';
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async function generateDidFromWallet(
|
|
155
|
+
connectUrl = 'https://store.blocklet.dev',
|
|
156
|
+
{ verbose = true, monikers = '' } = {}
|
|
157
|
+
) {
|
|
158
|
+
const infoList = await createConnect({
|
|
159
|
+
connectUrl,
|
|
160
|
+
verbose,
|
|
161
|
+
monikers,
|
|
162
|
+
connectAction: 'gen-key-pair',
|
|
163
|
+
openPage: open,
|
|
164
|
+
wrapSpinner,
|
|
165
|
+
});
|
|
166
|
+
return infoList.map((x) => x.address);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Run the cli interactively
|
|
170
|
+
async function run({ yes = false, force = false, monikers = '', connectUrl, did }) {
|
|
171
|
+
if (monikers) {
|
|
172
|
+
const blockletDidList = await generateDidFromWallet(connectUrl, { verbose: false, monikers });
|
|
173
|
+
print(blockletDidList.join(','));
|
|
174
|
+
process.exit(0);
|
|
175
|
+
}
|
|
176
|
+
const dir = process.cwd();
|
|
177
|
+
const metaFilePath = getMetaFile(dir, { throwOnError: false });
|
|
178
|
+
|
|
179
|
+
const defaultName = path.basename(dir);
|
|
180
|
+
|
|
181
|
+
// Do nothing if meta file found
|
|
182
|
+
if (metaFilePath) {
|
|
183
|
+
const metaData = readMetaFile(metaFilePath);
|
|
184
|
+
if (isValid(metaData.did)) {
|
|
185
|
+
printInfo(`Blocklet meta file found at: ${chalk.cyan(metaFilePath)}, skipping`);
|
|
186
|
+
process.exit(0);
|
|
187
|
+
} else {
|
|
188
|
+
printWarning('Blocklet did is invalid, generate a new blocklet did');
|
|
189
|
+
const [blockletDid] = await generateDidFromWallet(connectUrl, { monikers: defaultName });
|
|
190
|
+
updateMetaFile(metaFilePath, {
|
|
191
|
+
...metaData,
|
|
192
|
+
did: blockletDid,
|
|
193
|
+
});
|
|
194
|
+
process.exit(0);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
let packageJSON = {};
|
|
199
|
+
const packageJSONPath = path.join(dir, 'package.json');
|
|
200
|
+
if (fs.existsSync(packageJSONPath)) {
|
|
201
|
+
try {
|
|
202
|
+
packageJSON = JSON.parse(fs.readFileSync(packageJSONPath));
|
|
203
|
+
} catch (error) {
|
|
204
|
+
printError('Read package.json file failed.');
|
|
205
|
+
printInfo('Please check if package.json is a valid json file.');
|
|
206
|
+
process.exit(1);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const defaultMeta = Object.assign(
|
|
211
|
+
{
|
|
212
|
+
name: defaultName,
|
|
213
|
+
description: 'Blocklet Project',
|
|
214
|
+
group: BLOCKLET_GROUPS[0],
|
|
215
|
+
publicUrl: '/',
|
|
216
|
+
main: '.',
|
|
217
|
+
author: getNpmAuthor(),
|
|
218
|
+
specVersion: BLOCKLET_LATEST_SPEC_VERSION,
|
|
219
|
+
},
|
|
220
|
+
pickCommonFieldsInBlockletAndPackage(packageJSON)
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
let answers = {};
|
|
224
|
+
const questions = getPromptQuestions(defaultMeta);
|
|
225
|
+
if (yes || force) {
|
|
226
|
+
answers = questions.reduce((acc, item) => {
|
|
227
|
+
if (item.default) {
|
|
228
|
+
acc[item.name] = item.default;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return acc;
|
|
232
|
+
}, {});
|
|
233
|
+
} else {
|
|
234
|
+
print('This utility will walk you through create such files and folders(if not exists):');
|
|
235
|
+
print(`- ${BLOCKLET_META_FILE}`);
|
|
236
|
+
print('- blocklet.md');
|
|
237
|
+
print('- screenshots/');
|
|
238
|
+
print('\nIt only covers common items, if you want to check all items, please visit:');
|
|
239
|
+
print('https://github.com/ArcBlock/blocklets#keyinfo-blockletjson\n');
|
|
240
|
+
print('Press ^C to quit.');
|
|
241
|
+
answers = await inquirer.prompt(questions);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
ensureMainDirPathWhenStatic(answers);
|
|
245
|
+
const meta = Object.assign(defaultMeta, answers);
|
|
246
|
+
|
|
247
|
+
fixFiles(meta, dir);
|
|
248
|
+
fixInterfaces(meta);
|
|
249
|
+
fixRepository(meta);
|
|
250
|
+
|
|
251
|
+
const author = getUserName(meta.author);
|
|
252
|
+
meta.author = parsePerson(author);
|
|
253
|
+
let blockletDid = did;
|
|
254
|
+
if (!blockletDid) {
|
|
255
|
+
[blockletDid] = await generateDidFromWallet(connectUrl, { monikers: meta.name });
|
|
256
|
+
}
|
|
257
|
+
meta.did = blockletDid;
|
|
258
|
+
meta.name = meta.did;
|
|
259
|
+
meta.version = BLOCKLET_DEFAULT_VERSION;
|
|
260
|
+
|
|
261
|
+
if (meta.group === BlockletGroup.gateway) {
|
|
262
|
+
delete meta.main;
|
|
263
|
+
} else if (meta.group === BlockletGroup.dapp) {
|
|
264
|
+
meta.scripts = {
|
|
265
|
+
dev: 'node index.js',
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if (yes === false && force === false) {
|
|
270
|
+
const { isOK } = await inquirer.prompt([
|
|
271
|
+
{
|
|
272
|
+
type: 'confirm',
|
|
273
|
+
name: 'isOK',
|
|
274
|
+
message: 'Is this OK:',
|
|
275
|
+
default: true,
|
|
276
|
+
},
|
|
277
|
+
]);
|
|
278
|
+
|
|
279
|
+
if (!isOK) {
|
|
280
|
+
print('User aborted!');
|
|
281
|
+
process.exit(0);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
meta.logo = 'logo.png';
|
|
286
|
+
|
|
287
|
+
const blockletMeta = validateMeta(meta);
|
|
288
|
+
|
|
289
|
+
doInit({ metaFilePath: path.join(dir, BLOCKLET_META_FILE), blockletMeta });
|
|
290
|
+
process.exit(0);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
exports.run = async (...args) => {
|
|
294
|
+
try {
|
|
295
|
+
await run(...args);
|
|
296
|
+
} catch (err) {
|
|
297
|
+
printError(`Failed to init blocklet: ${err.message}`);
|
|
298
|
+
process.exit(1);
|
|
299
|
+
}
|
|
300
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const getBlockletMeta = require('@blocklet/meta/lib/parse');
|
|
2
|
+
|
|
3
|
+
const { printError, printInfo } = require('../../util');
|
|
4
|
+
const { pretty } = require('../../ui');
|
|
5
|
+
const debug = require('../../debug')('blocklet:meta');
|
|
6
|
+
|
|
7
|
+
exports.run = () => {
|
|
8
|
+
try {
|
|
9
|
+
const dir = process.cwd();
|
|
10
|
+
|
|
11
|
+
printInfo('Reading blocklet meta from', dir);
|
|
12
|
+
|
|
13
|
+
const meta = getBlockletMeta(dir, { ensureComponentStore: false });
|
|
14
|
+
printInfo('Blocklet meta', pretty(meta));
|
|
15
|
+
|
|
16
|
+
process.exit(0);
|
|
17
|
+
} catch (error) {
|
|
18
|
+
debug('read blocklet meta failed', error);
|
|
19
|
+
printError('Read blocklet meta failed:', error.message);
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const getBlockletMeta = require('@blocklet/meta/lib/parse');
|
|
3
|
+
const { select: getMetaFile, update: updateMetaFile } = require('@blocklet/meta/lib/file');
|
|
4
|
+
|
|
5
|
+
const { printSuccess, printError } = require('../../util');
|
|
6
|
+
const { fixComponents } = require('../../util/blocklet/meta');
|
|
7
|
+
const debug = require('../../debug')('blocklet:version');
|
|
8
|
+
|
|
9
|
+
exports.run = (component) => {
|
|
10
|
+
try {
|
|
11
|
+
const dir = process.cwd();
|
|
12
|
+
const file = getMetaFile(dir);
|
|
13
|
+
const meta = getBlockletMeta(dir, { fix: false });
|
|
14
|
+
|
|
15
|
+
fixComponents(meta);
|
|
16
|
+
|
|
17
|
+
meta.components = meta.components || [];
|
|
18
|
+
|
|
19
|
+
if (!meta.components.find((x) => x.name === component)) {
|
|
20
|
+
printError(`Cannot find component with name ${chalk.cyan(component)}`);
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
meta.components = meta.components.filter((x) => x.name !== component);
|
|
25
|
+
|
|
26
|
+
updateMetaFile(file, meta, { fix: false });
|
|
27
|
+
printSuccess(`Component ${chalk.cyan(component)} was successfully removed`);
|
|
28
|
+
|
|
29
|
+
process.exit(0);
|
|
30
|
+
} catch (error) {
|
|
31
|
+
debug('Failed remove component', error);
|
|
32
|
+
printError('Failed remove component: ', error.message);
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
require('dotenv-flow').config({ silent: true, node_env: 'test' });
|
|
2
|
+
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
const { fromSecretKey } = require('@ocap/wallet');
|
|
5
|
+
const { types } = require('@ocap/mcrypto');
|
|
6
|
+
const { replaceSlotToIp } = require('@blocklet/meta/lib/util');
|
|
7
|
+
const { BlockletStatus, BlockletEvents } = require('@blocklet/constant');
|
|
8
|
+
const getIP = require('@abtnode/util/lib/get-ip');
|
|
9
|
+
const sleep = require('@abtnode/util/lib/sleep');
|
|
10
|
+
|
|
11
|
+
const { print, printError, printInfo, printSuccess, getDevUrl, printWarning } = require('../../util');
|
|
12
|
+
const { getNode } = require('../../node');
|
|
13
|
+
const { getOption, waitForAnyEvents, checkNodeRunning, getAccessibleUrl } = require('./dev');
|
|
14
|
+
|
|
15
|
+
const ACTIONS = {
|
|
16
|
+
INIT: 'init',
|
|
17
|
+
START: 'start',
|
|
18
|
+
REMOVE: 'remove',
|
|
19
|
+
RESET: 'reset',
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const getUtil = ({ node, socket, publishEvent, appName, appSk, ownerSk }) => {
|
|
23
|
+
const app = fromSecretKey(appSk, { role: types.RoleType.ROLE_APPLICATION });
|
|
24
|
+
|
|
25
|
+
printInfo(`Test blocklet did: ${chalk.cyan(app.address)}`);
|
|
26
|
+
|
|
27
|
+
// init app container
|
|
28
|
+
const initApp = async () => {
|
|
29
|
+
const owner = fromSecretKey(ownerSk, { role: types.RoleType.ROLE_ACCOUNT });
|
|
30
|
+
printInfo(`Test owner did: ${chalk.cyan(owner.address)}`);
|
|
31
|
+
|
|
32
|
+
const exist = await node.getBlocklet({ did: app.address, attachConfig: false });
|
|
33
|
+
if (exist) {
|
|
34
|
+
printWarning(`Test blocklet already exist: ${chalk.cyan(app.address)}`);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
const blocklet = await node.installBlocklet({
|
|
40
|
+
appSk,
|
|
41
|
+
title: appName,
|
|
42
|
+
description: `Test blocklet ${appName}`,
|
|
43
|
+
skSource: 'blocklet:cli:test:init',
|
|
44
|
+
});
|
|
45
|
+
await publishEvent(BlockletEvents.installed, { blocklet });
|
|
46
|
+
await waitForAnyEvents({ blocklet, socket, events: [BlockletEvents.installed] }); // wait for db updating
|
|
47
|
+
printInfo(`Test blocklet created: ${app.address}`);
|
|
48
|
+
|
|
49
|
+
await node.setBlockletInitialized({ did: app.address, owner: { did: owner.address, pk: owner.publicKey } });
|
|
50
|
+
await node.addUser({
|
|
51
|
+
teamDid: app.address,
|
|
52
|
+
force: true,
|
|
53
|
+
user: {
|
|
54
|
+
did: owner.address,
|
|
55
|
+
pk: owner.publicKey,
|
|
56
|
+
role: 'owner',
|
|
57
|
+
fullName: 'TestOwner',
|
|
58
|
+
email: 'blocklet@arcblock.io',
|
|
59
|
+
approved: true,
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
await node.issuePassportToUser({ teamDid: app.address, userDid: owner.address, role: 'owner', notify: false });
|
|
63
|
+
await node.issuePassportToUser({ teamDid: app.address, userDid: owner.address, role: 'admin', notify: false });
|
|
64
|
+
await node.issuePassportToUser({ teamDid: app.address, userDid: owner.address, role: 'member', notify: false });
|
|
65
|
+
printInfo(`Test blocklet initialized: ${app.address}`);
|
|
66
|
+
} catch (err) {
|
|
67
|
+
printError(`Test blocklet init failed: ${err.message}`);
|
|
68
|
+
console.error(err);
|
|
69
|
+
// eslint-disable-next-line no-use-before-define
|
|
70
|
+
await removeApp();
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const removeApp = async () => {
|
|
76
|
+
try {
|
|
77
|
+
const deleted = await node.deleteBlocklet({
|
|
78
|
+
did: app.address,
|
|
79
|
+
keepData: false,
|
|
80
|
+
keepLogsDir: false,
|
|
81
|
+
keepConfigs: false,
|
|
82
|
+
});
|
|
83
|
+
await publishEvent(BlockletEvents.removed, { blocklet: deleted, context: { keepRouting: false } });
|
|
84
|
+
print(`Test blocklet was removed: ${app.address}`);
|
|
85
|
+
} catch (err) {
|
|
86
|
+
console.error(err);
|
|
87
|
+
}
|
|
88
|
+
await sleep(200);
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const startApp = async () => {
|
|
92
|
+
try {
|
|
93
|
+
const blocklet = await node.ensureBlockletIntegrity(app.address);
|
|
94
|
+
|
|
95
|
+
await node.startBlocklet({
|
|
96
|
+
did: app.address,
|
|
97
|
+
throwOnError: true,
|
|
98
|
+
checkHealthImmediately: true,
|
|
99
|
+
atomic: true,
|
|
100
|
+
componentDids: null,
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
blocklet.status = BlockletStatus.running;
|
|
104
|
+
await publishEvent(BlockletEvents.statusChange, blocklet);
|
|
105
|
+
|
|
106
|
+
await sleep(1000);
|
|
107
|
+
printSuccess(`Test blocklet successfully started: ${app.address}`);
|
|
108
|
+
|
|
109
|
+
// print info
|
|
110
|
+
const info = await node.getNodeInfo();
|
|
111
|
+
const port = Number(info.routing.httpsPort) !== 443 ? `:${info.routing.httpsPort}` : '';
|
|
112
|
+
const url = await getDevUrl({
|
|
113
|
+
getUrl: async () => {
|
|
114
|
+
const ips = await getIP();
|
|
115
|
+
|
|
116
|
+
const urls = blocklet.site.domainAliases.map(
|
|
117
|
+
(x) => `https://${replaceSlotToIp(x.value, ips.internal)}${port}`
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
return getAccessibleUrl(urls);
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
if (url) {
|
|
125
|
+
print('');
|
|
126
|
+
printInfo('You can access with the following URL\n');
|
|
127
|
+
print(`- ${chalk.cyan(url)}`);
|
|
128
|
+
}
|
|
129
|
+
} catch (err) {
|
|
130
|
+
console.error(err);
|
|
131
|
+
printError(err.message);
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const resetApp = async () => {
|
|
136
|
+
try {
|
|
137
|
+
await node.getBlocklet({ did: app.address });
|
|
138
|
+
await node.resetBlocklet({ did: app.address });
|
|
139
|
+
print(`\nTest blocklet has been reset: ${app.address}\n`);
|
|
140
|
+
} catch (err) {
|
|
141
|
+
console.error(err);
|
|
142
|
+
printError(err.message);
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
return {
|
|
147
|
+
initApp,
|
|
148
|
+
startApp,
|
|
149
|
+
resetApp,
|
|
150
|
+
removeApp,
|
|
151
|
+
};
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const run = async (action, { appName, appSk, ownerSk }) => {
|
|
155
|
+
try {
|
|
156
|
+
await checkNodeRunning();
|
|
157
|
+
|
|
158
|
+
const { node, publishEvent, getWsClient } = await getNode({ dir: process.cwd() });
|
|
159
|
+
const socket = await getWsClient();
|
|
160
|
+
const util = getUtil({ node, publishEvent, socket, appName, appSk, ownerSk });
|
|
161
|
+
|
|
162
|
+
node.onReady(async () => {
|
|
163
|
+
if (action === ACTIONS.INIT) {
|
|
164
|
+
await util.initApp();
|
|
165
|
+
process.exit(0);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (action === ACTIONS.REMOVE) {
|
|
169
|
+
await util.removeApp();
|
|
170
|
+
process.exit(0);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (action === ACTIONS.START) {
|
|
174
|
+
await util.startApp();
|
|
175
|
+
process.exit(0);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (action === ACTIONS.RESET) {
|
|
179
|
+
await util.resetApp();
|
|
180
|
+
process.exit(0);
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
} catch (err) {
|
|
184
|
+
printError(err.message);
|
|
185
|
+
process.exit(1);
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
const getOptions = (command) => ({
|
|
190
|
+
appSk: getOption(command, 'appSk'),
|
|
191
|
+
appName: getOption(command, 'appName'),
|
|
192
|
+
ownerSk: getOption(command, 'ownerSk'),
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
module.exports = {
|
|
196
|
+
run: (command) => run(null, getOptions(command)),
|
|
197
|
+
init: (command) => run('init', getOptions(command)),
|
|
198
|
+
start: (command) => run('start', getOptions(command)),
|
|
199
|
+
remove: (command) => run('remove', getOptions(command)),
|
|
200
|
+
reset: (command) => run('reset', getOptions(command)),
|
|
201
|
+
};
|