@volter/twin-googlefonts 1.0.1 → 1.0.2
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/spec/sources.json +11 -1
- package/api/src/semantics/doors.ts +4 -0
- package/css/spec/SOURCE.md +3 -1
- package/dist/api/spec/sources.json +11 -1
- package/dist/api/src/semantics/doors.js +4 -0
- package/dist/css/spec/SOURCE.md +3 -1
- package/dist/generated/pack-facts.json +68 -0
- package/dist/journeys/customer-life.json +81 -0
- package/dist/journeys/decisions.json +13 -3
- package/dist/journeys/demand.json +34 -0
- package/dist/src/cli.js +11 -10
- package/dist/src/manifest.js +4 -2
- package/dist/src/semantics/seed.js +40 -0
- package/generated/pack-facts.json +5 -2
- package/journeys/customer-life.json +81 -0
- package/journeys/decisions.json +13 -3
- package/journeys/demand.json +34 -0
- package/package.json +30 -14
- package/src/cli.ts +10 -10
- package/src/manifest.ts +4 -2
- package/src/semantics/seed.ts +40 -0
- package/vendor/SOURCE.md +7 -0
- package/vendor/inter-variable.json +52 -0
- package/dist/api/src/fetch.ts +0 -16
- package/dist/api/src/index.ts +0 -3
- package/dist/api/src/manifest.ts +0 -31
- package/dist/api/src/semantics/doors.ts +0 -131
- package/dist/api/src/semantics/index.ts +0 -8
- package/dist/api/src/semantics/shared.ts +0 -1
- package/dist/api/src/semantics/states.ts +0 -6
- package/dist/api/src/semantics/webfonts.ts +0 -32
- package/dist/assets/src/fetch.ts +0 -14
- package/dist/assets/src/index.ts +0 -3
- package/dist/assets/src/manifest.ts +0 -19
- package/dist/assets/src/semantics/index.ts +0 -10
- package/dist/assets/src/semantics/l.ts +0 -4
- package/dist/assets/src/semantics/s.ts +0 -4
- package/dist/assets/src/semantics/shared.ts +0 -13
- package/dist/css/src/fetch.ts +0 -14
- package/dist/css/src/index.ts +0 -3
- package/dist/css/src/manifest.ts +0 -19
- package/dist/css/src/semantics/css.ts +0 -19
- package/dist/css/src/semantics/css2.ts +0 -20
- package/dist/css/src/semantics/index.ts +0 -10
- package/dist/css/src/semantics/shared.ts +0 -1
- package/dist/journeys/customer-life.outline.md +0 -24
- package/dist/journeys/customer-life.outline.verdict.json +0 -82
- package/dist/journeys/customer-life.verdict.json +0 -149
- package/dist/vendor/Inter-OFL.txt +0 -93
- package/dist/vendor/OFL.txt +0 -94
- package/dist/vendor/SOURCE.md +0 -33
- package/dist/vendor/antic.json +0 -93
- package/dist/vendor/inputs.json +0 -460
- package/dist/vendor/inter.json +0 -44
- package/dist/vendor/noto-sans-sc-text.json +0 -24
- package/journeys/customer-life.outline.md +0 -24
- package/journeys/customer-life.outline.verdict.json +0 -82
- package/journeys/customer-life.verdict.json +0 -149
package/dist/api/src/fetch.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
// GENERATED by scripts/create-pack.ts (--init, --index): a fixed file. The kernel serves the discovery door, the clock,
|
|
2
|
-
// the World's doors, the vendor's screens and the API from the pack's parts (architecture, "The pack contract").
|
|
3
|
-
import { createPackFetch, type DerivedFetch, type DerivedSurface, type PackFetchOptions } from '@volter/world-core';
|
|
4
|
-
import surface from './generated/surface.gen.json' with { type: 'json' };
|
|
5
|
-
import { manifest } from './manifest.ts';
|
|
6
|
-
import * as doors from './semantics/doors.ts';
|
|
7
|
-
import { apiSemantics } from './semantics/index.ts';
|
|
8
|
-
|
|
9
|
-
export function createApiLaneFetch(options: PackFetchOptions = {}): DerivedFetch {
|
|
10
|
-
return createPackFetch({
|
|
11
|
-
manifest,
|
|
12
|
-
surface: surface as DerivedSurface,
|
|
13
|
-
handlers: apiSemantics,
|
|
14
|
-
doors,
|
|
15
|
-
}, options);
|
|
16
|
-
}
|
package/dist/api/src/index.ts
DELETED
package/dist/api/src/manifest.ts
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
// Vendor wire facts; the surface is derived from the retained spec.
|
|
2
|
-
import type { DerivedManifest } from '@volter/world-core';
|
|
3
|
-
import { states } from './semantics/states.ts';
|
|
4
|
-
export const manifest: DerivedManifest = {
|
|
5
|
-
vendor: 'googlefonts', service: 'googlefonts', body: { json: 'always' },
|
|
6
|
-
ids: { template: '{uuid}' }, time: 'iso',
|
|
7
|
-
error: { error: { code: '{status}', message: '{message}', status: '{kind}' } },
|
|
8
|
-
readOnly: { status: 403, kind: 'PERMISSION_DENIED', message: 'Read-only World' },
|
|
9
|
-
notFound: { status: 404, kind: 'NOT_FOUND', message: 'Not Found' },
|
|
10
|
-
gap: { status: 404, kind: 'NOT_FOUND', message: 'Not Found' },
|
|
11
|
-
list: { style: 'envelope', envelope: { items: '{data}' }, limit: { param: 'limit', default: 100, max: 100 } },
|
|
12
|
-
deleted: {},
|
|
13
|
-
resources: { webfont: { idPrefix: '', ...states.webfont,
|
|
14
|
-
refresh: { none: 'The selected reference snapshot is publisher data, not an account-owned font mutation; no full-live-catalog refresh is claimed.' } },
|
|
15
|
-
_project: { idPrefix: '' },
|
|
16
|
-
_api_key: { idPrefix: '', ...states._api_key } },
|
|
17
|
-
discovery: { twinOf: 'Google Fonts Developer API', stores: 'Published catalog and font representations in the shared googlefonts store' },
|
|
18
|
-
// The pinned helper uses the older documented prefix; no app request is rewritten.
|
|
19
|
-
pathPrefix: '^/webfonts(?=/v1/webfonts$)',
|
|
20
|
-
// The core reads query keys and gates against actual issued hashes. Doors bypass the API gate.
|
|
21
|
-
auth: { header: 'x-goog-api-key', scheme: '', also: [{ query: 'key' }], paths: '^/(webfonts/)?v1/webfonts$',
|
|
22
|
-
missing: { status: 403, kind: 'PERMISSION_DENIED', message: 'The request is missing a valid API key.' },
|
|
23
|
-
invalid: { status: 400, kind: 'INVALID_ARGUMENT', message: 'API key not valid. Please pass a valid API key.' },
|
|
24
|
-
invalidKeys: [], gateWhenAbsent: true, held: { storedAs: '_api_key', hashField: 'sha256' } },
|
|
25
|
-
doors: [{ id: 'appCredentials', method: 'POST', path: '/_twin/app-credentials', note: 'Issue the World application key' },
|
|
26
|
-
{ id: 'publishCatalog', method: 'POST', path: '/_twin/catalog', note: 'Google publishes fonts without a Developer API create operation; import its actual catalog/CSS/blob snapshot' }],
|
|
27
|
-
descriptor: { protocol: '3', transport: 'rest', archetype: 'crud', bin: 'world-googlefonts-api',
|
|
28
|
-
resources: [], specSource: 'api/spec/SOURCE.md', description: 'Google Fonts Developer API',
|
|
29
|
-
adoption: { sdks: [], envStems: [] }, hosts: [{ host: 'webfonts.googleapis.com' }, { host: 'www.googleapis.com', pathPattern: '^/webfonts/v1/webfonts$' }],
|
|
30
|
-
endpointEnvNone: 'The application hardcodes this vendor lane host.' },
|
|
31
|
-
};
|
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
// Google supplies catalog records and keys outside the read-only Fonts API.
|
|
2
|
-
import type { HandlerContext } from '@volter/world-core';
|
|
3
|
-
import { FONT, CATALOG, CSS, FILE, KEY, object, assetKey, cssKey, css2Key, css2Query, googleError } from './shared.ts';
|
|
4
|
-
|
|
5
|
-
// source: https://developers.google.com/fonts/docs/developer_api
|
|
6
|
-
export async function appCredentials(ctx: HandlerContext): Promise<Response> {
|
|
7
|
-
const project = await ctx.tenant('_project', async organization =>
|
|
8
|
-
String((await ctx.create('_project', { name: organization.label, owner: organization.members[0] }, 'project.create')).id));
|
|
9
|
-
const value = async (id: string): Promise<string> => 'AIza' + (await ctx.secret(`googlefonts:application-api-key:${id}`)).slice(0, 35);
|
|
10
|
-
const key = await ctx.accountKey('api-key', KEY, {
|
|
11
|
-
make: async () => {
|
|
12
|
-
const row = await ctx.create(KEY, { _project: project.id, name: ctx.deployment() || 'world' }, 'api_key.create');
|
|
13
|
-
const id = String(row.id), secret = await value(id);
|
|
14
|
-
await ctx.record(KEY, { sha256: ctx.crypto.digest('sha256', secret) }, id);
|
|
15
|
-
return id;
|
|
16
|
-
},
|
|
17
|
-
read: async row => {
|
|
18
|
-
if (row._project !== project.id) return undefined;
|
|
19
|
-
const secret = await value(String(row.id));
|
|
20
|
-
return row.sha256 === ctx.crypto.digest('sha256', secret) ? secret : undefined;
|
|
21
|
-
},
|
|
22
|
-
});
|
|
23
|
-
return Response.json({ api_key: key.secret });
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// source: https://developers.google.com/fonts/docs/developer_api
|
|
27
|
-
export async function publishCatalog(ctx: HandlerContext): Promise<Response> {
|
|
28
|
-
// There is no Developer API create-font operation. This explicit publisher
|
|
29
|
-
// door imports real vendor-shaped objects and their representations.
|
|
30
|
-
const input = object(ctx.body), catalog = object(input.catalog);
|
|
31
|
-
const fonts = Array.isArray(catalog.items) ? catalog.items.map(object) : [];
|
|
32
|
-
const sheets = Array.isArray(input.stylesheets) ? input.stylesheets.map(object) : [];
|
|
33
|
-
const files = Array.isArray(input.assets) ? input.assets.map(object) : [];
|
|
34
|
-
// CSS2 serves Google's whole library by its query, not only the Developer API items published here.
|
|
35
|
-
const sheets2 = Array.isArray(input.stylesheets2) ? input.stylesheets2.map(object) : [];
|
|
36
|
-
if (catalog.kind !== 'webfonts#webfontList' || fonts.length === 0)
|
|
37
|
-
return googleError(400, 'A webfonts#webfontList catalog is required');
|
|
38
|
-
const names = new Set<string>();
|
|
39
|
-
const strings = (value: unknown): value is string[] => Array.isArray(value) && value.length > 0 &&
|
|
40
|
-
value.every(v => typeof v === 'string' && v.length > 0) && new Set(value).size === value.length;
|
|
41
|
-
const decoded = new Map<string, { url: string; contentType: string; bytes: Uint8Array; hash: string }>();
|
|
42
|
-
try {
|
|
43
|
-
for (const file of files) {
|
|
44
|
-
if (typeof file.url !== 'string' || typeof file.contentType !== 'string' ||
|
|
45
|
-
!/^[a-zA-Z0-9.+-]+\/[a-zA-Z0-9.+-]+(?:;[^\r\n]*)?$/.test(file.contentType) ||
|
|
46
|
-
typeof file.base64 !== 'string' || !file.base64 ||
|
|
47
|
-
!/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(file.base64))
|
|
48
|
-
throw new Error('Each asset needs its gstatic URL, Content-Type and complete base64 bytes');
|
|
49
|
-
const key = assetKey(file.url);
|
|
50
|
-
if (!/^\/s\/[^?]+$|^\/l\/font\?/.test(key) || decoded.has(key)) throw new Error('Invalid or duplicate asset URL');
|
|
51
|
-
const bytes = Uint8Array.from(atob(file.base64), c => c.charCodeAt(0));
|
|
52
|
-
if (bytes.length === 0) throw new Error('Font bytes must not be empty');
|
|
53
|
-
decoded.set(key, { url: file.url, contentType: file.contentType, bytes, hash: ctx.crypto.digest('sha256', bytes) });
|
|
54
|
-
}
|
|
55
|
-
for (const font of fonts) {
|
|
56
|
-
if (typeof font.family !== 'string' || !font.family || names.has(font.family) ||
|
|
57
|
-
font.kind !== 'webfonts#webfont' || !strings(font.variants) || !strings(font.subsets) ||
|
|
58
|
-
typeof font.category !== 'string' || typeof font.version !== 'string' ||
|
|
59
|
-
typeof font.lastModified !== 'string' || !/^\d{4}-\d{2}-\d{2}$/.test(font.lastModified))
|
|
60
|
-
throw new Error('Invalid or duplicate vendor font item');
|
|
61
|
-
names.add(font.family);
|
|
62
|
-
const fileMap = object(font.files);
|
|
63
|
-
for (const variant of font.variants) {
|
|
64
|
-
if (typeof fileMap[variant] !== 'string' || !decoded.has(assetKey(fileMap[variant] as string)))
|
|
65
|
-
throw new Error('Every advertised variant file must have a genuine supplied blob');
|
|
66
|
-
}
|
|
67
|
-
if (font.menu !== undefined && (typeof font.menu !== 'string' || !decoded.has(assetKey(font.menu))))
|
|
68
|
-
throw new Error('An advertised menu URL must have a supplied blob');
|
|
69
|
-
}
|
|
70
|
-
const representations = new Set<string>();
|
|
71
|
-
for (const sheet of sheets) {
|
|
72
|
-
if (typeof sheet.family !== 'string' || typeof sheet.subset !== 'string' ||
|
|
73
|
-
typeof sheet.userAgent !== 'string' || !sheet.userAgent || typeof sheet.css !== 'string' ||
|
|
74
|
-
!sheet.css.includes('@font-face')) throw new Error('Invalid stylesheet representation');
|
|
75
|
-
const split = sheet.family.lastIndexOf(':');
|
|
76
|
-
const family = split < 0 ? sheet.family : sheet.family.slice(0, split);
|
|
77
|
-
const variant = split < 0 ? 'regular' : sheet.family.slice(split + 1);
|
|
78
|
-
const font = fonts.find(f => f.family === family);
|
|
79
|
-
if (!font || !(font.variants as string[]).includes(variant) ||
|
|
80
|
-
sheet.subset.split(',').some(s => !(font.subsets as string[]).includes(s)))
|
|
81
|
-
throw new Error('Stylesheet request must name a published font, variant and subset');
|
|
82
|
-
const key = cssKey(ctx, sheet.family, sheet.subset, sheet.userAgent);
|
|
83
|
-
if (representations.has(key)) throw new Error('Duplicate stylesheet request');
|
|
84
|
-
representations.add(key);
|
|
85
|
-
const urls = [...sheet.css.matchAll(/url\((https?:\/\/[^)]+)\)/g)].map(m => m[1]!);
|
|
86
|
-
if (urls.length === 0 || urls.some(url => !decoded.has(assetKey(url))))
|
|
87
|
-
throw new Error('Every stylesheet src URL must have a supplied blob');
|
|
88
|
-
}
|
|
89
|
-
for (const sheet of sheets2) {
|
|
90
|
-
const pairs = typeof sheet.query === 'string' ? css2Query(new URLSearchParams(sheet.query)) : undefined;
|
|
91
|
-
if (!pairs || typeof sheet.userAgent !== 'string' || typeof sheet.css !== 'string' || !sheet.css.includes('@font-face'))
|
|
92
|
-
throw new Error('Invalid CSS2 stylesheet representation');
|
|
93
|
-
const key = css2Key(ctx, pairs, sheet.userAgent);
|
|
94
|
-
if (representations.has(key)) throw new Error('Duplicate stylesheet request');
|
|
95
|
-
representations.add(key);
|
|
96
|
-
const urls = [...sheet.css.matchAll(/url\((https?:\/\/[^)]+)\)/g)].map(m => m[1]!);
|
|
97
|
-
if (urls.length === 0 || urls.some(url => !decoded.has(assetKey(url))))
|
|
98
|
-
throw new Error('Every stylesheet src URL must have a supplied blob');
|
|
99
|
-
}
|
|
100
|
-
// A content-addressed font URL keeps its bytes across publications.
|
|
101
|
-
for (const [key, file] of decoded) {
|
|
102
|
-
const prior = ctx.row(FILE, key);
|
|
103
|
-
if (prior && prior.sha256 !== file.hash) return googleError(409, 'Published font URL already names other bytes', 'ALREADY_EXISTS');
|
|
104
|
-
}
|
|
105
|
-
} catch (cause) { return googleError(400, cause instanceof Error ? cause.message : 'Invalid catalog publication'); }
|
|
106
|
-
const fingerprint = ctx.crypto.digest('sha256', JSON.stringify(input));
|
|
107
|
-
if (ctx.row(CATALOG, 'published')?.fingerprint === fingerprint) return Response.json({ families: [...names] });
|
|
108
|
-
// Validation precedes all writes. Publish the catalog pointer last: readers
|
|
109
|
-
// select only this generation's metadata and stylesheet records.
|
|
110
|
-
for (const [key, file] of decoded) {
|
|
111
|
-
const blob = 'font-' + file.hash;
|
|
112
|
-
await ctx.blobs.put(blob, file.bytes);
|
|
113
|
-
await ctx.record(FILE, { url: file.url, contentType: file.contentType, sha256: file.hash, blob }, key);
|
|
114
|
-
}
|
|
115
|
-
// A font item is the vendor's resource, not bookkeeping: written as the publication that made it.
|
|
116
|
-
for (const font of fonts) await ctx.write(FONT, fingerprint + ':' + font.family, font, 'webfont.publish');
|
|
117
|
-
for (const sheet of sheets) {
|
|
118
|
-
const key = cssKey(ctx, sheet.family as string, sheet.subset as string, sheet.userAgent as string);
|
|
119
|
-
const blob = 'css-' + ctx.crypto.digest('sha256', sheet.css as string);
|
|
120
|
-
await ctx.blobs.put(blob, new TextEncoder().encode(sheet.css as string));
|
|
121
|
-
await ctx.record(CSS, { family: sheet.family, subset: sheet.subset, userAgent: sheet.userAgent, blob }, fingerprint + ':' + key);
|
|
122
|
-
}
|
|
123
|
-
for (const sheet of sheets2) {
|
|
124
|
-
const key = css2Key(ctx, css2Query(new URLSearchParams(sheet.query as string))!, sheet.userAgent as string);
|
|
125
|
-
const blob = 'css-' + ctx.crypto.digest('sha256', sheet.css as string);
|
|
126
|
-
await ctx.blobs.put(blob, new TextEncoder().encode(sheet.css as string));
|
|
127
|
-
await ctx.record(CSS, { query: sheet.query, userAgent: sheet.userAgent, blob }, fingerprint + ':' + key);
|
|
128
|
-
}
|
|
129
|
-
await ctx.record(CATALOG, { families: [...names], fingerprint }, 'published');
|
|
130
|
-
return Response.json({ families: [...names] }, { status: 201 });
|
|
131
|
-
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
// GENERATED by scripts/create-pack.ts --index: the handler map, by operationId, from the families' files (each
|
|
2
|
-
// handler named as its operation). Do not edit; add a handler to its family's file and generate again.
|
|
3
|
-
import type { Handler } from '@volter/world-core';
|
|
4
|
-
import * as webfonts from './webfonts.ts';
|
|
5
|
-
|
|
6
|
-
export const apiSemantics: Record<string, Handler> = {
|
|
7
|
-
"webfonts.webfonts.list": webfonts.webfonts_webfonts_list,
|
|
8
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from '../../../src/semantics/shared.ts';
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
// The catalog and its immutable representations have no status transition.
|
|
2
|
-
// Keys are issued once and stored as hashes; this scope has no key revocation API.
|
|
3
|
-
export const states = {
|
|
4
|
-
webfont: { notState: ['family', 'variants', 'subsets', 'category', 'version', 'lastModified'] },
|
|
5
|
-
_api_key: { notState: ['sha256'] },
|
|
6
|
-
};
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import type { HandlerContext } from '@volter/world-core';
|
|
2
|
-
import { FONT, CATALOG, googleError } from './shared.ts';
|
|
3
|
-
|
|
4
|
-
// source: spec:webfonts.webfonts.list "Retrieves the list of fonts currently served by the Google Fonts Developer API."
|
|
5
|
-
export async function webfonts_webfonts_list(ctx: HandlerContext): Promise<Response> {
|
|
6
|
-
const query = new URL(ctx.call.request.url).searchParams;
|
|
7
|
-
// Where this scope stops: the demand selects the default static-TTF catalog.
|
|
8
|
-
// VF/WOFF2/FAMILY_TAGS need an independently published capability snapshot.
|
|
9
|
-
if (query.getAll('capability').some(c => c && c !== 'CAPABILITY_UNSPECIFIED'))
|
|
10
|
-
return googleError(400, 'This published catalog does not provide the requested capability');
|
|
11
|
-
const published = ctx.row(CATALOG, 'published');
|
|
12
|
-
const names = Array.isArray(published?.families) ? published.families : [];
|
|
13
|
-
let items = names.flatMap(family => {
|
|
14
|
-
const row = ctx.row(FONT, String(published?.fingerprint) + ':' + String(family));
|
|
15
|
-
return row ? [ctx.own(row)] : [];
|
|
16
|
-
});
|
|
17
|
-
for (const field of ['family', 'subset', 'category']) {
|
|
18
|
-
const asked = query.getAll(field);
|
|
19
|
-
if (asked.length) items = items.filter(item => asked.some(value => field === 'subset'
|
|
20
|
-
? Array.isArray(item.subsets) && item.subsets.includes(value) : item[field] === value));
|
|
21
|
-
}
|
|
22
|
-
// source: https://developers.google.com/fonts/docs/developer_api
|
|
23
|
-
const sort = (query.get('sort') ?? 'alpha').toLowerCase();
|
|
24
|
-
if (!['sort_undefined', 'alpha', 'date', 'popularity', 'style', 'trending'].includes(sort))
|
|
25
|
-
return googleError(400, 'Invalid sort value');
|
|
26
|
-
if (sort === 'alpha' || sort === 'sort_undefined') items.sort((a,b) => String(a.family).localeCompare(String(b.family), 'en'));
|
|
27
|
-
else if (sort === 'date') items.sort((a,b) => String(b.lastModified).localeCompare(String(a.lastModified)));
|
|
28
|
-
else if (sort === 'style') items.sort((a,b) => (Array.isArray(b.variants) ? b.variants.length : 0) - (Array.isArray(a.variants) ? a.variants.length : 0));
|
|
29
|
-
// Popularity/trending retain the publisher's catalog order. The reference
|
|
30
|
-
// snapshot is one actual font, not a manufactured global popularity ranking.
|
|
31
|
-
return Response.json({ kind: 'webfonts#webfontList', items });
|
|
32
|
-
}
|
package/dist/assets/src/fetch.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
// GENERATED by scripts/create-pack.ts (--init, --index): a fixed file. The kernel serves the discovery door, the clock,
|
|
2
|
-
// the World's doors, the vendor's screens and the API from the pack's parts (architecture, "The pack contract").
|
|
3
|
-
import { createPackFetch, type DerivedFetch, type DerivedSurface, type PackFetchOptions } from '@volter/world-core';
|
|
4
|
-
import surface from './generated/surface.gen.json' with { type: 'json' };
|
|
5
|
-
import { manifest } from './manifest.ts';
|
|
6
|
-
import { assetsSemantics } from './semantics/index.ts';
|
|
7
|
-
|
|
8
|
-
export function createAssetsLaneFetch(options: PackFetchOptions = {}): DerivedFetch {
|
|
9
|
-
return createPackFetch({
|
|
10
|
-
manifest,
|
|
11
|
-
surface: surface as DerivedSurface,
|
|
12
|
-
handlers: assetsSemantics,
|
|
13
|
-
}, options);
|
|
14
|
-
}
|
package/dist/assets/src/index.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
// Vendor wire facts; the surface is derived from the retained spec.
|
|
2
|
-
import type { DerivedManifest } from '@volter/world-core';
|
|
3
|
-
export const manifest: DerivedManifest = {
|
|
4
|
-
vendor: 'googlefonts', service: 'googlefonts', body: { json: 'always' },
|
|
5
|
-
ids: { template: '{uuid}' }, time: 'iso',
|
|
6
|
-
error: { error: { code: '{status}', message: '{message}', status: '{kind}' } },
|
|
7
|
-
readOnly: { status: 403, kind: 'PERMISSION_DENIED', message: 'Read-only World' },
|
|
8
|
-
notFound: { status: 404, kind: 'NOT_FOUND', message: 'Not Found' },
|
|
9
|
-
gap: { status: 404, kind: 'NOT_FOUND', message: 'Not Found' },
|
|
10
|
-
list: { style: 'envelope', envelope: { items: '{data}' }, limit: { param: 'limit', default: 100, max: 100 } },
|
|
11
|
-
deleted: {},
|
|
12
|
-
resources: {},
|
|
13
|
-
discovery: { twinOf: 'Google Fonts font bytes', stores: 'Published catalog and font representations in the shared googlefonts store' },
|
|
14
|
-
spanning: ['file'],
|
|
15
|
-
descriptor: { protocol: '3', transport: 'rest', archetype: 'crud', bin: 'world-googlefonts-assets',
|
|
16
|
-
resources: [], specSource: 'assets/spec/SOURCE.md', description: 'Google Fonts font bytes',
|
|
17
|
-
adoption: { sdks: [], envStems: [] }, hosts: [{ host: 'fonts.gstatic.com', pathPattern: '^/(s/|l/font$)' }],
|
|
18
|
-
endpointEnvNone: 'The application hardcodes this vendor lane host.' },
|
|
19
|
-
};
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
// GENERATED by scripts/create-pack.ts --index: the handler map, by operationId, from the families' files (each
|
|
2
|
-
// handler named as its operation). Do not edit; add a handler to its family's file and generate again.
|
|
3
|
-
import type { Handler } from '@volter/world-core';
|
|
4
|
-
import * as l from './l.ts';
|
|
5
|
-
import * as s from './s.ts';
|
|
6
|
-
|
|
7
|
-
export const assetsSemantics: Record<string, Handler> = {
|
|
8
|
-
getFontFile: s.getFontFile,
|
|
9
|
-
getLegacyFontFile: l.getLegacyFontFile,
|
|
10
|
-
};
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import type { HandlerContext } from '@volter/world-core';
|
|
2
|
-
import { downloadFont } from './shared.ts';
|
|
3
|
-
// source: spec:getLegacyFontFile "Download the font-file bytes named by a Google Fonts stylesheet src URL for a legacy browser."
|
|
4
|
-
export async function getLegacyFontFile(ctx: HandlerContext): Promise<Response> { return downloadFont(ctx); }
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import type { HandlerContext } from '@volter/world-core';
|
|
2
|
-
import { downloadFont } from './shared.ts';
|
|
3
|
-
// source: spec:getFontFile "Download the font-file bytes named by a Google Fonts stylesheet src URL."
|
|
4
|
-
export async function getFontFile(ctx: HandlerContext): Promise<Response> { return downloadFont(ctx); }
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import type { HandlerContext } from '@volter/world-core';
|
|
2
|
-
import { FILE, assetKey, htmlError } from '../../../src/semantics/shared.ts';
|
|
3
|
-
|
|
4
|
-
export async function downloadFont(ctx: HandlerContext): Promise<Response> {
|
|
5
|
-
const request = new URL(ctx.call.request.url);
|
|
6
|
-
const vendorUrl = new URL(request.pathname + request.search, 'https://fonts.gstatic.com');
|
|
7
|
-
const row = ctx.row(FILE, assetKey(vendorUrl.href));
|
|
8
|
-
if (!row) return htmlError(404, 'Font file not found');
|
|
9
|
-
const body = await ctx.blobs.get(String(row.blob));
|
|
10
|
-
if (!body) return htmlError(404, 'Font file not found');
|
|
11
|
-
return new Response(Uint8Array.from(body).buffer, { headers: { 'content-type': String(row.contentType), 'content-length': String(body.length),
|
|
12
|
-
'access-control-allow-origin': '*', 'cache-control': 'public, max-age=31536000, immutable' } });
|
|
13
|
-
}
|
package/dist/css/src/fetch.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
// GENERATED by scripts/create-pack.ts (--init, --index): a fixed file. The kernel serves the discovery door, the clock,
|
|
2
|
-
// the World's doors, the vendor's screens and the API from the pack's parts (architecture, "The pack contract").
|
|
3
|
-
import { createPackFetch, type DerivedFetch, type DerivedSurface, type PackFetchOptions } from '@volter/world-core';
|
|
4
|
-
import surface from './generated/surface.gen.json' with { type: 'json' };
|
|
5
|
-
import { manifest } from './manifest.ts';
|
|
6
|
-
import { cssSemantics } from './semantics/index.ts';
|
|
7
|
-
|
|
8
|
-
export function createCssLaneFetch(options: PackFetchOptions = {}): DerivedFetch {
|
|
9
|
-
return createPackFetch({
|
|
10
|
-
manifest,
|
|
11
|
-
surface: surface as DerivedSurface,
|
|
12
|
-
handlers: cssSemantics,
|
|
13
|
-
}, options);
|
|
14
|
-
}
|
package/dist/css/src/index.ts
DELETED
package/dist/css/src/manifest.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
// Vendor wire facts; the surface is derived from the retained spec.
|
|
2
|
-
import type { DerivedManifest } from '@volter/world-core';
|
|
3
|
-
export const manifest: DerivedManifest = {
|
|
4
|
-
vendor: 'googlefonts', service: 'googlefonts', body: { json: 'always' },
|
|
5
|
-
ids: { template: '{uuid}' }, time: 'iso',
|
|
6
|
-
error: { error: { code: '{status}', message: '{message}', status: '{kind}' } },
|
|
7
|
-
readOnly: { status: 403, kind: 'PERMISSION_DENIED', message: 'Read-only World' },
|
|
8
|
-
notFound: { status: 404, kind: 'NOT_FOUND', message: 'Not Found' },
|
|
9
|
-
gap: { status: 404, kind: 'NOT_FOUND', message: 'Not Found' },
|
|
10
|
-
list: { style: 'envelope', envelope: { items: '{data}' }, limit: { param: 'limit', default: 100, max: 100 } },
|
|
11
|
-
deleted: {},
|
|
12
|
-
resources: {},
|
|
13
|
-
discovery: { twinOf: 'Google Fonts browser-selected CSS', stores: 'Published catalog and font representations in the shared googlefonts store' },
|
|
14
|
-
|
|
15
|
-
descriptor: { protocol: '3', transport: 'rest', archetype: 'crud', bin: 'world-googlefonts-css',
|
|
16
|
-
resources: [], specSource: 'css/spec/SOURCE.md', description: 'Google Fonts browser-selected CSS',
|
|
17
|
-
adoption: { sdks: [], envStems: [] }, hosts: [{ host: 'fonts.googleapis.com', pathPattern: '^/css2?$' }],
|
|
18
|
-
endpointEnvNone: 'The application hardcodes this vendor lane host.' },
|
|
19
|
-
};
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import type { HandlerContext } from '@volter/world-core';
|
|
2
|
-
import { CATALOG, CSS, cssKey, htmlError } from './shared.ts';
|
|
3
|
-
|
|
4
|
-
// source: spec:getStylesheet "The representation is selected for the requesting browser."
|
|
5
|
-
export async function getStylesheet(ctx: HandlerContext): Promise<Response> {
|
|
6
|
-
const query = new URL(ctx.call.request.url).searchParams;
|
|
7
|
-
const family = query.get('family'), subset = query.get('subset') ?? 'latin';
|
|
8
|
-
if (!family || [...query.keys()].some(k => !['family','subset'].includes(k)))
|
|
9
|
-
return htmlError(400, 'Bad font request');
|
|
10
|
-
const generation = ctx.row(CATALOG, 'published')?.fingerprint;
|
|
11
|
-
const key = cssKey(ctx, family, subset, ctx.call.request.headers.get('user-agent') ?? '');
|
|
12
|
-
const row = generation === undefined ? undefined : ctx.row(CSS, String(generation) + ':' + key);
|
|
13
|
-
// Where the documentation stops: no supplied representation is a named
|
|
14
|
-
// refusal, not an invented conversion or another browser's font format.
|
|
15
|
-
if (!row) return htmlError(400, 'Font family or browser representation is not published');
|
|
16
|
-
const body = await ctx.blobs.get(String(row.blob));
|
|
17
|
-
if (!body) return htmlError(404, 'Font stylesheet not found');
|
|
18
|
-
return new Response(Uint8Array.from(body).buffer, { headers: { 'content-type': 'text/css; charset=utf-8', 'content-length': String(body.length), 'vary': 'User-Agent' } });
|
|
19
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import type { HandlerContext } from '@volter/world-core';
|
|
2
|
-
import { CATALOG, CSS, css2Key, css2Query, htmlError } from './shared.ts';
|
|
3
|
-
|
|
4
|
-
// source: spec:getStylesheet2 "To request multiple families, specify the family= param for each family."
|
|
5
|
-
export async function getStylesheet2(ctx: HandlerContext): Promise<Response> {
|
|
6
|
-
const pairs = css2Query(new URL(ctx.call.request.url).searchParams);
|
|
7
|
-
if (!pairs) return htmlError(400, 'Bad font request');
|
|
8
|
-
const generation = ctx.row(CATALOG, 'published')?.fingerprint;
|
|
9
|
-
const userAgent = ctx.call.request.headers.get('user-agent') ?? '';
|
|
10
|
-
const sheet = (agent: string) => generation === undefined ? undefined : ctx.row(CSS, String(generation) + ':' + css2Key(ctx, pairs, agent));
|
|
11
|
-
// Where the evidence stops: Google answered the same truetype stylesheet to no User-Agent, "node" and "undici"
|
|
12
|
-
// (vendor/inter.json). A request whose agent is not a browser's ("Mozilla/...") and has no representation of its
|
|
13
|
-
// own reads the one published for no agent; a browser without its own representation is refused, never sent
|
|
14
|
-
// another browser's format.
|
|
15
|
-
const row = sheet(userAgent) ?? (userAgent.startsWith('Mozilla/') ? undefined : sheet(''));
|
|
16
|
-
if (!row) return htmlError(400, 'Font family or browser representation is not published');
|
|
17
|
-
const body = await ctx.blobs.get(String(row.blob));
|
|
18
|
-
if (!body) return htmlError(404, 'Font stylesheet not found');
|
|
19
|
-
return new Response(Uint8Array.from(body).buffer, { headers: { 'content-type': 'text/css; charset=utf-8', 'content-length': String(body.length), 'access-control-allow-origin': '*' } });
|
|
20
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
// GENERATED by scripts/create-pack.ts --index: the handler map, by operationId, from the families' files (each
|
|
2
|
-
// handler named as its operation). Do not edit; add a handler to its family's file and generate again.
|
|
3
|
-
import type { Handler } from '@volter/world-core';
|
|
4
|
-
import * as css from './css.ts';
|
|
5
|
-
import * as css2 from './css2.ts';
|
|
6
|
-
|
|
7
|
-
export const cssSemantics: Record<string, Handler> = {
|
|
8
|
-
getStylesheet: css.getStylesheet,
|
|
9
|
-
getStylesheet2: css2.getStylesheet2,
|
|
10
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from '../../../src/semantics/shared.ts';
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
# Google Fonts used by google-webfonts-helper, Dub and Rallly
|
|
2
|
-
|
|
3
|
-
1. Google publishes the attributed Antic reference snapshot via the publisher
|
|
4
|
-
door (no Developer API font-creation endpoint exists). Actual item fields,
|
|
5
|
-
browser CSS and font bytes enter the shared World store.
|
|
6
|
-
2. The World asks appCredentials for its issued key.
|
|
7
|
-
3. The unchanged helper fetches the popularity-sorted catalog with that query
|
|
8
|
-
key; it consumes family/variants/subsets/category/version/lastModified and
|
|
9
|
-
chooses the returned Antic regular/latin record.
|
|
10
|
-
4. Each of its five exact UAs asks for Antic:regular/latin. The twin returns the
|
|
11
|
-
corresponding real CSS, preserving @font-face style/weight and gstatic URL.
|
|
12
|
-
5. Download every returned URL. The genuine EOT/WOFF/WOFF2/SVG/TTF bytes can feed
|
|
13
|
-
its ordinary JSZip archive.
|
|
14
|
-
6. Dub's Open Graph route asks CSS2 for Inter at one weight from its server; the
|
|
15
|
-
twin answers Google's retained truetype CSS and the returned gstatic URL
|
|
16
|
-
downloads the genuine Inter bytes.
|
|
17
|
-
7. Rallly's Open Graph image asks CSS2 for the glyphs of its CJK title (text=) from
|
|
18
|
-
its server; the twin answers Google's retained CSS for that request and the
|
|
19
|
-
returned URL downloads the genuine truetype subset.
|
|
20
|
-
|
|
21
|
-
The customer entry uses its pinned axios 1.20.0 and the ordinary env name.
|
|
22
|
-
Its prerequisite publisher operation uses the included actual source snapshot.
|
|
23
|
-
No journey, customer entry or application execution has been performed while
|
|
24
|
-
building; executable recipe data is retained for the final qualification phase.
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"judge": {
|
|
3
|
-
"agent": "Claude journey judge (Opus)",
|
|
4
|
-
"name": "Independent journey judge"
|
|
5
|
-
},
|
|
6
|
-
"authors": [
|
|
7
|
-
"yueranyuan",
|
|
8
|
-
"Aaron (A-001)",
|
|
9
|
-
"Claude Opus 5.5",
|
|
10
|
-
"Codex"
|
|
11
|
-
],
|
|
12
|
-
"sha256": "0eac01045d8500b03363a11644b863adf041b345d6f3edf1e11bd8a3944d80e3",
|
|
13
|
-
"judgedAt": "2026-10-15",
|
|
14
|
-
"plausible": true,
|
|
15
|
-
"rulings": [
|
|
16
|
-
{
|
|
17
|
-
"id": "refusals-without-cause",
|
|
18
|
-
"steps": [
|
|
19
|
-
"act 2",
|
|
20
|
-
"act 5",
|
|
21
|
-
"act 6",
|
|
22
|
-
"act 7"
|
|
23
|
-
],
|
|
24
|
-
"barItem": "a refusal without a cause the story shows, or the same cause drawn twice; a run of refusals",
|
|
25
|
-
"reason": "Act 2 refuses 'a key never issued by this World', act 5 refuses 'a URL without a published blob', act 6 refuses text= given twice and act 7 keeps 'an unpublished CSS representation' as a refused gap, but no actor in the story sends any of these: the pinned helper always sends the World's issued key (demand.json, fetchGoogleFonts.ts:62) and downloads only URLs Google's CSS returned, Dub sends no text= and Rallly sends one (https://developers.google.com/fonts/docs/css2: \"the 'text=' parameter can only be specified once\", so nobody here sends two). Together with act 6's other refusals they make a closing run of refusals.",
|
|
26
|
-
"correction": "Remove these four refusals from acts 2, 5, 6 and 7; keep a refusal only where an actor in the story has a reason to send that request."
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
"id": "refusals-google-answers",
|
|
30
|
-
"steps": [
|
|
31
|
-
"act 6"
|
|
32
|
-
],
|
|
33
|
-
"barItem": "vendor behaviour the vendor's documentation contradicts",
|
|
34
|
-
"reason": "Act 6 says a browser asking CSS2 and Rallly's CJK text subset 'are refused'. Google documents text= for exactly this case: \"This feature also works for international fonts, allowing you to specify UTF-8 characters\" (https://developers.google.com/fonts/docs/css2, https://developers.google.com/fonts/docs/getting_started). It also documents the stylesheet link as something placed in an HTML document for the browser to fetch (getting_started: 'add a special stylesheet link to your HTML document'). Google answers both requests with a stylesheet, and Rallly, named in the outline's title, is left with no successful call.",
|
|
35
|
-
"correction": "In act 6, have Rallly's Noto Sans SC wght@700 text= request answered with Google's retained CSS2 response and its truetype file (published in act 1), or take Rallly out of the title and story. Drop the browser refusal."
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
"id": "read-backs-without-need",
|
|
39
|
-
"steps": [
|
|
40
|
-
"act 2",
|
|
41
|
-
"act 6"
|
|
42
|
-
],
|
|
43
|
-
"barItem": "a read-back with no need: a list or get of what the story already holds, unless someone who does not hold it reads it",
|
|
44
|
-
"reason": "In act 2 the World asks for the key it was just issued again in the same sitting, before any resume. In act 6 the same CSS2 stylesheet is read a second time under a second server agent ('the same for node and undici'), but Dub is the only CSS2 caller and already holds it. That the two agents get the same answer is evidence for vendor/inter.json, not an act of anyone in the story.",
|
|
45
|
-
"correction": "Move the repeated key read into act 7's resume, where a re-booted World genuinely asks again, and drop the undici comparison from act 6."
|
|
46
|
-
}
|
|
47
|
-
],
|
|
48
|
-
"applied": {
|
|
49
|
-
"from": "7aa0eb6d1749ce9a2a716d778332a6a61257c666bcb5c50a4c89ecd333182ec4",
|
|
50
|
-
"wasPlausible": false,
|
|
51
|
-
"steps": [
|
|
52
|
-
"act 2",
|
|
53
|
-
"act 5",
|
|
54
|
-
"act 6",
|
|
55
|
-
"act 7"
|
|
56
|
-
],
|
|
57
|
-
"rulings": [
|
|
58
|
-
{
|
|
59
|
-
"steps": [
|
|
60
|
-
"act 2",
|
|
61
|
-
"act 5",
|
|
62
|
-
"act 6",
|
|
63
|
-
"act 7"
|
|
64
|
-
],
|
|
65
|
-
"why": "Remove these four refusals from acts 2, 5, 6 and 7; keep a refusal only where an actor in the story has a reason to send that request."
|
|
66
|
-
},
|
|
67
|
-
{
|
|
68
|
-
"steps": [
|
|
69
|
-
"act 6"
|
|
70
|
-
],
|
|
71
|
-
"why": "In act 6, have Rallly's Noto Sans SC wght@700 text= request answered with Google's retained CSS2 response and its truetype file (published in act 1), or take Rallly out of the title and story. Drop the browser refusal."
|
|
72
|
-
},
|
|
73
|
-
{
|
|
74
|
-
"steps": [
|
|
75
|
-
"act 2",
|
|
76
|
-
"act 6"
|
|
77
|
-
],
|
|
78
|
-
"why": "Move the repeated key read into act 7's resume, where a re-booted World genuinely asks again, and drop the undici comparison from act 6."
|
|
79
|
-
}
|
|
80
|
-
]
|
|
81
|
-
}
|
|
82
|
-
}
|