| author | |
| committer | |
| log | ecfdda5fa804582066c1aa62afea419677c24883 |
| tree | f55f08cf47c81cfa55d716e05a210c5534e2905a |
| parent | 4df84a7d03ff084ff4c6dd7aef54623984690fb7 |
| signature | Signed by PGP key B5690EEEBB952194 |
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>401 files changed, 7288 insertions(+), 7368 deletions(-)
apps/desktop/src/app/DesktopAppIdentity.test.ts+6-6| ... | @@ -38,7 +38,7 @@ interface ElectronAppCalls { | ... | @@ -38,7 +38,7 @@ interface ElectronAppCalls { |
| 38 | readonly setName: string[]; | 38 | readonly setName: string[]; |
| 39 | } | 39 | } |
| 40 | 40 | ||
| 41 | const makeElectronAppLayer = (calls: ElectronAppCalls) => | 41 | const layerElectronApp = (calls: ElectronAppCalls) => |
| 42 | Layer.succeed(ElectronApp.ElectronApp, { | 42 | Layer.succeed(ElectronApp.ElectronApp, { |
| 43 | metadata: Effect.die("unexpected metadata read"), | 43 | metadata: Effect.die("unexpected metadata read"), |
| 44 | name: Effect.succeed("T3 Code"), | 44 | name: Effect.succeed("T3 Code"), |
| ... | @@ -70,7 +70,7 @@ const makeElectronAppLayer = (calls: ElectronAppCalls) => | ... | @@ -70,7 +70,7 @@ const makeElectronAppLayer = (calls: ElectronAppCalls) => |
| 70 | on: () => Effect.void, | 70 | on: () => Effect.void, |
| 71 | } satisfies ElectronApp.ElectronApp["Service"]); | 71 | } satisfies ElectronApp.ElectronApp["Service"]); |
| 72 | 72 | ||
| 73 | const makeAssetsLayer = (png: Option.Option<string>) => | 73 | const layerAssets = (png: Option.Option<string>) => |
| 74 | Layer.succeed(DesktopAssets.DesktopAssets, { | 74 | Layer.succeed(DesktopAssets.DesktopAssets, { |
| 75 | iconPaths: Effect.succeed({ | 75 | iconPaths: Effect.succeed({ |
| 76 | ico: Option.none(), | 76 | ico: Option.none(), |
| ... | @@ -80,7 +80,7 @@ const makeAssetsLayer = (png: Option.Option<string>) => | ... | @@ -80,7 +80,7 @@ const makeAssetsLayer = (png: Option.Option<string>) => |
| 80 | resolveResourcePath: () => Effect.succeedNone, | 80 | resolveResourcePath: () => Effect.succeedNone, |
| 81 | } satisfies DesktopAssets.DesktopAssets["Service"]); | 81 | } satisfies DesktopAssets.DesktopAssets["Service"]); |
| 82 | 82 | ||
| 83 | const makeEnvironmentLayer = (overrides: TestEnvironmentInput = {}) => { | 83 | const layerEnvironment = (overrides: TestEnvironmentInput = {}) => { |
| 84 | const { env, ...environmentOverrides } = overrides; | 84 | const { env, ...environmentOverrides } = overrides; |
| 85 | return DesktopEnvironment.layer({ | 85 | return DesktopEnvironment.layer({ |
| 86 | ...defaultEnvironmentInput, | 86 | ...defaultEnvironmentInput, |
| ... | @@ -138,9 +138,9 @@ const withIdentity = <A, E, R>( | ... | @@ -138,9 +138,9 @@ const withIdentity = <A, E, R>( |
| 138 | Effect.succeed(input.packageJson ?? '{"t3codeCommitHash":"abcdef1234567890"}'), | 138 | Effect.succeed(input.packageJson ?? '{"t3codeCommitHash":"abcdef1234567890"}'), |
| 139 | }), | 139 | }), |
| 140 | ), | 140 | ), |
| 141 | Layer.provideMerge(makeAssetsLayer(input.pngIconPath ?? Option.none())), | 141 | Layer.provideMerge(layerAssets(input.pngIconPath ?? Option.none())), |
| 142 | Layer.provideMerge(makeElectronAppLayer(calls)), | 142 | Layer.provideMerge(layerElectronApp(calls)), |
| 143 | Layer.provideMerge(makeEnvironmentLayer(input.environment)), | 143 | Layer.provideMerge(layerEnvironment(input.environment)), |
| 144 | ), | 144 | ), |
| 145 | ), | 145 | ), |
| 146 | ); | 146 | ); |
apps/desktop/src/app/DesktopAssets.test.ts+8-8| ... | @@ -11,7 +11,7 @@ import * as DesktopAssets from "./DesktopAssets.ts"; | ... | @@ -11,7 +11,7 @@ import * as DesktopAssets from "./DesktopAssets.ts"; |
| 11 | import * as DesktopConfig from "./DesktopConfig.ts"; | 11 | import * as DesktopConfig from "./DesktopConfig.ts"; |
| 12 | import * as DesktopEnvironment from "./DesktopEnvironment.ts"; | 12 | import * as DesktopEnvironment from "./DesktopEnvironment.ts"; |
| 13 | 13 | ||
| 14 | const environmentLayer = DesktopEnvironment.layer({ | 14 | const layerEnvironment = DesktopEnvironment.layer({ |
| 15 | dirname: "/repo/apps/desktop/dist-electron", | 15 | dirname: "/repo/apps/desktop/dist-electron", |
| 16 | homeDirectory: "/Users/alice", | 16 | homeDirectory: "/Users/alice", |
| 17 | platform: "darwin", | 17 | platform: "darwin", |
| ... | @@ -30,7 +30,7 @@ const environmentLayer = DesktopEnvironment.layer({ | ... | @@ -30,7 +30,7 @@ const environmentLayer = DesktopEnvironment.layer({ |
| 30 | describe("DesktopAssets", () => { | 30 | describe("DesktopAssets", () => { |
| 31 | it.effect("uses canonical source-tree icons for unpackaged development", () => | 31 | it.effect("uses canonical source-tree icons for unpackaged development", () => |
| 32 | Effect.gen(function* () { | 32 | Effect.gen(function* () { |
| 33 | const developmentEnvironmentLayer = DesktopEnvironment.layer({ | 33 | const layerDevelopmentEnvironment = DesktopEnvironment.layer({ |
| 34 | dirname: "/repo/apps/desktop/dist-electron", | 34 | dirname: "/repo/apps/desktop/dist-electron", |
| 35 | homeDirectory: "/Users/alice", | 35 | homeDirectory: "/Users/alice", |
| 36 | platform: "linux", | 36 | platform: "linux", |
| ... | @@ -49,13 +49,13 @@ describe("DesktopAssets", () => { | ... | @@ -49,13 +49,13 @@ describe("DesktopAssets", () => { |
| 49 | ), | 49 | ), |
| 50 | ), | 50 | ), |
| 51 | ); | 51 | ); |
| 52 | const fileSystemLayer = FileSystem.layerNoop({ | 52 | const layerFileSystem = FileSystem.layerNoop({ |
| 53 | exists: (path) => Effect.succeed(String(path).includes("/assets/dev/")), | 53 | exists: (path) => Effect.succeed(String(path).includes("/assets/dev/")), |
| 54 | }); | 54 | }); |
| 55 | const assets = yield* DesktopAssets.DesktopAssets.pipe( | 55 | const assets = yield* DesktopAssets.DesktopAssets.pipe( |
| 56 | Effect.provide( | 56 | Effect.provide( |
| 57 | DesktopAssets.layer.pipe( | 57 | DesktopAssets.layer.pipe( |
| 58 | Layer.provide(Layer.merge(fileSystemLayer, developmentEnvironmentLayer)), | 58 | Layer.provide(Layer.merge(layerFileSystem, layerDevelopmentEnvironment)), |
| 59 | ), | 59 | ), |
| 60 | ), | 60 | ), |
| 61 | ); | 61 | ); |
| ... | @@ -79,13 +79,13 @@ describe("DesktopAssets", () => { | ... | @@ -79,13 +79,13 @@ describe("DesktopAssets", () => { |
| 79 | pathOrDescriptor: candidatePath, | 79 | pathOrDescriptor: candidatePath, |
| 80 | description: "private filesystem diagnostic", | 80 | description: "private filesystem diagnostic", |
| 81 | }); | 81 | }); |
| 82 | const fileSystemLayer = FileSystem.layerNoop({ | 82 | const layerFileSystem = FileSystem.layerNoop({ |
| 83 | exists: (path) => (path === candidatePath ? Effect.fail(cause) : Effect.succeed(false)), | 83 | exists: (path) => (path === candidatePath ? Effect.fail(cause) : Effect.succeed(false)), |
| 84 | }); | 84 | }); |
| 85 | const assetsLayer = DesktopAssets.layer.pipe( | 85 | const layerAssets = DesktopAssets.layer.pipe( |
| 86 | Layer.provide(Layer.merge(fileSystemLayer, environmentLayer)), | 86 | Layer.provide(Layer.merge(layerFileSystem, layerEnvironment)), |
| 87 | ); | 87 | ); |
| 88 | const assets = yield* DesktopAssets.DesktopAssets.pipe(Effect.provide(assetsLayer)); | 88 | const assets = yield* DesktopAssets.DesktopAssets.pipe(Effect.provide(layerAssets)); |
| 89 | 89 | ||
| 90 | const error = yield* assets.resolveResourcePath(fileName).pipe(Effect.flip); | 90 | const error = yield* assets.resolveResourcePath(fileName).pipe(Effect.flip); |
| 91 | 91 |
apps/desktop/src/app/DesktopClerk.test.ts+9-9| ... | @@ -38,7 +38,7 @@ import * as DesktopClerk from "./DesktopClerk.ts"; | ... | @@ -38,7 +38,7 @@ import * as DesktopClerk from "./DesktopClerk.ts"; |
| 38 | import * as DesktopEnvironment from "./DesktopEnvironment.ts"; | 38 | import * as DesktopEnvironment from "./DesktopEnvironment.ts"; |
| 39 | import * as DesktopPreReadyFileSystem from "./DesktopPreReadyFileSystem.ts"; | 39 | import * as DesktopPreReadyFileSystem from "./DesktopPreReadyFileSystem.ts"; |
| 40 | 40 | ||
| 41 | const makeDesktopClerkLayer = ( | 41 | const layerDesktopClerk = ( |
| 42 | isDevelopment = true, | 42 | isDevelopment = true, |
| 43 | events: string[] = [], | 43 | events: string[] = [], |
| 44 | platform: NodeJS.Platform = "darwin", | 44 | platform: NodeJS.Platform = "darwin", |
| ... | @@ -94,7 +94,7 @@ describe("DesktopClerk", () => { | ... | @@ -94,7 +94,7 @@ describe("DesktopClerk", () => { |
| 94 | }); | 94 | }); |
| 95 | 95 | ||
| 96 | return Effect.gen(function* () { | 96 | return Effect.gen(function* () { |
| 97 | yield* Effect.scoped(Layer.build(makeDesktopClerkLayer(true, events))); | 97 | yield* Effect.scoped(Layer.build(layerDesktopClerk(true, events))); |
| 98 | 98 | ||
| 99 | assert.deepEqual(createClerkBridgeMock.mock.calls, [ | 99 | assert.deepEqual(createClerkBridgeMock.mock.calls, [ |
| 100 | [ | 100 | [ |
| ... | @@ -143,7 +143,7 @@ describe("DesktopClerk", () => { | ... | @@ -143,7 +143,7 @@ describe("DesktopClerk", () => { |
| 143 | Effect.runSync( | 143 | Effect.runSync( |
| 144 | Effect.scoped( | 144 | Effect.scoped( |
| 145 | Layer.build( | 145 | Layer.build( |
| 146 | makeDesktopClerkLayer(isDevelopment, events, platform, DesktopPreReadyFileSystem.layer), | 146 | layerDesktopClerk(isDevelopment, events, platform, DesktopPreReadyFileSystem.layer), |
| 147 | ), | 147 | ), |
| 148 | ), | 148 | ), |
| 149 | ); | 149 | ); |
| ... | @@ -160,7 +160,7 @@ describe("DesktopClerk", () => { | ... | @@ -160,7 +160,7 @@ describe("DesktopClerk", () => { |
| 160 | }); | 160 | }); |
| 161 | 161 | ||
| 162 | return Effect.gen(function* () { | 162 | return Effect.gen(function* () { |
| 163 | const error = yield* Effect.scoped(Layer.build(makeDesktopClerkLayer())).pipe(Effect.flip); | 163 | const error = yield* Effect.scoped(Layer.build(layerDesktopClerk())).pipe(Effect.flip); |
| 164 | 164 | ||
| 165 | assert.instanceOf(error, DesktopClerk.DesktopClerkBridgeInitializationError); | 165 | assert.instanceOf(error, DesktopClerk.DesktopClerkBridgeInitializationError); |
| 166 | assert.equal(error.stateDir, "/tmp/t3-state"); | 166 | assert.equal(error.stateDir, "/tmp/t3-state"); |
| ... | @@ -183,7 +183,7 @@ describe("DesktopClerk", () => { | ... | @@ -183,7 +183,7 @@ describe("DesktopClerk", () => { |
| 183 | }); | 183 | }); |
| 184 | 184 | ||
| 185 | return Effect.gen(function* () { | 185 | return Effect.gen(function* () { |
| 186 | const exit = yield* Effect.exit(Effect.scoped(Layer.build(makeDesktopClerkLayer(false)))); | 186 | const exit = yield* Effect.exit(Effect.scoped(Layer.build(layerDesktopClerk(false)))); |
| 187 | 187 | ||
| 188 | assert.equal(exit._tag, "Failure"); | 188 | assert.equal(exit._tag, "Failure"); |
| 189 | if (exit._tag === "Failure") { | 189 | if (exit._tag === "Failure") { |
| ... | @@ -222,7 +222,7 @@ describe("DesktopClerk", () => { | ... | @@ -222,7 +222,7 @@ describe("DesktopClerk", () => { |
| 222 | assert.equal(quit.mock.calls.length, 0); | 222 | assert.equal(quit.mock.calls.length, 0); |
| 223 | assert.deepEqual(registeredEvents, ["open-url", "second-instance"]); | 223 | assert.deepEqual(registeredEvents, ["open-url", "second-instance"]); |
| 224 | }).pipe( | 224 | }).pipe( |
| 225 | Effect.provide(makeDesktopClerkLayer()), | 225 | Effect.provide(layerDesktopClerk()), |
| 226 | Effect.provideService(ElectronApp.ElectronApp, electronApp), | 226 | Effect.provideService(ElectronApp.ElectronApp, electronApp), |
| 227 | Effect.provideService(ElectronWindow.ElectronWindow, electronWindow), | 227 | Effect.provideService(ElectronWindow.ElectronWindow, electronWindow), |
| 228 | ); | 228 | ); |
| ... | @@ -250,7 +250,7 @@ describe("DesktopClerk", () => { | ... | @@ -250,7 +250,7 @@ describe("DesktopClerk", () => { |
| 250 | assert.equal(quit.mock.calls.length, 1); | 250 | assert.equal(quit.mock.calls.length, 1); |
| 251 | assert.deepEqual(registeredEvents, []); | 251 | assert.deepEqual(registeredEvents, []); |
| 252 | }).pipe( | 252 | }).pipe( |
| 253 | Effect.provide(makeDesktopClerkLayer()), | 253 | Effect.provide(layerDesktopClerk()), |
| 254 | Effect.provideService(ElectronApp.ElectronApp, electronApp), | 254 | Effect.provideService(ElectronApp.ElectronApp, electronApp), |
| 255 | Effect.provideService(ElectronWindow.ElectronWindow, electronWindow), | 255 | Effect.provideService(ElectronWindow.ElectronWindow, electronWindow), |
| 256 | ); | 256 | ); |
| ... | @@ -296,7 +296,7 @@ it.effect( | ... | @@ -296,7 +296,7 @@ it.effect( |
| 296 | assert.equal(event.preventDefault.mock.calls.length, 1); | 296 | assert.equal(event.preventDefault.mock.calls.length, 1); |
| 297 | }).pipe( | 297 | }).pipe( |
| 298 | Effect.scoped, | 298 | Effect.scoped, |
| 299 | Effect.provide(makeDesktopClerkLayer()), | 299 | Effect.provide(layerDesktopClerk()), |
| 300 | Effect.provideService(ElectronApp.ElectronApp, electronApp), | 300 | Effect.provideService(ElectronApp.ElectronApp, electronApp), |
| 301 | Effect.provideService(ElectronWindow.ElectronWindow, electronWindow), | 301 | Effect.provideService(ElectronWindow.ElectronWindow, electronWindow), |
| 302 | ); | 302 | ); |
| ... | @@ -374,7 +374,7 @@ it.effect.each(["startup", "open-url"] as const)( | ... | @@ -374,7 +374,7 @@ it.effect.each(["startup", "open-url"] as const)( |
| 374 | assert.strictEqual(delivery?.flowId, request.flowId); | 374 | assert.strictEqual(delivery?.flowId, request.flowId); |
| 375 | assert.strictEqual(delivery?.returnUrl, request.returnUrl); | 375 | assert.strictEqual(delivery?.returnUrl, request.returnUrl); |
| 376 | }).pipe( | 376 | }).pipe( |
| 377 | Effect.provide(makeDesktopClerkLayer(true, [], "darwin", undefined, shell)), | 377 | Effect.provide(layerDesktopClerk(true, [], "darwin", undefined, shell)), |
| 378 | Effect.provideService(HostProcessArguments, entry === "startup" ? ["t3", link] : ["t3"]), | 378 | Effect.provideService(HostProcessArguments, entry === "startup" ? ["t3", link] : ["t3"]), |
| 379 | Effect.provideService(ElectronApp.ElectronApp, electronApp), | 379 | Effect.provideService(ElectronApp.ElectronApp, electronApp), |
| 380 | Effect.provideService( | 380 | Effect.provideService( |
apps/desktop/src/app/DesktopConnectionCatalogStore.test.ts+17-17| ... | @@ -28,7 +28,7 @@ const encodeLegacySavedEnvironments = Schema.encodeEffect( | ... | @@ -28,7 +28,7 @@ const encodeLegacySavedEnvironments = Schema.encodeEffect( |
| 28 | Schema.Struct({ version: Schema.Literal(1), records: Schema.Array(Schema.Unknown) }), | 28 | Schema.Struct({ version: Schema.Literal(1), records: Schema.Array(Schema.Unknown) }), |
| 29 | ), | 29 | ), |
| 30 | ); | 30 | ); |
| 31 | function makeSafeStorageLayer(available: boolean, failDecrypt: Ref.Ref<boolean> | null = null) { | 31 | function layerSafeStorageFor(available: boolean, failDecrypt: Ref.Ref<boolean> | null = null) { |
| 32 | return Layer.succeed(ElectronSafeStorage.ElectronSafeStorage, { | 32 | return Layer.succeed(ElectronSafeStorage.ElectronSafeStorage, { |
| 33 | isEncryptionAvailable: Effect.succeed(available), | 33 | isEncryptionAvailable: Effect.succeed(available), |
| 34 | encryptString: (value) => Effect.succeed(textEncoder.encode(`encrypted:${value}`)), | 34 | encryptString: (value) => Effect.succeed(textEncoder.encode(`encrypted:${value}`)), |
| ... | @@ -50,13 +50,13 @@ function makeSafeStorageLayer(available: boolean, failDecrypt: Ref.Ref<boolean> | ... | @@ -50,13 +50,13 @@ function makeSafeStorageLayer(available: boolean, failDecrypt: Ref.Ref<boolean> |
| 50 | } satisfies ElectronSafeStorage.ElectronSafeStorage["Service"]); | 50 | } satisfies ElectronSafeStorage.ElectronSafeStorage["Service"]); |
| 51 | } | 51 | } |
| 52 | 52 | ||
| 53 | function makeLayer( | 53 | function layerFor( |
| 54 | baseDir: string, | 54 | baseDir: string, |
| 55 | encryptionAvailable = true, | 55 | encryptionAvailable = true, |
| 56 | failDecrypt: Ref.Ref<boolean> | null = null, | 56 | failDecrypt: Ref.Ref<boolean> | null = null, |
| 57 | fileSystemLayer: Layer.Layer<FileSystem.FileSystem> = NodeServices.layer, | 57 | fileSystemLayer: Layer.Layer<FileSystem.FileSystem> = NodeServices.layer, |
| 58 | ) { | 58 | ) { |
| 59 | const environmentLayer = DesktopEnvironment.layer({ | 59 | const layerEnvironment = DesktopEnvironment.layer({ |
| 60 | dirname: "/repo/apps/desktop/src", | 60 | dirname: "/repo/apps/desktop/src", |
| 61 | homeDirectory: baseDir, | 61 | homeDirectory: baseDir, |
| 62 | platform: "darwin", | 62 | platform: "darwin", |
| ... | @@ -71,20 +71,20 @@ function makeLayer( | ... | @@ -71,20 +71,20 @@ function makeLayer( |
| 71 | Layer.mergeAll(NodeServices.layer, DesktopConfig.layerTest({ T3CODE_HOME: baseDir })), | 71 | Layer.mergeAll(NodeServices.layer, DesktopConfig.layerTest({ T3CODE_HOME: baseDir })), |
| 72 | ), | 72 | ), |
| 73 | ); | 73 | ); |
| 74 | const safeStorageLayer = makeSafeStorageLayer(encryptionAvailable, failDecrypt); | 74 | const layerSafeStorage = layerSafeStorageFor(encryptionAvailable, failDecrypt); |
| 75 | const dependencies = Layer.mergeAll( | 75 | const layerDependencies = Layer.mergeAll( |
| 76 | environmentLayer, | 76 | layerEnvironment, |
| 77 | safeStorageLayer, | 77 | layerSafeStorage, |
| 78 | NodeServices.layer, | 78 | NodeServices.layer, |
| 79 | fileSystemLayer, | 79 | fileSystemLayer, |
| 80 | ); | 80 | ); |
| 81 | const savedEnvironmentsLayer = DesktopSavedEnvironments.layer.pipe( | 81 | const layerSavedEnvironments = DesktopSavedEnvironments.layer.pipe( |
| 82 | Layer.provideMerge(dependencies), | 82 | Layer.provideMerge(layerDependencies), |
| 83 | ); | 83 | ); |
| 84 | 84 | ||
| 85 | return DesktopConnectionCatalogStore.layer.pipe( | 85 | return DesktopConnectionCatalogStore.layer.pipe( |
| 86 | Layer.provideMerge(savedEnvironmentsLayer), | 86 | Layer.provideMerge(layerSavedEnvironments), |
| 87 | Layer.provideMerge(dependencies), | 87 | Layer.provideMerge(layerDependencies), |
| 88 | ); | 88 | ); |
| 89 | } | 89 | } |
| 90 | 90 | ||
| ... | @@ -97,7 +97,7 @@ const withStore = <A, E, R>( | ... | @@ -97,7 +97,7 @@ const withStore = <A, E, R>( |
| 97 | const baseDir = yield* fileSystem.makeTempDirectoryScoped({ | 97 | const baseDir = yield* fileSystem.makeTempDirectoryScoped({ |
| 98 | prefix: "t3-desktop-connection-catalog-test-", | 98 | prefix: "t3-desktop-connection-catalog-test-", |
| 99 | }); | 99 | }); |
| 100 | return yield* effect.pipe(Effect.provide(makeLayer(baseDir, encryptionAvailable))); | 100 | return yield* effect.pipe(Effect.provide(layerFor(baseDir, encryptionAvailable))); |
| 101 | }).pipe(Effect.provide(NodeServices.layer), Effect.scoped); | 101 | }).pipe(Effect.provide(NodeServices.layer), Effect.scoped); |
| 102 | 102 | ||
| 103 | describe("DesktopConnectionCatalogStore", () => { | 103 | describe("DesktopConnectionCatalogStore", () => { |
| ... | @@ -280,14 +280,14 @@ describe("DesktopConnectionCatalogStore", () => { | ... | @@ -280,14 +280,14 @@ describe("DesktopConnectionCatalogStore", () => { |
| 280 | method: "readFileString", | 280 | method: "readFileString", |
| 281 | pathOrDescriptor: path.join(baseDir, "userdata", "connection-catalog.json"), | 281 | pathOrDescriptor: path.join(baseDir, "userdata", "connection-catalog.json"), |
| 282 | }); | 282 | }); |
| 283 | const fileSystemLayer = Layer.succeed( | 283 | const layerFileSystem = Layer.succeed( |
| 284 | FileSystem.FileSystem, | 284 | FileSystem.FileSystem, |
| 285 | FileSystem.makeNoop({ | 285 | FileSystem.makeNoop({ |
| 286 | readFileString: () => Effect.fail(permissionError), | 286 | readFileString: () => Effect.fail(permissionError), |
| 287 | }), | 287 | }), |
| 288 | ); | 288 | ); |
| 289 | const store = yield* DesktopConnectionCatalogStore.DesktopConnectionCatalogStore.pipe( | 289 | const store = yield* DesktopConnectionCatalogStore.DesktopConnectionCatalogStore.pipe( |
| 290 | Effect.provide(makeLayer(baseDir, true, null, fileSystemLayer)), | 290 | Effect.provide(layerFor(baseDir, true, null, layerFileSystem)), |
| 291 | ); | 291 | ); |
| 292 | 292 | ||
| 293 | const error = yield* store.get.pipe(Effect.flip); | 293 | const error = yield* store.get.pipe(Effect.flip); |
| ... | @@ -318,14 +318,14 @@ describe("DesktopConnectionCatalogStore", () => { | ... | @@ -318,14 +318,14 @@ describe("DesktopConnectionCatalogStore", () => { |
| 318 | method: "makeDirectory", | 318 | method: "makeDirectory", |
| 319 | pathOrDescriptor: path.join(baseDir, "userdata"), | 319 | pathOrDescriptor: path.join(baseDir, "userdata"), |
| 320 | }); | 320 | }); |
| 321 | const fileSystemLayer = Layer.succeed( | 321 | const layerFileSystem = Layer.succeed( |
| 322 | FileSystem.FileSystem, | 322 | FileSystem.FileSystem, |
| 323 | FileSystem.makeNoop({ | 323 | FileSystem.makeNoop({ |
| 324 | makeDirectory: () => Effect.fail(permissionError), | 324 | makeDirectory: () => Effect.fail(permissionError), |
| 325 | }), | 325 | }), |
| 326 | ); | 326 | ); |
| 327 | const store = yield* DesktopConnectionCatalogStore.DesktopConnectionCatalogStore.pipe( | 327 | const store = yield* DesktopConnectionCatalogStore.DesktopConnectionCatalogStore.pipe( |
| 328 | Effect.provide(makeLayer(baseDir, true, null, fileSystemLayer)), | 328 | Effect.provide(layerFor(baseDir, true, null, layerFileSystem)), |
| 329 | ); | 329 | ); |
| 330 | 330 | ||
| 331 | const error = yield* store.set("{}").pipe(Effect.flip); | 331 | const error = yield* store.set("{}").pipe(Effect.flip); |
| ... | @@ -413,7 +413,7 @@ describe("DesktopConnectionCatalogStore", () => { | ... | @@ -413,7 +413,7 @@ describe("DesktopConnectionCatalogStore", () => { |
| 413 | prefix: "t3-desktop-connection-catalog-test-", | 413 | prefix: "t3-desktop-connection-catalog-test-", |
| 414 | }); | 414 | }); |
| 415 | const failDecrypt = yield* Ref.make(false); | 415 | const failDecrypt = yield* Ref.make(false); |
| 416 | const layer = makeLayer(baseDir, true, failDecrypt); | 416 | const layer = layerFor(baseDir, true, failDecrypt); |
| 417 | const store = yield* DesktopConnectionCatalogStore.DesktopConnectionCatalogStore.pipe( | 417 | const store = yield* DesktopConnectionCatalogStore.DesktopConnectionCatalogStore.pipe( |
| 418 | Effect.provide(layer), | 418 | Effect.provide(layer), |
| 419 | ); | 419 | ); |
apps/desktop/src/app/DesktopEnvironment.test.ts+2-3| ... | @@ -20,7 +20,7 @@ const defaultInput = { | ... | @@ -20,7 +20,7 @@ const defaultInput = { |
| 20 | runningUnderArm64Translation: false, | 20 | runningUnderArm64Translation: false, |
| 21 | } satisfies DesktopEnvironment.MakeDesktopEnvironmentInput; | 21 | } satisfies DesktopEnvironment.MakeDesktopEnvironmentInput; |
| 22 | 22 | ||
| 23 | const makeEnvironmentLayer = ( | 23 | const layerEnvironment = ( |
| 24 | overrides: Partial<DesktopEnvironment.MakeDesktopEnvironmentInput> = {}, | 24 | overrides: Partial<DesktopEnvironment.MakeDesktopEnvironmentInput> = {}, |
| 25 | env: Record<string, string | undefined> = {}, | 25 | env: Record<string, string | undefined> = {}, |
| 26 | ) => | 26 | ) => |
| ... | @@ -36,8 +36,7 @@ const makeEnvironmentLayer = ( | ... | @@ -36,8 +36,7 @@ const makeEnvironmentLayer = ( |
| 36 | const makeEnvironment = ( | 36 | const makeEnvironment = ( |
| 37 | overrides: Partial<DesktopEnvironment.MakeDesktopEnvironmentInput> = {}, | 37 | overrides: Partial<DesktopEnvironment.MakeDesktopEnvironmentInput> = {}, |
| 38 | env: Record<string, string | undefined> = {}, | 38 | env: Record<string, string | undefined> = {}, |
| 39 | ) => | 39 | ) => DesktopEnvironment.DesktopEnvironment.pipe(Effect.provide(layerEnvironment(overrides, env))); |
| 40 | DesktopEnvironment.DesktopEnvironment.pipe(Effect.provide(makeEnvironmentLayer(overrides, env))); | ||
| 41 | 40 | ||
| 42 | describe("DesktopEnvironment", () => { | 41 | describe("DesktopEnvironment", () => { |
| 43 | it.effect("derives state paths and development identity inside Effect", () => | 42 | it.effect("derives state paths and development identity inside Effect", () => |
apps/desktop/src/app/DesktopLifecycle.test.ts+24-24| ... | @@ -15,7 +15,7 @@ import * as DesktopShutdown from "./DesktopShutdown.ts"; | ... | @@ -15,7 +15,7 @@ import * as DesktopShutdown from "./DesktopShutdown.ts"; |
| 15 | import * as DesktopState from "./DesktopState.ts"; | 15 | import * as DesktopState from "./DesktopState.ts"; |
| 16 | import * as DesktopWindow from "../window/DesktopWindow.ts"; | 16 | import * as DesktopWindow from "../window/DesktopWindow.ts"; |
| 17 | 17 | ||
| 18 | function makeElectronAppLayer( | 18 | function layerElectronApp( |
| 19 | appListeners: Map<string, (...args: readonly unknown[]) => void>, | 19 | appListeners: Map<string, (...args: readonly unknown[]) => void>, |
| 20 | quit: Effect.Effect<void> = Effect.void, | 20 | quit: Effect.Effect<void> = Effect.void, |
| 21 | ) { | 21 | ) { |
| ... | @@ -54,13 +54,13 @@ function makeElectronAppLayer( | ... | @@ -54,13 +54,13 @@ function makeElectronAppLayer( |
| 54 | } satisfies ElectronApp.ElectronApp["Service"]); | 54 | } satisfies ElectronApp.ElectronApp["Service"]); |
| 55 | } | 55 | } |
| 56 | 56 | ||
| 57 | const electronThemeLayer = Layer.succeed(ElectronTheme.ElectronTheme, { | 57 | const layerElectronTheme = Layer.succeed(ElectronTheme.ElectronTheme, { |
| 58 | shouldUseDarkColors: Effect.succeed(false), | 58 | shouldUseDarkColors: Effect.succeed(false), |
| 59 | setSource: () => Effect.void, | 59 | setSource: () => Effect.void, |
| 60 | onUpdated: () => Effect.void, | 60 | onUpdated: () => Effect.void, |
| 61 | }); | 61 | }); |
| 62 | 62 | ||
| 63 | function makeElectronWindowLayer(destroyAll: Effect.Effect<void> = Effect.void) { | 63 | function layerElectronWindow(destroyAll: Effect.Effect<void> = Effect.void) { |
| 64 | return Layer.succeed(ElectronWindow.ElectronWindow, { | 64 | return Layer.succeed(ElectronWindow.ElectronWindow, { |
| 65 | create: () => Effect.die("unexpected window creation"), | 65 | create: () => Effect.die("unexpected window creation"), |
| 66 | main: Effect.die("unexpected main window read"), | 66 | main: Effect.die("unexpected main window read"), |
| ... | @@ -76,7 +76,7 @@ function makeElectronWindowLayer(destroyAll: Effect.Effect<void> = Effect.void) | ... | @@ -76,7 +76,7 @@ function makeElectronWindowLayer(destroyAll: Effect.Effect<void> = Effect.void) |
| 76 | }); | 76 | }); |
| 77 | } | 77 | } |
| 78 | 78 | ||
| 79 | function makeDesktopWindowLayer( | 79 | function layerDesktopWindow( |
| 80 | input: { | 80 | input: { |
| 81 | readonly activate?: Effect.Effect<void>; | 81 | readonly activate?: Effect.Effect<void>; |
| 82 | readonly flushMainWindowBounds?: Effect.Effect<void>; | 82 | readonly flushMainWindowBounds?: Effect.Effect<void>; |
| ... | @@ -106,23 +106,23 @@ describe("DesktopLifecycle", () => { | ... | @@ -106,23 +106,23 @@ describe("DesktopLifecycle", () => { |
| 106 | (platform) => { | 106 | (platform) => { |
| 107 | const appListeners = new Map<string, (...args: readonly unknown[]) => void>(); | 107 | const appListeners = new Map<string, (...args: readonly unknown[]) => void>(); |
| 108 | let windowsDestroyed = false; | 108 | let windowsDestroyed = false; |
| 109 | const environmentLayer = Layer.succeed(DesktopEnvironment.DesktopEnvironment, { | 109 | const layerEnvironment = Layer.succeed(DesktopEnvironment.DesktopEnvironment, { |
| 110 | platform, | 110 | platform, |
| 111 | isDevelopment: false, | 111 | isDevelopment: false, |
| 112 | } as DesktopEnvironment.DesktopEnvironment["Service"]); | 112 | } as DesktopEnvironment.DesktopEnvironment["Service"]); |
| 113 | 113 | ||
| 114 | const layer = DesktopLifecycle.layer.pipe( | 114 | const layer = DesktopLifecycle.layer.pipe( |
| 115 | Layer.provideMerge(makeElectronAppLayer(appListeners)), | 115 | Layer.provideMerge(layerElectronApp(appListeners)), |
| 116 | Layer.provideMerge(electronThemeLayer), | 116 | Layer.provideMerge(layerElectronTheme), |
| 117 | Layer.provideMerge( | 117 | Layer.provideMerge( |
| 118 | makeElectronWindowLayer( | 118 | layerElectronWindow( |
| 119 | Effect.sync(() => { | 119 | Effect.sync(() => { |
| 120 | windowsDestroyed = true; | 120 | windowsDestroyed = true; |
| 121 | }), | 121 | }), |
| 122 | ), | 122 | ), |
| 123 | ), | 123 | ), |
| 124 | Layer.provideMerge(makeDesktopWindowLayer()), | 124 | Layer.provideMerge(layerDesktopWindow()), |
| 125 | Layer.provideMerge(environmentLayer), | 125 | Layer.provideMerge(layerEnvironment), |
| 126 | Layer.provideMerge(DesktopShutdown.layer), | 126 | Layer.provideMerge(DesktopShutdown.layer), |
| 127 | Layer.provideMerge(DesktopState.layer), | 127 | Layer.provideMerge(DesktopState.layer), |
| 128 | ); | 128 | ); |
| ... | @@ -174,7 +174,7 @@ describe("DesktopLifecycle", () => { | ... | @@ -174,7 +174,7 @@ describe("DesktopLifecycle", () => { |
| 174 | events.push("flush"); | 174 | events.push("flush"); |
| 175 | }); | 175 | }); |
| 176 | 176 | ||
| 177 | const desktopShutdownLayer = Layer.succeed(DesktopShutdown.DesktopShutdown, { | 177 | const layerDesktopShutdown = Layer.succeed(DesktopShutdown.DesktopShutdown, { |
| 178 | request: Effect.sync(() => { | 178 | request: Effect.sync(() => { |
| 179 | events.push("request"); | 179 | events.push("request"); |
| 180 | }).pipe(Effect.andThen(Deferred.succeed(shutdownRequested, undefined)), Effect.asVoid), | 180 | }).pipe(Effect.andThen(Deferred.succeed(shutdownRequested, undefined)), Effect.asVoid), |
| ... | @@ -184,18 +184,18 @@ describe("DesktopLifecycle", () => { | ... | @@ -184,18 +184,18 @@ describe("DesktopLifecycle", () => { |
| 184 | isComplete: Deferred.isDone(allowShutdown), | 184 | isComplete: Deferred.isDone(allowShutdown), |
| 185 | }); | 185 | }); |
| 186 | 186 | ||
| 187 | const environmentLayer = Layer.succeed(DesktopEnvironment.DesktopEnvironment, { | 187 | const layerEnvironment = Layer.succeed(DesktopEnvironment.DesktopEnvironment, { |
| 188 | platform: "darwin", | 188 | platform: "darwin", |
| 189 | isDevelopment: false, | 189 | isDevelopment: false, |
| 190 | } as DesktopEnvironment.DesktopEnvironment["Service"]); | 190 | } as DesktopEnvironment.DesktopEnvironment["Service"]); |
| 191 | 191 | ||
| 192 | const layer = DesktopLifecycle.layer.pipe( | 192 | const layer = DesktopLifecycle.layer.pipe( |
| 193 | Layer.provideMerge(makeElectronAppLayer(appListeners, quit)), | 193 | Layer.provideMerge(layerElectronApp(appListeners, quit)), |
| 194 | Layer.provideMerge(electronThemeLayer), | 194 | Layer.provideMerge(layerElectronTheme), |
| 195 | Layer.provideMerge(makeElectronWindowLayer(destroyAll)), | 195 | Layer.provideMerge(layerElectronWindow(destroyAll)), |
| 196 | Layer.provideMerge(makeDesktopWindowLayer({ flushMainWindowBounds })), | 196 | Layer.provideMerge(layerDesktopWindow({ flushMainWindowBounds })), |
| 197 | Layer.provideMerge(environmentLayer), | 197 | Layer.provideMerge(layerEnvironment), |
| 198 | Layer.provideMerge(desktopShutdownLayer), | 198 | Layer.provideMerge(layerDesktopShutdown), |
| 199 | Layer.provideMerge(DesktopState.layer), | 199 | Layer.provideMerge(DesktopState.layer), |
| 200 | ); | 200 | ); |
| 201 | 201 | ||
| ... | @@ -226,16 +226,16 @@ describe("DesktopLifecycle", () => { | ... | @@ -226,16 +226,16 @@ describe("DesktopLifecycle", () => { |
| 226 | const activate = Effect.sync(() => { | 226 | const activate = Effect.sync(() => { |
| 227 | activationCount += 1; | 227 | activationCount += 1; |
| 228 | }); | 228 | }); |
| 229 | const environmentLayer = Layer.succeed(DesktopEnvironment.DesktopEnvironment, { | 229 | const layerEnvironment = Layer.succeed(DesktopEnvironment.DesktopEnvironment, { |
| 230 | platform: "darwin", | 230 | platform: "darwin", |
| 231 | isDevelopment: false, | 231 | isDevelopment: false, |
| 232 | } as DesktopEnvironment.DesktopEnvironment["Service"]); | 232 | } as DesktopEnvironment.DesktopEnvironment["Service"]); |
| 233 | const layer = DesktopLifecycle.layer.pipe( | 233 | const layer = DesktopLifecycle.layer.pipe( |
| 234 | Layer.provideMerge(makeElectronAppLayer(appListeners)), | 234 | Layer.provideMerge(layerElectronApp(appListeners)), |
| 235 | Layer.provideMerge(electronThemeLayer), | 235 | Layer.provideMerge(layerElectronTheme), |
| 236 | Layer.provideMerge(makeElectronWindowLayer()), | 236 | Layer.provideMerge(layerElectronWindow()), |
| 237 | Layer.provideMerge(makeDesktopWindowLayer({ activate })), | 237 | Layer.provideMerge(layerDesktopWindow({ activate })), |
| 238 | Layer.provideMerge(environmentLayer), | 238 | Layer.provideMerge(layerEnvironment), |
| 239 | Layer.provideMerge(DesktopShutdown.layer), | 239 | Layer.provideMerge(DesktopShutdown.layer), |
| 240 | Layer.provideMerge(DesktopState.layer), | 240 | Layer.provideMerge(DesktopState.layer), |
| 241 | ); | 241 | ); |
apps/desktop/src/app/DesktopLinuxUrlHandler.test.ts+3-3| ... | @@ -52,7 +52,7 @@ const mockProcess = (exitCode: number, stalled = false) => | ... | @@ -52,7 +52,7 @@ const mockProcess = (exitCode: number, stalled = false) => |
| 52 | getOutputFd: () => Stream.empty, | 52 | getOutputFd: () => Stream.empty, |
| 53 | }); | 53 | }); |
| 54 | 54 | ||
| 55 | const makeHandlerLayer = ( | 55 | const layerHandler = ( |
| 56 | recorded: RecordedRegistration, | 56 | recorded: RecordedRegistration, |
| 57 | input: { | 57 | input: { |
| 58 | readonly environment?: Record<string, unknown>; | 58 | readonly environment?: Record<string, unknown>; |
| ... | @@ -143,12 +143,12 @@ const makeHandlerLayer = ( | ... | @@ -143,12 +143,12 @@ const makeHandlerLayer = ( |
| 143 | 143 | ||
| 144 | const runRegister = ( | 144 | const runRegister = ( |
| 145 | recorded: RecordedRegistration, | 145 | recorded: RecordedRegistration, |
| 146 | input: Parameters<typeof makeHandlerLayer>[1] = {}, | 146 | input: Parameters<typeof layerHandler>[1] = {}, |
| 147 | ) => | 147 | ) => |
| 148 | Effect.gen(function* () { | 148 | Effect.gen(function* () { |
| 149 | const handler = yield* DesktopLinuxUrlHandler.DesktopLinuxUrlHandler; | 149 | const handler = yield* DesktopLinuxUrlHandler.DesktopLinuxUrlHandler; |
| 150 | yield* handler.register; | 150 | yield* handler.register; |
| 151 | }).pipe(Effect.provide(makeHandlerLayer(recorded, input))); | 151 | }).pipe(Effect.provide(layerHandler(recorded, input))); |
| 152 | 152 | ||
| 153 | const emptyRecording = (): RecordedRegistration => ({ | 153 | const emptyRecording = (): RecordedRegistration => ({ |
| 154 | directories: [], | 154 | directories: [], |
apps/desktop/src/app/DesktopObservability.test.ts+54-54| ... | @@ -52,7 +52,7 @@ const environmentInput = (baseDir: string) => | ... | @@ -52,7 +52,7 @@ const environmentInput = (baseDir: string) => |
| 52 | runningUnderArm64Translation: false, | 52 | runningUnderArm64Translation: false, |
| 53 | }) satisfies DesktopEnvironment.MakeDesktopEnvironmentInput; | 53 | }) satisfies DesktopEnvironment.MakeDesktopEnvironmentInput; |
| 54 | 54 | ||
| 55 | const makeEnvironmentLayer = ( | 55 | const layerEnvironmentFor = ( |
| 56 | baseDir: string, | 56 | baseDir: string, |
| 57 | isDevelopment = true, | 57 | isDevelopment = true, |
| 58 | env: Readonly<Record<string, string | undefined>> = {}, | 58 | env: Readonly<Record<string, string | undefined>> = {}, |
| ... | @@ -77,7 +77,7 @@ interface ExportedRequest { | ... | @@ -77,7 +77,7 @@ interface ExportedRequest { |
| 77 | } | 77 | } |
| 78 | 78 | ||
| 79 | /** Answers every export with a 200 and keeps what was posted for assertions. */ | 79 | /** Answers every export with a 200 and keeps what was posted for assertions. */ |
| 80 | const collectorLayer = (requests: Array<ExportedRequest>) => | 80 | const layerCollector = (requests: Array<ExportedRequest>) => |
| 81 | Layer.succeed( | 81 | Layer.succeed( |
| 82 | HttpClient.HttpClient, | 82 | HttpClient.HttpClient, |
| 83 | HttpClient.make((request) => | 83 | HttpClient.make((request) => |
| ... | @@ -94,7 +94,7 @@ const collectorLayer = (requests: Array<ExportedRequest>) => | ... | @@ -94,7 +94,7 @@ const collectorLayer = (requests: Array<ExportedRequest>) => |
| 94 | ); | 94 | ); |
| 95 | 95 | ||
| 96 | // A developer's own OTEL_* variables would otherwise pick the endpoints. | 96 | // A developer's own OTEL_* variables would otherwise pick the endpoints. |
| 97 | const emptyEnv = ConfigProvider.layer(ConfigProvider.fromEnv({ env: {} })); | 97 | const layerEmptyEnv = ConfigProvider.layer(ConfigProvider.fromEnv({ env: {} })); |
| 98 | 98 | ||
| 99 | const encodeObservabilitySettingsFile = Schema.encodeSync( | 99 | const encodeObservabilitySettingsFile = Schema.encodeSync( |
| 100 | Schema.fromJsonString( | 100 | Schema.fromJsonString( |
| ... | @@ -103,7 +103,7 @@ const encodeObservabilitySettingsFile = Schema.encodeSync( | ... | @@ -103,7 +103,7 @@ const encodeObservabilitySettingsFile = Schema.encodeSync( |
| 103 | ); | 103 | ); |
| 104 | 104 | ||
| 105 | const writeObservabilitySettings = Effect.fn(function* ( | 105 | const writeObservabilitySettings = Effect.fn(function* ( |
| 106 | environmentLayer: ReturnType<typeof makeEnvironmentLayer>, | 106 | environmentLayer: ReturnType<typeof layerEnvironmentFor>, |
| 107 | observability: Readonly<Record<string, string>>, | 107 | observability: Readonly<Record<string, string>>, |
| 108 | ) { | 108 | ) { |
| 109 | const fileSystem = yield* FileSystem.FileSystem; | 109 | const fileSystem = yield* FileSystem.FileSystem; |
| ... | @@ -146,15 +146,15 @@ describe("DesktopObservability", () => { | ... | @@ -146,15 +146,15 @@ describe("DesktopObservability", () => { |
| 146 | const baseDir = yield* fileSystem.makeTempDirectoryScoped({ | 146 | const baseDir = yield* fileSystem.makeTempDirectoryScoped({ |
| 147 | prefix: "t3-desktop-observability-test-", | 147 | prefix: "t3-desktop-observability-test-", |
| 148 | }); | 148 | }); |
| 149 | const environmentLayer = makeEnvironmentLayer(baseDir); | 149 | const layerEnvironment = layerEnvironmentFor(baseDir); |
| 150 | const tracePath = yield* Effect.gen(function* () { | 150 | const tracePath = yield* Effect.gen(function* () { |
| 151 | const environment = yield* DesktopEnvironment.DesktopEnvironment; | 151 | const environment = yield* DesktopEnvironment.DesktopEnvironment; |
| 152 | return environment.path.join(environment.logDir, "desktop.trace.ndjson"); | 152 | return environment.path.join(environment.logDir, "desktop.trace.ndjson"); |
| 153 | }).pipe(Effect.provide(environmentLayer)); | 153 | }).pipe(Effect.provide(layerEnvironment)); |
| 154 | const logPath = yield* Effect.gen(function* () { | 154 | const logPath = yield* Effect.gen(function* () { |
| 155 | const environment = yield* DesktopEnvironment.DesktopEnvironment; | 155 | const environment = yield* DesktopEnvironment.DesktopEnvironment; |
| 156 | return environment.path.join(environment.logDir, "desktop-main.log"); | 156 | return environment.path.join(environment.logDir, "desktop-main.log"); |
| 157 | }).pipe(Effect.provide(environmentLayer)); | 157 | }).pipe(Effect.provide(layerEnvironment)); |
| 158 | 158 | ||
| 159 | yield* Effect.scoped( | 159 | yield* Effect.scoped( |
| 160 | Effect.gen(function* () { | 160 | Effect.gen(function* () { |
| ... | @@ -162,7 +162,7 @@ describe("DesktopObservability", () => { | ... | @@ -162,7 +162,7 @@ describe("DesktopObservability", () => { |
| 162 | yield* Effect.logInfo("desktop trace event"); | 162 | yield* Effect.logInfo("desktop trace event"); |
| 163 | }).pipe( | 163 | }).pipe( |
| 164 | Effect.withSpan("desktop-observability-test"), | 164 | Effect.withSpan("desktop-observability-test"), |
| 165 | Effect.provide(DesktopObservability.layer.pipe(Layer.provideMerge(environmentLayer))), | 165 | Effect.provide(DesktopObservability.layer.pipe(Layer.provideMerge(layerEnvironment))), |
| 166 | ), | 166 | ), |
| 167 | ); | 167 | ); |
| 168 | 168 | ||
| ... | @@ -185,7 +185,7 @@ describe("DesktopObservability", () => { | ... | @@ -185,7 +185,7 @@ describe("DesktopObservability", () => { |
| 185 | assert.isFalse(yield* fileSystem.exists(logPath)); | 185 | assert.isFalse(yield* fileSystem.exists(logPath)); |
| 186 | }).pipe( | 186 | }).pipe( |
| 187 | Effect.scoped, | 187 | Effect.scoped, |
| 188 | Effect.provide(Layer.mergeAll(NodeServices.layer, NodeHttpClient.layerUndici, emptyEnv)), | 188 | Effect.provide(Layer.mergeAll(NodeServices.layer, NodeHttpClient.layerUndici, layerEmptyEnv)), |
| 189 | ), | 189 | ), |
| 190 | ); | 190 | ); |
| 191 | 191 | ||
| ... | @@ -195,15 +195,15 @@ describe("DesktopObservability", () => { | ... | @@ -195,15 +195,15 @@ describe("DesktopObservability", () => { |
| 195 | const baseDir = yield* fileSystem.makeTempDirectoryScoped({ | 195 | const baseDir = yield* fileSystem.makeTempDirectoryScoped({ |
| 196 | prefix: "t3-desktop-backend-output-log-test-", | 196 | prefix: "t3-desktop-backend-output-log-test-", |
| 197 | }); | 197 | }); |
| 198 | const environmentLayer = makeEnvironmentLayer(baseDir, false); | 198 | const layerEnvironment = layerEnvironmentFor(baseDir, false); |
| 199 | const logPath = yield* Effect.gen(function* () { | 199 | const logPath = yield* Effect.gen(function* () { |
| 200 | const environment = yield* DesktopEnvironment.DesktopEnvironment; | 200 | const environment = yield* DesktopEnvironment.DesktopEnvironment; |
| 201 | return environment.path.join(environment.logDir, "server-child.log"); | 201 | return environment.path.join(environment.logDir, "server-child.log"); |
| 202 | }).pipe(Effect.provide(environmentLayer)); | 202 | }).pipe(Effect.provide(layerEnvironment)); |
| 203 | const tracePath = yield* Effect.gen(function* () { | 203 | const tracePath = yield* Effect.gen(function* () { |
| 204 | const environment = yield* DesktopEnvironment.DesktopEnvironment; | 204 | const environment = yield* DesktopEnvironment.DesktopEnvironment; |
| 205 | return environment.path.join(environment.logDir, "desktop.trace.ndjson"); | 205 | return environment.path.join(environment.logDir, "desktop.trace.ndjson"); |
| 206 | }).pipe(Effect.provide(environmentLayer)); | 206 | }).pipe(Effect.provide(layerEnvironment)); |
| 207 | 207 | ||
| 208 | yield* Effect.gen(function* () { | 208 | yield* Effect.gen(function* () { |
| 209 | const factory = yield* DesktopObservability.DesktopBackendOutputLogFactory; | 209 | const factory = yield* DesktopObservability.DesktopBackendOutputLogFactory; |
| ... | @@ -219,7 +219,7 @@ describe("DesktopObservability", () => { | ... | @@ -219,7 +219,7 @@ describe("DesktopObservability", () => { |
| 219 | yield* outputLog.discardSession; | 219 | yield* outputLog.discardSession; |
| 220 | }).pipe( | 220 | }).pipe( |
| 221 | Effect.annotateLogs({ runId: "test-run" }), | 221 | Effect.annotateLogs({ runId: "test-run" }), |
| 222 | Effect.provide(DesktopObservability.layer.pipe(Layer.provideMerge(environmentLayer))), | 222 | Effect.provide(DesktopObservability.layer.pipe(Layer.provideMerge(layerEnvironment))), |
| 223 | ); | 223 | ); |
| 224 | 224 | ||
| 225 | const log = yield* fileSystem.readFileString(logPath); | 225 | const log = yield* fileSystem.readFileString(logPath); |
| ... | @@ -263,7 +263,7 @@ describe("DesktopObservability", () => { | ... | @@ -263,7 +263,7 @@ describe("DesktopObservability", () => { |
| 263 | ); | 263 | ); |
| 264 | }).pipe( | 264 | }).pipe( |
| 265 | Effect.scoped, | 265 | Effect.scoped, |
| 266 | Effect.provide(Layer.mergeAll(NodeServices.layer, NodeHttpClient.layerUndici, emptyEnv)), | 266 | Effect.provide(Layer.mergeAll(NodeServices.layer, NodeHttpClient.layerUndici, layerEmptyEnv)), |
| 267 | ), | 267 | ), |
| 268 | ); | 268 | ); |
| 269 | 269 | ||
| ... | @@ -273,11 +273,11 @@ describe("DesktopObservability", () => { | ... | @@ -273,11 +273,11 @@ describe("DesktopObservability", () => { |
| 273 | const baseDir = yield* fileSystem.makeTempDirectoryScoped({ | 273 | const baseDir = yield* fileSystem.makeTempDirectoryScoped({ |
| 274 | prefix: "t3-desktop-backend-output-snapshot-test-", | 274 | prefix: "t3-desktop-backend-output-snapshot-test-", |
| 275 | }); | 275 | }); |
| 276 | const environmentLayer = makeEnvironmentLayer(baseDir, false); | 276 | const layerEnvironment = layerEnvironmentFor(baseDir, false); |
| 277 | const logPath = yield* Effect.gen(function* () { | 277 | const logPath = yield* Effect.gen(function* () { |
| 278 | const environment = yield* DesktopEnvironment.DesktopEnvironment; | 278 | const environment = yield* DesktopEnvironment.DesktopEnvironment; |
| 279 | return environment.path.join(environment.logDir, "server-child.log"); | 279 | return environment.path.join(environment.logDir, "server-child.log"); |
| 280 | }).pipe(Effect.provide(environmentLayer)); | 280 | }).pipe(Effect.provide(layerEnvironment)); |
| 281 | 281 | ||
| 282 | yield* Effect.gen(function* () { | 282 | yield* Effect.gen(function* () { |
| 283 | const factory = yield* DesktopObservability.DesktopBackendOutputLogFactory; | 283 | const factory = yield* DesktopObservability.DesktopBackendOutputLogFactory; |
| ... | @@ -289,7 +289,7 @@ describe("DesktopObservability", () => { | ... | @@ -289,7 +289,7 @@ describe("DesktopObservability", () => { |
| 289 | yield* outputLog.persistFailure({ details: "code=1" }); | 289 | yield* outputLog.persistFailure({ details: "code=1" }); |
| 290 | }).pipe( | 290 | }).pipe( |
| 291 | Effect.annotateLogs({ runId: "test-run" }), | 291 | Effect.annotateLogs({ runId: "test-run" }), |
| 292 | Effect.provide(DesktopObservability.layer.pipe(Layer.provideMerge(environmentLayer))), | 292 | Effect.provide(DesktopObservability.layer.pipe(Layer.provideMerge(layerEnvironment))), |
| 293 | ); | 293 | ); |
| 294 | 294 | ||
| 295 | const records = yield* Effect.forEach( | 295 | const records = yield* Effect.forEach( |
| ... | @@ -303,7 +303,7 @@ describe("DesktopObservability", () => { | ... | @@ -303,7 +303,7 @@ describe("DesktopObservability", () => { |
| 303 | assert.equal(records.at(-1)?.annotations.details, "code=1"); | 303 | assert.equal(records.at(-1)?.annotations.details, "code=1"); |
| 304 | }).pipe( | 304 | }).pipe( |
| 305 | Effect.scoped, | 305 | Effect.scoped, |
| 306 | Effect.provide(Layer.mergeAll(NodeServices.layer, NodeHttpClient.layerUndici, emptyEnv)), | 306 | Effect.provide(Layer.mergeAll(NodeServices.layer, NodeHttpClient.layerUndici, layerEmptyEnv)), |
| 307 | ), | 307 | ), |
| 308 | ); | 308 | ); |
| 309 | 309 | ||
| ... | @@ -313,11 +313,11 @@ describe("DesktopObservability", () => { | ... | @@ -313,11 +313,11 @@ describe("DesktopObservability", () => { |
| 313 | const baseDir = yield* fileSystem.makeTempDirectoryScoped({ | 313 | const baseDir = yield* fileSystem.makeTempDirectoryScoped({ |
| 314 | prefix: "t3-desktop-backend-output-bound-test-", | 314 | prefix: "t3-desktop-backend-output-bound-test-", |
| 315 | }); | 315 | }); |
| 316 | const environmentLayer = makeEnvironmentLayer(baseDir, false); | 316 | const layerEnvironment = layerEnvironmentFor(baseDir, false); |
| 317 | const logPath = yield* Effect.gen(function* () { | 317 | const logPath = yield* Effect.gen(function* () { |
| 318 | const environment = yield* DesktopEnvironment.DesktopEnvironment; | 318 | const environment = yield* DesktopEnvironment.DesktopEnvironment; |
| 319 | return environment.path.join(environment.logDir, "server-child.log"); | 319 | return environment.path.join(environment.logDir, "server-child.log"); |
| 320 | }).pipe(Effect.provide(environmentLayer)); | 320 | }).pipe(Effect.provide(layerEnvironment)); |
| 321 | const maxBufferedBytes = 1024 * 1024; | 321 | const maxBufferedBytes = 1024 * 1024; |
| 322 | const discardedPrefixBytes = 128; | 322 | const discardedPrefixBytes = 128; |
| 323 | const output = new Uint8Array(maxBufferedBytes + discardedPrefixBytes); | 323 | const output = new Uint8Array(maxBufferedBytes + discardedPrefixBytes); |
| ... | @@ -332,7 +332,7 @@ describe("DesktopObservability", () => { | ... | @@ -332,7 +332,7 @@ describe("DesktopObservability", () => { |
| 332 | yield* outputLog.writeOutputChunk("stderr", output); | 332 | yield* outputLog.writeOutputChunk("stderr", output); |
| 333 | yield* outputLog.persistFailure({ details: "code=1" }); | 333 | yield* outputLog.persistFailure({ details: "code=1" }); |
| 334 | }).pipe( | 334 | }).pipe( |
| 335 | Effect.provide(DesktopObservability.layer.pipe(Layer.provideMerge(environmentLayer))), | 335 | Effect.provide(DesktopObservability.layer.pipe(Layer.provideMerge(layerEnvironment))), |
| 336 | ), | 336 | ), |
| 337 | ); | 337 | ); |
| 338 | 338 | ||
| ... | @@ -347,7 +347,7 @@ describe("DesktopObservability", () => { | ... | @@ -347,7 +347,7 @@ describe("DesktopObservability", () => { |
| 347 | assert.isFalse(text.includes("y")); | 347 | assert.isFalse(text.includes("y")); |
| 348 | }).pipe( | 348 | }).pipe( |
| 349 | Effect.scoped, | 349 | Effect.scoped, |
| 350 | Effect.provide(Layer.mergeAll(NodeServices.layer, NodeHttpClient.layerUndici, emptyEnv)), | 350 | Effect.provide(Layer.mergeAll(NodeServices.layer, NodeHttpClient.layerUndici, layerEmptyEnv)), |
| 351 | ), | 351 | ), |
| 352 | ); | 352 | ); |
| 353 | 353 | ||
| ... | @@ -357,11 +357,11 @@ describe("DesktopObservability", () => { | ... | @@ -357,11 +357,11 @@ describe("DesktopObservability", () => { |
| 357 | const baseDir = yield* fileSystem.makeTempDirectoryScoped({ | 357 | const baseDir = yield* fileSystem.makeTempDirectoryScoped({ |
| 358 | prefix: "t3-desktop-backend-output-chunks-test-", | 358 | prefix: "t3-desktop-backend-output-chunks-test-", |
| 359 | }); | 359 | }); |
| 360 | const environmentLayer = makeEnvironmentLayer(baseDir, false); | 360 | const layerEnvironment = layerEnvironmentFor(baseDir, false); |
| 361 | const logPath = yield* Effect.gen(function* () { | 361 | const logPath = yield* Effect.gen(function* () { |
| 362 | const environment = yield* DesktopEnvironment.DesktopEnvironment; | 362 | const environment = yield* DesktopEnvironment.DesktopEnvironment; |
| 363 | return environment.path.join(environment.logDir, "server-child.log"); | 363 | return environment.path.join(environment.logDir, "server-child.log"); |
| 364 | }).pipe(Effect.provide(environmentLayer)); | 364 | }).pipe(Effect.provide(layerEnvironment)); |
| 365 | 365 | ||
| 366 | yield* Effect.scoped( | 366 | yield* Effect.scoped( |
| 367 | Effect.gen(function* () { | 367 | Effect.gen(function* () { |
| ... | @@ -373,7 +373,7 @@ describe("DesktopObservability", () => { | ... | @@ -373,7 +373,7 @@ describe("DesktopObservability", () => { |
| 373 | } | 373 | } |
| 374 | yield* outputLog.persistFailure({ details: "code=1" }); | 374 | yield* outputLog.persistFailure({ details: "code=1" }); |
| 375 | }).pipe( | 375 | }).pipe( |
| 376 | Effect.provide(DesktopObservability.layer.pipe(Layer.provideMerge(environmentLayer))), | 376 | Effect.provide(DesktopObservability.layer.pipe(Layer.provideMerge(layerEnvironment))), |
| 377 | ), | 377 | ), |
| 378 | ); | 378 | ); |
| 379 | 379 | ||
| ... | @@ -381,7 +381,7 @@ describe("DesktopObservability", () => { | ... | @@ -381,7 +381,7 @@ describe("DesktopObservability", () => { |
| 381 | assert.equal(lines.length, 258); | 381 | assert.equal(lines.length, 258); |
| 382 | }).pipe( | 382 | }).pipe( |
| 383 | Effect.scoped, | 383 | Effect.scoped, |
| 384 | Effect.provide(Layer.mergeAll(NodeServices.layer, NodeHttpClient.layerUndici, emptyEnv)), | 384 | Effect.provide(Layer.mergeAll(NodeServices.layer, NodeHttpClient.layerUndici, layerEmptyEnv)), |
| 385 | ), | 385 | ), |
| 386 | ); | 386 | ); |
| 387 | 387 | ||
| ... | @@ -392,19 +392,19 @@ describe("DesktopObservability", () => { | ... | @@ -392,19 +392,19 @@ describe("DesktopObservability", () => { |
| 392 | const baseDir = yield* fileSystem.makeTempDirectoryScoped({ | 392 | const baseDir = yield* fileSystem.makeTempDirectoryScoped({ |
| 393 | prefix: "t3-desktop-observability-test-", | 393 | prefix: "t3-desktop-observability-test-", |
| 394 | }); | 394 | }); |
| 395 | const environmentLayer = makeEnvironmentLayer(baseDir, true, { | 395 | const layerEnvironment = layerEnvironmentFor(baseDir, true, { |
| 396 | T3CODE_OTLP_LOGS_URL: "https://collector.example.com/v1/logs", | 396 | T3CODE_OTLP_LOGS_URL: "https://collector.example.com/v1/logs", |
| 397 | T3CODE_OTLP_HEADERS: "x-scope=desktop", | 397 | T3CODE_OTLP_HEADERS: "x-scope=desktop", |
| 398 | }); | 398 | }); |
| 399 | const tracePath = yield* Effect.gen(function* () { | 399 | const tracePath = yield* Effect.gen(function* () { |
| 400 | const environment = yield* DesktopEnvironment.DesktopEnvironment; | 400 | const environment = yield* DesktopEnvironment.DesktopEnvironment; |
| 401 | return environment.path.join(environment.logDir, "desktop.trace.ndjson"); | 401 | return environment.path.join(environment.logDir, "desktop.trace.ndjson"); |
| 402 | }).pipe(Effect.provide(environmentLayer)); | 402 | }).pipe(Effect.provide(layerEnvironment)); |
| 403 | 403 | ||
| 404 | yield* Effect.scoped( | 404 | yield* Effect.scoped( |
| 405 | Effect.logInfo("desktop log export").pipe( | 405 | Effect.logInfo("desktop log export").pipe( |
| 406 | Effect.withSpan("desktop-log-export-test"), | 406 | Effect.withSpan("desktop-log-export-test"), |
| 407 | Effect.provide(DesktopObservability.layer.pipe(Layer.provideMerge(environmentLayer))), | 407 | Effect.provide(DesktopObservability.layer.pipe(Layer.provideMerge(layerEnvironment))), |
| 408 | ), | 408 | ), |
| 409 | ); | 409 | ); |
| 410 | 410 | ||
| ... | @@ -427,7 +427,7 @@ describe("DesktopObservability", () => { | ... | @@ -427,7 +427,7 @@ describe("DesktopObservability", () => { |
| 427 | assert.lengthOf(record?.events ?? [], 0); | 427 | assert.lengthOf(record?.events ?? [], 0); |
| 428 | }).pipe( | 428 | }).pipe( |
| 429 | Effect.scoped, | 429 | Effect.scoped, |
| 430 | Effect.provide(Layer.mergeAll(NodeServices.layer, collectorLayer(requests), emptyEnv)), | 430 | Effect.provide(Layer.mergeAll(NodeServices.layer, layerCollector(requests), layerEmptyEnv)), |
| 431 | ); | 431 | ); |
| 432 | }); | 432 | }); |
| 433 | 433 | ||
| ... | @@ -438,16 +438,16 @@ describe("DesktopObservability", () => { | ... | @@ -438,16 +438,16 @@ describe("DesktopObservability", () => { |
| 438 | const baseDir = yield* fileSystem.makeTempDirectoryScoped({ | 438 | const baseDir = yield* fileSystem.makeTempDirectoryScoped({ |
| 439 | prefix: "t3-desktop-observability-test-", | 439 | prefix: "t3-desktop-observability-test-", |
| 440 | }); | 440 | }); |
| 441 | const environmentLayer = makeEnvironmentLayer(baseDir, true, { | 441 | const layerEnvironment = layerEnvironmentFor(baseDir, true, { |
| 442 | T3CODE_OTLP_HEADERS: "x-scope=desktop", | 442 | T3CODE_OTLP_HEADERS: "x-scope=desktop", |
| 443 | }); | 443 | }); |
| 444 | yield* writeObservabilitySettings(environmentLayer, { | 444 | yield* writeObservabilitySettings(layerEnvironment, { |
| 445 | otlpLogsUrl: "https://settings.example.com/v1/logs", | 445 | otlpLogsUrl: "https://settings.example.com/v1/logs", |
| 446 | }); | 446 | }); |
| 447 | 447 | ||
| 448 | yield* Effect.scoped( | 448 | yield* Effect.scoped( |
| 449 | Effect.logInfo("desktop otel export").pipe( | 449 | Effect.logInfo("desktop otel export").pipe( |
| 450 | Effect.provide(DesktopObservability.layer.pipe(Layer.provideMerge(environmentLayer))), | 450 | Effect.provide(DesktopObservability.layer.pipe(Layer.provideMerge(layerEnvironment))), |
| 451 | ), | 451 | ), |
| 452 | ); | 452 | ); |
| 453 | 453 | ||
| ... | @@ -462,7 +462,7 @@ describe("DesktopObservability", () => { | ... | @@ -462,7 +462,7 @@ describe("DesktopObservability", () => { |
| 462 | Effect.provide( | 462 | Effect.provide( |
| 463 | Layer.mergeAll( | 463 | Layer.mergeAll( |
| 464 | NodeServices.layer, | 464 | NodeServices.layer, |
| 465 | collectorLayer(requests), | 465 | layerCollector(requests), |
| 466 | ConfigProvider.layer( | 466 | ConfigProvider.layer( |
| 467 | ConfigProvider.fromEnv({ | 467 | ConfigProvider.fromEnv({ |
| 468 | env: { | 468 | env: { |
| ... | @@ -484,13 +484,13 @@ describe("DesktopObservability", () => { | ... | @@ -484,13 +484,13 @@ describe("DesktopObservability", () => { |
| 484 | const baseDir = yield* fileSystem.makeTempDirectoryScoped({ | 484 | const baseDir = yield* fileSystem.makeTempDirectoryScoped({ |
| 485 | prefix: "t3-desktop-observability-test-", | 485 | prefix: "t3-desktop-observability-test-", |
| 486 | }); | 486 | }); |
| 487 | const environmentLayer = makeEnvironmentLayer(baseDir, true, { | 487 | const layerEnvironment = layerEnvironmentFor(baseDir, true, { |
| 488 | T3CODE_OTLP_LOGS_URL: "https://collector.example.com/v1/logs", | 488 | T3CODE_OTLP_LOGS_URL: "https://collector.example.com/v1/logs", |
| 489 | }); | 489 | }); |
| 490 | 490 | ||
| 491 | yield* Effect.scoped( | 491 | yield* Effect.scoped( |
| 492 | Effect.logInfo("desktop service name").pipe( | 492 | Effect.logInfo("desktop service name").pipe( |
| 493 | Effect.provide(DesktopObservability.layer.pipe(Layer.provideMerge(environmentLayer))), | 493 | Effect.provide(DesktopObservability.layer.pipe(Layer.provideMerge(layerEnvironment))), |
| 494 | ), | 494 | ), |
| 495 | ); | 495 | ); |
| 496 | 496 | ||
| ... | @@ -505,7 +505,7 @@ describe("DesktopObservability", () => { | ... | @@ -505,7 +505,7 @@ describe("DesktopObservability", () => { |
| 505 | Effect.provide( | 505 | Effect.provide( |
| 506 | Layer.mergeAll( | 506 | Layer.mergeAll( |
| 507 | NodeServices.layer, | 507 | NodeServices.layer, |
| 508 | collectorLayer(requests), | 508 | layerCollector(requests), |
| 509 | ConfigProvider.layer( | 509 | ConfigProvider.layer( |
| 510 | ConfigProvider.fromEnv({ | 510 | ConfigProvider.fromEnv({ |
| 511 | env: { | 511 | env: { |
| ... | @@ -527,16 +527,16 @@ describe("DesktopObservability", () => { | ... | @@ -527,16 +527,16 @@ describe("DesktopObservability", () => { |
| 527 | const baseDir = yield* fileSystem.makeTempDirectoryScoped({ | 527 | const baseDir = yield* fileSystem.makeTempDirectoryScoped({ |
| 528 | prefix: "t3-desktop-observability-test-", | 528 | prefix: "t3-desktop-observability-test-", |
| 529 | }); | 529 | }); |
| 530 | const environmentLayer = makeEnvironmentLayer(baseDir, true, { | 530 | const layerEnvironment = layerEnvironmentFor(baseDir, true, { |
| 531 | T3CODE_OTLP_HEADERS: "x-scope=desktop", | 531 | T3CODE_OTLP_HEADERS: "x-scope=desktop", |
| 532 | }); | 532 | }); |
| 533 | yield* writeObservabilitySettings(environmentLayer, { | 533 | yield* writeObservabilitySettings(layerEnvironment, { |
| 534 | otlpLogsUrl: "https://settings.example.com/v1/logs", | 534 | otlpLogsUrl: "https://settings.example.com/v1/logs", |
| 535 | }); | 535 | }); |
| 536 | 536 | ||
| 537 | yield* Effect.scoped( | 537 | yield* Effect.scoped( |
| 538 | Effect.logInfo("desktop otel off").pipe( | 538 | Effect.logInfo("desktop otel off").pipe( |
| 539 | Effect.provide(DesktopObservability.layer.pipe(Layer.provideMerge(environmentLayer))), | 539 | Effect.provide(DesktopObservability.layer.pipe(Layer.provideMerge(layerEnvironment))), |
| 540 | ), | 540 | ), |
| 541 | ); | 541 | ); |
| 542 | 542 | ||
| ... | @@ -546,7 +546,7 @@ describe("DesktopObservability", () => { | ... | @@ -546,7 +546,7 @@ describe("DesktopObservability", () => { |
| 546 | Effect.provide( | 546 | Effect.provide( |
| 547 | Layer.mergeAll( | 547 | Layer.mergeAll( |
| 548 | NodeServices.layer, | 548 | NodeServices.layer, |
| 549 | collectorLayer(requests), | 549 | layerCollector(requests), |
| 550 | ConfigProvider.layer( | 550 | ConfigProvider.layer( |
| 551 | ConfigProvider.fromEnv({ | 551 | ConfigProvider.fromEnv({ |
| 552 | env: { | 552 | env: { |
| ... | @@ -567,13 +567,13 @@ describe("DesktopObservability", () => { | ... | @@ -567,13 +567,13 @@ describe("DesktopObservability", () => { |
| 567 | const baseDir = yield* fileSystem.makeTempDirectoryScoped({ | 567 | const baseDir = yield* fileSystem.makeTempDirectoryScoped({ |
| 568 | prefix: "t3-desktop-observability-test-", | 568 | prefix: "t3-desktop-observability-test-", |
| 569 | }); | 569 | }); |
| 570 | const environmentLayer = makeEnvironmentLayer(baseDir, true, { | 570 | const layerEnvironment = layerEnvironmentFor(baseDir, true, { |
| 571 | T3CODE_OTLP_LOGS_URL: "https://collector.example.com/v1/logs", | 571 | T3CODE_OTLP_LOGS_URL: "https://collector.example.com/v1/logs", |
| 572 | }); | 572 | }); |
| 573 | 573 | ||
| 574 | yield* Effect.scoped( | 574 | yield* Effect.scoped( |
| 575 | Effect.void.pipe( | 575 | Effect.void.pipe( |
| 576 | Effect.provide(DesktopObservability.layer.pipe(Layer.provideMerge(environmentLayer))), | 576 | Effect.provide(DesktopObservability.layer.pipe(Layer.provideMerge(layerEnvironment))), |
| 577 | ), | 577 | ), |
| 578 | ); | 578 | ); |
| 579 | 579 | ||
| ... | @@ -583,7 +583,7 @@ describe("DesktopObservability", () => { | ... | @@ -583,7 +583,7 @@ describe("DesktopObservability", () => { |
| 583 | Effect.provide( | 583 | Effect.provide( |
| 584 | Layer.mergeAll( | 584 | Layer.mergeAll( |
| 585 | NodeServices.layer, | 585 | NodeServices.layer, |
| 586 | collectorLayer(requests), | 586 | layerCollector(requests), |
| 587 | ConfigProvider.layer(ConfigProvider.fromEnv({ env: { OTEL_SDK_DISABLED: "1" } })), | 587 | ConfigProvider.layer(ConfigProvider.fromEnv({ env: { OTEL_SDK_DISABLED: "1" } })), |
| 588 | ), | 588 | ), |
| 589 | ), | 589 | ), |
| ... | @@ -597,8 +597,8 @@ describe("DesktopObservability", () => { | ... | @@ -597,8 +597,8 @@ describe("DesktopObservability", () => { |
| 597 | const baseDir = yield* fileSystem.makeTempDirectoryScoped({ | 597 | const baseDir = yield* fileSystem.makeTempDirectoryScoped({ |
| 598 | prefix: "t3-desktop-observability-test-", | 598 | prefix: "t3-desktop-observability-test-", |
| 599 | }); | 599 | }); |
| 600 | const environmentLayer = makeEnvironmentLayer(baseDir); | 600 | const layerEnvironment = layerEnvironmentFor(baseDir); |
| 601 | yield* writeObservabilitySettings(environmentLayer, { | 601 | yield* writeObservabilitySettings(layerEnvironment, { |
| 602 | otlpTracesUrl: "https://settings.example.com/v1/traces", | 602 | otlpTracesUrl: "https://settings.example.com/v1/traces", |
| 603 | otlpLogsUrl: "https://settings.example.com/v1/logs", | 603 | otlpLogsUrl: "https://settings.example.com/v1/logs", |
| 604 | // The main process records no metrics yet, so this endpoint must | 604 | // The main process records no metrics yet, so this endpoint must |
| ... | @@ -609,7 +609,7 @@ describe("DesktopObservability", () => { | ... | @@ -609,7 +609,7 @@ describe("DesktopObservability", () => { |
| 609 | yield* Effect.scoped( | 609 | yield* Effect.scoped( |
| 610 | Effect.logInfo("desktop log export from settings").pipe( | 610 | Effect.logInfo("desktop log export from settings").pipe( |
| 611 | Effect.withSpan("desktop-settings-export-test"), | 611 | Effect.withSpan("desktop-settings-export-test"), |
| 612 | Effect.provide(DesktopObservability.layer.pipe(Layer.provideMerge(environmentLayer))), | 612 | Effect.provide(DesktopObservability.layer.pipe(Layer.provideMerge(layerEnvironment))), |
| 613 | ), | 613 | ), |
| 614 | ); | 614 | ); |
| 615 | 615 | ||
| ... | @@ -627,7 +627,7 @@ describe("DesktopObservability", () => { | ... | @@ -627,7 +627,7 @@ describe("DesktopObservability", () => { |
| 627 | ); | 627 | ); |
| 628 | }).pipe( | 628 | }).pipe( |
| 629 | Effect.scoped, | 629 | Effect.scoped, |
| 630 | Effect.provide(Layer.mergeAll(NodeServices.layer, collectorLayer(requests), emptyEnv)), | 630 | Effect.provide(Layer.mergeAll(NodeServices.layer, layerCollector(requests), layerEmptyEnv)), |
| 631 | ); | 631 | ); |
| 632 | }); | 632 | }); |
| 633 | 633 | ||
| ... | @@ -643,7 +643,7 @@ describe("DesktopObservability", () => { | ... | @@ -643,7 +643,7 @@ describe("DesktopObservability", () => { |
| 643 | Effect.logInfo("desktop log stays local").pipe( | 643 | Effect.logInfo("desktop log stays local").pipe( |
| 644 | Effect.withSpan("desktop-offline-test"), | 644 | Effect.withSpan("desktop-offline-test"), |
| 645 | Effect.provide( | 645 | Effect.provide( |
| 646 | DesktopObservability.layer.pipe(Layer.provideMerge(makeEnvironmentLayer(baseDir))), | 646 | DesktopObservability.layer.pipe(Layer.provideMerge(layerEnvironmentFor(baseDir))), |
| 647 | ), | 647 | ), |
| 648 | ), | 648 | ), |
| 649 | ); | 649 | ); |
| ... | @@ -651,7 +651,7 @@ describe("DesktopObservability", () => { | ... | @@ -651,7 +651,7 @@ describe("DesktopObservability", () => { |
| 651 | assert.lengthOf(requests, 0); | 651 | assert.lengthOf(requests, 0); |
| 652 | }).pipe( | 652 | }).pipe( |
| 653 | Effect.scoped, | 653 | Effect.scoped, |
| 654 | Effect.provide(Layer.mergeAll(NodeServices.layer, collectorLayer(requests), emptyEnv)), | 654 | Effect.provide(Layer.mergeAll(NodeServices.layer, layerCollector(requests), layerEmptyEnv)), |
| 655 | ); | 655 | ); |
| 656 | }); | 656 | }); |
| 657 | 657 | ||
| ... | @@ -662,8 +662,8 @@ describe("DesktopObservability", () => { | ... | @@ -662,8 +662,8 @@ describe("DesktopObservability", () => { |
| 662 | const baseDir = yield* fileSystem.makeTempDirectoryScoped({ | 662 | const baseDir = yield* fileSystem.makeTempDirectoryScoped({ |
| 663 | prefix: "t3-desktop-observability-test-", | 663 | prefix: "t3-desktop-observability-test-", |
| 664 | }); | 664 | }); |
| 665 | const environmentLayer = makeEnvironmentLayer(baseDir); | 665 | const layerEnvironment = layerEnvironmentFor(baseDir); |
| 666 | yield* writeObservabilitySettings(environmentLayer, { | 666 | yield* writeObservabilitySettings(layerEnvironment, { |
| 667 | otlpTracesUrl: "https://settings.example.com/v1/traces", | 667 | otlpTracesUrl: "https://settings.example.com/v1/traces", |
| 668 | otlpLogsUrl: "https://settings.example.com/v1/logs", | 668 | otlpLogsUrl: "https://settings.example.com/v1/logs", |
| 669 | }); | 669 | }); |
| ... | @@ -671,7 +671,7 @@ describe("DesktopObservability", () => { | ... | @@ -671,7 +671,7 @@ describe("DesktopObservability", () => { |
| 671 | yield* Effect.scoped( | 671 | yield* Effect.scoped( |
| 672 | Effect.logInfo("desktop log stays local when disabled").pipe( | 672 | Effect.logInfo("desktop log stays local when disabled").pipe( |
| 673 | Effect.withSpan("desktop-disabled-test"), | 673 | Effect.withSpan("desktop-disabled-test"), |
| 674 | Effect.provide(DesktopObservability.layer.pipe(Layer.provideMerge(environmentLayer))), | 674 | Effect.provide(DesktopObservability.layer.pipe(Layer.provideMerge(layerEnvironment))), |
| 675 | ), | 675 | ), |
| 676 | ); | 676 | ); |
| 677 | 677 | ||
| ... | @@ -681,7 +681,7 @@ describe("DesktopObservability", () => { | ... | @@ -681,7 +681,7 @@ describe("DesktopObservability", () => { |
| 681 | Effect.provide( | 681 | Effect.provide( |
| 682 | Layer.mergeAll( | 682 | Layer.mergeAll( |
| 683 | NodeServices.layer, | 683 | NodeServices.layer, |
| 684 | collectorLayer(requests), | 684 | layerCollector(requests), |
| 685 | ConfigProvider.layer(ConfigProvider.fromEnv({ env: { OTEL_SDK_DISABLED: "true" } })), | 685 | ConfigProvider.layer(ConfigProvider.fromEnv({ env: { OTEL_SDK_DISABLED: "true" } })), |
| 686 | ), | 686 | ), |
| 687 | ), | 687 | ), |
apps/desktop/src/app/DesktopObservability.ts+19-13| ... | @@ -2,9 +2,9 @@ import { PRIMARY_LOCAL_ENVIRONMENT_ID } from "@t3tools/contracts"; | ... | @@ -2,9 +2,9 @@ import { PRIMARY_LOCAL_ENVIRONMENT_ID } from "@t3tools/contracts"; |
| 2 | import { | 2 | import { |
| 3 | makeLocalFileTracer, | 3 | makeLocalFileTracer, |
| 4 | makeTraceSink, | 4 | makeTraceSink, |
| 5 | otlpSerializationLayer, | ||
| 6 | type SignalExport, | 5 | type SignalExport, |
| 7 | } from "@t3tools/shared/observability"; | 6 | } from "@t3tools/shared/observability"; |
| 7 | import * as SharedObservability from "@t3tools/shared/observability"; | ||
| 8 | import * as OtelEnvironment from "@t3tools/shared/otelEnvironment"; | 8 | import * as OtelEnvironment from "@t3tools/shared/otelEnvironment"; |
| 9 | import { | 9 | import { |
| 10 | parsePersistedServerObservabilitySettings, | 10 | parsePersistedServerObservabilitySettings, |
| ... | @@ -559,7 +559,7 @@ const makeBackendOutputLogShape = ( | ... | @@ -559,7 +559,7 @@ const makeBackendOutputLogShape = ( |
| 559 | }), | 559 | }), |
| 560 | }); | 560 | }); |
| 561 | 561 | ||
| 562 | const backendOutputLogFactoryLayer = Layer.effect( | 562 | const layerBackendOutputLogFactory = Layer.effect( |
| 563 | DesktopBackendOutputLogFactory, | 563 | DesktopBackendOutputLogFactory, |
| 564 | Effect.gen(function* () { | 564 | Effect.gen(function* () { |
| 565 | const environment = yield* DesktopEnvironment.DesktopEnvironment; | 565 | const environment = yield* DesktopEnvironment.DesktopEnvironment; |
| ... | @@ -621,7 +621,7 @@ const backendOutputLogFactoryLayer = Layer.effect( | ... | @@ -621,7 +621,7 @@ const backendOutputLogFactoryLayer = Layer.effect( |
| 621 | * one read of the environment and Settings, and because a process gets exactly | 621 | * one read of the environment and Settings, and because a process gets exactly |
| 622 | * one logger set. | 622 | * one logger set. |
| 623 | */ | 623 | */ |
| 624 | const telemetryLayer = Layer.unwrap( | 624 | const layerTelemetry = Layer.unwrap( |
| 625 | Effect.gen(function* () { | 625 | Effect.gen(function* () { |
| 626 | const environment = yield* DesktopEnvironment.DesktopEnvironment; | 626 | const environment = yield* DesktopEnvironment.DesktopEnvironment; |
| 627 | const endpoints = yield* resolveOtlpEndpoints; | 627 | const endpoints = yield* resolveOtlpEndpoints; |
| ... | @@ -645,7 +645,7 @@ const telemetryLayer = Layer.unwrap( | ... | @@ -645,7 +645,7 @@ const telemetryLayer = Layer.unwrap( |
| 645 | // while the OTLP logger carries every message as a log record stamped | 645 | // while the OTLP logger carries every message as a log record stamped |
| 646 | // with its trace and span ids. Keeping both would export every in-span | 646 | // with its trace and span ids. Keeping both would export every in-span |
| 647 | // message twice. | 647 | // message twice. |
| 648 | const loggerLayer = Logger.layer( | 648 | const layerLogger = Logger.layer( |
| 649 | endpoints.logs === undefined | 649 | endpoints.logs === undefined |
| 650 | ? [Logger.consolePretty(), Logger.tracerLogger] | 650 | ? [Logger.consolePretty(), Logger.tracerLogger] |
| 651 | : [ | 651 | : [ |
| ... | @@ -661,11 +661,13 @@ const telemetryLayer = Layer.unwrap( | ... | @@ -661,11 +661,13 @@ const telemetryLayer = Layer.unwrap( |
| 661 | ).pipe( | 661 | ).pipe( |
| 662 | Layer.provide(OtlpExporter.layerFlusher), | 662 | Layer.provide(OtlpExporter.layerFlusher), |
| 663 | Layer.provide( | 663 | Layer.provide( |
| 664 | otlpSerializationLayer(endpoints.logs?.export.protocol ?? environment.otlpProtocol), | 664 | SharedObservability.layerOtlpSerialization( |
| 665 | endpoints.logs?.export.protocol ?? environment.otlpProtocol, | ||
| 666 | ), | ||
| 665 | ), | 667 | ), |
| 666 | ); | 668 | ); |
| 667 | 669 | ||
| 668 | const tracerLayer = Layer.unwrap( | 670 | const layerTracer = Layer.unwrap( |
| 669 | Effect.gen(function* () { | 671 | Effect.gen(function* () { |
| 670 | const tracePath = environment.path.join(environment.logDir, "desktop.trace.ndjson"); | 672 | const tracePath = environment.path.join(environment.logDir, "desktop.trace.ndjson"); |
| 671 | const sink = yield* makeTraceSink({ | 673 | const sink = yield* makeTraceSink({ |
| ... | @@ -682,7 +684,11 @@ const telemetryLayer = Layer.unwrap( | ... | @@ -682,7 +684,11 @@ const telemetryLayer = Layer.unwrap( |
| 682 | exportInterval: `${endpoints.traces.export.exportIntervalMs} millis`, | 684 | exportInterval: `${endpoints.traces.export.exportIntervalMs} millis`, |
| 683 | headers: endpoints.traces.export.headers, | 685 | headers: endpoints.traces.export.headers, |
| 684 | resource, | 686 | resource, |
| 685 | }).pipe(Effect.provide(otlpSerializationLayer(endpoints.traces.export.protocol))); | 687 | }).pipe( |
| 688 | Effect.provide( | ||
| 689 | SharedObservability.layerOtlpSerialization(endpoints.traces.export.protocol), | ||
| 690 | ), | ||
| 691 | ); | ||
| 686 | const tracer = yield* makeLocalFileTracer({ | 692 | const tracer = yield* makeLocalFileTracer({ |
| 687 | filePath: tracePath, | 693 | filePath: tracePath, |
| 688 | maxBytes: DESKTOP_LOG_FILE_MAX_BYTES, | 694 | maxBytes: DESKTOP_LOG_FILE_MAX_BYTES, |
| ... | @@ -710,23 +716,23 @@ const telemetryLayer = Layer.unwrap( | ... | @@ -710,23 +716,23 @@ const telemetryLayer = Layer.unwrap( |
| 710 | // exportInterval: `${endpoints.metrics.export.exportIntervalMs} millis`, | 716 | // exportInterval: `${endpoints.metrics.export.exportIntervalMs} millis`, |
| 711 | // headers: endpoints.metrics.export.headers, | 717 | // headers: endpoints.metrics.export.headers, |
| 712 | // resource, | 718 | // resource, |
| 713 | // }).pipe(Layer.provide(otlpSerializationLayer(endpoints.metrics.export.protocol))); | 719 | // }).pipe(Layer.provide(SharedObservability.layerOtlpSerialization(endpoints.metrics.export.protocol))); |
| 714 | 720 | ||
| 715 | // Logged once the loggers above are installed, so the warnings use them. | 721 | // Logged once the loggers above are installed, so the warnings use them. |
| 716 | const otelWarningsLayer = Layer.effectDiscard( | 722 | const layerOtelWarnings = Layer.effectDiscard( |
| 717 | Effect.forEach(endpoints.warnings, (warning) => Effect.logWarning(warning)), | 723 | Effect.forEach(endpoints.warnings, (warning) => Effect.logWarning(warning)), |
| 718 | ); | 724 | ); |
| 719 | 725 | ||
| 720 | return otelWarningsLayer.pipe( | 726 | return layerOtelWarnings.pipe( |
| 721 | Layer.provideMerge(Layer.mergeAll(loggerLayer, tracerLayer)), | 727 | Layer.provideMerge(Layer.mergeAll(layerLogger, layerTracer)), |
| 722 | Layer.provide(OtelEnvironment.layerResourceAttributes(endpoints.resourceAttributes)), | 728 | Layer.provide(OtelEnvironment.layerResourceAttributes(endpoints.resourceAttributes)), |
| 723 | ); | 729 | ); |
| 724 | }), | 730 | }), |
| 725 | ); | 731 | ); |
| 726 | 732 | ||
| 727 | export const layer = Layer.mergeAll( | 733 | export const layer = Layer.mergeAll( |
| 728 | backendOutputLogFactoryLayer, | 734 | layerBackendOutputLogFactory, |
| 729 | telemetryLayer, | 735 | layerTelemetry, |
| 730 | Layer.succeed(References.MinimumLogLevel, "Info"), | 736 | Layer.succeed(References.MinimumLogLevel, "Info"), |
| 731 | Layer.succeed(Tracer.MinimumTraceLevel, "Info"), | 737 | Layer.succeed(Tracer.MinimumTraceLevel, "Info"), |
| 732 | Layer.succeed(References.TracerTimingEnabled, true), | 738 | Layer.succeed(References.TracerTimingEnabled, true), |
apps/desktop/src/app/DesktopPreReadyPlatform.test.ts+5-5| ... | @@ -167,11 +167,11 @@ describe("DesktopPreReadyPlatform", () => { | ... | @@ -167,11 +167,11 @@ describe("DesktopPreReadyPlatform", () => { |
| 167 | events.push("pre-ready"); | 167 | events.push("pre-ready"); |
| 168 | }); | 168 | }); |
| 169 | 169 | ||
| 170 | const preReadyLayer = DesktopPreReadyPlatform.layer.pipe( | 170 | const layerPreReady = DesktopPreReadyPlatform.layer.pipe( |
| 171 | Layer.provide(Layer.succeed(HostProcessPlatform, "darwin")), | 171 | Layer.provide(Layer.succeed(HostProcessPlatform, "darwin")), |
| 172 | ); | 172 | ); |
| 173 | 173 | ||
| 174 | const clerkShapedLayer = Layer.effect( | 174 | const layerClerkShaped = Layer.effect( |
| 175 | ClerkShaped, | 175 | ClerkShaped, |
| 176 | Effect.promise(() => Promise.resolve()).pipe( | 176 | Effect.promise(() => Promise.resolve()).pipe( |
| 177 | Effect.map(() => { | 177 | Effect.map(() => { |
| ... | @@ -181,15 +181,15 @@ describe("DesktopPreReadyPlatform", () => { | ... | @@ -181,15 +181,15 @@ describe("DesktopPreReadyPlatform", () => { |
| 181 | ), | 181 | ), |
| 182 | ); | 182 | ); |
| 183 | 183 | ||
| 184 | const runtimeLayer = clerkShapedLayer.pipe( | 184 | const layerRuntime = layerClerkShaped.pipe( |
| 185 | Layer.flatMap((clerkContext) => Layer.succeedContext(clerkContext)), | 185 | Layer.flatMap((clerkContext) => Layer.succeedContext(clerkContext)), |
| 186 | Layer.provideMerge(preReadyLayer), | 186 | Layer.provideMerge(layerPreReady), |
| 187 | ); | 187 | ); |
| 188 | 188 | ||
| 189 | const result = yield* Effect.all({ | 189 | const result = yield* Effect.all({ |
| 190 | clerk: ClerkShaped, | 190 | clerk: ClerkShaped, |
| 191 | preReady: DesktopPreReadyPlatform.DesktopPreReadyElectronOptions, | 191 | preReady: DesktopPreReadyPlatform.DesktopPreReadyElectronOptions, |
| 192 | }).pipe(Effect.provide(runtimeLayer)); | 192 | }).pipe(Effect.provide(layerRuntime)); |
| 193 | 193 | ||
| 194 | assert.deepEqual(result, { | 194 | assert.deepEqual(result, { |
| 195 | clerk: { ready: true }, | 195 | clerk: { ready: true }, |
apps/desktop/src/backend/DesktopBackendConfiguration.test.ts+52-52| ... | @@ -35,7 +35,7 @@ const isDesktopBackendObservabilitySettingsReadError = Schema.is( | ... | @@ -35,7 +35,7 @@ const isDesktopBackendObservabilitySettingsReadError = Schema.is( |
| 35 | DesktopBackendConfiguration.DesktopBackendObservabilitySettingsReadError, | 35 | DesktopBackendConfiguration.DesktopBackendObservabilitySettingsReadError, |
| 36 | ); | 36 | ); |
| 37 | 37 | ||
| 38 | const serverExposureLayer = Layer.succeed(DesktopServerExposure.DesktopServerExposure, { | 38 | const layerServerExposure = Layer.succeed(DesktopServerExposure.DesktopServerExposure, { |
| 39 | getState: Effect.die("unexpected getState"), | 39 | getState: Effect.die("unexpected getState"), |
| 40 | backendConfig: Effect.succeed({ | 40 | backendConfig: Effect.succeed({ |
| 41 | port: 4888, | 41 | port: 4888, |
| ... | @@ -50,7 +50,7 @@ const serverExposureLayer = Layer.succeed(DesktopServerExposure.DesktopServerExp | ... | @@ -50,7 +50,7 @@ const serverExposureLayer = Layer.succeed(DesktopServerExposure.DesktopServerExp |
| 50 | getAdvertisedEndpoints: Effect.succeed([]), | 50 | getAdvertisedEndpoints: Effect.succeed([]), |
| 51 | } satisfies DesktopServerExposure.DesktopServerExposure["Service"]); | 51 | } satisfies DesktopServerExposure.DesktopServerExposure["Service"]); |
| 52 | 52 | ||
| 53 | function makeEnvironmentLayer( | 53 | function layerEnvironment( |
| 54 | baseDir: string, | 54 | baseDir: string, |
| 55 | options?: { | 55 | options?: { |
| 56 | readonly appPath?: string; | 56 | readonly appPath?: string; |
| ... | @@ -122,11 +122,11 @@ const withHarness = <A, E, R>( | ... | @@ -122,11 +122,11 @@ const withHarness = <A, E, R>( |
| 122 | return yield* effect.pipe( | 122 | return yield* effect.pipe( |
| 123 | Effect.provide( | 123 | Effect.provide( |
| 124 | DesktopBackendConfiguration.layer.pipe( | 124 | DesktopBackendConfiguration.layer.pipe( |
| 125 | Layer.provideMerge(serverExposureLayer), | 125 | Layer.provideMerge(layerServerExposure), |
| 126 | Layer.provideMerge(DesktopAppSettings.layerTest()), | 126 | Layer.provideMerge(DesktopAppSettings.layerTest()), |
| 127 | Layer.provideMerge(DesktopWslEnvironment.layerTest()), | 127 | Layer.provideMerge(DesktopWslEnvironment.layerTest()), |
| 128 | Layer.provideMerge(DesktopWslServerTree.layerTest()), | 128 | Layer.provideMerge(DesktopWslServerTree.layerTest()), |
| 129 | Layer.provideMerge(makeEnvironmentLayer(baseDir)), | 129 | Layer.provideMerge(layerEnvironment(baseDir)), |
| 130 | ), | 130 | ), |
| 131 | ), | 131 | ), |
| 132 | ); | 132 | ); |
| ... | @@ -182,7 +182,7 @@ const withPackagedWslHarness = <A, E, R>( | ... | @@ -182,7 +182,7 @@ const withPackagedWslHarness = <A, E, R>( |
| 182 | mountedAppRoot, | 182 | mountedAppRoot, |
| 183 | mountedEntryPath, | 183 | mountedEntryPath, |
| 184 | } satisfies PackagedWslHarnessContext; | 184 | } satisfies PackagedWslHarnessContext; |
| 185 | const serverTreeLayer = input.forbidFallback | 185 | const layerServerTree = input.forbidFallback |
| 186 | ? Layer.succeed( | 186 | ? Layer.succeed( |
| 187 | DesktopWslServerTree.DesktopWslServerTree, | 187 | DesktopWslServerTree.DesktopWslServerTree, |
| 188 | DesktopWslServerTree.DesktopWslServerTree.of({ | 188 | DesktopWslServerTree.DesktopWslServerTree.of({ |
| ... | @@ -200,9 +200,9 @@ const withPackagedWslHarness = <A, E, R>( | ... | @@ -200,9 +200,9 @@ const withPackagedWslHarness = <A, E, R>( |
| 200 | return yield* effect(context).pipe( | 200 | return yield* effect(context).pipe( |
| 201 | Effect.provide( | 201 | Effect.provide( |
| 202 | DesktopBackendConfiguration.layer.pipe( | 202 | DesktopBackendConfiguration.layer.pipe( |
| 203 | Layer.provideMerge(serverExposureLayer), | 203 | Layer.provideMerge(layerServerExposure), |
| 204 | Layer.provideMerge(DesktopAppSettings.layerTest()), | 204 | Layer.provideMerge(DesktopAppSettings.layerTest()), |
| 205 | Layer.provideMerge(serverTreeLayer), | 205 | Layer.provideMerge(layerServerTree), |
| 206 | Layer.provideMerge( | 206 | Layer.provideMerge( |
| 207 | DesktopWslEnvironment.layerTest({ | 207 | DesktopWslEnvironment.layerTest({ |
| 208 | isAvailable: true, | 208 | isAvailable: true, |
| ... | @@ -213,7 +213,7 @@ const withPackagedWslHarness = <A, E, R>( | ... | @@ -213,7 +213,7 @@ const withPackagedWslHarness = <A, E, R>( |
| 213 | }), | 213 | }), |
| 214 | ), | 214 | ), |
| 215 | Layer.provideMerge( | 215 | Layer.provideMerge( |
| 216 | makeEnvironmentLayer(baseDir, { | 216 | layerEnvironment(baseDir, { |
| 217 | appPath: baseDir, | 217 | appPath: baseDir, |
| 218 | platform: "win32", | 218 | platform: "win32", |
| 219 | resourcesPath: baseDir, | 219 | resourcesPath: baseDir, |
| ... | @@ -276,7 +276,7 @@ describe("DesktopBackendConfiguration", () => { | ... | @@ -276,7 +276,7 @@ describe("DesktopBackendConfiguration", () => { |
| 276 | }).pipe( | 276 | }).pipe( |
| 277 | Effect.provide( | 277 | Effect.provide( |
| 278 | DesktopBackendConfiguration.layer.pipe( | 278 | DesktopBackendConfiguration.layer.pipe( |
| 279 | Layer.provideMerge(serverExposureLayer), | 279 | Layer.provideMerge(layerServerExposure), |
| 280 | Layer.provideMerge(DesktopAppSettings.layerTest()), | 280 | Layer.provideMerge(DesktopAppSettings.layerTest()), |
| 281 | Layer.provideMerge(DesktopWslEnvironment.layerTest()), | 281 | Layer.provideMerge(DesktopWslEnvironment.layerTest()), |
| 282 | Layer.provideMerge( | 282 | Layer.provideMerge( |
| ... | @@ -289,7 +289,7 @@ describe("DesktopBackendConfiguration", () => { | ... | @@ -289,7 +289,7 @@ describe("DesktopBackendConfiguration", () => { |
| 289 | ), | 289 | ), |
| 290 | ), | 290 | ), |
| 291 | Layer.provideMerge( | 291 | Layer.provideMerge( |
| 292 | makeEnvironmentLayer(baseDir, { | 292 | layerEnvironment(baseDir, { |
| 293 | appPath: `${resourcesPath}/app.asar`, | 293 | appPath: `${resourcesPath}/app.asar`, |
| 294 | platform: "win32", | 294 | platform: "win32", |
| 295 | resourcesPath, | 295 | resourcesPath, |
| ... | @@ -338,7 +338,7 @@ describe("DesktopBackendConfiguration", () => { | ... | @@ -338,7 +338,7 @@ describe("DesktopBackendConfiguration", () => { |
| 338 | }).pipe( | 338 | }).pipe( |
| 339 | Effect.provide( | 339 | Effect.provide( |
| 340 | DesktopBackendConfiguration.layer.pipe( | 340 | DesktopBackendConfiguration.layer.pipe( |
| 341 | Layer.provideMerge(serverExposureLayer), | 341 | Layer.provideMerge(layerServerExposure), |
| 342 | Layer.provideMerge(DesktopAppSettings.layerTest()), | 342 | Layer.provideMerge(DesktopAppSettings.layerTest()), |
| 343 | Layer.provideMerge(DesktopWslServerTree.layerTest()), | 343 | Layer.provideMerge(DesktopWslServerTree.layerTest()), |
| 344 | Layer.provideMerge( | 344 | Layer.provideMerge( |
| ... | @@ -363,7 +363,7 @@ describe("DesktopBackendConfiguration", () => { | ... | @@ -363,7 +363,7 @@ describe("DesktopBackendConfiguration", () => { |
| 363 | }), | 363 | }), |
| 364 | ), | 364 | ), |
| 365 | Layer.provideMerge( | 365 | Layer.provideMerge( |
| 366 | makeEnvironmentLayer(baseDir, { | 366 | layerEnvironment(baseDir, { |
| 367 | appPath: baseDir, | 367 | appPath: baseDir, |
| 368 | platform: "win32", | 368 | platform: "win32", |
| 369 | resourcesPath: baseDir, | 369 | resourcesPath: baseDir, |
| ... | @@ -660,7 +660,7 @@ describe("DesktopBackendConfiguration", () => { | ... | @@ -660,7 +660,7 @@ describe("DesktopBackendConfiguration", () => { |
| 660 | }).pipe( | 660 | }).pipe( |
| 661 | Effect.provide( | 661 | Effect.provide( |
| 662 | DesktopBackendConfiguration.layer.pipe( | 662 | DesktopBackendConfiguration.layer.pipe( |
| 663 | Layer.provideMerge(serverExposureLayer), | 663 | Layer.provideMerge(layerServerExposure), |
| 664 | Layer.provideMerge(DesktopAppSettings.layerTest()), | 664 | Layer.provideMerge(DesktopAppSettings.layerTest()), |
| 665 | Layer.provideMerge(DesktopWslServerTree.layerTest()), | 665 | Layer.provideMerge(DesktopWslServerTree.layerTest()), |
| 666 | Layer.provideMerge( | 666 | Layer.provideMerge( |
| ... | @@ -673,7 +673,7 @@ describe("DesktopBackendConfiguration", () => { | ... | @@ -673,7 +673,7 @@ describe("DesktopBackendConfiguration", () => { |
| 673 | }), | 673 | }), |
| 674 | ), | 674 | ), |
| 675 | Layer.provideMerge( | 675 | Layer.provideMerge( |
| 676 | makeEnvironmentLayer(baseDir, { | 676 | layerEnvironment(baseDir, { |
| 677 | appPath: baseDir, | 677 | appPath: baseDir, |
| 678 | devServerUrl, | 678 | devServerUrl, |
| 679 | isPackaged: true, | 679 | isPackaged: true, |
| ... | @@ -788,7 +788,7 @@ describe("DesktopBackendConfiguration", () => { | ... | @@ -788,7 +788,7 @@ describe("DesktopBackendConfiguration", () => { |
| 788 | }).pipe( | 788 | }).pipe( |
| 789 | Effect.provide( | 789 | Effect.provide( |
| 790 | DesktopBackendConfiguration.layer.pipe( | 790 | DesktopBackendConfiguration.layer.pipe( |
| 791 | Layer.provideMerge(serverExposureLayer), | 791 | Layer.provideMerge(layerServerExposure), |
| 792 | Layer.provideMerge(DesktopAppSettings.layerTest()), | 792 | Layer.provideMerge(DesktopAppSettings.layerTest()), |
| 793 | Layer.provideMerge(DesktopWslServerTree.layerTest()), | 793 | Layer.provideMerge(DesktopWslServerTree.layerTest()), |
| 794 | Layer.provideMerge( | 794 | Layer.provideMerge( |
| ... | @@ -799,7 +799,7 @@ describe("DesktopBackendConfiguration", () => { | ... | @@ -799,7 +799,7 @@ describe("DesktopBackendConfiguration", () => { |
| 799 | }), | 799 | }), |
| 800 | ), | 800 | ), |
| 801 | Layer.provideMerge( | 801 | Layer.provideMerge( |
| 802 | makeEnvironmentLayer(baseDir, { | 802 | layerEnvironment(baseDir, { |
| 803 | platform: "win32", | 803 | platform: "win32", |
| 804 | otlpTracesUrl: " http://127.0.0.1:4318/v1/traces ", | 804 | otlpTracesUrl: " http://127.0.0.1:4318/v1/traces ", |
| 805 | otlpMetricsUrl: " http://127.0.0.1:4318/v1/metrics ", | 805 | otlpMetricsUrl: " http://127.0.0.1:4318/v1/metrics ", |
| ... | @@ -846,12 +846,12 @@ describe("DesktopBackendConfiguration", () => { | ... | @@ -846,12 +846,12 @@ describe("DesktopBackendConfiguration", () => { |
| 846 | }).pipe( | 846 | }).pipe( |
| 847 | Effect.provide( | 847 | Effect.provide( |
| 848 | DesktopBackendConfiguration.layer.pipe( | 848 | DesktopBackendConfiguration.layer.pipe( |
| 849 | Layer.provideMerge(serverExposureLayer), | 849 | Layer.provideMerge(layerServerExposure), |
| 850 | Layer.provideMerge(DesktopAppSettings.layerTest()), | 850 | Layer.provideMerge(DesktopAppSettings.layerTest()), |
| 851 | Layer.provideMerge(DesktopWslServerTree.layerTest()), | 851 | Layer.provideMerge(DesktopWslServerTree.layerTest()), |
| 852 | Layer.provideMerge(DesktopWslEnvironment.layerTest()), | 852 | Layer.provideMerge(DesktopWslEnvironment.layerTest()), |
| 853 | Layer.provideMerge( | 853 | Layer.provideMerge( |
| 854 | makeEnvironmentLayer(baseDir, { otlpLogsUrl: "http://env:4318/v1/logs" }), | 854 | layerEnvironment(baseDir, { otlpLogsUrl: "http://env:4318/v1/logs" }), |
| 855 | ), | 855 | ), |
| 856 | ), | 856 | ), |
| 857 | ), | 857 | ), |
| ... | @@ -877,7 +877,7 @@ describe("DesktopBackendConfiguration", () => { | ... | @@ -877,7 +877,7 @@ describe("DesktopBackendConfiguration", () => { |
| 877 | const logger = Logger.make(({ message }) => { | 877 | const logger = Logger.make(({ message }) => { |
| 878 | messages.push(message); | 878 | messages.push(message); |
| 879 | }); | 879 | }); |
| 880 | const failingFileSystemLayer = Layer.succeed( | 880 | const layerFailingFileSystem = Layer.succeed( |
| 881 | FileSystem.FileSystem, | 881 | FileSystem.FileSystem, |
| 882 | FileSystem.makeNoop({ | 882 | FileSystem.makeNoop({ |
| 883 | readFileString: () => Effect.fail(cause), | 883 | readFileString: () => Effect.fail(cause), |
| ... | @@ -891,12 +891,12 @@ describe("DesktopBackendConfiguration", () => { | ... | @@ -891,12 +891,12 @@ describe("DesktopBackendConfiguration", () => { |
| 891 | Effect.provide( | 891 | Effect.provide( |
| 892 | Layer.mergeAll( | 892 | Layer.mergeAll( |
| 893 | DesktopBackendConfiguration.layer.pipe( | 893 | DesktopBackendConfiguration.layer.pipe( |
| 894 | Layer.provideMerge(serverExposureLayer), | 894 | Layer.provideMerge(layerServerExposure), |
| 895 | Layer.provideMerge(DesktopAppSettings.layerTest()), | 895 | Layer.provideMerge(DesktopAppSettings.layerTest()), |
| 896 | Layer.provideMerge(DesktopWslServerTree.layerTest()), | 896 | Layer.provideMerge(DesktopWslServerTree.layerTest()), |
| 897 | Layer.provideMerge(DesktopWslEnvironment.layerTest()), | 897 | Layer.provideMerge(DesktopWslEnvironment.layerTest()), |
| 898 | Layer.provideMerge(makeEnvironmentLayer(baseDir)), | 898 | Layer.provideMerge(layerEnvironment(baseDir)), |
| 899 | Layer.provideMerge(failingFileSystemLayer), | 899 | Layer.provideMerge(layerFailingFileSystem), |
| 900 | ), | 900 | ), |
| 901 | Logger.layer([logger], { mergeWithExisting: false }), | 901 | Logger.layer([logger], { mergeWithExisting: false }), |
| 902 | ), | 902 | ), |
| ... | @@ -936,12 +936,12 @@ describe("DesktopBackendConfiguration", () => { | ... | @@ -936,12 +936,12 @@ describe("DesktopBackendConfiguration", () => { |
| 936 | }).pipe( | 936 | }).pipe( |
| 937 | Effect.provide( | 937 | Effect.provide( |
| 938 | DesktopBackendConfiguration.layer.pipe( | 938 | DesktopBackendConfiguration.layer.pipe( |
| 939 | Layer.provideMerge(serverExposureLayer), | 939 | Layer.provideMerge(layerServerExposure), |
| 940 | Layer.provideMerge(DesktopAppSettings.layerTest()), | 940 | Layer.provideMerge(DesktopAppSettings.layerTest()), |
| 941 | Layer.provideMerge(DesktopWslServerTree.layerTest()), | 941 | Layer.provideMerge(DesktopWslServerTree.layerTest()), |
| 942 | Layer.provideMerge(DesktopWslEnvironment.layerTest()), | 942 | Layer.provideMerge(DesktopWslEnvironment.layerTest()), |
| 943 | Layer.provideMerge( | 943 | Layer.provideMerge( |
| 944 | makeEnvironmentLayer(baseDir, { | 944 | layerEnvironment(baseDir, { |
| 945 | isPackaged: false, | 945 | isPackaged: false, |
| 946 | devServerUrl: "http://127.0.0.1:5733", | 946 | devServerUrl: "http://127.0.0.1:5733", |
| 947 | }), | 947 | }), |
| ... | @@ -974,7 +974,7 @@ describe("DesktopBackendConfiguration", () => { | ... | @@ -974,7 +974,7 @@ describe("DesktopBackendConfiguration", () => { |
| 974 | }).pipe( | 974 | }).pipe( |
| 975 | Effect.provide( | 975 | Effect.provide( |
| 976 | DesktopBackendConfiguration.layer.pipe( | 976 | DesktopBackendConfiguration.layer.pipe( |
| 977 | Layer.provideMerge(serverExposureLayer), | 977 | Layer.provideMerge(layerServerExposure), |
| 978 | Layer.provideMerge(DesktopAppSettings.layerTest()), | 978 | Layer.provideMerge(DesktopAppSettings.layerTest()), |
| 979 | Layer.provideMerge(DesktopWslServerTree.layerTest()), | 979 | Layer.provideMerge(DesktopWslServerTree.layerTest()), |
| 980 | Layer.provideMerge( | 980 | Layer.provideMerge( |
| ... | @@ -984,7 +984,7 @@ describe("DesktopBackendConfiguration", () => { | ... | @@ -984,7 +984,7 @@ describe("DesktopBackendConfiguration", () => { |
| 984 | getDistroIp: () => Option.some("172.27.0.99"), | 984 | getDistroIp: () => Option.some("172.27.0.99"), |
| 985 | }), | 985 | }), |
| 986 | ), | 986 | ), |
| 987 | Layer.provideMerge(makeEnvironmentLayer(baseDir, { platform: "win32" })), | 987 | Layer.provideMerge(layerEnvironment(baseDir, { platform: "win32" })), |
| 988 | ), | 988 | ), |
| 989 | ), | 989 | ), |
| 990 | ); | 990 | ); |
| ... | @@ -1040,7 +1040,7 @@ describe("DesktopBackendConfiguration", () => { | ... | @@ -1040,7 +1040,7 @@ describe("DesktopBackendConfiguration", () => { |
| 1040 | }).pipe( | 1040 | }).pipe( |
| 1041 | Effect.provide( | 1041 | Effect.provide( |
| 1042 | DesktopBackendConfiguration.layer.pipe( | 1042 | DesktopBackendConfiguration.layer.pipe( |
| 1043 | Layer.provideMerge(serverExposureLayer), | 1043 | Layer.provideMerge(layerServerExposure), |
| 1044 | Layer.provideMerge(DesktopAppSettings.layerTest()), | 1044 | Layer.provideMerge(DesktopAppSettings.layerTest()), |
| 1045 | Layer.provideMerge(DesktopWslServerTree.layerTest()), | 1045 | Layer.provideMerge(DesktopWslServerTree.layerTest()), |
| 1046 | Layer.provideMerge( | 1046 | Layer.provideMerge( |
| ... | @@ -1050,7 +1050,7 @@ describe("DesktopBackendConfiguration", () => { | ... | @@ -1050,7 +1050,7 @@ describe("DesktopBackendConfiguration", () => { |
| 1050 | getDistroIp: () => Option.some("172.27.0.99"), | 1050 | getDistroIp: () => Option.some("172.27.0.99"), |
| 1051 | }), | 1051 | }), |
| 1052 | ), | 1052 | ), |
| 1053 | Layer.provideMerge(makeEnvironmentLayer(baseDir, { platform: "win32" })), | 1053 | Layer.provideMerge(layerEnvironment(baseDir, { platform: "win32" })), |
| 1054 | ), | 1054 | ), |
| 1055 | ), | 1055 | ), |
| 1056 | ); | 1056 | ); |
| ... | @@ -1114,7 +1114,7 @@ describe("DesktopBackendConfiguration", () => { | ... | @@ -1114,7 +1114,7 @@ describe("DesktopBackendConfiguration", () => { |
| 1114 | }).pipe( | 1114 | }).pipe( |
| 1115 | Effect.provide( | 1115 | Effect.provide( |
| 1116 | DesktopBackendConfiguration.layer.pipe( | 1116 | DesktopBackendConfiguration.layer.pipe( |
| 1117 | Layer.provideMerge(serverExposureLayer), | 1117 | Layer.provideMerge(layerServerExposure), |
| 1118 | Layer.provideMerge(DesktopAppSettings.layerTest()), | 1118 | Layer.provideMerge(DesktopAppSettings.layerTest()), |
| 1119 | Layer.provideMerge(DesktopWslServerTree.layerTest()), | 1119 | Layer.provideMerge(DesktopWslServerTree.layerTest()), |
| 1120 | Layer.provideMerge( | 1120 | Layer.provideMerge( |
| ... | @@ -1124,7 +1124,7 @@ describe("DesktopBackendConfiguration", () => { | ... | @@ -1124,7 +1124,7 @@ describe("DesktopBackendConfiguration", () => { |
| 1124 | getDistroIp: () => Option.some("172.27.0.99"), | 1124 | getDistroIp: () => Option.some("172.27.0.99"), |
| 1125 | }), | 1125 | }), |
| 1126 | ), | 1126 | ), |
| 1127 | Layer.provideMerge(makeEnvironmentLayer(baseDir, { platform: "win32" })), | 1127 | Layer.provideMerge(layerEnvironment(baseDir, { platform: "win32" })), |
| 1128 | ), | 1128 | ), |
| 1129 | ), | 1129 | ), |
| 1130 | ); | 1130 | ); |
| ... | @@ -1162,7 +1162,7 @@ describe("DesktopBackendConfiguration", () => { | ... | @@ -1162,7 +1162,7 @@ describe("DesktopBackendConfiguration", () => { |
| 1162 | }).pipe( | 1162 | }).pipe( |
| 1163 | Effect.provide( | 1163 | Effect.provide( |
| 1164 | DesktopBackendConfiguration.layer.pipe( | 1164 | DesktopBackendConfiguration.layer.pipe( |
| 1165 | Layer.provideMerge(serverExposureLayer), | 1165 | Layer.provideMerge(layerServerExposure), |
| 1166 | Layer.provideMerge( | 1166 | Layer.provideMerge( |
| 1167 | DesktopAppSettings.layerTest({ | 1167 | DesktopAppSettings.layerTest({ |
| 1168 | ...DesktopAppSettings.DEFAULT_DESKTOP_SETTINGS, | 1168 | ...DesktopAppSettings.DEFAULT_DESKTOP_SETTINGS, |
| ... | @@ -1172,7 +1172,7 @@ describe("DesktopBackendConfiguration", () => { | ... | @@ -1172,7 +1172,7 @@ describe("DesktopBackendConfiguration", () => { |
| 1172 | ), | 1172 | ), |
| 1173 | Layer.provideMerge(DesktopWslServerTree.layerTest()), | 1173 | Layer.provideMerge(DesktopWslServerTree.layerTest()), |
| 1174 | Layer.provideMerge(DesktopWslEnvironment.layerTest({ isAvailable: false })), | 1174 | Layer.provideMerge(DesktopWslEnvironment.layerTest({ isAvailable: false })), |
| 1175 | Layer.provideMerge(makeEnvironmentLayer(baseDir, { platform: "win32" })), | 1175 | Layer.provideMerge(layerEnvironment(baseDir, { platform: "win32" })), |
| 1176 | ), | 1176 | ), |
| 1177 | ), | 1177 | ), |
| 1178 | ); | 1178 | ); |
| ... | @@ -1199,7 +1199,7 @@ describe("DesktopBackendConfiguration", () => { | ... | @@ -1199,7 +1199,7 @@ describe("DesktopBackendConfiguration", () => { |
| 1199 | }).pipe( | 1199 | }).pipe( |
| 1200 | Effect.provide( | 1200 | Effect.provide( |
| 1201 | DesktopBackendConfiguration.layer.pipe( | 1201 | DesktopBackendConfiguration.layer.pipe( |
| 1202 | Layer.provideMerge(serverExposureLayer), | 1202 | Layer.provideMerge(layerServerExposure), |
| 1203 | Layer.provideMerge( | 1203 | Layer.provideMerge( |
| 1204 | DesktopAppSettings.layerTest({ | 1204 | DesktopAppSettings.layerTest({ |
| 1205 | ...DesktopAppSettings.DEFAULT_DESKTOP_SETTINGS, | 1205 | ...DesktopAppSettings.DEFAULT_DESKTOP_SETTINGS, |
| ... | @@ -1215,7 +1215,7 @@ describe("DesktopBackendConfiguration", () => { | ... | @@ -1215,7 +1215,7 @@ describe("DesktopBackendConfiguration", () => { |
| 1215 | distros: [{ name: "Ubuntu", isDefault: true, version: 2 }], | 1215 | distros: [{ name: "Ubuntu", isDefault: true, version: 2 }], |
| 1216 | }), | 1216 | }), |
| 1217 | ), | 1217 | ), |
| 1218 | Layer.provideMerge(makeEnvironmentLayer(baseDir, { platform: "win32" })), | 1218 | Layer.provideMerge(layerEnvironment(baseDir, { platform: "win32" })), |
| 1219 | ), | 1219 | ), |
| 1220 | ), | 1220 | ), |
| 1221 | ); | 1221 | ); |
| ... | @@ -1240,7 +1240,7 @@ describe("DesktopBackendConfiguration", () => { | ... | @@ -1240,7 +1240,7 @@ describe("DesktopBackendConfiguration", () => { |
| 1240 | }).pipe( | 1240 | }).pipe( |
| 1241 | Effect.provide( | 1241 | Effect.provide( |
| 1242 | DesktopBackendConfiguration.layer.pipe( | 1242 | DesktopBackendConfiguration.layer.pipe( |
| 1243 | Layer.provideMerge(serverExposureLayer), | 1243 | Layer.provideMerge(layerServerExposure), |
| 1244 | Layer.provideMerge(DesktopAppSettings.layerTest()), | 1244 | Layer.provideMerge(DesktopAppSettings.layerTest()), |
| 1245 | Layer.provideMerge(DesktopWslServerTree.layerTest()), | 1245 | Layer.provideMerge(DesktopWslServerTree.layerTest()), |
| 1246 | Layer.provideMerge( | 1246 | Layer.provideMerge( |
| ... | @@ -1251,7 +1251,7 @@ describe("DesktopBackendConfiguration", () => { | ... | @@ -1251,7 +1251,7 @@ describe("DesktopBackendConfiguration", () => { |
| 1251 | }), | 1251 | }), |
| 1252 | }), | 1252 | }), |
| 1253 | ), | 1253 | ), |
| 1254 | Layer.provideMerge(makeEnvironmentLayer(baseDir, { platform: "win32" })), | 1254 | Layer.provideMerge(layerEnvironment(baseDir, { platform: "win32" })), |
| 1255 | ), | 1255 | ), |
| 1256 | ), | 1256 | ), |
| 1257 | ); | 1257 | ); |
| ... | @@ -1275,7 +1275,7 @@ describe("DesktopBackendConfiguration", () => { | ... | @@ -1275,7 +1275,7 @@ describe("DesktopBackendConfiguration", () => { |
| 1275 | }).pipe( | 1275 | }).pipe( |
| 1276 | Effect.provide( | 1276 | Effect.provide( |
| 1277 | DesktopBackendConfiguration.layer.pipe( | 1277 | DesktopBackendConfiguration.layer.pipe( |
| 1278 | Layer.provideMerge(serverExposureLayer), | 1278 | Layer.provideMerge(layerServerExposure), |
| 1279 | Layer.provideMerge(DesktopAppSettings.layerTest()), | 1279 | Layer.provideMerge(DesktopAppSettings.layerTest()), |
| 1280 | Layer.provideMerge(DesktopWslServerTree.layerTest()), | 1280 | Layer.provideMerge(DesktopWslServerTree.layerTest()), |
| 1281 | Layer.provideMerge( | 1281 | Layer.provideMerge( |
| ... | @@ -1284,7 +1284,7 @@ describe("DesktopBackendConfiguration", () => { | ... | @@ -1284,7 +1284,7 @@ describe("DesktopBackendConfiguration", () => { |
| 1284 | distros: [{ name: "Ubuntu", isDefault: true, version: 2 }], | 1284 | distros: [{ name: "Ubuntu", isDefault: true, version: 2 }], |
| 1285 | }), | 1285 | }), |
| 1286 | ), | 1286 | ), |
| 1287 | Layer.provideMerge(makeEnvironmentLayer(baseDir, { platform: "win32" })), | 1287 | Layer.provideMerge(layerEnvironment(baseDir, { platform: "win32" })), |
| 1288 | ), | 1288 | ), |
| 1289 | ), | 1289 | ), |
| 1290 | ); | 1290 | ); |
| ... | @@ -1309,7 +1309,7 @@ describe("DesktopBackendConfiguration", () => { | ... | @@ -1309,7 +1309,7 @@ describe("DesktopBackendConfiguration", () => { |
| 1309 | }).pipe( | 1309 | }).pipe( |
| 1310 | Effect.provide( | 1310 | Effect.provide( |
| 1311 | DesktopBackendConfiguration.layer.pipe( | 1311 | DesktopBackendConfiguration.layer.pipe( |
| 1312 | Layer.provideMerge(serverExposureLayer), | 1312 | Layer.provideMerge(layerServerExposure), |
| 1313 | Layer.provideMerge(DesktopAppSettings.layerTest()), | 1313 | Layer.provideMerge(DesktopAppSettings.layerTest()), |
| 1314 | Layer.provideMerge( | 1314 | Layer.provideMerge( |
| 1315 | DesktopWslServerTree.layerTest({ | 1315 | DesktopWslServerTree.layerTest({ |
| ... | @@ -1326,7 +1326,7 @@ describe("DesktopBackendConfiguration", () => { | ... | @@ -1326,7 +1326,7 @@ describe("DesktopBackendConfiguration", () => { |
| 1326 | distros: [{ name: "Ubuntu", isDefault: true, version: 2 }], | 1326 | distros: [{ name: "Ubuntu", isDefault: true, version: 2 }], |
| 1327 | }), | 1327 | }), |
| 1328 | ), | 1328 | ), |
| 1329 | Layer.provideMerge(makeEnvironmentLayer(baseDir, { platform: "win32" })), | 1329 | Layer.provideMerge(layerEnvironment(baseDir, { platform: "win32" })), |
| 1330 | ), | 1330 | ), |
| 1331 | ), | 1331 | ), |
| 1332 | ); | 1332 | ); |
| ... | @@ -1350,7 +1350,7 @@ describe("DesktopBackendConfiguration", () => { | ... | @@ -1350,7 +1350,7 @@ describe("DesktopBackendConfiguration", () => { |
| 1350 | }).pipe( | 1350 | }).pipe( |
| 1351 | Effect.provide( | 1351 | Effect.provide( |
| 1352 | DesktopBackendConfiguration.layer.pipe( | 1352 | DesktopBackendConfiguration.layer.pipe( |
| 1353 | Layer.provideMerge(serverExposureLayer), | 1353 | Layer.provideMerge(layerServerExposure), |
| 1354 | Layer.provideMerge(DesktopAppSettings.layerTest()), | 1354 | Layer.provideMerge(DesktopAppSettings.layerTest()), |
| 1355 | Layer.provideMerge(DesktopWslServerTree.layerTest()), | 1355 | Layer.provideMerge(DesktopWslServerTree.layerTest()), |
| 1356 | Layer.provideMerge( | 1356 | Layer.provideMerge( |
| ... | @@ -1359,7 +1359,7 @@ describe("DesktopBackendConfiguration", () => { | ... | @@ -1359,7 +1359,7 @@ describe("DesktopBackendConfiguration", () => { |
| 1359 | distros: [{ name: "Ubuntu", isDefault: true, version: 2 }], | 1359 | distros: [{ name: "Ubuntu", isDefault: true, version: 2 }], |
| 1360 | }), | 1360 | }), |
| 1361 | ), | 1361 | ), |
| 1362 | Layer.provideMerge(makeEnvironmentLayer(baseDir, { platform: "win32" })), | 1362 | Layer.provideMerge(layerEnvironment(baseDir, { platform: "win32" })), |
| 1363 | ), | 1363 | ), |
| 1364 | ), | 1364 | ), |
| 1365 | ); | 1365 | ); |
| ... | @@ -1380,7 +1380,7 @@ describe("DesktopBackendConfiguration", () => { | ... | @@ -1380,7 +1380,7 @@ describe("DesktopBackendConfiguration", () => { |
| 1380 | }).pipe( | 1380 | }).pipe( |
| 1381 | Effect.provide( | 1381 | Effect.provide( |
| 1382 | DesktopBackendConfiguration.layer.pipe( | 1382 | DesktopBackendConfiguration.layer.pipe( |
| 1383 | Layer.provideMerge(serverExposureLayer), | 1383 | Layer.provideMerge(layerServerExposure), |
| 1384 | Layer.provideMerge( | 1384 | Layer.provideMerge( |
| 1385 | DesktopAppSettings.layerTest({ | 1385 | DesktopAppSettings.layerTest({ |
| 1386 | ...DesktopAppSettings.DEFAULT_DESKTOP_SETTINGS, | 1386 | ...DesktopAppSettings.DEFAULT_DESKTOP_SETTINGS, |
| ... | @@ -1391,7 +1391,7 @@ describe("DesktopBackendConfiguration", () => { | ... | @@ -1391,7 +1391,7 @@ describe("DesktopBackendConfiguration", () => { |
| 1391 | ), | 1391 | ), |
| 1392 | Layer.provideMerge(DesktopWslServerTree.layerTest()), | 1392 | Layer.provideMerge(DesktopWslServerTree.layerTest()), |
| 1393 | Layer.provideMerge(DesktopWslEnvironment.layerTest({ isAvailable: true })), | 1393 | Layer.provideMerge(DesktopWslEnvironment.layerTest({ isAvailable: true })), |
| 1394 | Layer.provideMerge(makeEnvironmentLayer(baseDir, { platform: "win32" })), | 1394 | Layer.provideMerge(layerEnvironment(baseDir, { platform: "win32" })), |
| 1395 | ), | 1395 | ), |
| 1396 | ), | 1396 | ), |
| 1397 | ); | 1397 | ); |
| ... | @@ -1429,12 +1429,12 @@ describe("DesktopBackendConfiguration", () => { | ... | @@ -1429,12 +1429,12 @@ describe("DesktopBackendConfiguration", () => { |
| 1429 | }).pipe( | 1429 | }).pipe( |
| 1430 | Effect.provide( | 1430 | Effect.provide( |
| 1431 | DesktopBackendConfiguration.layer.pipe( | 1431 | DesktopBackendConfiguration.layer.pipe( |
| 1432 | Layer.provideMerge(serverExposureLayer), | 1432 | Layer.provideMerge(layerServerExposure), |
| 1433 | Layer.provideMerge(DesktopAppSettings.layerTest()), | 1433 | Layer.provideMerge(DesktopAppSettings.layerTest()), |
| 1434 | Layer.provideMerge(DesktopWslServerTree.layerTest()), | 1434 | Layer.provideMerge(DesktopWslServerTree.layerTest()), |
| 1435 | Layer.provideMerge(DesktopWslEnvironment.layerTest()), | 1435 | Layer.provideMerge(DesktopWslEnvironment.layerTest()), |
| 1436 | Layer.provideMerge( | 1436 | Layer.provideMerge( |
| 1437 | makeEnvironmentLayer(baseDir, { | 1437 | layerEnvironment(baseDir, { |
| 1438 | appPath: `${resourcesPath}/app.asar`, | 1438 | appPath: `${resourcesPath}/app.asar`, |
| 1439 | dirname, | 1439 | dirname, |
| 1440 | isPackaged: true, | 1440 | isPackaged: true, |
| ... | @@ -1475,12 +1475,12 @@ describe("DesktopBackendConfiguration", () => { | ... | @@ -1475,12 +1475,12 @@ describe("DesktopBackendConfiguration", () => { |
| 1475 | }).pipe( | 1475 | }).pipe( |
| 1476 | Effect.provide( | 1476 | Effect.provide( |
| 1477 | DesktopBackendConfiguration.layer.pipe( | 1477 | DesktopBackendConfiguration.layer.pipe( |
| 1478 | Layer.provideMerge(serverExposureLayer), | 1478 | Layer.provideMerge(layerServerExposure), |
| 1479 | Layer.provideMerge(DesktopAppSettings.layerTest()), | 1479 | Layer.provideMerge(DesktopAppSettings.layerTest()), |
| 1480 | Layer.provideMerge(DesktopWslServerTree.layerTest()), | 1480 | Layer.provideMerge(DesktopWslServerTree.layerTest()), |
| 1481 | Layer.provideMerge(DesktopWslEnvironment.layerTest()), | 1481 | Layer.provideMerge(DesktopWslEnvironment.layerTest()), |
| 1482 | Layer.provideMerge( | 1482 | Layer.provideMerge( |
| 1483 | makeEnvironmentLayer(baseDir, { | 1483 | layerEnvironment(baseDir, { |
| 1484 | dirname, | 1484 | dirname, |
| 1485 | devServerUrl: "http://127.0.0.1:5733", | 1485 | devServerUrl: "http://127.0.0.1:5733", |
| 1486 | isPackaged: false, | 1486 | isPackaged: false, |
| ... | @@ -1519,7 +1519,7 @@ describe("DesktopBackendConfiguration", () => { | ... | @@ -1519,7 +1519,7 @@ describe("DesktopBackendConfiguration", () => { |
| 1519 | }).pipe( | 1519 | }).pipe( |
| 1520 | Effect.provide( | 1520 | Effect.provide( |
| 1521 | DesktopBackendConfiguration.layer.pipe( | 1521 | DesktopBackendConfiguration.layer.pipe( |
| 1522 | Layer.provideMerge(serverExposureLayer), | 1522 | Layer.provideMerge(layerServerExposure), |
| 1523 | Layer.provideMerge( | 1523 | Layer.provideMerge( |
| 1524 | DesktopAppSettings.layerTest({ | 1524 | DesktopAppSettings.layerTest({ |
| 1525 | ...DesktopAppSettings.DEFAULT_DESKTOP_SETTINGS, | 1525 | ...DesktopAppSettings.DEFAULT_DESKTOP_SETTINGS, |
| ... | @@ -1530,7 +1530,7 @@ describe("DesktopBackendConfiguration", () => { | ... | @@ -1530,7 +1530,7 @@ describe("DesktopBackendConfiguration", () => { |
| 1530 | ), | 1530 | ), |
| 1531 | Layer.provideMerge(DesktopWslServerTree.layerTest()), | 1531 | Layer.provideMerge(DesktopWslServerTree.layerTest()), |
| 1532 | Layer.provideMerge(DesktopWslEnvironment.layerTest({ isAvailable: false })), | 1532 | Layer.provideMerge(DesktopWslEnvironment.layerTest({ isAvailable: false })), |
| 1533 | Layer.provideMerge(makeEnvironmentLayer(baseDir, { platform: "win32" })), | 1533 | Layer.provideMerge(layerEnvironment(baseDir, { platform: "win32" })), |
| 1534 | ), | 1534 | ), |
| 1535 | ), | 1535 | ), |
| 1536 | ); | 1536 | ); |
| ... | @@ -1548,7 +1548,7 @@ describe("DesktopBackendConfiguration", () => { | ... | @@ -1548,7 +1548,7 @@ describe("DesktopBackendConfiguration", () => { |
| 1548 | // oxlint-disable-next-line t3code/no-manual-effect-runtime-in-tests -- This test intentionally replicates the sync IPC handler's runSync path to catch a regression to async-only resolution; it.effect would mask it. | 1548 | // oxlint-disable-next-line t3code/no-manual-effect-runtime-in-tests -- This test intentionally replicates the sync IPC handler's runSync path to catch a regression to async-only resolution; it.effect would mask it. |
| 1549 | const runtime = ManagedRuntime.make( | 1549 | const runtime = ManagedRuntime.make( |
| 1550 | DesktopBackendConfiguration.layer.pipe( | 1550 | DesktopBackendConfiguration.layer.pipe( |
| 1551 | Layer.provideMerge(serverExposureLayer), | 1551 | Layer.provideMerge(layerServerExposure), |
| 1552 | Layer.provideMerge(DesktopAppSettings.layerTest()), | 1552 | Layer.provideMerge(DesktopAppSettings.layerTest()), |
| 1553 | Layer.provideMerge(DesktopWslServerTree.layerTest()), | 1553 | Layer.provideMerge(DesktopWslServerTree.layerTest()), |
| 1554 | Layer.provideMerge(DesktopWslEnvironment.layer), | 1554 | Layer.provideMerge(DesktopWslEnvironment.layer), |
| ... | @@ -1562,7 +1562,7 @@ describe("DesktopBackendConfiguration", () => { | ... | @@ -1562,7 +1562,7 @@ describe("DesktopBackendConfiguration", () => { |
| 1562 | ), | 1562 | ), |
| 1563 | ), | 1563 | ), |
| 1564 | ), | 1564 | ), |
| 1565 | Layer.provideMerge(makeEnvironmentLayer("/tmp/t3-wsl-isavailable", { platform: "win32" })), | 1565 | Layer.provideMerge(layerEnvironment("/tmp/t3-wsl-isavailable", { platform: "win32" })), |
| 1566 | Layer.provide(NodeServices.layer), | 1566 | Layer.provide(NodeServices.layer), |
| 1567 | ), | 1567 | ), |
| 1568 | ); | 1568 | ); |
apps/desktop/src/backend/DesktopBackendManager.test.ts+67-67| ... | @@ -98,7 +98,7 @@ function responseForRequest( | ... | @@ -98,7 +98,7 @@ function responseForRequest( |
| 98 | return HttpClientResponse.fromWeb(request, new Response(null, { status })); | 98 | return HttpClientResponse.fromWeb(request, new Response(null, { status })); |
| 99 | } | 99 | } |
| 100 | 100 | ||
| 101 | function httpClientLayer( | 101 | function layerHttpClient( |
| 102 | handler: ( | 102 | handler: ( |
| 103 | request: HttpClientRequest.HttpClientRequest, | 103 | request: HttpClientRequest.HttpClientRequest, |
| 104 | ) => Effect.Effect<HttpClientResponse.HttpClientResponse>, | 104 | ) => Effect.Effect<HttpClientResponse.HttpClientResponse>, |
| ... | @@ -109,7 +109,7 @@ function httpClientLayer( | ... | @@ -109,7 +109,7 @@ function httpClientLayer( |
| 109 | ); | 109 | ); |
| 110 | } | 110 | } |
| 111 | 111 | ||
| 112 | const healthyHttpClientLayer = httpClientLayer((request) => | 112 | const layerHealthyHttpClient = layerHttpClient((request) => |
| 113 | Effect.succeed(responseForRequest(request, 200)), | 113 | Effect.succeed(responseForRequest(request, 200)), |
| 114 | ); | 114 | ); |
| 115 | 115 | ||
| ... | @@ -152,12 +152,12 @@ function makeTestInstance(input: MakeInstanceInput) { | ... | @@ -152,12 +152,12 @@ function makeTestInstance(input: MakeInstanceInput) { |
| 152 | discardSession: Effect.void, | 152 | discardSession: Effect.void, |
| 153 | ...input.backendOutputLog, | 153 | ...input.backendOutputLog, |
| 154 | }; | 154 | }; |
| 155 | const servicesLayer = Layer.mergeAll( | 155 | const layerServices = Layer.mergeAll( |
| 156 | FileSystem.layerNoop({ | 156 | FileSystem.layerNoop({ |
| 157 | exists: () => Effect.succeed(true), | 157 | exists: () => Effect.succeed(true), |
| 158 | }), | 158 | }), |
| 159 | input.spawnerLayer, | 159 | input.spawnerLayer, |
| 160 | input.httpClientLayer ?? healthyHttpClientLayer, | 160 | input.httpClientLayer ?? layerHealthyHttpClient, |
| 161 | Layer.succeed(DesktopObservability.DesktopBackendOutputLogFactory, { | 161 | Layer.succeed(DesktopObservability.DesktopBackendOutputLogFactory, { |
| 162 | forInstance: () => Effect.succeed(stubLog), | 162 | forInstance: () => Effect.succeed(stubLog), |
| 163 | } satisfies DesktopObservability.DesktopBackendOutputLogFactory["Service"]), | 163 | } satisfies DesktopObservability.DesktopBackendOutputLogFactory["Service"]), |
| ... | @@ -187,7 +187,7 @@ function makeTestInstance(input: MakeInstanceInput) { | ... | @@ -187,7 +187,7 @@ function makeTestInstance(input: MakeInstanceInput) { |
| 187 | ...(input.onPreflightFailed ? { onPreflightFailed: input.onPreflightFailed } : {}), | 187 | ...(input.onPreflightFailed ? { onPreflightFailed: input.onPreflightFailed } : {}), |
| 188 | }); | 188 | }); |
| 189 | 189 | ||
| 190 | return instance.pipe(Effect.provide(servicesLayer)); | 190 | return instance.pipe(Effect.provide(layerServices)); |
| 191 | } | 191 | } |
| 192 | 192 | ||
| 193 | describe("DesktopBackendManager", () => { | 193 | describe("DesktopBackendManager", () => { |
| ... | @@ -201,7 +201,7 @@ describe("DesktopBackendManager", () => { | ... | @@ -201,7 +201,7 @@ describe("DesktopBackendManager", () => { |
| 201 | const ready = yield* Deferred.make<void>(); | 201 | const ready = yield* Deferred.make<void>(); |
| 202 | const exited = yield* Queue.unbounded<void>(); | 202 | const exited = yield* Queue.unbounded<void>(); |
| 203 | 203 | ||
| 204 | const spawnerLayer = Layer.succeed( | 204 | const layerSpawner = Layer.succeed( |
| 205 | ChildProcessSpawner.ChildProcessSpawner, | 205 | ChildProcessSpawner.ChildProcessSpawner, |
| 206 | ChildProcessSpawner.make((command) => | 206 | ChildProcessSpawner.make((command) => |
| 207 | Effect.gen(function* () { | 207 | Effect.gen(function* () { |
| ... | @@ -229,7 +229,7 @@ describe("DesktopBackendManager", () => { | ... | @@ -229,7 +229,7 @@ describe("DesktopBackendManager", () => { |
| 229 | ...baseConfig, | 229 | ...baseConfig, |
| 230 | bootstrap: configWithObservability, | 230 | bootstrap: configWithObservability, |
| 231 | }, | 231 | }, |
| 232 | spawnerLayer, | 232 | spawnerLayer: layerSpawner, |
| 233 | desktopTelemetryStream: Stream.encodeText( | 233 | desktopTelemetryStream: Stream.encodeText( |
| 234 | Stream.make('{"version":1,"type":"desktopTelemetryHello","electronPid":123}\n'), | 234 | Stream.make('{"version":1,"type":"desktopTelemetryHello","electronPid":123}\n'), |
| 235 | ), | 235 | ), |
| ... | @@ -279,7 +279,7 @@ describe("DesktopBackendManager", () => { | ... | @@ -279,7 +279,7 @@ describe("DesktopBackendManager", () => { |
| 279 | const requested = yield* Deferred.make<HttpClientRequest.HttpClientRequest>(); | 279 | const requested = yield* Deferred.make<HttpClientRequest.HttpClientRequest>(); |
| 280 | const layer = Layer.merge( | 280 | const layer = Layer.merge( |
| 281 | TestClock.layer(), | 281 | TestClock.layer(), |
| 282 | httpClientLayer((request) => | 282 | layerHttpClient((request) => |
| 283 | Deferred.succeed(requested, request).pipe(Effect.andThen(Effect.never)), | 283 | Deferred.succeed(requested, request).pipe(Effect.andThen(Effect.never)), |
| 284 | ), | 284 | ), |
| 285 | ); | 285 | ); |
| ... | @@ -317,7 +317,7 @@ describe("DesktopBackendManager", () => { | ... | @@ -317,7 +317,7 @@ describe("DesktopBackendManager", () => { |
| 317 | 317 | ||
| 318 | it.effect("reports bootstrap encoding failures with stable process context", () => | 318 | it.effect("reports bootstrap encoding failures with stable process context", () => |
| 319 | Effect.gen(function* () { | 319 | Effect.gen(function* () { |
| 320 | const spawnerLayer = Layer.succeed( | 320 | const layerSpawner = Layer.succeed( |
| 321 | ChildProcessSpawner.ChildProcessSpawner, | 321 | ChildProcessSpawner.ChildProcessSpawner, |
| 322 | ChildProcessSpawner.make(() => Effect.die("unexpected backend spawn")), | 322 | ChildProcessSpawner.make(() => Effect.die("unexpected backend spawn")), |
| 323 | ); | 323 | ); |
| ... | @@ -331,7 +331,7 @@ describe("DesktopBackendManager", () => { | ... | @@ -331,7 +331,7 @@ describe("DesktopBackendManager", () => { |
| 331 | }).pipe( | 331 | }).pipe( |
| 332 | Effect.flip, | 332 | Effect.flip, |
| 333 | Effect.scoped, | 333 | Effect.scoped, |
| 334 | Effect.provide(Layer.merge(spawnerLayer, healthyHttpClientLayer)), | 334 | Effect.provide(Layer.merge(layerSpawner, layerHealthyHttpClient)), |
| 335 | ); | 335 | ); |
| 336 | 336 | ||
| 337 | if (error._tag !== "BackendProcessBootstrapEncodeError") { | 337 | if (error._tag !== "BackendProcessBootstrapEncodeError") { |
| ... | @@ -359,7 +359,7 @@ describe("DesktopBackendManager", () => { | ... | @@ -359,7 +359,7 @@ describe("DesktopBackendManager", () => { |
| 359 | pathOrDescriptor: baseConfig.executablePath, | 359 | pathOrDescriptor: baseConfig.executablePath, |
| 360 | description: "low-level detail that must not become the public message", | 360 | description: "low-level detail that must not become the public message", |
| 361 | }); | 361 | }); |
| 362 | const spawnerLayer = Layer.succeed( | 362 | const layerSpawner = Layer.succeed( |
| 363 | ChildProcessSpawner.ChildProcessSpawner, | 363 | ChildProcessSpawner.ChildProcessSpawner, |
| 364 | ChildProcessSpawner.make(() => Effect.fail(spawnCause)), | 364 | ChildProcessSpawner.make(() => Effect.fail(spawnCause)), |
| 365 | ); | 365 | ); |
| ... | @@ -369,7 +369,7 @@ describe("DesktopBackendManager", () => { | ... | @@ -369,7 +369,7 @@ describe("DesktopBackendManager", () => { |
| 369 | }).pipe( | 369 | }).pipe( |
| 370 | Effect.flip, | 370 | Effect.flip, |
| 371 | Effect.scoped, | 371 | Effect.scoped, |
| 372 | Effect.provide(Layer.merge(spawnerLayer, healthyHttpClientLayer)), | 372 | Effect.provide(Layer.merge(layerSpawner, layerHealthyHttpClient)), |
| 373 | ); | 373 | ); |
| 374 | 374 | ||
| 375 | if (error._tag !== "BackendProcessSpawnError") { | 375 | if (error._tag !== "BackendProcessSpawnError") { |
| ... | @@ -397,7 +397,7 @@ describe("DesktopBackendManager", () => { | ... | @@ -397,7 +397,7 @@ describe("DesktopBackendManager", () => { |
| 397 | method: "exitCode", | 397 | method: "exitCode", |
| 398 | description: "exit-status-secret-sentinel", | 398 | description: "exit-status-secret-sentinel", |
| 399 | }); | 399 | }); |
| 400 | const spawnerLayer = Layer.succeed( | 400 | const layerSpawner = Layer.succeed( |
| 401 | ChildProcessSpawner.ChildProcessSpawner, | 401 | ChildProcessSpawner.ChildProcessSpawner, |
| 402 | ChildProcessSpawner.make(() => | 402 | ChildProcessSpawner.make(() => |
| 403 | Effect.succeed( | 403 | Effect.succeed( |
| ... | @@ -413,7 +413,7 @@ describe("DesktopBackendManager", () => { | ... | @@ -413,7 +413,7 @@ describe("DesktopBackendManager", () => { |
| 413 | }).pipe( | 413 | }).pipe( |
| 414 | Effect.flip, | 414 | Effect.flip, |
| 415 | Effect.scoped, | 415 | Effect.scoped, |
| 416 | Effect.provide(Layer.merge(spawnerLayer, healthyHttpClientLayer)), | 416 | Effect.provide(Layer.merge(layerSpawner, layerHealthyHttpClient)), |
| 417 | ); | 417 | ); |
| 418 | 418 | ||
| 419 | if (error._tag !== "BackendProcessExitStatusError") { | 419 | if (error._tag !== "BackendProcessExitStatusError") { |
| ... | @@ -440,7 +440,7 @@ describe("DesktopBackendManager", () => { | ... | @@ -440,7 +440,7 @@ describe("DesktopBackendManager", () => { |
| 440 | description: "output-stream-secret-sentinel", | 440 | description: "output-stream-secret-sentinel", |
| 441 | }); | 441 | }); |
| 442 | const reported = yield* Deferred.make<DesktopBackendManager.BackendProcessOutputError>(); | 442 | const reported = yield* Deferred.make<DesktopBackendManager.BackendProcessOutputError>(); |
| 443 | const spawnerLayer = Layer.succeed( | 443 | const layerSpawner = Layer.succeed( |
| 444 | ChildProcessSpawner.ChildProcessSpawner, | 444 | ChildProcessSpawner.ChildProcessSpawner, |
| 445 | ChildProcessSpawner.make(() => | 445 | ChildProcessSpawner.make(() => |
| 446 | Effect.succeed( | 446 | Effect.succeed( |
| ... | @@ -456,7 +456,7 @@ describe("DesktopBackendManager", () => { | ... | @@ -456,7 +456,7 @@ describe("DesktopBackendManager", () => { |
| 456 | ...baseConfig, | 456 | ...baseConfig, |
| 457 | desktopTelemetryStream: Stream.empty, | 457 | desktopTelemetryStream: Stream.empty, |
| 458 | onOutputFailure: (error) => Deferred.succeed(reported, error).pipe(Effect.asVoid), | 458 | onOutputFailure: (error) => Deferred.succeed(reported, error).pipe(Effect.asVoid), |
| 459 | }).pipe(Effect.scoped, Effect.provide(Layer.merge(spawnerLayer, healthyHttpClientLayer))); | 459 | }).pipe(Effect.scoped, Effect.provide(Layer.merge(layerSpawner, layerHealthyHttpClient))); |
| 460 | const error = yield* Deferred.await(reported); | 460 | const error = yield* Deferred.await(reported); |
| 461 | 461 | ||
| 462 | assert.equal(exit.code.pipe(Option.getOrUndefined), 0); | 462 | assert.equal(exit.code.pipe(Option.getOrUndefined), 0); |
| ... | @@ -483,7 +483,7 @@ describe("DesktopBackendManager", () => { | ... | @@ -483,7 +483,7 @@ describe("DesktopBackendManager", () => { |
| 483 | const reported = yield* Deferred.make<DesktopBackendManager.BackendProcessOutputError>(); | 483 | const reported = yield* Deferred.make<DesktopBackendManager.BackendProcessOutputError>(); |
| 484 | const drained = yield* Deferred.make<void>(); | 484 | const drained = yield* Deferred.make<void>(); |
| 485 | let outputCount = 0; | 485 | let outputCount = 0; |
| 486 | const spawnerLayer = Layer.succeed( | 486 | const layerSpawner = Layer.succeed( |
| 487 | ChildProcessSpawner.ChildProcessSpawner, | 487 | ChildProcessSpawner.ChildProcessSpawner, |
| 488 | ChildProcessSpawner.make(() => | 488 | ChildProcessSpawner.make(() => |
| 489 | Effect.succeed( | 489 | Effect.succeed( |
| ... | @@ -505,7 +505,7 @@ describe("DesktopBackendManager", () => { | ... | @@ -505,7 +505,7 @@ describe("DesktopBackendManager", () => { |
| 505 | : Deferred.succeed(drained, void 0).pipe(Effect.asVoid); | 505 | : Deferred.succeed(drained, void 0).pipe(Effect.asVoid); |
| 506 | }, | 506 | }, |
| 507 | onOutputFailure: (error) => Deferred.succeed(reported, error).pipe(Effect.asVoid), | 507 | onOutputFailure: (error) => Deferred.succeed(reported, error).pipe(Effect.asVoid), |
| 508 | }).pipe(Effect.scoped, Effect.provide(Layer.merge(spawnerLayer, healthyHttpClientLayer))); | 508 | }).pipe(Effect.scoped, Effect.provide(Layer.merge(layerSpawner, layerHealthyHttpClient))); |
| 509 | const error = yield* Deferred.await(reported); | 509 | const error = yield* Deferred.await(reported); |
| 510 | 510 | ||
| 511 | assert.equal(exit.code.pipe(Option.getOrUndefined), 0); | 511 | assert.equal(exit.code.pipe(Option.getOrUndefined), 0); |
| ... | @@ -534,7 +534,7 @@ describe("DesktopBackendManager", () => { | ... | @@ -534,7 +534,7 @@ describe("DesktopBackendManager", () => { |
| 534 | Effect.gen(function* () { | 534 | Effect.gen(function* () { |
| 535 | const exitObserved = yield* Deferred.make<void>(); | 535 | const exitObserved = yield* Deferred.make<void>(); |
| 536 | const finishOutputDrain = yield* Deferred.make<void>(); | 536 | const finishOutputDrain = yield* Deferred.make<void>(); |
| 537 | const spawnerLayer = Layer.succeed( | 537 | const layerSpawner = Layer.succeed( |
| 538 | ChildProcessSpawner.ChildProcessSpawner, | 538 | ChildProcessSpawner.ChildProcessSpawner, |
| 539 | ChildProcessSpawner.make(() => | 539 | ChildProcessSpawner.make(() => |
| 540 | Effect.succeed( | 540 | Effect.succeed( |
| ... | @@ -555,7 +555,7 @@ describe("DesktopBackendManager", () => { | ... | @@ -555,7 +555,7 @@ describe("DesktopBackendManager", () => { |
| 555 | desktopTelemetryStream: Stream.empty, | 555 | desktopTelemetryStream: Stream.empty, |
| 556 | onExitObserved: () => Deferred.succeed(exitObserved, void 0).pipe(Effect.asVoid), | 556 | onExitObserved: () => Deferred.succeed(exitObserved, void 0).pipe(Effect.asVoid), |
| 557 | }).pipe( | 557 | }).pipe( |
| 558 | Effect.provide(Layer.merge(spawnerLayer, healthyHttpClientLayer)), | 558 | Effect.provide(Layer.merge(layerSpawner, layerHealthyHttpClient)), |
| 559 | Effect.forkChild, | 559 | Effect.forkChild, |
| 560 | ); | 560 | ); |
| 561 | 561 | ||
| ... | @@ -577,7 +577,7 @@ describe("DesktopBackendManager", () => { | ... | @@ -577,7 +577,7 @@ describe("DesktopBackendManager", () => { |
| 577 | type: "setDiagnosticsDemand", | 577 | type: "setDiagnosticsDemand", |
| 578 | enabled: true, | 578 | enabled: true, |
| 579 | }); | 579 | }); |
| 580 | const spawnerLayer = Layer.succeed( | 580 | const layerSpawner = Layer.succeed( |
| 581 | ChildProcessSpawner.ChildProcessSpawner, | 581 | ChildProcessSpawner.ChildProcessSpawner, |
| 582 | ChildProcessSpawner.make(() => | 582 | ChildProcessSpawner.make(() => |
| 583 | Effect.succeed( | 583 | Effect.succeed( |
| ... | @@ -592,7 +592,7 @@ describe("DesktopBackendManager", () => { | ... | @@ -592,7 +592,7 @@ describe("DesktopBackendManager", () => { |
| 592 | ), | 592 | ), |
| 593 | ); | 593 | ); |
| 594 | const instance = yield* makeTestInstance({ | 594 | const instance = yield* makeTestInstance({ |
| 595 | spawnerLayer, | 595 | spawnerLayer: layerSpawner, |
| 596 | desktopTelemetryPublisher: { | 596 | desktopTelemetryPublisher: { |
| 597 | handleControlForSource: (_sourceId, message) => | 597 | handleControlForSource: (_sourceId, message) => |
| 598 | message.type === "setDiagnosticsDemand" | 598 | message.type === "setDiagnosticsDemand" |
| ... | @@ -613,7 +613,7 @@ describe("DesktopBackendManager", () => { | ... | @@ -613,7 +613,7 @@ describe("DesktopBackendManager", () => { |
| 613 | const persistedOutput = yield* Deferred.make<ReadonlyArray<string>>(); | 613 | const persistedOutput = yield* Deferred.make<ReadonlyArray<string>>(); |
| 614 | const outputDrainStarted = yield* Deferred.make<void>(); | 614 | const outputDrainStarted = yield* Deferred.make<void>(); |
| 615 | const outputChunks = yield* Ref.make<Array<string>>([]); | 615 | const outputChunks = yield* Ref.make<Array<string>>([]); |
| 616 | const spawnerLayer = Layer.succeed( | 616 | const layerSpawner = Layer.succeed( |
| 617 | ChildProcessSpawner.ChildProcessSpawner, | 617 | ChildProcessSpawner.ChildProcessSpawner, |
| 618 | ChildProcessSpawner.make(() => | 618 | ChildProcessSpawner.make(() => |
| 619 | Effect.succeed( | 619 | Effect.succeed( |
| ... | @@ -630,8 +630,8 @@ describe("DesktopBackendManager", () => { | ... | @@ -630,8 +630,8 @@ describe("DesktopBackendManager", () => { |
| 630 | ), | 630 | ), |
| 631 | ); | 631 | ); |
| 632 | const instance = yield* makeTestInstance({ | 632 | const instance = yield* makeTestInstance({ |
| 633 | spawnerLayer, | 633 | spawnerLayer: layerSpawner, |
| 634 | httpClientLayer: httpClientLayer(() => Effect.never), | 634 | httpClientLayer: layerHttpClient(() => Effect.never), |
| 635 | backendOutputLog: { | 635 | backendOutputLog: { |
| 636 | writeOutputChunk: (_streamName, chunk) => | 636 | writeOutputChunk: (_streamName, chunk) => |
| 637 | Ref.update(outputChunks, (current) => [...current, new TextDecoder().decode(chunk)]), | 637 | Ref.update(outputChunks, (current) => [...current, new TextDecoder().decode(chunk)]), |
| ... | @@ -665,7 +665,7 @@ describe("DesktopBackendManager", () => { | ... | @@ -665,7 +665,7 @@ describe("DesktopBackendManager", () => { |
| 665 | const pruneComplete = yield* Deferred.make<void>(); | 665 | const pruneComplete = yield* Deferred.make<void>(); |
| 666 | const exited = yield* Queue.unbounded<void>(); | 666 | const exited = yield* Queue.unbounded<void>(); |
| 667 | 667 | ||
| 668 | const spawnerLayer = Layer.succeed( | 668 | const layerSpawner = Layer.succeed( |
| 669 | ChildProcessSpawner.ChildProcessSpawner, | 669 | ChildProcessSpawner.ChildProcessSpawner, |
| 670 | ChildProcessSpawner.make(() => | 670 | ChildProcessSpawner.make(() => |
| 671 | Effect.succeed( | 671 | Effect.succeed( |
| ... | @@ -679,7 +679,7 @@ describe("DesktopBackendManager", () => { | ... | @@ -679,7 +679,7 @@ describe("DesktopBackendManager", () => { |
| 679 | ); | 679 | ); |
| 680 | 680 | ||
| 681 | const instance = yield* makeTestInstance({ | 681 | const instance = yield* makeTestInstance({ |
| 682 | spawnerLayer, | 682 | spawnerLayer: layerSpawner, |
| 683 | config: { | 683 | config: { |
| 684 | ...baseConfig, | 684 | ...baseConfig, |
| 685 | runningDistro: "Ubuntu", | 685 | runningDistro: "Ubuntu", |
| ... | @@ -689,7 +689,7 @@ describe("DesktopBackendManager", () => { | ... | @@ -689,7 +689,7 @@ describe("DesktopBackendManager", () => { |
| 689 | Effect.sync(() => { | 689 | Effect.sync(() => { |
| 690 | prunedRuntimes.push([distro, runtimeId]); | 690 | prunedRuntimes.push([distro, runtimeId]); |
| 691 | }).pipe(Effect.andThen(Deferred.succeed(pruneComplete, void 0)), Effect.asVoid), | 691 | }).pipe(Effect.andThen(Deferred.succeed(pruneComplete, void 0)), Effect.asVoid), |
| 692 | httpClientLayer: httpClientLayer((request) => | 692 | httpClientLayer: layerHttpClient((request) => |
| 693 | Effect.gen(function* () { | 693 | Effect.gen(function* () { |
| 694 | const status = statuses.shift(); | 694 | const status = statuses.shift(); |
| 695 | assert.isDefined(status); | 695 | assert.isDefined(status); |
| ... | @@ -741,7 +741,7 @@ describe("DesktopBackendManager", () => { | ... | @@ -741,7 +741,7 @@ describe("DesktopBackendManager", () => { |
| 741 | const firstProbe = yield* Deferred.make<void>(); | 741 | const firstProbe = yield* Deferred.make<void>(); |
| 742 | const childExit = yield* Deferred.make<void>(); | 742 | const childExit = yield* Deferred.make<void>(); |
| 743 | 743 | ||
| 744 | const spawnerLayer = Layer.succeed( | 744 | const layerSpawner = Layer.succeed( |
| 745 | ChildProcessSpawner.ChildProcessSpawner, | 745 | ChildProcessSpawner.ChildProcessSpawner, |
| 746 | ChildProcessSpawner.make(() => | 746 | ChildProcessSpawner.make(() => |
| 747 | Effect.succeed( | 747 | Effect.succeed( |
| ... | @@ -757,7 +757,7 @@ describe("DesktopBackendManager", () => { | ... | @@ -757,7 +757,7 @@ describe("DesktopBackendManager", () => { |
| 757 | // The backend stays 503 through the first *two* readiness budgets | 757 | // The backend stays 503 through the first *two* readiness budgets |
| 758 | // and only becomes healthy (200) for the third round, i.e. it comes | 758 | // and only becomes healthy (200) for the third round, i.e. it comes |
| 759 | // up well after the initial 50ms budget has expired. | 759 | // up well after the initial 50ms budget has expired. |
| 760 | const httpLayer = httpClientLayer((request) => | 760 | const layerHttp = layerHttpClient((request) => |
| 761 | Effect.gen(function* () { | 761 | Effect.gen(function* () { |
| 762 | requestCount += 1; | 762 | requestCount += 1; |
| 763 | requestUrls.push(request.url); | 763 | requestUrls.push(request.url); |
| ... | @@ -778,7 +778,7 @@ describe("DesktopBackendManager", () => { | ... | @@ -778,7 +778,7 @@ describe("DesktopBackendManager", () => { |
| 778 | Effect.sync(() => { | 778 | Effect.sync(() => { |
| 779 | readinessTimeoutCount += 1; | 779 | readinessTimeoutCount += 1; |
| 780 | }), | 780 | }), |
| 781 | }).pipe(Effect.provide(Layer.merge(spawnerLayer, httpLayer)), Effect.forkChild); | 781 | }).pipe(Effect.provide(Layer.merge(layerSpawner, layerHttp)), Effect.forkChild); |
| 782 | 782 | ||
| 783 | yield* Deferred.await(firstProbe); | 783 | yield* Deferred.await(firstProbe); |
| 784 | assert.equal(readyCount, 0); | 784 | assert.equal(readyCount, 0); |
| ... | @@ -823,7 +823,7 @@ describe("DesktopBackendManager", () => { | ... | @@ -823,7 +823,7 @@ describe("DesktopBackendManager", () => { |
| 823 | let discardedSessionCount = 0; | 823 | let discardedSessionCount = 0; |
| 824 | let removedTelemetrySources = 0; | 824 | let removedTelemetrySources = 0; |
| 825 | 825 | ||
| 826 | const spawnerLayer = Layer.succeed( | 826 | const layerSpawner = Layer.succeed( |
| 827 | ChildProcessSpawner.ChildProcessSpawner, | 827 | ChildProcessSpawner.ChildProcessSpawner, |
| 828 | ChildProcessSpawner.make(() => | 828 | ChildProcessSpawner.make(() => |
| 829 | Effect.gen(function* () { | 829 | Effect.gen(function* () { |
| ... | @@ -852,7 +852,7 @@ describe("DesktopBackendManager", () => { | ... | @@ -852,7 +852,7 @@ describe("DesktopBackendManager", () => { |
| 852 | ); | 852 | ); |
| 853 | 853 | ||
| 854 | const instance = yield* makeTestInstance({ | 854 | const instance = yield* makeTestInstance({ |
| 855 | spawnerLayer, | 855 | spawnerLayer: layerSpawner, |
| 856 | onReady: Ref.set(backendReadyFlag, true).pipe( | 856 | onReady: Ref.set(backendReadyFlag, true).pipe( |
| 857 | Effect.andThen(Deferred.succeed(ready, void 0)), | 857 | Effect.andThen(Deferred.succeed(ready, void 0)), |
| 858 | Effect.asVoid, | 858 | Effect.asVoid, |
| ... | @@ -922,7 +922,7 @@ describe("DesktopBackendManager", () => { | ... | @@ -922,7 +922,7 @@ describe("DesktopBackendManager", () => { |
| 922 | const finishTeardown = yield* Deferred.make<void>(); | 922 | const finishTeardown = yield* Deferred.make<void>(); |
| 923 | let startCount = 0; | 923 | let startCount = 0; |
| 924 | 924 | ||
| 925 | const spawnerLayer = Layer.succeed( | 925 | const layerSpawner = Layer.succeed( |
| 926 | ChildProcessSpawner.ChildProcessSpawner, | 926 | ChildProcessSpawner.ChildProcessSpawner, |
| 927 | ChildProcessSpawner.make(() => | 927 | ChildProcessSpawner.make(() => |
| 928 | Effect.gen(function* () { | 928 | Effect.gen(function* () { |
| ... | @@ -953,8 +953,8 @@ describe("DesktopBackendManager", () => { | ... | @@ -953,8 +953,8 @@ describe("DesktopBackendManager", () => { |
| 953 | ); | 953 | ); |
| 954 | 954 | ||
| 955 | const instance = yield* makeTestInstance({ | 955 | const instance = yield* makeTestInstance({ |
| 956 | spawnerLayer, | 956 | spawnerLayer: layerSpawner, |
| 957 | httpClientLayer: httpClientLayer(() => Effect.never), | 957 | httpClientLayer: layerHttpClient(() => Effect.never), |
| 958 | }); | 958 | }); |
| 959 | 959 | ||
| 960 | yield* instance.start; | 960 | yield* instance.start; |
| ... | @@ -992,7 +992,7 @@ describe("DesktopBackendManager", () => { | ... | @@ -992,7 +992,7 @@ describe("DesktopBackendManager", () => { |
| 992 | method: "configResolve", | 992 | method: "configResolve", |
| 993 | description: "transient configuration failure", | 993 | description: "transient configuration failure", |
| 994 | }); | 994 | }); |
| 995 | const spawnerLayer = Layer.succeed( | 995 | const layerSpawner = Layer.succeed( |
| 996 | ChildProcessSpawner.ChildProcessSpawner, | 996 | ChildProcessSpawner.ChildProcessSpawner, |
| 997 | ChildProcessSpawner.make(() => | 997 | ChildProcessSpawner.make(() => |
| 998 | Effect.gen(function* () { | 998 | Effect.gen(function* () { |
| ... | @@ -1027,9 +1027,9 @@ describe("DesktopBackendManager", () => { | ... | @@ -1027,9 +1027,9 @@ describe("DesktopBackendManager", () => { |
| 1027 | ), | 1027 | ), |
| 1028 | ); | 1028 | ); |
| 1029 | const instance = yield* makeTestInstance({ | 1029 | const instance = yield* makeTestInstance({ |
| 1030 | spawnerLayer, | 1030 | spawnerLayer: layerSpawner, |
| 1031 | configResolve, | 1031 | configResolve, |
| 1032 | httpClientLayer: httpClientLayer(() => Effect.never), | 1032 | httpClientLayer: layerHttpClient(() => Effect.never), |
| 1033 | }); | 1033 | }); |
| 1034 | 1034 | ||
| 1035 | yield* instance.start; | 1035 | yield* instance.start; |
| ... | @@ -1062,7 +1062,7 @@ describe("DesktopBackendManager", () => { | ... | @@ -1062,7 +1062,7 @@ describe("DesktopBackendManager", () => { |
| 1062 | const finishTeardown = yield* Deferred.make<void>(); | 1062 | const finishTeardown = yield* Deferred.make<void>(); |
| 1063 | let startCount = 0; | 1063 | let startCount = 0; |
| 1064 | 1064 | ||
| 1065 | const spawnerLayer = Layer.succeed( | 1065 | const layerSpawner = Layer.succeed( |
| 1066 | ChildProcessSpawner.ChildProcessSpawner, | 1066 | ChildProcessSpawner.ChildProcessSpawner, |
| 1067 | ChildProcessSpawner.make(() => | 1067 | ChildProcessSpawner.make(() => |
| 1068 | Effect.gen(function* () { | 1068 | Effect.gen(function* () { |
| ... | @@ -1093,8 +1093,8 @@ describe("DesktopBackendManager", () => { | ... | @@ -1093,8 +1093,8 @@ describe("DesktopBackendManager", () => { |
| 1093 | ); | 1093 | ); |
| 1094 | 1094 | ||
| 1095 | const instance = yield* makeTestInstance({ | 1095 | const instance = yield* makeTestInstance({ |
| 1096 | spawnerLayer, | 1096 | spawnerLayer: layerSpawner, |
| 1097 | httpClientLayer: httpClientLayer(() => Effect.never), | 1097 | httpClientLayer: layerHttpClient(() => Effect.never), |
| 1098 | }); | 1098 | }); |
| 1099 | 1099 | ||
| 1100 | yield* instance.start; | 1100 | yield* instance.start; |
| ... | @@ -1128,7 +1128,7 @@ describe("DesktopBackendManager", () => { | ... | @@ -1128,7 +1128,7 @@ describe("DesktopBackendManager", () => { |
| 1128 | const closed = yield* Deferred.make<void>(); | 1128 | const closed = yield* Deferred.make<void>(); |
| 1129 | const startedPids = yield* Queue.unbounded<number>(); | 1129 | const startedPids = yield* Queue.unbounded<number>(); |
| 1130 | 1130 | ||
| 1131 | const spawnerLayer = Layer.succeed( | 1131 | const layerSpawner = Layer.succeed( |
| 1132 | ChildProcessSpawner.ChildProcessSpawner, | 1132 | ChildProcessSpawner.ChildProcessSpawner, |
| 1133 | ChildProcessSpawner.make(() => | 1133 | ChildProcessSpawner.make(() => |
| 1134 | Effect.gen(function* () { | 1134 | Effect.gen(function* () { |
| ... | @@ -1143,8 +1143,8 @@ describe("DesktopBackendManager", () => { | ... | @@ -1143,8 +1143,8 @@ describe("DesktopBackendManager", () => { |
| 1143 | ); | 1143 | ); |
| 1144 | 1144 | ||
| 1145 | const instance = yield* makeTestInstance({ | 1145 | const instance = yield* makeTestInstance({ |
| 1146 | spawnerLayer, | 1146 | spawnerLayer: layerSpawner, |
| 1147 | httpClientLayer: httpClientLayer(() => Effect.never), | 1147 | httpClientLayer: layerHttpClient(() => Effect.never), |
| 1148 | onShutdown: Effect.sync(() => { | 1148 | onShutdown: Effect.sync(() => { |
| 1149 | shutdownCount += 1; | 1149 | shutdownCount += 1; |
| 1150 | }), | 1150 | }), |
| ... | @@ -1164,7 +1164,7 @@ describe("DesktopBackendManager", () => { | ... | @@ -1164,7 +1164,7 @@ describe("DesktopBackendManager", () => { |
| 1164 | const failures = yield* Queue.unbounded<string>(); | 1164 | const failures = yield* Queue.unbounded<string>(); |
| 1165 | let startCount = 0; | 1165 | let startCount = 0; |
| 1166 | 1166 | ||
| 1167 | const spawnerLayer = Layer.succeed( | 1167 | const layerSpawner = Layer.succeed( |
| 1168 | ChildProcessSpawner.ChildProcessSpawner, | 1168 | ChildProcessSpawner.ChildProcessSpawner, |
| 1169 | ChildProcessSpawner.make(() => | 1169 | ChildProcessSpawner.make(() => |
| 1170 | Effect.sync(() => { | 1170 | Effect.sync(() => { |
| ... | @@ -1179,8 +1179,8 @@ describe("DesktopBackendManager", () => { | ... | @@ -1179,8 +1179,8 @@ describe("DesktopBackendManager", () => { |
| 1179 | ); | 1179 | ); |
| 1180 | 1180 | ||
| 1181 | const instance = yield* makeTestInstance({ | 1181 | const instance = yield* makeTestInstance({ |
| 1182 | spawnerLayer, | 1182 | spawnerLayer: layerSpawner, |
| 1183 | httpClientLayer: httpClientLayer(() => Effect.never), | 1183 | httpClientLayer: layerHttpClient(() => Effect.never), |
| 1184 | backendOutputLog: { | 1184 | backendOutputLog: { |
| 1185 | persistFailure: ({ details }) => Queue.offer(failures, details).pipe(Effect.asVoid), | 1185 | persistFailure: ({ details }) => Queue.offer(failures, details).pipe(Effect.asVoid), |
| 1186 | }, | 1186 | }, |
| ... | @@ -1209,13 +1209,13 @@ describe("DesktopBackendManager", () => { | ... | @@ -1209,13 +1209,13 @@ describe("DesktopBackendManager", () => { |
| 1209 | Effect.gen(function* () { | 1209 | Effect.gen(function* () { |
| 1210 | let shutdownCount = 0; | 1210 | let shutdownCount = 0; |
| 1211 | 1211 | ||
| 1212 | const spawnerLayer = Layer.succeed( | 1212 | const layerSpawner = Layer.succeed( |
| 1213 | ChildProcessSpawner.ChildProcessSpawner, | 1213 | ChildProcessSpawner.ChildProcessSpawner, |
| 1214 | ChildProcessSpawner.make(() => Effect.die("unexpected backend spawn")), | 1214 | ChildProcessSpawner.make(() => Effect.die("unexpected backend spawn")), |
| 1215 | ); | 1215 | ); |
| 1216 | 1216 | ||
| 1217 | const instance = yield* makeTestInstance({ | 1217 | const instance = yield* makeTestInstance({ |
| 1218 | spawnerLayer, | 1218 | spawnerLayer: layerSpawner, |
| 1219 | config: { | 1219 | config: { |
| 1220 | ...baseConfig, | 1220 | ...baseConfig, |
| 1221 | preflightFailure: Option.some({ reason: "preflight failed", fatal: false }), | 1221 | preflightFailure: Option.some({ reason: "preflight failed", fatal: false }), |
| ... | @@ -1238,13 +1238,13 @@ describe("DesktopBackendManager", () => { | ... | @@ -1238,13 +1238,13 @@ describe("DesktopBackendManager", () => { |
| 1238 | Effect.scoped( | 1238 | Effect.scoped( |
| 1239 | Effect.gen(function* () { | 1239 | Effect.gen(function* () { |
| 1240 | const failures: string[] = []; | 1240 | const failures: string[] = []; |
| 1241 | const spawnerLayer = Layer.succeed( | 1241 | const layerSpawner = Layer.succeed( |
| 1242 | ChildProcessSpawner.ChildProcessSpawner, | 1242 | ChildProcessSpawner.ChildProcessSpawner, |
| 1243 | ChildProcessSpawner.make(() => Effect.die("unexpected backend spawn")), | 1243 | ChildProcessSpawner.make(() => Effect.die("unexpected backend spawn")), |
| 1244 | ); | 1244 | ); |
| 1245 | 1245 | ||
| 1246 | const instance = yield* makeTestInstance({ | 1246 | const instance = yield* makeTestInstance({ |
| 1247 | spawnerLayer, | 1247 | spawnerLayer: layerSpawner, |
| 1248 | config: { | 1248 | config: { |
| 1249 | ...baseConfig, | 1249 | ...baseConfig, |
| 1250 | preflightFailure: Option.some({ reason: "Node.js not found", fatal: true }), | 1250 | preflightFailure: Option.some({ reason: "Node.js not found", fatal: true }), |
| ... | @@ -1279,7 +1279,7 @@ describe("DesktopBackendManager", () => { | ... | @@ -1279,7 +1279,7 @@ describe("DesktopBackendManager", () => { |
| 1279 | Effect.gen(function* () { | 1279 | Effect.gen(function* () { |
| 1280 | const failing = yield* Ref.make(true); | 1280 | const failing = yield* Ref.make(true); |
| 1281 | const starts = yield* Queue.unbounded<number>(); | 1281 | const starts = yield* Queue.unbounded<number>(); |
| 1282 | const spawnerLayer = Layer.succeed( | 1282 | const layerSpawner = Layer.succeed( |
| 1283 | ChildProcessSpawner.ChildProcessSpawner, | 1283 | ChildProcessSpawner.ChildProcessSpawner, |
| 1284 | ChildProcessSpawner.make(() => | 1284 | ChildProcessSpawner.make(() => |
| 1285 | Queue.offer(starts, 123).pipe( | 1285 | Queue.offer(starts, 123).pipe( |
| ... | @@ -1293,7 +1293,7 @@ describe("DesktopBackendManager", () => { | ... | @@ -1293,7 +1293,7 @@ describe("DesktopBackendManager", () => { |
| 1293 | ); | 1293 | ); |
| 1294 | 1294 | ||
| 1295 | const instance = yield* makeTestInstance({ | 1295 | const instance = yield* makeTestInstance({ |
| 1296 | spawnerLayer, | 1296 | spawnerLayer: layerSpawner, |
| 1297 | configResolve: Ref.get(failing).pipe( | 1297 | configResolve: Ref.get(failing).pipe( |
| 1298 | Effect.map((isFailing) => | 1298 | Effect.map((isFailing) => |
| 1299 | isFailing | 1299 | isFailing |
| ... | @@ -1338,13 +1338,13 @@ describe("DesktopBackendManager", () => { | ... | @@ -1338,13 +1338,13 @@ describe("DesktopBackendManager", () => { |
| 1338 | Effect.scoped( | 1338 | Effect.scoped( |
| 1339 | Effect.gen(function* () { | 1339 | Effect.gen(function* () { |
| 1340 | const failures: string[] = []; | 1340 | const failures: string[] = []; |
| 1341 | const spawnerLayer = Layer.succeed( | 1341 | const layerSpawner = Layer.succeed( |
| 1342 | ChildProcessSpawner.ChildProcessSpawner, | 1342 | ChildProcessSpawner.ChildProcessSpawner, |
| 1343 | ChildProcessSpawner.make(() => Effect.die("unexpected backend spawn")), | 1343 | ChildProcessSpawner.make(() => Effect.die("unexpected backend spawn")), |
| 1344 | ); | 1344 | ); |
| 1345 | 1345 | ||
| 1346 | const instance = yield* makeTestInstance({ | 1346 | const instance = yield* makeTestInstance({ |
| 1347 | spawnerLayer, | 1347 | spawnerLayer: layerSpawner, |
| 1348 | config: { | 1348 | config: { |
| 1349 | ...baseConfig, | 1349 | ...baseConfig, |
| 1350 | preflightFailure: Option.some({ reason: "wslpath conversion failed", fatal: false }), | 1350 | preflightFailure: Option.some({ reason: "wslpath conversion failed", fatal: false }), |
| ... | @@ -1368,13 +1368,13 @@ describe("DesktopBackendManager", () => { | ... | @@ -1368,13 +1368,13 @@ describe("DesktopBackendManager", () => { |
| 1368 | Effect.scoped( | 1368 | Effect.scoped( |
| 1369 | Effect.gen(function* () { | 1369 | Effect.gen(function* () { |
| 1370 | const failures: string[] = []; | 1370 | const failures: string[] = []; |
| 1371 | const spawnerLayer = Layer.succeed( | 1371 | const layerSpawner = Layer.succeed( |
| 1372 | ChildProcessSpawner.ChildProcessSpawner, | 1372 | ChildProcessSpawner.ChildProcessSpawner, |
| 1373 | ChildProcessSpawner.make(() => Effect.die("unexpected backend spawn")), | 1373 | ChildProcessSpawner.make(() => Effect.die("unexpected backend spawn")), |
| 1374 | ); | 1374 | ); |
| 1375 | 1375 | ||
| 1376 | const instance = yield* makeTestInstance({ | 1376 | const instance = yield* makeTestInstance({ |
| 1377 | spawnerLayer, | 1377 | spawnerLayer: layerSpawner, |
| 1378 | config: { | 1378 | config: { |
| 1379 | ...baseConfig, | 1379 | ...baseConfig, |
| 1380 | preflightFailure: Option.some({ | 1380 | preflightFailure: Option.some({ |
| ... | @@ -1406,7 +1406,7 @@ describe("DesktopBackendManager", () => { | ... | @@ -1406,7 +1406,7 @@ describe("DesktopBackendManager", () => { |
| 1406 | const secondClosed = yield* Deferred.make<void>(); | 1406 | const secondClosed = yield* Deferred.make<void>(); |
| 1407 | let startCount = 0; | 1407 | let startCount = 0; |
| 1408 | 1408 | ||
| 1409 | const spawnerLayer = Layer.succeed( | 1409 | const layerSpawner = Layer.succeed( |
| 1410 | ChildProcessSpawner.ChildProcessSpawner, | 1410 | ChildProcessSpawner.ChildProcessSpawner, |
| 1411 | ChildProcessSpawner.make(() => | 1411 | ChildProcessSpawner.make(() => |
| 1412 | Effect.gen(function* () { | 1412 | Effect.gen(function* () { |
| ... | @@ -1433,8 +1433,8 @@ describe("DesktopBackendManager", () => { | ... | @@ -1433,8 +1433,8 @@ describe("DesktopBackendManager", () => { |
| 1433 | ); | 1433 | ); |
| 1434 | 1434 | ||
| 1435 | const instance = yield* makeTestInstance({ | 1435 | const instance = yield* makeTestInstance({ |
| 1436 | spawnerLayer, | 1436 | spawnerLayer: layerSpawner, |
| 1437 | httpClientLayer: httpClientLayer(() => Effect.never), | 1437 | httpClientLayer: layerHttpClient(() => Effect.never), |
| 1438 | }); | 1438 | }); |
| 1439 | 1439 | ||
| 1440 | yield* instance.start; | 1440 | yield* instance.start; |
| ... | @@ -1465,7 +1465,7 @@ describe("DesktopBackendManager", () => { | ... | @@ -1465,7 +1465,7 @@ describe("DesktopBackendManager", () => { |
| 1465 | const starts = yield* Queue.unbounded<number>(); | 1465 | const starts = yield* Queue.unbounded<number>(); |
| 1466 | let startCount = 0; | 1466 | let startCount = 0; |
| 1467 | 1467 | ||
| 1468 | const spawnerLayer = Layer.succeed( | 1468 | const layerSpawner = Layer.succeed( |
| 1469 | ChildProcessSpawner.ChildProcessSpawner, | 1469 | ChildProcessSpawner.ChildProcessSpawner, |
| 1470 | ChildProcessSpawner.make(() => | 1470 | ChildProcessSpawner.make(() => |
| 1471 | Effect.sync(() => { | 1471 | Effect.sync(() => { |
| ... | @@ -1480,8 +1480,8 @@ describe("DesktopBackendManager", () => { | ... | @@ -1480,8 +1480,8 @@ describe("DesktopBackendManager", () => { |
| 1480 | ); | 1480 | ); |
| 1481 | 1481 | ||
| 1482 | const instance = yield* makeTestInstance({ | 1482 | const instance = yield* makeTestInstance({ |
| 1483 | spawnerLayer, | 1483 | spawnerLayer: layerSpawner, |
| 1484 | httpClientLayer: httpClientLayer(() => Effect.never), | 1484 | httpClientLayer: layerHttpClient(() => Effect.never), |
| 1485 | }); | 1485 | }); |
| 1486 | 1486 | ||
| 1487 | yield* instance.start; | 1487 | yield* instance.start; |
| ... | @@ -1534,7 +1534,7 @@ describe("DesktopBackendManager", () => { | ... | @@ -1534,7 +1534,7 @@ describe("DesktopBackendManager", () => { |
| 1534 | }), | 1534 | }), |
| 1535 | ), | 1535 | ), |
| 1536 | ), | 1536 | ), |
| 1537 | httpClientLayer: httpClientLayer(() => Effect.never), | 1537 | httpClientLayer: layerHttpClient(() => Effect.never), |
| 1538 | }); | 1538 | }); |
| 1539 | 1539 | ||
| 1540 | const instance1 = yield* makeInstance("instance1"); | 1540 | const instance1 = yield* makeInstance("instance1"); |
| ... | @@ -1543,7 +1543,7 @@ describe("DesktopBackendManager", () => { | ... | @@ -1543,7 +1543,7 @@ describe("DesktopBackendManager", () => { |
| 1543 | yield* instance1.start; | 1543 | yield* instance1.start; |
| 1544 | yield* instance2.start; | 1544 | yield* instance2.start; |
| 1545 | 1545 | ||
| 1546 | const mockPool = Layer.succeed(DesktopBackendPool.DesktopBackendPool, { | 1546 | const layerMockPool = Layer.succeed(DesktopBackendPool.DesktopBackendPool, { |
| 1547 | list: Effect.succeed([instance1, instance2]), | 1547 | list: Effect.succeed([instance1, instance2]), |
| 1548 | get: () => Effect.succeedNone, | 1548 | get: () => Effect.succeedNone, |
| 1549 | primary: Effect.die(new Error("primary not implemented")), | 1549 | primary: Effect.die(new Error("primary not implemented")), |
| ... | @@ -1556,7 +1556,7 @@ describe("DesktopBackendManager", () => { | ... | @@ -1556,7 +1556,7 @@ describe("DesktopBackendManager", () => { |
| 1556 | // as an ordinary interruptible effect. | 1556 | // as an ordinary interruptible effect. |
| 1557 | const quitFiber = yield* Effect.scoped( | 1557 | const quitFiber = yield* Effect.scoped( |
| 1558 | Effect.addFinalizer(() => DesktopApp.stopAllPoolInstances()), | 1558 | Effect.addFinalizer(() => DesktopApp.stopAllPoolInstances()), |
| 1559 | ).pipe(Effect.provide(mockPool), Effect.forkChild); | 1559 | ).pipe(Effect.provide(layerMockPool), Effect.forkChild); |
| 1560 | 1560 | ||
| 1561 | const started = yield* Queue.takeN(teardownStarted, 2); | 1561 | const started = yield* Queue.takeN(teardownStarted, 2); |
| 1562 | assert.deepEqual(started.toSorted(), ["instance1", "instance2"]); | 1562 | assert.deepEqual(started.toSorted(), ["instance1", "instance2"]); |
apps/desktop/src/backend/DesktopBackendPool.test.ts+2-4| ... | @@ -41,9 +41,7 @@ function makeStubInstance( | ... | @@ -41,9 +41,7 @@ function makeStubInstance( |
| 41 | }; | 41 | }; |
| 42 | } | 42 | } |
| 43 | 43 | ||
| 44 | function makePoolLayer( | 44 | function layerPool(labelRef: Ref.Ref<string>): Layer.Layer<DesktopBackendPool.DesktopBackendPool> { |
| 45 | labelRef: Ref.Ref<string>, | ||
| 46 | ): Layer.Layer<DesktopBackendPool.DesktopBackendPool> { | ||
| 47 | return DesktopBackendPool.layer.pipe( | 45 | return DesktopBackendPool.layer.pipe( |
| 48 | Layer.provideMerge( | 46 | Layer.provideMerge( |
| 49 | Layer.mergeAll( | 47 | Layer.mergeAll( |
| ... | @@ -144,7 +142,7 @@ describe("DesktopBackendPool", () => { | ... | @@ -144,7 +142,7 @@ describe("DesktopBackendPool", () => { |
| 144 | Effect.gen(function* () { | 142 | Effect.gen(function* () { |
| 145 | const labelRef = yield* Ref.make("Windows"); | 143 | const labelRef = yield* Ref.make("Windows"); |
| 146 | const pool = yield* DesktopBackendPool.DesktopBackendPool.pipe( | 144 | const pool = yield* DesktopBackendPool.DesktopBackendPool.pipe( |
| 147 | Effect.provide(makePoolLayer(labelRef)), | 145 | Effect.provide(layerPool(labelRef)), |
| 148 | ); | 146 | ); |
| 149 | const primary = yield* pool.primary; | 147 | const primary = yield* pool.primary; |
| 150 | 148 |
apps/desktop/src/backend/DesktopLocalEnvironmentAuth.test.ts+5-5| ... | @@ -32,7 +32,7 @@ describe("DesktopLocalEnvironmentAuth", () => { | ... | @@ -32,7 +32,7 @@ describe("DesktopLocalEnvironmentAuth", () => { |
| 32 | it.effect("exchanges the desktop bootstrap credential only once", () => | 32 | it.effect("exchanges the desktop bootstrap credential only once", () => |
| 33 | Effect.gen(function* () { | 33 | Effect.gen(function* () { |
| 34 | const requestCount = yield* Ref.make(0); | 34 | const requestCount = yield* Ref.make(0); |
| 35 | const httpClientLayer = Layer.succeed( | 35 | const layerHttpClient = Layer.succeed( |
| 36 | HttpClient.HttpClient, | 36 | HttpClient.HttpClient, |
| 37 | HttpClient.make((request) => | 37 | HttpClient.make((request) => |
| 38 | Ref.update(requestCount, (count) => count + 1).pipe( | 38 | Ref.update(requestCount, (count) => count + 1).pipe( |
| ... | @@ -54,7 +54,7 @@ describe("DesktopLocalEnvironmentAuth", () => { | ... | @@ -54,7 +54,7 @@ describe("DesktopLocalEnvironmentAuth", () => { |
| 54 | ), | 54 | ), |
| 55 | ), | 55 | ), |
| 56 | ); | 56 | ); |
| 57 | const poolLayer = Layer.succeed(DesktopBackendPool.DesktopBackendPool, { | 57 | const layerPool = Layer.succeed(DesktopBackendPool.DesktopBackendPool, { |
| 58 | list: Effect.succeed([ | 58 | list: Effect.succeed([ |
| 59 | { | 59 | { |
| 60 | id: PRIMARY_LOCAL_ENVIRONMENT_ID, | 60 | id: PRIMARY_LOCAL_ENVIRONMENT_ID, |
| ... | @@ -63,14 +63,14 @@ describe("DesktopLocalEnvironmentAuth", () => { | ... | @@ -63,14 +63,14 @@ describe("DesktopLocalEnvironmentAuth", () => { |
| 63 | }, | 63 | }, |
| 64 | ]), | 64 | ]), |
| 65 | } as unknown as DesktopBackendPool.DesktopBackendPool["Service"]); | 65 | } as unknown as DesktopBackendPool.DesktopBackendPool["Service"]); |
| 66 | const testLayer = DesktopLocalEnvironmentAuth.layer.pipe( | 66 | const layerTest = DesktopLocalEnvironmentAuth.layer.pipe( |
| 67 | Layer.provide(Layer.mergeAll(poolLayer, httpClientLayer)), | 67 | Layer.provide(Layer.mergeAll(layerPool, layerHttpClient)), |
| 68 | ); | 68 | ); |
| 69 | 69 | ||
| 70 | const [first, second] = yield* Effect.gen(function* () { | 70 | const [first, second] = yield* Effect.gen(function* () { |
| 71 | const auth = yield* DesktopLocalEnvironmentAuth.DesktopLocalEnvironmentAuth; | 71 | const auth = yield* DesktopLocalEnvironmentAuth.DesktopLocalEnvironmentAuth; |
| 72 | return yield* Effect.all([auth.getBearerToken, auth.getBearerToken]); | 72 | return yield* Effect.all([auth.getBearerToken, auth.getBearerToken]); |
| 73 | }).pipe(Effect.provide(testLayer)); | 73 | }).pipe(Effect.provide(layerTest)); |
| 74 | 74 | ||
| 75 | assert.strictEqual(first, "desktop-bearer-token"); | 75 | assert.strictEqual(first, "desktop-bearer-token"); |
| 76 | assert.strictEqual(second, "desktop-bearer-token"); | 76 | assert.strictEqual(second, "desktop-bearer-token"); |
apps/desktop/src/backend/DesktopNetworkInterfaces.test.ts+3-3| ... | @@ -15,7 +15,7 @@ vi.mock("node:os", () => ({ | ... | @@ -15,7 +15,7 @@ vi.mock("node:os", () => ({ |
| 15 | 15 | ||
| 16 | import * as DesktopNetworkInterfaces from "./DesktopNetworkInterfaces.ts"; | 16 | import * as DesktopNetworkInterfaces from "./DesktopNetworkInterfaces.ts"; |
| 17 | 17 | ||
| 18 | const TestLayer = DesktopNetworkInterfaces.layer.pipe( | 18 | const layerTest = DesktopNetworkInterfaces.layer.pipe( |
| 19 | Layer.provide(Layer.succeed(HostProcessPlatform, "linux")), | 19 | Layer.provide(Layer.succeed(HostProcessPlatform, "linux")), |
| 20 | ); | 20 | ); |
| 21 | 21 | ||
| ... | @@ -39,7 +39,7 @@ describe("DesktopNetworkInterfaces", () => { | ... | @@ -39,7 +39,7 @@ describe("DesktopNetworkInterfaces", () => { |
| 39 | return Effect.gen(function* () { | 39 | return Effect.gen(function* () { |
| 40 | const service = yield* DesktopNetworkInterfaces.DesktopNetworkInterfaces; | 40 | const service = yield* DesktopNetworkInterfaces.DesktopNetworkInterfaces; |
| 41 | assert.strictEqual(yield* service.read, interfaces); | 41 | assert.strictEqual(yield* service.read, interfaces); |
| 42 | }).pipe(Effect.provide(TestLayer)); | 42 | }).pipe(Effect.provide(layerTest)); |
| 43 | }); | 43 | }); |
| 44 | 44 | ||
| 45 | it.effect("preserves network interface read failures as structured defects", () => { | 45 | it.effect("preserves network interface read failures as structured defects", () => { |
| ... | @@ -60,6 +60,6 @@ describe("DesktopNetworkInterfaces", () => { | ... | @@ -60,6 +60,6 @@ describe("DesktopNetworkInterfaces", () => { |
| 60 | assert.strictEqual(error.cause, cause); | 60 | assert.strictEqual(error.cause, cause); |
| 61 | assert.equal(error.message, "Failed to read desktop network interfaces on linux."); | 61 | assert.equal(error.message, "Failed to read desktop network interfaces on linux."); |
| 62 | } | 62 | } |
| 63 | }).pipe(Effect.provide(TestLayer)); | 63 | }).pipe(Effect.provide(layerTest)); |
| 64 | }); | 64 | }); |
| 65 | }); | 65 | }); |
apps/desktop/src/backend/DesktopServerExposure.test.ts+13-13| ... | @@ -38,7 +38,7 @@ const tailnetNetworkInterfaces: DesktopNetworkInterfaces.NetworkInterfaces = { | ... | @@ -38,7 +38,7 @@ const tailnetNetworkInterfaces: DesktopNetworkInterfaces.NetworkInterfaces = { |
| 38 | ], | 38 | ], |
| 39 | }; | 39 | }; |
| 40 | 40 | ||
| 41 | function mockSpawnerLayer(statusJson = "{}") { | 41 | function layerMockSpawner(statusJson = "{}") { |
| 42 | return Layer.succeed( | 42 | return Layer.succeed( |
| 43 | ChildProcessSpawner.ChildProcessSpawner, | 43 | ChildProcessSpawner.ChildProcessSpawner, |
| 44 | ChildProcessSpawner.make(() => | 44 | ChildProcessSpawner.make(() => |
| ... | @@ -61,14 +61,14 @@ function mockSpawnerLayer(statusJson = "{}") { | ... | @@ -61,14 +61,14 @@ function mockSpawnerLayer(statusJson = "{}") { |
| 61 | ); | 61 | ); |
| 62 | } | 62 | } |
| 63 | 63 | ||
| 64 | function dieOnSpawnLayer() { | 64 | function layerDieOnSpawn() { |
| 65 | return Layer.succeed( | 65 | return Layer.succeed( |
| 66 | ChildProcessSpawner.ChildProcessSpawner, | 66 | ChildProcessSpawner.ChildProcessSpawner, |
| 67 | ChildProcessSpawner.make(() => Effect.die("unexpected tailscale spawn")), | 67 | ChildProcessSpawner.make(() => Effect.die("unexpected tailscale spawn")), |
| 68 | ); | 68 | ); |
| 69 | } | 69 | } |
| 70 | 70 | ||
| 71 | function makeEnvironmentLayer(baseDir: string, env: Record<string, string | undefined> = {}) { | 71 | function layerEnvironmentFor(baseDir: string, env: Record<string, string | undefined> = {}) { |
| 72 | return DesktopEnvironment.layer({ | 72 | return DesktopEnvironment.layer({ |
| 73 | dirname: "/repo/apps/desktop/src", | 73 | dirname: "/repo/apps/desktop/src", |
| 74 | homeDirectory: baseDir, | 74 | homeDirectory: baseDir, |
| ... | @@ -86,7 +86,7 @@ function makeEnvironmentLayer(baseDir: string, env: Record<string, string | unde | ... | @@ -86,7 +86,7 @@ function makeEnvironmentLayer(baseDir: string, env: Record<string, string | unde |
| 86 | ); | 86 | ); |
| 87 | } | 87 | } |
| 88 | 88 | ||
| 89 | function makeLayer(input: { | 89 | function layer(input: { |
| 90 | readonly baseDir: string; | 90 | readonly baseDir: string; |
| 91 | readonly networkInterfaces?: DesktopNetworkInterfaces.NetworkInterfaces; | 91 | readonly networkInterfaces?: DesktopNetworkInterfaces.NetworkInterfaces; |
| 92 | readonly env?: Record<string, string | undefined>; | 92 | readonly env?: Record<string, string | undefined>; |
| ... | @@ -94,8 +94,8 @@ function makeLayer(input: { | ... | @@ -94,8 +94,8 @@ function makeLayer(input: { |
| 94 | readonly desktopSettingsLayer?: Layer.Layer<DesktopAppSettings.DesktopAppSettings>; | 94 | readonly desktopSettingsLayer?: Layer.Layer<DesktopAppSettings.DesktopAppSettings>; |
| 95 | }) { | 95 | }) { |
| 96 | const env = { T3CODE_HOME: input.baseDir, ...input.env }; | 96 | const env = { T3CODE_HOME: input.baseDir, ...input.env }; |
| 97 | const environmentLayer = makeEnvironmentLayer(input.baseDir, env); | 97 | const layerEnvironment = layerEnvironmentFor(input.baseDir, env); |
| 98 | const networkLayer = Layer.succeed(DesktopNetworkInterfaces.DesktopNetworkInterfaces, { | 98 | const layerNetwork = Layer.succeed(DesktopNetworkInterfaces.DesktopNetworkInterfaces, { |
| 99 | read: Effect.succeed(input.networkInterfaces ?? emptyNetworkInterfaces), | 99 | read: Effect.succeed(input.networkInterfaces ?? emptyNetworkInterfaces), |
| 100 | }); | 100 | }); |
| 101 | 101 | ||
| ... | @@ -103,10 +103,10 @@ function makeLayer(input: { | ... | @@ -103,10 +103,10 @@ function makeLayer(input: { |
| 103 | Layer.provideMerge(input.desktopSettingsLayer ?? DesktopAppSettings.layer), | 103 | Layer.provideMerge(input.desktopSettingsLayer ?? DesktopAppSettings.layer), |
| 104 | Layer.provideMerge(NodeFileSystem.layer), | 104 | Layer.provideMerge(NodeFileSystem.layer), |
| 105 | Layer.provideMerge(NodeHttpClient.layerUndici), | 105 | Layer.provideMerge(NodeHttpClient.layerUndici), |
| 106 | Layer.provideMerge(input.spawnerLayer ?? mockSpawnerLayer()), | 106 | Layer.provideMerge(input.spawnerLayer ?? layerMockSpawner()), |
| 107 | Layer.provideMerge(networkLayer), | 107 | Layer.provideMerge(layerNetwork), |
| 108 | Layer.provideMerge(DesktopConfig.layerTest(env)), | 108 | Layer.provideMerge(DesktopConfig.layerTest(env)), |
| 109 | Layer.provideMerge(environmentLayer), | 109 | Layer.provideMerge(layerEnvironment), |
| 110 | ); | 110 | ); |
| 111 | } | 111 | } |
| 112 | 112 | ||
| ... | @@ -132,7 +132,7 @@ const withHarness = <A, E, R>( | ... | @@ -132,7 +132,7 @@ const withHarness = <A, E, R>( |
| 132 | }); | 132 | }); |
| 133 | return yield* effect.pipe( | 133 | return yield* effect.pipe( |
| 134 | Effect.provide( | 134 | Effect.provide( |
| 135 | makeLayer({ | 135 | layer({ |
| 136 | baseDir, | 136 | baseDir, |
| 137 | networkInterfaces, | 137 | networkInterfaces, |
| 138 | env, | 138 | env, |
| ... | @@ -247,7 +247,7 @@ describe("DesktopServerExposure", () => { | ... | @@ -247,7 +247,7 @@ describe("DesktopServerExposure", () => { |
| 247 | path: "/tmp/desktop-settings.json", | 247 | path: "/tmp/desktop-settings.json", |
| 248 | cause: diskFailure, | 248 | cause: diskFailure, |
| 249 | }); | 249 | }); |
| 250 | const settingsLayer = Layer.succeed(DesktopAppSettings.DesktopAppSettings, { | 250 | const layerSettings = Layer.succeed(DesktopAppSettings.DesktopAppSettings, { |
| 251 | get: Effect.succeed(DesktopAppSettings.DEFAULT_DESKTOP_SETTINGS), | 251 | get: Effect.succeed(DesktopAppSettings.DEFAULT_DESKTOP_SETTINGS), |
| 252 | load: Effect.succeed(DesktopAppSettings.DEFAULT_DESKTOP_SETTINGS), | 252 | load: Effect.succeed(DesktopAppSettings.DEFAULT_DESKTOP_SETTINGS), |
| 253 | setMainWindowBounds: () => Effect.die("unexpected main window bounds update"), | 253 | setMainWindowBounds: () => Effect.die("unexpected main window bounds update"), |
| ... | @@ -301,7 +301,7 @@ describe("DesktopServerExposure", () => { | ... | @@ -301,7 +301,7 @@ describe("DesktopServerExposure", () => { |
| 301 | }), | 301 | }), |
| 302 | {}, | 302 | {}, |
| 303 | undefined, | 303 | undefined, |
| 304 | settingsLayer, | 304 | layerSettings, |
| 305 | ); | 305 | ); |
| 306 | }); | 306 | }); |
| 307 | 307 | ||
| ... | @@ -365,7 +365,7 @@ describe("DesktopServerExposure", () => { | ... | @@ -365,7 +365,7 @@ describe("DesktopServerExposure", () => { |
| 365 | ); | 365 | ); |
| 366 | }), | 366 | }), |
| 367 | {}, | 367 | {}, |
| 368 | dieOnSpawnLayer(), | 368 | layerDieOnSpawn(), |
| 369 | ), | 369 | ), |
| 370 | ); | 370 | ); |
| 371 | 371 |
apps/desktop/src/backend/tailscaleEndpointProvider.test.ts+4-4| ... | @@ -9,7 +9,7 @@ import { | ... | @@ -9,7 +9,7 @@ import { |
| 9 | resolveTailscaleAdvertisedEndpoints, | 9 | resolveTailscaleAdvertisedEndpoints, |
| 10 | } from "./tailscaleEndpointProvider.ts"; | 10 | } from "./tailscaleEndpointProvider.ts"; |
| 11 | 11 | ||
| 12 | const unusedTailscaleExternalServicesLayer = Layer.mergeAll( | 12 | const layerUnusedTailscaleExternalServices = Layer.mergeAll( |
| 13 | Layer.succeed( | 13 | Layer.succeed( |
| 14 | HttpClient.HttpClient, | 14 | HttpClient.HttpClient, |
| 15 | HttpClient.make(() => Effect.die("unexpected Tailscale HTTPS probe")), | 15 | HttpClient.make(() => Effect.die("unexpected Tailscale HTTPS probe")), |
| ... | @@ -93,7 +93,7 @@ describe("tailscale endpoint provider", () => { | ... | @@ -93,7 +93,7 @@ describe("tailscale endpoint provider", () => { |
| 93 | description: "MagicDNS hostname. Configure Tailscale Serve for HTTPS access.", | 93 | description: "MagicDNS hostname. Configure Tailscale Serve for HTTPS access.", |
| 94 | }, | 94 | }, |
| 95 | ]); | 95 | ]); |
| 96 | }).pipe(Effect.provide(unusedTailscaleExternalServicesLayer)), | 96 | }).pipe(Effect.provide(layerUnusedTailscaleExternalServices)), |
| 97 | ); | 97 | ); |
| 98 | 98 | ||
| 99 | it.effect("uses an injected magic DNS name reader instead of spawning tailscale", () => | 99 | it.effect("uses an injected magic DNS name reader instead of spawning tailscale", () => |
| ... | @@ -112,7 +112,7 @@ describe("tailscale endpoint provider", () => { | ... | @@ -112,7 +112,7 @@ describe("tailscale endpoint provider", () => { |
| 112 | endpoints.map((endpoint) => endpoint.httpBaseUrl), | 112 | endpoints.map((endpoint) => endpoint.httpBaseUrl), |
| 113 | ["https://desktop.tail.ts.net/"], | 113 | ["https://desktop.tail.ts.net/"], |
| 114 | ); | 114 | ); |
| 115 | }).pipe(Effect.provide(unusedTailscaleExternalServicesLayer)), | 115 | }).pipe(Effect.provide(layerUnusedTailscaleExternalServices)), |
| 116 | ); | 116 | ); |
| 117 | 117 | ||
| 118 | it.effect( | 118 | it.effect( |
| ... | @@ -148,6 +148,6 @@ describe("tailscale endpoint provider", () => { | ... | @@ -148,6 +148,6 @@ describe("tailscale endpoint provider", () => { |
| 148 | description: "HTTPS endpoint served by Tailscale Serve.", | 148 | description: "HTTPS endpoint served by Tailscale Serve.", |
| 149 | }, | 149 | }, |
| 150 | ]); | 150 | ]); |
| 151 | }).pipe(Effect.provide(unusedTailscaleExternalServicesLayer)), | 151 | }).pipe(Effect.provide(layerUnusedTailscaleExternalServices)), |
| 152 | ); | 152 | ); |
| 153 | }); | 153 | }); |
apps/desktop/src/electron/ElectronMenu.test.ts+9-9| ... | @@ -27,7 +27,7 @@ vi.mock("electron", () => ({ | ... | @@ -27,7 +27,7 @@ vi.mock("electron", () => ({ |
| 27 | 27 | ||
| 28 | import * as ElectronMenu from "./ElectronMenu.ts"; | 28 | import * as ElectronMenu from "./ElectronMenu.ts"; |
| 29 | 29 | ||
| 30 | const TestLayer = ElectronMenu.layer.pipe( | 30 | const layerTest = ElectronMenu.layer.pipe( |
| 31 | Layer.provide(Layer.succeed(HostProcessPlatform, "linux")), | 31 | Layer.provide(Layer.succeed(HostProcessPlatform, "linux")), |
| 32 | ); | 32 | ); |
| 33 | 33 | ||
| ... | @@ -55,7 +55,7 @@ describe("ElectronMenu", () => { | ... | @@ -55,7 +55,7 @@ describe("ElectronMenu", () => { |
| 55 | 55 | ||
| 56 | assert.isTrue(Option.isNone(selectedItemId)); | 56 | assert.isTrue(Option.isNone(selectedItemId)); |
| 57 | assert.equal(buildFromTemplateMock.mock.calls.length, 0); | 57 | assert.equal(buildFromTemplateMock.mock.calls.length, 0); |
| 58 | }).pipe(Effect.provide(TestLayer)), | 58 | }).pipe(Effect.provide(layerTest)), |
| 59 | ); | 59 | ); |
| 60 | 60 | ||
| 61 | it.effect("resolves with the clicked leaf item id", () => | 61 | it.effect("resolves with the clicked leaf item id", () => |
| ... | @@ -82,7 +82,7 @@ describe("ElectronMenu", () => { | ... | @@ -82,7 +82,7 @@ describe("ElectronMenu", () => { |
| 82 | }); | 82 | }); |
| 83 | 83 | ||
| 84 | assert.equal(Option.getOrNull(selectedItemId), "copy"); | 84 | assert.equal(Option.getOrNull(selectedItemId), "copy"); |
| 85 | }).pipe(Effect.provide(TestLayer)), | 85 | }).pipe(Effect.provide(layerTest)), |
| 86 | ); | 86 | ); |
| 87 | 87 | ||
| 88 | it.effect("resolves with none when the menu closes without a click", () => | 88 | it.effect("resolves with none when the menu closes without a click", () => |
| ... | @@ -119,7 +119,7 @@ describe("ElectronMenu", () => { | ... | @@ -119,7 +119,7 @@ describe("ElectronMenu", () => { |
| 119 | ), | 119 | ), |
| 120 | ["Copy", "separator", "Delete"], | 120 | ["Copy", "separator", "Delete"], |
| 121 | ); | 121 | ); |
| 122 | }).pipe(Effect.provide(TestLayer)), | 122 | }).pipe(Effect.provide(layerTest)), |
| 123 | ); | 123 | ); |
| 124 | 124 | ||
| 125 | it.effect("keeps a preceding non-destructive action in the destructive section", () => | 125 | it.effect("keeps a preceding non-destructive action in the destructive section", () => |
| ... | @@ -145,7 +145,7 @@ describe("ElectronMenu", () => { | ... | @@ -145,7 +145,7 @@ describe("ElectronMenu", () => { |
| 145 | ), | 145 | ), |
| 146 | ["Copy", "separator", "Archive", "Delete"], | 146 | ["Copy", "separator", "Archive", "Delete"], |
| 147 | ); | 147 | ); |
| 148 | }).pipe(Effect.provide(TestLayer)), | 148 | }).pipe(Effect.provide(layerTest)), |
| 149 | ); | 149 | ); |
| 150 | 150 | ||
| 151 | it.effect("defers popupTemplate side effects until the returned Effect runs", () => | 151 | it.effect("defers popupTemplate side effects until the returned Effect runs", () => |
| ... | @@ -169,7 +169,7 @@ describe("ElectronMenu", () => { | ... | @@ -169,7 +169,7 @@ describe("ElectronMenu", () => { |
| 169 | assert.equal(buildFromTemplateMock.mock.calls.length, 1); | 169 | assert.equal(buildFromTemplateMock.mock.calls.length, 1); |
| 170 | assert.equal(popupMock.mock.calls.length, 1); | 170 | assert.equal(popupMock.mock.calls.length, 1); |
| 171 | assert.strictEqual(popupMock.mock.calls[0]?.[0].frame, frame); | 171 | assert.strictEqual(popupMock.mock.calls[0]?.[0].frame, frame); |
| 172 | }).pipe(Effect.provide(TestLayer)), | 172 | }).pipe(Effect.provide(layerTest)), |
| 173 | ); | 173 | ); |
| 174 | 174 | ||
| 175 | it.effect("preserves application-menu failures as structured defects", () => | 175 | it.effect("preserves application-menu failures as structured defects", () => |
| ... | @@ -195,7 +195,7 @@ describe("ElectronMenu", () => { | ... | @@ -195,7 +195,7 @@ describe("ElectronMenu", () => { |
| 195 | assert.strictEqual(error.cause, cause); | 195 | assert.strictEqual(error.cause, cause); |
| 196 | assert.notInclude(error.message, cause.message); | 196 | assert.notInclude(error.message, cause.message); |
| 197 | } | 197 | } |
| 198 | }).pipe(Effect.provide(TestLayer)), | 198 | }).pipe(Effect.provide(layerTest)), |
| 199 | ); | 199 | ); |
| 200 | 200 | ||
| 201 | it.effect("preserves popup-template failures with window context", () => | 201 | it.effect("preserves popup-template failures with window context", () => |
| ... | @@ -224,7 +224,7 @@ describe("ElectronMenu", () => { | ... | @@ -224,7 +224,7 @@ describe("ElectronMenu", () => { |
| 224 | assert.equal(error.itemCount, 1); | 224 | assert.equal(error.itemCount, 1); |
| 225 | assert.strictEqual(error.cause, cause); | 225 | assert.strictEqual(error.cause, cause); |
| 226 | } | 226 | } |
| 227 | }).pipe(Effect.provide(TestLayer)), | 227 | }).pipe(Effect.provide(layerTest)), |
| 228 | ); | 228 | ); |
| 229 | 229 | ||
| 230 | it.effect("preserves context-menu failures with normalized item context", () => | 230 | it.effect("preserves context-menu failures with normalized item context", () => |
| ... | @@ -252,6 +252,6 @@ describe("ElectronMenu", () => { | ... | @@ -252,6 +252,6 @@ describe("ElectronMenu", () => { |
| 252 | assert.equal(error.itemCount, 1); | 252 | assert.equal(error.itemCount, 1); |
| 253 | assert.strictEqual(error.cause, cause); | 253 | assert.strictEqual(error.cause, cause); |
| 254 | } | 254 | } |
| 255 | }).pipe(Effect.provide(TestLayer)), | 255 | }).pipe(Effect.provide(layerTest)), |
| 256 | ); | 256 | ); |
| 257 | }); | 257 | }); |
apps/desktop/src/electron/ElectronProtocol.test.ts+7-7| ... | @@ -19,7 +19,7 @@ vi.mock("electron", () => ({ | ... | @@ -19,7 +19,7 @@ vi.mock("electron", () => ({ |
| 19 | 19 | ||
| 20 | import * as ElectronProtocol from "./ElectronProtocol.ts"; | 20 | import * as ElectronProtocol from "./ElectronProtocol.ts"; |
| 21 | 21 | ||
| 22 | const protocolLayer = ElectronProtocol.layer.pipe(Layer.provide(NodeServices.layer)); | 22 | const layerProtocol = ElectronProtocol.layer.pipe(Layer.provide(NodeServices.layer)); |
| 23 | 23 | ||
| 24 | describe("ElectronProtocol", () => { | 24 | describe("ElectronProtocol", () => { |
| 25 | beforeEach(() => { | 25 | beforeEach(() => { |
| ... | @@ -65,7 +65,7 @@ describe("ElectronProtocol", () => { | ... | @@ -65,7 +65,7 @@ describe("ElectronProtocol", () => { |
| 65 | assert.equal((yield* request("/%invalid")).status, 400); | 65 | assert.equal((yield* request("/%invalid")).status, 400); |
| 66 | assert.equal((yield* request("/", { method: "POST" })).status, 405); | 66 | assert.equal((yield* request("/", { method: "POST" })).status, 405); |
| 67 | assert.equal(netFetchMock.mock.calls.length, 0); | 67 | assert.equal(netFetchMock.mock.calls.length, 0); |
| 68 | }).pipe(Effect.provide(Layer.merge(protocolLayer, NodeServices.layer)), Effect.scoped), | 68 | }).pipe(Effect.provide(Layer.merge(layerProtocol, NodeServices.layer)), Effect.scoped), |
| 69 | ); | 69 | ); |
| 70 | 70 | ||
| 71 | it.effect("proxies the stable renderer origin to the current app server", () => | 71 | it.effect("proxies the stable renderer origin to the current app server", () => |
| ... | @@ -129,7 +129,7 @@ describe("ElectronProtocol", () => { | ... | @@ -129,7 +129,7 @@ describe("ElectronProtocol", () => { |
| 129 | assert.isNull(forwardedHeaders.get("referer")); | 129 | assert.isNull(forwardedHeaders.get("referer")); |
| 130 | assert.isNull(forwardedHeaders.get("sec-fetch-site")); | 130 | assert.isNull(forwardedHeaders.get("sec-fetch-site")); |
| 131 | assert.deepEqual(unhandleMock.mock.calls, [["t3code-dev"]]); | 131 | assert.deepEqual(unhandleMock.mock.calls, [["t3code-dev"]]); |
| 132 | }).pipe(Effect.provide(protocolLayer)), | 132 | }).pipe(Effect.provide(layerProtocol)), |
| 133 | ); | 133 | ); |
| 134 | 134 | ||
| 135 | it.effect("rejects custom protocol requests for another host", () => | 135 | it.effect("rejects custom protocol requests for another host", () => |
| ... | @@ -153,7 +153,7 @@ describe("ElectronProtocol", () => { | ... | @@ -153,7 +153,7 @@ describe("ElectronProtocol", () => { |
| 153 | 153 | ||
| 154 | assert.equal(response.status, 404); | 154 | assert.equal(response.status, 404); |
| 155 | assert.equal(netFetchMock.mock.calls.length, 0); | 155 | assert.equal(netFetchMock.mock.calls.length, 0); |
| 156 | }).pipe(Effect.provide(protocolLayer)), | 156 | }).pipe(Effect.provide(layerProtocol)), |
| 157 | ); | 157 | ); |
| 158 | 158 | ||
| 159 | it.effect("retries transient renderer target failures", () => | 159 | it.effect("retries transient renderer target failures", () => |
| ... | @@ -180,7 +180,7 @@ describe("ElectronProtocol", () => { | ... | @@ -180,7 +180,7 @@ describe("ElectronProtocol", () => { |
| 180 | 180 | ||
| 181 | assert.equal(yield* Effect.promise(() => response.text()), "ready"); | 181 | assert.equal(yield* Effect.promise(() => response.text()), "ready"); |
| 182 | assert.equal(netFetchMock.mock.calls.length, 2); | 182 | assert.equal(netFetchMock.mock.calls.length, 2); |
| 183 | }).pipe(Effect.provide(protocolLayer)), | 183 | }).pipe(Effect.provide(layerProtocol)), |
| 184 | ); | 184 | ); |
| 185 | 185 | ||
| 186 | it.effect("preserves protocol registration failures", () => | 186 | it.effect("preserves protocol registration failures", () => |
| ... | @@ -203,7 +203,7 @@ describe("ElectronProtocol", () => { | ... | @@ -203,7 +203,7 @@ describe("ElectronProtocol", () => { |
| 203 | assert.equal(error.scheme, "t3code-dev"); | 203 | assert.equal(error.scheme, "t3code-dev"); |
| 204 | assert.strictEqual(error.cause, cause); | 204 | assert.strictEqual(error.cause, cause); |
| 205 | assert.equal(error.message, 'Failed to register Electron protocol scheme "t3code-dev".'); | 205 | assert.equal(error.message, 'Failed to register Electron protocol scheme "t3code-dev".'); |
| 206 | }).pipe(Effect.provide(protocolLayer)), | 206 | }).pipe(Effect.provide(layerProtocol)), |
| 207 | ); | 207 | ); |
| 208 | 208 | ||
| 209 | it.effect("preserves protocol unregistration failures", () => | 209 | it.effect("preserves protocol unregistration failures", () => |
| ... | @@ -232,7 +232,7 @@ describe("ElectronProtocol", () => { | ... | @@ -232,7 +232,7 @@ describe("ElectronProtocol", () => { |
| 232 | assert.strictEqual(error.cause, cause); | 232 | assert.strictEqual(error.cause, cause); |
| 233 | assert.equal(error.message, 'Failed to unregister Electron protocol scheme "t3code".'); | 233 | assert.equal(error.message, 'Failed to unregister Electron protocol scheme "t3code".'); |
| 234 | } | 234 | } |
| 235 | }).pipe(Effect.provide(protocolLayer)), | 235 | }).pipe(Effect.provide(layerProtocol)), |
| 236 | ); | 236 | ); |
| 237 | 237 | ||
| 238 | it("keeps executable sources host-restricted while allowing runtime network resources", () => { | 238 | it("keeps executable sources host-restricted while allowing runtime network resources", () => { |
apps/desktop/src/electron/ElectronWindow.test.ts+21-21| ... | @@ -57,10 +57,10 @@ vi.mock("electron", () => ({ | ... | @@ -57,10 +57,10 @@ vi.mock("electron", () => ({ |
| 57 | 57 | ||
| 58 | import * as ElectronWindow from "./ElectronWindow.ts"; | 58 | import * as ElectronWindow from "./ElectronWindow.ts"; |
| 59 | 59 | ||
| 60 | const testLayer = (platform: NodeJS.Platform) => | 60 | const layerTestFor = (platform: NodeJS.Platform) => |
| 61 | ElectronWindow.layer.pipe(Layer.provide(Layer.succeed(HostProcessPlatform, platform))); | 61 | ElectronWindow.layer.pipe(Layer.provide(Layer.succeed(HostProcessPlatform, platform))); |
| 62 | 62 | ||
| 63 | const TestLayer = testLayer("linux"); | 63 | const layerTest = layerTestFor("linux"); |
| 64 | 64 | ||
| 65 | function makeBrowserWindow(input: { readonly id: number; readonly destroyed: boolean }) { | 65 | function makeBrowserWindow(input: { readonly id: number; readonly destroyed: boolean }) { |
| 66 | return { | 66 | return { |
| ... | @@ -169,7 +169,7 @@ describe("ElectronWindow", () => { | ... | @@ -169,7 +169,7 @@ describe("ElectronWindow", () => { |
| 169 | assert.equal(error.message, 'Failed to create Electron BrowserWindow "T3 Code" (1100x780).'); | 169 | assert.equal(error.message, 'Failed to create Electron BrowserWindow "T3 Code" (1100x780).'); |
| 170 | assert.notInclude(error.message, cause.message); | 170 | assert.notInclude(error.message, cause.message); |
| 171 | assert.deepEqual(browserWindowMock.mock.calls, [[options]]); | 171 | assert.deepEqual(browserWindowMock.mock.calls, [[options]]); |
| 172 | }).pipe(Effect.provide(TestLayer)), | 172 | }).pipe(Effect.provide(layerTest)), |
| 173 | ); | 173 | ); |
| 174 | 174 | ||
| 175 | it.effect("skips windows destroyed before appearance sync runs", () => | 175 | it.effect("skips windows destroyed before appearance sync runs", () => |
| ... | @@ -187,7 +187,7 @@ describe("ElectronWindow", () => { | ... | @@ -187,7 +187,7 @@ describe("ElectronWindow", () => { |
| 187 | ); | 187 | ); |
| 188 | 188 | ||
| 189 | assert.deepEqual(syncedWindows, [liveWindow]); | 189 | assert.deepEqual(syncedWindows, [liveWindow]); |
| 190 | }).pipe(Effect.provide(TestLayer)), | 190 | }).pipe(Effect.provide(layerTest)), |
| 191 | ); | 191 | ); |
| 192 | 192 | ||
| 193 | it.effect("preserves window enumeration failures as structured defects", () => | 193 | it.effect("preserves window enumeration failures as structured defects", () => |
| ... | @@ -211,7 +211,7 @@ describe("ElectronWindow", () => { | ... | @@ -211,7 +211,7 @@ describe("ElectronWindow", () => { |
| 211 | assert.strictEqual(error.cause, cause); | 211 | assert.strictEqual(error.cause, cause); |
| 212 | assert.notInclude(error.message, cause.message); | 212 | assert.notInclude(error.message, cause.message); |
| 213 | } | 213 | } |
| 214 | }).pipe(Effect.provide(TestLayer)), | 214 | }).pipe(Effect.provide(layerTest)), |
| 215 | ); | 215 | ); |
| 216 | 216 | ||
| 217 | it.effect("preserves reveal failures with the target window", () => | 217 | it.effect("preserves reveal failures with the target window", () => |
| ... | @@ -238,7 +238,7 @@ describe("ElectronWindow", () => { | ... | @@ -238,7 +238,7 @@ describe("ElectronWindow", () => { |
| 238 | assert.isNull(error.channel); | 238 | assert.isNull(error.channel); |
| 239 | assert.strictEqual(error.cause, cause); | 239 | assert.strictEqual(error.cause, cause); |
| 240 | } | 240 | } |
| 241 | }).pipe(Effect.provide(TestLayer)), | 241 | }).pipe(Effect.provide(layerTest)), |
| 242 | ); | 242 | ); |
| 243 | 243 | ||
| 244 | it.effect("uses native Windows activation without querying the foreground window", () => | 244 | it.effect("uses native Windows activation without querying the foreground window", () => |
| ... | @@ -262,7 +262,7 @@ describe("ElectronWindow", () => { | ... | @@ -262,7 +262,7 @@ describe("ElectronWindow", () => { |
| 262 | assert.deepEqual(operations, ["app-focus", "show", "move-top", "focus", "native-activation"]); | 262 | assert.deepEqual(operations, ["app-focus", "show", "move-top", "focus", "native-activation"]); |
| 263 | assert.lengthOf(loadWindowsForegroundApiMock.mock.calls, 0); | 263 | assert.lengthOf(loadWindowsForegroundApiMock.mock.calls, 0); |
| 264 | assert.lengthOf(windowsForegroundFocusMock.mock.calls, 0); | 264 | assert.lengthOf(windowsForegroundFocusMock.mock.calls, 0); |
| 265 | }).pipe(Effect.provide(testLayer("win32"))), | 265 | }).pipe(Effect.provide(layerTestFor("win32"))), |
| 266 | ); | 266 | ); |
| 267 | 267 | ||
| 268 | it.effect("focuses the exact T3 window before activating from a shell-hosted app", () => | 268 | it.effect("focuses the exact T3 window before activating from a shell-hosted app", () => |
| ... | @@ -308,7 +308,7 @@ describe("ElectronWindow", () => { | ... | @@ -308,7 +308,7 @@ describe("ElectronWindow", () => { |
| 308 | }, | 308 | }, |
| 309 | ], | 309 | ], |
| 310 | ]); | 310 | ]); |
| 311 | }).pipe(Effect.provide(testLayer("win32"))), | 311 | }).pipe(Effect.provide(layerTestFor("win32"))), |
| 312 | ); | 312 | ); |
| 313 | 313 | ||
| 314 | it.effect("prepares the exact T3 window before a capture overlay", () => | 314 | it.effect("prepares the exact T3 window before a capture overlay", () => |
| ... | @@ -335,7 +335,7 @@ describe("ElectronWindow", () => { | ... | @@ -335,7 +335,7 @@ describe("ElectronWindow", () => { |
| 335 | ]); | 335 | ]); |
| 336 | assert.lengthOf(windowsForegroundFocusMock.mock.calls, 0); | 336 | assert.lengthOf(windowsForegroundFocusMock.mock.calls, 0); |
| 337 | assert.lengthOf(activateWindowsForegroundMock.mock.calls, 0); | 337 | assert.lengthOf(activateWindowsForegroundMock.mock.calls, 0); |
| 338 | }).pipe(Effect.provide(testLayer("win32"))), | 338 | }).pipe(Effect.provide(layerTestFor("win32"))), |
| 339 | ); | 339 | ); |
| 340 | 340 | ||
| 341 | it.effect.each([4, 8])( | 341 | it.effect.each([4, 8])( |
| ... | @@ -362,7 +362,7 @@ describe("ElectronWindow", () => { | ... | @@ -362,7 +362,7 @@ describe("ElectronWindow", () => { |
| 362 | assert.lengthOf(windowsForegroundFocusMock.mock.calls, 0); | 362 | assert.lengthOf(windowsForegroundFocusMock.mock.calls, 0); |
| 363 | assert.lengthOf(window.getTitle.mock.calls, 0); | 363 | assert.lengthOf(window.getTitle.mock.calls, 0); |
| 364 | assert.lengthOf(activateWindowsForegroundMock.mock.calls, 2); | 364 | assert.lengthOf(activateWindowsForegroundMock.mock.calls, 2); |
| 365 | }).pipe(Effect.provide(testLayer("win32"))), | 365 | }).pipe(Effect.provide(layerTestFor("win32"))), |
| 366 | ); | 366 | ); |
| 367 | 367 | ||
| 368 | it.effect.each([42n, 0n])( | 368 | it.effect.each([42n, 0n])( |
| ... | @@ -381,7 +381,7 @@ describe("ElectronWindow", () => { | ... | @@ -381,7 +381,7 @@ describe("ElectronWindow", () => { |
| 381 | 381 | ||
| 382 | assert.lengthOf(windowsForegroundFocusMock.mock.calls, 1); | 382 | assert.lengthOf(windowsForegroundFocusMock.mock.calls, 1); |
| 383 | assert.lengthOf(activateWindowsForegroundMock.mock.calls, 2); | 383 | assert.lengthOf(activateWindowsForegroundMock.mock.calls, 2); |
| 384 | }).pipe(Effect.provide(testLayer("win32"))), | 384 | }).pipe(Effect.provide(layerTestFor("win32"))), |
| 385 | ); | 385 | ); |
| 386 | 386 | ||
| 387 | it.effect("continues native focus when the foreground query fails", () => | 387 | it.effect("continues native focus when the foreground query fails", () => |
| ... | @@ -398,7 +398,7 @@ describe("ElectronWindow", () => { | ... | @@ -398,7 +398,7 @@ describe("ElectronWindow", () => { |
| 398 | 398 | ||
| 399 | assert.lengthOf(windowsForegroundFocusMock.mock.calls, 1); | 399 | assert.lengthOf(windowsForegroundFocusMock.mock.calls, 1); |
| 400 | assert.lengthOf(activateWindowsForegroundMock.mock.calls, 2); | 400 | assert.lengthOf(activateWindowsForegroundMock.mock.calls, 2); |
| 401 | }).pipe(Effect.provide(testLayer("win32"))), | 401 | }).pipe(Effect.provide(layerTestFor("win32"))), |
| 402 | ); | 402 | ); |
| 403 | 403 | ||
| 404 | it.effect("does not fail reveal when native focus rejects", () => | 404 | it.effect("does not fail reveal when native focus rejects", () => |
| ... | @@ -415,7 +415,7 @@ describe("ElectronWindow", () => { | ... | @@ -415,7 +415,7 @@ describe("ElectronWindow", () => { |
| 415 | 415 | ||
| 416 | assert.lengthOf(windowsForegroundFocusMock.mock.calls, 1); | 416 | assert.lengthOf(windowsForegroundFocusMock.mock.calls, 1); |
| 417 | assert.lengthOf(activateWindowsForegroundMock.mock.calls, 2); | 417 | assert.lengthOf(activateWindowsForegroundMock.mock.calls, 2); |
| 418 | }).pipe(Effect.provide(testLayer("win32"))), | 418 | }).pipe(Effect.provide(layerTestFor("win32"))), |
| 419 | ); | 419 | ); |
| 420 | 420 | ||
| 421 | it.effect("fails reveal when Windows refuses foreground activation", () => | 421 | it.effect("fails reveal when Windows refuses foreground activation", () => |
| ... | @@ -441,7 +441,7 @@ describe("ElectronWindow", () => { | ... | @@ -441,7 +441,7 @@ describe("ElectronWindow", () => { |
| 441 | [window.getNativeWindowHandle.mock.results[0]?.value], | 441 | [window.getNativeWindowHandle.mock.results[0]?.value], |
| 442 | [window.getNativeWindowHandle.mock.results[1]?.value], | 442 | [window.getNativeWindowHandle.mock.results[1]?.value], |
| 443 | ]); | 443 | ]); |
| 444 | }).pipe(Effect.provide(testLayer("win32"))), | 444 | }).pipe(Effect.provide(layerTestFor("win32"))), |
| 445 | ); | 445 | ); |
| 446 | 446 | ||
| 447 | it.effect("cancels native focus when destroyed during the foreground query", () => | 447 | it.effect("cancels native focus when destroyed during the foreground query", () => |
| ... | @@ -471,7 +471,7 @@ describe("ElectronWindow", () => { | ... | @@ -471,7 +471,7 @@ describe("ElectronWindow", () => { |
| 471 | assert.lengthOf(windowsForegroundFocusMock.mock.calls, 0); | 471 | assert.lengthOf(windowsForegroundFocusMock.mock.calls, 0); |
| 472 | assert.lengthOf(window.getNativeWindowHandle.mock.calls, 1); | 472 | assert.lengthOf(window.getNativeWindowHandle.mock.calls, 1); |
| 473 | assert.lengthOf(window.getTitle.mock.calls, 0); | 473 | assert.lengthOf(window.getTitle.mock.calls, 0); |
| 474 | }).pipe(Effect.provide(testLayer("win32"))), | 474 | }).pipe(Effect.provide(layerTestFor("win32"))), |
| 475 | ); | 475 | ); |
| 476 | 476 | ||
| 477 | it.effect("preserves message delivery failures with window and channel context", () => | 477 | it.effect("preserves message delivery failures with window and channel context", () => |
| ... | @@ -500,7 +500,7 @@ describe("ElectronWindow", () => { | ... | @@ -500,7 +500,7 @@ describe("ElectronWindow", () => { |
| 500 | assert.equal(error.channel, "desktop:update"); | 500 | assert.equal(error.channel, "desktop:update"); |
| 501 | assert.strictEqual(error.cause, cause); | 501 | assert.strictEqual(error.cause, cause); |
| 502 | } | 502 | } |
| 503 | }).pipe(Effect.provide(TestLayer)), | 503 | }).pipe(Effect.provide(layerTest)), |
| 504 | ); | 504 | ); |
| 505 | 505 | ||
| 506 | it.effect("preserves destroy failures and continues with later windows", () => | 506 | it.effect("preserves destroy failures and continues with later windows", () => |
| ... | @@ -531,7 +531,7 @@ describe("ElectronWindow", () => { | ... | @@ -531,7 +531,7 @@ describe("ElectronWindow", () => { |
| 531 | assert.strictEqual(error.cause, cause); | 531 | assert.strictEqual(error.cause, cause); |
| 532 | } | 532 | } |
| 533 | assert.equal(vi.mocked(laterWindow.destroy).mock.calls.length, 1); | 533 | assert.equal(vi.mocked(laterWindow.destroy).mock.calls.length, 1); |
| 534 | }).pipe(Effect.provide(TestLayer)), | 534 | }).pipe(Effect.provide(layerTest)), |
| 535 | ); | 535 | ); |
| 536 | it.effect("an ordinary reveal on Windows does not touch the Win32 foreground helpers", () => | 536 | it.effect("an ordinary reveal on Windows does not touch the Win32 foreground helpers", () => |
| 537 | Effect.gen(function* () { | 537 | Effect.gen(function* () { |
| ... | @@ -545,7 +545,7 @@ describe("ElectronWindow", () => { | ... | @@ -545,7 +545,7 @@ describe("ElectronWindow", () => { |
| 545 | assert.lengthOf(shellHostedForegroundMock.mock.calls, 0); | 545 | assert.lengthOf(shellHostedForegroundMock.mock.calls, 0); |
| 546 | assert.lengthOf(startWindowsForegroundFocusThreadMock.mock.calls, 0); | 546 | assert.lengthOf(startWindowsForegroundFocusThreadMock.mock.calls, 0); |
| 547 | assert.lengthOf(window.focus.mock.calls, 1); | 547 | assert.lengthOf(window.focus.mock.calls, 1); |
| 548 | }).pipe(Effect.provide(testLayer("win32"))), | 548 | }).pipe(Effect.provide(layerTestFor("win32"))), |
| 549 | ); | 549 | ); |
| 550 | 550 | ||
| 551 | it.effect("a capture reveal on Windows uses the Win32 path only once", () => | 551 | it.effect("a capture reveal on Windows uses the Win32 path only once", () => |
| ... | @@ -559,12 +559,12 @@ describe("ElectronWindow", () => { | ... | @@ -559,12 +559,12 @@ describe("ElectronWindow", () => { |
| 559 | 559 | ||
| 560 | assert.lengthOf(activateWindowsForegroundMock.mock.calls, 1); | 560 | assert.lengthOf(activateWindowsForegroundMock.mock.calls, 1); |
| 561 | assert.lengthOf(window.focus.mock.calls, 2); | 561 | assert.lengthOf(window.focus.mock.calls, 2); |
| 562 | }).pipe(Effect.provide(testLayer("win32"))), | 562 | }).pipe(Effect.provide(layerTestFor("win32"))), |
| 563 | ); | 563 | ); |
| 564 | 564 | ||
| 565 | it.effect("starts the Windows focus worker lazily and closes it with the layer", () => | 565 | it.effect("starts the Windows focus worker lazily and closes it with the layer", () => |
| 566 | Effect.gen(function* () { | 566 | Effect.gen(function* () { |
| 567 | yield* ElectronWindow.ElectronWindow.pipe(Effect.provide(testLayer("win32"))); | 567 | yield* ElectronWindow.ElectronWindow.pipe(Effect.provide(layerTestFor("win32"))); |
| 568 | assert.lengthOf(startWindowsForegroundFocusThreadMock.mock.calls, 0); | 568 | assert.lengthOf(startWindowsForegroundFocusThreadMock.mock.calls, 0); |
| 569 | assert.lengthOf(windowsForegroundCloseMock.mock.calls, 0); | 569 | assert.lengthOf(windowsForegroundCloseMock.mock.calls, 0); |
| 570 | 570 | ||
| ... | @@ -575,7 +575,7 @@ describe("ElectronWindow", () => { | ... | @@ -575,7 +575,7 @@ describe("ElectronWindow", () => { |
| 575 | yield* electronWindow.prepareReveal(window); | 575 | yield* electronWindow.prepareReveal(window); |
| 576 | assert.lengthOf(startWindowsForegroundFocusThreadMock.mock.calls, 1); | 576 | assert.lengthOf(startWindowsForegroundFocusThreadMock.mock.calls, 1); |
| 577 | assert.lengthOf(windowsForegroundCloseMock.mock.calls, 0); | 577 | assert.lengthOf(windowsForegroundCloseMock.mock.calls, 0); |
| 578 | }).pipe(Effect.provide(testLayer("win32"))); | 578 | }).pipe(Effect.provide(layerTestFor("win32"))); |
| 579 | assert.lengthOf(windowsForegroundCloseMock.mock.calls, 1); | 579 | assert.lengthOf(windowsForegroundCloseMock.mock.calls, 1); |
| 580 | }), | 580 | }), |
| 581 | ); | 581 | ); |
apps/desktop/src/ipc/methods/localEnvironment.test.ts+2-2| ... | @@ -14,7 +14,7 @@ import { getLocalEnvironmentEnabled, setLocalEnvironmentEnabled } from "./localE | ... | @@ -14,7 +14,7 @@ import { getLocalEnvironmentEnabled, setLocalEnvironmentEnabled } from "./localE |
| 14 | 14 | ||
| 15 | // `relaunch` declares the lifecycle runtime services as requirements even | 15 | // `relaunch` declares the lifecycle runtime services as requirements even |
| 16 | // though the mocked relaunch never touches them. | 16 | // though the mocked relaunch never touches them. |
| 17 | const unusedLifecycleRuntimeLayer = Layer.mergeAll( | 17 | const layerUnusedLifecycleRuntime = Layer.mergeAll( |
| 18 | DesktopShutdown.layer, | 18 | DesktopShutdown.layer, |
| 19 | DesktopState.layer, | 19 | DesktopState.layer, |
| 20 | Layer.succeed( | 20 | Layer.succeed( |
| ... | @@ -42,7 +42,7 @@ describe("local environment IPC", () => { | ... | @@ -42,7 +42,7 @@ describe("local environment IPC", () => { |
| 42 | relaunchReasons.push(reason); | 42 | relaunchReasons.push(reason); |
| 43 | }), | 43 | }), |
| 44 | }), | 44 | }), |
| 45 | unusedLifecycleRuntimeLayer, | 45 | layerUnusedLifecycleRuntime, |
| 46 | ); | 46 | ); |
| 47 | return Effect.gen(function* () { | 47 | return Effect.gen(function* () { |
| 48 | yield* setLocalEnvironmentEnabled.handler(false); | 48 | yield* setLocalEnvironmentEnabled.handler(false); |
apps/desktop/src/ipc/methods/sshEnvironment.test.ts+4-4| ... | @@ -22,7 +22,7 @@ function jsonResponse(request: HttpClientRequest.HttpClientRequest, body: unknow | ... | @@ -22,7 +22,7 @@ function jsonResponse(request: HttpClientRequest.HttpClientRequest, body: unknow |
| 22 | ); | 22 | ); |
| 23 | } | 23 | } |
| 24 | 24 | ||
| 25 | function makeHttpClientLayer( | 25 | function layerHttpClient( |
| 26 | handler: ( | 26 | handler: ( |
| 27 | request: HttpClientRequest.HttpClientRequest, | 27 | request: HttpClientRequest.HttpClientRequest, |
| 28 | ) => Effect.Effect<HttpClientResponse.HttpClientResponse, never>, | 28 | ) => Effect.Effect<HttpClientResponse.HttpClientResponse, never>, |
| ... | @@ -36,7 +36,7 @@ function makeHttpClientLayer( | ... | @@ -36,7 +36,7 @@ function makeHttpClientLayer( |
| 36 | describe("SSH environment IPC", () => { | 36 | describe("SSH environment IPC", () => { |
| 37 | it.effect("fetches and decodes the remote environment descriptor", () => { | 37 | it.effect("fetches and decodes the remote environment descriptor", () => { |
| 38 | const requestUrls: string[] = []; | 38 | const requestUrls: string[] = []; |
| 39 | const layer = makeHttpClientLayer((request) => | 39 | const layer = layerHttpClient((request) => |
| 40 | Effect.sync(() => { | 40 | Effect.sync(() => { |
| 41 | requestUrls.push(request.url); | 41 | requestUrls.push(request.url); |
| 42 | return jsonResponse(request, { | 42 | return jsonResponse(request, { |
| ... | @@ -66,7 +66,7 @@ describe("SSH environment IPC", () => { | ... | @@ -66,7 +66,7 @@ describe("SSH environment IPC", () => { |
| 66 | }); | 66 | }); |
| 67 | 67 | ||
| 68 | it.effect("wraps schema decode failures in a typed request error", () => { | 68 | it.effect("wraps schema decode failures in a typed request error", () => { |
| 69 | const layer = makeHttpClientLayer((request) => | 69 | const layer = layerHttpClient((request) => |
| 70 | Effect.succeed(jsonResponse(request, { environmentId: "remote-env" })), | 70 | Effect.succeed(jsonResponse(request, { environmentId: "remote-env" })), |
| 71 | ); | 71 | ); |
| 72 | 72 | ||
| ... | @@ -89,7 +89,7 @@ describe("SSH environment IPC", () => { | ... | @@ -89,7 +89,7 @@ describe("SSH environment IPC", () => { |
| 89 | 89 | ||
| 90 | it.effect("rejects non-loopback HTTP endpoints before issuing a request", () => { | 90 | it.effect("rejects non-loopback HTTP endpoints before issuing a request", () => { |
| 91 | let requestCount = 0; | 91 | let requestCount = 0; |
| 92 | const layer = makeHttpClientLayer((request) => | 92 | const layer = layerHttpClient((request) => |
| 93 | Effect.sync(() => { | 93 | Effect.sync(() => { |
| 94 | requestCount += 1; | 94 | requestCount += 1; |
| 95 | return jsonResponse(request, {}); | 95 | return jsonResponse(request, {}); |
apps/desktop/src/ipc/methods/wsl.test.ts+22-22| ... | @@ -28,7 +28,7 @@ const invokeSetWslDistro = (distro: string | null) => | ... | @@ -28,7 +28,7 @@ const invokeSetWslDistro = (distro: string | null) => |
| 28 | const invokeSetWslOnly = (enabled: boolean) => | 28 | const invokeSetWslOnly = (enabled: boolean) => |
| 29 | setWslOnly.handler(enabled).pipe(Effect.flatMap(decodeWslState)); | 29 | setWslOnly.handler(enabled).pipe(Effect.flatMap(decodeWslState)); |
| 30 | 30 | ||
| 31 | function makeWslBackendLayer(input: { readonly onReconcile?: Effect.Effect<void> } = {}) { | 31 | function layerWslBackend(input: { readonly onReconcile?: Effect.Effect<void> } = {}) { |
| 32 | return Layer.succeed( | 32 | return Layer.succeed( |
| 33 | DesktopWslBackend.DesktopWslBackend, | 33 | DesktopWslBackend.DesktopWslBackend, |
| 34 | DesktopWslBackend.DesktopWslBackend.of({ | 34 | DesktopWslBackend.DesktopWslBackend.of({ |
| ... | @@ -38,7 +38,7 @@ function makeWslBackendLayer(input: { readonly onReconcile?: Effect.Effect<void> | ... | @@ -38,7 +38,7 @@ function makeWslBackendLayer(input: { readonly onReconcile?: Effect.Effect<void> |
| 38 | ); | 38 | ); |
| 39 | } | 39 | } |
| 40 | 40 | ||
| 41 | function makeLifecycleLayer(relaunchReasons: Array<string>) { | 41 | function layerLifecycle(relaunchReasons: Array<string>) { |
| 42 | return Layer.succeed( | 42 | return Layer.succeed( |
| 43 | DesktopLifecycle.DesktopLifecycle, | 43 | DesktopLifecycle.DesktopLifecycle, |
| 44 | DesktopLifecycle.DesktopLifecycle.of({ | 44 | DesktopLifecycle.DesktopLifecycle.of({ |
| ... | @@ -51,7 +51,7 @@ function makeLifecycleLayer(relaunchReasons: Array<string>) { | ... | @@ -51,7 +51,7 @@ function makeLifecycleLayer(relaunchReasons: Array<string>) { |
| 51 | ); | 51 | ); |
| 52 | } | 52 | } |
| 53 | 53 | ||
| 54 | const unusedLifecycleRuntimeLayer = Layer.mergeAll( | 54 | const layerUnusedLifecycleRuntime = Layer.mergeAll( |
| 55 | DesktopShutdown.layer, | 55 | DesktopShutdown.layer, |
| 56 | DesktopState.layer, | 56 | DesktopState.layer, |
| 57 | Layer.succeed( | 57 | Layer.succeed( |
| ... | @@ -114,7 +114,7 @@ describe("WSL IPC", () => { | ... | @@ -114,7 +114,7 @@ describe("WSL IPC", () => { |
| 114 | wslOnly: true, | 114 | wslOnly: true, |
| 115 | }), | 115 | }), |
| 116 | DesktopWslEnvironment.layerTest(), | 116 | DesktopWslEnvironment.layerTest(), |
| 117 | makeWslBackendLayer(), | 117 | layerWslBackend(), |
| 118 | ), | 118 | ), |
| 119 | ), | 119 | ), |
| 120 | ), | 120 | ), |
| ... | @@ -129,9 +129,9 @@ describe("WSL IPC", () => { | ... | @@ -129,9 +129,9 @@ describe("WSL IPC", () => { |
| 129 | wslOnly: true, | 129 | wslOnly: true, |
| 130 | }), | 130 | }), |
| 131 | DesktopWslEnvironment.layerTest({ isAvailable: true }), | 131 | DesktopWslEnvironment.layerTest({ isAvailable: true }), |
| 132 | makeWslBackendLayer(), | 132 | layerWslBackend(), |
| 133 | makeLifecycleLayer(relaunchReasons), | 133 | layerLifecycle(relaunchReasons), |
| 134 | unusedLifecycleRuntimeLayer, | 134 | layerUnusedLifecycleRuntime, |
| 135 | ); | 135 | ); |
| 136 | 136 | ||
| 137 | return Effect.gen(function* () { | 137 | return Effect.gen(function* () { |
| ... | @@ -160,9 +160,9 @@ describe("WSL IPC", () => { | ... | @@ -160,9 +160,9 @@ describe("WSL IPC", () => { |
| 160 | wslOnly: false, | 160 | wslOnly: false, |
| 161 | }), | 161 | }), |
| 162 | DesktopWslEnvironment.layerTest({ isAvailable: true }), | 162 | DesktopWslEnvironment.layerTest({ isAvailable: true }), |
| 163 | makeWslBackendLayer(), | 163 | layerWslBackend(), |
| 164 | makeLifecycleLayer(relaunchReasons), | 164 | layerLifecycle(relaunchReasons), |
| 165 | unusedLifecycleRuntimeLayer, | 165 | layerUnusedLifecycleRuntime, |
| 166 | ); | 166 | ); |
| 167 | 167 | ||
| 168 | return Effect.gen(function* () { | 168 | return Effect.gen(function* () { |
| ... | @@ -197,13 +197,13 @@ describe("WSL IPC", () => { | ... | @@ -197,13 +197,13 @@ describe("WSL IPC", () => { |
| 197 | wslOnly: true, | 197 | wslOnly: true, |
| 198 | }), | 198 | }), |
| 199 | DesktopWslEnvironment.layerTest({ isAvailable: true }), | 199 | DesktopWslEnvironment.layerTest({ isAvailable: true }), |
| 200 | makeWslBackendLayer({ | 200 | layerWslBackend({ |
| 201 | onReconcile: Effect.sync(() => { | 201 | onReconcile: Effect.sync(() => { |
| 202 | reconcileCount += 1; | 202 | reconcileCount += 1; |
| 203 | }), | 203 | }), |
| 204 | }), | 204 | }), |
| 205 | makeLifecycleLayer(relaunchReasons), | 205 | layerLifecycle(relaunchReasons), |
| 206 | unusedLifecycleRuntimeLayer, | 206 | layerUnusedLifecycleRuntime, |
| 207 | ); | 207 | ); |
| 208 | 208 | ||
| 209 | return Effect.gen(function* () { | 209 | return Effect.gen(function* () { |
| ... | @@ -232,13 +232,13 @@ describe("WSL IPC", () => { | ... | @@ -232,13 +232,13 @@ describe("WSL IPC", () => { |
| 232 | wslOnly: false, | 232 | wslOnly: false, |
| 233 | }), | 233 | }), |
| 234 | DesktopWslEnvironment.layerTest({ isAvailable: true }), | 234 | DesktopWslEnvironment.layerTest({ isAvailable: true }), |
| 235 | makeWslBackendLayer({ | 235 | layerWslBackend({ |
| 236 | onReconcile: Effect.sync(() => { | 236 | onReconcile: Effect.sync(() => { |
| 237 | reconcileCount += 1; | 237 | reconcileCount += 1; |
| 238 | }), | 238 | }), |
| 239 | }), | 239 | }), |
| 240 | makeLifecycleLayer(relaunchReasons), | 240 | layerLifecycle(relaunchReasons), |
| 241 | unusedLifecycleRuntimeLayer, | 241 | layerUnusedLifecycleRuntime, |
| 242 | ); | 242 | ); |
| 243 | 243 | ||
| 244 | return Effect.gen(function* () { | 244 | return Effect.gen(function* () { |
| ... | @@ -261,13 +261,13 @@ describe("WSL IPC", () => { | ... | @@ -261,13 +261,13 @@ describe("WSL IPC", () => { |
| 261 | wslOnly: true, | 261 | wslOnly: true, |
| 262 | }), | 262 | }), |
| 263 | DesktopWslEnvironment.layerTest({ isAvailable: true }), | 263 | DesktopWslEnvironment.layerTest({ isAvailable: true }), |
| 264 | makeWslBackendLayer({ | 264 | layerWslBackend({ |
| 265 | onReconcile: Effect.sync(() => { | 265 | onReconcile: Effect.sync(() => { |
| 266 | reconcileCount += 1; | 266 | reconcileCount += 1; |
| 267 | }), | 267 | }), |
| 268 | }), | 268 | }), |
| 269 | makeLifecycleLayer(relaunchReasons), | 269 | layerLifecycle(relaunchReasons), |
| 270 | unusedLifecycleRuntimeLayer, | 270 | layerUnusedLifecycleRuntime, |
| 271 | ); | 271 | ); |
| 272 | 272 | ||
| 273 | return Effect.gen(function* () { | 273 | return Effect.gen(function* () { |
| ... | @@ -300,13 +300,13 @@ describe("WSL IPC", () => { | ... | @@ -300,13 +300,13 @@ describe("WSL IPC", () => { |
| 300 | wslOnly: false, | 300 | wslOnly: false, |
| 301 | }), | 301 | }), |
| 302 | DesktopWslEnvironment.layerTest({ isAvailable: true }), | 302 | DesktopWslEnvironment.layerTest({ isAvailable: true }), |
| 303 | makeWslBackendLayer({ | 303 | layerWslBackend({ |
| 304 | onReconcile: Effect.sync(() => { | 304 | onReconcile: Effect.sync(() => { |
| 305 | reconcileCount += 1; | 305 | reconcileCount += 1; |
| 306 | }), | 306 | }), |
| 307 | }), | 307 | }), |
| 308 | makeLifecycleLayer(relaunchReasons), | 308 | layerLifecycle(relaunchReasons), |
| 309 | unusedLifecycleRuntimeLayer, | 309 | layerUnusedLifecycleRuntime, |
| 310 | ); | 310 | ); |
| 311 | 311 | ||
| 312 | return Effect.gen(function* () { | 312 | return Effect.gen(function* () { |
apps/desktop/src/main.ts+37-37| ... | @@ -82,7 +82,7 @@ if (process.argv.includes("--version")) { | ... | @@ -82,7 +82,7 @@ if (process.argv.includes("--version")) { |
| 82 | Electron.app.exit(0); | 82 | Electron.app.exit(0); |
| 83 | } | 83 | } |
| 84 | 84 | ||
| 85 | const desktopEnvironmentLayer = Layer.unwrap( | 85 | const layerDesktopEnvironment = Layer.unwrap( |
| 86 | Effect.gen(function* () { | 86 | Effect.gen(function* () { |
| 87 | const metadata = yield* Effect.service(ElectronApp.ElectronApp).pipe( | 87 | const metadata = yield* Effect.service(ElectronApp.ElectronApp).pipe( |
| 88 | Effect.flatMap((app) => app.metadata), | 88 | Effect.flatMap((app) => app.metadata), |
| ... | @@ -115,7 +115,7 @@ const resolveDesktopSshCliRunner = ( | ... | @@ -115,7 +115,7 @@ const resolveDesktopSshCliRunner = ( |
| 115 | return { archiveVersion: environment.appVersion }; | 115 | return { archiveVersion: environment.appVersion }; |
| 116 | }; | 116 | }; |
| 117 | 117 | ||
| 118 | const desktopSshEnvironmentLayer = Layer.unwrap( | 118 | const layerDesktopSshEnvironment = Layer.unwrap( |
| 119 | Effect.gen(function* () { | 119 | Effect.gen(function* () { |
| 120 | const environment = yield* DesktopEnvironment.DesktopEnvironment; | 120 | const environment = yield* DesktopEnvironment.DesktopEnvironment; |
| 121 | return DesktopSshEnvironment.layer({ | 121 | return DesktopSshEnvironment.layer({ |
| ... | @@ -124,7 +124,7 @@ const desktopSshEnvironmentLayer = Layer.unwrap( | ... | @@ -124,7 +124,7 @@ const desktopSshEnvironmentLayer = Layer.unwrap( |
| 124 | }), | 124 | }), |
| 125 | ); | 125 | ); |
| 126 | 126 | ||
| 127 | const electronLayer = Layer.mergeAll( | 127 | const layerElectron = Layer.mergeAll( |
| 128 | ElectronApp.layer, | 128 | ElectronApp.layer, |
| 129 | ElectronDialog.layer, | 129 | ElectronDialog.layer, |
| 130 | ElectronMenu.layer, | 130 | ElectronMenu.layer, |
| ... | @@ -138,7 +138,7 @@ const electronLayer = Layer.mergeAll( | ... | @@ -138,7 +138,7 @@ const electronLayer = Layer.mergeAll( |
| 138 | DesktopIpc.layer(Electron.ipcMain), | 138 | DesktopIpc.layer(Electron.ipcMain), |
| 139 | ); | 139 | ); |
| 140 | 140 | ||
| 141 | const desktopFoundationLayer = Layer.mergeAll( | 141 | const layerDesktopFoundation = Layer.mergeAll( |
| 142 | MacPermissions.layer, | 142 | MacPermissions.layer, |
| 143 | DesktopState.layer, | 143 | DesktopState.layer, |
| 144 | DesktopShutdown.layer, | 144 | DesktopShutdown.layer, |
| ... | @@ -149,36 +149,36 @@ const desktopFoundationLayer = Layer.mergeAll( | ... | @@ -149,36 +149,36 @@ const desktopFoundationLayer = Layer.mergeAll( |
| 149 | DesktopAssets.layer, | 149 | DesktopAssets.layer, |
| 150 | DesktopObservability.layer, | 150 | DesktopObservability.layer, |
| 151 | DesktopRendererHistory.layer, | 151 | DesktopRendererHistory.layer, |
| 152 | ).pipe(Layer.provideMerge(desktopEnvironmentLayer)); | 152 | ).pipe(Layer.provideMerge(layerDesktopEnvironment)); |
| 153 | 153 | ||
| 154 | const desktopSshLayer = desktopSshEnvironmentLayer.pipe( | 154 | const layerDesktopSsh = layerDesktopSshEnvironment.pipe( |
| 155 | Layer.provideMerge(DesktopSshPasswordPrompts.layer()), | 155 | Layer.provideMerge(DesktopSshPasswordPrompts.layer()), |
| 156 | ); | 156 | ); |
| 157 | 157 | ||
| 158 | const desktopServerExposureLayer = DesktopServerExposure.layer.pipe( | 158 | const layerDesktopServerExposure = DesktopServerExposure.layer.pipe( |
| 159 | Layer.provideMerge(DesktopNetworkInterfaces.layer), | 159 | Layer.provideMerge(DesktopNetworkInterfaces.layer), |
| 160 | Layer.provideMerge(desktopFoundationLayer), | 160 | Layer.provideMerge(layerDesktopFoundation), |
| 161 | ); | 161 | ); |
| 162 | 162 | ||
| 163 | const desktopPreviewLayer = PreviewManager.layer.pipe( | 163 | const layerDesktopPreview = PreviewManager.layer.pipe( |
| 164 | // Merged rather than provided so the IPC handlers can reach the import | 164 | // Merged rather than provided so the IPC handlers can reach the import |
| 165 | // service alongside the manager; both sit on the same BrowserSession. | 165 | // service alongside the manager; both sit on the same BrowserSession. |
| 166 | Layer.provideMerge(BrowserImport.layer.pipe(Layer.provide(LinuxBrowserSecret.layer))), | 166 | Layer.provideMerge(BrowserImport.layer.pipe(Layer.provide(LinuxBrowserSecret.layer))), |
| 167 | Layer.provideMerge(BrowserSession.layer), | 167 | Layer.provideMerge(BrowserSession.layer), |
| 168 | Layer.provideMerge(desktopFoundationLayer), | 168 | Layer.provideMerge(layerDesktopFoundation), |
| 169 | ); | 169 | ); |
| 170 | 170 | ||
| 171 | const desktopWindowLayer = DesktopWindow.layer.pipe( | 171 | const layerDesktopWindow = DesktopWindow.layer.pipe( |
| 172 | Layer.provideMerge(desktopServerExposureLayer), | 172 | Layer.provideMerge(layerDesktopServerExposure), |
| 173 | Layer.provideMerge(desktopPreviewLayer), | 173 | Layer.provideMerge(layerDesktopPreview), |
| 174 | ); | 174 | ); |
| 175 | 175 | ||
| 176 | const desktopSnapShotLayer = DesktopSnapShot.layer.pipe( | 176 | const layerDesktopSnapShot = DesktopSnapShot.layer.pipe( |
| 177 | Layer.provideMerge(desktopWindowLayer), | 177 | Layer.provideMerge(layerDesktopWindow), |
| 178 | Layer.provideMerge(desktopFoundationLayer), | 178 | Layer.provideMerge(layerDesktopFoundation), |
| 179 | ); | 179 | ); |
| 180 | const desktopAppActivationLayer = DesktopAppActivation.layer.pipe( | 180 | const layerDesktopAppActivation = DesktopAppActivation.layer.pipe( |
| 181 | Layer.provide(desktopWindowLayer), | 181 | Layer.provide(layerDesktopWindow), |
| 182 | ); | 182 | ); |
| 183 | 183 | ||
| 184 | // Pool layer instantiates the backend factory once for the Windows | 184 | // Pool layer instantiates the backend factory once for the Windows |
| ... | @@ -186,63 +186,63 @@ const desktopAppActivationLayer = DesktopAppActivation.layer.pipe( | ... | @@ -186,63 +186,63 @@ const desktopAppActivationLayer = DesktopAppActivation.layer.pipe( |
| 186 | // the pool now; the legacy DesktopBackendManager service is gone. The | 186 | // the pool now; the legacy DesktopBackendManager service is gone. The |
| 187 | // WSL second instance gets registered later in the migration. See | 187 | // WSL second instance gets registered later in the migration. See |
| 188 | // DesktopBackendPool.ts header for the full rollout plan. | 188 | // DesktopBackendPool.ts header for the full rollout plan. |
| 189 | const desktopBackendLayer = DesktopBackendPool.layer.pipe( | 189 | const layerDesktopBackend = DesktopBackendPool.layer.pipe( |
| 190 | Layer.provideMerge(DesktopAppIdentity.layer), | 190 | Layer.provideMerge(DesktopAppIdentity.layer), |
| 191 | Layer.provideMerge(DesktopBackendConfiguration.layer), | 191 | Layer.provideMerge(DesktopBackendConfiguration.layer), |
| 192 | Layer.provideMerge(DesktopWslEnvironment.layer), | 192 | Layer.provideMerge(DesktopWslEnvironment.layer), |
| 193 | Layer.provideMerge(DesktopWslServerTree.layer), | 193 | Layer.provideMerge(DesktopWslServerTree.layer), |
| 194 | Layer.provideMerge(DesktopTelemetryPublisher.layer), | 194 | Layer.provideMerge(DesktopTelemetryPublisher.layer), |
| 195 | Layer.provideMerge(desktopWindowLayer), | 195 | Layer.provideMerge(layerDesktopWindow), |
| 196 | ); | 196 | ); |
| 197 | 197 | ||
| 198 | // WSL orchestrator hangs off the backend layer because it needs the | 198 | // WSL orchestrator hangs off the backend layer because it needs the |
| 199 | // pool + configuration + serverExposure; it pulls NetService and the | 199 | // pool + configuration + serverExposure; it pulls NetService and the |
| 200 | // foundation services through the same provideMerge chain. | 200 | // foundation services through the same provideMerge chain. |
| 201 | const desktopWslBackendLayer = DesktopWslBackend.layer.pipe( | 201 | const layerDesktopWslBackend = DesktopWslBackend.layer.pipe( |
| 202 | Layer.provideMerge(desktopBackendLayer), | 202 | Layer.provideMerge(layerDesktopBackend), |
| 203 | ); | 203 | ); |
| 204 | 204 | ||
| 205 | const desktopLocalEnvironmentAuthLayer = DesktopLocalEnvironmentAuth.layer.pipe( | 205 | const layerDesktopLocalEnvironmentAuth = DesktopLocalEnvironmentAuth.layer.pipe( |
| 206 | Layer.provideMerge(desktopBackendLayer), | 206 | Layer.provideMerge(layerDesktopBackend), |
| 207 | ); | 207 | ); |
| 208 | 208 | ||
| 209 | const desktopApplicationLayer = Layer.mergeAll( | 209 | const layerDesktopApplication = Layer.mergeAll( |
| 210 | DesktopLifecycle.layer, | 210 | DesktopLifecycle.layer, |
| 211 | desktopAppActivationLayer, | 211 | layerDesktopAppActivation, |
| 212 | DesktopApplicationMenu.layer, | 212 | DesktopApplicationMenu.layer, |
| 213 | DesktopLinuxUrlHandler.layer, | 213 | DesktopLinuxUrlHandler.layer, |
| 214 | DesktopShellEnvironment.layer, | 214 | DesktopShellEnvironment.layer, |
| 215 | desktopSshLayer, | 215 | layerDesktopSsh, |
| 216 | ).pipe( | 216 | ).pipe( |
| 217 | Layer.provideMerge(desktopSnapShotLayer), | 217 | Layer.provideMerge(layerDesktopSnapShot), |
| 218 | Layer.provideMerge(DesktopUpdates.layer), | 218 | Layer.provideMerge(DesktopUpdates.layer), |
| 219 | Layer.provideMerge(desktopWslBackendLayer), | 219 | Layer.provideMerge(layerDesktopWslBackend), |
| 220 | Layer.provideMerge(desktopLocalEnvironmentAuthLayer), | 220 | Layer.provideMerge(layerDesktopLocalEnvironmentAuth), |
| 221 | ); | 221 | ); |
| 222 | 222 | ||
| 223 | // Clerk resolves userData before Electron is ready, so it gets the synchronous FileSystem. | 223 | // Clerk resolves userData before Electron is ready, so it gets the synchronous FileSystem. |
| 224 | const desktopClerkLayer = DesktopClerk.layer.pipe( | 224 | const layerDesktopClerk = DesktopClerk.layer.pipe( |
| 225 | Layer.provide(DesktopPreReadyFileSystem.layer), | 225 | Layer.provide(DesktopPreReadyFileSystem.layer), |
| 226 | Layer.provideMerge(ElectronShell.layer), | 226 | Layer.provideMerge(ElectronShell.layer), |
| 227 | Layer.provideMerge(desktopEnvironmentLayer), | 227 | Layer.provideMerge(layerDesktopEnvironment), |
| 228 | Layer.provideMerge(NodeServices.layer), | 228 | Layer.provideMerge(NodeServices.layer), |
| 229 | Layer.provideMerge(ElectronApp.layer), | 229 | Layer.provideMerge(ElectronApp.layer), |
| 230 | ); | 230 | ); |
| 231 | 231 | ||
| 232 | const desktopApplicationRuntimeLayer = desktopApplicationLayer.pipe( | 232 | const layerDesktopApplicationRuntime = layerDesktopApplication.pipe( |
| 233 | Layer.provideMerge(NodeServices.layer), | 233 | Layer.provideMerge(NodeServices.layer), |
| 234 | Layer.provideMerge(NodeHttpClient.layerUndici), | 234 | Layer.provideMerge(NodeHttpClient.layerUndici), |
| 235 | Layer.provideMerge(NetService.layer), | 235 | Layer.provideMerge(NetService.layer), |
| 236 | Layer.provideMerge(electronLayer), | 236 | Layer.provideMerge(layerElectron), |
| 237 | ); | 237 | ); |
| 238 | 238 | ||
| 239 | // Acquire strict pre-ready setup before Clerk. Nothing before the Clerk bridge | 239 | // Acquire strict pre-ready setup before Clerk. Nothing before the Clerk bridge |
| 240 | // may yield, or Electron can emit ready before Clerk registers its scheme. | 240 | // may yield, or Electron can emit ready before Clerk registers its scheme. |
| 241 | const desktopRuntimeLayer = desktopClerkLayer.pipe( | 241 | const layerDesktopRuntime = layerDesktopClerk.pipe( |
| 242 | Layer.flatMap((clerkContext) => | 242 | Layer.flatMap((clerkContext) => |
| 243 | desktopApplicationRuntimeLayer.pipe(Layer.provideMerge(Layer.succeedContext(clerkContext))), | 243 | layerDesktopApplicationRuntime.pipe(Layer.provideMerge(Layer.succeedContext(clerkContext))), |
| 244 | ), | 244 | ), |
| 245 | Layer.provideMerge(DesktopPreReadyPlatform.layer), | 245 | Layer.provideMerge(DesktopPreReadyPlatform.layer), |
| 246 | ); | 246 | ); |
| 247 | 247 | ||
| 248 | DesktopApp.program.pipe(Effect.provide(desktopRuntimeLayer), NodeRuntime.runMain); | 248 | DesktopApp.program.pipe(Effect.provide(layerDesktopRuntime), NodeRuntime.runMain); |
apps/desktop/src/preview/BrowserImport/BrowserImport.test.ts+4-4| ... | @@ -34,7 +34,7 @@ const cookie = { | ... | @@ -34,7 +34,7 @@ const cookie = { |
| 34 | * Dies if the import reaches session work: every case here covers a request | 34 | * Dies if the import reaches session work: every case here covers a request |
| 35 | * that must be rejected before a cookie is read or written. | 35 | * that must be rejected before a cookie is read or written. |
| 36 | */ | 36 | */ |
| 37 | const rejectedBeforeSession = Layer.succeed( | 37 | const layerRejectedBeforeSession = Layer.succeed( |
| 38 | BrowserSession.BrowserSession, | 38 | BrowserSession.BrowserSession, |
| 39 | BrowserSession.BrowserSession.of({ | 39 | BrowserSession.BrowserSession.of({ |
| 40 | getPartition: () => Effect.die("getPartition must not be reached"), | 40 | getPartition: () => Effect.die("getPartition must not be reached"), |
| ... | @@ -52,7 +52,7 @@ const rejectedBeforeSession = Layer.succeed( | ... | @@ -52,7 +52,7 @@ const rejectedBeforeSession = Layer.succeed( |
| 52 | const withImporter = Effect.fnUntraced(function* () { | 52 | const withImporter = Effect.fnUntraced(function* () { |
| 53 | const fileSystem = yield* FileSystem.FileSystem; | 53 | const fileSystem = yield* FileSystem.FileSystem; |
| 54 | const home = yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3code-import-" }); | 54 | const home = yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3code-import-" }); |
| 55 | const environment = Layer.succeed(HostProcessEnvironment, { HOME: home }); | 55 | const layerEnvironment = Layer.succeed(HostProcessEnvironment, { HOME: home }); |
| 56 | const context = yield* sourcePathContext.pipe( | 56 | const context = yield* sourcePathContext.pipe( |
| 57 | Effect.provideService(HostProcessEnvironment, { HOME: home }), | 57 | Effect.provideService(HostProcessEnvironment, { HOME: home }), |
| 58 | Effect.provideService(HostProcessPlatform, "darwin"), | 58 | Effect.provideService(HostProcessPlatform, "darwin"), |
| ... | @@ -67,8 +67,8 @@ const withImporter = Effect.fnUntraced(function* () { | ... | @@ -67,8 +67,8 @@ const withImporter = Effect.fnUntraced(function* () { |
| 67 | const importer = yield* BrowserImport.BrowserImport.pipe( | 67 | const importer = yield* BrowserImport.BrowserImport.pipe( |
| 68 | Effect.provide( | 68 | Effect.provide( |
| 69 | BrowserImport.layer.pipe( | 69 | BrowserImport.layer.pipe( |
| 70 | Layer.provide(rejectedBeforeSession), | 70 | Layer.provide(layerRejectedBeforeSession), |
| 71 | Layer.provide(environment), | 71 | Layer.provide(layerEnvironment), |
| 72 | Layer.provide(Layer.succeed(HostProcessPlatform, "darwin")), | 72 | Layer.provide(Layer.succeed(HostProcessPlatform, "darwin")), |
| 73 | Layer.provide(Layer.succeed(HostProcessExecutablePath, "/Applications/T3 Code.app")), | 73 | Layer.provide(Layer.succeed(HostProcessExecutablePath, "/Applications/T3 Code.app")), |
| 74 | Layer.provide(NodeServices.layer), | 74 | Layer.provide(NodeServices.layer), |
apps/desktop/src/preview/BrowserImport/ChromiumKeys.test.ts+15-15| ... | @@ -39,7 +39,7 @@ type CapturedCommand = { | ... | @@ -39,7 +39,7 @@ type CapturedCommand = { |
| 39 | }; | 39 | }; |
| 40 | }; | 40 | }; |
| 41 | 41 | ||
| 42 | const helperLayer = (input: { | 42 | const layerHelper = (input: { |
| 43 | readonly stdout?: string; | 43 | readonly stdout?: string; |
| 44 | readonly stderr?: string; | 44 | readonly stderr?: string; |
| 45 | readonly stdoutStream?: Stream.Stream<Uint8Array>; | 45 | readonly stdoutStream?: Stream.Stream<Uint8Array>; |
| ... | @@ -83,7 +83,7 @@ describe("macOS Chromium secrets", () => { | ... | @@ -83,7 +83,7 @@ describe("macOS Chromium secrets", () => { |
| 83 | keychainAccount: "Chrome", | 83 | keychainAccount: "Chrome", |
| 84 | linuxSecretApplication: undefined, | 84 | linuxSecretApplication: undefined, |
| 85 | } as const; | 85 | } as const; |
| 86 | const noProcesses = Layer.succeed( | 86 | const layerNoProcesses = Layer.succeed( |
| 87 | ChildProcessSpawner.ChildProcessSpawner, | 87 | ChildProcessSpawner.ChildProcessSpawner, |
| 88 | ChildProcessSpawner.make(() => Effect.die("must not spawn")), | 88 | ChildProcessSpawner.make(() => Effect.die("must not spawn")), |
| 89 | ); | 89 | ); |
| ... | @@ -93,7 +93,7 @@ describe("macOS Chromium secrets", () => { | ... | @@ -93,7 +93,7 @@ describe("macOS Chromium secrets", () => { |
| 93 | getPassword.mockReturnValue("macos-secret"); | 93 | getPassword.mockReturnValue("macos-secret"); |
| 94 | const keys = yield* resolveChromiumKeys(request); | 94 | const keys = yield* resolveChromiumKeys(request); |
| 95 | expect(keys.cbcV10?.toString("hex")).toBe("3df7306fb1eac353289565a2f6b64f74"); | 95 | expect(keys.cbcV10?.toString("hex")).toBe("3df7306fb1eac353289565a2f6b64f74"); |
| 96 | }).pipe(Effect.provide(noProcesses)), | 96 | }).pipe(Effect.provide(layerNoProcesses)), |
| 97 | ); | 97 | ); |
| 98 | 98 | ||
| 99 | it.effect("reports a missing keychain entry", () => | 99 | it.effect("reports a missing keychain entry", () => |
| ... | @@ -101,7 +101,7 @@ describe("macOS Chromium secrets", () => { | ... | @@ -101,7 +101,7 @@ describe("macOS Chromium secrets", () => { |
| 101 | getPassword.mockReturnValue(null); | 101 | getPassword.mockReturnValue(null); |
| 102 | const error = yield* resolveChromiumKeys(request).pipe(Effect.flip); | 102 | const error = yield* resolveChromiumKeys(request).pipe(Effect.flip); |
| 103 | expect(error.reason).toBe("keychainItemMissing"); | 103 | expect(error.reason).toBe("keychainItemMissing"); |
| 104 | }).pipe(Effect.provide(noProcesses)), | 104 | }).pipe(Effect.provide(layerNoProcesses)), |
| 105 | ); | 105 | ); |
| 106 | 106 | ||
| 107 | it.effect("preserves a denied keychain approval", () => | 107 | it.effect("preserves a denied keychain approval", () => |
| ... | @@ -113,7 +113,7 @@ describe("macOS Chromium secrets", () => { | ... | @@ -113,7 +113,7 @@ describe("macOS Chromium secrets", () => { |
| 113 | const error = yield* resolveChromiumKeys(request).pipe(Effect.flip); | 113 | const error = yield* resolveChromiumKeys(request).pipe(Effect.flip); |
| 114 | expect(error.reason).toBe("needsKeychainApproval"); | 114 | expect(error.reason).toBe("needsKeychainApproval"); |
| 115 | expect(error.cause).toBe(denied); | 115 | expect(error.cause).toBe(denied); |
| 116 | }).pipe(Effect.provide(noProcesses)), | 116 | }).pipe(Effect.provide(layerNoProcesses)), |
| 117 | ); | 117 | ); |
| 118 | }); | 118 | }); |
| 119 | 119 | ||
| ... | @@ -131,7 +131,7 @@ describe("Linux Chromium secrets", () => { | ... | @@ -131,7 +131,7 @@ describe("Linux Chromium secrets", () => { |
| 131 | expect(keys.cbcV11Error?.reason).toBe("keychainUnavailable"); | 131 | expect(keys.cbcV11Error?.reason).toBe("keychainUnavailable"); |
| 132 | }).pipe( | 132 | }).pipe( |
| 133 | Effect.provide( | 133 | Effect.provide( |
| 134 | helperLayer({ | 134 | layerHelper({ |
| 135 | spawnError: PlatformError.systemError({ | 135 | spawnError: PlatformError.systemError({ |
| 136 | _tag: "NotFound", | 136 | _tag: "NotFound", |
| 137 | module: "ChildProcess", | 137 | module: "ChildProcess", |
| ... | @@ -171,7 +171,7 @@ describe("Linux Chromium secrets", () => { | ... | @@ -171,7 +171,7 @@ describe("Linux Chromium secrets", () => { |
| 171 | expect(keys.cbcV11).toHaveLength(16); | 171 | expect(keys.cbcV11).toHaveLength(16); |
| 172 | }).pipe( | 172 | }).pipe( |
| 173 | Effect.provide( | 173 | Effect.provide( |
| 174 | helperLayer({ stdout: "linux-secret", capture: (value) => (captured = value) }), | 174 | layerHelper({ stdout: "linux-secret", capture: (value) => (captured = value) }), |
| 175 | ), | 175 | ), |
| 176 | ); | 176 | ); |
| 177 | }); | 177 | }); |
| ... | @@ -183,7 +183,7 @@ describe("Linux Chromium secrets", () => { | ... | @@ -183,7 +183,7 @@ describe("Linux Chromium secrets", () => { |
| 183 | expect(error.reason).toBe("keychainUnavailable"); | 183 | expect(error.reason).toBe("keychainUnavailable"); |
| 184 | }).pipe( | 184 | }).pipe( |
| 185 | Effect.provide( | 185 | Effect.provide( |
| 186 | helperLayer({ stderr: "Cannot autolaunch D-Bus without X11 $DISPLAY", exitCode: 1 }), | 186 | layerHelper({ stderr: "Cannot autolaunch D-Bus without X11 $DISPLAY", exitCode: 1 }), |
| 187 | ), | 187 | ), |
| 188 | ), | 188 | ), |
| 189 | ); | 189 | ); |
| ... | @@ -192,7 +192,7 @@ describe("Linux Chromium secrets", () => { | ... | @@ -192,7 +192,7 @@ describe("Linux Chromium secrets", () => { |
| 192 | Effect.gen(function* () { | 192 | Effect.gen(function* () { |
| 193 | const secret = yield* readLinuxSecret("chrome"); | 193 | const secret = yield* readLinuxSecret("chrome"); |
| 194 | expect(secret).toBe("linux-secret \t\n"); | 194 | expect(secret).toBe("linux-secret \t\n"); |
| 195 | }).pipe(Effect.provide(helperLayer({ stdout: "linux-secret \t\n" }))), | 195 | }).pipe(Effect.provide(layerHelper({ stdout: "linux-secret \t\n" }))), |
| 196 | ); | 196 | ); |
| 197 | 197 | ||
| 198 | it.effect("drains stdout and stderr concurrently", () => | 198 | it.effect("drains stdout and stderr concurrently", () => |
| ... | @@ -206,7 +206,7 @@ describe("Linux Chromium secrets", () => { | ... | @@ -206,7 +206,7 @@ describe("Linux Chromium secrets", () => { |
| 206 | ); | 206 | ); |
| 207 | 207 | ||
| 208 | const secret = yield* readLinuxSecret("chrome").pipe( | 208 | const secret = yield* readLinuxSecret("chrome").pipe( |
| 209 | Effect.provide(helperLayer({ stdoutStream: stdout, stderrStream: stderr })), | 209 | Effect.provide(layerHelper({ stdoutStream: stdout, stderrStream: stderr })), |
| 210 | ); | 210 | ); |
| 211 | 211 | ||
| 212 | expect(secret).toBe("linux-secret"); | 212 | expect(secret).toBe("linux-secret"); |
| ... | @@ -226,7 +226,7 @@ describe("Linux Chromium secrets", () => { | ... | @@ -226,7 +226,7 @@ describe("Linux Chromium secrets", () => { |
| 226 | expect(captured?.options.env?.SESSION_MARKER).toBe("kept"); | 226 | expect(captured?.options.env?.SESSION_MARKER).toBe("kept"); |
| 227 | }).pipe( | 227 | }).pipe( |
| 228 | Effect.provide( | 228 | Effect.provide( |
| 229 | helperLayer({ | 229 | layerHelper({ |
| 230 | stderr: "Zugriff verweigert", | 230 | stderr: "Zugriff verweigert", |
| 231 | exitCode: 3, | 231 | exitCode: 3, |
| 232 | capture: (value) => (captured = value), | 232 | capture: (value) => (captured = value), |
| ... | @@ -250,7 +250,7 @@ describe("Linux Chromium secrets", () => { | ... | @@ -250,7 +250,7 @@ describe("Linux Chromium secrets", () => { |
| 250 | linuxSecretApplication: "brave", | 250 | linuxSecretApplication: "brave", |
| 251 | }).pipe(Effect.flip); | 251 | }).pipe(Effect.flip); |
| 252 | expect(error.reason).toBe("needsKeychainApproval"); | 252 | expect(error.reason).toBe("needsKeychainApproval"); |
| 253 | }).pipe(Effect.provide(helperLayer({ stderr: "Keyring is locked", exitCode: 3 }))), | 253 | }).pipe(Effect.provide(layerHelper({ stderr: "Keyring is locked", exitCode: 3 }))), |
| 254 | ); | 254 | ); |
| 255 | 255 | ||
| 256 | it.effect("keeps the v10 fallback when the Secret Service backend is unavailable", () => | 256 | it.effect("keeps the v10 fallback when the Secret Service backend is unavailable", () => |
| ... | @@ -265,7 +265,7 @@ describe("Linux Chromium secrets", () => { | ... | @@ -265,7 +265,7 @@ describe("Linux Chromium secrets", () => { |
| 265 | expect(keys.cbcV11).toBeUndefined(); | 265 | expect(keys.cbcV11).toBeUndefined(); |
| 266 | }).pipe( | 266 | }).pipe( |
| 267 | Effect.provide( | 267 | Effect.provide( |
| 268 | helperLayer({ | 268 | layerHelper({ |
| 269 | stderr: "Cannot autolaunch D-Bus without X11 $DISPLAY", | 269 | stderr: "Cannot autolaunch D-Bus without X11 $DISPLAY", |
| 270 | exitCode: 1, | 270 | exitCode: 1, |
| 271 | }), | 271 | }), |
| ... | @@ -283,7 +283,7 @@ describe("Linux Chromium secrets", () => { | ... | @@ -283,7 +283,7 @@ describe("Linux Chromium secrets", () => { |
| 283 | }); | 283 | }); |
| 284 | expect(keys.cbcV10).toHaveLength(16); | 284 | expect(keys.cbcV10).toHaveLength(16); |
| 285 | expect(keys.cbcV11).toBeUndefined(); | 285 | expect(keys.cbcV11).toBeUndefined(); |
| 286 | }).pipe(Effect.provide(helperLayer({ exitCode: 2 }))), | 286 | }).pipe(Effect.provide(layerHelper({ exitCode: 2 }))), |
| 287 | ); | 287 | ); |
| 288 | }); | 288 | }); |
| 289 | 289 | ||
| ... | @@ -320,7 +320,7 @@ describe("Windows Chromium secrets", () => { | ... | @@ -320,7 +320,7 @@ describe("Windows Chromium secrets", () => { |
| 320 | expect(captured?.args.join(" ")).not.toContain(wrapped.toString("base64")); | 320 | expect(captured?.args.join(" ")).not.toContain(wrapped.toString("base64")); |
| 321 | }).pipe( | 321 | }).pipe( |
| 322 | Effect.provide( | 322 | Effect.provide( |
| 323 | helperLayer({ stdout: key.toString("base64"), capture: (value) => (captured = value) }), | 323 | layerHelper({ stdout: key.toString("base64"), capture: (value) => (captured = value) }), |
| 324 | ), | 324 | ), |
| 325 | Effect.provideService(HostProcessEnvironment, { SystemRoot: "C:\\Windows" }), | 325 | Effect.provideService(HostProcessEnvironment, { SystemRoot: "C:\\Windows" }), |
| 326 | ); | 326 | ); |
apps/desktop/src/preview/BrowserImport/LinuxBrowserSecret.test.ts+2-2| ... | @@ -38,7 +38,7 @@ it.layer(NodeServices.layer)("Linux browser secret path", (it) => { | ... | @@ -38,7 +38,7 @@ it.layer(NodeServices.layer)("Linux browser secret path", (it) => { |
| 38 | yield* fileSystem.writeFileString(filename, "helper"); | 38 | yield* fileSystem.writeFileString(filename, "helper"); |
| 39 | } | 39 | } |
| 40 | const resolve = (isPackaged: boolean, platform: NodeJS.Platform = "linux") => { | 40 | const resolve = (isPackaged: boolean, platform: NodeJS.Platform = "linux") => { |
| 41 | const environment = DesktopEnvironment.layer({ | 41 | const layerEnvironment = DesktopEnvironment.layer({ |
| 42 | dirname: path.join(root, "apps", "desktop", "dist-electron"), | 42 | dirname: path.join(root, "apps", "desktop", "dist-electron"), |
| 43 | homeDirectory: root, | 43 | homeDirectory: root, |
| 44 | platform, | 44 | platform, |
| ... | @@ -50,7 +50,7 @@ it.layer(NodeServices.layer)("Linux browser secret path", (it) => { | ... | @@ -50,7 +50,7 @@ it.layer(NodeServices.layer)("Linux browser secret path", (it) => { |
| 50 | runningUnderArm64Translation: false, | 50 | runningUnderArm64Translation: false, |
| 51 | }).pipe(Layer.provide(DesktopConfig.layerTest({}))); | 51 | }).pipe(Layer.provide(DesktopConfig.layerTest({}))); |
| 52 | return LinuxBrowserSecret.LinuxBrowserSecretPath.pipe( | 52 | return LinuxBrowserSecret.LinuxBrowserSecretPath.pipe( |
| 53 | Effect.provide(LinuxBrowserSecret.layer.pipe(Layer.provide(environment))), | 53 | Effect.provide(LinuxBrowserSecret.layer.pipe(Layer.provide(layerEnvironment))), |
| 54 | ); | 54 | ); |
| 55 | }; | 55 | }; |
| 56 | 56 |
apps/desktop/src/preview/BrowserSession.test.ts+2-2| ... | @@ -197,7 +197,7 @@ describe("BrowserSession", () => { | ... | @@ -197,7 +197,7 @@ describe("BrowserSession", () => { |
| 197 | method: "digest", | 197 | method: "digest", |
| 198 | cause: nativeCause, | 198 | cause: nativeCause, |
| 199 | }); | 199 | }); |
| 200 | const failingCryptoLayer = Layer.succeed( | 200 | const layerFailingCrypto = Layer.succeed( |
| 201 | Crypto.Crypto, | 201 | Crypto.Crypto, |
| 202 | Crypto.make({ | 202 | Crypto.make({ |
| 203 | randomBytes: (size) => new Uint8Array(size), | 203 | randomBytes: (size) => new Uint8Array(size), |
| ... | @@ -218,7 +218,7 @@ describe("BrowserSession", () => { | ... | @@ -218,7 +218,7 @@ describe("BrowserSession", () => { |
| 218 | "Failed to derive a desktop preview browser partition for scope environment-a.", | 218 | "Failed to derive a desktop preview browser partition for scope environment-a.", |
| 219 | ); | 219 | ); |
| 220 | assert.notInclude(error.message, nativeCause.message); | 220 | assert.notInclude(error.message, nativeCause.message); |
| 221 | }).pipe(Effect.provide(BrowserSession.layer.pipe(Layer.provide(failingCryptoLayer)))); | 221 | }).pipe(Effect.provide(BrowserSession.layer.pipe(Layer.provide(layerFailingCrypto)))); |
| 222 | }); | 222 | }); |
| 223 | 223 | ||
| 224 | it.effect("preserves session scope, partition, and the Electron failure", () => | 224 | it.effect("preserves session scope, partition, and the Electron failure", () => |
apps/desktop/src/preview/Manager.test.ts+6-6| ... | @@ -234,7 +234,7 @@ vi.mock("electron", () => ({ | ... | @@ -234,7 +234,7 @@ vi.mock("electron", () => ({ |
| 234 | }, | 234 | }, |
| 235 | })); | 235 | })); |
| 236 | 236 | ||
| 237 | const browserSessionLayer = Layer.succeed( | 237 | const layerBrowserSession = Layer.succeed( |
| 238 | BrowserSession.BrowserSession, | 238 | BrowserSession.BrowserSession, |
| 239 | BrowserSession.BrowserSession.of({ | 239 | BrowserSession.BrowserSession.of({ |
| 240 | getPartition: () => Effect.succeed("persist:t3code-preview-test"), | 240 | getPartition: () => Effect.succeed("persist:t3code-preview-test"), |
| ... | @@ -245,7 +245,7 @@ const browserSessionLayer = Layer.succeed( | ... | @@ -245,7 +245,7 @@ const browserSessionLayer = Layer.succeed( |
| 245 | }), | 245 | }), |
| 246 | ); | 246 | ); |
| 247 | 247 | ||
| 248 | const environmentLayer = Layer.succeed( | 248 | const layerEnvironment = Layer.succeed( |
| 249 | DesktopEnvironment.DesktopEnvironment, | 249 | DesktopEnvironment.DesktopEnvironment, |
| 250 | DesktopEnvironment.DesktopEnvironment.of({ | 250 | DesktopEnvironment.DesktopEnvironment.of({ |
| 251 | browserArtifactsDir: "/tmp/t3/dev/browser-artifacts", | 251 | browserArtifactsDir: "/tmp/t3/dev/browser-artifacts", |
| ... | @@ -256,7 +256,7 @@ const environmentLayer = Layer.succeed( | ... | @@ -256,7 +256,7 @@ const environmentLayer = Layer.succeed( |
| 256 | } as DesktopEnvironment.DesktopEnvironment["Service"]), | 256 | } as DesktopEnvironment.DesktopEnvironment["Service"]), |
| 257 | ); | 257 | ); |
| 258 | 258 | ||
| 259 | const fileSystemLayer = FileSystem.layerNoop({ | 259 | const layerFileSystem = FileSystem.layerNoop({ |
| 260 | makeDirectory: (path) => | 260 | makeDirectory: (path) => |
| 261 | Effect.sync(() => { | 261 | Effect.sync(() => { |
| 262 | mkdir(path); | 262 | mkdir(path); |
| ... | @@ -275,9 +275,9 @@ const layer = PreviewManager.layer.pipe( | ... | @@ -275,9 +275,9 @@ const layer = PreviewManager.layer.pipe( |
| 275 | shutdown: Effect.void, | 275 | shutdown: Effect.void, |
| 276 | }), | 276 | }), |
| 277 | ), | 277 | ), |
| 278 | Layer.provideMerge(browserSessionLayer), | 278 | Layer.provideMerge(layerBrowserSession), |
| 279 | Layer.provideMerge(environmentLayer), | 279 | Layer.provideMerge(layerEnvironment), |
| 280 | Layer.provideMerge(fileSystemLayer), | 280 | Layer.provideMerge(layerFileSystem), |
| 281 | Layer.provideMerge(Path.layer), | 281 | Layer.provideMerge(Path.layer), |
| 282 | Layer.provideMerge(Layer.succeed(HostProcessPlatform, "darwin")), | 282 | Layer.provideMerge(Layer.succeed(HostProcessPlatform, "darwin")), |
| 283 | ); | 283 | ); |
apps/desktop/src/settings/DesktopAppSettings.test.ts+2-2| ... | @@ -39,7 +39,7 @@ const DesktopSettingsPatch = Schema.Struct({ | ... | @@ -39,7 +39,7 @@ const DesktopSettingsPatch = Schema.Struct({ |
| 39 | const decodeDesktopSettingsPatch = Schema.decodeEffect(Schema.fromJsonString(DesktopSettingsPatch)); | 39 | const decodeDesktopSettingsPatch = Schema.decodeEffect(Schema.fromJsonString(DesktopSettingsPatch)); |
| 40 | const encodeDesktopSettingsPatch = Schema.encodeEffect(Schema.fromJsonString(DesktopSettingsPatch)); | 40 | const encodeDesktopSettingsPatch = Schema.encodeEffect(Schema.fromJsonString(DesktopSettingsPatch)); |
| 41 | 41 | ||
| 42 | function makeEnvironmentLayer(baseDir: string, appVersion = "0.0.17") { | 42 | function layerEnvironment(baseDir: string, appVersion = "0.0.17") { |
| 43 | return DesktopEnvironment.layer({ | 43 | return DesktopEnvironment.layer({ |
| 44 | dirname: "/repo/apps/desktop/src", | 44 | dirname: "/repo/apps/desktop/src", |
| 45 | homeDirectory: baseDir, | 45 | homeDirectory: baseDir, |
| ... | @@ -73,7 +73,7 @@ const withSettings = <A, E, R>( | ... | @@ -73,7 +73,7 @@ const withSettings = <A, E, R>( |
| 73 | return yield* effect.pipe( | 73 | return yield* effect.pipe( |
| 74 | Effect.provide( | 74 | Effect.provide( |
| 75 | DesktopAppSettings.layer.pipe( | 75 | DesktopAppSettings.layer.pipe( |
| 76 | Layer.provideMerge(makeEnvironmentLayer(baseDir, options?.appVersion)), | 76 | Layer.provideMerge(layerEnvironment(baseDir, options?.appVersion)), |
| 77 | Layer.provideMerge(NodeServices.layer), | 77 | Layer.provideMerge(NodeServices.layer), |
| 78 | ), | 78 | ), |
| 79 | ), | 79 | ), |
apps/desktop/src/settings/DesktopClientSettings.diagnostics.test.ts+4-7| ... | @@ -19,8 +19,8 @@ interface LogRecord { | ... | @@ -19,8 +19,8 @@ interface LogRecord { |
| 19 | 19 | ||
| 20 | const baseDir = "/virtual-home"; | 20 | const baseDir = "/virtual-home"; |
| 21 | 21 | ||
| 22 | function makeLayer(fileSystemLayer: Layer.Layer<FileSystem.FileSystem>) { | 22 | function layer(fileSystemLayer: Layer.Layer<FileSystem.FileSystem>) { |
| 23 | const environmentLayer = DesktopEnvironment.layer({ | 23 | const layerEnvironment = DesktopEnvironment.layer({ |
| 24 | dirname: "/repo/apps/desktop/src", | 24 | dirname: "/repo/apps/desktop/src", |
| 25 | homeDirectory: baseDir, | 25 | homeDirectory: baseDir, |
| 26 | platform: "darwin", | 26 | platform: "darwin", |
| ... | @@ -37,7 +37,7 @@ function makeLayer(fileSystemLayer: Layer.Layer<FileSystem.FileSystem>) { | ... | @@ -37,7 +37,7 @@ function makeLayer(fileSystemLayer: Layer.Layer<FileSystem.FileSystem>) { |
| 37 | ); | 37 | ); |
| 38 | 38 | ||
| 39 | return DesktopClientSettings.layer.pipe( | 39 | return DesktopClientSettings.layer.pipe( |
| 40 | Layer.provideMerge(Layer.mergeAll(environmentLayer, NodeServices.layer, fileSystemLayer)), | 40 | Layer.provideMerge(Layer.mergeAll(layerEnvironment, NodeServices.layer, fileSystemLayer)), |
| 41 | ); | 41 | ); |
| 42 | } | 42 | } |
| 43 | 43 | ||
| ... | @@ -60,10 +60,7 @@ const readWithLogs = (fileSystemLayer: Layer.Layer<FileSystem.FileSystem>) => { | ... | @@ -60,10 +60,7 @@ const readWithLogs = (fileSystemLayer: Layer.Layer<FileSystem.FileSystem>) => { |
| 60 | }; | 60 | }; |
| 61 | }).pipe( | 61 | }).pipe( |
| 62 | Effect.provide( | 62 | Effect.provide( |
| 63 | Layer.mergeAll( | 63 | Layer.mergeAll(layer(fileSystemLayer), Logger.layer([logger], { mergeWithExisting: false })), |
| 64 | makeLayer(fileSystemLayer), | ||
| 65 | Logger.layer([logger], { mergeWithExisting: false }), | ||
| 66 | ), | ||
| 67 | ), | 64 | ), |
| 68 | ); | 65 | ); |
| 69 | }; | 66 | }; |
apps/desktop/src/settings/DesktopClientSettings.test.ts+4-4| ... | @@ -78,8 +78,8 @@ const decodeClientSettingsJson = Schema.decodeEffect(Schema.fromJsonString(Clien | ... | @@ -78,8 +78,8 @@ const decodeClientSettingsJson = Schema.decodeEffect(Schema.fromJsonString(Clien |
| 78 | const decodeRecordJson = Schema.decodeEffect( | 78 | const decodeRecordJson = Schema.decodeEffect( |
| 79 | Schema.fromJsonString(Schema.Record(Schema.String, Schema.Unknown)), | 79 | Schema.fromJsonString(Schema.Record(Schema.String, Schema.Unknown)), |
| 80 | ); | 80 | ); |
| 81 | function makeLayer(baseDir: string) { | 81 | function layer(baseDir: string) { |
| 82 | const environmentLayer = DesktopEnvironment.layer({ | 82 | const layerEnvironment = DesktopEnvironment.layer({ |
| 83 | dirname: "/repo/apps/desktop/src", | 83 | dirname: "/repo/apps/desktop/src", |
| 84 | homeDirectory: baseDir, | 84 | homeDirectory: baseDir, |
| 85 | platform: "darwin", | 85 | platform: "darwin", |
| ... | @@ -96,7 +96,7 @@ function makeLayer(baseDir: string) { | ... | @@ -96,7 +96,7 @@ function makeLayer(baseDir: string) { |
| 96 | ); | 96 | ); |
| 97 | 97 | ||
| 98 | return DesktopClientSettings.layer.pipe( | 98 | return DesktopClientSettings.layer.pipe( |
| 99 | Layer.provideMerge(environmentLayer), | 99 | Layer.provideMerge(layerEnvironment), |
| 100 | Layer.provideMerge(NodeServices.layer), | 100 | Layer.provideMerge(NodeServices.layer), |
| 101 | ); | 101 | ); |
| 102 | } | 102 | } |
| ... | @@ -109,7 +109,7 @@ const withClientSettings = <A, E, R>( | ... | @@ -109,7 +109,7 @@ const withClientSettings = <A, E, R>( |
| 109 | const baseDir = yield* fileSystem.makeTempDirectoryScoped({ | 109 | const baseDir = yield* fileSystem.makeTempDirectoryScoped({ |
| 110 | prefix: "t3-desktop-client-settings-test-", | 110 | prefix: "t3-desktop-client-settings-test-", |
| 111 | }); | 111 | }); |
| 112 | return yield* effect.pipe(Effect.provide(makeLayer(baseDir))); | 112 | return yield* effect.pipe(Effect.provide(layer(baseDir))); |
| 113 | }).pipe(Effect.provide(NodeServices.layer), Effect.scoped); | 113 | }).pipe(Effect.provide(NodeServices.layer), Effect.scoped); |
| 114 | 114 | ||
| 115 | describe("DesktopClientSettings", () => { | 115 | describe("DesktopClientSettings", () => { |
apps/desktop/src/settings/DesktopSavedEnvironments.test.ts+11-11| ... | @@ -60,7 +60,7 @@ const seedSavedEnvironmentRegistry = Effect.fn(function* (encryptedBearerToken?: | ... | @@ -60,7 +60,7 @@ const seedSavedEnvironmentRegistry = Effect.fn(function* (encryptedBearerToken?: |
| 60 | yield* fileSystem.writeFileString(environment.savedEnvironmentRegistryPath, `${encoded}\n`); | 60 | yield* fileSystem.writeFileString(environment.savedEnvironmentRegistryPath, `${encoded}\n`); |
| 61 | }); | 61 | }); |
| 62 | 62 | ||
| 63 | function makeSafeStorageLayer(input: { | 63 | function layerSafeStorageFor(input: { |
| 64 | readonly available: boolean; | 64 | readonly available: boolean; |
| 65 | readonly availabilityError?: unknown; | 65 | readonly availabilityError?: unknown; |
| 66 | readonly decryptError?: unknown; | 66 | readonly decryptError?: unknown; |
| ... | @@ -98,7 +98,7 @@ function makeSafeStorageLayer(input: { | ... | @@ -98,7 +98,7 @@ function makeSafeStorageLayer(input: { |
| 98 | } satisfies ElectronSafeStorage.ElectronSafeStorage["Service"]); | 98 | } satisfies ElectronSafeStorage.ElectronSafeStorage["Service"]); |
| 99 | } | 99 | } |
| 100 | 100 | ||
| 101 | function makeLayer( | 101 | function layer( |
| 102 | baseDir: string, | 102 | baseDir: string, |
| 103 | options?: { | 103 | options?: { |
| 104 | readonly availableSecretStorage?: boolean; | 104 | readonly availableSecretStorage?: boolean; |
| ... | @@ -107,7 +107,7 @@ function makeLayer( | ... | @@ -107,7 +107,7 @@ function makeLayer( |
| 107 | }, | 107 | }, |
| 108 | fileSystemLayer: Layer.Layer<FileSystem.FileSystem> = NodeServices.layer, | 108 | fileSystemLayer: Layer.Layer<FileSystem.FileSystem> = NodeServices.layer, |
| 109 | ) { | 109 | ) { |
| 110 | const environmentLayer = DesktopEnvironment.layer({ | 110 | const layerEnvironment = DesktopEnvironment.layer({ |
| 111 | dirname: "/repo/apps/desktop/src", | 111 | dirname: "/repo/apps/desktop/src", |
| 112 | homeDirectory: baseDir, | 112 | homeDirectory: baseDir, |
| 113 | platform: "darwin", | 113 | platform: "darwin", |
| ... | @@ -123,19 +123,19 @@ function makeLayer( | ... | @@ -123,19 +123,19 @@ function makeLayer( |
| 123 | ), | 123 | ), |
| 124 | ); | 124 | ); |
| 125 | 125 | ||
| 126 | const safeStorageLayer = makeSafeStorageLayer({ | 126 | const layerSafeStorage = layerSafeStorageFor({ |
| 127 | available: options?.availableSecretStorage ?? true, | 127 | available: options?.availableSecretStorage ?? true, |
| 128 | availabilityError: options?.availabilityError, | 128 | availabilityError: options?.availabilityError, |
| 129 | decryptError: options?.decryptError, | 129 | decryptError: options?.decryptError, |
| 130 | }); | 130 | }); |
| 131 | const dependencies = Layer.mergeAll( | 131 | const layerDependencies = Layer.mergeAll( |
| 132 | environmentLayer, | 132 | layerEnvironment, |
| 133 | safeStorageLayer, | 133 | layerSafeStorage, |
| 134 | NodeServices.layer, | 134 | NodeServices.layer, |
| 135 | fileSystemLayer, | 135 | fileSystemLayer, |
| 136 | ); | 136 | ); |
| 137 | 137 | ||
| 138 | return DesktopSavedEnvironments.layer.pipe(Layer.provideMerge(dependencies)); | 138 | return DesktopSavedEnvironments.layer.pipe(Layer.provideMerge(layerDependencies)); |
| 139 | } | 139 | } |
| 140 | 140 | ||
| 141 | const withSavedEnvironments = <A, E, R>( | 141 | const withSavedEnvironments = <A, E, R>( |
| ... | @@ -151,7 +151,7 @@ const withSavedEnvironments = <A, E, R>( | ... | @@ -151,7 +151,7 @@ const withSavedEnvironments = <A, E, R>( |
| 151 | const baseDir = yield* fileSystem.makeTempDirectoryScoped({ | 151 | const baseDir = yield* fileSystem.makeTempDirectoryScoped({ |
| 152 | prefix: "t3-desktop-saved-environments-test-", | 152 | prefix: "t3-desktop-saved-environments-test-", |
| 153 | }); | 153 | }); |
| 154 | return yield* effect.pipe(Effect.provide(makeLayer(baseDir, options))); | 154 | return yield* effect.pipe(Effect.provide(layer(baseDir, options))); |
| 155 | }).pipe(Effect.provide(NodeServices.layer), Effect.scoped); | 155 | }).pipe(Effect.provide(NodeServices.layer), Effect.scoped); |
| 156 | 156 | ||
| 157 | describe("DesktopSavedEnvironments", () => { | 157 | describe("DesktopSavedEnvironments", () => { |
| ... | @@ -346,14 +346,14 @@ describe("DesktopSavedEnvironments", () => { | ... | @@ -346,14 +346,14 @@ describe("DesktopSavedEnvironments", () => { |
| 346 | method: "readFileString", | 346 | method: "readFileString", |
| 347 | pathOrDescriptor: registryPath, | 347 | pathOrDescriptor: registryPath, |
| 348 | }); | 348 | }); |
| 349 | const fileSystemLayer = Layer.succeed( | 349 | const layerFileSystem = Layer.succeed( |
| 350 | FileSystem.FileSystem, | 350 | FileSystem.FileSystem, |
| 351 | FileSystem.makeNoop({ | 351 | FileSystem.makeNoop({ |
| 352 | readFileString: () => Effect.fail(permissionError), | 352 | readFileString: () => Effect.fail(permissionError), |
| 353 | }), | 353 | }), |
| 354 | ); | 354 | ); |
| 355 | const savedEnvironments = yield* DesktopSavedEnvironments.DesktopSavedEnvironments.pipe( | 355 | const savedEnvironments = yield* DesktopSavedEnvironments.DesktopSavedEnvironments.pipe( |
| 356 | Effect.provide(makeLayer(baseDir, undefined, fileSystemLayer)), | 356 | Effect.provide(layer(baseDir, undefined, layerFileSystem)), |
| 357 | ); | 357 | ); |
| 358 | 358 | ||
| 359 | const error = yield* savedEnvironments.getRegistry.pipe(Effect.flip); | 359 | const error = yield* savedEnvironments.getRegistry.pipe(Effect.flip); |
apps/desktop/src/shell/DesktopShellEnvironment.test.ts+3-3| ... | @@ -70,13 +70,13 @@ function runShellEnvironment(input: { | ... | @@ -70,13 +70,13 @@ function runShellEnvironment(input: { |
| 70 | readonly handler: (command: ChildProcess.Command) => string; | 70 | readonly handler: (command: ChildProcess.Command) => string; |
| 71 | readonly failure?: PlatformError.PlatformError; | 71 | readonly failure?: PlatformError.PlatformError; |
| 72 | }) { | 72 | }) { |
| 73 | const environmentLayer = Layer.succeed( | 73 | const layerEnvironment = Layer.succeed( |
| 74 | DesktopEnvironment.DesktopEnvironment, | 74 | DesktopEnvironment.DesktopEnvironment, |
| 75 | DesktopEnvironment.DesktopEnvironment.of({ | 75 | DesktopEnvironment.DesktopEnvironment.of({ |
| 76 | platform: input.platform, | 76 | platform: input.platform, |
| 77 | } as DesktopEnvironment.DesktopEnvironment["Service"]), | 77 | } as DesktopEnvironment.DesktopEnvironment["Service"]), |
| 78 | ); | 78 | ); |
| 79 | const spawnerLayer = Layer.succeed( | 79 | const layerSpawner = Layer.succeed( |
| 80 | ChildProcessSpawner.ChildProcessSpawner, | 80 | ChildProcessSpawner.ChildProcessSpawner, |
| 81 | ChildProcessSpawner.make((command) => | 81 | ChildProcessSpawner.make((command) => |
| 82 | input.failure === undefined | 82 | input.failure === undefined |
| ... | @@ -91,7 +91,7 @@ function runShellEnvironment(input: { | ... | @@ -91,7 +91,7 @@ function runShellEnvironment(input: { |
| 91 | }).pipe( | 91 | }).pipe( |
| 92 | Effect.provide( | 92 | Effect.provide( |
| 93 | DesktopShellEnvironment.layer.pipe( | 93 | DesktopShellEnvironment.layer.pipe( |
| 94 | Layer.provide(Layer.mergeAll(environmentLayer, NodeServices.layer, spawnerLayer)), | 94 | Layer.provide(Layer.mergeAll(layerEnvironment, NodeServices.layer, layerSpawner)), |
| 95 | ), | 95 | ), |
| 96 | ), | 96 | ), |
| 97 | ); | 97 | ); |
apps/desktop/src/snapShot/DesktopSnapShot.test.ts+49-49| ... | @@ -484,7 +484,7 @@ import { | ... | @@ -484,7 +484,7 @@ import { |
| 484 | snapShotAnimationDisplayBounds, | 484 | snapShotAnimationDisplayBounds, |
| 485 | snapShotAnimationOverlayBounds, | 485 | snapShotAnimationOverlayBounds, |
| 486 | } from "./SnapShotTransition.ts"; | 486 | } from "./SnapShotTransition.ts"; |
| 487 | const testLayer = ( | 487 | const layerTest = ( |
| 488 | platform: NodeJS.Platform, | 488 | platform: NodeJS.Platform, |
| 489 | fileSystemOverrides: Parameters<typeof FileSystem.layerNoop>[0] = {}, | 489 | fileSystemOverrides: Parameters<typeof FileSystem.layerNoop>[0] = {}, |
| 490 | initialSettings: Option.Option<ClientSettings> = Option.none(), | 490 | initialSettings: Option.Option<ClientSettings> = Option.none(), |
| ... | @@ -639,7 +639,7 @@ function concurrentCaptureFixture(platform: NodeJS.Platform, animations: boolean | ... | @@ -639,7 +639,7 @@ function concurrentCaptureFixture(platform: NodeJS.Platform, animations: boolean |
| 639 | const handoff = Effect.sync(() => captures[state.handoffs++]!.handoff.resolve()); | 639 | const handoff = Effect.sync(() => captures[state.handoffs++]!.handoff.resolve()); |
| 640 | let randomByte = 0; | 640 | let randomByte = 0; |
| 641 | const layer = Layer.mergeAll( | 641 | const layer = Layer.mergeAll( |
| 642 | testLayer(platform, { | 642 | layerTest(platform, { |
| 643 | makeDirectory: () => Effect.void, | 643 | makeDirectory: () => Effect.void, |
| 644 | writeFile: (path, bytes) => | 644 | writeFile: (path, bytes) => |
| 645 | Effect.sync(() => { | 645 | Effect.sync(() => { |
| ... | @@ -776,7 +776,7 @@ it.effect("rejects desktop config changes outside Niri or Hyprland", () => | ... | @@ -776,7 +776,7 @@ it.effect("rejects desktop config changes outside Niri or Hyprland", () => |
| 776 | assert.equal(error.message, "Config setup requires a Niri or Hyprland session."); | 776 | assert.equal(error.message, "Config setup requires a Niri or Hyprland session."); |
| 777 | } | 777 | } |
| 778 | }), | 778 | }), |
| 779 | ).pipe(Effect.provide(testLayer("win32"))), | 779 | ).pipe(Effect.provide(layerTest("win32"))), |
| 780 | ); | 780 | ); |
| 781 | 781 | ||
| 782 | it.effect("reads and acknowledges queued captures through Effect services", () => { | 782 | it.effect("reads and acknowledges queued captures through Effect services", () => { |
| ... | @@ -797,7 +797,7 @@ it.effect("reads and acknowledges queued captures through Effect services", () = | ... | @@ -797,7 +797,7 @@ it.effect("reads and acknowledges queued captures through Effect services", () = |
| 797 | windowTitle: "main.ts", | 797 | windowTitle: "main.ts", |
| 798 | }, | 798 | }, |
| 799 | }); | 799 | }); |
| 800 | const layer = testLayer("linux", { | 800 | const layer = layerTest("linux", { |
| 801 | readDirectory: () => Effect.succeed([captureId + ".json", "invalid.json"]), | 801 | readDirectory: () => Effect.succeed([captureId + ".json", "invalid.json"]), |
| 802 | readFileString: (filePath) => Effect.succeed(filePath === metadataPath ? metadata : "invalid"), | 802 | readFileString: (filePath) => Effect.succeed(filePath === metadataPath ? metadata : "invalid"), |
| 803 | readFile: () => Effect.succeed(new Uint8Array([1, 2, 3])), | 803 | readFile: () => Effect.succeed(new Uint8Array([1, 2, 3])), |
| ... | @@ -841,7 +841,7 @@ it.effect("captures the active Windows window without enumerating desktop source | ... | @@ -841,7 +841,7 @@ it.effect("captures the active Windows window without enumerating desktop source |
| 841 | getFileIconMock.mockReset().mockResolvedValue(fakeIcon("file")); | 841 | getFileIconMock.mockReset().mockResolvedValue(fakeIcon("file")); |
| 842 | const writtenFiles: Array<[string, Uint8Array]> = []; | 842 | const writtenFiles: Array<[string, Uint8Array]> = []; |
| 843 | let metadata = ""; | 843 | let metadata = ""; |
| 844 | const layer = testLayer("win32", { | 844 | const layer = layerTest("win32", { |
| 845 | makeDirectory: () => Effect.void, | 845 | makeDirectory: () => Effect.void, |
| 846 | rename: () => Effect.void, | 846 | rename: () => Effect.void, |
| 847 | writeFile: (path, bytes) => | 847 | writeFile: (path, bytes) => |
| ... | @@ -897,7 +897,7 @@ it.effect.each([ | ... | @@ -897,7 +897,7 @@ it.effect.each([ |
| 897 | result: Promise.resolve(undefined), | 897 | result: Promise.resolve(undefined), |
| 898 | }); | 898 | }); |
| 899 | let metadata = ""; | 899 | let metadata = ""; |
| 900 | const layer = testLayer("darwin", { | 900 | const layer = layerTest("darwin", { |
| 901 | makeDirectory: () => Effect.void, | 901 | makeDirectory: () => Effect.void, |
| 902 | rename: () => Effect.void, | 902 | rename: () => Effect.void, |
| 903 | writeFileString: (_, text) => | 903 | writeFileString: (_, text) => |
| ... | @@ -1027,7 +1027,7 @@ it.effect.each(["win32", "darwin", "linux"] as const)( | ... | @@ -1027,7 +1027,7 @@ it.effect.each(["win32", "darwin", "linux"] as const)( |
| 1027 | }), | 1027 | }), |
| 1028 | ).pipe( | 1028 | ).pipe( |
| 1029 | Effect.provide( | 1029 | Effect.provide( |
| 1030 | testLayer(platform, { | 1030 | layerTest(platform, { |
| 1031 | makeDirectory: () => Effect.void, | 1031 | makeDirectory: () => Effect.void, |
| 1032 | rename: () => Effect.void, | 1032 | rename: () => Effect.void, |
| 1033 | writeFile: (_, bytes) => | 1033 | writeFile: (_, bytes) => |
| ... | @@ -1288,7 +1288,7 @@ it.effect("matches Windows accessibility windows on a scaled display", () => { | ... | @@ -1288,7 +1288,7 @@ it.effect("matches Windows accessibility windows on a scaled display", () => { |
| 1288 | }), | 1288 | }), |
| 1289 | ).pipe( | 1289 | ).pipe( |
| 1290 | Effect.provide( | 1290 | Effect.provide( |
| 1291 | testLayer("win32", { | 1291 | layerTest("win32", { |
| 1292 | makeDirectory: () => Effect.void, | 1292 | makeDirectory: () => Effect.void, |
| 1293 | rename: () => Effect.void, | 1293 | rename: () => Effect.void, |
| 1294 | writeFile: () => Effect.void, | 1294 | writeFile: () => Effect.void, |
| ... | @@ -1320,7 +1320,7 @@ it.effect("skips accessibility capture when the setting is disabled", () => { | ... | @@ -1320,7 +1320,7 @@ it.effect("skips accessibility capture when the setting is disabled", () => { |
| 1320 | accessibilityProcessWarmMock.mockClear(); | 1320 | accessibilityProcessWarmMock.mockClear(); |
| 1321 | accessibilityProcessReadMock.mockClear(); | 1321 | accessibilityProcessReadMock.mockClear(); |
| 1322 | accessibilityByPidMock.mockClear(); | 1322 | accessibilityByPidMock.mockClear(); |
| 1323 | const layer = testLayer("win32", { | 1323 | const layer = layerTest("win32", { |
| 1324 | makeDirectory: () => Effect.void, | 1324 | makeDirectory: () => Effect.void, |
| 1325 | rename: () => Effect.void, | 1325 | rename: () => Effect.void, |
| 1326 | writeFile: () => Effect.void, | 1326 | writeFile: () => Effect.void, |
| ... | @@ -1364,7 +1364,7 @@ it.effect("keeps an accessibility helper warm only while capture data is enabled | ... | @@ -1364,7 +1364,7 @@ it.effect("keeps an accessibility helper warm only while capture data is enabled |
| 1364 | 1364 | ||
| 1365 | assert.lengthOf(accessibilityProcessCoolMock.mock.calls, 1); | 1365 | assert.lengthOf(accessibilityProcessCoolMock.mock.calls, 1); |
| 1366 | }), | 1366 | }), |
| 1367 | ).pipe(Effect.provide(testLayer("win32"))); | 1367 | ).pipe(Effect.provide(layerTest("win32"))); |
| 1368 | }); | 1368 | }); |
| 1369 | 1369 | ||
| 1370 | it.effect("rejects X11 capture without registering shortcuts or loading capture backends", () => { | 1370 | it.effect("rejects X11 capture without registering shortcuts or loading capture backends", () => { |
| ... | @@ -1389,7 +1389,7 @@ it.effect("rejects X11 capture without registering shortcuts or loading capture | ... | @@ -1389,7 +1389,7 @@ it.effect("rejects X11 capture without registering shortcuts or loading capture |
| 1389 | assert.lengthOf(activeWindowMock.mock.calls, 0); | 1389 | assert.lengthOf(activeWindowMock.mock.calls, 0); |
| 1390 | }), | 1390 | }), |
| 1391 | ).pipe( | 1391 | ).pipe( |
| 1392 | Effect.provide(testLayer("linux")), | 1392 | Effect.provide(layerTest("linux")), |
| 1393 | Effect.ensuring(Effect.sync(() => vi.unstubAllEnvs())), | 1393 | Effect.ensuring(Effect.sync(() => vi.unstubAllEnvs())), |
| 1394 | ); | 1394 | ); |
| 1395 | }); | 1395 | }); |
| ... | @@ -1476,7 +1476,7 @@ it.effect.each(["gnome", "niri", "kde", "hyprland"] as const)( | ... | @@ -1476,7 +1476,7 @@ it.effect.each(["gnome", "niri", "kde", "hyprland"] as const)( |
| 1476 | }), | 1476 | }), |
| 1477 | ).pipe( | 1477 | ).pipe( |
| 1478 | Effect.provide( | 1478 | Effect.provide( |
| 1479 | testLayer("linux", { | 1479 | layerTest("linux", { |
| 1480 | makeDirectory: () => Effect.void, | 1480 | makeDirectory: () => Effect.void, |
| 1481 | rename: () => Effect.void, | 1481 | rename: () => Effect.void, |
| 1482 | writeFile: () => Effect.void, | 1482 | writeFile: () => Effect.void, |
| ... | @@ -1543,7 +1543,7 @@ it.effect("persists a window capture from a portal activation without a setup te | ... | @@ -1543,7 +1543,7 @@ it.effect("persists a window capture from a portal activation without a setup te |
| 1543 | }), | 1543 | }), |
| 1544 | ).pipe( | 1544 | ).pipe( |
| 1545 | Effect.provide( | 1545 | Effect.provide( |
| 1546 | testLayer("linux", { | 1546 | layerTest("linux", { |
| 1547 | makeDirectory: () => Effect.void, | 1547 | makeDirectory: () => Effect.void, |
| 1548 | rename: () => Effect.void, | 1548 | rename: () => Effect.void, |
| 1549 | writeFile: (_, bytes) => | 1549 | writeFile: (_, bytes) => |
| ... | @@ -1574,7 +1574,7 @@ it.effect("does not fall back to the picker when an automatic Wayland capture fa | ... | @@ -1574,7 +1574,7 @@ it.effect("does not fall back to the picker when an automatic Wayland capture fa |
| 1574 | }), | 1574 | }), |
| 1575 | ).pipe( | 1575 | ).pipe( |
| 1576 | Effect.provide( | 1576 | Effect.provide( |
| 1577 | testLayer("linux", { | 1577 | layerTest("linux", { |
| 1578 | makeDirectory: () => Effect.void, | 1578 | makeDirectory: () => Effect.void, |
| 1579 | remove: () => Effect.void, | 1579 | remove: () => Effect.void, |
| 1580 | }), | 1580 | }), |
| ... | @@ -1649,7 +1649,7 @@ it.effect( | ... | @@ -1649,7 +1649,7 @@ it.effect( |
| 1649 | ).pipe( | 1649 | ).pipe( |
| 1650 | Effect.provide( | 1650 | Effect.provide( |
| 1651 | Layer.mergeAll( | 1651 | Layer.mergeAll( |
| 1652 | testLayer("linux", { | 1652 | layerTest("linux", { |
| 1653 | makeDirectory: () => Effect.void, | 1653 | makeDirectory: () => Effect.void, |
| 1654 | rename: () => Effect.void, | 1654 | rename: () => Effect.void, |
| 1655 | writeFile: () => Effect.void, | 1655 | writeFile: () => Effect.void, |
| ... | @@ -1686,7 +1686,7 @@ it.effect("does not read unverified accessibility context for a Wayland portal c | ... | @@ -1686,7 +1686,7 @@ it.effect("does not read unverified accessibility context for a Wayland portal c |
| 1686 | thumbnail: { isEmpty: () => false, toPNG: () => png }, | 1686 | thumbnail: { isEmpty: () => false, toPNG: () => png }, |
| 1687 | }, | 1687 | }, |
| 1688 | ]); | 1688 | ]); |
| 1689 | const layer = testLayer("linux", { | 1689 | const layer = layerTest("linux", { |
| 1690 | makeDirectory: () => Effect.void, | 1690 | makeDirectory: () => Effect.void, |
| 1691 | rename: () => Effect.void, | 1691 | rename: () => Effect.void, |
| 1692 | writeFile: () => Effect.void, | 1692 | writeFile: () => Effect.void, |
| ... | @@ -1728,7 +1728,7 @@ it.effect("uses display-local macOS capture surfaces across the source and main | ... | @@ -1728,7 +1728,7 @@ it.effect("uses display-local macOS capture surfaces across the source and main |
| 1728 | isDestroyed: () => false, | 1728 | isDestroyed: () => false, |
| 1729 | }); | 1729 | }); |
| 1730 | flashWindows.length = 0; | 1730 | flashWindows.length = 0; |
| 1731 | const layer = testLayer("darwin", { | 1731 | const layer = layerTest("darwin", { |
| 1732 | makeDirectory: () => Effect.void, | 1732 | makeDirectory: () => Effect.void, |
| 1733 | rename: () => Effect.void, | 1733 | rename: () => Effect.void, |
| 1734 | writeFileString: () => Effect.void, | 1734 | writeFileString: () => Effect.void, |
| ... | @@ -1807,7 +1807,7 @@ it.effect.each(["ready", "failed"] as const)( | ... | @@ -1807,7 +1807,7 @@ it.effect.each(["ready", "failed"] as const)( |
| 1807 | }), | 1807 | }), |
| 1808 | ).pipe( | 1808 | ).pipe( |
| 1809 | Effect.provide( | 1809 | Effect.provide( |
| 1810 | testLayer("win32", { | 1810 | layerTest("win32", { |
| 1811 | makeDirectory: () => Effect.void, | 1811 | makeDirectory: () => Effect.void, |
| 1812 | rename: () => Effect.void, | 1812 | rename: () => Effect.void, |
| 1813 | writeFile: () => Effect.void, | 1813 | writeFile: () => Effect.void, |
| ... | @@ -1848,7 +1848,7 @@ it.effect("uses the unfocused main window for a macOS cross-display transition", | ... | @@ -1848,7 +1848,7 @@ it.effect("uses the unfocused main window for a macOS cross-display transition", |
| 1848 | }, | 1848 | }, |
| 1849 | ]); | 1849 | ]); |
| 1850 | flashWindows.length = 0; | 1850 | flashWindows.length = 0; |
| 1851 | const layer = testLayer("darwin", { | 1851 | const layer = layerTest("darwin", { |
| 1852 | makeDirectory: () => Effect.void, | 1852 | makeDirectory: () => Effect.void, |
| 1853 | rename: () => Effect.void, | 1853 | rename: () => Effect.void, |
| 1854 | writeFileString: () => Effect.void, | 1854 | writeFileString: () => Effect.void, |
| ... | @@ -3048,7 +3048,7 @@ it.effect("does not request permissions or create the flash during desktop start | ... | @@ -3048,7 +3048,7 @@ it.effect("does not request permissions or create the flash during desktop start |
| 3048 | assert.lengthOf(getSourcesMock.mock.calls, 0); | 3048 | assert.lengthOf(getSourcesMock.mock.calls, 0); |
| 3049 | assert.lengthOf(openExternalMock.mock.calls, 0); | 3049 | assert.lengthOf(openExternalMock.mock.calls, 0); |
| 3050 | }), | 3050 | }), |
| 3051 | ).pipe(Effect.provide(testLayer("darwin", {}, Option.some(settings)))); | 3051 | ).pipe(Effect.provide(layerTest("darwin", {}, Option.some(settings)))); |
| 3052 | }); | 3052 | }); |
| 3053 | 3053 | ||
| 3054 | it.effect("keeps snapshots disabled when client settings cannot be read at startup", () => { | 3054 | it.effect("keeps snapshots disabled when client settings cannot be read at startup", () => { |
| ... | @@ -3064,7 +3064,7 @@ it.effect("keeps snapshots disabled when client settings cannot be read at start | ... | @@ -3064,7 +3064,7 @@ it.effect("keeps snapshots disabled when client settings cannot be read at start |
| 3064 | yield* service.initialize; | 3064 | yield* service.initialize; |
| 3065 | assert.isFalse((yield* service.state).shortcutRegistered); | 3065 | assert.isFalse((yield* service.state).shortcutRegistered); |
| 3066 | }), | 3066 | }), |
| 3067 | ).pipe(Effect.provide(testLayer("win32", {}, Option.none(), Effect.fail(readError)))); | 3067 | ).pipe(Effect.provide(layerTest("win32", {}, Option.none(), Effect.fail(readError)))); |
| 3068 | }); | 3068 | }); |
| 3069 | 3069 | ||
| 3070 | it.effect("does not request macOS permissions while synchronizing enabled settings", () => { | 3070 | it.effect("does not request macOS permissions while synchronizing enabled settings", () => { |
| ... | @@ -3083,7 +3083,7 @@ it.effect("does not request macOS permissions while synchronizing enabled settin | ... | @@ -3083,7 +3083,7 @@ it.effect("does not request macOS permissions while synchronizing enabled settin |
| 3083 | assert.lengthOf(getSourcesMock.mock.calls, 0); | 3083 | assert.lengthOf(getSourcesMock.mock.calls, 0); |
| 3084 | assert.lengthOf(openExternalMock.mock.calls, 0); | 3084 | assert.lengthOf(openExternalMock.mock.calls, 0); |
| 3085 | }), | 3085 | }), |
| 3086 | ).pipe(Effect.provide(testLayer("darwin"))); | 3086 | ).pipe(Effect.provide(layerTest("darwin"))); |
| 3087 | }); | 3087 | }); |
| 3088 | 3088 | ||
| 3089 | it.effect("requests macOS permissions only for an explicit enable action", () => { | 3089 | it.effect("requests macOS permissions only for an explicit enable action", () => { |
| ... | @@ -3101,7 +3101,7 @@ it.effect("requests macOS permissions only for an explicit enable action", () => | ... | @@ -3101,7 +3101,7 @@ it.effect("requests macOS permissions only for an explicit enable action", () => |
| 3101 | assert.lengthOf(getSourcesMock.mock.calls, 1); | 3101 | assert.lengthOf(getSourcesMock.mock.calls, 1); |
| 3102 | assert.lengthOf(openExternalMock.mock.calls, 1); | 3102 | assert.lengthOf(openExternalMock.mock.calls, 1); |
| 3103 | }), | 3103 | }), |
| 3104 | ).pipe(Effect.provide(testLayer("darwin"))); | 3104 | ).pipe(Effect.provide(layerTest("darwin"))); |
| 3105 | }); | 3105 | }); |
| 3106 | 3106 | ||
| 3107 | it.effect("does not request macOS accessibility permission when capture data is disabled", () => { | 3107 | it.effect("does not request macOS accessibility permission when capture data is disabled", () => { |
| ... | @@ -3119,7 +3119,7 @@ it.effect("does not request macOS accessibility permission when capture data is | ... | @@ -3119,7 +3119,7 @@ it.effect("does not request macOS accessibility permission when capture data is |
| 3119 | assert.lengthOf(getSourcesMock.mock.calls, 1); | 3119 | assert.lengthOf(getSourcesMock.mock.calls, 1); |
| 3120 | assert.lengthOf(openExternalMock.mock.calls, 1); | 3120 | assert.lengthOf(openExternalMock.mock.calls, 1); |
| 3121 | }), | 3121 | }), |
| 3122 | ).pipe(Effect.provide(testLayer("darwin"))); | 3122 | ).pipe(Effect.provide(layerTest("darwin"))); |
| 3123 | }); | 3123 | }); |
| 3124 | 3124 | ||
| 3125 | it.effect("reports macOS permission status and requests each permission on its own", () => { | 3125 | it.effect("reports macOS permission status and requests each permission on its own", () => { |
| ... | @@ -3143,7 +3143,7 @@ it.effect("reports macOS permission status and requests each permission on its o | ... | @@ -3143,7 +3143,7 @@ it.effect("reports macOS permission status and requests each permission on its o |
| 3143 | assert.lengthOf(getSourcesMock.mock.calls, 1); | 3143 | assert.lengthOf(getSourcesMock.mock.calls, 1); |
| 3144 | assert.lengthOf(openExternalMock.mock.calls, 1); | 3144 | assert.lengthOf(openExternalMock.mock.calls, 1); |
| 3145 | }), | 3145 | }), |
| 3146 | ).pipe(Effect.provide(testLayer("darwin"))); | 3146 | ).pipe(Effect.provide(layerTest("darwin"))); |
| 3147 | }); | 3147 | }); |
| 3148 | 3148 | ||
| 3149 | it.effect("flags revoked macOS permissions on read and re-registers once they return", () => { | 3149 | it.effect("flags revoked macOS permissions on read and re-registers once they return", () => { |
| ... | @@ -3183,7 +3183,7 @@ it.effect("flags revoked macOS permissions on read and re-registers once they re | ... | @@ -3183,7 +3183,7 @@ it.effect("flags revoked macOS permissions on read and re-registers once they re |
| 3183 | assert.isNull(recovered.message); | 3183 | assert.isNull(recovered.message); |
| 3184 | assert.isTrue(recovered.shortcutRegistered); | 3184 | assert.isTrue(recovered.shortcutRegistered); |
| 3185 | }), | 3185 | }), |
| 3186 | ).pipe(Effect.provide(testLayer("darwin"))); | 3186 | ).pipe(Effect.provide(layerTest("darwin"))); |
| 3187 | }); | 3187 | }); |
| 3188 | 3188 | ||
| 3189 | it.effect("rejects macOS permission actions off macOS and omits macPermissions there", () => { | 3189 | it.effect("rejects macOS permission actions off macOS and omits macPermissions there", () => { |
| ... | @@ -3196,7 +3196,7 @@ it.effect("rejects macOS permission actions off macOS and omits macPermissions t | ... | @@ -3196,7 +3196,7 @@ it.effect("rejects macOS permission actions off macOS and omits macPermissions t |
| 3196 | const error = yield* service.setup("allow-screen-recording").pipe(Effect.flip); | 3196 | const error = yield* service.setup("allow-screen-recording").pipe(Effect.flip); |
| 3197 | assert.equal(error.reason, "unsupported-session"); | 3197 | assert.equal(error.reason, "unsupported-session"); |
| 3198 | }), | 3198 | }), |
| 3199 | ).pipe(Effect.provide(testLayer("win32"))); | 3199 | ).pipe(Effect.provide(layerTest("win32"))); |
| 3200 | }); | 3200 | }); |
| 3201 | 3201 | ||
| 3202 | it.effect("registers macOS capture without accessibility permission when data is disabled", () => { | 3202 | it.effect("registers macOS capture without accessibility permission when data is disabled", () => { |
| ... | @@ -3216,7 +3216,7 @@ it.effect("registers macOS capture without accessibility permission when data is | ... | @@ -3216,7 +3216,7 @@ it.effect("registers macOS capture without accessibility permission when data is |
| 3216 | assert.lengthOf(accessibilityTrustedMock.mock.calls, 0); | 3216 | assert.lengthOf(accessibilityTrustedMock.mock.calls, 0); |
| 3217 | assert.isTrue((yield* service.state).shortcutRegistered); | 3217 | assert.isTrue((yield* service.state).shortcutRegistered); |
| 3218 | }), | 3218 | }), |
| 3219 | ).pipe(Effect.provide(testLayer("darwin"))); | 3219 | ).pipe(Effect.provide(layerTest("darwin"))); |
| 3220 | }); | 3220 | }); |
| 3221 | 3221 | ||
| 3222 | it.effect("starts the Shift listener outside the Electron main process", () => { | 3222 | it.effect("starts the Shift listener outside the Electron main process", () => { |
| ... | @@ -3240,7 +3240,7 @@ it.effect("starts the Shift listener outside the Electron main process", () => { | ... | @@ -3240,7 +3240,7 @@ it.effect("starts the Shift listener outside the Electron main process", () => { |
| 3240 | yield* Effect.promise(() => new Promise<void>((resolve) => queueMicrotask(resolve))); | 3240 | yield* Effect.promise(() => new Promise<void>((resolve) => queueMicrotask(resolve))); |
| 3241 | assert.isFalse((yield* service.state).shortcutRegistered); | 3241 | assert.isFalse((yield* service.state).shortcutRegistered); |
| 3242 | }), | 3242 | }), |
| 3243 | ).pipe(Effect.provide(testLayer("win32"))); | 3243 | ).pipe(Effect.provide(layerTest("win32"))); |
| 3244 | }); | 3244 | }); |
| 3245 | 3245 | ||
| 3246 | it.effect("passes the configured modifier pair to the listener process", () => { | 3246 | it.effect("passes the configured modifier pair to the listener process", () => { |
| ... | @@ -3261,7 +3261,7 @@ it.effect("passes the configured modifier pair to the listener process", () => { | ... | @@ -3261,7 +3261,7 @@ it.effect("passes the configured modifier pair to the listener process", () => { |
| 3261 | assert.isTrue(state.shortcutRegistered); | 3261 | assert.isTrue(state.shortcutRegistered); |
| 3262 | assert.deepEqual(shortcutForkArgs[0], ["meta"]); | 3262 | assert.deepEqual(shortcutForkArgs[0], ["meta"]); |
| 3263 | }), | 3263 | }), |
| 3264 | ).pipe(Effect.provide(testLayer("win32"))); | 3264 | ).pipe(Effect.provide(layerTest("win32"))); |
| 3265 | }); | 3265 | }); |
| 3266 | 3266 | ||
| 3267 | it.effect("registers a configured key chord instead of the Shift listener", () => { | 3267 | it.effect("registers a configured key chord instead of the Shift listener", () => { |
| ... | @@ -3289,7 +3289,7 @@ it.effect("registers a configured key chord instead of the Shift listener", () = | ... | @@ -3289,7 +3289,7 @@ it.effect("registers a configured key chord instead of the Shift listener", () = |
| 3289 | assert.strictEqual(registerShortcutMock.mock.calls[0]?.[0], "Control+Alt+K"); | 3289 | assert.strictEqual(registerShortcutMock.mock.calls[0]?.[0], "Control+Alt+K"); |
| 3290 | assert.lengthOf(shortcutProcesses, 0); | 3290 | assert.lengthOf(shortcutProcesses, 0); |
| 3291 | }), | 3291 | }), |
| 3292 | ).pipe(Effect.provide(testLayer("win32"))); | 3292 | ).pipe(Effect.provide(layerTest("win32"))); |
| 3293 | }); | 3293 | }); |
| 3294 | 3294 | ||
| 3295 | it.effect("an unrelated client-setting change keeps the registered shortcut", () => { | 3295 | it.effect("an unrelated client-setting change keeps the registered shortcut", () => { |
| ... | @@ -3330,7 +3330,7 @@ it.effect("an unrelated client-setting change keeps the registered shortcut", () | ... | @@ -3330,7 +3330,7 @@ it.effect("an unrelated client-setting change keeps the registered shortcut", () |
| 3330 | ["Control+Alt+K", "Control+Alt+J"], | 3330 | ["Control+Alt+K", "Control+Alt+J"], |
| 3331 | ); | 3331 | ); |
| 3332 | }), | 3332 | }), |
| 3333 | ).pipe(Effect.provide(testLayer("win32"))); | 3333 | ).pipe(Effect.provide(layerTest("win32"))); |
| 3334 | }); | 3334 | }); |
| 3335 | 3335 | ||
| 3336 | it.effect("capture fails closed while snapshots are disabled", () => { | 3336 | it.effect("capture fails closed while snapshots are disabled", () => { |
| ... | @@ -3348,7 +3348,7 @@ it.effect("capture fails closed while snapshots are disabled", () => { | ... | @@ -3348,7 +3348,7 @@ it.effect("capture fails closed while snapshots are disabled", () => { |
| 3348 | assert.lengthOf(getSourcesMock.mock.calls, 0); | 3348 | assert.lengthOf(getSourcesMock.mock.calls, 0); |
| 3349 | assert.lengthOf(regionCaptureMock.mock.calls, 0); | 3349 | assert.lengthOf(regionCaptureMock.mock.calls, 0); |
| 3350 | }), | 3350 | }), |
| 3351 | ).pipe(Effect.provide(testLayer("win32"))); | 3351 | ).pipe(Effect.provide(layerTest("win32"))); |
| 3352 | }); | 3352 | }); |
| 3353 | 3353 | ||
| 3354 | it.effect("keeps shortcut registration errors off the capture status", () => { | 3354 | it.effect("keeps shortcut registration errors off the capture status", () => { |
| ... | @@ -3378,7 +3378,7 @@ it.effect("keeps shortcut registration errors off the capture status", () => { | ... | @@ -3378,7 +3378,7 @@ it.effect("keeps shortcut registration errors off the capture status", () => { |
| 3378 | "This shortcut is already used by the system or another app.", | 3378 | "This shortcut is already used by the system or another app.", |
| 3379 | ); | 3379 | ); |
| 3380 | }), | 3380 | }), |
| 3381 | ).pipe(Effect.provide(testLayer("win32"))); | 3381 | ).pipe(Effect.provide(layerTest("win32"))); |
| 3382 | }); | 3382 | }); |
| 3383 | 3383 | ||
| 3384 | it.effect("uses an external Niri shortcut without registering an Electron accelerator", () => { | 3384 | it.effect("uses an external Niri shortcut without registering an Electron accelerator", () => { |
| ... | @@ -3411,7 +3411,7 @@ it.effect("uses an external Niri shortcut without registering an Electron accele | ... | @@ -3411,7 +3411,7 @@ it.effect("uses an external Niri shortcut without registering an Electron accele |
| 3411 | assert.isUndefined((yield* service.state).shortcutBinding); | 3411 | assert.isUndefined((yield* service.state).shortcutBinding); |
| 3412 | }), | 3412 | }), |
| 3413 | ).pipe( | 3413 | ).pipe( |
| 3414 | Effect.provide(testLayer("linux")), | 3414 | Effect.provide(layerTest("linux")), |
| 3415 | Effect.ensuring(Effect.sync(() => vi.unstubAllEnvs())), | 3415 | Effect.ensuring(Effect.sync(() => vi.unstubAllEnvs())), |
| 3416 | ); | 3416 | ); |
| 3417 | }); | 3417 | }); |
| ... | @@ -3452,7 +3452,7 @@ it.effect("defers ordinary Wayland shortcut registration until settings are appl | ... | @@ -3452,7 +3452,7 @@ it.effect("defers ordinary Wayland shortcut registration until settings are appl |
| 3452 | assert.lengthOf(registerShortcutMock.mock.calls, 0); | 3452 | assert.lengthOf(registerShortcutMock.mock.calls, 0); |
| 3453 | }), | 3453 | }), |
| 3454 | ).pipe( | 3454 | ).pipe( |
| 3455 | Effect.provide(testLayer("linux")), | 3455 | Effect.provide(layerTest("linux")), |
| 3456 | Effect.ensuring(Effect.sync(() => vi.unstubAllEnvs())), | 3456 | Effect.ensuring(Effect.sync(() => vi.unstubAllEnvs())), |
| 3457 | ); | 3457 | ); |
| 3458 | }); | 3458 | }); |
| ... | @@ -3472,7 +3472,7 @@ it.effect.each(["GNOME", "KDE", "niri", "Hyprland"] as const)( | ... | @@ -3472,7 +3472,7 @@ it.effect.each(["GNOME", "KDE", "niri", "Hyprland"] as const)( |
| 3472 | assert.isFalse(state.shortcutVerified); | 3472 | assert.isFalse(state.shortcutVerified); |
| 3473 | }), | 3473 | }), |
| 3474 | ).pipe( | 3474 | ).pipe( |
| 3475 | Effect.provide(testLayer("linux")), | 3475 | Effect.provide(layerTest("linux")), |
| 3476 | Effect.ensuring(Effect.sync(() => vi.unstubAllEnvs())), | 3476 | Effect.ensuring(Effect.sync(() => vi.unstubAllEnvs())), |
| 3477 | ); | 3477 | ); |
| 3478 | }, | 3478 | }, |
| ... | @@ -3539,7 +3539,7 @@ it.effect( | ... | @@ -3539,7 +3539,7 @@ it.effect( |
| 3539 | }); | 3539 | }); |
| 3540 | assert.isTrue((yield* service.state).shortcutVerified); | 3540 | assert.isTrue((yield* service.state).shortcutVerified); |
| 3541 | }), | 3541 | }), |
| 3542 | ).pipe(Effect.provide(testLayer("linux", {}, Option.some(settings)))); | 3542 | ).pipe(Effect.provide(layerTest("linux", {}, Option.some(settings)))); |
| 3543 | } | 3543 | } |
| 3544 | }).pipe( | 3544 | }).pipe( |
| 3545 | Effect.ensuring( | 3545 | Effect.ensuring( |
| ... | @@ -3569,7 +3569,7 @@ it.effect("does not register a Wayland modifier-pair shortcut when enabled", () | ... | @@ -3569,7 +3569,7 @@ it.effect("does not register a Wayland modifier-pair shortcut when enabled", () |
| 3569 | assert.match(state.shortcutMessage ?? "", /Modifier-pair shortcuts aren't available/); | 3569 | assert.match(state.shortcutMessage ?? "", /Modifier-pair shortcuts aren't available/); |
| 3570 | }), | 3570 | }), |
| 3571 | ).pipe( | 3571 | ).pipe( |
| 3572 | Effect.provide(testLayer("linux")), | 3572 | Effect.provide(layerTest("linux")), |
| 3573 | Effect.ensuring(Effect.sync(() => vi.unstubAllEnvs())), | 3573 | Effect.ensuring(Effect.sync(() => vi.unstubAllEnvs())), |
| 3574 | ); | 3574 | ); |
| 3575 | }); | 3575 | }); |
| ... | @@ -3626,7 +3626,7 @@ it.effect("exposes pending portal permission and then the real assigned shortcut | ... | @@ -3626,7 +3626,7 @@ it.effect("exposes pending portal permission and then the real assigned shortcut |
| 3626 | assert.equal(failed.shortcutMessage, "Permission denied"); | 3626 | assert.equal(failed.shortcutMessage, "Permission denied"); |
| 3627 | }), | 3627 | }), |
| 3628 | ).pipe( | 3628 | ).pipe( |
| 3629 | Effect.provide(testLayer("linux")), | 3629 | Effect.provide(layerTest("linux")), |
| 3630 | Effect.ensuring(Effect.sync(() => vi.unstubAllEnvs())), | 3630 | Effect.ensuring(Effect.sync(() => vi.unstubAllEnvs())), |
| 3631 | ); | 3631 | ); |
| 3632 | }); | 3632 | }); |
| ... | @@ -3683,7 +3683,7 @@ it.effect("verifies only native shortcut delivery and captures normally", () => | ... | @@ -3683,7 +3683,7 @@ it.effect("verifies only native shortcut delivery and captures normally", () => |
| 3683 | }), | 3683 | }), |
| 3684 | ).pipe( | 3684 | ).pipe( |
| 3685 | Effect.provide( | 3685 | Effect.provide( |
| 3686 | testLayer("linux", { | 3686 | layerTest("linux", { |
| 3687 | makeDirectory: () => Effect.void, | 3687 | makeDirectory: () => Effect.void, |
| 3688 | rename: () => Effect.void, | 3688 | rename: () => Effect.void, |
| 3689 | writeFile: () => Effect.void, | 3689 | writeFile: () => Effect.void, |
| ... | @@ -3743,7 +3743,7 @@ it.effect( | ... | @@ -3743,7 +3743,7 @@ it.effect( |
| 3743 | assert.isFalse((yield* service.state).shortcutRegistered); | 3743 | assert.isFalse((yield* service.state).shortcutRegistered); |
| 3744 | }), | 3744 | }), |
| 3745 | ).pipe( | 3745 | ).pipe( |
| 3746 | Effect.provide(testLayer("linux")), | 3746 | Effect.provide(layerTest("linux")), |
| 3747 | Effect.ensuring(Effect.sync(() => vi.unstubAllEnvs())), | 3747 | Effect.ensuring(Effect.sync(() => vi.unstubAllEnvs())), |
| 3748 | ); | 3748 | ); |
| 3749 | }, | 3749 | }, |
| ... | @@ -3781,7 +3781,7 @@ it.effect( | ... | @@ -3781,7 +3781,7 @@ it.effect( |
| 3781 | assert.notEqual((yield* service.state).shortcutActionRegistered, true); | 3781 | assert.notEqual((yield* service.state).shortcutActionRegistered, true); |
| 3782 | }), | 3782 | }), |
| 3783 | ).pipe( | 3783 | ).pipe( |
| 3784 | Effect.provide(testLayer("linux")), | 3784 | Effect.provide(layerTest("linux")), |
| 3785 | Effect.ensuring(Effect.sync(() => vi.unstubAllEnvs())), | 3785 | Effect.ensuring(Effect.sync(() => vi.unstubAllEnvs())), |
| 3786 | ); | 3786 | ); |
| 3787 | }, | 3787 | }, |
| ... | @@ -3796,7 +3796,7 @@ it.effect("advises about the system menu for a meta pair on Windows", () => | ... | @@ -3796,7 +3796,7 @@ it.effect("advises about the system menu for a meta pair on Windows", () => |
| 3796 | assert.match(result.message ?? "", /Super \+ Super is observed/); | 3796 | assert.match(result.message ?? "", /Super \+ Super is observed/); |
| 3797 | assert.match(result.message ?? "", /system's own menu/); | 3797 | assert.match(result.message ?? "", /system's own menu/); |
| 3798 | }), | 3798 | }), |
| 3799 | ).pipe(Effect.provide(testLayer("win32"))), | 3799 | ).pipe(Effect.provide(layerTest("win32"))), |
| 3800 | ); | 3800 | ); |
| 3801 | 3801 | ||
| 3802 | it.effect("probes macOS modifier pairs with the flags poller", () => { | 3802 | it.effect("probes macOS modifier pairs with the flags poller", () => { |
| ... | @@ -3812,7 +3812,7 @@ it.effect("probes macOS modifier pairs with the flags poller", () => { | ... | @@ -3812,7 +3812,7 @@ it.effect("probes macOS modifier pairs with the flags poller", () => { |
| 3812 | assert.deepEqual(spawnedPollers[0]?.args.slice(-2), ["2", "4"]); | 3812 | assert.deepEqual(spawnedPollers[0]?.args.slice(-2), ["2", "4"]); |
| 3813 | assert.strictEqual(spawnedPollers[0]?.kill.mock.calls.length, 1); | 3813 | assert.strictEqual(spawnedPollers[0]?.kill.mock.calls.length, 1); |
| 3814 | }), | 3814 | }), |
| 3815 | ).pipe(Effect.provide(testLayer("darwin"))); | 3815 | ).pipe(Effect.provide(layerTest("darwin"))); |
| 3816 | }); | 3816 | }); |
| 3817 | 3817 | ||
| 3818 | it.effect("registers macOS modifier pairs through the flags poller", () => { | 3818 | it.effect("registers macOS modifier pairs through the flags poller", () => { |
| ... | @@ -3836,7 +3836,7 @@ it.effect("registers macOS modifier pairs through the flags poller", () => { | ... | @@ -3836,7 +3836,7 @@ it.effect("registers macOS modifier pairs through the flags poller", () => { |
| 3836 | assert.deepEqual(spawnedPollers[0]?.args.slice(-2), ["8", "16"]); | 3836 | assert.deepEqual(spawnedPollers[0]?.args.slice(-2), ["8", "16"]); |
| 3837 | assert.isTrue(state.shortcutRegistered); | 3837 | assert.isTrue(state.shortcutRegistered); |
| 3838 | }), | 3838 | }), |
| 3839 | ).pipe(Effect.provide(testLayer("darwin"))); | 3839 | ).pipe(Effect.provide(layerTest("darwin"))); |
| 3840 | }); | 3840 | }); |
| 3841 | 3841 | ||
| 3842 | it.effect("waits to apply settings while permissions are pending", () => { | 3842 | it.effect("waits to apply settings while permissions are pending", () => { |
| ... | @@ -3849,7 +3849,7 @@ it.effect("waits to apply settings while permissions are pending", () => { | ... | @@ -3849,7 +3849,7 @@ it.effect("waits to apply settings while permissions are pending", () => { |
| 3849 | finishPermissionRequest = () => resolve([]); | 3849 | finishPermissionRequest = () => resolve([]); |
| 3850 | }), | 3850 | }), |
| 3851 | ); | 3851 | ); |
| 3852 | const layer = testLayer("darwin"); | 3852 | const layer = layerTest("darwin"); |
| 3853 | 3853 | ||
| 3854 | return Effect.scoped( | 3854 | return Effect.scoped( |
| 3855 | Effect.gen(function* () { | 3855 | Effect.gen(function* () { |
| ... | @@ -3905,7 +3905,7 @@ it.effect.each([false, true])( | ... | @@ -3905,7 +3905,7 @@ it.effect.each([false, true])( |
| 3905 | }), | 3905 | }), |
| 3906 | ).pipe( | 3906 | ).pipe( |
| 3907 | Effect.provide( | 3907 | Effect.provide( |
| 3908 | testLayer("darwin", { | 3908 | layerTest("darwin", { |
| 3909 | makeTempDirectoryScoped: () => | 3909 | makeTempDirectoryScoped: () => |
| 3910 | Effect.acquireRelease(Effect.succeed("/tmp/setup-test"), () => | 3910 | Effect.acquireRelease(Effect.succeed("/tmp/setup-test"), () => |
| 3911 | Effect.sync(() => { | 3911 | Effect.sync(() => { |
| ... | @@ -3926,5 +3926,5 @@ it.effect("rejects macOS test capture on other platforms", () => | ... | @@ -3926,5 +3926,5 @@ it.effect("rejects macOS test capture on other platforms", () => |
| 3926 | const error = yield* service.setup("test-mac-capture").pipe(Effect.flip); | 3926 | const error = yield* service.setup("test-mac-capture").pipe(Effect.flip); |
| 3927 | assert.equal(error.reason, "unsupported-session"); | 3927 | assert.equal(error.reason, "unsupported-session"); |
| 3928 | }), | 3928 | }), |
| 3929 | ).pipe(Effect.provide(testLayer("win32"))), | 3929 | ).pipe(Effect.provide(layerTest("win32"))), |
| 3930 | ); | 3930 | ); |
apps/desktop/src/ssh/DesktopSshPasswordPrompts.test.ts+8-8| ... | @@ -86,7 +86,7 @@ function makeTestWindow( | ... | @@ -86,7 +86,7 @@ function makeTestWindow( |
| 86 | }; | 86 | }; |
| 87 | } | 87 | } |
| 88 | 88 | ||
| 89 | function makeElectronWindowLayer(window: ReturnType<typeof makeTestWindow>["window"]) { | 89 | function layerElectronWindow(window: ReturnType<typeof makeTestWindow>["window"]) { |
| 90 | return Layer.succeed( | 90 | return Layer.succeed( |
| 91 | ElectronWindow.ElectronWindow, | 91 | ElectronWindow.ElectronWindow, |
| 92 | ElectronWindow.ElectronWindow.of({ | 92 | ElectronWindow.ElectronWindow.of({ |
| ... | @@ -105,9 +105,9 @@ function makeElectronWindowLayer(window: ReturnType<typeof makeTestWindow>["wind | ... | @@ -105,9 +105,9 @@ function makeElectronWindowLayer(window: ReturnType<typeof makeTestWindow>["wind |
| 105 | ); | 105 | ); |
| 106 | } | 106 | } |
| 107 | 107 | ||
| 108 | function makeLayer(window: ReturnType<typeof makeTestWindow>["window"]) { | 108 | function layer(window: ReturnType<typeof makeTestWindow>["window"]) { |
| 109 | return DesktopSshPasswordPrompts.layer({ passwordPromptTimeoutMs: 1_000 }).pipe( | 109 | return DesktopSshPasswordPrompts.layer({ passwordPromptTimeoutMs: 1_000 }).pipe( |
| 110 | Layer.provide(makeElectronWindowLayer(window)), | 110 | Layer.provide(layerElectronWindow(window)), |
| 111 | Layer.provide(NodeServices.layer), | 111 | Layer.provide(NodeServices.layer), |
| 112 | Layer.provideMerge(TestClock.layer()), | 112 | Layer.provideMerge(TestClock.layer()), |
| 113 | ); | 113 | ); |
| ... | @@ -141,7 +141,7 @@ describe("DesktopSshPasswordPrompts", () => { | ... | @@ -141,7 +141,7 @@ describe("DesktopSshPasswordPrompts", () => { |
| 141 | 141 | ||
| 142 | yield* prompts.resolve({ requestId: request.requestId, password: "secret" }); | 142 | yield* prompts.resolve({ requestId: request.requestId, password: "secret" }); |
| 143 | assert.equal(yield* Fiber.join(fiber), "secret"); | 143 | assert.equal(yield* Fiber.join(fiber), "secret"); |
| 144 | }).pipe(Effect.provide(makeLayer(testWindow.window)), Effect.scoped); | 144 | }).pipe(Effect.provide(layer(testWindow.window)), Effect.scoped); |
| 145 | }); | 145 | }); |
| 146 | 146 | ||
| 147 | it.effect("times out pending renderer prompts with a typed error", () => { | 147 | it.effect("times out pending renderer prompts with a typed error", () => { |
| ... | @@ -163,7 +163,7 @@ describe("DesktopSshPasswordPrompts", () => { | ... | @@ -163,7 +163,7 @@ describe("DesktopSshPasswordPrompts", () => { |
| 163 | const error = yield* Fiber.join(fiber).pipe(Effect.flip); | 163 | const error = yield* Fiber.join(fiber).pipe(Effect.flip); |
| 164 | assert.instanceOf(error, DesktopSshPasswordPrompts.DesktopSshPromptTimedOutError); | 164 | assert.instanceOf(error, DesktopSshPasswordPrompts.DesktopSshPromptTimedOutError); |
| 165 | assert.equal(error.destination, "devbox"); | 165 | assert.equal(error.destination, "devbox"); |
| 166 | }).pipe(Effect.provide(makeLayer(testWindow.window)), Effect.scoped); | 166 | }).pipe(Effect.provide(layer(testWindow.window)), Effect.scoped); |
| 167 | }); | 167 | }); |
| 168 | 168 | ||
| 169 | it.effect("cleans up a prompt that fails during renderer delivery", () => { | 169 | it.effect("cleans up a prompt that fails during renderer delivery", () => { |
| ... | @@ -194,7 +194,7 @@ describe("DesktopSshPasswordPrompts", () => { | ... | @@ -194,7 +194,7 @@ describe("DesktopSshPasswordPrompts", () => { |
| 194 | .resolve({ requestId, password: "secret" }) | 194 | .resolve({ requestId, password: "secret" }) |
| 195 | .pipe(Effect.flip); | 195 | .pipe(Effect.flip); |
| 196 | assert.instanceOf(resolveError, DesktopSshPasswordPrompts.DesktopSshPromptExpiredError); | 196 | assert.instanceOf(resolveError, DesktopSshPasswordPrompts.DesktopSshPromptExpiredError); |
| 197 | }).pipe(Effect.provide(makeLayer(testWindow.window)), Effect.scoped); | 197 | }).pipe(Effect.provide(layer(testWindow.window)), Effect.scoped); |
| 198 | }); | 198 | }); |
| 199 | 199 | ||
| 200 | it.effect("keeps a submitted password when a later presentation step fails", () => { | 200 | it.effect("keeps a submitted password when a later presentation step fails", () => { |
| ... | @@ -223,7 +223,7 @@ describe("DesktopSshPasswordPrompts", () => { | ... | @@ -223,7 +223,7 @@ describe("DesktopSshPasswordPrompts", () => { |
| 223 | assert.equal(password, "secret"); | 223 | assert.equal(password, "secret"); |
| 224 | assert.equal(testWindow.isFocused(), false); | 224 | assert.equal(testWindow.isFocused(), false); |
| 225 | assert.equal(testWindow.closedListenerCount(), 0); | 225 | assert.equal(testWindow.closedListenerCount(), 0); |
| 226 | }).pipe(Effect.provide(makeLayer(testWindow.window)), Effect.scoped); | 226 | }).pipe(Effect.provide(layer(testWindow.window)), Effect.scoped); |
| 227 | }); | 227 | }); |
| 228 | 228 | ||
| 229 | it.effect("classifies a failed initial window availability check", () => { | 229 | it.effect("classifies a failed initial window availability check", () => { |
| ... | @@ -244,6 +244,6 @@ describe("DesktopSshPasswordPrompts", () => { | ... | @@ -244,6 +244,6 @@ describe("DesktopSshPasswordPrompts", () => { |
| 244 | assert.equal(error.operation, "check-window-before-request"); | 244 | assert.equal(error.operation, "check-window-before-request"); |
| 245 | assert.equal(error.requestId, null); | 245 | assert.equal(error.requestId, null); |
| 246 | assert.deepEqual(testWindow.sentMessages, []); | 246 | assert.deepEqual(testWindow.sentMessages, []); |
| 247 | }).pipe(Effect.provide(makeLayer(testWindow.window)), Effect.scoped); | 247 | }).pipe(Effect.provide(layer(testWindow.window)), Effect.scoped); |
| 248 | }); | 248 | }); |
| 249 | }); | 249 | }); |
apps/desktop/src/telemetry/DesktopTelemetryPublisher.test.ts+13-13| ... | @@ -24,13 +24,13 @@ import * as ElectronPowerMonitor from "../electron/ElectronPowerMonitor.ts"; | ... | @@ -24,13 +24,13 @@ import * as ElectronPowerMonitor from "../electron/ElectronPowerMonitor.ts"; |
| 24 | import * as DesktopTelemetryPublisher from "./DesktopTelemetryPublisher.ts"; | 24 | import * as DesktopTelemetryPublisher from "./DesktopTelemetryPublisher.ts"; |
| 25 | import * as DesktopRendererHistory from "./DesktopRendererHistory.ts"; | 25 | import * as DesktopRendererHistory from "./DesktopRendererHistory.ts"; |
| 26 | 26 | ||
| 27 | const historyLayer = Layer.succeed(DesktopRendererHistory.DesktopRendererHistory, { | 27 | const layerHistory = Layer.succeed(DesktopRendererHistory.DesktopRendererHistory, { |
| 28 | register: () => Effect.void, | 28 | register: () => Effect.void, |
| 29 | recordMetrics: () => Effect.void, | 29 | recordMetrics: () => Effect.void, |
| 30 | shutdown: Effect.void, | 30 | shutdown: Effect.void, |
| 31 | }); | 31 | }); |
| 32 | 32 | ||
| 33 | function makeElectronAppLayer( | 33 | function layerElectronApp( |
| 34 | metrics: ReadonlyArray<Electron.ProcessMetric>, | 34 | metrics: ReadonlyArray<Electron.ProcessMetric>, |
| 35 | onMetricsRead: () => void = () => undefined, | 35 | onMetricsRead: () => void = () => undefined, |
| 36 | ) { | 36 | ) { |
| ... | @@ -65,7 +65,7 @@ describe("DesktopTelemetryPublisher", () => { | ... | @@ -65,7 +65,7 @@ describe("DesktopTelemetryPublisher", () => { |
| 65 | Effect.gen(function* () { | 65 | Effect.gen(function* () { |
| 66 | const pollStarted = yield* Deferred.make<void>(); | 66 | const pollStarted = yield* Deferred.make<void>(); |
| 67 | const blockPoll = yield* Deferred.make<void>(); | 67 | const blockPoll = yield* Deferred.make<void>(); |
| 68 | const powerLayer = Layer.succeed( | 68 | const layerPower = Layer.succeed( |
| 69 | ElectronPowerMonitor.ElectronPowerMonitor, | 69 | ElectronPowerMonitor.ElectronPowerMonitor, |
| 70 | ElectronPowerMonitor.ElectronPowerMonitor.of({ | 70 | ElectronPowerMonitor.ElectronPowerMonitor.of({ |
| 71 | isOnBatteryPower: Effect.succeed(false), | 71 | isOnBatteryPower: Effect.succeed(false), |
| ... | @@ -81,7 +81,7 @@ describe("DesktopTelemetryPublisher", () => { | ... | @@ -81,7 +81,7 @@ describe("DesktopTelemetryPublisher", () => { |
| 81 | }), | 81 | }), |
| 82 | ); | 82 | ); |
| 83 | const layer = DesktopTelemetryPublisher.layer.pipe( | 83 | const layer = DesktopTelemetryPublisher.layer.pipe( |
| 84 | Layer.provide(Layer.mergeAll(makeElectronAppLayer([]), powerLayer, historyLayer)), | 84 | Layer.provide(Layer.mergeAll(layerElectronApp([]), layerPower, layerHistory)), |
| 85 | ); | 85 | ); |
| 86 | const scope = yield* Scope.make(); | 86 | const scope = yield* Scope.make(); |
| 87 | 87 | ||
| ... | @@ -124,7 +124,7 @@ describe("DesktopTelemetryPublisher", () => { | ... | @@ -124,7 +124,7 @@ describe("DesktopTelemetryPublisher", () => { |
| 124 | }, | 124 | }, |
| 125 | } as Electron.ProcessMetric, | 125 | } as Electron.ProcessMetric, |
| 126 | ]; | 126 | ]; |
| 127 | const powerLayer = Layer.succeed( | 127 | const layerPower = Layer.succeed( |
| 128 | ElectronPowerMonitor.ElectronPowerMonitor, | 128 | ElectronPowerMonitor.ElectronPowerMonitor, |
| 129 | ElectronPowerMonitor.ElectronPowerMonitor.of({ | 129 | ElectronPowerMonitor.ElectronPowerMonitor.of({ |
| 130 | isOnBatteryPower: Ref.get(onBattery), | 130 | isOnBatteryPower: Ref.get(onBattery), |
| ... | @@ -149,10 +149,10 @@ describe("DesktopTelemetryPublisher", () => { | ... | @@ -149,10 +149,10 @@ describe("DesktopTelemetryPublisher", () => { |
| 149 | const layer = DesktopTelemetryPublisher.layer.pipe( | 149 | const layer = DesktopTelemetryPublisher.layer.pipe( |
| 150 | Layer.provide( | 150 | Layer.provide( |
| 151 | Layer.mergeAll( | 151 | Layer.mergeAll( |
| 152 | makeElectronAppLayer(metrics, () => { | 152 | layerElectronApp(metrics, () => { |
| 153 | metricsReadCount += 1; | 153 | metricsReadCount += 1; |
| 154 | }), | 154 | }), |
| 155 | powerLayer, | 155 | layerPower, |
| 156 | Layer.succeed(DesktopRendererHistory.DesktopRendererHistory, { | 156 | Layer.succeed(DesktopRendererHistory.DesktopRendererHistory, { |
| 157 | register: () => Effect.void, | 157 | register: () => Effect.void, |
| 158 | shutdown: Effect.void, | 158 | shutdown: Effect.void, |
| ... | @@ -413,7 +413,7 @@ describe("DesktopTelemetryPublisher", () => { | ... | @@ -413,7 +413,7 @@ describe("DesktopTelemetryPublisher", () => { |
| 413 | 413 | ||
| 414 | it.effect("routes requestDesktopUpdate control messages and replays update reports", () => | 414 | it.effect("routes requestDesktopUpdate control messages and replays update reports", () => |
| 415 | Effect.gen(function* () { | 415 | Effect.gen(function* () { |
| 416 | const powerLayer = Layer.succeed( | 416 | const layerPower = Layer.succeed( |
| 417 | ElectronPowerMonitor.ElectronPowerMonitor, | 417 | ElectronPowerMonitor.ElectronPowerMonitor, |
| 418 | ElectronPowerMonitor.ElectronPowerMonitor.of({ | 418 | ElectronPowerMonitor.ElectronPowerMonitor.of({ |
| 419 | isOnBatteryPower: Effect.succeed(false), | 419 | isOnBatteryPower: Effect.succeed(false), |
| ... | @@ -426,7 +426,7 @@ describe("DesktopTelemetryPublisher", () => { | ... | @@ -426,7 +426,7 @@ describe("DesktopTelemetryPublisher", () => { |
| 426 | }), | 426 | }), |
| 427 | ); | 427 | ); |
| 428 | const layer = DesktopTelemetryPublisher.layer.pipe( | 428 | const layer = DesktopTelemetryPublisher.layer.pipe( |
| 429 | Layer.provide(Layer.mergeAll(makeElectronAppLayer([]), powerLayer, historyLayer)), | 429 | Layer.provide(Layer.mergeAll(layerElectronApp([]), layerPower, layerHistory)), |
| 430 | ); | 430 | ); |
| 431 | 431 | ||
| 432 | yield* Effect.gen(function* () { | 432 | yield* Effect.gen(function* () { |
| ... | @@ -562,7 +562,7 @@ describe("DesktopRendererHistory", () => { | ... | @@ -562,7 +562,7 @@ describe("DesktopRendererHistory", () => { |
| 562 | readonly written: Deferred.Deferred<Record>; | 562 | readonly written: Deferred.Deferred<Record>; |
| 563 | } | 563 | } |
| 564 | | undefined; | 564 | | undefined; |
| 565 | const fileLayer = Layer.succeed(FileSystem.FileSystem, { | 565 | const layerFile = Layer.succeed(FileSystem.FileSystem, { |
| 566 | ...fileSystem, | 566 | ...fileSystem, |
| 567 | writeFile: (filePath, bytes, options) => | 567 | writeFile: (filePath, bytes, options) => |
| 568 | fileSystem.writeFile(filePath, bytes, options).pipe( | 568 | fileSystem.writeFile(filePath, bytes, options).pipe( |
| ... | @@ -590,7 +590,7 @@ describe("DesktopRendererHistory", () => { | ... | @@ -590,7 +590,7 @@ describe("DesktopRendererHistory", () => { |
| 590 | logDir: directory, | 590 | logDir: directory, |
| 591 | } as DesktopEnvironment.DesktopEnvironment["Service"]), | 591 | } as DesktopEnvironment.DesktopEnvironment["Service"]), |
| 592 | ), | 592 | ), |
| 593 | fileLayer, | 593 | layerFile, |
| 594 | ), | 594 | ), |
| 595 | ), | 595 | ), |
| 596 | ); | 596 | ); |
| ... | @@ -735,7 +735,7 @@ describe("DesktopRendererHistory", () => { | ... | @@ -735,7 +735,7 @@ describe("DesktopRendererHistory", () => { |
| 735 | ); | 735 | ); |
| 736 | } | 736 | } |
| 737 | const written = yield* Deferred.make<void>(); | 737 | const written = yield* Deferred.make<void>(); |
| 738 | const fileLayer = Layer.succeed(FileSystem.FileSystem, { | 738 | const layerFile = Layer.succeed(FileSystem.FileSystem, { |
| 739 | ...fileSystem, | 739 | ...fileSystem, |
| 740 | writeFile: (target, bytes, options) => | 740 | writeFile: (target, bytes, options) => |
| 741 | fileSystem | 741 | fileSystem |
| ... | @@ -751,7 +751,7 @@ describe("DesktopRendererHistory", () => { | ... | @@ -751,7 +751,7 @@ describe("DesktopRendererHistory", () => { |
| 751 | logDir: directory, | 751 | logDir: directory, |
| 752 | } as DesktopEnvironment.DesktopEnvironment["Service"]), | 752 | } as DesktopEnvironment.DesktopEnvironment["Service"]), |
| 753 | ), | 753 | ), |
| 754 | fileLayer, | 754 | layerFile, |
| 755 | ), | 755 | ), |
| 756 | ), | 756 | ), |
| 757 | ); | 757 | ); |
apps/desktop/src/updates/updatesTestHarness.ts+12-12| ... | @@ -67,7 +67,7 @@ export function makeHarness(options: UpdatesHarnessOptions = {}) { | ... | @@ -67,7 +67,7 @@ export function makeHarness(options: UpdatesHarnessOptions = {}) { |
| 67 | } | 67 | } |
| 68 | }; | 68 | }; |
| 69 | 69 | ||
| 70 | const updaterLayer = Layer.succeed(ElectronUpdater.ElectronUpdater, { | 70 | const layerUpdater = Layer.succeed(ElectronUpdater.ElectronUpdater, { |
| 71 | setFeedURL: (options) => | 71 | setFeedURL: (options) => |
| 72 | Effect.sync(() => { | 72 | Effect.sync(() => { |
| 73 | feedUrls.push(options); | 73 | feedUrls.push(options); |
| ... | @@ -109,7 +109,7 @@ export function makeHarness(options: UpdatesHarnessOptions = {}) { | ... | @@ -109,7 +109,7 @@ export function makeHarness(options: UpdatesHarnessOptions = {}) { |
| 109 | ).pipe(Effect.asVoid), | 109 | ).pipe(Effect.asVoid), |
| 110 | } satisfies ElectronUpdater.ElectronUpdater["Service"]); | 110 | } satisfies ElectronUpdater.ElectronUpdater["Service"]); |
| 111 | 111 | ||
| 112 | const windowLayer = Layer.succeed(ElectronWindow.ElectronWindow, { | 112 | const layerWindow = Layer.succeed(ElectronWindow.ElectronWindow, { |
| 113 | create: () => Effect.die("unexpected BrowserWindow creation"), | 113 | create: () => Effect.die("unexpected BrowserWindow creation"), |
| 114 | main: Effect.succeedNone, | 114 | main: Effect.succeedNone, |
| 115 | currentMainOrFirst: Effect.succeedNone, | 115 | currentMainOrFirst: Effect.succeedNone, |
| ... | @@ -145,9 +145,9 @@ export function makeHarness(options: UpdatesHarnessOptions = {}) { | ... | @@ -145,9 +145,9 @@ export function makeHarness(options: UpdatesHarnessOptions = {}) { |
| 145 | }), | 145 | }), |
| 146 | waitForReady: () => Effect.succeed(true), | 146 | waitForReady: () => Effect.succeed(true), |
| 147 | }; | 147 | }; |
| 148 | const backendLayer = DesktopBackendPool.layerTest([stubBackendInstance]); | 148 | const layerBackend = DesktopBackendPool.layerTest([stubBackendInstance]); |
| 149 | 149 | ||
| 150 | const environmentLayer = DesktopEnvironment.layer({ | 150 | const layerEnvironment = DesktopEnvironment.layer({ |
| 151 | dirname: "/repo/apps/desktop/src", | 151 | dirname: "/repo/apps/desktop/src", |
| 152 | homeDirectory: `/tmp/t3-desktop-updates-home-${process.pid}`, | 152 | homeDirectory: `/tmp/t3-desktop-updates-home-${process.pid}`, |
| 153 | platform: options.platform ?? "darwin", | 153 | platform: options.platform ?? "darwin", |
| ... | @@ -175,7 +175,7 @@ export function makeHarness(options: UpdatesHarnessOptions = {}) { | ... | @@ -175,7 +175,7 @@ export function makeHarness(options: UpdatesHarnessOptions = {}) { |
| 175 | ...DesktopAppSettings.DEFAULT_DESKTOP_SETTINGS, | 175 | ...DesktopAppSettings.DEFAULT_DESKTOP_SETTINGS, |
| 176 | }; | 176 | }; |
| 177 | const setUpdateChannelError = options.setUpdateChannelError; | 177 | const setUpdateChannelError = options.setUpdateChannelError; |
| 178 | const settingsLayer = | 178 | const layerSettings = |
| 179 | setUpdateChannelError || options.beforeSetUpdateChannel | 179 | setUpdateChannelError || options.beforeSetUpdateChannel |
| 180 | ? Layer.succeed(DesktopAppSettings.DesktopAppSettings, { | 180 | ? Layer.succeed(DesktopAppSettings.DesktopAppSettings, { |
| 181 | get: Effect.sync(() => testSettings), | 181 | get: Effect.sync(() => testSettings), |
| ... | @@ -211,7 +211,7 @@ export function makeHarness(options: UpdatesHarnessOptions = {}) { | ... | @@ -211,7 +211,7 @@ export function makeHarness(options: UpdatesHarnessOptions = {}) { |
| 211 | // Tracks the restart markers installs leave, so installs stay free of real | 211 | // Tracks the restart markers installs leave, so installs stay free of real |
| 212 | // disk I/O that would outrun the tests' settle loops. | 212 | // disk I/O that would outrun the tests' settle loops. |
| 213 | const updateRestartMarkers = new Set<string>(); | 213 | const updateRestartMarkers = new Set<string>(); |
| 214 | const fileSystemLayer = FileSystem.layerNoop({ | 214 | const layerFileSystem = FileSystem.layerNoop({ |
| 215 | readFileString: (path) => | 215 | readFileString: (path) => |
| 216 | path === "/missing/resources/package-type" && options.packageType !== undefined | 216 | path === "/missing/resources/package-type" && options.packageType !== undefined |
| 217 | ? Effect.succeed(options.packageType) | 217 | ? Effect.succeed(options.packageType) |
| ... | @@ -235,12 +235,12 @@ export function makeHarness(options: UpdatesHarnessOptions = {}) { | ... | @@ -235,12 +235,12 @@ export function makeHarness(options: UpdatesHarnessOptions = {}) { |
| 235 | }); | 235 | }); |
| 236 | 236 | ||
| 237 | const layer = DesktopUpdates.layer.pipe( | 237 | const layer = DesktopUpdates.layer.pipe( |
| 238 | Layer.provide(fileSystemLayer), | 238 | Layer.provide(layerFileSystem), |
| 239 | Layer.provideMerge(updaterLayer), | 239 | Layer.provideMerge(layerUpdater), |
| 240 | Layer.provideMerge(windowLayer), | 240 | Layer.provideMerge(layerWindow), |
| 241 | Layer.provideMerge(backendLayer), | 241 | Layer.provideMerge(layerBackend), |
| 242 | Layer.provideMerge(DesktopState.layer), | 242 | Layer.provideMerge(DesktopState.layer), |
| 243 | Layer.provideMerge(settingsLayer), | 243 | Layer.provideMerge(layerSettings), |
| 244 | Layer.provideMerge( | 244 | Layer.provideMerge( |
| 245 | DesktopConfig.layerTest({ | 245 | DesktopConfig.layerTest({ |
| 246 | T3CODE_HOME: `/tmp/t3-desktop-updates-test-${process.pid}`, | 246 | T3CODE_HOME: `/tmp/t3-desktop-updates-test-${process.pid}`, |
| ... | @@ -249,7 +249,7 @@ export function makeHarness(options: UpdatesHarnessOptions = {}) { | ... | @@ -249,7 +249,7 @@ export function makeHarness(options: UpdatesHarnessOptions = {}) { |
| 249 | ...options.env, | 249 | ...options.env, |
| 250 | }), | 250 | }), |
| 251 | ), | 251 | ), |
| 252 | Layer.provideMerge(environmentLayer), | 252 | Layer.provideMerge(layerEnvironment), |
| 253 | Layer.provideMerge(NodeServices.layer), | 253 | Layer.provideMerge(NodeServices.layer), |
| 254 | ); | 254 | ); |
| 255 | 255 |
apps/desktop/src/window/DesktopApplicationMenu.test.ts+10-10| ... | @@ -27,7 +27,7 @@ const environmentInput = { | ... | @@ -27,7 +27,7 @@ const environmentInput = { |
| 27 | runningUnderArm64Translation: false, | 27 | runningUnderArm64Translation: false, |
| 28 | } satisfies DesktopEnvironment.MakeDesktopEnvironmentInput; | 28 | } satisfies DesktopEnvironment.MakeDesktopEnvironmentInput; |
| 29 | 29 | ||
| 30 | const electronAppLayer = Layer.succeed(ElectronApp.ElectronApp, { | 30 | const layerElectronApp = Layer.succeed(ElectronApp.ElectronApp, { |
| 31 | metadata: Effect.die("unexpected metadata read"), | 31 | metadata: Effect.die("unexpected metadata read"), |
| 32 | name: Effect.succeed("T3 Code"), | 32 | name: Effect.succeed("T3 Code"), |
| 33 | systemLocale: Effect.succeed("en-US"), | 33 | systemLocale: Effect.succeed("en-US"), |
| ... | @@ -49,14 +49,14 @@ const electronAppLayer = Layer.succeed(ElectronApp.ElectronApp, { | ... | @@ -49,14 +49,14 @@ const electronAppLayer = Layer.succeed(ElectronApp.ElectronApp, { |
| 49 | on: () => Effect.void, | 49 | on: () => Effect.void, |
| 50 | } satisfies ElectronApp.ElectronApp["Service"]); | 50 | } satisfies ElectronApp.ElectronApp["Service"]); |
| 51 | 51 | ||
| 52 | const electronDialogLayer = Layer.succeed(ElectronDialog.ElectronDialog, { | 52 | const layerElectronDialog = Layer.succeed(ElectronDialog.ElectronDialog, { |
| 53 | pickFolder: () => Effect.succeedNone, | 53 | pickFolder: () => Effect.succeedNone, |
| 54 | pickFiles: () => Effect.succeed([]), | 54 | pickFiles: () => Effect.succeed([]), |
| 55 | showMessageBox: () => Effect.succeed({ response: 0, checkboxChecked: false }), | 55 | showMessageBox: () => Effect.succeed({ response: 0, checkboxChecked: false }), |
| 56 | showErrorBox: () => Effect.void, | 56 | showErrorBox: () => Effect.void, |
| 57 | } satisfies ElectronDialog.ElectronDialog["Service"]); | 57 | } satisfies ElectronDialog.ElectronDialog["Service"]); |
| 58 | 58 | ||
| 59 | const desktopUpdatesLayer = Layer.succeed(DesktopUpdates.DesktopUpdates, { | 59 | const layerDesktopUpdates = Layer.succeed(DesktopUpdates.DesktopUpdates, { |
| 60 | getState: Effect.die("unexpected getState"), | 60 | getState: Effect.die("unexpected getState"), |
| 61 | isActionActive: Effect.succeed(false), | 61 | isActionActive: Effect.succeed(false), |
| 62 | isInstallActive: Effect.succeed(false), | 62 | isInstallActive: Effect.succeed(false), |
| ... | @@ -71,7 +71,7 @@ const desktopUpdatesLayer = Layer.succeed(DesktopUpdates.DesktopUpdates, { | ... | @@ -71,7 +71,7 @@ const desktopUpdatesLayer = Layer.succeed(DesktopUpdates.DesktopUpdates, { |
| 71 | installPrepared: () => Effect.die("unexpected installPrepared"), | 71 | installPrepared: () => Effect.die("unexpected installPrepared"), |
| 72 | } satisfies DesktopUpdates.DesktopUpdates["Service"]); | 72 | } satisfies DesktopUpdates.DesktopUpdates["Service"]); |
| 73 | 73 | ||
| 74 | const makeDesktopWindowLayer = (selectedAction: Deferred.Deferred<string>) => | 74 | const layerDesktopWindow = (selectedAction: Deferred.Deferred<string>) => |
| 75 | Layer.succeed(DesktopWindow.DesktopWindow, { | 75 | Layer.succeed(DesktopWindow.DesktopWindow, { |
| 76 | createMain: Effect.die("unexpected createMain"), | 76 | createMain: Effect.die("unexpected createMain"), |
| 77 | ensureMain: Effect.die("unexpected ensureMain"), | 77 | ensureMain: Effect.die("unexpected ensureMain"), |
| ... | @@ -90,7 +90,7 @@ const makeDesktopWindowLayer = (selectedAction: Deferred.Deferred<string>) => | ... | @@ -90,7 +90,7 @@ const makeDesktopWindowLayer = (selectedAction: Deferred.Deferred<string>) => |
| 90 | syncAppearance: Effect.void, | 90 | syncAppearance: Effect.void, |
| 91 | } satisfies DesktopWindow.DesktopWindow["Service"]); | 91 | } satisfies DesktopWindow.DesktopWindow["Service"]); |
| 92 | 92 | ||
| 93 | const makeElectronMenuLayer = ( | 93 | const layerElectronMenu = ( |
| 94 | applicationMenuTemplate: Deferred.Deferred<readonly Electron.MenuItemConstructorOptions[]>, | 94 | applicationMenuTemplate: Deferred.Deferred<readonly Electron.MenuItemConstructorOptions[]>, |
| 95 | ) => | 95 | ) => |
| 96 | Layer.succeed(ElectronMenu.ElectronMenu, { | 96 | Layer.succeed(ElectronMenu.ElectronMenu, { |
| ... | @@ -110,11 +110,11 @@ const configureMenu = ( | ... | @@ -110,11 +110,11 @@ const configureMenu = ( |
| 110 | }).pipe( | 110 | }).pipe( |
| 111 | Effect.provide( | 111 | Effect.provide( |
| 112 | DesktopApplicationMenu.layer.pipe( | 112 | DesktopApplicationMenu.layer.pipe( |
| 113 | Layer.provideMerge(makeElectronMenuLayer(applicationMenuTemplate)), | 113 | Layer.provideMerge(layerElectronMenu(applicationMenuTemplate)), |
| 114 | Layer.provideMerge(makeDesktopWindowLayer(selectedAction)), | 114 | Layer.provideMerge(layerDesktopWindow(selectedAction)), |
| 115 | Layer.provideMerge(desktopUpdatesLayer), | 115 | Layer.provideMerge(layerDesktopUpdates), |
| 116 | Layer.provideMerge(electronDialogLayer), | 116 | Layer.provideMerge(layerElectronDialog), |
| 117 | Layer.provideMerge(electronAppLayer), | 117 | Layer.provideMerge(layerElectronApp), |
| 118 | Layer.provideMerge( | 118 | Layer.provideMerge( |
| 119 | DesktopEnvironment.layer(environmentInput).pipe( | 119 | DesktopEnvironment.layer(environmentInput).pipe( |
| 120 | Layer.provide(Layer.mergeAll(NodeServices.layer, DesktopConfig.layerTest({}))), | 120 | Layer.provide(Layer.mergeAll(NodeServices.layer, DesktopConfig.layerTest({}))), |
apps/desktop/src/window/DesktopWindow.test.ts+51-51| ... | @@ -151,15 +151,15 @@ function makeFakeBrowserWindow() { | ... | @@ -151,15 +151,15 @@ function makeFakeBrowserWindow() { |
| 151 | }; | 151 | }; |
| 152 | } | 152 | } |
| 153 | 153 | ||
| 154 | const desktopClientSettingsLayer = Layer.mock(DesktopClientSettings.DesktopClientSettings)({ | 154 | const layerDesktopClientSettings = Layer.mock(DesktopClientSettings.DesktopClientSettings)({ |
| 155 | get: Effect.succeedNone, | 155 | get: Effect.succeedNone, |
| 156 | }); | 156 | }); |
| 157 | 157 | ||
| 158 | const electronAppLayer = Layer.mock(ElectronApp.ElectronApp)({ | 158 | const layerElectronApp = Layer.mock(ElectronApp.ElectronApp)({ |
| 159 | quit: Effect.void, | 159 | quit: Effect.void, |
| 160 | }); | 160 | }); |
| 161 | 161 | ||
| 162 | const desktopAssetsLayer = Layer.succeed(DesktopAssets.DesktopAssets, { | 162 | const layerDesktopAssets = Layer.succeed(DesktopAssets.DesktopAssets, { |
| 163 | iconPaths: Effect.succeed({ | 163 | iconPaths: Effect.succeed({ |
| 164 | ico: Option.none<string>(), | 164 | ico: Option.none<string>(), |
| 165 | icns: Option.none<string>(), | 165 | icns: Option.none<string>(), |
| ... | @@ -168,7 +168,7 @@ const desktopAssetsLayer = Layer.succeed(DesktopAssets.DesktopAssets, { | ... | @@ -168,7 +168,7 @@ const desktopAssetsLayer = Layer.succeed(DesktopAssets.DesktopAssets, { |
| 168 | resolveResourcePath: () => Effect.succeed(Option.none<string>()), | 168 | resolveResourcePath: () => Effect.succeed(Option.none<string>()), |
| 169 | } satisfies DesktopAssets.DesktopAssets["Service"]); | 169 | } satisfies DesktopAssets.DesktopAssets["Service"]); |
| 170 | 170 | ||
| 171 | const desktopServerExposureLayer = Layer.succeed(DesktopServerExposure.DesktopServerExposure, { | 171 | const layerDesktopServerExposure = Layer.succeed(DesktopServerExposure.DesktopServerExposure, { |
| 172 | getState: Effect.die("unexpected getState"), | 172 | getState: Effect.die("unexpected getState"), |
| 173 | backendConfig: Effect.succeed({ | 173 | backendConfig: Effect.succeed({ |
| 174 | port: 3773, | 174 | port: 3773, |
| ... | @@ -183,19 +183,19 @@ const desktopServerExposureLayer = Layer.succeed(DesktopServerExposure.DesktopSe | ... | @@ -183,19 +183,19 @@ const desktopServerExposureLayer = Layer.succeed(DesktopServerExposure.DesktopSe |
| 183 | getAdvertisedEndpoints: Effect.die("unexpected getAdvertisedEndpoints"), | 183 | getAdvertisedEndpoints: Effect.die("unexpected getAdvertisedEndpoints"), |
| 184 | } satisfies DesktopServerExposure.DesktopServerExposure["Service"]); | 184 | } satisfies DesktopServerExposure.DesktopServerExposure["Service"]); |
| 185 | 185 | ||
| 186 | const electronMenuLayer = Layer.succeed(ElectronMenu.ElectronMenu, { | 186 | const layerElectronMenu = Layer.succeed(ElectronMenu.ElectronMenu, { |
| 187 | setApplicationMenu: () => Effect.void, | 187 | setApplicationMenu: () => Effect.void, |
| 188 | popupTemplate: () => Effect.void, | 188 | popupTemplate: () => Effect.void, |
| 189 | showContextMenu: () => Effect.succeedNone, | 189 | showContextMenu: () => Effect.succeedNone, |
| 190 | } satisfies ElectronMenu.ElectronMenu["Service"]); | 190 | } satisfies ElectronMenu.ElectronMenu["Service"]); |
| 191 | 191 | ||
| 192 | const electronThemeLayer = Layer.succeed(ElectronTheme.ElectronTheme, { | 192 | const layerElectronTheme = Layer.succeed(ElectronTheme.ElectronTheme, { |
| 193 | shouldUseDarkColors: Effect.succeed(false), | 193 | shouldUseDarkColors: Effect.succeed(false), |
| 194 | setSource: () => Effect.void, | 194 | setSource: () => Effect.void, |
| 195 | onUpdated: () => Effect.void, | 195 | onUpdated: () => Effect.void, |
| 196 | } satisfies ElectronTheme.ElectronTheme["Service"]); | 196 | } satisfies ElectronTheme.ElectronTheme["Service"]); |
| 197 | 197 | ||
| 198 | const desktopEnvironmentLayer = DesktopEnvironment.layer(environmentInput).pipe( | 198 | const layerDesktopEnvironment = DesktopEnvironment.layer(environmentInput).pipe( |
| 199 | Layer.provide( | 199 | Layer.provide( |
| 200 | Layer.mergeAll( | 200 | Layer.mergeAll( |
| 201 | NodeServices.layer, | 201 | NodeServices.layer, |
| ... | @@ -211,7 +211,7 @@ const desktopWindowBoundsEquivalence = Schema.toEquivalence( | ... | @@ -211,7 +211,7 @@ const desktopWindowBoundsEquivalence = Schema.toEquivalence( |
| 211 | DesktopAppSettings.DesktopWindowBoundsSchema, | 211 | DesktopAppSettings.DesktopWindowBoundsSchema, |
| 212 | ); | 212 | ); |
| 213 | 213 | ||
| 214 | function makeTestLayer(input: { | 214 | function layerTest(input: { |
| 215 | readonly window: Electron.BrowserWindow; | 215 | readonly window: Electron.BrowserWindow; |
| 216 | readonly createCount: Ref.Ref<number>; | 216 | readonly createCount: Ref.Ref<number>; |
| 217 | readonly mainWindow: Ref.Ref<Option.Option<Electron.BrowserWindow>>; | 217 | readonly mainWindow: Ref.Ref<Option.Option<Electron.BrowserWindow>>; |
| ... | @@ -229,7 +229,7 @@ function makeTestLayer(input: { | ... | @@ -229,7 +229,7 @@ function makeTestLayer(input: { |
| 229 | readonly onReveal?: (window: Electron.BrowserWindow) => void; | 229 | readonly onReveal?: (window: Electron.BrowserWindow) => void; |
| 230 | }) { | 230 | }) { |
| 231 | let desktopSettings = input.desktopSettings ?? DesktopAppSettings.DEFAULT_DESKTOP_SETTINGS; | 231 | let desktopSettings = input.desktopSettings ?? DesktopAppSettings.DEFAULT_DESKTOP_SETTINGS; |
| 232 | const desktopAppSettingsLayer = Layer.succeed(DesktopAppSettings.DesktopAppSettings, { | 232 | const layerDesktopAppSettings = Layer.succeed(DesktopAppSettings.DesktopAppSettings, { |
| 233 | get: Effect.sync(() => desktopSettings), | 233 | get: Effect.sync(() => desktopSettings), |
| 234 | load: Effect.sync(() => desktopSettings), | 234 | load: Effect.sync(() => desktopSettings), |
| 235 | setMainWindowBounds: (bounds, isMaximized) => | 235 | setMainWindowBounds: (bounds, isMaximized) => |
| ... | @@ -263,7 +263,7 @@ function makeTestLayer(input: { | ... | @@ -263,7 +263,7 @@ function makeTestLayer(input: { |
| 263 | applyWslWindowsFallbackInMemory: Effect.die("unexpected WSL Windows fallback"), | 263 | applyWslWindowsFallbackInMemory: Effect.die("unexpected WSL Windows fallback"), |
| 264 | } satisfies DesktopAppSettings.DesktopAppSettings["Service"]); | 264 | } satisfies DesktopAppSettings.DesktopAppSettings["Service"]); |
| 265 | 265 | ||
| 266 | const electronWindowLayer = Layer.succeed(ElectronWindow.ElectronWindow, { | 266 | const layerElectronWindow = Layer.succeed(ElectronWindow.ElectronWindow, { |
| 267 | create: (options) => | 267 | create: (options) => |
| 268 | Effect.sync(() => { | 268 | Effect.sync(() => { |
| 269 | input.createdWindowOptions?.push(options); | 269 | input.createdWindowOptions?.push(options); |
| ... | @@ -286,18 +286,18 @@ function makeTestLayer(input: { | ... | @@ -286,18 +286,18 @@ function makeTestLayer(input: { |
| 286 | return DesktopWindow.layer.pipe( | 286 | return DesktopWindow.layer.pipe( |
| 287 | Layer.provide( | 287 | Layer.provide( |
| 288 | Layer.mergeAll( | 288 | Layer.mergeAll( |
| 289 | desktopAssetsLayer, | 289 | layerDesktopAssets, |
| 290 | Layer.succeed(DesktopRendererHistory.DesktopRendererHistory, { | 290 | Layer.succeed(DesktopRendererHistory.DesktopRendererHistory, { |
| 291 | register: () => Effect.void, | 291 | register: () => Effect.void, |
| 292 | recordMetrics: () => Effect.void, | 292 | recordMetrics: () => Effect.void, |
| 293 | shutdown: Effect.void, | 293 | shutdown: Effect.void, |
| 294 | }), | 294 | }), |
| 295 | desktopEnvironmentLayer, | 295 | layerDesktopEnvironment, |
| 296 | desktopAppSettingsLayer, | 296 | layerDesktopAppSettings, |
| 297 | desktopClientSettingsLayer, | 297 | layerDesktopClientSettings, |
| 298 | desktopServerExposureLayer, | 298 | layerDesktopServerExposure, |
| 299 | DesktopState.layer, | 299 | DesktopState.layer, |
| 300 | electronAppLayer, | 300 | layerElectronApp, |
| 301 | Layer.succeed(ElectronMenu.ElectronMenu, { | 301 | Layer.succeed(ElectronMenu.ElectronMenu, { |
| 302 | setApplicationMenu: () => Effect.void, | 302 | setApplicationMenu: () => Effect.void, |
| 303 | showContextMenu: () => Effect.succeedNone, | 303 | showContextMenu: () => Effect.succeedNone, |
| ... | @@ -315,8 +315,8 @@ function makeTestLayer(input: { | ... | @@ -315,8 +315,8 @@ function makeTestLayer(input: { |
| 315 | input.copiedTexts?.push(text); | 315 | input.copiedTexts?.push(text); |
| 316 | }), | 316 | }), |
| 317 | } satisfies ElectronShell.ElectronShell["Service"]), | 317 | } satisfies ElectronShell.ElectronShell["Service"]), |
| 318 | electronThemeLayer, | 318 | layerElectronTheme, |
| 319 | electronWindowLayer, | 319 | layerElectronWindow, |
| 320 | Layer.mock(PreviewManager.PreviewManager)({ | 320 | Layer.mock(PreviewManager.PreviewManager)({ |
| 321 | getBrowserSession: () => Effect.succeed({} as Electron.Session), | 321 | getBrowserSession: () => Effect.succeed({} as Electron.Session), |
| 322 | setMainWindow: () => Effect.void, | 322 | setMainWindow: () => Effect.void, |
| ... | @@ -406,24 +406,24 @@ const makeSplashScenario = (createOutcomes: readonly (Electron.BrowserWindow | n | ... | @@ -406,24 +406,24 @@ const makeSplashScenario = (createOutcomes: readonly (Electron.BrowserWindow | n |
| 406 | const layer = DesktopWindow.layer.pipe( | 406 | const layer = DesktopWindow.layer.pipe( |
| 407 | Layer.provide( | 407 | Layer.provide( |
| 408 | Layer.mergeAll( | 408 | Layer.mergeAll( |
| 409 | desktopAssetsLayer, | 409 | layerDesktopAssets, |
| 410 | Layer.succeed(DesktopRendererHistory.DesktopRendererHistory, { | 410 | Layer.succeed(DesktopRendererHistory.DesktopRendererHistory, { |
| 411 | register: () => Effect.void, | 411 | register: () => Effect.void, |
| 412 | recordMetrics: () => Effect.void, | 412 | recordMetrics: () => Effect.void, |
| 413 | shutdown: Effect.void, | 413 | shutdown: Effect.void, |
| 414 | }), | 414 | }), |
| 415 | desktopEnvironmentLayer, | 415 | layerDesktopEnvironment, |
| 416 | DesktopAppSettings.layerTest(), | 416 | DesktopAppSettings.layerTest(), |
| 417 | desktopClientSettingsLayer, | 417 | layerDesktopClientSettings, |
| 418 | desktopServerExposureLayer, | 418 | layerDesktopServerExposure, |
| 419 | electronAppLayer, | 419 | layerElectronApp, |
| 420 | electronMenuLayer, | 420 | layerElectronMenu, |
| 421 | Layer.succeed(ElectronShell.ElectronShell, { | 421 | Layer.succeed(ElectronShell.ElectronShell, { |
| 422 | openExternal: () => Effect.succeed(true), | 422 | openExternal: () => Effect.succeed(true), |
| 423 | openSystemSettings: () => Effect.succeed(true), | 423 | openSystemSettings: () => Effect.succeed(true), |
| 424 | copyText: () => Effect.void, | 424 | copyText: () => Effect.void, |
| 425 | } satisfies ElectronShell.ElectronShell["Service"]), | 425 | } satisfies ElectronShell.ElectronShell["Service"]), |
| 426 | electronThemeLayer, | 426 | layerElectronTheme, |
| 427 | Layer.succeed(ElectronWindow.ElectronWindow, electronWindowShape), | 427 | Layer.succeed(ElectronWindow.ElectronWindow, electronWindowShape), |
| 428 | Layer.mock(PreviewManager.PreviewManager)({ | 428 | Layer.mock(PreviewManager.PreviewManager)({ |
| 429 | getBrowserSession: () => Effect.succeed({} as Electron.Session), | 429 | getBrowserSession: () => Effect.succeed({} as Electron.Session), |
| ... | @@ -466,7 +466,7 @@ describe("DesktopWindow", () => { | ... | @@ -466,7 +466,7 @@ describe("DesktopWindow", () => { |
| 466 | focusedContents: unknown; | 466 | focusedContents: unknown; |
| 467 | }>(); | 467 | }>(); |
| 468 | const copiedTexts: string[] = []; | 468 | const copiedTexts: string[] = []; |
| 469 | const layer = makeTestLayer({ | 469 | const layer = layerTest({ |
| 470 | window: host.window, | 470 | window: host.window, |
| 471 | createCount: yield* Ref.make(0), | 471 | createCount: yield* Ref.make(0), |
| 472 | mainWindow: yield* Ref.make<Option.Option<Electron.BrowserWindow>>(Option.none()), | 472 | mainWindow: yield* Ref.make<Option.Option<Electron.BrowserWindow>>(Option.none()), |
| ... | @@ -619,7 +619,7 @@ describe("DesktopWindow", () => { | ... | @@ -619,7 +619,7 @@ describe("DesktopWindow", () => { |
| 619 | const createCount = yield* Ref.make(0); | 619 | const createCount = yield* Ref.make(0); |
| 620 | const mainWindow = yield* Ref.make<Option.Option<Electron.BrowserWindow>>(Option.none()); | 620 | const mainWindow = yield* Ref.make<Option.Option<Electron.BrowserWindow>>(Option.none()); |
| 621 | const createdWindowOptions: Electron.BrowserWindowConstructorOptions[] = []; | 621 | const createdWindowOptions: Electron.BrowserWindowConstructorOptions[] = []; |
| 622 | const layer = makeTestLayer({ | 622 | const layer = layerTest({ |
| 623 | window: fakeWindow.window, | 623 | window: fakeWindow.window, |
| 624 | createCount, | 624 | createCount, |
| 625 | mainWindow, | 625 | mainWindow, |
| ... | @@ -653,7 +653,7 @@ describe("DesktopWindow", () => { | ... | @@ -653,7 +653,7 @@ describe("DesktopWindow", () => { |
| 653 | const fakeWindow = makeFakeBrowserWindow(); | 653 | const fakeWindow = makeFakeBrowserWindow(); |
| 654 | const createCount = yield* Ref.make(0); | 654 | const createCount = yield* Ref.make(0); |
| 655 | const mainWindow = yield* Ref.make<Option.Option<Electron.BrowserWindow>>(Option.none()); | 655 | const mainWindow = yield* Ref.make<Option.Option<Electron.BrowserWindow>>(Option.none()); |
| 656 | const layer = makeTestLayer({ | 656 | const layer = layerTest({ |
| 657 | window: fakeWindow.window, | 657 | window: fakeWindow.window, |
| 658 | createCount, | 658 | createCount, |
| 659 | mainWindow, | 659 | mainWindow, |
| ... | @@ -682,7 +682,7 @@ describe("DesktopWindow", () => { | ... | @@ -682,7 +682,7 @@ describe("DesktopWindow", () => { |
| 682 | const fakeWindow = makeFakeBrowserWindow(); | 682 | const fakeWindow = makeFakeBrowserWindow(); |
| 683 | const createCount = yield* Ref.make(0); | 683 | const createCount = yield* Ref.make(0); |
| 684 | const mainWindow = yield* Ref.make<Option.Option<Electron.BrowserWindow>>(Option.none()); | 684 | const mainWindow = yield* Ref.make<Option.Option<Electron.BrowserWindow>>(Option.none()); |
| 685 | const layer = makeTestLayer({ | 685 | const layer = layerTest({ |
| 686 | window: fakeWindow.window, | 686 | window: fakeWindow.window, |
| 687 | createCount, | 687 | createCount, |
| 688 | mainWindow, | 688 | mainWindow, |
| ... | @@ -728,7 +728,7 @@ describe("DesktopWindow", () => { | ... | @@ -728,7 +728,7 @@ describe("DesktopWindow", () => { |
| 728 | const send = vi.spyOn(fakeWindow.window.webContents, "send"); | 728 | const send = vi.spyOn(fakeWindow.window.webContents, "send"); |
| 729 | const createCount = yield* Ref.make(0); | 729 | const createCount = yield* Ref.make(0); |
| 730 | const mainWindow = yield* Ref.make<Option.Option<Electron.BrowserWindow>>(Option.none()); | 730 | const mainWindow = yield* Ref.make<Option.Option<Electron.BrowserWindow>>(Option.none()); |
| 731 | const layer = makeTestLayer({ window: fakeWindow.window, createCount, mainWindow }); | 731 | const layer = layerTest({ window: fakeWindow.window, createCount, mainWindow }); |
| 732 | 732 | ||
| 733 | yield* Effect.gen(function* () { | 733 | yield* Effect.gen(function* () { |
| 734 | const desktopWindow = yield* DesktopWindow.DesktopWindow; | 734 | const desktopWindow = yield* DesktopWindow.DesktopWindow; |
| ... | @@ -755,7 +755,7 @@ describe("DesktopWindow", () => { | ... | @@ -755,7 +755,7 @@ describe("DesktopWindow", () => { |
| 755 | const createCount = yield* Ref.make(0); | 755 | const createCount = yield* Ref.make(0); |
| 756 | const mainWindow = yield* Ref.make<Option.Option<Electron.BrowserWindow>>(Option.none()); | 756 | const mainWindow = yield* Ref.make<Option.Option<Electron.BrowserWindow>>(Option.none()); |
| 757 | const previewZoomReapplies: number[] = []; | 757 | const previewZoomReapplies: number[] = []; |
| 758 | const layer = makeTestLayer({ | 758 | const layer = layerTest({ |
| 759 | window: fakeWindow.window, | 759 | window: fakeWindow.window, |
| 760 | createCount, | 760 | createCount, |
| 761 | mainWindow, | 761 | mainWindow, |
| ... | @@ -787,7 +787,7 @@ describe("DesktopWindow", () => { | ... | @@ -787,7 +787,7 @@ describe("DesktopWindow", () => { |
| 787 | const fakeWindow = makeFakeBrowserWindow(); | 787 | const fakeWindow = makeFakeBrowserWindow(); |
| 788 | const createCount = yield* Ref.make(0); | 788 | const createCount = yield* Ref.make(0); |
| 789 | const mainWindow = yield* Ref.make<Option.Option<Electron.BrowserWindow>>(Option.none()); | 789 | const mainWindow = yield* Ref.make<Option.Option<Electron.BrowserWindow>>(Option.none()); |
| 790 | const layer = makeTestLayer({ window: fakeWindow.window, createCount, mainWindow }); | 790 | const layer = layerTest({ window: fakeWindow.window, createCount, mainWindow }); |
| 791 | 791 | ||
| 792 | yield* Effect.gen(function* () { | 792 | yield* Effect.gen(function* () { |
| 793 | const desktopWindow = yield* DesktopWindow.DesktopWindow; | 793 | const desktopWindow = yield* DesktopWindow.DesktopWindow; |
| ... | @@ -821,7 +821,7 @@ describe("DesktopWindow", () => { | ... | @@ -821,7 +821,7 @@ describe("DesktopWindow", () => { |
| 821 | const createCount = yield* Ref.make(0); | 821 | const createCount = yield* Ref.make(0); |
| 822 | const mainWindow = yield* Ref.make<Option.Option<Electron.BrowserWindow>>(Option.none()); | 822 | const mainWindow = yield* Ref.make<Option.Option<Electron.BrowserWindow>>(Option.none()); |
| 823 | const createdWindowOptions: Electron.BrowserWindowConstructorOptions[] = []; | 823 | const createdWindowOptions: Electron.BrowserWindowConstructorOptions[] = []; |
| 824 | const layer = makeTestLayer({ | 824 | const layer = layerTest({ |
| 825 | window: fakeWindow.window, | 825 | window: fakeWindow.window, |
| 826 | createCount, | 826 | createCount, |
| 827 | mainWindow, | 827 | mainWindow, |
| ... | @@ -849,7 +849,7 @@ describe("DesktopWindow", () => { | ... | @@ -849,7 +849,7 @@ describe("DesktopWindow", () => { |
| 849 | const fakeWindow = makeFakeBrowserWindow(); | 849 | const fakeWindow = makeFakeBrowserWindow(); |
| 850 | const createCount = yield* Ref.make(0); | 850 | const createCount = yield* Ref.make(0); |
| 851 | const mainWindow = yield* Ref.make<Option.Option<Electron.BrowserWindow>>(Option.none()); | 851 | const mainWindow = yield* Ref.make<Option.Option<Electron.BrowserWindow>>(Option.none()); |
| 852 | const layer = makeTestLayer({ | 852 | const layer = layerTest({ |
| 853 | window: fakeWindow.window, | 853 | window: fakeWindow.window, |
| 854 | createCount, | 854 | createCount, |
| 855 | mainWindow, | 855 | mainWindow, |
| ... | @@ -883,7 +883,7 @@ describe("DesktopWindow", () => { | ... | @@ -883,7 +883,7 @@ describe("DesktopWindow", () => { |
| 883 | const fakeWindow = makeFakeBrowserWindow(); | 883 | const fakeWindow = makeFakeBrowserWindow(); |
| 884 | const createCount = yield* Ref.make(0); | 884 | const createCount = yield* Ref.make(0); |
| 885 | const mainWindow = yield* Ref.make<Option.Option<Electron.BrowserWindow>>(Option.none()); | 885 | const mainWindow = yield* Ref.make<Option.Option<Electron.BrowserWindow>>(Option.none()); |
| 886 | const layer = makeTestLayer({ | 886 | const layer = layerTest({ |
| 887 | window: fakeWindow.window, | 887 | window: fakeWindow.window, |
| 888 | createCount, | 888 | createCount, |
| 889 | mainWindow, | 889 | mainWindow, |
| ... | @@ -910,7 +910,7 @@ describe("DesktopWindow", () => { | ... | @@ -910,7 +910,7 @@ describe("DesktopWindow", () => { |
| 910 | const createCount = yield* Ref.make(0); | 910 | const createCount = yield* Ref.make(0); |
| 911 | const mainWindow = yield* Ref.make<Option.Option<Electron.BrowserWindow>>(Option.none()); | 911 | const mainWindow = yield* Ref.make<Option.Option<Electron.BrowserWindow>>(Option.none()); |
| 912 | const mainWindowBoundsUpdates: DesktopAppSettings.DesktopWindowBounds[] = []; | 912 | const mainWindowBoundsUpdates: DesktopAppSettings.DesktopWindowBounds[] = []; |
| 913 | const layer = makeTestLayer({ | 913 | const layer = layerTest({ |
| 914 | window: fakeWindow.window, | 914 | window: fakeWindow.window, |
| 915 | createCount, | 915 | createCount, |
| 916 | mainWindow, | 916 | mainWindow, |
| ... | @@ -953,7 +953,7 @@ describe("DesktopWindow", () => { | ... | @@ -953,7 +953,7 @@ describe("DesktopWindow", () => { |
| 953 | const mainWindow = yield* Ref.make<Option.Option<Electron.BrowserWindow>>(Option.none()); | 953 | const mainWindow = yield* Ref.make<Option.Option<Electron.BrowserWindow>>(Option.none()); |
| 954 | const mainWindowBoundsUpdates: DesktopAppSettings.DesktopWindowBounds[] = []; | 954 | const mainWindowBoundsUpdates: DesktopAppSettings.DesktopWindowBounds[] = []; |
| 955 | const mainWindowMaximizedUpdates: boolean[] = []; | 955 | const mainWindowMaximizedUpdates: boolean[] = []; |
| 956 | const layer = makeTestLayer({ | 956 | const layer = layerTest({ |
| 957 | window: fakeWindow.window, | 957 | window: fakeWindow.window, |
| 958 | createCount, | 958 | createCount, |
| 959 | mainWindow, | 959 | mainWindow, |
| ... | @@ -987,7 +987,7 @@ describe("DesktopWindow", () => { | ... | @@ -987,7 +987,7 @@ describe("DesktopWindow", () => { |
| 987 | const mainWindow = yield* Ref.make<Option.Option<Electron.BrowserWindow>>(Option.none()); | 987 | const mainWindow = yield* Ref.make<Option.Option<Electron.BrowserWindow>>(Option.none()); |
| 988 | const mainWindowBoundsUpdates: DesktopAppSettings.DesktopWindowBounds[] = []; | 988 | const mainWindowBoundsUpdates: DesktopAppSettings.DesktopWindowBounds[] = []; |
| 989 | const mainWindowMaximizedUpdates: boolean[] = []; | 989 | const mainWindowMaximizedUpdates: boolean[] = []; |
| 990 | const layer = makeTestLayer({ | 990 | const layer = layerTest({ |
| 991 | window: fakeWindow.window, | 991 | window: fakeWindow.window, |
| 992 | createCount, | 992 | createCount, |
| 993 | mainWindow, | 993 | mainWindow, |
| ... | @@ -1024,7 +1024,7 @@ describe("DesktopWindow", () => { | ... | @@ -1024,7 +1024,7 @@ describe("DesktopWindow", () => { |
| 1024 | const createCount = yield* Ref.make(0); | 1024 | const createCount = yield* Ref.make(0); |
| 1025 | const mainWindow = yield* Ref.make<Option.Option<Electron.BrowserWindow>>(Option.none()); | 1025 | const mainWindow = yield* Ref.make<Option.Option<Electron.BrowserWindow>>(Option.none()); |
| 1026 | const mainWindowBoundsUpdates: DesktopAppSettings.DesktopWindowBounds[] = []; | 1026 | const mainWindowBoundsUpdates: DesktopAppSettings.DesktopWindowBounds[] = []; |
| 1027 | const layer = makeTestLayer({ | 1027 | const layer = layerTest({ |
| 1028 | window: fakeWindow.window, | 1028 | window: fakeWindow.window, |
| 1029 | createCount, | 1029 | createCount, |
| 1030 | mainWindow, | 1030 | mainWindow, |
| ... | @@ -1054,7 +1054,7 @@ describe("DesktopWindow", () => { | ... | @@ -1054,7 +1054,7 @@ describe("DesktopWindow", () => { |
| 1054 | const createCount = yield* Ref.make(0); | 1054 | const createCount = yield* Ref.make(0); |
| 1055 | const mainWindow = yield* Ref.make<Option.Option<Electron.BrowserWindow>>(Option.none()); | 1055 | const mainWindow = yield* Ref.make<Option.Option<Electron.BrowserWindow>>(Option.none()); |
| 1056 | const mainWindowBoundsUpdates: DesktopAppSettings.DesktopWindowBounds[] = []; | 1056 | const mainWindowBoundsUpdates: DesktopAppSettings.DesktopWindowBounds[] = []; |
| 1057 | const layer = makeTestLayer({ | 1057 | const layer = layerTest({ |
| 1058 | window: fakeWindow.window, | 1058 | window: fakeWindow.window, |
| 1059 | createCount, | 1059 | createCount, |
| 1060 | mainWindow, | 1060 | mainWindow, |
| ... | @@ -1096,7 +1096,7 @@ describe("DesktopWindow", () => { | ... | @@ -1096,7 +1096,7 @@ describe("DesktopWindow", () => { |
| 1096 | const createCount = yield* Ref.make(0); | 1096 | const createCount = yield* Ref.make(0); |
| 1097 | const mainWindow = yield* Ref.make<Option.Option<Electron.BrowserWindow>>(Option.none()); | 1097 | const mainWindow = yield* Ref.make<Option.Option<Electron.BrowserWindow>>(Option.none()); |
| 1098 | const mainWindowBoundsUpdates: DesktopAppSettings.DesktopWindowBounds[] = []; | 1098 | const mainWindowBoundsUpdates: DesktopAppSettings.DesktopWindowBounds[] = []; |
| 1099 | const layer = makeTestLayer({ | 1099 | const layer = layerTest({ |
| 1100 | window: fakeWindow.window, | 1100 | window: fakeWindow.window, |
| 1101 | createCount, | 1101 | createCount, |
| 1102 | mainWindow, | 1102 | mainWindow, |
| ... | @@ -1132,7 +1132,7 @@ describe("DesktopWindow", () => { | ... | @@ -1132,7 +1132,7 @@ describe("DesktopWindow", () => { |
| 1132 | const createCount = yield* Ref.make(0); | 1132 | const createCount = yield* Ref.make(0); |
| 1133 | const mainWindow = yield* Ref.make<Option.Option<Electron.BrowserWindow>>(Option.none()); | 1133 | const mainWindow = yield* Ref.make<Option.Option<Electron.BrowserWindow>>(Option.none()); |
| 1134 | const mainWindowBoundsUpdates: DesktopAppSettings.DesktopWindowBounds[] = []; | 1134 | const mainWindowBoundsUpdates: DesktopAppSettings.DesktopWindowBounds[] = []; |
| 1135 | const layer = makeTestLayer({ | 1135 | const layer = layerTest({ |
| 1136 | window: fakeWindow.window, | 1136 | window: fakeWindow.window, |
| 1137 | createCount, | 1137 | createCount, |
| 1138 | mainWindow, | 1138 | mainWindow, |
| ... | @@ -1180,7 +1180,7 @@ describe("DesktopWindow", () => { | ... | @@ -1180,7 +1180,7 @@ describe("DesktopWindow", () => { |
| 1180 | const createCount = yield* Ref.make(0); | 1180 | const createCount = yield* Ref.make(0); |
| 1181 | const mainWindow = yield* Ref.make<Option.Option<Electron.BrowserWindow>>(Option.none()); | 1181 | const mainWindow = yield* Ref.make<Option.Option<Electron.BrowserWindow>>(Option.none()); |
| 1182 | const createdWindowOptions: Electron.BrowserWindowConstructorOptions[] = []; | 1182 | const createdWindowOptions: Electron.BrowserWindowConstructorOptions[] = []; |
| 1183 | const layer = makeTestLayer({ | 1183 | const layer = layerTest({ |
| 1184 | window: fakeWindow.window, | 1184 | window: fakeWindow.window, |
| 1185 | createCount, | 1185 | createCount, |
| 1186 | mainWindow, | 1186 | mainWindow, |
| ... | @@ -1218,7 +1218,7 @@ describe("DesktopWindow", () => { | ... | @@ -1218,7 +1218,7 @@ describe("DesktopWindow", () => { |
| 1218 | const writeStarted = yield* Deferred.make<void>(); | 1218 | const writeStarted = yield* Deferred.make<void>(); |
| 1219 | const allowWrite = yield* Deferred.make<void>(); | 1219 | const allowWrite = yield* Deferred.make<void>(); |
| 1220 | const flushCompleted = yield* Deferred.make<void>(); | 1220 | const flushCompleted = yield* Deferred.make<void>(); |
| 1221 | const layer = makeTestLayer({ | 1221 | const layer = layerTest({ |
| 1222 | window: fakeWindow.window, | 1222 | window: fakeWindow.window, |
| 1223 | createCount, | 1223 | createCount, |
| 1224 | mainWindow, | 1224 | mainWindow, |
| ... | @@ -1270,7 +1270,7 @@ describe("DesktopWindow", () => { | ... | @@ -1270,7 +1270,7 @@ describe("DesktopWindow", () => { |
| 1270 | const fakeWindow = makeFakeBrowserWindow(); | 1270 | const fakeWindow = makeFakeBrowserWindow(); |
| 1271 | const createCount = yield* Ref.make(0); | 1271 | const createCount = yield* Ref.make(0); |
| 1272 | const mainWindow = yield* Ref.make<Option.Option<Electron.BrowserWindow>>(Option.none()); | 1272 | const mainWindow = yield* Ref.make<Option.Option<Electron.BrowserWindow>>(Option.none()); |
| 1273 | const layer = makeTestLayer({ | 1273 | const layer = layerTest({ |
| 1274 | window: fakeWindow.window, | 1274 | window: fakeWindow.window, |
| 1275 | createCount, | 1275 | createCount, |
| 1276 | mainWindow, | 1276 | mainWindow, |
| ... | @@ -1301,7 +1301,7 @@ describe("DesktopWindow", () => { | ... | @@ -1301,7 +1301,7 @@ describe("DesktopWindow", () => { |
| 1301 | const fakeWindow = makeFakeBrowserWindow(); | 1301 | const fakeWindow = makeFakeBrowserWindow(); |
| 1302 | const createCount = yield* Ref.make(0); | 1302 | const createCount = yield* Ref.make(0); |
| 1303 | const mainWindow = yield* Ref.make<Option.Option<Electron.BrowserWindow>>(Option.none()); | 1303 | const mainWindow = yield* Ref.make<Option.Option<Electron.BrowserWindow>>(Option.none()); |
| 1304 | const layer = makeTestLayer({ | 1304 | const layer = layerTest({ |
| 1305 | window: fakeWindow.window, | 1305 | window: fakeWindow.window, |
| 1306 | createCount, | 1306 | createCount, |
| 1307 | mainWindow, | 1307 | mainWindow, |
| ... | @@ -1369,7 +1369,7 @@ describe("DesktopWindow", () => { | ... | @@ -1369,7 +1369,7 @@ describe("DesktopWindow", () => { |
| 1369 | const createCount = yield* Ref.make(0); | 1369 | const createCount = yield* Ref.make(0); |
| 1370 | const mainWindow = yield* Ref.make<Option.Option<Electron.BrowserWindow>>(Option.none()); | 1370 | const mainWindow = yield* Ref.make<Option.Option<Electron.BrowserWindow>>(Option.none()); |
| 1371 | const openedExternalUrls: unknown[] = []; | 1371 | const openedExternalUrls: unknown[] = []; |
| 1372 | const layer = makeTestLayer({ | 1372 | const layer = layerTest({ |
| 1373 | window: fakeWindow.window, | 1373 | window: fakeWindow.window, |
| 1374 | createCount, | 1374 | createCount, |
| 1375 | mainWindow, | 1375 | mainWindow, |
| ... | @@ -1515,7 +1515,7 @@ describe("DesktopWindow", () => { | ... | @@ -1515,7 +1515,7 @@ describe("DesktopWindow", () => { |
| 1515 | }); | 1515 | }); |
| 1516 | const createCount = yield* Ref.make(0); | 1516 | const createCount = yield* Ref.make(0); |
| 1517 | const mainWindow = yield* Ref.make<Option.Option<Electron.BrowserWindow>>(Option.none()); | 1517 | const mainWindow = yield* Ref.make<Option.Option<Electron.BrowserWindow>>(Option.none()); |
| 1518 | const layer = makeTestLayer({ | 1518 | const layer = layerTest({ |
| 1519 | window: fakeWindow.window, | 1519 | window: fakeWindow.window, |
| 1520 | createCount, | 1520 | createCount, |
| 1521 | mainWindow, | 1521 | mainWindow, |
| ... | @@ -1550,7 +1550,7 @@ describe("DesktopWindow", () => { | ... | @@ -1550,7 +1550,7 @@ describe("DesktopWindow", () => { |
| 1550 | const fakeWindow = makeFakeBrowserWindow(); | 1550 | const fakeWindow = makeFakeBrowserWindow(); |
| 1551 | const createCount = yield* Ref.make(0); | 1551 | const createCount = yield* Ref.make(0); |
| 1552 | const mainWindow = yield* Ref.make<Option.Option<Electron.BrowserWindow>>(Option.none()); | 1552 | const mainWindow = yield* Ref.make<Option.Option<Electron.BrowserWindow>>(Option.none()); |
| 1553 | const layer = makeTestLayer({ | 1553 | const layer = layerTest({ |
| 1554 | window: fakeWindow.window, | 1554 | window: fakeWindow.window, |
| 1555 | createCount, | 1555 | createCount, |
| 1556 | mainWindow, | 1556 | mainWindow, |
| ... | @@ -1596,7 +1596,7 @@ describe("DesktopWindow", () => { | ... | @@ -1596,7 +1596,7 @@ describe("DesktopWindow", () => { |
| 1596 | const createCount = yield* Ref.make(0); | 1596 | const createCount = yield* Ref.make(0); |
| 1597 | const mainWindow = yield* Ref.make<Option.Option<Electron.BrowserWindow>>(Option.none()); | 1597 | const mainWindow = yield* Ref.make<Option.Option<Electron.BrowserWindow>>(Option.none()); |
| 1598 | const onReveal = vi.fn(); | 1598 | const onReveal = vi.fn(); |
| 1599 | const layer = makeTestLayer({ window: fakeWindow.window, createCount, mainWindow, onReveal }); | 1599 | const layer = layerTest({ window: fakeWindow.window, createCount, mainWindow, onReveal }); |
| 1600 | 1600 | ||
| 1601 | yield* Effect.gen(function* () { | 1601 | yield* Effect.gen(function* () { |
| 1602 | const desktopWindow = yield* DesktopWindow.DesktopWindow; | 1602 | const desktopWindow = yield* DesktopWindow.DesktopWindow; |
| ... | @@ -1620,7 +1620,7 @@ describe("DesktopWindow", () => { | ... | @@ -1620,7 +1620,7 @@ describe("DesktopWindow", () => { |
| 1620 | Option.some(fakeWindow.window), | 1620 | Option.some(fakeWindow.window), |
| 1621 | ); | 1621 | ); |
| 1622 | const onReveal = vi.fn(); | 1622 | const onReveal = vi.fn(); |
| 1623 | const layer = makeTestLayer({ window: fakeWindow.window, createCount, mainWindow, onReveal }); | 1623 | const layer = layerTest({ window: fakeWindow.window, createCount, mainWindow, onReveal }); |
| 1624 | vi.mocked(fakeWindow.window.webContents.isLoadingMainFrame).mockReturnValue(true); | 1624 | vi.mocked(fakeWindow.window.webContents.isLoadingMainFrame).mockReturnValue(true); |
| 1625 | 1625 | ||
| 1626 | yield* Effect.gen(function* () { | 1626 | yield* Effect.gen(function* () { |
apps/desktop/src/wsl/DesktopWslBackend.test.ts+14-14| ... | @@ -50,7 +50,7 @@ const primarySnapshot: DesktopBackendSnapshot = { | ... | @@ -50,7 +50,7 @@ const primarySnapshot: DesktopBackendSnapshot = { |
| 50 | restartScheduled: false, | 50 | restartScheduled: false, |
| 51 | }; | 51 | }; |
| 52 | 52 | ||
| 53 | const serverExposureLayer = Layer.succeed(DesktopServerExposure.DesktopServerExposure, { | 53 | const layerServerExposure = Layer.succeed(DesktopServerExposure.DesktopServerExposure, { |
| 54 | getState: Effect.die("unexpected getState"), | 54 | getState: Effect.die("unexpected getState"), |
| 55 | backendConfig: Effect.succeed({ | 55 | backendConfig: Effect.succeed({ |
| 56 | port: 3773, | 56 | port: 3773, |
| ... | @@ -65,7 +65,7 @@ const serverExposureLayer = Layer.succeed(DesktopServerExposure.DesktopServerExp | ... | @@ -65,7 +65,7 @@ const serverExposureLayer = Layer.succeed(DesktopServerExposure.DesktopServerExp |
| 65 | getAdvertisedEndpoints: Effect.succeed([]), | 65 | getAdvertisedEndpoints: Effect.succeed([]), |
| 66 | } satisfies DesktopServerExposure.DesktopServerExposure["Service"]); | 66 | } satisfies DesktopServerExposure.DesktopServerExposure["Service"]); |
| 67 | 67 | ||
| 68 | const backendConfigurationLayer = Layer.succeed( | 68 | const layerBackendConfiguration = Layer.succeed( |
| 69 | DesktopBackendConfiguration.DesktopBackendConfiguration, | 69 | DesktopBackendConfiguration.DesktopBackendConfiguration, |
| 70 | { | 70 | { |
| 71 | resolvePrimary: Effect.die("unexpected resolvePrimary"), | 71 | resolvePrimary: Effect.die("unexpected resolvePrimary"), |
| ... | @@ -74,7 +74,7 @@ const backendConfigurationLayer = Layer.succeed( | ... | @@ -74,7 +74,7 @@ const backendConfigurationLayer = Layer.succeed( |
| 74 | } satisfies DesktopBackendConfiguration.DesktopBackendConfiguration["Service"], | 74 | } satisfies DesktopBackendConfiguration.DesktopBackendConfiguration["Service"], |
| 75 | ); | 75 | ); |
| 76 | 76 | ||
| 77 | const netLayer = Layer.succeed(NetService.NetService, { | 77 | const layerNet = Layer.succeed(NetService.NetService, { |
| 78 | canListenOnHost: () => Effect.succeed(true), | 78 | canListenOnHost: () => Effect.succeed(true), |
| 79 | isPortAvailableOnLoopback: () => Effect.succeed(true), | 79 | isPortAvailableOnLoopback: () => Effect.succeed(true), |
| 80 | hasListenerOnHost: () => Effect.succeed(false), | 80 | hasListenerOnHost: () => Effect.succeed(false), |
| ... | @@ -91,9 +91,9 @@ describe("DesktopWslBackend", () => { | ... | @@ -91,9 +91,9 @@ describe("DesktopWslBackend", () => { |
| 91 | Effect.provide( | 91 | Effect.provide( |
| 92 | DesktopWslBackend.layer.pipe( | 92 | DesktopWslBackend.layer.pipe( |
| 93 | Layer.provide(Layer.mock(DesktopBackendPool.DesktopBackendPool, {})), | 93 | Layer.provide(Layer.mock(DesktopBackendPool.DesktopBackendPool, {})), |
| 94 | Layer.provide(backendConfigurationLayer), | 94 | Layer.provide(layerBackendConfiguration), |
| 95 | Layer.provide(serverExposureLayer), | 95 | Layer.provide(layerServerExposure), |
| 96 | Layer.provide(netLayer), | 96 | Layer.provide(layerNet), |
| 97 | Layer.provide(Layer.mock(DesktopWslEnvironment.DesktopWslEnvironment, {})), | 97 | Layer.provide(Layer.mock(DesktopWslEnvironment.DesktopWslEnvironment, {})), |
| 98 | Layer.provide( | 98 | Layer.provide( |
| 99 | DesktopAppSettings.layerTest({ | 99 | DesktopAppSettings.layerTest({ |
| ... | @@ -118,7 +118,7 @@ describe("DesktopWslBackend", () => { | ... | @@ -118,7 +118,7 @@ describe("DesktopWslBackend", () => { |
| 118 | label: "WSL (Ubuntu)", | 118 | label: "WSL (Ubuntu)", |
| 119 | snapshot: primarySnapshot, | 119 | snapshot: primarySnapshot, |
| 120 | }); | 120 | }); |
| 121 | const poolLayer = Layer.succeed(DesktopBackendPool.DesktopBackendPool, { | 121 | const layerPool = Layer.succeed(DesktopBackendPool.DesktopBackendPool, { |
| 122 | get: (id) => | 122 | get: (id) => |
| 123 | Effect.succeed( | 123 | Effect.succeed( |
| 124 | id === DesktopBackendPool.PRIMARY_INSTANCE_ID | 124 | id === DesktopBackendPool.PRIMARY_INSTANCE_ID |
| ... | @@ -160,10 +160,10 @@ describe("DesktopWslBackend", () => { | ... | @@ -160,10 +160,10 @@ describe("DesktopWslBackend", () => { |
| 160 | }).pipe( | 160 | }).pipe( |
| 161 | Effect.provide( | 161 | Effect.provide( |
| 162 | DesktopWslBackend.layer.pipe( | 162 | DesktopWslBackend.layer.pipe( |
| 163 | Layer.provideMerge(poolLayer), | 163 | Layer.provideMerge(layerPool), |
| 164 | Layer.provideMerge(backendConfigurationLayer), | 164 | Layer.provideMerge(layerBackendConfiguration), |
| 165 | Layer.provideMerge(serverExposureLayer), | 165 | Layer.provideMerge(layerServerExposure), |
| 166 | Layer.provideMerge(netLayer), | 166 | Layer.provideMerge(layerNet), |
| 167 | Layer.provideMerge( | 167 | Layer.provideMerge( |
| 168 | DesktopAppSettings.layerTest({ | 168 | DesktopAppSettings.layerTest({ |
| 169 | ...DesktopAppSettings.DEFAULT_DESKTOP_SETTINGS, | 169 | ...DesktopAppSettings.DEFAULT_DESKTOP_SETTINGS, |
| ... | @@ -204,9 +204,9 @@ describe("DesktopWslBackend", () => { | ... | @@ -204,9 +204,9 @@ describe("DesktopWslBackend", () => { |
| 204 | Effect.provide( | 204 | Effect.provide( |
| 205 | DesktopWslBackend.layer.pipe( | 205 | DesktopWslBackend.layer.pipe( |
| 206 | Layer.provideMerge(DesktopBackendPool.layerTest([primary, wsl])), | 206 | Layer.provideMerge(DesktopBackendPool.layerTest([primary, wsl])), |
| 207 | Layer.provideMerge(backendConfigurationLayer), | 207 | Layer.provideMerge(layerBackendConfiguration), |
| 208 | Layer.provideMerge(serverExposureLayer), | 208 | Layer.provideMerge(layerServerExposure), |
| 209 | Layer.provideMerge(netLayer), | 209 | Layer.provideMerge(layerNet), |
| 210 | Layer.provideMerge( | 210 | Layer.provideMerge( |
| 211 | DesktopAppSettings.layerTest({ | 211 | DesktopAppSettings.layerTest({ |
| 212 | ...DesktopAppSettings.DEFAULT_DESKTOP_SETTINGS, | 212 | ...DesktopAppSettings.DEFAULT_DESKTOP_SETTINGS, |
apps/desktop/src/wsl/DesktopWslServerTree.test.ts+7-7| ... | @@ -16,7 +16,7 @@ import * as DesktopWslServerTree from "./DesktopWslServerTree.ts"; | ... | @@ -16,7 +16,7 @@ import * as DesktopWslServerTree from "./DesktopWslServerTree.ts"; |
| 16 | // Electron) fs, so a plain directory named server.asar exercises the full | 16 | // Electron) fs, so a plain directory named server.asar exercises the full |
| 17 | // extraction path under plain Node. | 17 | // extraction path under plain Node. |
| 18 | 18 | ||
| 19 | const environmentLayer = (input: { | 19 | const layerEnvironment = (input: { |
| 20 | readonly baseDir: string; | 20 | readonly baseDir: string; |
| 21 | readonly resourcesPath: string; | 21 | readonly resourcesPath: string; |
| 22 | readonly appVersion?: string; | 22 | readonly appVersion?: string; |
| ... | @@ -69,7 +69,7 @@ const ensureWith = (input: { | ... | @@ -69,7 +69,7 @@ const ensureWith = (input: { |
| 69 | const tree = yield* DesktopWslServerTree.DesktopWslServerTree; | 69 | const tree = yield* DesktopWslServerTree.DesktopWslServerTree; |
| 70 | return yield* tree.ensure; | 70 | return yield* tree.ensure; |
| 71 | }).pipe( | 71 | }).pipe( |
| 72 | Effect.provide(DesktopWslServerTree.layer.pipe(Layer.provideMerge(environmentLayer(input)))), | 72 | Effect.provide(DesktopWslServerTree.layer.pipe(Layer.provideMerge(layerEnvironment(input)))), |
| 73 | ); | 73 | ); |
| 74 | 74 | ||
| 75 | describe("DesktopWslServerTree", () => { | 75 | describe("DesktopWslServerTree", () => { |
| ... | @@ -182,7 +182,7 @@ describe("DesktopWslServerTree", () => { | ... | @@ -182,7 +182,7 @@ describe("DesktopWslServerTree", () => { |
| 182 | }).pipe( | 182 | }).pipe( |
| 183 | Effect.provide( | 183 | Effect.provide( |
| 184 | DesktopWslServerTree.layer.pipe( | 184 | DesktopWslServerTree.layer.pipe( |
| 185 | Layer.provideMerge(environmentLayer({ baseDir: tempDir, resourcesPath })), | 185 | Layer.provideMerge(layerEnvironment({ baseDir: tempDir, resourcesPath })), |
| 186 | ), | 186 | ), |
| 187 | ), | 187 | ), |
| 188 | ); | 188 | ); |
| ... | @@ -313,7 +313,7 @@ describe("DesktopWslServerTree", () => { | ... | @@ -313,7 +313,7 @@ describe("DesktopWslServerTree", () => { |
| 313 | }).pipe( | 313 | }).pipe( |
| 314 | Effect.provide( | 314 | Effect.provide( |
| 315 | DesktopWslServerTree.layer.pipe( | 315 | DesktopWslServerTree.layer.pipe( |
| 316 | Layer.provideMerge(environmentLayer({ baseDir: tempDir, resourcesPath })), | 316 | Layer.provideMerge(layerEnvironment({ baseDir: tempDir, resourcesPath })), |
| 317 | ), | 317 | ), |
| 318 | ), | 318 | ), |
| 319 | ); | 319 | ); |
| ... | @@ -340,7 +340,7 @@ describe("DesktopWslServerTree", () => { | ... | @@ -340,7 +340,7 @@ describe("DesktopWslServerTree", () => { |
| 340 | const treeRoot = path.dirname(versionDir); | 340 | const treeRoot = path.dirname(versionDir); |
| 341 | const extractedEntryPath = path.join(versionDir, "apps/server/dist/bin.mjs"); | 341 | const extractedEntryPath = path.join(versionDir, "apps/server/dist/bin.mjs"); |
| 342 | let cleanupFailed = false; | 342 | let cleanupFailed = false; |
| 343 | const partialCleanupFileSystem = Layer.effect( | 343 | const layerPartialCleanupFileSystem = Layer.effect( |
| 344 | FileSystem.FileSystem, | 344 | FileSystem.FileSystem, |
| 345 | Effect.gen(function* () { | 345 | Effect.gen(function* () { |
| 346 | const realFileSystem = yield* FileSystem.FileSystem; | 346 | const realFileSystem = yield* FileSystem.FileSystem; |
| ... | @@ -371,8 +371,8 @@ describe("DesktopWslServerTree", () => { | ... | @@ -371,8 +371,8 @@ describe("DesktopWslServerTree", () => { |
| 371 | }).pipe( | 371 | }).pipe( |
| 372 | Effect.provide( | 372 | Effect.provide( |
| 373 | DesktopWslServerTree.layer.pipe( | 373 | DesktopWslServerTree.layer.pipe( |
| 374 | Layer.provideMerge(environmentLayer({ baseDir: tempDir, resourcesPath })), | 374 | Layer.provideMerge(layerEnvironment({ baseDir: tempDir, resourcesPath })), |
| 375 | Layer.provideMerge(partialCleanupFileSystem), | 375 | Layer.provideMerge(layerPartialCleanupFileSystem), |
| 376 | ), | 376 | ), |
| 377 | ), | 377 | ), |
| 378 | ); | 378 | ); |
apps/mobile/src/connection/background-activity.ts+2-2| ... | @@ -36,14 +36,14 @@ function normalizeAppState( | ... | @@ -36,14 +36,14 @@ function normalizeAppState( |
| 36 | return "unknown"; | 36 | return "unknown"; |
| 37 | } | 37 | } |
| 38 | 38 | ||
| 39 | export const mobileBackgroundActivityObserverLayer = Layer.succeed( | 39 | export const layerObserver = Layer.succeed( |
| 40 | EnvironmentRpcSubscriptionObserver, | 40 | EnvironmentRpcSubscriptionObserver, |
| 41 | EnvironmentRpcSubscriptionObserver.of({ | 41 | EnvironmentRpcSubscriptionObserver.of({ |
| 42 | observe: observeMobileBackgroundActivitySubscription, | 42 | observe: observeMobileBackgroundActivitySubscription, |
| 43 | }), | 43 | }), |
| 44 | ); | 44 | ); |
| 45 | 45 | ||
| 46 | export const mobileBackgroundActivityReporterLayer = Layer.effectDiscard( | 46 | export const layerReporter = Layer.effectDiscard( |
| 47 | Effect.gen(function* () { | 47 | Effect.gen(function* () { |
| 48 | const registry = yield* EnvironmentRegistry.EnvironmentRegistry; | 48 | const registry = yield* EnvironmentRegistry.EnvironmentRegistry; |
| 49 | const storage = yield* MobileStorage.MobileStorage; | 49 | const storage = yield* MobileStorage.MobileStorage; |
apps/mobile/src/connection/platform.ts+23-27| ... | @@ -29,7 +29,7 @@ import { clearThreadOutboxEnvironment } from "../state/thread-outbox-removal"; | ... | @@ -29,7 +29,7 @@ import { clearThreadOutboxEnvironment } from "../state/thread-outbox-removal"; |
| 29 | import { clearComposerDraftsEnvironment } from "../state/use-composer-drafts"; | 29 | import { clearComposerDraftsEnvironment } from "../state/use-composer-drafts"; |
| 30 | import { clearThreadComposerErrorsForEnvironment } from "../state/thread-composer-error"; | 30 | import { clearThreadComposerErrorsForEnvironment } from "../state/thread-composer-error"; |
| 31 | import { mobileApplicationActiveWakeup } from "./app-state-wakeups"; | 31 | import { mobileApplicationActiveWakeup } from "./app-state-wakeups"; |
| 32 | import { connectionStorageLayer } from "./storage"; | 32 | import * as ConnectionStorage from "./storage"; |
| 33 | 33 | ||
| 34 | function networkStatus(state: Network.NetworkState): "unknown" | "offline" | "online" { | 34 | function networkStatus(state: Network.NetworkState): "unknown" | "offline" | "online" { |
| 35 | if (state.isConnected === false) { | 35 | if (state.isConnected === false) { |
| ... | @@ -41,7 +41,7 @@ function networkStatus(state: Network.NetworkState): "unknown" | "offline" | "on | ... | @@ -41,7 +41,7 @@ function networkStatus(state: Network.NetworkState): "unknown" | "offline" | "on |
| 41 | return "unknown"; | 41 | return "unknown"; |
| 42 | } | 42 | } |
| 43 | 43 | ||
| 44 | const connectivityLayer = Connectivity.layer({ | 44 | const layerConnectivity = Connectivity.layer({ |
| 45 | status: Effect.tryPromise({ | 45 | status: Effect.tryPromise({ |
| 46 | try: () => Network.getNetworkStateAsync(), | 46 | try: () => Network.getNetworkStateAsync(), |
| 47 | catch: () => undefined, | 47 | catch: () => undefined, |
| ... | @@ -122,7 +122,7 @@ const networkPathChanges = Stream.callback<"network-changed">((queue) => | ... | @@ -122,7 +122,7 @@ const networkPathChanges = Stream.callback<"network-changed">((queue) => |
| 122 | ).pipe(Effect.asVoid), | 122 | ).pipe(Effect.asVoid), |
| 123 | ); | 123 | ); |
| 124 | 124 | ||
| 125 | const wakeupsLayer = Wakeups.layer({ | 125 | const layerWakeups = Wakeups.layer({ |
| 126 | changes: Stream.mergeAll( | 126 | changes: Stream.mergeAll( |
| 127 | [ | 127 | [ |
| 128 | Stream.callback<"application-active-probe" | "application-active-reconnect">((queue) => | 128 | Stream.callback<"application-active-probe" | "application-active-reconnect">((queue) => |
| ... | @@ -155,7 +155,7 @@ const wakeupsLayer = Wakeups.layer({ | ... | @@ -155,7 +155,7 @@ const wakeupsLayer = Wakeups.layer({ |
| 155 | ), | 155 | ), |
| 156 | }); | 156 | }); |
| 157 | 157 | ||
| 158 | const capabilitiesLayer = Layer.effectContext( | 158 | const layerCapabilities = Layer.effectContext( |
| 159 | Effect.gen(function* () { | 159 | Effect.gen(function* () { |
| 160 | const storage = yield* MobileStorage.MobileStorage; | 160 | const storage = yield* MobileStorage.MobileStorage; |
| 161 | return Context.make( | 161 | return Context.make( |
| ... | @@ -243,21 +243,17 @@ const capabilitiesLayer = Layer.effectContext( | ... | @@ -243,21 +243,17 @@ const capabilitiesLayer = Layer.effectContext( |
| 243 | }), | 243 | }), |
| 244 | ); | 244 | ); |
| 245 | 245 | ||
| 246 | const platformConnectionSourceLayer = Layer.succeed( | 246 | const layerPlatformConnectionSource = Layer.succeed( |
| 247 | PlatformConnectionSource.PlatformConnectionSource, | 247 | PlatformConnectionSource.PlatformConnectionSource, |
| 248 | PlatformConnectionSource.PlatformConnectionSource.of({ | 248 | PlatformConnectionSource.PlatformConnectionSource.of({ |
| 249 | registrations: Stream.empty, | 249 | registrations: Stream.empty, |
| 250 | }), | 250 | }), |
| 251 | ); | 251 | ); |
| 252 | 252 | ||
| 253 | const providedConnectionStorageLayer = connectionStorageLayer.pipe( | 253 | const layerProvidedConnectionStorage = ConnectionStorage.layer.pipe(Layer.provide(Runtime.layer)); |
| 254 | Layer.provide(Runtime.runtimeContextLayer), | 254 | const layerProvidedCapabilities = layerCapabilities.pipe(Layer.provide(Runtime.layer)); |
| 255 | ); | ||
| 256 | const providedCapabilitiesLayer = capabilitiesLayer.pipe( | ||
| 257 | Layer.provide(Runtime.runtimeContextLayer), | ||
| 258 | ); | ||
| 259 | 255 | ||
| 260 | const environmentOwnedDataCleanupLayer = Layer.succeed( | 256 | const layerEnvironmentOwnedDataCleanup = Layer.succeed( |
| 261 | Persistence.EnvironmentOwnedDataCleanup, | 257 | Persistence.EnvironmentOwnedDataCleanup, |
| 262 | Persistence.EnvironmentOwnedDataCleanup.of({ | 258 | Persistence.EnvironmentOwnedDataCleanup.of({ |
| 263 | clear: (environmentId) => | 259 | clear: (environmentId) => |
| ... | @@ -280,24 +276,24 @@ const environmentOwnedDataCleanupLayer = Layer.succeed( | ... | @@ -280,24 +276,24 @@ const environmentOwnedDataCleanupLayer = Layer.succeed( |
| 280 | ); | 276 | ); |
| 281 | 277 | ||
| 282 | type ConnectionPlatformLayerSource = | 278 | type ConnectionPlatformLayerSource = |
| 283 | | typeof providedConnectionStorageLayer | 279 | | typeof layerProvidedConnectionStorage |
| 284 | | typeof Runtime.runtimeContextLayer | 280 | | typeof Runtime.layer |
| 285 | | typeof connectivityLayer | 281 | | typeof layerConnectivity |
| 286 | | typeof wakeupsLayer | 282 | | typeof layerWakeups |
| 287 | | typeof providedCapabilitiesLayer | 283 | | typeof layerProvidedCapabilities |
| 288 | | typeof platformConnectionSourceLayer | 284 | | typeof layerPlatformConnectionSource |
| 289 | | typeof environmentOwnedDataCleanupLayer; | 285 | | typeof layerEnvironmentOwnedDataCleanup; |
| 290 | 286 | ||
| 291 | export const connectionPlatformLayer: Layer.Layer< | 287 | export const layer: Layer.Layer< |
| 292 | Layer.Success<ConnectionPlatformLayerSource>, | 288 | Layer.Success<ConnectionPlatformLayerSource>, |
| 293 | Layer.Error<ConnectionPlatformLayerSource>, | 289 | Layer.Error<ConnectionPlatformLayerSource>, |
| 294 | Layer.Services<ConnectionPlatformLayerSource> | 290 | Layer.Services<ConnectionPlatformLayerSource> |
| 295 | > = Layer.mergeAll( | 291 | > = Layer.mergeAll( |
| 296 | providedConnectionStorageLayer, | 292 | layerProvidedConnectionStorage, |
| 297 | Runtime.runtimeContextLayer, | 293 | Runtime.layer, |
| 298 | connectivityLayer, | 294 | layerConnectivity, |
| 299 | wakeupsLayer, | 295 | layerWakeups, |
| 300 | providedCapabilitiesLayer, | 296 | layerProvidedCapabilities, |
| 301 | platformConnectionSourceLayer, | 297 | layerPlatformConnectionSource, |
| 302 | environmentOwnedDataCleanupLayer, | 298 | layerEnvironmentOwnedDataCleanup, |
| 303 | ); | 299 | ); |
apps/mobile/src/connection/runtime.ts+19-24| ... | @@ -1,7 +1,7 @@ | ... | @@ -1,7 +1,7 @@ |
| 1 | import { Connection } from "@t3tools/client-runtime/connection"; | 1 | import { Connection } from "@t3tools/client-runtime/connection"; |
| 2 | import { ShellSnapshotLoader } from "@t3tools/client-runtime/state/shell"; | 2 | import { ShellSnapshotLoader } from "@t3tools/client-runtime/state/shell"; |
| 3 | import { | 3 | import { |
| 4 | boundedThreadSnapshotLoaderLayer, | 4 | BoundedThreadSnapshotLoader, |
| 5 | ThreadHistoryController, | 5 | ThreadHistoryController, |
| 6 | } from "@t3tools/client-runtime/state/threads"; | 6 | } from "@t3tools/client-runtime/state/threads"; |
| 7 | import * as Layer from "effect/Layer"; | 7 | import * as Layer from "effect/Layer"; |
| ... | @@ -9,49 +9,44 @@ import { Atom } from "effect/reactivity"; | ... | @@ -9,49 +9,44 @@ import { Atom } from "effect/reactivity"; |
| 9 | 9 | ||
| 10 | import type { FoundationHotModule } from "../lib/foundation-fast-refresh"; | 10 | import type { FoundationHotModule } from "../lib/foundation-fast-refresh"; |
| 11 | import { hotSwappableAtomRuntime } from "../lib/hot-swappable-atom-runtime"; | 11 | import { hotSwappableAtomRuntime } from "../lib/hot-swappable-atom-runtime"; |
| 12 | import { runtimeContextLayer } from "../lib/runtime"; | 12 | import * as Runtime from "../lib/runtime"; |
| 13 | import { appAtomRegistry } from "../state/atom-registry"; | 13 | import { appAtomRegistry } from "../state/atom-registry"; |
| 14 | import { | 14 | import * as BackgroundActivity from "./background-activity"; |
| 15 | mobileBackgroundActivityObserverLayer, | 15 | import * as ConnectionPlatform from "./platform"; |
| 16 | mobileBackgroundActivityReporterLayer, | ||
| 17 | } from "./background-activity"; | ||
| 18 | import { connectionPlatformLayer } from "./platform"; | ||
| 19 | 16 | ||
| 20 | declare const module: { readonly hot?: FoundationHotModule } | undefined; | 17 | declare const module: { readonly hot?: FoundationHotModule } | undefined; |
| 21 | 18 | ||
| 22 | const providedConnectionPlatformLayer = connectionPlatformLayer.pipe( | 19 | const layerProvidedConnectionPlatform = ConnectionPlatform.layer.pipe(Layer.provide(Runtime.layer)); |
| 23 | Layer.provide(runtimeContextLayer), | ||
| 24 | ); | ||
| 25 | 20 | ||
| 26 | const snapshotLoaderLayer = Layer.mergeAll( | 21 | const layerSnapshotLoader = Layer.mergeAll( |
| 27 | boundedThreadSnapshotLoaderLayer, | 22 | BoundedThreadSnapshotLoader.layer, |
| 28 | ShellSnapshotLoader.layer, | 23 | ShellSnapshotLoader.layer, |
| 29 | ThreadHistoryController.layer, | 24 | ThreadHistoryController.layer, |
| 30 | ); | 25 | ); |
| 31 | 26 | ||
| 32 | type ConnectionLayerSource = | 27 | type ConnectionLayerSource = |
| 33 | | typeof Connection.layer | 28 | | typeof Connection.layer |
| 34 | | typeof snapshotLoaderLayer | 29 | | typeof layerSnapshotLoader |
| 35 | | typeof runtimeContextLayer | 30 | | typeof Runtime.layer |
| 36 | | typeof connectionPlatformLayer | 31 | | typeof ConnectionPlatform.layer |
| 37 | | typeof mobileBackgroundActivityObserverLayer | 32 | | typeof BackgroundActivity.layerObserver |
| 38 | | typeof mobileBackgroundActivityReporterLayer; | 33 | | typeof BackgroundActivity.layerReporter; |
| 39 | 34 | ||
| 40 | const providedClientConnectionLayer = snapshotLoaderLayer.pipe( | 35 | const layerProvidedClientConnection = layerSnapshotLoader.pipe( |
| 41 | Layer.provideMerge( | 36 | Layer.provideMerge( |
| 42 | Connection.layerWithOptions({ usageLimitSources: true, usageLimitsCommand: true }), | 37 | Connection.layerWithOptions({ usageLimitSources: true, usageLimitsCommand: true }), |
| 43 | ), | 38 | ), |
| 44 | Layer.provideMerge( | 39 | Layer.provideMerge( |
| 45 | Layer.mergeAll( | 40 | Layer.mergeAll( |
| 46 | runtimeContextLayer, | 41 | Runtime.layer, |
| 47 | providedConnectionPlatformLayer, | 42 | layerProvidedConnectionPlatform, |
| 48 | mobileBackgroundActivityObserverLayer, | 43 | BackgroundActivity.layerObserver, |
| 49 | ), | 44 | ), |
| 50 | ), | 45 | ), |
| 51 | ); | 46 | ); |
| 52 | 47 | ||
| 53 | const connectionLayer = mobileBackgroundActivityReporterLayer.pipe( | 48 | const layerConnection = BackgroundActivity.layerReporter.pipe( |
| 54 | Layer.provideMerge(providedClientConnectionLayer), | 49 | Layer.provideMerge(layerProvidedClientConnection), |
| 55 | ); | 50 | ); |
| 56 | 51 | ||
| 57 | export const connectionAtomRuntime: Atom.AtomRuntime< | 52 | export const connectionAtomRuntime: Atom.AtomRuntime< |
| ... | @@ -61,5 +56,5 @@ export const connectionAtomRuntime: Atom.AtomRuntime< | ... | @@ -61,5 +56,5 @@ export const connectionAtomRuntime: Atom.AtomRuntime< |
| 61 | id: "t3.mobile.connection-runtime", | 56 | id: "t3.mobile.connection-runtime", |
| 62 | hotModule: typeof module === "undefined" ? undefined : module.hot, | 57 | hotModule: typeof module === "undefined" ? undefined : module.hot, |
| 63 | registry: appAtomRegistry, | 58 | registry: appAtomRegistry, |
| 64 | layer: connectionLayer, | 59 | layer: layerConnection, |
| 65 | }); | 60 | }); |
apps/mobile/src/connection/storage.ts+1-1| ... | @@ -39,7 +39,7 @@ function targetPersistenceError( | ... | @@ -39,7 +39,7 @@ function targetPersistenceError( |
| 39 | }); | 39 | }); |
| 40 | } | 40 | } |
| 41 | 41 | ||
| 42 | export const connectionStorageLayer = Layer.effectContext( | 42 | export const layer = Layer.effectContext( |
| 43 | Effect.gen(function* () { | 43 | Effect.gen(function* () { |
| 44 | const catalog = yield* CatalogStore.make(); | 44 | const catalog = yield* CatalogStore.make(); |
| 45 | const githubRoutingPermissions = yield* makeGitHubRoutingPermissions({ | 45 | const githubRoutingPermissions = yield* makeGitHubRoutingPermissions({ |
apps/mobile/src/features/agent-awareness/liveActivityPreferences.test.ts+6-6| ... | @@ -43,7 +43,7 @@ const connection: SavedRemoteConnection = { | ... | @@ -43,7 +43,7 @@ const connection: SavedRemoteConnection = { |
| 43 | bearerToken: "local-bearer", | 43 | bearerToken: "local-bearer", |
| 44 | }; | 44 | }; |
| 45 | 45 | ||
| 46 | const testLayer = Layer.mergeAll( | 46 | const layerTest = Layer.mergeAll( |
| 47 | Layer.succeed(ManagedRelay.ManagedRelayClient, null as never), | 47 | Layer.succeed(ManagedRelay.ManagedRelayClient, null as never), |
| 48 | Layer.succeed( | 48 | Layer.succeed( |
| 49 | HttpClient.HttpClient, | 49 | HttpClient.HttpClient, |
| ... | @@ -88,7 +88,7 @@ describe("liveActivityPreferences", () => { | ... | @@ -88,7 +88,7 @@ describe("liveActivityPreferences", () => { |
| 88 | connection, | 88 | connection, |
| 89 | liveActivitiesEnabled: false, | 89 | liveActivitiesEnabled: false, |
| 90 | }); | 90 | }); |
| 91 | }).pipe(Effect.provide(testLayer)), | 91 | }).pipe(Effect.provide(layerTest)), |
| 92 | ); | 92 | ); |
| 93 | 93 | ||
| 94 | it.effect("pushes enabled Live Activity preferences to relay registrations", () => | 94 | it.effect("pushes enabled Live Activity preferences to relay registrations", () => |
| ... | @@ -108,7 +108,7 @@ describe("liveActivityPreferences", () => { | ... | @@ -108,7 +108,7 @@ describe("liveActivityPreferences", () => { |
| 108 | connection, | 108 | connection, |
| 109 | liveActivitiesEnabled: true, | 109 | liveActivitiesEnabled: true, |
| 110 | }); | 110 | }); |
| 111 | }).pipe(Effect.provide(testLayer)), | 111 | }).pipe(Effect.provide(layerTest)), |
| 112 | ); | 112 | ); |
| 113 | 113 | ||
| 114 | it.effect("keeps local preferences refreshable when signed out", () => | 114 | it.effect("keeps local preferences refreshable when signed out", () => |
| ... | @@ -124,7 +124,7 @@ describe("liveActivityPreferences", () => { | ... | @@ -124,7 +124,7 @@ describe("liveActivityPreferences", () => { |
| 124 | liveActivitiesEnabled: false, | 124 | liveActivitiesEnabled: false, |
| 125 | }); | 125 | }); |
| 126 | expect(linkEnvironmentToCloudWithPreference).not.toHaveBeenCalled(); | 126 | expect(linkEnvironmentToCloudWithPreference).not.toHaveBeenCalled(); |
| 127 | }).pipe(Effect.provide(testLayer)), | 127 | }).pipe(Effect.provide(layerTest)), |
| 128 | ); | 128 | ); |
| 129 | 129 | ||
| 130 | it.effect("does not try to re-link managed relay connections without bearer credentials", () => { | 130 | it.effect("does not try to re-link managed relay connections without bearer credentials", () => { |
| ... | @@ -147,7 +147,7 @@ describe("liveActivityPreferences", () => { | ... | @@ -147,7 +147,7 @@ describe("liveActivityPreferences", () => { |
| 147 | connection, | 147 | connection, |
| 148 | liveActivitiesEnabled: true, | 148 | liveActivitiesEnabled: true, |
| 149 | }); | 149 | }); |
| 150 | }).pipe(Effect.provide(testLayer)); | 150 | }).pipe(Effect.provide(layerTest)); |
| 151 | }); | 151 | }); |
| 152 | 152 | ||
| 153 | it.effect("restores relay preferences when an environment update fails", () => { | 153 | it.effect("restores relay preferences when an environment update fails", () => { |
| ... | @@ -182,6 +182,6 @@ describe("liveActivityPreferences", () => { | ... | @@ -182,6 +182,6 @@ describe("liveActivityPreferences", () => { |
| 182 | connection, | 182 | connection, |
| 183 | liveActivitiesEnabled: true, | 183 | liveActivitiesEnabled: true, |
| 184 | }); | 184 | }); |
| 185 | }).pipe(Effect.provide(testLayer)); | 185 | }).pipe(Effect.provide(layerTest)); |
| 186 | }); | 186 | }); |
| 187 | }); | 187 | }); |
apps/mobile/src/features/agent-awareness/remoteRegistration.test.ts+27-27| ... | @@ -15,8 +15,8 @@ import { ManagedRelay } from "@t3tools/client-runtime/relay"; | ... | @@ -15,8 +15,8 @@ import { ManagedRelay } from "@t3tools/client-runtime/relay"; |
| 15 | import type { EnvironmentId } from "@t3tools/contracts"; | 15 | import type { EnvironmentId } from "@t3tools/contracts"; |
| 16 | import { verifyDpopProof } from "@t3tools/shared/dpop"; | 16 | import { verifyDpopProof } from "@t3tools/shared/dpop"; |
| 17 | import type { SavedRemoteConnection } from "../../lib/connection"; | 17 | import type { SavedRemoteConnection } from "../../lib/connection"; |
| 18 | import { cryptoLayer } from "../cloud/dpop"; | 18 | import * as Dpop from "../cloud/dpop"; |
| 19 | import { managedRelayClientLayer } from "../cloud/managedRelayLayer"; | 19 | import * as ManagedRelayLayer from "../cloud/managedRelayLayer"; |
| 20 | import { | 20 | import { |
| 21 | clearAgentAwarenessRegistrationRecord, | 21 | clearAgentAwarenessRegistrationRecord, |
| 22 | loadAgentAwarenessRegistrationRecord, | 22 | loadAgentAwarenessRegistrationRecord, |
| ... | @@ -220,8 +220,8 @@ function savedConnection(): SavedRemoteConnection { | ... | @@ -220,8 +220,8 @@ function savedConnection(): SavedRemoteConnection { |
| 220 | }; | 220 | }; |
| 221 | } | 221 | } |
| 222 | 222 | ||
| 223 | const relayTestLayer = managedRelayClientLayer("https://relay.example.test").pipe( | 223 | const layerRelayTest = ManagedRelayLayer.layer("https://relay.example.test").pipe( |
| 224 | Layer.provide(Layer.mergeAll(FetchHttpClient.layer, cryptoLayer)), | 224 | Layer.provide(Layer.mergeAll(FetchHttpClient.layer, Dpop.layer)), |
| 225 | ); | 225 | ); |
| 226 | 226 | ||
| 227 | const runBackgroundOperations = Effect.fn("TestRemoteRegistration.runBackgroundOperations")( | 227 | const runBackgroundOperations = Effect.fn("TestRemoteRegistration.runBackgroundOperations")( |
| ... | @@ -405,7 +405,7 @@ describe("makeRelayDeviceRegistrationRequest", () => { | ... | @@ -405,7 +405,7 @@ describe("makeRelayDeviceRegistrationRequest", () => { |
| 405 | 405 | ||
| 406 | expect(activity.getPushToken).toHaveBeenCalledTimes(2); | 406 | expect(activity.getPushToken).toHaveBeenCalledTimes(2); |
| 407 | expect(addPushTokenListener).toHaveBeenCalledTimes(1); | 407 | expect(addPushTokenListener).toHaveBeenCalledTimes(1); |
| 408 | }).pipe(Effect.provide(relayTestLayer)); | 408 | }).pipe(Effect.provide(layerRelayTest)); |
| 409 | }); | 409 | }); |
| 410 | 410 | ||
| 411 | it.effect("preserves Live Activity push-token lookup failures", () => { | 411 | it.effect("preserves Live Activity push-token lookup failures", () => { |
| ... | @@ -427,7 +427,7 @@ describe("makeRelayDeviceRegistrationRequest", () => { | ... | @@ -427,7 +427,7 @@ describe("makeRelayDeviceRegistrationRequest", () => { |
| 427 | cause, | 427 | cause, |
| 428 | message: "Agent awareness operation read-live-activity-push-token failed.", | 428 | message: "Agent awareness operation read-live-activity-push-token failed.", |
| 429 | }); | 429 | }); |
| 430 | }).pipe(Effect.provide(relayTestLayer)); | 430 | }).pipe(Effect.provide(layerRelayTest)); |
| 431 | }); | 431 | }); |
| 432 | 432 | ||
| 433 | it.effect( | 433 | it.effect( |
| ... | @@ -441,7 +441,7 @@ describe("makeRelayDeviceRegistrationRequest", () => { | ... | @@ -441,7 +441,7 @@ describe("makeRelayDeviceRegistrationRequest", () => { |
| 441 | 441 | ||
| 442 | return Effect.gen(function* () { | 442 | return Effect.gen(function* () { |
| 443 | expect(yield* registerLiveActivityPushToken({ activity: activity as never })).toBe(false); | 443 | expect(yield* registerLiveActivityPushToken({ activity: activity as never })).toBe(false); |
| 444 | }).pipe(Effect.provide(relayTestLayer)); | 444 | }).pipe(Effect.provide(layerRelayTest)); |
| 445 | }, | 445 | }, |
| 446 | ); | 446 | ); |
| 447 | 447 | ||
| ... | @@ -465,7 +465,7 @@ describe("makeRelayDeviceRegistrationRequest", () => { | ... | @@ -465,7 +465,7 @@ describe("makeRelayDeviceRegistrationRequest", () => { |
| 465 | expect(activity.start).not.toHaveBeenCalled(); | 465 | expect(activity.start).not.toHaveBeenCalled(); |
| 466 | expect(activity.update).not.toHaveBeenCalled(); | 466 | expect(activity.update).not.toHaveBeenCalled(); |
| 467 | expect(activity.end).not.toHaveBeenCalled(); | 467 | expect(activity.end).not.toHaveBeenCalled(); |
| 468 | }).pipe(Effect.provide(relayTestLayer)); | 468 | }).pipe(Effect.provide(layerRelayTest)); |
| 469 | }, | 469 | }, |
| 470 | ); | 470 | ); |
| 471 | 471 | ||
| ... | @@ -495,7 +495,7 @@ describe("makeRelayDeviceRegistrationRequest", () => { | ... | @@ -495,7 +495,7 @@ describe("makeRelayDeviceRegistrationRequest", () => { |
| 495 | } | 495 | } |
| 496 | yield* runBackgroundOperations(); | 496 | yield* runBackgroundOperations(); |
| 497 | expect(activity.getPushToken).toHaveBeenCalled(); | 497 | expect(activity.getPushToken).toHaveBeenCalled(); |
| 498 | }).pipe(Effect.provide(relayTestLayer)); | 498 | }).pipe(Effect.provide(layerRelayTest)); |
| 499 | }, | 499 | }, |
| 500 | ); | 500 | ); |
| 501 | 501 | ||
| ... | @@ -525,7 +525,7 @@ describe("makeRelayDeviceRegistrationRequest", () => { | ... | @@ -525,7 +525,7 @@ describe("makeRelayDeviceRegistrationRequest", () => { |
| 525 | yield* refreshAgentAwarenessRegistration(); | 525 | yield* refreshAgentAwarenessRegistration(); |
| 526 | 526 | ||
| 527 | expect(Notifications.getDevicePushTokenAsync).toHaveBeenCalledTimes(1); | 527 | expect(Notifications.getDevicePushTokenAsync).toHaveBeenCalledTimes(1); |
| 528 | }).pipe(Effect.provide(relayTestLayer)); | 528 | }).pipe(Effect.provide(layerRelayTest)); |
| 529 | }); | 529 | }); |
| 530 | 530 | ||
| 531 | it.effect("registers the APNs device when cloud auth becomes available", () => { | 531 | it.effect("registers the APNs device when cloud auth becomes available", () => { |
| ... | @@ -583,7 +583,7 @@ describe("makeRelayDeviceRegistrationRequest", () => { | ... | @@ -583,7 +583,7 @@ describe("makeRelayDeviceRegistrationRequest", () => { |
| 583 | }), | 583 | }), |
| 584 | ).toMatchObject({ ok: true }); | 584 | ).toMatchObject({ ok: true }); |
| 585 | expect(getAgentAwarenessRegistrationStatus()).toBe("registered"); | 585 | expect(getAgentAwarenessRegistrationStatus()).toBe("registered"); |
| 586 | }).pipe(Effect.provide(relayTestLayer)); | 586 | }).pipe(Effect.provide(layerRelayTest)); |
| 587 | }); | 587 | }); |
| 588 | 588 | ||
| 589 | it.effect("marks registration failed when device registration cannot complete", () => { | 589 | it.effect("marks registration failed when device registration cannot complete", () => { |
| ... | @@ -604,7 +604,7 @@ describe("makeRelayDeviceRegistrationRequest", () => { | ... | @@ -604,7 +604,7 @@ describe("makeRelayDeviceRegistrationRequest", () => { |
| 604 | // read as enabled. | 604 | // read as enabled. |
| 605 | yield* refreshAgentAwarenessRegistration(); | 605 | yield* refreshAgentAwarenessRegistration(); |
| 606 | expect(getAgentAwarenessRegistrationStatus()).toBe("failed"); | 606 | expect(getAgentAwarenessRegistrationStatus()).toBe("failed"); |
| 607 | }).pipe(Effect.provide(relayTestLayer)); | 607 | }).pipe(Effect.provide(layerRelayTest)); |
| 608 | }); | 608 | }); |
| 609 | 609 | ||
| 610 | it("clears registration status on cloud sign-out", () => { | 610 | it("clears registration status on cloud sign-out", () => { |
| ... | @@ -642,7 +642,7 @@ describe("makeRelayDeviceRegistrationRequest", () => { | ... | @@ -642,7 +642,7 @@ describe("makeRelayDeviceRegistrationRequest", () => { |
| 642 | return Effect.gen(function* () { | 642 | return Effect.gen(function* () { |
| 643 | yield* runBackgroundOperations(); | 643 | yield* runBackgroundOperations(); |
| 644 | expect(getAgentAwarenessRegistrationStatus()).toBe("unknown"); | 644 | expect(getAgentAwarenessRegistrationStatus()).toBe("unknown"); |
| 645 | }).pipe(Effect.provide(relayTestLayer)); | 645 | }).pipe(Effect.provide(layerRelayTest)); |
| 646 | }); | 646 | }); |
| 647 | 647 | ||
| 648 | it.effect("keeps a registered status when a later refresh fails", () => { | 648 | it.effect("keeps a registered status when a later refresh fails", () => { |
| ... | @@ -664,7 +664,7 @@ describe("makeRelayDeviceRegistrationRequest", () => { | ... | @@ -664,7 +664,7 @@ describe("makeRelayDeviceRegistrationRequest", () => { |
| 664 | ); | 664 | ); |
| 665 | yield* refreshAgentAwarenessRegistration(); | 665 | yield* refreshAgentAwarenessRegistration(); |
| 666 | expect(getAgentAwarenessRegistrationStatus()).toBe("registered"); | 666 | expect(getAgentAwarenessRegistrationStatus()).toBe("registered"); |
| 667 | }).pipe(Effect.provide(relayTestLayer)); | 667 | }).pipe(Effect.provide(layerRelayTest)); |
| 668 | }); | 668 | }); |
| 669 | 669 | ||
| 670 | it.effect("does not re-register the same account when nothing has changed", () => { | 670 | it.effect("does not re-register the same account when nothing has changed", () => { |
| ... | @@ -687,7 +687,7 @@ describe("makeRelayDeviceRegistrationRequest", () => { | ... | @@ -687,7 +687,7 @@ describe("makeRelayDeviceRegistrationRequest", () => { |
| 687 | yield* refreshAgentAwarenessRegistration(); | 687 | yield* refreshAgentAwarenessRegistration(); |
| 688 | expect(getAgentAwarenessRegistrationStatus()).toBe("registered"); | 688 | expect(getAgentAwarenessRegistrationStatus()).toBe("registered"); |
| 689 | expect(saveAgentAwarenessRegistrationRecord).not.toHaveBeenCalled(); | 689 | expect(saveAgentAwarenessRegistrationRecord).not.toHaveBeenCalled(); |
| 690 | }).pipe(Effect.provide(relayTestLayer)); | 690 | }).pipe(Effect.provide(layerRelayTest)); |
| 691 | }); | 691 | }); |
| 692 | 692 | ||
| 693 | it.effect("dedupes rapid activity-token re-registrations within the replay window", () => { | 693 | it.effect("dedupes rapid activity-token re-registrations within the replay window", () => { |
| ... | @@ -735,7 +735,7 @@ describe("makeRelayDeviceRegistrationRequest", () => { | ... | @@ -735,7 +735,7 @@ describe("makeRelayDeviceRegistrationRequest", () => { |
| 735 | vi.mocked(loadOrCreateAgentAwarenessDeviceId).mockClear(); | 735 | vi.mocked(loadOrCreateAgentAwarenessDeviceId).mockClear(); |
| 736 | yield* refreshActiveLiveActivityRemoteRegistration(); | 736 | yield* refreshActiveLiveActivityRemoteRegistration(); |
| 737 | expect(loadOrCreateAgentAwarenessDeviceId).not.toHaveBeenCalled(); | 737 | expect(loadOrCreateAgentAwarenessDeviceId).not.toHaveBeenCalled(); |
| 738 | }).pipe(Effect.provide(relayTestLayer)); | 738 | }).pipe(Effect.provide(layerRelayTest)); |
| 739 | }); | 739 | }); |
| 740 | 740 | ||
| 741 | it.effect("re-registers when the stored account identity differs", () => { | 741 | it.effect("re-registers when the stored account identity differs", () => { |
| ... | @@ -750,7 +750,7 @@ describe("makeRelayDeviceRegistrationRequest", () => { | ... | @@ -750,7 +750,7 @@ describe("makeRelayDeviceRegistrationRequest", () => { |
| 750 | return Effect.gen(function* () { | 750 | return Effect.gen(function* () { |
| 751 | yield* refreshAgentAwarenessRegistration(); | 751 | yield* refreshAgentAwarenessRegistration(); |
| 752 | expect(saveAgentAwarenessRegistrationRecord).toHaveBeenCalledTimes(1); | 752 | expect(saveAgentAwarenessRegistrationRecord).toHaveBeenCalledTimes(1); |
| 753 | }).pipe(Effect.provide(relayTestLayer)); | 753 | }).pipe(Effect.provide(layerRelayTest)); |
| 754 | }); | 754 | }); |
| 755 | 755 | ||
| 756 | it.effect("coalesces simultaneous sign-in and environment connection registrations", () => { | 756 | it.effect("coalesces simultaneous sign-in and environment connection registrations", () => { |
| ... | @@ -784,7 +784,7 @@ describe("makeRelayDeviceRegistrationRequest", () => { | ... | @@ -784,7 +784,7 @@ describe("makeRelayDeviceRegistrationRequest", () => { |
| 784 | return Effect.gen(function* () { | 784 | return Effect.gen(function* () { |
| 785 | yield* runBackgroundOperations(); | 785 | yield* runBackgroundOperations(); |
| 786 | expect(Notifications.getPermissionsAsync).toHaveBeenCalledTimes(1); | 786 | expect(Notifications.getPermissionsAsync).toHaveBeenCalledTimes(1); |
| 787 | }).pipe(Effect.provide(relayTestLayer)); | 787 | }).pipe(Effect.provide(layerRelayTest)); |
| 788 | }); | 788 | }); |
| 789 | 789 | ||
| 790 | it.effect("continues queued device registration after a failed auth lookup", () => { | 790 | it.effect("continues queued device registration after a failed auth lookup", () => { |
| ... | @@ -808,7 +808,7 @@ describe("makeRelayDeviceRegistrationRequest", () => { | ... | @@ -808,7 +808,7 @@ describe("makeRelayDeviceRegistrationRequest", () => { |
| 808 | 808 | ||
| 809 | expect(backgroundRuntime.pending).toHaveLength(0); | 809 | expect(backgroundRuntime.pending).toHaveLength(0); |
| 810 | expect(tokenProvider).toHaveBeenCalledTimes(2); | 810 | expect(tokenProvider).toHaveBeenCalledTimes(2); |
| 811 | }).pipe(Effect.provide(relayTestLayer)); | 811 | }).pipe(Effect.provide(layerRelayTest)); |
| 812 | }); | 812 | }); |
| 813 | 813 | ||
| 814 | it("only registers again when the authenticated identity changes", () => { | 814 | it("only registers again when the authenticated identity changes", () => { |
| ... | @@ -852,7 +852,7 @@ describe("makeRelayDeviceRegistrationRequest", () => { | ... | @@ -852,7 +852,7 @@ describe("makeRelayDeviceRegistrationRequest", () => { |
| 852 | return Effect.gen(function* () { | 852 | return Effect.gen(function* () { |
| 853 | yield* runBackgroundOperations(); | 853 | yield* runBackgroundOperations(); |
| 854 | expect(Notifications.getDevicePushTokenAsync).toHaveBeenCalledTimes(1); | 854 | expect(Notifications.getDevicePushTokenAsync).toHaveBeenCalledTimes(1); |
| 855 | }).pipe(Effect.provide(relayTestLayer)); | 855 | }).pipe(Effect.provide(layerRelayTest)); |
| 856 | }); | 856 | }); |
| 857 | 857 | ||
| 858 | it.effect( | 858 | it.effect( |
| ... | @@ -890,7 +890,7 @@ describe("makeRelayDeviceRegistrationRequest", () => { | ... | @@ -890,7 +890,7 @@ describe("makeRelayDeviceRegistrationRequest", () => { |
| 890 | unregisterAgentAwarenessConnection(savedConnection().environmentId); | 890 | unregisterAgentAwarenessConnection(savedConnection().environmentId); |
| 891 | 891 | ||
| 892 | expect(fetchMock).not.toHaveBeenCalled(); | 892 | expect(fetchMock).not.toHaveBeenCalled(); |
| 893 | }).pipe(Effect.provide(relayTestLayer)); | 893 | }).pipe(Effect.provide(layerRelayTest)); |
| 894 | }, | 894 | }, |
| 895 | ); | 895 | ); |
| 896 | 896 | ||
| ... | @@ -988,8 +988,8 @@ describe("makeRelayDeviceRegistrationRequest", () => { | ... | @@ -988,8 +988,8 @@ describe("makeRelayDeviceRegistrationRequest", () => { |
| 988 | }).pipe( | 988 | }).pipe( |
| 989 | Effect.provideService(FetchHttpClient.Fetch, globalThis.fetch), | 989 | Effect.provideService(FetchHttpClient.Fetch, globalThis.fetch), |
| 990 | Effect.provide( | 990 | Effect.provide( |
| 991 | managedRelayClientLayer("https://permission-relay.example.test").pipe( | 991 | ManagedRelayLayer.layer("https://permission-relay.example.test").pipe( |
| 992 | Layer.provide(Layer.mergeAll(FetchHttpClient.layer, cryptoLayer)), | 992 | Layer.provide(Layer.mergeAll(FetchHttpClient.layer, Dpop.layer)), |
| 993 | ), | 993 | ), |
| 994 | ), | 994 | ), |
| 995 | ); | 995 | ); |
| ... | @@ -1039,7 +1039,7 @@ describe("makeRelayDeviceRegistrationRequest", () => { | ... | @@ -1039,7 +1039,7 @@ describe("makeRelayDeviceRegistrationRequest", () => { |
| 1039 | } | 1039 | } |
| 1040 | expect(getAgentAwarenessRegistrationStatus()).toBe("failed"); | 1040 | expect(getAgentAwarenessRegistrationStatus()).toBe("failed"); |
| 1041 | expect(saveAgentAwarenessRegistrationRecord).not.toHaveBeenCalled(); | 1041 | expect(saveAgentAwarenessRegistrationRecord).not.toHaveBeenCalled(); |
| 1042 | }).pipe(Effect.provide(relayTestLayer)); | 1042 | }).pipe(Effect.provide(layerRelayTest)); |
| 1043 | }); | 1043 | }); |
| 1044 | 1044 | ||
| 1045 | it.effect("registers an Android FCM token without invoking Apple Live Activities", () => { | 1045 | it.effect("registers an Android FCM token without invoking Apple Live Activities", () => { |
| ... | @@ -1082,7 +1082,7 @@ describe("makeRelayDeviceRegistrationRequest", () => { | ... | @@ -1082,7 +1082,7 @@ describe("makeRelayDeviceRegistrationRequest", () => { |
| 1082 | vi.mocked(clearAndroidAgentNotifications).mockClear(); | 1082 | vi.mocked(clearAndroidAgentNotifications).mockClear(); |
| 1083 | setAgentAwarenessRelayTokenProvider(() => Promise.resolve("clerk-token-user-b"), "user-b"); | 1083 | setAgentAwarenessRelayTokenProvider(() => Promise.resolve("clerk-token-user-b"), "user-b"); |
| 1084 | expect(clearAndroidAgentNotifications).toHaveBeenCalled(); | 1084 | expect(clearAndroidAgentNotifications).toHaveBeenCalled(); |
| 1085 | }).pipe(Effect.provide(relayTestLayer)); | 1085 | }).pipe(Effect.provide(layerRelayTest)); |
| 1086 | }); | 1086 | }); |
| 1087 | it.effect( | 1087 | it.effect( |
| 1088 | "preserves same-account Android notifications and replays on remount and later foreground", | 1088 | "preserves same-account Android notifications and replays on remount and later foreground", |
| ... | @@ -1120,7 +1120,7 @@ describe("makeRelayDeviceRegistrationRequest", () => { | ... | @@ -1120,7 +1120,7 @@ describe("makeRelayDeviceRegistrationRequest", () => { |
| 1120 | setAgentAwarenessRelayTokenProvider(null); | 1120 | setAgentAwarenessRelayTokenProvider(null); |
| 1121 | expect(clearAndroidAgentNotifications).toHaveBeenCalled(); | 1121 | expect(clearAndroidAgentNotifications).toHaveBeenCalled(); |
| 1122 | expect(clearAgentAwarenessRegistrationRecord).toHaveBeenCalled(); | 1122 | expect(clearAgentAwarenessRegistrationRecord).toHaveBeenCalled(); |
| 1123 | }).pipe(Effect.provide(relayTestLayer)); | 1123 | }).pipe(Effect.provide(layerRelayTest)); |
| 1124 | }, | 1124 | }, |
| 1125 | ); | 1125 | ); |
| 1126 | 1126 | ||
| ... | @@ -1137,7 +1137,7 @@ describe("makeRelayDeviceRegistrationRequest", () => { | ... | @@ -1137,7 +1137,7 @@ describe("makeRelayDeviceRegistrationRequest", () => { |
| 1137 | yield* runBackgroundOperations(); | 1137 | yield* runBackgroundOperations(); |
| 1138 | expect(configureAndroidAgentNotifications).not.toHaveBeenCalled(); | 1138 | expect(configureAndroidAgentNotifications).not.toHaveBeenCalled(); |
| 1139 | expect(saveAgentAwarenessRegistrationRecord).not.toHaveBeenCalled(); | 1139 | expect(saveAgentAwarenessRegistrationRecord).not.toHaveBeenCalled(); |
| 1140 | }).pipe(Effect.provide(relayTestLayer)); | 1140 | }).pipe(Effect.provide(layerRelayTest)); |
| 1141 | }, | 1141 | }, |
| 1142 | ); | 1142 | ); |
| 1143 | }); | 1143 | }); |
apps/mobile/src/features/cloud/dpop.test.ts+7-11| ... | @@ -8,12 +8,8 @@ import * as Crypto from "effect/Crypto"; | ... | @@ -8,12 +8,8 @@ import * as Crypto from "effect/Crypto"; |
| 8 | import * as Effect from "effect/Effect"; | 8 | import * as Effect from "effect/Effect"; |
| 9 | import { verifyDpopProof } from "@t3tools/shared/dpop"; | 9 | import { verifyDpopProof } from "@t3tools/shared/dpop"; |
| 10 | 10 | ||
| 11 | import { | 11 | import { createDpopProof, generateDpopProofKeyPair, loadOrCreateDpopProofKeyPair } from "./dpop"; |
| 12 | createDpopProof, | 12 | import * as Dpop from "./dpop"; |
| 13 | generateDpopProofKeyPair, | ||
| 14 | loadOrCreateDpopProofKeyPair, | ||
| 15 | cryptoLayer, | ||
| 16 | } from "./dpop"; | ||
| 17 | 13 | ||
| 18 | vi.mock("expo-crypto", () => ({ | 14 | vi.mock("expo-crypto", () => ({ |
| 19 | CryptoDigestAlgorithm: { | 15 | CryptoDigestAlgorithm: { |
| ... | @@ -75,7 +71,7 @@ describe("mobile DPoP", () => { | ... | @@ -75,7 +71,7 @@ describe("mobile DPoP", () => { |
| 75 | expect(Buffer.from(digest).toString("hex")).toBe( | 71 | expect(Buffer.from(digest).toString("hex")).toBe( |
| 76 | NodeCrypto.createHash("sha256").update("typed-array").digest("hex"), | 72 | NodeCrypto.createHash("sha256").update("typed-array").digest("hex"), |
| 77 | ); | 73 | ); |
| 78 | }).pipe(Effect.provide(cryptoLayer)), | 74 | }).pipe(Effect.provide(Dpop.layer)), |
| 79 | ); | 75 | ); |
| 80 | 76 | ||
| 81 | it.effect("persists and reuses the installation proof key", () => | 77 | it.effect("persists and reuses the installation proof key", () => |
| ... | @@ -86,7 +82,7 @@ describe("mobile DPoP", () => { | ... | @@ -86,7 +82,7 @@ describe("mobile DPoP", () => { |
| 86 | 82 | ||
| 87 | expect(second.thumbprint).toBe(first.thumbprint); | 83 | expect(second.thumbprint).toBe(first.thumbprint); |
| 88 | expect(second.privateJwk).toEqual(first.privateJwk); | 84 | expect(second.privateJwk).toEqual(first.privateJwk); |
| 89 | }).pipe(Effect.provide(cryptoLayer)), | 85 | }).pipe(Effect.provide(Dpop.layer)), |
| 90 | ); | 86 | ); |
| 91 | 87 | ||
| 92 | it.effect("rejects malformed persisted proof keys", () => | 88 | it.effect("rejects malformed persisted proof keys", () => |
| ... | @@ -96,7 +92,7 @@ describe("mobile DPoP", () => { | ... | @@ -96,7 +92,7 @@ describe("mobile DPoP", () => { |
| 96 | const error = yield* loadOrCreateDpopProofKeyPair().pipe(Effect.flip); | 92 | const error = yield* loadOrCreateDpopProofKeyPair().pipe(Effect.flip); |
| 97 | 93 | ||
| 98 | expect(error.message).toBe("Stored DPoP proof key is invalid."); | 94 | expect(error.message).toBe("Stored DPoP proof key is invalid."); |
| 99 | }).pipe(Effect.provide(cryptoLayer)), | 95 | }).pipe(Effect.provide(Dpop.layer)), |
| 100 | ); | 96 | ); |
| 101 | 97 | ||
| 102 | it.effect("signs connect and bootstrap proofs with the same ephemeral proof key", () => | 98 | it.effect("signs connect and bootstrap proofs with the same ephemeral proof key", () => |
| ... | @@ -135,7 +131,7 @@ describe("mobile DPoP", () => { | ... | @@ -135,7 +131,7 @@ describe("mobile DPoP", () => { |
| 135 | nowEpochSeconds: proofIat(bootstrap.proof), | 131 | nowEpochSeconds: proofIat(bootstrap.proof), |
| 136 | }), | 132 | }), |
| 137 | ).toMatchObject({ ok: true, thumbprint: proofKey.thumbprint }); | 133 | ).toMatchObject({ ok: true, thumbprint: proofKey.thumbprint }); |
| 138 | }).pipe(Effect.provide(cryptoLayer)), | 134 | }).pipe(Effect.provide(Dpop.layer)), |
| 139 | ); | 135 | ); |
| 140 | 136 | ||
| 141 | it.effect("signs DPoP proofs with RFC 9449 htu normalization", () => | 137 | it.effect("signs DPoP proofs with RFC 9449 htu normalization", () => |
| ... | @@ -161,6 +157,6 @@ describe("mobile DPoP", () => { | ... | @@ -161,6 +157,6 @@ describe("mobile DPoP", () => { |
| 161 | nowEpochSeconds: proofIat(proof.proof), | 157 | nowEpochSeconds: proofIat(proof.proof), |
| 162 | }), | 158 | }), |
| 163 | ).toMatchObject({ ok: true }); | 159 | ).toMatchObject({ ok: true }); |
| 164 | }).pipe(Effect.provide(cryptoLayer)), | 160 | }).pipe(Effect.provide(Dpop.layer)), |
| 165 | ); | 161 | ); |
| 166 | }); | 162 | }); |
apps/mobile/src/features/cloud/dpop.ts+1-1| ... | @@ -65,7 +65,7 @@ function toExpoDigestAlgorithm( | ... | @@ -65,7 +65,7 @@ function toExpoDigestAlgorithm( |
| 65 | } | 65 | } |
| 66 | } | 66 | } |
| 67 | 67 | ||
| 68 | export const cryptoLayer = Layer.succeed( | 68 | export const layer = Layer.succeed( |
| 69 | Crypto.Crypto, | 69 | Crypto.Crypto, |
| 70 | Crypto.make({ | 70 | Crypto.make({ |
| 71 | randomBytes: ExpoCrypto.getRandomBytes, | 71 | randomBytes: ExpoCrypto.getRandomBytes, |
apps/mobile/src/features/cloud/linkEnvironment.test.ts+7-7| ... | @@ -5,7 +5,7 @@ import * as Layer from "effect/Layer"; | ... | @@ -5,7 +5,7 @@ import * as Layer from "effect/Layer"; |
| 5 | import { EnvironmentId } from "@t3tools/contracts"; | 5 | import { EnvironmentId } from "@t3tools/contracts"; |
| 6 | import { RelayMobileClientId } from "@t3tools/contracts/relay"; | 6 | import { RelayMobileClientId } from "@t3tools/contracts/relay"; |
| 7 | import { ManagedRelay } from "@t3tools/client-runtime/relay"; | 7 | import { ManagedRelay } from "@t3tools/client-runtime/relay"; |
| 8 | import { remoteHttpClientLayer } from "@t3tools/client-runtime/rpc"; | 8 | import { layerRemoteHttpClient } from "@t3tools/client-runtime/rpc"; |
| 9 | import { HttpClient } from "effect/http"; | 9 | import { HttpClient } from "effect/http"; |
| 10 | 10 | ||
| 11 | import * as MobileStorage from "../../persistence/mobile-storage"; | 11 | import * as MobileStorage from "../../persistence/mobile-storage"; |
| ... | @@ -63,7 +63,7 @@ const createProofMock = vi.fn( | ... | @@ -63,7 +63,7 @@ const createProofMock = vi.fn( |
| 63 | (input: { readonly method: string; readonly url: string; readonly accessToken?: string }) => | 63 | (input: { readonly method: string; readonly url: string; readonly accessToken?: string }) => |
| 64 | Effect.succeed(`dpop:${input.method}:${input.url}`), | 64 | Effect.succeed(`dpop:${input.method}:${input.url}`), |
| 65 | ); | 65 | ); |
| 66 | const testDpopSignerLayer = Layer.succeed( | 66 | const layerTestDpopSigner = Layer.succeed( |
| 67 | ManagedRelay.ManagedRelayDpopSigner, | 67 | ManagedRelay.ManagedRelayDpopSigner, |
| 68 | ManagedRelay.ManagedRelayDpopSigner.of({ | 68 | ManagedRelay.ManagedRelayDpopSigner.of({ |
| 69 | thumbprint: Effect.succeed("client-proof-key-thumbprint"), | 69 | thumbprint: Effect.succeed("client-proof-key-thumbprint"), |
| ... | @@ -71,10 +71,10 @@ const testDpopSignerLayer = Layer.succeed( | ... | @@ -71,10 +71,10 @@ const testDpopSignerLayer = Layer.succeed( |
| 71 | }), | 71 | }), |
| 72 | ); | 72 | ); |
| 73 | 73 | ||
| 74 | function cloudClientLayer() { | 74 | function layerCloudClient() { |
| 75 | const httpClientLayer = remoteHttpClientLayer((input, init) => globalThis.fetch(input, init)); | 75 | const layerHttpClient = layerRemoteHttpClient((input, init) => globalThis.fetch(input, init)); |
| 76 | return Layer.mergeAll( | 76 | return Layer.mergeAll( |
| 77 | httpClientLayer, | 77 | layerHttpClient, |
| 78 | Layer.succeed( | 78 | Layer.succeed( |
| 79 | MobileStorage.MobileStorage, | 79 | MobileStorage.MobileStorage, |
| 80 | MobileStorage.MobileStorage.of({ | 80 | MobileStorage.MobileStorage.of({ |
| ... | @@ -93,7 +93,7 @@ function cloudClientLayer() { | ... | @@ -93,7 +93,7 @@ function cloudClientLayer() { |
| 93 | ManagedRelay.layer({ | 93 | ManagedRelay.layer({ |
| 94 | relayUrl: "https://relay.example.test", | 94 | relayUrl: "https://relay.example.test", |
| 95 | clientId: RelayMobileClientId, | 95 | clientId: RelayMobileClientId, |
| 96 | }).pipe(Layer.provideMerge(testDpopSignerLayer), Layer.provide(httpClientLayer)), | 96 | }).pipe(Layer.provideMerge(layerTestDpopSigner), Layer.provide(layerHttpClient)), |
| 97 | ); | 97 | ); |
| 98 | } | 98 | } |
| 99 | 99 | ||
| ... | @@ -106,7 +106,7 @@ const withCloudServices = <A, E>( | ... | @@ -106,7 +106,7 @@ const withCloudServices = <A, E>( |
| 106 | | ManagedRelay.ManagedRelayDpopSigner | 106 | | ManagedRelay.ManagedRelayDpopSigner |
| 107 | | MobileStorage.MobileStorage | 107 | | MobileStorage.MobileStorage |
| 108 | >, | 108 | >, |
| 109 | ) => effect.pipe(Effect.provide(cloudClientLayer())); | 109 | ) => effect.pipe(Effect.provide(layerCloudClient())); |
| 110 | 110 | ||
| 111 | function validLinkProof() { | 111 | function validLinkProof() { |
| 112 | return "signed-environment-link-jwt"; | 112 | return "signed-environment-link-jwt"; |
apps/mobile/src/features/cloud/managedRelayLayer.test.ts+4-4| ... | @@ -9,8 +9,8 @@ import * as Effect from "effect/Effect"; | ... | @@ -9,8 +9,8 @@ import * as Effect from "effect/Effect"; |
| 9 | import * as Layer from "effect/Layer"; | 9 | import * as Layer from "effect/Layer"; |
| 10 | import { FetchHttpClient } from "effect/http"; | 10 | import { FetchHttpClient } from "effect/http"; |
| 11 | 11 | ||
| 12 | import { cryptoLayer } from "./dpop"; | 12 | import * as Dpop from "./dpop"; |
| 13 | import { managedRelayClientLayer } from "./managedRelayLayer"; | 13 | import * as ManagedRelayLayer from "./managedRelayLayer"; |
| 14 | 14 | ||
| 15 | vi.mock("expo-crypto", () => ({ | 15 | vi.mock("expo-crypto", () => ({ |
| 16 | CryptoDigestAlgorithm: { SHA256: "SHA-256" }, | 16 | CryptoDigestAlgorithm: { SHA256: "SHA-256" }, |
| ... | @@ -54,8 +54,8 @@ describe("managed relay DPoP signer", () => { | ... | @@ -54,8 +54,8 @@ describe("managed relay DPoP signer", () => { |
| 54 | assert.equal(yield* signer.thumbprint, thumbprint); | 54 | assert.equal(yield* signer.thumbprint, thumbprint); |
| 55 | }).pipe( | 55 | }).pipe( |
| 56 | Effect.provide( | 56 | Effect.provide( |
| 57 | managedRelayClientLayer("https://relay.example.test").pipe( | 57 | ManagedRelayLayer.layer("https://relay.example.test").pipe( |
| 58 | Layer.provide(Layer.mergeAll(FetchHttpClient.layer, cryptoLayer)), | 58 | Layer.provide(Layer.mergeAll(FetchHttpClient.layer, Dpop.layer)), |
| 59 | ), | 59 | ), |
| 60 | ), | 60 | ), |
| 61 | ), | 61 | ), |
apps/mobile/src/features/cloud/managedRelayLayer.ts+3-3| ... | @@ -10,7 +10,7 @@ import * as Layer from "effect/Layer"; | ... | @@ -10,7 +10,7 @@ import * as Layer from "effect/Layer"; |
| 10 | import { createDpopProof, loadOrCreateDpopProofKeyPair } from "./dpop"; | 10 | import { createDpopProof, loadOrCreateDpopProofKeyPair } from "./dpop"; |
| 11 | import { managedRelayAccessTokenStore } from "./managedRelayTokenStore"; | 11 | import { managedRelayAccessTokenStore } from "./managedRelayTokenStore"; |
| 12 | 12 | ||
| 13 | const relayDpopSignerLayer = Layer.effect( | 13 | const layerRelayDpopSigner = Layer.effect( |
| 14 | ManagedRelay.ManagedRelayDpopSigner, | 14 | ManagedRelay.ManagedRelayDpopSigner, |
| 15 | Effect.gen(function* () { | 15 | Effect.gen(function* () { |
| 16 | const crypto = yield* Crypto.Crypto; | 16 | const crypto = yield* Crypto.Crypto; |
| ... | @@ -64,9 +64,9 @@ const relayDpopSignerLayer = Layer.effect( | ... | @@ -64,9 +64,9 @@ const relayDpopSignerLayer = Layer.effect( |
| 64 | }), | 64 | }), |
| 65 | ); | 65 | ); |
| 66 | 66 | ||
| 67 | export const managedRelayClientLayer = (relayUrl: string) => | 67 | export const layer = (relayUrl: string) => |
| 68 | ManagedRelay.layer({ | 68 | ManagedRelay.layer({ |
| 69 | relayUrl, | 69 | relayUrl, |
| 70 | clientId: RelayMobileClientId, | 70 | clientId: RelayMobileClientId, |
| 71 | accessTokenStore: managedRelayAccessTokenStore, | 71 | accessTokenStore: managedRelayAccessTokenStore, |
| 72 | }).pipe(Layer.provideMerge(relayDpopSignerLayer)); | 72 | }).pipe(Layer.provideMerge(layerRelayDpopSigner)); |
apps/mobile/src/features/cloud/managedRelayState.ts+2-2| ... | @@ -14,11 +14,11 @@ import type { RelayClientEnvironmentRecord } from "@t3tools/contracts/relay"; | ... | @@ -14,11 +14,11 @@ import type { RelayClientEnvironmentRecord } from "@t3tools/contracts/relay"; |
| 14 | import { AsyncResult, Atom } from "effect/reactivity"; | 14 | import { AsyncResult, Atom } from "effect/reactivity"; |
| 15 | import { useCallback, useEffect } from "react"; | 15 | import { useCallback, useEffect } from "react"; |
| 16 | 16 | ||
| 17 | import { runtimeContextLayer } from "../../lib/runtime"; | 17 | import * as Runtime from "../../lib/runtime"; |
| 18 | import { appAtomRegistry } from "../../state/atom-registry"; | 18 | import { appAtomRegistry } from "../../state/atom-registry"; |
| 19 | import { cloudDebugLog } from "./cloudDebugLog"; | 19 | import { cloudDebugLog } from "./cloudDebugLog"; |
| 20 | 20 | ||
| 21 | const managedRelayAtomRuntime = Atom.runtime(runtimeContextLayer); | 21 | const managedRelayAtomRuntime = Atom.runtime(Runtime.layer); |
| 22 | 22 | ||
| 23 | export const managedRelayQueryManager = createManagedRelayQueryManager(managedRelayAtomRuntime, { | 23 | export const managedRelayQueryManager = createManagedRelayQueryManager(managedRelayAtomRuntime, { |
| 24 | onQueryEvent: (event) => | 24 | onQueryEvent: (event) => |
apps/mobile/src/features/observability/tracing.test.ts+12-12| ... | @@ -4,10 +4,10 @@ import * as Effect from "effect/Effect"; | ... | @@ -4,10 +4,10 @@ import * as Effect from "effect/Effect"; |
| 4 | import * as Layer from "effect/Layer"; | 4 | import * as Layer from "effect/Layer"; |
| 5 | import { vi } from "vite-plus/test"; | 5 | import { vi } from "vite-plus/test"; |
| 6 | 6 | ||
| 7 | import { remoteHttpClientLayer } from "@t3tools/client-runtime/rpc"; | 7 | import { layerRemoteHttpClient } from "@t3tools/client-runtime/rpc"; |
| 8 | import { withRelayClientTracing } from "@t3tools/shared/relayTracing"; | 8 | import { withRelayClientTracing } from "@t3tools/shared/relayTracing"; |
| 9 | 9 | ||
| 10 | import { makeTracingLayer } from "./tracing"; | 10 | import * as Tracing from "./tracing"; |
| 11 | 11 | ||
| 12 | vi.mock("expo-constants", () => ({ | 12 | vi.mock("expo-constants", () => ({ |
| 13 | default: { | 13 | default: { |
| ... | @@ -19,7 +19,7 @@ vi.mock("expo-constants", () => ({ | ... | @@ -19,7 +19,7 @@ vi.mock("expo-constants", () => ({ |
| 19 | 19 | ||
| 20 | it.effect("exports spans through the scoped mobile OTLP layer", () => { | 20 | it.effect("exports spans through the scoped mobile OTLP layer", () => { |
| 21 | const fetchFn = vi.fn<typeof fetch>(async () => new Response(null, { status: 202 })); | 21 | const fetchFn = vi.fn<typeof fetch>(async () => new Response(null, { status: 202 })); |
| 22 | const tracingLayer = makeTracingLayer( | 22 | const layerTracing = Tracing.layerFromConfig( |
| 23 | { | 23 | { |
| 24 | tracesUrl: "https://api.axiom.test/v1/traces", | 24 | tracesUrl: "https://api.axiom.test/v1/traces", |
| 25 | tracesDataset: "mobile-traces", | 25 | tracesDataset: "mobile-traces", |
| ... | @@ -29,13 +29,13 @@ it.effect("exports spans through the scoped mobile OTLP layer", () => { | ... | @@ -29,13 +29,13 @@ it.effect("exports spans through the scoped mobile OTLP layer", () => { |
| 29 | appVariant: "test", | 29 | appVariant: "test", |
| 30 | serviceVersion: "1.2.3", | 30 | serviceVersion: "1.2.3", |
| 31 | }, | 31 | }, |
| 32 | ).pipe(Layer.provide(remoteHttpClientLayer(fetchFn))); | 32 | ).pipe(Layer.provide(layerRemoteHttpClient(fetchFn))); |
| 33 | const tracedApplication = Layer.effectDiscard( | 33 | const layerTracedApplication = Layer.effectDiscard( |
| 34 | Effect.void.pipe(Effect.withSpan("mobile.test.span"), withRelayClientTracing), | 34 | Effect.void.pipe(Effect.withSpan("mobile.test.span"), withRelayClientTracing), |
| 35 | ).pipe(Layer.provide(tracingLayer)); | 35 | ).pipe(Layer.provide(layerTracing)); |
| 36 | 36 | ||
| 37 | return Effect.gen(function* () { | 37 | return Effect.gen(function* () { |
| 38 | yield* Layer.build(tracedApplication); | 38 | yield* Layer.build(layerTracedApplication); |
| 39 | 39 | ||
| 40 | expect(fetchFn).not.toHaveBeenCalled(); | 40 | expect(fetchFn).not.toHaveBeenCalled(); |
| 41 | }).pipe( | 41 | }).pipe( |
| ... | @@ -55,7 +55,7 @@ it.effect("exports spans through the scoped mobile OTLP layer", () => { | ... | @@ -55,7 +55,7 @@ it.effect("exports spans through the scoped mobile OTLP layer", () => { |
| 55 | 55 | ||
| 56 | it.effect("does not let OTLP serialization failures alter application effects", () => { | 56 | it.effect("does not let OTLP serialization failures alter application effects", () => { |
| 57 | const fetchFn = vi.fn<typeof fetch>(async () => new Response(null, { status: 202 })); | 57 | const fetchFn = vi.fn<typeof fetch>(async () => new Response(null, { status: 202 })); |
| 58 | const tracingLayer = makeTracingLayer( | 58 | const layerTracing = Tracing.layerFromConfig( |
| 59 | { | 59 | { |
| 60 | tracesUrl: "https://api.axiom.test/v1/traces", | 60 | tracesUrl: "https://api.axiom.test/v1/traces", |
| 61 | tracesDataset: "mobile-traces", | 61 | tracesDataset: "mobile-traces", |
| ... | @@ -65,9 +65,9 @@ it.effect("does not let OTLP serialization failures alter application effects", | ... | @@ -65,9 +65,9 @@ it.effect("does not let OTLP serialization failures alter application effects", |
| 65 | appVariant: "test", | 65 | appVariant: "test", |
| 66 | serviceVersion: "1.2.3", | 66 | serviceVersion: "1.2.3", |
| 67 | }, | 67 | }, |
| 68 | ).pipe(Layer.provide(remoteHttpClientLayer(fetchFn))); | 68 | ).pipe(Layer.provide(layerRemoteHttpClient(fetchFn))); |
| 69 | const failure = { durationNanos: 1n }; | 69 | const failure = { durationNanos: 1n }; |
| 70 | const tracedApplication = Layer.effectDiscard( | 70 | const layerTracedApplication = Layer.effectDiscard( |
| 71 | Effect.fail(failure).pipe( | 71 | Effect.fail(failure).pipe( |
| 72 | Effect.withSpan("mobile.test.failed-span"), | 72 | Effect.withSpan("mobile.test.failed-span"), |
| 73 | withRelayClientTracing, | 73 | withRelayClientTracing, |
| ... | @@ -81,9 +81,9 @@ it.effect("does not let OTLP serialization failures alter application effects", | ... | @@ -81,9 +81,9 @@ it.effect("does not let OTLP serialization failures alter application effects", |
| 81 | : Effect.die(new Error("Expected the original typed failure.")); | 81 | : Effect.die(new Error("Expected the original typed failure.")); |
| 82 | }), | 82 | }), |
| 83 | ), | 83 | ), |
| 84 | ).pipe(Layer.provide(tracingLayer)); | 84 | ).pipe(Layer.provide(layerTracing)); |
| 85 | 85 | ||
| 86 | return Layer.build(tracedApplication).pipe( | 86 | return Layer.build(layerTracedApplication).pipe( |
| 87 | Effect.scoped, | 87 | Effect.scoped, |
| 88 | Effect.andThen( | 88 | Effect.andThen( |
| 89 | Effect.sync(() => { | 89 | Effect.sync(() => { |
apps/mobile/src/features/observability/tracing.ts+4-4| ... | @@ -1,5 +1,5 @@ | ... | @@ -1,5 +1,5 @@ |
| 1 | import Constants from "expo-constants"; | 1 | import Constants from "expo-constants"; |
| 2 | import { makeRelayClientTracingLayer } from "@t3tools/shared/relayTracing"; | 2 | import * as RelayTracing from "@t3tools/shared/relayTracing"; |
| 3 | 3 | ||
| 4 | import { hasTracingPublicConfig, resolveCloudPublicConfig } from "../cloud/publicConfig"; | 4 | import { hasTracingPublicConfig, resolveCloudPublicConfig } from "../cloud/publicConfig"; |
| 5 | 5 | ||
| ... | @@ -23,8 +23,8 @@ export function resolveTracingConfig(): TracingConfig | null { | ... | @@ -23,8 +23,8 @@ export function resolveTracingConfig(): TracingConfig | null { |
| 23 | return { tracesUrl, tracesDataset, tracesToken }; | 23 | return { tracesUrl, tracesDataset, tracesToken }; |
| 24 | } | 24 | } |
| 25 | 25 | ||
| 26 | export function makeTracingLayer(config: TracingConfig | null, resource: TracingResource) { | 26 | export function layerFromConfig(config: TracingConfig | null, resource: TracingResource) { |
| 27 | return makeRelayClientTracingLayer(config, { | 27 | return RelayTracing.layer(config, { |
| 28 | serviceName: "t3code-mobile", | 28 | serviceName: "t3code-mobile", |
| 29 | serviceVersion: resource.serviceVersion, | 29 | serviceVersion: resource.serviceVersion, |
| 30 | runtime: "react-native", | 30 | runtime: "react-native", |
| ... | @@ -32,7 +32,7 @@ export function makeTracingLayer(config: TracingConfig | null, resource: Tracing | ... | @@ -32,7 +32,7 @@ export function makeTracingLayer(config: TracingConfig | null, resource: Tracing |
| 32 | }); | 32 | }); |
| 33 | } | 33 | } |
| 34 | 34 | ||
| 35 | export const tracingLayer = makeTracingLayer(resolveTracingConfig(), { | 35 | export const layer = layerFromConfig(resolveTracingConfig(), { |
| 36 | serviceVersion: Constants.expoConfig?.version, | 36 | serviceVersion: Constants.expoConfig?.version, |
| 37 | appVariant: | 37 | appVariant: |
| 38 | typeof Constants.expoConfig?.extra?.appVariant === "string" | 38 | typeof Constants.expoConfig?.extra?.appVariant === "string" |
apps/mobile/src/lib/hot-swappable-atom-runtime.test.ts+8-8| ... | @@ -10,7 +10,7 @@ class RuntimeValue extends Context.Service<RuntimeValue, { readonly value: strin | ... | @@ -10,7 +10,7 @@ class RuntimeValue extends Context.Service<RuntimeValue, { readonly value: strin |
| 10 | "t3/mobile/test/RuntimeValue", | 10 | "t3/mobile/test/RuntimeValue", |
| 11 | ) {} | 11 | ) {} |
| 12 | 12 | ||
| 13 | function runtimeLayer(value: string, events: string[]) { | 13 | function layerRuntime(value: string, events: string[]) { |
| 14 | return Layer.effect( | 14 | return Layer.effect( |
| 15 | RuntimeValue, | 15 | RuntimeValue, |
| 16 | Effect.acquireRelease( | 16 | Effect.acquireRelease( |
| ... | @@ -26,7 +26,7 @@ function runtimeLayer(value: string, events: string[]) { | ... | @@ -26,7 +26,7 @@ function runtimeLayer(value: string, events: string[]) { |
| 26 | ); | 26 | ); |
| 27 | } | 27 | } |
| 28 | 28 | ||
| 29 | function runtimeLayerWithRelease( | 29 | function layerRuntimeWithRelease( |
| 30 | value: string, | 30 | value: string, |
| 31 | events: string[], | 31 | events: string[], |
| 32 | release: Effect.Effect<void> = Effect.sync(() => { | 32 | release: Effect.Effect<void> = Effect.sync(() => { |
| ... | @@ -56,7 +56,7 @@ describe("hotSwappableAtomRuntime", () => { | ... | @@ -56,7 +56,7 @@ describe("hotSwappableAtomRuntime", () => { |
| 56 | id, | 56 | id, |
| 57 | hotModule: { accept }, | 57 | hotModule: { accept }, |
| 58 | registry, | 58 | registry, |
| 59 | layer: runtimeLayer("first", events), | 59 | layer: layerRuntime("first", events), |
| 60 | }); | 60 | }); |
| 61 | const valueAtom = runtime.atom(RuntimeValue.pipe(Effect.map((service) => service.value))); | 61 | const valueAtom = runtime.atom(RuntimeValue.pipe(Effect.map((service) => service.value))); |
| 62 | const values: string[] = []; | 62 | const values: string[] = []; |
| ... | @@ -81,13 +81,13 @@ describe("hotSwappableAtomRuntime", () => { | ... | @@ -81,13 +81,13 @@ describe("hotSwappableAtomRuntime", () => { |
| 81 | id, | 81 | id, |
| 82 | hotModule: { accept }, | 82 | hotModule: { accept }, |
| 83 | registry, | 83 | registry, |
| 84 | layer: runtimeLayer("second", events), | 84 | layer: layerRuntime("second", events), |
| 85 | }); | 85 | }); |
| 86 | hotSwappableAtomRuntime({ | 86 | hotSwappableAtomRuntime({ |
| 87 | id, | 87 | id, |
| 88 | hotModule: { accept }, | 88 | hotModule: { accept }, |
| 89 | registry, | 89 | registry, |
| 90 | layer: runtimeLayer("third", events), | 90 | layer: layerRuntime("third", events), |
| 91 | }); | 91 | }); |
| 92 | registry.set(unrelatedAtom, 8); | 92 | registry.set(unrelatedAtom, 8); |
| 93 | 93 | ||
| ... | @@ -120,7 +120,7 @@ describe("hotSwappableAtomRuntime", () => { | ... | @@ -120,7 +120,7 @@ describe("hotSwappableAtomRuntime", () => { |
| 120 | vi.stubGlobal("__DEV__", false); | 120 | vi.stubGlobal("__DEV__", false); |
| 121 | const registry = AtomRegistry.make(); | 121 | const registry = AtomRegistry.make(); |
| 122 | const accept = vi.fn(); | 122 | const accept = vi.fn(); |
| 123 | const layer = runtimeLayer("production", []); | 123 | const layer = layerRuntime("production", []); |
| 124 | 124 | ||
| 125 | const first = hotSwappableAtomRuntime({ | 125 | const first = hotSwappableAtomRuntime({ |
| 126 | id: "production", | 126 | id: "production", |
| ... | @@ -163,7 +163,7 @@ describe("hotSwappableAtomRuntime", () => { | ... | @@ -163,7 +163,7 @@ describe("hotSwappableAtomRuntime", () => { |
| 163 | id, | 163 | id, |
| 164 | hotModule: { accept() {} }, | 164 | hotModule: { accept() {} }, |
| 165 | registry, | 165 | registry, |
| 166 | layer: runtimeLayerWithRelease("first", events, firstRelease), | 166 | layer: layerRuntimeWithRelease("first", events, firstRelease), |
| 167 | }); | 167 | }); |
| 168 | const valueAtom = runtime.atom(RuntimeValue.pipe(Effect.map((service) => service.value))); | 168 | const valueAtom = runtime.atom(RuntimeValue.pipe(Effect.map((service) => service.value))); |
| 169 | const values: string[] = []; | 169 | const values: string[] = []; |
| ... | @@ -176,7 +176,7 @@ describe("hotSwappableAtomRuntime", () => { | ... | @@ -176,7 +176,7 @@ describe("hotSwappableAtomRuntime", () => { |
| 176 | id, | 176 | id, |
| 177 | hotModule: { accept() {} }, | 177 | hotModule: { accept() {} }, |
| 178 | registry, | 178 | registry, |
| 179 | layer: runtimeLayer("second", events), | 179 | layer: layerRuntime("second", events), |
| 180 | }); | 180 | }); |
| 181 | 181 | ||
| 182 | expect(events).toEqual(["acquire:first", "release:start:first", "acquire:second"]); | 182 | expect(events).toEqual(["acquire:first", "release:start:first", "acquire:second"]); |
apps/mobile/src/lib/runtime.ts+16-16| ... | @@ -2,12 +2,12 @@ import * as Layer from "effect/Layer"; | ... | @@ -2,12 +2,12 @@ import * as Layer from "effect/Layer"; |
| 2 | import * as ManagedRuntime from "effect/ManagedRuntime"; | 2 | import * as ManagedRuntime from "effect/ManagedRuntime"; |
| 3 | import * as Socket from "effect/socket/Socket"; | 3 | import * as Socket from "effect/socket/Socket"; |
| 4 | 4 | ||
| 5 | import { remoteHttpClientLayer } from "@t3tools/client-runtime/rpc"; | 5 | import { layerRemoteHttpClient } from "@t3tools/client-runtime/rpc"; |
| 6 | 6 | ||
| 7 | import { cryptoLayer } from "../features/cloud/dpop"; | 7 | import * as Dpop from "../features/cloud/dpop"; |
| 8 | import { managedRelayClientLayer } from "../features/cloud/managedRelayLayer"; | 8 | import * as ManagedRelayLayer from "../features/cloud/managedRelayLayer"; |
| 9 | import { resolveCloudPublicConfig } from "../features/cloud/publicConfig"; | 9 | import { resolveCloudPublicConfig } from "../features/cloud/publicConfig"; |
| 10 | import { tracingLayer } from "../features/observability/tracing"; | 10 | import * as Tracing from "../features/observability/tracing"; |
| 11 | import * as Persistence from "../persistence/layer"; | 11 | import * as Persistence from "../persistence/layer"; |
| 12 | import { disposeOnFoundationReplace, type FoundationHotModule } from "./foundation-fast-refresh"; | 12 | import { disposeOnFoundationReplace, type FoundationHotModule } from "./foundation-fast-refresh"; |
| 13 | 13 | ||
| ... | @@ -17,32 +17,32 @@ function configuredRelayUrl(): string { | ... | @@ -17,32 +17,32 @@ function configuredRelayUrl(): string { |
| 17 | return resolveCloudPublicConfig().relay.url ?? "http://relay.invalid"; | 17 | return resolveCloudPublicConfig().relay.url ?? "http://relay.invalid"; |
| 18 | } | 18 | } |
| 19 | 19 | ||
| 20 | const httpClientLayer = remoteHttpClientLayer(fetch); | 20 | const layerHttpClient = layerRemoteHttpClient(fetch); |
| 21 | 21 | ||
| 22 | type RuntimeLayerSource = | 22 | type RuntimeLayerSource = |
| 23 | | ReturnType<typeof managedRelayClientLayer> | 23 | | ReturnType<typeof ManagedRelayLayer.layer> |
| 24 | | typeof Socket.layerWebSocketConstructorGlobal | 24 | | typeof Socket.layerWebSocketConstructorGlobal |
| 25 | | typeof cryptoLayer | 25 | | typeof Dpop.layer |
| 26 | | typeof httpClientLayer | 26 | | typeof layerHttpClient |
| 27 | | typeof Persistence.layer | 27 | | typeof Persistence.layer |
| 28 | | typeof tracingLayer; | 28 | | typeof Tracing.layer; |
| 29 | 29 | ||
| 30 | const runtimeLayer = Layer.merge( | 30 | const layerRuntime = Layer.merge( |
| 31 | managedRelayClientLayer(configuredRelayUrl()), | 31 | ManagedRelayLayer.layer(configuredRelayUrl()), |
| 32 | Socket.layerWebSocketConstructorGlobal, | 32 | Socket.layerWebSocketConstructorGlobal, |
| 33 | ).pipe( | 33 | ).pipe( |
| 34 | Layer.provideMerge(cryptoLayer), | 34 | Layer.provideMerge(Dpop.layer), |
| 35 | Layer.provideMerge(httpClientLayer), | 35 | Layer.provideMerge(layerHttpClient), |
| 36 | Layer.provideMerge(tracingLayer.pipe(Layer.provide(httpClientLayer))), | 36 | Layer.provideMerge(Tracing.layer.pipe(Layer.provide(layerHttpClient))), |
| 37 | Layer.provideMerge(Persistence.layer), | 37 | Layer.provideMerge(Persistence.layer), |
| 38 | ); | 38 | ); |
| 39 | 39 | ||
| 40 | export const runtime: ManagedRuntime.ManagedRuntime< | 40 | export const runtime: ManagedRuntime.ManagedRuntime< |
| 41 | Layer.Success<RuntimeLayerSource>, | 41 | Layer.Success<RuntimeLayerSource>, |
| 42 | Layer.Error<RuntimeLayerSource> | 42 | Layer.Error<RuntimeLayerSource> |
| 43 | > = ManagedRuntime.make(runtimeLayer); | 43 | > = ManagedRuntime.make(layerRuntime); |
| 44 | 44 | ||
| 45 | export const runtimeContextLayer: Layer.Layer< | 45 | export const layer: Layer.Layer< |
| 46 | Layer.Success<RuntimeLayerSource>, | 46 | Layer.Success<RuntimeLayerSource>, |
| 47 | Layer.Error<RuntimeLayerSource> | 47 | Layer.Error<RuntimeLayerSource> |
| 48 | > = Layer.effectContext(runtime.contextEffect); | 48 | > = Layer.effectContext(runtime.contextEffect); |
apps/mobile/src/persistence/layer.ts+4-4| ... | @@ -6,11 +6,11 @@ import * as MobilePreferences from "./mobile-preferences"; | ... | @@ -6,11 +6,11 @@ import * as MobilePreferences from "./mobile-preferences"; |
| 6 | import * as MobileSecureStorage from "./mobile-secure-storage"; | 6 | import * as MobileSecureStorage from "./mobile-secure-storage"; |
| 7 | import * as MobileStorage from "./mobile-storage"; | 7 | import * as MobileStorage from "./mobile-storage"; |
| 8 | 8 | ||
| 9 | const baseLayer = Layer.merge(MobileDatabase.layer, MobileSecureStorage.layer); | 9 | const layerBase = Layer.merge(MobileDatabase.layer, MobileSecureStorage.layer); |
| 10 | const dependentLayer = Layer.mergeAll( | 10 | const layerDependent = Layer.mergeAll( |
| 11 | MobilePreferences.layer, | 11 | MobilePreferences.layer, |
| 12 | MobileStorage.layer, | 12 | MobileStorage.layer, |
| 13 | EnvironmentCacheStore.layer, | 13 | EnvironmentCacheStore.layer, |
| 14 | ).pipe(Layer.provide(baseLayer)); | 14 | ).pipe(Layer.provide(layerBase)); |
| 15 | 15 | ||
| 16 | export const layer = Layer.merge(baseLayer, dependentLayer); | 16 | export const layer = Layer.merge(layerBase, layerDependent); |
apps/mobile/src/state/client-cache-state.ts+1-1| ... | @@ -59,7 +59,7 @@ function aggregateCacheSummary( | ... | @@ -59,7 +59,7 @@ function aggregateCacheSummary( |
| 59 | }; | 59 | }; |
| 60 | } | 60 | } |
| 61 | 61 | ||
| 62 | const clientCacheRuntime = Atom.runtime(Runtime.runtimeContextLayer); | 62 | const clientCacheRuntime = Atom.runtime(Runtime.layer); |
| 63 | 63 | ||
| 64 | export const clientCacheSummaryAtom = clientCacheRuntime | 64 | export const clientCacheSummaryAtom = clientCacheRuntime |
| 65 | .atom( | 65 | .atom( |
apps/mobile/src/state/preferences.test.ts+1-1| ... | @@ -22,7 +22,7 @@ vi.mock("../lib/runtime", async () => { | ... | @@ -22,7 +22,7 @@ vi.mock("../lib/runtime", async () => { |
| 22 | const Layer = await import("effect/Layer"); | 22 | const Layer = await import("effect/Layer"); |
| 23 | return { | 23 | return { |
| 24 | runtime: { runPromise: vi.fn() }, | 24 | runtime: { runPromise: vi.fn() }, |
| 25 | runtimeContextLayer: Layer.empty, | 25 | layer: Layer.empty, |
| 26 | }; | 26 | }; |
| 27 | }); | 27 | }); |
| 28 | 28 |
apps/mobile/src/state/preferences.ts+1-1| ... | @@ -136,7 +136,7 @@ export function createMobilePreferencesState( | ... | @@ -136,7 +136,7 @@ export function createMobilePreferencesState( |
| 136 | return { preferencesAtom, updatePreferencesAtom } as const; | 136 | return { preferencesAtom, updatePreferencesAtom } as const; |
| 137 | } | 137 | } |
| 138 | 138 | ||
| 139 | const mobilePreferencesRuntime = Atom.runtime(Runtime.runtimeContextLayer); | 139 | const mobilePreferencesRuntime = Atom.runtime(Runtime.layer); |
| 140 | export const mobilePreferencesState = createMobilePreferencesState(mobilePreferencesRuntime); | 140 | export const mobilePreferencesState = createMobilePreferencesState(mobilePreferencesRuntime); |
| 141 | 141 | ||
| 142 | export const mobilePreferencesAtom = mobilePreferencesState.preferencesAtom; | 142 | export const mobilePreferencesAtom = mobilePreferencesState.preferencesAtom; |
apps/server/integration/NetworkTransferMeasurement.integration.ts+4-4| ... | @@ -165,19 +165,19 @@ function makeWebSocketTransferRecorder(): WebSocketTransferRecorder { | ... | @@ -165,19 +165,19 @@ function makeWebSocketTransferRecorder(): WebSocketTransferRecorder { |
| 165 | }; | 165 | }; |
| 166 | } | 166 | } |
| 167 | 167 | ||
| 168 | function countingWsRpcProtocolLayer(input: { | 168 | function layerCountingWsRpcProtocol(input: { |
| 169 | readonly url: string; | 169 | readonly url: string; |
| 170 | readonly cookie: string; | 170 | readonly cookie: string; |
| 171 | readonly recorder: WebSocketTransferRecorder; | 171 | readonly recorder: WebSocketTransferRecorder; |
| 172 | }) { | 172 | }) { |
| 173 | // Socket.makeWebSocket only ever passes its `protocols` option here. | 173 | // Socket.makeWebSocket only ever passes its `protocols` option here. |
| 174 | const webSocketConstructorLayer = Layer.succeed(Socket.WebSocketConstructor, (url, protocols) => | 174 | const layerWebSocketConstructor = Layer.succeed(Socket.WebSocketConstructor, (url, protocols) => |
| 175 | input.recorder.connect(url, protocols as string | string[] | undefined, input.cookie), | 175 | input.recorder.connect(url, protocols as string | string[] | undefined, input.cookie), |
| 176 | ); | 176 | ); |
| 177 | return RpcClient.layerProtocolSocket().pipe( | 177 | return RpcClient.layerProtocolSocket().pipe( |
| 178 | Layer.provide( | 178 | Layer.provide( |
| 179 | Socket.layerWebSocket(input.url, { openTimeout: "10 seconds" }).pipe( | 179 | Socket.layerWebSocket(input.url, { openTimeout: "10 seconds" }).pipe( |
| 180 | Layer.provide(webSocketConstructorLayer), | 180 | Layer.provide(layerWebSocketConstructor), |
| 181 | ), | 181 | ), |
| 182 | ), | 182 | ), |
| 183 | Layer.provide(RpcSerialization.layerJson), | 183 | Layer.provide(RpcSerialization.layerJson), |
| ... | @@ -207,7 +207,7 @@ export const openMeasuredWsClient = Effect.fn("TransferBudget.openMeasuredWsClie | ... | @@ -207,7 +207,7 @@ export const openMeasuredWsClient = Effect.fn("TransferBudget.openMeasuredWsClie |
| 207 | const parent = yield* Effect.scope; | 207 | const parent = yield* Effect.scope; |
| 208 | const scope = yield* Scope.fork(parent); | 208 | const scope = yield* Scope.fork(parent); |
| 209 | const protocol = yield* Layer.buildWithScope( | 209 | const protocol = yield* Layer.buildWithScope( |
| 210 | countingWsRpcProtocolLayer({ url: input.url, cookie: input.cookie, recorder }), | 210 | layerCountingWsRpcProtocol({ url: input.url, cookie: input.cookie, recorder }), |
| 211 | scope, | 211 | scope, |
| 212 | ); | 212 | ); |
| 213 | const client = yield* makeCountingWsRpcClient.pipe( | 213 | const client = yield* makeCountingWsRpcClient.pipe( |
apps/server/integration/transferBudgetV2.integration.test.ts+22-22| ... | @@ -35,8 +35,8 @@ import * as HttpApi from "effect/http-api/HttpApi"; | ... | @@ -35,8 +35,8 @@ import * as HttpApi from "effect/http-api/HttpApi"; |
| 35 | import * as HttpApiBuilder from "effect/http-api/HttpApiBuilder"; | 35 | import * as HttpApiBuilder from "effect/http-api/HttpApiBuilder"; |
| 36 | import { HttpRouter, HttpServer } from "effect/http"; | 36 | import { HttpRouter, HttpServer } from "effect/http"; |
| 37 | import { Rpc, RpcGroup, RpcServer, RpcSerialization } from "effect/rpc"; | 37 | import { Rpc, RpcGroup, RpcServer, RpcSerialization } from "effect/rpc"; |
| 38 | import { SqlitePersistenceMemory } from "../src/persistence/Layers/Sqlite.ts"; | 38 | import * as SqlitePersistence from "../src/persistence/Layers/Sqlite.ts"; |
| 39 | import { OrchestrationEventStoreLive } from "../src/persistence/Layers/OrchestrationEventStore.ts"; | 39 | import * as OrchestrationEventStore from "../src/persistence/Layers/OrchestrationEventStore.ts"; |
| 40 | import * as EventStore from "../src/orchestration-v2/EventStore.ts"; | 40 | import * as EventStore from "../src/orchestration-v2/EventStore.ts"; |
| 41 | import * as EventSink from "../src/orchestration-v2/EventSink.ts"; | 41 | import * as EventSink from "../src/orchestration-v2/EventSink.ts"; |
| 42 | import * as ProjectionStore from "../src/orchestration-v2/ProjectionStore.ts"; | 42 | import * as ProjectionStore from "../src/orchestration-v2/ProjectionStore.ts"; |
| ... | @@ -44,8 +44,8 @@ import * as ThreadManagementService from "../src/orchestration-v2/ThreadManageme | ... | @@ -44,8 +44,8 @@ import * as ThreadManagementService from "../src/orchestration-v2/ThreadManageme |
| 44 | import * as ProjectStore from "../src/orchestration-v2/ProjectStore.ts"; | 44 | import * as ProjectStore from "../src/orchestration-v2/ProjectStore.ts"; |
| 45 | import * as ProjectService from "../src/project/ProjectService.ts"; | 45 | import * as ProjectService from "../src/project/ProjectService.ts"; |
| 46 | import * as ProjectEnrichmentService from "../src/project/ProjectEnrichmentService.ts"; | 46 | import * as ProjectEnrichmentService from "../src/project/ProjectEnrichmentService.ts"; |
| 47 | import { orchestrationHttpApiLayer } from "../src/orchestration-v2/http.ts"; | 47 | import * as OrchestrationHttp from "../src/orchestration-v2/http.ts"; |
| 48 | import { httpCompressionLayer } from "../src/http.ts"; | 48 | import * as ServerHttp from "../src/http.ts"; |
| 49 | import { subscribeOrchestrationV2Thread, subscribeOrchestrationV2Shell } from "../src/ws.ts"; | 49 | import { subscribeOrchestrationV2Thread, subscribeOrchestrationV2Shell } from "../src/ws.ts"; |
| 50 | import { | 50 | import { |
| 51 | measureHttpGet, | 51 | measureHttpGet, |
| ... | @@ -69,15 +69,15 @@ const decodeShellSnapshot = Schema.decodeUnknownEffect( | ... | @@ -69,15 +69,15 @@ const decodeShellSnapshot = Schema.decodeUnknownEffect( |
| 69 | Schema.fromJsonString(Schema.toCodecJson(OrchestrationV2ShellSnapshot)), | 69 | Schema.fromJsonString(Schema.toCodecJson(OrchestrationV2ShellSnapshot)), |
| 70 | ); | 70 | ); |
| 71 | 71 | ||
| 72 | const persistence = EventSink.layer.pipe( | 72 | const layerPersistence = EventSink.layer.pipe( |
| 73 | Layer.provideMerge(EventStore.layerFromOrchestrationEventStore), | 73 | Layer.provideMerge(EventStore.layerFromOrchestrationEventStore), |
| 74 | Layer.provideMerge(ProjectionStore.layer), | 74 | Layer.provideMerge(ProjectionStore.layer), |
| 75 | Layer.provideMerge(OrchestrationEventStoreLive), | 75 | Layer.provideMerge(OrchestrationEventStore.layer), |
| 76 | Layer.provideMerge(SqlitePersistenceMemory), | 76 | Layer.provideMerge(SqlitePersistence.layerMemory), |
| 77 | ); | 77 | ); |
| 78 | // The facade's import/command paths are outside this measurement. Its reads and | 78 | // The facade's import/command paths are outside this measurement. Its reads and |
| 79 | // stream delegate to the real SQL projection and event sink, as the runtime does. | 79 | // stream delegate to the real SQL projection and event sink, as the runtime does. |
| 80 | const management = Layer.unwrap( | 80 | const layerManagement = Layer.unwrap( |
| 81 | Effect.gen(function* () { | 81 | Effect.gen(function* () { |
| 82 | const projections = yield* ProjectionStore.ProjectionStoreV2; | 82 | const projections = yield* ProjectionStore.ProjectionStoreV2; |
| 83 | const sink = yield* EventSink.EventSinkV2; | 83 | const sink = yield* EventSink.EventSinkV2; |
| ... | @@ -93,7 +93,7 @@ const management = Layer.unwrap( | ... | @@ -93,7 +93,7 @@ const management = Layer.unwrap( |
| 93 | }); | 93 | }); |
| 94 | }), | 94 | }), |
| 95 | ); | 95 | ); |
| 96 | const enrichment = Layer.unwrap( | 96 | const layerEnrichment = Layer.unwrap( |
| 97 | Effect.gen(function* () { | 97 | Effect.gen(function* () { |
| 98 | const changes = yield* PubSub.unbounded<never>(); | 98 | const changes = yield* PubSub.unbounded<never>(); |
| 99 | return Layer.mock(ProjectEnrichmentService.ProjectEnrichmentService)({ | 99 | return Layer.mock(ProjectEnrichmentService.ProjectEnrichmentService)({ |
| ... | @@ -108,16 +108,16 @@ const enrichment = Layer.unwrap( | ... | @@ -108,16 +108,16 @@ const enrichment = Layer.unwrap( |
| 108 | }), | 108 | }), |
| 109 | ); | 109 | ); |
| 110 | // The transfer history has no project events, so shell streams never read a project shell. | 110 | // The transfer history has no project events, so shell streams never read a project shell. |
| 111 | const services = management.pipe( | 111 | const layerServices = layerManagement.pipe( |
| 112 | Layer.provideMerge(ProjectStore.layer), | 112 | Layer.provideMerge(ProjectStore.layer), |
| 113 | Layer.provideMerge(Layer.mock(ProjectService.ProjectService)({})), | 113 | Layer.provideMerge(Layer.mock(ProjectService.ProjectService)({})), |
| 114 | Layer.provideMerge(enrichment), | 114 | Layer.provideMerge(layerEnrichment), |
| 115 | Layer.provideMerge(persistence), | 115 | Layer.provideMerge(layerPersistence), |
| 116 | ); | 116 | ); |
| 117 | class TransferApi extends HttpApi.make("environment").add( | 117 | class TransferApi extends HttpApi.make("environment").add( |
| 118 | EnvironmentHttpApi.groups.orchestration, | 118 | EnvironmentHttpApi.groups.orchestration, |
| 119 | ) {} | 119 | ) {} |
| 120 | const auth = Layer.succeed(EnvironmentAuthenticatedAuth, (effect) => | 120 | const layerAuth = Layer.succeed(EnvironmentAuthenticatedAuth, (effect) => |
| 121 | effect.pipe( | 121 | effect.pipe( |
| 122 | Effect.provideService(EnvironmentAuthenticatedPrincipal, { | 122 | Effect.provideService(EnvironmentAuthenticatedPrincipal, { |
| 123 | sessionId: AuthSessionId.make("transfer-session"), | 123 | sessionId: AuthSessionId.make("transfer-session"), |
| ... | @@ -141,22 +141,22 @@ const group = RpcGroup.make( | ... | @@ -141,22 +141,22 @@ const group = RpcGroup.make( |
| 141 | stream: true, | 141 | stream: true, |
| 142 | }), | 142 | }), |
| 143 | ); | 143 | ); |
| 144 | const handlers = group.toLayer({ | 144 | const layerHandlers = group.toLayer({ |
| 145 | [ORCHESTRATION_V2_WS_METHODS.subscribeThread]: (input) => | 145 | [ORCHESTRATION_V2_WS_METHODS.subscribeThread]: (input) => |
| 146 | Stream.unwrap(subscribeOrchestrationV2Thread(input)), | 146 | Stream.unwrap(subscribeOrchestrationV2Thread(input)), |
| 147 | [ORCHESTRATION_V2_WS_METHODS.subscribeShell]: (input) => | 147 | [ORCHESTRATION_V2_WS_METHODS.subscribeShell]: (input) => |
| 148 | Stream.unwrap(subscribeOrchestrationV2Shell(input)), | 148 | Stream.unwrap(subscribeOrchestrationV2Shell(input)), |
| 149 | }); | 149 | }); |
| 150 | const routes = Layer.mergeAll( | 150 | const layerRoutes = Layer.mergeAll( |
| 151 | HttpApiBuilder.layer(TransferApi).pipe( | 151 | HttpApiBuilder.layer(TransferApi).pipe( |
| 152 | Layer.provide(orchestrationHttpApiLayer), | 152 | Layer.provide(OrchestrationHttp.layer), |
| 153 | Layer.provide(auth), | 153 | Layer.provide(layerAuth), |
| 154 | ), | 154 | ), |
| 155 | RpcServer.layerHttp({ group, path: "/ws", protocol: "websocket" }).pipe( | 155 | RpcServer.layerHttp({ group, path: "/ws", protocol: "websocket" }).pipe( |
| 156 | Layer.provide(handlers), | 156 | Layer.provide(layerHandlers), |
| 157 | Layer.provide(RpcSerialization.layerJson), | 157 | Layer.provide(RpcSerialization.layerJson), |
| 158 | ), | 158 | ), |
| 159 | ).pipe(Layer.provide(httpCompressionLayer), Layer.provide(NodeHttpPlatform.layer)); | 159 | ).pipe(Layer.provide(ServerHttp.layerHttpCompression), Layer.provide(NodeHttpPlatform.layer)); |
| 160 | 160 | ||
| 161 | function difference( | 161 | function difference( |
| 162 | after: { wireBytes: number; decodedBytes: number; messages: number }, | 162 | after: { wireBytes: number; decodedBytes: number; messages: number }, |
| ... | @@ -229,9 +229,9 @@ it.live( | ... | @@ -229,9 +229,9 @@ it.live( |
| 229 | yield* sink.write({ events: [threadCreated(provider)] }); | 229 | yield* sink.write({ events: [threadCreated(provider)] }); |
| 230 | for (let index = 0; index < TRANSFER_HISTORY_TURN_COUNT; index++) | 230 | for (let index = 0; index < TRANSFER_HISTORY_TURN_COUNT; index++) |
| 231 | yield* sink.write({ events: turnEvents(provider, index, false) }); | 231 | yield* sink.write({ events: turnEvents(provider, index, false) }); |
| 232 | const context = yield* Effect.context<Layer.Success<typeof services>>(); | 232 | const context = yield* Effect.context<Layer.Success<typeof layerServices>>(); |
| 233 | const server = yield* Layer.build( | 233 | const server = yield* Layer.build( |
| 234 | HttpRouter.serve(routes, { disableListenLog: true }).pipe( | 234 | HttpRouter.serve(layerRoutes, { disableListenLog: true }).pipe( |
| 235 | Layer.provide(Layer.succeedContext(context)), | 235 | Layer.provide(Layer.succeedContext(context)), |
| 236 | Layer.provideMerge( | 236 | Layer.provideMerge( |
| 237 | NodeHttpServer.layer(NodeHttp.createServer, { | 237 | NodeHttpServer.layer(NodeHttp.createServer, { |
| ... | @@ -334,7 +334,7 @@ it.live( | ... | @@ -334,7 +334,7 @@ it.live( |
| 334 | reconnectSqlStatements: counter.count() - reconnectSqlStart, | 334 | reconnectSqlStatements: counter.count() - reconnectSqlStart, |
| 335 | } satisfies TransferBudgetRun; | 335 | } satisfies TransferBudgetRun; |
| 336 | }).pipe( | 336 | }).pipe( |
| 337 | Effect.provide(Layer.fresh(services).pipe(Layer.provideMerge(NodeServices.layer))), | 337 | Effect.provide(Layer.fresh(layerServices).pipe(Layer.provideMerge(NodeServices.layer))), |
| 338 | Effect.withTracer(counter.tracer), | 338 | Effect.withTracer(counter.tracer), |
| 339 | ), | 339 | ), |
| 340 | ); | 340 | ); |
apps/server/scripts/measure-pr-preview.ts+4-4| ... | @@ -22,7 +22,7 @@ if (!repository || numbers.length === 0 || numbers.some((number) => !/^\d+$/.tes | ... | @@ -22,7 +22,7 @@ if (!repository || numbers.length === 0 || numbers.some((number) => !/^\d+$/.tes |
| 22 | 22 | ||
| 23 | type Read = { graphqlRequests: number; restRequests: number; cost: number; debug: string }; | 23 | type Read = { graphqlRequests: number; restRequests: number; cost: number; debug: string }; |
| 24 | const reads: Read[] = []; | 24 | const reads: Read[] = []; |
| 25 | const measuredProcess = Layer.effect( | 25 | const layerMeasuredProcess = Layer.effect( |
| 26 | VcsProcess.VcsProcess, | 26 | VcsProcess.VcsProcess, |
| 27 | Effect.gen(function* () { | 27 | Effect.gen(function* () { |
| 28 | const vcs = yield* VcsProcess.VcsProcess; | 28 | const vcs = yield* VcsProcess.VcsProcess; |
| ... | @@ -47,9 +47,9 @@ const measuredProcess = Layer.effect( | ... | @@ -47,9 +47,9 @@ const measuredProcess = Layer.effect( |
| 47 | }), | 47 | }), |
| 48 | ).pipe(Layer.provide(VcsProcess.layer), Layer.provide(NodeServices.layer)); | 48 | ).pipe(Layer.provide(VcsProcess.layer), Layer.provide(NodeServices.layer)); |
| 49 | 49 | ||
| 50 | const services = GitHubPullRequestCli.layer.pipe( | 50 | const layerServices = GitHubPullRequestCli.layer.pipe( |
| 51 | Layer.provide(GitHubCli.layer), | 51 | Layer.provide(GitHubCli.layer), |
| 52 | Layer.provideMerge(measuredProcess), | 52 | Layer.provideMerge(layerMeasuredProcess), |
| 53 | ); | 53 | ); |
| 54 | 54 | ||
| 55 | const decodeJson = Schema.decodeSync(Schema.fromJsonString(Schema.Unknown)); | 55 | const decodeJson = Schema.decodeSync(Schema.fromJsonString(Schema.Unknown)); |
| ... | @@ -122,7 +122,7 @@ for (const mode of ["detail", "preview"] as const) { | ... | @@ -122,7 +122,7 @@ for (const mode of ["detail", "preview"] as const) { |
| 122 | }), | 122 | }), |
| 123 | ); | 123 | ); |
| 124 | }), | 124 | }), |
| 125 | ).pipe(Effect.provide(services)), | 125 | ).pipe(Effect.provide(layerServices)), |
| 126 | ); | 126 | ); |
| 127 | for (const row of rows) await Effect.runPromise(Console.log(encodeJson(row))); | 127 | for (const row of rows) await Effect.runPromise(Console.log(encodeJson(row))); |
| 128 | } | 128 | } |
apps/server/scripts/record-codex-app-server-replay-fixture.ts+6-6| ... | @@ -1145,7 +1145,7 @@ function makeRecorder({ | ... | @@ -1145,7 +1145,7 @@ function makeRecorder({ |
| 1145 | }); | 1145 | }); |
| 1146 | } | 1146 | } |
| 1147 | 1147 | ||
| 1148 | function makeCodexLayer({ recorder }: { readonly recorder: Recorder }) { | 1148 | function layerCodex({ recorder }: { readonly recorder: Recorder }) { |
| 1149 | const clientRequestMethodById = new Map<string, string>(); | 1149 | const clientRequestMethodById = new Map<string, string>(); |
| 1150 | const serverRequestMethodById = new Map<string, string>(); | 1150 | const serverRequestMethodById = new Map<string, string>(); |
| 1151 | const clientOptions: CodexClient.CodexAppServerClientOptions = { | 1151 | const clientOptions: CodexClient.CodexAppServerClientOptions = { |
| ... | @@ -1465,7 +1465,7 @@ function runReplaySession({ | ... | @@ -1465,7 +1465,7 @@ function runReplaySession({ |
| 1465 | return thread; | 1465 | return thread; |
| 1466 | }).pipe( | 1466 | }).pipe( |
| 1467 | Effect.provide( | 1467 | Effect.provide( |
| 1468 | makeCodexLayer({ | 1468 | layerCodex({ |
| 1469 | recorder, | 1469 | recorder, |
| 1470 | }), | 1470 | }), |
| 1471 | ), | 1471 | ), |
| ... | @@ -1486,7 +1486,7 @@ function runReplaySession({ | ... | @@ -1486,7 +1486,7 @@ function runReplaySession({ |
| 1486 | yield* runTurnStep(client, thread.thread.id, secondStep); | 1486 | yield* runTurnStep(client, thread.thread.id, secondStep); |
| 1487 | }).pipe( | 1487 | }).pipe( |
| 1488 | Effect.provide( | 1488 | Effect.provide( |
| 1489 | makeCodexLayer({ | 1489 | layerCodex({ |
| 1490 | recorder, | 1490 | recorder, |
| 1491 | }), | 1491 | }), |
| 1492 | ), | 1492 | ), |
| ... | @@ -1519,7 +1519,7 @@ function runReplaySession({ | ... | @@ -1519,7 +1519,7 @@ function runReplaySession({ |
| 1519 | yield* runTurnStep(client, thread.thread.id, first); | 1519 | yield* runTurnStep(client, thread.thread.id, first); |
| 1520 | yield* runTurnStep(client, thread.thread.id, second); | 1520 | yield* runTurnStep(client, thread.thread.id, second); |
| 1521 | return thread.thread.id; | 1521 | return thread.thread.id; |
| 1522 | }).pipe(Effect.provide(makeCodexLayer({ recorder }))); | 1522 | }).pipe(Effect.provide(layerCodex({ recorder }))); |
| 1523 | 1523 | ||
| 1524 | yield* recorder.writeRecord({ | 1524 | yield* recorder.writeRecord({ |
| 1525 | type: "runtime_exit", | 1525 | type: "runtime_exit", |
| ... | @@ -1536,7 +1536,7 @@ function runReplaySession({ | ... | @@ -1536,7 +1536,7 @@ function runReplaySession({ |
| 1536 | yield* revertCodexThread(client, threadId, rollback.numTurns); | 1536 | yield* revertCodexThread(client, threadId, rollback.numTurns); |
| 1537 | yield* client.request("thread/resume", resumeParams); | 1537 | yield* client.request("thread/resume", resumeParams); |
| 1538 | yield* runTurnStep(client, threadId, after); | 1538 | yield* runTurnStep(client, threadId, after); |
| 1539 | }).pipe(Effect.provide(makeCodexLayer({ recorder }))); | 1539 | }).pipe(Effect.provide(layerCodex({ recorder }))); |
| 1540 | return; | 1540 | return; |
| 1541 | } | 1541 | } |
| 1542 | 1542 | ||
| ... | @@ -1587,7 +1587,7 @@ function runReplaySession({ | ... | @@ -1587,7 +1587,7 @@ function runReplaySession({ |
| 1587 | } | 1587 | } |
| 1588 | }).pipe( | 1588 | }).pipe( |
| 1589 | Effect.provide( | 1589 | Effect.provide( |
| 1590 | makeCodexLayer({ | 1590 | layerCodex({ |
| 1591 | recorder, | 1591 | recorder, |
| 1592 | }), | 1592 | }), |
| 1593 | ), | 1593 | ), |
apps/server/scripts/record-grok-acp-replay-fixture.ts+5-7| ... | @@ -40,10 +40,8 @@ import { provideDeterministicTestRuntime } from "../src/orchestration-v2/testkit | ... | @@ -40,10 +40,8 @@ import { provideDeterministicTestRuntime } from "../src/orchestration-v2/testkit |
| 40 | import { ORCHESTRATOR_REPLAY_FIXTURES } from "../src/orchestration-v2/testkit/fixtures/index.ts"; | 40 | import { ORCHESTRATOR_REPLAY_FIXTURES } from "../src/orchestration-v2/testkit/fixtures/index.ts"; |
| 41 | import { materializeFixtureInput } from "../src/orchestration-v2/testkit/fixtures/shared.ts"; | 41 | import { materializeFixtureInput } from "../src/orchestration-v2/testkit/fixtures/shared.ts"; |
| 42 | import { runOrchestratorV2Scenario } from "../src/orchestration-v2/testkit/OrchestratorScenario.ts"; | 42 | import { runOrchestratorV2Scenario } from "../src/orchestration-v2/testkit/OrchestratorScenario.ts"; |
| 43 | import { | 43 | import { makeReplayServerConfig } from "../src/orchestration-v2/testkit/ProviderReplayHarness.ts"; |
| 44 | makeOrchestratorV2ReplayLayerWithRegistry, | 44 | import * as ProviderReplayHarness from "../src/orchestration-v2/testkit/ProviderReplayHarness.ts"; |
| 45 | makeReplayServerConfig, | ||
| 46 | } from "../src/orchestration-v2/testkit/ProviderReplayHarness.ts"; | ||
| 47 | import { checkpointWorkspace } from "../src/orchestration-v2/testkit/ReplayFixtureWorkspace.ts"; | 45 | import { checkpointWorkspace } from "../src/orchestration-v2/testkit/ReplayFixtureWorkspace.ts"; |
| 48 | import { makeGrokAcpRuntime } from "../src/provider/acp/GrokAcpSupport.ts"; | 46 | import { makeGrokAcpRuntime } from "../src/provider/acp/GrokAcpSupport.ts"; |
| 49 | import { buildRuntimeInstructions } from "../src/provider/RuntimeInstructions.ts"; | 47 | import { buildRuntimeInstructions } from "../src/provider/RuntimeInstructions.ts"; |
| ... | @@ -444,7 +442,7 @@ const recordScenario = Effect.fn("recordGrokScenario")(function* (fixtureName: s | ... | @@ -444,7 +442,7 @@ const recordScenario = Effect.fn("recordGrokScenario")(function* (fixtureName: s |
| 444 | 442 | ||
| 445 | const tee = makeWireTee(); | 443 | const tee = makeWireTee(); |
| 446 | const settings = { ...DEFAULT_GROK_SETTINGS, binaryPath: process.env.T3_GROK_BIN ?? "grok" }; | 444 | const settings = { ...DEFAULT_GROK_SETTINGS, binaryPath: process.env.T3_GROK_BIN ?? "grok" }; |
| 447 | const registryLayer = ProviderAdapterRegistry.makeLayerEffect( | 445 | const layerRegistry = ProviderAdapterRegistry.layerFromAdaptersEffect( |
| 448 | Effect.gen(function* () { | 446 | Effect.gen(function* () { |
| 449 | const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; | 447 | const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; |
| 450 | const environment = yield* HostProcessEnvironment; | 448 | const environment = yield* HostProcessEnvironment; |
| ... | @@ -532,9 +530,9 @@ const recordScenario = Effect.fn("recordGrokScenario")(function* (fixtureName: s | ... | @@ -532,9 +530,9 @@ const recordScenario = Effect.fn("recordGrokScenario")(function* (fixtureName: s |
| 532 | ); | 530 | ); |
| 533 | const result = yield* runOrchestratorV2Scenario(scenario, { afterSteps: waitForGrokIdle }).pipe( | 531 | const result = yield* runOrchestratorV2Scenario(scenario, { afterSteps: waitForGrokIdle }).pipe( |
| 534 | Effect.provide( | 532 | Effect.provide( |
| 535 | makeOrchestratorV2ReplayLayerWithRegistry( | 533 | ProviderReplayHarness.layerWithRegistry( |
| 536 | scenario, | 534 | scenario, |
| 537 | registryLayer, | 535 | layerRegistry, |
| 538 | variant.runContinuationWorker === true ? { runContinuationWorker: true } : {}, | 536 | variant.runContinuationWorker === true ? { runContinuationWorker: true } : {}, |
| 539 | ), | 537 | ), |
| 540 | ), | 538 | ), |
apps/server/scripts/record-pi-rpc-replay-fixture.ts+4-4| ... | @@ -23,12 +23,12 @@ import { TestClock } from "effect/testing"; | ... | @@ -23,12 +23,12 @@ import { TestClock } from "effect/testing"; |
| 23 | import { ChildProcess, ChildProcessSpawner } from "effect/process"; | 23 | import { ChildProcess, ChildProcessSpawner } from "effect/process"; |
| 24 | 24 | ||
| 25 | import { | 25 | import { |
| 26 | makePiProviderAdapterRegistryLayer, | ||
| 27 | makePiRecordingSpawner, | 26 | makePiRecordingSpawner, |
| 28 | PI_REPLAY_ANY, | 27 | PI_REPLAY_ANY, |
| 29 | PI_RPC_REPLAY_PROTOCOL, | 28 | PI_RPC_REPLAY_PROTOCOL, |
| 30 | PiOrchestratorReplayHarness, | 29 | PiOrchestratorReplayHarness, |
| 31 | } from "../src/orchestration-v2/Adapters/PiAdapterV2.testkit.ts"; | 30 | } from "../src/orchestration-v2/Adapters/PiAdapterV2.testkit.ts"; |
| 31 | import * as PiAdapterV2Testkit from "../src/orchestration-v2/Adapters/PiAdapterV2.testkit.ts"; | ||
| 32 | import { PI_PROVIDER } from "../src/orchestration-v2/Adapters/PiAdapterV2.ts"; | 32 | import { PI_PROVIDER } from "../src/orchestration-v2/Adapters/PiAdapterV2.ts"; |
| 33 | import * as IdAllocator from "../src/orchestration-v2/IdAllocator.ts"; | 33 | import * as IdAllocator from "../src/orchestration-v2/IdAllocator.ts"; |
| 34 | import { provideDeterministicTestRuntime } from "../src/orchestration-v2/testkit/DeterministicRuntime.ts"; | 34 | import { provideDeterministicTestRuntime } from "../src/orchestration-v2/testkit/DeterministicRuntime.ts"; |
| ... | @@ -251,7 +251,7 @@ const record = Effect.gen(function* () { | ... | @@ -251,7 +251,7 @@ const record = Effect.gen(function* () { |
| 251 | scenario: fixture.name, | 251 | scenario: fixture.name, |
| 252 | entries: [], | 252 | entries: [], |
| 253 | } satisfies ProviderReplayTranscript; | 253 | } satisfies ProviderReplayTranscript; |
| 254 | const recordingSpawner = Layer.effect( | 254 | const layerRecordingSpawner = Layer.effect( |
| 255 | ChildProcessSpawner.ChildProcessSpawner, | 255 | ChildProcessSpawner.ChildProcessSpawner, |
| 256 | Effect.map(Effect.service(ChildProcessSpawner.ChildProcessSpawner), (live) => | 256 | Effect.map(Effect.service(ChildProcessSpawner.ChildProcessSpawner), (live) => |
| 257 | makePiRecordingSpawner(live, entries), | 257 | makePiRecordingSpawner(live, entries), |
| ... | @@ -283,7 +283,7 @@ const record = Effect.gen(function* () { | ... | @@ -283,7 +283,7 @@ const record = Effect.gen(function* () { |
| 283 | driver: PI_PROVIDER, | 283 | driver: PI_PROVIDER, |
| 284 | decodeTranscript: Effect.succeed, | 284 | decodeTranscript: Effect.succeed, |
| 285 | makeProviderAdapterRegistryLayer: () => | 285 | makeProviderAdapterRegistryLayer: () => |
| 286 | makePiProviderAdapterRegistryLayer({ | 286 | PiAdapterV2Testkit.layer({ |
| 287 | scenario: fixture.name, | 287 | scenario: fixture.name, |
| 288 | binaryPath: piBinary, | 288 | binaryPath: piBinary, |
| 289 | launchArgs, | 289 | launchArgs, |
| ... | @@ -291,7 +291,7 @@ const record = Effect.gen(function* () { | ... | @@ -291,7 +291,7 @@ const record = Effect.gen(function* () { |
| 291 | { name: "PI_CODING_AGENT_SESSION_DIR", value: sessionDir, sensitive: false }, | 291 | { name: "PI_CODING_AGENT_SESSION_DIR", value: sessionDir, sensitive: false }, |
| 292 | { name: "PI_SKIP_VERSION_CHECK", value: "1", sensitive: false }, | 292 | { name: "PI_SKIP_VERSION_CHECK", value: "1", sensitive: false }, |
| 293 | ], | 293 | ], |
| 294 | spawner: recordingSpawner, | 294 | spawner: layerRecordingSpawner, |
| 295 | }), | 295 | }), |
| 296 | }, | 296 | }, |
| 297 | continuationOptions, | 297 | continuationOptions, |
apps/server/scripts/verify-background-live.ts+4-4| ... | @@ -295,11 +295,11 @@ try { | ... | @@ -295,11 +295,11 @@ try { |
| 295 | const ticket = Schema.decodeUnknownSync(Schema.Struct({ ticket: Schema.String }))( | 295 | const ticket = Schema.decodeUnknownSync(Schema.Struct({ ticket: Schema.String }))( |
| 296 | await ticketResponse.json(), | 296 | await ticketResponse.json(), |
| 297 | ); | 297 | ); |
| 298 | const socket = Socket.layerWebSocket( | 298 | const layerSocket = Socket.layerWebSocket( |
| 299 | `${origin.replace("http:", "ws:")}/ws?orchestrationProtocol=${ORCHESTRATION_PROTOCOL_VERSION}&wsTicket=${encodeURIComponent(ticket.ticket)}`, | 299 | `${origin.replace("http:", "ws:")}/ws?orchestrationProtocol=${ORCHESTRATION_PROTOCOL_VERSION}&wsTicket=${encodeURIComponent(ticket.ticket)}`, |
| 300 | ).pipe(Layer.provide(Socket.layerWebSocketConstructorGlobal)); | 300 | ).pipe(Layer.provide(Socket.layerWebSocketConstructorGlobal)); |
| 301 | const protocol = RpcClient.layerProtocolSocket().pipe( | 301 | const layerProtocol = RpcClient.layerProtocolSocket().pipe( |
| 302 | Layer.provide(socket), | 302 | Layer.provide(layerSocket), |
| 303 | Layer.provide(RpcSerialization.layerJson), | 303 | Layer.provide(RpcSerialization.layerJson), |
| 304 | ); | 304 | ); |
| 305 | const threadId = ThreadId.make(NodeCrypto.randomUUID()); | 305 | const threadId = ThreadId.make(NodeCrypto.randomUUID()); |
| ... | @@ -483,7 +483,7 @@ try { | ... | @@ -483,7 +483,7 @@ try { |
| 483 | .filter((message) => message.role === "assistant" && message.text.includes(secret)) | 483 | .filter((message) => message.role === "assistant" && message.text.includes(secret)) |
| 484 | .map((message) => message.id), | 484 | .map((message) => message.id), |
| 485 | }; | 485 | }; |
| 486 | }).pipe(Effect.scoped, Effect.provide(protocol), Effect.timeout(timeoutMs)); | 486 | }).pipe(Effect.scoped, Effect.provide(layerProtocol), Effect.timeout(timeoutMs)); |
| 487 | const finished = await Effect.runPromise(program, { signal: abort.signal }); | 487 | const finished = await Effect.runPromise(program, { signal: abort.signal }); |
| 488 | verdict = finished; | 488 | verdict = finished; |
| 489 | // Check persistence through an actual clean server restart on the same SQLite DB. | 489 | // Check persistence through an actual clean server restart on the same SQLite DB. |
apps/server/src/assets/AssetAccess.test.ts+40-40| ... | @@ -48,7 +48,7 @@ vi.mock("node:os", async (importOriginal) => { | ... | @@ -48,7 +48,7 @@ vi.mock("node:os", async (importOriginal) => { |
| 48 | return { ...actual, homedir: vi.fn(actual.homedir) }; | 48 | return { ...actual, homedir: vi.fn(actual.homedir) }; |
| 49 | }); | 49 | }); |
| 50 | 50 | ||
| 51 | const configLayer = ServerConfig.ServerConfig.layerTest(process.cwd(), { | 51 | const layerConfig = ServerConfig.ServerConfig.layerTest(process.cwd(), { |
| 52 | prefix: "t3-asset-access-test-", | 52 | prefix: "t3-asset-access-test-", |
| 53 | }); | 53 | }); |
| 54 | // A PNG header is enough for the dimension read: signature, then IHDR width and height. | 54 | // A PNG header is enough for the dimension read: signature, then IHDR width and height. |
| ... | @@ -103,7 +103,7 @@ const oversizedScreenshotItem = { | ... | @@ -103,7 +103,7 @@ const oversizedScreenshotItem = { |
| 103 | }, | 103 | }, |
| 104 | }; | 104 | }; |
| 105 | 105 | ||
| 106 | const testLayer = Layer.mergeAll( | 106 | const layerTest = Layer.mergeAll( |
| 107 | NodeHttpPlatform.layer, | 107 | NodeHttpPlatform.layer, |
| 108 | Layer.mock(Orchestrator.OrchestratorV2)({ | 108 | Layer.mock(Orchestrator.OrchestratorV2)({ |
| 109 | getTurnItem: ({ itemId }) => | 109 | getTurnItem: ({ itemId }) => |
| ... | @@ -115,14 +115,14 @@ const testLayer = Layer.mergeAll( | ... | @@ -115,14 +115,14 @@ const testLayer = Layer.mergeAll( |
| 115 | : null, | 115 | : null, |
| 116 | ), | 116 | ), |
| 117 | }), | 117 | }), |
| 118 | configLayer, | 118 | layerConfig, |
| 119 | WorkspacePaths.layer, | 119 | WorkspacePaths.layer, |
| 120 | ProjectFaviconResolver.layer.pipe( | 120 | ProjectFaviconResolver.layer.pipe( |
| 121 | Layer.provide(WorkspacePaths.layer), | 121 | Layer.provide(WorkspacePaths.layer), |
| 122 | Layer.provide(T3ProjectFileLoader.layer), | 122 | Layer.provide(T3ProjectFileLoader.layer), |
| 123 | ), | 123 | ), |
| 124 | NativeAppIconResolver.layer.pipe(Layer.provide(configLayer)), | 124 | NativeAppIconResolver.layer.pipe(Layer.provide(layerConfig)), |
| 125 | ServerSecretStore.layer.pipe(Layer.provide(configLayer)), | 125 | ServerSecretStore.layer.pipe(Layer.provide(layerConfig)), |
| 126 | ).pipe(Layer.provideMerge(NodeServices.layer)); | 126 | ).pipe(Layer.provideMerge(NodeServices.layer)); |
| 127 | 127 | ||
| 128 | describe("AssetAccess", () => { | 128 | describe("AssetAccess", () => { |
| ... | @@ -198,7 +198,7 @@ describe("AssetAccess", () => { | ... | @@ -198,7 +198,7 @@ describe("AssetAccess", () => { |
| 198 | resource: { ...resource, itemId: oversizedScreenshotItem.id }, | 198 | resource: { ...resource, itemId: oversizedScreenshotItem.id }, |
| 199 | }).pipe(Effect.flip); | 199 | }).pipe(Effect.flip); |
| 200 | expect(oversized._tag).toBe("AssetWorkspaceAssetNotFoundError"); | 200 | expect(oversized._tag).toBe("AssetWorkspaceAssetNotFoundError"); |
| 201 | }).pipe(Effect.provide(testLayer)), | 201 | }).pipe(Effect.provide(layerTest)), |
| 202 | ); | 202 | ); |
| 203 | 203 | ||
| 204 | it.effect("issues exact URLs for media and browser documents outside the workspace", () => | 204 | it.effect("issues exact URLs for media and browser documents outside the workspace", () => |
| ... | @@ -234,7 +234,7 @@ describe("AssetAccess", () => { | ... | @@ -234,7 +234,7 @@ describe("AssetAccess", () => { |
| 234 | expect(yield* resolveAsset(token, `../${name}`)).toBeNull(); | 234 | expect(yield* resolveAsset(token, `../${name}`)).toBeNull(); |
| 235 | expect(yield* resolveAsset(`${token}tampered`, name)).toBeNull(); | 235 | expect(yield* resolveAsset(`${token}tampered`, name)).toBeNull(); |
| 236 | } | 236 | } |
| 237 | }).pipe(Effect.provide(testLayer)), | 237 | }).pipe(Effect.provide(layerTest)), |
| 238 | ); | 238 | ); |
| 239 | 239 | ||
| 240 | it.effect("reports pixel dimensions from an image header and nothing for other files", () => | 240 | it.effect("reports pixel dimensions from an image header and nothing for other files", () => |
| ... | @@ -258,7 +258,7 @@ describe("AssetAccess", () => { | ... | @@ -258,7 +258,7 @@ describe("AssetAccess", () => { |
| 258 | expect((yield* issue("shot.png")).imageDimensions).toEqual({ width: 1600, height: 900 }); | 258 | expect((yield* issue("shot.png")).imageDimensions).toEqual({ width: 1600, height: 900 }); |
| 259 | expect((yield* issue("clip.mp4")).imageDimensions).toBeUndefined(); | 259 | expect((yield* issue("clip.mp4")).imageDimensions).toBeUndefined(); |
| 260 | expect((yield* issue("broken.png")).imageDimensions).toBeUndefined(); | 260 | expect((yield* issue("broken.png")).imageDimensions).toBeUndefined(); |
| 261 | }).pipe(Effect.provide(testLayer)), | 261 | }).pipe(Effect.provide(layerTest)), |
| 262 | ); | 262 | ); |
| 263 | 263 | ||
| 264 | it.effect("resolves relative media paths from the thread workspace, including outside it", () => | 264 | it.effect("resolves relative media paths from the thread workspace, including outside it", () => |
| ... | @@ -284,7 +284,7 @@ describe("AssetAccess", () => { | ... | @@ -284,7 +284,7 @@ describe("AssetAccess", () => { |
| 284 | path: yield* fs.realPath(filePath), | 284 | path: yield* fs.realPath(filePath), |
| 285 | }); | 285 | }); |
| 286 | } | 286 | } |
| 287 | }).pipe(Effect.provide(testLayer)), | 287 | }).pipe(Effect.provide(layerTest)), |
| 288 | ); | 288 | ); |
| 289 | 289 | ||
| 290 | it.effect("resolves home-relative media paths independently of the workspace", () => | 290 | it.effect("resolves home-relative media paths independently of the workspace", () => |
| ... | @@ -333,7 +333,7 @@ describe("AssetAccess", () => { | ... | @@ -333,7 +333,7 @@ describe("AssetAccess", () => { |
| 333 | } finally { | 333 | } finally { |
| 334 | homeSpy.mockRestore(); | 334 | homeSpy.mockRestore(); |
| 335 | } | 335 | } |
| 336 | }).pipe(Effect.provide(testLayer)), | 336 | }).pipe(Effect.provide(layerTest)), |
| 337 | ); | 337 | ); |
| 338 | 338 | ||
| 339 | it.effect.skipIf(!symlinksSupported)( | 339 | it.effect.skipIf(!symlinksSupported)( |
| ... | @@ -371,7 +371,7 @@ describe("AssetAccess", () => { | ... | @@ -371,7 +371,7 @@ describe("AssetAccess", () => { |
| 371 | }, | 371 | }, |
| 372 | }).pipe(Effect.flip); | 372 | }).pipe(Effect.flip); |
| 373 | expect(directoryError._tag).toBe("AssetWorkspaceAssetNotFoundError"); | 373 | expect(directoryError._tag).toBe("AssetWorkspaceAssetNotFoundError"); |
| 374 | }).pipe(Effect.provide(testLayer)), | 374 | }).pipe(Effect.provide(layerTest)), |
| 375 | ); | 375 | ); |
| 376 | 376 | ||
| 377 | it.effect.skipIf(!symlinksSupported)( | 377 | it.effect.skipIf(!symlinksSupported)( |
| ... | @@ -403,7 +403,7 @@ describe("AssetAccess", () => { | ... | @@ -403,7 +403,7 @@ describe("AssetAccess", () => { |
| 403 | yield* fs.remove(filePath); | 403 | yield* fs.remove(filePath); |
| 404 | yield* fs.symlink(replacementPath, filePath); | 404 | yield* fs.symlink(replacementPath, filePath); |
| 405 | expect(yield* resolveAsset(token, name)).toBeNull(); | 405 | expect(yield* resolveAsset(token, name)).toBeNull(); |
| 406 | }).pipe(Effect.provide(testLayer)), | 406 | }).pipe(Effect.provide(layerTest)), |
| 407 | ); | 407 | ); |
| 408 | 408 | ||
| 409 | it.effect.skipIf(!symlinksSupported)( | 409 | it.effect.skipIf(!symlinksSupported)( |
| ... | @@ -442,7 +442,7 @@ describe("AssetAccess", () => { | ... | @@ -442,7 +442,7 @@ describe("AssetAccess", () => { |
| 442 | yield* fs.remove(filePath); | 442 | yield* fs.remove(filePath); |
| 443 | yield* fs.rename(savedPath, filePath); | 443 | yield* fs.rename(savedPath, filePath); |
| 444 | } | 444 | } |
| 445 | }).pipe(Effect.provide(testLayer)), | 445 | }).pipe(Effect.provide(layerTest)), |
| 446 | ); | 446 | ); |
| 447 | 447 | ||
| 448 | it.effect.skipIf(!symlinksSupported)( | 448 | it.effect.skipIf(!symlinksSupported)( |
| ... | @@ -478,7 +478,7 @@ describe("AssetAccess", () => { | ... | @@ -478,7 +478,7 @@ describe("AssetAccess", () => { |
| 478 | Effect.provideService(FileSystem.FileSystem, swappingFileSystem), | 478 | Effect.provideService(FileSystem.FileSystem, swappingFileSystem), |
| 479 | ), | 479 | ), |
| 480 | ).toBeNull(); | 480 | ).toBeNull(); |
| 481 | }).pipe(Effect.provide(testLayer)), | 481 | }).pipe(Effect.provide(layerTest)), |
| 482 | ); | 482 | ); |
| 483 | 483 | ||
| 484 | it.effect.skipIf(!symlinksSupported)( | 484 | it.effect.skipIf(!symlinksSupported)( |
| ... | @@ -512,7 +512,7 @@ describe("AssetAccess", () => { | ... | @@ -512,7 +512,7 @@ describe("AssetAccess", () => { |
| 512 | expect(yield* openMediaFile(canonicalPath)).toBeNull(); | 512 | expect(yield* openMediaFile(canonicalPath)).toBeNull(); |
| 513 | expect(opened).toBeDefined(); | 513 | expect(opened).toBeDefined(); |
| 514 | expect(opened?.fd).toBe(-1); | 514 | expect(opened?.fd).toBe(-1); |
| 515 | }).pipe(Effect.provide(testLayer)), | 515 | }).pipe(Effect.provide(layerTest)), |
| 516 | ); | 516 | ); |
| 517 | 517 | ||
| 518 | it.effect.skipIf(!symlinksSupported)( | 518 | it.effect.skipIf(!symlinksSupported)( |
| ... | @@ -570,7 +570,7 @@ describe("AssetAccess", () => { | ... | @@ -570,7 +570,7 @@ describe("AssetAccess", () => { |
| 570 | Effect.provideService(FileSystem.FileSystem, swappingFileSystem), | 570 | Effect.provideService(FileSystem.FileSystem, swappingFileSystem), |
| 571 | ), | 571 | ), |
| 572 | ).toBeNull(); | 572 | ).toBeNull(); |
| 573 | }).pipe(Effect.provide(testLayer)), | 573 | }).pipe(Effect.provide(layerTest)), |
| 574 | ); | 574 | ); |
| 575 | 575 | ||
| 576 | it.effect("keeps in-place edits readable but requires a new URL after atomic replacement", () => | 576 | it.effect("keeps in-place edits readable but requires a new URL after atomic replacement", () => |
| ... | @@ -620,7 +620,7 @@ describe("AssetAccess", () => { | ... | @@ -620,7 +620,7 @@ describe("AssetAccess", () => { |
| 620 | renewedSuffix.slice(renewedSeparator + 1), | 620 | renewedSuffix.slice(renewedSeparator + 1), |
| 621 | ), | 621 | ), |
| 622 | ).toBeNull(); | 622 | ).toBeNull(); |
| 623 | }).pipe(Effect.provide(testLayer)), | 623 | }).pipe(Effect.provide(layerTest)), |
| 624 | ); | 624 | ); |
| 625 | 625 | ||
| 626 | it.effect("issues workspace URLs that resolve the entry file and sibling assets", () => | 626 | it.effect("issues workspace URLs that resolve the entry file and sibling assets", () => |
| ... | @@ -661,7 +661,7 @@ describe("AssetAccess", () => { | ... | @@ -661,7 +661,7 @@ describe("AssetAccess", () => { |
| 661 | expect(yield* resolveAsset(token, "../secret.txt")).toBeNull(); | 661 | expect(yield* resolveAsset(token, "../secret.txt")).toBeNull(); |
| 662 | expect(yield* resolveAsset(token, ".env")).toBeNull(); | 662 | expect(yield* resolveAsset(token, ".env")).toBeNull(); |
| 663 | expect(yield* resolveAsset(`${token}tampered`, "report.html")).toBeNull(); | 663 | expect(yield* resolveAsset(`${token}tampered`, "report.html")).toBeNull(); |
| 664 | }).pipe(Effect.provide(testLayer)), | 664 | }).pipe(Effect.provide(layerTest)), |
| 665 | ); | 665 | ); |
| 666 | 666 | ||
| 667 | it.effect("rejects workspace files outside the authorized root", () => | 667 | it.effect("rejects workspace files outside the authorized root", () => |
| ... | @@ -695,7 +695,7 @@ describe("AssetAccess", () => { | ... | @@ -695,7 +695,7 @@ describe("AssetAccess", () => { |
| 695 | }, | 695 | }, |
| 696 | }); | 696 | }); |
| 697 | expect(error.cause).toBeInstanceOf(WorkspacePaths.WorkspacePathOutsideRootError); | 697 | expect(error.cause).toBeInstanceOf(WorkspacePaths.WorkspacePathOutsideRootError); |
| 698 | }).pipe(Effect.provide(testLayer)), | 698 | }).pipe(Effect.provide(layerTest)), |
| 699 | ); | 699 | ); |
| 700 | 700 | ||
| 701 | it.effect("issues draft workspace URLs without a thread", () => | 701 | it.effect("issues draft workspace URLs without a thread", () => |
| ... | @@ -729,7 +729,7 @@ describe("AssetAccess", () => { | ... | @@ -729,7 +729,7 @@ describe("AssetAccess", () => { |
| 729 | path: canonicalCssPath, | 729 | path: canonicalCssPath, |
| 730 | }); | 730 | }); |
| 731 | expect(yield* resolveAsset(token, "../secret.txt")).toBeNull(); | 731 | expect(yield* resolveAsset(token, "../secret.txt")).toBeNull(); |
| 732 | }).pipe(Effect.provide(testLayer)), | 732 | }).pipe(Effect.provide(layerTest)), |
| 733 | ); | 733 | ); |
| 734 | 734 | ||
| 735 | it.effect("serves absolute draft media files exactly, wherever they live", () => | 735 | it.effect("serves absolute draft media files exactly, wherever they live", () => |
| ... | @@ -760,7 +760,7 @@ describe("AssetAccess", () => { | ... | @@ -760,7 +760,7 @@ describe("AssetAccess", () => { |
| 760 | mimeType: "video/mp4", | 760 | mimeType: "video/mp4", |
| 761 | }); | 761 | }); |
| 762 | expect(yield* resolveAsset(token, "other.mp4")).toBeNull(); | 762 | expect(yield* resolveAsset(token, "other.mp4")).toBeNull(); |
| 763 | }).pipe(Effect.provide(testLayer)), | 763 | }).pipe(Effect.provide(layerTest)), |
| 764 | ); | 764 | ); |
| 765 | 765 | ||
| 766 | it.effect("falls back to the resource cwd for relative draft paths", () => | 766 | it.effect("falls back to the resource cwd for relative draft paths", () => |
| ... | @@ -785,7 +785,7 @@ describe("AssetAccess", () => { | ... | @@ -785,7 +785,7 @@ describe("AssetAccess", () => { |
| 785 | kind: "file", | 785 | kind: "file", |
| 786 | path: canonicalHtmlPath, | 786 | path: canonicalHtmlPath, |
| 787 | }); | 787 | }); |
| 788 | }).pipe(Effect.provide(testLayer)), | 788 | }).pipe(Effect.provide(layerTest)), |
| 789 | ); | 789 | ); |
| 790 | 790 | ||
| 791 | it.effect("preserves non-missing canonical path failures when issuing asset URLs", () => | 791 | it.effect("preserves non-missing canonical path failures when issuing asset URLs", () => |
| ... | @@ -827,7 +827,7 @@ describe("AssetAccess", () => { | ... | @@ -827,7 +827,7 @@ describe("AssetAccess", () => { |
| 827 | }, | 827 | }, |
| 828 | }); | 828 | }); |
| 829 | expect(error.cause).toBe(cause); | 829 | expect(error.cause).toBe(cause); |
| 830 | }).pipe(Effect.provide(testLayer)), | 830 | }).pipe(Effect.provide(layerTest)), |
| 831 | ); | 831 | ); |
| 832 | 832 | ||
| 833 | it.effect("issues exact workspace URLs for image previews", () => | 833 | it.effect("issues exact workspace URLs for image previews", () => |
| ... | @@ -863,7 +863,7 @@ describe("AssetAccess", () => { | ... | @@ -863,7 +863,7 @@ describe("AssetAccess", () => { |
| 863 | }); | 863 | }); |
| 864 | expect(yield* resolveAsset(token, "other.png")).toBeNull(); | 864 | expect(yield* resolveAsset(token, "other.png")).toBeNull(); |
| 865 | expect(yield* resolveAsset(token, "../icon.png")).toBeNull(); | 865 | expect(yield* resolveAsset(token, "../icon.png")).toBeNull(); |
| 866 | }).pipe(Effect.provide(testLayer)), | 866 | }).pipe(Effect.provide(layerTest)), |
| 867 | ); | 867 | ); |
| 868 | 868 | ||
| 869 | it.effect("previews workspace files with literal fragment characters in their paths", () => | 869 | it.effect("previews workspace files with literal fragment characters in their paths", () => |
| ... | @@ -906,7 +906,7 @@ describe("AssetAccess", () => { | ... | @@ -906,7 +906,7 @@ describe("AssetAccess", () => { |
| 906 | workspaceRoot: root, | 906 | workspaceRoot: root, |
| 907 | }).pipe(Effect.flip); | 907 | }).pipe(Effect.flip); |
| 908 | expect(error).toBeInstanceOf(AssetPreviewTypeValidationError); | 908 | expect(error).toBeInstanceOf(AssetPreviewTypeValidationError); |
| 909 | }).pipe(Effect.provide(testLayer)), | 909 | }).pipe(Effect.provide(layerTest)), |
| 910 | ); | 910 | ); |
| 911 | 911 | ||
| 912 | it.effect("issues exact attachment capabilities by attachment id", () => | 912 | it.effect("issues exact attachment capabilities by attachment id", () => |
| ... | @@ -930,7 +930,7 @@ describe("AssetAccess", () => { | ... | @@ -930,7 +930,7 @@ describe("AssetAccess", () => { |
| 930 | kind: "file", | 930 | kind: "file", |
| 931 | path: attachmentPath, | 931 | path: attachmentPath, |
| 932 | }); | 932 | }); |
| 933 | }).pipe(Effect.provide(testLayer)), | 933 | }).pipe(Effect.provide(layerTest)), |
| 934 | ); | 934 | ); |
| 935 | 935 | ||
| 936 | it.effect("serves video attachments inline", () => | 936 | it.effect("serves video attachments inline", () => |
| ... | @@ -962,7 +962,7 @@ describe("AssetAccess", () => { | ... | @@ -962,7 +962,7 @@ describe("AssetAccess", () => { |
| 962 | fileName: "demo.mp4", | 962 | fileName: "demo.mp4", |
| 963 | mimeType: "video/mp4", | 963 | mimeType: "video/mp4", |
| 964 | }); | 964 | }); |
| 965 | }).pipe(Effect.provide(testLayer)), | 965 | }).pipe(Effect.provide(layerTest)), |
| 966 | ); | 966 | ); |
| 967 | it.effect("issues signed native application icon capabilities", () => | 967 | it.effect("issues signed native application icon capabilities", () => |
| 968 | Effect.gen(function* () { | 968 | Effect.gen(function* () { |
| ... | @@ -977,7 +977,7 @@ describe("AssetAccess", () => { | ... | @@ -977,7 +977,7 @@ describe("AssetAccess", () => { |
| 977 | new RegExp(`^${ASSET_ROUTE_PREFIX}/[^/]+/native-app-icon\\.png$`, "u"), | 977 | new RegExp(`^${ASSET_ROUTE_PREFIX}/[^/]+/native-app-icon\\.png$`, "u"), |
| 978 | ); | 978 | ); |
| 979 | expect(result.expiresAt).toBeGreaterThan(0); | 979 | expect(result.expiresAt).toBeGreaterThan(0); |
| 980 | }).pipe(Effect.provide(testLayer)), | 980 | }).pipe(Effect.provide(layerTest)), |
| 981 | ); | 981 | ); |
| 982 | 982 | ||
| 983 | it.effect("serves document attachments inline when a viewer requests it", () => | 983 | it.effect("serves document attachments inline when a viewer requests it", () => |
| ... | @@ -1010,7 +1010,7 @@ describe("AssetAccess", () => { | ... | @@ -1010,7 +1010,7 @@ describe("AssetAccess", () => { |
| 1010 | fileName: "report.pdf", | 1010 | fileName: "report.pdf", |
| 1011 | mimeType: "application/pdf", | 1011 | mimeType: "application/pdf", |
| 1012 | }); | 1012 | }); |
| 1013 | }).pipe(Effect.provide(testLayer)), | 1013 | }).pipe(Effect.provide(layerTest)), |
| 1014 | ); | 1014 | ); |
| 1015 | 1015 | ||
| 1016 | it.effect("serves audio previews with their stored format and keeps saving explicit", () => | 1016 | it.effect("serves audio previews with their stored format and keeps saving explicit", () => |
| ... | @@ -1044,7 +1044,7 @@ describe("AssetAccess", () => { | ... | @@ -1044,7 +1044,7 @@ describe("AssetAccess", () => { |
| 1044 | ...(disposition === "attachment" ? { download: true } : {}), | 1044 | ...(disposition === "attachment" ? { download: true } : {}), |
| 1045 | }); | 1045 | }); |
| 1046 | } | 1046 | } |
| 1047 | }).pipe(Effect.provide(testLayer)), | 1047 | }).pipe(Effect.provide(layerTest)), |
| 1048 | ); | 1048 | ); |
| 1049 | 1049 | ||
| 1050 | it.effect("keeps inline requests for other attachment types as downloads", () => | 1050 | it.effect("keeps inline requests for other attachment types as downloads", () => |
| ... | @@ -1072,7 +1072,7 @@ describe("AssetAccess", () => { | ... | @@ -1072,7 +1072,7 @@ describe("AssetAccess", () => { |
| 1072 | expect( | 1072 | expect( |
| 1073 | yield* resolveAsset(suffix.slice(0, separatorIndex), suffix.slice(separatorIndex + 1)), | 1073 | yield* resolveAsset(suffix.slice(0, separatorIndex), suffix.slice(separatorIndex + 1)), |
| 1074 | ).toMatchObject({ kind: "file", path: attachmentPath, download: true }); | 1074 | ).toMatchObject({ kind: "file", path: attachmentPath, download: true }); |
| 1075 | }).pipe(Effect.provide(testLayer)), | 1075 | }).pipe(Effect.provide(layerTest)), |
| 1076 | ); | 1076 | ); |
| 1077 | it.effect("issues project favicon capabilities with a signed fallback", () => | 1077 | it.effect("issues project favicon capabilities with a signed fallback", () => |
| 1078 | Effect.gen(function* () { | 1078 | Effect.gen(function* () { |
| ... | @@ -1129,7 +1129,7 @@ describe("AssetAccess", () => { | ... | @@ -1129,7 +1129,7 @@ describe("AssetAccess", () => { |
| 1129 | fallbackSuffix.slice(fallbackSeparatorIndex + 1), | 1129 | fallbackSuffix.slice(fallbackSeparatorIndex + 1), |
| 1130 | ), | 1130 | ), |
| 1131 | ).toBeNull(); | 1131 | ).toBeNull(); |
| 1132 | }).pipe(Effect.provide(testLayer)), | 1132 | }).pipe(Effect.provide(layerTest)), |
| 1133 | ); | 1133 | ); |
| 1134 | 1134 | ||
| 1135 | it.effect("issues project favicon capabilities for a saved override", () => | 1135 | it.effect("issues project favicon capabilities for a saved override", () => |
| ... | @@ -1150,7 +1150,7 @@ describe("AssetAccess", () => { | ... | @@ -1150,7 +1150,7 @@ describe("AssetAccess", () => { |
| 1150 | 1150 | ||
| 1151 | expect(result.sourcePath).toBe(path.join("brand", "custom.svg")); | 1151 | expect(result.sourcePath).toBe(path.join("brand", "custom.svg")); |
| 1152 | expect(result.relativeUrl).toMatch(/\/v[0-9a-f]{64}-custom\.svg$/); | 1152 | expect(result.relativeUrl).toMatch(/\/v[0-9a-f]{64}-custom\.svg$/); |
| 1153 | }).pipe(Effect.provide(testLayer)), | 1153 | }).pipe(Effect.provide(layerTest)), |
| 1154 | ); | 1154 | ); |
| 1155 | 1155 | ||
| 1156 | it.effect("issues an exact capability for a saved favicon outside the workspace", () => | 1156 | it.effect("issues an exact capability for a saved favicon outside the workspace", () => |
| ... | @@ -1188,7 +1188,7 @@ describe("AssetAccess", () => { | ... | @@ -1188,7 +1188,7 @@ describe("AssetAccess", () => { |
| 1188 | ); | 1188 | ); |
| 1189 | expect(tamperedSuffixResult).toEqual({ kind: "file", path: canonicalPath }); | 1189 | expect(tamperedSuffixResult).toEqual({ kind: "file", path: canonicalPath }); |
| 1190 | expect(tamperedSuffixResult).not.toEqual({ kind: "file", path: canonicalSiblingPath }); | 1190 | expect(tamperedSuffixResult).not.toEqual({ kind: "file", path: canonicalSiblingPath }); |
| 1191 | }).pipe(Effect.provide(testLayer)), | 1191 | }).pipe(Effect.provide(layerTest)), |
| 1192 | ); | 1192 | ); |
| 1193 | 1193 | ||
| 1194 | it.effect("ignores a client favicon path hint", () => | 1194 | it.effect("ignores a client favicon path hint", () => |
| ... | @@ -1209,7 +1209,7 @@ describe("AssetAccess", () => { | ... | @@ -1209,7 +1209,7 @@ describe("AssetAccess", () => { |
| 1209 | 1209 | ||
| 1210 | expect(result.sourcePath).toBe(path.join("brand", "saved.svg")); | 1210 | expect(result.sourcePath).toBe(path.join("brand", "saved.svg")); |
| 1211 | expect(result.relativeUrl).toMatch(/\/v[0-9a-f]{64}-saved\.svg$/); | 1211 | expect(result.relativeUrl).toMatch(/\/v[0-9a-f]{64}-saved\.svg$/); |
| 1212 | }).pipe(Effect.provide(testLayer)), | 1212 | }).pipe(Effect.provide(layerTest)), |
| 1213 | ); | 1213 | ); |
| 1214 | 1214 | ||
| 1215 | it.effect("keeps automatic favicon resolution separate from a saved override", () => | 1215 | it.effect("keeps automatic favicon resolution separate from a saved override", () => |
| ... | @@ -1229,7 +1229,7 @@ describe("AssetAccess", () => { | ... | @@ -1229,7 +1229,7 @@ describe("AssetAccess", () => { |
| 1229 | 1229 | ||
| 1230 | expect(result.sourcePath).toBe("favicon.svg"); | 1230 | expect(result.sourcePath).toBe("favicon.svg"); |
| 1231 | expect(result.relativeUrl).toMatch(/\/v[0-9a-f]{64}-favicon\.svg$/); | 1231 | expect(result.relativeUrl).toMatch(/\/v[0-9a-f]{64}-favicon\.svg$/); |
| 1232 | }).pipe(Effect.provide(testLayer)), | 1232 | }).pipe(Effect.provide(layerTest)), |
| 1233 | ); | 1233 | ); |
| 1234 | 1234 | ||
| 1235 | it.effect("rejects a resolved project favicon with a non-image extension", () => | 1235 | it.effect("rejects a resolved project favicon with a non-image extension", () => |
| ... | @@ -1247,7 +1247,7 @@ describe("AssetAccess", () => { | ... | @@ -1247,7 +1247,7 @@ describe("AssetAccess", () => { |
| 1247 | }).pipe(Effect.flip); | 1247 | }).pipe(Effect.flip); |
| 1248 | 1248 | ||
| 1249 | expect(error).toBeInstanceOf(AssetPreviewTypeValidationError); | 1249 | expect(error).toBeInstanceOf(AssetPreviewTypeValidationError); |
| 1250 | }).pipe(Effect.provide(testLayer)), | 1250 | }).pipe(Effect.provide(layerTest)), |
| 1251 | ); | 1251 | ); |
| 1252 | 1252 | ||
| 1253 | it.effect("buckets project favicon expiry after content hashing", () => | 1253 | it.effect("buckets project favicon expiry after content hashing", () => |
| ... | @@ -1272,7 +1272,7 @@ describe("AssetAccess", () => { | ... | @@ -1272,7 +1272,7 @@ describe("AssetAccess", () => { |
| 1272 | }).pipe(Effect.provideService(Crypto.Crypto, crossingCrypto)); | 1272 | }).pipe(Effect.provideService(Crypto.Crypto, crossingCrypto)); |
| 1273 | 1273 | ||
| 1274 | expect(result.expiresAt).toBe(3 * bucketMs); | 1274 | expect(result.expiresAt).toBe(3 * bucketMs); |
| 1275 | }).pipe(Effect.provide(testLayer)), | 1275 | }).pipe(Effect.provide(layerTest)), |
| 1276 | ); | 1276 | ); |
| 1277 | 1277 | ||
| 1278 | it.effect("preserves structured project favicon resolution causes", () => | 1278 | it.effect("preserves structured project favicon resolution causes", () => |
| ... | @@ -1306,7 +1306,7 @@ describe("AssetAccess", () => { | ... | @@ -1306,7 +1306,7 @@ describe("AssetAccess", () => { |
| 1306 | expect(error.message).toBe("Failed to resolve project favicon."); | 1306 | expect(error.message).toBe("Failed to resolve project favicon."); |
| 1307 | expect(error._tag).toBe("AssetProjectFaviconResolutionError"); | 1307 | expect(error._tag).toBe("AssetProjectFaviconResolutionError"); |
| 1308 | expect(error.cause).toBe(resolutionCause); | 1308 | expect(error.cause).toBe(resolutionCause); |
| 1309 | }).pipe(Effect.provide(testLayer)), | 1309 | }).pipe(Effect.provide(layerTest)), |
| 1310 | ); | 1310 | ); |
| 1311 | 1311 | ||
| 1312 | it.effect("serves GitHub-hosted pull request media through the repository's credential", () => | 1312 | it.effect("serves GitHub-hosted pull request media through the repository's credential", () => |
| ... | @@ -1362,6 +1362,6 @@ describe("AssetAccess", () => { | ... | @@ -1362,6 +1362,6 @@ describe("AssetAccess", () => { |
| 1362 | const encoded = yield* Schema.encodeEffect(Schema.fromJsonString(AssetAccessError))(error); | 1362 | const encoded = yield* Schema.encodeEffect(Schema.fromJsonString(AssetAccessError))(error); |
| 1363 | expect(encoded).not.toContain(url); | 1363 | expect(encoded).not.toContain(url); |
| 1364 | } | 1364 | } |
| 1365 | }).pipe(Effect.provide(testLayer)), | 1365 | }).pipe(Effect.provide(layerTest)), |
| 1366 | ); | 1366 | ); |
| 1367 | }); | 1367 | }); |
apps/server/src/assets/AttachmentUpload.test.ts+11-11| ... | @@ -24,7 +24,7 @@ import { | ... | @@ -24,7 +24,7 @@ import { |
| 24 | validateAttachmentUploadToken, | 24 | validateAttachmentUploadToken, |
| 25 | } from "./AttachmentUpload.ts"; | 25 | } from "./AttachmentUpload.ts"; |
| 26 | 26 | ||
| 27 | const testLayer = ServerSecretStore.layer.pipe( | 27 | const layerTest = ServerSecretStore.layer.pipe( |
| 28 | Layer.provideMerge(ServerConfig.layerTest(process.cwd(), { prefix: "t3-attachment-upload-" })), | 28 | Layer.provideMerge(ServerConfig.layerTest(process.cwd(), { prefix: "t3-attachment-upload-" })), |
| 29 | Layer.provideMerge(NodeServices.layer), | 29 | Layer.provideMerge(NodeServices.layer), |
| 30 | ); | 30 | ); |
| ... | @@ -62,7 +62,7 @@ describe("AttachmentUpload", () => { | ... | @@ -62,7 +62,7 @@ describe("AttachmentUpload", () => { |
| 62 | mimeType: "image/png", | 62 | mimeType: "image/png", |
| 63 | sizeBytes: 6, | 63 | sizeBytes: 6, |
| 64 | }); | 64 | }); |
| 65 | }).pipe(Effect.provide(testLayer)), | 65 | }).pipe(Effect.provide(layerTest)), |
| 66 | ); | 66 | ); |
| 67 | 67 | ||
| 68 | it.effect("rejects tampered and malformed upload tokens", () => | 68 | it.effect("rejects tampered and malformed upload tokens", () => |
| ... | @@ -74,7 +74,7 @@ describe("AttachmentUpload", () => { | ... | @@ -74,7 +74,7 @@ describe("AttachmentUpload", () => { |
| 74 | expect(yield* validateAttachmentUploadToken(`${payload}x.${signature}`)).toBeNull(); | 74 | expect(yield* validateAttachmentUploadToken(`${payload}x.${signature}`)).toBeNull(); |
| 75 | expect(yield* validateAttachmentUploadToken(`${token}.extra`)).toBeNull(); | 75 | expect(yield* validateAttachmentUploadToken(`${token}.extra`)).toBeNull(); |
| 76 | expect(yield* validateAttachmentUploadToken("garbage")).toBeNull(); | 76 | expect(yield* validateAttachmentUploadToken("garbage")).toBeNull(); |
| 77 | }).pipe(Effect.provide(testLayer)), | 77 | }).pipe(Effect.provide(layerTest)), |
| 78 | ); | 78 | ); |
| 79 | 79 | ||
| 80 | it.effect("accepts unexpired image upload tokens issued before file support", () => | 80 | it.effect("accepts unexpired image upload tokens issued before file support", () => |
| ... | @@ -99,7 +99,7 @@ describe("AttachmentUpload", () => { | ... | @@ -99,7 +99,7 @@ describe("AttachmentUpload", () => { |
| 99 | type: "image", | 99 | type: "image", |
| 100 | attachmentId: issued.attachmentId, | 100 | attachmentId: issued.attachmentId, |
| 101 | }); | 101 | }); |
| 102 | }).pipe(Effect.provide(testLayer)), | 102 | }).pipe(Effect.provide(layerTest)), |
| 103 | ); | 103 | ); |
| 104 | 104 | ||
| 105 | it.effect("rejects expired upload tokens", () => | 105 | it.effect("rejects expired upload tokens", () => |
| ... | @@ -109,7 +109,7 @@ describe("AttachmentUpload", () => { | ... | @@ -109,7 +109,7 @@ describe("AttachmentUpload", () => { |
| 109 | 109 | ||
| 110 | yield* TestClock.adjust("11 minutes"); | 110 | yield* TestClock.adjust("11 minutes"); |
| 111 | expect(yield* validateAttachmentUploadToken(token)).toBeNull(); | 111 | expect(yield* validateAttachmentUploadToken(token)).toBeNull(); |
| 112 | }).pipe(Effect.provide(testLayer)), | 112 | }).pipe(Effect.provide(layerTest)), |
| 113 | ); | 113 | ); |
| 114 | 114 | ||
| 115 | it.effect("removes expired pending uploads while issuing a new upload URL", () => | 115 | it.effect("removes expired pending uploads while issuing a new upload URL", () => |
| ... | @@ -124,7 +124,7 @@ describe("AttachmentUpload", () => { | ... | @@ -124,7 +124,7 @@ describe("AttachmentUpload", () => { |
| 124 | yield* issueAttachmentUploadUrl(uploadInput); | 124 | yield* issueAttachmentUploadUrl(uploadInput); |
| 125 | 125 | ||
| 126 | expect(NodeFS.existsSync(stalePath)).toBe(false); | 126 | expect(NodeFS.existsSync(stalePath)).toBe(false); |
| 127 | }).pipe(Effect.provide(testLayer)), | 127 | }).pipe(Effect.provide(layerTest)), |
| 128 | ); | 128 | ); |
| 129 | 129 | ||
| 130 | it.effect("stores the expected bytes without leaving temporary files", () => | 130 | it.effect("stores the expected bytes without leaving temporary files", () => |
| ... | @@ -148,7 +148,7 @@ describe("AttachmentUpload", () => { | ... | @@ -148,7 +148,7 @@ describe("AttachmentUpload", () => { |
| 148 | expect( | 148 | expect( |
| 149 | NodeFS.readdirSync(config.attachmentsDir).filter((entry) => entry.endsWith(".part")), | 149 | NodeFS.readdirSync(config.attachmentsDir).filter((entry) => entry.endsWith(".part")), |
| 150 | ).toEqual([]); | 150 | ).toEqual([]); |
| 151 | }).pipe(Effect.provide(testLayer)), | 151 | }).pipe(Effect.provide(layerTest)), |
| 152 | ); | 152 | ); |
| 153 | 153 | ||
| 154 | it.effect("streams generic files to a path with their original extension", () => | 154 | it.effect("streams generic files to a path with their original extension", () => |
| ... | @@ -179,7 +179,7 @@ describe("AttachmentUpload", () => { | ... | @@ -179,7 +179,7 @@ describe("AttachmentUpload", () => { |
| 179 | 179 | ||
| 180 | yield* deletePendingAttachment(issued.attachmentId); | 180 | yield* deletePendingAttachment(issued.attachmentId); |
| 181 | expect(NodeFS.readdirSync(config.attachmentsDir)).toEqual([]); | 181 | expect(NodeFS.readdirSync(config.attachmentsDir)).toEqual([]); |
| 182 | }).pipe(Effect.provide(testLayer)), | 182 | }).pipe(Effect.provide(layerTest)), |
| 183 | ); | 183 | ); |
| 184 | 184 | ||
| 185 | it.effect("removes partial streamed uploads that exceed their signed size", () => | 185 | it.effect("removes partial streamed uploads that exceed their signed size", () => |
| ... | @@ -197,7 +197,7 @@ describe("AttachmentUpload", () => { | ... | @@ -197,7 +197,7 @@ describe("AttachmentUpload", () => { |
| 197 | status: 400, | 197 | status: 400, |
| 198 | }); | 198 | }); |
| 199 | expect(NodeFS.readdirSync(config.attachmentsDir)).toEqual([]); | 199 | expect(NodeFS.readdirSync(config.attachmentsDir)).toEqual([]); |
| 200 | }).pipe(Effect.provide(testLayer)), | 200 | }).pipe(Effect.provide(layerTest)), |
| 201 | ); | 201 | ); |
| 202 | 202 | ||
| 203 | it.effect("removes partial streamed uploads when the upload is interrupted", () => | 203 | it.effect("removes partial streamed uploads when the upload is interrupted", () => |
| ... | @@ -227,7 +227,7 @@ describe("AttachmentUpload", () => { | ... | @@ -227,7 +227,7 @@ describe("AttachmentUpload", () => { |
| 227 | 227 | ||
| 228 | yield* Fiber.interrupt(upload); | 228 | yield* Fiber.interrupt(upload); |
| 229 | expect(NodeFS.readdirSync(config.attachmentsDir)).toEqual([]); | 229 | expect(NodeFS.readdirSync(config.attachmentsDir)).toEqual([]); |
| 230 | }).pipe(Effect.provide(testLayer)), | 230 | }).pipe(Effect.provide(layerTest)), |
| 231 | ); | 231 | ); |
| 232 | 232 | ||
| 233 | it.effect("deletes pending uploads without deleting thread-owned copies", () => | 233 | it.effect("deletes pending uploads without deleting thread-owned copies", () => |
| ... | @@ -245,6 +245,6 @@ describe("AttachmentUpload", () => { | ... | @@ -245,6 +245,6 @@ describe("AttachmentUpload", () => { |
| 245 | 245 | ||
| 246 | expect(NodeFS.existsSync(pendingPath)).toBe(false); | 246 | expect(NodeFS.existsSync(pendingPath)).toBe(false); |
| 247 | expect(NodeFS.existsSync(claimedPath)).toBe(true); | 247 | expect(NodeFS.existsSync(claimedPath)).toBe(true); |
| 248 | }).pipe(Effect.provide(testLayer)), | 248 | }).pipe(Effect.provide(layerTest)), |
| 249 | ); | 249 | ); |
| 250 | }); | 250 | }); |
apps/server/src/assets/NativeAppIconResolver.test.ts+5-5| ... | @@ -39,15 +39,15 @@ describe("resolveNativeAppIcon", () => { | ... | @@ -39,15 +39,15 @@ describe("resolveNativeAppIcon", () => { |
| 39 | return emptyProcessHandle(); | 39 | return emptyProcessHandle(); |
| 40 | }), | 40 | }), |
| 41 | ); | 41 | ); |
| 42 | const configLayer = ServerConfig.ServerConfig.layerTest(process.cwd(), { | 42 | const layerConfig = ServerConfig.ServerConfig.layerTest(process.cwd(), { |
| 43 | prefix: "t3-native-app-icon-test-", | 43 | prefix: "t3-native-app-icon-test-", |
| 44 | }); | 44 | }); |
| 45 | const dependencies = Layer.mergeAll( | 45 | const layerDependencies = Layer.mergeAll( |
| 46 | configLayer, | 46 | layerConfig, |
| 47 | Layer.succeed(HostProcessPlatform, "darwin"), | 47 | Layer.succeed(HostProcessPlatform, "darwin"), |
| 48 | Layer.succeed(ChildProcessSpawner.ChildProcessSpawner, spawner), | 48 | Layer.succeed(ChildProcessSpawner.ChildProcessSpawner, spawner), |
| 49 | ).pipe(Layer.provideMerge(NodeServices.layer)); | 49 | ).pipe(Layer.provideMerge(NodeServices.layer)); |
| 50 | const testLayer = NativeAppIconResolver.layer.pipe(Layer.provide(dependencies)); | 50 | const layerTest = NativeAppIconResolver.layer.pipe(Layer.provide(layerDependencies)); |
| 51 | const app = { _tag: "display-name", displayName: "Review * App" } as const; | 51 | const app = { _tag: "display-name", displayName: "Review * App" } as const; |
| 52 | 52 | ||
| 53 | return Effect.gen(function* () { | 53 | return Effect.gen(function* () { |
| ... | @@ -70,6 +70,6 @@ describe("resolveNativeAppIcon", () => { | ... | @@ -70,6 +70,6 @@ describe("resolveNativeAppIcon", () => { |
| 70 | expect(commands).toHaveLength(257); | 70 | expect(commands).toHaveLength(257); |
| 71 | expect(yield* resolver.resolve(app)).toBeNull(); | 71 | expect(yield* resolver.resolve(app)).toBeNull(); |
| 72 | expect(commands).toHaveLength(258); | 72 | expect(commands).toHaveLength(258); |
| 73 | }).pipe(Effect.provide(testLayer)); | 73 | }).pipe(Effect.provide(layerTest)); |
| 74 | }); | 74 | }); |
| 75 | }); | 75 | }); |
apps/server/src/auth/EnvironmentAuth.test.ts+20-20| ... | @@ -10,7 +10,7 @@ import * as SqlClient from "effect/sql/SqlClient"; | ... | @@ -10,7 +10,7 @@ import * as SqlClient from "effect/sql/SqlClient"; |
| 10 | import * as ServerConfig from "../config.ts"; | 10 | import * as ServerConfig from "../config.ts"; |
| 11 | import * as ServerEnvironment from "../environment/ServerEnvironment.ts"; | 11 | import * as ServerEnvironment from "../environment/ServerEnvironment.ts"; |
| 12 | import * as PersistenceErrors from "../persistence/Errors.ts"; | 12 | import * as PersistenceErrors from "../persistence/Errors.ts"; |
| 13 | import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; | 13 | import * as SqlitePersistence from "../persistence/Layers/Sqlite.ts"; |
| 14 | import * as PairingGrantStore from "./PairingGrantStore.ts"; | 14 | import * as PairingGrantStore from "./PairingGrantStore.ts"; |
| 15 | import * as EnvironmentAuth from "./EnvironmentAuth.ts"; | 15 | import * as EnvironmentAuth from "./EnvironmentAuth.ts"; |
| 16 | 16 | ||
| ... | @@ -22,7 +22,7 @@ const TEST_SERVER_PORT = 13_773; | ... | @@ -22,7 +22,7 @@ const TEST_SERVER_PORT = 13_773; |
| 22 | const isPairingCredentialIssueError = Schema.is(PairingGrantStore.PairingCredentialIssueError); | 22 | const isPairingCredentialIssueError = Schema.is(PairingGrantStore.PairingCredentialIssueError); |
| 23 | const isPersistenceSqlError = Schema.is(PersistenceErrors.PersistenceSqlError); | 23 | const isPersistenceSqlError = Schema.is(PersistenceErrors.PersistenceSqlError); |
| 24 | 24 | ||
| 25 | const makeServerConfigLayer = (overrides?: Partial<ServerConfig.ServerConfig["Service"]>) => | 25 | const layerServerConfig = (overrides?: Partial<ServerConfig.ServerConfig["Service"]>) => |
| 26 | Layer.effect( | 26 | Layer.effect( |
| 27 | ServerConfig.ServerConfig, | 27 | ServerConfig.ServerConfig, |
| 28 | Effect.gen(function* () { | 28 | Effect.gen(function* () { |
| ... | @@ -37,12 +37,12 @@ const makeServerConfigLayer = (overrides?: Partial<ServerConfig.ServerConfig["Se | ... | @@ -37,12 +37,12 @@ const makeServerConfigLayer = (overrides?: Partial<ServerConfig.ServerConfig["Se |
| 37 | }), | 37 | }), |
| 38 | ).pipe(Layer.provide(ServerConfig.layerTest(process.cwd(), { prefix: "t3-auth-server-test-" }))); | 38 | ).pipe(Layer.provide(ServerConfig.layerTest(process.cwd(), { prefix: "t3-auth-server-test-" }))); |
| 39 | 39 | ||
| 40 | const makeEnvironmentAuthLayer = (overrides?: Partial<ServerConfig.ServerConfig["Service"]>) => | 40 | const layerEnvironmentAuth = (overrides?: Partial<ServerConfig.ServerConfig["Service"]>) => |
| 41 | EnvironmentAuth.layer.pipe( | 41 | EnvironmentAuth.layer.pipe( |
| 42 | Layer.provideMerge(SqlitePersistenceMemory), | 42 | Layer.provideMerge(SqlitePersistence.layerMemory), |
| 43 | Layer.provide(ServerSecretStore.layer), | 43 | Layer.provide(ServerSecretStore.layer), |
| 44 | Layer.provide(ServerEnvironment.identityLayer), | 44 | Layer.provide(ServerEnvironment.layerIdentity), |
| 45 | Layer.provide(makeServerConfigLayer(overrides)), | 45 | Layer.provide(layerServerConfig(overrides)), |
| 46 | ); | 46 | ); |
| 47 | 47 | ||
| 48 | const makeCookieRequest = ( | 48 | const makeCookieRequest = ( |
| ... | @@ -105,7 +105,7 @@ it.layer(NodeServices.layer)("EnvironmentAuth.layer", (it) => { | ... | @@ -105,7 +105,7 @@ it.layer(NodeServices.layer)("EnvironmentAuth.layer", (it) => { |
| 105 | expect(authenticated.scopes).toEqual(["orchestration:read"]); | 105 | expect(authenticated.scopes).toEqual(["orchestration:read"]); |
| 106 | }).pipe( | 106 | }).pipe( |
| 107 | Effect.provide( | 107 | Effect.provide( |
| 108 | makeEnvironmentAuthLayer({ | 108 | layerEnvironmentAuth({ |
| 109 | mode: "web", | 109 | mode: "web", |
| 110 | devUrl: new URL("http://127.0.0.1:5173"), | 110 | devUrl: new URL("http://127.0.0.1:5173"), |
| 111 | devAuthToken: Redacted.make("reusable-dev-auth-token-that-is-long-enough"), | 111 | devAuthToken: Redacted.make("reusable-dev-auth-token-that-is-long-enough"), |
| ... | @@ -141,7 +141,7 @@ it.layer(NodeServices.layer)("EnvironmentAuth.layer", (it) => { | ... | @@ -141,7 +141,7 @@ it.layer(NodeServices.layer)("EnvironmentAuth.layer", (it) => { |
| 141 | expect(error._tag).toBe("ServerAuthInvalidCredentialError"); | 141 | expect(error._tag).toBe("ServerAuthInvalidCredentialError"); |
| 142 | }).pipe( | 142 | }).pipe( |
| 143 | Effect.provide( | 143 | Effect.provide( |
| 144 | makeEnvironmentAuthLayer({ | 144 | layerEnvironmentAuth({ |
| 145 | mode: "web", | 145 | mode: "web", |
| 146 | devUrl: new URL("http://127.0.0.1:5173"), | 146 | devUrl: new URL("http://127.0.0.1:5173"), |
| 147 | devAuthToken: Redacted.make("reusable-dev-auth-token-that-is-long-enough"), | 147 | devAuthToken: Redacted.make("reusable-dev-auth-token-that-is-long-enough"), |
| ... | @@ -167,7 +167,7 @@ it.layer(NodeServices.layer)("EnvironmentAuth.layer", (it) => { | ... | @@ -167,7 +167,7 @@ it.layer(NodeServices.layer)("EnvironmentAuth.layer", (it) => { |
| 167 | } | 167 | } |
| 168 | }).pipe( | 168 | }).pipe( |
| 169 | Effect.provide( | 169 | Effect.provide( |
| 170 | makeEnvironmentAuthLayer({ | 170 | layerEnvironmentAuth({ |
| 171 | mode: "web", | 171 | mode: "web", |
| 172 | devUrl: new URL("http://127.0.0.1:5173"), | 172 | devUrl: new URL("http://127.0.0.1:5173"), |
| 173 | devAuthToken: Redacted.make("reusable-dev-auth-token-that-is-long-enough"), | 173 | devAuthToken: Redacted.make("reusable-dev-auth-token-that-is-long-enough"), |
| ... | @@ -216,7 +216,7 @@ it.layer(NodeServices.layer)("EnvironmentAuth.layer", (it) => { | ... | @@ -216,7 +216,7 @@ it.layer(NodeServices.layer)("EnvironmentAuth.layer", (it) => { |
| 216 | expect((yield* sessions.verify(token)).subject).toBe("reusable-dev-token"); | 216 | expect((yield* sessions.verify(token)).subject).toBe("reusable-dev-token"); |
| 217 | }).pipe( | 217 | }).pipe( |
| 218 | Effect.provide( | 218 | Effect.provide( |
| 219 | makeEnvironmentAuthLayer({ | 219 | layerEnvironmentAuth({ |
| 220 | mode: "web", | 220 | mode: "web", |
| 221 | devUrl: new URL("http://127.0.0.1:5173"), | 221 | devUrl: new URL("http://127.0.0.1:5173"), |
| 222 | devAuthToken: Redacted.make("reusable-dev-auth-token-that-is-long-enough"), | 222 | devAuthToken: Redacted.make("reusable-dev-auth-token-that-is-long-enough"), |
| ... | @@ -244,7 +244,7 @@ it.layer(NodeServices.layer)("EnvironmentAuth.layer", (it) => { | ... | @@ -244,7 +244,7 @@ it.layer(NodeServices.layer)("EnvironmentAuth.layer", (it) => { |
| 244 | ).toMatchObject({ authenticated: true, scopes: AuthAdministrativeScopes }); | 244 | ).toMatchObject({ authenticated: true, scopes: AuthAdministrativeScopes }); |
| 245 | }).pipe( | 245 | }).pipe( |
| 246 | Effect.provide( | 246 | Effect.provide( |
| 247 | makeEnvironmentAuthLayer({ | 247 | layerEnvironmentAuth({ |
| 248 | mode: "web", | 248 | mode: "web", |
| 249 | devUrl: new URL("http://127.0.0.1:5173"), | 249 | devUrl: new URL("http://127.0.0.1:5173"), |
| 250 | devAuthToken: Redacted.make("reusable-dev-auth-token-that-is-long-enough"), | 250 | devAuthToken: Redacted.make("reusable-dev-auth-token-that-is-long-enough"), |
| ... | @@ -279,7 +279,7 @@ it.layer(NodeServices.layer)("EnvironmentAuth.layer", (it) => { | ... | @@ -279,7 +279,7 @@ it.layer(NodeServices.layer)("EnvironmentAuth.layer", (it) => { |
| 279 | } | 279 | } |
| 280 | }).pipe( | 280 | }).pipe( |
| 281 | Effect.provide( | 281 | Effect.provide( |
| 282 | makeEnvironmentAuthLayer({ | 282 | layerEnvironmentAuth({ |
| 283 | mode: "web", | 283 | mode: "web", |
| 284 | devUrl: new URL("http://127.0.0.1:5173"), | 284 | devUrl: new URL("http://127.0.0.1:5173"), |
| 285 | devAuthToken: Redacted.make("reusable-dev-auth-token-that-is-long-enough"), | 285 | devAuthToken: Redacted.make("reusable-dev-auth-token-that-is-long-enough"), |
| ... | @@ -336,7 +336,7 @@ it.layer(NodeServices.layer)("EnvironmentAuth.layer", (it) => { | ... | @@ -336,7 +336,7 @@ it.layer(NodeServices.layer)("EnvironmentAuth.layer", (it) => { |
| 336 | "relay:read", | 336 | "relay:read", |
| 337 | ]); | 337 | ]); |
| 338 | expect(verified.subject).toBe("one-time-token"); | 338 | expect(verified.subject).toBe("one-time-token"); |
| 339 | }).pipe(Effect.provide(makeEnvironmentAuthLayer())), | 339 | }).pipe(Effect.provide(layerEnvironmentAuth())), |
| 340 | ); | 340 | ); |
| 341 | 341 | ||
| 342 | it.effect("prefers a bearer token over a stale legacy cookie", () => | 342 | it.effect("prefers a bearer token over a stale legacy cookie", () => |
| ... | @@ -350,7 +350,7 @@ it.layer(NodeServices.layer)("EnvironmentAuth.layer", (it) => { | ... | @@ -350,7 +350,7 @@ it.layer(NodeServices.layer)("EnvironmentAuth.layer", (it) => { |
| 350 | } as never); | 350 | } as never); |
| 351 | 351 | ||
| 352 | expect(verified.sessionId).toBe(bearer.sessionId); | 352 | expect(verified.sessionId).toBe(bearer.sessionId); |
| 353 | }).pipe(Effect.provide(makeEnvironmentAuthLayer({ mode: "web", host: "192.168.1.50" }))), | 353 | }).pipe(Effect.provide(layerEnvironmentAuth({ mode: "web", host: "192.168.1.50" }))), |
| 354 | ); | 354 | ); |
| 355 | 355 | ||
| 356 | it.effect("does not exchange ordinary pairing grants for administrative access tokens", () => | 356 | it.effect("does not exchange ordinary pairing grants for administrative access tokens", () => |
| ... | @@ -367,7 +367,7 @@ it.layer(NodeServices.layer)("EnvironmentAuth.layer", (it) => { | ... | @@ -367,7 +367,7 @@ it.layer(NodeServices.layer)("EnvironmentAuth.layer", (it) => { |
| 367 | .pipe(Effect.flip); | 367 | .pipe(Effect.flip); |
| 368 | 368 | ||
| 369 | expect(error._tag).toBe("ServerAuthScopeNotGrantedError"); | 369 | expect(error._tag).toBe("ServerAuthScopeNotGrantedError"); |
| 370 | }).pipe(Effect.provide(makeEnvironmentAuthLayer())), | 370 | }).pipe(Effect.provide(layerEnvironmentAuth())), |
| 371 | ); | 371 | ); |
| 372 | 372 | ||
| 373 | it.effect("inherits a constrained pairing grant when token exchange omits scope", () => | 373 | it.effect("inherits a constrained pairing grant when token exchange omits scope", () => |
| ... | @@ -384,7 +384,7 @@ it.layer(NodeServices.layer)("EnvironmentAuth.layer", (it) => { | ... | @@ -384,7 +384,7 @@ it.layer(NodeServices.layer)("EnvironmentAuth.layer", (it) => { |
| 384 | ); | 384 | ); |
| 385 | 385 | ||
| 386 | expect(token.scope).toBe("orchestration:read"); | 386 | expect(token.scope).toBe("orchestration:read"); |
| 387 | }).pipe(Effect.provide(makeEnvironmentAuthLayer())), | 387 | }).pipe(Effect.provide(layerEnvironmentAuth())), |
| 388 | ); | 388 | ); |
| 389 | 389 | ||
| 390 | it.effect("rotates desktop bearer sessions without accumulating authorized clients", () => | 390 | it.effect("rotates desktop bearer sessions without accumulating authorized clients", () => |
| ... | @@ -445,7 +445,7 @@ it.layer(NodeServices.layer)("EnvironmentAuth.layer", (it) => { | ... | @@ -445,7 +445,7 @@ it.layer(NodeServices.layer)("EnvironmentAuth.layer", (it) => { |
| 445 | expect(active.map((entry) => entry.sessionId)).toContain(pairedSession.sessionId); | 445 | expect(active.map((entry) => entry.sessionId)).toContain(pairedSession.sessionId); |
| 446 | }).pipe( | 446 | }).pipe( |
| 447 | Effect.provide( | 447 | Effect.provide( |
| 448 | makeEnvironmentAuthLayer({ | 448 | layerEnvironmentAuth({ |
| 449 | desktopBootstrapToken: "desktop-bootstrap-token", | 449 | desktopBootstrapToken: "desktop-bootstrap-token", |
| 450 | }), | 450 | }), |
| 451 | ), | 451 | ), |
| ... | @@ -463,7 +463,7 @@ it.layer(NodeServices.layer)("EnvironmentAuth.layer", (it) => { | ... | @@ -463,7 +463,7 @@ it.layer(NodeServices.layer)("EnvironmentAuth.layer", (it) => { |
| 463 | expect( | 463 | expect( |
| 464 | listedPairingLinks.find((pairingLink) => pairingLink.id === pairingCredential.id)?.subject, | 464 | listedPairingLinks.find((pairingLink) => pairingLink.id === pairingCredential.id)?.subject, |
| 465 | ).toBe("one-time-token"); | 465 | ).toBe("one-time-token"); |
| 466 | }).pipe(Effect.provide(makeEnvironmentAuthLayer())), | 466 | }).pipe(Effect.provide(layerEnvironmentAuth())), |
| 467 | ); | 467 | ); |
| 468 | 468 | ||
| 469 | it.effect("issues startup pairing URLs that bootstrap administrative sessions", () => | 469 | it.effect("issues startup pairing URLs that bootstrap administrative sessions", () => |
| ... | @@ -497,7 +497,7 @@ it.layer(NodeServices.layer)("EnvironmentAuth.layer", (it) => { | ... | @@ -497,7 +497,7 @@ it.layer(NodeServices.layer)("EnvironmentAuth.layer", (it) => { |
| 497 | "relay:write", | 497 | "relay:write", |
| 498 | ]); | 498 | ]); |
| 499 | expect(verified.subject).toBe("administrative-bootstrap"); | 499 | expect(verified.subject).toBe("administrative-bootstrap"); |
| 500 | }).pipe(Effect.provide(makeEnvironmentAuthLayer())), | 500 | }).pipe(Effect.provide(layerEnvironmentAuth())), |
| 501 | ); | 501 | ); |
| 502 | 502 | ||
| 503 | it.effect( | 503 | it.effect( |
| ... | @@ -566,7 +566,7 @@ it.layer(NodeServices.layer)("EnvironmentAuth.layer", (it) => { | ... | @@ -566,7 +566,7 @@ it.layer(NodeServices.layer)("EnvironmentAuth.layer", (it) => { |
| 566 | expect(clientsAfterRevoke[0]?.sessionId).toBe(administrativeSession.sessionId); | 566 | expect(clientsAfterRevoke[0]?.sessionId).toBe(administrativeSession.sessionId); |
| 567 | }).pipe( | 567 | }).pipe( |
| 568 | Effect.provide( | 568 | Effect.provide( |
| 569 | makeEnvironmentAuthLayer({ | 569 | layerEnvironmentAuth({ |
| 570 | desktopBootstrapToken: "desktop-bootstrap-token", | 570 | desktopBootstrapToken: "desktop-bootstrap-token", |
| 571 | }), | 571 | }), |
| 572 | ), | 572 | ), |
apps/server/src/auth/EnvironmentAuth.ts+4-4| ... | @@ -1125,9 +1125,9 @@ export const layer = Layer.effect(EnvironmentAuth, make).pipe( | ... | @@ -1125,9 +1125,9 @@ export const layer = Layer.effect(EnvironmentAuth, make).pipe( |
| 1125 | Layer.provideMerge(EnvironmentAuthPolicy.layer), | 1125 | Layer.provideMerge(EnvironmentAuthPolicy.layer), |
| 1126 | ); | 1126 | ); |
| 1127 | 1127 | ||
| 1128 | const storageLayer = Layer.mergeAll(ServerSecretStore.layer, SqlitePersistence.layerConfig); | 1128 | const layerStorage = Layer.mergeAll(ServerSecretStore.layer, SqlitePersistence.layerConfig); |
| 1129 | 1129 | ||
| 1130 | export const runtimeLayer = layer.pipe( | 1130 | export const layerRuntime = layer.pipe( |
| 1131 | Layer.provideMerge(storageLayer), | 1131 | Layer.provideMerge(layerStorage), |
| 1132 | Layer.provideMerge(ServerEnvironment.identityLayer), | 1132 | Layer.provideMerge(ServerEnvironment.layerIdentity), |
| 1133 | ); | 1133 | ); |
apps/server/src/auth/EnvironmentAuthAdmin.test.ts+9-9| ... | @@ -5,12 +5,12 @@ import * as Layer from "effect/Layer"; | ... | @@ -5,12 +5,12 @@ import * as Layer from "effect/Layer"; |
| 5 | 5 | ||
| 6 | import * as ServerConfig from "../config.ts"; | 6 | import * as ServerConfig from "../config.ts"; |
| 7 | import * as ServerEnvironment from "../environment/ServerEnvironment.ts"; | 7 | import * as ServerEnvironment from "../environment/ServerEnvironment.ts"; |
| 8 | import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; | 8 | import * as SqlitePersistence from "../persistence/Layers/Sqlite.ts"; |
| 9 | import * as EnvironmentAuth from "./EnvironmentAuth.ts"; | 9 | import * as EnvironmentAuth from "./EnvironmentAuth.ts"; |
| 10 | import * as ServerSecretStore from "./ServerSecretStore.ts"; | 10 | import * as ServerSecretStore from "./ServerSecretStore.ts"; |
| 11 | import * as SessionStore from "./SessionStore.ts"; | 11 | import * as SessionStore from "./SessionStore.ts"; |
| 12 | 12 | ||
| 13 | const makeServerConfigLayer = ( | 13 | const layerServerConfig = ( |
| 14 | overrides?: Partial<Pick<ServerConfig.ServerConfig["Service"], "desktopBootstrapToken">>, | 14 | overrides?: Partial<Pick<ServerConfig.ServerConfig["Service"], "desktopBootstrapToken">>, |
| 15 | ) => | 15 | ) => |
| 16 | Layer.effect( | 16 | Layer.effect( |
| ... | @@ -30,14 +30,14 @@ const makeServerConfigLayer = ( | ... | @@ -30,14 +30,14 @@ const makeServerConfigLayer = ( |
| 30 | ), | 30 | ), |
| 31 | ); | 31 | ); |
| 32 | 32 | ||
| 33 | const makeEnvironmentAuthLayer = ( | 33 | const layerEnvironmentAuth = ( |
| 34 | overrides?: Partial<Pick<ServerConfig.ServerConfig["Service"], "desktopBootstrapToken">>, | 34 | overrides?: Partial<Pick<ServerConfig.ServerConfig["Service"], "desktopBootstrapToken">>, |
| 35 | ) => | 35 | ) => |
| 36 | EnvironmentAuth.layer.pipe( | 36 | EnvironmentAuth.layer.pipe( |
| 37 | Layer.provideMerge(ServerSecretStore.layer), | 37 | Layer.provideMerge(ServerSecretStore.layer), |
| 38 | Layer.provideMerge(SqlitePersistenceMemory), | 38 | Layer.provideMerge(SqlitePersistence.layerMemory), |
| 39 | Layer.provide(ServerEnvironment.identityLayer), | 39 | Layer.provide(ServerEnvironment.layerIdentity), |
| 40 | Layer.provide(makeServerConfigLayer(overrides)), | 40 | Layer.provide(layerServerConfig(overrides)), |
| 41 | ); | 41 | ); |
| 42 | 42 | ||
| 43 | it.layer(NodeServices.layer)("EnvironmentAuth administrative operations", (it) => { | 43 | it.layer(NodeServices.layer)("EnvironmentAuth administrative operations", (it) => { |
| ... | @@ -62,7 +62,7 @@ it.layer(NodeServices.layer)("EnvironmentAuth administrative operations", (it) = | ... | @@ -62,7 +62,7 @@ it.layer(NodeServices.layer)("EnvironmentAuth administrative operations", (it) = |
| 62 | expect(listedBeforeRevoke[0]).not.toHaveProperty("credential"); | 62 | expect(listedBeforeRevoke[0]).not.toHaveProperty("credential"); |
| 63 | expect(revoked).toBe(true); | 63 | expect(revoked).toBe(true); |
| 64 | expect(listedAfterRevoke).toHaveLength(0); | 64 | expect(listedAfterRevoke).toHaveLength(0); |
| 65 | }).pipe(Effect.provide(makeEnvironmentAuthLayer())), | 65 | }).pipe(Effect.provide(layerEnvironmentAuth())), |
| 66 | ); | 66 | ); |
| 67 | 67 | ||
| 68 | it.effect("issues bearer access token sessions without exposing raw tokens", () => | 68 | it.effect("issues bearer access token sessions without exposing raw tokens", () => |
| ... | @@ -108,7 +108,7 @@ it.layer(NodeServices.layer)("EnvironmentAuth administrative operations", (it) = | ... | @@ -108,7 +108,7 @@ it.layer(NodeServices.layer)("EnvironmentAuth administrative operations", (it) = |
| 108 | expect("token" in (listedBeforeRevoke[0] ?? {})).toBe(false); | 108 | expect("token" in (listedBeforeRevoke[0] ?? {})).toBe(false); |
| 109 | expect(revoked).toBe(true); | 109 | expect(revoked).toBe(true); |
| 110 | expect(listedAfterRevoke).toHaveLength(0); | 110 | expect(listedAfterRevoke).toHaveLength(0); |
| 111 | }).pipe(Effect.provide(makeEnvironmentAuthLayer())), | 111 | }).pipe(Effect.provide(layerEnvironmentAuth())), |
| 112 | ); | 112 | ); |
| 113 | 113 | ||
| 114 | it.effect("surfaces lastConnectedAt through the listed session view", () => | 114 | it.effect("surfaces lastConnectedAt through the listed session view", () => |
| ... | @@ -125,6 +125,6 @@ it.layer(NodeServices.layer)("EnvironmentAuth administrative operations", (it) = | ... | @@ -125,6 +125,6 @@ it.layer(NodeServices.layer)("EnvironmentAuth administrative operations", (it) = |
| 125 | 125 | ||
| 126 | expect(beforeConnect[0]?.lastConnectedAt).toBeNull(); | 126 | expect(beforeConnect[0]?.lastConnectedAt).toBeNull(); |
| 127 | expect(afterConnect[0]?.lastConnectedAt).not.toBeNull(); | 127 | expect(afterConnect[0]?.lastConnectedAt).not.toBeNull(); |
| 128 | }).pipe(Effect.provide(makeEnvironmentAuthLayer())), | 128 | }).pipe(Effect.provide(layerEnvironmentAuth())), |
| 129 | ); | 129 | ); |
| 130 | }); | 130 | }); |
apps/server/src/auth/EnvironmentAuthPolicy.test.ts+9-11| ... | @@ -7,11 +7,9 @@ import * as ServerConfig from "../config.ts"; | ... | @@ -7,11 +7,9 @@ import * as ServerConfig from "../config.ts"; |
| 7 | import * as ServerEnvironment from "../environment/ServerEnvironment.ts"; | 7 | import * as ServerEnvironment from "../environment/ServerEnvironment.ts"; |
| 8 | import * as EnvironmentAuthPolicy from "./EnvironmentAuthPolicy.ts"; | 8 | import * as EnvironmentAuthPolicy from "./EnvironmentAuthPolicy.ts"; |
| 9 | 9 | ||
| 10 | const makeEnvironmentAuthPolicyLayer = ( | 10 | const layerEnvironmentAuthPolicy = (overrides?: Partial<ServerConfig.ServerConfig["Service"]>) => |
| 11 | overrides?: Partial<ServerConfig.ServerConfig["Service"]>, | ||
| 12 | ) => | ||
| 13 | EnvironmentAuthPolicy.layer.pipe( | 11 | EnvironmentAuthPolicy.layer.pipe( |
| 14 | Layer.provide(ServerEnvironment.identityLayer), | 12 | Layer.provide(ServerEnvironment.layerIdentity), |
| 15 | Layer.provide( | 13 | Layer.provide( |
| 16 | Layer.effect( | 14 | Layer.effect( |
| 17 | ServerConfig.ServerConfig, | 15 | ServerConfig.ServerConfig, |
| ... | @@ -42,7 +40,7 @@ it.layer(NodeServices.layer)("EnvironmentAuthPolicy.layer", (it) => { | ... | @@ -42,7 +40,7 @@ it.layer(NodeServices.layer)("EnvironmentAuthPolicy.layer", (it) => { |
| 42 | expect(descriptor.sessionCookieName).toBe("t3_session_3773"); | 40 | expect(descriptor.sessionCookieName).toBe("t3_session_3773"); |
| 43 | }).pipe( | 41 | }).pipe( |
| 44 | Effect.provide( | 42 | Effect.provide( |
| 45 | makeEnvironmentAuthPolicyLayer({ | 43 | layerEnvironmentAuthPolicy({ |
| 46 | mode: "desktop", | 44 | mode: "desktop", |
| 47 | port: 3773, | 45 | port: 3773, |
| 48 | }), | 46 | }), |
| ... | @@ -58,7 +56,7 @@ it.layer(NodeServices.layer)("EnvironmentAuthPolicy.layer", (it) => { | ... | @@ -58,7 +56,7 @@ it.layer(NodeServices.layer)("EnvironmentAuthPolicy.layer", (it) => { |
| 58 | expect(descriptor.sessionCookieName).toBe("t3_session_3774"); | 56 | expect(descriptor.sessionCookieName).toBe("t3_session_3774"); |
| 59 | }).pipe( | 57 | }).pipe( |
| 60 | Effect.provide( | 58 | Effect.provide( |
| 61 | makeEnvironmentAuthPolicyLayer({ | 59 | layerEnvironmentAuthPolicy({ |
| 62 | mode: "desktop", | 60 | mode: "desktop", |
| 63 | port: 3774, | 61 | port: 3774, |
| 64 | }), | 62 | }), |
| ... | @@ -75,7 +73,7 @@ it.layer(NodeServices.layer)("EnvironmentAuthPolicy.layer", (it) => { | ... | @@ -75,7 +73,7 @@ it.layer(NodeServices.layer)("EnvironmentAuthPolicy.layer", (it) => { |
| 75 | expect(descriptor.bootstrapMethods).toEqual(["desktop-bootstrap", "one-time-token"]); | 73 | expect(descriptor.bootstrapMethods).toEqual(["desktop-bootstrap", "one-time-token"]); |
| 76 | }).pipe( | 74 | }).pipe( |
| 77 | Effect.provide( | 75 | Effect.provide( |
| 78 | makeEnvironmentAuthPolicyLayer({ | 76 | layerEnvironmentAuthPolicy({ |
| 79 | mode: "desktop", | 77 | mode: "desktop", |
| 80 | host: "0.0.0.0", | 78 | host: "0.0.0.0", |
| 81 | }), | 79 | }), |
| ... | @@ -93,7 +91,7 @@ it.layer(NodeServices.layer)("EnvironmentAuthPolicy.layer", (it) => { | ... | @@ -93,7 +91,7 @@ it.layer(NodeServices.layer)("EnvironmentAuthPolicy.layer", (it) => { |
| 93 | expect(descriptor.sessionCookieName).toMatch(/^t3_session_3773_[a-f0-9]{12}$/); | 91 | expect(descriptor.sessionCookieName).toMatch(/^t3_session_3773_[a-f0-9]{12}$/); |
| 94 | }).pipe( | 92 | }).pipe( |
| 95 | Effect.provide( | 93 | Effect.provide( |
| 96 | makeEnvironmentAuthPolicyLayer({ | 94 | layerEnvironmentAuthPolicy({ |
| 97 | mode: "web", | 95 | mode: "web", |
| 98 | host: "127.0.0.1", | 96 | host: "127.0.0.1", |
| 99 | port: 3773, | 97 | port: 3773, |
| ... | @@ -112,7 +110,7 @@ it.layer(NodeServices.layer)("EnvironmentAuthPolicy.layer", (it) => { | ... | @@ -112,7 +110,7 @@ it.layer(NodeServices.layer)("EnvironmentAuthPolicy.layer", (it) => { |
| 112 | expect(descriptor.sessionCookieName).toMatch(/^t3_session_[a-f0-9]{12}$/); | 110 | expect(descriptor.sessionCookieName).toMatch(/^t3_session_[a-f0-9]{12}$/); |
| 113 | }).pipe( | 111 | }).pipe( |
| 114 | Effect.provide( | 112 | Effect.provide( |
| 115 | makeEnvironmentAuthPolicyLayer({ | 113 | layerEnvironmentAuthPolicy({ |
| 116 | mode: "web", | 114 | mode: "web", |
| 117 | host: "0.0.0.0", | 115 | host: "0.0.0.0", |
| 118 | }), | 116 | }), |
| ... | @@ -129,7 +127,7 @@ it.layer(NodeServices.layer)("EnvironmentAuthPolicy.layer", (it) => { | ... | @@ -129,7 +127,7 @@ it.layer(NodeServices.layer)("EnvironmentAuthPolicy.layer", (it) => { |
| 129 | expect(descriptor.sessionCookieName).toMatch(/^t3_session_5775_[a-f0-9]{12}$/); | 127 | expect(descriptor.sessionCookieName).toMatch(/^t3_session_5775_[a-f0-9]{12}$/); |
| 130 | }).pipe( | 128 | }).pipe( |
| 131 | Effect.provide( | 129 | Effect.provide( |
| 132 | makeEnvironmentAuthPolicyLayer({ | 130 | layerEnvironmentAuthPolicy({ |
| 133 | mode: "web", | 131 | mode: "web", |
| 134 | host: "0.0.0.0", | 132 | host: "0.0.0.0", |
| 135 | port: 5775, | 133 | port: 5775, |
| ... | @@ -148,7 +146,7 @@ it.layer(NodeServices.layer)("EnvironmentAuthPolicy.layer", (it) => { | ... | @@ -148,7 +146,7 @@ it.layer(NodeServices.layer)("EnvironmentAuthPolicy.layer", (it) => { |
| 148 | expect(descriptor.sessionCookieName).toMatch(/^t3_session_[a-f0-9]{12}$/); | 146 | expect(descriptor.sessionCookieName).toMatch(/^t3_session_[a-f0-9]{12}$/); |
| 149 | }).pipe( | 147 | }).pipe( |
| 150 | Effect.provide( | 148 | Effect.provide( |
| 151 | makeEnvironmentAuthPolicyLayer({ | 149 | layerEnvironmentAuthPolicy({ |
| 152 | mode: "web", | 150 | mode: "web", |
| 153 | host: "192.168.1.50", | 151 | host: "192.168.1.50", |
| 154 | }), | 152 | }), |
apps/server/src/auth/PairingGrantStore.test.ts+16-16| ... | @@ -10,10 +10,10 @@ import * as TestClock from "effect/testing/TestClock"; | ... | @@ -10,10 +10,10 @@ import * as TestClock from "effect/testing/TestClock"; |
| 10 | import * as ServerConfig from "../config.ts"; | 10 | import * as ServerConfig from "../config.ts"; |
| 11 | import * as AuthPairingLinks from "../persistence/AuthPairingLinks.ts"; | 11 | import * as AuthPairingLinks from "../persistence/AuthPairingLinks.ts"; |
| 12 | import { PersistenceSqlError } from "../persistence/Errors.ts"; | 12 | import { PersistenceSqlError } from "../persistence/Errors.ts"; |
| 13 | import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; | 13 | import * as SqlitePersistence from "../persistence/Layers/Sqlite.ts"; |
| 14 | import * as PairingGrantStore from "./PairingGrantStore.ts"; | 14 | import * as PairingGrantStore from "./PairingGrantStore.ts"; |
| 15 | 15 | ||
| 16 | const makeServerConfigLayer = ( | 16 | const layerServerConfig = ( |
| 17 | overrides?: Partial<Pick<ServerConfig.ServerConfig["Service"], "desktopBootstrapToken">>, | 17 | overrides?: Partial<Pick<ServerConfig.ServerConfig["Service"], "desktopBootstrapToken">>, |
| 18 | ) => | 18 | ) => |
| 19 | Layer.effect( | 19 | Layer.effect( |
| ... | @@ -29,15 +29,15 @@ const makeServerConfigLayer = ( | ... | @@ -29,15 +29,15 @@ const makeServerConfigLayer = ( |
| 29 | Layer.provide(ServerConfig.layerTest(process.cwd(), { prefix: "t3-auth-bootstrap-test-" })), | 29 | Layer.provide(ServerConfig.layerTest(process.cwd(), { prefix: "t3-auth-bootstrap-test-" })), |
| 30 | ); | 30 | ); |
| 31 | 31 | ||
| 32 | const makePairingGrantStoreLayer = ( | 32 | const layerPairingGrantStore = ( |
| 33 | overrides?: Partial<Pick<ServerConfig.ServerConfig["Service"], "desktopBootstrapToken">>, | 33 | overrides?: Partial<Pick<ServerConfig.ServerConfig["Service"], "desktopBootstrapToken">>, |
| 34 | ) => | 34 | ) => |
| 35 | PairingGrantStore.layer.pipe( | 35 | PairingGrantStore.layer.pipe( |
| 36 | Layer.provide(SqlitePersistenceMemory), | 36 | Layer.provide(SqlitePersistence.layerMemory), |
| 37 | Layer.provide(makeServerConfigLayer(overrides)), | 37 | Layer.provide(layerServerConfig(overrides)), |
| 38 | ); | 38 | ); |
| 39 | 39 | ||
| 40 | const makePairingGrantStoreTestLayer = ( | 40 | const layerPairingGrantStoreTest = ( |
| 41 | overrides: Partial<AuthPairingLinks.AuthPairingLinkRepository["Service"]>, | 41 | overrides: Partial<AuthPairingLinks.AuthPairingLinkRepository["Service"]>, |
| 42 | ) => | 42 | ) => |
| 43 | Layer.effect(PairingGrantStore.PairingGrantStore, PairingGrantStore.make).pipe( | 43 | Layer.effect(PairingGrantStore.PairingGrantStore, PairingGrantStore.make).pipe( |
| ... | @@ -54,7 +54,7 @@ const makePairingGrantStoreTestLayer = ( | ... | @@ -54,7 +54,7 @@ const makePairingGrantStoreTestLayer = ( |
| 54 | }), | 54 | }), |
| 55 | ), | 55 | ), |
| 56 | ), | 56 | ), |
| 57 | Layer.provide(makeServerConfigLayer()), | 57 | Layer.provide(layerServerConfig()), |
| 58 | ); | 58 | ); |
| 59 | 59 | ||
| 60 | it.layer(NodeServices.layer)("PairingGrantStore.layer", (it) => { | 60 | it.layer(NodeServices.layer)("PairingGrantStore.layer", (it) => { |
| ... | @@ -64,7 +64,7 @@ it.layer(NodeServices.layer)("PairingGrantStore.layer", (it) => { | ... | @@ -64,7 +64,7 @@ it.layer(NodeServices.layer)("PairingGrantStore.layer", (it) => { |
| 64 | const issued = yield* bootstrapCredentials.issueOneTimeToken(); | 64 | const issued = yield* bootstrapCredentials.issueOneTimeToken(); |
| 65 | 65 | ||
| 66 | expect(issued.credential).toMatch(/^[23456789ABCDEFGHJKLMNPQRSTUVWXYZ]{12}$/); | 66 | expect(issued.credential).toMatch(/^[23456789ABCDEFGHJKLMNPQRSTUVWXYZ]{12}$/); |
| 67 | }).pipe(Effect.provide(makePairingGrantStoreLayer())), | 67 | }).pipe(Effect.provide(layerPairingGrantStore())), |
| 68 | ); | 68 | ); |
| 69 | 69 | ||
| 70 | it.effect("issues one-time bootstrap tokens that can only be consumed once", () => | 70 | it.effect("issues one-time bootstrap tokens that can only be consumed once", () => |
| ... | @@ -87,7 +87,7 @@ it.layer(NodeServices.layer)("PairingGrantStore.layer", (it) => { | ... | @@ -87,7 +87,7 @@ it.layer(NodeServices.layer)("PairingGrantStore.layer", (it) => { |
| 87 | expect(issued.label).toBe("Julius iPhone"); | 87 | expect(issued.label).toBe("Julius iPhone"); |
| 88 | expect(second._tag).toBe("UnknownBootstrapCredentialError"); | 88 | expect(second._tag).toBe("UnknownBootstrapCredentialError"); |
| 89 | expect(second.message).toContain("Unknown bootstrap credential"); | 89 | expect(second.message).toContain("Unknown bootstrap credential"); |
| 90 | }).pipe(Effect.provide(makePairingGrantStoreLayer())), | 90 | }).pipe(Effect.provide(layerPairingGrantStore())), |
| 91 | ); | 91 | ); |
| 92 | 92 | ||
| 93 | it.effect("atomically consumes a one-time token when multiple requests race", () => | 93 | it.effect("atomically consumes a one-time token when multiple requests race", () => |
| ... | @@ -112,7 +112,7 @@ it.layer(NodeServices.layer)("PairingGrantStore.layer", (it) => { | ... | @@ -112,7 +112,7 @@ it.layer(NodeServices.layer)("PairingGrantStore.layer", (it) => { |
| 112 | expect(failure.failure._tag).toBe("UnknownBootstrapCredentialError"); | 112 | expect(failure.failure._tag).toBe("UnknownBootstrapCredentialError"); |
| 113 | expect(failure.failure.message).toContain("Unknown bootstrap credential"); | 113 | expect(failure.failure.message).toContain("Unknown bootstrap credential"); |
| 114 | } | 114 | } |
| 115 | }).pipe(Effect.provide(makePairingGrantStoreLayer())), | 115 | }).pipe(Effect.provide(layerPairingGrantStore())), |
| 116 | ); | 116 | ); |
| 117 | 117 | ||
| 118 | it.effect("requires the bound proof key thumbprint when present", () => | 118 | it.effect("requires the bound proof key thumbprint when present", () => |
| ... | @@ -135,7 +135,7 @@ it.layer(NodeServices.layer)("PairingGrantStore.layer", (it) => { | ... | @@ -135,7 +135,7 @@ it.layer(NodeServices.layer)("PairingGrantStore.layer", (it) => { |
| 135 | expect(missing.message).toContain("proof key mismatch"); | 135 | expect(missing.message).toContain("proof key mismatch"); |
| 136 | expect(wrong.message).toContain("proof key mismatch"); | 136 | expect(wrong.message).toContain("proof key mismatch"); |
| 137 | expect(consumed.proofKeyThumbprint).toBe("client-proof-key-thumbprint"); | 137 | expect(consumed.proofKeyThumbprint).toBe("client-proof-key-thumbprint"); |
| 138 | }).pipe(Effect.provide(makePairingGrantStoreLayer())), | 138 | }).pipe(Effect.provide(layerPairingGrantStore())), |
| 139 | ); | 139 | ); |
| 140 | 140 | ||
| 141 | it.effect("seeds the desktop bootstrap credential as a reusable grant", () => | 141 | it.effect("seeds the desktop bootstrap credential as a reusable grant", () => |
| ... | @@ -161,7 +161,7 @@ it.layer(NodeServices.layer)("PairingGrantStore.layer", (it) => { | ... | @@ -161,7 +161,7 @@ it.layer(NodeServices.layer)("PairingGrantStore.layer", (it) => { |
| 161 | expect(third.method).toBe("desktop-bootstrap"); | 161 | expect(third.method).toBe("desktop-bootstrap"); |
| 162 | }).pipe( | 162 | }).pipe( |
| 163 | Effect.provide( | 163 | Effect.provide( |
| 164 | makePairingGrantStoreLayer({ | 164 | layerPairingGrantStore({ |
| 165 | desktopBootstrapToken: "desktop-bootstrap-token", | 165 | desktopBootstrapToken: "desktop-bootstrap-token", |
| 166 | }), | 166 | }), |
| 167 | ), | 167 | ), |
| ... | @@ -186,7 +186,7 @@ it.layer(NodeServices.layer)("PairingGrantStore.layer", (it) => { | ... | @@ -186,7 +186,7 @@ it.layer(NodeServices.layer)("PairingGrantStore.layer", (it) => { |
| 186 | }).pipe( | 186 | }).pipe( |
| 187 | Effect.provide( | 187 | Effect.provide( |
| 188 | Layer.merge( | 188 | Layer.merge( |
| 189 | makePairingGrantStoreLayer({ | 189 | layerPairingGrantStore({ |
| 190 | desktopBootstrapToken: "desktop-bootstrap-token", | 190 | desktopBootstrapToken: "desktop-bootstrap-token", |
| 191 | }), | 191 | }), |
| 192 | TestClock.layer(), | 192 | TestClock.layer(), |
| ... | @@ -217,7 +217,7 @@ it.layer(NodeServices.layer)("PairingGrantStore.layer", (it) => { | ... | @@ -217,7 +217,7 @@ it.layer(NodeServices.layer)("PairingGrantStore.layer", (it) => { |
| 217 | expect(consumed.scopes).toEqual(change.pairingLink.scopes); | 217 | expect(consumed.scopes).toEqual(change.pairingLink.scopes); |
| 218 | expect(yield* Queue.take(changes)).toEqual({ type: "pairingLinkRemoved", id: issued.id }); | 218 | expect(yield* Queue.take(changes)).toEqual({ type: "pairingLinkRemoved", id: issued.id }); |
| 219 | } | 219 | } |
| 220 | }).pipe(Effect.scoped, Effect.provide(makePairingGrantStoreLayer())), | 220 | }).pipe(Effect.scoped, Effect.provide(layerPairingGrantStore())), |
| 221 | ); | 221 | ); |
| 222 | 222 | ||
| 223 | it.effect("lists and revokes active pairing links", () => | 223 | it.effect("lists and revokes active pairing links", () => |
| ... | @@ -244,7 +244,7 @@ it.layer(NodeServices.layer)("PairingGrantStore.layer", (it) => { | ... | @@ -244,7 +244,7 @@ it.layer(NodeServices.layer)("PairingGrantStore.layer", (it) => { |
| 244 | expect(activeAfterRevoke.map((entry) => entry.id)).toContain(second.id); | 244 | expect(activeAfterRevoke.map((entry) => entry.id)).toContain(second.id); |
| 245 | expect(revokedConsume.message).toContain("no longer available"); | 245 | expect(revokedConsume.message).toContain("no longer available"); |
| 246 | expect(revokedConsume._tag).toBe("UnavailableBootstrapCredentialError"); | 246 | expect(revokedConsume._tag).toBe("UnavailableBootstrapCredentialError"); |
| 247 | }).pipe(Effect.provide(makePairingGrantStoreLayer())), | 247 | }).pipe(Effect.provide(layerPairingGrantStore())), |
| 248 | ); | 248 | ); |
| 249 | 249 | ||
| 250 | it.effect("identifies consume-available failures and preserves their cause", () => { | 250 | it.effect("identifies consume-available failures and preserves their cause", () => { |
| ... | @@ -264,7 +264,7 @@ it.layer(NodeServices.layer)("PairingGrantStore.layer", (it) => { | ... | @@ -264,7 +264,7 @@ it.layer(NodeServices.layer)("PairingGrantStore.layer", (it) => { |
| 264 | expect(error.cause).toBe(repositoryFailure); | 264 | expect(error.cause).toBe(repositoryFailure); |
| 265 | }).pipe( | 265 | }).pipe( |
| 266 | Effect.provide( | 266 | Effect.provide( |
| 267 | makePairingGrantStoreTestLayer({ | 267 | layerPairingGrantStoreTest({ |
| 268 | consumeAvailable: () => Effect.fail(repositoryFailure), | 268 | consumeAvailable: () => Effect.fail(repositoryFailure), |
| 269 | }), | 269 | }), |
| 270 | ), | 270 | ), |
apps/server/src/auth/RpcAuthorization.test.ts+2-2| ... | @@ -15,8 +15,8 @@ import { | ... | @@ -15,8 +15,8 @@ import { |
| 15 | RPC_REQUIRED_SCOPES, | 15 | RPC_REQUIRED_SCOPES, |
| 16 | requiredScopeForRpcMethod, | 16 | requiredScopeForRpcMethod, |
| 17 | requiredScopeForDeviceList, | 17 | requiredScopeForDeviceList, |
| 18 | rpcScopeAuthorizationLayer, | ||
| 19 | } from "./RpcAuthorization.ts"; | 18 | } from "./RpcAuthorization.ts"; |
| 19 | import * as RpcAuthorization from "./RpcAuthorization.ts"; | ||
| 20 | 20 | ||
| 21 | describe("RPC authorization scopes", () => { | 21 | describe("RPC authorization scopes", () => { |
| 22 | it("declares exactly one scope for every RPC in the server group", () => { | 22 | it("declares exactly one scope for every RPC in the server group", () => { |
| ... | @@ -150,7 +150,7 @@ describe("RPC scope middleware", () => { | ... | @@ -150,7 +150,7 @@ describe("RPC scope middleware", () => { |
| 150 | group.toLayerHandler(WS_METHODS.serverRetryResourceTelemetry, () => | 150 | group.toLayerHandler(WS_METHODS.serverRetryResourceTelemetry, () => |
| 151 | Effect.sync(() => handled.push("retry")).pipe(Effect.andThen(Effect.never)), | 151 | Effect.sync(() => handled.push("retry")).pipe(Effect.andThen(Effect.never)), |
| 152 | ), | 152 | ), |
| 153 | rpcScopeAuthorizationLayer([AuthOrchestrationReadScope]), | 153 | RpcAuthorization.layer([AuthOrchestrationReadScope]), |
| 154 | ), | 154 | ), |
| 155 | ), | 155 | ), |
| 156 | ); | 156 | ); |
apps/server/src/auth/RpcAuthorization.ts+1-1| ... | @@ -229,7 +229,7 @@ export const rpcAuthorizationError = (requiredScope: AuthEnvironmentScope) => | ... | @@ -229,7 +229,7 @@ export const rpcAuthorizationError = (requiredScope: AuthEnvironmentScope) => |
| 229 | }); | 229 | }); |
| 230 | 230 | ||
| 231 | /** Authorizes every RPC on one connection against that connection's session scopes. */ | 231 | /** Authorizes every RPC on one connection against that connection's session scopes. */ |
| 232 | export const rpcScopeAuthorizationLayer = (scopes: ReadonlyArray<AuthEnvironmentScope>) => | 232 | export const layer = (scopes: ReadonlyArray<AuthEnvironmentScope>) => |
| 233 | Layer.succeed(RpcScopeAuthorization)((effect, { rpc }) => { | 233 | Layer.succeed(RpcScopeAuthorization)((effect, { rpc }) => { |
| 234 | const requiredScope = requiredScopeForRpcMethod(rpc._tag); | 234 | const requiredScope = requiredScopeForRpcMethod(rpc._tag); |
| 235 | return scopes.includes(requiredScope) | 235 | return scopes.includes(requiredScope) |
apps/server/src/auth/ServerSecretStore.test.ts+27-28| ... | @@ -12,13 +12,12 @@ import * as PlatformError from "effect/PlatformError"; | ... | @@ -12,13 +12,12 @@ import * as PlatformError from "effect/PlatformError"; |
| 12 | import * as ServerConfig from "../config.ts"; | 12 | import * as ServerConfig from "../config.ts"; |
| 13 | import * as ServerSecretStore from "./ServerSecretStore.ts"; | 13 | import * as ServerSecretStore from "./ServerSecretStore.ts"; |
| 14 | 14 | ||
| 15 | const makeServerConfigLayer = () => | 15 | const layerServerConfig = () => |
| 16 | ServerConfig.layerTest(process.cwd(), { prefix: "t3-secret-store-test-" }); | 16 | ServerConfig.layerTest(process.cwd(), { prefix: "t3-secret-store-test-" }); |
| 17 | 17 | ||
| 18 | const makeServerSecretStoreLayer = () => | 18 | const layerServerSecretStore = () => Layer.provide(ServerSecretStore.layer, layerServerConfig()); |
| 19 | Layer.provide(ServerSecretStore.layer, makeServerConfigLayer()); | ||
| 20 | 19 | ||
| 21 | const PermissionDeniedFileSystemLayer = Layer.effect( | 20 | const layerPermissionDeniedFileSystem = Layer.effect( |
| 22 | FileSystem.FileSystem, | 21 | FileSystem.FileSystem, |
| 23 | Effect.gen(function* () { | 22 | Effect.gen(function* () { |
| 24 | const fileSystem = yield* FileSystem.FileSystem; | 23 | const fileSystem = yield* FileSystem.FileSystem; |
| ... | @@ -39,13 +38,13 @@ const PermissionDeniedFileSystemLayer = Layer.effect( | ... | @@ -39,13 +38,13 @@ const PermissionDeniedFileSystemLayer = Layer.effect( |
| 39 | }), | 38 | }), |
| 40 | ).pipe(Layer.provide(NodeServices.layer)); | 39 | ).pipe(Layer.provide(NodeServices.layer)); |
| 41 | 40 | ||
| 42 | const makePermissionDeniedSecretStoreLayer = () => | 41 | const layerPermissionDeniedSecretStore = () => |
| 43 | ServerSecretStore.layer.pipe( | 42 | ServerSecretStore.layer.pipe( |
| 44 | Layer.provide(makeServerConfigLayer()), | 43 | Layer.provide(layerServerConfig()), |
| 45 | Layer.provideMerge(PermissionDeniedFileSystemLayer), | 44 | Layer.provideMerge(layerPermissionDeniedFileSystem), |
| 46 | ); | 45 | ); |
| 47 | 46 | ||
| 48 | const RenameFailureFileSystemLayer = Layer.effect( | 47 | const layerRenameFailureFileSystem = Layer.effect( |
| 49 | FileSystem.FileSystem, | 48 | FileSystem.FileSystem, |
| 50 | Effect.gen(function* () { | 49 | Effect.gen(function* () { |
| 51 | const fileSystem = yield* FileSystem.FileSystem; | 50 | const fileSystem = yield* FileSystem.FileSystem; |
| ... | @@ -66,13 +65,13 @@ const RenameFailureFileSystemLayer = Layer.effect( | ... | @@ -66,13 +65,13 @@ const RenameFailureFileSystemLayer = Layer.effect( |
| 66 | }), | 65 | }), |
| 67 | ).pipe(Layer.provide(NodeServices.layer)); | 66 | ).pipe(Layer.provide(NodeServices.layer)); |
| 68 | 67 | ||
| 69 | const makeRenameFailureSecretStoreLayer = () => | 68 | const layerRenameFailureSecretStore = () => |
| 70 | ServerSecretStore.layer.pipe( | 69 | ServerSecretStore.layer.pipe( |
| 71 | Layer.provide(makeServerConfigLayer()), | 70 | Layer.provide(layerServerConfig()), |
| 72 | Layer.provideMerge(RenameFailureFileSystemLayer), | 71 | Layer.provideMerge(layerRenameFailureFileSystem), |
| 73 | ); | 72 | ); |
| 74 | 73 | ||
| 75 | const RemoveFailureFileSystemLayer = Layer.effect( | 74 | const layerRemoveFailureFileSystem = Layer.effect( |
| 76 | FileSystem.FileSystem, | 75 | FileSystem.FileSystem, |
| 77 | Effect.gen(function* () { | 76 | Effect.gen(function* () { |
| 78 | const fileSystem = yield* FileSystem.FileSystem; | 77 | const fileSystem = yield* FileSystem.FileSystem; |
| ... | @@ -93,13 +92,13 @@ const RemoveFailureFileSystemLayer = Layer.effect( | ... | @@ -93,13 +92,13 @@ const RemoveFailureFileSystemLayer = Layer.effect( |
| 93 | }), | 92 | }), |
| 94 | ).pipe(Layer.provide(NodeServices.layer)); | 93 | ).pipe(Layer.provide(NodeServices.layer)); |
| 95 | 94 | ||
| 96 | const makeRemoveFailureSecretStoreLayer = () => | 95 | const layerRemoveFailureSecretStore = () => |
| 97 | ServerSecretStore.layer.pipe( | 96 | ServerSecretStore.layer.pipe( |
| 98 | Layer.provide(makeServerConfigLayer()), | 97 | Layer.provide(layerServerConfig()), |
| 99 | Layer.provideMerge(RemoveFailureFileSystemLayer), | 98 | Layer.provideMerge(layerRemoveFailureFileSystem), |
| 100 | ); | 99 | ); |
| 101 | 100 | ||
| 102 | const ConcurrentReadMissFileSystemLayer = Layer.effect( | 101 | const layerConcurrentReadMissFileSystem = Layer.effect( |
| 103 | FileSystem.FileSystem, | 102 | FileSystem.FileSystem, |
| 104 | Effect.gen(function* () { | 103 | Effect.gen(function* () { |
| 105 | const fileSystem = yield* FileSystem.FileSystem; | 104 | const fileSystem = yield* FileSystem.FileSystem; |
| ... | @@ -139,10 +138,10 @@ const ConcurrentReadMissFileSystemLayer = Layer.effect( | ... | @@ -139,10 +138,10 @@ const ConcurrentReadMissFileSystemLayer = Layer.effect( |
| 139 | }), | 138 | }), |
| 140 | ).pipe(Layer.provide(NodeServices.layer)); | 139 | ).pipe(Layer.provide(NodeServices.layer)); |
| 141 | 140 | ||
| 142 | const makeConcurrentCreateSecretStoreLayer = () => | 141 | const layerConcurrentCreateSecretStore = () => |
| 143 | ServerSecretStore.layer.pipe( | 142 | ServerSecretStore.layer.pipe( |
| 144 | Layer.provide(makeServerConfigLayer()), | 143 | Layer.provide(layerServerConfig()), |
| 145 | Layer.provideMerge(ConcurrentReadMissFileSystemLayer), | 144 | Layer.provideMerge(layerConcurrentReadMissFileSystem), |
| 146 | ); | 145 | ); |
| 147 | 146 | ||
| 148 | it.layer(NodeServices.layer)("ServerSecretStore.layer", (it) => { | 147 | it.layer(NodeServices.layer)("ServerSecretStore.layer", (it) => { |
| ... | @@ -153,7 +152,7 @@ it.layer(NodeServices.layer)("ServerSecretStore.layer", (it) => { | ... | @@ -153,7 +152,7 @@ it.layer(NodeServices.layer)("ServerSecretStore.layer", (it) => { |
| 153 | const secret = yield* secretStore.get("missing-secret"); | 152 | const secret = yield* secretStore.get("missing-secret"); |
| 154 | 153 | ||
| 155 | assert.isTrue(Option.isNone(secret)); | 154 | assert.isTrue(Option.isNone(secret)); |
| 156 | }).pipe(Effect.provide(makeServerSecretStoreLayer())), | 155 | }).pipe(Effect.provide(layerServerSecretStore())), |
| 157 | ); | 156 | ); |
| 158 | 157 | ||
| 159 | it.effect("reuses an existing secret instead of regenerating it", () => | 158 | it.effect("reuses an existing secret instead of regenerating it", () => |
| ... | @@ -164,7 +163,7 @@ it.layer(NodeServices.layer)("ServerSecretStore.layer", (it) => { | ... | @@ -164,7 +163,7 @@ it.layer(NodeServices.layer)("ServerSecretStore.layer", (it) => { |
| 164 | const second = yield* secretStore.getOrCreateRandom("session-signing-key", 32); | 163 | const second = yield* secretStore.getOrCreateRandom("session-signing-key", 32); |
| 165 | 164 | ||
| 166 | assert.deepEqual(Array.from(second), Array.from(first)); | 165 | assert.deepEqual(Array.from(second), Array.from(first)); |
| 167 | }).pipe(Effect.provide(makeServerSecretStoreLayer())), | 166 | }).pipe(Effect.provide(layerServerSecretStore())), |
| 168 | ); | 167 | ); |
| 169 | 168 | ||
| 170 | it.effect("returns the persisted secret when concurrent creators race", () => | 169 | it.effect("returns the persisted secret when concurrent creators race", () => |
| ... | @@ -183,13 +182,13 @@ it.layer(NodeServices.layer)("ServerSecretStore.layer", (it) => { | ... | @@ -183,13 +182,13 @@ it.layer(NodeServices.layer)("ServerSecretStore.layer", (it) => { |
| 183 | 182 | ||
| 184 | assert.deepEqual(Array.from(first), Array.from(persistedBytes)); | 183 | assert.deepEqual(Array.from(first), Array.from(persistedBytes)); |
| 185 | assert.deepEqual(Array.from(second), Array.from(persistedBytes)); | 184 | assert.deepEqual(Array.from(second), Array.from(persistedBytes)); |
| 186 | }).pipe(Effect.provide(makeConcurrentCreateSecretStoreLayer())), | 185 | }).pipe(Effect.provide(layerConcurrentCreateSecretStore())), |
| 187 | ); | 186 | ); |
| 188 | 187 | ||
| 189 | it.effect("uses restrictive permissions for the secret directory and files", () => | 188 | it.effect("uses restrictive permissions for the secret directory and files", () => |
| 190 | Effect.gen(function* () { | 189 | Effect.gen(function* () { |
| 191 | const chmodCalls: Array<{ readonly path: string; readonly mode: number }> = []; | 190 | const chmodCalls: Array<{ readonly path: string; readonly mode: number }> = []; |
| 192 | const recordingFileSystemLayer = Layer.effect( | 191 | const layerRecordingFileSystem = Layer.effect( |
| 193 | FileSystem.FileSystem, | 192 | FileSystem.FileSystem, |
| 194 | Effect.gen(function* () { | 193 | Effect.gen(function* () { |
| 195 | const fileSystem = yield* FileSystem.FileSystem; | 194 | const fileSystem = yield* FileSystem.FileSystem; |
| ... | @@ -210,8 +209,8 @@ it.layer(NodeServices.layer)("ServerSecretStore.layer", (it) => { | ... | @@ -210,8 +209,8 @@ it.layer(NodeServices.layer)("ServerSecretStore.layer", (it) => { |
| 210 | const secretStore = yield* Effect.service(ServerSecretStore.ServerSecretStore).pipe( | 209 | const secretStore = yield* Effect.service(ServerSecretStore.ServerSecretStore).pipe( |
| 211 | Effect.provide( | 210 | Effect.provide( |
| 212 | ServerSecretStore.layer.pipe( | 211 | ServerSecretStore.layer.pipe( |
| 213 | Layer.provide(makeServerConfigLayer()), | 212 | Layer.provide(layerServerConfig()), |
| 214 | Layer.provideMerge(recordingFileSystemLayer), | 213 | Layer.provideMerge(layerRecordingFileSystem), |
| 215 | ), | 214 | ), |
| 216 | ), | 215 | ), |
| 217 | ); | 216 | ); |
| ... | @@ -235,7 +234,7 @@ it.layer(NodeServices.layer)("ServerSecretStore.layer", (it) => { | ... | @@ -235,7 +234,7 @@ it.layer(NodeServices.layer)("ServerSecretStore.layer", (it) => { |
| 235 | assert.include(error.message, "Failed to read secret session-signing-key."); | 234 | assert.include(error.message, "Failed to read secret session-signing-key."); |
| 236 | assert.instanceOf(error.cause, PlatformError.PlatformError); | 235 | assert.instanceOf(error.cause, PlatformError.PlatformError); |
| 237 | assert.equal((error.cause as PlatformError.PlatformError).reason._tag, "PermissionDenied"); | 236 | assert.equal((error.cause as PlatformError.PlatformError).reason._tag, "PermissionDenied"); |
| 238 | }).pipe(Effect.provide(makePermissionDeniedSecretStoreLayer())), | 237 | }).pipe(Effect.provide(layerPermissionDeniedSecretStore())), |
| 239 | ); | 238 | ); |
| 240 | 239 | ||
| 241 | it.effect("propagates write failures instead of treating them as success", () => | 240 | it.effect("propagates write failures instead of treating them as success", () => |
| ... | @@ -250,7 +249,7 @@ it.layer(NodeServices.layer)("ServerSecretStore.layer", (it) => { | ... | @@ -250,7 +249,7 @@ it.layer(NodeServices.layer)("ServerSecretStore.layer", (it) => { |
| 250 | assert.include(error.message, "Failed to persist secret session-signing-key."); | 249 | assert.include(error.message, "Failed to persist secret session-signing-key."); |
| 251 | assert.instanceOf(error.cause, PlatformError.PlatformError); | 250 | assert.instanceOf(error.cause, PlatformError.PlatformError); |
| 252 | assert.equal((error.cause as PlatformError.PlatformError).reason._tag, "PermissionDenied"); | 251 | assert.equal((error.cause as PlatformError.PlatformError).reason._tag, "PermissionDenied"); |
| 253 | }).pipe(Effect.provide(makeRenameFailureSecretStoreLayer())), | 252 | }).pipe(Effect.provide(layerRenameFailureSecretStore())), |
| 254 | ); | 253 | ); |
| 255 | 254 | ||
| 256 | it.effect("propagates remove failures other than missing-file errors", () => | 255 | it.effect("propagates remove failures other than missing-file errors", () => |
| ... | @@ -263,6 +262,6 @@ it.layer(NodeServices.layer)("ServerSecretStore.layer", (it) => { | ... | @@ -263,6 +262,6 @@ it.layer(NodeServices.layer)("ServerSecretStore.layer", (it) => { |
| 263 | assert.include(error.message, "Failed to remove secret session-signing-key."); | 262 | assert.include(error.message, "Failed to remove secret session-signing-key."); |
| 264 | assert.instanceOf(error.cause, PlatformError.PlatformError); | 263 | assert.instanceOf(error.cause, PlatformError.PlatformError); |
| 265 | assert.equal((error.cause as PlatformError.PlatformError).reason._tag, "PermissionDenied"); | 264 | assert.equal((error.cause as PlatformError.PlatformError).reason._tag, "PermissionDenied"); |
| 266 | }).pipe(Effect.provide(makeRemoveFailureSecretStoreLayer())), | 265 | }).pipe(Effect.provide(layerRemoveFailureSecretStore())), |
| 267 | ); | 266 | ); |
| 268 | }); | 267 | }); |
apps/server/src/auth/SessionStore.test.ts+44-52| ... | @@ -15,15 +15,13 @@ import * as SqlClient from "effect/sql/SqlClient"; | ... | @@ -15,15 +15,13 @@ import * as SqlClient from "effect/sql/SqlClient"; |
| 15 | import * as ServerConfig from "../config.ts"; | 15 | import * as ServerConfig from "../config.ts"; |
| 16 | import * as ServerEnvironment from "../environment/ServerEnvironment.ts"; | 16 | import * as ServerEnvironment from "../environment/ServerEnvironment.ts"; |
| 17 | import { PersistenceSqlError } from "../persistence/Errors.ts"; | 17 | import { PersistenceSqlError } from "../persistence/Errors.ts"; |
| 18 | import { | 18 | |
| 19 | makeSqlitePersistenceLive, | 19 | import * as SqlitePersistence from "../persistence/Layers/Sqlite.ts"; |
| 20 | SqlitePersistenceMemory, | ||
| 21 | } from "../persistence/Layers/Sqlite.ts"; | ||
| 22 | import * as AuthSessions from "../persistence/AuthSessions.ts"; | 20 | import * as AuthSessions from "../persistence/AuthSessions.ts"; |
| 23 | import * as SessionStore from "./SessionStore.ts"; | 21 | import * as SessionStore from "./SessionStore.ts"; |
| 24 | import * as ServerSecretStore from "./ServerSecretStore.ts"; | 22 | import * as ServerSecretStore from "./ServerSecretStore.ts"; |
| 25 | 23 | ||
| 26 | const makeServerConfigLayer = (overrides?: Partial<ServerConfig.ServerConfig["Service"]>) => | 24 | const layerServerConfig = (overrides?: Partial<ServerConfig.ServerConfig["Service"]>) => |
| 27 | Layer.effect( | 25 | Layer.effect( |
| 28 | ServerConfig.ServerConfig, | 26 | ServerConfig.ServerConfig, |
| 29 | Effect.gen(function* () { | 27 | Effect.gen(function* () { |
| ... | @@ -35,20 +33,20 @@ const makeServerConfigLayer = (overrides?: Partial<ServerConfig.ServerConfig["Se | ... | @@ -35,20 +33,20 @@ const makeServerConfigLayer = (overrides?: Partial<ServerConfig.ServerConfig["Se |
| 35 | }), | 33 | }), |
| 36 | ).pipe(Layer.provide(ServerConfig.layerTest(process.cwd(), { prefix: "t3-auth-session-test-" }))); | 34 | ).pipe(Layer.provide(ServerConfig.layerTest(process.cwd(), { prefix: "t3-auth-session-test-" }))); |
| 37 | 35 | ||
| 38 | const makeServerEnvironmentLayer = (environmentId: EnvironmentId) => | 36 | const layerServerEnvironment = (environmentId: EnvironmentId) => |
| 39 | Layer.succeed(ServerEnvironment.ServerEnvironmentIdentity, { | 37 | Layer.succeed(ServerEnvironment.ServerEnvironmentIdentity, { |
| 40 | getEnvironmentId: Effect.succeed(environmentId), | 38 | getEnvironmentId: Effect.succeed(environmentId), |
| 41 | }); | 39 | }); |
| 42 | 40 | ||
| 43 | const makeSessionStoreLayer = ( | 41 | const layerSessionStore = ( |
| 44 | overrides?: Partial<ServerConfig.ServerConfig["Service"]>, | 42 | overrides?: Partial<ServerConfig.ServerConfig["Service"]>, |
| 45 | environmentId = EnvironmentId.make("test-environment"), | 43 | environmentId = EnvironmentId.make("test-environment"), |
| 46 | ) => | 44 | ) => |
| 47 | SessionStore.layer.pipe( | 45 | SessionStore.layer.pipe( |
| 48 | Layer.provide(SqlitePersistenceMemory), | 46 | Layer.provide(SqlitePersistence.layerMemory), |
| 49 | Layer.provide(ServerSecretStore.layer), | 47 | Layer.provide(ServerSecretStore.layer), |
| 50 | Layer.provide(makeServerEnvironmentLayer(environmentId)), | 48 | Layer.provide(layerServerEnvironment(environmentId)), |
| 51 | Layer.provide(makeServerConfigLayer(overrides)), | 49 | Layer.provide(layerServerConfig(overrides)), |
| 52 | ); | 50 | ); |
| 53 | 51 | ||
| 54 | const relaySessionInput = { | 52 | const relaySessionInput = { |
| ... | @@ -68,13 +66,13 @@ const makeDiskSessionStoreLayer = Effect.fn("makeDiskSessionStoreLayer")(functio | ... | @@ -68,13 +66,13 @@ const makeDiskSessionStoreLayer = Effect.fn("makeDiskSessionStoreLayer")(functio |
| 68 | baseDirIsExplicit: true, | 66 | baseDirIsExplicit: true, |
| 69 | }); | 67 | }); |
| 70 | yield* ServerConfig.ensureServerDirectories(paths); | 68 | yield* ServerConfig.ensureServerDirectories(paths); |
| 71 | const persistence = makeSqlitePersistenceLive(paths.dbPath); | 69 | const layerPersistence = SqlitePersistence.layerFromPath(paths.dbPath); |
| 72 | return SessionStore.layer.pipe( | 70 | return SessionStore.layer.pipe( |
| 73 | Layer.provide(persistence), | 71 | Layer.provide(layerPersistence), |
| 74 | Layer.provide(ServerSecretStore.layer), | 72 | Layer.provide(ServerSecretStore.layer), |
| 75 | Layer.provide(makeServerEnvironmentLayer(EnvironmentId.make(baseDir))), | 73 | Layer.provide(layerServerEnvironment(EnvironmentId.make(baseDir))), |
| 76 | Layer.provide( | 74 | Layer.provide( |
| 77 | makeServerConfigLayer({ | 75 | layerServerConfig({ |
| 78 | ...paths, | 76 | ...paths, |
| 79 | baseDir, | 77 | baseDir, |
| 80 | mode: "web", | 78 | mode: "web", |
| ... | @@ -90,7 +88,7 @@ const repositoryFailure = new PersistenceSqlError({ | ... | @@ -90,7 +88,7 @@ const repositoryFailure = new PersistenceSqlError({ |
| 90 | detail: "sqlite is unavailable", | 88 | detail: "sqlite is unavailable", |
| 91 | }); | 89 | }); |
| 92 | 90 | ||
| 93 | const failingSessionLookupRepositoryLayer = Layer.succeed(AuthSessions.AuthSessionRepository, { | 91 | const layerFailingSessionLookupRepository = Layer.succeed(AuthSessions.AuthSessionRepository, { |
| 94 | create: () => Effect.void, | 92 | create: () => Effect.void, |
| 95 | createReplacingActive: () => Effect.succeed([]), | 93 | createReplacingActive: () => Effect.succeed([]), |
| 96 | createIfAbsent: () => Effect.void, | 94 | createIfAbsent: () => Effect.void, |
| ... | @@ -102,15 +100,15 @@ const failingSessionLookupRepositoryLayer = Layer.succeed(AuthSessions.AuthSessi | ... | @@ -102,15 +100,15 @@ const failingSessionLookupRepositoryLayer = Layer.succeed(AuthSessions.AuthSessi |
| 102 | setClientConnection: () => Effect.void, | 100 | setClientConnection: () => Effect.void, |
| 103 | }); | 101 | }); |
| 104 | 102 | ||
| 105 | const failingSessionLookupCredentialLayer = Layer.effect( | 103 | const layerFailingSessionLookupCredential = Layer.effect( |
| 106 | SessionStore.SessionStore, | 104 | SessionStore.SessionStore, |
| 107 | SessionStore.make, | 105 | SessionStore.make, |
| 108 | ).pipe( | 106 | ).pipe( |
| 109 | Layer.provide(failingSessionLookupRepositoryLayer), | 107 | Layer.provide(layerFailingSessionLookupRepository), |
| 110 | Layer.provide(ServerSecretStore.layer), | 108 | Layer.provide(ServerSecretStore.layer), |
| 111 | Layer.provide(SqlitePersistenceMemory), | 109 | Layer.provide(SqlitePersistence.layerMemory), |
| 112 | Layer.provide(makeServerEnvironmentLayer(EnvironmentId.make("test-environment"))), | 110 | Layer.provide(layerServerEnvironment(EnvironmentId.make("test-environment"))), |
| 113 | Layer.provide(makeServerConfigLayer()), | 111 | Layer.provide(layerServerConfig()), |
| 114 | ); | 112 | ); |
| 115 | 113 | ||
| 116 | it.layer(NodeServices.layer)("SessionStore.layer", (it) => { | 114 | it.layer(NodeServices.layer)("SessionStore.layer", (it) => { |
| ... | @@ -122,7 +120,7 @@ it.layer(NodeServices.layer)("SessionStore.layer", (it) => { | ... | @@ -122,7 +120,7 @@ it.layer(NodeServices.layer)("SessionStore.layer", (it) => { |
| 122 | return sessions.cookieName; | 120 | return sessions.cookieName; |
| 123 | }).pipe( | 121 | }).pipe( |
| 124 | Effect.provide( | 122 | Effect.provide( |
| 125 | makeSessionStoreLayer({ mode: "web", host: "192.168.1.50", stateDir }, environmentId), | 123 | layerSessionStore({ mode: "web", host: "192.168.1.50", stateDir }, environmentId), |
| 126 | ), | 124 | ), |
| 127 | ); | 125 | ); |
| 128 | 126 | ||
| ... | @@ -164,20 +162,20 @@ it.layer(NodeServices.layer)("SessionStore.layer", (it) => { | ... | @@ -164,20 +162,20 @@ it.layer(NodeServices.layer)("SessionStore.layer", (it) => { |
| 164 | ); | 162 | ); |
| 165 | }).pipe(Effect.provide(layerB), Effect.scoped); | 163 | }).pipe(Effect.provide(layerB), Effect.scoped); |
| 166 | 164 | ||
| 167 | const reopenedB = yield* makeDiskSessionStoreLayer(baseB, token); | 165 | const layerReopenedB = yield* makeDiskSessionStoreLayer(baseB, token); |
| 168 | yield* Effect.gen(function* () { | 166 | yield* Effect.gen(function* () { |
| 169 | const sessions = yield* SessionStore.SessionStore; | 167 | const sessions = yield* SessionStore.SessionStore; |
| 170 | const dev = yield* sessions.verify(token); | 168 | const dev = yield* sessions.verify(token); |
| 171 | expect(dev.sessionId).toBe(fromA.dev.sessionId); | 169 | expect(dev.sessionId).toBe(fromA.dev.sessionId); |
| 172 | const ticket = yield* sessions.issueWebSocketToken(dev.sessionId); | 170 | const ticket = yield* sessions.issueWebSocketToken(dev.sessionId); |
| 173 | expect((yield* sessions.verifyWebSocketToken(ticket.token)).sessionId).toBe(dev.sessionId); | 171 | expect((yield* sessions.verifyWebSocketToken(ticket.token)).sessionId).toBe(dev.sessionId); |
| 174 | }).pipe(Effect.provide(reopenedB), Effect.scoped); | 172 | }).pipe(Effect.provide(layerReopenedB), Effect.scoped); |
| 175 | 173 | ||
| 176 | const reopenedA = yield* makeDiskSessionStoreLayer(baseA, token); | 174 | const layerReopenedA = yield* makeDiskSessionStoreLayer(baseA, token); |
| 177 | yield* Effect.gen(function* () { | 175 | yield* Effect.gen(function* () { |
| 178 | const sessions = yield* SessionStore.SessionStore; | 176 | const sessions = yield* SessionStore.SessionStore; |
| 179 | expect((yield* Effect.flip(sessions.verify(token)))._tag).toBe("SessionTokenRevokedError"); | 177 | expect((yield* Effect.flip(sessions.verify(token)))._tag).toBe("SessionTokenRevokedError"); |
| 180 | }).pipe(Effect.provide(reopenedA), Effect.scoped); | 178 | }).pipe(Effect.provide(layerReopenedA), Effect.scoped); |
| 181 | }), | 179 | }), |
| 182 | ); | 180 | ); |
| 183 | 181 | ||
| ... | @@ -187,15 +185,15 @@ it.layer(NodeServices.layer)("SessionStore.layer", (it) => { | ... | @@ -187,15 +185,15 @@ it.layer(NodeServices.layer)("SessionStore.layer", (it) => { |
| 187 | const baseDir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-dev-auth-rotation-" }); | 185 | const baseDir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-dev-auth-rotation-" }); |
| 188 | const oldToken = "old-reusable-dev-auth-token-that-is-long-enough"; | 186 | const oldToken = "old-reusable-dev-auth-token-that-is-long-enough"; |
| 189 | const newToken = "new-reusable-dev-auth-token-that-is-long-enough"; | 187 | const newToken = "new-reusable-dev-auth-token-that-is-long-enough"; |
| 190 | const initialLayer = yield* makeDiskSessionStoreLayer(baseDir, oldToken); | 188 | const layerInitial = yield* makeDiskSessionStoreLayer(baseDir, oldToken); |
| 191 | const old = yield* Effect.gen(function* () { | 189 | const old = yield* Effect.gen(function* () { |
| 192 | const sessions = yield* SessionStore.SessionStore; | 190 | const sessions = yield* SessionStore.SessionStore; |
| 193 | const dev = yield* sessions.verify(oldToken); | 191 | const dev = yield* sessions.verify(oldToken); |
| 194 | const ticket = yield* sessions.issueWebSocketToken(dev.sessionId); | 192 | const ticket = yield* sessions.issueWebSocketToken(dev.sessionId); |
| 195 | return { dev, ticket }; | 193 | return { dev, ticket }; |
| 196 | }).pipe(Effect.provide(initialLayer), Effect.scoped); | 194 | }).pipe(Effect.provide(layerInitial), Effect.scoped); |
| 197 | 195 | ||
| 198 | const rotatedLayer = yield* makeDiskSessionStoreLayer(baseDir, newToken); | 196 | const layerRotated = yield* makeDiskSessionStoreLayer(baseDir, newToken); |
| 199 | const rotated = yield* Effect.gen(function* () { | 197 | const rotated = yield* Effect.gen(function* () { |
| 200 | const sessions = yield* SessionStore.SessionStore; | 198 | const sessions = yield* SessionStore.SessionStore; |
| 201 | expect((yield* Effect.flip(sessions.verify(oldToken)))._tag).toBe( | 199 | expect((yield* Effect.flip(sessions.verify(oldToken)))._tag).toBe( |
| ... | @@ -210,9 +208,9 @@ it.layer(NodeServices.layer)("SessionStore.layer", (it) => { | ... | @@ -210,9 +208,9 @@ it.layer(NodeServices.layer)("SessionStore.layer", (it) => { |
| 210 | const dev = yield* sessions.verify(newToken); | 208 | const dev = yield* sessions.verify(newToken); |
| 211 | const ticket = yield* sessions.issueWebSocketToken(dev.sessionId); | 209 | const ticket = yield* sessions.issueWebSocketToken(dev.sessionId); |
| 212 | return { dev, ticket }; | 210 | return { dev, ticket }; |
| 213 | }).pipe(Effect.provide(rotatedLayer), Effect.scoped); | 211 | }).pipe(Effect.provide(layerRotated), Effect.scoped); |
| 214 | 212 | ||
| 215 | const removedLayer = yield* makeDiskSessionStoreLayer(baseDir); | 213 | const layerRemoved = yield* makeDiskSessionStoreLayer(baseDir); |
| 216 | yield* Effect.gen(function* () { | 214 | yield* Effect.gen(function* () { |
| 217 | const sessions = yield* SessionStore.SessionStore; | 215 | const sessions = yield* SessionStore.SessionStore; |
| 218 | expect((yield* Effect.flip(sessions.verify(newToken)))._tag).toBe( | 216 | expect((yield* Effect.flip(sessions.verify(newToken)))._tag).toBe( |
| ... | @@ -224,7 +222,7 @@ it.layer(NodeServices.layer)("SessionStore.layer", (it) => { | ... | @@ -224,7 +222,7 @@ it.layer(NodeServices.layer)("SessionStore.layer", (it) => { |
| 224 | expect( | 222 | expect( |
| 225 | (yield* sessions.listActive()).some((row) => row.sessionId === rotated.dev.sessionId), | 223 | (yield* sessions.listActive()).some((row) => row.sessionId === rotated.dev.sessionId), |
| 226 | ).toBe(true); | 224 | ).toBe(true); |
| 227 | }).pipe(Effect.provide(removedLayer), Effect.scoped); | 225 | }).pipe(Effect.provide(layerRemoved), Effect.scoped); |
| 228 | }), | 226 | }), |
| 229 | ); | 227 | ); |
| 230 | 228 | ||
| ... | @@ -243,7 +241,7 @@ it.layer(NodeServices.layer)("SessionStore.layer", (it) => { | ... | @@ -243,7 +241,7 @@ it.layer(NodeServices.layer)("SessionStore.layer", (it) => { |
| 243 | }).pipe( | 241 | }).pipe( |
| 244 | Effect.provide( | 242 | Effect.provide( |
| 245 | Layer.merge( | 243 | Layer.merge( |
| 246 | makeSessionStoreLayer({ | 244 | layerSessionStore({ |
| 247 | mode: "web", | 245 | mode: "web", |
| 248 | devUrl: new URL("http://127.0.0.1:5173"), | 246 | devUrl: new URL("http://127.0.0.1:5173"), |
| 249 | devAuthToken: Redacted.make("reusable-dev-auth-token-that-is-long-enough"), | 247 | devAuthToken: Redacted.make("reusable-dev-auth-token-that-is-long-enough"), |
| ... | @@ -276,7 +274,7 @@ it.layer(NodeServices.layer)("SessionStore.layer", (it) => { | ... | @@ -276,7 +274,7 @@ it.layer(NodeServices.layer)("SessionStore.layer", (it) => { |
| 276 | expect(verified.client.label).toBe("Desktop app"); | 274 | expect(verified.client.label).toBe("Desktop app"); |
| 277 | expect(verified.client.browser).toBe("Electron"); | 275 | expect(verified.client.browser).toBe("Electron"); |
| 278 | expect(verified.expiresAt?.toString()).toBe(issued.expiresAt.toString()); | 276 | expect(verified.expiresAt?.toString()).toBe(issued.expiresAt.toString()); |
| 279 | }).pipe(Effect.provide(makeSessionStoreLayer())), | 277 | }).pipe(Effect.provide(layerSessionStore())), |
| 280 | ); | 278 | ); |
| 281 | it.effect("rejects malformed session tokens", () => | 279 | it.effect("rejects malformed session tokens", () => |
| 282 | Effect.gen(function* () { | 280 | Effect.gen(function* () { |
| ... | @@ -285,7 +283,7 @@ it.layer(NodeServices.layer)("SessionStore.layer", (it) => { | ... | @@ -285,7 +283,7 @@ it.layer(NodeServices.layer)("SessionStore.layer", (it) => { |
| 285 | 283 | ||
| 286 | expect(error._tag).toBe("MalformedSessionTokenError"); | 284 | expect(error._tag).toBe("MalformedSessionTokenError"); |
| 287 | expect(error.message).toContain("Malformed session token"); | 285 | expect(error.message).toContain("Malformed session token"); |
| 288 | }).pipe(Effect.provide(makeSessionStoreLayer())), | 286 | }).pipe(Effect.provide(layerSessionStore())), |
| 289 | ); | 287 | ); |
| 290 | it.effect("preserves repository failures while verifying session and websocket credentials", () => | 288 | it.effect("preserves repository failures while verifying session and websocket credentials", () => |
| 291 | Effect.gen(function* () { | 289 | Effect.gen(function* () { |
| ... | @@ -321,7 +319,7 @@ it.layer(NodeServices.layer)("SessionStore.layer", (it) => { | ... | @@ -321,7 +319,7 @@ it.layer(NodeServices.layer)("SessionStore.layer", (it) => { |
| 321 | currentSessionId: issued.sessionId, | 319 | currentSessionId: issued.sessionId, |
| 322 | cause: repositoryFailure, | 320 | cause: repositoryFailure, |
| 323 | }); | 321 | }); |
| 324 | }).pipe(Effect.provide(failingSessionLookupCredentialLayer)), | 322 | }).pipe(Effect.provide(layerFailingSessionLookupCredential)), |
| 325 | ); | 323 | ); |
| 326 | it.effect("verifies session tokens against the Effect clock", () => | 324 | it.effect("verifies session tokens against the Effect clock", () => |
| 327 | Effect.gen(function* () { | 325 | Effect.gen(function* () { |
| ... | @@ -341,7 +339,7 @@ it.layer(NodeServices.layer)("SessionStore.layer", (it) => { | ... | @@ -341,7 +339,7 @@ it.layer(NodeServices.layer)("SessionStore.layer", (it) => { |
| 341 | "review:write", | 339 | "review:write", |
| 342 | "relay:read", | 340 | "relay:read", |
| 343 | ]); | 341 | ]); |
| 344 | }).pipe(Effect.provide(Layer.merge(makeSessionStoreLayer(), TestClock.layer()))), | 342 | }).pipe(Effect.provide(Layer.merge(layerSessionStore(), TestClock.layer()))), |
| 345 | ); | 343 | ); |
| 346 | 344 | ||
| 347 | it.effect("atomically replaces active sessions with the same subject and method", () => | 345 | it.effect("atomically replaces active sessions with the same subject and method", () => |
| ... | @@ -382,7 +380,7 @@ it.layer(NodeServices.layer)("SessionStore.layer", (it) => { | ... | @@ -382,7 +380,7 @@ it.layer(NodeServices.layer)("SessionStore.layer", (it) => { |
| 382 | ), | 380 | ), |
| 383 | ).toHaveLength(1); | 381 | ).toHaveLength(1); |
| 384 | expect(bearerVerification.filter(Option.isSome)).toHaveLength(1); | 382 | expect(bearerVerification.filter(Option.isSome)).toHaveLength(1); |
| 385 | }).pipe(Effect.provide(makeSessionStoreLayer())), | 383 | }).pipe(Effect.provide(layerSessionStore())), |
| 386 | ); | 384 | ); |
| 387 | 385 | ||
| 388 | it.effect("keeps the previous desktop session valid when replacement fails", () => | 386 | it.effect("keeps the previous desktop session valid when replacement fails", () => |
| ... | @@ -413,7 +411,7 @@ it.layer(NodeServices.layer)("SessionStore.layer", (it) => { | ... | @@ -413,7 +411,7 @@ it.layer(NodeServices.layer)("SessionStore.layer", (it) => { |
| 413 | expect((yield* sessions.listActive()).map((session) => session.sessionId)).toEqual([ | 411 | expect((yield* sessions.listActive()).map((session) => session.sessionId)).toEqual([ |
| 414 | previous.sessionId, | 412 | previous.sessionId, |
| 415 | ]); | 413 | ]); |
| 416 | }).pipe(Effect.provide(Layer.mergeAll(makeSessionStoreLayer(), SqlitePersistenceMemory))), | 414 | }).pipe(Effect.provide(Layer.mergeAll(layerSessionStore(), SqlitePersistence.layerMemory))), |
| 417 | ); | 415 | ); |
| 418 | 416 | ||
| 419 | it.effect("rejects websocket tokens once the parent session has expired", () => | 417 | it.effect("rejects websocket tokens once the parent session has expired", () => |
| ... | @@ -437,7 +435,7 @@ it.layer(NodeServices.layer)("SessionStore.layer", (it) => { | ... | @@ -437,7 +435,7 @@ it.layer(NodeServices.layer)("SessionStore.layer", (it) => { |
| 437 | error.expiresAt.epochMilliseconds, | 435 | error.expiresAt.epochMilliseconds, |
| 438 | ); | 436 | ); |
| 439 | } | 437 | } |
| 440 | }).pipe(Effect.provide(Layer.merge(makeSessionStoreLayer(), TestClock.layer()))), | 438 | }).pipe(Effect.provide(Layer.merge(layerSessionStore(), TestClock.layer()))), |
| 441 | ); | 439 | ); |
| 442 | 440 | ||
| 443 | it.effect("includes expiry context when session and websocket tokens expire", () => | 441 | it.effect("includes expiry context when session and websocket tokens expire", () => |
| ... | @@ -475,7 +473,7 @@ it.layer(NodeServices.layer)("SessionStore.layer", (it) => { | ... | @@ -475,7 +473,7 @@ it.layer(NodeServices.layer)("SessionStore.layer", (it) => { |
| 475 | websocketError.expiresAt.epochMilliseconds, | 473 | websocketError.expiresAt.epochMilliseconds, |
| 476 | ); | 474 | ); |
| 477 | } | 475 | } |
| 478 | }).pipe(Effect.provide(Layer.merge(makeSessionStoreLayer(), TestClock.layer()))), | 476 | }).pipe(Effect.provide(Layer.merge(layerSessionStore(), TestClock.layer()))), |
| 479 | ); | 477 | ); |
| 480 | 478 | ||
| 481 | it.effect("lists active sessions, tracks connectivity, and revokes other sessions", () => | 479 | it.effect("lists active sessions, tracks connectivity, and revokes other sessions", () => |
| ... | @@ -537,7 +535,7 @@ it.layer(NodeServices.layer)("SessionStore.layer", (it) => { | ... | @@ -537,7 +535,7 @@ it.layer(NodeServices.layer)("SessionStore.layer", (it) => { |
| 537 | expect(revokedClientWebSocket.sessionId).toBe(client.sessionId); | 535 | expect(revokedClientWebSocket.sessionId).toBe(client.sessionId); |
| 538 | expect(revokedClientWebSocket.revokedAt.epochMilliseconds).toBeGreaterThanOrEqual(0); | 536 | expect(revokedClientWebSocket.revokedAt.epochMilliseconds).toBeGreaterThanOrEqual(0); |
| 539 | } | 537 | } |
| 540 | }).pipe(Effect.provide(makeSessionStoreLayer())), | 538 | }).pipe(Effect.provide(layerSessionStore())), |
| 541 | ); | 539 | ); |
| 542 | 540 | ||
| 543 | it.effect("persists lastConnectedAt on first connect and updates it after reconnect", () => | 541 | it.effect("persists lastConnectedAt on first connect and updates it after reconnect", () => |
| ... | @@ -579,7 +577,7 @@ it.layer(NodeServices.layer)("SessionStore.layer", (it) => { | ... | @@ -579,7 +577,7 @@ it.layer(NodeServices.layer)("SessionStore.layer", (it) => { |
| 579 | expect(afterReconnect[0]?.connected).toBe(true); | 577 | expect(afterReconnect[0]?.connected).toBe(true); |
| 580 | expect(afterReconnect[0]?.lastConnectedAt).not.toBeNull(); | 578 | expect(afterReconnect[0]?.lastConnectedAt).not.toBeNull(); |
| 581 | expect(afterReconnect[0]?.lastConnectedAt?.toString()).not.toBe(firstConnectedAt?.toString()); | 579 | expect(afterReconnect[0]?.lastConnectedAt?.toString()).not.toBe(firstConnectedAt?.toString()); |
| 582 | }).pipe(Effect.provide(Layer.merge(makeSessionStoreLayer(), TestClock.layer()))), | 580 | }).pipe(Effect.provide(Layer.merge(layerSessionStore(), TestClock.layer()))), |
| 583 | ); | 581 | ); |
| 584 | 582 | ||
| 585 | it.effect("keeps connected relay sessions visible through expiry and HTTP renewal", () => | 583 | it.effect("keeps connected relay sessions visible through expiry and HTTP renewal", () => |
| ... | @@ -619,7 +617,7 @@ it.layer(NodeServices.layer)("SessionStore.layer", (it) => { | ... | @@ -619,7 +617,7 @@ it.layer(NodeServices.layer)("SessionStore.layer", (it) => { |
| 619 | }), | 617 | }), |
| 620 | ]), | 618 | ]), |
| 621 | ); | 619 | ); |
| 622 | }).pipe(Effect.provide(Layer.merge(makeSessionStoreLayer(), TestClock.layer()))), | 620 | }).pipe(Effect.provide(Layer.merge(layerSessionStore(), TestClock.layer()))), |
| 623 | ); | 621 | ); |
| 624 | 622 | ||
| 625 | it.effect.each([1, 2])( | 623 | it.effect.each([1, 2])( |
| ... | @@ -659,10 +657,7 @@ it.layer(NodeServices.layer)("SessionStore.layer", (it) => { | ... | @@ -659,10 +657,7 @@ it.layer(NodeServices.layer)("SessionStore.layer", (it) => { |
| 659 | expect(listed).toEqual([]); | 657 | expect(listed).toEqual([]); |
| 660 | } | 658 | } |
| 661 | } | 659 | } |
| 662 | }).pipe( | 660 | }).pipe(Effect.scoped, Effect.provide(Layer.merge(layerSessionStore(), TestClock.layer()))), |
| 663 | Effect.scoped, | ||
| 664 | Effect.provide(Layer.merge(makeSessionStoreLayer(), TestClock.layer())), | ||
| 665 | ), | ||
| 666 | ); | 661 | ); |
| 667 | 662 | ||
| 668 | it.effect.each(["revoke", "revokeAllExcept"] as const)( | 663 | it.effect.each(["revoke", "revokeAllExcept"] as const)( |
| ... | @@ -693,10 +688,7 @@ it.layer(NodeServices.layer)("SessionStore.layer", (it) => { | ... | @@ -693,10 +688,7 @@ it.layer(NodeServices.layer)("SessionStore.layer", (it) => { |
| 693 | const listed = yield* sessions.listActive(); | 688 | const listed = yield* sessions.listActive(); |
| 694 | expect(listed).toHaveLength(1); | 689 | expect(listed).toHaveLength(1); |
| 695 | expect(listed[0]?.sessionId).toBe(administrative.sessionId); | 690 | expect(listed[0]?.sessionId).toBe(administrative.sessionId); |
| 696 | }).pipe( | 691 | }).pipe(Effect.scoped, Effect.provide(Layer.merge(layerSessionStore(), TestClock.layer()))), |
| 697 | Effect.scoped, | ||
| 698 | Effect.provide(Layer.merge(makeSessionStoreLayer(), TestClock.layer())), | ||
| 699 | ), | ||
| 700 | ); | 692 | ); |
| 701 | 693 | ||
| 702 | it.effect("records client connection metadata without clearing prior values", () => | 694 | it.effect("records client connection metadata without clearing prior values", () => |
| ... | @@ -728,6 +720,6 @@ it.layer(NodeServices.layer)("SessionStore.layer", (it) => { | ... | @@ -728,6 +720,6 @@ it.layer(NodeServices.layer)("SessionStore.layer", (it) => { |
| 728 | 720 | ||
| 729 | yield* sessions.recordClientConnection(issued.sessionId, {}); | 721 | yield* sessions.recordClientConnection(issued.sessionId, {}); |
| 730 | expect((yield* readRow)[0]).toEqual({ surface: "mobile", appVersion: "1.3.0" }); | 722 | expect((yield* readRow)[0]).toEqual({ surface: "mobile", appVersion: "1.3.0" }); |
| 731 | }).pipe(Effect.provide(Layer.mergeAll(makeSessionStoreLayer(), SqlitePersistenceMemory))), | 723 | }).pipe(Effect.provide(Layer.mergeAll(layerSessionStore(), SqlitePersistence.layerMemory))), |
| 732 | ); | 724 | ); |
| 733 | }); | 725 | }); |
apps/server/src/auth/http.test.ts+14-14| ... | @@ -15,15 +15,15 @@ import * as HttpRouter from "effect/http/HttpRouter"; | ... | @@ -15,15 +15,15 @@ import * as HttpRouter from "effect/http/HttpRouter"; |
| 15 | 15 | ||
| 16 | import * as ServerConfig from "../config.ts"; | 16 | import * as ServerConfig from "../config.ts"; |
| 17 | import * as ServerEnvironment from "../environment/ServerEnvironment.ts"; | 17 | import * as ServerEnvironment from "../environment/ServerEnvironment.ts"; |
| 18 | import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; | 18 | import * as SqlitePersistence from "../persistence/Layers/Sqlite.ts"; |
| 19 | import * as EnvironmentAuth from "./EnvironmentAuth.ts"; | 19 | import * as EnvironmentAuth from "./EnvironmentAuth.ts"; |
| 20 | import * as ServerSecretStore from "./ServerSecretStore.ts"; | 20 | import * as ServerSecretStore from "./ServerSecretStore.ts"; |
| 21 | import { authHttpApiLayer, environmentAuthenticatedAuthLayer } from "./http.ts"; | 21 | import * as AuthHttp from "./http.ts"; |
| 22 | 22 | ||
| 23 | const DEV_TOKEN = "reusable-dev-auth-token-that-is-long-enough"; | 23 | const DEV_TOKEN = "reusable-dev-auth-token-that-is-long-enough"; |
| 24 | class AuthTestApi extends HttpApi.make("environment").add(EnvironmentHttpApi.groups.auth) {} | 24 | class AuthTestApi extends HttpApi.make("environment").add(EnvironmentHttpApi.groups.auth) {} |
| 25 | 25 | ||
| 26 | const configLayer = Layer.effect( | 26 | const layerConfig = Layer.effect( |
| 27 | ServerConfig.ServerConfig, | 27 | ServerConfig.ServerConfig, |
| 28 | Effect.gen(function* () { | 28 | Effect.gen(function* () { |
| 29 | const config = yield* ServerConfig.ServerConfig; | 29 | const config = yield* ServerConfig.ServerConfig; |
| ... | @@ -36,17 +36,17 @@ const configLayer = Layer.effect( | ... | @@ -36,17 +36,17 @@ const configLayer = Layer.effect( |
| 36 | }), | 36 | }), |
| 37 | ).pipe(Layer.provide(ServerConfig.layerTest(process.cwd(), { prefix: "t3-auth-http-test-" }))); | 37 | ).pipe(Layer.provide(ServerConfig.layerTest(process.cwd(), { prefix: "t3-auth-http-test-" }))); |
| 38 | 38 | ||
| 39 | const environmentAuthLayer = EnvironmentAuth.layer.pipe( | 39 | const layerEnvironmentAuth = EnvironmentAuth.layer.pipe( |
| 40 | Layer.provide(SqlitePersistenceMemory), | 40 | Layer.provide(SqlitePersistence.layerMemory), |
| 41 | Layer.provide(ServerSecretStore.layer), | 41 | Layer.provide(ServerSecretStore.layer), |
| 42 | Layer.provide(ServerEnvironment.identityLayer), | 42 | Layer.provide(ServerEnvironment.layerIdentity), |
| 43 | Layer.provide(configLayer), | 43 | Layer.provide(layerConfig), |
| 44 | ); | 44 | ); |
| 45 | const routesLayer = HttpApiBuilder.layer(AuthTestApi).pipe( | 45 | const layerRoutes = HttpApiBuilder.layer(AuthTestApi).pipe( |
| 46 | Layer.provide(authHttpApiLayer), | 46 | Layer.provide(AuthHttp.layer), |
| 47 | Layer.provide(environmentAuthenticatedAuthLayer), | 47 | Layer.provide(AuthHttp.layerAuthenticatedAuth), |
| 48 | Layer.provideMerge(environmentAuthLayer), | 48 | Layer.provideMerge(layerEnvironmentAuth), |
| 49 | Layer.provide(configLayer), | 49 | Layer.provide(layerConfig), |
| 50 | Layer.provideMerge( | 50 | Layer.provideMerge( |
| 51 | HttpPlatform.layer.pipe( | 51 | HttpPlatform.layer.pipe( |
| 52 | Layer.provideMerge(NodeServices.layer), | 52 | Layer.provideMerge(NodeServices.layer), |
| ... | @@ -81,8 +81,8 @@ it.effect("sets the selected browser session cookies through the HTTP route", () | ... | @@ -81,8 +81,8 @@ it.effect("sets the selected browser session cookies through the HTTP route", () |
| 81 | Effect.sync( | 81 | Effect.sync( |
| 82 | () => | 82 | () => |
| 83 | [ | 83 | [ |
| 84 | HttpRouter.toWebHandler(routesLayer, { disableLogger: true }), | 84 | HttpRouter.toWebHandler(layerRoutes, { disableLogger: true }), |
| 85 | HttpRouter.toWebHandler(routesLayer, { disableLogger: true }), | 85 | HttpRouter.toWebHandler(layerRoutes, { disableLogger: true }), |
| 86 | ] as const, | 86 | ] as const, |
| 87 | ), | 87 | ), |
| 88 | ([environmentA, environmentB]) => | 88 | ([environmentA, environmentB]) => |
apps/server/src/auth/http.ts+2-2| ... | @@ -198,7 +198,7 @@ export const requireEnvironmentScope = Effect.fn("environment.auth.requireScope" | ... | @@ -198,7 +198,7 @@ export const requireEnvironmentScope = Effect.fn("environment.auth.requireScope" |
| 198 | return session; | 198 | return session; |
| 199 | }); | 199 | }); |
| 200 | 200 | ||
| 201 | export const environmentAuthenticatedAuthLayer = Layer.effect( | 201 | export const layerAuthenticatedAuth = Layer.effect( |
| 202 | EnvironmentAuthenticatedAuth, | 202 | EnvironmentAuthenticatedAuth, |
| 203 | Effect.gen(function* () { | 203 | Effect.gen(function* () { |
| 204 | const serverAuth = yield* EnvironmentAuth.EnvironmentAuth; | 204 | const serverAuth = yield* EnvironmentAuth.EnvironmentAuth; |
| ... | @@ -227,7 +227,7 @@ export const environmentAuthenticatedAuthLayer = Layer.effect( | ... | @@ -227,7 +227,7 @@ export const environmentAuthenticatedAuthLayer = Layer.effect( |
| 227 | }), | 227 | }), |
| 228 | ); | 228 | ); |
| 229 | 229 | ||
| 230 | export const authHttpApiLayer = HttpApiBuilder.group( | 230 | export const layer = HttpApiBuilder.group( |
| 231 | EnvironmentHttpApi, | 231 | EnvironmentHttpApi, |
| 232 | "auth", | 232 | "auth", |
| 233 | Effect.fnUntraced(function* (handlers) { | 233 | Effect.fnUntraced(function* (handlers) { |
apps/server/src/background/BackgroundPolicy.test.ts+17-17| ... | @@ -53,11 +53,11 @@ function makeReport(overrides: Partial<ClientActivityReportInput> = {}): ClientA | ... | @@ -53,11 +53,11 @@ function makeReport(overrides: Partial<ClientActivityReportInput> = {}): ClientA |
| 53 | }; | 53 | }; |
| 54 | } | 54 | } |
| 55 | 55 | ||
| 56 | function makeLayer( | 56 | function layerFor( |
| 57 | hostPower: HostPowerSnapshot, | 57 | hostPower: HostPowerSnapshot, |
| 58 | settingsOverrides: Parameters<typeof ServerSettings.layerTest>[0] = {}, | 58 | settingsOverrides: Parameters<typeof ServerSettings.layerTest>[0] = {}, |
| 59 | ) { | 59 | ) { |
| 60 | const hostLayer = Layer.effect( | 60 | const layerHost = Layer.effect( |
| 61 | HostPowerMonitor.HostPowerMonitor, | 61 | HostPowerMonitor.HostPowerMonitor, |
| 62 | Effect.gen(function* () { | 62 | Effect.gen(function* () { |
| 63 | const changes = yield* PubSub.sliding<HostPowerSnapshot>(1); | 63 | const changes = yield* PubSub.sliding<HostPowerSnapshot>(1); |
| ... | @@ -73,7 +73,7 @@ function makeLayer( | ... | @@ -73,7 +73,7 @@ function makeLayer( |
| 73 | }), | 73 | }), |
| 74 | ); | 74 | ); |
| 75 | return BackgroundPolicy.layer.pipe( | 75 | return BackgroundPolicy.layer.pipe( |
| 76 | Layer.provide(Layer.merge(hostLayer, ServerSettings.layerTest(settingsOverrides))), | 76 | Layer.provide(Layer.merge(layerHost, ServerSettings.layerTest(settingsOverrides))), |
| 77 | ); | 77 | ); |
| 78 | } | 78 | } |
| 79 | 79 | ||
| ... | @@ -95,7 +95,7 @@ describe("BackgroundPolicy", () => { | ... | @@ -95,7 +95,7 @@ describe("BackgroundPolicy", () => { |
| 95 | assert.equal(yield* policy.hasDemand({ type: "vcs-status", cwd: "/other" }), false); | 95 | assert.equal(yield* policy.hasDemand({ type: "vcs-status", cwd: "/other" }), false); |
| 96 | assert.equal(yield* policy.shouldRunScopeWork({ type: "vcs-status", cwd: "/repo" }), true); | 96 | assert.equal(yield* policy.shouldRunScopeWork({ type: "vcs-status", cwd: "/repo" }), true); |
| 97 | assert.equal(yield* policy.shouldRunScopeWork({ type: "vcs-status", cwd: "/other" }), false); | 97 | assert.equal(yield* policy.shouldRunScopeWork({ type: "vcs-status", cwd: "/other" }), false); |
| 98 | }).pipe(Effect.provide(makeLayer(nominalHostPower))), | 98 | }).pipe(Effect.provide(layerFor(nominalHostPower))), |
| 99 | ); | 99 | ); |
| 100 | 100 | ||
| 101 | it.effect("removes all leases for a disconnected websocket connection", () => | 101 | it.effect("removes all leases for a disconnected websocket connection", () => |
| ... | @@ -112,7 +112,7 @@ describe("BackgroundPolicy", () => { | ... | @@ -112,7 +112,7 @@ describe("BackgroundPolicy", () => { |
| 112 | assert.equal(snapshot.activeForegroundLeaseCount, 0); | 112 | assert.equal(snapshot.activeForegroundLeaseCount, 0); |
| 113 | assert.deepStrictEqual(snapshot.activeScopeKeys, []); | 113 | assert.deepStrictEqual(snapshot.activeScopeKeys, []); |
| 114 | assert.equal(snapshot.shouldRunOpportunisticWork, false); | 114 | assert.equal(snapshot.shouldRunOpportunisticWork, false); |
| 115 | }).pipe(Effect.provide(makeLayer(nominalHostPower))), | 115 | }).pipe(Effect.provide(layerFor(nominalHostPower))), |
| 116 | ); | 116 | ); |
| 117 | 117 | ||
| 118 | it.effect("keeps leases from another session when rpc client ids are reused", () => | 118 | it.effect("keeps leases from another session when rpc client ids are reused", () => |
| ... | @@ -136,7 +136,7 @@ describe("BackgroundPolicy", () => { | ... | @@ -136,7 +136,7 @@ describe("BackgroundPolicy", () => { |
| 136 | assert.equal(snapshot.activeForegroundLeaseCount, 1); | 136 | assert.equal(snapshot.activeForegroundLeaseCount, 1); |
| 137 | assert.equal(snapshot.leases[0]?.sessionId, AuthSessionId.make("session-2")); | 137 | assert.equal(snapshot.leases[0]?.sessionId, AuthSessionId.make("session-2")); |
| 138 | assert.equal(snapshot.leases[0]?.clientId, "client-2"); | 138 | assert.equal(snapshot.leases[0]?.clientId, "client-2"); |
| 139 | }).pipe(Effect.provide(makeLayer(nominalHostPower))), | 139 | }).pipe(Effect.provide(layerFor(nominalHostPower))), |
| 140 | ); | 140 | ); |
| 141 | 141 | ||
| 142 | it.effect("serializes lease mutation publications", () => | 142 | it.effect("serializes lease mutation publications", () => |
| ... | @@ -144,7 +144,7 @@ describe("BackgroundPolicy", () => { | ... | @@ -144,7 +144,7 @@ describe("BackgroundPolicy", () => { |
| 144 | const firstSnapshotStarted = yield* Deferred.make<void>(); | 144 | const firstSnapshotStarted = yield* Deferred.make<void>(); |
| 145 | const releaseFirstSnapshot = yield* Deferred.make<void>(); | 145 | const releaseFirstSnapshot = yield* Deferred.make<void>(); |
| 146 | const snapshotReads = yield* Ref.make(0); | 146 | const snapshotReads = yield* Ref.make(0); |
| 147 | const hostLayer = Layer.succeed( | 147 | const layerHost = Layer.succeed( |
| 148 | HostPowerMonitor.HostPowerMonitor, | 148 | HostPowerMonitor.HostPowerMonitor, |
| 149 | HostPowerMonitor.HostPowerMonitor.of({ | 149 | HostPowerMonitor.HostPowerMonitor.of({ |
| 150 | snapshot: Ref.updateAndGet(snapshotReads, (count) => count + 1).pipe( | 150 | snapshot: Ref.updateAndGet(snapshotReads, (count) => count + 1).pipe( |
| ... | @@ -162,7 +162,7 @@ describe("BackgroundPolicy", () => { | ... | @@ -162,7 +162,7 @@ describe("BackgroundPolicy", () => { |
| 162 | }), | 162 | }), |
| 163 | ); | 163 | ); |
| 164 | const layer = BackgroundPolicy.layer.pipe( | 164 | const layer = BackgroundPolicy.layer.pipe( |
| 165 | Layer.provide(Layer.merge(hostLayer, ServerSettings.layerTest())), | 165 | Layer.provide(Layer.merge(layerHost, ServerSettings.layerTest())), |
| 166 | ); | 166 | ); |
| 167 | 167 | ||
| 168 | yield* Effect.gen(function* () { | 168 | yield* Effect.gen(function* () { |
| ... | @@ -221,7 +221,7 @@ describe("BackgroundPolicy", () => { | ... | @@ -221,7 +221,7 @@ describe("BackgroundPolicy", () => { |
| 221 | `client-${BackgroundPolicy.MAX_CLIENT_ACTIVITY_LEASES_PER_RPC_CLIENT}`, | 221 | `client-${BackgroundPolicy.MAX_CLIENT_ACTIVITY_LEASES_PER_RPC_CLIENT}`, |
| 222 | ), | 222 | ), |
| 223 | ); | 223 | ); |
| 224 | }).pipe(Effect.provide(makeLayer(nominalHostPower))), | 224 | }).pipe(Effect.provide(layerFor(nominalHostPower))), |
| 225 | ); | 225 | ); |
| 226 | 226 | ||
| 227 | it.effect("host low power mode disables opportunistic work without dropping scoped demand", () => | 227 | it.effect("host low power mode disables opportunistic work without dropping scoped demand", () => |
| ... | @@ -239,7 +239,7 @@ describe("BackgroundPolicy", () => { | ... | @@ -239,7 +239,7 @@ describe("BackgroundPolicy", () => { |
| 239 | assert.equal(snapshot.shouldRunOpportunisticWork, false); | 239 | assert.equal(snapshot.shouldRunOpportunisticWork, false); |
| 240 | assert.equal(yield* policy.hasDemand({ type: "vcs-status", cwd: "/repo" }), true); | 240 | assert.equal(yield* policy.hasDemand({ type: "vcs-status", cwd: "/repo" }), true); |
| 241 | assert.equal(yield* policy.shouldRunScopeWork({ type: "vcs-status", cwd: "/repo" }), false); | 241 | assert.equal(yield* policy.shouldRunScopeWork({ type: "vcs-status", cwd: "/repo" }), false); |
| 242 | }).pipe(Effect.provide(makeLayer(constrainedHostPower))), | 242 | }).pipe(Effect.provide(layerFor(constrainedHostPower))), |
| 243 | ); | 243 | ); |
| 244 | 244 | ||
| 245 | it.effect("host suspension disables scoped and opportunistic work", () => | 245 | it.effect("host suspension disables scoped and opportunistic work", () => |
| ... | @@ -258,7 +258,7 @@ describe("BackgroundPolicy", () => { | ... | @@ -258,7 +258,7 @@ describe("BackgroundPolicy", () => { |
| 258 | assert.equal(yield* policy.shouldRunScopeWork({ type: "vcs-status", cwd: "/repo" }), false); | 258 | assert.equal(yield* policy.shouldRunScopeWork({ type: "vcs-status", cwd: "/repo" }), false); |
| 259 | }).pipe( | 259 | }).pipe( |
| 260 | Effect.provide( | 260 | Effect.provide( |
| 261 | makeLayer({ | 261 | layerFor({ |
| 262 | ...nominalHostPower, | 262 | ...nominalHostPower, |
| 263 | suspended: true, | 263 | suspended: true, |
| 264 | stale: false, | 264 | stale: false, |
| ... | @@ -281,7 +281,7 @@ describe("BackgroundPolicy", () => { | ... | @@ -281,7 +281,7 @@ describe("BackgroundPolicy", () => { |
| 281 | assert.deepStrictEqual(snapshot.activeScopeKeys, ["vcs-status:/repo"]); | 281 | assert.deepStrictEqual(snapshot.activeScopeKeys, ["vcs-status:/repo"]); |
| 282 | assert.equal(yield* policy.hasDemand({ type: "vcs-status", cwd: "/repo" }), true); | 282 | assert.equal(yield* policy.hasDemand({ type: "vcs-status", cwd: "/repo" }), true); |
| 283 | assert.equal(yield* policy.shouldRunScopeWork({ type: "vcs-status", cwd: "/repo" }), false); | 283 | assert.equal(yield* policy.shouldRunScopeWork({ type: "vcs-status", cwd: "/repo" }), false); |
| 284 | }).pipe(Effect.provide(makeLayer(nominalHostPower))), | 284 | }).pipe(Effect.provide(layerFor(nominalHostPower))), |
| 285 | ); | 285 | ); |
| 286 | 286 | ||
| 287 | it.effect("keeps scoped work active after a recently used visible window loses focus", () => | 287 | it.effect("keeps scoped work active after a recently used visible window loses focus", () => |
| ... | @@ -297,7 +297,7 @@ describe("BackgroundPolicy", () => { | ... | @@ -297,7 +297,7 @@ describe("BackgroundPolicy", () => { |
| 297 | assert.equal(snapshot.activeForegroundLeaseCount, 1); | 297 | assert.equal(snapshot.activeForegroundLeaseCount, 1); |
| 298 | assert.equal(snapshot.shouldRunOpportunisticWork, true); | 298 | assert.equal(snapshot.shouldRunOpportunisticWork, true); |
| 299 | assert.equal(yield* policy.shouldRunScopeWork({ type: "vcs-status", cwd: "/repo" }), true); | 299 | assert.equal(yield* policy.shouldRunScopeWork({ type: "vcs-status", cwd: "/repo" }), true); |
| 300 | }).pipe(Effect.provide(makeLayer(nominalHostPower))), | 300 | }).pipe(Effect.provide(layerFor(nominalHostPower))), |
| 301 | ); | 301 | ); |
| 302 | 302 | ||
| 303 | it.effect("pauses scoped work for a visible unfocused client without recent interaction", () => | 303 | it.effect("pauses scoped work for a visible unfocused client without recent interaction", () => |
| ... | @@ -313,7 +313,7 @@ describe("BackgroundPolicy", () => { | ... | @@ -313,7 +313,7 @@ describe("BackgroundPolicy", () => { |
| 313 | assert.equal(snapshot.activeForegroundLeaseCount, 0); | 313 | assert.equal(snapshot.activeForegroundLeaseCount, 0); |
| 314 | assert.equal(snapshot.shouldRunOpportunisticWork, false); | 314 | assert.equal(snapshot.shouldRunOpportunisticWork, false); |
| 315 | assert.equal(yield* policy.shouldRunScopeWork({ type: "vcs-status", cwd: "/repo" }), false); | 315 | assert.equal(yield* policy.shouldRunScopeWork({ type: "vcs-status", cwd: "/repo" }), false); |
| 316 | }).pipe(Effect.provide(makeLayer(nominalHostPower))), | 316 | }).pipe(Effect.provide(layerFor(nominalHostPower))), |
| 317 | ); | 317 | ); |
| 318 | 318 | ||
| 319 | it.effect( | 319 | it.effect( |
| ... | @@ -329,7 +329,7 @@ describe("BackgroundPolicy", () => { | ... | @@ -329,7 +329,7 @@ describe("BackgroundPolicy", () => { |
| 329 | 329 | ||
| 330 | assert.equal(yield* policy.shouldRunScopeWork({ type: "vcs-status", cwd: "/repo" }), true); | 330 | assert.equal(yield* policy.shouldRunScopeWork({ type: "vcs-status", cwd: "/repo" }), true); |
| 331 | }).pipe( | 331 | }).pipe( |
| 332 | Effect.provide(makeLayer(nominalHostPower, { backgroundActivityProfile: "performance" })), | 332 | Effect.provide(layerFor(nominalHostPower, { backgroundActivityProfile: "performance" })), |
| 333 | ), | 333 | ), |
| 334 | ); | 334 | ); |
| 335 | 335 | ||
| ... | @@ -345,7 +345,7 @@ describe("BackgroundPolicy", () => { | ... | @@ -345,7 +345,7 @@ describe("BackgroundPolicy", () => { |
| 345 | assert.equal(yield* policy.shouldRunScopeWork({ type: "vcs-status", cwd: "/repo" }), false); | 345 | assert.equal(yield* policy.shouldRunScopeWork({ type: "vcs-status", cwd: "/repo" }), false); |
| 346 | }).pipe( | 346 | }).pipe( |
| 347 | Effect.provide( | 347 | Effect.provide( |
| 348 | makeLayer( | 348 | layerFor( |
| 349 | { | 349 | { |
| 350 | ...nominalHostPower, | 350 | ...nominalHostPower, |
| 351 | onBattery: "true", | 351 | onBattery: "true", |
| ... | @@ -369,7 +369,7 @@ describe("BackgroundPolicy", () => { | ... | @@ -369,7 +369,7 @@ describe("BackgroundPolicy", () => { |
| 369 | assert.equal(yield* policy.shouldRunScopeWork({ type: "vcs-status", cwd: "/repo" }), true); | 369 | assert.equal(yield* policy.shouldRunScopeWork({ type: "vcs-status", cwd: "/repo" }), true); |
| 370 | }).pipe( | 370 | }).pipe( |
| 371 | Effect.provide( | 371 | Effect.provide( |
| 372 | makeLayer( | 372 | layerFor( |
| 373 | { | 373 | { |
| 374 | ...nominalHostPower, | 374 | ...nominalHostPower, |
| 375 | locked: "true", | 375 | locked: "true", |
apps/server/src/background/HostPowerMonitor.test.ts+4-4| ... | @@ -84,11 +84,11 @@ describe("HostPowerMonitor", () => { | ... | @@ -84,11 +84,11 @@ describe("HostPowerMonitor", () => { |
| 84 | const sampledAt = DateTime.makeUnsafe("2026-06-17T12:00:00.000Z"); | 84 | const sampledAt = DateTime.makeUnsafe("2026-06-17T12:00:00.000Z"); |
| 85 | const desktopChanges = yield* PubSub.sliding<DesktopHostTelemetrySnapshot>(1); | 85 | const desktopChanges = yield* PubSub.sliding<DesktopHostTelemetrySnapshot>(1); |
| 86 | const diagnosticsDemandWrites = yield* Ref.make(0); | 86 | const diagnosticsDemandWrites = yield* Ref.make(0); |
| 87 | const receiverLayer = DesktopTelemetryReceiver.layerTest({ | 87 | const layerReceiver = DesktopTelemetryReceiver.layerTest({ |
| 88 | changes: Stream.fromPubSub(desktopChanges), | 88 | changes: Stream.fromPubSub(desktopChanges), |
| 89 | setDiagnosticsDemand: () => Ref.update(diagnosticsDemandWrites, (count) => count + 1), | 89 | setDiagnosticsDemand: () => Ref.update(diagnosticsDemandWrites, (count) => count + 1), |
| 90 | }); | 90 | }); |
| 91 | const layer = HostPowerMonitor.layer.pipe(Layer.provide(receiverLayer)); | 91 | const layer = HostPowerMonitor.layer.pipe(Layer.provide(layerReceiver)); |
| 92 | 92 | ||
| 93 | yield* Effect.gen(function* () { | 93 | yield* Effect.gen(function* () { |
| 94 | const monitor = yield* HostPowerMonitor.HostPowerMonitor; | 94 | const monitor = yield* HostPowerMonitor.HostPowerMonitor; |
| ... | @@ -158,7 +158,7 @@ describe("HostPowerMonitor", () => { | ... | @@ -158,7 +158,7 @@ describe("HostPowerMonitor", () => { |
| 158 | }, | 158 | }, |
| 159 | }; | 159 | }; |
| 160 | const desktopChanges = yield* PubSub.sliding<DesktopHostTelemetrySnapshot>(1); | 160 | const desktopChanges = yield* PubSub.sliding<DesktopHostTelemetrySnapshot>(1); |
| 161 | const receiverLayer = DesktopTelemetryReceiver.layerTest({ | 161 | const layerReceiver = DesktopTelemetryReceiver.layerTest({ |
| 162 | latest: Effect.succeedSome(initial), | 162 | latest: Effect.succeedSome(initial), |
| 163 | changes: Stream.empty, | 163 | changes: Stream.empty, |
| 164 | subscribe: Effect.gen(function* () { | 164 | subscribe: Effect.gen(function* () { |
| ... | @@ -170,7 +170,7 @@ describe("HostPowerMonitor", () => { | ... | @@ -170,7 +170,7 @@ describe("HostPowerMonitor", () => { |
| 170 | }; | 170 | }; |
| 171 | }), | 171 | }), |
| 172 | }); | 172 | }); |
| 173 | const layer = HostPowerMonitor.layer.pipe(Layer.provide(receiverLayer)); | 173 | const layer = HostPowerMonitor.layer.pipe(Layer.provide(layerReceiver)); |
| 174 | 174 | ||
| 175 | yield* Effect.gen(function* () { | 175 | yield* Effect.gen(function* () { |
| 176 | const monitor = yield* HostPowerMonitor.HostPowerMonitor; | 176 | const monitor = yield* HostPowerMonitor.HostPowerMonitor; |
apps/server/src/binCli.ts+2-2| ... | @@ -27,7 +27,7 @@ import { themeCommand } from "./cli/theme.ts"; | ... | @@ -27,7 +27,7 @@ import { themeCommand } from "./cli/theme.ts"; |
| 27 | import { traceCommand } from "./cli/trace.ts"; | 27 | import { traceCommand } from "./cli/trace.ts"; |
| 28 | import { triageCommand } from "./cli/triage.ts"; | 28 | import { triageCommand } from "./cli/triage.ts"; |
| 29 | 29 | ||
| 30 | const CliRuntimeLayer = Layer.mergeAll(NodeServices.layer, NetService.layer); | 30 | const layerCliRuntime = Layer.mergeAll(NodeServices.layer, NetService.layer); |
| 31 | 31 | ||
| 32 | const connectPublicConfigMissingMessage = | 32 | const connectPublicConfigMissingMessage = |
| 33 | "T3 Connect commands are unavailable: this build is missing T3 Connect public configuration."; | 33 | "T3 Connect commands are unavailable: this build is missing T3 Connect public configuration."; |
| ... | @@ -92,7 +92,7 @@ export const cli = makeCli(); | ... | @@ -92,7 +92,7 @@ export const cli = makeCli(); |
| 92 | export function runCli() { | 92 | export function runCli() { |
| 93 | Command.run(cli, { version: packageJson.version }).pipe( | 93 | Command.run(cli, { version: packageJson.version }).pipe( |
| 94 | Effect.scoped, | 94 | Effect.scoped, |
| 95 | Effect.provide(CliRuntimeLayer), | 95 | Effect.provide(layerCliRuntime), |
| 96 | NodeRuntime.runMain, | 96 | NodeRuntime.runMain, |
| 97 | ); | 97 | ); |
| 98 | } | 98 | } |
apps/server/src/checkpointing/CheckpointDiffQuery.test.ts+6-6| ... | @@ -44,7 +44,7 @@ function makeProjection(): ProjectionCheckpointContext { | ... | @@ -44,7 +44,7 @@ function makeProjection(): ProjectionCheckpointContext { |
| 44 | }; | 44 | }; |
| 45 | } | 45 | } |
| 46 | 46 | ||
| 47 | function makeLayer(input: { | 47 | function layerFor(input: { |
| 48 | readonly projection: Effect.Effect<ProjectionCheckpointContext, OrchestratorProjectionError>; | 48 | readonly projection: Effect.Effect<ProjectionCheckpointContext, OrchestratorProjectionError>; |
| 49 | readonly diffCheckpoints?: CheckpointStore.CheckpointStore["Service"]["diffCheckpoints"]; | 49 | readonly diffCheckpoints?: CheckpointStore.CheckpointStore["Service"]["diffCheckpoints"]; |
| 50 | }) { | 50 | }) { |
| ... | @@ -66,7 +66,7 @@ it.effect("computes V2 run diffs from projected checkpoint scopes", () => { | ... | @@ -66,7 +66,7 @@ it.effect("computes V2 run diffs from projected checkpoint scopes", () => { |
| 66 | const diffCheckpoints = vi.fn((_input: CheckpointStore.DiffCheckpointsInput) => | 66 | const diffCheckpoints = vi.fn((_input: CheckpointStore.DiffCheckpointsInput) => |
| 67 | Effect.succeed("diff --git a/file b/file"), | 67 | Effect.succeed("diff --git a/file b/file"), |
| 68 | ); | 68 | ); |
| 69 | const layer = makeLayer({ projection: Effect.succeed(makeProjection()), diffCheckpoints }); | 69 | const layer = layerFor({ projection: Effect.succeed(makeProjection()), diffCheckpoints }); |
| 70 | 70 | ||
| 71 | return Effect.gen(function* () { | 71 | return Effect.gen(function* () { |
| 72 | const query = yield* CheckpointDiffQuery.CheckpointDiffQuery; | 72 | const query = yield* CheckpointDiffQuery.CheckpointDiffQuery; |
| ... | @@ -92,7 +92,7 @@ it.effect("computes V2 run diffs from projected checkpoint scopes", () => { | ... | @@ -92,7 +92,7 @@ it.effect("computes V2 run diffs from projected checkpoint scopes", () => { |
| 92 | }); | 92 | }); |
| 93 | 93 | ||
| 94 | it.effect("preserves the typed missing-thread error contract", () => { | 94 | it.effect("preserves the typed missing-thread error contract", () => { |
| 95 | const layer = makeLayer({ | 95 | const layer = layerFor({ |
| 96 | projection: Effect.fail(new OrchestratorProjectionError({ threadId })), | 96 | projection: Effect.fail(new OrchestratorProjectionError({ threadId })), |
| 97 | }); | 97 | }); |
| 98 | 98 | ||
| ... | @@ -111,7 +111,7 @@ it.effect("preserves the typed missing-thread error contract", () => { | ... | @@ -111,7 +111,7 @@ it.effect("preserves the typed missing-thread error contract", () => { |
| 111 | }); | 111 | }); |
| 112 | 112 | ||
| 113 | it.effect("preserves the typed unavailable-range error contract", () => { | 113 | it.effect("preserves the typed unavailable-range error contract", () => { |
| 114 | const layer = makeLayer({ projection: Effect.succeed(makeProjection()) }); | 114 | const layer = layerFor({ projection: Effect.succeed(makeProjection()) }); |
| 115 | 115 | ||
| 116 | return Effect.gen(function* () { | 116 | return Effect.gen(function* () { |
| 117 | const query = yield* CheckpointDiffQuery.CheckpointDiffQuery; | 117 | const query = yield* CheckpointDiffQuery.CheckpointDiffQuery; |
| ... | @@ -132,7 +132,7 @@ it.effect("preserves the typed unavailable-range error contract", () => { | ... | @@ -132,7 +132,7 @@ it.effect("preserves the typed unavailable-range error contract", () => { |
| 132 | 132 | ||
| 133 | it.effect("excludes ready checkpoints from rolled-back runs", () => { | 133 | it.effect("excludes ready checkpoints from rolled-back runs", () => { |
| 134 | const projection = makeProjection(); | 134 | const projection = makeProjection(); |
| 135 | const layer = makeLayer({ | 135 | const layer = layerFor({ |
| 136 | projection: Effect.succeed({ | 136 | projection: Effect.succeed({ |
| 137 | ...projection, | 137 | ...projection, |
| 138 | runs: projection.runs.map((run) => | 138 | runs: projection.runs.map((run) => |
| ... | @@ -170,7 +170,7 @@ it.effect("excludes ready checkpoints from rolled-back runs", () => { | ... | @@ -170,7 +170,7 @@ it.effect("excludes ready checkpoints from rolled-back runs", () => { |
| 170 | 170 | ||
| 171 | it.effect("preserves the typed missing-baseline-ref error contract", () => { | 171 | it.effect("preserves the typed missing-baseline-ref error contract", () => { |
| 172 | const projection = makeProjection(); | 172 | const projection = makeProjection(); |
| 173 | const layer = makeLayer({ | 173 | const layer = layerFor({ |
| 174 | projection: Effect.succeed({ | 174 | projection: Effect.succeed({ |
| 175 | ...projection, | 175 | ...projection, |
| 176 | checkpointScopes: projection.checkpointScopes.map((scope) => ({ | 176 | checkpointScopes: projection.checkpointScopes.map((scope) => ({ |
apps/server/src/checkpointing/CheckpointStore.test.ts+10-10| ... | @@ -19,19 +19,19 @@ import * as VcsDriverRegistry from "../vcs/VcsDriverRegistry.ts"; | ... | @@ -19,19 +19,19 @@ import * as VcsDriverRegistry from "../vcs/VcsDriverRegistry.ts"; |
| 19 | import * as VcsProcess from "../vcs/VcsProcess.ts"; | 19 | import * as VcsProcess from "../vcs/VcsProcess.ts"; |
| 20 | import * as ServerConfig from "../config.ts"; | 20 | import * as ServerConfig from "../config.ts"; |
| 21 | 21 | ||
| 22 | const ServerConfigLayer = ServerConfig.ServerConfig.layerTest(process.cwd(), { | 22 | const layerServerConfig = ServerConfig.ServerConfig.layerTest(process.cwd(), { |
| 23 | prefix: "t3-checkpoint-store-test-", | 23 | prefix: "t3-checkpoint-store-test-", |
| 24 | }); | 24 | }); |
| 25 | const VcsProcessTestLayer = VcsProcess.layer.pipe(Layer.provide(NodeServices.layer)); | 25 | const layerVcsProcessTest = VcsProcess.layer.pipe(Layer.provide(NodeServices.layer)); |
| 26 | const VcsDriverTestLayer = VcsDriverRegistry.layer.pipe(Layer.provide(VcsProcessTestLayer)); | 26 | const layerVcsDriverTest = VcsDriverRegistry.layer.pipe(Layer.provide(layerVcsProcessTest)); |
| 27 | const CheckpointStoreTestLayer = CheckpointStore.layer.pipe( | 27 | const layerCheckpointStoreTest = CheckpointStore.layer.pipe( |
| 28 | Layer.provideMerge(VcsDriverTestLayer), | 28 | Layer.provideMerge(layerVcsDriverTest), |
| 29 | Layer.provideMerge(NodeServices.layer), | 29 | Layer.provideMerge(NodeServices.layer), |
| 30 | ); | 30 | ); |
| 31 | const TestLayer = CheckpointStoreTestLayer.pipe( | 31 | const layerTest = layerCheckpointStoreTest.pipe( |
| 32 | Layer.provideMerge(VcsProcessTestLayer), | 32 | Layer.provideMerge(layerVcsProcessTest), |
| 33 | Layer.provideMerge(VcsDriverTestLayer), | 33 | Layer.provideMerge(layerVcsDriverTest), |
| 34 | Layer.provideMerge(ServerConfigLayer), | 34 | Layer.provideMerge(layerServerConfig), |
| 35 | Layer.provideMerge(NodeServices.layer), | 35 | Layer.provideMerge(NodeServices.layer), |
| 36 | ); | 36 | ); |
| 37 | 37 | ||
| ... | @@ -94,7 +94,7 @@ function buildLargeText(lineCount = 5_000): string { | ... | @@ -94,7 +94,7 @@ function buildLargeText(lineCount = 5_000): string { |
| 94 | .concat("\n"); | 94 | .concat("\n"); |
| 95 | } | 95 | } |
| 96 | 96 | ||
| 97 | it.layer(TestLayer)("CheckpointStore.layer", (it) => { | 97 | it.layer(layerTest)("CheckpointStore.layer", (it) => { |
| 98 | describe("isGitRepository", () => { | 98 | describe("isGitRepository", () => { |
| 99 | it.effect("returns false when no Git repository is detected", () => | 99 | it.effect("returns false when no Git repository is detected", () => |
| 100 | Effect.gen(function* () { | 100 | Effect.gen(function* () { |
apps/server/src/cli/auth.ts+1-1| ... | @@ -42,7 +42,7 @@ const runWithEnvironmentAuth = <A, E>( | ... | @@ -42,7 +42,7 @@ const runWithEnvironmentAuth = <A, E>( |
| 42 | return yield* run(environmentAuth); | 42 | return yield* run(environmentAuth); |
| 43 | }).pipe( | 43 | }).pipe( |
| 44 | Effect.provide( | 44 | Effect.provide( |
| 45 | Layer.mergeAll(EnvironmentAuth.runtimeLayer).pipe( | 45 | Layer.mergeAll(EnvironmentAuth.layerRuntime).pipe( |
| 46 | Layer.provide(ServerConfig.layer(config)), | 46 | Layer.provide(ServerConfig.layer(config)), |
| 47 | Layer.provide(Layer.succeed(References.MinimumLogLevel, minimumLogLevel)), | 47 | Layer.provide(Layer.succeed(References.MinimumLogLevel, minimumLogLevel)), |
| 48 | ), | 48 | ), |
apps/server/src/cli/config.test.ts+7-7| ... | @@ -146,7 +146,7 @@ it.layer(NodeServices.layer)("cli config resolution", (it) => { | ... | @@ -146,7 +146,7 @@ it.layer(NodeServices.layer)("cli config resolution", (it) => { |
| 146 | tailscaleServeEnabled: Option.none<boolean>(), | 146 | tailscaleServeEnabled: Option.none<boolean>(), |
| 147 | tailscaleServePort: Option.none<number>(), | 147 | tailscaleServePort: Option.none<number>(), |
| 148 | }; | 148 | }; |
| 149 | const configLayer = ConfigProvider.layer( | 149 | const layerConfig = ConfigProvider.layer( |
| 150 | ConfigProvider.fromEnv({ | 150 | ConfigProvider.fromEnv({ |
| 151 | env: { | 151 | env: { |
| 152 | T3CODE_DEV_AUTH_TOKEN: " reusable-dev-auth-token-that-is-long-enough ", | 152 | T3CODE_DEV_AUTH_TOKEN: " reusable-dev-auth-token-that-is-long-enough ", |
| ... | @@ -154,12 +154,12 @@ it.layer(NodeServices.layer)("cli config resolution", (it) => { | ... | @@ -154,12 +154,12 @@ it.layer(NodeServices.layer)("cli config resolution", (it) => { |
| 154 | }), | 154 | }), |
| 155 | ); | 155 | ); |
| 156 | const web = yield* resolveServerConfig(flags, Option.none()).pipe( | 156 | const web = yield* resolveServerConfig(flags, Option.none()).pipe( |
| 157 | Effect.provide(Layer.mergeAll(configLayer, NetService.layer)), | 157 | Effect.provide(Layer.mergeAll(layerConfig, NetService.layer)), |
| 158 | ); | 158 | ); |
| 159 | const desktop = yield* resolveServerConfig( | 159 | const desktop = yield* resolveServerConfig( |
| 160 | { ...flags, mode: Option.some("desktop" as const) }, | 160 | { ...flags, mode: Option.some("desktop" as const) }, |
| 161 | Option.none(), | 161 | Option.none(), |
| 162 | ).pipe(Effect.provide(Layer.mergeAll(configLayer, NetService.layer))); | 162 | ).pipe(Effect.provide(Layer.mergeAll(layerConfig, NetService.layer))); |
| 163 | 163 | ||
| 164 | expect(web.devAuthToken).toBeDefined(); | 164 | expect(web.devAuthToken).toBeDefined(); |
| 165 | if (web.devAuthToken === undefined) { | 165 | if (web.devAuthToken === undefined) { |
| ... | @@ -190,21 +190,21 @@ it.layer(NodeServices.layer)("cli config resolution", (it) => { | ... | @@ -190,21 +190,21 @@ it.layer(NodeServices.layer)("cli config resolution", (it) => { |
| 190 | tailscaleServeEnabled: Option.none<boolean>(), | 190 | tailscaleServeEnabled: Option.none<boolean>(), |
| 191 | tailscaleServePort: Option.none<number>(), | 191 | tailscaleServePort: Option.none<number>(), |
| 192 | }; | 192 | }; |
| 193 | const configLayer = ConfigProvider.layer( | 193 | const layerConfig = ConfigProvider.layer( |
| 194 | ConfigProvider.fromEnv({ env: { T3CODE_DEV_AUTH_TOKEN: secret } }), | 194 | ConfigProvider.fromEnv({ env: { T3CODE_DEV_AUTH_TOKEN: secret } }), |
| 195 | ); | 195 | ); |
| 196 | const error = yield* resolveServerConfig(flags, Option.none()).pipe( | 196 | const error = yield* resolveServerConfig(flags, Option.none()).pipe( |
| 197 | Effect.provide(Layer.mergeAll(configLayer, NetService.layer)), | 197 | Effect.provide(Layer.mergeAll(layerConfig, NetService.layer)), |
| 198 | Effect.flip, | 198 | Effect.flip, |
| 199 | ); | 199 | ); |
| 200 | const desktop = yield* resolveServerConfig( | 200 | const desktop = yield* resolveServerConfig( |
| 201 | { ...flags, mode: Option.some("desktop" as const) }, | 201 | { ...flags, mode: Option.some("desktop" as const) }, |
| 202 | Option.none(), | 202 | Option.none(), |
| 203 | ).pipe(Effect.provide(Layer.mergeAll(configLayer, NetService.layer))); | 203 | ).pipe(Effect.provide(Layer.mergeAll(layerConfig, NetService.layer))); |
| 204 | const staticWeb = yield* resolveServerConfig( | 204 | const staticWeb = yield* resolveServerConfig( |
| 205 | { ...flags, devUrl: Option.none() }, | 205 | { ...flags, devUrl: Option.none() }, |
| 206 | Option.none(), | 206 | Option.none(), |
| 207 | ).pipe(Effect.provide(Layer.mergeAll(configLayer, NetService.layer))); | 207 | ).pipe(Effect.provide(Layer.mergeAll(layerConfig, NetService.layer))); |
| 208 | 208 | ||
| 209 | expect(String(error)).not.toContain(secret); | 209 | expect(String(error)).not.toContain(secret); |
| 210 | const serialized = yield* encodeUnknownJson(error); | 210 | const serialized = yield* encodeUnknownJson(error); |
apps/server/src/cli/connect.ts+8-11| ... | @@ -37,18 +37,15 @@ import { | ... | @@ -37,18 +37,15 @@ import { |
| 37 | RELAY_URL_SECRET, | 37 | RELAY_URL_SECRET, |
| 38 | } from "../cloud/config.ts"; | 38 | } from "../cloud/config.ts"; |
| 39 | import { relayUrlConfig } from "../cloud/publicConfig.ts"; | 39 | import { relayUrlConfig } from "../cloud/publicConfig.ts"; |
| 40 | import { headlessRelayClientTracingLayer } from "../cloud/relayTracing.ts"; | 40 | import * as RelayTracing from "../cloud/relayTracing.ts"; |
| 41 | import * as ServerConfig from "../config.ts"; | 41 | import * as ServerConfig from "../config.ts"; |
| 42 | import * as ServerEnvironment from "../environment/ServerEnvironment.ts"; | 42 | import * as ServerEnvironment from "../environment/ServerEnvironment.ts"; |
| 43 | import * as ExternalLauncher from "../process/externalLauncher.ts"; | 43 | import * as ExternalLauncher from "../process/externalLauncher.ts"; |
| 44 | import { readPersistedServerRuntimeState } from "../serverRuntimeState.ts"; | 44 | import { readPersistedServerRuntimeState } from "../serverRuntimeState.ts"; |
| 45 | import { projectLocationFlags, resolveCliAuthConfig } from "./config.ts"; | 45 | import { projectLocationFlags, resolveCliAuthConfig } from "./config.ts"; |
| 46 | import { resolveCliCommand } from "./invocation.ts"; | 46 | import { resolveCliCommand } from "./invocation.ts"; |
| 47 | import { | 47 | import { offerServiceDuringOnboarding, recoverServiceOnboardingOffer } from "./service.ts"; |
| 48 | bootServiceLayer, | 48 | import * as CliService from "./service.ts"; |
| 49 | offerServiceDuringOnboarding, | ||
| 50 | recoverServiceOnboardingOffer, | ||
| 51 | } from "./service.ts"; | ||
| 52 | 49 | ||
| 53 | const jsonFlag = Flag.Boolean("json").pipe( | 50 | const jsonFlag = Flag.Boolean("json").pipe( |
| 54 | Flag.withDescription("Emit JSON instead of human-readable output."), | 51 | Flag.withDescription("Emit JSON instead of human-readable output."), |
| ... | @@ -434,22 +431,22 @@ const runCloudCommand = Effect.fn("cloud.cli.run_cloud_command")(function* <A, E | ... | @@ -434,22 +431,22 @@ const runCloudCommand = Effect.fn("cloud.cli.run_cloud_command")(function* <A, E |
| 434 | const logLevel = yield* GlobalFlag.LogLevel; | 431 | const logLevel = yield* GlobalFlag.LogLevel; |
| 435 | const config = yield* resolveCliAuthConfig(flags, logLevel); | 432 | const config = yield* resolveCliAuthConfig(flags, logLevel); |
| 436 | const minimumLogLevel = options?.quietLogs ? "Error" : config.logLevel; | 433 | const minimumLogLevel = options?.quietLogs ? "Error" : config.logLevel; |
| 437 | const runtimeLayer = Layer.mergeAll( | 434 | const layerRuntime = Layer.mergeAll( |
| 438 | ServerSecretStore.layer, | 435 | ServerSecretStore.layer, |
| 439 | CliTokenManager.layer.pipe( | 436 | CliTokenManager.layer.pipe( |
| 440 | Layer.provide(ServerSecretStore.layer), | 437 | Layer.provide(ServerSecretStore.layer), |
| 441 | Layer.provide(ExternalLauncher.layer), | 438 | Layer.provide(ExternalLauncher.layer), |
| 442 | ), | 439 | ), |
| 443 | RelayClient.layerCloudflared({ baseDir: config.baseDir }), | 440 | RelayClient.layerCloudflared({ baseDir: config.baseDir }), |
| 444 | EnvironmentAuth.runtimeLayer, | 441 | EnvironmentAuth.layerRuntime, |
| 445 | bootServiceLayer(config), | 442 | CliService.layer(config), |
| 446 | headlessRelayClientTracingLayer, | 443 | RelayTracing.layerHeadlessRelayClient, |
| 447 | ).pipe( | 444 | ).pipe( |
| 448 | Layer.provideMerge(FetchHttpClient.layer), | 445 | Layer.provideMerge(FetchHttpClient.layer), |
| 449 | Layer.provideMerge(ServerConfig.layer(config)), | 446 | Layer.provideMerge(ServerConfig.layer(config)), |
| 450 | Layer.provide(Layer.succeed(References.MinimumLogLevel, minimumLogLevel)), | 447 | Layer.provide(Layer.succeed(References.MinimumLogLevel, minimumLogLevel)), |
| 451 | ); | 448 | ); |
| 452 | return yield* run.pipe(Effect.provide(runtimeLayer)); | 449 | return yield* run.pipe(Effect.provide(layerRuntime)); |
| 453 | }); | 450 | }); |
| 454 | 451 | ||
| 455 | const connectedAs = (identity: string | null): string => (identity ? ` as ${identity}` : ""); | 452 | const connectedAs = (identity: string | null): string => (identity ? ` as ${identity}` : ""); |
apps/server/src/cli/pair.test.ts+2-2| ... | @@ -32,7 +32,7 @@ import { | ... | @@ -32,7 +32,7 @@ import { |
| 32 | 32 | ||
| 33 | import packageJson from "../../package.json" with { type: "json" }; | 33 | import packageJson from "../../package.json" with { type: "json" }; |
| 34 | 34 | ||
| 35 | const CliRuntimeLayer = Layer.mergeAll(NodeServices.layer, NetService.layer); | 35 | const layerCliRuntime = Layer.mergeAll(NodeServices.layer, NetService.layer); |
| 36 | 36 | ||
| 37 | const baseState = { | 37 | const baseState = { |
| 38 | version: 1, | 38 | version: 1, |
| ... | @@ -94,7 +94,7 @@ describe("pair tailscale local target", () => { | ... | @@ -94,7 +94,7 @@ describe("pair tailscale local target", () => { |
| 94 | const runCli = (args: ReadonlyArray<string>) => Command.runWith(cli, { version: "0.0.0" })(args); | 94 | const runCli = (args: ReadonlyArray<string>) => Command.runWith(cli, { version: "0.0.0" })(args); |
| 95 | 95 | ||
| 96 | const provideCliTestLayers = <A, E, R>(effect: Effect.Effect<A, E, R>) => | 96 | const provideCliTestLayers = <A, E, R>(effect: Effect.Effect<A, E, R>) => |
| 97 | Effect.provide(effect, Layer.mergeAll(CliRuntimeLayer, TestConsole.layer)); | 97 | Effect.provide(effect, Layer.mergeAll(layerCliRuntime, TestConsole.layer)); |
| 98 | 98 | ||
| 99 | // Console output accumulates across CLI runs within a test, and each | 99 | // Console output accumulates across CLI runs within a test, and each |
| 100 | // Console.log call is one entry — so the latest command's output is the last | 100 | // Console.log call is one entry — so the latest command's output is the last |
apps/server/src/cli/pair.ts+1-1| ... | @@ -437,7 +437,7 @@ const mintPairingLink = Effect.fn("pair.mintPairingLink")(function* (input: { | ... | @@ -437,7 +437,7 @@ const mintPairingLink = Effect.fn("pair.mintPairingLink")(function* (input: { |
| 437 | }); | 437 | }); |
| 438 | }).pipe( | 438 | }).pipe( |
| 439 | Effect.provide( | 439 | Effect.provide( |
| 440 | EnvironmentAuth.runtimeLayer.pipe( | 440 | EnvironmentAuth.layerRuntime.pipe( |
| 441 | Layer.provide(ServerConfig.layer(input.config)), | 441 | Layer.provide(ServerConfig.layer(input.config)), |
| 442 | Layer.provide(Layer.succeed(References.MinimumLogLevel, input.config.logLevel)), | 442 | Layer.provide(Layer.succeed(References.MinimumLogLevel, input.config.logLevel)), |
| 443 | ), | 443 | ), |
apps/server/src/cli/project.test.ts+5-8| ... | @@ -30,10 +30,7 @@ import * as ServerConfig from "../config.ts"; | ... | @@ -30,10 +30,7 @@ import * as ServerConfig from "../config.ts"; |
| 30 | import * as EventSink from "../orchestration-v2/EventSink.ts"; | 30 | import * as EventSink from "../orchestration-v2/EventSink.ts"; |
| 31 | import * as EventStore from "../orchestration-v2/EventStore.ts"; | 31 | import * as EventStore from "../orchestration-v2/EventStore.ts"; |
| 32 | import * as ProjectionStore from "../orchestration-v2/ProjectionStore.ts"; | 32 | import * as ProjectionStore from "../orchestration-v2/ProjectionStore.ts"; |
| 33 | import { | 33 | import * as RuntimeLayer from "../orchestration-v2/runtimeLayer.ts"; |
| 34 | OrchestrationV2EventSinkLayerLive, | ||
| 35 | ProjectServiceLayerLive, | ||
| 36 | } from "../orchestration-v2/runtimeLayer.ts"; | ||
| 37 | import * as SqlitePersistence from "../persistence/Layers/Sqlite.ts"; | 34 | import * as SqlitePersistence from "../persistence/Layers/Sqlite.ts"; |
| 38 | import * as ProjectEnrichmentService from "../project/ProjectEnrichmentService.ts"; | 35 | import * as ProjectEnrichmentService from "../project/ProjectEnrichmentService.ts"; |
| 39 | import * as ProjectFaviconResolver from "../project/ProjectFaviconResolver.ts"; | 36 | import * as ProjectFaviconResolver from "../project/ProjectFaviconResolver.ts"; |
| ... | @@ -47,9 +44,9 @@ import { | ... | @@ -47,9 +44,9 @@ import { |
| 47 | projectCommandErrorFromLiveServerRequest, | 44 | projectCommandErrorFromLiveServerRequest, |
| 48 | } from "./project.ts"; | 45 | } from "./project.ts"; |
| 49 | 46 | ||
| 50 | const CliRuntimeLayer = Layer.mergeAll(NodeServices.layer, NetService.layer); | 47 | const layerCliRuntime = Layer.mergeAll(NodeServices.layer, NetService.layer); |
| 51 | const runCli = (args: ReadonlyArray<string>) => | 48 | const runCli = (args: ReadonlyArray<string>) => |
| 52 | Command.runWith(cli, { version: "0.0.0" })(args).pipe(Effect.provide(CliRuntimeLayer)); | 49 | Command.runWith(cli, { version: "0.0.0" })(args).pipe(Effect.provide(layerCliRuntime)); |
| 53 | 50 | ||
| 54 | const makeConfig = (baseDir: string) => | 51 | const makeConfig = (baseDir: string) => |
| 55 | Effect.gen(function* () { | 52 | Effect.gen(function* () { |
| ... | @@ -90,7 +87,7 @@ const makeConfig = (baseDir: string) => | ... | @@ -90,7 +87,7 @@ const makeConfig = (baseDir: string) => |
| 90 | const readProjects = (baseDir: string) => | 87 | const readProjects = (baseDir: string) => |
| 91 | Effect.gen(function* () { | 88 | Effect.gen(function* () { |
| 92 | const config = yield* makeConfig(baseDir); | 89 | const config = yield* makeConfig(baseDir); |
| 93 | const layer = ProjectServiceLayerLive.pipe( | 90 | const layer = RuntimeLayer.layerProjectService.pipe( |
| 94 | Layer.provideMerge(ProjectEnrichmentService.layer), | 91 | Layer.provideMerge(ProjectEnrichmentService.layer), |
| 95 | Layer.provideMerge(RepositoryIdentityResolver.layer), | 92 | Layer.provideMerge(RepositoryIdentityResolver.layer), |
| 96 | Layer.provideMerge(ProjectFaviconResolver.layer), | 93 | Layer.provideMerge(ProjectFaviconResolver.layer), |
| ... | @@ -171,7 +168,7 @@ const makeThreadPersistenceLayer = Effect.fn("ProjectCliTest.makeThreadPersisten | ... | @@ -171,7 +168,7 @@ const makeThreadPersistenceLayer = Effect.fn("ProjectCliTest.makeThreadPersisten |
| 171 | function* (baseDir: string) { | 168 | function* (baseDir: string) { |
| 172 | const config = yield* makeConfig(baseDir); | 169 | const config = yield* makeConfig(baseDir); |
| 173 | return Layer.mergeAll( | 170 | return Layer.mergeAll( |
| 174 | OrchestrationV2EventSinkLayerLive, | 171 | RuntimeLayer.layerEventSink, |
| 175 | ProjectionStore.layer, | 172 | ProjectionStore.layer, |
| 176 | EventStore.layer, | 173 | EventStore.layer, |
| 177 | ).pipe( | 174 | ).pipe( |
apps/server/src/cli/project.ts+5-5| ... | @@ -25,7 +25,7 @@ import * as EnvironmentAuth from "../auth/EnvironmentAuth.ts"; | ... | @@ -25,7 +25,7 @@ import * as EnvironmentAuth from "../auth/EnvironmentAuth.ts"; |
| 25 | 25 | ||
| 26 | import * as ServerConfig from "../config.ts"; | 26 | import * as ServerConfig from "../config.ts"; |
| 27 | import * as SqlitePersistence from "../persistence/Layers/Sqlite.ts"; | 27 | import * as SqlitePersistence from "../persistence/Layers/Sqlite.ts"; |
| 28 | import { ProjectServiceLayerLive } from "../orchestration-v2/runtimeLayer.ts"; | 28 | import * as RuntimeLayer from "../orchestration-v2/runtimeLayer.ts"; |
| 29 | import * as ProjectEnrichmentService from "../project/ProjectEnrichmentService.ts"; | 29 | import * as ProjectEnrichmentService from "../project/ProjectEnrichmentService.ts"; |
| 30 | import * as ProjectFaviconResolver from "../project/ProjectFaviconResolver.ts"; | 30 | import * as ProjectFaviconResolver from "../project/ProjectFaviconResolver.ts"; |
| 31 | import * as RepositoryIdentityResolver from "../project/RepositoryIdentityResolver.ts"; | 31 | import * as RepositoryIdentityResolver from "../project/RepositoryIdentityResolver.ts"; |
| ... | @@ -196,7 +196,7 @@ const projectCommandUuid = Crypto.Crypto.pipe( | ... | @@ -196,7 +196,7 @@ const projectCommandUuid = Crypto.Crypto.pipe( |
| 196 | ), | 196 | ), |
| 197 | ); | 197 | ); |
| 198 | 198 | ||
| 199 | const ProjectCliRuntimeLive = ProjectServiceLayerLive.pipe( | 199 | const layerProjectCliRuntime = RuntimeLayer.layerProjectService.pipe( |
| 200 | Layer.provideMerge(ProjectEnrichmentService.layer), | 200 | Layer.provideMerge(ProjectEnrichmentService.layer), |
| 201 | Layer.provideMerge(RepositoryIdentityResolver.layer), | 201 | Layer.provideMerge(RepositoryIdentityResolver.layer), |
| 202 | Layer.provideMerge( | 202 | Layer.provideMerge( |
| ... | @@ -416,7 +416,7 @@ const runProjectMutation = Effect.fn("runProjectMutation")(function* ( | ... | @@ -416,7 +416,7 @@ const runProjectMutation = Effect.fn("runProjectMutation")(function* ( |
| 416 | ); | 416 | ); |
| 417 | } | 417 | } |
| 418 | 418 | ||
| 419 | const offlineRuntimeLayer = ProjectCliRuntimeLive.pipe( | 419 | const layerOfflineRuntime = layerProjectCliRuntime.pipe( |
| 420 | Layer.provide(ServerConfig.layer(config)), | 420 | Layer.provide(ServerConfig.layer(config)), |
| 421 | Layer.provide(Layer.succeed(References.MinimumLogLevel, minimumLogLevel)), | 421 | Layer.provide(Layer.succeed(References.MinimumLogLevel, minimumLogLevel)), |
| 422 | ); | 422 | ); |
| ... | @@ -430,10 +430,10 @@ const runProjectMutation = Effect.fn("runProjectMutation")(function* ( | ... | @@ -430,10 +430,10 @@ const runProjectMutation = Effect.fn("runProjectMutation")(function* ( |
| 430 | mode: "offline", | 430 | mode: "offline", |
| 431 | }); | 431 | }); |
| 432 | yield* Console.log(output); | 432 | yield* Console.log(output); |
| 433 | }).pipe(Effect.provide(offlineRuntimeLayer)); | 433 | }).pipe(Effect.provide(layerOfflineRuntime)); |
| 434 | }).pipe( | 434 | }).pipe( |
| 435 | Effect.provide( | 435 | Effect.provide( |
| 436 | Layer.mergeAll(EnvironmentAuth.runtimeLayer, WorkspacePaths.layer).pipe( | 436 | Layer.mergeAll(EnvironmentAuth.layerRuntime, WorkspacePaths.layer).pipe( |
| 437 | Layer.provideMerge(FetchHttpClient.layer), | 437 | Layer.provideMerge(FetchHttpClient.layer), |
| 438 | Layer.provide(ServerConfig.layer(config)), | 438 | Layer.provide(ServerConfig.layer(config)), |
| 439 | Layer.provide(Layer.succeed(References.MinimumLogLevel, minimumLogLevel)), | 439 | Layer.provide(Layer.succeed(References.MinimumLogLevel, minimumLogLevel)), |
apps/server/src/cli/service.ts+2-2| ... | @@ -13,7 +13,7 @@ import type * as ServerConfig from "../config.ts"; | ... | @@ -13,7 +13,7 @@ import type * as ServerConfig from "../config.ts"; |
| 13 | import * as ProcessRunner from "../processRunner.ts"; | 13 | import * as ProcessRunner from "../processRunner.ts"; |
| 14 | import { projectLocationFlags, resolveCliAuthConfig } from "./config.ts"; | 14 | import { projectLocationFlags, resolveCliAuthConfig } from "./config.ts"; |
| 15 | 15 | ||
| 16 | export const bootServiceLayer = (config: ServerConfig.ServerConfig["Service"]) => | 16 | export const layer = (config: ServerConfig.ServerConfig["Service"]) => |
| 17 | BootService.layer({ | 17 | BootService.layer({ |
| 18 | baseDir: config.baseDir, | 18 | baseDir: config.baseDir, |
| 19 | logsDir: config.logsDir, | 19 | logsDir: config.logsDir, |
| ... | @@ -107,7 +107,7 @@ const runServiceCommand = Effect.fn("cli.service.run")(function* <A, E>( | ... | @@ -107,7 +107,7 @@ const runServiceCommand = Effect.fn("cli.service.run")(function* <A, E>( |
| 107 | ) { | 107 | ) { |
| 108 | const logLevel = yield* GlobalFlag.LogLevel; | 108 | const logLevel = yield* GlobalFlag.LogLevel; |
| 109 | const config = yield* resolveCliAuthConfig(flags, logLevel); | 109 | const config = yield* resolveCliAuthConfig(flags, logLevel); |
| 110 | return yield* run.pipe(Effect.provide(bootServiceLayer(config))); | 110 | return yield* run.pipe(Effect.provide(layer(config))); |
| 111 | }); | 111 | }); |
| 112 | 112 | ||
| 113 | const serviceReconcileFlags = { | 113 | const serviceReconcileFlags = { |
apps/server/src/cli/uninstall.ts+2-2| ... | @@ -20,7 +20,7 @@ import { Command, Flag, GlobalFlag, Prompt } from "effect/cli"; | ... | @@ -20,7 +20,7 @@ import { Command, Flag, GlobalFlag, Prompt } from "effect/cli"; |
| 20 | import * as BootService from "../cloud/bootService.ts"; | 20 | import * as BootService from "../cloud/bootService.ts"; |
| 21 | import { pinnedRuntimeVersionsDir } from "../cloud/pinnedRuntime.ts"; | 21 | import { pinnedRuntimeVersionsDir } from "../cloud/pinnedRuntime.ts"; |
| 22 | import { projectLocationFlags, resolveCliAuthConfig } from "./config.ts"; | 22 | import { projectLocationFlags, resolveCliAuthConfig } from "./config.ts"; |
| 23 | import { bootServiceLayer } from "./service.ts"; | 23 | import * as CliService from "./service.ts"; |
| 24 | import { findWindowsShim, launcherOwnsVersionsDir, resolveLauncherPath } from "./update.ts"; | 24 | import { findWindowsShim, launcherOwnsVersionsDir, resolveLauncherPath } from "./update.ts"; |
| 25 | 25 | ||
| 26 | export class CliUninstallError extends Schema.TaggedError<CliUninstallError>()( | 26 | export class CliUninstallError extends Schema.TaggedError<CliUninstallError>()( |
| ... | @@ -117,7 +117,7 @@ export const uninstallCommand = Command.make("uninstall", { | ... | @@ -117,7 +117,7 @@ export const uninstallCommand = Command.make("uninstall", { |
| 117 | const logLevel = yield* GlobalFlag.LogLevel; | 117 | const logLevel = yield* GlobalFlag.LogLevel; |
| 118 | const config = yield* resolveCliAuthConfig(flags, logLevel); | 118 | const config = yield* resolveCliAuthConfig(flags, logLevel); |
| 119 | return yield* runUninstall({ baseDir: config.baseDir, assumeYes: flags.yes }).pipe( | 119 | return yield* runUninstall({ baseDir: config.baseDir, assumeYes: flags.yes }).pipe( |
| 120 | Effect.provide(bootServiceLayer(config)), | 120 | Effect.provide(CliService.layer(config)), |
| 121 | ); | 121 | ); |
| 122 | }), | 122 | }), |
| 123 | ), | 123 | ), |
apps/server/src/cli/update.ts+2-2| ... | @@ -38,7 +38,7 @@ import * as ProcessRunner from "../processRunner.ts"; | ... | @@ -38,7 +38,7 @@ import * as ProcessRunner from "../processRunner.ts"; |
| 38 | import { isProcessAlive, readPersistedServerRuntimeState } from "../serverRuntimeState.ts"; | 38 | import { isProcessAlive, readPersistedServerRuntimeState } from "../serverRuntimeState.ts"; |
| 39 | import { projectLocationFlags, resolveCliAuthConfig } from "./config.ts"; | 39 | import { projectLocationFlags, resolveCliAuthConfig } from "./config.ts"; |
| 40 | import { createUpdateProgress } from "./updateProgress.ts"; | 40 | import { createUpdateProgress } from "./updateProgress.ts"; |
| 41 | import { bootServiceLayer } from "./service.ts"; | 41 | import * as CliService from "./service.ts"; |
| 42 | 42 | ||
| 43 | export class CliUpdateError extends Schema.TaggedError<CliUpdateError>()("CliUpdateError", { | 43 | export class CliUpdateError extends Schema.TaggedError<CliUpdateError>()("CliUpdateError", { |
| 44 | reason: Schema.String, | 44 | reason: Schema.String, |
| ... | @@ -267,7 +267,7 @@ export const updateCommand = Command.make("update", { | ... | @@ -267,7 +267,7 @@ export const updateCommand = Command.make("update", { |
| 267 | assumeYes: flags.yes, | 267 | assumeYes: flags.yes, |
| 268 | }).pipe( | 268 | }).pipe( |
| 269 | Effect.provide( | 269 | Effect.provide( |
| 270 | Layer.mergeAll(bootServiceLayer(config), ProcessRunner.layer, FetchHttpClient.layer), | 270 | Layer.mergeAll(CliService.layer(config), ProcessRunner.layer, FetchHttpClient.layer), |
| 271 | ), | 271 | ), |
| 272 | ); | 272 | ); |
| 273 | }), | 273 | }), |
apps/server/src/cloud/CliState.test.ts+3-3| ... | @@ -29,7 +29,7 @@ const persistedCloudLinkSecrets = [ | ... | @@ -29,7 +29,7 @@ const persistedCloudLinkSecrets = [ |
| 29 | PUBLISH_AGENT_ACTIVITY_SECRET, | 29 | PUBLISH_AGENT_ACTIVITY_SECRET, |
| 30 | ] as const; | 30 | ] as const; |
| 31 | 31 | ||
| 32 | const makeTestLayer = () => | 32 | const layerTest = () => |
| 33 | ServerSecretStore.layer.pipe( | 33 | ServerSecretStore.layer.pipe( |
| 34 | Layer.provide( | 34 | Layer.provide( |
| 35 | ServerConfig.layerTest(process.cwd(), { | 35 | ServerConfig.layerTest(process.cwd(), { |
| ... | @@ -56,7 +56,7 @@ it.layer(NodeServices.layer)("CliState", (it) => { | ... | @@ -56,7 +56,7 @@ it.layer(NodeServices.layer)("CliState", (it) => { |
| 56 | for (const name of persistedCloudLinkSecrets) { | 56 | for (const name of persistedCloudLinkSecrets) { |
| 57 | assert.isTrue(Option.isNone(yield* secrets.get(name))); | 57 | assert.isTrue(Option.isNone(yield* secrets.get(name))); |
| 58 | } | 58 | } |
| 59 | }).pipe(Effect.provide(makeTestLayer())), | 59 | }).pipe(Effect.provide(layerTest())), |
| 60 | ); | 60 | ); |
| 61 | 61 | ||
| 62 | it.effect("round-trips the desired link mode and defaults legacy links to managed", () => | 62 | it.effect("round-trips the desired link mode and defaults legacy links to managed", () => |
| ... | @@ -79,6 +79,6 @@ it.layer(NodeServices.layer)("CliState", (it) => { | ... | @@ -79,6 +79,6 @@ it.layer(NodeServices.layer)("CliState", (it) => { |
| 79 | 79 | ||
| 80 | yield* CliState.setCliDesiredCloudLink(false); | 80 | yield* CliState.setCliDesiredCloudLink(false); |
| 81 | assert.equal(yield* CliState.readCliDesiredLinkMode, "managed"); | 81 | assert.equal(yield* CliState.readCliDesiredLinkMode, "managed"); |
| 82 | }).pipe(Effect.provide(makeTestLayer())), | 82 | }).pipe(Effect.provide(layerTest())), |
| 83 | ); | 83 | ); |
| 84 | }); | 84 | }); |
apps/server/src/cloud/CliTokenManager.test.ts+7-7| ... | @@ -137,7 +137,7 @@ const tokenGranted = { | ... | @@ -137,7 +137,7 @@ const tokenGranted = { |
| 137 | }), | 137 | }), |
| 138 | }; | 138 | }; |
| 139 | 139 | ||
| 140 | const makeDeviceFlowLayer = (server: DeviceFlowServer) => | 140 | const layerDeviceFlow = (server: DeviceFlowServer) => |
| 141 | Layer.succeed( | 141 | Layer.succeed( |
| 142 | HttpClient.HttpClient, | 142 | HttpClient.HttpClient, |
| 143 | HttpClient.make((request) => | 143 | HttpClient.make((request) => |
| ... | @@ -177,7 +177,7 @@ it.layer(NodeServices.layer)("CliTokenManager.deviceAuthorizationLogin", (it) => | ... | @@ -177,7 +177,7 @@ it.layer(NodeServices.layer)("CliTokenManager.deviceAuthorizationLogin", (it) => |
| 177 | Effect.sync(() => { | 177 | Effect.sync(() => { |
| 178 | prompts.push(prompt); | 178 | prompts.push(prompt); |
| 179 | }), | 179 | }), |
| 180 | ).pipe(Effect.provide(makeDeviceFlowLayer(server)), provideTestEnv, Effect.forkChild); | 180 | ).pipe(Effect.provide(layerDeviceFlow(server)), provideTestEnv, Effect.forkChild); |
| 181 | 181 | ||
| 182 | yield* TestClock.adjust(Duration.seconds(10)); | 182 | yield* TestClock.adjust(Duration.seconds(10)); |
| 183 | const { token, identity } = yield* Fiber.join(fiber); | 183 | const { token, identity } = yield* Fiber.join(fiber); |
| ... | @@ -219,7 +219,7 @@ it.layer(NodeServices.layer)("CliTokenManager.deviceAuthorizationLogin", (it) => | ... | @@ -219,7 +219,7 @@ it.layer(NodeServices.layer)("CliTokenManager.deviceAuthorizationLogin", (it) => |
| 219 | }; | 219 | }; |
| 220 | 220 | ||
| 221 | const fiber = yield* CliTokenManager.deviceAuthorizationLogin(() => Effect.void).pipe( | 221 | const fiber = yield* CliTokenManager.deviceAuthorizationLogin(() => Effect.void).pipe( |
| 222 | Effect.provide(makeDeviceFlowLayer(server)), | 222 | Effect.provide(layerDeviceFlow(server)), |
| 223 | provideTestEnv, | 223 | provideTestEnv, |
| 224 | Effect.forkChild, | 224 | Effect.forkChild, |
| 225 | ); | 225 | ); |
| ... | @@ -245,7 +245,7 @@ it.layer(NodeServices.layer)("CliTokenManager.deviceAuthorizationLogin", (it) => | ... | @@ -245,7 +245,7 @@ it.layer(NodeServices.layer)("CliTokenManager.deviceAuthorizationLogin", (it) => |
| 245 | }; | 245 | }; |
| 246 | 246 | ||
| 247 | const fiber = yield* CliTokenManager.deviceAuthorizationLogin(() => Effect.void).pipe( | 247 | const fiber = yield* CliTokenManager.deviceAuthorizationLogin(() => Effect.void).pipe( |
| 248 | Effect.provide(makeDeviceFlowLayer(server)), | 248 | Effect.provide(layerDeviceFlow(server)), |
| 249 | provideTestEnv, | 249 | provideTestEnv, |
| 250 | Effect.forkChild, | 250 | Effect.forkChild, |
| 251 | ); | 251 | ); |
| ... | @@ -270,7 +270,7 @@ it.layer(NodeServices.layer)("CliTokenManager.deviceAuthorizationLogin", (it) => | ... | @@ -270,7 +270,7 @@ it.layer(NodeServices.layer)("CliTokenManager.deviceAuthorizationLogin", (it) => |
| 270 | }; | 270 | }; |
| 271 | 271 | ||
| 272 | const fiber = yield* CliTokenManager.deviceAuthorizationLogin(() => Effect.void).pipe( | 272 | const fiber = yield* CliTokenManager.deviceAuthorizationLogin(() => Effect.void).pipe( |
| 273 | Effect.provide(makeDeviceFlowLayer(server)), | 273 | Effect.provide(layerDeviceFlow(server)), |
| 274 | provideTestEnv, | 274 | provideTestEnv, |
| 275 | Effect.flip, | 275 | Effect.flip, |
| 276 | Effect.forkChild, | 276 | Effect.forkChild, |
| ... | @@ -291,7 +291,7 @@ it.layer(NodeServices.layer)("CliTokenManager.deviceAuthorizationLogin", (it) => | ... | @@ -291,7 +291,7 @@ it.layer(NodeServices.layer)("CliTokenManager.deviceAuthorizationLogin", (it) => |
| 291 | }; | 291 | }; |
| 292 | 292 | ||
| 293 | const fiber = yield* CliTokenManager.deviceAuthorizationLogin(() => Effect.void).pipe( | 293 | const fiber = yield* CliTokenManager.deviceAuthorizationLogin(() => Effect.void).pipe( |
| 294 | Effect.provide(makeDeviceFlowLayer(server)), | 294 | Effect.provide(layerDeviceFlow(server)), |
| 295 | provideTestEnv, | 295 | provideTestEnv, |
| 296 | Effect.flip, | 296 | Effect.flip, |
| 297 | Effect.forkChild, | 297 | Effect.forkChild, |
| ... | @@ -311,7 +311,7 @@ it.layer(NodeServices.layer)("CliTokenManager.deviceAuthorizationLogin", (it) => | ... | @@ -311,7 +311,7 @@ it.layer(NodeServices.layer)("CliTokenManager.deviceAuthorizationLogin", (it) => |
| 311 | }; | 311 | }; |
| 312 | 312 | ||
| 313 | const fiber = yield* CliTokenManager.deviceAuthorizationLogin(() => Effect.void).pipe( | 313 | const fiber = yield* CliTokenManager.deviceAuthorizationLogin(() => Effect.void).pipe( |
| 314 | Effect.provide(makeDeviceFlowLayer(server)), | 314 | Effect.provide(layerDeviceFlow(server)), |
| 315 | provideTestEnv, | 315 | provideTestEnv, |
| 316 | Effect.flip, | 316 | Effect.flip, |
| 317 | Effect.forkChild, | 317 | Effect.forkChild, |
apps/server/src/cloud/CliTokenManager.ts+2-2| ... | @@ -461,7 +461,7 @@ export const make = Effect.gen(function* () { | ... | @@ -461,7 +461,7 @@ export const make = Effect.gen(function* () { |
| 461 | const hostedAppUrl = yield* hostedAppUrlConfig; | 461 | const hostedAppUrl = yield* hostedAppUrlConfig; |
| 462 | const { verifier, challenge, state } = yield* makePkceRequest; | 462 | const { verifier, challenge, state } = yield* makePkceRequest; |
| 463 | const callback = yield* Deferred.make<string>(); | 463 | const callback = yield* Deferred.make<string>(); |
| 464 | const callbackRoute = HttpRouter.add( | 464 | const layerCallbackRoute = HttpRouter.add( |
| 465 | "GET", | 465 | "GET", |
| 466 | "/callback", | 466 | "/callback", |
| 467 | Effect.gen(function* () { | 467 | Effect.gen(function* () { |
| ... | @@ -477,7 +477,7 @@ export const make = Effect.gen(function* () { | ... | @@ -477,7 +477,7 @@ export const make = Effect.gen(function* () { |
| 477 | return HttpServerResponse.html(renderLoopbackAuthorizationCompleteHtml()); | 477 | return HttpServerResponse.html(renderLoopbackAuthorizationCompleteHtml()); |
| 478 | }), | 478 | }), |
| 479 | ); | 479 | ); |
| 480 | yield* HttpRouter.serve(callbackRoute, { | 480 | yield* HttpRouter.serve(layerCallbackRoute, { |
| 481 | disableListenLog: true, | 481 | disableListenLog: true, |
| 482 | disableLogger: true, | 482 | disableLogger: true, |
| 483 | }).pipe( | 483 | }).pipe( |
apps/server/src/cloud/CloudPreferences.test.ts+2-2| ... | @@ -61,7 +61,7 @@ const withService = <A, E>( | ... | @@ -61,7 +61,7 @@ const withService = <A, E>( |
| 61 | }, | 61 | }, |
| 62 | { preconnect: () => {} }, | 62 | { preconnect: () => {} }, |
| 63 | ); | 63 | ); |
| 64 | const dependencies = Layer.mergeAll( | 64 | const layerDependencies = Layer.mergeAll( |
| 65 | Layer.mock(ServerSecretStore.ServerSecretStore)({ | 65 | Layer.mock(ServerSecretStore.ServerSecretStore)({ |
| 66 | get: (name) => | 66 | get: (name) => |
| 67 | (options.failActivityRead && name === PUBLISH_AGENT_ACTIVITY_SECRET) || | 67 | (options.failActivityRead && name === PUBLISH_AGENT_ACTIVITY_SECRET) || |
| ... | @@ -93,7 +93,7 @@ const withService = <A, E>( | ... | @@ -93,7 +93,7 @@ const withService = <A, E>( |
| 93 | const preferences = yield* CloudPreferences.CloudPreferences; | 93 | const preferences = yield* CloudPreferences.CloudPreferences; |
| 94 | return yield* body({ preferences, stored, relayCalls }); | 94 | return yield* body({ preferences, stored, relayCalls }); |
| 95 | }).pipe( | 95 | }).pipe( |
| 96 | Effect.provide(CloudPreferences.layer.pipe(Layer.provide(dependencies))), | 96 | Effect.provide(CloudPreferences.layer.pipe(Layer.provide(layerDependencies))), |
| 97 | Effect.provideService(FetchHttpClient.Fetch, fetch), | 97 | Effect.provideService(FetchHttpClient.Fetch, fetch), |
| 98 | ); | 98 | ); |
| 99 | }); | 99 | }); |
apps/server/src/cloud/ManagedEndpointRuntime.test.ts+3-3| ... | @@ -18,7 +18,7 @@ import * as RelayClient from "@t3tools/shared/relayClient"; | ... | @@ -18,7 +18,7 @@ import * as RelayClient from "@t3tools/shared/relayClient"; |
| 18 | import * as ServerSecretStore from "../auth/ServerSecretStore.ts"; | 18 | import * as ServerSecretStore from "../auth/ServerSecretStore.ts"; |
| 19 | import * as ManagedEndpointRuntime from "./ManagedEndpointRuntime.ts"; | 19 | import * as ManagedEndpointRuntime from "./ManagedEndpointRuntime.ts"; |
| 20 | 20 | ||
| 21 | const relayClientAvailableLayer = Layer.succeed( | 21 | const layerRelayClientAvailable = Layer.succeed( |
| 22 | RelayClient.RelayClient, | 22 | RelayClient.RelayClient, |
| 23 | RelayClient.RelayClient.of({ | 23 | RelayClient.RelayClient.of({ |
| 24 | resolve: Effect.succeed({ | 24 | resolve: Effect.succeed({ |
| ... | @@ -34,7 +34,7 @@ const relayClientAvailableLayer = Layer.succeed( | ... | @@ -34,7 +34,7 @@ const relayClientAvailableLayer = Layer.succeed( |
| 34 | 34 | ||
| 35 | const runtimeDependencies = ( | 35 | const runtimeDependencies = ( |
| 36 | spawner: ReturnType<typeof ChildProcessSpawner.make>, | 36 | spawner: ReturnType<typeof ChildProcessSpawner.make>, |
| 37 | relayClientLayer = relayClientAvailableLayer, | 37 | relayClientLayer = layerRelayClientAvailable, |
| 38 | ) => | 38 | ) => |
| 39 | Layer.mergeAll( | 39 | Layer.mergeAll( |
| 40 | Layer.succeed(ChildProcessSpawner.ChildProcessSpawner, spawner), | 40 | Layer.succeed(ChildProcessSpawner.ChildProcessSpawner, spawner), |
| ... | @@ -46,7 +46,7 @@ const runtimeDependencies = ( | ... | @@ -46,7 +46,7 @@ const runtimeDependencies = ( |
| 46 | 46 | ||
| 47 | const buildCloudManagedEndpointRuntime = ( | 47 | const buildCloudManagedEndpointRuntime = ( |
| 48 | spawner: ReturnType<typeof ChildProcessSpawner.make>, | 48 | spawner: ReturnType<typeof ChildProcessSpawner.make>, |
| 49 | relayClientLayer = relayClientAvailableLayer, | 49 | relayClientLayer = layerRelayClientAvailable, |
| 50 | ) => | 50 | ) => |
| 51 | Effect.gen(function* () { | 51 | Effect.gen(function* () { |
| 52 | const context = yield* Layer.build( | 52 | const context = yield* Layer.build( |
apps/server/src/cloud/environmentKeys.test.ts+3-3| ... | @@ -9,7 +9,7 @@ import * as ServerSecretStore from "../auth/ServerSecretStore.ts"; | ... | @@ -9,7 +9,7 @@ import * as ServerSecretStore from "../auth/ServerSecretStore.ts"; |
| 9 | import * as ServerConfig from "../config.ts"; | 9 | import * as ServerConfig from "../config.ts"; |
| 10 | import { getOrCreateEnvironmentKeyPairFromSecretStore } from "./environmentKeys.ts"; | 10 | import { getOrCreateEnvironmentKeyPairFromSecretStore } from "./environmentKeys.ts"; |
| 11 | 11 | ||
| 12 | const makeServerSecretStoreLayer = () => | 12 | const layerServerSecretStore = () => |
| 13 | ServerSecretStore.layer.pipe( | 13 | ServerSecretStore.layer.pipe( |
| 14 | Layer.provide(ServerConfig.layerTest(process.cwd(), { prefix: "t3-environment-keys-test-" })), | 14 | Layer.provide(ServerConfig.layerTest(process.cwd(), { prefix: "t3-environment-keys-test-" })), |
| 15 | ); | 15 | ); |
| ... | @@ -28,7 +28,7 @@ it.layer(NodeServices.layer)("getOrCreateEnvironmentKeyPairFromSecretStore", (it | ... | @@ -28,7 +28,7 @@ it.layer(NodeServices.layer)("getOrCreateEnvironmentKeyPairFromSecretStore", (it |
| 28 | assert.isTrue(Option.isSome(yield* secretStore.get("cloud-link-ed25519-key-pair"))); | 28 | assert.isTrue(Option.isSome(yield* secretStore.get("cloud-link-ed25519-key-pair"))); |
| 29 | assert.isTrue(Option.isNone(yield* secretStore.get("cloud-link-ed25519-private-key"))); | 29 | assert.isTrue(Option.isNone(yield* secretStore.get("cloud-link-ed25519-private-key"))); |
| 30 | assert.isTrue(Option.isNone(yield* secretStore.get("cloud-link-ed25519-public-key"))); | 30 | assert.isTrue(Option.isNone(yield* secretStore.get("cloud-link-ed25519-public-key"))); |
| 31 | }).pipe(Effect.provide(makeServerSecretStoreLayer())), | 31 | }).pipe(Effect.provide(layerServerSecretStore())), |
| 32 | ); | 32 | ); |
| 33 | 33 | ||
| 34 | it.effect("migrates a legacy keypair into the atomic secret", () => | 34 | it.effect("migrates a legacy keypair into the atomic secret", () => |
| ... | @@ -42,7 +42,7 @@ it.layer(NodeServices.layer)("getOrCreateEnvironmentKeyPairFromSecretStore", (it | ... | @@ -42,7 +42,7 @@ it.layer(NodeServices.layer)("getOrCreateEnvironmentKeyPairFromSecretStore", (it |
| 42 | publicKey: "public", | 42 | publicKey: "public", |
| 43 | }); | 43 | }); |
| 44 | assert.isTrue(Option.isSome(yield* secretStore.get("cloud-link-ed25519-key-pair"))); | 44 | assert.isTrue(Option.isSome(yield* secretStore.get("cloud-link-ed25519-key-pair"))); |
| 45 | }).pipe(Effect.provide(makeServerSecretStoreLayer())), | 45 | }).pipe(Effect.provide(layerServerSecretStore())), |
| 46 | ); | 46 | ); |
| 47 | 47 | ||
| 48 | it.effect("uses the persisted keypair when a concurrent creator wins", () => | 48 | it.effect("uses the persisted keypair when a concurrent creator wins", () => |
apps/server/src/cloud/http.ts+1-1| ... | @@ -1594,7 +1594,7 @@ const cloudMintCredentialHandler = Effect.fn("environment.cloud.mintCredential") | ... | @@ -1594,7 +1594,7 @@ const cloudMintCredentialHandler = Effect.fn("environment.cloud.mintCredential") |
| 1594 | ), | 1594 | ), |
| 1595 | ); | 1595 | ); |
| 1596 | 1596 | ||
| 1597 | export const connectHttpApiLayer = HttpApiBuilder.group( | 1597 | export const layer = HttpApiBuilder.group( |
| 1598 | EnvironmentHttpApi, | 1598 | EnvironmentHttpApi, |
| 1599 | "connect", | 1599 | "connect", |
| 1600 | Effect.fnUntraced(function* (handlers) { | 1600 | Effect.fnUntraced(function* (handlers) { |
apps/server/src/cloud/relayTracing.ts+7-10| ... | @@ -1,19 +1,16 @@ | ... | @@ -1,19 +1,16 @@ |
| 1 | import { makeRelayClientTracingLayer } from "@t3tools/shared/relayTracing"; | 1 | import * as RelayTracing from "@t3tools/shared/relayTracing"; |
| 2 | 2 | ||
| 3 | import { resolveRelayClientTracingConfig } from "./publicConfig.ts"; | 3 | import { resolveRelayClientTracingConfig } from "./publicConfig.ts"; |
| 4 | 4 | ||
| 5 | const relayClientTracingConfig = resolveRelayClientTracingConfig(); | 5 | const relayClientTracingConfig = resolveRelayClientTracingConfig(); |
| 6 | 6 | ||
| 7 | export const headlessRelayClientTracingLayer = makeRelayClientTracingLayer( | 7 | export const layerHeadlessRelayClient = RelayTracing.layer(relayClientTracingConfig, { |
| 8 | relayClientTracingConfig, | 8 | serviceName: "t3code-server", |
| 9 | { | 9 | runtime: "node", |
| 10 | serviceName: "t3code-server", | 10 | client: "headless-cli", |
| 11 | runtime: "node", | 11 | }); |
| 12 | client: "headless-cli", | ||
| 13 | }, | ||
| 14 | ); | ||
| 15 | 12 | ||
| 16 | export const serverRelayBrokerTracingLayer = makeRelayClientTracingLayer(relayClientTracingConfig, { | 13 | export const layerServerRelayBroker = RelayTracing.layer(relayClientTracingConfig, { |
| 17 | serviceName: "t3code-server", | 14 | serviceName: "t3code-server", |
| 18 | runtime: "node", | 15 | runtime: "node", |
| 19 | client: "environment-server", | 16 | client: "environment-server", |
apps/server/src/device/DeviceHubProxy.test.ts+2-2| ... | @@ -11,7 +11,7 @@ import * as Layer from "effect/Layer"; | ... | @@ -11,7 +11,7 @@ import * as Layer from "effect/Layer"; |
| 11 | import { HttpClient, HttpClientResponse, HttpRouter } from "effect/http"; | 11 | import { HttpClient, HttpClientResponse, HttpRouter } from "effect/http"; |
| 12 | import * as EnvironmentAuth from "../auth/EnvironmentAuth.ts"; | 12 | import * as EnvironmentAuth from "../auth/EnvironmentAuth.ts"; |
| 13 | import * as DeviceService from "./DeviceService.ts"; | 13 | import * as DeviceService from "./DeviceService.ts"; |
| 14 | import { deviceHubProxyRouteLayer } from "./DeviceHubProxy.ts"; | 14 | import * as DeviceHubProxy from "./DeviceHubProxy.ts"; |
| 15 | 15 | ||
| 16 | const disposers: Array<() => Promise<void>> = []; | 16 | const disposers: Array<() => Promise<void>> = []; |
| 17 | afterEach(async () => { | 17 | afterEach(async () => { |
| ... | @@ -36,7 +36,7 @@ const fixture = ( | ... | @@ -36,7 +36,7 @@ const fixture = ( |
| 36 | }), | 36 | }), |
| 37 | ); | 37 | ); |
| 38 | const { handler, dispose } = HttpRouter.toWebHandler( | 38 | const { handler, dispose } = HttpRouter.toWebHandler( |
| 39 | deviceHubProxyRouteLayer.pipe( | 39 | DeviceHubProxy.layer.pipe( |
| 40 | Layer.provideMerge( | 40 | Layer.provideMerge( |
| 41 | Layer.succeed(EnvironmentAuth.EnvironmentAuth, { | 41 | Layer.succeed(EnvironmentAuth.EnvironmentAuth, { |
| 42 | authenticateWebSocketUpgrade: () => | 42 | authenticateWebSocketUpgrade: () => |
apps/server/src/device/DeviceHubProxy.ts+1-5| ... | @@ -230,8 +230,4 @@ const handler = Effect.gen(function* () { | ... | @@ -230,8 +230,4 @@ const handler = Effect.gen(function* () { |
| 230 | return yield* proxyHttp(request, `${ready.hub.origin}${upstreamPath}`, ready.hub.origin); | 230 | return yield* proxyHttp(request, `${ready.hub.origin}${upstreamPath}`, ready.hub.origin); |
| 231 | }); | 231 | }); |
| 232 | 232 | ||
| 233 | export const deviceHubProxyRouteLayer = HttpRouter.add( | 233 | export const layer = HttpRouter.add("*", `${DeviceService.DEVICE_HUB_ROUTE_PREFIX}/*`, handler); |
| 234 | "*", | ||
| 235 | `${DeviceService.DEVICE_HUB_ROUTE_PREFIX}/*`, | ||
| 236 | handler, | ||
| 237 | ); |
apps/server/src/diagnostics/ProcessDiagnostics.test.ts+13-13| ... | @@ -31,11 +31,11 @@ function makeNativeSnapshot( | ... | @@ -31,11 +31,11 @@ function makeNativeSnapshot( |
| 31 | }; | 31 | }; |
| 32 | } | 32 | } |
| 33 | 33 | ||
| 34 | function makeTelemetryLayer( | 34 | function layerTelemetryFor( |
| 35 | snapshot: ResourceMonitorSnapshotEvent, | 35 | snapshot: ResourceMonitorSnapshotEvent, |
| 36 | desktopSnapshot?: DesktopHostTelemetrySnapshot, | 36 | desktopSnapshot?: DesktopHostTelemetrySnapshot, |
| 37 | ) { | 37 | ) { |
| 38 | const nativeLayer = NativeTelemetryClient.layerTest({ | 38 | const layerNative = NativeTelemetryClient.layerTest({ |
| 39 | sampleNow: Effect.succeed({ generation: 0, snapshot }), | 39 | sampleNow: Effect.succeed({ generation: 0, snapshot }), |
| 40 | health: Effect.succeed({ | 40 | health: Effect.succeed({ |
| 41 | status: "healthy", | 41 | status: "healthy", |
| ... | @@ -46,7 +46,7 @@ function makeTelemetryLayer( | ... | @@ -46,7 +46,7 @@ function makeTelemetryLayer( |
| 46 | sampleIntervalMs: 1_000, | 46 | sampleIntervalMs: 1_000, |
| 47 | }), | 47 | }), |
| 48 | }); | 48 | }); |
| 49 | const desktopLayer = desktopSnapshot | 49 | const layerDesktop = desktopSnapshot |
| 50 | ? DesktopTelemetryReceiver.layerTest({ | 50 | ? DesktopTelemetryReceiver.layerTest({ |
| 51 | latest: Effect.succeedSome(desktopSnapshot), | 51 | latest: Effect.succeedSome(desktopSnapshot), |
| 52 | health: Effect.succeed({ | 52 | health: Effect.succeed({ |
| ... | @@ -57,7 +57,7 @@ function makeTelemetryLayer( | ... | @@ -57,7 +57,7 @@ function makeTelemetryLayer( |
| 57 | }) | 57 | }) |
| 58 | : DesktopTelemetryReceiver.layerTest(); | 58 | : DesktopTelemetryReceiver.layerTest(); |
| 59 | return ResourceTelemetry.layer.pipe( | 59 | return ResourceTelemetry.layer.pipe( |
| 60 | Layer.provide(Layer.mergeAll(nativeLayer, desktopLayer, ResourceAttribution.layer)), | 60 | Layer.provide(Layer.mergeAll(layerNative, layerDesktop, ResourceAttribution.layer)), |
| 61 | ); | 61 | ); |
| 62 | } | 62 | } |
| 63 | 63 | ||
| ... | @@ -98,8 +98,8 @@ describe("ProcessDiagnostics", () => { | ... | @@ -98,8 +98,8 @@ describe("ProcessDiagnostics", () => { |
| 98 | ioSemantics: "storage", | 98 | ioSemantics: "storage", |
| 99 | }, | 99 | }, |
| 100 | ]); | 100 | ]); |
| 101 | const telemetryLayer = makeTelemetryLayer(snapshot); | 101 | const layerTelemetry = layerTelemetryFor(snapshot); |
| 102 | const layer = ProcessDiagnostics.layer.pipe(Layer.provideMerge(telemetryLayer)); | 102 | const layer = ProcessDiagnostics.layer.pipe(Layer.provideMerge(layerTelemetry)); |
| 103 | 103 | ||
| 104 | const diagnostics = yield* Effect.gen(function* () { | 104 | const diagnostics = yield* Effect.gen(function* () { |
| 105 | const processDiagnostics = yield* ProcessDiagnostics.ProcessDiagnostics; | 105 | const processDiagnostics = yield* ProcessDiagnostics.ProcessDiagnostics; |
| ... | @@ -116,8 +116,8 @@ describe("ProcessDiagnostics", () => { | ... | @@ -116,8 +116,8 @@ describe("ProcessDiagnostics", () => { |
| 116 | it.effect("rejects stale process identities before signaling", () => | 116 | it.effect("rejects stale process identities before signaling", () => |
| 117 | Effect.gen(function* () { | 117 | Effect.gen(function* () { |
| 118 | const snapshot = makeNativeSnapshot([]); | 118 | const snapshot = makeNativeSnapshot([]); |
| 119 | const telemetryLayer = makeTelemetryLayer(snapshot); | 119 | const layerTelemetry = layerTelemetryFor(snapshot); |
| 120 | const layer = ProcessDiagnostics.layer.pipe(Layer.provide(telemetryLayer)); | 120 | const layer = ProcessDiagnostics.layer.pipe(Layer.provide(layerTelemetry)); |
| 121 | 121 | ||
| 122 | const result = yield* Effect.service(ProcessDiagnostics.ProcessDiagnostics).pipe( | 122 | const result = yield* Effect.service(ProcessDiagnostics.ProcessDiagnostics).pipe( |
| 123 | Effect.flatMap((processDiagnostics) => | 123 | Effect.flatMap((processDiagnostics) => |
| ... | @@ -161,9 +161,9 @@ describe("ProcessDiagnostics", () => { | ... | @@ -161,9 +161,9 @@ describe("ProcessDiagnostics", () => { |
| 161 | ]); | 161 | ]); |
| 162 | const staleTelemetry = yield* Effect.service(ResourceTelemetry.ResourceTelemetry).pipe( | 162 | const staleTelemetry = yield* Effect.service(ResourceTelemetry.ResourceTelemetry).pipe( |
| 163 | Effect.flatMap((telemetry) => telemetry.latest), | 163 | Effect.flatMap((telemetry) => telemetry.latest), |
| 164 | Effect.provide(makeTelemetryLayer(snapshot)), | 164 | Effect.provide(layerTelemetryFor(snapshot)), |
| 165 | ); | 165 | ); |
| 166 | const telemetryLayer = Layer.succeed( | 166 | const layerTelemetry = Layer.succeed( |
| 167 | ResourceTelemetry.ResourceTelemetry, | 167 | ResourceTelemetry.ResourceTelemetry, |
| 168 | ResourceTelemetry.ResourceTelemetry.of({ | 168 | ResourceTelemetry.ResourceTelemetry.of({ |
| 169 | latest: Effect.succeed(staleTelemetry), | 169 | latest: Effect.succeed(staleTelemetry), |
| ... | @@ -180,7 +180,7 @@ describe("ProcessDiagnostics", () => { | ... | @@ -180,7 +180,7 @@ describe("ProcessDiagnostics", () => { |
| 180 | retry: Effect.die("unused"), | 180 | retry: Effect.die("unused"), |
| 181 | }), | 181 | }), |
| 182 | ); | 182 | ); |
| 183 | const layer = ProcessDiagnostics.layer.pipe(Layer.provide(telemetryLayer)); | 183 | const layer = ProcessDiagnostics.layer.pipe(Layer.provide(layerTelemetry)); |
| 184 | 184 | ||
| 185 | const result = yield* Effect.service(ProcessDiagnostics.ProcessDiagnostics).pipe( | 185 | const result = yield* Effect.service(ProcessDiagnostics.ProcessDiagnostics).pipe( |
| 186 | Effect.flatMap((processDiagnostics) => | 186 | Effect.flatMap((processDiagnostics) => |
| ... | @@ -228,7 +228,7 @@ describe("ProcessDiagnostics", () => { | ... | @@ -228,7 +228,7 @@ describe("ProcessDiagnostics", () => { |
| 228 | }, | 228 | }, |
| 229 | ]); | 229 | ]); |
| 230 | const sampledAt = DateTime.makeUnsafe(sampledAtUnixMs); | 230 | const sampledAt = DateTime.makeUnsafe(sampledAtUnixMs); |
| 231 | const telemetryLayer = makeTelemetryLayer(snapshot, { | 231 | const layerTelemetry = layerTelemetryFor(snapshot, { |
| 232 | version: 1, | 232 | version: 1, |
| 233 | type: "desktopTelemetry", | 233 | type: "desktopTelemetry", |
| 234 | sequence: 1, | 234 | sequence: 1, |
| ... | @@ -260,7 +260,7 @@ describe("ProcessDiagnostics", () => { | ... | @@ -260,7 +260,7 @@ describe("ProcessDiagnostics", () => { |
| 260 | }, | 260 | }, |
| 261 | ], | 261 | ], |
| 262 | }); | 262 | }); |
| 263 | const layer = ProcessDiagnostics.layer.pipe(Layer.provide(telemetryLayer)); | 263 | const layer = ProcessDiagnostics.layer.pipe(Layer.provide(layerTelemetry)); |
| 264 | 264 | ||
| 265 | const result = yield* Effect.service(ProcessDiagnostics.ProcessDiagnostics).pipe( | 265 | const result = yield* Effect.service(ProcessDiagnostics.ProcessDiagnostics).pipe( |
| 266 | Effect.flatMap((processDiagnostics) => | 266 | Effect.flatMap((processDiagnostics) => |
apps/server/src/environment/DirectEndpoints.test.ts+4-4| ... | @@ -135,7 +135,7 @@ const TAILSCALE_STATUS_JSON = JSON.stringify({ | ... | @@ -135,7 +135,7 @@ const TAILSCALE_STATUS_JSON = JSON.stringify({ |
| 135 | }); | 135 | }); |
| 136 | 136 | ||
| 137 | /** `tailscale status --json` reporting a MagicDNS name. */ | 137 | /** `tailscale status --json` reporting a MagicDNS name. */ |
| 138 | const tailscaleUpLayer = Layer.succeed( | 138 | const layerTailscaleUp = Layer.succeed( |
| 139 | ChildProcessSpawner.ChildProcessSpawner, | 139 | ChildProcessSpawner.ChildProcessSpawner, |
| 140 | ChildProcessSpawner.make(() => | 140 | ChildProcessSpawner.make(() => |
| 141 | Effect.succeed( | 141 | Effect.succeed( |
| ... | @@ -157,7 +157,7 @@ const tailscaleUpLayer = Layer.succeed( | ... | @@ -157,7 +157,7 @@ const tailscaleUpLayer = Layer.succeed( |
| 157 | ); | 157 | ); |
| 158 | 158 | ||
| 159 | /** Answers the Serve probe with `status`. */ | 159 | /** Answers the Serve probe with `status`. */ |
| 160 | const serveProbeLayer = (status: number) => | 160 | const layerServeProbe = (status: number) => |
| 161 | Layer.succeed( | 161 | Layer.succeed( |
| 162 | HttpClient.HttpClient, | 162 | HttpClient.HttpClient, |
| 163 | HttpClient.make((request) => | 163 | HttpClient.make((request) => |
| ... | @@ -166,7 +166,7 @@ const serveProbeLayer = (status: number) => | ... | @@ -166,7 +166,7 @@ const serveProbeLayer = (status: number) => |
| 166 | ); | 166 | ); |
| 167 | 167 | ||
| 168 | /** A loopback-only server with Tailscale Serve on, so only the Serve name can be listed. */ | 168 | /** A loopback-only server with Tailscale Serve on, so only the Serve name can be listed. */ |
| 169 | const serveConfigLayer = Layer.effect( | 169 | const layerServeConfig = Layer.effect( |
| 170 | ServerConfig.ServerConfig, | 170 | ServerConfig.ServerConfig, |
| 171 | Effect.map(ServerConfig.ServerConfig, (config) => ({ | 171 | Effect.map(ServerConfig.ServerConfig, (config) => ({ |
| 172 | ...config, | 172 | ...config, |
| ... | @@ -184,7 +184,7 @@ const resolveWithServe = (probeStatus: number) => | ... | @@ -184,7 +184,7 @@ const resolveWithServe = (probeStatus: number) => |
| 184 | Effect.provide( | 184 | Effect.provide( |
| 185 | DirectEndpoints.layer.pipe( | 185 | DirectEndpoints.layer.pipe( |
| 186 | Layer.provide( | 186 | Layer.provide( |
| 187 | Layer.mergeAll(serveConfigLayer, tailscaleUpLayer, serveProbeLayer(probeStatus)), | 187 | Layer.mergeAll(layerServeConfig, layerTailscaleUp, layerServeProbe(probeStatus)), |
| 188 | ), | 188 | ), |
| 189 | ), | 189 | ), |
| 190 | ), | 190 | ), |
apps/server/src/environment/RemoteOpenTargets.test.ts+3-3| ... | @@ -17,7 +17,7 @@ const TAILSCALE_STATUS_JSON = JSON.stringify({ | ... | @@ -17,7 +17,7 @@ const TAILSCALE_STATUS_JSON = JSON.stringify({ |
| 17 | }); | 17 | }); |
| 18 | 18 | ||
| 19 | /** Spawner whose `tailscale status --json` exits with the given output. */ | 19 | /** Spawner whose `tailscale status --json` exits with the given output. */ |
| 20 | const spawnerLayer = (input: { readonly exitCode: number; readonly stdout: string }) => | 20 | const layerSpawner = (input: { readonly exitCode: number; readonly stdout: string }) => |
| 21 | Layer.succeed( | 21 | Layer.succeed( |
| 22 | ChildProcessSpawner.ChildProcessSpawner, | 22 | ChildProcessSpawner.ChildProcessSpawner, |
| 23 | ChildProcessSpawner.make(() => | 23 | ChildProcessSpawner.make(() => |
| ... | @@ -39,7 +39,7 @@ const spawnerLayer = (input: { readonly exitCode: number; readonly stdout: strin | ... | @@ -39,7 +39,7 @@ const spawnerLayer = (input: { readonly exitCode: number; readonly stdout: strin |
| 39 | ), | 39 | ), |
| 40 | ); | 40 | ); |
| 41 | 41 | ||
| 42 | const netLayer = (input: { readonly ipv4: boolean; readonly ipv6: boolean }) => | 42 | const layerNet = (input: { readonly ipv4: boolean; readonly ipv6: boolean }) => |
| 43 | Layer.succeed(NetService.NetService, { | 43 | Layer.succeed(NetService.NetService, { |
| 44 | canListenOnHost: () => Effect.succeed(true), | 44 | canListenOnHost: () => Effect.succeed(true), |
| 45 | isPortAvailableOnLoopback: () => Effect.succeed(true), | 45 | isPortAvailableOnLoopback: () => Effect.succeed(true), |
| ... | @@ -57,7 +57,7 @@ const resolveTargets = (input: { | ... | @@ -57,7 +57,7 @@ const resolveTargets = (input: { |
| 57 | Effect.provideService(HostProcessHostname, input.hostname), | 57 | Effect.provideService(HostProcessHostname, input.hostname), |
| 58 | Effect.provide( | 58 | Effect.provide( |
| 59 | RemoteOpenTargets.layer.pipe( | 59 | RemoteOpenTargets.layer.pipe( |
| 60 | Layer.provide(Layer.mergeAll(netLayer(input.sshd), spawnerLayer(input.tailscale))), | 60 | Layer.provide(Layer.mergeAll(layerNet(input.sshd), layerSpawner(input.tailscale))), |
| 61 | ), | 61 | ), |
| 62 | ), | 62 | ), |
| 63 | ); | 63 | ); |
apps/server/src/environment/ServerEnvironment.test.ts+11-11| ... | @@ -31,13 +31,13 @@ const isServerEnvironmentIdPersistenceError = Schema.is( | ... | @@ -31,13 +31,13 @@ const isServerEnvironmentIdPersistenceError = Schema.is( |
| 31 | ServerEnvironment.ServerEnvironmentIdPersistenceError, | 31 | ServerEnvironment.ServerEnvironmentIdPersistenceError, |
| 32 | ); | 32 | ); |
| 33 | 33 | ||
| 34 | const makeServerEnvironmentLayer = (baseDir: string) => | 34 | const layerServerEnvironment = (baseDir: string) => |
| 35 | ServerEnvironment.layer.pipe( | 35 | ServerEnvironment.layer.pipe( |
| 36 | Layer.provide(ServerSecretStore.layer), | 36 | Layer.provide(ServerSecretStore.layer), |
| 37 | Layer.provide(ServerConfig.layerTest(process.cwd(), baseDir)), | 37 | Layer.provide(ServerConfig.layerTest(process.cwd(), baseDir)), |
| 38 | ); | 38 | ); |
| 39 | 39 | ||
| 40 | const emptySecretStoreLayer = Layer.succeed( | 40 | const layerEmptySecretStore = Layer.succeed( |
| 41 | ServerSecretStore.ServerSecretStore, | 41 | ServerSecretStore.ServerSecretStore, |
| 42 | ServerSecretStore.ServerSecretStore.of({ | 42 | ServerSecretStore.ServerSecretStore.of({ |
| 43 | get: () => Effect.succeedNone, | 43 | get: () => Effect.succeedNone, |
| ... | @@ -108,7 +108,7 @@ it.layer(NodeServices.layer)("ServerEnvironmentLive", (it) => { | ... | @@ -108,7 +108,7 @@ it.layer(NodeServices.layer)("ServerEnvironmentLive", (it) => { |
| 108 | }).pipe( | 108 | }).pipe( |
| 109 | Effect.provide( | 109 | Effect.provide( |
| 110 | ServerEnvironment.layer.pipe( | 110 | ServerEnvironment.layer.pipe( |
| 111 | Layer.provide(emptySecretStoreLayer), | 111 | Layer.provide(layerEmptySecretStore), |
| 112 | Layer.provide(ServerConfig.layer({ ...config, mode })), | 112 | Layer.provide(ServerConfig.layer({ ...config, mode })), |
| 113 | ), | 113 | ), |
| 114 | ), | 114 | ), |
| ... | @@ -152,7 +152,7 @@ it.layer(NodeServices.layer)("ServerEnvironmentLive", (it) => { | ... | @@ -152,7 +152,7 @@ it.layer(NodeServices.layer)("ServerEnvironmentLive", (it) => { |
| 152 | return yield* identity.getEnvironmentId; | 152 | return yield* identity.getEnvironmentId; |
| 153 | }).pipe( | 153 | }).pipe( |
| 154 | Effect.tap(() => Deferred.succeed(firstInitialized, undefined)), | 154 | Effect.tap(() => Deferred.succeed(firstInitialized, undefined)), |
| 155 | Effect.provide(Layer.fresh(ServerEnvironment.identityLayer)), | 155 | Effect.provide(Layer.fresh(ServerEnvironment.layerIdentity)), |
| 156 | Effect.provideService(ServerConfig.ServerConfig, serverConfig), | 156 | Effect.provideService(ServerConfig.ServerConfig, serverConfig), |
| 157 | Effect.provideService(FileSystem.FileSystem, { | 157 | Effect.provideService(FileSystem.FileSystem, { |
| 158 | ...fileSystem, | 158 | ...fileSystem, |
| ... | @@ -207,11 +207,11 @@ it.layer(NodeServices.layer)("ServerEnvironmentLive", (it) => { | ... | @@ -207,11 +207,11 @@ it.layer(NodeServices.layer)("ServerEnvironmentLive", (it) => { |
| 207 | const first = yield* Effect.gen(function* () { | 207 | const first = yield* Effect.gen(function* () { |
| 208 | const serverEnvironment = yield* ServerEnvironment.ServerEnvironment; | 208 | const serverEnvironment = yield* ServerEnvironment.ServerEnvironment; |
| 209 | return yield* serverEnvironment.getDescriptor; | 209 | return yield* serverEnvironment.getDescriptor; |
| 210 | }).pipe(Effect.provide(makeServerEnvironmentLayer(baseDir))); | 210 | }).pipe(Effect.provide(layerServerEnvironment(baseDir))); |
| 211 | const second = yield* Effect.gen(function* () { | 211 | const second = yield* Effect.gen(function* () { |
| 212 | const serverEnvironment = yield* ServerEnvironment.ServerEnvironment; | 212 | const serverEnvironment = yield* ServerEnvironment.ServerEnvironment; |
| 213 | return yield* serverEnvironment.getDescriptor; | 213 | return yield* serverEnvironment.getDescriptor; |
| 214 | }).pipe(Effect.provide(makeServerEnvironmentLayer(baseDir))); | 214 | }).pipe(Effect.provide(layerServerEnvironment(baseDir))); |
| 215 | 215 | ||
| 216 | expect(first.environmentId).toBe(second.environmentId); | 216 | expect(first.environmentId).toBe(second.environmentId); |
| 217 | expect(first.orchestrationProtocolVersion).toBe(ORCHESTRATION_PROTOCOL_VERSION); | 217 | expect(first.orchestrationProtocolVersion).toBe(ORCHESTRATION_PROTOCOL_VERSION); |
| ... | @@ -237,7 +237,7 @@ it.layer(NodeServices.layer)("ServerEnvironmentLive", (it) => { | ... | @@ -237,7 +237,7 @@ it.layer(NodeServices.layer)("ServerEnvironmentLive", (it) => { |
| 237 | const baseDir = yield* fileSystem.makeTempDirectoryScoped({ | 237 | const baseDir = yield* fileSystem.makeTempDirectoryScoped({ |
| 238 | prefix: "t3-server-environment-publish-test-", | 238 | prefix: "t3-server-environment-publish-test-", |
| 239 | }); | 239 | }); |
| 240 | const testLayer = Layer.mergeAll( | 240 | const layerTest = Layer.mergeAll( |
| 241 | ServerEnvironment.layer.pipe(Layer.provide(ServerSecretStore.layer)), | 241 | ServerEnvironment.layer.pipe(Layer.provide(ServerSecretStore.layer)), |
| 242 | ServerSecretStore.layer, | 242 | ServerSecretStore.layer, |
| 243 | ).pipe(Layer.provide(ServerConfig.layerTest(process.cwd(), baseDir))); | 243 | ).pipe(Layer.provide(ServerConfig.layerTest(process.cwd(), baseDir))); |
| ... | @@ -273,7 +273,7 @@ it.layer(NodeServices.layer)("ServerEnvironmentLive", (it) => { | ... | @@ -273,7 +273,7 @@ it.layer(NodeServices.layer)("ServerEnvironmentLive", (it) => { |
| 273 | yield* secrets.set(PUBLISH_AGENT_ACTIVITY_SECRET, encode("false")); | 273 | yield* secrets.set(PUBLISH_AGENT_ACTIVITY_SECRET, encode("false")); |
| 274 | const disabled = yield* serverEnvironment.getDescriptor; | 274 | const disabled = yield* serverEnvironment.getDescriptor; |
| 275 | expect(disabled.capabilities.agentActivityPublishing).toBe(false); | 275 | expect(disabled.capabilities.agentActivityPublishing).toBe(false); |
| 276 | }).pipe(Effect.provide(testLayer)); | 276 | }).pipe(Effect.provide(layerTest)); |
| 277 | }), | 277 | }), |
| 278 | ); | 278 | ); |
| 279 | 279 | ||
| ... | @@ -342,7 +342,7 @@ it.layer(NodeServices.layer)("ServerEnvironmentLive", (it) => { | ... | @@ -342,7 +342,7 @@ it.layer(NodeServices.layer)("ServerEnvironmentLive", (it) => { |
| 342 | description: "permission denied", | 342 | description: "permission denied", |
| 343 | pathOrDescriptor: environmentIdPath, | 343 | pathOrDescriptor: environmentIdPath, |
| 344 | }); | 344 | }); |
| 345 | const failingFileSystemLayer = FileSystem.layerNoop({ | 345 | const layerFailingFileSystem = FileSystem.layerNoop({ |
| 346 | exists: () => | 346 | exists: () => |
| 347 | operation === "check" ? Effect.fail(cause) : Effect.succeed(operation === "read"), | 347 | operation === "check" ? Effect.fail(cause) : Effect.succeed(operation === "read"), |
| 348 | readFileString: () => Effect.fail(cause), | 348 | readFileString: () => Effect.fail(cause), |
| ... | @@ -359,8 +359,8 @@ it.layer(NodeServices.layer)("ServerEnvironmentLive", (it) => { | ... | @@ -359,8 +359,8 @@ it.layer(NodeServices.layer)("ServerEnvironmentLive", (it) => { |
| 359 | }).pipe( | 359 | }).pipe( |
| 360 | Effect.provide( | 360 | Effect.provide( |
| 361 | ServerEnvironment.layer.pipe( | 361 | ServerEnvironment.layer.pipe( |
| 362 | Layer.provide(emptySecretStoreLayer), | 362 | Layer.provide(layerEmptySecretStore), |
| 363 | Layer.provide(Layer.merge(ServerConfig.layer(serverConfig), failingFileSystemLayer)), | 363 | Layer.provide(Layer.merge(ServerConfig.layer(serverConfig), layerFailingFileSystem)), |
| 364 | ), | 364 | ), |
| 365 | ), | 365 | ), |
| 366 | Effect.flip, | 366 | Effect.flip, |
apps/server/src/environment/ServerEnvironment.ts+2-2| ... | @@ -275,7 +275,7 @@ export const make = Effect.gen(function* () { | ... | @@ -275,7 +275,7 @@ export const make = Effect.gen(function* () { |
| 275 | }); | 275 | }); |
| 276 | }); | 276 | }); |
| 277 | 277 | ||
| 278 | export const identityLayer = Layer.effect(ServerEnvironmentIdentity, makeIdentity); | 278 | export const layerIdentity = Layer.effect(ServerEnvironmentIdentity, makeIdentity); |
| 279 | 279 | ||
| 280 | /** | 280 | /** |
| 281 | * ServerEnvironment is acquired from persisted filesystem and host-process | 281 | * ServerEnvironment is acquired from persisted filesystem and host-process |
| ... | @@ -284,6 +284,6 @@ export const identityLayer = Layer.effect(ServerEnvironmentIdentity, makeIdentit | ... | @@ -284,6 +284,6 @@ export const identityLayer = Layer.effect(ServerEnvironmentIdentity, makeIdentit |
| 284 | * ServerSecretStore backing the descriptor's publishing capability. | 284 | * ServerSecretStore backing the descriptor's publishing capability. |
| 285 | */ | 285 | */ |
| 286 | export const layer = Layer.effect(ServerEnvironment, make).pipe( | 286 | export const layer = Layer.effect(ServerEnvironment, make).pipe( |
| 287 | Layer.provideMerge(identityLayer), | 287 | Layer.provideMerge(layerIdentity), |
| 288 | Layer.provide(ProcessRunner.layer), | 288 | Layer.provide(ProcessRunner.layer), |
| 289 | ); | 289 | ); |
apps/server/src/environment/ServerEnvironmentLabel.test.ts+18-14| ... | @@ -27,16 +27,16 @@ interface CapturedLog { | ... | @@ -27,16 +27,16 @@ interface CapturedLog { |
| 27 | 27 | ||
| 28 | const runMock = vi.fn<ProcessRunner.ProcessRunner["Service"]["run"]>(); | 28 | const runMock = vi.fn<ProcessRunner.ProcessRunner["Service"]["run"]>(); |
| 29 | 29 | ||
| 30 | const ProcessRunnerTest = Layer.succeed( | 30 | const layerProcessRunnerTest = Layer.succeed( |
| 31 | ProcessRunner.ProcessRunner, | 31 | ProcessRunner.ProcessRunner, |
| 32 | ProcessRunner.ProcessRunner.of({ | 32 | ProcessRunner.ProcessRunner.of({ |
| 33 | run: (input) => runMock(input), | 33 | run: (input) => runMock(input), |
| 34 | }), | 34 | }), |
| 35 | ); | 35 | ); |
| 36 | const NoopFileSystemLayer = FileSystem.layerNoop({}); | 36 | const layerNoopFileSystem = FileSystem.layerNoop({}); |
| 37 | const TestLayer = Layer.merge(NoopFileSystemLayer, ProcessRunnerTest); | 37 | const layerTest = Layer.merge(layerNoopFileSystem, layerProcessRunnerTest); |
| 38 | const LinuxMachineInfoLayer = Layer.merge( | 38 | const layerLinuxMachineInfo = Layer.merge( |
| 39 | ProcessRunnerTest, | 39 | layerProcessRunnerTest, |
| 40 | FileSystem.layerNoop({ | 40 | FileSystem.layerNoop({ |
| 41 | exists: (path) => Effect.succeed(path === "/etc/machine-info"), | 41 | exists: (path) => Effect.succeed(path === "/etc/machine-info"), |
| 42 | readFileString: (path) => | 42 | readFileString: (path) => |
| ... | @@ -65,7 +65,7 @@ describe("resolveServerEnvironmentLabel", () => { | ... | @@ -65,7 +65,7 @@ describe("resolveServerEnvironmentLabel", () => { |
| 65 | Effect.gen(function* () { | 65 | Effect.gen(function* () { |
| 66 | const result = yield* ServerEnvironmentLabel.resolveServerEnvironmentLabel({ | 66 | const result = yield* ServerEnvironmentLabel.resolveServerEnvironmentLabel({ |
| 67 | cwdBaseName: "t3code", | 67 | cwdBaseName: "t3code", |
| 68 | }).pipe(Effect.provide(withHostPlatform(TestLayer, "win32", "macbook-pro"))); | 68 | }).pipe(Effect.provide(withHostPlatform(layerTest, "win32", "macbook-pro"))); |
| 69 | 69 | ||
| 70 | expect(result).toBe("macbook-pro"); | 70 | expect(result).toBe("macbook-pro"); |
| 71 | }), | 71 | }), |
| ... | @@ -88,7 +88,7 @@ describe("resolveServerEnvironmentLabel", () => { | ... | @@ -88,7 +88,7 @@ describe("resolveServerEnvironmentLabel", () => { |
| 88 | 88 | ||
| 89 | const result = yield* ServerEnvironmentLabel.resolveServerEnvironmentLabel({ | 89 | const result = yield* ServerEnvironmentLabel.resolveServerEnvironmentLabel({ |
| 90 | cwdBaseName: "t3code", | 90 | cwdBaseName: "t3code", |
| 91 | }).pipe(Effect.provide(withHostPlatform(TestLayer, "darwin", "macbook-pro"))); | 91 | }).pipe(Effect.provide(withHostPlatform(layerTest, "darwin", "macbook-pro"))); |
| 92 | 92 | ||
| 93 | expect(result).toBe("Julius's MacBook Pro"); | 93 | expect(result).toBe("Julius's MacBook Pro"); |
| 94 | expect(runMock).toHaveBeenCalledWith( | 94 | expect(runMock).toHaveBeenCalledWith( |
| ... | @@ -105,7 +105,7 @@ describe("resolveServerEnvironmentLabel", () => { | ... | @@ -105,7 +105,7 @@ describe("resolveServerEnvironmentLabel", () => { |
| 105 | Effect.gen(function* () { | 105 | Effect.gen(function* () { |
| 106 | const result = yield* ServerEnvironmentLabel.resolveServerEnvironmentLabel({ | 106 | const result = yield* ServerEnvironmentLabel.resolveServerEnvironmentLabel({ |
| 107 | cwdBaseName: "t3code", | 107 | cwdBaseName: "t3code", |
| 108 | }).pipe(Effect.provide(withHostPlatform(LinuxMachineInfoLayer, "linux", "buildbox"))); | 108 | }).pipe(Effect.provide(withHostPlatform(layerLinuxMachineInfo, "linux", "buildbox"))); |
| 109 | 109 | ||
| 110 | expect(result).toBe("Build Agent 01"); | 110 | expect(result).toBe("Build Agent 01"); |
| 111 | expect(runMock).not.toHaveBeenCalled(); | 111 | expect(runMock).not.toHaveBeenCalled(); |
| ... | @@ -129,7 +129,7 @@ describe("resolveServerEnvironmentLabel", () => { | ... | @@ -129,7 +129,7 @@ describe("resolveServerEnvironmentLabel", () => { |
| 129 | 129 | ||
| 130 | const result = yield* ServerEnvironmentLabel.resolveServerEnvironmentLabel({ | 130 | const result = yield* ServerEnvironmentLabel.resolveServerEnvironmentLabel({ |
| 131 | cwdBaseName: "t3code", | 131 | cwdBaseName: "t3code", |
| 132 | }).pipe(Effect.provide(withHostPlatform(TestLayer, "linux", "runner-01"))); | 132 | }).pipe(Effect.provide(withHostPlatform(layerTest, "linux", "runner-01"))); |
| 133 | 133 | ||
| 134 | expect(result).toBe("CI Runner"); | 134 | expect(result).toBe("CI Runner"); |
| 135 | expect(runMock).toHaveBeenCalledWith( | 135 | expect(runMock).toHaveBeenCalledWith( |
| ... | @@ -146,7 +146,7 @@ describe("resolveServerEnvironmentLabel", () => { | ... | @@ -146,7 +146,7 @@ describe("resolveServerEnvironmentLabel", () => { |
| 146 | Effect.gen(function* () { | 146 | Effect.gen(function* () { |
| 147 | const result = yield* ServerEnvironmentLabel.resolveServerEnvironmentLabel({ | 147 | const result = yield* ServerEnvironmentLabel.resolveServerEnvironmentLabel({ |
| 148 | cwdBaseName: "t3code", | 148 | cwdBaseName: "t3code", |
| 149 | }).pipe(Effect.provide(withHostPlatform(TestLayer, "win32", "JULIUS-LAPTOP"))); | 149 | }).pipe(Effect.provide(withHostPlatform(layerTest, "win32", "JULIUS-LAPTOP"))); |
| 150 | 150 | ||
| 151 | expect(result).toBe("JULIUS-LAPTOP"); | 151 | expect(result).toBe("JULIUS-LAPTOP"); |
| 152 | }), | 152 | }), |
| ... | @@ -192,7 +192,7 @@ describe("resolveServerEnvironmentLabel", () => { | ... | @@ -192,7 +192,7 @@ describe("resolveServerEnvironmentLabel", () => { |
| 192 | }).pipe( | 192 | }).pipe( |
| 193 | Effect.provide( | 193 | Effect.provide( |
| 194 | Layer.mergeAll( | 194 | Layer.mergeAll( |
| 195 | withHostPlatform(TestLayer, "darwin", "macbook-pro"), | 195 | withHostPlatform(layerTest, "darwin", "macbook-pro"), |
| 196 | Logger.layer([logger], { mergeWithExisting: false }), | 196 | Logger.layer([logger], { mergeWithExisting: false }), |
| 197 | Layer.succeed(References.MinimumLogLevel, "Debug"), | 197 | Layer.succeed(References.MinimumLogLevel, "Debug"), |
| 198 | ), | 198 | ), |
| ... | @@ -216,7 +216,7 @@ describe("resolveServerEnvironmentLabel", () => { | ... | @@ -216,7 +216,7 @@ describe("resolveServerEnvironmentLabel", () => { |
| 216 | pathOrDescriptor: "/etc/machine-info", | 216 | pathOrDescriptor: "/etc/machine-info", |
| 217 | cause: fileCause, | 217 | cause: fileCause, |
| 218 | }); | 218 | }); |
| 219 | const fileSystemLayer = FileSystem.layerNoop({ | 219 | const layerFileSystem = FileSystem.layerNoop({ |
| 220 | exists: () => Effect.fail(platformError), | 220 | exists: () => Effect.fail(platformError), |
| 221 | }); | 221 | }); |
| 222 | runMock.mockReturnValueOnce( | 222 | runMock.mockReturnValueOnce( |
| ... | @@ -252,7 +252,11 @@ describe("resolveServerEnvironmentLabel", () => { | ... | @@ -252,7 +252,11 @@ describe("resolveServerEnvironmentLabel", () => { |
| 252 | }).pipe( | 252 | }).pipe( |
| 253 | Effect.provide( | 253 | Effect.provide( |
| 254 | Layer.mergeAll( | 254 | Layer.mergeAll( |
| 255 | withHostPlatform(Layer.merge(ProcessRunnerTest, fileSystemLayer), "linux", "buildbox"), | 255 | withHostPlatform( |
| 256 | Layer.merge(layerProcessRunnerTest, layerFileSystem), | ||
| 257 | "linux", | ||
| 258 | "buildbox", | ||
| 259 | ), | ||
| 256 | Logger.layer([logger], { mergeWithExisting: false }), | 260 | Logger.layer([logger], { mergeWithExisting: false }), |
| 257 | Layer.succeed(References.MinimumLogLevel, "Debug"), | 261 | Layer.succeed(References.MinimumLogLevel, "Debug"), |
| 258 | ), | 262 | ), |
| ... | @@ -277,7 +281,7 @@ describe("resolveServerEnvironmentLabel", () => { | ... | @@ -277,7 +281,7 @@ describe("resolveServerEnvironmentLabel", () => { |
| 277 | 281 | ||
| 278 | const result = yield* ServerEnvironmentLabel.resolveServerEnvironmentLabel({ | 282 | const result = yield* ServerEnvironmentLabel.resolveServerEnvironmentLabel({ |
| 279 | cwdBaseName: "t3code", | 283 | cwdBaseName: "t3code", |
| 280 | }).pipe(Effect.provide(withHostPlatform(TestLayer, "linux", " "))); | 284 | }).pipe(Effect.provide(withHostPlatform(layerTest, "linux", " "))); |
| 281 | 285 | ||
| 282 | expect(result).toBe("t3code"); | 286 | expect(result).toBe("t3code"); |
| 283 | }), | 287 | }), |
apps/server/src/environment/ServerEnvironmentMachine.test.ts+6-6| ... | @@ -16,7 +16,7 @@ import { | ... | @@ -16,7 +16,7 @@ import { |
| 16 | 16 | ||
| 17 | const runMock = vi.fn<ProcessRunner.ProcessRunner["Service"]["run"]>(); | 17 | const runMock = vi.fn<ProcessRunner.ProcessRunner["Service"]["run"]>(); |
| 18 | 18 | ||
| 19 | const ProcessRunnerTest = Layer.succeed( | 19 | const layerProcessRunnerTest = Layer.succeed( |
| 20 | ProcessRunner.ProcessRunner, | 20 | ProcessRunner.ProcessRunner, |
| 21 | ProcessRunner.ProcessRunner.of({ run: (input) => runMock(input) }), | 21 | ProcessRunner.ProcessRunner.of({ run: (input) => runMock(input) }), |
| 22 | ); | 22 | ); |
| ... | @@ -33,7 +33,7 @@ const processOutput = (stdout: string, code = 0) => | ... | @@ -33,7 +33,7 @@ const processOutput = (stdout: string, code = 0) => |
| 33 | stderrInvalidUtf8: false, | 33 | stderrInvalidUtf8: false, |
| 34 | }); | 34 | }); |
| 35 | 35 | ||
| 36 | const dmiFileSystem = (files: Readonly<Record<string, string>>) => | 36 | const layerDmiFileSystem = (files: Readonly<Record<string, string>>) => |
| 37 | FileSystem.layerNoop({ | 37 | FileSystem.layerNoop({ |
| 38 | readFileString: (path) => { | 38 | readFileString: (path) => { |
| 39 | const name = path.slice(path.lastIndexOf("/") + 1); | 39 | const name = path.slice(path.lastIndexOf("/") + 1); |
| ... | @@ -52,7 +52,7 @@ const dmiFileSystem = (files: Readonly<Record<string, string>>) => | ... | @@ -52,7 +52,7 @@ const dmiFileSystem = (files: Readonly<Record<string, string>>) => |
| 52 | }); | 52 | }); |
| 53 | 53 | ||
| 54 | const withPlatform = (platform: NodeJS.Platform, fileSystem = FileSystem.layerNoop({})) => | 54 | const withPlatform = (platform: NodeJS.Platform, fileSystem = FileSystem.layerNoop({})) => |
| 55 | Layer.mergeAll(ProcessRunnerTest, fileSystem, Layer.succeed(HostProcessPlatform, platform)); | 55 | Layer.mergeAll(layerProcessRunnerTest, fileSystem, Layer.succeed(HostProcessPlatform, platform)); |
| 56 | 56 | ||
| 57 | afterEach(() => { | 57 | afterEach(() => { |
| 58 | runMock.mockReset(); | 58 | runMock.mockReset(); |
| ... | @@ -188,7 +188,7 @@ describe("detectServerEnvironmentMachineKind", () => { | ... | @@ -188,7 +188,7 @@ describe("detectServerEnvironmentMachineKind", () => { |
| 188 | Effect.provide( | 188 | Effect.provide( |
| 189 | withPlatform( | 189 | withPlatform( |
| 190 | "linux", | 190 | "linux", |
| 191 | dmiFileSystem({ | 191 | layerDmiFileSystem({ |
| 192 | chassis_type: "3\n", | 192 | chassis_type: "3\n", |
| 193 | sys_vendor: "GMKtec\n", | 193 | sys_vendor: "GMKtec\n", |
| 194 | product_name: "NucBox K8 Plus\n", | 194 | product_name: "NucBox K8 Plus\n", |
| ... | @@ -208,7 +208,7 @@ describe("detectServerEnvironmentMachineKind", () => { | ... | @@ -208,7 +208,7 @@ describe("detectServerEnvironmentMachineKind", () => { |
| 208 | Effect.provide( | 208 | Effect.provide( |
| 209 | withPlatform( | 209 | withPlatform( |
| 210 | "linux", | 210 | "linux", |
| 211 | dmiFileSystem({ | 211 | layerDmiFileSystem({ |
| 212 | osrelease: "5.15.153.1-microsoft-standard-WSL2\n", | 212 | osrelease: "5.15.153.1-microsoft-standard-WSL2\n", |
| 213 | chassis_type: "3\n", | 213 | chassis_type: "3\n", |
| 214 | sys_vendor: "Microsoft Corporation\n", | 214 | sys_vendor: "Microsoft Corporation\n", |
| ... | @@ -225,7 +225,7 @@ describe("detectServerEnvironmentMachineKind", () => { | ... | @@ -225,7 +225,7 @@ describe("detectServerEnvironmentMachineKind", () => { |
| 225 | it.effect("returns null on Linux without DMI (containers, ARM boards)", () => | 225 | it.effect("returns null on Linux without DMI (containers, ARM boards)", () => |
| 226 | Effect.gen(function* () { | 226 | Effect.gen(function* () { |
| 227 | const result = yield* detectServerEnvironmentMachineKind().pipe( | 227 | const result = yield* detectServerEnvironmentMachineKind().pipe( |
| 228 | Effect.provide(withPlatform("linux", dmiFileSystem({}))), | 228 | Effect.provide(withPlatform("linux", layerDmiFileSystem({}))), |
| 229 | ); | 229 | ); |
| 230 | 230 | ||
| 231 | expect(result).toBeNull(); | 231 | expect(result).toBeNull(); |
apps/server/src/git/GitManager.test.ts+17-17| ... | @@ -53,7 +53,7 @@ import * as SourceControlProviderRegistry from "../sourceControl/SourceControlPr | ... | @@ -53,7 +53,7 @@ import * as SourceControlProviderRegistry from "../sourceControl/SourceControlPr |
| 53 | import * as ServerConfig from "../config.ts"; | 53 | import * as ServerConfig from "../config.ts"; |
| 54 | import * as ProjectionStore from "../orchestration-v2/ProjectionStore.ts"; | 54 | import * as ProjectionStore from "../orchestration-v2/ProjectionStore.ts"; |
| 55 | import * as ProjectStore from "../orchestration-v2/ProjectStore.ts"; | 55 | import * as ProjectStore from "../orchestration-v2/ProjectStore.ts"; |
| 56 | import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; | 56 | import * as SqlitePersistence from "../persistence/Layers/Sqlite.ts"; |
| 57 | import * as ProjectSetupScriptRunner from "../project/ProjectSetupScriptRunner.ts"; | 57 | import * as ProjectSetupScriptRunner from "../project/ProjectSetupScriptRunner.ts"; |
| 58 | import * as ProviderRegistry from "../provider/Services/ProviderRegistry.ts"; | 58 | import * as ProviderRegistry from "../provider/Services/ProviderRegistry.ts"; |
| 59 | import * as ServerSettings from "../serverSettings.ts"; | 59 | import * as ServerSettings from "../serverSettings.ts"; |
| ... | @@ -679,13 +679,13 @@ function makeManager(input?: { | ... | @@ -679,13 +679,13 @@ function makeManager(input?: { |
| 679 | }) { | 679 | }) { |
| 680 | const { service: gitHubCli, ghCalls } = createGitHubCliWithFakeGh(input?.ghScenario); | 680 | const { service: gitHubCli, ghCalls } = createGitHubCliWithFakeGh(input?.ghScenario); |
| 681 | const textGeneration = createTextGeneration(input?.textGeneration); | 681 | const textGeneration = createTextGeneration(input?.textGeneration); |
| 682 | const serverConfigLayer = ServerConfig.layerTest(process.cwd(), { | 682 | const layerServerConfig = ServerConfig.layerTest(process.cwd(), { |
| 683 | prefix: "t3-git-manager-test-", | 683 | prefix: "t3-git-manager-test-", |
| 684 | }); | 684 | }); |
| 685 | 685 | ||
| 686 | const serverSettingsLayer = ServerSettings.ServerSettingsService.layerTest(input?.serverSettings); | 686 | const layerServerSettings = ServerSettings.ServerSettingsService.layerTest(input?.serverSettings); |
| 687 | 687 | ||
| 688 | const vcsDriverLayer = input?.gitConfigReads | 688 | const layerVcsDriver = input?.gitConfigReads |
| 689 | ? Layer.effect( | 689 | ? Layer.effect( |
| 690 | GitVcsDriver.GitVcsDriver, | 690 | GitVcsDriver.GitVcsDriver, |
| 691 | GitVcsDriver.make.pipe( | 691 | GitVcsDriver.make.pipe( |
| ... | @@ -702,14 +702,14 @@ function makeManager(input?: { | ... | @@ -702,14 +702,14 @@ function makeManager(input?: { |
| 702 | ).pipe( | 702 | ).pipe( |
| 703 | Layer.provideMerge(VcsProcess.layer), | 703 | Layer.provideMerge(VcsProcess.layer), |
| 704 | Layer.provideMerge(NodeServices.layer), | 704 | Layer.provideMerge(NodeServices.layer), |
| 705 | Layer.provideMerge(serverConfigLayer), | 705 | Layer.provideMerge(layerServerConfig), |
| 706 | ) | 706 | ) |
| 707 | : GitVcsDriver.layer.pipe( | 707 | : GitVcsDriver.layer.pipe( |
| 708 | Layer.provideMerge(VcsProcess.layer), | 708 | Layer.provideMerge(VcsProcess.layer), |
| 709 | Layer.provideMerge(NodeServices.layer), | 709 | Layer.provideMerge(NodeServices.layer), |
| 710 | Layer.provideMerge(serverConfigLayer), | 710 | Layer.provideMerge(layerServerConfig), |
| 711 | ); | 711 | ); |
| 712 | const sourceControlRegistryLayer = Layer.effect( | 712 | const layerSourceControlRegistry = Layer.effect( |
| 713 | SourceControlProviderRegistry.SourceControlProviderRegistry, | 713 | SourceControlProviderRegistry.SourceControlProviderRegistry, |
| 714 | (input?.sourceControlProvider === undefined | 714 | (input?.sourceControlProvider === undefined |
| 715 | ? GitHubSourceControlProvider.make | 715 | ? GitHubSourceControlProvider.make |
| ... | @@ -728,7 +728,7 @@ function makeManager(input?: { | ... | @@ -728,7 +728,7 @@ function makeManager(input?: { |
| 728 | ), | 728 | ), |
| 729 | ); | 729 | ); |
| 730 | 730 | ||
| 731 | const managerLayer = Layer.mergeAll( | 731 | const layerManager = Layer.mergeAll( |
| 732 | Layer.succeed(TextGeneration.TextGeneration, textGeneration), | 732 | Layer.succeed(TextGeneration.TextGeneration, textGeneration), |
| 733 | Layer.mock(ProviderRegistry.ProviderRegistry)({ | 733 | Layer.mock(ProviderRegistry.ProviderRegistry)({ |
| 734 | getProviders: Effect.succeed([]), | 734 | getProviders: Effect.succeed([]), |
| ... | @@ -739,21 +739,21 @@ function makeManager(input?: { | ... | @@ -739,21 +739,21 @@ function makeManager(input?: { |
| 739 | runForThread: () => Effect.succeed({ status: "no-script" as const }), | 739 | runForThread: () => Effect.succeed({ status: "no-script" as const }), |
| 740 | }, | 740 | }, |
| 741 | ), | 741 | ), |
| 742 | vcsDriverLayer, | 742 | layerVcsDriver, |
| 743 | serverSettingsLayer, | 743 | layerServerSettings, |
| 744 | ).pipe(Layer.provideMerge(sourceControlRegistryLayer), Layer.provideMerge(NodeServices.layer)); | 744 | ).pipe(Layer.provideMerge(layerSourceControlRegistry), Layer.provideMerge(NodeServices.layer)); |
| 745 | // Built into the test's scope: the manager reads these stores after this returns. | 745 | // Built into the test's scope: the manager reads these stores after this returns. |
| 746 | const storesLayer = Layer.merge(ProjectionStore.layer, ProjectStore.layer).pipe( | 746 | const layerStores = Layer.merge(ProjectionStore.layer, ProjectStore.layer).pipe( |
| 747 | Layer.provideMerge(SqlitePersistenceMemory), | 747 | Layer.provideMerge(SqlitePersistence.layerMemory), |
| 748 | ); | 748 | ); |
| 749 | 749 | ||
| 750 | return Effect.gen(function* () { | 750 | return Effect.gen(function* () { |
| 751 | const stores = yield* Layer.build(storesLayer); | 751 | const stores = yield* Layer.build(layerStores); |
| 752 | if (input?.seed !== undefined) { | 752 | if (input?.seed !== undefined) { |
| 753 | yield* input.seed.pipe(Effect.provideContext(stores), Effect.orDie); | 753 | yield* input.seed.pipe(Effect.provideContext(stores), Effect.orDie); |
| 754 | } | 754 | } |
| 755 | const manager = yield* GitManager.make.pipe( | 755 | const manager = yield* GitManager.make.pipe( |
| 756 | Effect.provide(managerLayer), | 756 | Effect.provide(layerManager), |
| 757 | Effect.provideContext(stores), | 757 | Effect.provideContext(stores), |
| 758 | ); | 758 | ); |
| 759 | return { manager, ghCalls }; | 759 | return { manager, ghCalls }; |
| ... | @@ -762,13 +762,13 @@ function makeManager(input?: { | ... | @@ -762,13 +762,13 @@ function makeManager(input?: { |
| 762 | 762 | ||
| 763 | const asThreadId = (threadId: string) => threadId as ThreadId; | 763 | const asThreadId = (threadId: string) => threadId as ThreadId; |
| 764 | 764 | ||
| 765 | const GitManagerTestLayer = GitVcsDriver.layer.pipe( | 765 | const layerGitManagerTest = GitVcsDriver.layer.pipe( |
| 766 | Layer.provide(ServerConfig.layerTest(process.cwd(), { prefix: "t3-git-manager-test-" })), | 766 | Layer.provide(ServerConfig.layerTest(process.cwd(), { prefix: "t3-git-manager-test-" })), |
| 767 | Layer.provideMerge(VcsProcess.layer), | 767 | Layer.provideMerge(VcsProcess.layer), |
| 768 | Layer.provideMerge(NodeServices.layer), | 768 | Layer.provideMerge(NodeServices.layer), |
| 769 | ); | 769 | ); |
| 770 | 770 | ||
| 771 | it.layer(GitManagerTestLayer)("GitManager", (it) => { | 771 | it.layer(layerGitManagerTest)("GitManager", (it) => { |
| 772 | it.effect("status includes draft PR metadata when branch already has a draft PR", () => | 772 | it.effect("status includes draft PR metadata when branch already has a draft PR", () => |
| 773 | Effect.gen(function* () { | 773 | Effect.gen(function* () { |
| 774 | const repoDir = yield* makeTempDir("t3code-git-manager-"); | 774 | const repoDir = yield* makeTempDir("t3code-git-manager-"); |
apps/server/src/git/GitWorkflowService.test.ts+9-9| ... | @@ -11,7 +11,7 @@ import * as GitWorkflowService from "./GitWorkflowService.ts"; | ... | @@ -11,7 +11,7 @@ import * as GitWorkflowService from "./GitWorkflowService.ts"; |
| 11 | import * as GitVcsDriver from "../vcs/GitVcsDriver.ts"; | 11 | import * as GitVcsDriver from "../vcs/GitVcsDriver.ts"; |
| 12 | import * as VcsDriverRegistry from "../vcs/VcsDriverRegistry.ts"; | 12 | import * as VcsDriverRegistry from "../vcs/VcsDriverRegistry.ts"; |
| 13 | 13 | ||
| 14 | function makeLayer(input: { | 14 | function layer(input: { |
| 15 | readonly detect: VcsDriverRegistry.VcsDriverRegistry["Service"]["detect"]; | 15 | readonly detect: VcsDriverRegistry.VcsDriverRegistry["Service"]["detect"]; |
| 16 | }) { | 16 | }) { |
| 17 | return GitWorkflowService.layer.pipe( | 17 | return GitWorkflowService.layer.pipe( |
| ... | @@ -34,7 +34,7 @@ describe("GitWorkflowService", () => { | ... | @@ -34,7 +34,7 @@ describe("GitWorkflowService", () => { |
| 34 | assert.equal(isRepository, false); | 34 | assert.equal(isRepository, false); |
| 35 | }).pipe( | 35 | }).pipe( |
| 36 | Effect.provide( | 36 | Effect.provide( |
| 37 | makeLayer({ | 37 | layer({ |
| 38 | detect: () => | 38 | detect: () => |
| 39 | Effect.succeed({ | 39 | Effect.succeed({ |
| 40 | kind: "jj", | 40 | kind: "jj", |
| ... | @@ -74,7 +74,7 @@ describe("GitWorkflowService", () => { | ... | @@ -74,7 +74,7 @@ describe("GitWorkflowService", () => { |
| 74 | }); | 74 | }); |
| 75 | }).pipe( | 75 | }).pipe( |
| 76 | Effect.provide( | 76 | Effect.provide( |
| 77 | makeLayer({ | 77 | layer({ |
| 78 | detect: () => Effect.succeed(null), | 78 | detect: () => Effect.succeed(null), |
| 79 | }), | 79 | }), |
| 80 | ), | 80 | ), |
| ... | @@ -105,7 +105,7 @@ describe("GitWorkflowService", () => { | ... | @@ -105,7 +105,7 @@ describe("GitWorkflowService", () => { |
| 105 | }); | 105 | }); |
| 106 | }).pipe( | 106 | }).pipe( |
| 107 | Effect.provide( | 107 | Effect.provide( |
| 108 | makeLayer({ | 108 | layer({ |
| 109 | detect: () => Effect.succeed(null), | 109 | detect: () => Effect.succeed(null), |
| 110 | }), | 110 | }), |
| 111 | ), | 111 | ), |
| ... | @@ -117,7 +117,7 @@ describe("GitWorkflowService", () => { | ... | @@ -117,7 +117,7 @@ describe("GitWorkflowService", () => { |
| 117 | const remoteStatus = vi.fn(); | 117 | const remoteStatus = vi.fn(); |
| 118 | const status = vi.fn(); | 118 | const status = vi.fn(); |
| 119 | 119 | ||
| 120 | const testLayer = GitWorkflowService.layer.pipe( | 120 | const layerTest = GitWorkflowService.layer.pipe( |
| 121 | Layer.provide( | 121 | Layer.provide( |
| 122 | Layer.mock(VcsDriverRegistry.VcsDriverRegistry)({ | 122 | Layer.mock(VcsDriverRegistry.VcsDriverRegistry)({ |
| 123 | detect: () => Effect.succeed(null), | 123 | detect: () => Effect.succeed(null), |
| ... | @@ -142,7 +142,7 @@ describe("GitWorkflowService", () => { | ... | @@ -142,7 +142,7 @@ describe("GitWorkflowService", () => { |
| 142 | assert.equal(localStatus.mock.calls.length, 0); | 142 | assert.equal(localStatus.mock.calls.length, 0); |
| 143 | assert.equal(remoteStatus.mock.calls.length, 0); | 143 | assert.equal(remoteStatus.mock.calls.length, 0); |
| 144 | assert.equal(status.mock.calls.length, 0); | 144 | assert.equal(status.mock.calls.length, 0); |
| 145 | }).pipe(Effect.provide(testLayer)); | 145 | }).pipe(Effect.provide(layerTest)); |
| 146 | }); | 146 | }); |
| 147 | 147 | ||
| 148 | it.effect("returns an empty ref list when no VCS repository is detected", () => | 148 | it.effect("returns an empty ref list when no VCS repository is detected", () => |
| ... | @@ -159,7 +159,7 @@ describe("GitWorkflowService", () => { | ... | @@ -159,7 +159,7 @@ describe("GitWorkflowService", () => { |
| 159 | }); | 159 | }); |
| 160 | }).pipe( | 160 | }).pipe( |
| 161 | Effect.provide( | 161 | Effect.provide( |
| 162 | makeLayer({ | 162 | layer({ |
| 163 | detect: () => Effect.succeed(null), | 163 | detect: () => Effect.succeed(null), |
| 164 | }), | 164 | }), |
| 165 | ), | 165 | ), |
| ... | @@ -186,7 +186,7 @@ describe("GitWorkflowService", () => { | ... | @@ -186,7 +186,7 @@ describe("GitWorkflowService", () => { |
| 186 | expect(error.message).not.toContain(cause.detail); | 186 | expect(error.message).not.toContain(cause.detail); |
| 187 | }).pipe( | 187 | }).pipe( |
| 188 | Effect.provide( | 188 | Effect.provide( |
| 189 | makeLayer({ | 189 | layer({ |
| 190 | detect: () => Effect.fail(cause), | 190 | detect: () => Effect.fail(cause), |
| 191 | }), | 191 | }), |
| 192 | ), | 192 | ), |
| ... | @@ -214,7 +214,7 @@ describe("GitWorkflowService", () => { | ... | @@ -214,7 +214,7 @@ describe("GitWorkflowService", () => { |
| 214 | expect(error.message).not.toContain(cause.detail); | 214 | expect(error.message).not.toContain(cause.detail); |
| 215 | }).pipe( | 215 | }).pipe( |
| 216 | Effect.provide( | 216 | Effect.provide( |
| 217 | makeLayer({ | 217 | layer({ |
| 218 | detect: () => Effect.fail(cause), | 218 | detect: () => Effect.fail(cause), |
| 219 | }), | 219 | }), |
| 220 | ), | 220 | ), |
apps/server/src/git/linkCreatedPullRequest.test.ts+14-14| ... | @@ -71,7 +71,7 @@ function prResult(pr: GitRunStackedActionResult["pr"]): Pick<GitRunStackedAction | ... | @@ -71,7 +71,7 @@ function prResult(pr: GitRunStackedActionResult["pr"]): Pick<GitRunStackedAction |
| 71 | return { pr }; | 71 | return { pr }; |
| 72 | } | 72 | } |
| 73 | 73 | ||
| 74 | const makeDependencies = ( | 74 | const layerDependenciesFor = ( |
| 75 | dispatch: Orchestrator.OrchestratorV2Shape["dispatch"], | 75 | dispatch: Orchestrator.OrchestratorV2Shape["dispatch"], |
| 76 | threadShell: PullRequestTestThread | null = thread, | 76 | threadShell: PullRequestTestThread | null = thread, |
| 77 | ) => | 77 | ) => |
| ... | @@ -159,7 +159,7 @@ describe("linkCreatedPullRequest", () => { | ... | @@ -159,7 +159,7 @@ describe("linkCreatedPullRequest", () => { |
| 159 | url: "https://github.com/t3tools/t3code/pull/42", | 159 | url: "https://github.com/t3tools/t3code/pull/42", |
| 160 | }), | 160 | }), |
| 161 | commandId, | 161 | commandId, |
| 162 | }).pipe(Effect.provide(makeDependencies(dispatch))); | 162 | }).pipe(Effect.provide(layerDependenciesFor(dispatch))); |
| 163 | 163 | ||
| 164 | expect(yield* Ref.get(commands)).toEqual([ | 164 | expect(yield* Ref.get(commands)).toEqual([ |
| 165 | { | 165 | { |
| ... | @@ -179,17 +179,17 @@ describe("linkCreatedPullRequest", () => { | ... | @@ -179,17 +179,17 @@ describe("linkCreatedPullRequest", () => { |
| 179 | it.effect("dispatches nothing when the action produced no pull request", () => | 179 | it.effect("dispatches nothing when the action produced no pull request", () => |
| 180 | Effect.gen(function* () { | 180 | Effect.gen(function* () { |
| 181 | const { commands, dispatch } = yield* recordingDispatch(); | 181 | const { commands, dispatch } = yield* recordingDispatch(); |
| 182 | const dependencies = makeDependencies(dispatch); | 182 | const layerDependencies = layerDependenciesFor(dispatch); |
| 183 | yield* linkCreatedPullRequest({ | 183 | yield* linkCreatedPullRequest({ |
| 184 | threadId: THREAD_ID, | 184 | threadId: THREAD_ID, |
| 185 | result: prResult({ status: "skipped_not_requested" }), | 185 | result: prResult({ status: "skipped_not_requested" }), |
| 186 | commandId, | 186 | commandId, |
| 187 | }).pipe(Effect.provide(dependencies)); | 187 | }).pipe(Effect.provide(layerDependencies)); |
| 188 | yield* linkCreatedPullRequest({ | 188 | yield* linkCreatedPullRequest({ |
| 189 | threadId: THREAD_ID, | 189 | threadId: THREAD_ID, |
| 190 | result: prResult({ status: "created", url: "https://github.com/t3tools/t3code/pull/42" }), | 190 | result: prResult({ status: "created", url: "https://github.com/t3tools/t3code/pull/42" }), |
| 191 | commandId, | 191 | commandId, |
| 192 | }).pipe(Effect.provide(dependencies)); | 192 | }).pipe(Effect.provide(layerDependencies)); |
| 193 | 193 | ||
| 194 | expect(yield* Ref.get(commands)).toEqual([]); | 194 | expect(yield* Ref.get(commands)).toEqual([]); |
| 195 | }), | 195 | }), |
| ... | @@ -211,14 +211,14 @@ describe("linkCreatedPullRequest", () => { | ... | @@ -211,14 +211,14 @@ describe("linkCreatedPullRequest", () => { |
| 211 | url: "https://github.com/t3tools/t3code/pull/7", | 211 | url: "https://github.com/t3tools/t3code/pull/7", |
| 212 | }); | 212 | }); |
| 213 | yield* linkCreatedPullRequest({ threadId: THREAD_ID, result, commandId }).pipe( | 213 | yield* linkCreatedPullRequest({ threadId: THREAD_ID, result, commandId }).pipe( |
| 214 | Effect.provide(makeDependencies(rejecting)), | 214 | Effect.provide(layerDependenciesFor(rejecting)), |
| 215 | ); | 215 | ); |
| 216 | yield* linkCreatedPullRequest({ threadId: THREAD_ID, result, commandId }).pipe( | 216 | yield* linkCreatedPullRequest({ threadId: THREAD_ID, result, commandId }).pipe( |
| 217 | Effect.provide(makeDependencies(() => Effect.die(new Error("engine down")))), | 217 | Effect.provide(layerDependenciesFor(() => Effect.die(new Error("engine down")))), |
| 218 | ); | 218 | ); |
| 219 | // A thread that vanished between the action and the link is not an error either. | 219 | // A thread that vanished between the action and the link is not an error either. |
| 220 | yield* linkCreatedPullRequest({ threadId: THREAD_ID, result, commandId }).pipe( | 220 | yield* linkCreatedPullRequest({ threadId: THREAD_ID, result, commandId }).pipe( |
| 221 | Effect.provide(makeDependencies(() => Effect.die(new Error("unreachable")), null)), | 221 | Effect.provide(layerDependenciesFor(() => Effect.die(new Error("unreachable")), null)), |
| 222 | ); | 222 | ); |
| 223 | }), | 223 | }), |
| 224 | ); | 224 | ); |
| ... | @@ -229,7 +229,7 @@ it.effect( | ... | @@ -229,7 +229,7 @@ it.effect( |
| 229 | () => | 229 | () => |
| 230 | Effect.gen(function* () { | 230 | Effect.gen(function* () { |
| 231 | const refreshed: string[] = []; | 231 | const refreshed: string[] = []; |
| 232 | const dependencies = Layer.mergeAll( | 232 | const layerDependencies = Layer.mergeAll( |
| 233 | Layer.mock(Orchestrator.OrchestratorV2)({ | 233 | Layer.mock(Orchestrator.OrchestratorV2)({ |
| 234 | getThreadShell: () => Effect.succeed(v2PullRequestThread(thread)), | 234 | getThreadShell: () => Effect.succeed(v2PullRequestThread(thread)), |
| 235 | }), | 235 | }), |
| ... | @@ -246,22 +246,22 @@ it.effect( | ... | @@ -246,22 +246,22 @@ it.effect( |
| 246 | yield* refreshPushedPullRequests( | 246 | yield* refreshPushedPullRequests( |
| 247 | { cwd: "/worktree", threadId: THREAD_ID }, | 247 | { cwd: "/worktree", threadId: THREAD_ID }, |
| 248 | { push: { status: "pushed" } }, | 248 | { push: { status: "pushed" } }, |
| 249 | ).pipe(Effect.provide(dependencies)); | 249 | ).pipe(Effect.provide(layerDependencies)); |
| 250 | yield* refreshPushedPullRequests( | 250 | yield* refreshPushedPullRequests( |
| 251 | { cwd: project.workspaceRoot }, | 251 | { cwd: project.workspaceRoot }, |
| 252 | { push: { status: "pushed" } }, | 252 | { push: { status: "pushed" } }, |
| 253 | ).pipe(Effect.provide(dependencies)); | 253 | ).pipe(Effect.provide(layerDependencies)); |
| 254 | yield* refreshPushedPullRequests({ cwd: "/unrelated" }, { push: { status: "pushed" } }).pipe( | 254 | yield* refreshPushedPullRequests({ cwd: "/unrelated" }, { push: { status: "pushed" } }).pipe( |
| 255 | Effect.provide(dependencies), | 255 | Effect.provide(layerDependencies), |
| 256 | ); | 256 | ); |
| 257 | yield* refreshPushedPullRequests( | 257 | yield* refreshPushedPullRequests( |
| 258 | { cwd: project.workspaceRoot, threadId: THREAD_ID }, | 258 | { cwd: project.workspaceRoot, threadId: THREAD_ID }, |
| 259 | { push: { status: "skipped_not_requested" } }, | 259 | { push: { status: "skipped_not_requested" } }, |
| 260 | ).pipe(Effect.provide(dependencies)); | 260 | ).pipe(Effect.provide(layerDependencies)); |
| 261 | yield* refreshPushedPullRequests( | 261 | yield* refreshPushedPullRequests( |
| 262 | { cwd: "/draft-worktree", projectId: PROJECT_ID }, | 262 | { cwd: "/draft-worktree", projectId: PROJECT_ID }, |
| 263 | { push: { status: "pushed" } }, | 263 | { push: { status: "pushed" } }, |
| 264 | ).pipe(Effect.provide(dependencies)); | 264 | ).pipe(Effect.provide(layerDependencies)); |
| 265 | expect(refreshed).toEqual([PROJECT_ID, PROJECT_ID, PROJECT_ID]); | 265 | expect(refreshed).toEqual([PROJECT_ID, PROJECT_ID, PROJECT_ID]); |
| 266 | }), | 266 | }), |
| 267 | ); | 267 | ); |
apps/server/src/htmlRender/HtmlRender.test.ts+11-11| ... | @@ -30,7 +30,7 @@ import * as PreviewBrowser from "./PreviewBrowser.ts"; | ... | @@ -30,7 +30,7 @@ import * as PreviewBrowser from "./PreviewBrowser.ts"; |
| 30 | // example one T3 installed under <T3 home>/tools/chrome-headless-shell. | 30 | // example one T3 installed under <T3 home>/tools/chrome-headless-shell. |
| 31 | const TEST_BROWSER_ENV = "T3CODE_TEST_HEADLESS_SHELL"; | 31 | const TEST_BROWSER_ENV = "T3CODE_TEST_HEADLESS_SHELL"; |
| 32 | 32 | ||
| 33 | const htmlRenderLayer = ( | 33 | const layerHtmlRender = ( |
| 34 | executable?: string, | 34 | executable?: string, |
| 35 | installed: Effect.Effect<Option.Option<string>> = Effect.succeed( | 35 | installed: Effect.Effect<Option.Option<string>> = Effect.succeed( |
| 36 | Option.fromUndefinedOr(executable), | 36 | Option.fromUndefinedOr(executable), |
| ... | @@ -52,7 +52,7 @@ const htmlRenderLayer = ( | ... | @@ -52,7 +52,7 @@ const htmlRenderLayer = ( |
| 52 | Layer.provideMerge(ServerConfig.layerTest(process.cwd(), { prefix: "t3-html-render-" })), | 52 | Layer.provideMerge(ServerConfig.layerTest(process.cwd(), { prefix: "t3-html-render-" })), |
| 53 | Layer.provideMerge(NodeServices.layer), | 53 | Layer.provideMerge(NodeServices.layer), |
| 54 | ); | 54 | ); |
| 55 | const testLayer = htmlRenderLayer(); | 55 | const layerTest = layerHtmlRender(); |
| 56 | 56 | ||
| 57 | const PNG_BYTES = new Uint8Array([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]); | 57 | const PNG_BYTES = new Uint8Array([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]); |
| 58 | 58 | ||
| ... | @@ -94,7 +94,7 @@ describe("HtmlRender", () => { | ... | @@ -94,7 +94,7 @@ describe("HtmlRender", () => { |
| 94 | // The theme bootstrap opens the head, ahead of the page's own markup. | 94 | // The theme bootstrap opens the head, ahead of the page's own markup. |
| 95 | expect(prepared.indexOf("<head>")).toBeLessThan(prepared.indexOf('<style id="t3-theme">')); | 95 | expect(prepared.indexOf("<head>")).toBeLessThan(prepared.indexOf('<style id="t3-theme">')); |
| 96 | expect(prepared.indexOf('<style id="t3-theme">')).toBeLessThan(prepared.indexOf("<title>")); | 96 | expect(prepared.indexOf('<style id="t3-theme">')).toBeLessThan(prepared.indexOf("<title>")); |
| 97 | }).pipe(Effect.provide(testLayer)), | 97 | }).pipe(Effect.provide(layerTest)), |
| 98 | ); | 98 | ); |
| 99 | 99 | ||
| 100 | it.effect("inlines an SVG behind processing instructions and a doctype subset", () => | 100 | it.effect("inlines an SVG behind processing instructions and a doctype subset", () => |
| ... | @@ -115,7 +115,7 @@ describe("HtmlRender", () => { | ... | @@ -115,7 +115,7 @@ describe("HtmlRender", () => { |
| 115 | const prepared = yield* htmlRender.prepare(`<img src="${svg}">`); | 115 | const prepared = yield* htmlRender.prepare(`<img src="${svg}">`); |
| 116 | 116 | ||
| 117 | expect(prepared).toContain(`data:image/svg+xml;base64,${Base64.encode(source)}`); | 117 | expect(prepared).toContain(`data:image/svg+xml;base64,${Base64.encode(source)}`); |
| 118 | }).pipe(Effect.provide(testLayer)), | 118 | }).pipe(Effect.provide(layerTest)), |
| 119 | ); | 119 | ); |
| 120 | 120 | ||
| 121 | it.effect("lists every local image it cannot read", () => | 121 | it.effect("lists every local image it cannot read", () => |
| ... | @@ -167,7 +167,7 @@ describe("HtmlRender", () => { | ... | @@ -167,7 +167,7 @@ describe("HtmlRender", () => { |
| 167 | upper, | 167 | upper, |
| 168 | longer, | 168 | longer, |
| 169 | ]); | 169 | ]); |
| 170 | }).pipe(Effect.provide(testLayer)), | 170 | }).pipe(Effect.provide(layerTest)), |
| 171 | ); | 171 | ); |
| 172 | 172 | ||
| 173 | it.effect("publishes the prepared page as an html thread attachment", () => | 173 | it.effect("publishes the prepared page as an html thread attachment", () => |
| ... | @@ -197,7 +197,7 @@ describe("HtmlRender", () => { | ... | @@ -197,7 +197,7 @@ describe("HtmlRender", () => { |
| 197 | const html = yield* fileSystem.readFileString(stored ?? ""); | 197 | const html = yield* fileSystem.readFileString(stored ?? ""); |
| 198 | expect(html).toContain('<style id="t3-theme">'); | 198 | expect(html).toContain('<style id="t3-theme">'); |
| 199 | expect(html).toContain("<p>Quarterly revenue</p>"); | 199 | expect(html).toContain("<p>Quarterly revenue</p>"); |
| 200 | }).pipe(Effect.provide(testLayer)), | 200 | }).pipe(Effect.provide(layerTest)), |
| 201 | ); | 201 | ); |
| 202 | 202 | ||
| 203 | it.effect("removes the page when publishing is interrupted", () => | 203 | it.effect("removes the page when publishing is interrupted", () => |
| ... | @@ -225,7 +225,7 @@ describe("HtmlRender", () => { | ... | @@ -225,7 +225,7 @@ describe("HtmlRender", () => { |
| 225 | expect(yield* storedPages).toEqual([]); | 225 | expect(yield* storedPages).toEqual([]); |
| 226 | }).pipe( | 226 | }).pipe( |
| 227 | Effect.provide( | 227 | Effect.provide( |
| 228 | htmlRenderLayer( | 228 | layerHtmlRender( |
| 229 | undefined, | 229 | undefined, |
| 230 | Deferred.succeed(measuring, undefined).pipe(Effect.andThen(Effect.never)), | 230 | Deferred.succeed(measuring, undefined).pipe(Effect.andThen(Effect.never)), |
| 231 | ), | 231 | ), |
| ... | @@ -283,7 +283,7 @@ describe("HtmlRender", () => { | ... | @@ -283,7 +283,7 @@ describe("HtmlRender", () => { |
| 283 | }, | 283 | }, |
| 284 | ]), | 284 | ]), |
| 285 | ); | 285 | ); |
| 286 | }).pipe(Effect.provide(htmlRenderLayer(executable))); | 286 | }).pipe(Effect.provide(layerHtmlRender(executable))); |
| 287 | }), | 287 | }), |
| 288 | 30_000, | 288 | 30_000, |
| 289 | ); | 289 | ); |
| ... | @@ -314,7 +314,7 @@ describe("HtmlRender", () => { | ... | @@ -314,7 +314,7 @@ describe("HtmlRender", () => { |
| 314 | const texts = preview.consoleMessages.map((message) => message.text); | 314 | const texts = preview.consoleMessages.map((message) => message.text); |
| 315 | expect(texts).toContain("secret: none"); | 315 | expect(texts).toContain("secret: none"); |
| 316 | expect(texts.join(" ")).not.toContain("abc123"); | 316 | expect(texts.join(" ")).not.toContain("abc123"); |
| 317 | }).pipe(Effect.scoped, Effect.provide(htmlRenderLayer(executable))); | 317 | }).pipe(Effect.scoped, Effect.provide(layerHtmlRender(executable))); |
| 318 | }), | 318 | }), |
| 319 | 30_000, | 319 | 30_000, |
| 320 | ); | 320 | ); |
| ... | @@ -370,7 +370,7 @@ describe("HtmlRender", () => { | ... | @@ -370,7 +370,7 @@ describe("HtmlRender", () => { |
| 370 | `window.open("${origin}/popup"); location.href = "${origin}/navigate";</script>`, | 370 | `window.open("${origin}/popup"); location.href = "${origin}/navigate";</script>`, |
| 371 | ].join(""), | 371 | ].join(""), |
| 372 | }); | 372 | }); |
| 373 | }).pipe(Effect.provide(htmlRenderLayer(executable))); | 373 | }).pipe(Effect.provide(layerHtmlRender(executable))); |
| 374 | expect(requests).toEqual([]); | 374 | expect(requests).toEqual([]); |
| 375 | expect(datagrams).toEqual([]); | 375 | expect(datagrams).toEqual([]); |
| 376 | }).pipe(Effect.scoped), | 376 | }).pipe(Effect.scoped), |
| ... | @@ -396,7 +396,7 @@ describe("HtmlRender", () => { | ... | @@ -396,7 +396,7 @@ describe("HtmlRender", () => { |
| 396 | expect(reference.heights).toEqual( | 396 | expect(reference.heights).toEqual( |
| 397 | HTML_RENDER_MEASURE_WIDTHS.map((width) => [width, Math.ceil(width / 2)]), | 397 | HTML_RENDER_MEASURE_WIDTHS.map((width) => [width, Math.ceil(width / 2)]), |
| 398 | ); | 398 | ); |
| 399 | }).pipe(Effect.provide(htmlRenderLayer(executable))); | 399 | }).pipe(Effect.provide(layerHtmlRender(executable))); |
| 400 | }), | 400 | }), |
| 401 | 30_000, | 401 | 30_000, |
| 402 | ); | 402 | ); |
apps/server/src/http.test.ts+20-21| ... | @@ -22,21 +22,19 @@ import * as ServerConfig from "./config.ts"; | ... | @@ -22,21 +22,19 @@ import * as ServerConfig from "./config.ts"; |
| 22 | 22 | ||
| 23 | import { | 23 | import { |
| 24 | assetResponseHeaders, | 24 | assetResponseHeaders, |
| 25 | browserApiCorsLayer, | ||
| 26 | assetFileResponse, | 25 | assetFileResponse, |
| 27 | downloadContentDisposition, | 26 | downloadContentDisposition, |
| 28 | httpCompressionLayer, | ||
| 29 | isLoopbackHostname, | 27 | isLoopbackHostname, |
| 30 | resolveDevRedirectUrl, | 28 | resolveDevRedirectUrl, |
| 31 | staticAndDevRouteLayer, | ||
| 32 | withUntracedRequests, | 29 | withUntracedRequests, |
| 33 | } from "./http.ts"; | 30 | } from "./http.ts"; |
| 31 | import * as ServerHttp from "./http.ts"; | ||
| 34 | 32 | ||
| 35 | describe("untraced requests", () => { | 33 | describe("untraced requests", () => { |
| 36 | it.effect("drops the HTTP server span for browser trace exports, query string included", () => { | 34 | it.effect("drops the HTTP server span for browser trace exports, query string included", () => { |
| 37 | const spanNames: Array<string> = []; | 35 | const spanNames: Array<string> = []; |
| 38 | return Effect.gen(function* () { | 36 | return Effect.gen(function* () { |
| 39 | const routes = Layer.effectDiscard( | 37 | const layerRoutes = Layer.effectDiscard( |
| 40 | Effect.gen(function* () { | 38 | Effect.gen(function* () { |
| 41 | const router = yield* HttpRouter.HttpRouter; | 39 | const router = yield* HttpRouter.HttpRouter; |
| 42 | yield* router.add("POST", "/api/observability/v1/traces", HttpServerResponse.empty()); | 40 | yield* router.add("POST", "/api/observability/v1/traces", HttpServerResponse.empty()); |
| ... | @@ -44,7 +42,7 @@ describe("untraced requests", () => { | ... | @@ -44,7 +42,7 @@ describe("untraced requests", () => { |
| 44 | }), | 42 | }), |
| 45 | ); | 43 | ); |
| 46 | const services = yield* Layer.build( | 44 | const services = yield* Layer.build( |
| 47 | withUntracedRequests(HttpRouter.serve(routes, { disableListenLog: true })).pipe( | 45 | withUntracedRequests(HttpRouter.serve(layerRoutes, { disableListenLog: true })).pipe( |
| 48 | Layer.provideMerge(NodeHttpServer.layerTest), | 46 | Layer.provideMerge(NodeHttpServer.layerTest), |
| 49 | ), | 47 | ), |
| 50 | ); | 48 | ); |
| ... | @@ -73,17 +71,17 @@ describe("untraced requests", () => { | ... | @@ -73,17 +71,17 @@ describe("untraced requests", () => { |
| 73 | 71 | ||
| 74 | describe("browser API CORS", () => { | 72 | describe("browser API CORS", () => { |
| 75 | it("accepts protocol negotiation with authenticated browser headers", async () => { | 73 | it("accepts protocol negotiation with authenticated browser headers", async () => { |
| 76 | const routeLayer = Layer.effectDiscard( | 74 | const layerRoute = Layer.effectDiscard( |
| 77 | Effect.gen(function* () { | 75 | Effect.gen(function* () { |
| 78 | const router = yield* HttpRouter.HttpRouter; | 76 | const router = yield* HttpRouter.HttpRouter; |
| 79 | yield* router.add("GET", "/api/environment", HttpServerResponse.empty()); | 77 | yield* router.add("GET", "/api/environment", HttpServerResponse.empty()); |
| 80 | }), | 78 | }), |
| 81 | ); | 79 | ); |
| 82 | const appLayer = Layer.merge(routeLayer, browserApiCorsLayer).pipe( | 80 | const layerApp = Layer.merge(layerRoute, ServerHttp.layerBrowserApiCors).pipe( |
| 83 | Layer.provide(ServerConfig.layerTest(process.cwd(), { prefix: "http-cors-test-" })), | 81 | Layer.provide(ServerConfig.layerTest(process.cwd(), { prefix: "http-cors-test-" })), |
| 84 | Layer.provide(NodeServices.layer), | 82 | Layer.provide(NodeServices.layer), |
| 85 | ); | 83 | ); |
| 86 | const { handler, dispose } = HttpRouter.toWebHandler(appLayer, { disableLogger: true }); | 84 | const { handler, dispose } = HttpRouter.toWebHandler(layerApp, { disableLogger: true }); |
| 87 | 85 | ||
| 88 | try { | 86 | try { |
| 89 | const response = await handler( | 87 | const response = await handler( |
| ... | @@ -117,19 +115,20 @@ describe("browser API CORS", () => { | ... | @@ -117,19 +115,20 @@ describe("browser API CORS", () => { |
| 117 | }); | 115 | }); |
| 118 | }); | 116 | }); |
| 119 | 117 | ||
| 120 | const fileResponseLayer = Layer.mergeAll(NodeHttpPlatform.layer, NodeServices.layer); | 118 | const layerFileResponse = Layer.mergeAll(NodeHttpPlatform.layer, NodeServices.layer); |
| 121 | 119 | ||
| 122 | const makeStaticRequest = Effect.fn("HttpTest.makeStaticRequest")(function* (staticDir: string) { | 120 | const makeStaticRequest = Effect.fn("HttpTest.makeStaticRequest")(function* (staticDir: string) { |
| 123 | const config = yield* ServerConfig.ServerConfig; | 121 | const config = yield* ServerConfig.ServerConfig; |
| 124 | const fileSystem = yield* FileSystem.FileSystem; | 122 | const fileSystem = yield* FileSystem.FileSystem; |
| 125 | const appLayer = Layer.merge(staticAndDevRouteLayer, httpCompressionLayer).pipe( | 123 | const layerApp = Layer.merge( |
| 126 | Layer.provideMerge(NodeHttpPlatform.layer), | 124 | ServerHttp.layerStaticAndDevRoute, |
| 127 | ); | 125 | ServerHttp.layerHttpCompression, |
| 126 | ).pipe(Layer.provideMerge(NodeHttpPlatform.layer)); | ||
| 128 | // HttpRouter.serve reuses services from the layers around it before the | 127 | // HttpRouter.serve reuses services from the layers around it before the |
| 129 | // app's own, and NodeHttpServer.layerTest brings a real FileSystem, so the | 128 | // app's own, and NodeHttpServer.layerTest brings a real FileSystem, so the |
| 130 | // caller's FileSystem and static directory go between the two. | 129 | // caller's FileSystem and static directory go between the two. |
| 131 | const services = yield* Layer.build( | 130 | const services = yield* Layer.build( |
| 132 | HttpRouter.serve(appLayer, { disableListenLog: true }).pipe( | 131 | HttpRouter.serve(layerApp, { disableListenLog: true }).pipe( |
| 133 | Layer.provideMerge(ServerConfig.layer({ ...config, staticDir })), | 132 | Layer.provideMerge(ServerConfig.layer({ ...config, staticDir })), |
| 134 | Layer.provideMerge(Layer.succeed(FileSystem.FileSystem, fileSystem)), | 133 | Layer.provideMerge(Layer.succeed(FileSystem.FileSystem, fileSystem)), |
| 135 | Layer.provideMerge(NodeHttpServer.layerTest), | 134 | Layer.provideMerge(NodeHttpServer.layerTest), |
| ... | @@ -351,7 +350,7 @@ describe("video asset byte ranges", () => { | ... | @@ -351,7 +350,7 @@ describe("video asset byte ranges", () => { |
| 351 | } | 350 | } |
| 352 | expect(yield* Effect.promise(() => response.text())).toBe(expected); | 351 | expect(yield* Effect.promise(() => response.text())).toBe(expected); |
| 353 | } | 352 | } |
| 354 | }).pipe(Effect.provide(fileResponseLayer)), | 353 | }).pipe(Effect.provide(layerFileResponse)), |
| 355 | ); | 354 | ); |
| 356 | 355 | ||
| 357 | it.effect( | 356 | it.effect( |
| ... | @@ -396,7 +395,7 @@ describe("video asset byte ranges", () => { | ... | @@ -396,7 +395,7 @@ describe("video asset byte ranges", () => { |
| 396 | ); | 395 | ); |
| 397 | } | 396 | } |
| 398 | } | 397 | } |
| 399 | }).pipe(Effect.provide(fileResponseLayer)), | 398 | }).pipe(Effect.provide(layerFileResponse)), |
| 400 | ); | 399 | ); |
| 401 | 400 | ||
| 402 | it.effect("streams guarded file ranges, including suffixes and conditional requests", () => | 401 | it.effect("streams guarded file ranges, including suffixes and conditional requests", () => |
| ... | @@ -436,7 +435,7 @@ describe("video asset byte ranges", () => { | ... | @@ -436,7 +435,7 @@ describe("video asset byte ranges", () => { |
| 436 | expect(response.headers.get("content-length")).toBe(String(expected.length)); | 435 | expect(response.headers.get("content-length")).toBe(String(expected.length)); |
| 437 | expect(yield* Effect.promise(() => response.text())).toBe(expected); | 436 | expect(yield* Effect.promise(() => response.text())).toBe(expected); |
| 438 | } | 437 | } |
| 439 | }).pipe(Effect.provide(fileResponseLayer)), | 438 | }).pipe(Effect.provide(layerFileResponse)), |
| 440 | ); | 439 | ); |
| 441 | 440 | ||
| 442 | it.effect("keeps attachment media out of the cache once its signed URL expires", () => | 441 | it.effect("keeps attachment media out of the cache once its signed URL expires", () => |
| ... | @@ -454,7 +453,7 @@ describe("video asset byte ranges", () => { | ... | @@ -454,7 +453,7 @@ describe("video asset byte ranges", () => { |
| 454 | ); | 453 | ); |
| 455 | expect(response.headers.get("cache-control")).toBe("private, no-store"); | 454 | expect(response.headers.get("cache-control")).toBe("private, no-store"); |
| 456 | expect(response.headers.get("accept-ranges")).toBe("bytes"); | 455 | expect(response.headers.get("accept-ranges")).toBe("bytes"); |
| 457 | }).pipe(Effect.provide(fileResponseLayer)), | 456 | }).pipe(Effect.provide(layerFileResponse)), |
| 458 | ); | 457 | ); |
| 459 | 458 | ||
| 460 | it.effect("closes guarded descriptors after full, HEAD, rejected, and cancelled responses", () => | 459 | it.effect("closes guarded descriptors after full, HEAD, rejected, and cancelled responses", () => |
| ... | @@ -501,7 +500,7 @@ describe("video asset byte ranges", () => { | ... | @@ -501,7 +500,7 @@ describe("video asset byte ranges", () => { |
| 501 | ); | 500 | ); |
| 502 | expect(file.handle.fd).toBe(-1); | 501 | expect(file.handle.fd).toBe(-1); |
| 503 | } | 502 | } |
| 504 | }).pipe(Effect.provide(fileResponseLayer)), | 503 | }).pipe(Effect.provide(layerFileResponse)), |
| 505 | ); | 504 | ); |
| 506 | 505 | ||
| 507 | it.effect("streams exactly the requested bytes and leaves full downloads intact", () => | 506 | it.effect("streams exactly the requested bytes and leaves full downloads intact", () => |
| ... | @@ -554,7 +553,7 @@ describe("video asset byte ranges", () => { | ... | @@ -554,7 +553,7 @@ describe("video asset byte ranges", () => { |
| 554 | expect(image.status).toBe(200); | 553 | expect(image.status).toBe(200); |
| 555 | expect(image.headers.has("accept-ranges")).toBe(false); | 554 | expect(image.headers.has("accept-ranges")).toBe(false); |
| 556 | expect(yield* Effect.promise(() => image.text())).toBe("0123456789"); | 555 | expect(yield* Effect.promise(() => image.text())).toBe("0123456789"); |
| 557 | }).pipe(Effect.provide(fileResponseLayer)), | 556 | }).pipe(Effect.provide(layerFileResponse)), |
| 558 | ); | 557 | ); |
| 559 | 558 | ||
| 560 | it.effect( | 559 | it.effect( |
| ... | @@ -584,7 +583,7 @@ describe("video asset byte ranges", () => { | ... | @@ -584,7 +583,7 @@ describe("video asset byte ranges", () => { |
| 584 | expect(download.status).toBe(200); | 583 | expect(download.status).toBe(200); |
| 585 | expect(download.headers.get("content-disposition")).toContain("attachment;"); | 584 | expect(download.headers.get("content-disposition")).toContain("attachment;"); |
| 586 | expect(yield* Effect.promise(() => download.text())).toBe("0123456789"); | 585 | expect(yield* Effect.promise(() => download.text())).toBe("0123456789"); |
| 587 | }).pipe(Effect.provide(fileResponseLayer)), | 586 | }).pipe(Effect.provide(layerFileResponse)), |
| 588 | ); | 587 | ); |
| 589 | 588 | ||
| 590 | it.effect("rejects ranges outside the file, including empty files", () => | 589 | it.effect("rejects ranges outside the file, including empty files", () => |
| ... | @@ -608,7 +607,7 @@ describe("video asset byte ranges", () => { | ... | @@ -608,7 +607,7 @@ describe("video asset byte ranges", () => { |
| 608 | ); | 607 | ); |
| 609 | expect(empty.status).toBe(416); | 608 | expect(empty.status).toBe(416); |
| 610 | expect(empty.headers.get("content-range")).toBe("bytes */0"); | 609 | expect(empty.headers.get("content-range")).toBe("bytes */0"); |
| 611 | }).pipe(Effect.provide(fileResponseLayer)), | 610 | }).pipe(Effect.provide(layerFileResponse)), |
| 612 | ); | 611 | ); |
| 613 | }); | 612 | }); |
| 614 | 613 |
apps/server/src/http.ts+9-9| ... | @@ -230,11 +230,11 @@ export const assetFileResponse = Effect.fn("assetFileResponse")(function* ( | ... | @@ -230,11 +230,11 @@ export const assetFileResponse = Effect.fn("assetFileResponse")(function* ( |
| 230 | return yield* HttpServerResponse.file(asset.path, { status, offset, bytesToRead, headers }); | 230 | return yield* HttpServerResponse.file(asset.path, { status, offset, bytesToRead, headers }); |
| 231 | }); | 231 | }); |
| 232 | 232 | ||
| 233 | export const httpCompressionLayer = HttpRouter.middleware(HttpMiddleware.compression(), { | 233 | export const layerHttpCompression = HttpRouter.middleware(HttpMiddleware.compression(), { |
| 234 | global: true, | 234 | global: true, |
| 235 | }); | 235 | }); |
| 236 | 236 | ||
| 237 | export const browserApiCorsLayer = Layer.unwrap( | 237 | export const layerBrowserApiCors = Layer.unwrap( |
| 238 | Effect.gen(function* () { | 238 | Effect.gen(function* () { |
| 239 | const config = yield* ServerConfig.ServerConfig; | 239 | const config = yield* ServerConfig.ServerConfig; |
| 240 | const devOrigin = config.devUrl?.origin; | 240 | const devOrigin = config.devUrl?.origin; |
| ... | @@ -297,7 +297,7 @@ const authenticateRawRouteWithScope = ( | ... | @@ -297,7 +297,7 @@ const authenticateRawRouteWithScope = ( |
| 297 | } | 297 | } |
| 298 | }); | 298 | }); |
| 299 | 299 | ||
| 300 | export const serverEnvironmentHttpApiLayer = HttpApiBuilder.group( | 300 | export const layerServerEnvironmentHttpApi = HttpApiBuilder.group( |
| 301 | EnvironmentHttpApi, | 301 | EnvironmentHttpApi, |
| 302 | "metadata", | 302 | "metadata", |
| 303 | Effect.fnUntraced(function* (handlers) { | 303 | Effect.fnUntraced(function* (handlers) { |
| ... | @@ -320,7 +320,7 @@ class DecodeOtlpTraceRecordsError extends Data.TaggedError("DecodeOtlpTraceRecor | ... | @@ -320,7 +320,7 @@ class DecodeOtlpTraceRecordsError extends Data.TaggedError("DecodeOtlpTraceRecor |
| 320 | // tracing this proxy would add more server spans than it forwards. | 320 | // tracing this proxy would add more server spans than it forwards. |
| 321 | // withTracerEnabled(false) drops the handler's spans, including the forward. | 321 | // withTracerEnabled(false) drops the handler's spans, including the forward. |
| 322 | // untracedRequestsLayer drops the HTTP server span. | 322 | // untracedRequestsLayer drops the HTTP server span. |
| 323 | export const otlpTracesProxyRouteLayer = HttpRouter.add( | 323 | export const layerOtlpTracesProxyRoute = HttpRouter.add( |
| 324 | "POST", | 324 | "POST", |
| 325 | OTLP_TRACES_PROXY_PATH, | 325 | OTLP_TRACES_PROXY_PATH, |
| 326 | Effect.gen(function* () { | 326 | Effect.gen(function* () { |
| ... | @@ -382,16 +382,16 @@ const UNTRACED_REQUEST_PATHS: ReadonlySet<string> = new Set([OTLP_TRACES_PROXY_P | ... | @@ -382,16 +382,16 @@ const UNTRACED_REQUEST_PATHS: ReadonlySet<string> = new Set([OTLP_TRACES_PROXY_P |
| 382 | // privately, so it is provided to the served layer, never merged into the | 382 | // privately, so it is provided to the served layer, never merged into the |
| 383 | // routes. Add paths here instead of providing it again. The query string is | 383 | // routes. Add paths here instead of providing it again. The query string is |
| 384 | // ignored, as in routing. | 384 | // ignored, as in routing. |
| 385 | const untracedRequestsLayer = Layer.succeed(HttpMiddleware.TracerDisabledWhen)((request) => { | 385 | const layerUntracedRequests = Layer.succeed(HttpMiddleware.TracerDisabledWhen)((request) => { |
| 386 | const queryIndex = request.url.indexOf("?"); | 386 | const queryIndex = request.url.indexOf("?"); |
| 387 | const path = queryIndex === -1 ? request.url : request.url.slice(0, queryIndex); | 387 | const path = queryIndex === -1 ? request.url : request.url.slice(0, queryIndex); |
| 388 | // Webhook URLs carry their secret token in the path, so they never reach a trace. | 388 | // Webhook URLs carry their secret token in the path, so they never reach a trace. |
| 389 | return UNTRACED_REQUEST_PATHS.has(path) || path.startsWith(`${WEBHOOK_ROUTE_PREFIX}/`); | 389 | return UNTRACED_REQUEST_PATHS.has(path) || path.startsWith(`${WEBHOOK_ROUTE_PREFIX}/`); |
| 390 | }); | 390 | }); |
| 391 | 391 | ||
| 392 | export const withUntracedRequests = Layer.provide(untracedRequestsLayer); | 392 | export const withUntracedRequests = Layer.provide(layerUntracedRequests); |
| 393 | 393 | ||
| 394 | export const assetRouteLayer = HttpRouter.add( | 394 | export const layerAssetRoute = HttpRouter.add( |
| 395 | "GET", | 395 | "GET", |
| 396 | `${ASSET_ROUTE_PREFIX}/*`, | 396 | `${ASSET_ROUTE_PREFIX}/*`, |
| 397 | Effect.gen(function* () { | 397 | Effect.gen(function* () { |
| ... | @@ -444,7 +444,7 @@ export const assetRouteLayer = HttpRouter.add( | ... | @@ -444,7 +444,7 @@ export const assetRouteLayer = HttpRouter.add( |
| 444 | }), | 444 | }), |
| 445 | ); | 445 | ); |
| 446 | 446 | ||
| 447 | export const attachmentUploadRouteLayer = HttpRouter.add( | 447 | export const layerAttachmentUploadRoute = HttpRouter.add( |
| 448 | "POST", | 448 | "POST", |
| 449 | `${ATTACHMENT_UPLOAD_ROUTE_PREFIX}/*`, | 449 | `${ATTACHMENT_UPLOAD_ROUTE_PREFIX}/*`, |
| 450 | Effect.gen(function* () { | 450 | Effect.gen(function* () { |
| ... | @@ -672,7 +672,7 @@ const handleStaticAndDevRequest = Effect.fn("handleStaticAndDevRequest")( | ... | @@ -672,7 +672,7 @@ const handleStaticAndDevRequest = Effect.fn("handleStaticAndDevRequest")( |
| 672 | ); | 672 | ); |
| 673 | 673 | ||
| 674 | // Read the installed build's manifest once. Unknown files use revalidation. | 674 | // Read the installed build's manifest once. Unknown files use revalidation. |
| 675 | export const staticAndDevRouteLayer = Layer.unwrap( | 675 | export const layerStaticAndDevRoute = Layer.unwrap( |
| 676 | loadImmutableBuildAssets.pipe( | 676 | loadImmutableBuildAssets.pipe( |
| 677 | Effect.map((assets) => HttpRouter.add("GET", "*", handleStaticAndDevRequest(assets))), | 677 | Effect.map((assets) => HttpRouter.add("GET", "*", handleStaticAndDevRequest(assets))), |
| 678 | ), | 678 | ), |
apps/server/src/keybindings.test.ts+20-23| ... | @@ -23,7 +23,7 @@ const encodeResolvedKeybindingFromConfig = Schema.encodeEffect( | ... | @@ -23,7 +23,7 @@ const encodeResolvedKeybindingFromConfig = Schema.encodeEffect( |
| 23 | const decodeResolvedKeybindingFromConfigExit = Schema.decodeUnknownExit( | 23 | const decodeResolvedKeybindingFromConfigExit = Schema.decodeUnknownExit( |
| 24 | Keybindings.ResolvedKeybindingFromConfig, | 24 | Keybindings.ResolvedKeybindingFromConfig, |
| 25 | ); | 25 | ); |
| 26 | const makeKeybindingsLayer = () => { | 26 | const layerKeybindings = () => { |
| 27 | return Keybindings.layer.pipe( | 27 | return Keybindings.layer.pipe( |
| 28 | Layer.provideMerge( | 28 | Layer.provideMerge( |
| 29 | Layer.fresh( | 29 | Layer.fresh( |
| ... | @@ -185,7 +185,7 @@ it.layer(NodeServices.layer)("keybindings", (it) => { | ... | @@ -185,7 +185,7 @@ it.layer(NodeServices.layer)("keybindings", (it) => { |
| 185 | 185 | ||
| 186 | const persisted = yield* readKeybindingsConfig(keybindingsConfigPath); | 186 | const persisted = yield* readKeybindingsConfig(keybindingsConfigPath); |
| 187 | assert.deepEqual(persisted, Keybindings.DEFAULT_KEYBINDINGS); | 187 | assert.deepEqual(persisted, Keybindings.DEFAULT_KEYBINDINGS); |
| 188 | }).pipe(Effect.provide(makeKeybindingsLayer())), | 188 | }).pipe(Effect.provide(layerKeybindings())), |
| 189 | ); | 189 | ); |
| 190 | 190 | ||
| 191 | it.effect("uses defaults in runtime when config is malformed without overriding file", () => | 191 | it.effect("uses defaults in runtime when config is malformed without overriding file", () => |
| ... | @@ -210,7 +210,7 @@ it.layer(NodeServices.layer)("keybindings", (it) => { | ... | @@ -210,7 +210,7 @@ it.layer(NodeServices.layer)("keybindings", (it) => { |
| 210 | }, | 210 | }, |
| 211 | ]); | 211 | ]); |
| 212 | assert.equal(yield* fs.readFileString(keybindingsConfigPath), "{ not-json"); | 212 | assert.equal(yield* fs.readFileString(keybindingsConfigPath), "{ not-json"); |
| 213 | }).pipe(Effect.provide(makeKeybindingsLayer())), | 213 | }).pipe(Effect.provide(layerKeybindings())), |
| 214 | ); | 214 | ); |
| 215 | 215 | ||
| 216 | it.effect("ignores invalid entries in runtime and reports them as issues", () => | 216 | it.effect("ignores invalid entries in runtime and reports them as issues", () => |
| ... | @@ -248,7 +248,7 @@ it.layer(NodeServices.layer)("keybindings", (it) => { | ... | @@ -248,7 +248,7 @@ it.layer(NodeServices.layer)("keybindings", (it) => { |
| 248 | message: configState.issues[1]?.message ?? "", | 248 | message: configState.issues[1]?.message ?? "", |
| 249 | }, | 249 | }, |
| 250 | ]); | 250 | ]); |
| 251 | }).pipe(Effect.provide(makeKeybindingsLayer())), | 251 | }).pipe(Effect.provide(layerKeybindings())), |
| 252 | ); | 252 | ); |
| 253 | 253 | ||
| 254 | it.effect( | 254 | it.effect( |
| ... | @@ -280,7 +280,7 @@ it.layer(NodeServices.layer)("keybindings", (it) => { | ... | @@ -280,7 +280,7 @@ it.layer(NodeServices.layer)("keybindings", (it) => { |
| 280 | assert.isTrue(byCommand.has(defaultRule.command), `expected ${defaultRule.command}`); | 280 | assert.isTrue(byCommand.has(defaultRule.command), `expected ${defaultRule.command}`); |
| 281 | } | 281 | } |
| 282 | assert.isTrue(byCommand.has("script.run-tests.run")); | 282 | assert.isTrue(byCommand.has("script.run-tests.run")); |
| 283 | }).pipe(Effect.provide(makeKeybindingsLayer())), | 283 | }).pipe(Effect.provide(layerKeybindings())), |
| 284 | ); | 284 | ); |
| 285 | 285 | ||
| 286 | it.effect("adds a late default to an existing command once", () => | 286 | it.effect("adds a late default to an existing command once", () => |
| ... | @@ -304,7 +304,7 @@ it.layer(NodeServices.layer)("keybindings", (it) => { | ... | @@ -304,7 +304,7 @@ it.layer(NodeServices.layer)("keybindings", (it) => { |
| 304 | yield* writeKeybindingsConfig(keybindingsConfigPath, [existing]); | 304 | yield* writeKeybindingsConfig(keybindingsConfigPath, [existing]); |
| 305 | yield* keybindings.syncDefaultKeybindingsOnStartup; | 305 | yield* keybindings.syncDefaultKeybindingsOnStartup; |
| 306 | assert.deepStrictEqual(yield* backgroundRules, [existing]); | 306 | assert.deepStrictEqual(yield* backgroundRules, [existing]); |
| 307 | }).pipe(Effect.provide(makeKeybindingsLayer())), | 307 | }).pipe(Effect.provide(layerKeybindings())), |
| 308 | ); | 308 | ); |
| 309 | 309 | ||
| 310 | it.effect("leaves a customized command without the late default", () => | 310 | it.effect("leaves a customized command without the late default", () => |
| ... | @@ -324,7 +324,7 @@ it.layer(NodeServices.layer)("keybindings", (it) => { | ... | @@ -324,7 +324,7 @@ it.layer(NodeServices.layer)("keybindings", (it) => { |
| 324 | persisted.filter((entry) => entry.command === "composer.sendBackground"), | 324 | persisted.filter((entry) => entry.command === "composer.sendBackground"), |
| 325 | [custom], | 325 | [custom], |
| 326 | ); | 326 | ); |
| 327 | }).pipe(Effect.provide(makeKeybindingsLayer())), | 327 | }).pipe(Effect.provide(layerKeybindings())), |
| 328 | ); | 328 | ); |
| 329 | 329 | ||
| 330 | it.effect("keeps a late default pending while the config is full", () => | 330 | it.effect("keeps a late default pending while the config is full", () => |
| ... | @@ -350,7 +350,7 @@ it.layer(NodeServices.layer)("keybindings", (it) => { | ... | @@ -350,7 +350,7 @@ it.layer(NodeServices.layer)("keybindings", (it) => { |
| 350 | yield* writeKeybindingsConfig(keybindingsConfigPath, [existing]); | 350 | yield* writeKeybindingsConfig(keybindingsConfigPath, [existing]); |
| 351 | yield* keybindings.syncDefaultKeybindingsOnStartup; | 351 | yield* keybindings.syncDefaultKeybindingsOnStartup; |
| 352 | assert.isTrue(yield* hasModEnter); | 352 | assert.isTrue(yield* hasModEnter); |
| 353 | }).pipe(Effect.provide(makeKeybindingsLayer())), | 353 | }).pipe(Effect.provide(layerKeybindings())), |
| 354 | ); | 354 | ); |
| 355 | 355 | ||
| 356 | it.effect("skips conflicting default keybindings on startup and logs a detailed warning", () => { | 356 | it.effect("skips conflicting default keybindings on startup and logs a detailed warning", () => { |
| ... | @@ -381,10 +381,7 @@ it.layer(NodeServices.layer)("keybindings", (it) => { | ... | @@ -381,10 +381,7 @@ it.layer(NodeServices.layer)("keybindings", (it) => { |
| 381 | ); | 381 | ); |
| 382 | }).pipe( | 382 | }).pipe( |
| 383 | Effect.provide( | 383 | Effect.provide( |
| 384 | Layer.mergeAll( | 384 | Layer.mergeAll(layerKeybindings(), Logger.layer([logger], { mergeWithExisting: false })), |
| 385 | makeKeybindingsLayer(), | ||
| 386 | Logger.layer([logger], { mergeWithExisting: false }), | ||
| 387 | ), | ||
| 388 | ), | 385 | ), |
| 389 | ); | 386 | ); |
| 390 | }); | 387 | }); |
| ... | @@ -412,7 +409,7 @@ it.layer(NodeServices.layer)("keybindings", (it) => { | ... | @@ -412,7 +409,7 @@ it.layer(NodeServices.layer)("keybindings", (it) => { |
| 412 | { key: "mod+shift+r", command: "script.run-tests.run" }, | 409 | { key: "mod+shift+r", command: "script.run-tests.run" }, |
| 413 | ]); | 410 | ]); |
| 414 | assert.isTrue(resolved.some((entry) => entry.command === "script.run-tests.run")); | 411 | assert.isTrue(resolved.some((entry) => entry.command === "script.run-tests.run")); |
| 415 | }).pipe(Effect.provide(makeKeybindingsLayer())), | 412 | }).pipe(Effect.provide(layerKeybindings())), |
| 416 | ); | 413 | ); |
| 417 | 414 | ||
| 418 | it.effect("appends additional custom keybindings for the same command", () => | 415 | it.effect("appends additional custom keybindings for the same command", () => |
| ... | @@ -435,7 +432,7 @@ it.layer(NodeServices.layer)("keybindings", (it) => { | ... | @@ -435,7 +432,7 @@ it.layer(NodeServices.layer)("keybindings", (it) => { |
| 435 | { key: "mod+r", command: "script.run-tests.run" }, | 432 | { key: "mod+r", command: "script.run-tests.run" }, |
| 436 | { key: "mod+shift+r", command: "script.run-tests.run" }, | 433 | { key: "mod+shift+r", command: "script.run-tests.run" }, |
| 437 | ]); | 434 | ]); |
| 438 | }).pipe(Effect.provide(makeKeybindingsLayer())), | 435 | }).pipe(Effect.provide(layerKeybindings())), |
| 439 | ); | 436 | ); |
| 440 | 437 | ||
| 441 | it.effect("replaces only the targeted custom keybinding", () => | 438 | it.effect("replaces only the targeted custom keybinding", () => |
| ... | @@ -460,7 +457,7 @@ it.layer(NodeServices.layer)("keybindings", (it) => { | ... | @@ -460,7 +457,7 @@ it.layer(NodeServices.layer)("keybindings", (it) => { |
| 460 | { key: "mod+shift+r", command: "script.run-tests.run" }, | 457 | { key: "mod+shift+r", command: "script.run-tests.run" }, |
| 461 | { key: "mod+alt+r", command: "script.run-tests.run" }, | 458 | { key: "mod+alt+r", command: "script.run-tests.run" }, |
| 462 | ]); | 459 | ]); |
| 463 | }).pipe(Effect.provide(makeKeybindingsLayer())), | 460 | }).pipe(Effect.provide(layerKeybindings())), |
| 464 | ); | 461 | ); |
| 465 | 462 | ||
| 466 | it.effect("replacing with a rule that already exists elsewhere does not duplicate it", () => | 463 | it.effect("replacing with a rule that already exists elsewhere does not duplicate it", () => |
| ... | @@ -482,7 +479,7 @@ it.layer(NodeServices.layer)("keybindings", (it) => { | ... | @@ -482,7 +479,7 @@ it.layer(NodeServices.layer)("keybindings", (it) => { |
| 482 | const persisted = yield* readKeybindingsConfig(keybindingsConfigPath); | 479 | const persisted = yield* readKeybindingsConfig(keybindingsConfigPath); |
| 483 | const persistedView = persisted.map(({ key, command }) => ({ key, command })); | 480 | const persistedView = persisted.map(({ key, command }) => ({ key, command })); |
| 484 | assert.deepEqual(persistedView, [{ key: "mod+alt+r", command: "script.run-tests.run" }]); | 481 | assert.deepEqual(persistedView, [{ key: "mod+alt+r", command: "script.run-tests.run" }]); |
| 485 | }).pipe(Effect.provide(makeKeybindingsLayer())), | 482 | }).pipe(Effect.provide(layerKeybindings())), |
| 486 | ); | 483 | ); |
| 487 | 484 | ||
| 488 | it.effect("removes only the targeted custom keybinding", () => | 485 | it.effect("removes only the targeted custom keybinding", () => |
| ... | @@ -503,7 +500,7 @@ it.layer(NodeServices.layer)("keybindings", (it) => { | ... | @@ -503,7 +500,7 @@ it.layer(NodeServices.layer)("keybindings", (it) => { |
| 503 | const persisted = yield* readKeybindingsConfig(keybindingsConfigPath); | 500 | const persisted = yield* readKeybindingsConfig(keybindingsConfigPath); |
| 504 | const persistedView = persisted.map(({ key, command }) => ({ key, command })); | 501 | const persistedView = persisted.map(({ key, command }) => ({ key, command })); |
| 505 | assert.deepEqual(persistedView, [{ key: "mod+shift+r", command: "script.run-tests.run" }]); | 502 | assert.deepEqual(persistedView, [{ key: "mod+shift+r", command: "script.run-tests.run" }]); |
| 506 | }).pipe(Effect.provide(makeKeybindingsLayer())), | 503 | }).pipe(Effect.provide(layerKeybindings())), |
| 507 | ); | 504 | ); |
| 508 | 505 | ||
| 509 | it.effect("refuses to overwrite malformed keybindings config", () => | 506 | it.effect("refuses to overwrite malformed keybindings config", () => |
| ... | @@ -523,7 +520,7 @@ it.layer(NodeServices.layer)("keybindings", (it) => { | ... | @@ -523,7 +520,7 @@ it.layer(NodeServices.layer)("keybindings", (it) => { |
| 523 | 520 | ||
| 524 | const persistedRaw = yield* fs.readFileString(keybindingsConfigPath); | 521 | const persistedRaw = yield* fs.readFileString(keybindingsConfigPath); |
| 525 | assert.equal(persistedRaw, "{ not-json"); | 522 | assert.equal(persistedRaw, "{ not-json"); |
| 526 | }).pipe(Effect.provide(makeKeybindingsLayer())), | 523 | }).pipe(Effect.provide(layerKeybindings())), |
| 527 | ); | 524 | ); |
| 528 | 525 | ||
| 529 | it.effect("reports non-array config parse errors without duplicate prefix", () => | 526 | it.effect("reports non-array config parse errors without duplicate prefix", () => |
| ... | @@ -552,7 +549,7 @@ it.layer(NodeServices.layer)("keybindings", (it) => { | ... | @@ -552,7 +549,7 @@ it.layer(NodeServices.layer)("keybindings", (it) => { |
| 552 | }); | 549 | }); |
| 553 | }).pipe(toDetailResult); | 550 | }).pipe(toDetailResult); |
| 554 | assertFailure(secondResult, "expected JSON array"); | 551 | assertFailure(secondResult, "expected JSON array"); |
| 555 | }).pipe(Effect.provide(makeKeybindingsLayer())), | 552 | }).pipe(Effect.provide(layerKeybindings())), |
| 556 | ); | 553 | ); |
| 557 | 554 | ||
| 558 | // chmod cannot make a directory unwritable on Windows, so the write succeeds. | 555 | // chmod cannot make a directory unwritable on Windows, so the write succeeds. |
| ... | @@ -582,7 +579,7 @@ it.layer(NodeServices.layer)("keybindings", (it) => { | ... | @@ -582,7 +579,7 @@ it.layer(NodeServices.layer)("keybindings", (it) => { |
| 582 | const persisted = yield* readKeybindingsConfig(keybindingsConfigPath); | 579 | const persisted = yield* readKeybindingsConfig(keybindingsConfigPath); |
| 583 | const persistedView = persisted.map(({ key, command }) => ({ key, command })); | 580 | const persistedView = persisted.map(({ key, command }) => ({ key, command })); |
| 584 | assert.deepEqual(persistedView, [{ key: "mod+j", command: "terminal.toggle" }]); | 581 | assert.deepEqual(persistedView, [{ key: "mod+j", command: "terminal.toggle" }]); |
| 585 | }).pipe(Effect.provide(makeKeybindingsLayer())), | 582 | }).pipe(Effect.provide(layerKeybindings())), |
| 586 | ); | 583 | ); |
| 587 | 584 | ||
| 588 | it.effect("caches loaded resolved config across repeated reads", () => | 585 | it.effect("caches loaded resolved config across repeated reads", () => |
| ... | @@ -601,7 +598,7 @@ it.layer(NodeServices.layer)("keybindings", (it) => { | ... | @@ -601,7 +598,7 @@ it.layer(NodeServices.layer)("keybindings", (it) => { |
| 601 | 598 | ||
| 602 | assert.deepEqual(first, second); | 599 | assert.deepEqual(first, second); |
| 603 | assert.isTrue(second.some((entry) => entry.command === "terminal.toggle")); | 600 | assert.isTrue(second.some((entry) => entry.command === "terminal.toggle")); |
| 604 | }).pipe(Effect.provide(makeKeybindingsLayer())), | 601 | }).pipe(Effect.provide(layerKeybindings())), |
| 605 | ); | 602 | ); |
| 606 | 603 | ||
| 607 | it.effect("updates cached resolved config after upsert", () => | 604 | it.effect("updates cached resolved config after upsert", () => |
| ... | @@ -623,7 +620,7 @@ it.layer(NodeServices.layer)("keybindings", (it) => { | ... | @@ -623,7 +620,7 @@ it.layer(NodeServices.layer)("keybindings", (it) => { |
| 623 | 620 | ||
| 624 | assert.isTrue(loadedAfterUpsert.some((entry) => entry.command === "script.run-tests.run")); | 621 | assert.isTrue(loadedAfterUpsert.some((entry) => entry.command === "script.run-tests.run")); |
| 625 | assert.isTrue(loadedAfterUpsert.some((entry) => entry.command === "terminal.toggle")); | 622 | assert.isTrue(loadedAfterUpsert.some((entry) => entry.command === "terminal.toggle")); |
| 626 | }).pipe(Effect.provide(makeKeybindingsLayer())), | 623 | }).pipe(Effect.provide(layerKeybindings())), |
| 627 | ); | 624 | ); |
| 628 | 625 | ||
| 629 | it.effect("serializes concurrent upserts to avoid lost updates", () => | 626 | it.effect("serializes concurrent upserts to avoid lost updates", () => |
| ... | @@ -653,6 +650,6 @@ it.layer(NodeServices.layer)("keybindings", (it) => { | ... | @@ -653,6 +650,6 @@ it.layer(NodeServices.layer)("keybindings", (it) => { |
| 653 | for (const command of commands) { | 650 | for (const command of commands) { |
| 654 | assert.isTrue(persistedCommands.has(command), `expected persisted command ${command}`); | 651 | assert.isTrue(persistedCommands.has(command), `expected persisted command ${command}`); |
| 655 | } | 652 | } |
| 656 | }).pipe(Effect.provide(makeKeybindingsLayer())), | 653 | }).pipe(Effect.provide(layerKeybindings())), |
| 657 | ); | 654 | ); |
| 658 | }); | 655 | }); |
apps/server/src/mcp/McpDeviceToolkit.test.ts+7-7| ... | @@ -81,7 +81,7 @@ new DataView(png.buffer).setUint32(12, 0x49484452); | ... | @@ -81,7 +81,7 @@ new DataView(png.buffer).setUint32(12, 0x49484452); |
| 81 | new DataView(png.buffer).setUint32(16, 1206); | 81 | new DataView(png.buffer).setUint32(16, 1206); |
| 82 | new DataView(png.buffer).setUint32(20, 2622); | 82 | new DataView(png.buffer).setUint32(20, 2622); |
| 83 | 83 | ||
| 84 | const DeviceServiceMock = Layer.mock(DeviceService.DeviceService)({ | 84 | const layerDeviceServiceMock = Layer.mock(DeviceService.DeviceService)({ |
| 85 | state: Effect.succeed(state), | 85 | state: Effect.succeed(state), |
| 86 | list: Effect.succeed(state), | 86 | list: Effect.succeed(state), |
| 87 | open: (input) => | 87 | open: (input) => |
| ... | @@ -100,9 +100,9 @@ const DeviceServiceMock = Layer.mock(DeviceService.DeviceService)({ | ... | @@ -100,9 +100,9 @@ const DeviceServiceMock = Layer.mock(DeviceService.DeviceService)({ |
| 100 | agentTarget: () => Effect.succeed(["--config", "/host.json", "--session", "thread-device"]), | 100 | agentTarget: () => Effect.succeed(["--config", "/host.json", "--session", "thread-device"]), |
| 101 | }); | 101 | }); |
| 102 | 102 | ||
| 103 | const TestLayer = McpHttpServer.DeviceToolkitRegistrationLive.pipe( | 103 | const layerTest = McpHttpServer.layerDeviceToolkit.pipe( |
| 104 | Layer.provideMerge(McpServer.McpServer.layer), | 104 | Layer.provideMerge(McpServer.McpServer.layer), |
| 105 | Layer.provideMerge(DeviceServiceMock), | 105 | Layer.provideMerge(layerDeviceServiceMock), |
| 106 | Layer.provide(ServerConfig.layerTest(process.cwd(), { prefix: "t3-mcp-device-toolkit-test-" })), | 106 | Layer.provide(ServerConfig.layerTest(process.cwd(), { prefix: "t3-mcp-device-toolkit-test-" })), |
| 107 | Layer.provide(NodeServices.layer), | 107 | Layer.provide(NodeServices.layer), |
| 108 | ); | 108 | ); |
| ... | @@ -138,11 +138,11 @@ it.effect("registers the device tools and returns the screenshot as image conten | ... | @@ -138,11 +138,11 @@ it.effect("registers the device tools and returns the screenshot as image conten |
| 138 | .pipe(callWith(["preview"]), Effect.provideService(McpSchema.McpServerClient, client)); | 138 | .pipe(callWith(["preview"]), Effect.provideService(McpSchema.McpServerClient, client)); |
| 139 | expect(denied.isError).toBe(true); | 139 | expect(denied.isError).toBe(true); |
| 140 | }), | 140 | }), |
| 141 | ).pipe(Effect.provide(TestLayer)), | 141 | ).pipe(Effect.provide(layerTest)), |
| 142 | ); | 142 | ); |
| 143 | 143 | ||
| 144 | it.effect("rejects unavailable agent access before booting or opening a device", () => { | 144 | it.effect("rejects unavailable agent access before booting or opening a device", () => { |
| 145 | const unavailable = Layer.mock(DeviceService.DeviceService)({ | 145 | const layerUnavailable = Layer.mock(DeviceService.DeviceService)({ |
| 146 | list: Effect.succeed(state), | 146 | list: Effect.succeed(state), |
| 147 | agentTarget: () => | 147 | agentTarget: () => |
| 148 | Effect.fail( | 148 | Effect.fail( |
| ... | @@ -170,9 +170,9 @@ it.effect("rejects unavailable agent access before booting or opening a device", | ... | @@ -170,9 +170,9 @@ it.effect("rejects unavailable agent access before booting or opening a device", |
| 170 | }).pipe( | 170 | }).pipe( |
| 171 | Effect.scoped, | 171 | Effect.scoped, |
| 172 | Effect.provide( | 172 | Effect.provide( |
| 173 | McpHttpServer.DeviceToolkitRegistrationLive.pipe( | 173 | McpHttpServer.layerDeviceToolkit.pipe( |
| 174 | Layer.provideMerge(McpServer.McpServer.layer), | 174 | Layer.provideMerge(McpServer.McpServer.layer), |
| 175 | Layer.provide(unavailable), | 175 | Layer.provide(layerUnavailable), |
| 176 | Layer.provide(NodeServices.layer), | 176 | Layer.provide(NodeServices.layer), |
| 177 | ), | 177 | ), |
| 178 | ), | 178 | ), |
apps/server/src/mcp/McpHttpServer.test.ts+17-17| ... | @@ -49,13 +49,13 @@ const client = McpSchema.McpServerClient.of({ | ... | @@ -49,13 +49,13 @@ const client = McpSchema.McpServerClient.of({ |
| 49 | }, | 49 | }, |
| 50 | getClient: Effect.die("unused"), | 50 | getClient: Effect.die("unused"), |
| 51 | }); | 51 | }); |
| 52 | const TestLayer = McpHttpServer.PreviewToolkitRegistrationLive.pipe( | 52 | const layerTest = McpHttpServer.layerPreviewToolkit.pipe( |
| 53 | Layer.provideMerge(McpServer.McpServer.layer), | 53 | Layer.provideMerge(McpServer.McpServer.layer), |
| 54 | Layer.provideMerge(PreviewAutomationBroker.layer), | 54 | Layer.provideMerge(PreviewAutomationBroker.layer), |
| 55 | Layer.provideMerge(ServerConfig.layerTest(process.cwd(), { prefix: "t3-mcp-http-server-test-" })), | 55 | Layer.provideMerge(ServerConfig.layerTest(process.cwd(), { prefix: "t3-mcp-http-server-test-" })), |
| 56 | Layer.provideMerge(NodeServices.layer), | 56 | Layer.provideMerge(NodeServices.layer), |
| 57 | ); | 57 | ); |
| 58 | const PullRequestsTestLayer = McpHttpServer.PullRequestsToolkitRegistrationLive.pipe( | 58 | const layerPullRequestsTest = McpHttpServer.layerPullRequestsToolkit.pipe( |
| 59 | Layer.provideMerge(McpServer.McpServer.layer), | 59 | Layer.provideMerge(McpServer.McpServer.layer), |
| 60 | Layer.provide( | 60 | Layer.provide( |
| 61 | Layer.mergeAll( | 61 | Layer.mergeAll( |
| ... | @@ -179,7 +179,7 @@ it.effect.each([{}, { includeImage: false }])( | ... | @@ -179,7 +179,7 @@ it.effect.each([{}, { includeImage: false }])( |
| 179 | }, | 179 | }, |
| 180 | }); | 180 | }); |
| 181 | }), | 181 | }), |
| 182 | ).pipe(Effect.provide(TestLayer)), | 182 | ).pipe(Effect.provide(layerTest)), |
| 183 | ); | 183 | ); |
| 184 | 184 | ||
| 185 | it.effect.each([ | 185 | it.effect.each([ |
| ... | @@ -214,7 +214,7 @@ it.effect.each([ | ... | @@ -214,7 +214,7 @@ it.effect.each([ |
| 214 | { type: "text", text: `Preview snapshot failed: ${advice}` }, | 214 | { type: "text", text: `Preview snapshot failed: ${advice}` }, |
| 215 | ]); | 215 | ]); |
| 216 | }), | 216 | }), |
| 217 | ).pipe(Effect.provide(TestLayer)), | 217 | ).pipe(Effect.provide(layerTest)), |
| 218 | ); | 218 | ); |
| 219 | 219 | ||
| 220 | it.effect("tells the agent how to fall back when no desktop app can run the snapshot", () => | 220 | it.effect("tells the agent how to fall back when no desktop app can run the snapshot", () => |
| ... | @@ -229,7 +229,7 @@ it.effect("tells the agent how to fall back when no desktop app can run the snap | ... | @@ -229,7 +229,7 @@ it.effect("tells the agent how to fall back when no desktop app can run the snap |
| 229 | expect(snapshot.structuredContent).toMatchObject({ | 229 | expect(snapshot.structuredContent).toMatchObject({ |
| 230 | error: { _tag: "PreviewAutomationNoAvailableHostError" }, | 230 | error: { _tag: "PreviewAutomationNoAvailableHostError" }, |
| 231 | }); | 231 | }); |
| 232 | }).pipe(Effect.provide(TestLayer)), | 232 | }).pipe(Effect.provide(layerTest)), |
| 233 | ); | 233 | ); |
| 234 | 234 | ||
| 235 | it.effect.each([ | 235 | it.effect.each([ |
| ... | @@ -350,7 +350,7 @@ it.effect.each([ | ... | @@ -350,7 +350,7 @@ it.effect.each([ |
| 350 | expect(nextDefault.structuredContent).not.toHaveProperty("accessibilityTree"); | 350 | expect(nextDefault.structuredContent).not.toHaveProperty("accessibilityTree"); |
| 351 | expect(requests).toBe(7); | 351 | expect(requests).toBe(7); |
| 352 | }), | 352 | }), |
| 353 | ).pipe(Effect.provide(TestLayer)), | 353 | ).pipe(Effect.provide(layerTest)), |
| 354 | ); | 354 | ); |
| 355 | 355 | ||
| 356 | it.effect("rejects non-boolean snapshot image options before selecting a browser host", () => | 356 | it.effect("rejects non-boolean snapshot image options before selecting a browser host", () => |
| ... | @@ -372,7 +372,7 @@ it.effect("rejects non-boolean snapshot image options before selecting a browser | ... | @@ -372,7 +372,7 @@ it.effect("rejects non-boolean snapshot image options before selecting a browser |
| 372 | error: { _tag: "AiError", operation: "snapshot", failureCount: 1 }, | 372 | error: { _tag: "AiError", operation: "snapshot", failureCount: 1 }, |
| 373 | }); | 373 | }); |
| 374 | } | 374 | } |
| 375 | }).pipe(Effect.provide(TestLayer)), | 375 | }).pipe(Effect.provide(layerTest)), |
| 376 | ); | 376 | ); |
| 377 | 377 | ||
| 378 | it.effect("saves the snapshot PNG on request and reports its path", () => | 378 | it.effect("saves the snapshot PNG on request and reports its path", () => |
| ... | @@ -411,7 +411,7 @@ it.effect("saves the snapshot PNG on request and reports its path", () => | ... | @@ -411,7 +411,7 @@ it.effect("saves the snapshot PNG on request and reports its path", () => |
| 411 | expect(others).toEqual([]); | 411 | expect(others).toEqual([]); |
| 412 | expect(only?.type === "text" ? decodeJsonText(only.text) : null).toEqual(saved); | 412 | expect(only?.type === "text" ? decodeJsonText(only.text) : null).toEqual(saved); |
| 413 | }), | 413 | }), |
| 414 | ).pipe(Effect.provide(TestLayer)), | 414 | ).pipe(Effect.provide(layerTest)), |
| 415 | ); | 415 | ); |
| 416 | 416 | ||
| 417 | it.effect("reports a tagged error when the screenshot cannot be saved", () => | 417 | it.effect("reports a tagged error when the screenshot cannot be saved", () => |
| ... | @@ -433,7 +433,7 @@ it.effect("reports a tagged error when the screenshot cannot be saved", () => | ... | @@ -433,7 +433,7 @@ it.effect("reports a tagged error when the screenshot cannot be saved", () => |
| 433 | error: { _tag: "PreviewScreenshotSaveError", operation: "snapshot", failureCount: 1 }, | 433 | error: { _tag: "PreviewScreenshotSaveError", operation: "snapshot", failureCount: 1 }, |
| 434 | }); | 434 | }); |
| 435 | }), | 435 | }), |
| 436 | ).pipe(Effect.provide(TestLayer)), | 436 | ).pipe(Effect.provide(layerTest)), |
| 437 | ); | 437 | ); |
| 438 | 438 | ||
| 439 | it.effect( | 439 | it.effect( |
| ... | @@ -465,7 +465,7 @@ it.effect( | ... | @@ -465,7 +465,7 @@ it.effect( |
| 465 | expect(denied.content).toEqual([ | 465 | expect(denied.content).toEqual([ |
| 466 | { type: "text", text: "MCP credential does not grant the pull-requests capability." }, | 466 | { type: "text", text: "MCP credential does not grant the pull-requests capability." }, |
| 467 | ]); | 467 | ]); |
| 468 | }).pipe(Effect.provide(PullRequestsTestLayer)), | 468 | }).pipe(Effect.provide(layerPullRequestsTest)), |
| 469 | ); | 469 | ); |
| 470 | 470 | ||
| 471 | it.effect("keeps the snapshot text under the agent's output ceiling", () => | 471 | it.effect("keeps the snapshot text under the agent's output ceiling", () => |
| ... | @@ -535,7 +535,7 @@ it.effect("keeps the snapshot text under the agent's output ceiling", () => | ... | @@ -535,7 +535,7 @@ it.effect("keeps the snapshot text under the agent's output ceiling", () => |
| 535 | omitted: expect.arrayContaining(["60 older console entries"]), | 535 | omitted: expect.arrayContaining(["60 older console entries"]), |
| 536 | }); | 536 | }); |
| 537 | }), | 537 | }), |
| 538 | ).pipe(Effect.provide(TestLayer)), | 538 | ).pipe(Effect.provide(layerTest)), |
| 539 | ); | 539 | ); |
| 540 | 540 | ||
| 541 | it.effect("bounds the snapshot text even when nothing but logs and the title are large", () => | 541 | it.effect("bounds the snapshot text even when nothing but logs and the title are large", () => |
| ... | @@ -567,7 +567,7 @@ it.effect("bounds the snapshot text even when nothing but logs and the title are | ... | @@ -567,7 +567,7 @@ it.effect("bounds the snapshot text even when nothing but logs and the title are |
| 567 | expect(noticeText).toContain("url or title after 2048 characters"); | 567 | expect(noticeText).toContain("url or title after 2048 characters"); |
| 568 | expect(noticeText).toContain("console entries text after 500 characters"); | 568 | expect(noticeText).toContain("console entries text after 500 characters"); |
| 569 | }), | 569 | }), |
| 570 | ).pipe(Effect.provide(TestLayer)), | 570 | ).pipe(Effect.provide(layerTest)), |
| 571 | ); | 571 | ); |
| 572 | 572 | ||
| 573 | it.effect("bounds page text made of wide characters before dropping locators", () => | 573 | it.effect("bounds page text made of wide characters before dropping locators", () => |
| ... | @@ -606,7 +606,7 @@ it.effect("bounds page text made of wide characters before dropping locators", ( | ... | @@ -606,7 +606,7 @@ it.effect("bounds page text made of wide characters before dropping locators", ( |
| 606 | "visibleText after 4000 characters", | 606 | "visibleText after 4000 characters", |
| 607 | ); | 607 | ); |
| 608 | }), | 608 | }), |
| 609 | ).pipe(Effect.provide(TestLayer)), | 609 | ).pipe(Effect.provide(layerTest)), |
| 610 | ); | 610 | ); |
| 611 | 611 | ||
| 612 | it.effect("sheds log entries before locators when every list is full", () => | 612 | it.effect("sheds log entries before locators when every list is full", () => |
| ... | @@ -672,19 +672,19 @@ it.effect("sheds log entries before locators when every list is full", () => | ... | @@ -672,19 +672,19 @@ it.effect("sheds log entries before locators when every list is full", () => |
| 672 | expect(noticeText).toContain("40 of 40 actionTimeline"); | 672 | expect(noticeText).toContain("40 of 40 actionTimeline"); |
| 673 | expect(noticeText).not.toMatch(/\d+ of \d+ interactiveElements/); | 673 | expect(noticeText).not.toMatch(/\d+ of \d+ interactiveElements/); |
| 674 | }), | 674 | }), |
| 675 | ).pipe(Effect.provide(TestLayer)), | 675 | ).pipe(Effect.provide(layerTest)), |
| 676 | ); | 676 | ); |
| 677 | 677 | ||
| 678 | it.effect("terminates HTTP MCP sessions with DELETE", () => | 678 | it.effect("terminates HTTP MCP sessions with DELETE", () => |
| 679 | Effect.scoped( | 679 | Effect.scoped( |
| 680 | Effect.gen(function* () { | 680 | Effect.gen(function* () { |
| 681 | const serverLayer = McpServer.layerHttp({ | 681 | const layerServer = McpServer.layerHttp({ |
| 682 | name: "MCP termination test", | 682 | name: "MCP termination test", |
| 683 | version: "1.0.0", | 683 | version: "1.0.0", |
| 684 | path: "/mcp", | 684 | path: "/mcp", |
| 685 | protocols: [McpProtocol.v2025_06_18], | 685 | protocols: [McpProtocol.v2025_06_18], |
| 686 | }); | 686 | }); |
| 687 | yield* HttpRouter.serve(serverLayer, { | 687 | yield* HttpRouter.serve(layerServer, { |
| 688 | disableListenLog: true, | 688 | disableListenLog: true, |
| 689 | disableLogger: true, | 689 | disableLogger: true, |
| 690 | }).pipe(Layer.build); | 690 | }).pipe(Layer.build); |
| ... | @@ -872,5 +872,5 @@ it.effect("registers annotated tools and preserves authenticated request context | ... | @@ -872,5 +872,5 @@ it.effect("registers annotated tools and preserves authenticated request context |
| 872 | expect(text?.type === "text" ? decodeJsonText(text.text) : null).toEqual({ toolIcon }); | 872 | expect(text?.type === "text" ? decodeJsonText(text.text) : null).toEqual({ toolIcon }); |
| 873 | } | 873 | } |
| 874 | }), | 874 | }), |
| 875 | ).pipe(Effect.provide(TestLayer)), | 875 | ).pipe(Effect.provide(layerTest)), |
| 876 | ); | 876 | ); |
apps/server/src/mcp/McpHttpServer.ts+59-68| ... | @@ -23,47 +23,38 @@ import * as PreviewBrowser from "../htmlRender/PreviewBrowser.ts"; | ... | @@ -23,47 +23,38 @@ import * as PreviewBrowser from "../htmlRender/PreviewBrowser.ts"; |
| 23 | import * as McpInvocationContext from "./McpInvocationContext.ts"; | 23 | import * as McpInvocationContext from "./McpInvocationContext.ts"; |
| 24 | import * as OrchestratorMcpService from "./OrchestratorMcpService.ts"; | 24 | import * as OrchestratorMcpService from "./OrchestratorMcpService.ts"; |
| 25 | import { PreviewControlsToolkit } from "./toolkits/previewControls/tools.ts"; | 25 | import { PreviewControlsToolkit } from "./toolkits/previewControls/tools.ts"; |
| 26 | import { PreviewControlsHandlersLive } from "./toolkits/previewControls/handlers.ts"; | 26 | import * as PreviewControlsHandlers from "./toolkits/previewControls/handlers.ts"; |
| 27 | import { EnvironmentToolkit } from "./toolkits/environment/tools.ts"; | 27 | import { EnvironmentToolkit } from "./toolkits/environment/tools.ts"; |
| 28 | import { EnvironmentHandlersLive } from "./toolkits/environment/handlers.ts"; | 28 | import * as EnvironmentHandlers from "./toolkits/environment/handlers.ts"; |
| 29 | import { ProjectToolkit } from "./toolkits/project/tools.ts"; | 29 | import { ProjectToolkit } from "./toolkits/project/tools.ts"; |
| 30 | import { ProjectHandlersLive } from "./toolkits/project/handlers.ts"; | 30 | import * as ProjectHandlers from "./toolkits/project/handlers.ts"; |
| 31 | import { AttachmentToolkit } from "./toolkits/attachment/tools.ts"; | 31 | import { AttachmentToolkit } from "./toolkits/attachment/tools.ts"; |
| 32 | import { AttachmentHandlersLive } from "./toolkits/attachment/handlers.ts"; | 32 | import * as AttachmentHandlers from "./toolkits/attachment/handlers.ts"; |
| 33 | import { ThreadToolkit } from "./toolkits/thread/tools.ts"; | 33 | import { ThreadToolkit } from "./toolkits/thread/tools.ts"; |
| 34 | import { ThreadToolkitHandlersLive } from "./toolkits/thread/handlers.ts"; | 34 | import * as ThreadHandlers from "./toolkits/thread/handlers.ts"; |
| 35 | import * as ThreadMetadataMcpService from "./ThreadMetadataMcpService.ts"; | 35 | import * as ThreadMetadataMcpService from "./ThreadMetadataMcpService.ts"; |
| 36 | import * as McpSessionRegistry from "./McpSessionRegistry.ts"; | 36 | import * as McpSessionRegistry from "./McpSessionRegistry.ts"; |
| 37 | import * as PreviewAutomationBroker from "./PreviewAutomationBroker.ts"; | 37 | import * as PreviewAutomationBroker from "./PreviewAutomationBroker.ts"; |
| 38 | import { OrchestratorToolkitHandlersLive } from "./toolkits/orchestrator/handlers.ts"; | 38 | import * as OrchestratorHandlers from "./toolkits/orchestrator/handlers.ts"; |
| 39 | import { OrchestratorToolkit } from "./toolkits/orchestrator/tools.ts"; | 39 | import { OrchestratorToolkit } from "./toolkits/orchestrator/tools.ts"; |
| 40 | import { | 40 | import * as PreviewHandlers from "./toolkits/preview/handlers.ts"; |
| 41 | PreviewSnapshotToolkitHandlersLive, | ||
| 42 | PreviewStandardToolkitHandlersLive, | ||
| 43 | } from "./toolkits/preview/handlers.ts"; | ||
| 44 | import { | 41 | import { |
| 45 | PreviewSnapshotTool, | 42 | PreviewSnapshotTool, |
| 46 | PreviewSnapshotToolkit, | 43 | PreviewSnapshotToolkit, |
| 47 | PreviewStandardToolkit, | 44 | PreviewStandardToolkit, |
| 48 | } from "./toolkits/preview/tools.ts"; | 45 | } from "./toolkits/preview/tools.ts"; |
| 49 | import { WorktreeToolkitHandlersLive } from "./toolkits/worktree/handlers.ts"; | 46 | import * as WorktreeHandlers from "./toolkits/worktree/handlers.ts"; |
| 50 | import { WorktreeToolkit } from "./toolkits/worktree/tools.ts"; | 47 | import { WorktreeToolkit } from "./toolkits/worktree/tools.ts"; |
| 51 | import * as WorktreeMcpService from "./WorktreeMcpService.ts"; | 48 | import * as WorktreeMcpService from "./WorktreeMcpService.ts"; |
| 52 | import { PullRequestsToolkitHandlersLive } from "./toolkits/pullRequests/handlers.ts"; | 49 | import * as PullRequestsHandlers from "./toolkits/pullRequests/handlers.ts"; |
| 53 | import { PullRequestsToolkit } from "./toolkits/pullRequests/tools.ts"; | 50 | import { PullRequestsToolkit } from "./toolkits/pullRequests/tools.ts"; |
| 54 | import { | 51 | import * as DeviceHandlers from "./toolkits/device/handlers.ts"; |
| 55 | DeviceScreenshotToolkitHandlersLive, | ||
| 56 | DeviceStandardToolkitHandlersLive, | ||
| 57 | } from "./toolkits/device/handlers.ts"; | ||
| 58 | import { | 52 | import { |
| 59 | DeviceScreenshotTool, | 53 | DeviceScreenshotTool, |
| 60 | DeviceScreenshotToolkit, | 54 | DeviceScreenshotToolkit, |
| 61 | DeviceStandardToolkit, | 55 | DeviceStandardToolkit, |
| 62 | } from "./toolkits/device/tools.ts"; | 56 | } from "./toolkits/device/tools.ts"; |
| 63 | import { | 57 | import * as HtmlHandlers from "./toolkits/html/handlers.ts"; |
| 64 | HtmlPreviewToolkitHandlersLive, | ||
| 65 | HtmlRenderToolkitHandlersLive, | ||
| 66 | } from "./toolkits/html/handlers.ts"; | ||
| 67 | import { HtmlPreviewTool, HtmlPreviewToolkit, HtmlRenderToolkit } from "./toolkits/html/tools.ts"; | 58 | import { HtmlPreviewTool, HtmlPreviewToolkit, HtmlRenderToolkit } from "./toolkits/html/tools.ts"; |
| 68 | 59 | ||
| 69 | const unauthorized = HttpServerResponse.jsonUnsafe( | 60 | const unauthorized = HttpServerResponse.jsonUnsafe( |
| ... | @@ -134,7 +125,7 @@ const makeMcpAuthMiddleware = McpSessionRegistry.McpSessionRegistry.pipe( | ... | @@ -134,7 +125,7 @@ const makeMcpAuthMiddleware = McpSessionRegistry.McpSessionRegistry.pipe( |
| 134 | Effect.withSpan("McpHttpServer.makeAuthMiddleware"), | 125 | Effect.withSpan("McpHttpServer.makeAuthMiddleware"), |
| 135 | ); | 126 | ); |
| 136 | 127 | ||
| 137 | const McpAuthMiddlewareLive = HttpRouter.middleware<{ | 128 | const layerMcpAuthMiddleware = HttpRouter.middleware<{ |
| 138 | provides: McpInvocationContext.McpInvocationContext; | 129 | provides: McpInvocationContext.McpInvocationContext; |
| 139 | }>()(makeMcpAuthMiddleware).layer; | 130 | }>()(makeMcpAuthMiddleware).layer; |
| 140 | 131 | ||
| ... | @@ -687,89 +678,89 @@ const registerHtmlPreview = Effect.fn("McpHttpServer.registerHtmlPreview")(funct | ... | @@ -687,89 +678,89 @@ const registerHtmlPreview = Effect.fn("McpHttpServer.registerHtmlPreview")(funct |
| 687 | ); | 678 | ); |
| 688 | }); | 679 | }); |
| 689 | 680 | ||
| 690 | export const HtmlToolkitRegistrationLive = Layer.mergeAll( | 681 | export const layerHtmlToolkit = Layer.mergeAll( |
| 691 | McpServer.toolkit(HtmlRenderToolkit).pipe(Layer.provide(HtmlRenderToolkitHandlersLive)), | 682 | McpServer.toolkit(HtmlRenderToolkit).pipe(Layer.provide(HtmlHandlers.layerRender)), |
| 692 | Layer.effectDiscard(registerHtmlPreview()).pipe(Layer.provide(HtmlPreviewToolkitHandlersLive)), | 683 | Layer.effectDiscard(registerHtmlPreview()).pipe(Layer.provide(HtmlHandlers.layerPreview)), |
| 693 | ).pipe(Layer.provide(HtmlRender.layer), Layer.provide(PreviewBrowser.layer)); | 684 | ).pipe(Layer.provide(HtmlRender.layer), Layer.provide(PreviewBrowser.layer)); |
| 694 | 685 | ||
| 695 | const PreviewStandardToolkitRegistrationLive = McpServer.toolkit(PreviewStandardToolkit).pipe( | 686 | const layerPreviewStandardToolkitRegistration = McpServer.toolkit(PreviewStandardToolkit).pipe( |
| 696 | Layer.provide(PreviewStandardToolkitHandlersLive), | 687 | Layer.provide(PreviewHandlers.layerStandard), |
| 697 | ); | 688 | ); |
| 698 | 689 | ||
| 699 | const PreviewSnapshotRegistrationLive = Layer.effectDiscard(registerPreviewSnapshot()).pipe( | 690 | const layerPreviewSnapshotRegistration = Layer.effectDiscard(registerPreviewSnapshot()).pipe( |
| 700 | Layer.provide(PreviewSnapshotToolkitHandlersLive), | 691 | Layer.provide(PreviewHandlers.layerSnapshot), |
| 701 | ); | 692 | ); |
| 702 | 693 | ||
| 703 | export const PreviewToolkitRegistrationLive = Layer.mergeAll( | 694 | export const layerPreviewToolkit = Layer.mergeAll( |
| 704 | PreviewStandardToolkitRegistrationLive, | 695 | layerPreviewStandardToolkitRegistration, |
| 705 | PreviewSnapshotRegistrationLive, | 696 | layerPreviewSnapshotRegistration, |
| 706 | ); | 697 | ); |
| 707 | 698 | ||
| 708 | export const OrchestratorToolkitRegistrationLive = McpServer.toolkit(OrchestratorToolkit).pipe( | 699 | export const layerOrchestratorToolkit = McpServer.toolkit(OrchestratorToolkit).pipe( |
| 709 | Layer.provide(OrchestratorToolkitHandlersLive), | 700 | Layer.provide(OrchestratorHandlers.layer), |
| 710 | Layer.provide(OrchestratorMcpService.layer), | 701 | Layer.provide(OrchestratorMcpService.layer), |
| 711 | Layer.provide(ThreadMetadataMcpService.layer), | 702 | Layer.provide(ThreadMetadataMcpService.layer), |
| 712 | ); | 703 | ); |
| 713 | 704 | ||
| 714 | export const ThreadToolkitRegistrationLive = McpServer.toolkit(ThreadToolkit).pipe( | 705 | export const layerThreadToolkit = McpServer.toolkit(ThreadToolkit).pipe( |
| 715 | Layer.provide(ThreadToolkitHandlersLive), | 706 | Layer.provide(ThreadHandlers.layer), |
| 716 | ); | 707 | ); |
| 717 | 708 | ||
| 718 | const WorktreeToolkitRegistrationLive = McpServer.toolkit(WorktreeToolkit).pipe( | 709 | const layerWorktreeToolkitRegistration = McpServer.toolkit(WorktreeToolkit).pipe( |
| 719 | Layer.provide(WorktreeToolkitHandlersLive), | 710 | Layer.provide(WorktreeHandlers.layer), |
| 720 | Layer.provide(WorktreeMcpService.layer), | 711 | Layer.provide(WorktreeMcpService.layer), |
| 721 | ); | 712 | ); |
| 722 | 713 | ||
| 723 | const PreviewControlsRegistrationLive = McpServer.toolkit(PreviewControlsToolkit).pipe( | 714 | const layerPreviewControlsRegistration = McpServer.toolkit(PreviewControlsToolkit).pipe( |
| 724 | Layer.provide(PreviewControlsHandlersLive), | 715 | Layer.provide(PreviewControlsHandlers.layer), |
| 725 | ); | 716 | ); |
| 726 | 717 | ||
| 727 | const EnvironmentRegistrationLive = McpServer.toolkit(EnvironmentToolkit).pipe( | 718 | const layerEnvironmentRegistration = McpServer.toolkit(EnvironmentToolkit).pipe( |
| 728 | Layer.provide(EnvironmentHandlersLive), | 719 | Layer.provide(EnvironmentHandlers.layer), |
| 729 | ); | 720 | ); |
| 730 | 721 | ||
| 731 | const ProjectRegistrationLive = McpServer.toolkit(ProjectToolkit).pipe( | 722 | const layerProjectRegistration = McpServer.toolkit(ProjectToolkit).pipe( |
| 732 | Layer.provide(ProjectHandlersLive), | 723 | Layer.provide(ProjectHandlers.layer), |
| 733 | ); | 724 | ); |
| 734 | 725 | ||
| 735 | const AttachmentRegistrationLive = McpServer.toolkit(AttachmentToolkit).pipe( | 726 | const layerAttachmentRegistration = McpServer.toolkit(AttachmentToolkit).pipe( |
| 736 | Layer.provide(AttachmentHandlersLive), | 727 | Layer.provide(AttachmentHandlers.layer), |
| 737 | ); | 728 | ); |
| 738 | 729 | ||
| 739 | export const PullRequestsToolkitRegistrationLive = McpServer.toolkit(PullRequestsToolkit).pipe( | 730 | export const layerPullRequestsToolkit = McpServer.toolkit(PullRequestsToolkit).pipe( |
| 740 | Layer.provide(PullRequestsToolkitHandlersLive), | 731 | Layer.provide(PullRequestsHandlers.layer), |
| 741 | ); | 732 | ); |
| 742 | 733 | ||
| 743 | const DeviceStandardToolkitRegistrationLive = McpServer.toolkit(DeviceStandardToolkit).pipe( | 734 | const layerDeviceStandardToolkitRegistration = McpServer.toolkit(DeviceStandardToolkit).pipe( |
| 744 | Layer.provide(DeviceStandardToolkitHandlersLive), | 735 | Layer.provide(DeviceHandlers.layerStandard), |
| 745 | ); | 736 | ); |
| 746 | 737 | ||
| 747 | const DeviceScreenshotRegistrationLive = Layer.effectDiscard(registerDeviceScreenshot()).pipe( | 738 | const layerDeviceScreenshotRegistration = Layer.effectDiscard(registerDeviceScreenshot()).pipe( |
| 748 | Layer.provide(DeviceScreenshotToolkitHandlersLive), | 739 | Layer.provide(DeviceHandlers.layerScreenshot), |
| 749 | ); | 740 | ); |
| 750 | 741 | ||
| 751 | export const DeviceToolkitRegistrationLive = Layer.mergeAll( | 742 | export const layerDeviceToolkit = Layer.mergeAll( |
| 752 | DeviceStandardToolkitRegistrationLive, | 743 | layerDeviceStandardToolkitRegistration, |
| 753 | DeviceScreenshotRegistrationLive, | 744 | layerDeviceScreenshotRegistration, |
| 754 | ); | 745 | ); |
| 755 | 746 | ||
| 756 | const McpTransportLive = McpServer.layerHttp({ | 747 | const layerMcpTransport = McpServer.layerHttp({ |
| 757 | name: "T3 Code", | 748 | name: "T3 Code", |
| 758 | version: packageJson.version, | 749 | version: packageJson.version, |
| 759 | path: "/mcp", | 750 | path: "/mcp", |
| 760 | protocols: [McpProtocol.v2025_06_18], | 751 | protocols: [McpProtocol.v2025_06_18], |
| 761 | }).pipe(Layer.provide(McpAuthMiddlewareLive)); | 752 | }).pipe(Layer.provide(layerMcpAuthMiddleware)); |
| 762 | 753 | ||
| 763 | export const layer = Layer.mergeAll( | 754 | export const layer = Layer.mergeAll( |
| 764 | PreviewToolkitRegistrationLive, | 755 | layerPreviewToolkit, |
| 765 | OrchestratorToolkitRegistrationLive, | 756 | layerOrchestratorToolkit, |
| 766 | ThreadToolkitRegistrationLive, | 757 | layerThreadToolkit, |
| 767 | AttachmentRegistrationLive, | 758 | layerAttachmentRegistration, |
| 768 | ProjectRegistrationLive, | 759 | layerProjectRegistration, |
| 769 | EnvironmentRegistrationLive, | 760 | layerEnvironmentRegistration, |
| 770 | PreviewControlsRegistrationLive, | 761 | layerPreviewControlsRegistration, |
| 771 | WorktreeToolkitRegistrationLive, | 762 | layerWorktreeToolkitRegistration, |
| 772 | PullRequestsToolkitRegistrationLive, | 763 | layerPullRequestsToolkit, |
| 773 | DeviceToolkitRegistrationLive, | 764 | layerDeviceToolkit, |
| 774 | HtmlToolkitRegistrationLive, | 765 | layerHtmlToolkit, |
| 775 | ).pipe(Layer.provideMerge(McpTransportLive)); | 766 | ).pipe(Layer.provideMerge(layerMcpTransport)); |
apps/server/src/mcp/OrchestratorMcpService.test.ts+28-22| ... | @@ -78,7 +78,7 @@ describe("OrchestratorMcpService", () => { | ... | @@ -78,7 +78,7 @@ describe("OrchestratorMcpService", () => { |
| 78 | providerThreads: [], | 78 | providerThreads: [], |
| 79 | } as unknown as OrchestrationV2ThreadProjection; | 79 | } as unknown as OrchestrationV2ThreadProjection; |
| 80 | let hasNestedWork = true; | 80 | let hasNestedWork = true; |
| 81 | const dependencies = Layer.mergeAll( | 81 | const layerDependencies = Layer.mergeAll( |
| 82 | NodeServices.layer, | 82 | NodeServices.layer, |
| 83 | Layer.mock(ThreadManagementService.ThreadManagementService)({ | 83 | Layer.mock(ThreadManagementService.ThreadManagementService)({ |
| 84 | getThreadRecords: (threadId) => | 84 | getThreadRecords: (threadId) => |
| ... | @@ -156,7 +156,7 @@ describe("OrchestratorMcpService", () => { | ... | @@ -156,7 +156,7 @@ describe("OrchestratorMcpService", () => { |
| 156 | const commandIds = yield* Ref.get(acknowledgementCommandIds); | 156 | const commandIds = yield* Ref.get(acknowledgementCommandIds); |
| 157 | assert.equal(commandIds.length, 2); | 157 | assert.equal(commandIds.length, 2); |
| 158 | assert.notEqual(commandIds[0], commandIds[1]); | 158 | assert.notEqual(commandIds[0], commandIds[1]); |
| 159 | }).pipe(Effect.provide(OrchestratorMcpService.layer.pipe(Layer.provide(dependencies)))); | 159 | }).pipe(Effect.provide(OrchestratorMcpService.layer.pipe(Layer.provide(layerDependencies)))); |
| 160 | }), | 160 | }), |
| 161 | ); | 161 | ); |
| 162 | 162 | ||
| ... | @@ -195,7 +195,7 @@ describe("OrchestratorMcpService", () => { | ... | @@ -195,7 +195,7 @@ describe("OrchestratorMcpService", () => { |
| 195 | providerThreads: [], | 195 | providerThreads: [], |
| 196 | turnItems: [], | 196 | turnItems: [], |
| 197 | } as unknown as OrchestrationV2ThreadProjection; | 197 | } as unknown as OrchestrationV2ThreadProjection; |
| 198 | const dependencies = Layer.mergeAll( | 198 | const layerDependencies = Layer.mergeAll( |
| 199 | NodeServices.layer, | 199 | NodeServices.layer, |
| 200 | Layer.mock(ThreadManagementService.ThreadManagementService)({ | 200 | Layer.mock(ThreadManagementService.ThreadManagementService)({ |
| 201 | getThreadRecords: (threadId) => | 201 | getThreadRecords: (threadId) => |
| ... | @@ -243,7 +243,7 @@ describe("OrchestratorMcpService", () => { | ... | @@ -243,7 +243,7 @@ describe("OrchestratorMcpService", () => { |
| 243 | const settled = yield* service.taskStatus(scope, taskId); | 243 | const settled = yield* service.taskStatus(scope, taskId); |
| 244 | assert.equal(settled.status, "cancelled"); | 244 | assert.equal(settled.status, "cancelled"); |
| 245 | assert.equal(yield* Ref.get(dispatched), 1); | 245 | assert.equal(yield* Ref.get(dispatched), 1); |
| 246 | }).pipe(Effect.provide(OrchestratorMcpService.layer.pipe(Layer.provide(dependencies)))); | 246 | }).pipe(Effect.provide(OrchestratorMcpService.layer.pipe(Layer.provide(layerDependencies)))); |
| 247 | }), | 247 | }), |
| 248 | ); | 248 | ); |
| 249 | 249 | ||
| ... | @@ -278,7 +278,7 @@ describe("OrchestratorMcpService", () => { | ... | @@ -278,7 +278,7 @@ describe("OrchestratorMcpService", () => { |
| 278 | subagents: [], | 278 | subagents: [], |
| 279 | providerThreads: [], | 279 | providerThreads: [], |
| 280 | } as unknown as OrchestrationV2ThreadProjection; | 280 | } as unknown as OrchestrationV2ThreadProjection; |
| 281 | const dependencies = Layer.mergeAll( | 281 | const layerDependencies = Layer.mergeAll( |
| 282 | NodeServices.layer, | 282 | NodeServices.layer, |
| 283 | Layer.mock(ThreadManagementService.ThreadManagementService)({ | 283 | Layer.mock(ThreadManagementService.ThreadManagementService)({ |
| 284 | getThreadRecords: (threadId) => | 284 | getThreadRecords: (threadId) => |
| ... | @@ -316,7 +316,7 @@ describe("OrchestratorMcpService", () => { | ... | @@ -316,7 +316,7 @@ describe("OrchestratorMcpService", () => { |
| 316 | .pipe(Effect.flip); | 316 | .pipe(Effect.flip); |
| 317 | assert.equal(error.code, "task_not_cancellable"); | 317 | assert.equal(error.code, "task_not_cancellable"); |
| 318 | assert.deepEqual(yield* Ref.get(dispatched), []); | 318 | assert.deepEqual(yield* Ref.get(dispatched), []); |
| 319 | }).pipe(Effect.provide(OrchestratorMcpService.layer.pipe(Layer.provide(dependencies)))); | 319 | }).pipe(Effect.provide(OrchestratorMcpService.layer.pipe(Layer.provide(layerDependencies)))); |
| 320 | }), | 320 | }), |
| 321 | ); | 321 | ); |
| 322 | 322 | ||
| ... | @@ -352,7 +352,7 @@ describe("OrchestratorMcpService", () => { | ... | @@ -352,7 +352,7 @@ describe("OrchestratorMcpService", () => { |
| 352 | subagents: [], | 352 | subagents: [], |
| 353 | providerThreads: [], | 353 | providerThreads: [], |
| 354 | } as unknown as OrchestrationV2ThreadProjection; | 354 | } as unknown as OrchestrationV2ThreadProjection; |
| 355 | const dependencies = Layer.mergeAll( | 355 | const layerDependencies = Layer.mergeAll( |
| 356 | NodeServices.layer, | 356 | NodeServices.layer, |
| 357 | Layer.mock(ThreadManagementService.ThreadManagementService)({ | 357 | Layer.mock(ThreadManagementService.ThreadManagementService)({ |
| 358 | getThreadRecords: (threadId) => | 358 | getThreadRecords: (threadId) => |
| ... | @@ -393,7 +393,7 @@ describe("OrchestratorMcpService", () => { | ... | @@ -393,7 +393,7 @@ describe("OrchestratorMcpService", () => { |
| 393 | (yield* Ref.get(dispatched)).map((command) => (command as { type: string }).type), | 393 | (yield* Ref.get(dispatched)).map((command) => (command as { type: string }).type), |
| 394 | ["thread.stop"], | 394 | ["thread.stop"], |
| 395 | ); | 395 | ); |
| 396 | }).pipe(Effect.provide(OrchestratorMcpService.layer.pipe(Layer.provide(dependencies)))); | 396 | }).pipe(Effect.provide(OrchestratorMcpService.layer.pipe(Layer.provide(layerDependencies)))); |
| 397 | }), | 397 | }), |
| 398 | ); | 398 | ); |
| 399 | 399 | ||
| ... | @@ -429,7 +429,7 @@ describe("OrchestratorMcpService", () => { | ... | @@ -429,7 +429,7 @@ describe("OrchestratorMcpService", () => { |
| 429 | subagents: [], | 429 | subagents: [], |
| 430 | providerThreads: [], | 430 | providerThreads: [], |
| 431 | } as unknown as OrchestrationV2ThreadProjection; | 431 | } as unknown as OrchestrationV2ThreadProjection; |
| 432 | const dependencies = Layer.mergeAll( | 432 | const layerDependencies = Layer.mergeAll( |
| 433 | NodeServices.layer, | 433 | NodeServices.layer, |
| 434 | Layer.mock(ThreadManagementService.ThreadManagementService)({ | 434 | Layer.mock(ThreadManagementService.ThreadManagementService)({ |
| 435 | getThreadRecords: (threadId) => | 435 | getThreadRecords: (threadId) => |
| ... | @@ -476,7 +476,7 @@ describe("OrchestratorMcpService", () => { | ... | @@ -476,7 +476,7 @@ describe("OrchestratorMcpService", () => { |
| 476 | (yield* Ref.get(dispatched)).map((command) => (command as { type: string }).type), | 476 | (yield* Ref.get(dispatched)).map((command) => (command as { type: string }).type), |
| 477 | ["thread.stop", "delegated_task.completion-delivery.dispose"], | 477 | ["thread.stop", "delegated_task.completion-delivery.dispose"], |
| 478 | ); | 478 | ); |
| 479 | }).pipe(Effect.provide(OrchestratorMcpService.layer.pipe(Layer.provide(dependencies)))); | 479 | }).pipe(Effect.provide(OrchestratorMcpService.layer.pipe(Layer.provide(layerDependencies)))); |
| 480 | }), | 480 | }), |
| 481 | ); | 481 | ); |
| 482 | }); | 482 | }); |
| ... | @@ -641,7 +641,7 @@ describe("OrchestratorMcpService provider resolution", () => { | ... | @@ -641,7 +641,7 @@ describe("OrchestratorMcpService provider resolution", () => { |
| 641 | }), | 641 | }), |
| 642 | forkShadow, | 642 | forkShadow, |
| 643 | ]; | 643 | ]; |
| 644 | const dependencies = Layer.mergeAll( | 644 | const layerDependencies = Layer.mergeAll( |
| 645 | NodeServices.layer, | 645 | NodeServices.layer, |
| 646 | Layer.mock(ThreadManagementService.ThreadManagementService)({ | 646 | Layer.mock(ThreadManagementService.ThreadManagementService)({ |
| 647 | getThreadRecords: () => Effect.succeed(parentProjection([])), | 647 | getThreadRecords: () => Effect.succeed(parentProjection([])), |
| ... | @@ -729,7 +729,9 @@ describe("OrchestratorMcpService provider resolution", () => { | ... | @@ -729,7 +729,9 @@ describe("OrchestratorMcpService provider resolution", () => { |
| 729 | assert.isTrue( | 729 | assert.isTrue( |
| 730 | fork!.constraints.includes("Driver 'forkOnly' is not registered in this build."), | 730 | fork!.constraints.includes("Driver 'forkOnly' is not registered in this build."), |
| 731 | ); | 731 | ); |
| 732 | }).pipe(Effect.provide(OrchestratorMcpService.layer.pipe(Layer.provide(dependencies)))); | 732 | }).pipe( |
| 733 | Effect.provide(OrchestratorMcpService.layer.pipe(Layer.provide(layerDependencies))), | ||
| 734 | ); | ||
| 733 | }), | 735 | }), |
| 734 | ); | 736 | ); |
| 735 | 737 | ||
| ... | @@ -759,7 +761,7 @@ describe("OrchestratorMcpService provider resolution", () => { | ... | @@ -759,7 +761,7 @@ describe("OrchestratorMcpService provider resolution", () => { |
| 759 | completedAt: null, | 761 | completedAt: null, |
| 760 | }; | 762 | }; |
| 761 | const dispatched = yield* Ref.make<ReadonlyArray<unknown>>([]); | 763 | const dispatched = yield* Ref.make<ReadonlyArray<unknown>>([]); |
| 762 | const dependencies = Layer.mergeAll( | 764 | const layerDependencies = Layer.mergeAll( |
| 763 | NodeServices.layer, | 765 | NodeServices.layer, |
| 764 | Layer.mock(ThreadManagementService.ThreadManagementService)({ | 766 | Layer.mock(ThreadManagementService.ThreadManagementService)({ |
| 765 | getThreadRecords: (threadId) => | 767 | getThreadRecords: (threadId) => |
| ... | @@ -826,7 +828,9 @@ describe("OrchestratorMcpService provider resolution", () => { | ... | @@ -826,7 +828,9 @@ describe("OrchestratorMcpService provider resolution", () => { |
| 826 | assert.equal(request.type, "delegated_task.request"); | 828 | assert.equal(request.type, "delegated_task.request"); |
| 827 | assert.equal(request.modelSelection.instanceId, antigravityInstanceId); | 829 | assert.equal(request.modelSelection.instanceId, antigravityInstanceId); |
| 828 | assert.equal(request.modelSelection.model, "ant-model"); | 830 | assert.equal(request.modelSelection.model, "ant-model"); |
| 829 | }).pipe(Effect.provide(OrchestratorMcpService.layer.pipe(Layer.provide(dependencies)))); | 831 | }).pipe( |
| 832 | Effect.provide(OrchestratorMcpService.layer.pipe(Layer.provide(layerDependencies))), | ||
| 833 | ); | ||
| 830 | }), | 834 | }), |
| 831 | ); | 835 | ); |
| 832 | 836 | ||
| ... | @@ -854,7 +858,7 @@ describe("OrchestratorMcpService provider resolution", () => { | ... | @@ -854,7 +858,7 @@ describe("OrchestratorMcpService provider resolution", () => { |
| 854 | completedAt: null, | 858 | completedAt: null, |
| 855 | }; | 859 | }; |
| 856 | let delegated = false; | 860 | let delegated = false; |
| 857 | const dependencies = Layer.mergeAll( | 861 | const layerDependencies = Layer.mergeAll( |
| 858 | NodeServices.layer, | 862 | NodeServices.layer, |
| 859 | Layer.mock(ThreadManagementService.ThreadManagementService)({ | 863 | Layer.mock(ThreadManagementService.ThreadManagementService)({ |
| 860 | getThreadRecords: (threadId) => | 864 | getThreadRecords: (threadId) => |
| ... | @@ -918,7 +922,7 @@ describe("OrchestratorMcpService provider resolution", () => { | ... | @@ -918,7 +922,7 @@ describe("OrchestratorMcpService provider resolution", () => { |
| 918 | }; | 922 | }; |
| 919 | assert.equal(request.modelSelection.instanceId, antigravityInstanceId); | 923 | assert.equal(request.modelSelection.instanceId, antigravityInstanceId); |
| 920 | assert.equal(request.modelSelection.model, "ant-model"); | 924 | assert.equal(request.modelSelection.model, "ant-model"); |
| 921 | }).pipe(Effect.provide(OrchestratorMcpService.layer.pipe(Layer.provide(dependencies)))); | 925 | }).pipe(Effect.provide(OrchestratorMcpService.layer.pipe(Layer.provide(layerDependencies)))); |
| 922 | }), | 926 | }), |
| 923 | ); | 927 | ); |
| 924 | 928 | ||
| ... | @@ -931,7 +935,7 @@ describe("OrchestratorMcpService provider resolution", () => { | ... | @@ -931,7 +935,7 @@ describe("OrchestratorMcpService provider resolution", () => { |
| 931 | reason: "Driver 'forkOnly' is not registered in this build.", | 935 | reason: "Driver 'forkOnly' is not registered in this build.", |
| 932 | checkedAt: "2026-09-13T00:00:00.000Z", | 936 | checkedAt: "2026-09-13T00:00:00.000Z", |
| 933 | }); | 937 | }); |
| 934 | const dependencies = Layer.mergeAll( | 938 | const layerDependencies = Layer.mergeAll( |
| 935 | NodeServices.layer, | 939 | NodeServices.layer, |
| 936 | Layer.mock(ThreadManagementService.ThreadManagementService)({ | 940 | Layer.mock(ThreadManagementService.ThreadManagementService)({ |
| 937 | getThreadRecords: () => Effect.succeed(parentProjection([])), | 941 | getThreadRecords: () => Effect.succeed(parentProjection([])), |
| ... | @@ -975,7 +979,7 @@ describe("OrchestratorMcpService provider resolution", () => { | ... | @@ -975,7 +979,7 @@ describe("OrchestratorMcpService provider resolution", () => { |
| 975 | assert.isTrue( | 979 | assert.isTrue( |
| 976 | byDriver.message.includes("No V2 provider adapter is registered for driver forkOnly."), | 980 | byDriver.message.includes("No V2 provider adapter is registered for driver forkOnly."), |
| 977 | ); | 981 | ); |
| 978 | }).pipe(Effect.provide(OrchestratorMcpService.layer.pipe(Layer.provide(dependencies)))); | 982 | }).pipe(Effect.provide(OrchestratorMcpService.layer.pipe(Layer.provide(layerDependencies)))); |
| 979 | }), | 983 | }), |
| 980 | ); | 984 | ); |
| 981 | 985 | ||
| ... | @@ -1023,7 +1027,7 @@ describe("OrchestratorMcpService provider resolution", () => { | ... | @@ -1023,7 +1027,7 @@ describe("OrchestratorMcpService provider resolution", () => { |
| 1023 | let cliInstalled = false; | 1027 | let cliInstalled = false; |
| 1024 | const probes = yield* Ref.make(0); | 1028 | const probes = yield* Ref.make(0); |
| 1025 | const dispatched = yield* Ref.make(0); | 1029 | const dispatched = yield* Ref.make(0); |
| 1026 | const dependencies = Layer.mergeAll( | 1030 | const layerDependencies = Layer.mergeAll( |
| 1027 | NodeServices.layer, | 1031 | NodeServices.layer, |
| 1028 | Layer.mock(ThreadManagementService.ThreadManagementService)({ | 1032 | Layer.mock(ThreadManagementService.ThreadManagementService)({ |
| 1029 | getThreadRecords: (threadId) => | 1033 | getThreadRecords: (threadId) => |
| ... | @@ -1077,7 +1081,7 @@ describe("OrchestratorMcpService provider resolution", () => { | ... | @@ -1077,7 +1081,7 @@ describe("OrchestratorMcpService provider resolution", () => { |
| 1077 | assert.equal(result.providerInstanceId, claudeInstanceId); | 1081 | assert.equal(result.providerInstanceId, claudeInstanceId); |
| 1078 | assert.equal(yield* Ref.get(probes), 2); | 1082 | assert.equal(yield* Ref.get(probes), 2); |
| 1079 | assert.equal(yield* Ref.get(dispatched), 1); | 1083 | assert.equal(yield* Ref.get(dispatched), 1); |
| 1080 | }).pipe(Effect.provide(OrchestratorMcpService.layer.pipe(Layer.provide(dependencies)))); | 1084 | }).pipe(Effect.provide(OrchestratorMcpService.layer.pipe(Layer.provide(layerDependencies)))); |
| 1081 | }), | 1085 | }), |
| 1082 | ); | 1086 | ); |
| 1083 | 1087 | ||
| ... | @@ -1167,7 +1171,7 @@ describe("OrchestratorMcpService provider resolution", () => { | ... | @@ -1167,7 +1171,7 @@ describe("OrchestratorMcpService provider resolution", () => { |
| 1167 | for (const testCase of cases) { | 1171 | for (const testCase of cases) { |
| 1168 | const dispatched = yield* Ref.make<ReadonlyArray<unknown>>([]); | 1172 | const dispatched = yield* Ref.make<ReadonlyArray<unknown>>([]); |
| 1169 | let delegated = false; | 1173 | let delegated = false; |
| 1170 | const dependencies = Layer.mergeAll( | 1174 | const layerDependencies = Layer.mergeAll( |
| 1171 | NodeServices.layer, | 1175 | NodeServices.layer, |
| 1172 | Layer.mock(ThreadManagementService.ThreadManagementService)({ | 1176 | Layer.mock(ThreadManagementService.ThreadManagementService)({ |
| 1173 | getThreadRecords: (threadId) => | 1177 | getThreadRecords: (threadId) => |
| ... | @@ -1284,7 +1288,9 @@ describe("OrchestratorMcpService provider resolution", () => { | ... | @@ -1284,7 +1288,9 @@ describe("OrchestratorMcpService provider resolution", () => { |
| 1284 | } else { | 1288 | } else { |
| 1285 | assert.equal(request.modelSelection.model, "codex-alt-model", testCase.name); | 1289 | assert.equal(request.modelSelection.model, "codex-alt-model", testCase.name); |
| 1286 | } | 1290 | } |
| 1287 | }).pipe(Effect.provide(OrchestratorMcpService.layer.pipe(Layer.provide(dependencies)))); | 1291 | }).pipe( |
| 1292 | Effect.provide(OrchestratorMcpService.layer.pipe(Layer.provide(layerDependencies))), | ||
| 1293 | ); | ||
| 1288 | } | 1294 | } |
| 1289 | }), | 1295 | }), |
| 1290 | ); | 1296 | ); |
apps/server/src/mcp/OrchestratorMcpToolkit.integration.test.ts+36-39| ... | @@ -64,15 +64,12 @@ import { | ... | @@ -64,15 +64,12 @@ import { |
| 64 | import * as ProviderAdapterRegistry from "../orchestration-v2/ProviderAdapterRegistry.ts"; | 64 | import * as ProviderAdapterRegistry from "../orchestration-v2/ProviderAdapterRegistry.ts"; |
| 65 | import * as ProviderContinuationRequests from "../orchestration-v2/ProviderContinuationRequests.ts"; | 65 | import * as ProviderContinuationRequests from "../orchestration-v2/ProviderContinuationRequests.ts"; |
| 66 | import { checkpointWorkspace } from "../orchestration-v2/testkit/ReplayFixtureWorkspace.ts"; | 66 | import { checkpointWorkspace } from "../orchestration-v2/testkit/ReplayFixtureWorkspace.ts"; |
| 67 | import { | 67 | import * as ProviderReplayHarness from "../orchestration-v2/testkit/ProviderReplayHarness.ts"; |
| 68 | makeOrchestratorV2ProviderReplayLayer, | ||
| 69 | makeOrchestratorV2ReplayLayerWithRegistry, | ||
| 70 | } from "../orchestration-v2/testkit/ProviderReplayHarness.ts"; | ||
| 71 | import { | 68 | import { |
| 72 | decodeProviderReplayNdjson, | 69 | decodeProviderReplayNdjson, |
| 73 | materializeReplayTranscriptWorkspace, | 70 | materializeReplayTranscriptWorkspace, |
| 74 | } from "../orchestration-v2/testkit/ReplayTranscriptNdjson.ts"; | 71 | } from "../orchestration-v2/testkit/ReplayTranscriptNdjson.ts"; |
| 75 | import { makeProviderRegistryLayer } from "../provider/testUtils/providerRegistryMock.ts"; | 72 | import * as ProviderRegistryMock from "../provider/testUtils/providerRegistryMock.ts"; |
| 76 | import * as ProjectService from "../project/ProjectService.ts"; | 73 | import * as ProjectService from "../project/ProjectService.ts"; |
| 77 | import * as ScheduledTaskService from "../scheduledTasks/ScheduledTaskService.ts"; | 74 | import * as ScheduledTaskService from "../scheduledTasks/ScheduledTaskService.ts"; |
| 78 | import * as SecretRequests from "../secrets/SecretRequests.ts"; | 75 | import * as SecretRequests from "../secrets/SecretRequests.ts"; |
| ... | @@ -480,7 +477,7 @@ function scheduledTaskFromUpsert(input: ScheduledTaskUpsertInput): ScheduledTask | ... | @@ -480,7 +477,7 @@ function scheduledTaskFromUpsert(input: ScheduledTaskUpsertInput): ScheduledTask |
| 480 | } | 477 | } |
| 481 | 478 | ||
| 482 | /** In-memory server secret store for tests that exercise secret requests. */ | 479 | /** In-memory server secret store for tests that exercise secret requests. */ |
| 483 | const memorySecretStoreLayer = Layer.sync(ServerSecretStore.ServerSecretStore, () => { | 480 | const layerMemorySecretStore = Layer.sync(ServerSecretStore.ServerSecretStore, () => { |
| 484 | const stored = new Map<string, Uint8Array>(); | 481 | const stored = new Map<string, Uint8Array>(); |
| 485 | return ServerSecretStore.ServerSecretStore.of({ | 482 | return ServerSecretStore.ServerSecretStore.of({ |
| 486 | get: (name) => Effect.succeed(Option.fromNullishOr(stored.get(name))), | 483 | get: (name) => Effect.succeed(Option.fromNullishOr(stored.get(name))), |
| ... | @@ -498,7 +495,7 @@ const memorySecretStoreLayer = Layer.sync(ServerSecretStore.ServerSecretStore, ( | ... | @@ -498,7 +495,7 @@ const memorySecretStoreLayer = Layer.sync(ServerSecretStore.ServerSecretStore, ( |
| 498 | }); | 495 | }); |
| 499 | }); | 496 | }); |
| 500 | 497 | ||
| 501 | const unusedScheduledTaskStubLayer = Layer.succeed( | 498 | const layerUnusedScheduledTaskStub = Layer.succeed( |
| 502 | ScheduledTaskService.ScheduledTaskService, | 499 | ScheduledTaskService.ScheduledTaskService, |
| 503 | ScheduledTaskService.ScheduledTaskService.of({ | 500 | ScheduledTaskService.ScheduledTaskService.of({ |
| 504 | list: () => Effect.succeed({ tasks: [] }), | 501 | list: () => Effect.succeed({ tasks: [] }), |
| ... | @@ -524,7 +521,7 @@ describe("orchestrator MCP toolkit", () => { | ... | @@ -524,7 +521,7 @@ describe("orchestrator MCP toolkit", () => { |
| 524 | const capturedTurns = yield* Ref.make<ReadonlyArray<CapturedTurn>>([]); | 521 | const capturedTurns = yield* Ref.make<ReadonlyArray<CapturedTurn>>([]); |
| 525 | const parentTerminalGates = new Map<ThreadId, Deferred.Deferred<void>>(); | 522 | const parentTerminalGates = new Map<ThreadId, Deferred.Deferred<void>>(); |
| 526 | const deliveryTerminalGates = new Map<ThreadId, Deferred.Deferred<void>>(); | 523 | const deliveryTerminalGates = new Map<ThreadId, Deferred.Deferred<void>>(); |
| 527 | const registryLayer = ProviderAdapterRegistry.makeLayer([ | 524 | const layerRegistry = ProviderAdapterRegistry.layerFromAdapters([ |
| 528 | makeDeterministicAdapter({ | 525 | makeDeterministicAdapter({ |
| 529 | instanceId: codexInstanceId, | 526 | instanceId: codexInstanceId, |
| 530 | driver: ProviderDriverKind.make("codex"), | 527 | driver: ProviderDriverKind.make("codex"), |
| ... | @@ -566,7 +563,7 @@ describe("orchestrator MCP toolkit", () => { | ... | @@ -566,7 +563,7 @@ describe("orchestrator MCP toolkit", () => { |
| 566 | const continuationOffers = yield* Ref.make< | 563 | const continuationOffers = yield* Ref.make< |
| 567 | ReadonlyArray<ProviderContinuationRequests.ProviderContinuationRequest> | 564 | ReadonlyArray<ProviderContinuationRequests.ProviderContinuationRequest> |
| 568 | >([]); | 565 | >([]); |
| 569 | const continuationProbeLayer = Layer.succeed( | 566 | const layerContinuationProbe = Layer.succeed( |
| 570 | ProviderContinuationRequests.ProviderContinuationRequests, | 567 | ProviderContinuationRequests.ProviderContinuationRequests, |
| 571 | { | 568 | { |
| 572 | offer: (request) => | 569 | offer: (request) => |
| ... | @@ -596,7 +593,7 @@ describe("orchestrator MCP toolkit", () => { | ... | @@ -596,7 +593,7 @@ describe("orchestrator MCP toolkit", () => { |
| 596 | expect(yield* Ref.get(continuationOffers)).toHaveLength(count); | 593 | expect(yield* Ref.get(continuationOffers)).toHaveLength(count); |
| 597 | } | 594 | } |
| 598 | }); | 595 | }); |
| 599 | const orchestratorLayer = makeOrchestratorV2ReplayLayerWithRegistry( | 596 | const layerOrchestrator = ProviderReplayHarness.layerWithRegistry( |
| 600 | { | 597 | { |
| 601 | name: "orchestrator-mcp-toolkit", | 598 | name: "orchestrator-mcp-toolkit", |
| 602 | runtimePolicyOverride: { | 599 | runtimePolicyOverride: { |
| ... | @@ -609,13 +606,13 @@ describe("orchestrator MCP toolkit", () => { | ... | @@ -609,13 +606,13 @@ describe("orchestrator MCP toolkit", () => { |
| 609 | }, | 606 | }, |
| 610 | }, | 607 | }, |
| 611 | }, | 608 | }, |
| 612 | registryLayer, | 609 | layerRegistry, |
| 613 | ).pipe(Layer.provide(continuationProbeLayer)); | 610 | ).pipe(Layer.provide(layerContinuationProbe)); |
| 614 | const orchestrationLayer = Layer.merge( | 611 | const layerOrchestration = Layer.merge( |
| 615 | orchestratorLayer, | 612 | layerOrchestrator, |
| 616 | ThreadManagementService.layer.pipe(Layer.provide(orchestratorLayer)), | 613 | ThreadManagementService.layer.pipe(Layer.provide(layerOrchestrator)), |
| 617 | ); | 614 | ); |
| 618 | const providerRegistryLayer = makeProviderRegistryLayer([ | 615 | const layerProviderRegistry = ProviderRegistryMock.layer([ |
| 619 | makeProviderSnapshot({ | 616 | makeProviderSnapshot({ |
| 620 | instanceId: codexInstanceId, | 617 | instanceId: codexInstanceId, |
| 621 | driver: ProviderDriverKind.make("codex"), | 618 | driver: ProviderDriverKind.make("codex"), |
| ... | @@ -647,7 +644,7 @@ describe("orchestrator MCP toolkit", () => { | ... | @@ -647,7 +644,7 @@ describe("orchestrator MCP toolkit", () => { |
| 647 | // In-memory ScheduledTaskService stub so the schedule/list/update/ | 644 | // In-memory ScheduledTaskService stub so the schedule/list/update/ |
| 648 | // delete tools can be exercised without SQL/launch wiring. | 645 | // delete tools can be exercised without SQL/launch wiring. |
| 649 | const scheduledStore = yield* Ref.make<ReadonlyArray<ScheduledTask>>([]); | 646 | const scheduledStore = yield* Ref.make<ReadonlyArray<ScheduledTask>>([]); |
| 650 | const scheduledTaskStubLayer = Layer.succeed( | 647 | const layerScheduledTaskStub = Layer.succeed( |
| 651 | ScheduledTaskService.ScheduledTaskService, | 648 | ScheduledTaskService.ScheduledTaskService, |
| 652 | ScheduledTaskService.ScheduledTaskService.of({ | 649 | ScheduledTaskService.ScheduledTaskService.of({ |
| 653 | list: () => Ref.get(scheduledStore).pipe(Effect.map((tasks) => ({ tasks }))), | 650 | list: () => Ref.get(scheduledStore).pipe(Effect.map((tasks) => ({ tasks }))), |
| ... | @@ -674,15 +671,15 @@ describe("orchestrator MCP toolkit", () => { | ... | @@ -674,15 +671,15 @@ describe("orchestrator MCP toolkit", () => { |
| 674 | triggerWebhook: () => Effect.die("unused in this test"), | 671 | triggerWebhook: () => Effect.die("unused in this test"), |
| 675 | }), | 672 | }), |
| 676 | ); | 673 | ); |
| 677 | const testLayer = Layer.merge( | 674 | const layerTest = Layer.merge( |
| 678 | McpHttpServer.OrchestratorToolkitRegistrationLive, | 675 | McpHttpServer.layerOrchestratorToolkit, |
| 679 | McpHttpServer.ThreadToolkitRegistrationLive, | 676 | McpHttpServer.layerThreadToolkit, |
| 680 | ).pipe( | 677 | ).pipe( |
| 681 | Layer.provideMerge(McpServer.McpServer.layer), | 678 | Layer.provideMerge(McpServer.McpServer.layer), |
| 682 | Layer.provideMerge(orchestrationLayer), | 679 | Layer.provideMerge(layerOrchestration), |
| 683 | Layer.provide(registryLayer), | 680 | Layer.provide(layerRegistry), |
| 684 | Layer.provide(providerRegistryLayer), | 681 | Layer.provide(layerProviderRegistry), |
| 685 | Layer.provide(scheduledTaskStubLayer), | 682 | Layer.provide(layerScheduledTaskStub), |
| 686 | Layer.provide( | 683 | Layer.provide( |
| 687 | Layer.mock(ProjectService.ProjectService)({ | 684 | Layer.mock(ProjectService.ProjectService)({ |
| 688 | getById: (id) => | 685 | getById: (id) => |
| ... | @@ -695,8 +692,8 @@ describe("orchestrator MCP toolkit", () => { | ... | @@ -695,8 +692,8 @@ describe("orchestrator MCP toolkit", () => { |
| 695 | ), | 692 | ), |
| 696 | Layer.provideMerge( | 693 | Layer.provideMerge( |
| 697 | SecretRequests.layer.pipe( | 694 | SecretRequests.layer.pipe( |
| 698 | Layer.provide(memorySecretStoreLayer), | 695 | Layer.provide(layerMemorySecretStore), |
| 699 | Layer.provide(orchestrationLayer), | 696 | Layer.provide(layerOrchestration), |
| 700 | ), | 697 | ), |
| 701 | ), | 698 | ), |
| 702 | Layer.provide(NodeServices.layer), | 699 | Layer.provide(NodeServices.layer), |
| ... | @@ -3677,7 +3674,7 @@ describe("orchestrator MCP toolkit", () => { | ... | @@ -3677,7 +3674,7 @@ describe("orchestrator MCP toolkit", () => { |
| 3677 | thirdFanoutDelivery.messageId, | 3674 | thirdFanoutDelivery.messageId, |
| 3678 | ]), | 3675 | ]), |
| 3679 | ); | 3676 | ); |
| 3680 | }).pipe(Effect.provide(testLayer)); | 3677 | }).pipe(Effect.provide(layerTest)); |
| 3681 | }), | 3678 | }), |
| 3682 | ), | 3679 | ), |
| 3683 | ); | 3680 | ); |
| ... | @@ -3690,7 +3687,7 @@ describe("orchestrator MCP toolkit", () => { | ... | @@ -3690,7 +3687,7 @@ describe("orchestrator MCP toolkit", () => { |
| 3690 | const transcript = yield* CodexOrchestratorReplayHarness.decodeTranscript( | 3687 | const transcript = yield* CodexOrchestratorReplayHarness.decodeTranscript( |
| 3691 | materializeReplayTranscriptWorkspace(rawTranscript, cwd), | 3688 | materializeReplayTranscriptWorkspace(rawTranscript, cwd), |
| 3692 | ); | 3689 | ); |
| 3693 | const orchestratorLayer = makeOrchestratorV2ProviderReplayLayer( | 3690 | const layerOrchestrator = ProviderReplayHarness.layerProviderReplay( |
| 3694 | { | 3691 | { |
| 3695 | name: "delegated-task-status/codex", | 3692 | name: "delegated-task-status/codex", |
| 3696 | transcript, | 3693 | transcript, |
| ... | @@ -3699,30 +3696,30 @@ describe("orchestrator MCP toolkit", () => { | ... | @@ -3699,30 +3696,30 @@ describe("orchestrator MCP toolkit", () => { |
| 3699 | }, | 3696 | }, |
| 3700 | CodexOrchestratorReplayHarness, | 3697 | CodexOrchestratorReplayHarness, |
| 3701 | ); | 3698 | ); |
| 3702 | const orchestrationLayer = Layer.merge( | 3699 | const layerOrchestration = Layer.merge( |
| 3703 | orchestratorLayer, | 3700 | layerOrchestrator, |
| 3704 | ThreadManagementService.layer.pipe(Layer.provide(orchestratorLayer)), | 3701 | ThreadManagementService.layer.pipe(Layer.provide(layerOrchestrator)), |
| 3705 | ); | 3702 | ); |
| 3706 | const providerRegistryLayer = makeProviderRegistryLayer([ | 3703 | const layerProviderRegistry = ProviderRegistryMock.layer([ |
| 3707 | makeProviderSnapshot({ | 3704 | makeProviderSnapshot({ |
| 3708 | instanceId: codexInstanceId, | 3705 | instanceId: codexInstanceId, |
| 3709 | driver: ProviderDriverKind.make("codex"), | 3706 | driver: ProviderDriverKind.make("codex"), |
| 3710 | model: codexModel, | 3707 | model: codexModel, |
| 3711 | }), | 3708 | }), |
| 3712 | ]); | 3709 | ]); |
| 3713 | const testLayer = McpHttpServer.OrchestratorToolkitRegistrationLive.pipe( | 3710 | const layerTest = McpHttpServer.layerOrchestratorToolkit.pipe( |
| 3714 | Layer.provideMerge(McpServer.McpServer.layer), | 3711 | Layer.provideMerge(McpServer.McpServer.layer), |
| 3715 | Layer.provideMerge(orchestrationLayer), | 3712 | Layer.provideMerge(layerOrchestration), |
| 3716 | Layer.provide( | 3713 | Layer.provide( |
| 3717 | CodexOrchestratorReplayHarness.makeProviderAdapterRegistryLayer(transcript), | 3714 | CodexOrchestratorReplayHarness.makeProviderAdapterRegistryLayer(transcript), |
| 3718 | ), | 3715 | ), |
| 3719 | Layer.provide(providerRegistryLayer), | 3716 | Layer.provide(layerProviderRegistry), |
| 3720 | Layer.provide(unusedScheduledTaskStubLayer), | 3717 | Layer.provide(layerUnusedScheduledTaskStub), |
| 3721 | Layer.provide(Layer.mock(ProjectService.ProjectService)({})), | 3718 | Layer.provide(Layer.mock(ProjectService.ProjectService)({})), |
| 3722 | Layer.provideMerge( | 3719 | Layer.provideMerge( |
| 3723 | SecretRequests.layer.pipe( | 3720 | SecretRequests.layer.pipe( |
| 3724 | Layer.provide(memorySecretStoreLayer), | 3721 | Layer.provide(layerMemorySecretStore), |
| 3725 | Layer.provide(orchestrationLayer), | 3722 | Layer.provide(layerOrchestration), |
| 3726 | ), | 3723 | ), |
| 3727 | ), | 3724 | ), |
| 3728 | Layer.provide(NodeServices.layer), | 3725 | Layer.provide(NodeServices.layer), |
| ... | @@ -3998,7 +3995,7 @@ describe("orchestrator MCP toolkit", () => { | ... | @@ -3998,7 +3995,7 @@ describe("orchestrator MCP toolkit", () => { |
| 3998 | latestTerminalSummary: queuedFollowupResult, | 3995 | latestTerminalSummary: queuedFollowupResult, |
| 3999 | latestTerminalResultContextTransferId: null, | 3996 | latestTerminalResultContextTransferId: null, |
| 4000 | }); | 3997 | }); |
| 4001 | }).pipe(Effect.provide(testLayer)); | 3998 | }).pipe(Effect.provide(layerTest)); |
| 4002 | }), | 3999 | }), |
| 4003 | ), | 4000 | ), |
| 4004 | ); | 4001 | ); |
apps/server/src/mcp/PreviewAutomationBroker.test.ts+2-2| ... | @@ -29,7 +29,7 @@ import * as TestClock from "effect/testing/TestClock"; | ... | @@ -29,7 +29,7 @@ import * as TestClock from "effect/testing/TestClock"; |
| 29 | import * as RpcGroup from "effect/rpc/RpcGroup"; | 29 | import * as RpcGroup from "effect/rpc/RpcGroup"; |
| 30 | import * as RpcTest from "effect/rpc/RpcTest"; | 30 | import * as RpcTest from "effect/rpc/RpcTest"; |
| 31 | 31 | ||
| 32 | import { rpcScopeAuthorizationLayer } from "../auth/RpcAuthorization.ts"; | 32 | import * as RpcAuthorization from "../auth/RpcAuthorization.ts"; |
| 33 | import * as PreviewAutomationBroker from "./PreviewAutomationBroker.ts"; | 33 | import * as PreviewAutomationBroker from "./PreviewAutomationBroker.ts"; |
| 34 | 34 | ||
| 35 | const makeBroker = PreviewAutomationBroker.make.pipe(Effect.provide(NodeServices.layer)); | 35 | const makeBroker = PreviewAutomationBroker.make.pipe(Effect.provide(NodeServices.layer)); |
| ... | @@ -1276,7 +1276,7 @@ it.effect("evicts an unanswered host and lets later calls use a healthy runtime" | ... | @@ -1276,7 +1276,7 @@ it.effect("evicts an unanswered host and lets later calls use a healthy runtime" |
| 1276 | group.toLayer({ | 1276 | group.toLayer({ |
| 1277 | [WS_METHODS.previewAutomationConnect]: (host) => Stream.unwrap(broker.connect(host)), | 1277 | [WS_METHODS.previewAutomationConnect]: (host) => Stream.unwrap(broker.connect(host)), |
| 1278 | }), | 1278 | }), |
| 1279 | rpcScopeAuthorizationLayer([AuthOrchestrationOperateScope]), | 1279 | RpcAuthorization.layer([AuthOrchestrationOperateScope]), |
| 1280 | ), | 1280 | ), |
| 1281 | ), | 1281 | ), |
| 1282 | ); | 1282 | ); |
apps/server/src/mcp/ThreadMetadataMcpService.test.ts+5-5| ... | @@ -23,7 +23,7 @@ const scope: McpInvocationContext.McpInvocationScope = { | ... | @@ -23,7 +23,7 @@ const scope: McpInvocationContext.McpInvocationScope = { |
| 23 | issuedAt: 1, | 23 | issuedAt: 1, |
| 24 | }; | 24 | }; |
| 25 | 25 | ||
| 26 | function serviceLayer( | 26 | function layerService( |
| 27 | getThreadShell: ThreadManagement.ThreadManagementService["Service"]["getThreadShell"], | 27 | getThreadShell: ThreadManagement.ThreadManagementService["Service"]["getThreadShell"], |
| 28 | ) { | 28 | ) { |
| 29 | return ThreadMetadataMcp.layer.pipe( | 29 | return ThreadMetadataMcp.layer.pipe( |
| ... | @@ -51,7 +51,7 @@ const updateCallingThread = Effect.gen(function* () { | ... | @@ -51,7 +51,7 @@ const updateCallingThread = Effect.gen(function* () { |
| 51 | it.effect("reports an absent calling thread as thread_not_found", () => | 51 | it.effect("reports an absent calling thread as thread_not_found", () => |
| 52 | Effect.gen(function* () { | 52 | Effect.gen(function* () { |
| 53 | const error = yield* updateCallingThread.pipe( | 53 | const error = yield* updateCallingThread.pipe( |
| 54 | Effect.provide(serviceLayer(() => Effect.succeed(null))), | 54 | Effect.provide(layerService(() => Effect.succeed(null))), |
| 55 | Effect.flip, | 55 | Effect.flip, |
| 56 | ); | 56 | ); |
| 57 | 57 | ||
| ... | @@ -63,7 +63,7 @@ it.effect("keeps calling-thread storage failures as orchestration errors", () => | ... | @@ -63,7 +63,7 @@ it.effect("keeps calling-thread storage failures as orchestration errors", () => |
| 63 | Effect.gen(function* () { | 63 | Effect.gen(function* () { |
| 64 | const error = yield* updateCallingThread.pipe( | 64 | const error = yield* updateCallingThread.pipe( |
| 65 | Effect.provide( | 65 | Effect.provide( |
| 66 | serviceLayer(() => | 66 | layerService(() => |
| 67 | Effect.fail( | 67 | Effect.fail( |
| 68 | new OrchestratorProjectionError({ | 68 | new OrchestratorProjectionError({ |
| 69 | threadId, | 69 | threadId, |
| ... | @@ -115,7 +115,7 @@ it.effect("refuses to change a thread that runs above the caller's modes", () => | ... | @@ -115,7 +115,7 @@ it.effect("refuses to change a thread that runs above the caller's modes", () => |
| 115 | title: "Renamed from a narrower thread", | 115 | title: "Renamed from a narrower thread", |
| 116 | }); | 116 | }); |
| 117 | }).pipe( | 117 | }).pipe( |
| 118 | Effect.provide(serviceLayer((id) => Effect.succeed((shells.get(id) ?? null) as never))), | 118 | Effect.provide(layerService((id) => Effect.succeed((shells.get(id) ?? null) as never))), |
| 119 | Effect.flip, | 119 | Effect.flip, |
| 120 | ); | 120 | ); |
| 121 | 121 | ||
| ... | @@ -159,7 +159,7 @@ it.effect("refuses another thread's metadata to a thread caller whose run has en | ... | @@ -159,7 +159,7 @@ it.effect("refuses another thread's metadata to a thread caller whose run has en |
| 159 | title: "Renamed after the run ended", | 159 | title: "Renamed after the run ended", |
| 160 | }); | 160 | }); |
| 161 | }).pipe( | 161 | }).pipe( |
| 162 | Effect.provide(serviceLayer((id) => Effect.succeed((shells.get(id) ?? null) as never))), | 162 | Effect.provide(layerService((id) => Effect.succeed((shells.get(id) ?? null) as never))), |
| 163 | Effect.flip, | 163 | Effect.flip, |
| 164 | ); | 164 | ); |
| 165 | 165 |
apps/server/src/mcp/WorktreeMcpService.test.ts+2-2| ... | @@ -294,7 +294,7 @@ const makeHarness = (options: HarnessOptions = {}) => { | ... | @@ -294,7 +294,7 @@ const makeHarness = (options: HarnessOptions = {}) => { |
| 294 | // inlined so the test does not depend on the host's path module. | 294 | // inlined so the test does not depend on the host's path module. |
| 295 | const win32IsAbsolute = (value: string) => /^(?:[a-zA-Z]:[\\/]|[\\/])/.test(value); | 295 | const win32IsAbsolute = (value: string) => /^(?:[a-zA-Z]:[\\/]|[\\/])/.test(value); |
| 296 | const posixIsAbsolute = (value: string) => value.startsWith("/"); | 296 | const posixIsAbsolute = (value: string) => value.startsWith("/"); |
| 297 | const serviceLayer = | 297 | const layerService = |
| 298 | options.pathSemantics === undefined | 298 | options.pathSemantics === undefined |
| 299 | ? WorktreeMcpService.layer | 299 | ? WorktreeMcpService.layer |
| 300 | : WorktreeMcpService.layer.pipe( | 300 | : WorktreeMcpService.layer.pipe( |
| ... | @@ -304,7 +304,7 @@ const makeHarness = (options: HarnessOptions = {}) => { | ... | @@ -304,7 +304,7 @@ const makeHarness = (options: HarnessOptions = {}) => { |
| 304 | } as unknown as Path.Path), | 304 | } as unknown as Path.Path), |
| 305 | ), | 305 | ), |
| 306 | ); | 306 | ); |
| 307 | const layer = serviceLayer.pipe( | 307 | const layer = layerService.pipe( |
| 308 | Layer.provide( | 308 | Layer.provide( |
| 309 | Layer.mergeAll( | 309 | Layer.mergeAll( |
| 310 | Layer.mock(ThreadManagementService.ThreadManagementService)({ | 310 | Layer.mock(ThreadManagementService.ThreadManagementService)({ |
apps/server/src/mcp/toolkits/attachment/handlers.ts+1-1| ... | @@ -31,7 +31,7 @@ export function resolveAttachmentReferences( | ... | @@ -31,7 +31,7 @@ export function resolveAttachmentReferences( |
| 31 | }); | 31 | }); |
| 32 | } | 32 | } |
| 33 | 33 | ||
| 34 | export const AttachmentHandlersLive = AttachmentToolkit.toLayer({ | 34 | export const layer = AttachmentToolkit.toLayer({ |
| 35 | t3_attachment_prepare_upload: (input) => | 35 | t3_attachment_prepare_upload: (input) => |
| 36 | Effect.gen(function* () { | 36 | Effect.gen(function* () { |
| 37 | yield* readMutationCaller(); | 37 | yield* readMutationCaller(); |
apps/server/src/mcp/toolkits/core.test.ts+7-7| ... | @@ -137,7 +137,7 @@ it.effect("checks capability before accessing services through the production re | ... | @@ -137,7 +137,7 @@ it.effect("checks capability before accessing services through the production re |
| 137 | expect(declaredFailure(result)).toMatchObject({ code: "capability_denied" }); | 137 | expect(declaredFailure(result)).toMatchObject({ code: "capability_denied" }); |
| 138 | }).pipe( | 138 | }).pipe( |
| 139 | Effect.provide( | 139 | Effect.provide( |
| 140 | McpHttpServer.ThreadToolkitRegistrationLive.pipe( | 140 | McpHttpServer.layerThreadToolkit.pipe( |
| 141 | Layer.provideMerge(McpServer.McpServer.layer), | 141 | Layer.provideMerge(McpServer.McpServer.layer), |
| 142 | Layer.provide(NodeCrypto.layer), | 142 | Layer.provide(NodeCrypto.layer), |
| 143 | Layer.provide(Layer.mock(ThreadManagement.ThreadManagementService)({})), | 143 | Layer.provide(Layer.mock(ThreadManagement.ThreadManagementService)({})), |
| ... | @@ -162,7 +162,7 @@ it.effect("returns a bounded public failure without serializing storage causes", | ... | @@ -162,7 +162,7 @@ it.effect("returns a bounded public failure without serializing storage causes", |
| 162 | }); | 162 | }); |
| 163 | }).pipe( | 163 | }).pipe( |
| 164 | Effect.provide( | 164 | Effect.provide( |
| 165 | McpHttpServer.ThreadToolkitRegistrationLive.pipe( | 165 | McpHttpServer.layerThreadToolkit.pipe( |
| 166 | Layer.provideMerge(McpServer.McpServer.layer), | 166 | Layer.provideMerge(McpServer.McpServer.layer), |
| 167 | Layer.provide(NodeCrypto.layer), | 167 | Layer.provide(NodeCrypto.layer), |
| 168 | Layer.provide( | 168 | Layer.provide( |
| ... | @@ -203,7 +203,7 @@ it.effect("returns an HTML render reference that Codex and Claude tool rows both | ... | @@ -203,7 +203,7 @@ it.effect("returns an HTML render reference that Codex and Claude tool rows both |
| 203 | ).toEqual(reference); | 203 | ).toEqual(reference); |
| 204 | }).pipe( | 204 | }).pipe( |
| 205 | Effect.provide( | 205 | Effect.provide( |
| 206 | McpHttpServer.HtmlToolkitRegistrationLive.pipe( | 206 | McpHttpServer.layerHtmlToolkit.pipe( |
| 207 | Layer.provideMerge(McpServer.McpServer.layer), | 207 | Layer.provideMerge(McpServer.McpServer.layer), |
| 208 | Layer.provide(ServerConfig.layerTest(process.cwd(), { prefix: "t3-mcp-html-render-" })), | 208 | Layer.provide(ServerConfig.layerTest(process.cwd(), { prefix: "t3-mcp-html-render-" })), |
| 209 | Layer.provide(NodeServices.layer), | 209 | Layer.provide(NodeServices.layer), |
| ... | @@ -332,7 +332,7 @@ it.effect("a client caller targets any thread within its ceiling and cannot act | ... | @@ -332,7 +332,7 @@ it.effect("a client caller targets any thread within its ceiling and cannot act |
| 332 | expect(declaredFailure(forked)).toMatchObject({ code: "target_required" }); | 332 | expect(declaredFailure(forked)).toMatchObject({ code: "target_required" }); |
| 333 | }).pipe( | 333 | }).pipe( |
| 334 | Effect.provide( | 334 | Effect.provide( |
| 335 | McpHttpServer.ThreadToolkitRegistrationLive.pipe( | 335 | McpHttpServer.layerThreadToolkit.pipe( |
| 336 | Layer.provideMerge(McpServer.McpServer.layer), | 336 | Layer.provideMerge(McpServer.McpServer.layer), |
| 337 | Layer.provide(NodeCrypto.layer), | 337 | Layer.provide(NodeCrypto.layer), |
| 338 | Layer.provide( | 338 | Layer.provide( |
| ... | @@ -379,7 +379,7 @@ it.effect("refuses act-as-caller tools to a client caller", () => | ... | @@ -379,7 +379,7 @@ it.effect("refuses act-as-caller tools to a client caller", () => |
| 379 | expect(declaredFailure(result)).toMatchObject({ code: "thread_credential_required" }); | 379 | expect(declaredFailure(result)).toMatchObject({ code: "thread_credential_required" }); |
| 380 | }).pipe( | 380 | }).pipe( |
| 381 | Effect.provide( | 381 | Effect.provide( |
| 382 | McpHttpServer.OrchestratorToolkitRegistrationLive.pipe( | 382 | McpHttpServer.layerOrchestratorToolkit.pipe( |
| 383 | Layer.provideMerge(McpServer.McpServer.layer), | 383 | Layer.provideMerge(McpServer.McpServer.layer), |
| 384 | Layer.provide(NodeCrypto.layer), | 384 | Layer.provide(NodeCrypto.layer), |
| 385 | Layer.provide(Layer.mock(ThreadManagement.ThreadManagementService)({})), | 385 | Layer.provide(Layer.mock(ThreadManagement.ThreadManagementService)({})), |
| ... | @@ -417,7 +417,7 @@ it.effect("a caller cannot rewrite a scheduled task that runs above its own mode | ... | @@ -417,7 +417,7 @@ it.effect("a caller cannot rewrite a scheduled task that runs above its own mode |
| 417 | expect(allowed.isError).toBe(false); | 417 | expect(allowed.isError).toBe(false); |
| 418 | }).pipe( | 418 | }).pipe( |
| 419 | Effect.provide( | 419 | Effect.provide( |
| 420 | McpHttpServer.OrchestratorToolkitRegistrationLive.pipe( | 420 | McpHttpServer.layerOrchestratorToolkit.pipe( |
| 421 | Layer.provideMerge(McpServer.McpServer.layer), | 421 | Layer.provideMerge(McpServer.McpServer.layer), |
| 422 | Layer.provide(NodeCrypto.layer), | 422 | Layer.provide(NodeCrypto.layer), |
| 423 | Layer.provide(Layer.mock(ThreadManagement.ThreadManagementService)({})), | 423 | Layer.provide(Layer.mock(ThreadManagement.ThreadManagementService)({})), |
| ... | @@ -483,7 +483,7 @@ it.effect("a caller cannot interrupt a thread that runs above its own modes", () | ... | @@ -483,7 +483,7 @@ it.effect("a caller cannot interrupt a thread that runs above its own modes", () |
| 483 | expect(declaredFailure(result)).toMatchObject({ code: "runtime_mode_escalation_denied" }); | 483 | expect(declaredFailure(result)).toMatchObject({ code: "runtime_mode_escalation_denied" }); |
| 484 | }).pipe( | 484 | }).pipe( |
| 485 | Effect.provide( | 485 | Effect.provide( |
| 486 | McpHttpServer.OrchestratorToolkitRegistrationLive.pipe( | 486 | McpHttpServer.layerOrchestratorToolkit.pipe( |
| 487 | Layer.provideMerge(McpServer.McpServer.layer), | 487 | Layer.provideMerge(McpServer.McpServer.layer), |
| 488 | Layer.provide(NodeCrypto.layer), | 488 | Layer.provide(NodeCrypto.layer), |
| 489 | Layer.provide( | 489 | Layer.provide( |
apps/server/src/mcp/toolkits/device/handlers.ts+2-2| ... | @@ -258,8 +258,8 @@ export function pngDimensions(png: Uint8Array): { width: number; height: number | ... | @@ -258,8 +258,8 @@ export function pngDimensions(png: Uint8Array): { width: number; height: number |
| 258 | 258 | ||
| 259 | const { device_screenshot, ...standardHandlers } = handlers; | 259 | const { device_screenshot, ...standardHandlers } = handlers; |
| 260 | 260 | ||
| 261 | export const DeviceStandardToolkitHandlersLive = DeviceStandardToolkit.toLayer(standardHandlers); | 261 | export const layerStandard = DeviceStandardToolkit.toLayer(standardHandlers); |
| 262 | 262 | ||
| 263 | export const DeviceScreenshotToolkitHandlersLive = DeviceScreenshotToolkit.toLayer({ | 263 | export const layerScreenshot = DeviceScreenshotToolkit.toLayer({ |
| 264 | device_screenshot, | 264 | device_screenshot, |
| 265 | }); | 265 | }); |
apps/server/src/mcp/toolkits/environment/handlers.ts+1-1| ... | @@ -44,7 +44,7 @@ const access = (writable = false) => | ... | @@ -44,7 +44,7 @@ const access = (writable = false) => |
| 44 | }); | 44 | }); |
| 45 | return { ...context, descriptor, settings: yield* Settings.ServerSettingsService }; | 45 | return { ...context, descriptor, settings: yield* Settings.ServerSettingsService }; |
| 46 | }); | 46 | }); |
| 47 | export const EnvironmentHandlersLive = EnvironmentToolkit.toLayer({ | 47 | export const layer = EnvironmentToolkit.toLayer({ |
| 48 | t3_environment_read: () => | 48 | t3_environment_read: () => |
| 49 | Effect.gen(function* () { | 49 | Effect.gen(function* () { |
| 50 | const { descriptor, settings } = yield* access(); | 50 | const { descriptor, settings } = yield* access(); |
apps/server/src/mcp/toolkits/html/handlers.ts+2-2| ... | @@ -58,10 +58,10 @@ const handlers = { | ... | @@ -58,10 +58,10 @@ const handlers = { |
| 58 | }), | 58 | }), |
| 59 | } satisfies Parameters<typeof HtmlToolkit.toLayer>[0]; | 59 | } satisfies Parameters<typeof HtmlToolkit.toLayer>[0]; |
| 60 | 60 | ||
| 61 | export const HtmlPreviewToolkitHandlersLive = HtmlPreviewToolkit.toLayer({ | 61 | export const layerPreview = HtmlPreviewToolkit.toLayer({ |
| 62 | html_preview: handlers.html_preview, | 62 | html_preview: handlers.html_preview, |
| 63 | }); | 63 | }); |
| 64 | 64 | ||
| 65 | export const HtmlRenderToolkitHandlersLive = HtmlRenderToolkit.toLayer({ | 65 | export const layerRender = HtmlRenderToolkit.toLayer({ |
| 66 | html_render: handlers.html_render, | 66 | html_render: handlers.html_render, |
| 67 | }); | 67 | }); |
apps/server/src/mcp/toolkits/orchestrator/handlers.ts+1-1| ... | @@ -104,4 +104,4 @@ const handlers = { | ... | @@ -104,4 +104,4 @@ const handlers = { |
| 104 | }), | 104 | }), |
| 105 | } satisfies Parameters<typeof OrchestratorToolkit.toLayer>[0]; | 105 | } satisfies Parameters<typeof OrchestratorToolkit.toLayer>[0]; |
| 106 | 106 | ||
| 107 | export const OrchestratorToolkitHandlersLive = OrchestratorToolkit.toLayer(handlers); | 107 | export const layer = OrchestratorToolkit.toLayer(handlers); |
apps/server/src/mcp/toolkits/preview/handlers.ts+2-2| ... | @@ -233,8 +233,8 @@ const handlers = { | ... | @@ -233,8 +233,8 @@ const handlers = { |
| 233 | 233 | ||
| 234 | const { preview_snapshot, ...standardHandlers } = handlers; | 234 | const { preview_snapshot, ...standardHandlers } = handlers; |
| 235 | 235 | ||
| 236 | export const PreviewStandardToolkitHandlersLive = PreviewStandardToolkit.toLayer(standardHandlers); | 236 | export const layerStandard = PreviewStandardToolkit.toLayer(standardHandlers); |
| 237 | 237 | ||
| 238 | export const PreviewSnapshotToolkitHandlersLive = PreviewSnapshotToolkit.toLayer({ | 238 | export const layerSnapshot = PreviewSnapshotToolkit.toLayer({ |
| 239 | preview_snapshot, | 239 | preview_snapshot, |
| 240 | }); | 240 | }); |
apps/server/src/mcp/toolkits/previewControls/handlers.test.ts+5-5| ... | @@ -14,7 +14,7 @@ import * as Stream from "effect/Stream"; | ... | @@ -14,7 +14,7 @@ import * as Stream from "effect/Stream"; |
| 14 | import * as Preview from "../../../preview/Manager.ts"; | 14 | import * as Preview from "../../../preview/Manager.ts"; |
| 15 | import * as ServerSettings from "../../../serverSettings.ts"; | 15 | import * as ServerSettings from "../../../serverSettings.ts"; |
| 16 | import * as McpInvocationContext from "../../McpInvocationContext.ts"; | 16 | import * as McpInvocationContext from "../../McpInvocationContext.ts"; |
| 17 | import { PreviewControlsHandlersLive } from "./handlers.ts"; | 17 | import * as PreviewControlsHandlers from "./handlers.ts"; |
| 18 | import { PreviewControlsToolkit } from "./tools.ts"; | 18 | import { PreviewControlsToolkit } from "./tools.ts"; |
| 19 | 19 | ||
| 20 | it.effect.each([ | 20 | it.effect.each([ |
| ... | @@ -47,7 +47,7 @@ it.effect.each([ | ... | @@ -47,7 +47,7 @@ it.effect.each([ |
| 47 | }; | 47 | }; |
| 48 | const manager = yield* Preview.make; | 48 | const manager = yield* Preview.make; |
| 49 | const tab = yield* manager.open({ threadId, url: "http://localhost:3000" }); | 49 | const tab = yield* manager.open({ threadId, url: "http://localhost:3000" }); |
| 50 | const dependencies = Layer.mergeAll( | 50 | const layerDependencies = Layer.mergeAll( |
| 51 | Layer.succeed(Preview.PreviewManager, manager), | 51 | Layer.succeed(Preview.PreviewManager, manager), |
| 52 | Layer.succeed(McpInvocationContext.McpInvocationContext, scope), | 52 | Layer.succeed(McpInvocationContext.McpInvocationContext, scope), |
| 53 | Layer.mock(ServerSettings.ServerSettingsService)({ | 53 | Layer.mock(ServerSettings.ServerSettingsService)({ |
| ... | @@ -55,14 +55,14 @@ it.effect.each([ | ... | @@ -55,14 +55,14 @@ it.effect.each([ |
| 55 | }), | 55 | }), |
| 56 | ); | 56 | ); |
| 57 | const toolkit = yield* PreviewControlsToolkit.pipe( | 57 | const toolkit = yield* PreviewControlsToolkit.pipe( |
| 58 | Effect.provide(PreviewControlsHandlersLive.pipe(Layer.provide(dependencies))), | 58 | Effect.provide(PreviewControlsHandlers.layer.pipe(Layer.provide(layerDependencies))), |
| 59 | ); | 59 | ); |
| 60 | const listed = yield* toolkit | 60 | const listed = yield* toolkit |
| 61 | .handle("t3_preview_list", {}) | 61 | .handle("t3_preview_list", {}) |
| 62 | .pipe(Stream.unwrap, Stream.runCollect, Effect.provide(dependencies)); | 62 | .pipe(Stream.unwrap, Stream.runCollect, Effect.provide(layerDependencies)); |
| 63 | const closed = yield* toolkit | 63 | const closed = yield* toolkit |
| 64 | .handle("t3_preview_close", { tabId: tab.tabId }) | 64 | .handle("t3_preview_close", { tabId: tab.tabId }) |
| 65 | .pipe(Stream.unwrap, Stream.runCollect, Effect.provide(dependencies)); | 65 | .pipe(Stream.unwrap, Stream.runCollect, Effect.provide(layerDependencies)); |
| 66 | if (projectAccess) { | 66 | if (projectAccess) { |
| 67 | expect(listed.at(-1)?.result).toMatchObject({ sessions: [tab], nextCursor: null }); | 67 | expect(listed.at(-1)?.result).toMatchObject({ sessions: [tab], nextCursor: null }); |
| 68 | expect(closed.at(-1)?.result).toEqual({}); | 68 | expect(closed.at(-1)?.result).toEqual({}); |
apps/server/src/mcp/toolkits/previewControls/handlers.ts+1-1| ... | @@ -9,7 +9,7 @@ const access = Effect.gen(function* () { | ... | @@ -9,7 +9,7 @@ const access = Effect.gen(function* () { |
| 9 | const scope = yield* requireThreadMcpCapability("preview"); | 9 | const scope = yield* requireThreadMcpCapability("preview"); |
| 10 | return { scope, manager: yield* Preview.PreviewManager }; | 10 | return { scope, manager: yield* Preview.PreviewManager }; |
| 11 | }); | 11 | }); |
| 12 | export const PreviewControlsHandlersLive = PreviewControlsToolkit.toLayer({ | 12 | export const layer = PreviewControlsToolkit.toLayer({ |
| 13 | t3_preview_list: (input) => | 13 | t3_preview_list: (input) => |
| 14 | Effect.gen(function* () { | 14 | Effect.gen(function* () { |
| 15 | const { scope, manager } = yield* access; | 15 | const { scope, manager } = yield* access; |
apps/server/src/mcp/toolkits/project/handlers.test.ts+13-13| ... | @@ -20,7 +20,7 @@ import * as ServerConfig from "../../../config.ts"; | ... | @@ -20,7 +20,7 @@ import * as ServerConfig from "../../../config.ts"; |
| 20 | import * as Project from "../../../project/ProjectService.ts"; | 20 | import * as Project from "../../../project/ProjectService.ts"; |
| 21 | import * as ManagedProjectFolders from "../../../project/ManagedProjectFolders.ts"; | 21 | import * as ManagedProjectFolders from "../../../project/ManagedProjectFolders.ts"; |
| 22 | import * as McpInvocationContext from "../../McpInvocationContext.ts"; | 22 | import * as McpInvocationContext from "../../McpInvocationContext.ts"; |
| 23 | import { ProjectHandlersLive } from "./handlers.ts"; | 23 | import * as ProjectHandlers from "./handlers.ts"; |
| 24 | import { ProjectToolkit } from "./tools.ts"; | 24 | import { ProjectToolkit } from "./tools.ts"; |
| 25 | 25 | ||
| 26 | it.effect("attributes a launched thread's first message to the calling thread", () => | 26 | it.effect("attributes a launched thread's first message to the calling thread", () => |
| ... | @@ -41,7 +41,7 @@ it.effect("attributes a launched thread's first message to the calling thread", | ... | @@ -41,7 +41,7 @@ it.effect("attributes a launched thread's first message to the calling thread", |
| 41 | deletedAt: null, | 41 | deletedAt: null, |
| 42 | } as OrchestrationV2ThreadShell; | 42 | } as OrchestrationV2ThreadShell; |
| 43 | let launchedSender: ThreadId | undefined; | 43 | let launchedSender: ThreadId | undefined; |
| 44 | const dependencies = Layer.mergeAll( | 44 | const layerDependencies = Layer.mergeAll( |
| 45 | NodeCrypto.layer, | 45 | NodeCrypto.layer, |
| 46 | Layer.succeed(McpInvocationContext.McpInvocationContext, { | 46 | Layer.succeed(McpInvocationContext.McpInvocationContext, { |
| 47 | environmentId: EnvironmentId.make("environment"), | 47 | environmentId: EnvironmentId.make("environment"), |
| ... | @@ -79,11 +79,11 @@ it.effect("attributes a launched thread's first message to the calling thread", | ... | @@ -79,11 +79,11 @@ it.effect("attributes a launched thread's first message to the calling thread", |
| 79 | ), | 79 | ), |
| 80 | ); | 80 | ); |
| 81 | const toolkit = yield* ProjectToolkit.pipe( | 81 | const toolkit = yield* ProjectToolkit.pipe( |
| 82 | Effect.provide(ProjectHandlersLive.pipe(Layer.provide(dependencies))), | 82 | Effect.provide(ProjectHandlers.layer.pipe(Layer.provide(layerDependencies))), |
| 83 | ); | 83 | ); |
| 84 | const result = yield* toolkit | 84 | const result = yield* toolkit |
| 85 | .handle("t3_thread_launch", { title: "Audit", message: "Review the change" }) | 85 | .handle("t3_thread_launch", { title: "Audit", message: "Review the change" }) |
| 86 | .pipe(Stream.unwrap, Stream.runCollect, Effect.provide(dependencies)); | 86 | .pipe(Stream.unwrap, Stream.runCollect, Effect.provide(layerDependencies)); |
| 87 | expect(result.at(-1)?.result).toMatchObject({ projectId, modelSelection }); | 87 | expect(result.at(-1)?.result).toMatchObject({ projectId, modelSelection }); |
| 88 | expect(launchedSender).toBe(sourceThreadId); | 88 | expect(launchedSender).toBe(sourceThreadId); |
| 89 | }), | 89 | }), |
| ... | @@ -107,7 +107,7 @@ it.effect("launches a scratch thread into the Scratch project", () => | ... | @@ -107,7 +107,7 @@ it.effect("launches a scratch thread into the Scratch project", () => |
| 107 | deletedAt: null, | 107 | deletedAt: null, |
| 108 | } as OrchestrationV2ThreadShell; | 108 | } as OrchestrationV2ThreadShell; |
| 109 | const launched: Array<ThreadLaunch.ThreadLaunchInput> = []; | 109 | const launched: Array<ThreadLaunch.ThreadLaunchInput> = []; |
| 110 | const dependencies = Layer.mergeAll( | 110 | const layerDependencies = Layer.mergeAll( |
| 111 | NodeCrypto.layer, | 111 | NodeCrypto.layer, |
| 112 | Layer.succeed(McpInvocationContext.McpInvocationContext, { | 112 | Layer.succeed(McpInvocationContext.McpInvocationContext, { |
| 113 | environmentId: EnvironmentId.make("environment"), | 113 | environmentId: EnvironmentId.make("environment"), |
| ... | @@ -148,12 +148,12 @@ it.effect("launches a scratch thread into the Scratch project", () => | ... | @@ -148,12 +148,12 @@ it.effect("launches a scratch thread into the Scratch project", () => |
| 148 | ), | 148 | ), |
| 149 | ); | 149 | ); |
| 150 | const toolkit = yield* ProjectToolkit.pipe( | 150 | const toolkit = yield* ProjectToolkit.pipe( |
| 151 | Effect.provide(ProjectHandlersLive.pipe(Layer.provide(dependencies))), | 151 | Effect.provide(ProjectHandlers.layer.pipe(Layer.provide(layerDependencies))), |
| 152 | ); | 152 | ); |
| 153 | const handle = (params: Parameters<typeof toolkit.handle<"t3_thread_launch">>[1]) => | 153 | const handle = (params: Parameters<typeof toolkit.handle<"t3_thread_launch">>[1]) => |
| 154 | toolkit | 154 | toolkit |
| 155 | .handle("t3_thread_launch", params) | 155 | .handle("t3_thread_launch", params) |
| 156 | .pipe(Stream.unwrap, Stream.runCollect, Effect.provide(dependencies)); | 156 | .pipe(Stream.unwrap, Stream.runCollect, Effect.provide(layerDependencies)); |
| 157 | 157 | ||
| 158 | const result = yield* handle({ title: "Notes", scratch: true, message: "Draft a list" }); | 158 | const result = yield* handle({ title: "Notes", scratch: true, message: "Draft a list" }); |
| 159 | expect(result.at(-1)?.result).toMatchObject({ projectId: scratchProjectId }); | 159 | expect(result.at(-1)?.result).toMatchObject({ projectId: scratchProjectId }); |
| ... | @@ -199,7 +199,7 @@ it.effect("starts a project from just a title when workspaceRoot is omitted", () | ... | @@ -199,7 +199,7 @@ it.effect("starts a project from just a title when workspaceRoot is omitted", () |
| 199 | updatedAt: "2026-10-01T00:00:00.000Z", | 199 | updatedAt: "2026-10-01T00:00:00.000Z", |
| 200 | deletedAt: null, | 200 | deletedAt: null, |
| 201 | }; | 201 | }; |
| 202 | const dependencies = Layer.mergeAll( | 202 | const layerDependencies = Layer.mergeAll( |
| 203 | NodeCrypto.layer, | 203 | NodeCrypto.layer, |
| 204 | Layer.succeed(McpInvocationContext.McpInvocationContext, { | 204 | Layer.succeed(McpInvocationContext.McpInvocationContext, { |
| 205 | environmentId: EnvironmentId.make("environment"), | 205 | environmentId: EnvironmentId.make("environment"), |
| ... | @@ -246,12 +246,12 @@ it.effect("starts a project from just a title when workspaceRoot is omitted", () | ... | @@ -246,12 +246,12 @@ it.effect("starts a project from just a title when workspaceRoot is omitted", () |
| 246 | ), | 246 | ), |
| 247 | ); | 247 | ); |
| 248 | const toolkit = yield* ProjectToolkit.pipe( | 248 | const toolkit = yield* ProjectToolkit.pipe( |
| 249 | Effect.provide(ProjectHandlersLive.pipe(Layer.provide(dependencies))), | 249 | Effect.provide(ProjectHandlers.layer.pipe(Layer.provide(layerDependencies))), |
| 250 | ); | 250 | ); |
| 251 | const handle = (params: Parameters<typeof toolkit.handle<"t3_project_create">>[1]) => | 251 | const handle = (params: Parameters<typeof toolkit.handle<"t3_project_create">>[1]) => |
| 252 | toolkit | 252 | toolkit |
| 253 | .handle("t3_project_create", params) | 253 | .handle("t3_project_create", params) |
| 254 | .pipe(Stream.unwrap, Stream.runCollect, Effect.provide(dependencies)); | 254 | .pipe(Stream.unwrap, Stream.runCollect, Effect.provide(layerDependencies)); |
| 255 | 255 | ||
| 256 | const result = yield* handle({ title: "Pinball Stats" }); | 256 | const result = yield* handle({ title: "Pinball Stats" }); |
| 257 | expect(result.at(-1)?.result).toMatchObject({ | 257 | expect(result.at(-1)?.result).toMatchObject({ |
| ... | @@ -283,7 +283,7 @@ const clientLaunchHarness = (input: { | ... | @@ -283,7 +283,7 @@ const clientLaunchHarness = (input: { |
| 283 | }) => { | 283 | }) => { |
| 284 | const projectId = ProjectId.make("project:client-target"); | 284 | const projectId = ProjectId.make("project:client-target"); |
| 285 | const modelSelection = { instanceId: ProviderInstanceId.make("claude"), model: "claude-opus" }; | 285 | const modelSelection = { instanceId: ProviderInstanceId.make("claude"), model: "claude-opus" }; |
| 286 | const dependencies = Layer.mergeAll( | 286 | const layerDependencies = Layer.mergeAll( |
| 287 | NodeCrypto.layer, | 287 | NodeCrypto.layer, |
| 288 | Layer.succeed(McpInvocationContext.McpInvocationContext, { | 288 | Layer.succeed(McpInvocationContext.McpInvocationContext, { |
| 289 | environmentId: EnvironmentId.make("environment"), | 289 | environmentId: EnvironmentId.make("environment"), |
| ... | @@ -329,7 +329,7 @@ const clientLaunchHarness = (input: { | ... | @@ -329,7 +329,7 @@ const clientLaunchHarness = (input: { |
| 329 | Layer.provide(NodeServices.layer), | 329 | Layer.provide(NodeServices.layer), |
| 330 | ), | 330 | ), |
| 331 | ); | 331 | ); |
| 332 | return { projectId, modelSelection, dependencies }; | 332 | return { projectId, modelSelection, dependencies: layerDependencies }; |
| 333 | }; | 333 | }; |
| 334 | 334 | ||
| 335 | it.effect("a client launches at its ceiling with the project's default model", () => | 335 | it.effect("a client launches at its ceiling with the project's default model", () => |
| ... | @@ -340,7 +340,7 @@ it.effect("a client launches at its ceiling with the project's default model", ( | ... | @@ -340,7 +340,7 @@ it.effect("a client launches at its ceiling with the project's default model", ( |
| 340 | launched, | 340 | launched, |
| 341 | }); | 341 | }); |
| 342 | const toolkit = yield* ProjectToolkit.pipe( | 342 | const toolkit = yield* ProjectToolkit.pipe( |
| 343 | Effect.provide(ProjectHandlersLive.pipe(Layer.provide(dependencies))), | 343 | Effect.provide(ProjectHandlers.layer.pipe(Layer.provide(dependencies))), |
| 344 | ); | 344 | ); |
| 345 | const handle = (params: Parameters<typeof toolkit.handle<"t3_thread_launch">>[1]) => | 345 | const handle = (params: Parameters<typeof toolkit.handle<"t3_thread_launch">>[1]) => |
| 346 | toolkit | 346 | toolkit |
apps/server/src/mcp/toolkits/project/handlers.ts+1-1| ... | @@ -38,7 +38,7 @@ const mutation = Effect.gen(function* () { | ... | @@ -38,7 +38,7 @@ const mutation = Effect.gen(function* () { |
| 38 | ); | 38 | ); |
| 39 | return yield* Project.ProjectService; | 39 | return yield* Project.ProjectService; |
| 40 | }); | 40 | }); |
| 41 | export const ProjectHandlersLive = ProjectToolkit.toLayer({ | 41 | export const layer = ProjectToolkit.toLayer({ |
| 42 | t3_thread_launch: (input) => | 42 | t3_thread_launch: (input) => |
| 43 | Effect.gen(function* () { | 43 | Effect.gen(function* () { |
| 44 | const context = yield* readMutationCaller(); | 44 | const context = yield* readMutationCaller(); |
apps/server/src/mcp/toolkits/pullRequests/handlers.test.ts+5-4| ... | @@ -23,7 +23,8 @@ import { | ... | @@ -23,7 +23,8 @@ import { |
| 23 | } from "../../../orchestration-v2/testkit/pullRequestFixtures.ts"; | 23 | } from "../../../orchestration-v2/testkit/pullRequestFixtures.ts"; |
| 24 | import * as ProjectService from "../../../project/ProjectService.ts"; | 24 | import * as ProjectService from "../../../project/ProjectService.ts"; |
| 25 | import * as McpInvocationContext from "../../McpInvocationContext.ts"; | 25 | import * as McpInvocationContext from "../../McpInvocationContext.ts"; |
| 26 | import { listThreadPullRequests, PullRequestsToolkitHandlersLive } from "./handlers.ts"; | 26 | import { listThreadPullRequests } from "./handlers.ts"; |
| 27 | import * as PullRequestsHandlers from "./handlers.ts"; | ||
| 27 | import { PullRequestLinkFailedError, PullRequestsToolkit } from "./tools.ts"; | 28 | import { PullRequestLinkFailedError, PullRequestsToolkit } from "./tools.ts"; |
| 28 | 29 | ||
| 29 | const PROJECT_ID = ProjectId.make("project-1"); | 30 | const PROJECT_ID = ProjectId.make("project-1"); |
| ... | @@ -152,7 +153,7 @@ const makeHarness = Effect.fn("makePullRequestsToolkitHarness")(function* ( | ... | @@ -152,7 +153,7 @@ const makeHarness = Effect.fn("makePullRequestsToolkitHarness")(function* ( |
| 152 | yield* Ref.update(commands, (recorded) => [...recorded, command]); | 153 | yield* Ref.update(commands, (recorded) => [...recorded, command]); |
| 153 | return { sequence: 1, storedEvents: [] }; | 154 | return { sequence: 1, storedEvents: [] }; |
| 154 | }); | 155 | }); |
| 155 | const dependencies = Layer.mergeAll( | 156 | const layerDependencies = Layer.mergeAll( |
| 156 | Layer.mock(ProjectService.ProjectService)({ | 157 | Layer.mock(ProjectService.ProjectService)({ |
| 157 | getShell: () => Effect.succeed(Option.fromNullishOr(project)), | 158 | getShell: () => Effect.succeed(Option.fromNullishOr(project)), |
| 158 | }), | 159 | }), |
| ... | @@ -164,7 +165,7 @@ const makeHarness = Effect.fn("makePullRequestsToolkitHarness")(function* ( | ... | @@ -164,7 +165,7 @@ const makeHarness = Effect.fn("makePullRequestsToolkitHarness")(function* ( |
| 164 | Layer.succeed(Crypto.Crypto, testCrypto), | 165 | Layer.succeed(Crypto.Crypto, testCrypto), |
| 165 | ); | 166 | ); |
| 166 | const toolkit = yield* PullRequestsToolkit.pipe( | 167 | const toolkit = yield* PullRequestsToolkit.pipe( |
| 167 | Effect.provide(PullRequestsToolkitHandlersLive.pipe(Layer.provide(dependencies))), | 168 | Effect.provide(PullRequestsHandlers.layer.pipe(Layer.provide(layerDependencies))), |
| 168 | ); | 169 | ); |
| 169 | const call = <Name extends keyof typeof PullRequestsToolkit.tools>( | 170 | const call = <Name extends keyof typeof PullRequestsToolkit.tools>( |
| 170 | name: Name, | 171 | name: Name, |
| ... | @@ -179,7 +180,7 @@ const makeHarness = Effect.fn("makePullRequestsToolkitHarness")(function* ( | ... | @@ -179,7 +180,7 @@ const makeHarness = Effect.fn("makePullRequestsToolkitHarness")(function* ( |
| 179 | (chunk) => chunk.at(-1)!.result as Tool.Success<(typeof PullRequestsToolkit.tools)[Name]>, | 180 | (chunk) => chunk.at(-1)!.result as Tool.Success<(typeof PullRequestsToolkit.tools)[Name]>, |
| 180 | ), | 181 | ), |
| 181 | Effect.provideService(McpInvocationContext.McpInvocationContext, invocation(capabilities)), | 182 | Effect.provideService(McpInvocationContext.McpInvocationContext, invocation(capabilities)), |
| 182 | Effect.provide(dependencies), | 183 | Effect.provide(layerDependencies), |
| 183 | ); | 184 | ); |
| 184 | return { commands, call }; | 185 | return { commands, call }; |
| 185 | }); | 186 | }); |
apps/server/src/mcp/toolkits/pullRequests/handlers.ts+1-1| ... | @@ -379,4 +379,4 @@ const make = Effect.gen(function* () { | ... | @@ -379,4 +379,4 @@ const make = Effect.gen(function* () { |
| 379 | }); | 379 | }); |
| 380 | }); | 380 | }); |
| 381 | 381 | ||
| 382 | export const PullRequestsToolkitHandlersLive = PullRequestsToolkit.toLayer(make); | 382 | export const layer = PullRequestsToolkit.toLayer(make); |
apps/server/src/mcp/toolkits/thread/handlers.ts+1-1| ... | @@ -75,7 +75,7 @@ const readQuestion = Effect.fn("mcp.readQuestion")(function* ( | ... | @@ -75,7 +75,7 @@ const readQuestion = Effect.fn("mcp.readQuestion")(function* ( |
| 75 | }); | 75 | }); |
| 76 | return { ...context, request, item }; | 76 | return { ...context, request, item }; |
| 77 | }); | 77 | }); |
| 78 | export const ThreadToolkitHandlersLive = ThreadToolkit.toLayer({ | 78 | export const layer = ThreadToolkit.toLayer({ |
| 79 | run_scheduled_task_now: (input) => | 79 | run_scheduled_task_now: (input) => |
| 80 | Effect.gen(function* () { | 80 | Effect.gen(function* () { |
| 81 | yield* readFullAccessCaller( | 81 | yield* readFullAccessCaller( |
apps/server/src/mcp/toolkits/worktree/handlers.ts+1-1| ... | @@ -48,4 +48,4 @@ const handlers = { | ... | @@ -48,4 +48,4 @@ const handlers = { |
| 48 | }), | 48 | }), |
| 49 | } satisfies Parameters<typeof WorktreeToolkit.toLayer>[0]; | 49 | } satisfies Parameters<typeof WorktreeToolkit.toLayer>[0]; |
| 50 | 50 | ||
| 51 | export const WorktreeToolkitHandlersLive = WorktreeToolkit.toLayer(handlers); | 51 | export const layer = WorktreeToolkit.toLayer(handlers); |
apps/server/src/mcp/toolkits/worktree/registration.test.ts+4-4| ... | @@ -26,7 +26,7 @@ import * as McpHttpServer from "../../McpHttpServer.ts"; | ... | @@ -26,7 +26,7 @@ import * as McpHttpServer from "../../McpHttpServer.ts"; |
| 26 | import * as McpSessionRegistry from "../../McpSessionRegistry.ts"; | 26 | import * as McpSessionRegistry from "../../McpSessionRegistry.ts"; |
| 27 | import * as PreviewAutomationBroker from "../../PreviewAutomationBroker.ts"; | 27 | import * as PreviewAutomationBroker from "../../PreviewAutomationBroker.ts"; |
| 28 | 28 | ||
| 29 | const StubServicesLive = Layer.mergeAll( | 29 | const layerStubServices = Layer.mergeAll( |
| 30 | Layer.mock(Orchestrator.OrchestratorV2)({}), | 30 | Layer.mock(Orchestrator.OrchestratorV2)({}), |
| 31 | Layer.mock(ProjectionStore.ProjectionStoreV2)({}), | 31 | Layer.mock(ProjectionStore.ProjectionStoreV2)({}), |
| 32 | Layer.mock(DeviceService.DeviceService)({}), | 32 | Layer.mock(DeviceService.DeviceService)({}), |
| ... | @@ -66,8 +66,8 @@ const decodeToolsListPayload = Schema.decodeUnknownEffect(ToolsListPayload); | ... | @@ -66,8 +66,8 @@ const decodeToolsListPayload = Schema.decodeUnknownEffect(ToolsListPayload); |
| 66 | it.effect("production mcp layer lists worktree tools over http", () => | 66 | it.effect("production mcp layer lists worktree tools over http", () => |
| 67 | Effect.scoped( | 67 | Effect.scoped( |
| 68 | Effect.gen(function* () { | 68 | Effect.gen(function* () { |
| 69 | const routes = McpHttpServer.layer.pipe(Layer.provide(McpSessionRegistry.layer)); | 69 | const layerRoutes = McpHttpServer.layer.pipe(Layer.provide(McpSessionRegistry.layer)); |
| 70 | yield* HttpRouter.serve(routes, { | 70 | yield* HttpRouter.serve(layerRoutes, { |
| 71 | disableListenLog: true, | 71 | disableListenLog: true, |
| 72 | disableLogger: true, | 72 | disableLogger: true, |
| 73 | }).pipe( | 73 | }).pipe( |
| ... | @@ -77,7 +77,7 @@ it.effect("production mcp layer lists worktree tools over http", () => | ... | @@ -77,7 +77,7 @@ it.effect("production mcp layer lists worktree tools over http", () => |
| 77 | }), | 77 | }), |
| 78 | ), | 78 | ), |
| 79 | Layer.provide(PreviewAutomationBroker.layer), | 79 | Layer.provide(PreviewAutomationBroker.layer), |
| 80 | Layer.provide(StubServicesLive), | 80 | Layer.provide(layerStubServices), |
| 81 | Layer.build, | 81 | Layer.build, |
| 82 | ); | 82 | ); |
| 83 | 83 |
apps/server/src/observability/Layers/Observability.ts+14-17| ... | @@ -1,9 +1,6 @@ | ... | @@ -1,9 +1,6 @@ |
| 1 | import { httpHeaderRedactionLayer } from "@t3tools/shared/httpObservability"; | 1 | import * as HttpObservability from "@t3tools/shared/httpObservability"; |
| 2 | import { | 2 | import { makeLocalFileTracer, makeTraceSink } from "@t3tools/shared/observability"; |
| 3 | makeLocalFileTracer, | 3 | import * as SharedObservability from "@t3tools/shared/observability"; |
| 4 | makeTraceSink, | ||
| 5 | otlpSerializationLayer, | ||
| 6 | } from "@t3tools/shared/observability"; | ||
| 7 | import * as OtelEnvironment from "@t3tools/shared/otelEnvironment"; | 4 | import * as OtelEnvironment from "@t3tools/shared/otelEnvironment"; |
| 8 | import * as Effect from "effect/Effect"; | 5 | import * as Effect from "effect/Effect"; |
| 9 | import * as Layer from "effect/Layer"; | 6 | import * as Layer from "effect/Layer"; |
| ... | @@ -15,10 +12,10 @@ import * as OtlpTracer from "effect/observability/OtlpTracer"; | ... | @@ -15,10 +12,10 @@ import * as OtlpTracer from "effect/observability/OtlpTracer"; |
| 15 | 12 | ||
| 16 | import * as ServerConfig from "../../config.ts"; | 13 | import * as ServerConfig from "../../config.ts"; |
| 17 | import * as ResourceAttribution from "../../resourceTelemetry/ResourceAttribution.ts"; | 14 | import * as ResourceAttribution from "../../resourceTelemetry/ResourceAttribution.ts"; |
| 18 | import { ServerLoggerLive } from "../../serverLogger.ts"; | 15 | import * as ServerLogger from "../../serverLogger.ts"; |
| 19 | import * as BrowserTraceCollector from "../BrowserTraceCollector.ts"; | 16 | import * as BrowserTraceCollector from "../BrowserTraceCollector.ts"; |
| 20 | 17 | ||
| 21 | export const ObservabilityLive = Layer.unwrap( | 18 | export const layer = Layer.unwrap( |
| 22 | Effect.gen(function* () { | 19 | Effect.gen(function* () { |
| 23 | const config = yield* ServerConfig.ServerConfig; | 20 | const config = yield* ServerConfig.ServerConfig; |
| 24 | 21 | ||
| ... | @@ -26,17 +23,17 @@ export const ObservabilityLive = Layer.unwrap( | ... | @@ -26,17 +23,17 @@ export const ObservabilityLive = Layer.unwrap( |
| 26 | const metrics = config.otlpMetricsExport; | 23 | const metrics = config.otlpMetricsExport; |
| 27 | // The trace serializer stays in the returned context because the browser | 24 | // The trace serializer stays in the returned context because the browser |
| 28 | // trace forwarder exports on the same signal. | 25 | // trace forwarder exports on the same signal. |
| 29 | const serializationLayer = otlpSerializationLayer(traces.protocol); | 26 | const layerSerialization = SharedObservability.layerOtlpSerialization(traces.protocol); |
| 30 | const resource = ServerConfig.otlpResource(config); | 27 | const resource = ServerConfig.otlpResource(config); |
| 31 | const attribution = yield* ResourceAttribution.ResourceAttribution; | 28 | const attribution = yield* ResourceAttribution.ResourceAttribution; |
| 32 | 29 | ||
| 33 | const traceReferencesLayer = Layer.mergeAll( | 30 | const layerTraceReferences = Layer.mergeAll( |
| 34 | Layer.succeed(Tracer.MinimumTraceLevel, config.traceMinLevel), | 31 | Layer.succeed(Tracer.MinimumTraceLevel, config.traceMinLevel), |
| 35 | Layer.succeed(References.TracerTimingEnabled, config.traceTimingEnabled), | 32 | Layer.succeed(References.TracerTimingEnabled, config.traceTimingEnabled), |
| 36 | httpHeaderRedactionLayer, | 33 | HttpObservability.layer, |
| 37 | ); | 34 | ); |
| 38 | 35 | ||
| 39 | const tracerLayer = Layer.unwrap( | 36 | const layerTracer = Layer.unwrap( |
| 40 | Effect.gen(function* () { | 37 | Effect.gen(function* () { |
| 41 | const sink = yield* makeTraceSink({ | 38 | const sink = yield* makeTraceSink({ |
| 42 | filePath: config.serverTracePath, | 39 | filePath: config.serverTracePath, |
| ... | @@ -76,7 +73,7 @@ export const ObservabilityLive = Layer.unwrap( | ... | @@ -76,7 +73,7 @@ export const ObservabilityLive = Layer.unwrap( |
| 76 | BrowserTraceCollector.layer(sink), | 73 | BrowserTraceCollector.layer(sink), |
| 77 | ); | 74 | ); |
| 78 | }), | 75 | }), |
| 79 | ).pipe(Layer.provide(OtlpExporter.layerFlusher), Layer.provideMerge(serializationLayer)); | 76 | ).pipe(Layer.provide(OtlpExporter.layerFlusher), Layer.provideMerge(layerSerialization)); |
| 80 | 77 | ||
| 81 | const metricsLayer = | 78 | const metricsLayer = |
| 82 | config.otlpMetricsUrl === undefined | 79 | config.otlpMetricsUrl === undefined |
| ... | @@ -86,16 +83,16 @@ export const ObservabilityLive = Layer.unwrap( | ... | @@ -86,16 +83,16 @@ export const ObservabilityLive = Layer.unwrap( |
| 86 | exportInterval: `${metrics.exportIntervalMs} millis`, | 83 | exportInterval: `${metrics.exportIntervalMs} millis`, |
| 87 | headers: metrics.headers, | 84 | headers: metrics.headers, |
| 88 | resource, | 85 | resource, |
| 89 | }).pipe(Layer.provide(otlpSerializationLayer(metrics.protocol))); | 86 | }).pipe(Layer.provide(SharedObservability.layerOtlpSerialization(metrics.protocol))); |
| 90 | 87 | ||
| 91 | // Logged once the server's loggers are installed, so the warnings use them. | 88 | // Logged once the server's loggers are installed, so the warnings use them. |
| 92 | const otelWarningsLayer = Layer.effectDiscard( | 89 | const layerOtelWarnings = Layer.effectDiscard( |
| 93 | Effect.forEach(config.otelEnvironment.warnings, (warning) => Effect.logWarning(warning)), | 90 | Effect.forEach(config.otelEnvironment.warnings, (warning) => Effect.logWarning(warning)), |
| 94 | ); | 91 | ); |
| 95 | 92 | ||
| 96 | return otelWarningsLayer.pipe( | 93 | return layerOtelWarnings.pipe( |
| 97 | Layer.provideMerge( | 94 | Layer.provideMerge( |
| 98 | Layer.mergeAll(ServerLoggerLive, traceReferencesLayer, tracerLayer, metricsLayer), | 95 | Layer.mergeAll(ServerLogger.layer, layerTraceReferences, layerTracer, metricsLayer), |
| 99 | ), | 96 | ), |
| 100 | Layer.provide( | 97 | Layer.provide( |
| 101 | OtelEnvironment.layerResourceAttributes(config.otelEnvironment.resourceAttributes), | 98 | OtelEnvironment.layerResourceAttributes(config.otelEnvironment.resourceAttributes), |
apps/server/src/orchestration-v2/AcpRegistryOrchestratorV2.live.test.ts+32-32| ... | @@ -23,12 +23,12 @@ import * as CheckpointStore from "../checkpointing/CheckpointStore.ts"; | ... | @@ -23,12 +23,12 @@ import * as CheckpointStore from "../checkpointing/CheckpointStore.ts"; |
| 23 | import * as BackgroundPolicy from "../background/BackgroundPolicy.ts"; | 23 | import * as BackgroundPolicy from "../background/BackgroundPolicy.ts"; |
| 24 | import * as HostPowerMonitor from "../background/HostPowerMonitor.ts"; | 24 | import * as HostPowerMonitor from "../background/HostPowerMonitor.ts"; |
| 25 | import * as ServerConfig from "../config.ts"; | 25 | import * as ServerConfig from "../config.ts"; |
| 26 | import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; | 26 | import * as SqlitePersistence from "../persistence/Layers/Sqlite.ts"; |
| 27 | import * as AntigravityInstallation from "../provider/AntigravityInstallation.ts"; | 27 | import * as AntigravityInstallation from "../provider/AntigravityInstallation.ts"; |
| 28 | import * as CodexInstallation from "../provider/CodexInstallation.ts"; | 28 | import * as CodexInstallation from "../provider/CodexInstallation.ts"; |
| 29 | import * as ServerEnvironment from "../environment/ServerEnvironment.ts"; | 29 | import * as ServerEnvironment from "../environment/ServerEnvironment.ts"; |
| 30 | import * as ModelManifest from "../provider/ModelManifest.ts"; | 30 | import * as ModelManifest from "../provider/ModelManifest.ts"; |
| 31 | import { ProviderInstanceRegistryHydrationLive } from "../provider/Layers/ProviderInstanceRegistryHydration.ts"; | 31 | import * as ProviderInstanceRegistryHydration from "../provider/Layers/ProviderInstanceRegistryHydration.ts"; |
| 32 | import * as ProviderEventLoggers from "../provider/Layers/ProviderEventLoggers.ts"; | 32 | import * as ProviderEventLoggers from "../provider/Layers/ProviderEventLoggers.ts"; |
| 33 | import * as OpenCodeRuntime from "../provider/opencodeRuntime.ts"; | 33 | import * as OpenCodeRuntime from "../provider/opencodeRuntime.ts"; |
| 34 | import * as OpenCodeServerLedger from "../provider/OpenCodeServerLedger.ts"; | 34 | import * as OpenCodeServerLedger from "../provider/OpenCodeServerLedger.ts"; |
| ... | @@ -36,8 +36,8 @@ import * as ServerSettings from "../serverSettings.ts"; | ... | @@ -36,8 +36,8 @@ import * as ServerSettings from "../serverSettings.ts"; |
| 36 | import * as VcsDriverRegistry from "../vcs/VcsDriverRegistry.ts"; | 36 | import * as VcsDriverRegistry from "../vcs/VcsDriverRegistry.ts"; |
| 37 | import * as VcsProcess from "../vcs/VcsProcess.ts"; | 37 | import * as VcsProcess from "../vcs/VcsProcess.ts"; |
| 38 | import * as Orchestrator from "./Orchestrator.ts"; | 38 | import * as Orchestrator from "./Orchestrator.ts"; |
| 39 | import { worktreeRepairDependenciesTestLayer } from "./ProviderTurnStartService.testkit.ts"; | 39 | import * as ProviderTurnStartServiceTestkit from "./ProviderTurnStartService.testkit.ts"; |
| 40 | import { OrchestrationV2LayerLive } from "./runtimeLayer.ts"; | 40 | import * as RuntimeLayer from "./runtimeLayer.ts"; |
| 41 | import * as McpSessionRegistryTestkit from "../mcp/McpSessionRegistry.testkit.ts"; | 41 | import * as McpSessionRegistryTestkit from "../mcp/McpSessionRegistry.testkit.ts"; |
| 42 | 42 | ||
| 43 | // The Antigravity switch is a durable, named conformance fixture for Google's | 43 | // The Antigravity switch is a durable, named conformance fixture for Google's |
| ... | @@ -46,7 +46,7 @@ import * as McpSessionRegistryTestkit from "../mcp/McpSessionRegistry.testkit.ts | ... | @@ -46,7 +46,7 @@ import * as McpSessionRegistryTestkit from "../mcp/McpSessionRegistry.testkit.ts |
| 46 | // | 46 | // |
| 47 | // T3_ACP_ANTIGRAVITY_LIVE=1 ../../node_modules/.bin/vp test run \ | 47 | // T3_ACP_ANTIGRAVITY_LIVE=1 ../../node_modules/.bin/vp test run \ |
| 48 | // src/orchestration-v2/AcpRegistryOrchestratorV2.live.test.ts | 48 | // src/orchestration-v2/AcpRegistryOrchestratorV2.live.test.ts |
| 49 | const PlatformTestLayer = Layer.merge( | 49 | const layerPlatformTest = Layer.merge( |
| 50 | NodeServices.layer, | 50 | NodeServices.layer, |
| 51 | Layer.mock(SourceControlProviderRegistry.SourceControlProviderRegistry)({ | 51 | Layer.mock(SourceControlProviderRegistry.SourceControlProviderRegistry)({ |
| 52 | resolveLink: () => Effect.die("unused title link"), | 52 | resolveLink: () => Effect.die("unused title link"), |
| ... | @@ -64,19 +64,19 @@ const liveModelSelection = { | ... | @@ -64,19 +64,19 @@ const liveModelSelection = { |
| 64 | model: "default", | 64 | model: "default", |
| 65 | } satisfies ModelSelection; | 65 | } satisfies ModelSelection; |
| 66 | 66 | ||
| 67 | const serverConfigLayer = ServerConfig.layerTest(process.cwd(), { | 67 | const layerServerConfig = ServerConfig.layerTest(process.cwd(), { |
| 68 | prefix: "t3-acp-registry-v2-live-", | 68 | prefix: "t3-acp-registry-v2-live-", |
| 69 | }); | 69 | }); |
| 70 | 70 | ||
| 71 | const vcsDriverRegistryLayer = VcsDriverRegistry.layer.pipe( | 71 | const layerVcsDriverRegistry = VcsDriverRegistry.layer.pipe( |
| 72 | Layer.provide(VcsProcess.layer), | 72 | Layer.provide(VcsProcess.layer), |
| 73 | Layer.provide(serverConfigLayer), | 73 | Layer.provide(layerServerConfig), |
| 74 | Layer.provide(PlatformTestLayer), | 74 | Layer.provide(layerPlatformTest), |
| 75 | ); | 75 | ); |
| 76 | 76 | ||
| 77 | const checkpointStoreLayer = CheckpointStore.layer.pipe(Layer.provide(vcsDriverRegistryLayer)); | 77 | const layerCheckpointStore = CheckpointStore.layer.pipe(Layer.provide(layerVcsDriverRegistry)); |
| 78 | 78 | ||
| 79 | const serverSettingsLayer = ServerSettings.layerTest({ | 79 | const layerServerSettings = ServerSettings.layerTest({ |
| 80 | providerInstances: { | 80 | providerInstances: { |
| 81 | [liveInstanceId]: { | 81 | [liveInstanceId]: { |
| 82 | driver: ProviderDriverKind.make("acpRegistry"), | 82 | driver: ProviderDriverKind.make("acpRegistry"), |
| ... | @@ -89,24 +89,24 @@ const serverSettingsLayer = ServerSettings.layerTest({ | ... | @@ -89,24 +89,24 @@ const serverSettingsLayer = ServerSettings.layerTest({ |
| 89 | }, | 89 | }, |
| 90 | }, | 90 | }, |
| 91 | }); | 91 | }); |
| 92 | const backgroundPolicyLayer = BackgroundPolicy.layer.pipe( | 92 | const layerBackgroundPolicy = BackgroundPolicy.layer.pipe( |
| 93 | Layer.provide(Layer.effect(HostPowerMonitor.HostPowerMonitor, HostPowerMonitor.make())), | 93 | Layer.provide(Layer.effect(HostPowerMonitor.HostPowerMonitor, HostPowerMonitor.make())), |
| 94 | Layer.provide(serverSettingsLayer), | 94 | Layer.provide(layerServerSettings), |
| 95 | ); | 95 | ); |
| 96 | const providerInstanceRegistryLayer = ProviderInstanceRegistryHydrationLive.pipe( | 96 | const layerProviderInstanceRegistry = ProviderInstanceRegistryHydration.layer.pipe( |
| 97 | Layer.provide( | 97 | Layer.provide( |
| 98 | Layer.mergeAll( | 98 | Layer.mergeAll( |
| 99 | serverConfigLayer.pipe(Layer.provide(PlatformTestLayer)), | 99 | layerServerConfig.pipe(Layer.provide(layerPlatformTest)), |
| 100 | serverSettingsLayer, | 100 | layerServerSettings, |
| 101 | ServerSecretStore.layer.pipe( | 101 | ServerSecretStore.layer.pipe( |
| 102 | Layer.provide(serverConfigLayer), | 102 | Layer.provide(layerServerConfig), |
| 103 | Layer.provide(PlatformTestLayer), | 103 | Layer.provide(layerPlatformTest), |
| 104 | ), | 104 | ), |
| 105 | NodeServices.layer, | 105 | NodeServices.layer, |
| 106 | FetchHttpClient.layer, | 106 | FetchHttpClient.layer, |
| 107 | OpenCodeRuntime.OpenCodeRuntimeLive.pipe( | 107 | OpenCodeRuntime.layer.pipe( |
| 108 | Layer.provide(OpenCodeServerLedger.layerTest), | 108 | Layer.provide(OpenCodeServerLedger.layerTest), |
| 109 | Layer.provide(PlatformTestLayer), | 109 | Layer.provide(layerPlatformTest), |
| 110 | ), | 110 | ), |
| 111 | Layer.succeed( | 111 | Layer.succeed( |
| 112 | ProviderEventLoggers.ProviderEventLoggers, | 112 | ProviderEventLoggers.ProviderEventLoggers, |
| ... | @@ -114,9 +114,9 @@ const providerInstanceRegistryLayer = ProviderInstanceRegistryHydrationLive.pipe | ... | @@ -114,9 +114,9 @@ const providerInstanceRegistryLayer = ProviderInstanceRegistryHydrationLive.pipe |
| 114 | ), | 114 | ), |
| 115 | ModelManifest.layerTest, | 115 | ModelManifest.layerTest, |
| 116 | AntigravityInstallation.AntigravityInstallation.layer.pipe( | 116 | AntigravityInstallation.AntigravityInstallation.layer.pipe( |
| 117 | Layer.provide(serverConfigLayer.pipe(Layer.provide(PlatformTestLayer))), | 117 | Layer.provide(layerServerConfig.pipe(Layer.provide(layerPlatformTest))), |
| 118 | Layer.provide(FetchHttpClient.layer), | 118 | Layer.provide(FetchHttpClient.layer), |
| 119 | Layer.provide(PlatformTestLayer), | 119 | Layer.provide(layerPlatformTest), |
| 120 | ), | 120 | ), |
| 121 | // The Codex driver now resolves managed ChatGPT installs; these runs never launch Codex. | 121 | // The Codex driver now resolves managed ChatGPT installs; these runs never launch Codex. |
| 122 | Layer.mock(CodexInstallation.CodexInstallation)({ | 122 | Layer.mock(CodexInstallation.CodexInstallation)({ |
| ... | @@ -131,17 +131,17 @@ const providerInstanceRegistryLayer = ProviderInstanceRegistryHydrationLive.pipe | ... | @@ -131,17 +131,17 @@ const providerInstanceRegistryLayer = ProviderInstanceRegistryHydrationLive.pipe |
| 131 | ), | 131 | ), |
| 132 | ); | 132 | ); |
| 133 | 133 | ||
| 134 | const liveLayer = OrchestrationV2LayerLive.pipe( | 134 | const layerLive = RuntimeLayer.layer.pipe( |
| 135 | Layer.provide(McpSessionRegistryTestkit.layer), | 135 | Layer.provide(McpSessionRegistryTestkit.layer), |
| 136 | Layer.provide(SqlitePersistenceMemory), | 136 | Layer.provide(SqlitePersistence.layerMemory), |
| 137 | Layer.provide(checkpointStoreLayer), | 137 | Layer.provide(layerCheckpointStore), |
| 138 | Layer.provide(serverConfigLayer), | 138 | Layer.provide(layerServerConfig), |
| 139 | Layer.provide(serverSettingsLayer), | 139 | Layer.provide(layerServerSettings), |
| 140 | Layer.provide(providerInstanceRegistryLayer), | 140 | Layer.provide(layerProviderInstanceRegistry), |
| 141 | Layer.provide(ResetCreditCoordinator.layer), | 141 | Layer.provide(ResetCreditCoordinator.layer), |
| 142 | Layer.provide(backgroundPolicyLayer), | 142 | Layer.provide(layerBackgroundPolicy), |
| 143 | Layer.provide(worktreeRepairDependenciesTestLayer), | 143 | Layer.provide(ProviderTurnStartServiceTestkit.layer), |
| 144 | Layer.provide(PlatformTestLayer), | 144 | Layer.provide(layerPlatformTest), |
| 145 | ); | 145 | ); |
| 146 | 146 | ||
| 147 | const waitForIdle = Effect.fn("AcpRegistryOrchestratorV2Live.waitForIdle")(function* ( | 147 | const waitForIdle = Effect.fn("AcpRegistryOrchestratorV2Live.waitForIdle")(function* ( |
| ... | @@ -248,7 +248,7 @@ describe.runIf(runAntigravityFixture || process.env.T3_ACP_REGISTRY_LIVE_ORCHEST | ... | @@ -248,7 +248,7 @@ describe.runIf(runAntigravityFixture || process.env.T3_ACP_REGISTRY_LIVE_ORCHEST |
| 248 | finalProjection.providerTurns.map((turn) => turn.status), | 248 | finalProjection.providerTurns.map((turn) => turn.status), |
| 249 | ["completed", "completed"], | 249 | ["completed", "completed"], |
| 250 | ); | 250 | ); |
| 251 | }).pipe(Effect.provide(liveLayer), Effect.scoped), | 251 | }).pipe(Effect.provide(layerLive), Effect.scoped), |
| 252 | 480_000, | 252 | 480_000, |
| 253 | ); | 253 | ); |
| 254 | }, | 254 | }, |
apps/server/src/orchestration-v2/Adapters/AcpAdapterV2.test.ts+90-90| ... | @@ -100,11 +100,11 @@ import { | ... | @@ -100,11 +100,11 @@ import { |
| 100 | 100 | ||
| 101 | const DEFAULT_GROK_SETTINGS = Schema.decodeSync(GrokSettings)({}); | 101 | const DEFAULT_GROK_SETTINGS = Schema.decodeSync(GrokSettings)({}); |
| 102 | 102 | ||
| 103 | const serverConfigLayer = ServerConfig.layerTest(process.cwd(), { | 103 | const layerServerConfig = ServerConfig.layerTest(process.cwd(), { |
| 104 | prefix: "t3-acp-v2-adapter-", | 104 | prefix: "t3-acp-v2-adapter-", |
| 105 | }).pipe(Layer.provide(NodeServices.layer)); | 105 | }).pipe(Layer.provide(NodeServices.layer)); |
| 106 | 106 | ||
| 107 | const testLayer = Layer.mergeAll(NodeServices.layer, IdAllocator.layer, serverConfigLayer); | 107 | const layerTest = Layer.mergeAll(NodeServices.layer, IdAllocator.layer, layerServerConfig); |
| 108 | const ACP_TEST_DRIVER = ProviderDriverKind.make("acp-test"); | 108 | const ACP_TEST_DRIVER = ProviderDriverKind.make("acp-test"); |
| 109 | const decodeUnknownJson = Schema.decodeUnknownOption(Schema.fromJsonString(Schema.Unknown)); | 109 | const decodeUnknownJson = Schema.decodeUnknownOption(Schema.fromJsonString(Schema.Unknown)); |
| 110 | 110 | ||
| ... | @@ -692,7 +692,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -692,7 +692,7 @@ describe("AcpAdapterV2", () => { |
| 692 | assert.equal(retries.at(-1)?.status, "completed"); | 692 | assert.equal(retries.at(-1)?.status, "completed"); |
| 693 | assert.equal(retries.at(-1)?.title, "Provider recovered"); | 693 | assert.equal(retries.at(-1)?.title, "Provider recovered"); |
| 694 | } | 694 | } |
| 695 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 695 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 696 | ); | 696 | ); |
| 697 | 697 | ||
| 698 | it("preserves legacy ids and scopes v2 ids by provider instance", () => { | 698 | it("preserves legacy ids and scopes v2 ids by provider instance", () => { |
| ... | @@ -777,7 +777,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -777,7 +777,7 @@ describe("AcpAdapterV2", () => { |
| 777 | assert.deepEqual(mcpServer.args, ["acp-mcp-bridge"]); | 777 | assert.deepEqual(mcpServer.args, ["acp-mcp-bridge"]); |
| 778 | assert.equal(runtimeInput?.processEnvironment?.T3_ACP_MCP_NODE, process.execPath); | 778 | assert.equal(runtimeInput?.processEnvironment?.T3_ACP_MCP_NODE, process.execPath); |
| 779 | assert.equal(runtimeInput?.processEnvironment?.T3_ACP_MCP_ENTRYPOINT, undefined); | 779 | assert.equal(runtimeInput?.processEnvironment?.T3_ACP_MCP_ENTRYPOINT, undefined); |
| 780 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 780 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 781 | ); | 781 | ); |
| 782 | 782 | ||
| 783 | it.live("refreshes ACP prompt instructions when the interaction mode changes", () => | 783 | it.live("refreshes ACP prompt instructions when the interaction mode changes", () => |
| ... | @@ -904,7 +904,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -904,7 +904,7 @@ describe("AcpAdapterV2", () => { |
| 904 | "session/set_config_option", | 904 | "session/set_config_option", |
| 905 | "Build should restore the native mode that T3 temporarily replaced for Plan", | 905 | "Build should restore the native mode that T3 temporarily replaced for Plan", |
| 906 | ); | 906 | ); |
| 907 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 907 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 908 | ); | 908 | ); |
| 909 | 909 | ||
| 910 | it.effect("starts a new replay message after ACP v2 plan boundaries", () => | 910 | it.effect("starts a new replay message after ACP v2 plan boundaries", () => |
| ... | @@ -995,7 +995,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -995,7 +995,7 @@ describe("AcpAdapterV2", () => { |
| 995 | snapshot.messages.map((message) => message.text), | 995 | snapshot.messages.map((message) => message.text), |
| 996 | ["before plan", "after plan"], | 996 | ["before plan", "after plan"], |
| 997 | ); | 997 | ); |
| 998 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 998 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 999 | ); | 999 | ); |
| 1000 | 1000 | ||
| 1001 | it.effect("keeps Devin parent paragraphs intact while projecting native child work", () => | 1001 | it.effect("keeps Devin parent paragraphs intact while projecting native child work", () => |
| ... | @@ -1303,7 +1303,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -1303,7 +1303,7 @@ describe("AcpAdapterV2", () => { |
| 1303 | ); | 1303 | ); |
| 1304 | assert.equal(parentTools.length, 2); | 1304 | assert.equal(parentTools.length, 2); |
| 1305 | assert.equal(parentTools[0]?.title, "Parent tool finished"); | 1305 | assert.equal(parentTools[0]?.title, "Parent tool finished"); |
| 1306 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 1306 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 1307 | ); | 1307 | ); |
| 1308 | 1308 | ||
| 1309 | it.effect("projects ACP v2 fidelity updates into first-class orchestration items", () => | 1309 | it.effect("projects ACP v2 fidelity updates into first-class orchestration items", () => |
| ... | @@ -1495,7 +1495,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -1495,7 +1495,7 @@ describe("AcpAdapterV2", () => { |
| 1495 | afterOmittedPatch.messages.some((message) => message.text === "late authoritative text"), | 1495 | afterOmittedPatch.messages.some((message) => message.text === "late authoritative text"), |
| 1496 | "omitted late message content must preserve the authoritative text", | 1496 | "omitted late message content must preserve the authoritative text", |
| 1497 | ); | 1497 | ); |
| 1498 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 1498 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 1499 | ); | 1499 | ); |
| 1500 | 1500 | ||
| 1501 | it.live( | 1501 | it.live( |
| ... | @@ -1582,7 +1582,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -1582,7 +1582,7 @@ describe("AcpAdapterV2", () => { |
| 1582 | nativeTurnId: "persisted-session:turn:1", | 1582 | nativeTurnId: "persisted-session:turn:1", |
| 1583 | }), | 1583 | }), |
| 1584 | ); | 1584 | ); |
| 1585 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 1585 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 1586 | ); | 1586 | ); |
| 1587 | 1587 | ||
| 1588 | it.live("preserves new-session fallback when an eager ACP session load is stale", () => | 1588 | it.live("preserves new-session fallback when an eager ACP session load is stale", () => |
| ... | @@ -1689,7 +1689,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -1689,7 +1689,7 @@ describe("AcpAdapterV2", () => { |
| 1689 | ), | 1689 | ), |
| 1690 | }), | 1690 | }), |
| 1691 | ); | 1691 | ); |
| 1692 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 1692 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 1693 | ); | 1693 | ); |
| 1694 | 1694 | ||
| 1695 | it.live("cleans detached fixtures when an assertion aborts the test scope", () => | 1695 | it.live("cleans detached fixtures when an assertion aborts the test scope", () => |
| ... | @@ -1728,7 +1728,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -1728,7 +1728,7 @@ describe("AcpAdapterV2", () => { |
| 1728 | Option.isSome(yield* waitForProcessesToExit(published)), | 1728 | Option.isSome(yield* waitForProcessesToExit(published)), |
| 1729 | "detached cleanup finalizer must reap the Bash and sleep fixture", | 1729 | "detached cleanup finalizer must reap the Bash and sleep fixture", |
| 1730 | ); | 1730 | ); |
| 1731 | }).pipe(Effect.provide(testLayer)), | 1731 | }).pipe(Effect.provide(layerTest)), |
| 1732 | ); | 1732 | ); |
| 1733 | 1733 | ||
| 1734 | it.live("replaces an unexpectedly terminated ACP runtime before the next turn", () => | 1734 | it.live("replaces an unexpectedly terminated ACP runtime before the next turn", () => |
| ... | @@ -1810,7 +1810,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -1810,7 +1810,7 @@ describe("AcpAdapterV2", () => { |
| 1810 | ); | 1810 | ); |
| 1811 | assert.equal(runtimeOrdinalSeen, 2); | 1811 | assert.equal(runtimeOrdinalSeen, 2); |
| 1812 | assert.lengthOf(runtimeInputs, 2); | 1812 | assert.lengthOf(runtimeInputs, 2); |
| 1813 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 1813 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 1814 | ); | 1814 | ); |
| 1815 | 1815 | ||
| 1816 | it.live("reaps detached native work when the provider exits before explicit teardown", () => | 1816 | it.live("reaps detached native work when the provider exits before explicit teardown", () => |
| ... | @@ -1895,7 +1895,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -1895,7 +1895,7 @@ describe("AcpAdapterV2", () => { |
| 1895 | Option.isSome(yield* waitForProcessesToExit(pids)), | 1895 | Option.isSome(yield* waitForProcessesToExit(pids)), |
| 1896 | "provider termination must reap the detached launcher, Bash, and sleep processes", | 1896 | "provider termination must reap the detached launcher, Bash, and sleep processes", |
| 1897 | ); | 1897 | ); |
| 1898 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 1898 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 1899 | ); | 1899 | ); |
| 1900 | 1900 | ||
| 1901 | it.live("surfaces reduced guarantee when delegated cgroup containment is unavailable", () => | 1901 | it.live("surfaces reduced guarantee when delegated cgroup containment is unavailable", () => |
| ... | @@ -1952,7 +1952,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -1952,7 +1952,7 @@ describe("AcpAdapterV2", () => { |
| 1952 | }); | 1952 | }); |
| 1953 | yield* runtime.ensureThread({ threadId, modelSelection, runtimePolicy }); | 1953 | yield* runtime.ensureThread({ threadId, modelSelection, runtimePolicy }); |
| 1954 | assert.equal(containment, "process-ledger-reduced-guarantee"); | 1954 | assert.equal(containment, "process-ledger-reduced-guarantee"); |
| 1955 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 1955 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 1956 | ); | 1956 | ); |
| 1957 | 1957 | ||
| 1958 | it.live("cleans a cgroup lease when the pre-exec join wrapper fails", () => | 1958 | it.live("cleans a cgroup lease when the pre-exec join wrapper fails", () => |
| ... | @@ -2030,7 +2030,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -2030,7 +2030,7 @@ describe("AcpAdapterV2", () => { |
| 2030 | assert.equal(createCalls, 1); | 2030 | assert.equal(createCalls, 1); |
| 2031 | assert.isAtLeast(killCalls, 1); | 2031 | assert.isAtLeast(killCalls, 1); |
| 2032 | assert.isAtLeast(removeCalls, 1); | 2032 | assert.isAtLeast(removeCalls, 1); |
| 2033 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 2033 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 2034 | ); | 2034 | ); |
| 2035 | 2035 | ||
| 2036 | it.effect("negotiates and executes optional native session forks through the ACP runtime", () => | 2036 | it.effect("negotiates and executes optional native session forks through the ACP runtime", () => |
| ... | @@ -2240,7 +2240,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -2240,7 +2240,7 @@ describe("AcpAdapterV2", () => { |
| 2240 | { requestId: "test-terminal-output", method: "terminal/output" }, | 2240 | { requestId: "test-terminal-output", method: "terminal/output" }, |
| 2241 | ); | 2241 | ); |
| 2242 | assert.equal(terminalOutput.output, "Bearer target-thread-token"); | 2242 | assert.equal(terminalOutput.output, "Bearer target-thread-token"); |
| 2243 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 2243 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 2244 | ); | 2244 | ); |
| 2245 | 2245 | ||
| 2246 | it.effect( | 2246 | it.effect( |
| ... | @@ -2339,7 +2339,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -2339,7 +2339,7 @@ describe("AcpAdapterV2", () => { |
| 2339 | { method: "fs/read_text_file", errorCode: -32601 }, | 2339 | { method: "fs/read_text_file", errorCode: -32601 }, |
| 2340 | ]); | 2340 | ]); |
| 2341 | assert.isFalse(yield* fileSystem.exists(probePath)); | 2341 | assert.isFalse(yield* fileSystem.exists(probePath)); |
| 2342 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 2342 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 2343 | ); | 2343 | ); |
| 2344 | 2344 | ||
| 2345 | it.effect("does not turn an unknown permission approval into an execute grant", () => | 2345 | it.effect("does not turn an unknown permission approval into an execute grant", () => |
| ... | @@ -2476,7 +2476,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -2476,7 +2476,7 @@ describe("AcpAdapterV2", () => { |
| 2476 | providerTurnId: pending.runtimeRequest.providerTurnId, | 2476 | providerTurnId: pending.runtimeRequest.providerTurnId, |
| 2477 | }); | 2477 | }); |
| 2478 | yield* Fiber.join(turnFiber); | 2478 | yield* Fiber.join(turnFiber); |
| 2479 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 2479 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 2480 | ); | 2480 | ); |
| 2481 | 2481 | ||
| 2482 | it.effect("fails missing native ACP session ids through the typed start-turn error channel", () => | 2482 | it.effect("fails missing native ACP session ids through the typed start-turn error channel", () => |
| ... | @@ -2538,7 +2538,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -2538,7 +2538,7 @@ describe("AcpAdapterV2", () => { |
| 2538 | assert.equal(error._tag, "ProviderAdapterTurnStartError"); | 2538 | assert.equal(error._tag, "ProviderAdapterTurnStartError"); |
| 2539 | assert.instanceOf(error.cause, ProviderAdapterProtocolError); | 2539 | assert.instanceOf(error.cause, ProviderAdapterProtocolError); |
| 2540 | assert.include(String(error.cause), "missing its ACP session id"); | 2540 | assert.include(String(error.cause), "missing its ACP session id"); |
| 2541 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 2541 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 2542 | ); | 2542 | ); |
| 2543 | 2543 | ||
| 2544 | it.effect("replaces the ACP session and clears conversation state on rollback", () => | 2544 | it.effect("replaces the ACP session and clears conversation state on rollback", () => |
| ... | @@ -2705,7 +2705,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -2705,7 +2705,7 @@ describe("AcpAdapterV2", () => { |
| 2705 | } | 2705 | } |
| 2706 | } | 2706 | } |
| 2707 | assert.equal(secondStatus, "completed"); | 2707 | assert.equal(secondStatus, "completed"); |
| 2708 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 2708 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 2709 | ); | 2709 | ); |
| 2710 | 2710 | ||
| 2711 | it.effect("quarantines callbacks from a failed rollback replacement before retrying", () => | 2711 | it.effect("quarantines callbacks from a failed rollback replacement before retrying", () => |
| ... | @@ -2866,7 +2866,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -2866,7 +2866,7 @@ describe("AcpAdapterV2", () => { |
| 2866 | status: "running", | 2866 | status: "running", |
| 2867 | }); | 2867 | }); |
| 2868 | assert.isFalse(yield* hasPendingBackgroundWork); | 2868 | assert.isFalse(yield* hasPendingBackgroundWork); |
| 2869 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 2869 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 2870 | ); | 2870 | ); |
| 2871 | 2871 | ||
| 2872 | it.effect("keeps the original ACP session usable when a staged replacement terminates", () => | 2872 | it.effect("keeps the original ACP session usable when a staged replacement terminates", () => |
| ... | @@ -2982,7 +2982,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -2982,7 +2982,7 @@ describe("AcpAdapterV2", () => { |
| 2982 | break; | 2982 | break; |
| 2983 | } | 2983 | } |
| 2984 | } | 2984 | } |
| 2985 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 2985 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 2986 | ); | 2986 | ); |
| 2987 | 2987 | ||
| 2988 | it.effect("closes an idle ACP session exactly once through the transition permit", () => | 2988 | it.effect("closes an idle ACP session exactly once through the transition permit", () => |
| ... | @@ -3031,7 +3031,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -3031,7 +3031,7 @@ describe("AcpAdapterV2", () => { |
| 3031 | yield* Scope.close(sessionScope, Exit.void); | 3031 | yield* Scope.close(sessionScope, Exit.void); |
| 3032 | const finalizerMethods = yield* pollProtocolMethods(protocolEvents); | 3032 | const finalizerMethods = yield* pollProtocolMethods(protocolEvents); |
| 3033 | assert.equal(finalizerMethods.filter((method) => method === "session/close").length, 1); | 3033 | assert.equal(finalizerMethods.filter((method) => method === "session/close").length, 1); |
| 3034 | }).pipe(Effect.provide(testLayer)), | 3034 | }).pipe(Effect.provide(layerTest)), |
| 3035 | ); | 3035 | ); |
| 3036 | 3036 | ||
| 3037 | it.effect.each(["grok-build", "composer-2"])( | 3037 | it.effect.each(["grok-build", "composer-2"])( |
| ... | @@ -3088,7 +3088,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -3088,7 +3088,7 @@ describe("AcpAdapterV2", () => { |
| 3088 | ).length, | 3088 | ).length, |
| 3089 | model === "grok-build" ? 0 : 1, | 3089 | model === "grok-build" ? 0 : 1, |
| 3090 | ); | 3090 | ); |
| 3091 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 3091 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 3092 | ); | 3092 | ); |
| 3093 | 3093 | ||
| 3094 | it.live("Grok reapplies an explicit return to the session's setup-time model", () => | 3094 | it.live("Grok reapplies an explicit return to the session's setup-time model", () => |
| ... | @@ -3169,7 +3169,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -3169,7 +3169,7 @@ describe("AcpAdapterV2", () => { |
| 3169 | ).length, | 3169 | ).length, |
| 3170 | 3, | 3170 | 3, |
| 3171 | ); | 3171 | ); |
| 3172 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 3172 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 3173 | ); | 3173 | ); |
| 3174 | 3174 | ||
| 3175 | it.effect("skips requested options that the active ACP session does not expose", () => | 3175 | it.effect("skips requested options that the active ACP session does not expose", () => |
| ... | @@ -3218,7 +3218,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -3218,7 +3218,7 @@ describe("AcpAdapterV2", () => { |
| 3218 | }); | 3218 | }); |
| 3219 | 3219 | ||
| 3220 | assert.equal(runtime.providerSession.status, "ready"); | 3220 | assert.equal(runtime.providerSession.status, "ready"); |
| 3221 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 3221 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 3222 | ); | 3222 | ); |
| 3223 | 3223 | ||
| 3224 | it.effect("reconfigures a loaded ACP session from its own active setup metadata", () => | 3224 | it.effect("reconfigures a loaded ACP session from its own active setup metadata", () => |
| ... | @@ -3329,7 +3329,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -3329,7 +3329,7 @@ describe("AcpAdapterV2", () => { |
| 3329 | rawProtocolRequestParam(event, "configId") === "model", | 3329 | rawProtocolRequestParam(event, "configId") === "model", |
| 3330 | ); | 3330 | ); |
| 3331 | assert.lengthOf(modelConfigurationRequests, 2); | 3331 | assert.lengthOf(modelConfigurationRequests, 2); |
| 3332 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 3332 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 3333 | ); | 3333 | ); |
| 3334 | 3334 | ||
| 3335 | it.live("terminalizes an empty successful foreground Bash tool when the turn completes", () => | 3335 | it.live("terminalizes an empty successful foreground Bash tool when the turn completes", () => |
| ... | @@ -3476,7 +3476,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -3476,7 +3476,7 @@ describe("AcpAdapterV2", () => { |
| 3476 | Stream.runHead, | 3476 | Stream.runHead, |
| 3477 | ); | 3477 | ); |
| 3478 | assert.isTrue(Option.isSome(loadAfterRestart)); | 3478 | assert.isTrue(Option.isSome(loadAfterRestart)); |
| 3479 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 3479 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 3480 | ); | 3480 | ); |
| 3481 | 3481 | ||
| 3482 | it.effect("drains native ACP cancellation before admitting the next prompt", () => | 3482 | it.effect("drains native ACP cancellation before admitting the next prompt", () => |
| ... | @@ -3590,7 +3590,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -3590,7 +3590,7 @@ describe("AcpAdapterV2", () => { |
| 3590 | ), | 3590 | ), |
| 3591 | ); | 3591 | ); |
| 3592 | assert.equal(nextTerminal.type === "turn.terminal" && nextTerminal.status, "completed"); | 3592 | assert.equal(nextTerminal.type === "turn.terminal" && nextTerminal.status, "completed"); |
| 3593 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 3593 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 3594 | ); | 3594 | ); |
| 3595 | 3595 | ||
| 3596 | it.effect("cancels pending permission requests while interrupting an ACP turn", () => | 3596 | it.effect("cancels pending permission requests while interrupting an ACP turn", () => |
| ... | @@ -3691,7 +3691,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -3691,7 +3691,7 @@ describe("AcpAdapterV2", () => { |
| 3691 | ), | 3691 | ), |
| 3692 | ); | 3692 | ); |
| 3693 | assert.equal(terminal.type, "turn.terminal"); | 3693 | assert.equal(terminal.type, "turn.terminal"); |
| 3694 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 3694 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 3695 | ); | 3695 | ); |
| 3696 | 3696 | ||
| 3697 | it.live("keeps hard teardown excluded until a permission response is enqueued", () => | 3697 | it.live("keeps hard teardown excluded until a permission response is enqueued", () => |
| ... | @@ -3790,7 +3790,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -3790,7 +3790,7 @@ describe("AcpAdapterV2", () => { |
| 3790 | yield* Deferred.succeed(releaseResponseAcknowledgement, undefined); | 3790 | yield* Deferred.succeed(releaseResponseAcknowledgement, undefined); |
| 3791 | yield* Fiber.join(responseFiber); | 3791 | yield* Fiber.join(responseFiber); |
| 3792 | yield* Fiber.join(interruptFiber); | 3792 | yield* Fiber.join(interruptFiber); |
| 3793 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 3793 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 3794 | ); | 3794 | ); |
| 3795 | 3795 | ||
| 3796 | it.live("carries elicitation request identity through the completed stdout write", () => | 3796 | it.live("carries elicitation request identity through the completed stdout write", () => |
| ... | @@ -3879,7 +3879,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -3879,7 +3879,7 @@ describe("AcpAdapterV2", () => { |
| 3879 | assert.isUndefined(responseFiber.pollUnsafe()); | 3879 | assert.isUndefined(responseFiber.pollUnsafe()); |
| 3880 | yield* Deferred.succeed(releaseResponseAcknowledgement, undefined); | 3880 | yield* Deferred.succeed(releaseResponseAcknowledgement, undefined); |
| 3881 | yield* Fiber.join(responseFiber); | 3881 | yield* Fiber.join(responseFiber); |
| 3882 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 3882 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 3883 | ); | 3883 | ); |
| 3884 | 3884 | ||
| 3885 | it.live("auto-approves tagged MCP elicitations under full-access policy", () => | 3885 | it.live("auto-approves tagged MCP elicitations under full-access policy", () => |
| ... | @@ -3947,7 +3947,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -3947,7 +3947,7 @@ describe("AcpAdapterV2", () => { |
| 3947 | 3947 | ||
| 3948 | assert.isFalse(events.some((event) => event.type === "runtime_request.updated")); | 3948 | assert.isFalse(events.some((event) => event.type === "runtime_request.updated")); |
| 3949 | assert.isTrue(events.some((event) => event.type === "turn.terminal")); | 3949 | assert.isTrue(events.some((event) => event.type === "turn.terminal")); |
| 3950 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 3950 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 3951 | ); | 3951 | ); |
| 3952 | 3952 | ||
| 3953 | it.live("fails a held native response acknowledgement before normal session close", () => | 3953 | it.live("fails a held native response acknowledgement before normal session close", () => |
| ... | @@ -4055,7 +4055,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -4055,7 +4055,7 @@ describe("AcpAdapterV2", () => { |
| 4055 | assert.include(responseLifecycle, "failed"); | 4055 | assert.include(responseLifecycle, "failed"); |
| 4056 | yield* Deferred.succeed(releaseResponseAcknowledgement, undefined); | 4056 | yield* Deferred.succeed(releaseResponseAcknowledgement, undefined); |
| 4057 | yield* Fiber.join(closeFiber); | 4057 | yield* Fiber.join(closeFiber); |
| 4058 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 4058 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 4059 | ); | 4059 | ); |
| 4060 | 4060 | ||
| 4061 | it.live("rejects delayed native response registration when normal close wins the permit", () => | 4061 | it.live("rejects delayed native response registration when normal close wins the permit", () => |
| ... | @@ -4169,7 +4169,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -4169,7 +4169,7 @@ describe("AcpAdapterV2", () => { |
| 4169 | assert.equal(responseLifecycle.filter((event) => event === "admission_rejected").length, 1); | 4169 | assert.equal(responseLifecycle.filter((event) => event === "admission_rejected").length, 1); |
| 4170 | yield* Deferred.succeed(releaseTransportClose, undefined); | 4170 | yield* Deferred.succeed(releaseTransportClose, undefined); |
| 4171 | yield* Fiber.join(closeFiber); | 4171 | yield* Fiber.join(closeFiber); |
| 4172 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 4172 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 4173 | ); | 4173 | ); |
| 4174 | 4174 | ||
| 4175 | it.live("bounds a missing pending permission response acknowledgement", () => | 4175 | it.live("bounds a missing pending permission response acknowledgement", () => |
| ... | @@ -4310,7 +4310,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -4310,7 +4310,7 @@ describe("AcpAdapterV2", () => { |
| 4310 | assert.include(responseLifecycle, "late_noop"); | 4310 | assert.include(responseLifecycle, "late_noop"); |
| 4311 | yield* Scope.close(sessionScope, Exit.void); | 4311 | yield* Scope.close(sessionScope, Exit.void); |
| 4312 | assert.notInclude(yield* pollProtocolMethods(protocolEvents), "session/close"); | 4312 | assert.notInclude(yield* pollProtocolMethods(protocolEvents), "session/close"); |
| 4313 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 4313 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 4314 | ); | 4314 | ); |
| 4315 | 4315 | ||
| 4316 | it.live("defers caller cancellation until a pending response acknowledgement is bounded", () => | 4316 | it.live("defers caller cancellation until a pending response acknowledgement is bounded", () => |
| ... | @@ -4426,7 +4426,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -4426,7 +4426,7 @@ describe("AcpAdapterV2", () => { |
| 4426 | yield* Fiber.join(cancellationFiber); | 4426 | yield* Fiber.join(cancellationFiber); |
| 4427 | yield* Fiber.join(interruptFiber); | 4427 | yield* Fiber.join(interruptFiber); |
| 4428 | assert.isTrue(yield* Deferred.isDone(releaseNativeHook)); | 4428 | assert.isTrue(yield* Deferred.isDone(releaseNativeHook)); |
| 4429 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 4429 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 4430 | ); | 4430 | ); |
| 4431 | 4431 | ||
| 4432 | it.live("waits for immediate allow and deny permission responses before hard teardown", () => | 4432 | it.live("waits for immediate allow and deny permission responses before hard teardown", () => |
| ... | @@ -4531,7 +4531,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -4531,7 +4531,7 @@ describe("AcpAdapterV2", () => { |
| 4531 | yield* Fiber.interrupt(turnFiber).pipe(Effect.forkDetach); | 4531 | yield* Fiber.interrupt(turnFiber).pipe(Effect.forkDetach); |
| 4532 | }).pipe(Effect.scoped); | 4532 | }).pipe(Effect.scoped); |
| 4533 | } | 4533 | } |
| 4534 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 4534 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 4535 | ); | 4535 | ); |
| 4536 | 4536 | ||
| 4537 | it.live("waits for immediate URL elicitation responses before hard teardown", () => | 4537 | it.live("waits for immediate URL elicitation responses before hard teardown", () => |
| ... | @@ -4624,7 +4624,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -4624,7 +4624,7 @@ describe("AcpAdapterV2", () => { |
| 4624 | yield* Deferred.succeed(releaseResponseAcknowledgement, undefined); | 4624 | yield* Deferred.succeed(releaseResponseAcknowledgement, undefined); |
| 4625 | yield* Fiber.join(interruptFiber); | 4625 | yield* Fiber.join(interruptFiber); |
| 4626 | yield* Fiber.interrupt(turnFiber).pipe(Effect.forkDetach); | 4626 | yield* Fiber.interrupt(turnFiber).pipe(Effect.forkDetach); |
| 4627 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 4627 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 4628 | ); | 4628 | ); |
| 4629 | 4629 | ||
| 4630 | it.live("bounds a missing immediate response acknowledgement before hard teardown", () => | 4630 | it.live("bounds a missing immediate response acknowledgement before hard teardown", () => |
| ... | @@ -4717,7 +4717,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -4717,7 +4717,7 @@ describe("AcpAdapterV2", () => { |
| 4717 | }); | 4717 | }); |
| 4718 | assert.isAtLeast((yield* Clock.currentTimeMillis) - startedAt, 1_500); | 4718 | assert.isAtLeast((yield* Clock.currentTimeMillis) - startedAt, 1_500); |
| 4719 | yield* Fiber.interrupt(turnFiber).pipe(Effect.forkDetach); | 4719 | yield* Fiber.interrupt(turnFiber).pipe(Effect.forkDetach); |
| 4720 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 4720 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 4721 | ); | 4721 | ); |
| 4722 | 4722 | ||
| 4723 | it.live("rejects an elicitation response when hard teardown wins admission", () => | 4723 | it.live("rejects an elicitation response when hard teardown wins admission", () => |
| ... | @@ -4830,7 +4830,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -4830,7 +4830,7 @@ describe("AcpAdapterV2", () => { |
| 4830 | assert.fail("teardown winning admission must reject the elicitation response"); | 4830 | assert.fail("teardown winning admission must reject the elicitation response"); |
| 4831 | } | 4831 | } |
| 4832 | assert.include(Cause.pretty(responseExit.cause), "No pending ACP runtime request"); | 4832 | assert.include(Cause.pretty(responseExit.cause), "No pending ACP runtime request"); |
| 4833 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 4833 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 4834 | ); | 4834 | ); |
| 4835 | 4835 | ||
| 4836 | it.effect("releases an ACP turn when cancellation times out", () => | 4836 | it.effect("releases an ACP turn when cancellation times out", () => |
| ... | @@ -4933,7 +4933,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -4933,7 +4933,7 @@ describe("AcpAdapterV2", () => { |
| 4933 | ), | 4933 | ), |
| 4934 | Stream.runHead, | 4934 | Stream.runHead, |
| 4935 | ); | 4935 | ); |
| 4936 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 4936 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 4937 | ); | 4937 | ); |
| 4938 | 4938 | ||
| 4939 | it.live("treats a second hard Stop as success when the turn is already gone", () => | 4939 | it.live("treats a second hard Stop as success when the turn is already gone", () => |
| ... | @@ -5031,7 +5031,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -5031,7 +5031,7 @@ describe("AcpAdapterV2", () => { |
| 5031 | } | 5031 | } |
| 5032 | } | 5032 | } |
| 5033 | assert.equal(terminal, "interrupted"); | 5033 | assert.equal(terminal, "interrupted"); |
| 5034 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 5034 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 5035 | ); | 5035 | ); |
| 5036 | 5036 | ||
| 5037 | it.effect("finalizes a settled turn held open for background work when interrupted", () => | 5037 | it.effect("finalizes a settled turn held open for background work when interrupted", () => |
| ... | @@ -5125,7 +5125,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -5125,7 +5125,7 @@ describe("AcpAdapterV2", () => { |
| 5125 | } | 5125 | } |
| 5126 | } | 5126 | } |
| 5127 | assert.equal(terminalStatus, "interrupted"); | 5127 | assert.equal(terminalStatus, "interrupted"); |
| 5128 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 5128 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 5129 | ); | 5129 | ); |
| 5130 | 5130 | ||
| 5131 | it.effect( | 5131 | it.effect( |
| ... | @@ -5273,7 +5273,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -5273,7 +5273,7 @@ describe("AcpAdapterV2", () => { |
| 5273 | } | 5273 | } |
| 5274 | assert.equal(carriedItemStatus, "completed"); | 5274 | assert.equal(carriedItemStatus, "completed"); |
| 5275 | assert.equal(secondTerminalStatus, "completed"); | 5275 | assert.equal(secondTerminalStatus, "completed"); |
| 5276 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 5276 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 5277 | ); | 5277 | ); |
| 5278 | 5278 | ||
| 5279 | it.effect( | 5279 | it.effect( |
| ... | @@ -5422,7 +5422,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -5422,7 +5422,7 @@ describe("AcpAdapterV2", () => { |
| 5422 | yield* hasPendingBackgroundWork, | 5422 | yield* hasPendingBackgroundWork, |
| 5423 | "hasPendingBackgroundWork must clear after carryover subagent terminals", | 5423 | "hasPendingBackgroundWork must clear after carryover subagent terminals", |
| 5424 | ); | 5424 | ); |
| 5425 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 5425 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 5426 | ); | 5426 | ); |
| 5427 | 5427 | ||
| 5428 | it.effect("handles a child terminal after carryover rehydrate", () => | 5428 | it.effect("handles a child terminal after carryover rehydrate", () => |
| ... | @@ -5657,7 +5657,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -5657,7 +5657,7 @@ describe("AcpAdapterV2", () => { |
| 5657 | 5657 | ||
| 5658 | yield* Deferred.succeed(releaseSecondPromptCompletion, undefined); | 5658 | yield* Deferred.succeed(releaseSecondPromptCompletion, undefined); |
| 5659 | yield* Fiber.join(secondTurnFiber); | 5659 | yield* Fiber.join(secondTurnFiber); |
| 5660 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 5660 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 5661 | ); | 5661 | ); |
| 5662 | 5662 | ||
| 5663 | it.effect( | 5663 | it.effect( |
| ... | @@ -5880,7 +5880,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -5880,7 +5880,7 @@ describe("AcpAdapterV2", () => { |
| 5880 | "finalize-window terminal must clear the carryover pin", | 5880 | "finalize-window terminal must clear the carryover pin", |
| 5881 | ); | 5881 | ); |
| 5882 | }).pipe(Effect.provideService(Clock.Clock, blockingClock)); | 5882 | }).pipe(Effect.provideService(Clock.Clock, blockingClock)); |
| 5883 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 5883 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 5884 | ); | 5884 | ); |
| 5885 | 5885 | ||
| 5886 | it.effect( | 5886 | it.effect( |
| ... | @@ -6101,7 +6101,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -6101,7 +6101,7 @@ describe("AcpAdapterV2", () => { |
| 6101 | assert.equal(attachTerminal, "completed"); | 6101 | assert.equal(attachTerminal, "completed"); |
| 6102 | assert.equal(completedAfterAttach, 1); | 6102 | assert.equal(completedAfterAttach, 1); |
| 6103 | assert.isFalse(yield* hasPendingBackgroundWork); | 6103 | assert.isFalse(yield* hasPendingBackgroundWork); |
| 6104 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 6104 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 6105 | ); | 6105 | ); |
| 6106 | 6106 | ||
| 6107 | it.effect("finishes a settled root's carryover subagent from its structured end", () => | 6107 | it.effect("finishes a settled root's carryover subagent from its structured end", () => |
| ... | @@ -6249,7 +6249,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -6249,7 +6249,7 @@ describe("AcpAdapterV2", () => { |
| 6249 | yield* hasPendingBackgroundWork, | 6249 | yield* hasPendingBackgroundWork, |
| 6250 | "a finished carryover subagent stops pinning", | 6250 | "a finished carryover subagent stops pinning", |
| 6251 | ); | 6251 | ); |
| 6252 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 6252 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 6253 | ); | 6253 | ); |
| 6254 | 6254 | ||
| 6255 | it.effect("projects completed-root carryover eagerly and drain cannot resurrect it", () => | 6255 | it.effect("projects completed-root carryover eagerly and drain cannot resurrect it", () => |
| ... | @@ -6499,7 +6499,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -6499,7 +6499,7 @@ describe("AcpAdapterV2", () => { |
| 6499 | "buffered spawn ACK must not create a continuation-owned turn item", | 6499 | "buffered spawn ACK must not create a continuation-owned turn item", |
| 6500 | ); | 6500 | ); |
| 6501 | assert.isFalse(yield* hasPendingBackgroundWork); | 6501 | assert.isFalse(yield* hasPendingBackgroundWork); |
| 6502 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 6502 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 6503 | ); | 6503 | ); |
| 6504 | 6504 | ||
| 6505 | it.effect( | 6505 | it.effect( |
| ... | @@ -6787,7 +6787,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -6787,7 +6787,7 @@ describe("AcpAdapterV2", () => { |
| 6787 | "queued continuation must drain the wake content after the user run", | 6787 | "queued continuation must drain the wake content after the user run", |
| 6788 | ); | 6788 | ); |
| 6789 | assert.isFalse(yield* hasPendingBackgroundWork); | 6789 | assert.isFalse(yield* hasPendingBackgroundWork); |
| 6790 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 6790 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 6791 | ); | 6791 | ); |
| 6792 | 6792 | ||
| 6793 | it.effect( | 6793 | it.effect( |
| ... | @@ -7030,7 +7030,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -7030,7 +7030,7 @@ describe("AcpAdapterV2", () => { |
| 7030 | } | 7030 | } |
| 7031 | assert.equal(completedSubagentTurnItems, 1); | 7031 | assert.equal(completedSubagentTurnItems, 1); |
| 7032 | assert.isFalse(yield* hasPendingBackgroundWork); | 7032 | assert.isFalse(yield* hasPendingBackgroundWork); |
| 7033 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 7033 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 7034 | ); | 7034 | ); |
| 7035 | 7035 | ||
| 7036 | it.effect("projects an interrupted root-session end notice at the next attach", () => | 7036 | it.effect("projects an interrupted root-session end notice at the next attach", () => |
| ... | @@ -7257,7 +7257,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -7257,7 +7257,7 @@ describe("AcpAdapterV2", () => { |
| 7257 | } | 7257 | } |
| 7258 | assert.equal(completedSubagentTurnItems, 1); | 7258 | assert.equal(completedSubagentTurnItems, 1); |
| 7259 | assert.isFalse(yield* hasPendingBackgroundWork); | 7259 | assert.isFalse(yield* hasPendingBackgroundWork); |
| 7260 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 7260 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 7261 | ); | 7261 | ); |
| 7262 | 7262 | ||
| 7263 | it.effect( | 7263 | it.effect( |
| ... | @@ -7528,7 +7528,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -7528,7 +7528,7 @@ describe("AcpAdapterV2", () => { |
| 7528 | yield* hasPendingBackgroundWork, | 7528 | yield* hasPendingBackgroundWork, |
| 7529 | "pin must clear after the continuation drains, not when the child session completed", | 7529 | "pin must clear after the continuation drains, not when the child session completed", |
| 7530 | ); | 7530 | ); |
| 7531 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 7531 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 7532 | ); | 7532 | ); |
| 7533 | 7533 | ||
| 7534 | it.effect( | 7534 | it.effect( |
| ... | @@ -7770,7 +7770,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -7770,7 +7770,7 @@ describe("AcpAdapterV2", () => { |
| 7770 | yield* hasPendingBackgroundWork, | 7770 | yield* hasPendingBackgroundWork, |
| 7771 | "hasPendingBackgroundWork must end false after the continuation drains", | 7771 | "hasPendingBackgroundWork must end false after the continuation drains", |
| 7772 | ); | 7772 | ); |
| 7773 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 7773 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 7774 | ); | 7774 | ); |
| 7775 | 7775 | ||
| 7776 | it.effect( | 7776 | it.effect( |
| ... | @@ -7948,7 +7948,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -7948,7 +7948,7 @@ describe("AcpAdapterV2", () => { |
| 7948 | 1, | 7948 | 1, |
| 7949 | "settled soft steer must not respawn the ACP runtime process", | 7949 | "settled soft steer must not respawn the ACP runtime process", |
| 7950 | ); | 7950 | ); |
| 7951 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 7951 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 7952 | ); | 7952 | ); |
| 7953 | 7953 | ||
| 7954 | it.live("preserveRuntimeOnSettledInterrupt does not soften a mid-prompt steering interrupt", () => | 7954 | it.live("preserveRuntimeOnSettledInterrupt does not soften a mid-prompt steering interrupt", () => |
| ... | @@ -8036,7 +8036,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -8036,7 +8036,7 @@ describe("AcpAdapterV2", () => { |
| 8036 | assert.fail("mid-prompt steering interrupt must still take the hard teardown path"); | 8036 | assert.fail("mid-prompt steering interrupt must still take the hard teardown path"); |
| 8037 | } | 8037 | } |
| 8038 | assert.include(Cause.pretty(interruptExit.cause), "session is poisoned"); | 8038 | assert.include(Cause.pretty(interruptExit.cause), "session is poisoned"); |
| 8039 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 8039 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 8040 | ); | 8040 | ); |
| 8041 | 8041 | ||
| 8042 | it.live( | 8042 | it.live( |
| ... | @@ -8232,7 +8232,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -8232,7 +8232,7 @@ describe("AcpAdapterV2", () => { |
| 8232 | 0, | 8232 | 0, |
| 8233 | "a cancel-backgrounded task completion must not wake a synthetic continuation run", | 8233 | "a cancel-backgrounded task completion must not wake a synthetic continuation run", |
| 8234 | ); | 8234 | ); |
| 8235 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 8235 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 8236 | ); | 8236 | ); |
| 8237 | 8237 | ||
| 8238 | it.live("direct Stop quarantine drops late background task mutations from the stopped run", () => | 8238 | it.live("direct Stop quarantine drops late background task mutations from the stopped run", () => |
| ... | @@ -8379,7 +8379,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -8379,7 +8379,7 @@ describe("AcpAdapterV2", () => { |
| 8379 | yield* hasPendingBackgroundWork, | 8379 | yield* hasPendingBackgroundWork, |
| 8380 | "direct Stop quarantine must drop residual background task mutations from the stopped run", | 8380 | "direct Stop quarantine must drop residual background task mutations from the stopped run", |
| 8381 | ); | 8381 | ); |
| 8382 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 8382 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 8383 | ); | 8383 | ); |
| 8384 | 8384 | ||
| 8385 | it.live("production Grok interrupt flags still hard-kill and respawn on user Stop", () => | 8385 | it.live("production Grok interrupt flags still hard-kill and respawn on user Stop", () => |
| ... | @@ -8505,7 +8505,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -8505,7 +8505,7 @@ describe("AcpAdapterV2", () => { |
| 8505 | 2, | 8505 | 2, |
| 8506 | "user Stop with production Grok flags must replace the ACP runtime process", | 8506 | "user Stop with production Grok flags must replace the ACP runtime process", |
| 8507 | ); | 8507 | ); |
| 8508 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 8508 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 8509 | ); | 8509 | ); |
| 8510 | 8510 | ||
| 8511 | // it.live: ownDetachedProcessGroup teardown uses wall-clock sleeps; under | 8511 | // it.live: ownDetachedProcessGroup teardown uses wall-clock sleeps; under |
| ... | @@ -8814,7 +8814,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -8814,7 +8814,7 @@ describe("AcpAdapterV2", () => { |
| 8814 | "Stop quarantine must drop turn-1 subagent carryover on the respawned runtime", | 8814 | "Stop quarantine must drop turn-1 subagent carryover on the respawned runtime", |
| 8815 | ); | 8815 | ); |
| 8816 | assert.equal(secondTerminalStatus, "completed"); | 8816 | assert.equal(secondTerminalStatus, "completed"); |
| 8817 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 8817 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 8818 | ); | 8818 | ); |
| 8819 | 8819 | ||
| 8820 | it.live("Direct Stop projects an interrupt-deferred terminal exactly once", () => | 8820 | it.live("Direct Stop projects an interrupt-deferred terminal exactly once", () => |
| ... | @@ -9009,7 +9009,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -9009,7 +9009,7 @@ describe("AcpAdapterV2", () => { |
| 9009 | } | 9009 | } |
| 9010 | assert.equal(completedAfterStop, 1); | 9010 | assert.equal(completedAfterStop, 1); |
| 9011 | assert.isFalse(yield* hasPendingBackgroundWork); | 9011 | assert.isFalse(yield* hasPendingBackgroundWork); |
| 9012 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 9012 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 9013 | ); | 9013 | ); |
| 9014 | 9014 | ||
| 9015 | it.effect( | 9015 | it.effect( |
| ... | @@ -9179,7 +9179,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -9179,7 +9179,7 @@ describe("AcpAdapterV2", () => { |
| 9179 | } | 9179 | } |
| 9180 | assert.equal(firstTerminalStatus, "interrupted"); | 9180 | assert.equal(firstTerminalStatus, "interrupted"); |
| 9181 | assert.equal(subagentPhase, "spawn"); | 9181 | assert.equal(subagentPhase, "spawn"); |
| 9182 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 9182 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 9183 | ); | 9183 | ); |
| 9184 | 9184 | ||
| 9185 | it.effect( | 9185 | it.effect( |
| ... | @@ -9310,7 +9310,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -9310,7 +9310,7 @@ describe("AcpAdapterV2", () => { |
| 9310 | yield* hasPendingBackgroundWork, | 9310 | yield* hasPendingBackgroundWork, |
| 9311 | "wake buffer must not stay non-empty and pin idle release after an already-handled re-report", | 9311 | "wake buffer must not stay non-empty and pin idle release after an already-handled re-report", |
| 9312 | ); | 9312 | ); |
| 9313 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 9313 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 9314 | ); | 9314 | ); |
| 9315 | 9315 | ||
| 9316 | it.effect( | 9316 | it.effect( |
| ... | @@ -9432,7 +9432,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -9432,7 +9432,7 @@ describe("AcpAdapterV2", () => { |
| 9432 | yield* first.clearIfCurrent!(); | 9432 | yield* first.clearIfCurrent!(); |
| 9433 | yield* lateTool("new-result-after-worker-drop"); | 9433 | yield* lateTool("new-result-after-worker-drop"); |
| 9434 | assert.lengthOf(continuationRequests, 2); | 9434 | assert.lengthOf(continuationRequests, 2); |
| 9435 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 9435 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 9436 | ); | 9436 | ); |
| 9437 | 9437 | ||
| 9438 | it.effect("keeps a buffered continuation current when a user turn starts before dispatch", () => | 9438 | it.effect("keeps a buffered continuation current when a user turn starts before dispatch", () => |
| ... | @@ -9639,7 +9639,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -9639,7 +9639,7 @@ describe("AcpAdapterV2", () => { |
| 9639 | assert.equal(continuationTerminalStatus, "completed"); | 9639 | assert.equal(continuationTerminalStatus, "completed"); |
| 9640 | assert.isTrue(bufferedTextSeen, "continuation must drain the wake buffer after the user run"); | 9640 | assert.isTrue(bufferedTextSeen, "continuation must drain the wake buffer after the user run"); |
| 9641 | assert.isFalse(yield* hasPendingBackgroundWork); | 9641 | assert.isFalse(yield* hasPendingBackgroundWork); |
| 9642 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 9642 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 9643 | ); | 9643 | ); |
| 9644 | 9644 | ||
| 9645 | it.effect( | 9645 | it.effect( |
| ... | @@ -9805,7 +9805,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -9805,7 +9805,7 @@ describe("AcpAdapterV2", () => { |
| 9805 | reportSeen, | 9805 | reportSeen, |
| 9806 | "the injected-turn report must project before the turn finalizes", | 9806 | "the injected-turn report must project before the turn finalizes", |
| 9807 | ); | 9807 | ); |
| 9808 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 9808 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 9809 | ); | 9809 | ); |
| 9810 | 9810 | ||
| 9811 | it.effect( | 9811 | it.effect( |
| ... | @@ -10007,7 +10007,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -10007,7 +10007,7 @@ describe("AcpAdapterV2", () => { |
| 10007 | 0, | 10007 | 0, |
| 10008 | "settle-without-report with mid-hold ext completion must not open a wake after the report streams", | 10008 | "settle-without-report with mid-hold ext completion must not open a wake after the report streams", |
| 10009 | ); | 10009 | ); |
| 10010 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 10010 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 10011 | ); | 10011 | ); |
| 10012 | 10012 | ||
| 10013 | it.effect( | 10013 | it.effect( |
| ... | @@ -10247,7 +10247,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -10247,7 +10247,7 @@ describe("AcpAdapterV2", () => { |
| 10247 | 0, | 10247 | 0, |
| 10248 | "pre-settle arm must be cleared when the injected report streams; no duplicate wake", | 10248 | "pre-settle arm must be cleared when the injected report streams; no duplicate wake", |
| 10249 | ); | 10249 | ); |
| 10250 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 10250 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 10251 | ); | 10251 | ); |
| 10252 | 10252 | ||
| 10253 | it.effect( | 10253 | it.effect( |
| ... | @@ -10466,7 +10466,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -10466,7 +10466,7 @@ describe("AcpAdapterV2", () => { |
| 10466 | 1, | 10466 | 1, |
| 10467 | "exactly one continuation when the last running task ends post-finalize with kept midTurn marks", | 10467 | "exactly one continuation when the last running task ends post-finalize with kept midTurn marks", |
| 10468 | ); | 10468 | ); |
| 10469 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 10469 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 10470 | ); | 10470 | ); |
| 10471 | 10471 | ||
| 10472 | it.effect( | 10472 | it.effect( |
| ... | @@ -10681,7 +10681,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -10681,7 +10681,7 @@ describe("AcpAdapterV2", () => { |
| 10681 | 0, | 10681 | 0, |
| 10682 | "interrupted turns must clear midTurn marks; B ending post-finalize must not offer", | 10682 | "interrupted turns must clear midTurn marks; B ending post-finalize must not offer", |
| 10683 | ); | 10683 | ); |
| 10684 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 10684 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 10685 | ); | 10685 | ); |
| 10686 | 10686 | ||
| 10687 | it.effect( | 10687 | it.effect( |
| ... | @@ -11055,7 +11055,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -11055,7 +11055,7 @@ describe("AcpAdapterV2", () => { |
| 11055 | yield* hasPendingBackgroundWork, | 11055 | yield* hasPendingBackgroundWork, |
| 11056 | "wake buffer must not retain turn-1 or turn-2 in-turn-handled ack residue", | 11056 | "wake buffer must not retain turn-1 or turn-2 in-turn-handled ack residue", |
| 11057 | ); | 11057 | ); |
| 11058 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 11058 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 11059 | ); | 11059 | ); |
| 11060 | 11060 | ||
| 11061 | it.effect("a wake names work that ended while the previous wake was queued", () => | 11061 | it.effect("a wake names work that ended while the previous wake was queued", () => |
| ... | @@ -11209,7 +11209,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -11209,7 +11209,7 @@ describe("AcpAdapterV2", () => { |
| 11209 | outcome: "completed", | 11209 | outcome: "completed", |
| 11210 | summary: 'Command "sleep b" finished', | 11210 | summary: 'Command "sleep b" finished', |
| 11211 | }); | 11211 | }); |
| 11212 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 11212 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 11213 | ); | 11213 | ); |
| 11214 | 11214 | ||
| 11215 | it.effect("mid-turn completed mutation defers offer until finalize only when unhandled", () => | 11215 | it.effect("mid-turn completed mutation defers offer until finalize only when unhandled", () => |
| ... | @@ -11538,7 +11538,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -11538,7 +11538,7 @@ describe("AcpAdapterV2", () => { |
| 11538 | 0, | 11538 | 0, |
| 11539 | "post-finalize must not offer when the mid-turn completion was handled in-turn", | 11539 | "post-finalize must not offer when the mid-turn completion was handled in-turn", |
| 11540 | ); | 11540 | ); |
| 11541 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 11541 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 11542 | ); | 11542 | ); |
| 11543 | 11543 | ||
| 11544 | it.effect( | 11544 | it.effect( |
| ... | @@ -11727,7 +11727,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -11727,7 +11727,7 @@ describe("AcpAdapterV2", () => { |
| 11727 | beforeWhitespace.messages, | 11727 | beforeWhitespace.messages, |
| 11728 | "whitespace-only late chunks must not mutate the loaded history snapshot", | 11728 | "whitespace-only late chunks must not mutate the loaded history snapshot", |
| 11729 | ); | 11729 | ); |
| 11730 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 11730 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 11731 | ); | 11731 | ); |
| 11732 | 11732 | ||
| 11733 | it.effect("empty-drain continuation turn waits the quiet window so late frames can attach", () => | 11733 | it.effect("empty-drain continuation turn waits the quiet window so late frames can attach", () => |
| ... | @@ -11971,7 +11971,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -11971,7 +11971,7 @@ describe("AcpAdapterV2", () => { |
| 11971 | } | 11971 | } |
| 11972 | } | 11972 | } |
| 11973 | assert.equal(continuationTerminal, "completed"); | 11973 | assert.equal(continuationTerminal, "completed"); |
| 11974 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 11974 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 11975 | ); | 11975 | ); |
| 11976 | 11976 | ||
| 11977 | it.effect("empty-drain continuation turn finalizes after the quiet window with no frames", () => | 11977 | it.effect("empty-drain continuation turn finalizes after the quiet window with no frames", () => |
| ... | @@ -12161,7 +12161,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -12161,7 +12161,7 @@ describe("AcpAdapterV2", () => { |
| 12161 | } | 12161 | } |
| 12162 | } | 12162 | } |
| 12163 | assert.equal(continuationTerminal, "completed"); | 12163 | assert.equal(continuationTerminal, "completed"); |
| 12164 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 12164 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 12165 | ); | 12165 | ); |
| 12166 | 12166 | ||
| 12167 | it.effect("restarts the ACP child process before the next prompt after interrupt", () => | 12167 | it.effect("restarts the ACP child process before the next prompt after interrupt", () => |
| ... | @@ -12258,7 +12258,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -12258,7 +12258,7 @@ describe("AcpAdapterV2", () => { |
| 12258 | Option.isSome(loadAfterRestart), | 12258 | Option.isSome(loadAfterRestart), |
| 12259 | "post-interrupt startTurn should respawn the runtime and replay session/resume", | 12259 | "post-interrupt startTurn should respawn the runtime and replay session/resume", |
| 12260 | ); | 12260 | ); |
| 12261 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 12261 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 12262 | ); | 12262 | ); |
| 12263 | 12263 | ||
| 12264 | it.effect("Windows teardown is one-shot explicitly with independent finalizer cleanup", () => | 12264 | it.effect("Windows teardown is one-shot explicitly with independent finalizer cleanup", () => |
| ... | @@ -12304,7 +12304,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -12304,7 +12304,7 @@ describe("AcpAdapterV2", () => { |
| 12304 | assert.equal(taskkillCommands.length, 1); | 12304 | assert.equal(taskkillCommands.length, 1); |
| 12305 | yield* Scope.close(runtimeScope, Exit.void); | 12305 | yield* Scope.close(runtimeScope, Exit.void); |
| 12306 | assert.equal(taskkillCommands.length, 1); | 12306 | assert.equal(taskkillCommands.length, 1); |
| 12307 | }).pipe(Effect.provide(testLayer)), | 12307 | }).pipe(Effect.provide(layerTest)), |
| 12308 | ); | 12308 | ); |
| 12309 | 12309 | ||
| 12310 | it("accepts only taskkill exit code zero as successful tree termination", () => { | 12310 | it("accepts only taskkill exit code zero as successful tree termination", () => { |
| ... | @@ -12407,7 +12407,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -12407,7 +12407,7 @@ describe("AcpAdapterV2", () => { |
| 12407 | ); | 12407 | ); |
| 12408 | const error = yield* Effect.flip(runtime.terminateProcessGroup!); | 12408 | const error = yield* Effect.flip(runtime.terminateProcessGroup!); |
| 12409 | assert.equal(error.detail, "mock taskkill failure"); | 12409 | assert.equal(error.detail, "mock taskkill failure"); |
| 12410 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 12410 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 12411 | ); | 12411 | ); |
| 12412 | 12412 | ||
| 12413 | it.live("poisons the session when hard teardown defects and blocks replacement work", () => | 12413 | it.live("poisons the session when hard teardown defects and blocks replacement work", () => |
| ... | @@ -12625,7 +12625,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -12625,7 +12625,7 @@ describe("AcpAdapterV2", () => { |
| 12625 | assert.isFalse(processExists(commandSleepPid!)); | 12625 | assert.isFalse(processExists(commandSleepPid!)); |
| 12626 | const finalizerMethods = yield* pollProtocolMethods(protocolEvents); | 12626 | const finalizerMethods = yield* pollProtocolMethods(protocolEvents); |
| 12627 | assert.notInclude(finalizerMethods, "session/close"); | 12627 | assert.notInclude(finalizerMethods, "session/close"); |
| 12628 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 12628 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 12629 | ); | 12629 | ); |
| 12630 | 12630 | ||
| 12631 | it.live("durably poisons start and resume when required hard teardown is unavailable", () => | 12631 | it.live("durably poisons start and resume when required hard teardown is unavailable", () => |
| ... | @@ -12714,7 +12714,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -12714,7 +12714,7 @@ describe("AcpAdapterV2", () => { |
| 12714 | .pipe(Effect.exit); | 12714 | .pipe(Effect.exit); |
| 12715 | if (Exit.isSuccess(resumeExit)) assert.fail("poisoned session must reject resumeThread"); | 12715 | if (Exit.isSuccess(resumeExit)) assert.fail("poisoned session must reject resumeThread"); |
| 12716 | assert.include(Cause.pretty(resumeExit.cause), "session is poisoned"); | 12716 | assert.include(Cause.pretty(resumeExit.cause), "session is poisoned"); |
| 12717 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 12717 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 12718 | ); | 12718 | ); |
| 12719 | 12719 | ||
| 12720 | it.live("holds concurrent startTurn behind successful hard teardown and reloads once", () => | 12720 | it.live("holds concurrent startTurn behind successful hard teardown and reloads once", () => |
| ... | @@ -12827,7 +12827,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -12827,7 +12827,7 @@ describe("AcpAdapterV2", () => { |
| 12827 | Stream.runHead, | 12827 | Stream.runHead, |
| 12828 | ); | 12828 | ); |
| 12829 | } | 12829 | } |
| 12830 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 12830 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 12831 | ); | 12831 | ); |
| 12832 | 12832 | ||
| 12833 | it.live("quarantines old-runtime callbacks after successful hard teardown", () => | 12833 | it.live("quarantines old-runtime callbacks after successful hard teardown", () => |
| ... | @@ -13189,7 +13189,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -13189,7 +13189,7 @@ describe("AcpAdapterV2", () => { |
| 13189 | }), | 13189 | }), |
| 13190 | requestRuntimeRestart: true, | 13190 | requestRuntimeRestart: true, |
| 13191 | }); | 13191 | }); |
| 13192 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 13192 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 13193 | ); | 13193 | ); |
| 13194 | 13194 | ||
| 13195 | it.live("keeps stale deferred cleanup inert while replacement requests remain live", () => | 13195 | it.live("keeps stale deferred cleanup inert while replacement requests remain live", () => |
| ... | @@ -13314,7 +13314,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -13314,7 +13314,7 @@ describe("AcpAdapterV2", () => { |
| 13314 | } | 13314 | } |
| 13315 | replacementTerminal = event.type === "turn.terminal"; | 13315 | replacementTerminal = event.type === "turn.terminal"; |
| 13316 | } | 13316 | } |
| 13317 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 13317 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 13318 | ); | 13318 | ); |
| 13319 | 13319 | ||
| 13320 | it.live("resolves owner cancellation and concurrent resume waiters after hard teardown", () => | 13320 | it.live("resolves owner cancellation and concurrent resume waiters after hard teardown", () => |
| ... | @@ -13447,7 +13447,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -13447,7 +13447,7 @@ describe("AcpAdapterV2", () => { |
| 13447 | const methodsAfterRestart = yield* pollProtocolMethods(protocolEvents); | 13447 | const methodsAfterRestart = yield* pollProtocolMethods(protocolEvents); |
| 13448 | assert.equal(methodsAfterRestart.filter((method) => method === "session/resume").length, 2); | 13448 | assert.equal(methodsAfterRestart.filter((method) => method === "session/resume").length, 2); |
| 13449 | assert.equal(methodsAfterRestart.filter((method) => method === "session/fork").length, 1); | 13449 | assert.equal(methodsAfterRestart.filter((method) => method === "session/fork").length, 1); |
| 13450 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 13450 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 13451 | ); | 13451 | ); |
| 13452 | 13452 | ||
| 13453 | it.live("direct Stop skips uninterruptible ACP cancel and recovers after native teardown", () => | 13453 | it.live("direct Stop skips uninterruptible ACP cancel and recovers after native teardown", () => |
| ... | @@ -13723,7 +13723,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -13723,7 +13723,7 @@ describe("AcpAdapterV2", () => { |
| 13723 | stoppedRunTextOnFollowUp, | 13723 | stoppedRunTextOnFollowUp, |
| 13724 | "stopped-run residual text must not attach to the follow-up run", | 13724 | "stopped-run residual text must not attach to the follow-up run", |
| 13725 | ); | 13725 | ); |
| 13726 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 13726 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 13727 | ); | 13727 | ); |
| 13728 | 13728 | ||
| 13729 | it.effect( | 13729 | it.effect( |
| ... | @@ -13878,7 +13878,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -13878,7 +13878,7 @@ describe("AcpAdapterV2", () => { |
| 13878 | carriedCompleted, | 13878 | carriedCompleted, |
| 13879 | "Direct Stop must not carry a stopped subagent into the follow-up run", | 13879 | "Direct Stop must not carry a stopped subagent into the follow-up run", |
| 13880 | ); | 13880 | ); |
| 13881 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 13881 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 13882 | ); | 13882 | ); |
| 13883 | 13883 | ||
| 13884 | it.live("restart_active terminates native work and reloads a clean runtime", () => | 13884 | it.live("restart_active terminates native work and reloads a clean runtime", () => |
| ... | @@ -14064,7 +14064,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -14064,7 +14064,7 @@ describe("AcpAdapterV2", () => { |
| 14064 | } | 14064 | } |
| 14065 | assert.equal(secondTerminal, "completed"); | 14065 | assert.equal(secondTerminal, "completed"); |
| 14066 | assert.isFalse(staleEventSeen, "interrupted runtime events must not attach to attempt 2"); | 14066 | assert.isFalse(staleEventSeen, "interrupted runtime events must not attach to attempt 2"); |
| 14067 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 14067 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 14068 | ); | 14068 | ); |
| 14069 | }); | 14069 | }); |
| 14070 | 14070 |
apps/server/src/orchestration-v2/Adapters/AcpRegistryAdapterV2.test.ts+9-9| ... | @@ -43,11 +43,11 @@ const decodeAcpRegistryAdapterSettings = Schema.decodeUnknownEffect( | ... | @@ -43,11 +43,11 @@ const decodeAcpRegistryAdapterSettings = Schema.decodeUnknownEffect( |
| 43 | AcpRegistryAdapterV2Driver.configSchema, | 43 | AcpRegistryAdapterV2Driver.configSchema, |
| 44 | ); | 44 | ); |
| 45 | 45 | ||
| 46 | const serverConfigLayer = ServerConfig.layerTest(process.cwd(), { | 46 | const layerServerConfig = ServerConfig.layerTest(process.cwd(), { |
| 47 | prefix: "t3-acp-registry-v2-adapter-", | 47 | prefix: "t3-acp-registry-v2-adapter-", |
| 48 | }).pipe(Layer.provide(NodeServices.layer)); | 48 | }).pipe(Layer.provide(NodeServices.layer)); |
| 49 | 49 | ||
| 50 | const registryLayer = Layer.succeed( | 50 | const layerRegistry = Layer.succeed( |
| 51 | HttpClient.HttpClient, | 51 | HttpClient.HttpClient, |
| 52 | HttpClient.make((request) => | 52 | HttpClient.make((request) => |
| 53 | Effect.succeed( | 53 | Effect.succeed( |
| ... | @@ -83,11 +83,11 @@ const registryLayer = Layer.succeed( | ... | @@ -83,11 +83,11 @@ const registryLayer = Layer.succeed( |
| 83 | ), | 83 | ), |
| 84 | ); | 84 | ); |
| 85 | 85 | ||
| 86 | const testLayer = Layer.mergeAll( | 86 | const layerTest = Layer.mergeAll( |
| 87 | NodeServices.layer, | 87 | NodeServices.layer, |
| 88 | IdAllocator.layer, | 88 | IdAllocator.layer, |
| 89 | serverConfigLayer, | 89 | layerServerConfig, |
| 90 | registryLayer, | 90 | layerRegistry, |
| 91 | ServerSettings.layerTest(), | 91 | ServerSettings.layerTest(), |
| 92 | ); | 92 | ); |
| 93 | 93 | ||
| ... | @@ -247,7 +247,7 @@ describe("AcpRegistryAdapterV2", () => { | ... | @@ -247,7 +247,7 @@ describe("AcpRegistryAdapterV2", () => { |
| 247 | answer("session/set_mode", {}), | 247 | answer("session/set_mode", {}), |
| 248 | ], | 248 | ], |
| 249 | storedModePick: "autoEdit", | 249 | storedModePick: "autoEdit", |
| 250 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 250 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 251 | ); | 251 | ); |
| 252 | 252 | ||
| 253 | it.effect("switches a mode config option under its own id", () => | 253 | it.effect("switches a mode config option under its own id", () => |
| ... | @@ -262,7 +262,7 @@ describe("AcpRegistryAdapterV2", () => { | ... | @@ -262,7 +262,7 @@ describe("AcpRegistryAdapterV2", () => { |
| 262 | answer("session/set_config_option", { configOptions: [permissionModeOption("auto")] }), | 262 | answer("session/set_config_option", { configOptions: [permissionModeOption("auto")] }), |
| 263 | ], | 263 | ], |
| 264 | storedModePick: "auto", | 264 | storedModePick: "auto", |
| 265 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 265 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 266 | ); | 266 | ); |
| 267 | }); | 267 | }); |
| 268 | 268 | ||
| ... | @@ -339,7 +339,7 @@ describe("AcpRegistryAdapterV2", () => { | ... | @@ -339,7 +339,7 @@ describe("AcpRegistryAdapterV2", () => { |
| 339 | fs: { readTextFile: false, writeTextFile: false }, | 339 | fs: { readTextFile: false, writeTextFile: false }, |
| 340 | terminal: false, | 340 | terminal: false, |
| 341 | }); | 341 | }); |
| 342 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 342 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 343 | ); | 343 | ); |
| 344 | 344 | ||
| 345 | it.effect("opens a real ACP child process resolved from registry configuration", () => | 345 | it.effect("opens a real ACP child process resolved from registry configuration", () => |
| ... | @@ -485,6 +485,6 @@ describe("AcpRegistryAdapterV2", () => { | ... | @@ -485,6 +485,6 @@ describe("AcpRegistryAdapterV2", () => { |
| 485 | name: "Auto", | 485 | name: "Auto", |
| 486 | description: null, | 486 | description: null, |
| 487 | }); | 487 | }); |
| 488 | }).pipe(Effect.provide(testLayer), Effect.scoped), | 488 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 489 | ); | 489 | ); |
| 490 | }); | 490 | }); |
apps/server/src/orchestration-v2/Adapters/AcpRegistryAdapterV2.testkit.ts+5-5| ... | @@ -33,16 +33,16 @@ const REPLAY_SETTINGS = Schema.decodeUnknownSync(AcpRegistrySettings)({ | ... | @@ -33,16 +33,16 @@ const REPLAY_SETTINGS = Schema.decodeUnknownSync(AcpRegistrySettings)({ |
| 33 | authMethodId: "replay", | 33 | authMethodId: "replay", |
| 34 | }); | 34 | }); |
| 35 | 35 | ||
| 36 | function makeAcpRegistryProviderAdapterRegistryReplayLayer( | 36 | function layerAcpRegistryProviderAdapterRegistryReplay( |
| 37 | transcript: AcpReplayTranscript, | 37 | transcript: AcpReplayTranscript, |
| 38 | options: { readonly replayGate?: ProviderReplayGate } = {}, | 38 | options: { readonly replayGate?: ProviderReplayGate } = {}, |
| 39 | ) { | 39 | ) { |
| 40 | const serverConfigLayer = Layer.effect( | 40 | const layerServerConfig = Layer.effect( |
| 41 | ServerConfig.ServerConfig, | 41 | ServerConfig.ServerConfig, |
| 42 | makeReplayServerConfig(`acp-registry-${transcript.scenario}`).pipe(Effect.orDie), | 42 | makeReplayServerConfig(`acp-registry-${transcript.scenario}`).pipe(Effect.orDie), |
| 43 | ).pipe(Layer.provide(NodeServices.layer)); | 43 | ).pipe(Layer.provide(NodeServices.layer)); |
| 44 | 44 | ||
| 45 | return ProviderAdapterRegistry.makeLayerEffect( | 45 | return ProviderAdapterRegistry.layerFromAdaptersEffect( |
| 46 | Effect.gen(function* () { | 46 | Effect.gen(function* () { |
| 47 | const fileSystem = yield* FileSystem.FileSystem; | 47 | const fileSystem = yield* FileSystem.FileSystem; |
| 48 | const path = yield* Path.Path; | 48 | const path = yield* Path.Path; |
| ... | @@ -86,7 +86,7 @@ function makeAcpRegistryProviderAdapterRegistryReplayLayer( | ... | @@ -86,7 +86,7 @@ function makeAcpRegistryProviderAdapterRegistryReplayLayer( |
| 86 | return [adapter]; | 86 | return [adapter]; |
| 87 | }), | 87 | }), |
| 88 | ).pipe( | 88 | ).pipe( |
| 89 | Layer.provide(Layer.mergeAll(serverConfigLayer, NodeServices.layer, IdAllocator.layer)), | 89 | Layer.provide(Layer.mergeAll(layerServerConfig, NodeServices.layer, IdAllocator.layer)), |
| 90 | // Held inbound lines must not outlive the scenario and wedge teardown. | 90 | // Held inbound lines must not outlive the scenario and wedge teardown. |
| 91 | Layer.merge( | 91 | Layer.merge( |
| 92 | Layer.effectDiscard( | 92 | Layer.effectDiscard( |
| ... | @@ -105,5 +105,5 @@ export const AcpRegistryOrchestratorReplayHarness: OrchestratorV2ProviderReplayH | ... | @@ -105,5 +105,5 @@ export const AcpRegistryOrchestratorReplayHarness: OrchestratorV2ProviderReplayH |
| 105 | decodeAcpReplayTranscript(transcript, ACP_REGISTRY_PROVIDER, { | 105 | decodeAcpReplayTranscript(transcript, ACP_REGISTRY_PROVIDER, { |
| 106 | retargetProvider: true, | 106 | retargetProvider: true, |
| 107 | }), | 107 | }), |
| 108 | makeProviderAdapterRegistryLayer: makeAcpRegistryProviderAdapterRegistryReplayLayer, | 108 | makeProviderAdapterRegistryLayer: layerAcpRegistryProviderAdapterRegistryReplay, |
| 109 | }; | 109 | }; |
apps/server/src/orchestration-v2/Adapters/AntigravityAdapterV2.test.ts+4-4| ... | @@ -126,7 +126,7 @@ describe("AntigravityAdapterV2 flavor", () => { | ... | @@ -126,7 +126,7 @@ describe("AntigravityAdapterV2 flavor", () => { |
| 126 | }); | 126 | }); |
| 127 | }); | 127 | }); |
| 128 | 128 | ||
| 129 | const sessionLayer = Layer.mergeAll( | 129 | const layerSession = Layer.mergeAll( |
| 130 | NodeServices.layer, | 130 | NodeServices.layer, |
| 131 | IdAllocator.layer, | 131 | IdAllocator.layer, |
| 132 | ServerConfig.layerTest(process.cwd(), { prefix: "t3-antigravity-v2-adapter-" }).pipe( | 132 | ServerConfig.layerTest(process.cwd(), { prefix: "t3-antigravity-v2-adapter-" }).pipe( |
| ... | @@ -284,7 +284,7 @@ describe("AntigravityAdapterV2 client file system", () => { | ... | @@ -284,7 +284,7 @@ describe("AntigravityAdapterV2 client file system", () => { |
| 284 | context("fs/read_text_file"), | 284 | context("fs/read_text_file"), |
| 285 | ); | 285 | ); |
| 286 | assert.equal(viaInsideLink.content, "inside"); | 286 | assert.equal(viaInsideLink.content, "inside"); |
| 287 | }).pipe(Effect.provide(sessionLayer), Effect.scoped), | 287 | }).pipe(Effect.provide(layerSession), Effect.scoped), |
| 288 | ); | 288 | ); |
| 289 | }); | 289 | }); |
| 290 | 290 | ||
| ... | @@ -419,7 +419,7 @@ describe("AntigravityAdapterV2 workspace changes", () => { | ... | @@ -419,7 +419,7 @@ describe("AntigravityAdapterV2 workspace changes", () => { |
| 419 | context, | 419 | context, |
| 420 | ).pipe(Effect.exit); | 420 | ).pipe(Effect.exit); |
| 421 | assert.isTrue(Exit.isFailure(fromA), "the previous workspace is no longer readable"); | 421 | assert.isTrue(Exit.isFailure(fromA), "the previous workspace is no longer readable"); |
| 422 | }).pipe(Effect.provide(sessionLayer), Effect.scoped), | 422 | }).pipe(Effect.provide(layerSession), Effect.scoped), |
| 423 | ); | 423 | ); |
| 424 | }); | 424 | }); |
| 425 | 425 | ||
| ... | @@ -532,6 +532,6 @@ describe("AntigravityAdapterV2 client file system under restrictive policies", ( | ... | @@ -532,6 +532,6 @@ describe("AntigravityAdapterV2 client file system under restrictive policies", ( |
| 532 | assert.isTrue(Exit.isFailure(outsideWrite), name); | 532 | assert.isTrue(Exit.isFailure(outsideWrite), name); |
| 533 | assert.isFalse(yield* fileSystem.exists(path.join(outside, "x.ts")), name); | 533 | assert.isFalse(yield* fileSystem.exists(path.join(outside, "x.ts")), name); |
| 534 | } | 534 | } |
| 535 | }).pipe(Effect.provide(sessionLayer), Effect.scoped), | 535 | }).pipe(Effect.provide(layerSession), Effect.scoped), |
| 536 | ); | 536 | ); |
| 537 | }); | 537 | }); |
apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.testkit.ts+9-9| ... | @@ -936,7 +936,7 @@ function replayQueryRunnerService( | ... | @@ -936,7 +936,7 @@ function replayQueryRunnerService( |
| 936 | }); | 936 | }); |
| 937 | } | 937 | } |
| 938 | 938 | ||
| 939 | function makeClaudeAgentSdkReplayQueryRunnerLayer( | 939 | function layerClaudeAgentSdkReplayQueryRunner( |
| 940 | transcript: ClaudeAgentSdkReplayTranscript, | 940 | transcript: ClaudeAgentSdkReplayTranscript, |
| 941 | options: { readonly replayGate?: ProviderReplayGate } = {}, | 941 | options: { readonly replayGate?: ProviderReplayGate } = {}, |
| 942 | ): Layer.Layer<ClaudeAdapterV2.ClaudeAgentSdkQueryRunner> { | 942 | ): Layer.Layer<ClaudeAdapterV2.ClaudeAgentSdkQueryRunner> { |
| ... | @@ -946,7 +946,7 @@ function makeClaudeAgentSdkReplayQueryRunnerLayer( | ... | @@ -946,7 +946,7 @@ function makeClaudeAgentSdkReplayQueryRunnerLayer( |
| 946 | ); | 946 | ); |
| 947 | } | 947 | } |
| 948 | 948 | ||
| 949 | function makeClaudeAgentSdkReplayLayer( | 949 | function layerClaudeAgentSdkReplay( |
| 950 | transcript: ClaudeAgentSdkReplayTranscript, | 950 | transcript: ClaudeAgentSdkReplayTranscript, |
| 951 | options: { | 951 | options: { |
| 952 | readonly replayGate?: ProviderReplayGate; | 952 | readonly replayGate?: ProviderReplayGate; |
| ... | @@ -974,18 +974,18 @@ function makeClaudeAgentSdkReplayLayer( | ... | @@ -974,18 +974,18 @@ function makeClaudeAgentSdkReplayLayer( |
| 974 | ); | 974 | ); |
| 975 | } | 975 | } |
| 976 | 976 | ||
| 977 | function makeClaudeProviderAdapterRegistryReplayLayer( | 977 | function layerClaudeProviderAdapterRegistryReplay( |
| 978 | transcript: ClaudeAgentSdkReplayTranscript, | 978 | transcript: ClaudeAgentSdkReplayTranscript, |
| 979 | options: { | 979 | options: { |
| 980 | readonly replayGate?: ProviderReplayGate; | 980 | readonly replayGate?: ProviderReplayGate; |
| 981 | readonly queryRunner?: ClaudeQueryRunner; | 981 | readonly queryRunner?: ClaudeQueryRunner; |
| 982 | } = {}, | 982 | } = {}, |
| 983 | ) { | 983 | ) { |
| 984 | const serverConfigLayer = Layer.effect( | 984 | const layerServerConfig = Layer.effect( |
| 985 | ServerConfig.ServerConfig, | 985 | ServerConfig.ServerConfig, |
| 986 | makeReplayServerConfig(transcript.scenario).pipe(Effect.orDie), | 986 | makeReplayServerConfig(transcript.scenario).pipe(Effect.orDie), |
| 987 | ).pipe(Layer.provide(NodeServices.layer)); | 987 | ).pipe(Layer.provide(NodeServices.layer)); |
| 988 | return ProviderAdapterRegistry.makeDriverLayer({ | 988 | return ProviderAdapterRegistry.layerFromDrivers({ |
| 989 | drivers: [ClaudeAdapterV2.ClaudeAdapterV2Driver], | 989 | drivers: [ClaudeAdapterV2.ClaudeAdapterV2Driver], |
| 990 | configMap: { | 990 | configMap: { |
| 991 | [ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID]: { | 991 | [ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID]: { |
| ... | @@ -995,10 +995,10 @@ function makeClaudeProviderAdapterRegistryReplayLayer( | ... | @@ -995,10 +995,10 @@ function makeClaudeProviderAdapterRegistryReplayLayer( |
| 995 | }).pipe( | 995 | }).pipe( |
| 996 | Layer.provide( | 996 | Layer.provide( |
| 997 | Layer.mergeAll( | 997 | Layer.mergeAll( |
| 998 | makeClaudeAgentSdkReplayLayer(transcript, options), | 998 | layerClaudeAgentSdkReplay(transcript, options), |
| 999 | IdAllocator.layer, | 999 | IdAllocator.layer, |
| 1000 | NodeServices.layer, | 1000 | NodeServices.layer, |
| 1001 | serverConfigLayer, | 1001 | layerServerConfig, |
| 1002 | ), | 1002 | ), |
| 1003 | ), | 1003 | ), |
| 1004 | ); | 1004 | ); |
| ... | @@ -2824,7 +2824,7 @@ export const ClaudeOrchestratorReplayHarness: OrchestratorV2ProviderReplayHarnes | ... | @@ -2824,7 +2824,7 @@ export const ClaudeOrchestratorReplayHarness: OrchestratorV2ProviderReplayHarnes |
| 2824 | ), | 2824 | ), |
| 2825 | ), | 2825 | ), |
| 2826 | makeProviderAdapterRegistryLayer: (transcript, options) => | 2826 | makeProviderAdapterRegistryLayer: (transcript, options) => |
| 2827 | makeClaudeProviderAdapterRegistryReplayLayer(transcript, options), | 2827 | layerClaudeProviderAdapterRegistryReplay(transcript, options), |
| 2828 | }; | 2828 | }; |
| 2829 | 2829 | ||
| 2830 | /** | 2830 | /** |
| ... | @@ -2837,7 +2837,7 @@ export function makeClaudeRestartReplayHarness(transcript: ClaudeAgentSdkReplayT | ... | @@ -2837,7 +2837,7 @@ export function makeClaudeRestartReplayHarness(transcript: ClaudeAgentSdkReplayT |
| 2837 | harness: { | 2837 | harness: { |
| 2838 | ...ClaudeOrchestratorReplayHarness, | 2838 | ...ClaudeOrchestratorReplayHarness, |
| 2839 | makeProviderAdapterRegistryLayer: (replayed) => | 2839 | makeProviderAdapterRegistryLayer: (replayed) => |
| 2840 | makeClaudeProviderAdapterRegistryReplayLayer(replayed, { queryRunner }), | 2840 | layerClaudeProviderAdapterRegistryReplay(replayed, { queryRunner }), |
| 2841 | } satisfies typeof ClaudeOrchestratorReplayHarness, | 2841 | } satisfies typeof ClaudeOrchestratorReplayHarness, |
| 2842 | assertComplete: replayQueryRunnerService(transcript, queryRunner).assertComplete, | 2842 | assertComplete: replayQueryRunnerService(transcript, queryRunner).assertComplete, |
| 2843 | }; | 2843 | }; |
apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts+1-1| ... | @@ -593,7 +593,7 @@ export function makeClaudeAgentSdkProtocolLogger(input: { | ... | @@ -593,7 +593,7 @@ export function makeClaudeAgentSdkProtocolLogger(input: { |
| 593 | }; | 593 | }; |
| 594 | } | 594 | } |
| 595 | 595 | ||
| 596 | export const claudeAgentSdkQueryRunnerLiveLayer: Layer.Layer< | 596 | export const layerQueryRunner: Layer.Layer< |
| 597 | ClaudeAgentSdkQueryRunner, | 597 | ClaudeAgentSdkQueryRunner, |
| 598 | never, | 598 | never, |
| 599 | Crypto.Crypto | ProviderEventLoggers.ProviderEventLoggers | 599 | Crypto.Crypto | ProviderEventLoggers.ProviderEventLoggers |
apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.test.ts+9-12| ... | @@ -56,7 +56,7 @@ import * as ProviderEventLoggers from "../../provider/Layers/ProviderEventLogger | ... | @@ -56,7 +56,7 @@ import * as ProviderEventLoggers from "../../provider/Layers/ProviderEventLogger |
| 56 | import * as IdAllocator from "../IdAllocator.ts"; | 56 | import * as IdAllocator from "../IdAllocator.ts"; |
| 57 | import * as EffectWorker from "../EffectWorker.ts"; | 57 | import * as EffectWorker from "../EffectWorker.ts"; |
| 58 | import * as Orchestrator from "../Orchestrator.ts"; | 58 | import * as Orchestrator from "../Orchestrator.ts"; |
| 59 | import { makeOrchestratorV2ReplayLayerWithRegistry } from "../testkit/ProviderReplayHarness.ts"; | 59 | import * as ProviderReplayHarness from "../testkit/ProviderReplayHarness.ts"; |
| 60 | import { | 60 | import { |
| 61 | ProviderAdapterForkThreadError, | 61 | ProviderAdapterForkThreadError, |
| 62 | ProviderAdapterOpenSessionError, | 62 | ProviderAdapterOpenSessionError, |
| ... | @@ -67,11 +67,8 @@ import { | ... | @@ -67,11 +67,8 @@ import { |
| 67 | } from "../ProviderAdapter.ts"; | 67 | } from "../ProviderAdapter.ts"; |
| 68 | import type { ProviderContinuationRequest } from "../ProviderContinuationRequests.ts"; | 68 | import type { ProviderContinuationRequest } from "../ProviderContinuationRequests.ts"; |
| 69 | import * as CodexAdapterV2 from "./CodexAdapterV2.ts"; | 69 | import * as CodexAdapterV2 from "./CodexAdapterV2.ts"; |
| 70 | import { | 70 | import { makeReplayServerConfig, withCodexReplayChildMetadata } from "./CodexAdapterV2.testkit.ts"; |
| 71 | makeReplayServerConfig, | 71 | import * as CodexAdapterV2Testkit from "./CodexAdapterV2.testkit.ts"; |
| 72 | makeCodexProviderAdapterRegistryReplayLayer, | ||
| 73 | withCodexReplayChildMetadata, | ||
| 74 | } from "./CodexAdapterV2.testkit.ts"; | ||
| 75 | 72 | ||
| 76 | const encodeUnknownJson = Schema.encodeUnknownSync(Schema.fromJsonString(Schema.Unknown)); | 73 | const encodeUnknownJson = Schema.encodeUnknownSync(Schema.fromJsonString(Schema.Unknown)); |
| 77 | const replayTranscriptJson = Schema.fromJsonString(CodexReplay.CodexAppServerReplayTranscript); | 74 | const replayTranscriptJson = Schema.fromJsonString(CodexReplay.CodexAppServerReplayTranscript); |
| ... | @@ -720,7 +717,7 @@ describe("CodexAdapterV2 process spawning", () => { | ... | @@ -720,7 +717,7 @@ describe("CodexAdapterV2 process spawning", () => { |
| 720 | ); | 717 | ); |
| 721 | }); | 718 | }); |
| 722 | const factory = yield* CodexAdapterV2.CodexAppServerClientFactory.pipe( | 719 | const factory = yield* CodexAdapterV2.CodexAppServerClientFactory.pipe( |
| 723 | Effect.provide(CodexAdapterV2.codexAppServerClientFactoryFromSettingsLayer), | 720 | Effect.provide(CodexAdapterV2.layerAppServerClientFactory), |
| 724 | Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner), | 721 | Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner), |
| 725 | Effect.provideService( | 722 | Effect.provideService( |
| 726 | ProviderEventLoggers.ProviderEventLoggers, | 723 | ProviderEventLoggers.ProviderEventLoggers, |
| ... | @@ -775,7 +772,7 @@ describe("CodexAdapterV2 process spawning", () => { | ... | @@ -775,7 +772,7 @@ describe("CodexAdapterV2 process spawning", () => { |
| 775 | }).pipe( | 772 | }).pipe( |
| 776 | Effect.provide( | 773 | Effect.provide( |
| 777 | Layer.mergeAll( | 774 | Layer.mergeAll( |
| 778 | CodexAdapterV2.codexAppServerClientFactoryFromSettingsLayer, | 775 | CodexAdapterV2.layerAppServerClientFactory, |
| 779 | ServerConfig.layerTest(process.cwd(), { prefix: "t3-codex-binary-home-" }), | 776 | ServerConfig.layerTest(process.cwd(), { prefix: "t3-codex-binary-home-" }), |
| 780 | ), | 777 | ), |
| 781 | ), | 778 | ), |
| ... | @@ -4238,9 +4235,9 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -4238,9 +4235,9 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 4238 | ); | 4235 | ); |
| 4239 | }).pipe( | 4236 | }).pipe( |
| 4240 | Effect.provide( | 4237 | Effect.provide( |
| 4241 | makeOrchestratorV2ReplayLayerWithRegistry( | 4238 | ProviderReplayHarness.layerWithRegistry( |
| 4242 | { name: "codex-background-stop", runtimePolicyOverride: { cwd } }, | 4239 | { name: "codex-background-stop", runtimePolicyOverride: { cwd } }, |
| 4243 | makeCodexProviderAdapterRegistryReplayLayer({ | 4240 | CodexAdapterV2Testkit.layer({ |
| 4244 | transcript: localTranscript, | 4241 | transcript: localTranscript, |
| 4245 | driver: replayDriver, | 4242 | driver: replayDriver, |
| 4246 | }), | 4243 | }), |
| ... | @@ -4361,9 +4358,9 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -4361,9 +4358,9 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 4361 | ); | 4358 | ); |
| 4362 | }).pipe( | 4359 | }).pipe( |
| 4363 | Effect.provide( | 4360 | Effect.provide( |
| 4364 | makeOrchestratorV2ReplayLayerWithRegistry( | 4361 | ProviderReplayHarness.layerWithRegistry( |
| 4365 | { name: "codex-background-stop-untracked", runtimePolicyOverride: { cwd } }, | 4362 | { name: "codex-background-stop-untracked", runtimePolicyOverride: { cwd } }, |
| 4366 | makeCodexProviderAdapterRegistryReplayLayer({ transcript: localTranscript }), | 4363 | CodexAdapterV2Testkit.layer({ transcript: localTranscript }), |
| 4367 | { runEffectWorker: false }, | 4364 | { runEffectWorker: false }, |
| 4368 | ), | 4365 | ), |
| 4369 | ), | 4366 | ), |
apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.testkit.ts+10-12| ... | @@ -177,18 +177,18 @@ export function makeReplayServerConfig( | ... | @@ -177,18 +177,18 @@ export function makeReplayServerConfig( |
| 177 | }); | 177 | }); |
| 178 | } | 178 | } |
| 179 | 179 | ||
| 180 | export function makeCodexProviderAdapterRegistryReplayLayer(input: { | 180 | export function layer(input: { |
| 181 | readonly transcript: CodexReplay.CodexAppServerReplayTranscript; | 181 | readonly transcript: CodexReplay.CodexAppServerReplayTranscript; |
| 182 | readonly driver?: CodexReplay.CodexAppServerReplayDriver; | 182 | readonly driver?: CodexReplay.CodexAppServerReplayDriver; |
| 183 | }) { | 183 | }) { |
| 184 | const replayLayer = | 184 | const layerReplay = |
| 185 | input.driver === undefined | 185 | input.driver === undefined |
| 186 | ? CodexReplay.layerReplay(input.transcript) | 186 | ? CodexReplay.layerReplay(input.transcript) |
| 187 | : CodexReplay.layerReplayWithDriver(input.driver); | 187 | : CodexReplay.layerReplayWithDriver(input.driver); |
| 188 | const replayClientFactoryLayer = Layer.succeed(CodexAdapterV2.CodexAppServerClientFactory, { | 188 | const layerReplayClientFactory = Layer.succeed(CodexAdapterV2.CodexAppServerClientFactory, { |
| 189 | open: (openInput) => | 189 | open: (openInput) => |
| 190 | Effect.gen(function* () { | 190 | Effect.gen(function* () { |
| 191 | const context = yield* Layer.build(replayLayer).pipe( | 191 | const context = yield* Layer.build(layerReplay).pipe( |
| 192 | Effect.mapError( | 192 | Effect.mapError( |
| 193 | (cause) => | 193 | (cause) => |
| 194 | new ProviderAdapterOpenSessionError({ | 194 | new ProviderAdapterOpenSessionError({ |
| ... | @@ -204,11 +204,11 @@ export function makeCodexProviderAdapterRegistryReplayLayer(input: { | ... | @@ -204,11 +204,11 @@ export function makeCodexProviderAdapterRegistryReplayLayer(input: { |
| 204 | ); | 204 | ); |
| 205 | }), | 205 | }), |
| 206 | }); | 206 | }); |
| 207 | const serverConfigLayer = Layer.effect( | 207 | const layerServerConfig = Layer.effect( |
| 208 | ServerConfig.ServerConfig, | 208 | ServerConfig.ServerConfig, |
| 209 | makeReplayServerConfig(input.transcript.scenario).pipe(Effect.orDie), | 209 | makeReplayServerConfig(input.transcript.scenario).pipe(Effect.orDie), |
| 210 | ).pipe(Layer.provide(NodeServices.layer)); | 210 | ).pipe(Layer.provide(NodeServices.layer)); |
| 211 | const registryLayer = ProviderAdapterRegistry.makeDriverLayer({ | 211 | const layerRegistry = ProviderAdapterRegistry.layerFromDrivers({ |
| 212 | drivers: [CodexAdapterV2.CodexAdapterV2Driver], | 212 | drivers: [CodexAdapterV2.CodexAdapterV2Driver], |
| 213 | configMap: { | 213 | configMap: { |
| 214 | [CodexAdapterV2.CODEX_DEFAULT_INSTANCE_ID]: { | 214 | [CodexAdapterV2.CODEX_DEFAULT_INSTANCE_ID]: { |
| ... | @@ -218,15 +218,15 @@ export function makeCodexProviderAdapterRegistryReplayLayer(input: { | ... | @@ -218,15 +218,15 @@ export function makeCodexProviderAdapterRegistryReplayLayer(input: { |
| 218 | }).pipe( | 218 | }).pipe( |
| 219 | Layer.provide( | 219 | Layer.provide( |
| 220 | Layer.mergeAll( | 220 | Layer.mergeAll( |
| 221 | replayClientFactoryLayer, | 221 | layerReplayClientFactory, |
| 222 | serverConfigLayer, | 222 | layerServerConfig, |
| 223 | NodeServices.layer, | 223 | NodeServices.layer, |
| 224 | IdAllocator.layer, | 224 | IdAllocator.layer, |
| 225 | ), | 225 | ), |
| 226 | ), | 226 | ), |
| 227 | ); | 227 | ); |
| 228 | 228 | ||
| 229 | return registryLayer; | 229 | return layerRegistry; |
| 230 | } | 230 | } |
| 231 | 231 | ||
| 232 | const decodeCodexAppServerReplayTranscript = Schema.decodeUnknownEffect( | 232 | const decodeCodexAppServerReplayTranscript = Schema.decodeUnknownEffect( |
| ... | @@ -267,9 +267,7 @@ export const CodexOrchestratorReplayHarness: OrchestratorV2ProviderReplayHarness | ... | @@ -267,9 +267,7 @@ export const CodexOrchestratorReplayHarness: OrchestratorV2ProviderReplayHarness |
| 267 | Effect.promise((signal) => replayGate.beforeEmit(entry.label, signal)), | 267 | Effect.promise((signal) => replayGate.beforeEmit(entry.label, signal)), |
| 268 | }, | 268 | }, |
| 269 | ); | 269 | ); |
| 270 | return yield* Layer.build( | 270 | return yield* Layer.build(layer({ transcript, driver })); |
| 271 | makeCodexProviderAdapterRegistryReplayLayer({ transcript, driver }), | ||
| 272 | ); | ||
| 273 | }), | 271 | }), |
| 274 | ); | 272 | ); |
| 275 | }, | 273 | }, |
apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.ts+2-2| ... | @@ -1340,7 +1340,7 @@ export const makeCodexAppServerSpawnCommand = Effect.fn( | ... | @@ -1340,7 +1340,7 @@ export const makeCodexAppServerSpawnCommand = Effect.fn( |
| 1340 | }); | 1340 | }); |
| 1341 | }); | 1341 | }); |
| 1342 | 1342 | ||
| 1343 | const makeCodexAppServerClientFactoryCommandLayer = ( | 1343 | const layerCodexAppServerClientFactoryCommand = ( |
| 1344 | options: CodexClient.CodexAppServerClientOptions & { | 1344 | options: CodexClient.CodexAppServerClientOptions & { |
| 1345 | readonly command: string; | 1345 | readonly command: string; |
| 1346 | readonly args?: ReadonlyArray<string>; | 1346 | readonly args?: ReadonlyArray<string>; |
| ... | @@ -1456,7 +1456,7 @@ function isSensitiveCodexProtocolKey(key: string): boolean { | ... | @@ -1456,7 +1456,7 @@ function isSensitiveCodexProtocolKey(key: string): boolean { |
| 1456 | ); | 1456 | ); |
| 1457 | } | 1457 | } |
| 1458 | 1458 | ||
| 1459 | export const codexAppServerClientFactoryFromSettingsLayer: Layer.Layer< | 1459 | export const layerAppServerClientFactory: Layer.Layer< |
| 1460 | CodexAppServerClientFactory, | 1460 | CodexAppServerClientFactory, |
| 1461 | never, | 1461 | never, |
| 1462 | ChildProcessSpawner.ChildProcessSpawner | ProviderEventLoggers | 1462 | ChildProcessSpawner.ChildProcessSpawner | ProviderEventLoggers |
apps/server/src/orchestration-v2/Adapters/CursorAdapterV2.testkit.ts+9-10| ... | @@ -492,7 +492,7 @@ export function makeCursorAgentSdkReplayRunner( | ... | @@ -492,7 +492,7 @@ export function makeCursorAgentSdkReplayRunner( |
| 492 | }; | 492 | }; |
| 493 | } | 493 | } |
| 494 | 494 | ||
| 495 | function makeCursorAgentSdkReplayLayer( | 495 | function layerCursorAgentSdkReplay( |
| 496 | transcript: CursorAgentSdkReplayTranscript, | 496 | transcript: CursorAgentSdkReplayTranscript, |
| 497 | options?: { | 497 | options?: { |
| 498 | readonly runner?: CursorAgentSdk.CursorAgentSdkRunnerShape; | 498 | readonly runner?: CursorAgentSdk.CursorAgentSdkRunnerShape; |
| ... | @@ -598,20 +598,20 @@ function makeReplayServerConfig( | ... | @@ -598,20 +598,20 @@ function makeReplayServerConfig( |
| 598 | }); | 598 | }); |
| 599 | } | 599 | } |
| 600 | 600 | ||
| 601 | export function makeCursorProviderAdapterRegistryReplayLayer( | 601 | export function layer( |
| 602 | transcript: CursorAgentSdkReplayTranscript, | 602 | transcript: CursorAgentSdkReplayTranscript, |
| 603 | options?: { | 603 | options?: { |
| 604 | readonly runner?: CursorAgentSdk.CursorAgentSdkRunnerShape; | 604 | readonly runner?: CursorAgentSdk.CursorAgentSdkRunnerShape; |
| 605 | readonly assertCompleteOnFinalize?: boolean; | 605 | readonly assertCompleteOnFinalize?: boolean; |
| 606 | }, | 606 | }, |
| 607 | ) { | 607 | ) { |
| 608 | const serverConfigLayer = Layer.effect( | 608 | const layerServerConfig = Layer.effect( |
| 609 | ServerConfig.ServerConfig, | 609 | ServerConfig.ServerConfig, |
| 610 | makeReplayServerConfig(transcript.scenario).pipe(Effect.orDie), | 610 | makeReplayServerConfig(transcript.scenario).pipe(Effect.orDie), |
| 611 | ).pipe(Layer.provide(NodeServices.layer)); | 611 | ).pipe(Layer.provide(NodeServices.layer)); |
| 612 | // Skill discovery also scans user roots under HOME; an empty HOME keeps | 612 | // Skill discovery also scans user roots under HOME; an empty HOME keeps |
| 613 | // replays from picking up the host's own skills. | 613 | // replays from picking up the host's own skills. |
| 614 | const hostEnvironmentLayer = Layer.effect( | 614 | const layerHostEnvironment = Layer.effect( |
| 615 | HostProcessEnvironment, | 615 | HostProcessEnvironment, |
| 616 | Effect.gen(function* () { | 616 | Effect.gen(function* () { |
| 617 | const fs = yield* FileSystem.FileSystem; | 617 | const fs = yield* FileSystem.FileSystem; |
| ... | @@ -619,7 +619,7 @@ export function makeCursorProviderAdapterRegistryReplayLayer( | ... | @@ -619,7 +619,7 @@ export function makeCursorProviderAdapterRegistryReplayLayer( |
| 619 | return { HOME: home }; | 619 | return { HOME: home }; |
| 620 | }).pipe(Effect.orDie), | 620 | }).pipe(Effect.orDie), |
| 621 | ).pipe(Layer.provide(NodeServices.layer)); | 621 | ).pipe(Layer.provide(NodeServices.layer)); |
| 622 | return ProviderAdapterRegistry.makeDriverLayer({ | 622 | return ProviderAdapterRegistry.layerFromDrivers({ |
| 623 | drivers: [CursorAdapterV2Driver], | 623 | drivers: [CursorAdapterV2Driver], |
| 624 | configMap: { | 624 | configMap: { |
| 625 | [CURSOR_DEFAULT_INSTANCE_ID]: { | 625 | [CURSOR_DEFAULT_INSTANCE_ID]: { |
| ... | @@ -629,9 +629,9 @@ export function makeCursorProviderAdapterRegistryReplayLayer( | ... | @@ -629,9 +629,9 @@ export function makeCursorProviderAdapterRegistryReplayLayer( |
| 629 | }).pipe( | 629 | }).pipe( |
| 630 | Layer.provide( | 630 | Layer.provide( |
| 631 | Layer.mergeAll( | 631 | Layer.mergeAll( |
| 632 | makeCursorAgentSdkReplayLayer(transcript, options), | 632 | layerCursorAgentSdkReplay(transcript, options), |
| 633 | serverConfigLayer, | 633 | layerServerConfig, |
| 634 | hostEnvironmentLayer, | 634 | layerHostEnvironment, |
| 635 | NodeServices.layer, | 635 | NodeServices.layer, |
| 636 | IdAllocator.layer, | 636 | IdAllocator.layer, |
| 637 | ), | 637 | ), |
| ... | @@ -666,8 +666,7 @@ export const CursorOrchestratorReplayHarness: OrchestratorV2ProviderReplayHarnes | ... | @@ -666,8 +666,7 @@ export const CursorOrchestratorReplayHarness: OrchestratorV2ProviderReplayHarnes |
| 666 | }), | 666 | }), |
| 667 | ), | 667 | ), |
| 668 | ), | 668 | ), |
| 669 | makeProviderAdapterRegistryLayer: (transcript) => | 669 | makeProviderAdapterRegistryLayer: (transcript) => layer(transcript), |
| 670 | makeCursorProviderAdapterRegistryReplayLayer(transcript), | ||
| 671 | }; | 670 | }; |
| 672 | 671 | ||
| 673 | function sanitizeReplayText( | 672 | function sanitizeReplayText( |
apps/server/src/orchestration-v2/Adapters/CursorAgentSdk.test.ts+6-6| ... | @@ -104,7 +104,7 @@ vi.mock("../../provider/cursorSdk.ts", () => ({ | ... | @@ -104,7 +104,7 @@ vi.mock("../../provider/cursorSdk.ts", () => ({ |
| 104 | }, | 104 | }, |
| 105 | })); | 105 | })); |
| 106 | 106 | ||
| 107 | const testLayer = CursorAgentSdk.cursorAgentSdkRunnerLiveLayer.pipe( | 107 | const layerTest = CursorAgentSdk.layer.pipe( |
| 108 | Layer.provide( | 108 | Layer.provide( |
| 109 | Layer.succeed(ProviderEventLoggers.ProviderEventLoggers, { | 109 | Layer.succeed(ProviderEventLoggers.ProviderEventLoggers, { |
| 110 | native: { | 110 | native: { |
| ... | @@ -147,7 +147,7 @@ describe("CursorAgentSdkRunner", () => { | ... | @@ -147,7 +147,7 @@ describe("CursorAgentSdkRunner", () => { |
| 147 | yield* run.wait; | 147 | yield* run.wait; |
| 148 | 148 | ||
| 149 | assert.deepStrictEqual(cursorSdkMock.runOperations, ["run.start"]); | 149 | assert.deepStrictEqual(cursorSdkMock.runOperations, ["run.start"]); |
| 150 | }).pipe(Effect.provide(testLayer)), | 150 | }).pipe(Effect.provide(layerTest)), |
| 151 | ); | 151 | ); |
| 152 | 152 | ||
| 153 | it.effect("retries a resumed send after cancelling its abandoned local run", () => | 153 | it.effect("retries a resumed send after cancelling its abandoned local run", () => |
| ... | @@ -188,7 +188,7 @@ describe("CursorAgentSdkRunner", () => { | ... | @@ -188,7 +188,7 @@ describe("CursorAgentSdkRunner", () => { |
| 188 | cwd: process.cwd(), | 188 | cwd: process.cwd(), |
| 189 | }, | 189 | }, |
| 190 | ]); | 190 | ]); |
| 191 | }).pipe(Effect.provide(testLayer)), | 191 | }).pipe(Effect.provide(layerTest)), |
| 192 | ); | 192 | ); |
| 193 | 193 | ||
| 194 | it.effect("logs agent.close before invoking the Cursor SDK", () => | 194 | it.effect("logs agent.close before invoking the Cursor SDK", () => |
| ... | @@ -221,7 +221,7 @@ describe("CursorAgentSdkRunner", () => { | ... | @@ -221,7 +221,7 @@ describe("CursorAgentSdkRunner", () => { |
| 221 | "sdk:agent.close", | 221 | "sdk:agent.close", |
| 222 | ]); | 222 | ]); |
| 223 | assert.equal(cursorSdkMock.agentClose.mock.calls.length, 1); | 223 | assert.equal(cursorSdkMock.agentClose.mock.calls.length, 1); |
| 224 | }).pipe(Effect.provide(testLayer)), | 224 | }).pipe(Effect.provide(layerTest)), |
| 225 | ); | 225 | ); |
| 226 | 226 | ||
| 227 | it.effect("logs agent.close even when the Cursor SDK close fails", () => | 227 | it.effect("logs agent.close even when the Cursor SDK close fails", () => |
| ... | @@ -259,7 +259,7 @@ describe("CursorAgentSdkRunner", () => { | ... | @@ -259,7 +259,7 @@ describe("CursorAgentSdkRunner", () => { |
| 259 | ]); | 259 | ]); |
| 260 | assert.equal(cursorSdkMock.agentClose.mock.calls.length, 1); | 260 | assert.equal(cursorSdkMock.agentClose.mock.calls.length, 1); |
| 261 | cursorSdkMock.setAgentCloseFailure(undefined); | 261 | cursorSdkMock.setAgentCloseFailure(undefined); |
| 262 | }).pipe(Effect.provide(testLayer)), | 262 | }).pipe(Effect.provide(layerTest)), |
| 263 | ); | 263 | ); |
| 264 | 264 | ||
| 265 | it.effect("surfaces interaction callback failures through run.wait", () => | 265 | it.effect("surfaces interaction callback failures through run.wait", () => |
| ... | @@ -299,6 +299,6 @@ describe("CursorAgentSdkRunner", () => { | ... | @@ -299,6 +299,6 @@ describe("CursorAgentSdkRunner", () => { |
| 299 | assert.equal(cursorSdkMock.create.mock.calls.length, 1); | 299 | assert.equal(cursorSdkMock.create.mock.calls.length, 1); |
| 300 | assert.equal(cursorSdkMock.send.mock.calls.length, 1); | 300 | assert.equal(cursorSdkMock.send.mock.calls.length, 1); |
| 301 | assert.equal(cursorSdkMock.runWait.mock.calls.length, 1); | 301 | assert.equal(cursorSdkMock.runWait.mock.calls.length, 1); |
| 302 | }).pipe(Effect.provide(testLayer)), | 302 | }).pipe(Effect.provide(layerTest)), |
| 303 | ); | 303 | ); |
| 304 | }); | 304 | }); |
apps/server/src/orchestration-v2/Adapters/CursorAgentSdk.ts+1-1| ... | @@ -593,7 +593,7 @@ export function makeCursorAgentSdkRunner( | ... | @@ -593,7 +593,7 @@ export function makeCursorAgentSdkRunner( |
| 593 | }); | 593 | }); |
| 594 | } | 594 | } |
| 595 | 595 | ||
| 596 | export const cursorAgentSdkRunnerLiveLayer: Layer.Layer< | 596 | export const layer: Layer.Layer< |
| 597 | CursorAgentSdkRunner, | 597 | CursorAgentSdkRunner, |
| 598 | never, | 598 | never, |
| 599 | ProviderEventLoggers.ProviderEventLoggers | 599 | ProviderEventLoggers.ProviderEventLoggers |
apps/server/src/orchestration-v2/Adapters/GrokAdapterV2.test.ts+3-3| ... | @@ -311,10 +311,10 @@ describe("Grok permission prompts", () => { | ... | @@ -311,10 +311,10 @@ describe("Grok permission prompts", () => { |
| 311 | }); | 311 | }); |
| 312 | 312 | ||
| 313 | describe("Grok launch permission mode", () => { | 313 | describe("Grok launch permission mode", () => { |
| 314 | const serverConfigLayer = ServerConfig.layerTest(process.cwd(), { | 314 | const layerServerConfig = ServerConfig.layerTest(process.cwd(), { |
| 315 | prefix: "t3-grok-v2-launch-", | 315 | prefix: "t3-grok-v2-launch-", |
| 316 | }).pipe(Layer.provide(NodeServices.layer)); | 316 | }).pipe(Layer.provide(NodeServices.layer)); |
| 317 | const testLayer = Layer.mergeAll(NodeServices.layer, IdAllocator.layer, serverConfigLayer); | 317 | const layerTest = Layer.mergeAll(NodeServices.layer, IdAllocator.layer, layerServerConfig); |
| 318 | 318 | ||
| 319 | // Opens a session through the adapter's own Grok runtime factory and returns | 319 | // Opens a session through the adapter's own Grok runtime factory and returns |
| 320 | // the argv it tried to launch. The spawn fails after recording, so no | 320 | // the argv it tried to launch. The spawn fails after recording, so no |
| ... | @@ -357,7 +357,7 @@ describe("Grok launch permission mode", () => { | ... | @@ -357,7 +357,7 @@ describe("Grok launch permission mode", () => { |
| 357 | }).pipe( | 357 | }).pipe( |
| 358 | // Keep the launch argv unwrapped by the Linux cgroup shim. | 358 | // Keep the launch argv unwrapped by the Linux cgroup shim. |
| 359 | Effect.provideService(HostProcessPlatform, "darwin"), | 359 | Effect.provideService(HostProcessPlatform, "darwin"), |
| 360 | Effect.provide(testLayer), | 360 | Effect.provide(layerTest), |
| 361 | ); | 361 | ); |
| 362 | 362 | ||
| 363 | const policy = ( | 363 | const policy = ( |
apps/server/src/orchestration-v2/Adapters/GrokAdapterV2.testkit.ts+5-5| ... | @@ -33,16 +33,16 @@ import { GROK_DEFAULT_INSTANCE_ID, GROK_PROVIDER, makeGrokAdapterV2 } from "./Gr | ... | @@ -33,16 +33,16 @@ import { GROK_DEFAULT_INSTANCE_ID, GROK_PROVIDER, makeGrokAdapterV2 } from "./Gr |
| 33 | 33 | ||
| 34 | const DEFAULT_GROK_SETTINGS = Schema.decodeUnknownSync(GrokSettings)({}); | 34 | const DEFAULT_GROK_SETTINGS = Schema.decodeUnknownSync(GrokSettings)({}); |
| 35 | 35 | ||
| 36 | function makeGrokProviderAdapterRegistryReplayLayer( | 36 | function layerGrokProviderAdapterRegistryReplay( |
| 37 | transcript: AcpReplayTranscript, | 37 | transcript: AcpReplayTranscript, |
| 38 | options: { readonly replayGate?: ProviderReplayGate } = {}, | 38 | options: { readonly replayGate?: ProviderReplayGate } = {}, |
| 39 | ) { | 39 | ) { |
| 40 | const serverConfigLayer = Layer.effect( | 40 | const layerServerConfig = Layer.effect( |
| 41 | ServerConfig.ServerConfig, | 41 | ServerConfig.ServerConfig, |
| 42 | makeReplayServerConfig(`grok-${transcript.scenario}`).pipe(Effect.orDie), | 42 | makeReplayServerConfig(`grok-${transcript.scenario}`).pipe(Effect.orDie), |
| 43 | ).pipe(Layer.provide(NodeServices.layer)); | 43 | ).pipe(Layer.provide(NodeServices.layer)); |
| 44 | 44 | ||
| 45 | return ProviderAdapterRegistry.makeLayerEffect( | 45 | return ProviderAdapterRegistry.layerFromAdaptersEffect( |
| 46 | Effect.gen(function* () { | 46 | Effect.gen(function* () { |
| 47 | const fileSystem = yield* FileSystem.FileSystem; | 47 | const fileSystem = yield* FileSystem.FileSystem; |
| 48 | const path = yield* Path.Path; | 48 | const path = yield* Path.Path; |
| ... | @@ -102,7 +102,7 @@ function makeGrokProviderAdapterRegistryReplayLayer( | ... | @@ -102,7 +102,7 @@ function makeGrokProviderAdapterRegistryReplayLayer( |
| 102 | return [adapter]; | 102 | return [adapter]; |
| 103 | }), | 103 | }), |
| 104 | ).pipe( | 104 | ).pipe( |
| 105 | Layer.provide(Layer.mergeAll(serverConfigLayer, NodeServices.layer, IdAllocator.layer)), | 105 | Layer.provide(Layer.mergeAll(layerServerConfig, NodeServices.layer, IdAllocator.layer)), |
| 106 | // Held inbound lines must not outlive the scenario and wedge teardown. | 106 | // Held inbound lines must not outlive the scenario and wedge teardown. |
| 107 | Layer.merge( | 107 | Layer.merge( |
| 108 | Layer.effectDiscard( | 108 | Layer.effectDiscard( |
| ... | @@ -118,5 +118,5 @@ export const GrokOrchestratorReplayHarness: OrchestratorV2ProviderReplayHarness< | ... | @@ -118,5 +118,5 @@ export const GrokOrchestratorReplayHarness: OrchestratorV2ProviderReplayHarness< |
| 118 | > = { | 118 | > = { |
| 119 | driver: GROK_PROVIDER, | 119 | driver: GROK_PROVIDER, |
| 120 | decodeTranscript: (transcript) => decodeAcpReplayTranscript(transcript, GROK_PROVIDER), | 120 | decodeTranscript: (transcript) => decodeAcpReplayTranscript(transcript, GROK_PROVIDER), |
| 121 | makeProviderAdapterRegistryLayer: makeGrokProviderAdapterRegistryReplayLayer, | 121 | makeProviderAdapterRegistryLayer: layerGrokProviderAdapterRegistryReplay, |
| 122 | }; | 122 | }; |
apps/server/src/orchestration-v2/Adapters/OpenCode2AdapterV2.testkit.ts+8-6| ... | @@ -270,20 +270,22 @@ const makeReplayAdapter = ( | ... | @@ -270,20 +270,22 @@ const makeReplayAdapter = ( |
| 270 | ); | 270 | ); |
| 271 | }); | 271 | }); |
| 272 | 272 | ||
| 273 | const replayServerConfig = (scenario: string) => | 273 | const layerReplayServerConfig = (scenario: string) => |
| 274 | Layer.effect(ServerConfig.ServerConfig, makeReplayServerConfig(scenario).pipe(Effect.orDie)).pipe( | 274 | Layer.effect(ServerConfig.ServerConfig, makeReplayServerConfig(scenario).pipe(Effect.orDie)).pipe( |
| 275 | Layer.provide(NodeServices.layer), | 275 | Layer.provide(NodeServices.layer), |
| 276 | ); | 276 | ); |
| 277 | 277 | ||
| 278 | function makeRegistryLayer( | 278 | function layerRegistry( |
| 279 | transcript: OpenCode2ReplayTranscript, | 279 | transcript: OpenCode2ReplayTranscript, |
| 280 | options?: { readonly replayGate?: ProviderReplayGate }, | 280 | options?: { readonly replayGate?: ProviderReplayGate }, |
| 281 | ) { | 281 | ) { |
| 282 | return Layer.unwrap( | 282 | return Layer.unwrap( |
| 283 | makeReplayAdapter(transcript, { external: true, ...options }).pipe( | 283 | makeReplayAdapter(transcript, { external: true, ...options }).pipe( |
| 284 | Effect.map((adapter) => ProviderAdapterRegistry.makeLayer([adapter])), | 284 | Effect.map((adapter) => ProviderAdapterRegistry.layerFromAdapters([adapter])), |
| 285 | ), | 285 | ), |
| 286 | ).pipe(Layer.provide(Layer.mergeAll(replayServerConfig(transcript.scenario), IdAllocator.layer))); | 286 | ).pipe( |
| 287 | Layer.provide(Layer.mergeAll(layerReplayServerConfig(transcript.scenario), IdAllocator.layer)), | ||
| 288 | ); | ||
| 287 | } | 289 | } |
| 288 | 290 | ||
| 289 | /** | 291 | /** |
| ... | @@ -320,7 +322,7 @@ export const openCode2ReplayRuntime = ( | ... | @@ -320,7 +322,7 @@ export const openCode2ReplayRuntime = ( |
| 320 | }, | 322 | }, |
| 321 | }); | 323 | }); |
| 322 | }).pipe( | 324 | }).pipe( |
| 323 | Effect.provide(Layer.mergeAll(replayServerConfig("opencode2_adapter"), IdAllocator.layer)), | 325 | Effect.provide(Layer.mergeAll(layerReplayServerConfig("opencode2_adapter"), IdAllocator.layer)), |
| 324 | ); | 326 | ); |
| 325 | 327 | ||
| 326 | export const OpenCode2OrchestratorReplayHarness: OrchestratorV2ProviderReplayHarness< | 328 | export const OpenCode2OrchestratorReplayHarness: OrchestratorV2ProviderReplayHarness< |
| ... | @@ -340,5 +342,5 @@ export const OpenCode2OrchestratorReplayHarness: OrchestratorV2ProviderReplayHar | ... | @@ -340,5 +342,5 @@ export const OpenCode2OrchestratorReplayHarness: OrchestratorV2ProviderReplayHar |
| 340 | }), | 342 | }), |
| 341 | ), | 343 | ), |
| 342 | ), | 344 | ), |
| 343 | makeProviderAdapterRegistryLayer: makeRegistryLayer, | 345 | makeProviderAdapterRegistryLayer: layerRegistry, |
| 344 | }; | 346 | }; |
apps/server/src/orchestration-v2/Adapters/OpenCodeAdapterV2.testkit.ts+7-7| ... | @@ -389,7 +389,7 @@ function makeReplayClient(controller: OpenCodeReplayController): OpencodeClient | ... | @@ -389,7 +389,7 @@ function makeReplayClient(controller: OpenCodeReplayController): OpencodeClient |
| 389 | } as unknown as OpencodeClient; | 389 | } as unknown as OpencodeClient; |
| 390 | } | 390 | } |
| 391 | 391 | ||
| 392 | function makeOpenCodeReplayRuntimeLayer(transcript: OpenCodeSdkReplayTranscript) { | 392 | function layerOpenCodeReplayRuntime(transcript: OpenCodeSdkReplayTranscript) { |
| 393 | return Layer.effect( | 393 | return Layer.effect( |
| 394 | OpenCodeRuntime.OpenCodeRuntime, | 394 | OpenCodeRuntime.OpenCodeRuntime, |
| 395 | Effect.gen(function* () { | 395 | Effect.gen(function* () { |
| ... | @@ -456,12 +456,12 @@ function makeOpenCodeReplayRuntimeLayer(transcript: OpenCodeSdkReplayTranscript) | ... | @@ -456,12 +456,12 @@ function makeOpenCodeReplayRuntimeLayer(transcript: OpenCodeSdkReplayTranscript) |
| 456 | ); | 456 | ); |
| 457 | } | 457 | } |
| 458 | 458 | ||
| 459 | function makeOpenCodeProviderAdapterRegistryReplayLayer(transcript: OpenCodeSdkReplayTranscript) { | 459 | function layerOpenCodeProviderAdapterRegistryReplay(transcript: OpenCodeSdkReplayTranscript) { |
| 460 | const serverConfigLayer = Layer.effect( | 460 | const layerServerConfig = Layer.effect( |
| 461 | ServerConfig.ServerConfig, | 461 | ServerConfig.ServerConfig, |
| 462 | makeReplayServerConfig(transcript.scenario).pipe(Effect.orDie), | 462 | makeReplayServerConfig(transcript.scenario).pipe(Effect.orDie), |
| 463 | ).pipe(Layer.provide(NodeServices.layer)); | 463 | ).pipe(Layer.provide(NodeServices.layer)); |
| 464 | return ProviderAdapterRegistry.makeDriverLayer({ | 464 | return ProviderAdapterRegistry.layerFromDrivers({ |
| 465 | drivers: [OpenCodeAdapterV2Driver], | 465 | drivers: [OpenCodeAdapterV2Driver], |
| 466 | configMap: { | 466 | configMap: { |
| 467 | [OPENCODE_DEFAULT_INSTANCE_ID]: { | 467 | [OPENCODE_DEFAULT_INSTANCE_ID]: { |
| ... | @@ -472,8 +472,8 @@ function makeOpenCodeProviderAdapterRegistryReplayLayer(transcript: OpenCodeSdkR | ... | @@ -472,8 +472,8 @@ function makeOpenCodeProviderAdapterRegistryReplayLayer(transcript: OpenCodeSdkR |
| 472 | }).pipe( | 472 | }).pipe( |
| 473 | Layer.provide( | 473 | Layer.provide( |
| 474 | Layer.mergeAll( | 474 | Layer.mergeAll( |
| 475 | makeOpenCodeReplayRuntimeLayer(transcript), | 475 | layerOpenCodeReplayRuntime(transcript), |
| 476 | serverConfigLayer, | 476 | layerServerConfig, |
| 477 | NodeServices.layer, | 477 | NodeServices.layer, |
| 478 | IdAllocator.layer, | 478 | IdAllocator.layer, |
| 479 | Layer.succeed( | 479 | Layer.succeed( |
| ... | @@ -508,5 +508,5 @@ export const OpenCodeOrchestratorReplayHarness: OrchestratorV2ProviderReplayHarn | ... | @@ -508,5 +508,5 @@ export const OpenCodeOrchestratorReplayHarness: OrchestratorV2ProviderReplayHarn |
| 508 | }), | 508 | }), |
| 509 | ), | 509 | ), |
| 510 | ), | 510 | ), |
| 511 | makeProviderAdapterRegistryLayer: makeOpenCodeProviderAdapterRegistryReplayLayer, | 511 | makeProviderAdapterRegistryLayer: layerOpenCodeProviderAdapterRegistryReplay, |
| 512 | }; | 512 | }; |
apps/server/src/orchestration-v2/Adapters/PiAdapterV2.test.ts+46-46| ... | @@ -43,11 +43,11 @@ import { handoffBudget } from "../ContextHandoffBudget.ts"; | ... | @@ -43,11 +43,11 @@ import { handoffBudget } from "../ContextHandoffBudget.ts"; |
| 43 | import { makePiAdapterV2, PI_PROVIDER } from "./PiAdapterV2.ts"; | 43 | import { makePiAdapterV2, PI_PROVIDER } from "./PiAdapterV2.ts"; |
| 44 | import { makePiRpcConnection, type PiRpcRecord } from "./PiRpc.ts"; | 44 | import { makePiRpcConnection, type PiRpcRecord } from "./PiRpc.ts"; |
| 45 | 45 | ||
| 46 | const serverConfigLayer = ServerConfig.layerTest(process.cwd(), { | 46 | const layerServerConfig = ServerConfig.layerTest(process.cwd(), { |
| 47 | prefix: "t3-pi-v2-adapter-", | 47 | prefix: "t3-pi-v2-adapter-", |
| 48 | }).pipe(Layer.provide(NodeServices.layer)); | 48 | }).pipe(Layer.provide(NodeServices.layer)); |
| 49 | 49 | ||
| 50 | const testLayer = Layer.mergeAll(NodeServices.layer, IdAllocator.layer, serverConfigLayer); | 50 | const layerTest = Layer.mergeAll(NodeServices.layer, IdAllocator.layer, layerServerConfig); |
| 51 | 51 | ||
| 52 | const decodeJsonLine = Schema.decodeSync(Schema.fromJsonString(Schema.Unknown)); | 52 | const decodeJsonLine = Schema.decodeSync(Schema.fromJsonString(Schema.Unknown)); |
| 53 | const encodeJsonLine = Schema.encodeSync(Schema.fromJsonString(Schema.Unknown)); | 53 | const encodeJsonLine = Schema.encodeSync(Schema.fromJsonString(Schema.Unknown)); |
| ... | @@ -458,7 +458,7 @@ const expectModelFailure = (errorMessage: string) => | ... | @@ -458,7 +458,7 @@ const expectModelFailure = (errorMessage: string) => |
| 458 | terminal.status === "failed" && | 458 | terminal.status === "failed" && |
| 459 | terminal.failure.message === errorMessage, | 459 | terminal.failure.message === errorMessage, |
| 460 | ); | 460 | ); |
| 461 | }).pipe(Effect.scoped, Effect.provide(testLayer)); | 461 | }).pipe(Effect.scoped, Effect.provide(layerTest)); |
| 462 | 462 | ||
| 463 | describe("PiAdapterV2", () => { | 463 | describe("PiAdapterV2", () => { |
| 464 | it.effect("stops provider-initiated work that has no T3 turn owner", () => | 464 | it.effect("stops provider-initiated work that has no T3 turn owner", () => |
| ... | @@ -481,7 +481,7 @@ describe("PiAdapterV2", () => { | ... | @@ -481,7 +481,7 @@ describe("PiAdapterV2", () => { |
| 481 | sessionError.type === "provider_session.updated" && | 481 | sessionError.type === "provider_session.updated" && |
| 482 | sessionError.providerSession.lastError?.includes("invisible tool execution") === true, | 482 | sessionError.providerSession.lastError?.includes("invisible tool execution") === true, |
| 483 | ); | 483 | ); |
| 484 | }).pipe(Effect.scoped, Effect.provide(testLayer)), | 484 | }).pipe(Effect.scoped, Effect.provide(layerTest)), |
| 485 | ); | 485 | ); |
| 486 | 486 | ||
| 487 | it.effect("injects the T3 MCP extension and bearer when a session exists", () => | 487 | it.effect("injects the T3 MCP extension and bearer when a session exists", () => |
| ... | @@ -510,7 +510,7 @@ describe("PiAdapterV2", () => { | ... | @@ -510,7 +510,7 @@ describe("PiAdapterV2", () => { |
| 510 | }).pipe( | 510 | }).pipe( |
| 511 | Effect.ensuring(Effect.sync(() => McpProviderSession.clearMcpProviderSession(THREAD_ID))), | 511 | Effect.ensuring(Effect.sync(() => McpProviderSession.clearMcpProviderSession(THREAD_ID))), |
| 512 | Effect.scoped, | 512 | Effect.scoped, |
| 513 | Effect.provide(testLayer), | 513 | Effect.provide(layerTest), |
| 514 | ), | 514 | ), |
| 515 | ); | 515 | ); |
| 516 | 516 | ||
| ... | @@ -528,7 +528,7 @@ describe("PiAdapterV2", () => { | ... | @@ -528,7 +528,7 @@ describe("PiAdapterV2", () => { |
| 528 | const error = yield* runtime.resumeThread({ providerThread }).pipe(Effect.flip); | 528 | const error = yield* runtime.resumeThread({ providerThread }).pipe(Effect.flip); |
| 529 | assert.equal(error._tag, "ProviderAdapterResumeThreadError"); | 529 | assert.equal(error._tag, "ProviderAdapterResumeThreadError"); |
| 530 | assert.match(String(error.cause), /while a turn is active/); | 530 | assert.match(String(error.cause), /while a turn is active/); |
| 531 | }).pipe(Effect.scoped, Effect.provide(testLayer)), | 531 | }).pipe(Effect.scoped, Effect.provide(layerTest)), |
| 532 | ); | 532 | ); |
| 533 | 533 | ||
| 534 | it.effect("waits for a slow Pi resume without starting a replacement", () => | 534 | it.effect("waits for a slow Pi resume without starting a replacement", () => |
| ... | @@ -555,7 +555,7 @@ describe("PiAdapterV2", () => { | ... | @@ -555,7 +555,7 @@ describe("PiAdapterV2", () => { |
| 555 | assert.isFalse(fake.allRequests().some((request) => request.type === "new_session")); | 555 | assert.isFalse(fake.allRequests().some((request) => request.type === "new_session")); |
| 556 | yield* startTurn(runtime, providerThread, "default"); | 556 | yield* startTurn(runtime, providerThread, "default"); |
| 557 | yield* fake.takeRequest("prompt"); | 557 | yield* fake.takeRequest("prompt"); |
| 558 | }).pipe(Effect.scoped, Effect.provide(testLayer)), | 558 | }).pipe(Effect.scoped, Effect.provide(layerTest)), |
| 559 | ); | 559 | ); |
| 560 | 560 | ||
| 561 | it.effect("creates a distinct native session after a failed resume", () => | 561 | it.effect("creates a distinct native session after a failed resume", () => |
| ... | @@ -586,7 +586,7 @@ describe("PiAdapterV2", () => { | ... | @@ -586,7 +586,7 @@ describe("PiAdapterV2", () => { |
| 586 | ); | 586 | ); |
| 587 | yield* startTurn(runtime, replacement, "default"); | 587 | yield* startTurn(runtime, replacement, "default"); |
| 588 | yield* fake.takeRequest("prompt"); | 588 | yield* fake.takeRequest("prompt"); |
| 589 | }).pipe(Effect.scoped, Effect.provide(testLayer)), | 589 | }).pipe(Effect.scoped, Effect.provide(layerTest)), |
| 590 | ); | 590 | ); |
| 591 | 591 | ||
| 592 | it.effect.each(["veto", "same identity"] as const)( | 592 | it.effect.each(["veto", "same identity"] as const)( |
| ... | @@ -617,7 +617,7 @@ describe("PiAdapterV2", () => { | ... | @@ -617,7 +617,7 @@ describe("PiAdapterV2", () => { |
| 617 | ); | 617 | ); |
| 618 | yield* startTurn(runtime, providerThread, "default").pipe(Effect.flip); | 618 | yield* startTurn(runtime, providerThread, "default").pipe(Effect.flip); |
| 619 | assert.isFalse(fake.allRequests().some((request) => request.type === "prompt")); | 619 | assert.isFalse(fake.allRequests().some((request) => request.type === "prompt")); |
| 620 | }).pipe(Effect.scoped, Effect.provide(testLayer)), | 620 | }).pipe(Effect.scoped, Effect.provide(layerTest)), |
| 621 | ); | 621 | ); |
| 622 | 622 | ||
| 623 | it.effect("retires a timed-out lifecycle process before a late switch can race replacement", () => | 623 | it.effect("retires a timed-out lifecycle process before a late switch can race replacement", () => |
| ... | @@ -661,7 +661,7 @@ describe("PiAdapterV2", () => { | ... | @@ -661,7 +661,7 @@ describe("PiAdapterV2", () => { |
| 661 | .allRequests() | 661 | .allRequests() |
| 662 | .some((request) => request.type === "new_session" || request.type === "prompt"), | 662 | .some((request) => request.type === "new_session" || request.type === "prompt"), |
| 663 | ); | 663 | ); |
| 664 | }).pipe(Effect.scoped, Effect.provide(testLayer)), | 664 | }).pipe(Effect.scoped, Effect.provide(layerTest)), |
| 665 | ); | 665 | ); |
| 666 | 666 | ||
| 667 | it.effect( | 667 | it.effect( |
| ... | @@ -707,7 +707,7 @@ describe("PiAdapterV2", () => { | ... | @@ -707,7 +707,7 @@ describe("PiAdapterV2", () => { |
| 707 | assert.equal(replacement.nativeThreadRef?.nativeId, "/fake/fresh-after-delay.jsonl"); | 707 | assert.equal(replacement.nativeThreadRef?.nativeId, "/fake/fresh-after-delay.jsonl"); |
| 708 | assert.isNull(replacement.contextUsage); | 708 | assert.isNull(replacement.contextUsage); |
| 709 | assert.isNull(replacement.nativeConversationHeadRef); | 709 | assert.isNull(replacement.nativeConversationHeadRef); |
| 710 | }).pipe(Effect.scoped, Effect.provide(testLayer)), | 710 | }).pipe(Effect.scoped, Effect.provide(layerTest)), |
| 711 | ); | 711 | ); |
| 712 | 712 | ||
| 713 | it.effect("replaces a native session when the first resume's state refresh fails", () => | 713 | it.effect("replaces a native session when the first resume's state refresh fails", () => |
| ... | @@ -736,7 +736,7 @@ describe("PiAdapterV2", () => { | ... | @@ -736,7 +736,7 @@ describe("PiAdapterV2", () => { |
| 736 | yield* startTurn(runtime, replacement, "default"); | 736 | yield* startTurn(runtime, replacement, "default"); |
| 737 | yield* fake.takeRequest("prompt"); | 737 | yield* fake.takeRequest("prompt"); |
| 738 | assert.isTrue(fake.allRequests().some((request) => request.type === "new_session")); | 738 | assert.isTrue(fake.allRequests().some((request) => request.type === "new_session")); |
| 739 | }).pipe(Effect.scoped, Effect.provide(testLayer)), | 739 | }).pipe(Effect.scoped, Effect.provide(layerTest)), |
| 740 | ); | 740 | ); |
| 741 | 741 | ||
| 742 | it.effect("retires an interrupted switch before accepting further requests", () => | 742 | it.effect("retires an interrupted switch before accepting further requests", () => |
| ... | @@ -765,7 +765,7 @@ describe("PiAdapterV2", () => { | ... | @@ -765,7 +765,7 @@ describe("PiAdapterV2", () => { |
| 765 | }) | 765 | }) |
| 766 | .pipe(Effect.flip); | 766 | .pipe(Effect.flip); |
| 767 | assert.isFalse(fake.allRequests().some((request) => request.type === "new_session")); | 767 | assert.isFalse(fake.allRequests().some((request) => request.type === "new_session")); |
| 768 | }).pipe(Effect.scoped, Effect.provide(testLayer)), | 768 | }).pipe(Effect.scoped, Effect.provide(layerTest)), |
| 769 | ); | 769 | ); |
| 770 | 770 | ||
| 771 | it.effect("budgets legacy native history with Pi's selected model capacity", () => | 771 | it.effect("budgets legacy native history with Pi's selected model capacity", () => |
| ... | @@ -814,7 +814,7 @@ describe("PiAdapterV2", () => { | ... | @@ -814,7 +814,7 @@ describe("PiAdapterV2", () => { |
| 814 | existingProviderThread: { ...providerThread, nativeThreadRef: null }, | 814 | existingProviderThread: { ...providerThread, nativeThreadRef: null }, |
| 815 | }); | 815 | }); |
| 816 | assert.equal(runtime.getModelContextWindow?.(modelSelection("default")), 32_000); | 816 | assert.equal(runtime.getModelContextWindow?.(modelSelection("default")), 32_000); |
| 817 | }).pipe(Effect.scoped, Effect.provide(testLayer)), | 817 | }).pipe(Effect.scoped, Effect.provide(layerTest)), |
| 818 | ); | 818 | ); |
| 819 | 819 | ||
| 820 | it.effect("adopts the run's provider thread identity instead of minting a second row", () => | 820 | it.effect("adopts the run's provider thread identity instead of minting a second row", () => |
| ... | @@ -856,7 +856,7 @@ describe("PiAdapterV2", () => { | ... | @@ -856,7 +856,7 @@ describe("PiAdapterV2", () => { |
| 856 | assert.isTrue( | 856 | assert.isTrue( |
| 857 | updated.type === "provider_thread.updated" && updated.providerThread.id === placeholder.id, | 857 | updated.type === "provider_thread.updated" && updated.providerThread.id === placeholder.id, |
| 858 | ); | 858 | ); |
| 859 | }).pipe(Effect.scoped, Effect.provide(testLayer)), | 859 | }).pipe(Effect.scoped, Effect.provide(layerTest)), |
| 860 | ); | 860 | ); |
| 861 | 861 | ||
| 862 | it.effect("resets applied thinking when returning to Pi default", () => | 862 | it.effect("resets applied thinking when returning to Pi default", () => |
| ... | @@ -909,7 +909,7 @@ describe("PiAdapterV2", () => { | ... | @@ -909,7 +909,7 @@ describe("PiAdapterV2", () => { |
| 909 | assert.equal(replayModel["modelId"], "grok-4.6"); | 909 | assert.equal(replayModel["modelId"], "grok-4.6"); |
| 910 | const resetLevel = yield* fake.takeRequest("set_thinking_level"); | 910 | const resetLevel = yield* fake.takeRequest("set_thinking_level"); |
| 911 | assert.equal(resetLevel["level"], "medium"); | 911 | assert.equal(resetLevel["level"], "medium"); |
| 912 | }).pipe(Effect.scoped, Effect.provide(testLayer)), | 912 | }).pipe(Effect.scoped, Effect.provide(layerTest)), |
| 913 | ); | 913 | ); |
| 914 | 914 | ||
| 915 | it.effect("expands a selected $ skill through Pi's native skill command", () => | 915 | it.effect("expands a selected $ skill through Pi's native skill command", () => |
| ... | @@ -944,7 +944,7 @@ describe("PiAdapterV2", () => { | ... | @@ -944,7 +944,7 @@ describe("PiAdapterV2", () => { |
| 944 | ); | 944 | ); |
| 945 | const prompt = yield* fake.takeRequest("prompt"); | 945 | const prompt = yield* fake.takeRequest("prompt"); |
| 946 | assert.equal(prompt["message"], "/skill:repo-review Review this change please"); | 946 | assert.equal(prompt["message"], "/skill:repo-review Review this change please"); |
| 947 | }).pipe(Effect.scoped, Effect.provide(testLayer)), | 947 | }).pipe(Effect.scoped, Effect.provide(layerTest)), |
| 948 | ); | 948 | ); |
| 949 | 949 | ||
| 950 | it.effect("expands every selected $ skill through Pi native skill commands", () => | 950 | it.effect("expands every selected $ skill through Pi native skill commands", () => |
| ... | @@ -980,7 +980,7 @@ describe("PiAdapterV2", () => { | ... | @@ -980,7 +980,7 @@ describe("PiAdapterV2", () => { |
| 980 | yield* startTurn(runtime, providerThread, "default", [], "use $repo-review and $deploy"); | 980 | yield* startTurn(runtime, providerThread, "default", [], "use $repo-review and $deploy"); |
| 981 | const prompt = yield* fake.takeRequest("prompt"); | 981 | const prompt = yield* fake.takeRequest("prompt"); |
| 982 | assert.equal(prompt["message"], "/skill:repo-review /skill:deploy use and"); | 982 | assert.equal(prompt["message"], "/skill:repo-review /skill:deploy use and"); |
| 983 | }).pipe(Effect.scoped, Effect.provide(testLayer)), | 983 | }).pipe(Effect.scoped, Effect.provide(layerTest)), |
| 984 | ); | 984 | ); |
| 985 | 985 | ||
| 986 | it.effect.each([ | 986 | it.effect.each([ |
| ... | @@ -1060,7 +1060,7 @@ describe("PiAdapterV2", () => { | ... | @@ -1060,7 +1060,7 @@ describe("PiAdapterV2", () => { |
| 1060 | fake.allRequests().findLast((request) => request.type === "switch_session")?.sessionPath, | 1060 | fake.allRequests().findLast((request) => request.type === "switch_session")?.sessionPath, |
| 1061 | forkFile, | 1061 | forkFile, |
| 1062 | ); | 1062 | ); |
| 1063 | }).pipe(Effect.scoped, Effect.provide(testLayer)), | 1063 | }).pipe(Effect.scoped, Effect.provide(layerTest)), |
| 1064 | ); | 1064 | ); |
| 1065 | 1065 | ||
| 1066 | it.effect("presents explicitly namespaced MCP extension tools", () => | 1066 | it.effect("presents explicitly namespaced MCP extension tools", () => |
| ... | @@ -1101,7 +1101,7 @@ describe("PiAdapterV2", () => { | ... | @@ -1101,7 +1101,7 @@ describe("PiAdapterV2", () => { |
| 1101 | }); | 1101 | }); |
| 1102 | assert.deepEqual(event.turnItem.input, { city: "Berlin" }); | 1102 | assert.deepEqual(event.turnItem.input, { city: "Berlin" }); |
| 1103 | } | 1103 | } |
| 1104 | }).pipe(Effect.scoped, Effect.provide(testLayer)), | 1104 | }).pipe(Effect.scoped, Effect.provide(layerTest)), |
| 1105 | ); | 1105 | ); |
| 1106 | 1106 | ||
| 1107 | it.effect("observes official subagent results without inventing child threads", () => | 1107 | it.effect("observes official subagent results without inventing child threads", () => |
| ... | @@ -1193,7 +1193,7 @@ describe("PiAdapterV2", () => { | ... | @@ -1193,7 +1193,7 @@ describe("PiAdapterV2", () => { |
| 1193 | subagentItem.turnItem.type === "subagent" && | 1193 | subagentItem.turnItem.type === "subagent" && |
| 1194 | subagentItem.turnItem.childThreadId === null, | 1194 | subagentItem.turnItem.childThreadId === null, |
| 1195 | ); | 1195 | ); |
| 1196 | }).pipe(Effect.scoped, Effect.provide(testLayer)), | 1196 | }).pipe(Effect.scoped, Effect.provide(layerTest)), |
| 1197 | ); | 1197 | ); |
| 1198 | 1198 | ||
| 1199 | it.effect("persists edit patches and write content on file change items", () => | 1199 | it.effect("persists edit patches and write content on file change items", () => |
| ... | @@ -1289,7 +1289,7 @@ describe("PiAdapterV2", () => { | ... | @@ -1289,7 +1289,7 @@ describe("PiAdapterV2", () => { |
| 1289 | failedEdit.turnItem.type === "file_change" && | 1289 | failedEdit.turnItem.type === "file_change" && |
| 1290 | failedEdit.turnItem.diffStr === "Could not find the text in c.ts.", | 1290 | failedEdit.turnItem.diffStr === "Could not find the text in c.ts.", |
| 1291 | ); | 1291 | ); |
| 1292 | }).pipe(Effect.scoped, Effect.provide(testLayer)), | 1292 | }).pipe(Effect.scoped, Effect.provide(layerTest)), |
| 1293 | ); | 1293 | ); |
| 1294 | 1294 | ||
| 1295 | it.effect("settles a command-only prompt from its deferred ack and idle probe", () => | 1295 | it.effect("settles a command-only prompt from its deferred ack and idle probe", () => |
| ... | @@ -1317,7 +1317,7 @@ describe("PiAdapterV2", () => { | ... | @@ -1317,7 +1317,7 @@ describe("PiAdapterV2", () => { |
| 1317 | // settles the turn as completed. | 1317 | // settles the turn as completed. |
| 1318 | const terminal = yield* takeEvent((event) => event.type === "turn.terminal"); | 1318 | const terminal = yield* takeEvent((event) => event.type === "turn.terminal"); |
| 1319 | assert.isTrue(terminal.type === "turn.terminal" && terminal.status === "completed"); | 1319 | assert.isTrue(terminal.type === "turn.terminal" && terminal.status === "completed"); |
| 1320 | }).pipe(Effect.scoped, Effect.provide(testLayer)), | 1320 | }).pipe(Effect.scoped, Effect.provide(layerTest)), |
| 1321 | ); | 1321 | ); |
| 1322 | 1322 | ||
| 1323 | it.effect("leaves /compacted as an ordinary prompt", () => | 1323 | it.effect("leaves /compacted as an ordinary prompt", () => |
| ... | @@ -1333,7 +1333,7 @@ describe("PiAdapterV2", () => { | ... | @@ -1333,7 +1333,7 @@ describe("PiAdapterV2", () => { |
| 1333 | const prompt = yield* fake.takeRequest("prompt"); | 1333 | const prompt = yield* fake.takeRequest("prompt"); |
| 1334 | assert.equal(prompt["message"], "/compacted please"); | 1334 | assert.equal(prompt["message"], "/compacted please"); |
| 1335 | assert.isFalse(fake.allRequests().some((request) => request["type"] === "compact")); | 1335 | assert.isFalse(fake.allRequests().some((request) => request["type"] === "compact")); |
| 1336 | }).pipe(Effect.scoped, Effect.provide(testLayer)), | 1336 | }).pipe(Effect.scoped, Effect.provide(layerTest)), |
| 1337 | ); | 1337 | ); |
| 1338 | 1338 | ||
| 1339 | it.effect("fails a compact that never started", () => | 1339 | it.effect("fails a compact that never started", () => |
| ... | @@ -1359,7 +1359,7 @@ describe("PiAdapterV2", () => { | ... | @@ -1359,7 +1359,7 @@ describe("PiAdapterV2", () => { |
| 1359 | terminal.status === "failed" && | 1359 | terminal.status === "failed" && |
| 1360 | terminal.failure.message === "Nothing to compact (session too small)", | 1360 | terminal.failure.message === "Nothing to compact (session too small)", |
| 1361 | ); | 1361 | ); |
| 1362 | }).pipe(Effect.scoped, Effect.provide(testLayer)), | 1362 | }).pipe(Effect.scoped, Effect.provide(layerTest)), |
| 1363 | ); | 1363 | ); |
| 1364 | 1364 | ||
| 1365 | it.effect("restarts Pi when Stop interrupts a user compact", () => | 1365 | it.effect("restarts Pi when Stop interrupts a user compact", () => |
| ... | @@ -1389,7 +1389,7 @@ describe("PiAdapterV2", () => { | ... | @@ -1389,7 +1389,7 @@ describe("PiAdapterV2", () => { |
| 1389 | yield* fake.closeStdout; | 1389 | yield* fake.closeStdout; |
| 1390 | const terminal = yield* takeEvent((event) => event.type === "turn.terminal"); | 1390 | const terminal = yield* takeEvent((event) => event.type === "turn.terminal"); |
| 1391 | assert.isTrue(terminal.type === "turn.terminal" && terminal.status === "interrupted"); | 1391 | assert.isTrue(terminal.type === "turn.terminal" && terminal.status === "interrupted"); |
| 1392 | }).pipe(Effect.scoped, Effect.provide(testLayer)), | 1392 | }).pipe(Effect.scoped, Effect.provide(layerTest)), |
| 1393 | ); | 1393 | ); |
| 1394 | 1394 | ||
| 1395 | it.effect("steers /compact as RPC compact instead of a prompt", () => | 1395 | it.effect("steers /compact as RPC compact instead of a prompt", () => |
| ... | @@ -1449,7 +1449,7 @@ describe("PiAdapterV2", () => { | ... | @@ -1449,7 +1449,7 @@ describe("PiAdapterV2", () => { |
| 1449 | yield* fake.takeRequest("get_state"); | 1449 | yield* fake.takeRequest("get_state"); |
| 1450 | const terminal = yield* takeEvent((event) => event.type === "turn.terminal"); | 1450 | const terminal = yield* takeEvent((event) => event.type === "turn.terminal"); |
| 1451 | assert.isTrue(terminal.type === "turn.terminal" && terminal.status === "completed"); | 1451 | assert.isTrue(terminal.type === "turn.terminal" && terminal.status === "completed"); |
| 1452 | }).pipe(Effect.scoped, Effect.provide(testLayer)), | 1452 | }).pipe(Effect.scoped, Effect.provide(layerTest)), |
| 1453 | ); | 1453 | ); |
| 1454 | 1454 | ||
| 1455 | it.effect("persists current xAI capacity text for the thread error banner", () => | 1455 | it.effect("persists current xAI capacity text for the thread error banner", () => |
| ... | @@ -1489,7 +1489,7 @@ describe("PiAdapterV2", () => { | ... | @@ -1489,7 +1489,7 @@ describe("PiAdapterV2", () => { |
| 1489 | const uiResponse = yield* fake.takeRequest("extension_ui_response"); | 1489 | const uiResponse = yield* fake.takeRequest("extension_ui_response"); |
| 1490 | assert.equal(uiResponse["id"], "ui-trust"); | 1490 | assert.equal(uiResponse["id"], "ui-trust"); |
| 1491 | assert.equal(uiResponse["confirmed"], true); | 1491 | assert.equal(uiResponse["confirmed"], true); |
| 1492 | }).pipe(Effect.scoped, Effect.provide(testLayer)), | 1492 | }).pipe(Effect.scoped, Effect.provide(layerTest)), |
| 1493 | ); | 1493 | ); |
| 1494 | 1494 | ||
| 1495 | it.effect("remembers session approvals only for identical confirmation content", () => | 1495 | it.effect("remembers session approvals only for identical confirmation content", () => |
| ... | @@ -1545,7 +1545,7 @@ describe("PiAdapterV2", () => { | ... | @@ -1545,7 +1545,7 @@ describe("PiAdapterV2", () => { |
| 1545 | other.type === "runtime_request.updated" && | 1545 | other.type === "runtime_request.updated" && |
| 1546 | other.runtimeRequest.nativeRequestRef?.nativeId === "ui-other", | 1546 | other.runtimeRequest.nativeRequestRef?.nativeId === "ui-other", |
| 1547 | ); | 1547 | ); |
| 1548 | }).pipe(Effect.scoped, Effect.provide(testLayer)), | 1548 | }).pipe(Effect.scoped, Effect.provide(layerTest)), |
| 1549 | ); | 1549 | ); |
| 1550 | 1550 | ||
| 1551 | it.effect("offers an explicit empty value for extension input dialogs", () => | 1551 | it.effect("offers an explicit empty value for extension input dialogs", () => |
| ... | @@ -1580,7 +1580,7 @@ describe("PiAdapterV2", () => { | ... | @@ -1580,7 +1580,7 @@ describe("PiAdapterV2", () => { |
| 1580 | const response = yield* fake.takeRequest("extension_ui_response"); | 1580 | const response = yield* fake.takeRequest("extension_ui_response"); |
| 1581 | assert.equal(response["value"], ""); | 1581 | assert.equal(response["value"], ""); |
| 1582 | assert.isUndefined(response["cancelled"]); | 1582 | assert.isUndefined(response["cancelled"]); |
| 1583 | }).pipe(Effect.scoped, Effect.provide(testLayer)), | 1583 | }).pipe(Effect.scoped, Effect.provide(layerTest)), |
| 1584 | ); | 1584 | ); |
| 1585 | 1585 | ||
| 1586 | it.effect("raises bridge edit confirmations as file-change approvals", () => | 1586 | it.effect("raises bridge edit confirmations as file-change approvals", () => |
| ... | @@ -1611,7 +1611,7 @@ describe("PiAdapterV2", () => { | ... | @@ -1611,7 +1611,7 @@ describe("PiAdapterV2", () => { |
| 1611 | `${title} should be ${requestKind}`, | 1611 | `${title} should be ${requestKind}`, |
| 1612 | ); | 1612 | ); |
| 1613 | } | 1613 | } |
| 1614 | }).pipe(Effect.scoped, Effect.provide(testLayer)), | 1614 | }).pipe(Effect.scoped, Effect.provide(layerTest)), |
| 1615 | ); | 1615 | ); |
| 1616 | 1616 | ||
| 1617 | it.effect("reads a thread snapshot from pi's active branch", () => | 1617 | it.effect("reads a thread snapshot from pi's active branch", () => |
| ... | @@ -1644,7 +1644,7 @@ describe("PiAdapterV2", () => { | ... | @@ -1644,7 +1644,7 @@ describe("PiAdapterV2", () => { |
| 1644 | assert.equal(snapshot.messages[0]!.text, "hello pi"); | 1644 | assert.equal(snapshot.messages[0]!.text, "hello pi"); |
| 1645 | assert.equal(snapshot.messages[1]!.role, "assistant"); | 1645 | assert.equal(snapshot.messages[1]!.role, "assistant"); |
| 1646 | assert.equal(snapshot.messages[1]!.text, "hello back"); | 1646 | assert.equal(snapshot.messages[1]!.text, "hello back"); |
| 1647 | }).pipe(Effect.scoped, Effect.provide(testLayer)), | 1647 | }).pipe(Effect.scoped, Effect.provide(layerTest)), |
| 1648 | ); | 1648 | ); |
| 1649 | 1649 | ||
| 1650 | it.effect("keeps snapshot message identities distinct across native sessions", () => | 1650 | it.effect("keeps snapshot message identities distinct across native sessions", () => |
| ... | @@ -1678,7 +1678,7 @@ describe("PiAdapterV2", () => { | ... | @@ -1678,7 +1678,7 @@ describe("PiAdapterV2", () => { |
| 1678 | fake.queueMessages(messages); | 1678 | fake.queueMessages(messages); |
| 1679 | const b = yield* runtime.readThreadSnapshot({ providerThread: second }); | 1679 | const b = yield* runtime.readThreadSnapshot({ providerThread: second }); |
| 1680 | assert.notEqual(a.messages[0]!.id, b.messages[0]!.id); | 1680 | assert.notEqual(a.messages[0]!.id, b.messages[0]!.id); |
| 1681 | }).pipe(Effect.scoped, Effect.provide(testLayer)), | 1681 | }).pipe(Effect.scoped, Effect.provide(layerTest)), |
| 1682 | ); | 1682 | ); |
| 1683 | 1683 | ||
| 1684 | it.effect("rejects a nonpersistent session UUID instead of treating it as a resumable path", () => | 1684 | it.effect("rejects a nonpersistent session UUID instead of treating it as a resumable path", () => |
| ... | @@ -1695,7 +1695,7 @@ describe("PiAdapterV2", () => { | ... | @@ -1695,7 +1695,7 @@ describe("PiAdapterV2", () => { |
| 1695 | }) | 1695 | }) |
| 1696 | .pipe(Effect.result); | 1696 | .pipe(Effect.result); |
| 1697 | assert.equal(result._tag, "Failure"); | 1697 | assert.equal(result._tag, "Failure"); |
| 1698 | }).pipe(Effect.scoped, Effect.provide(testLayer)), | 1698 | }).pipe(Effect.scoped, Effect.provide(layerTest)), |
| 1699 | ); | 1699 | ); |
| 1700 | 1700 | ||
| 1701 | it.effect("keeps a settled turn's late prompt rejection off the next turn", () => | 1701 | it.effect("keeps a settled turn's late prompt rejection off the next turn", () => |
| ... | @@ -1731,7 +1731,7 @@ describe("PiAdapterV2", () => { | ... | @@ -1731,7 +1731,7 @@ describe("PiAdapterV2", () => { |
| 1731 | assert.isTrue( | 1731 | assert.isTrue( |
| 1732 | secondTerminal.type === "turn.terminal" && secondTerminal.status === "completed", | 1732 | secondTerminal.type === "turn.terminal" && secondTerminal.status === "completed", |
| 1733 | ); | 1733 | ); |
| 1734 | }).pipe(Effect.scoped, Effect.provide(testLayer)), | 1734 | }).pipe(Effect.scoped, Effect.provide(layerTest)), |
| 1735 | ); | 1735 | ); |
| 1736 | 1736 | ||
| 1737 | it.effect("shows compaction progress and completes the same activity row", () => | 1737 | it.effect("shows compaction progress and completes the same activity row", () => |
| ... | @@ -1797,7 +1797,7 @@ describe("PiAdapterV2", () => { | ... | @@ -1797,7 +1797,7 @@ describe("PiAdapterV2", () => { |
| 1797 | yield* fake.emit({ type: "agent_settled" }); | 1797 | yield* fake.emit({ type: "agent_settled" }); |
| 1798 | const terminal = yield* takeEvent((event) => event.type === "turn.terminal"); | 1798 | const terminal = yield* takeEvent((event) => event.type === "turn.terminal"); |
| 1799 | assert.isTrue(terminal.type === "turn.terminal" && terminal.status === "completed"); | 1799 | assert.isTrue(terminal.type === "turn.terminal" && terminal.status === "completed"); |
| 1800 | }).pipe(Effect.scoped, Effect.provide(testLayer)), | 1800 | }).pipe(Effect.scoped, Effect.provide(layerTest)), |
| 1801 | ); | 1801 | ); |
| 1802 | 1802 | ||
| 1803 | it.effect("uses distinct compaction IDs for first turns in separate threads", () => | 1803 | it.effect("uses distinct compaction IDs for first turns in separate threads", () => |
| ... | @@ -1855,7 +1855,7 @@ describe("PiAdapterV2", () => { | ... | @@ -1855,7 +1855,7 @@ describe("PiAdapterV2", () => { |
| 1855 | first.turnItem.ordinal === second.turnItem.ordinal && | 1855 | first.turnItem.ordinal === second.turnItem.ordinal && |
| 1856 | first.turnItem.id !== second.turnItem.id, | 1856 | first.turnItem.id !== second.turnItem.id, |
| 1857 | ); | 1857 | ); |
| 1858 | }).pipe(Effect.scoped, Effect.provide(testLayer)), | 1858 | }).pipe(Effect.scoped, Effect.provide(layerTest)), |
| 1859 | ); | 1859 | ); |
| 1860 | 1860 | ||
| 1861 | it.effect("shows aborted compactions as stopped", () => | 1861 | it.effect("shows aborted compactions as stopped", () => |
| ... | @@ -1892,7 +1892,7 @@ describe("PiAdapterV2", () => { | ... | @@ -1892,7 +1892,7 @@ describe("PiAdapterV2", () => { |
| 1892 | stopped.turnItem.status === "cancelled" && | 1892 | stopped.turnItem.status === "cancelled" && |
| 1893 | stopped.turnItem.title === "Context compaction stopped", | 1893 | stopped.turnItem.title === "Context compaction stopped", |
| 1894 | ); | 1894 | ); |
| 1895 | }).pipe(Effect.scoped, Effect.provide(testLayer)), | 1895 | }).pipe(Effect.scoped, Effect.provide(layerTest)), |
| 1896 | ); | 1896 | ); |
| 1897 | 1897 | ||
| 1898 | it.effect("keeps the turn open and updates one retry row through final failure", () => | 1898 | it.effect("keeps the turn open and updates one retry row through final failure", () => |
| ... | @@ -1987,7 +1987,7 @@ describe("PiAdapterV2", () => { | ... | @@ -1987,7 +1987,7 @@ describe("PiAdapterV2", () => { |
| 1987 | terminal.retry.maxAttempts === 3 && | 1987 | terminal.retry.maxAttempts === 3 && |
| 1988 | terminal.retryStartedAt === firstRetry.turnItem.startedAt, | 1988 | terminal.retryStartedAt === firstRetry.turnItem.startedAt, |
| 1989 | ); | 1989 | ); |
| 1990 | }).pipe(Effect.scoped, Effect.provide(testLayer)), | 1990 | }).pipe(Effect.scoped, Effect.provide(layerTest)), |
| 1991 | ); | 1991 | ); |
| 1992 | 1992 | ||
| 1993 | it.effect("preserves exhausted retry failure through non-retrying compaction", () => | 1993 | it.effect("preserves exhausted retry failure through non-retrying compaction", () => |
| ... | @@ -2051,7 +2051,7 @@ describe("PiAdapterV2", () => { | ... | @@ -2051,7 +2051,7 @@ describe("PiAdapterV2", () => { |
| 2051 | terminal.retry?.attempt === 5 && | 2051 | terminal.retry?.attempt === 5 && |
| 2052 | terminal.retry.maxAttempts === 5, | 2052 | terminal.retry.maxAttempts === 5, |
| 2053 | ); | 2053 | ); |
| 2054 | }).pipe(Effect.scoped, Effect.provide(testLayer)), | 2054 | }).pipe(Effect.scoped, Effect.provide(layerTest)), |
| 2055 | ); | 2055 | ); |
| 2056 | 2056 | ||
| 2057 | it.effect("marks retry progress recovered when Pi succeeds", () => | 2057 | it.effect("marks retry progress recovered when Pi succeeds", () => |
| ... | @@ -2102,7 +2102,7 @@ describe("PiAdapterV2", () => { | ... | @@ -2102,7 +2102,7 @@ describe("PiAdapterV2", () => { |
| 2102 | yield* fake.emit({ type: "agent_settled" }); | 2102 | yield* fake.emit({ type: "agent_settled" }); |
| 2103 | const terminal = yield* takeEvent((event) => event.type === "turn.terminal"); | 2103 | const terminal = yield* takeEvent((event) => event.type === "turn.terminal"); |
| 2104 | assert.isTrue(terminal.type === "turn.terminal" && terminal.status === "completed"); | 2104 | assert.isTrue(terminal.type === "turn.terminal" && terminal.status === "completed"); |
| 2105 | }).pipe(Effect.scoped, Effect.provide(testLayer)), | 2105 | }).pipe(Effect.scoped, Effect.provide(layerTest)), |
| 2106 | ); | 2106 | ); |
| 2107 | 2107 | ||
| 2108 | it.effect("stops active retry progress when the turn is interrupted", () => | 2108 | it.effect("stops active retry progress when the turn is interrupted", () => |
| ... | @@ -2153,7 +2153,7 @@ describe("PiAdapterV2", () => { | ... | @@ -2153,7 +2153,7 @@ describe("PiAdapterV2", () => { |
| 2153 | ); | 2153 | ); |
| 2154 | const terminal = yield* takeEvent((event) => event.type === "turn.terminal"); | 2154 | const terminal = yield* takeEvent((event) => event.type === "turn.terminal"); |
| 2155 | assert.isTrue(terminal.type === "turn.terminal" && terminal.status === "interrupted"); | 2155 | assert.isTrue(terminal.type === "turn.terminal" && terminal.status === "interrupted"); |
| 2156 | }).pipe(Effect.scoped, Effect.provide(testLayer)), | 2156 | }).pipe(Effect.scoped, Effect.provide(layerTest)), |
| 2157 | ); | 2157 | ); |
| 2158 | 2158 | ||
| 2159 | it.effect("keeps extension-started compaction and recovery in the settled turn", () => | 2159 | it.effect("keeps extension-started compaction and recovery in the settled turn", () => |
| ... | @@ -2201,7 +2201,7 @@ describe("PiAdapterV2", () => { | ... | @@ -2201,7 +2201,7 @@ describe("PiAdapterV2", () => { |
| 2201 | yield* fake.takeRequest("get_state"); | 2201 | yield* fake.takeRequest("get_state"); |
| 2202 | const terminal = yield* takeEvent((event) => event.type === "turn.terminal"); | 2202 | const terminal = yield* takeEvent((event) => event.type === "turn.terminal"); |
| 2203 | assert.isTrue(terminal.type === "turn.terminal" && terminal.status === "completed"); | 2203 | assert.isTrue(terminal.type === "turn.terminal" && terminal.status === "completed"); |
| 2204 | }).pipe(Effect.scoped, Effect.provide(testLayer)), | 2204 | }).pipe(Effect.scoped, Effect.provide(layerTest)), |
| 2205 | ); | 2205 | ); |
| 2206 | 2206 | ||
| 2207 | it.effect("keeps working after a settle probe fails before detached compaction", () => | 2207 | it.effect("keeps working after a settle probe fails before detached compaction", () => |
| ... | @@ -2242,7 +2242,7 @@ describe("PiAdapterV2", () => { | ... | @@ -2242,7 +2242,7 @@ describe("PiAdapterV2", () => { |
| 2242 | yield* fake.takeRequest("get_state"); | 2242 | yield* fake.takeRequest("get_state"); |
| 2243 | const terminal = yield* takeEvent((event) => event.type === "turn.terminal"); | 2243 | const terminal = yield* takeEvent((event) => event.type === "turn.terminal"); |
| 2244 | assert.isTrue(terminal.type === "turn.terminal" && terminal.status === "completed"); | 2244 | assert.isTrue(terminal.type === "turn.terminal" && terminal.status === "completed"); |
| 2245 | }).pipe(Effect.scoped, Effect.provide(testLayer)), | 2245 | }).pipe(Effect.scoped, Effect.provide(layerTest)), |
| 2246 | ); | 2246 | ); |
| 2247 | 2247 | ||
| 2248 | it.effect("restarts Pi when Stop interrupts detached compaction", () => | 2248 | it.effect("restarts Pi when Stop interrupts detached compaction", () => |
| ... | @@ -2279,7 +2279,7 @@ describe("PiAdapterV2", () => { | ... | @@ -2279,7 +2279,7 @@ describe("PiAdapterV2", () => { |
| 2279 | yield* fake.closeStdout; | 2279 | yield* fake.closeStdout; |
| 2280 | const terminal = yield* takeEvent((event) => event.type === "turn.terminal"); | 2280 | const terminal = yield* takeEvent((event) => event.type === "turn.terminal"); |
| 2281 | assert.isTrue(terminal.type === "turn.terminal" && terminal.status === "interrupted"); | 2281 | assert.isTrue(terminal.type === "turn.terminal" && terminal.status === "interrupted"); |
| 2282 | }).pipe(Effect.scoped, Effect.provide(testLayer)), | 2282 | }).pipe(Effect.scoped, Effect.provide(layerTest)), |
| 2283 | ); | 2283 | ); |
| 2284 | 2284 | ||
| 2285 | it.effect("ignores an idle snapshot made stale by a steer", () => | 2285 | it.effect("ignores an idle snapshot made stale by a steer", () => |
| ... | @@ -2357,7 +2357,7 @@ describe("PiAdapterV2", () => { | ... | @@ -2357,7 +2357,7 @@ describe("PiAdapterV2", () => { |
| 2357 | yield* fake.takeRequest("get_state"); | 2357 | yield* fake.takeRequest("get_state"); |
| 2358 | const terminal = yield* takeEvent((event) => event.type === "turn.terminal"); | 2358 | const terminal = yield* takeEvent((event) => event.type === "turn.terminal"); |
| 2359 | assert.isTrue(terminal.type === "turn.terminal" && terminal.status === "completed"); | 2359 | assert.isTrue(terminal.type === "turn.terminal" && terminal.status === "completed"); |
| 2360 | }).pipe(Effect.scoped, Effect.provide(testLayer)), | 2360 | }).pipe(Effect.scoped, Effect.provide(layerTest)), |
| 2361 | ); | 2361 | ); |
| 2362 | }); | 2362 | }); |
| 2363 | 2363 |
apps/server/src/orchestration-v2/Adapters/PiAdapterV2.testkit.ts+5-5| ... | @@ -407,18 +407,18 @@ function makePiReplaySpawner( | ... | @@ -407,18 +407,18 @@ function makePiReplaySpawner( |
| 407 | * spawner that tees a live `pi`; replay passes the transcript-backed one. The | 407 | * spawner that tees a live `pi`; replay passes the transcript-backed one. The |
| 408 | * launch arguments are part of the recorded argv, so replay reuses them. | 408 | * launch arguments are part of the recorded argv, so replay reuses them. |
| 409 | */ | 409 | */ |
| 410 | export function makePiProviderAdapterRegistryLayer<E, R>(input: { | 410 | export function layer<E, R>(input: { |
| 411 | readonly scenario: string; | 411 | readonly scenario: string; |
| 412 | readonly spawner: Layer.Layer<ChildProcessSpawner.ChildProcessSpawner, E, R>; | 412 | readonly spawner: Layer.Layer<ChildProcessSpawner.ChildProcessSpawner, E, R>; |
| 413 | readonly binaryPath: string; | 413 | readonly binaryPath: string; |
| 414 | readonly launchArgs: string; | 414 | readonly launchArgs: string; |
| 415 | readonly environment?: ProviderInstanceEnvironment; | 415 | readonly environment?: ProviderInstanceEnvironment; |
| 416 | }) { | 416 | }) { |
| 417 | const serverConfigLayer = Layer.effect( | 417 | const layerServerConfig = Layer.effect( |
| 418 | ServerConfig.ServerConfig, | 418 | ServerConfig.ServerConfig, |
| 419 | makeReplayServerConfig(`pi-${input.scenario}`).pipe(Effect.orDie), | 419 | makeReplayServerConfig(`pi-${input.scenario}`).pipe(Effect.orDie), |
| 420 | ).pipe(Layer.provide(NodeServices.layer)); | 420 | ).pipe(Layer.provide(NodeServices.layer)); |
| 421 | return ProviderAdapterRegistry.makeDriverLayer({ | 421 | return ProviderAdapterRegistry.layerFromDrivers({ |
| 422 | drivers: [PiAdapterV2Driver], | 422 | drivers: [PiAdapterV2Driver], |
| 423 | configMap: { | 423 | configMap: { |
| 424 | [PI_PROVIDER]: { | 424 | [PI_PROVIDER]: { |
| ... | @@ -430,7 +430,7 @@ export function makePiProviderAdapterRegistryLayer<E, R>(input: { | ... | @@ -430,7 +430,7 @@ export function makePiProviderAdapterRegistryLayer<E, R>(input: { |
| 430 | }, | 430 | }, |
| 431 | }).pipe( | 431 | }).pipe( |
| 432 | Layer.provide(input.spawner), | 432 | Layer.provide(input.spawner), |
| 433 | Layer.provide(Layer.mergeAll(serverConfigLayer, NodeServices.layer, IdAllocator.layer)), | 433 | Layer.provide(Layer.mergeAll(layerServerConfig, NodeServices.layer, IdAllocator.layer)), |
| 434 | ); | 434 | ); |
| 435 | } | 435 | } |
| 436 | 436 | ||
| ... | @@ -457,7 +457,7 @@ export const PiOrchestratorReplayHarness: OrchestratorV2ProviderReplayHarness< | ... | @@ -457,7 +457,7 @@ export const PiOrchestratorReplayHarness: OrchestratorV2ProviderReplayHarness< |
| 457 | ), | 457 | ), |
| 458 | ), | 458 | ), |
| 459 | makeProviderAdapterRegistryLayer: (transcript) => | 459 | makeProviderAdapterRegistryLayer: (transcript) => |
| 460 | makePiProviderAdapterRegistryLayer({ | 460 | layer({ |
| 461 | scenario: transcript.scenario, | 461 | scenario: transcript.scenario, |
| 462 | binaryPath: "pi", | 462 | binaryPath: "pi", |
| 463 | launchArgs: metadataString(transcript, "launchArgs"), | 463 | launchArgs: metadataString(transcript, "launchArgs"), |
apps/server/src/orchestration-v2/AttachmentClaims.test.ts+11-11| ... | @@ -23,7 +23,7 @@ import { | ... | @@ -23,7 +23,7 @@ import { |
| 23 | releaseClaimedAttachments, | 23 | releaseClaimedAttachments, |
| 24 | } from "./AttachmentClaims.ts"; | 24 | } from "./AttachmentClaims.ts"; |
| 25 | 25 | ||
| 26 | const testLayer = Layer.mergeAll(NodeServices.layer).pipe( | 26 | const layerTest = Layer.mergeAll(NodeServices.layer).pipe( |
| 27 | Layer.provideMerge(ServerConfig.layerTest(process.cwd(), { prefix: "t3-attachment-claims-" })), | 27 | Layer.provideMerge(ServerConfig.layerTest(process.cwd(), { prefix: "t3-attachment-claims-" })), |
| 28 | Layer.provideMerge(NodeServices.layer), | 28 | Layer.provideMerge(NodeServices.layer), |
| 29 | ); | 29 | ); |
| ... | @@ -62,7 +62,7 @@ describe("AttachmentClaims", () => { | ... | @@ -62,7 +62,7 @@ describe("AttachmentClaims", () => { |
| 62 | const claimed = yield* claimPendingAttachments({ threadId: "thread-many", attachments }); | 62 | const claimed = yield* claimPendingAttachments({ threadId: "thread-many", attachments }); |
| 63 | expect(claimed.attachments).toEqual(attachments); | 63 | expect(claimed.attachments).toEqual(attachments); |
| 64 | expect(claimed.claimedPaths).toEqual([]); | 64 | expect(claimed.claimedPaths).toEqual([]); |
| 65 | }).pipe(Effect.provide(testLayer)), | 65 | }).pipe(Effect.provide(layerTest)), |
| 66 | ); | 66 | ); |
| 67 | 67 | ||
| 68 | it.effect("rejects oversized image batches before copying a pending upload", () => | 68 | it.effect("rejects oversized image batches before copying a pending upload", () => |
| ... | @@ -89,7 +89,7 @@ describe("AttachmentClaims", () => { | ... | @@ -89,7 +89,7 @@ describe("AttachmentClaims", () => { |
| 89 | if (result._tag === "Failure") expect(String(result.cause)).toContain("80 MiB"); | 89 | if (result._tag === "Failure") expect(String(result.cause)).toContain("80 MiB"); |
| 90 | const config = yield* ServerConfig.ServerConfig; | 90 | const config = yield* ServerConfig.ServerConfig; |
| 91 | expect(NodeFS.readdirSync(config.attachmentsDir)).toHaveLength(1); | 91 | expect(NodeFS.readdirSync(config.attachmentsDir)).toHaveLength(1); |
| 92 | }).pipe(Effect.provide(testLayer)), | 92 | }).pipe(Effect.provide(layerTest)), |
| 93 | ); | 93 | ); |
| 94 | 94 | ||
| 95 | it.effect("claims a pending upload into the thread store and rewrites the id", () => | 95 | it.effect("claims a pending upload into the thread store and rewrites the id", () => |
| ... | @@ -117,7 +117,7 @@ describe("AttachmentClaims", () => { | ... | @@ -117,7 +117,7 @@ describe("AttachmentClaims", () => { |
| 117 | entry.startsWith("pending-"), | 117 | entry.startsWith("pending-"), |
| 118 | ); | 118 | ); |
| 119 | expect(pendingFiles).toHaveLength(1); | 119 | expect(pendingFiles).toHaveLength(1); |
| 120 | }).pipe(Effect.provide(testLayer)), | 120 | }).pipe(Effect.provide(layerTest)), |
| 121 | ); | 121 | ); |
| 122 | 122 | ||
| 123 | it.effect("passes already-claimed attachments through untouched", () => | 123 | it.effect("passes already-claimed attachments through untouched", () => |
| ... | @@ -135,7 +135,7 @@ describe("AttachmentClaims", () => { | ... | @@ -135,7 +135,7 @@ describe("AttachmentClaims", () => { |
| 135 | }); | 135 | }); |
| 136 | expect(claimed.attachments[0]).toBe(stored); | 136 | expect(claimed.attachments[0]).toBe(stored); |
| 137 | expect(claimed.claimedPaths).toHaveLength(0); | 137 | expect(claimed.claimedPaths).toHaveLength(0); |
| 138 | }).pipe(Effect.provide(testLayer)), | 138 | }).pipe(Effect.provide(layerTest)), |
| 139 | ); | 139 | ); |
| 140 | 140 | ||
| 141 | it.effect("rejects a pending ref whose staged file is missing", () => | 141 | it.effect("rejects a pending ref whose staged file is missing", () => |
| ... | @@ -151,7 +151,7 @@ describe("AttachmentClaims", () => { | ... | @@ -151,7 +151,7 @@ describe("AttachmentClaims", () => { |
| 151 | claimPendingAttachments({ threadId: "thread-claims-3", attachments: [missing] }), | 151 | claimPendingAttachments({ threadId: "thread-claims-3", attachments: [missing] }), |
| 152 | ); | 152 | ); |
| 153 | expect(result._tag).toBe("Failure"); | 153 | expect(result._tag).toBe("Failure"); |
| 154 | }).pipe(Effect.provide(testLayer)), | 154 | }).pipe(Effect.provide(layerTest)), |
| 155 | ); | 155 | ); |
| 156 | 156 | ||
| 157 | it.effect("rejects a size mismatch and cleans up earlier claims from the batch", () => | 157 | it.effect("rejects a size mismatch and cleans up earlier claims from the batch", () => |
| ... | @@ -186,7 +186,7 @@ describe("AttachmentClaims", () => { | ... | @@ -186,7 +186,7 @@ describe("AttachmentClaims", () => { |
| 186 | entry.startsWith("thread-claims-4-"), | 186 | entry.startsWith("thread-claims-4-"), |
| 187 | ); | 187 | ); |
| 188 | expect(claimedFiles).toHaveLength(0); | 188 | expect(claimedFiles).toHaveLength(0); |
| 189 | }).pipe(Effect.provide(testLayer)), | 189 | }).pipe(Effect.provide(layerTest)), |
| 190 | ); | 190 | ); |
| 191 | 191 | ||
| 192 | it.effect("rejects duplicate attachment ids before copying anything", () => | 192 | it.effect("rejects duplicate attachment ids before copying anything", () => |
| ... | @@ -218,7 +218,7 @@ describe("AttachmentClaims", () => { | ... | @@ -218,7 +218,7 @@ describe("AttachmentClaims", () => { |
| 218 | expect( | 218 | expect( |
| 219 | NodeFS.readdirSync(config.attachmentsDir).filter((entry) => entry.startsWith("pending-")), | 219 | NodeFS.readdirSync(config.attachmentsDir).filter((entry) => entry.startsWith("pending-")), |
| 220 | ).toHaveLength(1); | 220 | ).toHaveLength(1); |
| 221 | }).pipe(Effect.provide(testLayer)), | 221 | }).pipe(Effect.provide(layerTest)), |
| 222 | ); | 222 | ); |
| 223 | 223 | ||
| 224 | it.effect("rolls back a copy that finishes after the claim is interrupted", () => | 224 | it.effect("rolls back a copy that finishes after the claim is interrupted", () => |
| ... | @@ -272,7 +272,7 @@ describe("AttachmentClaims", () => { | ... | @@ -272,7 +272,7 @@ describe("AttachmentClaims", () => { |
| 272 | expect( | 272 | expect( |
| 273 | NodeFS.readdirSync(config.attachmentsDir).filter((entry) => entry.startsWith("pending-")), | 273 | NodeFS.readdirSync(config.attachmentsDir).filter((entry) => entry.startsWith("pending-")), |
| 274 | ).toHaveLength(1); | 274 | ).toHaveLength(1); |
| 275 | }).pipe(Effect.provide(testLayer)), | 275 | }).pipe(Effect.provide(layerTest)), |
| 276 | ); | 276 | ); |
| 277 | 277 | ||
| 278 | it.effect("releaseClaimedAttachments removes claimed copies best-effort", () => | 278 | it.effect("releaseClaimedAttachments removes claimed copies best-effort", () => |
| ... | @@ -291,7 +291,7 @@ describe("AttachmentClaims", () => { | ... | @@ -291,7 +291,7 @@ describe("AttachmentClaims", () => { |
| 291 | expect(NodeFS.existsSync(claimed.claimedPaths[0]!)).toBe(false); | 291 | expect(NodeFS.existsSync(claimed.claimedPaths[0]!)).toBe(false); |
| 292 | // Releasing again is a no-op, not an error. | 292 | // Releasing again is a no-op, not an error. |
| 293 | yield* releaseClaimedAttachments(claimed.claimedPaths); | 293 | yield* releaseClaimedAttachments(claimed.claimedPaths); |
| 294 | }).pipe(Effect.provide(testLayer)), | 294 | }).pipe(Effect.provide(layerTest)), |
| 295 | ); | 295 | ); |
| 296 | 296 | ||
| 297 | it.effect("treats malformed ids as non-pending and passes them through", () => | 297 | it.effect("treats malformed ids as non-pending and passes them through", () => |
| ... | @@ -311,6 +311,6 @@ describe("AttachmentClaims", () => { | ... | @@ -311,6 +311,6 @@ describe("AttachmentClaims", () => { |
| 311 | }); | 311 | }); |
| 312 | expect(claimed.attachments[0]).toBe(bogus); | 312 | expect(claimed.attachments[0]).toBe(bogus); |
| 313 | expect(claimed.claimedPaths).toHaveLength(0); | 313 | expect(claimed.claimedPaths).toHaveLength(0); |
| 314 | }).pipe(Effect.provide(testLayer)), | 314 | }).pipe(Effect.provide(layerTest)), |
| 315 | ); | 315 | ); |
| 316 | }); | 316 | }); |
apps/server/src/orchestration-v2/BackgroundWorkStop.integration.test.ts+3-3| ... | @@ -31,7 +31,7 @@ import type { | ... | @@ -31,7 +31,7 @@ import type { |
| 31 | ProviderAdapterV2TurnInput, | 31 | ProviderAdapterV2TurnInput, |
| 32 | } from "./ProviderAdapter.ts"; | 32 | } from "./ProviderAdapter.ts"; |
| 33 | import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts"; | 33 | import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts"; |
| 34 | import { makeOrchestratorV2ReplayLayerWithRegistry } from "./testkit/ProviderReplayHarness.ts"; | 34 | import * as ProviderReplayHarness from "./testkit/ProviderReplayHarness.ts"; |
| 35 | import { checkpointWorkspace } from "./testkit/ReplayFixtureWorkspace.ts"; | 35 | import { checkpointWorkspace } from "./testkit/ReplayFixtureWorkspace.ts"; |
| 36 | 36 | ||
| 37 | const driver = ProviderDriverKind.make("codex"); | 37 | const driver = ProviderDriverKind.make("codex"); |
| ... | @@ -571,9 +571,9 @@ const stopEarlierBackgroundWork = ({ | ... | @@ -571,9 +571,9 @@ const stopEarlierBackgroundWork = ({ |
| 571 | ); | 571 | ); |
| 572 | }).pipe( | 572 | }).pipe( |
| 573 | Effect.provide( | 573 | Effect.provide( |
| 574 | makeOrchestratorV2ReplayLayerWithRegistry( | 574 | ProviderReplayHarness.layerWithRegistry( |
| 575 | { name: "background-work-stop" }, | 575 | { name: "background-work-stop" }, |
| 576 | ProviderAdapterRegistry.makeSingleLayer(adapter), | 576 | ProviderAdapterRegistry.layerSingle(adapter), |
| 577 | { runEffectWorker: false }, | 577 | { runEffectWorker: false }, |
| 578 | ), | 578 | ), |
| 579 | ), | 579 | ), |
apps/server/src/orchestration-v2/CheckpointCaptureService.test.ts+11-11| ... | @@ -21,7 +21,7 @@ import * as Layer from "effect/Layer"; | ... | @@ -21,7 +21,7 @@ import * as Layer from "effect/Layer"; |
| 21 | import * as Ref from "effect/Ref"; | 21 | import * as Ref from "effect/Ref"; |
| 22 | import * as SqlClient from "effect/sql/SqlClient"; | 22 | import * as SqlClient from "effect/sql/SqlClient"; |
| 23 | 23 | ||
| 24 | import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; | 24 | import * as SqlitePersistence from "../persistence/Layers/Sqlite.ts"; |
| 25 | import * as CheckpointStore from "../checkpointing/CheckpointStore.ts"; | 25 | import * as CheckpointStore from "../checkpointing/CheckpointStore.ts"; |
| 26 | import { VcsProcessTimeoutError } from "@t3tools/contracts"; | 26 | import { VcsProcessTimeoutError } from "@t3tools/contracts"; |
| 27 | import * as CheckpointService from "./CheckpointService.ts"; | 27 | import * as CheckpointService from "./CheckpointService.ts"; |
| ... | @@ -30,9 +30,9 @@ import * as EventSink from "./EventSink.ts"; | ... | @@ -30,9 +30,9 @@ import * as EventSink from "./EventSink.ts"; |
| 30 | import * as IdAllocator from "./IdAllocator.ts"; | 30 | import * as IdAllocator from "./IdAllocator.ts"; |
| 31 | import * as ProjectionStore from "./ProjectionStore.ts"; | 31 | import * as ProjectionStore from "./ProjectionStore.ts"; |
| 32 | 32 | ||
| 33 | const ProjectionStoreTestLayer = Layer.mergeAll( | 33 | const layerProjectionStoreTest = Layer.mergeAll( |
| 34 | ProjectionStore.layer.pipe(Layer.provideMerge(SqlitePersistenceMemory)), | 34 | ProjectionStore.layer.pipe(Layer.provideMerge(SqlitePersistence.layerMemory)), |
| 35 | SqlitePersistenceMemory, | 35 | SqlitePersistence.layerMemory, |
| 36 | ); | 36 | ); |
| 37 | 37 | ||
| 38 | const threadId = ThreadId.make("thread:checkpoint-capture-delegated"); | 38 | const threadId = ThreadId.make("thread:checkpoint-capture-delegated"); |
| ... | @@ -50,7 +50,7 @@ const modelSelection = { | ... | @@ -50,7 +50,7 @@ const modelSelection = { |
| 50 | model: "gpt-5.4", | 50 | model: "gpt-5.4", |
| 51 | } as const; | 51 | } as const; |
| 52 | 52 | ||
| 53 | it.layer(ProjectionStoreTestLayer)("CheckpointCaptureServiceV2", (it) => { | 53 | it.layer(layerProjectionStoreTest)("CheckpointCaptureServiceV2", (it) => { |
| 54 | it.effect.each([false, true])( | 54 | it.effect.each([false, true])( |
| 55 | "captures without decoding history or losing newer delegated completion, ref lookup fails=%s", | 55 | "captures without decoding history or losing newer delegated completion, ref lookup fails=%s", |
| 56 | (refLookupFails) => | 56 | (refLookupFails) => |
| ... | @@ -265,7 +265,7 @@ it.layer(ProjectionStoreTestLayer)("CheckpointCaptureServiceV2", (it) => { | ... | @@ -265,7 +265,7 @@ it.layer(ProjectionStoreTestLayer)("CheckpointCaptureServiceV2", (it) => { |
| 265 | ); | 265 | ); |
| 266 | 266 | ||
| 267 | const committed = yield* Ref.make<ReadonlyArray<OrchestrationV2DomainEvent>>([]); | 267 | const committed = yield* Ref.make<ReadonlyArray<OrchestrationV2DomainEvent>>([]); |
| 268 | const captureLayer = CheckpointCaptureService.layer.pipe( | 268 | const layerCapture = CheckpointCaptureService.layer.pipe( |
| 269 | Layer.provide( | 269 | Layer.provide( |
| 270 | Layer.mergeAll( | 270 | Layer.mergeAll( |
| 271 | IdAllocator.layer, | 271 | IdAllocator.layer, |
| ... | @@ -376,7 +376,7 @@ it.layer(ProjectionStoreTestLayer)("CheckpointCaptureServiceV2", (it) => { | ... | @@ -376,7 +376,7 @@ it.layer(ProjectionStoreTestLayer)("CheckpointCaptureServiceV2", (it) => { |
| 376 | yield* Ref.set(committed, []); | 376 | yield* Ref.set(committed, []); |
| 377 | yield* service.execute({ threadId, runId, scopeId }); | 377 | yield* service.execute({ threadId, runId, scopeId }); |
| 378 | assert.deepEqual(yield* Ref.get(committed), []); | 378 | assert.deepEqual(yield* Ref.get(committed), []); |
| 379 | }).pipe(Effect.provide(captureLayer)); | 379 | }).pipe(Effect.provide(layerCapture)); |
| 380 | }), | 380 | }), |
| 381 | ); | 381 | ); |
| 382 | 382 | ||
| ... | @@ -451,7 +451,7 @@ it.layer(ProjectionStoreTestLayer)("CheckpointCaptureServiceV2", (it) => { | ... | @@ -451,7 +451,7 @@ it.layer(ProjectionStoreTestLayer)("CheckpointCaptureServiceV2", (it) => { |
| 451 | }); | 451 | }); |
| 452 | 452 | ||
| 453 | const committed = yield* Ref.make<ReadonlyArray<OrchestrationV2DomainEvent>>([]); | 453 | const committed = yield* Ref.make<ReadonlyArray<OrchestrationV2DomainEvent>>([]); |
| 454 | const captureLayer = CheckpointCaptureService.layer.pipe( | 454 | const layerCapture = CheckpointCaptureService.layer.pipe( |
| 455 | Layer.provide( | 455 | Layer.provide( |
| 456 | Layer.mergeAll( | 456 | Layer.mergeAll( |
| 457 | IdAllocator.layer, | 457 | IdAllocator.layer, |
| ... | @@ -472,7 +472,7 @@ it.layer(ProjectionStoreTestLayer)("CheckpointCaptureServiceV2", (it) => { | ... | @@ -472,7 +472,7 @@ it.layer(ProjectionStoreTestLayer)("CheckpointCaptureServiceV2", (it) => { |
| 472 | Effect.flatMap((service) => | 472 | Effect.flatMap((service) => |
| 473 | service.execute({ threadId: stoppedThreadId, runId: stoppedRunId, scopeId }), | 473 | service.execute({ threadId: stoppedThreadId, runId: stoppedRunId, scopeId }), |
| 474 | ), | 474 | ), |
| 475 | Effect.provide(captureLayer), | 475 | Effect.provide(layerCapture), |
| 476 | ); | 476 | ); |
| 477 | 477 | ||
| 478 | assert.deepEqual(yield* Ref.get(committed), []); | 478 | assert.deepEqual(yield* Ref.get(committed), []); |
| ... | @@ -680,7 +680,7 @@ it.layer(ProjectionStoreTestLayer)("CheckpointCaptureServiceV2", (it) => { | ... | @@ -680,7 +680,7 @@ it.layer(ProjectionStoreTestLayer)("CheckpointCaptureServiceV2", (it) => { |
| 680 | capturedAt: cancelledAt, | 680 | capturedAt: cancelledAt, |
| 681 | }; | 681 | }; |
| 682 | const commits = yield* Ref.make(0); | 682 | const commits = yield* Ref.make(0); |
| 683 | const captureLayer = CheckpointCaptureService.layer.pipe( | 683 | const layerCapture = CheckpointCaptureService.layer.pipe( |
| 684 | Layer.provide( | 684 | Layer.provide( |
| 685 | Layer.mergeAll( | 685 | Layer.mergeAll( |
| 686 | IdAllocator.layer, | 686 | IdAllocator.layer, |
| ... | @@ -716,7 +716,7 @@ it.layer(ProjectionStoreTestLayer)("CheckpointCaptureServiceV2", (it) => { | ... | @@ -716,7 +716,7 @@ it.layer(ProjectionStoreTestLayer)("CheckpointCaptureServiceV2", (it) => { |
| 716 | runId: cancelledRunId, | 716 | runId: cancelledRunId, |
| 717 | scopeId: cancelledScopeId, | 717 | scopeId: cancelledScopeId, |
| 718 | }); | 718 | }); |
| 719 | }).pipe(Effect.provide(captureLayer)); | 719 | }).pipe(Effect.provide(layerCapture)); |
| 720 | 720 | ||
| 721 | assert.equal(yield* Ref.get(commits), 1); | 721 | assert.equal(yield* Ref.get(commits), 1); |
| 722 | const projected = yield* projectionStore.getCheckpointCaptureContext(cancelledThreadId, { | 722 | const projected = yield* projectionStore.getCheckpointCaptureContext(cancelledThreadId, { |
apps/server/src/orchestration-v2/CheckpointRestoreSafety.test.ts+2-2| ... | @@ -98,7 +98,7 @@ it.effect.each([ | ... | @@ -98,7 +98,7 @@ it.effect.each([ |
| 98 | checkpointScopes: [{ id: scopeId, cwd }], | 98 | checkpointScopes: [{ id: scopeId, cwd }], |
| 99 | runs: [{ id: "later-run", ordinal: 1, status: "completed", rootNodeId: null }], | 99 | runs: [{ id: "later-run", ordinal: 1, status: "completed", rootNodeId: null }], |
| 100 | } as unknown as OrchestrationV2ThreadProjection; | 100 | } as unknown as OrchestrationV2ThreadProjection; |
| 101 | const testLayer = CheckpointRollbackService.layer.pipe( | 101 | const layerTest = CheckpointRollbackService.layer.pipe( |
| 102 | Layer.provide( | 102 | Layer.provide( |
| 103 | Layer.mergeAll( | 103 | Layer.mergeAll( |
| 104 | NodeServices.layer, | 104 | NodeServices.layer, |
| ... | @@ -175,7 +175,7 @@ it.effect.each([ | ... | @@ -175,7 +175,7 @@ it.effect.each([ |
| 175 | ), | 175 | ), |
| 176 | ); | 176 | ); |
| 177 | const service = yield* CheckpointRollbackService.CheckpointRollbackServiceV2.pipe( | 177 | const service = yield* CheckpointRollbackService.CheckpointRollbackServiceV2.pipe( |
| 178 | Effect.provide(testLayer), | 178 | Effect.provide(layerTest), |
| 179 | ); | 179 | ); |
| 180 | const restoreFiles = owner !== "conversation"; | 180 | const restoreFiles = owner !== "conversation"; |
| 181 | const rejected = !["sibling", "stopped-provider", "shared-provider", "conversation"].includes( | 181 | const rejected = !["sibling", "stopped-provider", "shared-provider", "conversation"].includes( |
apps/server/src/orchestration-v2/CheckpointRollbackService.test.ts+11-11| ... | @@ -32,7 +32,7 @@ import * as RuntimePolicy from "./RuntimePolicy.ts"; | ... | @@ -32,7 +32,7 @@ import * as RuntimePolicy from "./RuntimePolicy.ts"; |
| 32 | const unrelatedProject = Option.some({ | 32 | const unrelatedProject = Option.some({ |
| 33 | workspaceRoot: "/nonexistent/t3-rollback-project", | 33 | workspaceRoot: "/nonexistent/t3-rollback-project", |
| 34 | } as never); | 34 | } as never); |
| 35 | const checkpointRollbackServiceLayer = CheckpointRollbackService.layer.pipe( | 35 | const layerCheckpointRollbackService = CheckpointRollbackService.layer.pipe( |
| 36 | Layer.provide( | 36 | Layer.provide( |
| 37 | Layer.mergeAll( | 37 | Layer.mergeAll( |
| 38 | NodeServices.layer, | 38 | NodeServices.layer, |
| ... | @@ -61,7 +61,7 @@ it.effect("rejects a non-ready checkpoint before opening a session or restoring | ... | @@ -61,7 +61,7 @@ it.effect("rejects a non-ready checkpoint before opening a session or restoring |
| 61 | checkpoints: [{ id: checkpointId, scopeId, status: "stale" }], | 61 | checkpoints: [{ id: checkpointId, scopeId, status: "stale" }], |
| 62 | checkpointScopes: [{ id: scopeId, cwd: process.cwd() }], | 62 | checkpointScopes: [{ id: scopeId, cwd: process.cwd() }], |
| 63 | } as unknown as OrchestrationV2ThreadProjection; | 63 | } as unknown as OrchestrationV2ThreadProjection; |
| 64 | const testLayer = checkpointRollbackServiceLayer.pipe( | 64 | const layerTest = layerCheckpointRollbackService.pipe( |
| 65 | Layer.provide( | 65 | Layer.provide( |
| 66 | Layer.mergeAll( | 66 | Layer.mergeAll( |
| 67 | Layer.mock(CheckpointService.CheckpointServiceV2)({ restore }), | 67 | Layer.mock(CheckpointService.CheckpointServiceV2)({ restore }), |
| ... | @@ -103,7 +103,7 @@ it.effect("rejects a non-ready checkpoint before opening a session or restoring | ... | @@ -103,7 +103,7 @@ it.effect("rejects a non-ready checkpoint before opening a session or restoring |
| 103 | assert.equal(resolveRuntimePolicy.mock.calls.length, 0); | 103 | assert.equal(resolveRuntimePolicy.mock.calls.length, 0); |
| 104 | assert.equal(open.mock.calls.length, 0); | 104 | assert.equal(open.mock.calls.length, 0); |
| 105 | assert.equal(restore.mock.calls.length, 0); | 105 | assert.equal(restore.mock.calls.length, 0); |
| 106 | }).pipe(Effect.provide(testLayer)); | 106 | }).pipe(Effect.provide(layerTest)); |
| 107 | }); | 107 | }); |
| 108 | 108 | ||
| 109 | it.effect("rejects a rollback when another provider thread became active", () => { | 109 | it.effect("rejects a rollback when another provider thread became active", () => { |
| ... | @@ -138,7 +138,7 @@ it.effect("rejects a rollback when another provider thread became active", () => | ... | @@ -138,7 +138,7 @@ it.effect("rejects a rollback when another provider thread became active", () => |
| 138 | checkpoints: [{ id: checkpointId, scopeId, status: "ready" }], | 138 | checkpoints: [{ id: checkpointId, scopeId, status: "ready" }], |
| 139 | checkpointScopes: [{ id: scopeId, cwd: process.cwd() }], | 139 | checkpointScopes: [{ id: scopeId, cwd: process.cwd() }], |
| 140 | } as unknown as OrchestrationV2ThreadProjection; | 140 | } as unknown as OrchestrationV2ThreadProjection; |
| 141 | const testLayer = checkpointRollbackServiceLayer.pipe( | 141 | const layerTest = layerCheckpointRollbackService.pipe( |
| 142 | Layer.provide( | 142 | Layer.provide( |
| 143 | Layer.mergeAll( | 143 | Layer.mergeAll( |
| 144 | Layer.mock(CheckpointService.CheckpointServiceV2)({ restore }), | 144 | Layer.mock(CheckpointService.CheckpointServiceV2)({ restore }), |
| ... | @@ -180,7 +180,7 @@ it.effect("rejects a rollback when another provider thread became active", () => | ... | @@ -180,7 +180,7 @@ it.effect("rejects a rollback when another provider thread became active", () => |
| 180 | assert.equal(resolveRuntimePolicy.mock.calls.length, 0); | 180 | assert.equal(resolveRuntimePolicy.mock.calls.length, 0); |
| 181 | assert.equal(open.mock.calls.length, 0); | 181 | assert.equal(open.mock.calls.length, 0); |
| 182 | assert.equal(restore.mock.calls.length, 0); | 182 | assert.equal(restore.mock.calls.length, 0); |
| 183 | }).pipe(Effect.provide(testLayer)); | 183 | }).pipe(Effect.provide(layerTest)); |
| 184 | }); | 184 | }); |
| 185 | 185 | ||
| 186 | it.effect("rejects a rollback when provider selection changed before execution", () => { | 186 | it.effect("rejects a rollback when provider selection changed before execution", () => { |
| ... | @@ -218,7 +218,7 @@ it.effect("rejects a rollback when provider selection changed before execution", | ... | @@ -218,7 +218,7 @@ it.effect("rejects a rollback when provider selection changed before execution", |
| 218 | checkpoints: [{ id: checkpointId, scopeId, status: "ready" }], | 218 | checkpoints: [{ id: checkpointId, scopeId, status: "ready" }], |
| 219 | checkpointScopes: [{ id: scopeId, cwd: process.cwd() }], | 219 | checkpointScopes: [{ id: scopeId, cwd: process.cwd() }], |
| 220 | } as unknown as OrchestrationV2ThreadProjection; | 220 | } as unknown as OrchestrationV2ThreadProjection; |
| 221 | const testLayer = checkpointRollbackServiceLayer.pipe( | 221 | const layerTest = layerCheckpointRollbackService.pipe( |
| 222 | Layer.provide( | 222 | Layer.provide( |
| 223 | Layer.mergeAll( | 223 | Layer.mergeAll( |
| 224 | Layer.mock(CheckpointService.CheckpointServiceV2)({ restore }), | 224 | Layer.mock(CheckpointService.CheckpointServiceV2)({ restore }), |
| ... | @@ -260,7 +260,7 @@ it.effect("rejects a rollback when provider selection changed before execution", | ... | @@ -260,7 +260,7 @@ it.effect("rejects a rollback when provider selection changed before execution", |
| 260 | assert.equal(resolveRuntimePolicy.mock.calls.length, 0); | 260 | assert.equal(resolveRuntimePolicy.mock.calls.length, 0); |
| 261 | assert.equal(open.mock.calls.length, 0); | 261 | assert.equal(open.mock.calls.length, 0); |
| 262 | assert.equal(restore.mock.calls.length, 0); | 262 | assert.equal(restore.mock.calls.length, 0); |
| 263 | }).pipe(Effect.provide(testLayer)); | 263 | }).pipe(Effect.provide(layerTest)); |
| 264 | }); | 264 | }); |
| 265 | 265 | ||
| 266 | it.effect("reports a missing provider turn as a structured rollback failure", () => { | 266 | it.effect("reports a missing provider turn as a structured rollback failure", () => { |
| ... | @@ -289,7 +289,7 @@ it.effect("reports a missing provider turn as a structured rollback failure", () | ... | @@ -289,7 +289,7 @@ it.effect("reports a missing provider turn as a structured rollback failure", () |
| 289 | attempts: [], | 289 | attempts: [], |
| 290 | providerTurns: [], | 290 | providerTurns: [], |
| 291 | } as unknown as OrchestrationV2ThreadProjection; | 291 | } as unknown as OrchestrationV2ThreadProjection; |
| 292 | const testLayer = checkpointRollbackServiceLayer.pipe( | 292 | const layerTest = layerCheckpointRollbackService.pipe( |
| 293 | Layer.provide( | 293 | Layer.provide( |
| 294 | Layer.mergeAll( | 294 | Layer.mergeAll( |
| 295 | Layer.mock(CheckpointService.CheckpointServiceV2)({ restore }), | 295 | Layer.mock(CheckpointService.CheckpointServiceV2)({ restore }), |
| ... | @@ -333,7 +333,7 @@ it.effect("reports a missing provider turn as a structured rollback failure", () | ... | @@ -333,7 +333,7 @@ it.effect("reports a missing provider turn as a structured rollback failure", () |
| 333 | ); | 333 | ); |
| 334 | assert.equal(error.cause, undefined); | 334 | assert.equal(error.cause, undefined); |
| 335 | assert.equal(restore.mock.calls.length, 0); | 335 | assert.equal(restore.mock.calls.length, 0); |
| 336 | }).pipe(Effect.provide(testLayer)); | 336 | }).pipe(Effect.provide(layerTest)); |
| 337 | }); | 337 | }); |
| 338 | 338 | ||
| 339 | it.effect.each([ | 339 | it.effect.each([ |
| ... | @@ -387,7 +387,7 @@ it.effect.each([ | ... | @@ -387,7 +387,7 @@ it.effect.each([ |
| 387 | activeAttemptId: `attempt-${ordinal}`, | 387 | activeAttemptId: `attempt-${ordinal}`, |
| 388 | })), | 388 | })), |
| 389 | } as unknown as OrchestrationV2ThreadProjection; | 389 | } as unknown as OrchestrationV2ThreadProjection; |
| 390 | const testLayer = checkpointRollbackServiceLayer.pipe( | 390 | const layerTest = layerCheckpointRollbackService.pipe( |
| 391 | Layer.provide( | 391 | Layer.provide( |
| 392 | Layer.mergeAll( | 392 | Layer.mergeAll( |
| 393 | Layer.mock(CheckpointService.CheckpointServiceV2)({ | 393 | Layer.mock(CheckpointService.CheckpointServiceV2)({ |
| ... | @@ -466,7 +466,7 @@ it.effect.each([ | ... | @@ -466,7 +466,7 @@ it.effect.each([ |
| 466 | calls, | 466 | calls, |
| 467 | restoreFiles ? ["provider", "files", "projection"] : ["provider", "projection"], | 467 | restoreFiles ? ["provider", "files", "projection"] : ["provider", "projection"], |
| 468 | ); | 468 | ); |
| 469 | }).pipe(Effect.provide(testLayer)); | 469 | }).pipe(Effect.provide(layerTest)); |
| 470 | }); | 470 | }); |
| 471 | 471 | ||
| 472 | it.effect.skipIf(!symlinksSupported)( | 472 | it.effect.skipIf(!symlinksSupported)( |
apps/server/src/orchestration-v2/CheckpointScopeOwnership.test.ts+7-7| ... | @@ -19,10 +19,10 @@ import * as IdAllocator from "./IdAllocator.ts"; | ... | @@ -19,10 +19,10 @@ import * as IdAllocator from "./IdAllocator.ts"; |
| 19 | import { checkpointRefForScopeOrdinal } from "./CheckpointService.ts"; | 19 | import { checkpointRefForScopeOrdinal } from "./CheckpointService.ts"; |
| 20 | import * as ProjectionStore from "./ProjectionStore.ts"; | 20 | import * as ProjectionStore from "./ProjectionStore.ts"; |
| 21 | import * as ThreadManagement from "./ThreadManagementService.ts"; | 21 | import * as ThreadManagement from "./ThreadManagementService.ts"; |
| 22 | import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; | 22 | import * as SqlitePersistence from "../persistence/Layers/Sqlite.ts"; |
| 23 | const projectionLayer = Layer.mergeAll( | 23 | const layerProjection = Layer.mergeAll( |
| 24 | ProjectionStore.layer.pipe(Layer.provideMerge(SqlitePersistenceMemory)), | 24 | ProjectionStore.layer.pipe(Layer.provideMerge(SqlitePersistence.layerMemory)), |
| 25 | SqlitePersistenceMemory, | 25 | SqlitePersistence.layerMemory, |
| 26 | IdAllocator.layer, | 26 | IdAllocator.layer, |
| 27 | ); | 27 | ); |
| 28 | it.effect("resolves the thread baseline after a second root run replaces scope ownership", () => | 28 | it.effect("resolves the thread baseline after a second root run replaces scope ownership", () => |
| ... | @@ -205,7 +205,7 @@ it.effect("resolves the thread baseline after a second root run replaces scope o | ... | @@ -205,7 +205,7 @@ it.effect("resolves the thread baseline after a second root run replaces scope o |
| 205 | assert.equal(context.checkpointScopes.length, 1); | 205 | assert.equal(context.checkpointScopes.length, 1); |
| 206 | assert.equal(context.checkpointScopes[0]?.runId, secondRunId); | 206 | assert.equal(context.checkpointScopes[0]?.runId, secondRunId); |
| 207 | 207 | ||
| 208 | const queryLayer = CheckpointDiffQuery.layer.pipe( | 208 | const layerQuery = CheckpointDiffQuery.layer.pipe( |
| 209 | Layer.provide( | 209 | Layer.provide( |
| 210 | Layer.mergeAll( | 210 | Layer.mergeAll( |
| 211 | Layer.mock(ThreadManagement.ThreadManagementService)({ | 211 | Layer.mock(ThreadManagement.ThreadManagementService)({ |
| ... | @@ -231,7 +231,7 @@ it.effect("resolves the thread baseline after a second root run replaces scope o | ... | @@ -231,7 +231,7 @@ it.effect("resolves the thread baseline after a second root run replaces scope o |
| 231 | const result = yield* Effect.gen(function* () { | 231 | const result = yield* Effect.gen(function* () { |
| 232 | const query = yield* CheckpointDiffQuery.CheckpointDiffQuery; | 232 | const query = yield* CheckpointDiffQuery.CheckpointDiffQuery; |
| 233 | return yield* query.getFullThreadDiff({ threadId, toTurnCount: 2 }); | 233 | return yield* query.getFullThreadDiff({ threadId, toTurnCount: 2 }); |
| 234 | }).pipe(Effect.provide(queryLayer)); | 234 | }).pipe(Effect.provide(layerQuery)); |
| 235 | assert.equal(result.diff, "two-run diff"); | 235 | assert.equal(result.diff, "two-run diff"); |
| 236 | }).pipe(Effect.provide(projectionLayer)), | 236 | }).pipe(Effect.provide(layerProjection)), |
| 237 | ); | 237 | ); |
apps/server/src/orchestration-v2/CheckpointService.test.ts+2-2| ... | @@ -47,7 +47,7 @@ it.effect.each([false, true, "interrupt"] as const)( | ... | @@ -47,7 +47,7 @@ it.effect.each([false, true, "interrupt"] as const)( |
| 47 | ) | 47 | ) |
| 48 | : Effect.succeed(true), | 48 | : Effect.succeed(true), |
| 49 | ); | 49 | ); |
| 50 | const testLayer = CheckpointService.layer.pipe( | 50 | const layerTest = CheckpointService.layer.pipe( |
| 51 | Layer.provide( | 51 | Layer.provide( |
| 52 | Layer.mergeAll( | 52 | Layer.mergeAll( |
| 53 | IdAllocator.layer, | 53 | IdAllocator.layer, |
| ... | @@ -98,6 +98,6 @@ it.effect.each([false, true, "interrupt"] as const)( | ... | @@ -98,6 +98,6 @@ it.effect.each([false, true, "interrupt"] as const)( |
| 98 | cwd: scope.cwd, | 98 | cwd: scope.cwd, |
| 99 | checkpointRef: baseline.ref, | 99 | checkpointRef: baseline.ref, |
| 100 | }); | 100 | }); |
| 101 | }).pipe(Effect.provide(testLayer)); | 101 | }).pipe(Effect.provide(layerTest)); |
| 102 | }, | 102 | }, |
| 103 | ); | 103 | ); |
apps/server/src/orchestration-v2/ClaudeAutomaticDelivery.integration.test.ts+9-5| ... | @@ -26,7 +26,7 @@ import * as Stream from "effect/Stream"; | ... | @@ -26,7 +26,7 @@ import * as Stream from "effect/Stream"; |
| 26 | import * as ScheduledTaskService from "../scheduledTasks/ScheduledTaskService.ts"; | 26 | import * as ScheduledTaskService from "../scheduledTasks/ScheduledTaskService.ts"; |
| 27 | import * as SecretRequests from "../secrets/SecretRequests.ts"; | 27 | import * as SecretRequests from "../secrets/SecretRequests.ts"; |
| 28 | import * as Scheduler from "../scheduling/Scheduler.ts"; | 28 | import * as Scheduler from "../scheduling/Scheduler.ts"; |
| 29 | import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; | 29 | import * as SqlitePersistence from "../persistence/Layers/Sqlite.ts"; |
| 30 | import * as ClaudeAdapterV2 from "./Adapters/ClaudeAdapterV2.ts"; | 30 | import * as ClaudeAdapterV2 from "./Adapters/ClaudeAdapterV2.ts"; |
| 31 | import * as EffectWorker from "./EffectWorker.ts"; | 31 | import * as EffectWorker from "./EffectWorker.ts"; |
| 32 | import * as EventSink from "./EventSink.ts"; | 32 | import * as EventSink from "./EventSink.ts"; |
| ... | @@ -36,7 +36,7 @@ import * as Orchestrator from "./Orchestrator.ts"; | ... | @@ -36,7 +36,7 @@ import * as Orchestrator from "./Orchestrator.ts"; |
| 36 | import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts"; | 36 | import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts"; |
| 37 | import * as ThreadLaunchService from "./ThreadLaunchService.ts"; | 37 | import * as ThreadLaunchService from "./ThreadLaunchService.ts"; |
| 38 | import * as ThreadManagementService from "./ThreadManagementService.ts"; | 38 | import * as ThreadManagementService from "./ThreadManagementService.ts"; |
| 39 | import { makeOrchestratorV2ReplayLayerWithRegistry } from "./testkit/ProviderReplayHarness.ts"; | 39 | import * as ProviderReplayHarness from "./testkit/ProviderReplayHarness.ts"; |
| 40 | import { checkpointWorkspace } from "./testkit/ReplayFixtureWorkspace.ts"; | 40 | import { checkpointWorkspace } from "./testkit/ReplayFixtureWorkspace.ts"; |
| 41 | 41 | ||
| 42 | const sessionId = "automatic-delivery-session"; | 42 | const sessionId = "automatic-delivery-session"; |
| ... | @@ -318,7 +318,11 @@ it.effect.each(["child completion", "scheduled message", "user steering"] as con | ... | @@ -318,7 +318,11 @@ it.effect.each(["child completion", "scheduled message", "user steering"] as con |
| 318 | Layer.provide(Layer.mock(ThreadLaunchService.ThreadLaunchService)({})), | 318 | Layer.provide(Layer.mock(ThreadLaunchService.ThreadLaunchService)({})), |
| 319 | Layer.provide(Layer.mock(SecretRequests.SecretRequests)({})), | 319 | Layer.provide(Layer.mock(SecretRequests.SecretRequests)({})), |
| 320 | Layer.provide( | 320 | Layer.provide( |
| 321 | Layer.mergeAll(NodeCrypto.layer, Scheduler.layer, SqlitePersistenceMemory), | 321 | Layer.mergeAll( |
| 322 | NodeCrypto.layer, | ||
| 323 | Scheduler.layer, | ||
| 324 | SqlitePersistence.layerMemory, | ||
| 325 | ), | ||
| 322 | ), | 326 | ), |
| 323 | ), | 327 | ), |
| 324 | ), | 328 | ), |
| ... | @@ -410,9 +414,9 @@ it.effect.each(["child completion", "scheduled message", "user steering"] as con | ... | @@ -410,9 +414,9 @@ it.effect.each(["child completion", "scheduled message", "user steering"] as con |
| 410 | assert.equal(offers.length, 2); | 414 | assert.equal(offers.length, 2); |
| 411 | }).pipe( | 415 | }).pipe( |
| 412 | Effect.provide( | 416 | Effect.provide( |
| 413 | makeOrchestratorV2ReplayLayerWithRegistry( | 417 | ProviderReplayHarness.layerWithRegistry( |
| 414 | { name: "claude-automatic-delivery" }, | 418 | { name: "claude-automatic-delivery" }, |
| 415 | ProviderAdapterRegistry.makeSingleLayer(adapter), | 419 | ProviderAdapterRegistry.layerSingle(adapter), |
| 416 | { runEffectWorker: false }, | 420 | { runEffectWorker: false }, |
| 417 | ), | 421 | ), |
| 418 | ), | 422 | ), |
apps/server/src/orchestration-v2/CommandReceiptStore.ts+4-4| ... | @@ -6,7 +6,7 @@ import * as Layer from "effect/Layer"; | ... | @@ -6,7 +6,7 @@ import * as Layer from "effect/Layer"; |
| 6 | import * as Option from "effect/Option"; | 6 | import * as Option from "effect/Option"; |
| 7 | import * as Schema from "effect/Schema"; | 7 | import * as Schema from "effect/Schema"; |
| 8 | 8 | ||
| 9 | import { OrchestrationCommandReceiptRepositoryLive } from "../persistence/Layers/OrchestrationCommandReceipts.ts"; | 9 | import * as OrchestrationCommandReceiptsLayer from "../persistence/Layers/OrchestrationCommandReceipts.ts"; |
| 10 | import * as OrchestrationCommandReceipts from "../persistence/Services/OrchestrationCommandReceipts.ts"; | 10 | import * as OrchestrationCommandReceipts from "../persistence/Services/OrchestrationCommandReceipts.ts"; |
| 11 | 11 | ||
| 12 | /** | 12 | /** |
| ... | @@ -139,7 +139,7 @@ function toApplicationReceipt( | ... | @@ -139,7 +139,7 @@ function toApplicationReceipt( |
| 139 | }; | 139 | }; |
| 140 | } | 140 | } |
| 141 | 141 | ||
| 142 | const baseLayer: Layer.Layer< | 142 | const layerBase: Layer.Layer< |
| 143 | CommandReceiptStoreV2, | 143 | CommandReceiptStoreV2, |
| 144 | never, | 144 | never, |
| 145 | OrchestrationCommandReceipts.OrchestrationCommandReceiptRepository | 145 | OrchestrationCommandReceipts.OrchestrationCommandReceiptRepository |
| ... | @@ -209,6 +209,6 @@ const baseLayer: Layer.Layer< | ... | @@ -209,6 +209,6 @@ const baseLayer: Layer.Layer< |
| 209 | }), | 209 | }), |
| 210 | ); | 210 | ); |
| 211 | 211 | ||
| 212 | export const layer = baseLayer.pipe(Layer.provide(OrchestrationCommandReceiptRepositoryLive)); | 212 | export const layer = layerBase.pipe(Layer.provide(OrchestrationCommandReceiptsLayer.layer)); |
| 213 | 213 | ||
| 214 | export const layerFromApplicationReceipts = baseLayer; | 214 | export const layerFromApplicationReceipts = layerBase; |
apps/server/src/orchestration-v2/ContextHandoffService.test.ts+2-2| ... | @@ -15,7 +15,7 @@ import * as Layer from "effect/Layer"; | ... | @@ -15,7 +15,7 @@ import * as Layer from "effect/Layer"; |
| 15 | import * as ContextHandoffService from "./ContextHandoffService.ts"; | 15 | import * as ContextHandoffService from "./ContextHandoffService.ts"; |
| 16 | import * as IdAllocator from "./IdAllocator.ts"; | 16 | import * as IdAllocator from "./IdAllocator.ts"; |
| 17 | 17 | ||
| 18 | const TestLayer = ContextHandoffService.layer.pipe(Layer.provide(IdAllocator.layer)); | 18 | const layerTest = ContextHandoffService.layer.pipe(Layer.provide(IdAllocator.layer)); |
| 19 | 19 | ||
| 20 | function importedItem( | 20 | function importedItem( |
| 21 | input: | 21 | input: |
| ... | @@ -67,7 +67,7 @@ function importedItem( | ... | @@ -67,7 +67,7 @@ function importedItem( |
| 67 | }; | 67 | }; |
| 68 | } | 68 | } |
| 69 | 69 | ||
| 70 | it.layer(TestLayer)("ContextHandoffService legacy import", (it) => { | 70 | it.layer(layerTest)("ContextHandoffService legacy import", (it) => { |
| 71 | it.effect("prepares imported history for the first native v2 turn", () => | 71 | it.effect("prepares imported history for the first native v2 turn", () => |
| 72 | Effect.gen(function* () { | 72 | Effect.gen(function* () { |
| 73 | const service = yield* ContextHandoffService.ContextHandoffServiceV2; | 73 | const service = yield* ContextHandoffService.ContextHandoffServiceV2; |
apps/server/src/orchestration-v2/CursorOrchestratorV2.live.test.ts+34-34| ... | @@ -21,12 +21,12 @@ import * as CheckpointStore from "../checkpointing/CheckpointStore.ts"; | ... | @@ -21,12 +21,12 @@ import * as CheckpointStore from "../checkpointing/CheckpointStore.ts"; |
| 21 | import * as BackgroundPolicy from "../background/BackgroundPolicy.ts"; | 21 | import * as BackgroundPolicy from "../background/BackgroundPolicy.ts"; |
| 22 | import * as HostPowerMonitor from "../background/HostPowerMonitor.ts"; | 22 | import * as HostPowerMonitor from "../background/HostPowerMonitor.ts"; |
| 23 | import * as ServerConfig from "../config.ts"; | 23 | import * as ServerConfig from "../config.ts"; |
| 24 | import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; | 24 | import * as SqlitePersistence from "../persistence/Layers/Sqlite.ts"; |
| 25 | import * as AntigravityInstallation from "../provider/AntigravityInstallation.ts"; | 25 | import * as AntigravityInstallation from "../provider/AntigravityInstallation.ts"; |
| 26 | import * as CodexInstallation from "../provider/CodexInstallation.ts"; | 26 | import * as CodexInstallation from "../provider/CodexInstallation.ts"; |
| 27 | import * as ServerEnvironment from "../environment/ServerEnvironment.ts"; | 27 | import * as ServerEnvironment from "../environment/ServerEnvironment.ts"; |
| 28 | import * as ModelManifest from "../provider/ModelManifest.ts"; | 28 | import * as ModelManifest from "../provider/ModelManifest.ts"; |
| 29 | import { ProviderInstanceRegistryHydrationLive } from "../provider/Layers/ProviderInstanceRegistryHydration.ts"; | 29 | import * as ProviderInstanceRegistryHydration from "../provider/Layers/ProviderInstanceRegistryHydration.ts"; |
| 30 | import * as ProviderEventLoggers from "../provider/Layers/ProviderEventLoggers.ts"; | 30 | import * as ProviderEventLoggers from "../provider/Layers/ProviderEventLoggers.ts"; |
| 31 | import * as OpenCodeRuntime from "../provider/opencodeRuntime.ts"; | 31 | import * as OpenCodeRuntime from "../provider/opencodeRuntime.ts"; |
| 32 | import * as OpenCodeServerLedger from "../provider/OpenCodeServerLedger.ts"; | 32 | import * as OpenCodeServerLedger from "../provider/OpenCodeServerLedger.ts"; |
| ... | @@ -34,54 +34,54 @@ import * as ServerSettings from "../serverSettings.ts"; | ... | @@ -34,54 +34,54 @@ import * as ServerSettings from "../serverSettings.ts"; |
| 34 | import * as VcsDriverRegistry from "../vcs/VcsDriverRegistry.ts"; | 34 | import * as VcsDriverRegistry from "../vcs/VcsDriverRegistry.ts"; |
| 35 | import * as VcsProcess from "../vcs/VcsProcess.ts"; | 35 | import * as VcsProcess from "../vcs/VcsProcess.ts"; |
| 36 | import * as Orchestrator from "./Orchestrator.ts"; | 36 | import * as Orchestrator from "./Orchestrator.ts"; |
| 37 | import { worktreeRepairDependenciesTestLayer } from "./ProviderTurnStartService.testkit.ts"; | 37 | import * as ProviderTurnStartServiceTestkit from "./ProviderTurnStartService.testkit.ts"; |
| 38 | import * as EffectWorker from "./EffectWorker.ts"; | 38 | import * as EffectWorker from "./EffectWorker.ts"; |
| 39 | import { OrchestrationV2LayerLive } from "./runtimeLayer.ts"; | 39 | import * as RuntimeLayer from "./runtimeLayer.ts"; |
| 40 | import * as McpSessionRegistryTestkit from "../mcp/McpSessionRegistry.testkit.ts"; | 40 | import * as McpSessionRegistryTestkit from "../mcp/McpSessionRegistry.testkit.ts"; |
| 41 | import { CURSOR_MODEL_SELECTION, SUBAGENT_PROMPT } from "./testkit/fixtures/shared.ts"; | 41 | import { CURSOR_MODEL_SELECTION, SUBAGENT_PROMPT } from "./testkit/fixtures/shared.ts"; |
| 42 | 42 | ||
| 43 | const PlatformTestLayer = Layer.merge( | 43 | const layerPlatformTest = Layer.merge( |
| 44 | NodeServices.layer, | 44 | NodeServices.layer, |
| 45 | Layer.mock(SourceControlProviderRegistry.SourceControlProviderRegistry)({ | 45 | Layer.mock(SourceControlProviderRegistry.SourceControlProviderRegistry)({ |
| 46 | resolveLink: () => Effect.die("unused title link"), | 46 | resolveLink: () => Effect.die("unused title link"), |
| 47 | }), | 47 | }), |
| 48 | ); | 48 | ); |
| 49 | 49 | ||
| 50 | const serverConfigLayer = ServerConfig.layerTest(process.cwd(), { | 50 | const layerServerConfig = ServerConfig.layerTest(process.cwd(), { |
| 51 | prefix: "t3-cursor-v2-live-", | 51 | prefix: "t3-cursor-v2-live-", |
| 52 | }); | 52 | }); |
| 53 | 53 | ||
| 54 | const vcsDriverRegistryLayer = VcsDriverRegistry.layer.pipe( | 54 | const layerVcsDriverRegistry = VcsDriverRegistry.layer.pipe( |
| 55 | Layer.provide(VcsProcess.layer), | 55 | Layer.provide(VcsProcess.layer), |
| 56 | Layer.provide(serverConfigLayer), | 56 | Layer.provide(layerServerConfig), |
| 57 | Layer.provide(PlatformTestLayer), | 57 | Layer.provide(layerPlatformTest), |
| 58 | ); | 58 | ); |
| 59 | 59 | ||
| 60 | const checkpointStoreLayer = CheckpointStore.layer.pipe(Layer.provide(vcsDriverRegistryLayer)); | 60 | const layerCheckpointStore = CheckpointStore.layer.pipe(Layer.provide(layerVcsDriverRegistry)); |
| 61 | 61 | ||
| 62 | const serverSettingsLayer = ServerSettings.layerTest({ | 62 | const layerServerSettings = ServerSettings.layerTest({ |
| 63 | providers: { | 63 | providers: { |
| 64 | cursor: { enabled: true }, | 64 | cursor: { enabled: true }, |
| 65 | }, | 65 | }, |
| 66 | }); | 66 | }); |
| 67 | const backgroundPolicyLayer = BackgroundPolicy.layer.pipe( | 67 | const layerBackgroundPolicy = BackgroundPolicy.layer.pipe( |
| 68 | Layer.provide(Layer.effect(HostPowerMonitor.HostPowerMonitor, HostPowerMonitor.make())), | 68 | Layer.provide(Layer.effect(HostPowerMonitor.HostPowerMonitor, HostPowerMonitor.make())), |
| 69 | Layer.provide(serverSettingsLayer), | 69 | Layer.provide(layerServerSettings), |
| 70 | ); | 70 | ); |
| 71 | const providerInstanceRegistryLayer = ProviderInstanceRegistryHydrationLive.pipe( | 71 | const layerProviderInstanceRegistry = ProviderInstanceRegistryHydration.layer.pipe( |
| 72 | Layer.provide( | 72 | Layer.provide( |
| 73 | Layer.mergeAll( | 73 | Layer.mergeAll( |
| 74 | serverConfigLayer.pipe(Layer.provide(PlatformTestLayer)), | 74 | layerServerConfig.pipe(Layer.provide(layerPlatformTest)), |
| 75 | serverSettingsLayer, | 75 | layerServerSettings, |
| 76 | ServerSecretStore.layer.pipe( | 76 | ServerSecretStore.layer.pipe( |
| 77 | Layer.provide(serverConfigLayer), | 77 | Layer.provide(layerServerConfig), |
| 78 | Layer.provide(PlatformTestLayer), | 78 | Layer.provide(layerPlatformTest), |
| 79 | ), | 79 | ), |
| 80 | NodeServices.layer, | 80 | NodeServices.layer, |
| 81 | FetchHttpClient.layer, | 81 | FetchHttpClient.layer, |
| 82 | OpenCodeRuntime.OpenCodeRuntimeLive.pipe( | 82 | OpenCodeRuntime.layer.pipe( |
| 83 | Layer.provide(OpenCodeServerLedger.layerTest), | 83 | Layer.provide(OpenCodeServerLedger.layerTest), |
| 84 | Layer.provide(PlatformTestLayer), | 84 | Layer.provide(layerPlatformTest), |
| 85 | ), | 85 | ), |
| 86 | Layer.succeed( | 86 | Layer.succeed( |
| 87 | ProviderEventLoggers.ProviderEventLoggers, | 87 | ProviderEventLoggers.ProviderEventLoggers, |
| ... | @@ -89,9 +89,9 @@ const providerInstanceRegistryLayer = ProviderInstanceRegistryHydrationLive.pipe | ... | @@ -89,9 +89,9 @@ const providerInstanceRegistryLayer = ProviderInstanceRegistryHydrationLive.pipe |
| 89 | ), | 89 | ), |
| 90 | ModelManifest.layerTest, | 90 | ModelManifest.layerTest, |
| 91 | AntigravityInstallation.AntigravityInstallation.layer.pipe( | 91 | AntigravityInstallation.AntigravityInstallation.layer.pipe( |
| 92 | Layer.provide(serverConfigLayer.pipe(Layer.provide(PlatformTestLayer))), | 92 | Layer.provide(layerServerConfig.pipe(Layer.provide(layerPlatformTest))), |
| 93 | Layer.provide(FetchHttpClient.layer), | 93 | Layer.provide(FetchHttpClient.layer), |
| 94 | Layer.provide(PlatformTestLayer), | 94 | Layer.provide(layerPlatformTest), |
| 95 | ), | 95 | ), |
| 96 | // The Codex driver now resolves managed ChatGPT installs; these runs never launch Codex. | 96 | // The Codex driver now resolves managed ChatGPT installs; these runs never launch Codex. |
| 97 | Layer.mock(CodexInstallation.CodexInstallation)({ | 97 | Layer.mock(CodexInstallation.CodexInstallation)({ |
| ... | @@ -106,17 +106,17 @@ const providerInstanceRegistryLayer = ProviderInstanceRegistryHydrationLive.pipe | ... | @@ -106,17 +106,17 @@ const providerInstanceRegistryLayer = ProviderInstanceRegistryHydrationLive.pipe |
| 106 | ), | 106 | ), |
| 107 | ); | 107 | ); |
| 108 | 108 | ||
| 109 | const liveLayer = OrchestrationV2LayerLive.pipe( | 109 | const layerLive = RuntimeLayer.layer.pipe( |
| 110 | Layer.provide(worktreeRepairDependenciesTestLayer), | 110 | Layer.provide(ProviderTurnStartServiceTestkit.layer), |
| 111 | Layer.provide(McpSessionRegistryTestkit.layer), | 111 | Layer.provide(McpSessionRegistryTestkit.layer), |
| 112 | Layer.provide(SqlitePersistenceMemory), | 112 | Layer.provide(SqlitePersistence.layerMemory), |
| 113 | Layer.provide(checkpointStoreLayer), | 113 | Layer.provide(layerCheckpointStore), |
| 114 | Layer.provide(serverConfigLayer), | 114 | Layer.provide(layerServerConfig), |
| 115 | Layer.provide(serverSettingsLayer), | 115 | Layer.provide(layerServerSettings), |
| 116 | Layer.provide(providerInstanceRegistryLayer), | 116 | Layer.provide(layerProviderInstanceRegistry), |
| 117 | Layer.provide(ResetCreditCoordinator.layer), | 117 | Layer.provide(ResetCreditCoordinator.layer), |
| 118 | Layer.provide(backgroundPolicyLayer), | 118 | Layer.provide(layerBackgroundPolicy), |
| 119 | Layer.provide(PlatformTestLayer), | 119 | Layer.provide(layerPlatformTest), |
| 120 | ); | 120 | ); |
| 121 | 121 | ||
| 122 | const waitForIdle = Effect.fn("CursorOrchestratorV2Live.waitForIdle")(function* ( | 122 | const waitForIdle = Effect.fn("CursorOrchestratorV2Live.waitForIdle")(function* ( |
| ... | @@ -234,7 +234,7 @@ describe.runIf(process.env.T3_CURSOR_LIVE_ORCHESTRATOR === "1")( | ... | @@ -234,7 +234,7 @@ describe.runIf(process.env.T3_CURSOR_LIVE_ORCHESTRATOR === "1")( |
| 234 | ); | 234 | ); |
| 235 | assert.include(targetProjection.contextHandoffs[0]?.summaryText ?? "", marker); | 235 | assert.include(targetProjection.contextHandoffs[0]?.summaryText ?? "", marker); |
| 236 | assert.include(assistantText(targetProjection), marker); | 236 | assert.include(assistantText(targetProjection), marker); |
| 237 | }).pipe(Effect.provide(liveLayer), Effect.scoped), | 237 | }).pipe(Effect.provide(layerLive), Effect.scoped), |
| 238 | 360_000, | 238 | 360_000, |
| 239 | ); | 239 | ); |
| 240 | 240 | ||
| ... | @@ -296,7 +296,7 @@ describe.runIf(process.env.T3_CURSOR_LIVE_ORCHESTRATOR === "1")( | ... | @@ -296,7 +296,7 @@ describe.runIf(process.env.T3_CURSOR_LIVE_ORCHESTRATOR === "1")( |
| 296 | supervised.runs.map((run) => run.status), | 296 | supervised.runs.map((run) => run.status), |
| 297 | ["completed"], | 297 | ["completed"], |
| 298 | ); | 298 | ); |
| 299 | }).pipe(Effect.provide(liveLayer), Effect.scoped), | 299 | }).pipe(Effect.provide(layerLive), Effect.scoped), |
| 300 | 360_000, | 300 | 360_000, |
| 301 | ); | 301 | ); |
| 302 | 302 | ||
| ... | @@ -371,7 +371,7 @@ describe.runIf(process.env.T3_CURSOR_LIVE_ORCHESTRATOR === "1")( | ... | @@ -371,7 +371,7 @@ describe.runIf(process.env.T3_CURSOR_LIVE_ORCHESTRATOR === "1")( |
| 371 | `child thread ${subagent.childThreadId} should contain the subagent response`, | 371 | `child thread ${subagent.childThreadId} should contain the subagent response`, |
| 372 | ); | 372 | ); |
| 373 | } | 373 | } |
| 374 | }).pipe(Effect.provide(liveLayer), Effect.scoped), | 374 | }).pipe(Effect.provide(layerLive), Effect.scoped), |
| 375 | 360_000, | 375 | 360_000, |
| 376 | ); | 376 | ); |
| 377 | }, | 377 | }, |
apps/server/src/orchestration-v2/DelegatedCompletionDelivery.test.ts+21-25| ... | @@ -23,7 +23,7 @@ import * as Stream from "effect/Stream"; | ... | @@ -23,7 +23,7 @@ import * as Stream from "effect/Stream"; |
| 23 | import * as CheckpointStore from "../checkpointing/CheckpointStore.ts"; | 23 | import * as CheckpointStore from "../checkpointing/CheckpointStore.ts"; |
| 24 | import * as ServerConfig from "../config.ts"; | 24 | import * as ServerConfig from "../config.ts"; |
| 25 | import * as McpSessionRegistryTestkit from "../mcp/McpSessionRegistry.testkit.ts"; | 25 | import * as McpSessionRegistryTestkit from "../mcp/McpSessionRegistry.testkit.ts"; |
| 26 | import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; | 26 | import * as SqlitePersistence from "../persistence/Layers/Sqlite.ts"; |
| 27 | import * as ProjectEnrichmentService from "../project/ProjectEnrichmentService.ts"; | 27 | import * as ProjectEnrichmentService from "../project/ProjectEnrichmentService.ts"; |
| 28 | import * as ProjectService from "../project/ProjectService.ts"; | 28 | import * as ProjectService from "../project/ProjectService.ts"; |
| 29 | import type { ProviderInstance } from "../provider/ProviderDriver.ts"; | 29 | import type { ProviderInstance } from "../provider/ProviderDriver.ts"; |
| ... | @@ -37,21 +37,17 @@ import * as EventSink from "./EventSink.ts"; | ... | @@ -37,21 +37,17 @@ import * as EventSink from "./EventSink.ts"; |
| 37 | import * as Orchestrator from "./Orchestrator.ts"; | 37 | import * as Orchestrator from "./Orchestrator.ts"; |
| 38 | import type { ProviderAdapterV2Shape } from "./ProviderAdapter.ts"; | 38 | import type { ProviderAdapterV2Shape } from "./ProviderAdapter.ts"; |
| 39 | import { continueRestartedRun } from "./RestartContinuation.ts"; | 39 | import { continueRestartedRun } from "./RestartContinuation.ts"; |
| 40 | import { | 40 | import * as RuntimeLayer from "./runtimeLayer.ts"; |
| 41 | OrchestrationV2EventSinkLayerLive, | 41 | import * as ProviderTurnStartServiceTestkit from "./ProviderTurnStartService.testkit.ts"; |
| 42 | OrchestrationV2LayerLive, | ||
| 43 | ProjectServiceLayerLive, | ||
| 44 | } from "./runtimeLayer.ts"; | ||
| 45 | import { worktreeRepairDependenciesTestLayer } from "./ProviderTurnStartService.testkit.ts"; | ||
| 46 | 42 | ||
| 47 | const PlatformTestLayer = Layer.merge( | 43 | const layerPlatformTest = Layer.merge( |
| 48 | NodeServices.layer, | 44 | NodeServices.layer, |
| 49 | Layer.mock(SourceControlProviderRegistry.SourceControlProviderRegistry)({ | 45 | Layer.mock(SourceControlProviderRegistry.SourceControlProviderRegistry)({ |
| 50 | resolveLink: () => Effect.die("unused title link"), | 46 | resolveLink: () => Effect.die("unused title link"), |
| 51 | }), | 47 | }), |
| 52 | ); | 48 | ); |
| 53 | 49 | ||
| 54 | const ServerConfigLayer = ServerConfig.layerTest(process.cwd(), { | 50 | const layerServerConfig = ServerConfig.layerTest(process.cwd(), { |
| 55 | prefix: "t3-orchestration-v2-delegated-completion-", | 51 | prefix: "t3-orchestration-v2-delegated-completion-", |
| 56 | }); | 52 | }); |
| 57 | 53 | ||
| ... | @@ -60,14 +56,14 @@ const modelSelection = { | ... | @@ -60,14 +56,14 @@ const modelSelection = { |
| 60 | model: "gpt-5.4", | 56 | model: "gpt-5.4", |
| 61 | } satisfies ModelSelection; | 57 | } satisfies ModelSelection; |
| 62 | 58 | ||
| 63 | const VcsDriverRegistryTestLayer = VcsDriverRegistry.layer.pipe( | 59 | const layerVcsDriverRegistryTest = VcsDriverRegistry.layer.pipe( |
| 64 | Layer.provide(VcsProcess.layer), | 60 | Layer.provide(VcsProcess.layer), |
| 65 | Layer.provide(ServerConfigLayer), | 61 | Layer.provide(layerServerConfig), |
| 66 | Layer.provide(PlatformTestLayer), | 62 | Layer.provide(layerPlatformTest), |
| 67 | ); | 63 | ); |
| 68 | 64 | ||
| 69 | const CheckpointStoreTestLayer = CheckpointStore.layer.pipe( | 65 | const layerCheckpointStoreTest = CheckpointStore.layer.pipe( |
| 70 | Layer.provide(VcsDriverRegistryTestLayer), | 66 | Layer.provide(layerVcsDriverRegistryTest), |
| 71 | ); | 67 | ); |
| 72 | 68 | ||
| 73 | const driver = ProviderDriverKind.make("codex"); | 69 | const driver = ProviderDriverKind.make("codex"); |
| ... | @@ -93,7 +89,7 @@ const providerInstance = { | ... | @@ -93,7 +89,7 @@ const providerInstance = { |
| 93 | textGeneration: {} as ProviderInstance["textGeneration"], | 89 | textGeneration: {} as ProviderInstance["textGeneration"], |
| 94 | } satisfies ProviderInstance; | 90 | } satisfies ProviderInstance; |
| 95 | 91 | ||
| 96 | const TestProviderInstanceRegistry = Layer.succeed( | 92 | const layerTestProviderInstanceRegistry = Layer.succeed( |
| 97 | ProviderInstanceRegistry.ProviderInstanceRegistry, | 93 | ProviderInstanceRegistry.ProviderInstanceRegistry, |
| 98 | { | 94 | { |
| 99 | getInstance: (instanceId) => | 95 | getInstance: (instanceId) => |
| ... | @@ -105,14 +101,14 @@ const TestProviderInstanceRegistry = Layer.succeed( | ... | @@ -105,14 +101,14 @@ const TestProviderInstanceRegistry = Layer.succeed( |
| 105 | }, | 101 | }, |
| 106 | ); | 102 | ); |
| 107 | 103 | ||
| 108 | const TestLayer = Layer.mergeAll(OrchestrationV2LayerLive, OrchestrationV2EventSinkLayerLive).pipe( | 104 | const layerTest = Layer.mergeAll(RuntimeLayer.layer, RuntimeLayer.layerEventSink).pipe( |
| 109 | Layer.provideMerge(ProjectServiceLayerLive), | 105 | Layer.provideMerge(RuntimeLayer.layerProjectService), |
| 110 | Layer.provide( | 106 | Layer.provide( |
| 111 | Layer.mock(WorkspacePaths.WorkspacePaths)({ | 107 | Layer.mock(WorkspacePaths.WorkspacePaths)({ |
| 112 | normalizeWorkspaceRoot: (workspaceRoot) => Effect.succeed(workspaceRoot), | 108 | normalizeWorkspaceRoot: (workspaceRoot) => Effect.succeed(workspaceRoot), |
| 113 | }), | 109 | }), |
| 114 | ), | 110 | ), |
| 115 | Layer.provide(worktreeRepairDependenciesTestLayer), | 111 | Layer.provide(ProviderTurnStartServiceTestkit.layer), |
| 116 | Layer.provide( | 112 | Layer.provide( |
| 117 | Layer.succeed(ProjectEnrichmentService.ProjectEnrichmentService, { | 113 | Layer.succeed(ProjectEnrichmentService.ProjectEnrichmentService, { |
| 118 | peek: () => | 114 | peek: () => |
| ... | @@ -133,12 +129,12 @@ const TestLayer = Layer.mergeAll(OrchestrationV2LayerLive, OrchestrationV2EventS | ... | @@ -133,12 +129,12 @@ const TestLayer = Layer.mergeAll(OrchestrationV2LayerLive, OrchestrationV2EventS |
| 133 | }), | 129 | }), |
| 134 | ), | 130 | ), |
| 135 | Layer.provide(McpSessionRegistryTestkit.layer), | 131 | Layer.provide(McpSessionRegistryTestkit.layer), |
| 136 | Layer.provide(SqlitePersistenceMemory), | 132 | Layer.provide(SqlitePersistence.layerMemory), |
| 137 | Layer.provide(CheckpointStoreTestLayer), | 133 | Layer.provide(layerCheckpointStoreTest), |
| 138 | Layer.provide(ServerConfigLayer), | 134 | Layer.provide(layerServerConfig), |
| 139 | Layer.provide(ServerSettings.layerTest()), | 135 | Layer.provide(ServerSettings.layerTest()), |
| 140 | Layer.provide(TestProviderInstanceRegistry), | 136 | Layer.provide(layerTestProviderInstanceRegistry), |
| 141 | Layer.provide(PlatformTestLayer), | 137 | Layer.provide(layerPlatformTest), |
| 142 | ); | 138 | ); |
| 143 | 139 | ||
| 144 | const seedParentWithTerminalTask = (input: { | 140 | const seedParentWithTerminalTask = (input: { |
| ... | @@ -292,7 +288,7 @@ const seedParentWithTerminalTask = (input: { | ... | @@ -292,7 +288,7 @@ const seedParentWithTerminalTask = (input: { |
| 292 | }); | 288 | }); |
| 293 | }); | 289 | }); |
| 294 | 290 | ||
| 295 | it.layer(TestLayer)("delegated completion delivery repairs", (it) => { | 291 | it.layer(layerTest)("delegated completion delivery repairs", (it) => { |
| 296 | it.effect("acceptance batches pending siblings without acknowledging their results", () => | 292 | it.effect("acceptance batches pending siblings without acknowledging their results", () => |
| 297 | Effect.gen(function* () { | 293 | Effect.gen(function* () { |
| 298 | const orchestrator = yield* Orchestrator.OrchestratorV2; | 294 | const orchestrator = yield* Orchestrator.OrchestratorV2; |
| ... | @@ -905,7 +901,7 @@ const seedRestartCancelledChild = (input: { | ... | @@ -905,7 +901,7 @@ const seedRestartCancelledChild = (input: { |
| 905 | return { taskId, childThreadId, childRunId }; | 901 | return { taskId, childThreadId, childRunId }; |
| 906 | }); | 902 | }); |
| 907 | 903 | ||
| 908 | it.layer(TestLayer)("delegated tasks across a server restart", (it) => { | 904 | it.layer(layerTest)("delegated tasks across a server restart", (it) => { |
| 909 | it.effect("holds a restart-cancelled child for its continuation's result", () => | 905 | it.effect("holds a restart-cancelled child for its continuation's result", () => |
| 910 | Effect.gen(function* () { | 906 | Effect.gen(function* () { |
| 911 | const orchestrator = yield* Orchestrator.OrchestratorV2; | 907 | const orchestrator = yield* Orchestrator.OrchestratorV2; |
apps/server/src/orchestration-v2/EffectOutbox.ts+1-1| ... | @@ -690,7 +690,7 @@ export const layer: Layer.Layer<EffectOutboxV2, never, SqlClient.SqlClient> = La | ... | @@ -690,7 +690,7 @@ export const layer: Layer.Layer<EffectOutboxV2, never, SqlClient.SqlClient> = La |
| 690 | ); | 690 | ); |
| 691 | 691 | ||
| 692 | /** Prunes settled effect rows once the server is active, then every hour. */ | 692 | /** Prunes settled effect rows once the server is active, then every hour. */ |
| 693 | export const pruneWorkerLive = Layer.effectDiscard( | 693 | export const layerPruneWorker = Layer.effectDiscard( |
| 694 | Effect.gen(function* () { | 694 | Effect.gen(function* () { |
| 695 | const outbox = yield* EffectOutboxV2; | 695 | const outbox = yield* EffectOutboxV2; |
| 696 | yield* forkParked( | 696 | yield* forkParked( |
apps/server/src/orchestration-v2/EffectWorker.test.ts+36-36| ... | @@ -76,14 +76,14 @@ function restartEffect( | ... | @@ -76,14 +76,14 @@ function restartEffect( |
| 76 | }; | 76 | }; |
| 77 | } | 77 | } |
| 78 | 78 | ||
| 79 | function makeExecutorLayer(input: { | 79 | function layerExecutorFor(input: { |
| 80 | readonly events: Ref.Ref<ReadonlyArray<string>>; | 80 | readonly events: Ref.Ref<ReadonlyArray<string>>; |
| 81 | readonly failFirstStart?: Ref.Ref<boolean>; | 81 | readonly failFirstStart?: Ref.Ref<boolean>; |
| 82 | readonly threads?: Partial<ThreadManagementService.ThreadManagementService["Service"]>; | 82 | readonly threads?: Partial<ThreadManagementService.ThreadManagementService["Service"]>; |
| 83 | readonly continueAfterRestart?: boolean; | 83 | readonly continueAfterRestart?: boolean; |
| 84 | }) { | 84 | }) { |
| 85 | const record = (event: string) => Ref.update(input.events, (events) => [...events, event]); | 85 | const record = (event: string) => Ref.update(input.events, (events) => [...events, event]); |
| 86 | const dependencies = Layer.mergeAll( | 86 | const layerDependencies = Layer.mergeAll( |
| 87 | Layer.succeed( | 87 | Layer.succeed( |
| 88 | ProviderTurnControlService.ProviderTurnControlServiceV2, | 88 | ProviderTurnControlService.ProviderTurnControlServiceV2, |
| 89 | ProviderTurnControlService.ProviderTurnControlServiceV2.of({ | 89 | ProviderTurnControlService.ProviderTurnControlServiceV2.of({ |
| ... | @@ -146,10 +146,10 @@ function makeExecutorLayer(input: { | ... | @@ -146,10 +146,10 @@ function makeExecutorLayer(input: { |
| 146 | }), | 146 | }), |
| 147 | ), | 147 | ), |
| 148 | ); | 148 | ); |
| 149 | return EffectWorker.executorLayer.pipe( | 149 | return EffectWorker.layerExecutor.pipe( |
| 150 | Layer.provide( | 150 | Layer.provide( |
| 151 | Layer.mergeAll( | 151 | Layer.mergeAll( |
| 152 | dependencies, | 152 | layerDependencies, |
| 153 | Layer.mock(ThreadManagementService.ThreadManagementService)(input.threads ?? {}), | 153 | Layer.mock(ThreadManagementService.ThreadManagementService)(input.threads ?? {}), |
| 154 | ServerSettings.layerTest( | 154 | ServerSettings.layerTest( |
| 155 | input.continueAfterRestart === true ? { continueThreadsAfterServerUpdate: true } : {}, | 155 | input.continueAfterRestart === true ? { continueThreadsAfterServerUpdate: true } : {}, |
| ... | @@ -222,7 +222,7 @@ it.effect("requeues a claim when a pre-execution worker check fails", () => | ... | @@ -222,7 +222,7 @@ it.effect("requeues a claim when a pre-execution worker check fails", () => |
| 222 | }> | 222 | }> |
| 223 | >([]); | 223 | >([]); |
| 224 | const executionCount = yield* Ref.make(0); | 224 | const executionCount = yield* Ref.make(0); |
| 225 | const outboxLayer = Layer.mock(EffectOutbox.EffectOutboxV2)({ | 225 | const layerOutbox = Layer.mock(EffectOutbox.EffectOutboxV2)({ |
| 226 | claimNext: () => Effect.succeed(Option.some(claimedEffect)), | 226 | claimNext: () => Effect.succeed(Option.some(claimedEffect)), |
| 227 | get: () => | 227 | get: () => |
| 228 | Effect.fail( | 228 | Effect.fail( |
| ... | @@ -235,19 +235,19 @@ it.effect("requeues a claim when a pre-execution worker check fails", () => | ... | @@ -235,19 +235,19 @@ it.effect("requeues a claim when a pre-execution worker check fails", () => |
| 235 | retry: (input) => | 235 | retry: (input) => |
| 236 | Ref.update(retries, (existing) => [...existing, input]).pipe(Effect.as(true)), | 236 | Ref.update(retries, (existing) => [...existing, input]).pipe(Effect.as(true)), |
| 237 | }); | 237 | }); |
| 238 | const executorLayer = Layer.succeed( | 238 | const layerExecutor = Layer.succeed( |
| 239 | EffectWorker.OrchestrationEffectExecutorV2, | 239 | EffectWorker.OrchestrationEffectExecutorV2, |
| 240 | EffectWorker.OrchestrationEffectExecutorV2.of({ | 240 | EffectWorker.OrchestrationEffectExecutorV2.of({ |
| 241 | execute: () => Ref.update(executionCount, (count) => count + 1), | 241 | execute: () => Ref.update(executionCount, (count) => count + 1), |
| 242 | }), | 242 | }), |
| 243 | ); | 243 | ); |
| 244 | const workerLayer = EffectWorker.layerWithOptions({ workerId }).pipe( | 244 | const layerWorker = EffectWorker.layerWithOptions({ workerId }).pipe( |
| 245 | Layer.provide(Layer.merge(outboxLayer, executorLayer)), | 245 | Layer.provide(Layer.merge(layerOutbox, layerExecutor)), |
| 246 | ); | 246 | ); |
| 247 | 247 | ||
| 248 | const exit = yield* EffectWorker.OrchestrationEffectWorkerV2.pipe( | 248 | const exit = yield* EffectWorker.OrchestrationEffectWorkerV2.pipe( |
| 249 | Effect.flatMap((worker) => worker.runOnce), | 249 | Effect.flatMap((worker) => worker.runOnce), |
| 250 | Effect.provide(workerLayer), | 250 | Effect.provide(layerWorker), |
| 251 | Effect.exit, | 251 | Effect.exit, |
| 252 | ); | 252 | ); |
| 253 | 253 | ||
| ... | @@ -289,7 +289,7 @@ it.effect("arms cancellation before the durable pre-execution check", () => | ... | @@ -289,7 +289,7 @@ it.effect("arms cancellation before the durable pre-execution check", () => |
| 289 | let cancellationArmed = false; | 289 | let cancellationArmed = false; |
| 290 | const executionCount = yield* Ref.make(0); | 290 | const executionCount = yield* Ref.make(0); |
| 291 | const settlementCount = yield* Ref.make(0); | 291 | const settlementCount = yield* Ref.make(0); |
| 292 | const outboxLayer = Layer.mock(EffectOutbox.EffectOutboxV2)({ | 292 | const layerOutbox = Layer.mock(EffectOutbox.EffectOutboxV2)({ |
| 293 | claimNext: () => Effect.succeed(Option.some(claimedEffect)), | 293 | claimNext: () => Effect.succeed(Option.some(claimedEffect)), |
| 294 | awaitCancellation: () => { | 294 | awaitCancellation: () => { |
| 295 | cancellationArmed = true; | 295 | cancellationArmed = true; |
| ... | @@ -308,20 +308,20 @@ it.effect("arms cancellation before the durable pre-execution check", () => | ... | @@ -308,20 +308,20 @@ it.effect("arms cancellation before the durable pre-execution check", () => |
| 308 | clearCancellation: () => Effect.void, | 308 | clearCancellation: () => Effect.void, |
| 309 | succeed: () => Ref.update(settlementCount, (count) => count + 1).pipe(Effect.as(true)), | 309 | succeed: () => Ref.update(settlementCount, (count) => count + 1).pipe(Effect.as(true)), |
| 310 | }); | 310 | }); |
| 311 | const executorLayer = Layer.succeed( | 311 | const layerExecutor = Layer.succeed( |
| 312 | EffectWorker.OrchestrationEffectExecutorV2, | 312 | EffectWorker.OrchestrationEffectExecutorV2, |
| 313 | EffectWorker.OrchestrationEffectExecutorV2.of({ | 313 | EffectWorker.OrchestrationEffectExecutorV2.of({ |
| 314 | execute: () => | 314 | execute: () => |
| 315 | Effect.yieldNow.pipe(Effect.andThen(Ref.update(executionCount, (count) => count + 1))), | 315 | Effect.yieldNow.pipe(Effect.andThen(Ref.update(executionCount, (count) => count + 1))), |
| 316 | }), | 316 | }), |
| 317 | ); | 317 | ); |
| 318 | const workerLayer = EffectWorker.layerWithOptions({ workerId }).pipe( | 318 | const layerWorker = EffectWorker.layerWithOptions({ workerId }).pipe( |
| 319 | Layer.provide(Layer.merge(outboxLayer, executorLayer)), | 319 | Layer.provide(Layer.merge(layerOutbox, layerExecutor)), |
| 320 | ); | 320 | ); |
| 321 | 321 | ||
| 322 | const exit = yield* EffectWorker.OrchestrationEffectWorkerV2.pipe( | 322 | const exit = yield* EffectWorker.OrchestrationEffectWorkerV2.pipe( |
| 323 | Effect.flatMap((worker) => worker.runOnce), | 323 | Effect.flatMap((worker) => worker.runOnce), |
| 324 | Effect.provide(workerLayer), | 324 | Effect.provide(layerWorker), |
| 325 | Effect.exit, | 325 | Effect.exit, |
| 326 | ); | 326 | ); |
| 327 | 327 | ||
| ... | @@ -359,7 +359,7 @@ it.effect("terminalizes a process-bound claim when success settlement fails", () | ... | @@ -359,7 +359,7 @@ it.effect("terminalizes a process-bound claim when success settlement fails", () |
| 359 | const retries = yield* Ref.make(0); | 359 | const retries = yield* Ref.make(0); |
| 360 | const terminalErrors = yield* Ref.make<ReadonlyArray<string>>([]); | 360 | const terminalErrors = yield* Ref.make<ReadonlyArray<string>>([]); |
| 361 | const executionCount = yield* Ref.make(0); | 361 | const executionCount = yield* Ref.make(0); |
| 362 | const outboxLayer = Layer.mock(EffectOutbox.EffectOutboxV2)({ | 362 | const layerOutbox = Layer.mock(EffectOutbox.EffectOutboxV2)({ |
| 363 | claimNext: () => Effect.succeed(Option.some(claimedEffect)), | 363 | claimNext: () => Effect.succeed(Option.some(claimedEffect)), |
| 364 | get: () => Effect.succeed(Option.some(claimedEffect)), | 364 | get: () => Effect.succeed(Option.some(claimedEffect)), |
| 365 | awaitCancellation: () => Effect.never, | 365 | awaitCancellation: () => Effect.never, |
| ... | @@ -376,19 +376,19 @@ it.effect("terminalizes a process-bound claim when success settlement fails", () | ... | @@ -376,19 +376,19 @@ it.effect("terminalizes a process-bound claim when success settlement fails", () |
| 376 | fail: ({ error }) => | 376 | fail: ({ error }) => |
| 377 | Ref.update(terminalErrors, (existing) => [...existing, error]).pipe(Effect.as(true)), | 377 | Ref.update(terminalErrors, (existing) => [...existing, error]).pipe(Effect.as(true)), |
| 378 | }); | 378 | }); |
| 379 | const executorLayer = Layer.succeed( | 379 | const layerExecutor = Layer.succeed( |
| 380 | EffectWorker.OrchestrationEffectExecutorV2, | 380 | EffectWorker.OrchestrationEffectExecutorV2, |
| 381 | EffectWorker.OrchestrationEffectExecutorV2.of({ | 381 | EffectWorker.OrchestrationEffectExecutorV2.of({ |
| 382 | execute: () => Ref.update(executionCount, (count) => count + 1), | 382 | execute: () => Ref.update(executionCount, (count) => count + 1), |
| 383 | }), | 383 | }), |
| 384 | ); | 384 | ); |
| 385 | const workerLayer = EffectWorker.layerWithOptions({ workerId }).pipe( | 385 | const layerWorker = EffectWorker.layerWithOptions({ workerId }).pipe( |
| 386 | Layer.provide(Layer.merge(outboxLayer, executorLayer)), | 386 | Layer.provide(Layer.merge(layerOutbox, layerExecutor)), |
| 387 | ); | 387 | ); |
| 388 | 388 | ||
| 389 | const exit = yield* EffectWorker.OrchestrationEffectWorkerV2.pipe( | 389 | const exit = yield* EffectWorker.OrchestrationEffectWorkerV2.pipe( |
| 390 | Effect.flatMap((worker) => worker.runOnce), | 390 | Effect.flatMap((worker) => worker.runOnce), |
| 391 | Effect.provide(workerLayer), | 391 | Effect.provide(layerWorker), |
| 392 | Effect.exit, | 392 | Effect.exit, |
| 393 | ); | 393 | ); |
| 394 | 394 | ||
| ... | @@ -424,7 +424,7 @@ it.effect("requeues a replay-safe claim when success settlement fails", () => | ... | @@ -424,7 +424,7 @@ it.effect("requeues a replay-safe claim when success settlement fails", () => |
| 424 | }; | 424 | }; |
| 425 | const retries = yield* Ref.make(0); | 425 | const retries = yield* Ref.make(0); |
| 426 | const terminalizations = yield* Ref.make(0); | 426 | const terminalizations = yield* Ref.make(0); |
| 427 | const outboxLayer = Layer.mock(EffectOutbox.EffectOutboxV2)({ | 427 | const layerOutbox = Layer.mock(EffectOutbox.EffectOutboxV2)({ |
| 428 | claimNext: () => Effect.succeed(Option.some(claimedEffect)), | 428 | claimNext: () => Effect.succeed(Option.some(claimedEffect)), |
| 429 | get: () => Effect.succeed(Option.some(claimedEffect)), | 429 | get: () => Effect.succeed(Option.some(claimedEffect)), |
| 430 | awaitCancellation: () => Effect.never, | 430 | awaitCancellation: () => Effect.never, |
| ... | @@ -440,17 +440,17 @@ it.effect("requeues a replay-safe claim when success settlement fails", () => | ... | @@ -440,17 +440,17 @@ it.effect("requeues a replay-safe claim when success settlement fails", () => |
| 440 | retry: () => Ref.update(retries, (count) => count + 1).pipe(Effect.as(true)), | 440 | retry: () => Ref.update(retries, (count) => count + 1).pipe(Effect.as(true)), |
| 441 | fail: () => Ref.update(terminalizations, (count) => count + 1).pipe(Effect.as(true)), | 441 | fail: () => Ref.update(terminalizations, (count) => count + 1).pipe(Effect.as(true)), |
| 442 | }); | 442 | }); |
| 443 | const executorLayer = Layer.succeed( | 443 | const layerExecutor = Layer.succeed( |
| 444 | EffectWorker.OrchestrationEffectExecutorV2, | 444 | EffectWorker.OrchestrationEffectExecutorV2, |
| 445 | EffectWorker.OrchestrationEffectExecutorV2.of({ execute: () => Effect.void }), | 445 | EffectWorker.OrchestrationEffectExecutorV2.of({ execute: () => Effect.void }), |
| 446 | ); | 446 | ); |
| 447 | const workerLayer = EffectWorker.layerWithOptions({ workerId }).pipe( | 447 | const layerWorker = EffectWorker.layerWithOptions({ workerId }).pipe( |
| 448 | Layer.provide(Layer.merge(outboxLayer, executorLayer)), | 448 | Layer.provide(Layer.merge(layerOutbox, layerExecutor)), |
| 449 | ); | 449 | ); |
| 450 | 450 | ||
| 451 | const exit = yield* EffectWorker.OrchestrationEffectWorkerV2.pipe( | 451 | const exit = yield* EffectWorker.OrchestrationEffectWorkerV2.pipe( |
| 452 | Effect.flatMap((worker) => worker.runOnce), | 452 | Effect.flatMap((worker) => worker.runOnce), |
| 453 | Effect.provide(workerLayer), | 453 | Effect.provide(layerWorker), |
| 454 | Effect.exit, | 454 | Effect.exit, |
| 455 | ); | 455 | ); |
| 456 | 456 | ||
| ... | @@ -485,7 +485,7 @@ it.effect("keeps a process-bound executor failure retryable when retry settlemen | ... | @@ -485,7 +485,7 @@ it.effect("keeps a process-bound executor failure retryable when retry settlemen |
| 485 | }; | 485 | }; |
| 486 | const retryAttempts = yield* Ref.make(0); | 486 | const retryAttempts = yield* Ref.make(0); |
| 487 | const terminalizations = yield* Ref.make(0); | 487 | const terminalizations = yield* Ref.make(0); |
| 488 | const outboxLayer = Layer.mock(EffectOutbox.EffectOutboxV2)({ | 488 | const layerOutbox = Layer.mock(EffectOutbox.EffectOutboxV2)({ |
| 489 | claimNext: () => Effect.succeed(Option.some(claimedEffect)), | 489 | claimNext: () => Effect.succeed(Option.some(claimedEffect)), |
| 490 | get: () => Effect.succeed(Option.some(claimedEffect)), | 490 | get: () => Effect.succeed(Option.some(claimedEffect)), |
| 491 | awaitCancellation: () => Effect.never, | 491 | awaitCancellation: () => Effect.never, |
| ... | @@ -506,7 +506,7 @@ it.effect("keeps a process-bound executor failure retryable when retry settlemen | ... | @@ -506,7 +506,7 @@ it.effect("keeps a process-bound executor failure retryable when retry settlemen |
| 506 | ), | 506 | ), |
| 507 | fail: () => Ref.update(terminalizations, (count) => count + 1).pipe(Effect.as(true)), | 507 | fail: () => Ref.update(terminalizations, (count) => count + 1).pipe(Effect.as(true)), |
| 508 | }); | 508 | }); |
| 509 | const executorLayer = Layer.succeed( | 509 | const layerExecutor = Layer.succeed( |
| 510 | EffectWorker.OrchestrationEffectExecutorV2, | 510 | EffectWorker.OrchestrationEffectExecutorV2, |
| 511 | EffectWorker.OrchestrationEffectExecutorV2.of({ | 511 | EffectWorker.OrchestrationEffectExecutorV2.of({ |
| 512 | execute: () => | 512 | execute: () => |
| ... | @@ -519,13 +519,13 @@ it.effect("keeps a process-bound executor failure retryable when retry settlemen | ... | @@ -519,13 +519,13 @@ it.effect("keeps a process-bound executor failure retryable when retry settlemen |
| 519 | ), | 519 | ), |
| 520 | }), | 520 | }), |
| 521 | ); | 521 | ); |
| 522 | const workerLayer = EffectWorker.layerWithOptions({ workerId }).pipe( | 522 | const layerWorker = EffectWorker.layerWithOptions({ workerId }).pipe( |
| 523 | Layer.provide(Layer.merge(outboxLayer, executorLayer)), | 523 | Layer.provide(Layer.merge(layerOutbox, layerExecutor)), |
| 524 | ); | 524 | ); |
| 525 | 525 | ||
| 526 | const exit = yield* EffectWorker.OrchestrationEffectWorkerV2.pipe( | 526 | const exit = yield* EffectWorker.OrchestrationEffectWorkerV2.pipe( |
| 527 | Effect.flatMap((worker) => worker.runOnce), | 527 | Effect.flatMap((worker) => worker.runOnce), |
| 528 | Effect.provide(workerLayer), | 528 | Effect.provide(layerWorker), |
| 529 | Effect.exit, | 529 | Effect.exit, |
| 530 | ); | 530 | ); |
| 531 | 531 | ||
| ... | @@ -557,7 +557,7 @@ it.effect("keeps a max-attempt replay-safe failure terminal when fail settlement | ... | @@ -557,7 +557,7 @@ it.effect("keeps a max-attempt replay-safe failure terminal when fail settlement |
| 557 | }; | 557 | }; |
| 558 | const failAttempts = yield* Ref.make(0); | 558 | const failAttempts = yield* Ref.make(0); |
| 559 | const retries = yield* Ref.make(0); | 559 | const retries = yield* Ref.make(0); |
| 560 | const outboxLayer = Layer.mock(EffectOutbox.EffectOutboxV2)({ | 560 | const layerOutbox = Layer.mock(EffectOutbox.EffectOutboxV2)({ |
| 561 | claimNext: () => Effect.succeed(Option.some(claimedEffect)), | 561 | claimNext: () => Effect.succeed(Option.some(claimedEffect)), |
| 562 | get: () => Effect.succeed(Option.some(claimedEffect)), | 562 | get: () => Effect.succeed(Option.some(claimedEffect)), |
| 563 | awaitCancellation: () => Effect.never, | 563 | awaitCancellation: () => Effect.never, |
| ... | @@ -578,7 +578,7 @@ it.effect("keeps a max-attempt replay-safe failure terminal when fail settlement | ... | @@ -578,7 +578,7 @@ it.effect("keeps a max-attempt replay-safe failure terminal when fail settlement |
| 578 | ), | 578 | ), |
| 579 | retry: () => Ref.update(retries, (count) => count + 1).pipe(Effect.as(true)), | 579 | retry: () => Ref.update(retries, (count) => count + 1).pipe(Effect.as(true)), |
| 580 | }); | 580 | }); |
| 581 | const executorLayer = Layer.succeed( | 581 | const layerExecutor = Layer.succeed( |
| 582 | EffectWorker.OrchestrationEffectExecutorV2, | 582 | EffectWorker.OrchestrationEffectExecutorV2, |
| 583 | EffectWorker.OrchestrationEffectExecutorV2.of({ | 583 | EffectWorker.OrchestrationEffectExecutorV2.of({ |
| 584 | execute: () => | 584 | execute: () => |
| ... | @@ -591,13 +591,13 @@ it.effect("keeps a max-attempt replay-safe failure terminal when fail settlement | ... | @@ -591,13 +591,13 @@ it.effect("keeps a max-attempt replay-safe failure terminal when fail settlement |
| 591 | ), | 591 | ), |
| 592 | }), | 592 | }), |
| 593 | ); | 593 | ); |
| 594 | const workerLayer = EffectWorker.layerWithOptions({ workerId, maxAttempts: 5 }).pipe( | 594 | const layerWorker = EffectWorker.layerWithOptions({ workerId, maxAttempts: 5 }).pipe( |
| 595 | Layer.provide(Layer.merge(outboxLayer, executorLayer)), | 595 | Layer.provide(Layer.merge(layerOutbox, layerExecutor)), |
| 596 | ); | 596 | ); |
| 597 | 597 | ||
| 598 | const exit = yield* EffectWorker.OrchestrationEffectWorkerV2.pipe( | 598 | const exit = yield* EffectWorker.OrchestrationEffectWorkerV2.pipe( |
| 599 | Effect.flatMap((worker) => worker.runOnce), | 599 | Effect.flatMap((worker) => worker.runOnce), |
| 600 | Effect.provide(workerLayer), | 600 | Effect.provide(layerWorker), |
| 601 | Effect.exit, | 601 | Effect.exit, |
| 602 | ); | 602 | ); |
| 603 | 603 | ||
| ... | @@ -734,7 +734,7 @@ it.effect("safely retries after replacement cleanup succeeds and start fails", ( | ... | @@ -734,7 +734,7 @@ it.effect("safely retries after replacement cleanup succeeds and start fails", ( |
| 734 | type: "replace", | 734 | type: "replace", |
| 735 | replacementProviderSessionId: replacementSessionId, | 735 | replacementProviderSessionId: replacementSessionId, |
| 736 | }); | 736 | }); |
| 737 | const layer = makeExecutorLayer({ events, failFirstStart }); | 737 | const layer = layerExecutorFor({ events, failFirstStart }); |
| 738 | 738 | ||
| 739 | const first = yield* Effect.gen(function* () { | 739 | const first = yield* Effect.gen(function* () { |
| 740 | const executor = yield* EffectWorker.OrchestrationEffectExecutorV2; | 740 | const executor = yield* EffectWorker.OrchestrationEffectExecutorV2; |
| ... | @@ -763,7 +763,7 @@ it.effect("settles a delegated child once its restart continuation fails for goo | ... | @@ -763,7 +763,7 @@ it.effect("settles a delegated child once its restart continuation fails for goo |
| 763 | const timestamp = DateTime.formatIso(yield* DateTime.now); | 763 | const timestamp = DateTime.formatIso(yield* DateTime.now); |
| 764 | const events = yield* Ref.make<ReadonlyArray<string>>([]); | 764 | const events = yield* Ref.make<ReadonlyArray<string>>([]); |
| 765 | const recovered = yield* Ref.make<ReadonlyArray<ThreadId>>([]); | 765 | const recovered = yield* Ref.make<ReadonlyArray<ThreadId>>([]); |
| 766 | const layer = makeExecutorLayer({ | 766 | const layer = layerExecutorFor({ |
| 767 | events, | 767 | events, |
| 768 | continueAfterRestart: true, | 768 | continueAfterRestart: true, |
| 769 | threads: { | 769 | threads: { |
apps/server/src/orchestration-v2/EffectWorker.ts+2-2| ... | @@ -78,7 +78,7 @@ export class OrchestrationEffectExecutorV2 extends Context.Service< | ... | @@ -78,7 +78,7 @@ export class OrchestrationEffectExecutorV2 extends Context.Service< |
| 78 | OrchestrationEffectExecutorV2Shape | 78 | OrchestrationEffectExecutorV2Shape |
| 79 | >()("t3/orchestration-v2/EffectWorker/OrchestrationEffectExecutorV2") {} | 79 | >()("t3/orchestration-v2/EffectWorker/OrchestrationEffectExecutorV2") {} |
| 80 | 80 | ||
| 81 | export const executorLayer: Layer.Layer< | 81 | export const layerExecutor: Layer.Layer< |
| 82 | OrchestrationEffectExecutorV2, | 82 | OrchestrationEffectExecutorV2, |
| 83 | never, | 83 | never, |
| 84 | | ProviderSessionManager.ProviderSessionManagerV2 | 84 | | ProviderSessionManager.ProviderSessionManagerV2 |
| ... | @@ -846,6 +846,6 @@ export const runDaemonWithOptions = (options: OrchestrationEffectDaemonOptions = | ... | @@ -846,6 +846,6 @@ export const runDaemonWithOptions = (options: OrchestrationEffectDaemonOptions = |
| 846 | 846 | ||
| 847 | export const runDaemon = runDaemonWithOptions(); | 847 | export const runDaemon = runDaemonWithOptions(); |
| 848 | 848 | ||
| 849 | const daemonLayer: Layer.Layer<never, never, OrchestrationEffectWorkerV2> = Layer.effectDiscard( | 849 | const layerDaemon: Layer.Layer<never, never, OrchestrationEffectWorkerV2> = Layer.effectDiscard( |
| 850 | runDaemon.pipe(Effect.forkScoped), | 850 | runDaemon.pipe(Effect.forkScoped), |
| 851 | ); | 851 | ); |
apps/server/src/orchestration-v2/EventSink.ts+3-3| ... | @@ -190,7 +190,7 @@ export class EventSinkV2 extends Context.Service<EventSinkV2, EventSinkV2Shape>( | ... | @@ -190,7 +190,7 @@ export class EventSinkV2 extends Context.Service<EventSinkV2, EventSinkV2Shape>( |
| 190 | /** | 190 | /** |
| 191 | * IMPLEMENTATIONS | 191 | * IMPLEMENTATIONS |
| 192 | */ | 192 | */ |
| 193 | const baseLayer: Layer.Layer< | 193 | const layerBase: Layer.Layer< |
| 194 | EventSinkV2, | 194 | EventSinkV2, |
| 195 | never, | 195 | never, |
| 196 | | CommandReceiptStore.CommandReceiptStoreV2 | 196 | | CommandReceiptStore.CommandReceiptStoreV2 |
| ... | @@ -873,13 +873,13 @@ const baseLayer: Layer.Layer< | ... | @@ -873,13 +873,13 @@ const baseLayer: Layer.Layer< |
| 873 | * important because enqueue notifications are in-memory wakeups backed by the | 873 | * important because enqueue notifications are in-memory wakeups backed by the |
| 874 | * durable SQL queue. | 874 | * durable SQL queue. |
| 875 | */ | 875 | */ |
| 876 | export const layerFromStores = baseLayer; | 876 | export const layerFromStores = layerBase; |
| 877 | 877 | ||
| 878 | export const layer: Layer.Layer< | 878 | export const layer: Layer.Layer< |
| 879 | EventSinkV2, | 879 | EventSinkV2, |
| 880 | never, | 880 | never, |
| 881 | EventStore.EventStoreV2 | ProjectionStore.ProjectionStoreV2 | SqlClient.SqlClient | 881 | EventStore.EventStoreV2 | ProjectionStore.ProjectionStoreV2 | SqlClient.SqlClient |
| 882 | > = baseLayer.pipe( | 882 | > = layerBase.pipe( |
| 883 | Layer.provide( | 883 | Layer.provide( |
| 884 | Layer.mergeAll( | 884 | Layer.mergeAll( |
| 885 | CommandReceiptStore.layer, | 885 | CommandReceiptStore.layer, |
apps/server/src/orchestration-v2/EventStore.ts+5-5| ... | @@ -13,7 +13,7 @@ import * as Schema from "effect/Schema"; | ... | @@ -13,7 +13,7 @@ import * as Schema from "effect/Schema"; |
| 13 | import * as Stream from "effect/Stream"; | 13 | import * as Stream from "effect/Stream"; |
| 14 | import type * as SqlClient from "effect/sql/SqlClient"; | 14 | import type * as SqlClient from "effect/sql/SqlClient"; |
| 15 | 15 | ||
| 16 | import { OrchestrationEventStoreLive } from "../persistence/Layers/OrchestrationEventStore.ts"; | 16 | import * as OrchestrationEventStoreLayer from "../persistence/Layers/OrchestrationEventStore.ts"; |
| 17 | import * as OrchestrationEventStore from "../persistence/Services/OrchestrationEventStore.ts"; | 17 | import * as OrchestrationEventStore from "../persistence/Services/OrchestrationEventStore.ts"; |
| 18 | 18 | ||
| 19 | export class EventStoreAppendEventsError extends Schema.TaggedError<EventStoreAppendEventsError>()( | 19 | export class EventStoreAppendEventsError extends Schema.TaggedError<EventStoreAppendEventsError>()( |
| ... | @@ -79,7 +79,7 @@ export class EventStoreV2 extends Context.Service<EventStoreV2, EventStoreV2Shap | ... | @@ -79,7 +79,7 @@ export class EventStoreV2 extends Context.Service<EventStoreV2, EventStoreV2Shap |
| 79 | "t3/orchestration-v2/EventStore/EventStoreV2", | 79 | "t3/orchestration-v2/EventStore/EventStoreV2", |
| 80 | ) {} | 80 | ) {} |
| 81 | 81 | ||
| 82 | const baseLayer: Layer.Layer<EventStoreV2, never, OrchestrationEventStore.OrchestrationEventStore> = | 82 | const layerBase: Layer.Layer<EventStoreV2, never, OrchestrationEventStore.OrchestrationEventStore> = |
| 83 | Layer.effect( | 83 | Layer.effect( |
| 84 | EventStoreV2, | 84 | EventStoreV2, |
| 85 | Effect.gen(function* () { | 85 | Effect.gen(function* () { |
| ... | @@ -149,8 +149,8 @@ const baseLayer: Layer.Layer<EventStoreV2, never, OrchestrationEventStore.Orches | ... | @@ -149,8 +149,8 @@ const baseLayer: Layer.Layer<EventStoreV2, never, OrchestrationEventStore.Orches |
| 149 | }), | 149 | }), |
| 150 | ); | 150 | ); |
| 151 | 151 | ||
| 152 | export const layer: Layer.Layer<EventStoreV2, never, SqlClient.SqlClient> = baseLayer.pipe( | 152 | export const layer: Layer.Layer<EventStoreV2, never, SqlClient.SqlClient> = layerBase.pipe( |
| 153 | Layer.provide(OrchestrationEventStoreLive), | 153 | Layer.provide(OrchestrationEventStoreLayer.layer), |
| 154 | ); | 154 | ); |
| 155 | 155 | ||
| 156 | export const layerFromOrchestrationEventStore = baseLayer; | 156 | export const layerFromOrchestrationEventStore = layerBase; |
apps/server/src/orchestration-v2/FoundationPersistence.test.ts+83-75| ... | @@ -43,7 +43,7 @@ import * as SqlClient from "effect/sql/SqlClient"; | ... | @@ -43,7 +43,7 @@ import * as SqlClient from "effect/sql/SqlClient"; |
| 43 | import * as Statement from "effect/sql/Statement"; | 43 | import * as Statement from "effect/sql/Statement"; |
| 44 | 44 | ||
| 45 | import { LIVE_STREAM_MAX_ITEMS, LiveStreamBufferError } from "./LiveStreamBudget.ts"; | 45 | import { LIVE_STREAM_MAX_ITEMS, LiveStreamBufferError } from "./LiveStreamBudget.ts"; |
| 46 | import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; | 46 | import * as SqlitePersistence from "../persistence/Layers/Sqlite.ts"; |
| 47 | import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts"; | 47 | import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts"; |
| 48 | import * as CommandReceiptStore from "./CommandReceiptStore.ts"; | 48 | import * as CommandReceiptStore from "./CommandReceiptStore.ts"; |
| 49 | import * as EffectOutbox from "./EffectOutbox.ts"; | 49 | import * as EffectOutbox from "./EffectOutbox.ts"; |
| ... | @@ -60,25 +60,31 @@ import * as TurnItemPositionStore from "./TurnItemPositionStore.ts"; | ... | @@ -60,25 +60,31 @@ import * as TurnItemPositionStore from "./TurnItemPositionStore.ts"; |
| 60 | const isLiveStreamBufferError = Schema.is(LiveStreamBufferError); | 60 | const isLiveStreamBufferError = Schema.is(LiveStreamBufferError); |
| 61 | const encodeJson = Schema.encodeEffect(Schema.fromJsonString(Schema.Unknown)); | 61 | const encodeJson = Schema.encodeEffect(Schema.fromJsonString(Schema.Unknown)); |
| 62 | 62 | ||
| 63 | const databaseLayer = SqlitePersistenceMemory; | 63 | const layerDatabase = SqlitePersistence.layerMemory; |
| 64 | const eventStoreProvided = EventStore.layer.pipe(Layer.provideMerge(databaseLayer)); | 64 | const layerEventStoreProvided = EventStore.layer.pipe(Layer.provideMerge(layerDatabase)); |
| 65 | const projectionStoreProvided = ProjectionStore.layer.pipe(Layer.provideMerge(databaseLayer)); | 65 | const layerProjectionStoreProvided = ProjectionStore.layer.pipe(Layer.provideMerge(layerDatabase)); |
| 66 | const storesProvided = Layer.mergeAll(databaseLayer, eventStoreProvided, projectionStoreProvided); | 66 | const layerStoresProvided = Layer.mergeAll( |
| 67 | const eventSinkProvided = EventSink.layer.pipe(Layer.provide(storesProvided)); | 67 | layerDatabase, |
| 68 | const effectOutboxProvided = EffectOutbox.layer.pipe(Layer.provide(databaseLayer)); | 68 | layerEventStoreProvided, |
| 69 | const commandReceiptStoreProvided = CommandReceiptStore.layer.pipe(Layer.provide(databaseLayer)); | 69 | layerProjectionStoreProvided, |
| 70 | ); | ||
| 71 | const layerEventSinkProvided = EventSink.layer.pipe(Layer.provide(layerStoresProvided)); | ||
| 72 | const layerEffectOutboxProvided = EffectOutbox.layer.pipe(Layer.provide(layerDatabase)); | ||
| 73 | const layerCommandReceiptStoreProvided = CommandReceiptStore.layer.pipe( | ||
| 74 | Layer.provide(layerDatabase), | ||
| 75 | ); | ||
| 70 | // Its own database, for tests that act on every row in the outbox table. | 76 | // Its own database, for tests that act on every row in the outbox table. |
| 71 | const isolatedOutboxLayer = Layer.fresh(EffectOutbox.layer.pipe(Layer.provideMerge(databaseLayer))); | 77 | const layerIsolatedOutbox = Layer.fresh(EffectOutbox.layer.pipe(Layer.provideMerge(layerDatabase))); |
| 72 | const projectionMaintenanceProvided = ProjectionMaintenance.layer.pipe( | 78 | const layerProjectionMaintenanceProvided = ProjectionMaintenance.layer.pipe( |
| 73 | Layer.provide(storesProvided), | 79 | Layer.provide(layerStoresProvided), |
| 74 | ); | 80 | ); |
| 75 | const TestLayer = Layer.mergeAll( | 81 | const layerTest = Layer.mergeAll( |
| 76 | storesProvided, | 82 | layerStoresProvided, |
| 77 | eventSinkProvided, | 83 | layerEventSinkProvided, |
| 78 | effectOutboxProvided, | 84 | layerEffectOutboxProvided, |
| 79 | commandReceiptStoreProvided, | 85 | layerCommandReceiptStoreProvided, |
| 80 | IdAllocator.layer, | 86 | IdAllocator.layer, |
| 81 | projectionMaintenanceProvided, | 87 | layerProjectionMaintenanceProvided, |
| 82 | ); | 88 | ); |
| 83 | 89 | ||
| 84 | const providerInstanceId = ProviderInstanceId.make("codex"); | 90 | const providerInstanceId = ProviderInstanceId.make("codex"); |
| ... | @@ -156,7 +162,7 @@ it.effect("rebuilds event history one bounded page at a time", () => | ... | @@ -156,7 +162,7 @@ it.effect("rebuilds event history one bounded page at a time", () => |
| 156 | let applied = 0; | 162 | let applied = 0; |
| 157 | let failAfterFirstPage = false; | 163 | let failAfterFirstPage = false; |
| 158 | const appliedAtRead: Array<number> = []; | 164 | const appliedAtRead: Array<number> = []; |
| 159 | const observedStores = Layer.mergeAll( | 165 | const layerObservedStores = Layer.mergeAll( |
| 160 | Layer.succeed(EventStore.EventStoreV2, { | 166 | Layer.succeed(EventStore.EventStoreV2, { |
| 161 | ...eventStore, | 167 | ...eventStore, |
| 162 | read: (input) => | 168 | read: (input) => |
| ... | @@ -186,7 +192,9 @@ it.effect("rebuilds event history one bounded page at a time", () => | ... | @@ -186,7 +192,9 @@ it.effect("rebuilds event history one bounded page at a time", () => |
| 186 | const maintenance = yield* ProjectionMaintenance.ProjectionMaintenanceV2; | 192 | const maintenance = yield* ProjectionMaintenance.ProjectionMaintenanceV2; |
| 187 | return yield* maintenance.rebuild; | 193 | return yield* maintenance.rebuild; |
| 188 | }).pipe( | 194 | }).pipe( |
| 189 | Effect.provide(Layer.fresh(ProjectionMaintenance.layer.pipe(Layer.provide(observedStores)))), | 195 | Effect.provide( |
| 196 | Layer.fresh(ProjectionMaintenance.layer.pipe(Layer.provide(layerObservedStores))), | ||
| 197 | ), | ||
| 190 | ); | 198 | ); |
| 191 | const rebuilt = yield* rebuild; | 199 | const rebuilt = yield* rebuild; |
| 192 | assert.isTrue(rebuilt.valid); | 200 | assert.isTrue(rebuilt.valid); |
| ... | @@ -207,7 +215,7 @@ it.effect("rebuilds event history one bounded page at a time", () => | ... | @@ -207,7 +215,7 @@ it.effect("rebuilds event history one bounded page at a time", () => |
| 207 | ); | 215 | ); |
| 208 | const maintenance = yield* ProjectionMaintenance.ProjectionMaintenanceV2; | 216 | const maintenance = yield* ProjectionMaintenance.ProjectionMaintenanceV2; |
| 209 | assert.isTrue((yield* maintenance.verify).valid); | 217 | assert.isTrue((yield* maintenance.verify).valid); |
| 210 | }).pipe(Effect.provide(TestLayer)), | 218 | }).pipe(Effect.provide(layerTest)), |
| 211 | ); | 219 | ); |
| 212 | 220 | ||
| 213 | it.effect("verifies thread membership using only the thread-created partial index", () => | 221 | it.effect("verifies thread membership using only the thread-created partial index", () => |
| ... | @@ -253,7 +261,7 @@ it.effect("verifies thread membership using only the thread-created partial inde | ... | @@ -253,7 +261,7 @@ it.effect("verifies thread membership using only the thread-created partial inde |
| 253 | const details = plan.map((row) => row.detail).join("\n"); | 261 | const details = plan.map((row) => row.detail).join("\n"); |
| 254 | assert.match(details, /USING (?:COVERING )?INDEX orchestration_events_v2_created_threads_idx/); | 262 | assert.match(details, /USING (?:COVERING )?INDEX orchestration_events_v2_created_threads_idx/); |
| 255 | assert.notMatch(details, /idx_orch_events_stream_sequence|TEMP B-TREE/); | 263 | assert.notMatch(details, /idx_orch_events_stream_sequence|TEMP B-TREE/); |
| 256 | }).pipe(Effect.provide(TestLayer)), | 264 | }).pipe(Effect.provide(layerTest)), |
| 257 | ); | 265 | ); |
| 258 | 266 | ||
| 259 | it.effect("keeps other database work runnable while discovering compaction candidates", () => | 267 | it.effect("keeps other database work runnable while discovering compaction candidates", () => |
| ... | @@ -339,10 +347,10 @@ it.effect("keeps other database work runnable while discovering compaction candi | ... | @@ -339,10 +347,10 @@ it.effect("keeps other database work runnable while discovering compaction candi |
| 339 | assert.isBelow(result.queriesAtProbe, queries[result.table]); | 347 | assert.isBelow(result.queriesAtProbe, queries[result.table]); |
| 340 | } | 348 | } |
| 341 | }), | 349 | }), |
| 342 | ).pipe(Effect.provide(TestLayer)), | 350 | ).pipe(Effect.provide(layerTest)), |
| 343 | ); | 351 | ); |
| 344 | 352 | ||
| 345 | it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { | 353 | it.layer(layerTest)("orchestration V2 foundation persistence", (it) => { |
| 346 | it.effect("projects oversized tool bodies before both replay and live RPC retention", () => | 354 | it.effect("projects oversized tool bodies before both replay and live RPC retention", () => |
| 347 | Effect.scoped( | 355 | Effect.scoped( |
| 348 | Effect.gen(function* () { | 356 | Effect.gen(function* () { |
| ... | @@ -1448,22 +1456,22 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { | ... | @@ -1448,22 +1456,22 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { |
| 1448 | assert.lengthOf(yield* outbox.listByCommandId(commandId), 1); | 1456 | assert.lengthOf(yield* outbox.listByCommandId(commandId), 1); |
| 1449 | 1457 | ||
| 1450 | const executionCount = yield* Ref.make(0); | 1458 | const executionCount = yield* Ref.make(0); |
| 1451 | const executorLayer = Layer.succeed( | 1459 | const layerExecutor = Layer.succeed( |
| 1452 | EffectWorker.OrchestrationEffectExecutorV2, | 1460 | EffectWorker.OrchestrationEffectExecutorV2, |
| 1453 | EffectWorker.OrchestrationEffectExecutorV2.of({ | 1461 | EffectWorker.OrchestrationEffectExecutorV2.of({ |
| 1454 | execute: () => Ref.update(executionCount, (count) => count + 1), | 1462 | execute: () => Ref.update(executionCount, (count) => count + 1), |
| 1455 | }), | 1463 | }), |
| 1456 | ); | 1464 | ); |
| 1457 | const workerLayer = EffectWorker.layerWithOptions({ workerId: "recovery-worker" }).pipe( | 1465 | const layerWorker = EffectWorker.layerWithOptions({ workerId: "recovery-worker" }).pipe( |
| 1458 | Layer.provide( | 1466 | Layer.provide( |
| 1459 | Layer.merge(Layer.succeed(EffectOutbox.EffectOutboxV2, outbox), executorLayer), | 1467 | Layer.merge(Layer.succeed(EffectOutbox.EffectOutboxV2, outbox), layerExecutor), |
| 1460 | ), | 1468 | ), |
| 1461 | ); | 1469 | ); |
| 1462 | yield* Effect.gen(function* () { | 1470 | yield* Effect.gen(function* () { |
| 1463 | const worker = yield* EffectWorker.OrchestrationEffectWorkerV2; | 1471 | const worker = yield* EffectWorker.OrchestrationEffectWorkerV2; |
| 1464 | assert.isTrue(yield* worker.runOnce); | 1472 | assert.isTrue(yield* worker.runOnce); |
| 1465 | assert.isFalse(yield* worker.runOnce); | 1473 | assert.isFalse(yield* worker.runOnce); |
| 1466 | }).pipe(Effect.provide(workerLayer)); | 1474 | }).pipe(Effect.provide(layerWorker)); |
| 1467 | 1475 | ||
| 1468 | assert.equal(yield* Ref.get(executionCount), 1); | 1476 | assert.equal(yield* Ref.get(executionCount), 1); |
| 1469 | const storedEffect = yield* outbox.get(effect.id); | 1477 | const storedEffect = yield* outbox.get(effect.id); |
| ... | @@ -1510,7 +1518,7 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { | ... | @@ -1510,7 +1518,7 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { |
| 1510 | const unexpectedWake = yield* outbox.awaitAvailable.pipe(Effect.forkChild); | 1518 | const unexpectedWake = yield* outbox.awaitAvailable.pipe(Effect.forkChild); |
| 1511 | yield* Effect.yieldNow; | 1519 | yield* Effect.yieldNow; |
| 1512 | assert.isUndefined(unexpectedWake.pollUnsafe()); | 1520 | assert.isUndefined(unexpectedWake.pollUnsafe()); |
| 1513 | }).pipe(Effect.provide(Layer.fresh(TestLayer))), | 1521 | }).pipe(Effect.provide(Layer.fresh(layerTest))), |
| 1514 | ); | 1522 | ); |
| 1515 | 1523 | ||
| 1516 | it.effect("does not publish a stale provider start after an interrupt wins", () => | 1524 | it.effect("does not publish a stale provider start after an interrupt wins", () => |
| ... | @@ -1861,7 +1869,7 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { | ... | @@ -1861,7 +1869,7 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { |
| 1861 | }, | 1869 | }, |
| 1862 | ]); | 1870 | ]); |
| 1863 | 1871 | ||
| 1864 | const executorLayer = Layer.succeed( | 1872 | const layerExecutor = Layer.succeed( |
| 1865 | EffectWorker.OrchestrationEffectExecutorV2, | 1873 | EffectWorker.OrchestrationEffectExecutorV2, |
| 1866 | EffectWorker.OrchestrationEffectExecutorV2.of({ | 1874 | EffectWorker.OrchestrationEffectExecutorV2.of({ |
| 1867 | execute: () => | 1875 | execute: () => |
| ... | @@ -1873,11 +1881,11 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { | ... | @@ -1873,11 +1881,11 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { |
| 1873 | ), | 1881 | ), |
| 1874 | }), | 1882 | }), |
| 1875 | ); | 1883 | ); |
| 1876 | const workerLayer = EffectWorker.layerWithOptions({ | 1884 | const layerWorker = EffectWorker.layerWithOptions({ |
| 1877 | workerId: "cancellation-worker", | 1885 | workerId: "cancellation-worker", |
| 1878 | }).pipe( | 1886 | }).pipe( |
| 1879 | Layer.provide( | 1887 | Layer.provide( |
| 1880 | Layer.merge(Layer.succeed(EffectOutbox.EffectOutboxV2, outbox), executorLayer), | 1888 | Layer.merge(Layer.succeed(EffectOutbox.EffectOutboxV2, outbox), layerExecutor), |
| 1881 | ), | 1889 | ), |
| 1882 | ); | 1890 | ); |
| 1883 | 1891 | ||
| ... | @@ -1894,7 +1902,7 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { | ... | @@ -1894,7 +1902,7 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { |
| 1894 | yield* outbox.signalCancellations(cancelledEffectIds); | 1902 | yield* outbox.signalCancellations(cancelledEffectIds); |
| 1895 | assert.isTrue(yield* Fiber.join(workerFiber)); | 1903 | assert.isTrue(yield* Fiber.join(workerFiber)); |
| 1896 | yield* Deferred.await(interrupted); | 1904 | yield* Deferred.await(interrupted); |
| 1897 | }).pipe(Effect.provide(workerLayer)); | 1905 | }).pipe(Effect.provide(layerWorker)); |
| 1898 | 1906 | ||
| 1899 | const cancelled = yield* outbox.get(effectId); | 1907 | const cancelled = yield* outbox.get(effectId); |
| 1900 | assert.isTrue(Option.isSome(cancelled)); | 1908 | assert.isTrue(Option.isSome(cancelled)); |
| ... | @@ -1923,7 +1931,7 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { | ... | @@ -1923,7 +1931,7 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { |
| 1923 | completedAt: null, | 1931 | completedAt: null, |
| 1924 | lastError: null, | 1932 | lastError: null, |
| 1925 | }; | 1933 | }; |
| 1926 | const outboxLayer = Layer.mock(EffectOutbox.EffectOutboxV2)({ | 1934 | const layerOutbox = Layer.mock(EffectOutbox.EffectOutboxV2)({ |
| 1927 | claimNext: () => Effect.succeed(Option.some(claimedEffect)), | 1935 | claimNext: () => Effect.succeed(Option.some(claimedEffect)), |
| 1928 | awaitCancellation: () => Effect.never, | 1936 | awaitCancellation: () => Effect.never, |
| 1929 | clearCancellation: () => Effect.void, | 1937 | clearCancellation: () => Effect.void, |
| ... | @@ -1939,18 +1947,18 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { | ... | @@ -1939,18 +1947,18 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { |
| 1939 | }), | 1947 | }), |
| 1940 | ), | 1948 | ), |
| 1941 | }); | 1949 | }); |
| 1942 | const executorLayer = Layer.succeed( | 1950 | const layerExecutor = Layer.succeed( |
| 1943 | EffectWorker.OrchestrationEffectExecutorV2, | 1951 | EffectWorker.OrchestrationEffectExecutorV2, |
| 1944 | EffectWorker.OrchestrationEffectExecutorV2.of({ execute: () => Effect.void }), | 1952 | EffectWorker.OrchestrationEffectExecutorV2.of({ execute: () => Effect.void }), |
| 1945 | ); | 1953 | ); |
| 1946 | const workerLayer = EffectWorker.layerWithOptions({ | 1954 | const layerWorker = EffectWorker.layerWithOptions({ |
| 1947 | workerId: "settlement-race-worker", | 1955 | workerId: "settlement-race-worker", |
| 1948 | }).pipe(Layer.provide(Layer.merge(outboxLayer, executorLayer))); | 1956 | }).pipe(Layer.provide(Layer.merge(layerOutbox, layerExecutor))); |
| 1949 | 1957 | ||
| 1950 | assert.isTrue( | 1958 | assert.isTrue( |
| 1951 | yield* EffectWorker.OrchestrationEffectWorkerV2.pipe( | 1959 | yield* EffectWorker.OrchestrationEffectWorkerV2.pipe( |
| 1952 | Effect.flatMap((worker) => worker.runOnce), | 1960 | Effect.flatMap((worker) => worker.runOnce), |
| 1953 | Effect.provide(workerLayer), | 1961 | Effect.provide(layerWorker), |
| 1954 | ), | 1962 | ), |
| 1955 | ); | 1963 | ); |
| 1956 | }), | 1964 | }), |
| ... | @@ -1978,7 +1986,7 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { | ... | @@ -1978,7 +1986,7 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { |
| 1978 | lastError: null, | 1986 | lastError: null, |
| 1979 | }; | 1987 | }; |
| 1980 | const executionCount = yield* Ref.make(0); | 1988 | const executionCount = yield* Ref.make(0); |
| 1981 | const outboxLayer = Layer.mock(EffectOutbox.EffectOutboxV2)({ | 1989 | const layerOutbox = Layer.mock(EffectOutbox.EffectOutboxV2)({ |
| 1982 | claimNext: () => Effect.succeed(Option.some(claimedEffect)), | 1990 | claimNext: () => Effect.succeed(Option.some(claimedEffect)), |
| 1983 | get: () => | 1991 | get: () => |
| 1984 | Effect.succeed( | 1992 | Effect.succeed( |
| ... | @@ -1993,20 +2001,20 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { | ... | @@ -1993,20 +2001,20 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { |
| 1993 | clearCancellation: () => Effect.void, | 2001 | clearCancellation: () => Effect.void, |
| 1994 | awaitCancellation: () => Effect.never, | 2002 | awaitCancellation: () => Effect.never, |
| 1995 | }); | 2003 | }); |
| 1996 | const executorLayer = Layer.succeed( | 2004 | const layerExecutor = Layer.succeed( |
| 1997 | EffectWorker.OrchestrationEffectExecutorV2, | 2005 | EffectWorker.OrchestrationEffectExecutorV2, |
| 1998 | EffectWorker.OrchestrationEffectExecutorV2.of({ | 2006 | EffectWorker.OrchestrationEffectExecutorV2.of({ |
| 1999 | execute: () => Ref.update(executionCount, (count) => count + 1), | 2007 | execute: () => Ref.update(executionCount, (count) => count + 1), |
| 2000 | }), | 2008 | }), |
| 2001 | ); | 2009 | ); |
| 2002 | const workerLayer = EffectWorker.layerWithOptions({ | 2010 | const layerWorker = EffectWorker.layerWithOptions({ |
| 2003 | workerId: "claim-cancellation-worker", | 2011 | workerId: "claim-cancellation-worker", |
| 2004 | }).pipe(Layer.provide(Layer.merge(outboxLayer, executorLayer))); | 2012 | }).pipe(Layer.provide(Layer.merge(layerOutbox, layerExecutor))); |
| 2005 | 2013 | ||
| 2006 | assert.isTrue( | 2014 | assert.isTrue( |
| 2007 | yield* EffectWorker.OrchestrationEffectWorkerV2.pipe( | 2015 | yield* EffectWorker.OrchestrationEffectWorkerV2.pipe( |
| 2008 | Effect.flatMap((worker) => worker.runOnce), | 2016 | Effect.flatMap((worker) => worker.runOnce), |
| 2009 | Effect.provide(workerLayer), | 2017 | Effect.provide(layerWorker), |
| 2010 | ), | 2018 | ), |
| 2011 | ); | 2019 | ); |
| 2012 | assert.equal(yield* Ref.get(executionCount), 0); | 2020 | assert.equal(yield* Ref.get(executionCount), 0); |
| ... | @@ -2275,7 +2283,7 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { | ... | @@ -2275,7 +2283,7 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { |
| 2275 | ["failed-old:1", "pending:1", "running:1", "succeeded-recent:1"], | 2283 | ["failed-old:1", "pending:1", "running:1", "succeeded-recent:1"], |
| 2276 | ); | 2284 | ); |
| 2277 | assert.equal(yield* outbox.pruneSettled, 0); | 2285 | assert.equal(yield* outbox.pruneSettled, 0); |
| 2278 | }).pipe(Effect.provide(isolatedOutboxLayer)), | 2286 | }).pipe(Effect.provide(layerIsolatedOutbox)), |
| 2279 | ); | 2287 | ); |
| 2280 | 2288 | ||
| 2281 | it.effect("prunes settled effects hourly from the layer-owned worker", () => | 2289 | it.effect("prunes settled effects hourly from the layer-owned worker", () => |
| ... | @@ -2303,7 +2311,7 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { | ... | @@ -2303,7 +2311,7 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { |
| 2303 | Duration.subtract(EffectOutbox.SETTLED_EFFECT_RETENTION, Duration.minutes(30)), | 2311 | Duration.subtract(EffectOutbox.SETTLED_EFFECT_RETENTION, Duration.minutes(30)), |
| 2304 | ); | 2312 | ); |
| 2305 | yield* Layer.build( | 2313 | yield* Layer.build( |
| 2306 | EffectOutbox.pruneWorkerLive.pipe( | 2314 | EffectOutbox.layerPruneWorker.pipe( |
| 2307 | Layer.provide(Layer.succeed(EffectOutbox.EffectOutboxV2, observed)), | 2315 | Layer.provide(Layer.succeed(EffectOutbox.EffectOutboxV2, observed)), |
| 2308 | ), | 2316 | ), |
| 2309 | ); | 2317 | ); |
| ... | @@ -2313,7 +2321,7 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { | ... | @@ -2313,7 +2321,7 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { |
| 2313 | yield* TestClock.adjust("1 hour"); | 2321 | yield* TestClock.adjust("1 hour"); |
| 2314 | assert.equal(yield* Queue.take(runs), 1); | 2322 | assert.equal(yield* Queue.take(runs), 1); |
| 2315 | assert.deepEqual(yield* ids, ["effect:prune-worker:pending"]); | 2323 | assert.deepEqual(yield* ids, ["effect:prune-worker:pending"]); |
| 2316 | }).pipe(Effect.scoped, Effect.provide(isolatedOutboxLayer)), | 2324 | }).pipe(Effect.scoped, Effect.provide(layerIsolatedOutbox)), |
| 2317 | ); | 2325 | ); |
| 2318 | 2326 | ||
| 2319 | it.effect("does not emit a SQL span for an empty safety claim", () => | 2327 | it.effect("does not emit a SQL span for an empty safety claim", () => |
| ... | @@ -2338,7 +2346,7 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { | ... | @@ -2338,7 +2346,7 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { |
| 2338 | 2346 | ||
| 2339 | assert.isTrue(Option.isNone(claim)); | 2347 | assert.isTrue(Option.isNone(claim)); |
| 2340 | assert.notInclude(spans, "sql.execute"); | 2348 | assert.notInclude(spans, "sql.execute"); |
| 2341 | }).pipe(Effect.provide(Layer.fresh(effectOutboxProvided))), | 2349 | }).pipe(Effect.provide(Layer.fresh(layerEffectOutboxProvided))), |
| 2342 | ); | 2350 | ); |
| 2343 | 2351 | ||
| 2344 | it.effect("wakes claimers when cancellation unblocks same-thread work", () => | 2352 | it.effect("wakes claimers when cancellation unblocks same-thread work", () => |
| ... | @@ -2395,7 +2403,7 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { | ... | @@ -2395,7 +2403,7 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { |
| 2395 | workerId: "cancellation-wakeup-worker", | 2403 | workerId: "cancellation-wakeup-worker", |
| 2396 | }); | 2404 | }); |
| 2397 | } | 2405 | } |
| 2398 | }).pipe(Effect.provide(Layer.fresh(effectOutboxProvided))), | 2406 | }).pipe(Effect.provide(Layer.fresh(layerEffectOutboxProvided))), |
| 2399 | ); | 2407 | ); |
| 2400 | 2408 | ||
| 2401 | it.effect("keeps later thread effects behind an earlier effect waiting to retry", () => | 2409 | it.effect("keeps later thread effects behind an earlier effect waiting to retry", () => |
| ... | @@ -2465,7 +2473,7 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { | ... | @@ -2465,7 +2473,7 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { |
| 2465 | assert.equal(Option.getOrUndefined(unblocked)?.id, "effect:foundation-retry-order:a-start"); | 2473 | assert.equal(Option.getOrUndefined(unblocked)?.id, "effect:foundation-retry-order:a-start"); |
| 2466 | yield* outbox.succeed({ effectId: "effect:foundation-retry-order:a-start", workerId }); | 2474 | yield* outbox.succeed({ effectId: "effect:foundation-retry-order:a-start", workerId }); |
| 2467 | yield* outbox.succeed({ effectId: "effect:foundation-retry-order:b-title", workerId }); | 2475 | yield* outbox.succeed({ effectId: "effect:foundation-retry-order:b-title", workerId }); |
| 2468 | }).pipe(Effect.provide(Layer.fresh(effectOutboxProvided))), | 2476 | }).pipe(Effect.provide(Layer.fresh(layerEffectOutboxProvided))), |
| 2469 | ); | 2477 | ); |
| 2470 | 2478 | ||
| 2471 | it.effect("executes a retry at its durable deadline instead of the liveness interval", () => | 2479 | it.effect("executes a retry at its durable deadline instead of the liveness interval", () => |
| ... | @@ -2476,7 +2484,7 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { | ... | @@ -2476,7 +2484,7 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { |
| 2476 | const threadId = ThreadId.make("thread:foundation-durable-retry-deadline"); | 2484 | const threadId = ThreadId.make("thread:foundation-durable-retry-deadline"); |
| 2477 | const executions = yield* Ref.make(0); | 2485 | const executions = yield* Ref.make(0); |
| 2478 | const completed = yield* Deferred.make<void>(); | 2486 | const completed = yield* Deferred.make<void>(); |
| 2479 | const executorLayer = Layer.succeed( | 2487 | const layerExecutor = Layer.succeed( |
| 2480 | EffectWorker.OrchestrationEffectExecutorV2, | 2488 | EffectWorker.OrchestrationEffectExecutorV2, |
| 2481 | EffectWorker.OrchestrationEffectExecutorV2.of({ | 2489 | EffectWorker.OrchestrationEffectExecutorV2.of({ |
| 2482 | execute: () => | 2490 | execute: () => |
| ... | @@ -2493,11 +2501,11 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { | ... | @@ -2493,11 +2501,11 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { |
| 2493 | }), | 2501 | }), |
| 2494 | }), | 2502 | }), |
| 2495 | ); | 2503 | ); |
| 2496 | const workerLayer = EffectWorker.layerWithOptions({ | 2504 | const layerWorker = EffectWorker.layerWithOptions({ |
| 2497 | workerId: "durable-retry-deadline-worker", | 2505 | workerId: "durable-retry-deadline-worker", |
| 2498 | }).pipe( | 2506 | }).pipe( |
| 2499 | Layer.provide( | 2507 | Layer.provide( |
| 2500 | Layer.merge(Layer.succeed(EffectOutbox.EffectOutboxV2, outbox), executorLayer), | 2508 | Layer.merge(Layer.succeed(EffectOutbox.EffectOutboxV2, outbox), layerExecutor), |
| 2501 | ), | 2509 | ), |
| 2502 | ); | 2510 | ); |
| 2503 | 2511 | ||
| ... | @@ -2531,7 +2539,7 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { | ... | @@ -2531,7 +2539,7 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { |
| 2531 | yield* TestClock.adjust("1 millis"); | 2539 | yield* TestClock.adjust("1 millis"); |
| 2532 | yield* Deferred.await(completed); | 2540 | yield* Deferred.await(completed); |
| 2533 | assert.equal(yield* Ref.get(executions), 2); | 2541 | assert.equal(yield* Ref.get(executions), 2); |
| 2534 | }).pipe(Effect.provide(workerLayer), Effect.scoped); | 2542 | }).pipe(Effect.provide(layerWorker), Effect.scoped); |
| 2535 | }).pipe(Effect.provide(TestClock.layer())), | 2543 | }).pipe(Effect.provide(TestClock.layer())), |
| 2536 | ); | 2544 | ); |
| 2537 | 2545 | ||
| ... | @@ -2555,7 +2563,7 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { | ... | @@ -2555,7 +2563,7 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { |
| 2555 | [effectA2, { started: startedA2, release: releaseA2 }], | 2563 | [effectA2, { started: startedA2, release: releaseA2 }], |
| 2556 | [effectB1, { started: startedB1, release: releaseB1 }], | 2564 | [effectB1, { started: startedB1, release: releaseB1 }], |
| 2557 | ]); | 2565 | ]); |
| 2558 | const executorLayer = Layer.succeed( | 2566 | const layerExecutor = Layer.succeed( |
| 2559 | EffectWorker.OrchestrationEffectExecutorV2, | 2567 | EffectWorker.OrchestrationEffectExecutorV2, |
| 2560 | EffectWorker.OrchestrationEffectExecutorV2.of({ | 2568 | EffectWorker.OrchestrationEffectExecutorV2.of({ |
| 2561 | execute: (effect) => { | 2569 | execute: (effect) => { |
| ... | @@ -2567,11 +2575,11 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { | ... | @@ -2567,11 +2575,11 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { |
| 2567 | }, | 2575 | }, |
| 2568 | }), | 2576 | }), |
| 2569 | ); | 2577 | ); |
| 2570 | const workerLayer = EffectWorker.layerWithOptions({ | 2578 | const layerWorker = EffectWorker.layerWithOptions({ |
| 2571 | workerId: "concurrency-worker", | 2579 | workerId: "concurrency-worker", |
| 2572 | }).pipe( | 2580 | }).pipe( |
| 2573 | Layer.provide( | 2581 | Layer.provide( |
| 2574 | Layer.merge(Layer.succeed(EffectOutbox.EffectOutboxV2, outbox), executorLayer), | 2582 | Layer.merge(Layer.succeed(EffectOutbox.EffectOutboxV2, outbox), layerExecutor), |
| 2575 | ), | 2583 | ), |
| 2576 | ); | 2584 | ); |
| 2577 | 2585 | ||
| ... | @@ -2625,7 +2633,7 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { | ... | @@ -2625,7 +2633,7 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { |
| 2625 | ); | 2633 | ); |
| 2626 | if (!settled) yield* Effect.yieldNow; | 2634 | if (!settled) yield* Effect.yieldNow; |
| 2627 | } | 2635 | } |
| 2628 | }).pipe(Effect.provide(workerLayer), Effect.scoped); | 2636 | }).pipe(Effect.provide(layerWorker), Effect.scoped); |
| 2629 | }), | 2637 | }), |
| 2630 | ); | 2638 | ); |
| 2631 | 2639 | ||
| ... | @@ -2655,7 +2663,7 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { | ... | @@ -2655,7 +2663,7 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { |
| 2655 | }, | 2663 | }, |
| 2656 | ]); | 2664 | ]); |
| 2657 | 2665 | ||
| 2658 | const executorLayer = Layer.succeed( | 2666 | const layerExecutor = Layer.succeed( |
| 2659 | EffectWorker.OrchestrationEffectExecutorV2, | 2667 | EffectWorker.OrchestrationEffectExecutorV2, |
| 2660 | EffectWorker.OrchestrationEffectExecutorV2.of({ | 2668 | EffectWorker.OrchestrationEffectExecutorV2.of({ |
| 2661 | execute: (effect) => | 2669 | execute: (effect) => |
| ... | @@ -2670,12 +2678,12 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { | ... | @@ -2670,12 +2678,12 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { |
| 2670 | ), | 2678 | ), |
| 2671 | }), | 2679 | }), |
| 2672 | ); | 2680 | ); |
| 2673 | const workerLayer = EffectWorker.layerWithOptions({ | 2681 | const layerWorker = EffectWorker.layerWithOptions({ |
| 2674 | workerId: "no-live-reclaim-worker", | 2682 | workerId: "no-live-reclaim-worker", |
| 2675 | leaseDurationMs: 1, | 2683 | leaseDurationMs: 1, |
| 2676 | }).pipe( | 2684 | }).pipe( |
| 2677 | Layer.provide( | 2685 | Layer.provide( |
| 2678 | Layer.merge(Layer.succeed(EffectOutbox.EffectOutboxV2, outbox), executorLayer), | 2686 | Layer.merge(Layer.succeed(EffectOutbox.EffectOutboxV2, outbox), layerExecutor), |
| 2679 | ), | 2687 | ), |
| 2680 | ); | 2688 | ); |
| 2681 | 2689 | ||
| ... | @@ -2696,7 +2704,7 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { | ... | @@ -2696,7 +2704,7 @@ it.layer(TestLayer)("orchestration V2 foundation persistence", (it) => { |
| 2696 | assert.isTrue(yield* Fiber.join(firstFiber)); | 2704 | assert.isTrue(yield* Fiber.join(firstFiber)); |
| 2697 | assert.isTrue(yield* worker.runOnce); | 2705 | assert.isTrue(yield* worker.runOnce); |
| 2698 | assert.deepEqual(yield* Ref.get(executions), [firstEffectId, secondEffectId]); | 2706 | assert.deepEqual(yield* Ref.get(executions), [firstEffectId, secondEffectId]); |
| 2699 | }).pipe(Effect.provide(workerLayer)); | 2707 | }).pipe(Effect.provide(layerWorker)); |
| 2700 | }), | 2708 | }), |
| 2701 | ); | 2709 | ); |
| 2702 | 2710 | ||
| ... | @@ -3348,7 +3356,7 @@ it.live("keeps claiming new work after repeated idle periods", () => | ... | @@ -3348,7 +3356,7 @@ it.live("keeps claiming new work after repeated idle periods", () => |
| 3348 | Effect.gen(function* () { | 3356 | Effect.gen(function* () { |
| 3349 | const outbox = yield* EffectOutbox.EffectOutboxV2; | 3357 | const outbox = yield* EffectOutbox.EffectOutboxV2; |
| 3350 | const completed = new Map<string, Deferred.Deferred<void>>(); | 3358 | const completed = new Map<string, Deferred.Deferred<void>>(); |
| 3351 | const executorLayer = Layer.succeed( | 3359 | const layerExecutor = Layer.succeed( |
| 3352 | EffectWorker.OrchestrationEffectExecutorV2, | 3360 | EffectWorker.OrchestrationEffectExecutorV2, |
| 3353 | EffectWorker.OrchestrationEffectExecutorV2.of({ | 3361 | EffectWorker.OrchestrationEffectExecutorV2.of({ |
| 3354 | execute: (effect) => { | 3362 | execute: (effect) => { |
| ... | @@ -3359,10 +3367,10 @@ it.live("keeps claiming new work after repeated idle periods", () => | ... | @@ -3359,10 +3367,10 @@ it.live("keeps claiming new work after repeated idle periods", () => |
| 3359 | }, | 3367 | }, |
| 3360 | }), | 3368 | }), |
| 3361 | ); | 3369 | ); |
| 3362 | const workerLayer = EffectWorker.layerWithOptions({ | 3370 | const layerWorker = EffectWorker.layerWithOptions({ |
| 3363 | workerId: "idle-wave-worker", | 3371 | workerId: "idle-wave-worker", |
| 3364 | }).pipe( | 3372 | }).pipe( |
| 3365 | Layer.provide(Layer.merge(Layer.succeed(EffectOutbox.EffectOutboxV2, outbox), executorLayer)), | 3373 | Layer.provide(Layer.merge(Layer.succeed(EffectOutbox.EffectOutboxV2, outbox), layerExecutor)), |
| 3366 | ); | 3374 | ); |
| 3367 | 3375 | ||
| 3368 | yield* Effect.gen(function* () { | 3376 | yield* Effect.gen(function* () { |
| ... | @@ -3384,8 +3392,8 @@ it.live("keeps claiming new work after repeated idle periods", () => | ... | @@ -3384,8 +3392,8 @@ it.live("keeps claiming new work after repeated idle periods", () => |
| 3384 | const observed = yield* Deferred.await(completion).pipe(Effect.timeoutOption("2 seconds")); | 3392 | const observed = yield* Deferred.await(completion).pipe(Effect.timeoutOption("2 seconds")); |
| 3385 | assert.isTrue(Option.isSome(observed), `worker stopped before idle wave ${wave}`); | 3393 | assert.isTrue(Option.isSome(observed), `worker stopped before idle wave ${wave}`); |
| 3386 | } | 3394 | } |
| 3387 | }).pipe(Effect.provide(workerLayer), Effect.scoped); | 3395 | }).pipe(Effect.provide(layerWorker), Effect.scoped); |
| 3388 | }).pipe(Effect.provide(TestLayer)), | 3396 | }).pipe(Effect.provide(layerTest)), |
| 3389 | ); | 3397 | ); |
| 3390 | 3398 | ||
| 3391 | it.effect("publishes live events in commit order across concurrent writers", () => | 3399 | it.effect("publishes live events in commit order across concurrent writers", () => |
| ... | @@ -3394,7 +3402,7 @@ it.effect("publishes live events in commit order across concurrent writers", () | ... | @@ -3394,7 +3402,7 @@ it.effect("publishes live events in commit order across concurrent writers", () |
| 3394 | const releaseFirst = yield* Deferred.make<void>(); | 3402 | const releaseFirst = yield* Deferred.make<void>(); |
| 3395 | // The first writer's post-commit wakeup stands in for any scheduler yield | 3403 | // The first writer's post-commit wakeup stands in for any scheduler yield |
| 3396 | // between its commit and its publish. | 3404 | // between its commit and its publish. |
| 3397 | const pausingOutbox = Layer.effect( | 3405 | const layerPausingOutbox = Layer.effect( |
| 3398 | EffectOutbox.EffectOutboxV2, | 3406 | EffectOutbox.EffectOutboxV2, |
| 3399 | Effect.gen(function* () { | 3407 | Effect.gen(function* () { |
| 3400 | const delegate = yield* EffectOutbox.EffectOutboxV2; | 3408 | const delegate = yield* EffectOutbox.EffectOutboxV2; |
| ... | @@ -3407,15 +3415,15 @@ it.effect("publishes live events in commit order across concurrent writers", () | ... | @@ -3407,15 +3415,15 @@ it.effect("publishes live events in commit order across concurrent writers", () |
| 3407 | ), | 3415 | ), |
| 3408 | }); | 3416 | }); |
| 3409 | }), | 3417 | }), |
| 3410 | ).pipe(Layer.provide(effectOutboxProvided)); | 3418 | ).pipe(Layer.provide(layerEffectOutboxProvided)); |
| 3411 | const eventSinkLayer = EventSink.layerFromStores.pipe( | 3419 | const layerEventSink = EventSink.layerFromStores.pipe( |
| 3412 | Layer.provide( | 3420 | Layer.provide( |
| 3413 | Layer.mergeAll( | 3421 | Layer.mergeAll( |
| 3414 | storesProvided, | 3422 | layerStoresProvided, |
| 3415 | pausingOutbox, | 3423 | layerPausingOutbox, |
| 3416 | commandReceiptStoreProvided, | 3424 | layerCommandReceiptStoreProvided, |
| 3417 | ProjectStore.layer.pipe(Layer.provide(databaseLayer)), | 3425 | ProjectStore.layer.pipe(Layer.provide(layerDatabase)), |
| 3418 | TurnItemPositionStore.layer.pipe(Layer.provide(databaseLayer)), | 3426 | TurnItemPositionStore.layer.pipe(Layer.provide(layerDatabase)), |
| 3419 | ), | 3427 | ), |
| 3420 | ), | 3428 | ), |
| 3421 | ); | 3429 | ); |
| ... | @@ -3471,6 +3479,6 @@ it.effect("publishes live events in commit order across concurrent writers", () | ... | @@ -3471,6 +3479,6 @@ it.effect("publishes live events in commit order across concurrent writers", () |
| 3471 | sequences, | 3479 | sequences, |
| 3472 | [...sequences].sort((left, right) => left - right), | 3480 | [...sequences].sort((left, right) => left - right), |
| 3473 | ); | 3481 | ); |
| 3474 | }).pipe(Effect.provide(eventSinkLayer)); | 3482 | }).pipe(Effect.provide(layerEventSink)); |
| 3475 | }).pipe(Effect.provide(databaseLayer)), | 3483 | }).pipe(Effect.provide(layerDatabase)), |
| 3476 | ); | 3484 | ); |
apps/server/src/orchestration-v2/GrokOrchestratorV2.live.test.ts+32-32| ... | @@ -21,12 +21,12 @@ import * as CheckpointStore from "../checkpointing/CheckpointStore.ts"; | ... | @@ -21,12 +21,12 @@ import * as CheckpointStore from "../checkpointing/CheckpointStore.ts"; |
| 21 | import * as BackgroundPolicy from "../background/BackgroundPolicy.ts"; | 21 | import * as BackgroundPolicy from "../background/BackgroundPolicy.ts"; |
| 22 | import * as HostPowerMonitor from "../background/HostPowerMonitor.ts"; | 22 | import * as HostPowerMonitor from "../background/HostPowerMonitor.ts"; |
| 23 | import * as ServerConfig from "../config.ts"; | 23 | import * as ServerConfig from "../config.ts"; |
| 24 | import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; | 24 | import * as SqlitePersistence from "../persistence/Layers/Sqlite.ts"; |
| 25 | import * as AntigravityInstallation from "../provider/AntigravityInstallation.ts"; | 25 | import * as AntigravityInstallation from "../provider/AntigravityInstallation.ts"; |
| 26 | import * as CodexInstallation from "../provider/CodexInstallation.ts"; | 26 | import * as CodexInstallation from "../provider/CodexInstallation.ts"; |
| 27 | import * as ServerEnvironment from "../environment/ServerEnvironment.ts"; | 27 | import * as ServerEnvironment from "../environment/ServerEnvironment.ts"; |
| 28 | import * as ModelManifest from "../provider/ModelManifest.ts"; | 28 | import * as ModelManifest from "../provider/ModelManifest.ts"; |
| 29 | import { ProviderInstanceRegistryHydrationLive } from "../provider/Layers/ProviderInstanceRegistryHydration.ts"; | 29 | import * as ProviderInstanceRegistryHydration from "../provider/Layers/ProviderInstanceRegistryHydration.ts"; |
| 30 | import * as ProviderEventLoggers from "../provider/Layers/ProviderEventLoggers.ts"; | 30 | import * as ProviderEventLoggers from "../provider/Layers/ProviderEventLoggers.ts"; |
| 31 | import * as OpenCodeRuntime from "../provider/opencodeRuntime.ts"; | 31 | import * as OpenCodeRuntime from "../provider/opencodeRuntime.ts"; |
| 32 | import * as OpenCodeServerLedger from "../provider/OpenCodeServerLedger.ts"; | 32 | import * as OpenCodeServerLedger from "../provider/OpenCodeServerLedger.ts"; |
| ... | @@ -34,53 +34,53 @@ import * as ServerSettings from "../serverSettings.ts"; | ... | @@ -34,53 +34,53 @@ import * as ServerSettings from "../serverSettings.ts"; |
| 34 | import * as VcsDriverRegistry from "../vcs/VcsDriverRegistry.ts"; | 34 | import * as VcsDriverRegistry from "../vcs/VcsDriverRegistry.ts"; |
| 35 | import * as VcsProcess from "../vcs/VcsProcess.ts"; | 35 | import * as VcsProcess from "../vcs/VcsProcess.ts"; |
| 36 | import * as Orchestrator from "./Orchestrator.ts"; | 36 | import * as Orchestrator from "./Orchestrator.ts"; |
| 37 | import { worktreeRepairDependenciesTestLayer } from "./ProviderTurnStartService.testkit.ts"; | 37 | import * as ProviderTurnStartServiceTestkit from "./ProviderTurnStartService.testkit.ts"; |
| 38 | import { OrchestrationV2LayerLive } from "./runtimeLayer.ts"; | 38 | import * as RuntimeLayer from "./runtimeLayer.ts"; |
| 39 | import * as McpSessionRegistryTestkit from "../mcp/McpSessionRegistry.testkit.ts"; | 39 | import * as McpSessionRegistryTestkit from "../mcp/McpSessionRegistry.testkit.ts"; |
| 40 | import { GROK_MODEL_SELECTION } from "./testkit/fixtures/shared.ts"; | 40 | import { GROK_MODEL_SELECTION } from "./testkit/fixtures/shared.ts"; |
| 41 | 41 | ||
| 42 | const PlatformTestLayer = Layer.merge( | 42 | const layerPlatformTest = Layer.merge( |
| 43 | NodeServices.layer, | 43 | NodeServices.layer, |
| 44 | Layer.mock(SourceControlProviderRegistry.SourceControlProviderRegistry)({ | 44 | Layer.mock(SourceControlProviderRegistry.SourceControlProviderRegistry)({ |
| 45 | resolveLink: () => Effect.die("unused title link"), | 45 | resolveLink: () => Effect.die("unused title link"), |
| 46 | }), | 46 | }), |
| 47 | ); | 47 | ); |
| 48 | 48 | ||
| 49 | const serverConfigLayer = ServerConfig.layerTest(process.cwd(), { | 49 | const layerServerConfig = ServerConfig.layerTest(process.cwd(), { |
| 50 | prefix: "t3-grok-v2-live-", | 50 | prefix: "t3-grok-v2-live-", |
| 51 | }); | 51 | }); |
| 52 | 52 | ||
| 53 | const vcsDriverRegistryLayer = VcsDriverRegistry.layer.pipe( | 53 | const layerVcsDriverRegistry = VcsDriverRegistry.layer.pipe( |
| 54 | Layer.provide(VcsProcess.layer), | 54 | Layer.provide(VcsProcess.layer), |
| 55 | Layer.provide(serverConfigLayer), | 55 | Layer.provide(layerServerConfig), |
| 56 | Layer.provide(PlatformTestLayer), | 56 | Layer.provide(layerPlatformTest), |
| 57 | ); | 57 | ); |
| 58 | 58 | ||
| 59 | const checkpointStoreLayer = CheckpointStore.layer.pipe(Layer.provide(vcsDriverRegistryLayer)); | 59 | const layerCheckpointStore = CheckpointStore.layer.pipe(Layer.provide(layerVcsDriverRegistry)); |
| 60 | 60 | ||
| 61 | const serverSettingsLayer = ServerSettings.layerTest({ | 61 | const layerServerSettings = ServerSettings.layerTest({ |
| 62 | providers: { | 62 | providers: { |
| 63 | grok: { enabled: true }, | 63 | grok: { enabled: true }, |
| 64 | }, | 64 | }, |
| 65 | }); | 65 | }); |
| 66 | const backgroundPolicyLayer = BackgroundPolicy.layer.pipe( | 66 | const layerBackgroundPolicy = BackgroundPolicy.layer.pipe( |
| 67 | Layer.provide(Layer.effect(HostPowerMonitor.HostPowerMonitor, HostPowerMonitor.make())), | 67 | Layer.provide(Layer.effect(HostPowerMonitor.HostPowerMonitor, HostPowerMonitor.make())), |
| 68 | Layer.provide(serverSettingsLayer), | 68 | Layer.provide(layerServerSettings), |
| 69 | ); | 69 | ); |
| 70 | const providerInstanceRegistryLayer = ProviderInstanceRegistryHydrationLive.pipe( | 70 | const layerProviderInstanceRegistry = ProviderInstanceRegistryHydration.layer.pipe( |
| 71 | Layer.provide( | 71 | Layer.provide( |
| 72 | Layer.mergeAll( | 72 | Layer.mergeAll( |
| 73 | serverConfigLayer.pipe(Layer.provide(PlatformTestLayer)), | 73 | layerServerConfig.pipe(Layer.provide(layerPlatformTest)), |
| 74 | serverSettingsLayer, | 74 | layerServerSettings, |
| 75 | ServerSecretStore.layer.pipe( | 75 | ServerSecretStore.layer.pipe( |
| 76 | Layer.provide(serverConfigLayer), | 76 | Layer.provide(layerServerConfig), |
| 77 | Layer.provide(PlatformTestLayer), | 77 | Layer.provide(layerPlatformTest), |
| 78 | ), | 78 | ), |
| 79 | NodeServices.layer, | 79 | NodeServices.layer, |
| 80 | FetchHttpClient.layer, | 80 | FetchHttpClient.layer, |
| 81 | OpenCodeRuntime.OpenCodeRuntimeLive.pipe( | 81 | OpenCodeRuntime.layer.pipe( |
| 82 | Layer.provide(OpenCodeServerLedger.layerTest), | 82 | Layer.provide(OpenCodeServerLedger.layerTest), |
| 83 | Layer.provide(PlatformTestLayer), | 83 | Layer.provide(layerPlatformTest), |
| 84 | ), | 84 | ), |
| 85 | Layer.succeed( | 85 | Layer.succeed( |
| 86 | ProviderEventLoggers.ProviderEventLoggers, | 86 | ProviderEventLoggers.ProviderEventLoggers, |
| ... | @@ -88,9 +88,9 @@ const providerInstanceRegistryLayer = ProviderInstanceRegistryHydrationLive.pipe | ... | @@ -88,9 +88,9 @@ const providerInstanceRegistryLayer = ProviderInstanceRegistryHydrationLive.pipe |
| 88 | ), | 88 | ), |
| 89 | ModelManifest.layerTest, | 89 | ModelManifest.layerTest, |
| 90 | AntigravityInstallation.AntigravityInstallation.layer.pipe( | 90 | AntigravityInstallation.AntigravityInstallation.layer.pipe( |
| 91 | Layer.provide(serverConfigLayer.pipe(Layer.provide(PlatformTestLayer))), | 91 | Layer.provide(layerServerConfig.pipe(Layer.provide(layerPlatformTest))), |
| 92 | Layer.provide(FetchHttpClient.layer), | 92 | Layer.provide(FetchHttpClient.layer), |
| 93 | Layer.provide(PlatformTestLayer), | 93 | Layer.provide(layerPlatformTest), |
| 94 | ), | 94 | ), |
| 95 | // The Codex driver now resolves managed ChatGPT installs; these runs never launch Codex. | 95 | // The Codex driver now resolves managed ChatGPT installs; these runs never launch Codex. |
| 96 | Layer.mock(CodexInstallation.CodexInstallation)({ | 96 | Layer.mock(CodexInstallation.CodexInstallation)({ |
| ... | @@ -105,17 +105,17 @@ const providerInstanceRegistryLayer = ProviderInstanceRegistryHydrationLive.pipe | ... | @@ -105,17 +105,17 @@ const providerInstanceRegistryLayer = ProviderInstanceRegistryHydrationLive.pipe |
| 105 | ), | 105 | ), |
| 106 | ); | 106 | ); |
| 107 | 107 | ||
| 108 | const liveLayer = OrchestrationV2LayerLive.pipe( | 108 | const layerLive = RuntimeLayer.layer.pipe( |
| 109 | Layer.provide(worktreeRepairDependenciesTestLayer), | 109 | Layer.provide(ProviderTurnStartServiceTestkit.layer), |
| 110 | Layer.provide(McpSessionRegistryTestkit.layer), | 110 | Layer.provide(McpSessionRegistryTestkit.layer), |
| 111 | Layer.provide(SqlitePersistenceMemory), | 111 | Layer.provide(SqlitePersistence.layerMemory), |
| 112 | Layer.provide(checkpointStoreLayer), | 112 | Layer.provide(layerCheckpointStore), |
| 113 | Layer.provide(serverConfigLayer), | 113 | Layer.provide(layerServerConfig), |
| 114 | Layer.provide(serverSettingsLayer), | 114 | Layer.provide(layerServerSettings), |
| 115 | Layer.provide(providerInstanceRegistryLayer), | 115 | Layer.provide(layerProviderInstanceRegistry), |
| 116 | Layer.provide(ResetCreditCoordinator.layer), | 116 | Layer.provide(ResetCreditCoordinator.layer), |
| 117 | Layer.provide(backgroundPolicyLayer), | 117 | Layer.provide(layerBackgroundPolicy), |
| 118 | Layer.provide(PlatformTestLayer), | 118 | Layer.provide(layerPlatformTest), |
| 119 | ); | 119 | ); |
| 120 | 120 | ||
| 121 | const waitForIdle = Effect.fn("GrokOrchestratorV2Live.waitForIdle")(function* (threadId: ThreadId) { | 121 | const waitForIdle = Effect.fn("GrokOrchestratorV2Live.waitForIdle")(function* (threadId: ThreadId) { |
| ... | @@ -230,7 +230,7 @@ describe.runIf(process.env.T3_GROK_LIVE_ORCHESTRATOR === "1")("Grok V2 live orch | ... | @@ -230,7 +230,7 @@ describe.runIf(process.env.T3_GROK_LIVE_ORCHESTRATOR === "1")("Grok V2 live orch |
| 230 | ); | 230 | ); |
| 231 | assert.include(targetProjection.contextHandoffs[0]?.summaryText ?? "", marker); | 231 | assert.include(targetProjection.contextHandoffs[0]?.summaryText ?? "", marker); |
| 232 | assert.include(assistantText(targetProjection), marker); | 232 | assert.include(assistantText(targetProjection), marker); |
| 233 | }).pipe(Effect.provide(liveLayer), Effect.scoped), | 233 | }).pipe(Effect.provide(layerLive), Effect.scoped), |
| 234 | 360_000, | 234 | 360_000, |
| 235 | ); | 235 | ); |
| 236 | }); | 236 | }); |
apps/server/src/orchestration-v2/OpenCode2OrchestratorV2.live.test.ts+44-44| ... | @@ -48,12 +48,12 @@ import * as CheckpointStore from "../checkpointing/CheckpointStore.ts"; | ... | @@ -48,12 +48,12 @@ import * as CheckpointStore from "../checkpointing/CheckpointStore.ts"; |
| 48 | import * as BackgroundPolicy from "../background/BackgroundPolicy.ts"; | 48 | import * as BackgroundPolicy from "../background/BackgroundPolicy.ts"; |
| 49 | import * as HostPowerMonitor from "../background/HostPowerMonitor.ts"; | 49 | import * as HostPowerMonitor from "../background/HostPowerMonitor.ts"; |
| 50 | import * as ServerConfig from "../config.ts"; | 50 | import * as ServerConfig from "../config.ts"; |
| 51 | import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; | 51 | import * as SqlitePersistence from "../persistence/Layers/Sqlite.ts"; |
| 52 | import * as AntigravityInstallation from "../provider/AntigravityInstallation.ts"; | 52 | import * as AntigravityInstallation from "../provider/AntigravityInstallation.ts"; |
| 53 | import * as CodexInstallation from "../provider/CodexInstallation.ts"; | 53 | import * as CodexInstallation from "../provider/CodexInstallation.ts"; |
| 54 | import * as ServerEnvironment from "../environment/ServerEnvironment.ts"; | 54 | import * as ServerEnvironment from "../environment/ServerEnvironment.ts"; |
| 55 | import * as ModelManifest from "../provider/ModelManifest.ts"; | 55 | import * as ModelManifest from "../provider/ModelManifest.ts"; |
| 56 | import { ProviderInstanceRegistryHydrationLive } from "../provider/Layers/ProviderInstanceRegistryHydration.ts"; | 56 | import * as ProviderInstanceRegistryHydration from "../provider/Layers/ProviderInstanceRegistryHydration.ts"; |
| 57 | import * as ProviderEventLoggers from "../provider/Layers/ProviderEventLoggers.ts"; | 57 | import * as ProviderEventLoggers from "../provider/Layers/ProviderEventLoggers.ts"; |
| 58 | import * as OpenCode2Client from "../provider/opencode2/OpenCode2Client.ts"; | 58 | import * as OpenCode2Client from "../provider/opencode2/OpenCode2Client.ts"; |
| 59 | import * as OpenCodeRuntime from "../provider/opencodeRuntime.ts"; | 59 | import * as OpenCodeRuntime from "../provider/opencodeRuntime.ts"; |
| ... | @@ -64,8 +64,8 @@ import * as VcsProcess from "../vcs/VcsProcess.ts"; | ... | @@ -64,8 +64,8 @@ import * as VcsProcess from "../vcs/VcsProcess.ts"; |
| 64 | import * as EffectWorker from "./EffectWorker.ts"; | 64 | import * as EffectWorker from "./EffectWorker.ts"; |
| 65 | import * as Orchestrator from "./Orchestrator.ts"; | 65 | import * as Orchestrator from "./Orchestrator.ts"; |
| 66 | import * as ProviderInstanceRegistry from "../provider/Services/ProviderInstanceRegistry.ts"; | 66 | import * as ProviderInstanceRegistry from "../provider/Services/ProviderInstanceRegistry.ts"; |
| 67 | import { worktreeRepairDependenciesTestLayer } from "./ProviderTurnStartService.testkit.ts"; | 67 | import * as ProviderTurnStartServiceTestkit from "./ProviderTurnStartService.testkit.ts"; |
| 68 | import { OrchestrationV2LayerLive } from "./runtimeLayer.ts"; | 68 | import * as RuntimeLayer from "./runtimeLayer.ts"; |
| 69 | import * as IdAllocator from "./IdAllocator.ts"; | 69 | import * as IdAllocator from "./IdAllocator.ts"; |
| 70 | import * as ProviderContinuationRequests from "./ProviderContinuationRequests.ts"; | 70 | import * as ProviderContinuationRequests from "./ProviderContinuationRequests.ts"; |
| 71 | import * as ProviderContinuationService from "./ProviderContinuationService.ts"; | 71 | import * as ProviderContinuationService from "./ProviderContinuationService.ts"; |
| ... | @@ -84,7 +84,7 @@ const SWITCHED_MODEL = "opencode/mimo-v2.6-flash-free"; | ... | @@ -84,7 +84,7 @@ const SWITCHED_MODEL = "opencode/mimo-v2.6-flash-free"; |
| 84 | 84 | ||
| 85 | /** The OpenCode servers the driver spawned, newest last, so a test can kill one by its own PID. */ | 85 | /** The OpenCode servers the driver spawned, newest last, so a test can kill one by its own PID. */ |
| 86 | const spawnedPids: Array<number> = []; | 86 | const spawnedPids: Array<number> = []; |
| 87 | const spawnedServers = Layer.succeed( | 87 | const layerSpawnedServers = Layer.succeed( |
| 88 | OpenCodeServerLedger.OpenCodeServerLedger, | 88 | OpenCodeServerLedger.OpenCodeServerLedger, |
| 89 | OpenCodeServerLedger.OpenCodeServerLedger.of({ | 89 | OpenCodeServerLedger.OpenCodeServerLedger.of({ |
| 90 | track: ({ pid }) => | 90 | track: ({ pid }) => |
| ... | @@ -100,7 +100,7 @@ const spawnedServers = Layer.succeed( | ... | @@ -100,7 +100,7 @@ const spawnedServers = Layer.succeed( |
| 100 | * MCP server the run can see called; without it they point nowhere, as in replay. | 100 | * MCP server the run can see called; without it they point nowhere, as in replay. |
| 101 | */ | 101 | */ |
| 102 | const MCP_URL = process.env.OPENCODE2_MCP_URL ?? "http://127.0.0.1/mcp"; | 102 | const MCP_URL = process.env.OPENCODE2_MCP_URL ?? "http://127.0.0.1/mcp"; |
| 103 | const mcpRegistryLayer = Layer.succeed( | 103 | const layerMcpRegistry = Layer.succeed( |
| 104 | McpSessionRegistry.McpSessionRegistry, | 104 | McpSessionRegistry.McpSessionRegistry, |
| 105 | McpSessionRegistry.McpSessionRegistry.of({ | 105 | McpSessionRegistry.McpSessionRegistry.of({ |
| 106 | issue: ({ threadId, providerInstanceId }) => | 106 | issue: ({ threadId, providerInstanceId }) => |
| ... | @@ -123,20 +123,20 @@ const mcpRegistryLayer = Layer.succeed( | ... | @@ -123,20 +123,20 @@ const mcpRegistryLayer = Layer.succeed( |
| 123 | }), | 123 | }), |
| 124 | ); | 124 | ); |
| 125 | 125 | ||
| 126 | const PlatformTestLayer = Layer.merge( | 126 | const layerPlatformTest = Layer.merge( |
| 127 | NodeServices.layer, | 127 | NodeServices.layer, |
| 128 | Layer.mock(SourceControlProviderRegistry.SourceControlProviderRegistry)({ | 128 | Layer.mock(SourceControlProviderRegistry.SourceControlProviderRegistry)({ |
| 129 | resolveLink: () => Effect.die("unused title link"), | 129 | resolveLink: () => Effect.die("unused title link"), |
| 130 | }), | 130 | }), |
| 131 | ); | 131 | ); |
| 132 | const serverConfigLayer = ServerConfig.layerTest(`${ROOT}/work`, { prefix: "t3-opencode2-live-" }); | 132 | const layerServerConfig = ServerConfig.layerTest(`${ROOT}/work`, { prefix: "t3-opencode2-live-" }); |
| 133 | const vcsDriverRegistryLayer = VcsDriverRegistry.layer.pipe( | 133 | const layerVcsDriverRegistry = VcsDriverRegistry.layer.pipe( |
| 134 | Layer.provide(VcsProcess.layer), | 134 | Layer.provide(VcsProcess.layer), |
| 135 | Layer.provide(serverConfigLayer), | 135 | Layer.provide(layerServerConfig), |
| 136 | Layer.provide(PlatformTestLayer), | 136 | Layer.provide(layerPlatformTest), |
| 137 | ); | 137 | ); |
| 138 | // Isolated OpenCode state: the server never touches the developer's own data. | 138 | // Isolated OpenCode state: the server never touches the developer's own data. |
| 139 | const serverSettingsLayer = ServerSettings.layerTest({ | 139 | const layerServerSettings = ServerSettings.layerTest({ |
| 140 | providerInstances: { | 140 | providerInstances: { |
| 141 | [INSTANCE]: { | 141 | [INSTANCE]: { |
| 142 | driver: ProviderDriverKind.make("opencode"), | 142 | driver: ProviderDriverKind.make("opencode"), |
| ... | @@ -162,24 +162,24 @@ const serverSettingsLayer = ServerSettings.layerTest({ | ... | @@ -162,24 +162,24 @@ const serverSettingsLayer = ServerSettings.layerTest({ |
| 162 | }, | 162 | }, |
| 163 | }, | 163 | }, |
| 164 | }); | 164 | }); |
| 165 | const backgroundPolicyLayer = BackgroundPolicy.layer.pipe( | 165 | const layerBackgroundPolicy = BackgroundPolicy.layer.pipe( |
| 166 | Layer.provide(Layer.effect(HostPowerMonitor.HostPowerMonitor, HostPowerMonitor.make())), | 166 | Layer.provide(Layer.effect(HostPowerMonitor.HostPowerMonitor, HostPowerMonitor.make())), |
| 167 | Layer.provide(serverSettingsLayer), | 167 | Layer.provide(layerServerSettings), |
| 168 | ); | 168 | ); |
| 169 | const providerInstanceRegistryLayer = ProviderInstanceRegistryHydrationLive.pipe( | 169 | const layerProviderInstanceRegistry = ProviderInstanceRegistryHydration.layer.pipe( |
| 170 | Layer.provide( | 170 | Layer.provide( |
| 171 | Layer.mergeAll( | 171 | Layer.mergeAll( |
| 172 | serverConfigLayer.pipe(Layer.provide(PlatformTestLayer)), | 172 | layerServerConfig.pipe(Layer.provide(layerPlatformTest)), |
| 173 | serverSettingsLayer, | 173 | layerServerSettings, |
| 174 | ServerSecretStore.layer.pipe( | 174 | ServerSecretStore.layer.pipe( |
| 175 | Layer.provide(serverConfigLayer), | 175 | Layer.provide(layerServerConfig), |
| 176 | Layer.provide(PlatformTestLayer), | 176 | Layer.provide(layerPlatformTest), |
| 177 | ), | 177 | ), |
| 178 | NodeServices.layer, | 178 | NodeServices.layer, |
| 179 | FetchHttpClient.layer, | 179 | FetchHttpClient.layer, |
| 180 | OpenCodeRuntime.OpenCodeRuntimeLive.pipe( | 180 | OpenCodeRuntime.layer.pipe( |
| 181 | Layer.provide(spawnedServers), | 181 | Layer.provide(layerSpawnedServers), |
| 182 | Layer.provide(PlatformTestLayer), | 182 | Layer.provide(layerPlatformTest), |
| 183 | ), | 183 | ), |
| 184 | Layer.succeed( | 184 | Layer.succeed( |
| 185 | ProviderEventLoggers.ProviderEventLoggers, | 185 | ProviderEventLoggers.ProviderEventLoggers, |
| ... | @@ -187,9 +187,9 @@ const providerInstanceRegistryLayer = ProviderInstanceRegistryHydrationLive.pipe | ... | @@ -187,9 +187,9 @@ const providerInstanceRegistryLayer = ProviderInstanceRegistryHydrationLive.pipe |
| 187 | ), | 187 | ), |
| 188 | ModelManifest.layerTest, | 188 | ModelManifest.layerTest, |
| 189 | AntigravityInstallation.AntigravityInstallation.layer.pipe( | 189 | AntigravityInstallation.AntigravityInstallation.layer.pipe( |
| 190 | Layer.provide(serverConfigLayer.pipe(Layer.provide(PlatformTestLayer))), | 190 | Layer.provide(layerServerConfig.pipe(Layer.provide(layerPlatformTest))), |
| 191 | Layer.provide(FetchHttpClient.layer), | 191 | Layer.provide(FetchHttpClient.layer), |
| 192 | Layer.provide(PlatformTestLayer), | 192 | Layer.provide(layerPlatformTest), |
| 193 | ), | 193 | ), |
| 194 | // The Codex driver now resolves managed ChatGPT installs; these runs never launch Codex. | 194 | // The Codex driver now resolves managed ChatGPT installs; these runs never launch Codex. |
| 195 | Layer.mock(CodexInstallation.CodexInstallation)({ | 195 | Layer.mock(CodexInstallation.CodexInstallation)({ |
| ... | @@ -203,22 +203,22 @@ const providerInstanceRegistryLayer = ProviderInstanceRegistryHydrationLive.pipe | ... | @@ -203,22 +203,22 @@ const providerInstanceRegistryLayer = ProviderInstanceRegistryHydrationLive.pipe |
| 203 | ), | 203 | ), |
| 204 | ), | 204 | ), |
| 205 | ); | 205 | ); |
| 206 | const orchestrationLayer = OrchestrationV2LayerLive.pipe( | 206 | const layerOrchestration = RuntimeLayer.layer.pipe( |
| 207 | Layer.provide(worktreeRepairDependenciesTestLayer), | 207 | Layer.provide(ProviderTurnStartServiceTestkit.layer), |
| 208 | Layer.provide(mcpRegistryLayer), | 208 | Layer.provide(layerMcpRegistry), |
| 209 | Layer.provide(SqlitePersistenceMemory), | 209 | Layer.provide(SqlitePersistence.layerMemory), |
| 210 | Layer.provide(CheckpointStore.layer.pipe(Layer.provide(vcsDriverRegistryLayer))), | 210 | Layer.provide(CheckpointStore.layer.pipe(Layer.provide(layerVcsDriverRegistry))), |
| 211 | Layer.provide(serverConfigLayer), | 211 | Layer.provide(layerServerConfig), |
| 212 | Layer.provide(serverSettingsLayer), | 212 | Layer.provide(layerServerSettings), |
| 213 | // Merged, not only provided: the test reads the same instance the orchestrator uses. | 213 | // Merged, not only provided: the test reads the same instance the orchestrator uses. |
| 214 | Layer.provideMerge(providerInstanceRegistryLayer), | 214 | Layer.provideMerge(layerProviderInstanceRegistry), |
| 215 | Layer.provide(ResetCreditCoordinator.layer), | 215 | Layer.provide(ResetCreditCoordinator.layer), |
| 216 | Layer.provide(backgroundPolicyLayer), | 216 | Layer.provide(layerBackgroundPolicy), |
| 217 | Layer.provide(PlatformTestLayer), | 217 | Layer.provide(layerPlatformTest), |
| 218 | ); | 218 | ); |
| 219 | 219 | ||
| 220 | // Starts the continuation run a provider wake asks for, as the production layer does. | 220 | // Starts the continuation run a provider wake asks for, as the production layer does. |
| 221 | const continuationWorkerLayer = ProviderContinuationService.workerLive.pipe( | 221 | const layerContinuationWorker = ProviderContinuationService.layer.pipe( |
| 222 | Layer.provide( | 222 | Layer.provide( |
| 223 | Layer.unwrap( | 223 | Layer.unwrap( |
| 224 | Effect.gen(function* () { | 224 | Effect.gen(function* () { |
| ... | @@ -234,7 +234,7 @@ const continuationWorkerLayer = ProviderContinuationService.workerLive.pipe( | ... | @@ -234,7 +234,7 @@ const continuationWorkerLayer = ProviderContinuationService.workerLive.pipe( |
| 234 | Layer.provide(IdAllocator.layer), | 234 | Layer.provide(IdAllocator.layer), |
| 235 | Layer.provide(ProviderContinuationRequests.layer), | 235 | Layer.provide(ProviderContinuationRequests.layer), |
| 236 | ); | 236 | ); |
| 237 | const liveLayer = continuationWorkerLayer.pipe(Layer.provideMerge(orchestrationLayer)); | 237 | const layerLive = layerContinuationWorker.pipe(Layer.provideMerge(layerOrchestration)); |
| 238 | 238 | ||
| 239 | // How long one step may take, in seconds. A free model sometimes takes minutes | 239 | // How long one step may take, in seconds. A free model sometimes takes minutes |
| 240 | // before its first tool call; `OPENCODE2_STEP_WAIT` raises it for such runs. | 240 | // before its first tool call; `OPENCODE2_STEP_WAIT` raises it for such runs. |
| ... | @@ -432,7 +432,7 @@ describe.runIf(binaryPath !== undefined && ROOT !== "")("OpenCode 2 live orchest | ... | @@ -432,7 +432,7 @@ describe.runIf(binaryPath !== undefined && ROOT !== "")("OpenCode 2 live orchest |
| 432 | const models = yield* assistantModels(sessionId!); | 432 | const models = yield* assistantModels(sessionId!); |
| 433 | assert.equal(models.at(-1), SWITCHED_MODEL); | 433 | assert.equal(models.at(-1), SWITCHED_MODEL); |
| 434 | assert.notEqual(models[0], SWITCHED_MODEL); | 434 | assert.notEqual(models[0], SWITCHED_MODEL); |
| 435 | }).pipe(Effect.provide(Layer.merge(liveLayer, NodeServices.layer)), Effect.scoped), | 435 | }).pipe(Effect.provide(Layer.merge(layerLive, NodeServices.layer)), Effect.scoped), |
| 436 | 360_000, | 436 | 360_000, |
| 437 | ); | 437 | ); |
| 438 | 438 | ||
| ... | @@ -542,7 +542,7 @@ describe.runIf(binaryPath !== undefined && ROOT !== "")("OpenCode 2 live orchest | ... | @@ -542,7 +542,7 @@ describe.runIf(binaryPath !== undefined && ROOT !== "")("OpenCode 2 live orchest |
| 542 | assert.equal(answered.runs.at(-1)?.status, "completed"); | 542 | assert.equal(answered.runs.at(-1)?.status, "completed"); |
| 543 | const reply = answered.messages.findLast((message) => message.role === "assistant"); | 543 | const reply = answered.messages.findLast((message) => message.role === "assistant"); |
| 544 | assert.match(reply?.text ?? "", /blue/i); | 544 | assert.match(reply?.text ?? "", /blue/i); |
| 545 | }).pipe(Effect.provide(Layer.merge(liveLayer, NodeServices.layer)), Effect.scoped), | 545 | }).pipe(Effect.provide(Layer.merge(layerLive, NodeServices.layer)), Effect.scoped), |
| 546 | 600_000, | 546 | 600_000, |
| 547 | ); | 547 | ); |
| 548 | 548 | ||
| ... | @@ -582,7 +582,7 @@ describe.runIf(binaryPath !== undefined && ROOT !== "")("OpenCode 2 live orchest | ... | @@ -582,7 +582,7 @@ describe.runIf(binaryPath !== undefined && ROOT !== "")("OpenCode 2 live orchest |
| 582 | assert.equal(planned.runs.at(-1)?.status, "completed"); | 582 | assert.equal(planned.runs.at(-1)?.status, "completed"); |
| 583 | assert.isFalse(yield* fs.exists(path.join(ROOT, "work", "plan_write_probe.txt"))); | 583 | assert.isFalse(yield* fs.exists(path.join(ROOT, "work", "plan_write_probe.txt"))); |
| 584 | assert.isTrue(yield* fs.exists(path.join(planDir, "probe-plan.md"))); | 584 | assert.isTrue(yield* fs.exists(path.join(planDir, "probe-plan.md"))); |
| 585 | }).pipe(Effect.provide(Layer.merge(liveLayer, NodeServices.layer)), Effect.scoped), | 585 | }).pipe(Effect.provide(Layer.merge(layerLive, NodeServices.layer)), Effect.scoped), |
| 586 | 360_000, | 586 | 360_000, |
| 587 | ); | 587 | ); |
| 588 | 588 | ||
| ... | @@ -718,7 +718,7 @@ describe.runIf(binaryPath !== undefined && ROOT !== "")("OpenCode 2 live orchest | ... | @@ -718,7 +718,7 @@ describe.runIf(binaryPath !== undefined && ROOT !== "")("OpenCode 2 live orchest |
| 718 | stoppedChild.turnItems.some((item) => item.status === "running"), | 718 | stoppedChild.turnItems.some((item) => item.status === "running"), |
| 719 | "the stopped child shows nothing running", | 719 | "the stopped child shows nothing running", |
| 720 | ); | 720 | ); |
| 721 | }).pipe(Effect.provide(Layer.merge(liveLayer, NodeServices.layer)), Effect.scoped), | 721 | }).pipe(Effect.provide(Layer.merge(layerLive, NodeServices.layer)), Effect.scoped), |
| 722 | 480_000, | 722 | 480_000, |
| 723 | ); | 723 | ); |
| 724 | 724 | ||
| ... | @@ -927,7 +927,7 @@ describe.runIf(binaryPath !== undefined && ROOT !== "")("OpenCode 2 live orchest | ... | @@ -927,7 +927,7 @@ describe.runIf(binaryPath !== undefined && ROOT !== "")("OpenCode 2 live orchest |
| 927 | .map((row) => String(row.id)); | 927 | .map((row) => String(row.id)); |
| 928 | db.close(); | 928 | db.close(); |
| 929 | assert.lengthOf(userMessages, 2); | 929 | assert.lengthOf(userMessages, 2); |
| 930 | }).pipe(Effect.provide(Layer.merge(liveLayer, NodeServices.layer)), Effect.scoped), | 930 | }).pipe(Effect.provide(Layer.merge(layerLive, NodeServices.layer)), Effect.scoped), |
| 931 | 600_000, | 931 | 600_000, |
| 932 | ); | 932 | ); |
| 933 | 933 | ||
| ... | @@ -1028,7 +1028,7 @@ describe.runIf(binaryPath !== undefined && ROOT !== "")("OpenCode 2 live orchest | ... | @@ -1028,7 +1028,7 @@ describe.runIf(binaryPath !== undefined && ROOT !== "")("OpenCode 2 live orchest |
| 1028 | compaction?.type === "compaction" ? (compaction.summary ?? "").length : 0, | 1028 | compaction?.type === "compaction" ? (compaction.summary ?? "").length : 0, |
| 1029 | 0, | 1029 | 0, |
| 1030 | ); | 1030 | ); |
| 1031 | }).pipe(Effect.provide(Layer.merge(liveLayer, NodeServices.layer)), Effect.scoped), | 1031 | }).pipe(Effect.provide(Layer.merge(layerLive, NodeServices.layer)), Effect.scoped), |
| 1032 | 360_000, | 1032 | 360_000, |
| 1033 | ); | 1033 | ); |
| 1034 | 1034 | ||
| ... | @@ -1114,7 +1114,7 @@ describe.runIf(binaryPath !== undefined && ROOT !== "")("OpenCode 2 live orchest | ... | @@ -1114,7 +1114,7 @@ describe.runIf(binaryPath !== undefined && ROOT !== "")("OpenCode 2 live orchest |
| 1114 | const answer = next.turnItems.findLast((item) => item.type === "assistant_message"); | 1114 | const answer = next.turnItems.findLast((item) => item.type === "assistant_message"); |
| 1115 | assert.include(answer?.type === "assistant_message" ? answer.text : "", "sleep 60"); | 1115 | assert.include(answer?.type === "assistant_message" ? answer.text : "", "sleep 60"); |
| 1116 | assert.lengthOf(next.providerThreads, 1); | 1116 | assert.lengthOf(next.providerThreads, 1); |
| 1117 | }).pipe(Effect.provide(Layer.merge(liveLayer, NodeServices.layer)), Effect.scoped), | 1117 | }).pipe(Effect.provide(Layer.merge(layerLive, NodeServices.layer)), Effect.scoped), |
| 1118 | 360_000, | 1118 | 360_000, |
| 1119 | ); | 1119 | ); |
| 1120 | 1120 | ||
| ... | @@ -1175,7 +1175,7 @@ describe.runIf(binaryPath !== undefined && ROOT !== "")("OpenCode 2 live orchest | ... | @@ -1175,7 +1175,7 @@ describe.runIf(binaryPath !== undefined && ROOT !== "")("OpenCode 2 live orchest |
| 1175 | [], | 1175 | [], |
| 1176 | "an external server gets no T3 MCP server, as with 1.x", | 1176 | "an external server gets no T3 MCP server, as with 1.x", |
| 1177 | ); | 1177 | ); |
| 1178 | }).pipe(Effect.provide(Layer.merge(liveLayer, NodeServices.layer)), Effect.scoped), | 1178 | }).pipe(Effect.provide(Layer.merge(layerLive, NodeServices.layer)), Effect.scoped), |
| 1179 | 360_000, | 1179 | 360_000, |
| 1180 | ); | 1180 | ); |
| 1181 | }); | 1181 | }); |
apps/server/src/orchestration-v2/Orchestrator.control-reads.test.ts+13-13| ... | @@ -21,13 +21,13 @@ import * as DateTime from "effect/DateTime"; | ... | @@ -21,13 +21,13 @@ import * as DateTime from "effect/DateTime"; |
| 21 | import * as Effect from "effect/Effect"; | 21 | import * as Effect from "effect/Effect"; |
| 22 | import * as Layer from "effect/Layer"; | 22 | import * as Layer from "effect/Layer"; |
| 23 | import * as SqlClient from "effect/sql/SqlClient"; | 23 | import * as SqlClient from "effect/sql/SqlClient"; |
| 24 | import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; | 24 | import * as SqlitePersistence from "../persistence/Layers/Sqlite.ts"; |
| 25 | import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts"; | 25 | import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts"; |
| 26 | import * as Orchestrator from "./Orchestrator.ts"; | 26 | import * as Orchestrator from "./Orchestrator.ts"; |
| 27 | import * as ProjectionStore from "./ProjectionStore.ts"; | 27 | import * as ProjectionStore from "./ProjectionStore.ts"; |
| 28 | import type { ProviderAdapterV2Shape } from "./ProviderAdapter.ts"; | 28 | import type { ProviderAdapterV2Shape } from "./ProviderAdapter.ts"; |
| 29 | import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts"; | 29 | import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts"; |
| 30 | import { makeOrchestratorV2ReplayLayerWithRegistry } from "./testkit/ProviderReplayHarness.ts"; | 30 | import * as ProviderReplayHarness from "./testkit/ProviderReplayHarness.ts"; |
| 31 | 31 | ||
| 32 | const instanceId = ProviderInstanceId.make("codex"); | 32 | const instanceId = ProviderInstanceId.make("codex"); |
| 33 | const modelSelection = { instanceId, model: "gpt-5.1-codex" }; | 33 | const modelSelection = { instanceId, model: "gpt-5.1-codex" }; |
| ... | @@ -38,14 +38,14 @@ const adapter = { | ... | @@ -38,14 +38,14 @@ const adapter = { |
| 38 | planSelectionTransition: () => Effect.succeed({ type: "apply_on_next_turn" as const }), | 38 | planSelectionTransition: () => Effect.succeed({ type: "apply_on_next_turn" as const }), |
| 39 | openSession: () => Effect.die("No provider process needed for metadata controls"), | 39 | openSession: () => Effect.die("No provider process needed for metadata controls"), |
| 40 | } as ProviderAdapterV2Shape; | 40 | } as ProviderAdapterV2Shape; |
| 41 | const database = SqlitePersistenceMemory; | 41 | const layerDatabase = SqlitePersistence.layerMemory; |
| 42 | const testLayer = Layer.mergeAll( | 42 | const layerTest = Layer.mergeAll( |
| 43 | database, | 43 | layerDatabase, |
| 44 | ProjectionStore.layer.pipe(Layer.provide(database)), | 44 | ProjectionStore.layer.pipe(Layer.provide(layerDatabase)), |
| 45 | makeOrchestratorV2ReplayLayerWithRegistry( | 45 | ProviderReplayHarness.layerWithRegistry( |
| 46 | { name: "control-reads" }, | 46 | { name: "control-reads" }, |
| 47 | ProviderAdapterRegistry.makeLayer([adapter]), | 47 | ProviderAdapterRegistry.layerFromAdapters([adapter]), |
| 48 | { databaseLayer: database, runEffectWorker: false }, | 48 | { databaseLayer: layerDatabase, runEffectWorker: false }, |
| 49 | ), | 49 | ), |
| 50 | ); | 50 | ); |
| 51 | 51 | ||
| ... | @@ -277,7 +277,7 @@ it.effect( | ... | @@ -277,7 +277,7 @@ it.effect( |
| 277 | threadId, | 277 | threadId, |
| 278 | }); | 278 | }); |
| 279 | assert.isNotNull((yield* projections.getThread(threadId)).deletedAt); | 279 | assert.isNotNull((yield* projections.getThread(threadId)).deletedAt); |
| 280 | }).pipe(Effect.provide(testLayer)), | 280 | }).pipe(Effect.provide(layerTest)), |
| 281 | ); | 281 | ); |
| 282 | 282 | ||
| 283 | it.effect("implements a proposed plan that the command projection leaves out", () => | 283 | it.effect("implements a proposed plan that the command projection leaves out", () => |
| ... | @@ -331,7 +331,7 @@ it.effect("implements a proposed plan that the command projection leaves out", ( | ... | @@ -331,7 +331,7 @@ it.effect("implements a proposed plan that the command projection leaves out", ( |
| 331 | }); | 331 | }); |
| 332 | 332 | ||
| 333 | assert.equal((yield* projections.getPlan(threadId, planId))?.status, "completed"); | 333 | assert.equal((yield* projections.getPlan(threadId, planId))?.status, "completed"); |
| 334 | }).pipe(Effect.provide(testLayer)), | 334 | }).pipe(Effect.provide(layerTest)), |
| 335 | ); | 335 | ); |
| 336 | 336 | ||
| 337 | // Stop's settle follow-up runs after the provider interrupt returns, possibly | 337 | // Stop's settle follow-up runs after the provider interrupt returns, possibly |
| ... | @@ -532,7 +532,7 @@ it.effect("settles only the stopped run's background work, once", () => | ... | @@ -532,7 +532,7 @@ it.effect("settles only the stopped run's background work, once", () => |
| 532 | `${commandItem(2)}:running`, | 532 | `${commandItem(2)}:running`, |
| 533 | `${commandItem(3)}:running`, | 533 | `${commandItem(3)}:running`, |
| 534 | ]); | 534 | ]); |
| 535 | }).pipe(Effect.provide(testLayer)), | 535 | }).pipe(Effect.provide(layerTest)), |
| 536 | ); | 536 | ); |
| 537 | 537 | ||
| 538 | it.effect("keeps delegated child pull-request links independent of the parent", () => | 538 | it.effect("keeps delegated child pull-request links independent of the parent", () => |
| ... | @@ -622,5 +622,5 @@ it.effect("keeps delegated child pull-request links independent of the parent", | ... | @@ -622,5 +622,5 @@ it.effect("keeps delegated child pull-request links independent of the parent", |
| 622 | const parentAfterChildLink = yield* projections.getThreadProjection(parentThreadId); | 622 | const parentAfterChildLink = yield* projections.getThreadProjection(parentThreadId); |
| 623 | assert.deepEqual(parentAfterChildLink.thread.linkedPullRequest, parentPullRequest); | 623 | assert.deepEqual(parentAfterChildLink.thread.linkedPullRequest, parentPullRequest); |
| 624 | assert.deepEqual(parentAfterChildLink.thread.pullRequests, parent.thread.pullRequests); | 624 | assert.deepEqual(parentAfterChildLink.thread.pullRequests, parent.thread.pullRequests); |
| 625 | }).pipe(Effect.provide(testLayer)), | 625 | }).pipe(Effect.provide(layerTest)), |
| 626 | ); | 626 | ); |
apps/server/src/orchestration-v2/ProjectSettingsUpgrade.integration.test.ts+15-15| ... | @@ -15,7 +15,7 @@ import * as GitWorkflow from "../git/GitWorkflowService.ts"; | ... | @@ -15,7 +15,7 @@ import * as GitWorkflow from "../git/GitWorkflowService.ts"; |
| 15 | import * as NodeSqliteClient from "@t3tools/shared/nodeSqliteClient"; | 15 | import * as NodeSqliteClient from "@t3tools/shared/nodeSqliteClient"; |
| 16 | 16 | ||
| 17 | import * as McpSessionRegistryTestkit from "../mcp/McpSessionRegistry.testkit.ts"; | 17 | import * as McpSessionRegistryTestkit from "../mcp/McpSessionRegistry.testkit.ts"; |
| 18 | import { makeSqlitePersistenceLive } from "../persistence/Layers/Sqlite.ts"; | 18 | import * as SqlitePersistence from "../persistence/Layers/Sqlite.ts"; |
| 19 | import { runMigrations } from "../persistence/Migrations.ts"; | 19 | import { runMigrations } from "../persistence/Migrations.ts"; |
| 20 | import * as ProjectEnrichmentService from "../project/ProjectEnrichmentService.ts"; | 20 | import * as ProjectEnrichmentService from "../project/ProjectEnrichmentService.ts"; |
| 21 | import * as ProjectService from "../project/ProjectService.ts"; | 21 | import * as ProjectService from "../project/ProjectService.ts"; |
| ... | @@ -26,7 +26,7 @@ import * as VcsDriverRegistry from "../vcs/VcsDriverRegistry.ts"; | ... | @@ -26,7 +26,7 @@ import * as VcsDriverRegistry from "../vcs/VcsDriverRegistry.ts"; |
| 26 | import * as VcsProcess from "../vcs/VcsProcess.ts"; | 26 | import * as VcsProcess from "../vcs/VcsProcess.ts"; |
| 27 | import * as WorkspacePaths from "../workspace/WorkspacePaths.ts"; | 27 | import * as WorkspacePaths from "../workspace/WorkspacePaths.ts"; |
| 28 | import * as LegacyV1ThreadImporter from "./legacy/LegacyV1ThreadImporter.ts"; | 28 | import * as LegacyV1ThreadImporter from "./legacy/LegacyV1ThreadImporter.ts"; |
| 29 | import { OrchestrationV2LayerLive, ProjectServiceLayerLive } from "./runtimeLayer.ts"; | 29 | import * as RuntimeLayer from "./runtimeLayer.ts"; |
| 30 | 30 | ||
| 31 | const projectId = ProjectId.make("project:upgrade"); | 31 | const projectId = ProjectId.make("project:upgrade"); |
| 32 | const icon = { kind: "emoji", emoji: "🦊" } as const; | 32 | const icon = { kind: "emoji", emoji: "🦊" } as const; |
| ... | @@ -116,28 +116,28 @@ const unusedEnrichment = { | ... | @@ -116,28 +116,28 @@ const unusedEnrichment = { |
| 116 | }; | 116 | }; |
| 117 | 117 | ||
| 118 | /** The production V2 runtime and project service against one file-backed database. */ | 118 | /** The production V2 runtime and project service against one file-backed database. */ |
| 119 | const makeRuntimeLayer = (dbPath: string) => { | 119 | const layerRuntime = (dbPath: string) => { |
| 120 | const platform = Layer.merge( | 120 | const layerPlatform = Layer.merge( |
| 121 | NodeServices.layer, | 121 | NodeServices.layer, |
| 122 | Layer.mock(SourceControlProviderRegistry.SourceControlProviderRegistry)({ | 122 | Layer.mock(SourceControlProviderRegistry.SourceControlProviderRegistry)({ |
| 123 | resolveLink: () => Effect.die("unused"), | 123 | resolveLink: () => Effect.die("unused"), |
| 124 | }), | 124 | }), |
| 125 | ); | 125 | ); |
| 126 | const serverConfig = ServerConfig.layerTest(process.cwd(), { | 126 | const layerServerConfig = ServerConfig.layerTest(process.cwd(), { |
| 127 | prefix: "t3-project-upgrade-", | 127 | prefix: "t3-project-upgrade-", |
| 128 | }); | 128 | }); |
| 129 | const checkpointStore = CheckpointStore.layer.pipe( | 129 | const layerCheckpointStore = CheckpointStore.layer.pipe( |
| 130 | Layer.provide( | 130 | Layer.provide( |
| 131 | VcsDriverRegistry.layer.pipe( | 131 | VcsDriverRegistry.layer.pipe( |
| 132 | Layer.provide(VcsProcess.layer), | 132 | Layer.provide(VcsProcess.layer), |
| 133 | Layer.provide(serverConfig), | 133 | Layer.provide(layerServerConfig), |
| 134 | Layer.provide(platform), | 134 | Layer.provide(layerPlatform), |
| 135 | ), | 135 | ), |
| 136 | ), | 136 | ), |
| 137 | ); | 137 | ); |
| 138 | return Layer.mergeAll( | 138 | return Layer.mergeAll( |
| 139 | OrchestrationV2LayerLive.pipe(Layer.provide(ProjectServiceLayerLive)), | 139 | RuntimeLayer.layer.pipe(Layer.provide(RuntimeLayer.layerProjectService)), |
| 140 | ProjectServiceLayerLive, | 140 | RuntimeLayer.layerProjectService, |
| 141 | ).pipe( | 141 | ).pipe( |
| 142 | Layer.provide( | 142 | Layer.provide( |
| 143 | Layer.mock(ProjectEnrichmentService.ProjectEnrichmentService)({ | 143 | Layer.mock(ProjectEnrichmentService.ProjectEnrichmentService)({ |
| ... | @@ -152,9 +152,9 @@ const makeRuntimeLayer = (dbPath: string) => { | ... | @@ -152,9 +152,9 @@ const makeRuntimeLayer = (dbPath: string) => { |
| 152 | }), | 152 | }), |
| 153 | ), | 153 | ), |
| 154 | Layer.provide(McpSessionRegistryTestkit.layer), | 154 | Layer.provide(McpSessionRegistryTestkit.layer), |
| 155 | Layer.provideMerge(makeSqlitePersistenceLive(dbPath)), | 155 | Layer.provideMerge(SqlitePersistence.layerFromPath(dbPath)), |
| 156 | Layer.provide(checkpointStore), | 156 | Layer.provide(layerCheckpointStore), |
| 157 | Layer.provide(serverConfig), | 157 | Layer.provide(layerServerConfig), |
| 158 | Layer.provide(ServerSettings.layerTest()), | 158 | Layer.provide(ServerSettings.layerTest()), |
| 159 | Layer.provide( | 159 | Layer.provide( |
| 160 | Layer.succeed(ProviderInstanceRegistry.ProviderInstanceRegistry, { | 160 | Layer.succeed(ProviderInstanceRegistry.ProviderInstanceRegistry, { |
| ... | @@ -170,7 +170,7 @@ const makeRuntimeLayer = (dbPath: string) => { | ... | @@ -170,7 +170,7 @@ const makeRuntimeLayer = (dbPath: string) => { |
| 170 | pruneWorktrees: () => Effect.void, | 170 | pruneWorktrees: () => Effect.void, |
| 171 | }), | 171 | }), |
| 172 | ), | 172 | ), |
| 173 | Layer.provide(platform), | 173 | Layer.provide(layerPlatform), |
| 174 | ); | 174 | ); |
| 175 | }; | 175 | }; |
| 176 | 176 | ||
| ... | @@ -189,7 +189,7 @@ it.live("keeps project settings through the V2 migrations and the first V2 boot" | ... | @@ -189,7 +189,7 @@ it.live("keeps project settings through the V2 migrations and the first V2 boot" |
| 189 | assert.deepEqual(yield* readSettings, expectedSettings); | 189 | assert.deepEqual(yield* readSettings, expectedSettings); |
| 190 | const project = yield* (yield* ProjectService.ProjectService).getById(projectId); | 190 | const project = yield* (yield* ProjectService.ProjectService).getById(projectId); |
| 191 | assert.equal(project._tag, "Some"); | 191 | assert.equal(project._tag, "Some"); |
| 192 | }).pipe(Effect.provide(makeRuntimeLayer(dbPath))); | 192 | }).pipe(Effect.provide(layerRuntime(dbPath))); |
| 193 | }), | 193 | }), |
| 194 | ).pipe(Effect.provide(NodeServices.layer)), | 194 | ).pipe(Effect.provide(NodeServices.layer)), |
| 195 | ); | 195 | ); |
apps/server/src/orchestration-v2/ProjectStore.test.ts+2-2| ... | @@ -5,10 +5,10 @@ import * as Layer from "effect/Layer"; | ... | @@ -5,10 +5,10 @@ import * as Layer from "effect/Layer"; |
| 5 | import * as Option from "effect/Option"; | 5 | import * as Option from "effect/Option"; |
| 6 | import * as SqlClient from "effect/sql/SqlClient"; | 6 | import * as SqlClient from "effect/sql/SqlClient"; |
| 7 | 7 | ||
| 8 | import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; | 8 | import * as SqlitePersistence from "../persistence/Layers/Sqlite.ts"; |
| 9 | import * as ProjectStore from "./ProjectStore.ts"; | 9 | import * as ProjectStore from "./ProjectStore.ts"; |
| 10 | 10 | ||
| 11 | it.layer(ProjectStore.layer.pipe(Layer.provideMerge(SqlitePersistenceMemory)))( | 11 | it.layer(ProjectStore.layer.pipe(Layer.provideMerge(SqlitePersistence.layerMemory)))( |
| 12 | "ProjectStoreV2", | 12 | "ProjectStoreV2", |
| 13 | (it) => { | 13 | (it) => { |
| 14 | it.effect("stores a model selection without options as JSON without an options key", () => | 14 | it.effect("stores a model selection without options as JSON without an options key", () => |
apps/server/src/orchestration-v2/ProjectionControlReads.test.ts+5-5| ... | @@ -20,7 +20,7 @@ import * as Effect from "effect/Effect"; | ... | @@ -20,7 +20,7 @@ import * as Effect from "effect/Effect"; |
| 20 | import * as Layer from "effect/Layer"; | 20 | import * as Layer from "effect/Layer"; |
| 21 | import * as Option from "effect/Option"; | 21 | import * as Option from "effect/Option"; |
| 22 | import * as SqlClient from "effect/sql/SqlClient"; | 22 | import * as SqlClient from "effect/sql/SqlClient"; |
| 23 | import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; | 23 | import * as SqlitePersistence from "../persistence/Layers/Sqlite.ts"; |
| 24 | import * as ProjectionStore from "./ProjectionStore.ts"; | 24 | import * as ProjectionStore from "./ProjectionStore.ts"; |
| 25 | import * as ProviderSessionManager from "./ProviderSessionManager.ts"; | 25 | import * as ProviderSessionManager from "./ProviderSessionManager.ts"; |
| 26 | import * as ProviderTurnControlService from "./ProviderTurnControlService.ts"; | 26 | import * as ProviderTurnControlService from "./ProviderTurnControlService.ts"; |
| ... | @@ -190,7 +190,7 @@ const storageCases = (["sqlite", "memory"] as const).map((storage) => ({ | ... | @@ -190,7 +190,7 @@ const storageCases = (["sqlite", "memory"] as const).map((storage) => ({ |
| 190 | storage, | 190 | storage, |
| 191 | storeLayer: | 191 | storeLayer: |
| 192 | storage === "sqlite" | 192 | storage === "sqlite" |
| 193 | ? ProjectionStore.layer.pipe(Layer.provideMerge(SqlitePersistenceMemory)) | 193 | ? ProjectionStore.layer.pipe(Layer.provideMerge(SqlitePersistence.layerMemory)) |
| 194 | : ProjectionStore.layerMemory, | 194 | : ProjectionStore.layerMemory, |
| 195 | })); | 195 | })); |
| 196 | 196 | ||
| ... | @@ -274,7 +274,7 @@ it.effect.each(storageCases)( | ... | @@ -274,7 +274,7 @@ it.effect.each(storageCases)( |
| 274 | assert.instanceOf(missingThread, ProjectionStore.ProjectionStoreThreadNotFoundError); | 274 | assert.instanceOf(missingThread, ProjectionStore.ProjectionStoreThreadNotFoundError); |
| 275 | 275 | ||
| 276 | const calls: string[] = []; | 276 | const calls: string[] = []; |
| 277 | const sessions = Layer.mock(ProviderSessionManager.ProviderSessionManagerV2)({ | 277 | const layerSessions = Layer.mock(ProviderSessionManager.ProviderSessionManagerV2)({ |
| 278 | get: () => | 278 | get: () => |
| 279 | Effect.succeed( | 279 | Effect.succeed( |
| 280 | Option.some({ | 280 | Option.some({ |
| ... | @@ -346,9 +346,9 @@ it.effect.each(storageCases)( | ... | @@ -346,9 +346,9 @@ it.effect.each(storageCases)( |
| 346 | }).pipe( | 346 | }).pipe( |
| 347 | Effect.provide( | 347 | Effect.provide( |
| 348 | Layer.merge(ProviderTurnControlService.layer, RuntimeRequestService.layer).pipe( | 348 | Layer.merge(ProviderTurnControlService.layer, RuntimeRequestService.layer).pipe( |
| 349 | Layer.provide(sessions), | 349 | Layer.provide(layerSessions), |
| 350 | ), | 350 | ), |
| 351 | ), | 351 | ), |
| 352 | ); | 352 | ); |
| 353 | }).pipe(Effect.provide(Layer.merge(storeLayer, SqlitePersistenceMemory))), | 353 | }).pipe(Effect.provide(Layer.merge(storeLayer, SqlitePersistence.layerMemory))), |
| 354 | ); | 354 | ); |
apps/server/src/orchestration-v2/ProjectionRecovery.test.ts+8-8| ... | @@ -22,14 +22,14 @@ import * as Effect from "effect/Effect"; | ... | @@ -22,14 +22,14 @@ import * as Effect from "effect/Effect"; |
| 22 | import * as Layer from "effect/Layer"; | 22 | import * as Layer from "effect/Layer"; |
| 23 | import * as SqlClient from "effect/sql/SqlClient"; | 23 | import * as SqlClient from "effect/sql/SqlClient"; |
| 24 | 24 | ||
| 25 | import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; | 25 | import * as SqlitePersistence from "../persistence/Layers/Sqlite.ts"; |
| 26 | import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts"; | 26 | import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts"; |
| 27 | import * as EffectOutbox from "./EffectOutbox.ts"; | 27 | import * as EffectOutbox from "./EffectOutbox.ts"; |
| 28 | import * as ProjectionStore from "./ProjectionStore.ts"; | 28 | import * as ProjectionStore from "./ProjectionStore.ts"; |
| 29 | import { restartContinuationRun } from "./RestartContinuation.ts"; | 29 | import { restartContinuationRun } from "./RestartContinuation.ts"; |
| 30 | 30 | ||
| 31 | const TestLayer = Layer.mergeAll(ProjectionStore.layer, EffectOutbox.layer).pipe( | 31 | const layerTest = Layer.mergeAll(ProjectionStore.layer, EffectOutbox.layer).pipe( |
| 32 | Layer.provideMerge(SqlitePersistenceMemory), | 32 | Layer.provideMerge(SqlitePersistence.layerMemory), |
| 33 | ); | 33 | ); |
| 34 | const providerInstanceId = ProviderInstanceId.make("codex"); | 34 | const providerInstanceId = ProviderInstanceId.make("codex"); |
| 35 | const modelSelection = { instanceId: providerInstanceId, model: "gpt-5.4" }; | 35 | const modelSelection = { instanceId: providerInstanceId, model: "gpt-5.4" }; |
| ... | @@ -221,7 +221,7 @@ it.effect("selects unfinished recovery work without reading settled thread histo | ... | @@ -221,7 +221,7 @@ it.effect("selects unfinished recovery work without reading settled thread histo |
| 221 | queued, | 221 | queued, |
| 222 | ThreadId.make("thread:recovery:settled-599"), | 222 | ThreadId.make("thread:recovery:settled-599"), |
| 223 | ]); | 223 | ]); |
| 224 | }).pipe(Effect.provide(TestLayer)), | 224 | }).pipe(Effect.provide(layerTest)), |
| 225 | ); | 225 | ); |
| 226 | 226 | ||
| 227 | it.effect("recovers terminal subagent results until their cross-thread transfer exists", () => | 227 | it.effect("recovers terminal subagent results until their cross-thread transfer exists", () => |
| ... | @@ -283,7 +283,7 @@ it.effect("recovers terminal subagent results until their cross-thread transfer | ... | @@ -283,7 +283,7 @@ it.effect("recovers terminal subagent results until their cross-thread transfer |
| 283 | new Set(yield* projections.getUnreadableThreadIds()), | 283 | new Set(yield* projections.getUnreadableThreadIds()), |
| 284 | new Set([parent, completed]), | 284 | new Set([parent, completed]), |
| 285 | ); | 285 | ); |
| 286 | }).pipe(Effect.provide(TestLayer)), | 286 | }).pipe(Effect.provide(layerTest)), |
| 287 | ); | 287 | ); |
| 288 | 288 | ||
| 289 | it.effect("includes shared sessions and provider-owned background rosters in recovery", () => | 289 | it.effect("includes shared sessions and provider-owned background rosters in recovery", () => |
| ... | @@ -417,7 +417,7 @@ it.effect("includes shared sessions and provider-owned background rosters in rec | ... | @@ -417,7 +417,7 @@ it.effect("includes shared sessions and provider-owned background rosters in rec |
| 417 | new Set(yield* projections.getUnreadableThreadIds()), | 417 | new Set(yield* projections.getUnreadableThreadIds()), |
| 418 | new Set([first, second]), | 418 | new Set([first, second]), |
| 419 | ); | 419 | ); |
| 420 | }).pipe(Effect.provide(TestLayer)), | 420 | }).pipe(Effect.provide(layerTest)), |
| 421 | ); | 421 | ); |
| 422 | 422 | ||
| 423 | it.effect("reads the run that owns a background roster, not a queued or resumed one", () => | 423 | it.effect("reads the run that owns a background roster, not a queued or resumed one", () => |
| ... | @@ -484,7 +484,7 @@ it.effect("reads the run that owns a background roster, not a queued or resumed | ... | @@ -484,7 +484,7 @@ it.effect("reads the run that owns a background roster, not a queued or resumed |
| 484 | yield* createRun(quiet, "completed"); | 484 | yield* createRun(quiet, "completed"); |
| 485 | const quietQueued = yield* createRun(quiet, "queued", { ordinal: 2, completedAt: null }); | 485 | const quietQueued = yield* createRun(quiet, "queued", { ordinal: 2, completedAt: null }); |
| 486 | assert.deepEqual(yield* runIds(quiet), [quietQueued]); | 486 | assert.deepEqual(yield* runIds(quiet), [quietQueued]); |
| 487 | }).pipe(Effect.provide(TestLayer)), | 487 | }).pipe(Effect.provide(layerTest)), |
| 488 | ); | 488 | ); |
| 489 | 489 | ||
| 490 | it.effect("marks fork descendants unreadable when their source is missing or corrupt", () => | 490 | it.effect("marks fork descendants unreadable when their source is missing or corrupt", () => |
| ... | @@ -513,5 +513,5 @@ it.effect("marks fork descendants unreadable when their source is missing or cor | ... | @@ -513,5 +513,5 @@ it.effect("marks fork descendants unreadable when their source is missing or cor |
| 513 | new Set(yield* projections.getUnreadableThreadIds()), | 513 | new Set(yield* projections.getUnreadableThreadIds()), |
| 514 | new Set([fork, descendant]), | 514 | new Set([fork, descendant]), |
| 515 | ); | 515 | ); |
| 516 | }).pipe(Effect.provide(TestLayer)), | 516 | }).pipe(Effect.provide(layerTest)), |
| 517 | ); | 517 | ); |
apps/server/src/orchestration-v2/ProjectionSettlement.test.ts+8-8| ... | @@ -21,11 +21,11 @@ import * as Schema from "effect/Schema"; | ... | @@ -21,11 +21,11 @@ import * as Schema from "effect/Schema"; |
| 21 | import * as SqlClient from "effect/sql/SqlClient"; | 21 | import * as SqlClient from "effect/sql/SqlClient"; |
| 22 | import * as Statement from "effect/sql/Statement"; | 22 | import * as Statement from "effect/sql/Statement"; |
| 23 | 23 | ||
| 24 | import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; | 24 | import * as SqlitePersistence from "../persistence/Layers/Sqlite.ts"; |
| 25 | import * as ProjectionStore from "./ProjectionStore.ts"; | 25 | import * as ProjectionStore from "./ProjectionStore.ts"; |
| 26 | import { isAutoSettlementCandidate, resolveAutoSettlementAt } from "./ThreadSettlementService.ts"; | 26 | import { isAutoSettlementCandidate, resolveAutoSettlementAt } from "./ThreadSettlementService.ts"; |
| 27 | 27 | ||
| 28 | const SqlLayer = ProjectionStore.layer.pipe(Layer.provideMerge(SqlitePersistenceMemory)); | 28 | const layerSql = ProjectionStore.layer.pipe(Layer.provideMerge(SqlitePersistence.layerMemory)); |
| 29 | const now = DateTime.makeUnsafe("2026-09-04T12:00:00Z"); | 29 | const now = DateTime.makeUnsafe("2026-09-04T12:00:00Z"); |
| 30 | const old = DateTime.subtract(now, { days: 10 }); | 30 | const old = DateTime.subtract(now, { days: 10 }); |
| 31 | const providerInstanceId = ProviderInstanceId.make("codex"); | 31 | const providerInstanceId = ProviderInstanceId.make("codex"); |
| ... | @@ -141,7 +141,7 @@ const createItem = Effect.fn(function* ( | ... | @@ -141,7 +141,7 @@ const createItem = Effect.fn(function* ( |
| 141 | }); | 141 | }); |
| 142 | 142 | ||
| 143 | it.effect.each([ | 143 | it.effect.each([ |
| 144 | ["sql", SqlLayer], | 144 | ["sql", layerSql], |
| 145 | ["memory", ProjectionStore.layerMemory], | 145 | ["memory", ProjectionStore.layerMemory], |
| 146 | ] as const)( | 146 | ] as const)( |
| 147 | "%s: discovers settlement work with the same activity and background semantics as the shell", | 147 | "%s: discovers settlement work with the same activity and background semantics as the shell", |
| ... | @@ -299,7 +299,7 @@ it.effect.each([ | ... | @@ -299,7 +299,7 @@ it.effect.each([ |
| 299 | ); | 299 | ); |
| 300 | 300 | ||
| 301 | it.effect.each([ | 301 | it.effect.each([ |
| 302 | ["sql", SqlLayer], | 302 | ["sql", layerSql], |
| 303 | ["memory", ProjectionStore.layerMemory], | 303 | ["memory", ProjectionStore.layerMemory], |
| 304 | ] as const)("%s: the user-authored message time ignores agent notifications", ([, testLayer]) => | 304 | ] as const)("%s: the user-authored message time ignores agent notifications", ([, testLayer]) => |
| 305 | Effect.gen(function* () { | 305 | Effect.gen(function* () { |
| ... | @@ -366,7 +366,7 @@ const pullRequestLink = (number: number) => ({ | ... | @@ -366,7 +366,7 @@ const pullRequestLink = (number: number) => ({ |
| 366 | }); | 366 | }); |
| 367 | 367 | ||
| 368 | it.effect.each([ | 368 | it.effect.each([ |
| 369 | ["sql", SqlLayer], | 369 | ["sql", layerSql], |
| 370 | ["memory", ProjectionStore.layerMemory], | 370 | ["memory", ProjectionStore.layerMemory], |
| 371 | ] as const)( | 371 | ] as const)( |
| 372 | "%s: lists only active threads with pull request links, oldest first", | 372 | "%s: lists only active threads with pull request links, oldest first", |
| ... | @@ -405,7 +405,7 @@ it.effect.each([ | ... | @@ -405,7 +405,7 @@ it.effect.each([ |
| 405 | ); | 405 | ); |
| 406 | 406 | ||
| 407 | it.effect.each([ | 407 | it.effect.each([ |
| 408 | ["sql", SqlLayer], | 408 | ["sql", layerSql], |
| 409 | ["memory", ProjectionStore.layerMemory], | 409 | ["memory", ProjectionStore.layerMemory], |
| 410 | ] as const)("%s: an unsettled-only shell read skips settled threads", ([, testLayer]) => | 410 | ] as const)("%s: an unsettled-only shell read skips settled threads", ([, testLayer]) => |
| 411 | Effect.gen(function* () { | 411 | Effect.gen(function* () { |
| ... | @@ -486,7 +486,7 @@ it.effect( | ... | @@ -486,7 +486,7 @@ it.effect( |
| 486 | WHERE thread_id = ${threadId} AND role = 'user' ORDER BY updated_at DESC, message_id DESC LIMIT 1`; | 486 | WHERE thread_id = ${threadId} AND role = 'user' ORDER BY updated_at DESC, message_id DESC LIMIT 1`; |
| 487 | assert.isTrue(messagePlan.some((row) => row.detail.includes("messages_latest_user_idx"))); | 487 | assert.isTrue(messagePlan.some((row) => row.detail.includes("messages_latest_user_idx"))); |
| 488 | assert.isFalse(messagePlan.some((row) => row.detail.includes("TEMP B-TREE"))); | 488 | assert.isFalse(messagePlan.some((row) => row.detail.includes("TEMP B-TREE"))); |
| 489 | }).pipe(Effect.provide(SqlLayer)), | 489 | }).pipe(Effect.provide(layerSql)), |
| 490 | ); | 490 | ); |
| 491 | 491 | ||
| 492 | it.effect("shell failure lookups stay on the thread's own turn items", () => | 492 | it.effect("shell failure lookups stay on the thread's own turn items", () => |
| ... | @@ -525,5 +525,5 @@ it.effect("shell failure lookups stay on the thread's own turn items", () => | ... | @@ -525,5 +525,5 @@ it.effect("shell failure lookups stay on the thread's own turn items", () => |
| 525 | const secretLookups = plan.filter((row) => row.detail.startsWith("SEARCH secret ")); | 525 | const secretLookups = plan.filter((row) => row.detail.startsWith("SEARCH secret ")); |
| 526 | assert.lengthOf(secretLookups, 1); | 526 | assert.lengthOf(secretLookups, 1); |
| 527 | assert.include(secretLookups[0]!.detail, "turn_items_thread_run_idx"); | 527 | assert.include(secretLookups[0]!.detail, "turn_items_thread_run_idx"); |
| 528 | }).pipe(Effect.provide(SqlLayer)), | 528 | }).pipe(Effect.provide(layerSql)), |
| 529 | ); | 529 | ); |
apps/server/src/orchestration-v2/ProjectionStore.test.ts+5-5| ... | @@ -30,7 +30,7 @@ import * as Schema from "effect/Schema"; | ... | @@ -30,7 +30,7 @@ import * as Schema from "effect/Schema"; |
| 30 | import * as SqlClient from "effect/sql/SqlClient"; | 30 | import * as SqlClient from "effect/sql/SqlClient"; |
| 31 | import { projectThreadAwarenessV2 } from "@t3tools/shared/agentAwareness"; | 31 | import { projectThreadAwarenessV2 } from "@t3tools/shared/agentAwareness"; |
| 32 | 32 | ||
| 33 | import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; | 33 | import * as SqlitePersistence from "../persistence/Layers/Sqlite.ts"; |
| 34 | import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts"; | 34 | import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts"; |
| 35 | import * as ProjectionStore from "./ProjectionStore.ts"; | 35 | import * as ProjectionStore from "./ProjectionStore.ts"; |
| 36 | import { | 36 | import { |
| ... | @@ -40,9 +40,9 @@ import { | ... | @@ -40,9 +40,9 @@ import { |
| 40 | THREAD_HISTORY_PAGE_POLICY, | 40 | THREAD_HISTORY_PAGE_POLICY, |
| 41 | } from "./threadHistoryPaging.ts"; | 41 | } from "./threadHistoryPaging.ts"; |
| 42 | 42 | ||
| 43 | const TestLayer = Layer.mergeAll( | 43 | const layerTest = Layer.mergeAll( |
| 44 | ProjectionStore.layer.pipe(Layer.provideMerge(SqlitePersistenceMemory)), | 44 | ProjectionStore.layer.pipe(Layer.provideMerge(SqlitePersistence.layerMemory)), |
| 45 | SqlitePersistenceMemory, | 45 | SqlitePersistence.layerMemory, |
| 46 | ); | 46 | ); |
| 47 | const modelSelection = { | 47 | const modelSelection = { |
| 48 | instanceId: ProviderInstanceId.make("codex"), | 48 | instanceId: ProviderInstanceId.make("codex"), |
| ... | @@ -330,7 +330,7 @@ it.effect("memory recovery selection includes unfinished items from missing runs | ... | @@ -330,7 +330,7 @@ it.effect("memory recovery selection includes unfinished items from missing runs |
| 330 | }).pipe(Effect.provide(ProjectionStore.layerMemory)), | 330 | }).pipe(Effect.provide(ProjectionStore.layerMemory)), |
| 331 | ); | 331 | ); |
| 332 | 332 | ||
| 333 | it.layer(TestLayer)("ProjectionStoreV2", (it) => { | 333 | it.layer(layerTest)("ProjectionStoreV2", (it) => { |
| 334 | it.effect( | 334 | it.effect( |
| 335 | "keeps restart-cancelled work through a stale run.updated", | 335 | "keeps restart-cancelled work through a stale run.updated", |
| 336 | () => restartCancelledWorkSurvivesStaleRunUpdate, | 336 | () => restartCancelledWorkSurvivesStaleRunUpdate, |
apps/server/src/orchestration-v2/ProviderAdapterRegistry.test.ts+4-4| ... | @@ -65,7 +65,7 @@ const instances = [ | ... | @@ -65,7 +65,7 @@ const instances = [ |
| 65 | makeInstance(personalId, personalAdapter), | 65 | makeInstance(personalId, personalAdapter), |
| 66 | makeInstance(workId, workAdapter), | 66 | makeInstance(workId, workAdapter), |
| 67 | ] as const; | 67 | ] as const; |
| 68 | const instanceRegistryLayer = Layer.succeed(ProviderInstanceRegistry.ProviderInstanceRegistry, { | 68 | const layerInstanceRegistry = Layer.succeed(ProviderInstanceRegistry.ProviderInstanceRegistry, { |
| 69 | getInstance: (instanceId) => | 69 | getInstance: (instanceId) => |
| 70 | Effect.succeed(instances.find((instance) => instance.instanceId === instanceId)), | 70 | Effect.succeed(instances.find((instance) => instance.instanceId === instanceId)), |
| 71 | listInstances: Effect.succeed(instances), | 71 | listInstances: Effect.succeed(instances), |
| ... | @@ -73,8 +73,8 @@ const instanceRegistryLayer = Layer.succeed(ProviderInstanceRegistry.ProviderIns | ... | @@ -73,8 +73,8 @@ const instanceRegistryLayer = Layer.succeed(ProviderInstanceRegistry.ProviderIns |
| 73 | streamChanges: Stream.empty, | 73 | streamChanges: Stream.empty, |
| 74 | subscribeChanges: Effect.never, | 74 | subscribeChanges: Effect.never, |
| 75 | }); | 75 | }); |
| 76 | const TestLayer = ProviderAdapterRegistry.layerFromProviderInstanceRegistry.pipe( | 76 | const layerTest = ProviderAdapterRegistry.layerFromProviderInstanceRegistry.pipe( |
| 77 | Layer.provide(instanceRegistryLayer), | 77 | Layer.provide(layerInstanceRegistry), |
| 78 | ); | 78 | ); |
| 79 | 79 | ||
| 80 | it.effect("routes two configured instances of the same driver independently", () => | 80 | it.effect("routes two configured instances of the same driver independently", () => |
| ... | @@ -84,7 +84,7 @@ it.effect("routes two configured instances of the same driver independently", () | ... | @@ -84,7 +84,7 @@ it.effect("routes two configured instances of the same driver independently", () |
| 84 | assert.strictEqual(yield* registry.get(personalId), personalAdapter); | 84 | assert.strictEqual(yield* registry.get(personalId), personalAdapter); |
| 85 | assert.strictEqual(yield* registry.get(workId), workAdapter); | 85 | assert.strictEqual(yield* registry.get(workId), workAdapter); |
| 86 | assert.deepEqual(yield* registry.list(), [personalId, workId]); | 86 | assert.deepEqual(yield* registry.list(), [personalId, workId]); |
| 87 | }).pipe(Effect.provide(TestLayer)), | 87 | }).pipe(Effect.provide(layerTest)), |
| 88 | ); | 88 | ); |
| 89 | 89 | ||
| 90 | const lifecycleDriver = ProviderDriverKind.make("lifecycle-test"); | 90 | const lifecycleDriver = ProviderDriverKind.make("lifecycle-test"); |
apps/server/src/orchestration-v2/ProviderAdapterRegistry.ts+5-5| ... | @@ -184,7 +184,7 @@ function makeRegistry( | ... | @@ -184,7 +184,7 @@ function makeRegistry( |
| 184 | }; | 184 | }; |
| 185 | } | 185 | } |
| 186 | 186 | ||
| 187 | export function makeLayer( | 187 | export function layerFromAdapters( |
| 188 | adapters: ReadonlyArray<ProviderAdapter.ProviderAdapterV2Shape>, | 188 | adapters: ReadonlyArray<ProviderAdapter.ProviderAdapterV2Shape>, |
| 189 | ): Layer.Layer<ProviderAdapterRegistryV2> { | 189 | ): Layer.Layer<ProviderAdapterRegistryV2> { |
| 190 | return Layer.succeed( | 190 | return Layer.succeed( |
| ... | @@ -193,7 +193,7 @@ export function makeLayer( | ... | @@ -193,7 +193,7 @@ export function makeLayer( |
| 193 | ); | 193 | ); |
| 194 | } | 194 | } |
| 195 | 195 | ||
| 196 | export function makeLayerEffect<R, E>( | 196 | export function layerFromAdaptersEffect<R, E>( |
| 197 | adapters: Effect.Effect<ReadonlyArray<ProviderAdapter.ProviderAdapterV2Shape>, E, R>, | 197 | adapters: Effect.Effect<ReadonlyArray<ProviderAdapter.ProviderAdapterV2Shape>, E, R>, |
| 198 | ): Layer.Layer<ProviderAdapterRegistryV2, E, R> { | 198 | ): Layer.Layer<ProviderAdapterRegistryV2, E, R> { |
| 199 | return Layer.effect( | 199 | return Layer.effect( |
| ... | @@ -202,10 +202,10 @@ export function makeLayerEffect<R, E>( | ... | @@ -202,10 +202,10 @@ export function makeLayerEffect<R, E>( |
| 202 | ); | 202 | ); |
| 203 | } | 203 | } |
| 204 | 204 | ||
| 205 | export function makeSingleLayer( | 205 | export function layerSingle( |
| 206 | adapter: ProviderAdapter.ProviderAdapterV2Shape, | 206 | adapter: ProviderAdapter.ProviderAdapterV2Shape, |
| 207 | ): Layer.Layer<ProviderAdapterRegistryV2> { | 207 | ): Layer.Layer<ProviderAdapterRegistryV2> { |
| 208 | return makeLayer([adapter]); | 208 | return layerFromAdapters([adapter]); |
| 209 | } | 209 | } |
| 210 | 210 | ||
| 211 | const decodedConfigEnabled = (config: unknown): boolean | undefined => { | 211 | const decodedConfigEnabled = (config: unknown): boolean | undefined => { |
| ... | @@ -338,7 +338,7 @@ export function makeRegistryFromConfigMap<R>(input: { | ... | @@ -338,7 +338,7 @@ export function makeRegistryFromConfigMap<R>(input: { |
| 338 | }); | 338 | }); |
| 339 | } | 339 | } |
| 340 | 340 | ||
| 341 | export function makeDriverLayer<R>(input: { | 341 | export function layerFromDrivers<R>(input: { |
| 342 | readonly drivers: ReadonlyArray<AnyProviderAdapterDriver<R>>; | 342 | readonly drivers: ReadonlyArray<AnyProviderAdapterDriver<R>>; |
| 343 | readonly configMap: ProviderInstanceConfigMap; | 343 | readonly configMap: ProviderInstanceConfigMap; |
| 344 | }): Layer.Layer<ProviderAdapterRegistryV2, ProviderAdapterRegistryBuildError, R> { | 344 | }): Layer.Layer<ProviderAdapterRegistryV2, ProviderAdapterRegistryBuildError, R> { |
apps/server/src/orchestration-v2/ProviderContinuationService.test.ts+37-35| ... | @@ -88,18 +88,20 @@ const makeGuard = Effect.fnUntraced(function* (completed?: Deferred.Deferred<voi | ... | @@ -88,18 +88,20 @@ const makeGuard = Effect.fnUntraced(function* (completed?: Deferred.Deferred<voi |
| 88 | }; | 88 | }; |
| 89 | }); | 89 | }); |
| 90 | 90 | ||
| 91 | function testLayer(input: { | 91 | function layerTest(input: { |
| 92 | readonly dispatched: Queue.Queue<unknown>; | 92 | readonly dispatched: Queue.Queue<unknown>; |
| 93 | readonly getThreadRecords: () => Effect.Effect<OrchestrationV2ThreadProjection>; | 93 | readonly getThreadRecords: () => Effect.Effect<OrchestrationV2ThreadProjection>; |
| 94 | }) { | 94 | }) { |
| 95 | const threads = Layer.mock(ThreadManagementService.ThreadManagementService)({ | 95 | const layerThreads = Layer.mock(ThreadManagementService.ThreadManagementService)({ |
| 96 | getThreadRecords: input.getThreadRecords, | 96 | getThreadRecords: input.getThreadRecords, |
| 97 | dispatch: (command) => Queue.offer(input.dispatched, command).pipe(Effect.as({} as never)), | 97 | dispatch: (command) => Queue.offer(input.dispatched, command).pipe(Effect.as({} as never)), |
| 98 | }); | 98 | }); |
| 99 | const worker = ProviderContinuationService.workerLive.pipe( | 99 | const layerWorker = ProviderContinuationService.layer.pipe( |
| 100 | Layer.provide(Layer.mergeAll(IdAllocator.layer, ProviderContinuationRequests.layer, threads)), | 100 | Layer.provide( |
| 101 | Layer.mergeAll(IdAllocator.layer, ProviderContinuationRequests.layer, layerThreads), | ||
| 102 | ), | ||
| 101 | ); | 103 | ); |
| 102 | return Layer.merge(ProviderContinuationRequests.layer, worker); | 104 | return Layer.merge(ProviderContinuationRequests.layer, layerWorker); |
| 103 | } | 105 | } |
| 104 | 106 | ||
| 105 | describe("ProviderContinuationService", () => { | 107 | describe("ProviderContinuationService", () => { |
| ... | @@ -145,7 +147,7 @@ describe("ProviderContinuationService", () => { | ... | @@ -145,7 +147,7 @@ describe("ProviderContinuationService", () => { |
| 145 | assert.equal(command.delegatedCompletion.taskIds.length, 2); | 147 | assert.equal(command.delegatedCompletion.taskIds.length, 2); |
| 146 | }).pipe( | 148 | }).pipe( |
| 147 | Effect.provide( | 149 | Effect.provide( |
| 148 | testLayer({ dispatched, getThreadRecords: () => Effect.succeed(recovered) }), | 150 | layerTest({ dispatched, getThreadRecords: () => Effect.succeed(recovered) }), |
| 149 | ), | 151 | ), |
| 150 | Effect.scoped, | 152 | Effect.scoped, |
| 151 | ); | 153 | ); |
| ... | @@ -171,7 +173,7 @@ describe("ProviderContinuationService", () => { | ... | @@ -171,7 +173,7 @@ describe("ProviderContinuationService", () => { |
| 171 | }); | 173 | }); |
| 172 | }).pipe( | 174 | }).pipe( |
| 173 | Effect.provide( | 175 | Effect.provide( |
| 174 | testLayer({ dispatched, getThreadRecords: () => Effect.succeed(projection) }), | 176 | layerTest({ dispatched, getThreadRecords: () => Effect.succeed(projection) }), |
| 175 | ), | 177 | ), |
| 176 | Effect.scoped, | 178 | Effect.scoped, |
| 177 | ); | 179 | ); |
| ... | @@ -213,7 +215,7 @@ describe("ProviderContinuationService", () => { | ... | @@ -213,7 +215,7 @@ describe("ProviderContinuationService", () => { |
| 213 | }); | 215 | }); |
| 214 | }).pipe( | 216 | }).pipe( |
| 215 | Effect.provide( | 217 | Effect.provide( |
| 216 | testLayer({ dispatched, getThreadRecords: () => Effect.succeed(projection) }), | 218 | layerTest({ dispatched, getThreadRecords: () => Effect.succeed(projection) }), |
| 217 | ), | 219 | ), |
| 218 | Effect.scoped, | 220 | Effect.scoped, |
| 219 | ); | 221 | ); |
| ... | @@ -262,7 +264,7 @@ describe("ProviderContinuationService", () => { | ... | @@ -262,7 +264,7 @@ describe("ProviderContinuationService", () => { |
| 262 | assert.include(command.text, "task_status"); | 264 | assert.include(command.text, "task_status"); |
| 263 | }).pipe( | 265 | }).pipe( |
| 264 | Effect.provide( | 266 | Effect.provide( |
| 265 | testLayer({ | 267 | layerTest({ |
| 266 | dispatched, | 268 | dispatched, |
| 267 | getThreadRecords: () => Effect.succeed(delegatedProjection()), | 269 | getThreadRecords: () => Effect.succeed(delegatedProjection()), |
| 268 | }), | 270 | }), |
| ... | @@ -276,7 +278,7 @@ describe("ProviderContinuationService", () => { | ... | @@ -276,7 +278,7 @@ describe("ProviderContinuationService", () => { |
| 276 | return Effect.gen(function* () { | 278 | return Effect.gen(function* () { |
| 277 | const attempts = yield* Ref.make(0); | 279 | const attempts = yield* Ref.make(0); |
| 278 | const dispatched = yield* Queue.unbounded<unknown>(); | 280 | const dispatched = yield* Queue.unbounded<unknown>(); |
| 279 | const threads = Layer.mock(ThreadManagementService.ThreadManagementService)({ | 281 | const layerThreads = Layer.mock(ThreadManagementService.ThreadManagementService)({ |
| 280 | getThreadRecords: () => Effect.succeed(delegatedProjection()), | 282 | getThreadRecords: () => Effect.succeed(delegatedProjection()), |
| 281 | dispatch: (command) => | 283 | dispatch: (command) => |
| 282 | Ref.getAndUpdate(attempts, (count) => count + 1).pipe( | 284 | Ref.getAndUpdate(attempts, (count) => count + 1).pipe( |
| ... | @@ -287,9 +289,9 @@ describe("ProviderContinuationService", () => { | ... | @@ -287,9 +289,9 @@ describe("ProviderContinuationService", () => { |
| 287 | ), | 289 | ), |
| 288 | ), | 290 | ), |
| 289 | }); | 291 | }); |
| 290 | const worker = ProviderContinuationService.workerLive.pipe( | 292 | const layerWorker = ProviderContinuationService.layer.pipe( |
| 291 | Layer.provide( | 293 | Layer.provide( |
| 292 | Layer.mergeAll(IdAllocator.layer, ProviderContinuationRequests.layer, threads), | 294 | Layer.mergeAll(IdAllocator.layer, ProviderContinuationRequests.layer, layerThreads), |
| 293 | ), | 295 | ), |
| 294 | ); | 296 | ); |
| 295 | 297 | ||
| ... | @@ -318,7 +320,7 @@ describe("ProviderContinuationService", () => { | ... | @@ -318,7 +320,7 @@ describe("ProviderContinuationService", () => { |
| 318 | assert.equal(command.messageId, delegatedMessageId); | 320 | assert.equal(command.messageId, delegatedMessageId); |
| 319 | assert.equal(yield* Ref.get(attempts), 2); | 321 | assert.equal(yield* Ref.get(attempts), 2); |
| 320 | }).pipe( | 322 | }).pipe( |
| 321 | Effect.provide(Layer.merge(ProviderContinuationRequests.layer, worker)), | 323 | Effect.provide(Layer.merge(ProviderContinuationRequests.layer, layerWorker)), |
| 322 | Effect.scoped, | 324 | Effect.scoped, |
| 323 | ); | 325 | ); |
| 324 | }); | 326 | }); |
| ... | @@ -328,7 +330,7 @@ describe("ProviderContinuationService", () => { | ... | @@ -328,7 +330,7 @@ describe("ProviderContinuationService", () => { |
| 328 | return Effect.gen(function* () { | 330 | return Effect.gen(function* () { |
| 329 | const attempts = yield* Ref.make(0); | 331 | const attempts = yield* Ref.make(0); |
| 330 | const disposition = yield* Ref.make<"open" | "disposed">("open"); | 332 | const disposition = yield* Ref.make<"open" | "disposed">("open"); |
| 331 | const threads = Layer.mock(ThreadManagementService.ThreadManagementService)({ | 333 | const layerThreads = Layer.mock(ThreadManagementService.ThreadManagementService)({ |
| 332 | getThreadRecords: () => | 334 | getThreadRecords: () => |
| 333 | Ref.get(disposition).pipe(Effect.map((state) => delegatedProjection(state))), | 335 | Ref.get(disposition).pipe(Effect.map((state) => delegatedProjection(state))), |
| 334 | dispatch: () => | 336 | dispatch: () => |
| ... | @@ -336,9 +338,9 @@ describe("ProviderContinuationService", () => { | ... | @@ -336,9 +338,9 @@ describe("ProviderContinuationService", () => { |
| 336 | Effect.andThen(Effect.fail(new Error("simulated persistent failure") as never)), | 338 | Effect.andThen(Effect.fail(new Error("simulated persistent failure") as never)), |
| 337 | ), | 339 | ), |
| 338 | }); | 340 | }); |
| 339 | const worker = ProviderContinuationService.workerLive.pipe( | 341 | const layerWorker = ProviderContinuationService.layer.pipe( |
| 340 | Layer.provide( | 342 | Layer.provide( |
| 341 | Layer.mergeAll(IdAllocator.layer, ProviderContinuationRequests.layer, threads), | 343 | Layer.mergeAll(IdAllocator.layer, ProviderContinuationRequests.layer, layerThreads), |
| 342 | ), | 344 | ), |
| 343 | ); | 345 | ); |
| 344 | 346 | ||
| ... | @@ -372,7 +374,7 @@ describe("ProviderContinuationService", () => { | ... | @@ -372,7 +374,7 @@ describe("ProviderContinuationService", () => { |
| 372 | yield* Ref.set(disposition, "disposed"); | 374 | yield* Ref.set(disposition, "disposed"); |
| 373 | yield* TestClock.adjust("400 millis"); | 375 | yield* TestClock.adjust("400 millis"); |
| 374 | }).pipe( | 376 | }).pipe( |
| 375 | Effect.provide(Layer.merge(ProviderContinuationRequests.layer, worker)), | 377 | Effect.provide(Layer.merge(ProviderContinuationRequests.layer, layerWorker)), |
| 376 | Effect.scoped, | 378 | Effect.scoped, |
| 377 | ); | 379 | ); |
| 378 | }); | 380 | }); |
| ... | @@ -385,7 +387,7 @@ describe("ProviderContinuationService", () => { | ... | @@ -385,7 +387,7 @@ describe("ProviderContinuationService", () => { |
| 385 | const blockedProjectionReads = yield* Ref.make(0); | 387 | const blockedProjectionReads = yield* Ref.make(0); |
| 386 | const blockedRequestDropped = yield* Deferred.make<void>(); | 388 | const blockedRequestDropped = yield* Deferred.make<void>(); |
| 387 | const state = yield* Ref.make<"open" | "blocked" | "disposed">("open"); | 389 | const state = yield* Ref.make<"open" | "blocked" | "disposed">("open"); |
| 388 | const threads = Layer.mock(ThreadManagementService.ThreadManagementService)({ | 390 | const layerThreads = Layer.mock(ThreadManagementService.ThreadManagementService)({ |
| 389 | getThreadRecords: () => | 391 | getThreadRecords: () => |
| 390 | Effect.gen(function* () { | 392 | Effect.gen(function* () { |
| 391 | const currentState = yield* Ref.get(state); | 393 | const currentState = yield* Ref.get(state); |
| ... | @@ -411,9 +413,9 @@ describe("ProviderContinuationService", () => { | ... | @@ -411,9 +413,9 @@ describe("ProviderContinuationService", () => { |
| 411 | Effect.andThen(Effect.fail(new Error("simulated persistent failure") as never)), | 413 | Effect.andThen(Effect.fail(new Error("simulated persistent failure") as never)), |
| 412 | ), | 414 | ), |
| 413 | }); | 415 | }); |
| 414 | const worker = ProviderContinuationService.workerLive.pipe( | 416 | const layerWorker = ProviderContinuationService.layer.pipe( |
| 415 | Layer.provide( | 417 | Layer.provide( |
| 416 | Layer.mergeAll(IdAllocator.layer, ProviderContinuationRequests.layer, threads), | 418 | Layer.mergeAll(IdAllocator.layer, ProviderContinuationRequests.layer, layerThreads), |
| 417 | ), | 419 | ), |
| 418 | ); | 420 | ); |
| 419 | const completionRequest = { | 421 | const completionRequest = { |
| ... | @@ -455,7 +457,7 @@ describe("ProviderContinuationService", () => { | ... | @@ -455,7 +457,7 @@ describe("ProviderContinuationService", () => { |
| 455 | yield* Ref.set(state, "disposed"); | 457 | yield* Ref.set(state, "disposed"); |
| 456 | yield* TestClock.adjust("200 millis"); | 458 | yield* TestClock.adjust("200 millis"); |
| 457 | }).pipe( | 459 | }).pipe( |
| 458 | Effect.provide(Layer.merge(ProviderContinuationRequests.layer, worker)), | 460 | Effect.provide(Layer.merge(ProviderContinuationRequests.layer, layerWorker)), |
| 459 | Effect.scoped, | 461 | Effect.scoped, |
| 460 | ); | 462 | ); |
| 461 | } | 463 | } |
| ... | @@ -466,7 +468,7 @@ describe("ProviderContinuationService", () => { | ... | @@ -466,7 +468,7 @@ describe("ProviderContinuationService", () => { |
| 466 | return Effect.gen(function* () { | 468 | return Effect.gen(function* () { |
| 467 | const attempts = yield* Ref.make(0); | 469 | const attempts = yield* Ref.make(0); |
| 468 | const disposition = yield* Ref.make<"open" | "disposed">("open"); | 470 | const disposition = yield* Ref.make<"open" | "disposed">("open"); |
| 469 | const threads = Layer.mock(ThreadManagementService.ThreadManagementService)({ | 471 | const layerThreads = Layer.mock(ThreadManagementService.ThreadManagementService)({ |
| 470 | getThreadRecords: () => | 472 | getThreadRecords: () => |
| 471 | Ref.get(disposition).pipe(Effect.map((state) => delegatedProjection(state))), | 473 | Ref.get(disposition).pipe(Effect.map((state) => delegatedProjection(state))), |
| 472 | dispatch: () => | 474 | dispatch: () => |
| ... | @@ -474,9 +476,9 @@ describe("ProviderContinuationService", () => { | ... | @@ -474,9 +476,9 @@ describe("ProviderContinuationService", () => { |
| 474 | Effect.andThen(Effect.fail(new Error("simulated dispatch failure") as never)), | 476 | Effect.andThen(Effect.fail(new Error("simulated dispatch failure") as never)), |
| 475 | ), | 477 | ), |
| 476 | }); | 478 | }); |
| 477 | const worker = ProviderContinuationService.workerLive.pipe( | 479 | const layerWorker = ProviderContinuationService.layer.pipe( |
| 478 | Layer.provide( | 480 | Layer.provide( |
| 479 | Layer.mergeAll(IdAllocator.layer, ProviderContinuationRequests.layer, threads), | 481 | Layer.mergeAll(IdAllocator.layer, ProviderContinuationRequests.layer, layerThreads), |
| 480 | ), | 482 | ), |
| 481 | ); | 483 | ); |
| 482 | 484 | ||
| ... | @@ -502,7 +504,7 @@ describe("ProviderContinuationService", () => { | ... | @@ -502,7 +504,7 @@ describe("ProviderContinuationService", () => { |
| 502 | yield* Effect.yieldNow; | 504 | yield* Effect.yieldNow; |
| 503 | assert.equal(yield* Ref.get(attempts), 1); | 505 | assert.equal(yield* Ref.get(attempts), 1); |
| 504 | }).pipe( | 506 | }).pipe( |
| 505 | Effect.provide(Layer.merge(ProviderContinuationRequests.layer, worker)), | 507 | Effect.provide(Layer.merge(ProviderContinuationRequests.layer, layerWorker)), |
| 506 | Effect.scoped, | 508 | Effect.scoped, |
| 507 | ); | 509 | ); |
| 508 | }); | 510 | }); |
| ... | @@ -531,7 +533,7 @@ describe("ProviderContinuationService", () => { | ... | @@ -531,7 +533,7 @@ describe("ProviderContinuationService", () => { |
| 531 | assert.deepEqual(command.dispatchMode, { type: "queue_after_active" }); | 533 | assert.deepEqual(command.dispatchMode, { type: "queue_after_active" }); |
| 532 | }).pipe( | 534 | }).pipe( |
| 533 | Effect.provide( | 535 | Effect.provide( |
| 534 | testLayer({ | 536 | layerTest({ |
| 535 | dispatched, | 537 | dispatched, |
| 536 | getThreadRecords: () => Effect.succeed(delegatedProjection()), | 538 | getThreadRecords: () => Effect.succeed(delegatedProjection()), |
| 537 | }), | 539 | }), |
| ... | @@ -566,7 +568,7 @@ describe("ProviderContinuationService", () => { | ... | @@ -566,7 +568,7 @@ describe("ProviderContinuationService", () => { |
| 566 | assert.isTrue(Option.isNone(yield* Queue.poll(dispatched))); | 568 | assert.isTrue(Option.isNone(yield* Queue.poll(dispatched))); |
| 567 | }).pipe( | 569 | }).pipe( |
| 568 | Effect.provide( | 570 | Effect.provide( |
| 569 | testLayer({ | 571 | layerTest({ |
| 570 | dispatched, | 572 | dispatched, |
| 571 | getThreadRecords: () => Effect.succeed(delegatedProjection(disposition)), | 573 | getThreadRecords: () => Effect.succeed(delegatedProjection(disposition)), |
| 572 | }), | 574 | }), |
| ... | @@ -600,7 +602,7 @@ describe("ProviderContinuationService", () => { | ... | @@ -600,7 +602,7 @@ describe("ProviderContinuationService", () => { |
| 600 | assert.isTrue(Option.isNone(yield* Queue.poll(dispatched))); | 602 | assert.isTrue(Option.isNone(yield* Queue.poll(dispatched))); |
| 601 | }).pipe( | 603 | }).pipe( |
| 602 | Effect.provide( | 604 | Effect.provide( |
| 603 | testLayer({ | 605 | layerTest({ |
| 604 | dispatched, | 606 | dispatched, |
| 605 | getThreadRecords: () => | 607 | getThreadRecords: () => |
| 606 | Effect.succeed({ | 608 | Effect.succeed({ |
| ... | @@ -637,7 +639,7 @@ describe("ProviderContinuationService", () => { | ... | @@ -637,7 +639,7 @@ describe("ProviderContinuationService", () => { |
| 637 | assert.isTrue(Option.isNone(yield* Queue.poll(dispatched))); | 639 | assert.isTrue(Option.isNone(yield* Queue.poll(dispatched))); |
| 638 | }).pipe( | 640 | }).pipe( |
| 639 | Effect.provide( | 641 | Effect.provide( |
| 640 | testLayer({ | 642 | layerTest({ |
| 641 | dispatched, | 643 | dispatched, |
| 642 | getThreadRecords: () => | 644 | getThreadRecords: () => |
| 643 | Effect.succeed({ | 645 | Effect.succeed({ |
| ... | @@ -666,7 +668,7 @@ describe("ProviderContinuationService", () => { | ... | @@ -666,7 +668,7 @@ describe("ProviderContinuationService", () => { |
| 666 | assert.isTrue(Option.isNone(yield* Queue.poll(dispatched))); | 668 | assert.isTrue(Option.isNone(yield* Queue.poll(dispatched))); |
| 667 | }).pipe( | 669 | }).pipe( |
| 668 | Effect.provide( | 670 | Effect.provide( |
| 669 | testLayer({ dispatched, getThreadRecords: () => Effect.succeed(projection) }), | 671 | layerTest({ dispatched, getThreadRecords: () => Effect.succeed(projection) }), |
| 670 | ), | 672 | ), |
| 671 | Effect.scoped, | 673 | Effect.scoped, |
| 672 | ); | 674 | ); |
| ... | @@ -685,7 +687,7 @@ describe("ProviderContinuationService", () => { | ... | @@ -685,7 +687,7 @@ describe("ProviderContinuationService", () => { |
| 685 | }); | 687 | }); |
| 686 | }).pipe( | 688 | }).pipe( |
| 687 | Effect.provide( | 689 | Effect.provide( |
| 688 | testLayer({ dispatched, getThreadRecords: () => Effect.succeed(projection) }), | 690 | layerTest({ dispatched, getThreadRecords: () => Effect.succeed(projection) }), |
| 689 | ), | 691 | ), |
| 690 | Effect.scoped, | 692 | Effect.scoped, |
| 691 | ); | 693 | ); |
| ... | @@ -706,7 +708,7 @@ describe("ProviderContinuationService", () => { | ... | @@ -706,7 +708,7 @@ describe("ProviderContinuationService", () => { |
| 706 | assert.isTrue(Option.isNone(yield* Queue.poll(dispatched))); | 708 | assert.isTrue(Option.isNone(yield* Queue.poll(dispatched))); |
| 707 | }).pipe( | 709 | }).pipe( |
| 708 | Effect.provide( | 710 | Effect.provide( |
| 709 | testLayer({ dispatched, getThreadRecords: () => Effect.succeed(projection) }), | 711 | layerTest({ dispatched, getThreadRecords: () => Effect.succeed(projection) }), |
| 710 | ), | 712 | ), |
| 711 | Effect.scoped, | 713 | Effect.scoped, |
| 712 | ); | 714 | ); |
| ... | @@ -731,7 +733,7 @@ describe("ProviderContinuationService", () => { | ... | @@ -731,7 +733,7 @@ describe("ProviderContinuationService", () => { |
| 731 | assert.isTrue(Option.isNone(yield* Queue.poll(dispatched))); | 733 | assert.isTrue(Option.isNone(yield* Queue.poll(dispatched))); |
| 732 | }).pipe( | 734 | }).pipe( |
| 733 | Effect.provide( | 735 | Effect.provide( |
| 734 | testLayer({ | 736 | layerTest({ |
| 735 | dispatched, | 737 | dispatched, |
| 736 | getThreadRecords: () => | 738 | getThreadRecords: () => |
| 737 | Deferred.succeed(projectionEntered, undefined).pipe( | 739 | Deferred.succeed(projectionEntered, undefined).pipe( |
| ... | @@ -766,7 +768,7 @@ describe("ProviderContinuationService", () => { | ... | @@ -766,7 +768,7 @@ describe("ProviderContinuationService", () => { |
| 766 | assert.isTrue(Option.isNone(yield* Queue.poll(dispatched))); | 768 | assert.isTrue(Option.isNone(yield* Queue.poll(dispatched))); |
| 767 | }).pipe( | 769 | }).pipe( |
| 768 | Effect.provide( | 770 | Effect.provide( |
| 769 | testLayer({ | 771 | layerTest({ |
| 770 | dispatched, | 772 | dispatched, |
| 771 | getThreadRecords: () => { | 773 | getThreadRecords: () => { |
| 772 | projectionCalls += 1; | 774 | projectionCalls += 1; |
| ... | @@ -801,7 +803,7 @@ describe("ProviderContinuationService", () => { | ... | @@ -801,7 +803,7 @@ describe("ProviderContinuationService", () => { |
| 801 | assert.isTrue(Option.isNone(yield* Queue.poll(dispatched))); | 803 | assert.isTrue(Option.isNone(yield* Queue.poll(dispatched))); |
| 802 | }).pipe( | 804 | }).pipe( |
| 803 | Effect.provide( | 805 | Effect.provide( |
| 804 | testLayer({ | 806 | layerTest({ |
| 805 | dispatched, | 807 | dispatched, |
| 806 | getThreadRecords: () => | 808 | getThreadRecords: () => |
| 807 | Effect.succeed({ | 809 | Effect.succeed({ |
apps/server/src/orchestration-v2/ProviderContinuationService.ts+1-1| ... | @@ -57,7 +57,7 @@ function delegatedCompletionRetryKey( | ... | @@ -57,7 +57,7 @@ function delegatedCompletionRetryKey( |
| 57 | * the orchestrator selects native steering or queued delivery under its thread | 57 | * the orchestrator selects native steering or queued delivery under its thread |
| 58 | * lock. Adapter-buffered continuations still queue behind active work. | 58 | * lock. Adapter-buffered continuations still queue behind active work. |
| 59 | */ | 59 | */ |
| 60 | export const workerLive = Layer.effectDiscard( | 60 | export const layer = Layer.effectDiscard( |
| 61 | Effect.gen(function* () { | 61 | Effect.gen(function* () { |
| 62 | const ids = yield* IdAllocator.IdAllocatorV2; | 62 | const ids = yield* IdAllocator.IdAllocatorV2; |
| 63 | const requests = yield* ProviderContinuationRequests.ProviderContinuationRequests; | 63 | const requests = yield* ProviderContinuationRequests.ProviderContinuationRequests; |
apps/server/src/orchestration-v2/ProviderEventIngestor.test.ts+14-14| ... | @@ -27,7 +27,7 @@ import * as Layer from "effect/Layer"; | ... | @@ -27,7 +27,7 @@ import * as Layer from "effect/Layer"; |
| 27 | import * as Stream from "effect/Stream"; | 27 | import * as Stream from "effect/Stream"; |
| 28 | import * as TestClock from "effect/testing/TestClock"; | 28 | import * as TestClock from "effect/testing/TestClock"; |
| 29 | 29 | ||
| 30 | import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; | 30 | import * as SqlitePersistence from "../persistence/Layers/Sqlite.ts"; |
| 31 | import * as EventSink from "./EventSink.ts"; | 31 | import * as EventSink from "./EventSink.ts"; |
| 32 | import * as EventStore from "./EventStore.ts"; | 32 | import * as EventStore from "./EventStore.ts"; |
| 33 | import * as IdAllocator from "./IdAllocator.ts"; | 33 | import * as IdAllocator from "./IdAllocator.ts"; |
| ... | @@ -42,25 +42,25 @@ import { | ... | @@ -42,25 +42,25 @@ import { |
| 42 | selectInheritedBackgroundTurnItems, | 42 | selectInheritedBackgroundTurnItems, |
| 43 | } from "./RunExecutionService.ts"; | 43 | } from "./RunExecutionService.ts"; |
| 44 | 44 | ||
| 45 | const TestDatabaseLayer = SqlitePersistenceMemory; | 45 | const layerTestDatabase = SqlitePersistence.layerMemory; |
| 46 | const TestStoresLayer = Layer.merge(EventStore.layer, ProjectionStore.layer).pipe( | 46 | const layerTestStores = Layer.merge(EventStore.layer, ProjectionStore.layer).pipe( |
| 47 | Layer.provide(TestDatabaseLayer), | 47 | Layer.provide(layerTestDatabase), |
| 48 | ); | 48 | ); |
| 49 | 49 | ||
| 50 | const TestEventSinkLayer = EventSink.layer.pipe( | 50 | const layerTestEventSink = EventSink.layer.pipe( |
| 51 | Layer.provide(Layer.mergeAll(TestStoresLayer, TestDatabaseLayer)), | 51 | Layer.provide(Layer.mergeAll(layerTestStores, layerTestDatabase)), |
| 52 | ); | 52 | ); |
| 53 | 53 | ||
| 54 | const TestLayer = Layer.mergeAll( | 54 | const layerTest = Layer.mergeAll( |
| 55 | TestStoresLayer, | 55 | layerTestStores, |
| 56 | TestEventSinkLayer, | 56 | layerTestEventSink, |
| 57 | IdAllocator.layer, | 57 | IdAllocator.layer, |
| 58 | ThreadCommandExecutor.layer, | 58 | ThreadCommandExecutor.layer, |
| 59 | ProviderEventIngestor.layer.pipe( | 59 | ProviderEventIngestor.layer.pipe( |
| 60 | Layer.provide( | 60 | Layer.provide( |
| 61 | Layer.mergeAll( | 61 | Layer.mergeAll( |
| 62 | TestStoresLayer, | 62 | layerTestStores, |
| 63 | TestEventSinkLayer, | 63 | layerTestEventSink, |
| 64 | IdAllocator.layer, | 64 | IdAllocator.layer, |
| 65 | ThreadCommandExecutor.layer, | 65 | ThreadCommandExecutor.layer, |
| 66 | ), | 66 | ), |
| ... | @@ -133,11 +133,11 @@ function threadCreatedEvent( | ... | @@ -133,11 +133,11 @@ function threadCreatedEvent( |
| 133 | }); | 133 | }); |
| 134 | } | 134 | } |
| 135 | 135 | ||
| 136 | const layer = it.layer(TestLayer); | 136 | const layer = it.layer(layerTest); |
| 137 | 137 | ||
| 138 | it.effect("records accepted billed turn usage once without billing the context window", () => { | 138 | it.effect("records accepted billed turn usage once without billing the context window", () => { |
| 139 | const recorded: Array<Readonly<Record<string, unknown>>> = []; | 139 | const recorded: Array<Readonly<Record<string, unknown>>> = []; |
| 140 | const analytics = Layer.succeed(ProviderEventIngestor.ProviderTurnAnalytics, { | 140 | const layerAnalytics = Layer.succeed(ProviderEventIngestor.ProviderTurnAnalytics, { |
| 141 | record: (properties: Readonly<Record<string, unknown>>) => | 141 | record: (properties: Readonly<Record<string, unknown>>) => |
| 142 | Effect.sync(() => { | 142 | Effect.sync(() => { |
| 143 | recorded.push(properties); | 143 | recorded.push(properties); |
| ... | @@ -232,7 +232,7 @@ it.effect("records accepted billed turn usage once without billing the context w | ... | @@ -232,7 +232,7 @@ it.effect("records accepted billed turn usage once without billing the context w |
| 232 | interactionMode: "default", | 232 | interactionMode: "default", |
| 233 | durationMs: 120, | 233 | durationMs: 120, |
| 234 | }); | 234 | }); |
| 235 | }).pipe(Effect.provide(TestLayer.pipe(Layer.provide(analytics)))); | 235 | }).pipe(Effect.provide(layerTest.pipe(Layer.provide(layerAnalytics)))); |
| 236 | }); | 236 | }); |
| 237 | 237 | ||
| 238 | layer("ProviderEventIngestorV2", (it) => { | 238 | layer("ProviderEventIngestorV2", (it) => { |
apps/server/src/orchestration-v2/ProviderEventIngestor.ts+1-1| ... | @@ -79,7 +79,7 @@ export class ProviderTurnAnalytics extends Context.Reference<{ | ... | @@ -79,7 +79,7 @@ export class ProviderTurnAnalytics extends Context.Reference<{ |
| 79 | defaultValue: () => ({ record: () => Effect.void }), | 79 | defaultValue: () => ({ record: () => Effect.void }), |
| 80 | }) {} | 80 | }) {} |
| 81 | 81 | ||
| 82 | export const analyticsLive = Layer.effect( | 82 | export const layerAnalytics = Layer.effect( |
| 83 | ProviderTurnAnalytics, | 83 | ProviderTurnAnalytics, |
| 84 | Effect.gen(function* () { | 84 | Effect.gen(function* () { |
| 85 | const analytics = yield* AnalyticsService.AnalyticsService; | 85 | const analytics = yield* AnalyticsService.AnalyticsService; |
apps/server/src/orchestration-v2/ProviderRuntimeRecoveryPerformance.test.ts+7-7| ... | @@ -25,7 +25,7 @@ import * as Effect from "effect/Effect"; | ... | @@ -25,7 +25,7 @@ import * as Effect from "effect/Effect"; |
| 25 | import * as Layer from "effect/Layer"; | 25 | import * as Layer from "effect/Layer"; |
| 26 | import * as SqlClient from "effect/sql/SqlClient"; | 26 | import * as SqlClient from "effect/sql/SqlClient"; |
| 27 | 27 | ||
| 28 | import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; | 28 | import * as SqlitePersistence from "../persistence/Layers/Sqlite.ts"; |
| 29 | import * as ServerSettings from "../serverSettings.ts"; | 29 | import * as ServerSettings from "../serverSettings.ts"; |
| 30 | import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts"; | 30 | import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts"; |
| 31 | import * as EffectOutbox from "./EffectOutbox.ts"; | 31 | import * as EffectOutbox from "./EffectOutbox.ts"; |
| ... | @@ -40,14 +40,14 @@ import * as ProviderRuntimeRecovery from "./ProviderRuntimeRecoveryService.ts"; | ... | @@ -40,14 +40,14 @@ import * as ProviderRuntimeRecovery from "./ProviderRuntimeRecoveryService.ts"; |
| 40 | // `it.live` keeps a real clock: each reconcile gets a fresh command id, so the | 40 | // `it.live` keeps a real clock: each reconcile gets a fresh command id, so the |
| 41 | // second recover cannot hide behind command receipt dedup. | 41 | // second recover cannot hide behind command receipt dedup. |
| 42 | 42 | ||
| 43 | const stores = Layer.mergeAll( | 43 | const layerStores = Layer.mergeAll( |
| 44 | EventStore.layer, | 44 | EventStore.layer, |
| 45 | ProjectionStore.layer, | 45 | ProjectionStore.layer, |
| 46 | EffectOutbox.layer, | 46 | EffectOutbox.layer, |
| 47 | IdAllocator.layer, | 47 | IdAllocator.layer, |
| 48 | ).pipe(Layer.provideMerge(SqlitePersistenceMemory)); | 48 | ).pipe(Layer.provideMerge(SqlitePersistence.layerMemory)); |
| 49 | const TestLayer = ProviderRuntimeRecovery.layer.pipe( | 49 | const layerTest = ProviderRuntimeRecovery.layer.pipe( |
| 50 | Layer.provideMerge(EventSink.layer.pipe(Layer.provideMerge(stores))), | 50 | Layer.provideMerge(EventSink.layer.pipe(Layer.provideMerge(layerStores))), |
| 51 | Layer.provideMerge(ServerSettings.layerTest({ continueThreadsAfterServerUpdate: true })), | 51 | Layer.provideMerge(ServerSettings.layerTest({ continueThreadsAfterServerUpdate: true })), |
| 52 | ); | 52 | ); |
| 53 | 53 | ||
| ... | @@ -440,8 +440,8 @@ const measureCrash = Effect.fn(function* (scenario: Scenario) { | ... | @@ -440,8 +440,8 @@ const measureCrash = Effect.fn(function* (scenario: Scenario) { |
| 440 | }); | 440 | }); |
| 441 | 441 | ||
| 442 | const measure = Effect.fn(function* (scenario: Scenario) { | 442 | const measure = Effect.fn(function* (scenario: Scenario) { |
| 443 | const graceful = yield* measureGraceful(scenario).pipe(Effect.provide(Layer.fresh(TestLayer))); | 443 | const graceful = yield* measureGraceful(scenario).pipe(Effect.provide(Layer.fresh(layerTest))); |
| 444 | const crash = yield* measureCrash(scenario).pipe(Effect.provide(Layer.fresh(TestLayer))); | 444 | const crash = yield* measureCrash(scenario).pipe(Effect.provide(Layer.fresh(layerTest))); |
| 445 | return { ...scenario, ...graceful, ...crash } satisfies Measurement; | 445 | return { ...scenario, ...graceful, ...crash } satisfies Measurement; |
| 446 | }); | 446 | }); |
| 447 | 447 |
apps/server/src/orchestration-v2/ProviderSessionManager.test.ts+69-71| ... | @@ -36,7 +36,7 @@ import * as ServerEnvironment from "../environment/ServerEnvironment.ts"; | ... | @@ -36,7 +36,7 @@ import * as ServerEnvironment from "../environment/ServerEnvironment.ts"; |
| 36 | import * as ProjectService from "../project/ProjectService.ts"; | 36 | import * as ProjectService from "../project/ProjectService.ts"; |
| 37 | import * as McpProviderSession from "../mcp/McpProviderSession.ts"; | 37 | import * as McpProviderSession from "../mcp/McpProviderSession.ts"; |
| 38 | import * as McpSessionRegistry from "../mcp/McpSessionRegistry.ts"; | 38 | import * as McpSessionRegistry from "../mcp/McpSessionRegistry.ts"; |
| 39 | import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; | 39 | import * as SqlitePersistence from "../persistence/Layers/Sqlite.ts"; |
| 40 | import * as ServerSettings from "../serverSettings.ts"; | 40 | import * as ServerSettings from "../serverSettings.ts"; |
| 41 | import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts"; | 41 | import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts"; |
| 42 | import * as EventSink from "./EventSink.ts"; | 42 | import * as EventSink from "./EventSink.ts"; |
| ... | @@ -56,14 +56,14 @@ import * as ProviderEventIngestor from "./ProviderEventIngestor.ts"; | ... | @@ -56,14 +56,14 @@ import * as ProviderEventIngestor from "./ProviderEventIngestor.ts"; |
| 56 | import * as ThreadCommandExecutor from "./ThreadCommandExecutor.ts"; | 56 | import * as ThreadCommandExecutor from "./ThreadCommandExecutor.ts"; |
| 57 | import * as ProviderSessionManager from "./ProviderSessionManager.ts"; | 57 | import * as ProviderSessionManager from "./ProviderSessionManager.ts"; |
| 58 | 58 | ||
| 59 | const TestDatabaseLayer = SqlitePersistenceMemory; | 59 | const layerTestDatabase = SqlitePersistence.layerMemory; |
| 60 | const TestStoresLayer = Layer.merge(EventStore.layer, ProjectionStore.layer).pipe( | 60 | const layerTestStores = Layer.merge(EventStore.layer, ProjectionStore.layer).pipe( |
| 61 | Layer.provide(TestDatabaseLayer), | 61 | Layer.provide(layerTestDatabase), |
| 62 | ); | 62 | ); |
| 63 | const TestEventSinkLayer = EventSink.layer.pipe( | 63 | const layerTestEventSink = EventSink.layer.pipe( |
| 64 | Layer.provide(Layer.mergeAll(TestStoresLayer, TestDatabaseLayer)), | 64 | Layer.provide(Layer.mergeAll(layerTestStores, layerTestDatabase)), |
| 65 | ); | 65 | ); |
| 66 | const FailingReleaseEventSinkLayer = Layer.effect( | 66 | const layerFailingReleaseEventSink = Layer.effect( |
| 67 | EventSink.EventSinkV2, | 67 | EventSink.EventSinkV2, |
| 68 | Effect.gen(function* () { | 68 | Effect.gen(function* () { |
| 69 | const delegate = yield* EventSink.EventSinkV2; | 69 | const delegate = yield* EventSink.EventSinkV2; |
| ... | @@ -79,7 +79,7 @@ const FailingReleaseEventSinkLayer = Layer.effect( | ... | @@ -79,7 +79,7 @@ const FailingReleaseEventSinkLayer = Layer.effect( |
| 79 | : delegate.write(input), | 79 | : delegate.write(input), |
| 80 | }); | 80 | }); |
| 81 | }), | 81 | }), |
| 82 | ).pipe(Layer.provide(TestEventSinkLayer)); | 82 | ).pipe(Layer.provide(layerTestEventSink)); |
| 83 | 83 | ||
| 84 | interface FlakyReleaseWrites { | 84 | interface FlakyReleaseWrites { |
| 85 | /** Which release writes fail right now. */ | 85 | /** Which release writes fail right now. */ |
| ... | @@ -94,7 +94,7 @@ interface FlakyReleaseWrites { | ... | @@ -94,7 +94,7 @@ interface FlakyReleaseWrites { |
| 94 | } | 94 | } |
| 95 | 95 | ||
| 96 | // Fails release writes with a defect, the way a failed SQL commit surfaces. | 96 | // Fails release writes with a defect, the way a failed SQL commit surfaces. |
| 97 | const makeFlakyReleaseEventSinkLayer = (flaky: FlakyReleaseWrites) => | 97 | const layerFlakyReleaseEventSink = (flaky: FlakyReleaseWrites) => |
| 98 | Layer.effect( | 98 | Layer.effect( |
| 99 | EventSink.EventSinkV2, | 99 | EventSink.EventSinkV2, |
| 100 | Effect.gen(function* () { | 100 | Effect.gen(function* () { |
| ... | @@ -125,7 +125,7 @@ const makeFlakyReleaseEventSinkLayer = (flaky: FlakyReleaseWrites) => | ... | @@ -125,7 +125,7 @@ const makeFlakyReleaseEventSinkLayer = (flaky: FlakyReleaseWrites) => |
| 125 | }), | 125 | }), |
| 126 | }); | 126 | }); |
| 127 | }), | 127 | }), |
| 128 | ).pipe(Layer.provide(TestEventSinkLayer)); | 128 | ).pipe(Layer.provide(layerTestEventSink)); |
| 129 | 129 | ||
| 130 | const CodexCapabilities: OrchestrationV2ProviderCapabilities = CodexProviderCapabilitiesV2; | 130 | const CodexCapabilities: OrchestrationV2ProviderCapabilities = CodexProviderCapabilitiesV2; |
| 131 | const ExclusiveCapabilities: OrchestrationV2ProviderCapabilities = { | 131 | const ExclusiveCapabilities: OrchestrationV2ProviderCapabilities = { |
| ... | @@ -392,7 +392,7 @@ function makeProviderAdapter( | ... | @@ -392,7 +392,7 @@ function makeProviderAdapter( |
| 392 | }; | 392 | }; |
| 393 | } | 393 | } |
| 394 | 394 | ||
| 395 | function makeTestLayer(input: { | 395 | function layerTest(input: { |
| 396 | readonly state: Ref.Ref<TestProviderRuntimeState>; | 396 | readonly state: Ref.Ref<TestProviderRuntimeState>; |
| 397 | readonly idleTimeoutMs: number; | 397 | readonly idleTimeoutMs: number; |
| 398 | readonly maxIdlePinMs?: number; | 398 | readonly maxIdlePinMs?: number; |
| ... | @@ -413,13 +413,13 @@ function makeTestLayer(input: { | ... | @@ -413,13 +413,13 @@ function makeTestLayer(input: { |
| 413 | readonly serverSettingsLayer?: ReturnType<typeof ServerSettings.layerTest>; | 413 | readonly serverSettingsLayer?: ReturnType<typeof ServerSettings.layerTest>; |
| 414 | readonly projectServiceLayer?: Layer.Layer<ProjectService.ProjectService>; | 414 | readonly projectServiceLayer?: Layer.Layer<ProjectService.ProjectService>; |
| 415 | }) { | 415 | }) { |
| 416 | const configuredEventSinkLayer = | 416 | const layerConfiguredEventSink = |
| 417 | input.flakyReleaseWrites !== undefined | 417 | input.flakyReleaseWrites !== undefined |
| 418 | ? makeFlakyReleaseEventSinkLayer(input.flakyReleaseWrites) | 418 | ? layerFlakyReleaseEventSink(input.flakyReleaseWrites) |
| 419 | : input.failReleaseEventWrites | 419 | : input.failReleaseEventWrites |
| 420 | ? FailingReleaseEventSinkLayer | 420 | ? layerFailingReleaseEventSink |
| 421 | : TestEventSinkLayer; | 421 | : layerTestEventSink; |
| 422 | const registryLayer = ProviderAdapterRegistry.makeSingleLayer( | 422 | const layerRegistry = ProviderAdapterRegistry.layerSingle( |
| 423 | makeProviderAdapter(input.state, { | 423 | makeProviderAdapter(input.state, { |
| 424 | failEventStream: input.failEventStream ?? false, | 424 | failEventStream: input.failEventStream ?? false, |
| 425 | ...(input.capabilities === undefined ? {} : { capabilities: input.capabilities }), | 425 | ...(input.capabilities === undefined ? {} : { capabilities: input.capabilities }), |
| ... | @@ -434,33 +434,33 @@ function makeTestLayer(input: { | ... | @@ -434,33 +434,33 @@ function makeTestLayer(input: { |
| 434 | ...(input.beforeUnload === undefined ? {} : { beforeUnload: input.beforeUnload }), | 434 | ...(input.beforeUnload === undefined ? {} : { beforeUnload: input.beforeUnload }), |
| 435 | }), | 435 | }), |
| 436 | ); | 436 | ); |
| 437 | const providerEventIngestorTestLayer = ProviderEventIngestor.layer.pipe( | 437 | const layerProviderEventIngestorTest = ProviderEventIngestor.layer.pipe( |
| 438 | Layer.provide( | 438 | Layer.provide( |
| 439 | Layer.mergeAll( | 439 | Layer.mergeAll( |
| 440 | configuredEventSinkLayer, | 440 | layerConfiguredEventSink, |
| 441 | IdAllocator.layer, | 441 | IdAllocator.layer, |
| 442 | TestStoresLayer, | 442 | layerTestStores, |
| 443 | ThreadCommandExecutor.layer, | 443 | ThreadCommandExecutor.layer, |
| 444 | ), | 444 | ), |
| 445 | ), | 445 | ), |
| 446 | ); | 446 | ); |
| 447 | return Layer.mergeAll( | 447 | return Layer.mergeAll( |
| 448 | TestStoresLayer, | 448 | layerTestStores, |
| 449 | configuredEventSinkLayer, | 449 | layerConfiguredEventSink, |
| 450 | IdAllocator.layer, | 450 | IdAllocator.layer, |
| 451 | TestMcpRegistryLayer, | 451 | layerTestMcpRegistry, |
| 452 | ProviderSessionManager.layerWithOptions({ | 452 | ProviderSessionManager.layerWithOptions({ |
| 453 | idleTimeoutMs: input.idleTimeoutMs, | 453 | idleTimeoutMs: input.idleTimeoutMs, |
| 454 | ...(input.maxIdlePinMs === undefined ? {} : { maxIdlePinMs: input.maxIdlePinMs }), | 454 | ...(input.maxIdlePinMs === undefined ? {} : { maxIdlePinMs: input.maxIdlePinMs }), |
| 455 | }).pipe( | 455 | }).pipe( |
| 456 | Layer.provide( | 456 | Layer.provide( |
| 457 | Layer.mergeAll( | 457 | Layer.mergeAll( |
| 458 | registryLayer, | 458 | layerRegistry, |
| 459 | configuredEventSinkLayer, | 459 | layerConfiguredEventSink, |
| 460 | IdAllocator.layer, | 460 | IdAllocator.layer, |
| 461 | providerEventIngestorTestLayer, | 461 | layerProviderEventIngestorTest, |
| 462 | TestMcpRegistryLayer, | 462 | layerTestMcpRegistry, |
| 463 | TestStoresLayer, | 463 | layerTestStores, |
| 464 | ...(input.serverSettingsLayer === undefined ? [] : [input.serverSettingsLayer]), | 464 | ...(input.serverSettingsLayer === undefined ? [] : [input.serverSettingsLayer]), |
| 465 | ...(input.projectServiceLayer === undefined ? [] : [input.projectServiceLayer]), | 465 | ...(input.projectServiceLayer === undefined ? [] : [input.projectServiceLayer]), |
| 466 | ), | 466 | ), |
| ... | @@ -479,7 +479,7 @@ const fakeEnvironment = ServerEnvironment.ServerEnvironment.of({ | ... | @@ -479,7 +479,7 @@ const fakeEnvironment = ServerEnvironment.ServerEnvironment.of({ |
| 479 | getDescriptor: Effect.die("unused"), | 479 | getDescriptor: Effect.die("unused"), |
| 480 | }); | 480 | }); |
| 481 | 481 | ||
| 482 | const TestMcpRegistryLayer = Layer.effect( | 482 | const layerTestMcpRegistry = Layer.effect( |
| 483 | McpSessionRegistry.McpSessionRegistry, | 483 | McpSessionRegistry.McpSessionRegistry, |
| 484 | McpSessionRegistry.__testing.make(), | 484 | McpSessionRegistry.__testing.make(), |
| 485 | ).pipe( | 485 | ).pipe( |
| ... | @@ -520,7 +520,7 @@ function runBrowserAccessScenario(input: { | ... | @@ -520,7 +520,7 @@ function runBrowserAccessScenario(input: { |
| 520 | >([]); | 520 | >([]); |
| 521 | const projectId = ProjectId.make("project-provider-session-manager-browser-access"); | 521 | const projectId = ProjectId.make("project-provider-session-manager-browser-access"); |
| 522 | const threadId = ThreadId.make("thread-provider-session-manager-browser-access"); | 522 | const threadId = ThreadId.make("thread-provider-session-manager-browser-access"); |
| 523 | const projectServiceLayer = Layer.mock(ProjectService.ProjectService)({ | 523 | const layerProjectService = Layer.mock(ProjectService.ProjectService)({ |
| 524 | getById: (requestedProjectId) => | 524 | getById: (requestedProjectId) => |
| 525 | Effect.succeed( | 525 | Effect.succeed( |
| 526 | input.projectExists === false | 526 | input.projectExists === false |
| ... | @@ -548,11 +548,11 @@ function runBrowserAccessScenario(input: { | ... | @@ -548,11 +548,11 @@ function runBrowserAccessScenario(input: { |
| 548 | .pipe(Effect.ignore); | 548 | .pipe(Effect.ignore); |
| 549 | }).pipe( | 549 | }).pipe( |
| 550 | Effect.provide( | 550 | Effect.provide( |
| 551 | makeTestLayer({ | 551 | layerTest({ |
| 552 | state, | 552 | state, |
| 553 | idleTimeoutMs: 1_000, | 553 | idleTimeoutMs: 1_000, |
| 554 | mcpConfigs, | 554 | mcpConfigs, |
| 555 | projectServiceLayer, | 555 | projectServiceLayer: layerProjectService, |
| 556 | serverSettingsLayer: ServerSettings.layerTest({ | 556 | serverSettingsLayer: ServerSettings.layerTest({ |
| 557 | enableAgentBrowserAccess: input.enableAgentBrowserAccess, | 557 | enableAgentBrowserAccess: input.enableAgentBrowserAccess, |
| 558 | projectSettingsOverrides: { | 558 | projectSettingsOverrides: { |
| ... | @@ -766,7 +766,7 @@ it.effect("ProviderSessionManagerV2 opens independent sessions concurrently", () | ... | @@ -766,7 +766,7 @@ it.effect("ProviderSessionManagerV2 opens independent sessions concurrently", () |
| 766 | 766 | ||
| 767 | yield* effect.pipe( | 767 | yield* effect.pipe( |
| 768 | Effect.provide( | 768 | Effect.provide( |
| 769 | makeTestLayer({ | 769 | layerTest({ |
| 770 | state, | 770 | state, |
| 771 | idleTimeoutMs: 60_000, | 771 | idleTimeoutMs: 60_000, |
| 772 | beforeOpen, | 772 | beforeOpen, |
| ... | @@ -823,7 +823,7 @@ it.effect("ProviderSessionManagerV2 closes every live session for a provider ins | ... | @@ -823,7 +823,7 @@ it.effect("ProviderSessionManagerV2 closes every live session for a provider ins |
| 823 | 823 | ||
| 824 | yield* effect.pipe( | 824 | yield* effect.pipe( |
| 825 | Effect.provide( | 825 | Effect.provide( |
| 826 | makeTestLayer({ | 826 | layerTest({ |
| 827 | state, | 827 | state, |
| 828 | idleTimeoutMs: 60_000, | 828 | idleTimeoutMs: 60_000, |
| 829 | }), | 829 | }), |
| ... | @@ -882,7 +882,7 @@ it.effect("ProviderSessionManagerV2 opens a duplicate session only once", () => | ... | @@ -882,7 +882,7 @@ it.effect("ProviderSessionManagerV2 opens a duplicate session only once", () => |
| 882 | 882 | ||
| 883 | yield* effect.pipe( | 883 | yield* effect.pipe( |
| 884 | Effect.provide( | 884 | Effect.provide( |
| 885 | makeTestLayer({ | 885 | layerTest({ |
| 886 | state, | 886 | state, |
| 887 | idleTimeoutMs: 60_000, | 887 | idleTimeoutMs: 60_000, |
| 888 | beforeOpen, | 888 | beforeOpen, |
| ... | @@ -923,7 +923,7 @@ it.effect("ProviderSessionManagerV2 releases live sessions when its layer shuts | ... | @@ -923,7 +923,7 @@ it.effect("ProviderSessionManagerV2 releases live sessions when its layer shuts |
| 923 | 923 | ||
| 924 | yield* effect.pipe( | 924 | yield* effect.pipe( |
| 925 | Effect.provide( | 925 | Effect.provide( |
| 926 | makeTestLayer({ | 926 | layerTest({ |
| 927 | state, | 927 | state, |
| 928 | idleTimeoutMs: 60_000, | 928 | idleTimeoutMs: 60_000, |
| 929 | }), | 929 | }), |
| ... | @@ -972,7 +972,7 @@ it.effect("ProviderSessionManagerV2 closes event subscriptions normally on serve | ... | @@ -972,7 +972,7 @@ it.effect("ProviderSessionManagerV2 closes event subscriptions normally on serve |
| 972 | assert.isEmpty(yield* bufferedSubscription.events.pipe(Stream.runCollect)); | 972 | assert.isEmpty(yield* bufferedSubscription.events.pipe(Stream.runCollect)); |
| 973 | }); | 973 | }); |
| 974 | 974 | ||
| 975 | yield* effect.pipe(Effect.provide(makeTestLayer({ state, idleTimeoutMs: 60_000 }))); | 975 | yield* effect.pipe(Effect.provide(layerTest({ state, idleTimeoutMs: 60_000 }))); |
| 976 | }), | 976 | }), |
| 977 | ); | 977 | ); |
| 978 | 978 | ||
| ... | @@ -1040,7 +1040,7 @@ it.effect("ProviderSessionManagerV2 drains subscribers when the provider stops", | ... | @@ -1040,7 +1040,7 @@ it.effect("ProviderSessionManagerV2 drains subscribers when the provider stops", |
| 1040 | assert.equal((yield* Ref.get(state)).closeCount, 1); | 1040 | assert.equal((yield* Ref.get(state)).closeCount, 1); |
| 1041 | }); | 1041 | }); |
| 1042 | 1042 | ||
| 1043 | yield* effect.pipe(Effect.provide(makeTestLayer({ state, idleTimeoutMs: 60_000 }))); | 1043 | yield* effect.pipe(Effect.provide(layerTest({ state, idleTimeoutMs: 60_000 }))); |
| 1044 | }), | 1044 | }), |
| 1045 | ); | 1045 | ); |
| 1046 | 1046 | ||
| ... | @@ -1095,7 +1095,7 @@ it.effect( | ... | @@ -1095,7 +1095,7 @@ it.effect( |
| 1095 | 1095 | ||
| 1096 | yield* effect.pipe( | 1096 | yield* effect.pipe( |
| 1097 | Effect.provide( | 1097 | Effect.provide( |
| 1098 | makeTestLayer({ | 1098 | layerTest({ |
| 1099 | state, | 1099 | state, |
| 1100 | idleTimeoutMs: 1_000, | 1100 | idleTimeoutMs: 1_000, |
| 1101 | mcpConfigs, | 1101 | mcpConfigs, |
| ... | @@ -1150,7 +1150,7 @@ it.effect( | ... | @@ -1150,7 +1150,7 @@ it.effect( |
| 1150 | 1150 | ||
| 1151 | yield* effect.pipe( | 1151 | yield* effect.pipe( |
| 1152 | Effect.provide( | 1152 | Effect.provide( |
| 1153 | makeTestLayer({ | 1153 | layerTest({ |
| 1154 | state, | 1154 | state, |
| 1155 | idleTimeoutMs: 1_000, | 1155 | idleTimeoutMs: 1_000, |
| 1156 | mcpConfigs, | 1156 | mcpConfigs, |
| ... | @@ -1252,7 +1252,7 @@ it.effect("ProviderSessionManagerV2 revokes MCP credentials when release persist | ... | @@ -1252,7 +1252,7 @@ it.effect("ProviderSessionManagerV2 revokes MCP credentials when release persist |
| 1252 | 1252 | ||
| 1253 | yield* effect.pipe( | 1253 | yield* effect.pipe( |
| 1254 | Effect.provide( | 1254 | Effect.provide( |
| 1255 | makeTestLayer({ | 1255 | layerTest({ |
| 1256 | state, | 1256 | state, |
| 1257 | idleTimeoutMs: 1_000, | 1257 | idleTimeoutMs: 1_000, |
| 1258 | mcpConfigs, | 1258 | mcpConfigs, |
| ... | @@ -1322,7 +1322,7 @@ it.effect("ProviderSessionManagerV2 duplicate detach preserves replacement MCP c | ... | @@ -1322,7 +1322,7 @@ it.effect("ProviderSessionManagerV2 duplicate detach preserves replacement MCP c |
| 1322 | 1322 | ||
| 1323 | yield* effect.pipe( | 1323 | yield* effect.pipe( |
| 1324 | Effect.provide( | 1324 | Effect.provide( |
| 1325 | makeTestLayer({ | 1325 | layerTest({ |
| 1326 | state, | 1326 | state, |
| 1327 | idleTimeoutMs: 1_000, | 1327 | idleTimeoutMs: 1_000, |
| 1328 | capabilities: ExclusiveCapabilities, | 1328 | capabilities: ExclusiveCapabilities, |
| ... | @@ -1398,7 +1398,7 @@ it.effect( | ... | @@ -1398,7 +1398,7 @@ it.effect( |
| 1398 | 1398 | ||
| 1399 | yield* effect.pipe( | 1399 | yield* effect.pipe( |
| 1400 | Effect.provide( | 1400 | Effect.provide( |
| 1401 | makeTestLayer({ | 1401 | layerTest({ |
| 1402 | state, | 1402 | state, |
| 1403 | idleTimeoutMs: 1_000, | 1403 | idleTimeoutMs: 1_000, |
| 1404 | capabilities: ExclusiveCapabilities, | 1404 | capabilities: ExclusiveCapabilities, |
| ... | @@ -1477,7 +1477,7 @@ it.effect( | ... | @@ -1477,7 +1477,7 @@ it.effect( |
| 1477 | 1477 | ||
| 1478 | yield* effect.pipe( | 1478 | yield* effect.pipe( |
| 1479 | Effect.provide( | 1479 | Effect.provide( |
| 1480 | makeTestLayer({ | 1480 | layerTest({ |
| 1481 | state, | 1481 | state, |
| 1482 | idleTimeoutMs: 1_000, | 1482 | idleTimeoutMs: 1_000, |
| 1483 | mcpConfigs, | 1483 | mcpConfigs, |
| ... | @@ -1537,9 +1537,7 @@ it.effect( | ... | @@ -1537,9 +1537,7 @@ it.effect( |
| 1537 | yield* manager.close(s1); | 1537 | yield* manager.close(s1); |
| 1538 | }); | 1538 | }); |
| 1539 | 1539 | ||
| 1540 | yield* effect.pipe( | 1540 | yield* effect.pipe(Effect.provide(layerTest({ state, idleTimeoutMs: 1_000, mcpConfigs }))); |
| 1541 | Effect.provide(makeTestLayer({ state, idleTimeoutMs: 1_000, mcpConfigs })), | ||
| 1542 | ); | ||
| 1543 | }), | 1541 | }), |
| 1544 | ); | 1542 | ); |
| 1545 | 1543 | ||
| ... | @@ -1601,7 +1599,7 @@ it.effect( | ... | @@ -1601,7 +1599,7 @@ it.effect( |
| 1601 | 1599 | ||
| 1602 | yield* effect.pipe( | 1600 | yield* effect.pipe( |
| 1603 | Effect.provide( | 1601 | Effect.provide( |
| 1604 | makeTestLayer({ | 1602 | layerTest({ |
| 1605 | state, | 1603 | state, |
| 1606 | idleTimeoutMs: 1_000, | 1604 | idleTimeoutMs: 1_000, |
| 1607 | mcpConfigs, | 1605 | mcpConfigs, |
| ... | @@ -1656,7 +1654,7 @@ it.effect("ProviderSessionManagerV2 terminal detach revokes the thread's MCP cre | ... | @@ -1656,7 +1654,7 @@ it.effect("ProviderSessionManagerV2 terminal detach revokes the thread's MCP cre |
| 1656 | assert.isUndefined(McpProviderSession.readMcpProviderSession(threadId)); | 1654 | assert.isUndefined(McpProviderSession.readMcpProviderSession(threadId)); |
| 1657 | }); | 1655 | }); |
| 1658 | 1656 | ||
| 1659 | yield* effect.pipe(Effect.provide(makeTestLayer({ state, idleTimeoutMs: 1_000, mcpConfigs }))); | 1657 | yield* effect.pipe(Effect.provide(layerTest({ state, idleTimeoutMs: 1_000, mcpConfigs }))); |
| 1660 | }), | 1658 | }), |
| 1661 | ); | 1659 | ); |
| 1662 | 1660 | ||
| ... | @@ -1704,7 +1702,7 @@ it.effect("ProviderSessionManagerV2 releases idle sessions without sweeping all | ... | @@ -1704,7 +1702,7 @@ it.effect("ProviderSessionManagerV2 releases idle sessions without sweeping all |
| 1704 | assert.equal(projection.providerSessions.at(-1)?.status, "stopped"); | 1702 | assert.equal(projection.providerSessions.at(-1)?.status, "stopped"); |
| 1705 | }); | 1703 | }); |
| 1706 | 1704 | ||
| 1707 | yield* effect.pipe(Effect.provide(makeTestLayer({ state, idleTimeoutMs: 1000 }))); | 1705 | yield* effect.pipe(Effect.provide(layerTest({ state, idleTimeoutMs: 1000 }))); |
| 1708 | }), | 1706 | }), |
| 1709 | ); | 1707 | ); |
| 1710 | 1708 | ||
| ... | @@ -1750,7 +1748,7 @@ it.effect("ProviderSessionManagerV2 persists release when session scope close ha | ... | @@ -1750,7 +1748,7 @@ it.effect("ProviderSessionManagerV2 persists release when session scope close ha |
| 1750 | }); | 1748 | }); |
| 1751 | 1749 | ||
| 1752 | yield* effect.pipe( | 1750 | yield* effect.pipe( |
| 1753 | Effect.provide(makeTestLayer({ state, idleTimeoutMs: 1000, hangSessionScopeClose: true })), | 1751 | Effect.provide(layerTest({ state, idleTimeoutMs: 1000, hangSessionScopeClose: true })), |
| 1754 | ); | 1752 | ); |
| 1755 | }), | 1753 | }), |
| 1756 | ); | 1754 | ); |
| ... | @@ -1799,7 +1797,7 @@ it.effect("ProviderSessionManagerV2 defers idle release while background work is | ... | @@ -1799,7 +1797,7 @@ it.effect("ProviderSessionManagerV2 defers idle release while background work is |
| 1799 | 1797 | ||
| 1800 | yield* effect.pipe( | 1798 | yield* effect.pipe( |
| 1801 | Effect.provide( | 1799 | Effect.provide( |
| 1802 | makeTestLayer({ | 1800 | layerTest({ |
| 1803 | state, | 1801 | state, |
| 1804 | idleTimeoutMs: 1000, | 1802 | idleTimeoutMs: 1000, |
| 1805 | hasPendingBackgroundWork: Ref.get(pendingWork), | 1803 | hasPendingBackgroundWork: Ref.get(pendingWork), |
| ... | @@ -1850,7 +1848,7 @@ it.effect("ProviderSessionManagerV2 releases pinned idle sessions once the pin c | ... | @@ -1850,7 +1848,7 @@ it.effect("ProviderSessionManagerV2 releases pinned idle sessions once the pin c |
| 1850 | 1848 | ||
| 1851 | yield* effect.pipe( | 1849 | yield* effect.pipe( |
| 1852 | Effect.provide( | 1850 | Effect.provide( |
| 1853 | makeTestLayer({ | 1851 | layerTest({ |
| 1854 | state, | 1852 | state, |
| 1855 | idleTimeoutMs: 1000, | 1853 | idleTimeoutMs: 1000, |
| 1856 | maxIdlePinMs: 3000, | 1854 | maxIdlePinMs: 3000, |
| ... | @@ -1969,7 +1967,7 @@ it.effect( | ... | @@ -1969,7 +1967,7 @@ it.effect( |
| 1969 | 1967 | ||
| 1970 | yield* effect.pipe( | 1968 | yield* effect.pipe( |
| 1971 | Effect.provide( | 1969 | Effect.provide( |
| 1972 | makeTestLayer({ | 1970 | layerTest({ |
| 1973 | state, | 1971 | state, |
| 1974 | idleTimeoutMs: 1000, | 1972 | idleTimeoutMs: 1000, |
| 1975 | // Uninterruptible so the markBusy-triggered interrupt cannot land | 1973 | // Uninterruptible so the markBusy-triggered interrupt cannot land |
| ... | @@ -2063,7 +2061,7 @@ it.effect("ProviderSessionManagerV2 does not apply a stale idle pin to a replace | ... | @@ -2063,7 +2061,7 @@ it.effect("ProviderSessionManagerV2 does not apply a stale idle pin to a replace |
| 2063 | 2061 | ||
| 2064 | yield* effect.pipe( | 2062 | yield* effect.pipe( |
| 2065 | Effect.provide( | 2063 | Effect.provide( |
| 2066 | makeTestLayer({ | 2064 | layerTest({ |
| 2067 | state, | 2065 | state, |
| 2068 | idleTimeoutMs: 1000, | 2066 | idleTimeoutMs: 1000, |
| 2069 | maxIdlePinMs: 60_000, | 2067 | maxIdlePinMs: 60_000, |
| ... | @@ -2176,7 +2174,7 @@ it.effect( | ... | @@ -2176,7 +2174,7 @@ it.effect( |
| 2176 | assert.equal(projection.providerSessions.at(-1)?.status, "stopped"); | 2174 | assert.equal(projection.providerSessions.at(-1)?.status, "stopped"); |
| 2177 | }); | 2175 | }); |
| 2178 | 2176 | ||
| 2179 | yield* effect.pipe(Effect.provide(makeTestLayer({ state, idleTimeoutMs: 1000 }))); | 2177 | yield* effect.pipe(Effect.provide(layerTest({ state, idleTimeoutMs: 1000 }))); |
| 2180 | }), | 2178 | }), |
| 2181 | ); | 2179 | ); |
| 2182 | 2180 | ||
| ... | @@ -2226,7 +2224,7 @@ it.effect("ProviderSessionManagerV2 uses the same release path for runtime failu | ... | @@ -2226,7 +2224,7 @@ it.effect("ProviderSessionManagerV2 uses the same release path for runtime failu |
| 2226 | assert.equal(projection.providerSessions.at(-1)?.lastError, "process exited"); | 2224 | assert.equal(projection.providerSessions.at(-1)?.lastError, "process exited"); |
| 2227 | }); | 2225 | }); |
| 2228 | 2226 | ||
| 2229 | yield* effect.pipe(Effect.provide(makeTestLayer({ state, idleTimeoutMs: 1000 }))); | 2227 | yield* effect.pipe(Effect.provide(layerTest({ state, idleTimeoutMs: 1000 }))); |
| 2230 | }), | 2228 | }), |
| 2231 | ); | 2229 | ); |
| 2232 | 2230 | ||
| ... | @@ -2274,7 +2272,7 @@ it.effect("ProviderSessionManagerV2 releases sessions when provider event stream | ... | @@ -2274,7 +2272,7 @@ it.effect("ProviderSessionManagerV2 releases sessions when provider event stream |
| 2274 | 2272 | ||
| 2275 | yield* effect.pipe( | 2273 | yield* effect.pipe( |
| 2276 | Effect.provide( | 2274 | Effect.provide( |
| 2277 | makeTestLayer({ | 2275 | layerTest({ |
| 2278 | state, | 2276 | state, |
| 2279 | idleTimeoutMs: 1000, | 2277 | idleTimeoutMs: 1000, |
| 2280 | failEventStream: true, | 2278 | failEventStream: true, |
| ... | @@ -2347,7 +2345,7 @@ it.effect("ProviderSessionManagerV2 marks pending runtime requests non-live on r | ... | @@ -2347,7 +2345,7 @@ it.effect("ProviderSessionManagerV2 marks pending runtime requests non-live on r |
| 2347 | assert.equal(requestTurnItem?.status, "failed"); | 2345 | assert.equal(requestTurnItem?.status, "failed"); |
| 2348 | }); | 2346 | }); |
| 2349 | 2347 | ||
| 2350 | yield* effect.pipe(Effect.provide(makeTestLayer({ state, idleTimeoutMs: 1000 }))); | 2348 | yield* effect.pipe(Effect.provide(layerTest({ state, idleTimeoutMs: 1000 }))); |
| 2351 | }), | 2349 | }), |
| 2352 | ); | 2350 | ); |
| 2353 | it.effect("ProviderSessionManagerV2 retries release records that failed to persist", () => | 2351 | it.effect("ProviderSessionManagerV2 retries release records that failed to persist", () => |
| ... | @@ -2408,7 +2406,7 @@ it.effect("ProviderSessionManagerV2 retries release records that failed to persi | ... | @@ -2408,7 +2406,7 @@ it.effect("ProviderSessionManagerV2 retries release records that failed to persi |
| 2408 | }); | 2406 | }); |
| 2409 | 2407 | ||
| 2410 | yield* effect.pipe( | 2408 | yield* effect.pipe( |
| 2411 | Effect.provide(makeTestLayer({ state, idleTimeoutMs: 60_000, flakyReleaseWrites: flaky })), | 2409 | Effect.provide(layerTest({ state, idleTimeoutMs: 60_000, flakyReleaseWrites: flaky })), |
| 2412 | ); | 2410 | ); |
| 2413 | }), | 2411 | }), |
| 2414 | ); | 2412 | ); |
| ... | @@ -2481,7 +2479,7 @@ it.effect("ProviderSessionManagerV2 release retries leave a replacement session | ... | @@ -2481,7 +2479,7 @@ it.effect("ProviderSessionManagerV2 release retries leave a replacement session |
| 2481 | }); | 2479 | }); |
| 2482 | 2480 | ||
| 2483 | yield* effect.pipe( | 2481 | yield* effect.pipe( |
| 2484 | Effect.provide(makeTestLayer({ state, idleTimeoutMs: 60_000, flakyReleaseWrites: flaky })), | 2482 | Effect.provide(layerTest({ state, idleTimeoutMs: 60_000, flakyReleaseWrites: flaky })), |
| 2485 | ); | 2483 | ); |
| 2486 | }), | 2484 | }), |
| 2487 | ); | 2485 | ); |
| ... | @@ -2572,7 +2570,7 @@ it.effect("ProviderSessionManagerV2 keeps each failed release's cleanup", () => | ... | @@ -2572,7 +2570,7 @@ it.effect("ProviderSessionManagerV2 keeps each failed release's cleanup", () => |
| 2572 | }); | 2570 | }); |
| 2573 | 2571 | ||
| 2574 | yield* effect.pipe( | 2572 | yield* effect.pipe( |
| 2575 | Effect.provide(makeTestLayer({ state, idleTimeoutMs: 60_000, flakyReleaseWrites: flaky })), | 2573 | Effect.provide(layerTest({ state, idleTimeoutMs: 60_000, flakyReleaseWrites: flaky })), |
| 2576 | ); | 2574 | ); |
| 2577 | }), | 2575 | }), |
| 2578 | ); | 2576 | ); |
| ... | @@ -2638,7 +2636,7 @@ it.effect("ProviderSessionManagerV2 settles a request the event pump persists du | ... | @@ -2638,7 +2636,7 @@ it.effect("ProviderSessionManagerV2 settles a request the event pump persists du |
| 2638 | }); | 2636 | }); |
| 2639 | 2637 | ||
| 2640 | yield* effect.pipe( | 2638 | yield* effect.pipe( |
| 2641 | Effect.provide(makeTestLayer({ state, idleTimeoutMs: 60_000, flakyReleaseWrites: flaky })), | 2639 | Effect.provide(layerTest({ state, idleTimeoutMs: 60_000, flakyReleaseWrites: flaky })), |
| 2642 | ); | 2640 | ); |
| 2643 | }), | 2641 | }), |
| 2644 | ); | 2642 | ); |
| ... | @@ -2712,7 +2710,7 @@ it.effect("ProviderSessionManagerV2 terminalizes a pending input transcript item | ... | @@ -2712,7 +2710,7 @@ it.effect("ProviderSessionManagerV2 terminalizes a pending input transcript item |
| 2712 | assert.equal(requestTurnItem?.status, "failed"); | 2710 | assert.equal(requestTurnItem?.status, "failed"); |
| 2713 | }); | 2711 | }); |
| 2714 | 2712 | ||
| 2715 | yield* effect.pipe(Effect.provide(makeTestLayer({ state, idleTimeoutMs: 1000 }))); | 2713 | yield* effect.pipe(Effect.provide(layerTest({ state, idleTimeoutMs: 1000 }))); |
| 2716 | }), | 2714 | }), |
| 2717 | ); | 2715 | ); |
| 2718 | 2716 | ||
| ... | @@ -2797,7 +2795,7 @@ it.effect("ProviderSessionManagerV2 persists session-scoped runtime requests wit | ... | @@ -2797,7 +2795,7 @@ it.effect("ProviderSessionManagerV2 persists session-scoped runtime requests wit |
| 2797 | assert.equal(turnItem?.status, "waiting"); | 2795 | assert.equal(turnItem?.status, "waiting"); |
| 2798 | }); | 2796 | }); |
| 2799 | 2797 | ||
| 2800 | yield* effect.pipe(Effect.provide(makeTestLayer({ state, idleTimeoutMs: 1000 }))); | 2798 | yield* effect.pipe(Effect.provide(layerTest({ state, idleTimeoutMs: 1000 }))); |
| 2801 | }), | 2799 | }), |
| 2802 | ); | 2800 | ); |
| 2803 | 2801 | ||
| ... | @@ -2871,7 +2869,7 @@ it.effect( | ... | @@ -2871,7 +2869,7 @@ it.effect( |
| 2871 | 2869 | ||
| 2872 | yield* effect.pipe( | 2870 | yield* effect.pipe( |
| 2873 | Effect.provide( | 2871 | Effect.provide( |
| 2874 | makeTestLayer({ | 2872 | layerTest({ |
| 2875 | state, | 2873 | state, |
| 2876 | idleTimeoutMs: 1000, | 2874 | idleTimeoutMs: 1000, |
| 2877 | beforeOpen: (input) => | 2875 | beforeOpen: (input) => |
| ... | @@ -3025,7 +3023,7 @@ it.effect( | ... | @@ -3025,7 +3023,7 @@ it.effect( |
| 3025 | assert.equal((yield* Ref.get(state)).closeCount, 1); | 3023 | assert.equal((yield* Ref.get(state)).closeCount, 1); |
| 3026 | }); | 3024 | }); |
| 3027 | 3025 | ||
| 3028 | yield* effect.pipe(Effect.provide(makeTestLayer({ state, idleTimeoutMs: 1000 }))); | 3026 | yield* effect.pipe(Effect.provide(layerTest({ state, idleTimeoutMs: 1000 }))); |
| 3029 | }), | 3027 | }), |
| 3030 | ); | 3028 | ); |
| 3031 | 3029 | ||
| ... | @@ -3190,7 +3188,7 @@ it.effect( | ... | @@ -3190,7 +3188,7 @@ it.effect( |
| 3190 | assert.equal((yield* Ref.get(state)).closeCount, 1); | 3188 | assert.equal((yield* Ref.get(state)).closeCount, 1); |
| 3191 | }); | 3189 | }); |
| 3192 | 3190 | ||
| 3193 | yield* effect.pipe(Effect.provide(makeTestLayer({ state, idleTimeoutMs: 1000 }))); | 3191 | yield* effect.pipe(Effect.provide(layerTest({ state, idleTimeoutMs: 1000 }))); |
| 3194 | }), | 3192 | }), |
| 3195 | ); | 3193 | ); |
| 3196 | 3194 | ||
| ... | @@ -3294,7 +3292,7 @@ it.effect( | ... | @@ -3294,7 +3292,7 @@ it.effect( |
| 3294 | }); | 3292 | }); |
| 3295 | 3293 | ||
| 3296 | yield* effect.pipe( | 3294 | yield* effect.pipe( |
| 3297 | Effect.provide(makeTestLayer({ state, idleTimeoutMs: 1000, beforeUnload })), | 3295 | Effect.provide(layerTest({ state, idleTimeoutMs: 1000, beforeUnload })), |
| 3298 | Effect.scoped, | 3296 | Effect.scoped, |
| 3299 | ); | 3297 | ); |
| 3300 | }), | 3298 | }), |
| ... | @@ -3353,7 +3351,7 @@ it.effect( | ... | @@ -3353,7 +3351,7 @@ it.effect( |
| 3353 | 3351 | ||
| 3354 | yield* effect.pipe( | 3352 | yield* effect.pipe( |
| 3355 | Effect.provide( | 3353 | Effect.provide( |
| 3356 | makeTestLayer({ state, idleTimeoutMs: 1000, capabilities: ExclusiveCapabilities }), | 3354 | layerTest({ state, idleTimeoutMs: 1000, capabilities: ExclusiveCapabilities }), |
| 3357 | ), | 3355 | ), |
| 3358 | ); | 3356 | ); |
| 3359 | }), | 3357 | }), |
| ... | @@ -3404,7 +3402,7 @@ it.effect.each(["missing", "file"] as const)( | ... | @@ -3404,7 +3402,7 @@ it.effect.each(["missing", "file"] as const)( |
| 3404 | (yield* projectionStore.getThreadProjection(threadId)).providerSessions, | 3402 | (yield* projectionStore.getThreadProjection(threadId)).providerSessions, |
| 3405 | [], | 3403 | [], |
| 3406 | ); | 3404 | ); |
| 3407 | }).pipe(Effect.provide(makeTestLayer({ state, idleTimeoutMs: 60_000 }))); | 3405 | }).pipe(Effect.provide(layerTest({ state, idleTimeoutMs: 60_000 }))); |
| 3408 | }).pipe(Effect.provide(NodeServices.layer)), | 3406 | }).pipe(Effect.provide(NodeServices.layer)), |
| 3409 | ); | 3407 | ); |
| 3410 | 3408 | ||
| ... | @@ -3454,7 +3452,7 @@ it.effect( | ... | @@ -3454,7 +3452,7 @@ it.effect( |
| 3454 | (yield* projectionStore.getThreadProjection(threadId)).providerSessions, | 3452 | (yield* projectionStore.getThreadProjection(threadId)).providerSessions, |
| 3455 | before.providerSessions, | 3453 | before.providerSessions, |
| 3456 | ); | 3454 | ); |
| 3457 | }).pipe(Effect.provide(makeTestLayer({ state, idleTimeoutMs: 60_000 }))); | 3455 | }).pipe(Effect.provide(layerTest({ state, idleTimeoutMs: 60_000 }))); |
| 3458 | }).pipe(Effect.provide(NodeServices.layer)), | 3456 | }).pipe(Effect.provide(NodeServices.layer)), |
| 3459 | ); | 3457 | ); |
| 3460 | 3458 |
apps/server/src/orchestration-v2/ProviderSwitchService.test.ts+12-12| ... | @@ -65,7 +65,7 @@ function deadSessionRecord( | ... | @@ -65,7 +65,7 @@ function deadSessionRecord( |
| 65 | }; | 65 | }; |
| 66 | } | 66 | } |
| 67 | 67 | ||
| 68 | function testLayer( | 68 | function layerTest( |
| 69 | metadata: Readonly<Record<string, { continuationKey: string }>>, | 69 | metadata: Readonly<Record<string, { continuationKey: string }>>, |
| 70 | planSelectionTransition: ProviderAdapterV2Shape["planSelectionTransition"] = () => | 70 | planSelectionTransition: ProviderAdapterV2Shape["planSelectionTransition"] = () => |
| 71 | Effect.succeed({ type: "restart_session" }), | 71 | Effect.succeed({ type: "restart_session" }), |
| ... | @@ -77,7 +77,7 @@ function testLayer( | ... | @@ -77,7 +77,7 @@ function testLayer( |
| 77 | planSelectionTransition, | 77 | planSelectionTransition, |
| 78 | openSession: () => Effect.die("ProviderSwitchService tests do not open sessions."), | 78 | openSession: () => Effect.die("ProviderSwitchService tests do not open sessions."), |
| 79 | }); | 79 | }); |
| 80 | const registry = Layer.mock(ProviderAdapterRegistry.ProviderAdapterRegistryV2)({ | 80 | const layerRegistry = Layer.mock(ProviderAdapterRegistry.ProviderAdapterRegistryV2)({ |
| 81 | get: (instanceId) => | 81 | get: (instanceId) => |
| 82 | metadata[instanceId] === undefined | 82 | metadata[instanceId] === undefined |
| 83 | ? Effect.fail( | 83 | ? Effect.fail( |
| ... | @@ -99,7 +99,7 @@ function testLayer( | ... | @@ -99,7 +99,7 @@ function testLayer( |
| 99 | }); | 99 | }); |
| 100 | }, | 100 | }, |
| 101 | }); | 101 | }); |
| 102 | return ProviderSwitch.layer.pipe(Layer.provide(registry)); | 102 | return ProviderSwitch.layer.pipe(Layer.provide(layerRegistry)); |
| 103 | } | 103 | } |
| 104 | 104 | ||
| 105 | it.effect( | 105 | it.effect( |
| ... | @@ -115,7 +115,7 @@ it.effect( | ... | @@ -115,7 +115,7 @@ it.effect( |
| 115 | assert.deepEqual(result.releaseProviderSessionIds, [currentSessionId]); | 115 | assert.deepEqual(result.releaseProviderSessionIds, [currentSessionId]); |
| 116 | }).pipe( | 116 | }).pipe( |
| 117 | Effect.provide( | 117 | Effect.provide( |
| 118 | testLayer({ [currentInstanceId]: { continuationKey: "codex:account:primary" } }), | 118 | layerTest({ [currentInstanceId]: { continuationKey: "codex:account:primary" } }), |
| 119 | ), | 119 | ), |
| 120 | ), | 120 | ), |
| 121 | ); | 121 | ); |
| ... | @@ -140,7 +140,7 @@ it.effect.each(["stopped", "error"] as const)( | ... | @@ -140,7 +140,7 @@ it.effect.each(["stopped", "error"] as const)( |
| 140 | assert.deepEqual(result.releaseProviderSessionIds, [currentSessionId]); | 140 | assert.deepEqual(result.releaseProviderSessionIds, [currentSessionId]); |
| 141 | }).pipe( | 141 | }).pipe( |
| 142 | Effect.provide( | 142 | Effect.provide( |
| 143 | testLayer({ [currentInstanceId]: { continuationKey: "codex:account:primary" } }), | 143 | layerTest({ [currentInstanceId]: { continuationKey: "codex:account:primary" } }), |
| 144 | ), | 144 | ), |
| 145 | ), | 145 | ), |
| 146 | ); | 146 | ); |
| ... | @@ -169,7 +169,7 @@ it.effect("releases the newest live session, not the newest record overall", () | ... | @@ -169,7 +169,7 @@ it.effect("releases the newest live session, not the newest record overall", () |
| 169 | assert.deepEqual(result.releaseProviderSessionIds, [newerLiveSessionId]); | 169 | assert.deepEqual(result.releaseProviderSessionIds, [newerLiveSessionId]); |
| 170 | }).pipe( | 170 | }).pipe( |
| 171 | Effect.provide( | 171 | Effect.provide( |
| 172 | testLayer({ [currentInstanceId]: { continuationKey: "codex:account:primary" } }), | 172 | layerTest({ [currentInstanceId]: { continuationKey: "codex:account:primary" } }), |
| 173 | ), | 173 | ), |
| 174 | ), | 174 | ), |
| 175 | ); | 175 | ); |
| ... | @@ -192,7 +192,7 @@ it.effect("creates a fresh session with handoff when every recorded session is d | ... | @@ -192,7 +192,7 @@ it.effect("creates a fresh session with handoff when every recorded session is d |
| 192 | assert.deepEqual(result.releaseProviderSessionIds, []); | 192 | assert.deepEqual(result.releaseProviderSessionIds, []); |
| 193 | }).pipe( | 193 | }).pipe( |
| 194 | Effect.provide( | 194 | Effect.provide( |
| 195 | testLayer({ [currentInstanceId]: { continuationKey: "codex:account:primary" } }), | 195 | layerTest({ [currentInstanceId]: { continuationKey: "codex:account:primary" } }), |
| 196 | ), | 196 | ), |
| 197 | ), | 197 | ), |
| 198 | ); | 198 | ); |
| ... | @@ -246,7 +246,7 @@ it.effect("falls back to the native provider thread when every recorded session | ... | @@ -246,7 +246,7 @@ it.effect("falls back to the native provider thread when every recorded session |
| 246 | assert.deepEqual(result.releaseProviderSessionIds, []); | 246 | assert.deepEqual(result.releaseProviderSessionIds, []); |
| 247 | }).pipe( | 247 | }).pipe( |
| 248 | Effect.provide( | 248 | Effect.provide( |
| 249 | testLayer({ [currentInstanceId]: { continuationKey: "codex:account:primary" } }), | 249 | layerTest({ [currentInstanceId]: { continuationKey: "codex:account:primary" } }), |
| 250 | ), | 250 | ), |
| 251 | ), | 251 | ), |
| 252 | ); | 252 | ); |
| ... | @@ -265,7 +265,7 @@ it.effect("hands off from a native provider thread after its session detaches", | ... | @@ -265,7 +265,7 @@ it.effect("hands off from a native provider thread after its session detaches", |
| 265 | assert.deepEqual(result.releaseProviderSessionIds, []); | 265 | assert.deepEqual(result.releaseProviderSessionIds, []); |
| 266 | }).pipe( | 266 | }).pipe( |
| 267 | Effect.provide( | 267 | Effect.provide( |
| 268 | testLayer({ | 268 | layerTest({ |
| 269 | [currentInstanceId]: { continuationKey: "codex:account:primary" }, | 269 | [currentInstanceId]: { continuationKey: "codex:account:primary" }, |
| 270 | codex_other: { continuationKey: "codex:account:other" }, | 270 | codex_other: { continuationKey: "codex:account:other" }, |
| 271 | }), | 271 | }), |
| ... | @@ -290,7 +290,7 @@ it.effect.each(["stopped", "error"] as const)( | ... | @@ -290,7 +290,7 @@ it.effect.each(["stopped", "error"] as const)( |
| 290 | assert.deepEqual(result.releaseProviderSessionIds, []); | 290 | assert.deepEqual(result.releaseProviderSessionIds, []); |
| 291 | }).pipe( | 291 | }).pipe( |
| 292 | Effect.provide( | 292 | Effect.provide( |
| 293 | testLayer({ [currentInstanceId]: { continuationKey: "codex:account:primary" } }, (input) => | 293 | layerTest({ [currentInstanceId]: { continuationKey: "codex:account:primary" } }, (input) => |
| 294 | Effect.succeed(acpSelectionTransition(input)), | 294 | Effect.succeed(acpSelectionTransition(input)), |
| 295 | ), | 295 | ), |
| 296 | ), | 296 | ), |
| ... | @@ -309,7 +309,7 @@ it.effect("rejects a model change the dead record never negotiated support for", | ... | @@ -309,7 +309,7 @@ it.effect("rejects a model change the dead record never negotiated support for", |
| 309 | assert.instanceOf(result, ProviderSwitch.ProviderSwitchPlanError); | 309 | assert.instanceOf(result, ProviderSwitch.ProviderSwitchPlanError); |
| 310 | }).pipe( | 310 | }).pipe( |
| 311 | Effect.provide( | 311 | Effect.provide( |
| 312 | testLayer({ [currentInstanceId]: { continuationKey: "codex:account:primary" } }, (input) => | 312 | layerTest({ [currentInstanceId]: { continuationKey: "codex:account:primary" } }, (input) => |
| 313 | Effect.succeed(acpSelectionTransition(input)), | 313 | Effect.succeed(acpSelectionTransition(input)), |
| 314 | ), | 314 | ), |
| 315 | ), | 315 | ), |
| ... | @@ -333,7 +333,7 @@ it.effect("distinguishes compatible and incompatible instances of the same drive | ... | @@ -333,7 +333,7 @@ it.effect("distinguishes compatible and incompatible instances of the same drive |
| 333 | assert.equal(incompatible.transition.type, "create_with_handoff"); | 333 | assert.equal(incompatible.transition.type, "create_with_handoff"); |
| 334 | }).pipe( | 334 | }).pipe( |
| 335 | Effect.provide( | 335 | Effect.provide( |
| 336 | testLayer({ | 336 | layerTest({ |
| 337 | [currentInstanceId]: { continuationKey: "codex:account:primary" }, | 337 | [currentInstanceId]: { continuationKey: "codex:account:primary" }, |
| 338 | codex_compatible: { continuationKey: "codex:account:primary" }, | 338 | codex_compatible: { continuationKey: "codex:account:primary" }, |
| 339 | codex_incompatible: { continuationKey: "codex:account:other" }, | 339 | codex_incompatible: { continuationKey: "codex:account:other" }, |
apps/server/src/orchestration-v2/ProviderTurnControlService.test.ts+6-6| ... | @@ -207,7 +207,7 @@ it.effect( | ... | @@ -207,7 +207,7 @@ it.effect( |
| 207 | rollbackThread: () => Effect.die("unused rollbackThread"), | 207 | rollbackThread: () => Effect.die("unused rollbackThread"), |
| 208 | forkThread: () => Effect.die("unused forkThread"), | 208 | forkThread: () => Effect.die("unused forkThread"), |
| 209 | }; | 209 | }; |
| 210 | const projectionLayer = Layer.succeed( | 210 | const layerProjection = Layer.succeed( |
| 211 | ProjectionStore.ProjectionStoreV2, | 211 | ProjectionStore.ProjectionStoreV2, |
| 212 | ProjectionStore.ProjectionStoreV2.of({ | 212 | ProjectionStore.ProjectionStoreV2.of({ |
| 213 | apply: () => Effect.void, | 213 | apply: () => Effect.void, |
| ... | @@ -258,7 +258,7 @@ it.effect( | ... | @@ -258,7 +258,7 @@ it.effect( |
| 258 | getThreadSnapshotWindow: () => Effect.die("unused getThreadSnapshotWindow"), | 258 | getThreadSnapshotWindow: () => Effect.die("unused getThreadSnapshotWindow"), |
| 259 | }), | 259 | }), |
| 260 | ); | 260 | ); |
| 261 | const sessionManagerLayer = Layer.succeed( | 261 | const layerSessionManager = Layer.succeed( |
| 262 | ProviderSessionManager.ProviderSessionManagerV2, | 262 | ProviderSessionManager.ProviderSessionManagerV2, |
| 263 | ProviderSessionManager.ProviderSessionManagerV2.of({ | 263 | ProviderSessionManager.ProviderSessionManagerV2.of({ |
| 264 | shutdown: Effect.void, | 264 | shutdown: Effect.void, |
| ... | @@ -273,8 +273,8 @@ it.effect( | ... | @@ -273,8 +273,8 @@ it.effect( |
| 273 | detach: () => Effect.void, | 273 | detach: () => Effect.void, |
| 274 | }), | 274 | }), |
| 275 | ); | 275 | ); |
| 276 | const controlLayer = ProviderTurnControlService.layer.pipe( | 276 | const layerControl = ProviderTurnControlService.layer.pipe( |
| 277 | Layer.provide(Layer.merge(projectionLayer, sessionManagerLayer)), | 277 | Layer.provide(Layer.merge(layerProjection, layerSessionManager)), |
| 278 | ); | 278 | ); |
| 279 | 279 | ||
| 280 | const [ordinaryInterrupt, unrelatedRestart] = yield* Effect.gen(function* () { | 280 | const [ordinaryInterrupt, unrelatedRestart] = yield* Effect.gen(function* () { |
| ... | @@ -298,7 +298,7 @@ it.effect( | ... | @@ -298,7 +298,7 @@ it.effect( |
| 298 | }), | 298 | }), |
| 299 | ); | 299 | ); |
| 300 | return [ordinary, unrelated] as const; | 300 | return [ordinary, unrelated] as const; |
| 301 | }).pipe(Effect.provide(controlLayer)); | 301 | }).pipe(Effect.provide(layerControl)); |
| 302 | 302 | ||
| 303 | assert.isTrue(Exit.isFailure(ordinaryInterrupt)); | 303 | assert.isTrue(Exit.isFailure(ordinaryInterrupt)); |
| 304 | assert.isTrue(Exit.isFailure(unrelatedRestart)); | 304 | assert.isTrue(Exit.isFailure(unrelatedRestart)); |
| ... | @@ -314,7 +314,7 @@ it.effect( | ... | @@ -314,7 +314,7 @@ it.effect( |
| 314 | providerTurnId, | 314 | providerTurnId, |
| 315 | interruptedAttemptId: attemptId, | 315 | interruptedAttemptId: attemptId, |
| 316 | }); | 316 | }); |
| 317 | }).pipe(Effect.provide(controlLayer)); | 317 | }).pipe(Effect.provide(layerControl)); |
| 318 | 318 | ||
| 319 | const interrupted = yield* Ref.get(interruptedThread); | 319 | const interrupted = yield* Ref.get(interruptedThread); |
| 320 | assert.isNotNull(interrupted); | 320 | assert.isNotNull(interrupted); |
apps/server/src/orchestration-v2/ProviderTurnStartService.testkit.ts+1-1| ... | @@ -5,7 +5,7 @@ import * as Option from "effect/Option"; | ... | @@ -5,7 +5,7 @@ import * as Option from "effect/Option"; |
| 5 | import * as GitWorkflow from "../git/GitWorkflowService.ts"; | 5 | import * as GitWorkflow from "../git/GitWorkflowService.ts"; |
| 6 | import * as ProjectService from "../project/ProjectService.ts"; | 6 | import * as ProjectService from "../project/ProjectService.ts"; |
| 7 | 7 | ||
| 8 | export const worktreeRepairDependenciesTestLayer = Layer.merge( | 8 | export const layer = Layer.merge( |
| 9 | Layer.mock(GitWorkflow.GitWorkflowService)({ | 9 | Layer.mock(GitWorkflow.GitWorkflowService)({ |
| 10 | pruneWorktrees: () => Effect.void, | 10 | pruneWorktrees: () => Effect.void, |
| 11 | createWorktree: () => Effect.succeed({} as never), | 11 | createWorktree: () => Effect.succeed({} as never), |
apps/server/src/orchestration-v2/PullRequestSyncReactor.test.ts+2-2| ... | @@ -213,7 +213,7 @@ const makeHarness = Effect.fn("makePullRequestSyncHarness")(function* (options: | ... | @@ -213,7 +213,7 @@ const makeHarness = Effect.fn("makePullRequestSyncHarness")(function* (options: |
| 213 | return Effect.die(new Error(`Unexpected command: ${command.type}`)); | 213 | return Effect.die(new Error(`Unexpected command: ${command.type}`)); |
| 214 | }; | 214 | }; |
| 215 | 215 | ||
| 216 | const dependencies = Layer.mergeAll( | 216 | const layerDependencies = Layer.mergeAll( |
| 217 | Layer.mock(PullRequestService.PullRequestService)({ | 217 | Layer.mock(PullRequestService.PullRequestService)({ |
| 218 | summary, | 218 | summary, |
| 219 | stack, | 219 | stack, |
| ... | @@ -269,7 +269,7 @@ const makeHarness = Effect.fn("makePullRequestSyncHarness")(function* (options: | ... | @@ -269,7 +269,7 @@ const makeHarness = Effect.fn("makePullRequestSyncHarness")(function* (options: |
| 269 | summaryCalls, | 269 | summaryCalls, |
| 270 | stackCalls, | 270 | stackCalls, |
| 271 | domainEvents, | 271 | domainEvents, |
| 272 | layer: PullRequestSyncReactor.layer.pipe(Layer.provide(dependencies)), | 272 | layer: PullRequestSyncReactor.layer.pipe(Layer.provide(layerDependencies)), |
| 273 | }; | 273 | }; |
| 274 | }); | 274 | }); |
| 275 | 275 |
apps/server/src/orchestration-v2/ResourceCleanupService.ts+1-1| ... | @@ -30,7 +30,7 @@ export class ResourceCleanupService extends Context.Reference<{ | ... | @@ -30,7 +30,7 @@ export class ResourceCleanupService extends Context.Reference<{ |
| 30 | }), | 30 | }), |
| 31 | }) {} | 31 | }) {} |
| 32 | 32 | ||
| 33 | export const live = Layer.effect( | 33 | export const layer = Layer.effect( |
| 34 | ResourceCleanupService, | 34 | ResourceCleanupService, |
| 35 | Effect.gen(function* () { | 35 | Effect.gen(function* () { |
| 36 | const terminals = yield* TerminalManager.TerminalManager; | 36 | const terminals = yield* TerminalManager.TerminalManager; |
apps/server/src/orchestration-v2/RestartContinuation.test.ts+16-7| ... | @@ -319,7 +319,7 @@ it.effect("does not duplicate delivery and yields to newer user work or opt-out" | ... | @@ -319,7 +319,7 @@ it.effect("does not duplicate delivery and yields to newer user work or opt-out" |
| 319 | const commands: Parameters< | 319 | const commands: Parameters< |
| 320 | ThreadManagementService.ThreadManagementService["Service"]["dispatch"] | 320 | ThreadManagementService.ThreadManagementService["Service"]["dispatch"] |
| 321 | >[0][] = []; | 321 | >[0][] = []; |
| 322 | const threads = Layer.mock(ThreadManagementService.ThreadManagementService)({ | 322 | const layerThreads = Layer.mock(ThreadManagementService.ThreadManagementService)({ |
| 323 | getThreadRecords: () => Effect.succeed(projection), | 323 | getThreadRecords: () => Effect.succeed(projection), |
| 324 | recoverDelegatedTask: () => Effect.void, | 324 | recoverDelegatedTask: () => Effect.void, |
| 325 | dispatch: (command) => { | 325 | dispatch: (command) => { |
| ... | @@ -329,12 +329,16 @@ it.effect("does not duplicate delivery and yields to newer user work or opt-out" | ... | @@ -329,12 +329,16 @@ it.effect("does not duplicate delivery and yields to newer user work or opt-out" |
| 329 | return Effect.succeed({} as never); | 329 | return Effect.succeed({} as never); |
| 330 | }, | 330 | }, |
| 331 | }); | 331 | }); |
| 332 | const enabled = Layer.merge( | 332 | const layerEnabled = Layer.merge( |
| 333 | threads, | 333 | layerThreads, |
| 334 | ServerSettings.layerTest({ continueThreadsAfterServerUpdate: true }), | 334 | ServerSettings.layerTest({ continueThreadsAfterServerUpdate: true }), |
| 335 | ); | 335 | ); |
| 336 | yield* continueRestartedRun({ threadId, sourceRunId: runId }).pipe(Effect.provide(enabled)); | 336 | yield* continueRestartedRun({ threadId, sourceRunId: runId }).pipe( |
| 337 | yield* continueRestartedRun({ threadId, sourceRunId: runId }).pipe(Effect.provide(enabled)); | 337 | Effect.provide(layerEnabled), |
| 338 | ); | ||
| 339 | yield* continueRestartedRun({ threadId, sourceRunId: runId }).pipe( | ||
| 340 | Effect.provide(layerEnabled), | ||
| 341 | ); | ||
| 338 | assert.lengthOf(commands, 1); | 342 | assert.lengthOf(commands, 1); |
| 339 | assert.match(String(commands[0]!.commandId), /run:restart$/); | 343 | assert.match(String(commands[0]!.commandId), /run:restart$/); |
| 340 | if (commands[0]!.type === "message.dispatch") | 344 | if (commands[0]!.type === "message.dispatch") |
| ... | @@ -352,12 +356,17 @@ it.effect("does not duplicate delivery and yields to newer user work or opt-out" | ... | @@ -352,12 +356,17 @@ it.effect("does not duplicate delivery and yields to newer user work or opt-out" |
| 352 | }, | 356 | }, |
| 353 | ], | 357 | ], |
| 354 | }; | 358 | }; |
| 355 | yield* continueRestartedRun({ threadId, sourceRunId: runId }).pipe(Effect.provide(enabled)); | 359 | yield* continueRestartedRun({ threadId, sourceRunId: runId }).pipe( |
| 360 | Effect.provide(layerEnabled), | ||
| 361 | ); | ||
| 356 | assert.lengthOf(commands, 1); | 362 | assert.lengthOf(commands, 1); |
| 357 | projection = { ...projection, runs: [projection.runs[0]!] }; | 363 | projection = { ...projection, runs: [projection.runs[0]!] }; |
| 358 | yield* continueRestartedRun({ threadId, sourceRunId: runId }).pipe( | 364 | yield* continueRestartedRun({ threadId, sourceRunId: runId }).pipe( |
| 359 | Effect.provide( | 365 | Effect.provide( |
| 360 | Layer.merge(threads, ServerSettings.layerTest({ continueThreadsAfterServerUpdate: false })), | 366 | Layer.merge( |
| 367 | layerThreads, | ||
| 368 | ServerSettings.layerTest({ continueThreadsAfterServerUpdate: false }), | ||
| 369 | ), | ||
| 361 | ), | 370 | ), |
| 362 | ); | 371 | ); |
| 363 | assert.lengthOf(commands, 1); | 372 | assert.lengthOf(commands, 1); |
apps/server/src/orchestration-v2/RunCompletionReads.test.ts+6-6| ... | @@ -13,12 +13,12 @@ import * as DateTime from "effect/DateTime"; | ... | @@ -13,12 +13,12 @@ import * as DateTime from "effect/DateTime"; |
| 13 | import * as Effect from "effect/Effect"; | 13 | import * as Effect from "effect/Effect"; |
| 14 | import * as Layer from "effect/Layer"; | 14 | import * as Layer from "effect/Layer"; |
| 15 | import * as SqlClient from "effect/sql/SqlClient"; | 15 | import * as SqlClient from "effect/sql/SqlClient"; |
| 16 | import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; | 16 | import * as SqlitePersistence from "../persistence/Layers/Sqlite.ts"; |
| 17 | import * as ProjectionStore from "./ProjectionStore.ts"; | 17 | import * as ProjectionStore from "./ProjectionStore.ts"; |
| 18 | 18 | ||
| 19 | const databaseLayer = Layer.mergeAll( | 19 | const layerDatabase = Layer.mergeAll( |
| 20 | SqlitePersistenceMemory, | 20 | SqlitePersistence.layerMemory, |
| 21 | ProjectionStore.layer.pipe(Layer.provide(SqlitePersistenceMemory)), | 21 | ProjectionStore.layer.pipe(Layer.provide(SqlitePersistence.layerMemory)), |
| 22 | ); | 22 | ); |
| 23 | 23 | ||
| 24 | it.effect.each(["sqlite", "memory"] as const)( | 24 | it.effect.each(["sqlite", "memory"] as const)( |
| ... | @@ -155,8 +155,8 @@ it.effect.each(["sqlite", "memory"] as const)( | ... | @@ -155,8 +155,8 @@ it.effect.each(["sqlite", "memory"] as const)( |
| 155 | }).pipe( | 155 | }).pipe( |
| 156 | Effect.provide( | 156 | Effect.provide( |
| 157 | storage === "sqlite" | 157 | storage === "sqlite" |
| 158 | ? databaseLayer | 158 | ? layerDatabase |
| 159 | : Layer.merge(SqlitePersistenceMemory, ProjectionStore.layerMemory), | 159 | : Layer.merge(SqlitePersistence.layerMemory, ProjectionStore.layerMemory), |
| 160 | ), | 160 | ), |
| 161 | ), | 161 | ), |
| 162 | ); | 162 | ); |
apps/server/src/orchestration-v2/RunExecutionService.test.ts+28-28| ... | @@ -57,7 +57,7 @@ import * as RunFinalizationService from "./RunFinalizationService.ts"; | ... | @@ -57,7 +57,7 @@ import * as RunFinalizationService from "./RunFinalizationService.ts"; |
| 57 | 57 | ||
| 58 | const driver = ProviderDriverKind.make("codex"); | 58 | const driver = ProviderDriverKind.make("codex"); |
| 59 | 59 | ||
| 60 | const RunExecutionTestLayer = RunExecutionService.layer.pipe( | 60 | const layerRunExecutionTest = RunExecutionService.layer.pipe( |
| 61 | Layer.provide( | 61 | Layer.provide( |
| 62 | Layer.mergeAll( | 62 | Layer.mergeAll( |
| 63 | Layer.mock(CheckpointService.CheckpointServiceV2)({ captureBaseline: () => Effect.void }), | 63 | Layer.mock(CheckpointService.CheckpointServiceV2)({ captureBaseline: () => Effect.void }), |
| ... | @@ -588,7 +588,7 @@ it.effect("rechecks run ownership immediately before calling the provider", () = | ... | @@ -588,7 +588,7 @@ it.effect("rechecks run ownership immediately before calling the provider", () = |
| 588 | 588 | ||
| 589 | assert.equal(yield* Ref.get(guardCalls), 2); | 589 | assert.equal(yield* Ref.get(guardCalls), 2); |
| 590 | assert.equal(yield* Ref.get(providerStarts), 0); | 590 | assert.equal(yield* Ref.get(providerStarts), 0); |
| 591 | }).pipe(Effect.provide(RunExecutionTestLayer)), | 591 | }).pipe(Effect.provide(layerRunExecutionTest)), |
| 592 | ); | 592 | ); |
| 593 | 593 | ||
| 594 | it.effect("fails the run when its ownership check cannot be read before calling the provider", () => | 594 | it.effect("fails the run when its ownership check cannot be read before calling the provider", () => |
| ... | @@ -600,7 +600,7 @@ it.effect("fails the run when its ownership check cannot be read before calling | ... | @@ -600,7 +600,7 @@ it.effect("fails the run when its ownership check cannot be read before calling |
| 600 | const runId = RunId.make("run:run-execution-start-guard-read"); | 600 | const runId = RunId.make("run:run-execution-start-guard-read"); |
| 601 | const attemptId = RunAttemptId.make("attempt:run-execution-start-guard-read"); | 601 | const attemptId = RunAttemptId.make("attempt:run-execution-start-guard-read"); |
| 602 | const providerInstanceId = ProviderInstanceId.make("codex"); | 602 | const providerInstanceId = ProviderInstanceId.make("codex"); |
| 603 | const testLayer = RunExecutionService.layer.pipe( | 603 | const layerTest = RunExecutionService.layer.pipe( |
| 604 | Layer.provide( | 604 | Layer.provide( |
| 605 | Layer.mergeAll( | 605 | Layer.mergeAll( |
| 606 | Layer.mock(CheckpointService.CheckpointServiceV2)({ captureBaseline: () => Effect.void }), | 606 | Layer.mock(CheckpointService.CheckpointServiceV2)({ captureBaseline: () => Effect.void }), |
| ... | @@ -686,7 +686,7 @@ it.effect("fails the run when its ownership check cannot be read before calling | ... | @@ -686,7 +686,7 @@ it.effect("fails the run when its ownership check cannot be read before calling |
| 686 | }, | 686 | }, |
| 687 | }, | 687 | }, |
| 688 | }); | 688 | }); |
| 689 | }).pipe(Effect.provide(testLayer)); | 689 | }).pipe(Effect.provide(layerTest)); |
| 690 | 690 | ||
| 691 | assert.equal(yield* Ref.get(guardCalls), 2); | 691 | assert.equal(yield* Ref.get(guardCalls), 2); |
| 692 | assert.equal(yield* Ref.get(providerStarts), 0); | 692 | assert.equal(yield* Ref.get(providerStarts), 0); |
| ... | @@ -778,7 +778,7 @@ it.effect( | ... | @@ -778,7 +778,7 @@ it.effect( |
| 778 | calls, | 778 | calls, |
| 779 | cases.map((testCase) => testCase.expected), | 779 | cases.map((testCase) => testCase.expected), |
| 780 | ); | 780 | ); |
| 781 | }).pipe(Effect.provide(RunExecutionTestLayer)), | 781 | }).pipe(Effect.provide(layerRunExecutionTest)), |
| 782 | ); | 782 | ); |
| 783 | 783 | ||
| 784 | it.effect("refreshes MCP credential liveness before calling the provider", () => | 784 | it.effect("refreshes MCP credential liveness before calling the provider", () => |
| ... | @@ -849,7 +849,7 @@ it.effect("refreshes MCP credential liveness before calling the provider", () => | ... | @@ -849,7 +849,7 @@ it.effect("refreshes MCP credential liveness before calling the provider", () => |
| 849 | .pipe(Effect.ensuring(Effect.sync(() => touchActiveMcpThread.mockRestore()))); | 849 | .pipe(Effect.ensuring(Effect.sync(() => touchActiveMcpThread.mockRestore()))); |
| 850 | 850 | ||
| 851 | assert.deepEqual(yield* Ref.get(order), [`touch:${threadId}`, "start-turn"]); | 851 | assert.deepEqual(yield* Ref.get(order), [`touch:${threadId}`, "start-turn"]); |
| 852 | }).pipe(Effect.provide(RunExecutionTestLayer)), | 852 | }).pipe(Effect.provide(layerRunExecutionTest)), |
| 853 | ); | 853 | ); |
| 854 | 854 | ||
| 855 | it.effect("starts the provider when checkpoint baseline capture fails", () => | 855 | it.effect("starts the provider when checkpoint baseline capture fails", () => |
| ... | @@ -873,7 +873,7 @@ it.effect("starts the provider when checkpoint baseline capture fails", () => | ... | @@ -873,7 +873,7 @@ it.effect("starts the provider when checkpoint baseline capture fails", () => |
| 873 | effects: ReadonlyArray<PendingOrchestrationEffectV2>; | 873 | effects: ReadonlyArray<PendingOrchestrationEffectV2>; |
| 874 | }> | 874 | }> |
| 875 | >([]); | 875 | >([]); |
| 876 | const testLayer = RunExecutionService.layer.pipe( | 876 | const layerTest = RunExecutionService.layer.pipe( |
| 877 | Layer.provide( | 877 | Layer.provide( |
| 878 | Layer.mergeAll( | 878 | Layer.mergeAll( |
| 879 | Layer.mock(CheckpointService.CheckpointServiceV2)({ | 879 | Layer.mock(CheckpointService.CheckpointServiceV2)({ |
| ... | @@ -950,7 +950,7 @@ it.effect("starts the provider when checkpoint baseline capture fails", () => | ... | @@ -950,7 +950,7 @@ it.effect("starts the provider when checkpoint baseline capture fails", () => |
| 950 | }, | 950 | }, |
| 951 | }, | 951 | }, |
| 952 | }); | 952 | }); |
| 953 | }).pipe(Effect.provide(testLayer)); | 953 | }).pipe(Effect.provide(layerTest)); |
| 954 | 954 | ||
| 955 | assert.equal(yield* Ref.get(providerStarts), 1); | 955 | assert.equal(yield* Ref.get(providerStarts), 1); |
| 956 | 956 | ||
| ... | @@ -990,7 +990,7 @@ it.effect.each(["failure", "interruption", "stale-attempt", "start-guard"] as co | ... | @@ -990,7 +990,7 @@ it.effect.each(["failure", "interruption", "stale-attempt", "start-guard"] as co |
| 990 | const refreshes = yield* Ref.make(0); | 990 | const refreshes = yield* Ref.make(0); |
| 991 | const guardedWrites = yield* Ref.make(0); | 991 | const guardedWrites = yield* Ref.make(0); |
| 992 | const writes = yield* Ref.make<ReadonlyArray<ReadonlyArray<OrchestrationV2DomainEvent>>>([]); | 992 | const writes = yield* Ref.make<ReadonlyArray<ReadonlyArray<OrchestrationV2DomainEvent>>>([]); |
| 993 | const testLayer = RunExecutionService.layer.pipe( | 993 | const layerTest = RunExecutionService.layer.pipe( |
| 994 | Layer.provide( | 994 | Layer.provide( |
| 995 | Layer.mergeAll( | 995 | Layer.mergeAll( |
| 996 | Layer.mock(CheckpointService.CheckpointServiceV2)({ | 996 | Layer.mock(CheckpointService.CheckpointServiceV2)({ |
| ... | @@ -1092,7 +1092,7 @@ it.effect.each(["failure", "interruption", "stale-attempt", "start-guard"] as co | ... | @@ -1092,7 +1092,7 @@ it.effect.each(["failure", "interruption", "stale-attempt", "start-guard"] as co |
| 1092 | }, | 1092 | }, |
| 1093 | }, | 1093 | }, |
| 1094 | }); | 1094 | }); |
| 1095 | }).pipe(Effect.provide(testLayer), Effect.exit); | 1095 | }).pipe(Effect.provide(layerTest), Effect.exit); |
| 1096 | 1096 | ||
| 1097 | assert.equal(yield* Ref.get(providerStarts), 0); | 1097 | assert.equal(yield* Ref.get(providerStarts), 0); |
| 1098 | const events = (yield* Ref.get(writes)).flat(); | 1098 | const events = (yield* Ref.get(writes)).flat(); |
| ... | @@ -1160,7 +1160,7 @@ it.effect("keeps ingesting owned child events after the root turn terminalizes", | ... | @@ -1160,7 +1160,7 @@ it.effect("keeps ingesting owned child events after the root turn terminalizes", |
| 1160 | const subagentNodeId = NodeId.make("node:run-execution-late-child:subagent"); | 1160 | const subagentNodeId = NodeId.make("node:run-execution-late-child:subagent"); |
| 1161 | const childMessageIngested = yield* Deferred.make<void>(); | 1161 | const childMessageIngested = yield* Deferred.make<void>(); |
| 1162 | const order = yield* Ref.make<ReadonlyArray<string>>([]); | 1162 | const order = yield* Ref.make<ReadonlyArray<string>>([]); |
| 1163 | const testLayer = RunExecutionService.layer.pipe( | 1163 | const layerTest = RunExecutionService.layer.pipe( |
| 1164 | Layer.provide( | 1164 | Layer.provide( |
| 1165 | Layer.mergeAll( | 1165 | Layer.mergeAll( |
| 1166 | Layer.mock(CheckpointService.CheckpointServiceV2)({ captureBaseline: () => Effect.void }), | 1166 | Layer.mock(CheckpointService.CheckpointServiceV2)({ captureBaseline: () => Effect.void }), |
| ... | @@ -1337,7 +1337,7 @@ it.effect("keeps ingesting owned child events after the root turn terminalizes", | ... | @@ -1337,7 +1337,7 @@ it.effect("keeps ingesting owned child events after the root turn terminalizes", |
| 1337 | }, | 1337 | }, |
| 1338 | }, | 1338 | }, |
| 1339 | }); | 1339 | }); |
| 1340 | }).pipe(Effect.provide(testLayer)); | 1340 | }).pipe(Effect.provide(layerTest)); |
| 1341 | 1341 | ||
| 1342 | const observed = yield* Deferred.await(childMessageIngested).pipe( | 1342 | const observed = yield* Deferred.await(childMessageIngested).pipe( |
| 1343 | Effect.timeoutOption("2 seconds"), | 1343 | Effect.timeoutOption("2 seconds"), |
| ... | @@ -1585,7 +1585,7 @@ it.effect( | ... | @@ -1585,7 +1585,7 @@ it.effect( |
| 1585 | }> | 1585 | }> |
| 1586 | >([]); | 1586 | >([]); |
| 1587 | const ingestionDone = yield* Deferred.make<void>(); | 1587 | const ingestionDone = yield* Deferred.make<void>(); |
| 1588 | const testLayer = RunExecutionService.layer.pipe( | 1588 | const layerTest = RunExecutionService.layer.pipe( |
| 1589 | Layer.provide( | 1589 | Layer.provide( |
| 1590 | Layer.mergeAll( | 1590 | Layer.mergeAll( |
| 1591 | Layer.mock(CheckpointService.CheckpointServiceV2)({ | 1591 | Layer.mock(CheckpointService.CheckpointServiceV2)({ |
| ... | @@ -1750,7 +1750,7 @@ it.effect( | ... | @@ -1750,7 +1750,7 @@ it.effect( |
| 1750 | }, | 1750 | }, |
| 1751 | }, | 1751 | }, |
| 1752 | }); | 1752 | }); |
| 1753 | }).pipe(Effect.provide(testLayer)); | 1753 | }).pipe(Effect.provide(layerTest)); |
| 1754 | 1754 | ||
| 1755 | const closed = yield* Deferred.await(ingestionDone).pipe(Effect.timeoutOption("2 seconds")); | 1755 | const closed = yield* Deferred.await(ingestionDone).pipe(Effect.timeoutOption("2 seconds")); |
| 1756 | assert.isTrue(Option.isSome(closed), "event subscription did not release"); | 1756 | assert.isTrue(Option.isSome(closed), "event subscription did not release"); |
| ... | @@ -1802,7 +1802,7 @@ it.effect("drops late root provider-thread writes from a superseded attempt", () | ... | @@ -1802,7 +1802,7 @@ it.effect("drops late root provider-thread writes from a superseded attempt", () |
| 1802 | readonly committed: boolean; | 1802 | readonly committed: boolean; |
| 1803 | }> | 1803 | }> |
| 1804 | >([]); | 1804 | >([]); |
| 1805 | const testLayer = RunExecutionService.layer.pipe( | 1805 | const layerTest = RunExecutionService.layer.pipe( |
| 1806 | Layer.provide( | 1806 | Layer.provide( |
| 1807 | Layer.mergeAll( | 1807 | Layer.mergeAll( |
| 1808 | Layer.mock(CheckpointService.CheckpointServiceV2)({ captureBaseline: () => Effect.void }), | 1808 | Layer.mock(CheckpointService.CheckpointServiceV2)({ captureBaseline: () => Effect.void }), |
| ... | @@ -1966,7 +1966,7 @@ it.effect("drops late root provider-thread writes from a superseded attempt", () | ... | @@ -1966,7 +1966,7 @@ it.effect("drops late root provider-thread writes from a superseded attempt", () |
| 1966 | }, | 1966 | }, |
| 1967 | }, | 1967 | }, |
| 1968 | }); | 1968 | }); |
| 1969 | }).pipe(Effect.provide(testLayer)); | 1969 | }).pipe(Effect.provide(layerTest)); |
| 1970 | 1970 | ||
| 1971 | const closed = yield* Deferred.await(ingestionDone).pipe(Effect.timeoutOption("2 seconds")); | 1971 | const closed = yield* Deferred.await(ingestionDone).pipe(Effect.timeoutOption("2 seconds")); |
| 1972 | assert.isTrue( | 1972 | assert.isTrue( |
| ... | @@ -2006,7 +2006,7 @@ it.effect( | ... | @@ -2006,7 +2006,7 @@ it.effect( |
| 2006 | // Probe stays true forever; open background items must pin the stream | 2006 | // Probe stays true forever; open background items must pin the stream |
| 2007 | // past ownership-loss so late turn_item completions still land. | 2007 | // past ownership-loss so late turn_item completions still land. |
| 2008 | const ingestionDone = yield* Deferred.make<void>(); | 2008 | const ingestionDone = yield* Deferred.make<void>(); |
| 2009 | const testLayer = RunExecutionService.layer.pipe( | 2009 | const layerTest = RunExecutionService.layer.pipe( |
| 2010 | Layer.provide( | 2010 | Layer.provide( |
| 2011 | Layer.mergeAll( | 2011 | Layer.mergeAll( |
| 2012 | Layer.mock(CheckpointService.CheckpointServiceV2)({ | 2012 | Layer.mock(CheckpointService.CheckpointServiceV2)({ |
| ... | @@ -2149,7 +2149,7 @@ it.effect( | ... | @@ -2149,7 +2149,7 @@ it.effect( |
| 2149 | }, | 2149 | }, |
| 2150 | }, | 2150 | }, |
| 2151 | }); | 2151 | }); |
| 2152 | }).pipe(Effect.provide(testLayer)); | 2152 | }).pipe(Effect.provide(layerTest)); |
| 2153 | 2153 | ||
| 2154 | const closed = yield* Deferred.await(ingestionDone).pipe(Effect.timeoutOption("2 seconds")); | 2154 | const closed = yield* Deferred.await(ingestionDone).pipe(Effect.timeoutOption("2 seconds")); |
| 2155 | assert.isTrue( | 2155 | assert.isTrue( |
| ... | @@ -2177,7 +2177,7 @@ it.effect( | ... | @@ -2177,7 +2177,7 @@ it.effect( |
| 2177 | const observed = yield* Ref.make<ReadonlyArray<string>>([]); | 2177 | const observed = yield* Ref.make<ReadonlyArray<string>>([]); |
| 2178 | const ingestionDone = yield* Deferred.make<void>(); | 2178 | const ingestionDone = yield* Deferred.make<void>(); |
| 2179 | const scopedProbeArgs = yield* Ref.make<ReadonlyArray<ProviderThreadId>>([]); | 2179 | const scopedProbeArgs = yield* Ref.make<ReadonlyArray<ProviderThreadId>>([]); |
| 2180 | const testLayer = RunExecutionService.layer.pipe( | 2180 | const layerTest = RunExecutionService.layer.pipe( |
| 2181 | Layer.provide( | 2181 | Layer.provide( |
| 2182 | Layer.mergeAll( | 2182 | Layer.mergeAll( |
| 2183 | Layer.mock(CheckpointService.CheckpointServiceV2)({ | 2183 | Layer.mock(CheckpointService.CheckpointServiceV2)({ |
| ... | @@ -2319,7 +2319,7 @@ it.effect( | ... | @@ -2319,7 +2319,7 @@ it.effect( |
| 2319 | }, | 2319 | }, |
| 2320 | }, | 2320 | }, |
| 2321 | }); | 2321 | }); |
| 2322 | }).pipe(Effect.provide(testLayer)); | 2322 | }).pipe(Effect.provide(layerTest)); |
| 2323 | 2323 | ||
| 2324 | const closed = yield* Deferred.await(ingestionDone).pipe(Effect.timeoutOption("2 seconds")); | 2324 | const closed = yield* Deferred.await(ingestionDone).pipe(Effect.timeoutOption("2 seconds")); |
| 2325 | assert.isTrue(Option.isSome(closed), "event subscription did not release"); | 2325 | assert.isTrue(Option.isSome(closed), "event subscription did not release"); |
| ... | @@ -2346,7 +2346,7 @@ it.effect( | ... | @@ -2346,7 +2346,7 @@ it.effect( |
| 2346 | const written = yield* Ref.make<ReadonlyArray<OrchestrationV2DomainEvent>>([]); | 2346 | const written = yield* Ref.make<ReadonlyArray<OrchestrationV2DomainEvent>>([]); |
| 2347 | const ingested = yield* Ref.make<ReadonlyArray<ProviderAdapterV2Event>>([]); | 2347 | const ingested = yield* Ref.make<ReadonlyArray<ProviderAdapterV2Event>>([]); |
| 2348 | const ingestionDone = yield* Deferred.make<void>(); | 2348 | const ingestionDone = yield* Deferred.make<void>(); |
| 2349 | const testLayer = RunExecutionService.layer.pipe( | 2349 | const layerTest = RunExecutionService.layer.pipe( |
| 2350 | Layer.provide( | 2350 | Layer.provide( |
| 2351 | Layer.mergeAll( | 2351 | Layer.mergeAll( |
| 2352 | Layer.mock(CheckpointService.CheckpointServiceV2)({ | 2352 | Layer.mock(CheckpointService.CheckpointServiceV2)({ |
| ... | @@ -2559,7 +2559,7 @@ it.effect( | ... | @@ -2559,7 +2559,7 @@ it.effect( |
| 2559 | }, | 2559 | }, |
| 2560 | }, | 2560 | }, |
| 2561 | }); | 2561 | }); |
| 2562 | }).pipe(Effect.provide(testLayer)); | 2562 | }).pipe(Effect.provide(layerTest)); |
| 2563 | 2563 | ||
| 2564 | const closed = yield* Deferred.await(ingestionDone).pipe(Effect.timeoutOption("2 seconds")); | 2564 | const closed = yield* Deferred.await(ingestionDone).pipe(Effect.timeoutOption("2 seconds")); |
| 2565 | assert.isTrue(Option.isSome(closed), "event ingestion fiber did not finish"); | 2565 | assert.isTrue(Option.isSome(closed), "event ingestion fiber did not finish"); |
| ... | @@ -2719,7 +2719,7 @@ it.effect( | ... | @@ -2719,7 +2719,7 @@ it.effect( |
| 2719 | const providerInstanceId = ProviderInstanceId.make("codex"); | 2719 | const providerInstanceId = ProviderInstanceId.make("codex"); |
| 2720 | const written = yield* Ref.make<ReadonlyArray<OrchestrationV2DomainEvent>>([]); | 2720 | const written = yield* Ref.make<ReadonlyArray<OrchestrationV2DomainEvent>>([]); |
| 2721 | const ingestionDone = yield* Deferred.make<void>(); | 2721 | const ingestionDone = yield* Deferred.make<void>(); |
| 2722 | const testLayer = RunExecutionService.layer.pipe( | 2722 | const layerTest = RunExecutionService.layer.pipe( |
| 2723 | Layer.provide( | 2723 | Layer.provide( |
| 2724 | Layer.mergeAll( | 2724 | Layer.mergeAll( |
| 2725 | Layer.mock(CheckpointService.CheckpointServiceV2)({ | 2725 | Layer.mock(CheckpointService.CheckpointServiceV2)({ |
| ... | @@ -2889,7 +2889,7 @@ it.effect( | ... | @@ -2889,7 +2889,7 @@ it.effect( |
| 2889 | }, | 2889 | }, |
| 2890 | }, | 2890 | }, |
| 2891 | }); | 2891 | }); |
| 2892 | }).pipe(Effect.provide(testLayer)); | 2892 | }).pipe(Effect.provide(layerTest)); |
| 2893 | 2893 | ||
| 2894 | const closed = yield* Deferred.await(ingestionDone).pipe(Effect.timeoutOption("2 seconds")); | 2894 | const closed = yield* Deferred.await(ingestionDone).pipe(Effect.timeoutOption("2 seconds")); |
| 2895 | assert.isTrue(Option.isSome(closed), "event ingestion fiber did not finish"); | 2895 | assert.isTrue(Option.isSome(closed), "event ingestion fiber did not finish"); |
| ... | @@ -3416,7 +3416,7 @@ function captureRootRunTermination(input: { | ... | @@ -3416,7 +3416,7 @@ function captureRootRunTermination(input: { |
| 3416 | } | 3416 | } |
| 3417 | } | 3417 | } |
| 3418 | }); | 3418 | }); |
| 3419 | const testLayer = RunExecutionService.layer.pipe( | 3419 | const layerTest = RunExecutionService.layer.pipe( |
| 3420 | Layer.provide( | 3420 | Layer.provide( |
| 3421 | Layer.mergeAll( | 3421 | Layer.mergeAll( |
| 3422 | Layer.mock(CheckpointService.CheckpointServiceV2)({ captureBaseline: () => Effect.void }), | 3422 | Layer.mock(CheckpointService.CheckpointServiceV2)({ captureBaseline: () => Effect.void }), |
| ... | @@ -3540,7 +3540,7 @@ function captureRootRunTermination(input: { | ... | @@ -3540,7 +3540,7 @@ function captureRootRunTermination(input: { |
| 3540 | }, | 3540 | }, |
| 3541 | }, | 3541 | }, |
| 3542 | }); | 3542 | }); |
| 3543 | }).pipe(Effect.provide(testLayer)); | 3543 | }).pipe(Effect.provide(layerTest)); |
| 3544 | 3544 | ||
| 3545 | yield* Deferred.await(ingestionDone); | 3545 | yield* Deferred.await(ingestionDone); |
| 3546 | return { written: yield* Ref.get(writtenItems), observed: yield* Ref.get(observed) }; | 3546 | return { written: yield* Ref.get(writtenItems), observed: yield* Ref.get(observed) }; |
| ... | @@ -3876,7 +3876,7 @@ function runBackgroundItemScenario( | ... | @@ -3876,7 +3876,7 @@ function runBackgroundItemScenario( |
| 3876 | const providerInstanceId = ProviderInstanceId.make("codex"); | 3876 | const providerInstanceId = ProviderInstanceId.make("codex"); |
| 3877 | const observed = yield* Ref.make<ReadonlyArray<string>>([]); | 3877 | const observed = yield* Ref.make<ReadonlyArray<string>>([]); |
| 3878 | const ingestionDone = yield* Deferred.make<void>(); | 3878 | const ingestionDone = yield* Deferred.make<void>(); |
| 3879 | const testLayer = RunExecutionService.layer.pipe( | 3879 | const layerTest = RunExecutionService.layer.pipe( |
| 3880 | Layer.provide( | 3880 | Layer.provide( |
| 3881 | Layer.mergeAll( | 3881 | Layer.mergeAll( |
| 3882 | Layer.mock(CheckpointService.CheckpointServiceV2)({ captureBaseline: () => Effect.void }), | 3882 | Layer.mock(CheckpointService.CheckpointServiceV2)({ captureBaseline: () => Effect.void }), |
| ... | @@ -3986,7 +3986,7 @@ function runBackgroundItemScenario( | ... | @@ -3986,7 +3986,7 @@ function runBackgroundItemScenario( |
| 3986 | }, | 3986 | }, |
| 3987 | }, | 3987 | }, |
| 3988 | }); | 3988 | }); |
| 3989 | }).pipe(Effect.provide(testLayer)); | 3989 | }).pipe(Effect.provide(layerTest)); |
| 3990 | 3990 | ||
| 3991 | const closed = yield* Deferred.await(ingestionDone).pipe(Effect.timeoutOption("2 seconds")); | 3991 | const closed = yield* Deferred.await(ingestionDone).pipe(Effect.timeoutOption("2 seconds")); |
| 3992 | assert.isTrue(Option.isSome(closed), "event ingestion fiber did not finish"); | 3992 | assert.isTrue(Option.isSome(closed), "event ingestion fiber did not finish"); |
apps/server/src/orchestration-v2/RunFinalizationService.test.ts+1-1| ... | @@ -87,7 +87,7 @@ it.effect.each( | ... | @@ -87,7 +87,7 @@ it.effect.each( |
| 87 | const refreshed: string[] = []; | 87 | const refreshed: string[] = []; |
| 88 | const threadId = ThreadId.make("thread-pr-refresh"); | 88 | const threadId = ThreadId.make("thread-pr-refresh"); |
| 89 | const runId = RunId.make("completed-run"); | 89 | const runId = RunId.make("completed-run"); |
| 90 | const layer = RunFinalization.observerLive.pipe( | 90 | const layer = RunFinalization.layerObserver.pipe( |
| 91 | Layer.provide( | 91 | Layer.provide( |
| 92 | Layer.mergeAll( | 92 | Layer.mergeAll( |
| 93 | Layer.mock(WorkspaceEntries.WorkspaceEntries)({ refresh: () => Effect.void }), | 93 | Layer.mock(WorkspaceEntries.WorkspaceEntries)({ refresh: () => Effect.void }), |
apps/server/src/orchestration-v2/RunFinalizationService.ts+1-1| ... | @@ -87,7 +87,7 @@ const make = Effect.gen(function* () { | ... | @@ -87,7 +87,7 @@ const make = Effect.gen(function* () { |
| 87 | 87 | ||
| 88 | export const layer = Layer.effect(RunFinalizationService, make); | 88 | export const layer = Layer.effect(RunFinalizationService, make); |
| 89 | 89 | ||
| 90 | export const observerLive = Layer.effect( | 90 | export const layerObserver = Layer.effect( |
| 91 | RunFinalizationObserver, | 91 | RunFinalizationObserver, |
| 92 | Effect.gen(function* () { | 92 | Effect.gen(function* () { |
| 93 | const workspaceEntries = yield* WorkspaceEntries.WorkspaceEntries; | 93 | const workspaceEntries = yield* WorkspaceEntries.WorkspaceEntries; |
apps/server/src/orchestration-v2/RuntimePolicy.test.ts+2-2| ... | @@ -76,7 +76,7 @@ const providerInstanceFor = (instanceId: ProviderInstanceId) => | ... | @@ -76,7 +76,7 @@ const providerInstanceFor = (instanceId: ProviderInstanceId) => |
| 76 | }, | 76 | }, |
| 77 | }) as ProviderInstance; | 77 | }) as ProviderInstance; |
| 78 | 78 | ||
| 79 | const TestLayer = RuntimePolicy.layerFromProjectStore.pipe( | 79 | const layerTest = RuntimePolicy.layerFromProjectStore.pipe( |
| 80 | Layer.provide( | 80 | Layer.provide( |
| 81 | Layer.succeed(ProviderInstanceRegistry.ProviderInstanceRegistry, { | 81 | Layer.succeed(ProviderInstanceRegistry.ProviderInstanceRegistry, { |
| 82 | getInstance: (instanceId) => Effect.succeed(providerInstanceFor(instanceId)), | 82 | getInstance: (instanceId) => Effect.succeed(providerInstanceFor(instanceId)), |
| ... | @@ -109,7 +109,7 @@ const TestLayer = RuntimePolicy.layerFromProjectStore.pipe( | ... | @@ -109,7 +109,7 @@ const TestLayer = RuntimePolicy.layerFromProjectStore.pipe( |
| 109 | ), | 109 | ), |
| 110 | ); | 110 | ); |
| 111 | 111 | ||
| 112 | it.layer(TestLayer)("RuntimePolicyV2", (it) => { | 112 | it.layer(layerTest)("RuntimePolicyV2", (it) => { |
| 113 | it.effect("uses the project root for local-checkout threads", () => | 113 | it.effect("uses the project root for local-checkout threads", () => |
| 114 | Effect.gen(function* () { | 114 | Effect.gen(function* () { |
| 115 | const policy = yield* RuntimePolicy.RuntimePolicyV2; | 115 | const policy = yield* RuntimePolicy.RuntimePolicyV2; |
apps/server/src/orchestration-v2/RuntimeRequestService.test.ts+13-13| ... | @@ -45,7 +45,7 @@ function resolvedRuntimeRequest( | ... | @@ -45,7 +45,7 @@ function resolvedRuntimeRequest( |
| 45 | }; | 45 | }; |
| 46 | } | 46 | } |
| 47 | 47 | ||
| 48 | function runtimeRequestTestLayer( | 48 | function layerRuntimeRequestTest( |
| 49 | projection: OrchestrationV2ThreadProjection, | 49 | projection: OrchestrationV2ThreadProjection, |
| 50 | getSession: ProviderSessionManager.ProviderSessionManagerV2Shape["get"], | 50 | getSession: ProviderSessionManager.ProviderSessionManagerV2Shape["get"], |
| 51 | ) { | 51 | ) { |
| ... | @@ -96,7 +96,7 @@ it.effect("forwards orchestrator-resolved runtime requests to the live adapter", | ... | @@ -96,7 +96,7 @@ it.effect("forwards orchestrator-resolved runtime requests to the live adapter", |
| 96 | }, | 96 | }, |
| 97 | ], | 97 | ], |
| 98 | } as unknown as OrchestrationV2ThreadProjection; | 98 | } as unknown as OrchestrationV2ThreadProjection; |
| 99 | const testLayer = RuntimeRequestService.layer.pipe( | 99 | const layerTest = RuntimeRequestService.layer.pipe( |
| 100 | Layer.provide( | 100 | Layer.provide( |
| 101 | Layer.mergeAll( | 101 | Layer.mergeAll( |
| 102 | Layer.mock(ProjectionStore.ProjectionStoreV2)({ | 102 | Layer.mock(ProjectionStore.ProjectionStoreV2)({ |
| ... | @@ -125,7 +125,7 @@ it.effect("forwards orchestrator-resolved runtime requests to the live adapter", | ... | @@ -125,7 +125,7 @@ it.effect("forwards orchestrator-resolved runtime requests to the live adapter", |
| 125 | requestId, | 125 | requestId, |
| 126 | decision: "accept", | 126 | decision: "accept", |
| 127 | }); | 127 | }); |
| 128 | }).pipe(Effect.provide(testLayer)); | 128 | }).pipe(Effect.provide(layerTest)); |
| 129 | }); | 129 | }); |
| 130 | 130 | ||
| 131 | it.effect("rejects expired runtime requests before invoking the live adapter", () => { | 131 | it.effect("rejects expired runtime requests before invoking the live adapter", () => { |
| ... | @@ -160,7 +160,7 @@ it.effect("rejects expired runtime requests before invoking the live adapter", ( | ... | @@ -160,7 +160,7 @@ it.effect("rejects expired runtime requests before invoking the live adapter", ( |
| 160 | }, | 160 | }, |
| 161 | ], | 161 | ], |
| 162 | } as unknown as OrchestrationV2ThreadProjection; | 162 | } as unknown as OrchestrationV2ThreadProjection; |
| 163 | const testLayer = RuntimeRequestService.layer.pipe( | 163 | const layerTest = RuntimeRequestService.layer.pipe( |
| 164 | Layer.provide( | 164 | Layer.provide( |
| 165 | Layer.mergeAll( | 165 | Layer.mergeAll( |
| 166 | Layer.mock(ProjectionStore.ProjectionStoreV2)({ | 166 | Layer.mock(ProjectionStore.ProjectionStoreV2)({ |
| ... | @@ -193,7 +193,7 @@ it.effect("rejects expired runtime requests before invoking the live adapter", ( | ... | @@ -193,7 +193,7 @@ it.effect("rejects expired runtime requests before invoking the live adapter", ( |
| 193 | assert.isUndefined(error.cause); | 193 | assert.isUndefined(error.cause); |
| 194 | assert.equal(getSession.mock.calls.length, 0); | 194 | assert.equal(getSession.mock.calls.length, 0); |
| 195 | assert.equal(respondToRuntimeRequest.mock.calls.length, 0); | 195 | assert.equal(respondToRuntimeRequest.mock.calls.length, 0); |
| 196 | }).pipe(Effect.provide(testLayer)); | 196 | }).pipe(Effect.provide(layerTest)); |
| 197 | }); | 197 | }); |
| 198 | 198 | ||
| 199 | it.effect("classifies runtime request response validation failures", () => { | 199 | it.effect("classifies runtime request response validation failures", () => { |
| ... | @@ -204,15 +204,15 @@ it.effect("classifies runtime request response validation failures", () => { | ... | @@ -204,15 +204,15 @@ it.effect("classifies runtime request response validation failures", () => { |
| 204 | ); | 204 | ); |
| 205 | const requestId = RuntimeRequestId.make("request-validation"); | 205 | const requestId = RuntimeRequestId.make("request-validation"); |
| 206 | const unexpectedGetSession = vi.fn(() => Effect.succeed(Option.none())); | 206 | const unexpectedGetSession = vi.fn(() => Effect.succeed(Option.none())); |
| 207 | const missingLayer = runtimeRequestTestLayer( | 207 | const layerMissing = layerRuntimeRequestTest( |
| 208 | projectionWithRuntimeRequest(), | 208 | projectionWithRuntimeRequest(), |
| 209 | unexpectedGetSession, | 209 | unexpectedGetSession, |
| 210 | ); | 210 | ); |
| 211 | const notResumableLayer = runtimeRequestTestLayer( | 211 | const layerNotResumable = layerRuntimeRequestTest( |
| 212 | projectionWithRuntimeRequest(resolvedRuntimeRequest(requestId, otherProviderSessionId)), | 212 | projectionWithRuntimeRequest(resolvedRuntimeRequest(requestId, otherProviderSessionId)), |
| 213 | unexpectedGetSession, | 213 | unexpectedGetSession, |
| 214 | ); | 214 | ); |
| 215 | const inactiveLayer = runtimeRequestTestLayer( | 215 | const layerInactive = layerRuntimeRequestTest( |
| 216 | projectionWithRuntimeRequest(resolvedRuntimeRequest(requestId, providerSessionId)), | 216 | projectionWithRuntimeRequest(resolvedRuntimeRequest(requestId, providerSessionId)), |
| 217 | () => Effect.succeed(Option.none()), | 217 | () => Effect.succeed(Option.none()), |
| 218 | ); | 218 | ); |
| ... | @@ -229,7 +229,7 @@ it.effect("classifies runtime request response validation failures", () => { | ... | @@ -229,7 +229,7 @@ it.effect("classifies runtime request response validation failures", () => { |
| 229 | }); | 229 | }); |
| 230 | 230 | ||
| 231 | return Effect.gen(function* () { | 231 | return Effect.gen(function* () { |
| 232 | const missing = yield* respond.pipe(Effect.provide(missingLayer)); | 232 | const missing = yield* respond.pipe(Effect.provide(layerMissing)); |
| 233 | assert.equal(missing.reason, "request-missing"); | 233 | assert.equal(missing.reason, "request-missing"); |
| 234 | assert.equal( | 234 | assert.equal( |
| 235 | missing.message, | 235 | missing.message, |
| ... | @@ -237,7 +237,7 @@ it.effect("classifies runtime request response validation failures", () => { | ... | @@ -237,7 +237,7 @@ it.effect("classifies runtime request response validation failures", () => { |
| 237 | ); | 237 | ); |
| 238 | assert.isUndefined(missing.cause); | 238 | assert.isUndefined(missing.cause); |
| 239 | 239 | ||
| 240 | const notResumable = yield* respond.pipe(Effect.provide(notResumableLayer)); | 240 | const notResumable = yield* respond.pipe(Effect.provide(layerNotResumable)); |
| 241 | assert.equal(notResumable.reason, "request-not-resumable"); | 241 | assert.equal(notResumable.reason, "request-not-resumable"); |
| 242 | assert.equal( | 242 | assert.equal( |
| 243 | notResumable.message, | 243 | notResumable.message, |
| ... | @@ -245,7 +245,7 @@ it.effect("classifies runtime request response validation failures", () => { | ... | @@ -245,7 +245,7 @@ it.effect("classifies runtime request response validation failures", () => { |
| 245 | ); | 245 | ); |
| 246 | assert.isUndefined(notResumable.cause); | 246 | assert.isUndefined(notResumable.cause); |
| 247 | 247 | ||
| 248 | const inactive = yield* respond.pipe(Effect.provide(inactiveLayer)); | 248 | const inactive = yield* respond.pipe(Effect.provide(layerInactive)); |
| 249 | assert.equal(inactive.reason, "provider-session-not-active"); | 249 | assert.equal(inactive.reason, "provider-session-not-active"); |
| 250 | assert.equal( | 250 | assert.equal( |
| 251 | inactive.message, | 251 | inactive.message, |
| ... | @@ -266,7 +266,7 @@ it.effect("preserves genuine provider session lookup failures as the cause", () | ... | @@ -266,7 +266,7 @@ it.effect("preserves genuine provider session lookup failures as the cause", () |
| 266 | providerSessionId, | 266 | providerSessionId, |
| 267 | cause: "lookup failed", | 267 | cause: "lookup failed", |
| 268 | }); | 268 | }); |
| 269 | const testLayer = runtimeRequestTestLayer( | 269 | const layerTest = layerRuntimeRequestTest( |
| 270 | projectionWithRuntimeRequest(resolvedRuntimeRequest(requestId, providerSessionId)), | 270 | projectionWithRuntimeRequest(resolvedRuntimeRequest(requestId, providerSessionId)), |
| 271 | () => Effect.fail(lookupFailure), | 271 | () => Effect.fail(lookupFailure), |
| 272 | ); | 272 | ); |
| ... | @@ -289,5 +289,5 @@ it.effect("preserves genuine provider session lookup failures as the cause", () | ... | @@ -289,5 +289,5 @@ it.effect("preserves genuine provider session lookup failures as the cause", () |
| 289 | `Failed to respond to runtime request ${requestId} on thread ${threadId} via provider session ${providerSessionId}.`, | 289 | `Failed to respond to runtime request ${requestId} on thread ${threadId} via provider session ${providerSessionId}.`, |
| 290 | ); | 290 | ); |
| 291 | assert.strictEqual(error.cause, lookupFailure); | 291 | assert.strictEqual(error.cause, lookupFailure); |
| 292 | }).pipe(Effect.provide(testLayer)); | 292 | }).pipe(Effect.provide(layerTest)); |
| 293 | }); | 293 | }); |
apps/server/src/orchestration-v2/SelectionRestart.integration.test.ts+11-11| ... | @@ -35,7 +35,7 @@ import { | ... | @@ -35,7 +35,7 @@ import { |
| 35 | type ProviderAdapterV2TurnInput, | 35 | type ProviderAdapterV2TurnInput, |
| 36 | } from "./ProviderAdapter.ts"; | 36 | } from "./ProviderAdapter.ts"; |
| 37 | import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts"; | 37 | import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts"; |
| 38 | import { makeOrchestratorV2ReplayLayerWithRegistry } from "./testkit/ProviderReplayHarness.ts"; | 38 | import * as ProviderReplayHarness from "./testkit/ProviderReplayHarness.ts"; |
| 39 | import { checkpointWorkspace } from "./testkit/ReplayFixtureWorkspace.ts"; | 39 | import { checkpointWorkspace } from "./testkit/ReplayFixtureWorkspace.ts"; |
| 40 | 40 | ||
| 41 | const driver = ProviderDriverKind.make("codex"); | 41 | const driver = ProviderDriverKind.make("codex"); |
| ... | @@ -403,7 +403,7 @@ it.live("restarts selection as a new attempt and retries after old-session clean | ... | @@ -403,7 +403,7 @@ it.live("restarts selection as a new attempt and retries after old-session clean |
| 403 | closedSessionCount: 0, | 403 | closedSessionCount: 0, |
| 404 | failedReplacementOpen: false, | 404 | failedReplacementOpen: false, |
| 405 | }); | 405 | }); |
| 406 | const registry = ProviderAdapterRegistry.makeSingleLayer(makeRestartAdapter(state)); | 406 | const layerRegistry = ProviderAdapterRegistry.layerSingle(makeRestartAdapter(state)); |
| 407 | 407 | ||
| 408 | const result = yield* Effect.gen(function* () { | 408 | const result = yield* Effect.gen(function* () { |
| 409 | const orchestrator = yield* Orchestrator.OrchestratorV2; | 409 | const orchestrator = yield* Orchestrator.OrchestratorV2; |
| ... | @@ -480,9 +480,9 @@ it.live("restarts selection as a new attempt and retries after old-session clean | ... | @@ -480,9 +480,9 @@ it.live("restarts selection as a new attempt and retries after old-session clean |
| 480 | return yield* Effect.die("selection restart did not complete"); | 480 | return yield* Effect.die("selection restart did not complete"); |
| 481 | }).pipe( | 481 | }).pipe( |
| 482 | Effect.provide( | 482 | Effect.provide( |
| 483 | makeOrchestratorV2ReplayLayerWithRegistry( | 483 | ProviderReplayHarness.layerWithRegistry( |
| 484 | { name: "selection-restart-lifecycle" }, | 484 | { name: "selection-restart-lifecycle" }, |
| 485 | registry, | 485 | layerRegistry, |
| 486 | ), | 486 | ), |
| 487 | ), | 487 | ), |
| 488 | ); | 488 | ); |
| ... | @@ -554,7 +554,7 @@ it.live.each(["stopped", "error"] as const)( | ... | @@ -554,7 +554,7 @@ it.live.each(["stopped", "error"] as const)( |
| 554 | // simulated replacement-open failure is skipped. | 554 | // simulated replacement-open failure is skipped. |
| 555 | failedReplacementOpen: true, | 555 | failedReplacementOpen: true, |
| 556 | }); | 556 | }); |
| 557 | const registry = ProviderAdapterRegistry.makeSingleLayer( | 557 | const layerRegistry = ProviderAdapterRegistry.layerSingle( |
| 558 | makeRestartAdapter(state, exclusiveCapabilities), | 558 | makeRestartAdapter(state, exclusiveCapabilities), |
| 559 | ); | 559 | ); |
| 560 | 560 | ||
| ... | @@ -677,7 +677,7 @@ it.live.each(["stopped", "error"] as const)( | ... | @@ -677,7 +677,7 @@ it.live.each(["stopped", "error"] as const)( |
| 677 | liveSessionId: liveSession.id, | 677 | liveSessionId: liveSession.id, |
| 678 | detachedSessionIds, | 678 | detachedSessionIds, |
| 679 | }; | 679 | }; |
| 680 | }).pipe(Effect.provide(makeOrchestratorV2ReplayLayerWithRegistry({ name }, registry))); | 680 | }).pipe(Effect.provide(ProviderReplayHarness.layerWithRegistry({ name }, layerRegistry))); |
| 681 | 681 | ||
| 682 | const { projection, captured } = result; | 682 | const { projection, captured } = result; |
| 683 | assert.lengthOf(projection.runs, 2); | 683 | assert.lengthOf(projection.runs, 2); |
| ... | @@ -719,7 +719,7 @@ it.live("detaches the old provider session after an active provider handoff", () | ... | @@ -719,7 +719,7 @@ it.live("detaches the old provider session after an active provider handoff", () |
| 719 | failedReplacementOpen: false, | 719 | failedReplacementOpen: false, |
| 720 | }); | 720 | }); |
| 721 | const targetStartCount = yield* Ref.make(0); | 721 | const targetStartCount = yield* Ref.make(0); |
| 722 | const registry = ProviderAdapterRegistry.makeLayer([ | 722 | const layerRegistry = ProviderAdapterRegistry.layerFromAdapters([ |
| 723 | makeRestartAdapter(state, exclusiveCapabilities), | 723 | makeRestartAdapter(state, exclusiveCapabilities), |
| 724 | makeCompletingHandoffAdapter(targetStartCount), | 724 | makeCompletingHandoffAdapter(targetStartCount), |
| 725 | ]); | 725 | ]); |
| ... | @@ -803,9 +803,9 @@ it.live("detaches the old provider session after an active provider handoff", () | ... | @@ -803,9 +803,9 @@ it.live("detaches the old provider session after an active provider handoff", () |
| 803 | return yield* Effect.die("active provider handoff did not complete"); | 803 | return yield* Effect.die("active provider handoff did not complete"); |
| 804 | }).pipe( | 804 | }).pipe( |
| 805 | Effect.provide( | 805 | Effect.provide( |
| 806 | makeOrchestratorV2ReplayLayerWithRegistry( | 806 | ProviderReplayHarness.layerWithRegistry( |
| 807 | { name: "selection-provider-handoff-lifecycle" }, | 807 | { name: "selection-provider-handoff-lifecycle" }, |
| 808 | registry, | 808 | layerRegistry, |
| 809 | ), | 809 | ), |
| 810 | ), | 810 | ), |
| 811 | ); | 811 | ); |
| ... | @@ -867,7 +867,7 @@ it.live.each(["active", "idle", "selection-command", "pooled", "separate-home"] | ... | @@ -867,7 +867,7 @@ it.live.each(["active", "idle", "selection-command", "pooled", "separate-home"] |
| 867 | ), | 867 | ), |
| 868 | } satisfies ProviderAdapterV2Shape; | 868 | } satisfies ProviderAdapterV2Shape; |
| 869 | }); | 869 | }); |
| 870 | const registry = Layer.succeed(ProviderAdapterRegistry.ProviderAdapterRegistryV2, { | 870 | const layerRegistry = Layer.succeed(ProviderAdapterRegistry.ProviderAdapterRegistryV2, { |
| 871 | get: (instanceId) => | 871 | get: (instanceId) => |
| 872 | Effect.succeed(adapters.find((adapter) => adapter.instanceId === instanceId)!), | 872 | Effect.succeed(adapters.find((adapter) => adapter.instanceId === instanceId)!), |
| 873 | list: () => Effect.succeed([providerInstanceId, targetId]), | 873 | list: () => Effect.succeed([providerInstanceId, targetId]), |
| ... | @@ -990,7 +990,7 @@ it.live.each(["active", "idle", "selection-command", "pooled", "separate-home"] | ... | @@ -990,7 +990,7 @@ it.live.each(["active", "idle", "selection-command", "pooled", "separate-home"] |
| 990 | assert.equal(returnedThread.providerInstanceId, providerInstanceId); | 990 | assert.equal(returnedThread.providerInstanceId, providerInstanceId); |
| 991 | assert.isEmpty(third.contextHandoffs); | 991 | assert.isEmpty(third.contextHandoffs); |
| 992 | assert.deepEqual(messages, ["first", "second", "third"]); | 992 | assert.deepEqual(messages, ["first", "second", "third"]); |
| 993 | }).pipe(Effect.provide(makeOrchestratorV2ReplayLayerWithRegistry({ name }, registry))); | 993 | }).pipe(Effect.provide(ProviderReplayHarness.layerWithRegistry({ name }, layerRegistry))); |
| 994 | }), | 994 | }), |
| 995 | ), | 995 | ), |
| 996 | ); | 996 | ); |
apps/server/src/orchestration-v2/SteeringCompletion.integration.test.ts+5-5| ... | @@ -30,7 +30,7 @@ import { | ... | @@ -30,7 +30,7 @@ import { |
| 30 | type ProviderAdapterV2TurnInput, | 30 | type ProviderAdapterV2TurnInput, |
| 31 | } from "./ProviderAdapter.ts"; | 31 | } from "./ProviderAdapter.ts"; |
| 32 | import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts"; | 32 | import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts"; |
| 33 | import { makeOrchestratorV2ReplayLayerWithRegistry } from "./testkit/ProviderReplayHarness.ts"; | 33 | import * as ProviderReplayHarness from "./testkit/ProviderReplayHarness.ts"; |
| 34 | import { checkpointWorkspace } from "./testkit/ReplayFixtureWorkspace.ts"; | 34 | import { checkpointWorkspace } from "./testkit/ReplayFixtureWorkspace.ts"; |
| 35 | 35 | ||
| 36 | const driver = ProviderDriverKind.make("codex"); | 36 | const driver = ProviderDriverKind.make("codex"); |
| ... | @@ -399,9 +399,9 @@ it.effect.each( | ... | @@ -399,9 +399,9 @@ it.effect.each( |
| 399 | assert.equal(started.length, 2); | 399 | assert.equal(started.length, 2); |
| 400 | }).pipe( | 400 | }).pipe( |
| 401 | Effect.provide( | 401 | Effect.provide( |
| 402 | makeOrchestratorV2ReplayLayerWithRegistry( | 402 | ProviderReplayHarness.layerWithRegistry( |
| 403 | { name: `steering-completion-${timing}` }, | 403 | { name: `steering-completion-${timing}` }, |
| 404 | ProviderAdapterRegistry.makeSingleLayer(adapter), | 404 | ProviderAdapterRegistry.layerSingle(adapter), |
| 405 | { runEffectWorker: false }, | 405 | { runEffectWorker: false }, |
| 406 | ), | 406 | ), |
| 407 | ), | 407 | ), |
| ... | @@ -514,9 +514,9 @@ const nextTurnSelectionHarness = Effect.fn("nextTurnSelectionHarness")(function* | ... | @@ -514,9 +514,9 @@ const nextTurnSelectionHarness = Effect.fn("nextTurnSelectionHarness")(function* |
| 514 | }; | 514 | }; |
| 515 | }), | 515 | }), |
| 516 | }; | 516 | }; |
| 517 | const layer = makeOrchestratorV2ReplayLayerWithRegistry( | 517 | const layer = ProviderReplayHarness.layerWithRegistry( |
| 518 | { name }, | 518 | { name }, |
| 519 | ProviderAdapterRegistry.makeSingleLayer(adapter), | 519 | ProviderAdapterRegistry.layerSingle(adapter), |
| 520 | { runEffectWorker: false }, | 520 | { runEffectWorker: false }, |
| 521 | ); | 521 | ); |
| 522 | // Creates the thread and starts its first turn on `runSelection`. | 522 | // Creates the thread and starts its first turn on `runSelection`. |
apps/server/src/orchestration-v2/ThreadFork.execution.test.ts+3-3| ... | @@ -23,7 +23,7 @@ import * as EventSink from "./EventSink.ts"; | ... | @@ -23,7 +23,7 @@ import * as EventSink from "./EventSink.ts"; |
| 23 | import * as Orchestrator from "./Orchestrator.ts"; | 23 | import * as Orchestrator from "./Orchestrator.ts"; |
| 24 | import type { ProviderAdapterV2Shape } from "./ProviderAdapter.ts"; | 24 | import type { ProviderAdapterV2Shape } from "./ProviderAdapter.ts"; |
| 25 | import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts"; | 25 | import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts"; |
| 26 | import { makeOrchestratorV2ReplayLayerWithRegistry } from "./testkit/ProviderReplayHarness.ts"; | 26 | import * as ProviderReplayHarness from "./testkit/ProviderReplayHarness.ts"; |
| 27 | 27 | ||
| 28 | const forkCases = (["codex", "claudeAgent"] as const).flatMap((driverName) => { | 28 | const forkCases = (["codex", "claudeAgent"] as const).flatMap((driverName) => { |
| 29 | const driver = ProviderDriverKind.make(driverName); | 29 | const driver = ProviderDriverKind.make(driverName); |
| ... | @@ -39,9 +39,9 @@ const forkCases = (["codex", "claudeAgent"] as const).flatMap((driverName) => { | ... | @@ -39,9 +39,9 @@ const forkCases = (["codex", "claudeAgent"] as const).flatMap((driverName) => { |
| 39 | planSelectionTransition: () => Effect.succeed({ type: "apply_on_next_turn" }), | 39 | planSelectionTransition: () => Effect.succeed({ type: "apply_on_next_turn" }), |
| 40 | openSession: () => Effect.die("Execution is paused after dispatch for handoff inspection"), | 40 | openSession: () => Effect.die("Execution is paused after dispatch for handoff inspection"), |
| 41 | }; | 41 | }; |
| 42 | const layer = makeOrchestratorV2ReplayLayerWithRegistry( | 42 | const layer = ProviderReplayHarness.layerWithRegistry( |
| 43 | { name: `fork-boundary-${driver}` }, | 43 | { name: `fork-boundary-${driver}` }, |
| 44 | ProviderAdapterRegistry.makeLayer([adapter]), | 44 | ProviderAdapterRegistry.layerFromAdapters([adapter]), |
| 45 | { runEffectWorker: false }, | 45 | { runEffectWorker: false }, |
| 46 | ); | 46 | ); |
| 47 | 47 |
apps/server/src/orchestration-v2/ThreadLaunchService.test.ts+37-28| ... | @@ -40,12 +40,12 @@ import * as Schema from "effect/Schema"; | ... | @@ -40,12 +40,12 @@ import * as Schema from "effect/Schema"; |
| 40 | import * as TestClock from "effect/testing/TestClock"; | 40 | import * as TestClock from "effect/testing/TestClock"; |
| 41 | 41 | ||
| 42 | import * as GitWorkflow from "../git/GitWorkflowService.ts"; | 42 | import * as GitWorkflow from "../git/GitWorkflowService.ts"; |
| 43 | import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; | 43 | import * as SqlitePersistence from "../persistence/Layers/Sqlite.ts"; |
| 44 | import * as ProjectStore from "./ProjectStore.ts"; | 44 | import * as ProjectStore from "./ProjectStore.ts"; |
| 45 | import * as ProjectService from "../project/ProjectService.ts"; | 45 | import * as ProjectService from "../project/ProjectService.ts"; |
| 46 | import * as ProjectSetupScriptRunner from "../project/ProjectSetupScriptRunner.ts"; | 46 | import * as ProjectSetupScriptRunner from "../project/ProjectSetupScriptRunner.ts"; |
| 47 | import * as ManagedProjectFolders from "../project/ManagedProjectFolders.ts"; | 47 | import * as ManagedProjectFolders from "../project/ManagedProjectFolders.ts"; |
| 48 | import { makeProviderRegistryLayer } from "../provider/testUtils/providerRegistryMock.ts"; | 48 | import * as ProviderRegistryMock from "../provider/testUtils/providerRegistryMock.ts"; |
| 49 | import * as ServerSettings from "../serverSettings.ts"; | 49 | import * as ServerSettings from "../serverSettings.ts"; |
| 50 | import * as ScheduledTasks from "../scheduledTasks/ScheduledTaskService.ts"; | 50 | import * as ScheduledTasks from "../scheduledTasks/ScheduledTaskService.ts"; |
| 51 | import * as SecretRequests from "../secrets/SecretRequests.ts"; | 51 | import * as SecretRequests from "../secrets/SecretRequests.ts"; |
| ... | @@ -59,7 +59,7 @@ import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts"; | ... | @@ -59,7 +59,7 @@ import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts"; |
| 59 | import * as ThreadLaunch from "./ThreadLaunchService.ts"; | 59 | import * as ThreadLaunch from "./ThreadLaunchService.ts"; |
| 60 | import * as ThreadManagement from "./ThreadManagementService.ts"; | 60 | import * as ThreadManagement from "./ThreadManagementService.ts"; |
| 61 | import * as ThreadTitleRegeneration from "./ThreadTitleRegenerationService.ts"; | 61 | import * as ThreadTitleRegeneration from "./ThreadTitleRegenerationService.ts"; |
| 62 | import { makeOrchestratorV2ReplayLayerWithRegistry } from "./testkit/ProviderReplayHarness.ts"; | 62 | import * as ProviderReplayHarness from "./testkit/ProviderReplayHarness.ts"; |
| 63 | 63 | ||
| 64 | const projectId = ProjectId.make("project:launch-test"); | 64 | const projectId = ProjectId.make("project:launch-test"); |
| 65 | const otherProjectId = ProjectId.make("project:launch-other"); | 65 | const otherProjectId = ProjectId.make("project:launch-other"); |
| ... | @@ -110,16 +110,16 @@ interface HarnessOptions { | ... | @@ -110,16 +110,16 @@ interface HarnessOptions { |
| 110 | } | 110 | } |
| 111 | 111 | ||
| 112 | function makeHarness(options: HarnessOptions = {}) { | 112 | function makeHarness(options: HarnessOptions = {}) { |
| 113 | const database = SqlitePersistenceMemory; | 113 | const layerDatabase = SqlitePersistence.layerMemory; |
| 114 | const registry = ProviderAdapterRegistry.makeLayer([adapter]); | 114 | const layerRegistry = ProviderAdapterRegistry.layerFromAdapters([adapter]); |
| 115 | const orchestrator = makeOrchestratorV2ReplayLayerWithRegistry( | 115 | const layerOrchestrator = ProviderReplayHarness.layerWithRegistry( |
| 116 | { name: "thread-launch" }, | 116 | { name: "thread-launch" }, |
| 117 | registry, | 117 | layerRegistry, |
| 118 | { databaseLayer: database, runEffectWorker: false }, | 118 | { databaseLayer: layerDatabase, runEffectWorker: false }, |
| 119 | ); | 119 | ); |
| 120 | const threadManagement = ThreadManagement.layer.pipe(Layer.provide(orchestrator)); | 120 | const layerThreadManagement = ThreadManagement.layer.pipe(Layer.provide(layerOrchestrator)); |
| 121 | const receipts = CommandReceiptStore.layer.pipe(Layer.provide(database)); | 121 | const layerReceipts = CommandReceiptStore.layer.pipe(Layer.provide(layerDatabase)); |
| 122 | const outbox = EffectOutbox.layer.pipe(Layer.provide(database)); | 122 | const layerOutbox = EffectOutbox.layer.pipe(Layer.provide(layerDatabase)); |
| 123 | const createWorktree = vi.fn( | 123 | const createWorktree = vi.fn( |
| 124 | options.createWorktree ?? | 124 | options.createWorktree ?? |
| 125 | ((input) => | 125 | ((input) => |
| ... | @@ -143,7 +143,7 @@ function makeHarness(options: HarnessOptions = {}) { | ... | @@ -143,7 +143,7 @@ function makeHarness(options: HarnessOptions = {}) { |
| 143 | const generateThreadTitle = vi.fn( | 143 | const generateThreadTitle = vi.fn( |
| 144 | options.generateTitle ?? (() => Effect.succeed({ title: "Generated title" })), | 144 | options.generateTitle ?? (() => Effect.succeed({ title: "Generated title" })), |
| 145 | ); | 145 | ); |
| 146 | const externalServices = Layer.mergeAll( | 146 | const layerExternalServices = Layer.mergeAll( |
| 147 | WorktreeSetupTracker.layer, | 147 | WorktreeSetupTracker.layer, |
| 148 | Layer.mock(ProjectCloneTracker.ProjectCloneTracker)({ get: () => Effect.succeed(null) }), | 148 | Layer.mock(ProjectCloneTracker.ProjectCloneTracker)({ get: () => Effect.succeed(null) }), |
| 149 | Layer.mock(TerminalManager.TerminalManager)({ close: () => Effect.void }), | 149 | Layer.mock(TerminalManager.TerminalManager)({ close: () => Effect.void }), |
| ... | @@ -184,17 +184,24 @@ function makeHarness(options: HarnessOptions = {}) { | ... | @@ -184,17 +184,24 @@ function makeHarness(options: HarnessOptions = {}) { |
| 184 | generateBranchName, | 184 | generateBranchName, |
| 185 | }), | 185 | }), |
| 186 | ServerSettings.layerTest(options.serverSettings), | 186 | ServerSettings.layerTest(options.serverSettings), |
| 187 | makeProviderRegistryLayer(options.providers), | 187 | ProviderRegistryMock.layer(options.providers), |
| 188 | options.managedFolders ?? | 188 | options.managedFolders ?? |
| 189 | Layer.mock(ManagedProjectFolders.ManagedProjectFolders)({ | 189 | Layer.mock(ManagedProjectFolders.ManagedProjectFolders)({ |
| 190 | namedProjectsRoot: "/projects", | 190 | namedProjectsRoot: "/projects", |
| 191 | folderForThread: () => Effect.succeed(Option.none()), | 191 | folderForThread: () => Effect.succeed(Option.none()), |
| 192 | }), | 192 | }), |
| 193 | ); | 193 | ); |
| 194 | const launch = ThreadLaunch.layer.pipe( | 194 | const layerLaunch = ThreadLaunch.layer.pipe( |
| 195 | Layer.provide(Layer.mergeAll(externalServices, threadManagement, receipts, IdAllocator.layer)), | 195 | Layer.provide( |
| 196 | Layer.mergeAll( | ||
| 197 | layerExternalServices, | ||
| 198 | layerThreadManagement, | ||
| 199 | layerReceipts, | ||
| 200 | IdAllocator.layer, | ||
| 201 | ), | ||
| 202 | ), | ||
| 196 | ); | 203 | ); |
| 197 | const projectedProjects = Layer.mock(ProjectStore.ProjectStoreV2)({ | 204 | const layerProjectedProjects = Layer.mock(ProjectStore.ProjectStoreV2)({ |
| 198 | get: (requestedProjectId) => | 205 | get: (requestedProjectId) => |
| 199 | Effect.succeed( | 206 | Effect.succeed( |
| 200 | requestedProjectId === projectId | 207 | requestedProjectId === projectId |
| ... | @@ -215,17 +222,19 @@ function makeHarness(options: HarnessOptions = {}) { | ... | @@ -215,17 +222,19 @@ function makeHarness(options: HarnessOptions = {}) { |
| 215 | : Option.none(), | 222 | : Option.none(), |
| 216 | ), | 223 | ), |
| 217 | }); | 224 | }); |
| 218 | const titleRegeneration = ThreadTitleRegeneration.layer.pipe( | 225 | const layerTitleRegeneration = ThreadTitleRegeneration.layer.pipe( |
| 219 | Layer.provide(Layer.mergeAll(threadManagement, projectedProjects, externalServices)), | 226 | Layer.provide( |
| 227 | Layer.mergeAll(layerThreadManagement, layerProjectedProjects, layerExternalServices), | ||
| 228 | ), | ||
| 220 | ); | 229 | ); |
| 221 | return { | 230 | return { |
| 222 | layer: Layer.mergeAll( | 231 | layer: Layer.mergeAll( |
| 223 | launch, | 232 | layerLaunch, |
| 224 | threadManagement, | 233 | layerThreadManagement, |
| 225 | titleRegeneration, | 234 | layerTitleRegeneration, |
| 226 | outbox, | 235 | layerOutbox, |
| 227 | database, | 236 | layerDatabase, |
| 228 | externalServices, | 237 | layerExternalServices, |
| 229 | ), | 238 | ), |
| 230 | createWorktree, | 239 | createWorktree, |
| 231 | removeWorktree, | 240 | removeWorktree, |
| ... | @@ -288,7 +297,7 @@ it.effect.each( | ... | @@ -288,7 +297,7 @@ it.effect.each( |
| 288 | "attributes $createdBy-configured automations in $target threads without changing their prompt", | 297 | "attributes $createdBy-configured automations in $target threads without changing their prompt", |
| 289 | ({ target, createdBy }) => { | 298 | ({ target, createdBy }) => { |
| 290 | const harness = makeHarness(); | 299 | const harness = makeHarness(); |
| 291 | const scheduledTasks = ScheduledTasks.layer.pipe( | 300 | const layerScheduledTasks = ScheduledTasks.layer.pipe( |
| 292 | Layer.provide( | 301 | Layer.provide( |
| 293 | Layer.mergeAll( | 302 | Layer.mergeAll( |
| 294 | harness.layer, | 303 | harness.layer, |
| ... | @@ -345,7 +354,7 @@ it.effect.each( | ... | @@ -345,7 +354,7 @@ it.effect.each( |
| 345 | ); | 354 | ); |
| 346 | assert.equal(turnItem?.text, task.prompt); | 355 | assert.equal(turnItem?.text, task.prompt); |
| 347 | assert.equal(turnItem?.scheduledTaskId, task.id); | 356 | assert.equal(turnItem?.scheduledTaskId, task.id); |
| 348 | }).pipe(Effect.provide(Layer.mergeAll(harness.layer, scheduledTasks))); | 357 | }).pipe(Effect.provide(Layer.mergeAll(harness.layer, layerScheduledTasks))); |
| 349 | }, | 358 | }, |
| 350 | ); | 359 | ); |
| 351 | 360 | ||
| ... | @@ -1955,7 +1964,7 @@ it.effect("creates a strong provider-thread mapping for an imported native sessi | ... | @@ -1955,7 +1964,7 @@ it.effect("creates a strong provider-thread mapping for an imported native sessi |
| 1955 | 1964 | ||
| 1956 | it.effect("shared intake preserves durable attachment bytes after a lost launch result", () => { | 1965 | it.effect("shared intake preserves durable attachment bytes after a lost launch result", () => { |
| 1957 | const harness = makeHarness(); | 1966 | const harness = makeHarness(); |
| 1958 | const files = ServerConfig.layerTest(process.cwd(), { prefix: "t3-message-intake-" }).pipe( | 1967 | const layerFiles = ServerConfig.layerTest(process.cwd(), { prefix: "t3-message-intake-" }).pipe( |
| 1959 | Layer.provideMerge(NodeServices.layer), | 1968 | Layer.provideMerge(NodeServices.layer), |
| 1960 | ); | 1969 | ); |
| 1961 | return Effect.gen(function* () { | 1970 | return Effect.gen(function* () { |
| ... | @@ -2171,7 +2180,7 @@ it.effect("shared intake preserves durable attachment bytes after a lost launch | ... | @@ -2171,7 +2180,7 @@ it.effect("shared intake preserves durable attachment bytes after a lost launch |
| 2171 | assert.isNotNull(path); | 2180 | assert.isNotNull(path); |
| 2172 | assert.deepEqual(yield* fs.readFile(path), new Uint8Array([1, 2, 3, 4])); | 2181 | assert.deepEqual(yield* fs.readFile(path), new Uint8Array([1, 2, 3, 4])); |
| 2173 | } | 2182 | } |
| 2174 | }).pipe(Effect.provide(Layer.mergeAll(harness.layer, files))); | 2183 | }).pipe(Effect.provide(Layer.mergeAll(harness.layer, layerFiles))); |
| 2175 | }); | 2184 | }); |
| 2176 | 2185 | ||
| 2177 | it.effect("cancels tracked setup before provider work is released", () => | 2186 | it.effect("cancels tracked setup before provider work is released", () => |
apps/server/src/orchestration-v2/ThreadManagementService.test.ts+10-10| ... | @@ -268,7 +268,7 @@ it.effect("classifies projection infrastructure failures separately from a missi | ... | @@ -268,7 +268,7 @@ it.effect("classifies projection infrastructure failures separately from a missi |
| 268 | threadId, | 268 | threadId, |
| 269 | cause: infrastructureCause, | 269 | cause: infrastructureCause, |
| 270 | }); | 270 | }); |
| 271 | const testLayer = ThreadManagementService.layer.pipe( | 271 | const layerTest = ThreadManagementService.layer.pipe( |
| 272 | Layer.provide( | 272 | Layer.provide( |
| 273 | Layer.mock(Orchestrator.OrchestratorV2)({ | 273 | Layer.mock(Orchestrator.OrchestratorV2)({ |
| 274 | getThreadProjection: () => Effect.fail(projectionError), | 274 | getThreadProjection: () => Effect.fail(projectionError), |
| ... | @@ -287,7 +287,7 @@ it.effect("classifies projection infrastructure failures separately from a missi | ... | @@ -287,7 +287,7 @@ it.effect("classifies projection infrastructure failures separately from a missi |
| 287 | cause: projectionError, | 287 | cause: projectionError, |
| 288 | }); | 288 | }); |
| 289 | expect(error.message).toBe(`Unable to load thread ${threadId} in project ${projectId}.`); | 289 | expect(error.message).toBe(`Unable to load thread ${threadId} in project ${projectId}.`); |
| 290 | }).pipe(Effect.provide(testLayer)); | 290 | }).pipe(Effect.provide(layerTest)); |
| 291 | }); | 291 | }); |
| 292 | 292 | ||
| 293 | it.effect("uses thread-not-found only after a projection loads outside the project", () => { | 293 | it.effect("uses thread-not-found only after a projection loads outside the project", () => { |
| ... | @@ -301,7 +301,7 @@ it.effect("uses thread-not-found only after a projection loads outside the proje | ... | @@ -301,7 +301,7 @@ it.effect("uses thread-not-found only after a projection loads outside the proje |
| 301 | deletedAt: null, | 301 | deletedAt: null, |
| 302 | }, | 302 | }, |
| 303 | } as OrchestrationV2ThreadProjection; | 303 | } as OrchestrationV2ThreadProjection; |
| 304 | const testLayer = ThreadManagementService.layer.pipe( | 304 | const layerTest = ThreadManagementService.layer.pipe( |
| 305 | Layer.provide( | 305 | Layer.provide( |
| 306 | Layer.mock(Orchestrator.OrchestratorV2)({ | 306 | Layer.mock(Orchestrator.OrchestratorV2)({ |
| 307 | getThreadProjection: () => Effect.succeed(projection), | 307 | getThreadProjection: () => Effect.succeed(projection), |
| ... | @@ -316,7 +316,7 @@ it.effect("uses thread-not-found only after a projection loads outside the proje | ... | @@ -316,7 +316,7 @@ it.effect("uses thread-not-found only after a projection loads outside the proje |
| 316 | expect(error).toBeInstanceOf(ThreadManagementService.ThreadManagementThreadNotFoundError); | 316 | expect(error).toBeInstanceOf(ThreadManagementService.ThreadManagementThreadNotFoundError); |
| 317 | expect(error).toMatchObject({ projectId, threadId }); | 317 | expect(error).toMatchObject({ projectId, threadId }); |
| 318 | expect("cause" in error).toBe(false); | 318 | expect("cause" in error).toBe(false); |
| 319 | }).pipe(Effect.provide(testLayer)); | 319 | }).pipe(Effect.provide(layerTest)); |
| 320 | }); | 320 | }); |
| 321 | 321 | ||
| 322 | it.effect("preserves failed legacy materialization when reading checkpoint context", () => { | 322 | it.effect("preserves failed legacy materialization when reading checkpoint context", () => { |
| ... | @@ -326,7 +326,7 @@ it.effect("preserves failed legacy materialization when reading checkpoint conte | ... | @@ -326,7 +326,7 @@ it.effect("preserves failed legacy materialization when reading checkpoint conte |
| 326 | operation: "hydrate transcript for", | 326 | operation: "hydrate transcript for", |
| 327 | cause: new Error("checkpoint import failed"), | 327 | cause: new Error("checkpoint import failed"), |
| 328 | }); | 328 | }); |
| 329 | const testLayer = ThreadManagementService.layerWithLegacyImporter.pipe( | 329 | const layerTest = ThreadManagementService.layerWithLegacyImporter.pipe( |
| 330 | Layer.provide( | 330 | Layer.provide( |
| 331 | Layer.mergeAll( | 331 | Layer.mergeAll( |
| 332 | Layer.mock(Orchestrator.OrchestratorV2)({ | 332 | Layer.mock(Orchestrator.OrchestratorV2)({ |
| ... | @@ -344,7 +344,7 @@ it.effect("preserves failed legacy materialization when reading checkpoint conte | ... | @@ -344,7 +344,7 @@ it.effect("preserves failed legacy materialization when reading checkpoint conte |
| 344 | const error = yield* service.getCheckpointContext(threadId).pipe(Effect.flip); | 344 | const error = yield* service.getCheckpointContext(threadId).pipe(Effect.flip); |
| 345 | expect(error).toBeInstanceOf(Orchestrator.OrchestratorProjectionError); | 345 | expect(error).toBeInstanceOf(Orchestrator.OrchestratorProjectionError); |
| 346 | expect(error).toMatchObject({ threadId, cause: importError }); | 346 | expect(error).toMatchObject({ threadId, cause: importError }); |
| 347 | }).pipe(Effect.provide(testLayer)); | 347 | }).pipe(Effect.provide(layerTest)); |
| 348 | }); | 348 | }); |
| 349 | 349 | ||
| 350 | it.effect.each([ | 350 | it.effect.each([ |
| ... | @@ -367,7 +367,7 @@ it.effect.each([ | ... | @@ -367,7 +367,7 @@ it.effect.each([ |
| 367 | thread: { id: threadId, projectId, deletedAt: null }, | 367 | thread: { id: threadId, projectId, deletedAt: null }, |
| 368 | runs: status === "missing" ? [] : [{ id: runId, status }], | 368 | runs: status === "missing" ? [] : [{ id: runId, status }], |
| 369 | }) as unknown as OrchestrationV2ThreadProjection; | 369 | }) as unknown as OrchestrationV2ThreadProjection; |
| 370 | const testLayer = ThreadManagementService.layer.pipe( | 370 | const layerTest = ThreadManagementService.layer.pipe( |
| 371 | Layer.provide( | 371 | Layer.provide( |
| 372 | Layer.mock(Orchestrator.OrchestratorV2)({ | 372 | Layer.mock(Orchestrator.OrchestratorV2)({ |
| 373 | getThreadEventSequence: () => Effect.succeed(0), | 373 | getThreadEventSequence: () => Effect.succeed(0), |
| ... | @@ -387,7 +387,7 @@ it.effect.each([ | ... | @@ -387,7 +387,7 @@ it.effect.each([ |
| 387 | ), | 387 | ), |
| 388 | ); | 388 | ); |
| 389 | const service = yield* ThreadManagementService.ThreadManagementService.pipe( | 389 | const service = yield* ThreadManagementService.ThreadManagementService.pipe( |
| 390 | Effect.provide(testLayer), | 390 | Effect.provide(layerTest), |
| 391 | ); | 391 | ); |
| 392 | const fiber = yield* service | 392 | const fiber = yield* service |
| 393 | .waitForThread({ | 393 | .waitForThread({ |
| ... | @@ -433,7 +433,7 @@ it.effect("waitForThread reads the run again only when the run updates", () => | ... | @@ -433,7 +433,7 @@ it.effect("waitForThread reads the run again only when the run updates", () => |
| 433 | let reads = 0; | 433 | let reads = 0; |
| 434 | const stored = (sequence: number, event: object) => | 434 | const stored = (sequence: number, event: object) => |
| 435 | ({ sequence, event: { threadId, ...event } }) as unknown as OrchestrationV2StoredEvent; | 435 | ({ sequence, event: { threadId, ...event } }) as unknown as OrchestrationV2StoredEvent; |
| 436 | const testLayer = ThreadManagementService.layer.pipe( | 436 | const layerTest = ThreadManagementService.layer.pipe( |
| 437 | Layer.provide( | 437 | Layer.provide( |
| 438 | Layer.mock(Orchestrator.OrchestratorV2)({ | 438 | Layer.mock(Orchestrator.OrchestratorV2)({ |
| 439 | getThreadEventSequence: () => Effect.succeed(0), | 439 | getThreadEventSequence: () => Effect.succeed(0), |
| ... | @@ -457,7 +457,7 @@ it.effect("waitForThread reads the run again only when the run updates", () => | ... | @@ -457,7 +457,7 @@ it.effect("waitForThread reads the run again only when the run updates", () => |
| 457 | ), | 457 | ), |
| 458 | ); | 458 | ); |
| 459 | const service = yield* ThreadManagementService.ThreadManagementService.pipe( | 459 | const service = yield* ThreadManagementService.ThreadManagementService.pipe( |
| 460 | Effect.provide(testLayer), | 460 | Effect.provide(layerTest), |
| 461 | ); | 461 | ); |
| 462 | const fiber = yield* service | 462 | const fiber = yield* service |
| 463 | .waitForThread({ projectId, threadId, runId, timeoutMs: 60 * 60 * 1_000 }) | 463 | .waitForThread({ projectId, threadId, runId, timeoutMs: 60 * 60 * 1_000 }) |
apps/server/src/orchestration-v2/ThreadManagementService.ts+2-2| ... | @@ -833,7 +833,7 @@ const make = Effect.gen(function* () { | ... | @@ -833,7 +833,7 @@ const make = Effect.gen(function* () { |
| 833 | }); | 833 | }); |
| 834 | }); | 834 | }); |
| 835 | 835 | ||
| 836 | const legacyV1ThreadImporterNoopLayer = Layer.succeed( | 836 | const layerLegacyV1ThreadImporterNoop = Layer.succeed( |
| 837 | LegacyV1ThreadImporter.LegacyV1ThreadImporter, | 837 | LegacyV1ThreadImporter.LegacyV1ThreadImporter, |
| 838 | LegacyV1ThreadImporter.LegacyV1ThreadImporter.of({ | 838 | LegacyV1ThreadImporter.LegacyV1ThreadImporter.of({ |
| 839 | pendingThreadCount: Effect.succeed(0), | 839 | pendingThreadCount: Effect.succeed(0), |
| ... | @@ -844,7 +844,7 @@ const legacyV1ThreadImporterNoopLayer = Layer.succeed( | ... | @@ -844,7 +844,7 @@ const legacyV1ThreadImporterNoopLayer = Layer.succeed( |
| 844 | ); | 844 | ); |
| 845 | 845 | ||
| 846 | export const layer: Layer.Layer<ThreadManagementService, never, Orchestrator.OrchestratorV2> = | 846 | export const layer: Layer.Layer<ThreadManagementService, never, Orchestrator.OrchestratorV2> = |
| 847 | Layer.effect(ThreadManagementService, make).pipe(Layer.provide(legacyV1ThreadImporterNoopLayer)); | 847 | Layer.effect(ThreadManagementService, make).pipe(Layer.provide(layerLegacyV1ThreadImporterNoop)); |
| 848 | 848 | ||
| 849 | export const layerWithLegacyImporter: Layer.Layer< | 849 | export const layerWithLegacyImporter: Layer.Layer< |
| 850 | ThreadManagementService, | 850 | ThreadManagementService, |
apps/server/src/orchestration-v2/ThreadMessageIntake.test.ts+21-21| ... | @@ -27,11 +27,11 @@ import { | ... | @@ -27,11 +27,11 @@ import { |
| 27 | import * as ThreadManagementService from "./ThreadManagementService.ts"; | 27 | import * as ThreadManagementService from "./ThreadManagementService.ts"; |
| 28 | import { dispatchCommand } from "./ThreadMessageIntake.ts"; | 28 | import { dispatchCommand } from "./ThreadMessageIntake.ts"; |
| 29 | 29 | ||
| 30 | const intakeTestLayer = ServerConfig.layerTest(process.cwd(), { | 30 | const layerIntakeTest = ServerConfig.layerTest(process.cwd(), { |
| 31 | prefix: "t3-question-intake-", | 31 | prefix: "t3-question-intake-", |
| 32 | }).pipe(Layer.provideMerge(NodeServices.layer)); | 32 | }).pipe(Layer.provideMerge(NodeServices.layer)); |
| 33 | 33 | ||
| 34 | const failingDispatch = (captured: OrchestrationV2ServerCommand[]) => | 34 | const layerFailingDispatch = (captured: OrchestrationV2ServerCommand[]) => |
| 35 | Layer.mock(ThreadManagementService.ThreadManagementService)({ | 35 | Layer.mock(ThreadManagementService.ThreadManagementService)({ |
| 36 | dispatch: (command) => { | 36 | dispatch: (command) => { |
| 37 | captured.push(command); | 37 | captured.push(command); |
| ... | @@ -143,7 +143,7 @@ it.effect("claims question uploads and passes readable paths through the V2 requ | ... | @@ -143,7 +143,7 @@ it.effect("claims question uploads and passes readable paths through the V2 requ |
| 143 | attachmentsByQuestionId: { | 143 | attachmentsByQuestionId: { |
| 144 | q: [{ type: "image", id, name: "screen.png", mimeType: "image/png", sizeBytes: 3 }], | 144 | q: [{ type: "image", id, name: "screen.png", mimeType: "image/png", sizeBytes: 3 }], |
| 145 | }, | 145 | }, |
| 146 | }).pipe(Effect.provide(failingDispatch(captured)), Effect.result); | 146 | }).pipe(Effect.provide(layerFailingDispatch(captured)), Effect.result); |
| 147 | expect(captured).toHaveLength(1); | 147 | expect(captured).toHaveLength(1); |
| 148 | const command = captured[0]!; | 148 | const command = captured[0]!; |
| 149 | expect(command.type).toBe("runtime-request.respond"); | 149 | expect(command.type).toBe("runtime-request.respond"); |
| ... | @@ -160,7 +160,7 @@ it.effect("claims question uploads and passes readable paths through the V2 requ | ... | @@ -160,7 +160,7 @@ it.effect("claims question uploads and passes readable paths through the V2 requ |
| 160 | "Selected option", | 160 | "Selected option", |
| 161 | `Attached image "screen.png": "${path}"`, | 161 | `Attached image "screen.png": "${path}"`, |
| 162 | ]); | 162 | ]); |
| 163 | }).pipe(Effect.provide(intakeTestLayer)), | 163 | }).pipe(Effect.provide(layerIntakeTest)), |
| 164 | ); | 164 | ); |
| 165 | 165 | ||
| 166 | it.effect("rolls back earlier question claims when a later pending upload is missing", () => | 166 | it.effect("rolls back earlier question claims when a later pending upload is missing", () => |
| ... | @@ -211,7 +211,7 @@ it.effect("rolls back earlier question claims when a later pending upload is mis | ... | @@ -211,7 +211,7 @@ it.effect("rolls back earlier question claims when a later pending upload is mis |
| 211 | }, | 211 | }, |
| 212 | ], | 212 | ], |
| 213 | }, | 213 | }, |
| 214 | }).pipe(Effect.provide(failingDispatch(captured)), Effect.result); | 214 | }).pipe(Effect.provide(layerFailingDispatch(captured)), Effect.result); |
| 215 | expect(result._tag).toBe("Failure"); | 215 | expect(result._tag).toBe("Failure"); |
| 216 | if (result._tag === "Failure") { | 216 | if (result._tag === "Failure") { |
| 217 | expect(result.failure._tag).toBe("AttachmentClaimError"); | 217 | expect(result.failure._tag).toBe("AttachmentClaimError"); |
| ... | @@ -227,7 +227,7 @@ it.effect("rolls back earlier question claims when a later pending upload is mis | ... | @@ -227,7 +227,7 @@ it.effect("rolls back earlier question claims when a later pending upload is mis |
| 227 | entry.startsWith("thread-q-fail-"), | 227 | entry.startsWith("thread-q-fail-"), |
| 228 | ), | 228 | ), |
| 229 | ).toEqual([`${existingId}.png`]); | 229 | ).toEqual([`${existingId}.png`]); |
| 230 | }).pipe(Effect.provide(intakeTestLayer)), | 230 | }).pipe(Effect.provide(layerIntakeTest)), |
| 231 | ); | 231 | ); |
| 232 | 232 | ||
| 233 | it.effect("rolls back earlier question claims when attachment path preparation fails", () => | 233 | it.effect("rolls back earlier question claims when attachment path preparation fails", () => |
| ... | @@ -265,7 +265,7 @@ it.effect("rolls back earlier question claims when attachment path preparation f | ... | @@ -265,7 +265,7 @@ it.effect("rolls back earlier question claims when attachment path preparation f |
| 265 | }, | 265 | }, |
| 266 | ], | 266 | ], |
| 267 | }, | 267 | }, |
| 268 | }).pipe(Effect.provide(failingDispatch(captured)), Effect.result); | 268 | }).pipe(Effect.provide(layerFailingDispatch(captured)), Effect.result); |
| 269 | expect(result._tag).toBe("Failure"); | 269 | expect(result._tag).toBe("Failure"); |
| 270 | if (result._tag === "Failure") { | 270 | if (result._tag === "Failure") { |
| 271 | expect(result.failure._tag).toBe("AttachmentClaimError"); | 271 | expect(result.failure._tag).toBe("AttachmentClaimError"); |
| ... | @@ -279,7 +279,7 @@ it.effect("rolls back earlier question claims when attachment path preparation f | ... | @@ -279,7 +279,7 @@ it.effect("rolls back earlier question claims when attachment path preparation f |
| 279 | entry.startsWith("thread-path-fail-"), | 279 | entry.startsWith("thread-path-fail-"), |
| 280 | ), | 280 | ), |
| 281 | ).toEqual([]); | 281 | ).toEqual([]); |
| 282 | }).pipe(Effect.provide(intakeTestLayer)), | 282 | }).pipe(Effect.provide(layerIntakeTest)), |
| 283 | ); | 283 | ); |
| 284 | 284 | ||
| 285 | it.effect("retains claimed copies when dispatch failure may have been accepted", () => | 285 | it.effect("retains claimed copies when dispatch failure may have been accepted", () => |
| ... | @@ -308,7 +308,7 @@ it.effect("retains claimed copies when dispatch failure may have been accepted", | ... | @@ -308,7 +308,7 @@ it.effect("retains claimed copies when dispatch failure may have been accepted", |
| 308 | }, | 308 | }, |
| 309 | ], | 309 | ], |
| 310 | }, | 310 | }, |
| 311 | }).pipe(Effect.provide(failingDispatch(captured)), Effect.result); | 311 | }).pipe(Effect.provide(layerFailingDispatch(captured)), Effect.result); |
| 312 | expect(result._tag).toBe("Failure"); | 312 | expect(result._tag).toBe("Failure"); |
| 313 | expect(captured).toHaveLength(1); | 313 | expect(captured).toHaveLength(1); |
| 314 | // The response may have been accepted before the error, so the copy that | 314 | // The response may have been accepted before the error, so the copy that |
| ... | @@ -318,7 +318,7 @@ it.effect("retains claimed copies when dispatch failure may have been accepted", | ... | @@ -318,7 +318,7 @@ it.effect("retains claimed copies when dispatch failure may have been accepted", |
| 318 | ); | 318 | ); |
| 319 | expect(threadFiles).toHaveLength(1); | 319 | expect(threadFiles).toHaveLength(1); |
| 320 | expect(NodeFS.existsSync(NodePath.join(config.attachmentsDir, `${pendingId}.png`))).toBe(true); | 320 | expect(NodeFS.existsSync(NodePath.join(config.attachmentsDir, `${pendingId}.png`))).toBe(true); |
| 321 | }).pipe(Effect.provide(intakeTestLayer)), | 321 | }).pipe(Effect.provide(layerIntakeTest)), |
| 322 | ); | 322 | ); |
| 323 | 323 | ||
| 324 | it.effect("releases claimed copies when the command was already rejected", () => | 324 | it.effect("releases claimed copies when the command was already rejected", () => |
| ... | @@ -370,7 +370,7 @@ it.effect("releases claimed copies when the command was already rejected", () => | ... | @@ -370,7 +370,7 @@ it.effect("releases claimed copies when the command was already rejected", () => |
| 370 | ), | 370 | ), |
| 371 | ).toEqual([]); | 371 | ).toEqual([]); |
| 372 | expect(NodeFS.existsSync(NodePath.join(config.attachmentsDir, `${pendingId}.png`))).toBe(true); | 372 | expect(NodeFS.existsSync(NodePath.join(config.attachmentsDir, `${pendingId}.png`))).toBe(true); |
| 373 | }).pipe(Effect.provide(intakeTestLayer)), | 373 | }).pipe(Effect.provide(layerIntakeTest)), |
| 374 | ); | 374 | ); |
| 375 | 375 | ||
| 376 | it.effect("releases claimed copies when dispatch replays an earlier accepted response", () => | 376 | it.effect("releases claimed copies when dispatch replays an earlier accepted response", () => |
| ... | @@ -438,7 +438,7 @@ it.effect("releases claimed copies when dispatch replays an earlier accepted res | ... | @@ -438,7 +438,7 @@ it.effect("releases claimed copies when dispatch replays an earlier accepted res |
| 438 | NodeFS.readFileSync(NodePath.join(config.attachmentsDir, `${earlierAttachment.id}.png`)), | 438 | NodeFS.readFileSync(NodePath.join(config.attachmentsDir, `${earlierAttachment.id}.png`)), |
| 439 | ).toEqual(Buffer.from([4, 4, 4])); | 439 | ).toEqual(Buffer.from([4, 4, 4])); |
| 440 | expect(NodeFS.existsSync(NodePath.join(config.attachmentsDir, `${pendingId}.png`))).toBe(true); | 440 | expect(NodeFS.existsSync(NodePath.join(config.attachmentsDir, `${pendingId}.png`))).toBe(true); |
| 441 | }).pipe(Effect.provide(intakeTestLayer)), | 441 | }).pipe(Effect.provide(layerIntakeTest)), |
| 442 | ); | 442 | ); |
| 443 | 443 | ||
| 444 | it.effect("releases claimed copies when the recorded answer has no attachments", () => | 444 | it.effect("releases claimed copies when the recorded answer has no attachments", () => |
| ... | @@ -489,7 +489,7 @@ it.effect("releases claimed copies when the recorded answer has no attachments", | ... | @@ -489,7 +489,7 @@ it.effect("releases claimed copies when the recorded answer has no attachments", |
| 489 | ), | 489 | ), |
| 490 | ).toEqual([]); | 490 | ).toEqual([]); |
| 491 | expect(NodeFS.existsSync(NodePath.join(config.attachmentsDir, `${pendingId}.png`))).toBe(true); | 491 | expect(NodeFS.existsSync(NodePath.join(config.attachmentsDir, `${pendingId}.png`))).toBe(true); |
| 492 | }).pipe(Effect.provide(intakeTestLayer)), | 492 | }).pipe(Effect.provide(layerIntakeTest)), |
| 493 | ); | 493 | ); |
| 494 | 494 | ||
| 495 | it.effect("releases claimed copies when the recorded answer was text-only", () => | 495 | it.effect("releases claimed copies when the recorded answer was text-only", () => |
| ... | @@ -540,7 +540,7 @@ it.effect("releases claimed copies when the recorded answer was text-only", () = | ... | @@ -540,7 +540,7 @@ it.effect("releases claimed copies when the recorded answer was text-only", () = |
| 540 | ), | 540 | ), |
| 541 | ).toEqual([]); | 541 | ).toEqual([]); |
| 542 | expect(NodeFS.existsSync(NodePath.join(config.attachmentsDir, `${pendingId}.png`))).toBe(true); | 542 | expect(NodeFS.existsSync(NodePath.join(config.attachmentsDir, `${pendingId}.png`))).toBe(true); |
| 543 | }).pipe(Effect.provide(intakeTestLayer)), | 543 | }).pipe(Effect.provide(layerIntakeTest)), |
| 544 | ); | 544 | ); |
| 545 | 545 | ||
| 546 | it.effect("retains claimed copies referenced by a fresh recorded answer", () => | 546 | it.effect("retains claimed copies referenced by a fresh recorded answer", () => |
| ... | @@ -609,7 +609,7 @@ it.effect("retains claimed copies referenced by a fresh recorded answer", () => | ... | @@ -609,7 +609,7 @@ it.effect("retains claimed copies referenced by a fresh recorded answer", () => |
| 609 | entry.startsWith("thread-fresh-answer-"), | 609 | entry.startsWith("thread-fresh-answer-"), |
| 610 | ), | 610 | ), |
| 611 | ).toHaveLength(2); | 611 | ).toHaveLength(2); |
| 612 | }).pipe(Effect.provide(intakeTestLayer)), | 612 | }).pipe(Effect.provide(layerIntakeTest)), |
| 613 | ); | 613 | ); |
| 614 | 614 | ||
| 615 | it.effect("releases claimed copies when preparation dies with a defect", () => | 615 | it.effect("releases claimed copies when preparation dies with a defect", () => |
| ... | @@ -644,7 +644,7 @@ it.effect("releases claimed copies when preparation dies with a defect", () => | ... | @@ -644,7 +644,7 @@ it.effect("releases claimed copies when preparation dies with a defect", () => |
| 644 | ], | 644 | ], |
| 645 | q2: [poisoned] as never, | 645 | q2: [poisoned] as never, |
| 646 | }, | 646 | }, |
| 647 | }).pipe(Effect.provide(failingDispatch(captured)), Effect.exit); | 647 | }).pipe(Effect.provide(layerFailingDispatch(captured)), Effect.exit); |
| 648 | expect(result._tag).toBe("Failure"); | 648 | expect(result._tag).toBe("Failure"); |
| 649 | expect(captured).toHaveLength(0); | 649 | expect(captured).toHaveLength(0); |
| 650 | expect( | 650 | expect( |
| ... | @@ -653,7 +653,7 @@ it.effect("releases claimed copies when preparation dies with a defect", () => | ... | @@ -653,7 +653,7 @@ it.effect("releases claimed copies when preparation dies with a defect", () => |
| 653 | ), | 653 | ), |
| 654 | ).toEqual([]); | 654 | ).toEqual([]); |
| 655 | expect(NodeFS.existsSync(NodePath.join(config.attachmentsDir, `${pendingId}.png`))).toBe(true); | 655 | expect(NodeFS.existsSync(NodePath.join(config.attachmentsDir, `${pendingId}.png`))).toBe(true); |
| 656 | }).pipe(Effect.provide(intakeTestLayer)), | 656 | }).pipe(Effect.provide(layerIntakeTest)), |
| 657 | ); | 657 | ); |
| 658 | 658 | ||
| 659 | it.effect("a retried response re-claims the preserved pending uploads", () => | 659 | it.effect("a retried response re-claims the preserved pending uploads", () => |
| ... | @@ -695,7 +695,7 @@ it.effect("a retried response re-claims the preserved pending uploads", () => | ... | @@ -695,7 +695,7 @@ it.effect("a retried response re-claims the preserved pending uploads", () => |
| 695 | }; | 695 | }; |
| 696 | const captured: OrchestrationV2ServerCommand[] = []; | 696 | const captured: OrchestrationV2ServerCommand[] = []; |
| 697 | const first = yield* dispatchCommand(command).pipe( | 697 | const first = yield* dispatchCommand(command).pipe( |
| 698 | Effect.provide(failingDispatch(captured)), | 698 | Effect.provide(layerFailingDispatch(captured)), |
| 699 | Effect.result, | 699 | Effect.result, |
| 700 | ); | 700 | ); |
| 701 | expect(first._tag).toBe("Failure"); | 701 | expect(first._tag).toBe("Failure"); |
| ... | @@ -736,7 +736,7 @@ it.effect("a retried response re-claims the preserved pending uploads", () => | ... | @@ -736,7 +736,7 @@ it.effect("a retried response re-claims the preserved pending uploads", () => |
| 736 | entry.startsWith("thread-retry-"), | 736 | entry.startsWith("thread-retry-"), |
| 737 | ), | 737 | ), |
| 738 | ).toHaveLength(2); | 738 | ).toHaveLength(2); |
| 739 | }).pipe(Effect.provide(intakeTestLayer)), | 739 | }).pipe(Effect.provide(layerIntakeTest)), |
| 740 | ); | 740 | ); |
| 741 | 741 | ||
| 742 | it.effect("applies the image budget across all questions before dispatch", () => | 742 | it.effect("applies the image budget across all questions before dispatch", () => |
| ... | @@ -757,10 +757,10 @@ it.effect("applies the image budget across all questions before dispatch", () => | ... | @@ -757,10 +757,10 @@ it.effect("applies the image budget across all questions before dispatch", () => |
| 757 | requestId: RuntimeRequestId.make("question-image-budget"), | 757 | requestId: RuntimeRequestId.make("question-image-budget"), |
| 758 | answers: {}, | 758 | answers: {}, |
| 759 | attachmentsByQuestionId: { q1: attachments, q2: attachments }, | 759 | attachmentsByQuestionId: { q1: attachments, q2: attachments }, |
| 760 | }).pipe(Effect.provide(failingDispatch(captured))), | 760 | }).pipe(Effect.provide(layerFailingDispatch(captured))), |
| 761 | ); | 761 | ); |
| 762 | expect(result._tag).toBe("Failure"); | 762 | expect(result._tag).toBe("Failure"); |
| 763 | if (result._tag === "Failure") expect(String(result.cause)).toContain("80 MiB"); | 763 | if (result._tag === "Failure") expect(String(result.cause)).toContain("80 MiB"); |
| 764 | expect(captured).toEqual([]); | 764 | expect(captured).toEqual([]); |
| 765 | }).pipe(Effect.provide(intakeTestLayer)), | 765 | }).pipe(Effect.provide(layerIntakeTest)), |
| 766 | ); | 766 | ); |
apps/server/src/orchestration-v2/ThreadPullRequestService.test.ts+4-4| ... | @@ -180,7 +180,7 @@ describe("ThreadPullRequestServiceV2 reads", () => { | ... | @@ -180,7 +180,7 @@ describe("ThreadPullRequestServiceV2 reads", () => { |
| 180 | const reads = yield* Queue.unbounded< | 180 | const reads = yield* Queue.unbounded< |
| 181 | ThreadId | { readonly location?: string; readonly unsettledOnly?: boolean } | 181 | ThreadId | { readonly location?: string; readonly unsettledOnly?: boolean } |
| 182 | >(); | 182 | >(); |
| 183 | const dependencies = Layer.mergeAll( | 183 | const layerDependencies = Layer.mergeAll( |
| 184 | Layer.mock(Orchestrator.OrchestratorV2)({ | 184 | Layer.mock(Orchestrator.OrchestratorV2)({ |
| 185 | streamDomainEvents: Stream.fromPubSub(events), | 185 | streamDomainEvents: Stream.fromPubSub(events), |
| 186 | getShellSnapshot: (options) => | 186 | getShellSnapshot: (options) => |
| ... | @@ -254,7 +254,7 @@ describe("ThreadPullRequestServiceV2 reads", () => { | ... | @@ -254,7 +254,7 @@ describe("ThreadPullRequestServiceV2 reads", () => { |
| 254 | expect(yield* Queue.take(reads)).toBe(thread.id); | 254 | expect(yield* Queue.take(reads)).toBe(thread.id); |
| 255 | yield* service.drain; | 255 | yield* service.drain; |
| 256 | expect(yield* Queue.size(reads)).toBe(0); | 256 | expect(yield* Queue.size(reads)).toBe(0); |
| 257 | }).pipe(Effect.provide(dependencies)); | 257 | }).pipe(Effect.provide(layerDependencies)); |
| 258 | }), | 258 | }), |
| 259 | ), | 259 | ), |
| 260 | ); | 260 | ); |
| ... | @@ -273,7 +273,7 @@ describe("ThreadPullRequestServiceV2 reads", () => { | ... | @@ -273,7 +273,7 @@ describe("ThreadPullRequestServiceV2 reads", () => { |
| 273 | readonly location?: string; | 273 | readonly location?: string; |
| 274 | readonly unsettledOnly?: boolean; | 274 | readonly unsettledOnly?: boolean; |
| 275 | }>(); | 275 | }>(); |
| 276 | const dependencies = Layer.mergeAll( | 276 | const layerDependencies = Layer.mergeAll( |
| 277 | Layer.mock(Orchestrator.OrchestratorV2)({ | 277 | Layer.mock(Orchestrator.OrchestratorV2)({ |
| 278 | streamDomainEvents: Stream.never, | 278 | streamDomainEvents: Stream.never, |
| 279 | getShellSnapshot: (options) => | 279 | getShellSnapshot: (options) => |
| ... | @@ -315,7 +315,7 @@ describe("ThreadPullRequestServiceV2 reads", () => { | ... | @@ -315,7 +315,7 @@ describe("ThreadPullRequestServiceV2 reads", () => { |
| 315 | yield* TestClock.adjust("1 minute"); | 315 | yield* TestClock.adjust("1 minute"); |
| 316 | expect(yield* Queue.take(reads)).toEqual({ location: "active", unsettledOnly: true }); | 316 | expect(yield* Queue.take(reads)).toEqual({ location: "active", unsettledOnly: true }); |
| 317 | yield* service.drain; | 317 | yield* service.drain; |
| 318 | }).pipe(Effect.provide(dependencies)); | 318 | }).pipe(Effect.provide(layerDependencies)); |
| 319 | }), | 319 | }), |
| 320 | ), | 320 | ), |
| 321 | ); | 321 | ); |
apps/server/src/orchestration-v2/ThreadSearch.test.ts+4-4| ... | @@ -12,16 +12,16 @@ import * as Effect from "effect/Effect"; | ... | @@ -12,16 +12,16 @@ import * as Effect from "effect/Effect"; |
| 12 | import * as Layer from "effect/Layer"; | 12 | import * as Layer from "effect/Layer"; |
| 13 | import * as SqlClient from "effect/sql/SqlClient"; | 13 | import * as SqlClient from "effect/sql/SqlClient"; |
| 14 | 14 | ||
| 15 | import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; | 15 | import * as SqlitePersistence from "../persistence/Layers/Sqlite.ts"; |
| 16 | import * as ProjectionStore from "./ProjectionStore.ts"; | 16 | import * as ProjectionStore from "./ProjectionStore.ts"; |
| 17 | import * as ProjectStore from "./ProjectStore.ts"; | 17 | import * as ProjectStore from "./ProjectStore.ts"; |
| 18 | import * as ThreadSearch from "./ThreadSearch.ts"; | 18 | import * as ThreadSearch from "./ThreadSearch.ts"; |
| 19 | 19 | ||
| 20 | const TestLayer = Layer.mergeAll( | 20 | const layerTest = Layer.mergeAll( |
| 21 | ThreadSearch.layer, | 21 | ThreadSearch.layer, |
| 22 | ProjectionStore.layer, | 22 | ProjectionStore.layer, |
| 23 | ProjectStore.layer, | 23 | ProjectStore.layer, |
| 24 | ).pipe(Layer.provideMerge(SqlitePersistenceMemory)); | 24 | ).pipe(Layer.provideMerge(SqlitePersistence.layerMemory)); |
| 25 | 25 | ||
| 26 | const providerInstanceId = ProviderInstanceId.make("codex"); | 26 | const providerInstanceId = ProviderInstanceId.make("codex"); |
| 27 | const at = (minute: number) => DateTime.makeUnsafe(Date.UTC(2026, 8, 27, 0, minute)); | 27 | const at = (minute: number) => DateTime.makeUnsafe(Date.UTC(2026, 8, 27, 0, minute)); |
| ... | @@ -114,7 +114,7 @@ const message = ( | ... | @@ -114,7 +114,7 @@ const message = ( |
| 114 | }, | 114 | }, |
| 115 | }); | 115 | }); |
| 116 | 116 | ||
| 117 | it.layer(TestLayer)("ThreadSearch", (it) => { | 117 | it.layer(layerTest)("ThreadSearch", (it) => { |
| 118 | it.effect("returns one finished user or assistant match per active thread", () => | 118 | it.effect("returns one finished user or assistant match per active thread", () => |
| 119 | Effect.gen(function* () { | 119 | Effect.gen(function* () { |
| 120 | const projections = yield* ProjectionStore.ProjectionStoreV2; | 120 | const projections = yield* ProjectionStore.ProjectionStoreV2; |
apps/server/src/orchestration-v2/ThreadSettlementService.test.ts+2-2| ... | @@ -571,7 +571,7 @@ const makeHarness = Effect.fn("makeThreadSettlementHarness")(function* (options: | ... | @@ -571,7 +571,7 @@ const makeHarness = Effect.fn("makeThreadSettlementHarness")(function* (options: |
| 571 | ), | 571 | ), |
| 572 | }); | 572 | }); |
| 573 | 573 | ||
| 574 | const dependencies = Layer.mergeAll( | 574 | const layerDependencies = Layer.mergeAll( |
| 575 | Layer.mock(ProjectStore.ProjectStoreV2)({ | 575 | Layer.mock(ProjectStore.ProjectStoreV2)({ |
| 576 | listShells: () => Ref.get(snapshots).pipe(Effect.map((snapshot) => snapshot.projects)), | 576 | listShells: () => Ref.get(snapshots).pipe(Effect.map((snapshot) => snapshot.projects)), |
| 577 | }), | 577 | }), |
| ... | @@ -639,7 +639,7 @@ const makeHarness = Effect.fn("makeThreadSettlementHarness")(function* (options: | ... | @@ -639,7 +639,7 @@ const makeHarness = Effect.fn("makeThreadSettlementHarness")(function* (options: |
| 639 | number: 42, | 639 | number: 42, |
| 640 | mergedAt: NOW, | 640 | mergedAt: NOW, |
| 641 | }), | 641 | }), |
| 642 | layer: ThreadSettlementService.layer.pipe(Layer.provide(dependencies)), | 642 | layer: ThreadSettlementService.layer.pipe(Layer.provide(layerDependencies)), |
| 643 | }; | 643 | }; |
| 644 | }); | 644 | }); |
| 645 | 645 |
apps/server/src/orchestration-v2/ThreadStop.test.ts+17-17| ... | @@ -19,7 +19,7 @@ import * as Schema from "effect/Schema"; | ... | @@ -19,7 +19,7 @@ import * as Schema from "effect/Schema"; |
| 19 | import * as SqlClient from "effect/sql/SqlClient"; | 19 | import * as SqlClient from "effect/sql/SqlClient"; |
| 20 | import * as TestClock from "effect/testing/TestClock"; | 20 | import * as TestClock from "effect/testing/TestClock"; |
| 21 | 21 | ||
| 22 | import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; | 22 | import * as SqlitePersistence from "../persistence/Layers/Sqlite.ts"; |
| 23 | import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts"; | 23 | import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts"; |
| 24 | import { OrchestrationEffectRequestV2 } from "./EffectOutbox.ts"; | 24 | import { OrchestrationEffectRequestV2 } from "./EffectOutbox.ts"; |
| 25 | import * as Orchestrator from "./Orchestrator.ts"; | 25 | import * as Orchestrator from "./Orchestrator.ts"; |
| ... | @@ -27,7 +27,7 @@ import * as ProjectionStore from "./ProjectionStore.ts"; | ... | @@ -27,7 +27,7 @@ import * as ProjectionStore from "./ProjectionStore.ts"; |
| 27 | import type { ProviderAdapterV2Shape } from "./ProviderAdapter.ts"; | 27 | import type { ProviderAdapterV2Shape } from "./ProviderAdapter.ts"; |
| 28 | import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts"; | 28 | import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts"; |
| 29 | import * as ThreadManagementService from "./ThreadManagementService.ts"; | 29 | import * as ThreadManagementService from "./ThreadManagementService.ts"; |
| 30 | import { makeOrchestratorV2ReplayLayerWithRegistry } from "./testkit/ProviderReplayHarness.ts"; | 30 | import * as ProviderReplayHarness from "./testkit/ProviderReplayHarness.ts"; |
| 31 | 31 | ||
| 32 | const instanceId = ProviderInstanceId.make("codex"); | 32 | const instanceId = ProviderInstanceId.make("codex"); |
| 33 | const modelSelection = { instanceId, model: "gpt-5.1-codex" }; | 33 | const modelSelection = { instanceId, model: "gpt-5.1-codex" }; |
| ... | @@ -38,17 +38,17 @@ const adapter = { | ... | @@ -38,17 +38,17 @@ const adapter = { |
| 38 | planSelectionTransition: () => Effect.succeed({ type: "apply_on_next_turn" as const }), | 38 | planSelectionTransition: () => Effect.succeed({ type: "apply_on_next_turn" as const }), |
| 39 | openSession: () => Effect.die("Runs here never reach a provider"), | 39 | openSession: () => Effect.die("Runs here never reach a provider"), |
| 40 | } as ProviderAdapterV2Shape; | 40 | } as ProviderAdapterV2Shape; |
| 41 | const database = SqlitePersistenceMemory; | 41 | const layerDatabase = SqlitePersistence.layerMemory; |
| 42 | // No effect worker: runs stay unstarted, so Stop ends them without a provider. | 42 | // No effect worker: runs stay unstarted, so Stop ends them without a provider. |
| 43 | const testLayer = ThreadManagementService.layer.pipe( | 43 | const layerTest = ThreadManagementService.layer.pipe( |
| 44 | Layer.provideMerge( | 44 | Layer.provideMerge( |
| 45 | Layer.mergeAll( | 45 | Layer.mergeAll( |
| 46 | database, | 46 | layerDatabase, |
| 47 | ProjectionStore.layer.pipe(Layer.provide(database)), | 47 | ProjectionStore.layer.pipe(Layer.provide(layerDatabase)), |
| 48 | makeOrchestratorV2ReplayLayerWithRegistry( | 48 | ProviderReplayHarness.layerWithRegistry( |
| 49 | { name: "thread-stop" }, | 49 | { name: "thread-stop" }, |
| 50 | ProviderAdapterRegistry.makeLayer([adapter]), | 50 | ProviderAdapterRegistry.layerFromAdapters([adapter]), |
| 51 | { databaseLayer: database, runEffectWorker: false }, | 51 | { databaseLayer: layerDatabase, runEffectWorker: false }, |
| 52 | ), | 52 | ), |
| 53 | ), | 53 | ), |
| 54 | ), | 54 | ), |
| ... | @@ -210,7 +210,7 @@ it.effect("Stop ends watches, holds queues, and stops the delegated tasks under | ... | @@ -210,7 +210,7 @@ it.effect("Stop ends watches, holds queues, and stops the delegated tasks under |
| 210 | // A retried effect stops nothing twice. | 210 | // A retried effect stops nothing twice. |
| 211 | yield* threads.stopDelegatedTasks({ threadId: parentThreadId, commandId: stopCommandId }); | 211 | yield* threads.stopDelegatedTasks({ threadId: parentThreadId, commandId: stopCommandId }); |
| 212 | assert.deepEqual((yield* threadState(childThreadId)).runs, ["interrupted", "queued:held"]); | 212 | assert.deepEqual((yield* threadState(childThreadId)).runs, ["interrupted", "queued:held"]); |
| 213 | }).pipe(Effect.provide(testLayer)), | 213 | }).pipe(Effect.provide(layerTest)), |
| 214 | ); | 214 | ); |
| 215 | 215 | ||
| 216 | it.effect( | 216 | it.effect( |
| ... | @@ -234,7 +234,7 @@ it.effect( | ... | @@ -234,7 +234,7 @@ it.effect( |
| 234 | threadId, | 234 | threadId, |
| 235 | }); | 235 | }); |
| 236 | assert.lengthOf(again.storedEvents, 0); | 236 | assert.lengthOf(again.storedEvents, 0); |
| 237 | }).pipe(Effect.provide(testLayer)), | 237 | }).pipe(Effect.provide(layerTest)), |
| 238 | ); | 238 | ); |
| 239 | 239 | ||
| 240 | it.effect("a run Stop reached cannot delegate or start a watch, even after it ends", () => | 240 | it.effect("a run Stop reached cannot delegate or start a watch, even after it ends", () => |
| ... | @@ -319,7 +319,7 @@ it.effect("a run Stop reached cannot delegate or start a watch, even after it en | ... | @@ -319,7 +319,7 @@ it.effect("a run Stop reached cannot delegate or start a watch, even after it en |
| 319 | }); | 319 | }); |
| 320 | } | 320 | } |
| 321 | assert.deepEqual((yield* threadState(threadId)).watched, [4]); | 321 | assert.deepEqual((yield* threadState(threadId)).watched, [4]); |
| 322 | }).pipe(Effect.provide(testLayer)), | 322 | }).pipe(Effect.provide(layerTest)), |
| 323 | ); | 323 | ); |
| 324 | 324 | ||
| 325 | it.effect("thread.stop keeps a restart continuation of the stopped run from starting", () => | 325 | it.effect("thread.stop keeps a restart continuation of the stopped run from starting", () => |
| ... | @@ -372,7 +372,7 @@ it.effect("thread.stop keeps a restart continuation of the stopped run from star | ... | @@ -372,7 +372,7 @@ it.effect("thread.stop keeps a restart continuation of the stopped run from star |
| 372 | restartContinuationOfRunId: run.id, | 372 | restartContinuationOfRunId: run.id, |
| 373 | }); | 373 | }); |
| 374 | assert.deepEqual(yield* threadState(threadId), { runs: ["cancelled"], watched: [] }); | 374 | assert.deepEqual(yield* threadState(threadId), { runs: ["cancelled"], watched: [] }); |
| 375 | }).pipe(Effect.provide(testLayer)), | 375 | }).pipe(Effect.provide(layerTest)), |
| 376 | ); | 376 | ); |
| 377 | 377 | ||
| 378 | it.effect.each(["thread.stop", "run.interrupt"] as const)( | 378 | it.effect.each(["thread.stop", "run.interrupt"] as const)( |
| ... | @@ -468,7 +468,7 @@ it.effect.each(["thread.stop", "run.interrupt"] as const)( | ... | @@ -468,7 +468,7 @@ it.effect.each(["thread.stop", "run.interrupt"] as const)( |
| 468 | runs: ["completed", "interrupted", "cancelled"], | 468 | runs: ["completed", "interrupted", "cancelled"], |
| 469 | watched: [], | 469 | watched: [], |
| 470 | }); | 470 | }); |
| 471 | }).pipe(Effect.provide(testLayer.pipe(Layer.provideMerge(TestClock.layer())))), | 471 | }).pipe(Effect.provide(layerTest.pipe(Layer.provideMerge(TestClock.layer())))), |
| 472 | ); | 472 | ); |
| 473 | 473 | ||
| 474 | it.effect("a delegated task that cannot be stopped fails the walk after its siblings stop", () => | 474 | it.effect("a delegated task that cannot be stopped fails the walk after its siblings stop", () => |
| ... | @@ -506,7 +506,7 @@ it.effect("a delegated task that cannot be stopped fails the walk after its sibl | ... | @@ -506,7 +506,7 @@ it.effect("a delegated task that cannot be stopped fails the walk after its sibl |
| 506 | ); | 506 | ); |
| 507 | assert.isTrue(Exit.isFailure(walked)); | 507 | assert.isTrue(Exit.isFailure(walked)); |
| 508 | assert.deepEqual((yield* threadState(childThreadId)).runs, ["interrupted"]); | 508 | assert.deepEqual((yield* threadState(childThreadId)).runs, ["interrupted"]); |
| 509 | }).pipe(Effect.provide(testLayer)), | 509 | }).pipe(Effect.provide(layerTest)), |
| 510 | ); | 510 | ); |
| 511 | 511 | ||
| 512 | it.effect("thread.stop on a finished thread refuses a late agent watch", () => | 512 | it.effect("thread.stop on a finished thread refuses a late agent watch", () => |
| ... | @@ -534,7 +534,7 @@ it.effect("thread.stop on a finished thread refuses a late agent watch", () => | ... | @@ -534,7 +534,7 @@ it.effect("thread.stop on a finished thread refuses a late agent watch", () => |
| 534 | }); | 534 | }); |
| 535 | assert.isTrue(Exit.isFailure(yield* Effect.exit(watch(threadId, 10)))); | 535 | assert.isTrue(Exit.isFailure(yield* Effect.exit(watch(threadId, 10)))); |
| 536 | assert.deepEqual(yield* threadState(threadId), { runs: ["completed"], watched: [] }); | 536 | assert.deepEqual(yield* threadState(threadId), { runs: ["completed"], watched: [] }); |
| 537 | }).pipe(Effect.provide(testLayer)), | 537 | }).pipe(Effect.provide(layerTest)), |
| 538 | ); | 538 | ); |
| 539 | 539 | ||
| 540 | it.effect("thread.stop marks a turn it cannot interrupt so a late agent watch is refused", () => | 540 | it.effect("thread.stop marks a turn it cannot interrupt so a late agent watch is refused", () => |
| ... | @@ -580,5 +580,5 @@ it.effect("thread.stop marks a turn it cannot interrupt so a late agent watch is | ... | @@ -580,5 +580,5 @@ it.effect("thread.stop marks a turn it cannot interrupt so a late agent watch is |
| 580 | }); | 580 | }); |
| 581 | assert.isTrue(Exit.isFailure(yield* Effect.exit(watch(threadId, 12)))); | 581 | assert.isTrue(Exit.isFailure(yield* Effect.exit(watch(threadId, 12)))); |
| 582 | assert.deepEqual(yield* threadState(threadId), { runs: ["running"], watched: [] }); | 582 | assert.deepEqual(yield* threadState(threadId), { runs: ["running"], watched: [] }); |
| 583 | }).pipe(Effect.provide(testLayer)), | 583 | }).pipe(Effect.provide(layerTest)), |
| 584 | ); | 584 | ); |
apps/server/src/orchestration-v2/ThreadTitleRegenerationService.test.ts+19-14| ... | @@ -16,7 +16,7 @@ import * as TestClock from "effect/testing/TestClock"; | ... | @@ -16,7 +16,7 @@ import * as TestClock from "effect/testing/TestClock"; |
| 16 | import * as Layer from "effect/Layer"; | 16 | import * as Layer from "effect/Layer"; |
| 17 | import * as Option from "effect/Option"; | 17 | import * as Option from "effect/Option"; |
| 18 | 18 | ||
| 19 | import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; | 19 | import * as SqlitePersistence from "../persistence/Layers/Sqlite.ts"; |
| 20 | import * as ProjectStore from "./ProjectStore.ts"; | 20 | import * as ProjectStore from "./ProjectStore.ts"; |
| 21 | import * as ServerSettings from "../serverSettings.ts"; | 21 | import * as ServerSettings from "../serverSettings.ts"; |
| 22 | import * as TextGeneration from "../textGeneration/TextGeneration.ts"; | 22 | import * as TextGeneration from "../textGeneration/TextGeneration.ts"; |
| ... | @@ -26,7 +26,7 @@ import type { ProviderAdapterV2Shape } from "./ProviderAdapter.ts"; | ... | @@ -26,7 +26,7 @@ import type { ProviderAdapterV2Shape } from "./ProviderAdapter.ts"; |
| 26 | import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts"; | 26 | import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts"; |
| 27 | import * as ThreadManagement from "./ThreadManagementService.ts"; | 27 | import * as ThreadManagement from "./ThreadManagementService.ts"; |
| 28 | import * as ThreadTitleRegeneration from "./ThreadTitleRegenerationService.ts"; | 28 | import * as ThreadTitleRegeneration from "./ThreadTitleRegenerationService.ts"; |
| 29 | import { makeOrchestratorV2ReplayLayerWithRegistry } from "./testkit/ProviderReplayHarness.ts"; | 29 | import * as ProviderReplayHarness from "./testkit/ProviderReplayHarness.ts"; |
| 30 | 30 | ||
| 31 | const projectId = ProjectId.make("project:title-regeneration"); | 31 | const projectId = ProjectId.make("project:title-regeneration"); |
| 32 | const modelSelection = { | 32 | const modelSelection = { |
| ... | @@ -47,19 +47,19 @@ function makeHarness( | ... | @@ -47,19 +47,19 @@ function makeHarness( |
| 47 | readonly generateTitle?: TextGeneration.TextGeneration["Service"]["generateThreadTitle"]; | 47 | readonly generateTitle?: TextGeneration.TextGeneration["Service"]["generateThreadTitle"]; |
| 48 | } = {}, | 48 | } = {}, |
| 49 | ) { | 49 | ) { |
| 50 | const database = SqlitePersistenceMemory; | 50 | const layerDatabase = SqlitePersistence.layerMemory; |
| 51 | const registry = ProviderAdapterRegistry.makeLayer([adapter]); | 51 | const layerRegistry = ProviderAdapterRegistry.layerFromAdapters([adapter]); |
| 52 | const orchestrator = makeOrchestratorV2ReplayLayerWithRegistry( | 52 | const layerOrchestrator = ProviderReplayHarness.layerWithRegistry( |
| 53 | { name: "thread-title-regeneration" }, | 53 | { name: "thread-title-regeneration" }, |
| 54 | registry, | 54 | layerRegistry, |
| 55 | { databaseLayer: database, runEffectWorker: false }, | 55 | { databaseLayer: layerDatabase, runEffectWorker: false }, |
| 56 | ); | 56 | ); |
| 57 | const threadManagement = ThreadManagement.layer.pipe(Layer.provide(orchestrator)); | 57 | const layerThreadManagement = ThreadManagement.layer.pipe(Layer.provide(layerOrchestrator)); |
| 58 | const outbox = EffectOutbox.layer.pipe(Layer.provide(database)); | 58 | const layerOutbox = EffectOutbox.layer.pipe(Layer.provide(layerDatabase)); |
| 59 | const generateThreadTitle = vi.fn( | 59 | const generateThreadTitle = vi.fn( |
| 60 | options.generateTitle ?? (() => Effect.succeed({ title: "Generated title" })), | 60 | options.generateTitle ?? (() => Effect.succeed({ title: "Generated title" })), |
| 61 | ); | 61 | ); |
| 62 | const projectedProjects = Layer.mock(ProjectStore.ProjectStoreV2)({ | 62 | const layerProjectedProjects = Layer.mock(ProjectStore.ProjectStoreV2)({ |
| 63 | get: (requestedProjectId) => | 63 | get: (requestedProjectId) => |
| 64 | Effect.succeed( | 64 | Effect.succeed( |
| 65 | requestedProjectId === projectId | 65 | requestedProjectId === projectId |
| ... | @@ -80,18 +80,23 @@ function makeHarness( | ... | @@ -80,18 +80,23 @@ function makeHarness( |
| 80 | : Option.none(), | 80 | : Option.none(), |
| 81 | ), | 81 | ), |
| 82 | }); | 82 | }); |
| 83 | const titleRegeneration = ThreadTitleRegeneration.layer.pipe( | 83 | const layerTitleRegeneration = ThreadTitleRegeneration.layer.pipe( |
| 84 | Layer.provide( | 84 | Layer.provide( |
| 85 | Layer.mergeAll( | 85 | Layer.mergeAll( |
| 86 | threadManagement, | 86 | layerThreadManagement, |
| 87 | projectedProjects, | 87 | layerProjectedProjects, |
| 88 | Layer.mock(TextGeneration.TextGeneration)({ generateThreadTitle }), | 88 | Layer.mock(TextGeneration.TextGeneration)({ generateThreadTitle }), |
| 89 | ServerSettings.layerTest({}), | 89 | ServerSettings.layerTest({}), |
| 90 | ), | 90 | ), |
| 91 | ), | 91 | ), |
| 92 | ); | 92 | ); |
| 93 | return { | 93 | return { |
| 94 | layer: Layer.mergeAll(threadManagement, titleRegeneration, outbox, database), | 94 | layer: Layer.mergeAll( |
| 95 | layerThreadManagement, | ||
| 96 | layerTitleRegeneration, | ||
| 97 | layerOutbox, | ||
| 98 | layerDatabase, | ||
| 99 | ), | ||
| 95 | generateThreadTitle, | 100 | generateThreadTitle, |
| 96 | }; | 101 | }; |
| 97 | } | 102 | } |
apps/server/src/orchestration-v2/TurnStartReads.test.ts+6-6| ... | @@ -16,12 +16,12 @@ import * as DateTime from "effect/DateTime"; | ... | @@ -16,12 +16,12 @@ import * as DateTime from "effect/DateTime"; |
| 16 | import * as Effect from "effect/Effect"; | 16 | import * as Effect from "effect/Effect"; |
| 17 | import * as Layer from "effect/Layer"; | 17 | import * as Layer from "effect/Layer"; |
| 18 | import * as SqlClient from "effect/sql/SqlClient"; | 18 | import * as SqlClient from "effect/sql/SqlClient"; |
| 19 | import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; | 19 | import * as SqlitePersistence from "../persistence/Layers/Sqlite.ts"; |
| 20 | import * as ProjectionStore from "./ProjectionStore.ts"; | 20 | import * as ProjectionStore from "./ProjectionStore.ts"; |
| 21 | 21 | ||
| 22 | const databaseLayer = Layer.mergeAll( | 22 | const layerDatabase = Layer.mergeAll( |
| 23 | SqlitePersistenceMemory, | 23 | SqlitePersistence.layerMemory, |
| 24 | ProjectionStore.layer.pipe(Layer.provide(SqlitePersistenceMemory)), | 24 | ProjectionStore.layer.pipe(Layer.provide(SqlitePersistence.layerMemory)), |
| 25 | ); | 25 | ); |
| 26 | 26 | ||
| 27 | it.effect.each(["sqlite", "memory"] as const)( | 27 | it.effect.each(["sqlite", "memory"] as const)( |
| ... | @@ -362,8 +362,8 @@ it.effect.each(["sqlite", "memory"] as const)( | ... | @@ -362,8 +362,8 @@ it.effect.each(["sqlite", "memory"] as const)( |
| 362 | }).pipe( | 362 | }).pipe( |
| 363 | Effect.provide( | 363 | Effect.provide( |
| 364 | storage === "sqlite" | 364 | storage === "sqlite" |
| 365 | ? databaseLayer | 365 | ? layerDatabase |
| 366 | : Layer.merge(SqlitePersistenceMemory, ProjectionStore.layerMemory), | 366 | : Layer.merge(SqlitePersistence.layerMemory, ProjectionStore.layerMemory), |
| 367 | ), | 367 | ), |
| 368 | ), | 368 | ), |
| 369 | ); | 369 | ); |
apps/server/src/orchestration-v2/UsageLimitRecoveryWorker.ts+1-1| ... | @@ -106,7 +106,7 @@ const makeSweep = Effect.gen(function* () { | ... | @@ -106,7 +106,7 @@ const makeSweep = Effect.gen(function* () { |
| 106 | 106 | ||
| 107 | // The shared scheduler derives due work from persisted failures and recovery | 107 | // The shared scheduler derives due work from persisted failures and recovery |
| 108 | // choices, so restarts need no timer restoration or connected client. | 108 | // choices, so restarts need no timer restoration or connected client. |
| 109 | export const workerLive = Layer.effectDiscard( | 109 | export const layer = Layer.effectDiscard( |
| 110 | Effect.gen(function* () { | 110 | Effect.gen(function* () { |
| 111 | const sweep = yield* makeSweep; | 111 | const sweep = yield* makeSweep; |
| 112 | const scheduler = yield* Scheduler.Scheduler; | 112 | const scheduler = yield* Scheduler.Scheduler; |
apps/server/src/orchestration-v2/http.ts+1-1| ... | @@ -63,7 +63,7 @@ function selectHistoryPageFromCursorOrError( | ... | @@ -63,7 +63,7 @@ function selectHistoryPageFromCursorOrError( |
| 63 | * compressible and cacheable — and then resume the WebSocket subscription via | 63 | * compressible and cacheable — and then resume the WebSocket subscription via |
| 64 | * `afterSequence`. | 64 | * `afterSequence`. |
| 65 | */ | 65 | */ |
| 66 | export const orchestrationHttpApiLayer = HttpApiBuilder.group( | 66 | export const layer = HttpApiBuilder.group( |
| 67 | EnvironmentHttpApi, | 67 | EnvironmentHttpApi, |
| 68 | "orchestration", | 68 | "orchestration", |
| 69 | Effect.fnUntraced(function* (handlers) { | 69 | Effect.fnUntraced(function* (handlers) { |
apps/server/src/orchestration-v2/legacy/LegacyV1Cutover.integration.test.ts+29-21| ... | @@ -27,7 +27,7 @@ import * as Stream from "effect/Stream"; | ... | @@ -27,7 +27,7 @@ import * as Stream from "effect/Stream"; |
| 27 | import * as SqlClient from "effect/sql/SqlClient"; | 27 | import * as SqlClient from "effect/sql/SqlClient"; |
| 28 | 28 | ||
| 29 | import { runMigrations } from "../../persistence/Migrations.ts"; | 29 | import { runMigrations } from "../../persistence/Migrations.ts"; |
| 30 | import { makeSqlitePersistenceLive } from "../../persistence/Layers/Sqlite.ts"; | 30 | import * as SqlitePersistence from "../../persistence/Layers/Sqlite.ts"; |
| 31 | import Migration0042 from "../../persistence/Migrations/042_ProjectionThreadLinkedPullRequest.ts"; | 31 | import Migration0042 from "../../persistence/Migrations/042_ProjectionThreadLinkedPullRequest.ts"; |
| 32 | import Migration0043 from "../../persistence/Migrations/043_ProjectionThreadsUnsettledAt.ts"; | 32 | import Migration0043 from "../../persistence/Migrations/043_ProjectionThreadsUnsettledAt.ts"; |
| 33 | import Migration0044 from "../../persistence/Migrations/044_ClearAutomaticProjectModelDefaults.ts"; | 33 | import Migration0044 from "../../persistence/Migrations/044_ClearAutomaticProjectModelDefaults.ts"; |
| ... | @@ -50,7 +50,7 @@ import { | ... | @@ -50,7 +50,7 @@ import { |
| 50 | type ProviderAdapterV2Shape, | 50 | type ProviderAdapterV2Shape, |
| 51 | } from "../ProviderAdapter.ts"; | 51 | } from "../ProviderAdapter.ts"; |
| 52 | import * as ProviderAdapterRegistry from "../ProviderAdapterRegistry.ts"; | 52 | import * as ProviderAdapterRegistry from "../ProviderAdapterRegistry.ts"; |
| 53 | import { makeOrchestratorV2ReplayLayerWithRegistry } from "../testkit/ProviderReplayHarness.ts"; | 53 | import * as ProviderReplayHarness from "../testkit/ProviderReplayHarness.ts"; |
| 54 | import { checkpointWorkspace } from "../testkit/ReplayFixtureWorkspace.ts"; | 54 | import { checkpointWorkspace } from "../testkit/ReplayFixtureWorkspace.ts"; |
| 55 | 55 | ||
| 56 | const PROJECT_ID = "project:cutover"; | 56 | const PROJECT_ID = "project:cutover"; |
| ... | @@ -579,24 +579,32 @@ const waitForIdle = Effect.fn("LegacyV1Cutover.waitForIdle")(function* (threadId | ... | @@ -579,24 +579,32 @@ const waitForIdle = Effect.fn("LegacyV1Cutover.waitForIdle")(function* (threadId |
| 579 | return yield* Effect.die(new Error("Cutover test timed out waiting for idle")); | 579 | return yield* Effect.die(new Error("Cutover test timed out waiting for idle")); |
| 580 | }); | 580 | }); |
| 581 | 581 | ||
| 582 | const makeBootLayer = (input: { | 582 | const layerBoot = (input: { |
| 583 | readonly name: string; | 583 | readonly name: string; |
| 584 | readonly dbPath: string; | 584 | readonly dbPath: string; |
| 585 | readonly workspace: string; | 585 | readonly workspace: string; |
| 586 | readonly capturedTurns: Ref.Ref<ReadonlyArray<CapturedTurn>>; | 586 | readonly capturedTurns: Ref.Ref<ReadonlyArray<CapturedTurn>>; |
| 587 | }) => { | 587 | }) => { |
| 588 | const databaseLayer = makeSqlitePersistenceLive(input.dbPath).pipe( | 588 | const layerDatabase = SqlitePersistence.layerFromPath(input.dbPath).pipe( |
| 589 | Layer.provide(NodeServices.layer), | 589 | Layer.provide(NodeServices.layer), |
| 590 | ); | 590 | ); |
| 591 | const eventStoreProvided = EventStore.layer.pipe(Layer.provideMerge(databaseLayer)); | 591 | const layerEventStoreProvided = EventStore.layer.pipe(Layer.provideMerge(layerDatabase)); |
| 592 | const projectionStoreProvided = ProjectionStore.layer.pipe(Layer.provideMerge(databaseLayer)); | 592 | const layerProjectionStoreProvided = ProjectionStore.layer.pipe( |
| 593 | const storesProvided = Layer.mergeAll(databaseLayer, eventStoreProvided, projectionStoreProvided); | 593 | Layer.provideMerge(layerDatabase), |
| 594 | const eventSinkProvided = EventSink.layer.pipe(Layer.provide(storesProvided)); | ||
| 595 | const importerProvided = LegacyV1ThreadImporter.layer.pipe( | ||
| 596 | Layer.provide(Layer.mergeAll(storesProvided, eventSinkProvided)), | ||
| 597 | ); | 594 | ); |
| 598 | const maintenanceProvided = ProjectionMaintenance.layer.pipe(Layer.provide(storesProvided)); | 595 | const layerStoresProvided = Layer.mergeAll( |
| 599 | const orchestratorProvided = makeOrchestratorV2ReplayLayerWithRegistry( | 596 | layerDatabase, |
| 597 | layerEventStoreProvided, | ||
| 598 | layerProjectionStoreProvided, | ||
| 599 | ); | ||
| 600 | const layerEventSinkProvided = EventSink.layer.pipe(Layer.provide(layerStoresProvided)); | ||
| 601 | const layerImporterProvided = LegacyV1ThreadImporter.layer.pipe( | ||
| 602 | Layer.provide(Layer.mergeAll(layerStoresProvided, layerEventSinkProvided)), | ||
| 603 | ); | ||
| 604 | const layerMaintenanceProvided = ProjectionMaintenance.layer.pipe( | ||
| 605 | Layer.provide(layerStoresProvided), | ||
| 606 | ); | ||
| 607 | const layerOrchestratorProvided = ProviderReplayHarness.layerWithRegistry( | ||
| 600 | { | 608 | { |
| 601 | name: input.name, | 609 | name: input.name, |
| 602 | runtimePolicyOverride: { | 610 | runtimePolicyOverride: { |
| ... | @@ -609,15 +617,15 @@ const makeBootLayer = (input: { | ... | @@ -609,15 +617,15 @@ const makeBootLayer = (input: { |
| 609 | }, | 617 | }, |
| 610 | }, | 618 | }, |
| 611 | }, | 619 | }, |
| 612 | ProviderAdapterRegistry.makeSingleLayer(makeCodexAdapter(input.capturedTurns)), | 620 | ProviderAdapterRegistry.layerSingle(makeCodexAdapter(input.capturedTurns)), |
| 613 | { databaseLayer }, | 621 | { databaseLayer: layerDatabase }, |
| 614 | ); | 622 | ); |
| 615 | return Layer.mergeAll( | 623 | return Layer.mergeAll( |
| 616 | storesProvided, | 624 | layerStoresProvided, |
| 617 | eventSinkProvided, | 625 | layerEventSinkProvided, |
| 618 | importerProvided, | 626 | layerImporterProvided, |
| 619 | maintenanceProvided, | 627 | layerMaintenanceProvided, |
| 620 | orchestratorProvided, | 628 | layerOrchestratorProvided, |
| 621 | ); | 629 | ); |
| 622 | }; | 630 | }; |
| 623 | 631 | ||
| ... | @@ -909,7 +917,7 @@ describe("orchestration v2 legacy v1 cutover", () => { | ... | @@ -909,7 +917,7 @@ describe("orchestration v2 legacy v1 cutover", () => { |
| 909 | }; | 917 | }; |
| 910 | }).pipe( | 918 | }).pipe( |
| 911 | Effect.provide( | 919 | Effect.provide( |
| 912 | makeBootLayer({ | 920 | layerBoot({ |
| 913 | name: "legacy-v1-cutover-first", | 921 | name: "legacy-v1-cutover-first", |
| 914 | dbPath: copyPath, | 922 | dbPath: copyPath, |
| 915 | workspace, | 923 | workspace, |
| ... | @@ -1020,7 +1028,7 @@ describe("orchestration v2 legacy v1 cutover", () => { | ... | @@ -1020,7 +1028,7 @@ describe("orchestration v2 legacy v1 cutover", () => { |
| 1020 | ); | 1028 | ); |
| 1021 | }).pipe( | 1029 | }).pipe( |
| 1022 | Effect.provide( | 1030 | Effect.provide( |
| 1023 | makeBootLayer({ | 1031 | layerBoot({ |
| 1024 | name: "legacy-v1-cutover-restart", | 1032 | name: "legacy-v1-cutover-restart", |
| 1025 | dbPath: copyPath, | 1033 | dbPath: copyPath, |
| 1026 | workspace, | 1034 | workspace, |
apps/server/src/orchestration-v2/legacy/LegacyV1ThreadImporter.test.ts+20-16| ... | @@ -6,7 +6,7 @@ import * as Layer from "effect/Layer"; | ... | @@ -6,7 +6,7 @@ import * as Layer from "effect/Layer"; |
| 6 | import * as Tracer from "effect/Tracer"; | 6 | import * as Tracer from "effect/Tracer"; |
| 7 | import * as SqlClient from "effect/sql/SqlClient"; | 7 | import * as SqlClient from "effect/sql/SqlClient"; |
| 8 | 8 | ||
| 9 | import { SqlitePersistenceMemory } from "../../persistence/Layers/Sqlite.ts"; | 9 | import * as SqlitePersistence from "../../persistence/Layers/Sqlite.ts"; |
| 10 | import { listLinkedPullRequestThreads } from "../../pullRequest/linkedThreads.ts"; | 10 | import { listLinkedPullRequestThreads } from "../../pullRequest/linkedThreads.ts"; |
| 11 | import * as EventSink from "../EventSink.ts"; | 11 | import * as EventSink from "../EventSink.ts"; |
| 12 | import * as EventStore from "../EventStore.ts"; | 12 | import * as EventStore from "../EventStore.ts"; |
| ... | @@ -14,25 +14,29 @@ import * as LegacyV1ThreadImporter from "./LegacyV1ThreadImporter.ts"; | ... | @@ -14,25 +14,29 @@ import * as LegacyV1ThreadImporter from "./LegacyV1ThreadImporter.ts"; |
| 14 | import * as ProjectionMaintenance from "../ProjectionMaintenance.ts"; | 14 | import * as ProjectionMaintenance from "../ProjectionMaintenance.ts"; |
| 15 | import * as ProjectionStore from "../ProjectionStore.ts"; | 15 | import * as ProjectionStore from "../ProjectionStore.ts"; |
| 16 | 16 | ||
| 17 | const databaseLayer = SqlitePersistenceMemory; | 17 | const layerDatabase = SqlitePersistence.layerMemory; |
| 18 | const eventStoreProvided = EventStore.layer.pipe(Layer.provideMerge(databaseLayer)); | 18 | const layerEventStoreProvided = EventStore.layer.pipe(Layer.provideMerge(layerDatabase)); |
| 19 | const projectionStoreProvided = ProjectionStore.layer.pipe(Layer.provideMerge(databaseLayer)); | 19 | const layerProjectionStoreProvided = ProjectionStore.layer.pipe(Layer.provideMerge(layerDatabase)); |
| 20 | const storesProvided = Layer.mergeAll(databaseLayer, eventStoreProvided, projectionStoreProvided); | 20 | const layerStoresProvided = Layer.mergeAll( |
| 21 | const eventSinkProvided = EventSink.layer.pipe(Layer.provide(storesProvided)); | 21 | layerDatabase, |
| 22 | const importerProvided = LegacyV1ThreadImporter.layer.pipe( | 22 | layerEventStoreProvided, |
| 23 | Layer.provide(Layer.mergeAll(storesProvided, eventSinkProvided)), | 23 | layerProjectionStoreProvided, |
| 24 | ); | 24 | ); |
| 25 | const projectionMaintenanceProvided = ProjectionMaintenance.layer.pipe( | 25 | const layerEventSinkProvided = EventSink.layer.pipe(Layer.provide(layerStoresProvided)); |
| 26 | Layer.provide(storesProvided), | 26 | const layerImporterProvided = LegacyV1ThreadImporter.layer.pipe( |
| 27 | Layer.provide(Layer.mergeAll(layerStoresProvided, layerEventSinkProvided)), | ||
| 27 | ); | 28 | ); |
| 28 | const TestLayer = Layer.mergeAll( | 29 | const layerProjectionMaintenanceProvided = ProjectionMaintenance.layer.pipe( |
| 29 | storesProvided, | 30 | Layer.provide(layerStoresProvided), |
| 30 | eventSinkProvided, | 31 | ); |
| 31 | importerProvided, | 32 | const layerTest = Layer.mergeAll( |
| 32 | projectionMaintenanceProvided, | 33 | layerStoresProvided, |
| 34 | layerEventSinkProvided, | ||
| 35 | layerImporterProvided, | ||
| 36 | layerProjectionMaintenanceProvided, | ||
| 33 | ); | 37 | ); |
| 34 | 38 | ||
| 35 | it.layer(TestLayer)("LegacyV1ThreadImporter", (it) => { | 39 | it.layer(layerTest)("LegacyV1ThreadImporter", (it) => { |
| 36 | it.effect("uses the created-thread index for startup migration checks", () => | 40 | it.effect("uses the created-thread index for startup migration checks", () => |
| 37 | Effect.gen(function* () { | 41 | Effect.gen(function* () { |
| 38 | const sql = yield* SqlClient.SqlClient; | 42 | const sql = yield* SqlClient.SqlClient; |
apps/server/src/orchestration-v2/runtimeLayer.test.ts+61-66| ... | @@ -43,7 +43,7 @@ import * as SqlClient from "effect/sql/SqlClient"; | ... | @@ -43,7 +43,7 @@ import * as SqlClient from "effect/sql/SqlClient"; |
| 43 | import * as CheckpointStore from "../checkpointing/CheckpointStore.ts"; | 43 | import * as CheckpointStore from "../checkpointing/CheckpointStore.ts"; |
| 44 | import * as GitWorkflow from "../git/GitWorkflowService.ts"; | 44 | import * as GitWorkflow from "../git/GitWorkflowService.ts"; |
| 45 | import * as ServerConfig from "../config.ts"; | 45 | import * as ServerConfig from "../config.ts"; |
| 46 | import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; | 46 | import * as SqlitePersistence from "../persistence/Layers/Sqlite.ts"; |
| 47 | import * as OrchestrationEventStore from "../persistence/Services/OrchestrationEventStore.ts"; | 47 | import * as OrchestrationEventStore from "../persistence/Services/OrchestrationEventStore.ts"; |
| 48 | import * as ProjectEnrichmentService from "../project/ProjectEnrichmentService.ts"; | 48 | import * as ProjectEnrichmentService from "../project/ProjectEnrichmentService.ts"; |
| 49 | import * as ProjectService from "../project/ProjectService.ts"; | 49 | import * as ProjectService from "../project/ProjectService.ts"; |
| ... | @@ -69,25 +69,20 @@ import * as PullRequestService from "../pullRequest/PullRequestService.ts"; | ... | @@ -69,25 +69,20 @@ import * as PullRequestService from "../pullRequest/PullRequestService.ts"; |
| 69 | import * as ProjectStore from "./ProjectStore.ts"; | 69 | import * as ProjectStore from "./ProjectStore.ts"; |
| 70 | import type { ProviderAdapterV2SessionRuntime, ProviderAdapterV2Shape } from "./ProviderAdapter.ts"; | 70 | import type { ProviderAdapterV2SessionRuntime, ProviderAdapterV2Shape } from "./ProviderAdapter.ts"; |
| 71 | import * as ProviderSessionManager from "./ProviderSessionManager.ts"; | 71 | import * as ProviderSessionManager from "./ProviderSessionManager.ts"; |
| 72 | import { | 72 | import * as RuntimeLayer from "./runtimeLayer.ts"; |
| 73 | OrchestrationEventInfrastructureLayerLive, | ||
| 74 | OrchestrationV2EventSinkLayerLive, | ||
| 75 | OrchestrationV2LayerLive, | ||
| 76 | ProjectServiceLayerLive, | ||
| 77 | } from "./runtimeLayer.ts"; | ||
| 78 | import { shellStreamItemFromThreadShell } from "./ShellStream.ts"; | 73 | import { shellStreamItemFromThreadShell } from "./ShellStream.ts"; |
| 79 | import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts"; | 74 | import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts"; |
| 80 | import * as ThreadManagementService from "./ThreadManagementService.ts"; | 75 | import * as ThreadManagementService from "./ThreadManagementService.ts"; |
| 81 | import * as ThreadCommandExecutor from "./ThreadCommandExecutor.ts"; | 76 | import * as ThreadCommandExecutor from "./ThreadCommandExecutor.ts"; |
| 82 | 77 | ||
| 83 | const PlatformTestLayer = Layer.merge( | 78 | const layerPlatformTest = Layer.merge( |
| 84 | NodeServices.layer, | 79 | NodeServices.layer, |
| 85 | Layer.mock(SourceControlProviderRegistry.SourceControlProviderRegistry)({ | 80 | Layer.mock(SourceControlProviderRegistry.SourceControlProviderRegistry)({ |
| 86 | resolveLink: () => Effect.die("unused title link"), | 81 | resolveLink: () => Effect.die("unused title link"), |
| 87 | }), | 82 | }), |
| 88 | ); | 83 | ); |
| 89 | 84 | ||
| 90 | const ServerConfigLayer = ServerConfig.layerTest(process.cwd(), { | 85 | const layerServerConfig = ServerConfig.layerTest(process.cwd(), { |
| 91 | prefix: "t3-orchestration-v2-runtime-layer-", | 86 | prefix: "t3-orchestration-v2-runtime-layer-", |
| 92 | }); | 87 | }); |
| 93 | 88 | ||
| ... | @@ -97,20 +92,20 @@ const modelSelection = { | ... | @@ -97,20 +92,20 @@ const modelSelection = { |
| 97 | } satisfies ModelSelection; | 92 | } satisfies ModelSelection; |
| 98 | const alternateInstanceId = ProviderInstanceId.make("codex_alternate"); | 93 | const alternateInstanceId = ProviderInstanceId.make("codex_alternate"); |
| 99 | 94 | ||
| 100 | const VcsDriverRegistryTestLayer = VcsDriverRegistry.layer.pipe( | 95 | const layerVcsDriverRegistryTest = VcsDriverRegistry.layer.pipe( |
| 101 | Layer.provide(VcsProcess.layer), | 96 | Layer.provide(VcsProcess.layer), |
| 102 | Layer.provide(ServerConfigLayer), | 97 | Layer.provide(layerServerConfig), |
| 103 | Layer.provide(PlatformTestLayer), | 98 | Layer.provide(layerPlatformTest), |
| 104 | ); | 99 | ); |
| 105 | 100 | ||
| 106 | const CheckpointStoreTestLayer = CheckpointStore.layer.pipe( | 101 | const layerCheckpointStoreTest = CheckpointStore.layer.pipe( |
| 107 | Layer.provide(VcsDriverRegistryTestLayer), | 102 | Layer.provide(layerVcsDriverRegistryTest), |
| 108 | ); | 103 | ); |
| 109 | const GitWorkflowTestLayer = Layer.mock(GitWorkflow.GitWorkflowService)({ | 104 | const layerGitWorkflowTest = Layer.mock(GitWorkflow.GitWorkflowService)({ |
| 110 | pruneWorktrees: () => Effect.void, | 105 | pruneWorktrees: () => Effect.void, |
| 111 | createWorktree: () => Effect.succeed({} as never), | 106 | createWorktree: () => Effect.succeed({} as never), |
| 112 | }); | 107 | }); |
| 113 | const ProjectServiceTestLayer = Layer.mock(ProjectService.ProjectService)({ | 108 | const layerProjectServiceTest = Layer.mock(ProjectService.ProjectService)({ |
| 114 | getById: () => Effect.succeed(Option.none()), | 109 | getById: () => Effect.succeed(Option.none()), |
| 115 | }); | 110 | }); |
| 116 | 111 | ||
| ... | @@ -150,7 +145,7 @@ const alternateProviderInstance = { | ... | @@ -150,7 +145,7 @@ const alternateProviderInstance = { |
| 150 | }, | 145 | }, |
| 151 | } satisfies ProviderInstance; | 146 | } satisfies ProviderInstance; |
| 152 | 147 | ||
| 153 | const TestProviderInstanceRegistry = Layer.succeed( | 148 | const layerTestProviderInstanceRegistry = Layer.succeed( |
| 154 | ProviderInstanceRegistry.ProviderInstanceRegistry, | 149 | ProviderInstanceRegistry.ProviderInstanceRegistry, |
| 155 | { | 150 | { |
| 156 | getInstance: (instanceId) => | 151 | getInstance: (instanceId) => |
| ... | @@ -268,41 +263,41 @@ const moveProject = (projectId: ProjectId, workspaceRoot: string, updatedAt: str | ... | @@ -268,41 +263,41 @@ const moveProject = (projectId: ProjectId, workspaceRoot: string, updatedAt: str |
| 268 | }), | 263 | }), |
| 269 | ); | 264 | ); |
| 270 | 265 | ||
| 271 | const TestLayer = Layer.mergeAll( | 266 | const layerTest = Layer.mergeAll( |
| 272 | OrchestrationV2LayerLive, | 267 | RuntimeLayer.layer, |
| 273 | OrchestrationV2EventSinkLayerLive, | 268 | RuntimeLayer.layerEventSink, |
| 274 | ProjectStore.layer, | 269 | ProjectStore.layer, |
| 275 | ProjectionStore.layer, | 270 | ProjectionStore.layer, |
| 276 | EffectOutbox.layer, | 271 | EffectOutbox.layer, |
| 277 | ThreadCommandExecutor.layer, | 272 | ThreadCommandExecutor.layer, |
| 278 | ).pipe( | 273 | ).pipe( |
| 279 | Layer.provide(McpSessionRegistryTestkit.layer), | 274 | Layer.provide(McpSessionRegistryTestkit.layer), |
| 280 | Layer.provide(SqlitePersistenceMemory), | 275 | Layer.provide(SqlitePersistence.layerMemory), |
| 281 | Layer.provide(CheckpointStoreTestLayer), | 276 | Layer.provide(layerCheckpointStoreTest), |
| 282 | Layer.provide(ServerConfigLayer), | 277 | Layer.provide(layerServerConfig), |
| 283 | Layer.provide(ServerSettings.layerTest()), | 278 | Layer.provide(ServerSettings.layerTest()), |
| 284 | Layer.provide(TestProviderInstanceRegistry), | 279 | Layer.provide(layerTestProviderInstanceRegistry), |
| 285 | Layer.provide(GitWorkflowTestLayer), | 280 | Layer.provide(layerGitWorkflowTest), |
| 286 | Layer.provide(ProjectServiceTestLayer), | 281 | Layer.provide(layerProjectServiceTest), |
| 287 | Layer.provide(PlatformTestLayer), | 282 | Layer.provide(layerPlatformTest), |
| 288 | ); | 283 | ); |
| 289 | 284 | ||
| 290 | const LegacyImportTestLayer = OrchestrationV2LayerLive.pipe( | 285 | const layerLegacyImportTest = RuntimeLayer.layer.pipe( |
| 291 | Layer.provide(McpSessionRegistryTestkit.layer), | 286 | Layer.provide(McpSessionRegistryTestkit.layer), |
| 292 | Layer.provideMerge(SqlitePersistenceMemory), | 287 | Layer.provideMerge(SqlitePersistence.layerMemory), |
| 293 | Layer.provide(CheckpointStoreTestLayer), | 288 | Layer.provide(layerCheckpointStoreTest), |
| 294 | Layer.provide(ServerConfigLayer), | 289 | Layer.provide(layerServerConfig), |
| 295 | Layer.provide(ServerSettings.layerTest()), | 290 | Layer.provide(ServerSettings.layerTest()), |
| 296 | Layer.provide(TestProviderInstanceRegistry), | 291 | Layer.provide(layerTestProviderInstanceRegistry), |
| 297 | Layer.provide(GitWorkflowTestLayer), | 292 | Layer.provide(layerGitWorkflowTest), |
| 298 | Layer.provide(ProjectServiceTestLayer), | 293 | Layer.provide(layerProjectServiceTest), |
| 299 | Layer.provide(PlatformTestLayer), | 294 | Layer.provide(layerPlatformTest), |
| 300 | ); | 295 | ); |
| 301 | 296 | ||
| 302 | const ProjectDeletionTestLayer = Layer.mergeAll( | 297 | const layerProjectDeletionTest = Layer.mergeAll( |
| 303 | OrchestrationV2LayerLive.pipe(Layer.provide(ProjectServiceLayerLive)), | 298 | RuntimeLayer.layer.pipe(Layer.provide(RuntimeLayer.layerProjectService)), |
| 304 | ProjectServiceLayerLive, | 299 | RuntimeLayer.layerProjectService, |
| 305 | OrchestrationV2EventSinkLayerLive, | 300 | RuntimeLayer.layerEventSink, |
| 306 | ThreadCommandExecutor.layer, | 301 | ThreadCommandExecutor.layer, |
| 307 | ).pipe( | 302 | ).pipe( |
| 308 | Layer.provide( | 303 | Layer.provide( |
| ... | @@ -328,16 +323,16 @@ const ProjectDeletionTestLayer = Layer.mergeAll( | ... | @@ -328,16 +323,16 @@ const ProjectDeletionTestLayer = Layer.mergeAll( |
| 328 | }), | 323 | }), |
| 329 | ), | 324 | ), |
| 330 | Layer.provide(McpSessionRegistryTestkit.layer), | 325 | Layer.provide(McpSessionRegistryTestkit.layer), |
| 331 | Layer.provide(SqlitePersistenceMemory), | 326 | Layer.provide(SqlitePersistence.layerMemory), |
| 332 | Layer.provide(CheckpointStoreTestLayer), | 327 | Layer.provide(layerCheckpointStoreTest), |
| 333 | Layer.provide(ServerConfigLayer), | 328 | Layer.provide(layerServerConfig), |
| 334 | Layer.provide(ServerSettings.layerTest()), | 329 | Layer.provide(ServerSettings.layerTest()), |
| 335 | Layer.provide(TestProviderInstanceRegistry), | 330 | Layer.provide(layerTestProviderInstanceRegistry), |
| 336 | Layer.provide(GitWorkflowTestLayer), | 331 | Layer.provide(layerGitWorkflowTest), |
| 337 | Layer.provide(PlatformTestLayer), | 332 | Layer.provide(layerPlatformTest), |
| 338 | ); | 333 | ); |
| 339 | 334 | ||
| 340 | it.layer(ProjectDeletionTestLayer)("project deletion during thread commands", (it) => { | 335 | it.layer(layerProjectDeletionTest)("project deletion during thread commands", (it) => { |
| 341 | it.effect("waits for an in-flight thread update before planning deletion", () => | 336 | it.effect("waits for an in-flight thread update before planning deletion", () => |
| 342 | Effect.gen(function* () { | 337 | Effect.gen(function* () { |
| 343 | const projects = yield* ProjectService.ProjectService; | 338 | const projects = yield* ProjectService.ProjectService; |
| ... | @@ -441,11 +436,11 @@ it.layer(ProjectDeletionTestLayer)("project deletion during thread commands", (i | ... | @@ -441,11 +436,11 @@ it.layer(ProjectDeletionTestLayer)("project deletion during thread commands", (i |
| 441 | ); | 436 | ); |
| 442 | }); | 437 | }); |
| 443 | 438 | ||
| 444 | const SharedApplicationDataPlaneTestLayer = Layer.mergeAll( | 439 | const layerSharedApplicationDataPlaneTest = Layer.mergeAll( |
| 445 | OrchestrationV2LayerLive.pipe(Layer.provide(ProjectServiceLayerLive)), | 440 | RuntimeLayer.layer.pipe(Layer.provide(RuntimeLayer.layerProjectService)), |
| 446 | ProjectServiceLayerLive, | 441 | RuntimeLayer.layerProjectService, |
| 447 | OrchestrationV2EventSinkLayerLive, | 442 | RuntimeLayer.layerEventSink, |
| 448 | OrchestrationEventInfrastructureLayerLive, | 443 | RuntimeLayer.layerEventInfrastructure, |
| 449 | ).pipe( | 444 | ).pipe( |
| 450 | Layer.provide( | 445 | Layer.provide( |
| 451 | Layer.mock(WorkspacePaths.WorkspacePaths)({ | 446 | Layer.mock(WorkspacePaths.WorkspacePaths)({ |
| ... | @@ -472,16 +467,16 @@ const SharedApplicationDataPlaneTestLayer = Layer.mergeAll( | ... | @@ -472,16 +467,16 @@ const SharedApplicationDataPlaneTestLayer = Layer.mergeAll( |
| 472 | }), | 467 | }), |
| 473 | ), | 468 | ), |
| 474 | Layer.provide(McpSessionRegistryTestkit.layer), | 469 | Layer.provide(McpSessionRegistryTestkit.layer), |
| 475 | Layer.provideMerge(SqlitePersistenceMemory), | 470 | Layer.provideMerge(SqlitePersistence.layerMemory), |
| 476 | Layer.provide(CheckpointStoreTestLayer), | 471 | Layer.provide(layerCheckpointStoreTest), |
| 477 | Layer.provide(ServerConfigLayer), | 472 | Layer.provide(layerServerConfig), |
| 478 | Layer.provide(ServerSettings.layerTest()), | 473 | Layer.provide(ServerSettings.layerTest()), |
| 479 | Layer.provide(TestProviderInstanceRegistry), | 474 | Layer.provide(layerTestProviderInstanceRegistry), |
| 480 | Layer.provide(GitWorkflowTestLayer), | 475 | Layer.provide(layerGitWorkflowTest), |
| 481 | Layer.provide(PlatformTestLayer), | 476 | Layer.provide(layerPlatformTest), |
| 482 | ); | 477 | ); |
| 483 | 478 | ||
| 484 | it.layer(TestLayer)("OrchestrationV2LayerLive", (it) => { | 479 | it.layer(layerTest)("OrchestrationV2LayerLive", (it) => { |
| 485 | it.effect("emits model updates separately from provider switches", () => | 480 | it.effect("emits model updates separately from provider switches", () => |
| 486 | Effect.gen(function* () { | 481 | Effect.gen(function* () { |
| 487 | const orchestrator = yield* Orchestrator.OrchestratorV2; | 482 | const orchestrator = yield* Orchestrator.OrchestratorV2; |
| ... | @@ -837,7 +832,7 @@ it.layer(TestLayer)("OrchestrationV2LayerLive", (it) => { | ... | @@ -837,7 +832,7 @@ it.layer(TestLayer)("OrchestrationV2LayerLive", (it) => { |
| 837 | assert.deepEqual(yield* outbox.listByCommandId(commandId), []); | 832 | assert.deepEqual(yield* outbox.listByCommandId(commandId), []); |
| 838 | } | 833 | } |
| 839 | } | 834 | } |
| 840 | }).pipe(Effect.provide(Layer.fresh(TestLayer))), | 835 | }).pipe(Effect.provide(Layer.fresh(layerTest))), |
| 841 | ); | 836 | ); |
| 842 | 837 | ||
| 843 | it.effect("resolves delivery intent against the active run and starts after it completes", () => | 838 | it.effect("resolves delivery intent against the active run and starts after it completes", () => |
| ... | @@ -1478,7 +1473,7 @@ it.layer(TestLayer)("OrchestrationV2LayerLive", (it) => { | ... | @@ -1478,7 +1473,7 @@ it.layer(TestLayer)("OrchestrationV2LayerLive", (it) => { |
| 1478 | ); | 1473 | ); |
| 1479 | }); | 1474 | }); |
| 1480 | 1475 | ||
| 1481 | it.layer(LegacyImportTestLayer)("OrchestrationV2 legacy import", (it) => { | 1476 | it.layer(layerLegacyImportTest)("OrchestrationV2 legacy import", (it) => { |
| 1482 | it.effect("hydrates imported transcripts before commands and propagates hydration failures", () => | 1477 | it.effect("hydrates imported transcripts before commands and propagates hydration failures", () => |
| 1483 | Effect.gen(function* () { | 1478 | Effect.gen(function* () { |
| 1484 | const sql = yield* SqlClient.SqlClient; | 1479 | const sql = yield* SqlClient.SqlClient; |
| ... | @@ -1678,7 +1673,7 @@ it.layer(LegacyImportTestLayer)("OrchestrationV2 legacy import", (it) => { | ... | @@ -1678,7 +1673,7 @@ it.layer(LegacyImportTestLayer)("OrchestrationV2 legacy import", (it) => { |
| 1678 | ); | 1673 | ); |
| 1679 | }); | 1674 | }); |
| 1680 | 1675 | ||
| 1681 | it.layer(TestLayer)("OrchestrationV2LayerLive lifecycle", (it) => { | 1676 | it.layer(layerTest)("OrchestrationV2LayerLive lifecycle", (it) => { |
| 1682 | it.effect("applies lifecycle commands idempotently and emits archive/removal shell deltas", () => | 1677 | it.effect("applies lifecycle commands idempotently and emits archive/removal shell deltas", () => |
| 1683 | Effect.gen(function* () { | 1678 | Effect.gen(function* () { |
| 1684 | const orchestrator = yield* Orchestrator.OrchestratorV2; | 1679 | const orchestrator = yield* Orchestrator.OrchestratorV2; |
| ... | @@ -4349,7 +4344,7 @@ it.layer(TestLayer)("OrchestrationV2LayerLive lifecycle", (it) => { | ... | @@ -4349,7 +4344,7 @@ it.layer(TestLayer)("OrchestrationV2LayerLive lifecycle", (it) => { |
| 4349 | ); | 4344 | ); |
| 4350 | }); | 4345 | }); |
| 4351 | 4346 | ||
| 4352 | it.layer(SharedApplicationDataPlaneTestLayer)("pending provider interruption", (it) => { | 4347 | it.layer(layerSharedApplicationDataPlaneTest)("pending provider interruption", (it) => { |
| 4353 | it.effect("interrupts a pending provider start without launching provider work", () => | 4348 | it.effect("interrupts a pending provider start without launching provider work", () => |
| 4354 | Effect.gen(function* () { | 4349 | Effect.gen(function* () { |
| 4355 | const projects = yield* ProjectService.ProjectService; | 4350 | const projects = yield* ProjectService.ProjectService; |
| ... | @@ -4423,7 +4418,7 @@ it.layer(SharedApplicationDataPlaneTestLayer)("pending provider interruption", ( | ... | @@ -4423,7 +4418,7 @@ it.layer(SharedApplicationDataPlaneTestLayer)("pending provider interruption", ( |
| 4423 | ); | 4418 | ); |
| 4424 | }); | 4419 | }); |
| 4425 | 4420 | ||
| 4426 | it.layer(SharedApplicationDataPlaneTestLayer)("snooze projection", (it) => { | 4421 | it.layer(layerSharedApplicationDataPlaneTest)("snooze projection", (it) => { |
| 4427 | it.effect("carries snooze state through the V2 shell projection", () => | 4422 | it.effect("carries snooze state through the V2 shell projection", () => |
| 4428 | Effect.gen(function* () { | 4423 | Effect.gen(function* () { |
| 4429 | const projects = yield* ProjectService.ProjectService; | 4424 | const projects = yield* ProjectService.ProjectService; |
| ... | @@ -4498,7 +4493,7 @@ it.layer(SharedApplicationDataPlaneTestLayer)("snooze projection", (it) => { | ... | @@ -4498,7 +4493,7 @@ it.layer(SharedApplicationDataPlaneTestLayer)("snooze projection", (it) => { |
| 4498 | ); | 4493 | ); |
| 4499 | }); | 4494 | }); |
| 4500 | 4495 | ||
| 4501 | it.layer(SharedApplicationDataPlaneTestLayer)("visited projection", (it) => { | 4496 | it.layer(layerSharedApplicationDataPlaneTest)("visited projection", (it) => { |
| 4502 | it.effect("carries the visited watermark through the V2 shell projection", () => | 4497 | it.effect("carries the visited watermark through the V2 shell projection", () => |
| 4503 | Effect.gen(function* () { | 4498 | Effect.gen(function* () { |
| 4504 | const projects = yield* ProjectService.ProjectService; | 4499 | const projects = yield* ProjectService.ProjectService; |
| ... | @@ -4606,7 +4601,7 @@ it.layer(SharedApplicationDataPlaneTestLayer)("visited projection", (it) => { | ... | @@ -4606,7 +4601,7 @@ it.layer(SharedApplicationDataPlaneTestLayer)("visited projection", (it) => { |
| 4606 | ); | 4601 | ); |
| 4607 | }); | 4602 | }); |
| 4608 | 4603 | ||
| 4609 | it.layer(SharedApplicationDataPlaneTestLayer)("shared application data plane", (it) => { | 4604 | it.layer(layerSharedApplicationDataPlaneTest)("shared application data plane", (it) => { |
| 4610 | it.effect("orders retained project transactions and V2 thread transactions in one source", () => | 4605 | it.effect("orders retained project transactions and V2 thread transactions in one source", () => |
| 4611 | Effect.gen(function* () { | 4606 | Effect.gen(function* () { |
| 4612 | const projects = yield* ProjectService.ProjectService; | 4607 | const projects = yield* ProjectService.ProjectService; |
| ... | @@ -4681,7 +4676,7 @@ it.layer(SharedApplicationDataPlaneTestLayer)("shared application data plane", ( | ... | @@ -4681,7 +4676,7 @@ it.layer(SharedApplicationDataPlaneTestLayer)("shared application data plane", ( |
| 4681 | ); | 4676 | ); |
| 4682 | }); | 4677 | }); |
| 4683 | 4678 | ||
| 4684 | it.layer(TestLayer)("usage-limit recovery", (it) => { | 4679 | it.layer(layerTest)("usage-limit recovery", (it) => { |
| 4685 | it.effect.each(["interrupted", "usage_limit"] as const)( | 4680 | it.effect.each(["interrupted", "usage_limit"] as const)( |
| 4686 | "manually resumes an %s run ahead of its queued message only once", | 4681 | "manually resumes an %s run ahead of its queued message only once", |
| 4687 | (reason) => | 4682 | (reason) => |
apps/server/src/orchestration-v2/runtimeLayer.ts+150-138| ... | @@ -5,7 +5,7 @@ import * as OrchestrationCommandReceipts from "../persistence/Layers/Orchestrati | ... | @@ -5,7 +5,7 @@ import * as OrchestrationCommandReceipts from "../persistence/Layers/Orchestrati |
| 5 | import * as OrchestrationEventStore from "../persistence/Layers/OrchestrationEventStore.ts"; | 5 | import * as OrchestrationEventStore from "../persistence/Layers/OrchestrationEventStore.ts"; |
| 6 | import * as ProviderSessionRuntime from "../persistence/ProviderSessionRuntime.ts"; | 6 | import * as ProviderSessionRuntime from "../persistence/ProviderSessionRuntime.ts"; |
| 7 | import * as TextGeneration from "../textGeneration/TextGeneration.ts"; | 7 | import * as TextGeneration from "../textGeneration/TextGeneration.ts"; |
| 8 | import { ProviderAuthServiceLive } from "../provider/Layers/ProviderAuthService.ts"; | 8 | import * as ProviderAuthService from "../provider/Layers/ProviderAuthService.ts"; |
| 9 | import * as AgentSessionImporter from "../project/AgentSessionImporter.ts"; | 9 | import * as AgentSessionImporter from "../project/AgentSessionImporter.ts"; |
| 10 | import * as AgentSessionScanner from "../project/AgentSessionScanner.ts"; | 10 | import * as AgentSessionScanner from "../project/AgentSessionScanner.ts"; |
| 11 | import * as ProjectService from "../project/ProjectService.ts"; | 11 | import * as ProjectService from "../project/ProjectService.ts"; |
| ... | @@ -51,57 +51,57 @@ import * as ScheduledTaskService from "../scheduledTasks/ScheduledTaskService.ts | ... | @@ -51,57 +51,57 @@ import * as ScheduledTaskService from "../scheduledTasks/ScheduledTaskService.ts |
| 51 | import * as SecretRequests from "../secrets/SecretRequests.ts"; | 51 | import * as SecretRequests from "../secrets/SecretRequests.ts"; |
| 52 | 52 | ||
| 53 | /** The shared application event log and its command receipts. */ | 53 | /** The shared application event log and its command receipts. */ |
| 54 | export const OrchestrationEventInfrastructureLayerLive = Layer.mergeAll( | 54 | export const layerEventInfrastructure = Layer.mergeAll( |
| 55 | OrchestrationEventStore.OrchestrationEventStoreLive, | 55 | OrchestrationEventStore.layer, |
| 56 | OrchestrationCommandReceipts.OrchestrationCommandReceiptRepositoryLive, | 56 | OrchestrationCommandReceipts.layer, |
| 57 | ); | 57 | ); |
| 58 | 58 | ||
| 59 | const runtimePolicyProvided = RuntimePolicy.layerFromProjectStore.pipe( | 59 | const layerRuntimePolicyProvided = RuntimePolicy.layerFromProjectStore.pipe( |
| 60 | Layer.provide(ProjectStore.layer), | 60 | Layer.provide(ProjectStore.layer), |
| 61 | ); | 61 | ); |
| 62 | 62 | ||
| 63 | const eventStoreProvided = EventStore.layerFromOrchestrationEventStore.pipe( | 63 | const layerEventStoreProvided = EventStore.layerFromOrchestrationEventStore.pipe( |
| 64 | Layer.provide(OrchestrationEventInfrastructureLayerLive), | 64 | Layer.provide(layerEventInfrastructure), |
| 65 | ); | 65 | ); |
| 66 | const commandReceiptStoreProvided = CommandReceiptStore.layerFromApplicationReceipts.pipe( | 66 | const layerCommandReceiptStoreProvided = CommandReceiptStore.layerFromApplicationReceipts.pipe( |
| 67 | Layer.provide(OrchestrationEventInfrastructureLayerLive), | 67 | Layer.provide(layerEventInfrastructure), |
| 68 | ); | 68 | ); |
| 69 | 69 | ||
| 70 | const storesLayer = Layer.mergeAll( | 70 | const layerStores = Layer.mergeAll( |
| 71 | OrchestrationEventInfrastructureLayerLive, | 71 | layerEventInfrastructure, |
| 72 | eventStoreProvided, | 72 | layerEventStoreProvided, |
| 73 | ProjectionStore.layer, | 73 | ProjectionStore.layer, |
| 74 | ProjectStore.layer, | 74 | ProjectStore.layer, |
| 75 | commandReceiptStoreProvided, | 75 | layerCommandReceiptStoreProvided, |
| 76 | EffectOutbox.layer, | 76 | EffectOutbox.layer, |
| 77 | TurnItemPositionStore.layer, | 77 | TurnItemPositionStore.layer, |
| 78 | ); | 78 | ); |
| 79 | 79 | ||
| 80 | export const OrchestrationV2EventSinkLayerLive = EventSink.layerFromStores.pipe( | 80 | export const layerEventSink = EventSink.layerFromStores.pipe(Layer.provide(layerStores)); |
| 81 | Layer.provide(storesLayer), | 81 | const layerEventSinkProvided = layerEventSink; |
| 82 | const layerProjectionMaintenanceProvided = ProjectionMaintenance.layer.pipe( | ||
| 83 | Layer.provide(layerStores), | ||
| 82 | ); | 84 | ); |
| 83 | const eventSinkProvided = OrchestrationV2EventSinkLayerLive; | 85 | const layerLegacyV1ThreadImporterProvided = LegacyV1ThreadImporter.layer.pipe( |
| 84 | const projectionMaintenanceProvided = ProjectionMaintenance.layer.pipe(Layer.provide(storesLayer)); | 86 | Layer.provide(layerEventSinkProvided), |
| 85 | const legacyV1ThreadImporterProvided = LegacyV1ThreadImporter.layer.pipe( | ||
| 86 | Layer.provide(eventSinkProvided), | ||
| 87 | ); | 87 | ); |
| 88 | 88 | ||
| 89 | export const ProjectServiceLayerLive = ProjectService.layer.pipe( | 89 | export const layerProjectService = ProjectService.layer.pipe( |
| 90 | Layer.provide( | 90 | Layer.provide( |
| 91 | Layer.mergeAll( | 91 | Layer.mergeAll( |
| 92 | ProjectStore.layer, | 92 | ProjectStore.layer, |
| 93 | ProjectionStore.layer, | 93 | ProjectionStore.layer, |
| 94 | eventSinkProvided, | 94 | layerEventSinkProvided, |
| 95 | IdAllocator.layer, | 95 | IdAllocator.layer, |
| 96 | legacyV1ThreadImporterProvided, | 96 | layerLegacyV1ThreadImporterProvided, |
| 97 | ), | 97 | ), |
| 98 | ), | 98 | ), |
| 99 | ); | 99 | ); |
| 100 | 100 | ||
| 101 | const providerEventIngestorProvided = ProviderEventIngestor.layer.pipe( | 101 | const layerProviderEventIngestorProvided = ProviderEventIngestor.layer.pipe( |
| 102 | Layer.provide( | 102 | Layer.provide( |
| 103 | Layer.mergeAll( | 103 | Layer.mergeAll( |
| 104 | eventSinkProvided, | 104 | layerEventSinkProvided, |
| 105 | IdAllocator.layer, | 105 | IdAllocator.layer, |
| 106 | ProjectionStore.layer, | 106 | ProjectionStore.layer, |
| 107 | ThreadCommandExecutor.layer, | 107 | ThreadCommandExecutor.layer, |
| ... | @@ -109,222 +109,234 @@ const providerEventIngestorProvided = ProviderEventIngestor.layer.pipe( | ... | @@ -109,222 +109,234 @@ const providerEventIngestorProvided = ProviderEventIngestor.layer.pipe( |
| 109 | ), | 109 | ), |
| 110 | ); | 110 | ); |
| 111 | 111 | ||
| 112 | const checkpointServiceProvided = CheckpointService.layer.pipe(Layer.provide(IdAllocator.layer)); | 112 | const layerCheckpointServiceProvided = CheckpointService.layer.pipe( |
| 113 | const contextHandoffServiceProvided = ContextHandoffService.layer.pipe( | 113 | Layer.provide(IdAllocator.layer), |
| 114 | ); | ||
| 115 | const layerContextHandoffServiceProvided = ContextHandoffService.layer.pipe( | ||
| 114 | Layer.provide(IdAllocator.layer), | 116 | Layer.provide(IdAllocator.layer), |
| 115 | ); | 117 | ); |
| 116 | 118 | ||
| 117 | const providerAdapterRegistryProvided = ProviderAdapterRegistry.layerFromProviderInstanceRegistry; | 119 | const layerProviderAdapterRegistryProvided = |
| 118 | const providerSwitchServiceProvided = ProviderSwitchService.layer.pipe( | 120 | ProviderAdapterRegistry.layerFromProviderInstanceRegistry; |
| 119 | Layer.provide(providerAdapterRegistryProvided), | 121 | const layerProviderSwitchServiceProvided = ProviderSwitchService.layer.pipe( |
| 122 | Layer.provide(layerProviderAdapterRegistryProvided), | ||
| 120 | ); | 123 | ); |
| 121 | 124 | ||
| 122 | const providerSessionManagerProvided = ProviderSessionManager.layer.pipe( | 125 | const layerProviderSessionManagerProvided = ProviderSessionManager.layer.pipe( |
| 123 | Layer.provide( | 126 | Layer.provide( |
| 124 | Layer.mergeAll( | 127 | Layer.mergeAll( |
| 125 | providerAdapterRegistryProvided, | 128 | layerProviderAdapterRegistryProvided, |
| 126 | eventSinkProvided, | 129 | layerEventSinkProvided, |
| 127 | IdAllocator.layer, | 130 | IdAllocator.layer, |
| 128 | providerEventIngestorProvided, | 131 | layerProviderEventIngestorProvided, |
| 129 | ProjectionStore.layer, | 132 | ProjectionStore.layer, |
| 130 | ), | 133 | ), |
| 131 | ), | 134 | ), |
| 132 | ); | 135 | ); |
| 133 | 136 | ||
| 134 | const providerAuthServiceProvided = ProviderAuthServiceLive.pipe( | 137 | const layerProviderAuthServiceProvided = ProviderAuthService.layer.pipe( |
| 135 | Layer.provide(Layer.merge(ProjectionStore.layer, providerSessionManagerProvided)), | 138 | Layer.provide(Layer.merge(ProjectionStore.layer, layerProviderSessionManagerProvided)), |
| 136 | ); | 139 | ); |
| 137 | 140 | ||
| 138 | const runExecutionServiceProvided = RunExecutionService.layer.pipe( | 141 | const layerRunExecutionServiceProvided = RunExecutionService.layer.pipe( |
| 139 | Layer.provide( | 142 | Layer.provide( |
| 140 | Layer.mergeAll( | 143 | Layer.mergeAll( |
| 141 | checkpointServiceProvided, | 144 | layerCheckpointServiceProvided, |
| 142 | eventSinkProvided, | 145 | layerEventSinkProvided, |
| 143 | IdAllocator.layer, | 146 | IdAllocator.layer, |
| 144 | providerEventIngestorProvided, | 147 | layerProviderEventIngestorProvided, |
| 145 | ), | 148 | ), |
| 146 | ), | 149 | ), |
| 147 | ); | 150 | ); |
| 148 | 151 | ||
| 149 | const providerTurnStartServiceProvided = ProviderTurnStartService.layer.pipe( | 152 | const layerProviderTurnStartServiceProvided = ProviderTurnStartService.layer.pipe( |
| 150 | Layer.provide( | 153 | Layer.provide( |
| 151 | Layer.mergeAll( | 154 | Layer.mergeAll( |
| 152 | contextHandoffServiceProvided, | 155 | layerContextHandoffServiceProvided, |
| 153 | eventSinkProvided, | 156 | layerEventSinkProvided, |
| 154 | IdAllocator.layer, | 157 | IdAllocator.layer, |
| 155 | ProjectionStore.layer, | 158 | ProjectionStore.layer, |
| 156 | providerSessionManagerProvided, | 159 | layerProviderSessionManagerProvided, |
| 157 | providerAuthServiceProvided, | 160 | layerProviderAuthServiceProvided, |
| 158 | runExecutionServiceProvided, | 161 | layerRunExecutionServiceProvided, |
| 159 | runtimePolicyProvided, | 162 | layerRuntimePolicyProvided, |
| 160 | ), | 163 | ), |
| 161 | ), | 164 | ), |
| 162 | ); | 165 | ); |
| 163 | 166 | ||
| 164 | const providerTurnControlServiceProvided = ProviderTurnControlService.layer.pipe( | 167 | const layerProviderTurnControlServiceProvided = ProviderTurnControlService.layer.pipe( |
| 165 | Layer.provide(Layer.merge(ProjectionStore.layer, providerSessionManagerProvided)), | 168 | Layer.provide(Layer.merge(ProjectionStore.layer, layerProviderSessionManagerProvided)), |
| 166 | ); | 169 | ); |
| 167 | const runtimeRequestServiceProvided = RuntimeRequestService.layer.pipe( | 170 | const layerRuntimeRequestServiceProvided = RuntimeRequestService.layer.pipe( |
| 168 | Layer.provide(Layer.merge(ProjectionStore.layer, providerSessionManagerProvided)), | 171 | Layer.provide(Layer.merge(ProjectionStore.layer, layerProviderSessionManagerProvided)), |
| 169 | ); | 172 | ); |
| 170 | const checkpointRollbackServiceProvided = CheckpointRollbackService.layer.pipe( | 173 | const layerCheckpointRollbackServiceProvided = CheckpointRollbackService.layer.pipe( |
| 171 | Layer.provide( | 174 | Layer.provide( |
| 172 | Layer.mergeAll( | 175 | Layer.mergeAll( |
| 173 | ProjectStore.layer, | 176 | ProjectStore.layer, |
| 174 | checkpointServiceProvided, | 177 | layerCheckpointServiceProvided, |
| 175 | eventSinkProvided, | 178 | layerEventSinkProvided, |
| 176 | IdAllocator.layer, | 179 | IdAllocator.layer, |
| 177 | ProjectionStore.layer, | 180 | ProjectionStore.layer, |
| 178 | providerSessionManagerProvided, | 181 | layerProviderSessionManagerProvided, |
| 179 | runtimePolicyProvided, | 182 | layerRuntimePolicyProvided, |
| 180 | ), | 183 | ), |
| 181 | ), | 184 | ), |
| 182 | ); | 185 | ); |
| 183 | const checkpointCaptureServiceProvided = CheckpointCaptureService.layer.pipe( | 186 | const layerCheckpointCaptureServiceProvided = CheckpointCaptureService.layer.pipe( |
| 184 | Layer.provide( | 187 | Layer.provide( |
| 185 | Layer.mergeAll( | 188 | Layer.mergeAll( |
| 186 | checkpointServiceProvided, | 189 | layerCheckpointServiceProvided, |
| 187 | eventSinkProvided, | 190 | layerEventSinkProvided, |
| 188 | IdAllocator.layer, | 191 | IdAllocator.layer, |
| 189 | ProjectionStore.layer, | 192 | ProjectionStore.layer, |
| 190 | ), | 193 | ), |
| 191 | ), | 194 | ), |
| 192 | ); | 195 | ); |
| 193 | const runFinalizationServiceProvided = RunFinalizationService.layer.pipe( | 196 | const layerRunFinalizationServiceProvided = RunFinalizationService.layer.pipe( |
| 194 | Layer.provide(Layer.merge(checkpointCaptureServiceProvided, ProjectionStore.layer)), | 197 | Layer.provide(Layer.merge(layerCheckpointCaptureServiceProvided, ProjectionStore.layer)), |
| 195 | ); | 198 | ); |
| 196 | 199 | ||
| 197 | const orchestratorProvided = Orchestrator.layer.pipe( | 200 | const layerOrchestratorProvided = Orchestrator.layer.pipe( |
| 198 | Layer.provide( | 201 | Layer.provide( |
| 199 | Layer.mergeAll( | 202 | Layer.mergeAll( |
| 200 | checkpointServiceProvided, | 203 | layerCheckpointServiceProvided, |
| 201 | CommandPolicy.layer, | 204 | CommandPolicy.layer, |
| 202 | storesLayer, | 205 | layerStores, |
| 203 | eventSinkProvided, | 206 | layerEventSinkProvided, |
| 204 | commandReceiptStoreProvided, | 207 | layerCommandReceiptStoreProvided, |
| 205 | contextHandoffServiceProvided, | 208 | layerContextHandoffServiceProvided, |
| 206 | IdAllocator.layer, | 209 | IdAllocator.layer, |
| 207 | ProjectStore.layer, | 210 | ProjectStore.layer, |
| 208 | providerAdapterRegistryProvided, | 211 | layerProviderAdapterRegistryProvided, |
| 209 | // Same layer reference as the continuation worker and the adapter | 212 | // Same layer reference as the continuation worker and the adapter |
| 210 | // infrastructure so layer memoization yields one shared request queue. | 213 | // infrastructure so layer memoization yields one shared request queue. |
| 211 | ProviderContinuationRequests.layer, | 214 | ProviderContinuationRequests.layer, |
| 212 | providerEventIngestorProvided, | 215 | layerProviderEventIngestorProvided, |
| 213 | runtimePolicyProvided, | 216 | layerRuntimePolicyProvided, |
| 214 | providerSessionManagerProvided, | 217 | layerProviderSessionManagerProvided, |
| 215 | providerSwitchServiceProvided, | 218 | layerProviderSwitchServiceProvided, |
| 216 | runExecutionServiceProvided, | 219 | layerRunExecutionServiceProvided, |
| 217 | ThreadForkService.layer, | 220 | ThreadForkService.layer, |
| 218 | ), | 221 | ), |
| 219 | ), | 222 | ), |
| 220 | ); | 223 | ); |
| 221 | 224 | ||
| 222 | const agentSessionImporterProvided = AgentSessionImporter.layer.pipe( | 225 | const layerAgentSessionImporterProvided = AgentSessionImporter.layer.pipe( |
| 223 | Layer.provide( | 226 | Layer.provide( |
| 224 | Layer.mergeAll( | 227 | Layer.mergeAll( |
| 225 | AgentSessionScanner.layer, | 228 | AgentSessionScanner.layer, |
| 226 | ProjectServiceLayerLive, | 229 | layerProjectService, |
| 227 | orchestratorProvided, | 230 | layerOrchestratorProvided, |
| 228 | eventSinkProvided, | 231 | layerEventSinkProvided, |
| 229 | IdAllocator.layer, | 232 | IdAllocator.layer, |
| 230 | ProviderSessionRuntime.layer, | 233 | ProviderSessionRuntime.layer, |
| 231 | ), | 234 | ), |
| 232 | ), | 235 | ), |
| 233 | ); | 236 | ); |
| 234 | 237 | ||
| 235 | const threadManagementProvided = ThreadManagementService.layerWithLegacyImporter.pipe( | 238 | const layerThreadManagementProvided = ThreadManagementService.layerWithLegacyImporter.pipe( |
| 236 | Layer.provide(Layer.merge(orchestratorProvided, legacyV1ThreadImporterProvided)), | 239 | Layer.provide(Layer.merge(layerOrchestratorProvided, layerLegacyV1ThreadImporterProvided)), |
| 237 | ); | 240 | ); |
| 238 | export const ProjectSetupScriptRunnerLayerLive = ProjectSetupScriptRunner.layer.pipe( | 241 | export const layerProjectSetupScriptRunner = ProjectSetupScriptRunner.layer.pipe( |
| 239 | Layer.provide(ProjectServiceLayerLive), | 242 | Layer.provide(layerProjectService), |
| 240 | ); | 243 | ); |
| 241 | const managedProjectFoldersProvided = ManagedProjectFolders.layer.pipe( | 244 | const layerManagedProjectFoldersProvided = ManagedProjectFolders.layer.pipe( |
| 242 | Layer.provide(ProjectServiceLayerLive), | 245 | Layer.provide(layerProjectService), |
| 243 | ); | 246 | ); |
| 244 | const threadLaunchProvided = ThreadLaunchService.layer.pipe( | 247 | const layerThreadLaunchProvided = ThreadLaunchService.layer.pipe( |
| 245 | Layer.provide( | 248 | Layer.provide( |
| 246 | Layer.mergeAll( | 249 | Layer.mergeAll( |
| 247 | ProjectServiceLayerLive, | 250 | layerProjectService, |
| 248 | ProjectSetupScriptRunnerLayerLive, | 251 | layerProjectSetupScriptRunner, |
| 249 | managedProjectFoldersProvided, | 252 | layerManagedProjectFoldersProvided, |
| 250 | threadManagementProvided, | 253 | layerThreadManagementProvided, |
| 251 | commandReceiptStoreProvided, | 254 | layerCommandReceiptStoreProvided, |
| 252 | IdAllocator.layer, | 255 | IdAllocator.layer, |
| 253 | ), | 256 | ), |
| 254 | ), | 257 | ), |
| 255 | ); | 258 | ); |
| 256 | const threadLifecycleProvided = ThreadLifecycleService.layer.pipe( | 259 | const layerThreadLifecycleProvided = ThreadLifecycleService.layer.pipe( |
| 257 | Layer.provide(threadManagementProvided), | 260 | Layer.provide(layerThreadManagementProvided), |
| 261 | ); | ||
| 262 | const layerSecretRequestsProvided = SecretRequests.layer.pipe( | ||
| 263 | Layer.provide(layerThreadManagementProvided), | ||
| 258 | ); | 264 | ); |
| 259 | const secretRequestsProvided = SecretRequests.layer.pipe(Layer.provide(threadManagementProvided)); | 265 | const layerScheduledTaskProvided = ScheduledTaskService.layer.pipe( |
| 260 | const scheduledTaskProvided = ScheduledTaskService.layer.pipe( | ||
| 261 | Layer.provide( | 266 | Layer.provide( |
| 262 | Layer.mergeAll(threadLaunchProvided, threadManagementProvided, secretRequestsProvided), | 267 | Layer.mergeAll( |
| 268 | layerThreadLaunchProvided, | ||
| 269 | layerThreadManagementProvided, | ||
| 270 | layerSecretRequestsProvided, | ||
| 271 | ), | ||
| 263 | ), | 272 | ), |
| 264 | ); | 273 | ); |
| 265 | const providerContinuationWorkerProvided = ProviderContinuationService.workerLive.pipe( | 274 | const layerProviderContinuationWorkerProvided = ProviderContinuationService.layer.pipe( |
| 266 | Layer.provide( | 275 | Layer.provide( |
| 267 | Layer.mergeAll(ProviderContinuationRequests.layer, threadManagementProvided, IdAllocator.layer), | 276 | Layer.mergeAll( |
| 277 | ProviderContinuationRequests.layer, | ||
| 278 | layerThreadManagementProvided, | ||
| 279 | IdAllocator.layer, | ||
| 280 | ), | ||
| 268 | ), | 281 | ), |
| 269 | ); | 282 | ); |
| 270 | const threadTitleRegenerationProvided = ThreadTitleRegenerationService.layer.pipe( | 283 | const layerThreadTitleRegenerationProvided = ThreadTitleRegenerationService.layer.pipe( |
| 271 | Layer.provide(Layer.mergeAll(threadManagementProvided, ProjectStore.layer, TextGeneration.layer)), | 284 | Layer.provide( |
| 285 | Layer.mergeAll(layerThreadManagementProvided, ProjectStore.layer, TextGeneration.layer), | ||
| 286 | ), | ||
| 272 | ); | 287 | ); |
| 273 | const effectExecutorProvided = EffectWorker.executorLayer.pipe( | 288 | const layerEffectExecutorProvided = EffectWorker.layerExecutor.pipe( |
| 274 | Layer.provide( | 289 | Layer.provide( |
| 275 | Layer.mergeAll( | 290 | Layer.mergeAll( |
| 276 | runFinalizationServiceProvided, | 291 | layerRunFinalizationServiceProvided, |
| 277 | checkpointRollbackServiceProvided, | 292 | layerCheckpointRollbackServiceProvided, |
| 278 | providerSessionManagerProvided, | 293 | layerProviderSessionManagerProvided, |
| 279 | providerTurnControlServiceProvided, | 294 | layerProviderTurnControlServiceProvided, |
| 280 | providerTurnStartServiceProvided, | 295 | layerProviderTurnStartServiceProvided, |
| 281 | runtimeRequestServiceProvided, | 296 | layerRuntimeRequestServiceProvided, |
| 282 | threadTitleRegenerationProvided, | 297 | layerThreadTitleRegenerationProvided, |
| 283 | threadManagementProvided, | 298 | layerThreadManagementProvided, |
| 284 | ), | 299 | ), |
| 285 | ), | 300 | ), |
| 286 | ); | 301 | ); |
| 287 | const effectWorkerProvided = EffectWorker.layer.pipe( | 302 | const layerEffectWorkerProvided = EffectWorker.layer.pipe( |
| 288 | Layer.provide(Layer.merge(storesLayer, effectExecutorProvided)), | 303 | Layer.provide(Layer.merge(layerStores, layerEffectExecutorProvided)), |
| 289 | ); | 304 | ); |
| 290 | const providerRuntimeRecoveryProvided = ProviderRuntimeRecoveryService.layer.pipe( | 305 | const layerProviderRuntimeRecoveryProvided = ProviderRuntimeRecoveryService.layer.pipe( |
| 291 | Layer.provide( | 306 | Layer.provide( |
| 292 | Layer.mergeAll( | 307 | Layer.mergeAll( |
| 293 | effectWorkerProvided, | 308 | layerEffectWorkerProvided, |
| 294 | storesLayer, | 309 | layerStores, |
| 295 | eventSinkProvided, | 310 | layerEventSinkProvided, |
| 296 | IdAllocator.layer, | 311 | IdAllocator.layer, |
| 297 | ProjectionStore.layer, | 312 | ProjectionStore.layer, |
| 298 | ), | 313 | ), |
| 299 | ), | 314 | ), |
| 300 | ); | 315 | ); |
| 301 | 316 | ||
| 302 | export const OrchestrationV2LayerLive = Layer.mergeAll( | 317 | export const layer = Layer.mergeAll( |
| 303 | orchestratorProvided, | 318 | layerOrchestratorProvided, |
| 304 | threadManagementProvided, | 319 | layerThreadManagementProvided, |
| 305 | effectWorkerProvided, | 320 | layerEffectWorkerProvided, |
| 306 | providerSessionManagerProvided, | 321 | layerProviderSessionManagerProvided, |
| 307 | providerAuthServiceProvided, | 322 | layerProviderAuthServiceProvided, |
| 308 | providerRuntimeRecoveryProvided, | 323 | layerProviderRuntimeRecoveryProvided, |
| 309 | projectionMaintenanceProvided, | 324 | layerProjectionMaintenanceProvided, |
| 310 | legacyV1ThreadImporterProvided, | 325 | layerLegacyV1ThreadImporterProvided, |
| 311 | ); | 326 | ); |
| 312 | 327 | ||
| 313 | export const OrchestrationV2ProductionLayerLive = Layer.mergeAll( | 328 | export const layerProduction = Layer.mergeAll( |
| 314 | OrchestrationV2LayerLive.pipe(Layer.provide(ProjectServiceLayerLive)), | 329 | layer.pipe(Layer.provide(layerProjectService)), |
| 315 | ProjectServiceLayerLive, | 330 | layerProjectService, |
| 316 | managedProjectFoldersProvided, | 331 | layerManagedProjectFoldersProvided, |
| 317 | threadLaunchProvided, | 332 | layerThreadLaunchProvided, |
| 318 | threadLifecycleProvided, | 333 | layerThreadLifecycleProvided, |
| 319 | scheduledTaskProvided, | 334 | layerScheduledTaskProvided, |
| 320 | secretRequestsProvided, | 335 | layerSecretRequestsProvided, |
| 321 | UsageLimitRecoveryWorker.workerLive.pipe( | 336 | UsageLimitRecoveryWorker.layer.pipe( |
| 322 | Layer.provide(Layer.mergeAll(ProjectionStore.layer, threadManagementProvided)), | 337 | Layer.provide(Layer.mergeAll(ProjectionStore.layer, layerThreadManagementProvided)), |
| 323 | ), | 338 | ), |
| 324 | providerContinuationWorkerProvided, | 339 | layerProviderContinuationWorkerProvided, |
| 325 | agentSessionImporterProvided, | 340 | layerAgentSessionImporterProvided, |
| 326 | EffectOutbox.pruneWorkerLive.pipe(Layer.provide(EffectOutbox.layer)), | 341 | EffectOutbox.layerPruneWorker.pipe(Layer.provide(EffectOutbox.layer)), |
| 327 | ).pipe( | 342 | ).pipe(Layer.provide(Scheduler.layer), Layer.provideMerge(layerEventInfrastructure)); |
| 328 | Layer.provide(Scheduler.layer), | ||
| 329 | Layer.provideMerge(OrchestrationEventInfrastructureLayerLive), | ||
| 330 | ); |
apps/server/src/orchestration-v2/testkit/ClaudeReplayFixtures.integration.test.ts+2-2| ... | @@ -18,7 +18,7 @@ import { provideDeterministicTestRuntime } from "./DeterministicRuntime.ts"; | ... | @@ -18,7 +18,7 @@ import { provideDeterministicTestRuntime } from "./DeterministicRuntime.ts"; |
| 18 | import { ORCHESTRATOR_REPLAY_FIXTURES } from "./fixtures/index.ts"; | 18 | import { ORCHESTRATOR_REPLAY_FIXTURES } from "./fixtures/index.ts"; |
| 19 | import { subagentInput } from "./fixtures/subagent/input.ts"; | 19 | import { subagentInput } from "./fixtures/subagent/input.ts"; |
| 20 | import { runOrchestratorV2Scenario } from "./OrchestratorScenario.ts"; | 20 | import { runOrchestratorV2Scenario } from "./OrchestratorScenario.ts"; |
| 21 | import { makeOrchestratorV2ProviderReplayLayer } from "./ProviderReplayHarness.ts"; | 21 | import * as ProviderReplayHarness from "./ProviderReplayHarness.ts"; |
| 22 | import { materializeReplayTranscriptRuntimeInstructions } from "./ReplayTranscriptNdjson.ts"; | 22 | import { materializeReplayTranscriptRuntimeInstructions } from "./ReplayTranscriptNdjson.ts"; |
| 23 | import { CLAUDE_MODEL_SELECTION, materializeFixtureInput } from "./fixtures/shared.ts"; | 23 | import { CLAUDE_MODEL_SELECTION, materializeFixtureInput } from "./fixtures/shared.ts"; |
| 24 | import { | 24 | import { |
| ... | @@ -188,7 +188,7 @@ describe("Claude Agent SDK replay fixtures", () => { | ... | @@ -188,7 +188,7 @@ describe("Claude Agent SDK replay fixtures", () => { |
| 188 | assert.deepEqual(after.messages, child.messages); | 188 | assert.deepEqual(after.messages, child.messages); |
| 189 | }).pipe( | 189 | }).pipe( |
| 190 | Effect.provide( | 190 | Effect.provide( |
| 191 | makeOrchestratorV2ProviderReplayLayer(scenario, ClaudeOrchestratorReplayHarness), | 191 | ProviderReplayHarness.layerProviderReplay(scenario, ClaudeOrchestratorReplayHarness), |
| 192 | ), | 192 | ), |
| 193 | provideDeterministicTestRuntime, | 193 | provideDeterministicTestRuntime, |
| 194 | Effect.scoped, | 194 | Effect.scoped, |
apps/server/src/orchestration-v2/testkit/OrchestratorReplayRecovery.integration.test.ts+9-11| ... | @@ -12,18 +12,16 @@ import { | ... | @@ -12,18 +12,16 @@ import { |
| 12 | ClaudeOrchestratorReplayHarness, | 12 | ClaudeOrchestratorReplayHarness, |
| 13 | makeClaudeRestartReplayHarness, | 13 | makeClaudeRestartReplayHarness, |
| 14 | } from "../Adapters/ClaudeAdapterV2.testkit.ts"; | 14 | } from "../Adapters/ClaudeAdapterV2.testkit.ts"; |
| 15 | import { | 15 | import { CodexOrchestratorReplayHarness } from "../Adapters/CodexAdapterV2.testkit.ts"; |
| 16 | CodexOrchestratorReplayHarness, | 16 | import * as CodexAdapterV2Testkit from "../Adapters/CodexAdapterV2.testkit.ts"; |
| 17 | makeCodexProviderAdapterRegistryReplayLayer, | ||
| 18 | } from "../Adapters/CodexAdapterV2.testkit.ts"; | ||
| 19 | import { | 17 | import { |
| 20 | type CursorAgentSdkReplayTranscript, | 18 | type CursorAgentSdkReplayTranscript, |
| 21 | CursorOrchestratorReplayHarness, | 19 | CursorOrchestratorReplayHarness, |
| 22 | makeCursorAgentSdkReplayRunner, | 20 | makeCursorAgentSdkReplayRunner, |
| 23 | makeCursorProviderAdapterRegistryReplayLayer, | ||
| 24 | } from "../Adapters/CursorAdapterV2.testkit.ts"; | 21 | } from "../Adapters/CursorAdapterV2.testkit.ts"; |
| 22 | import * as CursorAdapterV2Testkit from "../Adapters/CursorAdapterV2.testkit.ts"; | ||
| 25 | import * as IdAllocator from "../IdAllocator.ts"; | 23 | import * as IdAllocator from "../IdAllocator.ts"; |
| 26 | import { makeSqlitePersistenceLive } from "../../persistence/Layers/Sqlite.ts"; | 24 | import * as SqlitePersistence from "../../persistence/Layers/Sqlite.ts"; |
| 27 | import { provideDeterministicTestRuntime } from "./DeterministicRuntime.ts"; | 25 | import { provideDeterministicTestRuntime } from "./DeterministicRuntime.ts"; |
| 28 | import { | 26 | import { |
| 29 | CLAUDE_MODEL_SELECTION, | 27 | CLAUDE_MODEL_SELECTION, |
| ... | @@ -136,12 +134,12 @@ const runCursorRecovery = Effect.fn("runCursorRecovery")(function* (input: { | ... | @@ -136,12 +134,12 @@ const runCursorRecovery = Effect.fn("runCursorRecovery")(function* (input: { |
| 136 | const { phase1Commands, phase1Steps, phase2Commands, phase2Steps } = | 134 | const { phase1Commands, phase1Steps, phase2Commands, phase2Steps } = |
| 137 | splitAfterFirstIdle(materialized); | 135 | splitAfterFirstIdle(materialized); |
| 138 | const options = { | 136 | const options = { |
| 139 | databaseLayer: makeSqlitePersistenceLive(dbPath).pipe(Layer.provide(NodeServices.layer)), | 137 | databaseLayer: SqlitePersistence.layerFromPath(dbPath).pipe(Layer.provide(NodeServices.layer)), |
| 140 | }; | 138 | }; |
| 141 | const harness = { | 139 | const harness = { |
| 142 | ...CursorOrchestratorReplayHarness, | 140 | ...CursorOrchestratorReplayHarness, |
| 143 | makeProviderAdapterRegistryLayer: () => | 141 | makeProviderAdapterRegistryLayer: () => |
| 144 | makeCursorProviderAdapterRegistryReplayLayer(input.transcript, { | 142 | CursorAdapterV2Testkit.layer(input.transcript, { |
| 145 | runner: input.runner, | 143 | runner: input.runner, |
| 146 | assertCompleteOnFinalize: false, | 144 | assertCompleteOnFinalize: false, |
| 147 | }), | 145 | }), |
| ... | @@ -237,10 +235,10 @@ describe("orchestrator replay recovery", () => { | ... | @@ -237,10 +235,10 @@ describe("orchestrator replay recovery", () => { |
| 237 | const harness = { | 235 | const harness = { |
| 238 | ...CodexOrchestratorReplayHarness, | 236 | ...CodexOrchestratorReplayHarness, |
| 239 | makeProviderAdapterRegistryLayer: () => | 237 | makeProviderAdapterRegistryLayer: () => |
| 240 | makeCodexProviderAdapterRegistryReplayLayer({ transcript, driver }), | 238 | CodexAdapterV2Testkit.layer({ transcript, driver }), |
| 241 | }; | 239 | }; |
| 242 | const options = { | 240 | const options = { |
| 243 | databaseLayer: makeSqlitePersistenceLive(dbPath).pipe( | 241 | databaseLayer: SqlitePersistence.layerFromPath(dbPath).pipe( |
| 244 | Layer.provide(NodeServices.layer), | 242 | Layer.provide(NodeServices.layer), |
| 245 | ), | 243 | ), |
| 246 | }; | 244 | }; |
| ... | @@ -340,7 +338,7 @@ describe("orchestrator replay recovery", () => { | ... | @@ -340,7 +338,7 @@ describe("orchestrator replay recovery", () => { |
| 340 | splitAfterFirstIdle(materialized); | 338 | splitAfterFirstIdle(materialized); |
| 341 | const { harness, assertComplete } = makeClaudeRestartReplayHarness(transcript); | 339 | const { harness, assertComplete } = makeClaudeRestartReplayHarness(transcript); |
| 342 | const options = { | 340 | const options = { |
| 343 | databaseLayer: makeSqlitePersistenceLive(path.join(tempDir, "state.sqlite")).pipe( | 341 | databaseLayer: SqlitePersistence.layerFromPath(path.join(tempDir, "state.sqlite")).pipe( |
| 344 | Layer.provide(NodeServices.layer), | 342 | Layer.provide(NodeServices.layer), |
| 345 | ), | 343 | ), |
| 346 | }; | 344 | }; |
apps/server/src/orchestration-v2/testkit/OrchestratorReplayRestartBackgroundNote.integration.test.ts+8-10| ... | @@ -12,7 +12,7 @@ import { | ... | @@ -12,7 +12,7 @@ import { |
| 12 | } from "../Adapters/ClaudeAdapterV2.testkit.ts"; | 12 | } from "../Adapters/ClaudeAdapterV2.testkit.ts"; |
| 13 | import * as IdAllocator from "../IdAllocator.ts"; | 13 | import * as IdAllocator from "../IdAllocator.ts"; |
| 14 | import * as EffectWorker from "../EffectWorker.ts"; | 14 | import * as EffectWorker from "../EffectWorker.ts"; |
| 15 | import { makeSqlitePersistenceLive } from "../../persistence/Layers/Sqlite.ts"; | 15 | import * as SqlitePersistence from "../../persistence/Layers/Sqlite.ts"; |
| 16 | import { provideDeterministicTestRuntime } from "./DeterministicRuntime.ts"; | 16 | import { provideDeterministicTestRuntime } from "./DeterministicRuntime.ts"; |
| 17 | import { CLAUDE_BACKGROUND_SUBAGENT_AFTER_ROOT_PROMPT } from "./fixtures/claude_background_subagent_after_root/input.ts"; | 17 | import { CLAUDE_BACKGROUND_SUBAGENT_AFTER_ROOT_PROMPT } from "./fixtures/claude_background_subagent_after_root/input.ts"; |
| 18 | import { | 18 | import { |
| ... | @@ -20,10 +20,8 @@ import { | ... | @@ -20,10 +20,8 @@ import { |
| 20 | materializeFixtureInput, | 20 | materializeFixtureInput, |
| 21 | projectionFor, | 21 | projectionFor, |
| 22 | } from "./fixtures/shared.ts"; | 22 | } from "./fixtures/shared.ts"; |
| 23 | import { | 23 | import { runOrchestratorV2ProviderReplayScenario } from "./ProviderReplayHarness.ts"; |
| 24 | makeOrchestratorV2ProviderReplayLayer, | 24 | import * as ProviderReplayHarness from "./ProviderReplayHarness.ts"; |
| 25 | runOrchestratorV2ProviderReplayScenario, | ||
| 26 | } from "./ProviderReplayHarness.ts"; | ||
| 27 | import { runOrchestratorV2Scenario } from "./OrchestratorScenario.ts"; | 25 | import { runOrchestratorV2Scenario } from "./OrchestratorScenario.ts"; |
| 28 | import { checkpointWorkspace } from "./ReplayFixtureWorkspace.ts"; | 26 | import { checkpointWorkspace } from "./ReplayFixtureWorkspace.ts"; |
| 29 | import { readProviderReplayTranscript } from "./ReplayTranscriptNdjson.ts"; | 27 | import { readProviderReplayTranscript } from "./ReplayTranscriptNdjson.ts"; |
| ... | @@ -162,7 +160,7 @@ const runRestart = Effect.fn("runRestart")(function* (input: { | ... | @@ -162,7 +160,7 @@ const runRestart = Effect.fn("runRestart")(function* (input: { |
| 162 | const phase1Steps = materialized.steps.slice(0, firstIdle + 1); | 160 | const phase1Steps = materialized.steps.slice(0, firstIdle + 1); |
| 163 | const phase2Steps = materialized.steps.slice(firstIdle + 1); | 161 | const phase2Steps = materialized.steps.slice(firstIdle + 1); |
| 164 | const { harness, assertComplete } = makeClaudeRestartReplayHarness(transcript); | 162 | const { harness, assertComplete } = makeClaudeRestartReplayHarness(transcript); |
| 165 | const databaseLayer = makeSqlitePersistenceLive(path.join(tempDir, "state.sqlite")).pipe( | 163 | const layerDatabase = SqlitePersistence.layerFromPath(path.join(tempDir, "state.sqlite")).pipe( |
| 166 | Layer.provide(NodeServices.layer), | 164 | Layer.provide(NodeServices.layer), |
| 167 | ); | 165 | ); |
| 168 | const scenario = (name: string, steps: typeof materialized.steps) => ({ | 166 | const scenario = (name: string, steps: typeof materialized.steps) => ({ |
| ... | @@ -176,7 +174,7 @@ const runRestart = Effect.fn("runRestart")(function* (input: { | ... | @@ -176,7 +174,7 @@ const runRestart = Effect.fn("runRestart")(function* (input: { |
| 176 | 174 | ||
| 177 | const before = yield* Effect.scoped( | 175 | const before = yield* Effect.scoped( |
| 178 | runOrchestratorV2ProviderReplayScenario(scenario("before-restart", phase1Steps), harness, { | 176 | runOrchestratorV2ProviderReplayScenario(scenario("before-restart", phase1Steps), harness, { |
| 179 | databaseLayer, | 177 | databaseLayer: layerDatabase, |
| 180 | }), | 178 | }), |
| 181 | ); | 179 | ); |
| 182 | const settled = projectionFor(before, SCENARIO); | 180 | const settled = projectionFor(before, SCENARIO); |
| ... | @@ -193,8 +191,8 @@ const runRestart = Effect.fn("runRestart")(function* (input: { | ... | @@ -193,8 +191,8 @@ const runRestart = Effect.fn("runRestart")(function* (input: { |
| 193 | return yield* runOrchestratorV2Scenario(restartScenario); | 191 | return yield* runOrchestratorV2Scenario(restartScenario); |
| 194 | }).pipe( | 192 | }).pipe( |
| 195 | Effect.provide( | 193 | Effect.provide( |
| 196 | makeOrchestratorV2ProviderReplayLayer(restartScenario, harness, { | 194 | ProviderReplayHarness.layerProviderReplay(restartScenario, harness, { |
| 197 | databaseLayer, | 195 | databaseLayer: layerDatabase, |
| 198 | recoverOnStartup: true, | 196 | recoverOnStartup: true, |
| 199 | continueThreadsAfterServerUpdate: input.continueThreadsAfterServerUpdate, | 197 | continueThreadsAfterServerUpdate: input.continueThreadsAfterServerUpdate, |
| 200 | }), | 198 | }), |
| ... | @@ -211,7 +209,7 @@ const runRestart = Effect.fn("runRestart")(function* (input: { | ... | @@ -211,7 +209,7 @@ const runRestart = Effect.fn("runRestart")(function* (input: { |
| 211 | 209 | ||
| 212 | const after = yield* Effect.scoped( | 210 | const after = yield* Effect.scoped( |
| 213 | runOrchestratorV2ProviderReplayScenario(scenario("after-restart", phase2Steps), harness, { | 211 | runOrchestratorV2ProviderReplayScenario(scenario("after-restart", phase2Steps), harness, { |
| 214 | databaseLayer, | 212 | databaseLayer: layerDatabase, |
| 215 | continueThreadsAfterServerUpdate: input.continueThreadsAfterServerUpdate, | 213 | continueThreadsAfterServerUpdate: input.continueThreadsAfterServerUpdate, |
| 216 | }), | 214 | }), |
| 217 | ); | 215 | ); |
apps/server/src/orchestration-v2/testkit/ProviderReplayHarness.ts+120-107| ... | @@ -13,7 +13,7 @@ import type { SqlError } from "effect/sql/SqlError"; | ... | @@ -13,7 +13,7 @@ import type { SqlError } from "effect/sql/SqlError"; |
| 13 | 13 | ||
| 14 | import * as CheckpointStore from "../../checkpointing/CheckpointStore.ts"; | 14 | import * as CheckpointStore from "../../checkpointing/CheckpointStore.ts"; |
| 15 | import * as ServerConfig from "../../config.ts"; | 15 | import * as ServerConfig from "../../config.ts"; |
| 16 | import { SqlitePersistenceMemory } from "../../persistence/Layers/Sqlite.ts"; | 16 | import * as SqlitePersistence from "../../persistence/Layers/Sqlite.ts"; |
| 17 | import * as ServerSettings from "../../serverSettings.ts"; | 17 | import * as ServerSettings from "../../serverSettings.ts"; |
| 18 | import * as ThreadManagementService from "../ThreadManagementService.ts"; | 18 | import * as ThreadManagementService from "../ThreadManagementService.ts"; |
| 19 | import * as McpSessionRegistryTestkit from "../../mcp/McpSessionRegistry.testkit.ts"; | 19 | import * as McpSessionRegistryTestkit from "../../mcp/McpSessionRegistry.testkit.ts"; |
| ... | @@ -43,7 +43,7 @@ import * as ProviderSessionManager from "../ProviderSessionManager.ts"; | ... | @@ -43,7 +43,7 @@ import * as ProviderSessionManager from "../ProviderSessionManager.ts"; |
| 43 | import * as ProviderSwitchService from "../ProviderSwitchService.ts"; | 43 | import * as ProviderSwitchService from "../ProviderSwitchService.ts"; |
| 44 | import * as ProviderTurnControlService from "../ProviderTurnControlService.ts"; | 44 | import * as ProviderTurnControlService from "../ProviderTurnControlService.ts"; |
| 45 | import * as ProviderTurnStartService from "../ProviderTurnStartService.ts"; | 45 | import * as ProviderTurnStartService from "../ProviderTurnStartService.ts"; |
| 46 | import { worktreeRepairDependenciesTestLayer } from "../ProviderTurnStartService.testkit.ts"; | 46 | import * as ProviderTurnStartServiceTestkit from "../ProviderTurnStartService.testkit.ts"; |
| 47 | import * as RunExecutionService from "../RunExecutionService.ts"; | 47 | import * as RunExecutionService from "../RunExecutionService.ts"; |
| 48 | import * as RunFinalizationService from "../RunFinalizationService.ts"; | 48 | import * as RunFinalizationService from "../RunFinalizationService.ts"; |
| 49 | import * as ThreadTitleRegenerationService from "../ThreadTitleRegenerationService.ts"; | 49 | import * as ThreadTitleRegenerationService from "../ThreadTitleRegenerationService.ts"; |
| ... | @@ -206,7 +206,7 @@ export function runOrchestratorV2ProviderReplayScenario< | ... | @@ -206,7 +206,7 @@ export function runOrchestratorV2ProviderReplayScenario< |
| 206 | : [], | 206 | : [], |
| 207 | ) ?? [], | 207 | ) ?? [], |
| 208 | ); | 208 | ); |
| 209 | const layer = makeOrchestratorV2ProviderReplayLayer(scenario, harness, { | 209 | const layer = layerProviderReplay(scenario, harness, { |
| 210 | ...options, | 210 | ...options, |
| 211 | replayGate, | 211 | replayGate, |
| 212 | }); | 212 | }); |
| ... | @@ -214,10 +214,7 @@ export function runOrchestratorV2ProviderReplayScenario< | ... | @@ -214,10 +214,7 @@ export function runOrchestratorV2ProviderReplayScenario< |
| 214 | return runOrchestratorV2Scenario(scenario, { replayGate }).pipe(Effect.provide(layer)); | 214 | return runOrchestratorV2Scenario(scenario, { replayGate }).pipe(Effect.provide(layer)); |
| 215 | } | 215 | } |
| 216 | 216 | ||
| 217 | export function makeOrchestratorV2ProviderReplayLayer< | 217 | export function layerProviderReplay<Transcript extends ProviderReplayTranscript, Error>( |
| 218 | Transcript extends ProviderReplayTranscript, | ||
| 219 | Error, | ||
| 220 | >( | ||
| 221 | scenario: OrchestratorV2ProviderReplayScenario<Transcript>, | 218 | scenario: OrchestratorV2ProviderReplayScenario<Transcript>, |
| 222 | harness: OrchestratorV2ProviderReplayHarness<Transcript, Error>, | 219 | harness: OrchestratorV2ProviderReplayHarness<Transcript, Error>, |
| 223 | options: { | 220 | options: { |
| ... | @@ -239,14 +236,14 @@ export function makeOrchestratorV2ProviderReplayLayer< | ... | @@ -239,14 +236,14 @@ export function makeOrchestratorV2ProviderReplayLayer< |
| 239 | Orchestrator.OrchestratorV2 | EffectWorker.OrchestrationEffectWorkerV2 | EventSink.EventSinkV2, | 236 | Orchestrator.OrchestratorV2 | EffectWorker.OrchestrationEffectWorkerV2 | EventSink.EventSinkV2, |
| 240 | Error | MigrationError | PlatformError.PlatformError | SqlError | 237 | Error | MigrationError | PlatformError.PlatformError | SqlError |
| 241 | > { | 238 | > { |
| 242 | const registryLayer = harness.makeProviderAdapterRegistryLayer( | 239 | const layerRegistry = harness.makeProviderAdapterRegistryLayer( |
| 243 | scenario.transcript, | 240 | scenario.transcript, |
| 244 | options.replayGate === undefined ? {} : { replayGate: options.replayGate }, | 241 | options.replayGate === undefined ? {} : { replayGate: options.replayGate }, |
| 245 | ); | 242 | ); |
| 246 | return makeOrchestratorV2ReplayLayerWithRegistry(scenario, registryLayer, options); | 243 | return layerWithRegistry(scenario, layerRegistry, options); |
| 247 | } | 244 | } |
| 248 | 245 | ||
| 249 | export function makeOrchestratorV2ReplayLayerWithRegistry<Error>( | 246 | export function layerWithRegistry<Error>( |
| 250 | scenario: Pick<OrchestratorV2ProviderReplayScenario, "name" | "runtimePolicyOverride">, | 247 | scenario: Pick<OrchestratorV2ProviderReplayScenario, "name" | "runtimePolicyOverride">, |
| 251 | registryLayer: Layer.Layer<ProviderAdapterRegistry.ProviderAdapterRegistryV2, Error>, | 248 | registryLayer: Layer.Layer<ProviderAdapterRegistry.ProviderAdapterRegistryV2, Error>, |
| 252 | options: { | 249 | options: { |
| ... | @@ -267,166 +264,173 @@ export function makeOrchestratorV2ReplayLayerWithRegistry<Error>( | ... | @@ -267,166 +264,173 @@ export function makeOrchestratorV2ReplayLayerWithRegistry<Error>( |
| 267 | Orchestrator.OrchestratorV2 | EffectWorker.OrchestrationEffectWorkerV2 | EventSink.EventSinkV2, | 264 | Orchestrator.OrchestratorV2 | EffectWorker.OrchestrationEffectWorkerV2 | EventSink.EventSinkV2, |
| 268 | Error | MigrationError | PlatformError.PlatformError | SqlError | 265 | Error | MigrationError | PlatformError.PlatformError | SqlError |
| 269 | > { | 266 | > { |
| 270 | const serverConfigLayer = Layer.effect( | 267 | const layerServerConfig = Layer.effect( |
| 271 | ServerConfig.ServerConfig, | 268 | ServerConfig.ServerConfig, |
| 272 | makeReplayServerConfig(scenario.name).pipe(Effect.orDie), | 269 | makeReplayServerConfig(scenario.name).pipe(Effect.orDie), |
| 273 | ).pipe(Layer.provide(NodeServices.layer)); | 270 | ).pipe(Layer.provide(NodeServices.layer)); |
| 274 | const runtimeLayer = | 271 | const layerRuntime = |
| 275 | scenario.runtimePolicyOverride === undefined | 272 | scenario.runtimePolicyOverride === undefined |
| 276 | ? RuntimePolicy.layer | 273 | ? RuntimePolicy.layer |
| 277 | : RuntimePolicy.layerWithOverride(scenario.runtimePolicyOverride).pipe( | 274 | : RuntimePolicy.layerWithOverride(scenario.runtimePolicyOverride).pipe( |
| 278 | Layer.provide(RuntimePolicy.layer), | 275 | Layer.provide(RuntimePolicy.layer), |
| 279 | ); | 276 | ); |
| 280 | const databaseLayer = options.databaseLayer ?? SqlitePersistenceMemory; | 277 | const layerDatabase = options.databaseLayer ?? SqlitePersistence.layerMemory; |
| 281 | // One queue shared by the adapters, the orchestrator, and the worker, like | 278 | // One queue shared by the adapters, the orchestrator, and the worker, like |
| 282 | // runtimeLayer.ts; layer memoization keeps it a single instance. | 279 | // runtimeLayer.ts; layer memoization keeps it a single instance. |
| 283 | const continuationRequestsLayer = | 280 | const layerContinuationRequests = |
| 284 | options.runContinuationWorker === true ? ProviderContinuationRequests.layer : Layer.empty; | 281 | options.runContinuationWorker === true ? ProviderContinuationRequests.layer : Layer.empty; |
| 285 | const providedRegistryLayer = registryLayer.pipe(Layer.provide(continuationRequestsLayer)); | 282 | const layerProvidedRegistry = registryLayer.pipe(Layer.provide(layerContinuationRequests)); |
| 286 | const serverSettingsLayer = ServerSettings.layerTest({ | 283 | const layerServerSettings = ServerSettings.layerTest({ |
| 287 | responseStreamingMode: "turn", | 284 | responseStreamingMode: "turn", |
| 288 | ...(options.continueThreadsAfterServerUpdate === undefined | 285 | ...(options.continueThreadsAfterServerUpdate === undefined |
| 289 | ? {} | 286 | ? {} |
| 290 | : { continueThreadsAfterServerUpdate: options.continueThreadsAfterServerUpdate }), | 287 | : { continueThreadsAfterServerUpdate: options.continueThreadsAfterServerUpdate }), |
| 291 | }).pipe(Layer.orDie); | 288 | }).pipe(Layer.orDie); |
| 292 | const storesLayer = Layer.mergeAll( | 289 | const layerStores = Layer.mergeAll( |
| 293 | EventStore.layer, | 290 | EventStore.layer, |
| 294 | ProjectionStore.layer, | 291 | ProjectionStore.layer, |
| 295 | ProjectStore.layer, | 292 | ProjectStore.layer, |
| 296 | CommandReceiptStore.layer, | 293 | CommandReceiptStore.layer, |
| 297 | EffectOutbox.layer, | 294 | EffectOutbox.layer, |
| 298 | TurnItemPositionStore.layer, | 295 | TurnItemPositionStore.layer, |
| 299 | ).pipe(Layer.provide(databaseLayer)); | 296 | ).pipe(Layer.provide(layerDatabase)); |
| 300 | const eventSinkProvided = EventSink.layerFromStores.pipe( | 297 | const layerEventSinkProvided = EventSink.layerFromStores.pipe( |
| 301 | Layer.provide(Layer.mergeAll(storesLayer, databaseLayer)), | 298 | Layer.provide(Layer.mergeAll(layerStores, layerDatabase)), |
| 299 | ); | ||
| 300 | const layerCommandReceiptStoreProvided = CommandReceiptStore.layer.pipe( | ||
| 301 | Layer.provide(layerDatabase), | ||
| 302 | ); | 302 | ); |
| 303 | const commandReceiptStoreProvided = CommandReceiptStore.layer.pipe(Layer.provide(databaseLayer)); | 303 | const layerProviderEventIngestorProvided = ProviderEventIngestor.layer.pipe( |
| 304 | const providerEventIngestorProvided = ProviderEventIngestor.layer.pipe( | ||
| 305 | Layer.provide( | 304 | Layer.provide( |
| 306 | Layer.mergeAll( | 305 | Layer.mergeAll( |
| 307 | storesLayer, | 306 | layerStores, |
| 308 | eventSinkProvided, | 307 | layerEventSinkProvided, |
| 309 | IdAllocator.layer, | 308 | IdAllocator.layer, |
| 310 | ThreadCommandExecutor.layer, | 309 | ThreadCommandExecutor.layer, |
| 311 | ), | 310 | ), |
| 312 | ), | 311 | ), |
| 313 | ); | 312 | ); |
| 314 | const vcsDriverRegistryLayer = VcsDriverRegistry.layer.pipe( | 313 | const layerVcsDriverRegistry = VcsDriverRegistry.layer.pipe( |
| 315 | Layer.provide(VcsProcess.layer), | 314 | Layer.provide(VcsProcess.layer), |
| 316 | Layer.provide(serverConfigLayer), | 315 | Layer.provide(layerServerConfig), |
| 317 | Layer.provide(NodeServices.layer), | 316 | Layer.provide(NodeServices.layer), |
| 318 | ); | 317 | ); |
| 319 | const checkpointStoreLayer = CheckpointStore.layer.pipe( | 318 | const layerCheckpointStore = CheckpointStore.layer.pipe( |
| 320 | Layer.provide(vcsDriverRegistryLayer), | 319 | Layer.provide(layerVcsDriverRegistry), |
| 321 | Layer.provide(NodeServices.layer), | 320 | Layer.provide(NodeServices.layer), |
| 322 | ); | 321 | ); |
| 323 | const checkpointServiceProvided = CheckpointService.layer.pipe( | 322 | const layerCheckpointServiceProvided = CheckpointService.layer.pipe( |
| 324 | Layer.provide(Layer.mergeAll(checkpointStoreLayer, IdAllocator.layer)), | 323 | Layer.provide(Layer.mergeAll(layerCheckpointStore, IdAllocator.layer)), |
| 325 | ); | 324 | ); |
| 326 | const contextHandoffServiceProvided = ContextHandoffService.layer.pipe( | 325 | const layerContextHandoffServiceProvided = ContextHandoffService.layer.pipe( |
| 327 | Layer.provide(IdAllocator.layer), | 326 | Layer.provide(IdAllocator.layer), |
| 328 | ); | 327 | ); |
| 329 | const persistenceLayer = Layer.mergeAll( | 328 | const layerPersistence = Layer.mergeAll( |
| 330 | storesLayer, | 329 | layerStores, |
| 331 | eventSinkProvided, | 330 | layerEventSinkProvided, |
| 332 | commandReceiptStoreProvided, | 331 | layerCommandReceiptStoreProvided, |
| 333 | IdAllocator.layer, | 332 | IdAllocator.layer, |
| 334 | providerEventIngestorProvided, | 333 | layerProviderEventIngestorProvided, |
| 335 | ); | 334 | ); |
| 336 | const providerSessionManagerProvided = ProviderSessionManager.layerWithOptions({ | 335 | const layerProviderSessionManagerProvided = ProviderSessionManager.layerWithOptions({ |
| 337 | configureMcp: false, | 336 | configureMcp: false, |
| 338 | }).pipe( | 337 | }).pipe( |
| 339 | Layer.provide( | 338 | Layer.provide( |
| 340 | Layer.mergeAll( | 339 | Layer.mergeAll( |
| 341 | providedRegistryLayer, | 340 | layerProvidedRegistry, |
| 342 | eventSinkProvided, | 341 | layerEventSinkProvided, |
| 343 | IdAllocator.layer, | 342 | IdAllocator.layer, |
| 344 | McpSessionRegistryTestkit.layer, | 343 | McpSessionRegistryTestkit.layer, |
| 345 | providerEventIngestorProvided, | 344 | layerProviderEventIngestorProvided, |
| 346 | storesLayer, | 345 | layerStores, |
| 347 | ), | 346 | ), |
| 348 | ), | 347 | ), |
| 349 | ); | 348 | ); |
| 350 | const providerSwitchServiceProvided = ProviderSwitchService.layer.pipe( | 349 | const layerProviderSwitchServiceProvided = ProviderSwitchService.layer.pipe( |
| 351 | Layer.provide(providedRegistryLayer), | 350 | Layer.provide(layerProvidedRegistry), |
| 352 | ); | 351 | ); |
| 353 | const runExecutionServiceProvided = RunExecutionService.layer.pipe( | 352 | const layerRunExecutionServiceProvided = RunExecutionService.layer.pipe( |
| 354 | Layer.provide( | 353 | Layer.provide( |
| 355 | Layer.mergeAll( | 354 | Layer.mergeAll( |
| 356 | checkpointServiceProvided, | 355 | layerCheckpointServiceProvided, |
| 357 | eventSinkProvided, | 356 | layerEventSinkProvided, |
| 358 | IdAllocator.layer, | 357 | IdAllocator.layer, |
| 359 | providerEventIngestorProvided, | 358 | layerProviderEventIngestorProvided, |
| 360 | serverSettingsLayer, | 359 | layerServerSettings, |
| 361 | ), | 360 | ), |
| 362 | ), | 361 | ), |
| 363 | ); | 362 | ); |
| 364 | const providerTurnStartServiceProvided = ProviderTurnStartService.layer.pipe( | 363 | const layerProviderTurnStartServiceProvided = ProviderTurnStartService.layer.pipe( |
| 365 | Layer.provide( | 364 | Layer.provide( |
| 366 | Layer.mergeAll( | 365 | Layer.mergeAll( |
| 367 | contextHandoffServiceProvided, | 366 | layerContextHandoffServiceProvided, |
| 368 | eventSinkProvided, | 367 | layerEventSinkProvided, |
| 369 | IdAllocator.layer, | 368 | IdAllocator.layer, |
| 370 | storesLayer, | 369 | layerStores, |
| 371 | providerSessionManagerProvided, | 370 | layerProviderSessionManagerProvided, |
| 372 | Layer.mock(ProviderAuthService.ProviderAuthService)({ | 371 | Layer.mock(ProviderAuthService.ProviderAuthService)({ |
| 373 | tryHandlePromptCommand: () => Effect.succeed(false), | 372 | tryHandlePromptCommand: () => Effect.succeed(false), |
| 374 | }), | 373 | }), |
| 375 | runExecutionServiceProvided, | 374 | layerRunExecutionServiceProvided, |
| 376 | runtimeLayer, | 375 | layerRuntime, |
| 377 | ), | 376 | ), |
| 378 | ), | 377 | ), |
| 379 | ); | 378 | ); |
| 380 | const providerTurnControlServiceProvided = ProviderTurnControlService.layer.pipe( | 379 | const layerProviderTurnControlServiceProvided = ProviderTurnControlService.layer.pipe( |
| 381 | Layer.provide(Layer.merge(storesLayer, providerSessionManagerProvided)), | 380 | Layer.provide(Layer.merge(layerStores, layerProviderSessionManagerProvided)), |
| 382 | ); | 381 | ); |
| 383 | const runtimeRequestServiceProvided = RuntimeRequestService.layer.pipe( | 382 | const layerRuntimeRequestServiceProvided = RuntimeRequestService.layer.pipe( |
| 384 | Layer.provide(Layer.merge(storesLayer, providerSessionManagerProvided)), | 383 | Layer.provide(Layer.merge(layerStores, layerProviderSessionManagerProvided)), |
| 385 | ); | 384 | ); |
| 386 | const checkpointRollbackServiceProvided = CheckpointRollbackService.layer.pipe( | 385 | const layerCheckpointRollbackServiceProvided = CheckpointRollbackService.layer.pipe( |
| 387 | Layer.provide( | 386 | Layer.provide( |
| 388 | Layer.mergeAll( | 387 | Layer.mergeAll( |
| 389 | checkpointServiceProvided, | 388 | layerCheckpointServiceProvided, |
| 390 | eventSinkProvided, | 389 | layerEventSinkProvided, |
| 391 | IdAllocator.layer, | 390 | IdAllocator.layer, |
| 392 | storesLayer, | 391 | layerStores, |
| 393 | providerSessionManagerProvided, | 392 | layerProviderSessionManagerProvided, |
| 394 | runtimeLayer, | 393 | layerRuntime, |
| 395 | ), | 394 | ), |
| 396 | ), | 395 | ), |
| 397 | ); | 396 | ); |
| 398 | const checkpointCaptureServiceProvided = CheckpointCaptureService.layer.pipe( | 397 | const layerCheckpointCaptureServiceProvided = CheckpointCaptureService.layer.pipe( |
| 399 | Layer.provide( | 398 | Layer.provide( |
| 400 | Layer.mergeAll(checkpointServiceProvided, eventSinkProvided, IdAllocator.layer, storesLayer), | 399 | Layer.mergeAll( |
| 400 | layerCheckpointServiceProvided, | ||
| 401 | layerEventSinkProvided, | ||
| 402 | IdAllocator.layer, | ||
| 403 | layerStores, | ||
| 404 | ), | ||
| 401 | ), | 405 | ), |
| 402 | ); | 406 | ); |
| 403 | const runFinalizationServiceProvided = RunFinalizationService.layer.pipe( | 407 | const layerRunFinalizationServiceProvided = RunFinalizationService.layer.pipe( |
| 404 | Layer.provide(Layer.merge(checkpointCaptureServiceProvided, storesLayer)), | 408 | Layer.provide(Layer.merge(layerCheckpointCaptureServiceProvided, layerStores)), |
| 405 | ); | 409 | ); |
| 406 | const threadTitleRegenerationTestLayer = Layer.succeed( | 410 | const layerThreadTitleRegenerationTest = Layer.succeed( |
| 407 | ThreadTitleRegenerationService.ThreadTitleRegenerationService, | 411 | ThreadTitleRegenerationService.ThreadTitleRegenerationService, |
| 408 | ThreadTitleRegenerationService.ThreadTitleRegenerationService.of({ | 412 | ThreadTitleRegenerationService.ThreadTitleRegenerationService.of({ |
| 409 | execute: () => Effect.void, | 413 | execute: () => Effect.void, |
| 410 | }), | 414 | }), |
| 411 | ); | 415 | ); |
| 412 | const orchestratorProvided = Orchestrator.layer.pipe( | 416 | const layerOrchestratorProvided = Orchestrator.layer.pipe( |
| 413 | Layer.provide( | 417 | Layer.provide( |
| 414 | Layer.mergeAll( | 418 | Layer.mergeAll( |
| 415 | checkpointServiceProvided, | 419 | layerCheckpointServiceProvided, |
| 416 | CommandPolicy.layer, | 420 | CommandPolicy.layer, |
| 417 | contextHandoffServiceProvided, | 421 | layerContextHandoffServiceProvided, |
| 418 | persistenceLayer, | 422 | layerPersistence, |
| 419 | providedRegistryLayer, | 423 | layerProvidedRegistry, |
| 420 | continuationRequestsLayer, | 424 | layerContinuationRequests, |
| 421 | runtimeLayer, | 425 | layerRuntime, |
| 422 | providerSessionManagerProvided, | 426 | layerProviderSessionManagerProvided, |
| 423 | providerSwitchServiceProvided, | 427 | layerProviderSwitchServiceProvided, |
| 424 | runExecutionServiceProvided, | 428 | layerRunExecutionServiceProvided, |
| 425 | ThreadForkService.layer, | 429 | ThreadForkService.layer, |
| 426 | ), | 430 | ), |
| 427 | ), | 431 | ), |
| 428 | ); | 432 | ); |
| 429 | const threadManagementProvided = Layer.unwrap( | 433 | const layerThreadManagementProvided = Layer.unwrap( |
| 430 | Effect.gen(function* () { | 434 | Effect.gen(function* () { |
| 431 | const orchestrator = yield* Orchestrator.OrchestratorV2; | 435 | const orchestrator = yield* Orchestrator.OrchestratorV2; |
| 432 | return Layer.mock(ThreadManagementService.ThreadManagementService)({ | 436 | return Layer.mock(ThreadManagementService.ThreadManagementService)({ |
| ... | @@ -437,49 +441,53 @@ export function makeOrchestratorV2ReplayLayerWithRegistry<Error>( | ... | @@ -437,49 +441,53 @@ export function makeOrchestratorV2ReplayLayerWithRegistry<Error>( |
| 437 | delegatedTaskResultPending: orchestrator.delegatedTaskResultPending, | 441 | delegatedTaskResultPending: orchestrator.delegatedTaskResultPending, |
| 438 | }); | 442 | }); |
| 439 | }), | 443 | }), |
| 440 | ).pipe(Layer.provide(orchestratorProvided)); | 444 | ).pipe(Layer.provide(layerOrchestratorProvided)); |
| 441 | const continuationWorkerProvided = | 445 | const layerContinuationWorkerProvided = |
| 442 | options.runContinuationWorker === true | 446 | options.runContinuationWorker === true |
| 443 | ? ProviderContinuationService.workerLive.pipe( | 447 | ? ProviderContinuationService.layer.pipe( |
| 444 | Layer.provide( | 448 | Layer.provide( |
| 445 | Layer.mergeAll(continuationRequestsLayer, threadManagementProvided, IdAllocator.layer), | 449 | Layer.mergeAll( |
| 450 | layerContinuationRequests, | ||
| 451 | layerThreadManagementProvided, | ||
| 452 | IdAllocator.layer, | ||
| 453 | ), | ||
| 446 | ), | 454 | ), |
| 447 | ) | 455 | ) |
| 448 | : Layer.empty; | 456 | : Layer.empty; |
| 449 | const effectExecutorProvided = EffectWorker.executorLayer.pipe( | 457 | const layerEffectExecutorProvided = EffectWorker.layerExecutor.pipe( |
| 450 | Layer.provide( | 458 | Layer.provide( |
| 451 | Layer.mergeAll( | 459 | Layer.mergeAll( |
| 452 | runFinalizationServiceProvided, | 460 | layerRunFinalizationServiceProvided, |
| 453 | checkpointRollbackServiceProvided, | 461 | layerCheckpointRollbackServiceProvided, |
| 454 | providerSessionManagerProvided, | 462 | layerProviderSessionManagerProvided, |
| 455 | providerTurnControlServiceProvided, | 463 | layerProviderTurnControlServiceProvided, |
| 456 | providerTurnStartServiceProvided, | 464 | layerProviderTurnStartServiceProvided, |
| 457 | runtimeRequestServiceProvided, | 465 | layerRuntimeRequestServiceProvided, |
| 458 | threadTitleRegenerationTestLayer, | 466 | layerThreadTitleRegenerationTest, |
| 459 | serverSettingsLayer, | 467 | layerServerSettings, |
| 460 | threadManagementProvided, | 468 | layerThreadManagementProvided, |
| 461 | ), | 469 | ), |
| 462 | ), | 470 | ), |
| 463 | ); | 471 | ); |
| 464 | const effectWorkerProvided = EffectWorker.layer.pipe( | 472 | const layerEffectWorkerProvided = EffectWorker.layer.pipe( |
| 465 | Layer.provide(Layer.merge(storesLayer, effectExecutorProvided)), | 473 | Layer.provide(Layer.merge(layerStores, layerEffectExecutorProvided)), |
| 466 | ); | 474 | ); |
| 467 | const replayRuntime = Layer.mergeAll( | 475 | const layerReplayRuntime = Layer.mergeAll( |
| 468 | orchestratorProvided, | 476 | layerOrchestratorProvided, |
| 469 | effectWorkerProvided, | 477 | layerEffectWorkerProvided, |
| 470 | eventSinkProvided, | 478 | layerEventSinkProvided, |
| 471 | continuationWorkerProvided, | 479 | layerContinuationWorkerProvided, |
| 472 | ).pipe(Layer.provide(worktreeRepairDependenciesTestLayer), Layer.provide(NodeServices.layer)); | 480 | ).pipe(Layer.provide(ProviderTurnStartServiceTestkit.layer), Layer.provide(NodeServices.layer)); |
| 473 | 481 | ||
| 474 | // Build the daemon from the exact worker instance exposed alongside the | 482 | // Build the daemon from the exact worker instance exposed alongside the |
| 475 | // orchestrator. Keeping this acquisition in the replay layer makes the | 483 | // orchestrator. Keeping this acquisition in the replay layer makes the |
| 476 | // outbox lifecycle explicit and prevents test-only command-side draining. | 484 | // outbox lifecycle explicit and prevents test-only command-side draining. |
| 477 | if (options.runEffectWorker === false) { | 485 | if (options.runEffectWorker === false) { |
| 478 | return replayRuntime; | 486 | return layerReplayRuntime; |
| 479 | } | 487 | } |
| 480 | // Built before the runtime it shares stores with, so recovery commits before | 488 | // Built before the runtime it shares stores with, so recovery commits before |
| 481 | // the effect worker claims anything, as in serverRuntimeStartup. | 489 | // the effect worker claims anything, as in serverRuntimeStartup. |
| 482 | const startupRecovery: Layer.Layer< | 490 | const layerStartupRecovery: Layer.Layer< |
| 483 | never, | 491 | never, |
| 484 | MigrationError | PlatformError.PlatformError | SqlError | 492 | MigrationError | PlatformError.PlatformError | SqlError |
| 485 | > = | 493 | > = |
| ... | @@ -491,7 +499,12 @@ export function makeOrchestratorV2ReplayLayerWithRegistry<Error>( | ... | @@ -491,7 +499,12 @@ export function makeOrchestratorV2ReplayLayerWithRegistry<Error>( |
| 491 | ).pipe( | 499 | ).pipe( |
| 492 | Layer.provide(ProviderRuntimeRecoveryService.layer), | 500 | Layer.provide(ProviderRuntimeRecoveryService.layer), |
| 493 | Layer.provide( | 501 | Layer.provide( |
| 494 | Layer.mergeAll(storesLayer, eventSinkProvided, IdAllocator.layer, serverSettingsLayer), | 502 | Layer.mergeAll( |
| 503 | layerStores, | ||
| 504 | layerEventSinkProvided, | ||
| 505 | IdAllocator.layer, | ||
| 506 | layerServerSettings, | ||
| 507 | ), | ||
| 495 | ), | 508 | ), |
| 496 | ) | 509 | ) |
| 497 | : Layer.empty; | 510 | : Layer.empty; |
| ... | @@ -504,5 +517,5 @@ export function makeOrchestratorV2ReplayLayerWithRegistry<Error>( | ... | @@ -504,5 +517,5 @@ export function makeOrchestratorV2ReplayLayerWithRegistry<Error>( |
| 504 | yield* EffectWorker.runDaemon.pipe(Effect.forkScoped); | 517 | yield* EffectWorker.runDaemon.pipe(Effect.forkScoped); |
| 505 | return orchestrator; | 518 | return orchestrator; |
| 506 | }), | 519 | }), |
| 507 | ).pipe(Layer.provideMerge(replayRuntime), Layer.provide(startupRecovery)); | 520 | ).pipe(Layer.provideMerge(layerReplayRuntime), Layer.provide(layerStartupRecovery)); |
| 508 | } | 521 | } |
apps/server/src/orchestration-v2/testkit/ProviderSwitch.integration.test.ts+72-70| ... | @@ -32,7 +32,7 @@ import * as Stream from "effect/Stream"; | ... | @@ -32,7 +32,7 @@ import * as Stream from "effect/Stream"; |
| 32 | import * as Schema from "effect/Schema"; | 32 | import * as Schema from "effect/Schema"; |
| 33 | import * as SqlClient from "effect/sql/SqlClient"; | 33 | import * as SqlClient from "effect/sql/SqlClient"; |
| 34 | 34 | ||
| 35 | import { SqlitePersistenceMemory } from "../../persistence/Layers/Sqlite.ts"; | 35 | import * as SqlitePersistence from "../../persistence/Layers/Sqlite.ts"; |
| 36 | import { CommandPolicyCapabilityUnsupportedError } from "../CommandPolicy.ts"; | 36 | import { CommandPolicyCapabilityUnsupportedError } from "../CommandPolicy.ts"; |
| 37 | import { ClaudeProviderCapabilitiesV2 } from "../Adapters/ClaudeAdapterV2.ts"; | 37 | import { ClaudeProviderCapabilitiesV2 } from "../Adapters/ClaudeAdapterV2.ts"; |
| 38 | import { | 38 | import { |
| ... | @@ -64,7 +64,7 @@ import { | ... | @@ -64,7 +64,7 @@ import { |
| 64 | CURSOR_MODEL_SELECTION, | 64 | CURSOR_MODEL_SELECTION, |
| 65 | GROK_MODEL_SELECTION, | 65 | GROK_MODEL_SELECTION, |
| 66 | } from "./fixtures/shared.ts"; | 66 | } from "./fixtures/shared.ts"; |
| 67 | import { makeOrchestratorV2ReplayLayerWithRegistry } from "./ProviderReplayHarness.ts"; | 67 | import * as ProviderReplayHarness from "./ProviderReplayHarness.ts"; |
| 68 | import { checkpointWorkspace } from "./ReplayFixtureWorkspace.ts"; | 68 | import { checkpointWorkspace } from "./ReplayFixtureWorkspace.ts"; |
| 69 | 69 | ||
| 70 | const encodeJson = Schema.encodeEffect(Schema.fromJsonString(Schema.Unknown)); | 70 | const encodeJson = Schema.encodeEffect(Schema.fromJsonString(Schema.Unknown)); |
| ... | @@ -444,7 +444,7 @@ describe("orchestration v2 provider switching", () => { | ... | @@ -444,7 +444,7 @@ describe("orchestration v2 provider switching", () => { |
| 444 | ? { options: [{ id: "contextWindow", value: "1m" }] } | 444 | ? { options: [{ id: "contextWindow", value: "1m" }] } |
| 445 | : { model: `${CLAUDE_MODEL_SELECTION.model}-large` }), | 445 | : { model: `${CLAUDE_MODEL_SELECTION.model}-large` }), |
| 446 | }; | 446 | }; |
| 447 | const registry = ProviderAdapterRegistry.makeLayer([ | 447 | const layerRegistry = ProviderAdapterRegistry.layerFromAdapters([ |
| 448 | makeTestAdapter({ | 448 | makeTestAdapter({ |
| 449 | instanceId: CODEX_MODEL_SELECTION.instanceId, | 449 | instanceId: CODEX_MODEL_SELECTION.instanceId, |
| 450 | driver: CODEX_DRIVER, | 450 | driver: CODEX_DRIVER, |
| ... | @@ -880,7 +880,7 @@ describe("orchestration v2 provider switching", () => { | ... | @@ -880,7 +880,7 @@ describe("orchestration v2 provider switching", () => { |
| 880 | } | 880 | } |
| 881 | }).pipe( | 881 | }).pipe( |
| 882 | Effect.provide( | 882 | Effect.provide( |
| 883 | makeOrchestratorV2ReplayLayerWithRegistry( | 883 | ProviderReplayHarness.layerWithRegistry( |
| 884 | { | 884 | { |
| 885 | name: `handoff-${scenario}`, | 885 | name: `handoff-${scenario}`, |
| 886 | runtimePolicyOverride: { | 886 | runtimePolicyOverride: { |
| ... | @@ -889,7 +889,7 @@ describe("orchestration v2 provider switching", () => { | ... | @@ -889,7 +889,7 @@ describe("orchestration v2 provider switching", () => { |
| 889 | sandboxPolicy: { type: "readOnly" }, | 889 | sandboxPolicy: { type: "readOnly" }, |
| 890 | }, | 890 | }, |
| 891 | }, | 891 | }, |
| 892 | registry, | 892 | layerRegistry, |
| 893 | ), | 893 | ), |
| 894 | ), | 894 | ), |
| 895 | ); | 895 | ); |
| ... | @@ -906,7 +906,7 @@ describe("orchestration v2 provider switching", () => { | ... | @@ -906,7 +906,7 @@ describe("orchestration v2 provider switching", () => { |
| 906 | const injectedHistory = yield* Ref.make<ReadonlyArray<unknown>>([]); | 906 | const injectedHistory = yield* Ref.make<ReadonlyArray<unknown>>([]); |
| 907 | const failOnce = yield* Ref.make(true); | 907 | const failOnce = yield* Ref.make(true); |
| 908 | const generation = yield* Ref.make(0); | 908 | const generation = yield* Ref.make(0); |
| 909 | const registry = ProviderAdapterRegistry.makeLayer([ | 909 | const layerRegistry = ProviderAdapterRegistry.layerFromAdapters([ |
| 910 | makeTestAdapter({ | 910 | makeTestAdapter({ |
| 911 | instanceId: CODEX_MODEL_SELECTION.instanceId, | 911 | instanceId: CODEX_MODEL_SELECTION.instanceId, |
| 912 | driver: CODEX_DRIVER, | 912 | driver: CODEX_DRIVER, |
| ... | @@ -1042,7 +1042,7 @@ describe("orchestration v2 provider switching", () => { | ... | @@ -1042,7 +1042,7 @@ describe("orchestration v2 provider switching", () => { |
| 1042 | ); | 1042 | ); |
| 1043 | }).pipe( | 1043 | }).pipe( |
| 1044 | Effect.provide( | 1044 | Effect.provide( |
| 1045 | makeOrchestratorV2ReplayLayerWithRegistry( | 1045 | ProviderReplayHarness.layerWithRegistry( |
| 1046 | { | 1046 | { |
| 1047 | name: `handoff-retry-${failure}`, | 1047 | name: `handoff-retry-${failure}`, |
| 1048 | runtimePolicyOverride: { | 1048 | runtimePolicyOverride: { |
| ... | @@ -1051,7 +1051,7 @@ describe("orchestration v2 provider switching", () => { | ... | @@ -1051,7 +1051,7 @@ describe("orchestration v2 provider switching", () => { |
| 1051 | sandboxPolicy: { type: "readOnly" }, | 1051 | sandboxPolicy: { type: "readOnly" }, |
| 1052 | }, | 1052 | }, |
| 1053 | }, | 1053 | }, |
| 1054 | registry, | 1054 | layerRegistry, |
| 1055 | ), | 1055 | ), |
| 1056 | ), | 1056 | ), |
| 1057 | ); | 1057 | ); |
| ... | @@ -1068,7 +1068,7 @@ describe("orchestration v2 provider switching", () => { | ... | @@ -1068,7 +1068,7 @@ describe("orchestration v2 provider switching", () => { |
| 1068 | const capturedTurns = yield* Ref.make<ReadonlyArray<CapturedTurn>>([]); | 1068 | const capturedTurns = yield* Ref.make<ReadonlyArray<CapturedTurn>>([]); |
| 1069 | const failResumeOnce = yield* Ref.make(false); | 1069 | const failResumeOnce = yield* Ref.make(false); |
| 1070 | const generation = yield* Ref.make(0); | 1070 | const generation = yield* Ref.make(0); |
| 1071 | const registry = ProviderAdapterRegistry.makeLayer([ | 1071 | const layerRegistry = ProviderAdapterRegistry.layerFromAdapters([ |
| 1072 | makeTestAdapter({ | 1072 | makeTestAdapter({ |
| 1073 | instanceId: CLAUDE_MODEL_SELECTION.instanceId, | 1073 | instanceId: CLAUDE_MODEL_SELECTION.instanceId, |
| 1074 | driver: CLAUDE_DRIVER, | 1074 | driver: CLAUDE_DRIVER, |
| ... | @@ -1171,7 +1171,7 @@ describe("orchestration v2 provider switching", () => { | ... | @@ -1171,7 +1171,7 @@ describe("orchestration v2 provider switching", () => { |
| 1171 | assert.isTrue(turns[2]?.nativeThreadHasTurns); | 1171 | assert.isTrue(turns[2]?.nativeThreadHasTurns); |
| 1172 | }).pipe( | 1172 | }).pipe( |
| 1173 | Effect.provide( | 1173 | Effect.provide( |
| 1174 | makeOrchestratorV2ReplayLayerWithRegistry( | 1174 | ProviderReplayHarness.layerWithRegistry( |
| 1175 | { | 1175 | { |
| 1176 | name: "resume-fallback-native-turns", | 1176 | name: "resume-fallback-native-turns", |
| 1177 | runtimePolicyOverride: { | 1177 | runtimePolicyOverride: { |
| ... | @@ -1180,7 +1180,7 @@ describe("orchestration v2 provider switching", () => { | ... | @@ -1180,7 +1180,7 @@ describe("orchestration v2 provider switching", () => { |
| 1180 | sandboxPolicy: { type: "readOnly" }, | 1180 | sandboxPolicy: { type: "readOnly" }, |
| 1181 | }, | 1181 | }, |
| 1182 | }, | 1182 | }, |
| 1183 | registry, | 1183 | layerRegistry, |
| 1184 | ), | 1184 | ), |
| 1185 | ), | 1185 | ), |
| 1186 | ); | 1186 | ); |
| ... | @@ -1215,7 +1215,7 @@ describe("orchestration v2 provider switching", () => { | ... | @@ -1215,7 +1215,7 @@ describe("orchestration v2 provider switching", () => { |
| 1215 | const targetSelection = returning ? CODEX_MODEL_SELECTION : CLAUDE_MODEL_SELECTION; | 1215 | const targetSelection = returning ? CODEX_MODEL_SELECTION : CLAUDE_MODEL_SELECTION; |
| 1216 | const originalPrompt = "Keep the release marker violet and preserve the existing API."; | 1216 | const originalPrompt = "Keep the release marker violet and preserve the existing API."; |
| 1217 | const partialResponse = "I checked the API and found the release configuration."; | 1217 | const partialResponse = "I checked the API and found the release configuration."; |
| 1218 | const registryLayer = ProviderAdapterRegistry.makeLayer( | 1218 | const layerRegistry = ProviderAdapterRegistry.layerFromAdapters( |
| 1219 | ( | 1219 | ( |
| 1220 | [ | 1220 | [ |
| 1221 | [CODEX_MODEL_SELECTION, CODEX_DRIVER, CodexProviderCapabilitiesV2], | 1221 | [CODEX_MODEL_SELECTION, CODEX_DRIVER, CodexProviderCapabilitiesV2], |
| ... | @@ -1354,7 +1354,7 @@ describe("orchestration v2 provider switching", () => { | ... | @@ -1354,7 +1354,7 @@ describe("orchestration v2 provider switching", () => { |
| 1354 | assert.notInclude(back.text, partialResponse); | 1354 | assert.notInclude(back.text, partialResponse); |
| 1355 | }).pipe( | 1355 | }).pipe( |
| 1356 | Effect.provide( | 1356 | Effect.provide( |
| 1357 | makeOrchestratorV2ReplayLayerWithRegistry( | 1357 | ProviderReplayHarness.layerWithRegistry( |
| 1358 | { | 1358 | { |
| 1359 | name: `handoff-${status}-${queued}-${returning}`, | 1359 | name: `handoff-${status}-${queued}-${returning}`, |
| 1360 | runtimePolicyOverride: { | 1360 | runtimePolicyOverride: { |
| ... | @@ -1367,7 +1367,7 @@ describe("orchestration v2 provider switching", () => { | ... | @@ -1367,7 +1367,7 @@ describe("orchestration v2 provider switching", () => { |
| 1367 | }, | 1367 | }, |
| 1368 | }, | 1368 | }, |
| 1369 | }, | 1369 | }, |
| 1370 | registryLayer, | 1370 | layerRegistry, |
| 1371 | ), | 1371 | ), |
| 1372 | ), | 1372 | ), |
| 1373 | ); | 1373 | ); |
| ... | @@ -1387,7 +1387,7 @@ describe("orchestration v2 provider switching", () => { | ... | @@ -1387,7 +1387,7 @@ describe("orchestration v2 provider switching", () => { |
| 1387 | const capturedTurns = yield* Ref.make<ReadonlyArray<CapturedTurn>>([]); | 1387 | const capturedTurns = yield* Ref.make<ReadonlyArray<CapturedTurn>>([]); |
| 1388 | const started = yield* Deferred.make<void>(); | 1388 | const started = yield* Deferred.make<void>(); |
| 1389 | const scenarioThreadId = ThreadId.make(`thread:queued-capability:${key}`); | 1389 | const scenarioThreadId = ThreadId.make(`thread:queued-capability:${key}`); |
| 1390 | const registryLayer = ProviderAdapterRegistry.makeLayer([ | 1390 | const layerRegistry = ProviderAdapterRegistry.layerFromAdapters([ |
| 1391 | makeTestAdapter({ | 1391 | makeTestAdapter({ |
| 1392 | instanceId: CODEX_MODEL_SELECTION.instanceId, | 1392 | instanceId: CODEX_MODEL_SELECTION.instanceId, |
| 1393 | driver: CODEX_DRIVER, | 1393 | driver: CODEX_DRIVER, |
| ... | @@ -1477,7 +1477,7 @@ describe("orchestration v2 provider switching", () => { | ... | @@ -1477,7 +1477,7 @@ describe("orchestration v2 provider switching", () => { |
| 1477 | assert.deepEqual(projection.thread.modelSelection, CODEX_MODEL_SELECTION); | 1477 | assert.deepEqual(projection.thread.modelSelection, CODEX_MODEL_SELECTION); |
| 1478 | }).pipe( | 1478 | }).pipe( |
| 1479 | Effect.provide( | 1479 | Effect.provide( |
| 1480 | makeOrchestratorV2ReplayLayerWithRegistry( | 1480 | ProviderReplayHarness.layerWithRegistry( |
| 1481 | { | 1481 | { |
| 1482 | name: `queued-capability-${key}`, | 1482 | name: `queued-capability-${key}`, |
| 1483 | runtimePolicyOverride: { | 1483 | runtimePolicyOverride: { |
| ... | @@ -1490,7 +1490,7 @@ describe("orchestration v2 provider switching", () => { | ... | @@ -1490,7 +1490,7 @@ describe("orchestration v2 provider switching", () => { |
| 1490 | }, | 1490 | }, |
| 1491 | }, | 1491 | }, |
| 1492 | }, | 1492 | }, |
| 1493 | registryLayer, | 1493 | layerRegistry, |
| 1494 | ), | 1494 | ), |
| 1495 | ), | 1495 | ), |
| 1496 | ); | 1496 | ); |
| ... | @@ -1505,7 +1505,7 @@ describe("orchestration v2 provider switching", () => { | ... | @@ -1505,7 +1505,7 @@ describe("orchestration v2 provider switching", () => { |
| 1505 | const cwd = yield* checkpointWorkspace("queued-steer-provider-switch"); | 1505 | const cwd = yield* checkpointWorkspace("queued-steer-provider-switch"); |
| 1506 | const capturedTurns = yield* Ref.make<ReadonlyArray<CapturedTurn>>([]); | 1506 | const capturedTurns = yield* Ref.make<ReadonlyArray<CapturedTurn>>([]); |
| 1507 | const started = yield* Deferred.make<void>(); | 1507 | const started = yield* Deferred.make<void>(); |
| 1508 | const registryLayer = ProviderAdapterRegistry.makeLayer([ | 1508 | const layerRegistry = ProviderAdapterRegistry.layerFromAdapters([ |
| 1509 | makeTestAdapter({ | 1509 | makeTestAdapter({ |
| 1510 | instanceId: CODEX_MODEL_SELECTION.instanceId, | 1510 | instanceId: CODEX_MODEL_SELECTION.instanceId, |
| 1511 | driver: CODEX_DRIVER, | 1511 | driver: CODEX_DRIVER, |
| ... | @@ -1670,7 +1670,7 @@ describe("orchestration v2 provider switching", () => { | ... | @@ -1670,7 +1670,7 @@ describe("orchestration v2 provider switching", () => { |
| 1670 | return yield* orchestrator.getThreadProjection(queuedThreadId); | 1670 | return yield* orchestrator.getThreadProjection(queuedThreadId); |
| 1671 | }).pipe( | 1671 | }).pipe( |
| 1672 | Effect.provide( | 1672 | Effect.provide( |
| 1673 | makeOrchestratorV2ReplayLayerWithRegistry( | 1673 | ProviderReplayHarness.layerWithRegistry( |
| 1674 | { | 1674 | { |
| 1675 | name: "queued-steer-provider-switch", | 1675 | name: "queued-steer-provider-switch", |
| 1676 | runtimePolicyOverride: { | 1676 | runtimePolicyOverride: { |
| ... | @@ -1683,7 +1683,7 @@ describe("orchestration v2 provider switching", () => { | ... | @@ -1683,7 +1683,7 @@ describe("orchestration v2 provider switching", () => { |
| 1683 | }, | 1683 | }, |
| 1684 | }, | 1684 | }, |
| 1685 | }, | 1685 | }, |
| 1686 | registryLayer, | 1686 | layerRegistry, |
| 1687 | ), | 1687 | ), |
| 1688 | ), | 1688 | ), |
| 1689 | ); | 1689 | ); |
| ... | @@ -1744,7 +1744,7 @@ describe("orchestration v2 provider switching", () => { | ... | @@ -1744,7 +1744,7 @@ describe("orchestration v2 provider switching", () => { |
| 1744 | capturedTurns, | 1744 | capturedTurns, |
| 1745 | }), | 1745 | }), |
| 1746 | ]; | 1746 | ]; |
| 1747 | const registryLayer = Layer.succeed( | 1747 | const layerRegistry = Layer.succeed( |
| 1748 | ProviderAdapterRegistry.ProviderAdapterRegistryV2, | 1748 | ProviderAdapterRegistry.ProviderAdapterRegistryV2, |
| 1749 | ProviderAdapterRegistry.ProviderAdapterRegistryV2.of({ | 1749 | ProviderAdapterRegistry.ProviderAdapterRegistryV2.of({ |
| 1750 | get: (instanceId) => { | 1750 | get: (instanceId) => { |
| ... | @@ -1861,7 +1861,7 @@ describe("orchestration v2 provider switching", () => { | ... | @@ -1861,7 +1861,7 @@ describe("orchestration v2 provider switching", () => { |
| 1861 | return delivered; | 1861 | return delivered; |
| 1862 | }).pipe( | 1862 | }).pipe( |
| 1863 | Effect.provide( | 1863 | Effect.provide( |
| 1864 | makeOrchestratorV2ReplayLayerWithRegistry( | 1864 | ProviderReplayHarness.layerWithRegistry( |
| 1865 | { | 1865 | { |
| 1866 | name: "queued-account-switch", | 1866 | name: "queued-account-switch", |
| 1867 | runtimePolicyOverride: { | 1867 | runtimePolicyOverride: { |
| ... | @@ -1874,7 +1874,7 @@ describe("orchestration v2 provider switching", () => { | ... | @@ -1874,7 +1874,7 @@ describe("orchestration v2 provider switching", () => { |
| 1874 | }, | 1874 | }, |
| 1875 | }, | 1875 | }, |
| 1876 | }, | 1876 | }, |
| 1877 | registryLayer, | 1877 | layerRegistry, |
| 1878 | ), | 1878 | ), |
| 1879 | ), | 1879 | ), |
| 1880 | ); | 1880 | ); |
| ... | @@ -1900,7 +1900,7 @@ describe("orchestration v2 provider switching", () => { | ... | @@ -1900,7 +1900,7 @@ describe("orchestration v2 provider switching", () => { |
| 1900 | const cwd = yield* checkpointWorkspace("queued-provider-switch"); | 1900 | const cwd = yield* checkpointWorkspace("queued-provider-switch"); |
| 1901 | const capturedTurns = yield* Ref.make<ReadonlyArray<CapturedTurn>>([]); | 1901 | const capturedTurns = yield* Ref.make<ReadonlyArray<CapturedTurn>>([]); |
| 1902 | const started = yield* Deferred.make<void>(); | 1902 | const started = yield* Deferred.make<void>(); |
| 1903 | const registryLayer = ProviderAdapterRegistry.makeLayer([ | 1903 | const layerRegistry = ProviderAdapterRegistry.layerFromAdapters([ |
| 1904 | makeTestAdapter({ | 1904 | makeTestAdapter({ |
| 1905 | instanceId: ProviderInstanceId.make("codex"), | 1905 | instanceId: ProviderInstanceId.make("codex"), |
| 1906 | driver: CODEX_DRIVER, | 1906 | driver: CODEX_DRIVER, |
| ... | @@ -1924,8 +1924,8 @@ describe("orchestration v2 provider switching", () => { | ... | @@ -1924,8 +1924,8 @@ describe("orchestration v2 provider switching", () => { |
| 1924 | }), | 1924 | }), |
| 1925 | ]); | 1925 | ]); |
| 1926 | const queuedThreadId = ThreadId.make("thread:queued-provider-switch"); | 1926 | const queuedThreadId = ThreadId.make("thread:queued-provider-switch"); |
| 1927 | const databaseLayer = SqlitePersistenceMemory; | 1927 | const layerDatabase = SqlitePersistence.layerMemory; |
| 1928 | const outboxProvided = EffectOutbox.layer.pipe(Layer.provide(databaseLayer)); | 1928 | const layerOutboxProvided = EffectOutbox.layer.pipe(Layer.provide(layerDatabase)); |
| 1929 | const projection = yield* Effect.gen(function* () { | 1929 | const projection = yield* Effect.gen(function* () { |
| 1930 | const orchestrator = yield* Orchestrator.OrchestratorV2; | 1930 | const orchestrator = yield* Orchestrator.OrchestratorV2; |
| 1931 | const worker = yield* EffectWorker.OrchestrationEffectWorkerV2; | 1931 | const worker = yield* EffectWorker.OrchestrationEffectWorkerV2; |
| ... | @@ -2072,7 +2072,7 @@ describe("orchestration v2 provider switching", () => { | ... | @@ -2072,7 +2072,7 @@ describe("orchestration v2 provider switching", () => { |
| 2072 | }).pipe( | 2072 | }).pipe( |
| 2073 | Effect.provide( | 2073 | Effect.provide( |
| 2074 | Layer.merge( | 2074 | Layer.merge( |
| 2075 | makeOrchestratorV2ReplayLayerWithRegistry( | 2075 | ProviderReplayHarness.layerWithRegistry( |
| 2076 | { | 2076 | { |
| 2077 | name: "queued-provider-switch", | 2077 | name: "queued-provider-switch", |
| 2078 | runtimePolicyOverride: { | 2078 | runtimePolicyOverride: { |
| ... | @@ -2085,10 +2085,10 @@ describe("orchestration v2 provider switching", () => { | ... | @@ -2085,10 +2085,10 @@ describe("orchestration v2 provider switching", () => { |
| 2085 | }, | 2085 | }, |
| 2086 | }, | 2086 | }, |
| 2087 | }, | 2087 | }, |
| 2088 | registryLayer, | 2088 | layerRegistry, |
| 2089 | { databaseLayer }, | 2089 | { databaseLayer: layerDatabase }, |
| 2090 | ), | 2090 | ), |
| 2091 | outboxProvided, | 2091 | layerOutboxProvided, |
| 2092 | ), | 2092 | ), |
| 2093 | ), | 2093 | ), |
| 2094 | ); | 2094 | ); |
| ... | @@ -2150,7 +2150,7 @@ describe("orchestration v2 provider switching", () => { | ... | @@ -2150,7 +2150,7 @@ describe("orchestration v2 provider switching", () => { |
| 2150 | canConsumeHandoffSummaries: false, | 2150 | canConsumeHandoffSummaries: false, |
| 2151 | }, | 2151 | }, |
| 2152 | }; | 2152 | }; |
| 2153 | const registryLayer = ProviderAdapterRegistry.makeLayer([ | 2153 | const layerRegistry = ProviderAdapterRegistry.layerFromAdapters([ |
| 2154 | makeTestAdapter({ | 2154 | makeTestAdapter({ |
| 2155 | instanceId: CODEX_MODEL_SELECTION.instanceId, | 2155 | instanceId: CODEX_MODEL_SELECTION.instanceId, |
| 2156 | driver: CODEX_DRIVER, | 2156 | driver: CODEX_DRIVER, |
| ... | @@ -2299,7 +2299,7 @@ describe("orchestration v2 provider switching", () => { | ... | @@ -2299,7 +2299,7 @@ describe("orchestration v2 provider switching", () => { |
| 2299 | return yield* orchestrator.getThreadProjection(rejectedThreadId); | 2299 | return yield* orchestrator.getThreadProjection(rejectedThreadId); |
| 2300 | }).pipe( | 2300 | }).pipe( |
| 2301 | Effect.provide( | 2301 | Effect.provide( |
| 2302 | makeOrchestratorV2ReplayLayerWithRegistry( | 2302 | ProviderReplayHarness.layerWithRegistry( |
| 2303 | { | 2303 | { |
| 2304 | name: "queued-handoff-rejection", | 2304 | name: "queued-handoff-rejection", |
| 2305 | runtimePolicyOverride: { | 2305 | runtimePolicyOverride: { |
| ... | @@ -2312,7 +2312,7 @@ describe("orchestration v2 provider switching", () => { | ... | @@ -2312,7 +2312,7 @@ describe("orchestration v2 provider switching", () => { |
| 2312 | }, | 2312 | }, |
| 2313 | }, | 2313 | }, |
| 2314 | }, | 2314 | }, |
| 2315 | registryLayer, | 2315 | layerRegistry, |
| 2316 | ), | 2316 | ), |
| 2317 | ), | 2317 | ), |
| 2318 | ); | 2318 | ); |
| ... | @@ -2358,7 +2358,7 @@ describe("orchestration v2 provider switching", () => { | ... | @@ -2358,7 +2358,7 @@ describe("orchestration v2 provider switching", () => { |
| 2358 | const capturedTurns = yield* Ref.make<ReadonlyArray<CapturedTurn>>([]); | 2358 | const capturedTurns = yield* Ref.make<ReadonlyArray<CapturedTurn>>([]); |
| 2359 | const firstTurnStarted = yield* Deferred.make<void>(); | 2359 | const firstTurnStarted = yield* Deferred.make<void>(); |
| 2360 | const releaseFirstTurn = yield* Deferred.make<void>(); | 2360 | const releaseFirstTurn = yield* Deferred.make<void>(); |
| 2361 | const registryLayer = ProviderAdapterRegistry.makeLayer([ | 2361 | const layerRegistry = ProviderAdapterRegistry.layerFromAdapters([ |
| 2362 | makeTestAdapter({ | 2362 | makeTestAdapter({ |
| 2363 | instanceId: ProviderInstanceId.make("codex"), | 2363 | instanceId: ProviderInstanceId.make("codex"), |
| 2364 | driver: CODEX_DRIVER, | 2364 | driver: CODEX_DRIVER, |
| ... | @@ -2378,22 +2378,24 @@ describe("orchestration v2 provider switching", () => { | ... | @@ -2378,22 +2378,24 @@ describe("orchestration v2 provider switching", () => { |
| 2378 | capturedTurns, | 2378 | capturedTurns, |
| 2379 | }), | 2379 | }), |
| 2380 | ]); | 2380 | ]); |
| 2381 | const databaseLayer = SqlitePersistenceMemory; | 2381 | const layerDatabase = SqlitePersistence.layerMemory; |
| 2382 | const eventStoreProvided = EventStore.layer.pipe(Layer.provideMerge(databaseLayer)); | 2382 | const layerEventStoreProvided = EventStore.layer.pipe(Layer.provideMerge(layerDatabase)); |
| 2383 | const projectionStoreProvided = ProjectionStore.layer.pipe( | 2383 | const layerProjectionStoreProvided = ProjectionStore.layer.pipe( |
| 2384 | Layer.provideMerge(databaseLayer), | 2384 | Layer.provideMerge(layerDatabase), |
| 2385 | ); | 2385 | ); |
| 2386 | const storesProvided = Layer.mergeAll( | 2386 | const layerStoresProvided = Layer.mergeAll( |
| 2387 | databaseLayer, | 2387 | layerDatabase, |
| 2388 | eventStoreProvided, | 2388 | layerEventStoreProvided, |
| 2389 | projectionStoreProvided, | 2389 | layerProjectionStoreProvided, |
| 2390 | ); | 2390 | ); |
| 2391 | const eventSinkProvided = EventSink.layer.pipe(Layer.provide(storesProvided)); | 2391 | const layerEventSinkProvided = EventSink.layer.pipe(Layer.provide(layerStoresProvided)); |
| 2392 | const importerProvided = LegacyV1ThreadImporter.layer.pipe( | 2392 | const layerImporterProvided = LegacyV1ThreadImporter.layer.pipe( |
| 2393 | Layer.provide(Layer.mergeAll(storesProvided, eventSinkProvided)), | 2393 | Layer.provide(Layer.mergeAll(layerStoresProvided, layerEventSinkProvided)), |
| 2394 | ); | 2394 | ); |
| 2395 | const maintenanceProvided = ProjectionMaintenance.layer.pipe(Layer.provide(storesProvided)); | 2395 | const layerMaintenanceProvided = ProjectionMaintenance.layer.pipe( |
| 2396 | const orchestratorProvided = makeOrchestratorV2ReplayLayerWithRegistry( | 2396 | Layer.provide(layerStoresProvided), |
| 2397 | ); | ||
| 2398 | const layerOrchestratorProvided = ProviderReplayHarness.layerWithRegistry( | ||
| 2397 | { | 2399 | { |
| 2398 | name: "provider-switch-legacy-import", | 2400 | name: "provider-switch-legacy-import", |
| 2399 | runtimePolicyOverride: { | 2401 | runtimePolicyOverride: { |
| ... | @@ -2406,14 +2408,14 @@ describe("orchestration v2 provider switching", () => { | ... | @@ -2406,14 +2408,14 @@ describe("orchestration v2 provider switching", () => { |
| 2406 | }, | 2408 | }, |
| 2407 | }, | 2409 | }, |
| 2408 | }, | 2410 | }, |
| 2409 | registryLayer, | 2411 | layerRegistry, |
| 2410 | { databaseLayer }, | 2412 | { databaseLayer: layerDatabase }, |
| 2411 | ); | 2413 | ); |
| 2412 | const testLayer = Layer.mergeAll( | 2414 | const layerTest = Layer.mergeAll( |
| 2413 | storesProvided, | 2415 | layerStoresProvided, |
| 2414 | importerProvided, | 2416 | layerImporterProvided, |
| 2415 | maintenanceProvided, | 2417 | layerMaintenanceProvided, |
| 2416 | orchestratorProvided, | 2418 | layerOrchestratorProvided, |
| 2417 | ); | 2419 | ); |
| 2418 | 2420 | ||
| 2419 | const projection = yield* Effect.gen(function* () { | 2421 | const projection = yield* Effect.gen(function* () { |
| ... | @@ -2586,7 +2588,7 @@ describe("orchestration v2 provider switching", () => { | ... | @@ -2586,7 +2588,7 @@ describe("orchestration v2 provider switching", () => { |
| 2586 | const rebuilt = yield* orchestrator.getThreadProjection(importedThreadId); | 2588 | const rebuilt = yield* orchestrator.getThreadProjection(importedThreadId); |
| 2587 | assert.deepEqual(rebuilt.attempts, beforeRebuild.attempts); | 2589 | assert.deepEqual(rebuilt.attempts, beforeRebuild.attempts); |
| 2588 | return rebuilt; | 2590 | return rebuilt; |
| 2589 | }).pipe(Effect.provide(testLayer)); | 2591 | }).pipe(Effect.provide(layerTest)); |
| 2590 | 2592 | ||
| 2591 | const turns = yield* Ref.get(capturedTurns); | 2593 | const turns = yield* Ref.get(capturedTurns); |
| 2592 | assert.deepEqual( | 2594 | assert.deepEqual( |
| ... | @@ -2644,7 +2646,7 @@ describe("orchestration v2 provider switching", () => { | ... | @@ -2644,7 +2646,7 @@ describe("orchestration v2 provider switching", () => { |
| 2644 | const cwd = yield* checkpointWorkspace("provider-switch"); | 2646 | const cwd = yield* checkpointWorkspace("provider-switch"); |
| 2645 | const capturedTurns = yield* Ref.make<ReadonlyArray<CapturedTurn>>([]); | 2647 | const capturedTurns = yield* Ref.make<ReadonlyArray<CapturedTurn>>([]); |
| 2646 | const codexNativeThreadGeneration = yield* Ref.make(0); | 2648 | const codexNativeThreadGeneration = yield* Ref.make(0); |
| 2647 | const registryLayer = ProviderAdapterRegistry.makeLayer([ | 2649 | const layerRegistry = ProviderAdapterRegistry.layerFromAdapters([ |
| 2648 | makeTestAdapter({ | 2650 | makeTestAdapter({ |
| 2649 | instanceId: ProviderInstanceId.make("codex"), | 2651 | instanceId: ProviderInstanceId.make("codex"), |
| 2650 | driver: CODEX_DRIVER, | 2652 | driver: CODEX_DRIVER, |
| ... | @@ -2753,7 +2755,7 @@ describe("orchestration v2 provider switching", () => { | ... | @@ -2753,7 +2755,7 @@ describe("orchestration v2 provider switching", () => { |
| 2753 | return yield* waitForIdle(threadId); | 2755 | return yield* waitForIdle(threadId); |
| 2754 | }).pipe( | 2756 | }).pipe( |
| 2755 | Effect.provide( | 2757 | Effect.provide( |
| 2756 | makeOrchestratorV2ReplayLayerWithRegistry( | 2758 | ProviderReplayHarness.layerWithRegistry( |
| 2757 | { | 2759 | { |
| 2758 | name: "provider-switch", | 2760 | name: "provider-switch", |
| 2759 | runtimePolicyOverride: { | 2761 | runtimePolicyOverride: { |
| ... | @@ -2766,7 +2768,7 @@ describe("orchestration v2 provider switching", () => { | ... | @@ -2766,7 +2768,7 @@ describe("orchestration v2 provider switching", () => { |
| 2766 | }, | 2768 | }, |
| 2767 | }, | 2769 | }, |
| 2768 | }, | 2770 | }, |
| 2769 | registryLayer, | 2771 | layerRegistry, |
| 2770 | ), | 2772 | ), |
| 2771 | ), | 2773 | ), |
| 2772 | ); | 2774 | ); |
| ... | @@ -2856,7 +2858,7 @@ describe("orchestration v2 provider switching", () => { | ... | @@ -2856,7 +2858,7 @@ describe("orchestration v2 provider switching", () => { |
| 2856 | const targetPrompt = "What release color did we choose?"; | 2858 | const targetPrompt = "What release color did we choose?"; |
| 2857 | const cwd = yield* checkpointWorkspace("cross-provider-fork"); | 2859 | const cwd = yield* checkpointWorkspace("cross-provider-fork"); |
| 2858 | const capturedTurns = yield* Ref.make<ReadonlyArray<CapturedTurn>>([]); | 2860 | const capturedTurns = yield* Ref.make<ReadonlyArray<CapturedTurn>>([]); |
| 2859 | const registryLayer = ProviderAdapterRegistry.makeLayer([ | 2861 | const layerRegistry = ProviderAdapterRegistry.layerFromAdapters([ |
| 2860 | makeTestAdapter({ | 2862 | makeTestAdapter({ |
| 2861 | instanceId: ProviderInstanceId.make("codex"), | 2863 | instanceId: ProviderInstanceId.make("codex"), |
| 2862 | driver: CODEX_DRIVER, | 2864 | driver: CODEX_DRIVER, |
| ... | @@ -2935,7 +2937,7 @@ describe("orchestration v2 provider switching", () => { | ... | @@ -2935,7 +2937,7 @@ describe("orchestration v2 provider switching", () => { |
| 2935 | return yield* waitForIdle(targetThreadId); | 2937 | return yield* waitForIdle(targetThreadId); |
| 2936 | }).pipe( | 2938 | }).pipe( |
| 2937 | Effect.provide( | 2939 | Effect.provide( |
| 2938 | makeOrchestratorV2ReplayLayerWithRegistry( | 2940 | ProviderReplayHarness.layerWithRegistry( |
| 2939 | { | 2941 | { |
| 2940 | name: "cross-provider-fork", | 2942 | name: "cross-provider-fork", |
| 2941 | runtimePolicyOverride: { | 2943 | runtimePolicyOverride: { |
| ... | @@ -2948,7 +2950,7 @@ describe("orchestration v2 provider switching", () => { | ... | @@ -2948,7 +2950,7 @@ describe("orchestration v2 provider switching", () => { |
| 2948 | }, | 2950 | }, |
| 2949 | }, | 2951 | }, |
| 2950 | }, | 2952 | }, |
| 2951 | registryLayer, | 2953 | layerRegistry, |
| 2952 | ), | 2954 | ), |
| 2953 | ), | 2955 | ), |
| 2954 | ); | 2956 | ); |
| ... | @@ -2996,7 +2998,7 @@ describe("orchestration v2 provider switching", () => { | ... | @@ -2996,7 +2998,7 @@ describe("orchestration v2 provider switching", () => { |
| 2996 | const targetPrompt = "What deployment marker did we choose?"; | 2998 | const targetPrompt = "What deployment marker did we choose?"; |
| 2997 | const cwd = yield* checkpointWorkspace("cursor-portable-fork"); | 2999 | const cwd = yield* checkpointWorkspace("cursor-portable-fork"); |
| 2998 | const capturedTurns = yield* Ref.make<ReadonlyArray<CapturedTurn>>([]); | 3000 | const capturedTurns = yield* Ref.make<ReadonlyArray<CapturedTurn>>([]); |
| 2999 | const registryLayer = ProviderAdapterRegistry.makeLayer([ | 3001 | const layerRegistry = ProviderAdapterRegistry.layerFromAdapters([ |
| 3000 | makeTestAdapter({ | 3002 | makeTestAdapter({ |
| 3001 | instanceId: ProviderInstanceId.make("cursor"), | 3003 | instanceId: ProviderInstanceId.make("cursor"), |
| 3002 | driver: CURSOR_DRIVER, | 3004 | driver: CURSOR_DRIVER, |
| ... | @@ -3071,7 +3073,7 @@ describe("orchestration v2 provider switching", () => { | ... | @@ -3071,7 +3073,7 @@ describe("orchestration v2 provider switching", () => { |
| 3071 | return yield* waitForIdle(targetThreadId); | 3073 | return yield* waitForIdle(targetThreadId); |
| 3072 | }).pipe( | 3074 | }).pipe( |
| 3073 | Effect.provide( | 3075 | Effect.provide( |
| 3074 | makeOrchestratorV2ReplayLayerWithRegistry( | 3076 | ProviderReplayHarness.layerWithRegistry( |
| 3075 | { | 3077 | { |
| 3076 | name: "cursor-portable-fork", | 3078 | name: "cursor-portable-fork", |
| 3077 | runtimePolicyOverride: { | 3079 | runtimePolicyOverride: { |
| ... | @@ -3084,7 +3086,7 @@ describe("orchestration v2 provider switching", () => { | ... | @@ -3084,7 +3086,7 @@ describe("orchestration v2 provider switching", () => { |
| 3084 | }, | 3086 | }, |
| 3085 | }, | 3087 | }, |
| 3086 | }, | 3088 | }, |
| 3087 | registryLayer, | 3089 | layerRegistry, |
| 3088 | ), | 3090 | ), |
| 3089 | ), | 3091 | ), |
| 3090 | ); | 3092 | ); |
| ... | @@ -3131,7 +3133,7 @@ describe("orchestration v2 provider switching", () => { | ... | @@ -3131,7 +3133,7 @@ describe("orchestration v2 provider switching", () => { |
| 3131 | const mergePrompt = "Report all three remembered markers."; | 3133 | const mergePrompt = "Report all three remembered markers."; |
| 3132 | const cwd = yield* checkpointWorkspace("cross-provider-merge"); | 3134 | const cwd = yield* checkpointWorkspace("cross-provider-merge"); |
| 3133 | const capturedTurns = yield* Ref.make<ReadonlyArray<CapturedTurn>>([]); | 3135 | const capturedTurns = yield* Ref.make<ReadonlyArray<CapturedTurn>>([]); |
| 3134 | const registryLayer = ProviderAdapterRegistry.makeLayer([ | 3136 | const layerRegistry = ProviderAdapterRegistry.layerFromAdapters([ |
| 3135 | makeTestAdapter({ | 3137 | makeTestAdapter({ |
| 3136 | instanceId: ProviderInstanceId.make("codex"), | 3138 | instanceId: ProviderInstanceId.make("codex"), |
| 3137 | driver: CODEX_DRIVER, | 3139 | driver: CODEX_DRIVER, |
| ... | @@ -3283,7 +3285,7 @@ describe("orchestration v2 provider switching", () => { | ... | @@ -3283,7 +3285,7 @@ describe("orchestration v2 provider switching", () => { |
| 3283 | return yield* waitForIdle(sourceThreadId); | 3285 | return yield* waitForIdle(sourceThreadId); |
| 3284 | }).pipe( | 3286 | }).pipe( |
| 3285 | Effect.provide( | 3287 | Effect.provide( |
| 3286 | makeOrchestratorV2ReplayLayerWithRegistry( | 3288 | ProviderReplayHarness.layerWithRegistry( |
| 3287 | { | 3289 | { |
| 3288 | name: "cross-provider-merge", | 3290 | name: "cross-provider-merge", |
| 3289 | runtimePolicyOverride: { | 3291 | runtimePolicyOverride: { |
| ... | @@ -3296,7 +3298,7 @@ describe("orchestration v2 provider switching", () => { | ... | @@ -3296,7 +3298,7 @@ describe("orchestration v2 provider switching", () => { |
| 3296 | }, | 3298 | }, |
| 3297 | }, | 3299 | }, |
| 3298 | }, | 3300 | }, |
| 3299 | registryLayer, | 3301 | layerRegistry, |
| 3300 | ), | 3302 | ), |
| 3301 | ), | 3303 | ), |
| 3302 | ); | 3304 | ); |
| ... | @@ -3354,7 +3356,7 @@ describe("orchestration v2 provider switching", () => { | ... | @@ -3354,7 +3356,7 @@ describe("orchestration v2 provider switching", () => { |
| 3354 | } satisfies ModelSelection; | 3356 | } satisfies ModelSelection; |
| 3355 | const cwd = yield* checkpointWorkspace("custom-codex-instances"); | 3357 | const cwd = yield* checkpointWorkspace("custom-codex-instances"); |
| 3356 | const capturedTurns = yield* Ref.make<ReadonlyArray<CapturedTurn>>([]); | 3358 | const capturedTurns = yield* Ref.make<ReadonlyArray<CapturedTurn>>([]); |
| 3357 | const registryLayer = ProviderAdapterRegistry.makeLayer([ | 3359 | const layerRegistry = ProviderAdapterRegistry.layerFromAdapters([ |
| 3358 | makeTestAdapter({ | 3360 | makeTestAdapter({ |
| 3359 | instanceId: personalSelection.instanceId, | 3361 | instanceId: personalSelection.instanceId, |
| 3360 | driver: CODEX_DRIVER, | 3362 | driver: CODEX_DRIVER, |
| ... | @@ -3413,7 +3415,7 @@ describe("orchestration v2 provider switching", () => { | ... | @@ -3413,7 +3415,7 @@ describe("orchestration v2 provider switching", () => { |
| 3413 | ]); | 3415 | ]); |
| 3414 | }).pipe( | 3416 | }).pipe( |
| 3415 | Effect.provide( | 3417 | Effect.provide( |
| 3416 | makeOrchestratorV2ReplayLayerWithRegistry( | 3418 | ProviderReplayHarness.layerWithRegistry( |
| 3417 | { | 3419 | { |
| 3418 | name: "custom-codex-instances", | 3420 | name: "custom-codex-instances", |
| 3419 | runtimePolicyOverride: { | 3421 | runtimePolicyOverride: { |
| ... | @@ -3426,7 +3428,7 @@ describe("orchestration v2 provider switching", () => { | ... | @@ -3426,7 +3428,7 @@ describe("orchestration v2 provider switching", () => { |
| 3426 | }, | 3428 | }, |
| 3427 | }, | 3429 | }, |
| 3428 | }, | 3430 | }, |
| 3429 | registryLayer, | 3431 | layerRegistry, |
| 3430 | ), | 3432 | ), |
| 3431 | ), | 3433 | ), |
| 3432 | ); | 3434 | ); |
apps/server/src/persistence/Layers/OrchestrationCommandReceipts.ts+1-1| ... | @@ -120,7 +120,7 @@ const makeOrchestrationCommandReceiptRepository = Effect.gen(function* () { | ... | @@ -120,7 +120,7 @@ const makeOrchestrationCommandReceiptRepository = Effect.gen(function* () { |
| 120 | } satisfies OrchestrationCommandReceipts.OrchestrationCommandReceiptRepositoryShape; | 120 | } satisfies OrchestrationCommandReceipts.OrchestrationCommandReceiptRepositoryShape; |
| 121 | }); | 121 | }); |
| 122 | 122 | ||
| 123 | export const OrchestrationCommandReceiptRepositoryLive = Layer.effect( | 123 | export const layer = Layer.effect( |
| 124 | OrchestrationCommandReceipts.OrchestrationCommandReceiptRepository, | 124 | OrchestrationCommandReceipts.OrchestrationCommandReceiptRepository, |
| 125 | makeOrchestrationCommandReceiptRepository, | 125 | makeOrchestrationCommandReceiptRepository, |
| 126 | ); | 126 | ); |
apps/server/src/persistence/Layers/OrchestrationEventStore.sequence.test.ts+7-7| ... | @@ -11,11 +11,11 @@ import * as SqlClient from "effect/sql/SqlClient"; | ... | @@ -11,11 +11,11 @@ import * as SqlClient from "effect/sql/SqlClient"; |
| 11 | import { runMigrations } from "../Migrations.ts"; | 11 | import { runMigrations } from "../Migrations.ts"; |
| 12 | import * as NodeSqliteClient from "@t3tools/shared/nodeSqliteClient"; | 12 | import * as NodeSqliteClient from "@t3tools/shared/nodeSqliteClient"; |
| 13 | import * as OrchestrationEventStore from "../Services/OrchestrationEventStore.ts"; | 13 | import * as OrchestrationEventStore from "../Services/OrchestrationEventStore.ts"; |
| 14 | import { OrchestrationEventStoreLive } from "./OrchestrationEventStore.ts"; | 14 | import * as OrchestrationEventStoreLayer from "./OrchestrationEventStore.ts"; |
| 15 | import { SqlitePersistenceMemory } from "./Sqlite.ts"; | 15 | import * as SqlitePersistence from "./Sqlite.ts"; |
| 16 | 16 | ||
| 17 | const eventStoreLayer = OrchestrationEventStoreLive.pipe( | 17 | const layerEventStore = OrchestrationEventStoreLayer.layer.pipe( |
| 18 | Layer.provideMerge(SqlitePersistenceMemory), | 18 | Layer.provideMerge(SqlitePersistence.layerMemory), |
| 19 | ); | 19 | ); |
| 20 | const occurredAt = "2026-09-03T00:00:00.000Z"; | 20 | const occurredAt = "2026-09-03T00:00:00.000Z"; |
| 21 | const encodeJson = Schema.encodeSync(Schema.fromJsonString(Schema.Unknown)); | 21 | const encodeJson = Schema.encodeSync(Schema.fromJsonString(Schema.Unknown)); |
| ... | @@ -112,7 +112,7 @@ it.effect("keeps application and scoped agent high-water marks separate from leg | ... | @@ -112,7 +112,7 @@ it.effect("keeps application and scoped agent high-water marks separate from leg |
| 112 | assert.equal(yield* store.latestApplicationSequence, legacyProject.sequence); | 112 | assert.equal(yield* store.latestApplicationSequence, legacyProject.sequence); |
| 113 | assert.equal(yield* store.latestAgentSequence(), 0); | 113 | assert.equal(yield* store.latestAgentSequence(), 0); |
| 114 | assert.equal(yield* store.latestAgentSequence(target), 0); | 114 | assert.equal(yield* store.latestAgentSequence(target), 0); |
| 115 | }).pipe(Effect.provide(Layer.fresh(eventStoreLayer))), | 115 | }).pipe(Effect.provide(Layer.fresh(layerEventStore))), |
| 116 | ); | 116 | ); |
| 117 | 117 | ||
| 118 | it.effect( | 118 | it.effect( |
| ... | @@ -206,7 +206,7 @@ it.effect( | ... | @@ -206,7 +206,7 @@ it.effect( |
| 206 | [...catchUp.map((row) => row.sequence), liveSequence], | 206 | [...catchUp.map((row) => row.sequence), liveSequence], |
| 207 | ); | 207 | ); |
| 208 | assert.isAbove(liveSequence, highWater); | 208 | assert.isAbove(liveSequence, highWater); |
| 209 | }).pipe(Effect.provide(Layer.fresh(eventStoreLayer))), | 209 | }).pipe(Effect.provide(Layer.fresh(layerEventStore))), |
| 210 | ); | 210 | ); |
| 211 | 211 | ||
| 212 | it.effect("uses indexed high-water lookups for populated history without OR scans", () => | 212 | it.effect("uses indexed high-water lookups for populated history without OR scans", () => |
| ... | @@ -267,7 +267,7 @@ it.effect("uses indexed high-water lookups for populated history without OR scan | ... | @@ -267,7 +267,7 @@ it.effect("uses indexed high-water lookups for populated history without OR scan |
| 267 | } | 267 | } |
| 268 | }).pipe( | 268 | }).pipe( |
| 269 | Effect.provide( | 269 | Effect.provide( |
| 270 | OrchestrationEventStoreLive.pipe( | 270 | OrchestrationEventStoreLayer.layer.pipe( |
| 271 | Layer.provideMerge(NodeSqliteClient.layer({ filename: ":memory:" })), | 271 | Layer.provideMerge(NodeSqliteClient.layer({ filename: ":memory:" })), |
| 272 | ), | 272 | ), |
| 273 | ), | 273 | ), |
apps/server/src/persistence/Layers/OrchestrationEventStore.test.ts+8-6| ... | @@ -27,13 +27,15 @@ import { | ... | @@ -27,13 +27,15 @@ import { |
| 27 | import { PersistenceDecodeError } from "../Errors.ts"; | 27 | import { PersistenceDecodeError } from "../Errors.ts"; |
| 28 | import { toShellApplicationEvent } from "../../orchestration-v2/ShellStream.ts"; | 28 | import { toShellApplicationEvent } from "../../orchestration-v2/ShellStream.ts"; |
| 29 | import * as OrchestrationEventStore from "../Services/OrchestrationEventStore.ts"; | 29 | import * as OrchestrationEventStore from "../Services/OrchestrationEventStore.ts"; |
| 30 | import { OrchestrationEventStoreLive } from "./OrchestrationEventStore.ts"; | 30 | import * as OrchestrationEventStoreLayer from "./OrchestrationEventStore.ts"; |
| 31 | import { SqlitePersistenceMemory } from "./Sqlite.ts"; | 31 | import * as SqlitePersistence from "./Sqlite.ts"; |
| 32 | const isPersistenceDecodeError = Schema.is(PersistenceDecodeError); | 32 | const isPersistenceDecodeError = Schema.is(PersistenceDecodeError); |
| 33 | const isLiveStreamBufferError = Schema.is(LiveStreamBufferError); | 33 | const isLiveStreamBufferError = Schema.is(LiveStreamBufferError); |
| 34 | 34 | ||
| 35 | const TestLayer = OrchestrationEventStoreLive.pipe(Layer.provideMerge(SqlitePersistenceMemory)); | 35 | const layerTest = OrchestrationEventStoreLayer.layer.pipe( |
| 36 | const layer = it.layer(TestLayer); | 36 | Layer.provideMerge(SqlitePersistence.layerMemory), |
| 37 | ); | ||
| 38 | const layer = it.layer(layerTest); | ||
| 37 | 39 | ||
| 38 | layer("OrchestrationEventStore", (it) => { | 40 | layer("OrchestrationEventStore", (it) => { |
| 39 | it.effect("retains only shell metadata from oversized replay and live application events", () => | 41 | it.effect("retains only shell metadata from oversized replay and live application events", () => |
| ... | @@ -464,7 +466,7 @@ it.effect.each(["high-water", "replay"] as const)( | ... | @@ -464,7 +466,7 @@ it.effect.each(["high-water", "replay"] as const)( |
| 464 | assert.isTrue(isLiveStreamBufferError(result.failure.cause)); | 466 | assert.isTrue(isLiveStreamBufferError(result.failure.cause)); |
| 465 | } | 467 | } |
| 466 | }), | 468 | }), |
| 467 | ).pipe(Effect.provide(Layer.fresh(TestLayer))), | 469 | ).pipe(Effect.provide(Layer.fresh(layerTest))), |
| 468 | ); | 470 | ); |
| 469 | 471 | ||
| 470 | it.effect("releases consumed application replay pages", () => | 472 | it.effect("releases consumed application replay pages", () => |
| ... | @@ -516,5 +518,5 @@ it.effect("releases consumed application replay pages", () => | ... | @@ -516,5 +518,5 @@ it.effect("releases consumed application replay pages", () => |
| 516 | ), | 518 | ), |
| 517 | ); | 519 | ); |
| 518 | assert.equal(count, 1501); | 520 | assert.equal(count, 1501); |
| 519 | }).pipe(Effect.provide(TestLayer)), | 521 | }).pipe(Effect.provide(layerTest)), |
| 520 | ); | 522 | ); |
apps/server/src/persistence/Layers/OrchestrationEventStore.ts+1-4| ... | @@ -606,7 +606,4 @@ const makeEventStore = Effect.gen(function* () { | ... | @@ -606,7 +606,4 @@ const makeEventStore = Effect.gen(function* () { |
| 606 | } satisfies OrchestrationEventStore.OrchestrationEventStoreShape; | 606 | } satisfies OrchestrationEventStore.OrchestrationEventStoreShape; |
| 607 | }); | 607 | }); |
| 608 | 608 | ||
| 609 | export const OrchestrationEventStoreLive = Layer.effect( | 609 | export const layer = Layer.effect(OrchestrationEventStore.OrchestrationEventStore, makeEventStore); |
| 610 | OrchestrationEventStore.OrchestrationEventStore, | ||
| 611 | makeEventStore, | ||
| 612 | ); |
apps/server/src/persistence/Layers/Sqlite.test.ts+5-8| ... | @@ -10,11 +10,8 @@ import * as Effect from "effect/Effect"; | ... | @@ -10,11 +10,8 @@ import * as Effect from "effect/Effect"; |
| 10 | import * as Layer from "effect/Layer"; | 10 | import * as Layer from "effect/Layer"; |
| 11 | import * as SqlClient from "effect/sql/SqlClient"; | 11 | import * as SqlClient from "effect/sql/SqlClient"; |
| 12 | 12 | ||
| 13 | import { | 13 | import { WAL_SIZE_LIMIT_BYTES } from "./Sqlite.ts"; |
| 14 | SqlitePersistenceMemory, | 14 | import * as SqlitePersistence from "./Sqlite.ts"; |
| 15 | WAL_SIZE_LIMIT_BYTES, | ||
| 16 | makeSqlitePersistenceLive, | ||
| 17 | } from "./Sqlite.ts"; | ||
| 18 | 15 | ||
| 19 | const lockHolderSource = ` | 16 | const lockHolderSource = ` |
| 20 | const { DatabaseSync } = require("node:sqlite"); | 17 | const { DatabaseSync } = require("node:sqlite"); |
| ... | @@ -56,7 +53,7 @@ it.effect("waits out a concurrent writer instead of failing with SQLITE_BUSY", ( | ... | @@ -56,7 +53,7 @@ it.effect("waits out a concurrent writer instead of failing with SQLITE_BUSY", ( |
| 56 | const rows = yield* sql<{ readonly id: number }>`SELECT id FROM busy_probe`; | 53 | const rows = yield* sql<{ readonly id: number }>`SELECT id FROM busy_probe`; |
| 57 | assert.deepEqual([...rows], [{ id: 1 }]); | 54 | assert.deepEqual([...rows], [{ id: 1 }]); |
| 58 | }).pipe( | 55 | }).pipe( |
| 59 | Effect.provide(makeSqlitePersistenceLive(dbPath).pipe(Layer.provide(NodeServices.layer))), | 56 | Effect.provide(SqlitePersistence.layerFromPath(dbPath).pipe(Layer.provide(NodeServices.layer))), |
| 60 | Effect.ensuring(Effect.sync(() => NodeFS.rmSync(tempDir, { recursive: true, force: true }))), | 57 | Effect.ensuring(Effect.sync(() => NodeFS.rmSync(tempDir, { recursive: true, force: true }))), |
| 61 | ); | 58 | ); |
| 62 | }); | 59 | }); |
| ... | @@ -82,7 +79,7 @@ it.effect("shrinks the WAL file back to the size limit after a large write", () | ... | @@ -82,7 +79,7 @@ it.effect("shrinks the WAL file back to the size limit after a large write", () |
| 82 | yield* sql`INSERT INTO wal_probe(payload) VALUES (x'00')`; | 79 | yield* sql`INSERT INTO wal_probe(payload) VALUES (x'00')`; |
| 83 | assert.isAtMost(walFileSize(), WAL_SIZE_LIMIT_BYTES); | 80 | assert.isAtMost(walFileSize(), WAL_SIZE_LIMIT_BYTES); |
| 84 | }).pipe( | 81 | }).pipe( |
| 85 | Effect.provide(makeSqlitePersistenceLive(dbPath).pipe(Layer.provide(NodeServices.layer))), | 82 | Effect.provide(SqlitePersistence.layerFromPath(dbPath).pipe(Layer.provide(NodeServices.layer))), |
| 86 | Effect.ensuring(Effect.sync(() => NodeFS.rmSync(tempDir, { recursive: true, force: true }))), | 83 | Effect.ensuring(Effect.sync(() => NodeFS.rmSync(tempDir, { recursive: true, force: true }))), |
| 87 | ); | 84 | ); |
| 88 | }); | 85 | }); |
| ... | @@ -92,5 +89,5 @@ it.effect("applies busy_timeout in the shared persistence setup", () => | ... | @@ -92,5 +89,5 @@ it.effect("applies busy_timeout in the shared persistence setup", () => |
| 92 | const sql = yield* SqlClient.SqlClient; | 89 | const sql = yield* SqlClient.SqlClient; |
| 93 | const rows = yield* sql<{ readonly timeout: number }>`PRAGMA busy_timeout`; | 90 | const rows = yield* sql<{ readonly timeout: number }>`PRAGMA busy_timeout`; |
| 94 | assert.equal(rows[0]?.timeout, 5000); | 91 | assert.equal(rows[0]?.timeout, 5000); |
| 95 | }).pipe(Effect.provide(SqlitePersistenceMemory)), | 92 | }).pipe(Effect.provide(SqlitePersistence.layerMemory)), |
| 96 | ); | 93 | ); |
apps/server/src/persistence/Layers/Sqlite.ts+6-8| ... | @@ -12,7 +12,7 @@ import * as ServerConfig from "../../config.ts"; | ... | @@ -12,7 +12,7 @@ import * as ServerConfig from "../../config.ts"; |
| 12 | // Size the -wal file is cut back to on the first commit after a WAL reset. | 12 | // Size the -wal file is cut back to on the first commit after a WAL reset. |
| 13 | export const WAL_SIZE_LIMIT_BYTES = 32 * 1024 * 1024; | 13 | export const WAL_SIZE_LIMIT_BYTES = 32 * 1024 * 1024; |
| 14 | 14 | ||
| 15 | const setup = Layer.effectDiscard( | 15 | const layerSetup = Layer.effectDiscard( |
| 16 | Effect.gen(function* () { | 16 | Effect.gen(function* () { |
| 17 | const sql = yield* SqlClient.SqlClient; | 17 | const sql = yield* SqlClient.SqlClient; |
| 18 | // CLI and server write from separate processes; wait rather than fail with SQLITE_BUSY. | 18 | // CLI and server write from separate processes; wait rather than fail with SQLITE_BUSY. |
| ... | @@ -26,15 +26,13 @@ const setup = Layer.effectDiscard( | ... | @@ -26,15 +26,13 @@ const setup = Layer.effectDiscard( |
| 26 | }), | 26 | }), |
| 27 | ); | 27 | ); |
| 28 | 28 | ||
| 29 | export const makeSqlitePersistenceLive = Effect.fn("makeSqlitePersistenceLive")(function* ( | 29 | export const layerFromPath = Effect.fn("makeSqlitePersistenceLive")(function* (dbPath: string) { |
| 30 | dbPath: string, | ||
| 31 | ) { | ||
| 32 | const fs = yield* FileSystem.FileSystem; | 30 | const fs = yield* FileSystem.FileSystem; |
| 33 | const path = yield* Path.Path; | 31 | const path = yield* Path.Path; |
| 34 | yield* fs.makeDirectory(path.dirname(dbPath), { recursive: true }); | 32 | yield* fs.makeDirectory(path.dirname(dbPath), { recursive: true }); |
| 35 | 33 | ||
| 36 | return Layer.provideMerge( | 34 | return Layer.provideMerge( |
| 37 | setup, | 35 | layerSetup, |
| 38 | NodeSqliteClient.layer({ | 36 | NodeSqliteClient.layer({ |
| 39 | filename: dbPath, | 37 | filename: dbPath, |
| 40 | spanAttributes: { | 38 | spanAttributes: { |
| ... | @@ -45,8 +43,8 @@ export const makeSqlitePersistenceLive = Effect.fn("makeSqlitePersistenceLive")( | ... | @@ -45,8 +43,8 @@ export const makeSqlitePersistenceLive = Effect.fn("makeSqlitePersistenceLive")( |
| 45 | ); | 43 | ); |
| 46 | }, Layer.unwrap); | 44 | }, Layer.unwrap); |
| 47 | 45 | ||
| 48 | export const SqlitePersistenceMemory = Layer.provideMerge( | 46 | export const layerMemory = Layer.provideMerge( |
| 49 | setup, | 47 | layerSetup, |
| 50 | NodeSqliteClient.layer({ filename: ":memory:" }), | 48 | NodeSqliteClient.layer({ filename: ":memory:" }), |
| 51 | ); | 49 | ); |
| 52 | 50 | ||
| ... | @@ -54,6 +52,6 @@ export const layerConfig = Layer.unwrap( | ... | @@ -54,6 +52,6 @@ export const layerConfig = Layer.unwrap( |
| 54 | Effect.gen(function* () { | 52 | Effect.gen(function* () { |
| 55 | const { dbPath } = yield* ServerConfig.ServerConfig; | 53 | const { dbPath } = yield* ServerConfig.ServerConfig; |
| 56 | yield* initializeV2Database(dbPath); | 54 | yield* initializeV2Database(dbPath); |
| 57 | return makeSqlitePersistenceLive(dbPath); | 55 | return layerFromPath(dbPath); |
| 58 | }), | 56 | }), |
| 59 | ); | 57 | ); |
apps/server/src/persistence/RepositoryErrorCorrelation.test.ts+9-9| ... | @@ -8,7 +8,7 @@ import * as SqlClient from "effect/sql/SqlClient"; | ... | @@ -8,7 +8,7 @@ import * as SqlClient from "effect/sql/SqlClient"; |
| 8 | import * as AuthPairingLinks from "./AuthPairingLinks.ts"; | 8 | import * as AuthPairingLinks from "./AuthPairingLinks.ts"; |
| 9 | import * as AuthSessions from "./AuthSessions.ts"; | 9 | import * as AuthSessions from "./AuthSessions.ts"; |
| 10 | import * as PersistenceErrors from "./Errors.ts"; | 10 | import * as PersistenceErrors from "./Errors.ts"; |
| 11 | import { SqlitePersistenceMemory } from "./Layers/Sqlite.ts"; | 11 | import * as SqlitePersistence from "./Layers/Sqlite.ts"; |
| 12 | import * as ProviderSessionRuntime from "./ProviderSessionRuntime.ts"; | 12 | import * as ProviderSessionRuntime from "./ProviderSessionRuntime.ts"; |
| 13 | 13 | ||
| 14 | const issuedAt = DateTime.makeUnsafe("2026-06-20T00:00:00.000Z"); | 14 | const issuedAt = DateTime.makeUnsafe("2026-06-20T00:00:00.000Z"); |
| ... | @@ -16,12 +16,12 @@ const expiresAt = DateTime.makeUnsafe("2027-06-20T00:00:00.000Z"); | ... | @@ -16,12 +16,12 @@ const expiresAt = DateTime.makeUnsafe("2027-06-20T00:00:00.000Z"); |
| 16 | const now = DateTime.makeUnsafe("2026-06-21T00:00:00.000Z"); | 16 | const now = DateTime.makeUnsafe("2026-06-21T00:00:00.000Z"); |
| 17 | const scopes: ReadonlyArray<AuthEnvironmentScope> = ["access:read"]; | 17 | const scopes: ReadonlyArray<AuthEnvironmentScope> = ["access:read"]; |
| 18 | 18 | ||
| 19 | const authSessionLayer = AuthSessions.layer.pipe(Layer.provideMerge(SqlitePersistenceMemory)); | 19 | const layerAuthSession = AuthSessions.layer.pipe(Layer.provideMerge(SqlitePersistence.layerMemory)); |
| 20 | const authPairingLinkLayer = AuthPairingLinks.layer.pipe( | 20 | const layerAuthPairingLink = AuthPairingLinks.layer.pipe( |
| 21 | Layer.provideMerge(SqlitePersistenceMemory), | 21 | Layer.provideMerge(SqlitePersistence.layerMemory), |
| 22 | ); | 22 | ); |
| 23 | const providerSessionRuntimeLayer = ProviderSessionRuntime.layer.pipe( | 23 | const layerProviderSessionRuntime = ProviderSessionRuntime.layer.pipe( |
| 24 | Layer.provideMerge(SqlitePersistenceMemory), | 24 | Layer.provideMerge(SqlitePersistence.layerMemory), |
| 25 | ); | 25 | ); |
| 26 | 26 | ||
| 27 | describe("persistence error correlation", () => { | 27 | describe("persistence error correlation", () => { |
| ... | @@ -101,7 +101,7 @@ describe("persistence error correlation", () => { | ... | @@ -101,7 +101,7 @@ describe("persistence error correlation", () => { |
| 101 | "SQL error in AuthSessionRepository.revokeAllExcept:query", | 101 | "SQL error in AuthSessionRepository.revokeAllExcept:query", |
| 102 | ); | 102 | ); |
| 103 | assert.notInclude(revokeOtherError.message, DateTime.formatIso(now)); | 103 | assert.notInclude(revokeOtherError.message, DateTime.formatIso(now)); |
| 104 | }).pipe(Effect.provide(authSessionLayer)), | 104 | }).pipe(Effect.provide(layerAuthSession)), |
| 105 | ); | 105 | ); |
| 106 | 106 | ||
| 107 | it.effect("correlates pairing-link create and revoke failures by id only", () => | 107 | it.effect("correlates pairing-link create and revoke failures by id only", () => |
| ... | @@ -179,7 +179,7 @@ describe("persistence error correlation", () => { | ... | @@ -179,7 +179,7 @@ describe("persistence error correlation", () => { |
| 179 | assert.deepStrictEqual(revokeError.correlation, { pairingLinkId: id }); | 179 | assert.deepStrictEqual(revokeError.correlation, { pairingLinkId: id }); |
| 180 | assert.notInclude(revokeError.message, credential); | 180 | assert.notInclude(revokeError.message, credential); |
| 181 | assert.notInclude(revokeError.message, DateTime.formatIso(now)); | 181 | assert.notInclude(revokeError.message, DateTime.formatIso(now)); |
| 182 | }).pipe(Effect.provide(authPairingLinkLayer)), | 182 | }).pipe(Effect.provide(layerAuthPairingLink)), |
| 183 | ); | 183 | ); |
| 184 | 184 | ||
| 185 | it.effect("skips undecodable provider runtime rows and correlates SQL failures by thread", () => | 185 | it.effect("skips undecodable provider runtime rows and correlates SQL failures by thread", () => |
| ... | @@ -256,6 +256,6 @@ describe("persistence error correlation", () => { | ... | @@ -256,6 +256,6 @@ describe("persistence error correlation", () => { |
| 256 | ); | 256 | ); |
| 257 | assert.notInclude(sqlFailure.message, runtimePayload); | 257 | assert.notInclude(sqlFailure.message, runtimePayload); |
| 258 | assert.notInclude(sqlFailure.message, lastSeenAt); | 258 | assert.notInclude(sqlFailure.message, lastSeenAt); |
| 259 | }).pipe(Effect.provide(providerSessionRuntimeLayer)), | 259 | }).pipe(Effect.provide(layerProviderSessionRuntime)), |
| 260 | ); | 260 | ); |
| 261 | }); | 261 | }); |
apps/server/src/persistence/initializeV2Database.test.ts+11-11| ... | @@ -45,15 +45,15 @@ it.effect( | ... | @@ -45,15 +45,15 @@ it.effect( |
| 45 | yield* seed; | 45 | yield* seed; |
| 46 | const original = NodeFS.readFileSync(sourcePath); | 46 | const original = NodeFS.readFileSync(sourcePath); |
| 47 | const config = yield* ServerConfig.ServerConfig; | 47 | const config = yield* ServerConfig.ServerConfig; |
| 48 | const databaseLayer = SqlitePersistence.layerConfig.pipe( | 48 | const layerDatabase = SqlitePersistence.layerConfig.pipe( |
| 49 | Layer.provide(ServerConfig.layer({ ...config, dbPath: destinationPath })), | 49 | Layer.provide(ServerConfig.layer({ ...config, dbPath: destinationPath })), |
| 50 | ); | 50 | ); |
| 51 | const stores = Layer.mergeAll(EventStore.layer, ProjectionStore.layer).pipe( | 51 | const layerStores = Layer.mergeAll(EventStore.layer, ProjectionStore.layer).pipe( |
| 52 | Layer.provideMerge(databaseLayer), | 52 | Layer.provideMerge(layerDatabase), |
| 53 | ); | 53 | ); |
| 54 | const sink = EventSink.layer.pipe(Layer.provide(stores)); | 54 | const layerSink = EventSink.layer.pipe(Layer.provide(layerStores)); |
| 55 | const importer = LegacyV1ThreadImporter.layer.pipe( | 55 | const layerImporter = LegacyV1ThreadImporter.layer.pipe( |
| 56 | Layer.provideMerge(Layer.mergeAll(stores, sink)), | 56 | Layer.provideMerge(Layer.mergeAll(layerStores, layerSink)), |
| 57 | ); | 57 | ); |
| 58 | yield* Effect.gen(function* () { | 58 | yield* Effect.gen(function* () { |
| 59 | const sql = yield* SqlClient.SqlClient; | 59 | const sql = yield* SqlClient.SqlClient; |
| ... | @@ -81,7 +81,7 @@ it.effect( | ... | @@ -81,7 +81,7 @@ it.effect( |
| 81 | assert.isNotNull(imported[0]?.transcript_imported_at); | 81 | assert.isNotNull(imported[0]?.transcript_imported_at); |
| 82 | yield* sql`CREATE TABLE v2_work (text TEXT)`; | 82 | yield* sql`CREATE TABLE v2_work (text TEXT)`; |
| 83 | yield* sql`INSERT INTO v2_work VALUES ('Keep V2 work')`; | 83 | yield* sql`INSERT INTO v2_work VALUES ('Keep V2 work')`; |
| 84 | }).pipe(Effect.provide(importer)); | 84 | }).pipe(Effect.provide(layerImporter)); |
| 85 | assert.deepEqual(NodeFS.readFileSync(sourcePath), original); | 85 | assert.deepEqual(NodeFS.readFileSync(sourcePath), original); |
| 86 | const v1 = new NodeSqlite.DatabaseSync(sourcePath); | 86 | const v1 = new NodeSqlite.DatabaseSync(sourcePath); |
| 87 | try { | 87 | try { |
| ... | @@ -105,7 +105,7 @@ it.effect( | ... | @@ -105,7 +105,7 @@ it.effect( |
| 105 | const sql = yield* SqlClient.SqlClient; | 105 | const sql = yield* SqlClient.SqlClient; |
| 106 | assert.equal((yield* sql`SELECT text FROM v2_work`)[0]?.text, "Keep V2 work"); | 106 | assert.equal((yield* sql`SELECT text FROM v2_work`)[0]?.text, "Keep V2 work"); |
| 107 | assert.equal((yield* sql`SELECT title FROM projection_threads`)[0]?.title, "V1 thread"); | 107 | assert.equal((yield* sql`SELECT title FROM projection_threads`)[0]?.title, "V1 thread"); |
| 108 | }).pipe(Effect.provide(databaseLayer)); | 108 | }).pipe(Effect.provide(layerDatabase)); |
| 109 | }).pipe( | 109 | }).pipe( |
| 110 | Effect.provide( | 110 | Effect.provide( |
| 111 | ServerConfig.layerTest(directory, directory).pipe(Layer.provideMerge(NodeServices.layer)), | 111 | ServerConfig.layerTest(directory, directory).pipe(Layer.provideMerge(NodeServices.layer)), |
| ... | @@ -173,14 +173,14 @@ it.effect("starts fresh without V1 and never imports over existing V2 state", () | ... | @@ -173,14 +173,14 @@ it.effect("starts fresh without V1 and never imports over existing V2 state", () |
| 173 | const destinationPath = NodePath.join(directory, "userdata", "statev2.sqlite"); | 173 | const destinationPath = NodePath.join(directory, "userdata", "statev2.sqlite"); |
| 174 | return Effect.gen(function* () { | 174 | return Effect.gen(function* () { |
| 175 | const config = yield* ServerConfig.ServerConfig; | 175 | const config = yield* ServerConfig.ServerConfig; |
| 176 | const database = SqlitePersistence.layerConfig.pipe( | 176 | const layerDatabase = SqlitePersistence.layerConfig.pipe( |
| 177 | Layer.provide(ServerConfig.layer({ ...config, dbPath: destinationPath })), | 177 | Layer.provide(ServerConfig.layer({ ...config, dbPath: destinationPath })), |
| 178 | ); | 178 | ); |
| 179 | yield* Effect.gen(function* () { | 179 | yield* Effect.gen(function* () { |
| 180 | const sql = yield* SqlClient.SqlClient; | 180 | const sql = yield* SqlClient.SqlClient; |
| 181 | yield* sql`CREATE TABLE v2_work (text TEXT)`; | 181 | yield* sql`CREATE TABLE v2_work (text TEXT)`; |
| 182 | yield* sql`INSERT INTO v2_work VALUES ('fresh V2 work')`; | 182 | yield* sql`INSERT INTO v2_work VALUES ('fresh V2 work')`; |
| 183 | }).pipe(Effect.provide(database)); | 183 | }).pipe(Effect.provide(layerDatabase)); |
| 184 | const sourcePath = NodePath.join(NodePath.dirname(destinationPath), "state.sqlite"); | 184 | const sourcePath = NodePath.join(NodePath.dirname(destinationPath), "state.sqlite"); |
| 185 | assert.isFalse(NodeFS.existsSync(sourcePath)); | 185 | assert.isFalse(NodeFS.existsSync(sourcePath)); |
| 186 | NodeFS.writeFileSync(sourcePath, "This source must never be opened once V2 exists"); | 186 | NodeFS.writeFileSync(sourcePath, "This source must never be opened once V2 exists"); |
| ... | @@ -188,7 +188,7 @@ it.effect("starts fresh without V1 and never imports over existing V2 state", () | ... | @@ -188,7 +188,7 @@ it.effect("starts fresh without V1 and never imports over existing V2 state", () |
| 188 | yield* Effect.gen(function* () { | 188 | yield* Effect.gen(function* () { |
| 189 | const sql = yield* SqlClient.SqlClient; | 189 | const sql = yield* SqlClient.SqlClient; |
| 190 | assert.equal((yield* sql`SELECT text FROM v2_work`)[0]?.text, "fresh V2 work"); | 190 | assert.equal((yield* sql`SELECT text FROM v2_work`)[0]?.text, "fresh V2 work"); |
| 191 | }).pipe(Effect.provide(database)); | 191 | }).pipe(Effect.provide(layerDatabase)); |
| 192 | }).pipe( | 192 | }).pipe( |
| 193 | Effect.provide( | 193 | Effect.provide( |
| 194 | ServerConfig.layerTest(directory, directory).pipe(Layer.provideMerge(NodeServices.layer)), | 194 | ServerConfig.layerTest(directory, directory).pipe(Layer.provideMerge(NodeServices.layer)), |
apps/server/src/preview/PortScanner.test.ts+25-25| ... | @@ -39,13 +39,13 @@ const processProbeFailure: ProcessRunner.ProcessRunner["Service"]["run"] = (inpu | ... | @@ -39,13 +39,13 @@ const processProbeFailure: ProcessRunner.ProcessRunner["Service"]["run"] = (inpu |
| 39 | }), | 39 | }), |
| 40 | ); | 40 | ); |
| 41 | 41 | ||
| 42 | const TestProcessRunner = Layer.succeed(ProcessRunner.ProcessRunner, { | 42 | const layerTestProcessRunner = Layer.succeed(ProcessRunner.ProcessRunner, { |
| 43 | run: processProbeFailure, | 43 | run: processProbeFailure, |
| 44 | }); | 44 | }); |
| 45 | 45 | ||
| 46 | let integrationListeningPort: number | null = null; | 46 | let integrationListeningPort: number | null = null; |
| 47 | 47 | ||
| 48 | const TestIntegrationNet = Layer.succeed(Net.NetService, { | 48 | const layerTestIntegrationNet = Layer.succeed(Net.NetService, { |
| 49 | canListenOnHost: () => Effect.succeed(true), | 49 | canListenOnHost: () => Effect.succeed(true), |
| 50 | isPortAvailableOnLoopback: (port) => Effect.sync(() => port !== integrationListeningPort), | 50 | isPortAvailableOnLoopback: (port) => Effect.sync(() => port !== integrationListeningPort), |
| 51 | hasListenerOnHost: (port) => Effect.sync(() => port === integrationListeningPort), | 51 | hasListenerOnHost: (port) => Effect.sync(() => port === integrationListeningPort), |
| ... | @@ -53,7 +53,7 @@ const TestIntegrationNet = Layer.succeed(Net.NetService, { | ... | @@ -53,7 +53,7 @@ const TestIntegrationNet = Layer.succeed(Net.NetService, { |
| 53 | findAvailablePort: (preferred) => Effect.succeed(preferred), | 53 | findAvailablePort: (preferred) => Effect.succeed(preferred), |
| 54 | }); | 54 | }); |
| 55 | 55 | ||
| 56 | const makeProbeFailureLayer = ( | 56 | const layerProbeFailure = ( |
| 57 | run: ProcessRunner.ProcessRunner["Service"]["run"], | 57 | run: ProcessRunner.ProcessRunner["Service"]["run"], |
| 58 | fetch: typeof globalThis.fetch = globalThis.fetch, | 58 | fetch: typeof globalThis.fetch = globalThis.fetch, |
| 59 | ) => | 59 | ) => |
| ... | @@ -74,11 +74,11 @@ const makeProbeFailureLayer = ( | ... | @@ -74,11 +74,11 @@ const makeProbeFailureLayer = ( |
| 74 | ), | 74 | ), |
| 75 | ); | 75 | ); |
| 76 | 76 | ||
| 77 | const TestPortDiscoveryLive = PortScanner.layer.pipe( | 77 | const layerTestPortDiscovery = PortScanner.layer.pipe( |
| 78 | Layer.provide( | 78 | Layer.provide( |
| 79 | Layer.mergeAll( | 79 | Layer.mergeAll( |
| 80 | TestProcessRunner, | 80 | layerTestProcessRunner, |
| 81 | TestIntegrationNet, | 81 | layerTestIntegrationNet, |
| 82 | Layer.succeed(HostProcessPlatform, "win32"), | 82 | Layer.succeed(HostProcessPlatform, "win32"), |
| 83 | FetchHttpClient.layer, | 83 | FetchHttpClient.layer, |
| 84 | ), | 84 | ), |
| ... | @@ -87,7 +87,7 @@ const TestPortDiscoveryLive = PortScanner.layer.pipe( | ... | @@ -87,7 +87,7 @@ const TestPortDiscoveryLive = PortScanner.layer.pipe( |
| 87 | 87 | ||
| 88 | const LSOF_TEST_PORT = 43_123; | 88 | const LSOF_TEST_PORT = 43_123; |
| 89 | 89 | ||
| 90 | const makeLsofScannerLayer = (input: { | 90 | const layerLsofScanner = (input: { |
| 91 | readonly pid: () => number; | 91 | readonly pid: () => number; |
| 92 | readonly fetch: typeof globalThis.fetch; | 92 | readonly fetch: typeof globalThis.fetch; |
| 93 | }) => | 93 | }) => |
| ... | @@ -205,7 +205,7 @@ const commonNonHttpServer = Effect.acquireRelease( | ... | @@ -205,7 +205,7 @@ const commonNonHttpServer = Effect.acquireRelease( |
| 205 | * platform so the tests exercise the TCP-probe fallback without depending on | 205 | * platform so the tests exercise the TCP-probe fallback without depending on |
| 206 | * `lsof` being installed. | 206 | * `lsof` being installed. |
| 207 | */ | 207 | */ |
| 208 | effectIt.layer(TestPortDiscoveryLive)("PortDiscovery integration (TCP probe fallback)", (it) => { | 208 | effectIt.layer(layerTestPortDiscovery)("PortDiscovery integration (TCP probe fallback)", (it) => { |
| 209 | it.effect( | 209 | it.effect( |
| 210 | "scan() returns an HTTP server we just opened on a curated dev port", | 210 | "scan() returns an HTTP server we just opened on a curated dev port", |
| 211 | Effect.fn("PortScannerTest.scanFindsCommonDevServer")(function* () { | 211 | Effect.fn("PortScannerTest.scanFindsCommonDevServer")(function* () { |
| ... | @@ -254,7 +254,7 @@ effectIt.effect("revalidates a successful HTML probe after its cache entry expir | ... | @@ -254,7 +254,7 @@ effectIt.effect("revalidates a successful HTML probe after its cache entry expir |
| 254 | ? Promise.resolve(new Response("hello", { headers: { "content-type": "text/html" } })) | 254 | ? Promise.resolve(new Response("hello", { headers: { "content-type": "text/html" } })) |
| 255 | : Promise.reject(new TypeError("not HTTP")); | 255 | : Promise.reject(new TypeError("not HTTP")); |
| 256 | }) as typeof globalThis.fetch; | 256 | }) as typeof globalThis.fetch; |
| 257 | const layer = makeLsofScannerLayer({ pid: () => 1234, fetch: fetchFn }); | 257 | const layer = layerLsofScanner({ pid: () => 1234, fetch: fetchFn }); |
| 258 | 258 | ||
| 259 | return Effect.gen(function* () { | 259 | return Effect.gen(function* () { |
| 260 | const scanner = yield* PortScanner.PortDiscovery; | 260 | const scanner = yield* PortScanner.PortDiscovery; |
| ... | @@ -288,7 +288,7 @@ effectIt.effect("keeps a full configured URL when the discovered server root fai | ... | @@ -288,7 +288,7 @@ effectIt.effect("keeps a full configured URL when the discovered server root fai |
| 288 | }), | 288 | }), |
| 289 | ); | 289 | ); |
| 290 | }) as typeof globalThis.fetch; | 290 | }) as typeof globalThis.fetch; |
| 291 | const layer = makeLsofScannerLayer({ pid: () => 1234, fetch: fetchFn }); | 291 | const layer = layerLsofScanner({ pid: () => 1234, fetch: fetchFn }); |
| 292 | 292 | ||
| 293 | return Effect.gen(function* () { | 293 | return Effect.gen(function* () { |
| 294 | const scanner = yield* PortScanner.PortDiscovery; | 294 | const scanner = yield* PortScanner.PortDiscovery; |
| ... | @@ -308,7 +308,7 @@ effectIt.effect("probes configured custom ports through a canonical loopback hos | ... | @@ -308,7 +308,7 @@ effectIt.effect("probes configured custom ports through a canonical loopback hos |
| 308 | requests.push(String(input)); | 308 | requests.push(String(input)); |
| 309 | return Promise.resolve(new Response("docs", { headers: { "content-type": "text/html" } })); | 309 | return Promise.resolve(new Response("docs", { headers: { "content-type": "text/html" } })); |
| 310 | }) as typeof globalThis.fetch; | 310 | }) as typeof globalThis.fetch; |
| 311 | const layer = makeProbeFailureLayer(processProbeFailure, fetchFn); | 311 | const layer = layerProbeFailure(processProbeFailure, fetchFn); |
| 312 | 312 | ||
| 313 | return Effect.gen(function* () { | 313 | return Effect.gen(function* () { |
| 314 | const scanner = yield* PortScanner.PortDiscovery; | 314 | const scanner = yield* PortScanner.PortDiscovery; |
| ... | @@ -333,7 +333,7 @@ effectIt.effect("preserves explicit loopback hosts and bounds wildcard rewrites" | ... | @@ -333,7 +333,7 @@ effectIt.effect("preserves explicit loopback hosts and bounds wildcard rewrites" |
| 333 | requests.push(String(input)); | 333 | requests.push(String(input)); |
| 334 | return Promise.resolve(new Response("docs", { headers: { "content-type": "text/html" } })); | 334 | return Promise.resolve(new Response("docs", { headers: { "content-type": "text/html" } })); |
| 335 | }) as typeof globalThis.fetch; | 335 | }) as typeof globalThis.fetch; |
| 336 | const layer = makeProbeFailureLayer(processProbeFailure, fetchFn); | 336 | const layer = layerProbeFailure(processProbeFailure, fetchFn); |
| 337 | 337 | ||
| 338 | return Effect.gen(function* () { | 338 | return Effect.gen(function* () { |
| 339 | const scanner = yield* PortScanner.PortDiscovery; | 339 | const scanner = yield* PortScanner.PortDiscovery; |
| ... | @@ -354,7 +354,7 @@ effectIt.effect("projects configured paths independently for simultaneous subscr | ... | @@ -354,7 +354,7 @@ effectIt.effect("projects configured paths independently for simultaneous subscr |
| 354 | : new Response("not found", { status: 404 }), | 354 | : new Response("not found", { status: 404 }), |
| 355 | ); | 355 | ); |
| 356 | }) as typeof globalThis.fetch; | 356 | }) as typeof globalThis.fetch; |
| 357 | const layer = makeLsofScannerLayer({ pid: () => 1234, fetch: fetchFn }); | 357 | const layer = layerLsofScanner({ pid: () => 1234, fetch: fetchFn }); |
| 358 | 358 | ||
| 359 | return Effect.gen(function* () { | 359 | return Effect.gen(function* () { |
| 360 | const scanner = yield* PortScanner.PortDiscovery; | 360 | const scanner = yield* PortScanner.PortDiscovery; |
| ... | @@ -387,7 +387,7 @@ effectIt.effect( | ... | @@ -387,7 +387,7 @@ effectIt.effect( |
| 387 | ? new Response("app", { headers: { "content-type": "text/html" } }) | 387 | ? new Response("app", { headers: { "content-type": "text/html" } }) |
| 388 | : new Response("not found", { status: 404 }), | 388 | : new Response("not found", { status: 404 }), |
| 389 | )) as typeof globalThis.fetch; | 389 | )) as typeof globalThis.fetch; |
| 390 | const layer = makeLsofScannerLayer({ pid: () => 1234, fetch: fetchFn }); | 390 | const layer = layerLsofScanner({ pid: () => 1234, fetch: fetchFn }); |
| 391 | 391 | ||
| 392 | return Effect.gen(function* () { | 392 | return Effect.gen(function* () { |
| 393 | const scanner = yield* PortScanner.PortDiscovery; | 393 | const scanner = yield* PortScanner.PortDiscovery; |
| ... | @@ -420,7 +420,7 @@ effectIt.effect("stops probing a subscriber's configured paths after its scope c | ... | @@ -420,7 +420,7 @@ effectIt.effect("stops probing a subscriber's configured paths after its scope c |
| 420 | : new Response("not found", { status: 404 }), | 420 | : new Response("not found", { status: 404 }), |
| 421 | ); | 421 | ); |
| 422 | }) as typeof globalThis.fetch; | 422 | }) as typeof globalThis.fetch; |
| 423 | const layer = makeLsofScannerLayer({ pid: () => 1234, fetch: fetchFn }); | 423 | const layer = layerLsofScanner({ pid: () => 1234, fetch: fetchFn }); |
| 424 | 424 | ||
| 425 | return Effect.gen(function* () { | 425 | return Effect.gen(function* () { |
| 426 | const scanner = yield* PortScanner.PortDiscovery; | 426 | const scanner = yield* PortScanner.PortDiscovery; |
| ... | @@ -450,7 +450,7 @@ effectIt.effect("writes no poll span while no client retains the scanner", () => | ... | @@ -450,7 +450,7 @@ effectIt.effect("writes no poll span while no client retains the scanner", () => |
| 450 | return new Tracer.NativeSpan(options); | 450 | return new Tracer.NativeSpan(options); |
| 451 | }, | 451 | }, |
| 452 | }); | 452 | }); |
| 453 | const layer = makeProbeFailureLayer(processProbeFailure); | 453 | const layer = layerProbeFailure(processProbeFailure); |
| 454 | 454 | ||
| 455 | return Effect.gen(function* () { | 455 | return Effect.gen(function* () { |
| 456 | const scanner = yield* PortScanner.PortDiscovery; | 456 | const scanner = yield* PortScanner.PortDiscovery; |
| ... | @@ -468,7 +468,7 @@ effectIt.effect("uses the current configured fragment when readiness comes from | ... | @@ -468,7 +468,7 @@ effectIt.effect("uses the current configured fragment when readiness comes from |
| 468 | requests.push(String(input)); | 468 | requests.push(String(input)); |
| 469 | return Promise.resolve(new Response("docs", { headers: { "content-type": "text/html" } })); | 469 | return Promise.resolve(new Response("docs", { headers: { "content-type": "text/html" } })); |
| 470 | }) as typeof globalThis.fetch; | 470 | }) as typeof globalThis.fetch; |
| 471 | const layer = makeLsofScannerLayer({ pid: () => 1234, fetch: fetchFn }); | 471 | const layer = layerLsofScanner({ pid: () => 1234, fetch: fetchFn }); |
| 472 | const oldUrl = `http://localhost:${LSOF_TEST_PORT}/docs#old`; | 472 | const oldUrl = `http://localhost:${LSOF_TEST_PORT}/docs#old`; |
| 473 | const newUrl = `http://localhost:${LSOF_TEST_PORT}/docs#new`; | 473 | const newUrl = `http://localhost:${LSOF_TEST_PORT}/docs#new`; |
| 474 | 474 | ||
| ... | @@ -488,7 +488,7 @@ effectIt.effect("shares a configured root probe with discovered-root classificat | ... | @@ -488,7 +488,7 @@ effectIt.effect("shares a configured root probe with discovered-root classificat |
| 488 | requests.push(String(input)); | 488 | requests.push(String(input)); |
| 489 | return Promise.resolve(new Response("app", { headers: { "content-type": "text/html" } })); | 489 | return Promise.resolve(new Response("app", { headers: { "content-type": "text/html" } })); |
| 490 | }) as typeof globalThis.fetch; | 490 | }) as typeof globalThis.fetch; |
| 491 | const layer = makeLsofScannerLayer({ pid: () => 1234, fetch: fetchFn }); | 491 | const layer = layerLsofScanner({ pid: () => 1234, fetch: fetchFn }); |
| 492 | 492 | ||
| 493 | return Effect.gen(function* () { | 493 | return Effect.gen(function* () { |
| 494 | const scanner = yield* PortScanner.PortDiscovery; | 494 | const scanner = yield* PortScanner.PortDiscovery; |
| ... | @@ -517,7 +517,7 @@ effectIt.effect("starts fresh cache entries after the probing batch completes", | ... | @@ -517,7 +517,7 @@ effectIt.effect("starts fresh cache entries after the probing batch completes", |
| 517 | requests.push(String(input)); | 517 | requests.push(String(input)); |
| 518 | return Promise.resolve(new Response("app", { headers: { "content-type": "text/html" } })); | 518 | return Promise.resolve(new Response("app", { headers: { "content-type": "text/html" } })); |
| 519 | }) as typeof globalThis.fetch; | 519 | }) as typeof globalThis.fetch; |
| 520 | const layer = makeLsofScannerLayer({ pid: () => 1234, fetch: fetchFn }); | 520 | const layer = layerLsofScanner({ pid: () => 1234, fetch: fetchFn }); |
| 521 | 521 | ||
| 522 | yield* Effect.gen(function* () { | 522 | yield* Effect.gen(function* () { |
| 523 | const scanner = yield* PortScanner.PortDiscovery; | 523 | const scanner = yield* PortScanner.PortDiscovery; |
| ... | @@ -537,7 +537,7 @@ effectIt.effect("caches a failed web probe until its bounded cache entry expires | ... | @@ -537,7 +537,7 @@ effectIt.effect("caches a failed web probe until its bounded cache entry expires |
| 537 | ? Promise.resolve(new Response("hello", { headers: { "content-type": "text/html" } })) | 537 | ? Promise.resolve(new Response("hello", { headers: { "content-type": "text/html" } })) |
| 538 | : Promise.reject(new TypeError("not HTTP")); | 538 | : Promise.reject(new TypeError("not HTTP")); |
| 539 | }) as typeof globalThis.fetch; | 539 | }) as typeof globalThis.fetch; |
| 540 | const layer = makeLsofScannerLayer({ pid: () => 1234, fetch: fetchFn }); | 540 | const layer = layerLsofScanner({ pid: () => 1234, fetch: fetchFn }); |
| 541 | 541 | ||
| 542 | return Effect.gen(function* () { | 542 | return Effect.gen(function* () { |
| 543 | const scanner = yield* PortScanner.PortDiscovery; | 543 | const scanner = yield* PortScanner.PortDiscovery; |
| ... | @@ -562,7 +562,7 @@ effectIt.effect("falls back to HTTPS and does not follow redirects while probing | ... | @@ -562,7 +562,7 @@ effectIt.effect("falls back to HTTPS and does not follow redirects while probing |
| 562 | if (String(input).startsWith("http:")) throw new TypeError("TLS listener"); | 562 | if (String(input).startsWith("http:")) throw new TypeError("TLS listener"); |
| 563 | return new Response(null, { status: 302, headers: { location: "https://example.com" } }); | 563 | return new Response(null, { status: 302, headers: { location: "https://example.com" } }); |
| 564 | }) as typeof globalThis.fetch; | 564 | }) as typeof globalThis.fetch; |
| 565 | const layer = makeLsofScannerLayer({ pid: () => 1234, fetch: fetchFn }); | 565 | const layer = layerLsofScanner({ pid: () => 1234, fetch: fetchFn }); |
| 566 | 566 | ||
| 567 | return Effect.gen(function* () { | 567 | return Effect.gen(function* () { |
| 568 | const scanner = yield* PortScanner.PortDiscovery; | 568 | const scanner = yield* PortScanner.PortDiscovery; |
| ... | @@ -581,7 +581,7 @@ effectIt.effect( | ... | @@ -581,7 +581,7 @@ effectIt.effect( |
| 581 | new Response("not found", { status: 404, headers: { "content-type": "text/html" } }); | 581 | new Response("not found", { status: 404, headers: { "content-type": "text/html" } }); |
| 582 | const fetchFn = ((_input: Parameters<typeof globalThis.fetch>[0]) => | 582 | const fetchFn = ((_input: Parameters<typeof globalThis.fetch>[0]) => |
| 583 | Promise.resolve(makeResponse())) as typeof globalThis.fetch; | 583 | Promise.resolve(makeResponse())) as typeof globalThis.fetch; |
| 584 | const layer = makeLsofScannerLayer({ pid: () => pid, fetch: fetchFn }); | 584 | const layer = layerLsofScanner({ pid: () => pid, fetch: fetchFn }); |
| 585 | 585 | ||
| 586 | return Effect.gen(function* () { | 586 | return Effect.gen(function* () { |
| 587 | const scanner = yield* PortScanner.PortDiscovery; | 587 | const scanner = yield* PortScanner.PortDiscovery; |
| ... | @@ -639,7 +639,7 @@ effectIt.effect("aborts HTTP and HTTPS probes when they time out", () => { | ... | @@ -639,7 +639,7 @@ effectIt.effect("aborts HTTP and HTTPS probes when they time out", () => { |
| 639 | signal?.addEventListener("abort", onAbort, { once: true }); | 639 | signal?.addEventListener("abort", onAbort, { once: true }); |
| 640 | } | 640 | } |
| 641 | })) as typeof globalThis.fetch; | 641 | })) as typeof globalThis.fetch; |
| 642 | const layer = makeLsofScannerLayer({ pid: () => 1234, fetch: fetchFn }); | 642 | const layer = layerLsofScanner({ pid: () => 1234, fetch: fetchFn }); |
| 643 | 643 | ||
| 644 | return Effect.gen(function* () { | 644 | return Effect.gen(function* () { |
| 645 | const scanner = yield* PortScanner.PortDiscovery; | 645 | const scanner = yield* PortScanner.PortDiscovery; |
| ... | @@ -656,7 +656,7 @@ effectIt.effect("aborts HTTP and HTTPS probes when they time out", () => { | ... | @@ -656,7 +656,7 @@ effectIt.effect("aborts HTTP and HTTPS probes when they time out", () => { |
| 656 | effectIt.effect("does not swallow process probe defects", () => | 656 | effectIt.effect("does not swallow process probe defects", () => |
| 657 | Effect.gen(function* () { | 657 | Effect.gen(function* () { |
| 658 | const defect = new Error("unexpected process probe defect"); | 658 | const defect = new Error("unexpected process probe defect"); |
| 659 | const layer = makeProbeFailureLayer(() => Effect.die(defect)); | 659 | const layer = layerProbeFailure(() => Effect.die(defect)); |
| 660 | 660 | ||
| 661 | const exit = yield* Effect.flatMap(PortScanner.PortDiscovery, (scanner) => scanner.scan()).pipe( | 661 | const exit = yield* Effect.flatMap(PortScanner.PortDiscovery, (scanner) => scanner.scan()).pipe( |
| 662 | Effect.provide(layer), | 662 | Effect.provide(layer), |
| ... | @@ -673,7 +673,7 @@ effectIt.effect("does not swallow process probe defects", () => | ... | @@ -673,7 +673,7 @@ effectIt.effect("does not swallow process probe defects", () => |
| 673 | 673 | ||
| 674 | effectIt.effect("does not swallow process probe interruption", () => | 674 | effectIt.effect("does not swallow process probe interruption", () => |
| 675 | Effect.gen(function* () { | 675 | Effect.gen(function* () { |
| 676 | const layer = makeProbeFailureLayer(() => Effect.interrupt); | 676 | const layer = layerProbeFailure(() => Effect.interrupt); |
| 677 | 677 | ||
| 678 | const exit = yield* Effect.flatMap(PortScanner.PortDiscovery, (scanner) => scanner.scan()).pipe( | 678 | const exit = yield* Effect.flatMap(PortScanner.PortDiscovery, (scanner) => scanner.scan()).pipe( |
| 679 | Effect.provide(layer), | 679 | Effect.provide(layer), |
apps/server/src/process/externalLauncher.test.ts+34-34| ... | @@ -58,7 +58,7 @@ function makeMockDetachedHandle(input: MockSpawnResult & { readonly onUnref?: () | ... | @@ -58,7 +58,7 @@ function makeMockDetachedHandle(input: MockSpawnResult & { readonly onUnref?: () |
| 58 | }); | 58 | }); |
| 59 | } | 59 | } |
| 60 | 60 | ||
| 61 | const testLayer = (input: { | 61 | const layerTest = (input: { |
| 62 | readonly platform: NodeJS.Platform; | 62 | readonly platform: NodeJS.Platform; |
| 63 | readonly env?: Record<string, string>; | 63 | readonly env?: Record<string, string>; |
| 64 | readonly resolveExecutable?: (command: string) => string | undefined; | 64 | readonly resolveExecutable?: (command: string) => string | undefined; |
| ... | @@ -66,7 +66,7 @@ const testLayer = (input: { | ... | @@ -66,7 +66,7 @@ const testLayer = (input: { |
| 66 | readonly onUnref?: () => void; | 66 | readonly onUnref?: () => void; |
| 67 | readonly spawnResult?: (command: ChildProcess.StandardCommand) => MockSpawnResult | undefined; | 67 | readonly spawnResult?: (command: ChildProcess.StandardCommand) => MockSpawnResult | undefined; |
| 68 | }) => { | 68 | }) => { |
| 69 | const spawnerLayer = Layer.succeed( | 69 | const layerSpawner = Layer.succeed( |
| 70 | ChildProcessSpawner.ChildProcessSpawner, | 70 | ChildProcessSpawner.ChildProcessSpawner, |
| 71 | ChildProcessSpawner.make((command) => | 71 | ChildProcessSpawner.make((command) => |
| 72 | Effect.sync(() => { | 72 | Effect.sync(() => { |
| ... | @@ -84,7 +84,7 @@ const testLayer = (input: { | ... | @@ -84,7 +84,7 @@ const testLayer = (input: { |
| 84 | ); | 84 | ); |
| 85 | 85 | ||
| 86 | return Layer.mergeAll( | 86 | return Layer.mergeAll( |
| 87 | ExternalLauncher.layer.pipe(Layer.provide(Layer.merge(NodeServices.layer, spawnerLayer))), | 87 | ExternalLauncher.layer.pipe(Layer.provide(Layer.merge(NodeServices.layer, layerSpawner))), |
| 88 | Layer.succeed(HostProcessPlatform, input.platform), | 88 | Layer.succeed(HostProcessPlatform, input.platform), |
| 89 | Layer.succeed( | 89 | Layer.succeed( |
| 90 | SpawnExecutableResolution, | 90 | SpawnExecutableResolution, |
| ... | @@ -109,7 +109,7 @@ it.effect("launches the default browser through the platform command", () => { | ... | @@ -109,7 +109,7 @@ it.effect("launches the default browser through the platform command", () => { |
| 109 | assert.equal(didUnref, true); | 109 | assert.equal(didUnref, true); |
| 110 | }).pipe( | 110 | }).pipe( |
| 111 | Effect.provide( | 111 | Effect.provide( |
| 112 | testLayer({ | 112 | layerTest({ |
| 113 | platform: "linux", | 113 | platform: "linux", |
| 114 | onSpawn: (command) => { | 114 | onSpawn: (command) => { |
| 115 | spawned = command; | 115 | spawned = command; |
| ... | @@ -138,7 +138,7 @@ it.effect("launches an installed editor with platform-safe arguments", () => | ... | @@ -138,7 +138,7 @@ it.effect("launches an installed editor with platform-safe arguments", () => |
| 138 | }); | 138 | }); |
| 139 | }).pipe( | 139 | }).pipe( |
| 140 | Effect.provide( | 140 | Effect.provide( |
| 141 | testLayer({ | 141 | layerTest({ |
| 142 | platform: "win32", | 142 | platform: "win32", |
| 143 | env: { PATH: binDir, PATHEXT: ".COM;.EXE;.BAT;.CMD" }, | 143 | env: { PATH: binDir, PATHEXT: ".COM;.EXE;.BAT;.CMD" }, |
| 144 | resolveExecutable: (command) => | 144 | resolveExecutable: (command) => |
| ... | @@ -183,7 +183,7 @@ for (const platform of ["darwin", "linux"] as const) { | ... | @@ -183,7 +183,7 @@ for (const platform of ["darwin", "linux"] as const) { |
| 183 | } | 183 | } |
| 184 | }).pipe( | 184 | }).pipe( |
| 185 | Effect.provide( | 185 | Effect.provide( |
| 186 | testLayer({ | 186 | layerTest({ |
| 187 | platform, | 187 | platform, |
| 188 | env: { PATH: binDir }, | 188 | env: { PATH: binDir }, |
| 189 | onSpawn: (command) => spawned.push(command), | 189 | onSpawn: (command) => spawned.push(command), |
| ... | @@ -226,7 +226,7 @@ it.effect("launches Cursor in classic IDE mode through the Windows command shim" | ... | @@ -226,7 +226,7 @@ it.effect("launches Cursor in classic IDE mode through the Windows command shim" |
| 226 | }); | 226 | }); |
| 227 | }).pipe( | 227 | }).pipe( |
| 228 | Effect.provide( | 228 | Effect.provide( |
| 229 | testLayer({ | 229 | layerTest({ |
| 230 | platform: "win32", | 230 | platform: "win32", |
| 231 | env: { PATH: binDir, PATHEXT: ".COM;.EXE;.BAT;.CMD" }, | 231 | env: { PATH: binDir, PATHEXT: ".COM;.EXE;.BAT;.CMD" }, |
| 232 | resolveExecutable: (command) => | 232 | resolveExecutable: (command) => |
| ... | @@ -268,7 +268,7 @@ it.effect.skipIf(windowsHost)("reveals a file in Finder with open -R on macOS", | ... | @@ -268,7 +268,7 @@ it.effect.skipIf(windowsHost)("reveals a file in Finder with open -R on macOS", |
| 268 | }); | 268 | }); |
| 269 | }).pipe( | 269 | }).pipe( |
| 270 | Effect.provide( | 270 | Effect.provide( |
| 271 | testLayer({ | 271 | layerTest({ |
| 272 | platform: "darwin", | 272 | platform: "darwin", |
| 273 | env: { PATH: binDir }, | 273 | env: { PATH: binDir }, |
| 274 | onSpawn: (command) => { | 274 | onSpawn: (command) => { |
| ... | @@ -310,7 +310,7 @@ it.effect("reveals a file in File Explorer through PowerShell on Windows", () => | ... | @@ -310,7 +310,7 @@ it.effect("reveals a file in File Explorer through PowerShell on Windows", () => |
| 310 | return yield* launcher.resolveFileManagerRevealKind(); | 310 | return yield* launcher.resolveFileManagerRevealKind(); |
| 311 | }).pipe( | 311 | }).pipe( |
| 312 | Effect.provide( | 312 | Effect.provide( |
| 313 | testLayer({ | 313 | layerTest({ |
| 314 | platform: "win32", | 314 | platform: "win32", |
| 315 | env: { PATH: binDir, PATHEXT: ".COM;.EXE;.BAT;.CMD", SYSTEMROOT: systemRoot }, | 315 | env: { PATH: binDir, PATHEXT: ".COM;.EXE;.BAT;.CMD", SYSTEMROOT: systemRoot }, |
| 316 | onSpawn: (command) => { | 316 | onSpawn: (command) => { |
| ... | @@ -414,7 +414,7 @@ it.effect("does not advertise reveal on Windows when PowerShell is missing", () | ... | @@ -414,7 +414,7 @@ it.effect("does not advertise reveal on Windows when PowerShell is missing", () |
| 414 | }; | 414 | }; |
| 415 | }).pipe( | 415 | }).pipe( |
| 416 | Effect.provide( | 416 | Effect.provide( |
| 417 | testLayer({ | 417 | layerTest({ |
| 418 | platform: "win32", | 418 | platform: "win32", |
| 419 | env: { | 419 | env: { |
| 420 | PATH: binDir, | 420 | PATH: binDir, |
| ... | @@ -458,7 +458,7 @@ it.effect.skipIf(windowsHost)( | ... | @@ -458,7 +458,7 @@ it.effect.skipIf(windowsHost)( |
| 458 | return { kind, editors }; | 458 | return { kind, editors }; |
| 459 | }).pipe( | 459 | }).pipe( |
| 460 | Effect.provide( | 460 | Effect.provide( |
| 461 | testLayer({ | 461 | layerTest({ |
| 462 | platform: "linux", | 462 | platform: "linux", |
| 463 | env: { | 463 | env: { |
| 464 | PATH: binDir, | 464 | PATH: binDir, |
| ... | @@ -506,7 +506,7 @@ it.effect.skipIf(windowsHost)( | ... | @@ -506,7 +506,7 @@ it.effect.skipIf(windowsHost)( |
| 506 | }; | 506 | }; |
| 507 | }).pipe( | 507 | }).pipe( |
| 508 | Effect.provide( | 508 | Effect.provide( |
| 509 | testLayer({ | 509 | layerTest({ |
| 510 | platform: "linux", | 510 | platform: "linux", |
| 511 | env: { | 511 | env: { |
| 512 | PATH: binDir, | 512 | PATH: binDir, |
| ... | @@ -550,7 +550,7 @@ it.effect.skipIf(windowsHost)( | ... | @@ -550,7 +550,7 @@ it.effect.skipIf(windowsHost)( |
| 550 | return revealKind; | 550 | return revealKind; |
| 551 | }).pipe( | 551 | }).pipe( |
| 552 | Effect.provide( | 552 | Effect.provide( |
| 553 | testLayer({ | 553 | layerTest({ |
| 554 | platform: "linux", | 554 | platform: "linux", |
| 555 | env: { | 555 | env: { |
| 556 | PATH: binDir, | 556 | PATH: binDir, |
| ... | @@ -606,7 +606,7 @@ it.effect.skipIf(windowsHost)( | ... | @@ -606,7 +606,7 @@ it.effect.skipIf(windowsHost)( |
| 606 | return { editors, kind }; | 606 | return { editors, kind }; |
| 607 | }).pipe( | 607 | }).pipe( |
| 608 | Effect.provide( | 608 | Effect.provide( |
| 609 | testLayer({ | 609 | layerTest({ |
| 610 | platform: "linux", | 610 | platform: "linux", |
| 611 | env: { | 611 | env: { |
| 612 | PATH: binDir, | 612 | PATH: binDir, |
| ... | @@ -656,7 +656,7 @@ it.effect.skipIf(windowsHost)( | ... | @@ -656,7 +656,7 @@ it.effect.skipIf(windowsHost)( |
| 656 | }); | 656 | }); |
| 657 | }).pipe( | 657 | }).pipe( |
| 658 | Effect.provide( | 658 | Effect.provide( |
| 659 | testLayer({ | 659 | layerTest({ |
| 660 | platform: "linux", | 660 | platform: "linux", |
| 661 | env: { | 661 | env: { |
| 662 | PATH: binDir, | 662 | PATH: binDir, |
| ... | @@ -699,7 +699,7 @@ it.effect.skipIf(windowsHost)("reveals by opening the containing directory on Li | ... | @@ -699,7 +699,7 @@ it.effect.skipIf(windowsHost)("reveals by opening the containing directory on Li |
| 699 | }); | 699 | }); |
| 700 | }).pipe( | 700 | }).pipe( |
| 701 | Effect.provide( | 701 | Effect.provide( |
| 702 | testLayer({ | 702 | layerTest({ |
| 703 | platform: "linux", | 703 | platform: "linux", |
| 704 | env: { PATH: binDir, DISPLAY: ":0" }, | 704 | env: { PATH: binDir, DISPLAY: ":0" }, |
| 705 | onSpawn: (command) => { | 705 | onSpawn: (command) => { |
| ... | @@ -731,7 +731,7 @@ it.effect.skipIf(windowsHost)( | ... | @@ -731,7 +731,7 @@ it.effect.skipIf(windowsHost)( |
| 731 | const editors = yield* Effect.gen(function* () { | 731 | const editors = yield* Effect.gen(function* () { |
| 732 | const launcher = yield* ExternalLauncher.ExternalLauncher; | 732 | const launcher = yield* ExternalLauncher.ExternalLauncher; |
| 733 | return yield* launcher.resolveAvailableEditors(); | 733 | return yield* launcher.resolveAvailableEditors(); |
| 734 | }).pipe(Effect.provide(testLayer({ platform: "linux", env: { PATH: binDir } }))); | 734 | }).pipe(Effect.provide(layerTest({ platform: "linux", env: { PATH: binDir } }))); |
| 735 | 735 | ||
| 736 | assert.equal(editors.includes("file-manager"), false); | 736 | assert.equal(editors.includes("file-manager"), false); |
| 737 | }).pipe(Effect.scoped, Effect.provide(NodeServices.layer)), | 737 | }).pipe(Effect.scoped, Effect.provide(NodeServices.layer)), |
| ... | @@ -756,7 +756,7 @@ it.effect.skipIf(windowsHost)( | ... | @@ -756,7 +756,7 @@ it.effect.skipIf(windowsHost)( |
| 756 | return yield* launcher.resolveAvailableEditors(); | 756 | return yield* launcher.resolveAvailableEditors(); |
| 757 | }).pipe( | 757 | }).pipe( |
| 758 | Effect.provide( | 758 | Effect.provide( |
| 759 | testLayer({ | 759 | layerTest({ |
| 760 | platform: "linux", | 760 | platform: "linux", |
| 761 | env: { PATH: binDir, DISPLAY: ":0" }, | 761 | env: { PATH: binDir, DISPLAY: ":0" }, |
| 762 | onSpawn: (command) => { | 762 | onSpawn: (command) => { |
| ... | @@ -798,7 +798,7 @@ it.effect.skipIf(windowsHost)( | ... | @@ -798,7 +798,7 @@ it.effect.skipIf(windowsHost)( |
| 798 | return yield* launcher.resolveAvailableEditors(); | 798 | return yield* launcher.resolveAvailableEditors(); |
| 799 | }).pipe( | 799 | }).pipe( |
| 800 | Effect.provide( | 800 | Effect.provide( |
| 801 | testLayer({ | 801 | layerTest({ |
| 802 | platform: "linux", | 802 | platform: "linux", |
| 803 | env: { PATH: binDir, DISPLAY: ":0" }, | 803 | env: { PATH: binDir, DISPLAY: ":0" }, |
| 804 | spawnResult: (command) => (command.command === "xdg-mime" ? { stdout: "" } : undefined), | 804 | spawnResult: (command) => (command.command === "xdg-mime" ? { stdout: "" } : undefined), |
| ... | @@ -828,7 +828,7 @@ it.effect.skipIf(windowsHost)( | ... | @@ -828,7 +828,7 @@ it.effect.skipIf(windowsHost)( |
| 828 | return yield* launcher.resolveAvailableEditors(); | 828 | return yield* launcher.resolveAvailableEditors(); |
| 829 | }).pipe( | 829 | }).pipe( |
| 830 | Effect.provide( | 830 | Effect.provide( |
| 831 | testLayer({ | 831 | layerTest({ |
| 832 | platform: "linux", | 832 | platform: "linux", |
| 833 | env: { PATH: binDir, DISPLAY: ":0" }, | 833 | env: { PATH: binDir, DISPLAY: ":0" }, |
| 834 | spawnResult: (command) => | 834 | spawnResult: (command) => |
| ... | @@ -863,7 +863,7 @@ it.live.skipIf(windowsHost)("a stalled handler probe drops only the file manager | ... | @@ -863,7 +863,7 @@ it.live.skipIf(windowsHost)("a stalled handler probe drops only the file manager |
| 863 | return yield* launcher.resolveAvailableEditors(); | 863 | return yield* launcher.resolveAvailableEditors(); |
| 864 | }).pipe( | 864 | }).pipe( |
| 865 | Effect.provide( | 865 | Effect.provide( |
| 866 | testLayer({ | 866 | layerTest({ |
| 867 | platform: "linux", | 867 | platform: "linux", |
| 868 | env: { PATH: binDir, DISPLAY: ":0" }, | 868 | env: { PATH: binDir, DISPLAY: ":0" }, |
| 869 | spawnResult: (command) => (command.command === "xdg-mime" ? { stall: true } : undefined), | 869 | spawnResult: (command) => (command.command === "xdg-mime" ? { stall: true } : undefined), |
| ... | @@ -891,7 +891,7 @@ it.effect.skipIf(windowsHost)( | ... | @@ -891,7 +891,7 @@ it.effect.skipIf(windowsHost)( |
| 891 | const launcher = yield* ExternalLauncher.ExternalLauncher; | 891 | const launcher = yield* ExternalLauncher.ExternalLauncher; |
| 892 | return yield* launcher.resolveAvailableEditors(); | 892 | return yield* launcher.resolveAvailableEditors(); |
| 893 | }).pipe( | 893 | }).pipe( |
| 894 | Effect.provide(testLayer({ platform: "linux", env: { PATH: binDir, DISPLAY: ":0" } })), | 894 | Effect.provide(layerTest({ platform: "linux", env: { PATH: binDir, DISPLAY: ":0" } })), |
| 895 | ); | 895 | ); |
| 896 | 896 | ||
| 897 | assert.equal(editors.includes("file-manager"), false); | 897 | assert.equal(editors.includes("file-manager"), false); |
| ... | @@ -911,7 +911,7 @@ it.effect("discovers editors through the service API", () => | ... | @@ -911,7 +911,7 @@ it.effect("discovers editors through the service API", () => |
| 911 | return yield* launcher.resolveAvailableEditors(); | 911 | return yield* launcher.resolveAvailableEditors(); |
| 912 | }).pipe( | 912 | }).pipe( |
| 913 | Effect.provide( | 913 | Effect.provide( |
| 914 | testLayer({ | 914 | layerTest({ |
| 915 | platform: "win32", | 915 | platform: "win32", |
| 916 | env: { PATH: binDir, PATHEXT: ".COM;.EXE;.BAT;.CMD" }, | 916 | env: { PATH: binDir, PATHEXT: ".COM;.EXE;.BAT;.CMD" }, |
| 917 | }), | 917 | }), |
| ... | @@ -1021,7 +1021,7 @@ for (const { platform, installPath, editor, args } of [ | ... | @@ -1021,7 +1021,7 @@ for (const { platform, installPath, editor, args } of [ |
| 1021 | yield* launcher.launchEditor({ editor, cwd: "/workspace with spaces/file.ts:12:4" }); | 1021 | yield* launcher.launchEditor({ editor, cwd: "/workspace with spaces/file.ts:12:4" }); |
| 1022 | }).pipe( | 1022 | }).pipe( |
| 1023 | Effect.provide( | 1023 | Effect.provide( |
| 1024 | testLayer({ | 1024 | layerTest({ |
| 1025 | platform, | 1025 | platform, |
| 1026 | env: { HOME: home, LOCALAPPDATA: home, PATH: path.join(home, "empty") }, | 1026 | env: { HOME: home, LOCALAPPDATA: home, PATH: path.join(home, "empty") }, |
| 1027 | onSpawn: (command) => { | 1027 | onSpawn: (command) => { |
| ... | @@ -1064,7 +1064,7 @@ for (const { platform, installPath, onPath } of [ | ... | @@ -1064,7 +1064,7 @@ for (const { platform, installPath, onPath } of [ |
| 1064 | return yield* launcher.resolveAvailableEditors(); | 1064 | return yield* launcher.resolveAvailableEditors(); |
| 1065 | }).pipe( | 1065 | }).pipe( |
| 1066 | Effect.provide( | 1066 | Effect.provide( |
| 1067 | testLayer({ | 1067 | layerTest({ |
| 1068 | platform, | 1068 | platform, |
| 1069 | env: { | 1069 | env: { |
| 1070 | HOME: home, | 1070 | HOME: home, |
| ... | @@ -1091,17 +1091,17 @@ it.effect.skipIf(windowsHost)("ignores unusable app bundles and keeps PATH launc | ... | @@ -1091,17 +1091,17 @@ it.effect.skipIf(windowsHost)("ignores unusable app bundles and keeps PATH launc |
| 1091 | const launcher = yield* ExternalLauncher.ExternalLauncher; | 1091 | const launcher = yield* ExternalLauncher.ExternalLauncher; |
| 1092 | return yield* launcher.resolveAvailableEditors(); | 1092 | return yield* launcher.resolveAvailableEditors(); |
| 1093 | }); | 1093 | }); |
| 1094 | const before = yield* discover.pipe(Effect.provide(testLayer({ platform: "darwin", env }))); | 1094 | const before = yield* discover.pipe(Effect.provide(layerTest({ platform: "darwin", env }))); |
| 1095 | yield* fs.makeDirectory(executable, { recursive: true }); | 1095 | yield* fs.makeDirectory(executable, { recursive: true }); |
| 1096 | assert.deepEqual( | 1096 | assert.deepEqual( |
| 1097 | yield* discover.pipe(Effect.provide(testLayer({ platform: "darwin", env }))), | 1097 | yield* discover.pipe(Effect.provide(layerTest({ platform: "darwin", env }))), |
| 1098 | before, | 1098 | before, |
| 1099 | ); | 1099 | ); |
| 1100 | yield* fs.remove(executable, { recursive: true }); | 1100 | yield* fs.remove(executable, { recursive: true }); |
| 1101 | yield* fs.writeFileString(executable, "#!/bin/sh\n"); | 1101 | yield* fs.writeFileString(executable, "#!/bin/sh\n"); |
| 1102 | yield* fs.chmod(executable, 0o644); | 1102 | yield* fs.chmod(executable, 0o644); |
| 1103 | assert.deepEqual( | 1103 | assert.deepEqual( |
| 1104 | yield* discover.pipe(Effect.provide(testLayer({ platform: "darwin", env }))), | 1104 | yield* discover.pipe(Effect.provide(layerTest({ platform: "darwin", env }))), |
| 1105 | before, | 1105 | before, |
| 1106 | ); | 1106 | ); |
| 1107 | yield* fs.chmod(executable, 0o755); | 1107 | yield* fs.chmod(executable, 0o755); |
| ... | @@ -1114,7 +1114,7 @@ it.effect.skipIf(windowsHost)("ignores unusable app bundles and keeps PATH launc | ... | @@ -1114,7 +1114,7 @@ it.effect.skipIf(windowsHost)("ignores unusable app bundles and keeps PATH launc |
| 1114 | yield* launcher.launchEditor({ editor: "cursor", cwd: "/workspace" }); | 1114 | yield* launcher.launchEditor({ editor: "cursor", cwd: "/workspace" }); |
| 1115 | }).pipe( | 1115 | }).pipe( |
| 1116 | Effect.provide( | 1116 | Effect.provide( |
| 1117 | testLayer({ | 1117 | layerTest({ |
| 1118 | platform: "darwin", | 1118 | platform: "darwin", |
| 1119 | env: { ...env, PATH: `${env.PATH}:/usr/bin` }, | 1119 | env: { ...env, PATH: `${env.PATH}:/usr/bin` }, |
| 1120 | onSpawn: (command) => { | 1120 | onSpawn: (command) => { |
| ... | @@ -1130,7 +1130,7 @@ it.effect.skipIf(windowsHost)("ignores unusable app bundles and keeps PATH launc | ... | @@ -1130,7 +1130,7 @@ it.effect.skipIf(windowsHost)("ignores unusable app bundles and keeps PATH launc |
| 1130 | it.effect("memoizes editor discovery and refreshes after the cache window", () => { | 1130 | it.effect("memoizes editor discovery and refreshes after the cache window", () => { |
| 1131 | let statCalls = 0; | 1131 | let statCalls = 0; |
| 1132 | const fileInfo = { type: "File" } as FileSystem.File.Info; | 1132 | const fileInfo = { type: "File" } as FileSystem.File.Info; |
| 1133 | const launcherLayer = ExternalLauncher.layer.pipe( | 1133 | const layerLauncher = ExternalLauncher.layer.pipe( |
| 1134 | Layer.provide( | 1134 | Layer.provide( |
| 1135 | Layer.mergeAll( | 1135 | Layer.mergeAll( |
| 1136 | FileSystem.layerNoop({ | 1136 | FileSystem.layerNoop({ |
| ... | @@ -1171,7 +1171,7 @@ it.effect("memoizes editor discovery and refreshes after the cache window", () = | ... | @@ -1171,7 +1171,7 @@ it.effect("memoizes editor discovery and refreshes after the cache window", () = |
| 1171 | }).pipe( | 1171 | }).pipe( |
| 1172 | Effect.provide( | 1172 | Effect.provide( |
| 1173 | Layer.mergeAll( | 1173 | Layer.mergeAll( |
| 1174 | launcherLayer, | 1174 | layerLauncher, |
| 1175 | Layer.succeed(HostProcessPlatform, "win32"), | 1175 | Layer.succeed(HostProcessPlatform, "win32"), |
| 1176 | ConfigProvider.layer( | 1176 | ConfigProvider.layer( |
| 1177 | ConfigProvider.fromEnv({ | 1177 | ConfigProvider.fromEnv({ |
| ... | @@ -1194,7 +1194,7 @@ it.effect("keeps scanning after the caller is interrupted and shares that scan", | ... | @@ -1194,7 +1194,7 @@ it.effect("keeps scanning after the caller is interrupted and shares that scan", |
| 1194 | const fileInfo = { type: "File" } as FileSystem.File.Info; | 1194 | const fileInfo = { type: "File" } as FileSystem.File.Info; |
| 1195 | const release = Deferred.makeUnsafe<void>(); | 1195 | const release = Deferred.makeUnsafe<void>(); |
| 1196 | let parkedStats = 0; | 1196 | let parkedStats = 0; |
| 1197 | const launcherLayer = ExternalLauncher.layer.pipe( | 1197 | const layerLauncher = ExternalLauncher.layer.pipe( |
| 1198 | Layer.provide( | 1198 | Layer.provide( |
| 1199 | Layer.mergeAll( | 1199 | Layer.mergeAll( |
| 1200 | FileSystem.layerNoop({ | 1200 | FileSystem.layerNoop({ |
| ... | @@ -1236,7 +1236,7 @@ it.effect("keeps scanning after the caller is interrupted and shares that scan", | ... | @@ -1236,7 +1236,7 @@ it.effect("keeps scanning after the caller is interrupted and shares that scan", |
| 1236 | }).pipe( | 1236 | }).pipe( |
| 1237 | Effect.provide( | 1237 | Effect.provide( |
| 1238 | Layer.mergeAll( | 1238 | Layer.mergeAll( |
| 1239 | launcherLayer, | 1239 | layerLauncher, |
| 1240 | Layer.succeed(HostProcessPlatform, "win32"), | 1240 | Layer.succeed(HostProcessPlatform, "win32"), |
| 1241 | ConfigProvider.layer( | 1241 | ConfigProvider.layer( |
| 1242 | ConfigProvider.fromEnv({ | 1242 | ConfigProvider.fromEnv({ |
| ... | @@ -1260,5 +1260,5 @@ it.effect("rejects unknown editors through the service API", () => | ... | @@ -1260,5 +1260,5 @@ it.effect("rejects unknown editors through the service API", () => |
| 1260 | assert.instanceOf(error, ExternalLauncher.ExternalLauncherUnknownEditorError); | 1260 | assert.instanceOf(error, ExternalLauncher.ExternalLauncherUnknownEditorError); |
| 1261 | assert.equal(error.editor, "missing-editor"); | 1261 | assert.equal(error.editor, "missing-editor"); |
| 1262 | assert.equal(error.message, "Unknown editor: missing-editor"); | 1262 | assert.equal(error.message, "Unknown editor: missing-editor"); |
| 1263 | }).pipe(Effect.provide(testLayer({ platform: "linux", env: { PATH: "" } }))), | 1263 | }).pipe(Effect.provide(layerTest({ platform: "linux", env: { PATH: "" } }))), |
| 1264 | ); | 1264 | ); |
apps/server/src/project/AgentSessionImporter.test.ts+2-2| ... | @@ -59,7 +59,7 @@ it.effect("imports messages once and preserves the provider native resume bindin | ... | @@ -59,7 +59,7 @@ it.effect("imports messages once and preserves the provider native resume bindin |
| 59 | }, | 59 | }, |
| 60 | }), | 60 | }), |
| 61 | }); | 61 | }); |
| 62 | const testLayer = AgentSessionImporter.layer.pipe( | 62 | const layerTest = AgentSessionImporter.layer.pipe( |
| 63 | Layer.provide( | 63 | Layer.provide( |
| 64 | Layer.mergeAll( | 64 | Layer.mergeAll( |
| 65 | Layer.succeed(AgentSessionScanner.AgentSessionScanner, scanner), | 65 | Layer.succeed(AgentSessionScanner.AgentSessionScanner, scanner), |
| ... | @@ -143,5 +143,5 @@ it.effect("imports messages once and preserves the provider native resume bindin | ... | @@ -143,5 +143,5 @@ it.effect("imports messages once and preserves the provider native resume bindin |
| 143 | }), | 143 | }), |
| 144 | ]); | 144 | ]); |
| 145 | expect(recorded).toHaveLength(2); | 145 | expect(recorded).toHaveLength(2); |
| 146 | }).pipe(Effect.provide(testLayer)); | 146 | }).pipe(Effect.provide(layerTest)); |
| 147 | }); | 147 | }); |
apps/server/src/project/AgentSessionScanner.test.ts+10-10| ... | @@ -33,7 +33,7 @@ const makeProjectShell = (workspaceRoot: string): OrchestrationProjectShell => ( | ... | @@ -33,7 +33,7 @@ const makeProjectShell = (workspaceRoot: string): OrchestrationProjectShell => ( |
| 33 | updatedAt: "2026-01-01T00:00:00.000Z", | 33 | updatedAt: "2026-01-01T00:00:00.000Z", |
| 34 | }); | 34 | }); |
| 35 | 35 | ||
| 36 | const makeProjectStoreLayer = (importedWorkspaceRoots: ReadonlyArray<string>) => | 36 | const layerProjectStore = (importedWorkspaceRoots: ReadonlyArray<string>) => |
| 37 | Layer.mock(ProjectStore.ProjectStoreV2)({ | 37 | Layer.mock(ProjectStore.ProjectStoreV2)({ |
| 38 | listShells: () => | 38 | listShells: () => |
| 39 | Effect.succeed( | 39 | Effect.succeed( |
| ... | @@ -54,7 +54,7 @@ interface ScannerTestInput { | ... | @@ -54,7 +54,7 @@ interface ScannerTestInput { |
| 54 | readonly providerInstances?: ContractServerSettings["providerInstances"]; | 54 | readonly providerInstances?: ContractServerSettings["providerInstances"]; |
| 55 | } | 55 | } |
| 56 | 56 | ||
| 57 | const makeScannerTestLayer = (input: ScannerTestInput) => | 57 | const layerScannerTest = (input: ScannerTestInput) => |
| 58 | AgentSessionScanner.layer.pipe( | 58 | AgentSessionScanner.layer.pipe( |
| 59 | Layer.provide( | 59 | Layer.provide( |
| 60 | Layer.mergeAll( | 60 | Layer.mergeAll( |
| ... | @@ -71,7 +71,7 @@ const makeScannerTestLayer = (input: ScannerTestInput) => | ... | @@ -71,7 +71,7 @@ const makeScannerTestLayer = (input: ScannerTestInput) => |
| 71 | input.claudeHomePath, | 71 | input.claudeHomePath, |
| 72 | input.configBaseDir ?? { prefix: "t3code-scanner-config-" }, | 72 | input.configBaseDir ?? { prefix: "t3code-scanner-config-" }, |
| 73 | ), | 73 | ), |
| 74 | makeProjectStoreLayer(input.importedWorkspaceRoots ?? []), | 74 | layerProjectStore(input.importedWorkspaceRoots ?? []), |
| 75 | ), | 75 | ), |
| 76 | ), | 76 | ), |
| 77 | ); | 77 | ); |
| ... | @@ -80,7 +80,7 @@ const runScan = (input: ScannerTestInput) => | ... | @@ -80,7 +80,7 @@ const runScan = (input: ScannerTestInput) => |
| 80 | Effect.gen(function* () { | 80 | Effect.gen(function* () { |
| 81 | const scanner = yield* AgentSessionScanner.AgentSessionScanner; | 81 | const scanner = yield* AgentSessionScanner.AgentSessionScanner; |
| 82 | return yield* scanner.scan; | 82 | return yield* scanner.scan; |
| 83 | }).pipe(Effect.provide(makeScannerTestLayer(input))); | 83 | }).pipe(Effect.provide(layerScannerTest(input))); |
| 84 | 84 | ||
| 85 | const runRecentThreadOutcomes = (input: ScannerTestInput & { readonly workspaceRoot: string }) => | 85 | const runRecentThreadOutcomes = (input: ScannerTestInput & { readonly workspaceRoot: string }) => |
| 86 | Effect.gen(function* () { | 86 | Effect.gen(function* () { |
| ... | @@ -89,7 +89,7 @@ const runRecentThreadOutcomes = (input: ScannerTestInput & { readonly workspaceR | ... | @@ -89,7 +89,7 @@ const runRecentThreadOutcomes = (input: ScannerTestInput & { readonly workspaceR |
| 89 | Stream.runCollect, | 89 | Stream.runCollect, |
| 90 | Effect.map((outcomes) => Array.from(outcomes)), | 90 | Effect.map((outcomes) => Array.from(outcomes)), |
| 91 | ); | 91 | ); |
| 92 | }).pipe(Effect.provide(makeScannerTestLayer(input))); | 92 | }).pipe(Effect.provide(layerScannerTest(input))); |
| 93 | 93 | ||
| 94 | const runRecentThreads = (input: ScannerTestInput & { readonly workspaceRoot: string }) => | 94 | const runRecentThreads = (input: ScannerTestInput & { readonly workspaceRoot: string }) => |
| 95 | runRecentThreadOutcomes(input).pipe( | 95 | runRecentThreadOutcomes(input).pipe( |
| ... | @@ -1705,7 +1705,7 @@ it.layer(NodeServices.layer)("AgentSessionScanner", (it) => { | ... | @@ -1705,7 +1705,7 @@ it.layer(NodeServices.layer)("AgentSessionScanner", (it) => { |
| 1705 | expect(scan.candidates[0]?.threadCount).toBe(5); | 1705 | expect(scan.candidates[0]?.threadCount).toBe(5); |
| 1706 | return yield* scanner.recentThreads(workspace).pipe(Stream.runCollect); | 1706 | return yield* scanner.recentThreads(workspace).pipe(Stream.runCollect); |
| 1707 | }).pipe( | 1707 | }).pipe( |
| 1708 | Effect.provide(makeScannerTestLayer({ claudeHomePath, codexHomePath })), | 1708 | Effect.provide(layerScannerTest({ claudeHomePath, codexHomePath })), |
| 1709 | Effect.provideService(FileSystem.FileSystem, trackedFileSystem), | 1709 | Effect.provideService(FileSystem.FileSystem, trackedFileSystem), |
| 1710 | ); | 1710 | ); |
| 1711 | 1711 | ||
| ... | @@ -1855,7 +1855,7 @@ it.layer(NodeServices.layer)("AgentSessionScanner", (it) => { | ... | @@ -1855,7 +1855,7 @@ it.layer(NodeServices.layer)("AgentSessionScanner", (it) => { |
| 1855 | } else { | 1855 | } else { |
| 1856 | expect(outcomes).toEqual([{ _tag: "Skipped" }]); | 1856 | expect(outcomes).toEqual([{ _tag: "Skipped" }]); |
| 1857 | } | 1857 | } |
| 1858 | }).pipe(Effect.provide(makeScannerTestLayer({ claudeHomePath, codexHomePath }))); | 1858 | }).pipe(Effect.provide(layerScannerTest({ claudeHomePath, codexHomePath }))); |
| 1859 | }), | 1859 | }), |
| 1860 | ); | 1860 | ); |
| 1861 | } | 1861 | } |
| ... | @@ -1926,7 +1926,7 @@ it.layer(NodeServices.layer)("AgentSessionScanner", (it) => { | ... | @@ -1926,7 +1926,7 @@ it.layer(NodeServices.layer)("AgentSessionScanner", (it) => { |
| 1926 | thread: { providerSessionId: "replaced-session" }, | 1926 | thread: { providerSessionId: "replaced-session" }, |
| 1927 | source: { size: imported.source.size, mtimeMs: imported.source.mtimeMs }, | 1927 | source: { size: imported.source.size, mtimeMs: imported.source.mtimeMs }, |
| 1928 | }); | 1928 | }); |
| 1929 | }).pipe(Effect.provide(makeScannerTestLayer({ claudeHomePath, codexHomePath }))); | 1929 | }).pipe(Effect.provide(layerScannerTest({ claudeHomePath, codexHomePath }))); |
| 1930 | }), | 1930 | }), |
| 1931 | ); | 1931 | ); |
| 1932 | 1932 | ||
| ... | @@ -2350,7 +2350,7 @@ it.layer(NodeServices.layer)("AgentSessionScanner", (it) => { | ... | @@ -2350,7 +2350,7 @@ it.layer(NodeServices.layer)("AgentSessionScanner", (it) => { |
| 2350 | Effect.map((items) => Array.from(items)), | 2350 | Effect.map((items) => Array.from(items)), |
| 2351 | ); | 2351 | ); |
| 2352 | }).pipe( | 2352 | }).pipe( |
| 2353 | Effect.provide(makeScannerTestLayer({ claudeHomePath, codexHomePath })), | 2353 | Effect.provide(layerScannerTest({ claudeHomePath, codexHomePath })), |
| 2354 | Effect.provideService(FileSystem.FileSystem, simulatedFileSystem), | 2354 | Effect.provideService(FileSystem.FileSystem, simulatedFileSystem), |
| 2355 | ); | 2355 | ); |
| 2356 | 2356 | ||
| ... | @@ -2573,7 +2573,7 @@ it.layer(NodeServices.layer)("AgentSessionScanner", (it) => { | ... | @@ -2573,7 +2573,7 @@ it.layer(NodeServices.layer)("AgentSessionScanner", (it) => { |
| 2573 | expect(scan.truncated).toBe(true); | 2573 | expect(scan.truncated).toBe(true); |
| 2574 | return yield* scanner.recentThreads(recentWorkspace).pipe(Stream.runCollect); | 2574 | return yield* scanner.recentThreads(recentWorkspace).pipe(Stream.runCollect); |
| 2575 | }).pipe( | 2575 | }).pipe( |
| 2576 | Effect.provide(makeScannerTestLayer(input)), | 2576 | Effect.provide(layerScannerTest(input)), |
| 2577 | Effect.provideService(FileSystem.FileSystem, simulatedFileSystem), | 2577 | Effect.provideService(FileSystem.FileSystem, simulatedFileSystem), |
| 2578 | ); | 2578 | ); |
| 2579 | 2579 |
apps/server/src/project/ManagedProjectFolders.test.ts+13-13| ... | @@ -12,8 +12,8 @@ import * as Scope from "effect/Scope"; | ... | @@ -12,8 +12,8 @@ import * as Scope from "effect/Scope"; |
| 12 | 12 | ||
| 13 | import * as ServerConfig from "../config.ts"; | 13 | import * as ServerConfig from "../config.ts"; |
| 14 | import * as GitWorkflow from "../git/GitWorkflowService.ts"; | 14 | import * as GitWorkflow from "../git/GitWorkflowService.ts"; |
| 15 | import { ProjectServiceLayerLive } from "../orchestration-v2/runtimeLayer.ts"; | 15 | import * as RuntimeLayer from "../orchestration-v2/runtimeLayer.ts"; |
| 16 | import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; | 16 | import * as SqlitePersistence from "../persistence/Layers/Sqlite.ts"; |
| 17 | import * as ProcessRunner from "../processRunner.ts"; | 17 | import * as ProcessRunner from "../processRunner.ts"; |
| 18 | import * as GitVcsDriver from "../vcs/GitVcsDriver.ts"; | 18 | import * as GitVcsDriver from "../vcs/GitVcsDriver.ts"; |
| 19 | import * as VcsDriverRegistry from "../vcs/VcsDriverRegistry.ts"; | 19 | import * as VcsDriverRegistry from "../vcs/VcsDriverRegistry.ts"; |
| ... | @@ -27,7 +27,7 @@ import * as ManagedProjectFolders from "./ManagedProjectFolders.ts"; | ... | @@ -27,7 +27,7 @@ import * as ManagedProjectFolders from "./ManagedProjectFolders.ts"; |
| 27 | 27 | ||
| 28 | // Real repository detection: the service only asks the Git workflow whether | 28 | // Real repository detection: the service only asks the Git workflow whether |
| 29 | // the data dir is inside a checkout. | 29 | // the data dir is inside a checkout. |
| 30 | const gitWorkflowLayer = Layer.unwrap( | 30 | const layerGitWorkflow = Layer.unwrap( |
| 31 | Effect.gen(function* () { | 31 | Effect.gen(function* () { |
| 32 | const registry = yield* VcsDriverRegistry.VcsDriverRegistry; | 32 | const registry = yield* VcsDriverRegistry.VcsDriverRegistry; |
| 33 | return Layer.mock(GitWorkflow.GitWorkflowService)({ | 33 | return Layer.mock(GitWorkflow.GitWorkflowService)({ |
| ... | @@ -40,7 +40,7 @@ const gitWorkflowLayer = Layer.unwrap( | ... | @@ -40,7 +40,7 @@ const gitWorkflowLayer = Layer.unwrap( |
| 40 | }), | 40 | }), |
| 41 | ).pipe(Layer.provide(VcsDriverRegistry.layer.pipe(Layer.provide(VcsProcess.layer)))); | 41 | ).pipe(Layer.provide(VcsDriverRegistry.layer.pipe(Layer.provide(VcsProcess.layer)))); |
| 42 | 42 | ||
| 43 | const enrichmentLayer = ProjectEnrichmentService.layer.pipe( | 43 | const layerEnrichment = ProjectEnrichmentService.layer.pipe( |
| 44 | Layer.provide( | 44 | Layer.provide( |
| 45 | Layer.mergeAll( | 45 | Layer.mergeAll( |
| 46 | Layer.succeed(RepositoryIdentityResolver.RepositoryIdentityResolver, { | 46 | Layer.succeed(RepositoryIdentityResolver.RepositoryIdentityResolver, { |
| ... | @@ -53,7 +53,7 @@ const enrichmentLayer = ProjectEnrichmentService.layer.pipe( | ... | @@ -53,7 +53,7 @@ const enrichmentLayer = ProjectEnrichmentService.layer.pipe( |
| 53 | ), | 53 | ), |
| 54 | ); | 54 | ); |
| 55 | 55 | ||
| 56 | const realGitLayer = GitVcsDriver.layer.pipe(Layer.provide(VcsProcess.layer)); | 56 | const layerRealGit = GitVcsDriver.layer.pipe(Layer.provide(VcsProcess.layer)); |
| 57 | 57 | ||
| 58 | interface HarnessOptions { | 58 | interface HarnessOptions { |
| 59 | /** A git driver for failures real git cannot produce on demand. */ | 59 | /** A git driver for failures real git cannot produce on demand. */ |
| ... | @@ -68,7 +68,7 @@ interface HarnessOptions { | ... | @@ -68,7 +68,7 @@ interface HarnessOptions { |
| 68 | * The service over a real ProjectService and real git, with its data dir at | 68 | * The service over a real ProjectService and real git, with its data dir at |
| 69 | * `baseDir`. | 69 | * `baseDir`. |
| 70 | */ | 70 | */ |
| 71 | const makeLayer = (baseDir: string, options?: HarnessOptions) => | 71 | const layer = (baseDir: string, options?: HarnessOptions) => |
| 72 | ManagedProjectFolders.layer.pipe( | 72 | ManagedProjectFolders.layer.pipe( |
| 73 | Layer.provide( | 73 | Layer.provide( |
| 74 | options?.projects === undefined | 74 | options?.projects === undefined |
| ... | @@ -78,12 +78,12 @@ const makeLayer = (baseDir: string, options?: HarnessOptions) => | ... | @@ -78,12 +78,12 @@ const makeLayer = (baseDir: string, options?: HarnessOptions) => |
| 78 | ProjectService.ProjectService.pipe(Effect.map(options.projects)), | 78 | ProjectService.ProjectService.pipe(Effect.map(options.projects)), |
| 79 | ), | 79 | ), |
| 80 | ), | 80 | ), |
| 81 | Layer.provideMerge(ProjectServiceLayerLive), | 81 | Layer.provideMerge(RuntimeLayer.layerProjectService), |
| 82 | Layer.provideMerge(enrichmentLayer), | 82 | Layer.provideMerge(layerEnrichment), |
| 83 | Layer.provideMerge(WorkspacePaths.layer), | 83 | Layer.provideMerge(WorkspacePaths.layer), |
| 84 | Layer.provideMerge(gitWorkflowLayer), | 84 | Layer.provideMerge(layerGitWorkflow), |
| 85 | Layer.provideMerge(options?.git ?? realGitLayer), | 85 | Layer.provideMerge(options?.git ?? layerRealGit), |
| 86 | Layer.provideMerge(SqlitePersistenceMemory), | 86 | Layer.provideMerge(SqlitePersistence.layerMemory), |
| 87 | Layer.provideMerge(ServerConfig.layerTest(baseDir, baseDir)), | 87 | Layer.provideMerge(ServerConfig.layerTest(baseDir, baseDir)), |
| 88 | Layer.provideMerge(NodeServices.layer), | 88 | Layer.provideMerge(NodeServices.layer), |
| 89 | ); | 89 | ); |
| ... | @@ -105,7 +105,7 @@ const withScratch = <A, E>( | ... | @@ -105,7 +105,7 @@ const withScratch = <A, E>( |
| 105 | Effect.gen(function* () { | 105 | Effect.gen(function* () { |
| 106 | const fileSystem = yield* FileSystem.FileSystem; | 106 | const fileSystem = yield* FileSystem.FileSystem; |
| 107 | const baseDir = yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3-managed-folders-" }); | 107 | const baseDir = yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3-managed-folders-" }); |
| 108 | return yield* body({ baseDir }).pipe(Effect.provide(makeLayer(baseDir, options))); | 108 | return yield* body({ baseDir }).pipe(Effect.provide(layer(baseDir, options))); |
| 109 | }).pipe(Effect.scoped, Effect.provide(NodeServices.layer)); | 109 | }).pipe(Effect.scoped, Effect.provide(NodeServices.layer)); |
| 110 | 110 | ||
| 111 | const git = (cwd: string, args: ReadonlyArray<string>) => | 111 | const git = (cwd: string, args: ReadonlyArray<string>) => |
| ... | @@ -141,7 +141,7 @@ it.effect("offers nothing when the data dir sits inside a Git checkout", () => | ... | @@ -141,7 +141,7 @@ it.effect("offers nothing when the data dir sits inside a Git checkout", () => |
| 141 | assert.isTrue(Option.isNone(yield* scratch.scratchRoot)); | 141 | assert.isTrue(Option.isNone(yield* scratch.scratchRoot)); |
| 142 | const failure = yield* Effect.flip(scratch.ensureScratchProject); | 142 | const failure = yield* Effect.flip(scratch.ensureScratchProject); |
| 143 | assert.equal(failure._tag, "ScratchUnavailableError"); | 143 | assert.equal(failure._tag, "ScratchUnavailableError"); |
| 144 | }).pipe(Effect.provide(makeLayer(baseDir))); | 144 | }).pipe(Effect.provide(layer(baseDir))); |
| 145 | }).pipe(Effect.scoped, Effect.provide(NodeServices.layer)), | 145 | }).pipe(Effect.scoped, Effect.provide(NodeServices.layer)), |
| 146 | ); | 146 | ); |
| 147 | 147 |
apps/server/src/project/ProjectEnrichmentService.test.ts+13-13| ... | @@ -33,7 +33,7 @@ const waitForAvailable = Effect.fn("ProjectEnrichmentServiceTest.waitForAvailabl | ... | @@ -33,7 +33,7 @@ const waitForAvailable = Effect.fn("ProjectEnrichmentServiceTest.waitForAvailabl |
| 33 | return yield* Effect.die(`Project metadata for ${workspaceRoot} was not resolved in time.`); | 33 | return yield* Effect.die(`Project metadata for ${workspaceRoot} was not resolved in time.`); |
| 34 | }); | 34 | }); |
| 35 | 35 | ||
| 36 | const makeLayer = ( | 36 | const layer = ( |
| 37 | metadataLayer: Layer.Layer< | 37 | metadataLayer: Layer.Layer< |
| 38 | | ProjectFaviconResolver.ProjectFaviconResolver | 38 | | ProjectFaviconResolver.ProjectFaviconResolver |
| 39 | | RepositoryIdentityResolver.RepositoryIdentityResolver | 39 | | RepositoryIdentityResolver.RepositoryIdentityResolver |
| ... | @@ -46,7 +46,7 @@ const makeLayer = ( | ... | @@ -46,7 +46,7 @@ const makeLayer = ( |
| 46 | 46 | ||
| 47 | it.effect("preserves either enrichment field when the other resolver fails", () => | 47 | it.effect("preserves either enrichment field when the other resolver fails", () => |
| 48 | Effect.gen(function* () { | 48 | Effect.gen(function* () { |
| 49 | const metadataLayer = Layer.merge( | 49 | const layerMetadata = Layer.merge( |
| 50 | Layer.succeed(RepositoryIdentityResolver.RepositoryIdentityResolver, { | 50 | Layer.succeed(RepositoryIdentityResolver.RepositoryIdentityResolver, { |
| 51 | resolve: (workspaceRoot) => | 51 | resolve: (workspaceRoot) => |
| 52 | workspaceRoot === "/repo-fails" | 52 | workspaceRoot === "/repo-fails" |
| ... | @@ -90,14 +90,14 @@ it.effect("preserves either enrichment field when the other resolver fails", () | ... | @@ -90,14 +90,14 @@ it.effect("preserves either enrichment field when the other resolver fails", () |
| 90 | ); | 90 | ); |
| 91 | assert.isNull(repositoryFailure.repositoryIdentity); | 91 | assert.isNull(repositoryFailure.repositoryIdentity); |
| 92 | assert.equal(repositoryFailure.faviconPath, "/repo-fails/favicon.svg"); | 92 | assert.equal(repositoryFailure.faviconPath, "/repo-fails/favicon.svg"); |
| 93 | }).pipe(Effect.provide(makeLayer(metadataLayer))); | 93 | }).pipe(Effect.provide(layer(layerMetadata))); |
| 94 | }), | 94 | }), |
| 95 | ); | 95 | ); |
| 96 | 96 | ||
| 97 | it.effect("publishes repository completion while favicon enrichment is still pending", () => | 97 | it.effect("publishes repository completion while favicon enrichment is still pending", () => |
| 98 | Effect.gen(function* () { | 98 | Effect.gen(function* () { |
| 99 | const releaseFavicon = yield* Deferred.make<void>(); | 99 | const releaseFavicon = yield* Deferred.make<void>(); |
| 100 | const metadataLayer = Layer.merge( | 100 | const layerMetadata = Layer.merge( |
| 101 | Layer.succeed(RepositoryIdentityResolver.RepositoryIdentityResolver, { | 101 | Layer.succeed(RepositoryIdentityResolver.RepositoryIdentityResolver, { |
| 102 | resolve: (workspaceRoot) => Effect.succeed(identity(workspaceRoot)), | 102 | resolve: (workspaceRoot) => Effect.succeed(identity(workspaceRoot)), |
| 103 | }), | 103 | }), |
| ... | @@ -117,7 +117,7 @@ it.effect("publishes repository completion while favicon enrichment is still pen | ... | @@ -117,7 +117,7 @@ it.effect("publishes repository completion while favicon enrichment is still pen |
| 117 | assert.isTrue(change.repositoryIdentityResolved); | 117 | assert.isTrue(change.repositoryIdentityResolved); |
| 118 | assert.equal(change.enrichment.repositoryIdentity?.canonicalKey, "example.test/v1/completed"); | 118 | assert.equal(change.enrichment.repositoryIdentity?.canonicalKey, "example.test/v1/completed"); |
| 119 | assert.isNull(change.enrichment.faviconPath); | 119 | assert.isNull(change.enrichment.faviconPath); |
| 120 | }).pipe(Effect.provide(makeLayer(metadataLayer))); | 120 | }).pipe(Effect.provide(layer(layerMetadata))); |
| 121 | }), | 121 | }), |
| 122 | ); | 122 | ); |
| 123 | 123 | ||
| ... | @@ -125,7 +125,7 @@ it.effect("keeps repository workers available when every favicon worker is hung" | ... | @@ -125,7 +125,7 @@ it.effect("keeps repository workers available when every favicon worker is hung" |
| 125 | Effect.gen(function* () { | 125 | Effect.gen(function* () { |
| 126 | const faviconWorkersStarted = yield* Deferred.make<void>(); | 126 | const faviconWorkersStarted = yield* Deferred.make<void>(); |
| 127 | const faviconStarts = yield* Ref.make(0); | 127 | const faviconStarts = yield* Ref.make(0); |
| 128 | const metadataLayer = Layer.merge( | 128 | const layerMetadata = Layer.merge( |
| 129 | Layer.succeed(RepositoryIdentityResolver.RepositoryIdentityResolver, { | 129 | Layer.succeed(RepositoryIdentityResolver.RepositoryIdentityResolver, { |
| 130 | resolve: (workspaceRoot) => Effect.succeed(identity(workspaceRoot)), | 130 | resolve: (workspaceRoot) => Effect.succeed(identity(workspaceRoot)), |
| 131 | }), | 131 | }), |
| ... | @@ -158,7 +158,7 @@ it.effect("keeps repository workers available when every favicon worker is hung" | ... | @@ -158,7 +158,7 @@ it.effect("keeps repository workers available when every favicon worker is hung" |
| 158 | assert.equal(yield* Ref.get(faviconStarts), 2); | 158 | assert.equal(yield* Ref.get(faviconStarts), 2); |
| 159 | }).pipe( | 159 | }).pipe( |
| 160 | Effect.provide( | 160 | Effect.provide( |
| 161 | makeLayer(metadataLayer, { | 161 | layer(layerMetadata, { |
| 162 | cacheCapacity: 8, | 162 | cacheCapacity: 8, |
| 163 | maxPending: 4, | 163 | maxPending: 4, |
| 164 | concurrency: 2, | 164 | concurrency: 2, |
| ... | @@ -172,7 +172,7 @@ it.effect("getAvailable returns immediately while repository identity is still u | ... | @@ -172,7 +172,7 @@ it.effect("getAvailable returns immediately while repository identity is still u |
| 172 | Effect.gen(function* () { | 172 | Effect.gen(function* () { |
| 173 | const repositoryStarted = yield* Deferred.make<void>(); | 173 | const repositoryStarted = yield* Deferred.make<void>(); |
| 174 | const releaseRepository = yield* Deferred.make<void>(); | 174 | const releaseRepository = yield* Deferred.make<void>(); |
| 175 | const metadataLayer = Layer.merge( | 175 | const layerMetadata = Layer.merge( |
| 176 | Layer.succeed(RepositoryIdentityResolver.RepositoryIdentityResolver, { | 176 | Layer.succeed(RepositoryIdentityResolver.RepositoryIdentityResolver, { |
| 177 | resolve: (workspaceRoot) => | 177 | resolve: (workspaceRoot) => |
| 178 | Deferred.succeed(repositoryStarted, undefined).pipe( | 178 | Deferred.succeed(repositoryStarted, undefined).pipe( |
| ... | @@ -205,7 +205,7 @@ it.effect("getAvailable returns immediately while repository identity is still u | ... | @@ -205,7 +205,7 @@ it.effect("getAvailable returns immediately while repository identity is still u |
| 205 | ); | 205 | ); |
| 206 | assert.equal(resolved.repositoryIdentity?.canonicalKey, "example.test/v1/pending-identity"); | 206 | assert.equal(resolved.repositoryIdentity?.canonicalKey, "example.test/v1/pending-identity"); |
| 207 | assert.isTrue(resolved.repositoryIdentityResolved); | 207 | assert.isTrue(resolved.repositoryIdentityResolved); |
| 208 | }).pipe(Effect.provide(makeLayer(metadataLayer))); | 208 | }).pipe(Effect.provide(layer(layerMetadata))); |
| 209 | }), | 209 | }), |
| 210 | ); | 210 | ); |
| 211 | 211 | ||
| ... | @@ -215,7 +215,7 @@ it.effect( | ... | @@ -215,7 +215,7 @@ it.effect( |
| 215 | Effect.gen(function* () { | 215 | Effect.gen(function* () { |
| 216 | const repositoryStarted = yield* Deferred.make<void>(); | 216 | const repositoryStarted = yield* Deferred.make<void>(); |
| 217 | const releaseRepository = yield* Deferred.make<void>(); | 217 | const releaseRepository = yield* Deferred.make<void>(); |
| 218 | const metadataLayer = Layer.merge( | 218 | const layerMetadata = Layer.merge( |
| 219 | Layer.succeed(RepositoryIdentityResolver.RepositoryIdentityResolver, { | 219 | Layer.succeed(RepositoryIdentityResolver.RepositoryIdentityResolver, { |
| 220 | resolve: (workspaceRoot) => { | 220 | resolve: (workspaceRoot) => { |
| 221 | if (workspaceRoot === "/no-remote") { | 221 | if (workspaceRoot === "/no-remote") { |
| ... | @@ -268,7 +268,7 @@ it.effect( | ... | @@ -268,7 +268,7 @@ it.effect( |
| 268 | const failed = yield* service.peek("/fails"); | 268 | const failed = yield* service.peek("/fails"); |
| 269 | assert.isNull(failed.repositoryIdentity); | 269 | assert.isNull(failed.repositoryIdentity); |
| 270 | assert.isFalse(failed.repositoryIdentityResolved); | 270 | assert.isFalse(failed.repositoryIdentityResolved); |
| 271 | }).pipe(Effect.provide(makeLayer(metadataLayer))); | 271 | }).pipe(Effect.provide(layer(layerMetadata))); |
| 272 | }), | 272 | }), |
| 273 | ); | 273 | ); |
| 274 | 274 | ||
| ... | @@ -278,7 +278,7 @@ it.effect("deduplicates requests, bounds pending work, and reloads invalidated r | ... | @@ -278,7 +278,7 @@ it.effect("deduplicates requests, bounds pending work, and reloads invalidated r |
| 278 | const releaseFirst = yield* Deferred.make<void>(); | 278 | const releaseFirst = yield* Deferred.make<void>(); |
| 279 | const repositoryCalls = yield* Ref.make<ReadonlyArray<string>>([]); | 279 | const repositoryCalls = yield* Ref.make<ReadonlyArray<string>>([]); |
| 280 | const version = yield* Ref.make(1); | 280 | const version = yield* Ref.make(1); |
| 281 | const metadataLayer = Layer.merge( | 281 | const layerMetadata = Layer.merge( |
| 282 | Layer.succeed(RepositoryIdentityResolver.RepositoryIdentityResolver, { | 282 | Layer.succeed(RepositoryIdentityResolver.RepositoryIdentityResolver, { |
| 283 | resolve: (workspaceRoot) => | 283 | resolve: (workspaceRoot) => |
| 284 | Effect.gen(function* () { | 284 | Effect.gen(function* () { |
| ... | @@ -332,7 +332,7 @@ it.effect("deduplicates requests, bounds pending work, and reloads invalidated r | ... | @@ -332,7 +332,7 @@ it.effect("deduplicates requests, bounds pending work, and reloads invalidated r |
| 332 | ]); | 332 | ]); |
| 333 | }).pipe( | 333 | }).pipe( |
| 334 | Effect.provide( | 334 | Effect.provide( |
| 335 | makeLayer(metadataLayer, { | 335 | layer(layerMetadata, { |
| 336 | cacheCapacity: 8, | 336 | cacheCapacity: 8, |
| 337 | maxPending: 2, | 337 | maxPending: 2, |
| 338 | concurrency: 1, | 338 | concurrency: 1, |
apps/server/src/project/ProjectFaviconResolver.test.ts+2-2| ... | @@ -12,7 +12,7 @@ import * as WorkspacePaths from "../workspace/WorkspacePaths.ts"; | ... | @@ -12,7 +12,7 @@ import * as WorkspacePaths from "../workspace/WorkspacePaths.ts"; |
| 12 | import * as ProjectFaviconResolver from "./ProjectFaviconResolver.ts"; | 12 | import * as ProjectFaviconResolver from "./ProjectFaviconResolver.ts"; |
| 13 | import * as T3ProjectFileLoader from "./T3ProjectFileLoader.ts"; | 13 | import * as T3ProjectFileLoader from "./T3ProjectFileLoader.ts"; |
| 14 | 14 | ||
| 15 | const TestLayer = Layer.empty.pipe( | 15 | const layerTest = Layer.empty.pipe( |
| 16 | Layer.provideMerge( | 16 | Layer.provideMerge( |
| 17 | ProjectFaviconResolver.layer.pipe( | 17 | ProjectFaviconResolver.layer.pipe( |
| 18 | Layer.provide(WorkspacePaths.layer), | 18 | Layer.provide(WorkspacePaths.layer), |
| ... | @@ -49,7 +49,7 @@ const makeResolverWithFileSystem = (fileSystem: FileSystem.FileSystem) => | ... | @@ -49,7 +49,7 @@ const makeResolverWithFileSystem = (fileSystem: FileSystem.FileSystem) => |
| 49 | Effect.provideService(FileSystem.FileSystem, fileSystem), | 49 | Effect.provideService(FileSystem.FileSystem, fileSystem), |
| 50 | ); | 50 | ); |
| 51 | 51 | ||
| 52 | it.layer(TestLayer)("ProjectFaviconResolverLive", (it) => { | 52 | it.layer(layerTest)("ProjectFaviconResolverLive", (it) => { |
| 53 | describe("resolvePath", () => { | 53 | describe("resolvePath", () => { |
| 54 | it.effect("serves repeated resolves from cache instead of re-walking candidates", () => | 54 | it.effect("serves repeated resolves from cache instead of re-walking candidates", () => |
| 55 | Effect.gen(function* () { | 55 | Effect.gen(function* () { |
apps/server/src/project/ProjectService.deletion.test.ts+14-14| ... | @@ -27,19 +27,19 @@ import * as ProjectionStore from "../orchestration-v2/ProjectionStore.ts"; | ... | @@ -27,19 +27,19 @@ import * as ProjectionStore from "../orchestration-v2/ProjectionStore.ts"; |
| 27 | import * as ProjectStore from "../orchestration-v2/ProjectStore.ts"; | 27 | import * as ProjectStore from "../orchestration-v2/ProjectStore.ts"; |
| 28 | import * as ThreadCommandExecutor from "../orchestration-v2/ThreadCommandExecutor.ts"; | 28 | import * as ThreadCommandExecutor from "../orchestration-v2/ThreadCommandExecutor.ts"; |
| 29 | import { planThreadDeletion } from "../orchestration-v2/ThreadDeletion.ts"; | 29 | import { planThreadDeletion } from "../orchestration-v2/ThreadDeletion.ts"; |
| 30 | import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; | 30 | import * as SqlitePersistence from "../persistence/Layers/Sqlite.ts"; |
| 31 | import * as WorkspacePaths from "../workspace/WorkspacePaths.ts"; | 31 | import * as WorkspacePaths from "../workspace/WorkspacePaths.ts"; |
| 32 | import * as ProjectEnrichmentService from "./ProjectEnrichmentService.ts"; | 32 | import * as ProjectEnrichmentService from "./ProjectEnrichmentService.ts"; |
| 33 | import * as ProjectFaviconResolver from "./ProjectFaviconResolver.ts"; | 33 | import * as ProjectFaviconResolver from "./ProjectFaviconResolver.ts"; |
| 34 | import * as ProjectService from "./ProjectService.ts"; | 34 | import * as ProjectService from "./ProjectService.ts"; |
| 35 | import * as RepositoryIdentityResolver from "./RepositoryIdentityResolver.ts"; | 35 | import * as RepositoryIdentityResolver from "./RepositoryIdentityResolver.ts"; |
| 36 | 36 | ||
| 37 | const eventPersistenceLayer = EventSink.layer.pipe( | 37 | const layerEventPersistence = EventSink.layer.pipe( |
| 38 | Layer.provideMerge(Layer.merge(EventStore.layer, ProjectionStore.layer)), | 38 | Layer.provideMerge(Layer.merge(EventStore.layer, ProjectionStore.layer)), |
| 39 | ); | 39 | ); |
| 40 | const servicesLayer = Layer.mergeAll( | 40 | const layerServices = Layer.mergeAll( |
| 41 | LegacyV1ThreadImporter.layer.pipe(Layer.provideMerge(eventPersistenceLayer)), | 41 | LegacyV1ThreadImporter.layer.pipe(Layer.provideMerge(layerEventPersistence)), |
| 42 | ProjectionMaintenance.layer.pipe(Layer.provide(eventPersistenceLayer)), | 42 | ProjectionMaintenance.layer.pipe(Layer.provide(layerEventPersistence)), |
| 43 | ProjectStore.layer, | 43 | ProjectStore.layer, |
| 44 | IdAllocator.layer, | 44 | IdAllocator.layer, |
| 45 | ThreadCommandExecutor.layer, | 45 | ThreadCommandExecutor.layer, |
| ... | @@ -64,7 +64,7 @@ const servicesLayer = Layer.mergeAll( | ... | @@ -64,7 +64,7 @@ const servicesLayer = Layer.mergeAll( |
| 64 | ), | 64 | ), |
| 65 | ), | 65 | ), |
| 66 | ); | 66 | ); |
| 67 | const databaseLayer = SqlitePersistenceMemory.pipe( | 67 | const layerDatabase = SqlitePersistence.layerMemory.pipe( |
| 68 | Layer.provideMerge(ServerConfig.layerTest(process.cwd(), { prefix: "project-deletion-test-" })), | 68 | Layer.provideMerge(ServerConfig.layerTest(process.cwd(), { prefix: "project-deletion-test-" })), |
| 69 | Layer.provideMerge(NodeServices.layer), | 69 | Layer.provideMerge(NodeServices.layer), |
| 70 | ); | 70 | ); |
| ... | @@ -239,8 +239,8 @@ it.effect("retries a partial project deletion without repeating child events or | ... | @@ -239,8 +239,8 @@ it.effect("retries a partial project deletion without repeating child events or |
| 239 | ], | 239 | ], |
| 240 | ); | 240 | ); |
| 241 | } | 241 | } |
| 242 | }).pipe(Effect.provide(servicesLayer)); | 242 | }).pipe(Effect.provide(layerServices)); |
| 243 | }).pipe(Effect.provide(databaseLayer)), | 243 | }).pipe(Effect.provide(layerDatabase)), |
| 244 | ); | 244 | ); |
| 245 | 245 | ||
| 246 | it.effect( | 246 | it.effect( |
| ... | @@ -354,8 +354,8 @@ it.effect( | ... | @@ -354,8 +354,8 @@ it.effect( |
| 354 | type: "attachment.cleanup", | 354 | type: "attachment.cleanup", |
| 355 | attachmentIds: ["legacy_screenshot"], | 355 | attachmentIds: ["legacy_screenshot"], |
| 356 | }); | 356 | }); |
| 357 | }).pipe(Effect.provide(servicesLayer)); | 357 | }).pipe(Effect.provide(layerServices)); |
| 358 | }).pipe(Effect.provide(databaseLayer)), | 358 | }).pipe(Effect.provide(layerDatabase)), |
| 359 | ); | 359 | ); |
| 360 | 360 | ||
| 361 | it.effect("rejects a child deletion command ID already accepted for an unrelated thread", () => | 361 | it.effect("rejects a child deletion command ID already accepted for an unrelated thread", () => |
| ... | @@ -404,8 +404,8 @@ it.effect("rejects a child deletion command ID already accepted for an unrelated | ... | @@ -404,8 +404,8 @@ it.effect("rejects a child deletion command ID already accepted for an unrelated |
| 404 | WHERE thread_id = ${threadId} | 404 | WHERE thread_id = ${threadId} |
| 405 | `; | 405 | `; |
| 406 | assert.deepEqual(cleanup, []); | 406 | assert.deepEqual(cleanup, []); |
| 407 | }).pipe(Effect.provide(servicesLayer)); | 407 | }).pipe(Effect.provide(layerServices)); |
| 408 | }).pipe(Effect.provide(databaseLayer)), | 408 | }).pipe(Effect.provide(layerDatabase)), |
| 409 | ); | 409 | ); |
| 410 | 410 | ||
| 411 | it.effect("deletes a project without force once its imported threads were deleted in V2", () => | 411 | it.effect("deletes a project without force once its imported threads were deleted in V2", () => |
| ... | @@ -481,6 +481,6 @@ it.effect("deletes a project without force once its imported threads were delete | ... | @@ -481,6 +481,6 @@ it.effect("deletes a project without force once its imported threads were delete |
| 481 | }); | 481 | }); |
| 482 | assert.isNotNull(deleted.deletedAt); | 482 | assert.isNotNull(deleted.deletedAt); |
| 483 | assert.isTrue(Option.isNone(yield* service.getById(projectId))); | 483 | assert.isTrue(Option.isNone(yield* service.getById(projectId))); |
| 484 | }).pipe(Effect.provide(servicesLayer)); | 484 | }).pipe(Effect.provide(layerServices)); |
| 485 | }).pipe(Effect.provide(databaseLayer)), | 485 | }).pipe(Effect.provide(layerDatabase)), |
| 486 | ); | 486 | ); |
apps/server/src/project/ProjectService.test.ts+24-29| ... | @@ -16,25 +16,22 @@ import * as IdAllocator from "../orchestration-v2/IdAllocator.ts"; | ... | @@ -16,25 +16,22 @@ import * as IdAllocator from "../orchestration-v2/IdAllocator.ts"; |
| 16 | import * as LegacyV1ThreadImporter from "../orchestration-v2/legacy/LegacyV1ThreadImporter.ts"; | 16 | import * as LegacyV1ThreadImporter from "../orchestration-v2/legacy/LegacyV1ThreadImporter.ts"; |
| 17 | import * as ProjectionStore from "../orchestration-v2/ProjectionStore.ts"; | 17 | import * as ProjectionStore from "../orchestration-v2/ProjectionStore.ts"; |
| 18 | import * as ProjectStore from "../orchestration-v2/ProjectStore.ts"; | 18 | import * as ProjectStore from "../orchestration-v2/ProjectStore.ts"; |
| 19 | import { | 19 | import * as RuntimeLayer from "../orchestration-v2/runtimeLayer.ts"; |
| 20 | OrchestrationV2EventSinkLayerLive, | ||
| 21 | ProjectServiceLayerLive, | ||
| 22 | } from "../orchestration-v2/runtimeLayer.ts"; | ||
| 23 | import * as ThreadCommandExecutor from "../orchestration-v2/ThreadCommandExecutor.ts"; | 20 | import * as ThreadCommandExecutor from "../orchestration-v2/ThreadCommandExecutor.ts"; |
| 24 | import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; | 21 | import * as SqlitePersistence from "../persistence/Layers/Sqlite.ts"; |
| 25 | import * as WorkspacePaths from "../workspace/WorkspacePaths.ts"; | 22 | import * as WorkspacePaths from "../workspace/WorkspacePaths.ts"; |
| 26 | import * as ProjectEnrichmentService from "./ProjectEnrichmentService.ts"; | 23 | import * as ProjectEnrichmentService from "./ProjectEnrichmentService.ts"; |
| 27 | import * as ProjectFaviconResolver from "./ProjectFaviconResolver.ts"; | 24 | import * as ProjectFaviconResolver from "./ProjectFaviconResolver.ts"; |
| 28 | import * as ProjectService from "./ProjectService.ts"; | 25 | import * as ProjectService from "./ProjectService.ts"; |
| 29 | import * as RepositoryIdentityResolver from "./RepositoryIdentityResolver.ts"; | 26 | import * as RepositoryIdentityResolver from "./RepositoryIdentityResolver.ts"; |
| 30 | 27 | ||
| 31 | const workspacePathsLayer = Layer.succeed(WorkspacePaths.WorkspacePaths, { | 28 | const layerWorkspacePaths = Layer.succeed(WorkspacePaths.WorkspacePaths, { |
| 32 | normalizeWorkspaceRoot: (workspaceRoot) => Effect.succeed(workspaceRoot.replace(/\/$/, "")), | 29 | normalizeWorkspaceRoot: (workspaceRoot) => Effect.succeed(workspaceRoot.replace(/\/$/, "")), |
| 33 | resolveRelativePathWithinRoot: ({ workspaceRoot, relativePath }) => | 30 | resolveRelativePathWithinRoot: ({ workspaceRoot, relativePath }) => |
| 34 | Effect.succeed({ absolutePath: `${workspaceRoot}/${relativePath}`, relativePath }), | 31 | Effect.succeed({ absolutePath: `${workspaceRoot}/${relativePath}`, relativePath }), |
| 35 | }); | 32 | }); |
| 36 | 33 | ||
| 37 | const metadataLayer = Layer.merge( | 34 | const layerMetadata = Layer.merge( |
| 38 | Layer.succeed(RepositoryIdentityResolver.RepositoryIdentityResolver, { | 35 | Layer.succeed(RepositoryIdentityResolver.RepositoryIdentityResolver, { |
| 39 | resolve: (workspaceRoot) => | 36 | resolve: (workspaceRoot) => |
| 40 | Effect.succeed({ | 37 | Effect.succeed({ |
| ... | @@ -52,38 +49,36 @@ const metadataLayer = Layer.merge( | ... | @@ -52,38 +49,36 @@ const metadataLayer = Layer.merge( |
| 52 | }), | 49 | }), |
| 53 | ); | 50 | ); |
| 54 | 51 | ||
| 55 | const makeTestLayer = ( | 52 | const layerTestFor = ( |
| 56 | projectMetadataLayer: Layer.Layer< | 53 | projectMetadataLayer: Layer.Layer< |
| 57 | | ProjectFaviconResolver.ProjectFaviconResolver | 54 | | ProjectFaviconResolver.ProjectFaviconResolver |
| 58 | | RepositoryIdentityResolver.RepositoryIdentityResolver | 55 | | RepositoryIdentityResolver.RepositoryIdentityResolver |
| 59 | >, | 56 | >, |
| 60 | ) => | 57 | ) => |
| 61 | ProjectServiceLayerLive.pipe( | 58 | RuntimeLayer.layerProjectService.pipe( |
| 62 | Layer.provideMerge(ProjectEnrichmentService.layer), | 59 | Layer.provideMerge(ProjectEnrichmentService.layer), |
| 63 | Layer.provideMerge(workspacePathsLayer), | 60 | Layer.provideMerge(layerWorkspacePaths), |
| 64 | Layer.provideMerge(projectMetadataLayer), | 61 | Layer.provideMerge(projectMetadataLayer), |
| 65 | Layer.provideMerge(SqlitePersistenceMemory), | 62 | Layer.provideMerge(SqlitePersistence.layerMemory), |
| 66 | Layer.provide(ServerConfig.layerTest(process.cwd(), { prefix: "project-service-test-" })), | 63 | Layer.provide(ServerConfig.layerTest(process.cwd(), { prefix: "project-service-test-" })), |
| 67 | Layer.provide(NodeServices.layer), | 64 | Layer.provide(NodeServices.layer), |
| 68 | ); | 65 | ); |
| 69 | 66 | ||
| 70 | const TestLayer = makeTestLayer(metadataLayer); | 67 | const layerTest = layerTestFor(layerMetadata); |
| 71 | 68 | ||
| 72 | /** Every dependency of ProjectService.make, so a test can swap one of them. */ | 69 | /** Every dependency of ProjectService.make, so a test can swap one of them. */ |
| 73 | const ProjectServiceDependenciesLayer = Layer.mergeAll( | 70 | const layerProjectServiceDependencies = Layer.mergeAll( |
| 74 | OrchestrationV2EventSinkLayerLive, | 71 | RuntimeLayer.layerEventSink, |
| 75 | ProjectStore.layer, | 72 | ProjectStore.layer, |
| 76 | ProjectionStore.layer, | 73 | ProjectionStore.layer, |
| 77 | IdAllocator.layer, | 74 | IdAllocator.layer, |
| 78 | ThreadCommandExecutor.layer, | 75 | ThreadCommandExecutor.layer, |
| 79 | ).pipe( | 76 | ).pipe( |
| 80 | Layer.provideMerge( | 77 | Layer.provideMerge(LegacyV1ThreadImporter.layer.pipe(Layer.provide(RuntimeLayer.layerEventSink))), |
| 81 | LegacyV1ThreadImporter.layer.pipe(Layer.provide(OrchestrationV2EventSinkLayerLive)), | ||
| 82 | ), | ||
| 83 | Layer.provideMerge(ProjectEnrichmentService.layer), | 78 | Layer.provideMerge(ProjectEnrichmentService.layer), |
| 84 | Layer.provideMerge(workspacePathsLayer), | 79 | Layer.provideMerge(layerWorkspacePaths), |
| 85 | Layer.provideMerge(metadataLayer), | 80 | Layer.provideMerge(layerMetadata), |
| 86 | Layer.provideMerge(SqlitePersistenceMemory), | 81 | Layer.provideMerge(SqlitePersistence.layerMemory), |
| 87 | Layer.provide(ServerConfig.layerTest(process.cwd(), { prefix: "project-service-race-" })), | 82 | Layer.provide(ServerConfig.layerTest(process.cwd(), { prefix: "project-service-race-" })), |
| 88 | Layer.provide(NodeServices.layer), | 83 | Layer.provide(NodeServices.layer), |
| 89 | ); | 84 | ); |
| ... | @@ -101,7 +96,7 @@ const waitForProject = Effect.fn("ProjectServiceTest.waitForProject")(function* | ... | @@ -101,7 +96,7 @@ const waitForProject = Effect.fn("ProjectServiceTest.waitForProject")(function* |
| 101 | return yield* Effect.die(`Project ${projectId} was not enriched in time.`); | 96 | return yield* Effect.die(`Project ${projectId} was not enriched in time.`); |
| 102 | }); | 97 | }); |
| 103 | 98 | ||
| 104 | it.layer(TestLayer)("ProjectService", (it) => { | 99 | it.layer(layerTest)("ProjectService", (it) => { |
| 105 | it.effect("creates, updates, resolves, snapshots, and soft-deletes projects", () => | 100 | it.effect("creates, updates, resolves, snapshots, and soft-deletes projects", () => |
| 106 | Effect.gen(function* () { | 101 | Effect.gen(function* () { |
| 107 | const service = yield* ProjectService.ProjectService; | 102 | const service = yield* ProjectService.ProjectService; |
| ... | @@ -500,7 +495,7 @@ it.effect( | ... | @@ -500,7 +495,7 @@ it.effect( |
| 500 | const repositoryCalls = yield* Ref.make(0); | 495 | const repositoryCalls = yield* Ref.make(0); |
| 501 | const faviconCalls = yield* Ref.make(0); | 496 | const faviconCalls = yield* Ref.make(0); |
| 502 | 497 | ||
| 503 | const slowMetadataLayer = Layer.merge( | 498 | const layerSlowMetadata = Layer.merge( |
| 504 | Layer.succeed(RepositoryIdentityResolver.RepositoryIdentityResolver, { | 499 | Layer.succeed(RepositoryIdentityResolver.RepositoryIdentityResolver, { |
| 505 | resolve: (workspaceRoot) => | 500 | resolve: (workspaceRoot) => |
| 506 | Effect.gen(function* () { | 501 | Effect.gen(function* () { |
| ... | @@ -592,13 +587,13 @@ it.effect( | ... | @@ -592,13 +587,13 @@ it.effect( |
| 592 | assert.equal(secondSnapshot.projects[0]?.faviconPath, "/work/slow-enrichment/favicon.svg"); | 587 | assert.equal(secondSnapshot.projects[0]?.faviconPath, "/work/slow-enrichment/favicon.svg"); |
| 593 | assert.equal(yield* Ref.get(repositoryCalls), 1); | 588 | assert.equal(yield* Ref.get(repositoryCalls), 1); |
| 594 | assert.equal(yield* Ref.get(faviconCalls), 1); | 589 | assert.equal(yield* Ref.get(faviconCalls), 1); |
| 595 | }).pipe(Effect.provide(makeTestLayer(slowMetadataLayer))); | 590 | }).pipe(Effect.provide(layerTestFor(layerSlowMetadata))); |
| 596 | }), | 591 | }), |
| 597 | ); | 592 | ); |
| 598 | 593 | ||
| 599 | it.effect("keeps project snapshots available when optional metadata enrichment fails", () => | 594 | it.effect("keeps project snapshots available when optional metadata enrichment fails", () => |
| 600 | Effect.gen(function* () { | 595 | Effect.gen(function* () { |
| 601 | const failingMetadataLayer = Layer.merge( | 596 | const layerFailingMetadata = Layer.merge( |
| 602 | Layer.succeed(RepositoryIdentityResolver.RepositoryIdentityResolver, { | 597 | Layer.succeed(RepositoryIdentityResolver.RepositoryIdentityResolver, { |
| 603 | resolve: () => Effect.succeed(null), | 598 | resolve: () => Effect.succeed(null), |
| 604 | }), | 599 | }), |
| ... | @@ -630,14 +625,14 @@ it.effect("keeps project snapshots available when optional metadata enrichment f | ... | @@ -630,14 +625,14 @@ it.effect("keeps project snapshots available when optional metadata enrichment f |
| 630 | assert.equal(snapshot.projects[0]?.title, "Still visible"); | 625 | assert.equal(snapshot.projects[0]?.title, "Still visible"); |
| 631 | assert.isNull(snapshot.projects[0]?.repositoryIdentity ?? null); | 626 | assert.isNull(snapshot.projects[0]?.repositoryIdentity ?? null); |
| 632 | assert.isNull(snapshot.projects[0]?.faviconPath ?? null); | 627 | assert.isNull(snapshot.projects[0]?.faviconPath ?? null); |
| 633 | }).pipe(Effect.provide(makeTestLayer(failingMetadataLayer))); | 628 | }).pipe(Effect.provide(layerTestFor(layerFailingMetadata))); |
| 634 | }), | 629 | }), |
| 635 | ); | 630 | ); |
| 636 | 631 | ||
| 637 | it.effect("invalidates workspace-derived metadata when a project moves", () => | 632 | it.effect("invalidates workspace-derived metadata when a project moves", () => |
| 638 | Effect.gen(function* () { | 633 | Effect.gen(function* () { |
| 639 | const metadataVersion = yield* Ref.make(1); | 634 | const metadataVersion = yield* Ref.make(1); |
| 640 | const versionedMetadataLayer = Layer.merge( | 635 | const layerVersionedMetadata = Layer.merge( |
| 641 | Layer.succeed(RepositoryIdentityResolver.RepositoryIdentityResolver, { | 636 | Layer.succeed(RepositoryIdentityResolver.RepositoryIdentityResolver, { |
| 642 | resolve: (workspaceRoot) => | 637 | resolve: (workspaceRoot) => |
| 643 | Ref.get(metadataVersion).pipe( | 638 | Ref.get(metadataVersion).pipe( |
| ... | @@ -700,7 +695,7 @@ it.effect("invalidates workspace-derived metadata when a project moves", () => | ... | @@ -700,7 +695,7 @@ it.effect("invalidates workspace-derived metadata when a project moves", () => |
| 700 | )).faviconPath, | 695 | )).faviconPath, |
| 701 | "/work/original/favicon-v3.svg", | 696 | "/work/original/favicon-v3.svg", |
| 702 | ); | 697 | ); |
| 703 | }).pipe(Effect.provide(makeTestLayer(versionedMetadataLayer))); | 698 | }).pipe(Effect.provide(layerTestFor(layerVersionedMetadata))); |
| 704 | }), | 699 | }), |
| 705 | ); | 700 | ); |
| 706 | 701 | ||
| ... | @@ -741,7 +736,7 @@ it.effect("serializes two projects claiming the same workspace root", () => | ... | @@ -741,7 +736,7 @@ it.effect("serializes two projects claiming the same workspace root", () => |
| 741 | yield* Deferred.succeed(releaseFirst, undefined); | 736 | yield* Deferred.succeed(releaseFirst, undefined); |
| 742 | assert.equal((yield* Fiber.join(first)).id, "project:race:first"); | 737 | assert.equal((yield* Fiber.join(first)).id, "project:race:first"); |
| 743 | assert.equal((yield* Fiber.join(second))._tag, "ProjectConflictError"); | 738 | assert.equal((yield* Fiber.join(second))._tag, "ProjectConflictError"); |
| 744 | }).pipe(Effect.provide(ProjectServiceDependenciesLayer)), | 739 | }).pipe(Effect.provide(layerProjectServiceDependencies)), |
| 745 | ); | 740 | ); |
| 746 | 741 | ||
| 747 | it.effect("rejects an update that waited on the lock while its project was deleted", () => | 742 | it.effect("rejects an update that waited on the lock while its project was deleted", () => |
| ... | @@ -801,5 +796,5 @@ it.effect("rejects an update that waited on the lock while its project was delet | ... | @@ -801,5 +796,5 @@ it.effect("rejects an update that waited on the lock while its project was delet |
| 801 | Option.getOrThrow(yield* service.getById(projectId, { includeDeleted: true })).title, | 796 | Option.getOrThrow(yield* service.getById(projectId, { includeDeleted: true })).title, |
| 802 | "Update race", | 797 | "Update race", |
| 803 | ); | 798 | ); |
| 804 | }).pipe(Effect.provide(ProjectServiceDependenciesLayer)), | 799 | }).pipe(Effect.provide(layerProjectServiceDependencies)), |
| 805 | ); | 800 | ); |
apps/server/src/project/RepositoryIdentityResolver.test.ts+11-11| ... | @@ -26,7 +26,7 @@ const git = (cwd: string, args: ReadonlyArray<string>) => | ... | @@ -26,7 +26,7 @@ const git = (cwd: string, args: ReadonlyArray<string>) => |
| 26 | }); | 26 | }); |
| 27 | }).pipe(Effect.provide(ProcessRunner.layer)); | 27 | }).pipe(Effect.provide(ProcessRunner.layer)); |
| 28 | 28 | ||
| 29 | const makeRepositoryIdentityResolverTestLayer = (options: { | 29 | const layerRepositoryIdentityResolverTest = (options: { |
| 30 | readonly positiveCacheTtl?: Duration.Input; | 30 | readonly positiveCacheTtl?: Duration.Input; |
| 31 | readonly negativeCacheTtl?: Duration.Input; | 31 | readonly negativeCacheTtl?: Duration.Input; |
| 32 | }) => | 32 | }) => |
| ... | @@ -45,7 +45,7 @@ it.layer(NodeServices.layer)("RepositoryIdentityResolverLive", (it) => { | ... | @@ -45,7 +45,7 @@ it.layer(NodeServices.layer)("RepositoryIdentityResolverLive", (it) => { |
| 45 | let remoteUrl = "git@github.com:T3Tools/t3code.git"; | 45 | let remoteUrl = "git@github.com:T3Tools/t3code.git"; |
| 46 | let refinements = 0; | 46 | let refinements = 0; |
| 47 | let refinementFails = false; | 47 | let refinementFails = false; |
| 48 | const processRunner = Layer.succeed(ProcessRunner.ProcessRunner, { | 48 | const layerProcessRunner = Layer.succeed(ProcessRunner.ProcessRunner, { |
| 49 | run: (input) => | 49 | run: (input) => |
| 50 | Effect.sync(() => { | 50 | Effect.sync(() => { |
| 51 | calls.push(input.args); | 51 | calls.push(input.args); |
| ... | @@ -63,7 +63,7 @@ it.layer(NodeServices.layer)("RepositoryIdentityResolverLive", (it) => { | ... | @@ -63,7 +63,7 @@ it.layer(NodeServices.layer)("RepositoryIdentityResolverLive", (it) => { |
| 63 | }; | 63 | }; |
| 64 | }), | 64 | }), |
| 65 | }); | 65 | }); |
| 66 | const resolverLayer = Layer.effect( | 66 | const layerResolver = Layer.effect( |
| 67 | RepositoryIdentityResolver.RepositoryIdentityResolver, | 67 | RepositoryIdentityResolver.RepositoryIdentityResolver, |
| 68 | RepositoryIdentityResolver.make({ | 68 | RepositoryIdentityResolver.make({ |
| 69 | refine: (identity) => { | 69 | refine: (identity) => { |
| ... | @@ -88,7 +88,7 @@ it.layer(NodeServices.layer)("RepositoryIdentityResolverLive", (it) => { | ... | @@ -88,7 +88,7 @@ it.layer(NodeServices.layer)("RepositoryIdentityResolverLive", (it) => { |
| 88 | ); | 88 | ); |
| 89 | }, | 89 | }, |
| 90 | }), | 90 | }), |
| 91 | ).pipe(Layer.provide(processRunner)); | 91 | ).pipe(Layer.provide(layerProcessRunner)); |
| 92 | 92 | ||
| 93 | return Effect.gen(function* () { | 93 | return Effect.gen(function* () { |
| 94 | const resolver = yield* RepositoryIdentityResolver.RepositoryIdentityResolver; | 94 | const resolver = yield* RepositoryIdentityResolver.RepositoryIdentityResolver; |
| ... | @@ -125,13 +125,13 @@ it.layer(NodeServices.layer)("RepositoryIdentityResolverLive", (it) => { | ... | @@ -125,13 +125,13 @@ it.layer(NodeServices.layer)("RepositoryIdentityResolverLive", (it) => { |
| 125 | const unavailable = yield* resolver.resolve(rootPath, { refresh: true }); | 125 | const unavailable = yield* resolver.resolve(rootPath, { refresh: true }); |
| 126 | expect(unavailable?.webUrl).toBeUndefined(); | 126 | expect(unavailable?.webUrl).toBeUndefined(); |
| 127 | expect(unavailable?.canonicalKey).toBe("ssh.forge.test/team/repo"); | 127 | expect(unavailable?.canonicalKey).toBe("ssh.forge.test/team/repo"); |
| 128 | }).pipe(Effect.provide(Layer.merge(TestClock.layer(), resolverLayer))); | 128 | }).pipe(Effect.provide(Layer.merge(TestClock.layer(), layerResolver))); |
| 129 | }); | 129 | }); |
| 130 | 130 | ||
| 131 | it.effect("retries Git root discovery after the negative TTL", () => { | 131 | it.effect("retries Git root discovery after the negative TTL", () => { |
| 132 | const calls: Array<ReadonlyArray<string>> = []; | 132 | const calls: Array<ReadonlyArray<string>> = []; |
| 133 | let rootAttempts = 0; | 133 | let rootAttempts = 0; |
| 134 | const processRunner = Layer.succeed(ProcessRunner.ProcessRunner, { | 134 | const layerProcessRunner = Layer.succeed(ProcessRunner.ProcessRunner, { |
| 135 | run: (input) => | 135 | run: (input) => |
| 136 | Effect.sync(() => { | 136 | Effect.sync(() => { |
| 137 | calls.push(input.args); | 137 | calls.push(input.args); |
| ... | @@ -153,10 +153,10 @@ it.layer(NodeServices.layer)("RepositoryIdentityResolverLive", (it) => { | ... | @@ -153,10 +153,10 @@ it.layer(NodeServices.layer)("RepositoryIdentityResolverLive", (it) => { |
| 153 | }; | 153 | }; |
| 154 | }), | 154 | }), |
| 155 | }); | 155 | }); |
| 156 | const resolverLayer = Layer.effect( | 156 | const layerResolver = Layer.effect( |
| 157 | RepositoryIdentityResolver.RepositoryIdentityResolver, | 157 | RepositoryIdentityResolver.RepositoryIdentityResolver, |
| 158 | RepositoryIdentityResolver.make(), | 158 | RepositoryIdentityResolver.make(), |
| 159 | ).pipe(Layer.provide(processRunner)); | 159 | ).pipe(Layer.provide(layerProcessRunner)); |
| 160 | 160 | ||
| 161 | return Effect.gen(function* () { | 161 | return Effect.gen(function* () { |
| 162 | const resolver = yield* RepositoryIdentityResolver.RepositoryIdentityResolver; | 162 | const resolver = yield* RepositoryIdentityResolver.RepositoryIdentityResolver; |
| ... | @@ -171,7 +171,7 @@ it.layer(NodeServices.layer)("RepositoryIdentityResolverLive", (it) => { | ... | @@ -171,7 +171,7 @@ it.layer(NodeServices.layer)("RepositoryIdentityResolverLive", (it) => { |
| 171 | ["-C", "/repo/packages/web", "rev-parse", "--show-toplevel"], | 171 | ["-C", "/repo/packages/web", "rev-parse", "--show-toplevel"], |
| 172 | ["-C", "/repo", "remote", "-v"], | 172 | ["-C", "/repo", "remote", "-v"], |
| 173 | ]); | 173 | ]); |
| 174 | }).pipe(Effect.provide(Layer.merge(TestClock.layer(), resolverLayer))); | 174 | }).pipe(Effect.provide(Layer.merge(TestClock.layer(), layerResolver))); |
| 175 | }); | 175 | }); |
| 176 | 176 | ||
| 177 | it.effect("normalizes equivalent GitHub remotes into a stable repository identity", () => | 177 | it.effect("normalizes equivalent GitHub remotes into a stable repository identity", () => |
| ... | @@ -341,7 +341,7 @@ it.layer(NodeServices.layer)("RepositoryIdentityResolverLive", (it) => { | ... | @@ -341,7 +341,7 @@ it.layer(NodeServices.layer)("RepositoryIdentityResolverLive", (it) => { |
| 341 | Effect.provide( | 341 | Effect.provide( |
| 342 | Layer.merge( | 342 | Layer.merge( |
| 343 | TestClock.layer(), | 343 | TestClock.layer(), |
| 344 | makeRepositoryIdentityResolverTestLayer({ | 344 | layerRepositoryIdentityResolverTest({ |
| 345 | negativeCacheTtl: Duration.millis(50), | 345 | negativeCacheTtl: Duration.millis(50), |
| 346 | positiveCacheTtl: Duration.seconds(1), | 346 | positiveCacheTtl: Duration.seconds(1), |
| 347 | }), | 347 | }), |
| ... | @@ -382,7 +382,7 @@ it.layer(NodeServices.layer)("RepositoryIdentityResolverLive", (it) => { | ... | @@ -382,7 +382,7 @@ it.layer(NodeServices.layer)("RepositoryIdentityResolverLive", (it) => { |
| 382 | Effect.provide( | 382 | Effect.provide( |
| 383 | Layer.merge( | 383 | Layer.merge( |
| 384 | TestClock.layer(), | 384 | TestClock.layer(), |
| 385 | makeRepositoryIdentityResolverTestLayer({ | 385 | layerRepositoryIdentityResolverTest({ |
| 386 | negativeCacheTtl: Duration.millis(50), | 386 | negativeCacheTtl: Duration.millis(50), |
| 387 | positiveCacheTtl: Duration.millis(100), | 387 | positiveCacheTtl: Duration.millis(100), |
| 388 | }), | 388 | }), |
apps/server/src/project/T3ProjectFileLoader.test.ts+2-2| ... | @@ -8,7 +8,7 @@ import * as Path from "effect/Path"; | ... | @@ -8,7 +8,7 @@ import * as Path from "effect/Path"; |
| 8 | 8 | ||
| 9 | import * as T3ProjectFileLoader from "./T3ProjectFileLoader.ts"; | 9 | import * as T3ProjectFileLoader from "./T3ProjectFileLoader.ts"; |
| 10 | 10 | ||
| 11 | const TestLayer = Layer.empty.pipe( | 11 | const layerTest = Layer.empty.pipe( |
| 12 | Layer.provideMerge(T3ProjectFileLoader.layer), | 12 | Layer.provideMerge(T3ProjectFileLoader.layer), |
| 13 | Layer.provideMerge(NodeServices.layer), | 13 | Layer.provideMerge(NodeServices.layer), |
| 14 | ); | 14 | ); |
| ... | @@ -26,7 +26,7 @@ const writeProjectFile = Effect.fn("writeProjectFile")(function* (cwd: string, c | ... | @@ -26,7 +26,7 @@ const writeProjectFile = Effect.fn("writeProjectFile")(function* (cwd: string, c |
| 26 | yield* fileSystem.writeFileString(path.join(cwd, "t3.json"), contents).pipe(Effect.orDie); | 26 | yield* fileSystem.writeFileString(path.join(cwd, "t3.json"), contents).pipe(Effect.orDie); |
| 27 | }); | 27 | }); |
| 28 | 28 | ||
| 29 | it.layer(TestLayer)("T3ProjectFileLoader", (it) => { | 29 | it.layer(layerTest)("T3ProjectFileLoader", (it) => { |
| 30 | describe("load", () => { | 30 | describe("load", () => { |
| 31 | it.effect("loads and decodes a valid t3.json", () => | 31 | it.effect("loads and decodes a valid t3.json", () => |
| 32 | Effect.gen(function* () { | 32 | Effect.gen(function* () { |
apps/server/src/project/http.ts+1-1| ... | @@ -29,7 +29,7 @@ export const failProjectMutation = Effect.fn("environment.projects.failMutation" | ... | @@ -29,7 +29,7 @@ export const failProjectMutation = Effect.fn("environment.projects.failMutation" |
| 29 | return yield* failEnvironmentInternal("project_mutation_failed", cause); | 29 | return yield* failEnvironmentInternal("project_mutation_failed", cause); |
| 30 | }); | 30 | }); |
| 31 | 31 | ||
| 32 | export const projectHttpApiLayer = HttpApiBuilder.group( | 32 | export const layer = HttpApiBuilder.group( |
| 33 | EnvironmentHttpApi, | 33 | EnvironmentHttpApi, |
| 34 | "projects", | 34 | "projects", |
| 35 | Effect.fnUntraced(function* (handlers) { | 35 | Effect.fnUntraced(function* (handlers) { |
apps/server/src/provider/CodexManagedRuntime.test.ts+2-2| ... | @@ -86,7 +86,7 @@ it.effect.each( | ... | @@ -86,7 +86,7 @@ it.effect.each( |
| 86 | source, | 86 | source, |
| 87 | version: "0.156.1", | 87 | version: "0.156.1", |
| 88 | }; | 88 | }; |
| 89 | const installerLayer = Layer.mock(CodexInstallation.CodexInstallation)({ | 89 | const layerInstaller = Layer.mock(CodexInstallation.CodexInstallation)({ |
| 90 | managedDirectory: "/isolated/tools/codex", | 90 | managedDirectory: "/isolated/tools/codex", |
| 91 | resolve: () => Effect.succeed(executable), | 91 | resolve: () => Effect.succeed(executable), |
| 92 | acquire: () => | 92 | acquire: () => |
| ... | @@ -183,7 +183,7 @@ it.effect.each( | ... | @@ -183,7 +183,7 @@ it.effect.each( |
| 183 | EnvironmentId.make("00000000-0000-4000-8000-000000000001"), | 183 | EnvironmentId.make("00000000-0000-4000-8000-000000000001"), |
| 184 | ), | 184 | ), |
| 185 | }), | 185 | }), |
| 186 | Effect.provide(installerLayer), | 186 | Effect.provide(layerInstaller), |
| 187 | Effect.provideService( | 187 | Effect.provideService( |
| 188 | HttpClient.HttpClient, | 188 | HttpClient.HttpClient, |
| 189 | HttpClient.make((request) => | 189 | HttpClient.make((request) => |
apps/server/src/provider/Drivers/AntigravityDriver.test.ts+4-4| ... | @@ -127,7 +127,7 @@ const makeHarness = Effect.fn("makeAntigravityDriverHarness")(function* ( | ... | @@ -127,7 +127,7 @@ const makeHarness = Effect.fn("makeAntigravityDriverHarness")(function* ( |
| 127 | handle: ChildProcessSpawner.ChildProcessHandle; | 127 | handle: ChildProcessSpawner.ChildProcessHandle; |
| 128 | }> = []; | 128 | }> = []; |
| 129 | 129 | ||
| 130 | const installation = Layer.mock(AntigravityInstallation.AntigravityInstallation)({ | 130 | const layerInstallation = Layer.mock(AntigravityInstallation.AntigravityInstallation)({ |
| 131 | managedDirectory: root, | 131 | managedDirectory: root, |
| 132 | resolve: () => | 132 | resolve: () => |
| 133 | Effect.gen(function* () { | 133 | Effect.gen(function* () { |
| ... | @@ -203,7 +203,7 @@ const makeHarness = Effect.fn("makeAntigravityDriverHarness")(function* ( | ... | @@ -203,7 +203,7 @@ const makeHarness = Effect.fn("makeAntigravityDriverHarness")(function* ( |
| 203 | { name: "BROWSER", value: "must-not-run" }, | 203 | { name: "BROWSER", value: "must-not-run" }, |
| 204 | ].map((variable) => ({ ...variable, sensitive: false })), | 204 | ].map((variable) => ({ ...variable, sensitive: false })), |
| 205 | }).pipe( | 205 | }).pipe( |
| 206 | Effect.provide(installation), | 206 | Effect.provide(layerInstallation), |
| 207 | Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, observedSpawner), | 207 | Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, observedSpawner), |
| 208 | ); | 208 | ); |
| 209 | const refresh = instance.refreshModels; | 209 | const refresh = instance.refreshModels; |
| ... | @@ -244,7 +244,7 @@ const makeHarness = Effect.fn("makeAntigravityDriverHarness")(function* ( | ... | @@ -244,7 +244,7 @@ const makeHarness = Effect.fn("makeAntigravityDriverHarness")(function* ( |
| 244 | }; | 244 | }; |
| 245 | }); | 245 | }); |
| 246 | 246 | ||
| 247 | const testLayer = ServerConfig.layerTest(process.cwd(), { | 247 | const layerTest = ServerConfig.layerTest(process.cwd(), { |
| 248 | prefix: "t3-antigravity-driver-config-", | 248 | prefix: "t3-antigravity-driver-config-", |
| 249 | }).pipe( | 249 | }).pipe( |
| 250 | Layer.provideMerge(NodeServices.layer), | 250 | Layer.provideMerge(NodeServices.layer), |
| ... | @@ -264,7 +264,7 @@ const testLayer = ServerConfig.layerTest(process.cwd(), { | ... | @@ -264,7 +264,7 @@ const testLayer = ServerConfig.layerTest(process.cwd(), { |
| 264 | Layer.provideMerge(IdAllocator.layer), | 264 | Layer.provideMerge(IdAllocator.layer), |
| 265 | ); | 265 | ); |
| 266 | 266 | ||
| 267 | it.layer(testLayer)("AntigravityDriver", (it) => { | 267 | it.layer(layerTest)("AntigravityDriver", (it) => { |
| 268 | it.effect.skipIf(windowsHost)( | 268 | it.effect.skipIf(windowsHost)( |
| 269 | "preserves the Node install message when starting a standalone provider", | 269 | "preserves the Node install message when starting a standalone provider", |
| 270 | () => | 270 | () => |
apps/server/src/provider/Drivers/CodexDriver.test.ts+2-2| ... | @@ -39,7 +39,7 @@ import * as IdAllocator from "../../orchestration-v2/IdAllocator.ts"; | ... | @@ -39,7 +39,7 @@ import * as IdAllocator from "../../orchestration-v2/IdAllocator.ts"; |
| 39 | import { ProviderAdapterV2RuntimePolicy } from "../../orchestration-v2/ProviderAdapter.ts"; | 39 | import { ProviderAdapterV2RuntimePolicy } from "../../orchestration-v2/ProviderAdapter.ts"; |
| 40 | import * as ProviderCredentialStore from "../ProviderCredentialStore.ts"; | 40 | import * as ProviderCredentialStore from "../ProviderCredentialStore.ts"; |
| 41 | 41 | ||
| 42 | const testLayer = ServerConfig.layerTest(process.cwd(), { | 42 | const layerTest = ServerConfig.layerTest(process.cwd(), { |
| 43 | prefix: "t3-codex-driver-maintenance-", | 43 | prefix: "t3-codex-driver-maintenance-", |
| 44 | }).pipe( | 44 | }).pipe( |
| 45 | Layer.provideMerge(NodeServices.layer), | 45 | Layer.provideMerge(NodeServices.layer), |
| ... | @@ -90,7 +90,7 @@ const noSpawn = ChildProcessSpawner.make(() => | ... | @@ -90,7 +90,7 @@ const noSpawn = ChildProcessSpawner.make(() => |
| 90 | ); | 90 | ); |
| 91 | const encodeCredentials = Schema.encodeEffect(Schema.fromJsonString(Schema.Unknown)); | 91 | const encodeCredentials = Schema.encodeEffect(Schema.fromJsonString(Schema.Unknown)); |
| 92 | 92 | ||
| 93 | it.layer(testLayer)("CodexDriver", (it) => { | 93 | it.layer(layerTest)("CodexDriver", (it) => { |
| 94 | it.effect("disconnect refreshes a restored managed account while its auth flow is idle", () => | 94 | it.effect("disconnect refreshes a restored managed account while its auth flow is idle", () => |
| 95 | Effect.gen(function* () { | 95 | Effect.gen(function* () { |
| 96 | const instanceId = ProviderInstanceId.make("restored-managed-account"); | 96 | const instanceId = ProviderInstanceId.make("restored-managed-account"); |
apps/server/src/provider/Drivers/CursorDriver.test.ts+2-2| ... | @@ -21,7 +21,7 @@ import * as IdAllocator from "../../orchestration-v2/IdAllocator.ts"; | ... | @@ -21,7 +21,7 @@ import * as IdAllocator from "../../orchestration-v2/IdAllocator.ts"; |
| 21 | import { ProviderAdapterV2RuntimePolicy } from "../../orchestration-v2/ProviderAdapter.ts"; | 21 | import { ProviderAdapterV2RuntimePolicy } from "../../orchestration-v2/ProviderAdapter.ts"; |
| 22 | import { Cursor } from "../cursorSdk.ts"; | 22 | import { Cursor } from "../cursorSdk.ts"; |
| 23 | 23 | ||
| 24 | const testLayer = ServerSecretStore.layer.pipe( | 24 | const layerTest = ServerSecretStore.layer.pipe( |
| 25 | Layer.provideMerge( | 25 | Layer.provideMerge( |
| 26 | ServerConfig.layerTest(process.cwd(), { | 26 | ServerConfig.layerTest(process.cwd(), { |
| 27 | prefix: "t3-cursor-driver-copy-command-", | 27 | prefix: "t3-cursor-driver-copy-command-", |
| ... | @@ -54,7 +54,7 @@ const testLayer = ServerSecretStore.layer.pipe( | ... | @@ -54,7 +54,7 @@ const testLayer = ServerSecretStore.layer.pipe( |
| 54 | ), | 54 | ), |
| 55 | ); | 55 | ); |
| 56 | 56 | ||
| 57 | it.layer(testLayer)("CursorDriver", (it) => { | 57 | it.layer(layerTest)("CursorDriver", (it) => { |
| 58 | it.effect( | 58 | it.effect( |
| 59 | "persists browser credentials, uses them for chat, and closes the SDK session on logout", | 59 | "persists browser credentials, uses them for chat, and closes the SDK session on logout", |
| 60 | () => | 60 | () => |
apps/server/src/provider/Drivers/CursorDriver.ts+1-1| ... | @@ -231,7 +231,7 @@ export const CursorDriver: ProviderDriver<CursorSettings, CursorDriverEnv> = { | ... | @@ -231,7 +231,7 @@ export const CursorDriver: ProviderDriver<CursorSettings, CursorDriverEnv> = { |
| 231 | Effect.provideService(FileSystem.FileSystem, fileSystem), | 231 | Effect.provideService(FileSystem.FileSystem, fileSystem), |
| 232 | Effect.provideService(Path.Path, path), | 232 | Effect.provideService(Path.Path, path), |
| 233 | Effect.map(stampSnapshot), | 233 | Effect.map(stampSnapshot), |
| 234 | Effect.provide(CursorSdkCatalog.CursorSdkCatalogLive), | 234 | Effect.provide(CursorSdkCatalog.layer), |
| 235 | ); | 235 | ); |
| 236 | 236 | ||
| 237 | const snapshotSettings = makeProviderSnapshotSettingsSource(effectiveConfig, serverSettings); | 237 | const snapshotSettings = makeProviderSnapshotSettingsSource(effectiveConfig, serverSettings); |
apps/server/src/provider/Drivers/GrokDriver.test.ts+2-2| ... | @@ -17,7 +17,7 @@ import { GrokDriver } from "./GrokDriver.ts"; | ... | @@ -17,7 +17,7 @@ import { GrokDriver } from "./GrokDriver.ts"; |
| 17 | 17 | ||
| 18 | import * as IdAllocator from "../../orchestration-v2/IdAllocator.ts"; | 18 | import * as IdAllocator from "../../orchestration-v2/IdAllocator.ts"; |
| 19 | 19 | ||
| 20 | const testLayer = ServerConfig.layerTest(process.cwd(), { | 20 | const layerTest = ServerConfig.layerTest(process.cwd(), { |
| 21 | prefix: "t3-grok-driver-update-", | 21 | prefix: "t3-grok-driver-update-", |
| 22 | }).pipe( | 22 | }).pipe( |
| 23 | Layer.provideMerge(NodeServices.layer), | 23 | Layer.provideMerge(NodeServices.layer), |
| ... | @@ -49,7 +49,7 @@ const noSpawner = ChildProcessSpawner.make(() => | ... | @@ -49,7 +49,7 @@ const noSpawner = ChildProcessSpawner.make(() => |
| 49 | // The `#!/bin/sh` stub below cannot be resolved as an executable on Windows. | 49 | // The `#!/bin/sh` stub below cannot be resolved as an executable on Windows. |
| 50 | const windowsHost = HostProcessPlatform.defaultValue() === "win32"; | 50 | const windowsHost = HostProcessPlatform.defaultValue() === "win32"; |
| 51 | 51 | ||
| 52 | it.layer(testLayer)("GrokDriver", (it) => { | 52 | it.layer(layerTest)("GrokDriver", (it) => { |
| 53 | it.effect.skipIf(windowsHost)("updates through the configured executable's own updater", () => | 53 | it.effect.skipIf(windowsHost)("updates through the configured executable's own updater", () => |
| 54 | Effect.gen(function* () { | 54 | Effect.gen(function* () { |
| 55 | const fs = yield* FileSystem.FileSystem; | 55 | const fs = yield* FileSystem.FileSystem; |
apps/server/src/provider/Drivers/OpenCodeDriver.test.ts+2-2| ... | @@ -211,7 +211,7 @@ const changingRuntime = { | ... | @@ -211,7 +211,7 @@ const changingRuntime = { |
| 211 | startOpenCodeServerProcess: () => reachedServer("start"), | 211 | startOpenCodeServerProcess: () => reachedServer("start"), |
| 212 | connectToOpenCodeServer: () => reachedServer("connect"), | 212 | connectToOpenCodeServer: () => reachedServer("connect"), |
| 213 | } as unknown as OpenCodeRuntime.OpenCodeRuntimeShape; | 213 | } as unknown as OpenCodeRuntime.OpenCodeRuntimeShape; |
| 214 | const updateLayer = Layer.mergeAll( | 214 | const layerUpdate = Layer.mergeAll( |
| 215 | ServerConfig.layerTest(process.cwd(), { prefix: "t3-opencode-driver-update-" }), | 215 | ServerConfig.layerTest(process.cwd(), { prefix: "t3-opencode-driver-update-" }), |
| 216 | IdAllocator.layer, | 216 | IdAllocator.layer, |
| 217 | ServerSettings.layerTest(), | 217 | ServerSettings.layerTest(), |
| ... | @@ -223,7 +223,7 @@ const updateLayer = Layer.mergeAll( | ... | @@ -223,7 +223,7 @@ const updateLayer = Layer.mergeAll( |
| 223 | Layer.succeed(OpenCodeRuntime.OpenCodeRuntime, changingRuntime), | 223 | Layer.succeed(OpenCodeRuntime.OpenCodeRuntime, changingRuntime), |
| 224 | ).pipe(Layer.provideMerge(NodeServices.layer)); | 224 | ).pipe(Layer.provideMerge(NodeServices.layer)); |
| 225 | 225 | ||
| 226 | it.layer(updateLayer)("OpenCodeDriver updates", (it) => { | 226 | it.layer(layerUpdate)("OpenCodeDriver updates", (it) => { |
| 227 | it.effect("never runs the binary for a disabled instance's update check", () => | 227 | it.effect("never runs the binary for a disabled instance's update check", () => |
| 228 | Effect.gen(function* () { | 228 | Effect.gen(function* () { |
| 229 | versionProbes.length = 0; | 229 | versionProbes.length = 0; |
apps/server/src/provider/Layers/AcpRegistryCatalog.ts+1-1| ... | @@ -7,7 +7,7 @@ import * as AcpRegistrySupport from "../acp/AcpRegistrySupport.ts"; | ... | @@ -7,7 +7,7 @@ import * as AcpRegistrySupport from "../acp/AcpRegistrySupport.ts"; |
| 7 | import * as AcpRegistryRuntimeCoordinator from "../acp/AcpRegistryRuntimeCoordinator.ts"; | 7 | import * as AcpRegistryRuntimeCoordinator from "../acp/AcpRegistryRuntimeCoordinator.ts"; |
| 8 | 8 | ||
| 9 | /** Server-lifetime ACP Registry catalog shared by setup, snapshots, and turn launch. */ | 9 | /** Server-lifetime ACP Registry catalog shared by setup, snapshots, and turn launch. */ |
| 10 | export const AcpRegistryCatalogLive = Layer.merge( | 10 | export const layer = Layer.merge( |
| 11 | Layer.unwrap( | 11 | Layer.unwrap( |
| 12 | Effect.gen(function* () { | 12 | Effect.gen(function* () { |
| 13 | const config = yield* ServerConfig.ServerConfig; | 13 | const config = yield* ServerConfig.ServerConfig; |
apps/server/src/provider/Layers/AntigravityProvider.test.ts+2-2| ... | @@ -106,7 +106,7 @@ const expectedCommands = [ | ... | @@ -106,7 +106,7 @@ const expectedCommands = [ |
| 106 | { name: "logout", description: "Sign out of Google" }, | 106 | { name: "logout", description: "Sign out of Google" }, |
| 107 | ]; | 107 | ]; |
| 108 | 108 | ||
| 109 | const testLayer = Layer.merge( | 109 | const layerTest = Layer.merge( |
| 110 | Layer.mock(BackgroundPolicy.BackgroundPolicy)({ | 110 | Layer.mock(BackgroundPolicy.BackgroundPolicy)({ |
| 111 | shouldRunScopeWork: () => Effect.succeed(false), | 111 | shouldRunScopeWork: () => Effect.succeed(false), |
| 112 | }), | 112 | }), |
| ... | @@ -216,7 +216,7 @@ describe("Antigravity model catalog", () => { | ... | @@ -216,7 +216,7 @@ describe("Antigravity model catalog", () => { |
| 216 | }); | 216 | }); |
| 217 | }); | 217 | }); |
| 218 | 218 | ||
| 219 | it.layer(testLayer)("Antigravity provider snapshots", (it) => { | 219 | it.layer(layerTest)("Antigravity provider snapshots", (it) => { |
| 220 | it.effect("does not probe or run helper safety checks while disabled", () => | 220 | it.effect("does not probe or run helper safety checks while disabled", () => |
| 221 | Effect.scoped( | 221 | Effect.scoped( |
| 222 | Effect.gen(function* () { | 222 | Effect.gen(function* () { |
apps/server/src/provider/Layers/CursorProvider.test.ts+3-3| ... | @@ -176,7 +176,7 @@ describe("checkCursorProviderStatus", () => { | ... | @@ -176,7 +176,7 @@ describe("checkCursorProviderStatus", () => { |
| 176 | { CURSOR_API_KEY: "test-cursor-key" }, | 176 | { CURSOR_API_KEY: "test-cursor-key" }, |
| 177 | ).pipe( | 177 | ).pipe( |
| 178 | Effect.provide( | 178 | Effect.provide( |
| 179 | CursorSdkCatalog.makeCursorSdkCatalogTestLayer((apiKey) => { | 179 | CursorSdkCatalog.layerTest((apiKey) => { |
| 180 | expect(apiKey).toBe("test-cursor-key"); | 180 | expect(apiKey).toBe("test-cursor-key"); |
| 181 | return Effect.succeed({ | 181 | return Effect.succeed({ |
| 182 | user: { | 182 | user: { |
| ... | @@ -212,7 +212,7 @@ describe("checkCursorProviderStatus", () => { | ... | @@ -212,7 +212,7 @@ describe("checkCursorProviderStatus", () => { |
| 212 | CURSOR_API_KEY: "invalid-test-key", | 212 | CURSOR_API_KEY: "invalid-test-key", |
| 213 | }).pipe( | 213 | }).pipe( |
| 214 | Effect.provide( | 214 | Effect.provide( |
| 215 | CursorSdkCatalog.makeCursorSdkCatalogTestLayer(() => | 215 | CursorSdkCatalog.layerTest(() => |
| 216 | Effect.fail( | 216 | Effect.fail( |
| 217 | new CursorSdkCatalog.CursorSdkCatalogError({ | 217 | new CursorSdkCatalog.CursorSdkCatalogError({ |
| 218 | authenticationFailure: true, | 218 | authenticationFailure: true, |
| ... | @@ -235,7 +235,7 @@ describe("checkCursorProviderStatus", () => { | ... | @@ -235,7 +235,7 @@ describe("checkCursorProviderStatus", () => { |
| 235 | Effect.gen(function* () { | 235 | Effect.gen(function* () { |
| 236 | const provider = yield* checkCursorProviderStatus(baseCursorSettings).pipe( | 236 | const provider = yield* checkCursorProviderStatus(baseCursorSettings).pipe( |
| 237 | Effect.provide( | 237 | Effect.provide( |
| 238 | CursorSdkCatalog.makeCursorSdkCatalogTestLayer(() => | 238 | CursorSdkCatalog.layerTest(() => |
| 239 | Effect.die("SDK catalog must not be used without CURSOR_API_KEY"), | 239 | Effect.die("SDK catalog must not be used without CURSOR_API_KEY"), |
| 240 | ), | 240 | ), |
| 241 | ), | 241 | ), |
apps/server/src/provider/Layers/CursorSdkCatalog.ts+2-4| ... | @@ -85,10 +85,8 @@ export const makeCursorSdkCatalog = Effect.fn("CursorSdkCatalog.make")(function* | ... | @@ -85,10 +85,8 @@ export const makeCursorSdkCatalog = Effect.fn("CursorSdkCatalog.make")(function* |
| 85 | }); | 85 | }); |
| 86 | }); | 86 | }); |
| 87 | 87 | ||
| 88 | export const CursorSdkCatalogLive = Layer.effect(CursorSdkCatalog, makeCursorSdkCatalog()); | 88 | export const layer = Layer.effect(CursorSdkCatalog, makeCursorSdkCatalog()); |
| 89 | 89 | ||
| 90 | export function makeCursorSdkCatalogTestLayer( | 90 | export function layerTest(read: CursorSdkCatalogShape["read"]): Layer.Layer<CursorSdkCatalog> { |
| 91 | read: CursorSdkCatalogShape["read"], | ||
| 92 | ): Layer.Layer<CursorSdkCatalog> { | ||
| 93 | return Layer.succeed(CursorSdkCatalog, CursorSdkCatalog.of({ read })); | 91 | return Layer.succeed(CursorSdkCatalog, CursorSdkCatalog.of({ read })); |
| 94 | } | 92 | } |
apps/server/src/provider/Layers/OpenCodeProvider.test.ts+3-3| ... | @@ -314,7 +314,7 @@ it("keeps native and MCP commands while preserving compaction and separate skill | ... | @@ -314,7 +314,7 @@ it("keeps native and MCP commands while preserving compaction and separate skill |
| 314 | ); | 314 | ); |
| 315 | }); | 315 | }); |
| 316 | 316 | ||
| 317 | const testLayer = Layer.succeed(OpenCodeRuntime.OpenCodeRuntime, OpenCodeRuntimeTestDouble).pipe( | 317 | const layerTest = Layer.succeed(OpenCodeRuntime.OpenCodeRuntime, OpenCodeRuntimeTestDouble).pipe( |
| 318 | Layer.provideMerge(ServerConfig.layerTest(process.cwd(), process.cwd())), | 318 | Layer.provideMerge(ServerConfig.layerTest(process.cwd(), process.cwd())), |
| 319 | Layer.provideMerge(NodeServices.layer), | 319 | Layer.provideMerge(NodeServices.layer), |
| 320 | ); | 320 | ); |
| ... | @@ -358,7 +358,7 @@ const checkProvider = Effect.fn("checkProvider")(function* ( | ... | @@ -358,7 +358,7 @@ const checkProvider = Effect.fn("checkProvider")(function* ( |
| 358 | ); | 358 | ); |
| 359 | }); | 359 | }); |
| 360 | 360 | ||
| 361 | it.layer(testLayer)("checkOpenCodeProviderStatus", (it) => { | 361 | it.layer(layerTest)("checkOpenCodeProviderStatus", (it) => { |
| 362 | it.effect("shows a codex-style missing binary message", () => | 362 | it.effect("shows a codex-style missing binary message", () => |
| 363 | Effect.gen(function* () { | 363 | Effect.gen(function* () { |
| 364 | runtimeMock.state.runVersionError = new Error("spawn opencode ENOENT"); | 364 | runtimeMock.state.runVersionError = new Error("spawn opencode ENOENT"); |
| ... | @@ -657,7 +657,7 @@ it.layer(testLayer)("checkOpenCodeProviderStatus", (it) => { | ... | @@ -657,7 +657,7 @@ it.layer(testLayer)("checkOpenCodeProviderStatus", (it) => { |
| 657 | ); | 657 | ); |
| 658 | }); | 658 | }); |
| 659 | 659 | ||
| 660 | it.layer(testLayer)("checkOpenCodeProviderStatus with configured server URL", (it) => { | 660 | it.layer(layerTest)("checkOpenCodeProviderStatus with configured server URL", (it) => { |
| 661 | it.effect("does not send a local environment password to a configured server", () => | 661 | it.effect("does not send a local environment password to a configured server", () => |
| 662 | Effect.gen(function* () { | 662 | Effect.gen(function* () { |
| 663 | const snapshot = yield* checkProvider( | 663 | const snapshot = yield* checkProvider( |
apps/server/src/provider/Layers/ProviderAuthService.ts+1-4| ... | @@ -250,7 +250,4 @@ export const makeProviderAuthService = Effect.gen(function* () { | ... | @@ -250,7 +250,4 @@ export const makeProviderAuthService = Effect.gen(function* () { |
| 250 | }); | 250 | }); |
| 251 | }); | 251 | }); |
| 252 | 252 | ||
| 253 | export const ProviderAuthServiceLive = Layer.effect( | 253 | export const layer = Layer.effect(ProviderAuthService.ProviderAuthService, makeProviderAuthService); |
| 254 | ProviderAuthService.ProviderAuthService, | ||
| 255 | makeProviderAuthService, | ||
| 256 | ); |
apps/server/src/provider/Layers/ProviderInstanceRegistryHydration.ts+12-14| ... | @@ -29,7 +29,7 @@ | ... | @@ -29,7 +29,7 @@ |
| 29 | * ---------- | 29 | * ---------- |
| 30 | * On layer build we: | 30 | * On layer build we: |
| 31 | * 1. Read the current `ServerSettings` once and use it to seed the | 31 | * 1. Read the current `ServerSettings` once and use it to seed the |
| 32 | * registry's initial state via `ProviderInstanceRegistryMutableLayer`. | 32 | * registry's initial state via `ProviderInstanceRegistryLive.layer`. |
| 33 | * 2. Fork a daemon fiber (lifetime tied to the layer's scope) that | 33 | * 2. Fork a daemon fiber (lifetime tied to the layer's scope) that |
| 34 | * acquires `ServerSettingsService.subscribeChanges` and calls | 34 | * acquires `ServerSettingsService.subscribeChanges` and calls |
| 35 | * `ProviderInstanceRegistryMutator.reconcile` on every emission. | 35 | * `ProviderInstanceRegistryMutator.reconcile` on every emission. |
| ... | @@ -55,13 +55,11 @@ import * as Settings from "../../serverSettings.ts"; | ... | @@ -55,13 +55,11 @@ import * as Settings from "../../serverSettings.ts"; |
| 55 | import { BUILT_IN_DRIVERS, type BuiltInDriversEnv } from "../builtInDrivers.ts"; | 55 | import { BUILT_IN_DRIVERS, type BuiltInDriversEnv } from "../builtInDrivers.ts"; |
| 56 | import * as ProviderInstanceRegistry from "../Services/ProviderInstanceRegistry.ts"; | 56 | import * as ProviderInstanceRegistry from "../Services/ProviderInstanceRegistry.ts"; |
| 57 | import * as ProviderInstanceRegistryMutator from "../Services/ProviderInstanceRegistryMutator.ts"; | 57 | import * as ProviderInstanceRegistryMutator from "../Services/ProviderInstanceRegistryMutator.ts"; |
| 58 | import { ProviderInstanceRegistryMutableLayer } from "./ProviderInstanceRegistryLive.ts"; | 58 | import * as ProviderInstanceRegistryLive from "./ProviderInstanceRegistryLive.ts"; |
| 59 | import { | 59 | import { type ProviderOrchestrationAdapterInfrastructure } from "./ProviderOrchestrationAdapterInfrastructure.ts"; |
| 60 | type ProviderOrchestrationAdapterInfrastructure, | 60 | import * as ProviderOrchestrationAdapterInfrastructureLayer from "./ProviderOrchestrationAdapterInfrastructure.ts"; |
| 61 | ProviderOrchestrationAdapterInfrastructureLive, | ||
| 62 | } from "./ProviderOrchestrationAdapterInfrastructure.ts"; | ||
| 63 | import * as AcpRegistrySupport from "../acp/AcpRegistrySupport.ts"; | 61 | import * as AcpRegistrySupport from "../acp/AcpRegistrySupport.ts"; |
| 64 | import { AcpRegistryCatalogLive } from "./AcpRegistryCatalog.ts"; | 62 | import * as AcpRegistryCatalogLayer from "./AcpRegistryCatalog.ts"; |
| 65 | 63 | ||
| 66 | type ProviderInstanceRegistryHydrationEnv = | 64 | type ProviderInstanceRegistryHydrationEnv = |
| 67 | | Exclude< | 65 | | Exclude< |
| ... | @@ -127,7 +125,7 @@ export const deriveProviderInstanceConfigMap = ( | ... | @@ -127,7 +125,7 @@ export const deriveProviderInstanceConfigMap = ( |
| 127 | * configs, so the only way the watcher could fail is a settings stream | 125 | * configs, so the only way the watcher could fail is a settings stream |
| 128 | * tear-down, which logs and exits cleanly. | 126 | * tear-down, which logs and exits cleanly. |
| 129 | */ | 127 | */ |
| 130 | const SettingsWatcherLive = Layer.effectDiscard( | 128 | const layerSettingsWatcher = Layer.effectDiscard( |
| 131 | Effect.gen(function* () { | 129 | Effect.gen(function* () { |
| 132 | const mutator = yield* ProviderInstanceRegistryMutator.ProviderInstanceRegistryMutator; | 130 | const mutator = yield* ProviderInstanceRegistryMutator.ProviderInstanceRegistryMutator; |
| 133 | const serverSettings = yield* Settings.ServerSettingsService; | 131 | const serverSettings = yield* Settings.ServerSettingsService; |
| ... | @@ -152,7 +150,7 @@ const SettingsWatcherLive = Layer.effectDiscard( | ... | @@ -152,7 +150,7 @@ const SettingsWatcherLive = Layer.effectDiscard( |
| 152 | * sync with subsequent `streamChanges` emissions. | 150 | * sync with subsequent `streamChanges` emissions. |
| 153 | * | 151 | * |
| 154 | * The Layer's two halves: | 152 | * The Layer's two halves: |
| 155 | * - `ProviderInstanceRegistryMutableLayer` produces the registry + | 153 | * - `ProviderInstanceRegistryLive.layer` produces the registry + |
| 156 | * mutator from the initial config map. Its scope owns every | 154 | * mutator from the initial config map. Its scope owns every |
| 157 | * per-instance child scope created during reconcile. | 155 | * per-instance child scope created during reconcile. |
| 158 | * - `SettingsWatcherLive` consumes the mutator, acquires its settings | 156 | * - `SettingsWatcherLive` consumes the mutator, acquires its settings |
| ... | @@ -163,7 +161,7 @@ const SettingsWatcherLive = Layer.effectDiscard( | ... | @@ -163,7 +161,7 @@ const SettingsWatcherLive = Layer.effectDiscard( |
| 163 | * The mutator tag is technically also exposed; only this module imports | 161 | * The mutator tag is technically also exposed; only this module imports |
| 164 | * it, so the visibility leak is harmless in practice. | 162 | * it, so the visibility leak is harmless in practice. |
| 165 | */ | 163 | */ |
| 166 | export const ProviderInstanceRegistryHydrationLive: Layer.Layer< | 164 | export const layer: Layer.Layer< |
| 167 | ProviderInstanceRegistry.ProviderInstanceRegistry, | 165 | ProviderInstanceRegistry.ProviderInstanceRegistry, |
| 168 | never, | 166 | never, |
| 169 | ProviderInstanceRegistryHydrationEnv | 167 | ProviderInstanceRegistryHydrationEnv |
| ... | @@ -178,15 +176,15 @@ export const ProviderInstanceRegistryHydrationLive: Layer.Layer< | ... | @@ -178,15 +176,15 @@ export const ProviderInstanceRegistryHydrationLive: Layer.Layer< |
| 178 | ? ({} as ProviderInstanceConfigMap) | 176 | ? ({} as ProviderInstanceConfigMap) |
| 179 | : deriveProviderInstanceConfigMap(initialSettings); | 177 | : deriveProviderInstanceConfigMap(initialSettings); |
| 180 | 178 | ||
| 181 | const mutableLayer = ProviderInstanceRegistryMutableLayer({ | 179 | const layerMutable = ProviderInstanceRegistryLive.layer({ |
| 182 | drivers: BUILT_IN_DRIVERS, | 180 | drivers: BUILT_IN_DRIVERS, |
| 183 | configMap: initialConfigMap, | 181 | configMap: initialConfigMap, |
| 184 | }).pipe( | 182 | }).pipe( |
| 185 | Layer.provide(ProviderOrchestrationAdapterInfrastructureLive), | 183 | Layer.provide(ProviderOrchestrationAdapterInfrastructureLayer.layer), |
| 186 | Layer.provide(AcpRegistryCatalogLive), | 184 | Layer.provide(AcpRegistryCatalogLayer.layer), |
| 187 | ); | 185 | ); |
| 188 | 186 | ||
| 189 | return SettingsWatcherLive.pipe(Layer.provideMerge(mutableLayer)); | 187 | return layerSettingsWatcher.pipe(Layer.provideMerge(layerMutable)); |
| 190 | }), | 188 | }), |
| 191 | ) as Layer.Layer< | 189 | ) as Layer.Layer< |
| 192 | ProviderInstanceRegistry.ProviderInstanceRegistry, | 190 | ProviderInstanceRegistry.ProviderInstanceRegistry, |
apps/server/src/provider/Layers/ProviderInstanceRegistryLive.test.ts+22-22| ... | @@ -63,9 +63,9 @@ import * as OpenCodeServerLedger from "../OpenCodeServerLedger.ts"; | ... | @@ -63,9 +63,9 @@ import * as OpenCodeServerLedger from "../OpenCodeServerLedger.ts"; |
| 63 | import * as ResetCreditCoordinator from "./resetCreditCoordinator.ts"; | 63 | import * as ResetCreditCoordinator from "./resetCreditCoordinator.ts"; |
| 64 | import * as ProviderEventLoggers from "./ProviderEventLoggers.ts"; | 64 | import * as ProviderEventLoggers from "./ProviderEventLoggers.ts"; |
| 65 | import { makeProviderInstanceRegistry } from "./ProviderInstanceRegistryLive.ts"; | 65 | import { makeProviderInstanceRegistry } from "./ProviderInstanceRegistryLive.ts"; |
| 66 | import { ProviderOrchestrationAdapterInfrastructureLive } from "./ProviderOrchestrationAdapterInfrastructure.ts"; | 66 | import * as ProviderOrchestrationAdapterInfrastructure from "./ProviderOrchestrationAdapterInfrastructure.ts"; |
| 67 | 67 | ||
| 68 | const TestHttpClientLive = Layer.succeed( | 68 | const layerTestHttpClient = Layer.succeed( |
| 69 | HttpClient.HttpClient, | 69 | HttpClient.HttpClient, |
| 70 | HttpClient.make((request) => | 70 | HttpClient.make((request) => |
| 71 | Effect.succeed(HttpClientResponse.fromWeb(request, Response.json({ version: "0.0.0" }))), | 71 | Effect.succeed(HttpClientResponse.fromWeb(request, Response.json({ version: "0.0.0" }))), |
| ... | @@ -74,7 +74,7 @@ const TestHttpClientLive = Layer.succeed( | ... | @@ -74,7 +74,7 @@ const TestHttpClientLive = Layer.succeed( |
| 74 | 74 | ||
| 75 | const TEST_EPOCH = DateTime.makeUnsafe("1970-01-01T00:00:00.000Z"); | 75 | const TEST_EPOCH = DateTime.makeUnsafe("1970-01-01T00:00:00.000Z"); |
| 76 | 76 | ||
| 77 | const BackgroundPolicyAlwaysRunLayer = Layer.mock(BackgroundPolicy.BackgroundPolicy)({ | 77 | const layerBackgroundPolicyAlwaysRun = Layer.mock(BackgroundPolicy.BackgroundPolicy)({ |
| 78 | reportClientActivity: () => Effect.void, | 78 | reportClientActivity: () => Effect.void, |
| 79 | removeRpcClient: () => Effect.void, | 79 | removeRpcClient: () => Effect.void, |
| 80 | reportHostPowerState: () => Effect.void, | 80 | reportHostPowerState: () => Effect.void, |
| ... | @@ -201,7 +201,7 @@ describe("ProviderInstanceRegistryLive — multi-instance codex slice", () => { | ... | @@ -201,7 +201,7 @@ describe("ProviderInstanceRegistryLive — multi-instance codex slice", () => { |
| 201 | // `NodeServices.layer` through `Layer.provideMerge` to satisfy that | 201 | // `NodeServices.layer` through `Layer.provideMerge` to satisfy that |
| 202 | // dependency while still surfacing NodeServices to the test body (the | 202 | // dependency while still surfacing NodeServices to the test body (the |
| 203 | // codex driver's `create` yields `ChildProcessSpawner` directly). | 203 | // codex driver's `create` yields `ChildProcessSpawner` directly). |
| 204 | const baseLayer = ServerConfig.layerTest(process.cwd(), { | 204 | const layerBase = ServerConfig.layerTest(process.cwd(), { |
| 205 | prefix: "provider-instance-registry-test", | 205 | prefix: "provider-instance-registry-test", |
| 206 | }).pipe( | 206 | }).pipe( |
| 207 | Layer.provideMerge(NodeServices.layer), | 207 | Layer.provideMerge(NodeServices.layer), |
| ... | @@ -218,9 +218,9 @@ describe("ProviderInstanceRegistryLive — multi-instance codex slice", () => { | ... | @@ -218,9 +218,9 @@ describe("ProviderInstanceRegistryLive — multi-instance codex slice", () => { |
| 218 | ), | 218 | ), |
| 219 | }), | 219 | }), |
| 220 | ), | 220 | ), |
| 221 | Layer.provideMerge(BackgroundPolicyAlwaysRunLayer), | 221 | Layer.provideMerge(layerBackgroundPolicyAlwaysRun), |
| 222 | Layer.provideMerge(ServerSettings.layerTest()), | 222 | Layer.provideMerge(ServerSettings.layerTest()), |
| 223 | Layer.provideMerge(TestHttpClientLive), | 223 | Layer.provideMerge(layerTestHttpClient), |
| 224 | Layer.provideMerge(ServerSettings.layerTest()), | 224 | Layer.provideMerge(ServerSettings.layerTest()), |
| 225 | Layer.provideMerge( | 225 | Layer.provideMerge( |
| 226 | Layer.succeed( | 226 | Layer.succeed( |
| ... | @@ -231,8 +231,8 @@ describe("ProviderInstanceRegistryLive — multi-instance codex slice", () => { | ... | @@ -231,8 +231,8 @@ describe("ProviderInstanceRegistryLive — multi-instance codex slice", () => { |
| 231 | Layer.provideMerge(ModelManifest.layerTest), | 231 | Layer.provideMerge(ModelManifest.layerTest), |
| 232 | Layer.provideMerge(ResetCreditCoordinator.layerTest), | 232 | Layer.provideMerge(ResetCreditCoordinator.layerTest), |
| 233 | ); | 233 | ); |
| 234 | const testLayer = ProviderOrchestrationAdapterInfrastructureLive.pipe( | 234 | const layerTest = ProviderOrchestrationAdapterInfrastructure.layer.pipe( |
| 235 | Layer.provideMerge(baseLayer), | 235 | Layer.provideMerge(layerBase), |
| 236 | ); | 236 | ); |
| 237 | 237 | ||
| 238 | it.live("boots two independent codex instances from a ProviderInstanceConfigMap", () => | 238 | it.live("boots two independent codex instances from a ProviderInstanceConfigMap", () => |
| ... | @@ -310,7 +310,7 @@ describe("ProviderInstanceRegistryLive — multi-instance codex slice", () => { | ... | @@ -310,7 +310,7 @@ describe("ProviderInstanceRegistryLive — multi-instance codex slice", () => { |
| 310 | // Nothing goes to the unavailable bucket — both drivers are registered. | 310 | // Nothing goes to the unavailable bucket — both drivers are registered. |
| 311 | const unavailable = yield* registry.listUnavailable; | 311 | const unavailable = yield* registry.listUnavailable; |
| 312 | expect(unavailable).toEqual([]); | 312 | expect(unavailable).toEqual([]); |
| 313 | }).pipe(Effect.provide(testLayer)), | 313 | }).pipe(Effect.provide(layerTest)), |
| 314 | ); | 314 | ); |
| 315 | 315 | ||
| 316 | it.live("treats an explicit in-config enabled:false as disabling despite the envelope", () => | 316 | it.live("treats an explicit in-config enabled:false as disabling despite the envelope", () => |
| ... | @@ -336,7 +336,7 @@ describe("ProviderInstanceRegistryLive — multi-instance codex slice", () => { | ... | @@ -336,7 +336,7 @@ describe("ProviderInstanceRegistryLive — multi-instance codex slice", () => { |
| 336 | expect(instance!.enabled).toBe(false); | 336 | expect(instance!.enabled).toBe(false); |
| 337 | const snapshot = yield* instance!.snapshot.getSnapshot; | 337 | const snapshot = yield* instance!.snapshot.getSnapshot; |
| 338 | expect(snapshot.enabled).toBe(false); | 338 | expect(snapshot.enabled).toBe(false); |
| 339 | }).pipe(Effect.provide(testLayer)), | 339 | }).pipe(Effect.provide(layerTest)), |
| 340 | ); | 340 | ); |
| 341 | 341 | ||
| 342 | it.live("reports Codex's answer when a redemption changed nothing", () => | 342 | it.live("reports Codex's answer when a redemption changed nothing", () => |
| ... | @@ -374,7 +374,7 @@ describe("ProviderInstanceRegistryLive — multi-instance codex slice", () => { | ... | @@ -374,7 +374,7 @@ describe("ProviderInstanceRegistryLive — multi-instance codex slice", () => { |
| 374 | // The usage read fails, so the re-probe cannot confirm new limits. | 374 | // The usage read fails, so the re-probe cannot confirm new limits. |
| 375 | yield* codex!.snapshot.refresh; | 375 | yield* codex!.snapshot.refresh; |
| 376 | expect(yield* codex!.consumeResetCredit!()).toBe("alreadyRedeemed"); | 376 | expect(yield* codex!.consumeResetCredit!()).toBe("alreadyRedeemed"); |
| 377 | }).pipe(Effect.provide(testLayer)), | 377 | }).pipe(Effect.provide(layerTest)), |
| 378 | ); | 378 | ); |
| 379 | 379 | ||
| 380 | it.live("runs Codex and Claude readiness probes from configured tilde paths", () => | 380 | it.live("runs Codex and Claude readiness probes from configured tilde paths", () => |
| ... | @@ -428,7 +428,7 @@ describe("ProviderInstanceRegistryLive — multi-instance codex slice", () => { | ... | @@ -428,7 +428,7 @@ describe("ProviderInstanceRegistryLive — multi-instance codex slice", () => { |
| 428 | installed: true, | 428 | installed: true, |
| 429 | version: "2.1.219", | 429 | version: "2.1.219", |
| 430 | }); | 430 | }); |
| 431 | }).pipe(Effect.provide(testLayer)), | 431 | }).pipe(Effect.provide(layerTest)), |
| 432 | ); | 432 | ); |
| 433 | 433 | ||
| 434 | const redeemClaudeReset = (claim: { result: string; usageFailsAfterClaim: boolean }) => | 434 | const redeemClaudeReset = (claim: { result: string; usageFailsAfterClaim: boolean }) => |
| ... | @@ -497,7 +497,7 @@ describe("ProviderInstanceRegistryLive — multi-instance codex slice", () => { | ... | @@ -497,7 +497,7 @@ describe("ProviderInstanceRegistryLive — multi-instance codex slice", () => { |
| 497 | }).pipe( | 497 | }).pipe( |
| 498 | // macOS logins live in the Keychain, where resets are never read. | 498 | // macOS logins live in the Keychain, where resets are never read. |
| 499 | Effect.provideService(HostProcessPlatform, "linux"), | 499 | Effect.provideService(HostProcessPlatform, "linux"), |
| 500 | Effect.provide(testLayer), | 500 | Effect.provide(layerTest), |
| 501 | ); | 501 | ); |
| 502 | 502 | ||
| 503 | it.live("refreshes Claude usage after redeeming a reset", () => | 503 | it.live("refreshes Claude usage after redeeming a reset", () => |
| ... | @@ -558,7 +558,7 @@ describe("ProviderInstanceRegistryLive — multi-instance codex slice", () => { | ... | @@ -558,7 +558,7 @@ describe("ProviderInstanceRegistryLive — multi-instance codex slice", () => { |
| 558 | expect(ghost.driver).toBe("ghostDriver"); | 558 | expect(ghost.driver).toBe("ghostDriver"); |
| 559 | expect(ghost.availability).toBe("unavailable"); | 559 | expect(ghost.availability).toBe("unavailable"); |
| 560 | expect(ghost.unavailableReason).toMatch(/ghostDriver/); | 560 | expect(ghost.unavailableReason).toMatch(/ghostDriver/); |
| 561 | }).pipe(Effect.provide(testLayer)), | 561 | }).pipe(Effect.provide(layerTest)), |
| 562 | ); | 562 | ); |
| 563 | }); | 563 | }); |
| 564 | 564 | ||
| ... | @@ -575,7 +575,7 @@ describe("ProviderInstanceRegistryLive — all drivers slice", () => { | ... | @@ -575,7 +575,7 @@ describe("ProviderInstanceRegistryLive — all drivers slice", () => { |
| 575 | // provides `OpenCodeRuntimeLive`'s deps while keeping its own outputs | 575 | // provides `OpenCodeRuntimeLive`'s deps while keeping its own outputs |
| 576 | // surfaced; that merged layer then provides `ServerConfig.layerTest`'s | 576 | // surfaced; that merged layer then provides `ServerConfig.layerTest`'s |
| 577 | // `FileSystem` dep while keeping everything else surfaced to the test. | 577 | // `FileSystem` dep while keeping everything else surfaced to the test. |
| 578 | const infraLayer = OpenCodeRuntime.OpenCodeRuntimeLive.pipe( | 578 | const layerInfra = OpenCodeRuntime.layer.pipe( |
| 579 | Layer.provide(OpenCodeServerLedger.layerTest), | 579 | Layer.provide(OpenCodeServerLedger.layerTest), |
| 580 | Layer.provideMerge(NodeServices.layer), | 580 | Layer.provideMerge(NodeServices.layer), |
| 581 | Layer.provideMerge( | 581 | Layer.provideMerge( |
| ... | @@ -591,17 +591,17 @@ describe("ProviderInstanceRegistryLive — all drivers slice", () => { | ... | @@ -591,17 +591,17 @@ describe("ProviderInstanceRegistryLive — all drivers slice", () => { |
| 591 | }), | 591 | }), |
| 592 | ), | 592 | ), |
| 593 | ); | 593 | ); |
| 594 | const baseLayer = AntigravityInstallation.AntigravityInstallation.layer.pipe( | 594 | const layerBase = AntigravityInstallation.AntigravityInstallation.layer.pipe( |
| 595 | Layer.provideMerge(ServerSecretStore.layer), | 595 | Layer.provideMerge(ServerSecretStore.layer), |
| 596 | Layer.provideMerge( | 596 | Layer.provideMerge( |
| 597 | ServerConfig.layerTest(process.cwd(), { | 597 | ServerConfig.layerTest(process.cwd(), { |
| 598 | prefix: "provider-instance-registry-all-drivers-test", | 598 | prefix: "provider-instance-registry-all-drivers-test", |
| 599 | }), | 599 | }), |
| 600 | ), | 600 | ), |
| 601 | Layer.provideMerge(infraLayer), | 601 | Layer.provideMerge(layerInfra), |
| 602 | Layer.provideMerge(BackgroundPolicyAlwaysRunLayer), | 602 | Layer.provideMerge(layerBackgroundPolicyAlwaysRun), |
| 603 | Layer.provideMerge(ServerSettings.layerTest()), | 603 | Layer.provideMerge(ServerSettings.layerTest()), |
| 604 | Layer.provideMerge(TestHttpClientLive), | 604 | Layer.provideMerge(layerTestHttpClient), |
| 605 | Layer.provideMerge(ServerSettings.layerTest()), | 605 | Layer.provideMerge(ServerSettings.layerTest()), |
| 606 | Layer.provideMerge( | 606 | Layer.provideMerge( |
| 607 | Layer.succeed( | 607 | Layer.succeed( |
| ... | @@ -612,8 +612,8 @@ describe("ProviderInstanceRegistryLive — all drivers slice", () => { | ... | @@ -612,8 +612,8 @@ describe("ProviderInstanceRegistryLive — all drivers slice", () => { |
| 612 | Layer.provideMerge(ModelManifest.layerTest), | 612 | Layer.provideMerge(ModelManifest.layerTest), |
| 613 | Layer.provideMerge(ResetCreditCoordinator.layerTest), | 613 | Layer.provideMerge(ResetCreditCoordinator.layerTest), |
| 614 | ); | 614 | ); |
| 615 | const testLayer = ProviderOrchestrationAdapterInfrastructureLive.pipe( | 615 | const layerTest = ProviderOrchestrationAdapterInfrastructure.layer.pipe( |
| 616 | Layer.provideMerge(baseLayer), | 616 | Layer.provideMerge(layerBase), |
| 617 | ); | 617 | ); |
| 618 | 618 | ||
| 619 | it.live("boots one instance of every shipped driver from a single config map", () => | 619 | it.live("boots one instance of every shipped driver from a single config map", () => |
| ... | @@ -776,6 +776,6 @@ describe("ProviderInstanceRegistryLive — all drivers slice", () => { | ... | @@ -776,6 +776,6 @@ describe("ProviderInstanceRegistryLive — all drivers slice", () => { |
| 776 | expect(openCodeSnapshot.continuation?.groupKey).toBe( | 776 | expect(openCodeSnapshot.continuation?.groupKey).toBe( |
| 777 | `${openCodeDriverKind}:instance:${openCodeId}`, | 777 | `${openCodeDriverKind}:instance:${openCodeId}`, |
| 778 | ); | 778 | ); |
| 779 | }).pipe(Effect.provide(testLayer)), | 779 | }).pipe(Effect.provide(layerTest)), |
| 780 | ); | 780 | ); |
| 781 | }); | 781 | }); |
apps/server/src/provider/Layers/ProviderInstanceRegistryLive.ts+1-1| ... | @@ -412,7 +412,7 @@ export const makeProviderInstanceRegistry = <R>(input: { | ... | @@ -412,7 +412,7 @@ export const makeProviderInstanceRegistry = <R>(input: { |
| 412 | * changes. Tests that exercise the mutator directly can pair this Layer | 412 | * changes. Tests that exercise the mutator directly can pair this Layer |
| 413 | * with a test-local `ServerSettingsService`. | 413 | * with a test-local `ServerSettingsService`. |
| 414 | */ | 414 | */ |
| 415 | export const ProviderInstanceRegistryMutableLayer = <R>(input: { | 415 | export const layer = <R>(input: { |
| 416 | readonly drivers: ReadonlyArray<AnyProviderDriver<R>>; | 416 | readonly drivers: ReadonlyArray<AnyProviderDriver<R>>; |
| 417 | readonly configMap: ProviderInstanceConfigMap; | 417 | readonly configMap: ProviderInstanceConfigMap; |
| 418 | }): Layer.Layer< | 418 | }): Layer.Layer< |
apps/server/src/provider/Layers/ProviderOrchestrationAdapterInfrastructure.ts+4-4| ... | @@ -18,10 +18,10 @@ export type ProviderOrchestrationAdapterInfrastructure = | ... | @@ -18,10 +18,10 @@ export type ProviderOrchestrationAdapterInfrastructure = |
| 18 | * reference the orchestration runtime provides to its continuation worker so | 18 | * reference the orchestration runtime provides to its continuation worker so |
| 19 | * Effect layer memoization yields one shared queue. | 19 | * Effect layer memoization yields one shared queue. |
| 20 | */ | 20 | */ |
| 21 | export const ProviderOrchestrationAdapterInfrastructureLive = Layer.mergeAll( | 21 | export const layer = Layer.mergeAll( |
| 22 | ClaudeAdapterV2.claudeAgentSdkQueryRunnerLiveLayer, | 22 | ClaudeAdapterV2.layerQueryRunner, |
| 23 | CodexAdapterV2.codexAppServerClientFactoryFromSettingsLayer, | 23 | CodexAdapterV2.layerAppServerClientFactory, |
| 24 | CursorAgentSdk.cursorAgentSdkRunnerLiveLayer, | 24 | CursorAgentSdk.layer, |
| 25 | IdAllocator.layer, | 25 | IdAllocator.layer, |
| 26 | ProviderContinuationRequests.layer, | 26 | ProviderContinuationRequests.layer, |
| 27 | ); | 27 | ); |
apps/server/src/provider/Layers/ProviderRegistry.test.ts+2704-2744| ... | @@ -47,14 +47,14 @@ import * as ResetCreditCoordinator from "./resetCreditCoordinator.ts"; | ... | @@ -47,14 +47,14 @@ import * as ResetCreditCoordinator from "./resetCreditCoordinator.ts"; |
| 47 | import * as OpenCodeRuntime from "../opencodeRuntime.ts"; | 47 | import * as OpenCodeRuntime from "../opencodeRuntime.ts"; |
| 48 | import * as OpenCodeServerLedger from "../OpenCodeServerLedger.ts"; | 48 | import * as OpenCodeServerLedger from "../OpenCodeServerLedger.ts"; |
| 49 | import * as ProviderEventLoggers from "./ProviderEventLoggers.ts"; | 49 | import * as ProviderEventLoggers from "./ProviderEventLoggers.ts"; |
| 50 | import { ProviderInstanceRegistryHydrationLive } from "./ProviderInstanceRegistryHydration.ts"; | 50 | import * as ProviderInstanceRegistryHydration from "./ProviderInstanceRegistryHydration.ts"; |
| 51 | import { | 51 | import { |
| 52 | mergeProviderSnapshot, | 52 | mergeProviderSnapshot, |
| 53 | mergeProviderSnapshots, | 53 | mergeProviderSnapshots, |
| 54 | selectProvidersByKind, | 54 | selectProvidersByKind, |
| 55 | upsertProviderWorkspaceSnapshot, | 55 | upsertProviderWorkspaceSnapshot, |
| 56 | ProviderRegistryLive, | ||
| 57 | } from "./ProviderRegistry.ts"; | 56 | } from "./ProviderRegistry.ts"; |
| 57 | import * as ProviderRegistryLayer from "./ProviderRegistry.ts"; | ||
| 58 | import * as ServerConfig from "../../config.ts"; | 58 | import * as ServerConfig from "../../config.ts"; |
| 59 | import * as ServerSettingsModule from "../../serverSettings.ts"; | 59 | import * as ServerSettingsModule from "../../serverSettings.ts"; |
| 60 | import { | 60 | import { |
| ... | @@ -92,14 +92,14 @@ const withBundledCompatibility = (snapshot: ServerProvider) => | ... | @@ -92,14 +92,14 @@ const withBundledCompatibility = (snapshot: ServerProvider) => |
| 92 | ); | 92 | ); |
| 93 | 93 | ||
| 94 | // Provider metadata checks use a bundled manifest and stubbed HTTP. | 94 | // Provider metadata checks use a bundled manifest and stubbed HTTP. |
| 95 | const TestHttpClientLive = Layer.succeed( | 95 | const layerTestHttpClient = Layer.succeed( |
| 96 | HttpClient.HttpClient, | 96 | HttpClient.HttpClient, |
| 97 | HttpClient.make((request) => | 97 | HttpClient.make((request) => |
| 98 | Effect.succeed(HttpClientResponse.fromWeb(request, Response.json({ version: "0.0.0" }))), | 98 | Effect.succeed(HttpClientResponse.fromWeb(request, Response.json({ version: "0.0.0" }))), |
| 99 | ), | 99 | ), |
| 100 | ).pipe(Layer.provideMerge(ModelManifest.layerTest)); | 100 | ).pipe(Layer.provideMerge(ModelManifest.layerTest)); |
| 101 | 101 | ||
| 102 | const BackgroundPolicyAlwaysRunLayer = Layer.mock(BackgroundPolicy.BackgroundPolicy)({ | 102 | const layerBackgroundPolicyAlwaysRun = Layer.mock(BackgroundPolicy.BackgroundPolicy)({ |
| 103 | reportClientActivity: () => Effect.void, | 103 | reportClientActivity: () => Effect.void, |
| 104 | removeRpcClient: () => Effect.void, | 104 | removeRpcClient: () => Effect.void, |
| 105 | reportHostPowerState: () => Effect.void, | 105 | reportHostPowerState: () => Effect.void, |
| ... | @@ -191,7 +191,7 @@ function mockHandle(result: { stdout: string; stderr: string; code: number }) { | ... | @@ -191,7 +191,7 @@ function mockHandle(result: { stdout: string; stderr: string; code: number }) { |
| 191 | }); | 191 | }); |
| 192 | } | 192 | } |
| 193 | 193 | ||
| 194 | function mockSpawnerLayer( | 194 | function layerMockSpawner( |
| 195 | handler: (args: ReadonlyArray<string>) => { | 195 | handler: (args: ReadonlyArray<string>) => { |
| 196 | stdout: string; | 196 | stdout: string; |
| 197 | stderr: string; | 197 | stderr: string; |
| ... | @@ -234,7 +234,7 @@ function recordingMockSpawnerLayer( | ... | @@ -234,7 +234,7 @@ function recordingMockSpawnerLayer( |
| 234 | return { layer, commands }; | 234 | return { layer, commands }; |
| 235 | } | 235 | } |
| 236 | 236 | ||
| 237 | function mockCommandSpawnerLayer( | 237 | function layerMockCommandSpawner( |
| 238 | handler: ( | 238 | handler: ( |
| 239 | command: string, | 239 | command: string, |
| 240 | args: ReadonlyArray<string>, | 240 | args: ReadonlyArray<string>, |
| ... | @@ -252,7 +252,7 @@ function mockCommandSpawnerLayer( | ... | @@ -252,7 +252,7 @@ function mockCommandSpawnerLayer( |
| 252 | ); | 252 | ); |
| 253 | } | 253 | } |
| 254 | 254 | ||
| 255 | function failingSpawnerLayer(description: string) { | 255 | function layerFailingSpawner(description: string) { |
| 256 | return Layer.succeed( | 256 | return Layer.succeed( |
| 257 | ChildProcessSpawner.ChildProcessSpawner, | 257 | ChildProcessSpawner.ChildProcessSpawner, |
| 258 | ChildProcessSpawner.make(() => | 258 | ChildProcessSpawner.make(() => |
| ... | @@ -268,7 +268,7 @@ function failingSpawnerLayer(description: string) { | ... | @@ -268,7 +268,7 @@ function failingSpawnerLayer(description: string) { |
| 268 | ); | 268 | ); |
| 269 | } | 269 | } |
| 270 | 270 | ||
| 271 | function hangingScopedSpawnerLayer(killCalls: Ref.Ref<number>) { | 271 | function layerHangingScopedSpawner(killCalls: Ref.Ref<number>) { |
| 272 | return Layer.succeed( | 272 | return Layer.succeed( |
| 273 | ChildProcessSpawner.ChildProcessSpawner, | 273 | ChildProcessSpawner.ChildProcessSpawner, |
| 274 | ChildProcessSpawner.make(() => | 274 | ChildProcessSpawner.make(() => |
| ... | @@ -389,7 +389,7 @@ const awaitPersistedProvider = ( | ... | @@ -389,7 +389,7 @@ const awaitPersistedProvider = ( |
| 389 | Effect.forkScoped, | 389 | Effect.forkScoped, |
| 390 | ); | 390 | ); |
| 391 | 391 | ||
| 392 | const TestNodeServices = Layer.mergeAll( | 392 | const layerTestNodeServices = Layer.mergeAll( |
| 393 | NodeServices.layer, | 393 | NodeServices.layer, |
| 394 | Layer.mock(CodexInstallation.CodexInstallation)({ | 394 | Layer.mock(CodexInstallation.CodexInstallation)({ |
| 395 | managedDirectory: "unused-managed-installation", | 395 | managedDirectory: "unused-managed-installation", |
| ... | @@ -400,1714 +400,737 @@ const TestNodeServices = Layer.mergeAll( | ... | @@ -400,1714 +400,737 @@ const TestNodeServices = Layer.mergeAll( |
| 400 | }), | 400 | }), |
| 401 | ); | 401 | ); |
| 402 | 402 | ||
| 403 | it.layer(Layer.mergeAll(TestNodeServices, ServerSettingsModule.layerTest(), TestHttpClientLive))( | 403 | it.layer( |
| 404 | "ProviderRegistry", | 404 | Layer.mergeAll(layerTestNodeServices, ServerSettingsModule.layerTest(), layerTestHttpClient), |
| 405 | (it) => { | 405 | )("ProviderRegistry", (it) => { |
| 406 | describe("checkCodexProviderStatus", () => { | 406 | describe("checkCodexProviderStatus", () => { |
| 407 | it.effect("uses the app-server account and model list for provider status", () => | 407 | it.effect("uses the app-server account and model list for provider status", () => |
| 408 | Effect.gen(function* () { | 408 | Effect.gen(function* () { |
| 409 | const status = yield* checkCodexProviderStatus(defaultCodexSettings, () => | 409 | const status = yield* checkCodexProviderStatus(defaultCodexSettings, () => |
| 410 | Effect.succeed( | 410 | Effect.succeed( |
| 411 | makeCodexProbeSnapshot({ | 411 | makeCodexProbeSnapshot({ |
| 412 | skills: [ | 412 | skills: [ |
| 413 | { | 413 | { |
| 414 | name: "github:gh-fix-ci", | 414 | name: "github:gh-fix-ci", |
| 415 | path: "/Users/test/.codex/skills/gh-fix-ci/SKILL.md", | 415 | path: "/Users/test/.codex/skills/gh-fix-ci/SKILL.md", |
| 416 | enabled: true, | 416 | enabled: true, |
| 417 | displayName: "CI Debug", | 417 | displayName: "CI Debug", |
| 418 | shortDescription: "Debug failing GitHub Actions checks", | 418 | shortDescription: "Debug failing GitHub Actions checks", |
| 419 | }, | 419 | }, |
| 420 | ], | 420 | ], |
| 421 | }), | 421 | }), |
| 422 | ), | 422 | ), |
| 423 | ); | 423 | ); |
| 424 | assert.strictEqual(status.status, "ready"); | 424 | assert.strictEqual(status.status, "ready"); |
| 425 | assert.strictEqual(status.installed, true); | 425 | assert.strictEqual(status.installed, true); |
| 426 | assert.strictEqual(status.version, "1.0.0"); | 426 | assert.strictEqual(status.version, "1.0.0"); |
| 427 | assert.strictEqual(status.auth.status, "authenticated"); | 427 | assert.strictEqual(status.auth.status, "authenticated"); |
| 428 | assert.strictEqual(status.auth.type, "chatgpt"); | 428 | assert.strictEqual(status.auth.type, "chatgpt"); |
| 429 | assert.strictEqual(status.auth.label, "ChatGPT Pro 20x Subscription"); | 429 | assert.strictEqual(status.auth.label, "ChatGPT Pro 20x Subscription"); |
| 430 | assert.strictEqual(status.auth.email, "test@example.com"); | 430 | assert.strictEqual(status.auth.email, "test@example.com"); |
| 431 | assert.deepStrictEqual(status.models, [ | 431 | assert.deepStrictEqual(status.models, [ |
| 432 | { | 432 | { |
| 433 | slug: "gpt-live-codex", | 433 | slug: "gpt-live-codex", |
| 434 | name: "GPT Live Codex", | 434 | name: "GPT Live Codex", |
| 435 | isCustom: false, | 435 | isCustom: false, |
| 436 | capabilities: codexModelCapabilities, | 436 | capabilities: codexModelCapabilities, |
| 437 | }, | 437 | }, |
| 438 | ]); | 438 | ]); |
| 439 | assert.deepStrictEqual(status.skills, [ | 439 | assert.deepStrictEqual(status.skills, [ |
| 440 | { | 440 | { |
| 441 | name: "github:gh-fix-ci", | 441 | name: "github:gh-fix-ci", |
| 442 | path: "/Users/test/.codex/skills/gh-fix-ci/SKILL.md", | 442 | path: "/Users/test/.codex/skills/gh-fix-ci/SKILL.md", |
| 443 | enabled: true, | 443 | enabled: true, |
| 444 | displayName: "CI Debug", | 444 | displayName: "CI Debug", |
| 445 | shortDescription: "Debug failing GitHub Actions checks", | 445 | shortDescription: "Debug failing GitHub Actions checks", |
| 446 | }, | 446 | }, |
| 447 | ]); | 447 | ]); |
| 448 | assert.deepStrictEqual(status.slashCommands.slice(1), [ | 448 | assert.deepStrictEqual(status.slashCommands.slice(1), [ |
| 449 | { | 449 | { |
| 450 | name: "goal", | 450 | name: "goal", |
| 451 | description: "Set a goal Codex keeps working toward until it is done", | 451 | description: "Set a goal Codex keeps working toward until it is done", |
| 452 | input: { hint: "Objective, or pause, resume, clear" }, | 452 | input: { hint: "Objective, or pause, resume, clear" }, |
| 453 | }, | 453 | }, |
| 454 | { | 454 | { |
| 455 | name: "feedback", | 455 | name: "feedback", |
| 456 | description: "Send this thread and Codex logs to OpenAI", | 456 | description: "Send this thread and Codex logs to OpenAI", |
| 457 | input: { hint: "Describe the issue (optional)" }, | 457 | input: { hint: "Describe the issue (optional)" }, |
| 458 | }, | 458 | }, |
| 459 | ]); | 459 | ]); |
| 460 | }), | 460 | }), |
| 461 | ); | 461 | ); |
| 462 | 462 | ||
| 463 | it.effect("passes configured launch args to the Codex provider probe", () => | 463 | it.effect("passes configured launch args to the Codex provider probe", () => |
| 464 | Effect.gen(function* () { | 464 | Effect.gen(function* () { |
| 465 | let observedLaunchArgs: string | undefined; | 465 | let observedLaunchArgs: string | undefined; |
| 466 | const settings = decodeCodexSettings({ launchArgs: "--strict-config --enable foo" }); | 466 | const settings = decodeCodexSettings({ launchArgs: "--strict-config --enable foo" }); |
| 467 | 467 | ||
| 468 | const status = yield* checkCodexProviderStatus(settings, (input) => { | 468 | const status = yield* checkCodexProviderStatus(settings, (input) => { |
| 469 | observedLaunchArgs = input.launchArgs; | 469 | observedLaunchArgs = input.launchArgs; |
| 470 | return Effect.succeed(makeCodexProbeSnapshot()); | 470 | return Effect.succeed(makeCodexProbeSnapshot()); |
| 471 | }); | 471 | }); |
| 472 | 472 | ||
| 473 | assert.strictEqual(status.status, "ready"); | 473 | assert.strictEqual(status.status, "ready"); |
| 474 | assert.strictEqual(observedLaunchArgs, "--strict-config --enable foo"); | 474 | assert.strictEqual(observedLaunchArgs, "--strict-config --enable foo"); |
| 475 | }), | 475 | }), |
| 476 | ); | 476 | ); |
| 477 | 477 | ||
| 478 | it.effect("returns unauthenticated when app-server requires OpenAI auth", () => | 478 | it.effect("returns unauthenticated when app-server requires OpenAI auth", () => |
| 479 | Effect.gen(function* () { | 479 | Effect.gen(function* () { |
| 480 | const status = yield* checkCodexProviderStatus(defaultCodexSettings, () => | 480 | const status = yield* checkCodexProviderStatus(defaultCodexSettings, () => |
| 481 | Effect.succeed( | 481 | Effect.succeed( |
| 482 | makeCodexProbeSnapshot({ | 482 | makeCodexProbeSnapshot({ |
| 483 | account: { | 483 | account: { |
| 484 | account: null, | 484 | account: null, |
| 485 | requiresOpenaiAuth: true, | 485 | requiresOpenaiAuth: true, |
| 486 | }, | 486 | }, |
| 487 | }), | 487 | }), |
| 488 | ), | 488 | ), |
| 489 | ); | 489 | ); |
| 490 | 490 | ||
| 491 | assert.strictEqual(status.status, "error"); | 491 | assert.strictEqual(status.status, "error"); |
| 492 | assert.strictEqual(status.auth.status, "unauthenticated"); | 492 | assert.strictEqual(status.auth.status, "unauthenticated"); |
| 493 | assert.strictEqual( | 493 | assert.strictEqual( |
| 494 | status.message, | 494 | status.message, |
| 495 | "Codex CLI is not authenticated. Run `codex login` and try again.", | 495 | "Codex CLI is not authenticated. Run `codex login` and try again.", |
| 496 | ); | 496 | ); |
| 497 | }), | 497 | }), |
| 498 | ); | 498 | ); |
| 499 | 499 | ||
| 500 | it.effect( | 500 | it.effect("returns ready with unknown auth when app-server does not require OpenAI auth", () => |
| 501 | "returns ready with unknown auth when app-server does not require OpenAI auth", | 501 | Effect.gen(function* () { |
| 502 | () => | 502 | const status = yield* checkCodexProviderStatus(defaultCodexSettings, () => |
| 503 | Effect.gen(function* () { | 503 | Effect.succeed( |
| 504 | const status = yield* checkCodexProviderStatus(defaultCodexSettings, () => | 504 | makeCodexProbeSnapshot({ |
| 505 | Effect.succeed( | 505 | account: { |
| 506 | makeCodexProbeSnapshot({ | 506 | account: null, |
| 507 | account: { | 507 | requiresOpenaiAuth: false, |
| 508 | account: null, | 508 | }, |
| 509 | requiresOpenaiAuth: false, | 509 | }), |
| 510 | }, | 510 | ), |
| 511 | }), | 511 | ); |
| 512 | ), | ||
| 513 | ); | ||
| 514 | 512 | ||
| 515 | assert.strictEqual(status.status, "ready"); | 513 | assert.strictEqual(status.status, "ready"); |
| 516 | assert.strictEqual(status.auth.status, "unknown"); | 514 | assert.strictEqual(status.auth.status, "unknown"); |
| 517 | }), | 515 | }), |
| 518 | ); | 516 | ); |
| 519 | 517 | ||
| 520 | it.effect("returns an api key label for codex api key auth", () => | 518 | it.effect("returns an api key label for codex api key auth", () => |
| 521 | Effect.gen(function* () { | 519 | Effect.gen(function* () { |
| 522 | const status = yield* checkCodexProviderStatus(defaultCodexSettings, () => | 520 | const status = yield* checkCodexProviderStatus(defaultCodexSettings, () => |
| 523 | Effect.succeed( | 521 | Effect.succeed( |
| 524 | makeCodexProbeSnapshot({ | 522 | makeCodexProbeSnapshot({ |
| 525 | account: { | 523 | account: { |
| 526 | account: { type: "apiKey" }, | 524 | account: { type: "apiKey" }, |
| 527 | requiresOpenaiAuth: false, | 525 | requiresOpenaiAuth: false, |
| 528 | }, | 526 | }, |
| 529 | }), | 527 | }), |
| 530 | ), | 528 | ), |
| 531 | ); | 529 | ); |
| 532 | 530 | ||
| 533 | assert.strictEqual(status.status, "ready"); | 531 | assert.strictEqual(status.status, "ready"); |
| 534 | assert.strictEqual(status.auth.status, "authenticated"); | 532 | assert.strictEqual(status.auth.status, "authenticated"); |
| 535 | assert.strictEqual(status.auth.type, "apiKey"); | 533 | assert.strictEqual(status.auth.type, "apiKey"); |
| 536 | assert.strictEqual(status.auth.label, "OpenAI API Key"); | 534 | assert.strictEqual(status.auth.label, "OpenAI API Key"); |
| 537 | }), | 535 | }), |
| 538 | ); | 536 | ); |
| 539 | 537 | ||
| 540 | it.effect("returns an Amazon Bedrock label for codex Bedrock auth", () => | 538 | it.effect("returns an Amazon Bedrock label for codex Bedrock auth", () => |
| 541 | Effect.gen(function* () { | 539 | Effect.gen(function* () { |
| 542 | const status = yield* checkCodexProviderStatus(defaultCodexSettings, () => | 540 | const status = yield* checkCodexProviderStatus(defaultCodexSettings, () => |
| 543 | Effect.succeed( | 541 | Effect.succeed( |
| 544 | makeCodexProbeSnapshot({ | 542 | makeCodexProbeSnapshot({ |
| 545 | account: { | 543 | account: { |
| 546 | account: { type: "amazonBedrock" }, | 544 | account: { type: "amazonBedrock" }, |
| 547 | requiresOpenaiAuth: false, | 545 | requiresOpenaiAuth: false, |
| 548 | }, | 546 | }, |
| 549 | }), | 547 | }), |
| 550 | ), | 548 | ), |
| 549 | ); | ||
| 550 | |||
| 551 | assert.strictEqual(status.status, "ready"); | ||
| 552 | assert.strictEqual(status.auth.status, "authenticated"); | ||
| 553 | assert.strictEqual(status.auth.type, "amazonBedrock"); | ||
| 554 | assert.strictEqual(status.auth.label, "Amazon Bedrock"); | ||
| 555 | }), | ||
| 556 | ); | ||
| 557 | |||
| 558 | it.effect.each([ | ||
| 559 | "codex", | ||
| 560 | "/Applications/Custom App.app/Contents/Resources/codex", | ||
| 561 | "C:\\Tools\\codex.exe", | ||
| 562 | ])("explains how to configure a Codex executable that cannot start: %s", (binaryPath) => | ||
| 563 | Effect.gen(function* () { | ||
| 564 | const settings = { ...defaultCodexSettings, binaryPath }; | ||
| 565 | const status = yield* checkCodexProviderStatus(settings, (input) => { | ||
| 566 | assert.strictEqual(input.binaryPath, binaryPath); | ||
| 567 | return Effect.fail( | ||
| 568 | new CodexErrors.CodexAppServerSpawnError({ | ||
| 569 | command: `${binaryPath} app-server`, | ||
| 570 | cause: new Error("spawn ENOENT"), | ||
| 571 | }), | ||
| 551 | ); | 572 | ); |
| 573 | }); | ||
| 574 | assert.strictEqual(status.status, "error"); | ||
| 575 | assert.strictEqual(status.installed, false); | ||
| 576 | assert.strictEqual(status.auth.status, "unknown"); | ||
| 577 | assert.include(status.message, binaryPath); | ||
| 578 | assert.include(status.message, "Settings → Providers → Codex → Binary path on the server"); | ||
| 579 | assert.strictEqual( | ||
| 580 | status.message?.includes("Installing ChatGPT or Codex desktop"), | ||
| 581 | binaryPath === "codex", | ||
| 582 | ); | ||
| 583 | assert.strictEqual(settings.binaryPath, binaryPath); | ||
| 584 | }), | ||
| 585 | ); | ||
| 552 | 586 | ||
| 553 | assert.strictEqual(status.status, "ready"); | 587 | it.effect("closes the app-server probe scope when provider status times out", () => |
| 554 | assert.strictEqual(status.auth.status, "authenticated"); | 588 | Effect.gen(function* () { |
| 555 | assert.strictEqual(status.auth.type, "amazonBedrock"); | 589 | const killCalls = yield* Ref.make(0); |
| 556 | assert.strictEqual(status.auth.label, "Amazon Bedrock"); | 590 | const statusFiber = yield* checkCodexProviderStatus(defaultCodexSettings).pipe( |
| 557 | }), | 591 | Effect.provide(layerHangingScopedSpawner(killCalls)), |
| 592 | Effect.forkChild, | ||
| 593 | ); | ||
| 594 | |||
| 595 | yield* Effect.yieldNow; | ||
| 596 | yield* TestClock.adjust("11 seconds"); | ||
| 597 | yield* Effect.yieldNow; | ||
| 598 | |||
| 599 | const status = yield* Fiber.join(statusFiber); | ||
| 600 | assert.strictEqual(status.status, "error"); | ||
| 601 | assert.strictEqual( | ||
| 602 | status.message, | ||
| 603 | "Timed out while checking Codex app-server provider status.", | ||
| 604 | ); | ||
| 605 | assert.strictEqual(yield* Ref.get(killCalls), 1); | ||
| 606 | }), | ||
| 607 | ); | ||
| 608 | }); | ||
| 609 | |||
| 610 | describe("ProviderRegistryLive", () => { | ||
| 611 | it("stores workspace skills and commands without changing machine metadata", () => { | ||
| 612 | const provider = { | ||
| 613 | instanceId: ProviderInstanceId.make("codex"), | ||
| 614 | driver: ProviderDriverKind.make("codex"), | ||
| 615 | status: "ready", | ||
| 616 | enabled: true, | ||
| 617 | installed: true, | ||
| 618 | auth: { status: "authenticated" }, | ||
| 619 | checkedAt: "2026-03-25T00:00:00.000Z", | ||
| 620 | version: "1.0.0", | ||
| 621 | models: [], | ||
| 622 | slashCommands: [{ name: "global" }], | ||
| 623 | skills: [{ name: "global", path: "/global/SKILL.md", enabled: true }], | ||
| 624 | } satisfies ServerProvider; | ||
| 625 | const scopedSnapshot = { | ||
| 626 | ...provider, | ||
| 627 | checkedAt: "2026-03-25T00:01:00.000Z", | ||
| 628 | slashCommands: [{ name: "project" }], | ||
| 629 | skills: [{ name: "project", path: "/project/SKILL.md", enabled: true }], | ||
| 630 | } satisfies ServerProvider; | ||
| 631 | |||
| 632 | const result = upsertProviderWorkspaceSnapshot(provider, "/project", scopedSnapshot); | ||
| 633 | |||
| 634 | assert.deepStrictEqual(result.slashCommands, provider.slashCommands); | ||
| 635 | assert.deepStrictEqual(result.skills, provider.skills); | ||
| 636 | assert.deepStrictEqual(result.workspaceSnapshots, [ | ||
| 637 | { | ||
| 638 | cwd: "/project", | ||
| 639 | checkedAt: scopedSnapshot.checkedAt, | ||
| 640 | slashCommands: scopedSnapshot.slashCommands, | ||
| 641 | skills: scopedSnapshot.skills, | ||
| 642 | }, | ||
| 643 | ]); | ||
| 644 | |||
| 645 | const pendingSnapshot = { | ||
| 646 | ...scopedSnapshot, | ||
| 647 | slashCommands: [COMPACT_SLASH_COMMAND], | ||
| 648 | slashCommandsPending: true, | ||
| 649 | } satisfies ProviderWorkspaceSnapshot; | ||
| 650 | const partial = upsertProviderWorkspaceSnapshot(result, "/project", pendingSnapshot); | ||
| 651 | assert.deepStrictEqual(partial.workspaceSnapshots?.[0]?.slashCommands, [{ name: "project" }]); | ||
| 652 | assert.strictEqual(partial.workspaceSnapshots?.[0]?.slashCommandsPending, true); | ||
| 653 | const otherProject = upsertProviderWorkspaceSnapshot( | ||
| 654 | partial, | ||
| 655 | "/other-project", | ||
| 656 | pendingSnapshot, | ||
| 558 | ); | 657 | ); |
| 658 | assert.deepStrictEqual(otherProject.workspaceSnapshots?.[1]?.slashCommands, [ | ||
| 659 | COMPACT_SLASH_COMMAND, | ||
| 660 | ]); | ||
| 661 | const recovered = upsertProviderWorkspaceSnapshot(partial, "/project", { | ||
| 662 | ...scopedSnapshot, | ||
| 663 | slashCommands: [COMPACT_SLASH_COMMAND, { name: "replacement" }], | ||
| 664 | }); | ||
| 665 | assert.deepStrictEqual(recovered.workspaceSnapshots?.[0]?.slashCommands, [ | ||
| 666 | COMPACT_SLASH_COMMAND, | ||
| 667 | { name: "replacement" }, | ||
| 668 | ]); | ||
| 669 | assert.strictEqual(recovered.workspaceSnapshots?.[0]?.slashCommandsPending, undefined); | ||
| 670 | }); | ||
| 559 | 671 | ||
| 560 | it.effect.each([ | 672 | it("preserves previously discovered provider models when a refresh returns none", () => { |
| 561 | "codex", | 673 | const previousProvider = { |
| 562 | "/Applications/Custom App.app/Contents/Resources/codex", | 674 | instanceId: ProviderInstanceId.make("cursor"), |
| 563 | "C:\\Tools\\codex.exe", | 675 | driver: ProviderDriverKind.make("cursor"), |
| 564 | ])("explains how to configure a Codex executable that cannot start: %s", (binaryPath) => | 676 | status: "ready", |
| 565 | Effect.gen(function* () { | 677 | enabled: true, |
| 566 | const settings = { ...defaultCodexSettings, binaryPath }; | 678 | installed: true, |
| 567 | const status = yield* checkCodexProviderStatus(settings, (input) => { | 679 | auth: { status: "authenticated" }, |
| 568 | assert.strictEqual(input.binaryPath, binaryPath); | 680 | checkedAt: "2026-04-14T00:00:00.000Z", |
| 569 | return Effect.fail( | 681 | version: "2026.04.09-f2b0fcd", |
| 570 | new CodexErrors.CodexAppServerSpawnError({ | 682 | models: [ |
| 571 | command: `${binaryPath} app-server`, | 683 | { |
| 572 | cause: new Error("spawn ENOENT"), | 684 | slug: "claude-opus-4-6", |
| 573 | }), | 685 | name: "Opus 4.6", |
| 574 | ); | 686 | isCustom: false, |
| 575 | }); | 687 | capabilities: createModelCapabilities({ |
| 576 | assert.strictEqual(status.status, "error"); | 688 | optionDescriptors: [ |
| 577 | assert.strictEqual(status.installed, false); | 689 | selectDescriptor("reasoning", "Reasoning", [ |
| 578 | assert.strictEqual(status.auth.status, "unknown"); | 690 | { id: "high", label: "High", isDefault: true }, |
| 579 | assert.include(status.message, binaryPath); | 691 | ]), |
| 580 | assert.include( | 692 | booleanDescriptor("fastMode", "Fast Mode"), |
| 581 | status.message, | 693 | booleanDescriptor("thinking", "Thinking"), |
| 582 | "Settings → Providers → Codex → Binary path on the server", | 694 | ], |
| 583 | ); | 695 | }), |
| 584 | assert.strictEqual( | 696 | }, |
| 585 | status.message?.includes("Installing ChatGPT or Codex desktop"), | 697 | ], |
| 586 | binaryPath === "codex", | 698 | slashCommands: [{ name: "review", description: "Review changes" }], |
| 587 | ); | 699 | skills: [ |
| 588 | assert.strictEqual(settings.binaryPath, binaryPath); | 700 | { |
| 589 | }), | 701 | name: "typescript", |
| 702 | description: "TypeScript help", | ||
| 703 | path: "/skills/typescript/SKILL.md", | ||
| 704 | enabled: true, | ||
| 705 | }, | ||
| 706 | ], | ||
| 707 | } as const satisfies ServerProvider; | ||
| 708 | const refreshedProvider = { | ||
| 709 | ...previousProvider, | ||
| 710 | checkedAt: "2026-04-14T00:01:00.000Z", | ||
| 711 | models: [], | ||
| 712 | slashCommands: [], | ||
| 713 | skills: [], | ||
| 714 | } satisfies ServerProvider; | ||
| 715 | |||
| 716 | assert.deepStrictEqual(mergeProviderSnapshot(previousProvider, refreshedProvider).models, [ | ||
| 717 | ...previousProvider.models, | ||
| 718 | ]); | ||
| 719 | assert.deepStrictEqual( | ||
| 720 | mergeProviderSnapshot(previousProvider, refreshedProvider).slashCommands, | ||
| 721 | [], | ||
| 590 | ); | 722 | ); |
| 723 | assert.deepStrictEqual(mergeProviderSnapshot(previousProvider, refreshedProvider).skills, []); | ||
| 724 | }); | ||
| 591 | 725 | ||
| 592 | it.effect("closes the app-server probe scope when provider status times out", () => | 726 | it("drops custom models the refreshed snapshot no longer carries", () => { |
| 593 | Effect.gen(function* () { | 727 | const previousProvider = { |
| 594 | const killCalls = yield* Ref.make(0); | 728 | instanceId: ProviderInstanceId.make("claudeAgent"), |
| 595 | const statusFiber = yield* checkCodexProviderStatus(defaultCodexSettings).pipe( | 729 | driver: ProviderDriverKind.make("claudeAgent"), |
| 596 | Effect.provide(hangingScopedSpawnerLayer(killCalls)), | 730 | status: "ready", |
| 597 | Effect.forkChild, | 731 | enabled: true, |
| 598 | ); | 732 | installed: true, |
| 733 | auth: { status: "authenticated" }, | ||
| 734 | checkedAt: "2026-04-14T00:00:00.000Z", | ||
| 735 | version: "2.1.0", | ||
| 736 | models: [ | ||
| 737 | { | ||
| 738 | slug: "claude-sonnet-4-6", | ||
| 739 | name: "Sonnet 4.6", | ||
| 740 | isCustom: false, | ||
| 741 | capabilities: null, | ||
| 742 | }, | ||
| 743 | { | ||
| 744 | slug: "removed-custom", | ||
| 745 | name: "removed-custom", | ||
| 746 | isCustom: true, | ||
| 747 | capabilities: null, | ||
| 748 | }, | ||
| 749 | ], | ||
| 750 | slashCommands: [], | ||
| 751 | skills: [], | ||
| 752 | } as const satisfies ServerProvider; | ||
| 753 | const refreshedProvider = { | ||
| 754 | ...previousProvider, | ||
| 755 | checkedAt: "2026-04-14T00:01:00.000Z", | ||
| 756 | models: [previousProvider.models[0]], | ||
| 757 | } satisfies ServerProvider; | ||
| 758 | |||
| 759 | assert.deepStrictEqual(mergeProviderSnapshot(previousProvider, refreshedProvider).models, [ | ||
| 760 | ...refreshedProvider.models, | ||
| 761 | ]); | ||
| 762 | }); | ||
| 599 | 763 | ||
| 600 | yield* Effect.yieldNow; | 764 | it("drops stale ACP Registry models missing from a completed discovery probe", () => { |
| 601 | yield* TestClock.adjust("11 seconds"); | 765 | const previousProvider = { |
| 602 | yield* Effect.yieldNow; | 766 | instanceId: ProviderInstanceId.make("acpRegistry_codex"), |
| 767 | driver: ProviderDriverKind.make("acpRegistry"), | ||
| 768 | status: "ready", | ||
| 769 | enabled: true, | ||
| 770 | installed: true, | ||
| 771 | auth: { status: "authenticated" }, | ||
| 772 | checkedAt: "2026-08-13T00:00:00.000Z", | ||
| 773 | version: "1.2.0", | ||
| 774 | models: [ | ||
| 775 | { slug: "gpt-5.6-sol", name: "GPT-5.6-Sol", isCustom: false, capabilities: null }, | ||
| 776 | { | ||
| 777 | slug: "gpt-5.6-sol[low]", | ||
| 778 | name: "GPT-5.6-Sol (low)", | ||
| 779 | isCustom: false, | ||
| 780 | capabilities: null, | ||
| 781 | }, | ||
| 782 | { slug: "default", name: "Default", isCustom: false, capabilities: null }, | ||
| 783 | ], | ||
| 784 | slashCommands: [], | ||
| 785 | skills: [], | ||
| 786 | } as const satisfies ServerProvider; | ||
| 787 | const refreshedProvider = { | ||
| 788 | ...previousProvider, | ||
| 789 | checkedAt: "2026-08-13T00:01:00.000Z", | ||
| 790 | models: [{ slug: "gpt-5.6-sol", name: "GPT-5.6-Sol", isCustom: false, capabilities: null }], | ||
| 791 | } satisfies ServerProvider; | ||
| 792 | |||
| 793 | assert.deepStrictEqual(mergeProviderSnapshot(previousProvider, refreshedProvider).models, [ | ||
| 794 | ...refreshedProvider.models, | ||
| 795 | ]); | ||
| 796 | }); | ||
| 603 | 797 | ||
| 604 | const status = yield* Fiber.join(statusFiber); | 798 | it("retains ACP Registry models while discovery has not completed", () => { |
| 605 | assert.strictEqual(status.status, "error"); | 799 | const previousProvider = { |
| 606 | assert.strictEqual( | 800 | instanceId: ProviderInstanceId.make("acpRegistry_codex"), |
| 607 | status.message, | 801 | driver: ProviderDriverKind.make("acpRegistry"), |
| 608 | "Timed out while checking Codex app-server provider status.", | 802 | status: "ready", |
| 609 | ); | 803 | enabled: true, |
| 610 | assert.strictEqual(yield* Ref.get(killCalls), 1); | 804 | installed: true, |
| 611 | }), | 805 | auth: { status: "authenticated" }, |
| 612 | ); | 806 | checkedAt: "2026-08-13T00:00:00.000Z", |
| 807 | version: "1.2.0", | ||
| 808 | models: [{ slug: "gpt-5.6-sol", name: "GPT-5.6-Sol", isCustom: false, capabilities: null }], | ||
| 809 | slashCommands: [], | ||
| 810 | skills: [], | ||
| 811 | } as const satisfies ServerProvider; | ||
| 812 | const checkingProvider = { | ||
| 813 | ...previousProvider, | ||
| 814 | checkedAt: "2026-08-13T00:01:00.000Z", | ||
| 815 | auth: { status: "unknown" }, | ||
| 816 | models: [{ slug: "default", name: "Default", isCustom: false, capabilities: null }], | ||
| 817 | } satisfies ServerProvider; | ||
| 818 | const failedProbeProvider = { | ||
| 819 | ...checkingProvider, | ||
| 820 | status: "warning", | ||
| 821 | } satisfies ServerProvider; | ||
| 822 | |||
| 823 | assert.deepStrictEqual(mergeProviderSnapshot(previousProvider, checkingProvider).models, [ | ||
| 824 | { slug: "default", name: "Default", isCustom: false, capabilities: null }, | ||
| 825 | { slug: "gpt-5.6-sol", name: "GPT-5.6-Sol", isCustom: false, capabilities: null }, | ||
| 826 | ]); | ||
| 827 | assert.deepStrictEqual(mergeProviderSnapshot(previousProvider, failedProbeProvider).models, [ | ||
| 828 | { slug: "default", name: "Default", isCustom: false, capabilities: null }, | ||
| 829 | { slug: "gpt-5.6-sol", name: "GPT-5.6-Sol", isCustom: false, capabilities: null }, | ||
| 830 | ]); | ||
| 613 | }); | 831 | }); |
| 614 | 832 | ||
| 615 | describe("ProviderRegistryLive", () => { | 833 | it("drops stale OpenCode models missing from a successful refresh", () => { |
| 616 | it("stores workspace skills and commands without changing machine metadata", () => { | 834 | const previousProvider = { |
| 617 | const provider = { | 835 | instanceId: ProviderInstanceId.make("opencode"), |
| 618 | instanceId: ProviderInstanceId.make("codex"), | 836 | driver: ProviderDriverKind.make("opencode"), |
| 619 | driver: ProviderDriverKind.make("codex"), | 837 | status: "ready", |
| 620 | status: "ready", | 838 | enabled: true, |
| 621 | enabled: true, | 839 | installed: true, |
| 622 | installed: true, | 840 | auth: { status: "authenticated" }, |
| 623 | auth: { status: "authenticated" }, | 841 | checkedAt: "2026-07-17T00:00:00.000Z", |
| 624 | checkedAt: "2026-03-25T00:00:00.000Z", | 842 | version: "1.0.0", |
| 625 | version: "1.0.0", | 843 | models: [ |
| 626 | models: [], | 844 | { |
| 627 | slashCommands: [{ name: "global" }], | 845 | slug: "github/gpt-5", |
| 628 | skills: [{ name: "global", path: "/global/SKILL.md", enabled: true }], | 846 | name: "GPT-5", |
| 629 | } satisfies ServerProvider; | 847 | subProvider: "GitHub", |
| 630 | const scopedSnapshot = { | 848 | isCustom: false, |
| 631 | ...provider, | 849 | capabilities: null, |
| 632 | checkedAt: "2026-03-25T00:01:00.000Z", | 850 | }, |
| 633 | slashCommands: [{ name: "project" }], | 851 | { |
| 634 | skills: [{ name: "project", path: "/project/SKILL.md", enabled: true }], | 852 | slug: "removed-plugin/model", |
| 635 | } satisfies ServerProvider; | 853 | name: "Removed Plugin Model", |
| 854 | subProvider: "Removed Plugin", | ||
| 855 | isCustom: false, | ||
| 856 | capabilities: null, | ||
| 857 | }, | ||
| 858 | ], | ||
| 859 | slashCommands: [], | ||
| 860 | skills: [], | ||
| 861 | } as const satisfies ServerProvider; | ||
| 862 | const refreshedProvider = { | ||
| 863 | ...previousProvider, | ||
| 864 | checkedAt: "2026-07-17T00:01:00.000Z", | ||
| 865 | models: [ | ||
| 866 | { | ||
| 867 | slug: "github/gpt-5", | ||
| 868 | name: "GPT-5", | ||
| 869 | subProvider: "GitHub", | ||
| 870 | isCustom: false, | ||
| 871 | capabilities: null, | ||
| 872 | }, | ||
| 873 | ], | ||
| 874 | } satisfies ServerProvider; | ||
| 636 | 875 | ||
| 637 | const result = upsertProviderWorkspaceSnapshot(provider, "/project", scopedSnapshot); | 876 | assert.deepStrictEqual(mergeProviderSnapshot(previousProvider, refreshedProvider).models, [ |
| 877 | ...refreshedProvider.models, | ||
| 878 | ]); | ||
| 879 | }); | ||
| 638 | 880 | ||
| 639 | assert.deepStrictEqual(result.slashCommands, provider.slashCommands); | 881 | it("retains stale OpenCode models when a refresh fails", () => { |
| 640 | assert.deepStrictEqual(result.skills, provider.skills); | 882 | const previousProvider = { |
| 641 | assert.deepStrictEqual(result.workspaceSnapshots, [ | 883 | instanceId: ProviderInstanceId.make("opencode"), |
| 884 | driver: ProviderDriverKind.make("opencode"), | ||
| 885 | status: "ready", | ||
| 886 | enabled: true, | ||
| 887 | installed: true, | ||
| 888 | auth: { status: "authenticated" }, | ||
| 889 | checkedAt: "2026-07-17T00:00:00.000Z", | ||
| 890 | version: "1.0.0", | ||
| 891 | models: [ | ||
| 642 | { | 892 | { |
| 643 | cwd: "/project", | 893 | slug: "github/gpt-5", |
| 644 | checkedAt: scopedSnapshot.checkedAt, | 894 | name: "GPT-5", |
| 645 | slashCommands: scopedSnapshot.slashCommands, | 895 | subProvider: "GitHub", |
| 646 | skills: scopedSnapshot.skills, | 896 | isCustom: false, |
| 897 | capabilities: null, | ||
| 647 | }, | 898 | }, |
| 648 | ]); | 899 | ], |
| 900 | slashCommands: [{ name: "review", description: "Review changes" }], | ||
| 901 | skills: [ | ||
| 902 | { | ||
| 903 | name: "typescript", | ||
| 904 | description: "TypeScript help", | ||
| 905 | path: "/skills/typescript/SKILL.md", | ||
| 906 | enabled: true, | ||
| 907 | }, | ||
| 908 | ], | ||
| 909 | } as const satisfies ServerProvider; | ||
| 910 | const refreshedProvider = { | ||
| 911 | ...previousProvider, | ||
| 912 | status: "error", | ||
| 913 | auth: { status: "unknown" }, | ||
| 914 | checkedAt: "2026-07-17T00:01:00.000Z", | ||
| 915 | models: [], | ||
| 916 | message: "Failed to refresh OpenCode models.", | ||
| 917 | } satisfies ServerProvider; | ||
| 918 | |||
| 919 | assert.deepStrictEqual(mergeProviderSnapshot(previousProvider, refreshedProvider).models, [ | ||
| 920 | ...previousProvider.models, | ||
| 921 | ]); | ||
| 922 | assert.deepStrictEqual( | ||
| 923 | mergeProviderSnapshot(previousProvider, refreshedProvider).slashCommands, | ||
| 924 | previousProvider.slashCommands, | ||
| 925 | ); | ||
| 926 | assert.deepStrictEqual( | ||
| 927 | mergeProviderSnapshot(previousProvider, refreshedProvider).skills, | ||
| 928 | previousProvider.skills, | ||
| 929 | ); | ||
| 930 | }); | ||
| 649 | 931 | ||
| 650 | const pendingSnapshot = { | 932 | it("classifies pending, logout, uninstall, and reconnect OpenCode inventories", () => { |
| 651 | ...scopedSnapshot, | 933 | const previousProvider = { |
| 652 | slashCommands: [COMPACT_SLASH_COMMAND], | 934 | instanceId: ProviderInstanceId.make("opencode"), |
| 653 | slashCommandsPending: true, | 935 | driver: ProviderDriverKind.make("opencode"), |
| 654 | } satisfies ProviderWorkspaceSnapshot; | 936 | status: "ready", |
| 655 | const partial = upsertProviderWorkspaceSnapshot(result, "/project", pendingSnapshot); | 937 | enabled: true, |
| 656 | assert.deepStrictEqual(partial.workspaceSnapshots?.[0]?.slashCommands, [ | 938 | installed: true, |
| 657 | { name: "project" }, | 939 | auth: { status: "authenticated" }, |
| 658 | ]); | 940 | checkedAt: "2026-07-17T00:00:00.000Z", |
| 659 | assert.strictEqual(partial.workspaceSnapshots?.[0]?.slashCommandsPending, true); | 941 | version: "1.0.0", |
| 660 | const otherProject = upsertProviderWorkspaceSnapshot( | 942 | models: [ |
| 661 | partial, | 943 | { |
| 662 | "/other-project", | 944 | slug: "github/gpt-5", |
| 663 | pendingSnapshot, | 945 | name: "GPT-5", |
| 664 | ); | 946 | subProvider: "GitHub", |
| 665 | assert.deepStrictEqual(otherProject.workspaceSnapshots?.[1]?.slashCommands, [ | 947 | isCustom: false, |
| 666 | COMPACT_SLASH_COMMAND, | 948 | capabilities: null, |
| 667 | ]); | 949 | }, |
| 668 | const recovered = upsertProviderWorkspaceSnapshot(partial, "/project", { | 950 | { |
| 669 | ...scopedSnapshot, | 951 | slug: "removed-plugin/model", |
| 670 | slashCommands: [COMPACT_SLASH_COMMAND, { name: "replacement" }], | 952 | name: "Removed Plugin Model", |
| 671 | }); | 953 | subProvider: "Removed Plugin", |
| 672 | assert.deepStrictEqual(recovered.workspaceSnapshots?.[0]?.slashCommands, [ | 954 | isCustom: false, |
| 673 | COMPACT_SLASH_COMMAND, | 955 | capabilities: null, |
| 674 | { name: "replacement" }, | 956 | }, |
| 675 | ]); | 957 | ], |
| 676 | assert.strictEqual(recovered.workspaceSnapshots?.[0]?.slashCommandsPending, undefined); | 958 | slashCommands: [{ name: "review", description: "Review changes" }], |
| 677 | }); | 959 | skills: [ |
| 960 | { | ||
| 961 | name: "typescript", | ||
| 962 | description: "TypeScript help", | ||
| 963 | path: "/skills/typescript/SKILL.md", | ||
| 964 | enabled: true, | ||
| 965 | }, | ||
| 966 | ], | ||
| 967 | } as const satisfies ServerProvider; | ||
| 968 | const pendingProvider = { | ||
| 969 | ...previousProvider, | ||
| 970 | status: "warning", | ||
| 971 | installed: false, | ||
| 972 | auth: { status: "unknown" }, | ||
| 973 | checkedAt: "2026-07-17T00:01:00.000Z", | ||
| 974 | version: null, | ||
| 975 | models: [], | ||
| 976 | message: "OpenCode provider status has not been checked in this session yet.", | ||
| 977 | } satisfies ServerProvider; | ||
| 978 | const loggedOutProvider = { | ||
| 979 | ...previousProvider, | ||
| 980 | status: "warning", | ||
| 981 | auth: { status: "unknown" }, | ||
| 982 | checkedAt: "2026-07-17T00:02:00.000Z", | ||
| 983 | models: [], | ||
| 984 | slashCommands: [], | ||
| 985 | skills: [], | ||
| 986 | message: "OpenCode is available, but it did not report any connected upstream providers.", | ||
| 987 | } satisfies ServerProvider; | ||
| 988 | const missingProvider = { | ||
| 989 | ...previousProvider, | ||
| 990 | status: "error", | ||
| 991 | installed: false, | ||
| 992 | auth: { status: "unknown" }, | ||
| 993 | checkedAt: "2026-07-17T00:03:00.000Z", | ||
| 994 | version: null, | ||
| 995 | models: [], | ||
| 996 | message: "OpenCode CLI (`opencode`) is not installed or not on PATH.", | ||
| 997 | } satisfies ServerProvider; | ||
| 998 | const authoritativeProvider = { | ||
| 999 | ...previousProvider, | ||
| 1000 | checkedAt: "2026-07-17T00:04:00.000Z", | ||
| 1001 | models: [previousProvider.models[0]!], | ||
| 1002 | } satisfies ServerProvider; | ||
| 1003 | const failedProvider = { | ||
| 1004 | ...authoritativeProvider, | ||
| 1005 | status: "error", | ||
| 1006 | auth: { status: "unknown" }, | ||
| 1007 | checkedAt: "2026-07-17T00:05:00.000Z", | ||
| 1008 | models: [], | ||
| 1009 | message: "Failed to refresh OpenCode models.", | ||
| 1010 | } satisfies ServerProvider; | ||
| 1011 | |||
| 1012 | assert.deepStrictEqual(mergeProviderSnapshot(previousProvider, pendingProvider).models, [ | ||
| 1013 | ...previousProvider.models, | ||
| 1014 | ]); | ||
| 1015 | assert.deepStrictEqual(mergeProviderSnapshot(previousProvider, loggedOutProvider).models, []); | ||
| 1016 | assert.deepStrictEqual( | ||
| 1017 | mergeProviderSnapshot(previousProvider, loggedOutProvider).slashCommands, | ||
| 1018 | [], | ||
| 1019 | ); | ||
| 1020 | assert.deepStrictEqual(mergeProviderSnapshot(previousProvider, loggedOutProvider).skills, []); | ||
| 1021 | assert.deepStrictEqual(mergeProviderSnapshot(previousProvider, missingProvider).models, []); | ||
| 678 | 1022 | ||
| 679 | it("preserves previously discovered provider models when a refresh returns none", () => { | 1023 | const afterRemoval = mergeProviderSnapshot(previousProvider, authoritativeProvider); |
| 680 | const previousProvider = { | 1024 | const afterFailure = mergeProviderSnapshot(afterRemoval, failedProvider); |
| 681 | instanceId: ProviderInstanceId.make("cursor"), | ||
| 682 | driver: ProviderDriverKind.make("cursor"), | ||
| 683 | status: "ready", | ||
| 684 | enabled: true, | ||
| 685 | installed: true, | ||
| 686 | auth: { status: "authenticated" }, | ||
| 687 | checkedAt: "2026-04-14T00:00:00.000Z", | ||
| 688 | version: "2026.04.09-f2b0fcd", | ||
| 689 | models: [ | ||
| 690 | { | ||
| 691 | slug: "claude-opus-4-6", | ||
| 692 | name: "Opus 4.6", | ||
| 693 | isCustom: false, | ||
| 694 | capabilities: createModelCapabilities({ | ||
| 695 | optionDescriptors: [ | ||
| 696 | selectDescriptor("reasoning", "Reasoning", [ | ||
| 697 | { id: "high", label: "High", isDefault: true }, | ||
| 698 | ]), | ||
| 699 | booleanDescriptor("fastMode", "Fast Mode"), | ||
| 700 | booleanDescriptor("thinking", "Thinking"), | ||
| 701 | ], | ||
| 702 | }), | ||
| 703 | }, | ||
| 704 | ], | ||
| 705 | slashCommands: [{ name: "review", description: "Review changes" }], | ||
| 706 | skills: [ | ||
| 707 | { | ||
| 708 | name: "typescript", | ||
| 709 | description: "TypeScript help", | ||
| 710 | path: "/skills/typescript/SKILL.md", | ||
| 711 | enabled: true, | ||
| 712 | }, | ||
| 713 | ], | ||
| 714 | } as const satisfies ServerProvider; | ||
| 715 | const refreshedProvider = { | ||
| 716 | ...previousProvider, | ||
| 717 | checkedAt: "2026-04-14T00:01:00.000Z", | ||
| 718 | models: [], | ||
| 719 | slashCommands: [], | ||
| 720 | skills: [], | ||
| 721 | } satisfies ServerProvider; | ||
| 722 | 1025 | ||
| 723 | assert.deepStrictEqual(mergeProviderSnapshot(previousProvider, refreshedProvider).models, [ | 1026 | assert.deepStrictEqual(afterFailure.models, [authoritativeProvider.models[0]!]); |
| 724 | ...previousProvider.models, | 1027 | }); |
| 725 | ]); | 1028 | |
| 726 | assert.deepStrictEqual( | 1029 | describe("Codex model inventories", () => { |
| 727 | mergeProviderSnapshot(previousProvider, refreshedProvider).slashCommands, | 1030 | const cachedProvider = { |
| 728 | [], | 1031 | instanceId: ProviderInstanceId.make("codex-personal"), |
| 729 | ); | 1032 | driver: ProviderDriverKind.make("codex"), |
| 730 | assert.deepStrictEqual( | 1033 | status: "ready", |
| 731 | mergeProviderSnapshot(previousProvider, refreshedProvider).skills, | 1034 | enabled: true, |
| 732 | [], | 1035 | installed: true, |
| 733 | ); | 1036 | auth: { status: "authenticated" }, |
| 1037 | checkedAt: "2026-09-04T19:00:00.000Z", | ||
| 1038 | version: "0.153.3", | ||
| 1039 | models: ["vega-alpha", "joule-alpha", "kindle-alpha", "ultima-alpha", "solstice-alpha"].map( | ||
| 1040 | (slug) => ({ slug, name: slug, isCustom: false, capabilities: null }), | ||
| 1041 | ), | ||
| 1042 | slashCommands: [], | ||
| 1043 | skills: [], | ||
| 1044 | } satisfies ServerProvider; | ||
| 1045 | const customModel = { | ||
| 1046 | slug: "custom-model", | ||
| 1047 | name: "Custom model", | ||
| 1048 | isCustom: true, | ||
| 1049 | capabilities: null, | ||
| 1050 | } as const; | ||
| 1051 | const refreshedProvider = { | ||
| 1052 | ...cachedProvider, | ||
| 1053 | checkedAt: "2026-09-04T19:01:00.000Z", | ||
| 1054 | models: [ | ||
| 1055 | { slug: "gpt-6-astra", name: "GPT 6 Astra", isCustom: false, capabilities: null }, | ||
| 1056 | cachedProvider.models[0]!, | ||
| 1057 | customModel, | ||
| 1058 | ], | ||
| 1059 | } satisfies ServerProvider; | ||
| 1060 | const pendingProvider = { | ||
| 1061 | ...cachedProvider, | ||
| 1062 | status: "warning", | ||
| 1063 | installed: false, | ||
| 1064 | auth: { status: "unknown" }, | ||
| 1065 | models: [customModel], | ||
| 1066 | } satisfies ServerProvider; | ||
| 1067 | const failedProvider = { | ||
| 1068 | ...pendingProvider, | ||
| 1069 | checkedAt: "2026-09-04T19:02:00.000Z", | ||
| 1070 | status: "error", | ||
| 1071 | installed: true, | ||
| 1072 | } satisfies ServerProvider; | ||
| 1073 | |||
| 1074 | it("drops retired alpha models after discovery, including without OpenAI authentication", () => { | ||
| 1075 | for (const authStatus of ["authenticated", "unknown"] as const) { | ||
| 1076 | assert.deepStrictEqual( | ||
| 1077 | mergeProviderSnapshot(cachedProvider, { | ||
| 1078 | ...refreshedProvider, | ||
| 1079 | auth: { status: authStatus }, | ||
| 1080 | }).models, | ||
| 1081 | refreshedProvider.models, | ||
| 1082 | ); | ||
| 1083 | } | ||
| 734 | }); | 1084 | }); |
| 735 | 1085 | ||
| 736 | it("drops custom models the refreshed snapshot no longer carries", () => { | 1086 | it("keeps discovered models during startup and failed probes without restoring removed custom models", () => { |
| 737 | const previousProvider = { | 1087 | for (const provider of [pendingProvider, failedProvider]) { |
| 738 | instanceId: ProviderInstanceId.make("claudeAgent"), | 1088 | assert.deepStrictEqual( |
| 739 | driver: ProviderDriverKind.make("claudeAgent"), | 1089 | mergeProviderSnapshot( |
| 740 | status: "ready", | ||
| 741 | enabled: true, | ||
| 742 | installed: true, | ||
| 743 | auth: { status: "authenticated" }, | ||
| 744 | checkedAt: "2026-04-14T00:00:00.000Z", | ||
| 745 | version: "2.1.0", | ||
| 746 | models: [ | ||
| 747 | { | ||
| 748 | slug: "claude-sonnet-4-6", | ||
| 749 | name: "Sonnet 4.6", | ||
| 750 | isCustom: false, | ||
| 751 | capabilities: null, | ||
| 752 | }, | ||
| 753 | { | ||
| 754 | slug: "removed-custom", | ||
| 755 | name: "removed-custom", | ||
| 756 | isCustom: true, | ||
| 757 | capabilities: null, | ||
| 758 | }, | ||
| 759 | ], | ||
| 760 | slashCommands: [], | ||
| 761 | skills: [], | ||
| 762 | } as const satisfies ServerProvider; | ||
| 763 | const refreshedProvider = { | ||
| 764 | ...previousProvider, | ||
| 765 | checkedAt: "2026-04-14T00:01:00.000Z", | ||
| 766 | models: [previousProvider.models[0]], | ||
| 767 | } satisfies ServerProvider; | ||
| 768 | |||
| 769 | assert.deepStrictEqual(mergeProviderSnapshot(previousProvider, refreshedProvider).models, [ | ||
| 770 | ...refreshedProvider.models, | ||
| 771 | ]); | ||
| 772 | }); | ||
| 773 | |||
| 774 | it("drops stale ACP Registry models missing from a completed discovery probe", () => { | ||
| 775 | const previousProvider = { | ||
| 776 | instanceId: ProviderInstanceId.make("acpRegistry_codex"), | ||
| 777 | driver: ProviderDriverKind.make("acpRegistry"), | ||
| 778 | status: "ready", | ||
| 779 | enabled: true, | ||
| 780 | installed: true, | ||
| 781 | auth: { status: "authenticated" }, | ||
| 782 | checkedAt: "2026-08-13T00:00:00.000Z", | ||
| 783 | version: "1.2.0", | ||
| 784 | models: [ | ||
| 785 | { slug: "gpt-5.6-sol", name: "GPT-5.6-Sol", isCustom: false, capabilities: null }, | ||
| 786 | { | ||
| 787 | slug: "gpt-5.6-sol[low]", | ||
| 788 | name: "GPT-5.6-Sol (low)", | ||
| 789 | isCustom: false, | ||
| 790 | capabilities: null, | ||
| 791 | }, | ||
| 792 | { slug: "default", name: "Default", isCustom: false, capabilities: null }, | ||
| 793 | ], | ||
| 794 | slashCommands: [], | ||
| 795 | skills: [], | ||
| 796 | } as const satisfies ServerProvider; | ||
| 797 | const refreshedProvider = { | ||
| 798 | ...previousProvider, | ||
| 799 | checkedAt: "2026-08-13T00:01:00.000Z", | ||
| 800 | models: [ | ||
| 801 | { slug: "gpt-5.6-sol", name: "GPT-5.6-Sol", isCustom: false, capabilities: null }, | ||
| 802 | ], | ||
| 803 | } satisfies ServerProvider; | ||
| 804 | |||
| 805 | assert.deepStrictEqual(mergeProviderSnapshot(previousProvider, refreshedProvider).models, [ | ||
| 806 | ...refreshedProvider.models, | ||
| 807 | ]); | ||
| 808 | }); | ||
| 809 | |||
| 810 | it("retains ACP Registry models while discovery has not completed", () => { | ||
| 811 | const previousProvider = { | ||
| 812 | instanceId: ProviderInstanceId.make("acpRegistry_codex"), | ||
| 813 | driver: ProviderDriverKind.make("acpRegistry"), | ||
| 814 | status: "ready", | ||
| 815 | enabled: true, | ||
| 816 | installed: true, | ||
| 817 | auth: { status: "authenticated" }, | ||
| 818 | checkedAt: "2026-08-13T00:00:00.000Z", | ||
| 819 | version: "1.2.0", | ||
| 820 | models: [ | ||
| 821 | { slug: "gpt-5.6-sol", name: "GPT-5.6-Sol", isCustom: false, capabilities: null }, | ||
| 822 | ], | ||
| 823 | slashCommands: [], | ||
| 824 | skills: [], | ||
| 825 | } as const satisfies ServerProvider; | ||
| 826 | const checkingProvider = { | ||
| 827 | ...previousProvider, | ||
| 828 | checkedAt: "2026-08-13T00:01:00.000Z", | ||
| 829 | auth: { status: "unknown" }, | ||
| 830 | models: [{ slug: "default", name: "Default", isCustom: false, capabilities: null }], | ||
| 831 | } satisfies ServerProvider; | ||
| 832 | const failedProbeProvider = { | ||
| 833 | ...checkingProvider, | ||
| 834 | status: "warning", | ||
| 835 | } satisfies ServerProvider; | ||
| 836 | |||
| 837 | assert.deepStrictEqual(mergeProviderSnapshot(previousProvider, checkingProvider).models, [ | ||
| 838 | { slug: "default", name: "Default", isCustom: false, capabilities: null }, | ||
| 839 | { slug: "gpt-5.6-sol", name: "GPT-5.6-Sol", isCustom: false, capabilities: null }, | ||
| 840 | ]); | ||
| 841 | assert.deepStrictEqual( | ||
| 842 | mergeProviderSnapshot(previousProvider, failedProbeProvider).models, | ||
| 843 | [ | ||
| 844 | { slug: "default", name: "Default", isCustom: false, capabilities: null }, | ||
| 845 | { slug: "gpt-5.6-sol", name: "GPT-5.6-Sol", isCustom: false, capabilities: null }, | ||
| 846 | ], | ||
| 847 | ); | ||
| 848 | }); | ||
| 849 | |||
| 850 | it("drops stale OpenCode models missing from a successful refresh", () => { | ||
| 851 | const previousProvider = { | ||
| 852 | instanceId: ProviderInstanceId.make("opencode"), | ||
| 853 | driver: ProviderDriverKind.make("opencode"), | ||
| 854 | status: "ready", | ||
| 855 | enabled: true, | ||
| 856 | installed: true, | ||
| 857 | auth: { status: "authenticated" }, | ||
| 858 | checkedAt: "2026-07-17T00:00:00.000Z", | ||
| 859 | version: "1.0.0", | ||
| 860 | models: [ | ||
| 861 | { | ||
| 862 | slug: "github/gpt-5", | ||
| 863 | name: "GPT-5", | ||
| 864 | subProvider: "GitHub", | ||
| 865 | isCustom: false, | ||
| 866 | capabilities: null, | ||
| 867 | }, | ||
| 868 | { | ||
| 869 | slug: "removed-plugin/model", | ||
| 870 | name: "Removed Plugin Model", | ||
| 871 | subProvider: "Removed Plugin", | ||
| 872 | isCustom: false, | ||
| 873 | capabilities: null, | ||
| 874 | }, | ||
| 875 | ], | ||
| 876 | slashCommands: [], | ||
| 877 | skills: [], | ||
| 878 | } as const satisfies ServerProvider; | ||
| 879 | const refreshedProvider = { | ||
| 880 | ...previousProvider, | ||
| 881 | checkedAt: "2026-07-17T00:01:00.000Z", | ||
| 882 | models: [ | ||
| 883 | { | ||
| 884 | slug: "github/gpt-5", | ||
| 885 | name: "GPT-5", | ||
| 886 | subProvider: "GitHub", | ||
| 887 | isCustom: false, | ||
| 888 | capabilities: null, | ||
| 889 | }, | ||
| 890 | ], | ||
| 891 | } satisfies ServerProvider; | ||
| 892 | |||
| 893 | assert.deepStrictEqual(mergeProviderSnapshot(previousProvider, refreshedProvider).models, [ | ||
| 894 | ...refreshedProvider.models, | ||
| 895 | ]); | ||
| 896 | }); | ||
| 897 | |||
| 898 | it("retains stale OpenCode models when a refresh fails", () => { | ||
| 899 | const previousProvider = { | ||
| 900 | instanceId: ProviderInstanceId.make("opencode"), | ||
| 901 | driver: ProviderDriverKind.make("opencode"), | ||
| 902 | status: "ready", | ||
| 903 | enabled: true, | ||
| 904 | installed: true, | ||
| 905 | auth: { status: "authenticated" }, | ||
| 906 | checkedAt: "2026-07-17T00:00:00.000Z", | ||
| 907 | version: "1.0.0", | ||
| 908 | models: [ | ||
| 909 | { | ||
| 910 | slug: "github/gpt-5", | ||
| 911 | name: "GPT-5", | ||
| 912 | subProvider: "GitHub", | ||
| 913 | isCustom: false, | ||
| 914 | capabilities: null, | ||
| 915 | }, | ||
| 916 | ], | ||
| 917 | slashCommands: [{ name: "review", description: "Review changes" }], | ||
| 918 | skills: [ | ||
| 919 | { | ||
| 920 | name: "typescript", | ||
| 921 | description: "TypeScript help", | ||
| 922 | path: "/skills/typescript/SKILL.md", | ||
| 923 | enabled: true, | ||
| 924 | }, | ||
| 925 | ], | ||
| 926 | } as const satisfies ServerProvider; | ||
| 927 | const refreshedProvider = { | ||
| 928 | ...previousProvider, | ||
| 929 | status: "error", | ||
| 930 | auth: { status: "unknown" }, | ||
| 931 | checkedAt: "2026-07-17T00:01:00.000Z", | ||
| 932 | models: [], | ||
| 933 | message: "Failed to refresh OpenCode models.", | ||
| 934 | } satisfies ServerProvider; | ||
| 935 | |||
| 936 | assert.deepStrictEqual(mergeProviderSnapshot(previousProvider, refreshedProvider).models, [ | ||
| 937 | ...previousProvider.models, | ||
| 938 | ]); | ||
| 939 | assert.deepStrictEqual( | ||
| 940 | mergeProviderSnapshot(previousProvider, refreshedProvider).slashCommands, | ||
| 941 | previousProvider.slashCommands, | ||
| 942 | ); | ||
| 943 | assert.deepStrictEqual( | ||
| 944 | mergeProviderSnapshot(previousProvider, refreshedProvider).skills, | ||
| 945 | previousProvider.skills, | ||
| 946 | ); | ||
| 947 | }); | ||
| 948 | |||
| 949 | it("classifies pending, logout, uninstall, and reconnect OpenCode inventories", () => { | ||
| 950 | const previousProvider = { | ||
| 951 | instanceId: ProviderInstanceId.make("opencode"), | ||
| 952 | driver: ProviderDriverKind.make("opencode"), | ||
| 953 | status: "ready", | ||
| 954 | enabled: true, | ||
| 955 | installed: true, | ||
| 956 | auth: { status: "authenticated" }, | ||
| 957 | checkedAt: "2026-07-17T00:00:00.000Z", | ||
| 958 | version: "1.0.0", | ||
| 959 | models: [ | ||
| 960 | { | ||
| 961 | slug: "github/gpt-5", | ||
| 962 | name: "GPT-5", | ||
| 963 | subProvider: "GitHub", | ||
| 964 | isCustom: false, | ||
| 965 | capabilities: null, | ||
| 966 | }, | ||
| 967 | { | ||
| 968 | slug: "removed-plugin/model", | ||
| 969 | name: "Removed Plugin Model", | ||
| 970 | subProvider: "Removed Plugin", | ||
| 971 | isCustom: false, | ||
| 972 | capabilities: null, | ||
| 973 | }, | ||
| 974 | ], | ||
| 975 | slashCommands: [{ name: "review", description: "Review changes" }], | ||
| 976 | skills: [ | ||
| 977 | { | ||
| 978 | name: "typescript", | ||
| 979 | description: "TypeScript help", | ||
| 980 | path: "/skills/typescript/SKILL.md", | ||
| 981 | enabled: true, | ||
| 982 | }, | ||
| 983 | ], | ||
| 984 | } as const satisfies ServerProvider; | ||
| 985 | const pendingProvider = { | ||
| 986 | ...previousProvider, | ||
| 987 | status: "warning", | ||
| 988 | installed: false, | ||
| 989 | auth: { status: "unknown" }, | ||
| 990 | checkedAt: "2026-07-17T00:01:00.000Z", | ||
| 991 | version: null, | ||
| 992 | models: [], | ||
| 993 | message: "OpenCode provider status has not been checked in this session yet.", | ||
| 994 | } satisfies ServerProvider; | ||
| 995 | const loggedOutProvider = { | ||
| 996 | ...previousProvider, | ||
| 997 | status: "warning", | ||
| 998 | auth: { status: "unknown" }, | ||
| 999 | checkedAt: "2026-07-17T00:02:00.000Z", | ||
| 1000 | models: [], | ||
| 1001 | slashCommands: [], | ||
| 1002 | skills: [], | ||
| 1003 | message: "OpenCode is available, but it did not report any connected upstream providers.", | ||
| 1004 | } satisfies ServerProvider; | ||
| 1005 | const missingProvider = { | ||
| 1006 | ...previousProvider, | ||
| 1007 | status: "error", | ||
| 1008 | installed: false, | ||
| 1009 | auth: { status: "unknown" }, | ||
| 1010 | checkedAt: "2026-07-17T00:03:00.000Z", | ||
| 1011 | version: null, | ||
| 1012 | models: [], | ||
| 1013 | message: "OpenCode CLI (`opencode`) is not installed or not on PATH.", | ||
| 1014 | } satisfies ServerProvider; | ||
| 1015 | const authoritativeProvider = { | ||
| 1016 | ...previousProvider, | ||
| 1017 | checkedAt: "2026-07-17T00:04:00.000Z", | ||
| 1018 | models: [previousProvider.models[0]!], | ||
| 1019 | } satisfies ServerProvider; | ||
| 1020 | const failedProvider = { | ||
| 1021 | ...authoritativeProvider, | ||
| 1022 | status: "error", | ||
| 1023 | auth: { status: "unknown" }, | ||
| 1024 | checkedAt: "2026-07-17T00:05:00.000Z", | ||
| 1025 | models: [], | ||
| 1026 | message: "Failed to refresh OpenCode models.", | ||
| 1027 | } satisfies ServerProvider; | ||
| 1028 | |||
| 1029 | assert.deepStrictEqual(mergeProviderSnapshot(previousProvider, pendingProvider).models, [ | ||
| 1030 | ...previousProvider.models, | ||
| 1031 | ]); | ||
| 1032 | assert.deepStrictEqual( | ||
| 1033 | mergeProviderSnapshot(previousProvider, loggedOutProvider).models, | ||
| 1034 | [], | ||
| 1035 | ); | ||
| 1036 | assert.deepStrictEqual( | ||
| 1037 | mergeProviderSnapshot(previousProvider, loggedOutProvider).slashCommands, | ||
| 1038 | [], | ||
| 1039 | ); | ||
| 1040 | assert.deepStrictEqual( | ||
| 1041 | mergeProviderSnapshot(previousProvider, loggedOutProvider).skills, | ||
| 1042 | [], | ||
| 1043 | ); | ||
| 1044 | assert.deepStrictEqual(mergeProviderSnapshot(previousProvider, missingProvider).models, []); | ||
| 1045 | |||
| 1046 | const afterRemoval = mergeProviderSnapshot(previousProvider, authoritativeProvider); | ||
| 1047 | const afterFailure = mergeProviderSnapshot(afterRemoval, failedProvider); | ||
| 1048 | |||
| 1049 | assert.deepStrictEqual(afterFailure.models, [authoritativeProvider.models[0]!]); | ||
| 1050 | }); | ||
| 1051 | |||
| 1052 | describe("Codex model inventories", () => { | ||
| 1053 | const cachedProvider = { | ||
| 1054 | instanceId: ProviderInstanceId.make("codex-personal"), | ||
| 1055 | driver: ProviderDriverKind.make("codex"), | ||
| 1056 | status: "ready", | ||
| 1057 | enabled: true, | ||
| 1058 | installed: true, | ||
| 1059 | auth: { status: "authenticated" }, | ||
| 1060 | checkedAt: "2026-09-04T19:00:00.000Z", | ||
| 1061 | version: "0.153.3", | ||
| 1062 | models: [ | ||
| 1063 | "vega-alpha", | ||
| 1064 | "joule-alpha", | ||
| 1065 | "kindle-alpha", | ||
| 1066 | "ultima-alpha", | ||
| 1067 | "solstice-alpha", | ||
| 1068 | ].map((slug) => ({ slug, name: slug, isCustom: false, capabilities: null })), | ||
| 1069 | slashCommands: [], | ||
| 1070 | skills: [], | ||
| 1071 | } satisfies ServerProvider; | ||
| 1072 | const customModel = { | ||
| 1073 | slug: "custom-model", | ||
| 1074 | name: "Custom model", | ||
| 1075 | isCustom: true, | ||
| 1076 | capabilities: null, | ||
| 1077 | } as const; | ||
| 1078 | const refreshedProvider = { | ||
| 1079 | ...cachedProvider, | ||
| 1080 | checkedAt: "2026-09-04T19:01:00.000Z", | ||
| 1081 | models: [ | ||
| 1082 | { slug: "gpt-6-astra", name: "GPT 6 Astra", isCustom: false, capabilities: null }, | ||
| 1083 | cachedProvider.models[0]!, | ||
| 1084 | customModel, | ||
| 1085 | ], | ||
| 1086 | } satisfies ServerProvider; | ||
| 1087 | const pendingProvider = { | ||
| 1088 | ...cachedProvider, | ||
| 1089 | status: "warning", | ||
| 1090 | installed: false, | ||
| 1091 | auth: { status: "unknown" }, | ||
| 1092 | models: [customModel], | ||
| 1093 | } satisfies ServerProvider; | ||
| 1094 | const failedProvider = { | ||
| 1095 | ...pendingProvider, | ||
| 1096 | checkedAt: "2026-09-04T19:02:00.000Z", | ||
| 1097 | status: "error", | ||
| 1098 | installed: true, | ||
| 1099 | } satisfies ServerProvider; | ||
| 1100 | |||
| 1101 | it("drops retired alpha models after discovery, including without OpenAI authentication", () => { | ||
| 1102 | for (const authStatus of ["authenticated", "unknown"] as const) { | ||
| 1103 | assert.deepStrictEqual( | ||
| 1104 | mergeProviderSnapshot(cachedProvider, { | ||
| 1105 | ...refreshedProvider, | ||
| 1106 | auth: { status: authStatus }, | ||
| 1107 | }).models, | ||
| 1108 | refreshedProvider.models, | ||
| 1109 | ); | ||
| 1110 | } | ||
| 1111 | }); | ||
| 1112 | |||
| 1113 | it("keeps discovered models during startup and failed probes without restoring removed custom models", () => { | ||
| 1114 | for (const provider of [pendingProvider, failedProvider]) { | ||
| 1115 | assert.deepStrictEqual( | ||
| 1116 | mergeProviderSnapshot( | ||
| 1117 | { | ||
| 1118 | ...cachedProvider, | ||
| 1119 | models: [...cachedProvider.models, { ...customModel, slug: "removed-custom" }], | ||
| 1120 | }, | ||
| 1121 | provider, | ||
| 1122 | ).models, | ||
| 1123 | [customModel, ...cachedProvider.models], | ||
| 1124 | ); | ||
| 1125 | } | ||
| 1126 | }); | ||
| 1127 | |||
| 1128 | it("clears discovered models after sign-out, disable, uninstall, or empty discovery", () => { | ||
| 1129 | const emptyProvider = { ...refreshedProvider, models: [customModel] }; | ||
| 1130 | const clearedProviders = [ | ||
| 1131 | { ...emptyProvider, status: "error", auth: { status: "unauthenticated" } }, | ||
| 1132 | { ...emptyProvider, status: "disabled", enabled: false }, | ||
| 1133 | { ...emptyProvider, status: "error", installed: false, auth: { status: "unknown" } }, | ||
| 1134 | emptyProvider, | ||
| 1135 | { ...emptyProvider, models: [] }, | ||
| 1136 | ] satisfies ReadonlyArray<ServerProvider>; | ||
| 1137 | |||
| 1138 | for (const provider of clearedProviders) { | ||
| 1139 | assert.deepStrictEqual( | ||
| 1140 | mergeProviderSnapshot(cachedProvider, provider).models, | ||
| 1141 | provider.models, | ||
| 1142 | ); | ||
| 1143 | } | ||
| 1144 | }); | ||
| 1145 | |||
| 1146 | it.effect("persists removals across failed refreshes and registry restarts", () => | ||
| 1147 | Effect.gen(function* () { | ||
| 1148 | const config = yield* ServerConfig.ServerConfig; | ||
| 1149 | const filePath = yield* resolveProviderStatusCachePath({ | ||
| 1150 | cacheDir: config.providerStatusCacheDir, | ||
| 1151 | instanceId: cachedProvider.instanceId, | ||
| 1152 | }); | ||
| 1153 | yield* writeProviderStatusCache({ filePath, provider: cachedProvider }); | ||
| 1154 | const nextProvider = yield* Ref.make<ServerProvider>(refreshedProvider); | ||
| 1155 | const instance = { | ||
| 1156 | instanceId: cachedProvider.instanceId, | ||
| 1157 | driverKind: cachedProvider.driver, | ||
| 1158 | continuationIdentity: { | ||
| 1159 | driverKind: cachedProvider.driver, | ||
| 1160 | continuationKey: "codex:instance:codex-personal", | ||
| 1161 | }, | ||
| 1162 | displayName: undefined, | ||
| 1163 | enabled: true, | ||
| 1164 | snapshot: { | ||
| 1165 | resolveMaintenance: () => | ||
| 1166 | Effect.succeed( | ||
| 1167 | makeManualOnlyProviderMaintenanceCapabilities({ | ||
| 1168 | provider: cachedProvider.driver, | ||
| 1169 | packageName: null, | ||
| 1170 | }), | ||
| 1171 | ), | ||
| 1172 | getSnapshot: Effect.succeed(pendingProvider), | ||
| 1173 | refresh: Ref.get(nextProvider), | ||
| 1174 | streamChanges: Stream.empty, | ||
| 1175 | applyUsageLimits: () => Effect.void, | ||
| 1176 | }, | ||
| 1177 | orchestrationAdapter: {} as ProviderInstance["orchestrationAdapter"], | ||
| 1178 | textGeneration: {} as ProviderInstance["textGeneration"], | ||
| 1179 | } satisfies ProviderInstance; | ||
| 1180 | const instanceRegistryLayer = Layer.succeed( | ||
| 1181 | ProviderInstanceRegistry.ProviderInstanceRegistry, | ||
| 1182 | { | ||
| 1183 | getInstance: (id) => | ||
| 1184 | Effect.succeed(id === instance.instanceId ? instance : undefined), | ||
| 1185 | listInstances: Effect.succeed([instance]), | ||
| 1186 | listUnavailable: Effect.succeed([]), | ||
| 1187 | streamChanges: Stream.empty, | ||
| 1188 | subscribeChanges: Effect.flatMap(PubSub.unbounded<void>(), PubSub.subscribe), | ||
| 1189 | }, | ||
| 1190 | ); | ||
| 1191 | const retainedModels = [ | ||
| 1192 | customModel, | ||
| 1193 | ...refreshedProvider.models.filter((model) => !model.isCustom), | ||
| 1194 | ]; | ||
| 1195 | |||
| 1196 | for (const restarted of [false, true]) { | ||
| 1197 | yield* Effect.gen(function* () { | ||
| 1198 | const registry = yield* ProviderRegistry.ProviderRegistry; | ||
| 1199 | const expectedModels = restarted | ||
| 1200 | ? retainedModels | ||
| 1201 | : [customModel, ...cachedProvider.models]; | ||
| 1202 | assert.deepStrictEqual((yield* registry.getProviders)[0]?.models, expectedModels); | ||
| 1203 | |||
| 1204 | yield* registry.refreshInstance(instance.instanceId); | ||
| 1205 | assert.deepStrictEqual( | ||
| 1206 | (yield* readProviderStatusCache(filePath))?.models, | ||
| 1207 | restarted ? retainedModels : refreshedProvider.models, | ||
| 1208 | ); | ||
| 1209 | |||
| 1210 | yield* Ref.set(nextProvider, failedProvider); | ||
| 1211 | const afterFailure = yield* registry.refreshInstance(instance.instanceId); | ||
| 1212 | assert.deepStrictEqual(afterFailure[0]?.models, retainedModels); | ||
| 1213 | assert.deepStrictEqual( | ||
| 1214 | (yield* readProviderStatusCache(filePath))?.models, | ||
| 1215 | retainedModels, | ||
| 1216 | ); | ||
| 1217 | }).pipe( | ||
| 1218 | Effect.provide(ProviderRegistryLive.pipe(Layer.provide(instanceRegistryLayer))), | ||
| 1219 | Effect.scoped, | ||
| 1220 | ); | ||
| 1221 | } | ||
| 1222 | }).pipe( | ||
| 1223 | Effect.provide( | ||
| 1224 | ServerConfig.layerTest(process.cwd(), { | ||
| 1225 | prefix: "t3-codex-retired-model-cache-", | ||
| 1226 | }).pipe(Layer.provideMerge(NodeServices.layer)), | ||
| 1227 | ), | ||
| 1228 | ), | ||
| 1229 | ); | ||
| 1230 | }); | ||
| 1231 | |||
| 1232 | describe("Antigravity model inventories", () => { | ||
| 1233 | const previousProvider = { | ||
| 1234 | instanceId: ProviderInstanceId.make("antigravity-personal"), | ||
| 1235 | driver: ProviderDriverKind.make("antigravity"), | ||
| 1236 | status: "ready", | ||
| 1237 | enabled: true, | ||
| 1238 | installed: true, | ||
| 1239 | auth: { status: "authenticated" }, | ||
| 1240 | checkedAt: "2026-09-02T00:00:00.000Z", | ||
| 1241 | version: "0.1.3", | ||
| 1242 | models: [ | ||
| 1243 | { | ||
| 1244 | slug: "gemini-3.1-pro-high", | ||
| 1245 | name: "Gemini 3.1 Pro High", | ||
| 1246 | isCustom: false, | ||
| 1247 | capabilities: null, | ||
| 1248 | }, | ||
| 1249 | { | ||
| 1250 | slug: "gemini-3-flash", | ||
| 1251 | name: "Gemini 3 Flash", | ||
| 1252 | isCustom: false, | ||
| 1253 | capabilities: null, | ||
| 1254 | }, | ||
| 1255 | ], | ||
| 1256 | slashCommands: [], | ||
| 1257 | skills: [], | ||
| 1258 | } as const satisfies ServerProvider; | ||
| 1259 | |||
| 1260 | it("removes unavailable models after a successful refresh", () => { | ||
| 1261 | for (const status of ["ready", "warning"] as const) { | ||
| 1262 | const refreshedProvider = { | ||
| 1263 | ...previousProvider, | ||
| 1264 | status, | ||
| 1265 | checkedAt: "2026-09-02T00:01:00.000Z", | ||
| 1266 | models: [previousProvider.models[1]], | ||
| 1267 | } satisfies ServerProvider; | ||
| 1268 | const afterRefresh = mergeProviderSnapshot(previousProvider, refreshedProvider); | ||
| 1269 | |||
| 1270 | assert.deepStrictEqual(afterRefresh.models, refreshedProvider.models); | ||
| 1271 | |||
| 1272 | const afterFailure = mergeProviderSnapshot(afterRefresh, { | ||
| 1273 | ...refreshedProvider, | ||
| 1274 | status: "error", | ||
| 1275 | auth: { status: "unknown" }, | ||
| 1276 | models: [], | ||
| 1277 | }); | ||
| 1278 | assert.deepStrictEqual(afterFailure.models, refreshedProvider.models); | ||
| 1279 | } | ||
| 1280 | }); | ||
| 1281 | |||
| 1282 | it("keeps cached models during health checks and temporary failures", () => { | ||
| 1283 | for (const installed of [false, true]) { | ||
| 1284 | const pendingProvider = { | ||
| 1285 | ...previousProvider, | ||
| 1286 | status: "warning", | ||
| 1287 | installed, | ||
| 1288 | auth: { status: "unknown" }, | ||
| 1289 | checkedAt: "2026-09-02T00:01:00.000Z", | ||
| 1290 | version: installed ? previousProvider.version : null, | ||
| 1291 | models: [], | ||
| 1292 | } satisfies ServerProvider; | ||
| 1293 | |||
| 1294 | assert.deepStrictEqual( | ||
| 1295 | mergeProviderSnapshot(previousProvider, pendingProvider).models, | ||
| 1296 | previousProvider.models, | ||
| 1297 | ); | ||
| 1298 | } | ||
| 1299 | |||
| 1300 | for (const authStatus of ["unknown", "authenticated"] as const) { | ||
| 1301 | const failedProvider = { | ||
| 1302 | ...previousProvider, | ||
| 1303 | status: "error", | ||
| 1304 | auth: { status: authStatus }, | ||
| 1305 | checkedAt: "2026-09-02T00:02:00.000Z", | ||
| 1306 | models: [], | ||
| 1307 | } satisfies ServerProvider; | ||
| 1308 | |||
| 1309 | assert.deepStrictEqual( | ||
| 1310 | mergeProviderSnapshot(previousProvider, failedProvider).models, | ||
| 1311 | previousProvider.models, | ||
| 1312 | ); | ||
| 1313 | } | ||
| 1314 | }); | ||
| 1315 | |||
| 1316 | it("clears models after sign-out, disable, uninstall, or an empty successful refresh", () => { | ||
| 1317 | const emptyProvider = { | ||
| 1318 | ...previousProvider, | ||
| 1319 | checkedAt: "2026-09-02T00:01:00.000Z", | ||
| 1320 | models: [], | ||
| 1321 | } satisfies ServerProvider; | ||
| 1322 | const clearedProviders = [ | ||
| 1323 | { ...emptyProvider, status: "warning", auth: { status: "unauthenticated" } }, | ||
| 1324 | { ...emptyProvider, status: "error", auth: { status: "unauthenticated" } }, | ||
| 1325 | { ...emptyProvider, status: "disabled", enabled: false }, | ||
| 1326 | { ...emptyProvider, status: "error", enabled: false }, | ||
| 1327 | { ...emptyProvider, status: "error", installed: false, auth: { status: "unknown" } }, | ||
| 1328 | emptyProvider, | ||
| 1329 | ] satisfies ReadonlyArray<ServerProvider>; | ||
| 1330 | |||
| 1331 | for (const provider of clearedProviders) { | ||
| 1332 | const afterRemoval = mergeProviderSnapshot(previousProvider, provider); | ||
| 1333 | assert.deepStrictEqual(afterRemoval.models, []); | ||
| 1334 | |||
| 1335 | const afterFailure = mergeProviderSnapshot(afterRemoval, { | ||
| 1336 | ...emptyProvider, | ||
| 1337 | status: "error", | ||
| 1338 | auth: { status: "unknown" }, | ||
| 1339 | }); | ||
| 1340 | assert.deepStrictEqual(afterFailure.models, []); | ||
| 1341 | } | ||
| 1342 | }); | ||
| 1343 | }); | ||
| 1344 | |||
| 1345 | describe("Antigravity saved account", () => { | ||
| 1346 | const signedIn = { | ||
| 1347 | instanceId: ProviderInstanceId.make("antigravity-personal"), | ||
| 1348 | driver: ProviderDriverKind.make("antigravity"), | ||
| 1349 | status: "ready", | ||
| 1350 | enabled: true, | ||
| 1351 | installed: true, | ||
| 1352 | auth: { status: "authenticated", type: "oauth-personal", label: "Google account" }, | ||
| 1353 | checkedAt: "2026-09-05T00:00:00.000Z", | ||
| 1354 | version: "agy_acp_server_1.1.1", | ||
| 1355 | models: [ | ||
| 1356 | { | ||
| 1357 | slug: "gemini-3.7-flash-high", | ||
| 1358 | name: "Gemini 3.7 Flash", | ||
| 1359 | isCustom: false, | ||
| 1360 | capabilities: null, | ||
| 1361 | }, | ||
| 1362 | ], | ||
| 1363 | slashCommands: [{ name: "plan" }], | ||
| 1364 | skills: [], | ||
| 1365 | } as const satisfies ServerProvider; | ||
| 1366 | const uncheckedMessage = | ||
| 1367 | "Antigravity is installed. Google account access is not checked yet."; | ||
| 1368 | const restartProbe = { | ||
| 1369 | ...signedIn, | ||
| 1370 | status: "warning", | ||
| 1371 | auth: { status: "unknown" }, | ||
| 1372 | checkedAt: "2026-09-05T00:01:00.000Z", | ||
| 1373 | message: uncheckedMessage, | ||
| 1374 | models: [], | ||
| 1375 | } as const satisfies ServerProvider; | ||
| 1376 | |||
| 1377 | it("keeps the saved Google account through restart health checks", () => { | ||
| 1378 | const merged = mergeProviderSnapshot(signedIn, restartProbe); | ||
| 1379 | const { message: _uncheckedMessage, ...probeWithoutMessage } = restartProbe; | ||
| 1380 | assert.deepStrictEqual(merged, { | ||
| 1381 | ...probeWithoutMessage, | ||
| 1382 | status: "ready", | ||
| 1383 | auth: signedIn.auth, | ||
| 1384 | models: signedIn.models, | ||
| 1385 | }); | ||
| 1386 | assert.equal("message" in merged, false); | ||
| 1387 | // The next periodic probe reads the merged snapshot as its previous state. | ||
| 1388 | assert.deepStrictEqual(mergeProviderSnapshot(merged, restartProbe), merged); | ||
| 1389 | }); | ||
| 1390 | |||
| 1391 | it("carries the account through the boot probe and a failed probe without hiding them", () => { | ||
| 1392 | const booting = { | ||
| 1393 | ...restartProbe, | ||
| 1394 | installed: false, | ||
| 1395 | version: null, | ||
| 1396 | message: "Checking Antigravity availability.", | ||
| 1397 | } satisfies ServerProvider; | ||
| 1398 | assert.deepStrictEqual(mergeProviderSnapshot(signedIn, booting), { | ||
| 1399 | ...booting, | ||
| 1400 | auth: signedIn.auth, | ||
| 1401 | models: signedIn.models, | ||
| 1402 | }); | ||
| 1403 | |||
| 1404 | const failed = { | ||
| 1405 | ...restartProbe, | ||
| 1406 | status: "error", | ||
| 1407 | message: "Antigravity did not respond to its local health check within 90 seconds.", | ||
| 1408 | } satisfies ServerProvider; | ||
| 1409 | assert.deepStrictEqual(mergeProviderSnapshot(signedIn, failed), { | ||
| 1410 | ...failed, | ||
| 1411 | auth: signedIn.auth, | ||
| 1412 | models: signedIn.models, | ||
| 1413 | }); | ||
| 1414 | }); | ||
| 1415 | |||
| 1416 | it("does not invent an account after sign-out, disable, uninstall, or for other providers", () => { | ||
| 1417 | const untouched = [ | ||
| 1418 | { ...restartProbe, auth: { status: "unauthenticated" } }, | ||
| 1419 | { ...restartProbe, status: "disabled", enabled: false }, | ||
| 1420 | { ...restartProbe, status: "error", installed: false }, | ||
| 1421 | { ...restartProbe, driver: ProviderDriverKind.make("codex") }, | ||
| 1422 | // The instance was rebuilt with another sign-in method. | ||
| 1423 | { ...restartProbe, auth: { status: "unknown", type: "gemini-api-key" } }, | ||
| 1424 | ] satisfies ReadonlyArray<ServerProvider>; | ||
| 1425 | for (const next of untouched) { | ||
| 1426 | const merged = mergeProviderSnapshot(signedIn, next); | ||
| 1427 | assert.deepStrictEqual(merged.auth, next.auth); | ||
| 1428 | assert.equal(merged.status, next.status); | ||
| 1429 | assert.equal(merged.message, next.message); | ||
| 1430 | } | ||
| 1431 | assert.deepStrictEqual( | ||
| 1432 | mergeProviderSnapshot({ ...signedIn, auth: { status: "unknown" } }, restartProbe).auth, | ||
| 1433 | { status: "unknown" }, | ||
| 1434 | ); | ||
| 1435 | assert.equal( | ||
| 1436 | mergeProviderSnapshot( | ||
| 1437 | { ...signedIn, driver: ProviderDriverKind.make("codex") }, | ||
| 1438 | restartProbe, | ||
| 1439 | ).auth.status, | ||
| 1440 | "unknown", | ||
| 1441 | ); | ||
| 1442 | assert.deepStrictEqual( | ||
| 1443 | mergeProviderSnapshot(signedIn, { | ||
| 1444 | ...restartProbe, | ||
| 1445 | auth: { status: "unknown", type: "oauth-personal" }, | ||
| 1446 | }).auth, | ||
| 1447 | signedIn.auth, | ||
| 1448 | ); | ||
| 1449 | }); | ||
| 1450 | }); | ||
| 1451 | |||
| 1452 | it("fills missing capabilities from the previous provider snapshot", () => { | ||
| 1453 | const previousProvider = { | ||
| 1454 | instanceId: ProviderInstanceId.make("cursor"), | ||
| 1455 | driver: ProviderDriverKind.make("cursor"), | ||
| 1456 | status: "ready", | ||
| 1457 | enabled: true, | ||
| 1458 | installed: true, | ||
| 1459 | auth: { status: "authenticated" }, | ||
| 1460 | checkedAt: "2026-04-14T00:00:00.000Z", | ||
| 1461 | version: "2026.04.09-f2b0fcd", | ||
| 1462 | models: [ | ||
| 1463 | { | ||
| 1464 | slug: "claude-opus-4-6", | ||
| 1465 | name: "Opus 4.6", | ||
| 1466 | isCustom: false, | ||
| 1467 | capabilities: createModelCapabilities({ | ||
| 1468 | optionDescriptors: [ | ||
| 1469 | selectDescriptor("reasoning", "Reasoning", [ | ||
| 1470 | { id: "high", label: "High", isDefault: true }, | ||
| 1471 | ]), | ||
| 1472 | booleanDescriptor("fastMode", "Fast Mode"), | ||
| 1473 | booleanDescriptor("thinking", "Thinking"), | ||
| 1474 | ], | ||
| 1475 | }), | ||
| 1476 | }, | ||
| 1477 | ], | ||
| 1478 | slashCommands: [], | ||
| 1479 | skills: [], | ||
| 1480 | } as const satisfies ServerProvider; | ||
| 1481 | const refreshedProvider = { | ||
| 1482 | ...previousProvider, | ||
| 1483 | checkedAt: "2026-04-14T00:01:00.000Z", | ||
| 1484 | models: [ | ||
| 1485 | { | ||
| 1486 | slug: "claude-opus-4-6", | ||
| 1487 | name: "Opus 4.6", | ||
| 1488 | isCustom: false, | ||
| 1489 | capabilities: createModelCapabilities({ | ||
| 1490 | optionDescriptors: [], | ||
| 1491 | }), | ||
| 1492 | }, | ||
| 1493 | ], | ||
| 1494 | } satisfies ServerProvider; | ||
| 1495 | |||
| 1496 | assert.deepStrictEqual(mergeProviderSnapshot(previousProvider, refreshedProvider).models, [ | ||
| 1497 | ...previousProvider.models, | ||
| 1498 | ]); | ||
| 1499 | }); | ||
| 1500 | |||
| 1501 | it.effect("does not run provider probes during layer construction", () => | ||
| 1502 | Effect.gen(function* () { | ||
| 1503 | const codexDriver = ProviderDriverKind.make("codex"); | ||
| 1504 | const codexInstanceId = ProviderInstanceId.make("codex"); | ||
| 1505 | const initialProvider = { | ||
| 1506 | instanceId: codexInstanceId, | ||
| 1507 | driver: codexDriver, | ||
| 1508 | status: "warning", | ||
| 1509 | enabled: true, | ||
| 1510 | installed: false, | ||
| 1511 | auth: { status: "unknown" }, | ||
| 1512 | checkedAt: "2026-06-10T00:00:00.000Z", | ||
| 1513 | version: null, | ||
| 1514 | message: "Checking Codex provider status.", | ||
| 1515 | models: [], | ||
| 1516 | slashCommands: [], | ||
| 1517 | skills: [], | ||
| 1518 | } as const satisfies ServerProvider; | ||
| 1519 | const refreshCalls = yield* Ref.make(0); | ||
| 1520 | const instance = { | ||
| 1521 | instanceId: codexInstanceId, | ||
| 1522 | driverKind: codexDriver, | ||
| 1523 | continuationIdentity: { | ||
| 1524 | driverKind: codexDriver, | ||
| 1525 | continuationKey: "codex:instance:codex", | ||
| 1526 | }, | ||
| 1527 | displayName: undefined, | ||
| 1528 | enabled: true, | ||
| 1529 | snapshot: { | ||
| 1530 | resolveMaintenance: () => | ||
| 1531 | Effect.succeed( | ||
| 1532 | makeManualOnlyProviderMaintenanceCapabilities({ | ||
| 1533 | provider: codexDriver, | ||
| 1534 | packageName: null, | ||
| 1535 | }), | ||
| 1536 | ), | ||
| 1537 | getSnapshot: Effect.succeed(initialProvider), | ||
| 1538 | refresh: Ref.update(refreshCalls, (count) => count + 1).pipe( | ||
| 1539 | Effect.andThen(Effect.never), | ||
| 1540 | ), | ||
| 1541 | streamChanges: Stream.empty, | ||
| 1542 | applyUsageLimits: () => Effect.void, | ||
| 1543 | }, | ||
| 1544 | orchestrationAdapter: {} as ProviderInstance["orchestrationAdapter"], | ||
| 1545 | textGeneration: {} as ProviderInstance["textGeneration"], | ||
| 1546 | } satisfies ProviderInstance; | ||
| 1547 | const instanceRegistryLayer = Layer.succeed( | ||
| 1548 | ProviderInstanceRegistry.ProviderInstanceRegistry, | ||
| 1549 | { | ||
| 1550 | getInstance: (instanceId) => | ||
| 1551 | Effect.succeed(instanceId === codexInstanceId ? instance : undefined), | ||
| 1552 | listInstances: Effect.succeed([instance]), | ||
| 1553 | listUnavailable: Effect.succeed([]), | ||
| 1554 | streamChanges: Stream.empty, | ||
| 1555 | subscribeChanges: Effect.flatMap(PubSub.unbounded<void>(), PubSub.subscribe), | ||
| 1556 | }, | ||
| 1557 | ); | ||
| 1558 | const scope = yield* Scope.make(); | ||
| 1559 | yield* Effect.addFinalizer(() => Scope.close(scope, Exit.void)); | ||
| 1560 | const runtimeServices = yield* Layer.build( | ||
| 1561 | ProviderRegistryLive.pipe( | ||
| 1562 | Layer.provideMerge(instanceRegistryLayer), | ||
| 1563 | Layer.provideMerge( | ||
| 1564 | ServerConfig.layerTest(process.cwd(), { | ||
| 1565 | prefix: "t3-provider-registry-background-refresh-", | ||
| 1566 | }), | ||
| 1567 | ), | ||
| 1568 | Layer.provideMerge(NodeServices.layer), | ||
| 1569 | ), | ||
| 1570 | ).pipe(Scope.provide(scope)); | ||
| 1571 | yield* Effect.gen(function* () { | ||
| 1572 | const registry = yield* ProviderRegistry.ProviderRegistry; | ||
| 1573 | assert.deepStrictEqual(yield* registry.getProviders, [initialProvider]); | ||
| 1574 | assert.strictEqual(yield* Ref.get(refreshCalls), 0); | ||
| 1575 | }).pipe(Effect.provide(runtimeServices)); | ||
| 1576 | }), | ||
| 1577 | ); | ||
| 1578 | |||
| 1579 | it.effect("deduplicates cwd probes and clears snapshots when an instance rebuilds", () => | ||
| 1580 | Effect.gen(function* () { | ||
| 1581 | const driver = ProviderDriverKind.make("codex"); | ||
| 1582 | const instanceId = ProviderInstanceId.make("codex"); | ||
| 1583 | const machineProvider = { | ||
| 1584 | instanceId, | ||
| 1585 | driver, | ||
| 1586 | status: "ready", | ||
| 1587 | enabled: true, | ||
| 1588 | installed: true, | ||
| 1589 | auth: { status: "authenticated" }, | ||
| 1590 | checkedAt: "2026-06-10T00:00:00.000Z", | ||
| 1591 | version: "1.0.0", | ||
| 1592 | models: [], | ||
| 1593 | slashCommands: [{ name: "global" }], | ||
| 1594 | skills: [{ name: "global", path: "/global/SKILL.md", enabled: true }], | ||
| 1595 | } as const satisfies ServerProvider; | ||
| 1596 | const scopedProvider = { | ||
| 1597 | ...machineProvider, | ||
| 1598 | checkedAt: "2026-06-10T00:01:00.000Z", | ||
| 1599 | slashCommands: [{ name: "project" }], | ||
| 1600 | skills: [{ name: "project", path: "/workspace/SKILL.md", enabled: true }], | ||
| 1601 | } as const satisfies ServerProvider; | ||
| 1602 | const pendingScopedProvider = { | ||
| 1603 | ...scopedProvider, | ||
| 1604 | status: "error", | ||
| 1605 | installed: false, | ||
| 1606 | slashCommands: [], | ||
| 1607 | } as const satisfies ServerProvider; | ||
| 1608 | const snapshotCalls = yield* Ref.make(0); | ||
| 1609 | const scopedResult = yield* Ref.make<ProviderWorkspaceSnapshot>({ | ||
| 1610 | ...scopedProvider, | ||
| 1611 | status: "error", | ||
| 1612 | slashCommands: [COMPACT_SLASH_COMMAND], | ||
| 1613 | slashCommandsPending: true, | ||
| 1614 | }); | ||
| 1615 | const cacheInvalidations = yield* Ref.make(0); | ||
| 1616 | const scanGate = yield* Ref.make<{ | ||
| 1617 | readonly started: Deferred.Deferred<void>; | ||
| 1618 | readonly release: Deferred.Deferred<void>; | ||
| 1619 | } | null>(null); | ||
| 1620 | const returnPendingSnapshot = yield* Ref.make(true); | ||
| 1621 | const probeStarted = yield* Deferred.make<void>(); | ||
| 1622 | const releaseProbe = yield* Deferred.make<void>(); | ||
| 1623 | const makeInstance = ( | ||
| 1624 | provider: ServerProvider, | ||
| 1625 | snapshotForCwd: NonNullable<ProviderInstance["snapshotForCwd"]>, | ||
| 1626 | ): ProviderInstance => ({ | ||
| 1627 | instanceId, | ||
| 1628 | driverKind: driver, | ||
| 1629 | continuationIdentity: { | ||
| 1630 | driverKind: driver, | ||
| 1631 | continuationKey: "codex:instance:codex", | ||
| 1632 | }, | ||
| 1633 | displayName: undefined, | ||
| 1634 | enabled: true, | ||
| 1635 | snapshot: { | ||
| 1636 | resolveMaintenance: () => | ||
| 1637 | Effect.succeed( | ||
| 1638 | makeManualOnlyProviderMaintenanceCapabilities({ | ||
| 1639 | provider: driver, | ||
| 1640 | packageName: null, | ||
| 1641 | }), | ||
| 1642 | ), | ||
| 1643 | getSnapshot: Effect.succeed(provider), | ||
| 1644 | refresh: Effect.succeed(provider), | ||
| 1645 | streamChanges: Stream.empty, | ||
| 1646 | applyUsageLimits: () => Effect.void, | ||
| 1647 | }, | ||
| 1648 | snapshotForCwd, | ||
| 1649 | invalidateCaches: Ref.update(cacheInvalidations, (count) => count + 1), | ||
| 1650 | orchestrationAdapter: {} as ProviderInstance["orchestrationAdapter"], | ||
| 1651 | textGeneration: {} as ProviderInstance["textGeneration"], | ||
| 1652 | }); | ||
| 1653 | const firstInstance = makeInstance(machineProvider, () => | ||
| 1654 | Effect.gen(function* () { | ||
| 1655 | yield* Ref.update(snapshotCalls, (count) => count + 1); | ||
| 1656 | if (yield* Ref.get(returnPendingSnapshot)) return pendingScopedProvider; | ||
| 1657 | yield* Deferred.succeed(probeStarted, undefined); | ||
| 1658 | yield* Deferred.await(releaseProbe); | ||
| 1659 | const result = yield* Ref.get(scopedResult); | ||
| 1660 | const gate = yield* Ref.getAndSet(scanGate, null); | ||
| 1661 | if (gate) { | ||
| 1662 | yield* Deferred.succeed(gate.started, undefined); | ||
| 1663 | yield* Deferred.await(gate.release); | ||
| 1664 | } | ||
| 1665 | return result; | ||
| 1666 | }), | ||
| 1667 | ); | ||
| 1668 | const rebuiltProvider = { | ||
| 1669 | ...machineProvider, | ||
| 1670 | checkedAt: "2026-06-10T00:02:00.000Z", | ||
| 1671 | status: "warning", | ||
| 1672 | installed: false, | ||
| 1673 | auth: { status: "unknown" }, | ||
| 1674 | } satisfies ServerProvider; | ||
| 1675 | const rebuiltInstance = makeInstance(rebuiltProvider, () => | ||
| 1676 | Ref.update(snapshotCalls, (count) => count + 1).pipe(Effect.as(scopedProvider)), | ||
| 1677 | ); | ||
| 1678 | const registryChanges = yield* PubSub.unbounded<void>(); | ||
| 1679 | const instancesRef = yield* Ref.make<ReadonlyArray<ProviderInstance>>([firstInstance]); | ||
| 1680 | const instanceRegistryLayer = Layer.succeed( | ||
| 1681 | ProviderInstanceRegistry.ProviderInstanceRegistry, | ||
| 1682 | { | ||
| 1683 | getInstance: (requestedId) => | ||
| 1684 | Ref.get(instancesRef).pipe( | ||
| 1685 | Effect.map((instances) => | ||
| 1686 | instances.find((instance) => instance.instanceId === requestedId), | ||
| 1687 | ), | ||
| 1688 | ), | ||
| 1689 | listInstances: Ref.get(instancesRef), | ||
| 1690 | listUnavailable: Effect.succeed([]), | ||
| 1691 | streamChanges: Stream.fromPubSub(registryChanges), | ||
| 1692 | subscribeChanges: PubSub.subscribe(registryChanges), | ||
| 1693 | }, | ||
| 1694 | ); | ||
| 1695 | const scope = yield* Scope.make(); | ||
| 1696 | yield* Effect.addFinalizer(() => Scope.close(scope, Exit.void)); | ||
| 1697 | const runtimeServices = yield* Layer.build( | ||
| 1698 | ProviderRegistryLive.pipe( | ||
| 1699 | Layer.provideMerge(instanceRegistryLayer), | ||
| 1700 | Layer.provideMerge( | ||
| 1701 | ServerConfig.layerTest(process.cwd(), { | ||
| 1702 | prefix: "t3-provider-registry-workspace-snapshot-", | ||
| 1703 | }), | ||
| 1704 | ), | ||
| 1705 | Layer.provideMerge(NodeServices.layer), | ||
| 1706 | ), | ||
| 1707 | ).pipe(Scope.provide(scope)); | ||
| 1708 | |||
| 1709 | yield* Effect.gen(function* () { | ||
| 1710 | const registry = yield* ProviderRegistry.ProviderRegistry; | ||
| 1711 | yield* registry.refreshWorkspaceSnapshot({ instanceId, cwd: "/workspace" }); | ||
| 1712 | assert.strictEqual((yield* registry.getProviders)[0]?.workspaceSnapshots, undefined); | ||
| 1713 | yield* Ref.set(returnPendingSnapshot, false); | ||
| 1714 | const workspaceUpdate = yield* registry.streamChanges.pipe( | ||
| 1715 | Stream.runHead, | ||
| 1716 | Effect.forkChild, | ||
| 1717 | ); | ||
| 1718 | yield* Effect.yieldNow; | ||
| 1719 | const firstRefresh = yield* registry | ||
| 1720 | .refreshWorkspaceSnapshot({ instanceId, cwd: "/workspace" }) | ||
| 1721 | .pipe(Effect.forkChild); | ||
| 1722 | yield* Deferred.await(probeStarted); | ||
| 1723 | const duplicateRefresh = yield* registry | ||
| 1724 | .refreshWorkspaceSnapshot({ instanceId, cwd: "/workspace" }) | ||
| 1725 | .pipe(Effect.forkChild); | ||
| 1726 | yield* Effect.yieldNow; | ||
| 1727 | assert.strictEqual(yield* Ref.get(snapshotCalls), 2); | ||
| 1728 | yield* Deferred.succeed(releaseProbe, undefined); | ||
| 1729 | yield* Fiber.join(firstRefresh); | ||
| 1730 | yield* Fiber.join(duplicateRefresh); | ||
| 1731 | const published = yield* Fiber.join(workspaceUpdate); | ||
| 1732 | assert.strictEqual(published._tag, "Some"); | ||
| 1733 | const providers = yield* registry.getProviders; | ||
| 1734 | assert.deepStrictEqual(providers[0]?.skills, machineProvider.skills); | ||
| 1735 | assert.deepStrictEqual( | ||
| 1736 | providers[0]?.workspaceSnapshots?.[0]?.skills, | ||
| 1737 | scopedProvider.skills, | ||
| 1738 | ); | ||
| 1739 | assert.deepStrictEqual(providers[0]?.workspaceSnapshots?.[0]?.slashCommands, [ | ||
| 1740 | COMPACT_SLASH_COMMAND, | ||
| 1741 | ]); | ||
| 1742 | assert.strictEqual(providers[0]?.workspaceSnapshots?.[0]?.slashCommandsPending, true); | ||
| 1743 | yield* Ref.set(scopedResult, { | ||
| 1744 | ...scopedProvider, | ||
| 1745 | status: "error", | ||
| 1746 | slashCommandsPending: false, | ||
| 1747 | }); | ||
| 1748 | yield* registry.refreshWorkspaceSnapshot({ instanceId, cwd: "/workspace" }); | ||
| 1749 | assert.strictEqual(yield* Ref.get(snapshotCalls), 3); | ||
| 1750 | assert.deepStrictEqual( | ||
| 1751 | (yield* registry.getProviders)[0]?.workspaceSnapshots?.[0]?.slashCommands, | ||
| 1752 | scopedProvider.slashCommands, | ||
| 1753 | ); | ||
| 1754 | assert.strictEqual( | ||
| 1755 | (yield* registry.getProviders)[0]?.workspaceSnapshots?.[0]?.slashCommandsPending, | ||
| 1756 | undefined, | ||
| 1757 | ); | ||
| 1758 | yield* registry.refreshWorkspaceSnapshot({ instanceId, cwd: "/workspace" }); | ||
| 1759 | assert.strictEqual(yield* Ref.get(snapshotCalls), 3); | ||
| 1760 | const newSkills = [ | ||
| 1761 | ...scopedProvider.skills, | ||
| 1762 | { name: "added", path: "/workspace/added/SKILL.md", enabled: true }, | ||
| 1763 | ]; | ||
| 1764 | yield* Ref.set(scopedResult, { ...scopedProvider, skills: newSkills }); | ||
| 1765 | yield* registry.refreshWorkspaceSnapshot({ | ||
| 1766 | instanceId, | ||
| 1767 | cwd: "/workspace", | ||
| 1768 | fresh: true, | ||
| 1769 | }); | ||
| 1770 | assert.strictEqual(yield* Ref.get(snapshotCalls), 4); | ||
| 1771 | assert.strictEqual(yield* Ref.get(cacheInvalidations), 1); | ||
| 1772 | assert.deepStrictEqual( | ||
| 1773 | (yield* registry.getProviders)[0]?.workspaceSnapshots?.map((s) => s.skills), | ||
| 1774 | [newSkills], | ||
| 1775 | ); | ||
| 1776 | |||
| 1777 | // A slow fresh scan that read older files must not overwrite a | ||
| 1778 | // newer scan that finished first. | ||
| 1779 | const slowStarted = yield* Deferred.make<void>(); | ||
| 1780 | const releaseSlow = yield* Deferred.make<void>(); | ||
| 1781 | yield* Ref.set(scanGate, { started: slowStarted, release: releaseSlow }); | ||
| 1782 | yield* Ref.set(scopedResult, scopedProvider); | ||
| 1783 | const slowScan = yield* registry | ||
| 1784 | .refreshWorkspaceSnapshot({ instanceId, cwd: "/workspace", fresh: true }) | ||
| 1785 | .pipe(Effect.forkChild); | ||
| 1786 | yield* Deferred.await(slowStarted); | ||
| 1787 | const latestSkills = [ | ||
| 1788 | ...newSkills, | ||
| 1789 | { name: "latest", path: "/workspace/latest/SKILL.md", enabled: true }, | ||
| 1790 | ]; | ||
| 1791 | yield* Ref.set(scopedResult, { ...scopedProvider, skills: latestSkills }); | ||
| 1792 | yield* registry.refreshWorkspaceSnapshot({ | ||
| 1793 | instanceId, | ||
| 1794 | cwd: "/workspace", | ||
| 1795 | fresh: true, | ||
| 1796 | }); | ||
| 1797 | yield* Deferred.succeed(releaseSlow, undefined); | ||
| 1798 | yield* Fiber.join(slowScan); | ||
| 1799 | assert.deepStrictEqual( | ||
| 1800 | (yield* registry.getProviders)[0]?.workspaceSnapshots?.map((s) => s.skills), | ||
| 1801 | [latestSkills], | ||
| 1802 | ); | ||
| 1803 | |||
| 1804 | yield* Ref.set(instancesRef, [rebuiltInstance]); | ||
| 1805 | yield* PubSub.publish(registryChanges, undefined); | ||
| 1806 | let rebuilt = yield* registry.getProviders; | ||
| 1807 | for ( | ||
| 1808 | let attempt = 0; | ||
| 1809 | attempt < 50 && rebuilt[0]?.checkedAt !== rebuiltProvider.checkedAt; | ||
| 1810 | attempt += 1 | ||
| 1811 | ) { | ||
| 1812 | yield* Effect.yieldNow; | ||
| 1813 | rebuilt = yield* registry.getProviders; | ||
| 1814 | } | ||
| 1815 | assert.strictEqual(rebuilt[0]?.checkedAt, rebuiltProvider.checkedAt); | ||
| 1816 | assert.strictEqual(rebuilt[0]?.workspaceSnapshots, undefined); | ||
| 1817 | }).pipe(Effect.provide(runtimeServices)); | ||
| 1818 | }), | ||
| 1819 | ); | ||
| 1820 | |||
| 1821 | it.effect("refreshes OpenCode catalogs and preserves other providers", () => | ||
| 1822 | Effect.gen(function* () { | ||
| 1823 | const codexDriver = ProviderDriverKind.make("codex"); | ||
| 1824 | const openCodeDriver = ProviderDriverKind.make("opencode"); | ||
| 1825 | const codexInstanceId = ProviderInstanceId.make("codex"); | ||
| 1826 | const openCodeInstanceId = ProviderInstanceId.make("opencode"); | ||
| 1827 | const codexRefreshCalls = yield* Ref.make(0); | ||
| 1828 | const openCodeRefreshCalls = yield* Ref.make(0); | ||
| 1829 | const codexProvider = { | ||
| 1830 | instanceId: codexInstanceId, | ||
| 1831 | driver: codexDriver, | ||
| 1832 | status: "ready", | ||
| 1833 | enabled: true, | ||
| 1834 | installed: true, | ||
| 1835 | auth: { status: "authenticated" }, | ||
| 1836 | checkedAt: "2026-06-10T00:00:00.000Z", | ||
| 1837 | version: "1.0.0", | ||
| 1838 | models: [], | ||
| 1839 | slashCommands: [], | ||
| 1840 | skills: [], | ||
| 1841 | } as const satisfies ServerProvider; | ||
| 1842 | const failedOpenCodeProvider = { | ||
| 1843 | instanceId: openCodeInstanceId, | ||
| 1844 | driver: openCodeDriver, | ||
| 1845 | status: "error", | ||
| 1846 | enabled: true, | ||
| 1847 | installed: true, | ||
| 1848 | auth: { status: "unknown" }, | ||
| 1849 | checkedAt: "2026-06-10T00:00:00.000Z", | ||
| 1850 | version: "1.0.0", | ||
| 1851 | message: "Failed to refresh OpenCode models.", | ||
| 1852 | models: [], | ||
| 1853 | slashCommands: [], | ||
| 1854 | skills: [], | ||
| 1855 | } as const satisfies ServerProvider; | ||
| 1856 | const recoveredOpenCodeProvider = { | ||
| 1857 | ...failedOpenCodeProvider, | ||
| 1858 | status: "ready", | ||
| 1859 | auth: { status: "authenticated" }, | ||
| 1860 | checkedAt: "2026-06-10T00:01:00.000Z", | ||
| 1861 | message: "One upstream provider connected through OpenCode.", | ||
| 1862 | models: [ | ||
| 1863 | { | ||
| 1864 | slug: "github/gpt-5", | ||
| 1865 | name: "GPT-5", | ||
| 1866 | subProvider: "GitHub", | ||
| 1867 | isCustom: false, | ||
| 1868 | capabilities: null, | ||
| 1869 | }, | ||
| 1870 | ], | ||
| 1871 | } as const satisfies ServerProvider; | ||
| 1872 | const changedCatalogProvider = { | ||
| 1873 | ...recoveredOpenCodeProvider, | ||
| 1874 | checkedAt: "2026-06-10T00:02:00.000Z", | ||
| 1875 | models: [ | ||
| 1876 | { | ||
| 1877 | slug: "anthropic/claude-sonnet-4", | ||
| 1878 | name: "Claude Sonnet 4", | ||
| 1879 | subProvider: "Anthropic", | ||
| 1880 | isCustom: false, | ||
| 1881 | capabilities: null, | ||
| 1882 | }, | ||
| 1883 | ], | ||
| 1884 | } as const satisfies ServerProvider; | ||
| 1885 | const catalogSnapshot = yield* Ref.make<ServerProvider>(recoveredOpenCodeProvider); | ||
| 1886 | const instances = [ | ||
| 1887 | { | ||
| 1888 | instanceId: codexInstanceId, | ||
| 1889 | driverKind: codexDriver, | ||
| 1890 | continuationIdentity: { | ||
| 1891 | driverKind: codexDriver, | ||
| 1892 | continuationKey: "codex:instance:codex", | ||
| 1893 | }, | ||
| 1894 | displayName: undefined, | ||
| 1895 | enabled: true, | ||
| 1896 | snapshot: { | ||
| 1897 | resolveMaintenance: () => | ||
| 1898 | Effect.succeed( | ||
| 1899 | makeManualOnlyProviderMaintenanceCapabilities({ | ||
| 1900 | provider: codexDriver, | ||
| 1901 | packageName: null, | ||
| 1902 | }), | ||
| 1903 | ), | ||
| 1904 | getSnapshot: Effect.succeed(codexProvider), | ||
| 1905 | refresh: Ref.update(codexRefreshCalls, (count) => count + 1).pipe( | ||
| 1906 | Effect.as(codexProvider), | ||
| 1907 | ), | ||
| 1908 | streamChanges: Stream.empty, | ||
| 1909 | applyUsageLimits: () => Effect.void, | ||
| 1910 | }, | ||
| 1911 | orchestrationAdapter: {} as ProviderInstance["orchestrationAdapter"], | ||
| 1912 | textGeneration: {} as ProviderInstance["textGeneration"], | ||
| 1913 | }, | ||
| 1914 | { | ||
| 1915 | instanceId: openCodeInstanceId, | ||
| 1916 | driverKind: openCodeDriver, | ||
| 1917 | continuationIdentity: { | ||
| 1918 | driverKind: openCodeDriver, | ||
| 1919 | continuationKey: "opencode:instance:opencode", | ||
| 1920 | }, | ||
| 1921 | displayName: undefined, | ||
| 1922 | enabled: true, | ||
| 1923 | snapshot: { | ||
| 1924 | resolveMaintenance: () => | ||
| 1925 | Effect.succeed( | ||
| 1926 | makeManualOnlyProviderMaintenanceCapabilities({ | ||
| 1927 | provider: openCodeDriver, | ||
| 1928 | packageName: null, | ||
| 1929 | }), | ||
| 1930 | ), | ||
| 1931 | getSnapshot: Effect.succeed(failedOpenCodeProvider), | ||
| 1932 | refresh: Ref.update(openCodeRefreshCalls, (count) => count + 1).pipe( | ||
| 1933 | Effect.andThen(Ref.get(catalogSnapshot)), | ||
| 1934 | ), | ||
| 1935 | streamChanges: Stream.empty, | ||
| 1936 | applyUsageLimits: () => Effect.void, | ||
| 1937 | }, | ||
| 1938 | orchestrationAdapter: {} as ProviderInstance["orchestrationAdapter"], | ||
| 1939 | textGeneration: {} as ProviderInstance["textGeneration"], | ||
| 1940 | }, | ||
| 1941 | ] satisfies ReadonlyArray<ProviderInstance>; | ||
| 1942 | const instanceRegistryLayer = Layer.succeed( | ||
| 1943 | ProviderInstanceRegistry.ProviderInstanceRegistry, | ||
| 1944 | { | ||
| 1945 | getInstance: (instanceId) => | ||
| 1946 | Effect.succeed(instances.find((instance) => instance.instanceId === instanceId)), | ||
| 1947 | listInstances: Effect.succeed(instances), | ||
| 1948 | listUnavailable: Effect.succeed([]), | ||
| 1949 | streamChanges: Stream.empty, | ||
| 1950 | subscribeChanges: Effect.flatMap(PubSub.unbounded<void>(), PubSub.subscribe), | ||
| 1951 | }, | ||
| 1952 | ); | ||
| 1953 | const scope = yield* Scope.make(); | ||
| 1954 | yield* Effect.addFinalizer(() => Scope.close(scope, Exit.void)); | ||
| 1955 | const runtimeServices = yield* Layer.build( | ||
| 1956 | ProviderRegistryLive.pipe( | ||
| 1957 | Layer.provideMerge(instanceRegistryLayer), | ||
| 1958 | Layer.provideMerge( | ||
| 1959 | ServerConfig.layerTest(process.cwd(), { | ||
| 1960 | prefix: "t3-provider-registry-reconnect-refresh-", | ||
| 1961 | }), | ||
| 1962 | ), | ||
| 1963 | Layer.provideMerge(NodeServices.layer), | ||
| 1964 | ), | ||
| 1965 | ).pipe(Scope.provide(scope)); | ||
| 1966 | |||
| 1967 | yield* Effect.gen(function* () { | ||
| 1968 | const registry = yield* ProviderRegistry.ProviderRegistry; | ||
| 1969 | const initialProviders = yield* registry.getProviders; | ||
| 1970 | assert.strictEqual( | ||
| 1971 | initialProviders.find((provider) => provider.instanceId === openCodeInstanceId) | ||
| 1972 | ?.status, | ||
| 1973 | "error", | ||
| 1974 | ); | ||
| 1975 | |||
| 1976 | const recoveredProviders = yield* registry.refresh(); | ||
| 1977 | assert.deepStrictEqual( | ||
| 1978 | recoveredProviders.find((provider) => provider.instanceId === openCodeInstanceId) | ||
| 1979 | ?.models, | ||
| 1980 | recoveredOpenCodeProvider.models, | ||
| 1981 | ); | ||
| 1982 | assert.deepStrictEqual( | ||
| 1983 | recoveredProviders.find((provider) => provider.instanceId === codexInstanceId), | ||
| 1984 | withBundledCompatibility(codexProvider), | ||
| 1985 | ); | ||
| 1986 | |||
| 1987 | yield* Ref.set(catalogSnapshot, changedCatalogProvider); | ||
| 1988 | const changedProviders = yield* registry.refresh(); | ||
| 1989 | assert.deepStrictEqual( | ||
| 1990 | changedProviders.find((provider) => provider.instanceId === openCodeInstanceId) | ||
| 1991 | ?.models, | ||
| 1992 | changedCatalogProvider.models, | ||
| 1993 | ); | ||
| 1994 | assert.deepStrictEqual( | ||
| 1995 | changedProviders.find((provider) => provider.instanceId === codexInstanceId), | ||
| 1996 | withBundledCompatibility(codexProvider), | ||
| 1997 | ); | ||
| 1998 | }).pipe(Effect.provide(runtimeServices)); | ||
| 1999 | |||
| 2000 | assert.strictEqual(yield* Ref.get(codexRefreshCalls), 2); | ||
| 2001 | assert.strictEqual(yield* Ref.get(openCodeRefreshCalls), 2); | ||
| 2002 | }), | ||
| 2003 | ); | ||
| 2004 | |||
| 2005 | it("persists merged provider snapshots for the providers that were refreshed", () => { | ||
| 2006 | const previousProviders = [ | ||
| 2007 | { | ||
| 2008 | instanceId: ProviderInstanceId.make("cursor"), | ||
| 2009 | driver: ProviderDriverKind.make("cursor"), | ||
| 2010 | status: "ready", | ||
| 2011 | enabled: true, | ||
| 2012 | installed: true, | ||
| 2013 | auth: { status: "authenticated" }, | ||
| 2014 | checkedAt: "2026-04-14T00:00:00.000Z", | ||
| 2015 | version: "2026.04.09-f2b0fcd", | ||
| 2016 | models: [ | ||
| 2017 | { | ||
| 2018 | slug: "claude-opus-4-6", | ||
| 2019 | name: "Opus 4.6", | ||
| 2020 | isCustom: false, | ||
| 2021 | capabilities: createModelCapabilities({ | ||
| 2022 | optionDescriptors: [ | ||
| 2023 | selectDescriptor("reasoning", "Reasoning", [ | ||
| 2024 | { id: "high", label: "High", isDefault: true }, | ||
| 2025 | ]), | ||
| 2026 | booleanDescriptor("fastMode", "Fast Mode"), | ||
| 2027 | booleanDescriptor("thinking", "Thinking"), | ||
| 2028 | ], | ||
| 2029 | }), | ||
| 2030 | }, | ||
| 2031 | ], | ||
| 2032 | slashCommands: [], | ||
| 2033 | skills: [], | ||
| 2034 | }, | ||
| 2035 | { | ||
| 2036 | instanceId: ProviderInstanceId.make("codex"), | ||
| 2037 | driver: ProviderDriverKind.make("codex"), | ||
| 2038 | status: "ready", | ||
| 2039 | enabled: true, | ||
| 2040 | installed: true, | ||
| 2041 | auth: { status: "authenticated" }, | ||
| 2042 | checkedAt: "2026-04-14T00:00:00.000Z", | ||
| 2043 | version: "1.0.0", | ||
| 2044 | models: [], | ||
| 2045 | slashCommands: [], | ||
| 2046 | skills: [], | ||
| 2047 | }, | ||
| 2048 | ] as const satisfies ReadonlyArray<ServerProvider>; | ||
| 2049 | const refreshedCursor = { | ||
| 2050 | ...previousProviders[0], | ||
| 2051 | checkedAt: "2026-04-14T00:01:00.000Z", | ||
| 2052 | models: [], | ||
| 2053 | } satisfies ServerProvider; | ||
| 2054 | |||
| 2055 | const mergedProviders = mergeProviderSnapshots(previousProviders, [refreshedCursor]); | ||
| 2056 | const persistedProviders = selectProvidersByKind( | ||
| 2057 | mergedProviders, | ||
| 2058 | new Set([ProviderDriverKind.make("cursor")]), | ||
| 2059 | ); | ||
| 2060 | |||
| 2061 | assert.deepStrictEqual(persistedProviders, [ | ||
| 2062 | { | ||
| 2063 | ...refreshedCursor, | ||
| 2064 | models: [...previousProviders[0].models], | ||
| 2065 | }, | ||
| 2066 | ]); | ||
| 2067 | }); | ||
| 2068 | |||
| 2069 | it.effect("persists the merged snapshot when a live update has empty models", () => | ||
| 2070 | Effect.gen(function* () { | ||
| 2071 | const cursorDriver = ProviderDriverKind.make("cursor"); | ||
| 2072 | const cursorInstanceId = ProviderInstanceId.make("cursor"); | ||
| 2073 | const initialProvider = { | ||
| 2074 | instanceId: cursorInstanceId, | ||
| 2075 | driver: cursorDriver, | ||
| 2076 | status: "ready", | ||
| 2077 | enabled: true, | ||
| 2078 | installed: true, | ||
| 2079 | auth: { status: "authenticated" }, | ||
| 2080 | checkedAt: "2026-04-14T00:00:00.000Z", | ||
| 2081 | version: "2026.04.09-f2b0fcd", | ||
| 2082 | models: [ | ||
| 2083 | { | 1090 | { |
| 2084 | slug: "claude-opus-4-6", | 1091 | ...cachedProvider, |
| 2085 | name: "Opus 4.6", | 1092 | models: [...cachedProvider.models, { ...customModel, slug: "removed-custom" }], |
| 2086 | isCustom: false, | ||
| 2087 | capabilities: createModelCapabilities({ | ||
| 2088 | optionDescriptors: [ | ||
| 2089 | selectDescriptor("reasoning", "Reasoning", [ | ||
| 2090 | { id: "high", label: "High", isDefault: true }, | ||
| 2091 | ]), | ||
| 2092 | ], | ||
| 2093 | }), | ||
| 2094 | }, | 1093 | }, |
| 2095 | ], | 1094 | provider, |
| 2096 | slashCommands: [], | 1095 | ).models, |
| 2097 | skills: [], | 1096 | [customModel, ...cachedProvider.models], |
| 2098 | } as const satisfies ServerProvider; | 1097 | ); |
| 2099 | const refreshedProvider = { | 1098 | } |
| 2100 | ...initialProvider, | 1099 | }); |
| 2101 | checkedAt: "2026-04-14T00:01:00.000Z", | 1100 | |
| 2102 | models: [], | 1101 | it("clears discovered models after sign-out, disable, uninstall, or empty discovery", () => { |
| 2103 | } satisfies ServerProvider; | 1102 | const emptyProvider = { ...refreshedProvider, models: [customModel] }; |
| 2104 | const changes = yield* PubSub.unbounded<ServerProvider>(); | 1103 | const clearedProviders = [ |
| 1104 | { ...emptyProvider, status: "error", auth: { status: "unauthenticated" } }, | ||
| 1105 | { ...emptyProvider, status: "disabled", enabled: false }, | ||
| 1106 | { ...emptyProvider, status: "error", installed: false, auth: { status: "unknown" } }, | ||
| 1107 | emptyProvider, | ||
| 1108 | { ...emptyProvider, models: [] }, | ||
| 1109 | ] satisfies ReadonlyArray<ServerProvider>; | ||
| 1110 | |||
| 1111 | for (const provider of clearedProviders) { | ||
| 1112 | assert.deepStrictEqual( | ||
| 1113 | mergeProviderSnapshot(cachedProvider, provider).models, | ||
| 1114 | provider.models, | ||
| 1115 | ); | ||
| 1116 | } | ||
| 1117 | }); | ||
| 1118 | |||
| 1119 | it.effect("persists removals across failed refreshes and registry restarts", () => | ||
| 1120 | Effect.gen(function* () { | ||
| 1121 | const config = yield* ServerConfig.ServerConfig; | ||
| 1122 | const filePath = yield* resolveProviderStatusCachePath({ | ||
| 1123 | cacheDir: config.providerStatusCacheDir, | ||
| 1124 | instanceId: cachedProvider.instanceId, | ||
| 1125 | }); | ||
| 1126 | yield* writeProviderStatusCache({ filePath, provider: cachedProvider }); | ||
| 1127 | const nextProvider = yield* Ref.make<ServerProvider>(refreshedProvider); | ||
| 2105 | const instance = { | 1128 | const instance = { |
| 2106 | instanceId: cursorInstanceId, | 1129 | instanceId: cachedProvider.instanceId, |
| 2107 | driverKind: cursorDriver, | 1130 | driverKind: cachedProvider.driver, |
| 2108 | continuationIdentity: { | 1131 | continuationIdentity: { |
| 2109 | driverKind: cursorDriver, | 1132 | driverKind: cachedProvider.driver, |
| 2110 | continuationKey: "cursor:instance:cursor", | 1133 | continuationKey: "codex:instance:codex-personal", |
| 2111 | }, | 1134 | }, |
| 2112 | displayName: undefined, | 1135 | displayName: undefined, |
| 2113 | enabled: true, | 1136 | enabled: true, |
| ... | @@ -2115,225 +1138,728 @@ it.layer(Layer.mergeAll(TestNodeServices, ServerSettingsModule.layerTest(), Test | ... | @@ -2115,225 +1138,728 @@ it.layer(Layer.mergeAll(TestNodeServices, ServerSettingsModule.layerTest(), Test |
| 2115 | resolveMaintenance: () => | 1138 | resolveMaintenance: () => |
| 2116 | Effect.succeed( | 1139 | Effect.succeed( |
| 2117 | makeManualOnlyProviderMaintenanceCapabilities({ | 1140 | makeManualOnlyProviderMaintenanceCapabilities({ |
| 2118 | provider: cursorDriver, | 1141 | provider: cachedProvider.driver, |
| 2119 | packageName: null, | 1142 | packageName: null, |
| 2120 | }), | 1143 | }), |
| 2121 | ), | 1144 | ), |
| 2122 | getSnapshot: Effect.succeed(initialProvider), | 1145 | getSnapshot: Effect.succeed(pendingProvider), |
| 2123 | refresh: Effect.succeed(refreshedProvider), | 1146 | refresh: Ref.get(nextProvider), |
| 2124 | streamChanges: Stream.fromPubSub(changes), | 1147 | streamChanges: Stream.empty, |
| 2125 | applyUsageLimits: () => Effect.void, | 1148 | applyUsageLimits: () => Effect.void, |
| 2126 | }, | 1149 | }, |
| 2127 | orchestrationAdapter: {} as ProviderInstance["orchestrationAdapter"], | 1150 | orchestrationAdapter: {} as ProviderInstance["orchestrationAdapter"], |
| 2128 | textGeneration: {} as ProviderInstance["textGeneration"], | 1151 | textGeneration: {} as ProviderInstance["textGeneration"], |
| 2129 | } satisfies ProviderInstance; | 1152 | } satisfies ProviderInstance; |
| 2130 | const instanceRegistryLayer = Layer.succeed( | 1153 | const layerInstanceRegistry = Layer.succeed( |
| 2131 | ProviderInstanceRegistry.ProviderInstanceRegistry, | 1154 | ProviderInstanceRegistry.ProviderInstanceRegistry, |
| 2132 | { | 1155 | { |
| 2133 | getInstance: (instanceId) => | 1156 | getInstance: (id) => |
| 2134 | Effect.succeed(instanceId === cursorInstanceId ? instance : undefined), | 1157 | Effect.succeed(id === instance.instanceId ? instance : undefined), |
| 2135 | listInstances: Effect.succeed([instance]), | 1158 | listInstances: Effect.succeed([instance]), |
| 2136 | listUnavailable: Effect.succeed([]), | 1159 | listUnavailable: Effect.succeed([]), |
| 2137 | streamChanges: Stream.empty, | 1160 | streamChanges: Stream.empty, |
| 2138 | subscribeChanges: Effect.flatMap(PubSub.unbounded<void>(), (pubsub) => | 1161 | subscribeChanges: Effect.flatMap(PubSub.unbounded<void>(), PubSub.subscribe), |
| 2139 | PubSub.subscribe(pubsub), | ||
| 2140 | ), | ||
| 2141 | }, | 1162 | }, |
| 2142 | ); | 1163 | ); |
| 2143 | const scope = yield* Scope.make(); | 1164 | const retainedModels = [ |
| 2144 | yield* Effect.addFinalizer(() => Scope.close(scope, Exit.void)); | 1165 | customModel, |
| 2145 | const runtimeServices = yield* Layer.build( | 1166 | ...refreshedProvider.models.filter((model) => !model.isCustom), |
| 2146 | ProviderRegistryLive.pipe( | 1167 | ]; |
| 2147 | Layer.provideMerge(instanceRegistryLayer), | ||
| 2148 | Layer.provideMerge( | ||
| 2149 | ServerConfig.layerTest(process.cwd(), { | ||
| 2150 | prefix: "t3-provider-registry-merged-persist-", | ||
| 2151 | }), | ||
| 2152 | ), | ||
| 2153 | Layer.provideMerge(BackgroundPolicyAlwaysRunLayer), | ||
| 2154 | Layer.provideMerge(NodeServices.layer), | ||
| 2155 | ), | ||
| 2156 | ).pipe(Scope.provide(scope)); | ||
| 2157 | 1168 | ||
| 2158 | yield* Effect.gen(function* () { | 1169 | for (const restarted of [false, true]) { |
| 2159 | const registry = yield* ProviderRegistry.ProviderRegistry; | 1170 | yield* Effect.gen(function* () { |
| 2160 | const config = yield* ServerConfig.ServerConfig; | 1171 | const registry = yield* ProviderRegistry.ProviderRegistry; |
| 2161 | const filePath = yield* resolveProviderStatusCachePath({ | 1172 | const expectedModels = restarted |
| 2162 | cacheDir: config.providerStatusCacheDir, | 1173 | ? retainedModels |
| 2163 | instanceId: cursorInstanceId, | 1174 | : [customModel, ...cachedProvider.models]; |
| 2164 | }); | 1175 | assert.deepStrictEqual((yield* registry.getProviders)[0]?.models, expectedModels); |
| 1176 | |||
| 1177 | yield* registry.refreshInstance(instance.instanceId); | ||
| 1178 | assert.deepStrictEqual( | ||
| 1179 | (yield* readProviderStatusCache(filePath))?.models, | ||
| 1180 | restarted ? retainedModels : refreshedProvider.models, | ||
| 1181 | ); | ||
| 2165 | 1182 | ||
| 2166 | assert.deepStrictEqual((yield* registry.getProviders)[0]?.models, [ | 1183 | yield* Ref.set(nextProvider, failedProvider); |
| 2167 | ...initialProvider.models, | 1184 | const afterFailure = yield* registry.refreshInstance(instance.instanceId); |
| 2168 | ]); | 1185 | assert.deepStrictEqual(afterFailure[0]?.models, retainedModels); |
| 2169 | const persisted = yield* awaitPersistedProvider(registry, refreshedProvider.checkedAt); | 1186 | assert.deepStrictEqual( |
| 2170 | yield* PubSub.publish(changes, refreshedProvider); | 1187 | (yield* readProviderStatusCache(filePath))?.models, |
| 2171 | yield* Fiber.join(persisted); | 1188 | retainedModels, |
| 2172 | const cachedProvider = yield* readProviderStatusCache(filePath); | 1189 | ); |
| 2173 | 1190 | }).pipe( | |
| 2174 | assert.deepStrictEqual( | 1191 | Effect.provide( |
| 2175 | cachedProvider, | 1192 | ProviderRegistryLayer.layer.pipe(Layer.provide(layerInstanceRegistry)), |
| 2176 | withBundledCompatibility({ | 1193 | ), |
| 2177 | ...refreshedProvider, | 1194 | Effect.scoped, |
| 2178 | models: [...initialProvider.models], | ||
| 2179 | }), | ||
| 2180 | ); | 1195 | ); |
| 2181 | }).pipe(Effect.provide(runtimeServices)); | 1196 | } |
| 2182 | }), | 1197 | }).pipe( |
| 1198 | Effect.provide( | ||
| 1199 | ServerConfig.layerTest(process.cwd(), { | ||
| 1200 | prefix: "t3-codex-retired-model-cache-", | ||
| 1201 | }).pipe(Layer.provideMerge(NodeServices.layer)), | ||
| 1202 | ), | ||
| 1203 | ), | ||
| 2183 | ); | 1204 | ); |
| 1205 | }); | ||
| 2184 | 1206 | ||
| 2185 | it.effect( | 1207 | describe("Antigravity model inventories", () => { |
| 2186 | "persists authoritative OpenCode removals without resurrecting them on a failed live refresh", | 1208 | const previousProvider = { |
| 2187 | () => | 1209 | instanceId: ProviderInstanceId.make("antigravity-personal"), |
| 2188 | Effect.gen(function* () { | 1210 | driver: ProviderDriverKind.make("antigravity"), |
| 2189 | const openCodeDriver = ProviderDriverKind.make("opencode"); | 1211 | status: "ready", |
| 2190 | const openCodeInstanceId = ProviderInstanceId.make("opencode"); | 1212 | enabled: true, |
| 2191 | const initialProvider = { | 1213 | installed: true, |
| 2192 | instanceId: openCodeInstanceId, | 1214 | auth: { status: "authenticated" }, |
| 2193 | driver: openCodeDriver, | 1215 | checkedAt: "2026-09-02T00:00:00.000Z", |
| 2194 | status: "ready", | 1216 | version: "0.1.3", |
| 2195 | enabled: true, | 1217 | models: [ |
| 2196 | installed: true, | 1218 | { |
| 2197 | auth: { status: "authenticated" }, | 1219 | slug: "gemini-3.1-pro-high", |
| 2198 | checkedAt: "2026-07-17T00:00:00.000Z", | 1220 | name: "Gemini 3.1 Pro High", |
| 2199 | version: "1.0.0", | 1221 | isCustom: false, |
| 2200 | models: [ | 1222 | capabilities: null, |
| 2201 | { | 1223 | }, |
| 2202 | slug: "github/gpt-5", | 1224 | { |
| 2203 | name: "GPT-5", | 1225 | slug: "gemini-3-flash", |
| 2204 | subProvider: "GitHub", | 1226 | name: "Gemini 3 Flash", |
| 2205 | isCustom: false, | 1227 | isCustom: false, |
| 2206 | capabilities: null, | 1228 | capabilities: null, |
| 2207 | }, | 1229 | }, |
| 2208 | { | 1230 | ], |
| 2209 | slug: "removed-plugin/model", | 1231 | slashCommands: [], |
| 2210 | name: "Removed Plugin Model", | 1232 | skills: [], |
| 2211 | subProvider: "Removed Plugin", | 1233 | } as const satisfies ServerProvider; |
| 2212 | isCustom: false, | 1234 | |
| 2213 | capabilities: null, | 1235 | it("removes unavailable models after a successful refresh", () => { |
| 2214 | }, | 1236 | for (const status of ["ready", "warning"] as const) { |
| 1237 | const refreshedProvider = { | ||
| 1238 | ...previousProvider, | ||
| 1239 | status, | ||
| 1240 | checkedAt: "2026-09-02T00:01:00.000Z", | ||
| 1241 | models: [previousProvider.models[1]], | ||
| 1242 | } satisfies ServerProvider; | ||
| 1243 | const afterRefresh = mergeProviderSnapshot(previousProvider, refreshedProvider); | ||
| 1244 | |||
| 1245 | assert.deepStrictEqual(afterRefresh.models, refreshedProvider.models); | ||
| 1246 | |||
| 1247 | const afterFailure = mergeProviderSnapshot(afterRefresh, { | ||
| 1248 | ...refreshedProvider, | ||
| 1249 | status: "error", | ||
| 1250 | auth: { status: "unknown" }, | ||
| 1251 | models: [], | ||
| 1252 | }); | ||
| 1253 | assert.deepStrictEqual(afterFailure.models, refreshedProvider.models); | ||
| 1254 | } | ||
| 1255 | }); | ||
| 1256 | |||
| 1257 | it("keeps cached models during health checks and temporary failures", () => { | ||
| 1258 | for (const installed of [false, true]) { | ||
| 1259 | const pendingProvider = { | ||
| 1260 | ...previousProvider, | ||
| 1261 | status: "warning", | ||
| 1262 | installed, | ||
| 1263 | auth: { status: "unknown" }, | ||
| 1264 | checkedAt: "2026-09-02T00:01:00.000Z", | ||
| 1265 | version: installed ? previousProvider.version : null, | ||
| 1266 | models: [], | ||
| 1267 | } satisfies ServerProvider; | ||
| 1268 | |||
| 1269 | assert.deepStrictEqual( | ||
| 1270 | mergeProviderSnapshot(previousProvider, pendingProvider).models, | ||
| 1271 | previousProvider.models, | ||
| 1272 | ); | ||
| 1273 | } | ||
| 1274 | |||
| 1275 | for (const authStatus of ["unknown", "authenticated"] as const) { | ||
| 1276 | const failedProvider = { | ||
| 1277 | ...previousProvider, | ||
| 1278 | status: "error", | ||
| 1279 | auth: { status: authStatus }, | ||
| 1280 | checkedAt: "2026-09-02T00:02:00.000Z", | ||
| 1281 | models: [], | ||
| 1282 | } satisfies ServerProvider; | ||
| 1283 | |||
| 1284 | assert.deepStrictEqual( | ||
| 1285 | mergeProviderSnapshot(previousProvider, failedProvider).models, | ||
| 1286 | previousProvider.models, | ||
| 1287 | ); | ||
| 1288 | } | ||
| 1289 | }); | ||
| 1290 | |||
| 1291 | it("clears models after sign-out, disable, uninstall, or an empty successful refresh", () => { | ||
| 1292 | const emptyProvider = { | ||
| 1293 | ...previousProvider, | ||
| 1294 | checkedAt: "2026-09-02T00:01:00.000Z", | ||
| 1295 | models: [], | ||
| 1296 | } satisfies ServerProvider; | ||
| 1297 | const clearedProviders = [ | ||
| 1298 | { ...emptyProvider, status: "warning", auth: { status: "unauthenticated" } }, | ||
| 1299 | { ...emptyProvider, status: "error", auth: { status: "unauthenticated" } }, | ||
| 1300 | { ...emptyProvider, status: "disabled", enabled: false }, | ||
| 1301 | { ...emptyProvider, status: "error", enabled: false }, | ||
| 1302 | { ...emptyProvider, status: "error", installed: false, auth: { status: "unknown" } }, | ||
| 1303 | emptyProvider, | ||
| 1304 | ] satisfies ReadonlyArray<ServerProvider>; | ||
| 1305 | |||
| 1306 | for (const provider of clearedProviders) { | ||
| 1307 | const afterRemoval = mergeProviderSnapshot(previousProvider, provider); | ||
| 1308 | assert.deepStrictEqual(afterRemoval.models, []); | ||
| 1309 | |||
| 1310 | const afterFailure = mergeProviderSnapshot(afterRemoval, { | ||
| 1311 | ...emptyProvider, | ||
| 1312 | status: "error", | ||
| 1313 | auth: { status: "unknown" }, | ||
| 1314 | }); | ||
| 1315 | assert.deepStrictEqual(afterFailure.models, []); | ||
| 1316 | } | ||
| 1317 | }); | ||
| 1318 | }); | ||
| 1319 | |||
| 1320 | describe("Antigravity saved account", () => { | ||
| 1321 | const signedIn = { | ||
| 1322 | instanceId: ProviderInstanceId.make("antigravity-personal"), | ||
| 1323 | driver: ProviderDriverKind.make("antigravity"), | ||
| 1324 | status: "ready", | ||
| 1325 | enabled: true, | ||
| 1326 | installed: true, | ||
| 1327 | auth: { status: "authenticated", type: "oauth-personal", label: "Google account" }, | ||
| 1328 | checkedAt: "2026-09-05T00:00:00.000Z", | ||
| 1329 | version: "agy_acp_server_1.1.1", | ||
| 1330 | models: [ | ||
| 1331 | { | ||
| 1332 | slug: "gemini-3.7-flash-high", | ||
| 1333 | name: "Gemini 3.7 Flash", | ||
| 1334 | isCustom: false, | ||
| 1335 | capabilities: null, | ||
| 1336 | }, | ||
| 1337 | ], | ||
| 1338 | slashCommands: [{ name: "plan" }], | ||
| 1339 | skills: [], | ||
| 1340 | } as const satisfies ServerProvider; | ||
| 1341 | const uncheckedMessage = | ||
| 1342 | "Antigravity is installed. Google account access is not checked yet."; | ||
| 1343 | const restartProbe = { | ||
| 1344 | ...signedIn, | ||
| 1345 | status: "warning", | ||
| 1346 | auth: { status: "unknown" }, | ||
| 1347 | checkedAt: "2026-09-05T00:01:00.000Z", | ||
| 1348 | message: uncheckedMessage, | ||
| 1349 | models: [], | ||
| 1350 | } as const satisfies ServerProvider; | ||
| 1351 | |||
| 1352 | it("keeps the saved Google account through restart health checks", () => { | ||
| 1353 | const merged = mergeProviderSnapshot(signedIn, restartProbe); | ||
| 1354 | const { message: _uncheckedMessage, ...probeWithoutMessage } = restartProbe; | ||
| 1355 | assert.deepStrictEqual(merged, { | ||
| 1356 | ...probeWithoutMessage, | ||
| 1357 | status: "ready", | ||
| 1358 | auth: signedIn.auth, | ||
| 1359 | models: signedIn.models, | ||
| 1360 | }); | ||
| 1361 | assert.equal("message" in merged, false); | ||
| 1362 | // The next periodic probe reads the merged snapshot as its previous state. | ||
| 1363 | assert.deepStrictEqual(mergeProviderSnapshot(merged, restartProbe), merged); | ||
| 1364 | }); | ||
| 1365 | |||
| 1366 | it("carries the account through the boot probe and a failed probe without hiding them", () => { | ||
| 1367 | const booting = { | ||
| 1368 | ...restartProbe, | ||
| 1369 | installed: false, | ||
| 1370 | version: null, | ||
| 1371 | message: "Checking Antigravity availability.", | ||
| 1372 | } satisfies ServerProvider; | ||
| 1373 | assert.deepStrictEqual(mergeProviderSnapshot(signedIn, booting), { | ||
| 1374 | ...booting, | ||
| 1375 | auth: signedIn.auth, | ||
| 1376 | models: signedIn.models, | ||
| 1377 | }); | ||
| 1378 | |||
| 1379 | const failed = { | ||
| 1380 | ...restartProbe, | ||
| 1381 | status: "error", | ||
| 1382 | message: "Antigravity did not respond to its local health check within 90 seconds.", | ||
| 1383 | } satisfies ServerProvider; | ||
| 1384 | assert.deepStrictEqual(mergeProviderSnapshot(signedIn, failed), { | ||
| 1385 | ...failed, | ||
| 1386 | auth: signedIn.auth, | ||
| 1387 | models: signedIn.models, | ||
| 1388 | }); | ||
| 1389 | }); | ||
| 1390 | |||
| 1391 | it("does not invent an account after sign-out, disable, uninstall, or for other providers", () => { | ||
| 1392 | const untouched = [ | ||
| 1393 | { ...restartProbe, auth: { status: "unauthenticated" } }, | ||
| 1394 | { ...restartProbe, status: "disabled", enabled: false }, | ||
| 1395 | { ...restartProbe, status: "error", installed: false }, | ||
| 1396 | { ...restartProbe, driver: ProviderDriverKind.make("codex") }, | ||
| 1397 | // The instance was rebuilt with another sign-in method. | ||
| 1398 | { ...restartProbe, auth: { status: "unknown", type: "gemini-api-key" } }, | ||
| 1399 | ] satisfies ReadonlyArray<ServerProvider>; | ||
| 1400 | for (const next of untouched) { | ||
| 1401 | const merged = mergeProviderSnapshot(signedIn, next); | ||
| 1402 | assert.deepStrictEqual(merged.auth, next.auth); | ||
| 1403 | assert.equal(merged.status, next.status); | ||
| 1404 | assert.equal(merged.message, next.message); | ||
| 1405 | } | ||
| 1406 | assert.deepStrictEqual( | ||
| 1407 | mergeProviderSnapshot({ ...signedIn, auth: { status: "unknown" } }, restartProbe).auth, | ||
| 1408 | { status: "unknown" }, | ||
| 1409 | ); | ||
| 1410 | assert.equal( | ||
| 1411 | mergeProviderSnapshot( | ||
| 1412 | { ...signedIn, driver: ProviderDriverKind.make("codex") }, | ||
| 1413 | restartProbe, | ||
| 1414 | ).auth.status, | ||
| 1415 | "unknown", | ||
| 1416 | ); | ||
| 1417 | assert.deepStrictEqual( | ||
| 1418 | mergeProviderSnapshot(signedIn, { | ||
| 1419 | ...restartProbe, | ||
| 1420 | auth: { status: "unknown", type: "oauth-personal" }, | ||
| 1421 | }).auth, | ||
| 1422 | signedIn.auth, | ||
| 1423 | ); | ||
| 1424 | }); | ||
| 1425 | }); | ||
| 1426 | |||
| 1427 | it("fills missing capabilities from the previous provider snapshot", () => { | ||
| 1428 | const previousProvider = { | ||
| 1429 | instanceId: ProviderInstanceId.make("cursor"), | ||
| 1430 | driver: ProviderDriverKind.make("cursor"), | ||
| 1431 | status: "ready", | ||
| 1432 | enabled: true, | ||
| 1433 | installed: true, | ||
| 1434 | auth: { status: "authenticated" }, | ||
| 1435 | checkedAt: "2026-04-14T00:00:00.000Z", | ||
| 1436 | version: "2026.04.09-f2b0fcd", | ||
| 1437 | models: [ | ||
| 1438 | { | ||
| 1439 | slug: "claude-opus-4-6", | ||
| 1440 | name: "Opus 4.6", | ||
| 1441 | isCustom: false, | ||
| 1442 | capabilities: createModelCapabilities({ | ||
| 1443 | optionDescriptors: [ | ||
| 1444 | selectDescriptor("reasoning", "Reasoning", [ | ||
| 1445 | { id: "high", label: "High", isDefault: true }, | ||
| 1446 | ]), | ||
| 1447 | booleanDescriptor("fastMode", "Fast Mode"), | ||
| 1448 | booleanDescriptor("thinking", "Thinking"), | ||
| 2215 | ], | 1449 | ], |
| 2216 | slashCommands: [], | 1450 | }), |
| 2217 | skills: [], | 1451 | }, |
| 2218 | } as const satisfies ServerProvider; | 1452 | ], |
| 2219 | const authoritativeProvider = { | 1453 | slashCommands: [], |
| 2220 | ...initialProvider, | 1454 | skills: [], |
| 2221 | checkedAt: "2026-07-17T00:01:00.000Z", | 1455 | } as const satisfies ServerProvider; |
| 2222 | models: [initialProvider.models[0]!], | 1456 | const refreshedProvider = { |
| 2223 | } satisfies ServerProvider; | 1457 | ...previousProvider, |
| 2224 | const failedProvider = { | 1458 | checkedAt: "2026-04-14T00:01:00.000Z", |
| 2225 | ...authoritativeProvider, | 1459 | models: [ |
| 2226 | status: "error", | 1460 | { |
| 2227 | auth: { status: "unknown" }, | 1461 | slug: "claude-opus-4-6", |
| 2228 | checkedAt: "2026-07-17T00:02:00.000Z", | 1462 | name: "Opus 4.6", |
| 2229 | models: [], | 1463 | isCustom: false, |
| 2230 | message: "Failed to refresh OpenCode models.", | 1464 | capabilities: createModelCapabilities({ |
| 2231 | } satisfies ServerProvider; | 1465 | optionDescriptors: [], |
| 2232 | const changes = yield* PubSub.unbounded<ServerProvider>(); | 1466 | }), |
| 2233 | const instance = { | 1467 | }, |
| 2234 | instanceId: openCodeInstanceId, | 1468 | ], |
| 2235 | driverKind: openCodeDriver, | 1469 | } satisfies ServerProvider; |
| 2236 | continuationIdentity: { | 1470 | |
| 2237 | driverKind: openCodeDriver, | 1471 | assert.deepStrictEqual(mergeProviderSnapshot(previousProvider, refreshedProvider).models, [ |
| 2238 | continuationKey: "opencode:instance:opencode", | 1472 | ...previousProvider.models, |
| 2239 | }, | 1473 | ]); |
| 2240 | displayName: undefined, | 1474 | }); |
| 2241 | enabled: true, | 1475 | |
| 2242 | snapshot: { | 1476 | it.effect("does not run provider probes during layer construction", () => |
| 2243 | resolveMaintenance: () => | 1477 | Effect.gen(function* () { |
| 2244 | Effect.succeed( | 1478 | const codexDriver = ProviderDriverKind.make("codex"); |
| 2245 | makeManualOnlyProviderMaintenanceCapabilities({ | 1479 | const codexInstanceId = ProviderInstanceId.make("codex"); |
| 2246 | provider: openCodeDriver, | 1480 | const initialProvider = { |
| 2247 | packageName: null, | 1481 | instanceId: codexInstanceId, |
| 2248 | }), | 1482 | driver: codexDriver, |
| 2249 | ), | 1483 | status: "warning", |
| 2250 | getSnapshot: Effect.succeed(initialProvider), | 1484 | enabled: true, |
| 2251 | refresh: Effect.succeed(authoritativeProvider), | 1485 | installed: false, |
| 2252 | streamChanges: Stream.fromPubSub(changes), | 1486 | auth: { status: "unknown" }, |
| 2253 | applyUsageLimits: () => Effect.void, | 1487 | checkedAt: "2026-06-10T00:00:00.000Z", |
| 2254 | }, | 1488 | version: null, |
| 2255 | orchestrationAdapter: {} as ProviderInstance["orchestrationAdapter"], | 1489 | message: "Checking Codex provider status.", |
| 2256 | textGeneration: {} as ProviderInstance["textGeneration"], | 1490 | models: [], |
| 2257 | } satisfies ProviderInstance; | 1491 | slashCommands: [], |
| 2258 | const instanceRegistryLayer = Layer.succeed( | 1492 | skills: [], |
| 2259 | ProviderInstanceRegistry.ProviderInstanceRegistry, | 1493 | } as const satisfies ServerProvider; |
| 2260 | { | 1494 | const refreshCalls = yield* Ref.make(0); |
| 2261 | getInstance: (instanceId) => | 1495 | const instance = { |
| 2262 | Effect.succeed(instanceId === openCodeInstanceId ? instance : undefined), | 1496 | instanceId: codexInstanceId, |
| 2263 | listInstances: Effect.succeed([instance]), | 1497 | driverKind: codexDriver, |
| 2264 | listUnavailable: Effect.succeed([]), | 1498 | continuationIdentity: { |
| 2265 | streamChanges: Stream.empty, | 1499 | driverKind: codexDriver, |
| 2266 | subscribeChanges: Effect.flatMap(PubSub.unbounded<void>(), (pubsub) => | 1500 | continuationKey: "codex:instance:codex", |
| 2267 | PubSub.subscribe(pubsub), | 1501 | }, |
| 2268 | ), | 1502 | displayName: undefined, |
| 2269 | }, | 1503 | enabled: true, |
| 2270 | ); | 1504 | snapshot: { |
| 2271 | const scope = yield* Scope.make(); | 1505 | resolveMaintenance: () => |
| 2272 | yield* Effect.addFinalizer(() => Scope.close(scope, Exit.void)); | 1506 | Effect.succeed( |
| 2273 | const runtimeServices = yield* Layer.build( | 1507 | makeManualOnlyProviderMaintenanceCapabilities({ |
| 2274 | ProviderRegistryLive.pipe( | 1508 | provider: codexDriver, |
| 2275 | Layer.provideMerge(instanceRegistryLayer), | 1509 | packageName: null, |
| 2276 | Layer.provideMerge( | 1510 | }), |
| 2277 | ServerConfig.layerTest(process.cwd(), { | ||
| 2278 | prefix: "t3-provider-registry-opencode-authoritative-persist-", | ||
| 2279 | }), | ||
| 2280 | ), | ||
| 2281 | Layer.provideMerge(NodeServices.layer), | ||
| 2282 | ), | 1511 | ), |
| 2283 | ).pipe(Scope.provide(scope)); | 1512 | getSnapshot: Effect.succeed(initialProvider), |
| 1513 | refresh: Ref.update(refreshCalls, (count) => count + 1).pipe( | ||
| 1514 | Effect.andThen(Effect.never), | ||
| 1515 | ), | ||
| 1516 | streamChanges: Stream.empty, | ||
| 1517 | applyUsageLimits: () => Effect.void, | ||
| 1518 | }, | ||
| 1519 | orchestrationAdapter: {} as ProviderInstance["orchestrationAdapter"], | ||
| 1520 | textGeneration: {} as ProviderInstance["textGeneration"], | ||
| 1521 | } satisfies ProviderInstance; | ||
| 1522 | const layerInstanceRegistry = Layer.succeed( | ||
| 1523 | ProviderInstanceRegistry.ProviderInstanceRegistry, | ||
| 1524 | { | ||
| 1525 | getInstance: (instanceId) => | ||
| 1526 | Effect.succeed(instanceId === codexInstanceId ? instance : undefined), | ||
| 1527 | listInstances: Effect.succeed([instance]), | ||
| 1528 | listUnavailable: Effect.succeed([]), | ||
| 1529 | streamChanges: Stream.empty, | ||
| 1530 | subscribeChanges: Effect.flatMap(PubSub.unbounded<void>(), PubSub.subscribe), | ||
| 1531 | }, | ||
| 1532 | ); | ||
| 1533 | const scope = yield* Scope.make(); | ||
| 1534 | yield* Effect.addFinalizer(() => Scope.close(scope, Exit.void)); | ||
| 1535 | const runtimeServices = yield* Layer.build( | ||
| 1536 | ProviderRegistryLayer.layer.pipe( | ||
| 1537 | Layer.provideMerge(layerInstanceRegistry), | ||
| 1538 | Layer.provideMerge( | ||
| 1539 | ServerConfig.layerTest(process.cwd(), { | ||
| 1540 | prefix: "t3-provider-registry-background-refresh-", | ||
| 1541 | }), | ||
| 1542 | ), | ||
| 1543 | Layer.provideMerge(NodeServices.layer), | ||
| 1544 | ), | ||
| 1545 | ).pipe(Scope.provide(scope)); | ||
| 1546 | yield* Effect.gen(function* () { | ||
| 1547 | const registry = yield* ProviderRegistry.ProviderRegistry; | ||
| 1548 | assert.deepStrictEqual(yield* registry.getProviders, [initialProvider]); | ||
| 1549 | assert.strictEqual(yield* Ref.get(refreshCalls), 0); | ||
| 1550 | }).pipe(Effect.provide(runtimeServices)); | ||
| 1551 | }), | ||
| 1552 | ); | ||
| 2284 | 1553 | ||
| 2285 | yield* Effect.gen(function* () { | 1554 | it.effect("deduplicates cwd probes and clears snapshots when an instance rebuilds", () => |
| 2286 | const registry = yield* ProviderRegistry.ProviderRegistry; | 1555 | Effect.gen(function* () { |
| 2287 | const config = yield* ServerConfig.ServerConfig; | 1556 | const driver = ProviderDriverKind.make("codex"); |
| 2288 | const filePath = yield* resolveProviderStatusCachePath({ | 1557 | const instanceId = ProviderInstanceId.make("codex"); |
| 2289 | cacheDir: config.providerStatusCacheDir, | 1558 | const machineProvider = { |
| 2290 | instanceId: openCodeInstanceId, | 1559 | instanceId, |
| 2291 | }); | 1560 | driver, |
| 2292 | 1561 | status: "ready", | |
| 2293 | const authoritativePersisted = yield* awaitPersistedProvider( | 1562 | enabled: true, |
| 2294 | registry, | 1563 | installed: true, |
| 2295 | authoritativeProvider.checkedAt, | 1564 | auth: { status: "authenticated" }, |
| 2296 | ); | 1565 | checkedAt: "2026-06-10T00:00:00.000Z", |
| 2297 | yield* PubSub.publish(changes, authoritativeProvider); | 1566 | version: "1.0.0", |
| 2298 | yield* Fiber.join(authoritativePersisted); | 1567 | models: [], |
| 2299 | let cachedProvider = yield* readProviderStatusCache(filePath); | 1568 | slashCommands: [{ name: "global" }], |
| 1569 | skills: [{ name: "global", path: "/global/SKILL.md", enabled: true }], | ||
| 1570 | } as const satisfies ServerProvider; | ||
| 1571 | const scopedProvider = { | ||
| 1572 | ...machineProvider, | ||
| 1573 | checkedAt: "2026-06-10T00:01:00.000Z", | ||
| 1574 | slashCommands: [{ name: "project" }], | ||
| 1575 | skills: [{ name: "project", path: "/workspace/SKILL.md", enabled: true }], | ||
| 1576 | } as const satisfies ServerProvider; | ||
| 1577 | const pendingScopedProvider = { | ||
| 1578 | ...scopedProvider, | ||
| 1579 | status: "error", | ||
| 1580 | installed: false, | ||
| 1581 | slashCommands: [], | ||
| 1582 | } as const satisfies ServerProvider; | ||
| 1583 | const snapshotCalls = yield* Ref.make(0); | ||
| 1584 | const scopedResult = yield* Ref.make<ProviderWorkspaceSnapshot>({ | ||
| 1585 | ...scopedProvider, | ||
| 1586 | status: "error", | ||
| 1587 | slashCommands: [COMPACT_SLASH_COMMAND], | ||
| 1588 | slashCommandsPending: true, | ||
| 1589 | }); | ||
| 1590 | const cacheInvalidations = yield* Ref.make(0); | ||
| 1591 | const scanGate = yield* Ref.make<{ | ||
| 1592 | readonly started: Deferred.Deferred<void>; | ||
| 1593 | readonly release: Deferred.Deferred<void>; | ||
| 1594 | } | null>(null); | ||
| 1595 | const returnPendingSnapshot = yield* Ref.make(true); | ||
| 1596 | const probeStarted = yield* Deferred.make<void>(); | ||
| 1597 | const releaseProbe = yield* Deferred.make<void>(); | ||
| 1598 | const makeInstance = ( | ||
| 1599 | provider: ServerProvider, | ||
| 1600 | snapshotForCwd: NonNullable<ProviderInstance["snapshotForCwd"]>, | ||
| 1601 | ): ProviderInstance => ({ | ||
| 1602 | instanceId, | ||
| 1603 | driverKind: driver, | ||
| 1604 | continuationIdentity: { | ||
| 1605 | driverKind: driver, | ||
| 1606 | continuationKey: "codex:instance:codex", | ||
| 1607 | }, | ||
| 1608 | displayName: undefined, | ||
| 1609 | enabled: true, | ||
| 1610 | snapshot: { | ||
| 1611 | resolveMaintenance: () => | ||
| 1612 | Effect.succeed( | ||
| 1613 | makeManualOnlyProviderMaintenanceCapabilities({ | ||
| 1614 | provider: driver, | ||
| 1615 | packageName: null, | ||
| 1616 | }), | ||
| 1617 | ), | ||
| 1618 | getSnapshot: Effect.succeed(provider), | ||
| 1619 | refresh: Effect.succeed(provider), | ||
| 1620 | streamChanges: Stream.empty, | ||
| 1621 | applyUsageLimits: () => Effect.void, | ||
| 1622 | }, | ||
| 1623 | snapshotForCwd, | ||
| 1624 | invalidateCaches: Ref.update(cacheInvalidations, (count) => count + 1), | ||
| 1625 | orchestrationAdapter: {} as ProviderInstance["orchestrationAdapter"], | ||
| 1626 | textGeneration: {} as ProviderInstance["textGeneration"], | ||
| 1627 | }); | ||
| 1628 | const firstInstance = makeInstance(machineProvider, () => | ||
| 1629 | Effect.gen(function* () { | ||
| 1630 | yield* Ref.update(snapshotCalls, (count) => count + 1); | ||
| 1631 | if (yield* Ref.get(returnPendingSnapshot)) return pendingScopedProvider; | ||
| 1632 | yield* Deferred.succeed(probeStarted, undefined); | ||
| 1633 | yield* Deferred.await(releaseProbe); | ||
| 1634 | const result = yield* Ref.get(scopedResult); | ||
| 1635 | const gate = yield* Ref.getAndSet(scanGate, null); | ||
| 1636 | if (gate) { | ||
| 1637 | yield* Deferred.succeed(gate.started, undefined); | ||
| 1638 | yield* Deferred.await(gate.release); | ||
| 1639 | } | ||
| 1640 | return result; | ||
| 1641 | }), | ||
| 1642 | ); | ||
| 1643 | const rebuiltProvider = { | ||
| 1644 | ...machineProvider, | ||
| 1645 | checkedAt: "2026-06-10T00:02:00.000Z", | ||
| 1646 | status: "warning", | ||
| 1647 | installed: false, | ||
| 1648 | auth: { status: "unknown" }, | ||
| 1649 | } satisfies ServerProvider; | ||
| 1650 | const rebuiltInstance = makeInstance(rebuiltProvider, () => | ||
| 1651 | Ref.update(snapshotCalls, (count) => count + 1).pipe(Effect.as(scopedProvider)), | ||
| 1652 | ); | ||
| 1653 | const registryChanges = yield* PubSub.unbounded<void>(); | ||
| 1654 | const instancesRef = yield* Ref.make<ReadonlyArray<ProviderInstance>>([firstInstance]); | ||
| 1655 | const layerInstanceRegistry = Layer.succeed( | ||
| 1656 | ProviderInstanceRegistry.ProviderInstanceRegistry, | ||
| 1657 | { | ||
| 1658 | getInstance: (requestedId) => | ||
| 1659 | Ref.get(instancesRef).pipe( | ||
| 1660 | Effect.map((instances) => | ||
| 1661 | instances.find((instance) => instance.instanceId === requestedId), | ||
| 1662 | ), | ||
| 1663 | ), | ||
| 1664 | listInstances: Ref.get(instancesRef), | ||
| 1665 | listUnavailable: Effect.succeed([]), | ||
| 1666 | streamChanges: Stream.fromPubSub(registryChanges), | ||
| 1667 | subscribeChanges: PubSub.subscribe(registryChanges), | ||
| 1668 | }, | ||
| 1669 | ); | ||
| 1670 | const scope = yield* Scope.make(); | ||
| 1671 | yield* Effect.addFinalizer(() => Scope.close(scope, Exit.void)); | ||
| 1672 | const runtimeServices = yield* Layer.build( | ||
| 1673 | ProviderRegistryLayer.layer.pipe( | ||
| 1674 | Layer.provideMerge(layerInstanceRegistry), | ||
| 1675 | Layer.provideMerge( | ||
| 1676 | ServerConfig.layerTest(process.cwd(), { | ||
| 1677 | prefix: "t3-provider-registry-workspace-snapshot-", | ||
| 1678 | }), | ||
| 1679 | ), | ||
| 1680 | Layer.provideMerge(NodeServices.layer), | ||
| 1681 | ), | ||
| 1682 | ).pipe(Scope.provide(scope)); | ||
| 1683 | |||
| 1684 | yield* Effect.gen(function* () { | ||
| 1685 | const registry = yield* ProviderRegistry.ProviderRegistry; | ||
| 1686 | yield* registry.refreshWorkspaceSnapshot({ instanceId, cwd: "/workspace" }); | ||
| 1687 | assert.strictEqual((yield* registry.getProviders)[0]?.workspaceSnapshots, undefined); | ||
| 1688 | yield* Ref.set(returnPendingSnapshot, false); | ||
| 1689 | const workspaceUpdate = yield* registry.streamChanges.pipe( | ||
| 1690 | Stream.runHead, | ||
| 1691 | Effect.forkChild, | ||
| 1692 | ); | ||
| 1693 | yield* Effect.yieldNow; | ||
| 1694 | const firstRefresh = yield* registry | ||
| 1695 | .refreshWorkspaceSnapshot({ instanceId, cwd: "/workspace" }) | ||
| 1696 | .pipe(Effect.forkChild); | ||
| 1697 | yield* Deferred.await(probeStarted); | ||
| 1698 | const duplicateRefresh = yield* registry | ||
| 1699 | .refreshWorkspaceSnapshot({ instanceId, cwd: "/workspace" }) | ||
| 1700 | .pipe(Effect.forkChild); | ||
| 1701 | yield* Effect.yieldNow; | ||
| 1702 | assert.strictEqual(yield* Ref.get(snapshotCalls), 2); | ||
| 1703 | yield* Deferred.succeed(releaseProbe, undefined); | ||
| 1704 | yield* Fiber.join(firstRefresh); | ||
| 1705 | yield* Fiber.join(duplicateRefresh); | ||
| 1706 | const published = yield* Fiber.join(workspaceUpdate); | ||
| 1707 | assert.strictEqual(published._tag, "Some"); | ||
| 1708 | const providers = yield* registry.getProviders; | ||
| 1709 | assert.deepStrictEqual(providers[0]?.skills, machineProvider.skills); | ||
| 1710 | assert.deepStrictEqual( | ||
| 1711 | providers[0]?.workspaceSnapshots?.[0]?.skills, | ||
| 1712 | scopedProvider.skills, | ||
| 1713 | ); | ||
| 1714 | assert.deepStrictEqual(providers[0]?.workspaceSnapshots?.[0]?.slashCommands, [ | ||
| 1715 | COMPACT_SLASH_COMMAND, | ||
| 1716 | ]); | ||
| 1717 | assert.strictEqual(providers[0]?.workspaceSnapshots?.[0]?.slashCommandsPending, true); | ||
| 1718 | yield* Ref.set(scopedResult, { | ||
| 1719 | ...scopedProvider, | ||
| 1720 | status: "error", | ||
| 1721 | slashCommandsPending: false, | ||
| 1722 | }); | ||
| 1723 | yield* registry.refreshWorkspaceSnapshot({ instanceId, cwd: "/workspace" }); | ||
| 1724 | assert.strictEqual(yield* Ref.get(snapshotCalls), 3); | ||
| 1725 | assert.deepStrictEqual( | ||
| 1726 | (yield* registry.getProviders)[0]?.workspaceSnapshots?.[0]?.slashCommands, | ||
| 1727 | scopedProvider.slashCommands, | ||
| 1728 | ); | ||
| 1729 | assert.strictEqual( | ||
| 1730 | (yield* registry.getProviders)[0]?.workspaceSnapshots?.[0]?.slashCommandsPending, | ||
| 1731 | undefined, | ||
| 1732 | ); | ||
| 1733 | yield* registry.refreshWorkspaceSnapshot({ instanceId, cwd: "/workspace" }); | ||
| 1734 | assert.strictEqual(yield* Ref.get(snapshotCalls), 3); | ||
| 1735 | const newSkills = [ | ||
| 1736 | ...scopedProvider.skills, | ||
| 1737 | { name: "added", path: "/workspace/added/SKILL.md", enabled: true }, | ||
| 1738 | ]; | ||
| 1739 | yield* Ref.set(scopedResult, { ...scopedProvider, skills: newSkills }); | ||
| 1740 | yield* registry.refreshWorkspaceSnapshot({ | ||
| 1741 | instanceId, | ||
| 1742 | cwd: "/workspace", | ||
| 1743 | fresh: true, | ||
| 1744 | }); | ||
| 1745 | assert.strictEqual(yield* Ref.get(snapshotCalls), 4); | ||
| 1746 | assert.strictEqual(yield* Ref.get(cacheInvalidations), 1); | ||
| 1747 | assert.deepStrictEqual( | ||
| 1748 | (yield* registry.getProviders)[0]?.workspaceSnapshots?.map((s) => s.skills), | ||
| 1749 | [newSkills], | ||
| 1750 | ); | ||
| 2300 | 1751 | ||
| 2301 | assert.deepStrictEqual(cachedProvider?.models, [authoritativeProvider.models[0]!]); | 1752 | // A slow fresh scan that read older files must not overwrite a |
| 1753 | // newer scan that finished first. | ||
| 1754 | const slowStarted = yield* Deferred.make<void>(); | ||
| 1755 | const releaseSlow = yield* Deferred.make<void>(); | ||
| 1756 | yield* Ref.set(scanGate, { started: slowStarted, release: releaseSlow }); | ||
| 1757 | yield* Ref.set(scopedResult, scopedProvider); | ||
| 1758 | const slowScan = yield* registry | ||
| 1759 | .refreshWorkspaceSnapshot({ instanceId, cwd: "/workspace", fresh: true }) | ||
| 1760 | .pipe(Effect.forkChild); | ||
| 1761 | yield* Deferred.await(slowStarted); | ||
| 1762 | const latestSkills = [ | ||
| 1763 | ...newSkills, | ||
| 1764 | { name: "latest", path: "/workspace/latest/SKILL.md", enabled: true }, | ||
| 1765 | ]; | ||
| 1766 | yield* Ref.set(scopedResult, { ...scopedProvider, skills: latestSkills }); | ||
| 1767 | yield* registry.refreshWorkspaceSnapshot({ | ||
| 1768 | instanceId, | ||
| 1769 | cwd: "/workspace", | ||
| 1770 | fresh: true, | ||
| 1771 | }); | ||
| 1772 | yield* Deferred.succeed(releaseSlow, undefined); | ||
| 1773 | yield* Fiber.join(slowScan); | ||
| 1774 | assert.deepStrictEqual( | ||
| 1775 | (yield* registry.getProviders)[0]?.workspaceSnapshots?.map((s) => s.skills), | ||
| 1776 | [latestSkills], | ||
| 1777 | ); | ||
| 2302 | 1778 | ||
| 2303 | const failedPersisted = yield* awaitPersistedProvider( | 1779 | yield* Ref.set(instancesRef, [rebuiltInstance]); |
| 2304 | registry, | 1780 | yield* PubSub.publish(registryChanges, undefined); |
| 2305 | failedProvider.checkedAt, | 1781 | let rebuilt = yield* registry.getProviders; |
| 2306 | ); | 1782 | for ( |
| 2307 | yield* PubSub.publish(changes, failedProvider); | 1783 | let attempt = 0; |
| 2308 | yield* Fiber.join(failedPersisted); | 1784 | attempt < 50 && rebuilt[0]?.checkedAt !== rebuiltProvider.checkedAt; |
| 2309 | cachedProvider = yield* readProviderStatusCache(filePath); | 1785 | attempt += 1 |
| 2310 | 1786 | ) { | |
| 2311 | assert.deepStrictEqual(cachedProvider?.models, [authoritativeProvider.models[0]!]); | 1787 | yield* Effect.yieldNow; |
| 2312 | assert.deepStrictEqual((yield* registry.getProviders)[0]?.models, [ | 1788 | rebuilt = yield* registry.getProviders; |
| 2313 | authoritativeProvider.models[0]!, | 1789 | } |
| 2314 | ]); | 1790 | assert.strictEqual(rebuilt[0]?.checkedAt, rebuiltProvider.checkedAt); |
| 2315 | }).pipe(Effect.provide(runtimeServices)); | 1791 | assert.strictEqual(rebuilt[0]?.workspaceSnapshots, undefined); |
| 2316 | }), | 1792 | }).pipe(Effect.provide(runtimeServices)); |
| 2317 | ); | 1793 | }), |
| 1794 | ); | ||
| 2318 | 1795 | ||
| 2319 | it.effect("returns the cached provider list when a manual refresh fails", () => | 1796 | it.effect("refreshes OpenCode catalogs and preserves other providers", () => |
| 2320 | Effect.gen(function* () { | 1797 | Effect.gen(function* () { |
| 2321 | const codexDriver = ProviderDriverKind.make("codex"); | 1798 | const codexDriver = ProviderDriverKind.make("codex"); |
| 2322 | const codexInstanceId = ProviderInstanceId.make("codex"); | 1799 | const openCodeDriver = ProviderDriverKind.make("opencode"); |
| 2323 | const cachedProvider = { | 1800 | const codexInstanceId = ProviderInstanceId.make("codex"); |
| 2324 | instanceId: codexInstanceId, | 1801 | const openCodeInstanceId = ProviderInstanceId.make("opencode"); |
| 2325 | driver: codexDriver, | 1802 | const codexRefreshCalls = yield* Ref.make(0); |
| 2326 | status: "ready", | 1803 | const openCodeRefreshCalls = yield* Ref.make(0); |
| 2327 | enabled: true, | 1804 | const codexProvider = { |
| 2328 | installed: true, | 1805 | instanceId: codexInstanceId, |
| 2329 | auth: { status: "authenticated" }, | 1806 | driver: codexDriver, |
| 2330 | checkedAt: "2026-04-29T10:00:00.000Z", | 1807 | status: "ready", |
| 2331 | version: "1.0.0", | 1808 | enabled: true, |
| 2332 | models: [], | 1809 | installed: true, |
| 2333 | slashCommands: [], | 1810 | auth: { status: "authenticated" }, |
| 2334 | skills: [], | 1811 | checkedAt: "2026-06-10T00:00:00.000Z", |
| 2335 | } as const satisfies ServerProvider; | 1812 | version: "1.0.0", |
| 2336 | const instance = { | 1813 | models: [], |
| 1814 | slashCommands: [], | ||
| 1815 | skills: [], | ||
| 1816 | } as const satisfies ServerProvider; | ||
| 1817 | const failedOpenCodeProvider = { | ||
| 1818 | instanceId: openCodeInstanceId, | ||
| 1819 | driver: openCodeDriver, | ||
| 1820 | status: "error", | ||
| 1821 | enabled: true, | ||
| 1822 | installed: true, | ||
| 1823 | auth: { status: "unknown" }, | ||
| 1824 | checkedAt: "2026-06-10T00:00:00.000Z", | ||
| 1825 | version: "1.0.0", | ||
| 1826 | message: "Failed to refresh OpenCode models.", | ||
| 1827 | models: [], | ||
| 1828 | slashCommands: [], | ||
| 1829 | skills: [], | ||
| 1830 | } as const satisfies ServerProvider; | ||
| 1831 | const recoveredOpenCodeProvider = { | ||
| 1832 | ...failedOpenCodeProvider, | ||
| 1833 | status: "ready", | ||
| 1834 | auth: { status: "authenticated" }, | ||
| 1835 | checkedAt: "2026-06-10T00:01:00.000Z", | ||
| 1836 | message: "One upstream provider connected through OpenCode.", | ||
| 1837 | models: [ | ||
| 1838 | { | ||
| 1839 | slug: "github/gpt-5", | ||
| 1840 | name: "GPT-5", | ||
| 1841 | subProvider: "GitHub", | ||
| 1842 | isCustom: false, | ||
| 1843 | capabilities: null, | ||
| 1844 | }, | ||
| 1845 | ], | ||
| 1846 | } as const satisfies ServerProvider; | ||
| 1847 | const changedCatalogProvider = { | ||
| 1848 | ...recoveredOpenCodeProvider, | ||
| 1849 | checkedAt: "2026-06-10T00:02:00.000Z", | ||
| 1850 | models: [ | ||
| 1851 | { | ||
| 1852 | slug: "anthropic/claude-sonnet-4", | ||
| 1853 | name: "Claude Sonnet 4", | ||
| 1854 | subProvider: "Anthropic", | ||
| 1855 | isCustom: false, | ||
| 1856 | capabilities: null, | ||
| 1857 | }, | ||
| 1858 | ], | ||
| 1859 | } as const satisfies ServerProvider; | ||
| 1860 | const catalogSnapshot = yield* Ref.make<ServerProvider>(recoveredOpenCodeProvider); | ||
| 1861 | const instances = [ | ||
| 1862 | { | ||
| 2337 | instanceId: codexInstanceId, | 1863 | instanceId: codexInstanceId, |
| 2338 | driverKind: codexDriver, | 1864 | driverKind: codexDriver, |
| 2339 | continuationIdentity: { | 1865 | continuationIdentity: { |
| ... | @@ -2350,96 +1876,339 @@ it.layer(Layer.mergeAll(TestNodeServices, ServerSettingsModule.layerTest(), Test | ... | @@ -2350,96 +1876,339 @@ it.layer(Layer.mergeAll(TestNodeServices, ServerSettingsModule.layerTest(), Test |
| 2350 | packageName: null, | 1876 | packageName: null, |
| 2351 | }), | 1877 | }), |
| 2352 | ), | 1878 | ), |
| 2353 | getSnapshot: Effect.succeed(cachedProvider), | 1879 | getSnapshot: Effect.succeed(codexProvider), |
| 2354 | refresh: Effect.die(new Error("simulated refresh failure")), | 1880 | refresh: Ref.update(codexRefreshCalls, (count) => count + 1).pipe( |
| 1881 | Effect.as(codexProvider), | ||
| 1882 | ), | ||
| 2355 | streamChanges: Stream.empty, | 1883 | streamChanges: Stream.empty, |
| 2356 | applyUsageLimits: () => Effect.void, | 1884 | applyUsageLimits: () => Effect.void, |
| 2357 | }, | 1885 | }, |
| 2358 | orchestrationAdapter: {} as ProviderInstance["orchestrationAdapter"], | 1886 | orchestrationAdapter: {} as ProviderInstance["orchestrationAdapter"], |
| 2359 | textGeneration: {} as ProviderInstance["textGeneration"], | 1887 | textGeneration: {} as ProviderInstance["textGeneration"], |
| 2360 | } satisfies ProviderInstance; | 1888 | }, |
| 2361 | const instanceRegistryLayer = Layer.succeed( | 1889 | { |
| 2362 | ProviderInstanceRegistry.ProviderInstanceRegistry, | 1890 | instanceId: openCodeInstanceId, |
| 2363 | { | 1891 | driverKind: openCodeDriver, |
| 2364 | getInstance: (instanceId) => | 1892 | continuationIdentity: { |
| 2365 | Effect.succeed(instanceId === codexInstanceId ? instance : undefined), | 1893 | driverKind: openCodeDriver, |
| 2366 | listInstances: Effect.succeed([instance]), | 1894 | continuationKey: "opencode:instance:opencode", |
| 2367 | listUnavailable: Effect.succeed([]), | 1895 | }, |
| 2368 | streamChanges: Stream.empty, | 1896 | displayName: undefined, |
| 2369 | subscribeChanges: Effect.flatMap(PubSub.unbounded<void>(), (pubsub) => | 1897 | enabled: true, |
| 2370 | PubSub.subscribe(pubsub), | 1898 | snapshot: { |
| 1899 | resolveMaintenance: () => | ||
| 1900 | Effect.succeed( | ||
| 1901 | makeManualOnlyProviderMaintenanceCapabilities({ | ||
| 1902 | provider: openCodeDriver, | ||
| 1903 | packageName: null, | ||
| 1904 | }), | ||
| 1905 | ), | ||
| 1906 | getSnapshot: Effect.succeed(failedOpenCodeProvider), | ||
| 1907 | refresh: Ref.update(openCodeRefreshCalls, (count) => count + 1).pipe( | ||
| 1908 | Effect.andThen(Ref.get(catalogSnapshot)), | ||
| 2371 | ), | 1909 | ), |
| 1910 | streamChanges: Stream.empty, | ||
| 1911 | applyUsageLimits: () => Effect.void, | ||
| 2372 | }, | 1912 | }, |
| 1913 | orchestrationAdapter: {} as ProviderInstance["orchestrationAdapter"], | ||
| 1914 | textGeneration: {} as ProviderInstance["textGeneration"], | ||
| 1915 | }, | ||
| 1916 | ] satisfies ReadonlyArray<ProviderInstance>; | ||
| 1917 | const layerInstanceRegistry = Layer.succeed( | ||
| 1918 | ProviderInstanceRegistry.ProviderInstanceRegistry, | ||
| 1919 | { | ||
| 1920 | getInstance: (instanceId) => | ||
| 1921 | Effect.succeed(instances.find((instance) => instance.instanceId === instanceId)), | ||
| 1922 | listInstances: Effect.succeed(instances), | ||
| 1923 | listUnavailable: Effect.succeed([]), | ||
| 1924 | streamChanges: Stream.empty, | ||
| 1925 | subscribeChanges: Effect.flatMap(PubSub.unbounded<void>(), PubSub.subscribe), | ||
| 1926 | }, | ||
| 1927 | ); | ||
| 1928 | const scope = yield* Scope.make(); | ||
| 1929 | yield* Effect.addFinalizer(() => Scope.close(scope, Exit.void)); | ||
| 1930 | const runtimeServices = yield* Layer.build( | ||
| 1931 | ProviderRegistryLayer.layer.pipe( | ||
| 1932 | Layer.provideMerge(layerInstanceRegistry), | ||
| 1933 | Layer.provideMerge( | ||
| 1934 | ServerConfig.layerTest(process.cwd(), { | ||
| 1935 | prefix: "t3-provider-registry-reconnect-refresh-", | ||
| 1936 | }), | ||
| 1937 | ), | ||
| 1938 | Layer.provideMerge(NodeServices.layer), | ||
| 1939 | ), | ||
| 1940 | ).pipe(Scope.provide(scope)); | ||
| 1941 | |||
| 1942 | yield* Effect.gen(function* () { | ||
| 1943 | const registry = yield* ProviderRegistry.ProviderRegistry; | ||
| 1944 | const initialProviders = yield* registry.getProviders; | ||
| 1945 | assert.strictEqual( | ||
| 1946 | initialProviders.find((provider) => provider.instanceId === openCodeInstanceId)?.status, | ||
| 1947 | "error", | ||
| 2373 | ); | 1948 | ); |
| 2374 | const scope = yield* Scope.make(); | 1949 | |
| 2375 | yield* Effect.addFinalizer(() => Scope.close(scope, Exit.void)); | 1950 | const recoveredProviders = yield* registry.refresh(); |
| 2376 | const runtimeServices = yield* Layer.build( | 1951 | assert.deepStrictEqual( |
| 2377 | ProviderRegistryLive.pipe( | 1952 | recoveredProviders.find((provider) => provider.instanceId === openCodeInstanceId) |
| 2378 | Layer.provideMerge(instanceRegistryLayer), | 1953 | ?.models, |
| 2379 | Layer.provideMerge( | 1954 | recoveredOpenCodeProvider.models, |
| 2380 | ServerConfig.layerTest(process.cwd(), { | 1955 | ); |
| 2381 | prefix: "t3-provider-registry-refresh-failure-", | 1956 | assert.deepStrictEqual( |
| 1957 | recoveredProviders.find((provider) => provider.instanceId === codexInstanceId), | ||
| 1958 | withBundledCompatibility(codexProvider), | ||
| 1959 | ); | ||
| 1960 | |||
| 1961 | yield* Ref.set(catalogSnapshot, changedCatalogProvider); | ||
| 1962 | const changedProviders = yield* registry.refresh(); | ||
| 1963 | assert.deepStrictEqual( | ||
| 1964 | changedProviders.find((provider) => provider.instanceId === openCodeInstanceId)?.models, | ||
| 1965 | changedCatalogProvider.models, | ||
| 1966 | ); | ||
| 1967 | assert.deepStrictEqual( | ||
| 1968 | changedProviders.find((provider) => provider.instanceId === codexInstanceId), | ||
| 1969 | withBundledCompatibility(codexProvider), | ||
| 1970 | ); | ||
| 1971 | }).pipe(Effect.provide(runtimeServices)); | ||
| 1972 | |||
| 1973 | assert.strictEqual(yield* Ref.get(codexRefreshCalls), 2); | ||
| 1974 | assert.strictEqual(yield* Ref.get(openCodeRefreshCalls), 2); | ||
| 1975 | }), | ||
| 1976 | ); | ||
| 1977 | |||
| 1978 | it("persists merged provider snapshots for the providers that were refreshed", () => { | ||
| 1979 | const previousProviders = [ | ||
| 1980 | { | ||
| 1981 | instanceId: ProviderInstanceId.make("cursor"), | ||
| 1982 | driver: ProviderDriverKind.make("cursor"), | ||
| 1983 | status: "ready", | ||
| 1984 | enabled: true, | ||
| 1985 | installed: true, | ||
| 1986 | auth: { status: "authenticated" }, | ||
| 1987 | checkedAt: "2026-04-14T00:00:00.000Z", | ||
| 1988 | version: "2026.04.09-f2b0fcd", | ||
| 1989 | models: [ | ||
| 1990 | { | ||
| 1991 | slug: "claude-opus-4-6", | ||
| 1992 | name: "Opus 4.6", | ||
| 1993 | isCustom: false, | ||
| 1994 | capabilities: createModelCapabilities({ | ||
| 1995 | optionDescriptors: [ | ||
| 1996 | selectDescriptor("reasoning", "Reasoning", [ | ||
| 1997 | { id: "high", label: "High", isDefault: true }, | ||
| 1998 | ]), | ||
| 1999 | booleanDescriptor("fastMode", "Fast Mode"), | ||
| 2000 | booleanDescriptor("thinking", "Thinking"), | ||
| 2001 | ], | ||
| 2002 | }), | ||
| 2003 | }, | ||
| 2004 | ], | ||
| 2005 | slashCommands: [], | ||
| 2006 | skills: [], | ||
| 2007 | }, | ||
| 2008 | { | ||
| 2009 | instanceId: ProviderInstanceId.make("codex"), | ||
| 2010 | driver: ProviderDriverKind.make("codex"), | ||
| 2011 | status: "ready", | ||
| 2012 | enabled: true, | ||
| 2013 | installed: true, | ||
| 2014 | auth: { status: "authenticated" }, | ||
| 2015 | checkedAt: "2026-04-14T00:00:00.000Z", | ||
| 2016 | version: "1.0.0", | ||
| 2017 | models: [], | ||
| 2018 | slashCommands: [], | ||
| 2019 | skills: [], | ||
| 2020 | }, | ||
| 2021 | ] as const satisfies ReadonlyArray<ServerProvider>; | ||
| 2022 | const refreshedCursor = { | ||
| 2023 | ...previousProviders[0], | ||
| 2024 | checkedAt: "2026-04-14T00:01:00.000Z", | ||
| 2025 | models: [], | ||
| 2026 | } satisfies ServerProvider; | ||
| 2027 | |||
| 2028 | const mergedProviders = mergeProviderSnapshots(previousProviders, [refreshedCursor]); | ||
| 2029 | const persistedProviders = selectProvidersByKind( | ||
| 2030 | mergedProviders, | ||
| 2031 | new Set([ProviderDriverKind.make("cursor")]), | ||
| 2032 | ); | ||
| 2033 | |||
| 2034 | assert.deepStrictEqual(persistedProviders, [ | ||
| 2035 | { | ||
| 2036 | ...refreshedCursor, | ||
| 2037 | models: [...previousProviders[0].models], | ||
| 2038 | }, | ||
| 2039 | ]); | ||
| 2040 | }); | ||
| 2041 | |||
| 2042 | it.effect("persists the merged snapshot when a live update has empty models", () => | ||
| 2043 | Effect.gen(function* () { | ||
| 2044 | const cursorDriver = ProviderDriverKind.make("cursor"); | ||
| 2045 | const cursorInstanceId = ProviderInstanceId.make("cursor"); | ||
| 2046 | const initialProvider = { | ||
| 2047 | instanceId: cursorInstanceId, | ||
| 2048 | driver: cursorDriver, | ||
| 2049 | status: "ready", | ||
| 2050 | enabled: true, | ||
| 2051 | installed: true, | ||
| 2052 | auth: { status: "authenticated" }, | ||
| 2053 | checkedAt: "2026-04-14T00:00:00.000Z", | ||
| 2054 | version: "2026.04.09-f2b0fcd", | ||
| 2055 | models: [ | ||
| 2056 | { | ||
| 2057 | slug: "claude-opus-4-6", | ||
| 2058 | name: "Opus 4.6", | ||
| 2059 | isCustom: false, | ||
| 2060 | capabilities: createModelCapabilities({ | ||
| 2061 | optionDescriptors: [ | ||
| 2062 | selectDescriptor("reasoning", "Reasoning", [ | ||
| 2063 | { id: "high", label: "High", isDefault: true }, | ||
| 2064 | ]), | ||
| 2065 | ], | ||
| 2066 | }), | ||
| 2067 | }, | ||
| 2068 | ], | ||
| 2069 | slashCommands: [], | ||
| 2070 | skills: [], | ||
| 2071 | } as const satisfies ServerProvider; | ||
| 2072 | const refreshedProvider = { | ||
| 2073 | ...initialProvider, | ||
| 2074 | checkedAt: "2026-04-14T00:01:00.000Z", | ||
| 2075 | models: [], | ||
| 2076 | } satisfies ServerProvider; | ||
| 2077 | const changes = yield* PubSub.unbounded<ServerProvider>(); | ||
| 2078 | const instance = { | ||
| 2079 | instanceId: cursorInstanceId, | ||
| 2080 | driverKind: cursorDriver, | ||
| 2081 | continuationIdentity: { | ||
| 2082 | driverKind: cursorDriver, | ||
| 2083 | continuationKey: "cursor:instance:cursor", | ||
| 2084 | }, | ||
| 2085 | displayName: undefined, | ||
| 2086 | enabled: true, | ||
| 2087 | snapshot: { | ||
| 2088 | resolveMaintenance: () => | ||
| 2089 | Effect.succeed( | ||
| 2090 | makeManualOnlyProviderMaintenanceCapabilities({ | ||
| 2091 | provider: cursorDriver, | ||
| 2092 | packageName: null, | ||
| 2382 | }), | 2093 | }), |
| 2383 | ), | 2094 | ), |
| 2384 | Layer.provideMerge(BackgroundPolicyAlwaysRunLayer), | 2095 | getSnapshot: Effect.succeed(initialProvider), |
| 2385 | Layer.provideMerge(NodeServices.layer), | 2096 | refresh: Effect.succeed(refreshedProvider), |
| 2097 | streamChanges: Stream.fromPubSub(changes), | ||
| 2098 | applyUsageLimits: () => Effect.void, | ||
| 2099 | }, | ||
| 2100 | orchestrationAdapter: {} as ProviderInstance["orchestrationAdapter"], | ||
| 2101 | textGeneration: {} as ProviderInstance["textGeneration"], | ||
| 2102 | } satisfies ProviderInstance; | ||
| 2103 | const layerInstanceRegistry = Layer.succeed( | ||
| 2104 | ProviderInstanceRegistry.ProviderInstanceRegistry, | ||
| 2105 | { | ||
| 2106 | getInstance: (instanceId) => | ||
| 2107 | Effect.succeed(instanceId === cursorInstanceId ? instance : undefined), | ||
| 2108 | listInstances: Effect.succeed([instance]), | ||
| 2109 | listUnavailable: Effect.succeed([]), | ||
| 2110 | streamChanges: Stream.empty, | ||
| 2111 | subscribeChanges: Effect.flatMap(PubSub.unbounded<void>(), (pubsub) => | ||
| 2112 | PubSub.subscribe(pubsub), | ||
| 2386 | ), | 2113 | ), |
| 2387 | ).pipe(Scope.provide(scope)); | 2114 | }, |
| 2115 | ); | ||
| 2116 | const scope = yield* Scope.make(); | ||
| 2117 | yield* Effect.addFinalizer(() => Scope.close(scope, Exit.void)); | ||
| 2118 | const runtimeServices = yield* Layer.build( | ||
| 2119 | ProviderRegistryLayer.layer.pipe( | ||
| 2120 | Layer.provideMerge(layerInstanceRegistry), | ||
| 2121 | Layer.provideMerge( | ||
| 2122 | ServerConfig.layerTest(process.cwd(), { | ||
| 2123 | prefix: "t3-provider-registry-merged-persist-", | ||
| 2124 | }), | ||
| 2125 | ), | ||
| 2126 | Layer.provideMerge(layerBackgroundPolicyAlwaysRun), | ||
| 2127 | Layer.provideMerge(NodeServices.layer), | ||
| 2128 | ), | ||
| 2129 | ).pipe(Scope.provide(scope)); | ||
| 2388 | 2130 | ||
| 2389 | yield* Effect.gen(function* () { | 2131 | yield* Effect.gen(function* () { |
| 2390 | const registry = yield* ProviderRegistry.ProviderRegistry; | 2132 | const registry = yield* ProviderRegistry.ProviderRegistry; |
| 2133 | const config = yield* ServerConfig.ServerConfig; | ||
| 2134 | const filePath = yield* resolveProviderStatusCachePath({ | ||
| 2135 | cacheDir: config.providerStatusCacheDir, | ||
| 2136 | instanceId: cursorInstanceId, | ||
| 2137 | }); | ||
| 2391 | 2138 | ||
| 2392 | assert.deepStrictEqual(yield* registry.getProviders, [ | 2139 | assert.deepStrictEqual((yield* registry.getProviders)[0]?.models, [ |
| 2393 | withBundledCompatibility(cachedProvider), | 2140 | ...initialProvider.models, |
| 2394 | ]); | 2141 | ]); |
| 2395 | assert.deepStrictEqual(yield* registry.refresh(codexDriver), [ | 2142 | const persisted = yield* awaitPersistedProvider(registry, refreshedProvider.checkedAt); |
| 2396 | withBundledCompatibility(cachedProvider), | 2143 | yield* PubSub.publish(changes, refreshedProvider); |
| 2397 | ]); | 2144 | yield* Fiber.join(persisted); |
| 2398 | assert.deepStrictEqual(yield* registry.refreshInstance(codexInstanceId), [ | 2145 | const cachedProvider = yield* readProviderStatusCache(filePath); |
| 2399 | withBundledCompatibility(cachedProvider), | 2146 | |
| 2400 | ]); | 2147 | assert.deepStrictEqual( |
| 2401 | }).pipe(Effect.provide(runtimeServices)); | 2148 | cachedProvider, |
| 2402 | }), | 2149 | withBundledCompatibility({ |
| 2403 | ); | 2150 | ...refreshedProvider, |
| 2151 | models: [...initialProvider.models], | ||
| 2152 | }), | ||
| 2153 | ); | ||
| 2154 | }).pipe(Effect.provide(runtimeServices)); | ||
| 2155 | }), | ||
| 2156 | ); | ||
| 2404 | 2157 | ||
| 2405 | it.effect("keeps consuming registry changes after one sync fails", () => | 2158 | it.effect( |
| 2159 | "persists authoritative OpenCode removals without resurrecting them on a failed live refresh", | ||
| 2160 | () => | ||
| 2406 | Effect.gen(function* () { | 2161 | Effect.gen(function* () { |
| 2407 | const codexDriver = ProviderDriverKind.make("codex"); | 2162 | const openCodeDriver = ProviderDriverKind.make("opencode"); |
| 2408 | const codexInstanceId = ProviderInstanceId.make("codex"); | 2163 | const openCodeInstanceId = ProviderInstanceId.make("opencode"); |
| 2409 | const claudeDriver = ProviderDriverKind.make("claudeAgent"); | 2164 | const initialProvider = { |
| 2410 | const claudeInstanceId = ProviderInstanceId.make("claudeAgent"); | 2165 | instanceId: openCodeInstanceId, |
| 2411 | const codexProvider = { | 2166 | driver: openCodeDriver, |
| 2412 | instanceId: codexInstanceId, | ||
| 2413 | driver: codexDriver, | ||
| 2414 | status: "ready", | 2167 | status: "ready", |
| 2415 | enabled: true, | 2168 | enabled: true, |
| 2416 | installed: true, | 2169 | installed: true, |
| 2417 | auth: { status: "authenticated" }, | 2170 | auth: { status: "authenticated" }, |
| 2418 | checkedAt: "2026-04-29T10:00:00.000Z", | 2171 | checkedAt: "2026-07-17T00:00:00.000Z", |
| 2419 | version: "1.0.0", | 2172 | version: "1.0.0", |
| 2420 | models: [], | 2173 | models: [ |
| 2174 | { | ||
| 2175 | slug: "github/gpt-5", | ||
| 2176 | name: "GPT-5", | ||
| 2177 | subProvider: "GitHub", | ||
| 2178 | isCustom: false, | ||
| 2179 | capabilities: null, | ||
| 2180 | }, | ||
| 2181 | { | ||
| 2182 | slug: "removed-plugin/model", | ||
| 2183 | name: "Removed Plugin Model", | ||
| 2184 | subProvider: "Removed Plugin", | ||
| 2185 | isCustom: false, | ||
| 2186 | capabilities: null, | ||
| 2187 | }, | ||
| 2188 | ], | ||
| 2421 | slashCommands: [], | 2189 | slashCommands: [], |
| 2422 | skills: [], | 2190 | skills: [], |
| 2423 | } as const satisfies ServerProvider; | 2191 | } as const satisfies ServerProvider; |
| 2424 | const claudeProvider = { | 2192 | const authoritativeProvider = { |
| 2425 | instanceId: claudeInstanceId, | 2193 | ...initialProvider, |
| 2426 | driver: claudeDriver, | 2194 | checkedAt: "2026-07-17T00:01:00.000Z", |
| 2427 | status: "ready", | 2195 | models: [initialProvider.models[0]!], |
| 2428 | enabled: true, | 2196 | } satisfies ServerProvider; |
| 2429 | installed: true, | 2197 | const failedProvider = { |
| 2430 | auth: { status: "authenticated" }, | 2198 | ...authoritativeProvider, |
| 2431 | checkedAt: "2026-04-29T10:01:00.000Z", | 2199 | status: "error", |
| 2432 | version: "1.0.0", | 2200 | auth: { status: "unknown" }, |
| 2201 | checkedAt: "2026-07-17T00:02:00.000Z", | ||
| 2433 | models: [], | 2202 | models: [], |
| 2434 | slashCommands: [], | 2203 | message: "Failed to refresh OpenCode models.", |
| 2435 | skills: [], | 2204 | } satisfies ServerProvider; |
| 2436 | } as const satisfies ServerProvider; | 2205 | const changes = yield* PubSub.unbounded<ServerProvider>(); |
| 2437 | const makeInstance = (provider: ServerProvider): ProviderInstance => ({ | 2206 | const instance = { |
| 2438 | instanceId: provider.instanceId, | 2207 | instanceId: openCodeInstanceId, |
| 2439 | driverKind: provider.driver, | 2208 | driverKind: openCodeDriver, |
| 2440 | continuationIdentity: { | 2209 | continuationIdentity: { |
| 2441 | driverKind: provider.driver, | 2210 | driverKind: openCodeDriver, |
| 2442 | continuationKey: `${provider.driver}:instance:${provider.instanceId}`, | 2211 | continuationKey: "opencode:instance:opencode", |
| 2443 | }, | 2212 | }, |
| 2444 | displayName: undefined, | 2213 | displayName: undefined, |
| 2445 | enabled: true, | 2214 | enabled: true, |
| ... | @@ -2447,372 +2216,636 @@ it.layer(Layer.mergeAll(TestNodeServices, ServerSettingsModule.layerTest(), Test | ... | @@ -2447,372 +2216,636 @@ it.layer(Layer.mergeAll(TestNodeServices, ServerSettingsModule.layerTest(), Test |
| 2447 | resolveMaintenance: () => | 2216 | resolveMaintenance: () => |
| 2448 | Effect.succeed( | 2217 | Effect.succeed( |
| 2449 | makeManualOnlyProviderMaintenanceCapabilities({ | 2218 | makeManualOnlyProviderMaintenanceCapabilities({ |
| 2450 | provider: provider.driver, | 2219 | provider: openCodeDriver, |
| 2451 | packageName: null, | 2220 | packageName: null, |
| 2452 | }), | 2221 | }), |
| 2453 | ), | 2222 | ), |
| 2454 | getSnapshot: Effect.succeed(provider), | 2223 | getSnapshot: Effect.succeed(initialProvider), |
| 2455 | refresh: Effect.succeed(provider), | 2224 | refresh: Effect.succeed(authoritativeProvider), |
| 2456 | streamChanges: Stream.empty, | 2225 | streamChanges: Stream.fromPubSub(changes), |
| 2457 | applyUsageLimits: () => Effect.void, | 2226 | applyUsageLimits: () => Effect.void, |
| 2458 | }, | 2227 | }, |
| 2459 | orchestrationAdapter: {} as ProviderInstance["orchestrationAdapter"], | 2228 | orchestrationAdapter: {} as ProviderInstance["orchestrationAdapter"], |
| 2460 | textGeneration: {} as ProviderInstance["textGeneration"], | 2229 | textGeneration: {} as ProviderInstance["textGeneration"], |
| 2461 | }); | 2230 | } satisfies ProviderInstance; |
| 2462 | const codexInstance = makeInstance(codexProvider); | 2231 | const layerInstanceRegistry = Layer.succeed( |
| 2463 | const claudeInstance = makeInstance(claudeProvider); | ||
| 2464 | const changes = yield* PubSub.unbounded<void>(); | ||
| 2465 | const instancesRef = yield* Ref.make<ReadonlyArray<ProviderInstance>>([codexInstance]); | ||
| 2466 | const failNextList = yield* Ref.make(false); | ||
| 2467 | const wait = () => Effect.yieldNow; | ||
| 2468 | const instanceRegistryLayer = Layer.succeed( | ||
| 2469 | ProviderInstanceRegistry.ProviderInstanceRegistry, | 2232 | ProviderInstanceRegistry.ProviderInstanceRegistry, |
| 2470 | { | 2233 | { |
| 2471 | getInstance: (instanceId) => | 2234 | getInstance: (instanceId) => |
| 2472 | Ref.get(instancesRef).pipe( | 2235 | Effect.succeed(instanceId === openCodeInstanceId ? instance : undefined), |
| 2473 | Effect.map((instances) => | 2236 | listInstances: Effect.succeed([instance]), |
| 2474 | instances.find((instance) => instance.instanceId === instanceId), | ||
| 2475 | ), | ||
| 2476 | ), | ||
| 2477 | listInstances: Effect.gen(function* () { | ||
| 2478 | const shouldFail = yield* Ref.get(failNextList); | ||
| 2479 | if (shouldFail) { | ||
| 2480 | yield* Ref.set(failNextList, false); | ||
| 2481 | return yield* Effect.die(new Error("simulated registry list failure")); | ||
| 2482 | } | ||
| 2483 | return yield* Ref.get(instancesRef); | ||
| 2484 | }), | ||
| 2485 | listUnavailable: Effect.succeed([]), | 2237 | listUnavailable: Effect.succeed([]), |
| 2486 | streamChanges: Stream.fromPubSub(changes), | 2238 | streamChanges: Stream.empty, |
| 2487 | subscribeChanges: PubSub.subscribe(changes), | 2239 | subscribeChanges: Effect.flatMap(PubSub.unbounded<void>(), (pubsub) => |
| 2240 | PubSub.subscribe(pubsub), | ||
| 2241 | ), | ||
| 2488 | }, | 2242 | }, |
| 2489 | ); | 2243 | ); |
| 2490 | const scope = yield* Scope.make(); | 2244 | const scope = yield* Scope.make(); |
| 2491 | yield* Effect.addFinalizer(() => Scope.close(scope, Exit.void)); | 2245 | yield* Effect.addFinalizer(() => Scope.close(scope, Exit.void)); |
| 2492 | const runtimeServices = yield* Layer.build( | 2246 | const runtimeServices = yield* Layer.build( |
| 2493 | ProviderRegistryLive.pipe( | 2247 | ProviderRegistryLayer.layer.pipe( |
| 2494 | Layer.provideMerge(instanceRegistryLayer), | 2248 | Layer.provideMerge(layerInstanceRegistry), |
| 2495 | Layer.provideMerge( | 2249 | Layer.provideMerge( |
| 2496 | ServerConfig.layerTest(process.cwd(), { | 2250 | ServerConfig.layerTest(process.cwd(), { |
| 2497 | prefix: "t3-provider-registry-sync-failure-", | 2251 | prefix: "t3-provider-registry-opencode-authoritative-persist-", |
| 2498 | }), | 2252 | }), |
| 2499 | ), | 2253 | ), |
| 2500 | Layer.provideMerge(BackgroundPolicyAlwaysRunLayer), | ||
| 2501 | Layer.provideMerge(NodeServices.layer), | 2254 | Layer.provideMerge(NodeServices.layer), |
| 2502 | ), | 2255 | ), |
| 2503 | ).pipe(Scope.provide(scope)); | 2256 | ).pipe(Scope.provide(scope)); |
| 2504 | 2257 | ||
| 2505 | yield* Effect.gen(function* () { | 2258 | yield* Effect.gen(function* () { |
| 2506 | const registry = yield* ProviderRegistry.ProviderRegistry; | 2259 | const registry = yield* ProviderRegistry.ProviderRegistry; |
| 2507 | assert.deepStrictEqual(yield* registry.getProviders, [ | 2260 | const config = yield* ServerConfig.ServerConfig; |
| 2508 | withBundledCompatibility(codexProvider), | 2261 | const filePath = yield* resolveProviderStatusCachePath({ |
| 2509 | ]); | 2262 | cacheDir: config.providerStatusCacheDir, |
| 2263 | instanceId: openCodeInstanceId, | ||
| 2264 | }); | ||
| 2510 | 2265 | ||
| 2511 | yield* Ref.set(failNextList, true); | 2266 | const authoritativePersisted = yield* awaitPersistedProvider( |
| 2512 | yield* PubSub.publish(changes, undefined); | 2267 | registry, |
| 2513 | 2268 | authoritativeProvider.checkedAt, | |
| 2514 | yield* Ref.set(instancesRef, [codexInstance, claudeInstance]); | 2269 | ); |
| 2515 | yield* PubSub.publish(changes, undefined); | 2270 | yield* PubSub.publish(changes, authoritativeProvider); |
| 2516 | 2271 | yield* Fiber.join(authoritativePersisted); | |
| 2517 | let providers = yield* registry.getProviders; | 2272 | let cachedProvider = yield* readProviderStatusCache(filePath); |
| 2518 | for ( | ||
| 2519 | let attempt = 0; | ||
| 2520 | attempt < 50 && | ||
| 2521 | !providers.some((provider) => provider.instanceId === claudeInstanceId); | ||
| 2522 | attempt += 1 | ||
| 2523 | ) { | ||
| 2524 | yield* wait(); | ||
| 2525 | providers = yield* registry.getProviders; | ||
| 2526 | } | ||
| 2527 | 2273 | ||
| 2528 | assert.deepStrictEqual( | 2274 | assert.deepStrictEqual(cachedProvider?.models, [authoritativeProvider.models[0]!]); |
| 2529 | providers.map((provider) => provider.instanceId).toSorted(), | 2275 | |
| 2530 | [codexInstanceId, claudeInstanceId].toSorted(), | 2276 | const failedPersisted = yield* awaitPersistedProvider( |
| 2277 | registry, | ||
| 2278 | failedProvider.checkedAt, | ||
| 2531 | ); | 2279 | ); |
| 2280 | yield* PubSub.publish(changes, failedProvider); | ||
| 2281 | yield* Fiber.join(failedPersisted); | ||
| 2282 | cachedProvider = yield* readProviderStatusCache(filePath); | ||
| 2283 | |||
| 2284 | assert.deepStrictEqual(cachedProvider?.models, [authoritativeProvider.models[0]!]); | ||
| 2285 | assert.deepStrictEqual((yield* registry.getProviders)[0]?.models, [ | ||
| 2286 | authoritativeProvider.models[0]!, | ||
| 2287 | ]); | ||
| 2532 | }).pipe(Effect.provide(runtimeServices)); | 2288 | }).pipe(Effect.provide(runtimeServices)); |
| 2533 | }), | 2289 | }), |
| 2534 | ); | 2290 | ); |
| 2535 | 2291 | ||
| 2536 | // This test intentionally avoids `mockCommandSpawnerLayer` so the real | 2292 | it.effect("returns the cached provider list when a manual refresh fails", () => |
| 2537 | // `probeCodexAppServerProvider` path runs — including the full | 2293 | Effect.gen(function* () { |
| 2538 | // `codex app-server` RPC handshake via `CodexClient.layerChildProcess`. | 2294 | const codexDriver = ProviderDriverKind.make("codex"); |
| 2539 | // We point `binaryPath` at a name that cannot exist on any machine so | 2295 | const codexInstanceId = ProviderInstanceId.make("codex"); |
| 2540 | // the real `ChildProcessSpawner` deterministically returns ENOENT; the | 2296 | const cachedProvider = { |
| 2541 | // probe wraps that as `CodexAppServerSpawnError` and | 2297 | instanceId: codexInstanceId, |
| 2542 | // `checkCodexProviderStatus` turns it into the user-visible "not | 2298 | driver: codexDriver, |
| 2543 | // installed" error snapshot. If the aggregator's `syncLiveSources` | 2299 | status: "ready", |
| 2544 | // breaks — the `codex_personal`-never-probes bug we are guarding | 2300 | enabled: true, |
| 2545 | // against — that snapshot never lands in `getProviders` and the | 2301 | installed: true, |
| 2546 | // assertions below fail. | 2302 | auth: { status: "authenticated" }, |
| 2547 | it.effect("propagates real Codex probe failures to the aggregator at boot", () => | 2303 | checkedAt: "2026-04-29T10:00:00.000Z", |
| 2548 | Effect.gen(function* () { | 2304 | version: "1.0.0", |
| 2549 | const missingBinary = `t3code_codex_missing_`; | 2305 | models: [], |
| 2550 | const serverSettings = yield* makeMutableServerSettingsService( | 2306 | slashCommands: [], |
| 2551 | decodeServerSettings( | 2307 | skills: [], |
| 2552 | deepMerge(encodedDefaultServerSettings, { | 2308 | } as const satisfies ServerProvider; |
| 2553 | providers: { | 2309 | const instance = { |
| 2554 | // Disable every built-in probe that would otherwise spawn | 2310 | instanceId: codexInstanceId, |
| 2555 | // on the CI host. `enabled: false` short-circuits each | 2311 | driverKind: codexDriver, |
| 2556 | // driver's probe *before* it touches the spawner, so the | 2312 | continuationIdentity: { |
| 2557 | // test environment stays isolated from the dev | 2313 | driverKind: codexDriver, |
| 2558 | // machine's PATH. | 2314 | continuationKey: "codex:instance:codex", |
| 2559 | codex: { enabled: false }, | 2315 | }, |
| 2560 | claudeAgent: { enabled: false }, | 2316 | displayName: undefined, |
| 2561 | cursor: { enabled: false }, | 2317 | enabled: true, |
| 2562 | grok: { enabled: false }, | 2318 | snapshot: { |
| 2563 | opencode: { enabled: false }, | 2319 | resolveMaintenance: () => |
| 2564 | }, | 2320 | Effect.succeed( |
| 2565 | // `providerInstances` keys are branded `ProviderInstanceId`; | 2321 | makeManualOnlyProviderMaintenanceCapabilities({ |
| 2566 | // the branded index signature rejects plain string literals | 2322 | provider: codexDriver, |
| 2567 | // at the TS level even though the runtime schema happily | 2323 | packageName: null, |
| 2568 | // accepts + decodes them. Cast the patch to `unknown` so | 2324 | }), |
| 2569 | // the `Schema.decodeSync` below does the real validation. | 2325 | ), |
| 2570 | providerInstances: { | 2326 | getSnapshot: Effect.succeed(cachedProvider), |
| 2571 | // Matches the shape the user had in `.t3/dev/settings.json` | 2327 | refresh: Effect.die(new Error("simulated refresh failure")), |
| 2572 | // when the bug was reported: a custom enabled Codex instance | 2328 | streamChanges: Stream.empty, |
| 2573 | // pointing at a binary the server has to actually spawn. | 2329 | applyUsageLimits: () => Effect.void, |
| 2574 | codex_personal: { | 2330 | }, |
| 2575 | driver: "codex", | 2331 | orchestrationAdapter: {} as ProviderInstance["orchestrationAdapter"], |
| 2576 | displayName: "Codex Personal", | 2332 | textGeneration: {} as ProviderInstance["textGeneration"], |
| 2577 | enabled: true, | 2333 | } satisfies ProviderInstance; |
| 2578 | config: { | 2334 | const layerInstanceRegistry = Layer.succeed( |
| 2579 | binaryPath: missingBinary, | 2335 | ProviderInstanceRegistry.ProviderInstanceRegistry, |
| 2580 | homePath: `/tmp/${missingBinary}_home`, | 2336 | { |
| 2581 | }, | 2337 | getInstance: (instanceId) => |
| 2582 | }, | 2338 | Effect.succeed(instanceId === codexInstanceId ? instance : undefined), |
| 2583 | } as unknown as ContractServerSettings["providerInstances"], | 2339 | listInstances: Effect.succeed([instance]), |
| 2584 | }), | 2340 | listUnavailable: Effect.succeed([]), |
| 2585 | ), | 2341 | streamChanges: Stream.empty, |
| 2586 | ); | 2342 | subscribeChanges: Effect.flatMap(PubSub.unbounded<void>(), (pubsub) => |
| 2587 | const scope = yield* Scope.make(); | 2343 | PubSub.subscribe(pubsub), |
| 2588 | yield* Effect.addFinalizer(() => Scope.close(scope, Exit.void)); | ||
| 2589 | const providerRegistryLayer = ProviderRegistryLive.pipe( | ||
| 2590 | Layer.provideMerge(ProviderInstanceRegistryHydrationLive), | ||
| 2591 | Layer.provideMerge(AntigravityInstallation.AntigravityInstallation.layer), | ||
| 2592 | Layer.provideMerge( | ||
| 2593 | Layer.succeed(ServerSettingsModule.ServerSettingsService, serverSettings), | ||
| 2594 | ), | 2344 | ), |
| 2595 | Layer.provideMerge(ServerSecretStore.layer), | 2345 | }, |
| 2346 | ); | ||
| 2347 | const scope = yield* Scope.make(); | ||
| 2348 | yield* Effect.addFinalizer(() => Scope.close(scope, Exit.void)); | ||
| 2349 | const runtimeServices = yield* Layer.build( | ||
| 2350 | ProviderRegistryLayer.layer.pipe( | ||
| 2351 | Layer.provideMerge(layerInstanceRegistry), | ||
| 2596 | Layer.provideMerge( | 2352 | Layer.provideMerge( |
| 2597 | ServerConfig.layerTest(process.cwd(), { | 2353 | ServerConfig.layerTest(process.cwd(), { |
| 2598 | prefix: "t3-provider-registry-", | 2354 | prefix: "t3-provider-registry-refresh-failure-", |
| 2599 | }), | 2355 | }), |
| 2600 | ), | 2356 | ), |
| 2601 | Layer.provideMerge(TestHttpClientLive), | 2357 | Layer.provideMerge(layerBackgroundPolicyAlwaysRun), |
| 2602 | Layer.provideMerge( | 2358 | Layer.provideMerge(NodeServices.layer), |
| 2603 | Layer.succeed( | 2359 | ), |
| 2604 | ProviderEventLoggers.ProviderEventLoggers, | 2360 | ).pipe(Scope.provide(scope)); |
| 2605 | ProviderEventLoggers.NoOpProviderEventLoggers, | 2361 | |
| 2362 | yield* Effect.gen(function* () { | ||
| 2363 | const registry = yield* ProviderRegistry.ProviderRegistry; | ||
| 2364 | |||
| 2365 | assert.deepStrictEqual(yield* registry.getProviders, [ | ||
| 2366 | withBundledCompatibility(cachedProvider), | ||
| 2367 | ]); | ||
| 2368 | assert.deepStrictEqual(yield* registry.refresh(codexDriver), [ | ||
| 2369 | withBundledCompatibility(cachedProvider), | ||
| 2370 | ]); | ||
| 2371 | assert.deepStrictEqual(yield* registry.refreshInstance(codexInstanceId), [ | ||
| 2372 | withBundledCompatibility(cachedProvider), | ||
| 2373 | ]); | ||
| 2374 | }).pipe(Effect.provide(runtimeServices)); | ||
| 2375 | }), | ||
| 2376 | ); | ||
| 2377 | |||
| 2378 | it.effect("keeps consuming registry changes after one sync fails", () => | ||
| 2379 | Effect.gen(function* () { | ||
| 2380 | const codexDriver = ProviderDriverKind.make("codex"); | ||
| 2381 | const codexInstanceId = ProviderInstanceId.make("codex"); | ||
| 2382 | const claudeDriver = ProviderDriverKind.make("claudeAgent"); | ||
| 2383 | const claudeInstanceId = ProviderInstanceId.make("claudeAgent"); | ||
| 2384 | const codexProvider = { | ||
| 2385 | instanceId: codexInstanceId, | ||
| 2386 | driver: codexDriver, | ||
| 2387 | status: "ready", | ||
| 2388 | enabled: true, | ||
| 2389 | installed: true, | ||
| 2390 | auth: { status: "authenticated" }, | ||
| 2391 | checkedAt: "2026-04-29T10:00:00.000Z", | ||
| 2392 | version: "1.0.0", | ||
| 2393 | models: [], | ||
| 2394 | slashCommands: [], | ||
| 2395 | skills: [], | ||
| 2396 | } as const satisfies ServerProvider; | ||
| 2397 | const claudeProvider = { | ||
| 2398 | instanceId: claudeInstanceId, | ||
| 2399 | driver: claudeDriver, | ||
| 2400 | status: "ready", | ||
| 2401 | enabled: true, | ||
| 2402 | installed: true, | ||
| 2403 | auth: { status: "authenticated" }, | ||
| 2404 | checkedAt: "2026-04-29T10:01:00.000Z", | ||
| 2405 | version: "1.0.0", | ||
| 2406 | models: [], | ||
| 2407 | slashCommands: [], | ||
| 2408 | skills: [], | ||
| 2409 | } as const satisfies ServerProvider; | ||
| 2410 | const makeInstance = (provider: ServerProvider): ProviderInstance => ({ | ||
| 2411 | instanceId: provider.instanceId, | ||
| 2412 | driverKind: provider.driver, | ||
| 2413 | continuationIdentity: { | ||
| 2414 | driverKind: provider.driver, | ||
| 2415 | continuationKey: `${provider.driver}:instance:${provider.instanceId}`, | ||
| 2416 | }, | ||
| 2417 | displayName: undefined, | ||
| 2418 | enabled: true, | ||
| 2419 | snapshot: { | ||
| 2420 | resolveMaintenance: () => | ||
| 2421 | Effect.succeed( | ||
| 2422 | makeManualOnlyProviderMaintenanceCapabilities({ | ||
| 2423 | provider: provider.driver, | ||
| 2424 | packageName: null, | ||
| 2425 | }), | ||
| 2606 | ), | 2426 | ), |
| 2607 | ), | 2427 | getSnapshot: Effect.succeed(provider), |
| 2608 | Layer.provideMerge(ModelManifest.layerTest), | 2428 | refresh: Effect.succeed(provider), |
| 2609 | Layer.provideMerge(ResetCreditCoordinator.layerTest), | 2429 | streamChanges: Stream.empty, |
| 2610 | Layer.provideMerge( | 2430 | applyUsageLimits: () => Effect.void, |
| 2611 | OpenCodeRuntime.OpenCodeRuntimeLive.pipe( | 2431 | }, |
| 2612 | Layer.provide(OpenCodeServerLedger.layerTest), | 2432 | orchestrationAdapter: {} as ProviderInstance["orchestrationAdapter"], |
| 2433 | textGeneration: {} as ProviderInstance["textGeneration"], | ||
| 2434 | }); | ||
| 2435 | const codexInstance = makeInstance(codexProvider); | ||
| 2436 | const claudeInstance = makeInstance(claudeProvider); | ||
| 2437 | const changes = yield* PubSub.unbounded<void>(); | ||
| 2438 | const instancesRef = yield* Ref.make<ReadonlyArray<ProviderInstance>>([codexInstance]); | ||
| 2439 | const failNextList = yield* Ref.make(false); | ||
| 2440 | const wait = () => Effect.yieldNow; | ||
| 2441 | const layerInstanceRegistry = Layer.succeed( | ||
| 2442 | ProviderInstanceRegistry.ProviderInstanceRegistry, | ||
| 2443 | { | ||
| 2444 | getInstance: (instanceId) => | ||
| 2445 | Ref.get(instancesRef).pipe( | ||
| 2446 | Effect.map((instances) => | ||
| 2447 | instances.find((instance) => instance.instanceId === instanceId), | ||
| 2448 | ), | ||
| 2613 | ), | 2449 | ), |
| 2450 | listInstances: Effect.gen(function* () { | ||
| 2451 | const shouldFail = yield* Ref.get(failNextList); | ||
| 2452 | if (shouldFail) { | ||
| 2453 | yield* Ref.set(failNextList, false); | ||
| 2454 | return yield* Effect.die(new Error("simulated registry list failure")); | ||
| 2455 | } | ||
| 2456 | return yield* Ref.get(instancesRef); | ||
| 2457 | }), | ||
| 2458 | listUnavailable: Effect.succeed([]), | ||
| 2459 | streamChanges: Stream.fromPubSub(changes), | ||
| 2460 | subscribeChanges: PubSub.subscribe(changes), | ||
| 2461 | }, | ||
| 2462 | ); | ||
| 2463 | const scope = yield* Scope.make(); | ||
| 2464 | yield* Effect.addFinalizer(() => Scope.close(scope, Exit.void)); | ||
| 2465 | const runtimeServices = yield* Layer.build( | ||
| 2466 | ProviderRegistryLayer.layer.pipe( | ||
| 2467 | Layer.provideMerge(layerInstanceRegistry), | ||
| 2468 | Layer.provideMerge( | ||
| 2469 | ServerConfig.layerTest(process.cwd(), { | ||
| 2470 | prefix: "t3-provider-registry-sync-failure-", | ||
| 2471 | }), | ||
| 2614 | ), | 2472 | ), |
| 2615 | Layer.provideMerge(BackgroundPolicyAlwaysRunLayer), | 2473 | Layer.provideMerge(layerBackgroundPolicyAlwaysRun), |
| 2616 | // NO spawner mock — `ChildProcessSpawner` is supplied by the | 2474 | Layer.provideMerge(NodeServices.layer), |
| 2617 | // outer `NodeServices.layer` on `it.layer(...)` and will | 2475 | ), |
| 2618 | // genuinely spawn a subprocess. The missing-binary ENOENT is | 2476 | ).pipe(Scope.provide(scope)); |
| 2619 | // what exercises the same failure mode as a misconfigured | ||
| 2620 | // production `binaryPath`. | ||
| 2621 | ); | ||
| 2622 | const runtimeServices = yield* Layer.build(providerRegistryLayer).pipe( | ||
| 2623 | Scope.provide(scope), | ||
| 2624 | ); | ||
| 2625 | 2477 | ||
| 2626 | yield* Effect.gen(function* () { | 2478 | yield* Effect.gen(function* () { |
| 2627 | const registry = yield* ProviderRegistry.ProviderRegistry; | 2479 | const registry = yield* ProviderRegistry.ProviderRegistry; |
| 2628 | let providers = yield* registry.getProviders; | 2480 | assert.deepStrictEqual(yield* registry.getProviders, [ |
| 2629 | for ( | 2481 | withBundledCompatibility(codexProvider), |
| 2630 | let attempts = 0; | 2482 | ]); |
| 2631 | attempts < 50 && | ||
| 2632 | providers.find((provider) => provider.instanceId === "codex_personal")?.status !== | ||
| 2633 | "error"; | ||
| 2634 | attempts += 1 | ||
| 2635 | ) { | ||
| 2636 | yield* Effect.yieldNow; | ||
| 2637 | providers = yield* registry.getProviders; | ||
| 2638 | } | ||
| 2639 | const codexPersonal = providers.find( | ||
| 2640 | (provider) => provider.instanceId === "codex_personal", | ||
| 2641 | ); | ||
| 2642 | assert.notStrictEqual( | ||
| 2643 | codexPersonal, | ||
| 2644 | undefined, | ||
| 2645 | `Expected the aggregator to know about codex_personal; instead saw: ${providers | ||
| 2646 | .map((provider) => provider.instanceId) | ||
| 2647 | .join(", ")}`, | ||
| 2648 | ); | ||
| 2649 | assert.strictEqual( | ||
| 2650 | codexPersonal?.status, | ||
| 2651 | "error", | ||
| 2652 | "Real Codex probe against a missing binary should surface as 'error' in the aggregator", | ||
| 2653 | ); | ||
| 2654 | assert.strictEqual(codexPersonal?.installed, false); | ||
| 2655 | assert.include(codexPersonal?.message, missingBinary); | ||
| 2656 | assert.include(codexPersonal?.message, "Settings → Providers → Codex → Binary path"); | ||
| 2657 | }).pipe(Effect.provide(runtimeServices)); | ||
| 2658 | }), | ||
| 2659 | ); | ||
| 2660 | 2483 | ||
| 2661 | // A binary path change must rebuild Codex and publish its new probe result. | 2484 | yield* Ref.set(failNextList, true); |
| 2662 | it.effect("re-probes when settings change the codex binaryPath", () => | 2485 | yield* PubSub.publish(changes, undefined); |
| 2663 | Effect.gen(function* () { | 2486 | |
| 2664 | const firstMissing = `t3code_codex_first_`; | 2487 | yield* Ref.set(instancesRef, [codexInstance, claudeInstance]); |
| 2665 | const secondMissing = `t3code_codex_second_`; | 2488 | yield* PubSub.publish(changes, undefined); |
| 2666 | const spawnedCommands: Array<string> = []; | 2489 | |
| 2667 | const secondProbeStarted = yield* Deferred.make<void>(); | 2490 | let providers = yield* registry.getProviders; |
| 2668 | const releaseSecondProbe = yield* Deferred.make<void>(); | 2491 | for ( |
| 2669 | const allowLazySettingsStream = yield* Deferred.make<void>(); | 2492 | let attempt = 0; |
| 2670 | const mutableServerSettings = yield* makeMutableServerSettingsService( | 2493 | attempt < 50 && !providers.some((provider) => provider.instanceId === claudeInstanceId); |
| 2671 | decodeServerSettings( | 2494 | attempt += 1 |
| 2672 | deepMerge(encodedDefaultServerSettings, { | 2495 | ) { |
| 2673 | providers: { | 2496 | yield* wait(); |
| 2674 | codex: { enabled: true, binaryPath: firstMissing }, | 2497 | providers = yield* registry.getProviders; |
| 2675 | claudeAgent: { enabled: false }, | 2498 | } |
| 2676 | cursor: { enabled: false }, | 2499 | |
| 2677 | grok: { enabled: false }, | 2500 | assert.deepStrictEqual( |
| 2678 | opencode: { enabled: false }, | 2501 | providers.map((provider) => provider.instanceId).toSorted(), |
| 2502 | [codexInstanceId, claudeInstanceId].toSorted(), | ||
| 2503 | ); | ||
| 2504 | }).pipe(Effect.provide(runtimeServices)); | ||
| 2505 | }), | ||
| 2506 | ); | ||
| 2507 | |||
| 2508 | // This test intentionally avoids `mockCommandSpawnerLayer` so the real | ||
| 2509 | // `probeCodexAppServerProvider` path runs — including the full | ||
| 2510 | // `codex app-server` RPC handshake via `CodexClient.layerChildProcess`. | ||
| 2511 | // We point `binaryPath` at a name that cannot exist on any machine so | ||
| 2512 | // the real `ChildProcessSpawner` deterministically returns ENOENT; the | ||
| 2513 | // probe wraps that as `CodexAppServerSpawnError` and | ||
| 2514 | // `checkCodexProviderStatus` turns it into the user-visible "not | ||
| 2515 | // installed" error snapshot. If the aggregator's `syncLiveSources` | ||
| 2516 | // breaks — the `codex_personal`-never-probes bug we are guarding | ||
| 2517 | // against — that snapshot never lands in `getProviders` and the | ||
| 2518 | // assertions below fail. | ||
| 2519 | it.effect("propagates real Codex probe failures to the aggregator at boot", () => | ||
| 2520 | Effect.gen(function* () { | ||
| 2521 | const missingBinary = `t3code_codex_missing_`; | ||
| 2522 | const serverSettings = yield* makeMutableServerSettingsService( | ||
| 2523 | decodeServerSettings( | ||
| 2524 | deepMerge(encodedDefaultServerSettings, { | ||
| 2525 | providers: { | ||
| 2526 | // Disable every built-in probe that would otherwise spawn | ||
| 2527 | // on the CI host. `enabled: false` short-circuits each | ||
| 2528 | // driver's probe *before* it touches the spawner, so the | ||
| 2529 | // test environment stays isolated from the dev | ||
| 2530 | // machine's PATH. | ||
| 2531 | codex: { enabled: false }, | ||
| 2532 | claudeAgent: { enabled: false }, | ||
| 2533 | cursor: { enabled: false }, | ||
| 2534 | grok: { enabled: false }, | ||
| 2535 | opencode: { enabled: false }, | ||
| 2536 | }, | ||
| 2537 | // `providerInstances` keys are branded `ProviderInstanceId`; | ||
| 2538 | // the branded index signature rejects plain string literals | ||
| 2539 | // at the TS level even though the runtime schema happily | ||
| 2540 | // accepts + decodes them. Cast the patch to `unknown` so | ||
| 2541 | // the `Schema.decodeSync` below does the real validation. | ||
| 2542 | providerInstances: { | ||
| 2543 | // Matches the shape the user had in `.t3/dev/settings.json` | ||
| 2544 | // when the bug was reported: a custom enabled Codex instance | ||
| 2545 | // pointing at a binary the server has to actually spawn. | ||
| 2546 | codex_personal: { | ||
| 2547 | driver: "codex", | ||
| 2548 | displayName: "Codex Personal", | ||
| 2549 | enabled: true, | ||
| 2550 | config: { | ||
| 2551 | binaryPath: missingBinary, | ||
| 2552 | homePath: `/tmp/${missingBinary}_home`, | ||
| 2553 | }, | ||
| 2679 | }, | 2554 | }, |
| 2680 | }), | 2555 | } as unknown as ContractServerSettings["providerInstances"], |
| 2556 | }), | ||
| 2557 | ), | ||
| 2558 | ); | ||
| 2559 | const scope = yield* Scope.make(); | ||
| 2560 | yield* Effect.addFinalizer(() => Scope.close(scope, Exit.void)); | ||
| 2561 | const layerProviderRegistry = ProviderRegistryLayer.layer.pipe( | ||
| 2562 | Layer.provideMerge(ProviderInstanceRegistryHydration.layer), | ||
| 2563 | Layer.provideMerge(AntigravityInstallation.AntigravityInstallation.layer), | ||
| 2564 | Layer.provideMerge( | ||
| 2565 | Layer.succeed(ServerSettingsModule.ServerSettingsService, serverSettings), | ||
| 2566 | ), | ||
| 2567 | Layer.provideMerge(ServerSecretStore.layer), | ||
| 2568 | Layer.provideMerge( | ||
| 2569 | ServerConfig.layerTest(process.cwd(), { | ||
| 2570 | prefix: "t3-provider-registry-", | ||
| 2571 | }), | ||
| 2572 | ), | ||
| 2573 | Layer.provideMerge(layerTestHttpClient), | ||
| 2574 | Layer.provideMerge( | ||
| 2575 | Layer.succeed( | ||
| 2576 | ProviderEventLoggers.ProviderEventLoggers, | ||
| 2577 | ProviderEventLoggers.NoOpProviderEventLoggers, | ||
| 2681 | ), | 2578 | ), |
| 2579 | ), | ||
| 2580 | Layer.provideMerge(ModelManifest.layerTest), | ||
| 2581 | Layer.provideMerge(ResetCreditCoordinator.layerTest), | ||
| 2582 | Layer.provideMerge( | ||
| 2583 | OpenCodeRuntime.layer.pipe(Layer.provide(OpenCodeServerLedger.layerTest)), | ||
| 2584 | ), | ||
| 2585 | Layer.provideMerge(layerBackgroundPolicyAlwaysRun), | ||
| 2586 | // NO spawner mock — `ChildProcessSpawner` is supplied by the | ||
| 2587 | // outer `NodeServices.layer` on `it.layer(...)` and will | ||
| 2588 | // genuinely spawn a subprocess. The missing-binary ENOENT is | ||
| 2589 | // what exercises the same failure mode as a misconfigured | ||
| 2590 | // production `binaryPath`. | ||
| 2591 | ); | ||
| 2592 | const runtimeServices = yield* Layer.build(layerProviderRegistry).pipe( | ||
| 2593 | Scope.provide(scope), | ||
| 2594 | ); | ||
| 2595 | |||
| 2596 | yield* Effect.gen(function* () { | ||
| 2597 | const registry = yield* ProviderRegistry.ProviderRegistry; | ||
| 2598 | let providers = yield* registry.getProviders; | ||
| 2599 | for ( | ||
| 2600 | let attempts = 0; | ||
| 2601 | attempts < 50 && | ||
| 2602 | providers.find((provider) => provider.instanceId === "codex_personal")?.status !== | ||
| 2603 | "error"; | ||
| 2604 | attempts += 1 | ||
| 2605 | ) { | ||
| 2606 | yield* Effect.yieldNow; | ||
| 2607 | providers = yield* registry.getProviders; | ||
| 2608 | } | ||
| 2609 | const codexPersonal = providers.find( | ||
| 2610 | (provider) => provider.instanceId === "codex_personal", | ||
| 2682 | ); | 2611 | ); |
| 2683 | const serverSettings = { | 2612 | assert.notStrictEqual( |
| 2684 | ...mutableServerSettings, | 2613 | codexPersonal, |
| 2685 | streamChanges: Stream.unwrap( | 2614 | undefined, |
| 2686 | Deferred.await(allowLazySettingsStream).pipe( | 2615 | `Expected the aggregator to know about codex_personal; instead saw: ${providers |
| 2687 | Effect.as(mutableServerSettings.streamChanges), | 2616 | .map((provider) => provider.instanceId) |
| 2688 | ), | 2617 | .join(", ")}`, |
| 2689 | ), | 2618 | ); |
| 2690 | } satisfies ServerSettingsModule.ServerSettingsService["Service"]; | 2619 | assert.strictEqual( |
| 2691 | const scope = yield* Scope.make(); | 2620 | codexPersonal?.status, |
| 2692 | yield* Effect.addFinalizer(() => Scope.close(scope, Exit.void)); | 2621 | "error", |
| 2693 | const providerRegistryLayer = ProviderRegistryLive.pipe( | 2622 | "Real Codex probe against a missing binary should surface as 'error' in the aggregator", |
| 2694 | Layer.provideMerge(ProviderInstanceRegistryHydrationLive), | 2623 | ); |
| 2695 | Layer.provideMerge(AntigravityInstallation.AntigravityInstallation.layer), | 2624 | assert.strictEqual(codexPersonal?.installed, false); |
| 2696 | Layer.provideMerge( | 2625 | assert.include(codexPersonal?.message, missingBinary); |
| 2697 | Layer.succeed(ServerSettingsModule.ServerSettingsService, serverSettings), | 2626 | assert.include(codexPersonal?.message, "Settings → Providers → Codex → Binary path"); |
| 2698 | ), | 2627 | }).pipe(Effect.provide(runtimeServices)); |
| 2699 | Layer.provideMerge(ServerSecretStore.layer), | 2628 | }), |
| 2700 | Layer.provideMerge( | 2629 | ); |
| 2701 | ServerConfig.layerTest(process.cwd(), { | 2630 | |
| 2702 | prefix: "t3-provider-registry-", | 2631 | // A binary path change must rebuild Codex and publish its new probe result. |
| 2703 | }), | 2632 | it.effect("re-probes when settings change the codex binaryPath", () => |
| 2633 | Effect.gen(function* () { | ||
| 2634 | const firstMissing = `t3code_codex_first_`; | ||
| 2635 | const secondMissing = `t3code_codex_second_`; | ||
| 2636 | const spawnedCommands: Array<string> = []; | ||
| 2637 | const secondProbeStarted = yield* Deferred.make<void>(); | ||
| 2638 | const releaseSecondProbe = yield* Deferred.make<void>(); | ||
| 2639 | const allowLazySettingsStream = yield* Deferred.make<void>(); | ||
| 2640 | const mutableServerSettings = yield* makeMutableServerSettingsService( | ||
| 2641 | decodeServerSettings( | ||
| 2642 | deepMerge(encodedDefaultServerSettings, { | ||
| 2643 | providers: { | ||
| 2644 | codex: { enabled: true, binaryPath: firstMissing }, | ||
| 2645 | claudeAgent: { enabled: false }, | ||
| 2646 | cursor: { enabled: false }, | ||
| 2647 | grok: { enabled: false }, | ||
| 2648 | opencode: { enabled: false }, | ||
| 2649 | }, | ||
| 2650 | }), | ||
| 2651 | ), | ||
| 2652 | ); | ||
| 2653 | const serverSettings = { | ||
| 2654 | ...mutableServerSettings, | ||
| 2655 | streamChanges: Stream.unwrap( | ||
| 2656 | Deferred.await(allowLazySettingsStream).pipe( | ||
| 2657 | Effect.as(mutableServerSettings.streamChanges), | ||
| 2704 | ), | 2658 | ), |
| 2705 | Layer.provideMerge(TestHttpClientLive), | 2659 | ), |
| 2706 | Layer.provideMerge( | 2660 | } satisfies ServerSettingsModule.ServerSettingsService["Service"]; |
| 2707 | Layer.succeed( | 2661 | const scope = yield* Scope.make(); |
| 2708 | ProviderEventLoggers.ProviderEventLoggers, | 2662 | yield* Effect.addFinalizer(() => Scope.close(scope, Exit.void)); |
| 2709 | ProviderEventLoggers.NoOpProviderEventLoggers, | 2663 | const layerProviderRegistry = ProviderRegistryLayer.layer.pipe( |
| 2710 | ), | 2664 | Layer.provideMerge(ProviderInstanceRegistryHydration.layer), |
| 2665 | Layer.provideMerge(AntigravityInstallation.AntigravityInstallation.layer), | ||
| 2666 | Layer.provideMerge( | ||
| 2667 | Layer.succeed(ServerSettingsModule.ServerSettingsService, serverSettings), | ||
| 2668 | ), | ||
| 2669 | Layer.provideMerge(ServerSecretStore.layer), | ||
| 2670 | Layer.provideMerge( | ||
| 2671 | ServerConfig.layerTest(process.cwd(), { | ||
| 2672 | prefix: "t3-provider-registry-", | ||
| 2673 | }), | ||
| 2674 | ), | ||
| 2675 | Layer.provideMerge(layerTestHttpClient), | ||
| 2676 | Layer.provideMerge( | ||
| 2677 | Layer.succeed( | ||
| 2678 | ProviderEventLoggers.ProviderEventLoggers, | ||
| 2679 | ProviderEventLoggers.NoOpProviderEventLoggers, | ||
| 2711 | ), | 2680 | ), |
| 2712 | Layer.provideMerge(ModelManifest.layerTest), | 2681 | ), |
| 2713 | Layer.provideMerge(ResetCreditCoordinator.layerTest), | 2682 | Layer.provideMerge(ModelManifest.layerTest), |
| 2714 | Layer.provideMerge( | 2683 | Layer.provideMerge(ResetCreditCoordinator.layerTest), |
| 2715 | OpenCodeRuntime.OpenCodeRuntimeLive.pipe( | 2684 | Layer.provideMerge( |
| 2716 | Layer.provide(OpenCodeServerLedger.layerTest), | 2685 | OpenCodeRuntime.layer.pipe(Layer.provide(OpenCodeServerLedger.layerTest)), |
| 2717 | ), | 2686 | ), |
| 2687 | Layer.updateService(ChildProcessSpawner.ChildProcessSpawner, (spawner) => | ||
| 2688 | ChildProcessSpawner.make((command) => { | ||
| 2689 | if (command._tag !== "StandardCommand") return spawner.spawn(command); | ||
| 2690 | spawnedCommands.push(command.command); | ||
| 2691 | const beforeSpawn = | ||
| 2692 | command.command === secondMissing | ||
| 2693 | ? Deferred.succeed(secondProbeStarted, undefined).pipe( | ||
| 2694 | Effect.andThen(Deferred.await(releaseSecondProbe)), | ||
| 2695 | ) | ||
| 2696 | : Effect.void; | ||
| 2697 | return beforeSpawn.pipe(Effect.andThen(spawner.spawn(command))); | ||
| 2698 | }), | ||
| 2699 | ), | ||
| 2700 | Layer.provideMerge(NodeServices.layer), | ||
| 2701 | Layer.provideMerge(layerBackgroundPolicyAlwaysRun), | ||
| 2702 | ); | ||
| 2703 | const runtimeServices = yield* Layer.build(layerProviderRegistry).pipe( | ||
| 2704 | Scope.provide(scope), | ||
| 2705 | ); | ||
| 2706 | |||
| 2707 | yield* Effect.gen(function* () { | ||
| 2708 | const registry = yield* ProviderRegistry.ProviderRegistry; | ||
| 2709 | const codexSnapshots = registry.streamChanges.pipe( | ||
| 2710 | Stream.map((providers) => | ||
| 2711 | providers.find((provider) => provider.instanceId === "codex"), | ||
| 2718 | ), | 2712 | ), |
| 2719 | Layer.updateService(ChildProcessSpawner.ChildProcessSpawner, (spawner) => | 2713 | Stream.filter((provider): provider is ServerProvider => provider !== undefined), |
| 2720 | ChildProcessSpawner.make((command) => { | 2714 | ); |
| 2721 | if (command._tag !== "StandardCommand") return spawner.spawn(command); | 2715 | const firstError = yield* Stream.toPull( |
| 2722 | spawnedCommands.push(command.command); | 2716 | codexSnapshots.pipe(Stream.filter((provider) => provider.status === "error")), |
| 2723 | const beforeSpawn = | 2717 | ); |
| 2724 | command.command === secondMissing | 2718 | const currentCodex = (yield* registry.getProviders).find( |
| 2725 | ? Deferred.succeed(secondProbeStarted, undefined).pipe( | 2719 | (provider) => provider.instanceId === "codex", |
| 2726 | Effect.andThen(Deferred.await(releaseSecondProbe)), | 2720 | ); |
| 2727 | ) | 2721 | const initialCodex = |
| 2728 | : Effect.void; | 2722 | currentCodex?.status === "error" ? currentCodex : (yield* firstError)[0]; |
| 2729 | return beforeSpawn.pipe(Effect.andThen(spawner.spawn(command))); | 2723 | assert.strictEqual(initialCodex?.status, "error"); |
| 2730 | }), | 2724 | assert.strictEqual(initialCodex?.installed, false); |
| 2725 | assert.deepStrictEqual(spawnedCommands, [firstMissing]); | ||
| 2726 | |||
| 2727 | const pendingRebuild = yield* Stream.toPull( | ||
| 2728 | codexSnapshots.pipe( | ||
| 2729 | Stream.filter((provider) => provider.status === "warning" && !provider.installed), | ||
| 2731 | ), | 2730 | ), |
| 2732 | Layer.provideMerge(NodeServices.layer), | ||
| 2733 | Layer.provideMerge(BackgroundPolicyAlwaysRunLayer), | ||
| 2734 | ); | 2731 | ); |
| 2735 | const runtimeServices = yield* Layer.build(providerRegistryLayer).pipe( | 2732 | yield* serverSettings.updateSettings({ |
| 2736 | Scope.provide(scope), | 2733 | providers: { |
| 2734 | codex: { enabled: true, binaryPath: secondMissing }, | ||
| 2735 | }, | ||
| 2736 | }); | ||
| 2737 | // Start the lazy stream only after publishing. A watcher that did | ||
| 2738 | // not subscribe before forking has already lost this update. | ||
| 2739 | yield* Deferred.succeed(allowLazySettingsStream, undefined); | ||
| 2740 | |||
| 2741 | // Hold the second probe until the aggregator sees the rebuilt | ||
| 2742 | // instance. Its next error must come from the new executable. | ||
| 2743 | yield* Deferred.await(secondProbeStarted); | ||
| 2744 | yield* pendingRebuild; | ||
| 2745 | const rebuiltError = yield* Stream.toPull( | ||
| 2746 | codexSnapshots.pipe(Stream.filter((provider) => provider.status === "error")), | ||
| 2737 | ); | 2747 | ); |
| 2748 | yield* Deferred.succeed(releaseSecondProbe, undefined); | ||
| 2749 | const [reprobedCodex] = yield* rebuiltError; | ||
| 2750 | assert.deepStrictEqual(spawnedCommands, [firstMissing, secondMissing]); | ||
| 2751 | assert.strictEqual(reprobedCodex?.status, "error"); | ||
| 2752 | assert.strictEqual(reprobedCodex?.installed, false); | ||
| 2753 | }).pipe(Effect.provide(runtimeServices)); | ||
| 2754 | }), | ||
| 2755 | ); | ||
| 2738 | 2756 | ||
| 2739 | yield* Effect.gen(function* () { | 2757 | it.effect("includes unavailable instance snapshots in getProviders", () => |
| 2740 | const registry = yield* ProviderRegistry.ProviderRegistry; | 2758 | Effect.gen(function* () { |
| 2741 | const codexSnapshots = registry.streamChanges.pipe( | 2759 | const serverSettings = yield* makeMutableServerSettingsService( |
| 2742 | Stream.map((providers) => | 2760 | decodeServerSettings( |
| 2743 | providers.find((provider) => provider.instanceId === "codex"), | 2761 | deepMerge(encodedDefaultServerSettings, { |
| 2744 | ), | ||
| 2745 | Stream.filter((provider): provider is ServerProvider => provider !== undefined), | ||
| 2746 | ); | ||
| 2747 | const firstError = yield* Stream.toPull( | ||
| 2748 | codexSnapshots.pipe(Stream.filter((provider) => provider.status === "error")), | ||
| 2749 | ); | ||
| 2750 | const currentCodex = (yield* registry.getProviders).find( | ||
| 2751 | (provider) => provider.instanceId === "codex", | ||
| 2752 | ); | ||
| 2753 | const initialCodex = | ||
| 2754 | currentCodex?.status === "error" ? currentCodex : (yield* firstError)[0]; | ||
| 2755 | assert.strictEqual(initialCodex?.status, "error"); | ||
| 2756 | assert.strictEqual(initialCodex?.installed, false); | ||
| 2757 | assert.deepStrictEqual(spawnedCommands, [firstMissing]); | ||
| 2758 | |||
| 2759 | const pendingRebuild = yield* Stream.toPull( | ||
| 2760 | codexSnapshots.pipe( | ||
| 2761 | Stream.filter((provider) => provider.status === "warning" && !provider.installed), | ||
| 2762 | ), | ||
| 2763 | ); | ||
| 2764 | yield* serverSettings.updateSettings({ | ||
| 2765 | providers: { | 2762 | providers: { |
| 2766 | codex: { enabled: true, binaryPath: secondMissing }, | 2763 | codex: { enabled: false }, |
| 2764 | claudeAgent: { enabled: false }, | ||
| 2765 | cursor: { enabled: false }, | ||
| 2766 | grok: { enabled: false }, | ||
| 2767 | opencode: { enabled: false }, | ||
| 2767 | }, | 2768 | }, |
| 2768 | }); | 2769 | providerInstances: { |
| 2769 | // Start the lazy stream only after publishing. A watcher that did | 2770 | ghost_main: { |
| 2770 | // not subscribe before forking has already lost this update. | 2771 | driver: "ghostDriver", |
| 2771 | yield* Deferred.succeed(allowLazySettingsStream, undefined); | 2772 | displayName: "A fork-only driver we don't ship", |
| 2772 | 2773 | enabled: false, | |
| 2773 | // Hold the second probe until the aggregator sees the rebuilt | 2774 | config: { arbitrary: "payload" }, |
| 2774 | // instance. Its next error must come from the new executable. | 2775 | }, |
| 2775 | yield* Deferred.await(secondProbeStarted); | 2776 | } as unknown as ContractServerSettings["providerInstances"], |
| 2776 | yield* pendingRebuild; | 2777 | }), |
| 2777 | const rebuiltError = yield* Stream.toPull( | 2778 | ), |
| 2778 | codexSnapshots.pipe(Stream.filter((provider) => provider.status === "error")), | 2779 | ); |
| 2779 | ); | 2780 | const scope = yield* Scope.make(); |
| 2780 | yield* Deferred.succeed(releaseSecondProbe, undefined); | 2781 | yield* Effect.addFinalizer(() => Scope.close(scope, Exit.void)); |
| 2781 | const [reprobedCodex] = yield* rebuiltError; | 2782 | const layerProviderRegistry = ProviderRegistryLayer.layer.pipe( |
| 2782 | assert.deepStrictEqual(spawnedCommands, [firstMissing, secondMissing]); | 2783 | Layer.provideMerge(ProviderInstanceRegistryHydration.layer), |
| 2783 | assert.strictEqual(reprobedCodex?.status, "error"); | 2784 | Layer.provideMerge(AntigravityInstallation.AntigravityInstallation.layer), |
| 2784 | assert.strictEqual(reprobedCodex?.installed, false); | 2785 | Layer.provideMerge( |
| 2785 | }).pipe(Effect.provide(runtimeServices)); | 2786 | Layer.succeed(ServerSettingsModule.ServerSettingsService, serverSettings), |
| 2786 | }), | 2787 | ), |
| 2787 | ); | 2788 | Layer.provideMerge(ServerSecretStore.layer), |
| 2789 | Layer.provideMerge( | ||
| 2790 | ServerConfig.layerTest(process.cwd(), { | ||
| 2791 | prefix: "t3-provider-registry-", | ||
| 2792 | }), | ||
| 2793 | ), | ||
| 2794 | Layer.provideMerge(layerTestHttpClient), | ||
| 2795 | Layer.provideMerge( | ||
| 2796 | Layer.succeed( | ||
| 2797 | ProviderEventLoggers.ProviderEventLoggers, | ||
| 2798 | ProviderEventLoggers.NoOpProviderEventLoggers, | ||
| 2799 | ), | ||
| 2800 | ), | ||
| 2801 | Layer.provideMerge(ModelManifest.layerTest), | ||
| 2802 | Layer.provideMerge(ResetCreditCoordinator.layerTest), | ||
| 2803 | Layer.provideMerge( | ||
| 2804 | OpenCodeRuntime.layer.pipe(Layer.provide(OpenCodeServerLedger.layerTest)), | ||
| 2805 | ), | ||
| 2806 | Layer.provideMerge(NodeServices.layer), | ||
| 2807 | Layer.provideMerge(layerBackgroundPolicyAlwaysRun), | ||
| 2808 | ); | ||
| 2809 | const runtimeServices = yield* Layer.build(layerProviderRegistry).pipe( | ||
| 2810 | Scope.provide(scope), | ||
| 2811 | ); | ||
| 2812 | |||
| 2813 | yield* Effect.gen(function* () { | ||
| 2814 | const registry = yield* ProviderRegistry.ProviderRegistry; | ||
| 2815 | const providers = yield* registry.getProviders; | ||
| 2816 | const ghost = providers.find((provider) => provider.instanceId === "ghost_main"); | ||
| 2817 | |||
| 2818 | assert.notStrictEqual(ghost, undefined); | ||
| 2819 | assert.strictEqual(ghost?.driver, "ghostDriver"); | ||
| 2820 | assert.strictEqual(ghost?.availability, "unavailable"); | ||
| 2821 | assert.match(ghost?.unavailableReason ?? "", /ghostDriver/); | ||
| 2822 | }).pipe(Effect.provide(runtimeServices)); | ||
| 2823 | }), | ||
| 2824 | ); | ||
| 2788 | 2825 | ||
| 2789 | it.effect("includes unavailable instance snapshots in getProviders", () => | 2826 | it.effect( |
| 2827 | "keeps Cursor disabled and skips provider probing when settings use their defaults", | ||
| 2828 | () => | ||
| 2790 | Effect.gen(function* () { | 2829 | Effect.gen(function* () { |
| 2791 | const serverSettings = yield* makeMutableServerSettingsService( | 2830 | const serverSettings = yield* makeMutableServerSettingsService( |
| 2792 | decodeServerSettings( | 2831 | decodeServerSettings( |
| 2793 | deepMerge(encodedDefaultServerSettings, { | 2832 | deepMerge(encodedDefaultServerSettings, { |
| 2794 | providers: { | 2833 | providers: { |
| 2795 | codex: { enabled: false }, | 2834 | codex: { |
| 2796 | claudeAgent: { enabled: false }, | 2835 | enabled: false, |
| 2797 | cursor: { enabled: false }, | 2836 | }, |
| 2798 | grok: { enabled: false }, | 2837 | grok: { |
| 2799 | opencode: { enabled: false }, | ||
| 2800 | }, | ||
| 2801 | providerInstances: { | ||
| 2802 | ghost_main: { | ||
| 2803 | driver: "ghostDriver", | ||
| 2804 | displayName: "A fork-only driver we don't ship", | ||
| 2805 | enabled: false, | 2838 | enabled: false, |
| 2806 | config: { arbitrary: "payload" }, | ||
| 2807 | }, | 2839 | }, |
| 2808 | } as unknown as ContractServerSettings["providerInstances"], | 2840 | }, |
| 2809 | }), | 2841 | }), |
| 2810 | ), | 2842 | ), |
| 2811 | ); | 2843 | ); |
| 2844 | let cursorSpawned = false; | ||
| 2812 | const scope = yield* Scope.make(); | 2845 | const scope = yield* Scope.make(); |
| 2813 | yield* Effect.addFinalizer(() => Scope.close(scope, Exit.void)); | 2846 | yield* Effect.addFinalizer(() => Scope.close(scope, Exit.void)); |
| 2814 | const providerRegistryLayer = ProviderRegistryLive.pipe( | 2847 | const layerProviderRegistry = ProviderRegistryLayer.layer.pipe( |
| 2815 | Layer.provideMerge(ProviderInstanceRegistryHydrationLive), | 2848 | Layer.provideMerge(ProviderInstanceRegistryHydration.layer), |
| 2816 | Layer.provideMerge(AntigravityInstallation.AntigravityInstallation.layer), | 2849 | Layer.provideMerge(AntigravityInstallation.AntigravityInstallation.layer), |
| 2817 | Layer.provideMerge( | 2850 | Layer.provideMerge( |
| 2818 | Layer.succeed(ServerSettingsModule.ServerSettingsService, serverSettings), | 2851 | Layer.succeed(ServerSettingsModule.ServerSettingsService, serverSettings), |
| ... | @@ -2823,7 +2856,7 @@ it.layer(Layer.mergeAll(TestNodeServices, ServerSettingsModule.layerTest(), Test | ... | @@ -2823,7 +2856,7 @@ it.layer(Layer.mergeAll(TestNodeServices, ServerSettingsModule.layerTest(), Test |
| 2823 | prefix: "t3-provider-registry-", | 2856 | prefix: "t3-provider-registry-", |
| 2824 | }), | 2857 | }), |
| 2825 | ), | 2858 | ), |
| 2826 | Layer.provideMerge(TestHttpClientLive), | 2859 | Layer.provideMerge(layerTestHttpClient), |
| 2827 | Layer.provideMerge( | 2860 | Layer.provideMerge( |
| 2828 | Layer.succeed( | 2861 | Layer.succeed( |
| 2829 | ProviderEventLoggers.ProviderEventLoggers, | 2862 | ProviderEventLoggers.ProviderEventLoggers, |
| ... | @@ -2833,546 +2866,473 @@ it.layer(Layer.mergeAll(TestNodeServices, ServerSettingsModule.layerTest(), Test | ... | @@ -2833,546 +2866,473 @@ it.layer(Layer.mergeAll(TestNodeServices, ServerSettingsModule.layerTest(), Test |
| 2833 | Layer.provideMerge(ModelManifest.layerTest), | 2866 | Layer.provideMerge(ModelManifest.layerTest), |
| 2834 | Layer.provideMerge(ResetCreditCoordinator.layerTest), | 2867 | Layer.provideMerge(ResetCreditCoordinator.layerTest), |
| 2835 | Layer.provideMerge( | 2868 | Layer.provideMerge( |
| 2836 | OpenCodeRuntime.OpenCodeRuntimeLive.pipe( | 2869 | OpenCodeRuntime.layer.pipe(Layer.provide(OpenCodeServerLedger.layerTest)), |
| 2837 | Layer.provide(OpenCodeServerLedger.layerTest), | 2870 | ), |
| 2838 | ), | 2871 | Layer.provideMerge(layerBackgroundPolicyAlwaysRun), |
| 2872 | Layer.provideMerge( | ||
| 2873 | layerMockCommandSpawner((command, args) => { | ||
| 2874 | if (command === "cursor-agent") { | ||
| 2875 | cursorSpawned = true; | ||
| 2876 | } | ||
| 2877 | const joined = args.join(" "); | ||
| 2878 | if (joined === "--version") { | ||
| 2879 | return { | ||
| 2880 | stdout: `${command} 1.0.0\n`, | ||
| 2881 | stderr: "", | ||
| 2882 | code: 0, | ||
| 2883 | }; | ||
| 2884 | } | ||
| 2885 | if (joined === "auth status") { | ||
| 2886 | return { | ||
| 2887 | stdout: '{"authenticated":true}\n', | ||
| 2888 | stderr: "", | ||
| 2889 | code: 0, | ||
| 2890 | }; | ||
| 2891 | } | ||
| 2892 | throw new Error(`Unexpected args: ${command} ${joined}`); | ||
| 2893 | }), | ||
| 2839 | ), | 2894 | ), |
| 2840 | Layer.provideMerge(NodeServices.layer), | ||
| 2841 | Layer.provideMerge(BackgroundPolicyAlwaysRunLayer), | ||
| 2842 | ); | ||
| 2843 | const runtimeServices = yield* Layer.build(providerRegistryLayer).pipe( | ||
| 2844 | Scope.provide(scope), | ||
| 2845 | ); | 2895 | ); |
| 2896 | const runtimeServices = yield* Layer.build( | ||
| 2897 | Layer.mergeAll( | ||
| 2898 | Layer.succeed(ServerSettingsModule.ServerSettingsService, serverSettings), | ||
| 2899 | layerProviderRegistry, | ||
| 2900 | ), | ||
| 2901 | ).pipe(Scope.provide(scope)); | ||
| 2846 | 2902 | ||
| 2847 | yield* Effect.gen(function* () { | 2903 | yield* Effect.gen(function* () { |
| 2848 | const registry = yield* ProviderRegistry.ProviderRegistry; | 2904 | const registry = yield* ProviderRegistry.ProviderRegistry; |
| 2849 | const providers = yield* registry.getProviders; | 2905 | const providers = yield* registry.getProviders; |
| 2850 | const ghost = providers.find((provider) => provider.instanceId === "ghost_main"); | 2906 | const cursorProvider = providers.find( |
| 2907 | (provider) => provider.instanceId === ProviderInstanceId.make("cursor"), | ||
| 2908 | ); | ||
| 2851 | 2909 | ||
| 2852 | assert.notStrictEqual(ghost, undefined); | 2910 | assert.deepStrictEqual(providers.map((provider) => provider.instanceId).toSorted(), [ |
| 2853 | assert.strictEqual(ghost?.driver, "ghostDriver"); | 2911 | "antigravity", |
| 2854 | assert.strictEqual(ghost?.availability, "unavailable"); | 2912 | "claudeAgent", |
| 2855 | assert.match(ghost?.unavailableReason ?? "", /ghostDriver/); | 2913 | "codex", |
| 2914 | "cursor", | ||
| 2915 | "grok", | ||
| 2916 | "opencode", | ||
| 2917 | "pi", | ||
| 2918 | ]); | ||
| 2919 | assert.strictEqual(cursorProvider?.enabled, false); | ||
| 2920 | assert.strictEqual(cursorProvider?.status, "disabled"); | ||
| 2921 | assert.strictEqual(cursorProvider?.message, "Cursor is disabled in T3 Code settings."); | ||
| 2922 | assert.strictEqual(cursorSpawned, false); | ||
| 2856 | }).pipe(Effect.provide(runtimeServices)); | 2923 | }).pipe(Effect.provide(runtimeServices)); |
| 2857 | }), | 2924 | }), |
| 2858 | ); | 2925 | ); |
| 2859 | 2926 | ||
| 2860 | it.effect( | 2927 | it.effect("skips codex probes entirely when the provider is disabled", () => |
| 2861 | "keeps Cursor disabled and skips provider probing when settings use their defaults", | 2928 | Effect.gen(function* () { |
| 2862 | () => | 2929 | const status = yield* checkCodexProviderStatus(disabledCodexSettings).pipe( |
| 2863 | Effect.gen(function* () { | 2930 | Effect.provide(layerFailingSpawner("spawn codex ENOENT")), |
| 2864 | const serverSettings = yield* makeMutableServerSettingsService( | 2931 | ); |
| 2865 | decodeServerSettings( | 2932 | assert.strictEqual(status.enabled, false); |
| 2866 | deepMerge(encodedDefaultServerSettings, { | 2933 | assert.strictEqual(status.status, "disabled"); |
| 2867 | providers: { | 2934 | assert.strictEqual(status.installed, false); |
| 2868 | codex: { | 2935 | assert.strictEqual(status.message, "Codex is disabled in T3 Code settings."); |
| 2869 | enabled: false, | 2936 | }), |
| 2870 | }, | 2937 | ); |
| 2871 | grok: { | 2938 | }); |
| 2872 | enabled: false, | ||
| 2873 | }, | ||
| 2874 | }, | ||
| 2875 | }), | ||
| 2876 | ), | ||
| 2877 | ); | ||
| 2878 | let cursorSpawned = false; | ||
| 2879 | const scope = yield* Scope.make(); | ||
| 2880 | yield* Effect.addFinalizer(() => Scope.close(scope, Exit.void)); | ||
| 2881 | const providerRegistryLayer = ProviderRegistryLive.pipe( | ||
| 2882 | Layer.provideMerge(ProviderInstanceRegistryHydrationLive), | ||
| 2883 | Layer.provideMerge(AntigravityInstallation.AntigravityInstallation.layer), | ||
| 2884 | Layer.provideMerge( | ||
| 2885 | Layer.succeed(ServerSettingsModule.ServerSettingsService, serverSettings), | ||
| 2886 | ), | ||
| 2887 | Layer.provideMerge(ServerSecretStore.layer), | ||
| 2888 | Layer.provideMerge( | ||
| 2889 | ServerConfig.layerTest(process.cwd(), { | ||
| 2890 | prefix: "t3-provider-registry-", | ||
| 2891 | }), | ||
| 2892 | ), | ||
| 2893 | Layer.provideMerge(TestHttpClientLive), | ||
| 2894 | Layer.provideMerge( | ||
| 2895 | Layer.succeed( | ||
| 2896 | ProviderEventLoggers.ProviderEventLoggers, | ||
| 2897 | ProviderEventLoggers.NoOpProviderEventLoggers, | ||
| 2898 | ), | ||
| 2899 | ), | ||
| 2900 | Layer.provideMerge(ModelManifest.layerTest), | ||
| 2901 | Layer.provideMerge(ResetCreditCoordinator.layerTest), | ||
| 2902 | Layer.provideMerge( | ||
| 2903 | OpenCodeRuntime.OpenCodeRuntimeLive.pipe( | ||
| 2904 | Layer.provide(OpenCodeServerLedger.layerTest), | ||
| 2905 | ), | ||
| 2906 | ), | ||
| 2907 | Layer.provideMerge(BackgroundPolicyAlwaysRunLayer), | ||
| 2908 | Layer.provideMerge( | ||
| 2909 | mockCommandSpawnerLayer((command, args) => { | ||
| 2910 | if (command === "cursor-agent") { | ||
| 2911 | cursorSpawned = true; | ||
| 2912 | } | ||
| 2913 | const joined = args.join(" "); | ||
| 2914 | if (joined === "--version") { | ||
| 2915 | return { | ||
| 2916 | stdout: `${command} 1.0.0\n`, | ||
| 2917 | stderr: "", | ||
| 2918 | code: 0, | ||
| 2919 | }; | ||
| 2920 | } | ||
| 2921 | if (joined === "auth status") { | ||
| 2922 | return { | ||
| 2923 | stdout: '{"authenticated":true}\n', | ||
| 2924 | stderr: "", | ||
| 2925 | code: 0, | ||
| 2926 | }; | ||
| 2927 | } | ||
| 2928 | throw new Error(`Unexpected args: ${command} ${joined}`); | ||
| 2929 | }), | ||
| 2930 | ), | ||
| 2931 | ); | ||
| 2932 | const runtimeServices = yield* Layer.build( | ||
| 2933 | Layer.mergeAll( | ||
| 2934 | Layer.succeed(ServerSettingsModule.ServerSettingsService, serverSettings), | ||
| 2935 | providerRegistryLayer, | ||
| 2936 | ), | ||
| 2937 | ).pipe(Scope.provide(scope)); | ||
| 2938 | 2939 | ||
| 2939 | yield* Effect.gen(function* () { | 2940 | // ── checkClaudeProviderStatus tests ────────────────────────── |
| 2940 | const registry = yield* ProviderRegistry.ProviderRegistry; | ||
| 2941 | const providers = yield* registry.getProviders; | ||
| 2942 | const cursorProvider = providers.find( | ||
| 2943 | (provider) => provider.instanceId === ProviderInstanceId.make("cursor"), | ||
| 2944 | ); | ||
| 2945 | 2941 | ||
| 2946 | assert.deepStrictEqual(providers.map((provider) => provider.instanceId).toSorted(), [ | 2942 | describe("checkClaudeProviderStatus", () => { |
| 2947 | "antigravity", | 2943 | it.effect("returns ready when claude is installed and authenticated", () => |
| 2948 | "claudeAgent", | 2944 | Effect.gen(function* () { |
| 2949 | "codex", | 2945 | const status = yield* checkClaudeProviderStatus( |
| 2950 | "cursor", | 2946 | defaultClaudeSettings, |
| 2951 | "grok", | 2947 | claudeCapabilities(), |
| 2952 | "opencode", | 2948 | ); |
| 2953 | "pi", | 2949 | assert.strictEqual(status.status, "ready"); |
| 2954 | ]); | 2950 | assert.strictEqual(status.installed, true); |
| 2955 | assert.strictEqual(cursorProvider?.enabled, false); | 2951 | assert.strictEqual(status.auth.status, "authenticated"); |
| 2956 | assert.strictEqual(cursorProvider?.status, "disabled"); | 2952 | }).pipe( |
| 2957 | assert.strictEqual( | 2953 | Effect.provide( |
| 2958 | cursorProvider?.message, | 2954 | layerMockSpawner((args) => { |
| 2959 | "Cursor is disabled in T3 Code settings.", | 2955 | const joined = args.join(" "); |
| 2960 | ); | 2956 | if (joined === "--version") return { stdout: "1.0.0\n", stderr: "", code: 0 }; |
| 2961 | assert.strictEqual(cursorSpawned, false); | 2957 | if (joined === "auth status") |
| 2962 | }).pipe(Effect.provide(runtimeServices)); | 2958 | return { |
| 2959 | stdout: '{"loggedIn":true,"authMethod":"claude.ai"}\n', | ||
| 2960 | stderr: "", | ||
| 2961 | code: 0, | ||
| 2962 | }; | ||
| 2963 | throw new Error(`Unexpected args: ${joined}`); | ||
| 2963 | }), | 2964 | }), |
| 2964 | ); | ||
| 2965 | |||
| 2966 | it.effect("skips codex probes entirely when the provider is disabled", () => | ||
| 2967 | Effect.gen(function* () { | ||
| 2968 | const status = yield* checkCodexProviderStatus(disabledCodexSettings).pipe( | ||
| 2969 | Effect.provide(failingSpawnerLayer("spawn codex ENOENT")), | ||
| 2970 | ); | ||
| 2971 | assert.strictEqual(status.enabled, false); | ||
| 2972 | assert.strictEqual(status.status, "disabled"); | ||
| 2973 | assert.strictEqual(status.installed, false); | ||
| 2974 | assert.strictEqual(status.message, "Codex is disabled in T3 Code settings."); | ||
| 2975 | }), | ||
| 2976 | ); | ||
| 2977 | }); | ||
| 2978 | |||
| 2979 | // ── checkClaudeProviderStatus tests ────────────────────────── | ||
| 2980 | |||
| 2981 | describe("checkClaudeProviderStatus", () => { | ||
| 2982 | it.effect("returns ready when claude is installed and authenticated", () => | ||
| 2983 | Effect.gen(function* () { | ||
| 2984 | const status = yield* checkClaudeProviderStatus( | ||
| 2985 | defaultClaudeSettings, | ||
| 2986 | claudeCapabilities(), | ||
| 2987 | ); | ||
| 2988 | assert.strictEqual(status.status, "ready"); | ||
| 2989 | assert.strictEqual(status.installed, true); | ||
| 2990 | assert.strictEqual(status.auth.status, "authenticated"); | ||
| 2991 | }).pipe( | ||
| 2992 | Effect.provide( | ||
| 2993 | mockSpawnerLayer((args) => { | ||
| 2994 | const joined = args.join(" "); | ||
| 2995 | if (joined === "--version") return { stdout: "1.0.0\n", stderr: "", code: 0 }; | ||
| 2996 | if (joined === "auth status") | ||
| 2997 | return { | ||
| 2998 | stdout: '{"loggedIn":true,"authMethod":"claude.ai"}\n', | ||
| 2999 | stderr: "", | ||
| 3000 | code: 0, | ||
| 3001 | }; | ||
| 3002 | throw new Error(`Unexpected args: ${joined}`); | ||
| 3003 | }), | ||
| 3004 | ), | ||
| 3005 | ), | ||
| 3006 | ); | ||
| 3007 | |||
| 3008 | it.effect("returns ready and labels Bedrock-backed Claude as authenticated", () => | ||
| 3009 | Effect.gen(function* () { | ||
| 3010 | // Bedrock authenticates via external AWS credentials, so the SDK init | ||
| 3011 | // reports only `apiProvider` with no subscription or token. | ||
| 3012 | const status = yield* checkClaudeProviderStatus( | ||
| 3013 | defaultClaudeSettings, | ||
| 3014 | claudeCapabilities({ apiProvider: "bedrock" }), | ||
| 3015 | ); | ||
| 3016 | assert.strictEqual(status.status, "ready"); | ||
| 3017 | assert.strictEqual(status.installed, true); | ||
| 3018 | assert.strictEqual(status.auth.status, "authenticated"); | ||
| 3019 | assert.strictEqual(status.auth.type, "bedrock"); | ||
| 3020 | assert.strictEqual(status.auth.label, "Amazon Bedrock"); | ||
| 3021 | }).pipe( | ||
| 3022 | Effect.provide( | ||
| 3023 | mockSpawnerLayer((args) => { | ||
| 3024 | const joined = args.join(" "); | ||
| 3025 | if (joined === "--version") return { stdout: "1.0.0\n", stderr: "", code: 0 }; | ||
| 3026 | throw new Error(`Unexpected args: ${joined}`); | ||
| 3027 | }), | ||
| 3028 | ), | ||
| 3029 | ), | 2965 | ), |
| 3030 | ); | 2966 | ), |
| 2967 | ); | ||
| 3031 | 2968 | ||
| 3032 | it.effect("returns a display label for claude subscription types", () => | 2969 | it.effect("returns ready and labels Bedrock-backed Claude as authenticated", () => |
| 3033 | Effect.gen(function* () { | 2970 | Effect.gen(function* () { |
| 3034 | const status = yield* checkClaudeProviderStatus( | 2971 | // Bedrock authenticates via external AWS credentials, so the SDK init |
| 3035 | defaultClaudeSettings, | 2972 | // reports only `apiProvider` with no subscription or token. |
| 3036 | claudeCapabilities({ subscriptionType: "maxplan" }), | 2973 | const status = yield* checkClaudeProviderStatus( |
| 3037 | ); | 2974 | defaultClaudeSettings, |
| 3038 | assert.strictEqual(status.status, "ready"); | 2975 | claudeCapabilities({ apiProvider: "bedrock" }), |
| 3039 | assert.strictEqual(status.auth.status, "authenticated"); | 2976 | ); |
| 3040 | assert.strictEqual(status.auth.type, "maxplan"); | 2977 | assert.strictEqual(status.status, "ready"); |
| 3041 | assert.strictEqual(status.auth.label, "Claude Max Subscription"); | 2978 | assert.strictEqual(status.installed, true); |
| 3042 | }).pipe( | 2979 | assert.strictEqual(status.auth.status, "authenticated"); |
| 3043 | Effect.provide( | 2980 | assert.strictEqual(status.auth.type, "bedrock"); |
| 3044 | mockSpawnerLayer((args) => { | 2981 | assert.strictEqual(status.auth.label, "Amazon Bedrock"); |
| 3045 | const joined = args.join(" "); | 2982 | }).pipe( |
| 3046 | if (joined === "--version") return { stdout: "1.0.0\n", stderr: "", code: 0 }; | 2983 | Effect.provide( |
| 3047 | if (joined === "auth status") | 2984 | layerMockSpawner((args) => { |
| 3048 | return { | 2985 | const joined = args.join(" "); |
| 3049 | stdout: '{"loggedIn":true,"authMethod":"claude.ai"}\n', | 2986 | if (joined === "--version") return { stdout: "1.0.0\n", stderr: "", code: 0 }; |
| 3050 | stderr: "", | 2987 | throw new Error(`Unexpected args: ${joined}`); |
| 3051 | code: 0, | 2988 | }), |
| 3052 | }; | ||
| 3053 | throw new Error(`Unexpected args: ${joined}`); | ||
| 3054 | }), | ||
| 3055 | ), | ||
| 3056 | ), | 2989 | ), |
| 3057 | ); | 2990 | ), |
| 2991 | ); | ||
| 3058 | 2992 | ||
| 3059 | it.effect("reads banked resets only for subscription logins", () => | 2993 | it.effect("returns a display label for claude subscription types", () => |
| 3060 | Effect.gen(function* () { | 2994 | Effect.gen(function* () { |
| 3061 | const check = (overrides: Partial<TestClaudeCapabilities>) => | 2995 | const status = yield* checkClaudeProviderStatus( |
| 3062 | checkClaudeProviderStatus( | 2996 | defaultClaudeSettings, |
| 3063 | defaultClaudeSettings, | 2997 | claudeCapabilities({ subscriptionType: "maxplan" }), |
| 3064 | () => | 2998 | ); |
| 3065 | Effect.succeed({ | 2999 | assert.strictEqual(status.status, "ready"); |
| 3066 | email: undefined, | 3000 | assert.strictEqual(status.auth.status, "authenticated"); |
| 3067 | subscriptionType: undefined, | 3001 | assert.strictEqual(status.auth.type, "maxplan"); |
| 3068 | tokenSource: undefined, | 3002 | assert.strictEqual(status.auth.label, "Claude Max Subscription"); |
| 3069 | apiProvider: undefined, | 3003 | }).pipe( |
| 3070 | slashCommands: [], | 3004 | Effect.provide( |
| 3071 | usage: { rate_limits_available: true, rate_limits: {} }, | 3005 | layerMockSpawner((args) => { |
| 3072 | ...overrides, | 3006 | const joined = args.join(" "); |
| 3073 | }), | 3007 | if (joined === "--version") return { stdout: "1.0.0\n", stderr: "", code: 0 }; |
| 3074 | undefined, | 3008 | if (joined === "auth status") |
| 3075 | undefined, | 3009 | return { |
| 3076 | undefined, | 3010 | stdout: '{"loggedIn":true,"authMethod":"claude.ai"}\n', |
| 3077 | undefined, | 3011 | stderr: "", |
| 3078 | () => Effect.succeed({ availableCount: 2 }), | 3012 | code: 0, |
| 3079 | ); | 3013 | }; |
| 3080 | const subscription = yield* check({ subscriptionType: "max" }); | 3014 | throw new Error(`Unexpected args: ${joined}`); |
| 3081 | const bedrock = yield* check({ apiProvider: "bedrock" }); | 3015 | }), |
| 3082 | assert.deepStrictEqual(subscription.usageLimits?.resetCredits, { availableCount: 2 }); | ||
| 3083 | assert.strictEqual(bedrock.usageLimits?.resetCredits, undefined); | ||
| 3084 | }).pipe( | ||
| 3085 | Effect.provide( | ||
| 3086 | mockSpawnerLayer((args) => { | ||
| 3087 | const joined = args.join(" "); | ||
| 3088 | if (joined === "--version") return { stdout: "1.0.0\n", stderr: "", code: 0 }; | ||
| 3089 | throw new Error(`Unexpected args: ${joined}`); | ||
| 3090 | }), | ||
| 3091 | ), | ||
| 3092 | ), | 3016 | ), |
| 3093 | ); | 3017 | ), |
| 3018 | ); | ||
| 3094 | 3019 | ||
| 3095 | it.effect("does not duplicate Claude in full subscription labels", () => | 3020 | it.effect("reads banked resets only for subscription logins", () => |
| 3096 | Effect.gen(function* () { | 3021 | Effect.gen(function* () { |
| 3097 | const status = yield* checkClaudeProviderStatus( | 3022 | const check = (overrides: Partial<TestClaudeCapabilities>) => |
| 3023 | checkClaudeProviderStatus( | ||
| 3098 | defaultClaudeSettings, | 3024 | defaultClaudeSettings, |
| 3099 | claudeCapabilities({ | 3025 | () => |
| 3100 | subscriptionType: "Claude Max Subscription", | 3026 | Effect.succeed({ |
| 3101 | }), | 3027 | email: undefined, |
| 3028 | subscriptionType: undefined, | ||
| 3029 | tokenSource: undefined, | ||
| 3030 | apiProvider: undefined, | ||
| 3031 | slashCommands: [], | ||
| 3032 | usage: { rate_limits_available: true, rate_limits: {} }, | ||
| 3033 | ...overrides, | ||
| 3034 | }), | ||
| 3035 | undefined, | ||
| 3036 | undefined, | ||
| 3037 | undefined, | ||
| 3038 | undefined, | ||
| 3039 | () => Effect.succeed({ availableCount: 2 }), | ||
| 3102 | ); | 3040 | ); |
| 3103 | assert.strictEqual(status.auth.status, "authenticated"); | 3041 | const subscription = yield* check({ subscriptionType: "max" }); |
| 3104 | assert.strictEqual(status.auth.type, "Claude Max Subscription"); | 3042 | const bedrock = yield* check({ apiProvider: "bedrock" }); |
| 3105 | assert.strictEqual(status.auth.label, "Claude Max Subscription"); | 3043 | assert.deepStrictEqual(subscription.usageLimits?.resetCredits, { availableCount: 2 }); |
| 3106 | }).pipe( | 3044 | assert.strictEqual(bedrock.usageLimits?.resetCredits, undefined); |
| 3107 | Effect.provide( | 3045 | }).pipe( |
| 3108 | mockSpawnerLayer((args) => { | 3046 | Effect.provide( |
| 3109 | const joined = args.join(" "); | 3047 | layerMockSpawner((args) => { |
| 3110 | if (joined === "--version") return { stdout: "1.0.0\n", stderr: "", code: 0 }; | 3048 | const joined = args.join(" "); |
| 3111 | throw new Error(`Unexpected args: ${joined}`); | 3049 | if (joined === "--version") return { stdout: "1.0.0\n", stderr: "", code: 0 }; |
| 3112 | }), | 3050 | throw new Error(`Unexpected args: ${joined}`); |
| 3113 | ), | 3051 | }), |
| 3114 | ), | 3052 | ), |
| 3115 | ); | 3053 | ), |
| 3054 | ); | ||
| 3116 | 3055 | ||
| 3117 | it.effect("does not duplicate Claude in provider-prefixed subscription names", () => | 3056 | it.effect("does not duplicate Claude in full subscription labels", () => |
| 3118 | Effect.gen(function* () { | 3057 | Effect.gen(function* () { |
| 3119 | const status = yield* checkClaudeProviderStatus( | 3058 | const status = yield* checkClaudeProviderStatus( |
| 3120 | defaultClaudeSettings, | 3059 | defaultClaudeSettings, |
| 3121 | claudeCapabilities({ | 3060 | claudeCapabilities({ |
| 3122 | subscriptionType: "Claude Max", | 3061 | subscriptionType: "Claude Max Subscription", |
| 3123 | }), | 3062 | }), |
| 3124 | ); | 3063 | ); |
| 3125 | assert.strictEqual(status.auth.status, "authenticated"); | 3064 | assert.strictEqual(status.auth.status, "authenticated"); |
| 3126 | assert.strictEqual(status.auth.type, "Claude Max"); | 3065 | assert.strictEqual(status.auth.type, "Claude Max Subscription"); |
| 3127 | assert.strictEqual(status.auth.label, "Claude Max Subscription"); | 3066 | assert.strictEqual(status.auth.label, "Claude Max Subscription"); |
| 3128 | }).pipe( | 3067 | }).pipe( |
| 3129 | Effect.provide( | 3068 | Effect.provide( |
| 3130 | mockSpawnerLayer((args) => { | 3069 | layerMockSpawner((args) => { |
| 3131 | const joined = args.join(" "); | 3070 | const joined = args.join(" "); |
| 3132 | if (joined === "--version") return { stdout: "1.0.0\n", stderr: "", code: 0 }; | 3071 | if (joined === "--version") return { stdout: "1.0.0\n", stderr: "", code: 0 }; |
| 3133 | throw new Error(`Unexpected args: ${joined}`); | 3072 | throw new Error(`Unexpected args: ${joined}`); |
| 3134 | }), | 3073 | }), |
| 3135 | ), | ||
| 3136 | ), | 3074 | ), |
| 3137 | ); | 3075 | ), |
| 3076 | ); | ||
| 3138 | 3077 | ||
| 3139 | it.effect("returns claude auth email from initialization result", () => | 3078 | it.effect("does not duplicate Claude in provider-prefixed subscription names", () => |
| 3140 | Effect.gen(function* () { | 3079 | Effect.gen(function* () { |
| 3141 | const status = yield* checkClaudeProviderStatus( | 3080 | const status = yield* checkClaudeProviderStatus( |
| 3142 | defaultClaudeSettings, | 3081 | defaultClaudeSettings, |
| 3143 | claudeCapabilities({ email: "claude@example.com" }), | 3082 | claudeCapabilities({ |
| 3144 | ); | 3083 | subscriptionType: "Claude Max", |
| 3145 | assert.strictEqual(status.auth.status, "authenticated"); | 3084 | }), |
| 3146 | assert.strictEqual(status.auth.email, "claude@example.com"); | 3085 | ); |
| 3147 | }).pipe( | 3086 | assert.strictEqual(status.auth.status, "authenticated"); |
| 3148 | Effect.provide( | 3087 | assert.strictEqual(status.auth.type, "Claude Max"); |
| 3149 | mockSpawnerLayer((args) => { | 3088 | assert.strictEqual(status.auth.label, "Claude Max Subscription"); |
| 3150 | const joined = args.join(" "); | 3089 | }).pipe( |
| 3151 | if (joined === "--version") return { stdout: "1.0.0\n", stderr: "", code: 0 }; | 3090 | Effect.provide( |
| 3152 | if (joined === "auth status") | 3091 | layerMockSpawner((args) => { |
| 3153 | return { | 3092 | const joined = args.join(" "); |
| 3154 | stdout: | 3093 | if (joined === "--version") return { stdout: "1.0.0\n", stderr: "", code: 0 }; |
| 3155 | '{"loggedIn":true,"authMethod":"claude.ai","account":{"email":"claude@example.com"}}\n', | 3094 | throw new Error(`Unexpected args: ${joined}`); |
| 3156 | stderr: "", | 3095 | }), |
| 3157 | code: 0, | ||
| 3158 | }; | ||
| 3159 | throw new Error(`Unexpected args: ${joined}`); | ||
| 3160 | }), | ||
| 3161 | ), | ||
| 3162 | ), | 3096 | ), |
| 3163 | ); | 3097 | ), |
| 3164 | 3098 | ); | |
| 3165 | it.effect("runs Claude status probes with the configured CLAUDE_CONFIG_DIR", () => { | ||
| 3166 | const claudeConfigDir = "/tmp/t3code-claude-home"; | ||
| 3167 | const recorded = recordingMockSpawnerLayer((args) => { | ||
| 3168 | const joined = args.join(" "); | ||
| 3169 | if (joined === "--version") return { stdout: "1.0.0\n", stderr: "", code: 0 }; | ||
| 3170 | if (joined === "auth status") | ||
| 3171 | return { | ||
| 3172 | stdout: '{"loggedIn":true,"authMethod":"claude.ai"}\n', | ||
| 3173 | stderr: "", | ||
| 3174 | code: 0, | ||
| 3175 | }; | ||
| 3176 | throw new Error(`Unexpected args: ${joined}`); | ||
| 3177 | }); | ||
| 3178 | 3099 | ||
| 3179 | return Effect.gen(function* () { | 3100 | it.effect("returns claude auth email from initialization result", () => |
| 3180 | const status = yield* checkClaudeProviderStatus( | 3101 | Effect.gen(function* () { |
| 3181 | { | 3102 | const status = yield* checkClaudeProviderStatus( |
| 3182 | ...defaultClaudeSettings, | 3103 | defaultClaudeSettings, |
| 3183 | homePath: claudeConfigDir, | 3104 | claudeCapabilities({ email: "claude@example.com" }), |
| 3184 | }, | 3105 | ); |
| 3185 | claudeCapabilities(), | 3106 | assert.strictEqual(status.auth.status, "authenticated"); |
| 3186 | ); | 3107 | assert.strictEqual(status.auth.email, "claude@example.com"); |
| 3187 | assert.strictEqual(status.status, "ready"); | 3108 | }).pipe( |
| 3188 | // The home is resolved through the host Path before it reaches the env. | 3109 | Effect.provide( |
| 3189 | assert.deepStrictEqual( | 3110 | layerMockSpawner((args) => { |
| 3190 | recorded.commands.map((command) => command.env?.CLAUDE_CONFIG_DIR), | 3111 | const joined = args.join(" "); |
| 3191 | [(yield* Path.Path).resolve(claudeConfigDir)], | 3112 | if (joined === "--version") return { stdout: "1.0.0\n", stderr: "", code: 0 }; |
| 3192 | ); | 3113 | if (joined === "auth status") |
| 3193 | }).pipe(Effect.provide(recorded.layer)); | 3114 | return { |
| 3115 | stdout: | ||
| 3116 | '{"loggedIn":true,"authMethod":"claude.ai","account":{"email":"claude@example.com"}}\n', | ||
| 3117 | stderr: "", | ||
| 3118 | code: 0, | ||
| 3119 | }; | ||
| 3120 | throw new Error(`Unexpected args: ${joined}`); | ||
| 3121 | }), | ||
| 3122 | ), | ||
| 3123 | ), | ||
| 3124 | ); | ||
| 3125 | |||
| 3126 | it.effect("runs Claude status probes with the configured CLAUDE_CONFIG_DIR", () => { | ||
| 3127 | const claudeConfigDir = "/tmp/t3code-claude-home"; | ||
| 3128 | const recorded = recordingMockSpawnerLayer((args) => { | ||
| 3129 | const joined = args.join(" "); | ||
| 3130 | if (joined === "--version") return { stdout: "1.0.0\n", stderr: "", code: 0 }; | ||
| 3131 | if (joined === "auth status") | ||
| 3132 | return { | ||
| 3133 | stdout: '{"loggedIn":true,"authMethod":"claude.ai"}\n', | ||
| 3134 | stderr: "", | ||
| 3135 | code: 0, | ||
| 3136 | }; | ||
| 3137 | throw new Error(`Unexpected args: ${joined}`); | ||
| 3194 | }); | 3138 | }); |
| 3195 | 3139 | ||
| 3196 | it.effect("includes probed claude slash commands in the provider snapshot", () => | 3140 | return Effect.gen(function* () { |
| 3197 | Effect.gen(function* () { | 3141 | const status = yield* checkClaudeProviderStatus( |
| 3198 | const status = yield* checkClaudeProviderStatus( | 3142 | { |
| 3199 | defaultClaudeSettings, | 3143 | ...defaultClaudeSettings, |
| 3200 | claudeCapabilities({ | 3144 | homePath: claudeConfigDir, |
| 3201 | subscriptionType: "maxplan", | 3145 | }, |
| 3202 | slashCommands: [ | 3146 | claudeCapabilities(), |
| 3203 | { | 3147 | ); |
| 3204 | name: "review", | 3148 | assert.strictEqual(status.status, "ready"); |
| 3205 | description: "Review a pull request", | 3149 | // The home is resolved through the host Path before it reaches the env. |
| 3206 | input: { hint: "pr-or-branch" }, | 3150 | assert.deepStrictEqual( |
| 3207 | }, | 3151 | recorded.commands.map((command) => command.env?.CLAUDE_CONFIG_DIR), |
| 3208 | ], | 3152 | [(yield* Path.Path).resolve(claudeConfigDir)], |
| 3209 | }), | 3153 | ); |
| 3210 | ); | 3154 | }).pipe(Effect.provide(recorded.layer)); |
| 3155 | }); | ||
| 3211 | 3156 | ||
| 3212 | assert.deepStrictEqual(status.slashCommands.slice(1), [ | 3157 | it.effect("includes probed claude slash commands in the provider snapshot", () => |
| 3213 | { | 3158 | Effect.gen(function* () { |
| 3214 | name: "review", | 3159 | const status = yield* checkClaudeProviderStatus( |
| 3215 | description: "Review a pull request", | 3160 | defaultClaudeSettings, |
| 3216 | input: { hint: "pr-or-branch" }, | 3161 | claudeCapabilities({ |
| 3217 | }, | 3162 | subscriptionType: "maxplan", |
| 3218 | ]); | 3163 | slashCommands: [ |
| 3219 | }).pipe( | 3164 | { |
| 3220 | Effect.provide( | 3165 | name: "review", |
| 3221 | mockSpawnerLayer((args) => { | 3166 | description: "Review a pull request", |
| 3222 | const joined = args.join(" "); | 3167 | input: { hint: "pr-or-branch" }, |
| 3223 | if (joined === "--version") return { stdout: "1.0.0\n", stderr: "", code: 0 }; | 3168 | }, |
| 3224 | if (joined === "auth status") | 3169 | ], |
| 3225 | return { | 3170 | }), |
| 3226 | stdout: '{"loggedIn":true,"authMethod":"claude.ai"}\n', | 3171 | ); |
| 3227 | stderr: "", | 3172 | |
| 3228 | code: 0, | 3173 | assert.deepStrictEqual(status.slashCommands.slice(1), [ |
| 3229 | }; | 3174 | { |
| 3230 | throw new Error(`Unexpected args: ${joined}`); | 3175 | name: "review", |
| 3231 | }), | 3176 | description: "Review a pull request", |
| 3232 | ), | 3177 | input: { hint: "pr-or-branch" }, |
| 3178 | }, | ||
| 3179 | ]); | ||
| 3180 | }).pipe( | ||
| 3181 | Effect.provide( | ||
| 3182 | layerMockSpawner((args) => { | ||
| 3183 | const joined = args.join(" "); | ||
| 3184 | if (joined === "--version") return { stdout: "1.0.0\n", stderr: "", code: 0 }; | ||
| 3185 | if (joined === "auth status") | ||
| 3186 | return { | ||
| 3187 | stdout: '{"loggedIn":true,"authMethod":"claude.ai"}\n', | ||
| 3188 | stderr: "", | ||
| 3189 | code: 0, | ||
| 3190 | }; | ||
| 3191 | throw new Error(`Unexpected args: ${joined}`); | ||
| 3192 | }), | ||
| 3233 | ), | 3193 | ), |
| 3234 | ); | 3194 | ), |
| 3195 | ); | ||
| 3235 | 3196 | ||
| 3236 | it.effect("deduplicates probed claude slash commands by name", () => | 3197 | it.effect("deduplicates probed claude slash commands by name", () => |
| 3237 | Effect.gen(function* () { | 3198 | Effect.gen(function* () { |
| 3238 | const status = yield* checkClaudeProviderStatus( | 3199 | const status = yield* checkClaudeProviderStatus( |
| 3239 | defaultClaudeSettings, | 3200 | defaultClaudeSettings, |
| 3240 | claudeCapabilities({ | 3201 | claudeCapabilities({ |
| 3241 | subscriptionType: "maxplan", | 3202 | subscriptionType: "maxplan", |
| 3242 | slashCommands: [ | 3203 | slashCommands: [ |
| 3243 | { | 3204 | { |
| 3244 | name: "ui", | 3205 | name: "ui", |
| 3245 | description: "Explore and refine UI", | 3206 | description: "Explore and refine UI", |
| 3246 | }, | 3207 | }, |
| 3247 | { | 3208 | { |
| 3248 | name: "ui", | 3209 | name: "ui", |
| 3249 | input: { hint: "component-or-screen" }, | 3210 | input: { hint: "component-or-screen" }, |
| 3250 | }, | 3211 | }, |
| 3251 | ], | 3212 | ], |
| 3252 | }), | 3213 | }), |
| 3253 | ); | 3214 | ); |
| 3254 | 3215 | ||
| 3255 | assert.deepStrictEqual(status.slashCommands, [ | 3216 | assert.deepStrictEqual(status.slashCommands, [ |
| 3256 | COMPACT_SLASH_COMMAND, | 3217 | COMPACT_SLASH_COMMAND, |
| 3257 | { | 3218 | { |
| 3258 | name: "ui", | 3219 | name: "ui", |
| 3259 | description: "Explore and refine UI", | 3220 | description: "Explore and refine UI", |
| 3260 | input: { hint: "component-or-screen" }, | 3221 | input: { hint: "component-or-screen" }, |
| 3261 | }, | 3222 | }, |
| 3262 | ]); | 3223 | ]); |
| 3263 | }).pipe( | 3224 | }).pipe( |
| 3264 | Effect.provide( | 3225 | Effect.provide( |
| 3265 | mockSpawnerLayer((args) => { | 3226 | layerMockSpawner((args) => { |
| 3266 | const joined = args.join(" "); | 3227 | const joined = args.join(" "); |
| 3267 | if (joined === "--version") return { stdout: "1.0.0\n", stderr: "", code: 0 }; | 3228 | if (joined === "--version") return { stdout: "1.0.0\n", stderr: "", code: 0 }; |
| 3268 | if (joined === "auth status") | 3229 | if (joined === "auth status") |
| 3269 | return { | 3230 | return { |
| 3270 | stdout: '{"loggedIn":true,"authMethod":"claude.ai"}\n', | 3231 | stdout: '{"loggedIn":true,"authMethod":"claude.ai"}\n', |
| 3271 | stderr: "", | 3232 | stderr: "", |
| 3272 | code: 0, | 3233 | code: 0, |
| 3273 | }; | 3234 | }; |
| 3274 | throw new Error(`Unexpected args: ${joined}`); | 3235 | throw new Error(`Unexpected args: ${joined}`); |
| 3275 | }), | 3236 | }), |
| 3276 | ), | ||
| 3277 | ), | 3237 | ), |
| 3278 | ); | 3238 | ), |
| 3239 | ); | ||
| 3279 | 3240 | ||
| 3280 | it.effect("returns an api key label for claude api key auth", () => | 3241 | it.effect("returns an api key label for claude api key auth", () => |
| 3281 | Effect.gen(function* () { | 3242 | Effect.gen(function* () { |
| 3282 | const status = yield* checkClaudeProviderStatus( | 3243 | const status = yield* checkClaudeProviderStatus( |
| 3283 | defaultClaudeSettings, | 3244 | defaultClaudeSettings, |
| 3284 | claudeCapabilities({ tokenSource: "ANTHROPIC_AUTH_TOKEN" }), | 3245 | claudeCapabilities({ tokenSource: "ANTHROPIC_AUTH_TOKEN" }), |
| 3285 | ); | 3246 | ); |
| 3286 | assert.strictEqual(status.status, "ready"); | 3247 | assert.strictEqual(status.status, "ready"); |
| 3287 | assert.strictEqual(status.auth.status, "authenticated"); | 3248 | assert.strictEqual(status.auth.status, "authenticated"); |
| 3288 | assert.strictEqual(status.auth.type, "apiKey"); | 3249 | assert.strictEqual(status.auth.type, "apiKey"); |
| 3289 | assert.strictEqual(status.auth.label, "Claude API Key"); | 3250 | assert.strictEqual(status.auth.label, "Claude API Key"); |
| 3290 | }).pipe( | 3251 | }).pipe( |
| 3291 | Effect.provide( | 3252 | Effect.provide( |
| 3292 | mockSpawnerLayer((args) => { | 3253 | layerMockSpawner((args) => { |
| 3293 | const joined = args.join(" "); | 3254 | const joined = args.join(" "); |
| 3294 | if (joined === "--version") return { stdout: "1.0.0\n", stderr: "", code: 0 }; | 3255 | if (joined === "--version") return { stdout: "1.0.0\n", stderr: "", code: 0 }; |
| 3295 | if (joined === "auth status") | 3256 | if (joined === "auth status") |
| 3296 | return { | 3257 | return { |
| 3297 | stdout: '{"loggedIn":true,"authMethod":"api-key"}\n', | 3258 | stdout: '{"loggedIn":true,"authMethod":"api-key"}\n', |
| 3298 | stderr: "", | 3259 | stderr: "", |
| 3299 | code: 0, | 3260 | code: 0, |
| 3300 | }; | 3261 | }; |
| 3301 | throw new Error(`Unexpected args: ${joined}`); | 3262 | throw new Error(`Unexpected args: ${joined}`); |
| 3302 | }), | 3263 | }), |
| 3303 | ), | ||
| 3304 | ), | 3264 | ), |
| 3305 | ); | 3265 | ), |
| 3306 | 3266 | ); | |
| 3307 | it.effect("returns unavailable when claude is missing", () => | ||
| 3308 | Effect.gen(function* () { | ||
| 3309 | const status = yield* checkClaudeProviderStatus( | ||
| 3310 | defaultClaudeSettings, | ||
| 3311 | claudeCapabilities(), | ||
| 3312 | ); | ||
| 3313 | assert.strictEqual(status.status, "error"); | ||
| 3314 | assert.strictEqual(status.installed, false); | ||
| 3315 | assert.strictEqual(status.auth.status, "unknown"); | ||
| 3316 | assert.strictEqual(status.message, "Claude Agent CLI (`claude`) was not found on PATH."); | ||
| 3317 | }).pipe(Effect.provide(failingSpawnerLayer("spawn claude ENOENT"))), | ||
| 3318 | ); | ||
| 3319 | 3267 | ||
| 3320 | it.effect("returns error when version check fails with non-zero exit code", () => { | 3268 | it.effect("returns unavailable when claude is missing", () => |
| 3321 | const secretStderr = "Something went wrong: secret-token-value"; | 3269 | Effect.gen(function* () { |
| 3322 | return Effect.gen(function* () { | 3270 | const status = yield* checkClaudeProviderStatus( |
| 3323 | const status = yield* checkClaudeProviderStatus( | 3271 | defaultClaudeSettings, |
| 3324 | defaultClaudeSettings, | 3272 | claudeCapabilities(), |
| 3325 | claudeCapabilities(), | ||
| 3326 | ); | ||
| 3327 | assert.strictEqual(status.status, "error"); | ||
| 3328 | assert.strictEqual(status.installed, true); | ||
| 3329 | assert.strictEqual(status.message, "Claude Agent CLI is installed but failed to run."); | ||
| 3330 | assert.ok(!(status.message ?? "").includes(secretStderr)); | ||
| 3331 | }).pipe( | ||
| 3332 | Effect.provide( | ||
| 3333 | mockSpawnerLayer((args) => { | ||
| 3334 | const joined = args.join(" "); | ||
| 3335 | if (joined === "--version") | ||
| 3336 | return { | ||
| 3337 | stdout: "", | ||
| 3338 | stderr: secretStderr, | ||
| 3339 | code: 1, | ||
| 3340 | }; | ||
| 3341 | throw new Error(`Unexpected args: ${joined}`); | ||
| 3342 | }), | ||
| 3343 | ), | ||
| 3344 | ); | 3273 | ); |
| 3345 | }); | 3274 | assert.strictEqual(status.status, "error"); |
| 3346 | 3275 | assert.strictEqual(status.installed, false); | |
| 3347 | it.effect("returns warning when the Claude initialization result is unavailable", () => | 3276 | assert.strictEqual(status.auth.status, "unknown"); |
| 3348 | Effect.gen(function* () { | 3277 | assert.strictEqual(status.message, "Claude Agent CLI (`claude`) was not found on PATH."); |
| 3349 | const status = yield* checkClaudeProviderStatus( | 3278 | }).pipe(Effect.provide(layerFailingSpawner("spawn claude ENOENT"))), |
| 3350 | defaultClaudeSettings, | 3279 | ); |
| 3351 | noClaudeCapabilities, | 3280 | |
| 3352 | ); | 3281 | it.effect("returns error when version check fails with non-zero exit code", () => { |
| 3353 | assert.strictEqual(status.status, "warning"); | 3282 | const secretStderr = "Something went wrong: secret-token-value"; |
| 3354 | assert.strictEqual(status.installed, true); | 3283 | return Effect.gen(function* () { |
| 3355 | assert.strictEqual(status.auth.status, "unknown"); | 3284 | const status = yield* checkClaudeProviderStatus( |
| 3356 | assert.strictEqual( | 3285 | defaultClaudeSettings, |
| 3357 | status.message, | 3286 | claudeCapabilities(), |
| 3358 | "Could not verify Claude authentication status from initialization result.", | 3287 | ); |
| 3359 | ); | 3288 | assert.strictEqual(status.status, "error"); |
| 3360 | }).pipe( | 3289 | assert.strictEqual(status.installed, true); |
| 3361 | Effect.provide( | 3290 | assert.strictEqual(status.message, "Claude Agent CLI is installed but failed to run."); |
| 3362 | mockSpawnerLayer((args) => { | 3291 | assert.ok(!(status.message ?? "").includes(secretStderr)); |
| 3363 | const joined = args.join(" "); | 3292 | }).pipe( |
| 3364 | if (joined === "--version") return { stdout: "1.0.0\n", stderr: "", code: 0 }; | 3293 | Effect.provide( |
| 3365 | if (joined === "auth status") | 3294 | layerMockSpawner((args) => { |
| 3366 | return { | 3295 | const joined = args.join(" "); |
| 3367 | stdout: '{"loggedIn":false}\n', | 3296 | if (joined === "--version") |
| 3368 | stderr: "", | 3297 | return { |
| 3369 | code: 1, | 3298 | stdout: "", |
| 3370 | }; | 3299 | stderr: secretStderr, |
| 3371 | throw new Error(`Unexpected args: ${joined}`); | 3300 | code: 1, |
| 3372 | }), | 3301 | }; |
| 3373 | ), | 3302 | throw new Error(`Unexpected args: ${joined}`); |
| 3303 | }), | ||
| 3374 | ), | 3304 | ), |
| 3375 | ); | 3305 | ); |
| 3376 | }); | 3306 | }); |
| 3377 | }, | 3307 | |
| 3378 | ); | 3308 | it.effect("returns warning when the Claude initialization result is unavailable", () => |
| 3309 | Effect.gen(function* () { | ||
| 3310 | const status = yield* checkClaudeProviderStatus( | ||
| 3311 | defaultClaudeSettings, | ||
| 3312 | noClaudeCapabilities, | ||
| 3313 | ); | ||
| 3314 | assert.strictEqual(status.status, "warning"); | ||
| 3315 | assert.strictEqual(status.installed, true); | ||
| 3316 | assert.strictEqual(status.auth.status, "unknown"); | ||
| 3317 | assert.strictEqual( | ||
| 3318 | status.message, | ||
| 3319 | "Could not verify Claude authentication status from initialization result.", | ||
| 3320 | ); | ||
| 3321 | }).pipe( | ||
| 3322 | Effect.provide( | ||
| 3323 | layerMockSpawner((args) => { | ||
| 3324 | const joined = args.join(" "); | ||
| 3325 | if (joined === "--version") return { stdout: "1.0.0\n", stderr: "", code: 0 }; | ||
| 3326 | if (joined === "auth status") | ||
| 3327 | return { | ||
| 3328 | stdout: '{"loggedIn":false}\n', | ||
| 3329 | stderr: "", | ||
| 3330 | code: 1, | ||
| 3331 | }; | ||
| 3332 | throw new Error(`Unexpected args: ${joined}`); | ||
| 3333 | }), | ||
| 3334 | ), | ||
| 3335 | ), | ||
| 3336 | ); | ||
| 3337 | }); | ||
| 3338 | }); |
apps/server/src/provider/Layers/ProviderRegistry.ts+1-1| ... | @@ -326,7 +326,7 @@ const buildSnapshotSource = (instance: ProviderInstance): ProviderSnapshotSource | ... | @@ -326,7 +326,7 @@ const buildSnapshotSource = (instance: ProviderInstance): ProviderSnapshotSource |
| 326 | streamChanges: instance.snapshot.streamChanges, | 326 | streamChanges: instance.snapshot.streamChanges, |
| 327 | }); | 327 | }); |
| 328 | 328 | ||
| 329 | export const ProviderRegistryLive = Layer.effect( | 329 | export const layer = Layer.effect( |
| 330 | ProviderRegistry.ProviderRegistry, | 330 | ProviderRegistry.ProviderRegistry, |
| 331 | Effect.gen(function* () { | 331 | Effect.gen(function* () { |
| 332 | const instanceRegistry = yield* ProviderInstanceRegistry.ProviderInstanceRegistry; | 332 | const instanceRegistry = yield* ProviderInstanceRegistry.ProviderInstanceRegistry; |
apps/server/src/provider/Layers/ProviderUsageLimitsIngestion.ts+1-1| ... | @@ -4,4 +4,4 @@ | ... | @@ -4,4 +4,4 @@ |
| 4 | */ | 4 | */ |
| 5 | import * as Layer from "effect/Layer"; | 5 | import * as Layer from "effect/Layer"; |
| 6 | 6 | ||
| 7 | export const ProviderUsageLimitsIngestionLive = Layer.empty; | 7 | export const layer = Layer.empty; |
apps/server/src/provider/ModelManifest.test.ts+14-14| ... | @@ -333,13 +333,13 @@ const INVALID_REMOTE_MANIFESTS: ReadonlyArray<ModelManifest.ModelManifestData> = | ... | @@ -333,13 +333,13 @@ const INVALID_REMOTE_MANIFESTS: ReadonlyArray<ModelManifest.ModelManifestData> = |
| 333 | }), | 333 | }), |
| 334 | ]; | 334 | ]; |
| 335 | 335 | ||
| 336 | const httpClientLayer = (handler: () => Response) => | 336 | const layerHttpClient = (handler: () => Response) => |
| 337 | Layer.succeed( | 337 | Layer.succeed( |
| 338 | HttpClient.HttpClient, | 338 | HttpClient.HttpClient, |
| 339 | HttpClient.make((request) => Effect.succeed(HttpClientResponse.fromWeb(request, handler()))), | 339 | HttpClient.make((request) => Effect.succeed(HttpClientResponse.fromWeb(request, handler()))), |
| 340 | ); | 340 | ); |
| 341 | 341 | ||
| 342 | const serviceLayers = (input: { | 342 | const layerService = (input: { |
| 343 | readonly prefix: string; | 343 | readonly prefix: string; |
| 344 | readonly response: () => Response; | 344 | readonly response: () => Response; |
| 345 | readonly settings?: Parameters<typeof ServerSettings.layerTest>[0]; | 345 | readonly settings?: Parameters<typeof ServerSettings.layerTest>[0]; |
| ... | @@ -347,7 +347,7 @@ const serviceLayers = (input: { | ... | @@ -347,7 +347,7 @@ const serviceLayers = (input: { |
| 347 | ServerConfig.layerTest(process.cwd(), { prefix: input.prefix }).pipe( | 347 | ServerConfig.layerTest(process.cwd(), { prefix: input.prefix }).pipe( |
| 348 | Layer.provideMerge(NodeServices.layer), | 348 | Layer.provideMerge(NodeServices.layer), |
| 349 | Layer.provideMerge(ServerSettings.layerTest(input.settings ?? {})), | 349 | Layer.provideMerge(ServerSettings.layerTest(input.settings ?? {})), |
| 350 | Layer.provideMerge(httpClientLayer(input.response)), | 350 | Layer.provideMerge(layerHttpClient(input.response)), |
| 351 | ); | 351 | ); |
| 352 | 352 | ||
| 353 | describe("ModelManifest service", () => { | 353 | describe("ModelManifest service", () => { |
| ... | @@ -373,7 +373,7 @@ describe("ModelManifest service", () => { | ... | @@ -373,7 +373,7 @@ describe("ModelManifest service", () => { |
| 373 | }).pipe( | 373 | }).pipe( |
| 374 | Effect.scoped, | 374 | Effect.scoped, |
| 375 | Effect.provide( | 375 | Effect.provide( |
| 376 | serviceLayers({ | 376 | layerService({ |
| 377 | prefix: "model-manifest-force-refresh-test", | 377 | prefix: "model-manifest-force-refresh-test", |
| 378 | response: () => Response.json(fetchCount++ === 0 ? REMOTE_MANIFEST : updated), | 378 | response: () => Response.json(fetchCount++ === 0 ? REMOTE_MANIFEST : updated), |
| 379 | }), | 379 | }), |
| ... | @@ -395,7 +395,7 @@ describe("ModelManifest service", () => { | ... | @@ -395,7 +395,7 @@ describe("ModelManifest service", () => { |
| 395 | }).pipe( | 395 | }).pipe( |
| 396 | Effect.scoped, | 396 | Effect.scoped, |
| 397 | Effect.provide( | 397 | Effect.provide( |
| 398 | serviceLayers({ | 398 | layerService({ |
| 399 | prefix: "model-manifest-force-retry-test", | 399 | prefix: "model-manifest-force-retry-test", |
| 400 | response: () => | 400 | response: () => |
| 401 | fetchCount++ === 1 | 401 | fetchCount++ === 1 |
| ... | @@ -418,7 +418,7 @@ describe("ModelManifest service", () => { | ... | @@ -418,7 +418,7 @@ describe("ModelManifest service", () => { |
| 418 | }).pipe( | 418 | }).pipe( |
| 419 | Effect.scoped, | 419 | Effect.scoped, |
| 420 | Effect.provide( | 420 | Effect.provide( |
| 421 | serviceLayers({ | 421 | layerService({ |
| 422 | prefix: "model-manifest-force-initial-retry-test", | 422 | prefix: "model-manifest-force-initial-retry-test", |
| 423 | response: () => | 423 | response: () => |
| 424 | fetchCount++ === 0 | 424 | fetchCount++ === 0 |
| ... | @@ -442,7 +442,7 @@ describe("ModelManifest service", () => { | ... | @@ -442,7 +442,7 @@ describe("ModelManifest service", () => { |
| 442 | }).pipe( | 442 | }).pipe( |
| 443 | Effect.scoped, | 443 | Effect.scoped, |
| 444 | Effect.provide( | 444 | Effect.provide( |
| 445 | serviceLayers({ | 445 | layerService({ |
| 446 | prefix: "model-manifest-fetch-test", | 446 | prefix: "model-manifest-fetch-test", |
| 447 | response: () => Response.json(REMOTE_MANIFEST), | 447 | response: () => Response.json(REMOTE_MANIFEST), |
| 448 | }), | 448 | }), |
| ... | @@ -466,7 +466,7 @@ describe("ModelManifest service", () => { | ... | @@ -466,7 +466,7 @@ describe("ModelManifest service", () => { |
| 466 | }).pipe( | 466 | }).pipe( |
| 467 | Effect.scoped, | 467 | Effect.scoped, |
| 468 | Effect.provide( | 468 | Effect.provide( |
| 469 | serviceLayers({ | 469 | layerService({ |
| 470 | prefix: "model-manifest-stale-fetch-test", | 470 | prefix: "model-manifest-stale-fetch-test", |
| 471 | response: () => Response.json(remote), | 471 | response: () => Response.json(remote), |
| 472 | }), | 472 | }), |
| ... | @@ -481,7 +481,7 @@ describe("ModelManifest service", () => { | ... | @@ -481,7 +481,7 @@ describe("ModelManifest service", () => { |
| 481 | }).pipe( | 481 | }).pipe( |
| 482 | Effect.scoped, | 482 | Effect.scoped, |
| 483 | Effect.provide( | 483 | Effect.provide( |
| 484 | serviceLayers({ | 484 | layerService({ |
| 485 | prefix: "model-manifest-malformed-test", | 485 | prefix: "model-manifest-malformed-test", |
| 486 | response: () => Response.json({ version: 999, nonsense: true }), | 486 | response: () => Response.json({ version: 999, nonsense: true }), |
| 487 | }), | 487 | }), |
| ... | @@ -508,7 +508,7 @@ describe("ModelManifest service", () => { | ... | @@ -508,7 +508,7 @@ describe("ModelManifest service", () => { |
| 508 | }).pipe( | 508 | }).pipe( |
| 509 | Effect.scoped, | 509 | Effect.scoped, |
| 510 | Effect.provide( | 510 | Effect.provide( |
| 511 | serviceLayers({ | 511 | layerService({ |
| 512 | prefix: "model-manifest-last-good-test", | 512 | prefix: "model-manifest-last-good-test", |
| 513 | response: () => Response.json(responses[responseIndex]), | 513 | response: () => Response.json(responses[responseIndex]), |
| 514 | }), | 514 | }), |
| ... | @@ -548,7 +548,7 @@ describe("ModelManifest service", () => { | ... | @@ -548,7 +548,7 @@ describe("ModelManifest service", () => { |
| 548 | }).pipe( | 548 | }).pipe( |
| 549 | Effect.scoped, | 549 | Effect.scoped, |
| 550 | Effect.provide( | 550 | Effect.provide( |
| 551 | serviceLayers({ | 551 | layerService({ |
| 552 | prefix: "model-manifest-newer-bundle-test", | 552 | prefix: "model-manifest-newer-bundle-test", |
| 553 | response: () => Response.json(REMOTE_MANIFEST), | 553 | response: () => Response.json(REMOTE_MANIFEST), |
| 554 | }), | 554 | }), |
| ... | @@ -561,7 +561,7 @@ describe("ModelManifest service", () => { | ... | @@ -561,7 +561,7 @@ describe("ModelManifest service", () => { |
| 561 | let fetchCount = 0; | 561 | let fetchCount = 0; |
| 562 | const service = yield* ModelManifest.make.pipe( | 562 | const service = yield* ModelManifest.make.pipe( |
| 563 | Effect.provide( | 563 | Effect.provide( |
| 564 | httpClientLayer(() => { | 564 | layerHttpClient(() => { |
| 565 | fetchCount += 1; | 565 | fetchCount += 1; |
| 566 | return Response.json(REMOTE_MANIFEST); | 566 | return Response.json(REMOTE_MANIFEST); |
| 567 | }), | 567 | }), |
| ... | @@ -573,7 +573,7 @@ describe("ModelManifest service", () => { | ... | @@ -573,7 +573,7 @@ describe("ModelManifest service", () => { |
| 573 | }).pipe( | 573 | }).pipe( |
| 574 | Effect.scoped, | 574 | Effect.scoped, |
| 575 | Effect.provide( | 575 | Effect.provide( |
| 576 | serviceLayers({ | 576 | layerService({ |
| 577 | prefix: "model-manifest-optout-test", | 577 | prefix: "model-manifest-optout-test", |
| 578 | response: () => Response.json(REMOTE_MANIFEST), | 578 | response: () => Response.json(REMOTE_MANIFEST), |
| 579 | settings: { enableProviderUpdateChecks: false }, | 579 | settings: { enableProviderUpdateChecks: false }, |
| ... | @@ -607,7 +607,7 @@ it.effect("caches valid compatibility policies and keeps them after a malformed | ... | @@ -607,7 +607,7 @@ it.effect("caches valid compatibility policies and keeps them after a malformed |
| 607 | }).pipe( | 607 | }).pipe( |
| 608 | Effect.scoped, | 608 | Effect.scoped, |
| 609 | Effect.provide( | 609 | Effect.provide( |
| 610 | serviceLayers({ | 610 | layerService({ |
| 611 | prefix: "model-manifest-compatibility-test", | 611 | prefix: "model-manifest-compatibility-test", |
| 612 | response: () => | 612 | response: () => |
| 613 | Response.json( | 613 | Response.json( |
apps/server/src/provider/OpenCodeServerLedger.test.ts+2-2| ... | @@ -195,7 +195,7 @@ describe.skipIf(observedPlatforms.length === 0)("OpenCode server startup", () => | ... | @@ -195,7 +195,7 @@ describe.skipIf(observedPlatforms.length === 0)("OpenCode server startup", () => |
| 195 | const spawned = yield* Deferred.make<ChildProcessSpawner.ChildProcessHandle>(); | 195 | const spawned = yield* Deferred.make<ChildProcessSpawner.ChildProcessHandle>(); |
| 196 | const recording = yield* Deferred.make<number>(); | 196 | const recording = yield* Deferred.make<number>(); |
| 197 | const spawner = yield* ChildProcessSpawner.ChildProcessSpawner; | 197 | const spawner = yield* ChildProcessSpawner.ChildProcessSpawner; |
| 198 | const runtimeLayer = OpenCodeRuntime.OpenCodeRuntimeLive.pipe( | 198 | const layerRuntime = OpenCodeRuntime.layer.pipe( |
| 199 | Layer.provide( | 199 | Layer.provide( |
| 200 | Layer.succeed( | 200 | Layer.succeed( |
| 201 | ChildProcessSpawner.ChildProcessSpawner, | 201 | ChildProcessSpawner.ChildProcessSpawner, |
| ... | @@ -228,7 +228,7 @@ describe.skipIf(observedPlatforms.length === 0)("OpenCode server startup", () => | ... | @@ -228,7 +228,7 @@ describe.skipIf(observedPlatforms.length === 0)("OpenCode server startup", () => |
| 228 | runtime.startOpenCodeServerProcess({ binaryPath, directory: tempDir, port: 4096 }), | 228 | runtime.startOpenCodeServerProcess({ binaryPath, directory: tempDir, port: 4096 }), |
| 229 | ), | 229 | ), |
| 230 | Effect.provideService(Scope.Scope, serverScope), | 230 | Effect.provideService(Scope.Scope, serverScope), |
| 231 | Effect.provide(runtimeLayer), | 231 | Effect.provide(layerRuntime), |
| 232 | Effect.forkChild, | 232 | Effect.forkChild, |
| 233 | ); | 233 | ); |
| 234 | const pgid = yield* Deferred.await(recording); | 234 | const pgid = yield* Deferred.await(recording); |
apps/server/src/provider/acp/AcpRegistrySupport.test.ts+30-30| ... | @@ -50,7 +50,7 @@ function settings(input: Partial<AcpRegistrySettings> = {}): AcpRegistrySettings | ... | @@ -50,7 +50,7 @@ function settings(input: Partial<AcpRegistrySettings> = {}): AcpRegistrySettings |
| 50 | }); | 50 | }); |
| 51 | } | 51 | } |
| 52 | 52 | ||
| 53 | function resolverLayer( | 53 | function layerResolver( |
| 54 | execute: Parameters<typeof HttpClient.make>[0], | 54 | execute: Parameters<typeof HttpClient.make>[0], |
| 55 | environment: NodeJS.ProcessEnv = process.env, | 55 | environment: NodeJS.ProcessEnv = process.env, |
| 56 | ) { | 56 | ) { |
| ... | @@ -253,7 +253,7 @@ describe("AcpRegistrySupport", () => { | ... | @@ -253,7 +253,7 @@ describe("AcpRegistrySupport", () => { |
| 253 | }).pipe( | 253 | }).pipe( |
| 254 | Effect.scoped, | 254 | Effect.scoped, |
| 255 | Effect.provide( | 255 | Effect.provide( |
| 256 | resolverLayer((request) => { | 256 | layerResolver((request) => { |
| 257 | requests.push(request.url); | 257 | requests.push(request.url); |
| 258 | return Effect.die("unexpected registry request for a local provider"); | 258 | return Effect.die("unexpected registry request for a local provider"); |
| 259 | }), | 259 | }), |
| ... | @@ -310,7 +310,7 @@ describe("AcpRegistrySupport", () => { | ... | @@ -310,7 +310,7 @@ describe("AcpRegistrySupport", () => { |
| 310 | } | 310 | } |
| 311 | }).pipe( | 311 | }).pipe( |
| 312 | Effect.scoped, | 312 | Effect.scoped, |
| 313 | Effect.provide(resolverLayer(() => Effect.die("unexpected registry request"))), | 313 | Effect.provide(layerResolver(() => Effect.die("unexpected registry request"))), |
| 314 | ), | 314 | ), |
| 315 | ); | 315 | ); |
| 316 | 316 | ||
| ... | @@ -353,7 +353,7 @@ describe("AcpRegistrySupport", () => { | ... | @@ -353,7 +353,7 @@ describe("AcpRegistrySupport", () => { |
| 353 | } | 353 | } |
| 354 | }).pipe( | 354 | }).pipe( |
| 355 | Effect.scoped, | 355 | Effect.scoped, |
| 356 | Effect.provide(resolverLayer(() => Effect.die("unexpected registry request"))), | 356 | Effect.provide(layerResolver(() => Effect.die("unexpected registry request"))), |
| 357 | ), | 357 | ), |
| 358 | ); | 358 | ); |
| 359 | 359 | ||
| ... | @@ -402,7 +402,7 @@ describe("AcpRegistrySupport", () => { | ... | @@ -402,7 +402,7 @@ describe("AcpRegistrySupport", () => { |
| 402 | }).pipe( | 402 | }).pipe( |
| 403 | Effect.scoped, | 403 | Effect.scoped, |
| 404 | Effect.provide( | 404 | Effect.provide( |
| 405 | resolverLayer((request) => { | 405 | layerResolver((request) => { |
| 406 | requests.push(request.url); | 406 | requests.push(request.url); |
| 407 | return Effect.succeed( | 407 | return Effect.succeed( |
| 408 | HttpClientResponse.fromWeb(request, new Response(makeRegistry(agent))), | 408 | HttpClientResponse.fromWeb(request, new Response(makeRegistry(agent))), |
| ... | @@ -443,7 +443,7 @@ describe("AcpRegistrySupport", () => { | ... | @@ -443,7 +443,7 @@ describe("AcpRegistrySupport", () => { |
| 443 | }).pipe( | 443 | }).pipe( |
| 444 | Effect.scoped, | 444 | Effect.scoped, |
| 445 | Effect.provide( | 445 | Effect.provide( |
| 446 | resolverLayer((request) => | 446 | layerResolver((request) => |
| 447 | Effect.succeed(HttpClientResponse.fromWeb(request, new Response(makeRegistry(agent)))), | 447 | Effect.succeed(HttpClientResponse.fromWeb(request, new Response(makeRegistry(agent)))), |
| 448 | ), | 448 | ), |
| 449 | ), | 449 | ), |
| ... | @@ -483,7 +483,7 @@ describe("AcpRegistrySupport", () => { | ... | @@ -483,7 +483,7 @@ describe("AcpRegistrySupport", () => { |
| 483 | }).pipe( | 483 | }).pipe( |
| 484 | Effect.scoped, | 484 | Effect.scoped, |
| 485 | Effect.provide( | 485 | Effect.provide( |
| 486 | resolverLayer((request) => | 486 | layerResolver((request) => |
| 487 | Effect.succeed(HttpClientResponse.fromWeb(request, new Response(makeRegistry(agent)))), | 487 | Effect.succeed(HttpClientResponse.fromWeb(request, new Response(makeRegistry(agent)))), |
| 488 | ), | 488 | ), |
| 489 | ), | 489 | ), |
| ... | @@ -519,7 +519,7 @@ describe("AcpRegistrySupport", () => { | ... | @@ -519,7 +519,7 @@ describe("AcpRegistrySupport", () => { |
| 519 | }).pipe( | 519 | }).pipe( |
| 520 | Effect.scoped, | 520 | Effect.scoped, |
| 521 | Effect.provide( | 521 | Effect.provide( |
| 522 | resolverLayer((request) => | 522 | layerResolver((request) => |
| 523 | Effect.succeed(HttpClientResponse.fromWeb(request, new Response(makeRegistry(agent)))), | 523 | Effect.succeed(HttpClientResponse.fromWeb(request, new Response(makeRegistry(agent)))), |
| 524 | ), | 524 | ), |
| 525 | ), | 525 | ), |
| ... | @@ -572,7 +572,7 @@ describe("AcpRegistrySupport", () => { | ... | @@ -572,7 +572,7 @@ describe("AcpRegistrySupport", () => { |
| 572 | }).pipe( | 572 | }).pipe( |
| 573 | Effect.scoped, | 573 | Effect.scoped, |
| 574 | Effect.provide( | 574 | Effect.provide( |
| 575 | resolverLayer((request) => | 575 | layerResolver((request) => |
| 576 | Effect.succeed(HttpClientResponse.fromWeb(request, new Response(makeRegistry(agent)))), | 576 | Effect.succeed(HttpClientResponse.fromWeb(request, new Response(makeRegistry(agent)))), |
| 577 | ), | 577 | ), |
| 578 | ), | 578 | ), |
| ... | @@ -616,7 +616,7 @@ describe("AcpRegistrySupport", () => { | ... | @@ -616,7 +616,7 @@ describe("AcpRegistrySupport", () => { |
| 616 | }).pipe( | 616 | }).pipe( |
| 617 | Effect.scoped, | 617 | Effect.scoped, |
| 618 | Effect.provide( | 618 | Effect.provide( |
| 619 | resolverLayer((request) => { | 619 | layerResolver((request) => { |
| 620 | requests.push(request.url); | 620 | requests.push(request.url); |
| 621 | const response = | 621 | const response = |
| 622 | request.url === registryUrl | 622 | request.url === registryUrl |
| ... | @@ -704,7 +704,7 @@ describe("AcpRegistrySupport", () => { | ... | @@ -704,7 +704,7 @@ describe("AcpRegistrySupport", () => { |
| 704 | }).pipe( | 704 | }).pipe( |
| 705 | Effect.scoped, | 705 | Effect.scoped, |
| 706 | Effect.provide( | 706 | Effect.provide( |
| 707 | resolverLayer((request) => { | 707 | layerResolver((request) => { |
| 708 | requests.push(request.url); | 708 | requests.push(request.url); |
| 709 | return Effect.succeed( | 709 | return Effect.succeed( |
| 710 | HttpClientResponse.fromWeb( | 710 | HttpClientResponse.fromWeb( |
| ... | @@ -822,7 +822,7 @@ describe("AcpRegistrySupport", () => { | ... | @@ -822,7 +822,7 @@ describe("AcpRegistrySupport", () => { |
| 822 | }).pipe( | 822 | }).pipe( |
| 823 | Effect.scoped, | 823 | Effect.scoped, |
| 824 | Effect.provide( | 824 | Effect.provide( |
| 825 | resolverLayer((request) => | 825 | layerResolver((request) => |
| 826 | Effect.succeed( | 826 | Effect.succeed( |
| 827 | HttpClientResponse.fromWeb( | 827 | HttpClientResponse.fromWeb( |
| 828 | request, | 828 | request, |
| ... | @@ -863,7 +863,7 @@ describe("AcpRegistrySupport", () => { | ... | @@ -863,7 +863,7 @@ describe("AcpRegistrySupport", () => { |
| 863 | }).pipe( | 863 | }).pipe( |
| 864 | Effect.scoped, | 864 | Effect.scoped, |
| 865 | Effect.provide( | 865 | Effect.provide( |
| 866 | resolverLayer((request) => { | 866 | layerResolver((request) => { |
| 867 | requests += 1; | 867 | requests += 1; |
| 868 | return Effect.yieldNow.pipe( | 868 | return Effect.yieldNow.pipe( |
| 869 | Effect.as(HttpClientResponse.fromWeb(request, new Response(makeRegistry(agent)))), | 869 | Effect.as(HttpClientResponse.fromWeb(request, new Response(makeRegistry(agent)))), |
| ... | @@ -900,7 +900,7 @@ describe("AcpRegistrySupport", () => { | ... | @@ -900,7 +900,7 @@ describe("AcpRegistrySupport", () => { |
| 900 | }).pipe( | 900 | }).pipe( |
| 901 | Effect.scoped, | 901 | Effect.scoped, |
| 902 | Effect.provide( | 902 | Effect.provide( |
| 903 | resolverLayer( | 903 | layerResolver( |
| 904 | (request) => | 904 | (request) => |
| 905 | Effect.succeed( | 905 | Effect.succeed( |
| 906 | HttpClientResponse.fromWeb( | 906 | HttpClientResponse.fromWeb( |
| ... | @@ -942,7 +942,7 @@ describe("AcpRegistrySupport", () => { | ... | @@ -942,7 +942,7 @@ describe("AcpRegistrySupport", () => { |
| 942 | }).pipe( | 942 | }).pipe( |
| 943 | Effect.scoped, | 943 | Effect.scoped, |
| 944 | Effect.provide( | 944 | Effect.provide( |
| 945 | resolverLayer((request) => | 945 | layerResolver((request) => |
| 946 | Effect.succeed( | 946 | Effect.succeed( |
| 947 | HttpClientResponse.fromWeb( | 947 | HttpClientResponse.fromWeb( |
| 948 | request, | 948 | request, |
| ... | @@ -977,7 +977,7 @@ describe("AcpRegistrySupport", () => { | ... | @@ -977,7 +977,7 @@ describe("AcpRegistrySupport", () => { |
| 977 | }).pipe( | 977 | }).pipe( |
| 978 | Effect.scoped, | 978 | Effect.scoped, |
| 979 | Effect.provide( | 979 | Effect.provide( |
| 980 | resolverLayer((request) => | 980 | layerResolver((request) => |
| 981 | Effect.succeed(HttpClientResponse.fromWeb(request, new Response(makeRegistry(agent)))), | 981 | Effect.succeed(HttpClientResponse.fromWeb(request, new Response(makeRegistry(agent)))), |
| 982 | ), | 982 | ), |
| 983 | ), | 983 | ), |
| ... | @@ -1012,7 +1012,7 @@ describe("AcpRegistrySupport", () => { | ... | @@ -1012,7 +1012,7 @@ describe("AcpRegistrySupport", () => { |
| 1012 | }).pipe( | 1012 | }).pipe( |
| 1013 | Effect.scoped, | 1013 | Effect.scoped, |
| 1014 | Effect.provide( | 1014 | Effect.provide( |
| 1015 | resolverLayer( | 1015 | layerResolver( |
| 1016 | (request) => | 1016 | (request) => |
| 1017 | Effect.succeed( | 1017 | Effect.succeed( |
| 1018 | HttpClientResponse.fromWeb( | 1018 | HttpClientResponse.fromWeb( |
| ... | @@ -1056,7 +1056,7 @@ describe("AcpRegistrySupport", () => { | ... | @@ -1056,7 +1056,7 @@ describe("AcpRegistrySupport", () => { |
| 1056 | }).pipe( | 1056 | }).pipe( |
| 1057 | Effect.scoped, | 1057 | Effect.scoped, |
| 1058 | Effect.provide( | 1058 | Effect.provide( |
| 1059 | resolverLayer((request) => | 1059 | layerResolver((request) => |
| 1060 | Effect.succeed( | 1060 | Effect.succeed( |
| 1061 | HttpClientResponse.fromWeb( | 1061 | HttpClientResponse.fromWeb( |
| 1062 | request, | 1062 | request, |
| ... | @@ -1101,7 +1101,7 @@ describe("AcpRegistrySupport", () => { | ... | @@ -1101,7 +1101,7 @@ describe("AcpRegistrySupport", () => { |
| 1101 | }).pipe( | 1101 | }).pipe( |
| 1102 | Effect.scoped, | 1102 | Effect.scoped, |
| 1103 | Effect.provide( | 1103 | Effect.provide( |
| 1104 | resolverLayer((request) => { | 1104 | layerResolver((request) => { |
| 1105 | requests.push(request.url); | 1105 | requests.push(request.url); |
| 1106 | return Effect.succeed( | 1106 | return Effect.succeed( |
| 1107 | HttpClientResponse.fromWeb(request, new Response(makeRegistry(agent))), | 1107 | HttpClientResponse.fromWeb(request, new Response(makeRegistry(agent))), |
| ... | @@ -1141,7 +1141,7 @@ describe("AcpRegistrySupport", () => { | ... | @@ -1141,7 +1141,7 @@ describe("AcpRegistrySupport", () => { |
| 1141 | }).pipe( | 1141 | }).pipe( |
| 1142 | Effect.scoped, | 1142 | Effect.scoped, |
| 1143 | Effect.provide( | 1143 | Effect.provide( |
| 1144 | resolverLayer((request) => | 1144 | layerResolver((request) => |
| 1145 | Effect.succeed( | 1145 | Effect.succeed( |
| 1146 | HttpClientResponse.fromWeb(request, new Response("unavailable", { status: 503 })), | 1146 | HttpClientResponse.fromWeb(request, new Response("unavailable", { status: 503 })), |
| 1147 | ), | 1147 | ), |
| ... | @@ -1177,7 +1177,7 @@ describe("AcpRegistrySupport", () => { | ... | @@ -1177,7 +1177,7 @@ describe("AcpRegistrySupport", () => { |
| 1177 | }).pipe( | 1177 | }).pipe( |
| 1178 | Effect.scoped, | 1178 | Effect.scoped, |
| 1179 | Effect.provide( | 1179 | Effect.provide( |
| 1180 | resolverLayer((request) => { | 1180 | layerResolver((request) => { |
| 1181 | requests.push(request.url); | 1181 | requests.push(request.url); |
| 1182 | return Effect.succeed( | 1182 | return Effect.succeed( |
| 1183 | HttpClientResponse.fromWeb(request, new Response(makeRegistry(agent))), | 1183 | HttpClientResponse.fromWeb(request, new Response(makeRegistry(agent))), |
| ... | @@ -1211,7 +1211,7 @@ describe("AcpRegistrySupport", () => { | ... | @@ -1211,7 +1211,7 @@ describe("AcpRegistrySupport", () => { |
| 1211 | }).pipe( | 1211 | }).pipe( |
| 1212 | Effect.scoped, | 1212 | Effect.scoped, |
| 1213 | Effect.provide( | 1213 | Effect.provide( |
| 1214 | resolverLayer((request) => { | 1214 | layerResolver((request) => { |
| 1215 | requests += 1; | 1215 | requests += 1; |
| 1216 | return Effect.succeed( | 1216 | return Effect.succeed( |
| 1217 | HttpClientResponse.fromWeb(request, new Response(makeRegistry(agent))), | 1217 | HttpClientResponse.fromWeb(request, new Response(makeRegistry(agent))), |
| ... | @@ -1240,7 +1240,7 @@ describe("AcpRegistrySupport", () => { | ... | @@ -1240,7 +1240,7 @@ describe("AcpRegistrySupport", () => { |
| 1240 | }).pipe( | 1240 | }).pipe( |
| 1241 | Effect.scoped, | 1241 | Effect.scoped, |
| 1242 | Effect.provide( | 1242 | Effect.provide( |
| 1243 | resolverLayer((request) => { | 1243 | layerResolver((request) => { |
| 1244 | requests += 1; | 1244 | requests += 1; |
| 1245 | return Effect.succeed(HttpClientResponse.fromWeb(request, new Response("unused"))); | 1245 | return Effect.succeed(HttpClientResponse.fromWeb(request, new Response("unused"))); |
| 1246 | }), | 1246 | }), |
| ... | @@ -1279,7 +1279,7 @@ describe("AcpRegistrySupport", () => { | ... | @@ -1279,7 +1279,7 @@ describe("AcpRegistrySupport", () => { |
| 1279 | }).pipe( | 1279 | }).pipe( |
| 1280 | Effect.scoped, | 1280 | Effect.scoped, |
| 1281 | Effect.provide( | 1281 | Effect.provide( |
| 1282 | resolverLayer( | 1282 | layerResolver( |
| 1283 | (request) => | 1283 | (request) => |
| 1284 | Effect.succeed(HttpClientResponse.fromWeb(request, new Response(makeRegistry(agent)))), | 1284 | Effect.succeed(HttpClientResponse.fromWeb(request, new Response(makeRegistry(agent)))), |
| 1285 | { PATH: "" }, | 1285 | { PATH: "" }, |
| ... | @@ -1329,7 +1329,7 @@ describe("AcpRegistrySupport", () => { | ... | @@ -1329,7 +1329,7 @@ describe("AcpRegistrySupport", () => { |
| 1329 | }).pipe( | 1329 | }).pipe( |
| 1330 | Effect.scoped, | 1330 | Effect.scoped, |
| 1331 | Effect.provide( | 1331 | Effect.provide( |
| 1332 | resolverLayer((request) => | 1332 | layerResolver((request) => |
| 1333 | Effect.succeed(HttpClientResponse.fromWeb(request, new Response(makeRegistry(agent)))), | 1333 | Effect.succeed(HttpClientResponse.fromWeb(request, new Response(makeRegistry(agent)))), |
| 1334 | ), | 1334 | ), |
| 1335 | ), | 1335 | ), |
| ... | @@ -1395,7 +1395,7 @@ describe("AcpRegistrySupport", () => { | ... | @@ -1395,7 +1395,7 @@ describe("AcpRegistrySupport", () => { |
| 1395 | }).pipe( | 1395 | }).pipe( |
| 1396 | Effect.scoped, | 1396 | Effect.scoped, |
| 1397 | Effect.provide( | 1397 | Effect.provide( |
| 1398 | resolverLayer((request) => | 1398 | layerResolver((request) => |
| 1399 | Effect.succeed(HttpClientResponse.fromWeb(request, new Response("unused"))), | 1399 | Effect.succeed(HttpClientResponse.fromWeb(request, new Response("unused"))), |
| 1400 | ), | 1400 | ), |
| 1401 | ), | 1401 | ), |
| ... | @@ -1433,7 +1433,7 @@ describe("AcpRegistrySupport", () => { | ... | @@ -1433,7 +1433,7 @@ describe("AcpRegistrySupport", () => { |
| 1433 | }); | 1433 | }); |
| 1434 | }).pipe( | 1434 | }).pipe( |
| 1435 | Effect.scoped, | 1435 | Effect.scoped, |
| 1436 | Effect.provide(resolverLayer(() => Effect.die("unexpected HTTP request"))), | 1436 | Effect.provide(layerResolver(() => Effect.die("unexpected HTTP request"))), |
| 1437 | ), | 1437 | ), |
| 1438 | ); | 1438 | ); |
| 1439 | 1439 | ||
| ... | @@ -1508,7 +1508,7 @@ describe("AcpRegistrySupport", () => { | ... | @@ -1508,7 +1508,7 @@ describe("AcpRegistrySupport", () => { |
| 1508 | }).pipe( | 1508 | }).pipe( |
| 1509 | Effect.scoped, | 1509 | Effect.scoped, |
| 1510 | Effect.provide( | 1510 | Effect.provide( |
| 1511 | resolverLayer((request) => { | 1511 | layerResolver((request) => { |
| 1512 | if (request.url === registryUrl) { | 1512 | if (request.url === registryUrl) { |
| 1513 | return Effect.succeed( | 1513 | return Effect.succeed( |
| 1514 | HttpClientResponse.fromWeb(request, new Response(makeRegistry(agent))), | 1514 | HttpClientResponse.fromWeb(request, new Response(makeRegistry(agent))), |
| ... | @@ -1563,7 +1563,7 @@ describe("AcpRegistrySupport", () => { | ... | @@ -1563,7 +1563,7 @@ describe("AcpRegistrySupport", () => { |
| 1563 | }).pipe( | 1563 | }).pipe( |
| 1564 | Effect.scoped, | 1564 | Effect.scoped, |
| 1565 | Effect.provide( | 1565 | Effect.provide( |
| 1566 | resolverLayer((request) => | 1566 | layerResolver((request) => |
| 1567 | Effect.succeed( | 1567 | Effect.succeed( |
| 1568 | HttpClientResponse.fromWeb( | 1568 | HttpClientResponse.fromWeb( |
| 1569 | request, | 1569 | request, |
| ... | @@ -1607,7 +1607,7 @@ describe("AcpRegistrySupport", () => { | ... | @@ -1607,7 +1607,7 @@ describe("AcpRegistrySupport", () => { |
| 1607 | }).pipe( | 1607 | }).pipe( |
| 1608 | Effect.scoped, | 1608 | Effect.scoped, |
| 1609 | Effect.provide( | 1609 | Effect.provide( |
| 1610 | resolverLayer((request) => | 1610 | layerResolver((request) => |
| 1611 | Effect.succeed( | 1611 | Effect.succeed( |
| 1612 | HttpClientResponse.fromWeb( | 1612 | HttpClientResponse.fromWeb( |
| 1613 | request, | 1613 | request, |
apps/server/src/provider/makeManagedServerProvider.test.ts+18-18| ... | @@ -99,7 +99,7 @@ const refreshedSnapshotSecond: ServerProvider = { | ... | @@ -99,7 +99,7 @@ const refreshedSnapshotSecond: ServerProvider = { |
| 99 | message: "Refreshed provider availability again.", | 99 | message: "Refreshed provider availability again.", |
| 100 | }; | 100 | }; |
| 101 | 101 | ||
| 102 | function makeBackgroundPolicyLayer(shouldRunScopeWork: boolean) { | 102 | function layerBackgroundPolicy(shouldRunScopeWork: boolean) { |
| 103 | return Layer.mock(BackgroundPolicy.BackgroundPolicy)({ | 103 | return Layer.mock(BackgroundPolicy.BackgroundPolicy)({ |
| 104 | reportClientActivity: () => Effect.void, | 104 | reportClientActivity: () => Effect.void, |
| 105 | removeRpcClient: () => Effect.void, | 105 | removeRpcClient: () => Effect.void, |
| ... | @@ -130,11 +130,11 @@ function makeBackgroundPolicyLayer(shouldRunScopeWork: boolean) { | ... | @@ -130,11 +130,11 @@ function makeBackgroundPolicyLayer(shouldRunScopeWork: boolean) { |
| 130 | }); | 130 | }); |
| 131 | } | 131 | } |
| 132 | 132 | ||
| 133 | const BackgroundPolicyAlwaysRunLayer = makeBackgroundPolicyLayer(true); | 133 | const layerBackgroundPolicyAlwaysRun = layerBackgroundPolicy(true); |
| 134 | const BackgroundPolicyNeverRunLayer = makeBackgroundPolicyLayer(false); | 134 | const layerBackgroundPolicyNeverRun = layerBackgroundPolicy(false); |
| 135 | const ServerSettingsTestLayer = ServerSettings.layerTest(); | 135 | const layerServerSettingsTest = ServerSettings.layerTest(); |
| 136 | const AlwaysRunTestLayer = Layer.merge(BackgroundPolicyAlwaysRunLayer, ServerSettingsTestLayer); | 136 | const layerAlwaysRunTest = Layer.merge(layerBackgroundPolicyAlwaysRun, layerServerSettingsTest); |
| 137 | const NeverRunTestLayer = Layer.merge(BackgroundPolicyNeverRunLayer, ServerSettingsTestLayer); | 137 | const layerNeverRunTest = Layer.merge(layerBackgroundPolicyNeverRun, layerServerSettingsTest); |
| 138 | 138 | ||
| 139 | const enrichedSnapshotSecond: ServerProvider = { | 139 | const enrichedSnapshotSecond: ServerProvider = { |
| 140 | ...refreshedSnapshotSecond, | 140 | ...refreshedSnapshotSecond, |
| ... | @@ -189,7 +189,7 @@ describe("makeManagedServerProvider", () => { | ... | @@ -189,7 +189,7 @@ describe("makeManagedServerProvider", () => { |
| 189 | assert.deepStrictEqual(latest, refreshedSnapshot); | 189 | assert.deepStrictEqual(latest, refreshedSnapshot); |
| 190 | assert.strictEqual(yield* Ref.get(checkCalls), 1); | 190 | assert.strictEqual(yield* Ref.get(checkCalls), 1); |
| 191 | }), | 191 | }), |
| 192 | ).pipe(Effect.provide(AlwaysRunTestLayer)), | 192 | ).pipe(Effect.provide(layerAlwaysRunTest)), |
| 193 | ); | 193 | ); |
| 194 | 194 | ||
| 195 | it.effect("skips periodic provider refreshes without foreground provider-status demand", () => | 195 | it.effect("skips periodic provider refreshes without foreground provider-status demand", () => |
| ... | @@ -220,7 +220,7 @@ describe("makeManagedServerProvider", () => { | ... | @@ -220,7 +220,7 @@ describe("makeManagedServerProvider", () => { |
| 220 | 220 | ||
| 221 | assert.strictEqual(yield* Ref.get(checkCalls), 1); | 221 | assert.strictEqual(yield* Ref.get(checkCalls), 1); |
| 222 | }), | 222 | }), |
| 223 | ).pipe(Effect.provide(Layer.mergeAll(NeverRunTestLayer, TestClock.layer()))), | 223 | ).pipe(Effect.provide(Layer.mergeAll(layerNeverRunTest, TestClock.layer()))), |
| 224 | ); | 224 | ); |
| 225 | 225 | ||
| 226 | it.effect("disables periodic provider refreshes when the explicit interval is zero", () => | 226 | it.effect("disables periodic provider refreshes when the explicit interval is zero", () => |
| ... | @@ -247,7 +247,7 @@ describe("makeManagedServerProvider", () => { | ... | @@ -247,7 +247,7 @@ describe("makeManagedServerProvider", () => { |
| 247 | 247 | ||
| 248 | assert.strictEqual(yield* Ref.get(checkCalls), 1); | 248 | assert.strictEqual(yield* Ref.get(checkCalls), 1); |
| 249 | }), | 249 | }), |
| 250 | ).pipe(Effect.provide(Layer.mergeAll(AlwaysRunTestLayer, TestClock.layer()))), | 250 | ).pipe(Effect.provide(Layer.mergeAll(layerAlwaysRunTest, TestClock.layer()))), |
| 251 | ); | 251 | ); |
| 252 | 252 | ||
| 253 | it.effect("keeps manual refresh when interval refresh is disabled", () => | 253 | it.effect("keeps manual refresh when interval refresh is disabled", () => |
| ... | @@ -281,7 +281,7 @@ describe("makeManagedServerProvider", () => { | ... | @@ -281,7 +281,7 @@ describe("makeManagedServerProvider", () => { |
| 281 | yield* provider.refresh; | 281 | yield* provider.refresh; |
| 282 | assert.strictEqual(yield* Ref.get(checkCalls), 2); | 282 | assert.strictEqual(yield* Ref.get(checkCalls), 2); |
| 283 | }), | 283 | }), |
| 284 | ).pipe(Effect.provide(Layer.mergeAll(AlwaysRunTestLayer, TestClock.layer()))), | 284 | ).pipe(Effect.provide(Layer.mergeAll(layerAlwaysRunTest, TestClock.layer()))), |
| 285 | ); | 285 | ); |
| 286 | 286 | ||
| 287 | it.effect("wakes a sleeping provider refresh loop when its interval changes", () => | 287 | it.effect("wakes a sleeping provider refresh loop when its interval changes", () => |
| ... | @@ -293,7 +293,7 @@ describe("makeManagedServerProvider", () => { | ... | @@ -293,7 +293,7 @@ describe("makeManagedServerProvider", () => { |
| 293 | }; | 293 | }; |
| 294 | const serverSettingsRef = yield* Ref.make(initialServerSettings); | 294 | const serverSettingsRef = yield* Ref.make(initialServerSettings); |
| 295 | const serverSettingsChanges = yield* PubSub.unbounded<typeof initialServerSettings>(); | 295 | const serverSettingsChanges = yield* PubSub.unbounded<typeof initialServerSettings>(); |
| 296 | const serverSettingsLayer = Layer.succeed( | 296 | const layerServerSettings = Layer.succeed( |
| 297 | ServerSettings.ServerSettingsService, | 297 | ServerSettings.ServerSettingsService, |
| 298 | ServerSettings.ServerSettingsService.of({ | 298 | ServerSettings.ServerSettingsService.of({ |
| 299 | start: Effect.void, | 299 | start: Effect.void, |
| ... | @@ -326,7 +326,7 @@ describe("makeManagedServerProvider", () => { | ... | @@ -326,7 +326,7 @@ describe("makeManagedServerProvider", () => { |
| 326 | ), | 326 | ), |
| 327 | Effect.as(refreshedSnapshot), | 327 | Effect.as(refreshedSnapshot), |
| 328 | ), | 328 | ), |
| 329 | }).pipe(Effect.provide(Layer.merge(BackgroundPolicyAlwaysRunLayer, serverSettingsLayer))); | 329 | }).pipe(Effect.provide(Layer.merge(layerBackgroundPolicyAlwaysRun, layerServerSettings))); |
| 330 | 330 | ||
| 331 | yield* Deferred.await(initialCheckDone); | 331 | yield* Deferred.await(initialCheckDone); |
| 332 | const nextServerSettings = { | 332 | const nextServerSettings = { |
| ... | @@ -388,7 +388,7 @@ describe("makeManagedServerProvider", () => { | ... | @@ -388,7 +388,7 @@ describe("makeManagedServerProvider", () => { |
| 388 | assert.deepStrictEqual(latest, refreshedSnapshotSecond); | 388 | assert.deepStrictEqual(latest, refreshedSnapshotSecond); |
| 389 | assert.strictEqual(yield* Ref.get(checkCalls), 2); | 389 | assert.strictEqual(yield* Ref.get(checkCalls), 2); |
| 390 | }), | 390 | }), |
| 391 | ).pipe(Effect.provide(AlwaysRunTestLayer)), | 391 | ).pipe(Effect.provide(layerAlwaysRunTest)), |
| 392 | ); | 392 | ); |
| 393 | 393 | ||
| 394 | it.effect("can update settings and disable periodic checks without probing again", () => | 394 | it.effect("can update settings and disable periodic checks without probing again", () => |
| ... | @@ -423,7 +423,7 @@ describe("makeManagedServerProvider", () => { | ... | @@ -423,7 +423,7 @@ describe("makeManagedServerProvider", () => { |
| 423 | assert.strictEqual(yield* Ref.get(checkCalls), 1); | 423 | assert.strictEqual(yield* Ref.get(checkCalls), 1); |
| 424 | assert.strictEqual(yield* Ref.get(enrichmentCalls), 2); | 424 | assert.strictEqual(yield* Ref.get(enrichmentCalls), 2); |
| 425 | }), | 425 | }), |
| 426 | ).pipe(Effect.provide(Layer.mergeAll(AlwaysRunTestLayer, TestClock.layer()))), | 426 | ).pipe(Effect.provide(Layer.mergeAll(layerAlwaysRunTest, TestClock.layer()))), |
| 427 | ); | 427 | ); |
| 428 | 428 | ||
| 429 | it.effect("streams supplemental snapshot updates after the base provider check completes", () => | 429 | it.effect("streams supplemental snapshot updates after the base provider check completes", () => |
| ... | @@ -461,7 +461,7 @@ describe("makeManagedServerProvider", () => { | ... | @@ -461,7 +461,7 @@ describe("makeManagedServerProvider", () => { |
| 461 | assert.deepStrictEqual(updates, [refreshedSnapshot, enrichedSnapshot]); | 461 | assert.deepStrictEqual(updates, [refreshedSnapshot, enrichedSnapshot]); |
| 462 | assert.deepStrictEqual(latest, enrichedSnapshot); | 462 | assert.deepStrictEqual(latest, enrichedSnapshot); |
| 463 | }), | 463 | }), |
| 464 | ).pipe(Effect.provide(AlwaysRunTestLayer)), | 464 | ).pipe(Effect.provide(layerAlwaysRunTest)), |
| 465 | ); | 465 | ); |
| 466 | 466 | ||
| 467 | it.effect("ignores stale enrichment callbacks after a newer refresh advances generation", () => | 467 | it.effect("ignores stale enrichment callbacks after a newer refresh advances generation", () => |
| ... | @@ -522,7 +522,7 @@ describe("makeManagedServerProvider", () => { | ... | @@ -522,7 +522,7 @@ describe("makeManagedServerProvider", () => { |
| 522 | ]); | 522 | ]); |
| 523 | assert.deepStrictEqual(latest, enrichedSnapshotSecond); | 523 | assert.deepStrictEqual(latest, enrichedSnapshotSecond); |
| 524 | }), | 524 | }), |
| 525 | ).pipe(Effect.provide(AlwaysRunTestLayer)), | 525 | ).pipe(Effect.provide(layerAlwaysRunTest)), |
| 526 | ); | 526 | ); |
| 527 | 527 | ||
| 528 | it.effect("applies runtime usage updates onto the published snapshot", () => | 528 | it.effect("applies runtime usage updates onto the published snapshot", () => |
| ... | @@ -584,7 +584,7 @@ describe("makeManagedServerProvider", () => { | ... | @@ -584,7 +584,7 @@ describe("makeManagedServerProvider", () => { |
| 584 | }); | 584 | }); |
| 585 | assert.deepStrictEqual(yield* provider.getSnapshot, update); | 585 | assert.deepStrictEqual(yield* provider.getSnapshot, update); |
| 586 | }), | 586 | }), |
| 587 | ).pipe(Effect.provide(AlwaysRunTestLayer)), | 587 | ).pipe(Effect.provide(layerAlwaysRunTest)), |
| 588 | ); | 588 | ); |
| 589 | 589 | ||
| 590 | it.effect("keeps live usage windows across a failed probe and a stale enrichment", () => | 590 | it.effect("keeps live usage windows across a failed probe and a stale enrichment", () => |
| ... | @@ -655,6 +655,6 @@ describe("makeManagedServerProvider", () => { | ... | @@ -655,6 +655,6 @@ describe("makeManagedServerProvider", () => { |
| 655 | assert.strictEqual(refreshed.message, refreshedSnapshotSecond.message); | 655 | assert.strictEqual(refreshed.message, refreshedSnapshotSecond.message); |
| 656 | assert.deepStrictEqual(refreshed.usageLimits?.windows, [liveWindow]); | 656 | assert.deepStrictEqual(refreshed.usageLimits?.windows, [liveWindow]); |
| 657 | }), | 657 | }), |
| 658 | ).pipe(Effect.provide(AlwaysRunTestLayer)), | 658 | ).pipe(Effect.provide(layerAlwaysRunTest)), |
| 659 | ); | 659 | ); |
| 660 | }); | 660 | }); |
apps/server/src/provider/opencode2/OpenCode2Client.events.test.ts+8-6| ... | @@ -54,7 +54,7 @@ const newerServerStream = Effect.gen(function* () { | ... | @@ -54,7 +54,7 @@ const newerServerStream = Effect.gen(function* () { |
| 54 | ]; | 54 | ]; |
| 55 | }); | 55 | }); |
| 56 | 56 | ||
| 57 | const serving = (events: ReadonlyArray<unknown>) => | 57 | const layerServing = (events: ReadonlyArray<unknown>) => |
| 58 | Layer.succeed( | 58 | Layer.succeed( |
| 59 | HttpClient.HttpClient, | 59 | HttpClient.HttpClient, |
| 60 | HttpClient.make((request) => | 60 | HttpClient.make((request) => |
| ... | @@ -75,7 +75,7 @@ const connect = Effect.gen(function* () { | ... | @@ -75,7 +75,7 @@ const connect = Effect.gen(function* () { |
| 75 | }); | 75 | }); |
| 76 | 76 | ||
| 77 | /** Sends `events`, then keeps the connection open without another byte, like a frozen server. */ | 77 | /** Sends `events`, then keeps the connection open without another byte, like a frozen server. */ |
| 78 | const servingThenSilent = (events: ReadonlyArray<unknown>) => | 78 | const layerServingThenSilent = (events: ReadonlyArray<unknown>) => |
| 79 | Layer.succeed( | 79 | Layer.succeed( |
| 80 | HttpClient.HttpClient, | 80 | HttpClient.HttpClient, |
| 81 | HttpClient.make((request) => | 81 | HttpClient.make((request) => |
| ... | @@ -103,7 +103,9 @@ describe("OpenCode2Client events", () => { | ... | @@ -103,7 +103,9 @@ describe("OpenCode2Client events", () => { |
| 103 | const { events } = yield* connect.pipe( | 103 | const { events } = yield* connect.pipe( |
| 104 | Effect.provide( | 104 | Effect.provide( |
| 105 | OpenCode2Client.layer.pipe( | 105 | OpenCode2Client.layer.pipe( |
| 106 | Layer.provide(servingThenSilent([{ id: "evt_1", type: "server.connected", data: {} }])), | 106 | Layer.provide( |
| 107 | layerServingThenSilent([{ id: "evt_1", type: "server.connected", data: {} }]), | ||
| 108 | ), | ||
| 107 | ), | 109 | ), |
| 108 | ), | 110 | ), |
| 109 | ); | 111 | ); |
| ... | @@ -124,7 +126,7 @@ describe("OpenCode2Client events", () => { | ... | @@ -124,7 +126,7 @@ describe("OpenCode2Client events", () => { |
| 124 | Effect.provide( | 126 | Effect.provide( |
| 125 | OpenCode2Client.layer.pipe( | 127 | OpenCode2Client.layer.pipe( |
| 126 | Layer.provide( | 128 | Layer.provide( |
| 127 | serving([ | 129 | layerServing([ |
| 128 | { | 130 | { |
| 129 | id: "evt_1", | 131 | id: "evt_1", |
| 130 | created: 1, | 132 | created: 1, |
| ... | @@ -167,7 +169,7 @@ describe("OpenCode2Client events", () => { | ... | @@ -167,7 +169,7 @@ describe("OpenCode2Client events", () => { |
| 167 | Effect.gen(function* () { | 169 | Effect.gen(function* () { |
| 168 | const events = yield* newerServerStream; | 170 | const events = yield* newerServerStream; |
| 169 | const { events: subscribe } = yield* connect.pipe( | 171 | const { events: subscribe } = yield* connect.pipe( |
| 170 | Effect.provide(OpenCode2Client.layer.pipe(Layer.provide(serving(events)))), | 172 | Effect.provide(OpenCode2Client.layer.pipe(Layer.provide(layerServing(events)))), |
| 171 | ); | 173 | ); |
| 172 | const types = yield* (yield* subscribe).pipe( | 174 | const types = yield* (yield* subscribe).pipe( |
| 173 | Stream.map((event) => event.type), | 175 | Stream.map((event) => event.type), |
| ... | @@ -185,7 +187,7 @@ describe("OpenCode2Client events", () => { | ... | @@ -185,7 +187,7 @@ describe("OpenCode2Client events", () => { |
| 185 | Effect.gen(function* () { | 187 | Effect.gen(function* () { |
| 186 | const events = yield* newerServerStream; | 188 | const events = yield* newerServerStream; |
| 187 | const { client } = yield* connect.pipe( | 189 | const { client } = yield* connect.pipe( |
| 188 | Effect.provide(OpenCode2Client.layer.pipe(Layer.provide(serving(events)))), | 190 | Effect.provide(OpenCode2Client.layer.pipe(Layer.provide(layerServing(events)))), |
| 189 | ); | 191 | ); |
| 190 | const failure = yield* client.event.subscribe().pipe(Stream.runDrain, Effect.flip); | 192 | const failure = yield* client.event.subscribe().pipe(Stream.runDrain, Effect.flip); |
| 191 | assert.strictEqual(failure._tag, "ClientError"); | 193 | assert.strictEqual(failure._tag, "ClientError"); |
apps/server/src/provider/opencode2/OpenCode2Client.test.ts+2-2| ... | @@ -12,7 +12,7 @@ const INFO_BODY = '{"version":"2.0.18","pid":4242,"urls":[],"paths":{"tmp":"/tmp | ... | @@ -12,7 +12,7 @@ const INFO_BODY = '{"version":"2.0.18","pid":4242,"urls":[],"paths":{"tmp":"/tmp |
| 12 | /** Sends one request through the client and returns the Authorization header it carried. */ | 12 | /** Sends one request through the client and returns the Authorization header it carried. */ |
| 13 | const authorizationFor = (password: string | Redacted.Redacted) => { | 13 | const authorizationFor = (password: string | Redacted.Redacted) => { |
| 14 | const seen: Array<string | undefined> = []; | 14 | const seen: Array<string | undefined> = []; |
| 15 | const capturing = Layer.succeed( | 15 | const layerCapturing = Layer.succeed( |
| 16 | HttpClient.HttpClient, | 16 | HttpClient.HttpClient, |
| 17 | HttpClient.make((request) => | 17 | HttpClient.make((request) => |
| 18 | Effect.sync(() => { | 18 | Effect.sync(() => { |
| ... | @@ -29,7 +29,7 @@ const authorizationFor = (password: string | Redacted.Redacted) => { | ... | @@ -29,7 +29,7 @@ const authorizationFor = (password: string | Redacted.Redacted) => { |
| 29 | const { client } = yield* opencode.connect({ baseUrl: "http://127.0.0.1:4096", password }); | 29 | const { client } = yield* opencode.connect({ baseUrl: "http://127.0.0.1:4096", password }); |
| 30 | yield* client.server.info(); | 30 | yield* client.server.info(); |
| 31 | return seen[0]; | 31 | return seen[0]; |
| 32 | }).pipe(Effect.provide(OpenCode2Client.layer.pipe(Layer.provide(capturing)))); | 32 | }).pipe(Effect.provide(OpenCode2Client.layer.pipe(Layer.provide(layerCapturing)))); |
| 33 | }; | 33 | }; |
| 34 | 34 | ||
| 35 | const utf8Basic = (password: string) => | 35 | const utf8Basic = (password: string) => |
apps/server/src/provider/opencode2/OpenCode2Server.live.test.ts+1-1| ... | @@ -229,7 +229,7 @@ describe.runIf(binaryPath !== undefined)("OpenCode2Server live", () => { | ... | @@ -229,7 +229,7 @@ describe.runIf(binaryPath !== undefined)("OpenCode2Server live", () => { |
| 229 | Effect.provide( | 229 | Effect.provide( |
| 230 | Layer.mergeAll( | 230 | Layer.mergeAll( |
| 231 | OpenCode2Client.layer, | 231 | OpenCode2Client.layer, |
| 232 | OpenCodeRuntime.OpenCodeRuntimeLive.pipe(Layer.provide(OpenCodeServerLedger.layerTest)), | 232 | OpenCodeRuntime.layer.pipe(Layer.provide(OpenCodeServerLedger.layerTest)), |
| 233 | ).pipe(Layer.provideMerge(Layer.mergeAll(NodeServices.layer, FetchHttpClient.layer))), | 233 | ).pipe(Layer.provideMerge(Layer.mergeAll(NodeServices.layer, FetchHttpClient.layer))), |
| 234 | ), | 234 | ), |
| 235 | ), | 235 | ), |
apps/server/src/provider/opencode2/OpenCode2Server.test.ts+19-13| ... | @@ -27,7 +27,7 @@ const INFO_BODY = | ... | @@ -27,7 +27,7 @@ const INFO_BODY = |
| 27 | const UNAUTHORIZED_BODY = '{"_tag":"UnauthorizedError","message":"Authentication required"}'; | 27 | const UNAUTHORIZED_BODY = '{"_tag":"UnauthorizedError","message":"Authentication required"}'; |
| 28 | const SPA_BODY = "<!doctype html><html><head><title>OpenCode</title></head></html>"; | 28 | const SPA_BODY = "<!doctype html><html><head><title>OpenCode</title></head></html>"; |
| 29 | 29 | ||
| 30 | const serverReplying = (reply: { | 30 | const layerServerReplying = (reply: { |
| 31 | readonly status: number; | 31 | readonly status: number; |
| 32 | readonly contentType?: string; | 32 | readonly contentType?: string; |
| 33 | readonly body: string; | 33 | readonly body: string; |
| ... | @@ -58,7 +58,7 @@ describe("OpenCode2Server.verifyServer", () => { | ... | @@ -58,7 +58,7 @@ describe("OpenCode2Server.verifyServer", () => { |
| 58 | it.effect("returns the version of an authenticated OpenCode 2 server", () => | 58 | it.effect("returns the version of an authenticated OpenCode 2 server", () => |
| 59 | Effect.gen(function* () { | 59 | Effect.gen(function* () { |
| 60 | const version = yield* verify( | 60 | const version = yield* verify( |
| 61 | serverReplying({ status: 200, contentType: "application/json", body: INFO_BODY }), | 61 | layerServerReplying({ status: 200, contentType: "application/json", body: INFO_BODY }), |
| 62 | ); | 62 | ); |
| 63 | assert.strictEqual(version, "2.0.18"); | 63 | assert.strictEqual(version, "2.0.18"); |
| 64 | }), | 64 | }), |
| ... | @@ -67,7 +67,11 @@ describe("OpenCode2Server.verifyServer", () => { | ... | @@ -67,7 +67,11 @@ describe("OpenCode2Server.verifyServer", () => { |
| 67 | it.effect("reports a rejected password, not a wrong server", () => | 67 | it.effect("reports a rejected password, not a wrong server", () => |
| 68 | Effect.gen(function* () { | 68 | Effect.gen(function* () { |
| 69 | const error = yield* verify( | 69 | const error = yield* verify( |
| 70 | serverReplying({ status: 401, contentType: "application/json", body: UNAUTHORIZED_BODY }), | 70 | layerServerReplying({ |
| 71 | status: 401, | ||
| 72 | contentType: "application/json", | ||
| 73 | body: UNAUTHORIZED_BODY, | ||
| 74 | }), | ||
| 71 | ).pipe(Effect.flip); | 75 | ).pipe(Effect.flip); |
| 72 | assert.include(error.detail, "rejected the server password"); | 76 | assert.include(error.detail, "rejected the server password"); |
| 73 | }), | 77 | }), |
| ... | @@ -76,7 +80,7 @@ describe("OpenCode2Server.verifyServer", () => { | ... | @@ -76,7 +80,7 @@ describe("OpenCode2Server.verifyServer", () => { |
| 76 | // 1.x rejects a wrong password with an empty 401, which the client cannot decode. | 80 | // 1.x rejects a wrong password with an empty 401, which the client cannot decode. |
| 77 | it.effect("reports an empty-body 401 as a rejected password", () => | 81 | it.effect("reports an empty-body 401 as a rejected password", () => |
| 78 | Effect.gen(function* () { | 82 | Effect.gen(function* () { |
| 79 | const error = yield* verify(serverReplying({ status: 401, body: "" })).pipe(Effect.flip); | 83 | const error = yield* verify(layerServerReplying({ status: 401, body: "" })).pipe(Effect.flip); |
| 80 | assert.include(error.detail, "rejected the server password"); | 84 | assert.include(error.detail, "rejected the server password"); |
| 81 | }), | 85 | }), |
| 82 | ); | 86 | ); |
| ... | @@ -85,7 +89,7 @@ describe("OpenCode2Server.verifyServer", () => { | ... | @@ -85,7 +89,7 @@ describe("OpenCode2Server.verifyServer", () => { |
| 85 | Effect.gen(function* () { | 89 | Effect.gen(function* () { |
| 86 | for (const status of [500, 502]) { | 90 | for (const status of [500, 502]) { |
| 87 | const error = yield* verify( | 91 | const error = yield* verify( |
| 88 | serverReplying({ status, contentType: "text/plain", body: "upstream failed" }), | 92 | layerServerReplying({ status, contentType: "text/plain", body: "upstream failed" }), |
| 89 | ).pipe(Effect.flip); | 93 | ).pipe(Effect.flip); |
| 90 | assert.include(error.detail, `returned HTTP ${status}`); | 94 | assert.include(error.detail, `returned HTTP ${status}`); |
| 91 | } | 95 | } |
| ... | @@ -96,7 +100,7 @@ describe("OpenCode2Server.verifyServer", () => { | ... | @@ -96,7 +100,7 @@ describe("OpenCode2Server.verifyServer", () => { |
| 96 | it.effect("rejects a server that answers with the web UI's HTML", () => | 100 | it.effect("rejects a server that answers with the web UI's HTML", () => |
| 97 | Effect.gen(function* () { | 101 | Effect.gen(function* () { |
| 98 | const error = yield* verify( | 102 | const error = yield* verify( |
| 99 | serverReplying({ status: 200, contentType: "text/html", body: SPA_BODY }), | 103 | layerServerReplying({ status: 200, contentType: "text/html", body: SPA_BODY }), |
| 100 | ).pipe(Effect.flip); | 104 | ).pipe(Effect.flip); |
| 101 | assert.include(error.detail, "is not an OpenCode 2 server"); | 105 | assert.include(error.detail, "is not an OpenCode 2 server"); |
| 102 | }), | 106 | }), |
| ... | @@ -129,24 +133,26 @@ describe("OpenCode2Server error details", () => { | ... | @@ -129,24 +133,26 @@ describe("OpenCode2Server error details", () => { |
| 129 | Effect.provide( | 133 | Effect.provide( |
| 130 | Layer.mergeAll( | 134 | Layer.mergeAll( |
| 131 | OpenCode2Client.layer.pipe(Layer.provide(httpClient)), | 135 | OpenCode2Client.layer.pipe(Layer.provide(httpClient)), |
| 132 | OpenCodeRuntime.OpenCodeRuntimeLive.pipe(Layer.provide(OpenCodeServerLedger.layerTest)), | 136 | OpenCodeRuntime.layer.pipe(Layer.provide(OpenCodeServerLedger.layerTest)), |
| 133 | ).pipe(Layer.provideMerge(NodeServices.layer)), | 137 | ).pipe(Layer.provideMerge(NodeServices.layer)), |
| 134 | ), | 138 | ), |
| 135 | ); | 139 | ); |
| 136 | 140 | ||
| 137 | it.effect("never include the server URL", () => | 141 | it.effect("never include the server URL", () => |
| 138 | Effect.gen(function* () { | 142 | Effect.gen(function* () { |
| 139 | const hanging = Layer.succeed( | 143 | const layerHanging = Layer.succeed( |
| 140 | HttpClient.HttpClient, | 144 | HttpClient.HttpClient, |
| 141 | HttpClient.make(() => Effect.never), | 145 | HttpClient.make(() => Effect.never), |
| 142 | ); | 146 | ); |
| 143 | const timedOut = yield* detailFor(hanging).pipe(Effect.forkChild); | 147 | const timedOut = yield* detailFor(layerHanging).pipe(Effect.forkChild); |
| 144 | yield* TestClock.adjust("10 seconds"); | 148 | yield* TestClock.adjust("10 seconds"); |
| 145 | const details = [ | 149 | const details = [ |
| 146 | yield* detailFor(FetchHttpClient.layer), | 150 | yield* detailFor(FetchHttpClient.layer), |
| 147 | yield* detailFor(serverReplying({ status: 401, body: "" })), | 151 | yield* detailFor(layerServerReplying({ status: 401, body: "" })), |
| 148 | yield* detailFor(serverReplying({ status: 502, contentType: "text/plain", body: "" })), | 152 | yield* detailFor(layerServerReplying({ status: 502, contentType: "text/plain", body: "" })), |
| 149 | yield* detailFor(serverReplying({ status: 200, contentType: "text/html", body: SPA_BODY })), | 153 | yield* detailFor( |
| 154 | layerServerReplying({ status: 200, contentType: "text/html", body: SPA_BODY }), | ||
| 155 | ), | ||
| 150 | yield* Fiber.join(timedOut), | 156 | yield* Fiber.join(timedOut), |
| 151 | ]; | 157 | ]; |
| 152 | assert.deepStrictEqual(details, [ | 158 | assert.deepStrictEqual(details, [ |
| ... | @@ -244,7 +250,7 @@ describe("OpenCode2Server spawned server", () => { | ... | @@ -244,7 +250,7 @@ describe("OpenCode2Server spawned server", () => { |
| 244 | Effect.provide( | 250 | Effect.provide( |
| 245 | Layer.mergeAll( | 251 | Layer.mergeAll( |
| 246 | OpenCode2Client.layer, | 252 | OpenCode2Client.layer, |
| 247 | OpenCodeRuntime.OpenCodeRuntimeLive.pipe(Layer.provide(OpenCodeServerLedger.layerTest)), | 253 | OpenCodeRuntime.layer.pipe(Layer.provide(OpenCodeServerLedger.layerTest)), |
| 248 | ).pipe(Layer.provideMerge(Layer.mergeAll(NodeServices.layer, FetchHttpClient.layer))), | 254 | ).pipe(Layer.provideMerge(Layer.mergeAll(NodeServices.layer, FetchHttpClient.layer))), |
| 249 | ), | 255 | ), |
| 250 | ), | 256 | ), |
apps/server/src/provider/opencodeRuntime.environment.test.ts+1-1| ... | @@ -227,7 +227,7 @@ server.listen(0, "127.0.0.1", () => { | ... | @@ -227,7 +227,7 @@ server.listen(0, "127.0.0.1", () => { |
| 227 | }).pipe( | 227 | }).pipe( |
| 228 | Effect.scoped, | 228 | Effect.scoped, |
| 229 | Effect.provide([ | 229 | Effect.provide([ |
| 230 | OpenCodeRuntime.OpenCodeRuntimeLive.pipe( | 230 | OpenCodeRuntime.layer.pipe( |
| 231 | Layer.provide(OpenCodeServerLedger.layerTest), | 231 | Layer.provide(OpenCodeServerLedger.layerTest), |
| 232 | Layer.provideMerge(NodeServices.layer), | 232 | Layer.provideMerge(NodeServices.layer), |
| 233 | ), | 233 | ), |
apps/server/src/provider/opencodeRuntime.inventory.test.ts+2-2| ... | @@ -18,12 +18,12 @@ import { | ... | @@ -18,12 +18,12 @@ import { |
| 18 | import * as OpenCodeRuntime from "./opencodeRuntime.ts"; | 18 | import * as OpenCodeRuntime from "./opencodeRuntime.ts"; |
| 19 | import * as OpenCodeServerLedger from "./OpenCodeServerLedger.ts"; | 19 | import * as OpenCodeServerLedger from "./OpenCodeServerLedger.ts"; |
| 20 | 20 | ||
| 21 | const testLayer = OpenCodeRuntime.OpenCodeRuntimeLive.pipe( | 21 | const layerTest = OpenCodeRuntime.layer.pipe( |
| 22 | Layer.provide(OpenCodeServerLedger.layerTest), | 22 | Layer.provide(OpenCodeServerLedger.layerTest), |
| 23 | Layer.provideMerge(NodeServices.layer), | 23 | Layer.provideMerge(NodeServices.layer), |
| 24 | ); | 24 | ); |
| 25 | 25 | ||
| 26 | it.layer(testLayer)("OpenCodeRuntime inventory", (it) => { | 26 | it.layer(layerTest)("OpenCodeRuntime inventory", (it) => { |
| 27 | it.effect("aborts pending SDK requests when inventory loading is interrupted", () => | 27 | it.effect("aborts pending SDK requests when inventory loading is interrupted", () => |
| 28 | Effect.gen(function* () { | 28 | Effect.gen(function* () { |
| 29 | const runtime = yield* OpenCodeRuntime.OpenCodeRuntime; | 29 | const runtime = yield* OpenCodeRuntime.OpenCodeRuntime; |
apps/server/src/provider/opencodeRuntime.ts+1-1| ... | @@ -1106,6 +1106,6 @@ export class OpenCodeRuntime extends Context.Service<OpenCodeRuntime, OpenCodeRu | ... | @@ -1106,6 +1106,6 @@ export class OpenCodeRuntime extends Context.Service<OpenCodeRuntime, OpenCodeRu |
| 1106 | "t3/provider/opencodeRuntime", | 1106 | "t3/provider/opencodeRuntime", |
| 1107 | ) {} | 1107 | ) {} |
| 1108 | 1108 | ||
| 1109 | export const OpenCodeRuntimeLive = Layer.effect(OpenCodeRuntime, makeOpenCodeRuntime).pipe( | 1109 | export const layer = Layer.effect(OpenCodeRuntime, makeOpenCodeRuntime).pipe( |
| 1110 | Layer.provide(NetService.layer), | 1110 | Layer.provide(NetService.layer), |
| 1111 | ); | 1111 | ); |
apps/server/src/provider/providerCompatibility.test.ts+3-3| ... | @@ -10,7 +10,7 @@ import * as Ref from "effect/Ref"; | ... | @@ -10,7 +10,7 @@ import * as Ref from "effect/Ref"; |
| 10 | import * as Stream from "effect/Stream"; | 10 | import * as Stream from "effect/Stream"; |
| 11 | import * as ServerConfig from "../config.ts"; | 11 | import * as ServerConfig from "../config.ts"; |
| 12 | import * as ModelManifest from "./ModelManifest.ts"; | 12 | import * as ModelManifest from "./ModelManifest.ts"; |
| 13 | import { ProviderRegistryLive } from "./Layers/ProviderRegistry.ts"; | 13 | import * as ProviderRegistryLayer from "./Layers/ProviderRegistry.ts"; |
| 14 | import * as ProviderRegistry from "./Services/ProviderRegistry.ts"; | 14 | import * as ProviderRegistry from "./Services/ProviderRegistry.ts"; |
| 15 | import * as ProviderInstanceRegistry from "./Services/ProviderInstanceRegistry.ts"; | 15 | import * as ProviderInstanceRegistry from "./Services/ProviderInstanceRegistry.ts"; |
| 16 | import type { ProviderInstance } from "./ProviderDriver.ts"; | 16 | import type { ProviderInstance } from "./ProviderDriver.ts"; |
| ... | @@ -323,7 +323,7 @@ it.effect("a remote policy refresh preserves a newer health result on the regist | ... | @@ -323,7 +323,7 @@ it.effect("a remote policy refresh preserves a newer health result on the regist |
| 323 | })), | 323 | })), |
| 324 | ), | 324 | ), |
| 325 | ); | 325 | ); |
| 326 | const dependencies = Layer.mergeAll( | 326 | const layerDependencies = Layer.mergeAll( |
| 327 | Layer.succeed(ModelManifest.ModelManifest, { | 327 | Layer.succeed(ModelManifest.ModelManifest, { |
| 328 | current: Ref.get(manifest), | 328 | current: Ref.get(manifest), |
| 329 | refresh, | 329 | refresh, |
| ... | @@ -362,7 +362,7 @@ it.effect("a remote policy refresh preserves a newer health result on the regist | ... | @@ -362,7 +362,7 @@ it.effect("a remote policy refresh preserves a newer health result on the regist |
| 362 | assert.strictEqual(updated?.compatibilityAdvisory?.status, "supported"); | 362 | assert.strictEqual(updated?.compatibilityAdvisory?.status, "supported"); |
| 363 | assert.strictEqual(updated?.status, "error"); | 363 | assert.strictEqual(updated?.status, "error"); |
| 364 | assert.strictEqual(updated?.message, "Authentication failed"); | 364 | assert.strictEqual(updated?.message, "Authentication failed"); |
| 365 | }).pipe(Effect.provide(ProviderRegistryLive.pipe(Layer.provide(dependencies)))); | 365 | }).pipe(Effect.provide(ProviderRegistryLayer.layer.pipe(Layer.provide(layerDependencies)))); |
| 366 | }).pipe(Effect.scoped), | 366 | }).pipe(Effect.scoped), |
| 367 | ); | 367 | ); |
| 368 | 368 |
apps/server/src/provider/providerMaintenanceRunner.test.ts+52-52| ... | @@ -42,7 +42,7 @@ const encoder = new TextEncoder(); | ... | @@ -42,7 +42,7 @@ const encoder = new TextEncoder(); |
| 42 | // `{ command, args }` assertions below hold deterministically on any host | 42 | // `{ command, args }` assertions below hold deterministically on any host |
| 43 | // (including Windows). Windows-specific resolution is covered by the dedicated | 43 | // (including Windows). Windows-specific resolution is covered by the dedicated |
| 44 | // win32 case at the end of this suite. | 44 | // win32 case at the end of this suite. |
| 45 | const NonWindowsPlatform = Layer.succeed(HostProcessPlatform, "linux"); | 45 | const layerNonWindowsPlatform = Layer.succeed(HostProcessPlatform, "linux"); |
| 46 | 46 | ||
| 47 | function lifecycleFor(provider: ProviderDriverKind): ProviderMaintenanceCapabilities { | 47 | function lifecycleFor(provider: ProviderDriverKind): ProviderMaintenanceCapabilities { |
| 48 | if (provider === NATIVE_CLI_DRIVER) { | 48 | if (provider === NATIVE_CLI_DRIVER) { |
| ... | @@ -92,7 +92,7 @@ const baseOpenCodeProvider: ServerProvider = { | ... | @@ -92,7 +92,7 @@ const baseOpenCodeProvider: ServerProvider = { |
| 92 | driver: OPENCODE_DRIVER, | 92 | driver: OPENCODE_DRIVER, |
| 93 | }; | 93 | }; |
| 94 | 94 | ||
| 95 | const latestVersionHttpClient = (version: string) => | 95 | const layerLatestVersionHttpClient = (version: string) => |
| 96 | Layer.succeed( | 96 | Layer.succeed( |
| 97 | HttpClient.HttpClient, | 97 | HttpClient.HttpClient, |
| 98 | HttpClient.make((request) => | 98 | HttpClient.make((request) => |
| ... | @@ -126,7 +126,7 @@ function mockHandle(result: { | ... | @@ -126,7 +126,7 @@ function mockHandle(result: { |
| 126 | }); | 126 | }); |
| 127 | } | 127 | } |
| 128 | 128 | ||
| 129 | function mockSpawnerLayer( | 129 | function layerMockSpawner( |
| 130 | handler: ( | 130 | handler: ( |
| 131 | command: string, | 131 | command: string, |
| 132 | args: ReadonlyArray<string>, | 132 | args: ReadonlyArray<string>, |
| ... | @@ -265,9 +265,9 @@ describe("providerMaintenanceRunner", () => { | ... | @@ -265,9 +265,9 @@ describe("providerMaintenanceRunner", () => { |
| 265 | }).pipe( | 265 | }).pipe( |
| 266 | Effect.provide( | 266 | Effect.provide( |
| 267 | Layer.mergeAll( | 267 | Layer.mergeAll( |
| 268 | NonWindowsPlatform, | 268 | layerNonWindowsPlatform, |
| 269 | latestVersionHttpClient("0.0.0"), | 269 | layerLatestVersionHttpClient("0.0.0"), |
| 270 | mockSpawnerLayer((command, args) => { | 270 | layerMockSpawner((command, args) => { |
| 271 | calls.push({ command, args }); | 271 | calls.push({ command, args }); |
| 272 | return { stdout: "updated" }; | 272 | return { stdout: "updated" }; |
| 273 | }), | 273 | }), |
| ... | @@ -294,9 +294,9 @@ describe("providerMaintenanceRunner", () => { | ... | @@ -294,9 +294,9 @@ describe("providerMaintenanceRunner", () => { |
| 294 | }).pipe( | 294 | }).pipe( |
| 295 | Effect.provide( | 295 | Effect.provide( |
| 296 | Layer.mergeAll( | 296 | Layer.mergeAll( |
| 297 | NonWindowsPlatform, | 297 | layerNonWindowsPlatform, |
| 298 | latestVersionHttpClient("0.0.0"), | 298 | layerLatestVersionHttpClient("0.0.0"), |
| 299 | mockSpawnerLayer(() => ({ stdout: "updated" })), | 299 | layerMockSpawner(() => ({ stdout: "updated" })), |
| 300 | ), | 300 | ), |
| 301 | ), | 301 | ), |
| 302 | ); | 302 | ); |
| ... | @@ -321,9 +321,9 @@ describe("providerMaintenanceRunner", () => { | ... | @@ -321,9 +321,9 @@ describe("providerMaintenanceRunner", () => { |
| 321 | }).pipe( | 321 | }).pipe( |
| 322 | Effect.provide( | 322 | Effect.provide( |
| 323 | Layer.mergeAll( | 323 | Layer.mergeAll( |
| 324 | NonWindowsPlatform, | 324 | layerNonWindowsPlatform, |
| 325 | latestVersionHttpClient("0.0.0"), | 325 | layerLatestVersionHttpClient("0.0.0"), |
| 326 | mockSpawnerLayer(() => ({ stdout: "updated" })), | 326 | layerMockSpawner(() => ({ stdout: "updated" })), |
| 327 | ), | 327 | ), |
| 328 | ), | 328 | ), |
| 329 | ); | 329 | ); |
| ... | @@ -354,9 +354,9 @@ describe("providerMaintenanceRunner", () => { | ... | @@ -354,9 +354,9 @@ describe("providerMaintenanceRunner", () => { |
| 354 | }).pipe( | 354 | }).pipe( |
| 355 | Effect.provide( | 355 | Effect.provide( |
| 356 | Layer.mergeAll( | 356 | Layer.mergeAll( |
| 357 | NonWindowsPlatform, | 357 | layerNonWindowsPlatform, |
| 358 | latestVersionHttpClient("0.0.0"), | 358 | layerLatestVersionHttpClient("0.0.0"), |
| 359 | mockSpawnerLayer((_command, _args, options) => { | 359 | layerMockSpawner((_command, _args, options) => { |
| 360 | seen.push(options.env); | 360 | seen.push(options.env); |
| 361 | return { stdout: "updated" }; | 361 | return { stdout: "updated" }; |
| 362 | }), | 362 | }), |
| ... | @@ -395,9 +395,9 @@ describe("providerMaintenanceRunner", () => { | ... | @@ -395,9 +395,9 @@ describe("providerMaintenanceRunner", () => { |
| 395 | }).pipe( | 395 | }).pipe( |
| 396 | Effect.provide( | 396 | Effect.provide( |
| 397 | Layer.mergeAll( | 397 | Layer.mergeAll( |
| 398 | NonWindowsPlatform, | 398 | layerNonWindowsPlatform, |
| 399 | latestVersionHttpClient("0.0.0"), | 399 | layerLatestVersionHttpClient("0.0.0"), |
| 400 | mockSpawnerLayer((command, args) => { | 400 | layerMockSpawner((command, args) => { |
| 401 | calls.push({ command, args }); | 401 | calls.push({ command, args }); |
| 402 | return { stdout: "updated" }; | 402 | return { stdout: "updated" }; |
| 403 | }), | 403 | }), |
| ... | @@ -440,9 +440,9 @@ describe("providerMaintenanceRunner", () => { | ... | @@ -440,9 +440,9 @@ describe("providerMaintenanceRunner", () => { |
| 440 | }).pipe( | 440 | }).pipe( |
| 441 | Effect.provide( | 441 | Effect.provide( |
| 442 | Layer.mergeAll( | 442 | Layer.mergeAll( |
| 443 | NonWindowsPlatform, | 443 | layerNonWindowsPlatform, |
| 444 | latestVersionHttpClient("0.0.0"), | 444 | layerLatestVersionHttpClient("0.0.0"), |
| 445 | mockSpawnerLayer((command) => { | 445 | layerMockSpawner((command) => { |
| 446 | calls.push(command); | 446 | calls.push(command); |
| 447 | return { stdout: "updated" }; | 447 | return { stdout: "updated" }; |
| 448 | }), | 448 | }), |
| ... | @@ -490,9 +490,9 @@ describe("providerMaintenanceRunner", () => { | ... | @@ -490,9 +490,9 @@ describe("providerMaintenanceRunner", () => { |
| 490 | }).pipe( | 490 | }).pipe( |
| 491 | Effect.provide( | 491 | Effect.provide( |
| 492 | Layer.mergeAll( | 492 | Layer.mergeAll( |
| 493 | NonWindowsPlatform, | 493 | layerNonWindowsPlatform, |
| 494 | latestVersionHttpClient("0.0.0"), | 494 | layerLatestVersionHttpClient("0.0.0"), |
| 495 | mockSpawnerLayer((command, args) => { | 495 | layerMockSpawner((command, args) => { |
| 496 | calls.push({ command, args }); | 496 | calls.push({ command, args }); |
| 497 | return { stdout: "updated" }; | 497 | return { stdout: "updated" }; |
| 498 | }), | 498 | }), |
| ... | @@ -521,9 +521,9 @@ describe("providerMaintenanceRunner", () => { | ... | @@ -521,9 +521,9 @@ describe("providerMaintenanceRunner", () => { |
| 521 | }).pipe( | 521 | }).pipe( |
| 522 | Effect.provide( | 522 | Effect.provide( |
| 523 | Layer.mergeAll( | 523 | Layer.mergeAll( |
| 524 | NonWindowsPlatform, | 524 | layerNonWindowsPlatform, |
| 525 | latestVersionHttpClient("0.0.0"), | 525 | layerLatestVersionHttpClient("0.0.0"), |
| 526 | mockSpawnerLayer((command, args) => { | 526 | layerMockSpawner((command, args) => { |
| 527 | calls.push({ command, args }); | 527 | calls.push({ command, args }); |
| 528 | return { stdout: "updated" }; | 528 | return { stdout: "updated" }; |
| 529 | }), | 529 | }), |
| ... | @@ -594,9 +594,9 @@ describe("providerMaintenanceRunner", () => { | ... | @@ -594,9 +594,9 @@ describe("providerMaintenanceRunner", () => { |
| 594 | }).pipe( | 594 | }).pipe( |
| 595 | Effect.provide( | 595 | Effect.provide( |
| 596 | Layer.mergeAll( | 596 | Layer.mergeAll( |
| 597 | NonWindowsPlatform, | 597 | layerNonWindowsPlatform, |
| 598 | latestVersionHttpClient("0.124.0-alpha.3"), | 598 | layerLatestVersionHttpClient("0.124.0-alpha.3"), |
| 599 | mockSpawnerLayer((command, args) => { | 599 | layerMockSpawner((command, args) => { |
| 600 | calls.push({ command, args }); | 600 | calls.push({ command, args }); |
| 601 | return { stdout: "updated" }; | 601 | return { stdout: "updated" }; |
| 602 | }), | 602 | }), |
| ... | @@ -619,9 +619,9 @@ describe("providerMaintenanceRunner", () => { | ... | @@ -619,9 +619,9 @@ describe("providerMaintenanceRunner", () => { |
| 619 | }).pipe( | 619 | }).pipe( |
| 620 | Effect.provide( | 620 | Effect.provide( |
| 621 | Layer.mergeAll( | 621 | Layer.mergeAll( |
| 622 | NonWindowsPlatform, | 622 | layerNonWindowsPlatform, |
| 623 | latestVersionHttpClient("0.0.0"), | 623 | layerLatestVersionHttpClient("0.0.0"), |
| 624 | mockSpawnerLayer(() => ({ stderr: "permission denied", code: 1 })), | 624 | layerMockSpawner(() => ({ stderr: "permission denied", code: 1 })), |
| 625 | ), | 625 | ), |
| 626 | ), | 626 | ), |
| 627 | ), | 627 | ), |
| ... | @@ -645,9 +645,9 @@ describe("providerMaintenanceRunner", () => { | ... | @@ -645,9 +645,9 @@ describe("providerMaintenanceRunner", () => { |
| 645 | }).pipe( | 645 | }).pipe( |
| 646 | Effect.provide( | 646 | Effect.provide( |
| 647 | Layer.mergeAll( | 647 | Layer.mergeAll( |
| 648 | NonWindowsPlatform, | 648 | layerNonWindowsPlatform, |
| 649 | latestVersionHttpClient("9.9.9"), | 649 | layerLatestVersionHttpClient("9.9.9"), |
| 650 | mockSpawnerLayer(() => ({ stdout: "updated" })), | 650 | layerMockSpawner(() => ({ stdout: "updated" })), |
| 651 | ), | 651 | ), |
| 652 | ), | 652 | ), |
| 653 | ), | 653 | ), |
| ... | @@ -684,9 +684,9 @@ describe("providerMaintenanceRunner", () => { | ... | @@ -684,9 +684,9 @@ describe("providerMaintenanceRunner", () => { |
| 684 | }).pipe( | 684 | }).pipe( |
| 685 | Effect.provide( | 685 | Effect.provide( |
| 686 | Layer.mergeAll( | 686 | Layer.mergeAll( |
| 687 | NonWindowsPlatform, | 687 | layerNonWindowsPlatform, |
| 688 | latestVersionHttpClient("0.0.0"), | 688 | layerLatestVersionHttpClient("0.0.0"), |
| 689 | mockSpawnerLayer(() => { | 689 | layerMockSpawner(() => { |
| 690 | startedLatch.resolve(); | 690 | startedLatch.resolve(); |
| 691 | return { | 691 | return { |
| 692 | stdout: "updated", | 692 | stdout: "updated", |
| ... | @@ -761,9 +761,9 @@ describe("providerMaintenanceRunner", () => { | ... | @@ -761,9 +761,9 @@ describe("providerMaintenanceRunner", () => { |
| 761 | }).pipe( | 761 | }).pipe( |
| 762 | Effect.provide( | 762 | Effect.provide( |
| 763 | Layer.mergeAll( | 763 | Layer.mergeAll( |
| 764 | NonWindowsPlatform, | 764 | layerNonWindowsPlatform, |
| 765 | latestVersionHttpClient("2.0.0"), | 765 | layerLatestVersionHttpClient("2.0.0"), |
| 766 | mockSpawnerLayer((_command, args) => { | 766 | layerMockSpawner((_command, args) => { |
| 767 | calls.push(args.join(" ")); | 767 | calls.push(args.join(" ")); |
| 768 | if (calls.length === 1) { | 768 | if (calls.length === 1) { |
| 769 | firstStartedLatch.resolve(); | 769 | firstStartedLatch.resolve(); |
| ... | @@ -805,9 +805,9 @@ describe("providerMaintenanceRunner", () => { | ... | @@ -805,9 +805,9 @@ describe("providerMaintenanceRunner", () => { |
| 805 | }).pipe( | 805 | }).pipe( |
| 806 | Effect.provide( | 806 | Effect.provide( |
| 807 | Layer.mergeAll( | 807 | Layer.mergeAll( |
| 808 | NonWindowsPlatform, | 808 | layerNonWindowsPlatform, |
| 809 | latestVersionHttpClient("0.0.0"), | 809 | layerLatestVersionHttpClient("0.0.0"), |
| 810 | mockSpawnerLayer((_command, args) => { | 810 | layerMockSpawner((_command, args) => { |
| 811 | calls.push(args.join(" ")); | 811 | calls.push(args.join(" ")); |
| 812 | return { stdout: "updated" }; | 812 | return { stdout: "updated" }; |
| 813 | }), | 813 | }), |
| ... | @@ -860,9 +860,9 @@ describe("providerMaintenanceRunner", () => { | ... | @@ -860,9 +860,9 @@ describe("providerMaintenanceRunner", () => { |
| 860 | }).pipe( | 860 | }).pipe( |
| 861 | Effect.provide( | 861 | Effect.provide( |
| 862 | Layer.mergeAll( | 862 | Layer.mergeAll( |
| 863 | NonWindowsPlatform, | 863 | layerNonWindowsPlatform, |
| 864 | latestVersionHttpClient("0.0.0"), | 864 | layerLatestVersionHttpClient("0.0.0"), |
| 865 | mockSpawnerLayer(() => ({ stdout: "updated" })), | 865 | layerMockSpawner(() => ({ stdout: "updated" })), |
| 866 | ), | 866 | ), |
| 867 | ), | 867 | ), |
| 868 | ), | 868 | ), |
| ... | @@ -908,7 +908,7 @@ describe("providerMaintenanceRunner", () => { | ... | @@ -908,7 +908,7 @@ describe("providerMaintenanceRunner", () => { |
| 908 | Layer.succeed(SpawnExecutableResolution, (command) => | 908 | Layer.succeed(SpawnExecutableResolution, (command) => |
| 909 | command === "npm" ? "C:\\fake\\npm\\npm.cmd" : undefined, | 909 | command === "npm" ? "C:\\fake\\npm\\npm.cmd" : undefined, |
| 910 | ), | 910 | ), |
| 911 | latestVersionHttpClient("0.0.0"), | 911 | layerLatestVersionHttpClient("0.0.0"), |
| 912 | Layer.succeed( | 912 | Layer.succeed( |
| 913 | ChildProcessSpawner.ChildProcessSpawner, | 913 | ChildProcessSpawner.ChildProcessSpawner, |
| 914 | ChildProcessSpawner.make((command) => { | 914 | ChildProcessSpawner.make((command) => { |
| ... | @@ -1009,9 +1009,9 @@ it.effect("refuses incompatible latest versions and unapproved or unpinnable tar | ... | @@ -1009,9 +1009,9 @@ it.effect("refuses incompatible latest versions and unapproved or unpinnable tar |
| 1009 | }).pipe( | 1009 | }).pipe( |
| 1010 | Effect.provide( | 1010 | Effect.provide( |
| 1011 | Layer.mergeAll( | 1011 | Layer.mergeAll( |
| 1012 | NonWindowsPlatform, | 1012 | layerNonWindowsPlatform, |
| 1013 | latestVersionHttpClient("3.0.0"), | 1013 | layerLatestVersionHttpClient("3.0.0"), |
| 1014 | mockSpawnerLayer((_command, args) => { | 1014 | layerMockSpawner((_command, args) => { |
| 1015 | calls.push(args.join(" ")); | 1015 | calls.push(args.join(" ")); |
| 1016 | return { stdout: "installed" }; | 1016 | return { stdout: "installed" }; |
| 1017 | }), | 1017 | }), |
apps/server/src/provider/testUtils/providerRegistryMock.ts+1-1| ... | @@ -18,5 +18,5 @@ const makeProviderRegistryMock = ( | ... | @@ -18,5 +18,5 @@ const makeProviderRegistryMock = ( |
| 18 | streamChanges: Stream.empty, | 18 | streamChanges: Stream.empty, |
| 19 | }); | 19 | }); |
| 20 | 20 | ||
| 21 | export const makeProviderRegistryLayer = (providers: ReadonlyArray<ServerProvider> = []) => | 21 | export const layer = (providers: ReadonlyArray<ServerProvider> = []) => |
| 22 | Layer.succeed(ProviderRegistry.ProviderRegistry, makeProviderRegistryMock(providers)); | 22 | Layer.succeed(ProviderRegistry.ProviderRegistry, makeProviderRegistryMock(providers)); |
apps/server/src/pullRequest/PullRequestService.test.ts+2-2| ... | @@ -25,7 +25,7 @@ import * as ServerSettingsService from "../serverSettings.ts"; | ... | @@ -25,7 +25,7 @@ import * as ServerSettingsService from "../serverSettings.ts"; |
| 25 | import { PullRequestOperationError } from "@t3tools/contracts"; | 25 | import { PullRequestOperationError } from "@t3tools/contracts"; |
| 26 | 26 | ||
| 27 | import * as ProjectService from "../project/ProjectService.ts"; | 27 | import * as ProjectService from "../project/ProjectService.ts"; |
| 28 | import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; | 28 | import * as SqlitePersistence from "../persistence/Layers/Sqlite.ts"; |
| 29 | import * as PullRequestFilesViewed from "../persistence/PullRequestFilesViewed.ts"; | 29 | import * as PullRequestFilesViewed from "../persistence/PullRequestFilesViewed.ts"; |
| 30 | import * as RepositoryIdentityResolver from "../project/RepositoryIdentityResolver.ts"; | 30 | import * as RepositoryIdentityResolver from "../project/RepositoryIdentityResolver.ts"; |
| 31 | import * as SourceControlProviderRegistry from "../sourceControl/SourceControlProviderRegistry.ts"; | 31 | import * as SourceControlProviderRegistry from "../sourceControl/SourceControlProviderRegistry.ts"; |
| ... | @@ -505,7 +505,7 @@ function makeService(input: { | ... | @@ -505,7 +505,7 @@ function makeService(input: { |
| 505 | SourceControlRateLimit.layer, | 505 | SourceControlRateLimit.layer, |
| 506 | // The real store over a database of its own, so the environment-kept marks are exercised | 506 | // The real store over a database of its own, so the environment-kept marks are exercised |
| 507 | // through the SQL that holds them rather than through a stand-in that agrees with itself. | 507 | // through the SQL that holds them rather than through a stand-in that agrees with itself. |
| 508 | PullRequestFilesViewed.layer.pipe(Layer.provide(SqlitePersistenceMemory)), | 508 | PullRequestFilesViewed.layer.pipe(Layer.provide(SqlitePersistence.layerMemory)), |
| 509 | Layer.effect(PullRequestReadCache.PullRequestReadCache, PullRequestReadCache.make).pipe( | 509 | Layer.effect(PullRequestReadCache.PullRequestReadCache, PullRequestReadCache.make).pipe( |
| 510 | Layer.provide(KeyValueStore.layerMemory), | 510 | Layer.provide(KeyValueStore.layerMemory), |
| 511 | Layer.provide(NodeServices.layer), | 511 | Layer.provide(NodeServices.layer), |
apps/server/src/pullRequest/http.ts+1-1| ... | @@ -6,7 +6,7 @@ import { annotateEnvironmentRequest, requireEnvironmentScope } from "../auth/htt | ... | @@ -6,7 +6,7 @@ import { annotateEnvironmentRequest, requireEnvironmentScope } from "../auth/htt |
| 6 | import * as PullRequestService from "./PullRequestService.ts"; | 6 | import * as PullRequestService from "./PullRequestService.ts"; |
| 7 | 7 | ||
| 8 | /** The patch is often the largest PR payload and benefits from HTTP compression and flow control. */ | 8 | /** The patch is often the largest PR payload and benefits from HTTP compression and flow control. */ |
| 9 | export const pullRequestHttpApiLayer = HttpApiBuilder.group( | 9 | export const layer = HttpApiBuilder.group( |
| 10 | EnvironmentHttpApi, | 10 | EnvironmentHttpApi, |
| 11 | "pullRequests", | 11 | "pullRequests", |
| 12 | Effect.fnUntraced(function* (handlers) { | 12 | Effect.fnUntraced(function* (handlers) { |
apps/server/src/pullRequest/linkedThreads.test.ts+2-2| ... | @@ -3,7 +3,7 @@ import * as Effect from "effect/Effect"; | ... | @@ -3,7 +3,7 @@ import * as Effect from "effect/Effect"; |
| 3 | import * as Schema from "effect/Schema"; | 3 | import * as Schema from "effect/Schema"; |
| 4 | import * as SqlClient from "effect/sql/SqlClient"; | 4 | import * as SqlClient from "effect/sql/SqlClient"; |
| 5 | 5 | ||
| 6 | import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; | 6 | import * as SqlitePersistence from "../persistence/Layers/Sqlite.ts"; |
| 7 | import { listLinkedPullRequestThreads } from "./linkedThreads.ts"; | 7 | import { listLinkedPullRequestThreads } from "./linkedThreads.ts"; |
| 8 | 8 | ||
| 9 | const encodePayload = Schema.encodeEffect(Schema.fromJsonString(Schema.Unknown)); | 9 | const encodePayload = Schema.encodeEffect(Schema.fromJsonString(Schema.Unknown)); |
| ... | @@ -180,5 +180,5 @@ it.effect( | ... | @@ -180,5 +180,5 @@ it.effect( |
| 180 | }), | 180 | }), |
| 181 | { threads: [] }, | 181 | { threads: [] }, |
| 182 | ); | 182 | ); |
| 183 | }).pipe(Effect.provide(SqlitePersistenceMemory)), | 183 | }).pipe(Effect.provide(SqlitePersistence.layerMemory)), |
| 184 | ); | 184 | ); |
apps/server/src/resourceTelemetry/ResourceTelemetry.test.ts+35-35| ... | @@ -152,7 +152,7 @@ describe("ResourceTelemetry", () => { | ... | @@ -152,7 +152,7 @@ describe("ResourceTelemetry", () => { |
| 152 | childWriteBytes: 1_000, | 152 | childWriteBytes: 1_000, |
| 153 | }); | 153 | }); |
| 154 | const demandChanges = yield* Ref.make<ReadonlyArray<boolean>>([]); | 154 | const demandChanges = yield* Ref.make<ReadonlyArray<boolean>>([]); |
| 155 | const nativeLayer = NativeTelemetryClient.layerTest({ | 155 | const layerNative = NativeTelemetryClient.layerTest({ |
| 156 | sampleNow: Effect.succeed(nativeGeneration(sample, 0)), | 156 | sampleNow: Effect.succeed(nativeGeneration(sample, 0)), |
| 157 | health: Effect.succeed({ | 157 | health: Effect.succeed({ |
| 158 | status: "healthy", | 158 | status: "healthy", |
| ... | @@ -163,13 +163,13 @@ describe("ResourceTelemetry", () => { | ... | @@ -163,13 +163,13 @@ describe("ResourceTelemetry", () => { |
| 163 | sampleIntervalMs: 1_000, | 163 | sampleIntervalMs: 1_000, |
| 164 | }), | 164 | }), |
| 165 | }); | 165 | }); |
| 166 | const desktopLayer = DesktopTelemetryReceiver.layerTest({ | 166 | const layerDesktop = DesktopTelemetryReceiver.layerTest({ |
| 167 | latest: Effect.succeedSome(desktopSnapshot(sampledAtUnixMs)), | 167 | latest: Effect.succeedSome(desktopSnapshot(sampledAtUnixMs)), |
| 168 | setDiagnosticsDemand: (enabled) => | 168 | setDiagnosticsDemand: (enabled) => |
| 169 | Ref.update(demandChanges, (changes) => [...changes, enabled]), | 169 | Ref.update(demandChanges, (changes) => [...changes, enabled]), |
| 170 | }); | 170 | }); |
| 171 | const telemetryLayer = ResourceTelemetry.layer.pipe( | 171 | const layerTelemetry = ResourceTelemetry.layer.pipe( |
| 172 | Layer.provide(Layer.mergeAll(nativeLayer, desktopLayer, ResourceAttribution.layer)), | 172 | Layer.provide(Layer.mergeAll(layerNative, layerDesktop, ResourceAttribution.layer)), |
| 173 | ); | 173 | ); |
| 174 | 174 | ||
| 175 | const live = yield* Stream.runHead( | 175 | const live = yield* Stream.runHead( |
| ... | @@ -177,7 +177,7 @@ describe("ResourceTelemetry", () => { | ... | @@ -177,7 +177,7 @@ describe("ResourceTelemetry", () => { |
| 177 | const telemetry = yield* ResourceTelemetry.ResourceTelemetry; | 177 | const telemetry = yield* ResourceTelemetry.ResourceTelemetry; |
| 178 | return telemetry.changes; | 178 | return telemetry.changes; |
| 179 | }).pipe(Stream.unwrap), | 179 | }).pipe(Stream.unwrap), |
| 180 | ).pipe(Effect.provide(telemetryLayer)); | 180 | ).pipe(Effect.provide(layerTelemetry)); |
| 181 | 181 | ||
| 182 | expect(Option.isSome(live)).toBe(true); | 182 | expect(Option.isSome(live)).toBe(true); |
| 183 | expect(yield* Ref.get(demandChanges)).toEqual([true, false]); | 183 | expect(yield* Ref.get(demandChanges)).toEqual([true, false]); |
| ... | @@ -188,7 +188,7 @@ describe("ResourceTelemetry", () => { | ... | @@ -188,7 +188,7 @@ describe("ResourceTelemetry", () => { |
| 188 | Effect.gen(function* () { | 188 | Effect.gen(function* () { |
| 189 | const sampledAtUnixMs = DateTime.toEpochMillis(yield* DateTime.now); | 189 | const sampledAtUnixMs = DateTime.toEpochMillis(yield* DateTime.now); |
| 190 | const demandChanges = yield* Ref.make<ReadonlyArray<boolean>>([]); | 190 | const demandChanges = yield* Ref.make<ReadonlyArray<boolean>>([]); |
| 191 | const nativeLayer = NativeTelemetryClient.layerTest({ | 191 | const layerNative = NativeTelemetryClient.layerTest({ |
| 192 | sampleNow: Effect.never, | 192 | sampleNow: Effect.never, |
| 193 | health: Effect.succeed({ | 193 | health: Effect.succeed({ |
| 194 | status: "healthy", | 194 | status: "healthy", |
| ... | @@ -199,13 +199,13 @@ describe("ResourceTelemetry", () => { | ... | @@ -199,13 +199,13 @@ describe("ResourceTelemetry", () => { |
| 199 | sampleIntervalMs: 1_000, | 199 | sampleIntervalMs: 1_000, |
| 200 | }), | 200 | }), |
| 201 | }); | 201 | }); |
| 202 | const desktopLayer = DesktopTelemetryReceiver.layerTest({ | 202 | const layerDesktop = DesktopTelemetryReceiver.layerTest({ |
| 203 | latest: Effect.succeedSome(desktopSnapshot(sampledAtUnixMs)), | 203 | latest: Effect.succeedSome(desktopSnapshot(sampledAtUnixMs)), |
| 204 | setDiagnosticsDemand: (enabled) => | 204 | setDiagnosticsDemand: (enabled) => |
| 205 | Ref.update(demandChanges, (changes) => [...changes, enabled]), | 205 | Ref.update(demandChanges, (changes) => [...changes, enabled]), |
| 206 | }); | 206 | }); |
| 207 | const telemetryLayer = ResourceTelemetry.layer.pipe( | 207 | const layerTelemetry = ResourceTelemetry.layer.pipe( |
| 208 | Layer.provide(Layer.mergeAll(nativeLayer, desktopLayer, ResourceAttribution.layer)), | 208 | Layer.provide(Layer.mergeAll(layerNative, layerDesktop, ResourceAttribution.layer)), |
| 209 | ); | 209 | ); |
| 210 | 210 | ||
| 211 | const resultFiber = yield* Stream.runHead( | 211 | const resultFiber = yield* Stream.runHead( |
| ... | @@ -213,7 +213,7 @@ describe("ResourceTelemetry", () => { | ... | @@ -213,7 +213,7 @@ describe("ResourceTelemetry", () => { |
| 213 | const telemetry = yield* ResourceTelemetry.ResourceTelemetry; | 213 | const telemetry = yield* ResourceTelemetry.ResourceTelemetry; |
| 214 | return telemetry.changes; | 214 | return telemetry.changes; |
| 215 | }).pipe(Stream.unwrap), | 215 | }).pipe(Stream.unwrap), |
| 216 | ).pipe(Effect.timeoutOption("10 millis"), Effect.provide(telemetryLayer), Effect.forkChild); | 216 | ).pipe(Effect.timeoutOption("10 millis"), Effect.provide(layerTelemetry), Effect.forkChild); |
| 217 | yield* Effect.yieldNow; | 217 | yield* Effect.yieldNow; |
| 218 | yield* TestClock.adjust("10 millis"); | 218 | yield* TestClock.adjust("10 millis"); |
| 219 | const result = yield* Fiber.join(resultFiber); | 219 | const result = yield* Fiber.join(resultFiber); |
| ... | @@ -239,7 +239,7 @@ describe("ResourceTelemetry", () => { | ... | @@ -239,7 +239,7 @@ describe("ResourceTelemetry", () => { |
| 239 | ...desktopSnapshot(sampledAtUnixMs), | 239 | ...desktopSnapshot(sampledAtUnixMs), |
| 240 | electronProcesses: [], | 240 | electronProcesses: [], |
| 241 | }; | 241 | }; |
| 242 | const nativeLayer = NativeTelemetryClient.layerTest({ | 242 | const layerNative = NativeTelemetryClient.layerTest({ |
| 243 | sampleNow: Effect.succeed(nativeGeneration(sample, 0)), | 243 | sampleNow: Effect.succeed(nativeGeneration(sample, 0)), |
| 244 | health: Effect.succeed({ | 244 | health: Effect.succeed({ |
| 245 | status: "healthy", | 245 | status: "healthy", |
| ... | @@ -250,17 +250,17 @@ describe("ResourceTelemetry", () => { | ... | @@ -250,17 +250,17 @@ describe("ResourceTelemetry", () => { |
| 250 | sampleIntervalMs: 1_000, | 250 | sampleIntervalMs: 1_000, |
| 251 | }), | 251 | }), |
| 252 | }); | 252 | }); |
| 253 | const desktopLayer = DesktopTelemetryReceiver.layerTest({ | 253 | const layerDesktop = DesktopTelemetryReceiver.layerTest({ |
| 254 | latest: Effect.succeedSome(desktop), | 254 | latest: Effect.succeedSome(desktop), |
| 255 | }); | 255 | }); |
| 256 | const telemetryLayer = ResourceTelemetry.layer.pipe( | 256 | const layerTelemetry = ResourceTelemetry.layer.pipe( |
| 257 | Layer.provide(Layer.mergeAll(nativeLayer, desktopLayer, ResourceAttribution.layer)), | 257 | Layer.provide(Layer.mergeAll(layerNative, layerDesktop, ResourceAttribution.layer)), |
| 258 | ); | 258 | ); |
| 259 | 259 | ||
| 260 | const snapshot = yield* Effect.gen(function* () { | 260 | const snapshot = yield* Effect.gen(function* () { |
| 261 | const telemetry = yield* ResourceTelemetry.ResourceTelemetry; | 261 | const telemetry = yield* ResourceTelemetry.ResourceTelemetry; |
| 262 | return yield* telemetry.refresh; | 262 | return yield* telemetry.refresh; |
| 263 | }).pipe(Effect.provide(telemetryLayer)); | 263 | }).pipe(Effect.provide(layerTelemetry)); |
| 264 | 264 | ||
| 265 | expect(snapshot.processes.find((entry) => entry.identity.pid === 5_000)?.category).toBe( | 265 | expect(snapshot.processes.find((entry) => entry.identity.pid === 5_000)?.category).toBe( |
| 266 | "electron-main", | 266 | "electron-main", |
| ... | @@ -288,7 +288,7 @@ describe("ResourceTelemetry", () => { | ... | @@ -288,7 +288,7 @@ describe("ResourceTelemetry", () => { |
| 288 | }); | 288 | }); |
| 289 | const nativeSnapshots = | 289 | const nativeSnapshots = |
| 290 | yield* PubSub.unbounded<NativeTelemetryClient.NativeTelemetrySnapshot>(); | 290 | yield* PubSub.unbounded<NativeTelemetryClient.NativeTelemetrySnapshot>(); |
| 291 | const nativeLayer = NativeTelemetryClient.layerTest({ | 291 | const layerNative = NativeTelemetryClient.layerTest({ |
| 292 | snapshots: Stream.fromPubSub(nativeSnapshots), | 292 | snapshots: Stream.fromPubSub(nativeSnapshots), |
| 293 | sampleNow: Effect.never, | 293 | sampleNow: Effect.never, |
| 294 | health: Effect.succeed({ | 294 | health: Effect.succeed({ |
| ... | @@ -300,10 +300,10 @@ describe("ResourceTelemetry", () => { | ... | @@ -300,10 +300,10 @@ describe("ResourceTelemetry", () => { |
| 300 | sampleIntervalMs: 1_000, | 300 | sampleIntervalMs: 1_000, |
| 301 | }), | 301 | }), |
| 302 | }); | 302 | }); |
| 303 | const telemetryLayer = ResourceTelemetry.layer.pipe( | 303 | const layerTelemetry = ResourceTelemetry.layer.pipe( |
| 304 | Layer.provide( | 304 | Layer.provide( |
| 305 | Layer.mergeAll( | 305 | Layer.mergeAll( |
| 306 | nativeLayer, | 306 | layerNative, |
| 307 | DesktopTelemetryReceiver.layerTest(), | 307 | DesktopTelemetryReceiver.layerTest(), |
| 308 | ResourceAttribution.layer, | 308 | ResourceAttribution.layer, |
| 309 | ), | 309 | ), |
| ... | @@ -326,7 +326,7 @@ describe("ResourceTelemetry", () => { | ... | @@ -326,7 +326,7 @@ describe("ResourceTelemetry", () => { |
| 326 | expect(DateTime.toEpochMillis(Option.getOrThrow(received).readAt)).toBe( | 326 | expect(DateTime.toEpochMillis(Option.getOrThrow(received).readAt)).toBe( |
| 327 | current.sampledAtUnixMs, | 327 | current.sampledAtUnixMs, |
| 328 | ); | 328 | ); |
| 329 | }).pipe(Effect.provide(telemetryLayer)); | 329 | }).pipe(Effect.provide(layerTelemetry)); |
| 330 | }), | 330 | }), |
| 331 | ), | 331 | ), |
| 332 | ); | 332 | ); |
| ... | @@ -343,7 +343,7 @@ describe("ResourceTelemetry", () => { | ... | @@ -343,7 +343,7 @@ describe("ResourceTelemetry", () => { |
| 343 | yield* PubSub.sliding<DesktopTelemetryReceiver.DesktopTelemetryReceiverHealth>(4); | 343 | yield* PubSub.sliding<DesktopTelemetryReceiver.DesktopTelemetryReceiverHealth>(4); |
| 344 | const healthSubscribed = yield* Deferred.make<void>(); | 344 | const healthSubscribed = yield* Deferred.make<void>(); |
| 345 | const finishDesktopSnapshot = yield* Deferred.make<void>(); | 345 | const finishDesktopSnapshot = yield* Deferred.make<void>(); |
| 346 | const desktopLayer = DesktopTelemetryReceiver.layerTest({ | 346 | const layerDesktop = DesktopTelemetryReceiver.layerTest({ |
| 347 | health: Ref.get(health), | 347 | health: Ref.get(health), |
| 348 | subscribeHealth: Effect.gen(function* () { | 348 | subscribeHealth: Effect.gen(function* () { |
| 349 | const subscription = yield* PubSub.subscribe(healthChanges); | 349 | const subscription = yield* PubSub.subscribe(healthChanges); |
| ... | @@ -361,11 +361,11 @@ describe("ResourceTelemetry", () => { | ... | @@ -361,11 +361,11 @@ describe("ResourceTelemetry", () => { |
| 361 | }), | 361 | }), |
| 362 | ), | 362 | ), |
| 363 | }); | 363 | }); |
| 364 | const telemetryLayer = ResourceTelemetry.layer.pipe( | 364 | const layerTelemetry = ResourceTelemetry.layer.pipe( |
| 365 | Layer.provide( | 365 | Layer.provide( |
| 366 | Layer.mergeAll( | 366 | Layer.mergeAll( |
| 367 | NativeTelemetryClient.layerTest(), | 367 | NativeTelemetryClient.layerTest(), |
| 368 | desktopLayer, | 368 | layerDesktop, |
| 369 | ResourceAttribution.layer, | 369 | ResourceAttribution.layer, |
| 370 | ), | 370 | ), |
| 371 | ), | 371 | ), |
| ... | @@ -377,7 +377,7 @@ describe("ResourceTelemetry", () => { | ... | @@ -377,7 +377,7 @@ describe("ResourceTelemetry", () => { |
| 377 | if (snapshot.health.desktop.status === "degraded") return snapshot; | 377 | if (snapshot.health.desktop.status === "degraded") return snapshot; |
| 378 | yield* Effect.yieldNow; | 378 | yield* Effect.yieldNow; |
| 379 | } | 379 | } |
| 380 | }).pipe(Effect.provide(telemetryLayer), Effect.timeout("1 second"), Effect.forkChild); | 380 | }).pipe(Effect.provide(layerTelemetry), Effect.timeout("1 second"), Effect.forkChild); |
| 381 | 381 | ||
| 382 | yield* Deferred.await(healthSubscribed); | 382 | yield* Deferred.await(healthSubscribed); |
| 383 | const degraded: DesktopTelemetryReceiver.DesktopTelemetryReceiverHealth = { | 383 | const degraded: DesktopTelemetryReceiver.DesktopTelemetryReceiverHealth = { |
| ... | @@ -407,7 +407,7 @@ describe("ResourceTelemetry", () => { | ... | @@ -407,7 +407,7 @@ describe("ResourceTelemetry", () => { |
| 407 | const rebuildStarted = yield* Deferred.make<void>(); | 407 | const rebuildStarted = yield* Deferred.make<void>(); |
| 408 | const finishRebuild = yield* Deferred.make<void>(); | 408 | const finishRebuild = yield* Deferred.make<void>(); |
| 409 | const attributionReads = yield* Ref.make(0); | 409 | const attributionReads = yield* Ref.make(0); |
| 410 | const attributionLayer = Layer.succeed( | 410 | const layerAttribution = Layer.succeed( |
| 411 | ResourceAttribution.ResourceAttribution, | 411 | ResourceAttribution.ResourceAttribution, |
| 412 | ResourceAttribution.ResourceAttribution.of({ | 412 | ResourceAttribution.ResourceAttribution.of({ |
| 413 | record: () => Effect.void, | 413 | record: () => Effect.void, |
| ... | @@ -428,7 +428,7 @@ describe("ResourceTelemetry", () => { | ... | @@ -428,7 +428,7 @@ describe("ResourceTelemetry", () => { |
| 428 | ), | 428 | ), |
| 429 | }), | 429 | }), |
| 430 | ); | 430 | ); |
| 431 | const nativeLayer = NativeTelemetryClient.layerTest({ | 431 | const layerNative = NativeTelemetryClient.layerTest({ |
| 432 | sampleNow: Effect.never, | 432 | sampleNow: Effect.never, |
| 433 | health: Effect.succeed({ | 433 | health: Effect.succeed({ |
| 434 | status: "healthy", | 434 | status: "healthy", |
| ... | @@ -439,11 +439,11 @@ describe("ResourceTelemetry", () => { | ... | @@ -439,11 +439,11 @@ describe("ResourceTelemetry", () => { |
| 439 | sampleIntervalMs: 1_000, | 439 | sampleIntervalMs: 1_000, |
| 440 | }), | 440 | }), |
| 441 | }); | 441 | }); |
| 442 | const desktopLayer = DesktopTelemetryReceiver.layerTest({ | 442 | const layerDesktop = DesktopTelemetryReceiver.layerTest({ |
| 443 | changes: Stream.fromPubSub(desktopChanges), | 443 | changes: Stream.fromPubSub(desktopChanges), |
| 444 | }); | 444 | }); |
| 445 | const telemetryLayer = ResourceTelemetry.layer.pipe( | 445 | const layerTelemetry = ResourceTelemetry.layer.pipe( |
| 446 | Layer.provide(Layer.mergeAll(nativeLayer, desktopLayer, attributionLayer)), | 446 | Layer.provide(Layer.mergeAll(layerNative, layerDesktop, layerAttribution)), |
| 447 | ); | 447 | ); |
| 448 | 448 | ||
| 449 | yield* Effect.gen(function* () { | 449 | yield* Effect.gen(function* () { |
| ... | @@ -461,7 +461,7 @@ describe("ResourceTelemetry", () => { | ... | @@ -461,7 +461,7 @@ describe("ResourceTelemetry", () => { |
| 461 | nextDesktopSnapshot.sampledAtUnixMs, | 461 | nextDesktopSnapshot.sampledAtUnixMs, |
| 462 | ); | 462 | ); |
| 463 | expect(subscription.latest.power).toEqual(nextDesktopSnapshot.power); | 463 | expect(subscription.latest.power).toEqual(nextDesktopSnapshot.power); |
| 464 | }).pipe(Effect.provide(telemetryLayer)); | 464 | }).pipe(Effect.provide(layerTelemetry)); |
| 465 | }), | 465 | }), |
| 466 | ), | 466 | ), |
| 467 | ); | 467 | ); |
| ... | @@ -520,7 +520,7 @@ describe("ResourceTelemetry", () => { | ... | @@ -520,7 +520,7 @@ describe("ResourceTelemetry", () => { |
| 520 | }); | 520 | }); |
| 521 | const nativeHealthChanges = | 521 | const nativeHealthChanges = |
| 522 | yield* PubSub.sliding<NativeTelemetryClient.NativeTelemetryClientHealth>(4); | 522 | yield* PubSub.sliding<NativeTelemetryClient.NativeTelemetryClientHealth>(4); |
| 523 | const nativeLayer = NativeTelemetryClient.layerTest({ | 523 | const layerNative = NativeTelemetryClient.layerTest({ |
| 524 | setExternalProcesses: (processes) => Ref.set(externalProcesses, processes), | 524 | setExternalProcesses: (processes) => Ref.set(externalProcesses, processes), |
| 525 | readHistory: () => Effect.succeed(samples.slice(0, 2)), | 525 | readHistory: () => Effect.succeed(samples.slice(0, 2)), |
| 526 | sampleNow: Ref.modify(sampleIndex, (index) => { | 526 | sampleNow: Ref.modify(sampleIndex, (index) => { |
| ... | @@ -538,7 +538,7 @@ describe("ResourceTelemetry", () => { | ... | @@ -538,7 +538,7 @@ describe("ResourceTelemetry", () => { |
| 538 | }; | 538 | }; |
| 539 | }), | 539 | }), |
| 540 | }); | 540 | }); |
| 541 | const desktopLayer = DesktopTelemetryReceiver.layerTest({ | 541 | const layerDesktop = DesktopTelemetryReceiver.layerTest({ |
| 542 | latest: Effect.succeedSome(desktopSnapshot(startedAt)), | 542 | latest: Effect.succeedSome(desktopSnapshot(startedAt)), |
| 543 | health: Effect.succeed({ | 543 | health: Effect.succeed({ |
| 544 | status: "healthy", | 544 | status: "healthy", |
| ... | @@ -546,10 +546,10 @@ describe("ResourceTelemetry", () => { | ... | @@ -546,10 +546,10 @@ describe("ResourceTelemetry", () => { |
| 546 | lastError: Option.none(), | 546 | lastError: Option.none(), |
| 547 | }), | 547 | }), |
| 548 | }); | 548 | }); |
| 549 | const attributionLayer = ResourceAttribution.layer; | 549 | const layerAttribution = ResourceAttribution.layer; |
| 550 | const dependencies = Layer.mergeAll(nativeLayer, desktopLayer, attributionLayer); | 550 | const layerDependencies = Layer.mergeAll(layerNative, layerDesktop, layerAttribution); |
| 551 | const telemetryLayer = ResourceTelemetry.layer.pipe(Layer.provide(dependencies)); | 551 | const layerTelemetry = ResourceTelemetry.layer.pipe(Layer.provide(layerDependencies)); |
| 552 | const layer = Layer.mergeAll(dependencies, telemetryLayer); | 552 | const layer = Layer.mergeAll(layerDependencies, layerTelemetry); |
| 553 | 553 | ||
| 554 | yield* Effect.gen(function* () { | 554 | yield* Effect.gen(function* () { |
| 555 | const telemetry = yield* ResourceTelemetry.ResourceTelemetry; | 555 | const telemetry = yield* ResourceTelemetry.ResourceTelemetry; |
apps/server/src/review/ReviewService.test.ts+7-7| ... | @@ -11,7 +11,7 @@ import * as GitVcsDriver from "../vcs/GitVcsDriver.ts"; | ... | @@ -11,7 +11,7 @@ import * as GitVcsDriver from "../vcs/GitVcsDriver.ts"; |
| 11 | import * as VcsDriverRegistry from "../vcs/VcsDriverRegistry.ts"; | 11 | import * as VcsDriverRegistry from "../vcs/VcsDriverRegistry.ts"; |
| 12 | import * as ReviewService from "./ReviewService.ts"; | 12 | import * as ReviewService from "./ReviewService.ts"; |
| 13 | 13 | ||
| 14 | function makeLayer(input: { | 14 | function layer(input: { |
| 15 | readonly workspaceRoot: string; | 15 | readonly workspaceRoot: string; |
| 16 | readonly baseDir: string; | 16 | readonly baseDir: string; |
| 17 | readonly detectCalls?: Array<{ readonly cwd: string }>; | 17 | readonly detectCalls?: Array<{ readonly cwd: string }>; |
| ... | @@ -54,7 +54,7 @@ describe("ReviewService", () => { | ... | @@ -54,7 +54,7 @@ describe("ReviewService", () => { |
| 54 | const error = yield* Effect.gen(function* () { | 54 | const error = yield* Effect.gen(function* () { |
| 55 | const review = yield* ReviewService.ReviewService; | 55 | const review = yield* ReviewService.ReviewService; |
| 56 | return yield* review.getDiffPreview({ cwd: outsideRoot }).pipe(Effect.flip); | 56 | return yield* review.getDiffPreview({ cwd: outsideRoot }).pipe(Effect.flip); |
| 57 | }).pipe(Effect.provide(makeLayer({ workspaceRoot, baseDir, detectCalls }))); | 57 | }).pipe(Effect.provide(layer({ workspaceRoot, baseDir, detectCalls }))); |
| 58 | 58 | ||
| 59 | assert.strictEqual(error._tag, "VcsRepositoryDetectionError"); | 59 | assert.strictEqual(error._tag, "VcsRepositoryDetectionError"); |
| 60 | assert.strictEqual(error.operation, "ReviewService.getDiffPreview"); | 60 | assert.strictEqual(error.operation, "ReviewService.getDiffPreview"); |
| ... | @@ -87,7 +87,7 @@ describe("ReviewService", () => { | ... | @@ -87,7 +87,7 @@ describe("ReviewService", () => { |
| 87 | newPath: "file.ts", | 87 | newPath: "file.ts", |
| 88 | }) | 88 | }) |
| 89 | .pipe(Effect.flip); | 89 | .pipe(Effect.flip); |
| 90 | }).pipe(Effect.provide(makeLayer({ workspaceRoot, baseDir, detectCalls }))); | 90 | }).pipe(Effect.provide(layer({ workspaceRoot, baseDir, detectCalls }))); |
| 91 | 91 | ||
| 92 | assert.strictEqual(error._tag, "VcsRepositoryDetectionError"); | 92 | assert.strictEqual(error._tag, "VcsRepositoryDetectionError"); |
| 93 | assert.strictEqual(error.operation, "ReviewService.getDiffFileContents"); | 93 | assert.strictEqual(error.operation, "ReviewService.getDiffFileContents"); |
| ... | @@ -112,7 +112,7 @@ describe("ReviewService", () => { | ... | @@ -112,7 +112,7 @@ describe("ReviewService", () => { |
| 112 | return yield* review.getDiffPreview({ cwd: previous }); | 112 | return yield* review.getDiffPreview({ cwd: previous }); |
| 113 | }).pipe( | 113 | }).pipe( |
| 114 | Effect.provide( | 114 | Effect.provide( |
| 115 | makeLayer({ | 115 | layer({ |
| 116 | workspaceRoot, | 116 | workspaceRoot, |
| 117 | baseDir, | 117 | baseDir, |
| 118 | worktreesDirectory: "/", | 118 | worktreesDirectory: "/", |
| ... | @@ -128,7 +128,7 @@ describe("ReviewService", () => { | ... | @@ -128,7 +128,7 @@ describe("ReviewService", () => { |
| 128 | const error = yield* Effect.gen(function* () { | 128 | const error = yield* Effect.gen(function* () { |
| 129 | const review = yield* ReviewService.ReviewService; | 129 | const review = yield* ReviewService.ReviewService; |
| 130 | return yield* review.getDiffPreview({ cwd: outsideRoot }).pipe(Effect.flip); | 130 | return yield* review.getDiffPreview({ cwd: outsideRoot }).pipe(Effect.flip); |
| 131 | }).pipe(Effect.provide(makeLayer({ workspaceRoot, baseDir, worktreesDirectory }))); | 131 | }).pipe(Effect.provide(layer({ workspaceRoot, baseDir, worktreesDirectory }))); |
| 132 | assert.strictEqual(error._tag, "VcsRepositoryDetectionError"); | 132 | assert.strictEqual(error._tag, "VcsRepositoryDetectionError"); |
| 133 | } | 133 | } |
| 134 | }).pipe(Effect.provide(NodeServices.layer)), | 134 | }).pipe(Effect.provide(NodeServices.layer)), |
| ... | @@ -144,7 +144,7 @@ describe("ReviewService", () => { | ... | @@ -144,7 +144,7 @@ describe("ReviewService", () => { |
| 144 | const result = yield* Effect.gen(function* () { | 144 | const result = yield* Effect.gen(function* () { |
| 145 | const review = yield* ReviewService.ReviewService; | 145 | const review = yield* ReviewService.ReviewService; |
| 146 | return yield* review.getDiffPreview({ cwd: workspaceRoot }); | 146 | return yield* review.getDiffPreview({ cwd: workspaceRoot }); |
| 147 | }).pipe(Effect.provide(makeLayer({ workspaceRoot, baseDir, detectCalls }))); | 147 | }).pipe(Effect.provide(layer({ workspaceRoot, baseDir, detectCalls }))); |
| 148 | 148 | ||
| 149 | assert.strictEqual(result.cwd, workspaceRoot); | 149 | assert.strictEqual(result.cwd, workspaceRoot); |
| 150 | assert.deepStrictEqual(result.sources, []); | 150 | assert.deepStrictEqual(result.sources, []); |
| ... | @@ -163,7 +163,7 @@ describe("ReviewService", () => { | ... | @@ -163,7 +163,7 @@ describe("ReviewService", () => { |
| 163 | const error = yield* Effect.gen(function* () { | 163 | const error = yield* Effect.gen(function* () { |
| 164 | const review = yield* ReviewService.ReviewService; | 164 | const review = yield* ReviewService.ReviewService; |
| 165 | return yield* review.getDiffPreview({ cwd: invalidCwd }).pipe(Effect.flip); | 165 | return yield* review.getDiffPreview({ cwd: invalidCwd }).pipe(Effect.flip); |
| 166 | }).pipe(Effect.provide(makeLayer({ workspaceRoot, baseDir, detectCalls }))); | 166 | }).pipe(Effect.provide(layer({ workspaceRoot, baseDir, detectCalls }))); |
| 167 | 167 | ||
| 168 | assert.strictEqual(error._tag, "VcsRepositoryDetectionError"); | 168 | assert.strictEqual(error._tag, "VcsRepositoryDetectionError"); |
| 169 | if (error._tag !== "VcsRepositoryDetectionError") return; | 169 | if (error._tag !== "VcsRepositoryDetectionError") return; |
apps/server/src/scheduledTasks/ScheduledTaskService.schedule.test.ts+7-7| ... | @@ -11,14 +11,14 @@ import * as TestClock from "effect/testing/TestClock"; | ... | @@ -11,14 +11,14 @@ import * as TestClock from "effect/testing/TestClock"; |
| 11 | import * as ThreadLaunchService from "../orchestration-v2/ThreadLaunchService.ts"; | 11 | import * as ThreadLaunchService from "../orchestration-v2/ThreadLaunchService.ts"; |
| 12 | import * as ThreadManagementService from "../orchestration-v2/ThreadManagementService.ts"; | 12 | import * as ThreadManagementService from "../orchestration-v2/ThreadManagementService.ts"; |
| 13 | import * as SecretRequests from "../secrets/SecretRequests.ts"; | 13 | import * as SecretRequests from "../secrets/SecretRequests.ts"; |
| 14 | import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; | 14 | import * as SqlitePersistence from "../persistence/Layers/Sqlite.ts"; |
| 15 | import * as ScheduledTaskService from "./ScheduledTaskService.ts"; | 15 | import * as ScheduledTaskService from "./ScheduledTaskService.ts"; |
| 16 | 16 | ||
| 17 | const decodeUpsertInput = Schema.decodeUnknownEffect(ScheduledTaskUpsertInput); | 17 | const decodeUpsertInput = Schema.decodeUnknownEffect(ScheduledTaskUpsertInput); |
| 18 | 18 | ||
| 19 | it.effect("rejects a stale form save after deletion while preserving explicit-id creates", () => | 19 | it.effect("rejects a stale form save after deletion while preserving explicit-id creates", () => |
| 20 | Effect.gen(function* () { | 20 | Effect.gen(function* () { |
| 21 | const dependencies = Layer.mergeAll( | 21 | const layerDependencies = Layer.mergeAll( |
| 22 | NodeCrypto.layer, | 22 | NodeCrypto.layer, |
| 23 | Scheduler.layer, | 23 | Scheduler.layer, |
| 24 | Layer.mock(ThreadLaunchService.ThreadLaunchService)({}), | 24 | Layer.mock(ThreadLaunchService.ThreadLaunchService)({}), |
| ... | @@ -49,8 +49,8 @@ it.effect("rejects a stale form save after deletion while preserving explicit-id | ... | @@ -49,8 +49,8 @@ it.effect("rejects a stale form save after deletion while preserving explicit-id |
| 49 | expect((yield* service.list()).tasks).toEqual([]); | 49 | expect((yield* service.list()).tasks).toEqual([]); |
| 50 | 50 | ||
| 51 | expect((yield* service.upsert(input)).task.id).toBe(created.task.id); | 51 | expect((yield* service.upsert(input)).task.id).toBe(created.task.id); |
| 52 | }).pipe(Effect.provide(ScheduledTaskService.layer.pipe(Layer.provide(dependencies)))); | 52 | }).pipe(Effect.provide(ScheduledTaskService.layer.pipe(Layer.provide(layerDependencies)))); |
| 53 | }).pipe(Effect.provide(SqlitePersistenceMemory)), | 53 | }).pipe(Effect.provide(SqlitePersistence.layerMemory)), |
| 54 | ); | 54 | ); |
| 55 | 55 | ||
| 56 | it.effect("preserves a due run when a save only pads the scheduled hour", () => | 56 | it.effect("preserves a due run when a save only pads the scheduled hour", () => |
| ... | @@ -61,7 +61,7 @@ it.effect("preserves a due run when a save only pads the scheduled hour", () => | ... | @@ -61,7 +61,7 @@ it.effect("preserves a due run when a save only pads the scheduled hour", () => |
| 61 | ); | 61 | ); |
| 62 | yield* TestClock.setTime(DateTime.toEpochMillis(dueAt) - 1_000); | 62 | yield* TestClock.setTime(DateTime.toEpochMillis(dueAt) - 1_000); |
| 63 | 63 | ||
| 64 | const dependencies = Layer.mergeAll( | 64 | const layerDependencies = Layer.mergeAll( |
| 65 | NodeCrypto.layer, | 65 | NodeCrypto.layer, |
| 66 | Scheduler.layer, | 66 | Scheduler.layer, |
| 67 | Layer.mock(ThreadLaunchService.ThreadLaunchService)({}), | 67 | Layer.mock(ThreadLaunchService.ThreadLaunchService)({}), |
| ... | @@ -105,6 +105,6 @@ it.effect("preserves a due run when a save only pads the scheduled hour", () => | ... | @@ -105,6 +105,6 @@ it.effect("preserves a due run when a save only pads the scheduled hour", () => |
| 105 | expect(rescheduled.task.nextRunAt).toBe( | 105 | expect(rescheduled.task.nextRunAt).toBe( |
| 106 | DateTime.formatIso(DateTime.toUtc(DateTime.add(dueAt, { minutes: 30 }))), | 106 | DateTime.formatIso(DateTime.toUtc(DateTime.add(dueAt, { minutes: 30 }))), |
| 107 | ); | 107 | ); |
| 108 | }).pipe(Effect.provide(ScheduledTaskService.layer.pipe(Layer.provide(dependencies)))); | 108 | }).pipe(Effect.provide(ScheduledTaskService.layer.pipe(Layer.provide(layerDependencies)))); |
| 109 | }).pipe(Effect.provide(SqlitePersistenceMemory)), | 109 | }).pipe(Effect.provide(SqlitePersistence.layerMemory)), |
| 110 | ); | 110 | ); |
apps/server/src/scheduledTasks/ScheduledTaskService.test.ts+4-4| ... | @@ -18,7 +18,7 @@ import * as TestClock from "effect/testing/TestClock"; | ... | @@ -18,7 +18,7 @@ import * as TestClock from "effect/testing/TestClock"; |
| 18 | import * as ThreadLaunchService from "../orchestration-v2/ThreadLaunchService.ts"; | 18 | import * as ThreadLaunchService from "../orchestration-v2/ThreadLaunchService.ts"; |
| 19 | import * as ThreadManagementService from "../orchestration-v2/ThreadManagementService.ts"; | 19 | import * as ThreadManagementService from "../orchestration-v2/ThreadManagementService.ts"; |
| 20 | import * as SecretRequests from "../secrets/SecretRequests.ts"; | 20 | import * as SecretRequests from "../secrets/SecretRequests.ts"; |
| 21 | import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; | 21 | import * as SqlitePersistence from "../persistence/Layers/Sqlite.ts"; |
| 22 | import * as ScheduledTaskService from "./ScheduledTaskService.ts"; | 22 | import * as ScheduledTaskService from "./ScheduledTaskService.ts"; |
| 23 | 23 | ||
| 24 | const isScheduledTaskError = Schema.is(ScheduledTaskError); | 24 | const isScheduledTaskError = Schema.is(ScheduledTaskError); |
| ... | @@ -154,7 +154,7 @@ it.effect("loads only due tasks and skips corrupt due rows without decoding sett | ... | @@ -154,7 +154,7 @@ it.effect("loads only due tasks and skips corrupt due rows without decoding sett |
| 154 | last_run_status: string; | 154 | last_run_status: string; |
| 155 | }>`SELECT last_run_status FROM scheduled_tasks WHERE task_id = 'running'`; | 155 | }>`SELECT last_run_status FROM scheduled_tasks WHERE task_id = 'running'`; |
| 156 | assert.equal(running[0]?.last_run_status, "running"); | 156 | assert.equal(running[0]?.last_run_status, "running"); |
| 157 | }).pipe(Effect.provide(SqlitePersistenceMemory)), | 157 | }).pipe(Effect.provide(SqlitePersistence.layerMemory)), |
| 158 | ); | 158 | ); |
| 159 | 159 | ||
| 160 | it.effect( | 160 | it.effect( |
| ... | @@ -268,7 +268,7 @@ it.effect( | ... | @@ -268,7 +268,7 @@ it.effect( |
| 268 | assert.isNull(byId.get("due-huge")?.next_run_at); | 268 | assert.isNull(byId.get("due-huge")?.next_run_at); |
| 269 | assert.equal(byId.get("due-bad-date")?.last_run_status, "never"); | 269 | assert.equal(byId.get("due-bad-date")?.last_run_status, "never"); |
| 270 | assert.equal(byId.get("due-bad-date")?.run_count, 0); | 270 | assert.equal(byId.get("due-bad-date")?.run_count, 0); |
| 271 | }).pipe(Effect.provide(SqlitePersistenceMemory)), | 271 | }).pipe(Effect.provide(SqlitePersistence.layerMemory)), |
| 272 | ); | 272 | ); |
| 273 | 273 | ||
| 274 | it.effect( | 274 | it.effect( |
| ... | @@ -349,5 +349,5 @@ it.effect( | ... | @@ -349,5 +349,5 @@ it.effect( |
| 349 | yield* Deferred.succeed(releaseLast, undefined); | 349 | yield* Deferred.succeed(releaseLast, undefined); |
| 350 | }), | 350 | }), |
| 351 | ); | 351 | ); |
| 352 | }).pipe(Effect.provide(SqlitePersistenceMemory)), | 352 | }).pipe(Effect.provide(SqlitePersistence.layerMemory)), |
| 353 | ); | 353 | ); |
apps/server/src/scheduledTasks/ScheduledTaskService.webhook.test.ts+4-4| ... | @@ -17,7 +17,7 @@ import * as TestClock from "effect/testing/TestClock"; | ... | @@ -17,7 +17,7 @@ import * as TestClock from "effect/testing/TestClock"; |
| 17 | import * as ThreadLaunchService from "../orchestration-v2/ThreadLaunchService.ts"; | 17 | import * as ThreadLaunchService from "../orchestration-v2/ThreadLaunchService.ts"; |
| 18 | import * as ThreadManagementService from "../orchestration-v2/ThreadManagementService.ts"; | 18 | import * as ThreadManagementService from "../orchestration-v2/ThreadManagementService.ts"; |
| 19 | import * as SecretRequests from "../secrets/SecretRequests.ts"; | 19 | import * as SecretRequests from "../secrets/SecretRequests.ts"; |
| 20 | import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; | 20 | import * as SqlitePersistence from "../persistence/Layers/Sqlite.ts"; |
| 21 | import * as Scheduler from "../scheduling/Scheduler.ts"; | 21 | import * as Scheduler from "../scheduling/Scheduler.ts"; |
| 22 | import * as ScheduledTaskService from "./ScheduledTaskService.ts"; | 22 | import * as ScheduledTaskService from "./ScheduledTaskService.ts"; |
| 23 | 23 | ||
| ... | @@ -75,7 +75,7 @@ const withService = <A, E>( | ... | @@ -75,7 +75,7 @@ const withService = <A, E>( |
| 75 | Effect.gen(function* () { | 75 | Effect.gen(function* () { |
| 76 | const launches = yield* Queue.unbounded<LaunchInput>(); | 76 | const launches = yield* Queue.unbounded<LaunchInput>(); |
| 77 | const secretsByRef = new Map<string, string>(); | 77 | const secretsByRef = new Map<string, string>(); |
| 78 | const dependencies = Layer.mergeAll( | 78 | const layerDependencies = Layer.mergeAll( |
| 79 | NodePlatformCrypto.layer, | 79 | NodePlatformCrypto.layer, |
| 80 | Scheduler.layer, | 80 | Scheduler.layer, |
| 81 | Layer.mock(ThreadLaunchService.ThreadLaunchService)({ | 81 | Layer.mock(ThreadLaunchService.ThreadLaunchService)({ |
| ... | @@ -103,8 +103,8 @@ const withService = <A, E>( | ... | @@ -103,8 +103,8 @@ const withService = <A, E>( |
| 103 | return yield* Effect.gen(function* () { | 103 | return yield* Effect.gen(function* () { |
| 104 | const service = yield* ScheduledTaskService.ScheduledTaskService; | 104 | const service = yield* ScheduledTaskService.ScheduledTaskService; |
| 105 | return yield* body({ service, launches, secretsByRef }); | 105 | return yield* body({ service, launches, secretsByRef }); |
| 106 | }).pipe(Effect.provide(ScheduledTaskService.layer.pipe(Layer.provide(dependencies)))); | 106 | }).pipe(Effect.provide(ScheduledTaskService.layer.pipe(Layer.provide(layerDependencies)))); |
| 107 | }).pipe(Effect.provide(SqlitePersistenceMemory)); | 107 | }).pipe(Effect.provide(SqlitePersistence.layerMemory)); |
| 108 | 108 | ||
| 109 | it.effect("dispatches exactly the rendered prompt and logs the delivery", () => | 109 | it.effect("dispatches exactly the rendered prompt and logs the delivery", () => |
| 110 | withService(({ service, launches }) => | 110 | withService(({ service, launches }) => |
apps/server/src/scheduledTasks/webhookRoute.test.ts+3-2| ... | @@ -27,7 +27,8 @@ import * as ServerSecretStore from "../auth/ServerSecretStore.ts"; | ... | @@ -27,7 +27,8 @@ import * as ServerSecretStore from "../auth/ServerSecretStore.ts"; |
| 27 | import { CLOUD_MINT_PUBLIC_KEY, RELAY_ISSUER_SECRET } from "../cloud/config.ts"; | 27 | import { CLOUD_MINT_PUBLIC_KEY, RELAY_ISSUER_SECRET } from "../cloud/config.ts"; |
| 28 | import * as ServerEnvironment from "../environment/ServerEnvironment.ts"; | 28 | import * as ServerEnvironment from "../environment/ServerEnvironment.ts"; |
| 29 | import * as RelayDeliveryProof from "./RelayDeliveryProof.ts"; | 29 | import * as RelayDeliveryProof from "./RelayDeliveryProof.ts"; |
| 30 | import { WEBHOOK_MAX_BODY_BYTES, webhookHttpApiLayer } from "./webhookRoute.ts"; | 30 | import { WEBHOOK_MAX_BODY_BYTES } from "./webhookRoute.ts"; |
| 31 | import * as WebhookRoute from "./webhookRoute.ts"; | ||
| 31 | 32 | ||
| 32 | class WebhookTestApi extends HttpApi.make("environment").add(EnvironmentHttpApi.groups.webhooks) {} | 33 | class WebhookTestApi extends HttpApi.make("environment").add(EnvironmentHttpApi.groups.webhooks) {} |
| 33 | 34 | ||
| ... | @@ -77,7 +78,7 @@ const handlerFor = ( | ... | @@ -77,7 +78,7 @@ const handlerFor = ( |
| 77 | ) => | 78 | ) => |
| 78 | HttpRouter.toWebHandler( | 79 | HttpRouter.toWebHandler( |
| 79 | HttpApiBuilder.layer(WebhookTestApi).pipe( | 80 | HttpApiBuilder.layer(WebhookTestApi).pipe( |
| 80 | Layer.provide(webhookHttpApiLayer), | 81 | Layer.provide(WebhookRoute.layer), |
| 81 | Layer.provide(RelayDeliveryProof.layer), | 82 | Layer.provide(RelayDeliveryProof.layer), |
| 82 | Layer.provide(Layer.mock(ScheduledTaskService)({ triggerWebhook: trigger })), | 83 | Layer.provide(Layer.mock(ScheduledTaskService)({ triggerWebhook: trigger })), |
| 83 | Layer.provide( | 84 | Layer.provide( |
apps/server/src/scheduledTasks/webhookRoute.ts+1-1| ... | @@ -23,7 +23,7 @@ const WEBHOOK_OUTCOME_HEADER = "x-t3-hook-outcome"; | ... | @@ -23,7 +23,7 @@ const WEBHOOK_OUTCOME_HEADER = "x-t3-hook-outcome"; |
| 23 | const json = (status: number, body: Record<string, string>, outcome: string) => | 23 | const json = (status: number, body: Record<string, string>, outcome: string) => |
| 24 | HttpServerResponse.jsonUnsafe(body, { status, headers: { [WEBHOOK_OUTCOME_HEADER]: outcome } }); | 24 | HttpServerResponse.jsonUnsafe(body, { status, headers: { [WEBHOOK_OUTCOME_HEADER]: outcome } }); |
| 25 | 25 | ||
| 26 | export const webhookHttpApiLayer = HttpApiBuilder.group( | 26 | export const layer = HttpApiBuilder.group( |
| 27 | EnvironmentHttpApi, | 27 | EnvironmentHttpApi, |
| 28 | "webhooks", | 28 | "webhooks", |
| 29 | Effect.fnUntraced(function* (handlers) { | 29 | Effect.fnUntraced(function* (handlers) { |
apps/server/src/scheduling/Scheduler.integration.test.ts+7-7| ... | @@ -22,7 +22,7 @@ import * as ProjectionStore from "../orchestration-v2/ProjectionStore.ts"; | ... | @@ -22,7 +22,7 @@ import * as ProjectionStore from "../orchestration-v2/ProjectionStore.ts"; |
| 22 | import * as ThreadLaunchService from "../orchestration-v2/ThreadLaunchService.ts"; | 22 | import * as ThreadLaunchService from "../orchestration-v2/ThreadLaunchService.ts"; |
| 23 | import * as ThreadManagementService from "../orchestration-v2/ThreadManagementService.ts"; | 23 | import * as ThreadManagementService from "../orchestration-v2/ThreadManagementService.ts"; |
| 24 | import * as UsageLimitRecoveryWorker from "../orchestration-v2/UsageLimitRecoveryWorker.ts"; | 24 | import * as UsageLimitRecoveryWorker from "../orchestration-v2/UsageLimitRecoveryWorker.ts"; |
| 25 | import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; | 25 | import * as SqlitePersistence from "../persistence/Layers/Sqlite.ts"; |
| 26 | import * as ScheduledTasks from "../scheduledTasks/ScheduledTaskService.ts"; | 26 | import * as ScheduledTasks from "../scheduledTasks/ScheduledTaskService.ts"; |
| 27 | import * as SecretRequests from "../secrets/SecretRequests.ts"; | 27 | import * as SecretRequests from "../secrets/SecretRequests.ts"; |
| 28 | import * as ServerSettings from "../serverSettings.ts"; | 28 | import * as ServerSettings from "../serverSettings.ts"; |
| ... | @@ -86,7 +86,7 @@ it.effect.each(["on time", "after restart"])( | ... | @@ -86,7 +86,7 @@ it.effect.each(["on time", "after restart"])( |
| 86 | const current = yield* Ref.make(thread); | 86 | const current = yield* Ref.make(thread); |
| 87 | const commands = yield* Ref.make<ReadonlyArray<OrchestrationV2ServerCommand>>([]); | 87 | const commands = yield* Ref.make<ReadonlyArray<OrchestrationV2ServerCommand>>([]); |
| 88 | const receipts = yield* Queue.unbounded<"task" | "retry">(); | 88 | const receipts = yield* Queue.unbounded<"task" | "retry">(); |
| 89 | const dependencies = Layer.mergeAll( | 89 | const layerDependencies = Layer.mergeAll( |
| 90 | NodeCrypto.layer, | 90 | NodeCrypto.layer, |
| 91 | Layer.mock(ThreadLaunchService.ThreadLaunchService)({ | 91 | Layer.mock(ThreadLaunchService.ThreadLaunchService)({ |
| 92 | launch: () => | 92 | launch: () => |
| ... | @@ -115,10 +115,10 @@ it.effect.each(["on time", "after restart"])( | ... | @@ -115,10 +115,10 @@ it.effect.each(["on time", "after restart"])( |
| 115 | }), | 115 | }), |
| 116 | Layer.mock(SecretRequests.SecretRequests)({}), | 116 | Layer.mock(SecretRequests.SecretRequests)({}), |
| 117 | ); | 117 | ); |
| 118 | const workers = Layer.mergeAll( | 118 | const layerWorkers = Layer.mergeAll( |
| 119 | ScheduledTasks.layer, | 119 | ScheduledTasks.layer, |
| 120 | UsageLimitRecoveryWorker.workerLive, | 120 | UsageLimitRecoveryWorker.layer, |
| 121 | ).pipe(Layer.provide(dependencies), Layer.provide(Scheduler.layer)); | 121 | ).pipe(Layer.provide(layerDependencies), Layer.provide(Scheduler.layer)); |
| 122 | yield* Effect.gen(function* () { | 122 | yield* Effect.gen(function* () { |
| 123 | const tasks = yield* ScheduledTasks.ScheduledTaskService; | 123 | const tasks = yield* ScheduledTasks.ScheduledTaskService; |
| 124 | const { task } = yield* tasks.upsert({ | 124 | const { task } = yield* tasks.upsert({ |
| ... | @@ -151,6 +151,6 @@ it.effect.each(["on time", "after restart"])( | ... | @@ -151,6 +151,6 @@ it.effect.each(["on time", "after restart"])( |
| 151 | usageLimitRecoveryRequestId: thread.limitRecovery!.requestId, | 151 | usageLimitRecoveryRequestId: thread.limitRecovery!.requestId, |
| 152 | text: "Continue where you left off.", | 152 | text: "Continue where you left off.", |
| 153 | }); | 153 | }); |
| 154 | }).pipe(Effect.provide(workers)); | 154 | }).pipe(Effect.provide(layerWorkers)); |
| 155 | }).pipe(Effect.provide(SqlitePersistenceMemory)), | 155 | }).pipe(Effect.provide(SqlitePersistence.layerMemory)), |
| 156 | ); | 156 | ); |
apps/server/src/secrets/SecretRequests.test.ts+2-2| ... | @@ -48,7 +48,7 @@ const withService = <A, E>( | ... | @@ -48,7 +48,7 @@ const withService = <A, E>( |
| 48 | let secretStatus = "pending"; | 48 | let secretStatus = "pending"; |
| 49 | let failedRecords = options.failedRecords ?? 0; | 49 | let failedRecords = options.failedRecords ?? 0; |
| 50 | const requestThreadId = options.threadId ?? threadId; | 50 | const requestThreadId = options.threadId ?? threadId; |
| 51 | const dependencies = Layer.mergeAll( | 51 | const layerDependencies = Layer.mergeAll( |
| 52 | NodeCrypto.layer, | 52 | NodeCrypto.layer, |
| 53 | NodeServices.layer, | 53 | NodeServices.layer, |
| 54 | Layer.succeed( | 54 | Layer.succeed( |
| ... | @@ -130,7 +130,7 @@ const withService = <A, E>( | ... | @@ -130,7 +130,7 @@ const withService = <A, E>( |
| 130 | return yield* Effect.gen(function* () { | 130 | return yield* Effect.gen(function* () { |
| 131 | const service = yield* SecretRequests.SecretRequests; | 131 | const service = yield* SecretRequests.SecretRequests; |
| 132 | return yield* body({ service, stored, dispatched }); | 132 | return yield* body({ service, stored, dispatched }); |
| 133 | }).pipe(Effect.provide(SecretRequests.layer.pipe(Layer.provide(dependencies)))); | 133 | }).pipe(Effect.provide(SecretRequests.layer.pipe(Layer.provide(layerDependencies)))); |
| 134 | }); | 134 | }); |
| 135 | 135 | ||
| 136 | /** The secret values in the store, leaving out the server's own salt. */ | 136 | /** The secret values in the store, leaving out the server's own salt. */ |
apps/server/src/server.ts+194-217| ... | @@ -25,22 +25,14 @@ import * as HttpApiBuilder from "effect/http-api/HttpApiBuilder"; | ... | @@ -25,22 +25,14 @@ import * as HttpApiBuilder from "effect/http-api/HttpApiBuilder"; |
| 25 | import * as BackgroundPolicy from "./background/BackgroundPolicy.ts"; | 25 | import * as BackgroundPolicy from "./background/BackgroundPolicy.ts"; |
| 26 | import * as HostPowerMonitor from "./background/HostPowerMonitor.ts"; | 26 | import * as HostPowerMonitor from "./background/HostPowerMonitor.ts"; |
| 27 | import * as ServerConfig from "./config.ts"; | 27 | import * as ServerConfig from "./config.ts"; |
| 28 | import { | 28 | import { withUntracedRequests } from "./http.ts"; |
| 29 | otlpTracesProxyRouteLayer, | 29 | import * as ServerHttp from "./http.ts"; |
| 30 | assetRouteLayer, | ||
| 31 | attachmentUploadRouteLayer, | ||
| 32 | serverEnvironmentHttpApiLayer, | ||
| 33 | staticAndDevRouteLayer, | ||
| 34 | browserApiCorsLayer, | ||
| 35 | httpCompressionLayer, | ||
| 36 | withUntracedRequests, | ||
| 37 | } from "./http.ts"; | ||
| 38 | import { guardHttpResponseWriteErrors } from "./httpResponseErrorGuard.ts"; | 30 | import { guardHttpResponseWriteErrors } from "./httpResponseErrorGuard.ts"; |
| 39 | import { fixPath } from "./os-jank.ts"; | 31 | import { fixPath } from "./os-jank.ts"; |
| 40 | import { websocketRpcRouteLayer } from "./ws.ts"; | 32 | import * as Ws from "./ws.ts"; |
| 41 | import * as ExternalLauncher from "./process/externalLauncher.ts"; | 33 | import * as ExternalLauncher from "./process/externalLauncher.ts"; |
| 42 | import * as NodePtyAdapter from "./terminal/NodePtyAdapter.ts"; | 34 | import * as NodePtyAdapter from "./terminal/NodePtyAdapter.ts"; |
| 43 | import { pullRequestHttpApiLayer } from "./pullRequest/http.ts"; | 35 | import * as PullRequestHttp from "./pullRequest/http.ts"; |
| 44 | import * as PullRequestProviderRegistry from "./pullRequest/PullRequestProviderRegistry.ts"; | 36 | import * as PullRequestProviderRegistry from "./pullRequest/PullRequestProviderRegistry.ts"; |
| 45 | import * as PullRequestService from "./pullRequest/PullRequestService.ts"; | 37 | import * as PullRequestService from "./pullRequest/PullRequestService.ts"; |
| 46 | import * as SqlitePersistence from "./persistence/Layers/Sqlite.ts"; | 38 | import * as SqlitePersistence from "./persistence/Layers/Sqlite.ts"; |
| ... | @@ -53,7 +45,7 @@ import * as ResetCreditCoordinator from "./provider/Layers/resetCreditCoordinato | ... | @@ -53,7 +45,7 @@ import * as ResetCreditCoordinator from "./provider/Layers/resetCreditCoordinato |
| 53 | import * as ProviderEventLoggers from "./provider/Layers/ProviderEventLoggers.ts"; | 45 | import * as ProviderEventLoggers from "./provider/Layers/ProviderEventLoggers.ts"; |
| 54 | import * as OpenCodeRuntime from "./provider/opencodeRuntime.ts"; | 46 | import * as OpenCodeRuntime from "./provider/opencodeRuntime.ts"; |
| 55 | import * as OpenCodeServerLedger from "./provider/OpenCodeServerLedger.ts"; | 47 | import * as OpenCodeServerLedger from "./provider/OpenCodeServerLedger.ts"; |
| 56 | import { AcpRegistryCatalogLive } from "./provider/Layers/AcpRegistryCatalog.ts"; | 48 | import * as AcpRegistryCatalog from "./provider/Layers/AcpRegistryCatalog.ts"; |
| 57 | import * as CheckpointDiffQuery from "./checkpointing/CheckpointDiffQuery.ts"; | 49 | import * as CheckpointDiffQuery from "./checkpointing/CheckpointDiffQuery.ts"; |
| 58 | import * as CheckpointStore from "./checkpointing/CheckpointStore.ts"; | 50 | import * as CheckpointStore from "./checkpointing/CheckpointStore.ts"; |
| 59 | import * as AzureDevOpsCli from "./sourceControl/AzureDevOpsCli.ts"; | 51 | import * as AzureDevOpsCli from "./sourceControl/AzureDevOpsCli.ts"; |
| ... | @@ -62,13 +54,13 @@ import * as GitHubCli from "./sourceControl/GitHubCli.ts"; | ... | @@ -62,13 +54,13 @@ import * as GitHubCli from "./sourceControl/GitHubCli.ts"; |
| 62 | import * as GitLabCli from "./sourceControl/GitLabCli.ts"; | 54 | import * as GitLabCli from "./sourceControl/GitLabCli.ts"; |
| 63 | import * as ForgejoCli from "./sourceControl/ForgejoCli.ts"; | 55 | import * as ForgejoCli from "./sourceControl/ForgejoCli.ts"; |
| 64 | import * as TextGeneration from "./textGeneration/TextGeneration.ts"; | 56 | import * as TextGeneration from "./textGeneration/TextGeneration.ts"; |
| 65 | import { ProviderInstanceRegistryHydrationLive } from "./provider/Layers/ProviderInstanceRegistryHydration.ts"; | 57 | import * as ProviderInstanceRegistryHydration from "./provider/Layers/ProviderInstanceRegistryHydration.ts"; |
| 66 | import * as TerminalManager from "./terminal/Manager.ts"; | 58 | import * as TerminalManager from "./terminal/Manager.ts"; |
| 67 | import * as McpHttpServer from "./mcp/McpHttpServer.ts"; | 59 | import * as McpHttpServer from "./mcp/McpHttpServer.ts"; |
| 68 | import * as McpSessionRegistry from "./mcp/McpSessionRegistry.ts"; | 60 | import * as McpSessionRegistry from "./mcp/McpSessionRegistry.ts"; |
| 69 | import * as PreviewAutomationBroker from "./mcp/PreviewAutomationBroker.ts"; | 61 | import * as PreviewAutomationBroker from "./mcp/PreviewAutomationBroker.ts"; |
| 70 | import * as DeviceService from "./device/DeviceService.ts"; | 62 | import * as DeviceService from "./device/DeviceService.ts"; |
| 71 | import { deviceHubProxyRouteLayer } from "./device/DeviceHubProxy.ts"; | 63 | import * as DeviceHubProxy from "./device/DeviceHubProxy.ts"; |
| 72 | import * as PreviewManager from "./preview/Manager.ts"; | 64 | import * as PreviewManager from "./preview/Manager.ts"; |
| 73 | import * as PortScanner from "./preview/PortScanner.ts"; | 65 | import * as PortScanner from "./preview/PortScanner.ts"; |
| 74 | import * as ProcessRunner from "./processRunner.ts"; | 66 | import * as ProcessRunner from "./processRunner.ts"; |
| ... | @@ -78,7 +70,7 @@ import * as Keybindings from "./keybindings.ts"; | ... | @@ -78,7 +70,7 @@ import * as Keybindings from "./keybindings.ts"; |
| 78 | import * as ServerRuntimeStartup from "./serverRuntimeStartup.ts"; | 70 | import * as ServerRuntimeStartup from "./serverRuntimeStartup.ts"; |
| 79 | import * as AgentAwarenessRelay from "./relay/AgentAwarenessRelay.ts"; | 71 | import * as AgentAwarenessRelay from "./relay/AgentAwarenessRelay.ts"; |
| 80 | import { hasCloudPublicConfig } from "./cloud/publicConfig.ts"; | 72 | import { hasCloudPublicConfig } from "./cloud/publicConfig.ts"; |
| 81 | import { ProviderRegistryLive } from "./provider/Layers/ProviderRegistry.ts"; | 73 | import * as ProviderRegistryLayer from "./provider/Layers/ProviderRegistry.ts"; |
| 82 | import * as ServerSettings from "./serverSettings.ts"; | 74 | import * as ServerSettings from "./serverSettings.ts"; |
| 83 | import * as ProjectEnrichmentService from "./project/ProjectEnrichmentService.ts"; | 75 | import * as ProjectEnrichmentService from "./project/ProjectEnrichmentService.ts"; |
| 84 | import * as NativeAppIconResolver from "./assets/NativeAppIconResolver.ts"; | 76 | import * as NativeAppIconResolver from "./assets/NativeAppIconResolver.ts"; |
| ... | @@ -87,7 +79,7 @@ import * as CodexInstallation from "./provider/CodexInstallation.ts"; | ... | @@ -87,7 +79,7 @@ import * as CodexInstallation from "./provider/CodexInstallation.ts"; |
| 87 | import * as ProviderInstanceRegistry from "./provider/Services/ProviderInstanceRegistry.ts"; | 79 | import * as ProviderInstanceRegistry from "./provider/Services/ProviderInstanceRegistry.ts"; |
| 88 | import * as ProviderAdapterRegistry from "./orchestration-v2/ProviderAdapterRegistry.ts"; | 80 | import * as ProviderAdapterRegistry from "./orchestration-v2/ProviderAdapterRegistry.ts"; |
| 89 | import * as ProviderRegistry from "./provider/Services/ProviderRegistry.ts"; | 81 | import * as ProviderRegistry from "./provider/Services/ProviderRegistry.ts"; |
| 90 | import { ProviderUsageLimitsIngestionLive } from "./provider/Layers/ProviderUsageLimitsIngestion.ts"; | 82 | import * as ProviderUsageLimitsIngestion from "./provider/Layers/ProviderUsageLimitsIngestion.ts"; |
| 91 | import * as UsageLimitSources from "./usage/UsageLimitSources.ts"; | 83 | import * as UsageLimitSources from "./usage/UsageLimitSources.ts"; |
| 92 | import * as ProjectFaviconResolver from "./project/ProjectFaviconResolver.ts"; | 84 | import * as ProjectFaviconResolver from "./project/ProjectFaviconResolver.ts"; |
| 93 | import * as T3ProjectFileLoader from "./project/T3ProjectFileLoader.ts"; | 85 | import * as T3ProjectFileLoader from "./project/T3ProjectFileLoader.ts"; |
| ... | @@ -109,16 +101,16 @@ import * as PullRequestReadCache from "./pullRequest/PullRequestReadCache.ts"; | ... | @@ -109,16 +101,16 @@ import * as PullRequestReadCache from "./pullRequest/PullRequestReadCache.ts"; |
| 109 | import * as SourceControlRateLimit from "./sourceControl/SourceControlRateLimit.ts"; | 101 | import * as SourceControlRateLimit from "./sourceControl/SourceControlRateLimit.ts"; |
| 110 | import * as SourceControlRepositoryService from "./sourceControl/SourceControlRepositoryService.ts"; | 102 | import * as SourceControlRepositoryService from "./sourceControl/SourceControlRepositoryService.ts"; |
| 111 | import * as WorktreeSetupTracker from "./project/WorktreeSetupTracker.ts"; | 103 | import * as WorktreeSetupTracker from "./project/WorktreeSetupTracker.ts"; |
| 112 | import { ObservabilityLive } from "./observability/Layers/Observability.ts"; | 104 | import * as Observability from "./observability/Layers/Observability.ts"; |
| 113 | import * as HeapSnapshot from "./observability/HeapSnapshot.ts"; | 105 | import * as HeapSnapshot from "./observability/HeapSnapshot.ts"; |
| 114 | import * as EventLoopMonitor from "./observability/EventLoopMonitor.ts"; | 106 | import * as EventLoopMonitor from "./observability/EventLoopMonitor.ts"; |
| 115 | import * as ServerEnvironment from "./environment/ServerEnvironment.ts"; | 107 | import * as ServerEnvironment from "./environment/ServerEnvironment.ts"; |
| 116 | import * as DirectEndpoints from "./environment/DirectEndpoints.ts"; | 108 | import * as DirectEndpoints from "./environment/DirectEndpoints.ts"; |
| 117 | import * as RemoteOpenTargets from "./environment/RemoteOpenTargets.ts"; | 109 | import * as RemoteOpenTargets from "./environment/RemoteOpenTargets.ts"; |
| 118 | import { authHttpApiLayer, environmentAuthenticatedAuthLayer } from "./auth/http.ts"; | 110 | import * as AuthHttp from "./auth/http.ts"; |
| 119 | import * as ReplayMarkers from "./auth/replayMarkers.ts"; | 111 | import * as ReplayMarkers from "./auth/replayMarkers.ts"; |
| 120 | import * as ServerSecretStore from "./auth/ServerSecretStore.ts"; | 112 | import * as ServerSecretStore from "./auth/ServerSecretStore.ts"; |
| 121 | import { webhookHttpApiLayer } from "./scheduledTasks/webhookRoute.ts"; | 113 | import * as WebhookRoute from "./scheduledTasks/webhookRoute.ts"; |
| 122 | import * as RelayDeliveryProof from "./scheduledTasks/RelayDeliveryProof.ts"; | 114 | import * as RelayDeliveryProof from "./scheduledTasks/RelayDeliveryProof.ts"; |
| 123 | import * as CloudPreferences from "./cloud/CloudPreferences.ts"; | 115 | import * as CloudPreferences from "./cloud/CloudPreferences.ts"; |
| 124 | import * as HeldHooksWaker from "./relay/HeldHooksWaker.ts"; | 116 | import * as HeldHooksWaker from "./relay/HeldHooksWaker.ts"; |
| ... | @@ -133,7 +125,6 @@ import { | ... | @@ -133,7 +125,6 @@ import { |
| 133 | } from "./cloud/config.ts"; | 125 | } from "./cloud/config.ts"; |
| 134 | import * as EnvironmentAuth from "./auth/EnvironmentAuth.ts"; | 126 | import * as EnvironmentAuth from "./auth/EnvironmentAuth.ts"; |
| 135 | import { | 127 | import { |
| 136 | connectHttpApiLayer, | ||
| 137 | pendingServiceUpdateExists, | 128 | pendingServiceUpdateExists, |
| 138 | reconcileDesiredCloudLinkIfStillDesired, | 129 | reconcileDesiredCloudLinkIfStillDesired, |
| 139 | recoverManagedCloudTunnel, | 130 | recoverManagedCloudTunnel, |
| ... | @@ -141,7 +132,8 @@ import { | ... | @@ -141,7 +132,8 @@ import { |
| 141 | startManagedCloudTunnelIfOriginConfirmed, | 132 | startManagedCloudTunnelIfOriginConfirmed, |
| 142 | releaseManagedTunnelOnShutdown, | 133 | releaseManagedTunnelOnShutdown, |
| 143 | } from "./cloud/http.ts"; | 134 | } from "./cloud/http.ts"; |
| 144 | import { serverRelayBrokerTracingLayer } from "./cloud/relayTracing.ts"; | 135 | import * as CloudHttp from "./cloud/http.ts"; |
| 136 | import * as RelayTracing from "./cloud/relayTracing.ts"; | ||
| 145 | import { shouldRetryCloudLink } from "./cloud/relayResponse.ts"; | 137 | import { shouldRetryCloudLink } from "./cloud/relayResponse.ts"; |
| 146 | import * as CloudManagedEndpointRuntime from "./cloud/ManagedEndpointRuntime.ts"; | 138 | import * as CloudManagedEndpointRuntime from "./cloud/ManagedEndpointRuntime.ts"; |
| 147 | import { | 139 | import { |
| ... | @@ -165,12 +157,7 @@ import * as ResourceAttribution from "./resourceTelemetry/ResourceAttribution.ts | ... | @@ -165,12 +157,7 @@ import * as ResourceAttribution from "./resourceTelemetry/ResourceAttribution.ts |
| 165 | import * as ResourceMonitorBinary from "./resourceTelemetry/ResourceMonitorBinary.ts"; | 157 | import * as ResourceMonitorBinary from "./resourceTelemetry/ResourceMonitorBinary.ts"; |
| 166 | import * as ResourceTelemetry from "./resourceTelemetry/ResourceTelemetry.ts"; | 158 | import * as ResourceTelemetry from "./resourceTelemetry/ResourceTelemetry.ts"; |
| 167 | import * as UsageService from "./usage/UsageService.ts"; | 159 | import * as UsageService from "./usage/UsageService.ts"; |
| 168 | import { | 160 | import * as RuntimeLayer from "./orchestration-v2/runtimeLayer.ts"; |
| 169 | OrchestrationEventInfrastructureLayerLive, | ||
| 170 | OrchestrationV2ProductionLayerLive, | ||
| 171 | ProjectServiceLayerLive, | ||
| 172 | ProjectSetupScriptRunnerLayerLive, | ||
| 173 | } from "./orchestration-v2/runtimeLayer.ts"; | ||
| 174 | import * as ProjectStore from "./orchestration-v2/ProjectStore.ts"; | 161 | import * as ProjectStore from "./orchestration-v2/ProjectStore.ts"; |
| 175 | import * as ThreadSearch from "./orchestration-v2/ThreadSearch.ts"; | 162 | import * as ThreadSearch from "./orchestration-v2/ThreadSearch.ts"; |
| 176 | import * as ResourceCleanupService from "./orchestration-v2/ResourceCleanupService.ts"; | 163 | import * as ResourceCleanupService from "./orchestration-v2/ResourceCleanupService.ts"; |
| ... | @@ -183,8 +170,8 @@ import { | ... | @@ -183,8 +170,8 @@ import { |
| 183 | makePersistedServerRuntimeState, | 170 | makePersistedServerRuntimeState, |
| 184 | persistServerRuntimeState, | 171 | persistServerRuntimeState, |
| 185 | } from "./serverRuntimeState.ts"; | 172 | } from "./serverRuntimeState.ts"; |
| 186 | import { orchestrationHttpApiLayer } from "./orchestration-v2/http.ts"; | 173 | import * as OrchestrationHttp from "./orchestration-v2/http.ts"; |
| 187 | import { projectHttpApiLayer } from "./project/http.ts"; | 174 | import * as ProjectHttp from "./project/http.ts"; |
| 188 | import * as NetService from "@t3tools/shared/Net"; | 175 | import * as NetService from "@t3tools/shared/Net"; |
| 189 | import * as RelayClient from "@t3tools/shared/relayClient"; | 176 | import * as RelayClient from "@t3tools/shared/relayClient"; |
| 190 | import { disableTailscaleServe, ensureTailscaleServe } from "@t3tools/tailscale"; | 177 | import { disableTailscaleServe, ensureTailscaleServe } from "@t3tools/tailscale"; |
| ... | @@ -201,63 +188,63 @@ const HTTP_ROUTER_CONFIG = { | ... | @@ -201,63 +188,63 @@ const HTTP_ROUTER_CONFIG = { |
| 201 | // already closes the websocket gracefully. Do not add an artificial drain before | 188 | // already closes the websocket gracefully. Do not add an artificial drain before |
| 202 | // those finalizers get a chance to run. | 189 | // those finalizers get a chance to run. |
| 203 | const HTTP_PREEMPTIVE_SHUTDOWN_GRACE_MS = 0; | 190 | const HTTP_PREEMPTIVE_SHUTDOWN_GRACE_MS = 0; |
| 204 | const ResourceAttributionLayerLive = ResourceAttribution.layer; | 191 | const layerResourceAttribution = ResourceAttribution.layer; |
| 205 | const ApplicationObservabilityLive = EventLoopMonitor.layer.pipe( | 192 | const layerApplicationObservability = EventLoopMonitor.layer.pipe( |
| 206 | Layer.provideMerge(ObservabilityLive), | 193 | Layer.provideMerge(Observability.layer), |
| 207 | Layer.provideMerge(ResourceAttributionLayerLive), | 194 | Layer.provideMerge(layerResourceAttribution), |
| 208 | ); | 195 | ); |
| 209 | 196 | ||
| 210 | const PtyAdapterLive = NodePtyAdapter.layer; | 197 | const layerPtyAdapter = NodePtyAdapter.layer; |
| 211 | 198 | ||
| 212 | const ServerSettingsLayerLive = ServerSettings.layer.pipe( | 199 | const layerServerSettings = ServerSettings.layer.pipe( |
| 213 | Layer.provide(ServerSecretStore.layer), | 200 | Layer.provide(ServerSecretStore.layer), |
| 214 | Layer.provideMerge(SqlitePersistence.layerConfig), | 201 | Layer.provideMerge(SqlitePersistence.layerConfig), |
| 215 | ); | 202 | ); |
| 216 | 203 | ||
| 217 | const NativeTelemetryLayerLive = NativeTelemetryClient.layer.pipe( | 204 | const layerNativeTelemetry = NativeTelemetryClient.layer.pipe( |
| 218 | Layer.provide(ResourceMonitorBinary.layer), | 205 | Layer.provide(ResourceMonitorBinary.layer), |
| 219 | ); | 206 | ); |
| 220 | const DesktopTelemetryReceiverLayerLive = DesktopTelemetryReceiver.layer.pipe( | 207 | const layerDesktopTelemetryReceiver = DesktopTelemetryReceiver.layer.pipe( |
| 221 | Layer.provideMerge(ServerSettingsLayerLive), | 208 | Layer.provideMerge(layerServerSettings), |
| 222 | ); | 209 | ); |
| 223 | 210 | ||
| 224 | const ResourceTelemetryLayerLive = ResourceTelemetry.layer.pipe( | 211 | const layerResourceTelemetry = ResourceTelemetry.layer.pipe( |
| 225 | Layer.provideMerge(NativeTelemetryLayerLive), | 212 | Layer.provideMerge(layerNativeTelemetry), |
| 226 | Layer.provideMerge(DesktopTelemetryReceiverLayerLive), | 213 | Layer.provideMerge(layerDesktopTelemetryReceiver), |
| 227 | ); | 214 | ); |
| 228 | 215 | ||
| 229 | const HostPowerMonitorLayerLive = HostPowerMonitor.layer.pipe( | 216 | const layerHostPowerMonitor = HostPowerMonitor.layer.pipe( |
| 230 | Layer.provide(DesktopTelemetryReceiverLayerLive), | 217 | Layer.provide(layerDesktopTelemetryReceiver), |
| 231 | ); | 218 | ); |
| 232 | 219 | ||
| 233 | // Reuses DesktopTelemetryReceiverLayerLive: a fresh receiver layer here | 220 | // Reuses DesktopTelemetryReceiverLayerLive: a fresh receiver layer here |
| 234 | // would open a second reader on the desktop telemetry fd. | 221 | // would open a second reader on the desktop telemetry fd. |
| 235 | const DesktopAppUpdateLayerLive = DesktopAppUpdate.layer.pipe( | 222 | const layerDesktopAppUpdate = DesktopAppUpdate.layer.pipe( |
| 236 | Layer.provide(DesktopTelemetryReceiverLayerLive), | 223 | Layer.provide(layerDesktopTelemetryReceiver), |
| 237 | ); | 224 | ); |
| 238 | 225 | ||
| 239 | const BackgroundLayerLive = BackgroundPolicy.layer.pipe( | 226 | const layerBackground = BackgroundPolicy.layer.pipe( |
| 240 | Layer.provide(HostPowerMonitorLayerLive), | 227 | Layer.provide(layerHostPowerMonitor), |
| 241 | Layer.provideMerge(ServerSettingsLayerLive), | 228 | Layer.provideMerge(layerServerSettings), |
| 242 | ); | 229 | ); |
| 243 | 230 | ||
| 244 | const UsageLayerLive = UsageService.layer.pipe(Layer.provide(ServerSettingsLayerLive)); | 231 | const layerUsage = UsageService.layer.pipe(Layer.provide(layerServerSettings)); |
| 245 | 232 | ||
| 246 | const ResourceDiagnosticsLayerLive = Layer.mergeAll( | 233 | const layerResourceDiagnostics = Layer.mergeAll( |
| 247 | HostResources.layer, | 234 | HostResources.layer, |
| 248 | ResourceTelemetryLayerLive, | 235 | layerResourceTelemetry, |
| 249 | ProcessDiagnostics.layer.pipe(Layer.provide(ResourceTelemetryLayerLive)), | 236 | ProcessDiagnostics.layer.pipe(Layer.provide(layerResourceTelemetry)), |
| 250 | ProcessResourceMonitor.layer.pipe(Layer.provide(ResourceTelemetryLayerLive)), | 237 | ProcessResourceMonitor.layer.pipe(Layer.provide(layerResourceTelemetry)), |
| 251 | ); | 238 | ); |
| 252 | 239 | ||
| 253 | const RelayClientLive = Layer.unwrap( | 240 | const layerRelayClient = Layer.unwrap( |
| 254 | Effect.gen(function* () { | 241 | Effect.gen(function* () { |
| 255 | const config = yield* ServerConfig.ServerConfig; | 242 | const config = yield* ServerConfig.ServerConfig; |
| 256 | return RelayClient.layerCloudflared({ baseDir: config.baseDir }); | 243 | return RelayClient.layerCloudflared({ baseDir: config.baseDir }); |
| 257 | }), | 244 | }), |
| 258 | ); | 245 | ); |
| 259 | 246 | ||
| 260 | const HttpServerLive = Layer.unwrap( | 247 | const layerHttpServer = Layer.unwrap( |
| 261 | Effect.gen(function* () { | 248 | Effect.gen(function* () { |
| 262 | const config = yield* ServerConfig.ServerConfig; | 249 | const config = yield* ServerConfig.ServerConfig; |
| 263 | return NodeHttpServer.layer(() => guardHttpResponseWriteErrors(NodeHttp.createServer()), { | 250 | return NodeHttpServer.layer(() => guardHttpResponseWriteErrors(NodeHttp.createServer()), { |
| ... | @@ -275,15 +262,13 @@ const HttpServerLive = Layer.unwrap( | ... | @@ -275,15 +262,13 @@ const HttpServerLive = Layer.unwrap( |
| 275 | }), | 262 | }), |
| 276 | ); | 263 | ); |
| 277 | 264 | ||
| 278 | const PlatformServicesLive = NodeServices.layer; | 265 | const layerPlatformServices = NodeServices.layer; |
| 279 | 266 | ||
| 280 | const PersistenceLayerLive = Layer.empty.pipe(Layer.provideMerge(SqlitePersistence.layerConfig)); | 267 | const layerPersistence = Layer.empty.pipe(Layer.provideMerge(SqlitePersistence.layerConfig)); |
| 281 | 268 | ||
| 282 | const VcsDriverRegistryLayerLive = VcsDriverRegistry.layer.pipe( | 269 | const layerVcsDriverRegistry = VcsDriverRegistry.layer.pipe(Layer.provide(VcsProjectConfig.layer)); |
| 283 | Layer.provide(VcsProjectConfig.layer), | ||
| 284 | ); | ||
| 285 | 270 | ||
| 286 | const SourceControlProviderRegistryLayerLive = SourceControlProviderRegistry.layer.pipe( | 271 | const layerSourceControlProviderRegistry = SourceControlProviderRegistry.layer.pipe( |
| 287 | Layer.provide( | 272 | Layer.provide( |
| 288 | Layer.mergeAll( | 273 | Layer.mergeAll( |
| 289 | AzureDevOpsCli.layer, | 274 | AzureDevOpsCli.layer, |
| ... | @@ -294,10 +279,10 @@ const SourceControlProviderRegistryLayerLive = SourceControlProviderRegistry.lay | ... | @@ -294,10 +279,10 @@ const SourceControlProviderRegistryLayerLive = SourceControlProviderRegistry.lay |
| 294 | ), | 279 | ), |
| 295 | ), | 280 | ), |
| 296 | Layer.provideMerge(GitVcsDriver.layer), | 281 | Layer.provideMerge(GitVcsDriver.layer), |
| 297 | Layer.provideMerge(VcsDriverRegistryLayerLive), | 282 | Layer.provideMerge(layerVcsDriverRegistry), |
| 298 | ); | 283 | ); |
| 299 | 284 | ||
| 300 | const RepositoryIdentityResolverLayerLive = Layer.effect( | 285 | const layerRepositoryIdentityResolver = Layer.effect( |
| 301 | RepositoryIdentityResolver.RepositoryIdentityResolver, | 286 | RepositoryIdentityResolver.RepositoryIdentityResolver, |
| 302 | Effect.gen(function* () { | 287 | Effect.gen(function* () { |
| 303 | const registry = yield* SourceControlProviderRegistry.SourceControlProviderRegistry; | 288 | const registry = yield* SourceControlProviderRegistry.SourceControlProviderRegistry; |
| ... | @@ -331,136 +316,130 @@ const RepositoryIdentityResolverLayerLive = Layer.effect( | ... | @@ -331,136 +316,130 @@ const RepositoryIdentityResolverLayerLive = Layer.effect( |
| 331 | }), | 316 | }), |
| 332 | }); | 317 | }); |
| 333 | }), | 318 | }), |
| 334 | ).pipe(Layer.provide(SourceControlProviderRegistryLayerLive), Layer.provide(ProcessRunner.layer)); | 319 | ).pipe(Layer.provide(layerSourceControlProviderRegistry), Layer.provide(ProcessRunner.layer)); |
| 335 | 320 | ||
| 336 | const PullRequestServiceLive = PullRequestService.layer.pipe( | 321 | const layerPullRequestService = PullRequestService.layer.pipe( |
| 337 | Layer.provide(PullRequestProviderRegistry.layer), | 322 | Layer.provide(PullRequestProviderRegistry.layer), |
| 338 | // Where the viewed-file marks live for a host that keeps none of its own. | 323 | // Where the viewed-file marks live for a host that keeps none of its own. |
| 339 | Layer.provide(PullRequestFilesViewed.layer), | 324 | Layer.provide(PullRequestFilesViewed.layer), |
| 340 | Layer.provide(PullRequestReadCache.layer), | 325 | Layer.provide(PullRequestReadCache.layer), |
| 341 | Layer.provide(SourceControlProviderRegistryLayerLive), | 326 | Layer.provide(layerSourceControlProviderRegistry), |
| 342 | Layer.provide(SourceControlRateLimit.layer), | 327 | Layer.provide(SourceControlRateLimit.layer), |
| 343 | ); | 328 | ); |
| 344 | 329 | ||
| 345 | const GitManagerLayerLive = GitManager.layer.pipe( | 330 | const layerGitManager = GitManager.layer.pipe( |
| 346 | // Per-project git settings resolve the acting thread's project. | 331 | // Per-project git settings resolve the acting thread's project. |
| 347 | Layer.provide(Layer.merge(ProjectionStoreV2.layer, ProjectStore.layer)), | 332 | Layer.provide(Layer.merge(ProjectionStoreV2.layer, ProjectStore.layer)), |
| 348 | Layer.provideMerge(ProjectSetupScriptRunnerLayerLive), | 333 | Layer.provideMerge(RuntimeLayer.layerProjectSetupScriptRunner), |
| 349 | Layer.provideMerge(WorktreeSetupTracker.layer), | 334 | Layer.provideMerge(WorktreeSetupTracker.layer), |
| 350 | Layer.provideMerge(GitVcsDriver.layer), | 335 | Layer.provideMerge(GitVcsDriver.layer), |
| 351 | Layer.provideMerge(SourceControlProviderRegistryLayerLive), | 336 | Layer.provideMerge(layerSourceControlProviderRegistry), |
| 352 | Layer.provideMerge( | 337 | Layer.provideMerge(TextGeneration.layer.pipe(Layer.provide(layerSourceControlProviderRegistry))), |
| 353 | TextGeneration.layer.pipe(Layer.provide(SourceControlProviderRegistryLayerLive)), | ||
| 354 | ), | ||
| 355 | ); | 338 | ); |
| 356 | 339 | ||
| 357 | const GitLayerLive = Layer.empty.pipe( | 340 | const layerGit = Layer.empty.pipe( |
| 358 | Layer.provideMerge(GitManagerLayerLive), | 341 | Layer.provideMerge(layerGitManager), |
| 359 | Layer.provideMerge(GitVcsDriver.layer), | 342 | Layer.provideMerge(GitVcsDriver.layer), |
| 360 | ); | 343 | ); |
| 361 | 344 | ||
| 362 | const GitWorkflowLayerLive = GitWorkflowService.layer.pipe( | 345 | const layerGitWorkflow = GitWorkflowService.layer.pipe( |
| 363 | Layer.provideMerge(VcsDriverRegistryLayerLive), | 346 | Layer.provideMerge(layerVcsDriverRegistry), |
| 364 | Layer.provideMerge(GitLayerLive), | 347 | Layer.provideMerge(layerGit), |
| 365 | ); | 348 | ); |
| 366 | 349 | ||
| 367 | const SourceControlRepositoryServiceLayerLive = SourceControlRepositoryService.layer.pipe( | 350 | const layerSourceControlRepositoryService = SourceControlRepositoryService.layer.pipe( |
| 368 | Layer.provideMerge(GitVcsDriver.layer), | 351 | Layer.provideMerge(GitVcsDriver.layer), |
| 369 | Layer.provideMerge(SourceControlProviderRegistryLayerLive), | 352 | Layer.provideMerge(layerSourceControlProviderRegistry), |
| 370 | ); | 353 | ); |
| 371 | 354 | ||
| 372 | const ProjectCloneTrackerLayerLive = ProjectCloneTracker.layer.pipe( | 355 | const layerProjectCloneTracker = ProjectCloneTracker.layer.pipe( |
| 373 | Layer.provide(SourceControlRepositoryServiceLayerLive), | 356 | Layer.provide(layerSourceControlRepositoryService), |
| 374 | ); | 357 | ); |
| 375 | 358 | ||
| 376 | const ReviewLayerLive = ReviewService.layer.pipe( | 359 | const layerReview = ReviewService.layer.pipe( |
| 377 | Layer.provideMerge(GitVcsDriver.layer), | 360 | Layer.provideMerge(GitVcsDriver.layer), |
| 378 | Layer.provideMerge(VcsDriverRegistryLayerLive), | 361 | Layer.provideMerge(layerVcsDriverRegistry), |
| 379 | ); | 362 | ); |
| 380 | 363 | ||
| 381 | const VcsLayerLive = Layer.empty.pipe( | 364 | const layerVcs = Layer.empty.pipe( |
| 382 | Layer.provideMerge(VcsProjectConfig.layer), | 365 | Layer.provideMerge(VcsProjectConfig.layer), |
| 383 | Layer.provideMerge(VcsDriverRegistryLayerLive), | 366 | Layer.provideMerge(layerVcsDriverRegistry), |
| 384 | Layer.provideMerge(VcsProvisioningService.layer.pipe(Layer.provide(VcsDriverRegistryLayerLive))), | 367 | Layer.provideMerge(VcsProvisioningService.layer.pipe(Layer.provide(layerVcsDriverRegistry))), |
| 385 | Layer.provideMerge(GitWorkflowLayerLive), | 368 | Layer.provideMerge(layerGitWorkflow), |
| 386 | Layer.provideMerge(ReviewLayerLive), | 369 | Layer.provideMerge(layerReview), |
| 387 | Layer.provideMerge(SourceControlRepositoryServiceLayerLive), | 370 | Layer.provideMerge(layerSourceControlRepositoryService), |
| 388 | Layer.provideMerge(ProjectCloneTrackerLayerLive), | 371 | Layer.provideMerge(layerProjectCloneTracker), |
| 389 | Layer.provideMerge( | 372 | Layer.provideMerge( |
| 390 | VcsStatusBroadcaster.layer.pipe( | 373 | VcsStatusBroadcaster.layer.pipe( |
| 391 | Layer.provide(GitWorkflowLayerLive), | 374 | Layer.provide(layerGitWorkflow), |
| 392 | // Auto-pull reads the project row. The orchestration runtime also | 375 | // Auto-pull reads the project row. The orchestration runtime also |
| 393 | // consumes the broadcaster (run finalization), so the policy cannot read | 376 | // consumes the broadcaster (run finalization), so the policy cannot read |
| 394 | // the store from the runtime's output. | 377 | // the store from the runtime's output. |
| 395 | Layer.provide( | 378 | Layer.provide( |
| 396 | VcsStatusBroadcaster.autoPullPolicyLayer.pipe(Layer.provide(ProjectStore.layer)), | 379 | VcsStatusBroadcaster.layerAutoPullPolicy.pipe(Layer.provide(ProjectStore.layer)), |
| 397 | ), | 380 | ), |
| 398 | ), | 381 | ), |
| 399 | ), | 382 | ), |
| 400 | ); | 383 | ); |
| 401 | 384 | ||
| 402 | const CheckpointStoreLayerLive = CheckpointStore.layer.pipe( | 385 | const layerCheckpointStore = CheckpointStore.layer.pipe(Layer.provide(layerVcsDriverRegistry)); |
| 403 | Layer.provide(VcsDriverRegistryLayerLive), | ||
| 404 | ); | ||
| 405 | 386 | ||
| 406 | const PortScannerLayerLive = PortScanner.layer.pipe(Layer.provide(ProcessRunner.layer)); | 387 | const layerPortScanner = PortScanner.layer.pipe(Layer.provide(ProcessRunner.layer)); |
| 407 | 388 | ||
| 408 | const TerminalLayerLive = TerminalManager.layer.pipe( | 389 | const layerTerminal = TerminalManager.layer.pipe( |
| 409 | Layer.provide(PtyAdapterLive), | 390 | Layer.provide(layerPtyAdapter), |
| 410 | Layer.provide(PortScannerLayerLive), | 391 | Layer.provide(layerPortScanner), |
| 411 | Layer.provide(NativeTelemetryLayerLive), | 392 | Layer.provide(layerNativeTelemetry), |
| 412 | ); | 393 | ); |
| 413 | 394 | ||
| 414 | const PreviewLayerLive = Layer.empty.pipe( | 395 | const layerPreview = Layer.empty.pipe( |
| 415 | Layer.provideMerge(PreviewManager.layer), | 396 | Layer.provideMerge(PreviewManager.layer), |
| 416 | Layer.provideMerge(PortScannerLayerLive), | 397 | Layer.provideMerge(layerPortScanner), |
| 417 | ); | 398 | ); |
| 418 | 399 | ||
| 419 | const DeviceLayerLive = DeviceService.layer.pipe( | 400 | const layerDevice = DeviceService.layer.pipe( |
| 420 | Layer.provide(ServerSettingsLayerLive), | 401 | Layer.provide(layerServerSettings), |
| 421 | Layer.provide(ProcessRunner.layer), | 402 | Layer.provide(ProcessRunner.layer), |
| 422 | Layer.provide(NetService.layer), | 403 | Layer.provide(NetService.layer), |
| 423 | ); | 404 | ); |
| 424 | 405 | ||
| 425 | const WorkspaceEntriesLayerLive = WorkspaceEntries.layer.pipe(Layer.provide(WorkspacePaths.layer)); | 406 | const layerWorkspaceEntries = WorkspaceEntries.layer.pipe(Layer.provide(WorkspacePaths.layer)); |
| 426 | 407 | ||
| 427 | const WorkspaceFileSystemLayerLive = WorkspaceFileSystem.layer.pipe( | 408 | const layerWorkspaceFileSystem = WorkspaceFileSystem.layer.pipe( |
| 428 | Layer.provide(WorkspacePaths.layer), | 409 | Layer.provide(WorkspacePaths.layer), |
| 429 | Layer.provide(WorkspaceEntriesLayerLive), | 410 | Layer.provide(layerWorkspaceEntries), |
| 430 | ); | 411 | ); |
| 431 | 412 | ||
| 432 | const WorkspaceLayerLive = Layer.mergeAll( | 413 | const layerWorkspace = Layer.mergeAll( |
| 433 | WorkspacePaths.layer, | 414 | WorkspacePaths.layer, |
| 434 | WorkspaceEntriesLayerLive, | 415 | layerWorkspaceEntries, |
| 435 | WorkspaceFileSystemLayerLive, | 416 | layerWorkspaceFileSystem, |
| 436 | ); | 417 | ); |
| 437 | 418 | ||
| 438 | const ProjectFaviconResolverLayerLive = ProjectFaviconResolver.layer.pipe( | 419 | const layerProjectFaviconResolver = ProjectFaviconResolver.layer.pipe( |
| 439 | Layer.provide(WorkspacePaths.layer), | 420 | Layer.provide(WorkspacePaths.layer), |
| 440 | Layer.provide(T3ProjectFileLoader.layer), | 421 | Layer.provide(T3ProjectFileLoader.layer), |
| 441 | ); | 422 | ); |
| 442 | 423 | ||
| 443 | const ServerEnvironmentLayerLive = ServerEnvironment.layer.pipe( | 424 | const layerServerEnvironment = ServerEnvironment.layer.pipe(Layer.provide(ServerSecretStore.layer)); |
| 444 | Layer.provide(ServerSecretStore.layer), | ||
| 445 | ); | ||
| 446 | 425 | ||
| 447 | const AuthLayerLive = EnvironmentAuth.layer.pipe( | 426 | const layerAuth = EnvironmentAuth.layer.pipe( |
| 448 | Layer.provideMerge(PersistenceLayerLive), | 427 | Layer.provideMerge(layerPersistence), |
| 449 | Layer.provide(ServerEnvironmentLayerLive), | 428 | Layer.provide(layerServerEnvironment), |
| 450 | Layer.provide(ServerSecretStore.layer), | 429 | Layer.provide(ServerSecretStore.layer), |
| 451 | ); | 430 | ); |
| 452 | 431 | ||
| 453 | const CloudManagedEndpointRuntimeLive = Layer.mergeAll( | 432 | const layerCloudManagedEndpointRuntime = Layer.mergeAll( |
| 454 | RelayClientLive, | 433 | layerRelayClient, |
| 455 | CloudManagedEndpointRuntime.layer.pipe( | 434 | CloudManagedEndpointRuntime.layer.pipe( |
| 456 | Layer.provide(ServerSecretStore.layer), | 435 | Layer.provide(ServerSecretStore.layer), |
| 457 | Layer.provide(RelayClientLive), | 436 | Layer.provide(layerRelayClient), |
| 458 | ), | 437 | ), |
| 459 | ); | 438 | ); |
| 460 | 439 | ||
| 461 | // Webhook URLs go through the relay only when the managed tunnel it forwards | 440 | // Webhook URLs go through the relay only when the managed tunnel it forwards |
| 462 | // to is configured; otherwise clients show the environment-relative path. | 441 | // to is configured; otherwise clients show the environment-relative path. |
| 463 | const ScheduledTaskWebhookOriginLive = Layer.effect( | 442 | const layerScheduledTaskWebhookOrigin = Layer.effect( |
| 464 | ScheduledTaskWebhookOrigin, | 443 | ScheduledTaskWebhookOrigin, |
| 465 | Effect.gen(function* () { | 444 | Effect.gen(function* () { |
| 466 | const secrets = yield* ServerSecretStore.ServerSecretStore; | 445 | const secrets = yield* ServerSecretStore.ServerSecretStore; |
| ... | @@ -484,38 +463,38 @@ const ScheduledTaskWebhookOriginLive = Layer.effect( | ... | @@ -484,38 +463,38 @@ const ScheduledTaskWebhookOriginLive = Layer.effect( |
| 484 | }), | 463 | }), |
| 485 | ); | 464 | ); |
| 486 | 465 | ||
| 487 | const OrchestrationV2RuntimeLayerLive = OrchestrationV2ProductionLayerLive.pipe( | 466 | const layerOrchestrationV2Runtime = RuntimeLayer.layerProduction.pipe( |
| 488 | Layer.provide(ScheduledTaskWebhookOriginLive), | 467 | Layer.provide(layerScheduledTaskWebhookOrigin), |
| 489 | Layer.provide(ProviderEventIngestor.analyticsLive), | 468 | Layer.provide(ProviderEventIngestor.layerAnalytics), |
| 490 | Layer.provide(CheckpointStoreLayerLive), | 469 | Layer.provide(layerCheckpointStore), |
| 491 | Layer.provide(GitWorkflowLayerLive), | 470 | Layer.provide(layerGitWorkflow), |
| 492 | Layer.provide(ResourceCleanupService.live), | 471 | Layer.provide(ResourceCleanupService.layer), |
| 493 | Layer.provide( | 472 | Layer.provide( |
| 494 | RunFinalizationService.observerLive.pipe( | 473 | RunFinalizationService.layerObserver.pipe( |
| 495 | Layer.provide(ProjectionStoreV2.layer), | 474 | Layer.provide(ProjectionStoreV2.layer), |
| 496 | Layer.provide(PullRequestServiceLive), | 475 | Layer.provide(layerPullRequestService), |
| 497 | Layer.provide(ProjectServiceLayerLive), | 476 | Layer.provide(RuntimeLayer.layerProjectService), |
| 498 | ), | 477 | ), |
| 499 | ), | 478 | ), |
| 500 | ); | 479 | ); |
| 501 | 480 | ||
| 502 | const OrchestrationApplicationLayerLive = CheckpointDiffQuery.layer.pipe( | 481 | const layerOrchestrationApplication = CheckpointDiffQuery.layer.pipe( |
| 503 | Layer.provideMerge(CheckpointStoreLayerLive), | 482 | Layer.provideMerge(layerCheckpointStore), |
| 504 | Layer.provideMerge(OrchestrationV2RuntimeLayerLive), | 483 | Layer.provideMerge(layerOrchestrationV2Runtime), |
| 505 | ); | 484 | ); |
| 506 | 485 | ||
| 507 | // Automatic thread settlement (#8600): a server-owned sweep evaluates | 486 | // Automatic thread settlement (#8600): a server-owned sweep evaluates |
| 508 | // inactivity and merged pull requests, then settles through the orchestrator | 487 | // inactivity and merged pull requests, then settles through the orchestrator |
| 509 | // so every client sees the same shelf. | 488 | // so every client sees the same shelf. |
| 510 | const ThreadSettlementWorkerLive = Layer.effectDiscard( | 489 | const layerThreadSettlementWorker = Layer.effectDiscard( |
| 511 | ThreadSettlementService.make.pipe(Effect.flatMap((service) => service.start())), | 490 | ThreadSettlementService.make.pipe(Effect.flatMap((service) => service.start())), |
| 512 | ).pipe(Layer.provide(PullRequestServiceLive), Layer.provide(ProjectionStoreV2.layer)); | 491 | ).pipe(Layer.provide(layerPullRequestService), Layer.provide(ProjectionStoreV2.layer)); |
| 513 | 492 | ||
| 514 | const ThreadPullRequestWorkerLive = Layer.effectDiscard( | 493 | const layerThreadPullRequestWorker = Layer.effectDiscard( |
| 515 | ThreadPullRequestService.make.pipe(Effect.flatMap((service) => service.start())), | 494 | ThreadPullRequestService.make.pipe(Effect.flatMap((service) => service.start())), |
| 516 | ).pipe(Layer.provide(PullRequestServiceLive)); | 495 | ).pipe(Layer.provide(layerPullRequestService)); |
| 517 | 496 | ||
| 518 | const ProviderInstallationRefreshLive = Layer.effectDiscard( | 497 | const layerProviderInstallationRefresh = Layer.effectDiscard( |
| 519 | Effect.gen(function* () { | 498 | Effect.gen(function* () { |
| 520 | const antigravity = yield* AntigravityInstallation.AntigravityInstallation; | 499 | const antigravity = yield* AntigravityInstallation.AntigravityInstallation; |
| 521 | const codex = yield* CodexInstallation.CodexInstallation; | 500 | const codex = yield* CodexInstallation.CodexInstallation; |
| ... | @@ -547,15 +526,15 @@ const ProviderInstallationRefreshLive = Layer.effectDiscard( | ... | @@ -547,15 +526,15 @@ const ProviderInstallationRefreshLive = Layer.effectDiscard( |
| 547 | }), | 526 | }), |
| 548 | ); | 527 | ); |
| 549 | 528 | ||
| 550 | const RuntimeCoreDependenciesBaseLive = Layer.mergeAll( | 529 | const layerRuntimeCoreDependenciesBase = Layer.mergeAll( |
| 551 | AgentAwarenessRelay.layer, | 530 | AgentAwarenessRelay.layer, |
| 552 | // Asks T3 Connect to deliver webhooks it held while this environment was offline. | 531 | // Asks T3 Connect to deliver webhooks it held while this environment was offline. |
| 553 | HeldHooksWaker.layer, | 532 | HeldHooksWaker.layer, |
| 554 | ThreadSettlementWorkerLive, | 533 | layerThreadSettlementWorker, |
| 555 | Layer.effectDiscard(StorageCleanup.make.pipe(Effect.flatMap((service) => service.start()))).pipe( | 534 | Layer.effectDiscard(StorageCleanup.make.pipe(Effect.flatMap((service) => service.start()))).pipe( |
| 556 | Layer.provide(ProjectionStoreV2.layer), | 535 | Layer.provide(ProjectionStoreV2.layer), |
| 557 | ), | 536 | ), |
| 558 | ThreadPullRequestWorkerLive, | 537 | layerThreadPullRequestWorker, |
| 559 | Layer.effectDiscard( | 538 | Layer.effectDiscard( |
| 560 | Effect.gen(function* () { | 539 | Effect.gen(function* () { |
| 561 | const service = yield* PullRequestSyncReactor.PullRequestSyncReactor; | 540 | const service = yield* PullRequestSyncReactor.PullRequestSyncReactor; |
| ... | @@ -563,7 +542,7 @@ const RuntimeCoreDependenciesBaseLive = Layer.mergeAll( | ... | @@ -563,7 +542,7 @@ const RuntimeCoreDependenciesBaseLive = Layer.mergeAll( |
| 563 | }), | 542 | }), |
| 564 | ).pipe( | 543 | ).pipe( |
| 565 | Layer.provideMerge(PullRequestSyncReactor.layer), | 544 | Layer.provideMerge(PullRequestSyncReactor.layer), |
| 566 | Layer.provide(PullRequestServiceLive), | 545 | Layer.provide(layerPullRequestService), |
| 567 | Layer.provide(ProjectionStoreV2.layer), | 546 | Layer.provide(ProjectionStoreV2.layer), |
| 568 | ), | 547 | ), |
| 569 | Layer.effectDiscard( | 548 | Layer.effectDiscard( |
| ... | @@ -573,38 +552,38 @@ const RuntimeCoreDependenciesBaseLive = Layer.mergeAll( | ... | @@ -573,38 +552,38 @@ const RuntimeCoreDependenciesBaseLive = Layer.mergeAll( |
| 573 | }), | 552 | }), |
| 574 | ).pipe( | 553 | ).pipe( |
| 575 | Layer.provide(PullRequestWatchReactor.layer), | 554 | Layer.provide(PullRequestWatchReactor.layer), |
| 576 | Layer.provide(PullRequestServiceLive), | 555 | Layer.provide(layerPullRequestService), |
| 577 | Layer.provide(ProjectionStoreV2.layer), | 556 | Layer.provide(ProjectionStoreV2.layer), |
| 578 | ), | 557 | ), |
| 579 | // Subscribes to `account.rate-limits.updated` so usage bars track live | 558 | // Subscribes to `account.rate-limits.updated` so usage bars track live |
| 580 | // telemetry instead of waiting for the next status probe. | 559 | // telemetry instead of waiting for the next status probe. |
| 581 | ProviderUsageLimitsIngestionLive, | 560 | ProviderUsageLimitsIngestion.layer, |
| 582 | ProviderInstallationRefreshLive, | 561 | layerProviderInstallationRefresh, |
| 583 | ReplayMarkers.layer, | 562 | ReplayMarkers.layer, |
| 584 | ).pipe( | 563 | ).pipe( |
| 585 | // Core Services | 564 | // Core Services |
| 586 | Layer.provideMerge(OrchestrationApplicationLayerLive), | 565 | Layer.provideMerge(layerOrchestrationApplication), |
| 587 | Layer.provideMerge(OrchestrationEventInfrastructureLayerLive), | 566 | Layer.provideMerge(RuntimeLayer.layerEventInfrastructure), |
| 588 | Layer.provideMerge(Layer.merge(ProjectStore.layer, ThreadSearch.layer)), | 567 | Layer.provideMerge(Layer.merge(ProjectStore.layer, ThreadSearch.layer)), |
| 589 | Layer.provideMerge(ServerSettingsLayerLive), | 568 | Layer.provideMerge(layerServerSettings), |
| 590 | // The asset route uses the registry's GitHub credential for private PR media. | 569 | // The asset route uses the registry's GitHub credential for private PR media. |
| 591 | Layer.provideMerge(Layer.mergeAll(SourceControlProviderRegistryLayerLive, GitHubCli.layer)), | 570 | Layer.provideMerge(Layer.mergeAll(layerSourceControlProviderRegistry, GitHubCli.layer)), |
| 592 | Layer.provideMerge(GitLayerLive), | 571 | Layer.provideMerge(layerGit), |
| 593 | Layer.provideMerge(VcsLayerLive), | 572 | Layer.provideMerge(layerVcs), |
| 594 | Layer.provideMerge(Layer.mergeAll(TerminalLayerLive, PreviewLayerLive, DeviceLayerLive)), | 573 | Layer.provideMerge(Layer.mergeAll(layerTerminal, layerPreview, layerDevice)), |
| 595 | Layer.provideMerge(PersistenceLayerLive), | 574 | Layer.provideMerge(layerPersistence), |
| 596 | // Both read a user-owned file out of the state directory and stream changes | 575 | // Both read a user-owned file out of the state directory and stream changes |
| 597 | // to clients; neither depends on the other. | 576 | // to clients; neither depends on the other. |
| 598 | Layer.provideMerge( | 577 | Layer.provideMerge( |
| 599 | Layer.mergeAll(Keybindings.layer, EnvironmentTheme.layer, UsageLimitSources.layer), | 578 | Layer.mergeAll(Keybindings.layer, EnvironmentTheme.layer, UsageLimitSources.layer), |
| 600 | ), | 579 | ), |
| 601 | Layer.provideMerge(ProviderRegistryLive), | 580 | Layer.provideMerge(ProviderRegistryLayer.layer), |
| 602 | // The instance registry is the new routing keystone — text generation, | 581 | // The instance registry is the new routing keystone — text generation, |
| 603 | // adapter lookup, and runtime ingestion all resolve `ProviderInstanceId` | 582 | // adapter lookup, and runtime ingestion all resolve `ProviderInstanceId` |
| 604 | // through this layer. Built-in drivers come from `BUILT_IN_DRIVERS`; | 583 | // through this layer. Built-in drivers come from `BUILT_IN_DRIVERS`; |
| 605 | // `providerInstances` hydration merges `settings.providers.<kind>` | 584 | // `providerInstances` hydration merges `settings.providers.<kind>` |
| 606 | // with explicit `providerInstances` entries on boot. | 585 | // with explicit `providerInstances` entries on boot. |
| 607 | Layer.provideMerge(ProviderInstanceRegistryHydrationLive), | 586 | Layer.provideMerge(ProviderInstanceRegistryHydration.layer), |
| 608 | Layer.provideMerge( | 587 | Layer.provideMerge( |
| 609 | Layer.mergeAll( | 588 | Layer.mergeAll( |
| 610 | AntigravityInstallation.AntigravityInstallation.layer, | 589 | AntigravityInstallation.AntigravityInstallation.layer, |
| ... | @@ -613,11 +592,11 @@ const RuntimeCoreDependenciesBaseLive = Layer.mergeAll( | ... | @@ -613,11 +592,11 @@ const RuntimeCoreDependenciesBaseLive = Layer.mergeAll( |
| 613 | ), | 592 | ), |
| 614 | ); | 593 | ); |
| 615 | 594 | ||
| 616 | const RuntimeCoreDependenciesLive = RuntimeCoreDependenciesBaseLive.pipe( | 595 | const layerRuntimeCoreDependencies = layerRuntimeCoreDependenciesBase.pipe( |
| 617 | Layer.provideMerge(PtyAdapterLive), | 596 | Layer.provideMerge(layerPtyAdapter), |
| 618 | // Search, prepare, status inspection, and turn launch share one registry | 597 | // Search, prepare, status inspection, and turn launch share one registry |
| 619 | // cache so every client and provider instance sees the same prepared agents. | 598 | // cache so every client and provider instance sees the same prepared agents. |
| 620 | Layer.provideMerge(AcpRegistryCatalogLive.pipe(Layer.provide(ServerSettingsLayerLive))), | 599 | Layer.provideMerge(AcpRegistryCatalog.layer.pipe(Layer.provide(layerServerSettings))), |
| 621 | // Shared native/canonical NDJSON writers used by both the per-instance | 600 | // Shared native/canonical NDJSON writers used by both the per-instance |
| 622 | // V2 drivers and the orchestration runtime. Provide resource attribution so | 601 | // V2 drivers and the orchestration runtime. Provide resource attribution so |
| 623 | // the rewritten telemetry pipeline can account for logical NDJSON writes. | 602 | // the rewritten telemetry pipeline can account for logical NDJSON writes. |
| ... | @@ -634,15 +613,13 @@ const RuntimeCoreDependenciesLive = RuntimeCoreDependenciesBaseLive.pipe( | ... | @@ -634,15 +613,13 @@ const RuntimeCoreDependenciesLive = RuntimeCoreDependenciesBaseLive.pipe( |
| 634 | // the rewritten registry reads snapshots off the instance registry and | 613 | // the rewritten registry reads snapshots off the instance registry and |
| 635 | // no longer transitively provides it. Exposing it at the runtime level | 614 | // no longer transitively provides it. Exposing it at the runtime level |
| 636 | // keeps a single Live for all opencode consumers. | 615 | // keeps a single Live for all opencode consumers. |
| 637 | Layer.provideMerge( | 616 | Layer.provideMerge(OpenCodeRuntime.layer.pipe(Layer.provide(OpenCodeServerLedger.layer))), |
| 638 | OpenCodeRuntime.OpenCodeRuntimeLive.pipe(Layer.provide(OpenCodeServerLedger.layer)), | 617 | Layer.provideMerge(layerWorkspace), |
| 639 | ), | ||
| 640 | Layer.provideMerge(WorkspaceLayerLive), | ||
| 641 | Layer.provideMerge(ProjectEnrichmentService.layer), | 618 | Layer.provideMerge(ProjectEnrichmentService.layer), |
| 642 | Layer.provideMerge(Layer.mergeAll(NativeAppIconResolver.layer, ProjectFaviconResolverLayerLive)), | 619 | Layer.provideMerge(Layer.mergeAll(NativeAppIconResolver.layer, layerProjectFaviconResolver)), |
| 643 | Layer.provideMerge(RepositoryIdentityResolverLayerLive), | 620 | Layer.provideMerge(layerRepositoryIdentityResolver), |
| 644 | Layer.provideMerge(ServerEnvironmentLayerLive), | 621 | Layer.provideMerge(layerServerEnvironment), |
| 645 | Layer.provideMerge(AuthLayerLive), | 622 | Layer.provideMerge(layerAuth), |
| 646 | Layer.provideMerge(ServerSecretStore.layer), | 623 | Layer.provideMerge(ServerSecretStore.layer), |
| 647 | Layer.provideMerge( | 624 | Layer.provideMerge( |
| 648 | Layer.mergeAll( | 625 | Layer.mergeAll( |
| ... | @@ -650,16 +627,16 @@ const RuntimeCoreDependenciesLive = RuntimeCoreDependenciesBaseLive.pipe( | ... | @@ -650,16 +627,16 @@ const RuntimeCoreDependenciesLive = RuntimeCoreDependenciesBaseLive.pipe( |
| 650 | Layer.provide(ServerSecretStore.layer), | 627 | Layer.provide(ServerSecretStore.layer), |
| 651 | Layer.provide(ExternalLauncher.layer), | 628 | Layer.provide(ExternalLauncher.layer), |
| 652 | ), | 629 | ), |
| 653 | CloudManagedEndpointRuntimeLive, | 630 | layerCloudManagedEndpointRuntime, |
| 654 | ), | 631 | ), |
| 655 | ), | 632 | ), |
| 656 | ); | 633 | ); |
| 657 | 634 | ||
| 658 | const RuntimeDependenciesLive = RuntimeCoreDependenciesLive.pipe( | 635 | const layerRuntimeDependencies = layerRuntimeCoreDependencies.pipe( |
| 659 | // Misc. | 636 | // Misc. |
| 660 | Layer.provideMerge(BackgroundLayerLive), | 637 | Layer.provideMerge(layerBackground), |
| 661 | Layer.provideMerge(ResourceDiagnosticsLayerLive), | 638 | Layer.provideMerge(layerResourceDiagnostics), |
| 662 | Layer.provideMerge(UsageLayerLive), | 639 | Layer.provideMerge(layerUsage), |
| 663 | Layer.provideMerge(TraceDiagnostics.layer), | 640 | Layer.provideMerge(TraceDiagnostics.layer), |
| 664 | Layer.provideMerge(AnalyticsService.layer), | 641 | Layer.provideMerge(AnalyticsService.layer), |
| 665 | Layer.provideMerge(ExternalLauncher.layer), | 642 | Layer.provideMerge(ExternalLauncher.layer), |
| ... | @@ -669,7 +646,7 @@ const RuntimeDependenciesLive = RuntimeCoreDependenciesLive.pipe( | ... | @@ -669,7 +646,7 @@ const RuntimeDependenciesLive = RuntimeCoreDependenciesLive.pipe( |
| 669 | Layer.provide(NetService.layer), | 646 | Layer.provide(NetService.layer), |
| 670 | ); | 647 | ); |
| 671 | 648 | ||
| 672 | const commandReadinessLayer = HttpRouter.middleware( | 649 | const layerCommandReadiness = HttpRouter.middleware( |
| 673 | (httpEffect) => | 650 | (httpEffect) => |
| 674 | Effect.flatMap(ServerRuntimeStartup.ServerRuntimeStartup, (startup) => | 651 | Effect.flatMap(ServerRuntimeStartup.ServerRuntimeStartup, (startup) => |
| 675 | startup.awaitCommandReady.pipe(Effect.orDie, Effect.andThen(httpEffect)), | 652 | startup.awaitCommandReady.pipe(Effect.orDie, Effect.andThen(httpEffect)), |
| ... | @@ -677,24 +654,24 @@ const commandReadinessLayer = HttpRouter.middleware( | ... | @@ -677,24 +654,24 @@ const commandReadinessLayer = HttpRouter.middleware( |
| 677 | { global: true }, | 654 | { global: true }, |
| 678 | ); | 655 | ); |
| 679 | 656 | ||
| 680 | const makeRoutesLayer = Layer.mergeAll( | 657 | const layerMakeRoutes = Layer.mergeAll( |
| 681 | Layer.mergeAll( | 658 | Layer.mergeAll( |
| 682 | HttpApiBuilder.layer(EnvironmentHttpApi).pipe( | 659 | HttpApiBuilder.layer(EnvironmentHttpApi).pipe( |
| 683 | Layer.provide(authHttpApiLayer), | 660 | Layer.provide(AuthHttp.layer), |
| 684 | Layer.provide(connectHttpApiLayer.pipe(Layer.provide(CloudPreferences.layer))), | 661 | Layer.provide(CloudHttp.layer.pipe(Layer.provide(CloudPreferences.layer))), |
| 685 | Layer.provide(orchestrationHttpApiLayer), | 662 | Layer.provide(OrchestrationHttp.layer), |
| 686 | Layer.provide(pullRequestHttpApiLayer), | 663 | Layer.provide(PullRequestHttp.layer), |
| 687 | Layer.provide(projectHttpApiLayer), | 664 | Layer.provide(ProjectHttp.layer), |
| 688 | Layer.provide(serverEnvironmentHttpApiLayer), | 665 | Layer.provide(ServerHttp.layerServerEnvironmentHttpApi), |
| 689 | Layer.provide(webhookHttpApiLayer.pipe(Layer.provide(RelayDeliveryProof.layer))), | 666 | Layer.provide(WebhookRoute.layer.pipe(Layer.provide(RelayDeliveryProof.layer))), |
| 690 | Layer.provide(environmentAuthenticatedAuthLayer), | 667 | Layer.provide(AuthHttp.layerAuthenticatedAuth), |
| 691 | ), | 668 | ), |
| 692 | otlpTracesProxyRouteLayer, | 669 | ServerHttp.layerOtlpTracesProxyRoute, |
| 693 | assetRouteLayer, | 670 | ServerHttp.layerAssetRoute, |
| 694 | attachmentUploadRouteLayer, | 671 | ServerHttp.layerAttachmentUploadRoute, |
| 695 | deviceHubProxyRouteLayer, | 672 | DeviceHubProxy.layer, |
| 696 | staticAndDevRouteLayer, | 673 | ServerHttp.layerStaticAndDevRoute, |
| 697 | websocketRpcRouteLayer, | 674 | Ws.layer, |
| 698 | ), | 675 | ), |
| 699 | // The MCP session registry is provided globally (shared with V2 provider | 676 | // The MCP session registry is provided globally (shared with V2 provider |
| 700 | // sessions) rather than inline here. The orchestrator toolkit resolves | 677 | // sessions) rather than inline here. The orchestrator toolkit resolves |
| ... | @@ -707,36 +684,36 @@ const makeRoutesLayer = Layer.mergeAll( | ... | @@ -707,36 +684,36 @@ const makeRoutesLayer = Layer.mergeAll( |
| 707 | ).pipe( | 684 | ).pipe( |
| 708 | // Both transports consume the same service instance, so caches single-flight across clients | 685 | // Both transports consume the same service instance, so caches single-flight across clients |
| 709 | // and mutations observed on WebSocket invalidate patches subsequently read over HTTP. | 686 | // and mutations observed on WebSocket invalidate patches subsequently read over HTTP. |
| 710 | Layer.provide(PullRequestServiceLive), | 687 | Layer.provide(layerPullRequestService), |
| 711 | Layer.provide(PreviewAutomationBroker.layer), | 688 | Layer.provide(PreviewAutomationBroker.layer), |
| 712 | Layer.provide(ServerSelfUpdate.layer.pipe(Layer.provide(DesktopAppUpdateLayerLive))), | 689 | Layer.provide(ServerSelfUpdate.layer.pipe(Layer.provide(layerDesktopAppUpdate))), |
| 713 | Layer.provide(commandReadinessLayer), | 690 | Layer.provide(layerCommandReadiness), |
| 714 | Layer.provide(browserApiCorsLayer), | 691 | Layer.provide(ServerHttp.layerBrowserApiCors), |
| 715 | Layer.provide(httpCompressionLayer), | 692 | Layer.provide(ServerHttp.layerHttpCompression), |
| 716 | ); | 693 | ); |
| 717 | 694 | ||
| 718 | const makeServerLayer = Layer.unwrap( | 695 | const layerMakeServer = Layer.unwrap( |
| 719 | Effect.gen(function* () { | 696 | Effect.gen(function* () { |
| 720 | const config = yield* ServerConfig.ServerConfig; | 697 | const config = yield* ServerConfig.ServerConfig; |
| 721 | const activation = yield* Deferred.make<void>(); | 698 | const activation = yield* Deferred.make<void>(); |
| 722 | const awaitActivation = Deferred.await(activation); | 699 | const awaitActivation = Deferred.await(activation); |
| 723 | const activationLayer = Layer.succeed(ServerActivation.ServerActivation, awaitActivation); | 700 | const layerActivation = Layer.succeed(ServerActivation.ServerActivation, awaitActivation); |
| 724 | const runtimeStateParked = yield* Deferred.make<void>(); | 701 | const runtimeStateParked = yield* Deferred.make<void>(); |
| 725 | const tailscaleParked = yield* Deferred.make<void>(); | 702 | const tailscaleParked = yield* Deferred.make<void>(); |
| 726 | const cloudLinkParked = yield* Deferred.make<void>(); | 703 | const cloudLinkParked = yield* Deferred.make<void>(); |
| 727 | const routesReady = yield* Deferred.make<void>(); | 704 | const routesReady = yield* Deferred.make<void>(); |
| 728 | const launcherLayer = ServiceLauncherClient.layer; | 705 | const layerLauncher = ServiceLauncherClient.layer; |
| 729 | 706 | ||
| 730 | yield* fixPath(); | 707 | yield* fixPath(); |
| 731 | 708 | ||
| 732 | const httpListeningLayer = Layer.effectDiscard( | 709 | const layerHttpListening = Layer.effectDiscard( |
| 733 | Effect.gen(function* () { | 710 | Effect.gen(function* () { |
| 734 | yield* HttpServer.HttpServer; | 711 | yield* HttpServer.HttpServer; |
| 735 | const startup = yield* ServerRuntimeStartup.ServerRuntimeStartup; | 712 | const startup = yield* ServerRuntimeStartup.ServerRuntimeStartup; |
| 736 | yield* startup.markHttpListening; | 713 | yield* startup.markHttpListening; |
| 737 | }), | 714 | }), |
| 738 | ); | 715 | ); |
| 739 | const runtimeStateLayer = Layer.effectDiscard( | 716 | const layerRuntimeState = Layer.effectDiscard( |
| 740 | Effect.acquireRelease( | 717 | Effect.acquireRelease( |
| 741 | Effect.gen(function* () { | 718 | Effect.gen(function* () { |
| 742 | yield* Deferred.succeed(runtimeStateParked, undefined).pipe(Effect.orDie); | 719 | yield* Deferred.succeed(runtimeStateParked, undefined).pipe(Effect.orDie); |
| ... | @@ -770,7 +747,7 @@ const makeServerLayer = Layer.unwrap( | ... | @@ -770,7 +747,7 @@ const makeServerLayer = Layer.unwrap( |
| 770 | ), | 747 | ), |
| 771 | ), | 748 | ), |
| 772 | ); | 749 | ); |
| 773 | const tailscaleServeLayer = config.tailscaleServeEnabled | 750 | const layerTailscaleServe = config.tailscaleServeEnabled |
| 774 | ? Layer.effectDiscard( | 751 | ? Layer.effectDiscard( |
| 775 | Effect.acquireRelease( | 752 | Effect.acquireRelease( |
| 776 | Effect.gen(function* () { | 753 | Effect.gen(function* () { |
| ... | @@ -823,7 +800,7 @@ const makeServerLayer = Layer.unwrap( | ... | @@ -823,7 +800,7 @@ const makeServerLayer = Layer.unwrap( |
| 823 | ), | 800 | ), |
| 824 | ) | 801 | ) |
| 825 | : Layer.empty; | 802 | : Layer.empty; |
| 826 | const cloudDesiredLinkReconcileLayer = Layer.effectDiscard( | 803 | const layerCloudDesiredLinkReconcile = Layer.effectDiscard( |
| 827 | Effect.gen(function* () { | 804 | Effect.gen(function* () { |
| 828 | const releaseManagedTunnel = releaseManagedTunnelOnShutdown().pipe( | 805 | const releaseManagedTunnel = releaseManagedTunnelOnShutdown().pipe( |
| 829 | Effect.timeout("10 seconds"), | 806 | Effect.timeout("10 seconds"), |
| ... | @@ -1037,7 +1014,7 @@ const makeServerLayer = Layer.unwrap( | ... | @@ -1037,7 +1014,7 @@ const makeServerLayer = Layer.unwrap( |
| 1037 | }), | 1014 | }), |
| 1038 | ); | 1015 | ); |
| 1039 | 1016 | ||
| 1040 | const runtimeServicesLive = ServerRuntimeStartup.layerWithOptions({ | 1017 | const layerRuntimeServices = ServerRuntimeStartup.layerWithOptions({ |
| 1041 | activate: Deferred.succeed(activation, undefined).pipe(Effect.asVoid), | 1018 | activate: Deferred.succeed(activation, undefined).pipe(Effect.asVoid), |
| 1042 | abort: (error) => Deferred.die(activation, error).pipe(Effect.asVoid), | 1019 | abort: (error) => Deferred.die(activation, error).pipe(Effect.asVoid), |
| 1043 | awaitAuxiliaryParked: Effect.all( | 1020 | awaitAuxiliaryParked: Effect.all( |
| ... | @@ -1049,42 +1026,42 @@ const makeServerLayer = Layer.unwrap( | ... | @@ -1049,42 +1026,42 @@ const makeServerLayer = Layer.unwrap( |
| 1049 | ], | 1026 | ], |
| 1050 | { concurrency: "unbounded" }, | 1027 | { concurrency: "unbounded" }, |
| 1051 | ).pipe(Effect.asVoid), | 1028 | ).pipe(Effect.asVoid), |
| 1052 | }).pipe(Layer.provideMerge(RuntimeDependenciesLive), Layer.provide(launcherLayer)); | 1029 | }).pipe(Layer.provideMerge(layerRuntimeDependencies), Layer.provide(layerLauncher)); |
| 1053 | 1030 | ||
| 1054 | const routesLayer = HttpRouter.serve(makeRoutesLayer.pipe(Layer.provide(launcherLayer)), { | 1031 | const layerRoutes = HttpRouter.serve(layerMakeRoutes.pipe(Layer.provide(layerLauncher)), { |
| 1055 | disableLogger: !config.logWebSocketEvents, | 1032 | disableLogger: !config.logWebSocketEvents, |
| 1056 | routerConfig: HTTP_ROUTER_CONFIG, | 1033 | routerConfig: HTTP_ROUTER_CONFIG, |
| 1057 | }).pipe( | 1034 | }).pipe( |
| 1058 | withUntracedRequests, | 1035 | withUntracedRequests, |
| 1059 | Layer.tap(() => Deferred.succeed(routesReady, undefined).pipe(Effect.orDie)), | 1036 | Layer.tap(() => Deferred.succeed(routesReady, undefined).pipe(Effect.orDie)), |
| 1060 | ); | 1037 | ); |
| 1061 | const serverApplicationLayer = Layer.mergeAll( | 1038 | const layerServerApplication = Layer.mergeAll( |
| 1062 | routesLayer, | 1039 | layerRoutes, |
| 1063 | httpListeningLayer, | 1040 | layerHttpListening, |
| 1064 | runtimeStateLayer.pipe(Layer.provide(launcherLayer)), | 1041 | layerRuntimeState.pipe(Layer.provide(layerLauncher)), |
| 1065 | tailscaleServeLayer, | 1042 | layerTailscaleServe, |
| 1066 | cloudDesiredLinkReconcileLayer, | 1043 | layerCloudDesiredLinkReconcile, |
| 1067 | HeapSnapshot.layer, | 1044 | HeapSnapshot.layer, |
| 1068 | ); | 1045 | ); |
| 1069 | 1046 | ||
| 1070 | return serverApplicationLayer.pipe( | 1047 | return layerServerApplication.pipe( |
| 1071 | Layer.provideMerge(runtimeServicesLive), | 1048 | Layer.provideMerge(layerRuntimeServices), |
| 1072 | Layer.provideMerge( | 1049 | Layer.provideMerge( |
| 1073 | McpSessionRegistry.layer.pipe( | 1050 | McpSessionRegistry.layer.pipe( |
| 1074 | Layer.provide(ServerEnvironment.layer.pipe(Layer.provide(ServerSecretStore.layer))), | 1051 | Layer.provide(ServerEnvironment.layer.pipe(Layer.provide(ServerSecretStore.layer))), |
| 1075 | ), | 1052 | ), |
| 1076 | ), | 1053 | ), |
| 1077 | Layer.provide(activationLayer), | 1054 | Layer.provide(layerActivation), |
| 1078 | Layer.provideMerge(serverRelayBrokerTracingLayer), | 1055 | Layer.provideMerge(RelayTracing.layerServerRelayBroker), |
| 1079 | Layer.provideMerge(HttpServerLive), | 1056 | Layer.provideMerge(layerHttpServer), |
| 1080 | Layer.provide(ApplicationObservabilityLive), | 1057 | Layer.provide(layerApplicationObservability), |
| 1081 | Layer.provideMerge(FetchHttpClient.layer), | 1058 | Layer.provideMerge(FetchHttpClient.layer), |
| 1082 | // PR reads, Git operations, and WebSocket discovery share one process limiter. | 1059 | // PR reads, Git operations, and WebSocket discovery share one process limiter. |
| 1083 | Layer.provide(VcsProcess.layer), | 1060 | Layer.provide(VcsProcess.layer), |
| 1084 | Layer.provideMerge(PlatformServicesLive), | 1061 | Layer.provideMerge(layerPlatformServices), |
| 1085 | ); | 1062 | ); |
| 1086 | }), | 1063 | }), |
| 1087 | ); | 1064 | ); |
| 1088 | 1065 | ||
| 1089 | // The CLI supplies configuration. | 1066 | // The CLI supplies configuration. |
| 1090 | export const runServer = Layer.launch(makeServerLayer); | 1067 | export const runServer = Layer.launch(layerMakeServer); |
apps/server/src/serverLogger.test.ts+11-11| ... | @@ -13,7 +13,7 @@ import { DEFAULT_SIGNAL_EXPORT } from "@t3tools/shared/observability"; | ... | @@ -13,7 +13,7 @@ import { DEFAULT_SIGNAL_EXPORT } from "@t3tools/shared/observability"; |
| 13 | import * as OtelEnvironment from "@t3tools/shared/otelEnvironment"; | 13 | import * as OtelEnvironment from "@t3tools/shared/otelEnvironment"; |
| 14 | 14 | ||
| 15 | import * as ServerConfig from "./config.ts"; | 15 | import * as ServerConfig from "./config.ts"; |
| 16 | import { ServerLoggerLive } from "./serverLogger.ts"; | 16 | import * as ServerLogger from "./serverLogger.ts"; |
| 17 | 17 | ||
| 18 | interface ExportedRequest { | 18 | interface ExportedRequest { |
| 19 | readonly url: string; | 19 | readonly url: string; |
| ... | @@ -22,7 +22,7 @@ interface ExportedRequest { | ... | @@ -22,7 +22,7 @@ interface ExportedRequest { |
| 22 | } | 22 | } |
| 23 | 23 | ||
| 24 | /** Answers every export with a 200 and keeps what was posted for assertions. */ | 24 | /** Answers every export with a 200 and keeps what was posted for assertions. */ |
| 25 | const collectorLayer = (requests: Array<ExportedRequest>) => | 25 | const layerCollector = (requests: Array<ExportedRequest>) => |
| 26 | Layer.succeed( | 26 | Layer.succeed( |
| 27 | HttpClient.HttpClient, | 27 | HttpClient.HttpClient, |
| 28 | HttpClient.make((request) => | 28 | HttpClient.make((request) => |
| ... | @@ -38,7 +38,7 @@ const collectorLayer = (requests: Array<ExportedRequest>) => | ... | @@ -38,7 +38,7 @@ const collectorLayer = (requests: Array<ExportedRequest>) => |
| 38 | ), | 38 | ), |
| 39 | ); | 39 | ); |
| 40 | 40 | ||
| 41 | const configLayer = (overrides: Partial<ServerConfig.ServerConfig["Service"]>) => | 41 | const layerConfig = (overrides: Partial<ServerConfig.ServerConfig["Service"]>) => |
| 42 | Layer.effect( | 42 | Layer.effect( |
| 43 | ServerConfig.ServerConfig, | 43 | ServerConfig.ServerConfig, |
| 44 | Effect.gen(function* () { | 44 | Effect.gen(function* () { |
| ... | @@ -93,9 +93,9 @@ const logThrough = (overrides: Partial<ServerConfig.ServerConfig["Service"]>) => | ... | @@ -93,9 +93,9 @@ const logThrough = (overrides: Partial<ServerConfig.ServerConfig["Service"]>) => |
| 93 | const requests: Array<ExportedRequest> = []; | 93 | const requests: Array<ExportedRequest> = []; |
| 94 | yield* Effect.log("server logger under test").pipe( | 94 | yield* Effect.log("server logger under test").pipe( |
| 95 | Effect.provide( | 95 | Effect.provide( |
| 96 | ServerLoggerLive.pipe( | 96 | ServerLogger.layer.pipe( |
| 97 | Layer.provide(configLayer(overrides)), | 97 | Layer.provide(layerConfig(overrides)), |
| 98 | Layer.provide(collectorLayer(requests)), | 98 | Layer.provide(layerCollector(requests)), |
| 99 | ), | 99 | ), |
| 100 | ), | 100 | ), |
| 101 | ); | 101 | ); |
| ... | @@ -110,7 +110,7 @@ const logInSpanThrough = (overrides: Partial<ServerConfig.ServerConfig["Service" | ... | @@ -110,7 +110,7 @@ const logInSpanThrough = (overrides: Partial<ServerConfig.ServerConfig["Service" |
| 110 | Effect.gen(function* () { | 110 | Effect.gen(function* () { |
| 111 | const requests: Array<ExportedRequest> = []; | 111 | const requests: Array<ExportedRequest> = []; |
| 112 | const spans: Array<Tracer.NativeSpan> = []; | 112 | const spans: Array<Tracer.NativeSpan> = []; |
| 113 | const tracerLayer = Layer.succeed( | 113 | const layerTracer = Layer.succeed( |
| 114 | Tracer.Tracer, | 114 | Tracer.Tracer, |
| 115 | Tracer.make({ | 115 | Tracer.make({ |
| 116 | span: (spanOptions) => { | 116 | span: (spanOptions) => { |
| ... | @@ -124,11 +124,11 @@ const logInSpanThrough = (overrides: Partial<ServerConfig.ServerConfig["Service" | ... | @@ -124,11 +124,11 @@ const logInSpanThrough = (overrides: Partial<ServerConfig.ServerConfig["Service" |
| 124 | Effect.withSpan("server-logger-test"), | 124 | Effect.withSpan("server-logger-test"), |
| 125 | Effect.provide( | 125 | Effect.provide( |
| 126 | Layer.mergeAll( | 126 | Layer.mergeAll( |
| 127 | ServerLoggerLive.pipe( | 127 | ServerLogger.layer.pipe( |
| 128 | Layer.provide(configLayer(overrides)), | 128 | Layer.provide(layerConfig(overrides)), |
| 129 | Layer.provide(collectorLayer(requests)), | 129 | Layer.provide(layerCollector(requests)), |
| 130 | ), | 130 | ), |
| 131 | tracerLayer, | 131 | layerTracer, |
| 132 | ), | 132 | ), |
| 133 | ), | 133 | ), |
| 134 | ); | 134 | ); |
apps/server/src/serverLogger.ts+6-6| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | import { otlpSerializationLayer } from "@t3tools/shared/observability"; | 1 | import * as SharedObservability from "@t3tools/shared/observability"; |
| 2 | import * as Effect from "effect/Effect"; | 2 | import * as Effect from "effect/Effect"; |
| 3 | import * as Layer from "effect/Layer"; | 3 | import * as Layer from "effect/Layer"; |
| 4 | import * as Logger from "effect/Logger"; | 4 | import * as Logger from "effect/Logger"; |
| ... | @@ -8,9 +8,9 @@ import * as OtlpLogger from "effect/observability/OtlpLogger"; | ... | @@ -8,9 +8,9 @@ import * as OtlpLogger from "effect/observability/OtlpLogger"; |
| 8 | 8 | ||
| 9 | import * as ServerConfig from "./config.ts"; | 9 | import * as ServerConfig from "./config.ts"; |
| 10 | 10 | ||
| 11 | export const ServerLoggerLive = Effect.gen(function* () { | 11 | export const layer = Effect.gen(function* () { |
| 12 | const config = yield* ServerConfig.ServerConfig; | 12 | const config = yield* ServerConfig.ServerConfig; |
| 13 | const minimumLogLevelLayer = Layer.succeed(References.MinimumLogLevel, config.logLevel); | 13 | const layerMinimumLogLevel = Layer.succeed(References.MinimumLogLevel, config.logLevel); |
| 14 | 14 | ||
| 15 | const logs = config.otlpLogsExport; | 15 | const logs = config.otlpLogsExport; |
| 16 | const otlpLogger = | 16 | const otlpLogger = |
| ... | @@ -35,15 +35,15 @@ export const ServerLoggerLive = Effect.gen(function* () { | ... | @@ -35,15 +35,15 @@ export const ServerLoggerLive = Effect.gen(function* () { |
| 35 | // Recording events on spans is also the shape OpenTelemetry is deprecating, | 35 | // Recording events on spans is also the shape OpenTelemetry is deprecating, |
| 36 | // in favor of the log-based events this logger emits: | 36 | // in favor of the log-based events this logger emits: |
| 37 | // https://opentelemetry.io/blog/2026/deprecating-span-events/ | 37 | // https://opentelemetry.io/blog/2026/deprecating-span-events/ |
| 38 | const loggerLayer = Logger.layer( | 38 | const layerLogger = Logger.layer( |
| 39 | otlpLogger === undefined | 39 | otlpLogger === undefined |
| 40 | ? [Logger.consolePretty(), Logger.tracerLogger] | 40 | ? [Logger.consolePretty(), Logger.tracerLogger] |
| 41 | : [Logger.consolePretty(), otlpLogger], | 41 | : [Logger.consolePretty(), otlpLogger], |
| 42 | { mergeWithExisting: false }, | 42 | { mergeWithExisting: false }, |
| 43 | ).pipe( | 43 | ).pipe( |
| 44 | Layer.provide(OtlpExporter.layerFlusher), | 44 | Layer.provide(OtlpExporter.layerFlusher), |
| 45 | Layer.provide(otlpSerializationLayer(logs.protocol)), | 45 | Layer.provide(SharedObservability.layerOtlpSerialization(logs.protocol)), |
| 46 | ); | 46 | ); |
| 47 | 47 | ||
| 48 | return Layer.mergeAll(loggerLayer, minimumLogLevelLayer); | 48 | return Layer.mergeAll(layerLogger, layerMinimumLogLevel); |
| 49 | }).pipe(Layer.unwrap); | 49 | }).pipe(Layer.unwrap); |
apps/server/src/serverSettings.test.ts+71-71| ... | @@ -27,7 +27,7 @@ import * as TestClock from "effect/testing/TestClock"; | ... | @@ -27,7 +27,7 @@ import * as TestClock from "effect/testing/TestClock"; |
| 27 | import * as SqlClient from "effect/sql/SqlClient"; | 27 | import * as SqlClient from "effect/sql/SqlClient"; |
| 28 | import * as ServerSecretStore from "./auth/ServerSecretStore.ts"; | 28 | import * as ServerSecretStore from "./auth/ServerSecretStore.ts"; |
| 29 | import * as ServerConfig from "./config.ts"; | 29 | import * as ServerConfig from "./config.ts"; |
| 30 | import { SqlitePersistenceMemory } from "./persistence/Layers/Sqlite.ts"; | 30 | import * as SqlitePersistence from "./persistence/Layers/Sqlite.ts"; |
| 31 | import { writeFileStringAtomically } from "./atomicWrite.ts"; | 31 | import { writeFileStringAtomically } from "./atomicWrite.ts"; |
| 32 | import * as ServerSettingsModule from "./serverSettings.ts"; | 32 | import * as ServerSettingsModule from "./serverSettings.ts"; |
| 33 | import { resolveProviderInstanceTerminalEnvironment } from "./terminal/Manager.ts"; | 33 | import { resolveProviderInstanceTerminalEnvironment } from "./terminal/Manager.ts"; |
| ... | @@ -36,10 +36,10 @@ const decodeSettingsPatch = Schema.decodeUnknownEffect(ServerSettingsPatch); | ... | @@ -36,10 +36,10 @@ const decodeSettingsPatch = Schema.decodeUnknownEffect(ServerSettingsPatch); |
| 36 | const decodeServerSettings = Schema.decodeUnknownEffect(ServerSettings); | 36 | const decodeServerSettings = Schema.decodeUnknownEffect(ServerSettings); |
| 37 | const decodeServerSettingsJson = Schema.decodeUnknownEffect(Schema.fromJsonString(ServerSettings)); | 37 | const decodeServerSettingsJson = Schema.decodeUnknownEffect(Schema.fromJsonString(ServerSettings)); |
| 38 | 38 | ||
| 39 | const makeServerSettingsLayer = () => | 39 | const layerServerSettings = () => |
| 40 | ServerSettingsModule.layer.pipe( | 40 | ServerSettingsModule.layer.pipe( |
| 41 | Layer.provide(ServerSecretStore.layer), | 41 | Layer.provide(ServerSecretStore.layer), |
| 42 | Layer.provideMerge(Layer.fresh(SqlitePersistenceMemory)), | 42 | Layer.provideMerge(Layer.fresh(SqlitePersistence.layerMemory)), |
| 43 | Layer.provideMerge( | 43 | Layer.provideMerge( |
| 44 | Layer.fresh( | 44 | Layer.fresh( |
| 45 | ServerConfig.layerTest(process.cwd(), { | 45 | ServerConfig.layerTest(process.cwd(), { |
| ... | @@ -50,10 +50,10 @@ const makeServerSettingsLayer = () => | ... | @@ -50,10 +50,10 @@ const makeServerSettingsLayer = () => |
| 50 | ); | 50 | ); |
| 51 | 51 | ||
| 52 | /** Like `makeServerSettingsLayer`, but also exposes the secret store for assertions. */ | 52 | /** Like `makeServerSettingsLayer`, but also exposes the secret store for assertions. */ |
| 53 | const makeServerSettingsLayerWithSecrets = () => | 53 | const layerServerSettingsWithSecrets = () => |
| 54 | ServerSettingsModule.layer.pipe( | 54 | ServerSettingsModule.layer.pipe( |
| 55 | Layer.provideMerge(ServerSecretStore.layer), | 55 | Layer.provideMerge(ServerSecretStore.layer), |
| 56 | Layer.provideMerge(Layer.fresh(SqlitePersistenceMemory)), | 56 | Layer.provideMerge(Layer.fresh(SqlitePersistence.layerMemory)), |
| 57 | Layer.provideMerge( | 57 | Layer.provideMerge( |
| 58 | Layer.fresh( | 58 | Layer.fresh( |
| 59 | ServerConfig.layerTest(process.cwd(), { | 59 | ServerConfig.layerTest(process.cwd(), { |
| ... | @@ -63,7 +63,7 @@ const makeServerSettingsLayerWithSecrets = () => | ... | @@ -63,7 +63,7 @@ const makeServerSettingsLayerWithSecrets = () => |
| 63 | ), | 63 | ), |
| 64 | ); | 64 | ); |
| 65 | 65 | ||
| 66 | const makeFailingSecretStoreLayer = (cause: ServerSecretStore.SecretStoreError) => | 66 | const layerFailingSecretStore = (cause: ServerSecretStore.SecretStoreError) => |
| 67 | Layer.succeed( | 67 | Layer.succeed( |
| 68 | ServerSecretStore.ServerSecretStore, | 68 | ServerSecretStore.ServerSecretStore, |
| 69 | ServerSecretStore.ServerSecretStore.of({ | 69 | ServerSecretStore.ServerSecretStore.of({ |
| ... | @@ -130,7 +130,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { | ... | @@ -130,7 +130,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { |
| 130 | ); | 130 | ); |
| 131 | assert.equal(persisted.responseStreamingMode, "turn"); | 131 | assert.equal(persisted.responseStreamingMode, "turn"); |
| 132 | assert.deepEqual(persisted.projectSettingsOverrides, settings.projectSettingsOverrides); | 132 | assert.deepEqual(persisted.projectSettingsOverrides, settings.projectSettingsOverrides); |
| 133 | }).pipe(Effect.provide(makeServerSettingsLayer())), | 133 | }).pipe(Effect.provide(layerServerSettings())), |
| 134 | ); | 134 | ); |
| 135 | 135 | ||
| 136 | it.effect("saves through a symlinked settings file without replacing the link", () => | 136 | it.effect("saves through a symlinked settings file without replacing the link", () => |
| ... | @@ -152,7 +152,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { | ... | @@ -152,7 +152,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { |
| 152 | yield* fs.readFileString(linkedSettingsPath), | 152 | yield* fs.readFileString(linkedSettingsPath), |
| 153 | ); | 153 | ); |
| 154 | assert.equal(persisted.responseStreamingMode, "paragraph"); | 154 | assert.equal(persisted.responseStreamingMode, "paragraph"); |
| 155 | }).pipe(Effect.provide(makeServerSettingsLayer())), | 155 | }).pipe(Effect.provide(layerServerSettings())), |
| 156 | ); | 156 | ); |
| 157 | 157 | ||
| 158 | it.effect("reloads when the destination of a symlinked settings file changes", () => | 158 | it.effect("reloads when the destination of a symlinked settings file changes", () => |
| ... | @@ -178,7 +178,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { | ... | @@ -178,7 +178,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { |
| 178 | const change = yield* changes.pipe(Stream.runHead, Effect.timeout("2 seconds")); | 178 | const change = yield* changes.pipe(Stream.runHead, Effect.timeout("2 seconds")); |
| 179 | assert.equal(Option.getOrUndefined(change)?.responseStreamingMode, "paragraph"); | 179 | assert.equal(Option.getOrUndefined(change)?.responseStreamingMode, "paragraph"); |
| 180 | }), | 180 | }), |
| 181 | ).pipe(TestClock.withLive, Effect.provide(makeServerSettingsLayer())), | 181 | ).pipe(TestClock.withLive, Effect.provide(layerServerSettings())), |
| 182 | ); | 182 | ); |
| 183 | 183 | ||
| 184 | it.effect("follows a settings link that is repointed to another directory", () => | 184 | it.effect("follows a settings link that is repointed to another directory", () => |
| ... | @@ -213,7 +213,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { | ... | @@ -213,7 +213,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { |
| 213 | const edited = yield* editChanges.pipe(Stream.runHead, Effect.timeout("2 seconds")); | 213 | const edited = yield* editChanges.pipe(Stream.runHead, Effect.timeout("2 seconds")); |
| 214 | assert.equal(Option.getOrUndefined(edited)?.responseStreamingMode, "turn"); | 214 | assert.equal(Option.getOrUndefined(edited)?.responseStreamingMode, "turn"); |
| 215 | }), | 215 | }), |
| 216 | ).pipe(TestClock.withLive, Effect.provide(makeServerSettingsLayer())), | 216 | ).pipe(TestClock.withLive, Effect.provide(layerServerSettings())), |
| 217 | ); | 217 | ); |
| 218 | 218 | ||
| 219 | it.effect("reloads when a dangling settings link gets its destination", () => | 219 | it.effect("reloads when a dangling settings link gets its destination", () => |
| ... | @@ -238,7 +238,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { | ... | @@ -238,7 +238,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { |
| 238 | const change = yield* changes.pipe(Stream.runHead, Effect.timeout("2 seconds")); | 238 | const change = yield* changes.pipe(Stream.runHead, Effect.timeout("2 seconds")); |
| 239 | assert.equal(Option.getOrUndefined(change)?.responseStreamingMode, "paragraph"); | 239 | assert.equal(Option.getOrUndefined(change)?.responseStreamingMode, "paragraph"); |
| 240 | }), | 240 | }), |
| 241 | ).pipe(TestClock.withLive, Effect.provide(makeServerSettingsLayer())), | 241 | ).pipe(TestClock.withLive, Effect.provide(layerServerSettings())), |
| 242 | ); | 242 | ); |
| 243 | 243 | ||
| 244 | it.effect("preserves context when reading a provider environment secret fails", () => { | 244 | it.effect("preserves context when reading a provider environment secret fails", () => { |
| ... | @@ -253,15 +253,15 @@ it.layer(NodeServices.layer)("server settings", (it) => { | ... | @@ -253,15 +253,15 @@ it.layer(NodeServices.layer)("server settings", (it) => { |
| 253 | resource: "provider environment secret", | 253 | resource: "provider environment secret", |
| 254 | cause: platformCause, | 254 | cause: platformCause, |
| 255 | }); | 255 | }); |
| 256 | const configLayer = Layer.fresh( | 256 | const layerConfig = Layer.fresh( |
| 257 | ServerConfig.layerTest(process.cwd(), { | 257 | ServerConfig.layerTest(process.cwd(), { |
| 258 | prefix: "t3code-server-settings-secret-failure-test-", | 258 | prefix: "t3code-server-settings-secret-failure-test-", |
| 259 | }), | 259 | }), |
| 260 | ); | 260 | ); |
| 261 | const settingsLayer = ServerSettingsModule.layer.pipe( | 261 | const layerSettings = ServerSettingsModule.layer.pipe( |
| 262 | Layer.provide(makeFailingSecretStoreLayer(cause)), | 262 | Layer.provide(layerFailingSecretStore(cause)), |
| 263 | Layer.provideMerge(Layer.fresh(SqlitePersistenceMemory)), | 263 | Layer.provideMerge(Layer.fresh(SqlitePersistence.layerMemory)), |
| 264 | Layer.provideMerge(configLayer), | 264 | Layer.provideMerge(layerConfig), |
| 265 | ); | 265 | ); |
| 266 | 266 | ||
| 267 | return Effect.gen(function* () { | 267 | return Effect.gen(function* () { |
| ... | @@ -283,7 +283,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { | ... | @@ -283,7 +283,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { |
| 283 | }); | 283 | }); |
| 284 | assert.strictEqual(error.cause, cause); | 284 | assert.strictEqual(error.cause, cause); |
| 285 | assert.notInclude(error.message, cause.message); | 285 | assert.notInclude(error.message, cause.message); |
| 286 | }).pipe(Effect.provide(settingsLayer)); | 286 | }).pipe(Effect.provide(layerSettings)); |
| 287 | }); | 287 | }); |
| 288 | 288 | ||
| 289 | it.effect("identifies provider history query failures", () => | 289 | it.effect("identifies provider history query failures", () => |
| ... | @@ -300,7 +300,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { | ... | @@ -300,7 +300,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { |
| 300 | operation: "read-provider-history", | 300 | operation: "read-provider-history", |
| 301 | settingsPath: serverConfig.settingsPath, | 301 | settingsPath: serverConfig.settingsPath, |
| 302 | }); | 302 | }); |
| 303 | }).pipe(Effect.provide(makeServerSettingsLayer())), | 303 | }).pipe(Effect.provide(layerServerSettings())), |
| 304 | ); | 304 | ); |
| 305 | 305 | ||
| 306 | it.effect("decodes nested settings patches", () => | 306 | it.effect("decodes nested settings patches", () => |
| ... | @@ -414,7 +414,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { | ... | @@ -414,7 +414,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { |
| 414 | ], | 414 | ], |
| 415 | ), | 415 | ), |
| 416 | ); | 416 | ); |
| 417 | }).pipe(Effect.provide(makeServerSettingsLayer())), | 417 | }).pipe(Effect.provide(layerServerSettings())), |
| 418 | ); | 418 | ); |
| 419 | 419 | ||
| 420 | it.effect("creates provider instances atomically without overwriting a concurrent add", () => | 420 | it.effect("creates provider instances atomically without overwriting a concurrent add", () => |
| ... | @@ -445,7 +445,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { | ... | @@ -445,7 +445,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { |
| 445 | (yield* serverSettings.getSettings).providerInstances[instanceId]?.displayName ?? "", | 445 | (yield* serverSettings.getSettings).providerInstances[instanceId]?.displayName ?? "", |
| 446 | ), | 446 | ), |
| 447 | ); | 447 | ); |
| 448 | }).pipe(Effect.provide(makeServerSettingsLayer())), | 448 | }).pipe(Effect.provide(layerServerSettings())), |
| 449 | ); | 449 | ); |
| 450 | 450 | ||
| 451 | it.effect("pauses provider-instance mutations while a settings snapshot is in use", () => | 451 | it.effect("pauses provider-instance mutations while a settings snapshot is in use", () => |
| ... | @@ -489,7 +489,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { | ... | @@ -489,7 +489,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { |
| 489 | (yield* serverSettings.getSettings).providerInstances[instanceId]?.displayName, | 489 | (yield* serverSettings.getSettings).providerInstances[instanceId]?.displayName, |
| 490 | "Kilo", | 490 | "Kilo", |
| 491 | ); | 491 | ); |
| 492 | }).pipe(Effect.provide(makeServerSettingsLayer())), | 492 | }).pipe(Effect.provide(layerServerSettings())), |
| 493 | ); | 493 | ); |
| 494 | 494 | ||
| 495 | it.effect("buffers changes after a subscription is acquired but before it is consumed", () => | 495 | it.effect("buffers changes after a subscription is acquired but before it is consumed", () => |
| ... | @@ -512,7 +512,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { | ... | @@ -512,7 +512,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { |
| 512 | "/usr/local/bin/codex-next", | 512 | "/usr/local/bin/codex-next", |
| 513 | ); | 513 | ); |
| 514 | }), | 514 | }), |
| 515 | ).pipe(Effect.provide(makeServerSettingsLayer())), | 515 | ).pipe(Effect.provide(layerServerSettings())), |
| 516 | ); | 516 | ); |
| 517 | 517 | ||
| 518 | it.effect("persists custom usage prices and removes them from the settings file", () => | 518 | it.effect("persists custom usage prices and removes them from the settings file", () => |
| ... | @@ -538,7 +538,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { | ... | @@ -538,7 +538,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { |
| 538 | const restored = yield* readPersisted; | 538 | const restored = yield* readPersisted; |
| 539 | assert.deepStrictEqual(restored.usagePriceOverrides, {}); | 539 | assert.deepStrictEqual(restored.usagePriceOverrides, {}); |
| 540 | }), | 540 | }), |
| 541 | ).pipe(Effect.provide(makeServerSettingsLayer())), | 541 | ).pipe(Effect.provide(layerServerSettings())), |
| 542 | ); | 542 | ); |
| 543 | 543 | ||
| 544 | it.effect("persists and broadcasts thread settlement settings", () => | 544 | it.effect("persists and broadcasts thread settlement settings", () => |
| ... | @@ -566,7 +566,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { | ... | @@ -566,7 +566,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { |
| 566 | assert.strictEqual(persisted.sidebarAutoSettleAfterDays, null); | 566 | assert.strictEqual(persisted.sidebarAutoSettleAfterDays, null); |
| 567 | assert.isFalse(persisted.sidebarAutoSettleOnMerge); | 567 | assert.isFalse(persisted.sidebarAutoSettleOnMerge); |
| 568 | }), | 568 | }), |
| 569 | ).pipe(Effect.provide(makeServerSettingsLayer())), | 569 | ).pipe(Effect.provide(layerServerSettings())), |
| 570 | ); | 570 | ); |
| 571 | 571 | ||
| 572 | it.effect("preserves model when switching providers via textGenerationModelSelection", () => | 572 | it.effect("preserves model when switching providers via textGenerationModelSelection", () => |
| ... | @@ -604,7 +604,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { | ... | @@ -604,7 +604,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { |
| 604 | { id: "reasoningEffort", value: "high" }, | 604 | { id: "reasoningEffort", value: "high" }, |
| 605 | ]), | 605 | ]), |
| 606 | ); | 606 | ); |
| 607 | }).pipe(Effect.provide(makeServerSettingsLayer())), | 607 | }).pipe(Effect.provide(layerServerSettings())), |
| 608 | ); | 608 | ); |
| 609 | 609 | ||
| 610 | it.effect("preserves custom provider instance text generation selections", () => | 610 | it.effect("preserves custom provider instance text generation selections", () => |
| ... | @@ -629,7 +629,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { | ... | @@ -629,7 +629,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { |
| 629 | instanceId: ProviderInstanceId.make("claude_openrouter"), | 629 | instanceId: ProviderInstanceId.make("claude_openrouter"), |
| 630 | model: "openai/gpt-5.5", | 630 | model: "openai/gpt-5.5", |
| 631 | }); | 631 | }); |
| 632 | }).pipe(Effect.provide(makeServerSettingsLayer())), | 632 | }).pipe(Effect.provide(layerServerSettings())), |
| 633 | ); | 633 | ); |
| 634 | 634 | ||
| 635 | it.effect( | 635 | it.effect( |
| ... | @@ -662,7 +662,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { | ... | @@ -662,7 +662,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { |
| 662 | instanceId, | 662 | instanceId, |
| 663 | model: "openai/gpt-5.5", | 663 | model: "openai/gpt-5.5", |
| 664 | }); | 664 | }); |
| 665 | }).pipe(Effect.provide(makeServerSettingsLayer())), | 665 | }).pipe(Effect.provide(layerServerSettings())), |
| 666 | ); | 666 | ); |
| 667 | 667 | ||
| 668 | it.effect("preserves enabled text generation selections for non-built-in drivers", () => | 668 | it.effect("preserves enabled text generation selections for non-built-in drivers", () => |
| ... | @@ -688,7 +688,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { | ... | @@ -688,7 +688,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { |
| 688 | instanceId, | 688 | instanceId, |
| 689 | model: "openai/gpt-5.5", | 689 | model: "openai/gpt-5.5", |
| 690 | }); | 690 | }); |
| 691 | }).pipe(Effect.provide(makeServerSettingsLayer())), | 691 | }).pipe(Effect.provide(layerServerSettings())), |
| 692 | ); | 692 | ); |
| 693 | 693 | ||
| 694 | it.effect( | 694 | it.effect( |
| ... | @@ -755,7 +755,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { | ... | @@ -755,7 +755,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { |
| 755 | ServerSettingsModule.resolveSourceControlWriterModelSelection(restored), | 755 | ServerSettingsModule.resolveSourceControlWriterModelSelection(restored), |
| 756 | sourceControlWriterModelSelection, | 756 | sourceControlWriterModelSelection, |
| 757 | ); | 757 | ); |
| 758 | }).pipe(Effect.provide(makeServerSettingsLayer())), | 758 | }).pipe(Effect.provide(layerServerSettings())), |
| 759 | ); | 759 | ); |
| 760 | 760 | ||
| 761 | it.effect("drops stale text generation options when resetting model selection", () => | 761 | it.effect("drops stale text generation options when resetting model selection", () => |
| ... | @@ -788,7 +788,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { | ... | @@ -788,7 +788,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { |
| 788 | instanceId: DEFAULT_SERVER_SETTINGS.textGenerationModelSelection.instanceId, | 788 | instanceId: DEFAULT_SERVER_SETTINGS.textGenerationModelSelection.instanceId, |
| 789 | model: DEFAULT_SERVER_SETTINGS.textGenerationModelSelection.model, | 789 | model: DEFAULT_SERVER_SETTINGS.textGenerationModelSelection.model, |
| 790 | }); | 790 | }); |
| 791 | }).pipe(Effect.provide(makeServerSettingsLayer())), | 791 | }).pipe(Effect.provide(layerServerSettings())), |
| 792 | ); | 792 | ); |
| 793 | 793 | ||
| 794 | it.effect("replaces provider instance maps when clearing optional fields", () => | 794 | it.effect("replaces provider instance maps when clearing optional fields", () => |
| ... | @@ -825,7 +825,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { | ... | @@ -825,7 +825,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { |
| 825 | enabled: true, | 825 | enabled: true, |
| 826 | config: { homePath: "~/.codex" }, | 826 | config: { homePath: "~/.codex" }, |
| 827 | }); | 827 | }); |
| 828 | }).pipe(Effect.provide(makeServerSettingsLayer())), | 828 | }).pipe(Effect.provide(layerServerSettings())), |
| 829 | ); | 829 | ); |
| 830 | 830 | ||
| 831 | it.effect("enables previously used providers from sparse settings files", () => | 831 | it.effect("enables previously used providers from sparse settings files", () => |
| ... | @@ -845,7 +845,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { | ... | @@ -845,7 +845,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { |
| 845 | assert.isTrue(settings.providers.opencode.enabled); | 845 | assert.isTrue(settings.providers.opencode.enabled); |
| 846 | assert.isFalse(settings.providers.cursor.enabled); | 846 | assert.isFalse(settings.providers.cursor.enabled); |
| 847 | assert.equal(settings.providers.opencode.serverUrl, "http://127.0.0.1:4096"); | 847 | assert.equal(settings.providers.opencode.serverUrl, "http://127.0.0.1:4096"); |
| 848 | }).pipe(Effect.provide(makeServerSettingsLayer())), | 848 | }).pipe(Effect.provide(layerServerSettings())), |
| 849 | ); | 849 | ); |
| 850 | 850 | ||
| 851 | it.effect("preserves existing provider instances without explicit enabled flags", () => | 851 | it.effect("preserves existing provider instances without explicit enabled flags", () => |
| ... | @@ -870,7 +870,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { | ... | @@ -870,7 +870,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { |
| 870 | const unused = settings.providerInstances[ProviderInstanceId.make("opencode_unused")]; | 870 | const unused = settings.providerInstances[ProviderInstanceId.make("opencode_unused")]; |
| 871 | assert.isDefined(unused); | 871 | assert.isDefined(unused); |
| 872 | assert.isFalse(resolveProviderInstanceEnabled(unused)); | 872 | assert.isFalse(resolveProviderInstanceEnabled(unused)); |
| 873 | }).pipe(Effect.provide(makeServerSettingsLayer())), | 873 | }).pipe(Effect.provide(layerServerSettings())), |
| 874 | ); | 874 | ); |
| 875 | 875 | ||
| 876 | it.effect("preserves explicit provider disables in existing settings files", () => | 876 | it.effect("preserves explicit provider disables in existing settings files", () => |
| ... | @@ -894,7 +894,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { | ... | @@ -894,7 +894,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { |
| 894 | assert.isFalse(settings.providerInstances[ProviderInstanceId.make("grok")]?.enabled); | 894 | assert.isFalse(settings.providerInstances[ProviderInstanceId.make("grok")]?.enabled); |
| 895 | assert.isFalse(settings.providerInstances[ProviderInstanceId.make("opencode")]?.enabled); | 895 | assert.isFalse(settings.providerInstances[ProviderInstanceId.make("opencode")]?.enabled); |
| 896 | assert.isFalse(settings.providerInstances[ProviderInstanceId.make("cursor")]?.enabled); | 896 | assert.isFalse(settings.providerInstances[ProviderInstanceId.make("cursor")]?.enabled); |
| 897 | }).pipe(Effect.provide(makeServerSettingsLayer())), | 897 | }).pipe(Effect.provide(layerServerSettings())), |
| 898 | ); | 898 | ); |
| 899 | 899 | ||
| 900 | it.effect("skips a disabled provider instance when picking the text generation fallback", () => | 900 | it.effect("skips a disabled provider instance when picking the text generation fallback", () => |
| ... | @@ -912,7 +912,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { | ... | @@ -912,7 +912,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { |
| 912 | const settings = yield* serverSettings.getSettings; | 912 | const settings = yield* serverSettings.getSettings; |
| 913 | 913 | ||
| 914 | assert.equal(settings.textGenerationModelSelection.instanceId, "claudeAgent"); | 914 | assert.equal(settings.textGenerationModelSelection.instanceId, "claudeAgent"); |
| 915 | }).pipe(Effect.provide(makeServerSettingsLayer())), | 915 | }).pipe(Effect.provide(layerServerSettings())), |
| 916 | ); | 916 | ); |
| 917 | 917 | ||
| 918 | it.effect("keeps unused providers disabled in existing sparse settings files", () => | 918 | it.effect("keeps unused providers disabled in existing sparse settings files", () => |
| ... | @@ -927,7 +927,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { | ... | @@ -927,7 +927,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { |
| 927 | assert.isFalse(settings.providers.grok.enabled); | 927 | assert.isFalse(settings.providers.grok.enabled); |
| 928 | assert.isFalse(settings.providers.opencode.enabled); | 928 | assert.isFalse(settings.providers.opencode.enabled); |
| 929 | assert.isFalse(settings.providers.cursor.enabled); | 929 | assert.isFalse(settings.providers.cursor.enabled); |
| 930 | }).pipe(Effect.provide(makeServerSettingsLayer())), | 930 | }).pipe(Effect.provide(layerServerSettings())), |
| 931 | ); | 931 | ); |
| 932 | 932 | ||
| 933 | it.effect("preserves provider history when no settings file exists", () => | 933 | it.effect("preserves provider history when no settings file exists", () => |
| ... | @@ -940,7 +940,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { | ... | @@ -940,7 +940,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { |
| 940 | assert.isTrue(settings.providers.grok.enabled); | 940 | assert.isTrue(settings.providers.grok.enabled); |
| 941 | assert.isFalse(settings.providers.opencode.enabled); | 941 | assert.isFalse(settings.providers.opencode.enabled); |
| 942 | assert.isFalse(settings.providers.cursor.enabled); | 942 | assert.isFalse(settings.providers.cursor.enabled); |
| 943 | }).pipe(Effect.provide(makeServerSettingsLayer())), | 943 | }).pipe(Effect.provide(layerServerSettings())), |
| 944 | ); | 944 | ); |
| 945 | 945 | ||
| 946 | it.effect("preserves provider history when the settings file is invalid", () => | 946 | it.effect("preserves provider history when the settings file is invalid", () => |
| ... | @@ -956,7 +956,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { | ... | @@ -956,7 +956,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { |
| 956 | assert.isTrue(settings.providers.cursor.enabled); | 956 | assert.isTrue(settings.providers.cursor.enabled); |
| 957 | assert.isFalse(settings.providers.grok.enabled); | 957 | assert.isFalse(settings.providers.grok.enabled); |
| 958 | assert.isFalse(settings.providers.opencode.enabled); | 958 | assert.isFalse(settings.providers.opencode.enabled); |
| 959 | }).pipe(Effect.provide(makeServerSettingsLayer())), | 959 | }).pipe(Effect.provide(layerServerSettings())), |
| 960 | ); | 960 | ); |
| 961 | 961 | ||
| 962 | it.effect("preserves valid provider flags when another settings field is invalid", () => | 962 | it.effect("preserves valid provider flags when another settings field is invalid", () => |
| ... | @@ -975,7 +975,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { | ... | @@ -975,7 +975,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { |
| 975 | assert.isFalse(settings.providers.cursor.enabled); | 975 | assert.isFalse(settings.providers.cursor.enabled); |
| 976 | assert.isTrue(settings.providers.grok.enabled); | 976 | assert.isTrue(settings.providers.grok.enabled); |
| 977 | assert.isFalse(settings.providers.opencode.enabled); | 977 | assert.isFalse(settings.providers.opencode.enabled); |
| 978 | }).pipe(Effect.provide(makeServerSettingsLayer())), | 978 | }).pipe(Effect.provide(layerServerSettings())), |
| 979 | ); | 979 | ); |
| 980 | 980 | ||
| 981 | it.effect("restores providers from persisted runtime sessions", () => | 981 | it.effect("restores providers from persisted runtime sessions", () => |
| ... | @@ -1006,7 +1006,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { | ... | @@ -1006,7 +1006,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { |
| 1006 | assert.isFalse(settings.providers.grok.enabled); | 1006 | assert.isFalse(settings.providers.grok.enabled); |
| 1007 | assert.isTrue(settings.providers.opencode.enabled); | 1007 | assert.isTrue(settings.providers.opencode.enabled); |
| 1008 | assert.isFalse(settings.providers.cursor.enabled); | 1008 | assert.isFalse(settings.providers.cursor.enabled); |
| 1009 | }).pipe(Effect.provide(makeServerSettingsLayer())), | 1009 | }).pipe(Effect.provide(layerServerSettings())), |
| 1010 | ); | 1010 | ); |
| 1011 | 1011 | ||
| 1012 | it.effect("persists explicit disables after a provider has been used", () => | 1012 | it.effect("persists explicit disables after a provider has been used", () => |
| ... | @@ -1026,7 +1026,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { | ... | @@ -1026,7 +1026,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { |
| 1026 | const raw = yield* fileSystem.readFileString(serverConfig.settingsPath); | 1026 | const raw = yield* fileSystem.readFileString(serverConfig.settingsPath); |
| 1027 | // @effect-diagnostics-next-line preferSchemaOverJson:off | 1027 | // @effect-diagnostics-next-line preferSchemaOverJson:off |
| 1028 | assert.isFalse(JSON.parse(raw).providers.grok.enabled); | 1028 | assert.isFalse(JSON.parse(raw).providers.grok.enabled); |
| 1029 | }).pipe(Effect.provide(makeServerSettingsLayer())), | 1029 | }).pipe(Effect.provide(layerServerSettings())), |
| 1030 | ); | 1030 | ); |
| 1031 | 1031 | ||
| 1032 | it.effect("persists explicit provider enables before their first use", () => | 1032 | it.effect("persists explicit provider enables before their first use", () => |
| ... | @@ -1050,7 +1050,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { | ... | @@ -1050,7 +1050,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { |
| 1050 | assert.isTrue(persisted.providers.cursor.enabled); | 1050 | assert.isTrue(persisted.providers.cursor.enabled); |
| 1051 | assert.isTrue(persisted.providers.grok.enabled); | 1051 | assert.isTrue(persisted.providers.grok.enabled); |
| 1052 | assert.isTrue(persisted.providers.opencode.enabled); | 1052 | assert.isTrue(persisted.providers.opencode.enabled); |
| 1053 | }).pipe(Effect.provide(makeServerSettingsLayer())), | 1053 | }).pipe(Effect.provide(layerServerSettings())), |
| 1054 | ); | 1054 | ); |
| 1055 | 1055 | ||
| 1056 | it.effect("keeps optional providers disabled after a new installation writes settings", () => | 1056 | it.effect("keeps optional providers disabled after a new installation writes settings", () => |
| ... | @@ -1088,7 +1088,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { | ... | @@ -1088,7 +1088,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { |
| 1088 | assert.isFalse(persisted.providers.grok.enabled); | 1088 | assert.isFalse(persisted.providers.grok.enabled); |
| 1089 | assert.isFalse(persisted.providers.opencode.enabled); | 1089 | assert.isFalse(persisted.providers.opencode.enabled); |
| 1090 | assert.isUndefined(persisted.providerInstances.grok.enabled); | 1090 | assert.isUndefined(persisted.providerInstances.grok.enabled); |
| 1091 | }).pipe(Effect.provide(makeServerSettingsLayer())), | 1091 | }).pipe(Effect.provide(layerServerSettings())), |
| 1092 | ); | 1092 | ); |
| 1093 | 1093 | ||
| 1094 | it.effect("folds a legacy in-config enabled flag into the envelope on load", () => | 1094 | it.effect("folds a legacy in-config enabled flag into the envelope on load", () => |
| ... | @@ -1124,7 +1124,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { | ... | @@ -1124,7 +1124,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { |
| 1124 | driver: ProviderDriverKind.make("cursor"), | 1124 | driver: ProviderDriverKind.make("cursor"), |
| 1125 | config: { enabled: "nope" }, | 1125 | config: { enabled: "nope" }, |
| 1126 | }); | 1126 | }); |
| 1127 | }).pipe(Effect.provide(makeServerSettingsLayer())), | 1127 | }).pipe(Effect.provide(layerServerSettings())), |
| 1128 | ); | 1128 | ); |
| 1129 | 1129 | ||
| 1130 | it.effect("folds in-config enabled flags arriving through updates", () => | 1130 | it.effect("folds in-config enabled flags arriving through updates", () => |
| ... | @@ -1147,7 +1147,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { | ... | @@ -1147,7 +1147,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { |
| 1147 | enabled: false, | 1147 | enabled: false, |
| 1148 | config: { binaryPath: "/opt/grok" }, | 1148 | config: { binaryPath: "/opt/grok" }, |
| 1149 | }); | 1149 | }); |
| 1150 | }).pipe(Effect.provide(makeServerSettingsLayer())), | 1150 | }).pipe(Effect.provide(layerServerSettings())), |
| 1151 | ); | 1151 | ); |
| 1152 | 1152 | ||
| 1153 | it.effect("trims provider path settings when updates are applied", () => | 1153 | it.effect("trims provider path settings when updates are applied", () => |
| ... | @@ -1195,7 +1195,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { | ... | @@ -1195,7 +1195,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { |
| 1195 | serverPassword: "secret-password", | 1195 | serverPassword: "secret-password", |
| 1196 | customModels: [], | 1196 | customModels: [], |
| 1197 | }); | 1197 | }); |
| 1198 | }).pipe(Effect.provide(makeServerSettingsLayer())), | 1198 | }).pipe(Effect.provide(layerServerSettings())), |
| 1199 | ); | 1199 | ); |
| 1200 | 1200 | ||
| 1201 | it.effect("trims observability settings when updates are applied", () => | 1201 | it.effect("trims observability settings when updates are applied", () => |
| ... | @@ -1217,7 +1217,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { | ... | @@ -1217,7 +1217,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { |
| 1217 | otlpMetricsUrl: "http://localhost:4318/v1/metrics", | 1217 | otlpMetricsUrl: "http://localhost:4318/v1/metrics", |
| 1218 | otlpLogsUrl: "http://localhost:4318/v1/logs", | 1218 | otlpLogsUrl: "http://localhost:4318/v1/logs", |
| 1219 | }); | 1219 | }); |
| 1220 | }).pipe(Effect.provide(makeServerSettingsLayer())), | 1220 | }).pipe(Effect.provide(layerServerSettings())), |
| 1221 | ); | 1221 | ); |
| 1222 | 1222 | ||
| 1223 | it.effect("defaults blank binary paths to provider executables", () => | 1223 | it.effect("defaults blank binary paths to provider executables", () => |
| ... | @@ -1237,7 +1237,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { | ... | @@ -1237,7 +1237,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { |
| 1237 | 1237 | ||
| 1238 | assert.equal(next.providers.codex.binaryPath, "codex"); | 1238 | assert.equal(next.providers.codex.binaryPath, "codex"); |
| 1239 | assert.equal(next.providers.claudeAgent.binaryPath, "claude"); | 1239 | assert.equal(next.providers.claudeAgent.binaryPath, "claude"); |
| 1240 | }).pipe(Effect.provide(makeServerSettingsLayer())), | 1240 | }).pipe(Effect.provide(layerServerSettings())), |
| 1241 | ); | 1241 | ); |
| 1242 | 1242 | ||
| 1243 | it.effect("writes non-default settings and explicit optional provider defaults to disk", () => | 1243 | it.effect("writes non-default settings and explicit optional provider defaults to disk", () => |
| ... | @@ -1299,7 +1299,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { | ... | @@ -1299,7 +1299,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { |
| 1299 | }, | 1299 | }, |
| 1300 | automaticGitFetchInterval: 10_000, | 1300 | automaticGitFetchInterval: 10_000, |
| 1301 | }); | 1301 | }); |
| 1302 | }).pipe(Effect.provide(makeServerSettingsLayer())), | 1302 | }).pipe(Effect.provide(layerServerSettings())), |
| 1303 | ); | 1303 | ); |
| 1304 | 1304 | ||
| 1305 | it.effect("keeps the inline value on disk when secret migration fails", () => { | 1305 | it.effect("keeps the inline value on disk when secret migration fails", () => { |
| ... | @@ -1307,16 +1307,16 @@ it.layer(NodeServices.layer)("server settings", (it) => { | ... | @@ -1307,16 +1307,16 @@ it.layer(NodeServices.layer)("server settings", (it) => { |
| 1307 | resource: "provider environment secret", | 1307 | resource: "provider environment secret", |
| 1308 | cause: new Error("Secret storage unavailable"), | 1308 | cause: new Error("Secret storage unavailable"), |
| 1309 | }); | 1309 | }); |
| 1310 | const secretLayer = Layer.effect( | 1310 | const layerSecret = Layer.effect( |
| 1311 | ServerSecretStore.ServerSecretStore, | 1311 | ServerSecretStore.ServerSecretStore, |
| 1312 | Effect.map(ServerSecretStore.ServerSecretStore, (store) => ({ | 1312 | Effect.map(ServerSecretStore.ServerSecretStore, (store) => ({ |
| 1313 | ...store, | 1313 | ...store, |
| 1314 | set: () => Effect.fail(cause), | 1314 | set: () => Effect.fail(cause), |
| 1315 | })), | 1315 | })), |
| 1316 | ).pipe(Layer.provide(ServerSecretStore.layer)); | 1316 | ).pipe(Layer.provide(ServerSecretStore.layer)); |
| 1317 | const settingsLayer = ServerSettingsModule.layer.pipe( | 1317 | const layerSettings = ServerSettingsModule.layer.pipe( |
| 1318 | Layer.provide(secretLayer), | 1318 | Layer.provide(layerSecret), |
| 1319 | Layer.provideMerge(Layer.fresh(SqlitePersistenceMemory)), | 1319 | Layer.provideMerge(Layer.fresh(SqlitePersistence.layerMemory)), |
| 1320 | Layer.provideMerge( | 1320 | Layer.provideMerge( |
| 1321 | Layer.fresh( | 1321 | Layer.fresh( |
| 1322 | ServerConfig.layerTest(process.cwd(), { | 1322 | ServerConfig.layerTest(process.cwd(), { |
| ... | @@ -1352,7 +1352,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { | ... | @@ -1352,7 +1352,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { |
| 1352 | settings.providerInstances[instanceId]?.environment?.[0]?.value, | 1352 | settings.providerInstances[instanceId]?.environment?.[0]?.value, |
| 1353 | "inline-test-token", | 1353 | "inline-test-token", |
| 1354 | ); | 1354 | ); |
| 1355 | }).pipe(Effect.provide(settingsLayer)); | 1355 | }).pipe(Effect.provide(layerSettings)); |
| 1356 | }); | 1356 | }); |
| 1357 | 1357 | ||
| 1358 | it.effect.each( | 1358 | it.effect.each( |
| ... | @@ -1421,7 +1421,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { | ... | @@ -1421,7 +1421,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { |
| 1421 | ), | 1421 | ), |
| 1422 | ); | 1422 | ); |
| 1423 | assert.equal(reloaded.providerInstances[instanceId]?.environment?.[0]?.value, expected); | 1423 | assert.equal(reloaded.providerInstances[instanceId]?.environment?.[0]?.value, expected); |
| 1424 | }).pipe(Effect.provide(makeServerSettingsLayer())), | 1424 | }).pipe(Effect.provide(layerServerSettings())), |
| 1425 | ); | 1425 | ); |
| 1426 | 1426 | ||
| 1427 | it.effect.each([true, false])( | 1427 | it.effect.each([true, false])( |
| ... | @@ -1449,7 +1449,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { | ... | @@ -1449,7 +1449,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { |
| 1449 | next.providerInstances[instanceId]?.environment?.find((v) => v.sensitive)?.value, | 1449 | next.providerInstances[instanceId]?.environment?.find((v) => v.sensitive)?.value, |
| 1450 | sensitiveLast ? "secret-last" : "", | 1450 | sensitiveLast ? "secret-last" : "", |
| 1451 | ); | 1451 | ); |
| 1452 | }).pipe(Effect.provide(makeServerSettingsLayer())), | 1452 | }).pipe(Effect.provide(layerServerSettings())), |
| 1453 | ); | 1453 | ); |
| 1454 | 1454 | ||
| 1455 | it.effect("stores sensitive provider instance environment values outside settings.json", () => | 1455 | it.effect("stores sensitive provider instance environment values outside settings.json", () => |
| ... | @@ -1513,7 +1513,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { | ... | @@ -1513,7 +1513,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { |
| 1513 | roundTripped.providerInstances[instanceId]?.environment?.[0]?.value, | 1513 | roundTripped.providerInstances[instanceId]?.environment?.[0]?.value, |
| 1514 | "sk-or-secret", | 1514 | "sk-or-secret", |
| 1515 | ); | 1515 | ); |
| 1516 | }).pipe(Effect.provide(makeServerSettingsLayer())), | 1516 | }).pipe(Effect.provide(layerServerSettings())), |
| 1517 | ); | 1517 | ); |
| 1518 | 1518 | ||
| 1519 | it.effect( | 1519 | it.effect( |
| ... | @@ -1563,7 +1563,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { | ... | @@ -1563,7 +1563,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { |
| 1563 | ServerSettingsModule.redactServerSettingsForClient(cleared).bitbucket.accessToken, | 1563 | ServerSettingsModule.redactServerSettingsForClient(cleared).bitbucket.accessToken, |
| 1564 | "", | 1564 | "", |
| 1565 | ); | 1565 | ); |
| 1566 | }).pipe(Effect.provide(makeServerSettingsLayerWithSecrets())), | 1566 | }).pipe(Effect.provide(layerServerSettingsWithSecrets())), |
| 1567 | ); | 1567 | ); |
| 1568 | 1568 | ||
| 1569 | it.effect("removes a Bitbucket secret once its token is cleared by hand in settings.json", () => | 1569 | it.effect("removes a Bitbucket secret once its token is cleared by hand in settings.json", () => |
| ... | @@ -1579,7 +1579,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { | ... | @@ -1579,7 +1579,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { |
| 1579 | yield* serverSettings.updateSettings({ cursorKeychainUsageEnabled: true }); | 1579 | yield* serverSettings.updateSettings({ cursorKeychainUsageEnabled: true }); |
| 1580 | 1580 | ||
| 1581 | assert.isTrue(Option.isNone(yield* secrets.get("bitbucket-access-token"))); | 1581 | assert.isTrue(Option.isNone(yield* secrets.get("bitbucket-access-token"))); |
| 1582 | }).pipe(Effect.provide(makeServerSettingsLayerWithSecrets())), | 1582 | }).pipe(Effect.provide(layerServerSettingsWithSecrets())), |
| 1583 | ); | 1583 | ); |
| 1584 | 1584 | ||
| 1585 | it.effect("moves a hand-edited Bitbucket token into the secret store when settings load", () => | 1585 | it.effect("moves a hand-edited Bitbucket token into the secret store when settings load", () => |
| ... | @@ -1606,7 +1606,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { | ... | @@ -1606,7 +1606,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { |
| 1606 | Option.isSome(stored) ? new TextDecoder().decode(stored.value) : null, | 1606 | Option.isSome(stored) ? new TextDecoder().decode(stored.value) : null, |
| 1607 | "hand-edited-token", | 1607 | "hand-edited-token", |
| 1608 | ); | 1608 | ); |
| 1609 | }).pipe(Effect.provide(makeServerSettingsLayerWithSecrets())), | 1609 | }).pipe(Effect.provide(layerServerSettingsWithSecrets())), |
| 1610 | ); | 1610 | ); |
| 1611 | 1611 | ||
| 1612 | it.effect( | 1612 | it.effect( |
| ... | @@ -1635,7 +1635,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { | ... | @@ -1635,7 +1635,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { |
| 1635 | yield* fileSystem.readFileString(serverConfig.settingsPath), | 1635 | yield* fileSystem.readFileString(serverConfig.settingsPath), |
| 1636 | "hand-edited-token", | 1636 | "hand-edited-token", |
| 1637 | ); | 1637 | ); |
| 1638 | }).pipe(Effect.provide(makeServerSettingsLayer())), | 1638 | }).pipe(Effect.provide(layerServerSettings())), |
| 1639 | ); | 1639 | ); |
| 1640 | 1640 | ||
| 1641 | it.effect("materializes provider secrets for terminal environment resolution", () => | 1641 | it.effect("materializes provider secrets for terminal environment resolution", () => |
| ... | @@ -1670,7 +1670,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { | ... | @@ -1670,7 +1670,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { |
| 1670 | assert.match(environment.CODEX_HOME ?? "", /[\\/][.]codex-terminal$/); | 1670 | assert.match(environment.CODEX_HOME ?? "", /[\\/][.]codex-terminal$/); |
| 1671 | assert.notInclude(persisted, "sk-terminal-secret"); | 1671 | assert.notInclude(persisted, "sk-terminal-secret"); |
| 1672 | assert.include(persisted, '"valueRedacted": true'); | 1672 | assert.include(persisted, '"valueRedacted": true'); |
| 1673 | }).pipe(Effect.provide(makeServerSettingsLayer())), | 1673 | }).pipe(Effect.provide(layerServerSettings())), |
| 1674 | ); | 1674 | ); |
| 1675 | it.effect("rolls back provider secret changes when the settings file commit fails", () => | 1675 | it.effect("rolls back provider secret changes when the settings file commit fails", () => |
| 1676 | Effect.gen(function* () { | 1676 | Effect.gen(function* () { |
| ... | @@ -1691,7 +1691,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { | ... | @@ -1691,7 +1691,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { |
| 1691 | : fileSystem.rename(fromPath, toPath), | 1691 | : fileSystem.rename(fromPath, toPath), |
| 1692 | }); | 1692 | }); |
| 1693 | const instanceId = ProviderInstanceId.make("codex_write_failure"); | 1693 | const instanceId = ProviderInstanceId.make("codex_write_failure"); |
| 1694 | const settingsLayer = makeServerSettingsLayer().pipe( | 1694 | const layerSettings = layerServerSettings().pipe( |
| 1695 | Layer.provideMerge(Layer.succeed(FileSystem.FileSystem, failingFileSystem)), | 1695 | Layer.provideMerge(Layer.succeed(FileSystem.FileSystem, failingFileSystem)), |
| 1696 | ); | 1696 | ); |
| 1697 | 1697 | ||
| ... | @@ -1736,7 +1736,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { | ... | @@ -1736,7 +1736,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { |
| 1736 | ?.value, | 1736 | ?.value, |
| 1737 | "sk-kept", | 1737 | "sk-kept", |
| 1738 | ); | 1738 | ); |
| 1739 | }).pipe(Effect.provide(settingsLayer)); | 1739 | }).pipe(Effect.provide(layerSettings)); |
| 1740 | }), | 1740 | }), |
| 1741 | ); | 1741 | ); |
| 1742 | 1742 | ||
| ... | @@ -1746,7 +1746,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { | ... | @@ -1746,7 +1746,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { |
| 1746 | const textDecoder = new TextDecoder(); | 1746 | const textDecoder = new TextDecoder(); |
| 1747 | const secrets = new Map<string, Uint8Array>(); | 1747 | const secrets = new Map<string, Uint8Array>(); |
| 1748 | let rejectNewSecret = false; | 1748 | let rejectNewSecret = false; |
| 1749 | const secretStoreLayer = Layer.succeed( | 1749 | const layerSecretStore = Layer.succeed( |
| 1750 | ServerSecretStore.ServerSecretStore, | 1750 | ServerSecretStore.ServerSecretStore, |
| 1751 | ServerSecretStore.ServerSecretStore.of({ | 1751 | ServerSecretStore.ServerSecretStore.of({ |
| 1752 | get: (name) => | 1752 | get: (name) => |
| ... | @@ -1799,9 +1799,9 @@ it.layer(NodeServices.layer)("server settings", (it) => { | ... | @@ -1799,9 +1799,9 @@ it.layer(NodeServices.layer)("server settings", (it) => { |
| 1799 | }), | 1799 | }), |
| 1800 | }), | 1800 | }), |
| 1801 | ); | 1801 | ); |
| 1802 | const settingsLayer = ServerSettingsModule.layer.pipe( | 1802 | const layerSettings = ServerSettingsModule.layer.pipe( |
| 1803 | Layer.provideMerge(Layer.fresh(SqlitePersistenceMemory)), | 1803 | Layer.provideMerge(Layer.fresh(SqlitePersistence.layerMemory)), |
| 1804 | Layer.provide(secretStoreLayer), | 1804 | Layer.provide(layerSecretStore), |
| 1805 | Layer.provideMerge( | 1805 | Layer.provideMerge( |
| 1806 | Layer.fresh( | 1806 | Layer.fresh( |
| 1807 | ServerConfig.layerTest(process.cwd(), { | 1807 | ServerConfig.layerTest(process.cwd(), { |
| ... | @@ -1844,7 +1844,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { | ... | @@ -1844,7 +1844,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { |
| 1844 | ?.value, | 1844 | ?.value, |
| 1845 | "sk-kept", | 1845 | "sk-kept", |
| 1846 | ); | 1846 | ); |
| 1847 | }).pipe(Effect.provide(settingsLayer)); | 1847 | }).pipe(Effect.provide(layerSettings)); |
| 1848 | }, | 1848 | }, |
| 1849 | ); | 1849 | ); |
| 1850 | 1850 | ||
| ... | @@ -1928,7 +1928,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { | ... | @@ -1928,7 +1928,7 @@ it.layer(NodeServices.layer)("server settings", (it) => { |
| 1928 | ); | 1928 | ); |
| 1929 | assert.isTrue(persisted.projectSettingsFolded); | 1929 | assert.isTrue(persisted.projectSettingsFolded); |
| 1930 | assert.isUndefined(persisted.projectSettingsOverrides[legacyProject]); | 1930 | assert.isUndefined(persisted.projectSettingsOverrides[legacyProject]); |
| 1931 | }).pipe(Effect.provide(makeServerSettingsLayer())), | 1931 | }).pipe(Effect.provide(layerServerSettings())), |
| 1932 | ); | 1932 | ); |
| 1933 | 1933 | ||
| 1934 | it.effect("leaves an unreadable settings.json untouched instead of folding over it", () => | 1934 | it.effect("leaves an unreadable settings.json untouched instead of folding over it", () => |
| ... | @@ -1954,6 +1954,6 @@ it.layer(NodeServices.layer)("server settings", (it) => { | ... | @@ -1954,6 +1954,6 @@ it.layer(NodeServices.layer)("server settings", (it) => { |
| 1954 | assert.deepEqual(settings.projectSettingsOverrides, {}); | 1954 | assert.deepEqual(settings.projectSettingsOverrides, {}); |
| 1955 | // The user's file is still there to repair; nothing was written over it. | 1955 | // The user's file is still there to repair; nothing was written over it. |
| 1956 | assert.equal(yield* fileSystem.readFileString(serverConfig.settingsPath), broken); | 1956 | assert.equal(yield* fileSystem.readFileString(serverConfig.settingsPath), broken); |
| 1957 | }).pipe(Effect.provide(makeServerSettingsLayer())), | 1957 | }).pipe(Effect.provide(layerServerSettings())), |
| 1958 | ); | 1958 | ); |
| 1959 | }); | 1959 | }); |
apps/server/src/sourceControl/AzureDevOpsCli.test.ts+2-2| ... | @@ -21,13 +21,13 @@ const processOutput = (stdout: string): VcsProcess.VcsProcessOutput => ({ | ... | @@ -21,13 +21,13 @@ const processOutput = (stdout: string): VcsProcess.VcsProcessOutput => ({ |
| 21 | 21 | ||
| 22 | const mockRun = vi.fn<VcsProcess.VcsProcess["Service"]["run"]>(); | 22 | const mockRun = vi.fn<VcsProcess.VcsProcess["Service"]["run"]>(); |
| 23 | 23 | ||
| 24 | const supportLayer = Layer.mergeAll( | 24 | const layerSupport = Layer.mergeAll( |
| 25 | Layer.mock(VcsProcess.VcsProcess)({ | 25 | Layer.mock(VcsProcess.VcsProcess)({ |
| 26 | run: mockRun, | 26 | run: mockRun, |
| 27 | }), | 27 | }), |
| 28 | NodeServices.layer, | 28 | NodeServices.layer, |
| 29 | ); | 29 | ); |
| 30 | const layer = Layer.mergeAll(AzureDevOpsCli.layer.pipe(Layer.provide(supportLayer)), supportLayer); | 30 | const layer = Layer.mergeAll(AzureDevOpsCli.layer.pipe(Layer.provide(layerSupport)), layerSupport); |
| 31 | 31 | ||
| 32 | afterEach(() => { | 32 | afterEach(() => { |
| 33 | mockRun.mockReset(); | 33 | mockRun.mockReset(); |
apps/server/src/sourceControl/PrTemplateDetection.test.ts+2-2| ... | @@ -27,7 +27,7 @@ const TEMPLATE_DIRECTORIES = [ | ... | @@ -27,7 +27,7 @@ const TEMPLATE_DIRECTORIES = [ |
| 27 | "docs/PULL_REQUEST_TEMPLATE", | 27 | "docs/PULL_REQUEST_TEMPLATE", |
| 28 | ] as const; | 28 | ] as const; |
| 29 | 29 | ||
| 30 | const PrTemplateDetectionTestLayer = GitVcsDriver.layer.pipe( | 30 | const layerPrTemplateDetectionTest = GitVcsDriver.layer.pipe( |
| 31 | Layer.provide( | 31 | Layer.provide( |
| 32 | ServerConfig.layerTest(process.cwd(), { | 32 | ServerConfig.layerTest(process.cwd(), { |
| 33 | prefix: "t3-pr-template-test-", | 33 | prefix: "t3-pr-template-test-", |
| ... | @@ -59,7 +59,7 @@ const runWithTempDirectory = <A, E, R>( | ... | @@ -59,7 +59,7 @@ const runWithTempDirectory = <A, E, R>( |
| 59 | yield* runGit(cwd, ["config", "user.name", "Test User"]); | 59 | yield* runGit(cwd, ["config", "user.name", "Test User"]); |
| 60 | return yield* test(cwd); | 60 | return yield* test(cwd); |
| 61 | }), | 61 | }), |
| 62 | ).pipe(Effect.provide(PrTemplateDetectionTestLayer)); | 62 | ).pipe(Effect.provide(layerPrTemplateDetectionTest)); |
| 63 | 63 | ||
| 64 | const writeTemplate = (cwd: string, relativePath: string, contents: string) => | 64 | const writeTemplate = (cwd: string, relativePath: string, contents: string) => |
| 65 | Effect.gen(function* () { | 65 | Effect.gen(function* () { |
apps/server/src/sourceControl/SourceControlDiscovery.test.ts+7-7| ... | @@ -25,7 +25,7 @@ import * as SourceControlDiscovery from "./SourceControlDiscovery.ts"; | ... | @@ -25,7 +25,7 @@ import * as SourceControlDiscovery from "./SourceControlDiscovery.ts"; |
| 25 | import * as SourceControlProviderRegistry from "./SourceControlProviderRegistry.ts"; | 25 | import * as SourceControlProviderRegistry from "./SourceControlProviderRegistry.ts"; |
| 26 | import { firstNonEmptyLine } from "./SourceControlProviderDiscovery.ts"; | 26 | import { firstNonEmptyLine } from "./SourceControlProviderDiscovery.ts"; |
| 27 | 27 | ||
| 28 | const sourceControlProviderRegistryTestLayer = (input: { | 28 | const layerSourceControlProviderRegistryTest = (input: { |
| 29 | readonly bitbucket: Partial<BitbucketApi.BitbucketApi["Service"]>; | 29 | readonly bitbucket: Partial<BitbucketApi.BitbucketApi["Service"]>; |
| 30 | readonly process: Partial<VcsProcess.VcsProcess["Service"]>; | 30 | readonly process: Partial<VcsProcess.VcsProcess["Service"]>; |
| 31 | }) => | 31 | }) => |
| ... | @@ -530,7 +530,7 @@ it.effect("reports implemented tools separately from locally available executabl | ... | @@ -530,7 +530,7 @@ it.effect("reports implemented tools separately from locally available executabl |
| 530 | ); | 530 | ); |
| 531 | }, | 531 | }, |
| 532 | } satisfies Partial<VcsProcess.VcsProcess["Service"]>; | 532 | } satisfies Partial<VcsProcess.VcsProcess["Service"]>; |
| 533 | const testLayer = SourceControlDiscovery.layer.pipe( | 533 | const layerTest = SourceControlDiscovery.layer.pipe( |
| 534 | Layer.provide( | 534 | Layer.provide( |
| 535 | ServerConfig.layerTest(process.cwd(), { | 535 | ServerConfig.layerTest(process.cwd(), { |
| 536 | prefix: "t3-source-control-discovery-", | 536 | prefix: "t3-source-control-discovery-", |
| ... | @@ -538,7 +538,7 @@ it.effect("reports implemented tools separately from locally available executabl | ... | @@ -538,7 +538,7 @@ it.effect("reports implemented tools separately from locally available executabl |
| 538 | ), | 538 | ), |
| 539 | Layer.provide(Layer.mock(VcsProcess.VcsProcess)(processMock)), | 539 | Layer.provide(Layer.mock(VcsProcess.VcsProcess)(processMock)), |
| 540 | Layer.provide( | 540 | Layer.provide( |
| 541 | sourceControlProviderRegistryTestLayer({ | 541 | layerSourceControlProviderRegistryTest({ |
| 542 | process: processMock, | 542 | process: processMock, |
| 543 | bitbucket: { | 543 | bitbucket: { |
| 544 | probeAuth: Effect.succeed({ | 544 | probeAuth: Effect.succeed({ |
| ... | @@ -613,7 +613,7 @@ it.effect("reports implemented tools separately from locally available executabl | ... | @@ -613,7 +613,7 @@ it.effect("reports implemented tools separately from locally available executabl |
| 613 | const bitbucket = result.sourceControlProviders.find((item) => item.kind === "bitbucket"); | 613 | const bitbucket = result.sourceControlProviders.find((item) => item.kind === "bitbucket"); |
| 614 | assert.ok(bitbucket); | 614 | assert.ok(bitbucket); |
| 615 | assert.strictEqual(bitbucket.executable, undefined); | 615 | assert.strictEqual(bitbucket.executable, undefined); |
| 616 | }).pipe(Effect.provide(testLayer)); | 616 | }).pipe(Effect.provide(layerTest)); |
| 617 | }); | 617 | }); |
| 618 | 618 | ||
| 619 | it.effect("probes provider authentication without exposing token details", () => { | 619 | it.effect("probes provider authentication without exposing token details", () => { |
| ... | @@ -681,7 +681,7 @@ Logged in to gitlab.com as gitlab-user | ... | @@ -681,7 +681,7 @@ Logged in to gitlab.com as gitlab-user |
| 681 | ); | 681 | ); |
| 682 | }, | 682 | }, |
| 683 | } satisfies Partial<VcsProcess.VcsProcess["Service"]>; | 683 | } satisfies Partial<VcsProcess.VcsProcess["Service"]>; |
| 684 | const testLayer = SourceControlDiscovery.layer.pipe( | 684 | const layerTest = SourceControlDiscovery.layer.pipe( |
| 685 | Layer.provide( | 685 | Layer.provide( |
| 686 | ServerConfig.layerTest(process.cwd(), { | 686 | ServerConfig.layerTest(process.cwd(), { |
| 687 | prefix: "t3-source-control-auth-discovery-", | 687 | prefix: "t3-source-control-auth-discovery-", |
| ... | @@ -689,7 +689,7 @@ Logged in to gitlab.com as gitlab-user | ... | @@ -689,7 +689,7 @@ Logged in to gitlab.com as gitlab-user |
| 689 | ), | 689 | ), |
| 690 | Layer.provide(Layer.mock(VcsProcess.VcsProcess)(processMock)), | 690 | Layer.provide(Layer.mock(VcsProcess.VcsProcess)(processMock)), |
| 691 | Layer.provide( | 691 | Layer.provide( |
| 692 | sourceControlProviderRegistryTestLayer({ | 692 | layerSourceControlProviderRegistryTest({ |
| 693 | process: processMock, | 693 | process: processMock, |
| 694 | bitbucket: { | 694 | bitbucket: { |
| 695 | probeAuth: Effect.succeed({ | 695 | probeAuth: Effect.succeed({ |
| ... | @@ -748,7 +748,7 @@ Logged in to gitlab.com as gitlab-user | ... | @@ -748,7 +748,7 @@ Logged in to gitlab.com as gitlab-user |
| 748 | }, | 748 | }, |
| 749 | ], | 749 | ], |
| 750 | ); | 750 | ); |
| 751 | }).pipe(Effect.provide(testLayer)); | 751 | }).pipe(Effect.provide(layerTest)); |
| 752 | }); | 752 | }); |
| 753 | 753 | ||
| 754 | it.effect("discovers Forgejo accounts and retains the server port", () => | 754 | it.effect("discovers Forgejo accounts and retains the server port", () => |
apps/server/src/sourceControl/SourceControlProviderRegistry.test.ts+4-4| ... | @@ -60,7 +60,7 @@ function makeRegistry(input: { | ... | @@ -60,7 +60,7 @@ function makeRegistry(input: { |
| 60 | }), | 60 | }), |
| 61 | } satisfies Partial<VcsDriver.VcsDriver["Service"]>; | 61 | } satisfies Partial<VcsDriver.VcsDriver["Service"]>; |
| 62 | 62 | ||
| 63 | const registryLayer = Layer.mock(VcsDriverRegistry.VcsDriverRegistry)({ | 63 | const layerRegistry = Layer.mock(VcsDriverRegistry.VcsDriverRegistry)({ |
| 64 | get: () => Effect.succeed(driver as unknown as VcsDriver.VcsDriver["Service"]), | 64 | get: () => Effect.succeed(driver as unknown as VcsDriver.VcsDriver["Service"]), |
| 65 | resolve: | 65 | resolve: |
| 66 | input.resolve ?? | 66 | input.resolve ?? |
| ... | @@ -81,7 +81,7 @@ function makeRegistry(input: { | ... | @@ -81,7 +81,7 @@ function makeRegistry(input: { |
| 81 | })), | 81 | })), |
| 82 | }); | 82 | }); |
| 83 | 83 | ||
| 84 | const processLayer = Layer.mock(VcsProcess.VcsProcess)({ | 84 | const layerProcess = Layer.mock(VcsProcess.VcsProcess)({ |
| 85 | run: () => Effect.succeed(processOutput("")), | 85 | run: () => Effect.succeed(processOutput("")), |
| 86 | ...input.process, | 86 | ...input.process, |
| 87 | }); | 87 | }); |
| ... | @@ -90,8 +90,8 @@ function makeRegistry(input: { | ... | @@ -90,8 +90,8 @@ function makeRegistry(input: { |
| 90 | Effect.provide( | 90 | Effect.provide( |
| 91 | Layer.mergeAll( | 91 | Layer.mergeAll( |
| 92 | NodeServices.layer, | 92 | NodeServices.layer, |
| 93 | registryLayer, | 93 | layerRegistry, |
| 94 | processLayer, | 94 | layerProcess, |
| 95 | Layer.mock(AzureDevOpsCli.AzureDevOpsCli)({}), | 95 | Layer.mock(AzureDevOpsCli.AzureDevOpsCli)({}), |
| 96 | Layer.mock(BitbucketApi.BitbucketApi)({}), | 96 | Layer.mock(BitbucketApi.BitbucketApi)({}), |
| 97 | Layer.mock(GitHubCli.GitHubCli)(input.github ?? {}), | 97 | Layer.mock(GitHubCli.GitHubCli)(input.github ?? {}), |
apps/server/src/sourceControl/SourceControlRepositoryService.test.ts+15-15| ... | @@ -54,12 +54,12 @@ function processOutput(): GitVcsDriver.ExecuteGitResult { | ... | @@ -54,12 +54,12 @@ function processOutput(): GitVcsDriver.ExecuteGitResult { |
| 54 | }; | 54 | }; |
| 55 | } | 55 | } |
| 56 | 56 | ||
| 57 | function makeLayer(input: { | 57 | function layer(input: { |
| 58 | readonly provider?: SourceControlProvider.SourceControlProvider["Service"]; | 58 | readonly provider?: SourceControlProvider.SourceControlProvider["Service"]; |
| 59 | readonly git?: Partial<GitVcsDriver.GitVcsDriver["Service"]>; | 59 | readonly git?: Partial<GitVcsDriver.GitVcsDriver["Service"]>; |
| 60 | readonly fileSystem?: FileSystem.FileSystem; | 60 | readonly fileSystem?: FileSystem.FileSystem; |
| 61 | }) { | 61 | }) { |
| 62 | const serviceLayer = SourceControlRepositoryService.layer.pipe( | 62 | const layerService = SourceControlRepositoryService.layer.pipe( |
| 63 | Layer.provide( | 63 | Layer.provide( |
| 64 | Layer.mock(SourceControlProviderRegistry.SourceControlProviderRegistry)({ | 64 | Layer.mock(SourceControlProviderRegistry.SourceControlProviderRegistry)({ |
| 65 | resolveLink: () => undefined, | 65 | resolveLink: () => undefined, |
| ... | @@ -89,11 +89,11 @@ function makeLayer(input: { | ... | @@ -89,11 +89,11 @@ function makeLayer(input: { |
| 89 | ); | 89 | ); |
| 90 | 90 | ||
| 91 | return input.fileSystem | 91 | return input.fileSystem |
| 92 | ? serviceLayer.pipe( | 92 | ? layerService.pipe( |
| 93 | Layer.provide(Layer.succeed(FileSystem.FileSystem, input.fileSystem)), | 93 | Layer.provide(Layer.succeed(FileSystem.FileSystem, input.fileSystem)), |
| 94 | Layer.provideMerge(NodePath.layer), | 94 | Layer.provideMerge(NodePath.layer), |
| 95 | ) | 95 | ) |
| 96 | : serviceLayer.pipe(Layer.provideMerge(NodeServices.layer)); | 96 | : layerService.pipe(Layer.provideMerge(NodeServices.layer)); |
| 97 | } | 97 | } |
| 98 | 98 | ||
| 99 | it.effect("looks up repositories through the requested provider without search", () => { | 99 | it.effect("looks up repositories through the requested provider without search", () => { |
| ... | @@ -116,7 +116,7 @@ it.effect("looks up repositories through the requested provider without search", | ... | @@ -116,7 +116,7 @@ it.effect("looks up repositories through the requested provider without search", |
| 116 | 116 | ||
| 117 | assert.deepStrictEqual(result, { provider: "github", ...CLONE_URLS }); | 117 | assert.deepStrictEqual(result, { provider: "github", ...CLONE_URLS }); |
| 118 | assert.deepStrictEqual(calls, [{ cwd: "/workspace", repository: "octocat/t3code" }]); | 118 | assert.deepStrictEqual(calls, [{ cwd: "/workspace", repository: "octocat/t3code" }]); |
| 119 | }).pipe(Effect.provide(makeLayer({ provider }))); | 119 | }).pipe(Effect.provide(layer({ provider }))); |
| 120 | }); | 120 | }); |
| 121 | 121 | ||
| 122 | it.effect("preserves provider failures without deriving the repository message from them", () => { | 122 | it.effect("preserves provider failures without deriving the repository message from them", () => { |
| ... | @@ -149,7 +149,7 @@ it.effect("preserves provider failures without deriving the repository message f | ... | @@ -149,7 +149,7 @@ it.effect("preserves provider failures without deriving the repository message f |
| 149 | "Source control repository operation lookupRepository failed for github: The source control operation could not be completed.", | 149 | "Source control repository operation lookupRepository failed for github: The source control operation could not be completed.", |
| 150 | ); | 150 | ); |
| 151 | assert.strictEqual(error.cause, providerCause); | 151 | assert.strictEqual(error.cause, providerCause); |
| 152 | }).pipe(Effect.provide(makeLayer({ provider }))); | 152 | }).pipe(Effect.provide(layer({ provider }))); |
| 153 | }); | 153 | }); |
| 154 | 154 | ||
| 155 | it.effect("clones a looked-up repository into the requested destination", () => | 155 | it.effect("clones a looked-up repository into the requested destination", () => |
| ... | @@ -184,7 +184,7 @@ it.effect("clones a looked-up repository into the requested destination", () => | ... | @@ -184,7 +184,7 @@ it.effect("clones a looked-up repository into the requested destination", () => |
| 184 | ]); | 184 | ]); |
| 185 | }).pipe( | 185 | }).pipe( |
| 186 | Effect.provide( | 186 | Effect.provide( |
| 187 | makeLayer({ | 187 | layer({ |
| 188 | git: { | 188 | git: { |
| 189 | execute: (input) => | 189 | execute: (input) => |
| 190 | Effect.sync(() => { | 190 | Effect.sync(() => { |
| ... | @@ -226,7 +226,7 @@ it.effect("reports clone progress from git's stderr and keeps its error text on | ... | @@ -226,7 +226,7 @@ it.effect("reports clone progress from git's stderr and keeps its error text on |
| 226 | ); | 226 | ); |
| 227 | }).pipe( | 227 | }).pipe( |
| 228 | Effect.provide( | 228 | Effect.provide( |
| 229 | makeLayer({ | 229 | layer({ |
| 230 | git: { | 230 | git: { |
| 231 | execute: (input) => | 231 | execute: (input) => |
| 232 | Effect.gen(function* () { | 232 | Effect.gen(function* () { |
| ... | @@ -274,7 +274,7 @@ it.effect("strips embedded credentials from the remote URL it reports", () => | ... | @@ -274,7 +274,7 @@ it.effect("strips embedded credentials from the remote URL it reports", () => |
| 274 | }); | 274 | }); |
| 275 | }).pipe( | 275 | }).pipe( |
| 276 | Effect.provide( | 276 | Effect.provide( |
| 277 | makeLayer({ | 277 | layer({ |
| 278 | git: { | 278 | git: { |
| 279 | execute: (input) => | 279 | execute: (input) => |
| 280 | Effect.sync(() => { | 280 | Effect.sync(() => { |
| ... | @@ -316,7 +316,7 @@ it.effect("discards only a directory git wrote to", () => | ... | @@ -316,7 +316,7 @@ it.effect("discards only a directory git wrote to", () => |
| 316 | assert.include(replacedError.detail, "could not be inspected"); | 316 | assert.include(replacedError.detail, "could not be inspected"); |
| 317 | // A destination that never got created is nothing to discard. | 317 | // A destination that never got created is nothing to discard. |
| 318 | yield* service.discardClone(path.join(parent, "missing")); | 318 | yield* service.discardClone(path.join(parent, "missing")); |
| 319 | }).pipe(Effect.provide(makeLayer({}))); | 319 | }).pipe(Effect.provide(layer({}))); |
| 320 | 320 | ||
| 321 | // The partial clone is emptied but its directory (the workspace root) stays. | 321 | // The partial clone is emptied but its directory (the workspace root) stays. |
| 322 | assert.deepStrictEqual(yield* fs.readDirectory(partial), []); | 322 | assert.deepStrictEqual(yield* fs.readDirectory(partial), []); |
| ... | @@ -342,7 +342,7 @@ it.effect("redacts query tokens and userinfo containing '@' from reported URLs", | ... | @@ -342,7 +342,7 @@ it.effect("redacts query tokens and userinfo containing '@' from reported URLs", |
| 342 | destinationPath: path.join(parent, "b"), | 342 | destinationPath: path.join(parent, "b"), |
| 343 | }); | 343 | }); |
| 344 | assert.equal(nested.remoteUrl, "https://github.com/octocat/t3code.git"); | 344 | assert.equal(nested.remoteUrl, "https://github.com/octocat/t3code.git"); |
| 345 | }).pipe(Effect.provide(makeLayer({}))); | 345 | }).pipe(Effect.provide(layer({}))); |
| 346 | }).pipe(Effect.provide(NodeServices.layer)), | 346 | }).pipe(Effect.provide(NodeServices.layer)), |
| 347 | ); | 347 | ); |
| 348 | 348 | ||
| ... | @@ -368,7 +368,7 @@ it.effect("preserves destination probe failures instead of treating them as miss | ... | @@ -368,7 +368,7 @@ it.effect("preserves destination probe failures instead of treating them as miss |
| 368 | assert.strictEqual(error.cause, fileSystemCause); | 368 | assert.strictEqual(error.cause, fileSystemCause); |
| 369 | }).pipe( | 369 | }).pipe( |
| 370 | Effect.provide( | 370 | Effect.provide( |
| 371 | makeLayer({ | 371 | layer({ |
| 372 | fileSystem: FileSystem.makeNoop({ | 372 | fileSystem: FileSystem.makeNoop({ |
| 373 | exists: () => Effect.fail(fileSystemCause), | 373 | exists: () => Effect.fail(fileSystemCause), |
| 374 | makeDirectory: () => Effect.void, | 374 | makeDirectory: () => Effect.void, |
| ... | @@ -422,7 +422,7 @@ it.effect("publishes by creating the repository, adding a remote, and pushing up | ... | @@ -422,7 +422,7 @@ it.effect("publishes by creating the repository, adding a remote, and pushing up |
| 422 | assert.deepStrictEqual(pushCalls, [{ cwd: "/workspace", remoteName: "origin" }]); | 422 | assert.deepStrictEqual(pushCalls, [{ cwd: "/workspace", remoteName: "origin" }]); |
| 423 | }).pipe( | 423 | }).pipe( |
| 424 | Effect.provide( | 424 | Effect.provide( |
| 425 | makeLayer({ | 425 | layer({ |
| 426 | provider, | 426 | provider, |
| 427 | git: { | 427 | git: { |
| 428 | ensureRemote: (input) => | 428 | ensureRemote: (input) => |
| ... | @@ -464,7 +464,7 @@ it.effect("publishes to the remote name returned by ensureRemote", () => { | ... | @@ -464,7 +464,7 @@ it.effect("publishes to the remote name returned by ensureRemote", () => { |
| 464 | assert.deepStrictEqual(pushCalls, [{ cwd: "/workspace", remoteName: "origin-1" }]); | 464 | assert.deepStrictEqual(pushCalls, [{ cwd: "/workspace", remoteName: "origin-1" }]); |
| 465 | }).pipe( | 465 | }).pipe( |
| 466 | Effect.provide( | 466 | Effect.provide( |
| 467 | makeLayer({ | 467 | layer({ |
| 468 | git: { | 468 | git: { |
| 469 | ensureRemote: () => Effect.succeed("origin-1"), | 469 | ensureRemote: () => Effect.succeed("origin-1"), |
| 470 | pushCurrentBranch: (cwd, _fallbackBranch, options) => | 470 | pushCurrentBranch: (cwd, _fallbackBranch, options) => |
| ... | @@ -506,7 +506,7 @@ it.effect("publish succeeds with status remote_added when the local repo has no | ... | @@ -506,7 +506,7 @@ it.effect("publish succeeds with status remote_added when the local repo has no |
| 506 | assert.strictEqual(pushCalls, 0); | 506 | assert.strictEqual(pushCalls, 0); |
| 507 | }).pipe( | 507 | }).pipe( |
| 508 | Effect.provide( | 508 | Effect.provide( |
| 509 | makeLayer({ | 509 | layer({ |
| 510 | git: { | 510 | git: { |
| 511 | execute: (input) => | 511 | execute: (input) => |
| 512 | input.args[0] === "rev-parse" | 512 | input.args[0] === "rev-parse" |
apps/server/src/telemetry/AnalyticsService.test.ts+20-20| ... | @@ -61,7 +61,7 @@ const SentBatch = Schema.fromJsonString( | ... | @@ -61,7 +61,7 @@ const SentBatch = Schema.fromJsonString( |
| 61 | * before the response arrived. PostHog stores these batches, so the server | 61 | * before the response arrived. PostHog stores these batches, so the server |
| 62 | * must not send them forever. | 62 | * must not send them forever. |
| 63 | */ | 63 | */ |
| 64 | const acceptThenFailClient = (batches: Array<ReadonlyArray<{ readonly uuid: string }>>) => | 64 | const layerAcceptThenFailClient = (batches: Array<ReadonlyArray<{ readonly uuid: string }>>) => |
| 65 | Layer.succeed( | 65 | Layer.succeed( |
| 66 | HttpClient.HttpClient, | 66 | HttpClient.HttpClient, |
| 67 | HttpClient.make((request) => | 67 | HttpClient.make((request) => |
| ... | @@ -90,7 +90,7 @@ it.layer(NodeServices.layer)("AnalyticsService test", (it) => { | ... | @@ -90,7 +90,7 @@ it.layer(NodeServices.layer)("AnalyticsService test", (it) => { |
| 90 | it.effect("a batch that keeps failing is retried with backoff, then dropped", () => | 90 | it.effect("a batch that keeps failing is retried with backoff, then dropped", () => |
| 91 | Effect.gen(function* () { | 91 | Effect.gen(function* () { |
| 92 | const batches: Array<ReadonlyArray<{ readonly uuid: string }>> = []; | 92 | const batches: Array<ReadonlyArray<{ readonly uuid: string }>> = []; |
| 93 | const runtimeLayer = AnalyticsService.layer.pipe( | 93 | const layerRuntime = AnalyticsService.layer.pipe( |
| 94 | Layer.provideMerge( | 94 | Layer.provideMerge( |
| 95 | ServerConfig.ServerConfig.layerTest(process.cwd(), { prefix: "t3-telemetry-retry-" }), | 95 | ServerConfig.ServerConfig.layerTest(process.cwd(), { prefix: "t3-telemetry-retry-" }), |
| 96 | ), | 96 | ), |
| ... | @@ -107,7 +107,7 @@ it.layer(NodeServices.layer)("AnalyticsService test", (it) => { | ... | @@ -107,7 +107,7 @@ it.layer(NodeServices.layer)("AnalyticsService test", (it) => { |
| 107 | Layer.mergeAll( | 107 | Layer.mergeAll( |
| 108 | Layer.succeed(HostProcessPlatform, "win32"), | 108 | Layer.succeed(HostProcessPlatform, "win32"), |
| 109 | Layer.succeed(HostProcessArchitecture, "x64"), | 109 | Layer.succeed(HostProcessArchitecture, "x64"), |
| 110 | acceptThenFailClient(batches), | 110 | layerAcceptThenFailClient(batches), |
| 111 | ), | 111 | ), |
| 112 | ), | 112 | ), |
| 113 | ); | 113 | ); |
| ... | @@ -121,7 +121,7 @@ it.layer(NodeServices.layer)("AnalyticsService test", (it) => { | ... | @@ -121,7 +121,7 @@ it.layer(NodeServices.layer)("AnalyticsService test", (it) => { |
| 121 | for (let second = 0; second < 600; second += 1) { | 121 | for (let second = 0; second < 600; second += 1) { |
| 122 | yield* TestClock.adjust("1 second"); | 122 | yield* TestClock.adjust("1 second"); |
| 123 | } | 123 | } |
| 124 | }).pipe(Effect.provide(runtimeLayer)); | 124 | }).pipe(Effect.provide(layerRuntime)); |
| 125 | 125 | ||
| 126 | assert.equal(batches.length, 5); | 126 | assert.equal(batches.length, 5); |
| 127 | const uuids = batches.map((batch) => batch.map((event) => event.uuid).join(",")); | 127 | const uuids = batches.map((batch) => batch.map((event) => event.uuid).join(",")); |
| ... | @@ -133,12 +133,12 @@ it.layer(NodeServices.layer)("AnalyticsService test", (it) => { | ... | @@ -133,12 +133,12 @@ it.layer(NodeServices.layer)("AnalyticsService test", (it) => { |
| 133 | it.effect("flush drains all buffered events across multiple batches", () => | 133 | it.effect("flush drains all buffered events across multiple batches", () => |
| 134 | Effect.gen(function* () { | 134 | Effect.gen(function* () { |
| 135 | const capturedRequests: Array<RecordedBatchRequest> = []; | 135 | const capturedRequests: Array<RecordedBatchRequest> = []; |
| 136 | const serverConfigLayer = ServerConfig.ServerConfig.layerTest(process.cwd(), { | 136 | const layerServerConfig = ServerConfig.ServerConfig.layerTest(process.cwd(), { |
| 137 | prefix: "t3-telemetry-base-", | 137 | prefix: "t3-telemetry-base-", |
| 138 | }); | 138 | }); |
| 139 | 139 | ||
| 140 | const telemetryLayer = AnalyticsService.layer.pipe(Layer.provideMerge(serverConfigLayer)); | 140 | const layerTelemetry = AnalyticsService.layer.pipe(Layer.provideMerge(layerServerConfig)); |
| 141 | const configLayer = ConfigProvider.layer( | 141 | const layerConfig = ConfigProvider.layer( |
| 142 | ConfigProvider.fromUnknown({ | 142 | ConfigProvider.fromUnknown({ |
| 143 | T3CODE_TELEMETRY_ENABLED: true, | 143 | T3CODE_TELEMETRY_ENABLED: true, |
| 144 | T3CODE_POSTHOG_KEY: "phc_test_key", | 144 | T3CODE_POSTHOG_KEY: "phc_test_key", |
| ... | @@ -146,7 +146,7 @@ it.layer(NodeServices.layer)("AnalyticsService test", (it) => { | ... | @@ -146,7 +146,7 @@ it.layer(NodeServices.layer)("AnalyticsService test", (it) => { |
| 146 | T3CODE_TELEMETRY_FLUSH_BATCH_SIZE: 20, | 146 | T3CODE_TELEMETRY_FLUSH_BATCH_SIZE: 20, |
| 147 | }), | 147 | }), |
| 148 | ); | 148 | ); |
| 149 | const batchServerLayer = HttpServer.serve( | 149 | const layerBatchServer = HttpServer.serve( |
| 150 | Effect.gen(function* () { | 150 | Effect.gen(function* () { |
| 151 | const request = yield* HttpServerRequest.HttpServerRequest; | 151 | const request = yield* HttpServerRequest.HttpServerRequest; |
| 152 | if (request.method !== "POST") { | 152 | if (request.method !== "POST") { |
| ... | @@ -163,8 +163,8 @@ it.layer(NodeServices.layer)("AnalyticsService test", (it) => { | ... | @@ -163,8 +163,8 @@ it.layer(NodeServices.layer)("AnalyticsService test", (it) => { |
| 163 | return HttpServerResponse.jsonUnsafe({}); | 163 | return HttpServerResponse.jsonUnsafe({}); |
| 164 | }), | 164 | }), |
| 165 | ); | 165 | ); |
| 166 | const runtimeLayer = telemetryLayer.pipe( | 166 | const layerRuntime = layerTelemetry.pipe( |
| 167 | Layer.provide(configLayer), | 167 | Layer.provide(layerConfig), |
| 168 | Layer.provide( | 168 | Layer.provide( |
| 169 | Layer.mergeAll( | 169 | Layer.mergeAll( |
| 170 | Layer.succeed(HostProcessPlatform, "linux"), | 170 | Layer.succeed(HostProcessPlatform, "linux"), |
| ... | @@ -175,7 +175,7 @@ it.layer(NodeServices.layer)("AnalyticsService test", (it) => { | ... | @@ -175,7 +175,7 @@ it.layer(NodeServices.layer)("AnalyticsService test", (it) => { |
| 175 | ); | 175 | ); |
| 176 | 176 | ||
| 177 | yield* Effect.gen(function* () { | 177 | yield* Effect.gen(function* () { |
| 178 | yield* Layer.launch(batchServerLayer).pipe(Effect.forkScoped); | 178 | yield* Layer.launch(layerBatchServer).pipe(Effect.forkScoped); |
| 179 | const telemetryIdentifier = yield* getTelemetryIdentifier; | 179 | const telemetryIdentifier = yield* getTelemetryIdentifier; |
| 180 | assert.equal(telemetryIdentifier !== null, true); | 180 | assert.equal(telemetryIdentifier !== null, true); |
| 181 | const analytics = yield* AnalyticsService.AnalyticsService; | 181 | const analytics = yield* AnalyticsService.AnalyticsService; |
| ... | @@ -185,7 +185,7 @@ it.layer(NodeServices.layer)("AnalyticsService test", (it) => { | ... | @@ -185,7 +185,7 @@ it.layer(NodeServices.layer)("AnalyticsService test", (it) => { |
| 185 | } | 185 | } |
| 186 | 186 | ||
| 187 | yield* analytics.flush; | 187 | yield* analytics.flush; |
| 188 | }).pipe(Effect.provide(runtimeLayer)); | 188 | }).pipe(Effect.provide(layerRuntime)); |
| 189 | 189 | ||
| 190 | const batchRequests = capturedRequests.filter( | 190 | const batchRequests = capturedRequests.filter( |
| 191 | (request): request is RecordedBatchRequest & { readonly body: RecordedBatchBody } => | 191 | (request): request is RecordedBatchRequest & { readonly body: RecordedBatchBody } => |
| ... | @@ -235,26 +235,26 @@ it.layer(NodeServices.layer)("AnalyticsService test", (it) => { | ... | @@ -235,26 +235,26 @@ it.layer(NodeServices.layer)("AnalyticsService test", (it) => { |
| 235 | it.effect("does not send batch requests when telemetry is disabled", () => | 235 | it.effect("does not send batch requests when telemetry is disabled", () => |
| 236 | Effect.gen(function* () { | 236 | Effect.gen(function* () { |
| 237 | const capturedPaths: Array<string> = []; | 237 | const capturedPaths: Array<string> = []; |
| 238 | const serverConfigLayer = ServerConfig.ServerConfig.layerTest(process.cwd(), { | 238 | const layerServerConfig = ServerConfig.ServerConfig.layerTest(process.cwd(), { |
| 239 | prefix: "t3-telemetry-disabled-", | 239 | prefix: "t3-telemetry-disabled-", |
| 240 | }); | 240 | }); |
| 241 | const telemetryLayer = AnalyticsService.layer.pipe(Layer.provideMerge(serverConfigLayer)); | 241 | const layerTelemetry = AnalyticsService.layer.pipe(Layer.provideMerge(layerServerConfig)); |
| 242 | const configLayer = ConfigProvider.layer( | 242 | const layerConfig = ConfigProvider.layer( |
| 243 | ConfigProvider.fromUnknown({ | 243 | ConfigProvider.fromUnknown({ |
| 244 | T3CODE_TELEMETRY_ENABLED: false, | 244 | T3CODE_TELEMETRY_ENABLED: false, |
| 245 | T3CODE_POSTHOG_KEY: "phc_test_key", | 245 | T3CODE_POSTHOG_KEY: "phc_test_key", |
| 246 | T3CODE_POSTHOG_HOST: "http://localhost", | 246 | T3CODE_POSTHOG_HOST: "http://localhost", |
| 247 | }), | 247 | }), |
| 248 | ); | 248 | ); |
| 249 | const batchServerLayer = HttpServer.serve( | 249 | const layerBatchServer = HttpServer.serve( |
| 250 | Effect.gen(function* () { | 250 | Effect.gen(function* () { |
| 251 | const request = yield* HttpServerRequest.HttpServerRequest; | 251 | const request = yield* HttpServerRequest.HttpServerRequest; |
| 252 | capturedPaths.push(request.url); | 252 | capturedPaths.push(request.url); |
| 253 | return HttpServerResponse.jsonUnsafe({}); | 253 | return HttpServerResponse.jsonUnsafe({}); |
| 254 | }), | 254 | }), |
| 255 | ); | 255 | ); |
| 256 | const runtimeLayer = telemetryLayer.pipe( | 256 | const layerRuntime = layerTelemetry.pipe( |
| 257 | Layer.provide(configLayer), | 257 | Layer.provide(layerConfig), |
| 258 | Layer.provide( | 258 | Layer.provide( |
| 259 | Layer.mergeAll( | 259 | Layer.mergeAll( |
| 260 | Layer.succeed(HostProcessPlatform, "linux"), | 260 | Layer.succeed(HostProcessPlatform, "linux"), |
| ... | @@ -265,11 +265,11 @@ it.layer(NodeServices.layer)("AnalyticsService test", (it) => { | ... | @@ -265,11 +265,11 @@ it.layer(NodeServices.layer)("AnalyticsService test", (it) => { |
| 265 | ); | 265 | ); |
| 266 | 266 | ||
| 267 | yield* Effect.gen(function* () { | 267 | yield* Effect.gen(function* () { |
| 268 | yield* Layer.launch(batchServerLayer).pipe(Effect.forkScoped); | 268 | yield* Layer.launch(layerBatchServer).pipe(Effect.forkScoped); |
| 269 | const analytics = yield* AnalyticsService.AnalyticsService; | 269 | const analytics = yield* AnalyticsService.AnalyticsService; |
| 270 | yield* analytics.record("test.disabled", { index: 1 }); | 270 | yield* analytics.record("test.disabled", { index: 1 }); |
| 271 | yield* analytics.flush; | 271 | yield* analytics.flush; |
| 272 | }).pipe(Effect.provide(runtimeLayer)); | 272 | }).pipe(Effect.provide(layerRuntime)); |
| 273 | 273 | ||
| 274 | assert.deepEqual(capturedPaths, []); | 274 | assert.deepEqual(capturedPaths, []); |
| 275 | }), | 275 | }), |
apps/server/src/terminal/Manager.test.ts+15-12| ... | @@ -37,7 +37,7 @@ import { expect } from "vite-plus/test"; | ... | @@ -37,7 +37,7 @@ import { expect } from "vite-plus/test"; |
| 37 | 37 | ||
| 38 | import * as ServerSecretStore from "../auth/ServerSecretStore.ts"; | 38 | import * as ServerSecretStore from "../auth/ServerSecretStore.ts"; |
| 39 | import * as ServerConfig from "../config.ts"; | 39 | import * as ServerConfig from "../config.ts"; |
| 40 | import { SqlitePersistenceMemory } from "../persistence/Layers/Sqlite.ts"; | 40 | import * as SqlitePersistence from "../persistence/Layers/Sqlite.ts"; |
| 41 | import * as ProcessRunner from "../processRunner.ts"; | 41 | import * as ProcessRunner from "../processRunner.ts"; |
| 42 | import * as ServerSettings from "../serverSettings.ts"; | 42 | import * as ServerSettings from "../serverSettings.ts"; |
| 43 | import * as TerminalManager from "./Manager.ts"; | 43 | import * as TerminalManager from "./Manager.ts"; |
| ... | @@ -314,7 +314,7 @@ const createManager = ( | ... | @@ -314,7 +314,7 @@ const createManager = ( |
| 314 | }), | 314 | }), |
| 315 | ); | 315 | ); |
| 316 | 316 | ||
| 317 | const withHostPlatform = (platform: NodeJS.Platform) => | 317 | const layerWithHostPlatform = (platform: NodeJS.Platform) => |
| 318 | Layer.succeed(HostProcessPlatform, platform); | 318 | Layer.succeed(HostProcessPlatform, platform); |
| 319 | 319 | ||
| 320 | // Apply the existing line policy, then find the longest code-point-aligned byte tail. | 320 | // Apply the existing line policy, then find the longest code-point-aligned byte tail. |
| ... | @@ -1153,7 +1153,7 @@ it.layer( | ... | @@ -1153,7 +1153,7 @@ it.layer( |
| 1153 | subprocessPollIntervalMs: 20, | 1153 | subprocessPollIntervalMs: 20, |
| 1154 | }).pipe( | 1154 | }).pipe( |
| 1155 | Effect.provideService(ProcessRunner.ProcessRunner, processRunner), | 1155 | Effect.provideService(ProcessRunner.ProcessRunner, processRunner), |
| 1156 | Effect.provide(withHostPlatform("linux")), | 1156 | Effect.provide(layerWithHostPlatform("linux")), |
| 1157 | ); | 1157 | ); |
| 1158 | 1158 | ||
| 1159 | yield* manager.open(openInput()); | 1159 | yield* manager.open(openInput()); |
| ... | @@ -1214,7 +1214,7 @@ it.layer( | ... | @@ -1214,7 +1214,7 @@ it.layer( |
| 1214 | subprocessPollIntervalMs: 20, | 1214 | subprocessPollIntervalMs: 20, |
| 1215 | }).pipe( | 1215 | }).pipe( |
| 1216 | Effect.provideService(ProcessRunner.ProcessRunner, processRunner), | 1216 | Effect.provideService(ProcessRunner.ProcessRunner, processRunner), |
| 1217 | Effect.provide(withHostPlatform("linux")), | 1217 | Effect.provide(layerWithHostPlatform("linux")), |
| 1218 | ); | 1218 | ); |
| 1219 | 1219 | ||
| 1220 | yield* manager.open(openInput()); | 1220 | yield* manager.open(openInput()); |
| ... | @@ -1259,7 +1259,7 @@ it.layer( | ... | @@ -1259,7 +1259,7 @@ it.layer( |
| 1259 | snapshotCalls += 1; | 1259 | snapshotCalls += 1; |
| 1260 | return [{ pid: 100, ppid: 9000, name: "ping.exe" }]; | 1260 | return [{ pid: 100, ppid: 9000, name: "ping.exe" }]; |
| 1261 | }), | 1261 | }), |
| 1262 | }).pipe(Effect.provide(withHostPlatform("win32"))); | 1262 | }).pipe(Effect.provide(layerWithHostPlatform("win32"))); |
| 1263 | 1263 | ||
| 1264 | yield* manager.open(openInput()); | 1264 | yield* manager.open(openInput()); |
| 1265 | yield* waitFor( | 1265 | yield* waitFor( |
| ... | @@ -1291,7 +1291,7 @@ it.layer( | ... | @@ -1291,7 +1291,7 @@ it.layer( |
| 1291 | { pid: 301, ppid: 300, name: "sleep" }, | 1291 | { pid: 301, ppid: 300, name: "sleep" }, |
| 1292 | { pid: 9003, ppid: 1, name: "zsh" }, | 1292 | { pid: 9003, ppid: 1, name: "zsh" }, |
| 1293 | ]), | 1293 | ]), |
| 1294 | }).pipe(Effect.provide(withHostPlatform("linux"))); | 1294 | }).pipe(Effect.provide(layerWithHostPlatform("linux"))); |
| 1295 | yield* manager.open(openInput({ terminalId: "idle" })); | 1295 | yield* manager.open(openInput({ terminalId: "idle" })); |
| 1296 | yield* manager.open(openInput({ terminalId: "dev-server" })); | 1296 | yield* manager.open(openInput({ terminalId: "dev-server" })); |
| 1297 | yield* manager.open(openInput({ terminalId: "subshell" })); | 1297 | yield* manager.open(openInput({ terminalId: "subshell" })); |
| ... | @@ -1371,7 +1371,7 @@ it.layer( | ... | @@ -1371,7 +1371,7 @@ it.layer( |
| 1371 | ), | 1371 | ), |
| 1372 | }).pipe( | 1372 | }).pipe( |
| 1373 | Effect.provideService(ProcessRunner.ProcessRunner, processRunner), | 1373 | Effect.provideService(ProcessRunner.ProcessRunner, processRunner), |
| 1374 | Effect.provide(withHostPlatform("linux")), | 1374 | Effect.provide(layerWithHostPlatform("linux")), |
| 1375 | ); | 1375 | ); |
| 1376 | 1376 | ||
| 1377 | yield* manager.open(openInput()); | 1377 | yield* manager.open(openInput()); |
| ... | @@ -1869,7 +1869,7 @@ it.layer( | ... | @@ -1869,7 +1869,7 @@ it.layer( |
| 1869 | PATH: "C:\\Windows\\System32", | 1869 | PATH: "C:\\Windows\\System32", |
| 1870 | SystemRoot: "C:\\Windows", | 1870 | SystemRoot: "C:\\Windows", |
| 1871 | }, | 1871 | }, |
| 1872 | }).pipe(Effect.provide(withHostPlatform("win32"))); | 1872 | }).pipe(Effect.provide(layerWithHostPlatform("win32"))); |
| 1873 | 1873 | ||
| 1874 | yield* manager.open(openInput()); | 1874 | yield* manager.open(openInput()); |
| 1875 | 1875 | ||
| ... | @@ -1931,7 +1931,10 @@ it.layer( | ... | @@ -1931,7 +1931,10 @@ it.layer( |
| 1931 | }, | 1931 | }, |
| 1932 | }).pipe( | 1932 | }).pipe( |
| 1933 | Effect.provide( | 1933 | Effect.provide( |
| 1934 | Layer.merge(withHostPlatform("win32"), Layer.succeed(HostProcessArchitecture, "x64")), | 1934 | Layer.merge( |
| 1935 | layerWithHostPlatform("win32"), | ||
| 1936 | Layer.succeed(HostProcessArchitecture, "x64"), | ||
| 1937 | ), | ||
| 1935 | ), | 1938 | ), |
| 1936 | ); | 1939 | ); |
| 1937 | 1940 | ||
| ... | @@ -1954,7 +1957,7 @@ it.layer( | ... | @@ -1954,7 +1957,7 @@ it.layer( |
| 1954 | PATH: "C:\\Windows\\System32", | 1957 | PATH: "C:\\Windows\\System32", |
| 1955 | SystemRoot: "C:\\Windows", | 1958 | SystemRoot: "C:\\Windows", |
| 1956 | }, | 1959 | }, |
| 1957 | }).pipe(Effect.provide(withHostPlatform("win32"))); | 1960 | }).pipe(Effect.provide(layerWithHostPlatform("win32"))); |
| 1958 | ptyAdapter.spawnFailures.push( | 1961 | ptyAdapter.spawnFailures.push( |
| 1959 | new Error("spawn custom-shell.exe ENOENT"), | 1962 | new Error("spawn custom-shell.exe ENOENT"), |
| 1960 | new Error("spawn pwsh.exe ENOENT"), | 1963 | new Error("spawn pwsh.exe ENOENT"), |
| ... | @@ -1988,7 +1991,7 @@ it.layer( | ... | @@ -1988,7 +1991,7 @@ it.layer( |
| 1988 | const { manager, ptyAdapter } = yield* createManager(5, { | 1991 | const { manager, ptyAdapter } = yield* createManager(5, { |
| 1989 | shellResolver: () => "/bin/sh", | 1992 | shellResolver: () => "/bin/sh", |
| 1990 | env, | 1993 | env, |
| 1991 | }).pipe(Effect.provide(withHostPlatform(platform))); | 1994 | }).pipe(Effect.provide(layerWithHostPlatform(platform))); |
| 1992 | yield* manager.open( | 1995 | yield* manager.open( |
| 1993 | openInput({ env: runtimeColor === undefined ? {} : { COLORTERM: runtimeColor } }), | 1996 | openInput({ env: runtimeColor === undefined ? {} : { COLORTERM: runtimeColor } }), |
| 1994 | ); | 1997 | ); |
| ... | @@ -2447,7 +2450,7 @@ it.layer( | ... | @@ -2447,7 +2450,7 @@ it.layer( |
| 2447 | Effect.provide( | 2450 | Effect.provide( |
| 2448 | ServerSettings.layer.pipe( | 2451 | ServerSettings.layer.pipe( |
| 2449 | Layer.provide(ServerSecretStore.layer), | 2452 | Layer.provide(ServerSecretStore.layer), |
| 2450 | Layer.provide(SqlitePersistenceMemory), | 2453 | Layer.provide(SqlitePersistence.layerMemory), |
| 2451 | Layer.provide( | 2454 | Layer.provide( |
| 2452 | ServerConfig.layerTest(process.cwd(), { prefix: "t3code-terminal-provider-restart-" }), | 2455 | ServerConfig.layerTest(process.cwd(), { prefix: "t3code-terminal-provider-restart-" }), |
| 2453 | ), | 2456 | ), |
apps/server/src/terminal/NodePtyAdapter.test.ts+12-12| ... | @@ -61,7 +61,7 @@ const spawnInput = { shell: "powershell.exe", cwd: ".", cols: 80, rows: 24, env: | ... | @@ -61,7 +61,7 @@ const spawnInput = { shell: "powershell.exe", cwd: ".", cols: 80, rows: 24, env: |
| 61 | 61 | ||
| 62 | const fakeNodePty = { spawn } as unknown as typeof import("node-pty"); | 62 | const fakeNodePty = { spawn } as unknown as typeof import("node-pty"); |
| 63 | 63 | ||
| 64 | const makeTestLayer = (platform: NodeJS.Platform = "win32") => | 64 | const layerTestFor = (platform: NodeJS.Platform = "win32") => |
| 65 | NodePtyAdapter.layer.pipe( | 65 | NodePtyAdapter.layer.pipe( |
| 66 | Layer.provide( | 66 | Layer.provide( |
| 67 | Layer.mergeAll( | 67 | Layer.mergeAll( |
| ... | @@ -73,7 +73,7 @@ const makeTestLayer = (platform: NodeJS.Platform = "win32") => | ... | @@ -73,7 +73,7 @@ const makeTestLayer = (platform: NodeJS.Platform = "win32") => |
| 73 | ), | 73 | ), |
| 74 | ); | 74 | ); |
| 75 | 75 | ||
| 76 | const testLayer = makeTestLayer(); | 76 | const layerTest = layerTestFor(); |
| 77 | 77 | ||
| 78 | it.effect("waits for the Windows PID without requiring output", () => | 78 | it.effect("waits for the Windows PID without requiring output", () => |
| 79 | Effect.gen(function* () { | 79 | Effect.gen(function* () { |
| ... | @@ -107,7 +107,7 @@ it.effect("waits for the Windows PID without requiring output", () => | ... | @@ -107,7 +107,7 @@ it.effect("waits for the Windows PID without requiring output", () => |
| 107 | assert.deepEqual(exits, [{ exitCode: 0, signal: null }]); | 107 | assert.deepEqual(exits, [{ exitCode: 0, signal: null }]); |
| 108 | stopData(); | 108 | stopData(); |
| 109 | stopExit(); | 109 | stopExit(); |
| 110 | }).pipe(Effect.provide(testLayer)), | 110 | }).pipe(Effect.provide(layerTest)), |
| 111 | ); | 111 | ); |
| 112 | 112 | ||
| 113 | it.effect.each(["exit", "close", "error", "invalid-pid"] as const)( | 113 | it.effect.each(["exit", "close", "error", "invalid-pid"] as const)( |
| ... | @@ -129,7 +129,7 @@ it.effect.each(["exit", "close", "error", "invalid-pid"] as const)( | ... | @@ -129,7 +129,7 @@ it.effect.each(["exit", "close", "error", "invalid-pid"] as const)( |
| 129 | assert.equal(nativeProcess._socket.listenerCount("close"), 0); | 129 | assert.equal(nativeProcess._socket.listenerCount("close"), 0); |
| 130 | assert.equal(nativeProcess.events.listenerCount("exit"), 0); | 130 | assert.equal(nativeProcess.events.listenerCount("exit"), 0); |
| 131 | assert.equal(nativeProcess._agent.kill.mock.calls.length, 1); | 131 | assert.equal(nativeProcess._agent.kill.mock.calls.length, 1); |
| 132 | }).pipe(Effect.provide(testLayer)), | 132 | }).pipe(Effect.provide(layerTest)), |
| 133 | ); | 133 | ); |
| 134 | 134 | ||
| 135 | it.effect("cancels the Windows connection without waiting for output", () => | 135 | it.effect("cancels the Windows connection without waiting for output", () => |
| ... | @@ -143,7 +143,7 @@ it.effect("cancels the Windows connection without waiting for output", () => | ... | @@ -143,7 +143,7 @@ it.effect("cancels the Windows connection without waiting for output", () => |
| 143 | assert.equal(nativeProcess.kill.mock.calls.length, 0); | 143 | assert.equal(nativeProcess.kill.mock.calls.length, 0); |
| 144 | assert.equal(nativeProcess._socket.listenerCount("ready_datapipe"), 0); | 144 | assert.equal(nativeProcess._socket.listenerCount("ready_datapipe"), 0); |
| 145 | assert.equal(nativeProcess.events.listenerCount("exit"), 0); | 145 | assert.equal(nativeProcess.events.listenerCount("exit"), 0); |
| 146 | }).pipe(Effect.provide(testLayer)), | 146 | }).pipe(Effect.provide(layerTest)), |
| 147 | ); | 147 | ); |
| 148 | 148 | ||
| 149 | it.effect("reports an incompatible Windows readiness API instead of hanging", () => | 149 | it.effect("reports an incompatible Windows readiness API instead of hanging", () => |
| ... | @@ -157,7 +157,7 @@ it.effect("reports an incompatible Windows readiness API instead of hanging", () | ... | @@ -157,7 +157,7 @@ it.effect("reports an incompatible Windows readiness API instead of hanging", () |
| 157 | assert.instanceOf(error.cause, Error); | 157 | assert.instanceOf(error.cause, Error); |
| 158 | assert.equal(error.cause.message, "Windows PTY readiness socket is unavailable."); | 158 | assert.equal(error.cause.message, "Windows PTY readiness socket is unavailable."); |
| 159 | assert.equal(nativeProcess._agent.kill.mock.calls.length, 1); | 159 | assert.equal(nativeProcess._agent.kill.mock.calls.length, 1); |
| 160 | }).pipe(Effect.provide(testLayer)), | 160 | }).pipe(Effect.provide(layerTest)), |
| 161 | ); | 161 | ); |
| 162 | 162 | ||
| 163 | it.effect.each(["win32", "linux", "darwin"] as const)( | 163 | it.effect.each(["win32", "linux", "darwin"] as const)( |
| ... | @@ -189,7 +189,7 @@ it.effect.each(["win32", "linux", "darwin"] as const)( | ... | @@ -189,7 +189,7 @@ it.effect.each(["win32", "linux", "darwin"] as const)( |
| 189 | ? [[undefined], [undefined], [undefined]] | 189 | ? [[undefined], [undefined], [undefined]] |
| 190 | : [["SIGTERM"], ["SIGKILL"], [undefined]], | 190 | : [["SIGTERM"], ["SIGKILL"], [undefined]], |
| 191 | ); | 191 | ); |
| 192 | }).pipe(Effect.provide(makeTestLayer(platform))), | 192 | }).pipe(Effect.provide(layerTestFor(platform))), |
| 193 | ); | 193 | ); |
| 194 | 194 | ||
| 195 | it.effect("spawns through the public adapter with the provided host references", () => | 195 | it.effect("spawns through the public adapter with the provided host references", () => |
| ... | @@ -218,7 +218,7 @@ it.effect("spawns through the public adapter with the provided host references", | ... | @@ -218,7 +218,7 @@ it.effect("spawns through the public adapter with the provided host references", |
| 218 | name: "xterm-256color", | 218 | name: "xterm-256color", |
| 219 | }, | 219 | }, |
| 220 | ]); | 220 | ]); |
| 221 | }).pipe(Effect.provide(testLayer)), | 221 | }).pipe(Effect.provide(layerTest)), |
| 222 | ); | 222 | ); |
| 223 | 223 | ||
| 224 | it.effect("preserves a caller-provided TERM in the spawn env on win32", () => | 224 | it.effect("preserves a caller-provided TERM in the spawn env on win32", () => |
| ... | @@ -245,7 +245,7 @@ it.effect("preserves a caller-provided TERM in the spawn env on win32", () => | ... | @@ -245,7 +245,7 @@ it.effect("preserves a caller-provided TERM in the spawn env on win32", () => |
| 245 | name: "xterm-256color", | 245 | name: "xterm-256color", |
| 246 | }, | 246 | }, |
| 247 | ]); | 247 | ]); |
| 248 | }).pipe(Effect.provide(testLayer)), | 248 | }).pipe(Effect.provide(layerTest)), |
| 249 | ); | 249 | ); |
| 250 | 250 | ||
| 251 | it.effect("reports native module load failures as structured startup defects", () => | 251 | it.effect("reports native module load failures as structured startup defects", () => |
| ... | @@ -300,7 +300,7 @@ it.effect.each([2048, 8])( | ... | @@ -300,7 +300,7 @@ it.effect.each([2048, 8])( |
| 300 | nativeProcess.events.emit("exit", { exitCode: 0 }); | 300 | nativeProcess.events.emit("exit", { exitCode: 0 }); |
| 301 | yield* Fiber.join(fiber); | 301 | yield* Fiber.join(fiber); |
| 302 | assert.equal(exits.length, 1); | 302 | assert.equal(exits.length, 1); |
| 303 | }).pipe(Effect.provide(testLayer)), | 303 | }).pipe(Effect.provide(layerTest)), |
| 304 | ); | 304 | ); |
| 305 | 305 | ||
| 306 | it.effect("replays an exit to late subscribers and respects unsubscription", () => | 306 | it.effect("replays an exit to late subscribers and respects unsubscription", () => |
| ... | @@ -317,7 +317,7 @@ it.effect("replays an exit to late subscribers and respects unsubscription", () | ... | @@ -317,7 +317,7 @@ it.effect("replays an exit to late subscribers and respects unsubscription", () |
| 317 | assert.equal(removed.mock.calls.length, 0); | 317 | assert.equal(removed.mock.calls.length, 0); |
| 318 | assert.deepEqual(late.mock.calls, [[{ exitCode: 7, signal: 2 }]]); | 318 | assert.deepEqual(late.mock.calls, [[{ exitCode: 7, signal: 2 }]]); |
| 319 | assert.equal(nativeProcess.events.listenerCount("exit"), 0); | 319 | assert.equal(nativeProcess.events.listenerCount("exit"), 0); |
| 320 | }).pipe(Effect.provide(testLayer)), | 320 | }).pipe(Effect.provide(layerTest)), |
| 321 | ); | 321 | ); |
| 322 | 322 | ||
| 323 | it.effect.each(["spawn", "interrupt"] as const)( | 323 | it.effect.each(["spawn", "interrupt"] as const)( |
| ... | @@ -361,5 +361,5 @@ it.effect.each(["spawn", "interrupt"] as const)( | ... | @@ -361,5 +361,5 @@ it.effect.each(["spawn", "interrupt"] as const)( |
| 361 | ]); | 361 | ]); |
| 362 | assert.equal(nativeProcess.events.listenerCount("exit"), 0); | 362 | assert.equal(nativeProcess.events.listenerCount("exit"), 0); |
| 363 | assert.equal(nativeProcess._socket.listenerCount("ready_datapipe"), 0); | 363 | assert.equal(nativeProcess._socket.listenerCount("ready_datapipe"), 0); |
| 364 | }).pipe(Effect.provide(testLayer)), | 364 | }).pipe(Effect.provide(layerTest)), |
| 365 | ); | 365 | ); |
apps/server/src/textGeneration/ClaudeTextGeneration.test.ts+2-2| ... | @@ -24,7 +24,7 @@ import { makeClaudeTextGeneration } from "./ClaudeTextGeneration.ts"; | ... | @@ -24,7 +24,7 @@ import { makeClaudeTextGeneration } from "./ClaudeTextGeneration.ts"; |
| 24 | import { writeFakeCli } from "../testUtils/fakeCli.ts"; | 24 | import { writeFakeCli } from "../testUtils/fakeCli.ts"; |
| 25 | const decodeClaudeSettings = Schema.decodeSync(ClaudeSettings); | 25 | const decodeClaudeSettings = Schema.decodeSync(ClaudeSettings); |
| 26 | 26 | ||
| 27 | const ClaudeTextGenerationTestLayer = ServerConfig.ServerConfig.layerTest(process.cwd(), { | 27 | const layerClaudeTextGenerationTest = ServerConfig.ServerConfig.layerTest(process.cwd(), { |
| 28 | prefix: "t3code-claude-text-generation-test-", | 28 | prefix: "t3code-claude-text-generation-test-", |
| 29 | }).pipe(Layer.provideMerge(NodeServices.layer)); | 29 | }).pipe(Layer.provideMerge(NodeServices.layer)); |
| 30 | 30 | ||
| ... | @@ -191,7 +191,7 @@ function withFakeClaudeEnv<A, E, R>( | ... | @@ -191,7 +191,7 @@ function withFakeClaudeEnv<A, E, R>( |
| 191 | }).pipe(Effect.scoped); | 191 | }).pipe(Effect.scoped); |
| 192 | } | 192 | } |
| 193 | 193 | ||
| 194 | it.layer(ClaudeTextGenerationTestLayer)("ClaudeTextGeneration", (it) => { | 194 | it.layer(layerClaudeTextGenerationTest)("ClaudeTextGeneration", (it) => { |
| 195 | it.effect("forwards Claude thinking settings without passing unsupported effort", () => | 195 | it.effect("forwards Claude thinking settings without passing unsupported effort", () => |
| 196 | withFakeClaudeEnv( | 196 | withFakeClaudeEnv( |
| 197 | { | 197 | { |
apps/server/src/textGeneration/CodexTextGeneration.test.ts+2-2| ... | @@ -24,7 +24,7 @@ const DEFAULT_TEST_MODEL_SELECTION = createModelSelection( | ... | @@ -24,7 +24,7 @@ const DEFAULT_TEST_MODEL_SELECTION = createModelSelection( |
| 24 | "gpt-5.4-mini", | 24 | "gpt-5.4-mini", |
| 25 | ); | 25 | ); |
| 26 | 26 | ||
| 27 | const CodexTextGenerationTestLayer = ServerConfig.ServerConfig.layerTest(process.cwd(), { | 27 | const layerCodexTextGenerationTest = ServerConfig.ServerConfig.layerTest(process.cwd(), { |
| 28 | prefix: "t3code-codex-text-generation-test-", | 28 | prefix: "t3code-codex-text-generation-test-", |
| 29 | }).pipe(Layer.provideMerge(NodeServices.layer)); | 29 | }).pipe(Layer.provideMerge(NodeServices.layer)); |
| 30 | 30 | ||
| ... | @@ -169,7 +169,7 @@ function withFakeCodexEnv<A, E, R>( | ... | @@ -169,7 +169,7 @@ function withFakeCodexEnv<A, E, R>( |
| 169 | }).pipe(Effect.scoped); | 169 | }).pipe(Effect.scoped); |
| 170 | } | 170 | } |
| 171 | 171 | ||
| 172 | it.layer(CodexTextGenerationTestLayer)("CodexTextGeneration", (it) => { | 172 | it.layer(layerCodexTextGenerationTest)("CodexTextGeneration", (it) => { |
| 173 | it.effect.each(["gpt-5.6-luna", "openai.gpt-5.6-luna"])( | 173 | it.effect.each(["gpt-5.6-luna", "openai.gpt-5.6-luna"])( |
| 174 | "dispatches the qualified live model for %s", | 174 | "dispatches the qualified live model for %s", |
| 175 | (selectedModel) => | 175 | (selectedModel) => |
apps/server/src/textGeneration/CursorTextGeneration.test.ts+12-12| ... | @@ -27,7 +27,7 @@ const cursorSdkMock = vi.hoisted(() => ({ | ... | @@ -27,7 +27,7 @@ const cursorSdkMock = vi.hoisted(() => ({ |
| 27 | vi.mock("../provider/cursorSdk.ts", () => ({ Agent: { create: cursorSdkMock.create } })); | 27 | vi.mock("../provider/cursorSdk.ts", () => ({ Agent: { create: cursorSdkMock.create } })); |
| 28 | 28 | ||
| 29 | let hasCustomPolicy = false; | 29 | let hasCustomPolicy = false; |
| 30 | const fsLayer = FileSystem.layerNoop({ | 30 | const layerFs = FileSystem.layerNoop({ |
| 31 | exists: () => Effect.succeed(hasCustomPolicy), | 31 | exists: () => Effect.succeed(hasCustomPolicy), |
| 32 | makeTempDirectoryScoped: () => Effect.succeed("/isolated-text-generation"), | 32 | makeTempDirectoryScoped: () => Effect.succeed("/isolated-text-generation"), |
| 33 | }); | 33 | }); |
| ... | @@ -89,7 +89,7 @@ describe("CursorTextGeneration", () => { | ... | @@ -89,7 +89,7 @@ describe("CursorTextGeneration", () => { |
| 89 | expect(cursorSdkMock.create).toHaveBeenLastCalledWith( | 89 | expect(cursorSdkMock.create).toHaveBeenLastCalledWith( |
| 90 | expect.objectContaining({ apiKey: "second-browser-key" }), | 90 | expect.objectContaining({ apiKey: "second-browser-key" }), |
| 91 | ); | 91 | ); |
| 92 | }).pipe(Effect.provide(fsLayer)), | 92 | }).pipe(Effect.provide(layerFs)), |
| 93 | ); | 93 | ); |
| 94 | 94 | ||
| 95 | it.effect("uses the Cursor SDK prompt API with model parameters and API key", () => | 95 | it.effect("uses the Cursor SDK prompt API with model parameters and API key", () => |
| ... | @@ -138,7 +138,7 @@ describe("CursorTextGeneration", () => { | ... | @@ -138,7 +138,7 @@ describe("CursorTextGeneration", () => { |
| 138 | enableAgentRetries: true, | 138 | enableAgentRetries: true, |
| 139 | }, | 139 | }, |
| 140 | }); | 140 | }); |
| 141 | }).pipe(Effect.provide(fsLayer)), | 141 | }).pipe(Effect.provide(layerFs)), |
| 142 | ); | 142 | ); |
| 143 | 143 | ||
| 144 | it.effect("continues in the temp directory when the SDK cannot sandbox", () => | 144 | it.effect("continues in the temp directory when the SDK cannot sandbox", () => |
| ... | @@ -174,7 +174,7 @@ describe("CursorTextGeneration", () => { | ... | @@ -174,7 +174,7 @@ describe("CursorTextGeneration", () => { |
| 174 | enableAgentRetries: true, | 174 | enableAgentRetries: true, |
| 175 | }, | 175 | }, |
| 176 | }); | 176 | }); |
| 177 | }).pipe(Effect.provide(fsLayer)), | 177 | }).pipe(Effect.provide(layerFs)), |
| 178 | ); | 178 | ); |
| 179 | 179 | ||
| 180 | it.effect("does not retry Agent.create for errors other than an unsupported sandbox", () => | 180 | it.effect("does not retry Agent.create for errors other than an unsupported sandbox", () => |
| ... | @@ -198,7 +198,7 @@ describe("CursorTextGeneration", () => { | ... | @@ -198,7 +198,7 @@ describe("CursorTextGeneration", () => { |
| 198 | 198 | ||
| 199 | expect(error.detail).toBe("Cursor SDK text generation failed."); | 199 | expect(error.detail).toBe("Cursor SDK text generation failed."); |
| 200 | expect(cursorSdkMock.create).toHaveBeenCalledTimes(1); | 200 | expect(cursorSdkMock.create).toHaveBeenCalledTimes(1); |
| 201 | }).pipe(Effect.provide(fsLayer)), | 201 | }).pipe(Effect.provide(layerFs)), |
| 202 | ); | 202 | ); |
| 203 | 203 | ||
| 204 | it.effect("accepts json objects with extra assistant text around them", () => | 204 | it.effect("accepts json objects with extra assistant text around them", () => |
| ... | @@ -226,7 +226,7 @@ describe("CursorTextGeneration", () => { | ... | @@ -226,7 +226,7 @@ describe("CursorTextGeneration", () => { |
| 226 | 226 | ||
| 227 | expect(generated.subject).toBe("Update README dummy comment with attribution and date"); | 227 | expect(generated.subject).toBe("Update README dummy comment with attribution and date"); |
| 228 | expect(generated.body).toBe(""); | 228 | expect(generated.body).toBe(""); |
| 229 | }).pipe(Effect.provide(fsLayer)), | 229 | }).pipe(Effect.provide(layerFs)), |
| 230 | ); | 230 | ); |
| 231 | 231 | ||
| 232 | it.effect("generates thread titles through Cursor SDK text generation", () => | 232 | it.effect("generates thread titles through Cursor SDK text generation", () => |
| ... | @@ -250,7 +250,7 @@ describe("CursorTextGeneration", () => { | ... | @@ -250,7 +250,7 @@ describe("CursorTextGeneration", () => { |
| 250 | }); | 250 | }); |
| 251 | 251 | ||
| 252 | expect(generated.title).toBe("Trim reconnect spinner status after resume."); | 252 | expect(generated.title).toBe("Trim reconnect spinner status after resume."); |
| 253 | }).pipe(Effect.provide(fsLayer)), | 253 | }).pipe(Effect.provide(layerFs)), |
| 254 | ); | 254 | ); |
| 255 | 255 | ||
| 256 | it.effect.each(["error", "cancelled"] as const)( | 256 | it.effect.each(["error", "cancelled"] as const)( |
| ... | @@ -284,7 +284,7 @@ describe("CursorTextGeneration", () => { | ... | @@ -284,7 +284,7 @@ describe("CursorTextGeneration", () => { |
| 284 | : "Cursor SDK request finished with an error.", | 284 | : "Cursor SDK request finished with an error.", |
| 285 | ); | 285 | ); |
| 286 | expect(cursorSdkMock.close).toHaveBeenCalledOnce(); | 286 | expect(cursorSdkMock.close).toHaveBeenCalledOnce(); |
| 287 | }).pipe(Effect.provide(fsLayer)), | 287 | }).pipe(Effect.provide(layerFs)), |
| 288 | ); | 288 | ); |
| 289 | 289 | ||
| 290 | it.effect("fails closed when ambient sandbox policy can expand write access", () => | 290 | it.effect("fails closed when ambient sandbox policy can expand write access", () => |
| ... | @@ -300,7 +300,7 @@ describe("CursorTextGeneration", () => { | ... | @@ -300,7 +300,7 @@ describe("CursorTextGeneration", () => { |
| 300 | ); | 300 | ); |
| 301 | expect(failure.detail).toContain("custom ~/.cursor/sandbox.json"); | 301 | expect(failure.detail).toContain("custom ~/.cursor/sandbox.json"); |
| 302 | expect(cursorSdkMock.prompt).not.toHaveBeenCalled(); | 302 | expect(cursorSdkMock.prompt).not.toHaveBeenCalled(); |
| 303 | }).pipe(Effect.provide(fsLayer)), | 303 | }).pipe(Effect.provide(layerFs)), |
| 304 | ); | 304 | ); |
| 305 | 305 | ||
| 306 | it.effect("cancels the native run when text generation times out", () => | 306 | it.effect("cancels the native run when text generation times out", () => |
| ... | @@ -326,7 +326,7 @@ describe("CursorTextGeneration", () => { | ... | @@ -326,7 +326,7 @@ describe("CursorTextGeneration", () => { |
| 326 | expect((yield* Fiber.join(result)).detail).toContain("timed out"); | 326 | expect((yield* Fiber.join(result)).detail).toContain("timed out"); |
| 327 | expect(cursorSdkMock.cancel).toHaveBeenCalledOnce(); | 327 | expect(cursorSdkMock.cancel).toHaveBeenCalledOnce(); |
| 328 | expect(cursorSdkMock.close).toHaveBeenCalledOnce(); | 328 | expect(cursorSdkMock.close).toHaveBeenCalledOnce(); |
| 329 | }).pipe(Effect.provide(fsLayer), Effect.scoped), | 329 | }).pipe(Effect.provide(layerFs), Effect.scoped), |
| 330 | ); | 330 | ); |
| 331 | 331 | ||
| 332 | it.effect.each(["create", "send"] as const)( | 332 | it.effect.each(["create", "send"] as const)( |
| ... | @@ -388,7 +388,7 @@ describe("CursorTextGeneration", () => { | ... | @@ -388,7 +388,7 @@ describe("CursorTextGeneration", () => { |
| 388 | expect(cursorSdkMock.close).toHaveBeenCalledOnce(); | 388 | expect(cursorSdkMock.close).toHaveBeenCalledOnce(); |
| 389 | expect(wait).not.toHaveBeenCalled(); | 389 | expect(wait).not.toHaveBeenCalled(); |
| 390 | if (phase === "send") expect(cursorSdkMock.cancel).toHaveBeenCalledOnce(); | 390 | if (phase === "send") expect(cursorSdkMock.cancel).toHaveBeenCalledOnce(); |
| 391 | }).pipe(Effect.provide(fsLayer), Effect.scoped), | 391 | }).pipe(Effect.provide(layerFs), Effect.scoped), |
| 392 | ); | 392 | ); |
| 393 | 393 | ||
| 394 | it.effect("requires CURSOR_API_KEY before calling the SDK", () => | 394 | it.effect("requires CURSOR_API_KEY before calling the SDK", () => |
| ... | @@ -410,6 +410,6 @@ describe("CursorTextGeneration", () => { | ... | @@ -410,6 +410,6 @@ describe("CursorTextGeneration", () => { |
| 410 | 410 | ||
| 411 | expect(error.detail).toBe("Sign in with Cursor or add CURSOR_API_KEY in provider settings."); | 411 | expect(error.detail).toBe("Sign in with Cursor or add CURSOR_API_KEY in provider settings."); |
| 412 | expect(cursorSdkMock.prompt).not.toHaveBeenCalled(); | 412 | expect(cursorSdkMock.prompt).not.toHaveBeenCalled(); |
| 413 | }).pipe(Effect.provide(fsLayer)), | 413 | }).pipe(Effect.provide(layerFs)), |
| 414 | ); | 414 | ); |
| 415 | }); | 415 | }); |
apps/server/src/textGeneration/GrokTextGeneration.test.ts+2-2| ... | @@ -22,7 +22,7 @@ const decodeGrokSettings = Schema.decodeSync(GrokSettings); | ... | @@ -22,7 +22,7 @@ const decodeGrokSettings = Schema.decodeSync(GrokSettings); |
| 22 | const __dirname = NodePath.dirname(NodeURL.fileURLToPath(import.meta.url)); | 22 | const __dirname = NodePath.dirname(NodeURL.fileURLToPath(import.meta.url)); |
| 23 | const mockAgentPath = NodePath.join(__dirname, "../provider/testFixtures/grok-text-mock-agent.mjs"); | 23 | const mockAgentPath = NodePath.join(__dirname, "../provider/testFixtures/grok-text-mock-agent.mjs"); |
| 24 | 24 | ||
| 25 | const GrokTextGenerationTestLayer = ServerConfig.ServerConfig.layerTest(process.cwd(), { | 25 | const layerGrokTextGenerationTest = ServerConfig.ServerConfig.layerTest(process.cwd(), { |
| 26 | prefix: "t3code-grok-text-generation-test-", | 26 | prefix: "t3code-grok-text-generation-test-", |
| 27 | }).pipe(Layer.provideMerge(NodeServices.layer)); | 27 | }).pipe(Layer.provideMerge(NodeServices.layer)); |
| 28 | 28 | ||
| ... | @@ -66,7 +66,7 @@ function readJsonRpcRequests( | ... | @@ -66,7 +66,7 @@ function readJsonRpcRequests( |
| 66 | .map((line) => JSON.parse(line) as { method?: string; params?: Record<string, unknown> }); | 66 | .map((line) => JSON.parse(line) as { method?: string; params?: Record<string, unknown> }); |
| 67 | } | 67 | } |
| 68 | 68 | ||
| 69 | it.layer(GrokTextGenerationTestLayer)("GrokTextGeneration", (it) => { | 69 | it.layer(layerGrokTextGenerationTest)("GrokTextGeneration", (it) => { |
| 70 | it.effect("uses ACP with disabled tool capabilities and forwards the requested model id", () => { | 70 | it.effect("uses ACP with disabled tool capabilities and forwards the requested model id", () => { |
| 71 | const requestLogDir = NodeFS.mkdtempSync( | 71 | const requestLogDir = NodeFS.mkdtempSync( |
| 72 | NodePath.join(NodeOS.tmpdir(), "t3code-grok-text-log-"), | 72 | NodePath.join(NodeOS.tmpdir(), "t3code-grok-text-log-"), |
apps/server/src/textGeneration/OpenCodeTextGeneration.test.ts+4-4| ... | @@ -162,7 +162,7 @@ const DEFAULT_COMMIT_MESSAGE_INPUT = { | ... | @@ -162,7 +162,7 @@ const DEFAULT_COMMIT_MESSAGE_INPUT = { |
| 162 | 162 | ||
| 163 | const OPENCODE_TEXT_GENERATION_IDLE_TTL_MS = 30_000; | 163 | const OPENCODE_TEXT_GENERATION_IDLE_TTL_MS = 30_000; |
| 164 | 164 | ||
| 165 | const OpenCodeTextGenerationTestLayer = Layer.succeed( | 165 | const layerOpenCodeTextGenerationTest = Layer.succeed( |
| 166 | OpenCodeRuntime.OpenCodeRuntime, | 166 | OpenCodeRuntime.OpenCodeRuntime, |
| 167 | OpenCodeRuntimeTestDouble, | 167 | OpenCodeRuntimeTestDouble, |
| 168 | ).pipe( | 168 | ).pipe( |
| ... | @@ -175,7 +175,7 @@ const OpenCodeTextGenerationTestLayer = Layer.succeed( | ... | @@ -175,7 +175,7 @@ const OpenCodeTextGenerationTestLayer = Layer.succeed( |
| 175 | Layer.provideMerge(NodeServices.layer), | 175 | Layer.provideMerge(NodeServices.layer), |
| 176 | ); | 176 | ); |
| 177 | 177 | ||
| 178 | const OpenCodeTextGenerationExistingServerTestLayer = Layer.succeed( | 178 | const layerOpenCodeTextGenerationExistingServerTest = Layer.succeed( |
| 179 | OpenCodeRuntime.OpenCodeRuntime, | 179 | OpenCodeRuntime.OpenCodeRuntime, |
| 180 | OpenCodeRuntimeTestDouble, | 180 | OpenCodeRuntimeTestDouble, |
| 181 | ).pipe( | 181 | ).pipe( |
| ... | @@ -234,7 +234,7 @@ const advanceIdleClock = Effect.gen(function* () { | ... | @@ -234,7 +234,7 @@ const advanceIdleClock = Effect.gen(function* () { |
| 234 | yield* Effect.yieldNow; | 234 | yield* Effect.yieldNow; |
| 235 | }); | 235 | }); |
| 236 | 236 | ||
| 237 | it.layer(OpenCodeTextGenerationTestLayer)("OpenCodeTextGeneration", (it) => { | 237 | it.layer(layerOpenCodeTextGenerationTest)("OpenCodeTextGeneration", (it) => { |
| 238 | it.effect("excludes generic files from thread title generation", () => | 238 | it.effect("excludes generic files from thread title generation", () => |
| 239 | withOpenCodeTextGeneration(DEFAULT_OPENCODE_SETTINGS, (textGeneration) => | 239 | withOpenCodeTextGeneration(DEFAULT_OPENCODE_SETTINGS, (textGeneration) => |
| 240 | Effect.gen(function* () { | 240 | Effect.gen(function* () { |
| ... | @@ -545,7 +545,7 @@ it.layer(OpenCodeTextGenerationTestLayer)("OpenCodeTextGeneration", (it) => { | ... | @@ -545,7 +545,7 @@ it.layer(OpenCodeTextGenerationTestLayer)("OpenCodeTextGeneration", (it) => { |
| 545 | ); | 545 | ); |
| 546 | }); | 546 | }); |
| 547 | 547 | ||
| 548 | it.layer(OpenCodeTextGenerationExistingServerTestLayer)( | 548 | it.layer(layerOpenCodeTextGenerationExistingServerTest)( |
| 549 | "OpenCodeTextGeneration with configured server URL", | 549 | "OpenCodeTextGeneration with configured server URL", |
| 550 | (it) => { | 550 | (it) => { |
| 551 | it.effect("does not send a local environment password to a configured server", () => | 551 | it.effect("does not send a local environment password to a configured server", () => |
apps/server/src/textGeneration/ThreadTitleLinks.test.ts+4-4| ... | @@ -9,7 +9,7 @@ import { SourceControlProviderError } from "@t3tools/contracts"; | ... | @@ -9,7 +9,7 @@ import { SourceControlProviderError } from "@t3tools/contracts"; |
| 9 | import { resolveThreadTitleLinks } from "./ThreadTitleLinks.ts"; | 9 | import { resolveThreadTitleLinks } from "./ThreadTitleLinks.ts"; |
| 10 | import * as SourceControlProviderRegistry from "../sourceControl/SourceControlProviderRegistry.ts"; | 10 | import * as SourceControlProviderRegistry from "../sourceControl/SourceControlProviderRegistry.ts"; |
| 11 | 11 | ||
| 12 | const registry = Layer.mock(SourceControlProviderRegistry.SourceControlProviderRegistry); | 12 | const layerRegistry = Layer.mock(SourceControlProviderRegistry.SourceControlProviderRegistry); |
| 13 | const encodeSubject = Schema.encodeSync( | 13 | const encodeSubject = Schema.encodeSync( |
| 14 | Schema.fromJsonString(Schema.Struct({ title: Schema.String, body: Schema.String })), | 14 | Schema.fromJsonString(Schema.Struct({ title: Schema.String, body: Schema.String })), |
| 15 | ); | 15 | ); |
| ... | @@ -26,7 +26,7 @@ it.effect( | ... | @@ -26,7 +26,7 @@ it.effect( |
| 26 | "https://docs.test/guide [https://forge.test/change/1] https://forge.test/change/1#discussion https://forge.test/change/1?view=full `https://forge.test/change/2` https://forge.test/change/2. https://forge.test/change/3", | 26 | "https://docs.test/guide [https://forge.test/change/1] https://forge.test/change/1#discussion https://forge.test/change/1?view=full `https://forge.test/change/2` https://forge.test/change/2. https://forge.test/change/3", |
| 27 | }).pipe( | 27 | }).pipe( |
| 28 | Effect.provide( | 28 | Effect.provide( |
| 29 | registry({ | 29 | layerRegistry({ |
| 30 | resolveLink: ({ url, cwd }) => | 30 | resolveLink: ({ url, cwd }) => |
| 31 | url.host === "forge.test" | 31 | url.host === "forge.test" |
| 32 | ? Effect.sync(() => { | 32 | ? Effect.sync(() => { |
| ... | @@ -58,7 +58,7 @@ it.effect("returns unavailable when a lookup times out while retaining successfu | ... | @@ -58,7 +58,7 @@ it.effect("returns unavailable when a lookup times out while retaining successfu |
| 58 | message: "https://forge.test/change/1 https://forge.test/change/2", | 58 | message: "https://forge.test/change/1 https://forge.test/change/2", |
| 59 | }).pipe( | 59 | }).pipe( |
| 60 | Effect.provide( | 60 | Effect.provide( |
| 61 | registry({ | 61 | layerRegistry({ |
| 62 | resolveLink: ({ url }) => | 62 | resolveLink: ({ url }) => |
| 63 | url.pathname.endsWith("1") | 63 | url.pathname.endsWith("1") |
| 64 | ? Deferred.succeed(started, undefined).pipe(Effect.andThen(Effect.never)) | 64 | ? Deferred.succeed(started, undefined).pipe(Effect.andThen(Effect.never)) |
| ... | @@ -83,7 +83,7 @@ it.effect("keeps lookup failure out of generation and skips unlinked messages", | ... | @@ -83,7 +83,7 @@ it.effect("keeps lookup failure out of generation and skips unlinked messages", |
| 83 | ).toContain("unavailable"); | 83 | ).toContain("unavailable"); |
| 84 | }).pipe( | 84 | }).pipe( |
| 85 | Effect.provide( | 85 | Effect.provide( |
| 86 | registry({ | 86 | layerRegistry({ |
| 87 | resolveLink: () => | 87 | resolveLink: () => |
| 88 | Effect.fail( | 88 | Effect.fail( |
| 89 | new SourceControlProviderError({ | 89 | new SourceControlProviderError({ |
apps/server/src/usage/UsageService.test.ts+24-30| ... | @@ -78,7 +78,7 @@ const setup = Effect.gen(function* () { | ... | @@ -78,7 +78,7 @@ const setup = Effect.gen(function* () { |
| 78 | }; | 78 | }; |
| 79 | }); | 79 | }); |
| 80 | 80 | ||
| 81 | const serviceLayers = (input: { | 81 | const layerService = (input: { |
| 82 | readonly prefix: string; | 82 | readonly prefix: string; |
| 83 | readonly home: string; | 83 | readonly home: string; |
| 84 | readonly settings: Parameters<typeof ServerSettings.layerTest>[0]; | 84 | readonly settings: Parameters<typeof ServerSettings.layerTest>[0]; |
| ... | @@ -197,7 +197,7 @@ describe("UsageService", () => { | ... | @@ -197,7 +197,7 @@ describe("UsageService", () => { |
| 197 | return yield* service.readSummary(WINDOW); | 197 | return yield* service.readSummary(WINDOW); |
| 198 | }).pipe( | 198 | }).pipe( |
| 199 | Effect.provide( | 199 | Effect.provide( |
| 200 | serviceLayers({ | 200 | layerService({ |
| 201 | prefix: "usage-managed-accounts", | 201 | prefix: "usage-managed-accounts", |
| 202 | home, | 202 | home, |
| 203 | settings: { | 203 | settings: { |
| ... | @@ -254,7 +254,7 @@ describe("UsageService", () => { | ... | @@ -254,7 +254,7 @@ describe("UsageService", () => { |
| 254 | for (const platform of ["linux", "win32", "darwin"] as const) { | 254 | for (const platform of ["linux", "win32", "darwin"] as const) { |
| 255 | const service = yield* UsageService.make.pipe( | 255 | const service = yield* UsageService.make.pipe( |
| 256 | Effect.provide( | 256 | Effect.provide( |
| 257 | serviceLayers({ | 257 | layerService({ |
| 258 | prefix: `usage-service-cursor-no-login-${platform}`, | 258 | prefix: `usage-service-cursor-no-login-${platform}`, |
| 259 | home, | 259 | home, |
| 260 | settings, | 260 | settings, |
| ... | @@ -279,7 +279,7 @@ describe("UsageService", () => { | ... | @@ -279,7 +279,7 @@ describe("UsageService", () => { |
| 279 | }); | 279 | }); |
| 280 | const service = yield* UsageService.make.pipe( | 280 | const service = yield* UsageService.make.pipe( |
| 281 | Effect.provide( | 281 | Effect.provide( |
| 282 | serviceLayers({ prefix: "usage-service-cursor-invalid-login", home, settings }), | 282 | layerService({ prefix: "usage-service-cursor-invalid-login", home, settings }), |
| 283 | ), | 283 | ), |
| 284 | ); | 284 | ); |
| 285 | const summary = yield* service.readSummary(WINDOW); | 285 | const summary = yield* service.readSummary(WINDOW); |
| ... | @@ -293,7 +293,7 @@ describe("UsageService", () => { | ... | @@ -293,7 +293,7 @@ describe("UsageService", () => { |
| 293 | const { settings, home } = yield* setup; | 293 | const { settings, home } = yield* setup; |
| 294 | const service = yield* UsageService.make.pipe( | 294 | const service = yield* UsageService.make.pipe( |
| 295 | Effect.provide( | 295 | Effect.provide( |
| 296 | serviceLayers({ | 296 | layerService({ |
| 297 | prefix: "usage-service-cursor-keychain-disabled", | 297 | prefix: "usage-service-cursor-keychain-disabled", |
| 298 | home, | 298 | home, |
| 299 | settings, | 299 | settings, |
| ... | @@ -339,7 +339,7 @@ describe("UsageService", () => { | ... | @@ -339,7 +339,7 @@ describe("UsageService", () => { |
| 339 | }); | 339 | }); |
| 340 | const service = yield* UsageService.make.pipe( | 340 | const service = yield* UsageService.make.pipe( |
| 341 | Effect.provide( | 341 | Effect.provide( |
| 342 | serviceLayers({ | 342 | layerService({ |
| 343 | prefix: `usage-service-cursor-store-${index}`, | 343 | prefix: `usage-service-cursor-store-${index}`, |
| 344 | home, | 344 | home, |
| 345 | settings, | 345 | settings, |
| ... | @@ -395,7 +395,7 @@ describe("UsageService", () => { | ... | @@ -395,7 +395,7 @@ describe("UsageService", () => { |
| 395 | } | 395 | } |
| 396 | }); | 396 | }); |
| 397 | const service = yield* UsageService.make.pipe( | 397 | const service = yield* UsageService.make.pipe( |
| 398 | Effect.provide(serviceLayers({ prefix: "usage-service-opencode", home, settings })), | 398 | Effect.provide(layerService({ prefix: "usage-service-opencode", home, settings })), |
| 399 | ); | 399 | ); |
| 400 | const summary = yield* service.readSummary(WINDOW); | 400 | const summary = yield* service.readSummary(WINDOW); |
| 401 | assert.strictEqual(summary.buckets[0]?.provider, "opencode"); | 401 | assert.strictEqual(summary.buckets[0]?.provider, "opencode"); |
| ... | @@ -441,7 +441,7 @@ describe("UsageService", () => { | ... | @@ -441,7 +441,7 @@ describe("UsageService", () => { |
| 441 | }); | 441 | }); |
| 442 | const service = yield* UsageService.make.pipe( | 442 | const service = yield* UsageService.make.pipe( |
| 443 | Effect.provide( | 443 | Effect.provide( |
| 444 | serviceLayers({ | 444 | layerService({ |
| 445 | prefix: "usage-service-aliased-roots-test", | 445 | prefix: "usage-service-aliased-roots-test", |
| 446 | home, | 446 | home, |
| 447 | settings, | 447 | settings, |
| ... | @@ -521,7 +521,7 @@ describe("UsageService", () => { | ... | @@ -521,7 +521,7 @@ describe("UsageService", () => { |
| 521 | }); | 521 | }); |
| 522 | const service = yield* UsageService.make.pipe( | 522 | const service = yield* UsageService.make.pipe( |
| 523 | Effect.provide( | 523 | Effect.provide( |
| 524 | serviceLayers({ | 524 | layerService({ |
| 525 | prefix: "usage-service-accounts-test", | 525 | prefix: "usage-service-accounts-test", |
| 526 | home, | 526 | home, |
| 527 | settings: { | 527 | settings: { |
| ... | @@ -643,7 +643,7 @@ describe("UsageService", () => { | ... | @@ -643,7 +643,7 @@ describe("UsageService", () => { |
| 643 | ); | 643 | ); |
| 644 | }).pipe( | 644 | }).pipe( |
| 645 | Effect.provide( | 645 | Effect.provide( |
| 646 | serviceLayers({ | 646 | layerService({ |
| 647 | prefix: "usage-service-home-refresh-test", | 647 | prefix: "usage-service-home-refresh-test", |
| 648 | home, | 648 | home, |
| 649 | environment: { CLAUDE_CONFIG_DIR: NodePath.join(home, "host-ignored") }, | 649 | environment: { CLAUDE_CONFIG_DIR: NodePath.join(home, "host-ignored") }, |
| ... | @@ -673,7 +673,7 @@ describe("UsageService", () => { | ... | @@ -673,7 +673,7 @@ describe("UsageService", () => { |
| 673 | yield* Effect.promise(() => NodeFSP.writeFile(transcript, claudeLine(1, 5))); | 673 | yield* Effect.promise(() => NodeFSP.writeFile(transcript, claudeLine(1, 5))); |
| 674 | const service = yield* UsageService.make.pipe( | 674 | const service = yield* UsageService.make.pipe( |
| 675 | Effect.provide( | 675 | Effect.provide( |
| 676 | serviceLayers({ | 676 | layerService({ |
| 677 | prefix: "usage-service-inherited-homes-test", | 677 | prefix: "usage-service-inherited-homes-test", |
| 678 | home, | 678 | home, |
| 679 | environment: { | 679 | environment: { |
| ... | @@ -748,7 +748,7 @@ describe("UsageService", () => { | ... | @@ -748,7 +748,7 @@ describe("UsageService", () => { |
| 748 | assert.deepStrictEqual(restored.buckets, original.buckets); | 748 | assert.deepStrictEqual(restored.buckets, original.buckets); |
| 749 | }).pipe( | 749 | }).pipe( |
| 750 | Effect.provide( | 750 | Effect.provide( |
| 751 | serviceLayers({ prefix: "usage-service-price-overrides-test", home, settings }), | 751 | layerService({ prefix: "usage-service-price-overrides-test", home, settings }), |
| 752 | ), | 752 | ), |
| 753 | ); | 753 | ); |
| 754 | }).pipe(Effect.scoped), | 754 | }).pipe(Effect.scoped), |
| ... | @@ -760,7 +760,7 @@ describe("UsageService", () => { | ... | @@ -760,7 +760,7 @@ describe("UsageService", () => { |
| 760 | yield* Effect.promise(() => NodeFSP.writeFile(transcript, claudeLine(1, 5))); | 760 | yield* Effect.promise(() => NodeFSP.writeFile(transcript, claudeLine(1, 5))); |
| 761 | 761 | ||
| 762 | const service = yield* UsageService.make.pipe( | 762 | const service = yield* UsageService.make.pipe( |
| 763 | Effect.provide(serviceLayers({ prefix: "usage-service-grow-test", home, settings })), | 763 | Effect.provide(layerService({ prefix: "usage-service-grow-test", home, settings })), |
| 764 | ); | 764 | ); |
| 765 | 765 | ||
| 766 | const first = yield* service.readSummary(WINDOW); | 766 | const first = yield* service.readSummary(WINDOW); |
| ... | @@ -812,7 +812,7 @@ describe("UsageService", () => { | ... | @@ -812,7 +812,7 @@ describe("UsageService", () => { |
| 812 | ); | 812 | ); |
| 813 | }).pipe( | 813 | }).pipe( |
| 814 | Effect.provide( | 814 | Effect.provide( |
| 815 | serviceLayers({ | 815 | layerService({ |
| 816 | prefix: "usage-service-large-record-test", | 816 | prefix: "usage-service-large-record-test", |
| 817 | home, | 817 | home, |
| 818 | settings, | 818 | settings, |
| ... | @@ -899,7 +899,7 @@ describe("UsageService", () => { | ... | @@ -899,7 +899,7 @@ describe("UsageService", () => { |
| 899 | ); | 899 | ); |
| 900 | }).pipe( | 900 | }).pipe( |
| 901 | Effect.provide( | 901 | Effect.provide( |
| 902 | serviceLayers({ | 902 | layerService({ |
| 903 | prefix: "usage-service-v4-upgrade-test", | 903 | prefix: "usage-service-v4-upgrade-test", |
| 904 | home, | 904 | home, |
| 905 | settings, | 905 | settings, |
| ... | @@ -995,7 +995,7 @@ describe("UsageService", () => { | ... | @@ -995,7 +995,7 @@ describe("UsageService", () => { |
| 995 | assert.strictEqual(outsideWindow.sources[0]?.distinctSessions, 0); | 995 | assert.strictEqual(outsideWindow.sources[0]?.distinctSessions, 0); |
| 996 | }).pipe( | 996 | }).pipe( |
| 997 | Effect.provide( | 997 | Effect.provide( |
| 998 | serviceLayers({ | 998 | layerService({ |
| 999 | prefix: "usage-service-cleanup-test", | 999 | prefix: "usage-service-cleanup-test", |
| 1000 | home, | 1000 | home, |
| 1001 | settings: { providers: { ...settings.providers, claudeAgent: { homePath: alias } } }, | 1001 | settings: { providers: { ...settings.providers, claudeAgent: { homePath: alias } } }, |
| ... | @@ -1042,7 +1042,7 @@ describe("UsageService", () => { | ... | @@ -1042,7 +1042,7 @@ describe("UsageService", () => { |
| 1042 | const restored = yield* (yield* UsageService.make).readSummary(WINDOW); | 1042 | const restored = yield* (yield* UsageService.make).readSummary(WINDOW); |
| 1043 | assert.deepStrictEqual(restored.buckets, live.buckets); | 1043 | assert.deepStrictEqual(restored.buckets, live.buckets); |
| 1044 | }).pipe( | 1044 | }).pipe( |
| 1045 | Effect.provide(serviceLayers({ prefix: "usage-service-copy-order-test", home, settings })), | 1045 | Effect.provide(layerService({ prefix: "usage-service-copy-order-test", home, settings })), |
| 1046 | ); | 1046 | ); |
| 1047 | }).pipe(Effect.scoped), | 1047 | }).pipe(Effect.scoped), |
| 1048 | ); | 1048 | ); |
| ... | @@ -1073,9 +1073,7 @@ describe("UsageService", () => { | ... | @@ -1073,9 +1073,7 @@ describe("UsageService", () => { |
| 1073 | ); | 1073 | ); |
| 1074 | assert.strictEqual(totalOutputTokens(yield* service.readSummary(WINDOW)), 12); | 1074 | assert.strictEqual(totalOutputTokens(yield* service.readSummary(WINDOW)), 12); |
| 1075 | }).pipe( | 1075 | }).pipe( |
| 1076 | Effect.provide( | 1076 | Effect.provide(layerService({ prefix: "usage-service-stale-read-test", home, settings })), |
| 1077 | serviceLayers({ prefix: "usage-service-stale-read-test", home, settings }), | ||
| 1078 | ), | ||
| 1079 | ); | 1077 | ); |
| 1080 | }).pipe(Effect.scoped), | 1078 | }).pipe(Effect.scoped), |
| 1081 | ); | 1079 | ); |
| ... | @@ -1119,7 +1117,7 @@ describe("UsageService", () => { | ... | @@ -1119,7 +1117,7 @@ describe("UsageService", () => { |
| 1119 | ); | 1117 | ); |
| 1120 | assert.strictEqual(totalOutputTokens(yield* service.readSummary(WINDOW)), 12); | 1118 | assert.strictEqual(totalOutputTokens(yield* service.readSummary(WINDOW)), 12); |
| 1121 | }).pipe( | 1119 | }).pipe( |
| 1122 | Effect.provide(serviceLayers({ prefix: "usage-service-late-read-test", home, settings })), | 1120 | Effect.provide(layerService({ prefix: "usage-service-late-read-test", home, settings })), |
| 1123 | ); | 1121 | ); |
| 1124 | }).pipe(Effect.scoped), | 1122 | }).pipe(Effect.scoped), |
| 1125 | ); | 1123 | ); |
| ... | @@ -1133,9 +1131,7 @@ describe("UsageService", () => { | ... | @@ -1133,9 +1131,7 @@ describe("UsageService", () => { |
| 1133 | await NodeFSP.utimes(transcript, lastWrite, lastWrite); | 1131 | await NodeFSP.utimes(transcript, lastWrite, lastWrite); |
| 1134 | }); | 1132 | }); |
| 1135 | const service = yield* UsageService.make.pipe( | 1133 | const service = yield* UsageService.make.pipe( |
| 1136 | Effect.provide( | 1134 | Effect.provide(layerService({ prefix: "usage-service-saved-window-test", home, settings })), |
| 1137 | serviceLayers({ prefix: "usage-service-saved-window-test", home, settings }), | ||
| 1138 | ), | ||
| 1139 | ); | 1135 | ); |
| 1140 | const first = yield* service.readSummary(WINDOW); | 1136 | const first = yield* service.readSummary(WINDOW); |
| 1141 | yield* Effect.promise(() => | 1137 | yield* Effect.promise(() => |
| ... | @@ -1215,7 +1211,7 @@ describe("UsageService", () => { | ... | @@ -1215,7 +1211,7 @@ describe("UsageService", () => { |
| 1215 | assert.strictEqual(original.buckets[0]?.costUsd, 0); | 1211 | assert.strictEqual(original.buckets[0]?.costUsd, 0); |
| 1216 | assert.closeTo(updated.buckets[0]?.costUsd ?? -1, 0.00006, 1e-12); | 1212 | assert.closeTo(updated.buckets[0]?.costUsd ?? -1, 0.00006, 1e-12); |
| 1217 | }).pipe( | 1213 | }).pipe( |
| 1218 | Effect.provide(serviceLayers({ prefix: "usage-service-price-race-test", home, settings })), | 1214 | Effect.provide(layerService({ prefix: "usage-service-price-race-test", home, settings })), |
| 1219 | ); | 1215 | ); |
| 1220 | }).pipe(Effect.scoped), | 1216 | }).pipe(Effect.scoped), |
| 1221 | ); | 1217 | ); |
| ... | @@ -1228,7 +1224,7 @@ describe("UsageService", () => { | ... | @@ -1228,7 +1224,7 @@ describe("UsageService", () => { |
| 1228 | let ratesFetches = 0; | 1224 | let ratesFetches = 0; |
| 1229 | const service = yield* UsageService.make.pipe( | 1225 | const service = yield* UsageService.make.pipe( |
| 1230 | Effect.provide( | 1226 | Effect.provide( |
| 1231 | serviceLayers({ | 1227 | layerService({ |
| 1232 | prefix: "usage-service-flight-test", | 1228 | prefix: "usage-service-flight-test", |
| 1233 | home, | 1229 | home, |
| 1234 | settings, | 1230 | settings, |
| ... | @@ -1260,7 +1256,7 @@ describe("UsageService", () => { | ... | @@ -1260,7 +1256,7 @@ describe("UsageService", () => { |
| 1260 | let ratesFetches = 0; | 1256 | let ratesFetches = 0; |
| 1261 | const service = yield* UsageService.make.pipe( | 1257 | const service = yield* UsageService.make.pipe( |
| 1262 | Effect.provide( | 1258 | Effect.provide( |
| 1263 | serviceLayers({ | 1259 | layerService({ |
| 1264 | prefix: "usage-service-rates-refresh-test", | 1260 | prefix: "usage-service-rates-refresh-test", |
| 1265 | home, | 1261 | home, |
| 1266 | settings, | 1262 | settings, |
| ... | @@ -1298,9 +1294,7 @@ describe("UsageService", () => { | ... | @@ -1298,9 +1294,7 @@ describe("UsageService", () => { |
| 1298 | Effect.gen(function* () { | 1294 | Effect.gen(function* () { |
| 1299 | const { settings, home } = yield* setup; | 1295 | const { settings, home } = yield* setup; |
| 1300 | const service = yield* UsageService.make.pipe( | 1296 | const service = yield* UsageService.make.pipe( |
| 1301 | Effect.provide( | 1297 | Effect.provide(layerService({ prefix: "usage-service-interruption-test", home, settings })), |
| 1302 | serviceLayers({ prefix: "usage-service-interruption-test", home, settings }), | ||
| 1303 | ), | ||
| 1304 | ); | 1298 | ); |
| 1305 | 1299 | ||
| 1306 | let orphanedAt: number | undefined; | 1300 | let orphanedAt: number | undefined; |
apps/server/src/vcs/GitVcsDriver.test.ts+24-24| ... | @@ -24,16 +24,16 @@ import * as VcsDriverRegistry from "./VcsDriverRegistry.ts"; | ... | @@ -24,16 +24,16 @@ import * as VcsDriverRegistry from "./VcsDriverRegistry.ts"; |
| 24 | import * as VcsProcess from "./VcsProcess.ts"; | 24 | import * as VcsProcess from "./VcsProcess.ts"; |
| 25 | import { runVcsDriverContractSuite } from "./testing/VcsDriverContractHarness.ts"; | 25 | import { runVcsDriverContractSuite } from "./testing/VcsDriverContractHarness.ts"; |
| 26 | 26 | ||
| 27 | const ServerConfigLayer = ServerConfig.layerTest(process.cwd(), { | 27 | const layerServerConfig = ServerConfig.layerTest(process.cwd(), { |
| 28 | prefix: "t3-git-vcs-contract-", | 28 | prefix: "t3-git-vcs-contract-", |
| 29 | }); | 29 | }); |
| 30 | const GitContractLayer = Layer.mergeAll(GitVcsDriver.vcsLayer, GitVcsDriver.layer).pipe( | 30 | const layerGitContract = Layer.mergeAll(GitVcsDriver.layerVcs, GitVcsDriver.layer).pipe( |
| 31 | Layer.provide(ServerConfigLayer), | 31 | Layer.provide(layerServerConfig), |
| 32 | Layer.provideMerge(VcsProcess.layer), | 32 | Layer.provideMerge(VcsProcess.layer), |
| 33 | Layer.provideMerge(NodeServices.layer), | 33 | Layer.provideMerge(NodeServices.layer), |
| 34 | ); | 34 | ); |
| 35 | const GitCaptureContractLayer = Layer.merge( | 35 | const layerGitCaptureContract = Layer.merge( |
| 36 | GitContractLayer, | 36 | layerGitContract, |
| 37 | ProcessRunner.layer.pipe(Layer.provide(NodeServices.layer)), | 37 | ProcessRunner.layer.pipe(Layer.provide(NodeServices.layer)), |
| 38 | ); | 38 | ); |
| 39 | 39 | ||
| ... | @@ -69,7 +69,7 @@ type GitContractError = GitCommandError | PlatformError.PlatformError; | ... | @@ -69,7 +69,7 @@ type GitContractError = GitCommandError | PlatformError.PlatformError; |
| 69 | runVcsDriverContractSuite<GitVcsDriver.GitVcsDriver, GitContractError>({ | 69 | runVcsDriverContractSuite<GitVcsDriver.GitVcsDriver, GitContractError>({ |
| 70 | name: "Git", | 70 | name: "Git", |
| 71 | kind: "git", | 71 | kind: "git", |
| 72 | layer: GitContractLayer, | 72 | layer: layerGitContract, |
| 73 | fixture: { | 73 | fixture: { |
| 74 | createRepo: (cwd) => | 74 | createRepo: (cwd) => |
| 75 | Effect.gen(function* () { | 75 | Effect.gen(function* () { |
| ... | @@ -160,7 +160,7 @@ it.effect("checkpoint capture skips untracked nested repositories without a comm | ... | @@ -160,7 +160,7 @@ it.effect("checkpoint capture skips untracked nested repositories without a comm |
| 160 | yield* fileSystem.readFileString(path.join(cwd, nested, "private.txt")), | 160 | yield* fileSystem.readFileString(path.join(cwd, nested, "private.txt")), |
| 161 | "nested\n", | 161 | "nested\n", |
| 162 | ); | 162 | ); |
| 163 | }).pipe(Effect.scoped, Effect.provide(GitContractLayer)), | 163 | }).pipe(Effect.scoped, Effect.provide(layerGitContract)), |
| 164 | ); | 164 | ); |
| 165 | 165 | ||
| 166 | it.effect("checkpoint recovery discovers nested HEAD independently of inherited GIT_DIR", () => | 166 | it.effect("checkpoint recovery discovers nested HEAD independently of inherited GIT_DIR", () => |
| ... | @@ -188,7 +188,7 @@ it.effect("checkpoint recovery discovers nested HEAD independently of inherited | ... | @@ -188,7 +188,7 @@ it.effect("checkpoint recovery discovers nested HEAD independently of inherited |
| 188 | assert.strictEqual((yield* git(["show", `${checkpointRef}:file.txt`])).stdout, "unstaged\n"); | 188 | assert.strictEqual((yield* git(["show", `${checkpointRef}:file.txt`])).stdout, "unstaged\n"); |
| 189 | assert.strictEqual((yield* git(["ls-tree", "-r", checkpointRef, "--", "empty"])).stdout, ""); | 189 | assert.strictEqual((yield* git(["ls-tree", "-r", checkpointRef, "--", "empty"])).stdout, ""); |
| 190 | assert.deepEqual(yield* fs.readFile(path.join(cwd, ".git", "index")), originalIndex); | 190 | assert.deepEqual(yield* fs.readFile(path.join(cwd, ".git", "index")), originalIndex); |
| 191 | }).pipe(Effect.scoped, Effect.provide(GitContractLayer)), | 191 | }).pipe(Effect.scoped, Effect.provide(layerGitContract)), |
| 192 | ); | 192 | ); |
| 193 | 193 | ||
| 194 | it.effect("checkpoint capture still fails when a clean filter rejects a file", () => | 194 | it.effect("checkpoint capture still fails when a clean filter rejects a file", () => |
| ... | @@ -212,7 +212,7 @@ it.effect("checkpoint capture still fails when a clean filter rejects a file", ( | ... | @@ -212,7 +212,7 @@ it.effect("checkpoint capture still fails when a clean filter rejects a file", ( |
| 212 | assert.strictEqual(result._tag, "Failure"); | 212 | assert.strictEqual(result._tag, "Failure"); |
| 213 | assert.deepEqual(yield* fileSystem.readFile(path.join(cwd, ".git", "index")), originalIndex); | 213 | assert.deepEqual(yield* fileSystem.readFile(path.join(cwd, ".git", "index")), originalIndex); |
| 214 | assert.isFalse(yield* driver.checkpoints.hasCheckpointRef({ cwd, checkpointRef })); | 214 | assert.isFalse(yield* driver.checkpoints.hasCheckpointRef({ cwd, checkpointRef })); |
| 215 | }).pipe(Effect.scoped, Effect.provide(GitContractLayer)), | 215 | }).pipe(Effect.scoped, Effect.provide(layerGitContract)), |
| 216 | ); | 216 | ); |
| 217 | 217 | ||
| 218 | it.effect("checkpoint capture refuses a truncated nested repository listing", () => | 218 | it.effect("checkpoint capture refuses a truncated nested repository listing", () => |
| ... | @@ -242,7 +242,7 @@ it.effect("checkpoint capture refuses a truncated nested repository listing", () | ... | @@ -242,7 +242,7 @@ it.effect("checkpoint capture refuses a truncated nested repository listing", () |
| 242 | 242 | ||
| 243 | assert.strictEqual(result._tag, "Failure"); | 243 | assert.strictEqual(result._tag, "Failure"); |
| 244 | assert.isFalse(yield* driver.checkpoints.hasCheckpointRef({ cwd, checkpointRef })); | 244 | assert.isFalse(yield* driver.checkpoints.hasCheckpointRef({ cwd, checkpointRef })); |
| 245 | }).pipe(Effect.scoped, Effect.provide(GitContractLayer)), | 245 | }).pipe(Effect.scoped, Effect.provide(layerGitContract)), |
| 246 | ); | 246 | ); |
| 247 | 247 | ||
| 248 | it.effect("checkpoint recovery refuses excessive candidates before probing", () => | 248 | it.effect("checkpoint recovery refuses excessive candidates before probing", () => |
| ... | @@ -283,7 +283,7 @@ it.effect("checkpoint recovery refuses excessive candidates before probing", () | ... | @@ -283,7 +283,7 @@ it.effect("checkpoint recovery refuses excessive candidates before probing", () |
| 283 | if (result._tag === "Failure") assert.strictEqual(result.failure, stageError); | 283 | if (result._tag === "Failure") assert.strictEqual(result.failure, stageError); |
| 284 | assert.isFalse(yield* driver.checkpoints.hasCheckpointRef({ cwd, checkpointRef })); | 284 | assert.isFalse(yield* driver.checkpoints.hasCheckpointRef({ cwd, checkpointRef })); |
| 285 | assert.deepEqual(yield* fs.readFile(path.join(cwd, ".git", "index")), originalIndex); | 285 | assert.deepEqual(yield* fs.readFile(path.join(cwd, ".git", "index")), originalIndex); |
| 286 | }).pipe(Effect.scoped, Effect.provide(GitContractLayer)), | 286 | }).pipe(Effect.scoped, Effect.provide(layerGitContract)), |
| 287 | ); | 287 | ); |
| 288 | 288 | ||
| 289 | it.effect.each([ | 289 | it.effect.each([ |
| ... | @@ -389,7 +389,7 @@ it.effect.each([ | ... | @@ -389,7 +389,7 @@ it.effect.each([ |
| 389 | assert.isFalse(yield* fs.exists(`${index}.lock`)); | 389 | assert.isFalse(yield* fs.exists(`${index}.lock`)); |
| 390 | } | 390 | } |
| 391 | assert.deepEqual(yield* fs.readFile(path.join(cwd, ".git", "index")), originalIndex); | 391 | assert.deepEqual(yield* fs.readFile(path.join(cwd, ".git", "index")), originalIndex); |
| 392 | }).pipe(Effect.scoped, Effect.provide(GitCaptureContractLayer)), | 392 | }).pipe(Effect.scoped, Effect.provide(layerGitCaptureContract)), |
| 393 | ); | 393 | ); |
| 394 | 394 | ||
| 395 | it.effect.each(["discovery", "probe", "retry"] as const)( | 395 | it.effect.each(["discovery", "probe", "retry"] as const)( |
| ... | @@ -478,7 +478,7 @@ it.effect.each(["discovery", "probe", "retry"] as const)( | ... | @@ -478,7 +478,7 @@ it.effect.each(["discovery", "probe", "retry"] as const)( |
| 478 | assert.isFalse(yield* fs.exists(`${privateIndex!}.lock`)); | 478 | assert.isFalse(yield* fs.exists(`${privateIndex!}.lock`)); |
| 479 | assert.isFalse(yield* driver.checkpoints.hasCheckpointRef({ cwd, checkpointRef })); | 479 | assert.isFalse(yield* driver.checkpoints.hasCheckpointRef({ cwd, checkpointRef })); |
| 480 | assert.deepEqual(yield* fs.readFile(path.join(cwd, ".git", "index")), originalIndex); | 480 | assert.deepEqual(yield* fs.readFile(path.join(cwd, ".git", "index")), originalIndex); |
| 481 | }).pipe(Effect.scoped, Effect.provide(GitContractLayer)), | 481 | }).pipe(Effect.scoped, Effect.provide(layerGitContract)), |
| 482 | ); | 482 | ); |
| 483 | 483 | ||
| 484 | it.effect("checkpoint recovery preserves interruption and removes the private index", () => | 484 | it.effect("checkpoint recovery preserves interruption and removes the private index", () => |
| ... | @@ -512,7 +512,7 @@ it.effect("checkpoint recovery preserves interruption and removes the private in | ... | @@ -512,7 +512,7 @@ it.effect("checkpoint recovery preserves interruption and removes the private in |
| 512 | assert.isDefined(privateIndex); | 512 | assert.isDefined(privateIndex); |
| 513 | assert.isFalse(yield* fs.exists(privateIndex!)); | 513 | assert.isFalse(yield* fs.exists(privateIndex!)); |
| 514 | assert.isFalse(yield* driver.checkpoints.hasCheckpointRef({ cwd, checkpointRef })); | 514 | assert.isFalse(yield* driver.checkpoints.hasCheckpointRef({ cwd, checkpointRef })); |
| 515 | }).pipe(Effect.scoped, Effect.provide(GitContractLayer)), | 515 | }).pipe(Effect.scoped, Effect.provide(layerGitContract)), |
| 516 | ); | 516 | ); |
| 517 | 517 | ||
| 518 | it.effect("checkpoint capture does not rerun clean filters for unchanged indexed files", () => | 518 | it.effect("checkpoint capture does not rerun clean filters for unchanged indexed files", () => |
| ... | @@ -545,7 +545,7 @@ it.effect("checkpoint capture does not rerun clean filters for unchanged indexed | ... | @@ -545,7 +545,7 @@ it.effect("checkpoint capture does not rerun clean filters for unchanged indexed |
| 545 | assert.strictEqual((yield* git(["show", `${checkpointRef}:file.txt`])).stdout, "changed\n"); | 545 | assert.strictEqual((yield* git(["show", `${checkpointRef}:file.txt`])).stdout, "changed\n"); |
| 546 | assert.strictEqual((yield* git(["show", `${checkpointRef}:stable.txt`])).stdout, "unchanged\n"); | 546 | assert.strictEqual((yield* git(["show", `${checkpointRef}:stable.txt`])).stdout, "unchanged\n"); |
| 547 | assert.deepEqual(yield* fileSystem.readFile(path.join(cwd, ".git", "index")), originalIndex); | 547 | assert.deepEqual(yield* fileSystem.readFile(path.join(cwd, ".git", "index")), originalIndex); |
| 548 | }).pipe(Effect.scoped, Effect.provide(GitContractLayer)), | 548 | }).pipe(Effect.scoped, Effect.provide(layerGitContract)), |
| 549 | ); | 549 | ); |
| 550 | 550 | ||
| 551 | it.effect.each( | 551 | it.effect.each( |
| ... | @@ -643,7 +643,7 @@ it.effect.each( | ... | @@ -643,7 +643,7 @@ it.effect.each( |
| 643 | "working outside\n", | 643 | "working outside\n", |
| 644 | ); | 644 | ); |
| 645 | } | 645 | } |
| 646 | }).pipe(Effect.scoped, Effect.provide(GitContractLayer)), | 646 | }).pipe(Effect.scoped, Effect.provide(layerGitContract)), |
| 647 | ); | 647 | ); |
| 648 | 648 | ||
| 649 | it.effect("checkpoint capture keeps the legacy path when Git lacks add --sparse", () => | 649 | it.effect("checkpoint capture keeps the legacy path when Git lacks add --sparse", () => |
| ... | @@ -683,7 +683,7 @@ it.effect("checkpoint capture keeps the legacy path when Git lacks add --sparse" | ... | @@ -683,7 +683,7 @@ it.effect("checkpoint capture keeps the legacy path when Git lacks add --sparse" |
| 683 | yield* captureDriver.checkpoints.captureCheckpoint({ cwd, checkpointRef }); | 683 | yield* captureDriver.checkpoints.captureCheckpoint({ cwd, checkpointRef }); |
| 684 | assert.strictEqual((yield* git(["show", `${checkpointRef}:file.txt`])).stdout, "unstaged\n"); | 684 | assert.strictEqual((yield* git(["show", `${checkpointRef}:file.txt`])).stdout, "unstaged\n"); |
| 685 | assert.deepEqual(yield* fs.readFile(path.join(cwd, ".git/index")), originalIndex); | 685 | assert.deepEqual(yield* fs.readFile(path.join(cwd, ".git/index")), originalIndex); |
| 686 | }).pipe(Effect.scoped, Effect.provide(GitContractLayer)), | 686 | }).pipe(Effect.scoped, Effect.provide(layerGitContract)), |
| 687 | ); | 687 | ); |
| 688 | 688 | ||
| 689 | it.effect.each(["normal", "flags", "sparse"] as const)( | 689 | it.effect.each(["normal", "flags", "sparse"] as const)( |
| ... | @@ -737,7 +737,7 @@ it.effect.each(["normal", "flags", "sparse"] as const)( | ... | @@ -737,7 +737,7 @@ it.effect.each(["normal", "flags", "sparse"] as const)( |
| 737 | if (indexMode !== "flags") | 737 | if (indexMode !== "flags") |
| 738 | assert.strictEqual(yield* fs.readFileString(path.join(cwd, ".git/reads")), ""); | 738 | assert.strictEqual(yield* fs.readFileString(path.join(cwd, ".git/reads")), ""); |
| 739 | assert.deepEqual(yield* fs.readFile(path.join(cwd, ".git/index")), originalIndex); | 739 | assert.deepEqual(yield* fs.readFile(path.join(cwd, ".git/index")), originalIndex); |
| 740 | }).pipe(Effect.scoped, Effect.provide(GitContractLayer)), | 740 | }).pipe(Effect.scoped, Effect.provide(layerGitContract)), |
| 741 | ); | 741 | ); |
| 742 | 742 | ||
| 743 | it.effect.each([1_700_000_000, 1_700_000_000.9999])( | 743 | it.effect.each([1_700_000_000, 1_700_000_000.9999])( |
| ... | @@ -767,7 +767,7 @@ it.effect.each([1_700_000_000, 1_700_000_000.9999])( | ... | @@ -767,7 +767,7 @@ it.effect.each([1_700_000_000, 1_700_000_000.9999])( |
| 767 | assert.strictEqual((yield* git(["show", `${checkpointRef}:file.txt`])).stdout, "after!\n"); | 767 | assert.strictEqual((yield* git(["show", `${checkpointRef}:file.txt`])).stdout, "after!\n"); |
| 768 | assert.deepEqual(yield* fileSystem.readFile(indexPath), originalIndex); | 768 | assert.deepEqual(yield* fileSystem.readFile(indexPath), originalIndex); |
| 769 | assert.deepEqual((yield* fileSystem.stat(indexPath)).mtime, originalIndexMtime); | 769 | assert.deepEqual((yield* fileSystem.stat(indexPath)).mtime, originalIndexMtime); |
| 770 | }).pipe(Effect.scoped, Effect.provide(GitContractLayer)), | 770 | }).pipe(Effect.scoped, Effect.provide(layerGitContract)), |
| 771 | ); | 771 | ); |
| 772 | it.effect.each( | 772 | it.effect.each( |
| 773 | [false, true].flatMap((nested) => | 773 | [false, true].flatMap((nested) => |
| ... | @@ -864,7 +864,7 @@ it.effect.each( | ... | @@ -864,7 +864,7 @@ it.effect.each( |
| 864 | "working outside\n", | 864 | "working outside\n", |
| 865 | ); | 865 | ); |
| 866 | } | 866 | } |
| 867 | }).pipe(Effect.scoped, Effect.provide(GitContractLayer)), | 867 | }).pipe(Effect.scoped, Effect.provide(layerGitContract)), |
| 868 | ); | 868 | ); |
| 869 | 869 | ||
| 870 | it.effect("checkpoint capture preserves racy edits made after resetting the index", () => | 870 | it.effect("checkpoint capture preserves racy edits made after resetting the index", () => |
| ... | @@ -907,7 +907,7 @@ it.effect("checkpoint capture preserves racy edits made after resetting the inde | ... | @@ -907,7 +907,7 @@ it.effect("checkpoint capture preserves racy edits made after resetting the inde |
| 907 | assert.strictEqual((yield* git(["show", `${checkpointRef}:file.txt`])).stdout, "staged\n"); | 907 | assert.strictEqual((yield* git(["show", `${checkpointRef}:file.txt`])).stdout, "staged\n"); |
| 908 | assert.deepEqual(yield* fileSystem.readFile(indexPath), originalIndex); | 908 | assert.deepEqual(yield* fileSystem.readFile(indexPath), originalIndex); |
| 909 | assert.deepEqual((yield* fileSystem.stat(indexPath)).mtime, originalIndexMtime); | 909 | assert.deepEqual((yield* fileSystem.stat(indexPath)).mtime, originalIndexMtime); |
| 910 | }).pipe(Effect.scoped, Effect.provide(GitContractLayer)), | 910 | }).pipe(Effect.scoped, Effect.provide(layerGitContract)), |
| 911 | ); | 911 | ); |
| 912 | 912 | ||
| 913 | it.effect.each( | 913 | it.effect.each( |
| ... | @@ -985,7 +985,7 @@ it.effect.each( | ... | @@ -985,7 +985,7 @@ it.effect.each( |
| 985 | "added in second turn\n", | 985 | "added in second turn\n", |
| 986 | ); | 986 | ); |
| 987 | assert.deepEqual(yield* fileSystem.readFile(path.join(cwd, ".git", "index")), originalIndex); | 987 | assert.deepEqual(yield* fileSystem.readFile(path.join(cwd, ".git", "index")), originalIndex); |
| 988 | }).pipe(Effect.scoped, Effect.provide(GitContractLayer)), | 988 | }).pipe(Effect.scoped, Effect.provide(layerGitContract)), |
| 989 | ); | 989 | ); |
| 990 | 990 | ||
| 991 | it.effect.each(["missing", "invalid"] as const)( | 991 | it.effect.each(["missing", "invalid"] as const)( |
| ... | @@ -1012,7 +1012,7 @@ it.effect.each(["missing", "invalid"] as const)( | ... | @@ -1012,7 +1012,7 @@ it.effect.each(["missing", "invalid"] as const)( |
| 1012 | } else { | 1012 | } else { |
| 1013 | assert.strictEqual(yield* fileSystem.readFileString(indexPath), "invalid index"); | 1013 | assert.strictEqual(yield* fileSystem.readFileString(indexPath), "invalid index"); |
| 1014 | } | 1014 | } |
| 1015 | }).pipe(Effect.scoped, Effect.provide(GitContractLayer)), | 1015 | }).pipe(Effect.scoped, Effect.provide(layerGitContract)), |
| 1016 | ); | 1016 | ); |
| 1017 | 1017 | ||
| 1018 | it.effect("restores empty checkpoints without changing paths outside the workspace", () => | 1018 | it.effect("restores empty checkpoints without changing paths outside the workspace", () => |
| ... | @@ -1076,7 +1076,7 @@ it.effect("restores empty checkpoints without changing paths outside the workspa | ... | @@ -1076,7 +1076,7 @@ it.effect("restores empty checkpoints without changing paths outside the workspa |
| 1076 | assert.strictEqual(staged.stdout.trim(), "outside.txt"); | 1076 | assert.strictEqual(staged.stdout.trim(), "outside.txt"); |
| 1077 | } | 1077 | } |
| 1078 | } | 1078 | } |
| 1079 | }).pipe(Effect.scoped, Effect.provide(GitContractLayer)), | 1079 | }).pipe(Effect.scoped, Effect.provide(layerGitContract)), |
| 1080 | ); | 1080 | ); |
| 1081 | 1081 | ||
| 1082 | it.effect("GitVcsDriver forwards execute env to the VCS process", () => { | 1082 | it.effect("GitVcsDriver forwards execute env to the VCS process", () => { |
apps/server/src/vcs/GitVcsDriver.ts+1-1| ... | @@ -1257,5 +1257,5 @@ export const make = Effect.gen(function* () { | ... | @@ -1257,5 +1257,5 @@ export const make = Effect.gen(function* () { |
| 1257 | return GitVcsDriver.of(git); | 1257 | return GitVcsDriver.of(git); |
| 1258 | }); | 1258 | }); |
| 1259 | 1259 | ||
| 1260 | export const vcsLayer = Layer.effect(VcsDriver.VcsDriver, makeVcsDriver); | 1260 | export const layerVcs = Layer.effect(VcsDriver.VcsDriver, makeVcsDriver); |
| 1261 | export const layer = Layer.effect(GitVcsDriver, make); | 1261 | export const layer = Layer.effect(GitVcsDriver, make); |
apps/server/src/vcs/GitVcsDriverCore.test.ts+28-28| ... | @@ -40,11 +40,11 @@ import * as GitVcsDriver from "./GitVcsDriver.ts"; | ... | @@ -40,11 +40,11 @@ import * as GitVcsDriver from "./GitVcsDriver.ts"; |
| 40 | 40 | ||
| 41 | const encodeGitCommandError = Schema.encodeEffect(Schema.fromJsonString(GitCommandError)); | 41 | const encodeGitCommandError = Schema.encodeEffect(Schema.fromJsonString(GitCommandError)); |
| 42 | 42 | ||
| 43 | const ServerConfigLayer = ServerConfig.layerTest(process.cwd(), { | 43 | const layerServerConfig = ServerConfig.layerTest(process.cwd(), { |
| 44 | prefix: "t3-git-vcs-driver-test-", | 44 | prefix: "t3-git-vcs-driver-test-", |
| 45 | }); | 45 | }); |
| 46 | const TestLayer = GitVcsDriver.layer.pipe( | 46 | const layerTest = GitVcsDriver.layer.pipe( |
| 47 | Layer.provide(ServerConfigLayer), | 47 | Layer.provide(layerServerConfig), |
| 48 | Layer.provideMerge(NodeServices.layer), | 48 | Layer.provideMerge(NodeServices.layer), |
| 49 | ); | 49 | ); |
| 50 | 50 | ||
| ... | @@ -201,7 +201,7 @@ it.effect("bounds Git bursts across drivers without timing out queued commands", | ... | @@ -201,7 +201,7 @@ it.effect("bounds Git bursts across drivers without timing out queued commands", |
| 201 | ); | 201 | ); |
| 202 | assert.equal(duration.count, 16); | 202 | assert.equal(duration.count, 16); |
| 203 | assert.equal(duration.sum, 16_000); | 203 | assert.equal(duration.sum, 16_000); |
| 204 | }).pipe(Effect.provide(ServerConfigLayer.pipe(Layer.provideMerge(NodeServices.layer)))), | 204 | }).pipe(Effect.provide(layerServerConfig.pipe(Layer.provideMerge(NodeServices.layer)))), |
| 205 | ); | 205 | ); |
| 206 | 206 | ||
| 207 | it.effect.each([{ timeoutMs: null }, { timeoutMs: 30_001 }])( | 207 | it.effect.each([{ timeoutMs: null }, { timeoutMs: 30_001 }])( |
| ... | @@ -252,7 +252,7 @@ it.effect.each([{ timeoutMs: null }, { timeoutMs: 30_001 }])( | ... | @@ -252,7 +252,7 @@ it.effect.each([{ timeoutMs: null }, { timeoutMs: 30_001 }])( |
| 252 | yield* Deferred.succeed(slowGate, undefined); | 252 | yield* Deferred.succeed(slowGate, undefined); |
| 253 | assert.equal((yield* Fiber.join(slow)).stdout, "ok"); | 253 | assert.equal((yield* Fiber.join(slow)).stdout, "ok"); |
| 254 | assert.equal(active, 0); | 254 | assert.equal(active, 0); |
| 255 | }).pipe(Effect.provide(ServerConfigLayer.pipe(Layer.provideMerge(NodeServices.layer)))), | 255 | }).pipe(Effect.provide(layerServerConfig.pipe(Layer.provideMerge(NodeServices.layer)))), |
| 256 | ); | 256 | ); |
| 257 | 257 | ||
| 258 | it.effect.each(["root", "nested", "worktree"] as const)( | 258 | it.effect.each(["root", "nested", "worktree"] as const)( |
| ... | @@ -295,7 +295,7 @@ it.effect.each(["root", "nested", "worktree"] as const)( | ... | @@ -295,7 +295,7 @@ it.effect.each(["root", "nested", "worktree"] as const)( |
| 295 | const status = yield* driver.statusDetailsLocal(statusCwd); | 295 | const status = yield* driver.statusDetailsLocal(statusCwd); |
| 296 | assert.isFalse(status.hasWorkingTreeChanges); | 296 | assert.isFalse(status.hasWorkingTreeChanges); |
| 297 | assert.include(yield* fs.readFileString(runsPath), "clean"); | 297 | assert.include(yield* fs.readFileString(runsPath), "clean"); |
| 298 | }).pipe(Effect.provide(TestLayer)), | 298 | }).pipe(Effect.provide(layerTest)), |
| 299 | ); | 299 | ); |
| 300 | 300 | ||
| 301 | it.effect("uses stable diagnostics for every parsed non-repository command", () => { | 301 | it.effect("uses stable diagnostics for every parsed non-repository command", () => { |
| ... | @@ -312,13 +312,13 @@ it.effect("uses stable diagnostics for every parsed non-repository command", () | ... | @@ -312,13 +312,13 @@ it.effect("uses stable diagnostics for every parsed non-repository command", () |
| 312 | return makeNonRepositoryHandle(); | 312 | return makeNonRepositoryHandle(); |
| 313 | }), | 313 | }), |
| 314 | ); | 314 | ); |
| 315 | const nodeServicesLayer = Layer.merge( | 315 | const layerNodeServices = Layer.merge( |
| 316 | NodeServices.layer, | 316 | NodeServices.layer, |
| 317 | Layer.succeed(ChildProcessSpawner.ChildProcessSpawner, spawner), | 317 | Layer.succeed(ChildProcessSpawner.ChildProcessSpawner, spawner), |
| 318 | ); | 318 | ); |
| 319 | const layer = GitVcsDriver.layer.pipe( | 319 | const layer = GitVcsDriver.layer.pipe( |
| 320 | Layer.provide(ServerConfigLayer), | 320 | Layer.provide(layerServerConfig), |
| 321 | Layer.provideMerge(nodeServicesLayer), | 321 | Layer.provideMerge(layerNodeServices), |
| 322 | ); | 322 | ); |
| 323 | 323 | ||
| 324 | return Effect.gen(function* () { | 324 | return Effect.gen(function* () { |
| ... | @@ -353,7 +353,7 @@ it.effect("invalidates origin remote cache when a driver mutation adds origin", | ... | @@ -353,7 +353,7 @@ it.effect("invalidates origin remote cache when a driver mutation adds origin", |
| 353 | 353 | ||
| 354 | const after = yield* driver.statusDetailsLocal(cwd); | 354 | const after = yield* driver.statusDetailsLocal(cwd); |
| 355 | assert.equal(after.hasOriginRemote, true); | 355 | assert.equal(after.hasOriginRemote, true); |
| 356 | }).pipe(Effect.provide(TestLayer)), | 356 | }).pipe(Effect.provide(layerTest)), |
| 357 | ); | 357 | ); |
| 358 | 358 | ||
| 359 | it.effect("re-reads origin remote status after cache TTL expiry and bypassed invalidation", () => | 359 | it.effect("re-reads origin remote status after cache TTL expiry and bypassed invalidation", () => |
| ... | @@ -380,7 +380,7 @@ it.effect("re-reads origin remote status after cache TTL expiry and bypassed inv | ... | @@ -380,7 +380,7 @@ it.effect("re-reads origin remote status after cache TTL expiry and bypassed inv |
| 380 | // After expiry, the next call re-executes and picks up the remote | 380 | // After expiry, the next call re-executes and picks up the remote |
| 381 | const afterExpiry = yield* driver.statusDetailsLocal(cwd); | 381 | const afterExpiry = yield* driver.statusDetailsLocal(cwd); |
| 382 | assert.equal(afterExpiry.hasOriginRemote, true); | 382 | assert.equal(afterExpiry.hasOriginRemote, true); |
| 383 | }).pipe(Effect.provide(TestLayer)), | 383 | }).pipe(Effect.provide(layerTest)), |
| 384 | ); | 384 | ); |
| 385 | 385 | ||
| 386 | it.effect("coalesces concurrent ref pages into one repository snapshot", () => | 386 | it.effect("coalesces concurrent ref pages into one repository snapshot", () => |
| ... | @@ -492,7 +492,7 @@ it.effect("coalesces concurrent ref pages into one repository snapshot", () => | ... | @@ -492,7 +492,7 @@ it.effect("coalesces concurrent ref pages into one repository snapshot", () => |
| 492 | 2, | 492 | 2, |
| 493 | ); | 493 | ); |
| 494 | }), | 494 | }), |
| 495 | ).pipe(Effect.provide(ServerConfigLayer.pipe(Layer.provideMerge(NodeServices.layer)))), | 495 | ).pipe(Effect.provide(layerServerConfig.pipe(Layer.provideMerge(NodeServices.layer)))), |
| 496 | ); | 496 | ); |
| 497 | 497 | ||
| 498 | it.effect("retries an in-flight ref snapshot invalidated by a mutation", () => | 498 | it.effect("retries an in-flight ref snapshot invalidated by a mutation", () => |
| ... | @@ -551,7 +551,7 @@ it.effect("retries an in-flight ref snapshot invalidated by a mutation", () => | ... | @@ -551,7 +551,7 @@ it.effect("retries an in-flight ref snapshot invalidated by a mutation", () => |
| 551 | assert.isTrue(refs.refs.some((ref) => ref.name === "feature/during-refresh")); | 551 | assert.isTrue(refs.refs.some((ref) => ref.name === "feature/during-refresh")); |
| 552 | assert.equal(yield* Ref.get(refScans), 2); | 552 | assert.equal(yield* Ref.get(refScans), 2); |
| 553 | }), | 553 | }), |
| 554 | ).pipe(Effect.provide(ServerConfigLayer.pipe(Layer.provideMerge(NodeServices.layer)))), | 554 | ).pipe(Effect.provide(layerServerConfig.pipe(Layer.provideMerge(NodeServices.layer)))), |
| 555 | ); | 555 | ); |
| 556 | 556 | ||
| 557 | it.effect("invalidates a ref snapshot when a mutation fails after changing Git", () => | 557 | it.effect("invalidates a ref snapshot when a mutation fails after changing Git", () => |
| ... | @@ -583,7 +583,7 @@ it.effect("invalidates a ref snapshot when a mutation fails after changing Git", | ... | @@ -583,7 +583,7 @@ it.effect("invalidates a ref snapshot when a mutation fails after changing Git", |
| 583 | const refs = yield* driver.listRefs({ cwd }); | 583 | const refs = yield* driver.listRefs({ cwd }); |
| 584 | assert.isTrue(refs.refs.some((ref) => ref.name === "feature/partial-failure")); | 584 | assert.isTrue(refs.refs.some((ref) => ref.name === "feature/partial-failure")); |
| 585 | }), | 585 | }), |
| 586 | ).pipe(Effect.provide(ServerConfigLayer.pipe(Layer.provideMerge(NodeServices.layer)))), | 586 | ).pipe(Effect.provide(layerServerConfig.pipe(Layer.provideMerge(NodeServices.layer)))), |
| 587 | ); | 587 | ); |
| 588 | 588 | ||
| 589 | it.effect("fails a ref snapshot when for-each-ref exits unsuccessfully", () => | 589 | it.effect("fails a ref snapshot when for-each-ref exits unsuccessfully", () => |
| ... | @@ -619,7 +619,7 @@ it.effect("fails a ref snapshot when for-each-ref exits unsuccessfully", () => | ... | @@ -619,7 +619,7 @@ it.effect("fails a ref snapshot when for-each-ref exits unsuccessfully", () => |
| 619 | }); | 619 | }); |
| 620 | assert.equal(yield* Ref.get(snapshotAttempts), 1); | 620 | assert.equal(yield* Ref.get(snapshotAttempts), 1); |
| 621 | }), | 621 | }), |
| 622 | ).pipe(Effect.provide(ServerConfigLayer.pipe(Layer.provideMerge(NodeServices.layer)))), | 622 | ).pipe(Effect.provide(layerServerConfig.pipe(Layer.provideMerge(NodeServices.layer)))), |
| 623 | ); | 623 | ); |
| 624 | 624 | ||
| 625 | it.effect("marks the current branch when worktree metadata is unavailable", () => | 625 | it.effect("marks the current branch when worktree metadata is unavailable", () => |
| ... | @@ -654,7 +654,7 @@ it.effect("marks the current branch when worktree metadata is unavailable", () = | ... | @@ -654,7 +654,7 @@ it.effect("marks the current branch when worktree metadata is unavailable", () = |
| 654 | assert.isTrue(refs.isRepo); | 654 | assert.isTrue(refs.isRepo); |
| 655 | assert.isTrue(refs.refs.find((ref) => ref.name === initialBranch)?.current); | 655 | assert.isTrue(refs.refs.find((ref) => ref.name === initialBranch)?.current); |
| 656 | }), | 656 | }), |
| 657 | ).pipe(Effect.provide(ServerConfigLayer.pipe(Layer.provideMerge(NodeServices.layer)))), | 657 | ).pipe(Effect.provide(layerServerConfig.pipe(Layer.provideMerge(NodeServices.layer)))), |
| 658 | ); | 658 | ); |
| 659 | 659 | ||
| 660 | it.effect("ignores worktree metadata for directories that no longer exist", () => | 660 | it.effect("ignores worktree metadata for directories that no longer exist", () => |
| ... | @@ -690,7 +690,7 @@ it.effect("ignores worktree metadata for directories that no longer exist", () = | ... | @@ -690,7 +690,7 @@ it.effect("ignores worktree metadata for directories that no longer exist", () = |
| 690 | 690 | ||
| 691 | assert.equal(refs.refs.find((ref) => ref.name === "stale-worktree")?.worktreePath, null); | 691 | assert.equal(refs.refs.find((ref) => ref.name === "stale-worktree")?.worktreePath, null); |
| 692 | }), | 692 | }), |
| 693 | ).pipe(Effect.provide(ServerConfigLayer.pipe(Layer.provideMerge(NodeServices.layer)))), | 693 | ).pipe(Effect.provide(layerServerConfig.pipe(Layer.provideMerge(NodeServices.layer)))), |
| 694 | ); | 694 | ); |
| 695 | 695 | ||
| 696 | it.effect("refreshes the current branch after an external checkout", () => | 696 | it.effect("refreshes the current branch after an external checkout", () => |
| ... | @@ -718,7 +718,7 @@ it.effect("refreshes the current branch after an external checkout", () => | ... | @@ -718,7 +718,7 @@ it.effect("refreshes the current branch after an external checkout", () => |
| 718 | assert.isTrue(refreshedRefs.refs.find((ref) => ref.name === "external-checkout")?.current); | 718 | assert.isTrue(refreshedRefs.refs.find((ref) => ref.name === "external-checkout")?.current); |
| 719 | assert.isFalse(refreshedRefs.refs.find((ref) => ref.name === initialBranch)?.current); | 719 | assert.isFalse(refreshedRefs.refs.find((ref) => ref.name === initialBranch)?.current); |
| 720 | }), | 720 | }), |
| 721 | ).pipe(Effect.provide(TestLayer)), | 721 | ).pipe(Effect.provide(layerTest)), |
| 722 | ); | 722 | ); |
| 723 | 723 | ||
| 724 | it.effect("backs off and logs failed fetch attempts across linked worktrees", () => | 724 | it.effect("backs off and logs failed fetch attempts across linked worktrees", () => |
| ... | @@ -818,7 +818,7 @@ it.effect("backs off and logs failed fetch attempts across linked worktrees", () | ... | @@ -818,7 +818,7 @@ it.effect("backs off and logs failed fetch attempts across linked worktrees", () |
| 818 | assert.equal(yield* Ref.get(fetchAttempts), 3); | 818 | assert.equal(yield* Ref.get(fetchAttempts), 3); |
| 819 | assert.lengthOf(warnings, 3); | 819 | assert.lengthOf(warnings, 3); |
| 820 | }), | 820 | }), |
| 821 | ).pipe(Effect.provide(ServerConfigLayer.pipe(Layer.provideMerge(NodeServices.layer)))), | 821 | ).pipe(Effect.provide(layerServerConfig.pipe(Layer.provideMerge(NodeServices.layer)))), |
| 822 | ); | 822 | ); |
| 823 | 823 | ||
| 824 | it.effect.each([ | 824 | it.effect.each([ |
| ... | @@ -927,10 +927,10 @@ it.effect.each([ | ... | @@ -927,10 +927,10 @@ it.effect.each([ |
| 927 | assert.notProperty(error, "stderr"); | 927 | assert.notProperty(error, "stderr"); |
| 928 | assert.notProperty(error, "args"); | 928 | assert.notProperty(error, "args"); |
| 929 | assert.equal(yield* Ref.get(attempts), 1); | 929 | assert.equal(yield* Ref.get(attempts), 1); |
| 930 | }).pipe(Effect.provide(ServerConfigLayer.pipe(Layer.provideMerge(NodeServices.layer)))), | 930 | }).pipe(Effect.provide(layerServerConfig.pipe(Layer.provideMerge(NodeServices.layer)))), |
| 931 | ); | 931 | ); |
| 932 | 932 | ||
| 933 | it.layer(TestLayer)("GitVcsDriver core integration", (it) => { | 933 | it.layer(layerTest)("GitVcsDriver core integration", (it) => { |
| 934 | describe("process environment", () => { | 934 | describe("process environment", () => { |
| 935 | it.effect("preserves the caller locale for general Git subprocesses", () => | 935 | it.effect("preserves the caller locale for general Git subprocesses", () => |
| 936 | Effect.gen(function* () { | 936 | Effect.gen(function* () { |
| ... | @@ -1424,7 +1424,7 @@ it.layer(TestLayer)("GitVcsDriver core integration", (it) => { | ... | @@ -1424,7 +1424,7 @@ it.layer(TestLayer)("GitVcsDriver core integration", (it) => { |
| 1424 | }); | 1424 | }); |
| 1425 | const driver = yield* makeGitVcsDriverCore().pipe( | 1425 | const driver = yield* makeGitVcsDriverCore().pipe( |
| 1426 | Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner), | 1426 | Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner), |
| 1427 | Effect.provide(ServerConfigLayer), | 1427 | Effect.provide(layerServerConfig), |
| 1428 | ); | 1428 | ); |
| 1429 | const branch = yield* driver.getReviewDiffPreview({ | 1429 | const branch = yield* driver.getReviewDiffPreview({ |
| 1430 | cwd, | 1430 | cwd, |
| ... | @@ -1463,7 +1463,7 @@ it.layer(TestLayer)("GitVcsDriver core integration", (it) => { | ... | @@ -1463,7 +1463,7 @@ it.layer(TestLayer)("GitVcsDriver core integration", (it) => { |
| 1463 | ); | 1463 | ); |
| 1464 | const driver = yield* makeGitVcsDriverCore().pipe( | 1464 | const driver = yield* makeGitVcsDriverCore().pipe( |
| 1465 | Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner), | 1465 | Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner), |
| 1466 | Effect.provide(ServerConfigLayer), | 1466 | Effect.provide(layerServerConfig), |
| 1467 | ); | 1467 | ); |
| 1468 | const result = yield* driver.getReviewDiffPreview({ cwd }).pipe(Effect.result); | 1468 | const result = yield* driver.getReviewDiffPreview({ cwd }).pipe(Effect.result); |
| 1469 | assert.isTrue(Result.isFailure(result)); | 1469 | assert.isTrue(Result.isFailure(result)); |
| ... | @@ -1667,7 +1667,7 @@ it.layer(TestLayer)("GitVcsDriver core integration", (it) => { | ... | @@ -1667,7 +1667,7 @@ it.layer(TestLayer)("GitVcsDriver core integration", (it) => { |
| 1667 | }); | 1667 | }); |
| 1668 | const driver = yield* makeGitVcsDriverCore().pipe( | 1668 | const driver = yield* makeGitVcsDriverCore().pipe( |
| 1669 | Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, failingLsFilesSpawner), | 1669 | Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, failingLsFilesSpawner), |
| 1670 | Effect.provide(ServerConfigLayer), | 1670 | Effect.provide(layerServerConfig), |
| 1671 | ); | 1671 | ); |
| 1672 | const cwd = yield* makeTmpDir(); | 1672 | const cwd = yield* makeTmpDir(); |
| 1673 | yield* initRepoWithCommit(cwd).pipe( | 1673 | yield* initRepoWithCommit(cwd).pipe( |
| ... | @@ -2312,7 +2312,7 @@ it.layer(TestLayer)("GitVcsDriver core integration", (it) => { | ... | @@ -2312,7 +2312,7 @@ it.layer(TestLayer)("GitVcsDriver core integration", (it) => { |
| 2312 | ); | 2312 | ); |
| 2313 | const driver = yield* makeGitVcsDriverCore().pipe( | 2313 | const driver = yield* makeGitVcsDriverCore().pipe( |
| 2314 | Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner), | 2314 | Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner), |
| 2315 | Effect.provide(ServerConfigLayer), | 2315 | Effect.provide(layerServerConfig), |
| 2316 | ); | 2316 | ); |
| 2317 | 2317 | ||
| 2318 | for (const hasUpstream of [false, true]) { | 2318 | for (const hasUpstream of [false, true]) { |
| ... | @@ -3214,7 +3214,7 @@ it.layer(TestLayer)("GitVcsDriver core integration", (it) => { | ... | @@ -3214,7 +3214,7 @@ it.layer(TestLayer)("GitVcsDriver core integration", (it) => { |
| 3214 | ); | 3214 | ); |
| 3215 | const driver = yield* makeGitVcsDriverCore().pipe( | 3215 | const driver = yield* makeGitVcsDriverCore().pipe( |
| 3216 | Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, delayedRemovalSpawner), | 3216 | Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, delayedRemovalSpawner), |
| 3217 | Effect.provide(ServerConfigLayer), | 3217 | Effect.provide(layerServerConfig), |
| 3218 | ); | 3218 | ); |
| 3219 | const cwd = yield* makeTmpDir(); | 3219 | const cwd = yield* makeTmpDir(); |
| 3220 | const { initialBranch } = yield* initRepoWithCommit(cwd); | 3220 | const { initialBranch } = yield* initRepoWithCommit(cwd); |
| ... | @@ -3523,7 +3523,7 @@ it.layer(TestLayer)("GitVcsDriver core integration", (it) => { | ... | @@ -3523,7 +3523,7 @@ it.layer(TestLayer)("GitVcsDriver core integration", (it) => { |
| 3523 | ); | 3523 | ); |
| 3524 | const driver = yield* makeGitVcsDriverCore().pipe( | 3524 | const driver = yield* makeGitVcsDriverCore().pipe( |
| 3525 | Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner), | 3525 | Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner), |
| 3526 | Effect.provide(ServerConfigLayer), | 3526 | Effect.provide(layerServerConfig), |
| 3527 | ); | 3527 | ); |
| 3528 | const fetching = yield* driver | 3528 | const fetching = yield* driver |
| 3529 | .fetchRemote({ cwd, remoteName: "origin", refName: "main" }) | 3529 | .fetchRemote({ cwd, remoteName: "origin", refName: "main" }) |
| ... | @@ -3703,7 +3703,7 @@ it.layer(TestLayer)("GitVcsDriver core integration", (it) => { | ... | @@ -3703,7 +3703,7 @@ it.layer(TestLayer)("GitVcsDriver core integration", (it) => { |
| 3703 | ); | 3703 | ); |
| 3704 | const driver = yield* makeGitVcsDriverCore().pipe( | 3704 | const driver = yield* makeGitVcsDriverCore().pipe( |
| 3705 | Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, delayedPushSpawner), | 3705 | Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, delayedPushSpawner), |
| 3706 | Effect.provide(ServerConfigLayer), | 3706 | Effect.provide(layerServerConfig), |
| 3707 | ); | 3707 | ); |
| 3708 | const cwd = yield* makeTmpDir(); | 3708 | const cwd = yield* makeTmpDir(); |
| 3709 | const remote = yield* makeTmpDir("git-remote-"); | 3709 | const remote = yield* makeTmpDir("git-remote-"); |
apps/server/src/vcs/VcsProcess.test.ts+2-2| ... | @@ -30,10 +30,10 @@ const run = (input: VcsProcess.VcsProcessInput) => | ... | @@ -30,10 +30,10 @@ const run = (input: VcsProcess.VcsProcessInput) => |
| 30 | return yield* process.run(input); | 30 | return yield* process.run(input); |
| 31 | }); | 31 | }); |
| 32 | 32 | ||
| 33 | const liveLayer = VcsProcess.layer.pipe(Layer.provide(NodeServices.layer)); | 33 | const layerLive = VcsProcess.layer.pipe(Layer.provide(NodeServices.layer)); |
| 34 | 34 | ||
| 35 | const provideLive = <A, E, R>(effect: Effect.Effect<A, E, R | VcsProcess.VcsProcess>) => | 35 | const provideLive = <A, E, R>(effect: Effect.Effect<A, E, R | VcsProcess.VcsProcess>) => |
| 36 | effect.pipe(Effect.provide(liveLayer)); | 36 | effect.pipe(Effect.provide(layerLive)); |
| 37 | 37 | ||
| 38 | const baseInput = { | 38 | const baseInput = { |
| 39 | operation: "test.process-boundary", | 39 | operation: "test.process-boundary", |
apps/server/src/vcs/VcsProjectConfig.test.ts+8-8| ... | @@ -8,13 +8,13 @@ import * as Path from "effect/Path"; | ... | @@ -8,13 +8,13 @@ import * as Path from "effect/Path"; |
| 8 | 8 | ||
| 9 | import * as VcsProjectConfig from "./VcsProjectConfig.ts"; | 9 | import * as VcsProjectConfig from "./VcsProjectConfig.ts"; |
| 10 | 10 | ||
| 11 | const TestLayer = VcsProjectConfig.layer.pipe( | 11 | const layerTest = VcsProjectConfig.layer.pipe( |
| 12 | Layer.provide(NodeServices.layer), | 12 | Layer.provide(NodeServices.layer), |
| 13 | Layer.provideMerge(NodeServices.layer), | 13 | Layer.provideMerge(NodeServices.layer), |
| 14 | ); | 14 | ); |
| 15 | 15 | ||
| 16 | describe("VcsProjectConfig", () => { | 16 | describe("VcsProjectConfig", () => { |
| 17 | it.layer(TestLayer)("uses an explicit requested VCS kind before config", (it) => { | 17 | it.layer(layerTest)("uses an explicit requested VCS kind before config", (it) => { |
| 18 | it.effect("returns the requested kind", () => | 18 | it.effect("returns the requested kind", () => |
| 19 | Effect.gen(function* () { | 19 | Effect.gen(function* () { |
| 20 | const config = yield* VcsProjectConfig.VcsProjectConfig; | 20 | const config = yield* VcsProjectConfig.VcsProjectConfig; |
| ... | @@ -28,7 +28,7 @@ describe("VcsProjectConfig", () => { | ... | @@ -28,7 +28,7 @@ describe("VcsProjectConfig", () => { |
| 28 | ); | 28 | ); |
| 29 | }); | 29 | }); |
| 30 | 30 | ||
| 31 | it.layer(TestLayer)("discovers .t3code/vcs.json from nested workspaces", (it) => { | 31 | it.layer(layerTest)("discovers .t3code/vcs.json from nested workspaces", (it) => { |
| 32 | it.effect("returns the configured kind", () => | 32 | it.effect("returns the configured kind", () => |
| 33 | Effect.gen(function* () { | 33 | Effect.gen(function* () { |
| 34 | const fileSystem = yield* FileSystem.FileSystem; | 34 | const fileSystem = yield* FileSystem.FileSystem; |
| ... | @@ -54,7 +54,7 @@ describe("VcsProjectConfig", () => { | ... | @@ -54,7 +54,7 @@ describe("VcsProjectConfig", () => { |
| 54 | ); | 54 | ); |
| 55 | }); | 55 | }); |
| 56 | 56 | ||
| 57 | it.layer(TestLayer)("continues to parent configs after a candidate inspect failure", (it) => { | 57 | it.layer(layerTest)("continues to parent configs after a candidate inspect failure", (it) => { |
| 58 | it.effect("logs the failed candidate and returns the parent config", () => { | 58 | it.effect("logs the failed candidate and returns the parent config", () => { |
| 59 | const messages: unknown[] = []; | 59 | const messages: unknown[] = []; |
| 60 | const logger = Logger.make<unknown, void>(({ message }) => { | 60 | const logger = Logger.make<unknown, void>(({ message }) => { |
| ... | @@ -97,7 +97,7 @@ describe("VcsProjectConfig", () => { | ... | @@ -97,7 +97,7 @@ describe("VcsProjectConfig", () => { |
| 97 | }); | 97 | }); |
| 98 | }); | 98 | }); |
| 99 | 99 | ||
| 100 | it.layer(TestLayer)("falls back to auto when no config exists", (it) => { | 100 | it.layer(layerTest)("falls back to auto when no config exists", (it) => { |
| 101 | it.effect("returns auto", () => | 101 | it.effect("returns auto", () => |
| 102 | Effect.gen(function* () { | 102 | Effect.gen(function* () { |
| 103 | const fileSystem = yield* FileSystem.FileSystem; | 103 | const fileSystem = yield* FileSystem.FileSystem; |
| ... | @@ -112,7 +112,7 @@ describe("VcsProjectConfig", () => { | ... | @@ -112,7 +112,7 @@ describe("VcsProjectConfig", () => { |
| 112 | ); | 112 | ); |
| 113 | }); | 113 | }); |
| 114 | 114 | ||
| 115 | it.layer(TestLayer)("falls back to auto when config JSON is malformed", (it) => { | 115 | it.layer(layerTest)("falls back to auto when config JSON is malformed", (it) => { |
| 116 | it.effect("returns auto and logs the failed operation and path", () => { | 116 | it.effect("returns auto and logs the failed operation and path", () => { |
| 117 | const messages: unknown[] = []; | 117 | const messages: unknown[] = []; |
| 118 | const logger = Logger.make<unknown, void>(({ message }) => { | 118 | const logger = Logger.make<unknown, void>(({ message }) => { |
| ... | @@ -150,7 +150,7 @@ describe("VcsProjectConfig", () => { | ... | @@ -150,7 +150,7 @@ describe("VcsProjectConfig", () => { |
| 150 | }); | 150 | }); |
| 151 | }); | 151 | }); |
| 152 | 152 | ||
| 153 | it.layer(TestLayer)("falls back to auto when the config path cannot be read", (it) => { | 153 | it.layer(layerTest)("falls back to auto when the config path cannot be read", (it) => { |
| 154 | it.effect("retains the read failure context", () => { | 154 | it.effect("retains the read failure context", () => { |
| 155 | const messages: unknown[] = []; | 155 | const messages: unknown[] = []; |
| 156 | const logger = Logger.make<unknown, void>(({ message }) => { | 156 | const logger = Logger.make<unknown, void>(({ message }) => { |
| ... | @@ -184,7 +184,7 @@ describe("VcsProjectConfig", () => { | ... | @@ -184,7 +184,7 @@ describe("VcsProjectConfig", () => { |
| 184 | }); | 184 | }); |
| 185 | }); | 185 | }); |
| 186 | 186 | ||
| 187 | it.layer(TestLayer)("falls back to auto when config kind is invalid", (it) => { | 187 | it.layer(layerTest)("falls back to auto when config kind is invalid", (it) => { |
| 188 | it.effect("returns auto", () => | 188 | it.effect("returns auto", () => |
| 189 | Effect.gen(function* () { | 189 | Effect.gen(function* () { |
| 190 | const fileSystem = yield* FileSystem.FileSystem; | 190 | const fileSystem = yield* FileSystem.FileSystem; |
apps/server/src/vcs/VcsProvisioningService.test.ts+4-4| ... | @@ -61,7 +61,7 @@ function makeDriver(calls: string[]): VcsDriver.VcsDriver["Service"] { | ... | @@ -61,7 +61,7 @@ function makeDriver(calls: string[]): VcsDriver.VcsDriver["Service"] { |
| 61 | it.effect("routes repository initialization through an explicit VCS driver kind", () => { | 61 | it.effect("routes repository initialization through an explicit VCS driver kind", () => { |
| 62 | const calls: string[] = []; | 62 | const calls: string[] = []; |
| 63 | const driver = makeDriver(calls); | 63 | const driver = makeDriver(calls); |
| 64 | const testLayer = VcsProvisioningService.layer.pipe( | 64 | const layerTest = VcsProvisioningService.layer.pipe( |
| 65 | Layer.provide( | 65 | Layer.provide( |
| 66 | Layer.mock(VcsDriverRegistry.VcsDriverRegistry)({ | 66 | Layer.mock(VcsDriverRegistry.VcsDriverRegistry)({ |
| 67 | get: (kind) => (kind === "git" ? Effect.succeed(driver) : Effect.die("unexpected kind")), | 67 | get: (kind) => (kind === "git" ? Effect.succeed(driver) : Effect.die("unexpected kind")), |
| ... | @@ -74,13 +74,13 @@ it.effect("routes repository initialization through an explicit VCS driver kind" | ... | @@ -74,13 +74,13 @@ it.effect("routes repository initialization through an explicit VCS driver kind" |
| 74 | yield* provisioning.initRepository({ cwd: "/repo", kind: "git" }); | 74 | yield* provisioning.initRepository({ cwd: "/repo", kind: "git" }); |
| 75 | 75 | ||
| 76 | assert.deepStrictEqual(calls, ["git:/repo"]); | 76 | assert.deepStrictEqual(calls, ["git:/repo"]); |
| 77 | }).pipe(Effect.provide(testLayer)); | 77 | }).pipe(Effect.provide(layerTest)); |
| 78 | }); | 78 | }); |
| 79 | 79 | ||
| 80 | it.effect("defaults repository initialization to Git until callers choose a VCS kind", () => { | 80 | it.effect("defaults repository initialization to Git until callers choose a VCS kind", () => { |
| 81 | const calls: string[] = []; | 81 | const calls: string[] = []; |
| 82 | const driver = makeDriver(calls); | 82 | const driver = makeDriver(calls); |
| 83 | const testLayer = VcsProvisioningService.layer.pipe( | 83 | const layerTest = VcsProvisioningService.layer.pipe( |
| 84 | Layer.provide( | 84 | Layer.provide( |
| 85 | Layer.mock(VcsDriverRegistry.VcsDriverRegistry)({ | 85 | Layer.mock(VcsDriverRegistry.VcsDriverRegistry)({ |
| 86 | get: (kind) => (kind === "git" ? Effect.succeed(driver) : Effect.die("unexpected kind")), | 86 | get: (kind) => (kind === "git" ? Effect.succeed(driver) : Effect.die("unexpected kind")), |
| ... | @@ -93,5 +93,5 @@ it.effect("defaults repository initialization to Git until callers choose a VCS | ... | @@ -93,5 +93,5 @@ it.effect("defaults repository initialization to Git until callers choose a VCS |
| 93 | yield* provisioning.initRepository({ cwd: "/repo" }); | 93 | yield* provisioning.initRepository({ cwd: "/repo" }); |
| 94 | 94 | ||
| 95 | assert.deepStrictEqual(calls, ["default:/repo"]); | 95 | assert.deepStrictEqual(calls, ["default:/repo"]); |
| 96 | }).pipe(Effect.provide(testLayer)); | 96 | }).pipe(Effect.provide(layerTest)); |
| 97 | }); | 97 | }); |
apps/server/src/vcs/VcsStatusBroadcaster.test.ts+34-34| ... | @@ -69,7 +69,7 @@ const baseStatus: VcsStatusResult = { | ... | @@ -69,7 +69,7 @@ const baseStatus: VcsStatusResult = { |
| 69 | ...baseRemoteStatus, | 69 | ...baseRemoteStatus, |
| 70 | }; | 70 | }; |
| 71 | 71 | ||
| 72 | function makeTestLayer(state: { | 72 | function layerTestFor(state: { |
| 73 | currentLocalStatus: VcsStatusLocalResult; | 73 | currentLocalStatus: VcsStatusLocalResult; |
| 74 | currentRemoteStatus: VcsStatusRemoteResult | null; | 74 | currentRemoteStatus: VcsStatusRemoteResult | null; |
| 75 | localStatusCalls: number; | 75 | localStatusCalls: number; |
| ... | @@ -83,7 +83,7 @@ function makeTestLayer(state: { | ... | @@ -83,7 +83,7 @@ function makeTestLayer(state: { |
| 83 | }) { | 83 | }) { |
| 84 | return VcsStatusBroadcaster.layer.pipe( | 84 | return VcsStatusBroadcaster.layer.pipe( |
| 85 | Layer.provideMerge(NodeServices.layer), | 85 | Layer.provideMerge(NodeServices.layer), |
| 86 | Layer.provide(makeBackgroundPolicyLayer(() => state.backgroundWorkEnabled !== false)), | 86 | Layer.provide(layerBackgroundPolicy(() => state.backgroundWorkEnabled !== false)), |
| 87 | Layer.provide( | 87 | Layer.provide( |
| 88 | Layer.mock(GitWorkflowService.GitWorkflowService)({ | 88 | Layer.mock(GitWorkflowService.GitWorkflowService)({ |
| 89 | localStatus: () => | 89 | localStatus: () => |
| ... | @@ -119,7 +119,7 @@ function makeTestLayer(state: { | ... | @@ -119,7 +119,7 @@ function makeTestLayer(state: { |
| 119 | ); | 119 | ); |
| 120 | } | 120 | } |
| 121 | 121 | ||
| 122 | function makeBackgroundPolicyLayer(shouldRunScopeWork: (scope: BackgroundScope) => boolean) { | 122 | function layerBackgroundPolicy(shouldRunScopeWork: (scope: BackgroundScope) => boolean) { |
| 123 | return Layer.mock(BackgroundPolicy.BackgroundPolicy)({ | 123 | return Layer.mock(BackgroundPolicy.BackgroundPolicy)({ |
| 124 | reportClientActivity: () => Effect.void, | 124 | reportClientActivity: () => Effect.void, |
| 125 | removeRpcClient: () => Effect.void, | 125 | removeRpcClient: () => Effect.void, |
| ... | @@ -162,9 +162,9 @@ describe("VcsStatusBroadcaster", () => { | ... | @@ -162,9 +162,9 @@ describe("VcsStatusBroadcaster", () => { |
| 162 | isDefaultRef: true, | 162 | isDefaultRef: true, |
| 163 | refName: "main", | 163 | refName: "main", |
| 164 | }; | 164 | }; |
| 165 | const testLayer = VcsStatusBroadcaster.layer.pipe( | 165 | const layerTest = VcsStatusBroadcaster.layer.pipe( |
| 166 | Layer.provideMerge(NodeServices.layer), | 166 | Layer.provideMerge(NodeServices.layer), |
| 167 | Layer.provide(makeBackgroundPolicyLayer(() => true)), | 167 | Layer.provide(layerBackgroundPolicy(() => true)), |
| 168 | Layer.provide( | 168 | Layer.provide( |
| 169 | Layer.succeed(VcsStatusBroadcaster.VcsAutoPullPolicy, { | 169 | Layer.succeed(VcsStatusBroadcaster.VcsAutoPullPolicy, { |
| 170 | isEnabled: (cwd) => Effect.succeed(cwd === configuredWorkspaceRoot), | 170 | isEnabled: (cwd) => Effect.succeed(cwd === configuredWorkspaceRoot), |
| ... | @@ -208,7 +208,7 @@ describe("VcsStatusBroadcaster", () => { | ... | @@ -208,7 +208,7 @@ describe("VcsStatusBroadcaster", () => { |
| 208 | 208 | ||
| 209 | assert.equal(pullCalls, 1); | 209 | assert.equal(pullCalls, 1); |
| 210 | assert.equal(status.behindCount, 0); | 210 | assert.equal(status.behindCount, 0); |
| 211 | }).pipe(Effect.provide(testLayer)); | 211 | }).pipe(Effect.provide(layerTest)); |
| 212 | }, | 212 | }, |
| 213 | ); | 213 | ); |
| 214 | 214 | ||
| ... | @@ -234,7 +234,7 @@ describe("VcsStatusBroadcaster", () => { | ... | @@ -234,7 +234,7 @@ describe("VcsStatusBroadcaster", () => { |
| 234 | assert.equal(state.remoteStatusCalls, 1); | 234 | assert.equal(state.remoteStatusCalls, 1); |
| 235 | assert.equal(state.localInvalidationCalls, 0); | 235 | assert.equal(state.localInvalidationCalls, 0); |
| 236 | assert.equal(state.remoteInvalidationCalls, 0); | 236 | assert.equal(state.remoteInvalidationCalls, 0); |
| 237 | }).pipe(Effect.provide(makeTestLayer(state))); | 237 | }).pipe(Effect.provide(layerTestFor(state))); |
| 238 | }); | 238 | }); |
| 239 | 239 | ||
| 240 | it.effect("refreshes a loaded cwd without reusing a previous branch's PR", () => { | 240 | it.effect("refreshes a loaded cwd without reusing a previous branch's PR", () => { |
| ... | @@ -271,7 +271,7 @@ describe("VcsStatusBroadcaster", () => { | ... | @@ -271,7 +271,7 @@ describe("VcsStatusBroadcaster", () => { |
| 271 | const refreshedBranch = yield* broadcaster.refreshPullRequestStatus("/repo"); | 271 | const refreshedBranch = yield* broadcaster.refreshPullRequestStatus("/repo"); |
| 272 | assert.deepStrictEqual(refreshedBranch, baseRemoteStatus); | 272 | assert.deepStrictEqual(refreshedBranch, baseRemoteStatus); |
| 273 | assert.equal(state.remoteStatusCalls, 3); | 273 | assert.equal(state.remoteStatusCalls, 3); |
| 274 | }).pipe(Effect.provide(makeTestLayer(state))); | 274 | }).pipe(Effect.provide(layerTestFor(state))); |
| 275 | }); | 275 | }); |
| 276 | 276 | ||
| 277 | it.effect("a poll that started before the turn-end refresh cannot overwrite its PR", () => { | 277 | it.effect("a poll that started before the turn-end refresh cannot overwrite its PR", () => { |
| ... | @@ -280,7 +280,7 @@ describe("VcsStatusBroadcaster", () => { | ... | @@ -280,7 +280,7 @@ describe("VcsStatusBroadcaster", () => { |
| 280 | let remoteReads = 0; | 280 | let remoteReads = 0; |
| 281 | const layer = VcsStatusBroadcaster.layer.pipe( | 281 | const layer = VcsStatusBroadcaster.layer.pipe( |
| 282 | Layer.provideMerge(NodeServices.layer), | 282 | Layer.provideMerge(NodeServices.layer), |
| 283 | Layer.provide(makeBackgroundPolicyLayer(() => true)), | 283 | Layer.provide(layerBackgroundPolicy(() => true)), |
| 284 | Layer.provide( | 284 | Layer.provide( |
| 285 | Layer.mock(GitWorkflowService.GitWorkflowService)({ | 285 | Layer.mock(GitWorkflowService.GitWorkflowService)({ |
| 286 | localStatus: () => Effect.succeed(baseLocalStatus), | 286 | localStatus: () => Effect.succeed(baseLocalStatus), |
| ... | @@ -326,7 +326,7 @@ describe("VcsStatusBroadcaster", () => { | ... | @@ -326,7 +326,7 @@ describe("VcsStatusBroadcaster", () => { |
| 326 | const layer = VcsStatusBroadcaster.layer.pipe( | 326 | const layer = VcsStatusBroadcaster.layer.pipe( |
| 327 | Layer.provide(FileSystem.layerNoop({ realPath: (path) => Effect.succeed(path) })), | 327 | Layer.provide(FileSystem.layerNoop({ realPath: (path) => Effect.succeed(path) })), |
| 328 | Layer.provideMerge(NodeServices.layer), | 328 | Layer.provideMerge(NodeServices.layer), |
| 329 | Layer.provide(makeBackgroundPolicyLayer(() => true)), | 329 | Layer.provide(layerBackgroundPolicy(() => true)), |
| 330 | Layer.provide( | 330 | Layer.provide( |
| 331 | Layer.mock(GitWorkflowService.GitWorkflowService)({ | 331 | Layer.mock(GitWorkflowService.GitWorkflowService)({ |
| 332 | localStatus: () => Effect.succeed(baseLocalStatus), | 332 | localStatus: () => Effect.succeed(baseLocalStatus), |
| ... | @@ -377,7 +377,7 @@ describe("VcsStatusBroadcaster", () => { | ... | @@ -377,7 +377,7 @@ describe("VcsStatusBroadcaster", () => { |
| 377 | yield* broadcaster.refreshPullRequestStatus("/repo"); | 377 | yield* broadcaster.refreshPullRequestStatus("/repo"); |
| 378 | assert.equal(state.remoteStatusCalls, 1); | 378 | assert.equal(state.remoteStatusCalls, 1); |
| 379 | assert.equal(state.remoteInvalidationCalls, 0); | 379 | assert.equal(state.remoteInvalidationCalls, 0); |
| 380 | }).pipe(Effect.provide(makeTestLayer(state))); | 380 | }).pipe(Effect.provide(layerTestFor(state))); |
| 381 | }); | 381 | }); |
| 382 | 382 | ||
| 383 | it.effect("refreshes the cached snapshot after explicit invalidation", () => { | 383 | it.effect("refreshes the cached snapshot after explicit invalidation", () => { |
| ... | @@ -418,7 +418,7 @@ describe("VcsStatusBroadcaster", () => { | ... | @@ -418,7 +418,7 @@ describe("VcsStatusBroadcaster", () => { |
| 418 | assert.equal(state.remoteStatusCalls, 2); | 418 | assert.equal(state.remoteStatusCalls, 2); |
| 419 | assert.equal(state.localInvalidationCalls, 1); | 419 | assert.equal(state.localInvalidationCalls, 1); |
| 420 | assert.equal(state.remoteInvalidationCalls, 1); | 420 | assert.equal(state.remoteInvalidationCalls, 1); |
| 421 | }).pipe(Effect.provide(makeTestLayer(state))); | 421 | }).pipe(Effect.provide(layerTestFor(state))); |
| 422 | }); | 422 | }); |
| 423 | 423 | ||
| 424 | it.effect("keeps the cached snapshot unchanged when a refresh branch fails", () => { | 424 | it.effect("keeps the cached snapshot unchanged when a refresh branch fails", () => { |
| ... | @@ -431,9 +431,9 @@ describe("VcsStatusBroadcaster", () => { | ... | @@ -431,9 +431,9 @@ describe("VcsStatusBroadcaster", () => { |
| 431 | remoteInvalidationCalls: 0, | 431 | remoteInvalidationCalls: 0, |
| 432 | failRemoteStatus: false, | 432 | failRemoteStatus: false, |
| 433 | }; | 433 | }; |
| 434 | const testLayer = VcsStatusBroadcaster.layer.pipe( | 434 | const layerTest = VcsStatusBroadcaster.layer.pipe( |
| 435 | Layer.provideMerge(NodeServices.layer), | 435 | Layer.provideMerge(NodeServices.layer), |
| 436 | Layer.provide(makeBackgroundPolicyLayer(() => true)), | 436 | Layer.provide(layerBackgroundPolicy(() => true)), |
| 437 | Layer.provide( | 437 | Layer.provide( |
| 438 | Layer.mock(GitWorkflowService.GitWorkflowService)({ | 438 | Layer.mock(GitWorkflowService.GitWorkflowService)({ |
| 439 | localStatus: () => | 439 | localStatus: () => |
| ... | @@ -490,7 +490,7 @@ describe("VcsStatusBroadcaster", () => { | ... | @@ -490,7 +490,7 @@ describe("VcsStatusBroadcaster", () => { |
| 490 | 490 | ||
| 491 | assert.isTrue(Exit.isFailure(refreshExit)); | 491 | assert.isTrue(Exit.isFailure(refreshExit)); |
| 492 | assert.deepStrictEqual(cached, baseStatus); | 492 | assert.deepStrictEqual(cached, baseStatus); |
| 493 | }).pipe(Effect.provide(testLayer)); | 493 | }).pipe(Effect.provide(layerTest)); |
| 494 | }); | 494 | }); |
| 495 | 495 | ||
| 496 | it.effect("refreshes only the cached local snapshot when requested", () => { | 496 | it.effect("refreshes only the cached local snapshot when requested", () => { |
| ... | @@ -526,7 +526,7 @@ describe("VcsStatusBroadcaster", () => { | ... | @@ -526,7 +526,7 @@ describe("VcsStatusBroadcaster", () => { |
| 526 | assert.equal(state.remoteStatusCalls, 1); | 526 | assert.equal(state.remoteStatusCalls, 1); |
| 527 | assert.equal(state.localInvalidationCalls, 1); | 527 | assert.equal(state.localInvalidationCalls, 1); |
| 528 | assert.equal(state.remoteInvalidationCalls, 0); | 528 | assert.equal(state.remoteInvalidationCalls, 0); |
| 529 | }).pipe(Effect.provide(makeTestLayer(state))); | 529 | }).pipe(Effect.provide(layerTestFor(state))); |
| 530 | }); | 530 | }); |
| 531 | 531 | ||
| 532 | it.effect.skipIf(!symlinksSupported)( | 532 | it.effect.skipIf(!symlinksSupported)( |
| ... | @@ -541,9 +541,9 @@ describe("VcsStatusBroadcaster", () => { | ... | @@ -541,9 +541,9 @@ describe("VcsStatusBroadcaster", () => { |
| 541 | localInvalidationCalls: 0, | 541 | localInvalidationCalls: 0, |
| 542 | remoteInvalidationCalls: 0, | 542 | remoteInvalidationCalls: 0, |
| 543 | }; | 543 | }; |
| 544 | const testLayer = VcsStatusBroadcaster.layer.pipe( | 544 | const layerTest = VcsStatusBroadcaster.layer.pipe( |
| 545 | Layer.provideMerge(NodeServices.layer), | 545 | Layer.provideMerge(NodeServices.layer), |
| 546 | Layer.provide(makeBackgroundPolicyLayer(() => true)), | 546 | Layer.provide(layerBackgroundPolicy(() => true)), |
| 547 | Layer.provide( | 547 | Layer.provide( |
| 548 | Layer.mock(GitWorkflowService.GitWorkflowService)({ | 548 | Layer.mock(GitWorkflowService.GitWorkflowService)({ |
| 549 | localStatus: (input) => | 549 | localStatus: (input) => |
| ... | @@ -590,7 +590,7 @@ describe("VcsStatusBroadcaster", () => { | ... | @@ -590,7 +590,7 @@ describe("VcsStatusBroadcaster", () => { |
| 590 | assert.deepStrictEqual(seenCwds, [realPath, realPath]); | 590 | assert.deepStrictEqual(seenCwds, [realPath, realPath]); |
| 591 | assert.equal(state.localStatusCalls, 1); | 591 | assert.equal(state.localStatusCalls, 1); |
| 592 | assert.equal(state.remoteStatusCalls, 1); | 592 | assert.equal(state.remoteStatusCalls, 1); |
| 593 | }).pipe(Effect.provide(testLayer)); | 593 | }).pipe(Effect.provide(layerTest)); |
| 594 | }, | 594 | }, |
| 595 | ); | 595 | ); |
| 596 | 596 | ||
| ... | @@ -631,7 +631,7 @@ describe("VcsStatusBroadcaster", () => { | ... | @@ -631,7 +631,7 @@ describe("VcsStatusBroadcaster", () => { |
| 631 | _tag: "remoteUpdated", | 631 | _tag: "remoteUpdated", |
| 632 | remote: baseRemoteStatus, | 632 | remote: baseRemoteStatus, |
| 633 | } satisfies VcsStatusStreamEvent); | 633 | } satisfies VcsStatusStreamEvent); |
| 634 | }).pipe(Effect.provide(makeTestLayer(state))); | 634 | }).pipe(Effect.provide(layerTestFor(state))); |
| 635 | }); | 635 | }); |
| 636 | 636 | ||
| 637 | it.effect("loads remote status once when periodic refreshes are disabled", () => { | 637 | it.effect("loads remote status once when periodic refreshes are disabled", () => { |
| ... | @@ -687,7 +687,7 @@ describe("VcsStatusBroadcaster", () => { | ... | @@ -687,7 +687,7 @@ describe("VcsStatusBroadcaster", () => { |
| 687 | assert.equal(state.remoteInvalidationCalls, 0); | 687 | assert.equal(state.remoteInvalidationCalls, 0); |
| 688 | 688 | ||
| 689 | yield* Scope.close(scope, Exit.void); | 689 | yield* Scope.close(scope, Exit.void); |
| 690 | }).pipe(Effect.provide(Layer.merge(makeTestLayer(state), TestClock.layer()))); | 690 | }).pipe(Effect.provide(Layer.merge(layerTestFor(state), TestClock.layer()))); |
| 691 | }); | 691 | }); |
| 692 | 692 | ||
| 693 | it.effect("retries the initial remote load when periodic refreshes are disabled", () => { | 693 | it.effect("retries the initial remote load when periodic refreshes are disabled", () => { |
| ... | @@ -706,9 +706,9 @@ describe("VcsStatusBroadcaster", () => { | ... | @@ -706,9 +706,9 @@ describe("VcsStatusBroadcaster", () => { |
| 706 | messages.push(message as ReadonlyArray<unknown>); | 706 | messages.push(message as ReadonlyArray<unknown>); |
| 707 | }); | 707 | }); |
| 708 | let firstRemoteAttemptDeferred: Deferred.Deferred<void> | null = null; | 708 | let firstRemoteAttemptDeferred: Deferred.Deferred<void> | null = null; |
| 709 | const testLayer = VcsStatusBroadcaster.layer.pipe( | 709 | const layerTest = VcsStatusBroadcaster.layer.pipe( |
| 710 | Layer.provideMerge(NodeServices.layer), | 710 | Layer.provideMerge(NodeServices.layer), |
| 711 | Layer.provide(makeBackgroundPolicyLayer(() => true)), | 711 | Layer.provide(layerBackgroundPolicy(() => true)), |
| 712 | Layer.provide( | 712 | Layer.provide( |
| 713 | Layer.mock(GitWorkflowService.GitWorkflowService)({ | 713 | Layer.mock(GitWorkflowService.GitWorkflowService)({ |
| 714 | localStatus: () => | 714 | localStatus: () => |
| ... | @@ -803,7 +803,7 @@ describe("VcsStatusBroadcaster", () => { | ... | @@ -803,7 +803,7 @@ describe("VcsStatusBroadcaster", () => { |
| 803 | }).pipe( | 803 | }).pipe( |
| 804 | Effect.provide( | 804 | Effect.provide( |
| 805 | Layer.mergeAll( | 805 | Layer.mergeAll( |
| 806 | testLayer, | 806 | layerTest, |
| 807 | TestClock.layer(), | 807 | TestClock.layer(), |
| 808 | Logger.layer([logger], { mergeWithExisting: false }), | 808 | Logger.layer([logger], { mergeWithExisting: false }), |
| 809 | ), | 809 | ), |
| ... | @@ -850,7 +850,7 @@ describe("VcsStatusBroadcaster", () => { | ... | @@ -850,7 +850,7 @@ describe("VcsStatusBroadcaster", () => { |
| 850 | assert.equal(state.remoteInvalidationCalls, 1); | 850 | assert.equal(state.remoteInvalidationCalls, 1); |
| 851 | 851 | ||
| 852 | yield* Scope.close(scope, Exit.void); | 852 | yield* Scope.close(scope, Exit.void); |
| 853 | }).pipe(Effect.provide(Layer.merge(makeTestLayer(state), TestClock.layer()))); | 853 | }).pipe(Effect.provide(Layer.merge(layerTestFor(state), TestClock.layer()))); |
| 854 | }); | 854 | }); |
| 855 | 855 | ||
| 856 | // A push from a terminal moves ahead; a PR merged on the host moves ahead-of-default. | 856 | // A push from a terminal moves ahead; a PR merged on the host moves ahead-of-default. |
| ... | @@ -901,11 +901,11 @@ describe("VcsStatusBroadcaster", () => { | ... | @@ -901,11 +901,11 @@ describe("VcsStatusBroadcaster", () => { |
| 901 | local: pushedLocal, | 901 | local: pushedLocal, |
| 902 | } satisfies VcsStatusStreamEvent); | 902 | } satisfies VcsStatusStreamEvent); |
| 903 | yield* Scope.close(scope, Exit.void); | 903 | yield* Scope.close(scope, Exit.void); |
| 904 | }).pipe(Effect.provide(Layer.merge(makeTestLayer(state), TestClock.layer()))); | 904 | }).pipe(Effect.provide(Layer.merge(layerTestFor(state), TestClock.layer()))); |
| 905 | }); | 905 | }); |
| 906 | 906 | ||
| 907 | it.effect("an explicit refresh reads local totals after the fetch", () => { | 907 | it.effect("an explicit refresh reads local totals after the fetch", () => { |
| 908 | const state: Parameters<typeof makeTestLayer>[0] = { | 908 | const state: Parameters<typeof layerTestFor>[0] = { |
| 909 | currentLocalStatus: baseLocalStatus, | 909 | currentLocalStatus: baseLocalStatus, |
| 910 | currentRemoteStatus: baseRemoteStatus, | 910 | currentRemoteStatus: baseRemoteStatus, |
| 911 | localStatusCalls: 0, | 911 | localStatusCalls: 0, |
| ... | @@ -934,7 +934,7 @@ describe("VcsStatusBroadcaster", () => { | ... | @@ -934,7 +934,7 @@ describe("VcsStatusBroadcaster", () => { |
| 934 | 934 | ||
| 935 | const status = yield* Fiber.join(refresh); | 935 | const status = yield* Fiber.join(refresh); |
| 936 | assert.deepStrictEqual(status.branchChanges, fetchedLocal.branchChanges); | 936 | assert.deepStrictEqual(status.branchChanges, fetchedLocal.branchChanges); |
| 937 | }).pipe(Effect.provide(makeTestLayer(state))); | 937 | }).pipe(Effect.provide(layerTestFor(state))); |
| 938 | }); | 938 | }); |
| 939 | 939 | ||
| 940 | it("backs off remote refresh failures exponentially and honors larger configured intervals", () => { | 940 | it("backs off remote refresh failures exponentially and honors larger configured intervals", () => { |
| ... | @@ -990,9 +990,9 @@ describe("VcsStatusBroadcaster", () => { | ... | @@ -990,9 +990,9 @@ describe("VcsStatusBroadcaster", () => { |
| 990 | localInvalidationCalls: 0, | 990 | localInvalidationCalls: 0, |
| 991 | remoteInvalidationCalls: 0, | 991 | remoteInvalidationCalls: 0, |
| 992 | }; | 992 | }; |
| 993 | const testLayer = VcsStatusBroadcaster.layer.pipe( | 993 | const layerTest = VcsStatusBroadcaster.layer.pipe( |
| 994 | Layer.provideMerge(NodeServices.layer), | 994 | Layer.provideMerge(NodeServices.layer), |
| 995 | Layer.provide(makeBackgroundPolicyLayer(() => false)), | 995 | Layer.provide(layerBackgroundPolicy(() => false)), |
| 996 | Layer.provide( | 996 | Layer.provide( |
| 997 | Layer.mock(GitWorkflowService.GitWorkflowService)({ | 997 | Layer.mock(GitWorkflowService.GitWorkflowService)({ |
| 998 | localStatus: () => | 998 | localStatus: () => |
| ... | @@ -1029,7 +1029,7 @@ describe("VcsStatusBroadcaster", () => { | ... | @@ -1029,7 +1029,7 @@ describe("VcsStatusBroadcaster", () => { |
| 1029 | assert.isTrue(Option.isSome(snapshot)); | 1029 | assert.isTrue(Option.isSome(snapshot)); |
| 1030 | assert.equal(state.remoteStatusCalls, 0); | 1030 | assert.equal(state.remoteStatusCalls, 0); |
| 1031 | assert.equal(state.remoteInvalidationCalls, 0); | 1031 | assert.equal(state.remoteInvalidationCalls, 0); |
| 1032 | }).pipe(Effect.provide(testLayer)); | 1032 | }).pipe(Effect.provide(layerTest)); |
| 1033 | }); | 1033 | }); |
| 1034 | 1034 | ||
| 1035 | it.effect("stops the remote poller after the last stream subscriber disconnects", () => { | 1035 | it.effect("stops the remote poller after the last stream subscriber disconnects", () => { |
| ... | @@ -1043,9 +1043,9 @@ describe("VcsStatusBroadcaster", () => { | ... | @@ -1043,9 +1043,9 @@ describe("VcsStatusBroadcaster", () => { |
| 1043 | }; | 1043 | }; |
| 1044 | let remoteInterruptedDeferred: Deferred.Deferred<void, never> | null = null; | 1044 | let remoteInterruptedDeferred: Deferred.Deferred<void, never> | null = null; |
| 1045 | let remoteStartedDeferred: Deferred.Deferred<void, never> | null = null; | 1045 | let remoteStartedDeferred: Deferred.Deferred<void, never> | null = null; |
| 1046 | const testLayer = VcsStatusBroadcaster.layer.pipe( | 1046 | const layerTest = VcsStatusBroadcaster.layer.pipe( |
| 1047 | Layer.provideMerge(NodeServices.layer), | 1047 | Layer.provideMerge(NodeServices.layer), |
| 1048 | Layer.provide(makeBackgroundPolicyLayer(() => true)), | 1048 | Layer.provide(layerBackgroundPolicy(() => true)), |
| 1049 | Layer.provide( | 1049 | Layer.provide( |
| 1050 | Layer.mock(GitWorkflowService.GitWorkflowService)({ | 1050 | Layer.mock(GitWorkflowService.GitWorkflowService)({ |
| 1051 | localStatus: () => | 1051 | localStatus: () => |
| ... | @@ -1115,6 +1115,6 @@ describe("VcsStatusBroadcaster", () => { | ... | @@ -1115,6 +1115,6 @@ describe("VcsStatusBroadcaster", () => { |
| 1115 | yield* Scope.close(secondScope, Exit.void).pipe(Effect.forkScoped); | 1115 | yield* Scope.close(secondScope, Exit.void).pipe(Effect.forkScoped); |
| 1116 | yield* Deferred.await(remoteInterrupted); | 1116 | yield* Deferred.await(remoteInterrupted); |
| 1117 | assert.isTrue(Option.isSome(yield* Deferred.poll(remoteInterrupted))); | 1117 | assert.isTrue(Option.isSome(yield* Deferred.poll(remoteInterrupted))); |
| 1118 | }).pipe(Effect.provide(testLayer)); | 1118 | }).pipe(Effect.provide(layerTest)); |
| 1119 | }); | 1119 | }); |
| 1120 | }); | 1120 | }); |
apps/server/src/vcs/VcsStatusBroadcaster.ts+1-1| ... | @@ -149,7 +149,7 @@ export class VcsAutoPullPolicy extends Context.Reference<{ | ... | @@ -149,7 +149,7 @@ export class VcsAutoPullPolicy extends Context.Reference<{ |
| 149 | defaultValue: () => ({ isEnabled: () => Effect.succeed(false) }), | 149 | defaultValue: () => ({ isEnabled: () => Effect.succeed(false) }), |
| 150 | }) {} | 150 | }) {} |
| 151 | 151 | ||
| 152 | export const autoPullPolicyLayer = Layer.effect( | 152 | export const layerAutoPullPolicy = Layer.effect( |
| 153 | VcsAutoPullPolicy, | 153 | VcsAutoPullPolicy, |
| 154 | Effect.gen(function* () { | 154 | Effect.gen(function* () { |
| 155 | const projects = yield* ProjectStore.ProjectStoreV2; | 155 | const projects = yield* ProjectStore.ProjectStoreV2; |
apps/server/src/workspace/WorkspaceEntries.test.ts+2-2| ... | @@ -21,7 +21,7 @@ vi.mock("node:fs/promises", async (importOriginal) => { | ... | @@ -21,7 +21,7 @@ vi.mock("node:fs/promises", async (importOriginal) => { |
| 21 | return { ...actual, readdir: vi.fn(actual.readdir) }; | 21 | return { ...actual, readdir: vi.fn(actual.readdir) }; |
| 22 | }); | 22 | }); |
| 23 | 23 | ||
| 24 | const TestLayer = Layer.empty.pipe( | 24 | const layerTest = Layer.empty.pipe( |
| 25 | Layer.provideMerge(WorkspaceEntries.layer.pipe(Layer.provide(WorkspacePaths.layer))), | 25 | Layer.provideMerge(WorkspaceEntries.layer.pipe(Layer.provide(WorkspacePaths.layer))), |
| 26 | Layer.provideMerge(WorkspacePaths.layer), | 26 | Layer.provideMerge(WorkspacePaths.layer), |
| 27 | Layer.provideMerge(VcsProcess.layer), | 27 | Layer.provideMerge(VcsProcess.layer), |
| ... | @@ -90,7 +90,7 @@ const appendSeparator = (input: string) => | ... | @@ -90,7 +90,7 @@ const appendSeparator = (input: string) => |
| 90 | : `${input}${platform === "win32" ? "\\" : "/"}`, | 90 | : `${input}${platform === "win32" ? "\\" : "/"}`, |
| 91 | ); | 91 | ); |
| 92 | 92 | ||
| 93 | it.layer(TestLayer, { excludeTestServices: true })("WorkspaceEntries", (it) => { | 93 | it.layer(layerTest, { excludeTestServices: true })("WorkspaceEntries", (it) => { |
| 94 | afterEach(() => { | 94 | afterEach(() => { |
| 95 | vi.restoreAllMocks(); | 95 | vi.restoreAllMocks(); |
| 96 | }); | 96 | }); |
apps/server/src/workspace/WorkspaceFileSystem.test.ts+4-4| ... | @@ -17,13 +17,13 @@ import * as WorkspacePaths from "./WorkspacePaths.ts"; | ... | @@ -17,13 +17,13 @@ import * as WorkspacePaths from "./WorkspacePaths.ts"; |
| 17 | import { HostProcessPlatform } from "@t3tools/shared/hostProcess"; | 17 | import { HostProcessPlatform } from "@t3tools/shared/hostProcess"; |
| 18 | import { symlinksSupported } from "@t3tools/shared/testing/symlinks"; | 18 | import { symlinksSupported } from "@t3tools/shared/testing/symlinks"; |
| 19 | 19 | ||
| 20 | const ProjectLayer = WorkspaceFileSystem.layer.pipe( | 20 | const layerProject = WorkspaceFileSystem.layer.pipe( |
| 21 | Layer.provide(WorkspacePaths.layer), | 21 | Layer.provide(WorkspacePaths.layer), |
| 22 | Layer.provide(WorkspaceEntries.layer.pipe(Layer.provide(WorkspacePaths.layer))), | 22 | Layer.provide(WorkspaceEntries.layer.pipe(Layer.provide(WorkspacePaths.layer))), |
| 23 | ); | 23 | ); |
| 24 | 24 | ||
| 25 | const TestLayer = Layer.empty.pipe( | 25 | const layerTest = Layer.empty.pipe( |
| 26 | Layer.provideMerge(ProjectLayer), | 26 | Layer.provideMerge(layerProject), |
| 27 | Layer.provideMerge(WorkspaceEntries.layer.pipe(Layer.provide(WorkspacePaths.layer))), | 27 | Layer.provideMerge(WorkspaceEntries.layer.pipe(Layer.provide(WorkspacePaths.layer))), |
| 28 | Layer.provideMerge(WorkspacePaths.layer), | 28 | Layer.provideMerge(WorkspacePaths.layer), |
| 29 | Layer.provideMerge(VcsDriverRegistry.layer.pipe(Layer.provide(VcsProcess.layer))), | 29 | Layer.provideMerge(VcsDriverRegistry.layer.pipe(Layer.provide(VcsProcess.layer))), |
| ... | @@ -56,7 +56,7 @@ const writeTextFile = Effect.fn("writeTextFile")(function* ( | ... | @@ -56,7 +56,7 @@ const writeTextFile = Effect.fn("writeTextFile")(function* ( |
| 56 | yield* fileSystem.writeFileString(absolutePath, contents).pipe(Effect.orDie); | 56 | yield* fileSystem.writeFileString(absolutePath, contents).pipe(Effect.orDie); |
| 57 | }); | 57 | }); |
| 58 | 58 | ||
| 59 | it.layer(TestLayer, { excludeTestServices: true })("WorkspaceFileSystemLive", (it) => { | 59 | it.layer(layerTest, { excludeTestServices: true })("WorkspaceFileSystemLive", (it) => { |
| 60 | describe("readFile", () => { | 60 | describe("readFile", () => { |
| 61 | it.effect("reads UTF-8 files relative to the workspace root", () => | 61 | it.effect("reads UTF-8 files relative to the workspace root", () => |
| 62 | Effect.gen(function* () { | 62 | Effect.gen(function* () { |
apps/server/src/workspace/WorkspacePaths.test.ts+2-2| ... | @@ -8,7 +8,7 @@ import * as PlatformError from "effect/PlatformError"; | ... | @@ -8,7 +8,7 @@ import * as PlatformError from "effect/PlatformError"; |
| 8 | 8 | ||
| 9 | import * as WorkspacePaths from "./WorkspacePaths.ts"; | 9 | import * as WorkspacePaths from "./WorkspacePaths.ts"; |
| 10 | 10 | ||
| 11 | const TestLayer = Layer.empty.pipe( | 11 | const layerTest = Layer.empty.pipe( |
| 12 | Layer.provideMerge(WorkspacePaths.layer), | 12 | Layer.provideMerge(WorkspacePaths.layer), |
| 13 | Layer.provideMerge(NodeServices.layer), | 13 | Layer.provideMerge(NodeServices.layer), |
| 14 | ); | 14 | ); |
| ... | @@ -34,7 +34,7 @@ const writeTextFile = Effect.fn("writeTextFile")(function* ( | ... | @@ -34,7 +34,7 @@ const writeTextFile = Effect.fn("writeTextFile")(function* ( |
| 34 | yield* fileSystem.writeFileString(absolutePath, contents).pipe(Effect.orDie); | 34 | yield* fileSystem.writeFileString(absolutePath, contents).pipe(Effect.orDie); |
| 35 | }); | 35 | }); |
| 36 | 36 | ||
| 37 | it.layer(TestLayer)("WorkspacePathsLive", (it) => { | 37 | it.layer(layerTest)("WorkspacePathsLive", (it) => { |
| 38 | describe("normalizeWorkspaceRoot", () => { | 38 | describe("normalizeWorkspaceRoot", () => { |
| 39 | it.effect("resolves an existing directory", () => | 39 | it.effect("resolves an existing directory", () => |
| 40 | Effect.gen(function* () { | 40 | Effect.gen(function* () { |
apps/server/src/ws.ts+6-14| ... | @@ -212,11 +212,8 @@ import * as DirectEndpoints from "./environment/DirectEndpoints.ts"; | ... | @@ -212,11 +212,8 @@ import * as DirectEndpoints from "./environment/DirectEndpoints.ts"; |
| 212 | import * as RemoteOpenTargets from "./environment/RemoteOpenTargets.ts"; | 212 | import * as RemoteOpenTargets from "./environment/RemoteOpenTargets.ts"; |
| 213 | import * as BackgroundPolicy from "./background/BackgroundPolicy.ts"; | 213 | import * as BackgroundPolicy from "./background/BackgroundPolicy.ts"; |
| 214 | import * as EnvironmentAuth from "./auth/EnvironmentAuth.ts"; | 214 | import * as EnvironmentAuth from "./auth/EnvironmentAuth.ts"; |
| 215 | import { | 215 | import { requiredScopeForDeviceList, rpcAuthorizationError } from "./auth/RpcAuthorization.ts"; |
| 216 | requiredScopeForDeviceList, | 216 | import * as RpcAuthorization from "./auth/RpcAuthorization.ts"; |
| 217 | rpcAuthorizationError, | ||
| 218 | rpcScopeAuthorizationLayer, | ||
| 219 | } from "./auth/RpcAuthorization.ts"; | ||
| 220 | import * as ProcessDiagnostics from "./diagnostics/ProcessDiagnostics.ts"; | 217 | import * as ProcessDiagnostics from "./diagnostics/ProcessDiagnostics.ts"; |
| 221 | import * as ProcessResourceMonitor from "./diagnostics/ProcessResourceMonitor.ts"; | 218 | import * as ProcessResourceMonitor from "./diagnostics/ProcessResourceMonitor.ts"; |
| 222 | import * as ResourceTelemetry from "./resourceTelemetry/ResourceTelemetry.ts"; | 219 | import * as ResourceTelemetry from "./resourceTelemetry/ResourceTelemetry.ts"; |
| ... | @@ -1181,7 +1178,7 @@ export const subscribeOrchestrationV2Shell = Effect.fn("ws.orchestrationV2.subsc | ... | @@ -1181,7 +1178,7 @@ export const subscribeOrchestrationV2Shell = Effect.fn("ws.orchestrationV2.subsc |
| 1181 | }, | 1178 | }, |
| 1182 | ); | 1179 | ); |
| 1183 | 1180 | ||
| 1184 | const makeWsRpcLayer = ( | 1181 | const layerWsRpc = ( |
| 1185 | currentSession: EnvironmentAuth.AuthenticatedSession, | 1182 | currentSession: EnvironmentAuth.AuthenticatedSession, |
| 1186 | clientOrigin: OrchestrationClientOrigin, | 1183 | clientOrigin: OrchestrationClientOrigin, |
| 1187 | clientAnalyticsProps: Readonly<Record<string, unknown>>, | 1184 | clientAnalyticsProps: Readonly<Record<string, unknown>>, |
| ... | @@ -3796,7 +3793,7 @@ const makeWsRpcLayer = ( | ... | @@ -3796,7 +3793,7 @@ const makeWsRpcLayer = ( |
| 3796 | }), | 3793 | }), |
| 3797 | ); | 3794 | ); |
| 3798 | 3795 | ||
| 3799 | export const websocketRpcRouteLayer = Layer.unwrap( | 3796 | export const layer = Layer.unwrap( |
| 3800 | Effect.gen(function* () { | 3797 | Effect.gen(function* () { |
| 3801 | const previewAutomationBroker = yield* PreviewAutomationBroker.PreviewAutomationBroker; | 3798 | const previewAutomationBroker = yield* PreviewAutomationBroker.PreviewAutomationBroker; |
| 3802 | const serverSelfUpdate = yield* ServerSelfUpdate.ServerSelfUpdate; | 3799 | const serverSelfUpdate = yield* ServerSelfUpdate.ServerSelfUpdate; |
| ... | @@ -3840,19 +3837,14 @@ export const websocketRpcRouteLayer = Layer.unwrap( | ... | @@ -3840,19 +3837,14 @@ export const websocketRpcRouteLayer = Layer.unwrap( |
| 3840 | const { protocol, httpEffect } = yield* RpcServer.makeProtocolWithHttpEffectWebsocket; | 3837 | const { protocol, httpEffect } = yield* RpcServer.makeProtocolWithHttpEffectWebsocket; |
| 3841 | yield* RpcServer.make(ServerWsRpcGroup, { disableTracing: true }).pipe( | 3838 | yield* RpcServer.make(ServerWsRpcGroup, { disableTracing: true }).pipe( |
| 3842 | Effect.provideService(RpcServer.Protocol, withTerminalOutputWindow(protocol)), | 3839 | Effect.provideService(RpcServer.Protocol, withTerminalOutputWindow(protocol)), |
| 3843 | Effect.provide(rpcScopeAuthorizationLayer(session.scopes)), | 3840 | Effect.provide(RpcAuthorization.layer(session.scopes)), |
| 3844 | Effect.forkScoped, | 3841 | Effect.forkScoped, |
| 3845 | ); | 3842 | ); |
| 3846 | // @effect-diagnostics-next-line returnEffectInGen:off | 3843 | // @effect-diagnostics-next-line returnEffectInGen:off |
| 3847 | return httpEffect; | 3844 | return httpEffect; |
| 3848 | }).pipe( | 3845 | }).pipe( |
| 3849 | Effect.provide( | 3846 | Effect.provide( |
| 3850 | makeWsRpcLayer( | 3847 | layerWsRpc(session, clientOrigin, clientAnalyticsProps, previewAutomationBroker).pipe( |
| 3851 | session, | ||
| 3852 | clientOrigin, | ||
| 3853 | clientAnalyticsProps, | ||
| 3854 | previewAutomationBroker, | ||
| 3855 | ).pipe( | ||
| 3856 | Layer.provideMerge(RpcSerialization.layerJson), | 3848 | Layer.provideMerge(RpcSerialization.layerJson), |
| 3857 | Layer.provide(Layer.succeed(SqlClient.SqlClient, sql)), | 3849 | Layer.provide(Layer.succeed(SqlClient.SqlClient, sql)), |
| 3858 | Layer.provide(AgentSessionScanner.layer), | 3850 | Layer.provide(AgentSessionScanner.layer), |
apps/web/src/cloud/dpop.test.ts+3-2| ... | @@ -4,7 +4,8 @@ import * as Effect from "effect/Effect"; | ... | @@ -4,7 +4,8 @@ import * as Effect from "effect/Effect"; |
| 4 | import { decodeJwt } from "jose"; | 4 | import { decodeJwt } from "jose"; |
| 5 | import { vi } from "vite-plus/test"; | 5 | import { vi } from "vite-plus/test"; |
| 6 | 6 | ||
| 7 | import { browserCryptoLayer, createBrowserDpopProof, generateBrowserDpopKey } from "./dpop"; | 7 | import { createBrowserDpopProof, generateBrowserDpopKey } from "./dpop"; |
| 8 | import * as Dpop from "./dpop"; | ||
| 8 | 9 | ||
| 9 | describe("browser DPoP proofs", () => { | 10 | describe("browser DPoP proofs", () => { |
| 10 | it.effect("signs relay resource proofs with an access-token hash", () => | 11 | it.effect("signs relay resource proofs with an access-token hash", () => |
| ... | @@ -16,7 +17,7 @@ describe("browser DPoP proofs", () => { | ... | @@ -16,7 +17,7 @@ describe("browser DPoP proofs", () => { |
| 16 | url: "https://relay.example.test/v1/environments/env-1/connect?ignored=true", | 17 | url: "https://relay.example.test/v1/environments/env-1/connect?ignored=true", |
| 17 | accessToken: "relay-access-token", | 18 | accessToken: "relay-access-token", |
| 18 | proofKey, | 19 | proofKey, |
| 19 | }).pipe(Effect.provide(browserCryptoLayer)); | 20 | }).pipe(Effect.provide(Dpop.layer)); |
| 20 | const issuedAt = decodeJwt(proof.proof).iat; | 21 | const issuedAt = decodeJwt(proof.proof).iat; |
| 21 | expect(issuedAt).toBeTypeOf("number"); | 22 | expect(issuedAt).toBeTypeOf("number"); |
| 22 | 23 |
apps/web/src/cloud/dpop.ts+1-1| ... | @@ -27,7 +27,7 @@ const DPOP_KEY_STORE_NAME = "keys"; | ... | @@ -27,7 +27,7 @@ const DPOP_KEY_STORE_NAME = "keys"; |
| 27 | const DPOP_KEY_ID = "relay-dpop-proof-key"; | 27 | const DPOP_KEY_ID = "relay-dpop-proof-key"; |
| 28 | const decodeDpopPublicJwk = Schema.decodeUnknownEffect(DpopPublicJwk); | 28 | const decodeDpopPublicJwk = Schema.decodeUnknownEffect(DpopPublicJwk); |
| 29 | 29 | ||
| 30 | export const browserCryptoLayer = Layer.succeed( | 30 | export const layer = Layer.succeed( |
| 31 | Crypto.Crypto, | 31 | Crypto.Crypto, |
| 32 | Crypto.make({ | 32 | Crypto.make({ |
| 33 | randomBytes: (size) => globalThis.crypto.getRandomValues(new Uint8Array(size)), | 33 | randomBytes: (size) => globalThis.crypto.getRandomValues(new Uint8Array(size)), |
apps/web/src/cloud/linkEnvironment.test.ts+11-11| ... | @@ -22,7 +22,7 @@ import { | ... | @@ -22,7 +22,7 @@ import { |
| 22 | import { type RpcSession } from "@t3tools/client-runtime/rpc"; | 22 | import { type RpcSession } from "@t3tools/client-runtime/rpc"; |
| 23 | import { EnvironmentRegistry } from "@t3tools/client-runtime/connection"; | 23 | import { EnvironmentRegistry } from "@t3tools/client-runtime/connection"; |
| 24 | import { ManagedRelay } from "@t3tools/client-runtime/relay"; | 24 | import { ManagedRelay } from "@t3tools/client-runtime/relay"; |
| 25 | import { remoteHttpClientLayer } from "@t3tools/client-runtime/rpc"; | 25 | import { layerRemoteHttpClient } from "@t3tools/client-runtime/rpc"; |
| 26 | import { __resetDesktopPrimaryAuthForTests } from "../environments/primary/desktopAuth"; | 26 | import { __resetDesktopPrimaryAuthForTests } from "../environments/primary/desktopAuth"; |
| 27 | 27 | ||
| 28 | import { | 28 | import { |
| ... | @@ -53,7 +53,7 @@ vi.mock("./relayClientInstallDialog", () => ({ | ... | @@ -53,7 +53,7 @@ vi.mock("./relayClientInstallDialog", () => ({ |
| 53 | })); | 53 | })); |
| 54 | 54 | ||
| 55 | const createProof = vi.fn(() => Effect.succeed("dpop-proof")); | 55 | const createProof = vi.fn(() => Effect.succeed("dpop-proof")); |
| 56 | const dpopSignerLayer = Layer.succeed( | 56 | const layerDpopSigner = Layer.succeed( |
| 57 | ManagedRelay.ManagedRelayDpopSigner, | 57 | ManagedRelay.ManagedRelayDpopSigner, |
| 58 | ManagedRelay.ManagedRelayDpopSigner.of({ | 58 | ManagedRelay.ManagedRelayDpopSigner.of({ |
| 59 | thumbprint: Effect.succeed("thumbprint"), | 59 | thumbprint: Effect.succeed("thumbprint"), |
| ... | @@ -61,18 +61,18 @@ const dpopSignerLayer = Layer.succeed( | ... | @@ -61,18 +61,18 @@ const dpopSignerLayer = Layer.succeed( |
| 61 | }), | 61 | }), |
| 62 | ); | 62 | ); |
| 63 | 63 | ||
| 64 | function relayLayer() { | 64 | function layerRelay() { |
| 65 | const http = remoteHttpClientLayer(globalThis.fetch); | 65 | const layerHttp = layerRemoteHttpClient(globalThis.fetch); |
| 66 | return Layer.mergeAll( | 66 | return Layer.mergeAll( |
| 67 | http, | 67 | layerHttp, |
| 68 | ManagedRelay.layer({ | 68 | ManagedRelay.layer({ |
| 69 | relayUrl: "https://relay.example.test", | 69 | relayUrl: "https://relay.example.test", |
| 70 | clientId: RelayWebClientId, | 70 | clientId: RelayWebClientId, |
| 71 | }).pipe(Layer.provideMerge(dpopSignerLayer), Layer.provide(http)), | 71 | }).pipe(Layer.provideMerge(layerDpopSigner), Layer.provide(layerHttp)), |
| 72 | ); | 72 | ); |
| 73 | } | 73 | } |
| 74 | 74 | ||
| 75 | function registryLayer(options?: { | 75 | function layerRegistry(options?: { |
| 76 | readonly status?: { readonly status: "available"; readonly version: string }; | 76 | readonly status?: { readonly status: "available"; readonly version: string }; |
| 77 | readonly installEvents?: ReadonlyArray<RelayClientInstallProgressEvent>; | 77 | readonly installEvents?: ReadonlyArray<RelayClientInstallProgressEvent>; |
| 78 | }) { | 78 | }) { |
| ... | @@ -119,8 +119,8 @@ function registryLayer(options?: { | ... | @@ -119,8 +119,8 @@ function registryLayer(options?: { |
| 119 | ); | 119 | ); |
| 120 | } | 120 | } |
| 121 | 121 | ||
| 122 | function services(options?: Parameters<typeof registryLayer>[0]) { | 122 | function layerServices(options?: Parameters<typeof layerRegistry>[0]) { |
| 123 | return Layer.mergeAll(relayLayer(), registryLayer(options)); | 123 | return Layer.mergeAll(layerRelay(), layerRegistry(options)); |
| 124 | } | 124 | } |
| 125 | 125 | ||
| 126 | function withServices<A, E>( | 126 | function withServices<A, E>( |
| ... | @@ -131,9 +131,9 @@ function withServices<A, E>( | ... | @@ -131,9 +131,9 @@ function withServices<A, E>( |
| 131 | | ManagedRelay.ManagedRelayClient | 131 | | ManagedRelay.ManagedRelayClient |
| 132 | | EnvironmentRegistry.EnvironmentRegistry | 132 | | EnvironmentRegistry.EnvironmentRegistry |
| 133 | >, | 133 | >, |
| 134 | options?: Parameters<typeof registryLayer>[0], | 134 | options?: Parameters<typeof layerRegistry>[0], |
| 135 | ) { | 135 | ) { |
| 136 | return effect.pipe(Effect.provide(services(options))); | 136 | return effect.pipe(Effect.provide(layerServices(options))); |
| 137 | } | 137 | } |
| 138 | 138 | ||
| 139 | function bodyText(body: BodyInit | null | undefined): string { | 139 | function bodyText(body: BodyInit | null | undefined): string { |
apps/web/src/cloud/linkEnvironment.ts+5-5| ... | @@ -24,7 +24,7 @@ import { request, runStream } from "@t3tools/client-runtime/rpc"; | ... | @@ -24,7 +24,7 @@ import { request, runStream } from "@t3tools/client-runtime/rpc"; |
| 24 | import { makeEnvironmentHttpApiClient } from "@t3tools/client-runtime/rpc"; | 24 | import { makeEnvironmentHttpApiClient } from "@t3tools/client-runtime/rpc"; |
| 25 | import { ManagedRelay, relayProtectedErrorMessage } from "@t3tools/client-runtime/relay"; | 25 | import { ManagedRelay, relayProtectedErrorMessage } from "@t3tools/client-runtime/relay"; |
| 26 | 26 | ||
| 27 | import { primaryEnvironmentHttpLayer } from "../environments/primary/httpLayer"; | 27 | import * as PrimaryEnvironmentHttpLayer from "../environments/primary/httpLayer"; |
| 28 | import { resolveCloudPublicConfig } from "./publicConfig"; | 28 | import { resolveCloudPublicConfig } from "./publicConfig"; |
| 29 | import { | 29 | import { |
| 30 | finishRelayClientInstall, | 30 | finishRelayClientInstall, |
| ... | @@ -193,7 +193,7 @@ export function readPrimaryCloudLinkState(input: { | ... | @@ -193,7 +193,7 @@ export function readPrimaryCloudLinkState(input: { |
| 193 | return yield* client.connect | 193 | return yield* client.connect |
| 194 | .linkState({ headers: {} }) | 194 | .linkState({ headers: {} }) |
| 195 | .pipe(Effect.mapError(environmentApiError("Could not read environment cloud link state."))); | 195 | .pipe(Effect.mapError(environmentApiError("Could not read environment cloud link state."))); |
| 196 | }).pipe(Effect.provide(primaryEnvironmentHttpLayer)); | 196 | }).pipe(Effect.provide(PrimaryEnvironmentHttpLayer.layer)); |
| 197 | } | 197 | } |
| 198 | 198 | ||
| 199 | export function updatePrimaryCloudPreferences(input: { | 199 | export function updatePrimaryCloudPreferences(input: { |
| ... | @@ -212,7 +212,7 @@ export function updatePrimaryCloudPreferences(input: { | ... | @@ -212,7 +212,7 @@ export function updatePrimaryCloudPreferences(input: { |
| 212 | .pipe( | 212 | .pipe( |
| 213 | Effect.mapError(environmentApiError("Could not update environment cloud preferences.")), | 213 | Effect.mapError(environmentApiError("Could not update environment cloud preferences.")), |
| 214 | ); | 214 | ); |
| 215 | }).pipe(Effect.provide(primaryEnvironmentHttpLayer)); | 215 | }).pipe(Effect.provide(PrimaryEnvironmentHttpLayer.layer)); |
| 216 | } | 216 | } |
| 217 | 217 | ||
| 218 | export function unlinkPrimaryEnvironmentFromCloud(input: { | 218 | export function unlinkPrimaryEnvironmentFromCloud(input: { |
| ... | @@ -245,7 +245,7 @@ export function unlinkPrimaryEnvironmentFromCloud(input: { | ... | @@ -245,7 +245,7 @@ export function unlinkPrimaryEnvironmentFromCloud(input: { |
| 245 | ), | 245 | ), |
| 246 | ); | 246 | ); |
| 247 | } | 247 | } |
| 248 | }).pipe(Effect.provide(primaryEnvironmentHttpLayer)); | 248 | }).pipe(Effect.provide(PrimaryEnvironmentHttpLayer.layer)); |
| 249 | } | 249 | } |
| 250 | 250 | ||
| 251 | // "publish_only" links the environment to the relay for agent-activity | 251 | // "publish_only" links the environment to the relay for agent-activity |
| ... | @@ -346,5 +346,5 @@ export function linkPrimaryEnvironmentToCloud(input: { | ... | @@ -346,5 +346,5 @@ export function linkPrimaryEnvironmentToCloud(input: { |
| 346 | }, | 346 | }, |
| 347 | }) | 347 | }) |
| 348 | .pipe(Effect.mapError(environmentApiError("Could not configure environment relay access."))); | 348 | .pipe(Effect.mapError(environmentApiError("Could not configure environment relay access."))); |
| 349 | }).pipe(Effect.provide(primaryEnvironmentHttpLayer)); | 349 | }).pipe(Effect.provide(PrimaryEnvironmentHttpLayer.layer)); |
| 350 | } | 350 | } |
apps/web/src/cloud/managedRelayLayer.ts+3-3| ... | @@ -13,7 +13,7 @@ import { | ... | @@ -13,7 +13,7 @@ import { |
| 13 | type BrowserDpopKey, | 13 | type BrowserDpopKey, |
| 14 | } from "./dpop"; | 14 | } from "./dpop"; |
| 15 | 15 | ||
| 16 | const relayDpopSignerLayer = Layer.effect( | 16 | const layerRelayDpopSigner = Layer.effect( |
| 17 | ManagedRelay.ManagedRelayDpopSigner, | 17 | ManagedRelay.ManagedRelayDpopSigner, |
| 18 | Effect.gen(function* () { | 18 | Effect.gen(function* () { |
| 19 | const crypto = yield* Crypto.Crypto; | 19 | const crypto = yield* Crypto.Crypto; |
| ... | @@ -76,7 +76,7 @@ const relayDpopSignerLayer = Layer.effect( | ... | @@ -76,7 +76,7 @@ const relayDpopSignerLayer = Layer.effect( |
| 76 | }), | 76 | }), |
| 77 | ); | 77 | ); |
| 78 | 78 | ||
| 79 | export const managedRelayClientLayer = (relayUrl: string) => | 79 | export const layer = (relayUrl: string) => |
| 80 | ManagedRelay.layer({ relayUrl, clientId: RelayWebClientId }).pipe( | 80 | ManagedRelay.layer({ relayUrl, clientId: RelayWebClientId }).pipe( |
| 81 | Layer.provideMerge(relayDpopSignerLayer), | 81 | Layer.provideMerge(layerRelayDpopSigner), |
| 82 | ); | 82 | ); |
apps/web/src/connection/platform.ts+28-25| ... | @@ -41,7 +41,7 @@ import { FetchHttpClient } from "effect/http"; | ... | @@ -41,7 +41,7 @@ import { FetchHttpClient } from "effect/http"; |
| 41 | 41 | ||
| 42 | import { APP_VERSION } from "../branding"; | 42 | import { APP_VERSION } from "../branding"; |
| 43 | import { readDesktopPrimaryBearerToken } from "../environments/primary/desktopAuth"; | 43 | import { readDesktopPrimaryBearerToken } from "../environments/primary/desktopAuth"; |
| 44 | import { primaryEnvironmentHttpLayer } from "../environments/primary/httpLayer"; | 44 | import * as PrimaryEnvironmentHttpLayer from "../environments/primary/httpLayer"; |
| 45 | import { | 45 | import { |
| 46 | readPrimaryEnvironmentTarget, | 46 | readPrimaryEnvironmentTarget, |
| 47 | type PrimaryEnvironmentTarget, | 47 | type PrimaryEnvironmentTarget, |
| ... | @@ -56,7 +56,7 @@ import { | ... | @@ -56,7 +56,7 @@ import { |
| 56 | readDesktopSecondaryBootstrapsResult, | 56 | readDesktopSecondaryBootstrapsResult, |
| 57 | type DesktopSecondaryBootstrapsRead, | 57 | type DesktopSecondaryBootstrapsRead, |
| 58 | } from "./desktopLocal"; | 58 | } from "./desktopLocal"; |
| 59 | import { connectionStorageLayer } from "./storage"; | 59 | import * as ConnectionStorage from "./storage"; |
| 60 | import { clientPresentationMetadata } from "./clientMetadata"; | 60 | import { clientPresentationMetadata } from "./clientMetadata"; |
| 61 | 61 | ||
| 62 | let nextObservedRpcRequestId = 0; | 62 | let nextObservedRpcRequestId = 0; |
| ... | @@ -68,7 +68,7 @@ function currentNetworkStatus(): "unknown" | "offline" | "online" { | ... | @@ -68,7 +68,7 @@ function currentNetworkStatus(): "unknown" | "offline" | "online" { |
| 68 | return navigator.onLine ? "online" : "offline"; | 68 | return navigator.onLine ? "online" : "offline"; |
| 69 | } | 69 | } |
| 70 | 70 | ||
| 71 | const connectivityLayer = Connectivity.layer({ | 71 | const layerConnectivity = Connectivity.layer({ |
| 72 | status: Effect.sync(currentNetworkStatus), | 72 | status: Effect.sync(currentNetworkStatus), |
| 73 | changes: Stream.callback((queue) => | 73 | changes: Stream.callback((queue) => |
| 74 | Effect.acquireRelease( | 74 | Effect.acquireRelease( |
| ... | @@ -124,7 +124,7 @@ const networkPathChanges = Stream.callback<"network-changed">((queue) => | ... | @@ -124,7 +124,7 @@ const networkPathChanges = Stream.callback<"network-changed">((queue) => |
| 124 | ).pipe(Effect.asVoid), | 124 | ).pipe(Effect.asVoid), |
| 125 | ); | 125 | ); |
| 126 | 126 | ||
| 127 | const wakeupsLayer = Wakeups.layer({ | 127 | const layerWakeups = Wakeups.layer({ |
| 128 | changes: Stream.mergeAll( | 128 | changes: Stream.mergeAll( |
| 129 | [ | 129 | [ |
| 130 | Stream.callback<"application-active">((queue) => | 130 | Stream.callback<"application-active">((queue) => |
| ... | @@ -223,7 +223,7 @@ export const provisionDesktopSshEnvironment = Effect.fn( | ... | @@ -223,7 +223,7 @@ export const provisionDesktopSshEnvironment = Effect.fn( |
| 223 | }; | 223 | }; |
| 224 | }); | 224 | }); |
| 225 | 225 | ||
| 226 | const capabilitiesLayer = Layer.effectContext( | 226 | const layerCapabilities = Layer.effectContext( |
| 227 | Effect.sync(() => { | 227 | Effect.sync(() => { |
| 228 | const presentation = ClientCapabilities.ClientPresentation.of({ | 228 | const presentation = ClientCapabilities.ClientPresentation.of({ |
| 229 | metadata: clientMetadata(), | 229 | metadata: clientMetadata(), |
| ... | @@ -346,7 +346,10 @@ const loadPrimaryConnectionRegistration = Effect.fn( | ... | @@ -346,7 +346,10 @@ const loadPrimaryConnectionRegistration = Effect.fn( |
| 346 | )(function* (resolved: PrimaryEnvironmentTarget) { | 346 | )(function* (resolved: PrimaryEnvironmentTarget) { |
| 347 | const descriptor = yield* fetchRemoteEnvironmentDescriptor({ | 347 | const descriptor = yield* fetchRemoteEnvironmentDescriptor({ |
| 348 | httpBaseUrl: resolved.target.httpBaseUrl, | 348 | httpBaseUrl: resolved.target.httpBaseUrl, |
| 349 | }).pipe(Effect.provide(primaryEnvironmentHttpLayer), Effect.mapError(mapRemoteEnvironmentError)); | 349 | }).pipe( |
| 350 | Effect.provide(PrimaryEnvironmentHttpLayer.layer), | ||
| 351 | Effect.mapError(mapRemoteEnvironmentError), | ||
| 352 | ); | ||
| 350 | return new PrimaryConnectionRegistration({ | 353 | return new PrimaryConnectionRegistration({ |
| 351 | target: new PrimaryConnectionTarget({ | 354 | target: new PrimaryConnectionTarget({ |
| 352 | environmentId: descriptor.environmentId, | 355 | environmentId: descriptor.environmentId, |
| ... | @@ -511,7 +514,7 @@ export function secondaryRegistrationsToRetainAfterTopologyRead( | ... | @@ -511,7 +514,7 @@ export function secondaryRegistrationsToRetainAfterTopologyRead( |
| 511 | ); | 514 | ); |
| 512 | } | 515 | } |
| 513 | 516 | ||
| 514 | const platformConnectionSourceLayer = Layer.effect( | 517 | const layerPlatformConnectionSource = Layer.effect( |
| 515 | PlatformConnectionSource.PlatformConnectionSource, | 518 | PlatformConnectionSource.PlatformConnectionSource, |
| 516 | Effect.gen(function* () { | 519 | Effect.gen(function* () { |
| 517 | if (isHostedStaticApp() || isLocalEnvironmentDisabled()) { | 520 | if (isHostedStaticApp() || isLocalEnvironmentDisabled()) { |
| ... | @@ -631,7 +634,7 @@ const platformConnectionSourceLayer = Layer.effect( | ... | @@ -631,7 +634,7 @@ const platformConnectionSourceLayer = Layer.effect( |
| 631 | }), | 634 | }), |
| 632 | ); | 635 | ); |
| 633 | 636 | ||
| 634 | const environmentOwnedDataCleanupLayer = Layer.succeed( | 637 | const layerEnvironmentOwnedDataCleanup = Layer.succeed( |
| 635 | Persistence.EnvironmentOwnedDataCleanup, | 638 | Persistence.EnvironmentOwnedDataCleanup, |
| 636 | Persistence.EnvironmentOwnedDataCleanup.of({ | 639 | Persistence.EnvironmentOwnedDataCleanup.of({ |
| 637 | clear: (environmentId) => | 640 | clear: (environmentId) => |
| ... | @@ -641,7 +644,7 @@ const environmentOwnedDataCleanupLayer = Layer.succeed( | ... | @@ -641,7 +644,7 @@ const environmentOwnedDataCleanupLayer = Layer.succeed( |
| 641 | }), | 644 | }), |
| 642 | ); | 645 | ); |
| 643 | 646 | ||
| 644 | const rpcRequestObserverLayer = Layer.succeed( | 647 | const layerRpcRequestObserver = Layer.succeed( |
| 645 | EnvironmentRpcRequestObserver, | 648 | EnvironmentRpcRequestObserver, |
| 646 | EnvironmentRpcRequestObserver.of({ | 649 | EnvironmentRpcRequestObserver.of({ |
| 647 | observe: ({ environmentId, method }) => | 650 | observe: ({ environmentId, method }) => |
| ... | @@ -657,24 +660,24 @@ const rpcRequestObserverLayer = Layer.succeed( | ... | @@ -657,24 +660,24 @@ const rpcRequestObserverLayer = Layer.succeed( |
| 657 | ); | 660 | ); |
| 658 | 661 | ||
| 659 | type ConnectionPlatformLayerSource = | 662 | type ConnectionPlatformLayerSource = |
| 660 | | typeof connectionStorageLayer | 663 | | typeof ConnectionStorage.layer |
| 661 | | typeof connectivityLayer | 664 | | typeof layerConnectivity |
| 662 | | typeof wakeupsLayer | 665 | | typeof layerWakeups |
| 663 | | typeof capabilitiesLayer | 666 | | typeof layerCapabilities |
| 664 | | typeof platformConnectionSourceLayer | 667 | | typeof layerPlatformConnectionSource |
| 665 | | typeof environmentOwnedDataCleanupLayer | 668 | | typeof layerEnvironmentOwnedDataCleanup |
| 666 | | typeof rpcRequestObserverLayer; | 669 | | typeof layerRpcRequestObserver; |
| 667 | 670 | ||
| 668 | export const connectionPlatformLayer: Layer.Layer< | 671 | export const layer: Layer.Layer< |
| 669 | Layer.Success<ConnectionPlatformLayerSource>, | 672 | Layer.Success<ConnectionPlatformLayerSource>, |
| 670 | Layer.Error<ConnectionPlatformLayerSource>, | 673 | Layer.Error<ConnectionPlatformLayerSource>, |
| 671 | Layer.Services<ConnectionPlatformLayerSource> | 674 | Layer.Services<ConnectionPlatformLayerSource> |
| 672 | > = Layer.mergeAll( | 675 | > = Layer.mergeAll( |
| 673 | connectionStorageLayer, | 676 | ConnectionStorage.layer, |
| 674 | connectivityLayer, | 677 | layerConnectivity, |
| 675 | wakeupsLayer, | 678 | layerWakeups, |
| 676 | capabilitiesLayer, | 679 | layerCapabilities, |
| 677 | platformConnectionSourceLayer, | 680 | layerPlatformConnectionSource, |
| 678 | environmentOwnedDataCleanupLayer, | 681 | layerEnvironmentOwnedDataCleanup, |
| 679 | rpcRequestObserverLayer, | 682 | layerRpcRequestObserver, |
| 680 | ); | 683 | ); |
apps/web/src/connection/runtime.ts+19-24| ... | @@ -1,26 +1,21 @@ | ... | @@ -1,26 +1,21 @@ |
| 1 | import { Connection } from "@t3tools/client-runtime/connection"; | 1 | import { Connection } from "@t3tools/client-runtime/connection"; |
| 2 | import { ShellSnapshotLoader } from "@t3tools/client-runtime/state/shell"; | 2 | import { ShellSnapshotLoader } from "@t3tools/client-runtime/state/shell"; |
| 3 | import { | 3 | import { |
| 4 | boundedThreadSnapshotLoaderLayer, | 4 | BoundedThreadSnapshotLoader, |
| 5 | ThreadHistoryController, | 5 | ThreadHistoryController, |
| 6 | } from "@t3tools/client-runtime/state/threads"; | 6 | } from "@t3tools/client-runtime/state/threads"; |
| 7 | import { PullRequestDiffLoader } from "@t3tools/client-runtime/state/pull-requests"; | 7 | import { PullRequestDiffLoader } from "@t3tools/client-runtime/state/pull-requests"; |
| 8 | import * as Layer from "effect/Layer"; | 8 | import * as Layer from "effect/Layer"; |
| 9 | import { Atom } from "effect/reactivity"; | 9 | import { Atom } from "effect/reactivity"; |
| 10 | 10 | ||
| 11 | import { runtimeContextLayer } from "../lib/runtime"; | 11 | import * as Runtime from "../lib/runtime"; |
| 12 | import { | 12 | import * as BackgroundActivityReporter from "../lib/backgroundActivityReporter"; |
| 13 | backgroundActivityObserverLayer, | 13 | import * as ConnectionPlatform from "./platform"; |
| 14 | backgroundActivityReporterLayer, | ||
| 15 | } from "../lib/backgroundActivityReporter"; | ||
| 16 | import { connectionPlatformLayer } from "./platform"; | ||
| 17 | 14 | ||
| 18 | const providedConnectionPlatformLayer = connectionPlatformLayer.pipe( | 15 | const layerProvidedConnectionPlatform = ConnectionPlatform.layer.pipe(Layer.provide(Runtime.layer)); |
| 19 | Layer.provide(runtimeContextLayer), | ||
| 20 | ); | ||
| 21 | 16 | ||
| 22 | const snapshotLoaderLayer = Layer.mergeAll( | 17 | const layerSnapshotLoader = Layer.mergeAll( |
| 23 | boundedThreadSnapshotLoaderLayer, | 18 | BoundedThreadSnapshotLoader.layer, |
| 24 | ShellSnapshotLoader.layer, | 19 | ShellSnapshotLoader.layer, |
| 25 | ThreadHistoryController.layer, | 20 | ThreadHistoryController.layer, |
| 26 | PullRequestDiffLoader.layer, | 21 | PullRequestDiffLoader.layer, |
| ... | @@ -28,13 +23,13 @@ const snapshotLoaderLayer = Layer.mergeAll( | ... | @@ -28,13 +23,13 @@ const snapshotLoaderLayer = Layer.mergeAll( |
| 28 | 23 | ||
| 29 | type ConnectionLayerSource = | 24 | type ConnectionLayerSource = |
| 30 | | typeof Connection.layer | 25 | | typeof Connection.layer |
| 31 | | typeof snapshotLoaderLayer | 26 | | typeof layerSnapshotLoader |
| 32 | | typeof runtimeContextLayer | 27 | | typeof Runtime.layer |
| 33 | | typeof connectionPlatformLayer | 28 | | typeof ConnectionPlatform.layer |
| 34 | | typeof backgroundActivityObserverLayer | 29 | | typeof BackgroundActivityReporter.layerObserver |
| 35 | | typeof backgroundActivityReporterLayer; | 30 | | typeof BackgroundActivityReporter.layer; |
| 36 | 31 | ||
| 37 | const providedClientConnectionLayer = snapshotLoaderLayer.pipe( | 32 | const layerProvidedClientConnection = layerSnapshotLoader.pipe( |
| 38 | Layer.provideMerge( | 33 | Layer.provideMerge( |
| 39 | Connection.layerWithOptions({ | 34 | Connection.layerWithOptions({ |
| 40 | environmentThemes: true, | 35 | environmentThemes: true, |
| ... | @@ -44,18 +39,18 @@ const providedClientConnectionLayer = snapshotLoaderLayer.pipe( | ... | @@ -44,18 +39,18 @@ const providedClientConnectionLayer = snapshotLoaderLayer.pipe( |
| 44 | ), | 39 | ), |
| 45 | Layer.provideMerge( | 40 | Layer.provideMerge( |
| 46 | Layer.mergeAll( | 41 | Layer.mergeAll( |
| 47 | runtimeContextLayer, | 42 | Runtime.layer, |
| 48 | providedConnectionPlatformLayer, | 43 | layerProvidedConnectionPlatform, |
| 49 | backgroundActivityObserverLayer, | 44 | BackgroundActivityReporter.layerObserver, |
| 50 | ), | 45 | ), |
| 51 | ), | 46 | ), |
| 52 | ); | 47 | ); |
| 53 | 48 | ||
| 54 | const connectionLayer = backgroundActivityReporterLayer.pipe( | 49 | const layerConnection = BackgroundActivityReporter.layer.pipe( |
| 55 | Layer.provideMerge(providedClientConnectionLayer), | 50 | Layer.provideMerge(layerProvidedClientConnection), |
| 56 | ); | 51 | ); |
| 57 | 52 | ||
| 58 | export const connectionAtomRuntime: Atom.AtomRuntime< | 53 | export const connectionAtomRuntime: Atom.AtomRuntime< |
| 59 | Layer.Success<ConnectionLayerSource>, | 54 | Layer.Success<ConnectionLayerSource>, |
| 60 | Layer.Error<ConnectionLayerSource> | 55 | Layer.Error<ConnectionLayerSource> |
| 61 | > = Atom.runtime(connectionLayer); | 56 | > = Atom.runtime(layerConnection); |
apps/web/src/connection/storage.ts+1-1| ... | @@ -457,7 +457,7 @@ export function makeBrowserGitHubRoutingPermissions( | ... | @@ -457,7 +457,7 @@ export function makeBrowserGitHubRoutingPermissions( |
| 457 | }); | 457 | }); |
| 458 | } | 458 | } |
| 459 | 459 | ||
| 460 | export const connectionStorageLayer = Layer.effectContext( | 460 | export const layer = Layer.effectContext( |
| 461 | Effect.gen(function* () { | 461 | Effect.gen(function* () { |
| 462 | const database = yield* Effect.acquireRelease(openDatabase(), (database) => | 462 | const database = yield* Effect.acquireRelease(openDatabase(), (database) => |
| 463 | Effect.sync(() => database.close()), | 463 | Effect.sync(() => database.close()), |
apps/web/src/environments/primary/httpLayer.test.ts+3-3| ... | @@ -4,7 +4,7 @@ import * as Effect from "effect/Effect"; | ... | @@ -4,7 +4,7 @@ import * as Effect from "effect/Effect"; |
| 4 | import { HttpClient } from "effect/http"; | 4 | import { HttpClient } from "effect/http"; |
| 5 | 5 | ||
| 6 | import { __resetDesktopPrimaryAuthForTests } from "./desktopAuth"; | 6 | import { __resetDesktopPrimaryAuthForTests } from "./desktopAuth"; |
| 7 | import { makePrimaryEnvironmentHttpLayer } from "./httpLayer"; | 7 | import * as PrimaryEnvironmentHttpLayer from "./httpLayer"; |
| 8 | 8 | ||
| 9 | describe("primary environment HTTP layer", { concurrent: false }, () => { | 9 | describe("primary environment HTTP layer", { concurrent: false }, () => { |
| 10 | afterEach(() => { | 10 | afterEach(() => { |
| ... | @@ -32,7 +32,7 @@ describe("primary environment HTTP layer", { concurrent: false }, () => { | ... | @@ -32,7 +32,7 @@ describe("primary environment HTTP layer", { concurrent: false }, () => { |
| 32 | const request = new Request(fetchMock.mock.calls[0]?.[0], fetchMock.mock.calls[0]?.[1]); | 32 | const request = new Request(fetchMock.mock.calls[0]?.[0], fetchMock.mock.calls[0]?.[1]); |
| 33 | expect(request.credentials).toBe("include"); | 33 | expect(request.credentials).toBe("include"); |
| 34 | expect(request.headers.get("authorization")).toBeNull(); | 34 | expect(request.headers.get("authorization")).toBeNull(); |
| 35 | }).pipe(Effect.provide(makePrimaryEnvironmentHttpLayer())); | 35 | }).pipe(Effect.provide(PrimaryEnvironmentHttpLayer.layerForCurrentOrigin())); |
| 36 | }); | 36 | }); |
| 37 | 37 | ||
| 38 | it.effect("uses bearer auth without cookies for desktop-managed primaries", () => { | 38 | it.effect("uses bearer auth without cookies for desktop-managed primaries", () => { |
| ... | @@ -60,6 +60,6 @@ describe("primary environment HTTP layer", { concurrent: false }, () => { | ... | @@ -60,6 +60,6 @@ describe("primary environment HTTP layer", { concurrent: false }, () => { |
| 60 | const request = new Request(fetchMock.mock.calls[0]?.[0], fetchMock.mock.calls[0]?.[1]); | 60 | const request = new Request(fetchMock.mock.calls[0]?.[0], fetchMock.mock.calls[0]?.[1]); |
| 61 | expect(request.credentials).not.toBe("include"); | 61 | expect(request.credentials).not.toBe("include"); |
| 62 | expect(request.headers.get("authorization")).toBe("Bearer desktop-bearer-token"); | 62 | expect(request.headers.get("authorization")).toBe("Bearer desktop-bearer-token"); |
| 63 | }).pipe(Effect.provide(makePrimaryEnvironmentHttpLayer())); | 63 | }).pipe(Effect.provide(PrimaryEnvironmentHttpLayer.layerForCurrentOrigin())); |
| 64 | }); | 64 | }); |
| 65 | }); | 65 | }); |
apps/web/src/environments/primary/httpLayer.ts+8-8| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | import { remoteHttpClientLayer } from "@t3tools/client-runtime/rpc"; | 1 | import { layerRemoteHttpClient } from "@t3tools/client-runtime/rpc"; |
| 2 | import * as Effect from "effect/Effect"; | 2 | import * as Effect from "effect/Effect"; |
| 3 | import * as Layer from "effect/Layer"; | 3 | import * as Layer from "effect/Layer"; |
| 4 | import { FetchHttpClient, HttpClient, HttpClientRequest } from "effect/http"; | 4 | import { FetchHttpClient, HttpClient, HttpClientRequest } from "effect/http"; |
| ... | @@ -30,28 +30,28 @@ function withPrimaryBearerToken(client: HttpClient.HttpClient): HttpClient.HttpC | ... | @@ -30,28 +30,28 @@ function withPrimaryBearerToken(client: HttpClient.HttpClient): HttpClient.HttpC |
| 30 | ); | 30 | ); |
| 31 | } | 31 | } |
| 32 | 32 | ||
| 33 | export function makePrimaryEnvironmentHttpLayer() { | 33 | export function layerForCurrentOrigin() { |
| 34 | return Layer.unwrap( | 34 | return Layer.unwrap( |
| 35 | Effect.sync(() => { | 35 | Effect.sync(() => { |
| 36 | const baseLayer = remoteHttpClientLayer(globalThis.fetch); | 36 | const layerBase = layerRemoteHttpClient(globalThis.fetch); |
| 37 | if (isSameOriginBrowserPrimary()) { | 37 | if (isSameOriginBrowserPrimary()) { |
| 38 | return Layer.merge( | 38 | return Layer.merge( |
| 39 | baseLayer, | 39 | layerBase, |
| 40 | Layer.succeed(FetchHttpClient.RequestInit, { credentials: "include" }), | 40 | Layer.succeed(FetchHttpClient.RequestInit, { credentials: "include" }), |
| 41 | ); | 41 | ); |
| 42 | } | 42 | } |
| 43 | 43 | ||
| 44 | const bearerClientLayer = Layer.effect( | 44 | const layerBearerClient = Layer.effect( |
| 45 | HttpClient.HttpClient, | 45 | HttpClient.HttpClient, |
| 46 | Effect.map(HttpClient.HttpClient, withPrimaryBearerToken), | 46 | Effect.map(HttpClient.HttpClient, withPrimaryBearerToken), |
| 47 | ).pipe(Layer.provide(baseLayer)); | 47 | ).pipe(Layer.provide(layerBase)); |
| 48 | 48 | ||
| 49 | return Layer.merge( | 49 | return Layer.merge( |
| 50 | bearerClientLayer, | 50 | layerBearerClient, |
| 51 | Layer.succeed(FetchHttpClient.RequestInit, { credentials: "omit" }), | 51 | Layer.succeed(FetchHttpClient.RequestInit, { credentials: "omit" }), |
| 52 | ); | 52 | ); |
| 53 | }), | 53 | }), |
| 54 | ); | 54 | ); |
| 55 | } | 55 | } |
| 56 | 56 | ||
| 57 | export const primaryEnvironmentHttpLayer = makePrimaryEnvironmentHttpLayer(); | 57 | export const layer = layerForCurrentOrigin(); |
apps/web/src/lib/backgroundActivityReporter.ts+2-2| ... | @@ -164,14 +164,14 @@ export function retainedBackgroundScopes( | ... | @@ -164,14 +164,14 @@ export function retainedBackgroundScopes( |
| 164 | ).filter((scope): scope is BackgroundScope => scope !== null); | 164 | ).filter((scope): scope is BackgroundScope => scope !== null); |
| 165 | } | 165 | } |
| 166 | 166 | ||
| 167 | export const backgroundActivityObserverLayer = Layer.succeed( | 167 | export const layerObserver = Layer.succeed( |
| 168 | EnvironmentRpcSubscriptionObserver, | 168 | EnvironmentRpcSubscriptionObserver, |
| 169 | EnvironmentRpcSubscriptionObserver.of({ | 169 | EnvironmentRpcSubscriptionObserver.of({ |
| 170 | observe: observeBackgroundActivitySubscription, | 170 | observe: observeBackgroundActivitySubscription, |
| 171 | }), | 171 | }), |
| 172 | ); | 172 | ); |
| 173 | 173 | ||
| 174 | export const backgroundActivityReporterLayer = Layer.effectDiscard( | 174 | export const layer = Layer.effectDiscard( |
| 175 | Effect.gen(function* () { | 175 | Effect.gen(function* () { |
| 176 | if (typeof window === "undefined" || typeof document === "undefined") { | 176 | if (typeof window === "undefined" || typeof document === "undefined") { |
| 177 | return; | 177 | return; |
apps/web/src/lib/runtime.ts+21-21| ... | @@ -3,13 +3,13 @@ import type * as Effect from "effect/Effect"; | ... | @@ -3,13 +3,13 @@ import type * as Effect from "effect/Effect"; |
| 3 | import * as Layer from "effect/Layer"; | 3 | import * as Layer from "effect/Layer"; |
| 4 | import * as Socket from "effect/socket/Socket"; | 4 | import * as Socket from "effect/socket/Socket"; |
| 5 | 5 | ||
| 6 | import { remoteHttpClientLayer } from "@t3tools/client-runtime/rpc"; | 6 | import { layerRemoteHttpClient } from "@t3tools/client-runtime/rpc"; |
| 7 | import { makeRelayClientTracingLayer } from "@t3tools/shared/relayTracing"; | 7 | import * as RelayTracing from "@t3tools/shared/relayTracing"; |
| 8 | import * as PrimaryEnvironmentHttpClient from "../environments/primary/httpClient"; | 8 | import * as PrimaryEnvironmentHttpClient from "../environments/primary/httpClient"; |
| 9 | import { primaryEnvironmentHttpLayer } from "../environments/primary/httpLayer"; | 9 | import * as PrimaryEnvironmentHttpLayer from "../environments/primary/httpLayer"; |
| 10 | 10 | ||
| 11 | import { browserCryptoLayer } from "../cloud/dpop"; | 11 | import * as Dpop from "../cloud/dpop"; |
| 12 | import { managedRelayClientLayer } from "../cloud/managedRelayLayer"; | 12 | import * as ManagedRelayLayer from "../cloud/managedRelayLayer"; |
| 13 | import { resolveCloudPublicConfig, resolveRelayTracingConfig } from "../cloud/publicConfig"; | 13 | import { resolveCloudPublicConfig, resolveRelayTracingConfig } from "../cloud/publicConfig"; |
| 14 | import * as ClientTracer from "../observability/clientTracer"; | 14 | import * as ClientTracer from "../observability/clientTracer"; |
| 15 | 15 | ||
| ... | @@ -17,24 +17,24 @@ function configuredRelayUrl(): string { | ... | @@ -17,24 +17,24 @@ function configuredRelayUrl(): string { |
| 17 | return resolveCloudPublicConfig().relayUrl ?? "http://relay.invalid"; | 17 | return resolveCloudPublicConfig().relayUrl ?? "http://relay.invalid"; |
| 18 | } | 18 | } |
| 19 | 19 | ||
| 20 | const httpClientLayer = remoteHttpClientLayer((input, init) => globalThis.fetch(input, init)); | 20 | const layerHttpClient = layerRemoteHttpClient((input, init) => globalThis.fetch(input, init)); |
| 21 | const relayTracingLayer = makeRelayClientTracingLayer(resolveRelayTracingConfig(), { | 21 | const layerRelayTracing = RelayTracing.layer(resolveRelayTracingConfig(), { |
| 22 | serviceName: "t3code-web", | 22 | serviceName: "t3code-web", |
| 23 | serviceVersion: import.meta.env.APP_VERSION, | 23 | serviceVersion: import.meta.env.APP_VERSION, |
| 24 | runtime: "browser", | 24 | runtime: "browser", |
| 25 | client: typeof window !== "undefined" && window.desktopBridge ? "desktop" : "web", | 25 | client: typeof window !== "undefined" && window.desktopBridge ? "desktop" : "web", |
| 26 | }).pipe(Layer.provide(httpClientLayer)); | 26 | }).pipe(Layer.provide(layerHttpClient)); |
| 27 | 27 | ||
| 28 | type RuntimeLayerSource = | 28 | type RuntimeLayerSource = |
| 29 | | typeof httpClientLayer | 29 | | typeof layerHttpClient |
| 30 | | typeof browserCryptoLayer | 30 | | typeof Dpop.layer |
| 31 | | typeof Socket.layerWebSocketConstructorGlobal | 31 | | typeof Socket.layerWebSocketConstructorGlobal |
| 32 | | typeof relayTracingLayer | 32 | | typeof layerRelayTracing |
| 33 | | typeof ClientTracer.layer | 33 | | typeof ClientTracer.layer |
| 34 | | ReturnType<typeof managedRelayClientLayer>; | 34 | | ReturnType<typeof ManagedRelayLayer.layer>; |
| 35 | 35 | ||
| 36 | const primaryHttpRuntime = ManagedRuntime.make( | 36 | const primaryHttpRuntime = ManagedRuntime.make( |
| 37 | PrimaryEnvironmentHttpClient.layer.pipe(Layer.provide(primaryEnvironmentHttpLayer)), | 37 | PrimaryEnvironmentHttpClient.layer.pipe(Layer.provide(PrimaryEnvironmentHttpLayer.layer)), |
| 38 | ); | 38 | ); |
| 39 | 39 | ||
| 40 | export type PrimaryHttpEffectRunner = <A, E>( | 40 | export type PrimaryHttpEffectRunner = <A, E>( |
| ... | @@ -54,23 +54,23 @@ export function __setPrimaryHttpRunnerForTests(runner?: PrimaryHttpEffectRunner) | ... | @@ -54,23 +54,23 @@ export function __setPrimaryHttpRunnerForTests(runner?: PrimaryHttpEffectRunner) |
| 54 | primaryHttpRunner = runner ?? livePrimaryHttpRunner; | 54 | primaryHttpRunner = runner ?? livePrimaryHttpRunner; |
| 55 | } | 55 | } |
| 56 | 56 | ||
| 57 | const runtimeLayer = Layer.mergeAll( | 57 | const layerRuntime = Layer.mergeAll( |
| 58 | httpClientLayer, | 58 | layerHttpClient, |
| 59 | browserCryptoLayer, | 59 | Dpop.layer, |
| 60 | Socket.layerWebSocketConstructorGlobal, | 60 | Socket.layerWebSocketConstructorGlobal, |
| 61 | ClientTracer.layer, | 61 | ClientTracer.layer, |
| 62 | relayTracingLayer, | 62 | layerRelayTracing, |
| 63 | managedRelayClientLayer(configuredRelayUrl()).pipe( | 63 | ManagedRelayLayer.layer(configuredRelayUrl()).pipe( |
| 64 | Layer.provide(Layer.mergeAll(httpClientLayer, browserCryptoLayer)), | 64 | Layer.provide(Layer.mergeAll(layerHttpClient, Dpop.layer)), |
| 65 | ), | 65 | ), |
| 66 | ); | 66 | ); |
| 67 | 67 | ||
| 68 | export const runtime: ManagedRuntime.ManagedRuntime< | 68 | export const runtime: ManagedRuntime.ManagedRuntime< |
| 69 | Layer.Success<RuntimeLayerSource>, | 69 | Layer.Success<RuntimeLayerSource>, |
| 70 | Layer.Error<RuntimeLayerSource> | 70 | Layer.Error<RuntimeLayerSource> |
| 71 | > = ManagedRuntime.make(runtimeLayer); | 71 | > = ManagedRuntime.make(layerRuntime); |
| 72 | 72 | ||
| 73 | export const runtimeContextLayer: Layer.Layer< | 73 | export const layer: Layer.Layer< |
| 74 | Layer.Success<RuntimeLayerSource>, | 74 | Layer.Success<RuntimeLayerSource>, |
| 75 | Layer.Error<RuntimeLayerSource> | 75 | Layer.Error<RuntimeLayerSource> |
| 76 | > = Layer.effectContext(runtime.contextEffect); | 76 | > = Layer.effectContext(runtime.contextEffect); |
apps/web/src/observability/clientTracing.ts+4-4| ... | @@ -9,7 +9,7 @@ import { settleAsyncResult, squashAtomCommandFailure } from "@t3tools/client-run | ... | @@ -9,7 +9,7 @@ import { settleAsyncResult, squashAtomCommandFailure } from "@t3tools/client-run |
| 9 | import { safeErrorLogAttributes } from "@t3tools/client-runtime/errors"; | 9 | import { safeErrorLogAttributes } from "@t3tools/client-runtime/errors"; |
| 10 | import { resolvePrimaryEnvironmentHttpUrl } from "../environments/primary"; | 10 | import { resolvePrimaryEnvironmentHttpUrl } from "../environments/primary"; |
| 11 | import * as ClientTracer from "./clientTracer"; | 11 | import * as ClientTracer from "./clientTracer"; |
| 12 | import { primaryEnvironmentHttpLayer } from "../environments/primary/httpLayer"; | 12 | import * as PrimaryEnvironmentHttpLayer from "../environments/primary/httpLayer"; |
| 13 | import { isElectron } from "../env"; | 13 | import { isElectron } from "../env"; |
| 14 | import { APP_VERSION } from "~/branding"; | 14 | import { APP_VERSION } from "~/branding"; |
| 15 | 15 | ||
| ... | @@ -24,8 +24,8 @@ const CLIENT_TRACING_RESOURCE = { | ... | @@ -24,8 +24,8 @@ const CLIENT_TRACING_RESOURCE = { |
| 24 | }, | 24 | }, |
| 25 | } as const; | 25 | } as const; |
| 26 | 26 | ||
| 27 | const delegateRuntimeLayer = Layer.mergeAll( | 27 | const layerDelegateRuntime = Layer.mergeAll( |
| 28 | primaryEnvironmentHttpLayer, | 28 | PrimaryEnvironmentHttpLayer.layer, |
| 29 | OtlpExporter.layerFlusher, | 29 | OtlpExporter.layerFlusher, |
| 30 | OtlpSerialization.layerJson, | 30 | OtlpSerialization.layerJson, |
| 31 | Layer.succeed(HttpClient.TracerDisabledWhen, () => true), | 31 | Layer.succeed(HttpClient.TracerDisabledWhen, () => true), |
| ... | @@ -70,7 +70,7 @@ async function applyClientTracingConfig(config: ClientTracingConfig): Promise<vo | ... | @@ -70,7 +70,7 @@ async function applyClientTracingConfig(config: ClientTracingConfig): Promise<vo |
| 70 | 70 | ||
| 71 | await disposeTracerRuntime(previousRuntime, previousScope); | 71 | await disposeTracerRuntime(previousRuntime, previousScope); |
| 72 | 72 | ||
| 73 | const runtime = ManagedRuntime.make(delegateRuntimeLayer); | 73 | const runtime = ManagedRuntime.make(layerDelegateRuntime); |
| 74 | const scope = runtime.runSync(Scope.make()); | 74 | const scope = runtime.runSync(Scope.make()); |
| 75 | 75 | ||
| 76 | const delegateResult = await settleAsyncResult(() => | 76 | const delegateResult = await settleAsyncResult(() => |
infra/relay/alchemy.run.ts+3-3| ... | @@ -12,7 +12,7 @@ import { PublishClientConfig, tokenDigest } from "./src/clientConfig.ts"; | ... | @@ -12,7 +12,7 @@ import { PublishClientConfig, tokenDigest } from "./src/clientConfig.ts"; |
| 12 | import * as RelayDb from "./src/db.ts"; | 12 | import * as RelayDb from "./src/db.ts"; |
| 13 | import { RelayObservability } from "./src/observability.ts"; | 13 | import { RelayObservability } from "./src/observability.ts"; |
| 14 | import { ManagedEndpointZone, RelayApiZone } from "./src/zone.ts"; | 14 | import { ManagedEndpointZone, RelayApiZone } from "./src/zone.ts"; |
| 15 | import ApiLive, { Api } from "./src/worker.ts"; | 15 | import * as RelayWorker from "./src/worker.ts"; |
| 16 | 16 | ||
| 17 | export default Alchemy.Stack( | 17 | export default Alchemy.Stack( |
| 18 | "T3CodeRelay", | 18 | "T3CodeRelay", |
| ... | @@ -31,7 +31,7 @@ export default Alchemy.Stack( | ... | @@ -31,7 +31,7 @@ export default Alchemy.Stack( |
| 31 | const managedEndpointZone = yield* ManagedEndpointZone.pipe(Effect.orDie); | 31 | const managedEndpointZone = yield* ManagedEndpointZone.pipe(Effect.orDie); |
| 32 | const relayApiZone = yield* RelayApiZone.pipe(Effect.orDie); | 32 | const relayApiZone = yield* RelayApiZone.pipe(Effect.orDie); |
| 33 | const observability = yield* RelayObservability; | 33 | const observability = yield* RelayObservability; |
| 34 | const api = yield* Api; | 34 | const api = yield* RelayWorker.Api; |
| 35 | yield* PublishClientConfig({ | 35 | yield* PublishClientConfig({ |
| 36 | url: api.url, | 36 | url: api.url, |
| 37 | mobileTracingUrl: observability.traces.otelTracesEndpoint, | 37 | mobileTracingUrl: observability.traces.otelTracesEndpoint, |
| ... | @@ -61,5 +61,5 @@ export default Alchemy.Stack( | ... | @@ -61,5 +61,5 @@ export default Alchemy.Stack( |
| 61 | clientTracingDataset: observability.traces.name, | 61 | clientTracingDataset: observability.traces.name, |
| 62 | clientTracingToken: observability.clientIngestToken.token, | 62 | clientTracingToken: observability.clientIngestToken.token, |
| 63 | }; | 63 | }; |
| 64 | }).pipe(Effect.provide(ApiLive)), | 64 | }).pipe(Effect.provide(RelayWorker.layer)), |
| 65 | ); | 65 | ); |
infra/relay/scripts/android-push-watch.ts+7-5| ... | @@ -86,7 +86,7 @@ const main = Effect.gen(function* () { | ... | @@ -86,7 +86,7 @@ const main = Effect.gen(function* () { |
| 86 | const connection = yield* readFile(connectionPath).pipe( | 86 | const connection = yield* readFile(connectionPath).pipe( |
| 87 | Effect.flatMap(Schema.decodeUnknownEffect(Schema.fromJsonString(Connection))), | 87 | Effect.flatMap(Schema.decodeUnknownEffect(Schema.fromJsonString(Connection))), |
| 88 | ); | 88 | ); |
| 89 | const socketConstructor = Layer.succeed( | 89 | const layerSocketConstructor = Layer.succeed( |
| 90 | Socket.WebSocketConstructor, | 90 | Socket.WebSocketConstructor, |
| 91 | // Socket.makeWebSocket only ever passes its `protocols` option here. | 91 | // Socket.makeWebSocket only ever passes its `protocols` option here. |
| 92 | (url, protocols) => | 92 | (url, protocols) => |
| ... | @@ -94,11 +94,13 @@ const main = Effect.gen(function* () { | ... | @@ -94,11 +94,13 @@ const main = Effect.gen(function* () { |
| 94 | headers: { authorization: `Bearer ${connection.bearerToken}` }, | 94 | headers: { authorization: `Bearer ${connection.bearerToken}` }, |
| 95 | }) as unknown as globalThis.WebSocket, | 95 | }) as unknown as globalThis.WebSocket, |
| 96 | ); | 96 | ); |
| 97 | const protocol = RpcClient.layerProtocolSocket().pipe( | 97 | const layerProtocol = RpcClient.layerProtocolSocket().pipe( |
| 98 | Layer.provide(Socket.layerWebSocket(connection.wsUrl).pipe(Layer.provide(socketConstructor))), | 98 | Layer.provide( |
| 99 | Socket.layerWebSocket(connection.wsUrl).pipe(Layer.provide(layerSocketConstructor)), | ||
| 100 | ), | ||
| 99 | Layer.provide(RpcSerialization.layerJson), | 101 | Layer.provide(RpcSerialization.layerJson), |
| 100 | ); | 102 | ); |
| 101 | const fcm = FcmClient.layer.pipe( | 103 | const layerFcm = FcmClient.layer.pipe( |
| 102 | Layer.provide( | 104 | Layer.provide( |
| 103 | FcmAssertionSigner.layer.pipe( | 105 | FcmAssertionSigner.layer.pipe( |
| 104 | Layer.provide(Layer.succeed(WebCrypto.WebCrypto, { subtle: globalThis.crypto.subtle })), | 106 | Layer.provide(Layer.succeed(WebCrypto.WebCrypto, { subtle: globalThis.crypto.subtle })), |
| ... | @@ -206,7 +208,7 @@ const main = Effect.gen(function* () { | ... | @@ -206,7 +208,7 @@ const main = Effect.gen(function* () { |
| 206 | }), | 208 | }), |
| 207 | ), | 209 | ), |
| 208 | ); | 210 | ); |
| 209 | }).pipe(Effect.provide(Layer.mergeAll(protocol, fcm))); | 211 | }).pipe(Effect.provide(Layer.mergeAll(layerProtocol, layerFcm))); |
| 210 | }); | 212 | }); |
| 211 | 213 | ||
| 212 | NodeRuntime.runMain( | 214 | NodeRuntime.runMain( |
infra/relay/src/agentActivity/AgentActivityPublisher.test.ts+6-6| ... | @@ -10,7 +10,7 @@ import * as AgentActivityPublisher from "./AgentActivityPublisher.ts"; | ... | @@ -10,7 +10,7 @@ import * as AgentActivityPublisher from "./AgentActivityPublisher.ts"; |
| 10 | import * as FcmDeliveries from "./FcmDeliveries.ts"; | 10 | import * as FcmDeliveries from "./FcmDeliveries.ts"; |
| 11 | import * as ApnsDeliveries from "./ApnsDeliveries.ts"; | 11 | import * as ApnsDeliveries from "./ApnsDeliveries.ts"; |
| 12 | 12 | ||
| 13 | const publisherLayer = AgentActivityPublisher.layer.pipe( | 13 | const layerPublisher = AgentActivityPublisher.layer.pipe( |
| 14 | Layer.provide( | 14 | Layer.provide( |
| 15 | Layer.succeed(FcmDeliveries.FcmDeliveries, { | 15 | Layer.succeed(FcmDeliveries.FcmDeliveries, { |
| 16 | enqueue: () => Effect.succeed(null), | 16 | enqueue: () => Effect.succeed(null), |
| ... | @@ -225,7 +225,7 @@ describe("AgentActivityPublisher", () => { | ... | @@ -225,7 +225,7 @@ describe("AgentActivityPublisher", () => { |
| 225 | }); | 225 | }); |
| 226 | }).pipe( | 226 | }).pipe( |
| 227 | Effect.provide( | 227 | Effect.provide( |
| 228 | publisherLayer.pipe( | 228 | layerPublisher.pipe( |
| 229 | Layer.provide( | 229 | Layer.provide( |
| 230 | Layer.mergeAll( | 230 | Layer.mergeAll( |
| 231 | Layer.succeed(AgentActivityRows.AgentActivityRows, makeAgentActivityRows()), | 231 | Layer.succeed(AgentActivityRows.AgentActivityRows, makeAgentActivityRows()), |
| ... | @@ -298,7 +298,7 @@ describe("AgentActivityPublisher", () => { | ... | @@ -298,7 +298,7 @@ describe("AgentActivityPublisher", () => { |
| 298 | }); | 298 | }); |
| 299 | }).pipe( | 299 | }).pipe( |
| 300 | Effect.provide( | 300 | Effect.provide( |
| 301 | publisherLayer.pipe( | 301 | layerPublisher.pipe( |
| 302 | Layer.provide( | 302 | Layer.provide( |
| 303 | Layer.mergeAll( | 303 | Layer.mergeAll( |
| 304 | Layer.succeed( | 304 | Layer.succeed( |
| ... | @@ -392,7 +392,7 @@ describe("AgentActivityPublisher", () => { | ... | @@ -392,7 +392,7 @@ describe("AgentActivityPublisher", () => { |
| 392 | }); | 392 | }); |
| 393 | }).pipe( | 393 | }).pipe( |
| 394 | Effect.provide( | 394 | Effect.provide( |
| 395 | publisherLayer.pipe( | 395 | layerPublisher.pipe( |
| 396 | Layer.provide( | 396 | Layer.provide( |
| 397 | Layer.mergeAll( | 397 | Layer.mergeAll( |
| 398 | Layer.succeed( | 398 | Layer.succeed( |
| ... | @@ -498,7 +498,7 @@ describe("AgentActivityPublisher", () => { | ... | @@ -498,7 +498,7 @@ describe("AgentActivityPublisher", () => { |
| 498 | }); | 498 | }); |
| 499 | }).pipe( | 499 | }).pipe( |
| 500 | Effect.provide( | 500 | Effect.provide( |
| 501 | publisherLayer.pipe( | 501 | layerPublisher.pipe( |
| 502 | Layer.provide( | 502 | Layer.provide( |
| 503 | Layer.mergeAll( | 503 | Layer.mergeAll( |
| 504 | Layer.succeed( | 504 | Layer.succeed( |
| ... | @@ -610,7 +610,7 @@ describe("AgentActivityPublisher", () => { | ... | @@ -610,7 +610,7 @@ describe("AgentActivityPublisher", () => { |
| 610 | }); | 610 | }); |
| 611 | }).pipe( | 611 | }).pipe( |
| 612 | Effect.provide( | 612 | Effect.provide( |
| 613 | publisherLayer.pipe( | 613 | layerPublisher.pipe( |
| 614 | Layer.provide( | 614 | Layer.provide( |
| 615 | Layer.mergeAll( | 615 | Layer.mergeAll( |
| 616 | Layer.succeed( | 616 | Layer.succeed( |
infra/relay/src/agentActivity/ApnsClient.test.ts+7-7| ... | @@ -22,7 +22,7 @@ import * as ApnsProviderTokens from "./ApnsProviderTokens.ts"; | ... | @@ -22,7 +22,7 @@ import * as ApnsProviderTokens from "./ApnsProviderTokens.ts"; |
| 22 | const isApnsJwtSigningError = Schema.is(ApnsClient.ApnsJwtSigningError); | 22 | const isApnsJwtSigningError = Schema.is(ApnsClient.ApnsJwtSigningError); |
| 23 | const isApnsHttpRequestError = Schema.is(ApnsClient.ApnsHttpRequestError); | 23 | const isApnsHttpRequestError = Schema.is(ApnsClient.ApnsHttpRequestError); |
| 24 | 24 | ||
| 25 | const TestLayer = ApnsClient.layer.pipe( | 25 | const layerTest = ApnsClient.layer.pipe( |
| 26 | Layer.provide(ApnsProviderTokens.layer), | 26 | Layer.provide(ApnsProviderTokens.layer), |
| 27 | Layer.provide( | 27 | Layer.provide( |
| 28 | Layer.succeed( | 28 | Layer.succeed( |
| ... | @@ -76,7 +76,7 @@ describe("ApnsClient", () => { | ... | @@ -76,7 +76,7 @@ describe("ApnsClient", () => { |
| 76 | }, | 76 | }, |
| 77 | }, | 77 | }, |
| 78 | }); | 78 | }); |
| 79 | }).pipe(Effect.provide(TestLayer)), | 79 | }).pipe(Effect.provide(layerTest)), |
| 80 | ); | 80 | ); |
| 81 | 81 | ||
| 82 | it.effect("builds a low-priority update payload", () => | 82 | it.effect("builds a low-priority update payload", () => |
| ... | @@ -99,7 +99,7 @@ describe("ApnsClient", () => { | ... | @@ -99,7 +99,7 @@ describe("ApnsClient", () => { |
| 99 | }, | 99 | }, |
| 100 | }, | 100 | }, |
| 101 | }); | 101 | }); |
| 102 | }).pipe(Effect.provide(TestLayer)), | 102 | }).pipe(Effect.provide(layerTest)), |
| 103 | ); | 103 | ); |
| 104 | 104 | ||
| 105 | it.effect("builds a high-priority alerting update payload when an alert is attached", () => | 105 | it.effect("builds a high-priority alerting update payload when an alert is attached", () => |
| ... | @@ -125,7 +125,7 @@ describe("ApnsClient", () => { | ... | @@ -125,7 +125,7 @@ describe("ApnsClient", () => { |
| 125 | }, | 125 | }, |
| 126 | }, | 126 | }, |
| 127 | }); | 127 | }); |
| 128 | }).pipe(Effect.provide(TestLayer)), | 128 | }).pipe(Effect.provide(layerTest)), |
| 129 | ); | 129 | ); |
| 130 | 130 | ||
| 131 | it.effect("builds an end payload with a dismissal date", () => | 131 | it.effect("builds an end payload with a dismissal date", () => |
| ... | @@ -162,7 +162,7 @@ describe("ApnsClient", () => { | ... | @@ -162,7 +162,7 @@ describe("ApnsClient", () => { |
| 162 | "dismissal-date": 15, | 162 | "dismissal-date": 15, |
| 163 | }, | 163 | }, |
| 164 | }); | 164 | }); |
| 165 | }).pipe(Effect.provide(TestLayer)), | 165 | }).pipe(Effect.provide(layerTest)), |
| 166 | ); | 166 | ); |
| 167 | 167 | ||
| 168 | it.effect("builds a standard APNs alert payload with routing metadata", () => | 168 | it.effect("builds a standard APNs alert payload with routing metadata", () => |
| ... | @@ -193,7 +193,7 @@ describe("ApnsClient", () => { | ... | @@ -193,7 +193,7 @@ describe("ApnsClient", () => { |
| 193 | threadId: "thread", | 193 | threadId: "thread", |
| 194 | deepLink: "/threads/env/thread", | 194 | deepLink: "/threads/env/thread", |
| 195 | }); | 195 | }); |
| 196 | }).pipe(Effect.provide(TestLayer)), | 196 | }).pipe(Effect.provide(layerTest)), |
| 197 | ); | 197 | ); |
| 198 | 198 | ||
| 199 | it.effect("preserves JWT signing context and the crypto cause", () => | 199 | it.effect("preserves JWT signing context and the crypto cause", () => |
| ... | @@ -237,7 +237,7 @@ describe("ApnsClient", () => { | ... | @@ -237,7 +237,7 @@ describe("ApnsClient", () => { |
| 237 | cause: expect.any(Error), | 237 | cause: expect.any(Error), |
| 238 | message: "Failed to sign APNs JWT for key key-1.", | 238 | message: "Failed to sign APNs JWT for key key-1.", |
| 239 | }); | 239 | }); |
| 240 | }).pipe(Effect.provide(TestLayer)), | 240 | }).pipe(Effect.provide(layerTest)), |
| 241 | ); | 241 | ); |
| 242 | 242 | ||
| 243 | it.effect("preserves APNs request context and the HTTP cause", () => { | 243 | it.effect("preserves APNs request context and the HTTP cause", () => { |
infra/relay/src/agentActivity/ApnsDeliveries.test.ts+37-37| ... | @@ -141,7 +141,7 @@ const target: LiveActivities.TargetRow = { | ... | @@ -141,7 +141,7 @@ const target: LiveActivities.TargetRow = { |
| 141 | last_live_activity_delivery_at: null, | 141 | last_live_activity_delivery_at: null, |
| 142 | }; | 142 | }; |
| 143 | 143 | ||
| 144 | function makeLayer(input: { | 144 | function layerFor(input: { |
| 145 | readonly attempts: Array<DeliveryAttempts.DeliveryAttemptInput>; | 145 | readonly attempts: Array<DeliveryAttempts.DeliveryAttemptInput>; |
| 146 | readonly sourceJobClaims?: ReadonlyMap<string, DeliveryAttempts.DeliverySourceJobClaimResult>; | 146 | readonly sourceJobClaims?: ReadonlyMap<string, DeliveryAttempts.DeliverySourceJobClaimResult>; |
| 147 | readonly queuedJobs?: Array<SignedApnsDeliveryJob>; | 147 | readonly queuedJobs?: Array<SignedApnsDeliveryJob>; |
| ... | @@ -284,7 +284,7 @@ describe("ApnsDeliveries", () => { | ... | @@ -284,7 +284,7 @@ describe("ApnsDeliveries", () => { |
| 284 | expect(attempts).toHaveLength(0); | 284 | expect(attempts).toHaveLength(0); |
| 285 | }).pipe( | 285 | }).pipe( |
| 286 | Effect.provide( | 286 | Effect.provide( |
| 287 | makeLayer({ | 287 | layerFor({ |
| 288 | attempts, | 288 | attempts, |
| 289 | queuedJobs, | 289 | queuedJobs, |
| 290 | config: { ...config, apns: null }, | 290 | config: { ...config, apns: null }, |
| ... | @@ -322,7 +322,7 @@ describe("ApnsDeliveries", () => { | ... | @@ -322,7 +322,7 @@ describe("ApnsDeliveries", () => { |
| 322 | expect(queuedJobs).toEqual([]); | 322 | expect(queuedJobs).toEqual([]); |
| 323 | expect(queuedStarts).toEqual([]); | 323 | expect(queuedStarts).toEqual([]); |
| 324 | expect(attempts).toEqual([]); | 324 | expect(attempts).toEqual([]); |
| 325 | }).pipe(Effect.provide(makeLayer({ attempts, queuedJobs, queuedStarts }))); | 325 | }).pipe(Effect.provide(layerFor({ attempts, queuedJobs, queuedStarts }))); |
| 326 | }); | 326 | }); |
| 327 | 327 | ||
| 328 | it.effect("ends the armed card when nothing remains to show", () => { | 328 | it.effect("ends the armed card when nothing remains to show", () => { |
| ... | @@ -362,7 +362,7 @@ describe("ApnsDeliveries", () => { | ... | @@ -362,7 +362,7 @@ describe("ApnsDeliveries", () => { |
| 362 | }, | 362 | }, |
| 363 | ]); | 363 | ]); |
| 364 | expect(attempts).toEqual([]); | 364 | expect(attempts).toEqual([]); |
| 365 | }).pipe(Effect.provide(makeLayer({ attempts, queuedJobs }))); | 365 | }).pipe(Effect.provide(layerFor({ attempts, queuedJobs }))); |
| 366 | }); | 366 | }); |
| 367 | 367 | ||
| 368 | it.effect("does not queue a remote start when Live Activities are disabled", () => { | 368 | it.effect("does not queue a remote start when Live Activities are disabled", () => { |
| ... | @@ -386,7 +386,7 @@ describe("ApnsDeliveries", () => { | ... | @@ -386,7 +386,7 @@ describe("ApnsDeliveries", () => { |
| 386 | expect(result).toBeNull(); | 386 | expect(result).toBeNull(); |
| 387 | expect(queuedJobs).toEqual([]); | 387 | expect(queuedJobs).toEqual([]); |
| 388 | expect(attempts).toEqual([]); | 388 | expect(attempts).toEqual([]); |
| 389 | }).pipe(Effect.provide(makeLayer({ attempts, queuedJobs }))); | 389 | }).pipe(Effect.provide(layerFor({ attempts, queuedJobs }))); |
| 390 | }); | 390 | }); |
| 391 | 391 | ||
| 392 | it.effect("does not queue a duplicate remote start while a start is already queued", () => { | 392 | it.effect("does not queue a duplicate remote start while a start is already queued", () => { |
| ... | @@ -410,7 +410,7 @@ describe("ApnsDeliveries", () => { | ... | @@ -410,7 +410,7 @@ describe("ApnsDeliveries", () => { |
| 410 | expect(result).toBeNull(); | 410 | expect(result).toBeNull(); |
| 411 | expect(queuedJobs).toEqual([]); | 411 | expect(queuedJobs).toEqual([]); |
| 412 | expect(attempts).toEqual([]); | 412 | expect(attempts).toEqual([]); |
| 413 | }).pipe(Effect.provide(makeLayer({ attempts, queuedJobs }))); | 413 | }).pipe(Effect.provide(layerFor({ attempts, queuedJobs }))); |
| 414 | }); | 414 | }); |
| 415 | 415 | ||
| 416 | it.effect("queues bounded Live Activity aggregate payloads", () => { | 416 | it.effect("queues bounded Live Activity aggregate payloads", () => { |
| ... | @@ -449,7 +449,7 @@ describe("ApnsDeliveries", () => { | ... | @@ -449,7 +449,7 @@ describe("ApnsDeliveries", () => { |
| 449 | expect(payloadAggregate?.activities[0]?.status.length).toBeLessThanOrEqual(40); | 449 | expect(payloadAggregate?.activities[0]?.status.length).toBeLessThanOrEqual(40); |
| 450 | expect(payloadAggregate?.activities[0]?.deepLink).toBe("/"); | 450 | expect(payloadAggregate?.activities[0]?.deepLink).toBe("/"); |
| 451 | expect(attempts).toEqual([]); | 451 | expect(attempts).toEqual([]); |
| 452 | }).pipe(Effect.provide(makeLayer({ attempts, queuedJobs }))); | 452 | }).pipe(Effect.provide(layerFor({ attempts, queuedJobs }))); |
| 453 | }); | 453 | }); |
| 454 | 454 | ||
| 455 | it.effect("queues Live Activity jobs with the device's APNs routing", () => { | 455 | it.effect("queues Live Activity jobs with the device's APNs routing", () => { |
| ... | @@ -481,7 +481,7 @@ describe("ApnsDeliveries", () => { | ... | @@ -481,7 +481,7 @@ describe("ApnsDeliveries", () => { |
| 481 | }, | 481 | }, |
| 482 | }, | 482 | }, |
| 483 | ]); | 483 | ]); |
| 484 | }).pipe(Effect.provide(makeLayer({ attempts, queuedJobs }))); | 484 | }).pipe(Effect.provide(layerFor({ attempts, queuedJobs }))); |
| 485 | }); | 485 | }); |
| 486 | 486 | ||
| 487 | it.effect("sends signed jobs to the device's APNs environment and bundle topic", () => { | 487 | it.effect("sends signed jobs to the device's APNs environment and bundle topic", () => { |
| ... | @@ -521,7 +521,7 @@ describe("ApnsDeliveries", () => { | ... | @@ -521,7 +521,7 @@ describe("ApnsDeliveries", () => { |
| 521 | ); | 521 | ); |
| 522 | }).pipe( | 522 | }).pipe( |
| 523 | Effect.provide( | 523 | Effect.provide( |
| 524 | makeLayer({ | 524 | layerFor({ |
| 525 | attempts, | 525 | attempts, |
| 526 | currentTargets: [ | 526 | currentTargets: [ |
| 527 | { ...target, bundle_id: "com.t3tools.t3code.preview", aps_environment: "sandbox" }, | 527 | { ...target, bundle_id: "com.t3tools.t3code.preview", aps_environment: "sandbox" }, |
| ... | @@ -568,7 +568,7 @@ describe("ApnsDeliveries", () => { | ... | @@ -568,7 +568,7 @@ describe("ApnsDeliveries", () => { |
| 568 | expect(result).toBeNull(); | 568 | expect(result).toBeNull(); |
| 569 | expect(queuedJobs).toEqual([]); | 569 | expect(queuedJobs).toEqual([]); |
| 570 | expect(attempts).toEqual([]); | 570 | expect(attempts).toEqual([]); |
| 571 | }).pipe(Effect.provide(makeLayer({ attempts, queuedJobs }))); | 571 | }).pipe(Effect.provide(layerFor({ attempts, queuedJobs }))); |
| 572 | }, | 572 | }, |
| 573 | ); | 573 | ); |
| 574 | 574 | ||
| ... | @@ -612,7 +612,7 @@ describe("ApnsDeliveries", () => { | ... | @@ -612,7 +612,7 @@ describe("ApnsDeliveries", () => { |
| 612 | }, | 612 | }, |
| 613 | }, | 613 | }, |
| 614 | ]); | 614 | ]); |
| 615 | }).pipe(Effect.provide(makeLayer({ attempts, queuedJobs }))); | 615 | }).pipe(Effect.provide(layerFor({ attempts, queuedJobs }))); |
| 616 | }, | 616 | }, |
| 617 | ); | 617 | ); |
| 618 | 618 | ||
| ... | @@ -641,7 +641,7 @@ describe("ApnsDeliveries", () => { | ... | @@ -641,7 +641,7 @@ describe("ApnsDeliveries", () => { |
| 641 | 641 | ||
| 642 | expect(result).toBeNull(); | 642 | expect(result).toBeNull(); |
| 643 | expect(queuedJobs).toEqual([]); | 643 | expect(queuedJobs).toEqual([]); |
| 644 | }).pipe(Effect.provide(makeLayer({ attempts, queuedJobs }))); | 644 | }).pipe(Effect.provide(layerFor({ attempts, queuedJobs }))); |
| 645 | }, | 645 | }, |
| 646 | ); | 646 | ); |
| 647 | 647 | ||
| ... | @@ -672,7 +672,7 @@ describe("ApnsDeliveries", () => { | ... | @@ -672,7 +672,7 @@ describe("ApnsDeliveries", () => { |
| 672 | }, | 672 | }, |
| 673 | ]); | 673 | ]); |
| 674 | expect(attempts).toEqual([]); | 674 | expect(attempts).toEqual([]); |
| 675 | }).pipe(Effect.provide(makeLayer({ attempts, queuedJobs }))); | 675 | }).pipe(Effect.provide(layerFor({ attempts, queuedJobs }))); |
| 676 | }); | 676 | }); |
| 677 | 677 | ||
| 678 | it.effect( | 678 | it.effect( |
| ... | @@ -732,7 +732,7 @@ describe("ApnsDeliveries", () => { | ... | @@ -732,7 +732,7 @@ describe("ApnsDeliveries", () => { |
| 732 | }, | 732 | }, |
| 733 | ]); | 733 | ]); |
| 734 | expect(attempts).toEqual([]); | 734 | expect(attempts).toEqual([]); |
| 735 | }).pipe(Effect.provide(makeLayer({ attempts, queuedJobs }))); | 735 | }).pipe(Effect.provide(layerFor({ attempts, queuedJobs }))); |
| 736 | }, | 736 | }, |
| 737 | ); | 737 | ); |
| 738 | 738 | ||
| ... | @@ -774,7 +774,7 @@ describe("ApnsDeliveries", () => { | ... | @@ -774,7 +774,7 @@ describe("ApnsDeliveries", () => { |
| 774 | }, | 774 | }, |
| 775 | ]); | 775 | ]); |
| 776 | expect(attempts).toEqual([]); | 776 | expect(attempts).toEqual([]); |
| 777 | }).pipe(Effect.provide(makeLayer({ attempts, queuedJobs }))); | 777 | }).pipe(Effect.provide(layerFor({ attempts, queuedJobs }))); |
| 778 | }); | 778 | }); |
| 779 | 779 | ||
| 780 | it.effect( | 780 | it.effect( |
| ... | @@ -827,7 +827,7 @@ describe("ApnsDeliveries", () => { | ... | @@ -827,7 +827,7 @@ describe("ApnsDeliveries", () => { |
| 827 | }, | 827 | }, |
| 828 | ]); | 828 | ]); |
| 829 | expect(attempts).toEqual([]); | 829 | expect(attempts).toEqual([]); |
| 830 | }).pipe(Effect.provide(makeLayer({ attempts, queuedJobs }))); | 830 | }).pipe(Effect.provide(layerFor({ attempts, queuedJobs }))); |
| 831 | }, | 831 | }, |
| 832 | ); | 832 | ); |
| 833 | 833 | ||
| ... | @@ -852,7 +852,7 @@ describe("ApnsDeliveries", () => { | ... | @@ -852,7 +852,7 @@ describe("ApnsDeliveries", () => { |
| 852 | expect(result).toBeNull(); | 852 | expect(result).toBeNull(); |
| 853 | expect(queuedJobs).toEqual([]); | 853 | expect(queuedJobs).toEqual([]); |
| 854 | expect(attempts).toEqual([]); | 854 | expect(attempts).toEqual([]); |
| 855 | }).pipe(Effect.provide(makeLayer({ attempts, queuedJobs }))); | 855 | }).pipe(Effect.provide(layerFor({ attempts, queuedJobs }))); |
| 856 | }); | 856 | }); |
| 857 | 857 | ||
| 858 | it.effect("queues bounded alert notification payloads", () => { | 858 | it.effect("queues bounded alert notification payloads", () => { |
| ... | @@ -892,7 +892,7 @@ describe("ApnsDeliveries", () => { | ... | @@ -892,7 +892,7 @@ describe("ApnsDeliveries", () => { |
| 892 | expect(notification?.body.length).toBeLessThanOrEqual(120); | 892 | expect(notification?.body.length).toBeLessThanOrEqual(120); |
| 893 | expect(notification?.deepLink).toBe("/"); | 893 | expect(notification?.deepLink).toBe("/"); |
| 894 | expect(attempts).toEqual([]); | 894 | expect(attempts).toEqual([]); |
| 895 | }).pipe(Effect.provide(makeLayer({ attempts, queuedJobs }))); | 895 | }).pipe(Effect.provide(layerFor({ attempts, queuedJobs }))); |
| 896 | }); | 896 | }); |
| 897 | 897 | ||
| 898 | it.effect("preserves the schema cause for invalid queue payloads", () => { | 898 | it.effect("preserves the schema cause for invalid queue payloads", () => { |
| ... | @@ -908,7 +908,7 @@ describe("ApnsDeliveries", () => { | ... | @@ -908,7 +908,7 @@ describe("ApnsDeliveries", () => { |
| 908 | message: "Invalid APNs delivery queue job with object payload.", | 908 | message: "Invalid APNs delivery queue job with object payload.", |
| 909 | }); | 909 | }); |
| 910 | expect(error.cause).toMatchObject({ _tag: "SchemaError" }); | 910 | expect(error.cause).toMatchObject({ _tag: "SchemaError" }); |
| 911 | }).pipe(Effect.provide(makeLayer({ attempts }))); | 911 | }).pipe(Effect.provide(layerFor({ attempts }))); |
| 912 | }); | 912 | }); |
| 913 | 913 | ||
| 914 | it.effect("skips a queued start when the user no longer has live work", () => { | 914 | it.effect("skips a queued start when the user no longer has live work", () => { |
| ... | @@ -950,7 +950,7 @@ describe("ApnsDeliveries", () => { | ... | @@ -950,7 +950,7 @@ describe("ApnsDeliveries", () => { |
| 950 | }, | 950 | }, |
| 951 | ]); | 951 | ]); |
| 952 | expect(clearedStarts).toMatchObject([{ userId: target.user_id, deviceId: target.device_id }]); | 952 | expect(clearedStarts).toMatchObject([{ userId: target.user_id, deviceId: target.device_id }]); |
| 953 | }).pipe(Effect.provide(makeLayer({ attempts, clearedStarts, activityStates: [] }))); | 953 | }).pipe(Effect.provide(layerFor({ attempts, clearedStarts, activityStates: [] }))); |
| 954 | }); | 954 | }); |
| 955 | 955 | ||
| 956 | it.effect("processes signed jobs through APNs and records attempts", () => { | 956 | it.effect("processes signed jobs through APNs and records attempts", () => { |
| ... | @@ -1012,7 +1012,7 @@ describe("ApnsDeliveries", () => { | ... | @@ -1012,7 +1012,7 @@ describe("ApnsDeliveries", () => { |
| 1012 | }).pipe( | 1012 | }).pipe( |
| 1013 | Effect.provide( | 1013 | Effect.provide( |
| 1014 | Layer.mergeAll( | 1014 | Layer.mergeAll( |
| 1015 | makeLayer({ attempts }), | 1015 | layerFor({ attempts }), |
| 1016 | Logger.layer([logger], { mergeWithExisting: false }), | 1016 | Logger.layer([logger], { mergeWithExisting: false }), |
| 1017 | ), | 1017 | ), |
| 1018 | ), | 1018 | ), |
| ... | @@ -1047,7 +1047,7 @@ describe("ApnsDeliveries", () => { | ... | @@ -1047,7 +1047,7 @@ describe("ApnsDeliveries", () => { |
| 1047 | ); | 1047 | ); |
| 1048 | const started = yield* Deferred.make<void>(); | 1048 | const started = yield* Deferred.make<void>(); |
| 1049 | const results: Array<{ deviceId: string; ok: boolean }> = []; | 1049 | const results: Array<{ deviceId: string; ok: boolean }> = []; |
| 1050 | const layer = makeLayer({ | 1050 | const layer = layerFor({ |
| 1051 | attempts, | 1051 | attempts, |
| 1052 | markedDeliveries, | 1052 | markedDeliveries, |
| 1053 | currentTargets: [target, secondTarget], | 1053 | currentTargets: [target, secondTarget], |
| ... | @@ -1138,7 +1138,7 @@ describe("ApnsDeliveries", () => { | ... | @@ -1138,7 +1138,7 @@ describe("ApnsDeliveries", () => { |
| 1138 | ]); | 1138 | ]); |
| 1139 | }).pipe( | 1139 | }).pipe( |
| 1140 | Effect.provide( | 1140 | Effect.provide( |
| 1141 | makeLayer({ | 1141 | layerFor({ |
| 1142 | attempts, | 1142 | attempts, |
| 1143 | currentTargets: [ | 1143 | currentTargets: [ |
| 1144 | { | 1144 | { |
| ... | @@ -1197,7 +1197,7 @@ describe("ApnsDeliveries", () => { | ... | @@ -1197,7 +1197,7 @@ describe("ApnsDeliveries", () => { |
| 1197 | expect(attempts).toEqual([]); | 1197 | expect(attempts).toEqual([]); |
| 1198 | }).pipe( | 1198 | }).pipe( |
| 1199 | Effect.provide( | 1199 | Effect.provide( |
| 1200 | makeLayer({ | 1200 | layerFor({ |
| 1201 | attempts, | 1201 | attempts, |
| 1202 | sourceJobClaims: new Map([["job-push-duplicate", "completed"]]), | 1202 | sourceJobClaims: new Map([["job-push-duplicate", "completed"]]), |
| 1203 | config: signingConfig, | 1203 | config: signingConfig, |
| ... | @@ -1251,7 +1251,7 @@ describe("ApnsDeliveries", () => { | ... | @@ -1251,7 +1251,7 @@ describe("ApnsDeliveries", () => { |
| 1251 | ]); | 1251 | ]); |
| 1252 | }).pipe( | 1252 | }).pipe( |
| 1253 | Effect.provide( | 1253 | Effect.provide( |
| 1254 | makeLayer({ | 1254 | layerFor({ |
| 1255 | attempts, | 1255 | attempts, |
| 1256 | config: signingConfig, | 1256 | config: signingConfig, |
| 1257 | execute, | 1257 | execute, |
| ... | @@ -1311,7 +1311,7 @@ describe("ApnsDeliveries", () => { | ... | @@ -1311,7 +1311,7 @@ describe("ApnsDeliveries", () => { |
| 1311 | ]); | 1311 | ]); |
| 1312 | }).pipe( | 1312 | }).pipe( |
| 1313 | Effect.provide( | 1313 | Effect.provide( |
| 1314 | makeLayer({ | 1314 | layerFor({ |
| 1315 | attempts, | 1315 | attempts, |
| 1316 | currentTargets: [ | 1316 | currentTargets: [ |
| 1317 | { | 1317 | { |
| ... | @@ -1383,7 +1383,7 @@ describe("ApnsDeliveries", () => { | ... | @@ -1383,7 +1383,7 @@ describe("ApnsDeliveries", () => { |
| 1383 | ]); | 1383 | ]); |
| 1384 | }).pipe( | 1384 | }).pipe( |
| 1385 | Effect.provide( | 1385 | Effect.provide( |
| 1386 | makeLayer({ | 1386 | layerFor({ |
| 1387 | attempts, | 1387 | attempts, |
| 1388 | activityStates: [ | 1388 | activityStates: [ |
| 1389 | { | 1389 | { |
| ... | @@ -1452,7 +1452,7 @@ describe("ApnsDeliveries", () => { | ... | @@ -1452,7 +1452,7 @@ describe("ApnsDeliveries", () => { |
| 1452 | ]); | 1452 | ]); |
| 1453 | }).pipe( | 1453 | }).pipe( |
| 1454 | Effect.provide( | 1454 | Effect.provide( |
| 1455 | makeLayer({ | 1455 | layerFor({ |
| 1456 | attempts, | 1456 | attempts, |
| 1457 | currentTargets: [{ ...target, push_token: "apns-device-token" }], | 1457 | currentTargets: [{ ...target, push_token: "apns-device-token" }], |
| 1458 | currentActivityStates: [ | 1458 | currentActivityStates: [ |
| ... | @@ -1512,7 +1512,7 @@ describe("ApnsDeliveries", () => { | ... | @@ -1512,7 +1512,7 @@ describe("ApnsDeliveries", () => { |
| 1512 | expect(attempts).toEqual([]); | 1512 | expect(attempts).toEqual([]); |
| 1513 | }).pipe( | 1513 | }).pipe( |
| 1514 | Effect.provide( | 1514 | Effect.provide( |
| 1515 | makeLayer({ | 1515 | layerFor({ |
| 1516 | attempts, | 1516 | attempts, |
| 1517 | sourceJobClaims: new Map([["job-push-in-flight", "in_flight"]]), | 1517 | sourceJobClaims: new Map([["job-push-in-flight", "in_flight"]]), |
| 1518 | config: signingConfig, | 1518 | config: signingConfig, |
| ... | @@ -1573,7 +1573,7 @@ describe("ApnsDeliveries", () => { | ... | @@ -1573,7 +1573,7 @@ describe("ApnsDeliveries", () => { |
| 1573 | ]); | 1573 | ]); |
| 1574 | }).pipe( | 1574 | }).pipe( |
| 1575 | Effect.provide( | 1575 | Effect.provide( |
| 1576 | makeLayer({ | 1576 | layerFor({ |
| 1577 | attempts, | 1577 | attempts, |
| 1578 | invalidatedTokens, | 1578 | invalidatedTokens, |
| 1579 | currentTargets: [ | 1579 | currentTargets: [ |
| ... | @@ -1623,7 +1623,7 @@ describe("ApnsDeliveries", () => { | ... | @@ -1623,7 +1623,7 @@ describe("ApnsDeliveries", () => { |
| 1623 | ]); | 1623 | ]); |
| 1624 | }).pipe( | 1624 | }).pipe( |
| 1625 | Effect.provide( | 1625 | Effect.provide( |
| 1626 | makeLayer({ | 1626 | layerFor({ |
| 1627 | attempts, | 1627 | attempts, |
| 1628 | clearedStarts, | 1628 | clearedStarts, |
| 1629 | activityStates: [{ ...state, updatedAt: "9999-01-01T00:00:00.000Z" }], | 1629 | activityStates: [{ ...state, updatedAt: "9999-01-01T00:00:00.000Z" }], |
| ... | @@ -1676,7 +1676,7 @@ describe("ApnsDeliveries", () => { | ... | @@ -1676,7 +1676,7 @@ describe("ApnsDeliveries", () => { |
| 1676 | ]); | 1676 | ]); |
| 1677 | }).pipe( | 1677 | }).pipe( |
| 1678 | Effect.provide( | 1678 | Effect.provide( |
| 1679 | makeLayer({ | 1679 | layerFor({ |
| 1680 | attempts, | 1680 | attempts, |
| 1681 | invalidatedTokens, | 1681 | invalidatedTokens, |
| 1682 | activityStates: [{ ...state, updatedAt: "9999-01-01T00:00:00.000Z" }], | 1682 | activityStates: [{ ...state, updatedAt: "9999-01-01T00:00:00.000Z" }], |
| ... | @@ -1730,7 +1730,7 @@ describe("ApnsDeliveries", () => { | ... | @@ -1730,7 +1730,7 @@ describe("ApnsDeliveries", () => { |
| 1730 | }, | 1730 | }, |
| 1731 | ]); | 1731 | ]); |
| 1732 | }).pipe( | 1732 | }).pipe( |
| 1733 | Effect.provide(makeLayer({ attempts, invalidatedTokens, config: signingConfig, execute })), | 1733 | Effect.provide(layerFor({ attempts, invalidatedTokens, config: signingConfig, execute })), |
| 1734 | ); | 1734 | ); |
| 1735 | }); | 1735 | }); |
| 1736 | }); | 1736 | }); |
| ... | @@ -1938,7 +1938,7 @@ describe("queued iOS alert policy", () => { | ... | @@ -1938,7 +1938,7 @@ describe("queued iOS alert policy", () => { |
| 1938 | expect(sent).toBe(scenario === "enabled" ? 1 : 0); | 1938 | expect(sent).toBe(scenario === "enabled" ? 1 : 0); |
| 1939 | }).pipe( | 1939 | }).pipe( |
| 1940 | Effect.provide( | 1940 | Effect.provide( |
| 1941 | makeLayer({ | 1941 | layerFor({ |
| 1942 | attempts: [], | 1942 | attempts: [], |
| 1943 | config: signingConfig, | 1943 | config: signingConfig, |
| 1944 | currentTargets: [ | 1944 | currentTargets: [ |
| ... | @@ -1984,7 +1984,7 @@ describe("fast completion delivery", () => { | ... | @@ -1984,7 +1984,7 @@ describe("fast completion delivery", () => { |
| 1984 | expect( | 1984 | expect( |
| 1985 | queuedJobs.some((x) => x.payload.alert !== null && x.payload.alert !== undefined), | 1985 | queuedJobs.some((x) => x.payload.alert !== null && x.payload.alert !== undefined), |
| 1986 | ).toBe(true); | 1986 | ).toBe(true); |
| 1987 | }).pipe(Effect.provide(makeLayer({ attempts: [], queuedJobs, currentTargets: [device] }))); | 1987 | }).pipe(Effect.provide(layerFor({ attempts: [], queuedJobs, currentTargets: [device] }))); |
| 1988 | }); | 1988 | }); |
| 1989 | it.effect("replays a newly visible completion without alerting", () => { | 1989 | it.effect("replays a newly visible completion without alerting", () => { |
| 1990 | const queuedJobs: SignedApnsDeliveryJob[] = []; | 1990 | const queuedJobs: SignedApnsDeliveryJob[] = []; |
| ... | @@ -2010,7 +2010,7 @@ describe("fast completion delivery", () => { | ... | @@ -2010,7 +2010,7 @@ describe("fast completion delivery", () => { |
| 2010 | }); | 2010 | }); |
| 2011 | expect(queuedJobs).toHaveLength(1); | 2011 | expect(queuedJobs).toHaveLength(1); |
| 2012 | expect(queuedJobs[0]?.payload.alert).toBeUndefined(); | 2012 | expect(queuedJobs[0]?.payload.alert).toBeUndefined(); |
| 2013 | }).pipe(Effect.provide(makeLayer({ attempts: [], queuedJobs }))); | 2013 | }).pipe(Effect.provide(layerFor({ attempts: [], queuedJobs }))); |
| 2014 | }); | 2014 | }); |
| 2015 | }); | 2015 | }); |
| 2016 | 2016 | ||
| ... | @@ -2063,7 +2063,7 @@ describe("signed APNs registration metadata", () => { | ... | @@ -2063,7 +2063,7 @@ describe("signed APNs registration metadata", () => { |
| 2063 | ); | 2063 | ); |
| 2064 | }).pipe( | 2064 | }).pipe( |
| 2065 | Effect.provide( | 2065 | Effect.provide( |
| 2066 | makeLayer({ | 2066 | layerFor({ |
| 2067 | attempts, | 2067 | attempts, |
| 2068 | config: signingConfig, | 2068 | config: signingConfig, |
| 2069 | currentTargets: [ | 2069 | currentTargets: [ |
infra/relay/src/agentActivity/FcmClient.test.ts+4-4| ... | @@ -55,7 +55,7 @@ const input = { | ... | @@ -55,7 +55,7 @@ const input = { |
| 55 | alert: false, | 55 | alert: false, |
| 56 | }; | 56 | }; |
| 57 | 57 | ||
| 58 | function testLayer(requests: HttpClientRequest.HttpClientRequest[], responses: Response[]) { | 58 | function layerTest(requests: HttpClientRequest.HttpClientRequest[], responses: Response[]) { |
| 59 | const http = HttpClient.make((request) => { | 59 | const http = HttpClient.make((request) => { |
| 60 | requests.push(request); | 60 | requests.push(request); |
| 61 | const response = responses.shift(); | 61 | const response = responses.shift(); |
| ... | @@ -198,7 +198,7 @@ describe("FCM delivery", () => { | ... | @@ -198,7 +198,7 @@ describe("FCM delivery", () => { |
| 198 | }); | 198 | }); |
| 199 | }).pipe( | 199 | }).pipe( |
| 200 | Effect.provide( | 200 | Effect.provide( |
| 201 | testLayer(requests, [ | 201 | layerTest(requests, [ |
| 202 | Response.json({ access_token: "access-token" }), | 202 | Response.json({ access_token: "access-token" }), |
| 203 | Response.json({ name: "one" }), | 203 | Response.json({ name: "one" }), |
| 204 | Response.json({ name: "two" }), | 204 | Response.json({ name: "two" }), |
| ... | @@ -219,7 +219,7 @@ describe("FCM delivery", () => { | ... | @@ -219,7 +219,7 @@ describe("FCM delivery", () => { |
| 219 | expect(requests[3]!.headers.authorization).toBe("Bearer fresh-token"); | 219 | expect(requests[3]!.headers.authorization).toBe("Bearer fresh-token"); |
| 220 | }).pipe( | 220 | }).pipe( |
| 221 | Effect.provide( | 221 | Effect.provide( |
| 222 | testLayer(requests, [ | 222 | layerTest(requests, [ |
| 223 | Response.json({ access_token: "old-token" }), | 223 | Response.json({ access_token: "old-token" }), |
| 224 | Response.json({}, { status: 401 }), | 224 | Response.json({}, { status: 401 }), |
| 225 | Response.json({ access_token: "fresh-token" }), | 225 | Response.json({ access_token: "fresh-token" }), |
| ... | @@ -249,6 +249,6 @@ describe("FCM delivery", () => { | ... | @@ -249,6 +249,6 @@ describe("FCM delivery", () => { |
| 249 | .pipe(Effect.flip); | 249 | .pipe(Effect.flip); |
| 250 | expect(error.operation).toBe("send"); | 250 | expect(error.operation).toBe("send"); |
| 251 | expect(requests).toHaveLength(0); | 251 | expect(requests).toHaveLength(0); |
| 252 | }).pipe(Effect.provide(testLayer(requests, []))); | 252 | }).pipe(Effect.provide(layerTest(requests, []))); |
| 253 | }); | 253 | }); |
| 254 | }); | 254 | }); |
infra/relay/src/agentActivity/FcmDeliveries.test.ts+2-2| ... | @@ -98,7 +98,7 @@ function harness() { | ... | @@ -98,7 +98,7 @@ function harness() { |
| 98 | linked: true, | 98 | linked: true, |
| 99 | deliveryFailure: null as FcmClient.FcmClientError | null, | 99 | deliveryFailure: null as FcmClient.FcmClientError | null, |
| 100 | }; | 100 | }; |
| 101 | const services = Layer.mergeAll( | 101 | const layerServices = Layer.mergeAll( |
| 102 | NodeCryptoLayer.layer, | 102 | NodeCryptoLayer.layer, |
| 103 | Layer.succeed(RelayConfiguration.RelayConfiguration, config), | 103 | Layer.succeed(RelayConfiguration.RelayConfiguration, config), |
| 104 | Layer.succeed(FcmDeliveryQueueSender.FcmDeliveryQueueSender, { | 104 | Layer.succeed(FcmDeliveryQueueSender.FcmDeliveryQueueSender, { |
| ... | @@ -198,7 +198,7 @@ function harness() { | ... | @@ -198,7 +198,7 @@ function harness() { |
| 198 | queued, | 198 | queued, |
| 199 | marked, | 199 | marked, |
| 200 | current, | 200 | current, |
| 201 | layer: FcmDeliveries.layer.pipe(Layer.provide(services)), | 201 | layer: FcmDeliveries.layer.pipe(Layer.provide(layerServices)), |
| 202 | job: { | 202 | job: { |
| 203 | userId: "user", | 203 | userId: "user", |
| 204 | deviceId: "phone", | 204 | deviceId: "phone", |
infra/relay/src/agentActivity/MobileRegistrations.test.ts+4-4| ... | @@ -20,7 +20,7 @@ import * as RelayConfiguration from "../Config.ts"; | ... | @@ -20,7 +20,7 @@ import * as RelayConfiguration from "../Config.ts"; |
| 20 | import * as AgentActivityPublisher from "./AgentActivityPublisher.ts"; | 20 | import * as AgentActivityPublisher from "./AgentActivityPublisher.ts"; |
| 21 | import * as FcmDeliveries from "./FcmDeliveries.ts"; | 21 | import * as FcmDeliveries from "./FcmDeliveries.ts"; |
| 22 | 22 | ||
| 23 | const publisherLayer = AgentActivityPublisher.layer.pipe( | 23 | const layerPublisher = AgentActivityPublisher.layer.pipe( |
| 24 | Layer.provide( | 24 | Layer.provide( |
| 25 | Layer.succeed(FcmDeliveries.FcmDeliveries, { | 25 | Layer.succeed(FcmDeliveries.FcmDeliveries, { |
| 26 | enqueue: () => Effect.succeed(null), | 26 | enqueue: () => Effect.succeed(null), |
| ... | @@ -153,13 +153,13 @@ const config = RelayConfiguration.RelayConfiguration.of({ | ... | @@ -153,13 +153,13 @@ const config = RelayConfiguration.RelayConfiguration.of({ |
| 153 | managedEndpointNamespace: undefined, | 153 | managedEndpointNamespace: undefined, |
| 154 | }); | 154 | }); |
| 155 | 155 | ||
| 156 | function makeRegistrationReplayLayer(input: { | 156 | function layerRegistrationReplay(input: { |
| 157 | readonly devices: Devices.Devices["Service"]; | 157 | readonly devices: Devices.Devices["Service"]; |
| 158 | readonly liveActivities: LiveActivities.LiveActivities["Service"]; | 158 | readonly liveActivities: LiveActivities.LiveActivities["Service"]; |
| 159 | readonly queuedJobs: Array<SignedApnsDeliveryJob>; | 159 | readonly queuedJobs: Array<SignedApnsDeliveryJob>; |
| 160 | }) { | 160 | }) { |
| 161 | return MobileRegistrations.layer.pipe( | 161 | return MobileRegistrations.layer.pipe( |
| 162 | Layer.provide(publisherLayer), | 162 | Layer.provide(layerPublisher), |
| 163 | Layer.provide( | 163 | Layer.provide( |
| 164 | ApnsDeliveries.layer.pipe( | 164 | ApnsDeliveries.layer.pipe( |
| 165 | Layer.provide(ApnsClient.layer.pipe(Layer.provide(ApnsProviderTokens.layer))), | 165 | Layer.provide(ApnsClient.layer.pipe(Layer.provide(ApnsProviderTokens.layer))), |
| ... | @@ -490,7 +490,7 @@ describe("MobileRegistrations", () => { | ... | @@ -490,7 +490,7 @@ describe("MobileRegistrations", () => { |
| 490 | expect(registeredDevices).toHaveLength(1); | 490 | expect(registeredDevices).toHaveLength(1); |
| 491 | expect(queuedStarts).toEqual([]); | 491 | expect(queuedStarts).toEqual([]); |
| 492 | expect(queuedJobs).toEqual([]); | 492 | expect(queuedJobs).toEqual([]); |
| 493 | }).pipe(Effect.provide(makeRegistrationReplayLayer({ devices, liveActivities, queuedJobs }))); | 493 | }).pipe(Effect.provide(layerRegistrationReplay({ devices, liveActivities, queuedJobs }))); |
| 494 | }, | 494 | }, |
| 495 | ); | 495 | ); |
| 496 | }); | 496 | }); |
infra/relay/src/environments/EnvironmentConnector.test.ts+16-16| ... | @@ -156,7 +156,7 @@ function signHealthResponse( | ... | @@ -156,7 +156,7 @@ function signHealthResponse( |
| 156 | }; | 156 | }; |
| 157 | } | 157 | } |
| 158 | 158 | ||
| 159 | function connectorTestLayer( | 159 | function layerConnectorTest( |
| 160 | execute: ( | 160 | execute: ( |
| 161 | request: HttpClientRequest.HttpClientRequest, | 161 | request: HttpClientRequest.HttpClientRequest, |
| 162 | ) => Effect.Effect<HttpClientResponse.HttpClientResponse>, | 162 | ) => Effect.Effect<HttpClientResponse.HttpClientResponse>, |
| ... | @@ -267,7 +267,7 @@ describe("EnvironmentConnector", () => { | ... | @@ -267,7 +267,7 @@ describe("EnvironmentConnector", () => { |
| 267 | }); | 267 | }); |
| 268 | }).pipe( | 268 | }).pipe( |
| 269 | Effect.provide( | 269 | Effect.provide( |
| 270 | connectorTestLayer(execute, { | 270 | layerConnectorTest(execute, { |
| 271 | links: { | 271 | links: { |
| 272 | ...links, | 272 | ...links, |
| 273 | getForUser: (input) => waitForPeer.pipe(Effect.andThen(links.getForUser(input))), | 273 | getForUser: (input) => waitForPeer.pipe(Effect.andThen(links.getForUser(input))), |
| ... | @@ -326,7 +326,7 @@ describe("EnvironmentConnector", () => { | ... | @@ -326,7 +326,7 @@ describe("EnvironmentConnector", () => { |
| 326 | label: "Connector Test Environment", | 326 | label: "Connector Test Environment", |
| 327 | }, | 327 | }, |
| 328 | }); | 328 | }); |
| 329 | }).pipe(Effect.provide(connectorTestLayer(execute))); | 329 | }).pipe(Effect.provide(layerConnectorTest(execute))); |
| 330 | }); | 330 | }); |
| 331 | 331 | ||
| 332 | it.effect("rejects manual endpoints before sending a health request", () => { | 332 | it.effect("rejects manual endpoints before sending a health request", () => { |
| ... | @@ -359,7 +359,7 @@ describe("EnvironmentConnector", () => { | ... | @@ -359,7 +359,7 @@ describe("EnvironmentConnector", () => { |
| 359 | expect(requestCount).toBe(0); | 359 | expect(requestCount).toBe(0); |
| 360 | }).pipe( | 360 | }).pipe( |
| 361 | Effect.provide( | 361 | Effect.provide( |
| 362 | connectorTestLayer(execute, { | 362 | layerConnectorTest(execute, { |
| 363 | links: makeLinks({ | 363 | links: makeLinks({ |
| 364 | endpoint: { | 364 | endpoint: { |
| 365 | httpBaseUrl: "https://127.0.0.1/", | 365 | httpBaseUrl: "https://127.0.0.1/", |
| ... | @@ -424,7 +424,7 @@ describe("EnvironmentConnector", () => { | ... | @@ -424,7 +424,7 @@ describe("EnvironmentConnector", () => { |
| 424 | expect(requestCount).toBe(0); | 424 | expect(requestCount).toBe(0); |
| 425 | }).pipe( | 425 | }).pipe( |
| 426 | Effect.provide( | 426 | Effect.provide( |
| 427 | connectorTestLayer(execute, { | 427 | layerConnectorTest(execute, { |
| 428 | links: makeLinks({ | 428 | links: makeLinks({ |
| 429 | endpoint: { | 429 | endpoint: { |
| 430 | httpBaseUrl: "https://attacker.example.test/", | 430 | httpBaseUrl: "https://attacker.example.test/", |
| ... | @@ -468,7 +468,7 @@ describe("EnvironmentConnector", () => { | ... | @@ -468,7 +468,7 @@ describe("EnvironmentConnector", () => { |
| 468 | expect(requestCount).toBe(0); | 468 | expect(requestCount).toBe(0); |
| 469 | }).pipe( | 469 | }).pipe( |
| 470 | Effect.provide( | 470 | Effect.provide( |
| 471 | connectorTestLayer(execute, { | 471 | layerConnectorTest(execute, { |
| 472 | allocations: makeAllocations({ | 472 | allocations: makeAllocations({ |
| 473 | userId: "user_123", | 473 | userId: "user_123", |
| 474 | environmentId: "env-connector-test", | 474 | environmentId: "env-connector-test", |
| ... | @@ -519,7 +519,7 @@ describe("EnvironmentConnector", () => { | ... | @@ -519,7 +519,7 @@ describe("EnvironmentConnector", () => { |
| 519 | if (result._tag === "Failure") { | 519 | if (result._tag === "Failure") { |
| 520 | expect(result.cause.toString()).toContain("EnvironmentMintResponseInvalid"); | 520 | expect(result.cause.toString()).toContain("EnvironmentMintResponseInvalid"); |
| 521 | } | 521 | } |
| 522 | }).pipe(Effect.provide(connectorTestLayer(execute))); | 522 | }).pipe(Effect.provide(layerConnectorTest(execute))); |
| 523 | }); | 523 | }); |
| 524 | 524 | ||
| 525 | it.effect("reports offline status when the managed endpoint health request fails", () => { | 525 | it.effect("reports offline status when the managed endpoint health request fails", () => { |
| ... | @@ -550,7 +550,7 @@ describe("EnvironmentConnector", () => { | ... | @@ -550,7 +550,7 @@ describe("EnvironmentConnector", () => { |
| 550 | error: "Managed endpoint health request failed: Environment is unavailable.", | 550 | error: "Managed endpoint health request failed: Environment is unavailable.", |
| 551 | traceId: expect.any(String), | 551 | traceId: expect.any(String), |
| 552 | }); | 552 | }); |
| 553 | }).pipe(Effect.provide(connectorTestLayer(execute))); | 553 | }).pipe(Effect.provide(layerConnectorTest(execute))); |
| 554 | }); | 554 | }); |
| 555 | 555 | ||
| 556 | it.effect("rejects health responses with a mismatched top-level environment id", () => { | 556 | it.effect("rejects health responses with a mismatched top-level environment id", () => { |
| ... | @@ -581,7 +581,7 @@ describe("EnvironmentConnector", () => { | ... | @@ -581,7 +581,7 @@ describe("EnvironmentConnector", () => { |
| 581 | if (result._tag === "Failure") { | 581 | if (result._tag === "Failure") { |
| 582 | expect(result.cause.toString()).toContain("EnvironmentMintResponseInvalid"); | 582 | expect(result.cause.toString()).toContain("EnvironmentMintResponseInvalid"); |
| 583 | } | 583 | } |
| 584 | }).pipe(Effect.provide(connectorTestLayer(execute))); | 584 | }).pipe(Effect.provide(layerConnectorTest(execute))); |
| 585 | }); | 585 | }); |
| 586 | 586 | ||
| 587 | it.effect("rejects health responses with an unsigned top-level descriptor mutation", () => { | 587 | it.effect("rejects health responses with an unsigned top-level descriptor mutation", () => { |
| ... | @@ -617,7 +617,7 @@ describe("EnvironmentConnector", () => { | ... | @@ -617,7 +617,7 @@ describe("EnvironmentConnector", () => { |
| 617 | if (result._tag === "Failure") { | 617 | if (result._tag === "Failure") { |
| 618 | expect(result.cause.toString()).toContain("EnvironmentMintResponseInvalid"); | 618 | expect(result.cause.toString()).toContain("EnvironmentMintResponseInvalid"); |
| 619 | } | 619 | } |
| 620 | }).pipe(Effect.provide(connectorTestLayer(execute))); | 620 | }).pipe(Effect.provide(layerConnectorTest(execute))); |
| 621 | }); | 621 | }); |
| 622 | 622 | ||
| 623 | it.effect("rejects health responses when the linked environment public key is malformed", () => { | 623 | it.effect("rejects health responses when the linked environment public key is malformed", () => { |
| ... | @@ -645,7 +645,7 @@ describe("EnvironmentConnector", () => { | ... | @@ -645,7 +645,7 @@ describe("EnvironmentConnector", () => { |
| 645 | } | 645 | } |
| 646 | }).pipe( | 646 | }).pipe( |
| 647 | Effect.provide( | 647 | Effect.provide( |
| 648 | connectorTestLayer(execute, { | 648 | layerConnectorTest(execute, { |
| 649 | links: makeLinks({ | 649 | links: makeLinks({ |
| 650 | environmentPublicKey: "not a pem public key", | 650 | environmentPublicKey: "not a pem public key", |
| 651 | }), | 651 | }), |
| ... | @@ -696,7 +696,7 @@ describe("EnvironmentConnector", () => { | ... | @@ -696,7 +696,7 @@ describe("EnvironmentConnector", () => { |
| 696 | wsBaseUrl: "wss://env.example.test/ws", | 696 | wsBaseUrl: "wss://env.example.test/ws", |
| 697 | }, | 697 | }, |
| 698 | }); | 698 | }); |
| 699 | }).pipe(Effect.provide(connectorTestLayer(execute))); | 699 | }).pipe(Effect.provide(layerConnectorTest(execute))); |
| 700 | }); | 700 | }); |
| 701 | 701 | ||
| 702 | it.effect("only accepts mint responses signed by the user's linked environment key", () => { | 702 | it.effect("only accepts mint responses signed by the user's linked environment key", () => { |
| ... | @@ -725,7 +725,7 @@ describe("EnvironmentConnector", () => { | ... | @@ -725,7 +725,7 @@ describe("EnvironmentConnector", () => { |
| 725 | if (result._tag === "Failure") { | 725 | if (result._tag === "Failure") { |
| 726 | expect(result.cause.toString()).toContain("EnvironmentMintResponseInvalid"); | 726 | expect(result.cause.toString()).toContain("EnvironmentMintResponseInvalid"); |
| 727 | } | 727 | } |
| 728 | }).pipe(Effect.provide(connectorTestLayer(execute))); | 728 | }).pipe(Effect.provide(layerConnectorTest(execute))); |
| 729 | }); | 729 | }); |
| 730 | 730 | ||
| 731 | it.effect("rejects mint responses when the linked environment public key is malformed", () => { | 731 | it.effect("rejects mint responses when the linked environment public key is malformed", () => { |
| ... | @@ -754,7 +754,7 @@ describe("EnvironmentConnector", () => { | ... | @@ -754,7 +754,7 @@ describe("EnvironmentConnector", () => { |
| 754 | } | 754 | } |
| 755 | }).pipe( | 755 | }).pipe( |
| 756 | Effect.provide( | 756 | Effect.provide( |
| 757 | connectorTestLayer(execute, { | 757 | layerConnectorTest(execute, { |
| 758 | links: makeLinks({ | 758 | links: makeLinks({ |
| 759 | environmentPublicKey: "not a pem public key", | 759 | environmentPublicKey: "not a pem public key", |
| 760 | }), | 760 | }), |
| ... | @@ -790,7 +790,7 @@ describe("EnvironmentConnector", () => { | ... | @@ -790,7 +790,7 @@ describe("EnvironmentConnector", () => { |
| 790 | if (result._tag === "Failure") { | 790 | if (result._tag === "Failure") { |
| 791 | expect(result.cause.toString()).toContain("EnvironmentMintResponseInvalid"); | 791 | expect(result.cause.toString()).toContain("EnvironmentMintResponseInvalid"); |
| 792 | } | 792 | } |
| 793 | }).pipe(Effect.provide(connectorTestLayer(execute))); | 793 | }).pipe(Effect.provide(layerConnectorTest(execute))); |
| 794 | }); | 794 | }); |
| 795 | 795 | ||
| 796 | it.effect("times out hung managed endpoint mint requests", () => { | 796 | it.effect("times out hung managed endpoint mint requests", () => { |
| ... | @@ -827,6 +827,6 @@ describe("EnvironmentConnector", () => { | ... | @@ -827,6 +827,6 @@ describe("EnvironmentConnector", () => { |
| 827 | timeoutMs: EnvironmentConnector.ENVIRONMENT_MINT_REQUEST_TIMEOUT_MS, | 827 | timeoutMs: EnvironmentConnector.ENVIRONMENT_MINT_REQUEST_TIMEOUT_MS, |
| 828 | }); | 828 | }); |
| 829 | } | 829 | } |
| 830 | }).pipe(Effect.provide(Layer.merge(TestClock.layer(), connectorTestLayer(execute)))); | 830 | }).pipe(Effect.provide(Layer.merge(TestClock.layer(), layerConnectorTest(execute)))); |
| 831 | }); | 831 | }); |
| 832 | }); | 832 | }); |
infra/relay/src/environments/EnvironmentLinker.test.ts+5-5| ... | @@ -106,7 +106,7 @@ const makeRequest = Effect.gen(function* () { | ... | @@ -106,7 +106,7 @@ const makeRequest = Effect.gen(function* () { |
| 106 | }; | 106 | }; |
| 107 | }); | 107 | }); |
| 108 | 108 | ||
| 109 | function testLayer(input?: { | 109 | function layerTest(input?: { |
| 110 | readonly upsert?: EnvironmentLinks.EnvironmentLinks["Service"]["upsert"]; | 110 | readonly upsert?: EnvironmentLinks.EnvironmentLinks["Service"]["upsert"]; |
| 111 | readonly consume?: DpopProofs.DpopProofReplay["Service"]["consume"]; | 111 | readonly consume?: DpopProofs.DpopProofReplay["Service"]["consume"]; |
| 112 | readonly deprovision?: ManagedEndpointProvider.ManagedEndpointProvider["Service"]["deprovision"]; | 112 | readonly deprovision?: ManagedEndpointProvider.ManagedEndpointProvider["Service"]["deprovision"]; |
| ... | @@ -167,7 +167,7 @@ describe("EnvironmentLinker", () => { | ... | @@ -167,7 +167,7 @@ describe("EnvironmentLinker", () => { |
| 167 | expect(persistedEnvironmentId).toBe(payload.environmentId); | 167 | expect(persistedEnvironmentId).toBe(payload.environmentId); |
| 168 | }).pipe( | 168 | }).pipe( |
| 169 | Effect.provide( | 169 | Effect.provide( |
| 170 | testLayer({ | 170 | layerTest({ |
| 171 | upsert: (input) => | 171 | upsert: (input) => |
| 172 | Effect.sync(() => { | 172 | Effect.sync(() => { |
| 173 | persistedEnvironmentId = input.proof.environmentId; | 173 | persistedEnvironmentId = input.proof.environmentId; |
| ... | @@ -236,7 +236,7 @@ describe("EnvironmentLinker", () => { | ... | @@ -236,7 +236,7 @@ describe("EnvironmentLinker", () => { |
| 236 | expect(deprovisionedEnvironmentId).toBe("env-link-test"); | 236 | expect(deprovisionedEnvironmentId).toBe("env-link-test"); |
| 237 | }).pipe( | 237 | }).pipe( |
| 238 | Effect.provide( | 238 | Effect.provide( |
| 239 | testLayer({ | 239 | layerTest({ |
| 240 | upsert: (input) => | 240 | upsert: (input) => |
| 241 | Effect.sync(() => { | 241 | Effect.sync(() => { |
| 242 | persistedEndpoint = input.endpoint.httpBaseUrl; | 242 | persistedEndpoint = input.endpoint.httpBaseUrl; |
| ... | @@ -277,7 +277,7 @@ describe("EnvironmentLinker", () => { | ... | @@ -277,7 +277,7 @@ describe("EnvironmentLinker", () => { |
| 277 | expect(persisted).toBe(false); | 277 | expect(persisted).toBe(false); |
| 278 | }).pipe( | 278 | }).pipe( |
| 279 | Effect.provide( | 279 | Effect.provide( |
| 280 | testLayer({ | 280 | layerTest({ |
| 281 | upsert: () => | 281 | upsert: () => |
| 282 | Effect.sync(() => { | 282 | Effect.sync(() => { |
| 283 | persisted = true; | 283 | persisted = true; |
| ... | @@ -304,6 +304,6 @@ describe("EnvironmentLinker", () => { | ... | @@ -304,6 +304,6 @@ describe("EnvironmentLinker", () => { |
| 304 | }); | 304 | }); |
| 305 | } | 305 | } |
| 306 | } | 306 | } |
| 307 | }).pipe(Effect.provide(testLayer({ consume: () => Effect.succeed(false) }))), | 307 | }).pipe(Effect.provide(layerTest({ consume: () => Effect.succeed(false) }))), |
| 308 | ); | 308 | ); |
| 309 | }); | 309 | }); |
infra/relay/src/environments/ManagedEndpointProvider.test.ts+41-41| ... | @@ -346,7 +346,7 @@ function makeTunnelLimits( | ... | @@ -346,7 +346,7 @@ function makeTunnelLimits( |
| 346 | }); | 346 | }); |
| 347 | } | 347 | } |
| 348 | 348 | ||
| 349 | function providerLayer( | 349 | function layerProvider( |
| 350 | tunnelClient = makeTunnelClient(), | 350 | tunnelClient = makeTunnelClient(), |
| 351 | dnsClient = makeDnsClient(), | 351 | dnsClient = makeDnsClient(), |
| 352 | allocations = makeAllocations(), | 352 | allocations = makeAllocations(), |
| ... | @@ -492,7 +492,7 @@ describe("ManagedEndpointProvider", () => { | ... | @@ -492,7 +492,7 @@ describe("ManagedEndpointProvider", () => { |
| 492 | ]); | 492 | ]); |
| 493 | }).pipe( | 493 | }).pipe( |
| 494 | Effect.provide( | 494 | Effect.provide( |
| 495 | providerLayer( | 495 | layerProvider( |
| 496 | makeTunnelClient(tunnelCalls), | 496 | makeTunnelClient(tunnelCalls), |
| 497 | makeDnsClient(dnsCalls), | 497 | makeDnsClient(dnsCalls), |
| 498 | makeAllocations(allocationCalls), | 498 | makeAllocations(allocationCalls), |
| ... | @@ -515,7 +515,7 @@ describe("ManagedEndpointProvider", () => { | ... | @@ -515,7 +515,7 @@ describe("ManagedEndpointProvider", () => { |
| 515 | expect(limitCalls).toEqual([{ userId: "user_ABC", environmentId: "env_ABC" }]); | 515 | expect(limitCalls).toEqual([{ userId: "user_ABC", environmentId: "env_ABC" }]); |
| 516 | }).pipe( | 516 | }).pipe( |
| 517 | Effect.provide( | 517 | Effect.provide( |
| 518 | providerLayer( | 518 | layerProvider( |
| 519 | makeTunnelClient(), | 519 | makeTunnelClient(), |
| 520 | makeDnsClient(), | 520 | makeDnsClient(), |
| 521 | makeAllocations(), | 521 | makeAllocations(), |
| ... | @@ -552,7 +552,7 @@ describe("ManagedEndpointProvider", () => { | ... | @@ -552,7 +552,7 @@ describe("ManagedEndpointProvider", () => { |
| 552 | expect(allocationCalls).toEqual([]); | 552 | expect(allocationCalls).toEqual([]); |
| 553 | }).pipe( | 553 | }).pipe( |
| 554 | Effect.provide( | 554 | Effect.provide( |
| 555 | providerLayer( | 555 | layerProvider( |
| 556 | makeTunnelClient(tunnelCalls), | 556 | makeTunnelClient(tunnelCalls), |
| 557 | makeDnsClient(dnsCalls), | 557 | makeDnsClient(dnsCalls), |
| 558 | makeAllocations(allocationCalls), | 558 | makeAllocations(allocationCalls), |
| ... | @@ -607,7 +607,7 @@ describe("ManagedEndpointProvider", () => { | ... | @@ -607,7 +607,7 @@ describe("ManagedEndpointProvider", () => { |
| 607 | name: requestedName, | 607 | name: requestedName, |
| 608 | configSrc: "cloudflare", | 608 | configSrc: "cloudflare", |
| 609 | }); | 609 | }); |
| 610 | }).pipe(Effect.provide(providerLayer(makeTunnelClient(tunnelCalls)))); | 610 | }).pipe(Effect.provide(layerProvider(makeTunnelClient(tunnelCalls)))); |
| 611 | }); | 611 | }); |
| 612 | 612 | ||
| 613 | it.effect("formats IPv6 loopback origins as valid Cloudflare ingress service URLs", () => { | 613 | it.effect("formats IPv6 loopback origins as valid Cloudflare ingress service URLs", () => { |
| ... | @@ -633,7 +633,7 @@ describe("ManagedEndpointProvider", () => { | ... | @@ -633,7 +633,7 @@ describe("ManagedEndpointProvider", () => { |
| 633 | ], | 633 | ], |
| 634 | }, | 634 | }, |
| 635 | }); | 635 | }); |
| 636 | }).pipe(Effect.provide(providerLayer(makeTunnelClient(tunnelCalls)))); | 636 | }).pipe(Effect.provide(layerProvider(makeTunnelClient(tunnelCalls)))); |
| 637 | }); | 637 | }); |
| 638 | 638 | ||
| 639 | it.effect("rejects non-loopback managed endpoint origins before calling Cloudflare", () => { | 639 | it.effect("rejects non-loopback managed endpoint origins before calling Cloudflare", () => { |
| ... | @@ -660,7 +660,7 @@ describe("ManagedEndpointProvider", () => { | ... | @@ -660,7 +660,7 @@ describe("ManagedEndpointProvider", () => { |
| 660 | port: 3773, | 660 | port: 3773, |
| 661 | }); | 661 | }); |
| 662 | } | 662 | } |
| 663 | }).pipe(Effect.provide(providerLayer(makeTunnelClient(), makeDnsClient(dnsCalls)))); | 663 | }).pipe(Effect.provide(layerProvider(makeTunnelClient(), makeDnsClient(dnsCalls)))); |
| 664 | }); | 664 | }); |
| 665 | 665 | ||
| 666 | it.effect("rejects invalid managed endpoint origin ports before calling Cloudflare", () => { | 666 | it.effect("rejects invalid managed endpoint origin ports before calling Cloudflare", () => { |
| ... | @@ -681,7 +681,7 @@ describe("ManagedEndpointProvider", () => { | ... | @@ -681,7 +681,7 @@ describe("ManagedEndpointProvider", () => { |
| 681 | if (result._tag === "Failure") { | 681 | if (result._tag === "Failure") { |
| 682 | expect(result.failure._tag).toBe("ManagedEndpointOriginNotAllowed"); | 682 | expect(result.failure._tag).toBe("ManagedEndpointOriginNotAllowed"); |
| 683 | } | 683 | } |
| 684 | }).pipe(Effect.provide(providerLayer(makeTunnelClient(), makeDnsClient(dnsCalls)))); | 684 | }).pipe(Effect.provide(layerProvider(makeTunnelClient(), makeDnsClient(dnsCalls)))); |
| 685 | }); | 685 | }); |
| 686 | 686 | ||
| 687 | it.effect("reconciles an existing same-host DNS record through the DNS client", () => { | 687 | it.effect("reconciles an existing same-host DNS record through the DNS client", () => { |
| ... | @@ -698,7 +698,7 @@ describe("ManagedEndpointProvider", () => { | ... | @@ -698,7 +698,7 @@ describe("ManagedEndpointProvider", () => { |
| 698 | expect(dnsCalls[1]?.input).toMatchObject({ dnsRecordId: "existing-record-id" }); | 698 | expect(dnsCalls[1]?.input).toMatchObject({ dnsRecordId: "existing-record-id" }); |
| 699 | }).pipe( | 699 | }).pipe( |
| 700 | Effect.provide( | 700 | Effect.provide( |
| 701 | providerLayer(makeTunnelClient(), makeDnsClient(dnsCalls, [{ id: "existing-record-id" }])), | 701 | layerProvider(makeTunnelClient(), makeDnsClient(dnsCalls, [{ id: "existing-record-id" }])), |
| 702 | ), | 702 | ), |
| 703 | ); | 703 | ); |
| 704 | }); | 704 | }); |
| ... | @@ -707,7 +707,7 @@ describe("ManagedEndpointProvider", () => { | ... | @@ -707,7 +707,7 @@ describe("ManagedEndpointProvider", () => { |
| 707 | const tunnelCalls: TunnelCall[] = []; | 707 | const tunnelCalls: TunnelCall[] = []; |
| 708 | const dnsCalls: DnsCall[] = []; | 708 | const dnsCalls: DnsCall[] = []; |
| 709 | const allocationCalls: AllocationCall[] = []; | 709 | const allocationCalls: AllocationCall[] = []; |
| 710 | const layer = providerLayer( | 710 | const layer = layerProvider( |
| 711 | makePersistentTunnelClient(tunnelCalls), | 711 | makePersistentTunnelClient(tunnelCalls), |
| 712 | makeDnsClient(dnsCalls), | 712 | makeDnsClient(dnsCalls), |
| 713 | makeAllocations(allocationCalls), | 713 | makeAllocations(allocationCalls), |
| ... | @@ -754,7 +754,7 @@ describe("ManagedEndpointProvider", () => { | ... | @@ -754,7 +754,7 @@ describe("ManagedEndpointProvider", () => { |
| 754 | const dnsCalls: DnsCall[] = []; | 754 | const dnsCalls: DnsCall[] = []; |
| 755 | const allocationCalls: AllocationCall[] = []; | 755 | const allocationCalls: AllocationCall[] = []; |
| 756 | const dnsClient = makeDnsClient(dnsCalls); | 756 | const dnsClient = makeDnsClient(dnsCalls); |
| 757 | const layer = providerLayer( | 757 | const layer = layerProvider( |
| 758 | makePersistentTunnelClient(), | 758 | makePersistentTunnelClient(), |
| 759 | dnsClient, | 759 | dnsClient, |
| 760 | makeAllocations(allocationCalls), | 760 | makeAllocations(allocationCalls), |
| ... | @@ -810,7 +810,7 @@ describe("ManagedEndpointProvider", () => { | ... | @@ -810,7 +810,7 @@ describe("ManagedEndpointProvider", () => { |
| 810 | deleteRecord: () => Effect.void, | 810 | deleteRecord: () => Effect.void, |
| 811 | }); | 811 | }); |
| 812 | const allocations = makeAllocations(); | 812 | const allocations = makeAllocations(); |
| 813 | const layer = providerLayer(makePersistentTunnelClient(), dnsClient, allocations); | 813 | const layer = layerProvider(makePersistentTunnelClient(), dnsClient, allocations); |
| 814 | 814 | ||
| 815 | return Effect.gen(function* () { | 815 | return Effect.gen(function* () { |
| 816 | const provider = yield* ManagedEndpointProvider.ManagedEndpointProvider; | 816 | const provider = yield* ManagedEndpointProvider.ManagedEndpointProvider; |
| ... | @@ -846,7 +846,7 @@ describe("ManagedEndpointProvider", () => { | ... | @@ -846,7 +846,7 @@ describe("ManagedEndpointProvider", () => { |
| 846 | const tunnelCalls: TunnelCall[] = []; | 846 | const tunnelCalls: TunnelCall[] = []; |
| 847 | const dnsCalls: DnsCall[] = []; | 847 | const dnsCalls: DnsCall[] = []; |
| 848 | const allocationCalls: AllocationCall[] = []; | 848 | const allocationCalls: AllocationCall[] = []; |
| 849 | const layer = providerLayer( | 849 | const layer = layerProvider( |
| 850 | makePersistentTunnelClient(tunnelCalls), | 850 | makePersistentTunnelClient(tunnelCalls), |
| 851 | makeDnsClient(dnsCalls), | 851 | makeDnsClient(dnsCalls), |
| 852 | makeAllocations(allocationCalls), | 852 | makeAllocations(allocationCalls), |
| ... | @@ -890,7 +890,7 @@ describe("ManagedEndpointProvider", () => { | ... | @@ -890,7 +890,7 @@ describe("ManagedEndpointProvider", () => { |
| 890 | const tunnelCalls: TunnelCall[] = []; | 890 | const tunnelCalls: TunnelCall[] = []; |
| 891 | const dnsCalls: DnsCall[] = []; | 891 | const dnsCalls: DnsCall[] = []; |
| 892 | const allocationCalls: AllocationCall[] = []; | 892 | const allocationCalls: AllocationCall[] = []; |
| 893 | const layer = providerLayer( | 893 | const layer = layerProvider( |
| 894 | makePersistentTunnelClient(tunnelCalls), | 894 | makePersistentTunnelClient(tunnelCalls), |
| 895 | makeDnsClient(dnsCalls), | 895 | makeDnsClient(dnsCalls), |
| 896 | makeAllocations(allocationCalls), | 896 | makeAllocations(allocationCalls), |
| ... | @@ -931,7 +931,7 @@ describe("ManagedEndpointProvider", () => { | ... | @@ -931,7 +931,7 @@ describe("ManagedEndpointProvider", () => { |
| 931 | const tunnelCalls: TunnelCall[] = []; | 931 | const tunnelCalls: TunnelCall[] = []; |
| 932 | const dnsCalls: DnsCall[] = []; | 932 | const dnsCalls: DnsCall[] = []; |
| 933 | const allocationCalls: AllocationCall[] = []; | 933 | const allocationCalls: AllocationCall[] = []; |
| 934 | const layer = providerLayer( | 934 | const layer = layerProvider( |
| 935 | makePersistentTunnelClient(tunnelCalls), | 935 | makePersistentTunnelClient(tunnelCalls), |
| 936 | makeDnsClient(dnsCalls), | 936 | makeDnsClient(dnsCalls), |
| 937 | makeAllocations(allocationCalls), | 937 | makeAllocations(allocationCalls), |
| ... | @@ -974,7 +974,7 @@ describe("ManagedEndpointProvider", () => { | ... | @@ -974,7 +974,7 @@ describe("ManagedEndpointProvider", () => { |
| 974 | it.effect("treats an environment without a recorded tunnel as already released", () => { | 974 | it.effect("treats an environment without a recorded tunnel as already released", () => { |
| 975 | const tunnelCalls: TunnelCall[] = []; | 975 | const tunnelCalls: TunnelCall[] = []; |
| 976 | const dnsCalls: DnsCall[] = []; | 976 | const dnsCalls: DnsCall[] = []; |
| 977 | const layer = providerLayer( | 977 | const layer = layerProvider( |
| 978 | makePersistentTunnelClient(tunnelCalls), | 978 | makePersistentTunnelClient(tunnelCalls), |
| 979 | makeDnsClient(dnsCalls), | 979 | makeDnsClient(dnsCalls), |
| 980 | makeAllocations(), | 980 | makeAllocations(), |
| ... | @@ -999,7 +999,7 @@ describe("ManagedEndpointProvider", () => { | ... | @@ -999,7 +999,7 @@ describe("ManagedEndpointProvider", () => { |
| 999 | ...allocations, | 999 | ...allocations, |
| 1000 | claimRelease: () => Effect.succeed(null), | 1000 | claimRelease: () => Effect.succeed(null), |
| 1001 | }); | 1001 | }); |
| 1002 | const layer = providerLayer(makePersistentTunnelClient(tunnelCalls), makeDnsClient(), outdated); | 1002 | const layer = layerProvider(makePersistentTunnelClient(tunnelCalls), makeDnsClient(), outdated); |
| 1003 | 1003 | ||
| 1004 | return Effect.gen(function* () { | 1004 | return Effect.gen(function* () { |
| 1005 | const provider = yield* ManagedEndpointProvider.ManagedEndpointProvider; | 1005 | const provider = yield* ManagedEndpointProvider.ManagedEndpointProvider; |
| ... | @@ -1024,7 +1024,7 @@ describe("ManagedEndpointProvider", () => { | ... | @@ -1024,7 +1024,7 @@ describe("ManagedEndpointProvider", () => { |
| 1024 | 1024 | ||
| 1025 | it.effect("does not release a tunnel when the requested tunnel id is outdated", () => { | 1025 | it.effect("does not release a tunnel when the requested tunnel id is outdated", () => { |
| 1026 | const tunnelCalls: TunnelCall[] = []; | 1026 | const tunnelCalls: TunnelCall[] = []; |
| 1027 | const layer = providerLayer( | 1027 | const layer = layerProvider( |
| 1028 | makePersistentTunnelClient(tunnelCalls), | 1028 | makePersistentTunnelClient(tunnelCalls), |
| 1029 | makeDnsClient(), | 1029 | makeDnsClient(), |
| 1030 | makeAllocations(), | 1030 | makeAllocations(), |
| ... | @@ -1065,7 +1065,7 @@ describe("ManagedEndpointProvider", () => { | ... | @@ -1065,7 +1065,7 @@ describe("ManagedEndpointProvider", () => { |
| 1065 | }), | 1065 | }), |
| 1066 | ), | 1066 | ), |
| 1067 | }); | 1067 | }); |
| 1068 | const layer = providerLayer(makePersistentTunnelClient(tunnelCalls), makeDnsClient(), replaced); | 1068 | const layer = layerProvider(makePersistentTunnelClient(tunnelCalls), makeDnsClient(), replaced); |
| 1069 | 1069 | ||
| 1070 | return Effect.gen(function* () { | 1070 | return Effect.gen(function* () { |
| 1071 | const provider = yield* ManagedEndpointProvider.ManagedEndpointProvider; | 1071 | const provider = yield* ManagedEndpointProvider.ManagedEndpointProvider; |
| ... | @@ -1082,7 +1082,7 @@ describe("ManagedEndpointProvider", () => { | ... | @@ -1082,7 +1082,7 @@ describe("ManagedEndpointProvider", () => { |
| 1082 | 1082 | ||
| 1083 | it.effect("does no Cloudflare work when the registered origin is unchanged", () => { | 1083 | it.effect("does no Cloudflare work when the registered origin is unchanged", () => { |
| 1084 | const tunnelCalls: TunnelCall[] = []; | 1084 | const tunnelCalls: TunnelCall[] = []; |
| 1085 | const layer = providerLayer(makePersistentTunnelClient(tunnelCalls)); | 1085 | const layer = layerProvider(makePersistentTunnelClient(tunnelCalls)); |
| 1086 | 1086 | ||
| 1087 | return Effect.gen(function* () { | 1087 | return Effect.gen(function* () { |
| 1088 | const provider = yield* ManagedEndpointProvider.ManagedEndpointProvider; | 1088 | const provider = yield* ManagedEndpointProvider.ManagedEndpointProvider; |
| ... | @@ -1105,7 +1105,7 @@ describe("ManagedEndpointProvider", () => { | ... | @@ -1105,7 +1105,7 @@ describe("ManagedEndpointProvider", () => { |
| 1105 | 1105 | ||
| 1106 | it.effect("updates Cloudflare ingress once when the registered port changes", () => { | 1106 | it.effect("updates Cloudflare ingress once when the registered port changes", () => { |
| 1107 | const tunnelCalls: TunnelCall[] = []; | 1107 | const tunnelCalls: TunnelCall[] = []; |
| 1108 | const layer = providerLayer(makePersistentTunnelClient(tunnelCalls)); | 1108 | const layer = layerProvider(makePersistentTunnelClient(tunnelCalls)); |
| 1109 | 1109 | ||
| 1110 | return Effect.gen(function* () { | 1110 | return Effect.gen(function* () { |
| 1111 | const provider = yield* ManagedEndpointProvider.ManagedEndpointProvider; | 1111 | const provider = yield* ManagedEndpointProvider.ManagedEndpointProvider; |
| ... | @@ -1161,7 +1161,7 @@ describe("ManagedEndpointProvider", () => { | ... | @@ -1161,7 +1161,7 @@ describe("ManagedEndpointProvider", () => { |
| 1161 | ) | 1161 | ) |
| 1162 | : baseTunnelClient.putConfiguration(tunnelId, tunnelConfig), | 1162 | : baseTunnelClient.putConfiguration(tunnelId, tunnelConfig), |
| 1163 | }); | 1163 | }); |
| 1164 | const layer = providerLayer(tunnelClient); | 1164 | const layer = layerProvider(tunnelClient); |
| 1165 | 1165 | ||
| 1166 | return Effect.gen(function* () { | 1166 | return Effect.gen(function* () { |
| 1167 | const provider = yield* ManagedEndpointProvider.ManagedEndpointProvider; | 1167 | const provider = yield* ManagedEndpointProvider.ManagedEndpointProvider; |
| ... | @@ -1191,7 +1191,7 @@ describe("ManagedEndpointProvider", () => { | ... | @@ -1191,7 +1191,7 @@ describe("ManagedEndpointProvider", () => { |
| 1191 | withClaimedTunnel: (input, effect) => | 1191 | withClaimedTunnel: (input, effect) => |
| 1192 | loseClaim ? Effect.succeedNone : allocations.withClaimedTunnel(input, effect), | 1192 | loseClaim ? Effect.succeedNone : allocations.withClaimedTunnel(input, effect), |
| 1193 | }); | 1193 | }); |
| 1194 | const layer = providerLayer(makePersistentTunnelClient(), makeDnsClient(), changed); | 1194 | const layer = layerProvider(makePersistentTunnelClient(), makeDnsClient(), changed); |
| 1195 | 1195 | ||
| 1196 | return Effect.gen(function* () { | 1196 | return Effect.gen(function* () { |
| 1197 | const provider = yield* ManagedEndpointProvider.ManagedEndpointProvider; | 1197 | const provider = yield* ManagedEndpointProvider.ManagedEndpointProvider; |
| ... | @@ -1218,7 +1218,7 @@ describe("ManagedEndpointProvider", () => { | ... | @@ -1218,7 +1218,7 @@ describe("ManagedEndpointProvider", () => { |
| 1218 | }); | 1218 | }); |
| 1219 | 1219 | ||
| 1220 | it.effect("rejects an active endpoint that does not match the allocation hostname", () => { | 1220 | it.effect("rejects an active endpoint that does not match the allocation hostname", () => { |
| 1221 | const layer = providerLayer(makePersistentTunnelClient()); | 1221 | const layer = layerProvider(makePersistentTunnelClient()); |
| 1222 | 1222 | ||
| 1223 | return Effect.gen(function* () { | 1223 | return Effect.gen(function* () { |
| 1224 | const provider = yield* ManagedEndpointProvider.ManagedEndpointProvider; | 1224 | const provider = yield* ManagedEndpointProvider.ManagedEndpointProvider; |
| ... | @@ -1263,7 +1263,7 @@ describe("ManagedEndpointProvider", () => { | ... | @@ -1263,7 +1263,7 @@ describe("ManagedEndpointProvider", () => { |
| 1263 | return yield* allocations.recordTunnel(input); | 1263 | return yield* allocations.recordTunnel(input); |
| 1264 | }), | 1264 | }), |
| 1265 | }); | 1265 | }); |
| 1266 | const layer = providerLayer( | 1266 | const layer = layerProvider( |
| 1267 | makePersistentTunnelClient(tunnelCalls), | 1267 | makePersistentTunnelClient(tunnelCalls), |
| 1268 | makeDnsClient(), | 1268 | makeDnsClient(), |
| 1269 | changed, | 1269 | changed, |
| ... | @@ -1297,7 +1297,7 @@ describe("ManagedEndpointProvider", () => { | ... | @@ -1297,7 +1297,7 @@ describe("ManagedEndpointProvider", () => { |
| 1297 | ...allocations, | 1297 | ...allocations, |
| 1298 | recordDns: () => Effect.succeed(null), | 1298 | recordDns: () => Effect.succeed(null), |
| 1299 | }); | 1299 | }); |
| 1300 | const layer = providerLayer(makePersistentTunnelClient(), makeDnsClient(), changed); | 1300 | const layer = layerProvider(makePersistentTunnelClient(), makeDnsClient(), changed); |
| 1301 | 1301 | ||
| 1302 | return Effect.gen(function* () { | 1302 | return Effect.gen(function* () { |
| 1303 | const provider = yield* ManagedEndpointProvider.ManagedEndpointProvider; | 1303 | const provider = yield* ManagedEndpointProvider.ManagedEndpointProvider; |
| ... | @@ -1323,7 +1323,7 @@ describe("ManagedEndpointProvider", () => { | ... | @@ -1323,7 +1323,7 @@ describe("ManagedEndpointProvider", () => { |
| 1323 | ...allocations, | 1323 | ...allocations, |
| 1324 | withClaimedTunnel: () => Effect.succeedNone, | 1324 | withClaimedTunnel: () => Effect.succeedNone, |
| 1325 | }); | 1325 | }); |
| 1326 | const layer = providerLayer(makePersistentTunnelClient(tunnelCalls), makeDnsClient(), changed); | 1326 | const layer = layerProvider(makePersistentTunnelClient(tunnelCalls), makeDnsClient(), changed); |
| 1327 | 1327 | ||
| 1328 | return Effect.gen(function* () { | 1328 | return Effect.gen(function* () { |
| 1329 | const provider = yield* ManagedEndpointProvider.ManagedEndpointProvider; | 1329 | const provider = yield* ManagedEndpointProvider.ManagedEndpointProvider; |
| ... | @@ -1353,7 +1353,7 @@ describe("ManagedEndpointProvider", () => { | ... | @@ -1353,7 +1353,7 @@ describe("ManagedEndpointProvider", () => { |
| 1353 | withClaimedTunnel: (input, effect) => | 1353 | withClaimedTunnel: (input, effect) => |
| 1354 | ++lockCount === 1 ? allocations.withClaimedTunnel(input, effect) : Effect.succeedNone, | 1354 | ++lockCount === 1 ? allocations.withClaimedTunnel(input, effect) : Effect.succeedNone, |
| 1355 | }); | 1355 | }); |
| 1356 | const layer = providerLayer(makePersistentTunnelClient(), makeDnsClient(dnsCalls), changed); | 1356 | const layer = layerProvider(makePersistentTunnelClient(), makeDnsClient(dnsCalls), changed); |
| 1357 | 1357 | ||
| 1358 | return Effect.gen(function* () { | 1358 | return Effect.gen(function* () { |
| 1359 | const provider = yield* ManagedEndpointProvider.ManagedEndpointProvider; | 1359 | const provider = yield* ManagedEndpointProvider.ManagedEndpointProvider; |
| ... | @@ -1379,7 +1379,7 @@ describe("ManagedEndpointProvider", () => { | ... | @@ -1379,7 +1379,7 @@ describe("ManagedEndpointProvider", () => { |
| 1379 | ...allocations, | 1379 | ...allocations, |
| 1380 | markReady: () => Effect.succeed(false), | 1380 | markReady: () => Effect.succeed(false), |
| 1381 | }); | 1381 | }); |
| 1382 | const layer = providerLayer(makePersistentTunnelClient(), makeDnsClient(), changed); | 1382 | const layer = layerProvider(makePersistentTunnelClient(), makeDnsClient(), changed); |
| 1383 | 1383 | ||
| 1384 | return Effect.gen(function* () { | 1384 | return Effect.gen(function* () { |
| 1385 | const provider = yield* ManagedEndpointProvider.ManagedEndpointProvider; | 1385 | const provider = yield* ManagedEndpointProvider.ManagedEndpointProvider; |
| ... | @@ -1411,7 +1411,7 @@ describe("ManagedEndpointProvider", () => { | ... | @@ -1411,7 +1411,7 @@ describe("ManagedEndpointProvider", () => { |
| 1411 | connsInactiveAt: null, | 1411 | connsInactiveAt: null, |
| 1412 | }), | 1412 | }), |
| 1413 | }); | 1413 | }); |
| 1414 | const layer = providerLayer(tunnelClient, makeDnsClient(), makeAllocations()); | 1414 | const layer = layerProvider(tunnelClient, makeDnsClient(), makeAllocations()); |
| 1415 | 1415 | ||
| 1416 | return Effect.gen(function* () { | 1416 | return Effect.gen(function* () { |
| 1417 | const provider = yield* ManagedEndpointProvider.ManagedEndpointProvider; | 1417 | const provider = yield* ManagedEndpointProvider.ManagedEndpointProvider; |
| ... | @@ -1454,7 +1454,7 @@ describe("ManagedEndpointProvider", () => { | ... | @@ -1454,7 +1454,7 @@ describe("ManagedEndpointProvider", () => { |
| 1454 | ), | 1454 | ), |
| 1455 | ), | 1455 | ), |
| 1456 | }); | 1456 | }); |
| 1457 | const layer = providerLayer(makePersistentTunnelClient(tunnelCalls), makeDnsClient(), replaced); | 1457 | const layer = layerProvider(makePersistentTunnelClient(tunnelCalls), makeDnsClient(), replaced); |
| 1458 | 1458 | ||
| 1459 | return Effect.gen(function* () { | 1459 | return Effect.gen(function* () { |
| 1460 | const provider = yield* ManagedEndpointProvider.ManagedEndpointProvider; | 1460 | const provider = yield* ManagedEndpointProvider.ManagedEndpointProvider; |
| ... | @@ -1489,7 +1489,7 @@ describe("ManagedEndpointProvider", () => { | ... | @@ -1489,7 +1489,7 @@ describe("ManagedEndpointProvider", () => { |
| 1489 | }), | 1489 | }), |
| 1490 | ), | 1490 | ), |
| 1491 | }); | 1491 | }); |
| 1492 | const layer = providerLayer(tunnelClient, makeDnsClient(), makeAllocations()); | 1492 | const layer = layerProvider(tunnelClient, makeDnsClient(), makeAllocations()); |
| 1493 | 1493 | ||
| 1494 | return Effect.gen(function* () { | 1494 | return Effect.gen(function* () { |
| 1495 | const provider = yield* ManagedEndpointProvider.ManagedEndpointProvider; | 1495 | const provider = yield* ManagedEndpointProvider.ManagedEndpointProvider; |
| ... | @@ -1512,7 +1512,7 @@ describe("ManagedEndpointProvider", () => { | ... | @@ -1512,7 +1512,7 @@ describe("ManagedEndpointProvider", () => { |
| 1512 | ...makeTunnelClient(), | 1512 | ...makeTunnelClient(), |
| 1513 | delete: () => Effect.fail(failure), | 1513 | delete: () => Effect.fail(failure), |
| 1514 | }); | 1514 | }); |
| 1515 | const layer = providerLayer(tunnelClient, makeDnsClient(), makeAllocations()); | 1515 | const layer = layerProvider(tunnelClient, makeDnsClient(), makeAllocations()); |
| 1516 | 1516 | ||
| 1517 | return Effect.gen(function* () { | 1517 | return Effect.gen(function* () { |
| 1518 | const provider = yield* ManagedEndpointProvider.ManagedEndpointProvider; | 1518 | const provider = yield* ManagedEndpointProvider.ManagedEndpointProvider; |
| ... | @@ -1551,7 +1551,7 @@ describe("ManagedEndpointProvider", () => { | ... | @@ -1551,7 +1551,7 @@ describe("ManagedEndpointProvider", () => { |
| 1551 | }), | 1551 | }), |
| 1552 | ), | 1552 | ), |
| 1553 | }); | 1553 | }); |
| 1554 | const layer = providerLayer(tunnelClient, makeDnsClient(), makeAllocations()); | 1554 | const layer = layerProvider(tunnelClient, makeDnsClient(), makeAllocations()); |
| 1555 | 1555 | ||
| 1556 | return Effect.gen(function* () { | 1556 | return Effect.gen(function* () { |
| 1557 | const provider = yield* ManagedEndpointProvider.ManagedEndpointProvider; | 1557 | const provider = yield* ManagedEndpointProvider.ManagedEndpointProvider; |
| ... | @@ -1568,7 +1568,7 @@ describe("ManagedEndpointProvider", () => { | ... | @@ -1568,7 +1568,7 @@ describe("ManagedEndpointProvider", () => { |
| 1568 | const tunnelCalls: TunnelCall[] = []; | 1568 | const tunnelCalls: TunnelCall[] = []; |
| 1569 | const dnsCalls: DnsCall[] = []; | 1569 | const dnsCalls: DnsCall[] = []; |
| 1570 | const allocationCalls: AllocationCall[] = []; | 1570 | const allocationCalls: AllocationCall[] = []; |
| 1571 | const layer = providerLayer( | 1571 | const layer = layerProvider( |
| 1572 | makePersistentTunnelClient(tunnelCalls), | 1572 | makePersistentTunnelClient(tunnelCalls), |
| 1573 | makeDnsClient(dnsCalls), | 1573 | makeDnsClient(dnsCalls), |
| 1574 | makeAllocations(allocationCalls), | 1574 | makeAllocations(allocationCalls), |
| ... | @@ -1606,7 +1606,7 @@ describe("ManagedEndpointProvider", () => { | ... | @@ -1606,7 +1606,7 @@ describe("ManagedEndpointProvider", () => { |
| 1606 | } | 1606 | } |
| 1607 | }), | 1607 | }), |
| 1608 | }); | 1608 | }); |
| 1609 | const layer = providerLayer(tunnelClient, makeDnsClient(), makeAllocations(allocationCalls)); | 1609 | const layer = layerProvider(tunnelClient, makeDnsClient(), makeAllocations(allocationCalls)); |
| 1610 | 1610 | ||
| 1611 | return Effect.gen(function* () { | 1611 | return Effect.gen(function* () { |
| 1612 | const provider = yield* ManagedEndpointProvider.ManagedEndpointProvider; | 1612 | const provider = yield* ManagedEndpointProvider.ManagedEndpointProvider; |
| ... | @@ -1668,7 +1668,7 @@ describe("ManagedEndpointProvider", () => { | ... | @@ -1668,7 +1668,7 @@ describe("ManagedEndpointProvider", () => { |
| 1668 | }), | 1668 | }), |
| 1669 | ), | 1669 | ), |
| 1670 | }); | 1670 | }); |
| 1671 | const layer = providerLayer(tunnelClient, dnsClient, makeAllocations(allocationCalls)); | 1671 | const layer = layerProvider(tunnelClient, dnsClient, makeAllocations(allocationCalls)); |
| 1672 | 1672 | ||
| 1673 | return Effect.gen(function* () { | 1673 | return Effect.gen(function* () { |
| 1674 | const provider = yield* ManagedEndpointProvider.ManagedEndpointProvider; | 1674 | const provider = yield* ManagedEndpointProvider.ManagedEndpointProvider; |
| ... | @@ -1705,7 +1705,7 @@ describe("ManagedEndpointProvider", () => { | ... | @@ -1705,7 +1705,7 @@ describe("ManagedEndpointProvider", () => { |
| 1705 | { name: expectedManagedTunnelName("env_shared", "user_ABC"), isDeleted: false }, | 1705 | { name: expectedManagedTunnelName("env_shared", "user_ABC"), isDeleted: false }, |
| 1706 | { name: expectedManagedTunnelName("env_shared", "user_DEF"), isDeleted: false }, | 1706 | { name: expectedManagedTunnelName("env_shared", "user_DEF"), isDeleted: false }, |
| 1707 | ]); | 1707 | ]); |
| 1708 | }).pipe(Effect.provide(providerLayer(makeTunnelClient(tunnelCalls)))); | 1708 | }).pipe(Effect.provide(layerProvider(makeTunnelClient(tunnelCalls)))); |
| 1709 | }); | 1709 | }); |
| 1710 | 1710 | ||
| 1711 | it.effect("recovers when DNS creation reports failure after the record became visible", () => { | 1711 | it.effect("recovers when DNS creation reports failure after the record became visible", () => { |
| ... | @@ -1752,7 +1752,7 @@ describe("ManagedEndpointProvider", () => { | ... | @@ -1752,7 +1752,7 @@ describe("ManagedEndpointProvider", () => { |
| 1752 | "listRecords", | 1752 | "listRecords", |
| 1753 | "updateRecord", | 1753 | "updateRecord", |
| 1754 | ]); | 1754 | ]); |
| 1755 | }).pipe(Effect.provide(providerLayer(makeTunnelClient(), dnsClient))); | 1755 | }).pipe(Effect.provide(layerProvider(makeTunnelClient(), dnsClient))); |
| 1756 | }); | 1756 | }); |
| 1757 | 1757 | ||
| 1758 | it.effect("reports mismatched tunnel responses without manufacturing a cause", () => { | 1758 | it.effect("reports mismatched tunnel responses without manufacturing a cause", () => { |
| ... | @@ -1786,7 +1786,7 @@ describe("ManagedEndpointProvider", () => { | ... | @@ -1786,7 +1786,7 @@ describe("ManagedEndpointProvider", () => { |
| 1786 | expect(error.cause).toBeUndefined(); | 1786 | expect(error.cause).toBeUndefined(); |
| 1787 | } | 1787 | } |
| 1788 | expect(dnsCalls).toHaveLength(0); | 1788 | expect(dnsCalls).toHaveLength(0); |
| 1789 | }).pipe(Effect.provide(providerLayer(tunnelClient, makeDnsClient(dnsCalls)))); | 1789 | }).pipe(Effect.provide(layerProvider(tunnelClient, makeDnsClient(dnsCalls)))); |
| 1790 | }); | 1790 | }); |
| 1791 | 1791 | ||
| 1792 | it.effect("fails provisioning when the DNS client fails", () => { | 1792 | it.effect("fails provisioning when the DNS client fails", () => { |
| ... | @@ -1824,6 +1824,6 @@ describe("ManagedEndpointProvider", () => { | ... | @@ -1824,6 +1824,6 @@ describe("ManagedEndpointProvider", () => { |
| 1824 | if (error._tag === "ManagedEndpointProvisioningFailed") { | 1824 | if (error._tag === "ManagedEndpointProvisioningFailed") { |
| 1825 | expect(error.cause).toBe(failure); | 1825 | expect(error.cause).toBe(failure); |
| 1826 | } | 1826 | } |
| 1827 | }).pipe(Effect.provide(providerLayer(makeTunnelClient(), dnsClient))); | 1827 | }).pipe(Effect.provide(layerProvider(makeTunnelClient(), dnsClient))); |
| 1828 | }); | 1828 | }); |
| 1829 | }); | 1829 | }); |
infra/relay/src/hooks/HeldHooks.test.ts+2-2| ... | @@ -66,7 +66,7 @@ const withService = <A, E>( | ... | @@ -66,7 +66,7 @@ const withService = <A, E>( |
| 66 | allocationFor("user_1", ownKey, options.ownReady ?? true), | 66 | allocationFor("user_1", ownKey, options.ownReady ?? true), |
| 67 | allocationFor("user_2", otherKey, true), | 67 | allocationFor("user_2", otherKey, true), |
| 68 | ]; | 68 | ]; |
| 69 | const dependencies = Layer.mergeAll( | 69 | const layerDependencies = Layer.mergeAll( |
| 70 | Layer.succeed(RelayConfiguration.RelayConfiguration, settings), | 70 | Layer.succeed(RelayConfiguration.RelayConfiguration, settings), |
| 71 | Layer.mock(EnvironmentLinks.EnvironmentLinks, { | 71 | Layer.mock(EnvironmentLinks.EnvironmentLinks, { |
| 72 | findActiveManagedForEnvironment: (input) => | 72 | findActiveManagedForEnvironment: (input) => |
| ... | @@ -103,7 +103,7 @@ const withService = <A, E>( | ... | @@ -103,7 +103,7 @@ const withService = <A, E>( |
| 103 | return Effect.gen(function* () { | 103 | return Effect.gen(function* () { |
| 104 | const heldHooks = yield* HeldHooks.HeldHooks; | 104 | const heldHooks = yield* HeldHooks.HeldHooks; |
| 105 | return yield* body({ heldHooks, cleared, woken }); | 105 | return yield* body({ heldHooks, cleared, woken }); |
| 106 | }).pipe(Effect.provide(HeldHooks.layer.pipe(Layer.provide(dependencies)))); | 106 | }).pipe(Effect.provide(HeldHooks.layer.pipe(Layer.provide(layerDependencies)))); |
| 107 | }; | 107 | }; |
| 108 | 108 | ||
| 109 | describe("HeldHooks", () => { | 109 | describe("HeldHooks", () => { |
infra/relay/src/hooks/HookForwarder.test.ts+6-10| ... | @@ -29,12 +29,8 @@ import * as HttpServerResponse from "effect/http/HttpServerResponse"; | ... | @@ -29,12 +29,8 @@ import * as HttpServerResponse from "effect/http/HttpServerResponse"; |
| 29 | import * as RelayConfiguration from "../Config.ts"; | 29 | import * as RelayConfiguration from "../Config.ts"; |
| 30 | import * as EnvironmentLinks from "../environments/EnvironmentLinks.ts"; | 30 | import * as EnvironmentLinks from "../environments/EnvironmentLinks.ts"; |
| 31 | import * as ManagedEndpointAllocations from "../environments/ManagedEndpointAllocations.ts"; | 31 | import * as ManagedEndpointAllocations from "../environments/ManagedEndpointAllocations.ts"; |
| 32 | import { | 32 | import { RELAY_HTTP_ROUTER_CONFIG, traceRelayHttpRequestWith } from "../http/Api.ts"; |
| 33 | RELAY_HTTP_ROUTER_CONFIG, | 33 | import * as RelayHttpApi from "../http/Api.ts"; |
| 34 | relayCors, | ||
| 35 | relayNotFoundRoute, | ||
| 36 | traceRelayHttpRequestWith, | ||
| 37 | } from "../http/Api.ts"; | ||
| 38 | import * as HookForwarder from "./HookForwarder.ts"; | 34 | import * as HookForwarder from "./HookForwarder.ts"; |
| 39 | import { RELAY_HOOK_DELIVERY_TYP, verifyRelayJwt } from "@t3tools/shared/relayJwt"; | 35 | import { RELAY_HOOK_DELIVERY_TYP, verifyRelayJwt } from "@t3tools/shared/relayJwt"; |
| 40 | import * as HeldHooks from "./HeldHooks.ts"; | 36 | import * as HeldHooks from "./HeldHooks.ts"; |
| ... | @@ -114,7 +110,7 @@ function makeHarness(options: Harness = {}) { | ... | @@ -114,7 +110,7 @@ function makeHarness(options: Harness = {}) { |
| 114 | new Response("ok", { status: 200, headers: { "content-type": "text/plain" } }), | 110 | new Response("ok", { status: 200, headers: { "content-type": "text/plain" } }), |
| 115 | ), | 111 | ), |
| 116 | )); | 112 | )); |
| 117 | const forwarderLayer = HookForwarder.layer.pipe( | 113 | const layerForwarder = HookForwarder.layer.pipe( |
| 118 | Layer.provideMerge(HeldHooks.layer), | 114 | Layer.provideMerge(HeldHooks.layer), |
| 119 | Layer.provide( | 115 | Layer.provide( |
| 120 | Layer.mergeAll( | 116 | Layer.mergeAll( |
| ... | @@ -167,11 +163,11 @@ function makeHarness(options: Harness = {}) { | ... | @@ -167,11 +163,11 @@ function makeHarness(options: Harness = {}) { |
| 167 | const httpEffect = HttpRouter.toHttpEffect( | 163 | const httpEffect = HttpRouter.toHttpEffect( |
| 168 | Layer.mergeAll( | 164 | Layer.mergeAll( |
| 169 | HttpApiBuilder.layer(HttpApi.make("RelayApi").add(RelayApi.groups.hooks)).pipe( | 165 | HttpApiBuilder.layer(HttpApi.make("RelayApi").add(RelayApi.groups.hooks)).pipe( |
| 170 | Layer.provide(HookForwarder.hooksApi.pipe(Layer.provide(forwarderLayer))), | 166 | Layer.provide(HookForwarder.layerApi.pipe(Layer.provide(layerForwarder))), |
| 171 | Layer.provide([NodeServices.layer, NodeHttpPlatform.layer, Etag.layerWeak]), | 167 | Layer.provide([NodeServices.layer, NodeHttpPlatform.layer, Etag.layerWeak]), |
| 172 | ), | 168 | ), |
| 173 | relayNotFoundRoute, | 169 | RelayHttpApi.layerNotFoundRoute, |
| 174 | relayCors, | 170 | RelayHttpApi.layerCors, |
| 175 | ), | 171 | ), |
| 176 | ).pipe(Effect.provideService(HttpRouter.RouterConfig, RELAY_HTTP_ROUTER_CONFIG)); | 172 | ).pipe(Effect.provideService(HttpRouter.RouterConfig, RELAY_HTTP_ROUTER_CONFIG)); |
| 177 | const send = (request: Request) => | 173 | const send = (request: Request) => |
infra/relay/src/hooks/HookForwarder.ts+1-1| ... | @@ -446,7 +446,7 @@ export const layer = Layer.effect(HookForwarder, make); | ... | @@ -446,7 +446,7 @@ export const layer = Layer.effect(HookForwarder, make); |
| 446 | * re-reads the encoded path segments from the request so the token and hook | 446 | * re-reads the encoded path segments from the request so the token and hook |
| 447 | * id reach the environment byte for byte, and streams the body itself. | 447 | * id reach the environment byte for byte, and streams the body itself. |
| 448 | */ | 448 | */ |
| 449 | export const hooksApi = HttpApiBuilder.group( | 449 | export const layerApi = HttpApiBuilder.group( |
| 450 | RelayApi, | 450 | RelayApi, |
| 451 | "hooks", | 451 | "hooks", |
| 452 | Effect.fnUntraced(function* (handlers) { | 452 | Effect.fnUntraced(function* (handlers) { |
infra/relay/src/hooks/HookInboxObject.ts+3-3| ... | @@ -91,19 +91,19 @@ const deliver = (baseUrl: string, hook: HookInboxStore.HeldHook) => | ... | @@ -91,19 +91,19 @@ const deliver = (baseUrl: string, hook: HookInboxStore.HeldHook) => |
| 91 | Effect.provide(FetchHttpClient.layer), | 91 | Effect.provide(FetchHttpClient.layer), |
| 92 | ); | 92 | ); |
| 93 | 93 | ||
| 94 | export const HookInboxObjectLive = HookInboxObject.make( | 94 | export const layer = HookInboxObject.make( |
| 95 | Effect.gen(function* () { | 95 | Effect.gen(function* () { |
| 96 | const state = yield* Cloudflare.DurableObjectState; | 96 | const state = yield* Cloudflare.DurableObjectState; |
| 97 | // The init phase returns the per-instance Effect, which alchemy runs once | 97 | // The init phase returns the per-instance Effect, which alchemy runs once |
| 98 | // per object; only that inner Effect may touch storage. | 98 | // per object; only that inner Effect may touch storage. |
| 99 | // @effect-diagnostics-next-line returnEffectInGen:off | 99 | // @effect-diagnostics-next-line returnEffectInGen:off |
| 100 | return Effect.gen(function* () { | 100 | return Effect.gen(function* () { |
| 101 | const sql = SqliteClient.layer({ storage: state.raw.storage }); | 101 | const layerSql = SqliteClient.layer({ storage: state.raw.storage }); |
| 102 | // Inboxes are opened by endpoint key, so spans line up with the | 102 | // Inboxes are opened by endpoint key, so spans line up with the |
| 103 | // forward spans that held their requests. | 103 | // forward spans that held their requests. |
| 104 | const inboxId = state.raw.id.name ?? state.raw.id.toString(); | 104 | const inboxId = state.raw.id.name ?? state.raw.id.toString(); |
| 105 | const run = <A, E, R>(effect: Effect.Effect<A, E, R>) => | 105 | const run = <A, E, R>(effect: Effect.Effect<A, E, R>) => |
| 106 | effect.pipe(Effect.provide(sql), Effect.orDie); | 106 | effect.pipe(Effect.provide(layerSql), Effect.orDie); |
| 107 | yield* run(HookInboxStore.migrate); | 107 | yield* run(HookInboxStore.migrate); |
| 108 | 108 | ||
| 109 | /** Moves the alarm to `at`, unless one is already due sooner. */ | 109 | /** Moves the alarm to `at`, unless one is already due sooner. */ |
infra/relay/src/http/Api.test.ts+44-40| ... | @@ -42,22 +42,17 @@ import { RELAY_MANAGED_TUNNEL_RECOVERY_TYP, signRelayJwt } from "@t3tools/shared | ... | @@ -42,22 +42,17 @@ import { RELAY_MANAGED_TUNNEL_RECOVERY_TYP, signRelayJwt } from "@t3tools/shared |
| 42 | import { | 42 | import { |
| 43 | RELAY_HTTP_ROUTER_CONFIG, | 43 | RELAY_HTTP_ROUTER_CONFIG, |
| 44 | RELAY_REQUEST_DEADLINE_MS, | 44 | RELAY_REQUEST_DEADLINE_MS, |
| 45 | clientApi, | ||
| 46 | relayCors, | ||
| 47 | relayDocsRedirectRoute, | ||
| 48 | relayEnvironmentAuthLayer, | ||
| 49 | relayNotFoundRoute, | ||
| 50 | recoverEnvironmentTunnelRecord, | 45 | recoverEnvironmentTunnelRecord, |
| 51 | registerEnvironmentTunnelRecovery, | 46 | registerEnvironmentTunnelRecovery, |
| 52 | relayDpopFailureReason, | 47 | relayDpopFailureReason, |
| 53 | revokeEnvironmentLinkRecord, | 48 | revokeEnvironmentLinkRecord, |
| 54 | serverApi, | ||
| 55 | traceRelayHttpRequestWith, | 49 | traceRelayHttpRequestWith, |
| 56 | unlinkEnvironmentRecord, | 50 | unlinkEnvironmentRecord, |
| 57 | verifyRelayClientBearerToken, | 51 | verifyRelayClientBearerToken, |
| 58 | verifyEnvironmentTunnelRecoveryProof, | 52 | verifyEnvironmentTunnelRecoveryProof, |
| 59 | withoutCapturedParentSpan, | 53 | withoutCapturedParentSpan, |
| 60 | } from "./Api.ts"; | 54 | } from "./Api.ts"; |
| 55 | import * as RelayHttpApi from "./Api.ts"; | ||
| 61 | import * as RelayConfiguration from "../Config.ts"; | 56 | import * as RelayConfiguration from "../Config.ts"; |
| 62 | import * as RelayDb from "../db.ts"; | 57 | import * as RelayDb from "../db.ts"; |
| 63 | import * as EnvironmentCredentials from "../environments/EnvironmentCredentials.ts"; | 58 | import * as EnvironmentCredentials from "../environments/EnvironmentCredentials.ts"; |
| ... | @@ -119,7 +114,7 @@ describe("device listing compatibility", () => { | ... | @@ -119,7 +114,7 @@ describe("device listing compatibility", () => { |
| 119 | iosMajorVersion: null, | 114 | iosMajorVersion: null, |
| 120 | androidApiLevel: 36, | 115 | androidApiLevel: 36, |
| 121 | }; | 116 | }; |
| 122 | const handlers = clientApi.pipe( | 117 | const layerHandlers = RelayHttpApi.layerClientApi.pipe( |
| 123 | HttpRouter.provideRequest( | 118 | HttpRouter.provideRequest( |
| 124 | Layer.mergeAll( | 119 | Layer.mergeAll( |
| 125 | Layer.mock(EnvironmentCredentials.EnvironmentCredentials, {}), | 120 | Layer.mock(EnvironmentCredentials.EnvironmentCredentials, {}), |
| ... | @@ -160,7 +155,7 @@ describe("device listing compatibility", () => { | ... | @@ -160,7 +155,7 @@ describe("device listing compatibility", () => { |
| 160 | Effect.sync(() => | 155 | Effect.sync(() => |
| 161 | HttpRouter.toWebHandler( | 156 | HttpRouter.toWebHandler( |
| 162 | HttpApiBuilder.layer(HttpApi.make("RelayApi").add(RelayApi.groups.client)).pipe( | 157 | HttpApiBuilder.layer(HttpApi.make("RelayApi").add(RelayApi.groups.client)).pipe( |
| 163 | Layer.provide(handlers), | 158 | Layer.provide(layerHandlers), |
| 164 | Layer.provide(HttpServer.layerServices), | 159 | Layer.provide(HttpServer.layerServices), |
| 165 | ), | 160 | ), |
| 166 | { disableLogger: true }, | 161 | { disableLogger: true }, |
| ... | @@ -301,7 +296,7 @@ describe("relay environment authentication", () => { | ... | @@ -301,7 +296,7 @@ describe("relay environment authentication", () => { |
| 301 | route: {} as never, | 296 | route: {} as never, |
| 302 | }), | 297 | }), |
| 303 | Effect.provide( | 298 | Effect.provide( |
| 304 | relayEnvironmentAuthLayer.pipe( | 299 | RelayHttpApi.layerEnvironmentAuth.pipe( |
| 305 | Layer.provide(Layer.succeed(EnvironmentCredentials.EnvironmentCredentials, credentials)), | 300 | Layer.provide(Layer.succeed(EnvironmentCredentials.EnvironmentCredentials, credentials)), |
| 306 | ), | 301 | ), |
| 307 | ), | 302 | ), |
| ... | @@ -310,7 +305,7 @@ describe("relay environment authentication", () => { | ... | @@ -310,7 +305,7 @@ describe("relay environment authentication", () => { |
| 310 | }); | 305 | }); |
| 311 | }); | 306 | }); |
| 312 | 307 | ||
| 313 | function relayUnlinkTestLayer(input?: { | 308 | function layerRelayUnlinkTest(input?: { |
| 314 | readonly withTransaction?: RelayDb.RelayTransactions["Service"]["withTransaction"]; | 309 | readonly withTransaction?: RelayDb.RelayTransactions["Service"]["withTransaction"]; |
| 315 | readonly getForUser?: EnvironmentLinks.EnvironmentLinks["Service"]["getForUser"]; | 310 | readonly getForUser?: EnvironmentLinks.EnvironmentLinks["Service"]["getForUser"]; |
| 316 | readonly revokeForUser?: EnvironmentLinks.EnvironmentLinks["Service"]["revokeForUser"]; | 311 | readonly revokeForUser?: EnvironmentLinks.EnvironmentLinks["Service"]["revokeForUser"]; |
| ... | @@ -512,7 +507,7 @@ describe("relay managed tunnel recovery", () => { | ... | @@ -512,7 +507,7 @@ describe("relay managed tunnel recovery", () => { |
| 512 | }).pipe( | 507 | }).pipe( |
| 513 | Effect.provide( | 508 | Effect.provide( |
| 514 | Layer.merge( | 509 | Layer.merge( |
| 515 | relayUnlinkTestLayer({ | 510 | layerRelayUnlinkTest({ |
| 516 | getForUser: () => Effect.succeed(linkedEnvironmentRecord), | 511 | getForUser: () => Effect.succeed(linkedEnvironmentRecord), |
| 517 | provision: () => Effect.die("registration must not provision a tunnel"), | 512 | provision: () => Effect.die("registration must not provision a tunnel"), |
| 518 | }), | 513 | }), |
| ... | @@ -545,7 +540,7 @@ describe("relay managed tunnel recovery", () => { | ... | @@ -545,7 +540,7 @@ describe("relay managed tunnel recovery", () => { |
| 545 | }).pipe( | 540 | }).pipe( |
| 546 | Effect.provide( | 541 | Effect.provide( |
| 547 | Layer.merge( | 542 | Layer.merge( |
| 548 | relayUnlinkTestLayer({ | 543 | layerRelayUnlinkTest({ |
| 549 | getForUser: () => Effect.succeed(linkedEnvironmentRecord), | 544 | getForUser: () => Effect.succeed(linkedEnvironmentRecord), |
| 550 | reconcileOrigin: () => Effect.succeed("recovery_required"), | 545 | reconcileOrigin: () => Effect.succeed("recovery_required"), |
| 551 | }), | 546 | }), |
| ... | @@ -580,7 +575,7 @@ describe("relay managed tunnel recovery", () => { | ... | @@ -580,7 +575,7 @@ describe("relay managed tunnel recovery", () => { |
| 580 | }).pipe( | 575 | }).pipe( |
| 581 | Effect.provide( | 576 | Effect.provide( |
| 582 | Layer.merge( | 577 | Layer.merge( |
| 583 | relayUnlinkTestLayer({ | 578 | layerRelayUnlinkTest({ |
| 584 | getForUser: () => Effect.succeed(linkedEnvironmentRecord), | 579 | getForUser: () => Effect.succeed(linkedEnvironmentRecord), |
| 585 | }), | 580 | }), |
| 586 | Layer.mock(ManagedEndpointAllocations.ManagedEndpointAllocations)({ | 581 | Layer.mock(ManagedEndpointAllocations.ManagedEndpointAllocations)({ |
| ... | @@ -611,7 +606,7 @@ describe("relay managed tunnel recovery", () => { | ... | @@ -611,7 +606,7 @@ describe("relay managed tunnel recovery", () => { |
| 611 | }).pipe( | 606 | }).pipe( |
| 612 | Effect.provide( | 607 | Effect.provide( |
| 613 | Layer.merge( | 608 | Layer.merge( |
| 614 | relayUnlinkTestLayer({ | 609 | layerRelayUnlinkTest({ |
| 615 | getForUser: () => Effect.succeed(linkedEnvironmentRecord), | 610 | getForUser: () => Effect.succeed(linkedEnvironmentRecord), |
| 616 | }), | 611 | }), |
| 617 | Layer.mock(ManagedEndpointAllocations.ManagedEndpointAllocations)({ | 612 | Layer.mock(ManagedEndpointAllocations.ManagedEndpointAllocations)({ |
| ... | @@ -657,7 +652,7 @@ describe("relay managed tunnel recovery", () => { | ... | @@ -657,7 +652,7 @@ describe("relay managed tunnel recovery", () => { |
| 657 | }).pipe( | 652 | }).pipe( |
| 658 | Effect.provide( | 653 | Effect.provide( |
| 659 | Layer.merge( | 654 | Layer.merge( |
| 660 | relayUnlinkTestLayer({ | 655 | layerRelayUnlinkTest({ |
| 661 | getForUser: () => Effect.succeed(linkedEnvironmentRecord), | 656 | getForUser: () => Effect.succeed(linkedEnvironmentRecord), |
| 662 | provision: () => | 657 | provision: () => |
| 663 | Effect.succeed({ | 658 | Effect.succeed({ |
| ... | @@ -694,7 +689,7 @@ describe("relay managed tunnel recovery", () => { | ... | @@ -694,7 +689,7 @@ describe("relay managed tunnel recovery", () => { |
| 694 | }).pipe( | 689 | }).pipe( |
| 695 | Effect.provide( | 690 | Effect.provide( |
| 696 | Layer.merge( | 691 | Layer.merge( |
| 697 | relayUnlinkTestLayer({ | 692 | layerRelayUnlinkTest({ |
| 698 | getForUser: () => Effect.succeed(linkedEnvironmentRecord), | 693 | getForUser: () => Effect.succeed(linkedEnvironmentRecord), |
| 699 | provision: () => | 694 | provision: () => |
| 700 | Effect.sync(() => { | 695 | Effect.sync(() => { |
| ... | @@ -727,7 +722,7 @@ describe("relay managed tunnel recovery", () => { | ... | @@ -727,7 +722,7 @@ describe("relay managed tunnel recovery", () => { |
| 727 | }).pipe( | 722 | }).pipe( |
| 728 | Effect.provide( | 723 | Effect.provide( |
| 729 | Layer.merge( | 724 | Layer.merge( |
| 730 | relayUnlinkTestLayer({ | 725 | layerRelayUnlinkTest({ |
| 731 | getForUser: () => | 726 | getForUser: () => |
| 732 | Effect.succeed({ | 727 | Effect.succeed({ |
| 733 | ...linkedEnvironmentRecord, | 728 | ...linkedEnvironmentRecord, |
| ... | @@ -764,7 +759,7 @@ describe("relay managed tunnel recovery", () => { | ... | @@ -764,7 +759,7 @@ describe("relay managed tunnel recovery", () => { |
| 764 | }).pipe( | 759 | }).pipe( |
| 765 | Effect.provide( | 760 | Effect.provide( |
| 766 | Layer.merge( | 761 | Layer.merge( |
| 767 | relayUnlinkTestLayer({ | 762 | layerRelayUnlinkTest({ |
| 768 | getForUser: () => Effect.succeed(linkedEnvironmentRecord), | 763 | getForUser: () => Effect.succeed(linkedEnvironmentRecord), |
| 769 | provision: () => | 764 | provision: () => |
| 770 | Effect.succeed({ | 765 | Effect.succeed({ |
| ... | @@ -843,7 +838,7 @@ describe("relay managed tunnel recovery", () => { | ... | @@ -843,7 +838,7 @@ describe("relay managed tunnel recovery", () => { |
| 843 | }).pipe( | 838 | }).pipe( |
| 844 | Effect.provide( | 839 | Effect.provide( |
| 845 | Layer.merge( | 840 | Layer.merge( |
| 846 | relayUnlinkTestLayer({ | 841 | layerRelayUnlinkTest({ |
| 847 | getForUser: () => | 842 | getForUser: () => |
| 848 | Effect.sync(() => (++lookups === 1 ? linkedEnvironmentRecord : currentLink)), | 843 | Effect.sync(() => (++lookups === 1 ? linkedEnvironmentRecord : currentLink)), |
| 849 | provision: () => | 844 | provision: () => |
| ... | @@ -887,7 +882,7 @@ describe("relay environment unlink", () => { | ... | @@ -887,7 +882,7 @@ describe("relay environment unlink", () => { |
| 887 | expect(calls).toEqual(["transaction", "link", "credential"]); | 882 | expect(calls).toEqual(["transaction", "link", "credential"]); |
| 888 | }).pipe( | 883 | }).pipe( |
| 889 | Effect.provide( | 884 | Effect.provide( |
| 890 | relayUnlinkTestLayer({ | 885 | layerRelayUnlinkTest({ |
| 891 | withTransaction: (effect) => { | 886 | withTransaction: (effect) => { |
| 892 | calls.push("transaction"); | 887 | calls.push("transaction"); |
| 893 | return effect; | 888 | return effect; |
| ... | @@ -917,7 +912,7 @@ describe("relay environment unlink", () => { | ... | @@ -917,7 +912,7 @@ describe("relay environment unlink", () => { |
| 917 | managedEndpointNamespace: "dev", | 912 | managedEndpointNamespace: "dev", |
| 918 | }).pipe( | 913 | }).pipe( |
| 919 | Effect.provide( | 914 | Effect.provide( |
| 920 | relayUnlinkTestLayer({ | 915 | layerRelayUnlinkTest({ |
| 921 | getForUser: () => Effect.succeed(linkedEnvironmentRecord), | 916 | getForUser: () => Effect.succeed(linkedEnvironmentRecord), |
| 922 | revokeForUser: () => Effect.succeed(true), | 917 | revokeForUser: () => Effect.succeed(true), |
| 923 | prepareDeprovision: () => | 918 | prepareDeprovision: () => |
| ... | @@ -986,7 +981,7 @@ describe("relay environment unlink", () => { | ... | @@ -986,7 +981,7 @@ describe("relay environment unlink", () => { |
| 986 | ]); | 981 | ]); |
| 987 | }).pipe( | 982 | }).pipe( |
| 988 | Effect.provide( | 983 | Effect.provide( |
| 989 | relayUnlinkTestLayer({ | 984 | layerRelayUnlinkTest({ |
| 990 | withTransaction: (effect) => { | 985 | withTransaction: (effect) => { |
| 991 | calls.push("transaction"); | 986 | calls.push("transaction"); |
| 992 | return effect; | 987 | return effect; |
| ... | @@ -1041,7 +1036,7 @@ describe("relay environment unlink", () => { | ... | @@ -1041,7 +1036,7 @@ describe("relay environment unlink", () => { |
| 1041 | expect(calls).toEqual(["prepare", "transaction", "link", "credential"]); | 1036 | expect(calls).toEqual(["prepare", "transaction", "link", "credential"]); |
| 1042 | }).pipe( | 1037 | }).pipe( |
| 1043 | Effect.provide( | 1038 | Effect.provide( |
| 1044 | relayUnlinkTestLayer({ | 1039 | layerRelayUnlinkTest({ |
| 1045 | withTransaction: (effect) => { | 1040 | withTransaction: (effect) => { |
| 1046 | calls.push("transaction"); | 1041 | calls.push("transaction"); |
| 1047 | return effect; | 1042 | return effect; |
| ... | @@ -1083,7 +1078,7 @@ describe("relay environment unlink", () => { | ... | @@ -1083,7 +1078,7 @@ describe("relay environment unlink", () => { |
| 1083 | expect(calls).toEqual(["prepare", "deprovision"]); | 1078 | expect(calls).toEqual(["prepare", "deprovision"]); |
| 1084 | }).pipe( | 1079 | }).pipe( |
| 1085 | Effect.provide( | 1080 | Effect.provide( |
| 1086 | relayUnlinkTestLayer({ | 1081 | layerRelayUnlinkTest({ |
| 1087 | prepareDeprovision: () => | 1082 | prepareDeprovision: () => |
| 1088 | Effect.sync(() => { | 1083 | Effect.sync(() => { |
| 1089 | calls.push("prepare"); | 1084 | calls.push("prepare"); |
| ... | @@ -1125,7 +1120,7 @@ describe("relay environment unlink", () => { | ... | @@ -1125,7 +1120,7 @@ describe("relay environment unlink", () => { |
| 1125 | expect(targets).toEqual([target, target]); | 1120 | expect(targets).toEqual([target, target]); |
| 1126 | }).pipe( | 1121 | }).pipe( |
| 1127 | Effect.provide( | 1122 | Effect.provide( |
| 1128 | relayUnlinkTestLayer({ | 1123 | layerRelayUnlinkTest({ |
| 1129 | getForUser: () => Effect.sync(() => (++lookups === 1 ? linkedEnvironmentRecord : null)), | 1124 | getForUser: () => Effect.sync(() => (++lookups === 1 ? linkedEnvironmentRecord : null)), |
| 1130 | revokeForUser: () => Effect.succeed(true), | 1125 | revokeForUser: () => Effect.succeed(true), |
| 1131 | prepareDeprovision: () => Effect.succeed(target), | 1126 | prepareDeprovision: () => Effect.succeed(target), |
| ... | @@ -1234,7 +1229,7 @@ describe("relay routing fallback", () => { | ... | @@ -1234,7 +1229,7 @@ describe("relay routing fallback", () => { |
| 1234 | EnvironmentPublishSignatures.EnvironmentPublishSignatures["Service"]["verify"] | 1229 | EnvironmentPublishSignatures.EnvironmentPublishSignatures["Service"]["verify"] |
| 1235 | >[0] | 1230 | >[0] |
| 1236 | > = []; | 1231 | > = []; |
| 1237 | const publisher = Layer.succeed(AgentActivityPublisher.AgentActivityPublisher, { | 1232 | const layerPublisher = Layer.succeed(AgentActivityPublisher.AgentActivityPublisher, { |
| 1238 | publish: (input) => | 1233 | publish: (input) => |
| 1239 | Effect.sync(() => { | 1234 | Effect.sync(() => { |
| 1240 | published.push(input); | 1235 | published.push(input); |
| ... | @@ -1242,13 +1237,16 @@ describe("relay routing fallback", () => { | ... | @@ -1242,13 +1237,16 @@ describe("relay routing fallback", () => { |
| 1242 | }), | 1237 | }), |
| 1243 | replayForLiveActivityRegistration: () => Effect.succeed(null), | 1238 | replayForLiveActivityRegistration: () => Effect.succeed(null), |
| 1244 | }); | 1239 | }); |
| 1245 | const signatures = Layer.succeed(EnvironmentPublishSignatures.EnvironmentPublishSignatures, { | 1240 | const layerSignatures = Layer.succeed( |
| 1246 | verify: (input) => | 1241 | EnvironmentPublishSignatures.EnvironmentPublishSignatures, |
| 1247 | Effect.sync(() => { | 1242 | { |
| 1248 | verified.push(input); | 1243 | verify: (input) => |
| 1249 | }), | 1244 | Effect.sync(() => { |
| 1250 | }); | 1245 | verified.push(input); |
| 1251 | const auth = Layer.succeed(RelayEnvironmentAuth, { | 1246 | }), |
| 1247 | }, | ||
| 1248 | ); | ||
| 1249 | const layerAuth = Layer.succeed(RelayEnvironmentAuth, { | ||
| 1252 | environmentBearer: (effect) => | 1250 | environmentBearer: (effect) => |
| 1253 | effect.pipe( | 1251 | effect.pipe( |
| 1254 | Effect.provideService(RelayEnvironmentPrincipal, { | 1252 | Effect.provideService(RelayEnvironmentPrincipal, { |
| ... | @@ -1257,11 +1255,11 @@ describe("relay routing fallback", () => { | ... | @@ -1257,11 +1255,11 @@ describe("relay routing fallback", () => { |
| 1257 | }), | 1255 | }), |
| 1258 | ), | 1256 | ), |
| 1259 | }); | 1257 | }); |
| 1260 | const routes = HttpApiBuilder.layer( | 1258 | const layerRoutes = HttpApiBuilder.layer( |
| 1261 | HttpApi.make("RelayApi").add(RelayApi.groups.server), | 1259 | HttpApi.make("RelayApi").add(RelayApi.groups.server), |
| 1262 | ).pipe( | 1260 | ).pipe( |
| 1263 | Layer.provide( | 1261 | Layer.provide( |
| 1264 | serverApi.pipe( | 1262 | RelayHttpApi.layerServerApi.pipe( |
| 1265 | HttpRouter.provideRequest( | 1263 | HttpRouter.provideRequest( |
| 1266 | Layer.mergeAll( | 1264 | Layer.mergeAll( |
| 1267 | Layer.succeed(RelayConfiguration.RelayConfiguration, relaySettings), | 1265 | Layer.succeed(RelayConfiguration.RelayConfiguration, relaySettings), |
| ... | @@ -1270,14 +1268,14 @@ describe("relay routing fallback", () => { | ... | @@ -1270,14 +1268,14 @@ describe("relay routing fallback", () => { |
| 1270 | Layer.mock(ManagedEndpointProvider.ManagedEndpointProvider, {}), | 1268 | Layer.mock(ManagedEndpointProvider.ManagedEndpointProvider, {}), |
| 1271 | ), | 1269 | ), |
| 1272 | ), | 1270 | ), |
| 1273 | Layer.provide([publisher, signatures, Layer.mock(HeldHooks.HeldHooks, {})]), | 1271 | Layer.provide([layerPublisher, layerSignatures, Layer.mock(HeldHooks.HeldHooks, {})]), |
| 1274 | ), | 1272 | ), |
| 1275 | ), | 1273 | ), |
| 1276 | Layer.provide(auth), | 1274 | Layer.provide(layerAuth), |
| 1277 | Layer.provide([NodeServices.layer, NodeHttpPlatform.layer, Etag.layerWeak]), | 1275 | Layer.provide([NodeServices.layer, NodeHttpPlatform.layer, Etag.layerWeak]), |
| 1278 | ); | 1276 | ); |
| 1279 | const httpEffect = yield* HttpRouter.toHttpEffect( | 1277 | const httpEffect = yield* HttpRouter.toHttpEffect( |
| 1280 | Layer.mergeAll(routes, relayNotFoundRoute, relayCors), | 1278 | Layer.mergeAll(layerRoutes, RelayHttpApi.layerNotFoundRoute, RelayHttpApi.layerCors), |
| 1281 | ).pipe(Effect.provideService(HttpRouter.RouterConfig, RELAY_HTTP_ROUTER_CONFIG)); | 1279 | ).pipe(Effect.provideService(HttpRouter.RouterConfig, RELAY_HTTP_ROUTER_CONFIG)); |
| 1282 | const threadIds = [ | 1280 | const threadIds = [ |
| 1283 | "b7c8c522-d244-43dc-875f-7224fce79912", | 1281 | "b7c8c522-d244-43dc-875f-7224fce79912", |
| ... | @@ -1319,7 +1317,11 @@ describe("relay routing fallback", () => { | ... | @@ -1319,7 +1317,11 @@ describe("relay routing fallback", () => { |
| 1319 | Effect.gen(function* () { | 1317 | Effect.gen(function* () { |
| 1320 | const request = HttpServerRequest.fromWeb(new Request("https://relay.test/")); | 1318 | const request = HttpServerRequest.fromWeb(new Request("https://relay.test/")); |
| 1321 | const httpEffect = yield* HttpRouter.toHttpEffect( | 1319 | const httpEffect = yield* HttpRouter.toHttpEffect( |
| 1322 | Layer.mergeAll(relayDocsRedirectRoute, relayNotFoundRoute, relayCors), | 1320 | Layer.mergeAll( |
| 1321 | RelayHttpApi.layerDocsRedirectRoute, | ||
| 1322 | RelayHttpApi.layerNotFoundRoute, | ||
| 1323 | RelayHttpApi.layerCors, | ||
| 1324 | ), | ||
| 1323 | ); | 1325 | ); |
| 1324 | const response = yield* httpEffect.pipe( | 1326 | const response = yield* httpEffect.pipe( |
| 1325 | Effect.provideService(HttpServerRequest.HttpServerRequest, request), | 1327 | Effect.provideService(HttpServerRequest.HttpServerRequest, request), |
| ... | @@ -1336,7 +1338,9 @@ describe("relay routing fallback", () => { | ... | @@ -1336,7 +1338,9 @@ describe("relay routing fallback", () => { |
| 1336 | const request = HttpServerRequest.fromWeb( | 1338 | const request = HttpServerRequest.fromWeb( |
| 1337 | new Request("https://relay.test/v1/environmentsd", { method: "GET" }), | 1339 | new Request("https://relay.test/v1/environmentsd", { method: "GET" }), |
| 1338 | ); | 1340 | ); |
| 1339 | const httpEffect = yield* HttpRouter.toHttpEffect(Layer.merge(relayNotFoundRoute, relayCors)); | 1341 | const httpEffect = yield* HttpRouter.toHttpEffect( |
| 1342 | Layer.merge(RelayHttpApi.layerNotFoundRoute, RelayHttpApi.layerCors), | ||
| 1343 | ); | ||
| 1340 | const response = yield* httpEffect.pipe( | 1344 | const response = yield* httpEffect.pipe( |
| 1341 | Effect.provideService(HttpServerRequest.HttpServerRequest, request), | 1345 | Effect.provideService(HttpServerRequest.HttpServerRequest, request), |
| 1342 | ); | 1346 | ); |
infra/relay/src/http/Api.ts+16-16| ... | @@ -21,7 +21,7 @@ import * as HttpTraceContext from "effect/http/HttpTraceContext"; | ... | @@ -21,7 +21,7 @@ import * as HttpTraceContext from "effect/http/HttpTraceContext"; |
| 21 | import * as HttpApiBuilder from "effect/http-api/HttpApiBuilder"; | 21 | import * as HttpApiBuilder from "effect/http-api/HttpApiBuilder"; |
| 22 | import * as HttpApiError from "effect/http-api/HttpApiError"; | 22 | import * as HttpApiError from "effect/http-api/HttpApiError"; |
| 23 | import { encodeOAuthScope } from "@t3tools/shared/oauthScope"; | 23 | import { encodeOAuthScope } from "@t3tools/shared/oauthScope"; |
| 24 | import { httpHeaderRedactionLayer } from "@t3tools/shared/httpObservability"; | 24 | import * as HttpObservability from "@t3tools/shared/httpObservability"; |
| 25 | 25 | ||
| 26 | import { | 26 | import { |
| 27 | RelayApi, | 27 | RelayApi, |
| ... | @@ -146,7 +146,7 @@ const appendRelayTraceContextResponseHeader = Effect.gen(function* () { | ... | @@ -146,7 +146,7 @@ const appendRelayTraceContextResponseHeader = Effect.gen(function* () { |
| 146 | ); | 146 | ); |
| 147 | }).pipe(Effect.ignore); | 147 | }).pipe(Effect.ignore); |
| 148 | 148 | ||
| 149 | export const relayCors = HttpRouter.middleware( | 149 | export const layerCors = HttpRouter.middleware( |
| 150 | Effect.fnUntraced(function* <E, R>( | 150 | Effect.fnUntraced(function* <E, R>( |
| 151 | httpEffect: Effect.Effect< | 151 | httpEffect: Effect.Effect< |
| 152 | HttpServerResponse.HttpServerResponse, | 152 | HttpServerResponse.HttpServerResponse, |
| ... | @@ -171,13 +171,13 @@ export const relayCors = HttpRouter.middleware( | ... | @@ -171,13 +171,13 @@ export const relayCors = HttpRouter.middleware( |
| 171 | { global: true }, | 171 | { global: true }, |
| 172 | ); | 172 | ); |
| 173 | 173 | ||
| 174 | export const relayNotFoundRoute = HttpRouter.add( | 174 | export const layerNotFoundRoute = HttpRouter.add( |
| 175 | "*", | 175 | "*", |
| 176 | "/*", | 176 | "/*", |
| 177 | HttpServerResponse.empty({ status: 404 }), | 177 | HttpServerResponse.empty({ status: 404 }), |
| 178 | ); | 178 | ); |
| 179 | 179 | ||
| 180 | export const relayDocsRedirectRoute = HttpRouter.add( | 180 | export const layerDocsRedirectRoute = HttpRouter.add( |
| 181 | "GET", | 181 | "GET", |
| 182 | "/", | 182 | "/", |
| 183 | HttpServerResponse.redirect("/docs"), | 183 | HttpServerResponse.redirect("/docs"), |
| ... | @@ -280,7 +280,7 @@ export const traceRelayHttpRequest = <E, R>( | ... | @@ -280,7 +280,7 @@ export const traceRelayHttpRequest = <E, R>( |
| 280 | 280 | ||
| 281 | // Webhook senders put shared secrets and signatures in headers such as | 281 | // Webhook senders put shared secrets and signatures in headers such as |
| 282 | // x-hub-signature-256, stripe-signature, x-gitlab-token and x-webhook-key. | 282 | // x-hub-signature-256, stripe-signature, x-gitlab-token and x-webhook-key. |
| 283 | const webhookHeaderRedactionLayer = Layer.effect( | 283 | const layerWebhookHeaderRedaction = Layer.effect( |
| 284 | Headers.CurrentRedactedNames, | 284 | Headers.CurrentRedactedNames, |
| 285 | Effect.map(Headers.CurrentRedactedNames, (names) => [ | 285 | Effect.map(Headers.CurrentRedactedNames, (names) => [ |
| 286 | ...names, | 286 | ...names, |
| ... | @@ -304,7 +304,7 @@ export const traceRelayHttpRequestWith = <E, R, LayerError, LayerRequirements>( | ... | @@ -304,7 +304,7 @@ export const traceRelayHttpRequestWith = <E, R, LayerError, LayerRequirements>( |
| 304 | Effect.provide( | 304 | Effect.provide( |
| 305 | Layer.merge( | 305 | Layer.merge( |
| 306 | tracerLayer, | 306 | tracerLayer, |
| 307 | webhookHeaderRedactionLayer.pipe(Layer.provide(httpHeaderRedactionLayer)), | 307 | layerWebhookHeaderRedaction.pipe(Layer.provide(HttpObservability.layer)), |
| 308 | ), | 308 | ), |
| 309 | ), | 309 | ), |
| 310 | ); | 310 | ); |
| ... | @@ -319,7 +319,7 @@ export const withoutCapturedParentSpan = <A, E, R>( | ... | @@ -319,7 +319,7 @@ export const withoutCapturedParentSpan = <A, E, R>( |
| 319 | return effect.pipe(Effect.ensuring(Effect.sync(() => fiber.setContext(context)))); | 319 | return effect.pipe(Effect.ensuring(Effect.sync(() => fiber.setContext(context)))); |
| 320 | }); | 320 | }); |
| 321 | 321 | ||
| 322 | export const relayClientAuthLayer = Layer.effect( | 322 | export const layerClientAuth = Layer.effect( |
| 323 | RelayClientAuth, | 323 | RelayClientAuth, |
| 324 | Effect.gen(function* () { | 324 | Effect.gen(function* () { |
| 325 | const config = yield* RelayConfiguration.RelayConfiguration; | 325 | const config = yield* RelayConfiguration.RelayConfiguration; |
| ... | @@ -358,7 +358,7 @@ export const relayClientAuthLayer = Layer.effect( | ... | @@ -358,7 +358,7 @@ export const relayClientAuthLayer = Layer.effect( |
| 358 | }), | 358 | }), |
| 359 | ); | 359 | ); |
| 360 | 360 | ||
| 361 | export const relayEnvironmentAuthLayer = Layer.effect( | 361 | export const layerEnvironmentAuth = Layer.effect( |
| 362 | RelayEnvironmentAuth, | 362 | RelayEnvironmentAuth, |
| 363 | Effect.gen(function* () { | 363 | Effect.gen(function* () { |
| 364 | const credentials = yield* EnvironmentCredentials.EnvironmentCredentials; | 364 | const credentials = yield* EnvironmentCredentials.EnvironmentCredentials; |
| ... | @@ -391,7 +391,7 @@ export const relayEnvironmentAuthLayer = Layer.effect( | ... | @@ -391,7 +391,7 @@ export const relayEnvironmentAuthLayer = Layer.effect( |
| 391 | }), | 391 | }), |
| 392 | ); | 392 | ); |
| 393 | 393 | ||
| 394 | export const relayDpopClientAuthLayer = Layer.effect( | 394 | export const layerDpopClientAuth = Layer.effect( |
| 395 | RelayDpopClientAuth, | 395 | RelayDpopClientAuth, |
| 396 | Effect.gen(function* () { | 396 | Effect.gen(function* () { |
| 397 | const relayTokens = yield* RelayTokens.RelayTokens; | 397 | const relayTokens = yield* RelayTokens.RelayTokens; |
| ... | @@ -438,7 +438,7 @@ function readHttpAuthorizationCredential(credential: Redacted.Redacted<string>): | ... | @@ -438,7 +438,7 @@ function readHttpAuthorizationCredential(credential: Redacted.Redacted<string>): |
| 438 | return Redacted.value(credential).trimStart(); | 438 | return Redacted.value(credential).trimStart(); |
| 439 | } | 439 | } |
| 440 | 440 | ||
| 441 | export const metadataApi = HttpApiBuilder.group( | 441 | export const layerMetadataApi = HttpApiBuilder.group( |
| 442 | RelayApi, | 442 | RelayApi, |
| 443 | "metadata", | 443 | "metadata", |
| 444 | Effect.fnUntraced(function* (handlers) { | 444 | Effect.fnUntraced(function* (handlers) { |
| ... | @@ -472,7 +472,7 @@ export const metadataApi = HttpApiBuilder.group( | ... | @@ -472,7 +472,7 @@ export const metadataApi = HttpApiBuilder.group( |
| 472 | }), | 472 | }), |
| 473 | ); | 473 | ); |
| 474 | 474 | ||
| 475 | export const healthApi = HttpApiBuilder.group( | 475 | export const layerHealthApi = HttpApiBuilder.group( |
| 476 | RelayApi, | 476 | RelayApi, |
| 477 | "health", | 477 | "health", |
| 478 | Effect.fnUntraced(function* (handlers) { | 478 | Effect.fnUntraced(function* (handlers) { |
| ... | @@ -768,7 +768,7 @@ export const recoverEnvironmentTunnelRecord = Effect.fn( | ... | @@ -768,7 +768,7 @@ export const recoverEnvironmentTunnelRecord = Effect.fn( |
| 768 | }; | 768 | }; |
| 769 | }); | 769 | }); |
| 770 | 770 | ||
| 771 | export const mobileApi = HttpApiBuilder.group( | 771 | export const layerMobileApi = HttpApiBuilder.group( |
| 772 | RelayApi, | 772 | RelayApi, |
| 773 | "mobile", | 773 | "mobile", |
| 774 | Effect.fnUntraced(function* (handlers) { | 774 | Effect.fnUntraced(function* (handlers) { |
| ... | @@ -825,7 +825,7 @@ export const mobileApi = HttpApiBuilder.group( | ... | @@ -825,7 +825,7 @@ export const mobileApi = HttpApiBuilder.group( |
| 825 | }), | 825 | }), |
| 826 | ); | 826 | ); |
| 827 | 827 | ||
| 828 | export const clientApi = HttpApiBuilder.group( | 828 | export const layerClientApi = HttpApiBuilder.group( |
| 829 | RelayApi, | 829 | RelayApi, |
| 830 | "client", | 830 | "client", |
| 831 | Effect.fnUntraced(function* (handlers) { | 831 | Effect.fnUntraced(function* (handlers) { |
| ... | @@ -1006,7 +1006,7 @@ export const clientApi = HttpApiBuilder.group( | ... | @@ -1006,7 +1006,7 @@ export const clientApi = HttpApiBuilder.group( |
| 1006 | }), | 1006 | }), |
| 1007 | ); | 1007 | ); |
| 1008 | 1008 | ||
| 1009 | export const tokenApi = HttpApiBuilder.group( | 1009 | export const layerTokenApi = HttpApiBuilder.group( |
| 1010 | RelayApi, | 1010 | RelayApi, |
| 1011 | "token", | 1011 | "token", |
| 1012 | Effect.fnUntraced(function* (handlers) { | 1012 | Effect.fnUntraced(function* (handlers) { |
| ... | @@ -1068,7 +1068,7 @@ export const tokenApi = HttpApiBuilder.group( | ... | @@ -1068,7 +1068,7 @@ export const tokenApi = HttpApiBuilder.group( |
| 1068 | }), | 1068 | }), |
| 1069 | ); | 1069 | ); |
| 1070 | 1070 | ||
| 1071 | export const dpopClientApi = HttpApiBuilder.group( | 1071 | export const layerDpopClientApi = HttpApiBuilder.group( |
| 1072 | RelayApi, | 1072 | RelayApi, |
| 1073 | "dpopClient", | 1073 | "dpopClient", |
| 1074 | Effect.fnUntraced(function* (handlers) { | 1074 | Effect.fnUntraced(function* (handlers) { |
| ... | @@ -1171,7 +1171,7 @@ export const dpopClientApi = HttpApiBuilder.group( | ... | @@ -1171,7 +1171,7 @@ export const dpopClientApi = HttpApiBuilder.group( |
| 1171 | }), | 1171 | }), |
| 1172 | ); | 1172 | ); |
| 1173 | 1173 | ||
| 1174 | export const serverApi = HttpApiBuilder.group( | 1174 | export const layerServerApi = HttpApiBuilder.group( |
| 1175 | RelayApi, | 1175 | RelayApi, |
| 1176 | "server", | 1176 | "server", |
| 1177 | Effect.fnUntraced(function* (handlers) { | 1177 | Effect.fnUntraced(function* (handlers) { |
infra/relay/src/observability.test.ts+2-2| ... | @@ -10,7 +10,7 @@ import * as HttpServerResponse from "effect/http/HttpServerResponse"; | ... | @@ -10,7 +10,7 @@ import * as HttpServerResponse from "effect/http/HttpServerResponse"; |
| 10 | import type { OtlpTracer } from "effect/observability"; | 10 | import type { OtlpTracer } from "effect/observability"; |
| 11 | 11 | ||
| 12 | import * as EnvironmentConnector from "./environments/EnvironmentConnector.ts"; | 12 | import * as EnvironmentConnector from "./environments/EnvironmentConnector.ts"; |
| 13 | import { makeRelayTraceLayer } from "./observability.ts"; | 13 | import * as Observability from "./observability.ts"; |
| 14 | 14 | ||
| 15 | interface ExportedRequest { | 15 | interface ExportedRequest { |
| 16 | readonly authorization: string | undefined; | 16 | readonly authorization: string | undefined; |
| ... | @@ -52,7 +52,7 @@ it.effect("exports schema error fields as span attributes", () => | ... | @@ -52,7 +52,7 @@ it.effect("exports schema error fields as span attributes", () => |
| 52 | Effect.withSpan("relay.test.schema_error"), | 52 | Effect.withSpan("relay.test.schema_error"), |
| 53 | Effect.exit, | 53 | Effect.exit, |
| 54 | Effect.provide( | 54 | Effect.provide( |
| 55 | makeRelayTraceLayer({ | 55 | Observability.layer({ |
| 56 | tracesEndpoint: "/v1/traces", | 56 | tracesEndpoint: "/v1/traces", |
| 57 | tracesDatasetName: "relay-test-traces", | 57 | tracesDatasetName: "relay-test-traces", |
| 58 | ingestToken: Redacted.make("test-token"), | 58 | ingestToken: Redacted.make("test-token"), |
infra/relay/src/observability.ts+1-1| ... | @@ -212,7 +212,7 @@ const withSchemaErrorAttributes = (delegate: Tracer.Tracer): Tracer.Tracer => | ... | @@ -212,7 +212,7 @@ const withSchemaErrorAttributes = (delegate: Tracer.Tracer): Tracer.Tracer => |
| 212 | ...(delegate.context ? { context: delegate.context } : {}), | 212 | ...(delegate.context ? { context: delegate.context } : {}), |
| 213 | }); | 213 | }); |
| 214 | 214 | ||
| 215 | export const makeRelayTraceLayer = (input: { | 215 | export const layer = (input: { |
| 216 | readonly tracesEndpoint: string; | 216 | readonly tracesEndpoint: string; |
| 217 | readonly tracesDatasetName: string; | 217 | readonly tracesDatasetName: string; |
| 218 | readonly ingestToken: Redacted.Redacted<string>; | 218 | readonly ingestToken: Redacted.Redacted<string>; |
infra/relay/src/worker.ts+43-51| ... | @@ -21,25 +21,14 @@ import * as HttpApiScalar from "effect/http-api/HttpApiScalar"; | ... | @@ -21,25 +21,14 @@ import * as HttpApiScalar from "effect/http-api/HttpApiScalar"; |
| 21 | import { RelayApi } from "@t3tools/contracts/relay"; | 21 | import { RelayApi } from "@t3tools/contracts/relay"; |
| 22 | 22 | ||
| 23 | import { | 23 | import { |
| 24 | clientApi, | ||
| 25 | dpopClientApi, | ||
| 26 | healthApi, | ||
| 27 | metadataApi, | ||
| 28 | mobileApi, | ||
| 29 | RELAY_HTTP_ROUTER_CONFIG, | 24 | RELAY_HTTP_ROUTER_CONFIG, |
| 30 | relayClientAuthLayer, | ||
| 31 | relayDpopClientAuthLayer, | ||
| 32 | relayCors, | ||
| 33 | relayDocsRedirectRoute, | ||
| 34 | relayEnvironmentAuthLayer, | ||
| 35 | relayNotFoundRoute, | ||
| 36 | serverApi, | ||
| 37 | traceRelayHttpRequestWith, | 25 | traceRelayHttpRequestWith, |
| 38 | tokenApi, | ||
| 39 | withoutCapturedParentSpan, | 26 | withoutCapturedParentSpan, |
| 40 | } from "./http/Api.ts"; | 27 | } from "./http/Api.ts"; |
| 28 | import * as RelayHttpApi from "./http/Api.ts"; | ||
| 41 | import { ManagedEndpointZone, RelayApiZone, RelayDeploymentConfig } from "./zone.ts"; | 29 | import { ManagedEndpointZone, RelayApiZone, RelayDeploymentConfig } from "./zone.ts"; |
| 42 | import { makeRelayTraceLayer, RelayObservability } from "./observability.ts"; | 30 | import { RelayObservability } from "./observability.ts"; |
| 31 | import * as Observability from "./observability.ts"; | ||
| 43 | import * as DeliveryAttempts from "./agentActivity/DeliveryAttempts.ts"; | 32 | import * as DeliveryAttempts from "./agentActivity/DeliveryAttempts.ts"; |
| 44 | import * as AgentActivityRows from "./agentActivity/AgentActivityRows.ts"; | 33 | import * as AgentActivityRows from "./agentActivity/AgentActivityRows.ts"; |
| 45 | import * as Devices from "./agentActivity/Devices.ts"; | 34 | import * as Devices from "./agentActivity/Devices.ts"; |
| ... | @@ -78,9 +67,10 @@ import * as MobileRegistrations from "./agentActivity/MobileRegistrations.ts"; | ... | @@ -78,9 +67,10 @@ import * as MobileRegistrations from "./agentActivity/MobileRegistrations.ts"; |
| 78 | import * as HookForwarder from "./hooks/HookForwarder.ts"; | 67 | import * as HookForwarder from "./hooks/HookForwarder.ts"; |
| 79 | import * as HeldHooks from "./hooks/HeldHooks.ts"; | 68 | import * as HeldHooks from "./hooks/HeldHooks.ts"; |
| 80 | import * as HookInbox from "./hooks/HookInbox.ts"; | 69 | import * as HookInbox from "./hooks/HookInbox.ts"; |
| 81 | import { HookInboxObject, HookInboxObjectLive } from "./hooks/HookInboxObject.ts"; | 70 | import { HookInboxObject } from "./hooks/HookInboxObject.ts"; |
| 71 | import * as HookInboxObjectLayer from "./hooks/HookInboxObject.ts"; | ||
| 82 | 72 | ||
| 83 | const webcryptoLayer = Layer.succeed( | 73 | const layerWebcrypto = Layer.succeed( |
| 84 | Crypto.Crypto, | 74 | Crypto.Crypto, |
| 85 | Crypto.make({ | 75 | Crypto.make({ |
| 86 | randomBytes: (size) => globalThis.crypto.getRandomValues(new Uint8Array(size)), | 76 | randomBytes: (size) => globalThis.crypto.getRandomValues(new Uint8Array(size)), |
| ... | @@ -93,7 +83,7 @@ const webcryptoLayer = Layer.succeed( | ... | @@ -93,7 +83,7 @@ const webcryptoLayer = Layer.succeed( |
| 93 | }), | 83 | }), |
| 94 | ); | 84 | ); |
| 95 | 85 | ||
| 96 | const httpPlatformNotSupportedLayer = Layer.succeed(HttpPlatform.HttpPlatform, { | 86 | const layerHttpPlatformNotSupported = Layer.succeed(HttpPlatform.HttpPlatform, { |
| 97 | platform: "web", | 87 | platform: "web", |
| 98 | compression: { | 88 | compression: { |
| 99 | algorithms: new Set<HttpPlatform.CompressionAlgorithm>(), | 89 | algorithms: new Set<HttpPlatform.CompressionAlgorithm>(), |
| ... | @@ -103,14 +93,14 @@ const httpPlatformNotSupportedLayer = Layer.succeed(HttpPlatform.HttpPlatform, { | ... | @@ -103,14 +93,14 @@ const httpPlatformNotSupportedLayer = Layer.succeed(HttpPlatform.HttpPlatform, { |
| 103 | fileWebResponse: () => Effect.die("Relay API does not serve file responses"), | 93 | fileWebResponse: () => Effect.die("Relay API does not serve file responses"), |
| 104 | }); | 94 | }); |
| 105 | 95 | ||
| 106 | const relayApiLayer = Layer.mergeAll( | 96 | const layerRelayApi = Layer.mergeAll( |
| 107 | healthApi, | 97 | RelayHttpApi.layerHealthApi, |
| 108 | metadataApi, | 98 | RelayHttpApi.layerMetadataApi, |
| 109 | mobileApi, | 99 | RelayHttpApi.layerMobileApi, |
| 110 | clientApi, | 100 | RelayHttpApi.layerClientApi, |
| 111 | tokenApi, | 101 | RelayHttpApi.layerTokenApi, |
| 112 | dpopClientApi, | 102 | RelayHttpApi.layerDpopClientApi, |
| 113 | serverApi, | 103 | RelayHttpApi.layerServerApi, |
| 114 | ); | 104 | ); |
| 115 | 105 | ||
| 116 | const CloudMintKeyPair = Alchemy.KeyPair("CloudMintKeyPair"); | 106 | const CloudMintKeyPair = Alchemy.KeyPair("CloudMintKeyPair"); |
| ... | @@ -120,7 +110,7 @@ const ApnsDeliveryJobSigningSecret = Alchemy.makeRandom("ApnsDeliveryJobSigningS | ... | @@ -120,7 +110,7 @@ const ApnsDeliveryJobSigningSecret = Alchemy.makeRandom("ApnsDeliveryJobSigningS |
| 120 | 110 | ||
| 121 | export class Api extends Cloudflare.Worker<Api, {}>()("Api") {} | 111 | export class Api extends Cloudflare.Worker<Api, {}>()("Api") {} |
| 122 | 112 | ||
| 123 | export const ApiLive = Api.make( | 113 | export const layer = Api.make( |
| 124 | RelayDeploymentConfig.pipe( | 114 | RelayDeploymentConfig.pipe( |
| 125 | Effect.map(({ relayPublicDomain }) => ({ | 115 | Effect.map(({ relayPublicDomain }) => ({ |
| 126 | main: import.meta.filename, | 116 | main: import.meta.filename, |
| ... | @@ -235,12 +225,12 @@ export const ApiLive = Api.make( | ... | @@ -235,12 +225,12 @@ export const ApiLive = Api.make( |
| 235 | }); | 225 | }); |
| 236 | }); | 226 | }); |
| 237 | 227 | ||
| 238 | const relayTraceLayer = Layer.unwrap( | 228 | const layerRelayTrace = Layer.unwrap( |
| 239 | Effect.all({ | 229 | Effect.all({ |
| 240 | tracesDatasetName: axiomDatasetName, | 230 | tracesDatasetName: axiomDatasetName, |
| 241 | tracesEndpoint: axiomTracesEndpoint, | 231 | tracesEndpoint: axiomTracesEndpoint, |
| 242 | ingestToken: axiomIngestToken, | 232 | ingestToken: axiomIngestToken, |
| 243 | }).pipe(Effect.map(makeRelayTraceLayer)), | 233 | }).pipe(Effect.map(Observability.layer)), |
| 244 | ); | 234 | ); |
| 245 | 235 | ||
| 246 | // Each managed endpoint's held webhook requests live in its own Durable Object. | 236 | // Each managed endpoint's held webhook requests live in its own Durable Object. |
| ... | @@ -259,7 +249,7 @@ export const ApiLive = Api.make( | ... | @@ -259,7 +249,7 @@ export const ApiLive = Api.make( |
| 259 | ), | 249 | ), |
| 260 | ), | 250 | ), |
| 261 | ); | 251 | ); |
| 262 | const hookInboxLayer = Layer.succeed(HookInbox.HookInbox, { | 252 | const layerHookInbox = Layer.succeed(HookInbox.HookInbox, { |
| 263 | hold: ({ endpointKey, baseUrl, hook }) => | 253 | hold: ({ endpointKey, baseUrl, hook }) => |
| 264 | hookInboxes.getByName(endpointKey).hold(hook, baseUrl).pipe(inboxCall("hold", endpointKey)), | 254 | hookInboxes.getByName(endpointKey).hold(hook, baseUrl).pipe(inboxCall("hold", endpointKey)), |
| 265 | wake: ({ endpointKey, baseUrl }) => | 255 | wake: ({ endpointKey, baseUrl }) => |
| ... | @@ -268,7 +258,7 @@ export const ApiLive = Api.make( | ... | @@ -268,7 +258,7 @@ export const ApiLive = Api.make( |
| 268 | hookInboxes.getByName(endpointKey).clear().pipe(inboxCall("clear", endpointKey)), | 258 | hookInboxes.getByName(endpointKey).clear().pipe(inboxCall("clear", endpointKey)), |
| 269 | }); | 259 | }); |
| 270 | 260 | ||
| 271 | const runtimeLayer = Layer.empty.pipe( | 261 | const layerRuntime = Layer.empty.pipe( |
| 272 | Layer.provideMerge(MobileRegistrations.layer), | 262 | Layer.provideMerge(MobileRegistrations.layer), |
| 273 | Layer.provideMerge(AgentActivityPublisher.layer), | 263 | Layer.provideMerge(AgentActivityPublisher.layer), |
| 274 | Layer.provideMerge(EnvironmentConnector.layer), | 264 | Layer.provideMerge(EnvironmentConnector.layer), |
| ... | @@ -313,7 +303,7 @@ export const ApiLive = Api.make( | ... | @@ -313,7 +303,7 @@ export const ApiLive = Api.make( |
| 313 | Layer.provideMerge( | 303 | Layer.provideMerge( |
| 314 | ApnsDeliveryQueue.layerCloudflareQueues(apnsDeliveryQueueSender, alchemyRuntimeContext), | 304 | ApnsDeliveryQueue.layerCloudflareQueues(apnsDeliveryQueueSender, alchemyRuntimeContext), |
| 315 | ), | 305 | ), |
| 316 | Layer.provideMerge(Layer.mergeAll(AgentActivityRows.layer, Devices.layer, hookInboxLayer)), | 306 | Layer.provideMerge(Layer.mergeAll(AgentActivityRows.layer, Devices.layer, layerHookInbox)), |
| 317 | Layer.provideMerge(EnvironmentCredentials.layer), | 307 | Layer.provideMerge(EnvironmentCredentials.layer), |
| 318 | Layer.provideMerge( | 308 | Layer.provideMerge( |
| 319 | Layer.mergeAll( | 309 | Layer.mergeAll( |
| ... | @@ -331,7 +321,7 @@ export const ApiLive = Api.make( | ... | @@ -331,7 +321,7 @@ export const ApiLive = Api.make( |
| 331 | ), | 321 | ), |
| 332 | ), | 322 | ), |
| 333 | Layer.provideMerge(Layer.effect(RelayConfiguration.RelayConfiguration, loadSettings)), | 323 | Layer.provideMerge(Layer.effect(RelayConfiguration.RelayConfiguration, loadSettings)), |
| 334 | Layer.provideMerge(webcryptoLayer), | 324 | Layer.provideMerge(layerWebcrypto), |
| 335 | ); | 325 | ); |
| 336 | 326 | ||
| 337 | // Fails open: a limiter outage must not drop webhooks the environment would accept. | 327 | // Fails open: a limiter outage must not drop webhooks the environment would accept. |
| ... | @@ -351,22 +341,22 @@ export const ApiLive = Api.make( | ... | @@ -351,22 +341,22 @@ export const ApiLive = Api.make( |
| 351 | ), | 341 | ), |
| 352 | ), | 342 | ), |
| 353 | ); | 343 | ); |
| 354 | const hookRateLimiterLayer = Layer.succeed(HookForwarder.HookRateLimiter, { | 344 | const layerHookRateLimiter = Layer.succeed(HookForwarder.HookRateLimiter, { |
| 355 | allowHook: allowWith(hookRateLimit), | 345 | allowHook: allowWith(hookRateLimit), |
| 356 | allowEndpoint: allowWith(hookEndpointRateLimit), | 346 | allowEndpoint: allowWith(hookEndpointRateLimit), |
| 357 | }); | 347 | }); |
| 358 | 348 | ||
| 359 | const appLayer = Layer.merge( | 349 | const layerApp = Layer.merge( |
| 360 | relayApiLayer, | 350 | layerRelayApi, |
| 361 | HookForwarder.hooksApi.pipe( | 351 | HookForwarder.layerApi.pipe( |
| 362 | Layer.provide(HookForwarder.layer), | 352 | Layer.provide(HookForwarder.layer), |
| 363 | Layer.provide(hookRateLimiterLayer), | 353 | Layer.provide(layerHookRateLimiter), |
| 364 | ), | 354 | ), |
| 365 | ).pipe( | 355 | ).pipe( |
| 366 | Layer.provideMerge(relayClientAuthLayer), | 356 | Layer.provideMerge(RelayHttpApi.layerClientAuth), |
| 367 | Layer.provideMerge(relayDpopClientAuthLayer), | 357 | Layer.provideMerge(RelayHttpApi.layerDpopClientAuth), |
| 368 | Layer.provideMerge(relayEnvironmentAuthLayer), | 358 | Layer.provideMerge(RelayHttpApi.layerEnvironmentAuth), |
| 369 | Layer.provide(runtimeLayer), | 359 | Layer.provide(layerRuntime), |
| 370 | ); | 360 | ); |
| 371 | 361 | ||
| 372 | yield* Cloudflare.Queues.consumeQueueMessages<unknown>( | 362 | yield* Cloudflare.Queues.consumeQueueMessages<unknown>( |
| ... | @@ -387,7 +377,7 @@ export const ApiLive = Api.make( | ... | @@ -387,7 +377,7 @@ export const ApiLive = Api.make( |
| 387 | Effect.withSpan("relay.apn_delivery_queue.process_message"), | 377 | Effect.withSpan("relay.apn_delivery_queue.process_message"), |
| 388 | ), | 378 | ), |
| 389 | ), | 379 | ), |
| 390 | Effect.provide(runtimeLayer), | 380 | Effect.provide(layerRuntime), |
| 391 | ), | 381 | ), |
| 392 | ); | 382 | ); |
| 393 | 383 | ||
| ... | @@ -404,7 +394,7 @@ export const ApiLive = Api.make( | ... | @@ -404,7 +394,7 @@ export const ApiLive = Api.make( |
| 404 | stream.pipe( | 394 | stream.pipe( |
| 405 | Stream.withSpan("relay.fcm_delivery_queue.process_batch"), | 395 | Stream.withSpan("relay.fcm_delivery_queue.process_batch"), |
| 406 | Stream.runForEach(FcmDeliveryQueueConsumer.processMessage), | 396 | Stream.runForEach(FcmDeliveryQueueConsumer.processMessage), |
| 407 | Effect.provide(runtimeLayer), | 397 | Effect.provide(layerRuntime), |
| 408 | ), | 398 | ), |
| 409 | ); | 399 | ); |
| 410 | 400 | ||
| ... | @@ -447,24 +437,26 @@ export const ApiLive = Api.make( | ... | @@ -447,24 +437,26 @@ export const ApiLive = Api.make( |
| 447 | ).pipe( | 437 | ).pipe( |
| 448 | Effect.withSpan("relay.cron.prune_expired_state"), | 438 | Effect.withSpan("relay.cron.prune_expired_state"), |
| 449 | // Export cron spans to Axiom like HTTP spans; the scope flushes them before the run ends. | 439 | // Export cron spans to Axiom like HTTP spans; the scope flushes them before the run ends. |
| 450 | Effect.provide(Layer.merge(runtimeLayer, relayTraceLayer)), | 440 | Effect.provide(Layer.merge(layerRuntime, layerRelayTrace)), |
| 451 | ), | 441 | ), |
| 452 | ); | 442 | ); |
| 453 | 443 | ||
| 454 | const fetch = Layer.merge( | 444 | const fetch = Layer.merge( |
| 455 | Layer.mergeAll( | 445 | Layer.mergeAll( |
| 456 | HttpApiBuilder.layer(RelayApi, { openapiPath: "/openapi.json" }).pipe( | 446 | HttpApiBuilder.layer(RelayApi, { openapiPath: "/openapi.json" }).pipe( |
| 457 | Layer.provide(appLayer), | 447 | Layer.provide(layerApp), |
| 458 | ), | 448 | ), |
| 459 | HttpApiScalar.layer(RelayApi, { path: "/docs" }), | 449 | HttpApiScalar.layer(RelayApi, { path: "/docs" }), |
| 460 | relayDocsRedirectRoute, | 450 | RelayHttpApi.layerDocsRedirectRoute, |
| 461 | ).pipe(Layer.provide([Etag.layerWeak, httpPlatformNotSupportedLayer, relayCors])), | 451 | ).pipe( |
| 462 | relayNotFoundRoute, | 452 | Layer.provide([Etag.layerWeak, layerHttpPlatformNotSupported, RelayHttpApi.layerCors]), |
| 453 | ), | ||
| 454 | RelayHttpApi.layerNotFoundRoute, | ||
| 463 | ).pipe( | 455 | ).pipe( |
| 464 | HttpRouter.toHttpEffect, | 456 | HttpRouter.toHttpEffect, |
| 465 | Effect.provideService(HttpRouter.RouterConfig, RELAY_HTTP_ROUTER_CONFIG), | 457 | Effect.provideService(HttpRouter.RouterConfig, RELAY_HTTP_ROUTER_CONFIG), |
| 466 | withoutCapturedParentSpan, | 458 | withoutCapturedParentSpan, |
| 467 | Effect.flatMap((httpEffect) => traceRelayHttpRequestWith(httpEffect, relayTraceLayer)), | 459 | Effect.flatMap((httpEffect) => traceRelayHttpRequestWith(httpEffect, layerRelayTrace)), |
| 468 | ); | 460 | ); |
| 469 | 461 | ||
| 470 | return { fetch }; | 462 | return { fetch }; |
| ... | @@ -478,7 +470,7 @@ export const ApiLive = Api.make( | ... | @@ -478,7 +470,7 @@ export const ApiLive = Api.make( |
| 478 | Layer.provideMerge(Cloudflare.Tunnel.ReadWriteTunnelBinding), | 470 | Layer.provideMerge(Cloudflare.Tunnel.ReadWriteTunnelBinding), |
| 479 | Layer.provideMerge(Cloudflare.DNS.ReadWriteDnsHttp), | 471 | Layer.provideMerge(Cloudflare.DNS.ReadWriteDnsHttp), |
| 480 | Layer.provideMerge(Cloudflare.Workers.RateLimitBinding), | 472 | Layer.provideMerge(Cloudflare.Workers.RateLimitBinding), |
| 481 | Layer.provideMerge(HookInboxObjectLive), | 473 | Layer.provideMerge(HookInboxObjectLayer.layer), |
| 482 | // The worker runtime opens its own HTTP span around ours. For webhook | 474 | // The worker runtime opens its own HTTP span around ours. For webhook |
| 483 | // paths it would record the raw URL, token included, and adopt the | 475 | // paths it would record the raw URL, token included, and adopt the |
| 484 | // sender's traceparent, so only our redacted span covers those. | 476 | // sender's traceparent, so only our redacted span covers those. |
| ... | @@ -509,4 +501,4 @@ export const ApiLive = Api.make( | ... | @@ -509,4 +501,4 @@ export const ApiLive = Api.make( |
| 509 | ), | 501 | ), |
| 510 | ); | 502 | ); |
| 511 | 503 | ||
| 512 | export default ApiLive; | 504 | export default layer; |
packages/client-runtime/src/authorization/layer.test.ts+2-2| ... | @@ -16,7 +16,7 @@ import * as TestClock from "effect/testing/TestClock"; | ... | @@ -16,7 +16,7 @@ import * as TestClock from "effect/testing/TestClock"; |
| 16 | 16 | ||
| 17 | import { DPOP_UNKNOWN_HINT } from "../relay/errorPresentation.ts"; | 17 | import { DPOP_UNKNOWN_HINT } from "../relay/errorPresentation.ts"; |
| 18 | import * as ManagedRelay from "../relay/managedRelay.ts"; | 18 | import * as ManagedRelay from "../relay/managedRelay.ts"; |
| 19 | import { remoteHttpClientLayer } from "../rpc/http.ts"; | 19 | import * as RpcHttp from "../rpc/http.ts"; |
| 20 | import * as ClientCapabilities from "../platform/capabilities.ts"; | 20 | import * as ClientCapabilities from "../platform/capabilities.ts"; |
| 21 | import * as RemoteEnvironmentAuthorization from "./service.ts"; | 21 | import * as RemoteEnvironmentAuthorization from "./service.ts"; |
| 22 | import * as TokenStore from "./tokenStore.ts"; | 22 | import * as TokenStore from "./tokenStore.ts"; |
| ... | @@ -191,7 +191,7 @@ const makeHarness = Effect.fn("TestRemoteAuthorization.makeHarness")(function* ( | ... | @@ -191,7 +191,7 @@ const makeHarness = Effect.fn("TestRemoteAuthorization.makeHarness")(function* ( |
| 191 | const layer = RemoteEnvironmentAuthorization.layer.pipe( | 191 | const layer = RemoteEnvironmentAuthorization.layer.pipe( |
| 192 | Layer.provide( | 192 | Layer.provide( |
| 193 | Layer.mergeAll( | 193 | Layer.mergeAll( |
| 194 | remoteHttpClientLayer(fetch.fetchFn), | 194 | RpcHttp.layerRemoteHttpClient(fetch.fetchFn), |
| 195 | Layer.succeed(ManagedRelay.ManagedRelayDpopSigner, signer), | 195 | Layer.succeed(ManagedRelay.ManagedRelayDpopSigner, signer), |
| 196 | Layer.succeed(ManagedRelay.ManagedRelayClient, relay), | 196 | Layer.succeed(ManagedRelay.ManagedRelayClient, relay), |
| 197 | Layer.succeed(ClientCapabilities.CloudSession, { | 197 | Layer.succeed(ClientCapabilities.CloudSession, { |
packages/client-runtime/src/authorization/remote.test.ts+3-2| ... | @@ -18,7 +18,7 @@ import { | ... | @@ -18,7 +18,7 @@ import { |
| 18 | resolveRemoteWebSocketConnectionUrl, | 18 | resolveRemoteWebSocketConnectionUrl, |
| 19 | } from "./remote.ts"; | 19 | } from "./remote.ts"; |
| 20 | import { fetchRemoteEnvironmentDescriptor } from "../environment/descriptor.ts"; | 20 | import { fetchRemoteEnvironmentDescriptor } from "../environment/descriptor.ts"; |
| 21 | import { remoteHttpClientLayer } from "../rpc/http.ts"; | 21 | import * as RpcHttp from "../rpc/http.ts"; |
| 22 | 22 | ||
| 23 | const isEnvironmentAuthInvalidError = Schema.is(EnvironmentAuthInvalidError); | 23 | const isEnvironmentAuthInvalidError = Schema.is(EnvironmentAuthInvalidError); |
| 24 | 24 | ||
| ... | @@ -49,7 +49,8 @@ const hangingFetch = () => { | ... | @@ -49,7 +49,8 @@ const hangingFetch = () => { |
| 49 | return { fetchFn, calls }; | 49 | return { fetchFn, calls }; |
| 50 | }; | 50 | }; |
| 51 | 51 | ||
| 52 | const provideRemoteHttp = (fetchFn: typeof fetch) => Effect.provide(remoteHttpClientLayer(fetchFn)); | 52 | const provideRemoteHttp = (fetchFn: typeof fetch) => |
| 53 | Effect.provide(RpcHttp.layerRemoteHttpClient(fetchFn)); | ||
| 53 | 54 | ||
| 54 | const expectFetchCall = ( | 55 | const expectFetchCall = ( |
| 55 | calls: ReadonlyArray<FetchCall>, | 56 | calls: ReadonlyArray<FetchCall>, |
packages/client-runtime/src/connection/layer.ts+9-9| ... | @@ -73,19 +73,19 @@ export const watchDiscoveredCompatibility = Effect.fn("connection.watchDiscovere | ... | @@ -73,19 +73,19 @@ export const watchDiscoveredCompatibility = Effect.fn("connection.watchDiscovere |
| 73 | ); | 73 | ); |
| 74 | 74 | ||
| 75 | export function layerWithOptions(options: RpcSession.RpcSessionOptions) { | 75 | export function layerWithOptions(options: RpcSession.RpcSessionOptions) { |
| 76 | const driverLayer = ConnectionDriver.layer.pipe( | 76 | const layerDriver = ConnectionDriver.layer.pipe( |
| 77 | Layer.provide(Layer.mergeAll(ConnectionResolver.layer, RpcSession.layer(options))), | 77 | Layer.provide(Layer.mergeAll(ConnectionResolver.layer, RpcSession.layer(options))), |
| 78 | ); | 78 | ); |
| 79 | const registryLayer = EnvironmentRegistry.layer.pipe(Layer.provide(driverLayer)); | 79 | const layerRegistry = EnvironmentRegistry.layer.pipe(Layer.provide(layerDriver)); |
| 80 | const onboardingLayer = ConnectionOnboarding.layer.pipe(Layer.provide(registryLayer)); | 80 | const layerOnboarding = ConnectionOnboarding.layer.pipe(Layer.provide(layerRegistry)); |
| 81 | const connectionServicesLayer = Layer.mergeAll( | 81 | const layerConnectionServices = Layer.mergeAll( |
| 82 | registryLayer, | 82 | layerRegistry, |
| 83 | RelayEnvironmentDiscovery.layer, | 83 | RelayEnvironmentDiscovery.layer, |
| 84 | onboardingLayer, | 84 | layerOnboarding, |
| 85 | // Exposed for updating hosts too old to connect through the driver. | 85 | // Exposed for updating hosts too old to connect through the driver. |
| 86 | ConnectionResolver.layer, | 86 | ConnectionResolver.layer, |
| 87 | ); | 87 | ); |
| 88 | const connectionStartupLayer = Layer.effectDiscard( | 88 | const layerConnectionStartup = Layer.effectDiscard( |
| 89 | Effect.gen(function* () { | 89 | Effect.gen(function* () { |
| 90 | const registry = yield* EnvironmentRegistry.EnvironmentRegistry; | 90 | const registry = yield* EnvironmentRegistry.EnvironmentRegistry; |
| 91 | const platformSource = yield* PlatformConnectionSource.PlatformConnectionSource; | 91 | const platformSource = yield* PlatformConnectionSource.PlatformConnectionSource; |
| ... | @@ -97,8 +97,8 @@ export function layerWithOptions(options: RpcSession.RpcSessionOptions) { | ... | @@ -97,8 +97,8 @@ export function layerWithOptions(options: RpcSession.RpcSessionOptions) { |
| 97 | ); | 97 | ); |
| 98 | }).pipe(Effect.withSpan("clientRuntime.connection.application.start")), | 98 | }).pipe(Effect.withSpan("clientRuntime.connection.application.start")), |
| 99 | ); | 99 | ); |
| 100 | return connectionStartupLayer.pipe( | 100 | return layerConnectionStartup.pipe( |
| 101 | Layer.provideMerge(connectionServicesLayer), | 101 | Layer.provideMerge(layerConnectionServices), |
| 102 | Layer.provideMerge(RemoteEnvironmentAuthorization.layer), | 102 | Layer.provideMerge(RemoteEnvironmentAuthorization.layer), |
| 103 | ); | 103 | ); |
| 104 | } | 104 | } |
packages/client-runtime/src/connection/onboarding.test.ts+15-15| ... | @@ -8,7 +8,7 @@ import * as Effect from "effect/Effect"; | ... | @@ -8,7 +8,7 @@ import * as Effect from "effect/Effect"; |
| 8 | import * as Layer from "effect/Layer"; | 8 | import * as Layer from "effect/Layer"; |
| 9 | import * as Option from "effect/Option"; | 9 | import * as Option from "effect/Option"; |
| 10 | 10 | ||
| 11 | import { remoteHttpClientLayer } from "../rpc/http.ts"; | 11 | import * as RpcHttp from "../rpc/http.ts"; |
| 12 | import * as ClientCapabilities from "../platform/capabilities.ts"; | 12 | import * as ClientCapabilities from "../platform/capabilities.ts"; |
| 13 | import { BearerConnectionCredential, BearerConnectionProfile } from "./catalog.ts"; | 13 | import { BearerConnectionCredential, BearerConnectionProfile } from "./catalog.ts"; |
| 14 | import { BearerConnectionTarget } from "./model.ts"; | 14 | import { BearerConnectionTarget } from "./model.ts"; |
| ... | @@ -18,7 +18,7 @@ import { | ... | @@ -18,7 +18,7 @@ import { |
| 18 | prepareSshRegistration, | 18 | prepareSshRegistration, |
| 19 | } from "./onboarding.ts"; | 19 | } from "./onboarding.ts"; |
| 20 | 20 | ||
| 21 | const CLIENT_PRESENTATION_LAYER = Layer.succeed( | 21 | const layerClientPresentation = Layer.succeed( |
| 22 | ClientCapabilities.ClientPresentation, | 22 | ClientCapabilities.ClientPresentation, |
| 23 | ClientCapabilities.ClientPresentation.of({ | 23 | ClientCapabilities.ClientPresentation.of({ |
| 24 | metadata: { | 24 | metadata: { |
| ... | @@ -30,7 +30,7 @@ const CLIENT_PRESENTATION_LAYER = Layer.succeed( | ... | @@ -30,7 +30,7 @@ const CLIENT_PRESENTATION_LAYER = Layer.succeed( |
| 30 | }), | 30 | }), |
| 31 | ); | 31 | ); |
| 32 | 32 | ||
| 33 | function pairingHttpLayer( | 33 | function layerPairingHttp( |
| 34 | calls: Array<{ readonly url: string; readonly init: RequestInit }>, | 34 | calls: Array<{ readonly url: string; readonly init: RequestInit }>, |
| 35 | options?: { | 35 | options?: { |
| 36 | readonly failDescriptor?: boolean; | 36 | readonly failDescriptor?: boolean; |
| ... | @@ -81,7 +81,7 @@ function pairingHttpLayer( | ... | @@ -81,7 +81,7 @@ function pairingHttpLayer( |
| 81 | return Promise.reject(new Error(`Unexpected request: ${url}`)); | 81 | return Promise.reject(new Error(`Unexpected request: ${url}`)); |
| 82 | }) satisfies typeof fetch; | 82 | }) satisfies typeof fetch; |
| 83 | 83 | ||
| 84 | return remoteHttpClientLayer(fetchFn); | 84 | return RpcHttp.layerRemoteHttpClient(fetchFn); |
| 85 | } | 85 | } |
| 86 | 86 | ||
| 87 | describe("connection onboarding", () => { | 87 | describe("connection onboarding", () => { |
| ... | @@ -91,7 +91,7 @@ describe("connection onboarding", () => { | ... | @@ -91,7 +91,7 @@ describe("connection onboarding", () => { |
| 91 | const registration = yield* preparePairingRegistration({ | 91 | const registration = yield* preparePairingRegistration({ |
| 92 | host: "remote.example.test", | 92 | host: "remote.example.test", |
| 93 | pairingCode: "pairing-token", | 93 | pairingCode: "pairing-token", |
| 94 | }).pipe(Effect.provide(Layer.mergeAll(CLIENT_PRESENTATION_LAYER, pairingHttpLayer(calls)))); | 94 | }).pipe(Effect.provide(Layer.mergeAll(layerClientPresentation, layerPairingHttp(calls)))); |
| 95 | 95 | ||
| 96 | expect(registration).toMatchObject({ | 96 | expect(registration).toMatchObject({ |
| 97 | _tag: "BearerConnectionRegistration", | 97 | _tag: "BearerConnectionRegistration", |
| ... | @@ -137,8 +137,8 @@ describe("connection onboarding", () => { | ... | @@ -137,8 +137,8 @@ describe("connection onboarding", () => { |
| 137 | }).pipe( | 137 | }).pipe( |
| 138 | Effect.provide( | 138 | Effect.provide( |
| 139 | Layer.mergeAll( | 139 | Layer.mergeAll( |
| 140 | CLIENT_PRESENTATION_LAYER, | 140 | layerClientPresentation, |
| 141 | pairingHttpLayer(calls, { protocolVersion: ORCHESTRATION_PROTOCOL_VERSION + 1 }), | 141 | layerPairingHttp(calls, { protocolVersion: ORCHESTRATION_PROTOCOL_VERSION + 1 }), |
| 142 | ), | 142 | ), |
| 143 | ), | 143 | ), |
| 144 | Effect.flip, | 144 | Effect.flip, |
| ... | @@ -158,7 +158,7 @@ describe("connection onboarding", () => { | ... | @@ -158,7 +158,7 @@ describe("connection onboarding", () => { |
| 158 | pairingCode: "pairing-token", | 158 | pairingCode: "pairing-token", |
| 159 | expectedEnvironmentId: EnvironmentId.make("some-other-machine"), | 159 | expectedEnvironmentId: EnvironmentId.make("some-other-machine"), |
| 160 | }).pipe( | 160 | }).pipe( |
| 161 | Effect.provide(Layer.mergeAll(CLIENT_PRESENTATION_LAYER, pairingHttpLayer(calls))), | 161 | Effect.provide(Layer.mergeAll(layerClientPresentation, layerPairingHttp(calls))), |
| 162 | Effect.flip, | 162 | Effect.flip, |
| 163 | ); | 163 | ); |
| 164 | expect(error).toMatchObject({ reason: "configuration" }); | 164 | expect(error).toMatchObject({ reason: "configuration" }); |
| ... | @@ -178,8 +178,8 @@ describe("connection onboarding", () => { | ... | @@ -178,8 +178,8 @@ describe("connection onboarding", () => { |
| 178 | }).pipe( | 178 | }).pipe( |
| 179 | Effect.provide( | 179 | Effect.provide( |
| 180 | Layer.mergeAll( | 180 | Layer.mergeAll( |
| 181 | CLIENT_PRESENTATION_LAYER, | 181 | layerClientPresentation, |
| 182 | pairingHttpLayer(calls, { | 182 | layerPairingHttp(calls, { |
| 183 | protocolVersion: ORCHESTRATION_PROTOCOL_VERSION - 1, | 183 | protocolVersion: ORCHESTRATION_PROTOCOL_VERSION - 1, |
| 184 | selfUpdate: true, | 184 | selfUpdate: true, |
| 185 | }), | 185 | }), |
| ... | @@ -200,8 +200,8 @@ describe("connection onboarding", () => { | ... | @@ -200,8 +200,8 @@ describe("connection onboarding", () => { |
| 200 | }).pipe( | 200 | }).pipe( |
| 201 | Effect.provide( | 201 | Effect.provide( |
| 202 | Layer.mergeAll( | 202 | Layer.mergeAll( |
| 203 | CLIENT_PRESENTATION_LAYER, | 203 | layerClientPresentation, |
| 204 | pairingHttpLayer(calls, { protocolVersion: ORCHESTRATION_PROTOCOL_VERSION - 1 }), | 204 | layerPairingHttp(calls, { protocolVersion: ORCHESTRATION_PROTOCOL_VERSION - 1 }), |
| 205 | ), | 205 | ), |
| 206 | ), | 206 | ), |
| 207 | Effect.flip, | 207 | Effect.flip, |
| ... | @@ -224,8 +224,8 @@ describe("connection onboarding", () => { | ... | @@ -224,8 +224,8 @@ describe("connection onboarding", () => { |
| 224 | }).pipe( | 224 | }).pipe( |
| 225 | Effect.provide( | 225 | Effect.provide( |
| 226 | Layer.mergeAll( | 226 | Layer.mergeAll( |
| 227 | CLIENT_PRESENTATION_LAYER, | 227 | layerClientPresentation, |
| 228 | pairingHttpLayer(calls, { failDescriptor: true }), | 228 | layerPairingHttp(calls, { failDescriptor: true }), |
| 229 | ), | 229 | ), |
| 230 | ), | 230 | ), |
| 231 | Effect.flip, | 231 | Effect.flip, |
| ... | @@ -244,7 +244,7 @@ describe("connection onboarding", () => { | ... | @@ -244,7 +244,7 @@ describe("connection onboarding", () => { |
| 244 | host: "", | 244 | host: "", |
| 245 | pairingCode: "", | 245 | pairingCode: "", |
| 246 | }).pipe( | 246 | }).pipe( |
| 247 | Effect.provide(Layer.mergeAll(CLIENT_PRESENTATION_LAYER, pairingHttpLayer(calls))), | 247 | Effect.provide(Layer.mergeAll(layerClientPresentation, layerPairingHttp(calls))), |
| 248 | Effect.flip, | 248 | Effect.flip, |
| 249 | ); | 249 | ); |
| 250 | 250 |
packages/client-runtime/src/connection/registry.test.ts+2-2| ... | @@ -420,7 +420,7 @@ const makeHarness = Effect.fn("TestEnvironmentRegistry.makeHarness")(function* ( | ... | @@ -420,7 +420,7 @@ const makeHarness = Effect.fn("TestEnvironmentRegistry.makeHarness")(function* ( |
| 420 | checkRoute(route).pipe(Effect.map((check) => check === "answered")), | 420 | checkRoute(route).pipe(Effect.map((check) => check === "answered")), |
| 421 | }); | 421 | }); |
| 422 | 422 | ||
| 423 | const cacheLayer = Layer.succeed(Persistence.EnvironmentCacheStore, cacheStore); | 423 | const layerCache = Layer.succeed(Persistence.EnvironmentCacheStore, cacheStore); |
| 424 | const layer = EnvironmentRegistry.layer.pipe( | 424 | const layer = EnvironmentRegistry.layer.pipe( |
| 425 | Layer.provide( | 425 | Layer.provide( |
| 426 | Layer.mergeAll( | 426 | Layer.mergeAll( |
| ... | @@ -436,7 +436,7 @@ const makeHarness = Effect.fn("TestEnvironmentRegistry.makeHarness")(function* ( | ... | @@ -436,7 +436,7 @@ const makeHarness = Effect.fn("TestEnvironmentRegistry.makeHarness")(function* ( |
| 436 | ConnectionWakeups.ConnectionWakeups.of({ changes: Stream.never }), | 436 | ConnectionWakeups.ConnectionWakeups.of({ changes: Stream.never }), |
| 437 | ), | 437 | ), |
| 438 | Layer.succeed(ConnectionDriver.ConnectionDriver, driver), | 438 | Layer.succeed(ConnectionDriver.ConnectionDriver, driver), |
| 439 | cacheLayer, | 439 | layerCache, |
| 440 | Layer.succeed(Persistence.EnvironmentOwnedDataCleanup, ownedDataCleanup), | 440 | Layer.succeed(Persistence.EnvironmentOwnedDataCleanup, ownedDataCleanup), |
| 441 | ), | 441 | ), |
| 442 | ), | 442 | ), |
packages/client-runtime/src/connection/resolver.test.ts+24-24| ... | @@ -32,7 +32,7 @@ import { | ... | @@ -32,7 +32,7 @@ import { |
| 32 | type ConnectionTarget, | 32 | type ConnectionTarget, |
| 33 | } from "./model.ts"; | 33 | } from "./model.ts"; |
| 34 | import * as ConnectionProfileStore from "./profileStore.ts"; | 34 | import * as ConnectionProfileStore from "./profileStore.ts"; |
| 35 | import { remoteHttpClientLayer } from "../rpc/http.ts"; | 35 | import * as RpcHttp from "../rpc/http.ts"; |
| 36 | import { | 36 | import { |
| 37 | GitHubRoutingPermissions, | 37 | GitHubRoutingPermissions, |
| 38 | gitHubRoutingConnectionKey, | 38 | gitHubRoutingConnectionKey, |
| ... | @@ -145,8 +145,8 @@ const makeDependencies = Effect.fn("TestConnectionResolver.makeDependencies")((o | ... | @@ -145,8 +145,8 @@ const makeDependencies = Effect.fn("TestConnectionResolver.makeDependencies")((o |
| 145 | disconnect: () => Effect.void, | 145 | disconnect: () => Effect.void, |
| 146 | }); | 146 | }); |
| 147 | 147 | ||
| 148 | const dependencies = Layer.mergeAll( | 148 | const layerDependencies = Layer.mergeAll( |
| 149 | remoteHttpClientLayer((() => | 149 | RpcHttp.layerRemoteHttpClient((() => |
| 150 | Promise.resolve( | 150 | Promise.resolve( |
| 151 | Response.json({ | 151 | Response.json({ |
| 152 | environmentId: ENVIRONMENT_ID, | 152 | environmentId: ENVIRONMENT_ID, |
| ... | @@ -183,14 +183,14 @@ const makeDependencies = Effect.fn("TestConnectionResolver.makeDependencies")((o | ... | @@ -183,14 +183,14 @@ const makeDependencies = Effect.fn("TestConnectionResolver.makeDependencies")((o |
| 183 | Layer.succeed(ClientCapabilities.SshEnvironmentGateway, ssh), | 183 | Layer.succeed(ClientCapabilities.SshEnvironmentGateway, ssh), |
| 184 | ); | 184 | ); |
| 185 | 185 | ||
| 186 | return Effect.succeed(ConnectionResolver.layer.pipe(Layer.provide(dependencies))); | 186 | return Effect.succeed(ConnectionResolver.layer.pipe(Layer.provide(layerDependencies))); |
| 187 | }); | 187 | }); |
| 188 | 188 | ||
| 189 | describe("ConnectionResolver", () => { | 189 | describe("ConnectionResolver", () => { |
| 190 | it.effect("blocks an old host during discovery before opening orchestration RPC", () => | 190 | it.effect("blocks an old host during discovery before opening orchestration RPC", () => |
| 191 | Effect.gen(function* () { | 191 | Effect.gen(function* () { |
| 192 | const brokerLayer = yield* makeDependencies({ descriptorProtocolVersion: null }); | 192 | const layerBroker = yield* makeDependencies({ descriptorProtocolVersion: null }); |
| 193 | const broker = yield* ConnectionResolver.ConnectionResolver.pipe(Effect.provide(brokerLayer)); | 193 | const broker = yield* ConnectionResolver.ConnectionResolver.pipe(Effect.provide(layerBroker)); |
| 194 | const target = new PrimaryConnectionTarget({ | 194 | const target = new PrimaryConnectionTarget({ |
| 195 | environmentId: ENVIRONMENT_ID, | 195 | environmentId: ENVIRONMENT_ID, |
| 196 | label: "Primary", | 196 | label: "Primary", |
| ... | @@ -207,10 +207,10 @@ describe("ConnectionResolver", () => { | ... | @@ -207,10 +207,10 @@ describe("ConnectionResolver", () => { |
| 207 | 207 | ||
| 208 | it.effect("blocks an incompatible host during discovery before opening orchestration RPC", () => | 208 | it.effect("blocks an incompatible host during discovery before opening orchestration RPC", () => |
| 209 | Effect.gen(function* () { | 209 | Effect.gen(function* () { |
| 210 | const brokerLayer = yield* makeDependencies({ | 210 | const layerBroker = yield* makeDependencies({ |
| 211 | descriptorProtocolVersion: ORCHESTRATION_PROTOCOL_VERSION + 1, | 211 | descriptorProtocolVersion: ORCHESTRATION_PROTOCOL_VERSION + 1, |
| 212 | }); | 212 | }); |
| 213 | const broker = yield* ConnectionResolver.ConnectionResolver.pipe(Effect.provide(brokerLayer)); | 213 | const broker = yield* ConnectionResolver.ConnectionResolver.pipe(Effect.provide(layerBroker)); |
| 214 | const target = new PrimaryConnectionTarget({ | 214 | const target = new PrimaryConnectionTarget({ |
| 215 | environmentId: ENVIRONMENT_ID, | 215 | environmentId: ENVIRONMENT_ID, |
| 216 | label: "Primary", | 216 | label: "Primary", |
| ... | @@ -227,8 +227,8 @@ describe("ConnectionResolver", () => { | ... | @@ -227,8 +227,8 @@ describe("ConnectionResolver", () => { |
| 227 | 227 | ||
| 228 | it.effect("prepares a primary environment without remote capabilities", () => | 228 | it.effect("prepares a primary environment without remote capabilities", () => |
| 229 | Effect.gen(function* () { | 229 | Effect.gen(function* () { |
| 230 | const brokerLayer = yield* makeDependencies(); | 230 | const layerBroker = yield* makeDependencies(); |
| 231 | const broker = yield* ConnectionResolver.ConnectionResolver.pipe(Effect.provide(brokerLayer)); | 231 | const broker = yield* ConnectionResolver.ConnectionResolver.pipe(Effect.provide(layerBroker)); |
| 232 | const target = new PrimaryConnectionTarget({ | 232 | const target = new PrimaryConnectionTarget({ |
| 233 | environmentId: ENVIRONMENT_ID, | 233 | environmentId: ENVIRONMENT_ID, |
| 234 | label: "Primary", | 234 | label: "Primary", |
| ... | @@ -251,7 +251,7 @@ describe("ConnectionResolver", () => { | ... | @@ -251,7 +251,7 @@ describe("ConnectionResolver", () => { |
| 251 | it.effect("authorizes a desktop primary environment with its platform bearer token", () => | 251 | it.effect("authorizes a desktop primary environment with its platform bearer token", () => |
| 252 | Effect.gen(function* () { | 252 | Effect.gen(function* () { |
| 253 | const bearerInputs = yield* Ref.make<ReadonlyArray<{ token: string; method: string }>>([]); | 253 | const bearerInputs = yield* Ref.make<ReadonlyArray<{ token: string; method: string }>>([]); |
| 254 | const brokerLayer = yield* makeDependencies({ | 254 | const layerBroker = yield* makeDependencies({ |
| 255 | primaryBearerToken: "desktop-bearer", | 255 | primaryBearerToken: "desktop-bearer", |
| 256 | authorizeBearer: (input) => | 256 | authorizeBearer: (input) => |
| 257 | Ref.update(bearerInputs, (values) => [ | 257 | Ref.update(bearerInputs, (values) => [ |
| ... | @@ -270,7 +270,7 @@ describe("ConnectionResolver", () => { | ... | @@ -270,7 +270,7 @@ describe("ConnectionResolver", () => { |
| 270 | }), | 270 | }), |
| 271 | ), | 271 | ), |
| 272 | }); | 272 | }); |
| 273 | const broker = yield* ConnectionResolver.ConnectionResolver.pipe(Effect.provide(brokerLayer)); | 273 | const broker = yield* ConnectionResolver.ConnectionResolver.pipe(Effect.provide(layerBroker)); |
| 274 | const target = new PrimaryConnectionTarget({ | 274 | const target = new PrimaryConnectionTarget({ |
| 275 | environmentId: ENVIRONMENT_ID, | 275 | environmentId: ENVIRONMENT_ID, |
| 276 | label: "Primary", | 276 | label: "Primary", |
| ... | @@ -302,7 +302,7 @@ describe("ConnectionResolver", () => { | ... | @@ -302,7 +302,7 @@ describe("ConnectionResolver", () => { |
| 302 | httpBaseUrl: ENDPOINT.httpBaseUrl, | 302 | httpBaseUrl: ENDPOINT.httpBaseUrl, |
| 303 | wsBaseUrl: ENDPOINT.wsBaseUrl, | 303 | wsBaseUrl: ENDPOINT.wsBaseUrl, |
| 304 | }); | 304 | }); |
| 305 | const brokerLayer = yield* makeDependencies({ | 305 | const layerBroker = yield* makeDependencies({ |
| 306 | credentials: [["saved-1", new BearerConnectionCredential({ token: "secret-bearer" })]], | 306 | credentials: [["saved-1", new BearerConnectionCredential({ token: "secret-bearer" })]], |
| 307 | authorizeBearer: (input) => | 307 | authorizeBearer: (input) => |
| 308 | Ref.update(bearerInputs, (values) => [ | 308 | Ref.update(bearerInputs, (values) => [ |
| ... | @@ -321,7 +321,7 @@ describe("ConnectionResolver", () => { | ... | @@ -321,7 +321,7 @@ describe("ConnectionResolver", () => { |
| 321 | }), | 321 | }), |
| 322 | ), | 322 | ), |
| 323 | }); | 323 | }); |
| 324 | const broker = yield* ConnectionResolver.ConnectionResolver.pipe(Effect.provide(brokerLayer)); | 324 | const broker = yield* ConnectionResolver.ConnectionResolver.pipe(Effect.provide(layerBroker)); |
| 325 | 325 | ||
| 326 | expect( | 326 | expect( |
| 327 | (yield* broker.prepare(catalogEntry(target, Option.some(profile)))).socketUrl, | 327 | (yield* broker.prepare(catalogEntry(target, Option.some(profile)))).socketUrl, |
| ... | @@ -336,8 +336,8 @@ describe("ConnectionResolver", () => { | ... | @@ -336,8 +336,8 @@ describe("ConnectionResolver", () => { |
| 336 | environmentId: ENVIRONMENT_ID, | 336 | environmentId: ENVIRONMENT_ID, |
| 337 | label: "Cloud", | 337 | label: "Cloud", |
| 338 | }); | 338 | }); |
| 339 | const brokerLayer = yield* makeDependencies(); | 339 | const layerBroker = yield* makeDependencies(); |
| 340 | const broker = yield* ConnectionResolver.ConnectionResolver.pipe(Effect.provide(brokerLayer)); | 340 | const broker = yield* ConnectionResolver.ConnectionResolver.pipe(Effect.provide(layerBroker)); |
| 341 | 341 | ||
| 342 | expect(yield* broker.prepare(catalogEntry(target))).toEqual({ | 342 | expect(yield* broker.prepare(catalogEntry(target))).toEqual({ |
| 343 | environmentId: ENVIRONMENT_ID, | 343 | environmentId: ENVIRONMENT_ID, |
| ... | @@ -362,7 +362,7 @@ describe("ConnectionResolver", () => { | ... | @@ -362,7 +362,7 @@ describe("ConnectionResolver", () => { |
| 362 | environmentId: ENVIRONMENT_ID, | 362 | environmentId: ENVIRONMENT_ID, |
| 363 | label: "Cloud", | 363 | label: "Cloud", |
| 364 | }); | 364 | }); |
| 365 | const brokerLayer = yield* makeDependencies({ | 365 | const layerBroker = yield* makeDependencies({ |
| 366 | authorizeDpop: (input) => | 366 | authorizeDpop: (input) => |
| 367 | Effect.succeed({ | 367 | Effect.succeed({ |
| 368 | environmentId: input.expectedEnvironmentId, | 368 | environmentId: input.expectedEnvironmentId, |
| ... | @@ -376,7 +376,7 @@ describe("ConnectionResolver", () => { | ... | @@ -376,7 +376,7 @@ describe("ConnectionResolver", () => { |
| 376 | }, | 376 | }, |
| 377 | }).pipe(Effect.withSpan("test.remote.authorizeDpop")), | 377 | }).pipe(Effect.withSpan("test.remote.authorizeDpop")), |
| 378 | }); | 378 | }); |
| 379 | const broker = yield* ConnectionResolver.ConnectionResolver.pipe(Effect.provide(brokerLayer)); | 379 | const broker = yield* ConnectionResolver.ConnectionResolver.pipe(Effect.provide(layerBroker)); |
| 380 | 380 | ||
| 381 | yield* broker | 381 | yield* broker |
| 382 | .prepare(catalogEntry(target)) | 382 | .prepare(catalogEntry(target)) |
| ... | @@ -407,7 +407,7 @@ describe("ConnectionResolver", () => { | ... | @@ -407,7 +407,7 @@ describe("ConnectionResolver", () => { |
| 407 | label: "SSH", | 407 | label: "SSH", |
| 408 | target: SSH_TARGET, | 408 | target: SSH_TARGET, |
| 409 | }); | 409 | }); |
| 410 | const brokerLayer = yield* makeDependencies({ | 410 | const layerBroker = yield* makeDependencies({ |
| 411 | prepareSsh: (input) => | 411 | prepareSsh: (input) => |
| 412 | Ref.update(preparedTargets, (values) => [...values, input.target]).pipe( | 412 | Ref.update(preparedTargets, (values) => [...values, input.target]).pipe( |
| 413 | Effect.as({ | 413 | Effect.as({ |
| ... | @@ -434,7 +434,7 @@ describe("ConnectionResolver", () => { | ... | @@ -434,7 +434,7 @@ describe("ConnectionResolver", () => { |
| 434 | }), | 434 | }), |
| 435 | ), | 435 | ), |
| 436 | }); | 436 | }); |
| 437 | const broker = yield* ConnectionResolver.ConnectionResolver.pipe(Effect.provide(brokerLayer)); | 437 | const broker = yield* ConnectionResolver.ConnectionResolver.pipe(Effect.provide(layerBroker)); |
| 438 | 438 | ||
| 439 | expect( | 439 | expect( |
| 440 | (yield* broker.prepare(catalogEntry(target, Option.some(profile)))).socketUrl, | 440 | (yield* broker.prepare(catalogEntry(target, Option.some(profile)))).socketUrl, |
| ... | @@ -485,7 +485,7 @@ describe("ConnectionResolver", () => { | ... | @@ -485,7 +485,7 @@ describe("ConnectionResolver", () => { |
| 485 | ), | 485 | ), |
| 486 | }); | 486 | }); |
| 487 | let savedProfile: ConnectionProfile = profile; | 487 | let savedProfile: ConnectionProfile = profile; |
| 488 | const brokerLayer = yield* makeDependencies({ | 488 | const layerBroker = yield* makeDependencies({ |
| 489 | profileStore: { | 489 | profileStore: { |
| 490 | get: () => Effect.sync(() => Option.some(savedProfile)), | 490 | get: () => Effect.sync(() => Option.some(savedProfile)), |
| 491 | put: (value) => | 491 | put: (value) => |
| ... | @@ -518,7 +518,7 @@ describe("ConnectionResolver", () => { | ... | @@ -518,7 +518,7 @@ describe("ConnectionResolver", () => { |
| 518 | }), | 518 | }), |
| 519 | }); | 519 | }); |
| 520 | const broker = yield* ConnectionResolver.ConnectionResolver.pipe( | 520 | const broker = yield* ConnectionResolver.ConnectionResolver.pipe( |
| 521 | Effect.provide(brokerLayer), | 521 | Effect.provide(layerBroker), |
| 522 | ); | 522 | ); |
| 523 | const prepare = broker | 523 | const prepare = broker |
| 524 | .prepare(entry) | 524 | .prepare(entry) |
| ... | @@ -551,10 +551,10 @@ describe("ConnectionResolver", () => { | ... | @@ -551,10 +551,10 @@ describe("ConnectionResolver", () => { |
| 551 | detail: "Relay environment connection timed out.", | 551 | detail: "Relay environment connection timed out.", |
| 552 | traceId: "relay-trace", | 552 | traceId: "relay-trace", |
| 553 | }); | 553 | }); |
| 554 | const brokerLayer = yield* makeDependencies({ | 554 | const layerBroker = yield* makeDependencies({ |
| 555 | authorizeDpop: () => Effect.fail(authorizationError), | 555 | authorizeDpop: () => Effect.fail(authorizationError), |
| 556 | }); | 556 | }); |
| 557 | const broker = yield* ConnectionResolver.ConnectionResolver.pipe(Effect.provide(brokerLayer)); | 557 | const broker = yield* ConnectionResolver.ConnectionResolver.pipe(Effect.provide(layerBroker)); |
| 558 | const error = yield* Effect.flip(broker.prepare(catalogEntry(target))); | 558 | const error = yield* Effect.flip(broker.prepare(catalogEntry(target))); |
| 559 | 559 | ||
| 560 | expect(error).toBe(authorizationError); | 560 | expect(error).toBe(authorizationError); |
packages/client-runtime/src/connection/supervisor.test.ts+6-6| ... | @@ -16,7 +16,7 @@ import * as RemoteEnvironmentAuthorization from "../authorization/service.ts"; | ... | @@ -16,7 +16,7 @@ import * as RemoteEnvironmentAuthorization from "../authorization/service.ts"; |
| 16 | import * as TokenStore from "../authorization/tokenStore.ts"; | 16 | import * as TokenStore from "../authorization/tokenStore.ts"; |
| 17 | import * as ClientCapabilities from "../platform/capabilities.ts"; | 17 | import * as ClientCapabilities from "../platform/capabilities.ts"; |
| 18 | import * as ManagedRelay from "../relay/managedRelay.ts"; | 18 | import * as ManagedRelay from "../relay/managedRelay.ts"; |
| 19 | import { remoteHttpClientLayer } from "../rpc/http.ts"; | 19 | import * as RpcHttp from "../rpc/http.ts"; |
| 20 | import type { WsRpcProtocolClient } from "../rpc/protocol.ts"; | 20 | import type { WsRpcProtocolClient } from "../rpc/protocol.ts"; |
| 21 | import { fetchEnvironmentSessionState } from "../state/session.ts"; | 21 | import { fetchEnvironmentSessionState } from "../state/session.ts"; |
| 22 | import type { ConnectionCatalogEntry, ConnectionRoute } from "./catalog.ts"; | 22 | import type { ConnectionCatalogEntry, ConnectionRoute } from "./catalog.ts"; |
| ... | @@ -233,7 +233,7 @@ const makeHarness = Effect.fn("TestConnectionHarness.make")(function* (options?: | ... | @@ -233,7 +233,7 @@ const makeHarness = Effect.fn("TestConnectionHarness.make")(function* (options?: |
| 233 | ); | 233 | ); |
| 234 | }); | 234 | }); |
| 235 | 235 | ||
| 236 | const dependencies = Layer.mergeAll( | 236 | const layerDependencies = Layer.mergeAll( |
| 237 | // Jitter at its maximum, so each retry waits exactly its ceiling: 2s, 4s, 8s... | 237 | // Jitter at its maximum, so each retry waits exactly its ceiling: 2s, 4s, 8s... |
| 238 | Layer.succeed(Random.Random, { | 238 | Layer.succeed(Random.Random, { |
| 239 | nextDoubleUnsafe: () => 1 - Number.EPSILON, | 239 | nextDoubleUnsafe: () => 1 - Number.EPSILON, |
| ... | @@ -261,7 +261,7 @@ const makeHarness = Effect.fn("TestConnectionHarness.make")(function* (options?: | ... | @@ -261,7 +261,7 @@ const makeHarness = Effect.fn("TestConnectionHarness.make")(function* (options?: |
| 261 | ); | 261 | ); |
| 262 | 262 | ||
| 263 | return { | 263 | return { |
| 264 | dependencies, | 264 | dependencies: layerDependencies, |
| 265 | prepareCount, | 265 | prepareCount, |
| 266 | sessionCount, | 266 | sessionCount, |
| 267 | releaseCount, | 267 | releaseCount, |
| ... | @@ -1551,11 +1551,11 @@ describe("EnvironmentSupervisor", () => { | ... | @@ -1551,11 +1551,11 @@ describe("EnvironmentSupervisor", () => { |
| 1551 | getAgentActivitySnapshot: unused, | 1551 | getAgentActivitySnapshot: unused, |
| 1552 | resetTokenCache: Effect.void, | 1552 | resetTokenCache: Effect.void, |
| 1553 | }); | 1553 | }); |
| 1554 | const httpLayer = remoteHttpClientLayer(fetchFn); | 1554 | const layerHttp = RpcHttp.layerRemoteHttpClient(fetchFn); |
| 1555 | const remoteAuthorization = yield* RemoteEnvironmentAuthorization.make.pipe( | 1555 | const remoteAuthorization = yield* RemoteEnvironmentAuthorization.make.pipe( |
| 1556 | Effect.provide( | 1556 | Effect.provide( |
| 1557 | Layer.mergeAll( | 1557 | Layer.mergeAll( |
| 1558 | httpLayer, | 1558 | layerHttp, |
| 1559 | Layer.succeed(ManagedRelay.ManagedRelayDpopSigner, signer), | 1559 | Layer.succeed(ManagedRelay.ManagedRelayDpopSigner, signer), |
| 1560 | Layer.succeed(ManagedRelay.ManagedRelayClient, relay), | 1560 | Layer.succeed(ManagedRelay.ManagedRelayClient, relay), |
| 1561 | Layer.succeed(ClientCapabilities.CloudSession, { | 1561 | Layer.succeed(ClientCapabilities.CloudSession, { |
| ... | @@ -1593,7 +1593,7 @@ describe("EnvironmentSupervisor", () => { | ... | @@ -1593,7 +1593,7 @@ describe("EnvironmentSupervisor", () => { |
| 1593 | prepared, | 1593 | prepared, |
| 1594 | signer: Option.some(signer), | 1594 | signer: Option.some(signer), |
| 1595 | remoteAuthorization: Option.some(remoteAuthorization), | 1595 | remoteAuthorization: Option.some(remoteAuthorization), |
| 1596 | }).pipe(Effect.provide(httpLayer)); | 1596 | }).pipe(Effect.provide(layerHttp)); |
| 1597 | 1597 | ||
| 1598 | yield* TestClock.adjust("2 hours"); | 1598 | yield* TestClock.adjust("2 hours"); |
| 1599 | expect((yield* readSession).authenticated).toBe(true); | 1599 | expect((yield* readSession).authenticated).toBe(true); |
packages/client-runtime/src/operations/commands.performance.test.ts+2-2| ... | @@ -146,7 +146,7 @@ const TARGET = new PrimaryConnectionTarget({ | ... | @@ -146,7 +146,7 @@ const TARGET = new PrimaryConnectionTarget({ |
| 146 | wsBaseUrl: "wss://environment.example.test", | 146 | wsBaseUrl: "wss://environment.example.test", |
| 147 | }); | 147 | }); |
| 148 | 148 | ||
| 149 | const TEST_CRYPTO_LAYER = Layer.succeed( | 149 | const layerTestCrypto = Layer.succeed( |
| 150 | Crypto.Crypto, | 150 | Crypto.Crypto, |
| 151 | Crypto.make({ | 151 | Crypto.make({ |
| 152 | randomBytes: (size) => new Uint8Array(size), | 152 | randomBytes: (size) => new Uint8Array(size), |
| ... | @@ -280,6 +280,6 @@ describe("routine command transport budget", () => { | ... | @@ -280,6 +280,6 @@ describe("routine command transport budget", () => { |
| 280 | }); | 280 | }); |
| 281 | expect(currentRpcJsonBytes).toBeLessThanOrEqual(MAX_CURRENT_SEND_RPC_JSON_BYTES); | 281 | expect(currentRpcJsonBytes).toBeLessThanOrEqual(MAX_CURRENT_SEND_RPC_JSON_BYTES); |
| 282 | expect(currentRpcJsonBytes / formerRpcJsonBytes).toBeLessThanOrEqual(0.000_06); | 282 | expect(currentRpcJsonBytes / formerRpcJsonBytes).toBeLessThanOrEqual(0.000_06); |
| 283 | }).pipe(Effect.provide(TEST_CRYPTO_LAYER)), | 283 | }).pipe(Effect.provide(layerTestCrypto)), |
| 284 | ); | 284 | ); |
| 285 | }); | 285 | }); |
packages/client-runtime/src/operations/commands.test.ts+22-22| ... | @@ -56,7 +56,7 @@ import { | ... | @@ -56,7 +56,7 @@ import { |
| 56 | updateThreadMetadata, | 56 | updateThreadMetadata, |
| 57 | } from "./commands.ts"; | 57 | } from "./commands.ts"; |
| 58 | 58 | ||
| 59 | const TEST_CRYPTO_LAYER = Layer.succeed( | 59 | const layerTestCrypto = Layer.succeed( |
| 60 | Crypto.Crypto, | 60 | Crypto.Crypto, |
| 61 | Crypto.make({ | 61 | Crypto.make({ |
| 62 | randomBytes: (size) => new Uint8Array(size), | 62 | randomBytes: (size) => new Uint8Array(size), |
| ... | @@ -170,7 +170,7 @@ describe("V2 environment commands", () => { | ... | @@ -170,7 +170,7 @@ describe("V2 environment commands", () => { |
| 170 | createWorkspaceRootIfMissing: true, | 170 | createWorkspaceRootIfMissing: true, |
| 171 | }, | 171 | }, |
| 172 | ]); | 172 | ]); |
| 173 | }).pipe(Effect.provide(TEST_CRYPTO_LAYER)), | 173 | }).pipe(Effect.provide(layerTestCrypto)), |
| 174 | ); | 174 | ); |
| 175 | 175 | ||
| 176 | it.effect("persists and clears project presentation and environment settings", () => | 176 | it.effect("persists and clears project presentation and environment settings", () => |
| ... | @@ -214,7 +214,7 @@ describe("V2 environment commands", () => { | ... | @@ -214,7 +214,7 @@ describe("V2 environment commands", () => { |
| 214 | defaultThreadEnvMode: null, | 214 | defaultThreadEnvMode: null, |
| 215 | }, | 215 | }, |
| 216 | ]); | 216 | ]); |
| 217 | }).pipe(Effect.provide(TEST_CRYPTO_LAYER)), | 217 | }).pipe(Effect.provide(layerTestCrypto)), |
| 218 | ); | 218 | ); |
| 219 | 219 | ||
| 220 | it.effect("preserves caller command ids for idempotent V2 commands", () => | 220 | it.effect("preserves caller command ids for idempotent V2 commands", () => |
| ... | @@ -230,7 +230,7 @@ describe("V2 environment commands", () => { | ... | @@ -230,7 +230,7 @@ describe("V2 environment commands", () => { |
| 230 | expect(commands).toEqual([ | 230 | expect(commands).toEqual([ |
| 231 | { type: "thread.archive", commandId: "queued-command", threadId: "thread-1" }, | 231 | { type: "thread.archive", commandId: "queued-command", threadId: "thread-1" }, |
| 232 | ]); | 232 | ]); |
| 233 | }).pipe(Effect.provide(TEST_CRYPTO_LAYER)), | 233 | }).pipe(Effect.provide(layerTestCrypto)), |
| 234 | ); | 234 | ); |
| 235 | 235 | ||
| 236 | it.effect("resolves run ordinal zero to the persisted thread-start checkpoint", () => | 236 | it.effect("resolves run ordinal zero to the persisted thread-start checkpoint", () => |
| ... | @@ -274,7 +274,7 @@ describe("V2 environment commands", () => { | ... | @@ -274,7 +274,7 @@ describe("V2 environment commands", () => { |
| 274 | checkpointId, | 274 | checkpointId, |
| 275 | }, | 275 | }, |
| 276 | ]); | 276 | ]); |
| 277 | }).pipe(Effect.provide(TEST_CRYPTO_LAYER)), | 277 | }).pipe(Effect.provide(layerTestCrypto)), |
| 278 | ); | 278 | ); |
| 279 | 279 | ||
| 280 | it.effect("preserves plan implementation provenance on V2 runs", () => | 280 | it.effect("preserves plan implementation provenance on V2 runs", () => |
| ... | @@ -314,7 +314,7 @@ describe("V2 environment commands", () => { | ... | @@ -314,7 +314,7 @@ describe("V2 environment commands", () => { |
| 314 | deliveryIntent: "auto", | 314 | deliveryIntent: "auto", |
| 315 | dispatchMode: { type: "start_immediately" }, | 315 | dispatchMode: { type: "start_immediately" }, |
| 316 | }); | 316 | }); |
| 317 | }).pipe(Effect.provide(TEST_CRYPTO_LAYER)), | 317 | }).pipe(Effect.provide(layerTestCrypto)), |
| 318 | ); | 318 | ); |
| 319 | 319 | ||
| 320 | it.effect("preserves an existing worktree and branch during first-message launch", () => | 320 | it.effect("preserves an existing worktree and branch during first-message launch", () => |
| ... | @@ -358,7 +358,7 @@ describe("V2 environment commands", () => { | ... | @@ -358,7 +358,7 @@ describe("V2 environment commands", () => { |
| 358 | branch: "feature", | 358 | branch: "feature", |
| 359 | }, | 359 | }, |
| 360 | }); | 360 | }); |
| 361 | }).pipe(Effect.provide(TEST_CRYPTO_LAYER)), | 361 | }).pipe(Effect.provide(layerTestCrypto)), |
| 362 | ); | 362 | ); |
| 363 | 363 | ||
| 364 | it.effect("provisions an origin-based worktree for an existing empty thread", () => | 364 | it.effect("provisions an origin-based worktree for an existing empty thread", () => |
| ... | @@ -398,7 +398,7 @@ describe("V2 environment commands", () => { | ... | @@ -398,7 +398,7 @@ describe("V2 environment commands", () => { |
| 398 | startFromOrigin: true, | 398 | startFromOrigin: true, |
| 399 | }, | 399 | }, |
| 400 | }); | 400 | }); |
| 401 | }).pipe(Effect.provide(TEST_CRYPTO_LAYER)), | 401 | }).pipe(Effect.provide(layerTestCrypto)), |
| 402 | ); | 402 | ); |
| 403 | 403 | ||
| 404 | it.effect("uses server-resolved delivery intent without fetching the full projection", () => | 404 | it.effect("uses server-resolved delivery intent without fetching the full projection", () => |
| ... | @@ -434,7 +434,7 @@ describe("V2 environment commands", () => { | ... | @@ -434,7 +434,7 @@ describe("V2 environment commands", () => { |
| 434 | }); | 434 | }); |
| 435 | } | 435 | } |
| 436 | expect(projectionRequests).toEqual([]); | 436 | expect(projectionRequests).toEqual([]); |
| 437 | }).pipe(Effect.provide(TEST_CRYPTO_LAYER)), | 437 | }).pipe(Effect.provide(layerTestCrypto)), |
| 438 | ); | 438 | ); |
| 439 | 439 | ||
| 440 | it.effect("retains projection-shaped delivery for servers without command context support", () => | 440 | it.effect("retains projection-shaped delivery for servers without command context support", () => |
| ... | @@ -499,7 +499,7 @@ describe("V2 environment commands", () => { | ... | @@ -499,7 +499,7 @@ describe("V2 environment commands", () => { |
| 499 | expect(commands.at(-1)).not.toHaveProperty("deliveryIntent"); | 499 | expect(commands.at(-1)).not.toHaveProperty("deliveryIntent"); |
| 500 | } | 500 | } |
| 501 | expect(projectionRequests).toEqual([v2ThreadId, v2ThreadId, v2ThreadId, v2ThreadId]); | 501 | expect(projectionRequests).toEqual([v2ThreadId, v2ThreadId, v2ThreadId, v2ThreadId]); |
| 502 | }).pipe(Effect.provide(TEST_CRYPTO_LAYER)), | 502 | }).pipe(Effect.provide(layerTestCrypto)), |
| 503 | ); | 503 | ); |
| 504 | 504 | ||
| 505 | it.effect("Stop with no run left ends the thread's pull request watches", () => | 505 | it.effect("Stop with no run left ends the thread's pull request watches", () => |
| ... | @@ -553,7 +553,7 @@ describe("V2 environment commands", () => { | ... | @@ -553,7 +553,7 @@ describe("V2 environment commands", () => { |
| 553 | watching: false, | 553 | watching: false, |
| 554 | }, | 554 | }, |
| 555 | ]); | 555 | ]); |
| 556 | }).pipe(Effect.provide(TEST_CRYPTO_LAYER)), | 556 | }).pipe(Effect.provide(layerTestCrypto)), |
| 557 | ); | 557 | ); |
| 558 | 558 | ||
| 559 | it.effect.each([ | 559 | it.effect.each([ |
| ... | @@ -629,7 +629,7 @@ describe("V2 environment commands", () => { | ... | @@ -629,7 +629,7 @@ describe("V2 environment commands", () => { |
| 629 | }, | 629 | }, |
| 630 | ], | 630 | ], |
| 631 | ); | 631 | ); |
| 632 | }).pipe(Effect.provide(TEST_CRYPTO_LAYER)), | 632 | }).pipe(Effect.provide(layerTestCrypto)), |
| 633 | ); | 633 | ); |
| 634 | 634 | ||
| 635 | it.effect( | 635 | it.effect( |
| ... | @@ -717,7 +717,7 @@ describe("V2 environment commands", () => { | ... | @@ -717,7 +717,7 @@ describe("V2 environment commands", () => { |
| 717 | ]); | 717 | ]); |
| 718 | // A text-only edit must not send an attachments replacement list. | 718 | // A text-only edit must not send an attachments replacement list. |
| 719 | expect(commands[5]).not.toHaveProperty("attachments"); | 719 | expect(commands[5]).not.toHaveProperty("attachments"); |
| 720 | }).pipe(Effect.provide(TEST_CRYPTO_LAYER)), | 720 | }).pipe(Effect.provide(layerTestCrypto)), |
| 721 | ); | 721 | ); |
| 722 | 722 | ||
| 723 | it.effect("delegates model selection to the server without fetching the full projection", () => | 723 | it.effect("delegates model selection to the server without fetching the full projection", () => |
| ... | @@ -759,7 +759,7 @@ describe("V2 environment commands", () => { | ... | @@ -759,7 +759,7 @@ describe("V2 environment commands", () => { |
| 759 | }, | 759 | }, |
| 760 | ]); | 760 | ]); |
| 761 | expect(projectionRequests).toEqual([]); | 761 | expect(projectionRequests).toEqual([]); |
| 762 | }).pipe(Effect.provide(TEST_CRYPTO_LAYER)), | 762 | }).pipe(Effect.provide(layerTestCrypto)), |
| 763 | ); | 763 | ); |
| 764 | 764 | ||
| 765 | it.effect("retains provider-switch shaping for servers without command context support", () => | 765 | it.effect("retains provider-switch shaping for servers without command context support", () => |
| ... | @@ -791,7 +791,7 @@ describe("V2 environment commands", () => { | ... | @@ -791,7 +791,7 @@ describe("V2 environment commands", () => { |
| 791 | modelSelection: { instanceId: "claude", model: "claude-sonnet-4-6" }, | 791 | modelSelection: { instanceId: "claude", model: "claude-sonnet-4-6" }, |
| 792 | }, | 792 | }, |
| 793 | ]); | 793 | ]); |
| 794 | }).pipe(Effect.provide(TEST_CRYPTO_LAYER)), | 794 | }).pipe(Effect.provide(layerTestCrypto)), |
| 795 | ); | 795 | ); |
| 796 | 796 | ||
| 797 | it.effect.each([true, false])( | 797 | it.effect.each([true, false])( |
| ... | @@ -821,7 +821,7 @@ describe("V2 environment commands", () => { | ... | @@ -821,7 +821,7 @@ describe("V2 environment commands", () => { |
| 821 | restoreFiles, | 821 | restoreFiles, |
| 822 | }, | 822 | }, |
| 823 | ]); | 823 | ]); |
| 824 | }).pipe(Effect.provide(TEST_CRYPTO_LAYER)), | 824 | }).pipe(Effect.provide(layerTestCrypto)), |
| 825 | ); | 825 | ); |
| 826 | 826 | ||
| 827 | it.effect("validates identified checkpoints locally for older servers", () => | 827 | it.effect("validates identified checkpoints locally for older servers", () => |
| ... | @@ -884,7 +884,7 @@ describe("V2 environment commands", () => { | ... | @@ -884,7 +884,7 @@ describe("V2 environment commands", () => { |
| 884 | } | 884 | } |
| 885 | expect(projectionRequests).toEqual([v2ThreadId]); | 885 | expect(projectionRequests).toEqual([v2ThreadId]); |
| 886 | } | 886 | } |
| 887 | }).pipe(Effect.provide(TEST_CRYPTO_LAYER)), | 887 | }).pipe(Effect.provide(layerTestCrypto)), |
| 888 | ); | 888 | ); |
| 889 | 889 | ||
| 890 | it.effect("dispatches settle and unsettle commands without timestamps", () => | 890 | it.effect("dispatches settle and unsettle commands without timestamps", () => |
| ... | @@ -915,7 +915,7 @@ describe("V2 environment commands", () => { | ... | @@ -915,7 +915,7 @@ describe("V2 environment commands", () => { |
| 915 | reason: "user", | 915 | reason: "user", |
| 916 | }, | 916 | }, |
| 917 | ]); | 917 | ]); |
| 918 | }).pipe(Effect.provide(TEST_CRYPTO_LAYER)), | 918 | }).pipe(Effect.provide(layerTestCrypto)), |
| 919 | ); | 919 | ); |
| 920 | 920 | ||
| 921 | it.effect("sends an active order key without changing activity timestamps", () => | 921 | it.effect("sends an active order key without changing activity timestamps", () => |
| ... | @@ -935,7 +935,7 @@ describe("V2 environment commands", () => { | ... | @@ -935,7 +935,7 @@ describe("V2 environment commands", () => { |
| 935 | orderKey: "mf", | 935 | orderKey: "mf", |
| 936 | }, | 936 | }, |
| 937 | ]); | 937 | ]); |
| 938 | }).pipe(Effect.provide(TEST_CRYPTO_LAYER)), | 938 | }).pipe(Effect.provide(layerTestCrypto)), |
| 939 | ); | 939 | ); |
| 940 | 940 | ||
| 941 | it.effect("dismisses a pending user-input request", () => | 941 | it.effect("dismisses a pending user-input request", () => |
| ... | @@ -957,7 +957,7 @@ describe("V2 environment commands", () => { | ... | @@ -957,7 +957,7 @@ describe("V2 environment commands", () => { |
| 957 | requestId: "request-1", | 957 | requestId: "request-1", |
| 958 | }, | 958 | }, |
| 959 | ]); | 959 | ]); |
| 960 | }).pipe(Effect.provide(TEST_CRYPTO_LAYER)), | 960 | }).pipe(Effect.provide(layerTestCrypto)), |
| 961 | ); | 961 | ); |
| 962 | 962 | ||
| 963 | it.effect("dispatches an explicit idle start without fetching the full projection", () => | 963 | it.effect("dispatches an explicit idle start without fetching the full projection", () => |
| ... | @@ -992,7 +992,7 @@ describe("V2 environment commands", () => { | ... | @@ -992,7 +992,7 @@ describe("V2 environment commands", () => { |
| 992 | dispatchMode: { type: "start_immediately" }, | 992 | dispatchMode: { type: "start_immediately" }, |
| 993 | }, | 993 | }, |
| 994 | ]); | 994 | ]); |
| 995 | }).pipe(Effect.provide(TEST_CRYPTO_LAYER)), | 995 | }).pipe(Effect.provide(layerTestCrypto)), |
| 996 | ); | 996 | ); |
| 997 | 997 | ||
| 998 | it.effect("interrupts a known run without fetching the full projection", () => | 998 | it.effect("interrupts a known run without fetching the full projection", () => |
| ... | @@ -1021,6 +1021,6 @@ describe("V2 environment commands", () => { | ... | @@ -1021,6 +1021,6 @@ describe("V2 environment commands", () => { |
| 1021 | holdQueue: true, | 1021 | holdQueue: true, |
| 1022 | }, | 1022 | }, |
| 1023 | ]); | 1023 | ]); |
| 1024 | }).pipe(Effect.provide(TEST_CRYPTO_LAYER)), | 1024 | }).pipe(Effect.provide(layerTestCrypto)), |
| 1025 | ); | 1025 | ); |
| 1026 | }); | 1026 | }); |
packages/client-runtime/src/relay/managedRelay.test.ts+19-19| ... | @@ -13,19 +13,19 @@ import * as Schema from "effect/Schema"; | ... | @@ -13,19 +13,19 @@ import * as Schema from "effect/Schema"; |
| 13 | import * as TestClock from "effect/testing/TestClock"; | 13 | import * as TestClock from "effect/testing/TestClock"; |
| 14 | 14 | ||
| 15 | import * as ManagedRelay from "./managedRelay.ts"; | 15 | import * as ManagedRelay from "./managedRelay.ts"; |
| 16 | import { remoteHttpClientLayer } from "../rpc/http.ts"; | 16 | import * as RpcHttp from "../rpc/http.ts"; |
| 17 | import { NETWORK_BLOCKING_HINT } from "../errors/network.ts"; | 17 | import { NETWORK_BLOCKING_HINT } from "../errors/network.ts"; |
| 18 | 18 | ||
| 19 | const encodeRelayError = Schema.encodeEffect(ManagedRelay.ManagedRelayClientError); | 19 | const encodeRelayError = Schema.encodeEffect(ManagedRelay.ManagedRelayClientError); |
| 20 | const decodeRelayError = Schema.decodeUnknownEffect(ManagedRelay.ManagedRelayClientError); | 20 | const decodeRelayError = Schema.decodeUnknownEffect(ManagedRelay.ManagedRelayClientError); |
| 21 | 21 | ||
| 22 | function managedRelayTestLayer( | 22 | function layerManagedRelayTest( |
| 23 | fetchFn: typeof globalThis.fetch, | 23 | fetchFn: typeof globalThis.fetch, |
| 24 | relayUrl = "https://relay.example.test", | 24 | relayUrl = "https://relay.example.test", |
| 25 | accessTokenStore?: ManagedRelay.ManagedRelayAccessTokenStore, | 25 | accessTokenStore?: ManagedRelay.ManagedRelayAccessTokenStore, |
| 26 | ) { | 26 | ) { |
| 27 | const httpClientLayer = remoteHttpClientLayer(fetchFn); | 27 | const layerHttpClient = RpcHttp.layerRemoteHttpClient(fetchFn); |
| 28 | const signerLayer = Layer.succeed( | 28 | const layerSigner = Layer.succeed( |
| 29 | ManagedRelay.ManagedRelayDpopSigner, | 29 | ManagedRelay.ManagedRelayDpopSigner, |
| 30 | ManagedRelay.ManagedRelayDpopSigner.of({ | 30 | ManagedRelay.ManagedRelayDpopSigner.of({ |
| 31 | thumbprint: Effect.succeed("client-thumbprint"), | 31 | thumbprint: Effect.succeed("client-thumbprint"), |
| ... | @@ -37,7 +37,7 @@ function managedRelayTestLayer( | ... | @@ -37,7 +37,7 @@ function managedRelayTestLayer( |
| 37 | relayUrl, | 37 | relayUrl, |
| 38 | clientId: "t3-mobile", | 38 | clientId: "t3-mobile", |
| 39 | ...(accessTokenStore ? { accessTokenStore } : {}), | 39 | ...(accessTokenStore ? { accessTokenStore } : {}), |
| 40 | }).pipe(Layer.provide(signerLayer), Layer.provide(httpClientLayer)); | 40 | }).pipe(Layer.provide(layerSigner), Layer.provide(layerHttpClient)); |
| 41 | } | 41 | } |
| 42 | 42 | ||
| 43 | function clerkToken(subject: string, nonce: string): string { | 43 | function clerkToken(subject: string, nonce: string): string { |
| ... | @@ -114,7 +114,7 @@ describe("ManagedRelayClient", () => { | ... | @@ -114,7 +114,7 @@ describe("ManagedRelayClient", () => { |
| 114 | "clientRuntime.managedRelay.createRequestProof", | 114 | "clientRuntime.managedRelay.createRequestProof", |
| 115 | ]), | 115 | ]), |
| 116 | ); | 116 | ); |
| 117 | }).pipe(Effect.withTracer(tracer), Effect.provide(managedRelayTestLayer(fetchFn))); | 117 | }).pipe(Effect.withTracer(tracer), Effect.provide(layerManagedRelayTest(fetchFn))); |
| 118 | }); | 118 | }); |
| 119 | 119 | ||
| 120 | it.effect("rejects unsafe relay URLs before sending credentials", () => { | 120 | it.effect("rejects unsafe relay URLs before sending credentials", () => { |
| ... | @@ -136,7 +136,7 @@ describe("ManagedRelayClient", () => { | ... | @@ -136,7 +136,7 @@ describe("ManagedRelayClient", () => { |
| 136 | message: "Relay URL must be a secure absolute HTTPS origin.", | 136 | message: "Relay URL must be a secure absolute HTTPS origin.", |
| 137 | }); | 137 | }); |
| 138 | expect(requestCount).toBe(0); | 138 | expect(requestCount).toBe(0); |
| 139 | }).pipe(Effect.provide(managedRelayTestLayer(fetchFn, "http://relay.example.test"))); | 139 | }).pipe(Effect.provide(layerManagedRelayTest(fetchFn, "http://relay.example.test"))); |
| 140 | }); | 140 | }); |
| 141 | 141 | ||
| 142 | it.effect("reuses usable DPoP tokens and refreshes cleared or expiring cache entries", () => { | 142 | it.effect("reuses usable DPoP tokens and refreshes cleared or expiring cache entries", () => { |
| ... | @@ -195,7 +195,7 @@ describe("ManagedRelayClient", () => { | ... | @@ -195,7 +195,7 @@ describe("ManagedRelayClient", () => { |
| 195 | yield* relayClient.resetTokenCache; | 195 | yield* relayClient.resetTokenCache; |
| 196 | yield* relayClient.getEnvironmentStatus(statusInput); | 196 | yield* relayClient.getEnvironmentStatus(statusInput); |
| 197 | expect(tokenExchangeCount).toBe(3); | 197 | expect(tokenExchangeCount).toBe(3); |
| 198 | }).pipe(Effect.provide(managedRelayTestLayer(fetchFn))); | 198 | }).pipe(Effect.provide(layerManagedRelayTest(fetchFn))); |
| 199 | }); | 199 | }); |
| 200 | 200 | ||
| 201 | it.effect("uses a cached token while another scope waits for an exchange", () => { | 201 | it.effect("uses a cached token while another scope waits for an exchange", () => { |
| ... | @@ -290,7 +290,7 @@ describe("ManagedRelayClient", () => { | ... | @@ -290,7 +290,7 @@ describe("ManagedRelayClient", () => { |
| 290 | expect(tokenExchangeCount).toBe(2); | 290 | expect(tokenExchangeCount).toBe(2); |
| 291 | }).pipe( | 291 | }).pipe( |
| 292 | Effect.ensuring(Effect.sync(() => releaseExchange.resolve())), | 292 | Effect.ensuring(Effect.sync(() => releaseExchange.resolve())), |
| 293 | Effect.provide(managedRelayTestLayer(fetchFn, undefined, accessTokenStore)), | 293 | Effect.provide(layerManagedRelayTest(fetchFn, undefined, accessTokenStore)), |
| 294 | ); | 294 | ); |
| 295 | }); | 295 | }); |
| 296 | 296 | ||
| ... | @@ -352,7 +352,7 @@ describe("ManagedRelayClient", () => { | ... | @@ -352,7 +352,7 @@ describe("ManagedRelayClient", () => { |
| 352 | yield* Effect.gen(function* () { | 352 | yield* Effect.gen(function* () { |
| 353 | const relayClient = yield* ManagedRelay.ManagedRelayClient; | 353 | const relayClient = yield* ManagedRelay.ManagedRelayClient; |
| 354 | yield* relayClient.getEnvironmentStatus(statusInput(clerkToken("user-1", "session-1"))); | 354 | yield* relayClient.getEnvironmentStatus(statusInput(clerkToken("user-1", "session-1"))); |
| 355 | }).pipe(Effect.provide(managedRelayTestLayer(fetchFn, undefined, accessTokenStore))); | 355 | }).pipe(Effect.provide(layerManagedRelayTest(fetchFn, undefined, accessTokenStore))); |
| 356 | 356 | ||
| 357 | expect(tokenExchangeCount).toBe(1); | 357 | expect(tokenExchangeCount).toBe(1); |
| 358 | expect(persistedTokens).toHaveLength(1); | 358 | expect(persistedTokens).toHaveLength(1); |
| ... | @@ -360,7 +360,7 @@ describe("ManagedRelayClient", () => { | ... | @@ -360,7 +360,7 @@ describe("ManagedRelayClient", () => { |
| 360 | yield* Effect.gen(function* () { | 360 | yield* Effect.gen(function* () { |
| 361 | const relayClient = yield* ManagedRelay.ManagedRelayClient; | 361 | const relayClient = yield* ManagedRelay.ManagedRelayClient; |
| 362 | yield* relayClient.getEnvironmentStatus(statusInput(clerkToken("user-1", "session-2"))); | 362 | yield* relayClient.getEnvironmentStatus(statusInput(clerkToken("user-1", "session-2"))); |
| 363 | }).pipe(Effect.provide(managedRelayTestLayer(fetchFn, undefined, accessTokenStore))); | 363 | }).pipe(Effect.provide(layerManagedRelayTest(fetchFn, undefined, accessTokenStore))); |
| 364 | 364 | ||
| 365 | expect(tokenExchangeCount).toBe(1); | 365 | expect(tokenExchangeCount).toBe(1); |
| 366 | }); | 366 | }); |
| ... | @@ -457,7 +457,7 @@ describe("ManagedRelayClient", () => { | ... | @@ -457,7 +457,7 @@ describe("ManagedRelayClient", () => { |
| 457 | accessToken: "fresh-relay-token", | 457 | accessToken: "fresh-relay-token", |
| 458 | }, | 458 | }, |
| 459 | ]); | 459 | ]); |
| 460 | }).pipe(Effect.provide(managedRelayTestLayer(fetchFn, undefined, accessTokenStore))); | 460 | }).pipe(Effect.provide(layerManagedRelayTest(fetchFn, undefined, accessTokenStore))); |
| 461 | }); | 461 | }); |
| 462 | 462 | ||
| 463 | it.effect("does not persist tokens when the Clerk subject cannot be decoded", () => { | 463 | it.effect("does not persist tokens when the Clerk subject cannot be decoded", () => { |
| ... | @@ -513,7 +513,7 @@ describe("ManagedRelayClient", () => { | ... | @@ -513,7 +513,7 @@ describe("ManagedRelayClient", () => { |
| 513 | }); | 513 | }); |
| 514 | 514 | ||
| 515 | expect(persistedTokens).toEqual([]); | 515 | expect(persistedTokens).toEqual([]); |
| 516 | }).pipe(Effect.provide(managedRelayTestLayer(fetchFn, undefined, accessTokenStore))); | 516 | }).pipe(Effect.provide(layerManagedRelayTest(fetchFn, undefined, accessTokenStore))); |
| 517 | }); | 517 | }); |
| 518 | 518 | ||
| 519 | it.effect("times out stalled relay environment listing requests", () => { | 519 | it.effect("times out stalled relay environment listing requests", () => { |
| ... | @@ -536,7 +536,7 @@ describe("ManagedRelayClient", () => { | ... | @@ -536,7 +536,7 @@ describe("ManagedRelayClient", () => { |
| 536 | timeoutMs: ManagedRelay.MANAGED_RELAY_REQUEST_TIMEOUT_MS, | 536 | timeoutMs: ManagedRelay.MANAGED_RELAY_REQUEST_TIMEOUT_MS, |
| 537 | message: `Relay environment listing timed out. ${NETWORK_BLOCKING_HINT}`, | 537 | message: `Relay environment listing timed out. ${NETWORK_BLOCKING_HINT}`, |
| 538 | }); | 538 | }); |
| 539 | }).pipe(Effect.provide(Layer.merge(TestClock.layer(), managedRelayTestLayer(fetchFn)))); | 539 | }).pipe(Effect.provide(Layer.merge(TestClock.layer(), layerManagedRelayTest(fetchFn)))); |
| 540 | }); | 540 | }); |
| 541 | 541 | ||
| 542 | it.effect("suggests checking network filtering when fetch fails without a response", () => { | 542 | it.effect("suggests checking network filtering when fetch fails without a response", () => { |
| ... | @@ -555,7 +555,7 @@ describe("ManagedRelayClient", () => { | ... | @@ -555,7 +555,7 @@ describe("ManagedRelayClient", () => { |
| 555 | const encoded = yield* encodeRelayError(error); | 555 | const encoded = yield* encodeRelayError(error); |
| 556 | const decoded = yield* decodeRelayError(encoded); | 556 | const decoded = yield* decodeRelayError(encoded); |
| 557 | expect(decoded.message).toBe(error.message); | 557 | expect(decoded.message).toBe(error.message); |
| 558 | }).pipe(Effect.provide(managedRelayTestLayer(fetchFn))); | 558 | }).pipe(Effect.provide(layerManagedRelayTest(fetchFn))); |
| 559 | }); | 559 | }); |
| 560 | 560 | ||
| 561 | it.effect("does not suggest network filtering for an HTTP server error", () => { | 561 | it.effect("does not suggest network filtering for an HTTP server error", () => { |
| ... | @@ -569,7 +569,7 @@ describe("ManagedRelayClient", () => { | ... | @@ -569,7 +569,7 @@ describe("ManagedRelayClient", () => { |
| 569 | .listEnvironments({ clerkToken: "clerk-token" }) | 569 | .listEnvironments({ clerkToken: "clerk-token" }) |
| 570 | .pipe(Effect.flip); | 570 | .pipe(Effect.flip); |
| 571 | expect(error.message).toBe("Could not list relay-managed environments."); | 571 | expect(error.message).toBe("Could not list relay-managed environments."); |
| 572 | }).pipe(Effect.provide(managedRelayTestLayer(fetchFn))); | 572 | }).pipe(Effect.provide(layerManagedRelayTest(fetchFn))); |
| 573 | }); | 573 | }); |
| 574 | 574 | ||
| 575 | it.effect("preserves typed relay trace IDs on client errors", () => { | 575 | it.effect("preserves typed relay trace IDs on client errors", () => { |
| ... | @@ -596,7 +596,7 @@ describe("ManagedRelayClient", () => { | ... | @@ -596,7 +596,7 @@ describe("ManagedRelayClient", () => { |
| 596 | _tag: "ManagedRelayRequestFailedError", | 596 | _tag: "ManagedRelayRequestFailedError", |
| 597 | traceId: "trace-managed-relay", | 597 | traceId: "trace-managed-relay", |
| 598 | }); | 598 | }); |
| 599 | }).pipe(Effect.provide(managedRelayTestLayer(fetchFn))); | 599 | }).pipe(Effect.provide(layerManagedRelayTest(fetchFn))); |
| 600 | }); | 600 | }); |
| 601 | 601 | ||
| 602 | it.effect("accepts generic DPoP errors from relays without the optional reason", () => { | 602 | it.effect("accepts generic DPoP errors from relays without the optional reason", () => { |
| ... | @@ -633,7 +633,7 @@ describe("ManagedRelayClient", () => { | ... | @@ -633,7 +633,7 @@ describe("ManagedRelayClient", () => { |
| 633 | ) { | 633 | ) { |
| 634 | expect(error.relayError.dpopFailureReason).toBeUndefined(); | 634 | expect(error.relayError.dpopFailureReason).toBeUndefined(); |
| 635 | } | 635 | } |
| 636 | }).pipe(Effect.provide(managedRelayTestLayer(fetchFn))); | 636 | }).pipe(Effect.provide(layerManagedRelayTest(fetchFn))); |
| 637 | }); | 637 | }); |
| 638 | 638 | ||
| 639 | it.effect("lists account devices through the v2 Clerk bearer client endpoint", () => { | 639 | it.effect("lists account devices through the v2 Clerk bearer client endpoint", () => { |
| ... | @@ -703,6 +703,6 @@ describe("ManagedRelayClient", () => { | ... | @@ -703,6 +703,6 @@ describe("ManagedRelayClient", () => { |
| 703 | androidApiLevel: 36, | 703 | androidApiLevel: 36, |
| 704 | }, | 704 | }, |
| 705 | ]); | 705 | ]); |
| 706 | }).pipe(Effect.provide(managedRelayTestLayer(fetchFn))); | 706 | }).pipe(Effect.provide(layerManagedRelayTest(fetchFn))); |
| 707 | }); | 707 | }); |
| 708 | }); | 708 | }); |
packages/client-runtime/src/rpc/http.ts+3-3| ... | @@ -8,7 +8,7 @@ import { | ... | @@ -8,7 +8,7 @@ import { |
| 8 | type EnvironmentResourceNotFoundError, | 8 | type EnvironmentResourceNotFoundError, |
| 9 | type EnvironmentScopeRequiredError, | 9 | type EnvironmentScopeRequiredError, |
| 10 | } from "@t3tools/contracts"; | 10 | } from "@t3tools/contracts"; |
| 11 | import { httpHeaderRedactionLayer } from "@t3tools/shared/httpObservability"; | 11 | import * as HttpObservability from "@t3tools/shared/httpObservability"; |
| 12 | import * as Data from "effect/Data"; | 12 | import * as Data from "effect/Data"; |
| 13 | import * as Duration from "effect/Duration"; | 13 | import * as Duration from "effect/Duration"; |
| 14 | import * as Effect from "effect/Effect"; | 14 | import * as Effect from "effect/Effect"; |
| ... | @@ -78,12 +78,12 @@ export type RemoteEnvironmentRequestError = | ... | @@ -78,12 +78,12 @@ export type RemoteEnvironmentRequestError = |
| 78 | | RemoteEnvironmentAuthUndeclaredStatusError | 78 | | RemoteEnvironmentAuthUndeclaredStatusError |
| 79 | | RemoteEnvironmentAuthTimeoutError; | 79 | | RemoteEnvironmentAuthTimeoutError; |
| 80 | 80 | ||
| 81 | export const remoteHttpClientLayer = ( | 81 | export const layerRemoteHttpClient = ( |
| 82 | fetchFn: typeof globalThis.fetch, | 82 | fetchFn: typeof globalThis.fetch, |
| 83 | ): Layer.Layer<HttpClient.HttpClient> => | 83 | ): Layer.Layer<HttpClient.HttpClient> => |
| 84 | Layer.merge( | 84 | Layer.merge( |
| 85 | FetchHttpClient.layer.pipe(Layer.provide(Layer.succeed(FetchHttpClient.Fetch, fetchFn))), | 85 | FetchHttpClient.layer.pipe(Layer.provide(Layer.succeed(FetchHttpClient.Fetch, fetchFn))), |
| 86 | httpHeaderRedactionLayer, | 86 | HttpObservability.layer, |
| 87 | ); | 87 | ); |
| 88 | 88 | ||
| 89 | const remoteApiBaseUrl = (httpBaseUrl: string): string => { | 89 | const remoteApiBaseUrl = (httpBaseUrl: string): string => { |
packages/client-runtime/src/rpc/session.test.ts+2-2| ... | @@ -215,12 +215,12 @@ const makeFactory = Effect.fn("TestRpcSessionFactory.make")(function* ( | ... | @@ -215,12 +215,12 @@ const makeFactory = Effect.fn("TestRpcSessionFactory.make")(function* ( |
| 215 | options: RpcSession.RpcSessionOptions = {}, | 215 | options: RpcSession.RpcSessionOptions = {}, |
| 216 | ) { | 216 | ) { |
| 217 | const sockets: TestWebSocket[] = []; | 217 | const sockets: TestWebSocket[] = []; |
| 218 | const constructorLayer = Layer.succeed(Socket.WebSocketConstructor, (url) => { | 218 | const layerConstructor = Layer.succeed(Socket.WebSocketConstructor, (url) => { |
| 219 | const socket = new TestWebSocket(url); | 219 | const socket = new TestWebSocket(url); |
| 220 | sockets.push(socket); | 220 | sockets.push(socket); |
| 221 | return socket as unknown as globalThis.WebSocket; | 221 | return socket as unknown as globalThis.WebSocket; |
| 222 | }); | 222 | }); |
| 223 | const layer = RpcSession.layer(options).pipe(Layer.provide(constructorLayer)); | 223 | const layer = RpcSession.layer(options).pipe(Layer.provide(layerConstructor)); |
| 224 | const factory = yield* RpcSession.RpcSessionFactory.pipe(Effect.provide(layer)); | 224 | const factory = yield* RpcSession.RpcSessionFactory.pipe(Effect.provide(layer)); |
| 225 | return { factory, sockets }; | 225 | return { factory, sockets }; |
| 226 | }); | 226 | }); |
packages/client-runtime/src/rpc/session.ts+4-4| ... | @@ -195,10 +195,10 @@ export const make = Effect.fn("RpcSessionFactory.make")(function* ( | ... | @@ -195,10 +195,10 @@ export const make = Effect.fn("RpcSessionFactory.make")(function* ( |
| 195 | Effect.asVoid, | 195 | Effect.asVoid, |
| 196 | ), | 196 | ), |
| 197 | }); | 197 | }); |
| 198 | const socketLayer = Socket.layerWebSocket(connection.socketUrl, { | 198 | const layerSocket = Socket.layerWebSocket(connection.socketUrl, { |
| 199 | openTimeout: SOCKET_OPEN_TIMEOUT, | 199 | openTimeout: SOCKET_OPEN_TIMEOUT, |
| 200 | }).pipe(Layer.provide(Layer.succeed(Socket.WebSocketConstructor, webSocketConstructor))); | 200 | }).pipe(Layer.provide(Layer.succeed(Socket.WebSocketConstructor, webSocketConstructor))); |
| 201 | const protocolLayer = Layer.effect( | 201 | const layerProtocol = Layer.effect( |
| 202 | RpcClient.Protocol, | 202 | RpcClient.Protocol, |
| 203 | RpcClient.makeProtocolSocket({ | 203 | RpcClient.makeProtocolSocket({ |
| 204 | retryTransientErrors: false, | 204 | retryTransientErrors: false, |
| ... | @@ -207,13 +207,13 @@ export const make = Effect.fn("RpcSessionFactory.make")(function* ( | ... | @@ -207,13 +207,13 @@ export const make = Effect.fn("RpcSessionFactory.make")(function* ( |
| 207 | ).pipe( | 207 | ).pipe( |
| 208 | Layer.provide( | 208 | Layer.provide( |
| 209 | Layer.mergeAll( | 209 | Layer.mergeAll( |
| 210 | socketLayer, | 210 | layerSocket, |
| 211 | RpcSerialization.layerJson, | 211 | RpcSerialization.layerJson, |
| 212 | Layer.succeed(RpcClient.ConnectionHooks, hooks), | 212 | Layer.succeed(RpcClient.ConnectionHooks, hooks), |
| 213 | ), | 213 | ), |
| 214 | ), | 214 | ), |
| 215 | ); | 215 | ); |
| 216 | const protocolContext = yield* Layer.build(protocolLayer).pipe( | 216 | const protocolContext = yield* Layer.build(layerProtocol).pipe( |
| 217 | Effect.withSpan("environment.websocket.connect"), | 217 | Effect.withSpan("environment.websocket.connect"), |
| 218 | ); | 218 | ); |
| 219 | const protocolClient = yield* makeWsRpcProtocolClient.pipe(Effect.provide(protocolContext)); | 219 | const protocolClient = yield* makeWsRpcProtocolClient.pipe(Effect.provide(protocolContext)); |
packages/client-runtime/src/state/boundedThreadSnapshotHttp.test.ts+6-6| ... | @@ -4,8 +4,8 @@ import * as Effect from "effect/Effect"; | ... | @@ -4,8 +4,8 @@ import * as Effect from "effect/Effect"; |
| 4 | import * as Layer from "effect/Layer"; | 4 | import * as Layer from "effect/Layer"; |
| 5 | 5 | ||
| 6 | import { PrimaryConnectionTarget, type PreparedConnection } from "../connection/model.ts"; | 6 | import { PrimaryConnectionTarget, type PreparedConnection } from "../connection/model.ts"; |
| 7 | import { remoteHttpClientLayer } from "../rpc/http.ts"; | 7 | import * as RpcHttp from "../rpc/http.ts"; |
| 8 | import { boundedThreadSnapshotLoaderLayer } from "./boundedThreadSnapshotHttp.ts"; | 8 | import * as BoundedThreadSnapshotHttp from "./boundedThreadSnapshotHttp.ts"; |
| 9 | import * as ThreadSnapshotLoader from "./threadSnapshotHttp.ts"; | 9 | import * as ThreadSnapshotLoader from "./threadSnapshotHttp.ts"; |
| 10 | 10 | ||
| 11 | const TARGET = new PrimaryConnectionTarget({ | 11 | const TARGET = new PrimaryConnectionTarget({ |
| ... | @@ -110,7 +110,7 @@ describe("boundedThreadSnapshotLoader", () => { | ... | @@ -110,7 +110,7 @@ describe("boundedThreadSnapshotLoader", () => { |
| 110 | } | 110 | } |
| 111 | }).pipe( | 111 | }).pipe( |
| 112 | Effect.provide( | 112 | Effect.provide( |
| 113 | Layer.provide(boundedThreadSnapshotLoaderLayer, remoteHttpClientLayer(fetchFn)), | 113 | Layer.provide(BoundedThreadSnapshotHttp.layer, RpcHttp.layerRemoteHttpClient(fetchFn)), |
| 114 | ), | 114 | ), |
| 115 | ); | 115 | ); |
| 116 | }); | 116 | }); |
| ... | @@ -138,7 +138,7 @@ describe("boundedThreadSnapshotLoader", () => { | ... | @@ -138,7 +138,7 @@ describe("boundedThreadSnapshotLoader", () => { |
| 138 | expect(result).toEqual({ _tag: "missing" }); | 138 | expect(result).toEqual({ _tag: "missing" }); |
| 139 | }).pipe( | 139 | }).pipe( |
| 140 | Effect.provide( | 140 | Effect.provide( |
| 141 | Layer.provide(boundedThreadSnapshotLoaderLayer, remoteHttpClientLayer(fetchFn)), | 141 | Layer.provide(BoundedThreadSnapshotHttp.layer, RpcHttp.layerRemoteHttpClient(fetchFn)), |
| 142 | ), | 142 | ), |
| 143 | ); | 143 | ); |
| 144 | }); | 144 | }); |
| ... | @@ -176,7 +176,7 @@ describe("boundedThreadSnapshotLoader", () => { | ... | @@ -176,7 +176,7 @@ describe("boundedThreadSnapshotLoader", () => { |
| 176 | expect(fullCalls).toBe(1); | 176 | expect(fullCalls).toBe(1); |
| 177 | }).pipe( | 177 | }).pipe( |
| 178 | Effect.provide( | 178 | Effect.provide( |
| 179 | Layer.provide(boundedThreadSnapshotLoaderLayer, remoteHttpClientLayer(fetchFn)), | 179 | Layer.provide(BoundedThreadSnapshotHttp.layer, RpcHttp.layerRemoteHttpClient(fetchFn)), |
| 180 | ), | 180 | ), |
| 181 | ); | 181 | ); |
| 182 | }); | 182 | }); |
| ... | @@ -206,7 +206,7 @@ describe("boundedThreadSnapshotLoader", () => { | ... | @@ -206,7 +206,7 @@ describe("boundedThreadSnapshotLoader", () => { |
| 206 | expect(fullCalls).toBe(0); | 206 | expect(fullCalls).toBe(0); |
| 207 | }).pipe( | 207 | }).pipe( |
| 208 | Effect.provide( | 208 | Effect.provide( |
| 209 | Layer.provide(boundedThreadSnapshotLoaderLayer, remoteHttpClientLayer(fetchFn)), | 209 | Layer.provide(BoundedThreadSnapshotHttp.layer, RpcHttp.layerRemoteHttpClient(fetchFn)), |
| 210 | ), | 210 | ), |
| 211 | ); | 211 | ); |
| 212 | }, | 212 | }, |
packages/client-runtime/src/state/boundedThreadSnapshotHttp.ts+1-1| ... | @@ -53,7 +53,7 @@ export const fetchEnvironmentBoundedThreadSnapshot = Effect.fn( | ... | @@ -53,7 +53,7 @@ export const fetchEnvironmentBoundedThreadSnapshot = Effect.fn( |
| 53 | * endpoint still means missing. Transient failures report `unavailable` so the | 53 | * endpoint still means missing. Transient failures report `unavailable` so the |
| 54 | * socket path remains a last resort for connectivity issues. | 54 | * socket path remains a last resort for connectivity issues. |
| 55 | */ | 55 | */ |
| 56 | export const boundedThreadSnapshotLoaderLayer: Layer.Layer< | 56 | export const layer: Layer.Layer< |
| 57 | ThreadSnapshotLoader.ThreadSnapshotLoader, | 57 | ThreadSnapshotLoader.ThreadSnapshotLoader, |
| 58 | never, | 58 | never, |
| 59 | HttpClient.HttpClient | 59 | HttpClient.HttpClient |
packages/client-runtime/src/state/environmentHttpAuth.test.ts+9-10| ... | @@ -27,16 +27,15 @@ import { | ... | @@ -27,16 +27,15 @@ import { |
| 27 | type PreparedHttpAuthorization, | 27 | type PreparedHttpAuthorization, |
| 28 | } from "../connection/model.ts"; | 28 | } from "../connection/model.ts"; |
| 29 | import * as ManagedRelay from "../relay/managedRelay.ts"; | 29 | import * as ManagedRelay from "../relay/managedRelay.ts"; |
| 30 | import { remoteHttpClientLayer, type RemoteEnvironmentRequestError } from "../rpc/http.ts"; | 30 | import { type RemoteEnvironmentRequestError } from "../rpc/http.ts"; |
| 31 | import * as RpcHttp from "../rpc/http.ts"; | ||
| 31 | import * as PullRequestDiffLoader from "./pullRequestDiffHttp.ts"; | 32 | import * as PullRequestDiffLoader from "./pullRequestDiffHttp.ts"; |
| 32 | import { withOrchestrationProtocolHeader } from "./environmentHttpAuth.ts"; | 33 | import { withOrchestrationProtocolHeader } from "./environmentHttpAuth.ts"; |
| 33 | import { fetchEnvironmentSessionState } from "./session.ts"; | 34 | import { fetchEnvironmentSessionState } from "./session.ts"; |
| 34 | import { fetchEnvironmentShellSnapshot } from "./shellSnapshotHttp.ts"; | 35 | import { fetchEnvironmentShellSnapshot } from "./shellSnapshotHttp.ts"; |
| 35 | import * as ThreadSnapshotLoader from "./threadSnapshotHttp.ts"; | 36 | import * as ThreadSnapshotLoader from "./threadSnapshotHttp.ts"; |
| 36 | import { | 37 | import { fetchEnvironmentBoundedThreadSnapshot } from "./boundedThreadSnapshotHttp.ts"; |
| 37 | boundedThreadSnapshotLoaderLayer, | 38 | import * as BoundedThreadSnapshotHttp from "./boundedThreadSnapshotHttp.ts"; |
| 38 | fetchEnvironmentBoundedThreadSnapshot, | ||
| 39 | } from "./boundedThreadSnapshotHttp.ts"; | ||
| 40 | import { fetchEnvironmentThreadHistoryPage } from "./threadHistoryHttp.ts"; | 39 | import { fetchEnvironmentThreadHistoryPage } from "./threadHistoryHttp.ts"; |
| 41 | import { v2Projection } from "./orchestrationV2TestFixtures.ts"; | 40 | import { v2Projection } from "./orchestrationV2TestFixtures.ts"; |
| 42 | 41 | ||
| ... | @@ -161,7 +160,7 @@ function makeHarness(reply: (requestNumber: number) => Response | Promise<Respon | ... | @@ -161,7 +160,7 @@ function makeHarness(reply: (requestNumber: number) => Response | Promise<Respon |
| 161 | signer: Option.some(signer), | 160 | signer: Option.some(signer), |
| 162 | remoteAuthorization: Option.some(remoteAuthorization), | 161 | remoteAuthorization: Option.some(remoteAuthorization), |
| 163 | }, | 162 | }, |
| 164 | httpLayer: remoteHttpClientLayer(fetchFn), | 163 | httpLayer: RpcHttp.layerRemoteHttpClient(fetchFn), |
| 165 | }; | 164 | }; |
| 166 | } | 165 | } |
| 167 | 166 | ||
| ... | @@ -360,7 +359,7 @@ describe("authenticated environment HTTP requests", () => { | ... | @@ -360,7 +359,7 @@ describe("authenticated environment HTTP requests", () => { |
| 360 | ? credentialRejectedResponse() | 359 | ? credentialRejectedResponse() |
| 361 | : Response.json(encodeBoundedSnapshot(BOUNDED_THREAD)), | 360 | : Response.json(encodeBoundedSnapshot(BOUNDED_THREAD)), |
| 362 | ); | 361 | ); |
| 363 | const loaderLayer = boundedThreadSnapshotLoaderLayer.pipe( | 362 | const layerLoader = BoundedThreadSnapshotHttp.layer.pipe( |
| 364 | Layer.provide( | 363 | Layer.provide( |
| 365 | Layer.mergeAll( | 364 | Layer.mergeAll( |
| 366 | harness.httpLayer, | 365 | harness.httpLayer, |
| ... | @@ -376,7 +375,7 @@ describe("authenticated environment HTTP requests", () => { | ... | @@ -376,7 +375,7 @@ describe("authenticated environment HTTP requests", () => { |
| 376 | ), | 375 | ), |
| 377 | ); | 376 | ); |
| 378 | const loader = yield* ThreadSnapshotLoader.ThreadSnapshotLoader.pipe( | 377 | const loader = yield* ThreadSnapshotLoader.ThreadSnapshotLoader.pipe( |
| 379 | Effect.provide(loaderLayer), | 378 | Effect.provide(layerLoader), |
| 380 | ); | 379 | ); |
| 381 | const result = yield* loader.load(PREPARED, THREAD.projection.thread.id); | 380 | const result = yield* loader.load(PREPARED, THREAD.projection.thread.id); |
| 382 | expect(result).toEqual({ | 381 | expect(result).toEqual({ |
| ... | @@ -398,7 +397,7 @@ describe("authenticated environment HTTP requests", () => { | ... | @@ -398,7 +397,7 @@ describe("authenticated environment HTTP requests", () => { |
| 398 | it.effect("uses the authorization service captured by the diff loader layer", () => | 397 | it.effect("uses the authorization service captured by the diff loader layer", () => |
| 399 | Effect.gen(function* () { | 398 | Effect.gen(function* () { |
| 400 | const harness = makeHarness(() => Response.json(DIFF_RESULT)); | 399 | const harness = makeHarness(() => Response.json(DIFF_RESULT)); |
| 401 | const loaderLayer = PullRequestDiffLoader.layer.pipe( | 400 | const layerLoader = PullRequestDiffLoader.layer.pipe( |
| 402 | Layer.provide( | 401 | Layer.provide( |
| 403 | Layer.mergeAll( | 402 | Layer.mergeAll( |
| 404 | harness.httpLayer, | 403 | harness.httpLayer, |
| ... | @@ -414,7 +413,7 @@ describe("authenticated environment HTTP requests", () => { | ... | @@ -414,7 +413,7 @@ describe("authenticated environment HTTP requests", () => { |
| 414 | ), | 413 | ), |
| 415 | ); | 414 | ); |
| 416 | const loader = yield* PullRequestDiffLoader.PullRequestDiffLoader.pipe( | 415 | const loader = yield* PullRequestDiffLoader.PullRequestDiffLoader.pipe( |
| 417 | Effect.provide(loaderLayer), | 416 | Effect.provide(layerLoader), |
| 418 | ); | 417 | ); |
| 419 | const result = yield* loader.load(PREPARED, DIFF); | 418 | const result = yield* loader.load(PREPARED, DIFF); |
| 420 | 419 |
packages/client-runtime/src/state/pullRequestDiffHttp.test.ts+3-3| ... | @@ -4,7 +4,7 @@ import * as Effect from "effect/Effect"; | ... | @@ -4,7 +4,7 @@ import * as Effect from "effect/Effect"; |
| 4 | import * as Option from "effect/Option"; | 4 | import * as Option from "effect/Option"; |
| 5 | 5 | ||
| 6 | import { PrimaryConnectionTarget, type PreparedConnection } from "../connection/model.ts"; | 6 | import { PrimaryConnectionTarget, type PreparedConnection } from "../connection/model.ts"; |
| 7 | import { remoteHttpClientLayer } from "../rpc/http.ts"; | 7 | import * as RpcHttp from "../rpc/http.ts"; |
| 8 | import { | 8 | import { |
| 9 | fetchEnvironmentPullRequestDiff, | 9 | fetchEnvironmentPullRequestDiff, |
| 10 | PullRequestDiffCredentialRejectedError, | 10 | PullRequestDiffCredentialRejectedError, |
| ... | @@ -50,7 +50,7 @@ describe("fetchEnvironmentPullRequestDiff", () => { | ... | @@ -50,7 +50,7 @@ describe("fetchEnvironmentPullRequestDiff", () => { |
| 50 | number: 42, | 50 | number: 42, |
| 51 | cursor: "next-page", | 51 | cursor: "next-page", |
| 52 | }, | 52 | }, |
| 53 | }).pipe(Effect.provide(remoteHttpClientLayer(fetchFn))); | 53 | }).pipe(Effect.provide(RpcHttp.layerRemoteHttpClient(fetchFn))); |
| 54 | 54 | ||
| 55 | expect(result).toEqual({ | 55 | expect(result).toEqual({ |
| 56 | patch: "diff --git a/file.ts b/file.ts", | 56 | patch: "diff --git a/file.ts b/file.ts", |
| ... | @@ -104,7 +104,7 @@ describe("fetchEnvironmentPullRequestDiff", () => { | ... | @@ -104,7 +104,7 @@ describe("fetchEnvironmentPullRequestDiff", () => { |
| 104 | repository: "owner/repository", | 104 | repository: "owner/repository", |
| 105 | number: 42, | 105 | number: 42, |
| 106 | }, | 106 | }, |
| 107 | }).pipe(Effect.provide(remoteHttpClientLayer(fetchFn)), Effect.flip); | 107 | }).pipe(Effect.provide(RpcHttp.layerRemoteHttpClient(fetchFn)), Effect.flip); |
| 108 | 108 | ||
| 109 | expect(error).toBeInstanceOf(PullRequestDiffCredentialRejectedError); | 109 | expect(error).toBeInstanceOf(PullRequestDiffCredentialRejectedError); |
| 110 | expect(error).toMatchObject({ | 110 | expect(error).toMatchObject({ |
packages/client-runtime/src/state/threads.ts+1-1| ... | @@ -995,7 +995,7 @@ export function createEnvironmentThreadStateAtoms<R, E>( | ... | @@ -995,7 +995,7 @@ export function createEnvironmentThreadStateAtoms<R, E>( |
| 995 | 995 | ||
| 996 | export * from "./archivedThreads.ts"; | 996 | export * from "./archivedThreads.ts"; |
| 997 | export * from "./checkpointDiff.ts"; | 997 | export * from "./checkpointDiff.ts"; |
| 998 | export * from "./boundedThreadSnapshotHttp.ts"; | 998 | export * as BoundedThreadSnapshotLoader from "./boundedThreadSnapshotHttp.ts"; |
| 999 | export * as ThreadHistoryController from "./threadHistoryController.ts"; | 999 | export * as ThreadHistoryController from "./threadHistoryController.ts"; |
| 1000 | // Flat so consumers' inferred types can name it. | 1000 | // Flat so consumers' inferred types can name it. |
| 1001 | export type { ThreadHistoryLoadEarlierResult } from "./threadHistoryController.ts"; | 1001 | export type { ThreadHistoryLoadEarlierResult } from "./threadHistoryController.ts"; |
packages/effect-acp/scripts/generate.ts+2-2| ... | @@ -514,7 +514,7 @@ const generateCommand = Command.make( | ... | @@ -514,7 +514,7 @@ const generateCommand = Command.make( |
| 514 | ({ skipDownload }) => generateSchemas(skipDownload), | 514 | ({ skipDownload }) => generateSchemas(skipDownload), |
| 515 | ).pipe(Command.withDescription("Generate Effect ACP schemas from the pinned ACP release assets.")); | 515 | ).pipe(Command.withDescription("Generate Effect ACP schemas from the pinned ACP release assets.")); |
| 516 | 516 | ||
| 517 | const runtimeLayer = Layer.mergeAll( | 517 | const layerRuntime = Layer.mergeAll( |
| 518 | Logger.layer([Logger.consolePretty()]), | 518 | Logger.layer([Logger.consolePretty()]), |
| 519 | NodeServices.layer, | 519 | NodeServices.layer, |
| 520 | FetchHttpClient.layer, | 520 | FetchHttpClient.layer, |
| ... | @@ -522,6 +522,6 @@ const runtimeLayer = Layer.mergeAll( | ... | @@ -522,6 +522,6 @@ const runtimeLayer = Layer.mergeAll( |
| 522 | 522 | ||
| 523 | Command.run(generateCommand, { version: "0.0.0" }).pipe( | 523 | Command.run(generateCommand, { version: "0.0.0" }).pipe( |
| 524 | Effect.scoped, | 524 | Effect.scoped, |
| 525 | Effect.provide(runtimeLayer), | 525 | Effect.provide(layerRuntime), |
| 526 | NodeRuntime.runMain, | 526 | NodeRuntime.runMain, |
| 527 | ); | 527 | ); |
packages/effect-acp/src/agent.ts+2-2| ... | @@ -316,7 +316,7 @@ export const make = Effect.fn("effect-acp/AcpAgent.make")(function* ( | ... | @@ -316,7 +316,7 @@ export const make = Effect.fn("effect-acp/AcpAgent.make")(function* ( |
| 316 | method, | 316 | method, |
| 317 | }); | 317 | }); |
| 318 | 318 | ||
| 319 | const agentHandlerLayer = AcpRpcs.AgentRpcs.toLayer( | 319 | const layerAgentHandler = AcpRpcs.AgentRpcs.toLayer( |
| 320 | AcpRpcs.AgentRpcs.of({ | 320 | AcpRpcs.AgentRpcs.of({ |
| 321 | [AGENT_METHODS.initialize]: (payload, { requestId }) => | 321 | [AGENT_METHODS.initialize]: (payload, { requestId }) => |
| 322 | runHandler( | 322 | runHandler( |
| ... | @@ -423,7 +423,7 @@ export const make = Effect.fn("effect-acp/AcpAgent.make")(function* ( | ... | @@ -423,7 +423,7 @@ export const make = Effect.fn("effect-acp/AcpAgent.make")(function* ( |
| 423 | 423 | ||
| 424 | yield* RpcServer.make(AcpRpcs.AgentRpcs).pipe( | 424 | yield* RpcServer.make(AcpRpcs.AgentRpcs).pipe( |
| 425 | Effect.provideService(RpcServer.Protocol, transport.serverProtocol), | 425 | Effect.provideService(RpcServer.Protocol, transport.serverProtocol), |
| 426 | Effect.provide(agentHandlerLayer), | 426 | Effect.provide(layerAgentHandler), |
| 427 | Effect.forkScoped, | 427 | Effect.forkScoped, |
| 428 | ); | 428 | ); |
| 429 | 429 |
packages/effect-acp/src/client.test.ts+8-8| ... | @@ -309,8 +309,8 @@ it.layer(NodeServices.layer)("effect-acp client", (it) => { | ... | @@ -309,8 +309,8 @@ it.layer(NodeServices.layer)("effect-acp client", (it) => { |
| 309 | const requestContexts = yield* Ref.make<Array<AcpProtocol.AcpRequestContext>>([]); | 309 | const requestContexts = yield* Ref.make<Array<AcpProtocol.AcpRequestContext>>([]); |
| 310 | const handle = yield* makeHandle(); | 310 | const handle = yield* makeHandle(); |
| 311 | const scope = yield* Scope.make(); | 311 | const scope = yield* Scope.make(); |
| 312 | const acpLayer = AcpClient.layerChildProcess(handle); | 312 | const layerAcp = AcpClient.layerChildProcess(handle); |
| 313 | const context = yield* Layer.buildWithScope(acpLayer, scope); | 313 | const context = yield* Layer.buildWithScope(layerAcp, scope); |
| 314 | 314 | ||
| 315 | const ext = yield* Effect.gen(function* () { | 315 | const ext = yield* Effect.gen(function* () { |
| 316 | const acp = yield* AcpClient.AcpClient; | 316 | const acp = yield* AcpClient.AcpClient; |
| ... | @@ -431,8 +431,8 @@ it.layer(NodeServices.layer)("effect-acp client", (it) => { | ... | @@ -431,8 +431,8 @@ it.layer(NodeServices.layer)("effect-acp client", (it) => { |
| 431 | Effect.gen(function* () { | 431 | Effect.gen(function* () { |
| 432 | const handle = yield* makeHandle({ ACP_MOCK_BAD_TYPED_REQUEST: "1" }); | 432 | const handle = yield* makeHandle({ ACP_MOCK_BAD_TYPED_REQUEST: "1" }); |
| 433 | const scope = yield* Scope.make(); | 433 | const scope = yield* Scope.make(); |
| 434 | const acpLayer = AcpClient.layerChildProcess(handle); | 434 | const layerAcp = AcpClient.layerChildProcess(handle); |
| 435 | const context = yield* Layer.buildWithScope(acpLayer, scope); | 435 | const context = yield* Layer.buildWithScope(layerAcp, scope); |
| 436 | 436 | ||
| 437 | const result = yield* Effect.gen(function* () { | 437 | const result = yield* Effect.gen(function* () { |
| 438 | const acp = yield* AcpClient.AcpClient; | 438 | const acp = yield* AcpClient.AcpClient; |
| ... | @@ -598,8 +598,8 @@ it.layer(NodeServices.layer)("effect-acp client", (it) => { | ... | @@ -598,8 +598,8 @@ it.layer(NodeServices.layer)("effect-acp client", (it) => { |
| 598 | const typedNotifications = yield* Ref.make<Array<unknown>>([]); | 598 | const typedNotifications = yield* Ref.make<Array<unknown>>([]); |
| 599 | const handle = yield* makeHandle(); | 599 | const handle = yield* makeHandle(); |
| 600 | const scope = yield* Scope.make(); | 600 | const scope = yield* Scope.make(); |
| 601 | const acpLayer = AcpClient.layerChildProcess(handle); | 601 | const layerAcp = AcpClient.layerChildProcess(handle); |
| 602 | const context = yield* Layer.buildWithScope(acpLayer, scope); | 602 | const context = yield* Layer.buildWithScope(layerAcp, scope); |
| 603 | 603 | ||
| 604 | yield* Effect.gen(function* () { | 604 | yield* Effect.gen(function* () { |
| 605 | const acp = yield* AcpClient.AcpClient; | 605 | const acp = yield* AcpClient.AcpClient; |
| ... | @@ -679,8 +679,8 @@ it.layer(NodeServices.layer)("effect-acp client", (it) => { | ... | @@ -679,8 +679,8 @@ it.layer(NodeServices.layer)("effect-acp client", (it) => { |
| 679 | const successfulHandlers = yield* Ref.make(0); | 679 | const successfulHandlers = yield* Ref.make(0); |
| 680 | const handle = yield* makeHandle(); | 680 | const handle = yield* makeHandle(); |
| 681 | const scope = yield* Scope.make(); | 681 | const scope = yield* Scope.make(); |
| 682 | const acpLayer = AcpClient.layerChildProcess(handle); | 682 | const layerAcp = AcpClient.layerChildProcess(handle); |
| 683 | const context = yield* Layer.buildWithScope(acpLayer, scope); | 683 | const context = yield* Layer.buildWithScope(layerAcp, scope); |
| 684 | 684 | ||
| 685 | yield* Effect.gen(function* () { | 685 | yield* Effect.gen(function* () { |
| 686 | const acp = yield* AcpClient.AcpClient; | 686 | const acp = yield* AcpClient.AcpClient; |
packages/effect-acp/src/client.ts+2-2| ... | @@ -971,7 +971,7 @@ export const make = Effect.fn("effect-acp/AcpClient.make")(function* ( | ... | @@ -971,7 +971,7 @@ export const make = Effect.fn("effect-acp/AcpClient.make")(function* ( |
| 971 | method, | 971 | method, |
| 972 | }); | 972 | }); |
| 973 | 973 | ||
| 974 | const clientHandlerLayer = AcpRpcs.CompatClientRpcs.toLayer( | 974 | const layerClientHandler = AcpRpcs.CompatClientRpcs.toLayer( |
| 975 | AcpRpcs.CompatClientRpcs.of({ | 975 | AcpRpcs.CompatClientRpcs.of({ |
| 976 | [CLIENT_METHODS.session_request_permission]: (payload, { requestId }) => | 976 | [CLIENT_METHODS.session_request_permission]: (payload, { requestId }) => |
| 977 | runHandler( | 977 | runHandler( |
| ... | @@ -1101,7 +1101,7 @@ export const make = Effect.fn("effect-acp/AcpClient.make")(function* ( | ... | @@ -1101,7 +1101,7 @@ export const make = Effect.fn("effect-acp/AcpClient.make")(function* ( |
| 1101 | 1101 | ||
| 1102 | yield* RpcServer.make(AcpRpcs.CompatClientRpcs).pipe( | 1102 | yield* RpcServer.make(AcpRpcs.CompatClientRpcs).pipe( |
| 1103 | Effect.provideService(RpcServer.Protocol, transport.serverProtocol), | 1103 | Effect.provideService(RpcServer.Protocol, transport.serverProtocol), |
| 1104 | Effect.provide(clientHandlerLayer), | 1104 | Effect.provide(layerClientHandler), |
| 1105 | Effect.forkScoped, | 1105 | Effect.forkScoped, |
| 1106 | ); | 1106 | ); |
| 1107 | 1107 |
packages/effect-codex-app-server/src/client.test.ts+4-4| ... | @@ -35,8 +35,8 @@ it.layer(NodeServices.layer)("effect-codex-app-server client", (it) => { | ... | @@ -35,8 +35,8 @@ it.layer(NodeServices.layer)("effect-codex-app-server client", (it) => { |
| 35 | const messageDeltas = yield* Ref.make<Array<unknown>>([]); | 35 | const messageDeltas = yield* Ref.make<Array<unknown>>([]); |
| 36 | const handle = yield* makeHandle(); | 36 | const handle = yield* makeHandle(); |
| 37 | const scope = yield* Scope.make(); | 37 | const scope = yield* Scope.make(); |
| 38 | const clientLayer = CodexClient.layerChildProcess(handle); | 38 | const layerClient = CodexClient.layerChildProcess(handle); |
| 39 | const context = yield* Layer.buildWithScope(clientLayer, scope); | 39 | const context = yield* Layer.buildWithScope(layerClient, scope); |
| 40 | 40 | ||
| 41 | const result = yield* Effect.gen(function* () { | 41 | const result = yield* Effect.gen(function* () { |
| 42 | const client = yield* CodexClient.CodexAppServerClient; | 42 | const client = yield* CodexClient.CodexAppServerClient; |
| ... | @@ -130,8 +130,8 @@ it.layer(NodeServices.layer)("effect-codex-app-server client", (it) => { | ... | @@ -130,8 +130,8 @@ it.layer(NodeServices.layer)("effect-codex-app-server client", (it) => { |
| 130 | CODEX_APP_SERVER_TEST_STDERR_BYTES: String(512 * 1024), | 130 | CODEX_APP_SERVER_TEST_STDERR_BYTES: String(512 * 1024), |
| 131 | }); | 131 | }); |
| 132 | const scope = yield* Scope.make(); | 132 | const scope = yield* Scope.make(); |
| 133 | const clientLayer = CodexClient.layerChildProcess(handle); | 133 | const layerClient = CodexClient.layerChildProcess(handle); |
| 134 | const context = yield* Layer.buildWithScope(clientLayer, scope); | 134 | const context = yield* Layer.buildWithScope(layerClient, scope); |
| 135 | 135 | ||
| 136 | const initialized = yield* Effect.gen(function* () { | 136 | const initialized = yield* Effect.gen(function* () { |
| 137 | const client = yield* CodexClient.CodexAppServerClient; | 137 | const client = yield* CodexClient.CodexAppServerClient; |
packages/effect-codex-app-server/test/examples/codex-app-server-probe.ts+2-2| ... | @@ -15,7 +15,7 @@ const program = Effect.gen(function* () { | ... | @@ -15,7 +15,7 @@ const program = Effect.gen(function* () { |
| 15 | shell: false, | 15 | shell: false, |
| 16 | }), | 16 | }), |
| 17 | ); | 17 | ); |
| 18 | const codexLayer = CodexClient.layerChildProcess(handle, { | 18 | const layerCodex = CodexClient.layerChildProcess(handle, { |
| 19 | logIncoming: true, | 19 | logIncoming: true, |
| 20 | logOutgoing: true, | 20 | logOutgoing: true, |
| 21 | }); | 21 | }); |
| ... | @@ -61,7 +61,7 @@ const program = Effect.gen(function* () { | ... | @@ -61,7 +61,7 @@ const program = Effect.gen(function* () { |
| 61 | cwds: [process.cwd()], | 61 | cwds: [process.cwd()], |
| 62 | }); | 62 | }); |
| 63 | yield* Console.log("skills/list", skills); | 63 | yield* Console.log("skills/list", skills); |
| 64 | }).pipe(Effect.provide(codexLayer)); | 64 | }).pipe(Effect.provide(layerCodex)); |
| 65 | }); | 65 | }); |
| 66 | 66 | ||
| 67 | program.pipe(Effect.scoped, Effect.provide(NodeServices.layer), NodeRuntime.runMain); | 67 | program.pipe(Effect.scoped, Effect.provide(NodeServices.layer), NodeRuntime.runMain); |
packages/shared/src/httpObservability.ts+1-1| ... | @@ -2,7 +2,7 @@ import * as Effect from "effect/Effect"; | ... | @@ -2,7 +2,7 @@ import * as Effect from "effect/Effect"; |
| 2 | import * as Layer from "effect/Layer"; | 2 | import * as Layer from "effect/Layer"; |
| 3 | import * as Headers from "effect/http/Headers"; | 3 | import * as Headers from "effect/http/Headers"; |
| 4 | 4 | ||
| 5 | export const httpHeaderRedactionLayer = Layer.effect( | 5 | export const layer = Layer.effect( |
| 6 | Headers.CurrentRedactedNames, | 6 | Headers.CurrentRedactedNames, |
| 7 | Effect.map(Headers.CurrentRedactedNames, (names) => [...names, "dpop"]), | 7 | Effect.map(Headers.CurrentRedactedNames, (names) => [...names, "dpop"]), |
| 8 | ); | 8 | ); |
packages/shared/src/observability.test.ts+3-3| ... | @@ -99,7 +99,7 @@ const readTraceRecords = Effect.fn("readTraceRecords")(function* (tracePath: str | ... | @@ -99,7 +99,7 @@ const readTraceRecords = Effect.fn("readTraceRecords")(function* (tracePath: str |
| 99 | .map((line) => decodeTraceRecordLine(line)); | 99 | .map((line) => decodeTraceRecordLine(line)); |
| 100 | }); | 100 | }); |
| 101 | 101 | ||
| 102 | const makeTestLayer = (tracePath: string) => | 102 | const layerTest = (tracePath: string) => |
| 103 | Layer.mergeAll( | 103 | Layer.mergeAll( |
| 104 | Layer.effect( | 104 | Layer.effect( |
| 105 | Tracer.Tracer, | 105 | Tracer.Tracer, |
| ... | @@ -550,7 +550,7 @@ describe("observability", () => { | ... | @@ -550,7 +550,7 @@ describe("observability", () => { |
| 550 | }).pipe(Effect.withSpan("child-span")); | 550 | }).pipe(Effect.withSpan("child-span")); |
| 551 | }).pipe(Effect.withSpan("parent-span")); | 551 | }).pipe(Effect.withSpan("parent-span")); |
| 552 | 552 | ||
| 553 | yield* program.pipe(Effect.provide(makeTestLayer(tracePath))); | 553 | yield* program.pipe(Effect.provide(layerTest(tracePath))); |
| 554 | }), | 554 | }), |
| 555 | ); | 555 | ); |
| 556 | 556 | ||
| ... | @@ -597,7 +597,7 @@ describe("observability", () => { | ... | @@ -597,7 +597,7 @@ describe("observability", () => { |
| 597 | Effect.exit( | 597 | Effect.exit( |
| 598 | Effect.interrupt.pipe( | 598 | Effect.interrupt.pipe( |
| 599 | Effect.withSpan("interrupt-span"), | 599 | Effect.withSpan("interrupt-span"), |
| 600 | Effect.provide(makeTestLayer(tracePath)), | 600 | Effect.provide(layerTest(tracePath)), |
| 601 | ), | 601 | ), |
| 602 | ), | 602 | ), |
| 603 | ); | 603 | ); |
packages/shared/src/observability.ts+1-1| ... | @@ -13,7 +13,7 @@ import { RotatingFileSink } from "./logging.ts"; | ... | @@ -13,7 +13,7 @@ import { RotatingFileSink } from "./logging.ts"; |
| 13 | 13 | ||
| 14 | export const OtlpProtocol = Schema.Literals(["http/json", "http/protobuf"]); | 14 | export const OtlpProtocol = Schema.Literals(["http/json", "http/protobuf"]); |
| 15 | export type OtlpProtocol = typeof OtlpProtocol.Type; | 15 | export type OtlpProtocol = typeof OtlpProtocol.Type; |
| 16 | export const otlpSerializationLayer = (protocol: OtlpProtocol) => | 16 | export const layerOtlpSerialization = (protocol: OtlpProtocol) => |
| 17 | protocol === "http/protobuf" ? OtlpSerialization.layerProtobuf : OtlpSerialization.layerJson; | 17 | protocol === "http/protobuf" ? OtlpSerialization.layerProtobuf : OtlpSerialization.layerJson; |
| 18 | 18 | ||
| 19 | /** | 19 | /** |
packages/shared/src/otelEnvironment.test.ts+6-3| ... | @@ -488,13 +488,16 @@ describe("OtelEnvironment", () => { | ... | @@ -488,13 +488,16 @@ describe("OtelEnvironment", () => { |
| 488 | { name: "encoded separators", raw: "a%2Cb=x%3Dy", attributes: ["a,b"] }, | 488 | { name: "encoded separators", raw: "a%2Cb=x%3Dy", attributes: ["a,b"] }, |
| 489 | ])("lets the exporters' own read succeed with $name", ({ raw, attributes }) => | 489 | ])("lets the exporters' own read succeed with $name", ({ raw, attributes }) => |
| 490 | Effect.gen(function* () { | 490 | Effect.gen(function* () { |
| 491 | const env = ConfigProvider.layer( | 491 | const layerEnv = ConfigProvider.layer( |
| 492 | ConfigProvider.fromEnv({ env: { OTEL_RESOURCE_ATTRIBUTES: raw } }), | 492 | ConfigProvider.fromEnv({ env: { OTEL_RESOURCE_ATTRIBUTES: raw } }), |
| 493 | ); | 493 | ); |
| 494 | const otel = yield* OtelEnvironment.load.pipe(Effect.provide(env)); | 494 | const otel = yield* OtelEnvironment.load.pipe(Effect.provide(layerEnv)); |
| 495 | const resource = yield* OtlpResource.fromConfig({ serviceName: "t3" }).pipe( | 495 | const resource = yield* OtlpResource.fromConfig({ serviceName: "t3" }).pipe( |
| 496 | Effect.provide( | 496 | Effect.provide( |
| 497 | Layer.provide(OtelEnvironment.layerResourceAttributes(otel.resourceAttributes), env), | 497 | Layer.provide( |
| 498 | OtelEnvironment.layerResourceAttributes(otel.resourceAttributes), | ||
| 499 | layerEnv, | ||
| 500 | ), | ||
| 498 | ), | 501 | ), |
| 499 | ); | 502 | ); |
| 500 | assert.deepStrictEqual( | 503 | assert.deepStrictEqual( |
packages/shared/src/relayClient.test.ts+18-18| ... | @@ -18,7 +18,7 @@ import * as RelayClient from "./relayClient.ts"; | ... | @@ -18,7 +18,7 @@ import * as RelayClient from "./relayClient.ts"; |
| 18 | // POSIX exec bits that NTFS never reports; the win32 branch skips that check. | 18 | // POSIX exec bits that NTFS never reports; the win32 branch skips that check. |
| 19 | const windowsHost = HostProcessPlatform.defaultValue() === "win32"; | 19 | const windowsHost = HostProcessPlatform.defaultValue() === "win32"; |
| 20 | 20 | ||
| 21 | const hostRuntimeLayer = (env: Record<string, string> = {}) => | 21 | const layerHostRuntime = (env: Record<string, string> = {}) => |
| 22 | Layer.mergeAll( | 22 | Layer.mergeAll( |
| 23 | Layer.succeed(HostProcessPlatform, "linux"), | 23 | Layer.succeed(HostProcessPlatform, "linux"), |
| 24 | Layer.succeed(HostProcessArchitecture, "x64"), | 24 | Layer.succeed(HostProcessArchitecture, "x64"), |
| ... | @@ -41,7 +41,7 @@ function makeHandle(exitCode = 0) { | ... | @@ -41,7 +41,7 @@ function makeHandle(exitCode = 0) { |
| 41 | }); | 41 | }); |
| 42 | } | 42 | } |
| 43 | 43 | ||
| 44 | const makeHttpClientLayer = (bytes: Uint8Array) => | 44 | const layerHttpClient = (bytes: Uint8Array) => |
| 45 | Layer.succeed( | 45 | Layer.succeed( |
| 46 | HttpClient.HttpClient, | 46 | HttpClient.HttpClient, |
| 47 | HttpClient.make((request) => | 47 | HttpClient.make((request) => |
| ... | @@ -51,7 +51,7 @@ const makeHttpClientLayer = (bytes: Uint8Array) => | ... | @@ -51,7 +51,7 @@ const makeHttpClientLayer = (bytes: Uint8Array) => |
| 51 | ), | 51 | ), |
| 52 | ); | 52 | ); |
| 53 | 53 | ||
| 54 | const makeSpawnerLayer = (commands: Array<string>) => | 54 | const layerSpawner = (commands: Array<string>) => |
| 55 | Layer.succeed( | 55 | Layer.succeed( |
| 56 | ChildProcessSpawner.ChildProcessSpawner, | 56 | ChildProcessSpawner.ChildProcessSpawner, |
| 57 | ChildProcessSpawner.make((command) => | 57 | ChildProcessSpawner.make((command) => |
| ... | @@ -101,9 +101,9 @@ describe("RelayClient", () => { | ... | @@ -101,9 +101,9 @@ describe("RelayClient", () => { |
| 101 | Effect.provide( | 101 | Effect.provide( |
| 102 | Layer.mergeAll( | 102 | Layer.mergeAll( |
| 103 | NodeServices.layer, | 103 | NodeServices.layer, |
| 104 | makeHttpClientLayer(new Uint8Array()), | 104 | layerHttpClient(new Uint8Array()), |
| 105 | makeSpawnerLayer([]), | 105 | layerSpawner([]), |
| 106 | hostRuntimeLayer(), | 106 | layerHostRuntime(), |
| 107 | ), | 107 | ), |
| 108 | ), | 108 | ), |
| 109 | ), | 109 | ), |
| ... | @@ -160,9 +160,9 @@ describe("RelayClient", () => { | ... | @@ -160,9 +160,9 @@ describe("RelayClient", () => { |
| 160 | Effect.provide( | 160 | Effect.provide( |
| 161 | Layer.mergeAll( | 161 | Layer.mergeAll( |
| 162 | NodeServices.layer, | 162 | NodeServices.layer, |
| 163 | makeHttpClientLayer(new TextEncoder().encode("test-cloudflared-binary")), | 163 | layerHttpClient(new TextEncoder().encode("test-cloudflared-binary")), |
| 164 | makeSpawnerLayer([]), | 164 | layerSpawner([]), |
| 165 | hostRuntimeLayer(), | 165 | layerHostRuntime(), |
| 166 | ), | 166 | ), |
| 167 | ), | 167 | ), |
| 168 | ), | 168 | ), |
| ... | @@ -191,9 +191,9 @@ describe("RelayClient", () => { | ... | @@ -191,9 +191,9 @@ describe("RelayClient", () => { |
| 191 | Effect.provide( | 191 | Effect.provide( |
| 192 | Layer.mergeAll( | 192 | Layer.mergeAll( |
| 193 | NodeServices.layer, | 193 | NodeServices.layer, |
| 194 | makeHttpClientLayer(new TextEncoder().encode("tampered")), | 194 | layerHttpClient(new TextEncoder().encode("tampered")), |
| 195 | makeSpawnerLayer([]), | 195 | layerSpawner([]), |
| 196 | hostRuntimeLayer(), | 196 | layerHostRuntime(), |
| 197 | ), | 197 | ), |
| 198 | ), | 198 | ), |
| 199 | ), | 199 | ), |
| ... | @@ -226,9 +226,9 @@ describe("RelayClient", () => { | ... | @@ -226,9 +226,9 @@ describe("RelayClient", () => { |
| 226 | Effect.provide( | 226 | Effect.provide( |
| 227 | Layer.mergeAll( | 227 | Layer.mergeAll( |
| 228 | NodeServices.layer, | 228 | NodeServices.layer, |
| 229 | makeHttpClientLayer(bytes), | 229 | layerHttpClient(bytes), |
| 230 | makeSpawnerLayer(commands), | 230 | layerSpawner(commands), |
| 231 | hostRuntimeLayer(), | 231 | layerHostRuntime(), |
| 232 | ), | 232 | ), |
| 233 | ), | 233 | ), |
| 234 | ); | 234 | ); |
| ... | @@ -270,9 +270,9 @@ describe("RelayClient", () => { | ... | @@ -270,9 +270,9 @@ describe("RelayClient", () => { |
| 270 | Effect.provide( | 270 | Effect.provide( |
| 271 | Layer.mergeAll( | 271 | Layer.mergeAll( |
| 272 | NodeServices.layer, | 272 | NodeServices.layer, |
| 273 | makeHttpClientLayer(new Uint8Array()), | 273 | layerHttpClient(new Uint8Array()), |
| 274 | makeSpawnerLayer([]), | 274 | layerSpawner([]), |
| 275 | hostRuntimeLayer(env), | 275 | layerHostRuntime(env), |
| 276 | ), | 276 | ), |
| 277 | ), | 277 | ), |
| 278 | ); | 278 | ); |
packages/shared/src/relayTracing.test.ts+8-11| ... | @@ -6,11 +6,8 @@ import * as Tracer from "effect/Tracer"; | ... | @@ -6,11 +6,8 @@ import * as Tracer from "effect/Tracer"; |
| 6 | import { FetchHttpClient } from "effect/http"; | 6 | import { FetchHttpClient } from "effect/http"; |
| 7 | import { vi } from "vite-plus/test"; | 7 | import { vi } from "vite-plus/test"; |
| 8 | 8 | ||
| 9 | import { | 9 | import { RelayClientTracer, withRelayClientTracing } from "./relayTracing.ts"; |
| 10 | makeRelayClientTracingLayer, | 10 | import * as RelayTracing from "./relayTracing.ts"; |
| 11 | RelayClientTracer, | ||
| 12 | withRelayClientTracing, | ||
| 13 | } from "./relayTracing.ts"; | ||
| 14 | 11 | ||
| 15 | function collectingTracer(spans: Array<string>): Tracer.Tracer { | 12 | function collectingTracer(spans: Array<string>): Tracer.Tracer { |
| 16 | return Tracer.make({ | 13 | return Tracer.make({ |
| ... | @@ -64,10 +61,10 @@ describe("withRelayClientTracing", () => { | ... | @@ -64,10 +61,10 @@ describe("withRelayClientTracing", () => { |
| 64 | 61 | ||
| 65 | it.effect("preserves nested error causes in exported relay spans", () => { | 62 | it.effect("preserves nested error causes in exported relay spans", () => { |
| 66 | const fetchFn = vi.fn<typeof fetch>(async () => new Response(null, { status: 202 })); | 63 | const fetchFn = vi.fn<typeof fetch>(async () => new Response(null, { status: 202 })); |
| 67 | const httpClientLayer = FetchHttpClient.layer.pipe( | 64 | const layerHttpClient = FetchHttpClient.layer.pipe( |
| 68 | Layer.provide(Layer.succeed(FetchHttpClient.Fetch, fetchFn)), | 65 | Layer.provide(Layer.succeed(FetchHttpClient.Fetch, fetchFn)), |
| 69 | ); | 66 | ); |
| 70 | const tracingLayer = makeRelayClientTracingLayer( | 67 | const layerTracing = RelayTracing.layer( |
| 71 | { | 68 | { |
| 72 | tracesUrl: "https://api.axiom.test/v1/traces", | 69 | tracesUrl: "https://api.axiom.test/v1/traces", |
| 73 | tracesDataset: "relay-traces", | 70 | tracesDataset: "relay-traces", |
| ... | @@ -78,18 +75,18 @@ describe("withRelayClientTracing", () => { | ... | @@ -78,18 +75,18 @@ describe("withRelayClientTracing", () => { |
| 78 | runtime: "test", | 75 | runtime: "test", |
| 79 | client: "test", | 76 | client: "test", |
| 80 | }, | 77 | }, |
| 81 | ).pipe(Layer.provide(httpClientLayer)); | 78 | ).pipe(Layer.provide(layerHttpClient)); |
| 82 | const rootCause = new Error("relay socket closed"); | 79 | const rootCause = new Error("relay socket closed"); |
| 83 | const failure = new Error("relay request failed", { cause: rootCause }); | 80 | const failure = new Error("relay request failed", { cause: rootCause }); |
| 84 | const tracedApplication = Layer.effectDiscard( | 81 | const layerTracedApplication = Layer.effectDiscard( |
| 85 | Effect.fail(failure).pipe( | 82 | Effect.fail(failure).pipe( |
| 86 | Effect.withSpan("relay.failed-operation"), | 83 | Effect.withSpan("relay.failed-operation"), |
| 87 | withRelayClientTracing, | 84 | withRelayClientTracing, |
| 88 | Effect.exit, | 85 | Effect.exit, |
| 89 | ), | 86 | ), |
| 90 | ).pipe(Layer.provide(tracingLayer)); | 87 | ).pipe(Layer.provide(layerTracing)); |
| 91 | 88 | ||
| 92 | return Layer.build(tracedApplication).pipe( | 89 | return Layer.build(layerTracedApplication).pipe( |
| 93 | Effect.scoped, | 90 | Effect.scoped, |
| 94 | Effect.andThen( | 91 | Effect.andThen( |
| 95 | Effect.sync(() => { | 92 | Effect.sync(() => { |
packages/shared/src/relayTracing.ts+3-3| ... | @@ -124,7 +124,7 @@ function nonInterferingTracer(delegate: Tracer.Tracer): Tracer.Tracer { | ... | @@ -124,7 +124,7 @@ function nonInterferingTracer(delegate: Tracer.Tracer): Tracer.Tracer { |
| 124 | }); | 124 | }); |
| 125 | } | 125 | } |
| 126 | 126 | ||
| 127 | export function makeRelayClientTracingLayer( | 127 | export function layer( |
| 128 | config: RelayClientTracingConfig | null, | 128 | config: RelayClientTracingConfig | null, |
| 129 | resource: RelayClientTracingResource, | 129 | resource: RelayClientTracingResource, |
| 130 | ): Layer.Layer<never, never, HttpClient.HttpClient> { | 130 | ): Layer.Layer<never, never, HttpClient.HttpClient> { |
| ... | @@ -132,7 +132,7 @@ export function makeRelayClientTracingLayer( | ... | @@ -132,7 +132,7 @@ export function makeRelayClientTracingLayer( |
| 132 | return Layer.succeed(RelayClientTracer, Option.none()); | 132 | return Layer.succeed(RelayClientTracer, Option.none()); |
| 133 | } | 133 | } |
| 134 | 134 | ||
| 135 | const tracerLayer = OtlpTracer.layer({ | 135 | const layerTracer = OtlpTracer.layer({ |
| 136 | url: config.tracesUrl, | 136 | url: config.tracesUrl, |
| 137 | headers: { | 137 | headers: { |
| 138 | Authorization: `Bearer ${config.tracesToken}`, | 138 | Authorization: `Bearer ${config.tracesToken}`, |
| ... | @@ -153,5 +153,5 @@ export function makeRelayClientTracingLayer( | ... | @@ -153,5 +153,5 @@ export function makeRelayClientTracingLayer( |
| 153 | return Layer.effect( | 153 | return Layer.effect( |
| 154 | RelayClientTracer, | 154 | RelayClientTracer, |
| 155 | Tracer.Tracer.pipe(Effect.map(nonInterferingTracer), Effect.asSome), | 155 | Tracer.Tracer.pipe(Effect.map(nonInterferingTracer), Effect.asSome), |
| 156 | ).pipe(Layer.provide(tracerLayer)); | 156 | ).pipe(Layer.provide(layerTracer)); |
| 157 | } | 157 | } |
packages/ssh/src/command.test.ts+9-9| ... | @@ -113,8 +113,8 @@ describe("ssh command", () => { | ... | @@ -113,8 +113,8 @@ describe("ssh command", () => { |
| 113 | const spawner = ChildProcessSpawner.make(() => | 113 | const spawner = ChildProcessSpawner.make(() => |
| 114 | Effect.succeed(makeFailedProcess({ stdout: "Pairing token creation failed\n" })), | 114 | Effect.succeed(makeFailedProcess({ stdout: "Pairing token creation failed\n" })), |
| 115 | ); | 115 | ); |
| 116 | const spawnerLayer = Layer.succeed(ChildProcessSpawner.ChildProcessSpawner, spawner); | 116 | const layerSpawner = Layer.succeed(ChildProcessSpawner.ChildProcessSpawner, spawner); |
| 117 | const processLayer = Layer.mergeAll(NodeServices.layer, spawnerLayer); | 117 | const layerProcess = Layer.mergeAll(NodeServices.layer, layerSpawner); |
| 118 | 118 | ||
| 119 | return Effect.gen(function* () { | 119 | return Effect.gen(function* () { |
| 120 | const result = yield* Effect.result( | 120 | const result = yield* Effect.result( |
| ... | @@ -136,15 +136,15 @@ describe("ssh command", () => { | ... | @@ -136,15 +136,15 @@ describe("ssh command", () => { |
| 136 | assert.equal(result.failure.stdout, "Pairing token creation failed\n"); | 136 | assert.equal(result.failure.stdout, "Pairing token creation failed\n"); |
| 137 | assert.equal(result.failure.stderr, ""); | 137 | assert.equal(result.failure.stderr, ""); |
| 138 | } | 138 | } |
| 139 | }).pipe(Effect.provide(processLayer)); | 139 | }).pipe(Effect.provide(layerProcess)); |
| 140 | }); | 140 | }); |
| 141 | 141 | ||
| 142 | it.effect("redacts credentials from stdout in non-zero command failures", () => { | 142 | it.effect("redacts credentials from stdout in non-zero command failures", () => { |
| 143 | const spawner = ChildProcessSpawner.make(() => | 143 | const spawner = ChildProcessSpawner.make(() => |
| 144 | Effect.succeed(makeFailedProcess({ stdout: '{"credential":"pairing-secret"}\n' })), | 144 | Effect.succeed(makeFailedProcess({ stdout: '{"credential":"pairing-secret"}\n' })), |
| 145 | ); | 145 | ); |
| 146 | const spawnerLayer = Layer.succeed(ChildProcessSpawner.ChildProcessSpawner, spawner); | 146 | const layerSpawner = Layer.succeed(ChildProcessSpawner.ChildProcessSpawner, spawner); |
| 147 | const processLayer = Layer.mergeAll(NodeServices.layer, spawnerLayer); | 147 | const layerProcess = Layer.mergeAll(NodeServices.layer, layerSpawner); |
| 148 | 148 | ||
| 149 | return Effect.gen(function* () { | 149 | return Effect.gen(function* () { |
| 150 | const result = yield* Effect.result( | 150 | const result = yield* Effect.result( |
| ... | @@ -165,13 +165,13 @@ describe("ssh command", () => { | ... | @@ -165,13 +165,13 @@ describe("ssh command", () => { |
| 165 | assert.equal(result.failure.message, '{"credential":"[redacted]"}'); | 165 | assert.equal(result.failure.message, '{"credential":"[redacted]"}'); |
| 166 | assert.equal(result.failure.stdout, '{"credential":"[redacted]"}\n'); | 166 | assert.equal(result.failure.stdout, '{"credential":"[redacted]"}\n'); |
| 167 | } | 167 | } |
| 168 | }).pipe(Effect.provide(processLayer)); | 168 | }).pipe(Effect.provide(layerProcess)); |
| 169 | }); | 169 | }); |
| 170 | 170 | ||
| 171 | it.effect("fails commands that never finish", () => { | 171 | it.effect("fails commands that never finish", () => { |
| 172 | const spawner = ChildProcessSpawner.make(() => Effect.succeed(makeNeverFinishingProcess())); | 172 | const spawner = ChildProcessSpawner.make(() => Effect.succeed(makeNeverFinishingProcess())); |
| 173 | const spawnerLayer = Layer.succeed(ChildProcessSpawner.ChildProcessSpawner, spawner); | 173 | const layerSpawner = Layer.succeed(ChildProcessSpawner.ChildProcessSpawner, spawner); |
| 174 | const processLayer = Layer.mergeAll(NodeServices.layer, spawnerLayer, TestClock.layer()); | 174 | const layerProcess = Layer.mergeAll(NodeServices.layer, layerSpawner, TestClock.layer()); |
| 175 | 175 | ||
| 176 | return Effect.gen(function* () { | 176 | return Effect.gen(function* () { |
| 177 | const fiber = yield* Effect.forkChild( | 177 | const fiber = yield* Effect.forkChild( |
| ... | @@ -196,6 +196,6 @@ describe("ssh command", () => { | ... | @@ -196,6 +196,6 @@ describe("ssh command", () => { |
| 196 | if (Result.isFailure(result)) { | 196 | if (Result.isFailure(result)) { |
| 197 | assert.include(result.failure.message, "SSH command timed out after 1ms."); | 197 | assert.include(result.failure.message, "SSH command timed out after 1ms."); |
| 198 | } | 198 | } |
| 199 | }).pipe(Effect.provide(processLayer)); | 199 | }).pipe(Effect.provide(layerProcess)); |
| 200 | }); | 200 | }); |
| 201 | }); | 201 | }); |
packages/ssh/src/tunnel.test.ts+15-15| ... | @@ -325,14 +325,14 @@ describe("ssh tunnel scripts", () => { | ... | @@ -325,14 +325,14 @@ describe("ssh tunnel scripts", () => { |
| 325 | return makeSuccessfulProcess('loaded nvm default\n{"remotePort":3774}\n'); | 325 | return makeSuccessfulProcess('loaded nvm default\n{"remotePort":3774}\n'); |
| 326 | }), | 326 | }), |
| 327 | ); | 327 | ); |
| 328 | const spawnerLayer = Layer.succeed(ChildProcessSpawner.ChildProcessSpawner, spawner); | 328 | const layerSpawner = Layer.succeed(ChildProcessSpawner.ChildProcessSpawner, spawner); |
| 329 | const processLayer = Layer.merge(NodeServices.layer, spawnerLayer); | 329 | const layerProcess = Layer.merge(NodeServices.layer, layerSpawner); |
| 330 | 330 | ||
| 331 | return Effect.gen(function* () { | 331 | return Effect.gen(function* () { |
| 332 | const result = yield* SshTunnel.launchOrReuseRemoteServer(target, undefined, ARCHIVE); | 332 | const result = yield* SshTunnel.launchOrReuseRemoteServer(target, undefined, ARCHIVE); |
| 333 | assert.equal(result.remotePort, 3774); | 333 | assert.equal(result.remotePort, 3774); |
| 334 | assert.deepEqual(spawnedCommands[0]?.slice(-5, -1), ["sh", "-l", "-s", "--"]); | 334 | assert.deepEqual(spawnedCommands[0]?.slice(-5, -1), ["sh", "-l", "-s", "--"]); |
| 335 | }).pipe(Effect.provide(processLayer)); | 335 | }).pipe(Effect.provide(layerProcess)); |
| 336 | }); | 336 | }); |
| 337 | 337 | ||
| 338 | it.effect("allows cold remote launches to exceed the default SSH command timeout", () => { | 338 | it.effect("allows cold remote launches to exceed the default SSH command timeout", () => { |
| ... | @@ -345,8 +345,8 @@ describe("ssh tunnel scripts", () => { | ... | @@ -345,8 +345,8 @@ describe("ssh tunnel scripts", () => { |
| 345 | const spawner = ChildProcessSpawner.make(() => | 345 | const spawner = ChildProcessSpawner.make(() => |
| 346 | Effect.succeed(makeDelayedSuccessfulProcess('{"remotePort":3774}\n', 75_000)), | 346 | Effect.succeed(makeDelayedSuccessfulProcess('{"remotePort":3774}\n', 75_000)), |
| 347 | ); | 347 | ); |
| 348 | const spawnerLayer = Layer.succeed(ChildProcessSpawner.ChildProcessSpawner, spawner); | 348 | const layerSpawner = Layer.succeed(ChildProcessSpawner.ChildProcessSpawner, spawner); |
| 349 | const processLayer = Layer.mergeAll(NodeServices.layer, spawnerLayer, TestClock.layer()); | 349 | const layerProcess = Layer.mergeAll(NodeServices.layer, layerSpawner, TestClock.layer()); |
| 350 | 350 | ||
| 351 | return Effect.gen(function* () { | 351 | return Effect.gen(function* () { |
| 352 | const fiber = yield* Effect.forkChild( | 352 | const fiber = yield* Effect.forkChild( |
| ... | @@ -357,7 +357,7 @@ describe("ssh tunnel scripts", () => { | ... | @@ -357,7 +357,7 @@ describe("ssh tunnel scripts", () => { |
| 357 | 357 | ||
| 358 | const result = yield* Fiber.join(fiber); | 358 | const result = yield* Fiber.join(fiber); |
| 359 | assert.equal(result.remotePort, 3774); | 359 | assert.equal(result.remotePort, 3774); |
| 360 | }).pipe(Effect.provide(processLayer)); | 360 | }).pipe(Effect.provide(layerProcess)); |
| 361 | }); | 361 | }); |
| 362 | 362 | ||
| 363 | it.effect("gives cold archive launches a larger budget than node-script launches", () => { | 363 | it.effect("gives cold archive launches a larger budget than node-script launches", () => { |
| ... | @@ -370,8 +370,8 @@ describe("ssh tunnel scripts", () => { | ... | @@ -370,8 +370,8 @@ describe("ssh tunnel scripts", () => { |
| 370 | const spawner = ChildProcessSpawner.make(() => | 370 | const spawner = ChildProcessSpawner.make(() => |
| 371 | Effect.succeed(makeDelayedSuccessfulProcess('{"remotePort":3774}\n', 800_000)), | 371 | Effect.succeed(makeDelayedSuccessfulProcess('{"remotePort":3774}\n', 800_000)), |
| 372 | ); | 372 | ); |
| 373 | const spawnerLayer = Layer.succeed(ChildProcessSpawner.ChildProcessSpawner, spawner); | 373 | const layerSpawner = Layer.succeed(ChildProcessSpawner.ChildProcessSpawner, spawner); |
| 374 | const processLayer = Layer.mergeAll(NodeServices.layer, spawnerLayer, TestClock.layer()); | 374 | const layerProcess = Layer.mergeAll(NodeServices.layer, layerSpawner, TestClock.layer()); |
| 375 | 375 | ||
| 376 | return Effect.gen(function* () { | 376 | return Effect.gen(function* () { |
| 377 | const fiber = yield* Effect.forkChild( | 377 | const fiber = yield* Effect.forkChild( |
| ... | @@ -382,7 +382,7 @@ describe("ssh tunnel scripts", () => { | ... | @@ -382,7 +382,7 @@ describe("ssh tunnel scripts", () => { |
| 382 | 382 | ||
| 383 | const result = yield* Fiber.join(fiber); | 383 | const result = yield* Fiber.join(fiber); |
| 384 | assert.equal(result.remotePort, 3774); | 384 | assert.equal(result.remotePort, 3774); |
| 385 | }).pipe(Effect.provide(processLayer)); | 385 | }).pipe(Effect.provide(layerProcess)); |
| 386 | }); | 386 | }); |
| 387 | 387 | ||
| 388 | it("allows the remote port picker to run without a state file path", () => { | 388 | it("allows the remote port picker to run without a state file path", () => { |
| ... | @@ -453,12 +453,12 @@ describe("ssh tunnel scripts", () => { | ... | @@ -453,12 +453,12 @@ describe("ssh tunnel scripts", () => { |
| 453 | `), | 453 | `), |
| 454 | ), | 454 | ), |
| 455 | ); | 455 | ); |
| 456 | const spawnerLayer = Layer.succeed(ChildProcessSpawner.ChildProcessSpawner, spawner); | 456 | const layerSpawner = Layer.succeed(ChildProcessSpawner.ChildProcessSpawner, spawner); |
| 457 | const processLayer = Layer.merge(NodeServices.layer, spawnerLayer); | 457 | const layerProcess = Layer.merge(NodeServices.layer, layerSpawner); |
| 458 | return Effect.gen(function* () { | 458 | return Effect.gen(function* () { |
| 459 | const result = yield* SshTunnel.issueRemotePairingToken(target, undefined, ARCHIVE); | 459 | const result = yield* SshTunnel.issueRemotePairingToken(target, undefined, ARCHIVE); |
| 460 | assert.equal(result.credential, "LCL4R2TPHDKQ"); | 460 | assert.equal(result.credential, "LCL4R2TPHDKQ"); |
| 461 | }).pipe(Effect.provide(processLayer)); | 461 | }).pipe(Effect.provide(layerProcess)); |
| 462 | }); | 462 | }); |
| 463 | 463 | ||
| 464 | it.effect("accepts pretty-printed pairing JSON after remote shell startup noise", () => { | 464 | it.effect("accepts pretty-printed pairing JSON after remote shell startup noise", () => { |
| ... | @@ -481,12 +481,12 @@ describe("ssh tunnel scripts", () => { | ... | @@ -481,12 +481,12 @@ describe("ssh tunnel scripts", () => { |
| 481 | `), | 481 | `), |
| 482 | ), | 482 | ), |
| 483 | ); | 483 | ); |
| 484 | const spawnerLayer = Layer.succeed(ChildProcessSpawner.ChildProcessSpawner, spawner); | 484 | const layerSpawner = Layer.succeed(ChildProcessSpawner.ChildProcessSpawner, spawner); |
| 485 | const processLayer = Layer.merge(NodeServices.layer, spawnerLayer); | 485 | const layerProcess = Layer.merge(NodeServices.layer, layerSpawner); |
| 486 | return Effect.gen(function* () { | 486 | return Effect.gen(function* () { |
| 487 | const result = yield* SshTunnel.issueRemotePairingToken(target, undefined, ARCHIVE); | 487 | const result = yield* SshTunnel.issueRemotePairingToken(target, undefined, ARCHIVE); |
| 488 | assert.equal(result.credential, "LCL4R2TPHDKQ"); | 488 | assert.equal(result.credential, "LCL4R2TPHDKQ"); |
| 489 | }).pipe(Effect.provide(processLayer)); | 489 | }).pipe(Effect.provide(layerProcess)); |
| 490 | }); | 490 | }); |
| 491 | 491 | ||
| 492 | it.effect.each(["successful stop", "failed stop"] as const)( | 492 | it.effect.each(["successful stop", "failed stop"] as const)( |
packages/tailscale/src/tailscale.test.ts+12-12| ... | @@ -101,20 +101,20 @@ function neverFinishingMockHandle() { | ... | @@ -101,20 +101,20 @@ function neverFinishingMockHandle() { |
| 101 | 101 | ||
| 102 | // The executable name depends on the host platform (`tailscale.exe` on | 102 | // The executable name depends on the host platform (`tailscale.exe` on |
| 103 | // Windows), so pin it: these tests assert the posix spelling. | 103 | // Windows), so pin it: these tests assert the posix spelling. |
| 104 | function spawnerLayer(spawner: ChildProcessSpawner.ChildProcessSpawner["Service"]) { | 104 | function layerSpawner(spawner: ChildProcessSpawner.ChildProcessSpawner["Service"]) { |
| 105 | return Layer.merge( | 105 | return Layer.merge( |
| 106 | Layer.succeed(ChildProcessSpawner.ChildProcessSpawner, spawner), | 106 | Layer.succeed(ChildProcessSpawner.ChildProcessSpawner, spawner), |
| 107 | Layer.succeed(HostProcessPlatform, "linux"), | 107 | Layer.succeed(HostProcessPlatform, "linux"), |
| 108 | ); | 108 | ); |
| 109 | } | 109 | } |
| 110 | 110 | ||
| 111 | function mockSpawnerLayer( | 111 | function layerMockSpawner( |
| 112 | handler: ( | 112 | handler: ( |
| 113 | command: string, | 113 | command: string, |
| 114 | args: ReadonlyArray<string>, | 114 | args: ReadonlyArray<string>, |
| 115 | ) => { stdout?: string; stderr?: string; code?: number }, | 115 | ) => { stdout?: string; stderr?: string; code?: number }, |
| 116 | ) { | 116 | ) { |
| 117 | return spawnerLayer( | 117 | return layerSpawner( |
| 118 | ChildProcessSpawner.make((command) => { | 118 | ChildProcessSpawner.make((command) => { |
| 119 | const childProcess = command as unknown as { | 119 | const childProcess = command as unknown as { |
| 120 | readonly command: string; | 120 | readonly command: string; |
| ... | @@ -178,7 +178,7 @@ describe("tailscale", () => { | ... | @@ -178,7 +178,7 @@ describe("tailscale", () => { |
| 178 | ); | 178 | ); |
| 179 | 179 | ||
| 180 | it.effect("reads tailscale status through the process spawner service", () => { | 180 | it.effect("reads tailscale status through the process spawner service", () => { |
| 181 | const layer = mockSpawnerLayer((command, args) => { | 181 | const layer = layerMockSpawner((command, args) => { |
| 182 | assert.equal(command, "tailscale"); | 182 | assert.equal(command, "tailscale"); |
| 183 | assert.deepEqual(args, ["status", "--json"]); | 183 | assert.deepEqual(args, ["status", "--json"]); |
| 184 | return { | 184 | return { |
| ... | @@ -203,7 +203,7 @@ describe("tailscale", () => { | ... | @@ -203,7 +203,7 @@ describe("tailscale", () => { |
| 203 | method: "spawn", | 203 | method: "spawn", |
| 204 | cause: systemCause, | 204 | cause: systemCause, |
| 205 | }); | 205 | }); |
| 206 | const layer = spawnerLayer(ChildProcessSpawner.make(() => Effect.fail(cause))); | 206 | const layer = layerSpawner(ChildProcessSpawner.make(() => Effect.fail(cause))); |
| 207 | 207 | ||
| 208 | return Effect.gen(function* () { | 208 | return Effect.gen(function* () { |
| 209 | const error = yield* readTailscaleStatus.pipe(Effect.flip, Effect.provide(layer)); | 209 | const error = yield* readTailscaleStatus.pipe(Effect.flip, Effect.provide(layer)); |
| ... | @@ -224,7 +224,7 @@ describe("tailscale", () => { | ... | @@ -224,7 +224,7 @@ describe("tailscale", () => { |
| 224 | // inside an `Effect.callback` registration, so that throw arrives as a | 224 | // inside an `Effect.callback` registration, so that throw arrives as a |
| 225 | // defect rather than a typed error - the shape reproduced here. | 225 | // defect rather than a typed error - the shape reproduced here. |
| 226 | const defect = Object.assign(new Error("spawn tailscale ENOTDIR"), { code: "ENOTDIR" }); | 226 | const defect = Object.assign(new Error("spawn tailscale ENOTDIR"), { code: "ENOTDIR" }); |
| 227 | const layer = spawnerLayer( | 227 | const layer = layerSpawner( |
| 228 | ChildProcessSpawner.make(() => | 228 | ChildProcessSpawner.make(() => |
| 229 | Effect.callback<never, never>(() => { | 229 | Effect.callback<never, never>(() => { |
| 230 | throw defect; | 230 | throw defect; |
| ... | @@ -257,7 +257,7 @@ describe("tailscale", () => { | ... | @@ -257,7 +257,7 @@ describe("tailscale", () => { |
| 257 | }); | 257 | }); |
| 258 | 258 | ||
| 259 | it.effect("keeps nonzero exit diagnostics structured", () => { | 259 | it.effect("keeps nonzero exit diagnostics structured", () => { |
| 260 | const layer = mockSpawnerLayer(() => ({ | 260 | const layer = layerMockSpawner(() => ({ |
| 261 | code: 7, | 261 | code: 7, |
| 262 | stderr: "not logged in tskey-auth-secret-token-value", | 262 | stderr: "not logged in tskey-auth-secret-token-value", |
| 263 | })); | 263 | })); |
| ... | @@ -282,7 +282,7 @@ describe("tailscale", () => { | ... | @@ -282,7 +282,7 @@ describe("tailscale", () => { |
| 282 | }); | 282 | }); |
| 283 | 283 | ||
| 284 | it.effect("classifies unrecognized stderr without quoting it", () => { | 284 | it.effect("classifies unrecognized stderr without quoting it", () => { |
| 285 | const layer = mockSpawnerLayer(() => ({ | 285 | const layer = layerMockSpawner(() => ({ |
| 286 | code: 3, | 286 | code: 3, |
| 287 | stderr: "something novel went wrong for node fluffy-badger tskey-auth-secret-token-value", | 287 | stderr: "something novel went wrong for node fluffy-badger tskey-auth-secret-token-value", |
| 288 | })); | 288 | })); |
| ... | @@ -302,7 +302,7 @@ describe("tailscale", () => { | ... | @@ -302,7 +302,7 @@ describe("tailscale", () => { |
| 302 | it.effect("times out tailscale status through TestClock", () => { | 302 | it.effect("times out tailscale status through TestClock", () => { |
| 303 | const layer = Layer.merge( | 303 | const layer = Layer.merge( |
| 304 | TestClock.layer(), | 304 | TestClock.layer(), |
| 305 | spawnerLayer(ChildProcessSpawner.make(() => Effect.succeed(neverFinishingMockHandle()))), | 305 | layerSpawner(ChildProcessSpawner.make(() => Effect.succeed(neverFinishingMockHandle()))), |
| 306 | ); | 306 | ); |
| 307 | 307 | ||
| 308 | return Effect.gen(function* () { | 308 | return Effect.gen(function* () { |
| ... | @@ -322,7 +322,7 @@ describe("tailscale", () => { | ... | @@ -322,7 +322,7 @@ describe("tailscale", () => { |
| 322 | }); | 322 | }); |
| 323 | 323 | ||
| 324 | it.effect("configures tailscale serve through the process spawner service", () => { | 324 | it.effect("configures tailscale serve through the process spawner service", () => { |
| 325 | const layer = mockSpawnerLayer((command, args) => { | 325 | const layer = layerMockSpawner((command, args) => { |
| 326 | assert.equal(command, "tailscale"); | 326 | assert.equal(command, "tailscale"); |
| 327 | assert.deepEqual(args, ["serve", "--bg", "--https=8443", "http://127.0.0.1:13773"]); | 327 | assert.deepEqual(args, ["serve", "--bg", "--https=8443", "http://127.0.0.1:13773"]); |
| 328 | return {}; | 328 | return {}; |
| ... | @@ -332,7 +332,7 @@ describe("tailscale", () => { | ... | @@ -332,7 +332,7 @@ describe("tailscale", () => { |
| 332 | }); | 332 | }); |
| 333 | 333 | ||
| 334 | it.effect("retains tailscale serve exit diagnostics", () => { | 334 | it.effect("retains tailscale serve exit diagnostics", () => { |
| 335 | const layer = mockSpawnerLayer(() => ({ | 335 | const layer = layerMockSpawner(() => ({ |
| 336 | code: 1, | 336 | code: 1, |
| 337 | stderr: "serve permission denied tskey-auth-secret-token-value", | 337 | stderr: "serve permission denied tskey-auth-secret-token-value", |
| 338 | })); | 338 | })); |
| ... | @@ -364,7 +364,7 @@ describe("tailscale", () => { | ... | @@ -364,7 +364,7 @@ describe("tailscale", () => { |
| 364 | readonly command: string; | 364 | readonly command: string; |
| 365 | readonly args: ReadonlyArray<string>; | 365 | readonly args: ReadonlyArray<string>; |
| 366 | }[] = []; | 366 | }[] = []; |
| 367 | const layer = mockSpawnerLayer((command, args) => { | 367 | const layer = layerMockSpawner((command, args) => { |
| 368 | commands.push({ command, args }); | 368 | commands.push({ command, args }); |
| 369 | assert.equal(command, "tailscale"); | 369 | assert.equal(command, "tailscale"); |
| 370 | assert.deepEqual(args, ["serve", "--https=8443", "off"]); | 370 | assert.deepEqual(args, ["serve", "--https=8443", "off"]); |