effect 4.0.0-beta.35 → 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.map +1 -1
- package/dist/Channel.js +9 -7
- package/dist/Channel.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/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/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 +29 -5
- package/dist/Schema.d.ts.map +1 -1
- package/dist/Schema.js +20 -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/internal/effect.js +3 -2
- package/dist/internal/effect.js.map +1 -1
- package/dist/unstable/ai/LanguageModel.d.ts.map +1 -1
- package/dist/unstable/ai/LanguageModel.js +15 -3
- package/dist/unstable/ai/LanguageModel.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/package.json +1 -1
- package/src/Channel.ts +12 -10
- package/src/Cron.ts +142 -45
- package/src/Equivalence.ts +56 -0
- package/src/LayerMap.ts +7 -5
- package/src/PubSub.ts +13 -5
- package/src/Schema.ts +32 -8
- package/src/SchemaParser.ts +53 -0
- package/src/internal/effect.ts +3 -2
- package/src/unstable/ai/LanguageModel.ts +17 -2
- 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/PubSub.ts
CHANGED
|
@@ -41,6 +41,7 @@ import { nextPow2 } from "./Number.ts"
|
|
|
41
41
|
import * as Option from "./Option.ts"
|
|
42
42
|
import { type Pipeable, pipeArguments } from "./Pipeable.ts"
|
|
43
43
|
import * as Scope from "./Scope.ts"
|
|
44
|
+
import * as ServiceMap from "./ServiceMap.ts"
|
|
44
45
|
import type { Covariant, Invariant } from "./Types.ts"
|
|
45
46
|
|
|
46
47
|
const TypeId = "~effect/PubSub"
|
|
@@ -881,7 +882,7 @@ export const publish: {
|
|
|
881
882
|
} = dual(2, <A>(self: PubSub<A>, value: A): Effect.Effect<boolean> =>
|
|
882
883
|
Effect.suspend(() => {
|
|
883
884
|
if (self.shutdownFlag.current) {
|
|
884
|
-
return Effect.
|
|
885
|
+
return Effect.succeed(false)
|
|
885
886
|
}
|
|
886
887
|
|
|
887
888
|
if (self.pubsub.publish(value)) {
|
|
@@ -1110,7 +1111,7 @@ export const publishAll: {
|
|
|
1110
1111
|
} = dual(2, <A>(self: PubSub<A>, elements: Iterable<A>): Effect.Effect<boolean> =>
|
|
1111
1112
|
Effect.suspend(() => {
|
|
1112
1113
|
if (self.shutdownFlag.current) {
|
|
1113
|
-
return Effect.
|
|
1114
|
+
return Effect.succeed(false)
|
|
1114
1115
|
}
|
|
1115
1116
|
const surplus = self.pubsub.publishAll(elements)
|
|
1116
1117
|
self.strategy.completeSubscribersUnsafe(self.pubsub, self.subscribers)
|
|
@@ -1173,9 +1174,16 @@ export const publishAll: {
|
|
|
1173
1174
|
* @category subscription
|
|
1174
1175
|
*/
|
|
1175
1176
|
export const subscribe = <A>(self: PubSub<A>): Effect.Effect<Subscription<A>, never, Scope.Scope> =>
|
|
1176
|
-
Effect.
|
|
1177
|
-
Effect.
|
|
1178
|
-
|
|
1177
|
+
Effect.uninterruptible(
|
|
1178
|
+
Effect.servicesWith((services) => {
|
|
1179
|
+
const localScope = ServiceMap.get(services, Scope.Scope)
|
|
1180
|
+
const scope = Scope.forkUnsafe(self.scope)
|
|
1181
|
+
const subscription = makeSubscriptionUnsafe(self.pubsub, self.subscribers, self.strategy)
|
|
1182
|
+
return Scope.addFinalizer(scope, unsubscribe(subscription)).pipe(
|
|
1183
|
+
Effect.andThen(Scope.addFinalizerExit(localScope, (exit) => Scope.close(scope, exit))),
|
|
1184
|
+
Effect.as(subscription)
|
|
1185
|
+
)
|
|
1186
|
+
})
|
|
1179
1187
|
)
|
|
1180
1188
|
|
|
1181
1189
|
const unsubscribe = <A>(self: Subscription<A>): Effect.Effect<void> =>
|
package/src/Schema.ts
CHANGED
|
@@ -1209,6 +1209,18 @@ export const decodeOption = Parser.decodeOption
|
|
|
1209
1209
|
* @category Decoding
|
|
1210
1210
|
* @since 4.0.0
|
|
1211
1211
|
*/
|
|
1212
|
+
export const decodeUnknownResult = Parser.decodeUnknownResult
|
|
1213
|
+
|
|
1214
|
+
/**
|
|
1215
|
+
* @category Decoding
|
|
1216
|
+
* @since 4.0.0
|
|
1217
|
+
*/
|
|
1218
|
+
export const decodeResult = Parser.decodeResult
|
|
1219
|
+
|
|
1220
|
+
/**
|
|
1221
|
+
* @category Decoding
|
|
1222
|
+
* @since 4.0.0
|
|
1223
|
+
*/
|
|
1212
1224
|
export const decodeUnknownPromise = Parser.decodeUnknownPromise
|
|
1213
1225
|
|
|
1214
1226
|
/**
|
|
@@ -1367,6 +1379,18 @@ export const encodeOption = Parser.encodeOption
|
|
|
1367
1379
|
* @category Encoding
|
|
1368
1380
|
* @since 4.0.0
|
|
1369
1381
|
*/
|
|
1382
|
+
export const encodeUnknownResult = Parser.encodeUnknownResult
|
|
1383
|
+
|
|
1384
|
+
/**
|
|
1385
|
+
* @category Encoding
|
|
1386
|
+
* @since 4.0.0
|
|
1387
|
+
*/
|
|
1388
|
+
export const encodeResult = Parser.encodeResult
|
|
1389
|
+
|
|
1390
|
+
/**
|
|
1391
|
+
* @category Encoding
|
|
1392
|
+
* @since 4.0.0
|
|
1393
|
+
*/
|
|
1370
1394
|
export const encodeUnknownPromise = Parser.encodeUnknownPromise
|
|
1371
1395
|
|
|
1372
1396
|
/**
|
|
@@ -2276,7 +2300,7 @@ export declare namespace Struct {
|
|
|
2276
2300
|
/**
|
|
2277
2301
|
* @since 4.0.0
|
|
2278
2302
|
*/
|
|
2279
|
-
export type Type<F extends Fields> = Type_<F
|
|
2303
|
+
export type Type<F extends Fields> = Simplify<Type_<F>>
|
|
2280
2304
|
|
|
2281
2305
|
type Iso_<
|
|
2282
2306
|
F extends Fields,
|
|
@@ -2291,7 +2315,7 @@ export declare namespace Struct {
|
|
|
2291
2315
|
/**
|
|
2292
2316
|
* @since 4.0.0
|
|
2293
2317
|
*/
|
|
2294
|
-
export type Iso<F extends Fields> = Iso_<F
|
|
2318
|
+
export type Iso<F extends Fields> = Simplify<Iso_<F>>
|
|
2295
2319
|
|
|
2296
2320
|
type EncodedOptionalKeys<Fields extends Struct.Fields> = {
|
|
2297
2321
|
[K in keyof Fields]: Fields[K] extends { readonly "~encoded.optionality": "optional" } ? K
|
|
@@ -2316,7 +2340,7 @@ export declare namespace Struct {
|
|
|
2316
2340
|
/**
|
|
2317
2341
|
* @since 4.0.0
|
|
2318
2342
|
*/
|
|
2319
|
-
export type Encoded<F extends Fields> = Encoded_<F
|
|
2343
|
+
export type Encoded<F extends Fields> = Simplify<Encoded_<F>>
|
|
2320
2344
|
|
|
2321
2345
|
/**
|
|
2322
2346
|
* @since 4.0.0
|
|
@@ -2343,7 +2367,7 @@ export declare namespace Struct {
|
|
|
2343
2367
|
/**
|
|
2344
2368
|
* @since 4.0.0
|
|
2345
2369
|
*/
|
|
2346
|
-
export type MakeIn<F extends Fields> = MakeIn_<F
|
|
2370
|
+
export type MakeIn<F extends Fields> = Simplify<MakeIn_<F>>
|
|
2347
2371
|
}
|
|
2348
2372
|
|
|
2349
2373
|
/**
|
|
@@ -2351,14 +2375,14 @@ export declare namespace Struct {
|
|
|
2351
2375
|
*/
|
|
2352
2376
|
export interface Struct<Fields extends Struct.Fields> extends
|
|
2353
2377
|
Bottom<
|
|
2354
|
-
|
|
2355
|
-
|
|
2378
|
+
Struct.Type<Fields>,
|
|
2379
|
+
Struct.Encoded<Fields>,
|
|
2356
2380
|
Struct.DecodingServices<Fields>,
|
|
2357
2381
|
Struct.EncodingServices<Fields>,
|
|
2358
2382
|
AST.Objects,
|
|
2359
2383
|
Struct<Fields>,
|
|
2360
|
-
|
|
2361
|
-
|
|
2384
|
+
Struct.MakeIn<Fields>,
|
|
2385
|
+
Struct.Iso<Fields>
|
|
2362
2386
|
>
|
|
2363
2387
|
{
|
|
2364
2388
|
readonly "~rebuild.out": this
|
package/src/SchemaParser.ts
CHANGED
|
@@ -204,6 +204,24 @@ export const decodeOption: <S extends Schema.Top & { readonly DecodingServices:
|
|
|
204
204
|
schema: S
|
|
205
205
|
) => (input: S["Encoded"], options?: AST.ParseOptions) => Option.Option<S["Type"]> = decodeUnknownOption
|
|
206
206
|
|
|
207
|
+
/**
|
|
208
|
+
* @category Decoding
|
|
209
|
+
* @since 4.0.0
|
|
210
|
+
*/
|
|
211
|
+
export function decodeUnknownResult<S extends Schema.Top & { readonly DecodingServices: never }>(
|
|
212
|
+
schema: S
|
|
213
|
+
): (input: unknown, options?: AST.ParseOptions) => Result.Result<S["Type"], Issue.Issue> {
|
|
214
|
+
return asResult(decodeUnknownEffect(schema))
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* @category Decoding
|
|
219
|
+
* @since 4.0.0
|
|
220
|
+
*/
|
|
221
|
+
export const decodeResult: <S extends Schema.Top & { readonly DecodingServices: never }>(
|
|
222
|
+
schema: S
|
|
223
|
+
) => (input: S["Encoded"], options?: AST.ParseOptions) => Result.Result<S["Type"], Issue.Issue> = decodeUnknownResult
|
|
224
|
+
|
|
207
225
|
/**
|
|
208
226
|
* @category Decoding
|
|
209
227
|
* @since 4.0.0
|
|
@@ -293,6 +311,24 @@ export const encodeOption: <S extends Schema.Top & { readonly EncodingServices:
|
|
|
293
311
|
schema: S
|
|
294
312
|
) => (input: S["Type"], options?: AST.ParseOptions) => Option.Option<S["Encoded"]> = encodeUnknownOption
|
|
295
313
|
|
|
314
|
+
/**
|
|
315
|
+
* @category Encoding
|
|
316
|
+
* @since 4.0.0
|
|
317
|
+
*/
|
|
318
|
+
export function encodeUnknownResult<S extends Schema.Top & { readonly EncodingServices: never }>(
|
|
319
|
+
schema: S
|
|
320
|
+
): (input: unknown, options?: AST.ParseOptions) => Result.Result<S["Encoded"], Issue.Issue> {
|
|
321
|
+
return asResult(encodeUnknownEffect(schema))
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* @category Encoding
|
|
326
|
+
* @since 4.0.0
|
|
327
|
+
*/
|
|
328
|
+
export const encodeResult: <S extends Schema.Top & { readonly EncodingServices: never }>(
|
|
329
|
+
schema: S
|
|
330
|
+
) => (input: S["Type"], options?: AST.ParseOptions) => Result.Result<S["Encoded"], Issue.Issue> = encodeUnknownResult
|
|
331
|
+
|
|
296
332
|
/**
|
|
297
333
|
* @category Encoding
|
|
298
334
|
* @since 4.0.0
|
|
@@ -343,6 +379,23 @@ export function asOption<T, E, R>(
|
|
|
343
379
|
return (input: E, options?: AST.ParseOptions) => Exit.getSuccess(parserExit(input, options))
|
|
344
380
|
}
|
|
345
381
|
|
|
382
|
+
function asResult<T, E, R>(
|
|
383
|
+
parser: (input: E, options?: AST.ParseOptions) => Effect.Effect<T, Issue.Issue, R>
|
|
384
|
+
): (input: E, options?: AST.ParseOptions) => Result.Result<T, Issue.Issue> {
|
|
385
|
+
const parserExit = asExit(parser)
|
|
386
|
+
return (input: E, options?: AST.ParseOptions) => {
|
|
387
|
+
const exit = parserExit(input, options)
|
|
388
|
+
if (Exit.isSuccess(exit)) {
|
|
389
|
+
return Result.succeed(exit.value)
|
|
390
|
+
}
|
|
391
|
+
const error = Cause.findError(exit.cause)
|
|
392
|
+
if (Result.isFailure(error)) {
|
|
393
|
+
throw Cause.squash(error.failure)
|
|
394
|
+
}
|
|
395
|
+
return Result.fail(error.success)
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
346
399
|
function asSync<T, E, R>(
|
|
347
400
|
parser: (input: E, options?: AST.ParseOptions) => Effect.Effect<T, Issue.Issue, R>
|
|
348
401
|
): (input: E, options?: AST.ParseOptions) => T {
|
package/src/internal/effect.ts
CHANGED
|
@@ -4063,7 +4063,8 @@ export const cachedInvalidateWithTTL: {
|
|
|
4063
4063
|
const wait = flatMap(latch.await, () => exit!)
|
|
4064
4064
|
return [
|
|
4065
4065
|
withFiber((fiber) => {
|
|
4066
|
-
const
|
|
4066
|
+
const clock = fiber.getRef(ClockRef)
|
|
4067
|
+
const now = isFinite ? clock.currentTimeMillisUnsafe() : 0
|
|
4067
4068
|
if (running || now < expiresAt) return exit ?? wait
|
|
4068
4069
|
running = true
|
|
4069
4070
|
latch.closeUnsafe()
|
|
@@ -4071,7 +4072,7 @@ export const cachedInvalidateWithTTL: {
|
|
|
4071
4072
|
return onExit(self, (exit_) =>
|
|
4072
4073
|
sync(() => {
|
|
4073
4074
|
running = false
|
|
4074
|
-
expiresAt =
|
|
4075
|
+
expiresAt = clock.currentTimeMillisUnsafe() + ttlMillis
|
|
4075
4076
|
exit = exit_
|
|
4076
4077
|
latch.openUnsafe()
|
|
4077
4078
|
}))
|
|
@@ -1407,6 +1407,7 @@ export const make: (params: {
|
|
|
1407
1407
|
| Cause.Done
|
|
1408
1408
|
| Schema.SchemaError
|
|
1409
1409
|
>()
|
|
1410
|
+
const deferredFinishParts: Array<Response.StreamPart<Tools>> = []
|
|
1410
1411
|
|
|
1411
1412
|
// Emit pre-resolved tool results so Chat.streamText persists them to
|
|
1412
1413
|
// history. This ensures collectToolApprovals({ excludeResolved }) can
|
|
@@ -1465,8 +1466,19 @@ export const make: (params: {
|
|
|
1465
1466
|
}
|
|
1466
1467
|
}
|
|
1467
1468
|
}
|
|
1468
|
-
//
|
|
1469
|
-
|
|
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
|
+
}
|
|
1470
1482
|
// Fork tool call handlers - use the raw chunk for encoded params
|
|
1471
1483
|
for (const part of chunk) {
|
|
1472
1484
|
if (part.type === "tool-call" && part.providerExecuted !== true) {
|
|
@@ -1484,6 +1496,9 @@ export const make: (params: {
|
|
|
1484
1496
|
FiberSet.awaitEmpty(toolCallFibers)
|
|
1485
1497
|
)
|
|
1486
1498
|
),
|
|
1499
|
+
Effect.andThen(
|
|
1500
|
+
Queue.offerAll(queue, deferredFinishParts)
|
|
1501
|
+
),
|
|
1487
1502
|
// And then end the queue
|
|
1488
1503
|
Effect.andThen(Queue.end(queue)),
|
|
1489
1504
|
Effect.tapCause((cause) => Queue.failCause(queue, cause)),
|
|
@@ -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)
|