herdr-remote 0.2.39 → 0.3.32

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 (205) hide show
  1. package/README.md +4 -2
  2. package/README.zh-CN.md +2 -2
  3. package/dist/chunks/3FQAXVGC.js +3 -0
  4. package/dist/chunks/42KARAL4.js +31 -0
  5. package/dist/chunks/44IGVPMA.js +5 -0
  6. package/dist/chunks/5LZFJ6UZ.js +3 -0
  7. package/dist/chunks/7SMZ43LI.js +9 -0
  8. package/dist/chunks/AX4EPJCP.js +4 -0
  9. package/dist/chunks/B76DFI4H.js +16 -0
  10. package/dist/chunks/BYGDPZZD.js +3 -0
  11. package/dist/chunks/C2UUZY6J.js +2 -0
  12. package/dist/chunks/CVJ4HBMP.js +3 -0
  13. package/dist/chunks/DGUZCGQN.js +3 -0
  14. package/dist/chunks/FONUO5O5.js +4 -0
  15. package/dist/chunks/H24ZP5D5.js +16 -0
  16. package/dist/chunks/I6NRCYLI.js +3 -0
  17. package/dist/chunks/JJGEV4EZ.js +3 -0
  18. package/dist/chunks/K5RFPHSF.js +5 -0
  19. package/dist/chunks/KGOQKOHH.js +5 -0
  20. package/dist/chunks/KRVF37OW.js +3 -0
  21. package/dist/chunks/KUDCC7ZO.js +6 -0
  22. package/dist/chunks/LPR662JH.js +77 -0
  23. package/dist/chunks/LY2ZGCXN.js +4 -0
  24. package/dist/chunks/M52LJEES.js +6 -0
  25. package/dist/chunks/MCUVFSSK.js +3 -0
  26. package/dist/chunks/MEFDV375.js +10 -0
  27. package/dist/chunks/MKAEXKXG.js +10 -0
  28. package/dist/chunks/O2LNVFUT.js +3 -0
  29. package/dist/chunks/PBMBFWLQ.js +3 -0
  30. package/dist/chunks/PBRQ3HEG.js +3 -0
  31. package/dist/chunks/QCSILMFB.js +3 -0
  32. package/dist/chunks/QRGNEKME.js +5 -0
  33. package/dist/chunks/RMHH3IAX.js +63 -0
  34. package/dist/chunks/TNLCNVA2.js +3 -0
  35. package/dist/chunks/U6QAYG3E.js +3 -0
  36. package/dist/chunks/VD54DF7R.js +45 -0
  37. package/dist/chunks/WY7WHEJ4.js +3 -0
  38. package/dist/cli.js +21 -294
  39. package/dist/connector/admin-broker.js +3 -303
  40. package/dist/connector/main.js +20 -18
  41. package/dist/harfbuzz-subset.wasm +0 -0
  42. package/dist/postinstall.js +17 -0
  43. package/herdr-plugin.toml +1 -1
  44. package/node_modules/herdr-remote-relay/bin/herdr-remote-relay.js +33 -92
  45. package/node_modules/herdr-remote-relay/package.json +3 -46
  46. package/package.json +8 -27
  47. package/dist/agent-status.js +0 -130
  48. package/dist/binary-plist.js +0 -157
  49. package/dist/config.js +0 -278
  50. package/dist/connector/admin-broker-setup.js +0 -40
  51. package/dist/connector/admin-shell.js +0 -67
  52. package/dist/connector/admin-tab.js +0 -90
  53. package/dist/connector/adminBrokerEndpoint.js +0 -68
  54. package/dist/connector/agent-watch.js +0 -123
  55. package/dist/connector/elevatedPty.js +0 -141
  56. package/dist/connector/font-server.js +0 -165
  57. package/dist/connector/herdr-prefix-keys.js +0 -28
  58. package/dist/connector/host-connector.js +0 -433
  59. package/dist/connector/host-hello.js +0 -32
  60. package/dist/connector/lock.js +0 -73
  61. package/dist/connector/sessions.js +0 -381
  62. package/dist/connector/update-report.js +0 -70
  63. package/dist/connector/wire.js +0 -42
  64. package/dist/exit-codes.js +0 -13
  65. package/dist/font-subset.js +0 -116
  66. package/dist/herdr-api.js +0 -254
  67. package/dist/herdr-command.js +0 -245
  68. package/dist/herdr-plugin.js +0 -120
  69. package/dist/herdr-server.js +0 -190
  70. package/dist/i18n/en.js +0 -257
  71. package/dist/i18n/index.js +0 -48
  72. package/dist/i18n/zh.js +0 -256
  73. package/dist/keepalive/detached.js +0 -97
  74. package/dist/keepalive/environment.js +0 -44
  75. package/dist/keepalive/index.js +0 -129
  76. package/dist/keepalive/launchd.js +0 -117
  77. package/dist/keepalive/systemd.js +0 -112
  78. package/dist/keepalive/types.js +0 -1
  79. package/dist/keepalive/windows-service-setup.js +0 -138
  80. package/dist/keepalive/windows-service.js +0 -272
  81. package/dist/keepalive/windows-task.js +0 -191
  82. package/dist/lib/process.js +0 -132
  83. package/dist/lifecycle.js +0 -79
  84. package/dist/net-interfaces.js +0 -82
  85. package/dist/node-pty-loader.js +0 -80
  86. package/dist/pasted-files.js +0 -115
  87. package/dist/paths.js +0 -74
  88. package/dist/pty-session.js +0 -133
  89. package/dist/relay-client.js +0 -106
  90. package/dist/relay-urls.js +0 -99
  91. package/dist/runtime.js +0 -107
  92. package/dist/service.js +0 -267
  93. package/dist/settings-model.js +0 -298
  94. package/dist/socket-discovery.js +0 -70
  95. package/dist/supervisor.js +0 -323
  96. package/dist/terminal-font/deps.js +0 -83
  97. package/dist/terminal-font/files.js +0 -357
  98. package/dist/terminal-font/formats.js +0 -355
  99. package/dist/terminal-font/index.js +0 -187
  100. package/dist/terminal-font/readers.js +0 -373
  101. package/dist/terminal-font/terminals.js +0 -147
  102. package/dist/terminal-palette.js +0 -399
  103. package/dist/tui/App.js +0 -199
  104. package/dist/tui/api.js +0 -91
  105. package/dist/tui/components/TextField.js +0 -50
  106. package/dist/tui/components/common.js +0 -80
  107. package/dist/tui/index.js +0 -36
  108. package/dist/tui/mouse/index.js +0 -164
  109. package/dist/tui/mouse/protocol.js +0 -96
  110. package/dist/tui/mouse/source.js +0 -79
  111. package/dist/tui/screens/About.js +0 -172
  112. package/dist/tui/screens/Herdr.js +0 -75
  113. package/dist/tui/screens/Keepalive.js +0 -141
  114. package/dist/tui/screens/Overview.js +0 -36
  115. package/dist/tui/screens/Pair.js +0 -74
  116. package/dist/tui/screens/Relay.js +0 -243
  117. package/dist/tui/screens/Services.js +0 -63
  118. package/dist/tui/screens/Wizard.js +0 -241
  119. package/dist/tui/terminal.js +0 -39
  120. package/dist/tui/theme.js +0 -33
  121. package/dist/update-command.js +0 -118
  122. package/dist/updater/in-place.js +0 -83
  123. package/dist/updater/npm.js +0 -62
  124. package/dist/updater.js +0 -401
  125. package/node_modules/herdr-remote-relay/README.md +0 -76
  126. package/node_modules/herdr-remote-relay/README.zh-CN.md +0 -76
  127. package/node_modules/herdr-remote-relay/config.example.json +0 -18
  128. package/node_modules/herdr-remote-relay/deploy/cloudflared/config.yml.example +0 -9
  129. package/node_modules/herdr-remote-relay/deploy/docker/compose.yaml +0 -23
  130. package/node_modules/herdr-remote-relay/deploy/nginx/herdr-remote-relay.conf.example +0 -49
  131. package/node_modules/herdr-remote-relay/deploy/systemd/herdr-remote-relay.service +0 -41
  132. package/node_modules/herdr-remote-relay/deploy/systemd/relay.env.example +0 -25
  133. package/node_modules/herdr-remote-relay/dist/auth-store.d.ts +0 -114
  134. package/node_modules/herdr-remote-relay/dist/auth-store.js +0 -289
  135. package/node_modules/herdr-remote-relay/dist/metrics.d.ts +0 -76
  136. package/node_modules/herdr-remote-relay/dist/metrics.js +0 -249
  137. package/node_modules/herdr-remote-relay/dist/protocol/frames.d.ts +0 -27
  138. package/node_modules/herdr-remote-relay/dist/protocol/frames.js +0 -110
  139. package/node_modules/herdr-remote-relay/dist/protocol/herdr-keys.d.ts +0 -8
  140. package/node_modules/herdr-remote-relay/dist/protocol/herdr-keys.js +0 -34
  141. package/node_modules/herdr-remote-relay/dist/protocol/http.d.ts +0 -165
  142. package/node_modules/herdr-remote-relay/dist/protocol/http.js +0 -3
  143. package/node_modules/herdr-remote-relay/dist/protocol/index.d.ts +0 -7
  144. package/node_modules/herdr-remote-relay/dist/protocol/index.js +0 -9
  145. package/node_modules/herdr-remote-relay/dist/protocol/messages.d.ts +0 -362
  146. package/node_modules/herdr-remote-relay/dist/protocol/messages.js +0 -20
  147. package/node_modules/herdr-remote-relay/dist/protocol/paste.d.ts +0 -14
  148. package/node_modules/herdr-remote-relay/dist/protocol/paste.js +0 -50
  149. package/node_modules/herdr-remote-relay/dist/protocol/release.d.ts +0 -8
  150. package/node_modules/herdr-remote-relay/dist/protocol/release.js +0 -17
  151. package/node_modules/herdr-remote-relay/dist/protocol/terminal.d.ts +0 -79
  152. package/node_modules/herdr-remote-relay/dist/protocol/terminal.js +0 -150
  153. package/node_modules/herdr-remote-relay/dist/relay-config.d.ts +0 -74
  154. package/node_modules/herdr-remote-relay/dist/relay-config.js +0 -316
  155. package/node_modules/herdr-remote-relay/dist/relay-server.d.ts +0 -67
  156. package/node_modules/herdr-remote-relay/dist/relay-server.js +0 -196
  157. package/node_modules/herdr-remote-relay/dist/scroll-input.d.ts +0 -17
  158. package/node_modules/herdr-remote-relay/dist/scroll-input.js +0 -124
  159. package/node_modules/herdr-remote-relay/dist/server/client-channel.d.ts +0 -3
  160. package/node_modules/herdr-remote-relay/dist/server/client-channel.js +0 -396
  161. package/node_modules/herdr-remote-relay/dist/server/font-proxy.d.ts +0 -19
  162. package/node_modules/herdr-remote-relay/dist/server/font-proxy.js +0 -155
  163. package/node_modules/herdr-remote-relay/dist/server/host-broadcasts.d.ts +0 -27
  164. package/node_modules/herdr-remote-relay/dist/server/host-broadcasts.js +0 -83
  165. package/node_modules/herdr-remote-relay/dist/server/host-channel.d.ts +0 -3
  166. package/node_modules/herdr-remote-relay/dist/server/host-channel.js +0 -271
  167. package/node_modules/herdr-remote-relay/dist/server/http-api.d.ts +0 -4
  168. package/node_modules/herdr-remote-relay/dist/server/http-api.js +0 -219
  169. package/node_modules/herdr-remote-relay/dist/server/maintenance.d.ts +0 -5
  170. package/node_modules/herdr-remote-relay/dist/server/maintenance.js +0 -64
  171. package/node_modules/herdr-remote-relay/dist/server/requests.d.ts +0 -34
  172. package/node_modules/herdr-remote-relay/dist/server/requests.js +0 -142
  173. package/node_modules/herdr-remote-relay/dist/server/sessions.d.ts +0 -36
  174. package/node_modules/herdr-remote-relay/dist/server/sessions.js +0 -230
  175. package/node_modules/herdr-remote-relay/dist/server/sockets.d.ts +0 -12
  176. package/node_modules/herdr-remote-relay/dist/server/sockets.js +0 -64
  177. package/node_modules/herdr-remote-relay/dist/server/status.d.ts +0 -11
  178. package/node_modules/herdr-remote-relay/dist/server/status.js +0 -99
  179. package/node_modules/herdr-remote-relay/dist/server/transport.d.ts +0 -16
  180. package/node_modules/herdr-remote-relay/dist/server/transport.js +0 -110
  181. package/node_modules/herdr-remote-relay/dist/server/types.d.ts +0 -133
  182. package/node_modules/herdr-remote-relay/dist/server/types.js +0 -3
  183. package/node_modules/herdr-remote-relay/dist/server/version.d.ts +0 -2
  184. package/node_modules/herdr-remote-relay/dist/server/version.js +0 -5
  185. package/node_modules/herdr-remote-relay/dist/state.d.ts +0 -6
  186. package/node_modules/herdr-remote-relay/dist/state.js +0 -47
  187. package/node_modules/ws/LICENSE +0 -20
  188. package/node_modules/ws/README.md +0 -548
  189. package/node_modules/ws/browser.js +0 -8
  190. package/node_modules/ws/index.js +0 -22
  191. package/node_modules/ws/lib/buffer-util.js +0 -131
  192. package/node_modules/ws/lib/constants.js +0 -19
  193. package/node_modules/ws/lib/event-target.js +0 -292
  194. package/node_modules/ws/lib/extension.js +0 -203
  195. package/node_modules/ws/lib/limiter.js +0 -55
  196. package/node_modules/ws/lib/permessage-deflate.js +0 -530
  197. package/node_modules/ws/lib/receiver.js +0 -743
  198. package/node_modules/ws/lib/sender.js +0 -607
  199. package/node_modules/ws/lib/stream.js +0 -161
  200. package/node_modules/ws/lib/subprotocol.js +0 -62
  201. package/node_modules/ws/lib/validation.js +0 -152
  202. package/node_modules/ws/lib/websocket-server.js +0 -562
  203. package/node_modules/ws/lib/websocket.js +0 -1407
  204. package/node_modules/ws/package.json +0 -74
  205. package/node_modules/ws/wrapper.mjs +0 -21
@@ -1,357 +0,0 @@
1
- // The files behind a font family: found with fontconfig (or, on a stock
2
- // macOS, by reading the fonts' own name tables) and described by hash.
3
- import crypto from 'node:crypto';
4
- import fs from 'node:fs';
5
- import path from 'node:path';
6
- import { MAX_TERMINAL_FONT_BYTES, TERMINAL_FONT_STYLES, } from 'herdr-remote-relay/protocol';
7
- import { GENERIC_FAMILIES, localAppData, withDefaults } from './deps.js';
8
- const FACE_PATTERNS = {
9
- regular: ':weight=regular:slant=roman',
10
- bold: ':weight=bold:slant=roman',
11
- italic: ':weight=regular:slant=italic',
12
- boldItalic: ':weight=bold:slant=italic',
13
- };
14
- function escapeFontconfig(value) {
15
- return value.replace(/([\\\-:,])/g, '\\$1');
16
- }
17
- /** `00 01 00 00` / `true` are TrueType, `OTTO` is CFF OpenType. */
18
- function sfntFormat(header) {
19
- if (!header || header.length < 4)
20
- return null;
21
- const tag = header.subarray(0, 4).toString('latin1');
22
- if (tag === '\x00\x01\x00\x00' || tag === 'true')
23
- return 'truetype';
24
- if (tag === 'OTTO')
25
- return 'opentype';
26
- return null;
27
- }
28
- function readHeader(filePath, length) {
29
- let fd;
30
- try {
31
- fd = fs.openSync(filePath, 'r');
32
- const buffer = Buffer.alloc(length);
33
- const read = fs.readSync(fd, buffer, 0, length, 0);
34
- return buffer.subarray(0, read);
35
- }
36
- catch {
37
- return null;
38
- }
39
- finally {
40
- if (fd !== undefined)
41
- try {
42
- fs.closeSync(fd);
43
- }
44
- catch {
45
- // Closing a read-only descriptor cannot lose data.
46
- }
47
- }
48
- }
49
- /**
50
- * The family, subfamily and PostScript names in a TTF/OTF `name` table. Used
51
- * where fontconfig is absent — a stock macOS — to find a family's files.
52
- */
53
- function readSfntNames(filePath) {
54
- let fd;
55
- try {
56
- fd = fs.openSync(filePath, 'r');
57
- const head = Buffer.alloc(12);
58
- fs.readSync(fd, head, 0, 12, 0);
59
- if (!sfntFormat(head))
60
- return null;
61
- const tables = head.readUInt16BE(4);
62
- const directory = Buffer.alloc(tables * 16);
63
- fs.readSync(fd, directory, 0, directory.length, 12);
64
- let offset = -1;
65
- let length = 0;
66
- for (let i = 0; i < tables; i += 1) {
67
- if (directory.toString('latin1', i * 16, i * 16 + 4) === 'name') {
68
- offset = directory.readUInt32BE(i * 16 + 8);
69
- length = directory.readUInt32BE(i * 16 + 12);
70
- }
71
- }
72
- if (offset < 0 || length > 1024 * 1024)
73
- return null;
74
- const table = Buffer.alloc(length);
75
- fs.readSync(fd, table, 0, length, offset);
76
- const count = table.readUInt16BE(2);
77
- const strings = table.readUInt16BE(4);
78
- const names = {};
79
- for (let i = 0; i < count; i += 1) {
80
- const record = 6 + i * 12;
81
- const platformId = table.readUInt16BE(record);
82
- const languageId = table.readUInt16BE(record + 4);
83
- const nameId = table.readUInt16BE(record + 6);
84
- const size = table.readUInt16BE(record + 8);
85
- const start = strings + table.readUInt16BE(record + 10);
86
- if (![1, 2, 6, 16, 17].includes(nameId) || start + size > table.length)
87
- continue;
88
- let text;
89
- if ((platformId === 3 || platformId === 0) && size % 2 === 0) {
90
- if (platformId === 3 && languageId !== 0x409 && names[nameId])
91
- continue;
92
- const utf16 = Buffer.from(table.subarray(start, start + size));
93
- text = utf16.swap16().toString('utf16le');
94
- }
95
- else if (platformId === 1 && !names[nameId]) {
96
- text = table.toString('latin1', start, start + size);
97
- }
98
- if (text)
99
- names[nameId] = text;
100
- }
101
- return {
102
- family: names[16] || names[1] || null,
103
- subfamily: names[17] || names[2] || '',
104
- postscript: names[6] || null,
105
- };
106
- }
107
- catch {
108
- return null;
109
- }
110
- finally {
111
- if (fd !== undefined)
112
- try {
113
- fs.closeSync(fd);
114
- }
115
- catch {
116
- // Closing a read-only descriptor cannot lose data.
117
- }
118
- }
119
- }
120
- function fontFolderFiles(deps) {
121
- const roots = deps.platform === 'darwin'
122
- ? [path.join(deps.home, 'Library', 'Fonts'), '/Library/Fonts', '/System/Library/Fonts']
123
- : deps.platform === 'win32'
124
- ? [
125
- path.join(deps.env.WINDIR || deps.env.SystemRoot || 'C:\\Windows', 'Fonts'),
126
- path.join(localAppData(deps), 'Microsoft', 'Windows', 'Fonts'),
127
- ]
128
- : [];
129
- const files = [];
130
- const walk = (directory, depth) => {
131
- let entries;
132
- try {
133
- entries = fs.readdirSync(directory, { withFileTypes: true });
134
- }
135
- catch {
136
- return;
137
- }
138
- for (const entry of entries) {
139
- const full = path.join(directory, entry.name);
140
- if (entry.isDirectory() && depth < 2)
141
- walk(full, depth + 1);
142
- else if (/\.(ttf|otf)$/i.test(entry.name))
143
- files.push(full);
144
- }
145
- };
146
- for (const root of roots)
147
- walk(root, 0);
148
- return files;
149
- }
150
- /** Apple's own terminal faces, which live outside the font folders. */
151
- const APPLE_POSTSCRIPT_FAMILIES = [
152
- [/^\.?SF(NS)?Mono/, 'SF Mono'],
153
- [/^Menlo-/, 'Menlo'],
154
- [/^Monaco$/, 'Monaco'],
155
- [/^Courier(New)?/, 'Courier New'],
156
- [/^AndaleMono$/, 'Andale Mono'],
157
- ];
158
- export function familyForPostScriptName(postscript, deps) {
159
- const apple = APPLE_POSTSCRIPT_FAMILIES.find(([pattern]) => pattern.test(postscript));
160
- if (apple && deps.platform === 'darwin')
161
- return apple[1];
162
- const listed = deps.run('fc-list', [`:postscriptname=${escapeFontconfig(postscript)}`, 'family']);
163
- if (listed.status === 0 && listed.stdout.trim())
164
- return listed.stdout.trim().split('\n')[0].split(',')[0].trim();
165
- if (deps.platform !== 'darwin')
166
- return null;
167
- for (const file of fontFolderFiles(deps)) {
168
- const names = readSfntNames(file);
169
- if (names?.postscript === postscript)
170
- return names.family;
171
- }
172
- return null;
173
- }
174
- function styleOfSubfamily(subfamily) {
175
- const text = String(subfamily || '').toLowerCase();
176
- const bold = /\bbold\b/.test(text) && !/(semi|demi|extra|ultra)\s*bold/.test(text);
177
- const italic = /italic|oblique/.test(text);
178
- if (bold && italic)
179
- return 'boldItalic';
180
- if (bold)
181
- return 'bold';
182
- if (italic)
183
- return 'italic';
184
- return /^(regular|normal|book|roman)$/.test(text.trim()) ? 'regular' : null;
185
- }
186
- /** Candidate files per style, from fontconfig or, failing that, a scan. */
187
- function locateFaceFiles(family, deps) {
188
- const located = {};
189
- let fontconfig = false;
190
- for (const style of TERMINAL_FONT_STYLES) {
191
- const result = deps.run('fc-match', [
192
- '-f',
193
- '%{file}\n%{family}\n%{index}\n',
194
- `${escapeFontconfig(family)}${FACE_PATTERNS[style]}`,
195
- ]);
196
- if (result.status !== 0)
197
- continue;
198
- fontconfig = true;
199
- const [file, families = '', index = '0'] = result.stdout.split('\n');
200
- // fontconfig always answers; an answer in another family means the
201
- // requested one is not installed. A collection member is not a file a
202
- // browser can load on its own.
203
- const names = families.split(',').map((name) => name.trim().toLowerCase());
204
- if (!file || !names.includes(family.toLowerCase()) || Number(index) !== 0)
205
- continue;
206
- located[style] = file;
207
- }
208
- if (fontconfig || (deps.platform !== 'darwin' && deps.platform !== 'win32'))
209
- return located;
210
- for (const file of fontFolderFiles(deps)) {
211
- const names = readSfntNames(file);
212
- if (!names || names.family?.toLowerCase() !== family.toLowerCase())
213
- continue;
214
- const style = styleOfSubfamily(names.subfamily);
215
- if (style && !located[style])
216
- located[style] = file;
217
- }
218
- return located;
219
- }
220
- function hashFile(filePath) {
221
- return crypto.createHash('sha256').update(fs.readFileSync(filePath)).digest('hex');
222
- }
223
- /**
224
- * The files behind a family, ready to be offered to a browser.
225
- *
226
- * A style fontconfig can only satisfy with the regular file is left out: the
227
- * browser synthesises bold and italic from the regular face just as well, and
228
- * sending the same file twice would only cost the phone the bytes.
229
- */
230
- export function resolveFontFaces(family, deps = {}) {
231
- const resolved = withDefaults(deps);
232
- if (!family || GENERIC_FAMILIES.has(family.toLowerCase()))
233
- return [];
234
- const located = locateFaceFiles(family, resolved);
235
- const faces = [];
236
- const usedFiles = new Set();
237
- for (const style of TERMINAL_FONT_STYLES) {
238
- const file = located[style];
239
- if (!file || usedFiles.has(file))
240
- continue;
241
- // Apple's system fonts (SF Mono, Menlo…) are licensed for Apple devices
242
- // only; offering them to any browser would redistribute them. An Apple
243
- // device has them already, and anything else falls back by name.
244
- if (/^\/System\//.test(file) || /\.app\/Contents\//.test(file))
245
- continue;
246
- if (style !== 'regular' && !faces.length)
247
- continue;
248
- let stat;
249
- try {
250
- stat = fs.statSync(file);
251
- }
252
- catch {
253
- continue;
254
- }
255
- if (!stat.isFile() || stat.size <= 0 || stat.size > MAX_TERMINAL_FONT_BYTES)
256
- continue;
257
- const format = sfntFormat(readHeader(file, 4));
258
- if (!format)
259
- continue;
260
- usedFiles.add(file);
261
- faces.push({
262
- style,
263
- format,
264
- bytes: stat.size,
265
- sha256: hashFile(file),
266
- path: file,
267
- mtimeMs: stat.mtimeMs,
268
- });
269
- }
270
- return faces;
271
- }
272
- /** A file a subset can be cut from; far larger than a face sent whole. */
273
- const MAX_SUBSET_SOURCE_BYTES = 64 * 1024 * 1024;
274
- /** Where fontconfig finds `pattern`: the file, the face's names, its index. */
275
- function locateFont(pattern, deps) {
276
- const result = deps.run('fc-match', ['-f', '%{file}\n%{family}\n%{index}\n', pattern]);
277
- if (result.status !== 0)
278
- return null;
279
- const [file, families = '', index = '0'] = result.stdout.split('\n');
280
- if (!file)
281
- return null;
282
- return {
283
- file,
284
- families: families
285
- .split(',')
286
- .map((name) => name.trim())
287
- .filter(Boolean),
288
- index: Number(index) || 0,
289
- };
290
- }
291
- /**
292
- * Fonts the browser receives a few characters at a time instead of whole:
293
- *
294
- * - `cjk`: the face fontconfig falls back to for Chinese under this family —
295
- * what the workstation's terminal actually draws Hanzi with. 16–20 MB, and
296
- * often inside a collection (.ttc) a browser cannot load anyway.
297
- * - `all`: the family itself, when it is too large or in a collection to be
298
- * sent as a whole file (a CJK programming font like Sarasa Mono).
299
- *
300
- * Only regular weights: the browser emboldens them for bold text, which is
301
- * how a missing bold is drawn everywhere else too.
302
- */
303
- export function resolveSubsetSources(family, faces, deps = {}) {
304
- const resolved = withDefaults(deps);
305
- if (!family || GENERIC_FAMILIES.has(family.toLowerCase()))
306
- return [];
307
- const wanted = family.toLowerCase();
308
- const sources = [];
309
- const add = (located, scope, name) => {
310
- if (!located || /^\/System\//.test(located.file) || /\.app\/Contents\//.test(located.file))
311
- return;
312
- let stat;
313
- try {
314
- stat = fs.statSync(located.file);
315
- }
316
- catch {
317
- return;
318
- }
319
- if (!stat.isFile() || stat.size > MAX_SUBSET_SOURCE_BYTES)
320
- return;
321
- const header = readHeader(located.file, 4);
322
- if (!sfntFormat(header) && header?.toString('latin1') !== 'ttcf')
323
- return;
324
- // Identifies the face without reading 20 MB: a browser caches by it.
325
- const sha256 = crypto
326
- .createHash('sha256')
327
- .update(`${located.file}\0${located.index}\0${stat.size}\0${stat.mtimeMs}`)
328
- .digest('hex');
329
- sources.push({
330
- family: name,
331
- style: 'regular',
332
- scope,
333
- sha256,
334
- path: located.file,
335
- index: located.index,
336
- bytes: stat.size,
337
- mtimeMs: stat.mtimeMs,
338
- });
339
- };
340
- const self = locateFont(`${escapeFontconfig(family)}${FACE_PATTERNS.regular}`, resolved);
341
- const selfInstalled = self?.families.some((name) => name.toLowerCase() === wanted);
342
- if (selfInstalled && !faces.some((face) => face.style === 'regular'))
343
- add(self, 'all', family);
344
- const cjk = locateFont(`${escapeFontconfig(family)}:charset=4e00${FACE_PATTERNS.regular}`, resolved);
345
- const coveredBySelf = cjk?.families.some((name) => name.toLowerCase() === wanted);
346
- if (cjk && !coveredBySelf && cjk.families[0])
347
- add(cjk, 'cjk', cjk.families[0]);
348
- return sources;
349
- }
350
- /** A generic family is replaced by the real face fontconfig picks for it. */
351
- export function concreteFamily(family, deps) {
352
- if (!GENERIC_FAMILIES.has(String(family).toLowerCase()))
353
- return family;
354
- const result = deps.run('fc-match', ['-f', '%{family[0]}', family]);
355
- const matched = result.status === 0 ? result.stdout.trim() : '';
356
- return matched || null;
357
- }