effect 4.0.0-beta.14 → 4.0.0-beta.16

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 (82) hide show
  1. package/dist/Channel.d.ts +6 -6
  2. package/dist/Channel.d.ts.map +1 -1
  3. package/dist/Channel.js +4 -4
  4. package/dist/Channel.js.map +1 -1
  5. package/dist/Effect.d.ts +7 -7
  6. package/dist/Effect.d.ts.map +1 -1
  7. package/dist/Effect.js.map +1 -1
  8. package/dist/Schema.d.ts +43 -0
  9. package/dist/Schema.d.ts.map +1 -1
  10. package/dist/Schema.js +37 -0
  11. package/dist/Schema.js.map +1 -1
  12. package/dist/SchemaParser.d.ts +5 -0
  13. package/dist/SchemaParser.d.ts.map +1 -1
  14. package/dist/SchemaParser.js +10 -0
  15. package/dist/SchemaParser.js.map +1 -1
  16. package/dist/SchemaTransformation.d.ts +70 -3
  17. package/dist/SchemaTransformation.d.ts.map +1 -1
  18. package/dist/SchemaTransformation.js +79 -4
  19. package/dist/SchemaTransformation.js.map +1 -1
  20. package/dist/Stream.d.ts +7 -7
  21. package/dist/Stream.d.ts.map +1 -1
  22. package/dist/Stream.js +8 -6
  23. package/dist/Stream.js.map +1 -1
  24. package/dist/Types.d.ts +70 -0
  25. package/dist/Types.d.ts.map +1 -1
  26. package/dist/internal/effect.js +4 -4
  27. package/dist/internal/effect.js.map +1 -1
  28. package/dist/internal/schema/schema.js +1 -0
  29. package/dist/internal/schema/schema.js.map +1 -1
  30. package/dist/unstable/ai/LanguageModel.d.ts +2 -0
  31. package/dist/unstable/ai/LanguageModel.d.ts.map +1 -1
  32. package/dist/unstable/ai/LanguageModel.js.map +1 -1
  33. package/dist/unstable/cli/Command.d.ts +34 -4
  34. package/dist/unstable/cli/Command.d.ts.map +1 -1
  35. package/dist/unstable/cli/Command.js +73 -24
  36. package/dist/unstable/cli/Command.js.map +1 -1
  37. package/dist/unstable/cli/GlobalFlag.d.ts +21 -56
  38. package/dist/unstable/cli/GlobalFlag.d.ts.map +1 -1
  39. package/dist/unstable/cli/GlobalFlag.js +9 -48
  40. package/dist/unstable/cli/GlobalFlag.js.map +1 -1
  41. package/dist/unstable/cli/internal/command.d.ts +3 -0
  42. package/dist/unstable/cli/internal/command.d.ts.map +1 -1
  43. package/dist/unstable/cli/internal/command.js +2 -0
  44. package/dist/unstable/cli/internal/command.js.map +1 -1
  45. package/dist/unstable/cli/internal/help.d.ts +18 -4
  46. package/dist/unstable/cli/internal/help.d.ts.map +1 -1
  47. package/dist/unstable/cli/internal/help.js +61 -7
  48. package/dist/unstable/cli/internal/help.js.map +1 -1
  49. package/dist/unstable/httpapi/HttpApiBuilder.d.ts +10 -4
  50. package/dist/unstable/httpapi/HttpApiBuilder.d.ts.map +1 -1
  51. package/dist/unstable/httpapi/HttpApiBuilder.js +17 -6
  52. package/dist/unstable/httpapi/HttpApiBuilder.js.map +1 -1
  53. package/dist/unstable/httpapi/HttpApiEndpoint.d.ts +7 -2
  54. package/dist/unstable/httpapi/HttpApiEndpoint.d.ts.map +1 -1
  55. package/dist/unstable/httpapi/HttpApiEndpoint.js.map +1 -1
  56. package/dist/unstable/httpapi/OpenApi.d.ts.map +1 -1
  57. package/dist/unstable/httpapi/OpenApi.js +11 -1
  58. package/dist/unstable/httpapi/OpenApi.js.map +1 -1
  59. package/dist/unstable/reactivity/AtomHttpApi.d.ts +2 -2
  60. package/dist/unstable/reactivity/AtomHttpApi.d.ts.map +1 -1
  61. package/dist/unstable/reactivity/AtomRegistry.js +2 -6
  62. package/dist/unstable/reactivity/AtomRegistry.js.map +1 -1
  63. package/package.json +1 -1
  64. package/src/Channel.ts +24 -14
  65. package/src/Effect.ts +30 -8
  66. package/src/Schema.ts +61 -0
  67. package/src/SchemaParser.ts +11 -0
  68. package/src/SchemaTransformation.ts +85 -4
  69. package/src/Stream.ts +46 -22
  70. package/src/Types.ts +66 -0
  71. package/src/internal/effect.ts +41 -14
  72. package/src/internal/schema/schema.ts +1 -0
  73. package/src/unstable/ai/LanguageModel.ts +9 -6
  74. package/src/unstable/cli/Command.ts +119 -31
  75. package/src/unstable/cli/GlobalFlag.ts +36 -114
  76. package/src/unstable/cli/internal/command.ts +5 -0
  77. package/src/unstable/cli/internal/help.ts +103 -22
  78. package/src/unstable/httpapi/HttpApiBuilder.ts +68 -13
  79. package/src/unstable/httpapi/HttpApiEndpoint.ts +13 -4
  80. package/src/unstable/httpapi/OpenApi.ts +18 -1
  81. package/src/unstable/reactivity/AtomHttpApi.ts +2 -2
  82. package/src/unstable/reactivity/AtomRegistry.ts +2 -6
@@ -4,7 +4,6 @@
4
4
 
5
5
  import * as Console from "../../Console.ts"
6
6
  import * as Effect from "../../Effect.ts"
7
- import { dual } from "../../Function.ts"
8
7
  import type { LogLevel as LogLevelType } from "../../LogLevel.ts"
9
8
  import * as Option from "../../Option.ts"
10
9
  import * as ServiceMap from "../../ServiceMap.ts"
@@ -49,18 +48,30 @@ export interface Action<A> {
49
48
  * @since 4.0.0
50
49
  * @category models
51
50
  */
52
- export interface Setting<A> extends ServiceMap.Reference<A> {
51
+ export interface Setting<Id extends string, A> extends ServiceMap.Service<Setting.Identifier<Id>, A> {
53
52
  readonly _tag: "Setting"
53
+ readonly id: Id
54
54
  readonly flag: Flag.Flag<A>
55
55
  }
56
56
 
57
+ /**
58
+ * @since 4.0.0
59
+ */
60
+ export declare namespace Setting {
61
+ /**
62
+ * @since 4.0.0
63
+ * @category models
64
+ */
65
+ export type Identifier<Id extends string> = `effect/unstable/cli/GlobalFlag/${Id}`
66
+ }
67
+
57
68
  /**
58
69
  * Global flag discriminated union.
59
70
  *
60
71
  * @since 4.0.0
61
72
  * @category models
62
73
  */
63
- export type GlobalFlag<A> = Action<A> | Setting<A>
74
+ export type GlobalFlag<A> = Action<A> | Setting<any, A>
64
75
 
65
76
  /* ========================================================================== */
66
77
  /* Constructors */
@@ -90,17 +101,19 @@ export const action = <A>(options: {
90
101
  * @since 4.0.0
91
102
  * @category constructors
92
103
  */
93
- export const setting = <A>(options: {
104
+ export const setting = <const Id extends string>(
105
+ id: Id
106
+ ) =>
107
+ <A>(options: {
94
108
  readonly flag: Flag.Flag<A>
95
- readonly defaultValue: () => A
96
- }): Setting<A> => {
109
+ }): Setting<Id, A> => {
97
110
  settingIdCounter += 1
98
- const ref = ServiceMap.Reference<A>(
99
- `effect/cli/GlobalFlag/Setting/${settingIdCounter}`,
100
- { defaultValue: options.defaultValue }
111
+ const ref = ServiceMap.Service<Setting.Identifier<Id>, A>(
112
+ `effect/unstable/cli/GlobalFlag/${id}/${settingIdCounter}`
101
113
  )
102
114
  return Object.assign(ref, {
103
115
  _tag: "Setting" as const,
116
+ id,
104
117
  flag: options.flag
105
118
  })
106
119
  }
@@ -130,7 +143,7 @@ export const Help: Action<boolean> = action({
130
143
  run: (_, { command, commandPath }) =>
131
144
  Effect.gen(function*() {
132
145
  const formatter = yield* CliOutput.Formatter
133
- const helpDoc = yield* HelpInternal.getHelpForCommandPath(command, commandPath, Registry)
146
+ const helpDoc = yield* HelpInternal.getHelpForCommandPath(command, commandPath, BuiltIns)
134
147
  yield* Console.log(formatter.formatHelpDoc(helpDoc))
135
148
  })
136
149
  })
@@ -184,7 +197,7 @@ export const Completions: Action<Option.Option<"bash" | "zsh" | "fish">> = actio
184
197
  * @since 4.0.0
185
198
  * @category references
186
199
  */
187
- export const LogLevel: Setting<Option.Option<LogLevelType>> = setting({
200
+ export const LogLevel: Setting<"log-level", Option.Option<LogLevelType>> = setting("log-level")({
188
201
  flag: Flag.choiceWithValue(
189
202
  "log-level",
190
203
  [
@@ -201,121 +214,30 @@ export const LogLevel: Setting<Option.Option<LogLevelType>> = setting({
201
214
  ).pipe(
202
215
  Flag.optional,
203
216
  Flag.withDescription("Sets the minimum log level")
204
- ),
205
- defaultValue: () => Option.none()
217
+ )
206
218
  })
207
219
 
208
220
  /* ========================================================================== */
209
- /* Registry */
221
+ /* References */
210
222
  /* ========================================================================== */
211
223
 
212
224
  /**
213
- * The ordered set of global flag references.
214
- * The parser iterates this set to know which flags to extract.
225
+ * Built-in global flags in default precedence order.
215
226
  *
216
227
  * @since 4.0.0
217
228
  * @category references
218
229
  */
219
- export const Registry: ServiceMap.Reference<
220
- Set<GlobalFlag<any>>
221
- > = ServiceMap.Reference("effect/cli/GlobalFlag/Registry", {
222
- defaultValue: () =>
223
- new Set<GlobalFlag<any>>([
224
- Help,
225
- Version,
226
- Completions,
227
- LogLevel
228
- ])
229
- })
230
-
231
- /* ========================================================================== */
232
- /* Public API */
233
- /* ========================================================================== */
230
+ export const BuiltIns: ReadonlyArray<GlobalFlag<any>> = [
231
+ Help,
232
+ Version,
233
+ Completions,
234
+ LogLevel
235
+ ]
234
236
 
235
237
  /**
236
- * Adds a global flag to the registry.
238
+ * Built-in setting context identifiers.
237
239
  *
238
240
  * @since 4.0.0
239
- * @category modifiers
240
- */
241
- export const add: {
242
- /* ========================================================================== */
243
- /* Public API */
244
- /* ========================================================================== */
245
-
246
- /**
247
- * Adds a global flag to the registry.
248
- *
249
- * @since 4.0.0
250
- * @category modifiers
251
- */
252
- <A>(flag: GlobalFlag<A>): <B, E, R>(
253
- self: Effect.Effect<B, E, R>
254
- ) => Effect.Effect<B, E, R>
255
- /* ========================================================================== */
256
- /* Public API */
257
- /* ========================================================================== */
258
-
259
- /**
260
- * Adds a global flag to the registry.
261
- *
262
- * @since 4.0.0
263
- * @category modifiers
264
- */
265
- <B, E, R, A>(self: Effect.Effect<B, E, R>, flag: GlobalFlag<A>): Effect.Effect<B, E, R>
266
- } = dual(
267
- 2,
268
- Effect.fnUntraced(function*<B, E, R, A>(
269
- self: Effect.Effect<B, E, R>,
270
- flag: GlobalFlag<A>
271
- ) {
272
- const currentRegistry = yield* Registry
273
- const nextRegistry = new Set([...currentRegistry, flag])
274
- return yield* Effect.provideService(self, Registry, nextRegistry)
275
- })
276
- )
277
-
278
- /**
279
- * Removes a global flag by its reference.
280
- *
281
- * @since 4.0.0
282
- * @category modifiers
283
- */
284
- export const remove: {
285
- /**
286
- * Removes a global flag by its reference.
287
- *
288
- * @since 4.0.0
289
- * @category modifiers
290
- */
291
- <A>(flag: GlobalFlag<A>): <B, E, R>(
292
- self: Effect.Effect<B, E, R>
293
- ) => Effect.Effect<B, E, R>
294
- /**
295
- * Removes a global flag by its reference.
296
- *
297
- * @since 4.0.0
298
- * @category modifiers
299
- */
300
- <B, E, R, A>(self: Effect.Effect<B, E, R>, flag: GlobalFlag<A>): Effect.Effect<B, E, R>
301
- } = dual(
302
- 2,
303
- Effect.fnUntraced(function*<B, E, R, A>(
304
- self: Effect.Effect<B, E, R>,
305
- flag: GlobalFlag<A>
306
- ) {
307
- const currentRegistry = yield* Registry
308
- const nextRegistry = new Set(currentRegistry)
309
- nextRegistry.delete(flag)
310
- return yield* Effect.provideService(self, Registry, nextRegistry)
311
- })
312
- )
313
-
314
- /**
315
- * Removes all global flags (built-in and user-defined).
316
- *
317
- * @since 4.0.0
318
- * @category modifiers
241
+ * @category models
319
242
  */
320
- export const clear = <B, E, R>(self: Effect.Effect<B, E, R>): Effect.Effect<B, E, R> =>
321
- Effect.provideService(self, Registry, new Set())
243
+ export type BuiltInSettingContext = Setting.Identifier<"log-level">
@@ -12,6 +12,7 @@ import { pipeArguments } from "../../../Pipeable.ts"
12
12
  import * as Predicate from "../../../Predicate.ts"
13
13
  import * as ServiceMap from "../../../ServiceMap.ts"
14
14
  import * as CliError from "../CliError.ts"
15
+ import type * as GlobalFlag from "../GlobalFlag.ts"
15
16
  import type { ArgDoc, ExampleDoc, FlagDoc, HelpDoc, SubcommandGroupDoc } from "../HelpDoc.ts"
16
17
  import * as Param from "../Param.ts"
17
18
  import * as Primitive from "../Primitive.ts"
@@ -36,6 +37,7 @@ export interface CommandInternal<Name extends string, Input, E, R> extends Comma
36
37
  readonly config: ConfigInternal
37
38
  readonly service: ServiceMap.Service<CommandContext<Name>, Input>
38
39
  readonly annotations: ServiceMap.ServiceMap<never>
40
+ readonly globalFlags: ReadonlyArray<GlobalFlag.GlobalFlag<any>>
39
41
  readonly parse: (input: ParsedTokens) => Effect.Effect<Input, CliError.CliError, Environment>
40
42
  readonly handle: (
41
43
  input: Input,
@@ -88,6 +90,7 @@ export const makeCommand = <const Name extends string, Input, E, R>(options: {
88
90
  readonly config: ConfigInternal
89
91
  readonly service?: ServiceMap.Service<CommandContext<Name>, Input> | undefined
90
92
  readonly annotations?: ServiceMap.ServiceMap<never> | undefined
93
+ readonly globalFlags?: ReadonlyArray<GlobalFlag.GlobalFlag<any>> | undefined
91
94
  readonly description?: string | undefined
92
95
  readonly shortDescription?: string | undefined
93
96
  readonly alias?: string | undefined
@@ -101,6 +104,7 @@ export const makeCommand = <const Name extends string, Input, E, R>(options: {
101
104
  const service = options.service ?? ServiceMap.Service<CommandContext<Name>, Input>(`${TypeId}/${options.name}`)
102
105
  const config = options.config
103
106
  const annotations = options.annotations ?? ServiceMap.empty()
107
+ const globalFlags = options.globalFlags ?? []
104
108
  const subcommands = options.subcommands ?? []
105
109
 
106
110
  const handle = (
@@ -191,6 +195,7 @@ export const makeCommand = <const Name extends string, Input, E, R>(options: {
191
195
  name: options.name,
192
196
  examples: options.examples ?? [],
193
197
  annotations,
198
+ globalFlags,
194
199
  subcommands,
195
200
  config,
196
201
  service,
@@ -6,46 +6,127 @@
6
6
  * Extracted from command.ts to avoid circular dependencies.
7
7
  */
8
8
  import * as Effect from "../../../Effect.ts"
9
- import type * as ServiceMap from "../../../ServiceMap.ts"
10
9
  import type { Command } from "../Command.ts"
11
- import type { GlobalFlag } from "../GlobalFlag.ts"
10
+ import type * as GlobalFlag from "../GlobalFlag.ts"
12
11
  import type { FlagDoc, HelpDoc } from "../HelpDoc.ts"
13
12
  import * as Param from "../Param.ts"
14
13
  import * as Primitive from "../Primitive.ts"
15
14
  import { toImpl } from "./command.ts"
16
15
 
16
+ const dedupeGlobalFlags = (
17
+ flags: ReadonlyArray<GlobalFlag.GlobalFlag<any>>
18
+ ): ReadonlyArray<GlobalFlag.GlobalFlag<any>> => {
19
+ const seen = new Set<GlobalFlag.GlobalFlag<any>>()
20
+ const deduped: Array<GlobalFlag.GlobalFlag<any>> = []
21
+ for (const flag of flags) {
22
+ if (seen.has(flag)) {
23
+ continue
24
+ }
25
+ seen.add(flag)
26
+ deduped.push(flag)
27
+ }
28
+ return deduped
29
+ }
30
+
17
31
  /**
18
- * Helper function to get help documentation for a specific command path.
19
- * Navigates through the command hierarchy to find the right command.
20
- * Reads global flags from the registry and includes them in the help doc.
32
+ * Returns the resolved command lineage for the provided path.
33
+ * Includes the root command as the first element.
21
34
  */
22
- export const getHelpForCommandPath = <Name extends string, Input, E, R>(
35
+ export const getCommandsForCommandPath = <Name extends string, Input, E, R>(
23
36
  command: Command<Name, Input, E, R>,
24
- commandPath: ReadonlyArray<string>,
25
- registry: ServiceMap.Reference<Set<GlobalFlag<unknown>>>
26
- ): Effect.Effect<HelpDoc, never, never> =>
27
- Effect.gen(function*() {
28
- let currentCommand: Command.Any = command
37
+ commandPath: ReadonlyArray<string>
38
+ ): ReadonlyArray<Command.Any> => {
39
+ const commands: Array<Command.Any> = [command]
40
+ let currentCommand: Command.Any = command
29
41
 
30
- for (let i = 1; i < commandPath.length; i++) {
31
- const subcommandName = commandPath[i]
32
- let subcommand: Command.Any | undefined = undefined
42
+ for (let i = 1; i < commandPath.length; i++) {
43
+ const subcommandName = commandPath[i]
44
+ let subcommand: Command.Any | undefined = undefined
33
45
 
34
- for (const group of currentCommand.subcommands) {
35
- subcommand = group.commands.find((sub) => sub.name === subcommandName)
36
- if (subcommand) {
37
- break
38
- }
46
+ for (const group of currentCommand.subcommands) {
47
+ subcommand = group.commands.find((sub) => sub.name === subcommandName)
48
+ if (subcommand) {
49
+ break
39
50
  }
51
+ }
40
52
 
41
- if (subcommand) {
42
- currentCommand = subcommand
53
+ if (!subcommand) {
54
+ break
55
+ }
56
+
57
+ commands.push(subcommand)
58
+ currentCommand = subcommand
59
+ }
60
+
61
+ return commands
62
+ }
63
+
64
+ /**
65
+ * Returns active global flags for a command path.
66
+ * Built-ins are prepended and declarations are collected root -> leaf.
67
+ */
68
+ export const getGlobalFlagsForCommandPath = <Name extends string, Input, E, R>(
69
+ command: Command<Name, Input, E, R>,
70
+ commandPath: ReadonlyArray<string>,
71
+ builtIns: ReadonlyArray<GlobalFlag.GlobalFlag<any>>
72
+ ): ReadonlyArray<GlobalFlag.GlobalFlag<any>> => {
73
+ const commands = getCommandsForCommandPath(command, commandPath)
74
+ const declared = commands.flatMap((current) => toImpl(current).globalFlags)
75
+ return dedupeGlobalFlags([
76
+ ...builtIns,
77
+ ...declared
78
+ ])
79
+ }
80
+
81
+ const collectDeclaredGlobalFlags = (command: Command.Any): ReadonlyArray<GlobalFlag.GlobalFlag<any>> => {
82
+ const collected: Array<GlobalFlag.GlobalFlag<any>> = []
83
+
84
+ const visit = (current: Command.Any): void => {
85
+ const impl = toImpl(current)
86
+ for (const flag of impl.globalFlags) {
87
+ collected.push(flag)
88
+ }
89
+ for (const group of current.subcommands) {
90
+ for (const subcommand of group.commands) {
91
+ visit(subcommand)
43
92
  }
44
93
  }
94
+ }
95
+
96
+ visit(command)
97
+ return dedupeGlobalFlags(collected)
98
+ }
99
+
100
+ /**
101
+ * Returns all global flags declared in a command tree.
102
+ * Built-ins are prepended and command declarations are deduplicated by identity.
103
+ */
104
+ export const getGlobalFlagsForCommandTree = <Name extends string, Input, E, R>(
105
+ command: Command<Name, Input, E, R>,
106
+ builtIns: ReadonlyArray<GlobalFlag.GlobalFlag<any>>
107
+ ): ReadonlyArray<GlobalFlag.GlobalFlag<any>> =>
108
+ dedupeGlobalFlags([
109
+ ...builtIns,
110
+ ...collectDeclaredGlobalFlags(command)
111
+ ])
112
+
113
+ /**
114
+ * Helper function to get help documentation for a specific command path.
115
+ * Navigates through the command hierarchy to find the right command.
116
+ * Reads active global flags for the path and includes them in the help doc.
117
+ */
118
+ export const getHelpForCommandPath = <Name extends string, Input, E, R>(
119
+ command: Command<Name, Input, E, R>,
120
+ commandPath: ReadonlyArray<string>,
121
+ builtIns: ReadonlyArray<GlobalFlag.GlobalFlag<any>>
122
+ ): Effect.Effect<HelpDoc, never, never> =>
123
+ Effect.gen(function*() {
124
+ const commands = getCommandsForCommandPath(command, commandPath)
125
+ const currentCommand = commands.length > 0 ? commands[commands.length - 1] : command
45
126
 
46
127
  const baseDoc = toImpl(currentCommand).buildHelpDoc(commandPath)
47
128
 
48
- const flags = yield* registry
129
+ const flags = getGlobalFlagsForCommandPath(command, commandPath, builtIns)
49
130
  const globalFlagDocs: Array<FlagDoc> = []
50
131
  for (const flag of flags) {
51
132
  const singles = Param.extractSingleParams(flag.flag)
@@ -20,7 +20,7 @@ import * as Transformation from "../../SchemaTransformation.ts"
20
20
  import type * as Scope from "../../Scope.ts"
21
21
  import * as ServiceMap from "../../ServiceMap.ts"
22
22
  import * as Stream from "../../Stream.ts"
23
- import type { Covariant } from "../../Types.ts"
23
+ import type { Covariant, NoInfer } from "../../Types.ts"
24
24
  import * as UndefinedOr from "../../UndefinedOr.ts"
25
25
  import type { Cookie } from "../http/Cookies.ts"
26
26
  import type * as Etag from "../http/Etag.ts"
@@ -63,7 +63,6 @@ export const layer = <Id extends string, Groups extends HttpApiGroup.Any>(
63
63
  | HttpPlatform
64
64
  | Path
65
65
  | HttpApiGroup.ToService<Id, Groups>
66
- | HttpApiGroup.ErrorServicesEncode<Groups>
67
66
  > =>
68
67
  HttpRouter.use(Effect.fnUntraced(function*(router) {
69
68
  const services = yield* Effect.services<
@@ -168,7 +167,7 @@ export interface Handlers<
168
167
  | R
169
168
  | HttpApiEndpoint.MiddlewareWithName<Endpoints, Name>
170
169
  | HttpApiEndpoint.MiddlewareServicesWithName<Endpoints, Name>
171
- | HttpApiEndpoint.ExcludeProvided<
170
+ | HttpApiEndpoint.ExcludeProvidedWithName<
172
171
  Endpoints,
173
172
  Name,
174
173
  R1 | HttpApiEndpoint.ServerServicesWithName<Endpoints, Name>
@@ -188,7 +187,7 @@ export interface Handlers<
188
187
  | R
189
188
  | HttpApiEndpoint.MiddlewareWithName<Endpoints, Name>
190
189
  | HttpApiEndpoint.MiddlewareServicesWithName<Endpoints, Name>
191
- | HttpApiEndpoint.ExcludeProvided<
190
+ | HttpApiEndpoint.ExcludeProvidedWithName<
192
191
  Endpoints,
193
192
  Name,
194
193
  R1 | HttpApiEndpoint.ServerServicesWithName<Endpoints, Name>
@@ -284,6 +283,54 @@ export declare namespace Handlers {
284
283
  never
285
284
  }
286
285
 
286
+ /**
287
+ * @since 4.0.0
288
+ * @category handlers
289
+ */
290
+ export const endpoint = <
291
+ ApiId extends string,
292
+ Groups extends HttpApiGroup.Any,
293
+ const GroupName extends HttpApiGroup.Name<Groups>,
294
+ const EndpointName extends HttpApiEndpoint.Name<HttpApiGroup.Endpoints<HttpApiGroup.WithName<Groups, GroupName>>>,
295
+ R,
296
+ Group extends HttpApiGroup.Any = HttpApiGroup.WithName<Groups, GroupName>,
297
+ Endpoint extends HttpApiEndpoint.Any = HttpApiEndpoint.WithName<HttpApiGroup.Endpoints<Group>, EndpointName>
298
+ >(
299
+ api: HttpApi.HttpApi<ApiId, Groups>,
300
+ groupName: GroupName,
301
+ endpointName: EndpointName,
302
+ handler: NoInfer<
303
+ HttpApiEndpoint.HandlerWithName<
304
+ HttpApiGroup.Endpoints<HttpApiGroup.WithName<Groups, GroupName>>,
305
+ EndpointName,
306
+ never,
307
+ R
308
+ >
309
+ >
310
+ ): Effect.Effect<
311
+ Effect.Effect<
312
+ HttpServerResponse,
313
+ never,
314
+ | HttpServerRequest
315
+ | HttpRouter.RouteContext
316
+ | Request.ParsedSearchParams
317
+ | Exclude<R, HttpApiEndpoint.MiddlewareProvides<Endpoint>>
318
+ >,
319
+ never,
320
+ | HttpApiEndpoint.ServerServices<Endpoint>
321
+ | HttpApiEndpoint.Middleware<Endpoint>
322
+ | HttpApiEndpoint.MiddlewareServices<Endpoint>
323
+ | Etag.Generator
324
+ | FileSystem
325
+ | HttpPlatform
326
+ | Path
327
+ > =>
328
+ Effect.servicesWith((services: ServiceMap.ServiceMap<any>) => {
329
+ const group = api.groups[groupName] as unknown as HttpApiGroup.AnyWithProps
330
+ const endpoint = group.endpoints[endpointName] as unknown as HttpApiEndpoint.AnyWithProps
331
+ return Effect.succeed(handlerToHttpEffect(group, endpoint, services, handler as any, false))
332
+ })
333
+
287
334
  /**
288
335
  * @since 4.0.0
289
336
  * @category security
@@ -497,22 +544,23 @@ function decodePayload(
497
544
  }
498
545
  }
499
546
 
500
- function handlerToRoute(
547
+ function handlerToHttpEffect(
501
548
  group: HttpApiGroup.AnyWithProps,
502
- handler: Handlers.Item<any>,
503
- services: ServiceMap.ServiceMap<any>
504
- ): HttpRouter.Route<any, any> {
505
- const endpoint = handler.endpoint
549
+ endpoint: HttpApiEndpoint.AnyWithProps,
550
+ services: ServiceMap.ServiceMap<any>,
551
+ handler: HttpApiEndpoint.Handler<any, any, any>,
552
+ isRaw: boolean
553
+ ) {
506
554
  const encodeSuccess = Schema.encodeUnknownEffect(makeSuccessSchema(endpoint))
507
555
  const encodeError = Schema.encodeUnknownEffect(makeErrorSchema(endpoint))
508
556
  const decodeParams = UndefinedOr.map(endpoint.params, Schema.decodeUnknownEffect)
509
557
  const decodeHeaders = UndefinedOr.map(endpoint.headers, Schema.decodeUnknownEffect)
510
558
  const decodeQuery = UndefinedOr.map(endpoint.query, Schema.decodeUnknownEffect)
511
559
 
512
- const shouldParsePayload = endpoint.payload.size > 0 && !handler.isRaw
560
+ const shouldParsePayload = endpoint.payload.size > 0 && !isRaw
513
561
  const payloadBy = shouldParsePayload ? buildPayloadDecoders(endpoint.payload) : undefined
514
562
 
515
- const effect = applyMiddleware(
563
+ return applyMiddleware(
516
564
  group,
517
565
  endpoint,
518
566
  services,
@@ -545,7 +593,7 @@ function handlerToRoute(
545
593
  request.payload = yield* result
546
594
  }
547
595
  }
548
- const response = yield* handler.handler(request)
596
+ const response = yield* handler(request)
549
597
  return Response.isHttpServerResponse(response) ? response : yield* encodeSuccess(response)
550
598
  })
551
599
  ).pipe(
@@ -558,11 +606,18 @@ function handlerToRoute(
558
606
  }),
559
607
  Effect.provideServices(services)
560
608
  )
609
+ }
561
610
 
611
+ function handlerToRoute(
612
+ group: HttpApiGroup.AnyWithProps,
613
+ handler: Handlers.Item<any>,
614
+ services: ServiceMap.ServiceMap<any>
615
+ ): HttpRouter.Route<any, any> {
616
+ const endpoint = handler.endpoint
562
617
  return HttpRouter.route(
563
618
  endpoint.method,
564
619
  endpoint.path as HttpRouter.PathInput,
565
- effect,
620
+ handlerToHttpEffect(group, endpoint, services, handler.handler, handler.isRaw),
566
621
  { uninterruptible: handler.uninterruptible }
567
622
  )
568
623
  }
@@ -513,8 +513,8 @@ export type ServerServices<Endpoint> = Endpoint extends HttpApiEndpoint<
513
513
  | _Payload["DecodingServices"]
514
514
  | _Headers["DecodingServices"]
515
515
  | _Success["EncodingServices"]
516
- // Error services are handled globally
517
- // | _Error["EncodingServices"]
516
+ | _Error["EncodingServices"]
517
+ | HttpApiMiddleware.ErrorServicesEncode<_M>
518
518
  : never
519
519
 
520
520
  /**
@@ -668,10 +668,19 @@ export type MiddlewareServicesWithName<Endpoints extends Any, Name extends strin
668
668
  * @since 4.0.0
669
669
  * @category models
670
670
  */
671
- export type ExcludeProvided<Endpoints extends Any, Name extends string, R> = Exclude<
671
+ export type ExcludeProvidedWithName<Endpoints extends Any, Name extends string, R> = ExcludeProvided<
672
+ WithName<Endpoints, Name>,
673
+ R
674
+ >
675
+
676
+ /**
677
+ * @since 4.0.0
678
+ * @category models
679
+ */
680
+ export type ExcludeProvided<Endpoint extends Any, R> = Exclude<
672
681
  R,
673
682
  | HttpRouter.Provided
674
- | HttpApiMiddleware.Provides<MiddlewareWithName<Endpoints, Name>>
683
+ | HttpApiMiddleware.Provides<Middleware<Endpoint>>
675
684
  >
676
685
 
677
686
  /**
@@ -635,9 +635,26 @@ function toEncodingAST(ast: AST.AST, _tag: HttpApiSchema.Encoding["_tag"]): AST.
635
635
  return Schema.String.ast
636
636
  case "FormUrlEncoded":
637
637
  case "Json":
638
- case "Multipart":
639
638
  return ast
639
+ case "Multipart":
640
+ return persistedFileToBinaryEncoding(ast)
641
+ }
642
+ }
643
+
644
+ function persistedFileToBinaryEncoding(ast: AST.AST): AST.AST {
645
+ if (
646
+ AST.isDeclaration(ast) &&
647
+ ((ast.annotations as (Schema.Annotations.Declaration<unknown, readonly []> | undefined))?.typeConstructor?._tag ===
648
+ "effect/http/PersistedFile")
649
+ ) {
650
+ return Uint8ArrayEncoding.ast
651
+ }
652
+
653
+ if (typeof (ast as any)?.recur === "function") {
654
+ return (ast as any).recur(persistedFileToBinaryEncoding)
640
655
  }
656
+
657
+ return ast
641
658
  }
642
659
 
643
660
  const makeSecurityScheme = (security: HttpApiSecurity): OpenAPISecurityScheme => {
@@ -66,7 +66,7 @@ export interface AtomHttpApiClient<Self, Id extends string, Groups extends HttpA
66
66
  readonly reactivityKeys?: ReadonlyArray<unknown> | ReadonlyRecord<string, ReadonlyArray<unknown>> | undefined
67
67
  }
68
68
  >,
69
- WithResponse extends true ? [_Success, HttpClientResponse] : _Success,
69
+ WithResponse extends true ? [_Success["Type"], HttpClientResponse] : _Success["Type"],
70
70
  _Error | HttpClientError.HttpClientError | SchemaError
71
71
  >
72
72
  : never
@@ -123,7 +123,7 @@ export interface AtomHttpApiClient<Self, Id extends string, Groups extends HttpA
123
123
  >
124
124
  ] ? Atom.Atom<
125
125
  AsyncResult.AsyncResult<
126
- WithResponse extends true ? [_Success, HttpClientResponse] : _Success,
126
+ WithResponse extends true ? [_Success["Type"], HttpClientResponse] : _Success["Type"],
127
127
  _Error | HttpClientError.HttpClientError | SchemaError
128
128
  >
129
129
  >
@@ -482,9 +482,7 @@ class RegistryImpl implements AtomRegistry {
482
482
 
483
483
  removeNodeTimeout(node: NodeImpl<any>): void {
484
484
  const bucket = this.nodeTimeoutBucket.get(node)
485
- if (bucket === undefined) {
486
- return
487
- }
485
+ if (bucket === undefined) return
488
486
  this.nodeTimeoutBucket.delete(node)
489
487
  this.scheduleNodeRemoval(node)
490
488
 
@@ -502,10 +500,8 @@ class RegistryImpl implements AtomRegistry {
502
500
  this.timeoutBuckets.delete(bucket)
503
501
 
504
502
  nodes.forEach((node) => {
505
- if (!node.canBeRemoved) {
506
- return
507
- }
508
503
  this.nodeTimeoutBucket.delete(node)
504
+ if (!node.canBeRemoved) return
509
505
  this.nodes.delete(atomKey(node.atom))
510
506
  this.onNodeRemoved?.(node)
511
507
  this.#currentSweepTTL = node.atom.idleTTL ?? this.defaultIdleTTL!