effect 4.0.0-beta.98 → 4.0.0-beta.99

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 (83) hide show
  1. package/README.md +1 -3
  2. package/dist/Array.js +1 -1
  3. package/dist/Array.js.map +1 -1
  4. package/dist/Graph.d.ts +1187 -196
  5. package/dist/Graph.d.ts.map +1 -1
  6. package/dist/Graph.js +1031 -283
  7. package/dist/Graph.js.map +1 -1
  8. package/dist/Iterable.d.ts.map +1 -1
  9. package/dist/Iterable.js +41 -1
  10. package/dist/Iterable.js.map +1 -1
  11. package/dist/Schema.js +1 -1
  12. package/dist/Schema.js.map +1 -1
  13. package/dist/internal/effect.js +9 -3
  14. package/dist/internal/effect.js.map +1 -1
  15. package/dist/unstable/ai/McpSchema.d.ts +128 -128
  16. package/dist/unstable/ai/McpSchema.d.ts.map +1 -1
  17. package/dist/unstable/ai/Tool.d.ts.map +1 -1
  18. package/dist/unstable/ai/Tool.js +11 -13
  19. package/dist/unstable/ai/Tool.js.map +1 -1
  20. package/dist/unstable/cli/CliConfig.d.ts +76 -0
  21. package/dist/unstable/cli/CliConfig.d.ts.map +1 -0
  22. package/dist/unstable/cli/CliConfig.js +63 -0
  23. package/dist/unstable/cli/CliConfig.js.map +1 -0
  24. package/dist/unstable/cli/CliError.d.ts.map +1 -1
  25. package/dist/unstable/cli/CliError.js +3 -0
  26. package/dist/unstable/cli/CliError.js.map +1 -1
  27. package/dist/unstable/cli/Command.d.ts +36 -1
  28. package/dist/unstable/cli/Command.d.ts.map +1 -1
  29. package/dist/unstable/cli/Command.js +61 -4
  30. package/dist/unstable/cli/Command.js.map +1 -1
  31. package/dist/unstable/cli/GlobalFlag.d.ts +23 -2
  32. package/dist/unstable/cli/GlobalFlag.d.ts.map +1 -1
  33. package/dist/unstable/cli/GlobalFlag.js +20 -4
  34. package/dist/unstable/cli/GlobalFlag.js.map +1 -1
  35. package/dist/unstable/cli/Param.js +6 -2
  36. package/dist/unstable/cli/Param.js.map +1 -1
  37. package/dist/unstable/cli/index.d.ts +4 -0
  38. package/dist/unstable/cli/index.d.ts.map +1 -1
  39. package/dist/unstable/cli/index.js +4 -0
  40. package/dist/unstable/cli/index.js.map +1 -1
  41. package/dist/unstable/cli/internal/ansi.d.ts.map +1 -1
  42. package/dist/unstable/cli/internal/ansi.js +2 -0
  43. package/dist/unstable/cli/internal/ansi.js.map +1 -1
  44. package/dist/unstable/cli/internal/parser.js +11 -2
  45. package/dist/unstable/cli/internal/parser.js.map +1 -1
  46. package/dist/unstable/cli/internal/wizard.d.ts +13 -0
  47. package/dist/unstable/cli/internal/wizard.d.ts.map +1 -0
  48. package/dist/unstable/cli/internal/wizard.js +212 -0
  49. package/dist/unstable/cli/internal/wizard.js.map +1 -0
  50. package/dist/unstable/cluster/RunnerStorage.js +1 -1
  51. package/dist/unstable/cluster/RunnerStorage.js.map +1 -1
  52. package/dist/unstable/http/Multipart.d.ts.map +1 -1
  53. package/dist/unstable/http/Multipart.js +3 -1
  54. package/dist/unstable/http/Multipart.js.map +1 -1
  55. package/dist/unstable/httpapi/HttpApiBuilder.d.ts +1 -1
  56. package/dist/unstable/httpapi/HttpApiBuilder.d.ts.map +1 -1
  57. package/dist/unstable/persistence/Redis.d.ts.map +1 -1
  58. package/dist/unstable/persistence/Redis.js +5 -3
  59. package/dist/unstable/persistence/Redis.js.map +1 -1
  60. package/dist/unstable/sql/SqlClient.d.ts +1 -2
  61. package/dist/unstable/sql/SqlClient.d.ts.map +1 -1
  62. package/dist/unstable/sql/SqlClient.js.map +1 -1
  63. package/package.json +3 -3
  64. package/src/Array.ts +1 -1
  65. package/src/Graph.ts +2967 -457
  66. package/src/Iterable.ts +32 -2
  67. package/src/Schema.ts +1 -1
  68. package/src/internal/effect.ts +9 -3
  69. package/src/unstable/ai/Tool.ts +13 -16
  70. package/src/unstable/cli/CliConfig.ts +85 -0
  71. package/src/unstable/cli/CliError.ts +3 -0
  72. package/src/unstable/cli/Command.ts +83 -7
  73. package/src/unstable/cli/GlobalFlag.ts +33 -5
  74. package/src/unstable/cli/Param.ts +15 -3
  75. package/src/unstable/cli/index.ts +5 -0
  76. package/src/unstable/cli/internal/ansi.ts +3 -0
  77. package/src/unstable/cli/internal/parser.ts +14 -2
  78. package/src/unstable/cli/internal/wizard.ts +283 -0
  79. package/src/unstable/cluster/RunnerStorage.ts +1 -1
  80. package/src/unstable/http/Multipart.ts +3 -1
  81. package/src/unstable/httpapi/HttpApiBuilder.ts +1 -1
  82. package/src/unstable/persistence/Redis.ts +9 -4
  83. package/src/unstable/sql/SqlClient.ts +1 -2
@@ -0,0 +1,283 @@
1
+ import * as Console from "../../../Console.ts"
2
+ import * as Effect from "../../../Effect.ts"
3
+ import * as Option from "../../../Option.ts"
4
+ import * as Redacted from "../../../Redacted.ts"
5
+ import type * as Terminal from "../../../Terminal.ts"
6
+ import type * as CliError from "../CliError.ts"
7
+ import type * as Command from "../Command.ts"
8
+ import * as Param from "../Param.ts"
9
+ import * as Primitive from "../Primitive.ts"
10
+ import * as Prompt from "../Prompt.ts"
11
+ import * as Ansi from "./ansi.ts"
12
+ import { toImpl } from "./command.ts"
13
+
14
+ export interface Options {
15
+ readonly commandPath?: ReadonlyArray<string> | undefined
16
+ readonly prefix?: ReadonlyArray<string> | undefined
17
+ }
18
+
19
+ export const run: (
20
+ command: Command.Command.Any,
21
+ options?: Options
22
+ ) => Effect.Effect<Array<string>, CliError.CliError | Terminal.QuitError, Command.Environment> = Effect.fnUntraced(
23
+ function*(command, options) {
24
+ const commandPath = options?.commandPath ?? [command.name]
25
+ const selected = getCommandAtPath(command, commandPath)
26
+ const commandLine = [...(options?.prefix ?? commandPath)]
27
+ yield* logCurrentCommand(commandLine)
28
+ yield* promptCommand(selected, commandLine, selected === command ? "ROOT" : selected.name)
29
+ return commandLine
30
+ }
31
+ )
32
+
33
+ const getCommandAtPath = (
34
+ command: Command.Command.Any,
35
+ commandPath: ReadonlyArray<string>
36
+ ): Command.Command.Any => {
37
+ let current = command
38
+ for (const name of commandPath.slice(1)) {
39
+ const child = current.subcommands
40
+ .flatMap((group) => group.commands)
41
+ .find((candidate) => candidate.name === name || candidate.alias === name)
42
+ if (child === undefined) {
43
+ break
44
+ }
45
+ current = child
46
+ }
47
+ return current
48
+ }
49
+
50
+ const promptCommand: (
51
+ command: Command.Command.Any,
52
+ commandLine: Array<string>,
53
+ sectionName: string
54
+ ) => Effect.Effect<void, CliError.CliError | Terminal.QuitError, Command.Environment> = Effect.fnUntraced(
55
+ function*(command, commandLine, sectionName) {
56
+ const impl = toImpl(command)
57
+ const visibleSubcommands = command.subcommands.flatMap((group) => group.commands.filter((child) => !child.hidden))
58
+ const config = visibleSubcommands.length === 0 ? impl.config : impl.contextConfig
59
+
60
+ if (config.flags.length > 0) {
61
+ yield* Console.log(renderSection(sectionName, "FLAGS"))
62
+ for (const param of config.flags) {
63
+ commandLine.push(...yield* promptParam(param))
64
+ }
65
+ if (config.arguments.length > 0 || visibleSubcommands.length > 0) {
66
+ yield* logCurrentCommand(commandLine)
67
+ }
68
+ }
69
+
70
+ if (config.arguments.length > 0) {
71
+ yield* Console.log(renderSection(sectionName, "ARGUMENTS"))
72
+ for (const param of config.arguments) {
73
+ commandLine.push(...yield* promptParam(param))
74
+ }
75
+ if (visibleSubcommands.length > 0) {
76
+ yield* logCurrentCommand(commandLine)
77
+ }
78
+ }
79
+
80
+ if (visibleSubcommands.length === 0) {
81
+ return
82
+ }
83
+
84
+ const child = yield* Prompt.run(Prompt.select({
85
+ message: "Command",
86
+ choices: visibleSubcommands.map((command) => ({
87
+ title: command.name,
88
+ value: command,
89
+ ...(command.shortDescription !== undefined
90
+ ? { description: command.shortDescription }
91
+ : command.description !== undefined
92
+ ? { description: command.description }
93
+ : {})
94
+ }))
95
+ }))
96
+ yield* Console.log()
97
+ commandLine.push(child.name)
98
+ if (hasWizardSteps(child)) {
99
+ yield* logCurrentCommand(commandLine)
100
+ }
101
+ yield* promptCommand(child, commandLine, child.name)
102
+ }
103
+ )
104
+
105
+ const hasWizardSteps = (command: Command.Command.Any): boolean => {
106
+ const hasVisibleSubcommands = command.subcommands.some((group) => group.commands.some((child) => !child.hidden))
107
+ return hasVisibleSubcommands || toImpl(command).config.orderedParams.length > 0
108
+ }
109
+
110
+ const promptParam: (
111
+ param: Param.Any
112
+ ) => Effect.Effect<Array<string>, CliError.CliError | Terminal.QuitError, Command.Environment> = Effect.fnUntraced(
113
+ function*(param) {
114
+ const single = Param.getUnderlyingSingleOrThrow(param)
115
+ const metadata = Param.getParamMetadata(param)
116
+
117
+ if (metadata.isOptional) {
118
+ const include = yield* Prompt.run(Prompt.confirm({
119
+ message: `Set ${renderParamLabel(single)}?`,
120
+ initial: false
121
+ }))
122
+ if (!include) {
123
+ yield* Console.log()
124
+ return []
125
+ }
126
+ }
127
+
128
+ const count = !metadata.isVariadic
129
+ ? 1
130
+ : yield* Prompt.run(Prompt.integer({
131
+ message: `${renderParamLabel(single)} count`,
132
+ default: Option.getOrElse(metadata.variadicMin, () => 0),
133
+ min: Option.getOrElse(metadata.variadicMin, () => 0),
134
+ ...(Option.isSome(metadata.variadicMax) ? { max: metadata.variadicMax.value } : {})
135
+ }))
136
+ const values: Array<string> = []
137
+ for (let i = 0; i < count; i++) {
138
+ values.push(yield* promptSingle(single))
139
+ }
140
+
141
+ const parsed = single.kind === Param.flagKind
142
+ ? {
143
+ flags: { [single.name]: values },
144
+ arguments: []
145
+ }
146
+ : {
147
+ flags: {},
148
+ arguments: values
149
+ }
150
+ yield* param.parse(parsed)
151
+ yield* Console.log()
152
+
153
+ if (single.kind === Param.argumentKind) {
154
+ return values
155
+ }
156
+ return values.flatMap((value) => [`--${single.name}`, value])
157
+ }
158
+ )
159
+
160
+ const promptSingle = (
161
+ single: Param.Single<Param.ParamKind, unknown>
162
+ ): Effect.Effect<string, Terminal.QuitError, Command.Environment> => {
163
+ const message = renderParamMessage(single)
164
+ switch (single.primitiveType._tag) {
165
+ case "Boolean":
166
+ return Effect.map(
167
+ Prompt.run(Prompt.confirm({
168
+ message,
169
+ label: {
170
+ confirm: "true",
171
+ deny: "false"
172
+ },
173
+ placeholder: {
174
+ defaultConfirm: "(T/f)",
175
+ defaultDeny: "(t/F)"
176
+ }
177
+ })),
178
+ String
179
+ )
180
+ case "Choice": {
181
+ const choices = Primitive.getChoiceKeys(single.primitiveType) ?? []
182
+ return Prompt.run(Prompt.select({
183
+ message,
184
+ choices: choices.map((choice) => ({ title: choice, value: choice }))
185
+ }))
186
+ }
187
+ case "Date":
188
+ return Effect.map(Prompt.run(Prompt.date({ message })), (date) => date.toISOString())
189
+ case "Float":
190
+ return Effect.map(Prompt.run(Prompt.float({ message })), String)
191
+ case "Integer":
192
+ return Effect.map(Prompt.run(Prompt.integer({ message })), String)
193
+ case "Redacted":
194
+ return Effect.map(Prompt.run(Prompt.password({ message })), Redacted.value)
195
+ default:
196
+ return Prompt.run(Prompt.text({ message }))
197
+ }
198
+ }
199
+
200
+ const formatName = (single: Param.Single<Param.ParamKind, unknown>): string =>
201
+ single.kind === Param.flagKind ? `--${single.name}` : single.name
202
+
203
+ const renderParamMessage = (single: Param.Single<Param.ParamKind, unknown>): string => renderParamLabel(single)
204
+
205
+ const renderParamLabel = (single: Param.Single<Param.ParamKind, unknown>): string => {
206
+ const description = Option.getOrUndefined(single.description)?.trim()
207
+ const label = single.kind === Param.flagKind && description !== undefined && description.length <= 32
208
+ ? description
209
+ : humanize(single.name)
210
+ return single.kind === Param.flagKind ? `${label} (${formatName(single)})` : label
211
+ }
212
+
213
+ const humanize = (name: string): string => {
214
+ const words = name.split(/[-_]+/).filter((word) => word.length > 0)
215
+ if (words.length === 0) return name
216
+ return [words[0][0].toUpperCase() + words[0].slice(1), ...words.slice(1)].join(" ")
217
+ }
218
+
219
+ const logCurrentCommand = (commandLine: ReadonlyArray<string>): Effect.Effect<void> =>
220
+ Console.log(renderCommandBlock("Current command", commandLine, Ansi.magenta))
221
+
222
+ const renderSection = (commandName: string, section: string): string =>
223
+ `${Ansi.annotate(commandName.toUpperCase(), Ansi.bold, Ansi.cyanBright)} ${Ansi.annotate("·", Ansi.blackBright)} ${
224
+ Ansi.annotate(section, Ansi.bold, Ansi.white)
225
+ }`
226
+
227
+ export const renderIntroduction = (name: string, version: string, summary: string | undefined): string => {
228
+ const title = `${Ansi.annotate(name, Ansi.bold, Ansi.cyanBright)} ${Ansi.annotate(`v${version}`, Ansi.white)} ${
229
+ Ansi.annotate("· Command wizard", Ansi.bold, Ansi.white)
230
+ }`
231
+ return [
232
+ title,
233
+ ...(summary === undefined || summary.length === 0 ? [] : [summary]),
234
+ Ansi.annotate("Build a command interactively. Press Ctrl+C to cancel.", Ansi.blackBright),
235
+ ""
236
+ ].join("\n")
237
+ }
238
+
239
+ export const renderCompletion = (commandLine: ReadonlyArray<string>): string =>
240
+ renderCommandBlock("Command ready", commandLine, Ansi.cyanBright, Ansi.green)
241
+
242
+ export const renderQuit = (): string => `\n${Ansi.annotate("Wizard cancelled.", Ansi.red)}`
243
+
244
+ const renderCommandBlock = (
245
+ label: string,
246
+ commandLine: ReadonlyArray<string>,
247
+ commandColor: string,
248
+ labelColor: string = Ansi.white
249
+ ): string => {
250
+ const lines = wrapCommand(commandLine)
251
+ return [
252
+ Ansi.annotate(label, Ansi.bold, labelColor),
253
+ ...lines.map((line) => Ansi.annotate(line, commandColor)),
254
+ ""
255
+ ].join("\n")
256
+ }
257
+
258
+ const wrapCommand = (commandLine: ReadonlyArray<string>): Array<string> => {
259
+ const width = 88
260
+ const firstIndent = " $ "
261
+ const continuationIndent = " "
262
+ const args = commandLine.map(formatShellArg)
263
+ const lines: Array<string> = []
264
+ let current = firstIndent
265
+
266
+ for (const arg of args) {
267
+ const separator = current === firstIndent || current === continuationIndent ? "" : " "
268
+ if (current.length + separator.length + arg.length > width && current !== firstIndent) {
269
+ lines.push(`${current} \\`)
270
+ current = `${continuationIndent}${arg}`
271
+ } else {
272
+ current += `${separator}${arg}`
273
+ }
274
+ }
275
+
276
+ if (current !== firstIndent) {
277
+ lines.push(current)
278
+ }
279
+ return lines
280
+ }
281
+
282
+ const formatShellArg = (arg: string): string =>
283
+ /^[A-Za-z0-9_./:@%+=,-]+$/.test(arg) ? arg : `'${arg.replaceAll("'", `'"'"'`)}'`
@@ -220,7 +220,7 @@ export const makeMemory = Effect.gen(function*() {
220
220
  setRunnerHealth: () => Effect.void,
221
221
  acquire: (_address, shardIds) => {
222
222
  acquired = Array.from(shardIds)
223
- return Effect.succeed(Array.from(shardIds))
223
+ return Effect.succeed(acquired)
224
224
  },
225
225
  refresh: () => Effect.sync(() => acquired),
226
226
  release: () => Effect.void,
@@ -452,7 +452,9 @@ export const makeChannel = <IE>(headers: Record<string, string>): Channel.Channe
452
452
  exit = Option.some(Exit.fail(convertError(error_)))
453
453
  },
454
454
  onDone() {
455
- exit = Option.some(Exit.fail(Cause.Done()))
455
+ if (Option.isNone(exit)) {
456
+ exit = Option.some(Exit.fail(Cause.Done()))
457
+ }
456
458
  }
457
459
  })
458
460
 
@@ -171,7 +171,7 @@ type HandlerRequirements<
171
171
  > =
172
172
  | HttpApiEndpoint.Middleware<Endpoint>
173
173
  | HttpApiEndpoint.MiddlewareServices<Endpoint>
174
- | ([R] extends [never] ? never : HttpRouter.Request<"Requires", R>)
174
+ | ([R] extends [never] ? never : HttpRouter.Request.From<"Requires", R>)
175
175
 
176
176
  interface HandlerOptions {
177
177
  readonly uninterruptible?: boolean | undefined
@@ -11,8 +11,10 @@
11
11
  */
12
12
  import * as Cache from "../../Cache.ts"
13
13
  import * as Context from "../../Context.ts"
14
+ import * as Duration from "../../Duration.ts"
14
15
  import * as Effect from "../../Effect.ts"
15
16
  import * as Equal from "../../Equal.ts"
17
+ import * as Exit from "../../Exit.ts"
16
18
  import { constant, identity } from "../../Function.ts"
17
19
  import * as Hash from "../../Hash.ts"
18
20
  import * as Schema from "../../Schema.ts"
@@ -50,10 +52,13 @@ export const make = Effect.fnUntraced(function*(
50
52
  readonly send: <A = unknown>(command: string, ...args: ReadonlyArray<string>) => Effect.Effect<A, RedisError>
51
53
  }
52
54
  ) {
53
- const scriptCache = yield* Cache.make({
54
- lookup: (script: Script<any>) => options.send<string>("SCRIPT", "LOAD", script.lua),
55
- capacity: Number.POSITIVE_INFINITY
56
- })
55
+ const scriptCache = yield* Cache.makeWith(
56
+ (script: Script<any>) => options.send<string>("SCRIPT", "LOAD", script.lua),
57
+ {
58
+ capacity: Number.POSITIVE_INFINITY,
59
+ timeToLive: (exit) => Exit.isSuccess(exit) ? Duration.infinity : Duration.zero
60
+ }
61
+ )
57
62
 
58
63
  const eval_ = <
59
64
  Config extends {
@@ -62,8 +62,7 @@ export interface SqlClient extends Constructor {
62
62
  readonly transactionService: Context.Service<TransactionConnection, TransactionConnection.Service>
63
63
 
64
64
  /**
65
- * Use the Reactivity service from @effect/experimental to create a reactive
66
- * query.
65
+ * Use the Reactivity service to create a reactive query.
67
66
  */
68
67
  readonly reactive: <A, E, R>(
69
68
  keys: ReadonlyArray<unknown> | ReadonlyRecord<string, ReadonlyArray<unknown>>,