@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.
- package/api/src/semantics/doors.ts +6 -5
- package/api/src/semantics/index.ts +1 -1
- package/assets/src/semantics/index.ts +1 -1
- package/css/spec/SOURCE.md +7 -1
- package/css/src/semantics/css.ts +5 -3
- package/css/src/semantics/css2.ts +9 -8
- package/css/src/semantics/index.ts +1 -1
- package/dist/api/src/semantics/doors.js +6 -5
- package/dist/css/spec/SOURCE.md +7 -1
- package/dist/css/src/semantics/css.js +5 -3
- package/dist/css/src/semantics/css2.js +9 -8
- package/dist/journeys/decisions.json +3 -3
- package/dist/journeys/demand.json +22 -0
- package/dist/src/manifest.js +1 -1
- package/dist/src/semantics/seed.js +95 -0
- package/dist/src/semantics/shared.d.ts +1 -0
- package/dist/src/semantics/shared.js +50 -2
- package/journeys/decisions.json +3 -3
- package/journeys/demand.json +22 -0
- package/package.json +3 -3
- package/src/manifest.ts +1 -1
- package/src/semantics/seed.ts +95 -0
- package/src/semantics/shared.ts +55 -2
- package/vendor/DMMono-OFL.txt +93 -0
- package/vendor/SOURCE.md +21 -0
- package/vendor/browser-classes.json +529 -0
- package/vendor/inter-dm-mono-chromium.json +108 -0
|
@@ -2,6 +2,7 @@ import type { HandlerContext } from '@volter/world-core';
|
|
|
2
2
|
export declare const FONT = "webfont", CATALOG = "_font_catalog", CSS = "_stylesheet", FILE = "_font_file", KEY = "_api_key";
|
|
3
3
|
export declare const object: (value: unknown) => Record<string, unknown>;
|
|
4
4
|
export declare const assetKey: (value: string) => string;
|
|
5
|
+
export declare const browserClass: (userAgent: string) => string;
|
|
5
6
|
export declare const cssKey: (ctx: HandlerContext, family: string, subset: string, userAgent: string) => string;
|
|
6
7
|
export declare const css2Query: (query: URLSearchParams) => Array<[string, string]> | undefined;
|
|
7
8
|
export declare const css2Key: (ctx: HandlerContext, pairs: Array<[string, string]>, userAgent: string) => string;
|
|
@@ -8,7 +8,55 @@ export const assetKey = (value) => {
|
|
|
8
8
|
url.searchParams.sort();
|
|
9
9
|
return url.pathname + url.search;
|
|
10
10
|
};
|
|
11
|
-
|
|
11
|
+
const major = (userAgent, pattern) => Number(pattern.exec(userAgent)?.[1] ?? Number.NaN);
|
|
12
|
+
export const browserClass = (userAgent) => {
|
|
13
|
+
const ua = userAgent;
|
|
14
|
+
const platform = /\b(?:Android|iPhone|iPad|iPod)\b/.test(ua) ? 'mobile' : /\bMacintosh\b/.test(ua) ? 'mac' : 'desktop';
|
|
15
|
+
// an agent that is no browser (none, node, undici, curl) is answered truetype, as an unknown browser on a desktop is
|
|
16
|
+
if (!ua.startsWith('Mozilla/'))
|
|
17
|
+
return 'truetype';
|
|
18
|
+
const legacy = (engine, version) => `legacy ${engine} ${version} ${platform}`;
|
|
19
|
+
const msie = major(ua, /\bMSIE (\d+)/);
|
|
20
|
+
if (msie <= 8)
|
|
21
|
+
return 'eot';
|
|
22
|
+
if (msie >= 9 || /\bTrident\/7\./.test(ua))
|
|
23
|
+
return 'woff desktop';
|
|
24
|
+
// every browser on iOS is Safari's engine, whatever it calls itself (CriOS, FxiOS)
|
|
25
|
+
const ios = /\bOS (\d+)_(\d+)(?:_\d+)? like Mac OS X\b/.exec(ua);
|
|
26
|
+
if (ios) {
|
|
27
|
+
const [v, minor] = [Number(ios[1]), Number(ios[2])];
|
|
28
|
+
if (v <= 4)
|
|
29
|
+
return 'svg';
|
|
30
|
+
return v >= 11 || (v === 10 && minor >= 3) ? 'woff2+range mobile' : legacy('ios', `${v}_${minor}`);
|
|
31
|
+
}
|
|
32
|
+
const chrome = /\b(?:HeadlessChrome|Chrome|Chromium|CriOS)\/((\d+)[\d.]*)/.exec(ua);
|
|
33
|
+
if (chrome) {
|
|
34
|
+
const v = Number(chrome[2]);
|
|
35
|
+
if (v >= 43)
|
|
36
|
+
return `woff2+range ${platform}`;
|
|
37
|
+
if (platform !== 'desktop')
|
|
38
|
+
return legacy('chrome', chrome[1]);
|
|
39
|
+
return v >= 40 ? 'woff+range desktop' : 'woff desktop';
|
|
40
|
+
}
|
|
41
|
+
const firefox = /\bFirefox\/((\d+)[\d.]*)/.exec(ua);
|
|
42
|
+
if (firefox) {
|
|
43
|
+
const v = Number(firefox[2]);
|
|
44
|
+
// on macOS Google answers Firefox 44 to 59 the mobile faces, and Firefox 60 and later WOFF with unicode-range
|
|
45
|
+
if (platform === 'mac')
|
|
46
|
+
return v >= 60 ? 'woff+range mac' : v >= 44 ? 'woff2+range mobile' : legacy('firefox', firefox[1]);
|
|
47
|
+
if (v >= 44)
|
|
48
|
+
return `woff2+range ${platform}`;
|
|
49
|
+
if (platform !== 'desktop')
|
|
50
|
+
return legacy('firefox', firefox[1]);
|
|
51
|
+
return v >= 39 ? 'woff2 desktop' : 'woff desktop';
|
|
52
|
+
}
|
|
53
|
+
// Safari on macOS names its version; Android's own browser names one too, and is answered truetype
|
|
54
|
+
const safari = /\bVersion\/((\d+)[\d.]*)(?: Mobile)?(?:\/\S+)? Safari\//.exec(ua);
|
|
55
|
+
if (safari && platform === 'mac')
|
|
56
|
+
return Number(safari[2]) >= 10 ? 'woff+range mac' : legacy('safari', safari[1]);
|
|
57
|
+
return platform === 'mobile' ? 'truetype mobile' : 'truetype';
|
|
58
|
+
};
|
|
59
|
+
export const cssKey = (ctx, family, subset, userAgent) => ctx.crypto.digest('sha256', JSON.stringify([family, subset, browserClass(userAgent)]));
|
|
12
60
|
// A CSS2 request is its ordered query pairs; family may repeat, text and display may not.
|
|
13
61
|
export const css2Query = (query) => {
|
|
14
62
|
const pairs = [...query.entries()];
|
|
@@ -18,6 +66,6 @@ export const css2Query = (query) => {
|
|
|
18
66
|
return undefined;
|
|
19
67
|
return pairs;
|
|
20
68
|
};
|
|
21
|
-
export const css2Key = (ctx, pairs, userAgent) => ctx.crypto.digest('sha256', JSON.stringify(['css2', pairs, userAgent]));
|
|
69
|
+
export const css2Key = (ctx, pairs, userAgent) => ctx.crypto.digest('sha256', JSON.stringify(['css2', pairs, browserClass(userAgent)]));
|
|
22
70
|
export const googleError = (status, message, kind = status === 403 ? 'PERMISSION_DENIED' : 'INVALID_ARGUMENT') => Response.json({ error: { code: status, message, status: kind } }, { status });
|
|
23
71
|
export const htmlError = (status, message) => new Response('<!doctype html><title>' + status + '</title><p>' + message.replace(/[<>&]/g, c => ({ '<': '<', '>': '>', '&': '&' }[c])) + '</p>', { status, headers: { 'content-type': 'text/html; charset=UTF-8' } });
|
package/journeys/decisions.json
CHANGED
|
@@ -13,14 +13,14 @@
|
|
|
13
13
|
"lane": "css",
|
|
14
14
|
"mechanism": "handler",
|
|
15
15
|
"demand": "All five pinned config.ts UAs",
|
|
16
|
-
"why": "Use exact published CSS per request/
|
|
16
|
+
"why": "Use the exact published CSS per request and browser class (src/semantics/shared.ts browserClass; each of the five pinned config.ts UAs is one class's sample), with the real font-family/style/weight/src consumed by fetchCSS; first-use downloads every returned src. A class with no published representation answers a refusal."
|
|
17
17
|
},
|
|
18
18
|
{
|
|
19
19
|
"operation": "getStylesheet2",
|
|
20
20
|
"lane": "css",
|
|
21
21
|
"mechanism": "handler",
|
|
22
|
-
"demand": "Dub's Inter weights and Rallly's CJK text subsets, both from a server; Rallly's and Cal.diy's next/font/google build of Inter's variable weights, with next/font's browser User-Agent",
|
|
23
|
-
"why": "Use the exact published CSS per ordered CSS2 query and
|
|
22
|
+
"demand": "Dub's Inter weights and Rallly's CJK text subsets, both from a server; Rallly's and Cal.diy's next/font/google build of Inter's variable weights, with next/font's browser User-Agent; Twenty's front end's Inter and DM Mono stylesheets, from a Chromium browser",
|
|
23
|
+
"why": "Use the exact published CSS per ordered CSS2 query and browser class (src/semantics/shared.ts browserClass, the classes of Google's answers in vendor/browser-classes.json): Google serves any browser, choosing WOFF 2.0 and unicode-range by the browser, and a non-browser agent is the truetype class, as Google answered the same CSS to none, node and undici. A class without a published representation, and unpublished families and text subsets, answer a refusal; text= and display= given twice, or another parameter, are a bad request."
|
|
24
24
|
},
|
|
25
25
|
{
|
|
26
26
|
"operation": "getFontFile",
|
package/journeys/demand.json
CHANGED
|
@@ -95,6 +95,28 @@
|
|
|
95
95
|
"evidence": "the same build"
|
|
96
96
|
}
|
|
97
97
|
]
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
"name": "twentyhq/twenty",
|
|
101
|
+
"commit": "323e66433ee5b0878f43bc6c54abe4b1ac9be64b",
|
|
102
|
+
"client": "the front end in a browser (Playwright 1.63's Chromium 153 headless shell in the bench task twenty-20877), its own User-Agent",
|
|
103
|
+
"backend": [
|
|
104
|
+
{
|
|
105
|
+
"call": "GET fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap",
|
|
106
|
+
"operation": "getStylesheet2",
|
|
107
|
+
"evidence": "packages/twenty-front/index.html:33 (a stylesheet link, until 3aeb2b0d self-hosts the fonts)"
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
"call": "GET fonts.googleapis.com/css2?family=DM+Mono:wght@400;500;600&display=swap",
|
|
111
|
+
"operation": "getStylesheet2",
|
|
112
|
+
"evidence": "packages/twenty-front/index.html:37"
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"call": "GET fonts.gstatic.com/s/{inter,dmmono}/... woff2, the faces the page's text needs",
|
|
116
|
+
"operation": "getFontFile",
|
|
117
|
+
"evidence": "the browser loads each unicode-range face its text uses"
|
|
118
|
+
}
|
|
119
|
+
]
|
|
98
120
|
}
|
|
99
121
|
],
|
|
100
122
|
"notDemanded": "OAuth, font submission/publishing UI, effects and advanced catalog capabilities are outside these client paths."
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volter/twin-googlefonts",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "The googlefonts twin (Protocol 3).",
|
|
5
5
|
"author": "Volter (https://github.com/volter-ai)",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"journeys/first-use.json"
|
|
36
36
|
],
|
|
37
37
|
"peerDependencies": {
|
|
38
|
-
"@volter/world-core": "^3.0.
|
|
38
|
+
"@volter/world-core": "^3.0.190"
|
|
39
39
|
},
|
|
40
40
|
"engines": {
|
|
41
41
|
"node": ">=22.3"
|
|
@@ -54,6 +54,6 @@
|
|
|
54
54
|
}
|
|
55
55
|
},
|
|
56
56
|
"volterRelease": {
|
|
57
|
-
"commit": "
|
|
57
|
+
"commit": "71c14672a241845f01d16a370c666e263af87f55"
|
|
58
58
|
}
|
|
59
59
|
}
|
package/src/manifest.ts
CHANGED
|
@@ -13,7 +13,7 @@ export const manifest: VendorManifest = {
|
|
|
13
13
|
protocol: '3', transport: 'rest', archetype: 'crud', bin: 'world-googlefonts',
|
|
14
14
|
resources: ['webfont', '_api_key', '_font_catalog', '_stylesheet', '_font_file'],
|
|
15
15
|
specSource: 'api/spec/SOURCE.md: unchanged Google Discovery; css/spec/SOURCE.md and assets/spec/SOURCE.md: Google documentation and retained font responses',
|
|
16
|
-
description: 'Google Fonts catalog, exact published CSS representations and genuine font bytes; a World-issued query API key. Seed data is the attributed Antic reference catalog
|
|
16
|
+
description: 'Google Fonts catalog, exact published CSS representations and genuine font bytes; a World-issued query API key. Seed data is the attributed Antic reference catalog, the CSS2 weights 500-700 of Inter, the variable weights of Inter and the Inter and DM Mono stylesheets Twenty links, not the complete live Google catalog.',
|
|
17
17
|
// next/font/google fetches the font's stylesheet and files from Google at an app's build (twin-world architecture,
|
|
18
18
|
// Selection: a module that reaches the vendor by itself is evidence, as an SDK is)
|
|
19
19
|
adoption: { sdks: [], envStems: ['GOOGLE_FONTS'], modules: ['next/font/google'] },
|