@thomas.f/prnext 0.1.16-alpha → 0.1.18-alpha

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 (215) hide show
  1. package/AI.md +63 -0
  2. package/README.md +35 -503
  3. package/build/ADAPTERS.md +27 -2
  4. package/build/actions.mjs +6 -1
  5. package/build/analyze.mjs +340 -0
  6. package/build/app-config.mjs +20 -8
  7. package/build/app-static.mjs +41 -14
  8. package/build/app-styles.mjs +79 -0
  9. package/build/app.mjs +99 -31
  10. package/build/build-lock.mjs +6 -6
  11. package/build/bundle-runtime.mjs +39 -11
  12. package/build/cache-components.mjs +1 -1
  13. package/build/cache-handlers.mjs +1 -1
  14. package/build/check.mjs +56 -11
  15. package/build/compact-css.mjs +53 -0
  16. package/build/compilation-cache.mjs +269 -0
  17. package/build/compilation-store.mjs +142 -0
  18. package/build/compiler.mjs +51 -4
  19. package/build/concurrent.mjs +12 -0
  20. package/build/config.mjs +50 -13
  21. package/build/create.mjs +239 -0
  22. package/build/css-modules.mjs +65 -10
  23. package/build/deployment-assets.mjs +89 -0
  24. package/build/deployment-config.mjs +18 -0
  25. package/build/dev-compiler-health.mjs +11 -0
  26. package/build/dev-css-update.mjs +85 -0
  27. package/build/dev-dependencies.mjs +44 -0
  28. package/build/dev-discovery.mjs +28 -0
  29. package/build/dev-worker.mjs +14 -5
  30. package/build/dev.mjs +64 -6
  31. package/build/dynamic.mjs +1 -1
  32. package/build/edge.mjs +5 -4
  33. package/build/fonts.mjs +1 -1
  34. package/build/framework-imports.mjs +50 -0
  35. package/build/html-only-config.mjs +53 -0
  36. package/build/html-only-graph.mjs +67 -0
  37. package/build/incremental.mjs +8 -4
  38. package/build/index.mjs +140 -112
  39. package/build/inspect.mjs +3 -0
  40. package/build/inspection-summary.mjs +37 -4
  41. package/build/instrumentation.mjs +30 -0
  42. package/build/loader-cache.mjs +53 -26
  43. package/build/metadata-routes.mjs +3 -4
  44. package/build/migrate.mjs +60 -57
  45. package/build/migration-audit.mjs +1 -1
  46. package/build/native-type-config.mjs +37 -0
  47. package/build/npm-packages.mjs +3 -18
  48. package/build/package-manager.mjs +308 -0
  49. package/build/persistent-cache.mjs +112 -0
  50. package/build/precompress.mjs +62 -30
  51. package/build/prerender-cache.mjs +70 -0
  52. package/build/prerender-config.mjs +29 -0
  53. package/build/prerender-pages.mjs +100 -0
  54. package/build/prerender-pool.mjs +165 -0
  55. package/build/prerender-process-worker.mjs +58 -0
  56. package/build/prerender-worker.mjs +10 -0
  57. package/build/production-builder.mjs +129 -0
  58. package/build/react-compiler-report.mjs +225 -0
  59. package/build/react-compiler.mjs +139 -0
  60. package/build/runtime-contract.mjs +2 -2
  61. package/build/scan.mjs +4 -4
  62. package/build/source-analysis.mjs +42 -0
  63. package/build/static-adapter.mjs +13 -5
  64. package/build/static-export.mjs +4 -4
  65. package/build/static-input.mjs +48 -0
  66. package/build/styles.mjs +17 -3
  67. package/build/transform-cache.mjs +37 -4
  68. package/build/transform.mjs +1 -1
  69. package/build/type-command.mjs +41 -0
  70. package/build/typed-routes.mjs +95 -0
  71. package/build/typescript-dev-worker.mjs +18 -0
  72. package/build/typescript-dev.mjs +49 -0
  73. package/build/typescript-native-worker.mjs +55 -0
  74. package/build/typescript-native.mjs +18 -0
  75. package/build/typescript-toolchain.mjs +19 -0
  76. package/build/typescript-watch.mjs +22 -0
  77. package/build/typescript.mjs +11 -8
  78. package/build/webpack-graph.mjs +170 -30
  79. package/build/worker-memory.mjs +9 -0
  80. package/build/worker-preload.mjs +9 -0
  81. package/cli.mjs +162 -122
  82. package/compat/activity-cache.cjs +26 -0
  83. package/compat/after.cjs +6 -4
  84. package/compat/app-layout-context.cjs +52 -2
  85. package/compat/base64.cjs +10 -0
  86. package/compat/cache-handlers.cjs +12 -2
  87. package/compat/cache-wire.cjs +28 -0
  88. package/compat/cache.cjs +2 -1
  89. package/compat/cache.d.cts +4 -0
  90. package/compat/cookies.cjs +21 -5
  91. package/compat/data-cache.cjs +12 -8
  92. package/compat/flight-delta.cjs +150 -0
  93. package/compat/headers.cjs +4 -2
  94. package/compat/incremental-cache.cjs +20 -7
  95. package/compat/index.d.ts +89 -10
  96. package/compat/link.cjs +12 -9
  97. package/compat/link.d.cts +4 -3
  98. package/compat/navigation-authorization.cjs +9 -0
  99. package/compat/navigation-stages.cjs +19 -0
  100. package/compat/navigation.d.cts +4 -3
  101. package/compat/offline-store.cjs +11 -0
  102. package/compat/offline.cjs +5 -0
  103. package/compat/offline.d.cts +2 -0
  104. package/compat/redis-cache.cjs +201 -0
  105. package/compat/redis-cache.d.cts +37 -0
  106. package/compat/router.d.cts +4 -3
  107. package/compat/server-primitives.cjs +31 -11
  108. package/compat/server.d.cts +15 -2
  109. package/compat/static-generation.cjs +7 -0
  110. package/compat/typed-route.d.cts +17 -0
  111. package/compat/use-cache.cjs +3 -0
  112. package/docs/ai/compatibility.md +275 -0
  113. package/docs/ai/data.md +288 -0
  114. package/docs/ai/deployment.md +269 -0
  115. package/docs/ai/manifest.json +11 -0
  116. package/docs/ai/quickstart.md +171 -0
  117. package/docs/ai/routing.md +214 -0
  118. package/docs/ai/troubleshooting.md +155 -0
  119. package/index.mjs +2 -0
  120. package/native/diagnostics.mjs +34 -0
  121. package/native/resolve.mjs +7 -2
  122. package/package.json +44 -7
  123. package/release/ai-docs.mjs +48 -0
  124. package/release/verify.mjs +2 -0
  125. package/runtime/action-client.mjs +10 -4
  126. package/runtime/action-server.mjs +2 -0
  127. package/runtime/ai-docs.mjs +202 -0
  128. package/runtime/app-client.mjs +226 -68
  129. package/runtime/app-content.mjs +4 -3
  130. package/runtime/app-partial-client.mjs +3 -0
  131. package/runtime/app-partial.mjs +1 -1
  132. package/runtime/app-prefetch-constants.mjs +4 -0
  133. package/runtime/app-prefetch.mjs +2 -3
  134. package/runtime/app-render-key.mjs +5 -0
  135. package/runtime/app-render.mjs +100 -28
  136. package/runtime/app-routing.mjs +22 -9
  137. package/runtime/app-static.mjs +18 -8
  138. package/runtime/app-stream-html.mjs +10 -53
  139. package/runtime/app-style-selection.mjs +34 -0
  140. package/runtime/app-styles.mjs +154 -0
  141. package/runtime/cache-components-rsc.mjs +4 -1
  142. package/runtime/cache-request.mjs +2 -1
  143. package/runtime/cloud/archive.mjs +5 -6
  144. package/runtime/compile-cache.mjs +8 -0
  145. package/runtime/deployment-client.mjs +19 -0
  146. package/runtime/dev-ai-brands.mjs +26 -0
  147. package/runtime/dev-ai-style.mjs +99 -0
  148. package/runtime/dev-build-reasons.mjs +25 -0
  149. package/runtime/dev-cache-control.mjs +53 -0
  150. package/runtime/dev-client.mjs +245 -46
  151. package/runtime/dev-compiler-broker.mjs +42 -0
  152. package/runtime/dev-console-copy.mjs +19 -0
  153. package/runtime/dev-console.mjs +106 -0
  154. package/runtime/dev-dependency-graph.mjs +40 -0
  155. package/runtime/dev-element-picker-style.mjs +27 -0
  156. package/runtime/dev-element-picker.mjs +213 -0
  157. package/runtime/dev-error-overlay-style.mjs +41 -0
  158. package/runtime/dev-error-overlay.mjs +167 -0
  159. package/runtime/dev-error-report.mjs +121 -0
  160. package/runtime/dev-file-changes.mjs +1 -1
  161. package/runtime/dev-gateway.mjs +44 -0
  162. package/runtime/dev-host-relay.mjs +66 -0
  163. package/runtime/dev-indicator.mjs +191 -0
  164. package/runtime/dev-issue-motion.mjs +214 -0
  165. package/runtime/dev-lazy-tool.mjs +34 -0
  166. package/runtime/dev-logo.mjs +2 -0
  167. package/runtime/dev-notification.mjs +81 -0
  168. package/runtime/dev-popout.mjs +34 -0
  169. package/runtime/dev-position.mjs +107 -0
  170. package/runtime/dev-preview-touch.mjs +122 -0
  171. package/runtime/dev-runtime-pool.mjs +45 -0
  172. package/runtime/dev-runtime-spare.mjs +57 -0
  173. package/runtime/dev-tools-client.mjs +155 -0
  174. package/runtime/dev-tools-mcp.mjs +126 -0
  175. package/runtime/dev-tools-metrics.mjs +31 -0
  176. package/runtime/dev-tools-model.mjs +42 -0
  177. package/runtime/dev-tools-panel.mjs +341 -0
  178. package/runtime/dev-tools-result.mjs +37 -0
  179. package/runtime/dev-tools-server.mjs +256 -0
  180. package/runtime/dev-tools-source.mjs +104 -0
  181. package/runtime/dev-tooltip.mjs +94 -0
  182. package/runtime/dev-ui-icons.mjs +8 -0
  183. package/runtime/dev-workbench-style.mjs +106 -0
  184. package/runtime/dev-workbench.mjs +428 -0
  185. package/runtime/dev-worker-logs.mjs +31 -0
  186. package/runtime/development.mjs +324 -0
  187. package/runtime/document.mjs +1 -1
  188. package/runtime/edge.mjs +6 -5
  189. package/runtime/env.mjs +2 -1
  190. package/runtime/fetch-cache.mjs +10 -8
  191. package/runtime/flight-delta.mjs +50 -0
  192. package/runtime/flight-tap.mjs +28 -0
  193. package/runtime/html-only.mjs +27 -0
  194. package/runtime/http.mjs +2 -0
  195. package/runtime/incremental-cache.mjs +25 -4
  196. package/runtime/instrumentation.mjs +49 -0
  197. package/runtime/navigation-recovery.mjs +36 -0
  198. package/runtime/navigation.mjs +7 -0
  199. package/runtime/node-html-stream.mjs +47 -0
  200. package/runtime/pages-client.mjs +24 -8
  201. package/runtime/pages-navigation.mjs +54 -64
  202. package/runtime/persistent/client.mjs +6 -5
  203. package/runtime/persistent/daemon.mjs +66 -56
  204. package/runtime/persistent/native-proxy.mjs +63 -0
  205. package/runtime/prefetch-scheduler.mjs +110 -0
  206. package/runtime/preload.mjs +12 -0
  207. package/runtime/profile.mjs +14 -0
  208. package/runtime/profiles.json +2 -2
  209. package/runtime/render.mjs +3 -0
  210. package/runtime/route-pattern.mjs +44 -0
  211. package/runtime/rsc-worker.mjs +41 -12
  212. package/runtime/trace.mjs +78 -0
  213. package/runtime/typed-route.mjs +19 -0
  214. package/runtime/watch-project.mjs +29 -18
  215. package/runtime/worker.mjs +102 -24
@@ -0,0 +1,34 @@
1
+ function ownsBranch(scope, branch) {
2
+ const path = branch.replace(/#default$/, '');
3
+ if (scope.kind === 'layout') return !scope.path || path === scope.path || path.startsWith(scope.path + '/');
4
+ return path === scope.path && (scope.kind === 'default') === branch.endsWith('#default');
5
+ }
6
+
7
+ /** Parallel route entries include both canonical and intercepted alternatives.
8
+ * Their actual router selection determines which sheets may become active. */
9
+ export function selectAppStyles(model) {
10
+ if (!model.routing || !Array.isArray(model.cssScopes)) return;
11
+ const branches = Object.values(model.routing.slots || {}).map(slot => slot.branch);
12
+ model.cssScopes = model.cssScopes.filter(scope => (scope.kind === 'layout' && !scope.path) || branches.some(branch => ownsBranch(scope, branch)));
13
+ model.css = [...new Set(model.cssScopes.flatMap(scope => scope.css || []))];
14
+ }
15
+
16
+ /** A retained parallel/intercepted slot keeps only the styles belonging to
17
+ * that branch. Carrying the preceding route's complete list would leak its
18
+ * inactive page globals into the new main page. */
19
+ export function retainAppStyles(model, previous) {
20
+ selectAppStyles(model);
21
+ if (!model.routing || !previous?.routing || model.globalError) return;
22
+ const retained = Object.entries(model.routing.slots || {}).flatMap(([key, slot]) => {
23
+ const old = previous.routing.slots?.[key];
24
+ return old && old.branch === slot.branch && old.url === slot.url ? [slot.branch] : [];
25
+ });
26
+ if (!retained.length) return;
27
+ const scopes = [...model.cssScopes || []];
28
+ for (const scope of previous.cssScopes || []) {
29
+ if (scopes.some(current => current.path === scope.path && current.kind === scope.kind)) continue;
30
+ if (retained.some(branch => ownsBranch(scope, branch))) scopes.push(scope);
31
+ }
32
+ model.cssScopes = scopes;
33
+ model.css = [...new Set([...(model.css || []), ...scopes.flatMap(scope => scope.css || [])])];
34
+ }
@@ -0,0 +1,154 @@
1
+ const managers = new WeakMap();
2
+
3
+ function aborted() { return new DOMException('Navigation cancelled', 'AbortError'); }
4
+
5
+ /** Styles are downloaded while the old page remains visible, but become
6
+ * active together in the new tree's layout effect, before the next paint.
7
+ * Keeping one manager per document also covers development runtime updates. */
8
+ export function appStyleManager(document, { timeout = 15_000 } = {}) {
9
+ if (managers.has(document)) return managers.get(document);
10
+ const sheets = new Map();
11
+ let handed = new Map();
12
+ let cleanup = 0;
13
+ const reserved = sheet => handed.get(sheet.link.href) === sheet;
14
+ function urls(values) {
15
+ if (!Array.isArray(values) || values.length > 1024) throw new Error('Invalid App Router stylesheet list');
16
+ return [...new Set(values.map(value => {
17
+ if (typeof value !== 'string' || value.length > 4096) throw new Error('Invalid App Router stylesheet URL');
18
+ const url = new URL(value, document.baseURI);
19
+ if (!['http:', 'https:'].includes(url.protocol)) throw new Error('Invalid App Router stylesheet protocol');
20
+ return url.href;
21
+ }))];
22
+ }
23
+ function remove(href, sheet) {
24
+ if (sheet.removed) return;
25
+ sheet.removed = true;
26
+ sheet.detach?.();
27
+ sheet.link.remove();
28
+ const records = sheets.get(href);
29
+ if (records) {
30
+ const index = records.indexOf(sheet);
31
+ if (index >= 0) records.splice(index, 1);
32
+ if (!records.length) sheets.delete(href);
33
+ }
34
+ }
35
+ function acquire(href, before, replace = false) {
36
+ const records = sheets.get(href) || [];
37
+ const current = records.at(-1);
38
+ if (current && !replace) return current;
39
+ // A replacement keeps the old active (or local-reset recovery) occurrence
40
+ // in place until commit. Discard older abandoned candidates immediately.
41
+ if (replace) for (const record of [...records]) if (!record.active && !record.recovery && !reserved(record)) remove(href, record);
42
+ const tracked = new Set(records.map(record => record.link));
43
+ const existing = replace ? undefined : [...document.querySelectorAll('link[rel="stylesheet"]')].find(link => link.href === href && !tracked.has(link));
44
+ const link = existing || document.createElement('link');
45
+ const sheet = { link, active: !!existing, ready: !!existing?.sheet, recovery: false };
46
+ const retained = sheets.get(href) || [];
47
+ retained.push(sheet); sheets.set(href, retained);
48
+ if (sheet.ready) sheet.promise = Promise.resolve();
49
+ else sheet.promise = new Promise((resolve, reject) => {
50
+ let finished = false;
51
+ const detach = () => { clearTimeout(timer); link.removeEventListener('load', loaded); link.removeEventListener('error', failed); };
52
+ const finish = error => {
53
+ if (finished) return;
54
+ finished = true; detach(); sheet.detach = undefined;
55
+ if (error) { remove(href, sheet); reject(error); }
56
+ else { sheet.ready = true; resolve(); }
57
+ };
58
+ const loaded = () => finish();
59
+ const failed = () => finish(new Error(`Could not load stylesheet ${href}`));
60
+ const timer = setTimeout(() => finish(new Error('App Router stylesheet loading timed out')), timeout);
61
+ sheet.detach = () => finish(aborted());
62
+ link.addEventListener('load', loaded);
63
+ link.addEventListener('error', failed);
64
+ });
65
+ // Cancellation and a competing navigation can remove a pending sheet.
66
+ // Each caller still observes the original rejection through prepare().
67
+ void sheet.promise.catch(() => {});
68
+ if (!existing) {
69
+ link.rel = 'stylesheet'; link.href = href; link.media = 'not all';
70
+ link.dataset.prnextAppStyle = '';
71
+ if (before) document.head.insertBefore(link, before);
72
+ else document.head.append(link);
73
+ }
74
+ return sheet;
75
+ }
76
+ const manager = {
77
+ async prepare(values = [], signal) {
78
+ if (signal?.aborted) throw aborted();
79
+ const wanted = urls(values);
80
+ const prepared = [];
81
+ let before;
82
+ // Establish cascade order before downloading. Re-inserting an already
83
+ // loaded link can unload its CSSOM and refetch, even within the same head.
84
+ // Keep a correctly ordered suffix; prepare a separate inactive occurrence
85
+ // when an existing sheet would otherwise need to move across that suffix.
86
+ for (let index = wanted.length - 1; index >= 0; index--) {
87
+ const href = wanted[index];
88
+ let sheet = acquire(href, before);
89
+ if (before && !(sheet.link.compareDocumentPosition(before) & 4)) sheet = acquire(href, before, true);
90
+ prepared.push(sheet.promise);
91
+ before = sheet.link;
92
+ }
93
+ const pending = Promise.all(prepared);
94
+ if (!signal) return pending;
95
+ await new Promise((resolve, reject) => {
96
+ const cancel = () => reject(aborted());
97
+ signal.addEventListener('abort', cancel, { once: true });
98
+ pending.then(resolve, reject).finally(() => signal.removeEventListener('abort', cancel));
99
+ });
100
+ },
101
+ handoff(values = []) {
102
+ // A concurrent React render can suspend after its CSS has loaded. Pin
103
+ // these exact occurrences until that render commits or is superseded;
104
+ // pagehide/actions must cancel network work, not its already-ready CSS.
105
+ const next = new Map(urls(values).map(href => {
106
+ const sheet = sheets.get(href)?.at(-1);
107
+ if (!sheet?.ready) throw new Error('App Router styles were not prepared before rendering');
108
+ return [href, sheet];
109
+ }));
110
+ const previous = handed; handed = next;
111
+ for (const [href, sheet] of previous) if (!reserved(sheet) && !sheet.active && !sheet.recovery) remove(href, sheet);
112
+ },
113
+ commit(values = []) {
114
+ cleanup++;
115
+ const wanted = new Set(urls(values));
116
+ const handedUrls = [...handed.keys()];
117
+ const pinned = wanted.size === handed.size && [...wanted].every((href, index) => handedUrls[index] === href) ? handed : null;
118
+ for (const href of wanted) {
119
+ const records = sheets.get(href) || [];
120
+ const prior = !pinned && handed.size ? records.find(record => record.active) || records.find(record => record.recovery) : undefined;
121
+ const sheet = pinned?.get(href) || prior || records.findLast(record => record.ready) || acquire(href);
122
+ // Initial HTML links are render-blocking. A missing sheet is restored
123
+ // on explicit recovery; normal navigation always prepares first.
124
+ sheet.active = true; sheet.recovery = false; sheet.link.media = '';
125
+ for (const previous of [...sheets.get(href)]) if (previous !== sheet && !reserved(previous)) remove(href, previous);
126
+ }
127
+ if (pinned) handed = new Map();
128
+ for (const [href, records] of sheets) {
129
+ for (const sheet of [...records]) if ((!wanted.has(href) || !sheet.active) && !reserved(sheet)) remove(href, sheet);
130
+ }
131
+ },
132
+ release() {
133
+ const revision = ++cleanup;
134
+ // A normal tree update or StrictMode's effect replay commits in the same
135
+ // turn. Only a genuine fatal/global unmount retires the normal styles.
136
+ queueMicrotask(() => {
137
+ if (cleanup !== revision) return;
138
+ const recovery = new Set([...sheets.values()].flat().filter(sheet => sheet.ready && (sheet.active || reserved(sheet))));
139
+ for (const [href, records] of sheets) for (const sheet of [...records]) {
140
+ // Local reset is allowed to remount the same server tree without a
141
+ // request. Keep already loaded sheets inert so that reset can restore
142
+ // them synchronously, rather than painting before a second load event.
143
+ if (recovery.has(sheet)) { sheet.active = false; sheet.recovery = true; sheet.link.media = 'not all'; }
144
+ else if (recovery.size || !sheet.recovery) remove(href, sheet);
145
+ }
146
+ });
147
+ },
148
+ cancel() {
149
+ for (const [href, records] of sheets) for (const sheet of [...records]) if (!sheet.active && !sheet.recovery && !reserved(sheet)) remove(href, sheet);
150
+ },
151
+ };
152
+ managers.set(document, manager);
153
+ return manager;
154
+ }
@@ -44,7 +44,10 @@ globalThis[codecSymbol] = {
44
44
  const bundler = flightBundler(context.clientModules, false);
45
45
  let failure;
46
46
  // React calls then inside its rendering context, preserving React.cache isolation.
47
- const result = { then(resolve, reject) { Promise.resolve().then(callback).then(resolve, reject); } };
47
+ // React can subscribe more than once while unwrapping a thenable. Start
48
+ // lazily in its rendering context, but never execute the producer twice.
49
+ let execution;
50
+ const result = { then(resolve, reject) { (execution ||= Promise.resolve().then(callback)).then(resolve, reject); } };
48
51
  const stream = renderToReadableStream(result, bundler, { signal: context.signal, temporaryReferences: encoded.serverTemporaryReferences, onError(error) { failure ||= error; return 'PRNextCacheRenderError'; } });
49
52
  const chunks = [];
50
53
  let size = 0;
@@ -1,3 +1,4 @@
1
+ import {reportRequestError} from './instrumentation.mjs';
1
2
  import { runRequestContext, currentRequest } from '../compat/headers.cjs';
2
3
  import { flushCacheInvalidations, flushCacheWork } from '../compat/data-cache.cjs';
3
4
  import { createAfterScope } from '../compat/after.cjs';
@@ -9,7 +10,7 @@ export function withCacheRequest(options, phase, callback) {
9
10
  // only needs metadata, and may survive the response for stale revalidation.
10
11
  return runRequestContext({ ...options, body: undefined, routePattern: options.route?.pattern, cacheConfig: options.route?.cacheConfig, phase }, async () => {
11
12
  const context = currentRequest();
12
- if (phase === 'route' && options.route?.cacheConfig?.runtime !== 'edge') context.afterScope = createAfterScope({ signal: options.signal, timeoutMs: options.timeoutMs ?? (options.route?.cacheConfig?.maxDuration ? options.route.cacheConfig.maxDuration * 1000 : 25000) });
13
+ if (phase === 'route' && options.route?.cacheConfig?.runtime !== 'edge') context.afterScope = createAfterScope({report:error=>{console.error('[prnext] after() failed:',error?.stack||error);return reportRequestError(error);}, signal: options.signal, timeoutMs: options.timeoutMs ?? (options.route?.cacheConfig?.maxDuration ? options.route.cacheConfig.maxDuration * 1000 : 25000) });
13
14
  let finalized;
14
15
  const finalizeCache = () => finalized ||= (async () => { await context.afterScope?.finish(); await flushCacheWork(context); })();
15
16
  // The worker owns response completion even when application code throws.
@@ -237,12 +237,10 @@ export async function archiveProject(
237
237
  throw new Error('package.json is not valid JSON.');
238
238
  }
239
239
  if (
240
- !packageJson ||
241
- typeof packageJson.scripts?.build !== 'string' ||
242
- !packageJson.scripts.build.trim()
240
+ !packageJson || typeof packageJson !== 'object' || Array.isArray(packageJson)
243
241
  )
244
242
  throw new Error(
245
- 'Add a production build script to package.json before deploying.'
243
+ 'package.json must contain an object.'
246
244
  );
247
245
  }
248
246
  let compressed = deflateRawSync(contents, { level: 6 });
@@ -291,10 +289,11 @@ export async function archiveProject(
291
289
  }
292
290
  }
293
291
  await walk();
294
- if (!packageJson)
292
+ if (!files.includes('index.html') && (typeof packageJson?.scripts?.build !== 'string' || !packageJson.scripts.build.trim()))
295
293
  throw new Error(
296
- 'The selected directory needs a package.json with a build script; do not exclude it.'
294
+ 'Include index.html for a plain site, or package.json with a production build script; do not exclude it.'
297
295
  );
296
+ packageJson ||= {};
298
297
  const end = Buffer.alloc(22);
299
298
  end.writeUInt32LE(0x06054b50);
300
299
  end.writeUInt16LE(files.length, 8);
@@ -0,0 +1,8 @@
1
+ import {flushCompileCache} from 'node:module';
2
+ let pending;
3
+ // Flush after imports settle so later workers can reuse compiled code even
4
+ // while this worker stays warm. Disk failure is an optimization miss only.
5
+ export function scheduleCompileCacheFlush(){
6
+ if(pending||!process.env.NODE_COMPILE_CACHE||process.env.NODE_DISABLE_COMPILE_CACHE==='1')return;
7
+ pending=setTimeout(()=>{pending=undefined;try{flushCompileCache();}catch{}},1000);pending.unref();
8
+ }
@@ -0,0 +1,19 @@
1
+ const HEADER = 'x-prnext-deployment-id';
2
+
3
+ /** A generation is an artifact identity, independent from a user-supplied buildId. */
4
+ export function deploymentHeaders(id) { return id ? { [HEADER]: id } : {}; }
5
+
6
+ export async function assertDeploymentResponse(response, expected) {
7
+ if (response.headers.get('x-prnext-document-navigation') === '1') {
8
+ await response.body?.cancel().catch(() => {});
9
+ const error = new Error('This route requires a full document navigation.');
10
+ error.code = 'PRNEXT_DOCUMENT_NAVIGATION';
11
+ throw error;
12
+ }
13
+ if (!expected) return;
14
+ if (response.headers.get(HEADER) === expected && response.status !== 409) return;
15
+ await response.body?.cancel().catch(() => {});
16
+ const error = new Error('This page belongs to a previous deployment. Reload the page before continuing.');
17
+ error.code = 'PRNEXT_DEPLOYMENT_CHANGED';
18
+ throw error;
19
+ }
@@ -0,0 +1,26 @@
1
+ // Offline identification marks copied from PRNext Host's local assets:
2
+ // hosting/public/assets/ai-icons/{codex,cursor,claude,other}.svg (vendored 2026-10-20).
3
+ // Provenance retained from that directory's README:
4
+ // - Codex/OpenAI: official Codex VS Code extension 26.1007.21434,
5
+ // resources/blossom-white.svg (OpenAI blossom mark).
6
+ // - Cursor: official https://cursor.com/marketing-static/favicon.svg;
7
+ // brand guidance https://cursor.com/brand.
8
+ // - Claude: official https://claude.com/favicon.svg (also claude.ai/favicon.svg).
9
+ // Brand marks remain their owners' trademarks; no trademark-license or
10
+ // endorsement claim is made. Geometry/colors are preserved. Only decorative
11
+ // accessibility attributes, display size and Cursor's local clip ID are changed.
12
+ // The generic MCP symbol is local, unbranded and inherits the text color.
13
+ // These strings are trusted constants only; never interpolate remote markup.
14
+
15
+ const codexIcon="<svg width=\"20\" height=\"20\" aria-hidden=\"true\" focusable=\"false\"\n xmlns=\"http://www.w3.org/2000/svg\"\n \n \n fill=\"none\"\n viewBox=\"0 0 24 24\"\n>\n <path\n d=\"M13.798 23.976a5.7 5.7 0 0 1-2.26-.456 6.1 6.1 0 0 1-1.903-1.27 5.7 5.7 0 0 1-1.88.311 5.75 5.75 0 0 1-2.95-.79 6.2 6.2 0 0 1-2.188-2.159q-.81-1.366-.809-3.045 0-.695.19-1.51a6.4 6.4 0 0 1-1.475-2.038A5.95 5.95 0 0 1 0 10.573Q0 9.278.547 8.08q.547-1.2 1.523-2.062a5.5 5.5 0 0 1 2.307-1.223A5.7 5.7 0 0 1 5.472 2.35 6.1 6.1 0 0 1 7.565.623 5.8 5.8 0 0 1 10.206 0q1.19 0 2.26.456a6.1 6.1 0 0 1 1.903 1.27 5.7 5.7 0 0 1 1.88-.311q1.594 0 2.95.79a6 6 0 0 1 2.165 2.159q.832 1.366.832 3.045 0 .695-.19 1.51a6.3 6.3 0 0 1 1.475 2.062q.523 1.15.523 2.422a5.9 5.9 0 0 1-.547 2.493q-.547 1.2-1.546 2.086a5.4 5.4 0 0 1-2.284 1.199 5.56 5.56 0 0 1-1.118 2.445 5.9 5.9 0 0 1-2.07 1.727 5.8 5.8 0 0 1-2.64.623m-5.876-2.997q1.19 0 2.07-.504l4.472-2.589a.53.53 0 0 0 .238-.455v-2.062L8.945 18.7a.96.96 0 0 1-1.047 0l-4.496-2.613a.7.7 0 0 1-.024.168v.287q0 1.224.571 2.254a4.24 4.24 0 0 0 1.642 1.583q1.047.6 2.331.599m.238-3.908a.6.6 0 0 0 .262.072q.118 0 .238-.072l1.784-1.031-5.734-3.357q-.522-.312-.523-.935V6.545a4.3 4.3 0 0 0-1.903 1.63 4.25 4.25 0 0 0-.714 2.398q0 1.176.595 2.254.594 1.08 1.546 1.63zm5.638 5.323q1.26 0 2.284-.576a4.3 4.3 0 0 0 1.618-1.582q.595-1.008.595-2.254v-5.179a.47.47 0 0 0-.238-.431l-1.808-1.055v6.689q0 .624-.524.935l-4.496 2.613a4.3 4.3 0 0 0 2.57.84m.904-8.776v-3.26l-2.688-1.535-2.712 1.535v3.26l2.712 1.535zM7.756 5.97q0-.623.523-.935l4.496-2.613a4.3 4.3 0 0 0-2.569-.84q-1.26 0-2.284.576A4.3 4.3 0 0 0 6.304 3.74q-.57 1.008-.57 2.254v5.155q0 .287.237.455l1.785 1.055zM19.84 17.43a4.16 4.16 0 0 0 1.88-1.63 4.33 4.33 0 0 0 .713-2.397q0-1.176-.595-2.254-.594-1.08-1.546-1.63l-4.449-2.59q-.143-.096-.261-.072a.46.46 0 0 0-.238.072L13.56 7.936l5.758 3.38a.9.9 0 0 1 .38.384q.143.216.143.528zM15.059 5.25q.524-.335 1.047 0l4.52 2.662V7.48q0-1.15-.57-2.181A4.14 4.14 0 0 0 18.46 3.62q-1.023-.623-2.379-.623-1.19 0-2.07.503L9.54 6.09a.53.53 0 0 0-.238.455v2.062z\"\n fill=\"white\"\n />\n</svg>";
16
+ const cursorIcon="<svg width=\"20\" height=\"20\" aria-hidden=\"true\" focusable=\"false\" fill=\"none\" viewBox=\"0 0 512 512\" xmlns=\"http://www.w3.org/2000/svg\"><clipPath id=\"prnext-dev-cursor-cutout\"><path d=\"m96 73h320.735v365.65h-320.735z\"/></clipPath><path d=\"m512.001 325.499c0 7.124 0 14.24-.041 21.364-.035 5.999-.103 11.998-.268 17.99-.356 13.068-1.124 26.245-3.448 39.169-2.359 13.109-6.205 25.306-12.266 37.222-5.958 11.703-13.746 22.419-23.029 31.709-9.29 9.291-20 17.072-31.71 23.03-11.909 6.061-24.113 9.907-37.222 12.266-12.923 2.324-26.101 3.092-39.169 3.448-5.999.165-11.991.233-17.99.268-7.123.048-14.24.041-21.364.041h-139c-7.124 0-14.24 0-21.364-.041-5.999-.035-11.998-.103-17.99-.268-13.068-.356-26.245-1.124-39.169-3.448-13.109-2.359-25.306-6.205-37.2219-12.266-11.7034-5.958-22.4195-13.746-31.7095-23.03-9.29-9.29-17.0717-19.999-23.0296-31.709-6.06082-11.909-9.90709-24.113-12.26559-37.222-2.32422-12.924-3.092104-26.101-3.448621-39.169-.164547-5.999-.2331075-11.991-.267388-17.99-.02742444-7.124-.02742444-14.24-.02742444-21.364v-139c0-7.124 0-14.24.04113664-21.364.0342805-5.999.1028418-11.998.2673878-17.99.356517-13.068 1.124399-26.245 3.448619-39.169 2.3585-13.1091 6.20477-25.3061 12.26558-37.222 5.9579-11.7034 13.7465-22.4195 23.0296-31.7095 9.2901-9.29 19.9993-17.0717 31.7095-23.0296 11.9091-6.06082 24.1129-9.90709 37.2222-12.26559 12.923-2.32422 26.101-3.092101 39.168-3.448618 6-.164547 11.992-.2331075 17.991-.2673881 7.117-.03428046 14.233-.03428046 21.357-.03428046h139c7.124 0 14.24 0 21.364.04113656 5.999.0342806 11.998.102842 17.99.267388 13.068.356517 26.245 1.124402 39.169 3.448622 13.109 2.3585 25.306 6.20477 37.222 12.26553 11.703 5.958 22.419 13.7465 31.709 23.0297 9.29 9.29 17.072 19.9992 23.03 31.7094 6.061 11.9091 9.907 24.113 12.266 37.2222 2.324 12.923 3.092 26.101 3.448 39.169.165 5.999.233 11.991.268 17.99.048 7.123.041 14.24.041 21.364v139z\" fill=\"#14120b\"/><path d=\"m186.501 3.99902h139c7.125 0 14.231-.00004 21.341.04102 5.985.0342 11.952.10221 17.903.26562h.001c13.003.35475 25.943 1.11709 38.569 3.3877 12.775 2.29828 24.592 6.03144 36.118 11.89354v-.001c10.973 5.5867 21.052 12.8384 29.848 21.4571l.847.8379c8.993 8.993 16.525 19.3593 22.292 30.6933l3.565-1.8135-3.565 1.8145c5.678 11.1575 9.36 22.6024 11.674 34.9208l.219 1.195c2.129 11.837 2.932 23.95 3.316 36.132l.072 2.438c.164 5.958.232 11.919.266 17.904v.004c.048 7.107.041 14.207.041 21.337v129.344l-.007-.007v9.656c0 7.125 0 14.231-.041 21.341-.034 5.985-.102 11.952-.266 17.903v.001c-.354 13.003-1.117 25.944-3.387 38.569-2.227 12.376-5.8 23.853-11.351 35.037l-.543 1.08c-5.767 11.327-13.306 21.701-22.293 30.695-8.993 8.993-19.361 16.526-30.695 22.293-11.518 5.861-23.342 9.595-36.116 11.894-11.837 2.128-23.95 2.931-36.132 3.315l-2.438.072c-5.958.164-11.919.232-17.904.266h-.004c-7.107.048-14.207.041-21.337.041h-139c-7.125 0-14.231 0-21.341-.041-5.985-.034-11.952-.102-17.903-.266h-.001c-13.003-.355-25.944-1.117-38.57-3.387-12.7742-2.299-24.5914-6.032-36.1165-11.894h.001c-10.974-5.587-21.0534-12.837-29.8496-21.456l-.8467-.838c-8.7118-8.712-16.0531-18.713-21.7461-29.634l-.5459-1.06-.544-1.081c-5.3709-10.816-8.8904-21.919-11.12983-33.84l-.21973-1.196c-2.27061-12.625-3.03294-25.566-3.38769-38.569v-.001c-.16342-5.958-.23143-11.918-.26563-17.903-.02736-7.112-.02734-14.219-.02734-21.341v-139c0-7.125-.00005-14.231.04101-21.341.0342-5.985.10222-11.952.26563-17.903v-.001c.35475-13.003 1.11699-25.944 3.38769-38.57 2.29829-12.7743 6.03159-24.5916 11.89359-36.1166l-.001-.001c5.7665-11.3269 13.3073-21.6999 22.2939-30.6934 8.9933-8.9932 19.36-16.5261 30.6944-22.2929h.0009c11.5174-5.8615 23.3407-9.59617 36.1149-11.89455 11.837-2.12878 23.951-2.93017 36.133-3.31446l2.438-.07226c5.958-.16342 11.919-.23142 17.904-.26563l-.001-.00097c7.104-.03422 14.211-.03321 21.335-.03321z\" stroke=\"#edecec\" stroke-opacity=\".2\" stroke-width=\"8\"/><g clip-path=\"url(#prnext-dev-cursor-cutout)\"><path d=\"m410.344 159.545-146.38-84.5111c-4.7-2.7145-10.5-2.7145-15.2 0l-146.373 84.5111c-3.9515 2.282-6.391 6.501-6.391 11.071v170.418c0 4.569 2.4395 8.789 6.391 11.07l146.379 84.512c4.701 2.714 10.501 2.714 15.201 0l146.38-84.512c3.951-2.281 6.391-6.501 6.391-11.07v-170.418c0-4.57-2.44-8.789-6.391-11.071zm-9.195 17.902-141.308 244.751c-.955 1.65-3.477.976-3.477-.934v-160.261c0-3.203-1.711-6.164-4.487-7.772l-138.786-80.127c-1.65-.956-.976-3.478.934-3.478h282.616c4.013 0 6.522 4.35 4.515 7.828h-.007z\" fill=\"#edecec\"/></g></svg>";
17
+ const claudeIcon="<svg width=\"20\" height=\"20\" aria-hidden=\"true\" focusable=\"false\" viewBox=\"0 0 248 248\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n<path d=\"M52.4285 162.873L98.7844 136.879L99.5485 134.602L98.7844 133.334H96.4921L88.7237 132.862L62.2346 132.153L39.3113 131.207L17.0249 130.026L11.4214 128.844L6.2 121.873L6.7094 118.447L11.4214 115.257L18.171 115.847L33.0711 116.911L55.485 118.447L71.6586 119.392L95.728 121.873H99.5485L100.058 120.337L98.7844 119.392L97.7656 118.447L74.5877 102.732L49.4995 86.1905L36.3823 76.62L29.3779 71.7757L25.8121 67.2858L24.2839 57.3608L30.6515 50.2716L39.3113 50.8623L41.4763 51.4531L50.2636 58.1879L68.9842 72.7209L93.4357 90.6804L97.0015 93.6343L98.4374 92.6652L98.6571 91.9801L97.0015 89.2625L83.757 65.2772L69.621 40.8192L63.2534 30.6579L61.5978 24.632C60.9565 22.1032 60.579 20.0111 60.579 17.4246L67.8381 7.49965L71.9133 6.19995L81.7193 7.49965L85.7946 11.0443L91.9074 24.9865L101.714 46.8451L116.996 76.62L121.453 85.4816L123.873 93.6343L124.764 96.1155H126.292V94.6976L127.566 77.9197L129.858 57.3608L132.15 30.8942L132.915 23.4505L136.608 14.4708L143.994 9.62643L149.725 12.344L154.437 19.0788L153.8 23.4505L150.998 41.6463L145.522 70.1215L141.957 89.2625H143.994L146.414 86.7813L156.093 74.0206L172.266 53.698L179.398 45.6635L187.803 36.802L193.152 32.5484H203.34L210.726 43.6549L207.415 55.1159L196.972 68.3492L188.312 79.5739L175.896 96.2095L168.191 109.585L168.882 110.689L170.738 110.53L198.755 104.504L213.91 101.787L231.994 98.7149L240.144 102.496L241.036 106.395L237.852 114.311L218.495 119.037L195.826 123.645L162.07 131.592L161.696 131.893L162.137 132.547L177.36 133.925L183.855 134.279H199.774L229.447 136.524L237.215 141.605L241.8 147.867L241.036 152.711L229.065 158.737L213.019 154.956L175.45 145.977L162.587 142.787H160.805V143.85L171.502 154.366L191.242 172.089L215.82 195.011L217.094 200.682L213.91 205.172L210.599 204.699L188.949 188.394L180.544 181.069L161.696 165.118H160.422V166.772L164.752 173.152L187.803 207.771L188.949 218.405L187.294 221.832L181.308 223.959L174.813 222.777L161.187 203.754L147.305 182.486L136.098 163.345L134.745 164.2L128.075 235.42L125.019 239.082L117.887 241.8L111.902 237.31L108.718 229.984L111.902 215.452L115.722 196.547L118.779 181.541L121.58 162.873L123.291 156.636L123.14 156.219L121.773 156.449L107.699 175.752L86.304 204.699L69.3663 222.777L65.291 224.431L58.2867 220.768L58.9235 214.27L62.8713 208.48L86.304 178.705L100.44 160.155L109.551 149.507L109.462 147.967L108.959 147.924L46.6977 188.512L35.6182 189.93L30.7788 185.44L31.4156 178.115L33.7079 175.752L52.4285 162.873Z\" fill=\"#D97757\"/>\n</svg>";
18
+ const otherIcon="<svg width=\"20\" height=\"20\" aria-hidden=\"true\" focusable=\"false\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m8 7-5 5 5 5m8-10 5 5-5 5m-3-14-2 18\"/></svg>";
19
+
20
+ export const devAiBrands=Object.freeze([
21
+ Object.freeze({id:'codex',label:'Codex',caption:'CLI & VS Code',icon:codexIcon}),
22
+ Object.freeze({id:'cursor',label:'Cursor',caption:'Editor',icon:cursorIcon}),
23
+ Object.freeze({id:'claude',label:'Claude Code',caption:'CLI & editor',icon:claudeIcon}),
24
+ ]);
25
+ export const devMcpBrand=Object.freeze({id:'other',label:'Other MCP',caption:'Manual setup',icon:otherIcon});
26
+ export const devAiProviders=Object.freeze([...devAiBrands,devMcpBrand]);
@@ -0,0 +1,99 @@
1
+ // Development-only inspector surfaces. No styles escape the tools' shadow root.
2
+ export const devAiStyle = `
3
+ dialog.wb-tools[data-panel=ai]{width:min(420px,calc(100vw - 32px));height:fit-content;max-height:calc(100dvh - 48px)}
4
+ dialog.wb-tools[data-panel=ai][data-selection=true]{width:min(560px,calc(100vw - 32px))}
5
+ [data-panel=ai] .wb-tools-content{padding:48px 20px 20px;flex:0 1 auto;max-height:calc(100dvh - 48px);overscroll-behavior:contain}
6
+ [data-panel=ai] .wb-head{display:contents}
7
+ [data-panel=ai] .wb-head>:not(.wb-close),[data-panel=ai] .wb-tools-nav,[data-panel=ai] .wb-tools-search,[data-panel=ai] .wb-ai-hero{display:none}
8
+ [data-panel=ai] .wb-close{position:absolute;top:8px;right:8px;z-index:2;width:34px;min-height:34px;background:#080808;color:#999}
9
+ [data-panel=ai] .wb-close:hover{color:#fff;background:#191919}
10
+ [data-panel=ai] .wb-tools-feedback{padding:0 20px 16px;font-size:11px;overflow-wrap:anywhere}
11
+ [data-panel=ai] [hidden]{display:none!important}
12
+ .wb-ai-layout{display:block;width:100%;margin:0 auto;min-width:0}
13
+ .wb-ai-layout>*{min-width:0}.wb-ai-has-selection{max-width:none}
14
+ .wb-ai-connect{min-width:0}
15
+ .wb-ai-connect>.wb-host-error{margin:8px 0 0;font-size:11px;line-height:1.5;overflow-wrap:anywhere}
16
+ .wb-ai-hero{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:5px 10px;margin-bottom:16px}
17
+ .wb-ai-heading{margin:0;font-size:14px;font-weight:550;letter-spacing:-.02em;line-height:1.4}
18
+ .wb-ai-description{grid-column:1/-1;color:#999;font-size:11px;line-height:1.6;margin:0}
19
+ .wb-ai-status{display:inline-flex;align-items:center;gap:6px;grid-column:2;grid-row:1;font-size:10px;white-space:nowrap;color:#999}
20
+ .wb-ai-status i{width:5px;height:5px;border-radius:50%;background:#777;flex:none}
21
+ .wb-ai-status[data-connected=true]{color:#a8dabd}.wb-ai-status[data-connected=true] i{background:#87d5a8}
22
+ .wb-ai-layout[data-connected=true] .wb-ai-hero{margin-bottom:0}
23
+ .wb-ai-setup{margin:0;min-width:0}
24
+ .wb-ai-layout[data-connected=true] .wb-ai-setup{margin-top:0}
25
+ .wb-ai-setup>summary{display:flex;align-items:center;gap:8px;min-height:32px;list-style:none;color:#8e8e8e;font-size:10px;cursor:pointer;user-select:none}
26
+ .wb-ai-setup>summary::-webkit-details-marker{display:none}
27
+ .wb-ai-setup>summary::after{content:'';width:5px;height:5px;margin-left:auto;border-right:1px solid currentColor;border-bottom:1px solid currentColor;rotate:45deg;translate:0 -2px}
28
+ .wb-ai-setup[open]>summary::after{rotate:225deg;translate:0 1px}
29
+ .wb-ai-setup>summary:hover{color:#ddd}
30
+ .wb-ai-setup-content{min-width:0}
31
+ .wb-ai-layout[data-connected=true] .wb-ai-setup-content{padding-top:0}
32
+ .wb-ai-workspace{min-width:0;margin:0;padding:0;border:0}
33
+ .wb-ai-ready{display:flex;flex-direction:column;align-items:center;gap:10px;padding:12px 0 8px;text-align:center}
34
+ .wb-ai-ready p{max-width:340px;margin:0;color:#888;font-size:11px;line-height:1.6;text-wrap:pretty}
35
+ .wb .wb-ai-select{display:inline-flex;align-items:center;justify-content:center;gap:8px;min-height:36px;padding:8px 13px;border-radius:8px;background:#ededed;color:#0c0c0c;font-size:11px;font-weight:550}
36
+ .wb .wb-ai-select:hover:not(:disabled){background:#d6d6d6;color:#080808}.wb-ai-select svg{width:15px;height:15px}
37
+ .wb-host-connect{padding:0;background:transparent}
38
+ .wb-host-heading{display:flex;align-items:center;gap:7px;min-width:0;font-size:11px}.wb-host-heading svg{width:14px;height:14px;color:#999}
39
+ .wb-host-heading strong{font-weight:500}.wb-host-state{display:none}
40
+ .wb-host-command{display:flex;align-items:center;gap:8px;margin-top:8px;min-width:0;background:#111;border-radius:8px;padding:3px 3px 3px 10px}
41
+ .wb-host-command code{min-width:0;flex:1;white-space:pre-wrap;overflow-wrap:anywhere;font:11px/1.7 ui-monospace,SFMono-Regular,Consolas,monospace;color:#ddd}
42
+ .wb .wb-host-copy{display:grid;place-items:center;flex:none;width:34px;padding:6px;color:#999}.wb-host-copy svg{width:14px;height:14px}
43
+ .wb-host-note{color:#888;font-size:10px;line-height:1.6;margin:6px 0 0}
44
+ .wb-ai-divider{margin:16px 0 8px;color:#888;font-size:10px}
45
+ .wb-ai-clients{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:2px;padding:3px;border-radius:10px;background:#111}
46
+ .wb .wb-ai-client{display:flex;align-items:center;justify-content:center;gap:7px;min-width:0;min-height:40px;padding:8px 6px;border:0;border-radius:7px;background:transparent;color:#aaa}
47
+ .wb .wb-ai-client:hover:not(:disabled){background:#1b1b1b;color:#fff}
48
+ .wb .wb-ai-client[aria-pressed=true]{background:#262626;color:#fff}
49
+ .wb-ai-client-logo{display:grid;place-items:center;width:20px;height:20px}.wb-ai-client-logo svg{width:20px;height:20px}
50
+ .wb-ai-client-label{font-size:11px;font-weight:500}.wb-ai-client-check{display:none}
51
+ .wb-ai-client:disabled{opacity:.5;cursor:default}
52
+ .wb .wb-ai-other{display:flex;align-items:center;justify-content:center;gap:6px;margin:4px 0 0 auto;min-height:28px;padding:4px 7px;color:#888;font-size:10px}
53
+ .wb-ai-other svg{width:13px;height:13px}.wb-ai-other[aria-pressed=true]{color:#eee;background:#191919}
54
+ .wb-local-setup{margin-top:8px;padding:0}
55
+ .wb-local-setup summary{display:flex;align-items:center;gap:8px;list-style:none;min-height:28px;color:#aaa;font-size:11px;cursor:pointer}
56
+ .wb-local-setup summary::-webkit-details-marker{display:none}.wb-local-setup summary::after{content:'';width:5px;height:5px;margin-left:auto;border-right:1px solid currentColor;border-bottom:1px solid currentColor;rotate:45deg;translate:0 -2px}
57
+ .wb-local-setup[open] summary::after{rotate:225deg;translate:0 1px}
58
+ .wb-mcp-reveal,.wb-mcp-help-reveal{height:0;min-width:0;overflow:hidden;opacity:0}
59
+ .wb-mcp-reveal[data-expanded=true],.wb-mcp-help-reveal[data-expanded=true]{opacity:1}
60
+ .wb-mcp-content{min-width:0;padding-top:8px;opacity:1}.wb-mcp-content[data-swapping=true]{opacity:.35}
61
+ .wb-local-setup .wb-mcp-controls{justify-content:space-between;align-items:center;flex-wrap:nowrap;gap:8px;margin:0;min-width:0}.wb-mcp-format{font-size:10px;color:#999}
62
+ .wb .wb-mcp-help-toggle{display:grid;place-items:center;flex:none;width:28px;min-height:28px;padding:0;border-radius:7px;color:#999;background:transparent;font-size:11px}
63
+ .wb .wb-mcp-help-toggle:hover,.wb .wb-mcp-help-toggle[aria-expanded=true]{background:#191919;color:#ddd}.wb-mcp-help-toggle svg{width:14px;height:14px}
64
+ .wb-mcp-help{padding:7px 0 1px;color:#999;font-size:10px;line-height:1.6;overflow-wrap:anywhere}.wb-mcp-help p{margin:0}.wb-mcp-help a{display:inline-flex;align-items:center;gap:4px;min-height:28px;color:#c3b9f1;text-decoration:none;text-underline-offset:3px}.wb-mcp-help a:hover{text-decoration:underline}
65
+ .wb-mcp-snippet{display:grid;grid-template-columns:minmax(0,1fr) 34px;align-items:center;gap:6px;min-width:0;margin-top:8px}
66
+ .wb .wb-config-copy{display:grid;place-items:center;align-self:center;width:34px;height:34px;min-height:34px;padding:0;border-radius:8px;color:#999;background:transparent}.wb .wb-config-copy:hover{color:#eee;background:#191919}.wb-config-copy svg{width:14px;height:14px}
67
+ .wb-local-setup pre.wb-mcp-code{width:100%;height:83px;min-height:83px;max-height:83px;margin:0;padding:10px;overflow:auto;overscroll-behavior:contain;white-space:pre-wrap;overflow-wrap:anywhere;tab-size:2;font:10px/18px ui-monospace,SFMono-Regular,Consolas,monospace;color:#bcbcbc;background:#101010;border-radius:8px;scrollbar-width:thin;scrollbar-color:#333 #101010}
68
+ .wb-component-head{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:15px}.wb-component-identity{display:flex;align-items:center;gap:10px;min-width:0}.wb-component-identity>div{min-width:0}
69
+ .wb-component-mark{display:grid;place-items:center;width:36px;height:36px;border-radius:11px;background:#b7c3ff12;color:#c4ccff;flex:none}.wb-component-mark svg{width:20px;height:20px}
70
+ .wb-component h3{margin:0 0 3px;font-size:14px;letter-spacing:-.02em;font-weight:550;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
71
+ .wb-component-kind{display:block;font:10px/1.5 ui-monospace,SFMono-Regular,Consolas,monospace;color:#929292;font-variant-numeric:tabular-nums}
72
+ .wb .wb-component-reselect{display:flex;align-items:center;gap:6px;flex:none;padding:6px 8px;color:#999;font-size:10px}.wb-component-reselect svg{width:14px;height:14px}
73
+ .wb-component-visual{display:grid;place-items:center;position:relative;min-height:135px;max-height:190px;overflow:auto;isolation:isolate;border-radius:12px;background:#101010;box-shadow:inset 0 0 0 1px #ffffff08;padding:20px}
74
+ .wb-component-visual .wb-selection-image{display:block;max-width:100%;max-height:150px;object-fit:contain;object-position:center;margin:0;border-radius:4px}
75
+ .wb-component-visual [hidden]{display:none!important}.wb-component-placeholder{font-size:11px;color:#888;text-align:center;overflow-wrap:anywhere}
76
+ .wb-component-visual[data-state=error]{place-items:start;background:#e14a580b;color:#ffa2ac;min-height:90px}.wb-component-visual[data-state=error] .wb-component-placeholder{color:#ffa2ac;text-align:left;font:11px/1.65 ui-monospace,monospace}
77
+ .wb-component-source{display:flex;align-items:center;gap:10px;min-width:0;margin:11px 0 18px}.wb-component-location{min-width:0;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:#909090;font:10px/1.6 ui-monospace,SFMono-Regular,Consolas,monospace}
78
+ .wb-component-editor{display:inline-flex;align-items:center;min-height:28px;text-decoration:none;color:#aaa;font-size:10px;white-space:nowrap}.wb-component-editor:hover{color:#eee}.wb-component-editor[hidden]{display:none}
79
+ .wb-component-composer{border:1px solid #303030;border-radius:12px;overflow:hidden;background:#0c0c0c}
80
+ .wb-component-label{display:flex;flex-direction:column;gap:9px;padding:13px 13px 0;font-size:12px;color:#ccc;font-weight:500}
81
+ .wb-component-label textarea{display:block;width:100%;min-height:78px;max-height:200px;resize:vertical;margin:0;padding:0 1px;border:0;border-radius:0;background:transparent;color:#eee;font:12px/1.65 ui-sans-serif,system-ui;outline-offset:3px;scrollbar-color:#333 #0c0c0c}
82
+ .wb-component-label textarea::placeholder{color:#666}.wb-component-label textarea:focus-visible{outline:0}
83
+ .wb-component-composer:focus-within{border-color:#777;box-shadow:0 0 0 1px #777}
84
+ .wb-component-compose-foot{display:flex;align-items:center;justify-content:space-between;gap:8px;flex-wrap:wrap;padding:10px}.wb-component-parts{font-size:9px;color:#888;overflow-wrap:anywhere}
85
+ .wb .wb-ai-primary{display:inline-flex;align-items:center;justify-content:center;gap:10px;min-height:34px;padding:7px 11px;border-radius:8px;background:#ededed;color:#0c0c0c;font-size:11px;font-weight:600}
86
+ .wb .wb-ai-primary:hover:not(:disabled){background:#d6d6d6;color:#080808}.wb-ai-primary svg{width:14px;height:14px}.wb .wb-ai-primary:disabled{opacity:.45;cursor:wait}
87
+ .wb-component-details{margin-top:14px}.wb-component-details summary{color:#888;font-size:10px;cursor:pointer;min-height:28px;padding:4px 0}.wb-component-details pre{max-height:180px;overflow:auto;color:#aaa;background:#111;padding:12px;border-radius:9px;margin:8px 0;scrollbar-color:#333 #111}
88
+ .wb-component-styles{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:5px 14px;font:10px/1.5 ui-monospace,monospace;color:#aaa;margin:12px 0}
89
+ .wb-component-styles dt{color:#777;min-width:0;overflow-wrap:anywhere}.wb-component-styles dd{margin:0;min-width:0;overflow-wrap:anywhere;text-align:right}
90
+ .wb-ai-verify{display:flex;align-items:center;justify-content:space-between;gap:14px;border-top:1px solid #202020;margin-top:16px;padding-top:12px}.wb-ai-verify strong{font-size:11px;font-weight:500;color:#bbb}.wb-ai-verify p{color:#777;font-size:10px;margin:3px 0 0}
91
+ .wb .wb-ai-verify-button{display:inline-flex;align-items:center;gap:7px;background:transparent;flex:none;font-size:11px;color:#bbb}.wb-ai-verify-button svg{width:13px;height:13px}
92
+ .wb-ai-layout button:focus-visible,.wb-ai-layout summary:focus-visible,.wb-ai-layout a:focus-visible{outline:2px solid #888;outline-offset:-3px}
93
+ @media(max-width:740px){.wb-component-visual{min-height:110px}}
94
+ @media(max-width:500px){dialog.wb-tools[data-panel=ai],dialog.wb-tools[data-panel=ai][data-selection=true]{width:calc(100vw - 16px);height:fit-content;max-height:calc(100dvh - 32px)}[data-panel=ai] .wb-tools-content{padding:48px 14px 18px;max-height:calc(100dvh - 32px)}[data-panel=ai] .wb-tools-feedback{padding:0 14px 14px}.wb-component-compose-foot{gap:10px}.wb-component-parts{width:100%}.wb .wb-ai-primary{width:100%}.wb-ai-verify{gap:10px}.wb-ai-verify p{max-width:150px}.wb-mcp-controls{align-items:flex-start}.wb-component-reselect{font-size:0!important;gap:0!important}.wb-component-reselect svg{width:17px;height:17px}.wb-component-identity{gap:8px}.wb-component-location{font-size:9px}}
95
+ @media(pointer:coarse){[data-panel=ai] .wb-tools-content{padding-top:60px}[data-panel=ai] .wb-close,.wb .wb-host-copy,.wb .wb-config-copy,.wb .wb-mcp-help-toggle{width:44px;min-height:44px}.wb-mcp-snippet{grid-template-columns:minmax(0,1fr) 44px}.wb-mcp-help a{min-height:44px}.wb .wb-ai-client,.wb .wb-config-copy,.wb .wb-ai-other,.wb-local-setup summary,.wb-ai-setup>summary,.wb-component-details summary,.wb-component-editor,.wb .wb-component-reselect,.wb .wb-ai-primary,.wb .wb-ai-select,.wb .wb-ai-verify-button{min-height:44px}.wb .wb-component-reselect{min-width:44px;justify-content:center}.wb-component-label textarea{font-size:16px}}
96
+ @media(prefers-reduced-motion:no-preference){.wb .wb-ai-client,.wb .wb-ai-select,.wb-ai-setup>summary{transition:background-color 120ms,color 120ms}.wb-component-composer{transition:border-color 120ms,box-shadow 120ms}.wb-mcp-reveal,.wb-mcp-help-reveal{transition:height 220ms ease-out,opacity 160ms ease-out}.wb-mcp-content{transition:opacity 120ms ease-out}}
97
+ @media(prefers-reduced-motion:reduce){.wb-mcp-reveal,.wb-mcp-help-reveal,.wb-mcp-content{transition:none!important}}
98
+ @media(forced-colors:active){.wb-component-visual{border:1px solid CanvasText}.wb-ai-clients{background:Canvas}.wb .wb-ai-client{border:1px solid ButtonText}.wb .wb-ai-client[aria-pressed=true]{outline:2px solid Highlight;outline-offset:-2px}.wb-component-mark{color:ButtonText}.wb .wb-ai-primary,.wb .wb-ai-select{background:ButtonFace;color:ButtonText;border:1px solid ButtonText}.wb .wb-mcp-help-toggle[aria-expanded=true]{outline:1px solid Highlight;outline-offset:-2px}.wb-mcp-help a{color:LinkText}.wb-ai-workspace,.wb-ai-verify{border-color:CanvasText}.wb-component-composer:focus-within{border-color:Highlight;outline:2px solid Highlight}.wb-ai-layout button:focus-visible,.wb-ai-layout summary:focus-visible,.wb-ai-layout a:focus-visible{outline-color:Highlight}}
99
+ `;
@@ -0,0 +1,25 @@
1
+ const messages={
2
+ 'initial-build':'Compile the requested routes.',
3
+ 'route-opened':'Compile a newly opened route.',
4
+ 'source-changed':'Source files changed.',
5
+ 'environment-changed':'Environment variables changed; restart the compiler.',
6
+ 'configuration-changed':'Build configuration changed; rebuild the active routes.',
7
+ 'safe-mode':'Safe mode uses a fresh compiler and the complete route graph.',
8
+ 'unknown-dependency':'A changed file is outside the dependency index; refresh all active routes.',
9
+ 'dependency-capacity':'The affected graph exceeds the selective-update budget; rebuild all active routes.',
10
+ 'missing-style-dependencies':'This build has no per-bundle style graph; rebuild styles and JavaScript together.',
11
+ 'css-only':'Update styles without replacing the application worker.',
12
+ 'css-module-exports':'CSS Module exports changed; rebuild JavaScript and styles together.',
13
+ 'css-processor':'Custom CSS processing requires a complete build.',
14
+ 'stylesheet-language':'This stylesheet requires the complete compiler.',
15
+ 'unknown-style-dependency':'A stylesheet is outside the recorded style graph.',
16
+ 'external-asset-base':'External asset URLs require a complete build.',
17
+ 'outside-active-graph':'Keep this unopened route uncompiled until it is requested.',
18
+ };
19
+ export function devBuildReason({initial=false,safe=false,changed=[],cause}={}){
20
+ const code=safe?'safe-mode':initial?'initial-build':
21
+ changed.some(file=>/(?:^|\/)\.env(?:\.|$)/.test(file))?'environment-changed':
22
+ changed.some(file=>/(?:^|\/)(?:(?:prnext|next|postcss|tsconfig|jsconfig)\.config\.|(?:tsconfig|jsconfig)\.json$|package(?:-lock)?\.json$|pnpm-lock\.yaml$|yarn\.lock$)/.test(file))?'configuration-changed':
23
+ cause&&messages[cause]?cause:changed.length?'source-changed':'route-opened';
24
+ return {code,summary:messages[code]};
25
+ }
@@ -0,0 +1,53 @@
1
+ import {open,lstat,realpath,rename,rm,readFile} from 'node:fs/promises';
2
+ import {constants} from 'node:fs';
3
+ import {randomUUID} from 'node:crypto';
4
+ import path from 'node:path';
5
+
6
+ const name='.dev-cache-rpc.json';
7
+ function endpoint(value){
8
+ let url;try{url=new URL(value);}catch{throw Error('Development cache connection is unavailable');}
9
+ if(url.protocol!=='http:'||url.hostname!=='127.0.0.1'||!url.port||url.pathname!=='/cache'||url.search||url.hash||url.username||url.password)throw Error('Invalid development cache endpoint');return url.href;
10
+ }
11
+ async function scopedDirectory(root,directory){
12
+ const requestedRoot=path.resolve(root),requestedDirectory=path.resolve(requestedRoot,directory);
13
+ root=await realpath(requestedRoot);
14
+ // macOS /var and Windows short-name paths can alias the project root. Map
15
+ // only that trusted root to its real path; links inside a generation must
16
+ // still be rejected rather than silently following another directory.
17
+ const relative=[requestedRoot,root].map(base=>path.relative(path.join(base,'.prnext-dev'),requestedDirectory)).find(value=>
18
+ value&&value!=='..'&&!value.startsWith('..'+path.sep)&&!path.isAbsolute(value)&&value.split(path.sep).length===2);
19
+ const expected=relative&&path.join(root,'.prnext-dev',relative);
20
+ if(!expected||await realpath(requestedDirectory)!==expected)throw Error('Development cache must belong to a live project generation');
21
+ return expected;
22
+ }
23
+ export function normalizeDevCachePath(value){
24
+ if(typeof value!=='string'||!value.startsWith('/')||value.startsWith('//')||value.length>1024||/[\\\u0000-\u001f]/.test(value))throw Error('A local cache pathname is required');
25
+ return new URL(value,'http://local').pathname.replace(/%[\da-f]{2}/gi,escape=>{const character=String.fromCharCode(parseInt(escape.slice(1),16));return /[a-z\d_.~-]/i.test(character)?character:escape.toUpperCase();}).replace(/\/+$/,'')||'/';
26
+ }
27
+ /** Only a development worker may publish its native process's private RPC. */
28
+ export async function publishDevCacheCapability({root,distDir,manifest,env=process.env}){
29
+ if(manifest.dev!==true||typeof manifest.devSession!=='string'||typeof manifest.buildId!=='string'||!env.PRNEXT_CACHE_URL||!env.PRNEXT_CACHE_TOKEN)return false;
30
+ const directory=await scopedDirectory(root,distDir);
31
+ if(!/^[a-f\d]{64}$/i.test(env.PRNEXT_CACHE_TOKEN))throw Error('Invalid development cache capability');
32
+ const value={version:1,session:manifest.devSession,buildId:manifest.buildId,endpoint:endpoint(env.PRNEXT_CACHE_URL),token:env.PRNEXT_CACHE_TOKEN};
33
+ const filename=path.join(directory,name),temporary=filename+'.'+randomUUID()+'.tmp';
34
+ try{const file=await open(temporary,'wx',0o600);try{await file.writeFile(JSON.stringify(value));}finally{await file.close();}await rename(temporary,filename);return true;}finally{await rm(temporary,{force:true});}
35
+ }
36
+ export async function invalidateDevRouteCache({root,directory,session,buildId,paths,signal,fetcher=fetch}){
37
+ directory=await scopedDirectory(root,directory);
38
+ const manifest=JSON.parse(await readFile(path.join(directory,'manifest.json'),'utf8'));
39
+ if(manifest.dev!==true||manifest.devSession!==session||manifest.buildId!==buildId)throw Error('Development cache generation changed; retry');
40
+ if(manifest.config?.cacheHandler||Object.keys(manifest.config?.cacheHandlers||{}).length)return {invalidated:false,scope:'custom-handler',reason:'Custom cache handlers must be invalidated through their own API.',persistentTransformsPreserved:true,generationPreserved:true};
41
+ const filename=path.join(directory,name),info=await lstat(filename).catch(()=>null);
42
+ if(!info)return {invalidated:false,scope:'native-route-cache',reason:'No active runtime cache yet. Visit the route first.',persistentTransformsPreserved:true,generationPreserved:true};
43
+ if(!info.isFile()||info.isSymbolicLink()||info.size>4096||process.platform!=='win32'&&(info.mode&0o077))throw Error('Invalid private development cache connection');
44
+ const file=await open(filename,constants.O_RDONLY|(constants.O_NOFOLLOW||0));let capability;
45
+ try{const stat=await file.stat();if(stat.ino!==info.ino||!stat.isFile()||stat.size>4096)throw Error('Development cache connection changed');capability=JSON.parse(await file.readFile('utf8'));}finally{await file.close();}
46
+ if(capability.version!==1||capability.session!==session||capability.buildId!==buildId||!/^[a-f\d]{64}$/i.test(capability.token||''))throw Error('Development cache connection changed; retry');
47
+ const associations=[...new Set(paths.map(value=>'page:'+normalizeDevCachePath(value)))];if(!associations.length||associations.length>2)throw Error('A targeted development route is required');
48
+ const timeout=AbortSignal.timeout(5000),combined=signal?AbortSignal.any([signal,timeout]):timeout;
49
+ const response=await fetcher(endpoint(capability.endpoint),{method:'POST',redirect:'error',headers:{'content-type':'application/json',authorization:'Bearer '+capability.token},body:JSON.stringify({op:'invalidate',paths:associations,mode:'expire'}),signal:combined});
50
+ if(!response.ok){await response.body?.cancel();throw Error('Development cache invalidation failed ('+response.status+')');}
51
+ const body=await response.text();if(Buffer.byteLength(body)>16384)throw Error('Invalid development cache reply');JSON.parse(body);
52
+ return {invalidated:true,scope:'native-route-cache',paths:associations.map(value=>value.slice(5)),persistentTransformsPreserved:true,generationPreserved:true,customHandlersInvalidated:false};
53
+ }