@vesk/adapter 0.2.50-canary.3 → 0.2.50-canary.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"dev-server.d.ts","sourceRoot":"","sources":["../src/dev-server.ts"],"names":[],"mappings":"AAEA,OAAO,EAAgB,KAAK,MAAM,EAA6C,MAAM,WAAW,CAAC;AAQjG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,OAAO,CAAC;AAK7C,OAAO,KAAK,EAAa,gBAAgB,EAAwB,MAAM,yBAAyB,CAAC;AAgBjG;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,IAAI,GAAG,WAAW,GAAG,OAAO,GAAG,MAAM,CAAC;IAC9C,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,KAAK,EAAE,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACxD,QAAQ,EAAE,OAAO,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB;+EAC2E;IAC3E,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,mEAAmE;AACnE,wBAAgB,kBAAkB,IAAI,WAAW,CAEhD;AAuCD,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,KAAK,GAAG;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CAIzE;AAkBD,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,mHAAmH;IACnH,QAAQ,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC7B;;8BAE0B;IAC1B,MAAM,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,UAAU,GAAG,YAAY,GAAG,SAAS,GAAG,WAAW,GAAG,QAAQ,CAAC;IACrE,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,WAAW,CAAC;IAChC,8EAA8E;IAC9E,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,uBAAuB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3E;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,wBAAwB,GAAG;IACvE,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;CAChH,CAUA;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAa3C;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAIrD;AA8CD,wBAAsB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAoqBvG"}
1
+ {"version":3,"file":"dev-server.d.ts","sourceRoot":"","sources":["../src/dev-server.ts"],"names":[],"mappings":"AAEA,OAAO,EAAgB,KAAK,MAAM,EAA6C,MAAM,WAAW,CAAC;AASjG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,OAAO,CAAC;AAK7C,OAAO,KAAK,EAAa,gBAAgB,EAAwB,MAAM,yBAAyB,CAAC;AAgBjG;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,IAAI,GAAG,WAAW,GAAG,OAAO,GAAG,MAAM,CAAC;IAC9C,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,KAAK,EAAE,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACxD,QAAQ,EAAE,OAAO,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB;+EAC2E;IAC3E,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,mEAAmE;AACnE,wBAAgB,kBAAkB,IAAI,WAAW,CAEhD;AAuCD,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,KAAK,GAAG;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CAIzE;AAkBD,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,mHAAmH;IACnH,QAAQ,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC7B;;8BAE0B;IAC1B,MAAM,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,UAAU,GAAG,YAAY,GAAG,SAAS,GAAG,WAAW,GAAG,QAAQ,CAAC;IACrE,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,WAAW,CAAC;IAChC,8EAA8E;IAC9E,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,uBAAuB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3E;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,wBAAwB,GAAG;IACvE,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;CAChH,CAUA;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAa3C;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAIrD;AA8CD,wBAAsB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAwqBvG"}
@@ -6,6 +6,7 @@ import { stripCodeTypes } from '@vesk/compiler/src/strip-ts';
6
6
  import { DEFAULT_MAX_BODY_BYTES, safeJsonForScript } from '@vesk/compiler/src/server-codegen';
7
7
  import { build } from '@vesk/adapter/src/index';
8
8
  import { cacheControlFor } from '@vesk/adapter/src/asset-hash';
9
+ import { reportServerError, registerErrorHooks } from '@vesk/adapter/src/error-report';
9
10
  import { buildErrorPayload, createHmrServer } from './hmr';
10
11
  import * as hmrApi from './hmr';
11
12
  import { createDevApiRouter } from './dev-api';
@@ -206,6 +207,9 @@ export async function startDevServer(appDir, options) {
206
207
  const devDir = resolve(appDir, '..', '.vesk', 'dev');
207
208
  const publicDir = options?.publicDir || resolve(appDir, '..', 'public');
208
209
  installMdReadHook([publicDir, resolve(devDir, 'static', 'public')]);
210
+ // Plugin `onError` hooks only exist here (dev): in production there are no
211
+ // plugin objects, and `_events.ts` is the seam that ships.
212
+ registerErrorHooks(options?.plugins);
209
213
  // Dev-panel plugin list: plugin / module names declared in the dev server's own config.
210
214
  const configPluginNames = (options?.plugins || []).map((p) => {
211
215
  if (typeof p === 'string')
@@ -700,6 +704,7 @@ export async function startDevServer(appDir, options) {
700
704
  }
701
705
  catch (e) {
702
706
  console.error('[vesk dev] SSR render failed:', e instanceof Error ? (e.stack || e.message) : String(e));
707
+ await reportServerError(e, { url: url.href, target: 'dev', status: 500 });
703
708
  res.writeHead(500, { 'Content-Type': 'text/html' });
704
709
  res.end(renderSsrErrorPage(e, url.pathname));
705
710
  }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * The production error-reporting seam.
3
+ *
4
+ * Production never leaks a stack trace to the client (that is the right
5
+ * default), which used to mean an adopter had two bad options: show the stack,
6
+ * or replace the adapter's error handling. This is the supported middle: a
7
+ * plugin's `onError` hook receives the error, the route and the build id.
8
+ *
9
+ * Design rules:
10
+ * - a hook that throws is caught and logged, never turned into a second
11
+ * failure (a reporting outage must not take down the page that was already
12
+ * failing);
13
+ * - one failing hook does not stop the others;
14
+ * - nothing is reported twice for one request.
15
+ */
16
+ import type { ServerErrorContext, VeskPlugin } from '@vesk/types';
17
+ export interface BuildConfigLike {
18
+ version?: number;
19
+ buildId?: string;
20
+ veskVersion?: string;
21
+ [key: string]: unknown;
22
+ }
23
+ export declare function registerErrorReporter(fn: (err: unknown, ctx: ServerErrorContext) => void | Promise<void>): void;
24
+ export declare function clearErrorReporters(): void;
25
+ export declare function errorReporterCount(): number;
26
+ /** Register every plugin that declares `onError`. */
27
+ export declare function registerErrorHooks(plugins: VeskPlugin[] | null | undefined): void;
28
+ export interface ReportOptions {
29
+ url?: string;
30
+ routePath?: string;
31
+ target: ServerErrorContext['target'];
32
+ status?: number;
33
+ durationMs?: number;
34
+ buildConfig?: BuildConfigLike | null;
35
+ /** Suppress a duplicate report for the same error object. */
36
+ dedupe?: boolean;
37
+ }
38
+ /**
39
+ * Report a production failure. Never throws: a failure to report is logged and
40
+ * swallowed, because the caller is already handling a worse problem.
41
+ */
42
+ export declare function reportServerError(err: unknown, options: ReportOptions): Promise<void>;
43
+ //# sourceMappingURL=error-report.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error-report.d.ts","sourceRoot":"","sources":["../src/error-report.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAElE,MAAM,WAAW,eAAe;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAMD,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,kBAAkB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAE/G;AAED,wBAAgB,mBAAmB,IAAI,IAAI,CAE1C;AAED,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAED,qDAAqD;AACrD,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,IAAI,GAAG,SAAS,GAAG,IAAI,CAIjF;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,kBAAkB,CAAC,QAAQ,CAAC,CAAC;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;IACrC,6DAA6D;IAC7D,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAyB3F"}
@@ -0,0 +1,52 @@
1
+ /** Process-wide hook list; the config's plugins are registered at boot. */
2
+ const hooks = [];
3
+ const reported = new WeakSet();
4
+ export function registerErrorReporter(fn) {
5
+ if (typeof fn === 'function')
6
+ hooks.push(fn);
7
+ }
8
+ export function clearErrorReporters() {
9
+ hooks.length = 0;
10
+ }
11
+ export function errorReporterCount() {
12
+ return hooks.length;
13
+ }
14
+ /** Register every plugin that declares `onError`. */
15
+ export function registerErrorHooks(plugins) {
16
+ for (const plugin of plugins || []) {
17
+ if (plugin && typeof plugin.onError === 'function')
18
+ registerErrorReporter(plugin.onError);
19
+ }
20
+ }
21
+ /**
22
+ * Report a production failure. Never throws: a failure to report is logged and
23
+ * swallowed, because the caller is already handling a worse problem.
24
+ */
25
+ export async function reportServerError(err, options) {
26
+ if (options.dedupe !== false && err && typeof err === 'object') {
27
+ if (reported.has(err))
28
+ return;
29
+ reported.add(err);
30
+ }
31
+ const cfg = options.buildConfig || null;
32
+ const ctx = {
33
+ url: options.url,
34
+ routePath: options.routePath,
35
+ target: options.target,
36
+ status: options.status ?? 500,
37
+ durationMs: options.durationMs,
38
+ version: typeof cfg?.veskVersion === 'string' ? cfg.veskVersion : undefined,
39
+ buildId: typeof cfg?.buildId === 'string' ? cfg.buildId : undefined,
40
+ errorName: err instanceof Error ? err.name : typeof err,
41
+ message: err instanceof Error ? err.message : String(err),
42
+ stack: err instanceof Error ? err.stack : undefined,
43
+ };
44
+ for (const hook of hooks) {
45
+ try {
46
+ await hook(err, ctx);
47
+ }
48
+ catch (e) {
49
+ console.error('vesk: an onError hook threw and was ignored:', e?.message ?? e);
50
+ }
51
+ }
52
+ }
package/dist/events.d.ts CHANGED
@@ -1,3 +1,20 @@
1
+ /**
2
+ * Wrapper module bundled into `server/events.js`. It lifts the user's
3
+ * `_events.ts` exports into `executeStart` / `executeRequest` / `executeStop`
4
+ * entry points with a context that routes `set`/`get` into the process-wide
5
+ * server context (`globalThis.__vesk_server_ctx`).
6
+ *
7
+ * The `__vesk_events_started` guard is global (not module-local) so lazy edge
8
+ * isolates and the Node prod server share one lifetime; `onStop` clears it so
9
+ * dev HMR reloads can boot a fresh module.
10
+ */
11
+ export declare function buildEventsEntry(sourcePath: string): string;
12
+ /**
13
+ * Report a production failure to `_events.ts`'s `onError`, if it exports one.
14
+ * Never throws: a reporting outage must not add a second failure to a request
15
+ * that already failed.
16
+ */
17
+ export declare function executeError(err: unknown, base: Record<string, unknown>, mod: unknown): Promise<void>;
1
18
  /**
2
19
  * Compile the app's `_events.ts` (if present) into a self-contained
3
20
  * `server/events.js` next to the server runtime. Returns true when an events
@@ -1 +1 @@
1
- {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":"AAqEA;;;;;;GAMG;AACH,wBAAsB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAiCpF"}
1
+ {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":"AAOA;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAuD3D;AAED;;;;GAIG;AACH,wBAAsB,YAAY,CAChC,GAAG,EAAE,OAAO,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,GAAG,EAAE,OAAO,GACX,OAAO,CAAC,IAAI,CAAC,CAQf;AAED;;;;;;GAMG;AACH,wBAAsB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAiCpF"}
package/dist/events.js CHANGED
@@ -13,14 +13,17 @@ let buildId = 0;
13
13
  * isolates and the Node prod server share one lifetime; `onStop` clears it so
14
14
  * dev HMR reloads can boot a fresh module.
15
15
  */
16
- function buildEntry(sourcePath) {
16
+ export function buildEventsEntry(sourcePath) {
17
17
  return [
18
18
  `import * as __events from ${JSON.stringify(sourcePath)};`,
19
19
  '',
20
20
  'const __store = () => (globalThis.__vesk_server_ctx ||= {});',
21
21
  '',
22
22
  'const __handlers = {};',
23
- "for (const __name of ['onStart', 'onRequest', 'onStop']) {",
23
+ // `onError` joins the server events: production has no plugin objects, so
24
+ // this is the seam an error reporter actually uses there (plugins carry
25
+ // their own `onError` in dev, where plugin objects do exist).
26
+ "for (const __name of ['onStart', 'onRequest', 'onStop', 'onError']) {",
24
27
  ' const __fn = __events[__name];',
25
28
  " if (typeof __fn === 'function') __handlers[__name] = __fn;",
26
29
  '}',
@@ -61,9 +64,26 @@ function buildEntry(sourcePath) {
61
64
  'export const onStart = __handlers.onStart;',
62
65
  'export const onRequest = __handlers.onRequest;',
63
66
  'export const onStop = __handlers.onStop;',
67
+ 'export const onError = __handlers.onError;',
64
68
  '',
65
69
  ].join('\n');
66
70
  }
71
+ /**
72
+ * Report a production failure to `_events.ts`'s `onError`, if it exports one.
73
+ * Never throws: a reporting outage must not add a second failure to a request
74
+ * that already failed.
75
+ */
76
+ export async function executeError(err, base, mod) {
77
+ const fn = mod?.onError;
78
+ if (typeof fn !== 'function')
79
+ return;
80
+ try {
81
+ await fn(err, base);
82
+ }
83
+ catch (e) {
84
+ console.error('vesk: _events onError threw and was ignored:', e instanceof Error ? e.message : e);
85
+ }
86
+ }
67
87
  /**
68
88
  * Compile the app's `_events.ts` (if present) into a self-contained
69
89
  * `server/events.js` next to the server runtime. Returns true when an events
@@ -78,7 +98,7 @@ export async function compileEvents(appDir, outDir) {
78
98
  const serverDir = resolve(outDir, 'server');
79
99
  mkdirSync(serverDir, { recursive: true });
80
100
  const entryFile = resolve(serverDir, `.events-entry-${buildId++}.mjs`);
81
- writeFileSync(entryFile, buildEntry(sourcePath), 'utf-8');
101
+ writeFileSync(entryFile, buildEventsEntry(sourcePath), 'utf-8');
82
102
  try {
83
103
  const result = await esbuild.build({
84
104
  entryPoints: [entryFile],
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EACe,YAAY,EAAE,WAAW,EAC7B,UAAU,EAChC,MAAM,yBAAyB,CAAC;AAiBjC;;;;;;GAMG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;CACjB;AACD,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,iBAAiB,EAAE,CAAC;CAC9B;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,UAAU,EAAE,EACrB,OAAO,EAAE,iBAAiB,EAAE,GAAG,IAAI,GAAG,SAAS,GAC9C,UAAU,EAAE,CAQd;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,UAAU,EAAE,EACrB,KAAK,EAAE,eAAe,GAAG,IAAI,GAAG,SAAS,GACxC,UAAU,EAAE,CAGd;AAkDD,wBAAsB,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC,CAiWpG;AAED,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAIhE,OAAO,EACL,kBAAkB,EAClB,oBAAoB,EACpB,yBAAyB,EACzB,eAAe,GAChB,MAAM,2BAA2B,CAAC;AACnC,YAAY,EACV,mBAAmB,EACnB,YAAY,EACZ,kBAAkB,EAClB,cAAc,EACd,iBAAiB,EACjB,gBAAgB,EAChB,aAAa,EACb,aAAa,GACd,MAAM,2BAA2B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EACe,YAAY,EAAE,WAAW,EAC7B,UAAU,EAChC,MAAM,yBAAyB,CAAC;AAiBjC;;;;;;GAMG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;CACjB;AACD,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,iBAAiB,EAAE,CAAC;CAC9B;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,UAAU,EAAE,EACrB,OAAO,EAAE,iBAAiB,EAAE,GAAG,IAAI,GAAG,SAAS,GAC9C,UAAU,EAAE,CAQd;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,UAAU,EAAE,EACrB,KAAK,EAAE,eAAe,GAAG,IAAI,GAAG,SAAS,GACxC,UAAU,EAAE,CAGd;AAkDD,wBAAsB,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC,CAqWpG;AAED,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAIhE,OAAO,EACL,kBAAkB,EAClB,oBAAoB,EACpB,yBAAyB,EACzB,eAAe,GAChB,MAAM,2BAA2B,CAAC;AACnC,YAAY,EACV,mBAAmB,EACnB,YAAY,EACZ,kBAAkB,EAClB,cAAc,EACd,iBAAiB,EACjB,gBAAgB,EAChB,aAAa,EACb,aAAa,GACd,MAAM,2BAA2B,CAAC"}
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { mkdirSync, writeFileSync, existsSync, readFileSync } from 'node:fs';
2
- import { resolve, dirname, relative, basename } from 'node:path';
2
+ import { resolve, dirname, relative, basename, join } from 'node:path';
3
3
  import { fileURLToPath } from 'node:url';
4
4
  import { bundleRuntime } from '@vesk/adapter/src/runtime-bundle';
5
5
  import { generateSsrFunction } from '@vesk/adapter/src/ssr-function';
@@ -7,6 +7,10 @@ import { resolveUserCssPath, isTailwindPlugin, resolveCssUrls, hasBuiltGlobalCss
7
7
  import { collectActionIds } from '@vesk/compiler/src/actions';
8
8
  import { generateApiFunction } from '@vesk/adapter/src/api-function';
9
9
  import { describeAsset } from '@vesk/adapter/src/asset-hash';
10
+ // The adapter's own package.json, for the version stamped into the manifest.
11
+ function PACKAGES_DIR_SELF() {
12
+ return resolve(dirname(fileURLToPath(import.meta.url)), '..');
13
+ }
10
14
  import { compileMiddleware, compileMiddlewareCode } from '@vesk/adapter/src/middleware';
11
15
  import { compileEvents } from '@vesk/adapter/src/events';
12
16
  import { generateClientBundle } from '@vesk/adapter/src/client-bundle';
@@ -380,6 +384,10 @@ export async function build(appDir, options) {
380
384
  const manifest = generateManifest(routeTree, ssrRoutes, apiRoutes, prerenderedRoutes, middlewareEnabled, actionMap, pluginHeadExtra, hasEvents);
381
385
  // The asset map is what makes a deploy auditable: sizes, content hashes and
382
386
  // the SRI digests a page can carry.
387
+ // The build id is the asset map's own fingerprint: same assets, same id.
388
+ const buildId = emittedAssets.map((a) => a.hash).join('-');
389
+ manifest.buildId = buildId.slice(0, 32);
390
+ manifest.veskVersion = JSON.parse(readFileSync(join(PACKAGES_DIR_SELF(), 'package.json'), 'utf-8')).version;
383
391
  manifest.assets = {
384
392
  hashed,
385
393
  client: { file: clientFileName, url: clientUrl, hash: clientAsset.hash, bytes: clientAsset.bytes, integrity: clientAsset.integrity },
@@ -1 +1 @@
1
- {"version":3,"file":"prod-server.d.ts","sourceRoot":"","sources":["../src/prod-server.ts"],"names":[],"mappings":"AAEA,OAAO,EAA2D,KAAK,MAAM,EAAE,MAAM,WAAW,CAAC;AA+CjG,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,KAAK,GAAG;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CAIzE;AAiGD,wBAAsB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CA0cxI"}
1
+ {"version":3,"file":"prod-server.d.ts","sourceRoot":"","sources":["../src/prod-server.ts"],"names":[],"mappings":"AAEA,OAAO,EAA2D,KAAK,MAAM,EAAE,MAAM,WAAW,CAAC;AAiDjG,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,KAAK,GAAG;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CAIzE;AAkGD,wBAAsB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAwdxI"}
@@ -5,6 +5,8 @@ import { fileURLToPath } from 'node:url';
5
5
  import { createRateLimiter, resolveComponentName, safeJsonForScript, getClientProtocol, DEFAULT_MAX_BODY_BYTES } from '@vesk/compiler/src/server-codegen';
6
6
  import { securityHeaders } from '@vesk/compiler/src/server-utils';
7
7
  import { cacheControlFor } from '@vesk/adapter/src/asset-hash';
8
+ import { reportServerError } from '@vesk/adapter/src/error-report';
9
+ import { executeError } from '@vesk/adapter/src/events';
8
10
  import { resolveWithin, installMdReadHook } from '@vesk/adapter/src/paths';
9
11
  import { resolveCssUrls, hasBuiltGlobalCss } from '@vesk/adapter/src/css';
10
12
  import { loadVeskConfig } from '@vesk/adapter/src/load-config';
@@ -511,6 +513,19 @@ export async function startProdServer(outDir, options) {
511
513
  return;
512
514
  }
513
515
  console.error('vesk ssr error:', err.message);
516
+ // Two seams, both best-effort: a plugin's `onError` (dev-style
517
+ // plugin objects don't exist here) and `_events.ts`'s, which IS
518
+ // baked into the production build.
519
+ await reportServerError(err, {
520
+ url: url.pathname + url.search,
521
+ routePath: route.path,
522
+ target: 'node',
523
+ status: 500,
524
+ buildConfig,
525
+ });
526
+ if (eventsMod) {
527
+ await executeError(err, { url: url.href, routePath: route.path, target: 'node', status: 500 }, eventsMod);
528
+ }
514
529
  const errPath = resolve(appDir, 'error.vsk');
515
530
  let errorHtml = null;
516
531
  if (existsSync(errPath)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vesk/adapter",
3
- "version": "0.2.50-canary.3",
3
+ "version": "0.2.50-canary.5",
4
4
  "type": "module",
5
5
  "description": "Vesk adapter — builds deployable .vesk/ output for Deno and Node serverless",
6
6
  "main": "./dist/index.js",
@@ -23,9 +23,9 @@
23
23
  "sharp": "^0.35.3"
24
24
  },
25
25
  "dependencies": {
26
- "@vesk/compiler": "^0.2.50-canary.3",
27
- "@vesk/runtime": "^0.2.50-canary.3",
28
- "@vesk/types": "^0.2.50-canary.3"
26
+ "@vesk/compiler": "^0.2.50-canary.5",
27
+ "@vesk/runtime": "^0.2.50-canary.5",
28
+ "@vesk/types": "^0.2.50-canary.5"
29
29
  },
30
30
  "devDependencies": {
31
31
  "@types/ws": "^8.18.1",