veryfront 0.1.498 → 0.1.500

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 (152) hide show
  1. package/esm/cli/auth/provider-store.d.ts +1 -1
  2. package/esm/cli/auth/provider-store.js +1 -1
  3. package/esm/cli/commands/doctor/ai-checks.js +1 -1
  4. package/esm/cli/skills/core-skills.js +1 -1
  5. package/esm/deno.d.ts +4 -0
  6. package/esm/deno.js +9 -5
  7. package/esm/extensions/{ext-anthropic → ext-llm-anthropic}/src/anthropic-provider.d.ts +5 -5
  8. package/esm/extensions/ext-llm-anthropic/src/anthropic-provider.d.ts.map +1 -0
  9. package/esm/extensions/{ext-anthropic → ext-llm-anthropic}/src/anthropic-provider.js +2 -2
  10. package/esm/extensions/{ext-anthropic → ext-llm-anthropic}/src/index.d.ts +3 -3
  11. package/esm/extensions/ext-llm-anthropic/src/index.d.ts.map +1 -0
  12. package/esm/extensions/{ext-anthropic → ext-llm-anthropic}/src/index.js +8 -8
  13. package/esm/extensions/{ext-google → ext-llm-google}/src/google-provider.d.ts +6 -6
  14. package/esm/extensions/ext-llm-google/src/google-provider.d.ts.map +1 -0
  15. package/esm/extensions/{ext-google → ext-llm-google}/src/google-provider.js +2 -2
  16. package/esm/extensions/{ext-google → ext-llm-google}/src/index.d.ts +3 -3
  17. package/esm/extensions/ext-llm-google/src/index.d.ts.map +1 -0
  18. package/esm/extensions/ext-llm-google/src/index.js +26 -0
  19. package/esm/extensions/{ext-openai → ext-llm-openai}/src/index.d.ts +3 -3
  20. package/esm/extensions/ext-llm-openai/src/index.d.ts.map +1 -0
  21. package/esm/extensions/ext-llm-openai/src/index.js +28 -0
  22. package/esm/extensions/{ext-openai → ext-llm-openai}/src/openai-provider.d.ts +7 -7
  23. package/esm/extensions/ext-llm-openai/src/openai-provider.d.ts.map +1 -0
  24. package/esm/extensions/{ext-openai → ext-llm-openai}/src/openai-provider.js +2 -2
  25. package/esm/src/agent/agent-service-server.d.ts +9 -1
  26. package/esm/src/agent/agent-service-server.d.ts.map +1 -1
  27. package/esm/src/agent/agent-service-server.js +14 -1
  28. package/esm/src/agent/hosted-agent-service-runtime.d.ts +10 -1
  29. package/esm/src/agent/hosted-agent-service-runtime.d.ts.map +1 -1
  30. package/esm/src/agent/hosted-agent-service-runtime.js +18 -1
  31. package/esm/src/agent/hosted-stream-terminal-error.js +1 -1
  32. package/esm/src/agent/index.d.ts +3 -2
  33. package/esm/src/agent/index.d.ts.map +1 -1
  34. package/esm/src/agent/index.js +3 -2
  35. package/esm/src/agent/runtime-agent-definition-files.d.ts.map +1 -1
  36. package/esm/src/agent/runtime-agent-definition-files.js +8 -3
  37. package/esm/src/agent/veryfront-cloud-agent-service.d.ts +41 -0
  38. package/esm/src/agent/veryfront-cloud-agent-service.d.ts.map +1 -0
  39. package/esm/src/agent/veryfront-cloud-agent-service.js +501 -0
  40. package/esm/src/chat/provider-errors.js +3 -3
  41. package/esm/src/config/schemas/config.schema.d.ts +24 -0
  42. package/esm/src/config/schemas/config.schema.d.ts.map +1 -1
  43. package/esm/src/config/schemas/config.schema.js +48 -0
  44. package/esm/src/discovery/index.d.ts +1 -0
  45. package/esm/src/discovery/index.d.ts.map +1 -1
  46. package/esm/src/discovery/index.js +1 -0
  47. package/esm/src/discovery/project-discovery-config.d.ts +30 -0
  48. package/esm/src/discovery/project-discovery-config.d.ts.map +1 -0
  49. package/esm/src/discovery/project-discovery-config.js +33 -0
  50. package/esm/src/embedding/resolve.js +5 -5
  51. package/esm/src/embedding/veryfront-cloud/provider.js +3 -3
  52. package/esm/src/extensions/builtin-extensions.d.ts +2 -2
  53. package/esm/src/extensions/builtin-extensions.d.ts.map +1 -1
  54. package/esm/src/extensions/builtin-extensions.js +26 -27
  55. package/esm/src/extensions/interfaces/index.d.ts +9 -5
  56. package/esm/src/extensions/interfaces/index.d.ts.map +1 -1
  57. package/esm/src/extensions/interfaces/index.js +7 -3
  58. package/esm/src/extensions/{interfaces → llm}/embedding-provider.d.ts +1 -1
  59. package/esm/src/extensions/llm/embedding-provider.d.ts.map +1 -0
  60. package/esm/src/extensions/{interfaces → llm}/embedding-provider.js +1 -1
  61. package/esm/src/extensions/llm/index.d.ts +16 -0
  62. package/esm/src/extensions/llm/index.d.ts.map +1 -0
  63. package/esm/src/extensions/llm/index.js +13 -0
  64. package/esm/src/extensions/llm/llm-provider-registry.d.ts +11 -0
  65. package/esm/src/extensions/llm/llm-provider-registry.d.ts.map +1 -0
  66. package/esm/src/extensions/{registries/ai-provider-registry.js → llm/llm-provider-registry.js} +6 -6
  67. package/esm/src/extensions/{interfaces/ai-provider.d.ts → llm/llm-provider.d.ts} +16 -16
  68. package/esm/src/extensions/llm/llm-provider.d.ts.map +1 -0
  69. package/esm/src/extensions/{interfaces/ai-provider.js → llm/llm-provider.js} +4 -4
  70. package/esm/src/extensions/loader.d.ts +1 -1
  71. package/esm/src/extensions/loader.js +1 -1
  72. package/esm/src/extensions/orchestrate.d.ts +1 -1
  73. package/esm/src/extensions/orchestrate.d.ts.map +1 -1
  74. package/esm/src/extensions/recommendations.js +4 -4
  75. package/esm/src/provider/local/env.d.ts +1 -1
  76. package/esm/src/provider/local/env.js +1 -1
  77. package/esm/src/provider/model-registry.d.ts.map +1 -1
  78. package/esm/src/provider/model-registry.js +9 -9
  79. package/esm/src/provider/shared/index.d.ts +1 -1
  80. package/esm/src/provider/shared/index.js +1 -1
  81. package/esm/src/provider/veryfront-cloud/openai.js +4 -4
  82. package/esm/src/provider/veryfront-cloud/provider.js +2 -2
  83. package/esm/src/server/bootstrap.d.ts.map +1 -1
  84. package/esm/src/server/bootstrap.js +4 -5
  85. package/esm/src/server/handlers/request/api/project-discovery.d.ts.map +1 -1
  86. package/esm/src/server/handlers/request/api/project-discovery.js +6 -22
  87. package/esm/src/server/index.d.ts +1 -1
  88. package/esm/src/server/index.d.ts.map +1 -1
  89. package/esm/src/server/index.js +1 -1
  90. package/esm/src/server/service-server.d.ts +18 -0
  91. package/esm/src/server/service-server.d.ts.map +1 -1
  92. package/esm/src/server/service-server.js +318 -0
  93. package/esm/src/utils/version-constant.d.ts +1 -1
  94. package/esm/src/utils/version-constant.js +1 -1
  95. package/esm/src/workflow/schemas/workflow.schema.d.ts +1 -1
  96. package/package.json +6 -1
  97. package/src/cli/auth/provider-store.ts +1 -1
  98. package/src/cli/commands/doctor/ai-checks.ts +1 -1
  99. package/src/cli/skills/core-skills.ts +1 -1
  100. package/src/deno.js +9 -5
  101. package/src/extensions/{ext-anthropic → ext-llm-anthropic}/src/anthropic-provider.ts +5 -5
  102. package/src/extensions/ext-llm-anthropic/src/index.ts +34 -0
  103. package/src/extensions/{ext-google → ext-llm-google}/src/google-provider.ts +6 -6
  104. package/src/extensions/ext-llm-google/src/index.ts +31 -0
  105. package/src/extensions/ext-llm-openai/src/index.ts +33 -0
  106. package/src/extensions/{ext-openai → ext-llm-openai}/src/openai-provider.ts +7 -7
  107. package/src/src/agent/agent-service-server.ts +26 -0
  108. package/src/src/agent/hosted-agent-service-runtime.ts +42 -0
  109. package/src/src/agent/hosted-stream-terminal-error.ts +1 -1
  110. package/src/src/agent/index.ts +16 -0
  111. package/src/src/agent/runtime-agent-definition-files.ts +8 -3
  112. package/src/src/agent/veryfront-cloud-agent-service.ts +798 -0
  113. package/src/src/chat/provider-errors.ts +3 -3
  114. package/src/src/config/schemas/config.schema.ts +48 -0
  115. package/src/src/discovery/index.ts +4 -0
  116. package/src/src/discovery/project-discovery-config.ts +89 -0
  117. package/src/src/embedding/resolve.ts +6 -6
  118. package/src/src/embedding/veryfront-cloud/provider.ts +4 -4
  119. package/src/src/extensions/builtin-extensions.ts +32 -33
  120. package/src/src/extensions/interfaces/index.ts +11 -7
  121. package/src/src/extensions/{interfaces → llm}/embedding-provider.ts +1 -1
  122. package/src/src/extensions/llm/index.ts +17 -0
  123. package/src/src/extensions/{registries/ai-provider-registry.ts → llm/llm-provider-registry.ts} +12 -12
  124. package/src/src/extensions/{interfaces/ai-provider.ts → llm/llm-provider.ts} +15 -15
  125. package/src/src/extensions/loader.ts +1 -1
  126. package/src/src/extensions/orchestrate.ts +1 -1
  127. package/src/src/extensions/recommendations.ts +4 -4
  128. package/src/src/provider/local/env.ts +1 -1
  129. package/src/src/provider/model-registry.ts +11 -10
  130. package/src/src/provider/shared/index.ts +1 -1
  131. package/src/src/provider/veryfront-cloud/openai.ts +4 -4
  132. package/src/src/provider/veryfront-cloud/provider.ts +2 -2
  133. package/src/src/server/bootstrap.ts +7 -5
  134. package/src/src/server/handlers/request/api/project-discovery.ts +6 -27
  135. package/src/src/server/index.ts +4 -0
  136. package/src/src/server/service-server.ts +428 -0
  137. package/src/src/utils/version-constant.ts +1 -1
  138. package/esm/extensions/ext-anthropic/src/anthropic-provider.d.ts.map +0 -1
  139. package/esm/extensions/ext-anthropic/src/index.d.ts.map +0 -1
  140. package/esm/extensions/ext-google/src/google-provider.d.ts.map +0 -1
  141. package/esm/extensions/ext-google/src/index.d.ts.map +0 -1
  142. package/esm/extensions/ext-google/src/index.js +0 -26
  143. package/esm/extensions/ext-openai/src/index.d.ts.map +0 -1
  144. package/esm/extensions/ext-openai/src/index.js +0 -28
  145. package/esm/extensions/ext-openai/src/openai-provider.d.ts.map +0 -1
  146. package/esm/src/extensions/interfaces/ai-provider.d.ts.map +0 -1
  147. package/esm/src/extensions/interfaces/embedding-provider.d.ts.map +0 -1
  148. package/esm/src/extensions/registries/ai-provider-registry.d.ts +0 -11
  149. package/esm/src/extensions/registries/ai-provider-registry.d.ts.map +0 -1
  150. package/src/extensions/ext-anthropic/src/index.ts +0 -34
  151. package/src/extensions/ext-google/src/index.ts +0 -31
  152. package/src/extensions/ext-openai/src/index.ts +0 -33
@@ -19,10 +19,10 @@ import {
19
19
  getGoogleGenAIEnvConfig,
20
20
  getOpenAIEnvConfig,
21
21
  } from "../config/env.js";
22
- import { ensureBuiltinAIProviders } from "../extensions/builtin-extensions.js";
22
+ import { ensureBuiltinLLMProviders } from "../extensions/builtin-extensions.js";
23
23
  import { tryResolve } from "../extensions/contracts.js";
24
- import type { AIProviderRegistry } from "../extensions/interfaces/index.js";
25
- import { AIProviderRegistryName } from "../extensions/interfaces/index.js";
24
+ import type { LLMProviderRegistry } from "../extensions/interfaces/index.js";
25
+ import { LLMProviderRegistryName } from "../extensions/interfaces/index.js";
26
26
  import { ProjectScopedRegistryManager } from "../registry/project-scoped-registry-manager.js";
27
27
  import { serverLogger } from "../utils/index.js";
28
28
  import { DEFAULT_LOCAL_MODEL } from "./local/model-catalog.js";
@@ -91,7 +91,7 @@ function autoInitializeFromEnv(): void {
91
91
  }),
92
92
  );
93
93
  }
94
- const registry = ensureBuiltinAIProviders();
94
+ const registry = ensureBuiltinLLMProviders();
95
95
  const provider = registry.get("openai");
96
96
  if (provider) {
97
97
  return provider.createModel(id, {
@@ -101,7 +101,8 @@ function autoInitializeFromEnv(): void {
101
101
  }
102
102
  throw toError(createError({
103
103
  type: "config",
104
- message: "OpenAI provider not installed. Add @veryfront/ext-openai to use openai/* models.",
104
+ message:
105
+ "OpenAI provider not installed. Add @veryfront/ext-llm-openai to use openai/* models.",
105
106
  }));
106
107
  });
107
108
  }
@@ -118,7 +119,7 @@ function autoInitializeFromEnv(): void {
118
119
  }),
119
120
  );
120
121
  }
121
- const registry = ensureBuiltinAIProviders();
122
+ const registry = ensureBuiltinLLMProviders();
122
123
  const provider = registry.get("anthropic");
123
124
  if (provider) {
124
125
  return provider.createModel(id, {
@@ -129,7 +130,7 @@ function autoInitializeFromEnv(): void {
129
130
  throw toError(createError({
130
131
  type: "config",
131
132
  message:
132
- "Anthropic provider not installed. Add @veryfront/ext-anthropic to use anthropic/* models.",
133
+ "Anthropic provider not installed. Add @veryfront/ext-llm-anthropic to use anthropic/* models.",
133
134
  }));
134
135
  });
135
136
  }
@@ -146,7 +147,7 @@ function autoInitializeFromEnv(): void {
146
147
  }),
147
148
  );
148
149
  }
149
- const registry = ensureBuiltinAIProviders();
150
+ const registry = ensureBuiltinLLMProviders();
150
151
  const provider = registry.get("google");
151
152
  if (provider) {
152
153
  return provider.createModel(id, {
@@ -157,7 +158,7 @@ function autoInitializeFromEnv(): void {
157
158
  createError({
158
159
  type: "config",
159
160
  message:
160
- "Google provider not installed. Add @veryfront/ext-google to use google/* models.",
161
+ "Google provider not installed. Add @veryfront/ext-llm-google to use google/* models.",
161
162
  }),
162
163
  );
163
164
  });
@@ -233,7 +234,7 @@ function isMissingProviderConfiguration(errorData: ReturnType<typeof fromError>)
233
234
  */
234
235
  export function findAvailableCloudModel(): string | null {
235
236
  autoInitializeFromEnv();
236
- const registry = tryResolve<AIProviderRegistry>(AIProviderRegistryName);
237
+ const registry = tryResolve<LLMProviderRegistry>(LLMProviderRegistryName);
237
238
  for (const { provider, model, hasKey } of CLOUD_UPGRADE_CANDIDATES) {
238
239
  if (!hasKey()) continue;
239
240
  if (!manager.has(provider)) continue;
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * This barrel is the stable public surface: implementations currently live
5
5
  * in `runtime-loader.ts` and `runtime-loader/` subdirectory. Future PRs
6
- * (post ext-anthropic / ext-google extraction) may move the implementations
6
+ * (post ext-llm-anthropic / ext-llm-google extraction) may move the implementations
7
7
  * into this directory; extensions keep importing from here unchanged.
8
8
  *
9
9
  * @module provider/shared
@@ -1,7 +1,7 @@
1
1
  import type { EmbeddingRuntime, ModelRuntime } from "../types.js";
2
- import { OpenAIProvider } from "../../../extensions/ext-openai/src/index.js";
2
+ import { OpenAIProvider } from "../../../extensions/ext-llm-openai/src/index.js";
3
3
 
4
- const openAIProvider = new OpenAIProvider();
4
+ const openLLMProvider = new OpenAIProvider();
5
5
 
6
6
  interface VeryfrontCloudOpenAIConfig {
7
7
  apiToken: string;
@@ -13,7 +13,7 @@ export function createVeryfrontCloudOpenAIModel(
13
13
  modelId: string,
14
14
  config: VeryfrontCloudOpenAIConfig,
15
15
  ): ModelRuntime {
16
- return openAIProvider.createModel(modelId, {
16
+ return openLLMProvider.createModel(modelId, {
17
17
  credential: config.apiToken,
18
18
  baseURL: config.baseURL,
19
19
  name: "veryfront-cloud",
@@ -25,7 +25,7 @@ export function createVeryfrontCloudOpenAIEmbeddingModel(
25
25
  modelId: string,
26
26
  config: VeryfrontCloudOpenAIConfig,
27
27
  ): EmbeddingRuntime {
28
- return openAIProvider.createEmbedding(modelId, {
28
+ return openLLMProvider.createEmbedding(modelId, {
29
29
  credential: config.apiToken,
30
30
  baseURL: config.baseURL,
31
31
  name: "veryfront-cloud",
@@ -1,5 +1,5 @@
1
1
  import { createError, toError } from "../../errors/veryfront-error.js";
2
- import { ensureBuiltinAIProviders } from "../../extensions/builtin-extensions.js";
2
+ import { ensureBuiltinLLMProviders } from "../../extensions/builtin-extensions.js";
3
3
  import type { ModelRuntime } from "../types.js";
4
4
  import {
5
5
  createVeryfrontCloudFetch,
@@ -14,7 +14,7 @@ export function createVeryfrontCloudModel(modelId: string): ModelRuntime {
14
14
  const { apiBaseUrl, apiToken, projectSlug } = requireVeryfrontCloudBootstrap();
15
15
  const baseURL = getVeryfrontCloudGatewayBaseUrl(apiBaseUrl, provider);
16
16
  const fetch = createVeryfrontCloudFetch(apiToken, projectSlug);
17
- const registry = ensureBuiltinAIProviders();
17
+ const registry = ensureBuiltinLLMProviders();
18
18
 
19
19
  switch (provider) {
20
20
  case "anthropic": {
@@ -3,8 +3,10 @@ import type { VeryfrontConfig } from "../config/index.js";
3
3
  import type { InvalidationProjectContext } from "../platform/adapters/fs/veryfront/types.js";
4
4
  import { clearConfigCache, getConfig } from "../config/index.js";
5
5
  import { type ExtensionLoader, orchestrateExtensions, tryResolve } from "../extensions/index.js";
6
- import { AIProviderRegistryName } from "../extensions/interfaces/index.js";
7
- import { createAIProviderRegistry } from "../extensions/registries/ai-provider-registry.js";
6
+ import {
7
+ createLLMProviderRegistry,
8
+ LLMProviderRegistryName,
9
+ } from "../extensions/llm/index.js";
8
10
  import { createBuiltinExtensions } from "../extensions/builtin-extensions.js";
9
11
  import { MISSING_EXTENSION_ERROR } from "../extensions/errors.js";
10
12
  import { getRecommendation } from "../extensions/recommendations.js";
@@ -281,7 +283,7 @@ export async function bootstrap(
281
283
  projectDir,
282
284
  config,
283
285
  logger: bootstrapLog,
284
- primeContracts: { [AIProviderRegistryName]: createAIProviderRegistry() },
286
+ primeContracts: { [LLMProviderRegistryName]: createLLMProviderRegistry() },
285
287
  builtinExtensions: createBuiltinExtensions(),
286
288
  });
287
289
  wireTracingShim();
@@ -325,7 +327,7 @@ export async function bootstrap(
325
327
  projectDir,
326
328
  config,
327
329
  logger: bootstrapLog,
328
- primeContracts: { [AIProviderRegistryName]: createAIProviderRegistry() },
330
+ primeContracts: { [LLMProviderRegistryName]: createLLMProviderRegistry() },
329
331
  builtinExtensions: createBuiltinExtensions(),
330
332
  });
331
333
  wireTracingShim();
@@ -398,7 +400,7 @@ export async function bootstrap(
398
400
  projectDir,
399
401
  config,
400
402
  logger: bootstrapLog,
401
- primeContracts: { [AIProviderRegistryName]: createAIProviderRegistry() },
403
+ primeContracts: { [LLMProviderRegistryName]: createLLMProviderRegistry() },
402
404
  builtinExtensions: createBuiltinExtensions(),
403
405
  }),
404
406
  fsDispose,
@@ -1,5 +1,5 @@
1
1
  import { serverLogger } from "../../../../utils/index.js";
2
- import { clearTrackedAgents } from "../../../../discovery/index.js";
2
+ import { clearTrackedAgents, createProjectDiscoveryConfig } from "../../../../discovery/index.js";
3
3
  import { tryGetCacheKeyContext } from "../../../../cache/cache-key-builder.js";
4
4
  import type { HandlerContext } from "../../types.js";
5
5
 
@@ -16,31 +16,6 @@ const logger = serverLogger.component("api-wrapper");
16
16
  */
17
17
  const discoveredProjects = new Map<string, Promise<void>>();
18
18
 
19
- function isDiscoveryEnabled(
20
- discovery: { enabled?: boolean } | undefined,
21
- ): boolean {
22
- return discovery?.enabled ?? true;
23
- }
24
-
25
- function buildDiscoveryConfig(ctx: HandlerContext) {
26
- const discoveryConfig = ctx.config?.ai;
27
- const toolDiscovery = discoveryConfig?.tools?.discovery;
28
- const agentDiscovery = discoveryConfig?.agents?.discovery;
29
- const skillDiscovery = discoveryConfig?.skills?.discovery;
30
-
31
- return {
32
- baseDir: ctx.projectDir,
33
- toolDirs: isDiscoveryEnabled(toolDiscovery) ? (toolDiscovery?.paths ?? ["tools"]) : [],
34
- agentDirs: isDiscoveryEnabled(agentDiscovery) ? (agentDiscovery?.paths ?? ["agents"]) : [],
35
- skillDirs: isDiscoveryEnabled(skillDiscovery) ? (skillDiscovery?.paths ?? ["skills"]) : [],
36
- resourceDirs: ["resources"],
37
- promptDirs: ["prompts"],
38
- workflowDirs: ["workflows"],
39
- fsAdapter: ctx.adapter.fs,
40
- verbose: false,
41
- };
42
- }
43
-
44
19
  /** Build a discovery cache key that incorporates the release/version. */
45
20
  function discoveryKey(ctx: HandlerContext): string {
46
21
  const cacheContext = tryGetCacheKeyContext();
@@ -99,7 +74,11 @@ export async function ensureProjectDiscovery(ctx: HandlerContext): Promise<void>
99
74
  agentRegistry.clear();
100
75
  toolRegistry.clear();
101
76
 
102
- const discoveryOptions = buildDiscoveryConfig(ctx);
77
+ const discoveryOptions = createProjectDiscoveryConfig({
78
+ projectDir: ctx.projectDir,
79
+ config: ctx.config,
80
+ fsAdapter: ctx.adapter.fs,
81
+ });
103
82
  const result = await discoverAll(discoveryOptions);
104
83
  const shouldWarnOnEmptyAiDiscovery = discoveryOptions.toolDirs.length > 0 ||
105
84
  discoveryOptions.agentDirs.length > 0;
@@ -65,11 +65,15 @@ export {
65
65
  type NodeVeryfrontServiceServer,
66
66
  startNodeVeryfrontServer,
67
67
  type StartNodeVeryfrontServerOptions,
68
+ startVeryfrontServer,
69
+ type StartVeryfrontServerOptions,
70
+ type VeryfrontServiceServer,
68
71
  type VeryfrontServiceServerFetch,
69
72
  type VeryfrontServiceServerLogger,
70
73
  type VeryfrontServiceServerModule,
71
74
  type VeryfrontServiceServerModuleResponse,
72
75
  type VeryfrontServiceServerRuntime,
76
+ type VeryfrontServiceServerRuntimeKind,
73
77
  } from "./service-server.js";
74
78
  export type {
75
79
  DevServerOptions,
@@ -43,12 +43,80 @@ export type StartNodeVeryfrontServerOptions = {
43
43
  hardShutdownTimeoutMs?: number;
44
44
  };
45
45
 
46
+ export type StartVeryfrontServerOptions = {
47
+ runtime: VeryfrontServiceServerRuntime;
48
+ port: number;
49
+ bindAddress?: string;
50
+ logger?: VeryfrontServiceServerLogger;
51
+ signals?: readonly NodeJS.Signals[];
52
+ hardShutdownTimeoutMs?: number;
53
+ };
54
+
55
+ export type VeryfrontServiceServerRuntimeKind = "node" | "deno" | "bun";
56
+
57
+ export type VeryfrontServiceServer = {
58
+ ready: Promise<void>;
59
+ stop: () => Promise<void>;
60
+ port: number;
61
+ url: string;
62
+ runtime: VeryfrontServiceServerRuntimeKind;
63
+ };
64
+
46
65
  export type NodeVeryfrontServiceServer = {
47
66
  server: import("node:http").Server;
48
67
  ready: Promise<void>;
49
68
  stop: () => Promise<void>;
50
69
  port: number;
51
70
  url: string;
71
+ runtime: "node";
72
+ };
73
+
74
+ type DenoServeOptions = {
75
+ port: number;
76
+ hostname?: string;
77
+ signal?: AbortSignal;
78
+ onListen?: (address: { port: number; hostname: string }) => void;
79
+ };
80
+
81
+ type DenoServeHandler = (request: Request) => Response | Promise<Response>;
82
+
83
+ type DenoHttpServer = {
84
+ addr?: {
85
+ port?: number;
86
+ };
87
+ finished?: Promise<void>;
88
+ shutdown?: () => void | Promise<void>;
89
+ };
90
+
91
+ type DenoServeRuntime = {
92
+ serve: (options: DenoServeOptions, handler: DenoServeHandler) => DenoHttpServer;
93
+ addSignalListener?: (signal: NodeJS.Signals, handler: SignalHandler) => void;
94
+ removeSignalListener?: (signal: NodeJS.Signals, handler: SignalHandler) => void;
95
+ exit?: (code: number) => never | void;
96
+ };
97
+
98
+ type BunServeOptions = {
99
+ port: number;
100
+ hostname?: string;
101
+ fetch: VeryfrontServiceServerFetch;
102
+ };
103
+
104
+ type BunHttpServer = {
105
+ port?: number;
106
+ url?: URL;
107
+ stop?: () => void | Promise<void>;
108
+ };
109
+
110
+ type BunServeRuntime = {
111
+ serve: (options: BunServeOptions) => BunHttpServer;
112
+ };
113
+
114
+ type SignalHandler = () => void;
115
+
116
+ type SignalRuntime = {
117
+ add: (signal: NodeJS.Signals, handler: SignalHandler) => void;
118
+ remove?: (signal: NodeJS.Signals, handler: SignalHandler) => void;
119
+ exit?: (code: number) => never | void;
52
120
  };
53
121
 
54
122
  function defaultNotFound(): Response {
@@ -116,6 +184,365 @@ function closeNodeServer(server: import("node:http").Server): Promise<void> {
116
184
  });
117
185
  }
118
186
 
187
+ function isObject(value: unknown): value is Record<string, unknown> {
188
+ return typeof value === "object" && value !== null;
189
+ }
190
+
191
+ function isDenoHttpServer(value: unknown): value is DenoHttpServer {
192
+ if (!isObject(value)) {
193
+ return false;
194
+ }
195
+ const finished = value.finished;
196
+ const shutdown = value.shutdown;
197
+ const addr = value.addr;
198
+ const port = isObject(addr) ? addr.port : undefined;
199
+ return (addr === undefined || isObject(addr)) &&
200
+ (port === undefined || typeof port === "number") &&
201
+ (finished === undefined || finished instanceof Promise) &&
202
+ (shutdown === undefined || typeof shutdown === "function");
203
+ }
204
+
205
+ function isBunHttpServer(value: unknown): value is BunHttpServer {
206
+ if (!isObject(value)) {
207
+ return false;
208
+ }
209
+ const port = value.port;
210
+ const url = value.url;
211
+ const stop = value.stop;
212
+ return (port === undefined || typeof port === "number") &&
213
+ (url === undefined || url instanceof URL) &&
214
+ (stop === undefined || typeof stop === "function");
215
+ }
216
+
217
+ function getDenoServeRuntime(): DenoServeRuntime | null {
218
+ const denoGlobal: unknown = Reflect.get(dntShim.dntGlobalThis, "Deno");
219
+ if (!isObject(denoGlobal)) {
220
+ return null;
221
+ }
222
+ const serve = denoGlobal.serve;
223
+ const addSignalListener = denoGlobal.addSignalListener;
224
+ const removeSignalListener = denoGlobal.removeSignalListener;
225
+ const exit = denoGlobal.exit;
226
+ if (typeof serve !== "function") {
227
+ return null;
228
+ }
229
+ const runtime: DenoServeRuntime = {
230
+ serve: (options, handler) => {
231
+ const server: unknown = Reflect.apply(serve, denoGlobal, [options, handler]);
232
+ if (!isDenoHttpServer(server)) {
233
+ return {};
234
+ }
235
+ return server;
236
+ },
237
+ };
238
+ if (typeof addSignalListener === "function") {
239
+ runtime.addSignalListener = (signal, handler) => {
240
+ Reflect.apply(addSignalListener, denoGlobal, [signal, handler]);
241
+ };
242
+ }
243
+ if (typeof removeSignalListener === "function") {
244
+ runtime.removeSignalListener = (signal, handler) => {
245
+ Reflect.apply(removeSignalListener, denoGlobal, [signal, handler]);
246
+ };
247
+ }
248
+ if (typeof exit === "function") {
249
+ runtime.exit = (code) => Reflect.apply(exit, denoGlobal, [code]);
250
+ }
251
+ return runtime;
252
+ }
253
+
254
+ function getBunServeRuntime(): BunServeRuntime | null {
255
+ const bunGlobal: unknown = Reflect.get(dntShim.dntGlobalThis, "Bun");
256
+ if (!isObject(bunGlobal)) {
257
+ return null;
258
+ }
259
+ const serve = bunGlobal.serve;
260
+ if (typeof serve !== "function") {
261
+ return null;
262
+ }
263
+ return {
264
+ serve: (options) => {
265
+ const server: unknown = Reflect.apply(serve, bunGlobal, [options]);
266
+ if (!isBunHttpServer(server)) {
267
+ return {};
268
+ }
269
+ return server;
270
+ },
271
+ };
272
+ }
273
+
274
+ function getProcessSignalRuntime(): SignalRuntime | null {
275
+ const processGlobal: unknown = Reflect.get(dntShim.dntGlobalThis, "process");
276
+ if (!isObject(processGlobal)) {
277
+ return null;
278
+ }
279
+ const on = processGlobal.on;
280
+ const off = processGlobal.off;
281
+ const removeListener = processGlobal.removeListener;
282
+ const exit = processGlobal.exit;
283
+ if (typeof on !== "function") {
284
+ return null;
285
+ }
286
+ const runtime: SignalRuntime = {
287
+ add: (signal, handler) => {
288
+ Reflect.apply(on, processGlobal, [signal, handler]);
289
+ },
290
+ };
291
+ if (typeof off === "function") {
292
+ runtime.remove = (signal, handler) => {
293
+ Reflect.apply(off, processGlobal, [signal, handler]);
294
+ };
295
+ } else if (typeof removeListener === "function") {
296
+ runtime.remove = (signal, handler) => {
297
+ Reflect.apply(removeListener, processGlobal, [signal, handler]);
298
+ };
299
+ }
300
+ if (typeof exit === "function") {
301
+ runtime.exit = (code) => Reflect.apply(exit, processGlobal, [code]);
302
+ }
303
+ return runtime;
304
+ }
305
+
306
+ function createDenoSignalRuntime(deno: DenoServeRuntime): SignalRuntime | null {
307
+ if (!deno.addSignalListener) {
308
+ return null;
309
+ }
310
+ return {
311
+ add: deno.addSignalListener,
312
+ remove: deno.removeSignalListener,
313
+ exit: deno.exit,
314
+ };
315
+ }
316
+
317
+ function resolveRuntimeKind(): VeryfrontServiceServerRuntimeKind {
318
+ if (getBunServeRuntime()) {
319
+ return "bun";
320
+ }
321
+ if (getDenoServeRuntime()) {
322
+ return "deno";
323
+ }
324
+ return "node";
325
+ }
326
+
327
+ async function stopRuntime(
328
+ runtime: VeryfrontServiceServerRuntime,
329
+ stopServer: () => void | Promise<void>,
330
+ ): Promise<void> {
331
+ runtime.setShuttingDown();
332
+ await stopServer();
333
+ await runtime.stop();
334
+ }
335
+
336
+ function installSignalHandlers(options: {
337
+ signalRuntime: SignalRuntime | null;
338
+ signals?: readonly NodeJS.Signals[];
339
+ logger: VeryfrontServiceServerLogger;
340
+ stop: () => Promise<void>;
341
+ hardShutdownTimeoutMs?: number;
342
+ runtime: VeryfrontServiceServerRuntimeKind;
343
+ }): () => void {
344
+ if (!options.signalRuntime) {
345
+ return () => undefined;
346
+ }
347
+
348
+ const hardShutdownTimeoutMs = options.hardShutdownTimeoutMs ?? 20_000;
349
+ const installedHandlers: Array<{ signal: NodeJS.Signals; handler: SignalHandler }> = [];
350
+ let signalShutdownStarted = false;
351
+
352
+ for (const signal of options.signals ?? ["SIGTERM"]) {
353
+ const handler = () => {
354
+ if (signalShutdownStarted) {
355
+ return;
356
+ }
357
+
358
+ signalShutdownStarted = true;
359
+ options.logger.info?.("Veryfront service server received shutdown signal", {
360
+ signal,
361
+ runtime: options.runtime,
362
+ });
363
+ const hardTimeout = dntShim.setTimeout(() => {
364
+ options.logger.error?.("Veryfront service server graceful shutdown timed out", {
365
+ signal,
366
+ runtime: options.runtime,
367
+ });
368
+ options.signalRuntime?.exit?.(1);
369
+ }, hardShutdownTimeoutMs);
370
+
371
+ void options.stop()
372
+ .then(() => {
373
+ clearTimeout(hardTimeout);
374
+ options.signalRuntime?.exit?.(0);
375
+ })
376
+ .catch((error: unknown) => {
377
+ clearTimeout(hardTimeout);
378
+ options.logger.error?.("Veryfront service server shutdown failed", {
379
+ signal,
380
+ runtime: options.runtime,
381
+ error: error instanceof Error ? error.message : String(error),
382
+ });
383
+ options.signalRuntime?.exit?.(1);
384
+ });
385
+ };
386
+
387
+ try {
388
+ options.signalRuntime.add(signal, handler);
389
+ installedHandlers.push({ signal, handler });
390
+ } catch (error) {
391
+ options.logger.warn?.("Veryfront service server could not install shutdown signal handler", {
392
+ signal,
393
+ runtime: options.runtime,
394
+ error: error instanceof Error ? error.message : String(error),
395
+ });
396
+ }
397
+ }
398
+
399
+ return () => {
400
+ for (const { signal, handler } of installedHandlers) {
401
+ try {
402
+ options.signalRuntime?.remove?.(signal, handler);
403
+ } catch (error) {
404
+ options.logger.warn?.("Veryfront service server could not remove shutdown signal handler", {
405
+ signal,
406
+ runtime: options.runtime,
407
+ error: error instanceof Error ? error.message : String(error),
408
+ });
409
+ }
410
+ }
411
+ };
412
+ }
413
+
414
+ async function startDenoVeryfrontServer(
415
+ options: StartVeryfrontServerOptions,
416
+ deno: DenoServeRuntime,
417
+ ): Promise<VeryfrontServiceServer> {
418
+ const logger = options.logger ?? serverLogger.component("service-server");
419
+ const bindAddress = options.bindAddress ?? "0.0.0.0";
420
+ const abortController = new AbortController();
421
+ const server = deno.serve({
422
+ port: options.port,
423
+ hostname: bindAddress,
424
+ signal: abortController.signal,
425
+ onListen: () => undefined,
426
+ }, options.runtime.fetch);
427
+ let shutdownStarted = false;
428
+ let removeSignalHandlers: () => void = () => undefined;
429
+
430
+ const stop = async () => {
431
+ if (shutdownStarted) {
432
+ return;
433
+ }
434
+
435
+ shutdownStarted = true;
436
+ try {
437
+ await stopRuntime(options.runtime, async () => {
438
+ if (server.shutdown) {
439
+ await server.shutdown();
440
+ return;
441
+ }
442
+ abortController.abort();
443
+ await server.finished?.catch(() => undefined);
444
+ });
445
+ } finally {
446
+ removeSignalHandlers();
447
+ }
448
+ };
449
+
450
+ removeSignalHandlers = installSignalHandlers({
451
+ signalRuntime: createDenoSignalRuntime(deno),
452
+ signals: options.signals,
453
+ logger,
454
+ stop,
455
+ hardShutdownTimeoutMs: options.hardShutdownTimeoutMs,
456
+ runtime: "deno",
457
+ });
458
+
459
+ logger.info?.("Veryfront service server listening", {
460
+ port: server.addr?.port ?? options.port,
461
+ bindAddress,
462
+ runtime: "deno",
463
+ });
464
+
465
+ return {
466
+ ready: Promise.resolve(),
467
+ stop,
468
+ port: server.addr?.port ?? options.port,
469
+ url: `http://${bindAddress}:${server.addr?.port ?? options.port}`,
470
+ runtime: "deno",
471
+ };
472
+ }
473
+
474
+ async function startBunVeryfrontServer(
475
+ options: StartVeryfrontServerOptions,
476
+ bun: BunServeRuntime,
477
+ ): Promise<VeryfrontServiceServer> {
478
+ const logger = options.logger ?? serverLogger.component("service-server");
479
+ const bindAddress = options.bindAddress ?? "0.0.0.0";
480
+ const server = bun.serve({
481
+ port: options.port,
482
+ hostname: bindAddress,
483
+ fetch: options.runtime.fetch,
484
+ });
485
+ let shutdownStarted = false;
486
+ let removeSignalHandlers: () => void = () => undefined;
487
+
488
+ const stop = async () => {
489
+ if (shutdownStarted) {
490
+ return;
491
+ }
492
+
493
+ shutdownStarted = true;
494
+ try {
495
+ await stopRuntime(options.runtime, async () => {
496
+ await server.stop?.();
497
+ });
498
+ } finally {
499
+ removeSignalHandlers();
500
+ }
501
+ };
502
+
503
+ removeSignalHandlers = installSignalHandlers({
504
+ signalRuntime: getProcessSignalRuntime(),
505
+ signals: options.signals,
506
+ logger,
507
+ stop,
508
+ hardShutdownTimeoutMs: options.hardShutdownTimeoutMs,
509
+ runtime: "bun",
510
+ });
511
+
512
+ logger.info?.("Veryfront service server listening", {
513
+ port: server.port ?? options.port,
514
+ bindAddress,
515
+ runtime: "bun",
516
+ });
517
+
518
+ return {
519
+ ready: Promise.resolve(),
520
+ stop,
521
+ port: server.port ?? options.port,
522
+ url: server.url?.toString() ?? `http://${bindAddress}:${options.port}`,
523
+ runtime: "bun",
524
+ };
525
+ }
526
+
527
+ export async function startVeryfrontServer(
528
+ options: StartVeryfrontServerOptions,
529
+ ): Promise<VeryfrontServiceServer | NodeVeryfrontServiceServer> {
530
+ const runtimeKind = resolveRuntimeKind();
531
+ if (runtimeKind === "bun") {
532
+ const bun = getBunServeRuntime();
533
+ if (bun) {
534
+ return await startBunVeryfrontServer(options, bun);
535
+ }
536
+ }
537
+ if (runtimeKind === "deno") {
538
+ const deno = getDenoServeRuntime();
539
+ if (deno) {
540
+ return await startDenoVeryfrontServer(options, deno);
541
+ }
542
+ }
543
+ return await startNodeVeryfrontServer(options);
544
+ }
545
+
119
546
  export async function startNodeVeryfrontServer(
120
547
  options: StartNodeVeryfrontServerOptions,
121
548
  ): Promise<NodeVeryfrontServiceServer> {
@@ -183,5 +610,6 @@ export async function startNodeVeryfrontServer(
183
610
  stop,
184
611
  port: options.port,
185
612
  url: `http://${bindAddress}:${options.port}`,
613
+ runtime: "node",
186
614
  };
187
615
  }
@@ -1,3 +1,3 @@
1
1
  // Keep in sync with deno.json version.
2
2
  // scripts/release.ts updates this constant during releases.
3
- export const VERSION = "0.1.498";
3
+ export const VERSION = "0.1.500";