node-sword-interface 1.0.45 → 1.0.47

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/binding.gyp CHANGED
@@ -44,7 +44,7 @@
44
44
  'message': 'Downloading sword-build-win32 artifacts from GitHub ...',
45
45
  'inputs': [],
46
46
  'outputs': ['sword-build-win32'],
47
- 'action': ['call', 'PowerShell.exe', '-ExecutionPolicy', 'Bypass', '-File', '<(module_root_dir)/scripts/get_sword_build_win32.ps1'],
47
+ 'action': ['node', '<(module_root_dir)/scripts/get_sword_build_win32.js'],
48
48
  }
49
49
  ]
50
50
  }]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-sword-interface",
3
- "version": "1.0.45",
3
+ "version": "1.0.47",
4
4
  "description": "Javascript (N-API) interface to SWORD library",
5
5
  "keywords": [
6
6
  "C++",
@@ -0,0 +1,160 @@
1
+ #!/usr/bin/env node
2
+ // This file is part of node-sword-interface.
3
+ // This script downloads a release asset (zip) from GitHub, extracts it and
4
+ // renames the directory to sword-build-win32 inside the current working directory.
5
+ //
6
+ // License: GPL-2.0+
7
+
8
+ const https = require('https');
9
+ const fs = require('fs');
10
+ const path = require('path');
11
+ const { execFileSync } = require('child_process');
12
+
13
+ const RELEASE_TAG = 'v1.8.900-2022-11-06';
14
+ const API_URL = `https://api.github.com/repos/ezra-bible-app/sword-build-win32/releases/tags/${RELEASE_TAG}`;
15
+
16
+ function fetchJson(url, headers = {}) {
17
+ return new Promise((resolve, reject) => {
18
+ const req = https.get(url, {
19
+ headers: {
20
+ 'User-Agent': 'node-sword-interface',
21
+ 'Accept': 'application/vnd.github+json',
22
+ ...headers,
23
+ }
24
+ }, res => {
25
+ let data = '';
26
+ res.on('data', chunk => data += chunk);
27
+ res.on('end', () => {
28
+ if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
29
+ try {
30
+ resolve(JSON.parse(data));
31
+ } catch (e) {
32
+ reject(e);
33
+ }
34
+ } else {
35
+ reject(new Error(`Request failed: ${res.statusCode} ${data}`));
36
+ }
37
+ });
38
+ });
39
+ req.on('error', reject);
40
+ });
41
+ }
42
+
43
+ function downloadFile(url, outPath, headers = {}, maxRedirects = 5) {
44
+ return new Promise((resolve, reject) => {
45
+ let redirects = 0;
46
+
47
+ const doRequest = (reqUrl) => {
48
+ const file = fs.createWriteStream(outPath);
49
+ const req = https.get(reqUrl, {
50
+ headers: {
51
+ 'User-Agent': 'node-sword-interface',
52
+ 'Accept': 'application/octet-stream',
53
+ ...headers,
54
+ }
55
+ }, res => {
56
+ // Handle redirects
57
+ if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
58
+ file.close(); // ensure stream closed before re-request
59
+ fs.unlink(outPath, () => {
60
+ if (redirects >= maxRedirects) {
61
+ reject(new Error(`Too many redirects downloading: ${reqUrl}`));
62
+ return;
63
+ }
64
+ redirects++;
65
+ const nextUrl = res.headers.location.startsWith('http')
66
+ ? res.headers.location
67
+ : new URL(res.headers.location, reqUrl).toString();
68
+ doRequest(nextUrl);
69
+ });
70
+ return;
71
+ }
72
+
73
+ if (res.statusCode && (res.statusCode < 200 || res.statusCode >= 300)) {
74
+ file.close();
75
+ fs.unlink(outPath, () => reject(new Error(`Download failed: ${res.statusCode}`)));
76
+ return;
77
+ }
78
+
79
+ res.pipe(file);
80
+ file.on('finish', () => file.close(resolve));
81
+ });
82
+
83
+ req.on('error', err => {
84
+ file.close();
85
+ fs.unlink(outPath, () => reject(err));
86
+ });
87
+ };
88
+
89
+ doRequest(url);
90
+ });
91
+ }
92
+
93
+ async function main() {
94
+ try {
95
+ const isCI = process.env.CI === 'true';
96
+ const githubToken = process.env.GITHUB_TOKEN;
97
+ const authHeader = (isCI && githubToken) ? { Authorization: `Bearer ${githubToken}` } : {};
98
+
99
+ if (isCI) {
100
+ console.log('GitHub actions build ... using GITHUB_TOKEN for authentication!');
101
+ }
102
+
103
+ const release = await fetchJson(API_URL, authHeader);
104
+ if (!release.assets || !release.assets.length) {
105
+ throw new Error('No assets found in release.');
106
+ }
107
+
108
+ const asset = release.assets[0];
109
+ const zipName = asset.name;
110
+ const zipUrl = asset.browser_download_url;
111
+
112
+ const cwd = process.cwd();
113
+
114
+ // Remove existing folder if present
115
+ const existingDir = path.join(cwd, 'sword-build-win32');
116
+ if (fs.existsSync(existingDir)) {
117
+ fs.rmSync(existingDir, { recursive: true, force: true });
118
+ }
119
+
120
+ const zipPath = path.join(cwd, zipName);
121
+ console.log(`Downloading ${zipUrl} -> ${zipPath}`);
122
+ await downloadFile(zipUrl, zipPath, authHeader);
123
+
124
+ // Extract ZIP file
125
+ const extractedDirName = zipName.replace(/\.zip$/i, '');
126
+ try {
127
+ if (process.platform === 'win32') {
128
+ execFileSync('powershell', ['-NoProfile', '-Command', `Expand-Archive -Path '${zipPath}' -DestinationPath '.' -Force`], { stdio: 'inherit' });
129
+ } else {
130
+ // Prefer system unzip if available
131
+ try {
132
+ execFileSync('unzip', ['-o', zipPath], { stdio: 'inherit' });
133
+ } catch (unzipErr) {
134
+ // Fallback to 7z if unzip is not present
135
+ execFileSync('7z', ['x', zipPath], { stdio: 'inherit' });
136
+ }
137
+ }
138
+ } catch (e) {
139
+ throw new Error(`Extraction failed: ${e.message}`);
140
+ }
141
+
142
+ // Remove zip file
143
+ fs.unlinkSync(zipPath);
144
+
145
+ // Rename extracted folder
146
+ if (!fs.existsSync(path.join(cwd, extractedDirName))) {
147
+ throw new Error(`Expected extracted directory '${extractedDirName}' not found.`);
148
+ }
149
+ fs.renameSync(path.join(cwd, extractedDirName), existingDir);
150
+
151
+ console.log('Download of Windows library artifacts completed!');
152
+ } catch (err) {
153
+ console.error('Error:', err.message);
154
+ process.exit(1);
155
+ }
156
+ }
157
+
158
+ if (require.main === module) {
159
+ main();
160
+ }