effect 4.0.0-beta.34 → 4.0.0-beta.36
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.
- package/dist/Channel.d.ts +10 -1
- package/dist/Channel.d.ts.map +1 -1
- package/dist/Channel.js +14 -8
- package/dist/Channel.js.map +1 -1
- package/dist/Config.d.ts +4 -0
- package/dist/Config.d.ts.map +1 -1
- package/dist/Config.js.map +1 -1
- package/dist/Cron.d.ts +7 -0
- package/dist/Cron.d.ts.map +1 -1
- package/dist/Cron.js +109 -45
- package/dist/Cron.js.map +1 -1
- package/dist/Effect.d.ts +2 -2
- package/dist/Effect.d.ts.map +1 -1
- package/dist/Effect.js.map +1 -1
- package/dist/Equivalence.d.ts +52 -0
- package/dist/Equivalence.d.ts.map +1 -1
- package/dist/Equivalence.js +52 -0
- package/dist/Equivalence.js.map +1 -1
- package/dist/Layer.d.ts +5 -2
- package/dist/Layer.d.ts.map +1 -1
- package/dist/Layer.js +14 -1
- package/dist/Layer.js.map +1 -1
- package/dist/LayerMap.d.ts +5 -4
- package/dist/LayerMap.d.ts.map +1 -1
- package/dist/LayerMap.js.map +1 -1
- package/dist/PubSub.d.ts.map +1 -1
- package/dist/PubSub.js +9 -3
- package/dist/PubSub.js.map +1 -1
- package/dist/Schema.d.ts +50 -5
- package/dist/Schema.d.ts.map +1 -1
- package/dist/Schema.js +40 -0
- package/dist/Schema.js.map +1 -1
- package/dist/SchemaParser.d.ts +29 -0
- package/dist/SchemaParser.d.ts.map +1 -1
- package/dist/SchemaParser.js +38 -0
- package/dist/SchemaParser.js.map +1 -1
- package/dist/Stream.d.ts +10 -1
- package/dist/Stream.d.ts.map +1 -1
- package/dist/Stream.js +5 -1
- package/dist/Stream.js.map +1 -1
- package/dist/internal/dateTime.js +8 -1
- package/dist/internal/dateTime.js.map +1 -1
- package/dist/internal/effect.js +4 -3
- package/dist/internal/effect.js.map +1 -1
- package/dist/unstable/ai/AiError.d.ts +1 -0
- package/dist/unstable/ai/AiError.d.ts.map +1 -1
- package/dist/unstable/ai/AiError.js +7 -3
- package/dist/unstable/ai/AiError.js.map +1 -1
- package/dist/unstable/ai/LanguageModel.d.ts.map +1 -1
- package/dist/unstable/ai/LanguageModel.js +49 -21
- package/dist/unstable/ai/LanguageModel.js.map +1 -1
- package/dist/unstable/cli/CliOutput.js +4 -3
- package/dist/unstable/cli/CliOutput.js.map +1 -1
- package/dist/unstable/cli/Prompt.js +146 -13
- package/dist/unstable/cli/Prompt.js.map +1 -1
- package/dist/unstable/httpapi/HttpApiEndpoint.js +2 -2
- package/dist/unstable/httpapi/HttpApiEndpoint.js.map +1 -1
- package/dist/unstable/httpapi/HttpApiMiddleware.d.ts +15 -13
- package/dist/unstable/httpapi/HttpApiMiddleware.d.ts.map +1 -1
- package/dist/unstable/httpapi/HttpApiMiddleware.js +5 -3
- package/dist/unstable/httpapi/HttpApiMiddleware.js.map +1 -1
- package/dist/unstable/rpc/RpcSerialization.js +1 -1
- package/dist/unstable/rpc/RpcSerialization.js.map +1 -1
- package/package.json +1 -1
- package/src/Channel.ts +23 -11
- package/src/Config.ts +5 -0
- package/src/Cron.ts +142 -45
- package/src/Effect.ts +3 -3
- package/src/Equivalence.ts +56 -0
- package/src/Layer.ts +27 -9
- package/src/LayerMap.ts +7 -5
- package/src/PubSub.ts +13 -5
- package/src/Schema.ts +62 -8
- package/src/SchemaParser.ts +53 -0
- package/src/Stream.ts +11 -1
- package/src/internal/dateTime.ts +8 -1
- package/src/internal/effect.ts +16 -13
- package/src/unstable/ai/AiError.ts +4 -2
- package/src/unstable/ai/LanguageModel.ts +59 -40
- package/src/unstable/cli/CliOutput.ts +5 -4
- package/src/unstable/cli/Prompt.ts +147 -14
- package/src/unstable/httpapi/HttpApiEndpoint.ts +2 -2
- package/src/unstable/httpapi/HttpApiMiddleware.ts +36 -20
- package/src/unstable/rpc/RpcSerialization.ts +1 -1
|
@@ -937,19 +937,23 @@ export const make: (params: {
|
|
|
937
937
|
const tracker = Option.getOrUndefined(yield* Effect.serviceOption(ResponseIdTracker.ResponseIdTracker))
|
|
938
938
|
const toolChoice = options.toolChoice ?? "auto"
|
|
939
939
|
|
|
940
|
-
const
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
940
|
+
const generateWithNonIncrementalFallback = () => {
|
|
941
|
+
const requestOptions: ProviderOptions = {
|
|
942
|
+
...providerOptions
|
|
943
|
+
}
|
|
944
|
+
const fallbackPrompt = requestOptions.prompt
|
|
945
|
+
const fallbackOptions: ProviderOptions = {
|
|
946
|
+
...requestOptions,
|
|
947
|
+
prompt: fallbackPrompt,
|
|
948
|
+
incrementalPrompt: undefined,
|
|
949
|
+
previousResponseId: undefined
|
|
950
|
+
}
|
|
951
|
+
return requestOptions.incrementalPrompt
|
|
952
|
+
? params.generateText(requestOptions).pipe(
|
|
953
|
+
Effect.catchReason("AiError", "InvalidRequestError", (_) => params.generateText(fallbackOptions))
|
|
954
|
+
)
|
|
955
|
+
: params.generateText(requestOptions)
|
|
956
|
+
}
|
|
953
957
|
|
|
954
958
|
// Check for pending approvals that need resolution
|
|
955
959
|
const { approved, denied } = collectToolApprovals(
|
|
@@ -982,7 +986,7 @@ export const make: (params: {
|
|
|
982
986
|
const ResponseSchema = Schema.mutable(
|
|
983
987
|
Schema.Array(Response.Part(Toolkit.empty))
|
|
984
988
|
)
|
|
985
|
-
const rawContent = yield*
|
|
989
|
+
const rawContent = yield* generateWithNonIncrementalFallback()
|
|
986
990
|
const content = yield* Schema.decodeEffect(ResponseSchema)(rawContent)
|
|
987
991
|
if (tracker) {
|
|
988
992
|
const responseMetadata = content.find((part) => part.type === "response-metadata")
|
|
@@ -1020,7 +1024,7 @@ export const make: (params: {
|
|
|
1020
1024
|
const ResponseSchema = Schema.mutable(
|
|
1021
1025
|
Schema.Array(Response.Part(Toolkit.empty))
|
|
1022
1026
|
)
|
|
1023
|
-
const rawContent = yield*
|
|
1027
|
+
const rawContent = yield* generateWithNonIncrementalFallback()
|
|
1024
1028
|
const content = yield* Schema.decodeEffect(ResponseSchema)(rawContent)
|
|
1025
1029
|
if (tracker) {
|
|
1026
1030
|
const responseMetadata = content.find((part) => part.type === "response-metadata")
|
|
@@ -1099,7 +1103,7 @@ export const make: (params: {
|
|
|
1099
1103
|
// If tool call resolution is disabled, return the response without
|
|
1100
1104
|
// resolving the tool calls that were generated
|
|
1101
1105
|
if (options.disableToolCallResolution === true) {
|
|
1102
|
-
const rawContent = yield*
|
|
1106
|
+
const rawContent = yield* generateWithNonIncrementalFallback()
|
|
1103
1107
|
const content = yield* Schema.decodeEffect(ResponseSchema)(rawContent)
|
|
1104
1108
|
if (tracker) {
|
|
1105
1109
|
const responseMetadata = content.find((part) => part.type === "response-metadata")
|
|
@@ -1110,7 +1114,7 @@ export const make: (params: {
|
|
|
1110
1114
|
return content as Array<Response.Part<Tools>>
|
|
1111
1115
|
}
|
|
1112
1116
|
|
|
1113
|
-
const rawContent = yield*
|
|
1117
|
+
const rawContent = yield* generateWithNonIncrementalFallback()
|
|
1114
1118
|
|
|
1115
1119
|
// Resolve the generated tool calls
|
|
1116
1120
|
const toolResults = yield* resolveToolCalls(
|
|
@@ -1168,19 +1172,23 @@ export const make: (params: {
|
|
|
1168
1172
|
const tracker = Option.getOrUndefined(yield* Effect.serviceOption(ResponseIdTracker.ResponseIdTracker))
|
|
1169
1173
|
const toolChoice = options.toolChoice ?? "auto"
|
|
1170
1174
|
|
|
1171
|
-
const
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1175
|
+
const streamWithNonIncrementalFallback = () => {
|
|
1176
|
+
const requestOptions: ProviderOptions = {
|
|
1177
|
+
...providerOptions
|
|
1178
|
+
}
|
|
1179
|
+
const fallbackPrompt = requestOptions.prompt
|
|
1180
|
+
const fallbackOptions: ProviderOptions = {
|
|
1181
|
+
...requestOptions,
|
|
1182
|
+
prompt: fallbackPrompt,
|
|
1183
|
+
incrementalPrompt: undefined,
|
|
1184
|
+
previousResponseId: undefined
|
|
1185
|
+
}
|
|
1186
|
+
return requestOptions.incrementalPrompt
|
|
1187
|
+
? params.streamText(requestOptions).pipe(
|
|
1188
|
+
Stream.catchReason("AiError", "InvalidRequestError", (_) => params.streamText(fallbackOptions))
|
|
1189
|
+
)
|
|
1190
|
+
: params.streamText(requestOptions)
|
|
1191
|
+
}
|
|
1184
1192
|
|
|
1185
1193
|
// Check for pending approvals that need resolution
|
|
1186
1194
|
const { approved: pendingApproved, denied: pendingDenied } = collectToolApprovals(providerOptions.prompt.content, {
|
|
@@ -1212,8 +1220,7 @@ export const make: (params: {
|
|
|
1212
1220
|
const schema = Schema.NonEmptyArray(Response.StreamPart(Toolkit.empty))
|
|
1213
1221
|
const decodeParts = Schema.decodeEffect(schema)
|
|
1214
1222
|
return pipe(
|
|
1215
|
-
|
|
1216
|
-
withNonIncrementalFallback,
|
|
1223
|
+
streamWithNonIncrementalFallback(),
|
|
1217
1224
|
Stream.mapArrayEffect((parts) =>
|
|
1218
1225
|
decodeParts(parts).pipe(
|
|
1219
1226
|
tracker ?
|
|
@@ -1262,8 +1269,7 @@ export const make: (params: {
|
|
|
1262
1269
|
const schema = Schema.NonEmptyArray(Response.StreamPart(Toolkit.empty))
|
|
1263
1270
|
const decodeParts = Schema.decodeEffect(schema)
|
|
1264
1271
|
return pipe(
|
|
1265
|
-
|
|
1266
|
-
withNonIncrementalFallback,
|
|
1272
|
+
streamWithNonIncrementalFallback(),
|
|
1267
1273
|
Stream.mapArrayEffect((parts) =>
|
|
1268
1274
|
decodeParts(parts).pipe(
|
|
1269
1275
|
tracker ?
|
|
@@ -1369,8 +1375,7 @@ export const make: (params: {
|
|
|
1369
1375
|
if (options.disableToolCallResolution === true) {
|
|
1370
1376
|
const schema = Schema.NonEmptyArray(Response.StreamPart(toolkit))
|
|
1371
1377
|
const decodeParts = Schema.decodeEffect(schema)
|
|
1372
|
-
return
|
|
1373
|
-
withNonIncrementalFallback,
|
|
1378
|
+
return streamWithNonIncrementalFallback().pipe(
|
|
1374
1379
|
Stream.mapArrayEffect((parts) =>
|
|
1375
1380
|
decodeParts(parts).pipe(
|
|
1376
1381
|
tracker ?
|
|
@@ -1402,6 +1407,7 @@ export const make: (params: {
|
|
|
1402
1407
|
| Cause.Done
|
|
1403
1408
|
| Schema.SchemaError
|
|
1404
1409
|
>()
|
|
1410
|
+
const deferredFinishParts: Array<Response.StreamPart<Tools>> = []
|
|
1405
1411
|
|
|
1406
1412
|
// Emit pre-resolved tool results so Chat.streamText persists them to
|
|
1407
1413
|
// history. This ensures collectToolApprovals({ excludeResolved }) can
|
|
@@ -1449,8 +1455,7 @@ export const make: (params: {
|
|
|
1449
1455
|
)
|
|
1450
1456
|
})
|
|
1451
1457
|
|
|
1452
|
-
yield*
|
|
1453
|
-
withNonIncrementalFallback,
|
|
1458
|
+
yield* streamWithNonIncrementalFallback().pipe(
|
|
1454
1459
|
Stream.runForEachArray(
|
|
1455
1460
|
Effect.fnUntraced(function*(chunk) {
|
|
1456
1461
|
const parts = yield* decodeParts(chunk)
|
|
@@ -1461,8 +1466,19 @@ export const make: (params: {
|
|
|
1461
1466
|
}
|
|
1462
1467
|
}
|
|
1463
1468
|
}
|
|
1464
|
-
//
|
|
1465
|
-
|
|
1469
|
+
// Defer finish parts until all tool handlers complete. This guarantees
|
|
1470
|
+
// tool results are emitted before finish in streaming mode.
|
|
1471
|
+
const immediateParts: Array<Response.StreamPart<Tools>> = []
|
|
1472
|
+
for (const part of parts) {
|
|
1473
|
+
if (part.type === "finish") {
|
|
1474
|
+
deferredFinishParts.push(part)
|
|
1475
|
+
} else {
|
|
1476
|
+
immediateParts.push(part)
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
if (immediateParts.length > 0) {
|
|
1480
|
+
yield* Queue.offerAll(queue, immediateParts)
|
|
1481
|
+
}
|
|
1466
1482
|
// Fork tool call handlers - use the raw chunk for encoded params
|
|
1467
1483
|
for (const part of chunk) {
|
|
1468
1484
|
if (part.type === "tool-call" && part.providerExecuted !== true) {
|
|
@@ -1480,6 +1496,9 @@ export const make: (params: {
|
|
|
1480
1496
|
FiberSet.awaitEmpty(toolCallFibers)
|
|
1481
1497
|
)
|
|
1482
1498
|
),
|
|
1499
|
+
Effect.andThen(
|
|
1500
|
+
Queue.offerAll(queue, deferredFinishParts)
|
|
1501
|
+
),
|
|
1483
1502
|
// And then end the queue
|
|
1484
1503
|
Effect.andThen(Queue.end(queue)),
|
|
1485
1504
|
Effect.tapCause((cause) => Queue.failCause(queue, cause)),
|
|
@@ -406,8 +406,9 @@ interface Row {
|
|
|
406
406
|
* Renders a table with aligned columns.
|
|
407
407
|
* @internal
|
|
408
408
|
*/
|
|
409
|
-
const renderTable = (rows: ReadonlyArray<Row>, widthCap
|
|
410
|
-
const
|
|
409
|
+
const renderTable = (rows: ReadonlyArray<Row>, widthCap?: number) => {
|
|
410
|
+
const maxColumn = Math.max(...rows.map((r) => visualLength(r.left))) + 4
|
|
411
|
+
const col = widthCap === undefined ? maxColumn : Math.min(maxColumn, widthCap)
|
|
411
412
|
return rows.map(({ left, right }) => ` ${pad(left, col)}${right}`).join("\n")
|
|
412
413
|
}
|
|
413
414
|
|
|
@@ -497,7 +498,7 @@ const formatHelpDocImpl = (doc: HelpDoc, colors: ColorFunctions): string => {
|
|
|
497
498
|
}
|
|
498
499
|
})
|
|
499
500
|
|
|
500
|
-
sections.push(renderTable(flagRows
|
|
501
|
+
sections.push(renderTable(flagRows))
|
|
501
502
|
sections.push("")
|
|
502
503
|
}
|
|
503
504
|
|
|
@@ -525,7 +526,7 @@ const formatHelpDocImpl = (doc: HelpDoc, colors: ColorFunctions): string => {
|
|
|
525
526
|
}
|
|
526
527
|
})
|
|
527
528
|
|
|
528
|
-
sections.push(renderTable(globalFlagRows
|
|
529
|
+
sections.push(renderTable(globalFlagRows))
|
|
529
530
|
sections.push("")
|
|
530
531
|
}
|
|
531
532
|
|
|
@@ -739,7 +739,7 @@ export const file = (options: FileOptions = {}): Prompt<string> => {
|
|
|
739
739
|
const currentPath = yield* resolveCurrentPath(Option.none(), opts)
|
|
740
740
|
const files = yield* getFileList(currentPath, opts)
|
|
741
741
|
const confirm = Confirm.Hide()
|
|
742
|
-
return { cursor: 0, files, path: Option.none(), confirm }
|
|
742
|
+
return { cursor: 0, files, allFiles: files, query: "", path: Option.none(), confirm }
|
|
743
743
|
})
|
|
744
744
|
return custom(initialState, {
|
|
745
745
|
render: handleFileRender(opts),
|
|
@@ -1855,11 +1855,16 @@ interface FileOptionsReq extends Required<Omit<FileOptions, "startingPath">> {
|
|
|
1855
1855
|
interface FileState {
|
|
1856
1856
|
readonly cursor: number
|
|
1857
1857
|
readonly files: ReadonlyArray<string>
|
|
1858
|
+
readonly allFiles: ReadonlyArray<string>
|
|
1859
|
+
readonly query: string
|
|
1858
1860
|
readonly path: Option.Option<string>
|
|
1859
1861
|
readonly confirm: Confirm
|
|
1860
1862
|
}
|
|
1861
1863
|
|
|
1862
1864
|
const CONFIRM_MESSAGE = "The selected directory contains files. Would you like to traverse the selected directory?"
|
|
1865
|
+
const FILE_FILTER_LABEL = "filter"
|
|
1866
|
+
const FILE_FILTER_PLACEHOLDER = "type to filter"
|
|
1867
|
+
const FILE_EMPTY_MESSAGE = "No matches"
|
|
1863
1868
|
type Confirm = Data.TaggedEnum<{
|
|
1864
1869
|
readonly Show: {}
|
|
1865
1870
|
readonly Hide: {}
|
|
@@ -1914,6 +1919,40 @@ const getFileList = Effect.fnUntraced(function*(directory: string, options: File
|
|
|
1914
1919
|
}, { concurrency: files.length })
|
|
1915
1920
|
})
|
|
1916
1921
|
|
|
1922
|
+
const filterFiles = (files: ReadonlyArray<string>, query: string) => {
|
|
1923
|
+
if (query.length === 0) {
|
|
1924
|
+
return files
|
|
1925
|
+
}
|
|
1926
|
+
const normalizedQuery = query.toLowerCase()
|
|
1927
|
+
const filtered: Array<string> = []
|
|
1928
|
+
for (let index = 0; index < files.length; index++) {
|
|
1929
|
+
if (files[index].toLowerCase().includes(normalizedQuery)) {
|
|
1930
|
+
filtered.push(files[index])
|
|
1931
|
+
}
|
|
1932
|
+
}
|
|
1933
|
+
return filtered
|
|
1934
|
+
}
|
|
1935
|
+
|
|
1936
|
+
const updateFileState = (
|
|
1937
|
+
state: FileState,
|
|
1938
|
+
query: string,
|
|
1939
|
+
allFiles: ReadonlyArray<string> = state.allFiles
|
|
1940
|
+
): FileState => {
|
|
1941
|
+
const files = filterFiles(allFiles, query)
|
|
1942
|
+
if (files.length === 0) {
|
|
1943
|
+
return { ...state, query, allFiles, files, cursor: 0 }
|
|
1944
|
+
}
|
|
1945
|
+
const selected = state.files[state.cursor]
|
|
1946
|
+
const cursor = selected === undefined ? 0 : files.indexOf(selected)
|
|
1947
|
+
return {
|
|
1948
|
+
...state,
|
|
1949
|
+
query,
|
|
1950
|
+
allFiles,
|
|
1951
|
+
files,
|
|
1952
|
+
cursor: cursor === -1 ? 0 : cursor
|
|
1953
|
+
}
|
|
1954
|
+
}
|
|
1955
|
+
|
|
1917
1956
|
const handleFileClear = (options: FileOptionsReq) => {
|
|
1918
1957
|
return Effect.fnUntraced(function*(state: FileState, _: Action<FileState, string>) {
|
|
1919
1958
|
const terminal = yield* Terminal.Terminal
|
|
@@ -1922,12 +1961,14 @@ const handleFileClear = (options: FileOptionsReq) => {
|
|
|
1922
1961
|
const figures = yield* platformFigures
|
|
1923
1962
|
const currentPath = yield* resolveCurrentPath(state.path, options)
|
|
1924
1963
|
const selectedPath = state.files[state.cursor]
|
|
1925
|
-
const resolvedPath = path.resolve(currentPath, selectedPath)
|
|
1964
|
+
const resolvedPath = selectedPath === undefined ? currentPath : path.resolve(currentPath, selectedPath)
|
|
1926
1965
|
const resolvedPathText = `${figures.pointerSmall} ${resolvedPath}`
|
|
1927
1966
|
const isConfirming = showConfirmation(state.confirm)
|
|
1928
1967
|
const promptText = isConfirming
|
|
1929
1968
|
? renderPrompt("(Y/n)", CONFIRM_MESSAGE, "?", figures.pointerSmall, { plain: true })
|
|
1930
|
-
: renderPrompt(
|
|
1969
|
+
: renderPrompt(renderFileFilter(state, { plain: true }), options.message, figures.tick, figures.ellipsis, {
|
|
1970
|
+
plain: true
|
|
1971
|
+
})
|
|
1931
1972
|
const filesText = isConfirming
|
|
1932
1973
|
? ""
|
|
1933
1974
|
: renderFiles(state, state.files, figures, options, { plain: true })
|
|
@@ -1995,6 +2036,17 @@ const renderFileName = (file: string, isSelected: boolean, renderOptions?: Rende
|
|
|
1995
2036
|
: file
|
|
1996
2037
|
}
|
|
1997
2038
|
|
|
2039
|
+
const renderFileFilter = (state: FileState, renderOptions?: RenderOptions | undefined) => {
|
|
2040
|
+
const filterValue = state.query.length === 0
|
|
2041
|
+
? renderOptions?.plain === true
|
|
2042
|
+
? FILE_FILTER_PLACEHOLDER
|
|
2043
|
+
: Ansi.annotate(FILE_FILTER_PLACEHOLDER, Ansi.blackBright)
|
|
2044
|
+
: renderOptions?.plain === true
|
|
2045
|
+
? state.query
|
|
2046
|
+
: Ansi.annotate(state.query, Ansi.combine(Ansi.underlined, Ansi.cyanBright))
|
|
2047
|
+
return `[${FILE_FILTER_LABEL}: ${filterValue}]`
|
|
2048
|
+
}
|
|
2049
|
+
|
|
1998
2050
|
const renderFiles = (
|
|
1999
2051
|
state: FileState,
|
|
2000
2052
|
files: ReadonlyArray<string>,
|
|
@@ -2003,6 +2055,11 @@ const renderFiles = (
|
|
|
2003
2055
|
renderOptions?: RenderOptions | undefined
|
|
2004
2056
|
) => {
|
|
2005
2057
|
const length = files.length
|
|
2058
|
+
if (length === 0) {
|
|
2059
|
+
return renderOptions?.plain === true
|
|
2060
|
+
? FILE_EMPTY_MESSAGE
|
|
2061
|
+
: Ansi.annotate(FILE_EMPTY_MESSAGE, Ansi.blackBright)
|
|
2062
|
+
}
|
|
2006
2063
|
const toDisplay = entriesToDisplay(state.cursor, length, options.maxPerPage)
|
|
2007
2064
|
const documents: Array<string> = []
|
|
2008
2065
|
for (let index = toDisplay.startIndex; index < toDisplay.endIndex; index++) {
|
|
@@ -2019,7 +2076,7 @@ const renderFileNextFrame = Effect.fnUntraced(function*(state: FileState, option
|
|
|
2019
2076
|
const figures = yield* platformFigures
|
|
2020
2077
|
const currentPath = yield* resolveCurrentPath(state.path, options)
|
|
2021
2078
|
const selectedPath = state.files[state.cursor]
|
|
2022
|
-
const resolvedPath = path.resolve(currentPath, selectedPath)
|
|
2079
|
+
const resolvedPath = selectedPath === undefined ? currentPath : path.resolve(currentPath, selectedPath)
|
|
2023
2080
|
const resolvedPathMsg = Ansi.annotate(figures.pointerSmall + " " + resolvedPath, Ansi.blackBright)
|
|
2024
2081
|
|
|
2025
2082
|
if (showConfirmation(state.confirm)) {
|
|
@@ -2031,33 +2088,36 @@ const renderFileNextFrame = Effect.fnUntraced(function*(state: FileState, option
|
|
|
2031
2088
|
}
|
|
2032
2089
|
const leadingSymbol = Ansi.annotate(figures.tick, Ansi.green)
|
|
2033
2090
|
const trailingSymbol = Ansi.annotate(figures.ellipsis, Ansi.blackBright)
|
|
2034
|
-
const promptMsg = renderPrompt(
|
|
2091
|
+
const promptMsg = renderPrompt(renderFileFilter(state), options.message, leadingSymbol, trailingSymbol)
|
|
2035
2092
|
const files = renderFiles(state, state.files, figures, options)
|
|
2036
2093
|
return Ansi.cursorHide + promptMsg + "\n" + resolvedPathMsg + "\n" + files
|
|
2037
2094
|
})
|
|
2038
2095
|
|
|
2039
|
-
const renderFileSubmission = Effect.fnUntraced(function*(value: string, options: FileOptionsReq) {
|
|
2096
|
+
const renderFileSubmission = Effect.fnUntraced(function*(state: FileState, value: string, options: FileOptionsReq) {
|
|
2040
2097
|
const figures = yield* platformFigures
|
|
2041
2098
|
const leadingSymbol = Ansi.annotate(figures.tick, Ansi.green)
|
|
2042
2099
|
const trailingSymbol = Ansi.annotate(figures.ellipsis, Ansi.blackBright)
|
|
2043
|
-
const promptMsg = renderPrompt(
|
|
2100
|
+
const promptMsg = renderPrompt(renderFileFilter(state), options.message, leadingSymbol, trailingSymbol)
|
|
2044
2101
|
return promptMsg + " " + Ansi.annotate(value, Ansi.white) + "\n"
|
|
2045
2102
|
})
|
|
2046
2103
|
|
|
2047
2104
|
const handleFileRender = (options: FileOptionsReq) => {
|
|
2048
2105
|
return (
|
|
2049
|
-
|
|
2106
|
+
state: FileState,
|
|
2050
2107
|
action: Action<FileState, string>
|
|
2051
2108
|
): Effect.Effect<string, never, Path.Path | FileSystem.FileSystem> => {
|
|
2052
2109
|
return Action.$match(action, {
|
|
2053
2110
|
Beep: () => Effect.succeed(renderBeep),
|
|
2054
2111
|
NextFrame: ({ state }) => renderFileNextFrame(state, options),
|
|
2055
|
-
Submit: ({ value }) => renderFileSubmission(value, options)
|
|
2112
|
+
Submit: ({ value }) => renderFileSubmission(state, value, options)
|
|
2056
2113
|
})
|
|
2057
2114
|
}
|
|
2058
2115
|
}
|
|
2059
2116
|
|
|
2060
2117
|
const processFileCursorUp = (state: FileState) => {
|
|
2118
|
+
if (state.files.length === 0) {
|
|
2119
|
+
return Effect.succeed(Action.Beep())
|
|
2120
|
+
}
|
|
2061
2121
|
const cursor = state.cursor - 1
|
|
2062
2122
|
return Effect.succeed(Action.NextFrame({
|
|
2063
2123
|
state: { ...state, cursor: cursor < 0 ? state.files.length - 1 : cursor }
|
|
@@ -2065,12 +2125,36 @@ const processFileCursorUp = (state: FileState) => {
|
|
|
2065
2125
|
}
|
|
2066
2126
|
|
|
2067
2127
|
const processFileCursorDown = (state: FileState) => {
|
|
2128
|
+
if (state.files.length === 0) {
|
|
2129
|
+
return Effect.succeed(Action.Beep())
|
|
2130
|
+
}
|
|
2068
2131
|
return Effect.succeed(Action.NextFrame({
|
|
2069
2132
|
state: { ...state, cursor: (state.cursor + 1) % state.files.length }
|
|
2070
2133
|
}))
|
|
2071
2134
|
}
|
|
2072
2135
|
|
|
2136
|
+
const processFileBackspace = (state: FileState) => {
|
|
2137
|
+
if (state.query.length === 0) {
|
|
2138
|
+
return Effect.succeed(Action.Beep())
|
|
2139
|
+
}
|
|
2140
|
+
const query = state.query.slice(0, state.query.length - 1)
|
|
2141
|
+
return Effect.succeed(Action.NextFrame({ state: updateFileState(state, query) }))
|
|
2142
|
+
}
|
|
2143
|
+
|
|
2144
|
+
const processFileClear = (state: FileState) => Effect.succeed(Action.NextFrame({ state: updateFileState(state, "") }))
|
|
2145
|
+
|
|
2146
|
+
const processFileInput = (input: string, state: FileState) => {
|
|
2147
|
+
if (input.length === 0) {
|
|
2148
|
+
return Effect.succeed(Action.Beep())
|
|
2149
|
+
}
|
|
2150
|
+
const query = state.query + input
|
|
2151
|
+
return Effect.succeed(Action.NextFrame({ state: updateFileState(state, query) }))
|
|
2152
|
+
}
|
|
2153
|
+
|
|
2073
2154
|
const processSelection = Effect.fnUntraced(function*(state: FileState, options: FileOptionsReq) {
|
|
2155
|
+
if (state.files.length === 0) {
|
|
2156
|
+
return Action.Beep()
|
|
2157
|
+
}
|
|
2074
2158
|
const fs = yield* FileSystem.FileSystem
|
|
2075
2159
|
const path = yield* Path.Path
|
|
2076
2160
|
const currentPath = yield* resolveCurrentPath(state.path, options)
|
|
@@ -2097,6 +2181,8 @@ const processSelection = Effect.fnUntraced(function*(state: FileState, options:
|
|
|
2097
2181
|
state: {
|
|
2098
2182
|
cursor: 0,
|
|
2099
2183
|
files,
|
|
2184
|
+
allFiles: files,
|
|
2185
|
+
query: "",
|
|
2100
2186
|
path: Option.some(resolvedPath),
|
|
2101
2187
|
confirm: Confirm.Hide()
|
|
2102
2188
|
}
|
|
@@ -2107,6 +2193,12 @@ const processSelection = Effect.fnUntraced(function*(state: FileState, options:
|
|
|
2107
2193
|
|
|
2108
2194
|
const handleFileProcess = (options: FileOptionsReq) => {
|
|
2109
2195
|
return Effect.fnUntraced(function*(input: Terminal.UserInput, state: FileState) {
|
|
2196
|
+
if (input.key.ctrl && input.key.name === "u") {
|
|
2197
|
+
if (showConfirmation(state.confirm)) {
|
|
2198
|
+
return Action.Beep()
|
|
2199
|
+
}
|
|
2200
|
+
return yield* processFileClear(state)
|
|
2201
|
+
}
|
|
2110
2202
|
switch (input.key.name) {
|
|
2111
2203
|
case "k":
|
|
2112
2204
|
case "up": {
|
|
@@ -2117,6 +2209,12 @@ const handleFileProcess = (options: FileOptionsReq) => {
|
|
|
2117
2209
|
case "tab": {
|
|
2118
2210
|
return yield* processFileCursorDown(state)
|
|
2119
2211
|
}
|
|
2212
|
+
case "backspace": {
|
|
2213
|
+
if (showConfirmation(state.confirm)) {
|
|
2214
|
+
return Action.Beep()
|
|
2215
|
+
}
|
|
2216
|
+
return yield* processFileBackspace(state)
|
|
2217
|
+
}
|
|
2120
2218
|
case "enter":
|
|
2121
2219
|
case "return": {
|
|
2122
2220
|
return yield* processSelection(state, options)
|
|
@@ -2133,12 +2231,14 @@ const handleFileProcess = (options: FileOptionsReq) => {
|
|
|
2133
2231
|
state: {
|
|
2134
2232
|
cursor: 0,
|
|
2135
2233
|
files,
|
|
2234
|
+
allFiles: files,
|
|
2235
|
+
query: "",
|
|
2136
2236
|
path: Option.some(resolvedPath),
|
|
2137
2237
|
confirm: Confirm.Hide()
|
|
2138
2238
|
}
|
|
2139
2239
|
})
|
|
2140
2240
|
}
|
|
2141
|
-
return
|
|
2241
|
+
return yield* processFileInput(Option.getOrElse(input.input, () => ""), state)
|
|
2142
2242
|
}
|
|
2143
2243
|
case "n":
|
|
2144
2244
|
case "f": {
|
|
@@ -2149,10 +2249,13 @@ const handleFileProcess = (options: FileOptionsReq) => {
|
|
|
2149
2249
|
const resolvedPath = path.resolve(currentPath, selectedPath)
|
|
2150
2250
|
return Action.Submit({ value: resolvedPath })
|
|
2151
2251
|
}
|
|
2152
|
-
return
|
|
2252
|
+
return yield* processFileInput(Option.getOrElse(input.input, () => ""), state)
|
|
2153
2253
|
}
|
|
2154
2254
|
default: {
|
|
2155
|
-
|
|
2255
|
+
if (showConfirmation(state.confirm)) {
|
|
2256
|
+
return Action.Beep()
|
|
2257
|
+
}
|
|
2258
|
+
return yield* processFileInput(Option.getOrElse(input.input, () => ""), state)
|
|
2156
2259
|
}
|
|
2157
2260
|
}
|
|
2158
2261
|
})
|
|
@@ -3259,6 +3362,20 @@ const processTextCursorRight = (state: TextState) => {
|
|
|
3259
3362
|
)
|
|
3260
3363
|
}
|
|
3261
3364
|
|
|
3365
|
+
const processTextCursorStart = (state: TextState) =>
|
|
3366
|
+
Effect.succeed(
|
|
3367
|
+
Action.NextFrame({
|
|
3368
|
+
state: { ...state, cursor: 0, error: Option.none() }
|
|
3369
|
+
})
|
|
3370
|
+
)
|
|
3371
|
+
|
|
3372
|
+
const processTextCursorEnd = (state: TextState) =>
|
|
3373
|
+
Effect.succeed(
|
|
3374
|
+
Action.NextFrame({
|
|
3375
|
+
state: { ...state, cursor: state.value.length, error: Option.none() }
|
|
3376
|
+
})
|
|
3377
|
+
)
|
|
3378
|
+
|
|
3262
3379
|
const processTab = (state: TextState, options: TextOptionsReq) => {
|
|
3263
3380
|
if (state.value === options.default) {
|
|
3264
3381
|
return Effect.succeed(Action.Beep())
|
|
@@ -3295,8 +3412,18 @@ const handleTextRender = (options: TextOptionsReq) => {
|
|
|
3295
3412
|
|
|
3296
3413
|
const handleTextProcess = (options: TextOptionsReq) => {
|
|
3297
3414
|
return (input: Terminal.UserInput, state: TextState) => {
|
|
3298
|
-
if (input.key.ctrl
|
|
3299
|
-
|
|
3415
|
+
if (input.key.ctrl) {
|
|
3416
|
+
switch (input.key.name) {
|
|
3417
|
+
case "u": {
|
|
3418
|
+
return processTextClear(state)
|
|
3419
|
+
}
|
|
3420
|
+
case "a": {
|
|
3421
|
+
return processTextCursorStart(state)
|
|
3422
|
+
}
|
|
3423
|
+
case "e": {
|
|
3424
|
+
return processTextCursorEnd(state)
|
|
3425
|
+
}
|
|
3426
|
+
}
|
|
3300
3427
|
}
|
|
3301
3428
|
switch (input.key.name) {
|
|
3302
3429
|
case "backspace": {
|
|
@@ -3308,6 +3435,12 @@ const handleTextProcess = (options: TextOptionsReq) => {
|
|
|
3308
3435
|
case "right": {
|
|
3309
3436
|
return processTextCursorRight(state)
|
|
3310
3437
|
}
|
|
3438
|
+
case "home": {
|
|
3439
|
+
return processTextCursorStart(state)
|
|
3440
|
+
}
|
|
3441
|
+
case "end": {
|
|
3442
|
+
return processTextCursorEnd(state)
|
|
3443
|
+
}
|
|
3311
3444
|
case "enter":
|
|
3312
3445
|
case "return": {
|
|
3313
3446
|
const value = state.value
|
|
@@ -175,8 +175,8 @@ export function getErrorSchemas(endpoint: AnyWithProps): [Schema.Top, ...Array<S
|
|
|
175
175
|
const schemas = new Set<Schema.Top>(endpoint.error)
|
|
176
176
|
for (const middleware of endpoint.middlewares) {
|
|
177
177
|
const key = middleware as any as HttpApiMiddleware.AnyService
|
|
178
|
-
|
|
179
|
-
schemas.add(
|
|
178
|
+
for (const schema of key.error) {
|
|
179
|
+
schemas.add(schema)
|
|
180
180
|
}
|
|
181
181
|
}
|
|
182
182
|
return Arr.append(Array.from(schemas), BadRequestFromSchemaError)
|