veryfront 0.1.90 → 0.1.92

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 (66) hide show
  1. package/esm/deno.js +1 -1
  2. package/esm/src/config/environment-config.js +1 -1
  3. package/esm/src/jobs/schemas.d.ts +44 -44
  4. package/esm/src/jobs/schemas.js +3 -3
  5. package/esm/src/observability/metrics/manager.js +2 -2
  6. package/esm/src/observability/tracing/otlp-setup.js +2 -2
  7. package/esm/src/platform/adapters/fs/veryfront/adapter.d.ts +1 -0
  8. package/esm/src/platform/adapters/fs/veryfront/adapter.d.ts.map +1 -1
  9. package/esm/src/platform/adapters/fs/veryfront/adapter.js +11 -5
  10. package/esm/src/platform/adapters/veryfront-api-client/client.d.ts +3 -1
  11. package/esm/src/platform/adapters/veryfront-api-client/client.d.ts.map +1 -1
  12. package/esm/src/platform/adapters/veryfront-api-client/client.js +6 -0
  13. package/esm/src/platform/adapters/veryfront-api-client/index.d.ts +2 -2
  14. package/esm/src/platform/adapters/veryfront-api-client/index.d.ts.map +1 -1
  15. package/esm/src/platform/adapters/veryfront-api-client/index.js +1 -1
  16. package/esm/src/platform/adapters/veryfront-api-client/operations.d.ts +24 -0
  17. package/esm/src/platform/adapters/veryfront-api-client/operations.d.ts.map +1 -1
  18. package/esm/src/platform/adapters/veryfront-api-client/operations.js +65 -3
  19. package/esm/src/platform/adapters/veryfront-api-client/schemas/api.schema.d.ts +28 -0
  20. package/esm/src/platform/adapters/veryfront-api-client/schemas/api.schema.d.ts.map +1 -1
  21. package/esm/src/platform/adapters/veryfront-api-client/schemas/api.schema.js +13 -0
  22. package/esm/src/platform/adapters/veryfront-api-client/schemas/index.d.ts +1 -1
  23. package/esm/src/platform/adapters/veryfront-api-client/schemas/index.d.ts.map +1 -1
  24. package/esm/src/platform/adapters/veryfront-api-client/schemas/index.js +1 -1
  25. package/esm/src/proxy/logger.d.ts.map +1 -1
  26. package/esm/src/proxy/logger.js +2 -6
  27. package/esm/src/proxy/tracing.d.ts.map +1 -1
  28. package/esm/src/proxy/tracing.js +2 -5
  29. package/esm/src/proxy/version.d.ts +2 -0
  30. package/esm/src/proxy/version.d.ts.map +1 -0
  31. package/esm/src/proxy/version.js +18 -0
  32. package/esm/src/sandbox/index.d.ts +1 -1
  33. package/esm/src/sandbox/index.d.ts.map +1 -1
  34. package/esm/src/sandbox/index.js +1 -1
  35. package/esm/src/sandbox/sandbox.d.ts +58 -0
  36. package/esm/src/sandbox/sandbox.d.ts.map +1 -1
  37. package/esm/src/sandbox/sandbox.js +111 -0
  38. package/esm/src/server/handlers/dev/styles-css.handler.d.ts +5 -0
  39. package/esm/src/server/handlers/dev/styles-css.handler.d.ts.map +1 -1
  40. package/esm/src/server/handlers/dev/styles-css.handler.js +121 -1
  41. package/esm/src/server/handlers/monitoring/health.handler.js +2 -2
  42. package/esm/src/utils/logger/logger.js +2 -2
  43. package/esm/src/utils/version.d.ts +9 -1
  44. package/esm/src/utils/version.d.ts.map +1 -1
  45. package/esm/src/utils/version.js +29 -2
  46. package/package.json +1 -1
  47. package/src/deno.js +1 -1
  48. package/src/src/config/environment-config.ts +1 -1
  49. package/src/src/jobs/schemas.ts +3 -3
  50. package/src/src/observability/metrics/manager.ts +2 -2
  51. package/src/src/observability/tracing/otlp-setup.ts +2 -2
  52. package/src/src/platform/adapters/fs/veryfront/adapter.ts +12 -5
  53. package/src/src/platform/adapters/veryfront-api-client/client.ts +17 -0
  54. package/src/src/platform/adapters/veryfront-api-client/index.ts +6 -0
  55. package/src/src/platform/adapters/veryfront-api-client/operations.ts +110 -3
  56. package/src/src/platform/adapters/veryfront-api-client/schemas/api.schema.ts +16 -0
  57. package/src/src/platform/adapters/veryfront-api-client/schemas/index.ts +2 -0
  58. package/src/src/proxy/logger.ts +2 -8
  59. package/src/src/proxy/tracing.ts +2 -6
  60. package/src/src/proxy/version.ts +21 -0
  61. package/src/src/sandbox/index.ts +13 -1
  62. package/src/src/sandbox/sandbox.ts +183 -0
  63. package/src/src/server/handlers/dev/styles-css.handler.ts +179 -1
  64. package/src/src/server/handlers/monitoring/health.handler.ts +2 -2
  65. package/src/src/utils/logger/logger.ts +2 -2
  66. package/src/src/utils/version.ts +37 -2
@@ -6,13 +6,9 @@ import * as dntShim from "../../_dnt.shims.js";
6
6
 
7
7
 
8
8
  import type { Context, Span, Tracer } from "@opentelemetry/api";
9
- import denoConfig from "../../deno.js";
10
9
  import { getEnv } from "./env.js";
11
10
  import { proxyLogger } from "./logger.js";
12
-
13
- // Get version from environment variable or root deno.json
14
- const VERYFRONT_VERSION: string = getEnv("VERYFRONT_VERSION") ??
15
- (typeof denoConfig.version === "string" ? denoConfig.version : "0.0.0");
11
+ import { PROXY_RUNTIME_VERSION } from "./version.js";
16
12
 
17
13
  let initialized = false;
18
14
  let tracerProvider: { shutdown: () => Promise<void> } | null = null;
@@ -100,7 +96,7 @@ export async function initializeOTLPWithApis(): Promise<void> {
100
96
  const resourceAttrs = parseResourceAttributes(getEnv("OTEL_RESOURCE_ATTRIBUTES"));
101
97
  const resource = new Resource({
102
98
  [ATTR_SERVICE_NAME]: config.serviceName,
103
- [ATTR_SERVICE_VERSION]: VERYFRONT_VERSION,
99
+ [ATTR_SERVICE_VERSION]: PROXY_RUNTIME_VERSION,
104
100
  ...resourceAttrs,
105
101
  });
106
102
 
@@ -0,0 +1,21 @@
1
+ import denoConfig from "../../deno.js";
2
+ import { getEnv } from "./env.js";
3
+
4
+ function normalizeVersion(version: string | undefined): string | undefined {
5
+ if (!version) return undefined;
6
+ return version.replace(/^v(?=\d)/, "");
7
+ }
8
+
9
+ function getVersionEnv(name: string): string | undefined {
10
+ try {
11
+ return getEnv(name);
12
+ } catch {
13
+ return undefined;
14
+ }
15
+ }
16
+
17
+ export const PROXY_RUNTIME_VERSION = normalizeVersion(
18
+ getVersionEnv("VERYFRONT_VERSION") ?? getVersionEnv("RELEASE_VERSION"),
19
+ ) ??
20
+ normalizeVersion(typeof denoConfig.version === "string" ? denoConfig.version : undefined) ??
21
+ "0.0.0";
@@ -19,4 +19,16 @@
19
19
  import "../../_dnt.polyfills.js";
20
20
 
21
21
 
22
- export { type ExecResult, type ExecStreamEvent, Sandbox, type SandboxOptions } from "./sandbox.js";
22
+ export {
23
+ type CommandJob,
24
+ type CommandJobHeartbeatStatus,
25
+ type CommandJobOutput,
26
+ type CommandJobStatus,
27
+ type ExecResult,
28
+ type ExecStreamEvent,
29
+ Sandbox,
30
+ type SandboxListOptions,
31
+ type SandboxListResult,
32
+ type SandboxOptions,
33
+ type SandboxSession,
34
+ } from "./sandbox.js";
@@ -41,6 +41,60 @@ export interface ExecStreamEvent {
41
41
  exitCode?: number;
42
42
  }
43
43
 
44
+ /** Status of an async command job. */
45
+ export type CommandJobStatus = "running" | "completed" | "failed" | "canceled";
46
+
47
+ /** Heartbeat health status for a command job. */
48
+ export type CommandJobHeartbeatStatus = "disabled" | "healthy" | "degraded";
49
+
50
+ /** An async command job running in a sandbox. */
51
+ export interface CommandJob {
52
+ id: string;
53
+ status: CommandJobStatus;
54
+ exitCode: number | null;
55
+ signal: string | null;
56
+ startedAt: string;
57
+ finishedAt: string | null;
58
+ heartbeatStatus: CommandJobHeartbeatStatus;
59
+ lastHeartbeatAt: string | null;
60
+ lastHeartbeatError: string | null;
61
+ heartbeatFailureCount: number;
62
+ }
63
+
64
+ /** A command job with its captured output. */
65
+ export interface CommandJobOutput extends CommandJob {
66
+ stdout: string;
67
+ stderr: string;
68
+ stdoutTruncated: boolean;
69
+ stderrTruncated: boolean;
70
+ }
71
+
72
+ /** A sandbox session summary returned by list. */
73
+ export interface SandboxSession {
74
+ id: string;
75
+ shortId: string;
76
+ endpoint: string;
77
+ status: string;
78
+ createdAt: string;
79
+ }
80
+
81
+ /** Options for listing sandbox sessions. */
82
+ export interface SandboxListOptions extends SandboxOptions {
83
+ cursor?: string;
84
+ limit?: number;
85
+ }
86
+
87
+ /** Paginated result of sandbox sessions. */
88
+ export interface SandboxListResult {
89
+ data: SandboxSession[];
90
+ pageInfo: {
91
+ self: string | null;
92
+ first: null;
93
+ next: string | null;
94
+ prev: string | null;
95
+ };
96
+ }
97
+
44
98
  /** Client for isolated ephemeral compute environments with command execution and file I/O. */
45
99
  export class Sandbox {
46
100
  private constructor(
@@ -117,6 +171,47 @@ export class Sandbox {
117
171
  return new Sandbox(endpoint, id, authToken, apiUrl);
118
172
  }
119
173
 
174
+ /** List sandbox sessions with optional pagination. */
175
+ static async list(options: SandboxListOptions = {}): Promise<SandboxListResult> {
176
+ const apiUrl = Sandbox.resolveApiUrl(options);
177
+ const authToken = Sandbox.resolveAuthToken(options);
178
+
179
+ const params = new URLSearchParams();
180
+ if (options.cursor) params.set("cursor", options.cursor);
181
+ if (options.limit !== undefined) params.set("limit", String(options.limit));
182
+
183
+ const query = params.toString();
184
+ const url = `${apiUrl}/sandbox-sessions${query ? `?${query}` : ""}`;
185
+
186
+ const res = await dntShim.fetch(url, {
187
+ headers: { Authorization: `Bearer ${authToken}` },
188
+ });
189
+
190
+ if (!res.ok) {
191
+ throw REQUEST_ERROR.create({
192
+ detail: `Failed to list sandboxes: ${res.status} ${await res.text()}`,
193
+ });
194
+ }
195
+
196
+ const json = await res.json();
197
+
198
+ return {
199
+ data: json.data.map((s: Record<string, unknown>) => ({
200
+ id: s.id,
201
+ shortId: s.short_id,
202
+ endpoint: s.endpoint,
203
+ status: s.status,
204
+ createdAt: s.created_at,
205
+ })),
206
+ pageInfo: {
207
+ self: json.page_info?.self ?? null,
208
+ first: null,
209
+ next: json.page_info?.next ?? null,
210
+ prev: json.page_info?.prev ?? null,
211
+ },
212
+ };
213
+ }
214
+
120
215
  private static async waitForReady(
121
216
  apiUrl: string,
122
217
  id: string,
@@ -247,6 +342,94 @@ export class Sandbox {
247
342
  }
248
343
  }
249
344
 
345
+ /** Start an async command job in the sandbox. */
346
+ async startCommandJob(command: string): Promise<CommandJob> {
347
+ const res = await dntShim.fetch(`${this.endpoint}/exec/jobs`, {
348
+ method: "POST",
349
+ headers: {
350
+ Authorization: `Bearer ${this.authToken}`,
351
+ "Content-Type": "application/json",
352
+ },
353
+ body: JSON.stringify({ command }),
354
+ });
355
+
356
+ if (!res.ok) {
357
+ throw REQUEST_ERROR.create({
358
+ detail: `Start command job failed: ${res.status} ${await res.text()}`,
359
+ });
360
+ }
361
+
362
+ return Sandbox.mapCommandJob(await res.json());
363
+ }
364
+
365
+ /** Get the status of an async command job. */
366
+ async getCommandJob(jobId: string): Promise<CommandJob> {
367
+ const res = await dntShim.fetch(`${this.endpoint}/exec/jobs/${jobId}`, {
368
+ headers: { Authorization: `Bearer ${this.authToken}` },
369
+ });
370
+
371
+ if (!res.ok) {
372
+ throw REQUEST_ERROR.create({
373
+ detail: `Get command job failed: ${res.status} ${await res.text()}`,
374
+ });
375
+ }
376
+
377
+ return Sandbox.mapCommandJob(await res.json());
378
+ }
379
+
380
+ /** Get the output of an async command job. */
381
+ async getCommandJobOutput(jobId: string): Promise<CommandJobOutput> {
382
+ const res = await dntShim.fetch(`${this.endpoint}/exec/jobs/${jobId}/output`, {
383
+ headers: { Authorization: `Bearer ${this.authToken}` },
384
+ });
385
+
386
+ if (!res.ok) {
387
+ throw REQUEST_ERROR.create({
388
+ detail: `Get command job output failed: ${res.status} ${await res.text()}`,
389
+ });
390
+ }
391
+
392
+ const json = await res.json();
393
+ return {
394
+ ...Sandbox.mapCommandJob(json),
395
+ stdout: json.stdout,
396
+ stderr: json.stderr,
397
+ stdoutTruncated: json.stdout_truncated,
398
+ stderrTruncated: json.stderr_truncated,
399
+ };
400
+ }
401
+
402
+ /** Cancel an async command job. */
403
+ async cancelCommandJob(jobId: string): Promise<CommandJob> {
404
+ const res = await dntShim.fetch(`${this.endpoint}/exec/jobs/${jobId}/cancel`, {
405
+ method: "POST",
406
+ headers: { Authorization: `Bearer ${this.authToken}` },
407
+ });
408
+
409
+ if (!res.ok) {
410
+ throw REQUEST_ERROR.create({
411
+ detail: `Cancel command job failed: ${res.status} ${await res.text()}`,
412
+ });
413
+ }
414
+
415
+ return Sandbox.mapCommandJob(await res.json());
416
+ }
417
+
418
+ private static mapCommandJob(json: Record<string, unknown>): CommandJob {
419
+ return {
420
+ id: json.id as string,
421
+ status: json.status as CommandJobStatus,
422
+ exitCode: json.exit_code as number | null,
423
+ signal: json.signal as string | null,
424
+ startedAt: json.started_at as string,
425
+ finishedAt: json.finished_at as string | null,
426
+ heartbeatStatus: json.heartbeat_status as CommandJobHeartbeatStatus,
427
+ lastHeartbeatAt: json.last_heartbeat_at as string | null,
428
+ lastHeartbeatError: json.last_heartbeat_error as string | null,
429
+ heartbeatFailureCount: json.heartbeat_failure_count as number,
430
+ };
431
+ }
432
+
250
433
  /** Send a heartbeat to prevent idle timeout. */
251
434
  async heartbeat(): Promise<void> {
252
435
  await dntShim.fetch(`${this.apiUrl}/sandbox-sessions/${this.sessionId}/heartbeat`, {
@@ -11,22 +11,33 @@ import { BaseHandler } from "../response/base.js";
11
11
  import type { HandlerContext, HandlerMetadata, HandlerPriority, HandlerResult } from "../types.js";
12
12
  import { HTTP_OK, PRIORITY_HIGH_DEV } from "../../../utils/constants/index.js";
13
13
  import { joinPath } from "../../../utils/path-utils.js";
14
- import { formatCSSError, getProjectCSS } from "../../../html/styles-builder/tailwind-compiler.js";
14
+ import {
15
+ formatCSSError,
16
+ getCSSByHashAsync,
17
+ getProjectCSS,
18
+ regenerateCSSByHash,
19
+ } from "../../../html/styles-builder/tailwind-compiler.js";
15
20
  import { DEFAULT_STYLESHEET } from "../../../html/styles-builder/css-hash-cache.js";
16
21
  import { resolveStyleContentVersion } from "../../../html/styles-builder/content-version.js";
17
22
  import {
18
23
  createPreparedProjectCSSContext,
24
+ type PreparedProjectCSSRequestContext,
19
25
  storePreparedProjectCSS,
20
26
  tryGetPreparedProjectCSS,
21
27
  } from "../../../html/styles-builder/prepared-project-css-cache.js";
22
28
  import { createStyleScopeProfile } from "../../../html/styles-builder/style-scope-profile.js";
23
29
  import { serverLogger } from "../../../utils/index.js";
24
30
  import type { ResolvedContentContext } from "../../../platform/adapters/fs/veryfront/types.js";
31
+ import type {
32
+ ResolveStyleArtifactInput,
33
+ VeryfrontApiClient,
34
+ } from "../../../platform/adapters/veryfront-api-client/index.js";
25
35
  import { extractProjectCandidates } from "./styles-candidate-scanner.js";
26
36
 
27
37
  const logger = serverLogger.component("styles-css-handler");
28
38
 
29
39
  type GeneratedStylesResult = Awaited<ReturnType<typeof getProjectCSS>>;
40
+ type StyleArtifactSelectorContext = Omit<ResolveStyleArtifactInput, "styleProfileHash">;
30
41
 
31
42
  export class StylesCSSHandler extends BaseHandler {
32
43
  metadata: HandlerMetadata = {
@@ -78,6 +89,25 @@ export class StylesCSSHandler extends BaseHandler {
78
89
  }
79
90
  }
80
91
 
92
+ const remotePrepared = await this.tryResolveRemotePreparedCSS(
93
+ ctx,
94
+ projectScope,
95
+ styleProfile.hash,
96
+ contentContext,
97
+ preparedContext,
98
+ );
99
+ if (remotePrepared) {
100
+ logger.debug("Prepared CSS resolved via style artifact metadata", {
101
+ projectScope,
102
+ styleProfileHash: styleProfile.hash,
103
+ cssHash: remotePrepared.hash,
104
+ });
105
+
106
+ return this.respond(
107
+ responseBuilder.withContentType("text/css; charset=utf-8", remotePrepared.css, HTTP_OK),
108
+ );
109
+ }
110
+
81
111
  let candidates: Set<string>;
82
112
  try {
83
113
  candidates = await extractProjectCandidates(ctx);
@@ -148,6 +178,15 @@ body::before {
148
178
  });
149
179
  }
150
180
 
181
+ if ("hash" in result) {
182
+ await this.registerPreparedCSSArtifact(
183
+ ctx,
184
+ styleProfile.hash,
185
+ contentContext,
186
+ result.hash,
187
+ );
188
+ }
189
+
151
190
  return this.respond(
152
191
  responseBuilder.withContentType("text/css; charset=utf-8", result.css, HTTP_OK),
153
192
  );
@@ -212,6 +251,17 @@ body::before {
212
251
  return typeof fsAdapter.getContentContext === "function" ? fsAdapter.getContentContext() : null;
213
252
  }
214
253
 
254
+ private getVeryfrontApiClient(ctx: HandlerContext): VeryfrontApiClient | null {
255
+ const wrappedFs = ctx.adapter.fs as { getUnderlyingAdapter?: () => unknown };
256
+ if (typeof wrappedFs.getUnderlyingAdapter !== "function") return null;
257
+
258
+ const fsAdapter = wrappedFs.getUnderlyingAdapter() as {
259
+ getClient?: () => VeryfrontApiClient;
260
+ };
261
+
262
+ return typeof fsAdapter.getClient === "function" ? fsAdapter.getClient() : null;
263
+ }
264
+
215
265
  private createPreparedCSSContext(
216
266
  projectScope: string | undefined,
217
267
  rawCss: string,
@@ -237,4 +287,132 @@ body::before {
237
287
  },
238
288
  );
239
289
  }
290
+
291
+ private resolveStyleArtifactSelector(
292
+ contentContext: ResolvedContentContext | null,
293
+ ctx: HandlerContext,
294
+ ): StyleArtifactSelectorContext | null {
295
+ if (contentContext?.sourceType === "branch" && contentContext.branch) {
296
+ return {
297
+ branch: contentContext.branch,
298
+ };
299
+ }
300
+
301
+ if (contentContext?.sourceType === "environment" && contentContext.environmentName) {
302
+ return {
303
+ environmentName: contentContext.environmentName,
304
+ };
305
+ }
306
+
307
+ if (contentContext?.sourceType === "release" && contentContext.releaseId) {
308
+ return {
309
+ releaseId: contentContext.releaseId,
310
+ };
311
+ }
312
+
313
+ if (ctx.parsedDomain?.branch) {
314
+ return {
315
+ branch: ctx.parsedDomain.branch,
316
+ };
317
+ }
318
+
319
+ if (ctx.environmentName) {
320
+ return {
321
+ environmentName: ctx.environmentName,
322
+ };
323
+ }
324
+
325
+ if (ctx.releaseId) {
326
+ return {
327
+ releaseId: ctx.releaseId,
328
+ };
329
+ }
330
+
331
+ return null;
332
+ }
333
+
334
+ private async tryResolveRemotePreparedCSS(
335
+ ctx: HandlerContext,
336
+ projectScope: string | undefined,
337
+ styleProfileHash: string,
338
+ contentContext: ResolvedContentContext | null,
339
+ preparedContext?: PreparedProjectCSSRequestContext,
340
+ ): Promise<{ css: string; hash: string } | undefined> {
341
+ if (!projectScope) return undefined;
342
+
343
+ const selector = this.resolveStyleArtifactSelector(contentContext, ctx);
344
+ if (!selector) return undefined;
345
+
346
+ const client = this.getVeryfrontApiClient(ctx);
347
+ if (!client) return undefined;
348
+
349
+ try {
350
+ const resolved = await client.resolveStyleArtifact({
351
+ ...selector,
352
+ styleProfileHash,
353
+ });
354
+
355
+ if (resolved.status !== "ready" || !resolved.artifactHash) {
356
+ return undefined;
357
+ }
358
+
359
+ const css = await this.getPreparedCSSByHash(resolved.artifactHash, projectScope);
360
+ if (!css) return undefined;
361
+
362
+ if (preparedContext) {
363
+ await storePreparedProjectCSS(preparedContext, {
364
+ css,
365
+ hash: resolved.artifactHash,
366
+ });
367
+ }
368
+
369
+ return {
370
+ css,
371
+ hash: resolved.artifactHash,
372
+ };
373
+ } catch (error) {
374
+ logger.debug("Failed to resolve prepared CSS via style artifact metadata", {
375
+ projectScope,
376
+ styleProfileHash,
377
+ error: error instanceof Error ? error.message : String(error),
378
+ });
379
+ return undefined;
380
+ }
381
+ }
382
+
383
+ private async getPreparedCSSByHash(
384
+ cssHash: string,
385
+ projectScope: string,
386
+ ): Promise<string | undefined> {
387
+ const cached = await getCSSByHashAsync(cssHash);
388
+ if (cached) return cached;
389
+ return regenerateCSSByHash(cssHash, projectScope);
390
+ }
391
+
392
+ private async registerPreparedCSSArtifact(
393
+ ctx: HandlerContext,
394
+ styleProfileHash: string,
395
+ contentContext: ResolvedContentContext | null,
396
+ cssHash: string,
397
+ ): Promise<void> {
398
+ const selector = this.resolveStyleArtifactSelector(contentContext, ctx);
399
+ if (!selector) return;
400
+
401
+ const client = this.getVeryfrontApiClient(ctx);
402
+ if (!client) return;
403
+
404
+ try {
405
+ await client.upsertStyleArtifact({
406
+ ...selector,
407
+ styleProfileHash,
408
+ artifactHash: cssHash,
409
+ });
410
+ } catch (error) {
411
+ logger.debug("Failed to register prepared CSS artifact", {
412
+ cssHash,
413
+ styleProfileHash,
414
+ error: error instanceof Error ? error.message : String(error),
415
+ });
416
+ }
417
+ }
240
418
  }
@@ -4,7 +4,7 @@ import type { HandlerContext, HandlerMetadata, HandlerPriority, HandlerResult }
4
4
  import { joinPath } from "../../../utils/path-utils.js";
5
5
  import { HTTP_OK, HTTP_UNAVAILABLE, PRIORITY_HIGH } from "../../../utils/constants/index.js";
6
6
  import { isTracingDegraded, isTracingEnabled } from "../../../observability/tracing/index.js";
7
- import { VERSION } from "../../../utils/version.js";
7
+ import { RUNTIME_VERSION } from "../../../utils/version.js";
8
8
 
9
9
  let serverInitialized = false;
10
10
 
@@ -74,7 +74,7 @@ export class HealthHandler extends BaseHandler {
74
74
  status: tracingDegraded ? "degraded" : "ok",
75
75
  timestamp: new Date().toISOString(),
76
76
  mode: hasStaticBuild ? "static+ssr" : "ssr",
77
- version: VERSION,
77
+ version: RUNTIME_VERSION,
78
78
  tracing: {
79
79
  enabled: isTracingEnabled(),
80
80
  degraded: tracingDegraded,
@@ -1,7 +1,7 @@
1
1
  import * as dntShim from "../../../_dnt.shims.js";
2
2
  import { getEnv } from "../../platform/compat/process.js";
3
3
  import { hasDenoRuntime, hasNodeProcess } from "../runtime-guards.js";
4
- import { VERSION } from "../version.js";
4
+ import { RUNTIME_VERSION } from "../version.js";
5
5
  import {
6
6
  ANSI,
7
7
  colorize,
@@ -287,7 +287,7 @@ class ConsoleLogger implements Logger {
287
287
  timestamp: new Date().toISOString(),
288
288
  level,
289
289
  service: this.prefix.toLowerCase(),
290
- veryfrontVersion: VERSION,
290
+ veryfrontVersion: RUNTIME_VERSION,
291
291
  message,
292
292
  };
293
293
 
@@ -1,6 +1,41 @@
1
+ import denoConfig from "../../deno.js";
2
+ import { getEnv } from "../platform/compat/process.js";
3
+
1
4
  // Keep in sync with deno.json version.
2
5
  // scripts/release.ts updates this constant during releases.
3
- export const VERSION = "0.1.89";
6
+ export const VERSION = "0.1.92";
7
+
8
+ export function normalizeVeryfrontVersion(version: string | undefined): string | undefined {
9
+ if (!version) return undefined;
10
+ return version.replace(/^v(?=\d)/, "");
11
+ }
12
+
13
+ function getVersionEnv(name: string): string | undefined {
14
+ try {
15
+ return getEnv(name);
16
+ } catch {
17
+ return undefined;
18
+ }
19
+ }
20
+
21
+ export function resolveRuntimeVersion(options: {
22
+ veryfrontVersion?: string;
23
+ releaseVersion?: string;
24
+ denoVersion?: string;
25
+ fallbackVersion?: string;
26
+ } = {}): string {
27
+ return normalizeVeryfrontVersion(options.veryfrontVersion ?? options.releaseVersion) ??
28
+ normalizeVeryfrontVersion(options.denoVersion) ??
29
+ options.fallbackVersion ??
30
+ VERSION;
31
+ }
32
+
33
+ export const RUNTIME_VERSION = resolveRuntimeVersion({
34
+ veryfrontVersion: getVersionEnv("VERYFRONT_VERSION"),
35
+ releaseVersion: getVersionEnv("RELEASE_VERSION"),
36
+ denoVersion: typeof denoConfig.version === "string" ? denoConfig.version : undefined,
37
+ fallbackVersion: VERSION,
38
+ });
4
39
 
5
40
  export const SERVER_START_TIME: number = Date.now();
6
41
 
@@ -12,7 +47,7 @@ export interface BuildVersion {
12
47
 
13
48
  export function createBuildVersion(projectUpdatedAt?: string): BuildVersion {
14
49
  return {
15
- framework: VERSION,
50
+ framework: RUNTIME_VERSION,
16
51
  serverStart: SERVER_START_TIME,
17
52
  projectUpdated: projectUpdatedAt,
18
53
  };