gspace 0.1.2 → 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/lib/bootstrap.js CHANGED
@@ -9,7 +9,7 @@ import { Readable } from 'stream';
9
9
  import { requestHTTPS, cancelBody } from './https.js';
10
10
  import { extractBundle, validateFiles, verifyBundle } from './bundle.js';
11
11
 
12
- const VERSION = '0.1.2';
12
+ const VERSION = '0.2.0';
13
13
  const DAY = 24 * 60 * 60 * 1000;
14
14
  const MAX_ARTIFACT = 256 * 1024 * 1024;
15
15
  const nonce = () => randomBytes(12).toString('hex');
@@ -161,6 +161,11 @@ function artifact(value, source) {
161
161
  const url = new URL(value.url, source);
162
162
  if (source.protocol === 'https:' && url.protocol !== 'https:') throw new Error('Remote manifests cannot reference local or insecure artifacts.');
163
163
  if (!['https:', 'file:'].includes(url.protocol)) throw new Error('Unsupported artifact URL.');
164
+ if (value.mirrors !== undefined && (!Array.isArray(value.mirrors) || value.mirrors.length > 3)) throw new Error('Invalid artifact mirrors.');
165
+ for (const mirror of value.mirrors || []) {
166
+ const parsed = new URL(mirror);
167
+ if (parsed.protocol !== 'https:' || parsed.username || parsed.password) throw new Error('Mirrors require HTTPS without credentials.');
168
+ }
164
169
  return { ...value, url: url.href };
165
170
  }
166
171
 
@@ -222,6 +227,15 @@ async function downloadRanges(value, handle, first, request) {
222
227
  }
223
228
 
224
229
  export async function downloadVerified(value, destination, transport = requestHTTPS) {
230
+ let failure;
231
+ for (const url of [value.url, ...(value.mirrors || [])]) {
232
+ try { return await downloadFromSource({ ...value, url }, destination, transport); }
233
+ catch (error) { failure = error; }
234
+ }
235
+ throw failure;
236
+ }
237
+
238
+ async function downloadFromSource(value, destination, transport) {
225
239
  if (!Number.isSafeInteger(value.size) || value.size <= 0 || value.size > MAX_ARTIFACT || !/^[a-f0-9]{64}$/.test(value.sha256)) throw new Error('Invalid release artifact metadata.');
226
240
  const url = new URL(value.url);
227
241
  const temporary = `${destination}.download-${nonce()}`;
@@ -367,7 +381,8 @@ export async function main(args, env = process.env) {
367
381
  releaseLock = await lock(context.root);
368
382
  runtime = await currentRuntime(context.root);
369
383
  const config = JSON.parse(await fs.readFile(new URL('../release.json', import.meta.url), 'utf8'));
370
- const location = env.GSPACE_RELEASE_MANIFEST || config.manifest;
384
+ const locations = env.GSPACE_RELEASE_MANIFEST ? [env.GSPACE_RELEASE_MANIFEST] : [config.manifest, ...(config.fallbacks || [])].filter(Boolean);
385
+ const location = locations[0];
371
386
  const forceUpdate = args[0] === '--update';
372
387
  if (forceUpdate) args = args.slice(1);
373
388
  const cacheFile = path.join(context.root, 'runtime', 'update.json');
@@ -377,9 +392,16 @@ export async function main(args, env = process.env) {
377
392
  if (!location && !runtime) throw new Error('No release source configured. Set GSPACE_RELEASE_MANIFEST to a local manifest.json or HTTPS URL, or use GSPACE_CORE for a development build.');
378
393
  if (location) {
379
394
  try {
380
- const source = asURL(location);
381
- const raw = await fetchBytes(source, 1024 * 1024);
382
- const manifest = validateManifest(JSON.parse(raw.toString('utf8')), source);
395
+ let source, raw, manifest, sourceError;
396
+ for (const candidate of locations) {
397
+ try {
398
+ source = asURL(candidate);
399
+ raw = await fetchBytes(source, 1024 * 1024);
400
+ manifest = validateManifest(JSON.parse(raw.toString('utf8')), source);
401
+ break;
402
+ } catch (error) { sourceError = error; }
403
+ }
404
+ if (!manifest) throw sourceError;
383
405
  const hash = createHash('sha256').update(raw).digest('hex');
384
406
  if (!runtime || runtime.manifest_hash !== hash) {
385
407
  if (!runtime) console.error(`Gspace is not installed on this workstation.\nUsing ${context.goinfre}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gspace",
3
- "version": "0.1.2",
3
+ "version": "0.2.0",
4
4
  "description": "Gspace launcher for 1337/42 Linux workstations",
5
5
  "repository": {
6
6
  "type": "git",
package/release.json CHANGED
@@ -1 +1 @@
1
- {"manifest": "https://github.com/Seraph919/gspace-releases/releases/latest/download/manifest.json"}
1
+ {"manifest":"https://gspace-downloads.pages.dev/manifest.json","fallbacks":["https://github.com/Seraph919/gspace-releases/releases/latest/download/manifest.json"]}