effect-app 4.0.0-beta.13 → 4.0.0-beta.131

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/CHANGELOG.md +503 -0
  2. package/dist/Config/SecretURL.js +2 -2
  3. package/dist/Config.d.ts +7 -0
  4. package/dist/Config.d.ts.map +1 -0
  5. package/dist/Config.js +6 -0
  6. package/dist/ConfigProvider.d.ts +39 -0
  7. package/dist/ConfigProvider.d.ts.map +1 -0
  8. package/dist/ConfigProvider.js +42 -0
  9. package/dist/{ServiceMap.d.ts → Context.d.ts} +14 -18
  10. package/dist/Context.d.ts.map +1 -0
  11. package/dist/Context.js +66 -0
  12. package/dist/Effect.d.ts +8 -9
  13. package/dist/Effect.d.ts.map +1 -1
  14. package/dist/Effect.js +3 -6
  15. package/dist/Layer.d.ts +5 -4
  16. package/dist/Layer.d.ts.map +1 -1
  17. package/dist/Layer.js +1 -1
  18. package/dist/Operations.d.ts +198 -33
  19. package/dist/Operations.d.ts.map +1 -1
  20. package/dist/Pure.d.ts +2 -2
  21. package/dist/Pure.d.ts.map +1 -1
  22. package/dist/Pure.js +13 -13
  23. package/dist/Schema/Class.d.ts +48 -10
  24. package/dist/Schema/Class.d.ts.map +1 -1
  25. package/dist/Schema/Class.js +120 -16
  26. package/dist/Schema/SpecialJsonSchema.d.ts +33 -0
  27. package/dist/Schema/SpecialJsonSchema.d.ts.map +1 -0
  28. package/dist/Schema/SpecialJsonSchema.js +122 -0
  29. package/dist/Schema/SpecialOpenApi.d.ts +32 -0
  30. package/dist/Schema/SpecialOpenApi.d.ts.map +1 -0
  31. package/dist/Schema/SpecialOpenApi.js +123 -0
  32. package/dist/Schema/brand.d.ts +10 -1
  33. package/dist/Schema/brand.d.ts.map +1 -1
  34. package/dist/Schema/brand.js +1 -1
  35. package/dist/Schema/email.d.ts.map +1 -1
  36. package/dist/Schema/email.js +9 -4
  37. package/dist/Schema/ext.d.ts +112 -47
  38. package/dist/Schema/ext.d.ts.map +1 -1
  39. package/dist/Schema/ext.js +115 -53
  40. package/dist/Schema/moreStrings.d.ts +110 -10
  41. package/dist/Schema/moreStrings.d.ts.map +1 -1
  42. package/dist/Schema/moreStrings.js +19 -10
  43. package/dist/Schema/numbers.d.ts +126 -14
  44. package/dist/Schema/numbers.d.ts.map +1 -1
  45. package/dist/Schema/numbers.js +10 -9
  46. package/dist/Schema/phoneNumber.d.ts.map +1 -1
  47. package/dist/Schema/phoneNumber.js +8 -3
  48. package/dist/Schema/strings.d.ts +36 -4
  49. package/dist/Schema/strings.d.ts.map +1 -1
  50. package/dist/Schema/strings.js +1 -1
  51. package/dist/Schema.d.ts +74 -55
  52. package/dist/Schema.d.ts.map +1 -1
  53. package/dist/Schema.js +85 -64
  54. package/dist/client/apiClientFactory.d.ts +12 -28
  55. package/dist/client/apiClientFactory.d.ts.map +1 -1
  56. package/dist/client/apiClientFactory.js +16 -17
  57. package/dist/client/clientFor.d.ts +6 -5
  58. package/dist/client/clientFor.d.ts.map +1 -1
  59. package/dist/client/errors.d.ts +18 -9
  60. package/dist/client/errors.d.ts.map +1 -1
  61. package/dist/client/errors.js +35 -10
  62. package/dist/client/makeClient.d.ts +73 -28
  63. package/dist/client/makeClient.d.ts.map +1 -1
  64. package/dist/client/makeClient.js +49 -23
  65. package/dist/http/Request.d.ts.map +1 -1
  66. package/dist/http/Request.js +5 -5
  67. package/dist/ids.d.ts +2 -2
  68. package/dist/ids.d.ts.map +1 -1
  69. package/dist/ids.js +3 -2
  70. package/dist/index.d.ts +3 -7
  71. package/dist/index.d.ts.map +1 -1
  72. package/dist/index.js +4 -8
  73. package/dist/middleware.d.ts +2 -2
  74. package/dist/middleware.d.ts.map +1 -1
  75. package/dist/middleware.js +3 -3
  76. package/dist/rpc/MiddlewareMaker.d.ts +4 -3
  77. package/dist/rpc/MiddlewareMaker.d.ts.map +1 -1
  78. package/dist/rpc/MiddlewareMaker.js +23 -24
  79. package/dist/rpc/RpcContextMap.d.ts +2 -2
  80. package/dist/rpc/RpcContextMap.d.ts.map +1 -1
  81. package/dist/rpc/RpcContextMap.js +4 -4
  82. package/dist/rpc/RpcMiddleware.d.ts +4 -3
  83. package/dist/rpc/RpcMiddleware.d.ts.map +1 -1
  84. package/dist/rpc/RpcMiddleware.js +1 -1
  85. package/dist/utils/gen.d.ts +1 -1
  86. package/dist/utils/gen.d.ts.map +1 -1
  87. package/dist/utils/logger.d.ts +2 -2
  88. package/dist/utils/logger.d.ts.map +1 -1
  89. package/dist/utils/logger.js +3 -3
  90. package/dist/utils.d.ts +7 -1
  91. package/dist/utils.d.ts.map +1 -1
  92. package/dist/utils.js +8 -2
  93. package/package.json +30 -14
  94. package/src/Config/SecretURL.ts +1 -1
  95. package/src/Config.ts +14 -0
  96. package/src/ConfigProvider.ts +48 -0
  97. package/src/{ServiceMap.ts → Context.ts} +51 -59
  98. package/src/Effect.ts +11 -14
  99. package/src/Layer.ts +5 -4
  100. package/src/Pure.ts +17 -18
  101. package/src/Schema/Class.ts +157 -30
  102. package/src/Schema/SpecialJsonSchema.ts +137 -0
  103. package/src/Schema/SpecialOpenApi.ts +130 -0
  104. package/src/Schema/brand.ts +18 -3
  105. package/src/Schema/email.ts +10 -2
  106. package/src/Schema/ext.ts +196 -87
  107. package/src/Schema/moreStrings.ts +31 -17
  108. package/src/Schema/numbers.ts +14 -13
  109. package/src/Schema/phoneNumber.ts +8 -1
  110. package/src/Schema/strings.ts +4 -4
  111. package/src/Schema.ts +195 -104
  112. package/src/client/apiClientFactory.ts +104 -112
  113. package/src/client/clientFor.ts +6 -1
  114. package/src/client/errors.ts +42 -17
  115. package/src/client/makeClient.ts +150 -61
  116. package/src/http/Request.ts +7 -4
  117. package/src/ids.ts +2 -1
  118. package/src/index.ts +3 -10
  119. package/src/middleware.ts +2 -2
  120. package/src/rpc/MiddlewareMaker.ts +33 -44
  121. package/src/rpc/RpcContextMap.ts +6 -5
  122. package/src/rpc/RpcMiddleware.ts +5 -4
  123. package/src/utils/gen.ts +1 -1
  124. package/src/utils/logger.ts +2 -2
  125. package/src/utils.ts +8 -4
  126. package/test/dist/moreStrings.test.d.ts.map +1 -0
  127. package/test/dist/rpc.test.d.ts.map +1 -1
  128. package/test/dist/secretURL.test.d.ts.map +1 -0
  129. package/test/dist/special.test.d.ts.map +1 -0
  130. package/test/moreStrings.test.ts +17 -0
  131. package/test/rpc.test.ts +30 -6
  132. package/test/schema.test.ts +517 -4
  133. package/test/secretURL.test.ts +157 -0
  134. package/test/special.test.ts +862 -0
  135. package/test/utils.test.ts +2 -2
  136. package/tsconfig.base.json +0 -1
  137. package/tsconfig.json +0 -1
  138. package/dist/ServiceMap.d.ts.map +0 -1
  139. package/dist/ServiceMap.js +0 -91
@@ -1,3 +1,4 @@
1
+ import { SchemaTransformation } from "effect"
1
2
  import { type GetContextConfig, type GetEffectError, type RequestContextMapTagAny } from "../rpc/RpcContextMap.js"
2
3
  import * as S from "../Schema.js"
3
4
  import { AST } from "../Schema.js"
@@ -8,33 +9,42 @@ const merge = (a: any, b: Array<any>) =>
8
9
  /**
9
10
  * Whatever the input, we will only decode or encode to void
10
11
  */
11
- const ForceVoid: S.Codec<void> = S.Void as any
12
+ export const ForceVoid = S
13
+ .declare((_: unknown): _ is unknown => true)
14
+ .pipe(
15
+ S.decodeTo(S.Any, SchemaTransformation.transform<unknown, unknown>({ decode: () => void 0, encode: () => void 0 }))
16
+ )
12
17
 
13
18
  type SchemaOrFields<T> = T extends S.Top ? T : T extends S.Struct.Fields ? S.Struct<T> : S.Void
14
19
 
15
- type TaggedRequestResult<
20
+ type TaggedRequestForResult<
21
+ Self,
16
22
  Tag extends string,
17
23
  Payload extends S.Struct.Fields,
18
24
  Success extends S.Top,
19
25
  Error extends S.Top,
20
- Config = Record<string, never>
26
+ Config,
27
+ ModuleName extends string,
28
+ Type extends "command" | "query"
21
29
  > =
22
- & S.TaggedStruct<Tag, Payload>
30
+ & S.EnhancedClass<Self, S.TaggedStruct<Tag, Payload>, {}>
23
31
  & {
24
- new(...args: any[]): any
25
32
  readonly _tag: Tag
26
- readonly fields: { readonly _tag: S.tag<Tag> } & Payload
27
33
  readonly success: Success
28
34
  readonly error: Error
29
35
  readonly config: Config
30
36
  readonly "~decodingServices": S.Codec.DecodingServices<Success> | S.Codec.DecodingServices<Error>
37
+ readonly "~encodingServices": S.Codec.EncodingServices<Success> | S.Codec.EncodingServices<Error>
38
+ readonly id: `${ModuleName}.${Tag}`
39
+ readonly moduleName: ModuleName
40
+ readonly type: Type
31
41
  }
32
42
 
33
43
  export const makeRpcClient = <
34
44
  RequestContextMap extends RequestContextMapTagAny,
35
45
  GeneralErrors extends S.Top = never
36
46
  >(rcs: RequestContextMap, generalErrors?: GeneralErrors) => {
37
- // Long way around ServiceMap/C extends etc to support actual jsdoc from passed in RequestConfig etc... (??)
47
+ // Long way around Context/C extends etc to support actual jsdoc from passed in RequestConfig etc... (??)
38
48
  type ServiceMap = {
39
49
  success: S.Top | S.Struct.Fields // SchemaOrFields will make a Schema type out of Struct.Fields
40
50
  error: S.Top | S.Struct.Fields // SchemaOrFields will make a Schema type out of Struct.Fields
@@ -47,64 +57,143 @@ export const makeRpcClient = <
47
57
  : [GeneralErrors] extends [never] ? GetEffectError<RequestContextMap["config"], C>
48
58
  : MergeError<GetEffectError<RequestContextMap["config"], C>>
49
59
 
50
- function TaggedRequest<_Self>(): {
51
- <Tag extends string, Payload extends S.Struct.Fields, C extends ServiceMap>(
52
- tag: Tag,
53
- fields: Payload,
54
- config: RequestConfig & C
55
- ): TaggedRequestResult<Tag, Payload, SchemaOrFields<C["success"]>, ErrorResult<C>, Omit<C, "success" | "error">>
56
- <Tag extends string, Payload extends S.Struct.Fields, C extends Pick<ServiceMap, "success">>(
57
- tag: Tag,
58
- fields: Payload,
59
- config: RequestConfig & C
60
- ): TaggedRequestResult<Tag, Payload, SchemaOrFields<C["success"]>, ErrorResult<C>, Omit<C, "success" | "error">>
61
- <Tag extends string, Payload extends S.Struct.Fields, C extends Pick<ServiceMap, "error">>(
62
- tag: Tag,
63
- fields: Payload,
64
- config: RequestConfig & C
65
- ): TaggedRequestResult<Tag, Payload, S.Codec<void>, ErrorResult<C>, Omit<C, "success" | "error">>
66
- <Tag extends string, Payload extends S.Struct.Fields, C extends Record<string, any>>(
67
- tag: Tag,
68
- fields: Payload,
69
- config: C & RequestConfig
70
- ): TaggedRequestResult<Tag, Payload, S.Codec<void>, ErrorResult<C>, Omit<C, "success" | "error">>
71
- <Tag extends string, Payload extends S.Struct.Fields>(
72
- tag: Tag,
73
- fields: Payload
74
- ): TaggedRequestResult<Tag, Payload, S.Codec<void>, ErrorResult<never>, Record<string, never>>
75
- } {
76
- // TODO: filter errors based on config + take care of inversion
77
- const errorSchemas = Object.values(rcs.config).map((_) => _.error)
78
- return (<Tag extends string, Fields extends S.Struct.Fields, C extends ServiceMap>(
79
- tag: Tag,
80
- fields: Fields,
81
- config?: C
82
- ) => {
83
- // TODO: S.TaggedRequest removed in v4 — needs rework to use Rpc.make or Request.TaggedClass
84
- // For now, creating a simple tagged struct class with success/failure properties
85
- const failureSchema = merge(
86
- config?.error ? S.isSchema(config.error) ? config.error : S.Struct(config.error) : undefined,
87
- [...errorSchemas, generalErrors].filter(Boolean)
88
- )
89
- const successSchema = config?.success
90
- ? S.isSchema(config.success)
91
- ? AST.isVoid(config.success.ast) ? ForceVoid : config.success
92
- : S.Struct(config.success)
93
- : ForceVoid
60
+ // TODO: filter errors based on config + take care of inversion
61
+ const errorSchemas = Object.values(rcs.config).map((_) => _.error)
94
62
 
95
- const RequestClass = S.TaggedClass<any>()(tag, fields)
96
- Object.assign(RequestClass, {
97
- _tag: tag,
98
- success: successSchema,
99
- error: failureSchema,
100
- config
101
- })
63
+ function makeRequestClass<Tag extends string, Fields extends S.Struct.Fields, C extends ServiceMap>(
64
+ tag: Tag,
65
+ fields: Fields,
66
+ config?: C
67
+ ) {
68
+ const failureSchema = merge(
69
+ config?.error ? S.isSchema(config.error) ? config.error : S.Struct(config.error) : undefined,
70
+ [...errorSchemas, generalErrors].filter(Boolean)
71
+ )
72
+ const successSchema = config?.success
73
+ ? S.isSchema(config.success)
74
+ ? AST.isVoid(config.success.ast) ? ForceVoid : config.success
75
+ : S.Struct(config.success)
76
+ : ForceVoid
102
77
 
103
- return RequestClass
104
- }) as any
78
+ const RequestClass = S.TaggedClass<any>()(tag, fields)
79
+ Object.assign(RequestClass, {
80
+ _tag: tag,
81
+ success: successSchema,
82
+ error: failureSchema,
83
+ config
84
+ })
85
+
86
+ return RequestClass
87
+ }
88
+
89
+ function makeTaggedRequestWithMeta<ModuleName extends string, Type extends "command" | "query">(
90
+ moduleName: ModuleName,
91
+ type: Type
92
+ ) {
93
+ function TaggedRequestWithMeta<Self>(): {
94
+ <Tag extends string, Payload extends S.Struct.Fields, C extends ServiceMap>(
95
+ tag: Tag,
96
+ fields: Payload,
97
+ config: RequestConfig & C
98
+ ): TaggedRequestForResult<
99
+ Self,
100
+ Tag,
101
+ Payload,
102
+ SchemaOrFields<C["success"]>,
103
+ ErrorResult<C>,
104
+ Omit<C, "success" | "error">,
105
+ ModuleName,
106
+ Type
107
+ >
108
+ <Tag extends string, Payload extends S.Struct.Fields, C extends Pick<ServiceMap, "success">>(
109
+ tag: Tag,
110
+ fields: Payload,
111
+ config: RequestConfig & C
112
+ ): TaggedRequestForResult<
113
+ Self,
114
+ Tag,
115
+ Payload,
116
+ SchemaOrFields<C["success"]>,
117
+ ErrorResult<C>,
118
+ Omit<C, "success" | "error">,
119
+ ModuleName,
120
+ Type
121
+ >
122
+ <Tag extends string, Payload extends S.Struct.Fields, C extends Pick<ServiceMap, "error">>(
123
+ tag: Tag,
124
+ fields: Payload,
125
+ config: RequestConfig & C
126
+ ): TaggedRequestForResult<
127
+ Self,
128
+ Tag,
129
+ Payload,
130
+ typeof ForceVoid,
131
+ ErrorResult<C>,
132
+ Omit<C, "success" | "error">,
133
+ ModuleName,
134
+ Type
135
+ >
136
+ <Tag extends string, Payload extends S.Struct.Fields, C extends Record<string, any>>(
137
+ tag: Tag,
138
+ fields: Payload,
139
+ config: C & RequestConfig
140
+ ): TaggedRequestForResult<
141
+ Self,
142
+ Tag,
143
+ Payload,
144
+ typeof ForceVoid,
145
+ ErrorResult<C>,
146
+ Omit<C, "success" | "error">,
147
+ ModuleName,
148
+ Type
149
+ >
150
+ <Tag extends string, Payload extends S.Struct.Fields>(
151
+ tag: Tag,
152
+ fields: Payload
153
+ ): TaggedRequestForResult<
154
+ Self,
155
+ Tag,
156
+ Payload,
157
+ typeof ForceVoid,
158
+ ErrorResult<{}>,
159
+ Record<string, never>,
160
+ ModuleName,
161
+ Type
162
+ >
163
+ } {
164
+ return (<Tag extends string, Fields extends S.Struct.Fields, C extends ServiceMap>(
165
+ tag: Tag,
166
+ fields: Fields,
167
+ config?: C
168
+ ) => {
169
+ const cls = makeRequestClass(tag, fields, config)
170
+ Object.assign(cls, { id: `${moduleName}.${tag}`, moduleName, type })
171
+ return cls
172
+ }) as any
173
+ }
174
+ return Object.assign(TaggedRequestWithMeta, { moduleName, type } as const)
175
+ }
176
+
177
+ function TaggedRequestFor<ModuleName extends string>(moduleName: ModuleName) {
178
+ const Query = makeTaggedRequestWithMeta(moduleName, "query")
179
+ const Command = makeTaggedRequestWithMeta(moduleName, "command")
180
+
181
+ return {
182
+ moduleName,
183
+ /**
184
+ * Create query request classes for this module.
185
+ * Queries read state and should not mutate server state.
186
+ */
187
+ Query,
188
+ /**
189
+ * Create command request classes for this module.
190
+ * Commands mutate state and should avoid returning complex read models.
191
+ */
192
+ Command
193
+ } as const
105
194
  }
106
195
 
107
196
  return {
108
- TaggedRequest
197
+ TaggedRequestFor
109
198
  }
110
199
  }
@@ -1,3 +1,4 @@
1
+ import { Option } from "effect"
1
2
  import type { HttpClientResponse } from "effect/unstable/http/HttpClientResponse"
2
3
  import * as Effect from "../Effect.js"
3
4
  import { HttpClient, HttpClientError, HttpClientRequest, HttpHeaders } from "./internal/lib.js"
@@ -24,16 +25,18 @@ export const demandJson = (client: HttpClient.HttpClient) =>
24
25
  .mapRequest(client, (_) => HttpClientRequest.acceptJson(_))
25
26
  .pipe(HttpClient.transform((r, request) =>
26
27
  Effect.tap(r, (response) =>
27
- HttpHeaders
28
- .get(response.headers, "Content-Type")
29
- ?.startsWith("application/json")
28
+ Option
29
+ .exists(
30
+ HttpHeaders.get(response.headers, "Content-Type"),
31
+ (_) => _.startsWith("application/json")
32
+ )
30
33
  ? Effect.void
31
34
  : Effect.fail(
32
35
  new HttpClientError.DecodeError({
33
36
  request,
34
37
  response,
35
38
  description: "not json response: "
36
- + HttpHeaders.get(response.headers, "Content-Type")
39
+ + Option.getOrElse(HttpHeaders.get(response.headers, "Content-Type"), () => "<missing>")
37
40
  })
38
41
  ))
39
42
  ))
package/src/ids.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { Effect } from "effect"
1
2
  import { brandedStringId, type Codec, NonEmptyString255, StringId, type StringIdBrand, withDefaultMake } from "effect-app/Schema"
2
3
  import type { B } from "effect-app/Schema/schema"
3
4
  import type { Simplify } from "effect/Types"
@@ -17,7 +18,7 @@ export const RequestId = extendM(
17
18
  const make = StringId.make as () => NonEmptyString255
18
19
  return ({
19
20
  make,
20
- withDefault: s.pipe(S.withDefaultConstructor(make))
21
+ withDefault: S.withConstructorDefault(Effect.sync(make))(s as typeof s & S.WithoutConstructorDefault)
21
22
  })
22
23
  }
23
24
  )
package/src/index.ts CHANGED
@@ -1,24 +1,17 @@
1
1
  import "./builtin.js"
2
2
 
3
- import * as ServiceMap from "./ServiceMap.js"
4
-
5
3
  export * as Fnc from "./Function.js"
6
4
  export * as Utils from "./utils.js"
7
5
 
8
6
  export * as Array from "./Array.js"
7
+ export * as Config from "./Config.js"
8
+ export * as ConfigProvider from "./ConfigProvider.js"
9
+ export * as Context from "./Context.js"
9
10
  export * as Effect from "./Effect.js"
10
11
  export * as Layer from "./Layer.js"
11
12
  export * as NonEmptySet from "./NonEmptySet.js"
12
- export * as ServiceMap from "./ServiceMap.js"
13
13
  export * as Set from "./Set.js"
14
14
 
15
- export {
16
- /**
17
- * @deprecated use ServiceMap directly instead
18
- */
19
- ServiceMap as Context
20
- }
21
-
22
15
  export { type NonEmptyArray, type NonEmptyReadonlyArray } from "./Array.js"
23
16
 
24
17
  export * from "effect"
package/src/middleware.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
- import { ServiceMap } from "effect-app"
2
+ import { Context } from "effect-app"
3
3
  import { RpcX } from "./rpc.js"
4
4
 
5
- export class DevMode extends ServiceMap.Reference("DevMode", { defaultValue: () => false }) {}
5
+ export class DevMode extends Context.Reference("DevMode", { defaultValue: () => false }) {}
6
6
 
7
7
  export class RequestCacheMiddleware
8
8
  extends RpcX.RpcMiddleware.Tag<RequestCacheMiddleware>()("RequestCacheMiddleware")
@@ -1,11 +1,10 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
- import { Effect, Layer, type Schema, Schema as S, type Scope, ServiceMap } from "effect"
2
+ import { Effect, Layer, type Schema, Schema as S, type Scope } from "effect"
3
3
  import { type NonEmptyArray, type NonEmptyReadonlyArray } from "effect/Array"
4
4
  import { type Simplify } from "effect/Types"
5
5
  import { Rpc, type RpcGroup, type RpcSchema } from "effect/unstable/rpc"
6
6
  import { type HandlersFrom } from "effect/unstable/rpc/RpcGroup"
7
- import { type RequestId } from "effect/unstable/rpc/RpcMessage"
8
- import { type HttpHeaders } from "../http.js"
7
+ import * as Context from "../Context.js"
9
8
  import { PreludeLogger } from "../logger.js"
10
9
  import { type TypeTestId } from "../TypeTest.js"
11
10
  import { typedValuesOf } from "../utils.js"
@@ -61,13 +60,13 @@ export interface MiddlewareMaker<
61
60
  }
62
61
  >
63
62
  {
64
- readonly layer: Layer.Layer<Self, never, ServiceMap.Service.Identifier<MiddlewareProviders[number]>>
63
+ readonly layer: Layer.Layer<Self, never, Context.Service.Identifier<MiddlewareProviders[number]>>
65
64
  readonly requestContext: RequestContextTag<RequestContextMap>
66
65
  readonly requestContextMap: RequestContextMap
67
66
  }
68
67
 
69
68
  export interface RequestContextTag<RequestContextMap extends Record<string, RpcContextMap.Any>>
70
- extends ServiceMap.Service<"RequestContextConfig", GetContextConfig<RequestContextMap>>
69
+ extends Context.Service<"RequestContextConfig", GetContextConfig<RequestContextMap>>
71
70
  {}
72
71
 
73
72
  export namespace MiddlewareMaker {
@@ -258,10 +257,10 @@ export type MiddlewaresBuilder<
258
257
  : { new(_: never): {} }
259
258
  : { new(_: never): {} })
260
259
 
261
- const middlewareMaker = <
260
+ const middlewareMaker = Effect.fnUntraced(function*<
262
261
  MiddlewareProviders extends ReadonlyArray<MiddlewareMaker.Any>
263
- >(middlewares: MiddlewareProviders): Effect.Effect<
264
- RpcMiddlewareV4<
262
+ >(middlewares: MiddlewareProviders) {
263
+ type Middleware = RpcMiddlewareV4<
265
264
  MiddlewareMaker.ManyProvided<MiddlewareProviders>,
266
265
  MiddlewareMaker.ManyErrors<MiddlewareProviders>,
267
266
  Exclude<
@@ -270,42 +269,32 @@ const middlewareMaker = <
270
269
  > extends never ? never
271
270
  : Exclude<MiddlewareMaker.ManyRequired<MiddlewareProviders>, MiddlewareMaker.ManyProvided<MiddlewareProviders>>
272
271
  >
273
- > => {
272
+ type Next = Parameters<Middleware>[0]
273
+ type Options = Parameters<Middleware>[1]
274
+
274
275
  // we want to run them in reverse order because latter middlewares will provide context to former ones
275
- middlewares = middlewares.toReversed() as any
276
-
277
- return Effect.gen(function*() {
278
- const context = yield* Effect.services()
279
-
280
- // returns a Effect/RpcMiddlewareV4 with Scope.Scope in requirements
281
- // v4: wrap middleware takes (effect, options) as two params instead of a single options bag
282
- return (
283
- next: Effect.Effect<any, any, any>,
284
- options: {
285
- readonly clientId: number
286
- readonly requestId: RequestId
287
- readonly rpc: Rpc.AnyWithProps
288
- readonly payload: unknown
289
- readonly headers: HttpHeaders.Headers
290
- }
291
- ) => {
292
- // we start with the actual handler
293
- let handler = next
294
-
295
- // inspired from Effect/RpcMiddleware
296
- for (const tag of middlewares) {
297
- // use the tag to get the middleware from context
298
- const middleware = ServiceMap.getUnsafe(context, tag)
299
-
300
- // wrap the current handler, allowing the middleware to run before and after it
301
- handler = PreludeLogger.logDebug("Applying middleware wrap " + tag.key).pipe(
302
- Effect.andThen(middleware(handler, options))
303
- ) as any
304
- }
305
- return handler
276
+ const reversed = middlewares.toReversed()
277
+ const context = yield* Effect.context()
278
+
279
+ // returns a Effect/RpcMiddlewareV4 with Scope.Scope in requirements
280
+ // v4: wrap middleware takes (effect, options) as two params instead of a single options bag
281
+ return (next: Next, options: Options) => {
282
+ // we start with the actual handler
283
+ let handler = next
284
+
285
+ // inspired from Effect/RpcMiddleware
286
+ for (const tag of reversed) {
287
+ // use the tag to get the middleware from context
288
+ const middleware = Context.getUnsafe(context, tag)
289
+
290
+ // wrap the current handler, allowing the middleware to run before and after it
291
+ handler = PreludeLogger.logDebug("Applying middleware wrap " + tag.key).pipe(
292
+ Effect.andThen(middleware(handler, options))
293
+ ) as any
306
294
  }
307
- }) as any
308
- }
295
+ return handler
296
+ }
297
+ })
309
298
 
310
299
  const makeMiddlewareBasic = <Self>() =>
311
300
  // by setting RequestContextMap beforehand, execute contextual typing does not fuck up itself to anys
@@ -367,7 +356,7 @@ const makeMiddlewareBasic = <Self>() =>
367
356
  return Object.assign(MiddlewareMaker, {
368
357
  layer,
369
358
  // tag to be used to retrieve the RequestContextConfig from Rpc annotations
370
- requestContext: ServiceMap.Service<"RequestContextConfig", GetContextConfig<RequestContextMap>>(
359
+ requestContext: Context.Service<"RequestContextConfig", GetContextConfig<RequestContextMap>>(
371
360
  "RequestContextConfig"
372
361
  ),
373
362
  requestContextMap: rcm
@@ -380,7 +369,7 @@ export const Tag = <Self>() =>
380
369
  RequestContextMap extends RequestContextMapTagAny
381
370
  >(id: Id, rcm: RequestContextMap): MiddlewaresBuilder<Self, Id, RequestContextMap["config"]> => {
382
371
  let allMiddleware: MiddlewareMaker.Any[] = []
383
- const requestContext = ServiceMap.Service<"RequestContextConfig", GetContextConfig<RequestContextMap["config"]>>(
372
+ const requestContext = Context.Service<"RequestContextConfig", GetContextConfig<RequestContextMap["config"]>>(
384
373
  "RequestContextConfig"
385
374
  )
386
375
  const it = {
@@ -2,14 +2,15 @@
2
2
  /* eslint-disable @typescript-eslint/no-unsafe-return */
3
3
  /* eslint-disable @typescript-eslint/no-explicit-any */
4
4
 
5
- import { type Schema as S, ServiceMap } from "effect"
5
+ import { type Schema as S } from "effect"
6
6
  import { type AnyWithProps } from "effect/unstable/rpc/Rpc"
7
+ import * as Context from "../Context.js"
7
8
  import { type RpcDynamic } from "./RpcMiddleware.js"
8
9
 
9
10
  type Values<T extends Record<any, any>> = T[keyof T]
10
11
 
11
12
  /**
12
- * Middleware is inactivate by default, the Key is optional in route context, and the service is optionally provided as Effect ServiceMap.
13
+ * Middleware is inactivate by default, the Key is optional in route context, and the service is optionally provided as Effect Context.
13
14
  * Unless explicitly configured as `true`.
14
15
  */
15
16
  export type RpcContextMap<Service, E> = {
@@ -22,7 +23,7 @@ export type RpcContextMap<Service, E> = {
22
23
 
23
24
  export declare namespace RpcContextMap {
24
25
  /**
25
- * Middleware is active by default, and provides the Service at Key in route context, and the Service is provided as Effect ServiceMap.
26
+ * Middleware is active by default, and provides the Service at Key in route context, and the Service is provided as Effect Context.
26
27
  * Unless explicitly omitted.
27
28
  */
28
29
  export type Inverted<Service, E> = {
@@ -97,7 +98,7 @@ export type GetEffectError<RequestContextMap extends Record<string, RpcContextMa
97
98
  }
98
99
  >
99
100
 
100
- const tag = ServiceMap.Service("RequestContextConfig")
101
+ const tag = Context.Service("RequestContextConfig")
101
102
 
102
103
  export const makeMap = <const Config extends Record<string, RpcContextMap.Any>>(config: Config) => {
103
104
  const cls = class {
@@ -109,7 +110,7 @@ export const makeMap = <const Config extends Record<string, RpcContextMap.Any>>(
109
110
  return Object.assign(cls, {
110
111
  config, /** Retrieves RequestContextConfig out of the Rpc annotations */
111
112
  getConfig: (rpc: AnyWithProps): GetContextConfig<Config> => {
112
- return ServiceMap.getOrElse(rpc.annotations, tag as any, () => ({}))
113
+ return Context.getOrElse(rpc.annotations, tag as any, () => ({}))
113
114
  },
114
115
  /** Adapter used when setting the dynamic prop on a middleware implementation */
115
116
  get: <
@@ -1,10 +1,11 @@
1
1
  /* eslint-disable @typescript-eslint/no-unsafe-assignment */
2
2
  /* eslint-disable @typescript-eslint/no-unsafe-return */
3
3
  /* eslint-disable @typescript-eslint/no-explicit-any */
4
- import { type Effect, type Schema, type Schema as S, type Scope, type ServiceMap, type Stream } from "effect"
4
+ import { type Effect, type Schema, type Schema as S, type Scope, type Stream } from "effect"
5
5
  import { type NonEmptyReadonlyArray } from "effect/Array"
6
6
  import { type Rpc, RpcMiddleware } from "effect/unstable/rpc"
7
7
  import { type TypeId } from "effect/unstable/rpc/RpcMiddleware"
8
+ import type * as Context from "../Context.js"
8
9
  import { type GetEffectContext, type RpcContextMap } from "./RpcContextMap.js"
9
10
 
10
11
  export type RpcMiddlewareV4<Provides, E, Requires> = RpcMiddleware.RpcMiddleware<Provides, E, Requires>
@@ -102,8 +103,8 @@ export declare namespace TagClass {
102
103
  requires?: any
103
104
  provides?: any
104
105
  }
105
- > extends ServiceMap.Service<Self, Service> {
106
- new(_: never): ServiceMap.ServiceClass.Shape<Name, Service>
106
+ > extends Context.Service<Self, Service> {
107
+ new(_: never): Context.ServiceClass.Shape<Name, Service>
107
108
  readonly [TypeId]: TypeId
108
109
  readonly optional: Optional<Options>
109
110
  readonly error: FailureSchema<Options>
@@ -226,7 +227,7 @@ export type ExtractProvides<R extends Rpc.Any, Tag extends string> = R extends
226
227
  Rpc.Rpc<Tag, infer _Payload, infer _Success, infer _Error, infer _Middleware, infer _Requires> ? _Middleware extends {
227
228
  readonly provides: infer _P
228
229
  } ? [_P] extends [never] ? never
229
- : _P /*_P extends ServiceMap.Service<infer _I, infer _S> ? _I
230
+ : _P /*_P extends Context.Service<infer _I, infer _S> ? _I
230
231
  : never */
231
232
  : never
232
233
  : never
package/src/utils/gen.ts CHANGED
@@ -15,7 +15,7 @@ export namespace EffectGenUtils {
15
15
  : EG extends (..._: infer _3) => Generator<Yieldable<any, infer _, infer E, infer _R>, infer _A, infer _2> ? E
16
16
  : never
17
17
 
18
- export type ServiceMap<EG> = EG extends Effect<infer _A, infer _E, infer R> ? R
18
+ export type Context<EG> = EG extends Effect<infer _A, infer _E, infer R> ? R
19
19
  // there could be a case where the generator function does not yield anything, so we need to handle that
20
20
  : EG extends (..._: infer _3) => Generator<never, infer _A, infer _2> ? never
21
21
  // v4: generators can yield Yieldable (Effect, Service, etc.), all have asEffect()
@@ -2,11 +2,11 @@
2
2
  /* eslint-disable @typescript-eslint/no-explicit-any */
3
3
 
4
4
  import { Effect, type LogLevel } from "effect"
5
- import * as ServiceMap from "../ServiceMap.js"
5
+ import * as Context from "../Context.js"
6
6
 
7
7
  type Levels = "info" | "debug" | "warn" | "error"
8
8
 
9
- export class LogLevels extends ServiceMap.Reference("LogLevels", {
9
+ export class LogLevels extends Context.Reference("LogLevels", {
10
10
  defaultValue: () => new Map<string, Levels>()
11
11
  }) {}
12
12
 
package/src/utils.ts CHANGED
@@ -313,9 +313,7 @@ function decorateNew(
313
313
  if (out.descriptor) {
314
314
  out.descriptor = Object.assign({}, out.descriptor)
315
315
  }
316
- const actualDesc: PropertyDescriptor = <any> (
317
- out.descriptor || /* istanbul ignore next */ out
318
- )
316
+ const actualDesc: PropertyDescriptor = out.descriptor || /* istanbul ignore next */ out
319
317
 
320
318
  const originalMethod = validateAndExtractMethodFromDescriptor(actualDesc)
321
319
  const isStatic = inp.placement === "static"
@@ -684,7 +682,7 @@ export type OptPromise<T extends () => any> = (
684
682
  ) => Promise<ReturnType<T>> | ReturnType<T>
685
683
 
686
684
  export function access<T extends string, T2>(t: Record<T, T2>) {
687
- return (key: T) => t[key] as T2
685
+ return (key: T) => t[key]
688
686
  }
689
687
 
690
688
  export function todayAtUTCNoon() {
@@ -755,6 +753,12 @@ type CopyOriginSelf<A, U> = Equals<{}, U> extends true
755
753
 
756
754
  // just one input param: the convention is that the ctor takes an object
757
755
  // containing the properties of the class (I can't put object there as type because of contravariance)
756
+ /**
757
+ * By design this does not return `Self` directly.
758
+ *
759
+ * The return type is computed from `Self` and the update payload so callers can
760
+ * expose an explicit structural return type that remains assignable to `Self`.
761
+ */
758
762
  export const copyOrigin = <Ctor extends new(_: any) => any>(ctor: Ctor) =>
759
763
  dual<
760
764
  {
@@ -0,0 +1 @@
1
+ {"version":3,"file":"moreStrings.test.d.ts","sourceRoot":"","sources":["../moreStrings.test.ts"],"names":[],"mappings":""}
@@ -1 +1 @@
1
- {"version":3,"file":"rpc.test.d.ts","sourceRoot":"","sources":["../rpc.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AACrF,OAAO,EAAE,CAAC,EAAE,MAAM,iBAAiB,CAAA;AACnC,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;;;;;;;;;;;;;;;;;;;;;;;;AAE7C,qBAAa,iBAAkB,SAAQ,sBAIrC;CAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAIL,qBAAa,KAAM,SAAQ,UAQzB;CAAG"}
1
+ {"version":3,"file":"rpc.test.d.ts","sourceRoot":"","sources":["../rpc.test.ts"],"names":[],"mappings":"AACA,OAAO,EAAiB,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AAErF,OAAO,EAAE,CAAC,EAAE,MAAM,iBAAiB,CAAA;AACnC,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;;;;;;;;;;;;;;;;;;;;;;;;AAE7C,qBAAa,iBAAkB,SAAQ,sBAIrC;CAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAKL,qBAAa,KAAM,SAAQ,UAQzB;CAAG"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secretURL.test.d.ts","sourceRoot":"","sources":["../secretURL.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"special.test.d.ts","sourceRoot":"","sources":["../special.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,17 @@
1
+ import { S } from "effect-app"
2
+ import * as fc from "fast-check"
3
+ import { urlAlphabet } from "nanoid"
4
+ import { test } from "vitest"
5
+
6
+ const nanoidAlphabet = new Set(urlAlphabet)
7
+
8
+ const isNanoId = (value: string) => value.length === 21 && Array.from(value).every((char) => nanoidAlphabet.has(char))
9
+
10
+ test("StringId arbitrary generates nanoid-shaped values", () => {
11
+ fc.assert(
12
+ fc.property(S.toArbitrary(S.StringId), (value) => {
13
+ expect(isNanoId(value)).toBe(true)
14
+ expect(S.is(S.StringId)(value)).toBe(true)
15
+ })
16
+ )
17
+ })