cross-packer 0.1.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 (126) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +151 -0
  3. package/README.zh-CN.md +151 -0
  4. package/dist/archive/ar.d.ts +12 -0
  5. package/dist/archive/seven-za.d.ts +32 -0
  6. package/dist/archive/tar.d.ts +30 -0
  7. package/dist/archive/zip-reader.d.ts +17 -0
  8. package/dist/asar/dependency/collect.d.ts +2 -0
  9. package/dist/asar/dependency/detector.d.ts +8 -0
  10. package/dist/asar/dependency/hoister.d.ts +27 -0
  11. package/dist/asar/dependency/utils.d.ts +59 -0
  12. package/dist/asar/fileset/matcher.d.ts +12 -0
  13. package/dist/asar/fileset/platform-fileset.d.ts +36 -0
  14. package/dist/asar/fileset/transformer.d.ts +2 -0
  15. package/dist/asar/fileset/unpack-detector.d.ts +10 -0
  16. package/dist/asar/fileset/walker.d.ts +14 -0
  17. package/dist/asar/pack.d.ts +23 -0
  18. package/dist/asar/packer/app-collector.d.ts +2 -0
  19. package/dist/asar/packer/collector.d.ts +18 -0
  20. package/dist/asar/packer/concurrency.d.ts +1 -0
  21. package/dist/asar/packer/integrity.d.ts +8 -0
  22. package/dist/asar/packer/nm-collector.d.ts +7 -0
  23. package/dist/asar/packer/stream-builder.d.ts +18 -0
  24. package/dist/cli.d.ts +2 -0
  25. package/dist/config/index.d.ts +97 -0
  26. package/dist/electron/dist-zip.d.ts +18 -0
  27. package/dist/electron/download.d.ts +21 -0
  28. package/dist/index.d.ts +11 -0
  29. package/dist/packer/common/app-update-yml.d.ts +5 -0
  30. package/dist/packer/common/extra-resources.d.ts +39 -0
  31. package/dist/packer/common/staging.d.ts +3 -0
  32. package/dist/packer/common/template.d.ts +2 -0
  33. package/dist/packer/linux/linux.d.ts +2 -0
  34. package/dist/packer/mac/mac.d.ts +23 -0
  35. package/dist/packer/win/nsis.d.ts +24 -0
  36. package/dist/packer/win/resedit.d.ts +5 -0
  37. package/dist/packer/win/toolchain.d.ts +8 -0
  38. package/dist/packer/win/win.d.ts +12 -0
  39. package/dist/pipeline/pipeline.d.ts +41 -0
  40. package/dist/pipeline/targets.d.ts +28 -0
  41. package/dist/shared/hash.d.ts +2 -0
  42. package/dist/shared/logger.d.ts +39 -0
  43. package/dist/shared/platform.d.ts +26 -0
  44. package/dist/shared/types.d.ts +68 -0
  45. package/dist/update/blockmap.d.ts +13 -0
  46. package/dist/update/metadata.d.ts +20 -0
  47. package/dist/update/version.d.ts +7 -0
  48. package/package.json +71 -0
  49. package/src/archive/ar.ts +41 -0
  50. package/src/archive/seven-za.ts +160 -0
  51. package/src/archive/tar.ts +142 -0
  52. package/src/archive/zip-reader.ts +61 -0
  53. package/src/asar/dependency/collect.ts +437 -0
  54. package/src/asar/dependency/detector.ts +73 -0
  55. package/src/asar/dependency/hoister.ts +714 -0
  56. package/src/asar/dependency/utils.ts +225 -0
  57. package/src/asar/fileset/matcher.ts +101 -0
  58. package/src/asar/fileset/platform-fileset.ts +416 -0
  59. package/src/asar/fileset/transformer.ts +154 -0
  60. package/src/asar/fileset/unpack-detector.ts +38 -0
  61. package/src/asar/fileset/walker.ts +115 -0
  62. package/src/asar/pack.ts +186 -0
  63. package/src/asar/packer/app-collector.ts +74 -0
  64. package/src/asar/packer/collector.ts +134 -0
  65. package/src/asar/packer/concurrency.ts +24 -0
  66. package/src/asar/packer/integrity.ts +53 -0
  67. package/src/asar/packer/nm-collector.ts +194 -0
  68. package/src/asar/packer/stream-builder.ts +723 -0
  69. package/src/cli.ts +182 -0
  70. package/src/config/index.ts +365 -0
  71. package/src/electron/dist-zip.ts +65 -0
  72. package/src/electron/download.ts +121 -0
  73. package/src/index.ts +14 -0
  74. package/src/packer/common/app-update-yml.ts +21 -0
  75. package/src/packer/common/extra-resources.ts +83 -0
  76. package/src/packer/common/staging.ts +33 -0
  77. package/src/packer/common/template.ts +24 -0
  78. package/src/packer/linux/linux.ts +329 -0
  79. package/src/packer/mac/mac.ts +495 -0
  80. package/src/packer/win/nsis.ts +244 -0
  81. package/src/packer/win/resedit.ts +60 -0
  82. package/src/packer/win/toolchain.ts +63 -0
  83. package/src/packer/win/win.ts +421 -0
  84. package/src/pipeline/pipeline.ts +149 -0
  85. package/src/pipeline/targets.ts +67 -0
  86. package/src/shared/hash.ts +15 -0
  87. package/src/shared/logger.ts +91 -0
  88. package/src/shared/platform.ts +41 -0
  89. package/src/shared/types.ts +82 -0
  90. package/src/update/blockmap.ts +247 -0
  91. package/src/update/metadata.ts +97 -0
  92. package/src/update/version.ts +59 -0
  93. package/templates/linux/app.desktop +11 -0
  94. package/templates/linux/control +9 -0
  95. package/templates/linux/postinst +10 -0
  96. package/templates/linux/postrm +8 -0
  97. package/templates/linux/preinst +49 -0
  98. package/templates/linux/prerm +43 -0
  99. package/templates/mac/Info.plist +18 -0
  100. package/templates/mac/app-update.yml +4 -0
  101. package/templates/win/nsis/README.md +51 -0
  102. package/templates/win/nsis/THIRD-PARTY-NOTICES.md +20 -0
  103. package/templates/win/nsis/assistedInstaller.nsh +154 -0
  104. package/templates/win/nsis/assistedMessages.yml +368 -0
  105. package/templates/win/nsis/common.nsh +147 -0
  106. package/templates/win/nsis/empty-license.txt +1 -0
  107. package/templates/win/nsis/include/FileAssociation.nsh +123 -0
  108. package/templates/win/nsis/include/StdUtils.nsh +496 -0
  109. package/templates/win/nsis/include/StrContains.nsh +48 -0
  110. package/templates/win/nsis/include/UAC.nsh +299 -0
  111. package/templates/win/nsis/include/allowOnlyOneInstallerInstance.nsh +164 -0
  112. package/templates/win/nsis/include/extractAppPackage.nsh +138 -0
  113. package/templates/win/nsis/include/getProcessInfo.nsh +157 -0
  114. package/templates/win/nsis/include/installUtil.nsh +248 -0
  115. package/templates/win/nsis/include/installer.nsh +247 -0
  116. package/templates/win/nsis/include/nsProcess.nsh +28 -0
  117. package/templates/win/nsis/include/webPackage.nsh +64 -0
  118. package/templates/win/nsis/installSection.nsh +110 -0
  119. package/templates/win/nsis/installer.nsi +132 -0
  120. package/templates/win/nsis/messages.yml +240 -0
  121. package/templates/win/nsis/multiUser.nsh +134 -0
  122. package/templates/win/nsis/multiUserUi.nsh +230 -0
  123. package/templates/win/nsis/oneClick.nsh +19 -0
  124. package/templates/win/nsis/portable.nsi +91 -0
  125. package/templates/win/nsis/uninstaller.nsh +263 -0
  126. package/templates/win/nsis-header.nsh +57 -0
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Logging facade. All output is written through process.stdout.write or
3
+ * process.stderr.write; quiet mode suppresses every channel.
4
+ */
5
+ import type { Arch, Platform } from './platform.ts'
6
+
7
+ interface LoggerOptions {
8
+ quiet?: boolean
9
+ }
10
+
11
+ interface BannerSummary {
12
+ platforms: Platform[]
13
+ arches: Arch[]
14
+ }
15
+
16
+ interface TargetResult {
17
+ platform: Platform
18
+ arch: Arch
19
+ output?: string
20
+ error?: string
21
+ }
22
+
23
+ interface SummaryResult {
24
+ targets: TargetResult[]
25
+ }
26
+
27
+ /** Project identity fields shown in the banner; any config object with these fields works. */
28
+ export interface BannerInput {
29
+ productName: string
30
+ version: string
31
+ electronVersion: string
32
+ }
33
+
34
+ export interface Logger {
35
+ info: (message: string) => void
36
+ warn: (message: string) => void
37
+ error: (message: string) => void
38
+ step: (label: string, message: string) => void
39
+ banner: (project: BannerInput, summary: BannerSummary) => void
40
+ summary: (result: SummaryResult) => void
41
+ }
42
+
43
+ export function createLogger(options: LoggerOptions = {}): Logger {
44
+ const { quiet = false } = options
45
+
46
+ const emit = (stream: NodeJS.WriteStream, prefix: string, message: string): void => {
47
+ if (quiet) return
48
+ stream.write(`${prefix ? `${prefix} ` : ''}${message}\n`)
49
+ }
50
+
51
+ return {
52
+ info: (message) => emit(process.stdout, '', message),
53
+ warn: (message) => emit(process.stderr, '[warn]', message),
54
+ error: (message) => emit(process.stderr, '[error]', message),
55
+ step: (label, message) => emit(process.stdout, ` [${label}]`, message),
56
+
57
+ banner(project, summary) {
58
+ if (quiet) return
59
+ const targets = summary.platforms.join(' + ')
60
+ const lines = [
61
+ '=========================================',
62
+ ' cross-packer — Cross-platform packaging',
63
+ '=========================================',
64
+ ` Project: ${project.productName} v${project.version}`,
65
+ ` Electron: v${project.electronVersion}`,
66
+ ` Targets: ${targets}`,
67
+ ` Architectures: ${summary.arches.join(', ')}`,
68
+ '=========================================',
69
+ ]
70
+ process.stdout.write(`${lines.join('\n')}\n`)
71
+ },
72
+
73
+ summary(result) {
74
+ if (quiet) return
75
+ const lines = [
76
+ '\n=========================================',
77
+ ' Packaging Results',
78
+ '=========================================',
79
+ ]
80
+ for (const r of result.targets) {
81
+ const status = r.error ? 'FAIL' : 'OK'
82
+ lines.push(` [${status}] ${r.platform}-${r.arch}: ${r.output || r.error}`)
83
+ }
84
+ lines.push('=========================================')
85
+ process.stdout.write(`${lines.join('\n')}\n`)
86
+ },
87
+ }
88
+ }
89
+
90
+ /** Shared default logger instance, used by packer/collector/util modules. */
91
+ export const logger: Logger = createLogger()
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Cross-layer platform vocabulary: target platforms, architectures, and
3
+ * Node.js host platforms. Zero project dependencies; both config and the
4
+ * asar domain consume these enums.
5
+ */
6
+ export enum Platform {
7
+ WIN = 'win',
8
+ MAC = 'mac',
9
+ LINUX = 'linux',
10
+ }
11
+
12
+ export enum Arch {
13
+ AMD64 = 'amd64',
14
+ ARM64 = 'arm64',
15
+ }
16
+
17
+ export enum NodePlatform {
18
+ WIN32 = 'win32',
19
+ DARWIN = 'darwin',
20
+ LINUX = 'linux',
21
+ }
22
+
23
+ export function nodeArchName(arch: Arch): 'x64' | 'arm64' {
24
+ return arch === Arch.AMD64 ? 'x64' : 'arm64'
25
+ }
26
+
27
+ /** All runtime values of NodePlatform, used for narrowing `process.platform`. */
28
+ const NODE_PLATFORMS: readonly NodePlatform[] = Object.values(NodePlatform)
29
+
30
+ /**
31
+ * Narrow the host's `process.platform` to NodePlatform.
32
+ * Throws on unsupported hosts (node_modules collection is only defined
33
+ * for win32 / darwin / linux).
34
+ */
35
+ export function nodePlatformOf(host: NodeJS.Platform): NodePlatform {
36
+ const narrow = NODE_PLATFORMS.find((p) => p === host)
37
+ if (narrow === undefined) {
38
+ throw new Error(`Unsupported host platform: ${host}`)
39
+ }
40
+ return narrow
41
+ }
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Zero-dependency shared types.
3
+ *
4
+ * This file may only import from `node:` builtins and external type
5
+ * packages — never from project modules. Every other layer depends on
6
+ * `shared/`, which keeps type-only dependencies acyclic at the module level.
7
+ */
8
+ import type { Stats } from 'node:fs'
9
+
10
+ /** The kind of an asar-storable entry. */
11
+ export enum EntryKind {
12
+ FILE = 'file',
13
+ DIRECTORY = 'directory',
14
+ LINK = 'link',
15
+ }
16
+
17
+ /** A plain JSON object: arbitrary string keys, JSON-compatible values. */
18
+ export type JsonObject = Record<string, unknown>
19
+
20
+ /** A logical file set: an absolute source dir plus the collected files. */
21
+ export interface FileSet {
22
+ src: string
23
+ destination: string
24
+ files: string[]
25
+ metadata: Map<string, FileStat>
26
+ }
27
+
28
+ /** File filter predicate used across collectors. */
29
+ export type FileFilter = (file: string, stat: FileStat) => boolean
30
+
31
+ /**
32
+ * Callback invoked for each collected node_modules file; returning true
33
+ * forces the file to be included even if it matches default exclusions.
34
+ */
35
+ export type FileVisitor = (file: string) => boolean
36
+
37
+ /**
38
+ * Collected entry metadata: the entry kind plus the module metadata that
39
+ * the collectors attach. Pure data with no behavior.
40
+ */
41
+ export interface FileStat {
42
+ type: EntryKind
43
+ size: number
44
+ mode: number
45
+ /** Real on-disk source path when the packed file differs from the logical path. */
46
+ realFilePath?: string
47
+ /** Name of the node module the file belongs to. */
48
+ moduleName?: string
49
+ /** Root directory of the owning module. */
50
+ moduleRootPath?: string
51
+ /** Full path relative to the module root. */
52
+ moduleFullFilePath?: string
53
+ /** Symlink target relative to the module root. */
54
+ relativeLink?: string
55
+ /** Symlink target relative to the link's directory. */
56
+ linkRelativeToFile?: string
57
+ }
58
+
59
+ /** A resolved production dependency reported by the collector. */
60
+ export interface DependencyEntry {
61
+ name: string
62
+ dir: string
63
+ dependencies?: DependencyEntry[]
64
+ }
65
+
66
+ /** A parsed minimatch pattern (from the `minimatch` package). */
67
+ export interface MinimatchPattern {
68
+ match: (p: string, partial?: boolean) => boolean
69
+ set: Array<Array<string | object | symbol>>
70
+ negate: boolean
71
+ }
72
+
73
+ /**
74
+ * Map an fs stat result to its entry kind, or null for special entries
75
+ * (sockets, FIFOs, devices) that the asar format cannot store.
76
+ */
77
+ export function entryKind(stat: Stats): EntryKind | null {
78
+ if (stat.isSymbolicLink()) return EntryKind.LINK
79
+ if (stat.isFile()) return EntryKind.FILE
80
+ if (stat.isDirectory()) return EntryKind.DIRECTORY
81
+ return null
82
+ }
@@ -0,0 +1,247 @@
1
+ /**
2
+ * Pure-JavaScript blockmap generator using Rabin fingerprinting.
3
+ * Ported from electron-builder v26.15.2 (app-builder-lib/out/targets/blockmap/blockmap.js),
4
+ * which replaced the Go-based app-builder.exe blockmap command.
5
+ *
6
+ * The output format is identical (blockmap v2 JSON), so the generated
7
+ * .blockmap files are fully compatible with electron-updater.
8
+ */
9
+
10
+ import { createHash } from 'node:crypto'
11
+ import { createReadStream } from 'node:fs'
12
+ import { appendFile, writeFile } from 'node:fs/promises'
13
+ import zlib from 'node:zlib'
14
+ import { blake2b } from '@noble/hashes/blake2.js'
15
+
16
+ // Rabin fingerprinting constants from github.com/aclements/go-rabin:
17
+ // Poly64 = 0xbfe6b8a5bf378d83 (degree-63 irreducible polynomial over GF(2))
18
+ // Window = 64 bytes, Avg = 16 KB, Min = 8 KB, Max = 32 KB
19
+ // Boundary condition: (hash & (AVG-1)) === (AVG-1), i.e. all mask bits set.
20
+ //
21
+ // The tables below are precomputed offline (avoiding BigInt in production):
22
+ // PUSH[i] = (i * x^63 + (i * x^63 mod p)) truncated to 64 bits
23
+ // POP[i] = (i * x^(63*8)) mod p
24
+ // Each stored as signed Int32 hi/lo pair; hash = hi*2^32 + lo (unsigned 64-bit).
25
+ // prettier-ignore
26
+ const PUSH_HI = new Int32Array([
27
+ 0, -1075398491, 2144170315, -1070872082, 1081891379, -6626666, 1068575608, -2141744163,
28
+ 1058933955, -2131184538, 1087602056, -13253331, 2137151216, -1065030059, 11478971, -1085694178,
29
+ 2117867910, -1042636509, 32598221, -1105735576, 1044399029, -2119763184, 1099772670, -26506661,
30
+ 1092752709, -20664864, 1055876110, -2130060117, 22957942, -1095174189, 2123578941, -1049262440,
31
+ 1134463913, -59231476, 1012136674, -2085273017, 65196442, -1140561601, 2083496145, -1010231180,
32
+ 2088798058, -1016709169, 55440929, -1129623932, 1023204697, -2095421956, 1127328786, -53013321,
33
+ 1034064431, -2109461878, 1114628964, -41329727, 2111752220, -1036488519, 34847063, -1108016654,
34
+ 45915884, -1118165431, 2104618919, -1030269182, 1119929567, -47809414, 1024308628, -2098524879,
35
+ 953921527, -2026039470, 1192681148, -118462951, 2024273348, -952021663, 124421263, -1198769110,
36
+ 130392884, -1205658735, 2013844095, -940676390, 1203370247, -127975006, 947161164, -2020462359,
37
+ 1189590641, -117371180, 959104826, -2033418337, 110881858, -1182968601, 2035719433, -961534548,
38
+ 2046409394, -971046377, 104123385, -1177390244, 965075073, -2040309724, 1179161034, -106026641,
39
+ 2068128862, -995910405, 76043541, -1150358096, 993623661, -2065709368, 1156845350, -82659453,
40
+ 1146824861, -71462856, 999709142, -2072977037, 69694126, -1144927733, 2078933989, -1005798592,
41
+ 91831768, -1163950723, 2058636435, -984419274, 1157982187, -85729458, 986191520, -2060538363,
42
+ 979841307, -2055108162, 1168785488, -95618827, 2048617256, -973220979, 97917539, -1171217722,
43
+ 1907843054, -833493173, 242888357, -1315138048, 835388893, -1909605000, 1309045910, -236925901,
44
+ 1319719725, -246420600, 828645990, -1904043325, 248842526, -1322012229, 1897429077, -822165264,
45
+ 260785768, -1334968627, 1883649827, -811561082, 1341594715, -267279106, 809135376, -1881352779,
46
+ 815090347, -1888226802, 1331182560, -255950011, 1894322328, -821057475, 254042579, -1329407626,
47
+ 841602119, -1915786014, 1306830092, -234742359, 1918209652, -844754337, 126847026, -1200980652,
48
+ 1300297915, -237634370, 1906513758, -835821708, 237740260, -1327042663, 1911303428, -840027147,
49
+ 126171951, -1202760392,
50
+ ])
51
+ // prettier-ignore
52
+ const PUSH_LO = new Int32Array([
53
+ 0, -1086878333, 2121210630, -1051158907, 1139391375, -52546036, 1032233097, -2102317814,
54
+ 954530461, -2016184546, 1183572379, -105092072, 2064466194, -1002779503, 90331668, -1168779369,
55
+ 1909060922, -822183751, 262598204, -1332649025, 840978101, -1927822538, 1280267699, -210184144,
56
+ 1227689895, -166034908, 927077537, -2005559006, 180663336, -1242351189, 1957408558, -878959955,
57
+ 1554278391, -476845452, 583761137, -1644367502, 525196408, -1602596357, 1629669246, -569029891,
58
+ 1681956202, -612953879, 439322220, -1525150737, 665396965, -1734431898, 1506164195, -420368288,
59
+ 762155725, -1839587506, 1392675275, -332069816, 1854155074, -776756031, 283849284, -1344487481,
60
+ 361326672, -1430329901, 1810264918, -724435243, 1449186271, -380150180, 672123097, -1757919910,
61
+ 109003373, -1186410514, 2014337387, -953690904, 1167522274, -90082207, 1006232292, -2066845849,
62
+ 1050392816, -2119420557, 1089774582, -3985803, 2104623999, -1035628804, 52238457, -1138059782,
63
+ 2008463191, -931054892, 165260369, -1225907758, 878644440, -1956085413, 1244665822, -184051107,
64
+ 1330793930, -261767095, 826103500, -1911891121, 213644869, -1282638906, 1926556995, -840736576,
65
+ 1524311450, -437475303, 615792284, -1685867745, 422747669, -1509616746, 1734182163, -664139632,
66
+ 1648353031, -586657148, 475055105, -1553512062, 567698568, -1629361909, 1605992334, -527502835,
67
+ 722653344, -1809490653, 1434307494, -364231131, 1761307439, -674437460, 378826793, -1448870486,
68
+ 334899773, -1396594754, 1838756155, -760300360, 1344246194, -282584015, 779127476, -1857616073,
69
+ 218006746, -1278670503, 1922146268, -844754337, 1326923605, -266292522, 829957203, -1907381808,
70
+ 874151495, -1959922748, 1249207617, -180164414, 2012464584, -926660533, 161275598, -1230286003,
71
+ 2100785632, -1040122781, 56126182, -1133516955, 1054788207, -2115418132, 1085395305, -7971606,
72
+ 1171491709, -85719298, 1002213499, -2071257608, 104476914, -1190281871, 2018847732, -949836169,
73
+ 1348133677, -278040914, 775288875, -1862109784, 330520738,
74
+ ])
75
+ // prettier-ignore
76
+ const POP_HI = new Int32Array([
77
+ 0, 2138251555, 1090583266, 1047780289, 1038568800, 1117023299, 2095560578, 60069537, 2077137601,
78
+ 79543266, 986603555, 1170030848, 1177147297, 962262658, 120139075, 2019167328, 1216056615,
79
+ 923315204, 159086533, 1980258022, 1973207111, 183492964, 882653861, 1273961350, 867506150,
80
+ 1288058565, 1924525316, 231132199, 240278150, 1898019749, 1330815076, 807502151, 789684970,
81
+ 1348633545, 1846630408, 291666219, 318173066, 1837485737, 1408636264, 746927179, 1423915051,
82
+ 732698888, 366985929, 1789715434, 1765307723, 374035560, 674795433, 1464577674, 1735012301,
83
+ 404295406, 644535599, 1494873100, 1519215277, 637420430, 462264399, 1694415212, 480556300,
84
+ 1675072559, 1571049454, 584543949, 558102636, 1580259663, 1615004302, 523248557, 1579369941,
85
+ 558916854, 522436407, 1615896084, 1673791669, 481765782, 583332439, 1572328308, 636346132,
86
+ 1520369207, 1693263350, 463340757, 403346036, 1736029015, 1493854358, 645482933, 374917362,
87
+ 1764485585, 1465397776, 673911603, 733971858, 1422697649, 1790934896, 365715027, 1838567987,
88
+ 317027088, 748071121, 1407551986, 1349590867, 788676208, 292677041, 1845675154, 1899161407,
89
+ 239191580, 808590813, 1329675518, 1289071199, 866552700, 232083645, 1923510686, 184319486,
90
+ 1972329693, 1274840860, 881829439, 924528798, 1214779837, 1981532796, 157870943, 961112600,
91
+ 1178225467, 2018087162, 121287129, 78522232, 2078082651, 1169087898, 987626681, 1116205273,
92
+ 1039454714, 59181627, 2096376600, 2137046457, 1285274, 1046497115, 1091790456, 60830479,
93
+ 2094207532, 1117833709, 1037297870, 1044872815, 1093951308, 2135393421, 3450286, 2020518350,
94
+ 119376109, 963531564, 1176334863, 1166664878, 989513101, 76095052, 2079997807, 1272692264,
95
+ 883465995, 182142154, 1973970409, 1983705928, 156226155, 926681514, 1213147273, 806692073,
96
+ 1332086218, 1897258507, 241631016, 233990537, 1921075370, 1290965867, 864137800, 749834725,
97
+ 1405268166, 1840343815, 314722852, 290905221, 1847983526, 1347823207, 790955844, 1467943716,
98
+ 671885831, 377483718, 1762447589, 178,
99
+ ])
100
+ // prettier-ignore
101
+ const POP_LO = new Int32Array([
102
+ 0, -1826189856, -1720019389, 173808547, -1916624635, 518270181, 347617094, -2019640666, 461718026,
103
+ -2002530326, -2097163191, 299456937, -1773673713, 90271471, 255685964, -1675888468, -2009265769,
104
+ 454788215, 289906644, -2107567564, 100640914, -1764153998, -1668989231, 262386481, -1816638563,
105
+ 10405501, 180542942, -1713090498, 511371928, -1923324040, -2030011173, 338096443, -1353867603,
106
+ 1013650253, 909576430, -1525578482, 579813288, -1314278840, -1141528085, 684925963, -1261904729,
107
+ 669929799, 766659300, -1097545980, 956988834, -1439895486, -1603212319, 861300225, 661690170,
108
+ -1269425446, -1106640519, 757894297, -1431161281, 966048735, 868786300, -1595003492, 1022743856,
109
+ -1345103664, -1517337741, 917098131, -1321765835, 571603413, 676192886, -1150586986, 1587232090,
110
+ -843206470, -941511911, 1422337785, -749101985, 1082069439, 1243810332, -653948932, 1159626576,
111
+ -700910928, -597366509, 1329751283, -925049259, 1543132085, 1369851926, -1031748106, -693750579,
112
+ 1166063917, 1339859598, -587583634, 1533318600, -935192536, -1038220405, 1362660971, -853313849,
113
+ 1577450279, 1415176324, -947950236, 1088542658, -741909982, -644136575, 1253952609, -237591561,
114
+ 1659907607, 1756116404, -74794924, 2081686258, -281899246, -445737807, 1984436561, -363601411,
115
+ 2037738525, 1932097470, -535823778, 1737572600, -189281000, -18098501, 1842174811, 2045487712,
116
+ -355653760, -527352797, 1941418435, -198636699, 1729070725, 1834196262, -25882426, 1651435626,
117
+ -246913654, -82543063, 1748169673, -273921681, 2089469071, 1993793324, -437234996, 33683255,
118
+ -1859683625, -1686412940, 140373140, -1883023822, 484826066, 381272177, -2053157487, 428127549,
119
+ -1969080099, -2130828418, 332967582, -1807346632, 123771352, 222069371, -1642459237, -1975714144,
120
+ 421163840, 323381475, -2141133565, 134111141, -1797729723, -1635464730, 228738054, -1850098518,
121
+ 43987274, 147008233, -1679448311, 477830575, -1889693617, -2063496212, 371656204, -1387501158,
122
+ 1047192698, 876019673, -1492094407, 546228383, -1635627768, 336483553, -1433766908, -2057987869,
123
+ 82141755, -1254257515, -1910308274, 1756305977, -858749288, 1060136802, -1933640611,
124
+ ])
125
+
126
+ const RABIN_MIN = 8192
127
+ const RABIN_AVG = 16384
128
+ const RABIN_MAX = 32768
129
+ const RABIN_WINDOW = 64
130
+ const RABIN_HASH_MASK = RABIN_AVG - 1 // 0x3FFF
131
+
132
+ export async function buildBlockMap(
133
+ inFile: string,
134
+ compressionFormat: string,
135
+ outFile?: string | null,
136
+ ): Promise<{ sha512: string; size: number; blockMapSize?: number }> {
137
+ const fileHash = createHash('sha512')
138
+ const checksums: string[] = []
139
+ const sizes: number[] = []
140
+ let totalSize = 0
141
+
142
+ const chunkBuf = Buffer.allocUnsafe(RABIN_MAX)
143
+ let chunkN = 0
144
+ let hi = 0
145
+ let lo = 0
146
+ const win = new Uint8Array(RABIN_WINDOW)
147
+ let wpos = 0
148
+
149
+ function emitChunk(): void {
150
+ checksums.push(
151
+ Buffer.from(blake2b(chunkBuf.subarray(0, chunkN), { dkLen: 18 })).toString('base64'),
152
+ )
153
+ sizes.push(chunkN)
154
+ chunkN = 0
155
+ hi = 0
156
+ lo = 0
157
+ win.fill(0)
158
+ wpos = 0
159
+ }
160
+
161
+ // Feed one byte into the Rabin fingerprint window.
162
+ function processByte(b: number): void {
163
+ chunkBuf[chunkN++] = b
164
+ if (chunkN <= RABIN_MIN - RABIN_WINDOW) {
165
+ return
166
+ }
167
+ if (chunkN <= RABIN_MIN) {
168
+ win[wpos] = b
169
+ wpos = (wpos + 1) % RABIN_WINDOW
170
+ const top = (hi >>> 23) & 0xff
171
+ const nhi = ((hi << 8) | (lo >>> 24)) & 0x7fffffff
172
+ const nlo = ((lo << 8) | b) & 0xffffffff
173
+ hi = (nhi ^ PUSH_HI[top]) | 0
174
+ lo = (nlo ^ PUSH_LO[top]) | 0
175
+ if (chunkN === RABIN_MIN && (lo & RABIN_HASH_MASK) === RABIN_HASH_MASK) {
176
+ emitChunk()
177
+ }
178
+ return
179
+ }
180
+ const old = win[wpos]
181
+ win[wpos] = b
182
+ wpos = (wpos + 1) % RABIN_WINDOW
183
+ hi = (hi ^ POP_HI[old]) | 0
184
+ lo = (lo ^ POP_LO[old]) | 0
185
+ const top = (hi >>> 23) & 0xff
186
+ const nhi = ((hi << 8) | (lo >>> 24)) & 0x7fffffff
187
+ const nlo = ((lo << 8) | b) & 0xffffffff
188
+ hi = (nhi ^ PUSH_HI[top]) | 0
189
+ lo = (nlo ^ PUSH_LO[top]) | 0
190
+ if ((lo & RABIN_HASH_MASK) === RABIN_HASH_MASK || chunkN >= RABIN_MAX) {
191
+ emitChunk()
192
+ }
193
+ }
194
+
195
+ // Stream-read the file, feeding SHA-512 and the Rabin chunker simultaneously.
196
+ const done = new Promise<void>((resolve, reject) => {
197
+ const rs = createReadStream(inFile, { highWaterMark: 256 * 1024 })
198
+ rs.on('data', (chunk) => {
199
+ const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)
200
+ fileHash.update(buf)
201
+ totalSize += buf.length
202
+ for (let i = 0; i < buf.length; i++) {
203
+ processByte(buf[i])
204
+ }
205
+ })
206
+ rs.on('end', () => {
207
+ if (chunkN > 0) {
208
+ emitChunk()
209
+ }
210
+ resolve()
211
+ })
212
+ rs.on('error', reject)
213
+ })
214
+ await done
215
+
216
+ const blockMap = {
217
+ version: '2',
218
+ files: [{ name: 'file', offset: 0, checksums, sizes }],
219
+ }
220
+ const compressed = compress(Buffer.from(JSON.stringify(blockMap)), compressionFormat)
221
+
222
+ if (outFile == null || outFile === '') {
223
+ const sizeHeader = Buffer.allocUnsafe(4)
224
+ sizeHeader.writeUInt32BE(compressed.length, 0)
225
+ const appended = Buffer.concat([compressed, sizeHeader])
226
+ await appendFile(inFile, appended)
227
+ fileHash.update(appended)
228
+ return {
229
+ size: totalSize + appended.length,
230
+ sha512: fileHash.digest('base64'),
231
+ blockMapSize: compressed.length,
232
+ }
233
+ }
234
+
235
+ await writeFile(outFile, compressed)
236
+ return {
237
+ size: totalSize,
238
+ sha512: fileHash.digest('base64'),
239
+ blockMapSize: compressed.length,
240
+ }
241
+ }
242
+
243
+ function compress(data: Buffer, format: string): Buffer {
244
+ return format === 'deflate'
245
+ ? zlib.deflateRawSync(data, { level: 9 })
246
+ : zlib.gzipSync(data, { level: 9 })
247
+ }
@@ -0,0 +1,97 @@
1
+ /**
2
+ * electron-updater compatible metadata generation
3
+ * (latest.yml / latest-mac.yml / latest-linux.yml).
4
+ */
5
+ import { existsSync, statSync, writeFileSync } from 'node:fs'
6
+ import path from 'node:path'
7
+ import { hashFile } from '../shared/hash.ts'
8
+ import { NodePlatform } from '../shared/platform.ts'
9
+
10
+ export interface UpdateMetadataOptions {
11
+ filePath: string
12
+ version: string
13
+ outputDir: string
14
+ platform: NodePlatform
15
+ blockMapSize?: number
16
+ channel?: string
17
+ extraFiles?: string[]
18
+ }
19
+
20
+ /**
21
+ * Derive the update channel from a semver prerelease suffix
22
+ * (for example, 1.2.3-beta.1 yields "beta").
23
+ */
24
+ export function getUpdateChannel(
25
+ version: string,
26
+ explicitChannel?: string,
27
+ defaultChannel = 'latest',
28
+ ): string {
29
+ if (explicitChannel) return explicitChannel
30
+ const match = version.match(/^\d+\.\d+\.\d+-([0-9A-Za-z-]+)\.\d+$/)
31
+ return match ? match[1] : defaultChannel
32
+ }
33
+
34
+ export function getUpdateMetadataFileName(
35
+ version: string,
36
+ explicitChannel?: string,
37
+ platform: NodePlatform = NodePlatform.WIN32,
38
+ defaultChannel = 'latest',
39
+ ): string {
40
+ const channel = getUpdateChannel(version, explicitChannel, defaultChannel)
41
+ if (platform === NodePlatform.DARWIN)
42
+ return `${channel === 'latest' ? 'latest' : channel}-mac.yml`
43
+ if (platform === NodePlatform.LINUX)
44
+ return `${channel === 'latest' ? 'latest' : channel}-linux.yml`
45
+ return `${channel}.yml`
46
+ }
47
+
48
+ /** Get a file's size in bytes, 0 when missing. */
49
+ function getFileSize(filePath: string): number {
50
+ return existsSync(filePath) ? statSync(filePath).size : 0
51
+ }
52
+
53
+ /** Describe one artifact for the files[] array, with an optional inline blockMapSize. */
54
+ function fileEntryYaml(url: string, sha512: string, size: number, blockMapSize?: number): string {
55
+ const blockMapEntry = blockMapSize != null ? `\n blockMapSize: ${blockMapSize}` : ''
56
+ return ` - url: ${url}\n sha512: ${sha512}\n size: ${size}${blockMapEntry}`
57
+ }
58
+
59
+ export function generateUpdateMetadata({
60
+ filePath,
61
+ version,
62
+ outputDir,
63
+ platform,
64
+ blockMapSize,
65
+ channel,
66
+ extraFiles = [],
67
+ }: UpdateMetadataOptions): { metadataPath: string; metadataFileName: string } | null {
68
+ if (!existsSync(filePath)) return null
69
+
70
+ const url = path.basename(filePath)
71
+ const primaryHash = hashFile(filePath, 'sha512', 'base64')
72
+ const primarySize = getFileSize(filePath)
73
+ const filesYaml = [
74
+ fileEntryYaml(url, primaryHash, primarySize, blockMapSize),
75
+ ...extraFiles
76
+ .filter((extra) => existsSync(extra) && extra !== filePath)
77
+ .map((extra) =>
78
+ fileEntryYaml(
79
+ path.basename(extra),
80
+ hashFile(extra, 'sha512', 'base64'),
81
+ getFileSize(extra),
82
+ ),
83
+ ),
84
+ ].join('\n')
85
+
86
+ const metadataFileName = getUpdateMetadataFileName(version, channel, platform)
87
+ const content = `version: ${version}
88
+ files:
89
+ ${filesYaml}
90
+ path: ${url}
91
+ sha512: ${primaryHash}
92
+ releaseDate: ${new Date().toISOString()}
93
+ `
94
+ const metadataPath = path.join(outputDir, metadataFileName)
95
+ writeFileSync(metadataPath, content, 'utf8')
96
+ return { metadataPath, metadataFileName }
97
+ }
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Convert a semver string into the four-part numeric build version.
3
+ * NSIS VIProductVersion and PE version resources require the X.X.X.X format.
4
+ * The fourth segment prefers: explicit buildNumber param > prerelease number
5
+ * in the version string > CI build number (only when CI=true).
6
+ */
7
+ export function getBuildVersion(version: string, buildNumber?: number | string): string {
8
+ const ciBuildNumber = getCiBuildNumber()
9
+ const semverMatch = version.match(/^(\d+)\.(\d+)\.(\d+)(?:-(?:beta|rc)\.(\d+))?/i)
10
+
11
+ if (semverMatch) {
12
+ const major = Number.parseInt(semverMatch[1], 10)
13
+ const minor = Number.parseInt(semverMatch[2], 10)
14
+ const patch = Number.parseInt(semverMatch[3], 10)
15
+
16
+ if (buildNumber != null && /^\d+$/.test(String(buildNumber))) {
17
+ return `${major}.${minor}.${patch}.${Number.parseInt(String(buildNumber), 10)}`
18
+ }
19
+
20
+ if (ciBuildNumber && /^\d+$/.test(ciBuildNumber)) {
21
+ return `${major}.${minor}.${patch}.${Number.parseInt(ciBuildNumber, 10)}`
22
+ }
23
+
24
+ const prereleaseBuild = semverMatch[4] ? Number.parseInt(semverMatch[4], 10) : 0
25
+ return `${major}.${minor}.${patch}.${prereleaseBuild}`
26
+ }
27
+
28
+ const parts = version.replace(/-/g, '.').split('.')
29
+ const major = Number.parseInt(parts[0], 10) || 0
30
+ const minor = Number.parseInt(parts[1], 10) || 0
31
+ const patch = Number.parseInt(parts[2], 10) || 0
32
+ const fallbackBuild =
33
+ buildNumber != null && /^\d+$/.test(String(buildNumber))
34
+ ? Number.parseInt(String(buildNumber), 10)
35
+ : Number.parseInt(parts[3], 10) || 0
36
+ const build =
37
+ ciBuildNumber && /^\d+$/.test(ciBuildNumber)
38
+ ? Number.parseInt(ciBuildNumber, 10)
39
+ : fallbackBuild
40
+
41
+ return `${major}.${minor}.${patch}.${build}`
42
+ }
43
+
44
+ /**
45
+ * Read the CI build number from well-known environment variables.
46
+ * Only consulted inside a CI runner (CI=true) so that a stray BUILD_NUMBER
47
+ * on a developer machine cannot silently alter artifact versions.
48
+ */
49
+ function getCiBuildNumber(): string | undefined {
50
+ if (process.env.CI !== 'true') return undefined
51
+ return (
52
+ process.env.CI_PIPELINE_IID ||
53
+ process.env.BUILD_BUILDNUMBER ||
54
+ process.env.CIRCLE_BUILD_NUM ||
55
+ process.env.APPVEYOR_BUILD_NUMBER ||
56
+ process.env.TRAVIS_BUILD_NUMBER ||
57
+ process.env.BUILD_NUMBER
58
+ )
59
+ }
@@ -0,0 +1,11 @@
1
+ [Desktop Entry]
2
+ Name={{productName}}
3
+ {{desktopExtra}}Comment={{description}}
4
+ Exec="/opt/{{installDir}}/{{name}}" %U
5
+ Icon={{name}}
6
+ Type=Application
7
+ Categories=Utility;
8
+ {{mimeType}}
9
+ StartupWMClass={{name}}
10
+ StartupNotify=true
11
+ Terminal=false
@@ -0,0 +1,9 @@
1
+ Package: {{name}}
2
+ Version: {{version}}
3
+ Architecture: {{debArch}}
4
+ Maintainer: {{maintainer}}
5
+ Depends: {{depends}}
6
+ {{recommends}}Section: utils
7
+ Priority: optional
8
+ Installed-Size: {{installedSize}}
9
+ Description: {{description}}
@@ -0,0 +1,10 @@
1
+ #!/bin/sh
2
+ chown root:root "/opt/{{installDir}}/chrome-sandbox"
3
+ chmod 4755 "/opt/{{installDir}}/chrome-sandbox"
4
+ chmod 755 "/opt/{{installDir}}/{{name}}"
5
+ if command -v gtk-update-icon-cache > /dev/null 2>&1; then
6
+ gtk-update-icon-cache -f -q /usr/share/icons/hicolor 2>/dev/null || true
7
+ fi
8
+ if command -v update-desktop-database > /dev/null 2>&1; then
9
+ update-desktop-database -q /usr/share/applications 2>/dev/null || true
10
+ fi
@@ -0,0 +1,8 @@
1
+ #!/bin/sh
2
+ rm -rf "/opt/{{installDir}}"
3
+ if command -v gtk-update-icon-cache > /dev/null 2>&1; then
4
+ gtk-update-icon-cache -f -q /usr/share/icons/hicolor 2>/dev/null || true
5
+ fi
6
+ if command -v update-desktop-database > /dev/null 2>&1; then
7
+ update-desktop-database -q /usr/share/applications 2>/dev/null || true
8
+ fi