@glassnote/client 2.4.0 → 2.4.3

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/INSTALACION.md CHANGED
File without changes
package/README.md CHANGED
File without changes
package/autoStart.js CHANGED
File without changes
package/autoupdate.js CHANGED
File without changes
@@ -5,6 +5,7 @@ const https = require('https');
5
5
  const crypto = require('crypto');
6
6
  const { exec } = require('child_process');
7
7
  const userData = require('./userData');
8
+ const { baselineRendererDir, rendererVersionFrom } = require('./rendererPaths');
8
9
 
9
10
  // El renderer se baja siempre del bucket de PRODUCCIÓN. Antes esto lo decidía un campo
10
11
  // `stage` en package.json que elegía entre este bucket y `glassnotedev`: ese ambiente ya
@@ -23,29 +24,21 @@ class AutoUpdateRenderer {
23
24
  }
24
25
 
25
26
  /**
26
- * Obtiene la versión actual del renderer desde package.json
27
+ * La versión del renderer que está EN USO, que es contra la que hay que comparar el
28
+ * canal. En uso es la que elige loadRendererHtml: la más alta entre lo bajado a
29
+ * userData y el baseline que viaja con el paquete.
30
+ *
31
+ * Antes esto miraba solo userData y, si no había, `<paquete>/renderer/package.json`
32
+ * —una carpeta que dejó de existir cuando el renderer pasó a ser dependencia npm—.
33
+ * Devolvía 0.0.0, así que en cada instalación nueva cualquier versión del canal le
34
+ * ganaba: bajaba el zip de S3 aunque fuera MÁS VIEJO que el baseline, y después el
35
+ * loader se quedaba con el baseline igual: 1.1 MB tirados y una versión "actual"
36
+ * que no era la que se estaba usando.
27
37
  */
28
38
  getCurrentRendererVersion() {
29
- try {
30
- // Primero verificar en userData (actualizaciones)
31
- const userDataRendererPath = path.join(userData.getUserDataDir(), 'renderer', 'package.json');
32
- if (fs.existsSync(userDataRendererPath)) {
33
- const packageJson = JSON.parse(fs.readFileSync(userDataRendererPath, 'utf8'));
34
- return packageJson.version;
35
- }
36
-
37
- // Si no existe en userData, verificar en la aplicación (versión por defecto)
38
- const appRendererPath = path.join(__dirname, 'renderer', 'package.json');
39
- if (fs.existsSync(appRendererPath)) {
40
- const packageJson = JSON.parse(fs.readFileSync(appRendererPath, 'utf8'));
41
- return packageJson.version;
42
- }
43
-
44
- return '0.0.0'; // Versión por defecto si no existe
45
- } catch (error) {
46
- console.error('Error reading renderer version:', error);
47
- return '0.0.0';
48
- }
39
+ const enUserData = rendererVersionFrom(path.join(userData.getUserDataDir(), 'renderer'));
40
+ const enBaseline = rendererVersionFrom(baselineRendererDir());
41
+ return this.compareVersions(enUserData, enBaseline) >= 0 ? enUserData : enBaseline;
49
42
  }
50
43
 
51
44
  /**
package/bin/glassnote.js CHANGED
@@ -29,9 +29,58 @@ const MARK_B = '# <<< glassnote <<<';
29
29
  function npmEnv() {
30
30
  return Object.assign({}, process.env, {
31
31
  PATH: `${mode.BIN_DIR}${path.delimiter}${process.env.PATH || ''}`,
32
+ // Cache propia, bajo ~/.glassnote. Un ~/.npm con archivos de root —herencia de
33
+ // cualquier `sudo npm` viejo— hace que npm muera con EACCES antes de bajar nada,
34
+ // y el usuario tiene que andar con chown. Se pisa a propósito el npm_config_cache
35
+ // heredado: cuando esto corre bajo npx, npm nos pasa el suyo en el entorno, que es
36
+ // justo el que puede estar roto. Para forzar otra: GLASSNOTE_NPM_CACHE.
37
+ npm_config_cache:
38
+ process.env.GLASSNOTE_NPM_CACHE || path.join(mode.HOME_DIR, 'npm-cache'),
32
39
  });
33
40
  }
34
41
 
42
+ // userData tira de `uuid`, y este CLI corre también donde el árbol de dependencias no
43
+ // está (un tarball recién desempacado, un install a medias). Por eso se carga tarde y
44
+ // tolerando el fallo: si no se puede dejar constancia, el peor caso es que la app
45
+ // reintente en el chequeo siguiente. Romper el CLI —que es quien instala— sería peor.
46
+ function conUserData(hacer) {
47
+ try {
48
+ return hacer(require('../userData'));
49
+ } catch (error) {
50
+ return undefined;
51
+ }
52
+ }
53
+
54
+ // La app mira esto antes de reintentar: sin memoria del fallo, un npm que falla siempre
55
+ // deja al cliente cerrándose y abriéndose cada veinte segundos (falla, se relanza,
56
+ // chequea a los 5 segundos, vuelve a fallar).
57
+ function recordarFalloDeUpdate(version, motivo) {
58
+ conUserData((userData) => userData.set('failedUpdate', { version, at: Date.now(), motivo }));
59
+ }
60
+
61
+ function olvidarFalloDeUpdate() {
62
+ conUserData((userData) => {
63
+ if (userData.get('failedUpdate')) userData.remove('failedUpdate');
64
+ });
65
+ }
66
+
67
+ // ¿Hay un cliente vivo? Levanta su servidor local en 48984 (localserver.js), así que su
68
+ // /uuid es la señal más barata. Node no tiene HTTP síncrono y el CLI sí lo es, así que la
69
+ // consulta va en un proceso aparte.
70
+ function clienteEnMarcha() {
71
+ const sonda =
72
+ "const http=require('http');" +
73
+ "const r=http.get('http://127.0.0.1:48984/uuid',(res)=>{res.resume();process.exit(res.statusCode===200?0:1)});" +
74
+ "r.on('error',()=>process.exit(1));" +
75
+ "r.setTimeout(700,()=>{r.destroy();process.exit(1)});";
76
+ const res = spawnSync(process.execPath, ['-e', sonda], {
77
+ env: Object.assign({}, process.env, { ELECTRON_RUN_AS_NODE: '1' }),
78
+ stdio: 'ignore',
79
+ timeout: 4000,
80
+ });
81
+ return res.status === 0;
82
+ }
83
+
35
84
  // Sueño síncrono sin dependencias: el ayudante de actualización tiene que esperar a que
36
85
  // la app termine de cerrarse antes de que npm le reemplace los archivos.
37
86
  function sleepSync(ms) {
@@ -242,6 +291,108 @@ function toSpec(arg) {
242
291
  return looksLikeSpec ? arg : `${mode.PKG_NAME}@${arg}`;
243
292
  }
244
293
 
294
+ const MAC_APP_NAME = 'Glassnote.app';
295
+
296
+ // Instalado por npm queda un comando, no una app: cerrada desde la bandeja no hay forma
297
+ // de volver a abrirla desde el Finder, Spotlight o Launchpad. Esto crea un .app minimo
298
+ // —un envoltorio que llama al CLI instalado— para tener de donde relanzarla.
299
+ function macAppDirs() {
300
+ return [
301
+ path.join('/Applications', MAC_APP_NAME),
302
+ path.join(os.homedir(), 'Applications', MAC_APP_NAME),
303
+ ];
304
+ }
305
+
306
+ function instalarAppMac() {
307
+ if (process.platform !== 'darwin') return null;
308
+
309
+ // /Applications pide ser admin; si no se puede escribir, la de usuario sirve igual y
310
+ // Spotlight la indexa lo mismo. Nada de sudo: la instalacion entera va sin root.
311
+ const [global, local] = macAppDirs();
312
+ let appDir = local;
313
+ try {
314
+ fs.accessSync('/Applications', fs.constants.W_OK);
315
+ appDir = global;
316
+ } catch (error) {
317
+ /* sin permiso: se usa ~/Applications */
318
+ }
319
+
320
+ const contents = path.join(appDir, 'Contents');
321
+ const cli = path.join(mode.BIN_DIR, mode.BIN_NAME);
322
+ try {
323
+ fs.mkdirSync(path.join(contents, 'MacOS'), { recursive: true });
324
+ fs.mkdirSync(path.join(contents, 'Resources'), { recursive: true });
325
+
326
+ // El ejecutable del bundle solo delega en el CLI, que ya sabe encontrar electron y
327
+ // arrancar desprendido. Asi el .app no se queda viejo cuando el cliente se actualiza.
328
+ const exe = path.join(contents, 'MacOS', 'Glassnote');
329
+ fs.writeFileSync(
330
+ exe,
331
+ [
332
+ '#!/bin/sh',
333
+ "# Generado por 'glassnote install': se reescribe en cada instalacion.",
334
+ `exec "${cli}" start`,
335
+ '',
336
+ ].join('\n')
337
+ );
338
+ fs.chmodSync(exe, 0o755);
339
+
340
+ // El icono del bundle tiene que ser .icns; sips viene con macOS. Si falla, el .app
341
+ // queda con el icono generico: molesta, pero no impide relanzar.
342
+ const png = path.join(mode.INSTALLED_PACKAGE_DIR, 'images', 'icon-512.png');
343
+ if (fs.existsSync(png)) {
344
+ spawnSync(
345
+ 'sips',
346
+ ['-s', 'format', 'icns', png, '--out', path.join(contents, 'Resources', 'glassnote.icns')],
347
+ { stdio: 'ignore' }
348
+ );
349
+ }
350
+
351
+ // LSUIElement, igual que la app: el envoltorio tampoco tiene que dejar icono en el Dock.
352
+ const version = installedVersion() || '0.0.0';
353
+ fs.writeFileSync(
354
+ path.join(contents, 'Info.plist'),
355
+ `<?xml version="1.0" encoding="UTF-8"?>
356
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
357
+ <plist version="1.0">
358
+ <dict>
359
+ <key>CFBundleName</key><string>Glassnote</string>
360
+ <key>CFBundleDisplayName</key><string>Glassnote</string>
361
+ <key>CFBundleIdentifier</key><string>com.glassnote.launcher</string>
362
+ <key>CFBundleExecutable</key><string>Glassnote</string>
363
+ <key>CFBundleIconFile</key><string>glassnote</string>
364
+ <key>CFBundlePackageType</key><string>APPL</string>
365
+ <key>CFBundleShortVersionString</key><string>${version}</string>
366
+ <key>CFBundleVersion</key><string>${version}</string>
367
+ <key>LSUIElement</key><true/>
368
+ </dict>
369
+ </plist>
370
+ `
371
+ );
372
+
373
+ // El Finder cachea el bundle por fecha: sin esto puede seguir mostrando el icono viejo.
374
+ spawnSync('touch', [appDir], { stdio: 'ignore' });
375
+ return appDir;
376
+ } catch (error) {
377
+ // Que no se pueda crear la app no rompe la instalacion: el comando ya funciona.
378
+ log(` (no pude crear ${MAC_APP_NAME}: ${error.message})`);
379
+ return null;
380
+ }
381
+ }
382
+
383
+ function borrarAppMac() {
384
+ if (process.platform !== 'darwin') return;
385
+ for (const dir of macAppDirs()) {
386
+ if (!fs.existsSync(dir)) continue;
387
+ try {
388
+ step(`borrando ${dir}`);
389
+ fs.rmSync(dir, { recursive: true, force: true });
390
+ } catch (error) {
391
+ log(` (no pude borrar ${dir}: ${error.message})`);
392
+ }
393
+ }
394
+ }
395
+
245
396
  function cmdInstall(args) {
246
397
  const version = args.find((a) => !a.startsWith('-')) || 'latest';
247
398
  const noPath = args.includes('--no-path');
@@ -281,6 +432,9 @@ function cmdInstall(args) {
281
432
  }
282
433
  }
283
434
 
435
+ const appMac = instalarAppMac();
436
+ if (appMac) step(`app creada en ${appMac} (para relanzarlo desde el Finder)`);
437
+
284
438
  let pathInfo = { touched: [], line: '' };
285
439
  if (!noPath) {
286
440
  pathInfo = IS_WIN ? addToPathWindows() : addToPathUnix();
@@ -346,6 +500,7 @@ function cmdUpdate(args) {
346
500
  env: npmEnv(),
347
501
  });
348
502
  if (res.status !== 0) {
503
+ recordarFalloDeUpdate(version, `npm salió con ${res.status}`);
349
504
  if (relaunch) {
350
505
  // Falló la actualización: al menos el cliente vuelve. Quedarse sin app por
351
506
  // un npm que no pudo con la red es peor que seguir en la versión vieja.
@@ -355,6 +510,11 @@ function cmdUpdate(args) {
355
510
  fail('npm no pudo actualizar el paquete.');
356
511
  }
357
512
 
513
+ olvidarFalloDeUpdate();
514
+ // Se rehace en cada update: quien ya tenia el cliente instalado de antes no paso
515
+ // por el install, y el .app tiene que aparecerle igual (y con la version al dia).
516
+ const appMac = instalarAppMac();
517
+ if (appMac) step(`app actualizada en ${appMac}`);
358
518
  log(`Actualizado a v${installedVersion()}.`);
359
519
  if (relaunch) {
360
520
  launch({ detach: true });
@@ -366,6 +526,10 @@ function cmdUpdate(args) {
366
526
 
367
527
  function cmdUninstall() {
368
528
  log('glassnote — desinstalando');
529
+ // Se comprueba ANTES de borrar: después los archivos ya no están, pero el proceso que
530
+ // los tenía abiertos sigue vivo y con su ícono en la bandeja. Decir "Desinstalado" con
531
+ // la app todavía en pantalla es lo que confunde.
532
+ const seguiaAbierto = clienteEnMarcha();
369
533
  step('quitando el autoarranque');
370
534
  removeAutoStart();
371
535
 
@@ -378,11 +542,19 @@ function cmdUninstall() {
378
542
  step('el PATH de usuario ya no tenía la entrada');
379
543
  }
380
544
 
545
+ borrarAppMac();
546
+
381
547
  if (fs.existsSync(mode.HOME_DIR)) {
382
548
  step(`borrando ${mode.HOME_DIR}`);
383
549
  fs.rmSync(mode.HOME_DIR, { recursive: true, force: true });
384
550
  }
385
551
  log('');
552
+ if (seguiaAbierto) {
553
+ log('OJO: el cliente sigue abierto. Sus archivos ya no están, pero el proceso vive');
554
+ log('hasta que lo cierres desde el ícono de la bandeja (Salir). Después no vuelve a');
555
+ log('abrirse: el autoarranque ya no está.');
556
+ log('');
557
+ }
386
558
  log('Desinstalado. Tus datos y notas siguen donde estaban:');
387
559
  log(` ${userDataDir()}`);
388
560
  log('Borralos a mano si también querés perderlos.');
package/images/demo01.png CHANGED
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
package/images/icon.ico CHANGED
File without changes
package/images/icon.png CHANGED
File without changes
package/images/splash.svg CHANGED
File without changes
package/localserver.js CHANGED
File without changes
package/logUtilities.js CHANGED
File without changes
package/main.js CHANGED
@@ -4,6 +4,7 @@ const {
4
4
  Notification,
5
5
  Tray,
6
6
  Menu,
7
+ nativeImage,
7
8
  screen,
8
9
  } = require('electron');
9
10
  const packageJson = require('./package.json');
@@ -17,6 +18,7 @@ const AutoUpdate = require('./autoupdate');
17
18
  const NpmUpdate = require('./npmUpdate');
18
19
  const npmMode = require('./npmMode');
19
20
  const AutoUpdateRenderer = require('./autoupdate_renderer');
21
+ const { baselineRendererDir, rendererVersionFrom: getRendererVersionFromPath } = require('./rendererPaths');
20
22
  const { createTailUtility } = require('./logUtilities');
21
23
 
22
24
  const logRenderer = packageJson.logRenderer !== undefined ? packageJson.logRenderer : true;
@@ -120,6 +122,23 @@ cleanupOldLogs();
120
122
  createTailUtility(logsDir);
121
123
 
122
124
  app.whenReady().then(() => {
125
+ // macOS: esto es una app de bandeja, se maneja desde la barra de menu. Instalada por
126
+ // npm no hay .app propio, asi que el Dock mostraba el icono por defecto de Electron y
127
+ // una entrada que no sirve para nada. Se saca del Dock, pero antes se le pone el icono
128
+ // de glassnote: si algo lo vuelve a mostrar (un dialogo modal lo hace), que al menos
129
+ // no aparezca como "Electron".
130
+ if (process.platform === 'darwin' && app.dock) {
131
+ try {
132
+ const dockIcon = nativeImage.createFromPath(
133
+ path.join(__dirname, 'images', 'icon-512.png')
134
+ );
135
+ if (!dockIcon.isEmpty()) app.dock.setIcon(dockIcon);
136
+ } catch (error) {
137
+ console.warn('[dock] no se pudo poner el icono:', error.message);
138
+ }
139
+ app.dock.hide();
140
+ }
141
+
123
142
  // Configure auto-start - this will handle shortcut creation
124
143
  //
125
144
  // Corriendo por `npx` no se registra nada: la app vive en la caché de npx, que se
@@ -556,20 +575,7 @@ process.on('unhandledRejection', (reason, promise) => {
556
575
  *
557
576
  * Gana la version mas alta, sin importar de donde venga; empate, gana userData.
558
577
  */
559
- // De donde sale el baseline. En una instalacion es la dependencia @glassnote/renderer,
560
- // que npm resuelve sola. Pero si hay un clon del renderer al lado —el flujo de quien lo
561
- // desarrolla, con `npm run watch-renderer`— ese gana: si no, editarlo no se veria nunca,
562
- // tapado por la version publicada.
563
- function baselineRendererDir() {
564
- const clonLocal = path.join(__dirname, 'glassnote-renderer', 'dist');
565
- if (fs.existsSync(clonLocal)) return clonLocal;
566
- try {
567
- return path.join(path.dirname(require.resolve('@glassnote/renderer/package.json')), 'dist');
568
- } catch (error) {
569
- // Sin dependencia ni clon solo queda userData; si tampoco esta, mas abajo se avisa.
570
- return clonLocal;
571
- }
572
- }
578
+ // baselineRendererDir vive en rendererPaths.js: lo comparte con el updater del renderer.
573
579
 
574
580
  function loadRendererHtml(window, uuid) {
575
581
  // Definir las dos fuentes a considerar con archivos HTML alternativos
@@ -695,38 +701,7 @@ function compareVersions(version1, version2) {
695
701
  return 0; // Las versiones son iguales
696
702
  }
697
703
 
698
- /**
699
- * Obtiene la versión del renderer desde el package.json en el directorio especificado
700
- * Si el directorio es un dist/ sin package.json, se mira el del paquete que lo contiene.
701
- */
702
- function getRendererVersionFromPath(rendererPath) {
703
- try {
704
- // Primero intentar en el directorio especificado
705
- const packageJsonPath = path.join(rendererPath, 'package.json');
706
- if (fs.existsSync(packageJsonPath)) {
707
- const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
708
- return packageJson.version || '0.0.0';
709
- }
710
-
711
- // Un dist/ sin package.json propio: la version la tiene el paquete de arriba.
712
- if (path.basename(rendererPath) === 'dist') {
713
- const parentDir = path.join(rendererPath, '..'); // Subir un nivel
714
- const parentPackageJsonPath = path.join(parentDir, 'package.json');
715
- if (fs.existsSync(parentPackageJsonPath)) {
716
- const packageJson = JSON.parse(fs.readFileSync(parentPackageJsonPath, 'utf8'));
717
- return packageJson.version || '0.0.0';
718
- }
719
- }
720
- } catch (error) {
721
- console.error(
722
- 'Error reading renderer version from',
723
- rendererPath,
724
- ':',
725
- error
726
- );
727
- }
728
- return '0.0.0'; // Versión por defecto si no existe o hay error
729
- }
704
+ // getRendererVersionFromPath = rendererVersionFrom de rendererPaths.js (ver el require de arriba).
730
705
 
731
706
  /**
732
707
  * Recarga el main window después de una actualización del renderer
package/npmMode.js CHANGED
File without changes
package/npmUpdate.js CHANGED
@@ -8,6 +8,7 @@
8
8
  // desprendido, la app se cierra, el ayudante actualiza y la vuelve a abrir. Igual en las
9
9
  // tres plataformas, así no hay una rama por sistema operativo que solo se pruebe en uno.
10
10
  const https = require('https');
11
+ const fs = require('fs');
11
12
  const path = require('path');
12
13
  const { spawn } = require('child_process');
13
14
 
@@ -90,18 +91,38 @@ class NpmUpdate {
90
91
  const meta = await this.getJson(`${REGISTRY}/${npmMode.PKG_NAME}/${target}`);
91
92
  if (!meta || !meta.version) return;
92
93
 
93
- if (isLatest) {
94
- if (compareVersions(this.currentVersion, meta.version) < 0) {
95
- this.applyUpdate(meta.version);
96
- }
97
- } else if (this.currentVersion !== meta.version) {
98
- this.applyUpdate(meta.version);
99
- }
94
+ const objetivo = meta.version;
95
+ const hayQueActualizar = isLatest
96
+ ? compareVersions(this.currentVersion, objetivo) < 0
97
+ : this.currentVersion !== objetivo;
98
+ if (!hayQueActualizar) return;
99
+ if (this.fallidoHacePoco(objetivo)) return;
100
+ this.applyUpdate(objetivo);
100
101
  } catch (error) {
101
102
  console.error('[update] error comprobando actualizaciones:', error.message);
102
103
  }
103
104
  }
104
105
 
106
+ // Una actualización que falló NO se reintenta enseguida. cmdUpdate relanza la app a
107
+ // propósito cuando npm falla —quedarse sin cliente es peor que quedarse en la versión
108
+ // vieja—, pero el chequeo vuelve a los 5 segundos de arrancar. Con un fallo que
109
+ // persiste (sin red, el registry caído, una dependencia que no resuelve) eso es la app
110
+ // cerrándose y abriéndose cada veinte segundos, para siempre y sin decir por qué. Se
111
+ // recuerda la versión que falló y se espera al chequeo normal.
112
+ fallidoHacePoco(version) {
113
+ try {
114
+ const fallo = userData.get('failedUpdate');
115
+ if (!fallo || fallo.version !== version) return false;
116
+ const desde = Date.now() - (fallo.at || 0);
117
+ if (desde >= CHECK_INTERVAL_MS) return false;
118
+ const minutos = Math.max(1, Math.round((CHECK_INTERVAL_MS - desde) / 60000));
119
+ console.log(`[update] ${version} ya falló; no se reintenta hasta dentro de ~${minutos} min`);
120
+ return true;
121
+ } catch (error) {
122
+ return false;
123
+ }
124
+ }
125
+
105
126
  applyUpdate(version) {
106
127
  if (this.updating) return;
107
128
  this.updating = true;
@@ -117,11 +138,22 @@ class NpmUpdate {
117
138
  PATH: `${npmMode.BIN_DIR}${path.delimiter}${process.env.PATH || ''}`,
118
139
  });
119
140
 
141
+ // El ayudante corre desprendido y su salida no la veía nadie: cuando npm fallaba,
142
+ // el único síntoma era la app reiniciándose sola. Va a un archivo al lado de los
143
+ // datos de usuario, que es donde alguien lo va a buscar.
144
+ let salida = 'ignore';
145
+ try {
146
+ fs.mkdirSync(userData.getUserDataDir(), { recursive: true });
147
+ salida = fs.openSync(path.join(userData.getUserDataDir(), 'update.log'), 'a');
148
+ } catch (error) {
149
+ console.error('[update] no se pudo abrir update.log:', error.message);
150
+ }
151
+
120
152
  try {
121
153
  const child = spawn(
122
154
  process.execPath,
123
155
  [cli, 'update', version, '--relaunch', '--wait', '4000'],
124
- { detached: true, stdio: 'ignore', env }
156
+ { detached: true, stdio: ['ignore', salida, salida], env }
125
157
  );
126
158
  child.unref();
127
159
  } catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glassnote/client",
3
- "version": "2.4.0",
3
+ "version": "2.4.3",
4
4
  "description": "GlassNote — cliente de escritorio de notas superpuestas. Se instala con npx, sin instaladores nativos ni firmas.",
5
5
  "main": "main.js",
6
6
  "publishConfig": {
@@ -29,6 +29,7 @@
29
29
  "autoStart.js",
30
30
  "autoupdate.js",
31
31
  "autoupdate_renderer.js",
32
+ "rendererPaths.js",
32
33
  "npmMode.js",
33
34
  "npmUpdate.js",
34
35
  "localserver.js",
package/preload.js CHANGED
File without changes
@@ -0,0 +1,62 @@
1
+ // De dónde sale el renderer y qué versión tiene. En un solo sitio a propósito.
2
+ //
3
+ // Esto vivía duplicado en main.js y en autoupdate_renderer.js, y las dos copias se
4
+ // fueron separando: la del updater seguía mirando `<paquete>/renderer/`, una carpeta que
5
+ // dejó de existir cuando el renderer pasó a ser la dependencia @glassnote/renderer. Como
6
+ // no la encontraba devolvía 0.0.0, cualquier versión del canal le ganaba, y cada
7
+ // instalación nueva bajaba el zip de S3 —incluso más viejo que el baseline— para después
8
+ // no usarlo. Un bug silencioso: se ve como 1.1 MB de tráfico que nadie pidió.
9
+ 'use strict';
10
+
11
+ const fs = require('fs');
12
+ const path = require('path');
13
+
14
+ // El baseline es la dependencia @glassnote/renderer, que npm resuelve sola. Pero si hay
15
+ // un clon del renderer al lado —el flujo de quien lo desarrolla, con `npm run
16
+ // watch-renderer`— ese gana: si no, editarlo no se vería nunca, tapado por la versión
17
+ // publicada.
18
+ function baselineRendererDir() {
19
+ const clonLocal = path.join(__dirname, 'glassnote-renderer', 'dist');
20
+ if (fs.existsSync(clonLocal)) return clonLocal;
21
+ try {
22
+ return path.join(path.dirname(require.resolve('@glassnote/renderer/package.json')), 'dist');
23
+ } catch (error) {
24
+ // Sin dependencia ni clon solo queda userData; quien llama avisa si tampoco está.
25
+ return clonLocal;
26
+ }
27
+ }
28
+
29
+ // La versión de un directorio de renderer. El dist/ publicado trae su propio
30
+ // package.json; si algún día no lo trajera, la versión la tiene el paquete de arriba.
31
+ function rendererVersionFrom(rendererPath) {
32
+ try {
33
+ const propio = path.join(rendererPath, 'package.json');
34
+ if (fs.existsSync(propio)) {
35
+ return JSON.parse(fs.readFileSync(propio, 'utf8')).version || '0.0.0';
36
+ }
37
+ if (path.basename(rendererPath) === 'dist') {
38
+ const delPadre = path.join(rendererPath, '..', 'package.json');
39
+ if (fs.existsSync(delPadre)) {
40
+ return JSON.parse(fs.readFileSync(delPadre, 'utf8')).version || '0.0.0';
41
+ }
42
+ }
43
+ } catch (error) {
44
+ console.error('Error reading renderer version from', rendererPath, ':', error);
45
+ }
46
+ return '0.0.0'; // No hay renderer ahí, o no se pudo leer.
47
+ }
48
+
49
+ // Compara dos versiones semánticas: 1 si a es mayor, -1 si b lo es, 0 si empatan.
50
+ function compareRendererVersions(a, b) {
51
+ const pa = String(a).split('.').map((n) => parseInt(n, 10) || 0);
52
+ const pb = String(b).split('.').map((n) => parseInt(n, 10) || 0);
53
+ for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
54
+ const va = i < pa.length ? pa[i] : 0;
55
+ const vb = i < pb.length ? pb[i] : 0;
56
+ if (va > vb) return 1;
57
+ if (va < vb) return -1;
58
+ }
59
+ return 0;
60
+ }
61
+
62
+ module.exports = { baselineRendererDir, rendererVersionFrom, compareRendererVersions };
package/userData.js CHANGED
File without changes