@airmoney-degn/airmoney-cli 0.24.0 → 0.25.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/dist/cli/build.js +9 -7
- package/dist/cli/upload.js +79 -22
- package/dist/config.json +1 -1
- package/dist/index.js +8 -3
- package/dist/util/metadata.js +74 -6
- package/dist/util/tarball.js +78 -0
- package/package.json +6 -3
package/dist/cli/build.js
CHANGED
|
@@ -41,12 +41,13 @@ const child_process_1 = require("child_process");
|
|
|
41
41
|
const metadata_1 = require("../util/metadata");
|
|
42
42
|
const tarball_1 = require("../util/tarball");
|
|
43
43
|
const LogService_1 = require("../service/log/LogService");
|
|
44
|
-
async function buildCommand({ locationFolder }) {
|
|
44
|
+
async function buildCommand({ locationFolder, publicDir }) {
|
|
45
45
|
const projectPath = locationFolder ? path.resolve(process.cwd(), locationFolder) : process.cwd();
|
|
46
46
|
const meta = await (0, metadata_1.loadMetadata)(projectPath);
|
|
47
47
|
if (!meta) {
|
|
48
48
|
throw new Error('No metadata.json found.');
|
|
49
49
|
}
|
|
50
|
+
(0, metadata_1.applyVersionFromCargoToml)(meta, projectPath);
|
|
50
51
|
// Populate build metadata before building
|
|
51
52
|
(0, LogService_1.log)('Populating build metadata...').white();
|
|
52
53
|
(0, metadata_1.populateBuildMetadata)(meta, projectPath);
|
|
@@ -71,12 +72,10 @@ async function buildCommand({ locationFolder }) {
|
|
|
71
72
|
cwd: projectPath,
|
|
72
73
|
stdio: 'inherit',
|
|
73
74
|
});
|
|
74
|
-
// 2. Find the binary
|
|
75
|
-
|
|
76
|
-
const binName = "user_app";
|
|
75
|
+
// 2. Find the binary (name from Cargo.toml [package] name)
|
|
76
|
+
const binName = (0, metadata_1.readPackageNameFromCargoToml)(projectPath) ?? 'user_app';
|
|
77
77
|
const binPath = path.join(projectPath, 'target', 'aarch64-unknown-linux-gnu', 'release', binName);
|
|
78
78
|
if (!fs.existsSync(binPath)) {
|
|
79
|
-
// Fallback: try to find any executable in the release folder if the name doesn't match exactly
|
|
80
79
|
(0, LogService_1.log)(`Binary not found at expected path: ${binPath}`).white();
|
|
81
80
|
throw new Error(`Could not find native binary for app: ${binName}`);
|
|
82
81
|
}
|
|
@@ -91,8 +90,11 @@ async function buildCommand({ locationFolder }) {
|
|
|
91
90
|
if (fs.existsSync(logoPath)) {
|
|
92
91
|
fs.copyFileSync(logoPath, path.join(tempDir, 'dapp-logo.png'));
|
|
93
92
|
}
|
|
94
|
-
|
|
95
|
-
|
|
93
|
+
fs.copyFileSync(binPath, path.join(tempDir, binName));
|
|
94
|
+
if (publicDir) {
|
|
95
|
+
const publicPath = path.join(projectPath, publicDir);
|
|
96
|
+
(0, tarball_1.copyDirContents)(publicPath, tempDir);
|
|
97
|
+
}
|
|
96
98
|
// 4. Pack the project
|
|
97
99
|
await (0, tarball_1.packProject)(meta, tempDir);
|
|
98
100
|
(0, LogService_1.log)('Build and packaging complete!').green();
|
package/dist/cli/upload.js
CHANGED
|
@@ -119,30 +119,44 @@ async function calculateFileHash(filePath) {
|
|
|
119
119
|
/**
|
|
120
120
|
* Prepares the package by packing and reading it
|
|
121
121
|
*/
|
|
122
|
-
async function preparePackage(meta, projectPath) {
|
|
122
|
+
async function preparePackage(meta, projectPath, publicDir) {
|
|
123
123
|
const pkgName = (0, metadata_1.getPackageName)(meta);
|
|
124
124
|
if (meta.type === 'Native') {
|
|
125
125
|
(0, LogService_1.log)(`Preparing Native package for ${meta.displayName}...`).white();
|
|
126
|
-
|
|
127
|
-
const
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
|
|
126
|
+
const binName = (0, metadata_1.readPackageNameFromCargoToml)(projectPath) ?? 'user_app';
|
|
127
|
+
const binInTarget = path_1.default.join(projectPath, 'target', 'aarch64-unknown-linux-gnu', 'release', binName);
|
|
128
|
+
const binAtRoot = path_1.default.join(projectPath, binName);
|
|
129
|
+
const metadataAtRoot = path_1.default.join(projectPath, 'metadata.json');
|
|
130
|
+
const logoAtRoot = path_1.default.join(projectPath, 'dapp-logo.png');
|
|
131
|
+
// If folder already has binary + metadata + dapp-logo at root (e.g. dist/ or production/), zip the whole folder (includes all *.png and other assets)
|
|
132
|
+
if (fs.existsSync(binAtRoot) && fs.existsSync(metadataAtRoot) && fs.existsSync(logoAtRoot)) {
|
|
133
|
+
(0, LogService_1.log)(`Uploading folder as-is (binary and metadata at root; all files will be included).`).white();
|
|
134
|
+
(0, LogService_1.log)(`Packing ${pkgName}...`).white();
|
|
135
|
+
await (0, tarball_1.packProject)(meta, projectPath);
|
|
131
136
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
fs.
|
|
137
|
+
else if (fs.existsSync(binInTarget)) {
|
|
138
|
+
// Build layout: binary in target/; copy only metadata, logo, binary to avoid target/ and other junk
|
|
139
|
+
const tempDir = fs.mkdtempSync(path_1.default.join(os.tmpdir(), 'airmoney-upload-'));
|
|
140
|
+
try {
|
|
141
|
+
fs.copyFileSync(path_1.default.join(projectPath, 'metadata.json'), path_1.default.join(tempDir, 'metadata.json'));
|
|
142
|
+
const logoPath = path_1.default.join(projectPath, 'dapp-logo.png');
|
|
143
|
+
if (fs.existsSync(logoPath)) {
|
|
144
|
+
fs.copyFileSync(logoPath, path_1.default.join(tempDir, 'dapp-logo.png'));
|
|
145
|
+
}
|
|
146
|
+
fs.copyFileSync(binInTarget, path_1.default.join(tempDir, binName));
|
|
147
|
+
if (publicDir) {
|
|
148
|
+
const publicPath = path_1.default.join(projectPath, publicDir);
|
|
149
|
+
(0, tarball_1.copyDirContents)(publicPath, tempDir);
|
|
150
|
+
}
|
|
151
|
+
(0, LogService_1.log)(`Packing ${pkgName}...`).white();
|
|
152
|
+
await (0, tarball_1.packProject)(meta, tempDir);
|
|
153
|
+
}
|
|
154
|
+
finally {
|
|
155
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
139
156
|
}
|
|
140
|
-
fs.copyFileSync(binPath, path_1.default.join(tempDir, "user_app"));
|
|
141
|
-
(0, LogService_1.log)(`Packing ${pkgName}...`).white();
|
|
142
|
-
await (0, tarball_1.packProject)(meta, tempDir);
|
|
143
157
|
}
|
|
144
|
-
|
|
145
|
-
|
|
158
|
+
else {
|
|
159
|
+
throw new Error(`Native binary not found. Either run "airmoney-cli build" first, or point -f to a folder that already contains ${binName}, metadata.json, and dapp-logo.png at root (e.g. dist/).`);
|
|
146
160
|
}
|
|
147
161
|
}
|
|
148
162
|
else {
|
|
@@ -186,6 +200,10 @@ async function uploadPackageToServer(network, userId, apiKey, meta, pkgPath, fil
|
|
|
186
200
|
});
|
|
187
201
|
const text = await res.text();
|
|
188
202
|
if (!res.ok) {
|
|
203
|
+
if (res.status === 403 && text.includes('package is belong to someone else')) {
|
|
204
|
+
(0, LogService_1.log)(`Error: The package '${meta.name}' is already owned by another user.`).red();
|
|
205
|
+
throw new Error(`Upload failed: Package name '${meta.name}' is already taken.`);
|
|
206
|
+
}
|
|
189
207
|
(0, LogService_1.log)(`Error uploading package: ${text}`).red();
|
|
190
208
|
throw new Error(`Upload failed: ${text}`);
|
|
191
209
|
}
|
|
@@ -208,12 +226,50 @@ function cleanupPackageFile(pkgPath) {
|
|
|
208
226
|
/**
|
|
209
227
|
* Main upload command
|
|
210
228
|
*/
|
|
211
|
-
async function uploadCommand({ network, locationFolder, }) {
|
|
229
|
+
async function uploadCommand({ network, locationFolder, publicDir, packagePath: packagePathOpt, }) {
|
|
212
230
|
let packageData = null;
|
|
231
|
+
let createdPackageByUs = false;
|
|
213
232
|
try {
|
|
214
233
|
const credentials = (0, env_1.validateCredential)();
|
|
215
234
|
const { userId, apiKey, network } = credentials;
|
|
216
235
|
(0, format_1.logUserAndNetwork)(userId, network);
|
|
236
|
+
if (packagePathOpt) {
|
|
237
|
+
// Upload an existing zip (e.g. output of airmoney-cli build)
|
|
238
|
+
const zipPath = path_1.default.isAbsolute(packagePathOpt)
|
|
239
|
+
? packagePathOpt
|
|
240
|
+
: path_1.default.join(process.cwd(), packagePathOpt);
|
|
241
|
+
if (!fs.existsSync(zipPath) || !fs.statSync(zipPath).isFile()) {
|
|
242
|
+
throw new Error(`Package file not found: ${zipPath}`);
|
|
243
|
+
}
|
|
244
|
+
const projectPath = path_1.default.dirname(zipPath);
|
|
245
|
+
const meta = await (0, metadata_1.loadMetadata)(projectPath);
|
|
246
|
+
if (!meta) {
|
|
247
|
+
throw new Error(`No metadata.json found next to the package. Put metadata.json in the same directory as ${path_1.default.basename(zipPath)}.`);
|
|
248
|
+
}
|
|
249
|
+
(0, LogService_1.log)('Verifying zip package...').white();
|
|
250
|
+
const verifyOptions = meta.type === 'Native'
|
|
251
|
+
? { binaryName: (0, metadata_1.readPackageNameFromCargoToml)(projectPath) ?? 'user_app' }
|
|
252
|
+
: undefined;
|
|
253
|
+
await (0, tarball_1.verifyZipPackage)(zipPath, meta.type, verifyOptions);
|
|
254
|
+
(0, LogService_1.log)('Zip package verified.').green();
|
|
255
|
+
const dappService = new DappService_1.DappService(userId, apiKey, network);
|
|
256
|
+
(0, LogService_1.log)(`Checking for existing versions of ${meta.name}...`).white();
|
|
257
|
+
const existingBuilds = await dappService.fetchDappVersions(meta.name);
|
|
258
|
+
const conflict = existingBuilds.find(b => b.version === meta.version);
|
|
259
|
+
if (conflict) {
|
|
260
|
+
(0, LogService_1.log)(`\nERROR: Version ${meta.version} already exists on the server.`).red();
|
|
261
|
+
(0, LogService_1.log)(`Status: ${conflict.status || 'Unknown'}`).red();
|
|
262
|
+
(0, LogService_1.log)(`Please bump your version and rebuild before uploading.`).yellow();
|
|
263
|
+
throw new Error(`Version conflict: ${meta.version} already exists.`);
|
|
264
|
+
}
|
|
265
|
+
const fileSize = fs.statSync(zipPath).size;
|
|
266
|
+
const fileHash = await calculateFileHash(zipPath);
|
|
267
|
+
(0, LogService_1.log)(`Package Hash: ${fileHash}`).white();
|
|
268
|
+
(0, LogService_1.log)(`Package Size: ${formatFileSize(fileSize)}`).white();
|
|
269
|
+
(0, LogService_1.log)('Publishing package to DEGN Dapp Store...').white();
|
|
270
|
+
await uploadPackageToServer(network || 'devnet', userId, apiKey, meta, zipPath, fileSize);
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
217
273
|
const projectPath = getProjectPath(locationFolder);
|
|
218
274
|
// Prompt for confirmation if uploading from current directory
|
|
219
275
|
if (!locationFolder) {
|
|
@@ -227,6 +283,7 @@ async function uploadCommand({ network, locationFolder, }) {
|
|
|
227
283
|
(0, LogService_1.log)(`Upload folder: ${path_1.default.resolve(projectPath)}`).white();
|
|
228
284
|
}
|
|
229
285
|
const meta = await loadAndValidateMetadata(locationFolder);
|
|
286
|
+
(0, metadata_1.applyVersionFromCargoToml)(meta, projectPath);
|
|
230
287
|
// Version Conflict Check
|
|
231
288
|
const dappService = new DappService_1.DappService(userId, apiKey, network);
|
|
232
289
|
(0, LogService_1.log)(`Checking for existing versions of ${meta.name}...`).white();
|
|
@@ -242,7 +299,8 @@ async function uploadCommand({ network, locationFolder, }) {
|
|
|
242
299
|
(0, LogService_1.log)('Populating build metadata...').white();
|
|
243
300
|
(0, metadata_1.populateBuildMetadata)(meta, projectPath);
|
|
244
301
|
(0, metadata_1.saveMetadata)(meta, projectPath);
|
|
245
|
-
packageData = await preparePackage(meta, projectPath);
|
|
302
|
+
packageData = await preparePackage(meta, projectPath, publicDir);
|
|
303
|
+
createdPackageByUs = true;
|
|
246
304
|
(0, LogService_1.log)('Publishing package to DEGN Dapp Store...').white();
|
|
247
305
|
await uploadPackageToServer(network || "devnet", userId, apiKey, meta, packageData.pkgPath, packageData.fileSize);
|
|
248
306
|
}
|
|
@@ -252,8 +310,7 @@ async function uploadCommand({ network, locationFolder, }) {
|
|
|
252
310
|
throw err;
|
|
253
311
|
}
|
|
254
312
|
finally {
|
|
255
|
-
|
|
256
|
-
if (packageData) {
|
|
313
|
+
if (createdPackageByUs && packageData) {
|
|
257
314
|
cleanupPackageFile(packageData.pkgPath);
|
|
258
315
|
}
|
|
259
316
|
}
|
package/dist/config.json
CHANGED
package/dist/index.js
CHANGED
|
@@ -54,10 +54,11 @@ program
|
|
|
54
54
|
.command('build')
|
|
55
55
|
.description('Build and package the project')
|
|
56
56
|
.option('-f, --app-path <string>', 'path to the project')
|
|
57
|
+
.option('--public-dir <string>', 'folder whose contents are bundled as static assets (default: public)', 'public')
|
|
57
58
|
.action(async (opts) => {
|
|
58
59
|
try {
|
|
59
|
-
const { appPath } = opts;
|
|
60
|
-
await (0, build_1.buildCommand)({ locationFolder: appPath });
|
|
60
|
+
const { appPath, publicDir } = opts;
|
|
61
|
+
await (0, build_1.buildCommand)({ locationFolder: appPath, publicDir });
|
|
61
62
|
}
|
|
62
63
|
catch (err) {
|
|
63
64
|
process.exit(1);
|
|
@@ -134,8 +135,10 @@ program
|
|
|
134
135
|
.description('Publish the app to the DEGN Dapp Store')
|
|
135
136
|
.option('-n, --network <string>', 'network devnet|mainnet')
|
|
136
137
|
.option('-f, --index-app-path <string>', 'path for the index.html', './')
|
|
138
|
+
.option('-p, --package <path>', 'path to existing zip (e.g. output of build); upload this file and skip re-packing')
|
|
139
|
+
.option('--public-dir <string>', 'folder whose contents are bundled as static assets (default: public)', 'public')
|
|
137
140
|
.action(async (opts) => {
|
|
138
|
-
let { network, indexAppPath } = opts;
|
|
141
|
+
let { network, indexAppPath, package: packagePath, publicDir } = opts;
|
|
139
142
|
if (indexAppPath == './') {
|
|
140
143
|
indexAppPath = undefined;
|
|
141
144
|
}
|
|
@@ -143,6 +146,8 @@ program
|
|
|
143
146
|
await (0, upload_1.uploadCommand)({
|
|
144
147
|
network,
|
|
145
148
|
locationFolder: indexAppPath,
|
|
149
|
+
publicDir,
|
|
150
|
+
packagePath,
|
|
146
151
|
});
|
|
147
152
|
}
|
|
148
153
|
catch (err) {
|
package/dist/util/metadata.js
CHANGED
|
@@ -32,13 +32,20 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
32
32
|
return result;
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
35
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
39
|
exports.loadMetadata = loadMetadata;
|
|
37
40
|
exports.saveMetadata = saveMetadata;
|
|
38
41
|
exports.getPackageName = getPackageName;
|
|
42
|
+
exports.readPackageNameFromCargoToml = readPackageNameFromCargoToml;
|
|
43
|
+
exports.readVersionFromCargoToml = readVersionFromCargoToml;
|
|
44
|
+
exports.applyVersionFromCargoToml = applyVersionFromCargoToml;
|
|
39
45
|
exports.populateBuildMetadata = populateBuildMetadata;
|
|
40
46
|
const fs = __importStar(require("fs"));
|
|
41
47
|
const path = __importStar(require("path"));
|
|
48
|
+
const toml_1 = __importDefault(require("toml"));
|
|
42
49
|
const LogService_1 = require("../service/log/LogService");
|
|
43
50
|
const child_process_1 = require("child_process");
|
|
44
51
|
const remote_1 = require("./remote");
|
|
@@ -87,14 +94,75 @@ function getPackageName(pkg) {
|
|
|
87
94
|
// e.g. myApp-0.1.0.zip
|
|
88
95
|
return `${pkg.name}-${pkg.version}.zip`;
|
|
89
96
|
}
|
|
97
|
+
function parseCargoToml(projectPath) {
|
|
98
|
+
const cargoPath = path.join(projectPath, 'Cargo.toml');
|
|
99
|
+
if (!fs.existsSync(cargoPath)) {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
try {
|
|
103
|
+
const content = fs.readFileSync(cargoPath, 'utf8');
|
|
104
|
+
return toml_1.default.parse(content);
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Reads the package name from Cargo.toml [package] section (used as binary name).
|
|
112
|
+
* Returns null if file is missing or name is not found.
|
|
113
|
+
*/
|
|
114
|
+
function readPackageNameFromCargoToml(projectPath) {
|
|
115
|
+
const parsed = parseCargoToml(projectPath);
|
|
116
|
+
const name = parsed?.package?.name;
|
|
117
|
+
return typeof name === 'string' ? name : null;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Reads the package version from Cargo.toml [package] section.
|
|
121
|
+
* Returns null if file is missing or version is not found.
|
|
122
|
+
*/
|
|
123
|
+
function readVersionFromCargoToml(projectPath) {
|
|
124
|
+
const parsed = parseCargoToml(projectPath);
|
|
125
|
+
const version = parsed?.package?.version;
|
|
126
|
+
return typeof version === 'string' ? version : null;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* For Native projects, reads version from Cargo.toml, sets meta.version, and saves metadata.json.
|
|
130
|
+
* No-op for Web or when Cargo.toml is missing / has no version.
|
|
131
|
+
*/
|
|
132
|
+
function applyVersionFromCargoToml(meta, projectPath) {
|
|
133
|
+
if (meta.type !== 'Native') {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const versionFromCargo = readVersionFromCargoToml(projectPath);
|
|
137
|
+
if (versionFromCargo) {
|
|
138
|
+
meta.version = versionFromCargo;
|
|
139
|
+
saveMetadata(meta, projectPath);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
90
142
|
function populateBuildMetadata(pkg, projectPath) {
|
|
91
|
-
// 1. Build Number:
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
143
|
+
// 1. Build Number: use git rev-list --count HEAD; on failure fall back to increment or timestamp
|
|
144
|
+
try {
|
|
145
|
+
const count = (0, child_process_1.execSync)('git rev-list --count HEAD', {
|
|
146
|
+
cwd: projectPath,
|
|
147
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
148
|
+
encoding: 'utf8',
|
|
149
|
+
})
|
|
150
|
+
.trim();
|
|
151
|
+
if (count !== '') {
|
|
152
|
+
pkg.buildNumber = count;
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
throw new Error('empty');
|
|
156
|
+
}
|
|
95
157
|
}
|
|
96
|
-
|
|
97
|
-
|
|
158
|
+
catch {
|
|
159
|
+
const currentBuild = parseInt(pkg.buildNumber || '0', 10);
|
|
160
|
+
if (!isNaN(currentBuild)) {
|
|
161
|
+
pkg.buildNumber = (currentBuild + 1).toString();
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
pkg.buildNumber = Math.floor(Date.now() / 1000).toString();
|
|
165
|
+
}
|
|
98
166
|
}
|
|
99
167
|
// 2. Commit Hash
|
|
100
168
|
try {
|
package/dist/util/tarball.js
CHANGED
|
@@ -36,15 +36,93 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
36
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.verifyZipPackage = verifyZipPackage;
|
|
40
|
+
exports.copyDirContents = copyDirContents;
|
|
39
41
|
exports.packProject = packProject;
|
|
40
42
|
const fs = __importStar(require("fs"));
|
|
41
43
|
const path = __importStar(require("path"));
|
|
42
44
|
const crypto = __importStar(require("crypto"));
|
|
43
45
|
const archiver_1 = __importDefault(require("archiver"));
|
|
46
|
+
const yauzl_1 = __importDefault(require("yauzl"));
|
|
44
47
|
const metadata_1 = require("./metadata");
|
|
45
48
|
const LogService_1 = require("../service/log/LogService");
|
|
46
49
|
const REQUIRED_FILES_WEB = ['metadata.json', 'dapp-logo.png', 'index.html'];
|
|
47
50
|
const REQUIRED_FILES_NATIVE = ['metadata.json', 'dapp-logo.png'];
|
|
51
|
+
/**
|
|
52
|
+
* Returns entry file names in the zip (directories excluded; names may include path).
|
|
53
|
+
*/
|
|
54
|
+
function getZipEntryNames(zipPath) {
|
|
55
|
+
return new Promise((resolve, reject) => {
|
|
56
|
+
yauzl_1.default.open(zipPath, { lazyEntries: true }, (err, zipfile) => {
|
|
57
|
+
if (err) {
|
|
58
|
+
reject(err);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const names = [];
|
|
62
|
+
zipfile.on('entry', (entry) => {
|
|
63
|
+
if (!/\/$/.test(entry.fileName)) {
|
|
64
|
+
names.push(entry.fileName);
|
|
65
|
+
}
|
|
66
|
+
zipfile.readEntry();
|
|
67
|
+
});
|
|
68
|
+
zipfile.on('end', () => {
|
|
69
|
+
zipfile.close();
|
|
70
|
+
resolve(names);
|
|
71
|
+
});
|
|
72
|
+
zipfile.readEntry();
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Verifies the zip is valid and contains required files for the given package type.
|
|
78
|
+
* For Native, also verifies the binary file is present when binaryName is provided.
|
|
79
|
+
* @throws Error if the zip is invalid or missing required files
|
|
80
|
+
*/
|
|
81
|
+
async function verifyZipPackage(zipPath, type, options) {
|
|
82
|
+
const required = type === 'Native' ? REQUIRED_FILES_NATIVE : REQUIRED_FILES_WEB;
|
|
83
|
+
let names;
|
|
84
|
+
try {
|
|
85
|
+
names = await getZipEntryNames(zipPath);
|
|
86
|
+
}
|
|
87
|
+
catch (err) {
|
|
88
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
89
|
+
throw new Error(`Invalid or corrupt zip file: ${msg}`);
|
|
90
|
+
}
|
|
91
|
+
const hasFile = (name) => names.some(n => n === name || n.endsWith('/' + name));
|
|
92
|
+
const missing = required.filter(r => !hasFile(r));
|
|
93
|
+
if (missing.length > 0) {
|
|
94
|
+
throw new Error(`Zip is missing required files: ${missing.join(', ')}. Required for ${type}: ${required.join(', ')}.`);
|
|
95
|
+
}
|
|
96
|
+
if (type === 'Native' && options?.binaryName) {
|
|
97
|
+
if (!hasFile(options.binaryName)) {
|
|
98
|
+
throw new Error(`Zip is missing Native binary: ${options.binaryName}. Required for Native packages.`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Copies the contents of srcDir into destDir (merge; destDir gets srcDir's files/subdirs at root).
|
|
104
|
+
*/
|
|
105
|
+
function copyDirContents(srcDir, destDir) {
|
|
106
|
+
if (!fs.existsSync(srcDir)) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const stat = fs.statSync(srcDir);
|
|
110
|
+
if (!stat.isDirectory()) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const entries = fs.readdirSync(srcDir, { withFileTypes: true });
|
|
114
|
+
for (const e of entries) {
|
|
115
|
+
const src = path.join(srcDir, e.name);
|
|
116
|
+
const dest = path.join(destDir, e.name);
|
|
117
|
+
if (e.isDirectory()) {
|
|
118
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
119
|
+
copyDirContents(src, dest);
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
fs.copyFileSync(src, dest);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
48
126
|
/**
|
|
49
127
|
* Validates that the project path exists
|
|
50
128
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@airmoney-degn/airmoney-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.25.0",
|
|
4
4
|
"description": "airmoney-cli is a command-line interface tool designed to facilitate the development and management of decentralized applications (DApps) for Airmoney.",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -50,8 +50,10 @@
|
|
|
50
50
|
"node-fetch": "^2.6.7",
|
|
51
51
|
"open": "^8.4.0",
|
|
52
52
|
"tar": "^6.1.13",
|
|
53
|
+
"toml": "^3.0.0",
|
|
53
54
|
"websocket": "^1.0.35",
|
|
54
|
-
"ws": "^8.18.1"
|
|
55
|
+
"ws": "^8.18.1",
|
|
56
|
+
"yauzl": "^3.2.0"
|
|
55
57
|
},
|
|
56
58
|
"devDependencies": {
|
|
57
59
|
"@airmoney-degn/controller-sdk": "^5.4.1",
|
|
@@ -67,8 +69,9 @@
|
|
|
67
69
|
"@types/node-fetch": "^2.6.12",
|
|
68
70
|
"@types/tar": "^6.1.13",
|
|
69
71
|
"@types/websocket": "^1.0.10",
|
|
72
|
+
"@types/yauzl": "^2.10.3",
|
|
70
73
|
"prettier": "^3.6.1",
|
|
71
74
|
"typescript": "^5.0.4"
|
|
72
75
|
},
|
|
73
76
|
"packageManager": "npm@>=10.9.3 <11"
|
|
74
|
-
}
|
|
77
|
+
}
|