@swiftbrowser/web 0.8.0 → 0.9.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.
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAKH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAKvC,OAAO,EAAmD,KAAK,UAAU,EAAE,KAAK,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAChI,OAAO,EAA0B,KAAK,iBAAiB,EAAwD,MAAM,0BAA0B,CAAC;AAEhJ,OAAO,EAAmC,KAAK,OAAO,EAAE,MAAM,WAAW,CAAC;AAE1E,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,OAAO,EAAE,CAAC;IAChB,iFAAiF;IACjF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,+EAA+E;IAC/E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB;;;OAGG;IACH,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,sGAAsG;IACtG,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB;;;OAGG;IACH,UAAU,CAAC,EAAE,iBAAiB,CAAC;CAChC;AAED,kEAAkE;AAClE,wBAAgB,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC,GAAG,UAAU,CAG9F;AAED,qGAAqG;AACrG,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wDAAwD;IACxD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,oHAAoH;IACpH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,yGAAyG;IACzG,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,0GAA0G;IAC1G,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uEAAuE;IACvE,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAOD;;;;;GAKG;AACH,wBAAgB,OAAO,IAAI,MAAM,CAoBhC;AAED,gDAAgD;AAChD,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS,OAAO,EAAE,GAAG,UAAU,EAAE,CAWnE;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,WAAW,GAAG,UAAU,CA4DjE"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAKH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAKvC,OAAO,EAAmD,KAAK,UAAU,EAAE,KAAK,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAChI,OAAO,EAAE,KAAK,iBAAiB,EAAyC,MAAM,0BAA0B,CAAC;AAEzG,OAAO,EAAmC,KAAK,OAAO,EAAE,MAAM,WAAW,CAAC;AAE1E,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,OAAO,EAAE,CAAC;IAChB,iFAAiF;IACjF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,+EAA+E;IAC/E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB;;;OAGG;IACH,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,sGAAsG;IACtG,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB;;;OAGG;IACH,UAAU,CAAC,EAAE,iBAAiB,CAAC;CAChC;AAED,kEAAkE;AAClE,wBAAgB,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC,GAAG,UAAU,CAG9F;AAED,qGAAqG;AACrG,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wDAAwD;IACxD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,oHAAoH;IACpH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,yGAAyG;IACzG,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,0GAA0G;IAC1G,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uEAAuE;IACvE,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAOD;;;;;GAKG;AACH,wBAAgB,OAAO,IAAI,MAAM,CAoBhC;AAED,gDAAgD;AAChD,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS,OAAO,EAAE,GAAG,UAAU,EAAE,CAWnE;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,WAAW,GAAG,UAAU,CAmEjE"}
@@ -20,7 +20,7 @@ import { assetCatalogs } from '../plugins/asset-catalogs.js';
20
20
  import { bundleResources } from '../plugins/bundle-resources.js';
21
21
  import { includedSites, readSiteApps } from '../plugins/included-sites.js';
22
22
  import { ROOT_START_URL, siteLayoutPlugin } from '../plugins/site-layout.js';
23
- import { PLAYGROUND_ASSETS_PATH, playgroundAliases, playgroundAssets, playgroundFiles } from '../plugins/playground.js';
23
+ import { playgroundAliases, playgroundPackages } from '../plugins/playground.js';
24
24
  import { swiftWasm } from '../plugins/swift-wasm.js';
25
25
  import { displayNameOf, validateAppSpecs } from './apps.js';
26
26
  /** The layout `options` resolve to (see `SiteOptions.layout`). */
@@ -122,9 +122,17 @@ export function createViteConfig(options) {
122
122
  bundleResources(apps),
123
123
  includedSites(landing, sites),
124
124
  siteLayoutPlugin(site, main),
125
- ...(options.playground ? [playgroundAssets(options.playground)] : []),
125
+ ...(options.playground ? [playgroundPackages(path.resolve(root, '..'), options.playground)] : []),
126
126
  ],
127
- ...(options.playground ? { resolve: { alias: playgroundAliases(path.resolve(root, '..')) }, worker: { format: 'es' } } : {}),
127
+ ...(options.playground
128
+ ? {
129
+ resolve: { alias: playgroundAliases(path.resolve(root, '..')) },
130
+ worker: { format: 'es' },
131
+ // Found only once the worker loads it, it would make the dev server
132
+ // reload the page mid-download.
133
+ optimizeDeps: { include: ['brotli-dec-wasm/web'] },
134
+ }
135
+ : {}),
128
136
  // The landing page at `/` and the app at `/app/` (or the app alone). `mpa`
129
137
  // keeps the dev server from answering unknown URLs with a page.
130
138
  appType: 'mpa',
@@ -135,8 +143,7 @@ export function createViteConfig(options) {
135
143
  __SB_COMMIT__: JSON.stringify(options.commit ?? 'dev'),
136
144
  __SB_BRANCH__: JSON.stringify(options.branch ?? ''),
137
145
  __SB_BUILT_AT__: JSON.stringify(new Date().toISOString()),
138
- __SB_PLAYGROUND_ASSETS__: JSON.stringify(options.playground?.assetsUrl ?? PLAYGROUND_ASSETS_PATH),
139
- __SB_PLAYGROUND_FILES__: JSON.stringify(options.playground ? playgroundFiles(options.playground) : null),
146
+ __SB_PLAYGROUND__: JSON.stringify(options.playground ?? null),
140
147
  },
141
148
  server: {
142
149
  port: 5173,
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../../config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../config.ts"],"names":[],"mappings":"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"}
@@ -3,24 +3,57 @@
3
3
  * by the WebAssembly compiler (docs/in-browser-compiler.md, Milestone 3).
4
4
  * Only this repository's site has it: its code imports
5
5
  * @swiftbrowser/wasm-compiler and the CLI's browser entry from their sources
6
- * (the aliases below). playground/build-assets.ts writes the assets and
7
- * their manifest to a directory; the build reads the manifest, and the page
8
- * fetches the files from /playground-assets/, where the dev server serves
9
- * that directory, or, deployed, from where they were uploaded
10
- * (playground/publish-assets.mjs).
6
+ * (the aliases below). The page fetches the npm packages it reads
7
+ * (src/playground/files.ts) from a CDN that serves them file by file, at one
8
+ * version, except those the site serves itself (`localPackages`): the kit
9
+ * and @swiftbrowser/playground made from the same commit as the site's
10
+ * renderer, so the two cannot drift, and any package the CDN does not have
11
+ * at that version. The dev server serves this repository's packages at
12
+ * /npm/, laid out as the CDN serves them, and a built site carries the
13
+ * files the page reads of its local packages there.
11
14
  */
12
15
  import type { Plugin } from 'vite';
13
- import { type PlaygroundFiles } from '../src/playground/files.js';
14
- export interface PlaygroundOptions {
15
- /** The directory build-assets wrote: its manifest, and the files the dev server serves at /playground-assets/. */
16
- assetsDir: string;
17
- /** Where the page fetches the assets from; default /playground-assets/. */
18
- assetsUrl?: string;
19
- }
20
- /** The downloads the assets' manifest names. */
21
- export declare function playgroundFiles(options: PlaygroundOptions): PlaygroundFiles;
22
- export declare const PLAYGROUND_ASSETS_PATH = "/playground-assets/";
16
+ import { type PlaygroundManifest, type PlaygroundSource } from '../src/playground/files.js';
17
+ export type PlaygroundOptions = PlaygroundSource;
18
+ /** Where the dev server serves the workspaces' packages, and a built site its local ones. */
19
+ export declare const PLAYGROUND_NPM_PATH = "/npm/";
20
+ /** The kit, which must come from the same commit as the renderer. */
21
+ export declare const KIT_PACKAGE = "@swiftbrowser/kit";
22
+ /**
23
+ * Files a site stores brotli-compressed past this size: Cloudflare Pages
24
+ * serves files of up to 25 MiB.
25
+ */
26
+ export declare const SITE_FILE_LIMIT: number;
27
+ /**
28
+ * Files a site stores brotli-compressed from this size, when compressing
29
+ * saves a tenth: a static host may not compress what it serves in transit
30
+ * (Cloudflare Pages leaves application/octet-stream as it is), and the
31
+ * archives and modules compress to about a fifth.
32
+ */
33
+ export declare const SITE_COMPRESS_FROM: number;
23
34
  /** The source aliases of the playground's imports, from the repository root. */
24
35
  export declare function playgroundAliases(repoRoot: string): Record<string, string>;
25
- export declare function playgroundAssets(options: PlaygroundOptions): Plugin;
36
+ /** The workspaces' package directories by package name. */
37
+ export declare function workspacePackages(repoRoot: string): Map<string, string>;
38
+ /** `@scope/name@version/file` → [name, file], whatever the version. */
39
+ export declare function packageRequest(pathname: string): [string, string] | null;
40
+ /** Every file of `manifest` by package, as the package holds it. */
41
+ export declare function manifestFiles(manifest: PlaygroundManifest): Map<string, string[]>;
42
+ /**
43
+ * The packages the site serves itself: @swiftbrowser/playground and the kit
44
+ * always, and `others`, each at /npm/<name>@site-<hash>/, the hash naming
45
+ * what the workspaces hold so a browser's cache keeps no stale file.
46
+ */
47
+ export declare function localPackages(repoRoot: string, others?: readonly string[]): Record<string, string>;
48
+ /**
49
+ * Writes the files the page reads of each local package under `outDir`, at
50
+ * the paths `packages` names: brotli-compressed as `<file>.brotli` from
51
+ * SITE_COMPRESS_FROM (and always over the size limit), and playground.json
52
+ * naming those (`brotli`).
53
+ */
54
+ export declare function writeLocalPackages(repoRoot: string, packages: Record<string, string>, outDir: string, limit?: number, compressFrom?: number): {
55
+ files: number;
56
+ bytes: number;
57
+ };
58
+ export declare function playgroundPackages(repoRoot: string, options: PlaygroundOptions): Plugin;
26
59
  //# sourceMappingURL=playground.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"playground.d.ts","sourceRoot":"","sources":["../../../plugins/playground.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AACnC,OAAO,EAAuB,KAAK,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAEvF,MAAM,WAAW,iBAAiB;IAChC,kHAAkH;IAClH,SAAS,EAAE,MAAM,CAAC;IAClB,2EAA2E;IAC3E,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,gDAAgD;AAChD,wBAAgB,eAAe,CAAC,OAAO,EAAE,iBAAiB,GAAG,eAAe,CAI3E;AAED,eAAO,MAAM,sBAAsB,wBAAwB,CAAC;AAE5D,gFAAgF;AAChF,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAK1E;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,iBAAiB,GAAG,MAAM,CAcnE"}
1
+ {"version":3,"file":"playground.d.ts","sourceRoot":"","sources":["../../../plugins/playground.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAMH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AACnC,OAAO,EAQL,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,EACtB,MAAM,4BAA4B,CAAC;AAEpC,MAAM,MAAM,iBAAiB,GAAG,gBAAgB,CAAC;AAEjD,6FAA6F;AAC7F,eAAO,MAAM,mBAAmB,UAAU,CAAC;AAC3C,qEAAqE;AACrE,eAAO,MAAM,WAAW,sBAAsB,CAAC;AAC/C;;;GAGG;AACH,eAAO,MAAM,eAAe,QAAmB,CAAC;AAIhD;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,QAAY,CAAC;AAE5C,gFAAgF;AAChF,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAK1E;AAED,2DAA2D;AAC3D,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAQvE;AAED,uEAAuE;AACvE,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAGxE;AAED,oEAAoE;AACpE,wBAAgB,aAAa,CAAC,QAAQ,EAAE,kBAAkB,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAQjF;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAE,SAAS,MAAM,EAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CActG;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,SAAkB,EAAE,YAAY,SAAqB,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAkDnM;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,MAAM,CA0BvF"}
@@ -3,23 +3,38 @@
3
3
  * by the WebAssembly compiler (docs/in-browser-compiler.md, Milestone 3).
4
4
  * Only this repository's site has it: its code imports
5
5
  * @swiftbrowser/wasm-compiler and the CLI's browser entry from their sources
6
- * (the aliases below). playground/build-assets.ts writes the assets and
7
- * their manifest to a directory; the build reads the manifest, and the page
8
- * fetches the files from /playground-assets/, where the dev server serves
9
- * that directory, or, deployed, from where they were uploaded
10
- * (playground/publish-assets.mjs).
6
+ * (the aliases below). The page fetches the npm packages it reads
7
+ * (src/playground/files.ts) from a CDN that serves them file by file, at one
8
+ * version, except those the site serves itself (`localPackages`): the kit
9
+ * and @swiftbrowser/playground made from the same commit as the site's
10
+ * renderer, so the two cannot drift, and any package the CDN does not have
11
+ * at that version. The dev server serves this repository's packages at
12
+ * /npm/, laid out as the CDN serves them, and a built site carries the
13
+ * files the page reads of its local packages there.
11
14
  */
12
- import { createReadStream, existsSync, readFileSync, statSync } from 'node:fs';
15
+ import { createHash } from 'node:crypto';
16
+ import { createReadStream, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, statSync, writeFileSync } from 'node:fs';
13
17
  import path from 'node:path';
14
- import { PLAYGROUND_MANIFEST } from '../src/playground/files.js';
15
- /** The downloads the assets' manifest names. */
16
- export function playgroundFiles(options) {
17
- const manifest = path.join(options.assetsDir, PLAYGROUND_MANIFEST);
18
- if (!existsSync(manifest))
19
- throw new Error(`${manifest} is missing: build the playground's assets (playground/build-assets.ts)`);
20
- return JSON.parse(readFileSync(manifest, 'utf8'));
21
- }
22
- export const PLAYGROUND_ASSETS_PATH = '/playground-assets/';
18
+ import { brotliCompressSync, constants } from 'node:zlib';
19
+ import { PLAYGROUND_ICU, PLAYGROUND_MANIFEST, PLAYGROUND_MODULE_CACHE, PLAYGROUND_PACKAGE, SITE_BROTLI, isBrotli, } from '../src/playground/files.js';
20
+ /** Where the dev server serves the workspaces' packages, and a built site its local ones. */
21
+ export const PLAYGROUND_NPM_PATH = '/npm/';
22
+ /** The kit, which must come from the same commit as the renderer. */
23
+ export const KIT_PACKAGE = '@swiftbrowser/kit';
24
+ /**
25
+ * Files a site stores brotli-compressed past this size: Cloudflare Pages
26
+ * serves files of up to 25 MiB.
27
+ */
28
+ export const SITE_FILE_LIMIT = 24 * 1024 * 1024;
29
+ /** Bumped whenever writeLocalPackages writes other files for the same packages. */
30
+ const SITE_LAYOUT = 2;
31
+ /**
32
+ * Files a site stores brotli-compressed from this size, when compressing
33
+ * saves a tenth: a static host may not compress what it serves in transit
34
+ * (Cloudflare Pages leaves application/octet-stream as it is), and the
35
+ * archives and modules compress to about a fifth.
36
+ */
37
+ export const SITE_COMPRESS_FROM = 64 * 1024;
23
38
  /** The source aliases of the playground's imports, from the repository root. */
24
39
  export function playgroundAliases(repoRoot) {
25
40
  return {
@@ -27,20 +42,151 @@ export function playgroundAliases(repoRoot) {
27
42
  '@swiftbrowser/cli/browser': path.join(repoRoot, 'packages/cli/src/browser.ts'),
28
43
  };
29
44
  }
30
- export function playgroundAssets(options) {
45
+ /** The workspaces' package directories by package name. */
46
+ export function workspacePackages(repoRoot) {
47
+ const dirs = new Map();
48
+ const packagesDir = path.join(repoRoot, 'packages');
49
+ for (const name of readdirSync(packagesDir)) {
50
+ const manifest = path.join(packagesDir, name, 'package.json');
51
+ if (existsSync(manifest))
52
+ dirs.set(JSON.parse(readFileSync(manifest, 'utf8')).name, path.join(packagesDir, name));
53
+ }
54
+ return dirs;
55
+ }
56
+ /** `@scope/name@version/file` → [name, file], whatever the version. */
57
+ export function packageRequest(pathname) {
58
+ const match = /^(@[^/]+\/[^/@]+)@[^/]+\/(.+)$/.exec(pathname);
59
+ return match ? [match[1], match[2]] : null;
60
+ }
61
+ /** Every file of `manifest` by package, as the package holds it. */
62
+ export function manifestFiles(manifest) {
63
+ const files = new Map();
64
+ const add = ({ package: name, file }) => void (files.get(name) ?? files.set(name, []).get(name)).push(file);
65
+ for (const file of [manifest.frontend, manifest.linker, ...Object.values(manifest.plugins)])
66
+ add(file);
67
+ for (const [name, list] of Object.entries(manifest.sdk))
68
+ for (const file of list)
69
+ add({ package: name, file: `wasm32-unknown-wasip1/${file}` });
70
+ for (const file of [...manifest.kit, 'kit.json'])
71
+ add({ package: KIT_PACKAGE, file });
72
+ for (const file of [PLAYGROUND_MANIFEST, PLAYGROUND_ICU, PLAYGROUND_MODULE_CACHE])
73
+ add({ package: PLAYGROUND_PACKAGE, file });
74
+ return files;
75
+ }
76
+ /**
77
+ * The packages the site serves itself: @swiftbrowser/playground and the kit
78
+ * always, and `others`, each at /npm/<name>@site-<hash>/, the hash naming
79
+ * what the workspaces hold so a browser's cache keeps no stale file.
80
+ */
81
+ export function localPackages(repoRoot, others = []) {
82
+ const dirs = workspacePackages(repoRoot);
83
+ const own = dirs.get(PLAYGROUND_PACKAGE);
84
+ const manifestFile = path.join(own, PLAYGROUND_MANIFEST);
85
+ if (!existsSync(manifestFile))
86
+ throw new Error(`${manifestFile} is missing: run \`npm run playground:assets --workspace @swiftbrowser/web\``);
87
+ const hash = createHash('sha256');
88
+ for (const file of [PLAYGROUND_MANIFEST, PLAYGROUND_ICU, PLAYGROUND_MODULE_CACHE])
89
+ hash.update(readFileSync(path.join(own, file)));
90
+ hash.update(readFileSync(path.join(dirs.get(KIT_PACKAGE), 'kit.json')));
91
+ const names = [...new Set([PLAYGROUND_PACKAGE, KIT_PACKAGE, ...others])].sort();
92
+ for (const name of names)
93
+ hash.update(`${name}\n`);
94
+ // And how writeLocalPackages lays them out: another layout is other files.
95
+ hash.update(`layout ${SITE_LAYOUT} ${SITE_FILE_LIMIT} ${SITE_COMPRESS_FROM}\n`);
96
+ const version = `site-${hash.digest('hex').slice(0, 12)}`;
97
+ return Object.fromEntries(names.map((name) => [name, `${PLAYGROUND_NPM_PATH}${name}@${version}/`]));
98
+ }
99
+ /**
100
+ * Writes the files the page reads of each local package under `outDir`, at
101
+ * the paths `packages` names: brotli-compressed as `<file>.brotli` from
102
+ * SITE_COMPRESS_FROM (and always over the size limit), and playground.json
103
+ * naming those (`brotli`).
104
+ */
105
+ export function writeLocalPackages(repoRoot, packages, outDir, limit = SITE_FILE_LIMIT, compressFrom = SITE_COMPRESS_FROM) {
106
+ const dirs = workspacePackages(repoRoot);
107
+ const manifest = JSON.parse(readFileSync(path.join(dirs.get(PLAYGROUND_PACKAGE), PLAYGROUND_MANIFEST), 'utf8'));
108
+ const brotli = new Set();
109
+ const rename = (file) => (brotli.has(`${file.package}/${file.file}`) ? { ...file, file: `${file.file}${SITE_BROTLI}` } : file);
110
+ const written = { files: 0, bytes: 0 };
111
+ for (const [name, files] of manifestFiles(manifest)) {
112
+ const base = packages[name];
113
+ if (!base)
114
+ continue;
115
+ const dir = dirs.get(name);
116
+ if (!dir)
117
+ throw new Error(`the site serves ${name}, which is not a package of this repository`);
118
+ for (const file of files) {
119
+ if (name === PLAYGROUND_PACKAGE && file === PLAYGROUND_MANIFEST)
120
+ continue;
121
+ let data = readFileSync(path.join(dir, file));
122
+ let target = file;
123
+ if (isBrotli(file)) {
124
+ if (data.length > limit)
125
+ throw new Error(`${name}/${file} is over ${limit} bytes compressed: the site cannot serve it`);
126
+ }
127
+ else if (data.length >= compressFrom || data.length > limit) {
128
+ const compressed = brotliCompressSync(data, { params: { [constants.BROTLI_PARAM_QUALITY]: 5, [constants.BROTLI_PARAM_LGWIN]: 24, [constants.BROTLI_PARAM_SIZE_HINT]: data.length } });
129
+ if (compressed.length > limit)
130
+ throw new Error(`${name}/${file} is over ${limit} bytes even compressed`);
131
+ if (data.length > limit || compressed.length < data.length * 0.9) {
132
+ data = compressed;
133
+ brotli.add(`${name}/${file}`);
134
+ target = `${file}${SITE_BROTLI}`;
135
+ }
136
+ }
137
+ const out = path.join(outDir, base, target);
138
+ mkdirSync(path.dirname(out), { recursive: true });
139
+ writeFileSync(out, data);
140
+ written.files += 1;
141
+ written.bytes += data.length;
142
+ }
143
+ }
144
+ if (packages[PLAYGROUND_PACKAGE]) {
145
+ // A tool or plugin is named by its compressed file; the rest by the list.
146
+ const tools = new Set([manifest.frontend, manifest.linker, ...Object.values(manifest.plugins)].map((file) => `${file.package}/${file.file}`));
147
+ const listed = [...brotli].filter((entry) => !tools.has(entry)).sort();
148
+ const site = {
149
+ ...manifest,
150
+ frontend: rename(manifest.frontend),
151
+ linker: rename(manifest.linker),
152
+ plugins: Object.fromEntries(Object.entries(manifest.plugins).map(([macro, file]) => [macro, rename(file)])),
153
+ ...(listed.length > 0 ? { brotli: listed } : {}),
154
+ };
155
+ const out = path.join(outDir, packages[PLAYGROUND_PACKAGE], PLAYGROUND_MANIFEST);
156
+ mkdirSync(path.dirname(out), { recursive: true });
157
+ writeFileSync(out, `${JSON.stringify(site)}\n`);
158
+ written.files += 1;
159
+ }
160
+ return written;
161
+ }
162
+ export function playgroundPackages(repoRoot, options) {
163
+ let outDir = '';
31
164
  return {
32
- name: 'swiftbrowser-playground-assets',
165
+ name: 'swiftbrowser-playground-packages',
166
+ configResolved(config) {
167
+ outDir = path.resolve(config.root, config.build.outDir);
168
+ },
33
169
  configureServer(server) {
34
- const dir = path.resolve(options.assetsDir);
35
- server.middlewares.use(PLAYGROUND_ASSETS_PATH, (req, res, next) => {
36
- const file = path.join(dir, decodeURIComponent(new URL(req.url ?? '/', 'http://x').pathname));
37
- if (!file.startsWith(dir) || !existsSync(file) || statSync(file).isDirectory())
170
+ const packages = workspacePackages(repoRoot);
171
+ server.middlewares.use(PLAYGROUND_NPM_PATH, (req, res, next) => {
172
+ const request = packageRequest(decodeURIComponent(new URL(req.url ?? '/', 'http://x').pathname).replace(/^\//, ''));
173
+ const dir = request && packages.get(request[0]);
174
+ if (!request || !dir)
38
175
  return next();
39
- res.setHeader('Content-Type', file.endsWith('.gz') ? 'application/gzip' : 'application/octet-stream');
176
+ const file = path.join(dir, request[1]);
177
+ if (!existsSync(file) || statSync(file).isDirectory() || !realpathSync(file).startsWith(realpathSync(dir)))
178
+ return next();
179
+ res.setHeader('Content-Type', file.endsWith('.wasm') ? 'application/wasm' : file.endsWith('.json') ? 'application/json' : 'application/octet-stream');
40
180
  res.setHeader('Content-Length', statSync(file).size);
41
181
  createReadStream(file).pipe(res);
42
182
  });
43
183
  },
184
+ writeBundle() {
185
+ if (!options.packages)
186
+ return;
187
+ const { files, bytes } = writeLocalPackages(repoRoot, options.packages, outDir);
188
+ this.info(`the playground's local packages: ${files} files, ${(bytes / 1e6).toFixed(1)} MB`);
189
+ },
44
190
  };
45
191
  }
46
192
  //# sourceMappingURL=playground.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"playground.js","sourceRoot":"","sources":["../../../plugins/playground.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,gBAAgB,EAAE,UAAU,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC/E,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,mBAAmB,EAAwB,MAAM,4BAA4B,CAAC;AASvF,gDAAgD;AAChD,MAAM,UAAU,eAAe,CAAC,OAA0B;IACxD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,mBAAmB,CAAC,CAAC;IACnE,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,GAAG,QAAQ,yEAAyE,CAAC,CAAC;IACjI,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAoB,CAAC;AACvE,CAAC;AAED,MAAM,CAAC,MAAM,sBAAsB,GAAG,qBAAqB,CAAC;AAE5D,gFAAgF;AAChF,MAAM,UAAU,iBAAiB,CAAC,QAAgB;IAChD,OAAO;QACL,6BAA6B,EAAE,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,qCAAqC,CAAC;QACzF,2BAA2B,EAAE,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,6BAA6B,CAAC;KAChF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,OAA0B;IACzD,OAAO;QACL,IAAI,EAAE,gCAAgC;QACtC,eAAe,CAAC,MAAM;YACpB,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;YAC5C,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,sBAAsB,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;gBAChE,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,kBAAkB,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,EAAE,UAAU,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;gBAC9F,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE;oBAAE,OAAO,IAAI,EAAE,CAAC;gBAC9F,GAAG,CAAC,SAAS,CAAC,cAAc,EAAE,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,0BAA0B,CAAC,CAAC;gBACtG,GAAG,CAAC,SAAS,CAAC,gBAAgB,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC;gBACrD,gBAAgB,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACnC,CAAC,CAAC,CAAC;QACL,CAAC;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"playground.js","sourceRoot":"","sources":["../../../plugins/playground.ts"],"names":[],"mappings":"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"}
@@ -1,12 +1,70 @@
1
- export interface PlaygroundFiles {
2
- /** The compiler (`swift-frontend-<hash>.wasm.gz`). */
3
- frontend: string;
4
- /** The linker (`wasm-ld-<hash>.wasm.gz`). */
5
- linker: string;
6
- /** The SDK and kit files, ICU data and module cache (`playground-<hash>.pack.gz`). */
7
- pack: string;
1
+ export declare const PLAYGROUND_PACKAGE = "@swiftbrowser/playground";
2
+ export declare const PLAYGROUND_MANIFEST = "playground.json";
3
+ /** The trimmed ICU data, in @swiftbrowser/playground. */
4
+ export declare const PLAYGROUND_ICU = "icu.dat";
5
+ /** The module cache, packed (pack.ts), in @swiftbrowser/playground. */
6
+ export declare const PLAYGROUND_MODULE_CACHE = "module-cache.pack";
7
+ /** Where the npm packages are fetched from by default. */
8
+ export declare const JSDELIVR = "https://cdn.jsdelivr.net/npm/";
9
+ /** A file of one of the packages. */
10
+ export interface PackageFile {
11
+ package: string;
12
+ /** The path in the package; `.br` files are brotli-compressed. */
13
+ file: string;
14
+ }
15
+ /** @swiftbrowser/playground's playground.json. */
16
+ export interface PlaygroundManifest {
17
+ /** The symbol Foundation's ICU reads its data through (`icudtNN_dat`). */
18
+ icuSymbol: string;
19
+ /** The apps whose compiles recorded the files. */
20
+ apps: string[];
21
+ frontend: PackageFile;
22
+ linker: PackageFile;
8
23
  /** The macro plugins by the module whose macros they expand. */
9
- plugins: Record<string, string>;
24
+ plugins: Record<string, PackageFile>;
25
+ /**
26
+ * The Swift SDK's files a compile reads, by package, each relative to the
27
+ * SDK's `wasm32-unknown-wasip1` directory (the package's own
28
+ * `wasm32-unknown-wasip1/` holds it there).
29
+ */
30
+ sdk: Record<string, string[]>;
31
+ /** The kit's files a compile reads, relative to @swiftbrowser/kit (`wasm32-unknown-wasip1/...`). */
32
+ kit: string[];
33
+ /**
34
+ * The SwiftBrowser sources the kit was built from (@swiftbrowser/swift's
35
+ * `sourcesHash`), when build-assets.ts could tell: the page compares it
36
+ * with the sources its own renderer was built from.
37
+ */
38
+ shimSources?: string;
39
+ /**
40
+ * Files, as `<package>/<path>`, that the site serving them stores
41
+ * brotli-compressed (`<path>.brotli`, SITE_BROTLI): an SDK or kit file,
42
+ * the ICU data or the module cache (plugins/playground.ts). A tool or
43
+ * plugin is named by its compressed file instead.
44
+ */
45
+ brotli?: string[];
46
+ }
47
+ /**
48
+ * The suffix of a file a site stores brotli-compressed. Not `.br`: a static
49
+ * server may send a `.br` file as `Content-Encoding: br`, which the browser
50
+ * unpacks before the page could.
51
+ */
52
+ export declare const SITE_BROTLI = ".brotli";
53
+ /** Whether a file is brotli-compressed by its name: an npm package's `.br`, or a site's. */
54
+ export declare const isBrotli: (file: string) => boolean;
55
+ /** Where the packages come from. */
56
+ export interface PlaygroundSource {
57
+ /** A CDN's npm base URL, which serves every package at `version`... */
58
+ npm: string;
59
+ version: string;
60
+ /**
61
+ * ...but those named here, each served from its own base URL: the site's
62
+ * copy, made from the same commit as its renderer (plugins/playground.ts).
63
+ */
64
+ packages?: Record<string, string>;
65
+ /** The SwiftBrowser sources the site's renderer was built from (`sourcesHash`). */
66
+ shimSources?: string;
10
67
  }
11
- export declare const PLAYGROUND_MANIFEST = "assets.json";
68
+ /** The URL of a package's file. */
69
+ export declare function packageUrl(source: PlaygroundSource, name: string, file: string): string;
12
70
  //# sourceMappingURL=files.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"files.d.ts","sourceRoot":"","sources":["../../../../src/playground/files.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,eAAe;IAC9B,sDAAsD;IACtD,QAAQ,EAAE,MAAM,CAAC;IACjB,6CAA6C;IAC7C,MAAM,EAAE,MAAM,CAAC;IACf,sFAAsF;IACtF,IAAI,EAAE,MAAM,CAAC;IACb,gEAAgE;IAChE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AAED,eAAO,MAAM,mBAAmB,gBAAgB,CAAC"}
1
+ {"version":3,"file":"files.d.ts","sourceRoot":"","sources":["../../../../src/playground/files.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,kBAAkB,6BAA6B,CAAC;AAC7D,eAAO,MAAM,mBAAmB,oBAAoB,CAAC;AACrD,yDAAyD;AACzD,eAAO,MAAM,cAAc,YAAY,CAAC;AACxC,uEAAuE;AACvE,eAAO,MAAM,uBAAuB,sBAAsB,CAAC;AAC3D,0DAA0D;AAC1D,eAAO,MAAM,QAAQ,kCAAkC,CAAC;AAExD,qCAAqC;AACrC,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,kEAAkE;IAClE,IAAI,EAAE,MAAM,CAAC;CACd;AAED,kDAAkD;AAClD,MAAM,WAAW,kBAAkB;IACjC,0EAA0E;IAC1E,SAAS,EAAE,MAAM,CAAC;IAClB,kDAAkD;IAClD,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,QAAQ,EAAE,WAAW,CAAC;IACtB,MAAM,EAAE,WAAW,CAAC;IACpB,gEAAgE;IAChE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACrC;;;;OAIG;IACH,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAC9B,oGAAoG;IACpG,GAAG,EAAE,MAAM,EAAE,CAAC;IACd;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;;;GAIG;AACH,eAAO,MAAM,WAAW,YAAY,CAAC;AAErC,4FAA4F;AAC5F,eAAO,MAAM,QAAQ,GAAI,MAAM,MAAM,KAAG,OAA6D,CAAC;AAEtG,oCAAoC;AACpC,MAAM,WAAW,gBAAgB;IAC/B,uEAAuE;IACvE,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,mFAAmF;IACnF,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,mCAAmC;AACnC,wBAAgB,UAAU,CAAC,MAAM,EAAE,gBAAgB,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAGvF"}
@@ -1,7 +1,30 @@
1
- // The playground's downloads by role, as playground/build-assets.ts names
2
- // them in assets.json: gzip files named by a hash of their contents, so any
3
- // cache may keep them forever and a new build is new names. The site's
4
- // build reads the manifest (plugins/playground.ts) and the page fetches
5
- // these names from the assets' base URL.
6
- export const PLAYGROUND_MANIFEST = 'assets.json';
1
+ // What the playground downloads, all from npm packages at one version (the
2
+ // release's lockstep version), through a CDN that serves npm packages file by
3
+ // file (jsDelivr): the compiler, the linker and the toolchain's macro plugins
4
+ // from @swiftbrowser/wasm-tools, the Swift SDK's files from the
5
+ // @swiftbrowser/wasm-sdk packages, the kit's from @swiftbrowser/kit, and what
6
+ // only the playground needs from @swiftbrowser/playground: its manifest
7
+ // (which files of those packages a compile reads), the trimmed ICU data and
8
+ // the module cache. playground/build-assets.ts makes that package.
9
+ export const PLAYGROUND_PACKAGE = '@swiftbrowser/playground';
10
+ export const PLAYGROUND_MANIFEST = 'playground.json';
11
+ /** The trimmed ICU data, in @swiftbrowser/playground. */
12
+ export const PLAYGROUND_ICU = 'icu.dat';
13
+ /** The module cache, packed (pack.ts), in @swiftbrowser/playground. */
14
+ export const PLAYGROUND_MODULE_CACHE = 'module-cache.pack';
15
+ /** Where the npm packages are fetched from by default. */
16
+ export const JSDELIVR = 'https://cdn.jsdelivr.net/npm/';
17
+ /**
18
+ * The suffix of a file a site stores brotli-compressed. Not `.br`: a static
19
+ * server may send a `.br` file as `Content-Encoding: br`, which the browser
20
+ * unpacks before the page could.
21
+ */
22
+ export const SITE_BROTLI = '.brotli';
23
+ /** Whether a file is brotli-compressed by its name: an npm package's `.br`, or a site's. */
24
+ export const isBrotli = (file) => file.endsWith('.br') || file.endsWith(SITE_BROTLI);
25
+ /** The URL of a package's file. */
26
+ export function packageUrl(source, name, file) {
27
+ const base = source.packages?.[name];
28
+ return base ? new URL(file, base).href : new URL(`${name}@${source.version}/${file}`, source.npm).href;
29
+ }
7
30
  //# sourceMappingURL=files.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"files.js","sourceRoot":"","sources":["../../../../src/playground/files.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,4EAA4E;AAC5E,uEAAuE;AACvE,wEAAwE;AACxE,yCAAyC;AAazC,MAAM,CAAC,MAAM,mBAAmB,GAAG,aAAa,CAAC"}
1
+ {"version":3,"file":"files.js","sourceRoot":"","sources":["../../../../src/playground/files.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,8EAA8E;AAC9E,8EAA8E;AAC9E,gEAAgE;AAChE,8EAA8E;AAC9E,wEAAwE;AACxE,4EAA4E;AAC5E,mEAAmE;AAEnE,MAAM,CAAC,MAAM,kBAAkB,GAAG,0BAA0B,CAAC;AAC7D,MAAM,CAAC,MAAM,mBAAmB,GAAG,iBAAiB,CAAC;AACrD,yDAAyD;AACzD,MAAM,CAAC,MAAM,cAAc,GAAG,SAAS,CAAC;AACxC,uEAAuE;AACvE,MAAM,CAAC,MAAM,uBAAuB,GAAG,mBAAmB,CAAC;AAC3D,0DAA0D;AAC1D,MAAM,CAAC,MAAM,QAAQ,GAAG,+BAA+B,CAAC;AA0CxD;;;;GAIG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,SAAS,CAAC;AAErC,4FAA4F;AAC5F,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,IAAY,EAAW,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;AAgBtG,mCAAmC;AACnC,MAAM,UAAU,UAAU,CAAC,MAAwB,EAAE,IAAY,EAAE,IAAY;IAC7E,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,EAAE,CAAC,IAAI,CAAC,CAAC;IACrC,OAAO,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,GAAG,IAAI,IAAI,MAAM,CAAC,OAAO,IAAI,IAAI,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;AACzG,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swiftbrowser/web",
3
- "version": "0.8.0",
3
+ "version": "0.9.0",
4
4
  "type": "module",
5
5
  "description": "SwiftBrowser web renderer and dev server: applies render ops from a Swift/Wasm app to the DOM inside an iPhone frame.",
6
6
  "repository": {
@@ -56,6 +56,7 @@
56
56
  "@playwright/test": "^1.56.0",
57
57
  "@types/node": "^26.6.4",
58
58
  "@types/qrcode": "^1.5.6",
59
+ "brotli-dec-wasm": "^2.3.2",
59
60
  "typescript": "^5.9.2",
60
61
  "vitest": "^5.0.3"
61
62
  }
@@ -3,33 +3,54 @@
3
3
  * by the WebAssembly compiler (docs/in-browser-compiler.md, Milestone 3).
4
4
  * Only this repository's site has it: its code imports
5
5
  * @swiftbrowser/wasm-compiler and the CLI's browser entry from their sources
6
- * (the aliases below). playground/build-assets.ts writes the assets and
7
- * their manifest to a directory; the build reads the manifest, and the page
8
- * fetches the files from /playground-assets/, where the dev server serves
9
- * that directory, or, deployed, from where they were uploaded
10
- * (playground/publish-assets.mjs).
6
+ * (the aliases below). The page fetches the npm packages it reads
7
+ * (src/playground/files.ts) from a CDN that serves them file by file, at one
8
+ * version, except those the site serves itself (`localPackages`): the kit
9
+ * and @swiftbrowser/playground made from the same commit as the site's
10
+ * renderer, so the two cannot drift, and any package the CDN does not have
11
+ * at that version. The dev server serves this repository's packages at
12
+ * /npm/, laid out as the CDN serves them, and a built site carries the
13
+ * files the page reads of its local packages there.
11
14
  */
12
15
 
13
- import { createReadStream, existsSync, readFileSync, statSync } from 'node:fs';
16
+ import { createHash } from 'node:crypto';
17
+ import { createReadStream, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, statSync, writeFileSync } from 'node:fs';
14
18
  import path from 'node:path';
19
+ import { brotliCompressSync, constants } from 'node:zlib';
15
20
  import type { Plugin } from 'vite';
16
- import { PLAYGROUND_MANIFEST, type PlaygroundFiles } from '../src/playground/files.js';
21
+ import {
22
+ PLAYGROUND_ICU,
23
+ PLAYGROUND_MANIFEST,
24
+ PLAYGROUND_MODULE_CACHE,
25
+ PLAYGROUND_PACKAGE,
26
+ SITE_BROTLI,
27
+ isBrotli,
28
+ type PackageFile,
29
+ type PlaygroundManifest,
30
+ type PlaygroundSource,
31
+ } from '../src/playground/files.js';
17
32
 
18
- export interface PlaygroundOptions {
19
- /** The directory build-assets wrote: its manifest, and the files the dev server serves at /playground-assets/. */
20
- assetsDir: string;
21
- /** Where the page fetches the assets from; default /playground-assets/. */
22
- assetsUrl?: string;
23
- }
33
+ export type PlaygroundOptions = PlaygroundSource;
24
34
 
25
- /** The downloads the assets' manifest names. */
26
- export function playgroundFiles(options: PlaygroundOptions): PlaygroundFiles {
27
- const manifest = path.join(options.assetsDir, PLAYGROUND_MANIFEST);
28
- if (!existsSync(manifest)) throw new Error(`${manifest} is missing: build the playground's assets (playground/build-assets.ts)`);
29
- return JSON.parse(readFileSync(manifest, 'utf8')) as PlaygroundFiles;
30
- }
35
+ /** Where the dev server serves the workspaces' packages, and a built site its local ones. */
36
+ export const PLAYGROUND_NPM_PATH = '/npm/';
37
+ /** The kit, which must come from the same commit as the renderer. */
38
+ export const KIT_PACKAGE = '@swiftbrowser/kit';
39
+ /**
40
+ * Files a site stores brotli-compressed past this size: Cloudflare Pages
41
+ * serves files of up to 25 MiB.
42
+ */
43
+ export const SITE_FILE_LIMIT = 24 * 1024 * 1024;
44
+ /** Bumped whenever writeLocalPackages writes other files for the same packages. */
45
+ const SITE_LAYOUT = 2;
31
46
 
32
- export const PLAYGROUND_ASSETS_PATH = '/playground-assets/';
47
+ /**
48
+ * Files a site stores brotli-compressed from this size, when compressing
49
+ * saves a tenth: a static host may not compress what it serves in transit
50
+ * (Cloudflare Pages leaves application/octet-stream as it is), and the
51
+ * archives and modules compress to about a fifth.
52
+ */
53
+ export const SITE_COMPRESS_FROM = 64 * 1024;
33
54
 
34
55
  /** The source aliases of the playground's imports, from the repository root. */
35
56
  export function playgroundAliases(repoRoot: string): Record<string, string> {
@@ -39,18 +60,137 @@ export function playgroundAliases(repoRoot: string): Record<string, string> {
39
60
  };
40
61
  }
41
62
 
42
- export function playgroundAssets(options: PlaygroundOptions): Plugin {
63
+ /** The workspaces' package directories by package name. */
64
+ export function workspacePackages(repoRoot: string): Map<string, string> {
65
+ const dirs = new Map<string, string>();
66
+ const packagesDir = path.join(repoRoot, 'packages');
67
+ for (const name of readdirSync(packagesDir)) {
68
+ const manifest = path.join(packagesDir, name, 'package.json');
69
+ if (existsSync(manifest)) dirs.set((JSON.parse(readFileSync(manifest, 'utf8')) as { name: string }).name, path.join(packagesDir, name));
70
+ }
71
+ return dirs;
72
+ }
73
+
74
+ /** `@scope/name@version/file` → [name, file], whatever the version. */
75
+ export function packageRequest(pathname: string): [string, string] | null {
76
+ const match = /^(@[^/]+\/[^/@]+)@[^/]+\/(.+)$/.exec(pathname);
77
+ return match ? [match[1]!, match[2]!] : null;
78
+ }
79
+
80
+ /** Every file of `manifest` by package, as the package holds it. */
81
+ export function manifestFiles(manifest: PlaygroundManifest): Map<string, string[]> {
82
+ const files = new Map<string, string[]>();
83
+ const add = ({ package: name, file }: PackageFile): void => void (files.get(name) ?? files.set(name, []).get(name)!).push(file);
84
+ for (const file of [manifest.frontend, manifest.linker, ...Object.values(manifest.plugins)]) add(file);
85
+ for (const [name, list] of Object.entries(manifest.sdk)) for (const file of list) add({ package: name, file: `wasm32-unknown-wasip1/${file}` });
86
+ for (const file of [...manifest.kit, 'kit.json']) add({ package: KIT_PACKAGE, file });
87
+ for (const file of [PLAYGROUND_MANIFEST, PLAYGROUND_ICU, PLAYGROUND_MODULE_CACHE]) add({ package: PLAYGROUND_PACKAGE, file });
88
+ return files;
89
+ }
90
+
91
+ /**
92
+ * The packages the site serves itself: @swiftbrowser/playground and the kit
93
+ * always, and `others`, each at /npm/<name>@site-<hash>/, the hash naming
94
+ * what the workspaces hold so a browser's cache keeps no stale file.
95
+ */
96
+ export function localPackages(repoRoot: string, others: readonly string[] = []): Record<string, string> {
97
+ const dirs = workspacePackages(repoRoot);
98
+ const own = dirs.get(PLAYGROUND_PACKAGE)!;
99
+ const manifestFile = path.join(own, PLAYGROUND_MANIFEST);
100
+ if (!existsSync(manifestFile)) throw new Error(`${manifestFile} is missing: run \`npm run playground:assets --workspace @swiftbrowser/web\``);
101
+ const hash = createHash('sha256');
102
+ for (const file of [PLAYGROUND_MANIFEST, PLAYGROUND_ICU, PLAYGROUND_MODULE_CACHE]) hash.update(readFileSync(path.join(own, file)));
103
+ hash.update(readFileSync(path.join(dirs.get(KIT_PACKAGE)!, 'kit.json')));
104
+ const names = [...new Set([PLAYGROUND_PACKAGE, KIT_PACKAGE, ...others])].sort();
105
+ for (const name of names) hash.update(`${name}\n`);
106
+ // And how writeLocalPackages lays them out: another layout is other files.
107
+ hash.update(`layout ${SITE_LAYOUT} ${SITE_FILE_LIMIT} ${SITE_COMPRESS_FROM}\n`);
108
+ const version = `site-${hash.digest('hex').slice(0, 12)}`;
109
+ return Object.fromEntries(names.map((name) => [name, `${PLAYGROUND_NPM_PATH}${name}@${version}/`]));
110
+ }
111
+
112
+ /**
113
+ * Writes the files the page reads of each local package under `outDir`, at
114
+ * the paths `packages` names: brotli-compressed as `<file>.brotli` from
115
+ * SITE_COMPRESS_FROM (and always over the size limit), and playground.json
116
+ * naming those (`brotli`).
117
+ */
118
+ export function writeLocalPackages(repoRoot: string, packages: Record<string, string>, outDir: string, limit = SITE_FILE_LIMIT, compressFrom = SITE_COMPRESS_FROM): { files: number; bytes: number } {
119
+ const dirs = workspacePackages(repoRoot);
120
+ const manifest = JSON.parse(readFileSync(path.join(dirs.get(PLAYGROUND_PACKAGE)!, PLAYGROUND_MANIFEST), 'utf8')) as PlaygroundManifest;
121
+ const brotli = new Set<string>();
122
+ const rename = (file: PackageFile): PackageFile => (brotli.has(`${file.package}/${file.file}`) ? { ...file, file: `${file.file}${SITE_BROTLI}` } : file);
123
+ const written = { files: 0, bytes: 0 };
124
+ for (const [name, files] of manifestFiles(manifest)) {
125
+ const base = packages[name];
126
+ if (!base) continue;
127
+ const dir = dirs.get(name);
128
+ if (!dir) throw new Error(`the site serves ${name}, which is not a package of this repository`);
129
+ for (const file of files) {
130
+ if (name === PLAYGROUND_PACKAGE && file === PLAYGROUND_MANIFEST) continue;
131
+ let data: Uint8Array = readFileSync(path.join(dir, file));
132
+ let target = file;
133
+ if (isBrotli(file)) {
134
+ if (data.length > limit) throw new Error(`${name}/${file} is over ${limit} bytes compressed: the site cannot serve it`);
135
+ } else if (data.length >= compressFrom || data.length > limit) {
136
+ const compressed = brotliCompressSync(data, { params: { [constants.BROTLI_PARAM_QUALITY]: 5, [constants.BROTLI_PARAM_LGWIN]: 24, [constants.BROTLI_PARAM_SIZE_HINT]: data.length } });
137
+ if (compressed.length > limit) throw new Error(`${name}/${file} is over ${limit} bytes even compressed`);
138
+ if (data.length > limit || compressed.length < data.length * 0.9) {
139
+ data = compressed;
140
+ brotli.add(`${name}/${file}`);
141
+ target = `${file}${SITE_BROTLI}`;
142
+ }
143
+ }
144
+ const out = path.join(outDir, base, target);
145
+ mkdirSync(path.dirname(out), { recursive: true });
146
+ writeFileSync(out, data);
147
+ written.files += 1;
148
+ written.bytes += data.length;
149
+ }
150
+ }
151
+ if (packages[PLAYGROUND_PACKAGE]) {
152
+ // A tool or plugin is named by its compressed file; the rest by the list.
153
+ const tools = new Set([manifest.frontend, manifest.linker, ...Object.values(manifest.plugins)].map((file) => `${file.package}/${file.file}`));
154
+ const listed = [...brotli].filter((entry) => !tools.has(entry)).sort();
155
+ const site: PlaygroundManifest = {
156
+ ...manifest,
157
+ frontend: rename(manifest.frontend),
158
+ linker: rename(manifest.linker),
159
+ plugins: Object.fromEntries(Object.entries(manifest.plugins).map(([macro, file]) => [macro, rename(file)])),
160
+ ...(listed.length > 0 ? { brotli: listed } : {}),
161
+ };
162
+ const out = path.join(outDir, packages[PLAYGROUND_PACKAGE], PLAYGROUND_MANIFEST);
163
+ mkdirSync(path.dirname(out), { recursive: true });
164
+ writeFileSync(out, `${JSON.stringify(site)}\n`);
165
+ written.files += 1;
166
+ }
167
+ return written;
168
+ }
169
+
170
+ export function playgroundPackages(repoRoot: string, options: PlaygroundOptions): Plugin {
171
+ let outDir = '';
43
172
  return {
44
- name: 'swiftbrowser-playground-assets',
173
+ name: 'swiftbrowser-playground-packages',
174
+ configResolved(config) {
175
+ outDir = path.resolve(config.root, config.build.outDir);
176
+ },
45
177
  configureServer(server) {
46
- const dir = path.resolve(options.assetsDir);
47
- server.middlewares.use(PLAYGROUND_ASSETS_PATH, (req, res, next) => {
48
- const file = path.join(dir, decodeURIComponent(new URL(req.url ?? '/', 'http://x').pathname));
49
- if (!file.startsWith(dir) || !existsSync(file) || statSync(file).isDirectory()) return next();
50
- res.setHeader('Content-Type', file.endsWith('.gz') ? 'application/gzip' : 'application/octet-stream');
178
+ const packages = workspacePackages(repoRoot);
179
+ server.middlewares.use(PLAYGROUND_NPM_PATH, (req, res, next) => {
180
+ const request = packageRequest(decodeURIComponent(new URL(req.url ?? '/', 'http://x').pathname).replace(/^\//, ''));
181
+ const dir = request && packages.get(request[0]);
182
+ if (!request || !dir) return next();
183
+ const file = path.join(dir, request[1]);
184
+ if (!existsSync(file) || statSync(file).isDirectory() || !realpathSync(file).startsWith(realpathSync(dir))) return next();
185
+ res.setHeader('Content-Type', file.endsWith('.wasm') ? 'application/wasm' : file.endsWith('.json') ? 'application/json' : 'application/octet-stream');
51
186
  res.setHeader('Content-Length', statSync(file).size);
52
187
  createReadStream(file).pipe(res);
53
188
  });
54
189
  },
190
+ writeBundle() {
191
+ if (!options.packages) return;
192
+ const { files, bytes } = writeLocalPackages(repoRoot, options.packages, outDir);
193
+ this.info(`the playground's local packages: ${files} files, ${(bytes / 1e6).toFixed(1)} MB`);
194
+ },
55
195
  };
56
196
  }
@@ -1,53 +1,112 @@
1
- // The playground's downloads (playground/build-assets.ts makes them): the
2
- // compiler, the linker, the macro plugins and the pack of SDK and kit files,
3
- // fetched from one base URL (a public bucket, or the dev server's copy) and
4
- // kept in the Cache API, so a compile needs no server request after the
5
- // first visit. Each file is gzipped, unpacked here as it arrives (no server
6
- // or CDN has to set Content-Encoding), and named by its contents, so a new
7
- // build is a new download.
1
+ // The playground's downloads (files.ts): its manifest from
2
+ // @swiftbrowser/playground, then the tools, the SDK's and the kit's files it
3
+ // names, the ICU data and the module cache, every one a file of an npm
4
+ // package, from the CDN at the source's version or from the site itself.
5
+ // Each is kept in the Cache API under its URL, which a version makes
6
+ // immutable, so a compile needs no request after the first visit. The tools
7
+ // come brotli-compressed, as does a file too big for the site, and no
8
+ // browser's DecompressionStream reads brotli: a small WebAssembly decoder
9
+ // unpacks them as they arrive, and the cache keeps them unpacked. Other files
10
+ // the server compresses in transit.
8
11
 
9
12
  import { memoryDirectory } from '@swiftbrowser/wasm-compiler';
10
13
  import type { Toolchain } from './build';
11
- import type { PlaygroundFiles } from './files';
14
+ import {
15
+ PLAYGROUND_ICU,
16
+ PLAYGROUND_MANIFEST,
17
+ PLAYGROUND_MODULE_CACHE,
18
+ PLAYGROUND_PACKAGE,
19
+ SITE_BROTLI,
20
+ isBrotli,
21
+ type PackageFile,
22
+ type PlaygroundManifest,
23
+ type PlaygroundSource,
24
+ packageUrl,
25
+ } from './files';
12
26
  import { readPack } from './pack';
13
27
 
14
- /** What the pack's index records besides its files. */
15
- export interface PlaygroundMeta {
16
- /** The symbol Foundation's ICU reads its data through (`icudtNN_dat`). */
17
- icuSymbol: string;
18
- /** The apps whose compiles the pack was recorded from. */
19
- apps: string[];
28
+ const CACHE = 'swiftbrowser-playground';
29
+ /** Requests in flight at once for the SDK's and kit's files. */
30
+ const CONCURRENCY = 24;
31
+
32
+ type Brotli = typeof import('brotli-dec-wasm/web');
33
+ let brotli: Promise<Brotli> | null = null;
34
+
35
+ /** The brotli decoder, loaded on first use. */
36
+ function brotliDecoder(): Promise<Brotli> {
37
+ brotli ??= Promise.all([import('brotli-dec-wasm/web'), import('brotli-dec-wasm/web/bg.wasm?url')]).then(async ([decoder, wasm]) => {
38
+ await decoder.default({ module_or_path: wasm.default });
39
+ return decoder;
40
+ });
41
+ return brotli;
20
42
  }
21
43
 
22
- const CACHE = 'swiftbrowser-playground';
44
+ /** A stream that unpacks brotli as its input arrives. */
45
+ function brotliStream(decoder: Brotli): TransformStream<Uint8Array, Uint8Array> {
46
+ const stream = new decoder.BrotliDecStream();
47
+ let code: number = decoder.BrotliStreamResultCode.NeedsMoreInput;
48
+ return new TransformStream({
49
+ transform(chunk, controller) {
50
+ let input = chunk;
51
+ do {
52
+ const result = stream.dec(input, 1 << 22);
53
+ if (result.buf.length > 0) controller.enqueue(result.buf);
54
+ input = input.subarray(result.input_offset);
55
+ code = result.code;
56
+ } while (code === decoder.BrotliStreamResultCode.NeedsMoreOutput);
57
+ },
58
+ flush() {
59
+ stream.free();
60
+ if (code !== decoder.BrotliStreamResultCode.ResultSuccess) throw new Error('a brotli file ended early');
61
+ },
62
+ });
63
+ }
64
+
65
+ const openCache = async (): Promise<Cache | null> => (typeof caches === 'undefined' ? null : caches.open(CACHE).catch(() => null));
23
66
 
24
- /** A response for `url`, from the cache when it has one. */
25
- async function cached(url: string): Promise<Response> {
26
- const cache = typeof caches === 'undefined' ? null : await caches.open(CACHE).catch(() => null);
67
+ /** The file at `url`, unpacked when it is brotli (`isBrotli`), from the cache when it has it. */
68
+ async function download(cache: Cache | null, url: string): Promise<Response> {
27
69
  const hit = await cache?.match(url);
28
70
  if (hit) return hit;
29
71
  const response = await fetch(url);
30
- if (!response.ok) throw new Error(`${url}: HTTP ${response.status}`);
31
- await cache?.put(url, response.clone()).catch(() => {});
32
- return response;
33
- }
34
-
35
- /** The file at `url`, unpacked when it is gzipped. */
36
- async function body(url: string): Promise<Response> {
37
- const response = await cached(url);
38
- if (!url.endsWith('.gz') || !response.body) return response;
39
- const type = url.endsWith('.wasm.gz') ? 'application/wasm' : 'application/octet-stream';
40
- return new Response(response.body.pipeThrough(new DecompressionStream('gzip')), { headers: { 'content-type': type } });
72
+ if (!response.ok || !response.body) throw new Error(`${url}: HTTP ${response.status}`);
73
+ let file = response;
74
+ if (isBrotli(url)) {
75
+ const decoder = await brotliDecoder();
76
+ const type = /\.wasm\.(br|brotli)$/.test(url) ? 'application/wasm' : 'application/octet-stream';
77
+ file = new Response(response.body.pipeThrough(brotliStream(decoder)), { headers: { 'content-type': type } });
78
+ }
79
+ if (cache) {
80
+ // The page reads one copy as the cache takes the other, unpacked: of the
81
+ // headers only the type goes with it (compileStreaming wants it).
82
+ const headers = { 'content-type': file.headers.get('content-type') ?? 'application/octet-stream' };
83
+ const [kept, used] = file.body!.tee();
84
+ void cache.put(url, new Response(kept, { headers })).catch(() => {});
85
+ return new Response(used, { headers });
86
+ }
87
+ return file;
41
88
  }
42
89
 
43
- async function compileModule(url: string): Promise<WebAssembly.Module> {
44
- const response = await body(url);
90
+ async function compileModule(response: Response): Promise<WebAssembly.Module> {
45
91
  if (typeof WebAssembly.compileStreaming === 'function' && (response.headers.get('content-type') ?? '').includes('application/wasm')) {
46
92
  return WebAssembly.compileStreaming(response);
47
93
  }
48
94
  return WebAssembly.compile(await response.arrayBuffer());
49
95
  }
50
96
 
97
+ /** `work` over `items`, at most `limit` at once, in order. */
98
+ async function pooled<T, R>(items: readonly T[], limit: number, work: (item: T) => Promise<R>): Promise<R[]> {
99
+ const results: R[] = new Array(items.length);
100
+ let next = 0;
101
+ await Promise.all(Array.from({ length: Math.min(limit, items.length) }, async () => {
102
+ while (next < items.length) {
103
+ const index = next++;
104
+ results[index] = await work(items[index]!);
105
+ }
106
+ }));
107
+ return results;
108
+ }
109
+
51
110
  export interface LoadedToolchain {
52
111
  /** The tools and files, without macro plugins until `plugins` settles. */
53
112
  toolchain: Toolchain;
@@ -58,39 +117,81 @@ export interface LoadedToolchain {
58
117
  plugins: Promise<Toolchain['plugins']>;
59
118
  /** The module cache built with the playground's flags. */
60
119
  moduleCache: [string, Uint8Array][];
61
- meta: PlaygroundMeta;
120
+ manifest: PlaygroundManifest;
121
+ /** Why the kit may not suit this page's renderer, when it may not. */
122
+ warning?: string;
62
123
  }
63
124
 
64
- export async function loadToolchain(base: string, files: PlaygroundFiles, onProgress: (text: string) => void = () => {}): Promise<LoadedToolchain> {
65
- const url = (file: string): string => new URL(file, base).href;
125
+ /**
126
+ * Whether the kit the manifest names was built from the sources the page's
127
+ * renderer was: the two speak one protocol, which either may change.
128
+ */
129
+ export function kitWarning(manifest: Pick<PlaygroundManifest, 'shimSources'>, source: Pick<PlaygroundSource, 'shimSources'>): string | undefined {
130
+ if (!manifest.shimSources || !source.shimSources || manifest.shimSources === source.shimSources) return undefined;
131
+ return `The kit was built from other SwiftBrowser sources (${manifest.shimSources}) than this page's renderer (${source.shimSources}): an app may not run as it should.`;
132
+ }
133
+
134
+ export async function loadToolchain(source: PlaygroundSource, onProgress: (text: string) => void = () => {}): Promise<LoadedToolchain> {
135
+ const cache = await openCache();
136
+ const url = ({ package: name, file }: PackageFile): string => packageUrl(source, name, file);
137
+ const bytes = async (file: PackageFile): Promise<Uint8Array<ArrayBuffer>> => new Uint8Array(await (await download(cache, url(file))).arrayBuffer());
66
138
  const started = performance.now();
67
139
  const lap = (text: string): void => onProgress(`${text} (${((performance.now() - started) / 1000).toFixed(1)} s)`);
68
- const [frontend, linker, packBytes] = await Promise.all([
69
- compileModule(url(files.frontend)).then((module) => (lap('compiler ready'), module)),
70
- compileModule(url(files.linker)).then((module) => (lap('linker ready'), module)),
71
- body(url(files.pack)).then((response) => response.arrayBuffer()).then((buffer) => (lap('SDK ready'), new Uint8Array(buffer))),
140
+ const own = (file: string): PackageFile => ({ package: PLAYGROUND_PACKAGE, file });
141
+ const manifest = JSON.parse(new TextDecoder().decode(await bytes(own(PLAYGROUND_MANIFEST)))) as PlaygroundManifest;
142
+
143
+ const kitPackage = '@swiftbrowser/kit';
144
+ // What a site stores brotli-compressed it serves under another name.
145
+ const brotli = new Set(manifest.brotli ?? []);
146
+ const served = (name: string, file: string): PackageFile => ({ package: name, file: brotli.has(`${name}/${file}`) ? `${file}${SITE_BROTLI}` : file });
147
+ const sdkFiles = Object.entries(manifest.sdk).flatMap(([name, files]) => files.map((file) => ({ path: file, from: served(name, `wasm32-unknown-wasip1/${file}`) })));
148
+ const kitFiles = [...manifest.kit, 'kit.json'].map((file) => ({ path: file, from: served(kitPackage, file) }));
149
+ let done = 0;
150
+ const total = sdkFiles.length + kitFiles.length;
151
+ const files = pooled([...sdkFiles, ...kitFiles], CONCURRENCY, async ({ path, from }) => {
152
+ const data = await bytes(from);
153
+ if (++done % 50 === 0 || done === total) onProgress(`SDK and kit: ${done} of ${total} files`);
154
+ return [path, data] as [string, Uint8Array];
155
+ });
156
+ const [frontend, linker, all, icu, moduleCacheBytes] = await Promise.all([
157
+ download(cache, url(manifest.frontend)).then(compileModule).then((module) => (lap('compiler ready'), module)),
158
+ download(cache, url(manifest.linker)).then(compileModule).then((module) => (lap('linker ready'), module)),
159
+ files.then((list) => (lap('SDK ready'), list)),
160
+ bytes(served(PLAYGROUND_PACKAGE, PLAYGROUND_ICU)),
161
+ bytes(served(PLAYGROUND_PACKAGE, PLAYGROUND_MODULE_CACHE)),
72
162
  ]);
73
- const pack = readPack<PlaygroundMeta>(packBytes);
74
- const plugins = Promise.all(Object.entries(files.plugins).map(async ([name, file]) => {
75
- const bytes = new Uint8Array(await (await body(url(file))).arrayBuffer());
76
- return [name, { module: await WebAssembly.compile(bytes), bytes }] as const;
163
+ const sdk = all.slice(0, sdkFiles.length);
164
+ const kit = all.slice(sdkFiles.length);
165
+ const kitJson = kit.pop()![1];
166
+
167
+ const plugins = Promise.all(Object.entries(manifest.plugins).map(async ([name, file]) => {
168
+ const data = await bytes(file);
169
+ return [name, { module: await WebAssembly.compile(data), bytes: data }] as const;
77
170
  })).then(Object.fromEntries<Toolchain['plugins'][string]>);
78
171
  // A failed download surfaces when a compile needs the plugins.
79
172
  plugins.catch(() => {});
80
- const kitJson = pack.file('kit.json');
81
- const icu = pack.file('icu.dat');
82
- if (!kitJson || !icu) throw new Error('playground.pack has no kit.json or icu.dat');
173
+ // Every file is named by its version: drop what an older one left.
174
+ void plugins.then(async () => {
175
+ const wanted = new Set([
176
+ ...[manifest.frontend, manifest.linker, ...Object.values(manifest.plugins), ...sdkFiles.map((f) => f.from), ...kitFiles.map((f) => f.from)].map(url),
177
+ url(own(PLAYGROUND_MANIFEST)),
178
+ ...[PLAYGROUND_ICU, PLAYGROUND_MODULE_CACHE].map((file) => url(served(PLAYGROUND_PACKAGE, file))),
179
+ ]);
180
+ for (const request of (await cache?.keys()) ?? []) if (!wanted.has(request.url)) await cache!.delete(request);
181
+ }).catch(() => {});
182
+
83
183
  return {
84
184
  toolchain: {
85
185
  tools: { 'swift-frontend': frontend, 'wasm-ld': linker },
86
186
  plugins: {},
87
- sdk: memoryDirectory(pack.files('sdk')),
88
- kit: memoryDirectory(pack.files('kit')),
187
+ sdk: memoryDirectory(sdk),
188
+ kit: memoryDirectory(kit),
89
189
  kitManifest: JSON.parse(new TextDecoder().decode(kitJson)) as Toolchain['kitManifest'],
90
- icu: { symbol: pack.meta.icuSymbol, data: icu },
190
+ icu: { symbol: manifest.icuSymbol, data: icu },
91
191
  },
92
192
  plugins,
93
- moduleCache: pack.files('module-cache'),
94
- meta: pack.meta,
193
+ moduleCache: readPack(moduleCacheBytes).files('module-cache'),
194
+ manifest,
195
+ ...(kitWarning(manifest, source) ? { warning: kitWarning(manifest, source)! } : {}),
95
196
  };
96
197
  }
@@ -1,18 +1,87 @@
1
- // The playground's downloads by role, as playground/build-assets.ts names
2
- // them in assets.json: gzip files named by a hash of their contents, so any
3
- // cache may keep them forever and a new build is new names. The site's
4
- // build reads the manifest (plugins/playground.ts) and the page fetches
5
- // these names from the assets' base URL.
1
+ // What the playground downloads, all from npm packages at one version (the
2
+ // release's lockstep version), through a CDN that serves npm packages file by
3
+ // file (jsDelivr): the compiler, the linker and the toolchain's macro plugins
4
+ // from @swiftbrowser/wasm-tools, the Swift SDK's files from the
5
+ // @swiftbrowser/wasm-sdk packages, the kit's from @swiftbrowser/kit, and what
6
+ // only the playground needs from @swiftbrowser/playground: its manifest
7
+ // (which files of those packages a compile reads), the trimmed ICU data and
8
+ // the module cache. playground/build-assets.ts makes that package.
6
9
 
7
- export interface PlaygroundFiles {
8
- /** The compiler (`swift-frontend-<hash>.wasm.gz`). */
9
- frontend: string;
10
- /** The linker (`wasm-ld-<hash>.wasm.gz`). */
11
- linker: string;
12
- /** The SDK and kit files, ICU data and module cache (`playground-<hash>.pack.gz`). */
13
- pack: string;
10
+ export const PLAYGROUND_PACKAGE = '@swiftbrowser/playground';
11
+ export const PLAYGROUND_MANIFEST = 'playground.json';
12
+ /** The trimmed ICU data, in @swiftbrowser/playground. */
13
+ export const PLAYGROUND_ICU = 'icu.dat';
14
+ /** The module cache, packed (pack.ts), in @swiftbrowser/playground. */
15
+ export const PLAYGROUND_MODULE_CACHE = 'module-cache.pack';
16
+ /** Where the npm packages are fetched from by default. */
17
+ export const JSDELIVR = 'https://cdn.jsdelivr.net/npm/';
18
+
19
+ /** A file of one of the packages. */
20
+ export interface PackageFile {
21
+ package: string;
22
+ /** The path in the package; `.br` files are brotli-compressed. */
23
+ file: string;
24
+ }
25
+
26
+ /** @swiftbrowser/playground's playground.json. */
27
+ export interface PlaygroundManifest {
28
+ /** The symbol Foundation's ICU reads its data through (`icudtNN_dat`). */
29
+ icuSymbol: string;
30
+ /** The apps whose compiles recorded the files. */
31
+ apps: string[];
32
+ frontend: PackageFile;
33
+ linker: PackageFile;
14
34
  /** The macro plugins by the module whose macros they expand. */
15
- plugins: Record<string, string>;
35
+ plugins: Record<string, PackageFile>;
36
+ /**
37
+ * The Swift SDK's files a compile reads, by package, each relative to the
38
+ * SDK's `wasm32-unknown-wasip1` directory (the package's own
39
+ * `wasm32-unknown-wasip1/` holds it there).
40
+ */
41
+ sdk: Record<string, string[]>;
42
+ /** The kit's files a compile reads, relative to @swiftbrowser/kit (`wasm32-unknown-wasip1/...`). */
43
+ kit: string[];
44
+ /**
45
+ * The SwiftBrowser sources the kit was built from (@swiftbrowser/swift's
46
+ * `sourcesHash`), when build-assets.ts could tell: the page compares it
47
+ * with the sources its own renderer was built from.
48
+ */
49
+ shimSources?: string;
50
+ /**
51
+ * Files, as `<package>/<path>`, that the site serving them stores
52
+ * brotli-compressed (`<path>.brotli`, SITE_BROTLI): an SDK or kit file,
53
+ * the ICU data or the module cache (plugins/playground.ts). A tool or
54
+ * plugin is named by its compressed file instead.
55
+ */
56
+ brotli?: string[];
57
+ }
58
+
59
+ /**
60
+ * The suffix of a file a site stores brotli-compressed. Not `.br`: a static
61
+ * server may send a `.br` file as `Content-Encoding: br`, which the browser
62
+ * unpacks before the page could.
63
+ */
64
+ export const SITE_BROTLI = '.brotli';
65
+
66
+ /** Whether a file is brotli-compressed by its name: an npm package's `.br`, or a site's. */
67
+ export const isBrotli = (file: string): boolean => file.endsWith('.br') || file.endsWith(SITE_BROTLI);
68
+
69
+ /** Where the packages come from. */
70
+ export interface PlaygroundSource {
71
+ /** A CDN's npm base URL, which serves every package at `version`... */
72
+ npm: string;
73
+ version: string;
74
+ /**
75
+ * ...but those named here, each served from its own base URL: the site's
76
+ * copy, made from the same commit as its renderer (plugins/playground.ts).
77
+ */
78
+ packages?: Record<string, string>;
79
+ /** The SwiftBrowser sources the site's renderer was built from (`sourcesHash`). */
80
+ shimSources?: string;
16
81
  }
17
82
 
18
- export const PLAYGROUND_MANIFEST = 'assets.json';
83
+ /** The URL of a package's file. */
84
+ export function packageUrl(source: PlaygroundSource, name: string, file: string): string {
85
+ const base = source.packages?.[name];
86
+ return base ? new URL(file, base).href : new URL(`${name}@${source.version}/${file}`, source.npm).href;
87
+ }
@@ -21,6 +21,7 @@ const run = $<HTMLButtonElement>('#run');
21
21
  const status = $<HTMLElement>('#status');
22
22
  const diagnostics = $<HTMLPreElement>('#diagnostics');
23
23
  const frame = $<HTMLIFrameElement>('#app');
24
+ const warning = $<HTMLElement>('#warning');
24
25
 
25
26
  // Path relative to Sources/ → text, as edited.
26
27
  const files: Record<string, string> = Object.fromEntries(
@@ -48,7 +49,13 @@ editor.addEventListener('input', () => (files[current] = editor.value));
48
49
  showFiles();
49
50
 
50
51
  const worker = new Worker(new URL('./worker.ts', import.meta.url), { type: 'module' });
51
- const base = new URL(__SB_PLAYGROUND_ASSETS__, location.href).href;
52
+ const source = {
53
+ ...__SB_PLAYGROUND__,
54
+ npm: new URL(__SB_PLAYGROUND__.npm, location.href).href,
55
+ ...(__SB_PLAYGROUND__.packages
56
+ ? { packages: Object.fromEntries(Object.entries(__SB_PLAYGROUND__.packages).map(([name, base]) => [name, new URL(base, location.href).href])) }
57
+ : {}),
58
+ };
52
59
  let nextId = 1;
53
60
  let moduleUrl: string | null = null;
54
61
  const started = new Map<number, number>();
@@ -59,6 +66,11 @@ worker.onmessage = (event: MessageEvent<FromWorker>) => {
59
66
  status.textContent = message.text;
60
67
  return;
61
68
  }
69
+ if (message.type === 'warning') {
70
+ warning.hidden = false;
71
+ warning.textContent = message.text;
72
+ return;
73
+ }
62
74
  run.disabled = false;
63
75
  if (message.type === 'failure') {
64
76
  status.textContent = 'The compiler failed.';
@@ -87,7 +99,7 @@ function compile(): void {
87
99
  status.textContent = 'Compiling…';
88
100
  const id = nextId++;
89
101
  started.set(id, performance.now());
90
- const message: ToWorker = { type: 'compile', id, base, files: __SB_PLAYGROUND_FILES__, app: { name: APP, files: { ...files } } };
102
+ const message: ToWorker = { type: 'compile', id, source, app: { name: APP, files: { ...files } } };
91
103
  worker.postMessage(message);
92
104
  }
93
105
  run.addEventListener('click', compile);
@@ -27,4 +27,5 @@ main { display: grid; grid-template-columns: minmax(0, 1fr) 420px; gap: 16px; pa
27
27
  #run:disabled { opacity: 0.5; cursor: default; }
28
28
  #status { color: var(--muted); font-size: 13px; }
29
29
  #diagnostics { margin: 0; padding: 12px; border: 1px solid var(--error); border-radius: 8px; background: var(--panel); color: var(--error); font: 12px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace; white-space: pre-wrap; overflow: auto; max-height: 320px; }
30
+ #warning { margin: 0; padding: 8px 12px; border: 1px solid var(--error); border-radius: 8px; background: var(--panel); font-size: 13px; }
30
31
  .preview iframe { width: 100%; height: 860px; border: none; border-radius: 12px; background: var(--panel); }
@@ -7,11 +7,12 @@
7
7
 
8
8
  import { type AppSources, missingPlugin, buildApp } from './build';
9
9
  import { type LoadedToolchain, loadToolchain } from './assets';
10
- import type { PlaygroundFiles } from './files';
10
+ import type { PlaygroundSource } from './files';
11
11
 
12
- export type ToWorker = { type: 'compile'; id: number; base: string; files: PlaygroundFiles; app: AppSources };
12
+ export type ToWorker = { type: 'compile'; id: number; source: PlaygroundSource; app: AppSources };
13
13
  export type FromWorker =
14
14
  | { type: 'progress'; text: string }
15
+ | { type: 'warning'; text: string }
15
16
  | { type: 'result'; id: number; ok: boolean; output: string; wasm?: Uint8Array<ArrayBuffer>; steps: { tool: string; seconds: number }[] }
16
17
  | { type: 'failure'; id: number; error: string };
17
18
 
@@ -20,11 +21,12 @@ let loaded: Promise<LoadedToolchain> | null = null;
20
21
  let moduleCache: [string, Uint8Array][] | null = null;
21
22
 
22
23
  self.onmessage = async (event: MessageEvent<ToWorker>) => {
23
- const { id, base, files, app } = event.data;
24
+ const { id, source, app } = event.data;
24
25
  try {
25
- loaded ??= loadToolchain(base, files, (text) => post({ type: 'progress', text })).then((toolchain) => {
26
+ loaded ??= loadToolchain(source, (text) => post({ type: 'progress', text })).then((toolchain) => {
26
27
  // Later compiles use the plugins as soon as they are in.
27
28
  toolchain.plugins.then((plugins) => (toolchain.toolchain.plugins = plugins), () => {});
29
+ if (toolchain.warning) post({ type: 'warning', text: toolchain.warning });
28
30
  return toolchain;
29
31
  });
30
32
  const { toolchain, plugins, moduleCache: prebuilt } = await loaded;
package/src/vite-env.d.ts CHANGED
@@ -26,10 +26,8 @@ interface SBLandingApp {
26
26
  declare const __SB_APPS__: readonly SBLandingApp[];
27
27
  /** The app the app page runs without `?app=` (`SiteOptions.defaultApp`, node/config.ts). */
28
28
  declare const __SB_DEFAULT_APP__: string;
29
- /** Where the playground page fetches its assets (plugins/playground.ts). */
30
- declare const __SB_PLAYGROUND_ASSETS__: string;
31
- /** The playground's downloads, from the assets' manifest (src/playground/files.ts). */
32
- declare const __SB_PLAYGROUND_FILES__: import('./playground/files').PlaygroundFiles;
29
+ /** Where the playground page fetches its npm packages, and their version (plugins/playground.ts); null without the page. */
30
+ declare const __SB_PLAYGROUND__: import('./playground/files').PlaygroundSource;
33
31
  /** What a wide screen shows: `auto` (the frame) or `device` (the full window, a built single-app site). See plugins/site-layout.ts. */
34
32
  declare const __SB_PAGE_MODE__: 'auto' | 'device';
35
33