veryfront 0.1.403 → 0.1.404
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.
- package/esm/cli/commands/install/registry.d.ts.map +1 -1
- package/esm/cli/commands/install/registry.js +9 -3
- package/esm/cli/templates/manifest.d.ts +30 -0
- package/esm/cli/templates/manifest.js +30 -0
- package/esm/deno.js +1 -1
- package/esm/src/agent/hosted-child-status.d.ts +5 -0
- package/esm/src/agent/hosted-child-status.d.ts.map +1 -1
- package/esm/src/agent/hosted-child-status.js +14 -0
- package/esm/src/config/schemas/config.schema.d.ts +18 -0
- package/esm/src/config/schemas/config.schema.d.ts.map +1 -1
- package/esm/src/config/schemas/config.schema.js +16 -0
- package/esm/src/observability/file-log-subscriber.d.ts +32 -0
- package/esm/src/observability/file-log-subscriber.d.ts.map +1 -0
- package/esm/src/observability/file-log-subscriber.js +163 -0
- package/esm/src/observability/index.d.ts +1 -0
- package/esm/src/observability/index.d.ts.map +1 -1
- package/esm/src/observability/index.js +1 -0
- package/esm/src/server/bootstrap.d.ts.map +1 -1
- package/esm/src/server/bootstrap.js +149 -97
- package/esm/src/utils/version-constant.d.ts +1 -1
- package/esm/src/utils/version-constant.js +1 -1
- package/package.json +1 -1
- package/src/cli/commands/install/registry.ts +10 -3
- package/src/cli/templates/manifest.js +30 -0
- package/src/deno.js +1 -1
- package/src/src/agent/hosted-child-status.ts +27 -0
- package/src/src/config/schemas/config.schema.ts +16 -0
- package/src/src/observability/file-log-subscriber.ts +187 -0
- package/src/src/observability/index.ts +7 -0
- package/src/src/server/bootstrap.ts +184 -119
- package/src/src/utils/version-constant.ts +1 -1
|
@@ -33,6 +33,12 @@ import {
|
|
|
33
33
|
markEnvLoaded,
|
|
34
34
|
supportsEnvFiles,
|
|
35
35
|
} from "../utils/env-loader.js";
|
|
36
|
+
import { getLogBuffer } from "../observability/log-buffer.js";
|
|
37
|
+
import {
|
|
38
|
+
createFileLogSubscriber,
|
|
39
|
+
type FileLogConfig,
|
|
40
|
+
type FileLogSubscriber,
|
|
41
|
+
} from "../observability/file-log-subscriber.js";
|
|
36
42
|
import { ReloadNotifier } from "./reload-notifier.js";
|
|
37
43
|
import { clearDomainCache } from "./utils/domain-lookup.js";
|
|
38
44
|
|
|
@@ -119,15 +125,60 @@ function wireTracingShim(): void {
|
|
|
119
125
|
}
|
|
120
126
|
}
|
|
121
127
|
|
|
128
|
+
const DEFAULT_FILE_LOG_PATH = ".veryfront/logs/server.log";
|
|
129
|
+
const DEFAULT_FILE_LOG_MAX_SIZE = "10mb";
|
|
130
|
+
const DEFAULT_FILE_LOG_MAX_FILES = 5;
|
|
131
|
+
const DEFAULT_FILE_LOG_LEVEL = "warn" as const;
|
|
132
|
+
const DEFAULT_FILE_LOG_FORMAT = "json" as const;
|
|
133
|
+
|
|
134
|
+
interface FileLogHandle {
|
|
135
|
+
subscriber: FileLogSubscriber;
|
|
136
|
+
unsubscribe: () => void;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function maybeAttachFileLogSubscriber(config: VeryfrontConfig): FileLogHandle | null {
|
|
140
|
+
const fileConfig = config.observability?.logging?.file;
|
|
141
|
+
if (!fileConfig?.enabled) return null;
|
|
142
|
+
|
|
143
|
+
const resolved: FileLogConfig = {
|
|
144
|
+
enabled: true,
|
|
145
|
+
path: fileConfig.path ?? DEFAULT_FILE_LOG_PATH,
|
|
146
|
+
maxSize: fileConfig.maxSize ?? DEFAULT_FILE_LOG_MAX_SIZE,
|
|
147
|
+
maxFiles: fileConfig.maxFiles ?? DEFAULT_FILE_LOG_MAX_FILES,
|
|
148
|
+
level: fileConfig.level ?? DEFAULT_FILE_LOG_LEVEL,
|
|
149
|
+
format: fileConfig.format ?? DEFAULT_FILE_LOG_FORMAT,
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
const subscriber = createFileLogSubscriber(resolved);
|
|
153
|
+
const unsubscribe = getLogBuffer().subscribe(subscriber.getSubscriber());
|
|
154
|
+
bootstrapLog.debug("[bootstrap] File log subscriber attached", {
|
|
155
|
+
path: resolved.path,
|
|
156
|
+
level: resolved.level,
|
|
157
|
+
format: resolved.format,
|
|
158
|
+
});
|
|
159
|
+
return { subscriber, unsubscribe };
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
async function teardownFileLog(handle: FileLogHandle | null): Promise<void> {
|
|
163
|
+
if (!handle) return;
|
|
164
|
+
handle.unsubscribe();
|
|
165
|
+
await handle.subscriber.close();
|
|
166
|
+
}
|
|
167
|
+
|
|
122
168
|
function combineDispose(
|
|
123
169
|
extensionLoader: ExtensionLoader,
|
|
124
170
|
fsDispose?: () => void,
|
|
171
|
+
fileLogHandle?: FileLogHandle | null,
|
|
125
172
|
): () => Promise<void> {
|
|
126
173
|
return async () => {
|
|
127
174
|
try {
|
|
128
175
|
await extensionLoader.teardownAll();
|
|
129
176
|
} finally {
|
|
130
|
-
|
|
177
|
+
try {
|
|
178
|
+
await teardownFileLog(fileLogHandle ?? null);
|
|
179
|
+
} finally {
|
|
180
|
+
if (fsDispose) fsDispose();
|
|
181
|
+
}
|
|
131
182
|
}
|
|
132
183
|
};
|
|
133
184
|
}
|
|
@@ -218,141 +269,155 @@ export async function bootstrap(
|
|
|
218
269
|
bootstrapLog.debug("Loading config with base adapter");
|
|
219
270
|
let config = await getConfig(projectDir, adapter);
|
|
220
271
|
|
|
221
|
-
|
|
222
|
-
const needsFSAdapter = fsType != null && fsType !== "local";
|
|
272
|
+
let fileLog = maybeAttachFileLogSubscriber(config);
|
|
223
273
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
const
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
274
|
+
try {
|
|
275
|
+
const fsType = config.fs?.type;
|
|
276
|
+
const needsFSAdapter = fsType != null && fsType !== "local";
|
|
277
|
+
|
|
278
|
+
if (!needsFSAdapter) {
|
|
279
|
+
bootstrapLog.debug("Using local filesystem (no FSAdapter needed)");
|
|
280
|
+
const extensionLoader = await orchestrateExtensions({
|
|
281
|
+
projectDir,
|
|
282
|
+
config,
|
|
283
|
+
logger: bootstrapLog,
|
|
284
|
+
primeContracts: { [AIProviderRegistryName]: createAIProviderRegistry() },
|
|
285
|
+
builtinExtensions: createBuiltinExtensions(),
|
|
286
|
+
});
|
|
287
|
+
wireTracingShim();
|
|
288
|
+
assertRequiredContracts();
|
|
289
|
+
return {
|
|
290
|
+
adapter,
|
|
291
|
+
config,
|
|
292
|
+
usingFSAdapter: false,
|
|
293
|
+
extensionLoader,
|
|
294
|
+
dispose: combineDispose(extensionLoader, undefined, fileLog),
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
bootstrapLog.debug("Initializing FSAdapter", { type: fsType });
|
|
299
|
+
|
|
300
|
+
// Inject server-layer callbacks into FS config so the platform layer
|
|
301
|
+
// doesn't need to import from the server layer
|
|
302
|
+
const fsWithCallbacks = {
|
|
303
|
+
...config.fs,
|
|
304
|
+
invalidationCallbacks: {
|
|
305
|
+
triggerReload: (changedPaths?: string[], project?: InvalidationProjectContext) =>
|
|
306
|
+
ReloadNotifier.triggerReload(changedPaths, project),
|
|
307
|
+
clearDomainCache,
|
|
308
|
+
},
|
|
241
309
|
};
|
|
242
|
-
}
|
|
243
310
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
...config.fs,
|
|
250
|
-
invalidationCallbacks: {
|
|
251
|
-
triggerReload: (changedPaths?: string[], project?: InvalidationProjectContext) =>
|
|
252
|
-
ReloadNotifier.triggerReload(changedPaths, project),
|
|
253
|
-
clearDomainCache,
|
|
254
|
-
},
|
|
255
|
-
};
|
|
311
|
+
const enhancedAdapter = await enhanceAdapterWithFS(
|
|
312
|
+
adapter,
|
|
313
|
+
{ ...config, fs: fsWithCallbacks },
|
|
314
|
+
projectDir,
|
|
315
|
+
);
|
|
256
316
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
317
|
+
if (enhancedAdapter === adapter) {
|
|
318
|
+
bootstrapLog.debug("Framework initialized successfully", {
|
|
319
|
+
projectDir,
|
|
320
|
+
runtime: adapter.id,
|
|
321
|
+
fsAdapter: "local",
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
const extensionLoader = await orchestrateExtensions({
|
|
325
|
+
projectDir,
|
|
326
|
+
config,
|
|
327
|
+
logger: bootstrapLog,
|
|
328
|
+
primeContracts: { [AIProviderRegistryName]: createAIProviderRegistry() },
|
|
329
|
+
builtinExtensions: createBuiltinExtensions(),
|
|
330
|
+
});
|
|
331
|
+
wireTracingShim();
|
|
332
|
+
assertRequiredContracts();
|
|
333
|
+
return {
|
|
334
|
+
adapter,
|
|
335
|
+
config,
|
|
336
|
+
usingFSAdapter: false,
|
|
337
|
+
extensionLoader,
|
|
338
|
+
dispose: combineDispose(extensionLoader, undefined, fileLog),
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const isProxyMode = config.fs?.veryfront?.proxyMode === true;
|
|
343
|
+
const isProductionMode = config.fs?.veryfront?.productionMode === true;
|
|
344
|
+
|
|
345
|
+
if (isProxyMode) {
|
|
346
|
+
bootstrapLog.debug("Skipping config reload in proxy mode (using local config)");
|
|
347
|
+
} else if (isProductionMode) {
|
|
348
|
+
bootstrapLog.debug("Skipping config reload in production mode (using local config)");
|
|
349
|
+
} else {
|
|
350
|
+
bootstrapLog.debug("Reloading config with FSAdapter");
|
|
351
|
+
clearConfigCache();
|
|
352
|
+
|
|
353
|
+
const originalConfig = config;
|
|
354
|
+
const reloadedConfig = await getConfig(projectDir, enhancedAdapter);
|
|
355
|
+
|
|
356
|
+
const usesDefaultDevConfig = reloadedConfig.dev?.port === 3000 &&
|
|
357
|
+
reloadedConfig.dev?.host === "localhost" &&
|
|
358
|
+
!reloadedConfig.dev?.hmr;
|
|
359
|
+
|
|
360
|
+
if (usesDefaultDevConfig && originalConfig.dev) {
|
|
361
|
+
bootstrapLog.debug("Keeping original config (FSAdapter returned defaults)");
|
|
362
|
+
config = originalConfig;
|
|
363
|
+
} else {
|
|
364
|
+
config = reloadedConfig;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// Re-attach file log subscriber if config was reloaded with different settings
|
|
368
|
+
const newFileLog = maybeAttachFileLogSubscriber(config);
|
|
369
|
+
if (newFileLog) {
|
|
370
|
+
await teardownFileLog(fileLog);
|
|
371
|
+
fileLog = newFileLog;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
262
374
|
|
|
263
|
-
if (enhancedAdapter === adapter) {
|
|
264
375
|
bootstrapLog.debug("Framework initialized successfully", {
|
|
265
376
|
projectDir,
|
|
266
377
|
runtime: adapter.id,
|
|
267
|
-
fsAdapter:
|
|
378
|
+
fsAdapter: fsType,
|
|
268
379
|
});
|
|
269
380
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
381
|
+
let fsDispose: (() => void) | undefined;
|
|
382
|
+
if (isExtendedFSAdapter(enhancedAdapter.fs)) {
|
|
383
|
+
const underlying = enhancedAdapter.fs.getUnderlyingAdapter();
|
|
384
|
+
if (
|
|
385
|
+
"dispose" in underlying &&
|
|
386
|
+
typeof (underlying as { dispose?: () => void }).dispose === "function"
|
|
387
|
+
) {
|
|
388
|
+
fsDispose = () => (underlying as { dispose: () => void }).dispose();
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// If extension orchestration fails after the FS adapter has been wired up,
|
|
393
|
+
// release the FS resources (WebSocket connections, caches) before
|
|
394
|
+
// propagating the error — otherwise the adapter would leak.
|
|
395
|
+
const extensionLoader = await orchestrateOrDisposeFS(
|
|
396
|
+
() =>
|
|
397
|
+
orchestrateExtensions({
|
|
398
|
+
projectDir,
|
|
399
|
+
config,
|
|
400
|
+
logger: bootstrapLog,
|
|
401
|
+
primeContracts: { [AIProviderRegistryName]: createAIProviderRegistry() },
|
|
402
|
+
builtinExtensions: createBuiltinExtensions(),
|
|
403
|
+
}),
|
|
404
|
+
fsDispose,
|
|
405
|
+
);
|
|
277
406
|
wireTracingShim();
|
|
278
407
|
assertRequiredContracts();
|
|
408
|
+
|
|
279
409
|
return {
|
|
280
|
-
adapter,
|
|
410
|
+
adapter: enhancedAdapter,
|
|
281
411
|
config,
|
|
282
|
-
usingFSAdapter:
|
|
412
|
+
usingFSAdapter: true,
|
|
413
|
+
fsAdapterType: fsType,
|
|
283
414
|
extensionLoader,
|
|
284
|
-
dispose: combineDispose(extensionLoader),
|
|
415
|
+
dispose: combineDispose(extensionLoader, fsDispose, fileLog),
|
|
285
416
|
};
|
|
417
|
+
} catch (err) {
|
|
418
|
+
await teardownFileLog(fileLog);
|
|
419
|
+
throw err;
|
|
286
420
|
}
|
|
287
|
-
|
|
288
|
-
const isProxyMode = config.fs?.veryfront?.proxyMode === true;
|
|
289
|
-
const isProductionMode = config.fs?.veryfront?.productionMode === true;
|
|
290
|
-
|
|
291
|
-
if (isProxyMode) {
|
|
292
|
-
bootstrapLog.debug("Skipping config reload in proxy mode (using local config)");
|
|
293
|
-
} else if (isProductionMode) {
|
|
294
|
-
bootstrapLog.debug("Skipping config reload in production mode (using local config)");
|
|
295
|
-
} else {
|
|
296
|
-
bootstrapLog.debug("Reloading config with FSAdapter");
|
|
297
|
-
clearConfigCache();
|
|
298
|
-
|
|
299
|
-
const originalConfig = config;
|
|
300
|
-
const reloadedConfig = await getConfig(projectDir, enhancedAdapter);
|
|
301
|
-
|
|
302
|
-
const usesDefaultDevConfig = reloadedConfig.dev?.port === 3000 &&
|
|
303
|
-
reloadedConfig.dev?.host === "localhost" &&
|
|
304
|
-
!reloadedConfig.dev?.hmr;
|
|
305
|
-
|
|
306
|
-
if (usesDefaultDevConfig && originalConfig.dev) {
|
|
307
|
-
bootstrapLog.debug("Keeping original config (FSAdapter returned defaults)");
|
|
308
|
-
config = originalConfig;
|
|
309
|
-
} else {
|
|
310
|
-
config = reloadedConfig;
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
bootstrapLog.debug("Framework initialized successfully", {
|
|
315
|
-
projectDir,
|
|
316
|
-
runtime: adapter.id,
|
|
317
|
-
fsAdapter: fsType,
|
|
318
|
-
});
|
|
319
|
-
|
|
320
|
-
let fsDispose: (() => void) | undefined;
|
|
321
|
-
if (isExtendedFSAdapter(enhancedAdapter.fs)) {
|
|
322
|
-
const underlying = enhancedAdapter.fs.getUnderlyingAdapter();
|
|
323
|
-
if (
|
|
324
|
-
"dispose" in underlying &&
|
|
325
|
-
typeof (underlying as { dispose?: () => void }).dispose === "function"
|
|
326
|
-
) {
|
|
327
|
-
fsDispose = () => (underlying as { dispose: () => void }).dispose();
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
// If extension orchestration fails after the FS adapter has been wired up,
|
|
332
|
-
// release the FS resources (WebSocket connections, caches) before
|
|
333
|
-
// propagating the error — otherwise the adapter would leak.
|
|
334
|
-
const extensionLoader = await orchestrateOrDisposeFS(
|
|
335
|
-
() =>
|
|
336
|
-
orchestrateExtensions({
|
|
337
|
-
projectDir,
|
|
338
|
-
config,
|
|
339
|
-
logger: bootstrapLog,
|
|
340
|
-
primeContracts: { [AIProviderRegistryName]: createAIProviderRegistry() },
|
|
341
|
-
builtinExtensions: createBuiltinExtensions(),
|
|
342
|
-
}),
|
|
343
|
-
fsDispose,
|
|
344
|
-
);
|
|
345
|
-
wireTracingShim();
|
|
346
|
-
assertRequiredContracts();
|
|
347
|
-
|
|
348
|
-
return {
|
|
349
|
-
adapter: enhancedAdapter,
|
|
350
|
-
config,
|
|
351
|
-
usingFSAdapter: true,
|
|
352
|
-
fsAdapterType: fsType,
|
|
353
|
-
extensionLoader,
|
|
354
|
-
dispose: combineDispose(extensionLoader, fsDispose),
|
|
355
|
-
};
|
|
356
421
|
}
|
|
357
422
|
|
|
358
423
|
export async function bootstrapDev(
|