@@ -45,15 +45,33 @@ import * as Semaphore from "effect/Semaphore";
4545import { writeFileStringAtomically } from "./atomicWrite.ts" ;
4646import { ServerConfig } from "./config.ts" ;
4747import { type DeepPartial , deepMerge } from "@t3tools/shared/Struct" ;
48- import { fromLenientJson } from "@t3tools/shared/schemaJson" ;
48+ import { fromJsonStringPretty , fromLenientJson } from "@t3tools/shared/schemaJson" ;
4949import { applyServerSettingsPatch } from "@t3tools/shared/serverSettings" ;
5050import { ServerSecretStoreLive } from "./auth/Layers/ServerSecretStore.ts" ;
5151import { ServerSecretStore } from "./auth/Services/ServerSecretStore.ts" ;
52- const decodeServerSettings = Schema . decodeEffect ( ServerSettings ) ;
52+
53+ const encodeServerSettings = Schema . encodeEffect ( ServerSettings ) ;
54+ const encodeServerSettingsJson = Schema . encodeUnknownEffect ( fromJsonStringPretty ( ServerSettings ) ) ;
55+ const decodeServerSettings = Schema . decodeUnknownEffect ( ServerSettings ) ;
5356
5457const textEncoder = new TextEncoder ( ) ;
5558const textDecoder = new TextDecoder ( ) ;
5659
60+ const normalizeServerSettings = (
61+ settings : ServerSettings ,
62+ ) : Effect . Effect < ServerSettings , ServerSettingsError > =>
63+ encodeServerSettings ( settings ) . pipe (
64+ Effect . flatMap ( decodeServerSettings ) ,
65+ Effect . mapError (
66+ ( cause ) =>
67+ new ServerSettingsError ( {
68+ settingsPath : "<memory>" ,
69+ detail : `failed to normalize server settings: ${ SchemaIssue . makeFormatterDefault ( ) ( cause . issue ) } ` ,
70+ cause,
71+ } ) ,
72+ ) ,
73+ ) ;
74+
5775function providerEnvironmentSecretName ( input : {
5876 readonly instanceId : string ;
5977 readonly name : string ;
@@ -117,28 +135,24 @@ export class ServerSettingsService extends Context.Service<
117135 Layer . effect (
118136 ServerSettingsService ,
119137 Effect . gen ( function * ( ) {
120- const currentSettingsRef = yield * Ref . make < ServerSettings > (
121- deepMerge ( DEFAULT_SERVER_SETTINGS , overrides ) ,
122- ) ;
138+ const { automaticGitFetchInterval, ...overridesForMerge } = overrides ;
139+ const merged = deepMerge ( DEFAULT_SERVER_SETTINGS , overridesForMerge ) ;
140+ const initialSettings = yield * normalizeServerSettings ( {
141+ ...merged ,
142+ ...( automaticGitFetchInterval !== undefined
143+ ? { automaticGitFetchInterval : automaticGitFetchInterval as Duration . Duration }
144+ : { } ) ,
145+ } ) ;
146+ const currentSettingsRef = yield * Ref . make < ServerSettings > ( initialSettings ) ;
123147
124148 return {
125149 start : Effect . void ,
126150 ready : Effect . void ,
127151 getSettings : Ref . get ( currentSettingsRef ) ,
128152 updateSettings : ( patch ) =>
129153 Ref . get ( currentSettingsRef ) . pipe (
130- Effect . flatMap ( ( currentSettings ) =>
131- decodeServerSettings ( applyServerSettingsPatch ( currentSettings , patch ) ) . pipe (
132- Effect . mapError (
133- ( cause ) =>
134- new ServerSettingsError ( {
135- settingsPath : "<memory>" ,
136- detail : `failed to normalize server settings: ${ SchemaIssue . makeFormatterDefault ( ) ( cause . issue ) } ` ,
137- cause,
138- } ) ,
139- ) ,
140- ) ,
141- ) ,
154+ Effect . map ( ( currentSettings ) => applyServerSettingsPatch ( currentSettings , patch ) ) ,
155+ Effect . flatMap ( normalizeServerSettings ) ,
142156 Effect . tap ( ( nextSettings ) => Ref . set ( currentSettingsRef , nextSettings ) ) ,
143157 ) ,
144158 streamChanges : Stream . empty ,
@@ -200,7 +214,10 @@ function fallbackTextGenerationProvider(settings: ServerSettings): ServerSetting
200214}
201215
202216// Values under these keys are compared as a whole — never stripped field-by-field.
203- const ATOMIC_SETTINGS_KEYS : ReadonlySet < string > = new Set ( [ "textGenerationModelSelection" ] ) ;
217+ const ATOMIC_SETTINGS_KEYS : ReadonlySet < string > = new Set ( [
218+ "automaticGitFetchInterval" ,
219+ "textGenerationModelSelection" ,
220+ ] ) ;
204221
205222function stripDefaultServerSettings ( current : unknown , defaults : unknown ) : unknown | undefined {
206223 if ( Array . isArray ( current ) || Array . isArray ( defaults ) ) {
@@ -430,25 +447,29 @@ const makeServerSettings = Effect.gen(function* () {
430447 } ;
431448 } ) ;
432449
433- const writeSettingsAtomically = ( settings : ServerSettings ) => {
434- const sparseSettings = stripDefaultServerSettings ( settings , DEFAULT_SERVER_SETTINGS ) ?? { } ;
450+ const writeSettingsAtomically = Effect . fnUntraced (
451+ function * ( settings : ServerSettings ) {
452+ const sparseSettingsJson = yield * encodeServerSettingsJson (
453+ stripDefaultServerSettings ( settings , DEFAULT_SERVER_SETTINGS ) ?? { } ,
454+ ) ;
435455
436- return writeFileStringAtomically ( {
437- filePath : settingsPath ,
438- contents : `${ JSON . stringify ( sparseSettings , null , 2 ) } \n` ,
439- } ) . pipe (
440- Effect . provideService ( FileSystem . FileSystem , fs ) ,
441- Effect . provideService ( Path . Path , pathService ) ,
442- Effect . mapError (
443- ( cause ) =>
444- new ServerSettingsError ( {
445- settingsPath,
446- detail : "failed to write settings file" ,
447- cause,
448- } ) ,
449- ) ,
450- ) ;
451- } ;
456+ return yield * writeFileStringAtomically ( {
457+ filePath : settingsPath ,
458+ contents : `${ sparseSettingsJson } \n` ,
459+ } ) . pipe (
460+ Effect . provideService ( FileSystem . FileSystem , fs ) ,
461+ Effect . provideService ( Path . Path , pathService ) ,
462+ ) ;
463+ } ,
464+ Effect . mapError (
465+ ( cause ) =>
466+ new ServerSettingsError ( {
467+ settingsPath,
468+ detail : "failed to write settings file" ,
469+ cause,
470+ } ) ,
471+ ) ,
472+ ) ;
452473
453474 const revalidateAndEmit = writeSemaphore . withPermits ( 1 ) (
454475 Effect . gen ( function * ( ) {
@@ -533,16 +554,7 @@ const makeServerSettings = Effect.gen(function* () {
533554 current ,
534555 applyServerSettingsPatch ( current , patch ) ,
535556 ) ;
536- const next = yield * decodeServerSettings ( nextPersisted ) . pipe (
537- Effect . mapError (
538- ( cause ) =>
539- new ServerSettingsError ( {
540- settingsPath : "<memory>" ,
541- detail : `failed to normalize server settings: ${ SchemaIssue . makeFormatterDefault ( ) ( cause . issue ) } ` ,
542- cause,
543- } ) ,
544- ) ,
545- ) ;
557+ const next = yield * normalizeServerSettings ( nextPersisted ) ;
546558 yield * writeSettingsAtomically ( next ) ;
547559 yield * Cache . set ( settingsCache , cacheKey , next ) ;
548560 yield * emitChange ( next ) ;
0 commit comments