node-nim 10.9.71-beta.69 → 10.9.71-beta.87
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/package.json +6 -7
- package/script/download-sdk.js +311 -150
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-nim",
|
|
3
|
-
"version": "10.9.71-beta.
|
|
3
|
+
"version": "10.9.71-beta.87",
|
|
4
4
|
"description": "NetEase IM nodejs wrapper based on NetEase IM C++ SDK",
|
|
5
5
|
"main": "dist/node-nim.js",
|
|
6
6
|
"bin": {
|
|
@@ -20,8 +20,8 @@
|
|
|
20
20
|
"build_ts": "rimraf ./dist && rimraf ./types && tsc",
|
|
21
21
|
"build_html_doc": "typedoc --plugin typedoc-github-theme",
|
|
22
22
|
"prepublishOnly": "npm run build_ts",
|
|
23
|
-
"
|
|
24
|
-
"download_sdk": "node
|
|
23
|
+
"postinstall": "node script/download-sdk.js",
|
|
24
|
+
"download_sdk": "node script/download-sdk.js",
|
|
25
25
|
"publish_to_netease_npm": "node script/publish-to-netease-npm.js http://npm.netease.im/",
|
|
26
26
|
"publish_to_npmjs": "node script/publish-to-netease-npm.js https://registry.npmjs.org",
|
|
27
27
|
"test": "npx cross-env BABEL_ENV=test mocha"
|
|
@@ -33,11 +33,10 @@
|
|
|
33
33
|
]
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"
|
|
36
|
+
"axios": "^1.6.2",
|
|
37
37
|
"compare-versions": "^4.1.4",
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"tar": "^6.2.0"
|
|
38
|
+
"decompress": "^4.2.1",
|
|
39
|
+
"eventemitter3": "^4.0.7"
|
|
41
40
|
},
|
|
42
41
|
"devDependencies": {
|
|
43
42
|
"@babel/preset-env": "^7.24.0",
|
package/script/download-sdk.js
CHANGED
|
@@ -1,12 +1,8 @@
|
|
|
1
|
-
const
|
|
1
|
+
const axios = require('axios')
|
|
2
2
|
const fs = require('fs')
|
|
3
3
|
const path = require('path')
|
|
4
4
|
const compareVersions = require('compare-versions')
|
|
5
|
-
const
|
|
6
|
-
const tar = require('tar')
|
|
7
|
-
const { pipeline } = require('stream')
|
|
8
|
-
const { promisify } = require('util')
|
|
9
|
-
const pipelineAsync = promisify(pipeline)
|
|
5
|
+
const decompress = require('decompress')
|
|
10
6
|
|
|
11
7
|
// Global variables
|
|
12
8
|
const default_arch = 'universal'
|
|
@@ -18,11 +14,10 @@ const product = 'nim'
|
|
|
18
14
|
const savePath = path.join(__dirname, '..', 'temporary')
|
|
19
15
|
|
|
20
16
|
if (process.env.npm_config_ignoredownloadsdk) {
|
|
21
|
-
console.log('
|
|
17
|
+
console.log('[node-nim] Ignore download product')
|
|
22
18
|
process.exit(0)
|
|
23
19
|
}
|
|
24
20
|
let version
|
|
25
|
-
let downloadUrl = process.env.npm_config_nimsdkurl
|
|
26
21
|
if (process.env.npm_package_version) {
|
|
27
22
|
version = process.env.npm_package_version.split('-')[0]
|
|
28
23
|
}
|
|
@@ -30,155 +25,125 @@ if (process.env.npm_config_nimsdkversion) {
|
|
|
30
25
|
version = process.env.npm_config_nimsdkversion
|
|
31
26
|
}
|
|
32
27
|
|
|
33
|
-
//
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
// Download the file
|
|
40
|
-
const response = await fetch(url)
|
|
41
|
-
if (!response.ok) {
|
|
42
|
-
throw new Error(`Failed to download: ${response.statusText}`)
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// Determine file type from URL
|
|
46
|
-
const isZip = url.toLowerCase().includes('.zip')
|
|
47
|
-
const isTarGz = url.toLowerCase().includes('.tar.gz') || url.toLowerCase().includes('.tgz')
|
|
48
|
-
|
|
49
|
-
let archivePath
|
|
50
|
-
let fileExtension
|
|
51
|
-
|
|
52
|
-
if (isZip) {
|
|
53
|
-
fileExtension = '.zip'
|
|
54
|
-
archivePath = path.join(destination, 'temp.zip')
|
|
55
|
-
} else if (isTarGz) {
|
|
56
|
-
fileExtension = '.tar.gz'
|
|
57
|
-
archivePath = path.join(destination, 'temp.tar.gz')
|
|
58
|
-
} else {
|
|
59
|
-
// Default to zip if we can't determine the type
|
|
60
|
-
fileExtension = '.zip'
|
|
61
|
-
archivePath = path.join(destination, 'temp.zip')
|
|
62
|
-
console.warn('[node-nim] Could not determine archive type from URL, assuming ZIP format')
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Save the downloaded file
|
|
66
|
-
const writeStream = fs.createWriteStream(archivePath)
|
|
67
|
-
await pipelineAsync(response.body, writeStream)
|
|
28
|
+
// Simple logger that works with npm postinstall
|
|
29
|
+
// Use console.error to ensure output is visible when installed as dependency
|
|
30
|
+
// npm captures stdout but is less aggressive with stderr
|
|
31
|
+
function log(message) {
|
|
32
|
+
console.error(message)
|
|
33
|
+
}
|
|
68
34
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
35
|
+
// Progress bar utility - real-time progress bar
|
|
36
|
+
function createProgressBar(total) {
|
|
37
|
+
let lastUpdate = 0
|
|
38
|
+
const updateInterval = 100 // Update every 100ms
|
|
39
|
+
return (loaded) => {
|
|
40
|
+
const now = Date.now()
|
|
41
|
+
const percent = Math.floor((loaded * 100) / total)
|
|
42
|
+
// Update based on time interval or when complete
|
|
43
|
+
if (now - lastUpdate < updateInterval && loaded < total) {
|
|
44
|
+
return
|
|
45
|
+
}
|
|
46
|
+
lastUpdate = now
|
|
47
|
+
const size = formatBytes(loaded)
|
|
48
|
+
const totalSize = formatBytes(total)
|
|
49
|
+
// Create progress bar
|
|
50
|
+
const barLength = 30
|
|
51
|
+
const filledLength = Math.floor((barLength * loaded) / total)
|
|
52
|
+
const bar = '█'.repeat(filledLength) + '░'.repeat(barLength - filledLength)
|
|
53
|
+
// Use \r to overwrite the same line
|
|
54
|
+
process.stderr.write(`\r[node-nim] ⬇ ${bar} ${percent}% (${size}/${totalSize})`)
|
|
55
|
+
// Print newline when complete
|
|
56
|
+
if (loaded >= total) {
|
|
57
|
+
process.stderr.write('\n')
|
|
58
|
+
log(` ✅ Download complete`)
|
|
59
|
+
}
|
|
74
60
|
}
|
|
75
|
-
|
|
76
|
-
// Clean up the temporary archive file
|
|
77
|
-
fs.unlinkSync(archivePath)
|
|
78
61
|
}
|
|
79
62
|
|
|
80
|
-
//
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
zipfile.on('entry', (entry) => {
|
|
88
|
-
// Filter out macOS hidden files (._files)
|
|
89
|
-
if (entry.fileName.includes('._')) {
|
|
90
|
-
zipfile.readEntry()
|
|
91
|
-
return
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
if (/\/$/.test(entry.fileName)) {
|
|
95
|
-
// Directory entry
|
|
96
|
-
const dirPath = path.join(destination, entry.fileName)
|
|
97
|
-
fs.mkdirSync(dirPath, { recursive: true })
|
|
98
|
-
zipfile.readEntry()
|
|
99
|
-
} else {
|
|
100
|
-
// File entry
|
|
101
|
-
zipfile.openReadStream(entry, (err, readStream) => {
|
|
102
|
-
if (err) return reject(err)
|
|
103
|
-
|
|
104
|
-
const filePath = path.join(destination, entry.fileName)
|
|
105
|
-
const fileDir = path.dirname(filePath)
|
|
106
|
-
|
|
107
|
-
if (!fs.existsSync(fileDir)) {
|
|
108
|
-
fs.mkdirSync(fileDir, { recursive: true })
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
const writeStream = fs.createWriteStream(filePath)
|
|
112
|
-
readStream.pipe(writeStream)
|
|
113
|
-
|
|
114
|
-
writeStream.on('close', () => {
|
|
115
|
-
zipfile.readEntry()
|
|
116
|
-
})
|
|
117
|
-
|
|
118
|
-
writeStream.on('error', reject)
|
|
119
|
-
})
|
|
120
|
-
}
|
|
121
|
-
})
|
|
122
|
-
|
|
123
|
-
zipfile.on('end', () => {
|
|
124
|
-
resolve()
|
|
125
|
-
})
|
|
126
|
-
|
|
127
|
-
zipfile.on('error', reject)
|
|
128
|
-
})
|
|
129
|
-
})
|
|
63
|
+
// Format bytes to human readable format
|
|
64
|
+
function formatBytes(bytes) {
|
|
65
|
+
if (bytes === 0) return '0 B'
|
|
66
|
+
const k = 1024
|
|
67
|
+
const sizes = ['B', 'KB', 'MB', 'GB']
|
|
68
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
|
69
|
+
return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]
|
|
130
70
|
}
|
|
131
71
|
|
|
132
|
-
//
|
|
133
|
-
async function
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
cwd: destination,
|
|
137
|
-
filter: (path, entry) => {
|
|
138
|
-
// Filter out macOS hidden files (._files)
|
|
139
|
-
return !path.includes('/._') && !path.startsWith('._')
|
|
140
|
-
}
|
|
141
|
-
})
|
|
142
|
-
}
|
|
143
|
-
async function downloadSDK(custom_sdk_url) {
|
|
144
|
-
if (custom_sdk_url) {
|
|
145
|
-
downloadUrl = custom_sdk_url
|
|
72
|
+
// Download and extract function using axios + decompress
|
|
73
|
+
async function downloadAndExtract(url, destination) {
|
|
74
|
+
if (!fs.existsSync(destination)) {
|
|
75
|
+
fs.mkdirSync(destination, { recursive: true })
|
|
146
76
|
}
|
|
147
|
-
//
|
|
148
|
-
const
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
77
|
+
// Determine archive file name from URL
|
|
78
|
+
const urlPath = new URL(url).pathname
|
|
79
|
+
const fileName = path.basename(urlPath) || 'temp-archive'
|
|
80
|
+
const archivePath = path.join(destination, fileName)
|
|
81
|
+
log(` 📥 Starting download...`)
|
|
82
|
+
try {
|
|
83
|
+
// Download with progress tracking
|
|
84
|
+
const response = await axios({
|
|
85
|
+
method: 'GET',
|
|
86
|
+
url: url,
|
|
87
|
+
responseType: 'stream',
|
|
88
|
+
timeout: 300000, // 5 minutes timeout
|
|
89
|
+
onDownloadProgress: (progressEvent) => {
|
|
90
|
+
if (progressEvent.total) {
|
|
91
|
+
const progressBar = createProgressBar(progressEvent.total)
|
|
92
|
+
progressBar(progressEvent.loaded)
|
|
93
|
+
}
|
|
162
94
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
95
|
+
})
|
|
96
|
+
// Save the downloaded file
|
|
97
|
+
const writeStream = fs.createWriteStream(archivePath)
|
|
98
|
+
response.data.pipe(writeStream)
|
|
99
|
+
// Wait for download to complete
|
|
100
|
+
await new Promise((resolve, reject) => {
|
|
101
|
+
writeStream.on('finish', resolve)
|
|
102
|
+
writeStream.on('error', reject)
|
|
103
|
+
response.data.on('error', reject)
|
|
104
|
+
})
|
|
105
|
+
log(` 📦 Extracting archive...`)
|
|
106
|
+
// Extract using decompress (auto-detects format)
|
|
107
|
+
await decompress(archivePath, destination, {
|
|
108
|
+
filter: (file) => {
|
|
109
|
+
// Filter out macOS hidden files (._files) and __MACOSX folders
|
|
110
|
+
const filePath = file.path
|
|
111
|
+
return !filePath.includes('._') &&
|
|
112
|
+
!filePath.includes('__MACOSX') &&
|
|
113
|
+
!filePath.startsWith('._')
|
|
114
|
+
},
|
|
115
|
+
map: (file) => {
|
|
116
|
+
// Remove any leading directory if needed
|
|
117
|
+
file.path = file.path.replace(/^[^\/]+\//, '')
|
|
118
|
+
return file
|
|
169
119
|
}
|
|
170
120
|
})
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
121
|
+
log(` ✅ Extraction complete`)
|
|
122
|
+
// Clean up the temporary archive file
|
|
123
|
+
fs.unlinkSync(archivePath)
|
|
124
|
+
} catch (error) {
|
|
125
|
+
// Clean up on error
|
|
126
|
+
if (fs.existsSync(archivePath)) {
|
|
127
|
+
fs.unlinkSync(archivePath)
|
|
174
128
|
}
|
|
175
|
-
|
|
129
|
+
throw error
|
|
176
130
|
}
|
|
131
|
+
}
|
|
132
|
+
async function downloadSDK(customPackageUrl) {
|
|
133
|
+
// Use custom URL if provided, otherwise fetch from official server
|
|
134
|
+
let downloadUrl = customPackageUrl
|
|
177
135
|
if (!downloadUrl) {
|
|
178
|
-
|
|
179
|
-
|
|
136
|
+
// Fetch package list from official server
|
|
137
|
+
const res = await axios.get('https://admin.netease.im/public-service/free/publish/list')
|
|
138
|
+
const publishData = res.data.data[channel]
|
|
139
|
+
// Find package URL for specified version or latest
|
|
140
|
+
downloadUrl = findPackageUrl(publishData, version, platform, arch, product)
|
|
141
|
+
if (!downloadUrl) {
|
|
142
|
+
log(` ❌ ERROR: Package not found for ${platform} (${arch})`)
|
|
143
|
+
return
|
|
144
|
+
}
|
|
145
|
+
log(` 🚀 Preparing to download package for ${platform} (${arch})`)
|
|
180
146
|
}
|
|
181
|
-
console.info(`[node-nim] Downloading prebuilt SDK from ${downloadUrl} to ${savePath}`)
|
|
182
147
|
// remove temporary download folder and target folder
|
|
183
148
|
const target = path.join(__dirname, '..', 'build', 'Release')
|
|
184
149
|
if (fs.existsSync(savePath)) {
|
|
@@ -190,7 +155,6 @@ async function downloadSDK(custom_sdk_url) {
|
|
|
190
155
|
// download sdk
|
|
191
156
|
try {
|
|
192
157
|
await downloadAndExtract(downloadUrl, savePath)
|
|
193
|
-
|
|
194
158
|
// create build/Release folder
|
|
195
159
|
if (!fs.existsSync(target)) {
|
|
196
160
|
fs.mkdirSync(target, { recursive: true })
|
|
@@ -199,20 +163,217 @@ async function downloadSDK(custom_sdk_url) {
|
|
|
199
163
|
const from = path.join(savePath, platform === 'win32' ? 'bin' : 'lib')
|
|
200
164
|
const files = fs.readdirSync(from)
|
|
201
165
|
files.forEach((file) => {
|
|
202
|
-
|
|
166
|
+
log(` 📁 Installing ${file}`)
|
|
203
167
|
fs.renameSync(path.join(from, file), path.join(target, file))
|
|
204
168
|
})
|
|
205
169
|
// remove temporary download folder
|
|
206
170
|
fs.rmSync(savePath, { recursive: true })
|
|
207
|
-
|
|
171
|
+
log(` ✅ Package installation complete!`)
|
|
208
172
|
} catch (err) {
|
|
209
|
-
|
|
173
|
+
log(` ❌ ERROR: ${err.message}`)
|
|
174
|
+
throw err
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
// Helper function to find package download URL from publish data
|
|
178
|
+
function findPackageUrl(publishData, targetVersion, platform, arch, product) {
|
|
179
|
+
let latestVersion = '0.0.0'
|
|
180
|
+
let latestDownloadUrl = ''
|
|
181
|
+
let targetDownloadUrl = ''
|
|
182
|
+
// Check if package matches current platform/arch
|
|
183
|
+
const isMatchingPackage = (member) => {
|
|
184
|
+
return member.filename.includes(product) &&
|
|
185
|
+
member.filename.includes(platform) &&
|
|
186
|
+
member.filename.includes(arch)
|
|
187
|
+
}
|
|
188
|
+
// Iterate through all versions
|
|
189
|
+
Object.keys(publishData).forEach((versionKey) => {
|
|
190
|
+
const versionSDKs = publishData[versionKey]
|
|
191
|
+
// Track latest version
|
|
192
|
+
if (compareVersions.compare(latestVersion, versionKey, '<')) {
|
|
193
|
+
const latestPackage = versionSDKs.find(isMatchingPackage)
|
|
194
|
+
if (latestPackage) {
|
|
195
|
+
latestVersion = versionKey
|
|
196
|
+
latestDownloadUrl = latestPackage.cdnlink
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
// Find target version
|
|
200
|
+
if (targetVersion === versionKey) {
|
|
201
|
+
const targetPackage = versionSDKs.find(isMatchingPackage)
|
|
202
|
+
if (targetPackage) {
|
|
203
|
+
targetDownloadUrl = targetPackage.cdnlink
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
})
|
|
207
|
+
// Use target version if found, otherwise fallback to latest
|
|
208
|
+
if (targetDownloadUrl) {
|
|
209
|
+
return targetDownloadUrl
|
|
210
|
+
}
|
|
211
|
+
if (latestDownloadUrl) {
|
|
212
|
+
log(` ⚠️ Version ${targetVersion} not found, using latest version ${latestVersion}`)
|
|
213
|
+
return latestDownloadUrl
|
|
214
|
+
}
|
|
215
|
+
return null
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Parse directory listing from HTTP server
|
|
219
|
+
async function parseDirectoryListing(url) {
|
|
220
|
+
try {
|
|
221
|
+
const response = await axios.get(url)
|
|
222
|
+
const html = response.data
|
|
223
|
+
// Extract directory/file names from href attributes
|
|
224
|
+
// Matches patterns like: href="dirname/" or href="filename.tar.gz"
|
|
225
|
+
const hrefRegex = /href="([^"]+)"/g
|
|
226
|
+
const items = []
|
|
227
|
+
let match
|
|
228
|
+
while ((match = hrefRegex.exec(html)) !== null) {
|
|
229
|
+
const item = match[1]
|
|
230
|
+
// Skip parent directory and absolute URLs
|
|
231
|
+
if (item !== '../' && !item.startsWith('http') && !item.startsWith('/')) {
|
|
232
|
+
items.push(item)
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return items
|
|
236
|
+
} catch (error) {
|
|
237
|
+
throw new Error(`Failed to parse directory listing from ${url}: ${error.message}`)
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Find latest build number from directory listing
|
|
242
|
+
async function findLatestBuild(baseUrl, branch) {
|
|
243
|
+
const branchUrl = `${baseUrl}/${branch}/`
|
|
244
|
+
log(` 🔍 Searching for latest build in ${branchUrl}`)
|
|
245
|
+
const items = await parseDirectoryListing(branchUrl)
|
|
246
|
+
// Filter directories (end with /) and extract build numbers
|
|
247
|
+
const buildNumbers = items
|
|
248
|
+
.filter(item => item.endsWith('/'))
|
|
249
|
+
.map(item => parseInt(item.replace('/', '')))
|
|
250
|
+
.filter(num => !isNaN(num))
|
|
251
|
+
.sort((a, b) => b - a) // Sort in descending order
|
|
252
|
+
|
|
253
|
+
if (buildNumbers.length === 0) {
|
|
254
|
+
throw new Error(`No build directories found in ${branchUrl}`)
|
|
210
255
|
}
|
|
256
|
+
// Return all build numbers sorted by latest first
|
|
257
|
+
return buildNumbers
|
|
211
258
|
}
|
|
259
|
+
|
|
260
|
+
// Find latest build that contains the requested platform package
|
|
261
|
+
async function findLatestBuildWithPackage(baseUrl, branch, nodePlatform, nodeArch) {
|
|
262
|
+
const buildNumbers = await findLatestBuild(baseUrl, branch)
|
|
263
|
+
|
|
264
|
+
// Try builds from latest to oldest
|
|
265
|
+
for (const buildNumber of buildNumbers) {
|
|
266
|
+
try {
|
|
267
|
+
const buildUrl = `${baseUrl}/${branch}/${buildNumber}/`
|
|
268
|
+
log(` 🔍 Checking build ${buildNumber} for ${nodePlatform}-${nodeArch} package...`)
|
|
269
|
+
|
|
270
|
+
// Try to find package in this build
|
|
271
|
+
const packageUrl = await findPackage(buildUrl, nodePlatform, nodeArch)
|
|
272
|
+
log(` ✅ Found package in build ${buildNumber}`)
|
|
273
|
+
return { buildNumber, packageUrl }
|
|
274
|
+
} catch (error) {
|
|
275
|
+
log(` ⚠️ Build ${buildNumber} does not contain ${nodePlatform}-${nodeArch} package, trying previous build...`)
|
|
276
|
+
// Continue to next build
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
throw new Error(`No build found with ${nodePlatform}-${nodeArch} package for branch ${branch}`)
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Map Node.js platform/arch to SDK directory format
|
|
284
|
+
function getPlatformArchDir(nodePlatform, nodeArch) {
|
|
285
|
+
// darwin-arm64, darwin-x64, win32-ia32, linux-arm64, linux-x64
|
|
286
|
+
let platform = nodePlatform
|
|
287
|
+
let arch = nodeArch
|
|
288
|
+
// Map Node.js arch to package arch format
|
|
289
|
+
if (nodePlatform === 'darwin') {
|
|
290
|
+
arch = nodeArch === 'arm64' ? 'arm64' : 'x64'
|
|
291
|
+
} else if (nodePlatform === 'win32') {
|
|
292
|
+
platform = 'win32'
|
|
293
|
+
arch = nodeArch === 'ia32' ? 'ia32' : nodeArch === 'x64' ? 'x64' : nodeArch
|
|
294
|
+
} else if (nodePlatform === 'linux') {
|
|
295
|
+
arch = nodeArch === 'arm64' ? 'arm64' : 'x64'
|
|
296
|
+
}
|
|
297
|
+
return `${platform}-${arch}/`
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Find package from directory listing
|
|
301
|
+
async function findPackage(buildUrl, nodePlatform, nodeArch) {
|
|
302
|
+
const platformArchDir = getPlatformArchDir(nodePlatform, nodeArch)
|
|
303
|
+
const fullUrl = `${buildUrl}${platformArchDir}`
|
|
304
|
+
log(` 🔍 Searching for package in ${fullUrl}`)
|
|
305
|
+
const items = await parseDirectoryListing(fullUrl)
|
|
306
|
+
// Filter tar.gz files and exclude symbol files
|
|
307
|
+
const packageFiles = items.filter(item => {
|
|
308
|
+
if (!item.endsWith('.tar.gz') && !item.endsWith('.zip')) {
|
|
309
|
+
return false
|
|
310
|
+
}
|
|
311
|
+
// Exclude symbol files based on platform
|
|
312
|
+
if (nodePlatform === 'darwin' && item.includes('-dSYM')) {
|
|
313
|
+
return false
|
|
314
|
+
}
|
|
315
|
+
if (nodePlatform === 'win32' && item.includes('-PDB')) {
|
|
316
|
+
return false
|
|
317
|
+
}
|
|
318
|
+
if (nodePlatform === 'linux' && item.includes('-with-symbol')) {
|
|
319
|
+
return false
|
|
320
|
+
}
|
|
321
|
+
// Must start with nim- prefix
|
|
322
|
+
return item.startsWith('nim-')
|
|
323
|
+
})
|
|
324
|
+
if (packageFiles.length === 0) {
|
|
325
|
+
throw new Error(`No package found in ${fullUrl}`)
|
|
326
|
+
}
|
|
327
|
+
// If multiple files, prefer the first one (they should be the same package, just different compression)
|
|
328
|
+
const packageFile = packageFiles[0]
|
|
329
|
+
log(` ✅ Found package: ${packageFile}`)
|
|
330
|
+
return `${fullUrl}${packageFile}`
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Build package URL from branch name
|
|
334
|
+
async function buildPackageUrlFromBranch(branch, nodePlatform, nodeArch) {
|
|
335
|
+
// Base64 encoded internal server URL (decode when needed)
|
|
336
|
+
const encodedBaseUrl = 'aHR0cDovLzEwLjIxOS4yNS4xMjc6ODgvSU0tTmF0aXZlL0Rlc2t0b3A='
|
|
337
|
+
const baseUrl = Buffer.from(encodedBaseUrl, 'base64').toString('utf-8')
|
|
338
|
+
log(` 🌿 Resolving package URL for branch: ${branch}`)
|
|
339
|
+
|
|
340
|
+
// Find latest build that contains the requested platform package
|
|
341
|
+
// This will automatically fallback to previous builds if the latest doesn't have the package
|
|
342
|
+
const { buildNumber, packageUrl } = await findLatestBuildWithPackage(baseUrl, branch, nodePlatform, nodeArch)
|
|
343
|
+
|
|
344
|
+
return packageUrl
|
|
345
|
+
}
|
|
346
|
+
|
|
212
347
|
if (require.main === module) {
|
|
213
348
|
const args = process.argv
|
|
214
|
-
const
|
|
215
|
-
const
|
|
216
|
-
|
|
349
|
+
const urlIndex = args.indexOf('--nimSdkUrl')
|
|
350
|
+
const branchIndex = args.indexOf('--branch')
|
|
351
|
+
;(async () => {
|
|
352
|
+
try {
|
|
353
|
+
let url
|
|
354
|
+
let downloadUrl = process.env.npm_config_nimsdkurl
|
|
355
|
+
let branch = process.env.npm_config_branch
|
|
356
|
+
|
|
357
|
+
// 优先使用环境变量,然后才是命令行参数
|
|
358
|
+
if (branch || (branchIndex !== -1 && args[branchIndex + 1])) {
|
|
359
|
+
// Build URL from branch name
|
|
360
|
+
branch = branch || args[branchIndex + 1]
|
|
361
|
+
url = await buildPackageUrlFromBranch(branch, platform, process.arch)
|
|
362
|
+
} else if (downloadUrl || (urlIndex !== -1 && args[urlIndex + 1])) {
|
|
363
|
+
// Use provided URL directly
|
|
364
|
+
url = downloadUrl || args[urlIndex + 1]
|
|
365
|
+
// If URL is a directory path (ends with /), find the package
|
|
366
|
+
if (url.endsWith('/')) {
|
|
367
|
+
url = await findPackage(url, platform, arch)
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
// Pass undefined to downloadSDK when no custom URL is provided
|
|
371
|
+
// This allows it to fetch the latest package from admin.netease.im
|
|
372
|
+
await downloadSDK(url)
|
|
373
|
+
} catch (error) {
|
|
374
|
+
log(` ❌ ERROR: ${error.message}`)
|
|
375
|
+
process.exit(1)
|
|
376
|
+
}
|
|
377
|
+
})()
|
|
217
378
|
}
|
|
218
379
|
exports.downloadSDK = downloadSDK
|