@pwfabric/cli 0.0.0-stage → 1.0.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +122 -2
  3. package/dist/args.d.ts +11 -0
  4. package/dist/args.js +43 -0
  5. package/dist/args.js.map +1 -0
  6. package/dist/bin.d.ts +2 -0
  7. package/dist/bin.js +10 -0
  8. package/dist/bin.js.map +1 -0
  9. package/dist/commands/add.d.ts +3 -0
  10. package/dist/commands/add.js +56 -0
  11. package/dist/commands/add.js.map +1 -0
  12. package/dist/commands/build.d.ts +3 -0
  13. package/dist/commands/build.js +55 -0
  14. package/dist/commands/build.js.map +1 -0
  15. package/dist/commands/create.d.ts +9 -0
  16. package/dist/commands/create.js +119 -0
  17. package/dist/commands/create.js.map +1 -0
  18. package/dist/commands/dev.d.ts +12 -0
  19. package/dist/commands/dev.js +107 -0
  20. package/dist/commands/dev.js.map +1 -0
  21. package/dist/commands/lib/flags.d.ts +10 -0
  22. package/dist/commands/lib/flags.js +33 -0
  23. package/dist/commands/lib/flags.js.map +1 -0
  24. package/dist/commands/lib/identity.d.ts +38 -0
  25. package/dist/commands/lib/identity.js +120 -0
  26. package/dist/commands/lib/identity.js.map +1 -0
  27. package/dist/commands/lib/project.d.ts +34 -0
  28. package/dist/commands/lib/project.js +78 -0
  29. package/dist/commands/lib/project.js.map +1 -0
  30. package/dist/commands/lib/report.d.ts +15 -0
  31. package/dist/commands/lib/report.js +75 -0
  32. package/dist/commands/lib/report.js.map +1 -0
  33. package/dist/commands/lib/starter.d.ts +41 -0
  34. package/dist/commands/lib/starter.js +136 -0
  35. package/dist/commands/lib/starter.js.map +1 -0
  36. package/dist/commands/lib/static-server.d.ts +45 -0
  37. package/dist/commands/lib/static-server.js +176 -0
  38. package/dist/commands/lib/static-server.js.map +1 -0
  39. package/dist/commands/pack.d.ts +5 -0
  40. package/dist/commands/pack.js +56 -0
  41. package/dist/commands/pack.js.map +1 -0
  42. package/dist/commands/registry.d.ts +7 -0
  43. package/dist/commands/registry.js +16 -0
  44. package/dist/commands/registry.js.map +1 -0
  45. package/dist/commands/run.d.ts +14 -0
  46. package/dist/commands/run.js +79 -0
  47. package/dist/commands/run.js.map +1 -0
  48. package/dist/commands/types.d.ts +34 -0
  49. package/dist/commands/types.js +10 -0
  50. package/dist/commands/types.js.map +1 -0
  51. package/dist/commands/validate.d.ts +6 -0
  52. package/dist/commands/validate.js +33 -0
  53. package/dist/commands/validate.js.map +1 -0
  54. package/dist/dev/assemble.d.ts +47 -0
  55. package/dist/dev/assemble.js +339 -0
  56. package/dist/dev/assemble.js.map +1 -0
  57. package/dist/dev/compile.d.ts +54 -0
  58. package/dist/dev/compile.js +215 -0
  59. package/dist/dev/compile.js.map +1 -0
  60. package/dist/dev/csp.d.ts +67 -0
  61. package/dist/dev/csp.js +260 -0
  62. package/dist/dev/csp.js.map +1 -0
  63. package/dist/dev/diagnostics.d.ts +21 -0
  64. package/dist/dev/diagnostics.js +22 -0
  65. package/dist/dev/diagnostics.js.map +1 -0
  66. package/dist/dev/envelope.d.ts +92 -0
  67. package/dist/dev/envelope.js +154 -0
  68. package/dist/dev/envelope.js.map +1 -0
  69. package/dist/dev/media.d.ts +53 -0
  70. package/dist/dev/media.js +128 -0
  71. package/dist/dev/media.js.map +1 -0
  72. package/dist/dev/needs-host.d.ts +2 -0
  73. package/dist/dev/needs-host.js +32 -0
  74. package/dist/dev/needs-host.js.map +1 -0
  75. package/dist/dev/output.d.ts +9 -0
  76. package/dist/dev/output.js +32 -0
  77. package/dist/dev/output.js.map +1 -0
  78. package/dist/dev/overlay.d.ts +21 -0
  79. package/dist/dev/overlay.js +59 -0
  80. package/dist/dev/overlay.js.map +1 -0
  81. package/dist/dev/server.d.ts +27 -0
  82. package/dist/dev/server.js +268 -0
  83. package/dist/dev/server.js.map +1 -0
  84. package/dist/dev/shell.d.ts +94 -0
  85. package/dist/dev/shell.js +267 -0
  86. package/dist/dev/shell.js.map +1 -0
  87. package/dist/dev/sources.d.ts +45 -0
  88. package/dist/dev/sources.js +165 -0
  89. package/dist/dev/sources.js.map +1 -0
  90. package/dist/dev/theme.d.ts +51 -0
  91. package/dist/dev/theme.js +185 -0
  92. package/dist/dev/theme.js.map +1 -0
  93. package/dist/dev/types.d.ts +40 -0
  94. package/dist/dev/types.js +11 -0
  95. package/dist/dev/types.js.map +1 -0
  96. package/dist/dev/watcher.d.ts +29 -0
  97. package/dist/dev/watcher.js +150 -0
  98. package/dist/dev/watcher.js.map +1 -0
  99. package/dist/host/definitions.d.ts +61 -0
  100. package/dist/host/definitions.js +136 -0
  101. package/dist/host/definitions.js.map +1 -0
  102. package/dist/host/http.d.ts +39 -0
  103. package/dist/host/http.js +156 -0
  104. package/dist/host/http.js.map +1 -0
  105. package/dist/host/local-host.d.ts +59 -0
  106. package/dist/host/local-host.js +420 -0
  107. package/dist/host/local-host.js.map +1 -0
  108. package/dist/host/oidc.d.ts +46 -0
  109. package/dist/host/oidc.js +192 -0
  110. package/dist/host/oidc.js.map +1 -0
  111. package/dist/host/query.d.ts +35 -0
  112. package/dist/host/query.js +158 -0
  113. package/dist/host/query.js.map +1 -0
  114. package/dist/host/receipts.d.ts +25 -0
  115. package/dist/host/receipts.js +72 -0
  116. package/dist/host/receipts.js.map +1 -0
  117. package/dist/host/records.d.ts +73 -0
  118. package/dist/host/records.js +229 -0
  119. package/dist/host/records.js.map +1 -0
  120. package/dist/host/tokens.d.ts +28 -0
  121. package/dist/host/tokens.js +52 -0
  122. package/dist/host/tokens.js.map +1 -0
  123. package/dist/host/types.d.ts +32 -0
  124. package/dist/host/types.js +11 -0
  125. package/dist/host/types.js.map +1 -0
  126. package/dist/index.d.ts +18 -0
  127. package/dist/index.js +51 -0
  128. package/dist/index.js.map +1 -0
  129. package/package.json +66 -3
@@ -0,0 +1,59 @@
1
+ import { escapeHtml } from './shell.js';
2
+ /** Server-Sent Events endpoint the page listens on. */
3
+ export const EVENTS_PATH = '/_pwf/events';
4
+ /** The current build's status as JSON. */
5
+ export const STATUS_PATH = '/_pwf/status';
6
+ /** Where the dev server serves the reload client's script. */
7
+ export const RELOAD_PATH = '/_pwf/reload.js';
8
+ /** The reload client's script: reloads the page when the server announces a new build. */
9
+ export const RELOAD_JS = `(function () {
10
+ if (typeof EventSource === 'undefined') return
11
+ var es = new EventSource('${EVENTS_PATH}')
12
+ es.addEventListener('reload', function () { location.reload() })
13
+ })()
14
+ `;
15
+ /**
16
+ * The reload client, as markup for a page. A script file on the dev server's
17
+ * own origin, not an inline script, so the app's Content Security Policy
18
+ * covers it with 'self' (and its event stream with connect-src 'self').
19
+ */
20
+ export const RELOAD_CLIENT = `
21
+ <script src="${RELOAD_PATH}"></script>`;
22
+ /** The page shown while the last build has errors. */
23
+ export function overlayHtml(diagnostics, title = 'Build failed') {
24
+ const errors = diagnostics.filter((d) => d.severity === 'error');
25
+ const rest = diagnostics.length - errors.length;
26
+ const items = errors
27
+ .map((d) => `<li><code>${escapeHtml(d.code)}</code>${d.path ? ` <span class="path">${escapeHtml(d.path)}${d.line !== undefined ? `:${d.line}` : ''}</span>` : ''}<pre>${escapeHtml(d.message)}</pre></li>`)
28
+ .join('\n');
29
+ return `<!doctype html>
30
+ <html lang="en">
31
+ <head>
32
+ <meta charset="UTF-8" />
33
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
34
+ <title>${escapeHtml(title)} — pwf dev</title>
35
+ <style>
36
+ body { margin: 0; font: 14px/1.5 ui-sans-serif, system-ui, sans-serif; background: #1c1917; color: #fafaf9; }
37
+ main { max-width: 960px; margin: 0 auto; padding: 32px 16px; }
38
+ h1 { font-size: 20px; color: #fca5a5; margin: 0 0 4px; }
39
+ p { color: #d6d3d1; margin: 0 0 24px; }
40
+ ul { list-style: none; padding: 0; margin: 0; }
41
+ li { border-left: 3px solid #f87171; background: #292524; padding: 12px 16px; margin: 0 0 12px; border-radius: 4px; }
42
+ code { color: #fca5a5; font-weight: 600; }
43
+ .path { color: #a8a29e; margin-left: 8px; }
44
+ pre { white-space: pre-wrap; word-break: break-word; margin: 8px 0 0; font: 13px/1.5 ui-monospace, Menlo, Consolas, monospace; }
45
+ </style>
46
+ </head>
47
+ <body>
48
+ <main>
49
+ <h1>${escapeHtml(title)}</h1>
50
+ <p>${errors.length} error${errors.length === 1 ? '' : 's'}${rest > 0 ? `, ${rest} other finding${rest === 1 ? '' : 's'} in the terminal` : ''}. Fix the files and the page reloads.</p>
51
+ <ul>
52
+ ${items}
53
+ </ul>
54
+ </main>${RELOAD_CLIENT}
55
+ </body>
56
+ </html>
57
+ `;
58
+ }
59
+ //# sourceMappingURL=overlay.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"overlay.js","sourceRoot":"","sources":["../../src/dev/overlay.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAEvC,uDAAuD;AACvD,MAAM,CAAC,MAAM,WAAW,GAAG,cAAc,CAAA;AACzC,0CAA0C;AAC1C,MAAM,CAAC,MAAM,WAAW,GAAG,cAAc,CAAA;AAEzC,8DAA8D;AAC9D,MAAM,CAAC,MAAM,WAAW,GAAG,iBAAiB,CAAA;AAE5C,0FAA0F;AAC1F,MAAM,CAAC,MAAM,SAAS,GAAG;;8BAEK,WAAW;;;CAGxC,CAAA;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG;mBACV,WAAW,aAAa,CAAA;AAE3C,sDAAsD;AACtD,MAAM,UAAU,WAAW,CAAC,WAA0C,EAAE,KAAK,GAAG,cAAc;IAC5F,MAAM,MAAM,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAA;IAChE,MAAM,IAAI,GAAG,WAAW,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAA;IAC/C,MAAM,KAAK,GAAG,MAAM;SACjB,GAAG,CACF,CAAC,CAAC,EAAE,EAAE,CAAC,aAAa,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,uBAAuB,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,QAAQ,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,aAAa,CACtM;SACA,IAAI,CAAC,IAAI,CAAC,CAAA;IACb,OAAO;;;;;aAKI,UAAU,CAAC,KAAK,CAAC;;;;;;;;;;;;;;;YAelB,UAAU,CAAC,KAAK,CAAC;WAClB,MAAM,CAAC,MAAM,SAAS,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,iBAAiB,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,kBAAkB,CAAC,CAAC,CAAC,EAAE;;EAEjJ,KAAK;;aAEM,aAAa;;;CAGzB,CAAA;AACD,CAAC","sourcesContent":["/**\n * What `pwf dev` adds to a page: the reload client, and the overlay shown\n * instead of the app when the last build failed.\n */\nimport type { AssembleDiagnostic } from './diagnostics.js'\nimport { escapeHtml } from './shell.js'\n\n/** Server-Sent Events endpoint the page listens on. */\nexport const EVENTS_PATH = '/_pwf/events'\n/** The current build's status as JSON. */\nexport const STATUS_PATH = '/_pwf/status'\n\n/** Where the dev server serves the reload client's script. */\nexport const RELOAD_PATH = '/_pwf/reload.js'\n\n/** The reload client's script: reloads the page when the server announces a new build. */\nexport const RELOAD_JS = `(function () {\n if (typeof EventSource === 'undefined') return\n var es = new EventSource('${EVENTS_PATH}')\n es.addEventListener('reload', function () { location.reload() })\n})()\n`\n\n/**\n * The reload client, as markup for a page. A script file on the dev server's\n * own origin, not an inline script, so the app's Content Security Policy\n * covers it with 'self' (and its event stream with connect-src 'self').\n */\nexport const RELOAD_CLIENT = `\n <script src=\"${RELOAD_PATH}\"></script>`\n\n/** The page shown while the last build has errors. */\nexport function overlayHtml(diagnostics: readonly AssembleDiagnostic[], title = 'Build failed'): string {\n const errors = diagnostics.filter((d) => d.severity === 'error')\n const rest = diagnostics.length - errors.length\n const items = errors\n .map(\n (d) => `<li><code>${escapeHtml(d.code)}</code>${d.path ? ` <span class=\"path\">${escapeHtml(d.path)}${d.line !== undefined ? `:${d.line}` : ''}</span>` : ''}<pre>${escapeHtml(d.message)}</pre></li>`\n )\n .join('\\n')\n return `<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n <title>${escapeHtml(title)} — pwf dev</title>\n <style>\n body { margin: 0; font: 14px/1.5 ui-sans-serif, system-ui, sans-serif; background: #1c1917; color: #fafaf9; }\n main { max-width: 960px; margin: 0 auto; padding: 32px 16px; }\n h1 { font-size: 20px; color: #fca5a5; margin: 0 0 4px; }\n p { color: #d6d3d1; margin: 0 0 24px; }\n ul { list-style: none; padding: 0; margin: 0; }\n li { border-left: 3px solid #f87171; background: #292524; padding: 12px 16px; margin: 0 0 12px; border-radius: 4px; }\n code { color: #fca5a5; font-weight: 600; }\n .path { color: #a8a29e; margin-left: 8px; }\n pre { white-space: pre-wrap; word-break: break-word; margin: 8px 0 0; font: 13px/1.5 ui-monospace, Menlo, Consolas, monospace; }\n </style>\n </head>\n <body>\n <main>\n <h1>${escapeHtml(title)}</h1>\n <p>${errors.length} error${errors.length === 1 ? '' : 's'}${rest > 0 ? `, ${rest} other finding${rest === 1 ? '' : 's'} in the terminal` : ''}. Fix the files and the page reloads.</p>\n <ul>\n${items}\n </ul>\n </main>${RELOAD_CLIENT}\n </body>\n</html>\n`\n}\n"]}
@@ -0,0 +1,27 @@
1
+ import type { CreateLocalHost } from '../host/types.js';
2
+ import { type AssembleOptions } from './assemble.js';
3
+ import { type AssembleDiagnostic } from './diagnostics.js';
4
+ import type { DevServer, DevServerOptions, StartDevServer } from './types.js';
5
+ import { type WatchOptions } from './watcher.js';
6
+ export declare const DEFAULT_PORT = 4600;
7
+ export declare const DEFAULT_HOSTNAME = "127.0.0.1";
8
+ /** Where a host this server creates is mounted. */
9
+ export declare const HOST_PREFIX = "/_host";
10
+ /** Seams for the `dev` command and for tests; not part of the public contract. */
11
+ export interface DevServerInternals {
12
+ /** Watching; `false` turns it off. */
13
+ readonly watch?: WatchOptions | false;
14
+ /** Makes a host when the app needs one and none was passed. */
15
+ readonly createHost?: CreateLocalHost;
16
+ /** Assembly options (runtime bundles, cache folder). */
17
+ readonly assemble?: Pick<AssembleOptions, 'runtime' | 'cacheDir' | 'publishedAt'>;
18
+ }
19
+ export declare function contentTypeOf(path: string): string;
20
+ /** The address a browser should open for a bound interface. */
21
+ export declare function urlFor(hostname: string, port: number): string;
22
+ /** Lines for the terminal: everything that is the app's own, and a count of what pinned packs say. */
23
+ export declare function logLines(diagnostics: readonly AssembleDiagnostic[]): string[];
24
+ /** Starts the dev server with the internal seams. {@link startDevServer} is the public entry. */
25
+ export declare function createDevServer(options: DevServerOptions, internals?: DevServerInternals): Promise<DevServer>;
26
+ /** Implements the `pwf dev` contract (see ./types.ts). */
27
+ export declare const startDevServer: StartDevServer;
@@ -0,0 +1,268 @@
1
+ /**
2
+ * `pwf dev`: serves an app folder as a pwapp and keeps it current.
3
+ *
4
+ * One `node:http` server on 127.0.0.1 (port 4600 by default). It assembles
5
+ * the app into memory (see ./assemble.ts), serves the files from there,
6
+ * watches the folder, reassembles on change and tells the open page to reload
7
+ * through Server-Sent Events. While the last build has errors the page is an
8
+ * overlay listing them; the terminal gets the same list.
9
+ *
10
+ * When the app declares sign-in or records it needs a local host. A host
11
+ * passed in `localHost` is mounted on the same server: requests it claims go
12
+ * to `host.handle()`, and the envelope carries `host.envelopeEntries()`.
13
+ * This module never constructs a host itself (it depends on the host's
14
+ * interface only); a caller that can make one passes `createHost`.
15
+ */
16
+ import { createServer } from 'node:http';
17
+ import { extname, join, resolve } from 'node:path';
18
+ import { assemblePwapp } from './assemble.js';
19
+ import { createCompileCache } from './compile.js';
20
+ import { formatDiagnostic } from './diagnostics.js';
21
+ import { hostNeeds } from './needs-host.js';
22
+ import { EVENTS_PATH, overlayHtml, RELOAD_CLIENT, RELOAD_JS, RELOAD_PATH, STATUS_PATH } from './overlay.js';
23
+ import { withBaseHref } from './shell.js';
24
+ import { PWF_DIR } from './sources.js';
25
+ import { watchFolder } from './watcher.js';
26
+ export const DEFAULT_PORT = 4600;
27
+ export const DEFAULT_HOSTNAME = '127.0.0.1';
28
+ /** Where a host this server creates is mounted. */
29
+ export const HOST_PREFIX = '/_host';
30
+ const CONTENT_TYPES = {
31
+ '.html': 'text/html; charset=utf-8',
32
+ '.js': 'text/javascript; charset=utf-8',
33
+ '.mjs': 'text/javascript; charset=utf-8',
34
+ '.json': 'application/json; charset=utf-8',
35
+ '.map': 'application/json; charset=utf-8',
36
+ '.css': 'text/css; charset=utf-8',
37
+ '.svg': 'image/svg+xml',
38
+ '.png': 'image/png',
39
+ '.jpg': 'image/jpeg',
40
+ '.jpeg': 'image/jpeg',
41
+ '.gif': 'image/gif',
42
+ '.webp': 'image/webp',
43
+ '.avif': 'image/avif',
44
+ '.ico': 'image/x-icon',
45
+ '.woff2': 'font/woff2',
46
+ '.txt': 'text/plain; charset=utf-8',
47
+ };
48
+ export function contentTypeOf(path) {
49
+ return CONTENT_TYPES[extname(path).toLowerCase()] ?? 'application/octet-stream';
50
+ }
51
+ function isLocalHost(value) {
52
+ return (value !== null &&
53
+ typeof value === 'object' &&
54
+ typeof value.handle === 'function' &&
55
+ typeof value.envelopeEntries === 'function');
56
+ }
57
+ /** The address a browser should open for a bound interface. */
58
+ export function urlFor(hostname, port) {
59
+ const h = hostname === '0.0.0.0' || hostname === '::' || hostname === '' ? 'localhost' : hostname;
60
+ return `http://${h.includes(':') ? `[${h}]` : h}:${port}/`;
61
+ }
62
+ function send(res, status, type, body, head = false) {
63
+ res.writeHead(status, {
64
+ 'content-type': type,
65
+ 'cache-control': 'no-store',
66
+ 'content-length': typeof body === 'string' ? Buffer.byteLength(body) : body.byteLength,
67
+ });
68
+ res.end(head ? undefined : body);
69
+ }
70
+ /** Lines for the terminal: everything that is the app's own, and a count of what pinned packs say. */
71
+ export function logLines(diagnostics) {
72
+ const own = [];
73
+ let packWarnings = 0;
74
+ for (const d of diagnostics) {
75
+ if (d.severity !== 'error' && d.path?.startsWith(`${PWF_DIR}/`)) {
76
+ packWarnings++;
77
+ continue;
78
+ }
79
+ if (d.severity === 'info')
80
+ continue;
81
+ own.push(formatDiagnostic(d));
82
+ }
83
+ if (packWarnings > 0)
84
+ own.push(`${packWarnings} finding${packWarnings === 1 ? '' : 's'} in pinned packs (\`pwf build\` lists them)`);
85
+ return own;
86
+ }
87
+ /** Starts the dev server with the internal seams. {@link startDevServer} is the public entry. */
88
+ export async function createDevServer(options, internals = {}) {
89
+ const root = resolve(options.root);
90
+ const log = options.log ?? (() => undefined);
91
+ const hostname = options.hostname ?? DEFAULT_HOSTNAME;
92
+ const cache = createCompileCache(internals.assemble?.cacheDir === undefined ? join(root, PWF_DIR, 'build-cache') : internals.assemble.cacheDir);
93
+ // ── the local host ───────────────────────────────────────────────
94
+ let host = null;
95
+ if (isLocalHost(options.localHost)) {
96
+ host = options.localHost;
97
+ }
98
+ else if (options.localHost !== false) {
99
+ const needs = await hostNeeds(root);
100
+ const wanted = options.localHost === true || needs.length > 0;
101
+ if (wanted && internals.createHost) {
102
+ host = internals.createHost({ root, prefix: HOST_PREFIX });
103
+ }
104
+ else if (wanted) {
105
+ log(`note: this app declares ${needs.join(' and ') || 'a host'}, but no local host is mounted — pages render, ${needs.includes('sign-in') ? 'sign-in cannot complete and ' : ''}data stays empty`);
106
+ }
107
+ }
108
+ // ── state ────────────────────────────────────────────────────────
109
+ let state = null;
110
+ const packMemo = new Map();
111
+ const clients = new Set();
112
+ let origin = '';
113
+ const broadcast = (event, data) => {
114
+ const frame = `event: ${event}\ndata: ${JSON.stringify(data)}\n\n`;
115
+ for (const c of clients)
116
+ c.write(frame);
117
+ };
118
+ const build = async () => {
119
+ let result;
120
+ try {
121
+ result = await assemblePwapp(root, {
122
+ ...internals.assemble,
123
+ cache,
124
+ packMemo,
125
+ shellExtra: RELOAD_CLIENT,
126
+ ...(host ? { envelopeEntries: host.envelopeEntries(origin) } : {}),
127
+ });
128
+ }
129
+ catch (e) {
130
+ const diagnostics = [
131
+ { severity: 'error', code: 'INTERNAL', message: e instanceof Error ? (e.stack ?? e.message) : String(e) },
132
+ ];
133
+ result = {
134
+ ok: false,
135
+ files: state?.files ?? new Map(),
136
+ diagnostics,
137
+ stats: { pages: 0, blocks: 0, engines: 0, atoms: 0, compiled: 0, fromCache: 0, failed: 0 },
138
+ ms: 0,
139
+ };
140
+ }
141
+ state = result;
142
+ const { stats } = result;
143
+ log(`${result.ok ? 'built' : 'build failed'} in ${result.ms} ms — ${stats.pages} page${stats.pages === 1 ? '' : 's'}, ${stats.blocks} block type${stats.blocks === 1 ? '' : 's'}, ${stats.compiled}/${stats.atoms} atoms compiled (${stats.fromCache} from cache${stats.failed ? `, ${stats.failed} failed` : ''})`);
144
+ for (const line of logLines(result.diagnostics))
145
+ log(` ${line}`);
146
+ broadcast('reload', { ok: result.ok });
147
+ return { ok: result.ok, compiled: stats.compiled, diagnostics: result.diagnostics.map(formatDiagnostic), ms: result.ms };
148
+ };
149
+ let chain = Promise.resolve();
150
+ const rebuild = () => {
151
+ const next = chain.then(build, build);
152
+ chain = next.catch(() => undefined);
153
+ return next;
154
+ };
155
+ // ── HTTP ─────────────────────────────────────────────────────────
156
+ const handle = async (req, res) => {
157
+ const url = new URL(req.url ?? '/', 'http://localhost');
158
+ if (url.pathname === EVENTS_PATH) {
159
+ res.writeHead(200, { 'content-type': 'text/event-stream', 'cache-control': 'no-store', connection: 'keep-alive' });
160
+ res.write(': connected\n\n');
161
+ clients.add(res);
162
+ req.on('close', () => clients.delete(res));
163
+ return;
164
+ }
165
+ if (url.pathname === RELOAD_PATH) {
166
+ send(res, 200, CONTENT_TYPES['.js'], RELOAD_JS, req.method === 'HEAD');
167
+ return;
168
+ }
169
+ if (url.pathname === STATUS_PATH) {
170
+ send(res, 200, CONTENT_TYPES['.json'], JSON.stringify({ ok: state?.ok ?? false, diagnostics: state?.diagnostics ?? [] }));
171
+ return;
172
+ }
173
+ if (host && (await host.handle(req, res)))
174
+ return;
175
+ if (req.method !== 'GET' && req.method !== 'HEAD') {
176
+ send(res, 405, CONTENT_TYPES['.txt'], 'method not allowed');
177
+ return;
178
+ }
179
+ const head = req.method === 'HEAD';
180
+ let path;
181
+ try {
182
+ path = decodeURIComponent(url.pathname);
183
+ }
184
+ catch {
185
+ send(res, 400, CONTENT_TYPES['.txt'], 'bad path', head);
186
+ return;
187
+ }
188
+ let rel = path.replace(/^\/+/, '');
189
+ if (rel === '' || rel.endsWith('/'))
190
+ rel += 'index.html';
191
+ if (rel.split('/').some((s) => s === '..' || s === '.') || rel.includes('\\') || rel.includes('\0')) {
192
+ send(res, 400, CONTENT_TYPES['.txt'], 'bad path', head);
193
+ return;
194
+ }
195
+ const current = state;
196
+ const isPage = rel === 'index.html' || extname(rel) === '';
197
+ if (isPage && current && !current.ok) {
198
+ send(res, 200, CONTENT_TYPES['.html'], overlayHtml(current.diagnostics), head);
199
+ return;
200
+ }
201
+ // `_headers` configures a static host; it is not part of the site.
202
+ const file = rel === '_headers'
203
+ ? undefined
204
+ : (current?.files.get(rel) ?? (extname(rel) === '' ? current?.files.get('index.html') : undefined));
205
+ if (!file) {
206
+ send(res, 404, CONTENT_TYPES['.txt'], `not found: /${rel}`, head);
207
+ return;
208
+ }
209
+ // A nested page path answered with the shell needs the app's base, or the
210
+ // shell's relative script addresses resolve under the page's folder.
211
+ const body = isPage && rel !== 'index.html' ? withBaseHref(file) : file;
212
+ send(res, 200, contentTypeOf(isPage ? 'index.html' : rel), body, head);
213
+ };
214
+ const server = createServer((req, res) => {
215
+ handle(req, res).catch((e) => {
216
+ if (!res.headersSent)
217
+ send(res, 500, CONTENT_TYPES['.txt'], e instanceof Error ? e.message : String(e));
218
+ else
219
+ res.end();
220
+ });
221
+ });
222
+ await new Promise((ok, fail) => {
223
+ server.once('error', fail);
224
+ server.listen(options.port ?? DEFAULT_PORT, hostname, () => {
225
+ server.off('error', fail);
226
+ ok();
227
+ });
228
+ });
229
+ const port = server.address().port;
230
+ const url = urlFor(hostname, port);
231
+ origin = url.replace(/\/$/, '');
232
+ if (host) {
233
+ log(`local host mounted at ${origin}${HOST_PREFIX} — development only: records in memory, one user, any password`);
234
+ }
235
+ await rebuild();
236
+ let watcher = null;
237
+ if (internals.watch !== false) {
238
+ watcher = watchFolder(root, (paths) => {
239
+ // A pack build changed (or the platform could not say what did): read it again.
240
+ if (paths === null || paths.some((p) => p.startsWith(`${PWF_DIR}/packs`)))
241
+ packMemo.clear();
242
+ void rebuild();
243
+ }, internals.watch ?? {});
244
+ log(`watching ${root} (${watcher.mode === 'native' ? 'file events' : 'polling'})`);
245
+ }
246
+ let closed = null;
247
+ return {
248
+ url,
249
+ rebuild,
250
+ close() {
251
+ closed ??= (async () => {
252
+ watcher?.close();
253
+ for (const c of clients)
254
+ c.end();
255
+ clients.clear();
256
+ await new Promise((ok) => {
257
+ server.close(() => ok());
258
+ server.closeAllConnections();
259
+ });
260
+ await chain;
261
+ })();
262
+ return closed;
263
+ },
264
+ };
265
+ }
266
+ /** Implements the `pwf dev` contract (see ./types.ts). */
267
+ export const startDevServer = (options) => createDevServer(options);
268
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/dev/server.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `pwf dev`: serves an app folder as a pwapp and keeps it current.\n *\n * One `node:http` server on 127.0.0.1 (port 4600 by default). It assembles\n * the app into memory (see ./assemble.ts), serves the files from there,\n * watches the folder, reassembles on change and tells the open page to reload\n * through Server-Sent Events. While the last build has errors the page is an\n * overlay listing them; the terminal gets the same list.\n *\n * When the app declares sign-in or records it needs a local host. A host\n * passed in `localHost` is mounted on the same server: requests it claims go\n * to `host.handle()`, and the envelope carries `host.envelopeEntries()`.\n * This module never constructs a host itself (it depends on the host's\n * interface only); a caller that can make one passes `createHost`.\n */\nimport { createServer, type IncomingMessage, type ServerResponse } from 'node:http'\nimport type { AddressInfo } from 'node:net'\nimport { extname, join, resolve } from 'node:path'\nimport type { CreateLocalHost, LocalHost } from '../host/types.js'\nimport { assemblePwapp, type AssembleOptions, type AssembleResult } from './assemble.js'\nimport { createCompileCache } from './compile.js'\nimport { formatDiagnostic, type AssembleDiagnostic } from './diagnostics.js'\nimport { hostNeeds } from './needs-host.js'\nimport { EVENTS_PATH, overlayHtml, RELOAD_CLIENT, RELOAD_JS, RELOAD_PATH, STATUS_PATH } from './overlay.js'\nimport { withBaseHref } from './shell.js'\nimport { PWF_DIR, type PackMemo } from './sources.js'\nimport type { DevBuildResult, DevServer, DevServerOptions, StartDevServer } from './types.js'\nimport { watchFolder, type WatchOptions, type Watcher } from './watcher.js'\n\nexport const DEFAULT_PORT = 4600\nexport const DEFAULT_HOSTNAME = '127.0.0.1'\n/** Where a host this server creates is mounted. */\nexport const HOST_PREFIX = '/_host'\n\n/** Seams for the `dev` command and for tests; not part of the public contract. */\nexport interface DevServerInternals {\n /** Watching; `false` turns it off. */\n readonly watch?: WatchOptions | false\n /** Makes a host when the app needs one and none was passed. */\n readonly createHost?: CreateLocalHost\n /** Assembly options (runtime bundles, cache folder). */\n readonly assemble?: Pick<AssembleOptions, 'runtime' | 'cacheDir' | 'publishedAt'>\n}\n\nconst CONTENT_TYPES: Readonly<Record<string, string>> = {\n '.html': 'text/html; charset=utf-8',\n '.js': 'text/javascript; charset=utf-8',\n '.mjs': 'text/javascript; charset=utf-8',\n '.json': 'application/json; charset=utf-8',\n '.map': 'application/json; charset=utf-8',\n '.css': 'text/css; charset=utf-8',\n '.svg': 'image/svg+xml',\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.gif': 'image/gif',\n '.webp': 'image/webp',\n '.avif': 'image/avif',\n '.ico': 'image/x-icon',\n '.woff2': 'font/woff2',\n '.txt': 'text/plain; charset=utf-8',\n}\n\nexport function contentTypeOf(path: string): string {\n return CONTENT_TYPES[extname(path).toLowerCase()] ?? 'application/octet-stream'\n}\n\nfunction isLocalHost(value: unknown): value is LocalHost {\n return (\n value !== null &&\n typeof value === 'object' &&\n typeof (value as LocalHost).handle === 'function' &&\n typeof (value as LocalHost).envelopeEntries === 'function'\n )\n}\n\n/** The address a browser should open for a bound interface. */\nexport function urlFor(hostname: string, port: number): string {\n const h = hostname === '0.0.0.0' || hostname === '::' || hostname === '' ? 'localhost' : hostname\n return `http://${h.includes(':') ? `[${h}]` : h}:${port}/`\n}\n\nfunction send(res: ServerResponse, status: number, type: string, body: string | Uint8Array, head = false): void {\n res.writeHead(status, {\n 'content-type': type,\n 'cache-control': 'no-store',\n 'content-length': typeof body === 'string' ? Buffer.byteLength(body) : body.byteLength,\n })\n res.end(head ? undefined : body)\n}\n\n/** Lines for the terminal: everything that is the app's own, and a count of what pinned packs say. */\nexport function logLines(diagnostics: readonly AssembleDiagnostic[]): string[] {\n const own: string[] = []\n let packWarnings = 0\n for (const d of diagnostics) {\n if (d.severity !== 'error' && d.path?.startsWith(`${PWF_DIR}/`)) {\n packWarnings++\n continue\n }\n if (d.severity === 'info') continue\n own.push(formatDiagnostic(d))\n }\n if (packWarnings > 0) own.push(`${packWarnings} finding${packWarnings === 1 ? '' : 's'} in pinned packs (\\`pwf build\\` lists them)`)\n return own\n}\n\n/** Starts the dev server with the internal seams. {@link startDevServer} is the public entry. */\nexport async function createDevServer(options: DevServerOptions, internals: DevServerInternals = {}): Promise<DevServer> {\n const root = resolve(options.root)\n const log = options.log ?? ((): void => undefined)\n const hostname = options.hostname ?? DEFAULT_HOSTNAME\n const cache = createCompileCache(\n internals.assemble?.cacheDir === undefined ? join(root, PWF_DIR, 'build-cache') : internals.assemble.cacheDir\n )\n\n // ── the local host ───────────────────────────────────────────────\n let host: LocalHost | null = null\n if (isLocalHost(options.localHost)) {\n host = options.localHost\n } else if (options.localHost !== false) {\n const needs = await hostNeeds(root)\n const wanted = options.localHost === true || needs.length > 0\n if (wanted && internals.createHost) {\n host = internals.createHost({ root, prefix: HOST_PREFIX })\n } else if (wanted) {\n log(\n `note: this app declares ${needs.join(' and ') || 'a host'}, but no local host is mounted — pages render, ${\n needs.includes('sign-in') ? 'sign-in cannot complete and ' : ''\n }data stays empty`\n )\n }\n }\n\n // ── state ────────────────────────────────────────────────────────\n let state: AssembleResult | null = null\n const packMemo: PackMemo = new Map()\n const clients = new Set<ServerResponse>()\n let origin = ''\n\n const broadcast = (event: string, data: unknown): void => {\n const frame = `event: ${event}\\ndata: ${JSON.stringify(data)}\\n\\n`\n for (const c of clients) c.write(frame)\n }\n\n const build = async (): Promise<DevBuildResult> => {\n let result: AssembleResult\n try {\n result = await assemblePwapp(root, {\n ...internals.assemble,\n cache,\n packMemo,\n shellExtra: RELOAD_CLIENT,\n ...(host ? { envelopeEntries: host.envelopeEntries(origin) } : {}),\n })\n } catch (e) {\n const diagnostics: AssembleDiagnostic[] = [\n { severity: 'error', code: 'INTERNAL', message: e instanceof Error ? (e.stack ?? e.message) : String(e) },\n ]\n result = {\n ok: false,\n files: state?.files ?? new Map(),\n diagnostics,\n stats: { pages: 0, blocks: 0, engines: 0, atoms: 0, compiled: 0, fromCache: 0, failed: 0 },\n ms: 0,\n }\n }\n state = result\n const { stats } = result\n log(\n `${result.ok ? 'built' : 'build failed'} in ${result.ms} ms — ${stats.pages} page${stats.pages === 1 ? '' : 's'}, ${\n stats.blocks\n } block type${stats.blocks === 1 ? '' : 's'}, ${stats.compiled}/${stats.atoms} atoms compiled (${stats.fromCache} from cache${\n stats.failed ? `, ${stats.failed} failed` : ''\n })`\n )\n for (const line of logLines(result.diagnostics)) log(` ${line}`)\n broadcast('reload', { ok: result.ok })\n return { ok: result.ok, compiled: stats.compiled, diagnostics: result.diagnostics.map(formatDiagnostic), ms: result.ms }\n }\n\n let chain: Promise<unknown> = Promise.resolve()\n const rebuild = (): Promise<DevBuildResult> => {\n const next = chain.then(build, build)\n chain = next.catch(() => undefined)\n return next\n }\n\n // ── HTTP ─────────────────────────────────────────────────────────\n const handle = async (req: IncomingMessage, res: ServerResponse): Promise<void> => {\n const url = new URL(req.url ?? '/', 'http://localhost')\n if (url.pathname === EVENTS_PATH) {\n res.writeHead(200, { 'content-type': 'text/event-stream', 'cache-control': 'no-store', connection: 'keep-alive' })\n res.write(': connected\\n\\n')\n clients.add(res)\n req.on('close', () => clients.delete(res))\n return\n }\n if (url.pathname === RELOAD_PATH) {\n send(res, 200, CONTENT_TYPES['.js'] as string, RELOAD_JS, req.method === 'HEAD')\n return\n }\n if (url.pathname === STATUS_PATH) {\n send(res, 200, CONTENT_TYPES['.json'] as string, JSON.stringify({ ok: state?.ok ?? false, diagnostics: state?.diagnostics ?? [] }))\n return\n }\n if (host && (await host.handle(req, res))) return\n if (req.method !== 'GET' && req.method !== 'HEAD') {\n send(res, 405, CONTENT_TYPES['.txt'] as string, 'method not allowed')\n return\n }\n const head = req.method === 'HEAD'\n let path: string\n try {\n path = decodeURIComponent(url.pathname)\n } catch {\n send(res, 400, CONTENT_TYPES['.txt'] as string, 'bad path', head)\n return\n }\n let rel = path.replace(/^\\/+/, '')\n if (rel === '' || rel.endsWith('/')) rel += 'index.html'\n if (rel.split('/').some((s) => s === '..' || s === '.') || rel.includes('\\\\') || rel.includes('\\0')) {\n send(res, 400, CONTENT_TYPES['.txt'] as string, 'bad path', head)\n return\n }\n const current = state\n const isPage = rel === 'index.html' || extname(rel) === ''\n if (isPage && current && !current.ok) {\n send(res, 200, CONTENT_TYPES['.html'] as string, overlayHtml(current.diagnostics), head)\n return\n }\n // `_headers` configures a static host; it is not part of the site.\n const file =\n rel === '_headers'\n ? undefined\n : (current?.files.get(rel) ?? (extname(rel) === '' ? current?.files.get('index.html') : undefined))\n if (!file) {\n send(res, 404, CONTENT_TYPES['.txt'] as string, `not found: /${rel}`, head)\n return\n }\n // A nested page path answered with the shell needs the app's base, or the\n // shell's relative script addresses resolve under the page's folder.\n const body = isPage && rel !== 'index.html' ? withBaseHref(file) : file\n send(res, 200, contentTypeOf(isPage ? 'index.html' : rel), body, head)\n }\n\n const server = createServer((req, res) => {\n handle(req, res).catch((e: unknown) => {\n if (!res.headersSent) send(res, 500, CONTENT_TYPES['.txt'] as string, e instanceof Error ? e.message : String(e))\n else res.end()\n })\n })\n await new Promise<void>((ok, fail) => {\n server.once('error', fail)\n server.listen(options.port ?? DEFAULT_PORT, hostname, () => {\n server.off('error', fail)\n ok()\n })\n })\n const port = (server.address() as AddressInfo).port\n const url = urlFor(hostname, port)\n origin = url.replace(/\\/$/, '')\n if (host) {\n log(`local host mounted at ${origin}${HOST_PREFIX} — development only: records in memory, one user, any password`)\n }\n\n await rebuild()\n\n let watcher: Watcher | null = null\n if (internals.watch !== false) {\n watcher = watchFolder(\n root,\n (paths) => {\n // A pack build changed (or the platform could not say what did): read it again.\n if (paths === null || paths.some((p) => p.startsWith(`${PWF_DIR}/packs`))) packMemo.clear()\n void rebuild()\n },\n internals.watch ?? {}\n )\n log(`watching ${root} (${watcher.mode === 'native' ? 'file events' : 'polling'})`)\n }\n\n let closed: Promise<void> | null = null\n return {\n url,\n rebuild,\n close() {\n closed ??= (async () => {\n watcher?.close()\n for (const c of clients) c.end()\n clients.clear()\n await new Promise<void>((ok) => {\n server.close(() => ok())\n server.closeAllConnections()\n })\n await chain\n })()\n return closed\n },\n }\n}\n\n/** Implements the `pwf dev` contract (see ./types.ts). */\nexport const startDevServer: StartDevServer = (options) => createDevServer(options)\n"]}
@@ -0,0 +1,94 @@
1
+ /** The runner and renderer bundles a pwapp ships. */
2
+ export interface RuntimeAssets {
3
+ /** `assets/runner.js` — the ES module exporting `runApp`. */
4
+ readonly runnerJs: string;
5
+ /** Its source map, when the build has one. */
6
+ readonly runnerMap?: string;
7
+ /** `assets/embed.js` — the classic script that defines `window.PWFabric`. */
8
+ readonly embedJs: string;
9
+ }
10
+ /** Reads the built runner and renderer from the installed engine packages (once per process). */
11
+ export declare function loadRuntimeAssets(): Promise<RuntimeAssets>;
12
+ /**
13
+ * Globals the renderer bundle does not seed itself, and the vendor-table key
14
+ * the shell seeds each from (see {@link buildShellHtml}), so blocks that reach
15
+ * for these get the renderer's own instance instead of `undefined`.
16
+ *
17
+ * The renderer bundle (`@pwfabric/embed`'s `embed.js`) carries `react-dom`,
18
+ * `@pwfabric/core` and `@pwfabric/authoring` (the `sdk@v1` shim's package)
19
+ * in its `__PW_VENDOR__` table but does not seed these globals itself, so a
20
+ * block that uses a VALUE from core or the authoring kit (as opposed to a
21
+ * type) reads the renderer's instance here instead of `undefined`.
22
+ */
23
+ export declare const SHELL_SEEDED_GLOBALS: Readonly<Record<string, string>>;
24
+ /** Whether `name` can be an `export const { name }` binding. */
25
+ export declare function isExportableName(name: string): boolean;
26
+ /** Evaluates the renderer bundle in an isolated context and returns what it put on `globalThis`. */
27
+ export declare function seededGlobals(embedJs: string): Record<string, unknown>;
28
+ /** The global each shim file reads (every specifier sharing a shim names the same global). */
29
+ export declare function shimGlobal(shim: string): string;
30
+ /** Source of one shim module. */
31
+ export declare function shimSource(shim: string, global: string, names: readonly string[]): string;
32
+ /**
33
+ * Every shim file the import map can point at, with its source.
34
+ *
35
+ * Names come from, in order: the global the renderer seeds; the vendor table
36
+ * entry the shell seeds the global from; the package itself (for globals no
37
+ * standalone page seeds — their exports resolve to `undefined`, which keeps a
38
+ * block linking instead of failing at load).
39
+ */
40
+ export declare function buildShims(embedJs: string): Promise<Map<string, string>>;
41
+ /** {@link buildShims}, once per renderer bundle. */
42
+ export declare function shimsFor(embedJs: string): Promise<Map<string, string>>;
43
+ /** The import map: every host import → `./shims/<file>`. */
44
+ export declare function importMap(): {
45
+ readonly imports: Record<string, string>;
46
+ };
47
+ export declare function escapeHtml(s: string): string;
48
+ /** JSON safe to place inside a `<script>` element. */
49
+ export declare function scriptJson(value: unknown): string;
50
+ export interface ShellOptions {
51
+ readonly title: string;
52
+ /** `<html lang>`; default `en`. */
53
+ readonly lang?: string;
54
+ /** The app's appearance configuration (its webfonts are linked). */
55
+ readonly appearance?: Record<string, unknown>;
56
+ /** Markup appended to `<body>` (the dev server's reload client). */
57
+ readonly bodyExtra?: string;
58
+ /** The policy for the shell's `<meta http-equiv="Content-Security-Policy">`; none when absent. */
59
+ readonly csp?: string;
60
+ }
61
+ /** The exact text of the shell's inline import map (the CSP hashes this). */
62
+ export declare function importMapText(): string;
63
+ /**
64
+ * `assets/seed.js`: seeds the globals the renderer does not seed itself from
65
+ * its vendor table (a file, not an inline script, so the policy needs no hash).
66
+ */
67
+ export declare function seedScript(): string;
68
+ /** `assets/boot.js`: the module that starts the runner. */
69
+ export declare function bootScript(locale?: 'en' | 'tr'): string;
70
+ /** A BCP-47-looking tag, or `en`. */
71
+ export declare function htmlLangOf(locale: string | undefined): string;
72
+ /** `tr-TR` → `tr`; the runner's copy knows `en` and `tr`. */
73
+ export declare function runnerLocaleOf(locale: string | undefined): 'en' | 'tr';
74
+ /**
75
+ * `index.html`, in the order the pwapp format requires: import map, the
76
+ * renderer (a classic script that seeds the shared globals) and `seed.js`,
77
+ * the mount element, then `boot.js`, which starts the runner. The import map
78
+ * is the only inline script, so a Content Security Policy needs one hash.
79
+ * Every address is relative, so the folder works at a host root, under a
80
+ * sub-path, or from disk behind any static server.
81
+ */
82
+ export declare function buildShellHtml(options: ShellOptions): string;
83
+ /**
84
+ * The shell with `<base href="/">` as the first element of its head, unless it
85
+ * already declares a base.
86
+ *
87
+ * The shell addresses its scripts and the import map relatively (`./assets/…`,
88
+ * `./shims/…`) so the folder works under any sub-path. When a server answers a
89
+ * nested page path (`/team/members`) with that shell, the browser would resolve
90
+ * those addresses against `/team/` and miss every file. The pwapp format puts
91
+ * the fix on the host: under path routing it declares the app's base with
92
+ * `<base href>`. This server serves the app at its root, so the base is `/`.
93
+ */
94
+ export declare function withBaseHref(body: Uint8Array, href?: string): Uint8Array;