diff --git a/packages/effect/src/Effect.ts b/packages/effect/src/Effect.ts index 548691208f..0d7383ace8 100644 --- a/packages/effect/src/Effect.ts +++ b/packages/effect/src/Effect.ts @@ -57,7 +57,7 @@ import * as Scheduler from "./Scheduler.js" import type * as Scope from "./Scope.js" import type * as Supervisor from "./Supervisor.js" import type * as Tracer from "./Tracer.js" -import type { Concurrency, Covariant, NoInfer, NotFunction } from "./Types.js" +import type { Concurrency, Covariant, NoInfer, NotFunction, Simplify } from "./Types.js" import type * as Unify from "./Unify.js" import type { YieldWrap } from "./Utils.js" @@ -3326,7 +3326,7 @@ export const mapInputContext: { * @category context */ export const provide: { - , ...Array>]>( + ]>( layers: Layers ): ( self: Effect @@ -3344,7 +3344,7 @@ export const provide: { ( managedRuntime: ManagedRuntime.ManagedRuntime ): (self: Effect) => Effect> - , ...Array>]>( + ]>( self: Effect, layers: Layers ): Effect< @@ -6249,6 +6249,32 @@ export declare namespace Tag { * @category models */ export type AllowedType = (Record & ProhibitedType) | string | number | symbol + + /** + * @since 2.0.0 + * @category models + */ + export type Proxy = { + [ + k in keyof Type as Type[k] extends ((...args: [...infer Args]) => infer Ret) ? + ((...args: Readonly) => Ret) extends Type[k] ? k : never + : k + ]: Type[k] extends (...args: [...infer Args]) => Effect ? + (...args: Readonly) => Effect + : Type[k] extends (...args: [...infer Args]) => infer A ? (...args: Readonly) => Effect + : Type[k] extends Effect ? Effect + : Effect + } + + /** + * @since 2.0.0 + * @category models + */ + export type ProxyUse = { + use: ( + body: (_: Type) => X + ) => X extends Effect ? Effect : Effect + } } /** @@ -6260,23 +6286,8 @@ export const Tag: (id: Id) => < Type extends Tag.AllowedType >() => & Context.TagClass - & (Type extends Record ? { - [ - k in keyof Type as Type[k] extends ((...args: [...infer Args]) => infer Ret) ? - ((...args: Readonly) => Ret) extends Type[k] ? k : never - : k - ]: Type[k] extends (...args: [...infer Args]) => Effect ? - (...args: Readonly) => Effect - : Type[k] extends (...args: [...infer Args]) => infer A ? (...args: Readonly) => Effect - : Type[k] extends Effect ? Effect - : Effect - } : - {}) - & { - use: ( - body: (_: Type) => X - ) => X extends Effect ? Effect : Effect - } = (id) => () => { + & Tag.ProxyUse + & (Type extends Record ? Tag.Proxy : {}) = (id) => () => { const limit = Error.stackTraceLimit Error.stackTraceLimit = 2 const creationError = new Error() @@ -6339,6 +6350,7 @@ export declare namespace Service { Service?: `property "Service" is forbidden` Identifier?: `property "Identifier" is forbidden` Layer?: `property "Layer" is forbidden` + Default?: `property "Layer" is forbidden` _op_layer?: `property "_op_layer" is forbidden` _op?: `property "_op" is forbidden` _tag?: `property "_tag" is forbidden` @@ -6361,34 +6373,19 @@ export declare namespace Service { * @since 2.0.0 * @category models */ - export type Maker

= { - effect: Effect, any, any> - dependencies?: never - } | { - effect: Effect, any, any> - dependencies: [] - } | { - effect: Effect, any, any> - dependencies: [Layer.Layer, ...Array>] - } | { - scoped: Effect, any, any> - dependencies?: never - } | { - scoped: Effect, any, any> - dependencies: [] - } | { - scoped: Effect, any, any> - dependencies: [Layer.Layer, ...Array>] - } | { - sync: () => AllowedType

- dependencies?: never - } | { - sync: () => AllowedType

- dependencies: [Layer.Layer, ...Array>] - } | { - sync: () => AllowedType

- dependencies: [] - } + export type WithDependencies = + | Simplify + | Simplify + | Simplify] }> + + /** + * @since 2.0.0 + * @category models + */ + export type Maker

= + | WithDependencies<{ effect: Effect, any, any> }> + | WithDependencies<{ scoped: Effect, any, any> }> + | WithDependencies<{ sync: () => AllowedType

}> /** * @since 2.0.0 @@ -6397,7 +6394,7 @@ export declare namespace Service { export interface TagClass extends Context.Tag { make(_: Type): Self - new(_: never): Type & { + new(_: Type): Type & { readonly _tag: Id } } @@ -6408,25 +6405,31 @@ export declare namespace Service { */ export type Return = & TagClass - & (Proxy extends true ? (T extends Record ? { - [ - k in keyof T as T[k] extends ((...args: [...infer Args]) => infer Ret) - ? ((...args: Readonly) => Ret) extends T[k] ? k : never - : k - ]: T[k] extends (...args: [...infer Args]) => Effect - ? (...args: Readonly) => Effect - : T[k] extends (...args: [...infer Args]) => infer A ? (...args: Readonly) => Effect - : T[k] extends Effect ? Effect - : Effect - } : - {}) : - {}) - & (Proxy extends true ? { - use: ( - body: (_: Type) => X - ) => X extends Effect ? Effect : Effect - } : - {}) + & ([Proxy] extends [true] ? Tag.Proxy : {}) + & Tag.ProxyUse + + /** + * @since 2.0.0 + * @category models + */ + export type DefaultLayer = Maker extends { + dependencies: infer Layers extends [Layer.Layer.Any, ...Array] + } ? Layer.Layer< + A, + E | { [k in keyof Layers]: Layer.Layer.Error }[number], + | Exclude }[number]> + | { [k in keyof Layers]: Layer.Layer.Context }[number] + > : + Layer.Layer + + /** + * @since 2.0.0 + * @category models + */ + export type InstanceLayers = { + readonly Layer: Layer.Layer + readonly Default: DefaultLayer + } /** * @since 2.0.0 @@ -6434,76 +6437,18 @@ export declare namespace Service { */ export type ReturnWithMaker = & {} - & Maker extends { - scoped: Effect - dependencies: infer Layers extends [Layer.Layer, ...Array>] - } ? + & Maker extends { scoped: Effect } ? & Return - & Layer.Layer< - Self, - E | { [k in keyof Layers]: Layer.Layer.Error }[number], - | Exclude }[number]> - | { [k in keyof Layers]: Layer.Layer.Context }[number] - > - & { readonly Layer: Layer.Layer> } - : Maker extends { - scoped: Effect - } ? Return & Layer.Layer> & { - readonly Layer: Layer.Layer> - } - : Maker extends { - scoped: Effect - dependencies: [] - } ? Return & Layer.Layer> & { - readonly Layer: Layer.Layer> - } - : Maker extends { - effect: Effect - dependencies: infer Layers extends [Layer.Layer, ...Array>] - } ? + & DefaultLayer, Maker> + & InstanceLayers, Maker> + : Maker extends { effect: Effect } ? & Return - & Layer.Layer< - Self, - E | { [k in keyof Layers]: Layer.Layer.Error }[number], - | Exclude }[number]> - | { [k in keyof Layers]: Layer.Layer.Context }[number] - > - & { readonly Layer: Layer.Layer> } - : Maker extends { - effect: Effect - } ? Return & Layer.Layer & { - readonly Layer: Layer.Layer - } - : Maker extends { - effect: Effect - dependencies: [] - } ? Return & Layer.Layer & { - readonly Layer: Layer.Layer - } - : Maker extends { - sync: () => infer Type - dependencies: infer Layers extends [Layer.Layer, ...Array>] - } ? + & DefaultLayer + & InstanceLayers + : Maker extends { sync: () => infer Type } ? & Return - & Layer.Layer< - Self, - { [k in keyof Layers]: Layer.Layer.Error }[number], - { [k in keyof Layers]: Layer.Layer.Context }[number] - > - & { - readonly Layer: Layer.Layer - } - : Maker extends { - sync: () => infer Type - dependencies: [] - } ? Service.Return & Layer.Layer & { - readonly Layer: Layer.Layer - } - : Maker extends { - sync: () => infer Type - } ? Return & Layer.Layer & { - readonly Layer: Layer.Layer - } + & DefaultLayer + & InstanceLayers : never } @@ -6529,7 +6474,11 @@ export const Service: { Error.stackTraceLimit = 2 const creationError = new Error() Error.stackTraceLimit = limit - function TagClass() {} + const TagClass: any = class { + constructor(args: any) { + return of(args) + } + } Object.setPrototypeOf(TagClass, TagProto) TagClass.key = id Object.defineProperty(TagClass, "stack", { @@ -6538,7 +6487,7 @@ export const Service: { } }) - TagClass["of"] = (args: any) => + TagClass["make"] = (args: any) => new Proxy(args, { get(_target: any, prop: any, _receiver) { if (prop === "_tag") { @@ -6547,14 +6496,11 @@ export const Service: { return args[prop] } }) - TagClass["make"] = TagClass["of"] - const of = TagClass["of"] + const of = TagClass["make"] if ("effect" in maker) { - // @ts-expect-error TagClass["Layer"] = layer.fromEffect(TagClass, maker["effect"]).pipe( - // @ts-expect-error layer.flatMap((c) => layer.succeedContext(Context.add(c, TagClass, of(Context.unsafeGet(c, TagClass))))) ) let live = TagClass["Layer"] @@ -6566,9 +6512,7 @@ export const Service: { } if ("scoped" in maker) { - // @ts-expect-error TagClass["Layer"] = layer.scoped(TagClass, maker["scoped"]).pipe( - // @ts-expect-error layer.flatMap((c) => layer.succeedContext(Context.add(c, TagClass, of(Context.unsafeGet(c, TagClass))))) ) let live = TagClass["Layer"] @@ -6580,9 +6524,7 @@ export const Service: { } if ("sync" in maker) { - // @ts-expect-error TagClass["Layer"] = layer.sync(TagClass, maker["sync"]).pipe( - // @ts-expect-error layer.flatMap((c) => layer.succeedContext(Context.add(c, TagClass, of(Context.unsafeGet(c, TagClass))))) ) let live = TagClass["Layer"] @@ -6592,28 +6534,26 @@ export const Service: { Object.assign(TagClass, live) Object.assign(TagClass, layer.proto) } + + // @ts-expect-error + TagClass["use"] = (body) => core.andThen(TagClass, body) + if (proxy === false) { return TagClass } + const cache = new Map() const done = new Proxy(TagClass, { get(_target: any, prop: any, _receiver) { - if (prop === "use") { - // @ts-expect-error - return (body) => core.andThen(TagClass, body) - } if (prop in TagClass) { - // @ts-expect-error return TagClass[prop] } if (cache.has(prop)) { return cache.get(prop) } const fn = (...args: Array) => - // @ts-expect-error core.andThen(TagClass, (s: any) => { if (typeof s[prop] === "function") { - // @ts-expect-error cache.set(prop, (...args: Array) => core.andThen(TagClass, (s: any) => s[prop](...args))) return s[prop](...args) } diff --git a/packages/effect/src/Layer.ts b/packages/effect/src/Layer.ts index 0e6a1ebfe4..808aa075c1 100644 --- a/packages/effect/src/Layer.ts +++ b/packages/effect/src/Layer.ts @@ -65,6 +65,14 @@ export interface Layer extends Layer.Va * @since 2.0.0 */ export declare namespace Layer { + /** + * @since 2.0.0 + * @category models + */ + export type Any = { + readonly [LayerTypeId]: any + } + /** * @since 2.0.0 * @category models @@ -80,19 +88,19 @@ export declare namespace Layer { * @since 2.0.0 * @category type-level */ - export type Context> = [T] extends [Layer] ? _RIn + export type Context = [T] extends [Layer] ? _RIn : never /** * @since 2.0.0 * @category type-level */ - export type Error> = [T] extends [Layer] ? _E + export type Error = [T] extends [Layer] ? _E : never /** * @since 2.0.0 * @category type-level */ - export type Success> = [T] extends [Layer] ? _ROut + export type Success = [T] extends [Layer] ? _ROut : never } diff --git a/packages/effect/src/internal/layer.ts b/packages/effect/src/internal/layer.ts index 97b6fa8c66..4a96e682b8 100644 --- a/packages/effect/src/internal/layer.ts +++ b/packages/effect/src/internal/layer.ts @@ -180,7 +180,7 @@ class MemoMapImpl implements Layer.MemoMap { constructor( readonly ref: Synchronized.SynchronizedRef< Map< - Layer.Layer, + Layer.Layer.Any, readonly [Effect.Effect, Scope.Scope.Finalizer] > > @@ -304,7 +304,7 @@ export const makeMemoMap: Effect.Effect = core.suspend(() => core.map( circular.makeSynchronized< Map< - Layer.Layer, + Layer.Layer.Any, readonly [ Effect.Effect, Scope.Scope.Finalizer @@ -1293,7 +1293,7 @@ const provideSomeRuntime = dual< /** @internal */ export const effect_provide = dual< { - , ...Array>]>( + ]>( layers: Layers ): ( self: Effect.Effect @@ -1317,7 +1317,7 @@ export const effect_provide = dual< ): (self: Effect.Effect) => Effect.Effect> }, { - , ...Array>]>( + ]>( self: Effect.Effect, layers: Layers ): Effect.Effect< @@ -1352,7 +1352,7 @@ export const effect_provide = dual< | Context.Context | Runtime.Runtime | ManagedRuntime.ManagedRuntime - | Array> + | Array ): Effect.Effect> => { if (Array.isArray(source)) { // @ts-expect-error diff --git a/packages/effect/test/Effect/service.test.ts b/packages/effect/test/Effect/service.test.ts index b6b974b39c..6e6166113a 100644 --- a/packages/effect/test/Effect/service.test.ts +++ b/packages/effect/test/Effect/service.test.ts @@ -32,11 +32,13 @@ class Logger extends Effect.Service()("Logger", { dependencies: [Prefix, Postfix] }) { static Test = Layer.succeed(this, Logger.make({ info: () => Effect.void })) + static TestWithNew = Layer.succeed(this, new Logger({ info: () => Effect.void })) } describe("Effect", () => { it.effect("Service correctly wires dependencies", () => Effect.gen(function*() { + messages.splice(0) const { _tag } = yield* Logger expect(_tag).toEqual("Logger") yield* Logger.info("Ok") @@ -45,7 +47,30 @@ describe("Effect", () => { expect(prefix).toEqual("PRE") const { postfix } = yield* Postfix expect(postfix).toEqual("POST") + expect(yield* Postfix.use((_) => _._tag)).toEqual("Postfix") }).pipe( Effect.provide([Logger, Prefix, Postfix]) )) + + it.effect("Test instance works", () => + Effect.gen(function*() { + messages.splice(0) + const { _tag } = yield* Logger + expect(_tag).toEqual("Logger") + yield* Logger.info("Ok") + expect(messages).toEqual([]) + }).pipe( + Effect.provide([Logger.Test]) + )) + + it.effect("Test with new instance works", () => + Effect.gen(function*() { + messages.splice(0) + const { _tag } = yield* Logger + expect(_tag).toEqual("Logger") + yield* Logger.info("Ok") + expect(messages).toEqual([]) + }).pipe( + Effect.provide([Logger.TestWithNew]) + )) })