@happier-dev/relay-server 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.
Files changed (52) hide show
  1. package/bin/happier-server.mjs +92 -101
  2. package/node_modules/@happier-dev/release-runtime/dist/.tsbuildinfo +1 -0
  3. package/node_modules/@happier-dev/release-runtime/dist/_cjs/releaseRings.js +134 -0
  4. package/node_modules/@happier-dev/release-runtime/dist/assets.d.ts +22 -0
  5. package/node_modules/@happier-dev/release-runtime/dist/assets.d.ts.map +1 -0
  6. package/node_modules/@happier-dev/release-runtime/dist/assets.js +44 -0
  7. package/node_modules/@happier-dev/release-runtime/dist/assets.js.map +1 -0
  8. package/node_modules/@happier-dev/release-runtime/dist/checksums.d.ts +5 -0
  9. package/node_modules/@happier-dev/release-runtime/dist/checksums.d.ts.map +1 -0
  10. package/node_modules/@happier-dev/release-runtime/dist/checksums.js +21 -0
  11. package/node_modules/@happier-dev/release-runtime/dist/checksums.js.map +1 -0
  12. package/node_modules/@happier-dev/release-runtime/dist/extractPlan.d.ts +14 -0
  13. package/node_modules/@happier-dev/release-runtime/dist/extractPlan.d.ts.map +1 -0
  14. package/node_modules/@happier-dev/release-runtime/dist/extractPlan.js +48 -0
  15. package/node_modules/@happier-dev/release-runtime/dist/extractPlan.js.map +1 -0
  16. package/node_modules/@happier-dev/release-runtime/dist/github.d.ts +26 -0
  17. package/node_modules/@happier-dev/release-runtime/dist/github.d.ts.map +1 -0
  18. package/node_modules/@happier-dev/release-runtime/dist/github.js +141 -0
  19. package/node_modules/@happier-dev/release-runtime/dist/github.js.map +1 -0
  20. package/node_modules/@happier-dev/release-runtime/dist/http.d.ts +18 -0
  21. package/node_modules/@happier-dev/release-runtime/dist/http.d.ts.map +1 -0
  22. package/node_modules/@happier-dev/release-runtime/dist/http.js +112 -0
  23. package/node_modules/@happier-dev/release-runtime/dist/http.js.map +1 -0
  24. package/node_modules/@happier-dev/release-runtime/dist/index.d.ts +8 -0
  25. package/node_modules/@happier-dev/release-runtime/dist/index.d.ts.map +1 -0
  26. package/node_modules/@happier-dev/release-runtime/dist/index.js +8 -0
  27. package/node_modules/@happier-dev/release-runtime/dist/index.js.map +1 -0
  28. package/node_modules/@happier-dev/release-runtime/dist/minisign.d.ts +7 -0
  29. package/node_modules/@happier-dev/release-runtime/dist/minisign.d.ts.map +1 -0
  30. package/node_modules/@happier-dev/release-runtime/dist/minisign.js +92 -0
  31. package/node_modules/@happier-dev/release-runtime/dist/minisign.js.map +1 -0
  32. package/node_modules/@happier-dev/release-runtime/dist/releaseRings.cjs +134 -0
  33. package/node_modules/@happier-dev/release-runtime/dist/releaseRings.d.ts +27 -0
  34. package/node_modules/@happier-dev/release-runtime/dist/releaseRings.d.ts.map +1 -0
  35. package/node_modules/@happier-dev/release-runtime/dist/releaseRings.js +124 -0
  36. package/node_modules/@happier-dev/release-runtime/dist/releaseRings.js.map +1 -0
  37. package/node_modules/@happier-dev/release-runtime/dist/verifiedDownload.d.ts +26 -0
  38. package/node_modules/@happier-dev/release-runtime/dist/verifiedDownload.d.ts.map +1 -0
  39. package/node_modules/@happier-dev/release-runtime/dist/verifiedDownload.js +47 -0
  40. package/node_modules/@happier-dev/release-runtime/dist/verifiedDownload.js.map +1 -0
  41. package/node_modules/@happier-dev/release-runtime/package.json +43 -0
  42. package/package.json +10 -4
  43. package/src/checksums.mjs +1 -17
  44. package/src/checksums.test.mjs +1 -2
  45. package/src/minisign.mjs +1 -94
  46. package/src/minisign.verify.test.mjs +1 -2
  47. package/src/releaseAssets.mjs +30 -30
  48. package/src/releaseAssets.test.mjs +33 -1
  49. package/src/runnerConfig.mjs +69 -0
  50. package/src/runnerConfig.test.mjs +37 -0
  51. package/src/target.mjs +48 -0
  52. package/src/target.test.mjs +54 -0
@@ -1,33 +1,33 @@
1
- export function resolveServerReleaseAssets({ release, os, arch }) {
2
- const assets = Array.isArray(release?.assets) ? release.assets : [];
3
- const byName = new Map();
4
- for (const asset of assets) {
5
- const name = String(asset?.name ?? '');
6
- const url = String(asset?.browser_download_url ?? '');
7
- if (!name || !url) continue;
8
- byName.set(name, { name, url });
9
- }
10
-
11
- const checksumsName = [...byName.keys()].find((name) => /^checksums-happier-server-v\d+\.\d+\.\d+\.txt$/.test(name));
12
- if (!checksumsName) {
13
- throw new Error('[server] missing checksums-happier-server-v<version>.txt asset');
14
- }
15
- const versionMatch = /^checksums-happier-server-v(\d+\.\d+\.\d+)\.txt$/.exec(checksumsName);
16
- const version = versionMatch?.[1] ?? null;
17
- if (!version) {
18
- throw new Error('[server] unable to derive server version from checksums filename');
19
- }
20
-
21
- const checksumsSigName = `${checksumsName}.minisig`;
22
- const tarballName = `happier-server-v${version}-${os}-${arch}.tar.gz`;
1
+ import { resolveReleaseAssetBundle } from '@happier-dev/release-runtime/assets';
23
2
 
24
- const checksums = byName.get(checksumsName) ?? null;
25
- const checksumsSig = byName.get(checksumsSigName) ?? null;
26
- const tarball = byName.get(tarballName) ?? null;
27
-
28
- if (!checksums) throw new Error(`[server] missing release asset: ${checksumsName}`);
29
- if (!checksumsSig) throw new Error(`[server] missing release asset: ${checksumsSigName}`);
30
- if (!tarball) throw new Error(`[server] missing release asset: ${tarballName}`);
3
+ export function resolveServerReleaseAssets({ release, os, arch }) {
4
+ const resolved = resolveReleaseAssetBundle({
5
+ assets: release?.assets,
6
+ product: 'happier-server',
7
+ os,
8
+ arch,
9
+ preferZipOnWindows: true,
10
+ });
11
+ return {
12
+ version: resolved.version,
13
+ tarball: resolved.archive,
14
+ checksums: resolved.checksums,
15
+ checksumsSig: resolved.checksumsSig,
16
+ };
17
+ }
31
18
 
32
- return { version, tarball, checksums, checksumsSig };
19
+ export function resolveUiWebReleaseAssets({ release }) {
20
+ const resolved = resolveReleaseAssetBundle({
21
+ assets: release?.assets,
22
+ product: 'happier-ui-web',
23
+ os: 'web',
24
+ arch: 'any',
25
+ preferZipOnWindows: false,
26
+ });
27
+ return {
28
+ version: resolved.version,
29
+ tarball: resolved.archive,
30
+ checksums: resolved.checksums,
31
+ checksumsSig: resolved.checksumsSig,
32
+ };
33
33
  }
@@ -1,7 +1,7 @@
1
1
  import test from 'node:test';
2
2
  import assert from 'node:assert/strict';
3
3
 
4
- import { resolveServerReleaseAssets } from './releaseAssets.mjs';
4
+ import { resolveServerReleaseAssets, resolveUiWebReleaseAssets } from './releaseAssets.mjs';
5
5
 
6
6
  test('resolveServerReleaseAssets picks tarball + checksums + minisig for linux-x64', () => {
7
7
  const release = {
@@ -21,8 +21,40 @@ test('resolveServerReleaseAssets picks tarball + checksums + minisig for linux-x
21
21
  assert.equal(resolved.checksumsSig.name, 'checksums-happier-server-v0.1.0.txt.minisig');
22
22
  });
23
23
 
24
+ test('resolveServerReleaseAssets prefers windows zip artifacts when available', () => {
25
+ const release = {
26
+ tag_name: 'server-preview',
27
+ assets: [
28
+ { name: 'checksums-happier-server-v0.2.0-preview.7.txt', browser_download_url: 'https://example/checksums.txt' },
29
+ { name: 'checksums-happier-server-v0.2.0-preview.7.txt.minisig', browser_download_url: 'https://example/checksums.txt.minisig' },
30
+ { name: 'happier-server-v0.2.0-preview.7-windows-x64.zip', browser_download_url: 'https://example/server-win.zip' },
31
+ { name: 'happier-server-v0.2.0-preview.7-windows-x64.tar.gz', browser_download_url: 'https://example/server-win.tgz' },
32
+ ],
33
+ };
34
+
35
+ const resolved = resolveServerReleaseAssets({ release, os: 'windows', arch: 'x64' });
36
+ assert.equal(resolved.version, '0.2.0-preview.7');
37
+ assert.equal(resolved.tarball.name, 'happier-server-v0.2.0-preview.7-windows-x64.zip');
38
+ });
39
+
24
40
  test('resolveServerReleaseAssets throws when required assets are missing', () => {
25
41
  const release = { tag_name: 'server-preview', assets: [{ name: 'nope', browser_download_url: 'x' }] };
26
42
  assert.throws(() => resolveServerReleaseAssets({ release, os: 'linux', arch: 'x64' }));
27
43
  });
28
44
 
45
+ test('resolveUiWebReleaseAssets picks ui-web tarball + checksums + minisig', () => {
46
+ const release = {
47
+ tag_name: 'ui-web-preview',
48
+ assets: [
49
+ { name: 'checksums-happier-ui-web-v0.3.0-preview.1.1.txt', browser_download_url: 'https://example/checksums.txt' },
50
+ { name: 'checksums-happier-ui-web-v0.3.0-preview.1.1.txt.minisig', browser_download_url: 'https://example/checksums.txt.minisig' },
51
+ { name: 'happier-ui-web-v0.3.0-preview.1.1-web-any.tar.gz', browser_download_url: 'https://example/ui-web.tgz' },
52
+ ],
53
+ };
54
+
55
+ const resolved = resolveUiWebReleaseAssets({ release });
56
+ assert.equal(resolved.version, '0.3.0-preview.1.1');
57
+ assert.equal(resolved.tarball.name, 'happier-ui-web-v0.3.0-preview.1.1-web-any.tar.gz');
58
+ assert.equal(resolved.checksums.name, 'checksums-happier-ui-web-v0.3.0-preview.1.1.txt');
59
+ assert.equal(resolved.checksumsSig.name, 'checksums-happier-ui-web-v0.3.0-preview.1.1.txt.minisig');
60
+ });
@@ -0,0 +1,69 @@
1
+ import {
2
+ getReleaseRingCatalogEntry,
3
+ normalizePublicReleaseRingId,
4
+ } from '@happier-dev/release-runtime/releaseRings';
5
+
6
+ function parseArgs(argv) {
7
+ const kv = new Map();
8
+ const flags = new Set();
9
+ const positionals = [];
10
+ for (let i = 0; i < argv.length; i += 1) {
11
+ const a = argv[i];
12
+ if (a === '--') {
13
+ positionals.push(...argv.slice(i + 1));
14
+ break;
15
+ }
16
+ if (!String(a ?? '').startsWith('--')) {
17
+ positionals.push(String(a));
18
+ continue;
19
+ }
20
+ const next = argv[i + 1];
21
+ if (next && !String(next).startsWith('--')) {
22
+ kv.set(a, String(next));
23
+ i += 1;
24
+ continue;
25
+ }
26
+ flags.add(String(a));
27
+ kv.set(a, 'true');
28
+ }
29
+ return { kv, flags, positionals };
30
+ }
31
+
32
+ function normalizeChannel(raw) {
33
+ const requested = String(raw ?? '').trim() || 'stable';
34
+ const channel = normalizePublicReleaseRingId(requested);
35
+ if (!channel) {
36
+ throw new Error(`Invalid --channel '${requested}'. Expected stable|preview|dev.`);
37
+ }
38
+ return channel;
39
+ }
40
+
41
+ function parseBooleanFlag(raw, fallback) {
42
+ const value = String(raw ?? '').trim().toLowerCase();
43
+ if (!value) return fallback;
44
+ if (value === '1' || value === 'true' || value === 'yes' || value === 'y' || value === 'on') return true;
45
+ if (value === '0' || value === 'false' || value === 'no' || value === 'n' || value === 'off') return false;
46
+ return fallback;
47
+ }
48
+
49
+ export function parseRunnerInvocation(argv = []) {
50
+ const { kv, flags, positionals } = parseArgs(Array.isArray(argv) ? argv : []);
51
+ const channel = normalizeChannel(kv.get('--channel'));
52
+ const suffix = getReleaseRingCatalogEntry(channel).rollingReleaseSuffix;
53
+ if (!suffix) {
54
+ throw new Error(`Missing rolling release suffix for channel '${channel}'.`);
55
+ }
56
+ const serverTag = String(kv.get('--tag') ?? '').trim() || `server-${suffix}`;
57
+ const uiWebTag = String(kv.get('--ui-tag') ?? '').trim() || `ui-web-${suffix}`;
58
+
59
+ const withUiWeb =
60
+ !(flags.has('--without-ui') || parseBooleanFlag(kv.get('--with-ui'), true) === false);
61
+
62
+ return {
63
+ channel,
64
+ serverTag,
65
+ uiWebTag,
66
+ withUiWeb,
67
+ positionals,
68
+ };
69
+ }
@@ -0,0 +1,37 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+
4
+ import { parseRunnerInvocation } from './runnerConfig.mjs';
5
+
6
+ test('parseRunnerInvocation defaults to stable channel and installs ui web bundle', () => {
7
+ const parsed = parseRunnerInvocation([]);
8
+ assert.equal(parsed.channel, 'stable');
9
+ assert.equal(parsed.serverTag, 'server-stable');
10
+ assert.equal(parsed.uiWebTag, 'ui-web-stable');
11
+ assert.equal(parsed.withUiWeb, true);
12
+ });
13
+
14
+ test('parseRunnerInvocation resolves preview tags from channel', () => {
15
+ const parsed = parseRunnerInvocation(['--channel', 'preview']);
16
+ assert.equal(parsed.channel, 'preview');
17
+ assert.equal(parsed.serverTag, 'server-preview');
18
+ assert.equal(parsed.uiWebTag, 'ui-web-preview');
19
+ });
20
+
21
+ test('parseRunnerInvocation resolves dev user input to publicdev tags', () => {
22
+ const parsed = parseRunnerInvocation(['--channel', 'dev']);
23
+ assert.equal(parsed.channel, 'publicdev');
24
+ assert.equal(parsed.serverTag, 'server-dev');
25
+ assert.equal(parsed.uiWebTag, 'ui-web-dev');
26
+ });
27
+
28
+ test('parseRunnerInvocation honors explicit tag overrides', () => {
29
+ const parsed = parseRunnerInvocation(['--tag', 'server-preview', '--ui-tag', 'ui-web-preview']);
30
+ assert.equal(parsed.serverTag, 'server-preview');
31
+ assert.equal(parsed.uiWebTag, 'ui-web-preview');
32
+ });
33
+
34
+ test('parseRunnerInvocation supports --without-ui', () => {
35
+ const parsed = parseRunnerInvocation(['--without-ui']);
36
+ assert.equal(parsed.withUiWeb, false);
37
+ });
package/src/target.mjs ADDED
@@ -0,0 +1,48 @@
1
+ import { join } from 'node:path';
2
+
3
+ export function resolveServerRunnerTarget({ platform, arch }) {
4
+ const p = String(platform ?? '').trim();
5
+ const a = String(arch ?? '').trim();
6
+ if (!p) {
7
+ throw new Error('Unsupported platform: (empty)');
8
+ }
9
+ if (p !== 'linux' && p !== 'darwin' && p !== 'win32') {
10
+ throw new Error(`Unsupported platform '${p}'. Expected linux|darwin|win32.`);
11
+ }
12
+
13
+ const os = p === 'win32' ? 'windows' : p;
14
+ if (a !== 'x64' && a !== 'arm64') {
15
+ throw new Error(`Unsupported architecture '${a}'. Expected x64 or arm64.`);
16
+ }
17
+ if (p === 'win32' && a !== 'x64') {
18
+ throw new Error(`Unsupported architecture '${a}' for windows. Expected x64.`);
19
+ }
20
+
21
+ return {
22
+ os,
23
+ arch: a,
24
+ exeName: os === 'windows' ? 'happier-server.exe' : 'happier-server',
25
+ };
26
+ }
27
+
28
+ export function resolveRunnerCacheRoot({ platform, homedir, env }) {
29
+ const p = String(platform ?? '').trim();
30
+ const home = String(homedir ?? '').trim();
31
+ const e = env && typeof env === 'object' ? env : {};
32
+
33
+ if (String(e.HAPPIER_CACHE_DIR ?? '').trim()) {
34
+ return String(e.HAPPIER_CACHE_DIR).trim();
35
+ }
36
+
37
+ if (p === 'win32') {
38
+ const local = String(e.LOCALAPPDATA ?? '').trim();
39
+ return local || join(home || 'C:\\\\Users\\\\Default', 'AppData', 'Local');
40
+ }
41
+ if (p === 'darwin') {
42
+ return join(home || '', 'Library', 'Caches');
43
+ }
44
+ const xdg = String(e.XDG_CACHE_HOME ?? '').trim();
45
+ if (xdg) return xdg;
46
+ return join(home || '', '.cache');
47
+ }
48
+
@@ -0,0 +1,54 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { join } from 'node:path';
4
+
5
+ import { resolveServerRunnerTarget, resolveRunnerCacheRoot } from './target.mjs';
6
+
7
+ test('resolveServerRunnerTarget maps linux platforms to linux assets', () => {
8
+ assert.deepEqual(resolveServerRunnerTarget({ platform: 'linux', arch: 'x64' }), {
9
+ os: 'linux',
10
+ arch: 'x64',
11
+ exeName: 'happier-server',
12
+ });
13
+ });
14
+
15
+ test('resolveServerRunnerTarget maps darwin platforms to darwin assets', () => {
16
+ assert.deepEqual(resolveServerRunnerTarget({ platform: 'darwin', arch: 'arm64' }), {
17
+ os: 'darwin',
18
+ arch: 'arm64',
19
+ exeName: 'happier-server',
20
+ });
21
+ });
22
+
23
+ test('resolveServerRunnerTarget maps win32 platforms to windows assets', () => {
24
+ assert.deepEqual(resolveServerRunnerTarget({ platform: 'win32', arch: 'x64' }), {
25
+ os: 'windows',
26
+ arch: 'x64',
27
+ exeName: 'happier-server.exe',
28
+ });
29
+ });
30
+
31
+ test('resolveServerRunnerTarget rejects unsupported platform/arch combinations', () => {
32
+ assert.throws(() => resolveServerRunnerTarget({ platform: 'freebsd', arch: 'x64' }), /Unsupported platform/i);
33
+ assert.throws(() => resolveServerRunnerTarget({ platform: 'win32', arch: 'arm64' }), /Unsupported architecture/i);
34
+ });
35
+
36
+ test('resolveRunnerCacheRoot uses platform-specific defaults', () => {
37
+ assert.equal(
38
+ resolveRunnerCacheRoot({ platform: 'linux', homedir: '/home/me', env: {} }),
39
+ join('/home/me', '.cache')
40
+ );
41
+ assert.equal(
42
+ resolveRunnerCacheRoot({ platform: 'linux', homedir: '/home/me', env: { XDG_CACHE_HOME: '/tmp/xdg' } }),
43
+ '/tmp/xdg'
44
+ );
45
+ assert.equal(
46
+ resolveRunnerCacheRoot({ platform: 'darwin', homedir: '/Users/me', env: {} }),
47
+ join('/Users/me', 'Library', 'Caches')
48
+ );
49
+ assert.equal(
50
+ resolveRunnerCacheRoot({ platform: 'win32', homedir: 'C:\\\\Users\\\\me', env: { LOCALAPPDATA: 'C:\\\\Local' } }),
51
+ 'C:\\\\Local'
52
+ );
53
+ });
54
+