@volter/twin-googlefonts 1.0.2 → 1.0.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.
@@ -7,8 +7,61 @@ export const assetKey = (value: string): string => {
7
7
  if (url.hostname !== 'fonts.gstatic.com' || url.username || url.password || !['http:', 'https:'].includes(url.protocol)) throw new Error('Invalid font URL');
8
8
  url.searchParams.sort(); return url.pathname + url.search;
9
9
  };
10
+ // THE BROWSER CLASS. Google answers a stylesheet to any browser, choosing the representation by the browser its
11
+ // User-Agent names: WOFF 2.0 only to a browser that supports it, unicode-range subsets only to one that supports them.
12
+ // source: https://developers.googleblog.com/2015/02/smaller-fonts-with-woff-20-and-unicode.html "serve WOFF 2.0 only to supporting browsers (what we do at Google Fonts)"
13
+ // source: https://developers.googleblog.com/2015/02/smaller-fonts-with-woff-20-and-unicode.html "you should serve unicode-range property only to browsers that support it fully"
14
+ // source: https://developers.google.com/fonts/docs/getting_started "the browser will select from the subsets supported by the font to get what it needs to render the text"
15
+ // google-webfonts-helper picks one agent per format, which is the sample the pack retains for each of those classes.
16
+ // source: https://raw.githubusercontent.com/majodev/google-webfonts-helper/ff245dc87cfcbaeacd4120cf1b4a7443b59db42f/server/config.ts "must serve complete woff2 file for one variant (no unicode range support yet!)"
17
+ // Where the documentation stops: Google publishes no rule from an agent to its representation. The classes below are
18
+ // the answers Google gave on 2026-11-13 to the agents vendor/SOURCE.md lists (GET /css2?family=Inter:wght@500, each
19
+ // answer's SHA-256 compared): agents in one class were answered the same bytes. Google also builds the files per
20
+ // platform (macOS, mobile, and every other desktop were answered different faces), so a class names its platform where
21
+ // Google's answer did. A legacy agent whose answer the probes did not find shared with others (an old Safari, iOS 5 to
22
+ // 10, an old Chrome or Firefox on macOS or Android) is a class of its own exact engine version: it reads only a
23
+ // representation published for that version, never another browser's.
24
+ type Platform = 'desktop' | 'mac' | 'mobile';
25
+ const major = (userAgent: string, pattern: RegExp): number => Number(pattern.exec(userAgent)?.[1] ?? Number.NaN);
26
+ export const browserClass = (userAgent: string): string => {
27
+ const ua = userAgent;
28
+ const platform: Platform = /\b(?:Android|iPhone|iPad|iPod)\b/.test(ua) ? 'mobile' : /\bMacintosh\b/.test(ua) ? 'mac' : 'desktop';
29
+ // an agent that is no browser (none, node, undici, curl) is answered truetype, as an unknown browser on a desktop is
30
+ if (!ua.startsWith('Mozilla/')) return 'truetype';
31
+ const legacy = (engine: string, version: string): string => `legacy ${engine} ${version} ${platform}`;
32
+ const msie = major(ua, /\bMSIE (\d+)/);
33
+ if (msie <= 8) return 'eot';
34
+ if (msie >= 9 || /\bTrident\/7\./.test(ua)) return 'woff desktop';
35
+ // every browser on iOS is Safari's engine, whatever it calls itself (CriOS, FxiOS)
36
+ const ios = /\bOS (\d+)_(\d+)(?:_\d+)? like Mac OS X\b/.exec(ua);
37
+ if (ios) {
38
+ const [v, minor] = [Number(ios[1]), Number(ios[2])];
39
+ if (v <= 4) return 'svg';
40
+ return v >= 11 || (v === 10 && minor >= 3) ? 'woff2+range mobile' : legacy('ios', `${v}_${minor}`);
41
+ }
42
+ const chrome = /\b(?:HeadlessChrome|Chrome|Chromium|CriOS)\/((\d+)[\d.]*)/.exec(ua);
43
+ if (chrome) {
44
+ const v = Number(chrome[2]);
45
+ if (v >= 43) return `woff2+range ${platform}`;
46
+ if (platform !== 'desktop') return legacy('chrome', chrome[1]!);
47
+ return v >= 40 ? 'woff+range desktop' : 'woff desktop';
48
+ }
49
+ const firefox = /\bFirefox\/((\d+)[\d.]*)/.exec(ua);
50
+ if (firefox) {
51
+ const v = Number(firefox[2]);
52
+ // on macOS Google answers Firefox 44 to 59 the mobile faces, and Firefox 60 and later WOFF with unicode-range
53
+ if (platform === 'mac') return v >= 60 ? 'woff+range mac' : v >= 44 ? 'woff2+range mobile' : legacy('firefox', firefox[1]!);
54
+ if (v >= 44) return `woff2+range ${platform}`;
55
+ if (platform !== 'desktop') return legacy('firefox', firefox[1]!);
56
+ return v >= 39 ? 'woff2 desktop' : 'woff desktop';
57
+ }
58
+ // Safari on macOS names its version; Android's own browser names one too, and is answered truetype
59
+ const safari = /\bVersion\/((\d+)[\d.]*)(?: Mobile)?(?:\/\S+)? Safari\//.exec(ua);
60
+ if (safari && platform === 'mac') return Number(safari[2]) >= 10 ? 'woff+range mac' : legacy('safari', safari[1]!);
61
+ return platform === 'mobile' ? 'truetype mobile' : 'truetype';
62
+ };
10
63
  export const cssKey = (ctx: HandlerContext, family: string, subset: string, userAgent: string): string =>
11
- ctx.crypto.digest('sha256', JSON.stringify([family, subset, userAgent]));
64
+ ctx.crypto.digest('sha256', JSON.stringify([family, subset, browserClass(userAgent)]));
12
65
  // A CSS2 request is its ordered query pairs; family may repeat, text and display may not.
13
66
  export const css2Query = (query: URLSearchParams): Array<[string, string]> | undefined => {
14
67
  const pairs = [...query.entries()];
@@ -18,7 +71,7 @@ export const css2Query = (query: URLSearchParams): Array<[string, string]> | und
18
71
  return pairs;
19
72
  };
20
73
  export const css2Key = (ctx: HandlerContext, pairs: Array<[string, string]>, userAgent: string): string =>
21
- ctx.crypto.digest('sha256', JSON.stringify(['css2', pairs, userAgent]));
74
+ ctx.crypto.digest('sha256', JSON.stringify(['css2', pairs, browserClass(userAgent)]));
22
75
  export const googleError = (status: number, message: string, kind = status === 403 ? 'PERMISSION_DENIED' : 'INVALID_ARGUMENT'): Response =>
23
76
  Response.json({ error: { code: status, message, status: kind } }, { status });
24
77
  export const htmlError = (status: number, message: string): Response =>
@@ -0,0 +1,93 @@
1
+ Copyright 2020 The DM Mono Project Authors (https://www.github.com/googlefonts/dm-mono)
2
+
3
+ This Font Software is licensed under the SIL Open Font License, Version 1.1.
4
+ This license is copied below, and is also available with a FAQ at:
5
+ http://scripts.sil.org/OFL
6
+
7
+
8
+ -----------------------------------------------------------
9
+ SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
10
+ -----------------------------------------------------------
11
+
12
+ PREAMBLE
13
+ The goals of the Open Font License (OFL) are to stimulate worldwide
14
+ development of collaborative font projects, to support the font creation
15
+ efforts of academic and linguistic communities, and to provide a free and
16
+ open framework in which fonts may be shared and improved in partnership
17
+ with others.
18
+
19
+ The OFL allows the licensed fonts to be used, studied, modified and
20
+ redistributed freely as long as they are not sold by themselves. The
21
+ fonts, including any derivative works, can be bundled, embedded,
22
+ redistributed and/or sold with any software provided that any reserved
23
+ names are not used by derivative works. The fonts and derivatives,
24
+ however, cannot be released under any other type of license. The
25
+ requirement for fonts to remain under this license does not apply
26
+ to any document created using the fonts or their derivatives.
27
+
28
+ DEFINITIONS
29
+ "Font Software" refers to the set of files released by the Copyright
30
+ Holder(s) under this license and clearly marked as such. This may
31
+ include source files, build scripts and documentation.
32
+
33
+ "Reserved Font Name" refers to any names specified as such after the
34
+ copyright statement(s).
35
+
36
+ "Original Version" refers to the collection of Font Software components as
37
+ distributed by the Copyright Holder(s).
38
+
39
+ "Modified Version" refers to any derivative made by adding to, deleting,
40
+ or substituting -- in part or in whole -- any of the components of the
41
+ Original Version, by changing formats or by porting the Font Software to a
42
+ new environment.
43
+
44
+ "Author" refers to any designer, engineer, programmer, technical
45
+ writer or other person who contributed to the Font Software.
46
+
47
+ PERMISSION & CONDITIONS
48
+ Permission is hereby granted, free of charge, to any person obtaining
49
+ a copy of the Font Software, to use, study, copy, merge, embed, modify,
50
+ redistribute, and sell modified and unmodified copies of the Font
51
+ Software, subject to the following conditions:
52
+
53
+ 1) Neither the Font Software nor any of its individual components,
54
+ in Original or Modified Versions, may be sold by itself.
55
+
56
+ 2) Original or Modified Versions of the Font Software may be bundled,
57
+ redistributed and/or sold with any software, provided that each copy
58
+ contains the above copyright notice and this license. These can be
59
+ included either as stand-alone text files, human-readable headers or
60
+ in the appropriate machine-readable metadata fields within text or
61
+ binary files as long as those fields can be easily viewed by the user.
62
+
63
+ 3) No Modified Version of the Font Software may use the Reserved Font
64
+ Name(s) unless explicit written permission is granted by the corresponding
65
+ Copyright Holder. This restriction only applies to the primary font name as
66
+ presented to the users.
67
+
68
+ 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
69
+ Software shall not be used to promote, endorse or advertise any
70
+ Modified Version, except to acknowledge the contribution(s) of the
71
+ Copyright Holder(s) and the Author(s) or with their explicit written
72
+ permission.
73
+
74
+ 5) The Font Software, modified or unmodified, in part or in whole,
75
+ must be distributed entirely under this license, and must not be
76
+ distributed under any other license. The requirement for fonts to
77
+ remain under this license does not apply to any document created
78
+ using the Font Software.
79
+
80
+ TERMINATION
81
+ This license becomes null and void if any of the above conditions are
82
+ not met.
83
+
84
+ DISCLAIMER
85
+ THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
86
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
87
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
88
+ OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
89
+ COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
90
+ INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
91
+ DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
92
+ FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
93
+ OTHER DEALINGS IN THE FONT SOFTWARE.
package/vendor/SOURCE.md CHANGED
@@ -34,6 +34,27 @@ per unicode-range subset. SHA-256: the CSS 9b6ad588c451f646d506bc6badb76ecc38469
34
34
  the faces, in the CSS's order, fccca918…21b5, aebf2ab4…4bd6, a2e2c783…c271, 46dd4cdc…22bf, 8db00ff4…1931,
35
35
  a28eb6d3…aaa2, c9407645…c3e4. Inter's font software is under the SIL Open Font License 1.1 (Inter-OFL.txt).
36
36
 
37
+ The Chromium data (vendor/inter-dm-mono-chromium.json) is Google's CSS2 answer for the two stylesheets
38
+ Twenty's front-end index.html links, `family=Inter:wght@400;500;600&display=swap` and
39
+ `family=DM+Mono:wght@400;500;600&display=swap`, retained on 2026-11-13 with the User-Agent of Playwright's
40
+ Chromium 153.0.8010.12 headless shell on Linux and on macOS, and the woff2 faces they name. SHA-256 of the
41
+ four stylesheets: Inter on Linux 414e9dbfc53a116f58f01160269a679e84bc1733d98a39d29ff1362b2b764166, DM Mono on
42
+ Linux dc9977cb7b69cc1c1f28d712c1a7744da2a120b6c9be05e38788db1a07fc978a, Inter on macOS
43
+ dac87631d9b2228a3d47cea91e82a8d4f91b6be79f8d6ab8e0189d41f9becfe3, DM Mono on macOS
44
+ a93b88b1a1e247952a5d420495bbeb0c2498c7fde2d0b0787fe3e2643947d450. The macOS Inter answer names the seven faces
45
+ of vendor/inter-variable.json (the same URLs and bytes, kept once). The fifteen new faces, in the file's order:
46
+ Inter ca157063…5ebd, 71d5ee93…0075, 6e9e020a…2190, 1be3448e…b6f6, 5c66f9e0…3ff2, 34b9c504…c956, 3100e775…4c62;
47
+ DM Mono a52e19eb…7175, e1896b13…3e1e, 8711f938…22f5, 9964608a…9951, 7f8712cb…14d9, fd7521f3…7b6d, e284f2a1…6f6c,
48
+ 0e263db5…9f36. Inter's font software is under the SIL Open Font License 1.1 (Inter-OFL.txt); DM Mono's is
49
+ under the SIL Open Font License 1.1 (DMMono-OFL.txt, https://raw.githubusercontent.com/google/fonts/main/ofl/dmmono/OFL.txt,
50
+ retained 2026-11-13, SHA-256 2bada5ea45c3c63b7f1ea1f88ce9672c9e4f0c42b2c3b7378949084fe55a3066).
51
+
52
+ The browser classes (vendor/browser-classes.json) are Google's CSS2 answer to
53
+ `family=Inter:wght@500` for each of 58 User-Agents, retained on 2026-11-13 with each answer's SHA-256, formats
54
+ and face count. Google documents that it serves WOFF 2.0 and unicode-range only to browsers that support them
55
+ but no rule from an agent to its answer; the agents answered identical bytes are one class of
56
+ src/semantics/shared.ts browserClass, and every class there was answered one set of bytes.
57
+
37
58
  Antic's font software is under the SIL Open Font License 1.1 (OFL.txt).
38
59
  Google documentation and its example data are CC BY 4.0, with attribution here:
39
60
  https://developers.google.com/fonts/docs/developer_api