clanka 0.2.28 → 0.2.30
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/Agent.d.ts +2 -0
- package/dist/Agent.d.ts.map +1 -1
- package/dist/Agent.js +4 -2
- package/dist/Agent.js.map +1 -1
- package/dist/AgentExecutor.d.ts +227 -1
- package/dist/AgentExecutor.d.ts.map +1 -1
- package/dist/AgentTools.d.ts +123 -0
- package/dist/AgentTools.d.ts.map +1 -1
- package/dist/AgentTools.js +53 -0
- package/dist/AgentTools.js.map +1 -1
- package/dist/ScriptPreprocessing.d.ts.map +1 -1
- package/dist/ScriptPreprocessing.js +326 -292
- package/dist/ScriptPreprocessing.js.map +1 -1
- package/dist/ScriptPreprocessing.test.js +2 -0
- package/dist/ScriptPreprocessing.test.js.map +1 -1
- package/dist/cli.js +3 -4
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
- package/src/Agent.ts +6 -2
- package/src/AgentTools.ts +63 -0
- package/src/ScriptPreprocessing.test.ts +2 -0
- package/src/ScriptPreprocessing.ts +465 -366
- package/src/cli.ts +8 -5
- package/src/fixtures/patch13-broken.txt +149 -0
- package/src/fixtures/patch13-fixed.txt +149 -0
- package/src/fixtures/patch14-broken.txt +68 -0
- package/src/fixtures/patch14-fixed.txt +68 -0
package/src/cli.ts
CHANGED
|
@@ -197,12 +197,15 @@ Command.make("clanka", { provider, model, semantic, prompt }).pipe(
|
|
|
197
197
|
Effect.provide([
|
|
198
198
|
Agent.layerLocal({
|
|
199
199
|
directory: process.cwd(),
|
|
200
|
-
})
|
|
200
|
+
}).pipe(
|
|
201
|
+
Layer.provide(
|
|
202
|
+
Option.match(semantic, {
|
|
203
|
+
onNone: () => Layer.empty,
|
|
204
|
+
onSome: Search,
|
|
205
|
+
}),
|
|
206
|
+
),
|
|
207
|
+
),
|
|
201
208
|
Model,
|
|
202
|
-
Option.match(semantic, {
|
|
203
|
-
onNone: () => Layer.empty,
|
|
204
|
-
onSome: Search,
|
|
205
|
-
}),
|
|
206
209
|
]),
|
|
207
210
|
)
|
|
208
211
|
}),
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
await applyPatch(`*** Begin Patch
|
|
2
|
+
*** Update File: src/AgentTools.ts
|
|
3
|
+
@@
|
|
4
|
+
import * as ExaSearch from "./ExaSearch.ts"
|
|
5
|
+
import * as WebToMarkdown from "./WebToMarkdown.ts"
|
|
6
|
+
-import type * as HttpClient from "effect/unstable/http/HttpClient"
|
|
7
|
+
+import * as HttpClient from "effect/unstable/http/HttpClient"
|
|
8
|
+
@@
|
|
9
|
+
Tool.make("fetchMarkdown", {
|
|
10
|
+
description: "Fetch a web page and convert it to markdown.",
|
|
11
|
+
parameters: Schema.String.annotate({
|
|
12
|
+
identifier: "url",
|
|
13
|
+
}),
|
|
14
|
+
success: Schema.String,
|
|
15
|
+
}),
|
|
16
|
+
+ Tool.make("fetchJson", {
|
|
17
|
+
+ description: "Fetch a URL and parse the response body as JSON.",
|
|
18
|
+
+ parameters: Schema.String.annotate({
|
|
19
|
+
+ identifier: "url",
|
|
20
|
+
+ }),
|
|
21
|
+
+ success: Schema.Unknown,
|
|
22
|
+
+ }),
|
|
23
|
+
Tool.make("sleep", {
|
|
24
|
+
description: "Sleep for a specified number of milliseconds",
|
|
25
|
+
parameters: Schema.Finite.annotate({
|
|
26
|
+
identifier: "ms",
|
|
27
|
+
}),
|
|
28
|
+
@@
|
|
29
|
+
const pathService = yield* Path.Path
|
|
30
|
+
const webSearch = yield* ExaSearch.ExaSearch
|
|
31
|
+
const fetchMarkdown = yield* WebToMarkdown.WebToMarkdown
|
|
32
|
+
+ const httpClient = (yield* HttpClient.HttpClient).pipe(
|
|
33
|
+
+ HttpClient.followRedirects(),
|
|
34
|
+
+ HttpClient.filterStatusOk,
|
|
35
|
+
+ HttpClient.retryTransient({
|
|
36
|
+
+ times: 3,
|
|
37
|
+
+ }),
|
|
38
|
+
+ )
|
|
39
|
+
@@
|
|
40
|
+
fetchMarkdown: Effect.fn("AgentTools.fetchMarkdown")(function* (url) {
|
|
41
|
+
yield* Effect.logInfo(`Calling "fetchMarkdown"`).pipe(
|
|
42
|
+
Effect.annotateLogs({ url }),
|
|
43
|
+
)
|
|
44
|
+
return yield* fetchMarkdown.convertUrl(url)
|
|
45
|
+
}, Effect.orDie),
|
|
46
|
+
+ fetchJson: Effect.fn("AgentTools.fetchJson")(function* (url) {
|
|
47
|
+
+ yield* Effect.logInfo(`Calling "fetchJson"`).pipe(
|
|
48
|
+
+ Effect.annotateLogs({ url }),
|
|
49
|
+
+ )
|
|
50
|
+
+ const response = yield* httpClient.get(url)
|
|
51
|
+
+ return yield* response.json
|
|
52
|
+
+ }, Effect.orDie),
|
|
53
|
+
sleep: Effect.fn("AgentTools.sleep")(function* (ms) {
|
|
54
|
+
yield* Effect.logInfo(`Calling "sleep" for ${ms}ms`)
|
|
55
|
+
return yield* Effect.sleep(ms)
|
|
56
|
+
}),
|
|
57
|
+
*** Add File: src/AgentTools.test.ts
|
|
58
|
+
+import * as NodeServices from "@effect/platform-node/NodeServices"
|
|
59
|
+
+import { assert, describe, it } from "@effect/vitest"
|
|
60
|
+
+import * as Effect from "effect/Effect"
|
|
61
|
+
+import * as Stream from "effect/Stream"
|
|
62
|
+
+import * as Ref from "effect/Ref"
|
|
63
|
+
+import {
|
|
64
|
+
+ HttpClient,
|
|
65
|
+
+ type HttpClientRequest,
|
|
66
|
+
+ HttpClientResponse,
|
|
67
|
+
+} from "effect/unstable/http"
|
|
68
|
+
+import * as AgentTools from "./AgentTools.ts"
|
|
69
|
+
+import * as ToolkitRenderer from "./ToolkitRenderer.ts"
|
|
70
|
+
+
|
|
71
|
+
+describe("AgentTools", () => {
|
|
72
|
+
+ it.effect("renders fetchJson in the toolkit dts", () =>
|
|
73
|
+
+ Effect.gen(function* () {
|
|
74
|
+
+ const renderer = yield* ToolkitRenderer.ToolkitRenderer
|
|
75
|
+
+ const output = renderer.render(AgentTools.AgentTools)
|
|
76
|
+
+
|
|
77
|
+
+ assert.include(
|
|
78
|
+
+ output,
|
|
79
|
+
+ 'declare function fetchJson(url: string): Promise<unknown>',
|
|
80
|
+
+ )
|
|
81
|
+
+ }).pipe(Effect.provide(ToolkitRenderer.ToolkitRenderer.layer)),
|
|
82
|
+
+ )
|
|
83
|
+
+
|
|
84
|
+
+ it.effect("fetchJson follows redirects and parses json", () =>
|
|
85
|
+
+ Effect.gen(function* () {
|
|
86
|
+
+ const requests = yield* Ref.make<Array<string>>([])
|
|
87
|
+
+ const client = HttpClient.make(
|
|
88
|
+
+ (request: HttpClientRequest.HttpClientRequest) =>
|
|
89
|
+
+ Effect.gen(function* () {
|
|
90
|
+
+ yield* Ref.update(requests, (current) => [...current, request.url])
|
|
91
|
+
+
|
|
92
|
+
+ if (request.url === "https://example.com/start") {
|
|
93
|
+
+ return HttpClientResponse.fromWeb(
|
|
94
|
+
+ request,
|
|
95
|
+
+ new Response(null, {
|
|
96
|
+
+ status: 302,
|
|
97
|
+
+ headers: { location: "/data" },
|
|
98
|
+
+ }),
|
|
99
|
+
+ )
|
|
100
|
+
+ }
|
|
101
|
+
+
|
|
102
|
+
+ if (request.url === "https://example.com/data") {
|
|
103
|
+
+ return HttpClientResponse.fromWeb(
|
|
104
|
+
+ request,
|
|
105
|
+
+ new Response('{"message":"hello","count":1}', {
|
|
106
|
+
+ status: 200,
|
|
107
|
+
+ headers: { "content-type": "application/json" },
|
|
108
|
+
+ }),
|
|
109
|
+
+ )
|
|
110
|
+
+ }
|
|
111
|
+
+
|
|
112
|
+
+ return HttpClientResponse.fromWeb(
|
|
113
|
+
+ request,
|
|
114
|
+
+ new Response("not found", { status: 404 }),
|
|
115
|
+
+ )
|
|
116
|
+
+ }),
|
|
117
|
+
+ )
|
|
118
|
+
+
|
|
119
|
+
+ const toolkit = yield* AgentTools.AgentTools.asEffect()
|
|
120
|
+
+ const stream = yield* toolkit.handle(
|
|
121
|
+
+ "fetchJson",
|
|
122
|
+
+ "https://example.com/start",
|
|
123
|
+
+ )
|
|
124
|
+
+ const results = Array.from(yield* Stream.runCollect(stream))
|
|
125
|
+
+ const seen = yield* Ref.get(requests)
|
|
126
|
+
+
|
|
127
|
+
+ assert.deepStrictEqual(seen, [
|
|
128
|
+
+ "https://example.com/start",
|
|
129
|
+
+ "https://example.com/data",
|
|
130
|
+
+ ])
|
|
131
|
+
+ assert.deepStrictEqual(results[0]?.result, {
|
|
132
|
+
+ message: "hello",
|
|
133
|
+
+ count: 1,
|
|
134
|
+
+ })
|
|
135
|
+
+ }).pipe(
|
|
136
|
+
+ Effect.provide(AgentTools.AgentToolHandlersTest),
|
|
137
|
+
+ Effect.provide(NodeServices.layer),
|
|
138
|
+
+ Effect.provideService(HttpClient.HttpClient, client),
|
|
139
|
+
+ ),
|
|
140
|
+
+ )
|
|
141
|
+
+})
|
|
142
|
+
*** Add File: .changeset/warm-poets-share.md
|
|
143
|
+
+---
|
|
144
|
+
+"clanka": patch
|
|
145
|
+
+---
|
|
146
|
+
+
|
|
147
|
+
+Add a built-in `fetchJson` agent tool for fetching and parsing JSON responses.
|
|
148
|
+
*** End Patch`);
|
|
149
|
+
console.log('patch applied');
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
await applyPatch(`*** Begin Patch
|
|
2
|
+
*** Update File: src/AgentTools.ts
|
|
3
|
+
@@
|
|
4
|
+
import * as ExaSearch from "./ExaSearch.ts"
|
|
5
|
+
import * as WebToMarkdown from "./WebToMarkdown.ts"
|
|
6
|
+
-import type * as HttpClient from "effect/unstable/http/HttpClient"
|
|
7
|
+
+import * as HttpClient from "effect/unstable/http/HttpClient"
|
|
8
|
+
@@
|
|
9
|
+
Tool.make("fetchMarkdown", {
|
|
10
|
+
description: "Fetch a web page and convert it to markdown.",
|
|
11
|
+
parameters: Schema.String.annotate({
|
|
12
|
+
identifier: "url",
|
|
13
|
+
}),
|
|
14
|
+
success: Schema.String,
|
|
15
|
+
}),
|
|
16
|
+
+ Tool.make("fetchJson", {
|
|
17
|
+
+ description: "Fetch a URL and parse the response body as JSON.",
|
|
18
|
+
+ parameters: Schema.String.annotate({
|
|
19
|
+
+ identifier: "url",
|
|
20
|
+
+ }),
|
|
21
|
+
+ success: Schema.Unknown,
|
|
22
|
+
+ }),
|
|
23
|
+
Tool.make("sleep", {
|
|
24
|
+
description: "Sleep for a specified number of milliseconds",
|
|
25
|
+
parameters: Schema.Finite.annotate({
|
|
26
|
+
identifier: "ms",
|
|
27
|
+
}),
|
|
28
|
+
@@
|
|
29
|
+
const pathService = yield* Path.Path
|
|
30
|
+
const webSearch = yield* ExaSearch.ExaSearch
|
|
31
|
+
const fetchMarkdown = yield* WebToMarkdown.WebToMarkdown
|
|
32
|
+
+ const httpClient = (yield* HttpClient.HttpClient).pipe(
|
|
33
|
+
+ HttpClient.followRedirects(),
|
|
34
|
+
+ HttpClient.filterStatusOk,
|
|
35
|
+
+ HttpClient.retryTransient({
|
|
36
|
+
+ times: 3,
|
|
37
|
+
+ }),
|
|
38
|
+
+ )
|
|
39
|
+
@@
|
|
40
|
+
fetchMarkdown: Effect.fn("AgentTools.fetchMarkdown")(function* (url) {
|
|
41
|
+
yield* Effect.logInfo(\`Calling "fetchMarkdown"\`).pipe(
|
|
42
|
+
Effect.annotateLogs({ url }),
|
|
43
|
+
)
|
|
44
|
+
return yield* fetchMarkdown.convertUrl(url)
|
|
45
|
+
}, Effect.orDie),
|
|
46
|
+
+ fetchJson: Effect.fn("AgentTools.fetchJson")(function* (url) {
|
|
47
|
+
+ yield* Effect.logInfo(\`Calling "fetchJson"\`).pipe(
|
|
48
|
+
+ Effect.annotateLogs({ url }),
|
|
49
|
+
+ )
|
|
50
|
+
+ const response = yield* httpClient.get(url)
|
|
51
|
+
+ return yield* response.json
|
|
52
|
+
+ }, Effect.orDie),
|
|
53
|
+
sleep: Effect.fn("AgentTools.sleep")(function* (ms) {
|
|
54
|
+
yield* Effect.logInfo(\`Calling "sleep" for \${ms}ms\`)
|
|
55
|
+
return yield* Effect.sleep(ms)
|
|
56
|
+
}),
|
|
57
|
+
*** Add File: src/AgentTools.test.ts
|
|
58
|
+
+import * as NodeServices from "@effect/platform-node/NodeServices"
|
|
59
|
+
+import { assert, describe, it } from "@effect/vitest"
|
|
60
|
+
+import * as Effect from "effect/Effect"
|
|
61
|
+
+import * as Stream from "effect/Stream"
|
|
62
|
+
+import * as Ref from "effect/Ref"
|
|
63
|
+
+import {
|
|
64
|
+
+ HttpClient,
|
|
65
|
+
+ type HttpClientRequest,
|
|
66
|
+
+ HttpClientResponse,
|
|
67
|
+
+} from "effect/unstable/http"
|
|
68
|
+
+import * as AgentTools from "./AgentTools.ts"
|
|
69
|
+
+import * as ToolkitRenderer from "./ToolkitRenderer.ts"
|
|
70
|
+
+
|
|
71
|
+
+describe("AgentTools", () => {
|
|
72
|
+
+ it.effect("renders fetchJson in the toolkit dts", () =>
|
|
73
|
+
+ Effect.gen(function* () {
|
|
74
|
+
+ const renderer = yield* ToolkitRenderer.ToolkitRenderer
|
|
75
|
+
+ const output = renderer.render(AgentTools.AgentTools)
|
|
76
|
+
+
|
|
77
|
+
+ assert.include(
|
|
78
|
+
+ output,
|
|
79
|
+
+ 'declare function fetchJson(url: string): Promise<unknown>',
|
|
80
|
+
+ )
|
|
81
|
+
+ }).pipe(Effect.provide(ToolkitRenderer.ToolkitRenderer.layer)),
|
|
82
|
+
+ )
|
|
83
|
+
+
|
|
84
|
+
+ it.effect("fetchJson follows redirects and parses json", () =>
|
|
85
|
+
+ Effect.gen(function* () {
|
|
86
|
+
+ const requests = yield* Ref.make<Array<string>>([])
|
|
87
|
+
+ const client = HttpClient.make(
|
|
88
|
+
+ (request: HttpClientRequest.HttpClientRequest) =>
|
|
89
|
+
+ Effect.gen(function* () {
|
|
90
|
+
+ yield* Ref.update(requests, (current) => [...current, request.url])
|
|
91
|
+
+
|
|
92
|
+
+ if (request.url === "https://example.com/start") {
|
|
93
|
+
+ return HttpClientResponse.fromWeb(
|
|
94
|
+
+ request,
|
|
95
|
+
+ new Response(null, {
|
|
96
|
+
+ status: 302,
|
|
97
|
+
+ headers: { location: "/data" },
|
|
98
|
+
+ }),
|
|
99
|
+
+ )
|
|
100
|
+
+ }
|
|
101
|
+
+
|
|
102
|
+
+ if (request.url === "https://example.com/data") {
|
|
103
|
+
+ return HttpClientResponse.fromWeb(
|
|
104
|
+
+ request,
|
|
105
|
+
+ new Response('{"message":"hello","count":1}', {
|
|
106
|
+
+ status: 200,
|
|
107
|
+
+ headers: { "content-type": "application/json" },
|
|
108
|
+
+ }),
|
|
109
|
+
+ )
|
|
110
|
+
+ }
|
|
111
|
+
+
|
|
112
|
+
+ return HttpClientResponse.fromWeb(
|
|
113
|
+
+ request,
|
|
114
|
+
+ new Response("not found", { status: 404 }),
|
|
115
|
+
+ )
|
|
116
|
+
+ }),
|
|
117
|
+
+ )
|
|
118
|
+
+
|
|
119
|
+
+ const toolkit = yield* AgentTools.AgentTools.asEffect()
|
|
120
|
+
+ const stream = yield* toolkit.handle(
|
|
121
|
+
+ "fetchJson",
|
|
122
|
+
+ "https://example.com/start",
|
|
123
|
+
+ )
|
|
124
|
+
+ const results = Array.from(yield* Stream.runCollect(stream))
|
|
125
|
+
+ const seen = yield* Ref.get(requests)
|
|
126
|
+
+
|
|
127
|
+
+ assert.deepStrictEqual(seen, [
|
|
128
|
+
+ "https://example.com/start",
|
|
129
|
+
+ "https://example.com/data",
|
|
130
|
+
+ ])
|
|
131
|
+
+ assert.deepStrictEqual(results[0]?.result, {
|
|
132
|
+
+ message: "hello",
|
|
133
|
+
+ count: 1,
|
|
134
|
+
+ })
|
|
135
|
+
+ }).pipe(
|
|
136
|
+
+ Effect.provide(AgentTools.AgentToolHandlersTest),
|
|
137
|
+
+ Effect.provide(NodeServices.layer),
|
|
138
|
+
+ Effect.provideService(HttpClient.HttpClient, client),
|
|
139
|
+
+ ),
|
|
140
|
+
+ )
|
|
141
|
+
+})
|
|
142
|
+
*** Add File: .changeset/warm-poets-share.md
|
|
143
|
+
+---
|
|
144
|
+
+"clanka": patch
|
|
145
|
+
+---
|
|
146
|
+
+
|
|
147
|
+
+Add a built-in \`fetchJson\` agent tool for fetching and parsing JSON responses.
|
|
148
|
+
*** End Patch`);
|
|
149
|
+
console.log('patch applied');
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
const patch = `*** Begin Patch
|
|
2
|
+
*** Update File: packages/effect/src/Stream.ts
|
|
3
|
+
@@
|
|
4
|
+
- * Maps each element to a stream and concatenates the results in order.
|
|
5
|
+
+ * Maps each element to a stream and concatenates the results in order.
|
|
6
|
+
+ *
|
|
7
|
+
+ * Set \`switch: true\` to interrupt the previous inner stream when a new
|
|
8
|
+
+ * element arrives.
|
|
9
|
+
@@
|
|
10
|
+
<A, A2, E2, R2>(
|
|
11
|
+
f: (a: A) => Stream<A2, E2, R2>,
|
|
12
|
+
options?: {
|
|
13
|
+
readonly concurrency?: number | \"unbounded\" | undefined
|
|
14
|
+
readonly bufferSize?: number | undefined
|
|
15
|
+
+ readonly switch?: boolean | undefined
|
|
16
|
+
} | undefined
|
|
17
|
+
): <E, R>(self: Stream<A, E, R>) => Stream<A2, E2 | E, R2 | R>
|
|
18
|
+
<A, E, R, A2, E2, R2>(
|
|
19
|
+
self: Stream<A, E, R>,
|
|
20
|
+
f: (a: A) => Stream<A2, E2, R2>,
|
|
21
|
+
options?: {
|
|
22
|
+
readonly concurrency?: number | \"unbounded\" | undefined
|
|
23
|
+
readonly bufferSize?: number | undefined
|
|
24
|
+
+ readonly switch?: boolean | undefined
|
|
25
|
+
} | undefined
|
|
26
|
+
): Stream<A2, E | E2, R | R2>
|
|
27
|
+
} = dual((args) => isStream(args[0]), <A, E, R, A2, E2, R2>(
|
|
28
|
+
self: Stream<A, E, R>,
|
|
29
|
+
f: (a: A) => Stream<A2, E2, R2>,
|
|
30
|
+
options?: {
|
|
31
|
+
readonly concurrency?: number | \"unbounded\" | undefined
|
|
32
|
+
readonly bufferSize?: number | undefined
|
|
33
|
+
+ readonly switch?: boolean | undefined
|
|
34
|
+
} | undefined
|
|
35
|
+
): Stream<A2, E | E2, R | R2> =>
|
|
36
|
+
self.channel.pipe(
|
|
37
|
+
Channel.flattenArray,
|
|
38
|
+
- Channel.flatMap((a) => f(a).channel, options),
|
|
39
|
+
+ (options?.switch ? Channel.switchMap : Channel.flatMap)((a) => f(a).channel, options),
|
|
40
|
+
fromChannel
|
|
41
|
+
))
|
|
42
|
+
*** Update File: packages/effect/test/Stream.test.ts
|
|
43
|
+
@@
|
|
44
|
+
describe("callback", () => {
|
|
45
|
+
@@
|
|
46
|
+
}))
|
|
47
|
+
})
|
|
48
|
+
+
|
|
49
|
+
+ describe("flatMap", () => {
|
|
50
|
+
+ it.effect("supports switch semantics", () =>
|
|
51
|
+
+ Effect.gen(function*() {
|
|
52
|
+
+ const result = yield* Stream.make(1, 2, 3).pipe(
|
|
53
|
+
+ Stream.flatMap((n) => n === 3 ? Stream.make(n) : Stream.never, { switch: true }),
|
|
54
|
+
+ Stream.runCollect
|
|
55
|
+
+ )
|
|
56
|
+
+ assert.deepStrictEqual(result, [3])
|
|
57
|
+
+ }))
|
|
58
|
+
+ })
|
|
59
|
+
|
|
60
|
+
describe("bufferArray", () => {
|
|
61
|
+
*** Add File: .changeset/olive-bears-kiss.md
|
|
62
|
+
+---
|
|
63
|
+
+"effect": patch
|
|
64
|
+
+---
|
|
65
|
+
+
|
|
66
|
+
+Add a `switch` option to `Stream.flatMap` for `switchMap` semantics.
|
|
67
|
+
*** End Patch`
|
|
68
|
+
console.log(await applyPatch(patch))
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
const patch = `*** Begin Patch
|
|
2
|
+
*** Update File: packages/effect/src/Stream.ts
|
|
3
|
+
@@
|
|
4
|
+
- * Maps each element to a stream and concatenates the results in order.
|
|
5
|
+
+ * Maps each element to a stream and concatenates the results in order.
|
|
6
|
+
+ *
|
|
7
|
+
+ * Set \`switch: true\` to interrupt the previous inner stream when a new
|
|
8
|
+
+ * element arrives.
|
|
9
|
+
@@
|
|
10
|
+
<A, A2, E2, R2>(
|
|
11
|
+
f: (a: A) => Stream<A2, E2, R2>,
|
|
12
|
+
options?: {
|
|
13
|
+
readonly concurrency?: number | "unbounded" | undefined
|
|
14
|
+
readonly bufferSize?: number | undefined
|
|
15
|
+
+ readonly switch?: boolean | undefined
|
|
16
|
+
} | undefined
|
|
17
|
+
): <E, R>(self: Stream<A, E, R>) => Stream<A2, E2 | E, R2 | R>
|
|
18
|
+
<A, E, R, A2, E2, R2>(
|
|
19
|
+
self: Stream<A, E, R>,
|
|
20
|
+
f: (a: A) => Stream<A2, E2, R2>,
|
|
21
|
+
options?: {
|
|
22
|
+
readonly concurrency?: number | "unbounded" | undefined
|
|
23
|
+
readonly bufferSize?: number | undefined
|
|
24
|
+
+ readonly switch?: boolean | undefined
|
|
25
|
+
} | undefined
|
|
26
|
+
): Stream<A2, E | E2, R | R2>
|
|
27
|
+
} = dual((args) => isStream(args[0]), <A, E, R, A2, E2, R2>(
|
|
28
|
+
self: Stream<A, E, R>,
|
|
29
|
+
f: (a: A) => Stream<A2, E2, R2>,
|
|
30
|
+
options?: {
|
|
31
|
+
readonly concurrency?: number | "unbounded" | undefined
|
|
32
|
+
readonly bufferSize?: number | undefined
|
|
33
|
+
+ readonly switch?: boolean | undefined
|
|
34
|
+
} | undefined
|
|
35
|
+
): Stream<A2, E | E2, R | R2> =>
|
|
36
|
+
self.channel.pipe(
|
|
37
|
+
Channel.flattenArray,
|
|
38
|
+
- Channel.flatMap((a) => f(a).channel, options),
|
|
39
|
+
+ (options?.switch ? Channel.switchMap : Channel.flatMap)((a) => f(a).channel, options),
|
|
40
|
+
fromChannel
|
|
41
|
+
))
|
|
42
|
+
*** Update File: packages/effect/test/Stream.test.ts
|
|
43
|
+
@@
|
|
44
|
+
describe("callback", () => {
|
|
45
|
+
@@
|
|
46
|
+
}))
|
|
47
|
+
})
|
|
48
|
+
+
|
|
49
|
+
+ describe("flatMap", () => {
|
|
50
|
+
+ it.effect("supports switch semantics", () =>
|
|
51
|
+
+ Effect.gen(function*() {
|
|
52
|
+
+ const result = yield* Stream.make(1, 2, 3).pipe(
|
|
53
|
+
+ Stream.flatMap((n) => n === 3 ? Stream.make(n) : Stream.never, { switch: true }),
|
|
54
|
+
+ Stream.runCollect
|
|
55
|
+
+ )
|
|
56
|
+
+ assert.deepStrictEqual(result, [3])
|
|
57
|
+
+ }))
|
|
58
|
+
+ })
|
|
59
|
+
|
|
60
|
+
describe("bufferArray", () => {
|
|
61
|
+
*** Add File: .changeset/olive-bears-kiss.md
|
|
62
|
+
+---
|
|
63
|
+
+"effect": patch
|
|
64
|
+
+---
|
|
65
|
+
+
|
|
66
|
+
+Add a \`switch\` option to \`Stream.flatMap\` for \`switchMap\` semantics.
|
|
67
|
+
*** End Patch`
|
|
68
|
+
console.log(await applyPatch(patch))
|